瀏覽代碼

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

GDScript: Fix `get_*_list()` methods return incorrect info
Rémi Verschelde 1 年之前
父節點
當前提交
13f0ab88f2

+ 6 - 3
modules/gdscript/editor/gdscript_docgen.cpp

@@ -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";
 				}

+ 35 - 92
modules/gdscript/gdscript.cpp

@@ -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;
 	}

+ 6 - 8
modules/gdscript/gdscript.h

@@ -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();

+ 6 - 4
modules/gdscript/gdscript_analyzer.cpp

@@ -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) {

+ 0 - 3
modules/gdscript/gdscript_byte_codegen.cpp

@@ -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) {

+ 26 - 23
modules/gdscript/gdscript_compiler.cpp

@@ -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;

+ 0 - 36
modules/gdscript/gdscript_function.cpp

@@ -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
 }

+ 62 - 126
modules/gdscript/gdscript_function.h

@@ -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;
-
-	GDScript *_script = nullptr;
+	SelfList<GDScriptFunction> function_list{ this };
+	mutable Variant nil;
+	HashMap<int, Variant::Type> temporary_slots;
+	List<StackDebug> stack_debug;
 
-	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();
 };

+ 100 - 0
modules/gdscript/gdscript_parser.cpp

@@ -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:

+ 8 - 1
modules/gdscript/gdscript_parser.h

@@ -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

+ 1 - 1
modules/gdscript/gdscript_utility_functions.cpp

@@ -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();

+ 4 - 4
modules/gdscript/gdscript_vm.cpp

@@ -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()) {

+ 125 - 0
modules/gdscript/tests/scripts/runtime/features/member_info.gd

@@ -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

+ 45 - 0
modules/gdscript/tests/scripts/runtime/features/member_info.out

@@ -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])

+ 5 - 4
modules/gdscript/tests/test_gdscript.cpp

@@ -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);
 	}