mirror of
https://github.com/godotengine/godot.git
synced 2024-11-22 12:12:28 +00:00
GDScript: Add support for builtin static method calls
This commit is contained in:
parent
6e621441ca
commit
ec783dd885
@ -2078,9 +2078,23 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool is_awa
|
||||
mark_node_unsafe(p_call);
|
||||
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 {
|
||||
// Invalid call. Error already sent in parser.
|
||||
// TODO: Could check if Callable here too.
|
||||
|
@ -1017,6 +1017,56 @@ void GDScriptByteCodeGenerator::write_call_builtin_type(const Address &p_target,
|
||||
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) {
|
||||
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++) {
|
||||
|
@ -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_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_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;
|
||||
|
@ -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_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_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;
|
||||
|
@ -537,39 +537,44 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
|
||||
const GDScriptParser::SubscriptNode *subscript = static_cast<const GDScriptParser::SubscriptNode *>(call->callee);
|
||||
|
||||
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 {
|
||||
// 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 {
|
||||
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 {
|
||||
_set_error("Cannot call something that isn't a function.", call->callee);
|
||||
|
@ -542,6 +542,28 @@ void GDScriptFunction::disassemble(const Vector<String> &p_code_lines) const {
|
||||
|
||||
incr = 5 + argc;
|
||||
} 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: {
|
||||
text += "call-ptrcall (no return) ";
|
||||
|
||||
|
@ -263,6 +263,7 @@ public:
|
||||
OPCODE_CALL_SELF_BASE,
|
||||
OPCODE_CALL_METHOD_BIND,
|
||||
OPCODE_CALL_METHOD_BIND_RET,
|
||||
OPCODE_CALL_BUILTIN_STATIC,
|
||||
// ptrcall have one instruction per return type.
|
||||
OPCODE_CALL_PTRCALL_NO_RETURN,
|
||||
OPCODE_CALL_PTRCALL_BOOL,
|
||||
|
@ -196,6 +196,7 @@ String GDScriptFunction::_get_call_error(const Callable::CallError &p_err, const
|
||||
&&OPCODE_CALL_SELF_BASE, \
|
||||
&&OPCODE_CALL_METHOD_BIND, \
|
||||
&&OPCODE_CALL_METHOD_BIND_RET, \
|
||||
&&OPCODE_CALL_BUILTIN_STATIC, \
|
||||
&&OPCODE_CALL_PTRCALL_NO_RETURN, \
|
||||
&&OPCODE_CALL_PTRCALL_BOOL, \
|
||||
&&OPCODE_CALL_PTRCALL_INT, \
|
||||
@ -1573,6 +1574,51 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
|
||||
}
|
||||
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
|
||||
#define OPCODE_CALL_PTR(m_type) \
|
||||
OPCODE(OPCODE_CALL_PTRCALL_##m_type) { \
|
||||
|
Loading…
Reference in New Issue
Block a user