Merge pull request #91192 from vnen/gdscript-validated-native-static-calls

GDScript: Perform validated calls with static methods
This commit is contained in:
Rémi Verschelde 2024-05-01 09:55:04 +02:00
commit 731ea17dd4
No known key found for this signature in database
GPG Key ID: C3336907360768E1
9 changed files with 330 additions and 165 deletions

View File

@ -1196,11 +1196,8 @@ void GDScriptByteCodeGenerator::write_call_builtin_type_static(const Address &p_
}
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_opcode_and_argcount(GDScriptFunction::OPCODE_CALL_NATIVE_STATIC, p_arguments.size() + 1);
for (int i = 0; i < p_arguments.size(); i++) {
@ -1213,6 +1210,35 @@ void GDScriptByteCodeGenerator::write_call_native_static(const Address &p_target
ct.cleanup();
return;
}
void GDScriptByteCodeGenerator::write_call_native_static_validated(const GDScriptCodeGenerator::Address &p_target, MethodBind *p_method, const Vector<GDScriptCodeGenerator::Address> &p_arguments) {
Variant::Type return_type = Variant::NIL;
bool has_return = p_method->has_return();
if (has_return) {
PropertyInfo return_info = p_method->get_return_info();
return_type = return_info.type;
}
CallTarget ct = get_call_target(p_target, return_type);
if (has_return) {
Variant::Type temp_type = temporaries[ct.target.address].type;
if (temp_type != return_type) {
write_type_adjust(ct.target, return_type);
}
}
GDScriptFunction::Opcode code = p_method->has_return() ? GDScriptFunction::OPCODE_CALL_NATIVE_STATIC_VALIDATED_RETURN : GDScriptFunction::OPCODE_CALL_NATIVE_STATIC_VALIDATED_NO_RETURN;
append_opcode_and_argcount(code, 1 + p_arguments.size());
for (int i = 0; i < p_arguments.size(); i++) {
append(p_arguments[i]);
}
append(ct.target);
append(p_arguments.size());
append(p_method);
ct.cleanup();
}
void GDScriptByteCodeGenerator::write_call_method_bind(const Address &p_target, const Address &p_base, MethodBind *p_method, const Vector<Address> &p_arguments) {

View File

@ -518,6 +518,7 @@ public:
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_native_static_validated(const Address &p_target, 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_method_bind_validated(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

@ -131,6 +131,7 @@ public:
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_native_static_validated(const Address &p_target, 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_method_bind_validated(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

@ -673,7 +673,15 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
} 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) && !Engine::get_singleton()->has_singleton(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);
StringName class_name = static_cast<GDScriptParser::IdentifierNode *>(subscript->base)->name;
MethodBind *method = ClassDB::get_method(class_name, subscript->attribute->name);
if (_can_use_validate_call(method, arguments)) {
// Exact arguments, use validated call.
gen->write_call_native_static_validated(result, method, arguments);
} else {
// Not exact arguments, use regular static call
gen->write_call_native_static(result, class_name, subscript->attribute->name, arguments);
}
} else {
GDScriptCodeGenerator::Address base = _parse_expression(codegen, r_error, subscript->base);
if (r_error) {

View File

@ -678,6 +678,50 @@ void GDScriptFunction::disassemble(const Vector<String> &p_code_lines) const {
incr += 4 + argc;
} break;
case OPCODE_CALL_NATIVE_STATIC_VALIDATED_RETURN: {
int instr_var_args = _code_ptr[++ip];
text += "call native static method validated (return) ";
MethodBind *method = _methods_ptr[_code_ptr[ip + 2 + instr_var_args]];
int argc = _code_ptr[ip + 1 + instr_var_args];
text += DADDR(1 + argc) + " = ";
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_NATIVE_STATIC_VALIDATED_NO_RETURN: {
int instr_var_args = _code_ptr[++ip];
text += "call native static method validated (no return) ";
MethodBind *method = _methods_ptr[_code_ptr[ip + 2 + instr_var_args]];
int argc = _code_ptr[ip + 1 + instr_var_args];
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_METHOD_BIND_VALIDATED_RETURN: {
int instr_var_args = _code_ptr[++ip];
text += "call method-bind validated (return) ";

View File

@ -264,6 +264,8 @@ public:
OPCODE_CALL_METHOD_BIND_RET,
OPCODE_CALL_BUILTIN_STATIC,
OPCODE_CALL_NATIVE_STATIC,
OPCODE_CALL_NATIVE_STATIC_VALIDATED_RETURN,
OPCODE_CALL_NATIVE_STATIC_VALIDATED_NO_RETURN,
OPCODE_CALL_METHOD_BIND_VALIDATED_RETURN,
OPCODE_CALL_METHOD_BIND_VALIDATED_NO_RETURN,
OPCODE_AWAIT,

View File

@ -261,6 +261,8 @@ void (*type_init_function_table[])(Variant *) = {
&&OPCODE_CALL_METHOD_BIND_RET, \
&&OPCODE_CALL_BUILTIN_STATIC, \
&&OPCODE_CALL_NATIVE_STATIC, \
&&OPCODE_CALL_NATIVE_STATIC_VALIDATED_RETURN, \
&&OPCODE_CALL_NATIVE_STATIC_VALIDATED_NO_RETURN, \
&&OPCODE_CALL_METHOD_BIND_VALIDATED_RETURN, \
&&OPCODE_CALL_METHOD_BIND_VALIDATED_NO_RETURN, \
&&OPCODE_AWAIT, \
@ -1956,6 +1958,78 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
}
DISPATCH_OPCODE;
OPCODE(OPCODE_CALL_NATIVE_STATIC_VALIDATED_RETURN) {
LOAD_INSTRUCTION_ARGS
CHECK_SPACE(3 + instr_arg_count);
ip += instr_arg_count;
int argc = _code_ptr[ip + 1];
GD_ERR_BREAK(argc < 0);
GD_ERR_BREAK(_code_ptr[ip + 2] < 0 || _code_ptr[ip + 2] >= _methods_count);
MethodBind *method = _methods_ptr[_code_ptr[ip + 2]];
Variant **argptrs = instruction_args;
#ifdef DEBUG_ENABLED
uint64_t call_time = 0;
if (GDScriptLanguage::get_singleton()->profiling && GDScriptLanguage::get_singleton()->profile_native_calls) {
call_time = OS::get_singleton()->get_ticks_usec();
}
#endif
GET_INSTRUCTION_ARG(ret, argc);
method->validated_call(nullptr, (const Variant **)argptrs, ret);
#ifdef DEBUG_ENABLED
if (GDScriptLanguage::get_singleton()->profiling && GDScriptLanguage::get_singleton()->profile_native_calls) {
uint64_t t_taken = OS::get_singleton()->get_ticks_usec() - call_time;
_profile_native_call(t_taken, method->get_name(), method->get_instance_class());
function_call_time += t_taken;
}
#endif
ip += 3;
}
DISPATCH_OPCODE;
OPCODE(OPCODE_CALL_NATIVE_STATIC_VALIDATED_NO_RETURN) {
LOAD_INSTRUCTION_ARGS
CHECK_SPACE(3 + instr_arg_count);
ip += instr_arg_count;
int argc = _code_ptr[ip + 1];
GD_ERR_BREAK(argc < 0);
GD_ERR_BREAK(_code_ptr[ip + 2] < 0 || _code_ptr[ip + 2] >= _methods_count);
MethodBind *method = _methods_ptr[_code_ptr[ip + 2]];
Variant **argptrs = instruction_args;
#ifdef DEBUG_ENABLED
uint64_t call_time = 0;
if (GDScriptLanguage::get_singleton()->profiling && GDScriptLanguage::get_singleton()->profile_native_calls) {
call_time = OS::get_singleton()->get_ticks_usec();
}
#endif
GET_INSTRUCTION_ARG(ret, argc);
VariantInternal::initialize(ret, Variant::NIL);
method->validated_call(nullptr, (const Variant **)argptrs, nullptr);
#ifdef DEBUG_ENABLED
if (GDScriptLanguage::get_singleton()->profiling && GDScriptLanguage::get_singleton()->profile_native_calls) {
uint64_t t_taken = OS::get_singleton()->get_ticks_usec() - call_time;
_profile_native_call(t_taken, method->get_name(), method->get_instance_class());
function_call_time += t_taken;
}
#endif
ip += 3;
}
DISPATCH_OPCODE;
OPCODE(OPCODE_CALL_METHOD_BIND_VALIDATED_RETURN) {
LOAD_INSTRUCTION_ARGS
CHECK_SPACE(3 + instr_arg_count);

View File

@ -0,0 +1,7 @@
func test():
# Validated native static call with return value.
print(FileAccess.file_exists("some_file"))
# Validated native static call without return value.
Node.print_orphan_nodes()

View File

@ -0,0 +1,2 @@
GDTEST_OK
false