Quellcode durchsuchen

Use type information to enable GDScript introspection

This makes the Script API provide accurate information when requesting
property or method info.
George Marques vor 7 Jahren
Ursprung
Commit
e3d72d14ff

+ 8 - 12
modules/gdscript/gdscript.cpp

@@ -220,16 +220,14 @@ void GDScript::_placeholder_erased(PlaceHolderScriptInstance *p_placeholder) {
 void GDScript::get_script_method_list(List<MethodInfo> *p_list) const {
 
 	for (const Map<StringName, GDScriptFunction *>::Element *E = member_functions.front(); E; E = E->next()) {
+		GDScriptFunction *func = E->get();
 		MethodInfo mi;
 		mi.name = E->key();
-		for (int i = 0; i < E->get()->get_argument_count(); i++) {
-			PropertyInfo arg;
-			arg.type = Variant::NIL; //variant
-			arg.name = E->get()->get_argument_name(i);
-			mi.arguments.push_back(arg);
+		for (int i = 0; i < func->get_argument_count(); i++) {
+			mi.arguments.push_back(func->get_argument_type(i));
 		}
 
-		mi.return_val.name = "Variant";
+		mi.return_val = func->get_return_type();
 		p_list->push_back(mi);
 	}
 }
@@ -277,16 +275,14 @@ MethodInfo GDScript::get_method_info(const StringName &p_method) const {
 	if (!E)
 		return MethodInfo();
 
+	GDScriptFunction *func = E->get();
 	MethodInfo mi;
 	mi.name = E->key();
-	for (int i = 0; i < E->get()->get_argument_count(); i++) {
-		PropertyInfo arg;
-		arg.type = Variant::NIL; //variant
-		arg.name = E->get()->get_argument_name(i);
-		mi.arguments.push_back(arg);
+	for (int i = 0; i < func->get_argument_count(); i++) {
+		mi.arguments.push_back(func->get_argument_type(i));
 	}
 
-	mi.return_val.name = "Variant";
+	mi.return_val = func->get_return_type();
 	return mi;
 }
 

+ 1 - 0
modules/gdscript/gdscript.h

@@ -152,6 +152,7 @@ public:
 	}
 	const Map<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; }
 
 	virtual bool has_script_signal(const StringName &p_signal) const;
 	virtual void get_script_signal_list(List<MethodInfo> *r_signals) const;

+ 61 - 39
modules/gdscript/gdscript_compiler.cpp

@@ -111,23 +111,50 @@ bool GDScriptCompiler::_create_binary_operator(CodeGen &codegen, const GDScriptP
 	return true;
 }
 
-/*
-int GDScriptCompiler::_parse_subexpression(CodeGen& codegen,const GDScriptParser::Node *p_expression) {
-
+GDScriptDataType GDScriptCompiler::_gdtype_from_datatype(const GDScriptParser::DataType &p_datatype) const {
+	if (!p_datatype.has_type) {
+		return GDScriptDataType();
+	}
 
-	int ret = _parse_expression(codegen,p_expression);
-	if (ret<0)
-		return ret;
+	GDScriptDataType result;
+	result.has_type = true;
 
-	if (ret&(GDScriptFunction::ADDR_TYPE_STACK<<GDScriptFunction::ADDR_BITS)) {
-		codegen.stack_level++;
-		codegen.check_max_stack_level();
-		//stack was used, keep value
+	switch (p_datatype.kind) {
+		case GDScriptParser::DataType::BUILTIN: {
+			result.kind = GDScriptDataType::BUILTIN;
+			result.builtin_type = p_datatype.builtin_type;
+		} break;
+		case GDScriptParser::DataType::NATIVE: {
+			result.kind = GDScriptDataType::NATIVE;
+			result.native_type = p_datatype.native_type;
+		} break;
+		case GDScriptParser::DataType::SCRIPT: {
+			result.kind = GDScriptDataType::SCRIPT;
+			result.script_type = p_datatype.script_type;
+			result.native_type = result.script_type->get_instance_base_type();
+		}
+		case GDScriptParser::DataType::GDSCRIPT: {
+			result.kind = GDScriptDataType::GDSCRIPT;
+			result.script_type = p_datatype.script_type;
+			result.native_type = result.script_type->get_instance_base_type();
+		} break;
+		case GDScriptParser::DataType::CLASS: {
+			result.kind = GDScriptDataType::GDSCRIPT;
+			if (p_datatype.class_type->name == StringName()) {
+				result.script_type = Ref<GDScript>(main_script);
+			} else {
+				result.script_type = class_map[p_datatype.class_type->name];
+			}
+			result.native_type = result.script_type->get_instance_base_type();
+		} break;
+		default: {
+			ERR_PRINT("Parser bug: converting unresolved type.");
+			result.has_type = false;
+		}
 	}
 
-	return ret;
+	return result;
 }
-*/
 
 int GDScriptCompiler::_parse_assign_right_expression(CodeGen &codegen, const GDScriptParser::OperatorNode *p_expression, int p_stack_level) {
 
@@ -1883,41 +1910,41 @@ Error GDScriptCompiler::_parse_class_level(GDScript *p_script, GDScript *p_owner
 	for (int i = 0; i < p_class->variables.size(); i++) {
 
 		StringName name = p_class->variables[i].identifier;
-		if (p_script->member_indices.has(name)) {
-			_set_error("Member '" + name + "' already exists (in current or parent class)", p_class);
-			return ERR_ALREADY_EXISTS;
-		}
-		if (_is_class_member_property(p_script, name)) {
-			_set_error("Member '" + name + "' already exists as a class property.", p_class);
-			return ERR_ALREADY_EXISTS;
-		}
 
-		if (p_class->variables[i]._export.type != Variant::NIL) {
+		GDScript::MemberInfo minfo;
+		minfo.index = p_script->member_indices.size();
+		minfo.setter = p_class->variables[i].setter;
+		minfo.getter = p_class->variables[i].getter;
+		minfo.rpc_mode = p_class->variables[i].rpc_mode;
+		minfo.data_type = _gdtype_from_datatype(p_class->variables[i].data_type);
+
+		PropertyInfo prop_info = minfo.data_type;
+		prop_info.name = name;
+		PropertyInfo export_info = p_class->variables[i]._export;
+
+		if (export_info.type != Variant::NIL) {
 
-			p_script->member_info[name] = p_class->variables[i]._export;
+			if (!minfo.data_type.has_type) {
+				prop_info.type = export_info.type;
+				prop_info.class_name = export_info.class_name;
+			}
+			prop_info.hint = export_info.hint;
+			prop_info.hint_string = export_info.hint_string;
+			prop_info.usage = export_info.usage;
 #ifdef TOOLS_ENABLED
 			if (p_class->variables[i].default_value.get_type() != Variant::NIL) {
-
 				p_script->member_default_values[name] = p_class->variables[i].default_value;
 			}
 #endif
 		} else {
-
-			p_script->member_info[name] = PropertyInfo(Variant::NIL, name, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_SCRIPT_VARIABLE);
+			prop_info.usage = PROPERTY_USAGE_SCRIPT_VARIABLE;
 		}
 
-		//int new_idx = p_script->member_indices.size();
-		GDScript::MemberInfo minfo;
-		minfo.index = p_script->member_indices.size();
-		minfo.setter = p_class->variables[i].setter;
-		minfo.getter = p_class->variables[i].getter;
-		minfo.rpc_mode = p_class->variables[i].rpc_mode;
-
+		p_script->member_info[name] = prop_info;
 		p_script->member_indices[name] = minfo;
 		p_script->members.insert(name);
 
 #ifdef TOOLS_ENABLED
-
 		p_script->member_lines[name] = p_class->variables[i].line;
 #endif
 	}
@@ -1928,15 +1955,9 @@ Error GDScriptCompiler::_parse_class_level(GDScript *p_script, GDScript *p_owner
 
 		ERR_CONTINUE(E->get().expression->type != GDScriptParser::Node::TYPE_CONSTANT);
 
-		if (_is_class_member_property(p_script, name)) {
-			_set_error("Member '" + name + "' already exists as a class property.", p_class);
-			return ERR_ALREADY_EXISTS;
-		}
-
 		GDScriptParser::ConstantNode *constant = static_cast<GDScriptParser::ConstantNode *>(E->get().expression);
 
 		p_script->constants.insert(name, constant->value);
-//p_script->constants[constant->value].make_const();
 #ifdef TOOLS_ENABLED
 
 		p_script->member_lines[name] = E->get().expression->line;
@@ -2144,6 +2165,7 @@ Error GDScriptCompiler::compile(const GDScriptParser *p_parser, GDScript *p_scri
 	err_column = -1;
 	error = "";
 	parser = p_parser;
+	main_script = p_script;
 	const GDScriptParser::Node *root = parser->get_parse_tree();
 	ERR_FAIL_COND_V(root->type != GDScriptParser::Node::TYPE_CLASS, ERR_INVALID_DATA);
 

+ 3 - 0
modules/gdscript/gdscript_compiler.h

@@ -41,6 +41,7 @@ class GDScriptCompiler {
 	Map<StringName, Ref<GDScript> > class_map;
 	Set<StringName> parsed_classes;
 	Set<StringName> parsing_classes;
+	GDScript *main_script;
 	struct CodeGen {
 
 		GDScript *script;
@@ -142,6 +143,8 @@ class GDScriptCompiler {
 	bool _create_unary_operator(CodeGen &codegen, const GDScriptParser::OperatorNode *on, Variant::Operator op, int p_stack_level);
 	bool _create_binary_operator(CodeGen &codegen, const GDScriptParser::OperatorNode *on, Variant::Operator op, int p_stack_level, bool p_initializer = false);
 
+	GDScriptDataType _gdtype_from_datatype(const GDScriptParser::DataType &p_datatype) const;
+
 	int _parse_assign_right_expression(CodeGen &codegen, const GDScriptParser::OperatorNode *p_expression, int p_stack_level);
 	int _parse_expression(CodeGen &codegen, const GDScriptParser::Node *p_expression, int p_stack_level, bool p_root = false, bool p_initializer = false);
 	Error _parse_block(CodeGen &codegen, const GDScriptParser::BlockNode *p_block, int p_stack_level = 0, int p_break_addr = -1, int p_continue_addr = -1);

+ 50 - 0
modules/gdscript/gdscript_parser.cpp

@@ -4957,6 +4957,56 @@ void GDScriptParser::_determine_inheritance(ClassNode *p_class) {
 	}
 }
 
+String GDScriptParser::DataType::to_string() const {
+	if (!has_type) return "var";
+	switch (kind) {
+		case BUILTIN: {
+			if (builtin_type == Variant::NIL) return "null";
+			return Variant::get_type_name(builtin_type);
+		} break;
+		case NATIVE: {
+			if (is_meta_type) {
+				return "GDScriptNativeClass";
+			}
+			return native_type.operator String();
+		} break;
+
+		case GDSCRIPT: {
+			Ref<GDScript> gds = script_type;
+			const String &gds_class = gds->get_script_class_name();
+			if (!gds_class.empty()) {
+				return gds_class;
+			}
+		} // fallthrough
+		case SCRIPT: {
+			if (is_meta_type) {
+				return script_type->get_class_name().operator String();
+			}
+			String name = script_type->get_name();
+			if (name != String()) {
+				return name;
+			}
+			name = script_type->get_path().get_file();
+			if (name != String()) {
+				return name;
+			}
+			return native_type.operator String();
+		} break;
+		case CLASS: {
+			ERR_FAIL_COND_V(!class_type, String());
+			if (is_meta_type) {
+				return "GDScript";
+			}
+			if (class_type->name == StringName()) {
+				return "self";
+			}
+			return class_type->name.operator String();
+		} break;
+	}
+
+	return "Unresolved";
+}
+
 bool GDScriptParser::_parse_type(DataType &r_type, bool p_can_be_void) {
 	tokenizer->advance();
 	r_type.has_type = true;

+ 1 - 42
modules/gdscript/gdscript_parser.h

@@ -62,48 +62,7 @@ public:
 		Ref<Script> script_type;
 		ClassNode *class_type;
 
-		String to_string() const {
-			if (!has_type) return "var";
-			switch (kind) {
-				case BUILTIN: {
-					if (builtin_type == Variant::NIL) return "null";
-					return Variant::get_type_name(builtin_type);
-				} break;
-				case NATIVE: {
-					if (is_meta_type) {
-						return "GDScriptNativeClass";
-					}
-					return native_type.operator String();
-				} break;
-				case SCRIPT:
-				case GDSCRIPT: {
-					if (is_meta_type) {
-						return script_type->get_class_name().operator String();
-					}
-					String name = script_type->get_name();
-					if (name != String()) {
-						return name;
-					}
-					name = script_type->get_path().get_file();
-					if (name != String()) {
-						return name;
-					}
-					return native_type.operator String();
-				} break;
-				case CLASS: {
-					ERR_FAIL_COND_V(!class_type, String());
-					if (is_meta_type) {
-						return "GDScript";
-					}
-					if (class_type->name == StringName()) {
-						return "self";
-					}
-					return class_type->name.operator String();
-				} break;
-			}
-
-			return "Unresolved";
-		}
+		String to_string() const;
 
 		bool operator==(const DataType &other) const {
 			if (!has_type || !other.has_type) {