GDScript: Add support for static method calls in native types

This commit is contained in:
George Marques 2022-04-06 14:14:38 -03:00
parent e4f0fc50f7
commit 4710e2b278
No known key found for this signature in database
GPG key ID: 046BD46A3201E43D
11 changed files with 135 additions and 20 deletions

View file

@ -92,6 +92,21 @@ Object *GDScriptNativeClass::instantiate() {
return ClassDB::instantiate(name);
}
Variant GDScriptNativeClass::callp(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
if (p_method == SNAME("new")) {
// Constructor.
return Object::callp(p_method, p_args, p_argcount, r_error);
}
MethodBind *method = ClassDB::get_method(name, p_method);
if (method) {
// Native static method.
return method->call(nullptr, p_args, p_argcount, r_error);
}
r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
return Variant();
}
GDScriptFunction *GDScript::_super_constructor(GDScript *p_script) {
if (p_script->initializer) {
return p_script->initializer;

View file

@ -52,6 +52,7 @@ public:
_FORCE_INLINE_ const StringName &get_name() const { return name; }
Variant _new();
Object *instantiate();
virtual Variant callp(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) override;
GDScriptNativeClass(const StringName &p_name);
};

View file

@ -2508,8 +2508,9 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
call_type = return_type;
} else {
// Check if the name exists as something else.
bool found = false;
// Check if the name exists as something else.
if (!p_call->is_super && callee_type != GDScriptParser::Node::NONE) {
GDScriptParser::IdentifierNode *callee_id;
if (callee_type == GDScriptParser::Node::IDENTIFIER) {
@ -2539,6 +2540,8 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
if (!found && (is_self || (base_type.is_hard_type() && base_type.kind == GDScriptParser::DataType::BUILTIN))) {
String base_name = is_self && !p_call->is_super ? "self" : base_type.to_string();
push_error(vformat(R"*(Function "%s()" not found in base %s.)*", p_call->function_name, base_name), p_call->is_super ? p_call : p_call->callee);
} else if (!found && (!p_call->is_super && base_type.is_hard_type() && base_type.kind == GDScriptParser::DataType::NATIVE && base_type.is_meta_type)) {
push_error(vformat(R"*(Static function "%s()" not found in base "%s".)*", p_call->function_name, base_type.native_type.operator String()), p_call);
}
}
@ -3773,6 +3776,7 @@ bool GDScriptAnalyzer::function_signature_from_info(const MethodInfo &p_info, GD
r_return_type = type_from_property(p_info.return_val);
r_default_arg_count = p_info.default_arguments.size();
r_vararg = (p_info.flags & METHOD_FLAG_VARARG) != 0;
r_static = (p_info.flags & METHOD_FLAG_STATIC) != 0;
for (const PropertyInfo &E : p_info.arguments) {
r_par_types.push_back(type_from_property(E));

View file

@ -1080,6 +1080,24 @@ void GDScriptByteCodeGenerator::write_call_builtin_type_static(const Address &p_
append(Variant::get_validated_builtin_method(p_type, p_method));
}
void GDScriptByteCodeGenerator::write_call_native_static(const Address &p_target, const StringName &p_class, const StringName &p_method, const Vector<Address> &p_arguments) {
bool is_validated = false;
MethodBind *method = ClassDB::get_method(p_class, p_method);
if (!is_validated) {
// Perform regular call.
append(GDScriptFunction::OPCODE_CALL_NATIVE_STATIC, p_arguments.size() + 1);
for (int i = 0; i < p_arguments.size(); i++) {
append(p_arguments[i]);
}
append(p_target);
append(method);
append(p_arguments.size());
return;
}
}
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());
for (int i = 0; i < p_arguments.size(); i++) {

View file

@ -464,6 +464,7 @@ public:
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_static(const Address &p_target, Variant::Type p_type, const StringName &p_method, const Vector<Address> &p_arguments) override;
virtual void write_call_native_static(const Address &p_target, const StringName &p_class, 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_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;

View file

@ -125,6 +125,7 @@ public:
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_static(const Address &p_target, Variant::Type p_type, const StringName &p_method, const Vector<Address> &p_arguments) = 0;
virtual void write_call_native_static(const Address &p_target, const StringName &p_class, 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_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;

View file

@ -575,6 +575,9 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
// 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 if (!call->is_super && subscript->base->type == GDScriptParser::Node::IDENTIFIER && call->function_name != SNAME("new") && ClassDB::class_exists(static_cast<GDScriptParser::IdentifierNode *>(subscript->base)->name)) {
// It's a static native method call.
gen->write_call_native_static(result, 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) {

View file

@ -564,6 +564,28 @@ void GDScriptFunction::disassemble(const Vector<String> &p_code_lines) const {
incr += 5 + argc;
} break;
case OPCODE_CALL_NATIVE_STATIC: {
MethodBind *method = _methods_ptr[_code_ptr[ip + 1 + instr_var_args]];
int argc = _code_ptr[ip + 2 + instr_var_args];
text += "call native method static ";
text += DADDR(1 + argc);
text += " = ";
text += method->get_instance_class();
text += ".";
text += method->get_name();
text += "(";
for (int i = 0; i < argc; i++) {
if (i > 0) {
text += ", ";
}
text += DADDR(1 + i);
}
text += ")";
incr += 4 + argc;
} break;
case OPCODE_CALL_PTRCALL_NO_RETURN: {
text += "call-ptrcall (no return) ";

View file

@ -957,7 +957,7 @@ static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base
bool _static = base_type.is_meta_type;
if (_static && base_type.kind != GDScriptParser::DataType::BUILTIN) {
ScriptLanguage::CodeCompletionOption option("new", ScriptLanguage::CODE_COMPLETION_KIND_FUNCTION);
ScriptLanguage::CodeCompletionOption option("new", ScriptLanguage::CODE_COMPLETION_KIND_FUNCTION, ScriptLanguage::LOCATION_LOCAL);
option.insert_text += "(";
r_result.insert(option.display, option);
}
@ -1058,22 +1058,25 @@ static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base
}
}
if (!_static || Engine::get_singleton()->has_singleton(type)) {
List<MethodInfo> methods;
ClassDB::get_method_list(type, &methods, false, true);
for (const MethodInfo &E : methods) {
if (E.name.begins_with("_")) {
continue;
}
int location = p_recursion_depth + _get_method_location(type, E.name);
ScriptLanguage::CodeCompletionOption option(E.name, ScriptLanguage::CODE_COMPLETION_KIND_FUNCTION, location);
if (E.arguments.size()) {
option.insert_text += "(";
} else {
option.insert_text += "()";
}
r_result.insert(option.display, option);
bool only_static = _static && !Engine::get_singleton()->has_singleton(type);
List<MethodInfo> methods;
ClassDB::get_method_list(type, &methods, false, true);
for (const MethodInfo &E : methods) {
if (only_static && (E.flags & METHOD_FLAG_STATIC) == 0) {
continue;
}
if (E.name.begins_with("_")) {
continue;
}
int location = p_recursion_depth + _get_method_location(type, E.name);
ScriptLanguage::CodeCompletionOption option(E.name, ScriptLanguage::CODE_COMPLETION_KIND_FUNCTION, location);
if (E.arguments.size()) {
option.insert_text += "(";
} else {
option.insert_text += "()";
}
r_result.insert(option.display, option);
}
return;
} break;

View file

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

View file

@ -93,9 +93,13 @@ static String _get_var_type(const Variant *p_var) {
basestr = "null instance";
}
} else {
basestr = bobj->get_class();
if (bobj->get_script_instance()) {
basestr += " (" + _get_script_name(bobj->get_script_instance()->get_script()) + ")";
if (bobj->is_class_ptr(GDScriptNativeClass::get_class_ptr_static())) {
basestr = Object::cast_to<GDScriptNativeClass>(bobj)->get_name();
} else {
basestr = bobj->get_class();
if (bobj->get_script_instance()) {
basestr += " (" + _get_script_name(bobj->get_script_instance()->get_script()) + ")";
}
}
}
@ -263,6 +267,7 @@ void (*type_init_function_table[])(Variant *) = {
&&OPCODE_CALL_METHOD_BIND, \
&&OPCODE_CALL_METHOD_BIND_RET, \
&&OPCODE_CALL_BUILTIN_STATIC, \
&&OPCODE_CALL_NATIVE_STATIC, \
&&OPCODE_CALL_PTRCALL_NO_RETURN, \
&&OPCODE_CALL_PTRCALL_BOOL, \
&&OPCODE_CALL_PTRCALL_INT, \
@ -1710,6 +1715,47 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
}
DISPATCH_OPCODE;
OPCODE(OPCODE_CALL_NATIVE_STATIC) {
CHECK_SPACE(3 + instr_arg_count);
ip += instr_arg_count;
GD_ERR_BREAK(_code_ptr[ip + 1] < 0 || _code_ptr[ip + 1] >= _methods_count);
MethodBind *method = _methods_ptr[_code_ptr[ip + 1]];
int argc = _code_ptr[ip + 2];
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;
*ret = method->call(nullptr, argptrs, argc, 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 '" + method->get_name().operator String() + "' in type '" + method->get_instance_class().operator String() + "'", argptrs);
OPCODE_BREAK;
}
#endif
ip += 3;
}
DISPATCH_OPCODE;
#ifdef DEBUG_ENABLED
#define OPCODE_CALL_PTR(m_type) \
OPCODE(OPCODE_CALL_PTRCALL_##m_type) { \