GDScript: Add support for static method calls in native types
This commit is contained in:
parent
e4f0fc50f7
commit
4710e2b278
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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++) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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) ";
|
||||
|
||||
|
|
|
@ -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,10 +1058,14 @@ static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base
|
|||
}
|
||||
}
|
||||
|
||||
if (!_static || Engine::get_singleton()->has_singleton(type)) {
|
||||
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;
|
||||
}
|
||||
|
@ -1074,7 +1078,6 @@ static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base
|
|||
}
|
||||
r_result.insert(option.display, option);
|
||||
}
|
||||
}
|
||||
return;
|
||||
} break;
|
||||
case GDScriptParser::DataType::BUILTIN: {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -92,12 +92,16 @@ static String _get_var_type(const Variant *p_var) {
|
|||
} else {
|
||||
basestr = "null instance";
|
||||
}
|
||||
} else {
|
||||
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()) + ")";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
if (p_var->get_type() == Variant::ARRAY) {
|
||||
|
@ -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) { \
|
||||
|
|
Loading…
Reference in New Issue