Răsfoiți Sursa

GDScript: Add support for builtin static method calls

George Marques 4 ani în urmă
părinte
comite
ec783dd885

+ 16 - 2
modules/gdscript/gdscript_analyzer.cpp

@@ -2078,9 +2078,23 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool is_awa
 			mark_node_unsafe(p_call);
 			mark_node_unsafe(p_call);
 			return;
 			return;
 		}
 		}
-		reduce_expression(subscript->base);
+		if (subscript->attribute == nullptr) {
+			// Invalid call. Error already sent in parser.
+			p_call->set_datatype(call_type);
+			mark_node_unsafe(p_call);
+			return;
+		}
 
 
-		base_type = subscript->base->get_datatype();
+		GDScriptParser::IdentifierNode *base_id = nullptr;
+		if (subscript->base->type == GDScriptParser::Node::IDENTIFIER) {
+			base_id = static_cast<GDScriptParser::IdentifierNode *>(subscript->base);
+		}
+		if (base_id && GDScriptParser::get_builtin_type(base_id->name) < Variant::VARIANT_MAX) {
+			base_type = make_builtin_meta_type(GDScriptParser::get_builtin_type(base_id->name));
+		} else {
+			reduce_expression(subscript->base);
+			base_type = subscript->base->get_datatype();
+		}
 	} else {
 	} else {
 		// Invalid call. Error already sent in parser.
 		// Invalid call. Error already sent in parser.
 		// TODO: Could check if Callable here too.
 		// TODO: Could check if Callable here too.

+ 50 - 0
modules/gdscript/gdscript_byte_codegen.cpp

@@ -1017,6 +1017,56 @@ void GDScriptByteCodeGenerator::write_call_builtin_type(const Address &p_target,
 	append(Variant::get_validated_builtin_method(p_type, p_method));
 	append(Variant::get_validated_builtin_method(p_type, p_method));
 }
 }
 
 
