Merge pull request #81079 from dalexeev/gds-fix-get-method-list

GDScript: Fix `get_*_list()` methods return incorrect info
This commit is contained in:
Rémi Verschelde 2023-09-11 15:36:52 +02:00
commit 13f0ab88f2
No known key found for this signature in database
GPG Key ID: C3336907360768E1
15 changed files with 429 additions and 305 deletions

View File

@ -87,7 +87,7 @@ static void _doctype_from_gdtype(const GDType &p_gdtype, String &r_type, String
case GDType::SCRIPT:
if (p_gdtype.script_type.is_valid()) {
if (p_gdtype.script_type->get_global_name() != StringName()) {
r_type = _get_script_path(p_gdtype.script_type->get_global_name());
r_type = p_gdtype.script_type->get_global_name();
return;
}
if (!p_gdtype.script_type->get_path().is_empty()) {
@ -129,10 +129,10 @@ void GDScriptDocGen::generate_docs(GDScript *p_script, const GDP::ClassNode *p_c
DocData::ClassDoc &doc = p_script->doc;
doc.script_path = _get_script_path(p_script->get_script_path());
if (p_script->name.is_empty()) {
if (p_script->local_name == StringName()) {
doc.name = doc.script_path;
} else {
doc.name = p_script->name;
doc.name = p_script->local_name;
}
if (p_script->_owner) {
@ -204,6 +204,9 @@ void GDScriptDocGen::generate_docs(GDScript *p_script, const GDP::ClassNode *p_c
if (m_func->return_type) {
_doctype_from_gdtype(m_func->return_type->get_datatype(), method_doc.return_type, method_doc.return_enum, true);
} else if (!m_func->body->has_return) {
// If no `return` statement, then return type is `void`, not `Variant`.
method_doc.return_type = "void";
} else {
method_doc.return_type = "Variant";
}

View File

@ -254,7 +254,7 @@ Ref<Script> GDScript::get_base_script() const {
}
StringName GDScript::get_global_name() const {
return name;
return global_name;
}
StringName GDScript::get_instance_base_type() const {
@ -284,27 +284,9 @@ void GDScript::_get_script_method_list(List<MethodInfo> *r_list, bool p_include_
const GDScript *current = this;
while (current) {
for (const KeyValue<StringName, GDScriptFunction *> &E : current->member_functions) {
GDScriptFunction *func = E.value;
MethodInfo mi;
mi.name = E.key;
if (func->is_static()) {
mi.flags |= METHOD_FLAG_STATIC;
}
for (int i = 0; i < func->get_argument_count(); i++) {
PropertyInfo arginfo = func->get_argument_type(i);
#ifdef TOOLS_ENABLED
arginfo.name = func->get_argument_name(i);
#endif
mi.arguments.push_back(arginfo);
}
#ifdef TOOLS_ENABLED
mi.default_arguments.append_array(func->get_default_arg_values());
#endif
mi.return_val = func->get_return_type();
r_list->push_back(mi);
r_list->push_back(E.value->get_method_info());
}
if (!p_include_base) {
return;
}
@ -323,10 +305,9 @@ void GDScript::_get_script_property_list(List<PropertyInfo> *r_list, bool p_incl
while (sptr) {
Vector<_GDScriptMemberSort> msort;
for (const KeyValue<StringName, PropertyInfo> &E : sptr->member_info) {
for (const KeyValue<StringName, MemberInfo> &E : sptr->member_indices) {
_GDScriptMemberSort ms;
ERR_CONTINUE(!sptr->member_indices.has(E.key));
ms.index = sptr->member_indices[E.key].index;
ms.index = E.value.index;
ms.name = E.key;
msort.push_back(ms);
}
@ -334,7 +315,7 @@ void GDScript::_get_script_property_list(List<PropertyInfo> *r_list, bool p_incl
msort.sort();
msort.reverse();
for (int i = 0; i < msort.size(); i++) {
props.push_front(sptr->member_info[msort[i].name]);
props.push_front(sptr->member_indices[msort[i].name].property_info);
}
#ifdef TOOLS_ENABLED
@ -368,15 +349,7 @@ MethodInfo GDScript::get_method_info(const StringName &p_method) const {
return MethodInfo();
}
GDScriptFunction *func = E->value;
MethodInfo mi;
mi.name = E->key;
for (int i = 0; i < func->get_argument_count(); i++) {
mi.arguments.push_back(func->get_argument_type(i));
}
mi.return_val = func->get_return_type();
return mi;
return E->value->get_method_info();
}
bool GDScript::get_property_default_value(const StringName &p_property, Variant &r_value) const {
@ -557,13 +530,7 @@ bool GDScript::_update_exports(bool *r_err, bool p_recursive_call, PlaceHolderSc
member_default_values_cache[member.variable->identifier->name] = default_value;
} break;
case GDScriptParser::ClassNode::Member::SIGNAL: {
// TODO: Cache this in parser to avoid loops like this.
Vector<StringName> parameters_names;
parameters_names.resize(member.signal->parameters.size());
for (int j = 0; j < member.signal->parameters.size(); j++) {
parameters_names.write[j] = member.signal->parameters[j]->identifier->name;
}
_signals[member.signal->identifier->name] = parameters_names;
_signals[member.signal->identifier->name] = member.signal->method_info;
} break;
case GDScriptParser::ClassNode::Member::GROUP: {
members_cache.push_back(member.annotation->export_info);
@ -977,22 +944,26 @@ bool GDScript::_set(const StringName &p_name, const Variant &p_value) {
void GDScript::_get_property_list(List<PropertyInfo> *p_properties) const {
p_properties->push_back(PropertyInfo(Variant::STRING, "script/source", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL));
List<PropertyInfo> property_list;
List<const GDScript *> classes;
const GDScript *top = this;
while (top) {
for (const KeyValue<StringName, MemberInfo> &E : top->static_variables_indices) {
PropertyInfo pi = PropertyInfo(E.value.data_type);
pi.name = E.key;
pi.usage |= PROPERTY_USAGE_SCRIPT_VARIABLE; // For the script (as a class) it is a non-static property.
property_list.push_back(pi);
}
classes.push_back(top);
top = top->_base;
}
for (const List<PropertyInfo>::Element *E = property_list.back(); E; E = E->prev()) {
p_properties->push_back(E->get());
for (const List<const GDScript *>::Element *E = classes.back(); E; E = E->prev()) {
Vector<_GDScriptMemberSort> msort;
for (const KeyValue<StringName, MemberInfo> &F : E->get()->static_variables_indices) {
_GDScriptMemberSort ms;
ms.index = F.value.index;
ms.name = F.key;
msort.push_back(ms);
}
msort.sort();
for (int i = 0; i < msort.size(); i++) {
p_properties->push_back(E->get()->static_variables_indices[msort[i].name].property_info);
}
}
}
@ -1110,7 +1081,7 @@ GDScript *GDScript::find_class(const String &p_qualified_name) {
Vector<String> class_names;
GDScript *result = nullptr;
// Empty initial name means start here.
if (first.is_empty() || first == name) {
if (first.is_empty() || first == global_name) {
class_names = p_qualified_name.split("::");
result = this;
} else if (p_qualified_name.begins_with(get_root_script()->path)) {
@ -1245,15 +1216,8 @@ bool GDScript::has_script_signal(const StringName &p_signal) const {
}
void GDScript::_get_script_signal_list(List<MethodInfo> *r_list, bool p_include_base) const {
for (const KeyValue<StringName, Vector<StringName>> &E : _signals) {
MethodInfo mi;
mi.name = E.key;
for (int i = 0; i < E.value.size(); i++) {
PropertyInfo arg;
arg.name = E.value[i];
mi.arguments.push_back(arg);
}
r_list->push_back(mi);
for (const KeyValue<StringName, MethodInfo> &E : _signals) {
r_list->push_back(E.value);
}
if (!p_include_base) {
@ -1274,21 +1238,6 @@ void GDScript::get_script_signal_list(List<MethodInfo> *r_signals) const {
_get_script_signal_list(r_signals, true);
}
String GDScript::_get_gdscript_reference_class_name(const GDScript *p_gdscript) {
ERR_FAIL_NULL_V(p_gdscript, String());
String class_name;
while (p_gdscript) {
if (class_name.is_empty()) {
class_name = p_gdscript->get_script_class_name();
} else {
class_name = p_gdscript->get_script_class_name() + "." + class_name;
}
p_gdscript = p_gdscript->_owner;
}
return class_name;
}
GDScript *GDScript::_get_gdscript_from_variant(const Variant &p_variant) {
Object *obj = p_variant;
if (obj == nullptr || obj->get_instance_id().is_null()) {
@ -1420,8 +1369,8 @@ String GDScript::debug_get_script_name(const Ref<Script> &p_script) {
if (p_script.is_valid()) {
Ref<GDScript> gdscript = p_script;
if (gdscript.is_valid()) {
if (!gdscript->get_script_class_name().is_empty()) {
return gdscript->get_script_class_name();
if (gdscript->get_local_name() != StringName()) {
return gdscript->get_local_name();
}
return gdscript->get_fully_qualified_name().get_file();
}
@ -1667,7 +1616,7 @@ bool GDScriptInstance::get(const StringName &p_name, Variant &r_ret) const {
}
{
HashMap<StringName, Vector<StringName>>::ConstIterator E = sptr->_signals.find(p_name);
HashMap<StringName, MethodInfo>::ConstIterator E = sptr->_signals.find(p_name);
if (E) {
r_ret = Signal(this->owner, E->key);
return true;
@ -1717,11 +1666,11 @@ bool GDScriptInstance::get(const StringName &p_name, Variant &r_ret) const {
Variant::Type GDScriptInstance::get_property_type(const StringName &p_name, bool *r_is_valid) const {
const GDScript *sptr = script.ptr();
while (sptr) {
if (sptr->member_info.has(p_name)) {
if (sptr->member_indices.has(p_name)) {
if (r_is_valid) {
*r_is_valid = true;
}
return sptr->member_info[p_name].type;
return sptr->member_indices[p_name].property_info.type;
}
sptr = sptr->_base;
}
@ -1798,10 +1747,9 @@ void GDScriptInstance::get_property_list(List<PropertyInfo> *p_properties) const
//instance a fake script for editing the values
Vector<_GDScriptMemberSort> msort;
for (const KeyValue<StringName, PropertyInfo> &F : sptr->member_info) {
for (const KeyValue<StringName, GDScript::MemberInfo> &F : sptr->member_indices) {
_GDScriptMemberSort ms;
ERR_CONTINUE(!sptr->member_indices.has(F.key));
ms.index = sptr->member_indices[F.key].index;
ms.index = F.value.index;
ms.name = F.key;
msort.push_back(ms);
}
@ -1809,7 +1757,7 @@ void GDScriptInstance::get_property_list(List<PropertyInfo> *p_properties) const
msort.sort();
msort.reverse();
for (int i = 0; i < msort.size(); i++) {
props.push_front(sptr->member_info[msort[i].name]);
props.push_front(sptr->member_indices[msort[i].name].property_info);
}
#ifdef TOOLS_ENABLED
@ -1872,12 +1820,7 @@ void GDScriptInstance::get_method_list(List<MethodInfo> *p_list) const {
const GDScript *sptr = script.ptr();
while (sptr) {
for (const KeyValue<StringName, GDScriptFunction *> &E : sptr->member_functions) {
MethodInfo mi;
mi.name = E.key;
for (int i = 0; i < E.value->get_argument_count(); i++) {
mi.arguments.push_back(PropertyInfo(Variant::NIL, "arg" + itos(i)));
}
p_list->push_back(mi);
p_list->push_back(E.value->get_method_info());
}
sptr = sptr->_base;
}

View File

@ -69,6 +69,7 @@ class GDScript : public Script {
StringName setter;
StringName getter;
GDScriptDataType data_type;
PropertyInfo property_info;
};
struct ClearData {
@ -100,7 +101,7 @@ class GDScript : public Script {
HashMap<StringName, GDScriptFunction *> member_functions;
HashMap<StringName, MemberInfo> member_indices; //members are just indices to the instantiated script.
HashMap<StringName, Ref<GDScript>> subclasses;
HashMap<StringName, Vector<StringName>> _signals;
HashMap<StringName, MethodInfo> _signals;
Dictionary rpc_config;
#ifdef TOOLS_ENABLED
@ -126,8 +127,6 @@ class GDScript : public Script {
void _add_doc(const DocData::ClassDoc &p_inner_class);
#endif
HashMap<StringName, PropertyInfo> member_info;
GDScriptFunction *implicit_initializer = nullptr;
GDScriptFunction *initializer = nullptr; //direct pointer to new , faster to locate
GDScriptFunction *implicit_ready = nullptr;
@ -142,7 +141,8 @@ class GDScript : public Script {
//exported members
String source;
String path;
String name;
StringName local_name; // Inner class identifier or `class_name`.
StringName global_name; // `class_name`.
String fully_qualified_name;
String simplified_icon_path;
SelfList<GDScript> script_list;
@ -174,9 +174,6 @@ class GDScript : public Script {
void _get_script_method_list(List<MethodInfo> *r_list, bool p_include_base) const;
void _get_script_signal_list(List<MethodInfo> *r_list, bool p_include_base) const;
// This method will map the class name from "RefCounted" to "MyClass.InnerClass".
static String _get_gdscript_reference_class_name(const GDScript *p_gdscript);
GDScript *_get_gdscript_from_variant(const Variant &p_variant);
void _get_dependencies(RBSet<GDScript *> &p_dependencies, const GDScript *p_except);
@ -194,6 +191,8 @@ public:
static String debug_get_script_name(const Ref<Script> &p_script);
#endif
_FORCE_INLINE_ StringName get_local_name() const { return local_name; }
void clear(GDScript::ClearData *p_clear_data = nullptr);
virtual bool is_valid() const override { return valid; }
@ -214,7 +213,6 @@ public:
}
const HashMap<StringName, GDScriptFunction *> &get_member_functions() const { return member_functions; }
const Ref<GDScriptNativeClass> &get_native() const { return native; }
const String &get_script_class_name() const { return name; }
RBSet<GDScript *> get_dependencies();
RBSet<GDScript *> get_inverted_dependencies();

View File

@ -1000,10 +1000,11 @@ void GDScriptAnalyzer::resolve_class_member(GDScriptParser::ClassNode *p_class,
GDScriptParser::ParameterNode *param = member.signal->parameters[j];
GDScriptParser::DataType param_type = type_from_metatype(resolve_datatype(param->datatype_specifier));
param->set_datatype(param_type);
mi.arguments.push_back(PropertyInfo(param_type.builtin_type, param->identifier->name));
// TODO: add signal parameter default values
mi.arguments.push_back(param_type.to_property_info(param->identifier->name));
// Signals do not support parameter default values.
}
member.signal->set_datatype(make_signal_type(mi));
member.signal->method_info = mi;
// Apply annotations.
for (GDScriptParser::AnnotationNode *&E : member.signal->annotations) {
@ -1604,9 +1605,11 @@ void GDScriptAnalyzer::resolve_function_signature(GDScriptParser::FunctionNode *
}
is_shadowing(p_function->parameters[i]->identifier, "function parameter", true);
#endif // DEBUG_ENABLED
#ifdef TOOLS_ENABLED
if (p_function->parameters[i]->initializer) {
#ifdef TOOLS_ENABLED
default_value_count++;
#endif // TOOLS_ENABLED
if (p_function->parameters[i]->initializer->is_constant) {
p_function->default_arg_values.push_back(p_function->parameters[i]->initializer->reduced_value);
@ -1614,7 +1617,6 @@ void GDScriptAnalyzer::resolve_function_signature(GDScriptParser::FunctionNode *
p_function->default_arg_values.push_back(Variant()); // Prevent shift.
}
}
#endif // TOOLS_ENABLED
}
if (!p_is_lambda && function_name == GDScriptLanguage::get_singleton()->strings._init) {

View File

@ -35,9 +35,6 @@
#include "core/debugger/engine_debugger.h"
uint32_t GDScriptByteCodeGenerator::add_parameter(const StringName &p_name, bool p_is_optional, const GDScriptDataType &p_type) {
#ifdef TOOLS_ENABLED
function->arg_names.push_back(p_name);
#endif
function->_argument_count++;
function->argument_types.push_back(p_type);
if (p_is_optional) {

View File

@ -2165,8 +2165,14 @@ GDScriptFunction *GDScriptCompiler::_parse_function(Error &r_error, GDScript *p_
}
}
MethodInfo method_info;
codegen.function_name = func_name;
method_info.name = func_name;
codegen.is_static = is_static;
if (is_static) {
method_info.flags |= METHOD_FLAG_STATIC;
}
codegen.generator->write_start(p_script, func_name, is_static, rpc_config, return_type);
int optional_parameters = 0;
@ -2178,10 +2184,14 @@ GDScriptFunction *GDScriptCompiler::_parse_function(Error &r_error, GDScript *p_
uint32_t par_addr = codegen.generator->add_parameter(parameter->identifier->name, parameter->initializer != nullptr, par_type);
codegen.parameters[parameter->identifier->name] = GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::FUNCTION_PARAMETER, par_addr, par_type);
method_info.arguments.push_back(parameter->get_datatype().to_property_info(parameter->identifier->name));
if (parameter->initializer != nullptr) {
optional_parameters++;
}
}
method_info.default_arguments.append_array(p_func->default_arg_values);
}
// Parse initializer if applies.
@ -2335,20 +2345,20 @@ GDScriptFunction *GDScriptCompiler::_parse_function(Error &r_error, GDScript *p_
}
if (p_func) {
// if no return statement -> return type is void not unresolved Variant
// If no `return` statement, then return type is `void`, not `Variant`.
if (p_func->body->has_return) {
gd_function->return_type = _gdtype_from_datatype(p_func->get_datatype(), p_script);
method_info.return_val = p_func->get_datatype().to_property_info(String());
} else {
gd_function->return_type = GDScriptDataType();
gd_function->return_type.has_type = true;
gd_function->return_type.kind = GDScriptDataType::BUILTIN;
gd_function->return_type.builtin_type = Variant::NIL;
}
#ifdef TOOLS_ENABLED
gd_function->default_arg_values = p_func->default_arg_values;
#endif
}
gd_function->method_info = method_info;
if (!is_implicit_initializer && !is_implicit_ready && !p_for_lambda) {
p_script->member_functions[func_name] = gd_function;
}
@ -2554,7 +2564,6 @@ Error GDScriptCompiler::_populate_class_members(GDScript *p_script, const GDScri
p_script->member_functions.clear();
p_script->member_indices.clear();
p_script->member_info.clear();
p_script->static_variables_indices.clear();
p_script->static_variables.clear();
p_script->_signals.clear();
@ -2567,9 +2576,9 @@ Error GDScriptCompiler::_populate_class_members(GDScript *p_script, const GDScri
p_script->tool = parser->is_tool();
if (!p_script->name.is_empty()) {
if (ClassDB::class_exists(p_script->name) && ClassDB::is_class_exposed(p_script->name)) {
_set_error("The class '" + p_script->name + "' shadows a native class", p_class);
if (p_script->local_name != StringName()) {
if (ClassDB::class_exists(p_script->local_name) && ClassDB::is_class_exposed(p_script->local_name)) {
_set_error(vformat(R"(The class "%s" shadows a native class)", p_script->local_name), p_class);
return ERR_ALREADY_EXISTS;
}
}
@ -2636,7 +2645,6 @@ Error GDScriptCompiler::_populate_class_members(GDScript *p_script, const GDScri
StringName name = variable->identifier->name;
GDScript::MemberInfo minfo;
minfo.index = p_script->member_indices.size();
switch (variable->property) {
case GDScriptParser::VariableNode::PROP_NONE:
break; // Nothing to do.
@ -2659,8 +2667,7 @@ Error GDScriptCompiler::_populate_class_members(GDScript *p_script, const GDScri
}
minfo.data_type = _gdtype_from_datatype(variable->get_datatype(), p_script);
PropertyInfo prop_info = minfo.data_type;
prop_info.name = name;
PropertyInfo prop_info = variable->get_datatype().to_property_info(name);
PropertyInfo export_info = variable->export_info;
if (variable->exported) {
@ -2670,16 +2677,16 @@ Error GDScriptCompiler::_populate_class_members(GDScript *p_script, const GDScri
}
prop_info.hint = export_info.hint;
prop_info.hint_string = export_info.hint_string;
prop_info.usage = export_info.usage | PROPERTY_USAGE_SCRIPT_VARIABLE;
} else {
prop_info.usage = PROPERTY_USAGE_SCRIPT_VARIABLE;
prop_info.usage = export_info.usage;
}
prop_info.usage |= PROPERTY_USAGE_SCRIPT_VARIABLE;
minfo.property_info = prop_info;
if (variable->is_static) {
minfo.index = p_script->static_variables_indices.size();
p_script->static_variables_indices[name] = minfo;
} else {
p_script->member_info[name] = prop_info;
minfo.index = p_script->member_indices.size();
p_script->member_indices[name] = minfo;
p_script->members.insert(name);
}
@ -2712,12 +2719,7 @@ Error GDScriptCompiler::_populate_class_members(GDScript *p_script, const GDScri
const GDScriptParser::SignalNode *signal = member.signal;
StringName name = signal->identifier->name;
Vector<StringName> parameters_names;
parameters_names.resize(signal->parameters.size());
for (int j = 0; j < signal->parameters.size(); j++) {
parameters_names.write[j] = signal->parameters[j]->identifier->name;
}
p_script->_signals[name] = parameters_names;
p_script->_signals[name] = signal->method_info;
} break;
case GDScriptParser::ClassNode::Member::ENUM: {
@ -2740,8 +2742,8 @@ Error GDScriptCompiler::_populate_class_members(GDScript *p_script, const GDScri
prop_info.name = annotation->export_info.name;
prop_info.usage = annotation->export_info.usage;
prop_info.hint_string = annotation->export_info.hint_string;
minfo.property_info = prop_info;
p_script->member_info[name] = prop_info;
p_script->member_indices[name] = minfo;
p_script->members.insert(Variant());
} break;
@ -2927,7 +2929,8 @@ void GDScriptCompiler::convert_to_initializer_type(Variant &p_variant, const GDS
void GDScriptCompiler::make_scripts(GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state) {
p_script->fully_qualified_name = p_class->fqcn;
p_script->name = p_class->identifier ? p_class->identifier->name : "";
p_script->local_name = p_class->identifier ? p_class->identifier->name : StringName();
p_script->global_name = p_class->get_global_name();
p_script->simplified_icon_path = p_class->simplified_icon_path;
HashMap<StringName, Ref<GDScript>> old_subclasses;

View File

@ -32,14 +32,6 @@
#include "gdscript.h"
const int *GDScriptFunction::get_code() const {
return _code_ptr;
}
int GDScriptFunction::get_code_size() const {
return _code_size;
}
Variant GDScriptFunction::get_constant(int p_idx) const {
ERR_FAIL_INDEX_V(p_idx, constants.size(), "<errconst>");
return constants[p_idx];
@ -50,32 +42,6 @@ StringName GDScriptFunction::get_global_name(int p_idx) const {
return global_names[p_idx];
}
int GDScriptFunction::get_default_argument_count() const {
return _default_arg_count;
}
int GDScriptFunction::get_default_argument_addr(int p_idx) const {
ERR_FAIL_INDEX_V(p_idx, default_arguments.size(), -1);
return default_arguments[p_idx];
}
GDScriptDataType GDScriptFunction::get_return_type() const {
return return_type;
}
GDScriptDataType GDScriptFunction::get_argument_type(int p_idx) const {
ERR_FAIL_INDEX_V(p_idx, argument_types.size(), GDScriptDataType());
return argument_types[p_idx];
}
StringName GDScriptFunction::get_name() const {
return name;
}
int GDScriptFunction::get_max_stack_size() const {
return _stack_size;
}
struct _GDFKC {
int order = 0;
List<int> pos;
@ -161,9 +127,7 @@ GDScriptFunction::~GDScriptFunction() {
return_type.script_type_ref = Ref<Script>();
#ifdef DEBUG_ENABLED
MutexLock lock(GDScriptLanguage::get_singleton()->mutex);
GDScriptLanguage::get_singleton()->function_list.remove(&function_list);
#endif
}

View File

@ -147,33 +147,6 @@ public:
return false;
}
operator PropertyInfo() const {
PropertyInfo info;
info.usage = PROPERTY_USAGE_NONE;
if (has_type) {
switch (kind) {
case UNINITIALIZED:
break;
case BUILTIN: {
info.type = builtin_type;
} break;
case NATIVE: {
info.type = Variant::OBJECT;
info.class_name = native_type;
} break;
case SCRIPT:
case GDSCRIPT: {
info.type = Variant::OBJECT;
info.class_name = script_type->get_instance_base_type();
} break;
}
} else {
info.type = Variant::NIL;
info.usage |= PROPERTY_USAGE_NIL_IS_VARIANT;
}
return info;
}
void set_container_element_type(const GDScriptDataType &p_element_type) {
container_element_type = memnew(GDScriptDataType(p_element_type));
}
@ -437,59 +410,32 @@ private:
friend class GDScript;
friend class GDScriptCompiler;
friend class GDScriptByteCodeGenerator;
friend class GDScriptLanguage;
StringName name;
StringName source;
bool _static = false;
Vector<GDScriptDataType> argument_types;
GDScriptDataType return_type;
MethodInfo method_info;
Variant rpc_config;
mutable Variant nil;
mutable Variant *_constants_ptr = nullptr;
int _constant_count = 0;
const StringName *_global_names_ptr = nullptr;
int _global_names_count = 0;
const int *_default_arg_ptr = nullptr;
int _default_arg_count = 0;
int _operator_funcs_count = 0;
const Variant::ValidatedOperatorEvaluator *_operator_funcs_ptr = nullptr;
int _setters_count = 0;
const Variant::ValidatedSetter *_setters_ptr = nullptr;
int _getters_count = 0;
const Variant::ValidatedGetter *_getters_ptr = nullptr;
int _keyed_setters_count = 0;
const Variant::ValidatedKeyedSetter *_keyed_setters_ptr = nullptr;
int _keyed_getters_count = 0;
const Variant::ValidatedKeyedGetter *_keyed_getters_ptr = nullptr;
int _indexed_setters_count = 0;
const Variant::ValidatedIndexedSetter *_indexed_setters_ptr = nullptr;
int _indexed_getters_count = 0;
const Variant::ValidatedIndexedGetter *_indexed_getters_ptr = nullptr;
int _builtin_methods_count = 0;
const Variant::ValidatedBuiltInMethod *_builtin_methods_ptr = nullptr;
int _constructors_count = 0;
const Variant::ValidatedConstructor *_constructors_ptr = nullptr;
int _utilities_count = 0;
const Variant::ValidatedUtilityFunction *_utilities_ptr = nullptr;
int _gds_utilities_count = 0;
const GDScriptUtilityFunctions::FunctionPtr *_gds_utilities_ptr = nullptr;
int _methods_count = 0;
MethodBind **_methods_ptr = nullptr;
int _lambdas_count = 0;
GDScriptFunction **_lambdas_ptr = nullptr;
int *_code_ptr = nullptr;
int _code_size = 0;
GDScript *_script = nullptr;
int _initial_line = 0;
int _argument_count = 0;
int _stack_size = 0;
int _instruction_args_size = 0;
int _ptrcall_args_size = 0;
int _initial_line = 0;
bool _static = false;
Variant rpc_config;
SelfList<GDScriptFunction> function_list{ this };
mutable Variant nil;
HashMap<int, Variant::Type> temporary_slots;
List<StackDebug> stack_debug;
GDScript *_script = nullptr;
StringName name;
Vector<int> code;
Vector<int> default_arguments;
Vector<Variant> constants;
Vector<StringName> global_names;
Vector<int> default_arguments;
Vector<Variant::ValidatedOperatorEvaluator> operator_funcs;
Vector<Variant::ValidatedSetter> setters;
Vector<Variant::ValidatedGetter> getters;
@ -503,18 +449,47 @@ private:
Vector<GDScriptUtilityFunctions::FunctionPtr> gds_utilities;
Vector<MethodBind *> methods;
Vector<GDScriptFunction *> lambdas;
Vector<int> code;
Vector<GDScriptDataType> argument_types;
GDScriptDataType return_type;
HashMap<int, Variant::Type> temporary_slots;
int _code_size = 0;
int _default_arg_count = 0;
int _constant_count = 0;
int _global_names_count = 0;
int _operator_funcs_count = 0;
int _setters_count = 0;
int _getters_count = 0;
int _keyed_setters_count = 0;
int _keyed_getters_count = 0;
int _indexed_setters_count = 0;
int _indexed_getters_count = 0;
int _builtin_methods_count = 0;
int _constructors_count = 0;
int _utilities_count = 0;
int _gds_utilities_count = 0;
int _methods_count = 0;
int _lambdas_count = 0;
#ifdef TOOLS_ENABLED
Vector<StringName> arg_names;
Vector<Variant> default_arg_values;
#endif
int *_code_ptr = nullptr;
const int *_default_arg_ptr = nullptr;
mutable Variant *_constants_ptr = nullptr;
const StringName *_global_names_ptr = nullptr;
const Variant::ValidatedOperatorEvaluator *_operator_funcs_ptr = nullptr;
const Variant::ValidatedSetter *_setters_ptr = nullptr;
const Variant::ValidatedGetter *_getters_ptr = nullptr;
const Variant::ValidatedKeyedSetter *_keyed_setters_ptr = nullptr;
const Variant::ValidatedKeyedGetter *_keyed_getters_ptr = nullptr;
const Variant::ValidatedIndexedSetter *_indexed_setters_ptr = nullptr;
const Variant::ValidatedIndexedGetter *_indexed_getters_ptr = nullptr;
const Variant::ValidatedBuiltInMethod *_builtin_methods_ptr = nullptr;
const Variant::ValidatedConstructor *_constructors_ptr = nullptr;
const Variant::ValidatedUtilityFunction *_utilities_ptr = nullptr;
const GDScriptUtilityFunctions::FunctionPtr *_gds_utilities_ptr = nullptr;
MethodBind **_methods_ptr = nullptr;
GDScriptFunction **_lambdas_ptr = nullptr;
#ifdef DEBUG_ENABLED
CharString func_cname;
const char *_func_cname = nullptr;
Vector<String> operator_names;
Vector<String> setter_names;
Vector<String> getter_names;
@ -522,20 +497,6 @@ private:
Vector<String> constructors_names;
Vector<String> utilities_names;
Vector<String> gds_utilities_names;
#endif
List<StackDebug> stack_debug;
Variant _get_default_variant_for_data_type(const GDScriptDataType &p_data_type);
_FORCE_INLINE_ String _get_call_error(const Callable::CallError &p_err, const String &p_where, const Variant **argptrs) const;
friend class GDScriptLanguage;
SelfList<GDScriptFunction> function_list{ this };
#ifdef DEBUG_ENABLED
CharString func_cname;
const char *_func_cname = nullptr;
struct Profile {
StringName signature;
@ -549,9 +510,11 @@ private:
uint64_t last_frame_self_time = 0;
uint64_t last_frame_total_time = 0;
} profile;
#endif
_FORCE_INLINE_ String _get_call_error(const Callable::CallError &p_err, const String &p_where, const Variant **argptrs) const;
Variant _get_default_variant_for_data_type(const GDScriptDataType &p_data_type);
public:
static constexpr int MAX_CALL_DEPTH = 2048; // Limit to try to avoid crash because of a stack overflow.
@ -571,51 +534,24 @@ public:
Variant result;
};
_FORCE_INLINE_ StringName get_name() const { return name; }
_FORCE_INLINE_ StringName get_source() const { return source; }
_FORCE_INLINE_ GDScript *get_script() const { return _script; }
_FORCE_INLINE_ bool is_static() const { return _static; }
_FORCE_INLINE_ MethodInfo get_method_info() const { return method_info; }
_FORCE_INLINE_ Variant get_rpc_config() const { return rpc_config; }
_FORCE_INLINE_ int get_max_stack_size() const { return _stack_size; }
const int *get_code() const; //used for debug
int get_code_size() const;
Variant get_constant(int p_idx) const;
StringName get_global_name(int p_idx) const;
StringName get_name() const;
int get_max_stack_size() const;
int get_default_argument_count() const;
int get_default_argument_addr(int p_idx) const;
GDScriptDataType get_return_type() const;
GDScriptDataType get_argument_type(int p_idx) const;
GDScript *get_script() const { return _script; }
StringName get_source() const { return source; }
void debug_get_stack_member_state(int p_line, List<Pair<StringName, int>> *r_stackvars) const;
_FORCE_INLINE_ bool is_empty() const { return _code_size == 0; }
int get_argument_count() const { return _argument_count; }
StringName get_argument_name(int p_idx) const {
#ifdef TOOLS_ENABLED
ERR_FAIL_INDEX_V(p_idx, arg_names.size(), StringName());
return arg_names[p_idx];
#else
return StringName();
#endif
}
Variant get_default_argument(int p_idx) const {
ERR_FAIL_INDEX_V(p_idx, default_arguments.size(), Variant());
return default_arguments[p_idx];
}
#ifdef TOOLS_ENABLED
const Vector<Variant> &get_default_arg_values() const {
return default_arg_values;
}
#endif // TOOLS_ENABLED
Variant call(GDScriptInstance *p_instance, const Variant **p_args, int p_argcount, Callable::CallError &r_err, CallState *p_state = nullptr);
void debug_get_stack_member_state(int p_line, List<Pair<StringName, int>> *r_stackvars) const;
#ifdef DEBUG_ENABLED
void disassemble(const Vector<String> &p_code_lines) const;
#endif
_FORCE_INLINE_ const Variant get_rpc_config() const { return rpc_config; }
GDScriptFunction();
~GDScriptFunction();
};

View File

@ -4112,6 +4112,8 @@ bool GDScriptParser::export_annotations(const AnnotationNode *p_annotation, Node
}
variable->export_info.hint_string = enum_hint_string;
variable->export_info.usage |= PROPERTY_USAGE_CLASS_IS_ENUM;
variable->export_info.class_name = String(export_type.native_type).replace("::", ".");
} break;
default:
push_error(R"(Export type can only be built-in, a resource, a node, or an enum.)", variable);
@ -4370,6 +4372,104 @@ String GDScriptParser::DataType::to_string() const {
ERR_FAIL_V_MSG("<unresolved type>", "Kind set outside the enum range.");
}
PropertyInfo GDScriptParser::DataType::to_property_info(const String &p_name) const {
PropertyInfo result;
result.name = p_name;
result.usage = PROPERTY_USAGE_NONE;
if (!is_hard_type()) {
result.usage |= PROPERTY_USAGE_NIL_IS_VARIANT;
return result;
}
switch (kind) {
case BUILTIN:
result.type = builtin_type;
if (builtin_type == Variant::ARRAY && has_container_element_type()) {
const DataType *elem_type = container_element_type;
switch (elem_type->kind) {
case BUILTIN:
result.hint = PROPERTY_HINT_ARRAY_TYPE;
result.hint_string = Variant::get_type_name(elem_type->builtin_type);
break;
case NATIVE:
result.hint = PROPERTY_HINT_ARRAY_TYPE;
result.hint_string = elem_type->native_type;
break;
case SCRIPT:
result.hint = PROPERTY_HINT_ARRAY_TYPE;
if (elem_type->script_type.is_valid() && elem_type->script_type->get_global_name() != StringName()) {
result.hint_string = elem_type->script_type->get_global_name();
} else {
result.hint_string = elem_type->native_type;
}
break;
case CLASS:
result.hint = PROPERTY_HINT_ARRAY_TYPE;
if (elem_type->class_type != nullptr && elem_type->class_type->get_global_name() != StringName()) {
result.hint_string = elem_type->class_type->get_global_name();
} else {
result.hint_string = elem_type->native_type;
}
break;
case ENUM:
result.hint = PROPERTY_HINT_ARRAY_TYPE;
result.hint_string = String(elem_type->native_type).replace("::", ".");
break;
case VARIANT:
case RESOLVING:
case UNRESOLVED:
break;
}
}
break;
case NATIVE:
result.type = Variant::OBJECT;
if (is_meta_type) {
result.class_name = GDScriptNativeClass::get_class_static();
} else {
result.class_name = native_type;
}
break;
case SCRIPT:
result.type = Variant::OBJECT;
if (is_meta_type) {
result.class_name = script_type.is_valid() ? script_type->get_class() : Script::get_class_static();
} else if (script_type.is_valid() && script_type->get_global_name() != StringName()) {
result.class_name = script_type->get_global_name();
} else {
result.class_name = native_type;
}
break;
case CLASS:
result.type = Variant::OBJECT;
if (is_meta_type) {
result.class_name = GDScript::get_class_static();
} else if (class_type != nullptr && class_type->get_global_name() != StringName()) {
result.class_name = class_type->get_global_name();
} else {
result.class_name = native_type;
}
break;
case ENUM:
if (is_meta_type) {
result.type = Variant::DICTIONARY;
} else {
result.type = Variant::INT;
result.usage |= PROPERTY_USAGE_CLASS_IS_ENUM;
result.class_name = String(native_type).replace("::", ".");
}
break;
case VARIANT:
case RESOLVING:
case UNRESOLVED:
result.usage |= PROPERTY_USAGE_NIL_IS_VARIANT;
break;
}
return result;
}
static Variant::Type _variant_type_to_typed_array_element_type(Variant::Type p_type) {
switch (p_type) {
case Variant::PACKED_BYTE_ARRAY:

View File

@ -147,7 +147,9 @@ public:
_FORCE_INLINE_ bool has_no_type() const { return type_source == UNDETECTED; }
_FORCE_INLINE_ bool is_variant() const { return kind == VARIANT || kind == RESOLVING || kind == UNRESOLVED; }
_FORCE_INLINE_ bool is_hard_type() const { return type_source > INFERRED; }
String to_string() const;
PropertyInfo to_property_info(const String &p_name) const;
_FORCE_INLINE_ void set_container_element_type(const DataType &p_type) {
container_element_type = memnew(DataType(p_type));
@ -749,6 +751,10 @@ public:
bool resolved_interface = false;
bool resolved_body = false;
StringName get_global_name() const {
return (outer == nullptr && identifier != nullptr) ? identifier->name : StringName();
}
Member get_member(const StringName &p_name) const {
return members[members_indices[p_name]];
}
@ -836,8 +842,8 @@ public:
Variant rpc_config;
MethodInfo info;
LambdaNode *source_lambda = nullptr;
#ifdef TOOLS_ENABLED
Vector<Variant> default_arg_values;
#ifdef TOOLS_ENABLED
MemberDocData doc_data;
#endif // TOOLS_ENABLED
@ -1026,6 +1032,7 @@ public:
IdentifierNode *identifier = nullptr;
Vector<ParameterNode *> parameters;
HashMap<StringName, int> parameters_indices;
MethodInfo method_info;
#ifdef TOOLS_ENABLED
MemberDocData doc_data;
#endif // TOOLS_ENABLED

View File

@ -279,7 +279,7 @@ struct GDScriptUtilityFunctionsDefinitions {
Vector<StringName> sname;
while (p->_owner) {
sname.push_back(p->name);
sname.push_back(p->local_name);
p = p->_owner;
}
sname.reverse();

View File

@ -466,8 +466,8 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
err_file = "<built-in>";
}
String err_func = name;
if (p_instance && ObjectDB::get_instance(p_instance->owner_id) != nullptr && p_instance->script->is_valid() && !p_instance->script->name.is_empty()) {
err_func = p_instance->script->name + "." + err_func;
if (p_instance && ObjectDB::get_instance(p_instance->owner_id) != nullptr && p_instance->script->is_valid() && p_instance->script->local_name != StringName()) {
err_func = p_instance->script->local_name.operator String() + "." + err_func;
}
int err_line = _initial_line;
const char *err_text = "Stack overflow. Check for infinite recursion in your script.";
@ -3649,8 +3649,8 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
err_file = "<built-in>";
}
String err_func = name;
if (instance_valid_with_script && !p_instance->script->name.is_empty()) {
err_func = p_instance->script->name + "." + err_func;
if (instance_valid_with_script && p_instance->script->local_name != StringName()) {
err_func = p_instance->script->local_name.operator String() + "." + err_func;
}
int err_line = line;
if (err_text.is_empty()) {

View File

@ -0,0 +1,125 @@
class_name TestMemberInfo
class MyClass:
pass
enum MyEnum {}
static var test_static_var_untyped
static var test_static_var_weak_null = null
static var test_static_var_weak_int = 1
static var test_static_var_hard_int: int
var test_var_untyped
var test_var_weak_null = null
var test_var_weak_int = 1
@export var test_var_weak_int_exported = 1
var test_var_weak_variant_type = TYPE_NIL
@export var test_var_weak_variant_type_exported = TYPE_NIL
var test_var_hard_variant: Variant
var test_var_hard_int: int
var test_var_hard_variant_type: Variant.Type
@export var test_var_hard_variant_type_exported: Variant.Type
var test_var_hard_node_process_mode: Node.ProcessMode
var test_var_hard_my_enum: MyEnum
var test_var_hard_array: Array
var test_var_hard_array_int: Array[int]
var test_var_hard_array_variant_type: Array[Variant.Type]
var test_var_hard_array_node_process_mode: Array[Node.ProcessMode]
var test_var_hard_array_my_enum: Array[MyEnum]
var test_var_hard_array_resource: Array[Resource]
var test_var_hard_array_this: Array[TestMemberInfo]
var test_var_hard_array_my_class: Array[MyClass]
var test_var_hard_resource: Resource
var test_var_hard_this: TestMemberInfo
var test_var_hard_my_class: MyClass
static func test_static_func(): pass
func test_func_implicit_void(): pass
func test_func_explicit_void() -> void: pass
func test_func_weak_null(): return null
func test_func_weak_int(): return 1
func test_func_hard_variant() -> Variant: return null
func test_func_hard_int() -> int: return 1
func test_func_args_1(_a: int, _b: Array[int], _c: int = 1, _d = 2): pass
func test_func_args_2(_a = 1, _b = _a, _c = [2], _d = 3): pass
signal test_signal_1()
signal test_signal_2(a: Variant, b)
signal test_signal_3(a: int, b: Array[int])
signal test_signal_4(a: Variant.Type, b: Array[Variant.Type])
signal test_signal_5(a: MyEnum, b: Array[MyEnum])
signal test_signal_6(a: Resource, b: Array[Resource])
signal test_signal_7(a: TestMemberInfo, b: Array[TestMemberInfo])
signal test_signal_8(a: MyClass, b: Array[MyClass])
func test():
var script: Script = get_script()
for property in script.get_property_list():
if str(property.name).begins_with("test_"):
if not (property.usage & PROPERTY_USAGE_SCRIPT_VARIABLE):
print("Error: Missing `PROPERTY_USAGE_SCRIPT_VARIABLE` flag.")
print("static var ", property.name, ": ", get_type(property))
for property in get_property_list():
if str(property.name).begins_with("test_"):
if not (property.usage & PROPERTY_USAGE_SCRIPT_VARIABLE):
print("Error: Missing `PROPERTY_USAGE_SCRIPT_VARIABLE` flag.")
print("var ", property.name, ": ", get_type(property))
for method in get_method_list():
if str(method.name).begins_with("test_"):
print(get_signature(method))
for method in get_signal_list():
if str(method.name).begins_with("test_"):
print(get_signature(method, true))
func get_type(property: Dictionary, is_return: bool = false) -> String:
match property.type:
TYPE_NIL:
if property.usage & PROPERTY_USAGE_NIL_IS_VARIANT:
return "Variant"
return "void" if is_return else "null"
TYPE_BOOL:
return "bool"
TYPE_INT:
if property.usage & PROPERTY_USAGE_CLASS_IS_ENUM:
return property.class_name
return "int"
TYPE_STRING:
return "String"
TYPE_DICTIONARY:
return "Dictionary"
TYPE_ARRAY:
if property.hint == PROPERTY_HINT_ARRAY_TYPE:
return "Array[%s]" % property.hint_string
return "Array"
TYPE_OBJECT:
if not str(property.class_name).is_empty():
return property.class_name
return "Object"
return "<error>"
func get_signature(method: Dictionary, is_signal: bool = false) -> String:
var result: String = ""
if method.flags & METHOD_FLAG_STATIC:
result += "static "
result += ("signal " if is_signal else "func ") + method.name + "("
var args: Array[Dictionary] = method.args
var default_args: Array = method.default_args
var mandatory_argc: int = args.size() - default_args.size()
for i in args.size():
if i > 0:
result += ", "
var arg: Dictionary = args[i]
result += arg.name + ": " + get_type(arg)
if i >= mandatory_argc:
result += " = " + var_to_str(default_args[i - mandatory_argc])
result += ")"
if is_signal:
if get_type(method.return, true) != "void":
print("Error: Signal return type must be `void`.")
else:
result += " -> " + get_type(method.return, true)
return result

View File

@ -0,0 +1,45 @@
GDTEST_OK
static var test_static_var_untyped: Variant
static var test_static_var_weak_null: Variant
static var test_static_var_weak_int: Variant
static var test_static_var_hard_int: int
var test_var_untyped: Variant
var test_var_weak_null: Variant
var test_var_weak_int: Variant
var test_var_weak_int_exported: int
var test_var_weak_variant_type: Variant
var test_var_weak_variant_type_exported: Variant.Type
var test_var_hard_variant: Variant
var test_var_hard_int: int
var test_var_hard_variant_type: Variant.Type
var test_var_hard_variant_type_exported: Variant.Type
var test_var_hard_node_process_mode: Node.ProcessMode
var test_var_hard_my_enum: TestMemberInfo.MyEnum
var test_var_hard_array: Array
var test_var_hard_array_int: Array[int]
var test_var_hard_array_variant_type: Array[Variant.Type]
var test_var_hard_array_node_process_mode: Array[Node.ProcessMode]
var test_var_hard_array_my_enum: Array[TestMemberInfo.MyEnum]
var test_var_hard_array_resource: Array[Resource]
var test_var_hard_array_this: Array[TestMemberInfo]
var test_var_hard_array_my_class: Array[RefCounted]
var test_var_hard_resource: Resource
var test_var_hard_this: TestMemberInfo
var test_var_hard_my_class: RefCounted
static func test_static_func() -> void
func test_func_implicit_void() -> void
func test_func_explicit_void() -> void
func test_func_weak_null() -> Variant
func test_func_weak_int() -> Variant
func test_func_hard_variant() -> Variant
func test_func_hard_int() -> int
func test_func_args_1(_a: int, _b: Array[int], _c: int = 1, _d: Variant = 2) -> void
func test_func_args_2(_a: Variant = 1, _b: Variant = null, _c: Variant = null, _d: Variant = 3) -> void
signal test_signal_1()
signal test_signal_2(a: Variant, b: Variant)
signal test_signal_3(a: int, b: Array[int])
signal test_signal_4(a: Variant.Type, b: Array[Variant.Type])
signal test_signal_5(a: TestMemberInfo.MyEnum, b: Array[TestMemberInfo.MyEnum])
signal test_signal_6(a: Resource, b: Array[Resource])
signal test_signal_7(a: TestMemberInfo, b: Array[TestMemberInfo])
signal test_signal_8(a: RefCounted, b: Array[RefCounted])

View File

@ -138,12 +138,13 @@ static void recursively_disassemble_functions(const Ref<GDScript> script, const
for (const KeyValue<StringName, GDScriptFunction *> &E : script->get_member_functions()) {
const GDScriptFunction *func = E.value;
String signature = "Disassembling " + func->get_name().operator String() + "(";
for (int i = 0; i < func->get_argument_count(); i++) {
const MethodInfo &mi = func->get_method_info();
String signature = "Disassembling " + mi.name + "(";
for (int i = 0; i < mi.arguments.size(); i++) {
if (i > 0) {
signature += ", ";
}
signature += func->get_argument_name(i);
signature += mi.arguments[i].name;
}
print_line(signature + ")");
#ifdef TOOLS_ENABLED
@ -156,7 +157,7 @@ static void recursively_disassemble_functions(const Ref<GDScript> script, const
for (const KeyValue<StringName, Ref<GDScript>> &F : script->get_subclasses()) {
const Ref<GDScript> inner_script = F.value;
print_line("");
print_line(vformat("Inner Class: %s", inner_script->get_script_class_name()));
print_line(vformat("Inner Class: %s", inner_script->get_local_name()));
print_line("");
recursively_disassemble_functions(inner_script, p_lines);
}