diff options
Diffstat (limited to 'modules/gdscript/gdscript_vm.cpp')
-rw-r--r-- | modules/gdscript/gdscript_vm.cpp | 282 |
1 files changed, 173 insertions, 109 deletions
diff --git a/modules/gdscript/gdscript_vm.cpp b/modules/gdscript/gdscript_vm.cpp index b99f5d2685..0ffc025c24 100644 --- a/modules/gdscript/gdscript_vm.cpp +++ b/modules/gdscript/gdscript_vm.cpp @@ -28,12 +28,12 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /**************************************************************************/ +#include "gdscript.h" #include "gdscript_function.h" +#include "gdscript_lambda_callable.h" #include "core/core_string_names.h" #include "core/os/os.h" -#include "gdscript.h" -#include "gdscript_lambda_callable.h" #ifdef DEBUG_ENABLED static String _get_script_name(const Ref<Script> p_script) { @@ -201,8 +201,10 @@ void (*type_init_function_table[])(Variant *) = { static const void *switch_table_ops[] = { \ &&OPCODE_OPERATOR, \ &&OPCODE_OPERATOR_VALIDATED, \ - &&OPCODE_EXTENDS_TEST, \ - &&OPCODE_IS_BUILTIN, \ + &&OPCODE_TYPE_TEST_BUILTIN, \ + &&OPCODE_TYPE_TEST_ARRAY, \ + &&OPCODE_TYPE_TEST_NATIVE, \ + &&OPCODE_TYPE_TEST_SCRIPT, \ &&OPCODE_SET_KEYED, \ &&OPCODE_SET_KEYED_VALIDATED, \ &&OPCODE_SET_INDEXED_VALIDATED, \ @@ -447,6 +449,9 @@ void (*type_init_function_table[])(Variant *) = { #define OP_GET_BASIS get_basis #define OP_GET_RID get_rid +#define METHOD_CALL_ON_NULL_VALUE_ERROR(method_pointer) "Cannot call method '" + (method_pointer)->get_name() + "' on a null value." +#define METHOD_CALL_ON_FREED_INSTANCE_ERROR(method_pointer) "Cannot call method '" + (method_pointer)->get_name() + "' on a previously freed instance." + Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_args, int p_argcount, Callable::CallError &r_err, CallState *p_state) { OPCODES_TABLE; @@ -456,6 +461,33 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a r_err.error = Callable::CallError::CALL_OK; + static thread_local int call_depth = 0; + if (unlikely(++call_depth > MAX_CALL_DEPTH)) { + call_depth--; +#ifdef DEBUG_ENABLED + String err_file; + if (p_instance && ObjectDB::get_instance(p_instance->owner_id) != nullptr && p_instance->script->is_valid() && !p_instance->script->path.is_empty()) { + err_file = p_instance->script->path; + } else if (_script) { + err_file = _script->path; + } + if (err_file.is_empty()) { + err_file = "<built-in>"; + } + String err_func = name; + if (p_instance && ObjectDB::get_instance(p_instance->owner_id) != nullptr && p_instance->script->is_valid() && !p_instance->script->name.is_empty()) { + err_func = p_instance->script->name + "." + err_func; + } + int err_line = _initial_line; + const char *err_text = "Stack overflow. Check for infinite recursion in your script."; + if (!GDScriptLanguage::get_singleton()->debug_break(err_text, false)) { + // Debugger break did not happen. + _err_print_error(err_func.utf8().get_data(), err_file.utf8().get_data(), err_line, err_text, false, ERR_HANDLER_SCRIPT); + } +#endif + return _get_default_variant_for_data_type(return_type); + } + Variant retvalue; Variant *stack = nullptr; Variant **instruction_args = nullptr; @@ -490,10 +522,12 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a r_err.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS; r_err.argument = _argument_count; + call_depth--; return _get_default_variant_for_data_type(return_type); } else if (p_argcount < _argument_count - _default_arg_count) { r_err.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; r_err.argument = _argument_count - _default_arg_count; + call_depth--; return _get_default_variant_for_data_type(return_type); } else { defarg = _argument_count - p_argcount; @@ -521,6 +555,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a r_err.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; r_err.argument = i; r_err.expected = argument_types[i].builtin_type; + call_depth--; return _get_default_variant_for_data_type(return_type); } if (argument_types[i].kind == GDScriptDataType::BUILTIN) { @@ -645,10 +680,10 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a bool awaited = false; #endif #ifdef DEBUG_ENABLED - int variant_address_limits[ADDR_TYPE_MAX] = { _stack_size, _constant_count, p_instance ? p_instance->members.size() : 0 }; + int variant_address_limits[ADDR_TYPE_MAX] = { _stack_size, _constant_count, p_instance ? p_instance->members.size() : 0, script->static_variables.size() }; #endif - Variant *variant_addresses[ADDR_TYPE_MAX] = { stack, _constants_ptr, p_instance ? p_instance->members.ptrw() : nullptr }; + Variant *variant_addresses[ADDR_TYPE_MAX] = { stack, _constants_ptr, p_instance ? p_instance->members.ptrw() : nullptr, script->static_variables.ptrw() }; #ifdef DEBUG_ENABLED OPCODE_WHILE(ip < _code_size) { @@ -710,91 +745,95 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a } DISPATCH_OPCODE; - OPCODE(OPCODE_EXTENDS_TEST) { + OPCODE(OPCODE_TYPE_TEST_BUILTIN) { CHECK_SPACE(4); - GET_VARIANT_PTR(a, 0); - GET_VARIANT_PTR(b, 1); - GET_VARIANT_PTR(dst, 2); - -#ifdef DEBUG_ENABLED - if (b->get_type() != Variant::OBJECT || b->operator Object *() == nullptr) { - err_text = "Right operand of 'is' is not a class."; - OPCODE_BREAK; - } -#endif + GET_VARIANT_PTR(dst, 0); + GET_VARIANT_PTR(value, 1); - bool extends_ok = false; - if (a->get_type() == Variant::OBJECT && a->operator Object *() != nullptr) { -#ifdef DEBUG_ENABLED - bool was_freed; - Object *obj_A = a->get_validated_object_with_check(was_freed); + Variant::Type builtin_type = (Variant::Type)_code_ptr[ip + 3]; + GD_ERR_BREAK(builtin_type < 0 || builtin_type >= Variant::VARIANT_MAX); - if (was_freed) { - err_text = "Left operand of 'is' is a previously freed instance."; - OPCODE_BREAK; - } + *dst = value->get_type() == builtin_type; + ip += 4; + } + DISPATCH_OPCODE; - Object *obj_B = b->get_validated_object_with_check(was_freed); + OPCODE(OPCODE_TYPE_TEST_ARRAY) { + CHECK_SPACE(6); - if (was_freed) { - err_text = "Right operand of 'is' is a previously freed instance."; - OPCODE_BREAK; - } -#else + GET_VARIANT_PTR(dst, 0); + GET_VARIANT_PTR(value, 1); - Object *obj_A = *a; - Object *obj_B = *b; -#endif // DEBUG_ENABLED + GET_VARIANT_PTR(script_type, 2); + Variant::Type builtin_type = (Variant::Type)_code_ptr[ip + 4]; + int native_type_idx = _code_ptr[ip + 5]; + GD_ERR_BREAK(native_type_idx < 0 || native_type_idx >= _global_names_count); + const StringName native_type = _global_names_ptr[native_type_idx]; - GDScript *scr_B = Object::cast_to<GDScript>(obj_B); + bool result = false; + if (value->get_type() == Variant::ARRAY) { + Array *array = VariantInternal::get_array(value); + result = array->get_typed_builtin() == ((uint32_t)builtin_type) && array->get_typed_class_name() == native_type && array->get_typed_script() == *script_type; + } - if (scr_B) { - //if B is a script, the only valid condition is that A has an instance which inherits from the script - //in other situation, this should return false. + *dst = result; + ip += 6; + } + DISPATCH_OPCODE; - if (obj_A->get_script_instance() && obj_A->get_script_instance()->get_language() == GDScriptLanguage::get_singleton()) { - GDScript *cmp = static_cast<GDScript *>(obj_A->get_script_instance()->get_script().ptr()); - //bool found=false; - while (cmp) { - if (cmp == scr_B) { - //inherits from script, all ok - extends_ok = true; - break; - } + OPCODE(OPCODE_TYPE_TEST_NATIVE) { + CHECK_SPACE(4); - cmp = cmp->_base; - } - } + GET_VARIANT_PTR(dst, 0); + GET_VARIANT_PTR(value, 1); - } else { - GDScriptNativeClass *nc = Object::cast_to<GDScriptNativeClass>(obj_B); + int native_type_idx = _code_ptr[ip + 3]; + GD_ERR_BREAK(native_type_idx < 0 || native_type_idx >= _global_names_count); + const StringName native_type = _global_names_ptr[native_type_idx]; -#ifdef DEBUG_ENABLED - if (!nc) { - err_text = "Right operand of 'is' is not a class (type: '" + obj_B->get_class() + "')."; - OPCODE_BREAK; - } -#endif - extends_ok = ClassDB::is_parent_class(obj_A->get_class_name(), nc->get_name()); - } + bool was_freed = false; + Object *object = value->get_validated_object_with_check(was_freed); + if (was_freed) { + err_text = "Left operand of 'is' is a previously freed instance."; + OPCODE_BREAK; } - *dst = extends_ok; + *dst = object && ClassDB::is_parent_class(object->get_class_name(), native_type); ip += 4; } DISPATCH_OPCODE; - OPCODE(OPCODE_IS_BUILTIN) { + OPCODE(OPCODE_TYPE_TEST_SCRIPT) { CHECK_SPACE(4); - GET_VARIANT_PTR(value, 0); - GET_VARIANT_PTR(dst, 1); - Variant::Type var_type = (Variant::Type)_code_ptr[ip + 3]; + GET_VARIANT_PTR(dst, 0); + GET_VARIANT_PTR(value, 1); - GD_ERR_BREAK(var_type < 0 || var_type >= Variant::VARIANT_MAX); + GET_VARIANT_PTR(type, 2); + Script *script_type = Object::cast_to<Script>(type->operator Object *()); + GD_ERR_BREAK(!script_type); - *dst = value->get_type() == var_type; + bool was_freed = false; + Object *object = value->get_validated_object_with_check(was_freed); + if (was_freed) { + err_text = "Left operand of 'is' is a previously freed instance."; + OPCODE_BREAK; + } + + bool result = false; + if (object && object->get_script_instance()) { + Script *script_ptr = object->get_script_instance()->get_script().ptr(); + while (script_ptr) { + if (script_ptr == script_type) { + result = true; + break; + } + script_ptr = script_ptr->get_base_script().ptr(); + } + } + + *dst = result; ip += 4; } DISPATCH_OPCODE; @@ -1213,7 +1252,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a Array *array = VariantInternal::get_array(src); - if (array->get_typed_builtin() != ((uint32_t)builtin_type) || array->get_typed_class_name() != native_type || array->get_typed_script() != *script_type || array->get_typed_class_name() != native_type) { + if (array->get_typed_builtin() != ((uint32_t)builtin_type) || array->get_typed_class_name() != native_type || array->get_typed_script() != *script_type) { #ifdef DEBUG_ENABLED err_text = vformat(R"(Trying to assign an array of type "%s" to a variable of type "Array[%s]".)", _get_var_type(src), _get_element_type(builtin_type, native_type, *script_type)); @@ -1241,12 +1280,20 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a "' to a variable of type '" + nc->get_name() + "'."; OPCODE_BREAK; } - Object *src_obj = src->operator Object *(); - if (src_obj && !ClassDB::is_parent_class(src_obj->get_class_name(), nc->get_name())) { - err_text = "Trying to assign value of type '" + src_obj->get_class_name() + - "' to a variable of type '" + nc->get_name() + "'."; - OPCODE_BREAK; + if (src->get_type() == Variant::OBJECT) { + bool was_freed = false; + Object *src_obj = src->get_validated_object_with_check(was_freed); + if (!src_obj && was_freed) { + err_text = "Trying to assign invalid previously freed instance."; + OPCODE_BREAK; + } + + if (src_obj && !ClassDB::is_parent_class(src_obj->get_class_name(), nc->get_name())) { + err_text = "Trying to assign value of type '" + src_obj->get_class_name() + + "' to a variable of type '" + nc->get_name() + "'."; + OPCODE_BREAK; + } } #endif // DEBUG_ENABLED *dst = *src; @@ -1271,29 +1318,38 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a OPCODE_BREAK; } - if (src->get_type() != Variant::NIL && src->operator Object *() != nullptr) { - ScriptInstance *scr_inst = src->operator Object *()->get_script_instance(); - if (!scr_inst) { - err_text = "Trying to assign value of type '" + src->operator Object *()->get_class_name() + - "' to a variable of type '" + base_type->get_path().get_file() + "'."; + if (src->get_type() == Variant::OBJECT) { + bool was_freed = false; + Object *val_obj = src->get_validated_object_with_check(was_freed); + if (!val_obj && was_freed) { + err_text = "Trying to assign invalid previously freed instance."; OPCODE_BREAK; } - Script *src_type = src->operator Object *()->get_script_instance()->get_script().ptr(); - bool valid = false; + if (val_obj) { // src is not null + ScriptInstance *scr_inst = val_obj->get_script_instance(); + if (!scr_inst) { + err_text = "Trying to assign value of type '" + val_obj->get_class_name() + + "' to a variable of type '" + base_type->get_path().get_file() + "'."; + OPCODE_BREAK; + } - while (src_type) { - if (src_type == base_type) { - valid = true; - break; + Script *src_type = scr_inst->get_script().ptr(); + bool valid = false; + + while (src_type) { + if (src_type == base_type) { + valid = true; + break; + } + src_type = src_type->get_base_script().ptr(); } - src_type = src_type->get_base_script().ptr(); - } - if (!valid) { - err_text = "Trying to assign value of type '" + src->operator Object *()->get_script_instance()->get_script()->get_path().get_file() + - "' to a variable of type '" + base_type->get_path().get_file() + "'."; - OPCODE_BREAK; + if (!valid) { + err_text = "Trying to assign value of type '" + val_obj->get_script_instance()->get_script()->get_path().get_file() + + "' to a variable of type '" + base_type->get_path().get_file() + "'."; + OPCODE_BREAK; + } } } #endif // DEBUG_ENABLED @@ -1595,10 +1651,6 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a bool was_freed = false; Object *obj = ret->get_validated_object_with_check(was_freed); - if (was_freed) { - err_text = "Got a freed object as a result of the call."; - OPCODE_BREAK; - } if (obj && obj->is_class_ptr(GDScriptFunctionState::get_class_ptr_static())) { err_text = R"(Trying to call an async function without "await".)"; OPCODE_BREAK; @@ -1675,10 +1727,10 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a bool freed = false; Object *base_obj = base->get_validated_object_with_check(freed); if (freed) { - err_text = "Trying to call a function on a previously freed instance."; + err_text = METHOD_CALL_ON_FREED_INSTANCE_ERROR(method); OPCODE_BREAK; } else if (!base_obj) { - err_text = "Trying to call a function on a null value."; + err_text = METHOD_CALL_ON_NULL_VALUE_ERROR(method); OPCODE_BREAK; } #else @@ -1839,10 +1891,10 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a bool freed = false; \ Object *base_obj = base->get_validated_object_with_check(freed); \ if (freed) { \ - err_text = "Trying to call a function on a previously freed instance."; \ + err_text = METHOD_CALL_ON_FREED_INSTANCE_ERROR(method); \ OPCODE_BREAK; \ } else if (!base_obj) { \ - err_text = "Trying to call a function on a null value."; \ + err_text = METHOD_CALL_ON_NULL_VALUE_ERROR(method); \ OPCODE_BREAK; \ } \ const void **argptrs = call_args_ptr; \ @@ -1941,10 +1993,10 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a bool freed = false; Object *base_obj = base->get_validated_object_with_check(freed); if (freed) { - err_text = "Trying to call a function on a previously freed instance."; + err_text = METHOD_CALL_ON_FREED_INSTANCE_ERROR(method); OPCODE_BREAK; } else if (!base_obj) { - err_text = "Trying to call a function on a null value."; + err_text = METHOD_CALL_ON_NULL_VALUE_ERROR(method); OPCODE_BREAK; } #else @@ -1969,7 +2021,13 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a VariantInternal::initialize(ret, Variant::OBJECT); Object **ret_opaque = VariantInternal::get_object(ret); method->ptrcall(base_obj, argptrs, ret_opaque); - VariantInternal::update_object_id(ret); + if (method->is_return_type_raw_object_ptr()) { + // The Variant has to participate in the ref count since the method returns a raw Object *. + VariantInternal::object_assign(ret, *ret_opaque); + } else { + // The method, in case it returns something, returns an already encapsulated object. + VariantInternal::update_object_id(ret); + } #ifdef DEBUG_ENABLED if (GDScriptLanguage::get_singleton()->profiling) { @@ -1996,10 +2054,10 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a bool freed = false; Object *base_obj = base->get_validated_object_with_check(freed); if (freed) { - err_text = "Trying to call a function on a previously freed instance."; + err_text = METHOD_CALL_ON_FREED_INSTANCE_ERROR(method); OPCODE_BREAK; } else if (!base_obj) { - err_text = "Trying to call a function on a null value."; + err_text = METHOD_CALL_ON_NULL_VALUE_ERROR(method); OPCODE_BREAK; } #else @@ -2521,7 +2579,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a Array *array = VariantInternal::get_array(r); - if (array->get_typed_builtin() != ((uint32_t)builtin_type) || array->get_typed_class_name() != native_type || array->get_typed_script() != *script_type || array->get_typed_class_name() != native_type) { + if (array->get_typed_builtin() != ((uint32_t)builtin_type) || array->get_typed_class_name() != native_type || array->get_typed_script() != *script_type) { #ifdef DEBUG_ENABLED err_text = vformat(R"(Trying to return an array of type "%s" where expected return type is "Array[%s]".)", _get_var_type(r), _get_element_type(builtin_type, native_type, *script_type)); @@ -3427,7 +3485,10 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a String message_str; if (_code_ptr[ip + 2] != 0) { GET_VARIANT_PTR(message, 1); - message_str = *message; + Variant message_var = *message; + if (message->get_type() != Variant::NIL) { + message_str = message_var; + } } if (message_str.is_empty()) { err_text = "Assertion failed."; @@ -3507,8 +3568,9 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a //error // function, file, line, error, explanation String err_file; - if (p_instance && ObjectDB::get_instance(p_instance->owner_id) != nullptr && p_instance->script->is_valid() && !p_instance->script->path.is_empty()) { - err_file = p_instance->script->path; + bool instance_valid_with_script = p_instance && ObjectDB::get_instance(p_instance->owner_id) != nullptr && p_instance->script->is_valid(); + if (instance_valid_with_script && !get_script()->path.is_empty()) { + err_file = get_script()->path; } else if (script) { err_file = script->path; } @@ -3516,7 +3578,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a err_file = "<built-in>"; } String err_func = name; - if (p_instance && ObjectDB::get_instance(p_instance->owner_id) != nullptr && p_instance->script->is_valid() && !p_instance->script->name.is_empty()) { + if (instance_valid_with_script && !p_instance->script->name.is_empty()) { err_func = p_instance->script->name + "." + err_func; } int err_line = line; @@ -3570,5 +3632,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a stack[i].~Variant(); } + call_depth--; + return retvalue; } |