+void GDScriptByteCodeGenerator::write_call_builtin_type_static(const Address &p_target, Variant::Type p_type, const StringName &p_method, const Vector<Address> &p_arguments) {
+	bool is_validated = false;
+
+	// Check if all types are correct.
+	if (Variant::is_builtin_method_vararg(p_type, p_method)) {
+		is_validated = true; // Vararg works fine with any argument, since they can be any type.
+	} else if (p_arguments.size() == Variant::get_builtin_method_argument_count(p_type, p_method)) {
+		bool all_types_exact = true;
+		for (int i = 0; i < p_arguments.size(); i++) {
+			if (!IS_BUILTIN_TYPE(p_arguments[i], Variant::get_builtin_method_argument_type(p_type, p_method, i))) {
+				all_types_exact = false;
+				break;
+			}
+		}
+
+		is_validated = all_types_exact;
+	}
+
+	if (!is_validated) {
+		// Perform regular call.
+		append(GDScriptFunction::OPCODE_CALL_BUILTIN_STATIC, p_arguments.size() + 1);
+		for (int i = 0; i < p_arguments.size(); i++) {
+			append(p_arguments[i]);
+		}
+		append(p_target);
+		append(p_type);
+		append(p_method);
+		append(p_arguments.size());
+		return;
+	}
+
+	if (p_target.mode == Address::TEMPORARY) {
+		Variant::Type result_type = Variant::get_builtin_method_return_type(p_type, p_method);
+		Variant::Type temp_type = temporaries[p_target.address].type;
+		if (result_type != temp_type) {
+			write_type_adjust(p_target, result_type);
+		}
+	}
+
+	append(GDScriptFunction::OPCODE_CALL_BUILTIN_TYPE_VALIDATED, 2 + p_arguments.size());
+
+	for (int i = 0; i < p_arguments.size(); i++) {
+		append(p_arguments[i]);
+	}
+	append(Address()); // No base since it's static.
+	append(p_target);
+	append(p_arguments.size());
+	append(Variant::get_validated_builtin_method(p_type, p_method));
+}
+
 void GDScriptByteCodeGenerator::write_call_method_bind(const Address &p_target, const Address &p_base, MethodBind *p_method, const Vector<Address> &p_arguments) {
 void GDScriptByteCodeGenerator::write_call_method_bind(const Address &p_target, const Address &p_base, MethodBind *p_method, const Vector<Address> &p_arguments) {
 	append(p_target.mode == Address::NIL ? GDScriptFunction::OPCODE_CALL_METHOD_BIND : GDScriptFunction::OPCODE_CALL_METHOD_BIND_RET, 2 + p_arguments.size());
 	append(p_target.mode == Address::NIL ? GDScriptFunction::OPCODE_CALL_METHOD_BIND : GDScriptFunction::OPCODE_CALL_METHOD_BIND_RET, 2 + p_arguments.size());
 	for (int i = 0; i < p_arguments.size(); i++) {
 	for (int i = 0; i < p_arguments.size(); i++) {

+ 1 - 0
modules/gdscript/gdscript_byte_codegen.h

@@ -461,6 +461,7 @@ public:
 	virtual void write_call_utility(const Address &p_target, const StringName &p_function, const Vector<Address> &p_arguments) override;
 	virtual void write_call_utility(const Address &p_target, const StringName &p_function, const Vector<Address> &p_arguments) override;
 	virtual void write_call_gdscript_utility(const Address &p_target, GDScriptUtilityFunctions::FunctionPtr p_function, const Vector<Address> &p_arguments) override;
 	virtual void write_call_gdscript_utility(const Address &p_target, GDScriptUtilityFunctions::FunctionPtr p_function, const Vector<Address> &p_arguments) override;
 	virtual void write_call_builtin_type(const Address &p_target, const Address &p_base, Variant::Type p_type, const StringName &p_method, const Vector<Address> &p_arguments) override;
 	virtual void write_call_builtin_type(const Address &p_target, const Address &p_base, Variant::Type p_type, const StringName &p_method, const Vector<Address> &p_arguments) override;
+	virtual void write_call_builtin_type_static(const Address &p_target, Variant::Type p_type, const StringName &p_method, const Vector<Address> &p_arguments) override;
 	virtual void write_call_method_bind(const Address &p_target, const Address &p_base, MethodBind *p_method, const Vector<Address> &p_arguments) override;
 	virtual void write_call_method_bind(const Address &p_target, const Address &p_base, MethodBind *p_method, const Vector<Address> &p_arguments) override;
 	virtual void write_call_ptrcall(const Address &p_target, const Address &p_base, MethodBind *p_method, const Vector<Address> &p_arguments) override;
 	virtual void write_call_ptrcall(const Address &p_target, const Address &p_base, MethodBind *p_method, const Vector<Address> &p_arguments) override;
 	virtual void write_call_self(const Address &p_target, const StringName &p_function_name, const Vector<Address> &p_arguments) override;
 	virtual void write_call_self(const Address &p_target, const StringName &p_function_name, const Vector<Address> &p_arguments) override;

+ 1 - 0
modules/gdscript/gdscript_codegen.h

@@ -122,6 +122,7 @@ public:
 	virtual void write_call_utility(const Address &p_target, const StringName &p_function, const Vector<Address> &p_arguments) = 0;
 	virtual void write_call_utility(const Address &p_target, const StringName &p_function, const Vector<Address> &p_arguments) = 0;
 	virtual void write_call_gdscript_utility(const Address &p_target, GDScriptUtilityFunctions::FunctionPtr p_function, const Vector<Address> &p_arguments) = 0;
 	virtual void write_call_gdscript_utility(const Address &p_target, GDScriptUtilityFunctions::FunctionPtr p_function, const Vector<Address> &p_arguments) = 0;
 	virtual void write_call_builtin_type(const Address &p_target, const Address &p_base, Variant::Type p_type, const StringName &p_method, const Vector<Address> &p_arguments) = 0;
 	virtual void write_call_builtin_type(const Address &p_target, const Address &p_base, Variant::Type p_type, const StringName &p_method, const Vector<Address> &p_arguments) = 0;
+	virtual void write_call_builtin_type_static(const Address &p_target, Variant::Type p_type, const StringName &p_method, const Vector<Address> &p_arguments) = 0;
 	virtual void write_call_method_bind(const Address &p_target, const Address &p_base, MethodBind *p_method, const Vector<Address> &p_arguments) = 0;
 	virtual void write_call_method_bind(const Address &p_target, const Address &p_base, MethodBind *p_method, const Vector<Address> &p_arguments) = 0;
 	virtual void write_call_ptrcall(const Address &p_target, const Address &p_base, MethodBind *p_method, const Vector<Address> &p_arguments) = 0;
 	virtual void write_call_ptrcall(const Address &p_target, const Address &p_base, MethodBind *p_method, const Vector<Address> &p_arguments) = 0;
 	virtual void write_call_self(const Address &p_target, const StringName &p_function_name, const Vector<Address> &p_arguments) = 0;
 	virtual void write_call_self(const Address &p_target, const StringName &p_function_name, const Vector<Address> &p_arguments) = 0;

+ 32 - 27
modules/gdscript/gdscript_compiler.cpp

@@ -537,39 +537,44 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
 						const GDScriptParser::SubscriptNode *subscript = static_cast<const GDScriptParser::SubscriptNode *>(call->callee);
 						const GDScriptParser::SubscriptNode *subscript = static_cast<const GDScriptParser::SubscriptNode *>(call->callee);
 
 
 						if (subscript->is_attribute) {
 						if (subscript->is_attribute) {
-							GDScriptCodeGenerator::Address base = _parse_expression(codegen, r_error, subscript->base);
-							if (r_error) {
-								return GDScriptCodeGenerator::Address();
-							}
-							if (within_await) {
-								gen->write_call_async(result, base, call->function_name, arguments);
-							} else if (base.type.has_type && base.type.kind != GDScriptDataType::BUILTIN) {
-								// Native method, use faster path.
-								StringName class_name;
-								if (base.type.kind == GDScriptDataType::NATIVE) {
-									class_name = base.type.native_type;
-								} else {
-									class_name = base.type.native_type == StringName() ? base.type.script_type->get_instance_base_type() : base.type.native_type;
+							// May be static built-in method call.
+							if (!call->is_super && subscript->base->type == GDScriptParser::Node::IDENTIFIER && GDScriptParser::get_builtin_type(static_cast<GDScriptParser::IdentifierNode *>(subscript->base)->name) < Variant::VARIANT_MAX) {
+								gen->write_call_builtin_type_static(result, GDScriptParser::get_builtin_type(static_cast<GDScriptParser::IdentifierNode *>(subscript->base)->name), subscript->attribute->name, arguments);
+							} else {
+								GDScriptCodeGenerator::Address base = _parse_expression(codegen, r_error, subscript->base);
+								if (r_error) {
+									return GDScriptCodeGenerator::Address();
 								}
 								}
-								if (ClassDB::class_exists(class_name) && ClassDB::has_method(class_name, call->function_name)) {
-									MethodBind *method = ClassDB::get_method(class_name, call->function_name);
-									if (_have_exact_arguments(method, arguments)) {
-										// Exact arguments, use ptrcall.
-										gen->write_call_ptrcall(result, base, method, arguments);
+								if (within_await) {
+									gen->write_call_async(result, base, call->function_name, arguments);
+								} else if (base.type.has_type && base.type.kind != GDScriptDataType::BUILTIN) {
+									// Native method, use faster path.
+									StringName class_name;
+									if (base.type.kind == GDScriptDataType::NATIVE) {
+										class_name = base.type.native_type;
 									} else {
 									} else {
-										// Not exact arguments, but still can use method bind call.
-										gen->write_call_method_bind(result, base, method, arguments);
+										class_name = base.type.native_type == StringName() ? base.type.script_type->get_instance_base_type() : base.type.native_type;
 									}
 									}
+									if (ClassDB::class_exists(class_name) && ClassDB::has_method(class_name, call->function_name)) {
+										MethodBind *method = ClassDB::get_method(class_name, call->function_name);
+										if (_have_exact_arguments(method, arguments)) {
+											// Exact arguments, use ptrcall.
+											gen->write_call_ptrcall(result, base, method, arguments);
+										} else {
+											// Not exact arguments, but still can use method bind call.
+											gen->write_call_method_bind(result, base, method, arguments);
+										}
+									} else {
+										gen->write_call(result, base, call->function_name, arguments);
+									}
+								} else if (base.type.has_type && base.type.kind == GDScriptDataType::BUILTIN) {
+									gen->write_call_builtin_type(result, base, base.type.builtin_type, call->function_name, arguments);
 								} else {
 								} else {
 									gen->write_call(result, base, call->function_name, arguments);
 									gen->write_call(result, base, call->function_name, arguments);
 								}
 								}
-							} else if (base.type.has_type && base.type.kind == GDScriptDataType::BUILTIN) {
-								gen->write_call_builtin_type(result, base, base.type.builtin_type, call->function_name, arguments);
-							} else {
-								gen->write_call(result, base, call->function_name, arguments);
-							}
-							if (base.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
-								gen->pop_temporary();
+								if (base.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
+									gen->pop_temporary();
+								}
 							}
 							}
 						} else {
 						} else {
 							_set_error("Cannot call something that isn't a function.", call->callee);
 							_set_error("Cannot call something that isn't a function.", call->callee);

+ 22 - 0
modules/gdscript/gdscript_disassembler.cpp

@@ -542,6 +542,28 @@ void GDScriptFunction::disassemble(const Vector<String> &p_code_lines) const {
 
 
 				incr = 5 + argc;
 				incr = 5 + argc;
 			} break;
 			} break;
+			case OPCODE_CALL_BUILTIN_STATIC: {
+				Variant::Type type = (Variant::Type)_code_ptr[ip + 1 + instr_var_args];
+				int argc = _code_ptr[ip + 3 + instr_var_args];
+
+				text += "call built-in method static ";
+				text += DADDR(1 + argc);
+				text += " = ";
+				text += Variant::get_type_name(type);
+				text += ".";
+				text += _global_names_ptr[_code_ptr[ip + 2 + instr_var_args]].operator String();
+				text += "(";
+
+				for (int i = 0; i < argc; i++) {
+					if (i > 0) {
+						text += ", ";
+					}
+					text += DADDR(1 + i);
+				}
+				text += ")";
+
+				incr += 5 + argc;
+			} break;
 			case OPCODE_CALL_PTRCALL_NO_RETURN: {
 			case OPCODE_CALL_PTRCALL_NO_RETURN: {
 				text += "call-ptrcall (no return) ";
 				text += "call-ptrcall (no return) ";
 
 

+ 1 - 0
modules/gdscript/gdscript_function.h

@@ -263,6 +263,7 @@ public:
 		OPCODE_CALL_SELF_BASE,
 		OPCODE_CALL_SELF_BASE,
 		OPCODE_CALL_METHOD_BIND,
 		OPCODE_CALL_METHOD_BIND,
 		OPCODE_CALL_METHOD_BIND_RET,
 		OPCODE_CALL_METHOD_BIND_RET,
+		OPCODE_CALL_BUILTIN_STATIC,
 		// ptrcall have one instruction per return type.
 		// ptrcall have one instruction per return type.
 		OPCODE_CALL_PTRCALL_NO_RETURN,
 		OPCODE_CALL_PTRCALL_NO_RETURN,
 		OPCODE_CALL_PTRCALL_BOOL,
 		OPCODE_CALL_PTRCALL_BOOL,

+ 46 - 0
modules/gdscript/gdscript_vm.cpp

@@ -196,6 +196,7 @@ String GDScriptFunction::_get_call_error(const Callable::CallError &p_err, const
 		&&OPCODE_CALL_SELF_BASE,                     \
 		&&OPCODE_CALL_SELF_BASE,                     \
 		&&OPCODE_CALL_METHOD_BIND,                   \
 		&&OPCODE_CALL_METHOD_BIND,                   \
 		&&OPCODE_CALL_METHOD_BIND_RET,               \
 		&&OPCODE_CALL_METHOD_BIND_RET,               \
+		&&OPCODE_CALL_BUILTIN_STATIC,                \
 		&&OPCODE_CALL_PTRCALL_NO_RETURN,             \
 		&&OPCODE_CALL_PTRCALL_NO_RETURN,             \
 		&&OPCODE_CALL_PTRCALL_BOOL,                  \
 		&&OPCODE_CALL_PTRCALL_BOOL,                  \
 		&&OPCODE_CALL_PTRCALL_INT,                   \
 		&&OPCODE_CALL_PTRCALL_INT,                   \
@@ -1573,6 +1574,51 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
 			}
 			}
 			DISPATCH_OPCODE;
 			DISPATCH_OPCODE;
 
 
+			OPCODE(OPCODE_CALL_BUILTIN_STATIC) {
+				CHECK_SPACE(4 + instr_arg_count);
+
+				ip += instr_arg_count;
+
+				GD_ERR_BREAK(_code_ptr[ip + 1] < 0 || _code_ptr[ip + 1] >= Variant::VARIANT_MAX);
+				Variant::Type builtin_type = (Variant::Type)_code_ptr[ip + 1];
+
+				int methodname_idx = _code_ptr[ip + 2];
+				GD_ERR_BREAK(methodname_idx < 0 || methodname_idx >= _global_names_count);
+				const StringName *methodname = &_global_names_ptr[methodname_idx];
+
+				int argc = _code_ptr[ip + 3];
+				GD_ERR_BREAK(argc < 0);
+
+				GET_INSTRUCTION_ARG(ret, argc);
+
+				const Variant **argptrs = const_cast<const Variant **>(instruction_args);
+
+#ifdef DEBUG_ENABLED
+				uint64_t call_time = 0;
+
+				if (GDScriptLanguage::get_singleton()->profiling) {
+					call_time = OS::get_singleton()->get_ticks_usec();
+				}
+#endif
+
+				Callable::CallError err;
+				Variant::call_static(builtin_type, *methodname, argptrs, argc, *ret, err);
+
+#ifdef DEBUG_ENABLED
+				if (GDScriptLanguage::get_singleton()->profiling) {
+					function_call_time += OS::get_singleton()->get_ticks_usec() - call_time;
+				}
+
+				if (err.error != Callable::CallError::CALL_OK) {
+					err_text = _get_call_error(err, "static function '" + methodname->operator String() + "' in type '" + Variant::get_type_name(builtin_type) + "'", argptrs);
+					OPCODE_BREAK;
+				}
+#endif
+
+				ip += 4;
+			}
+			DISPATCH_OPCODE;
+
 #ifdef DEBUG_ENABLED
 #ifdef DEBUG_ENABLED
 #define OPCODE_CALL_PTR(m_type)                                                      \
 #define OPCODE_CALL_PTR(m_type)                                                      \
 	OPCODE(OPCODE_CALL_PTRCALL_##m_type) {                                           \
 	OPCODE(OPCODE_CALL_PTRCALL_##m_type) {                                           \