diff options
Diffstat (limited to 'modules/gdscript/gdscript_vm.cpp')
-rw-r--r-- | modules/gdscript/gdscript_vm.cpp | 365 |
1 files changed, 247 insertions, 118 deletions
diff --git a/modules/gdscript/gdscript_vm.cpp b/modules/gdscript/gdscript_vm.cpp index ffc871bdcb..8bf6a8b08b 100644 --- a/modules/gdscript/gdscript_vm.cpp +++ b/modules/gdscript/gdscript_vm.cpp @@ -34,22 +34,22 @@ #include "core/os/os.h" #include "gdscript.h" -Variant *GDScriptFunction::_get_variant(int p_address, GDScriptInstance *p_instance, GDScript *p_script, Variant &self, Variant &static_ref, Variant *p_stack, String &r_error) const { +Variant *GDScriptFunction::_get_variant(int p_address, GDScriptInstance *p_instance, Variant *p_stack, String &r_error) const { int address = p_address & ADDR_MASK; //sequential table (jump table generated by compiler) switch ((p_address & ADDR_TYPE_MASK) >> ADDR_BITS) { - case ADDR_TYPE_SELF: { + case ADDR_TYPE_STACK: { #ifdef DEBUG_ENABLED - if (unlikely(!p_instance)) { - r_error = "Cannot access self without instance."; - return nullptr; - } + ERR_FAIL_INDEX_V(address, _stack_size, nullptr); #endif - return &self; + return &p_stack[address]; } break; - case ADDR_TYPE_CLASS: { - return &static_ref; + case ADDR_TYPE_CONSTANT: { +#ifdef DEBUG_ENABLED + ERR_FAIL_INDEX_V(address, _constant_count, nullptr); +#endif + return &_constants_ptr[address]; } break; case ADDR_TYPE_MEMBER: { #ifdef DEBUG_ENABLED @@ -61,65 +61,6 @@ Variant *GDScriptFunction::_get_variant(int p_address, GDScriptInstance *p_insta //member indexing is O(1) return &p_instance->members.write[address]; } break; - case ADDR_TYPE_CLASS_CONSTANT: { - //todo change to index! - GDScript *s = p_script; -#ifdef DEBUG_ENABLED - ERR_FAIL_INDEX_V(address, _global_names_count, nullptr); -#endif - const StringName *sn = &_global_names_ptr[address]; - - while (s) { - GDScript *o = s; - while (o) { - Map<StringName, Variant>::Element *E = o->constants.find(*sn); - if (E) { - return &E->get(); - } - o = o->_owner; - } - s = s->_base; - } - - ERR_FAIL_V_MSG(nullptr, "GDScriptCompiler bug."); - } break; - case ADDR_TYPE_LOCAL_CONSTANT: { -#ifdef DEBUG_ENABLED - ERR_FAIL_INDEX_V(address, _constant_count, nullptr); -#endif - return &_constants_ptr[address]; - } break; - case ADDR_TYPE_STACK: - case ADDR_TYPE_STACK_VARIABLE: { -#ifdef DEBUG_ENABLED - ERR_FAIL_INDEX_V(address, _stack_size, nullptr); -#endif - return &p_stack[address]; - } break; - case ADDR_TYPE_GLOBAL: { -#ifdef DEBUG_ENABLED - ERR_FAIL_INDEX_V(address, GDScriptLanguage::get_singleton()->get_global_array_size(), nullptr); -#endif - return &GDScriptLanguage::get_singleton()->get_global_array()[address]; - } break; -#ifdef TOOLS_ENABLED - case ADDR_TYPE_NAMED_GLOBAL: { -#ifdef DEBUG_ENABLED - ERR_FAIL_INDEX_V(address, _global_names_count, nullptr); -#endif - StringName id = _global_names_ptr[address]; - - if (GDScriptLanguage::get_singleton()->get_named_globals_map().has(id)) { - return (Variant *)&GDScriptLanguage::get_singleton()->get_named_globals_map()[id]; - } else { - r_error = "Autoload singleton '" + String(id) + "' has been removed."; - return nullptr; - } - } break; -#endif - case ADDR_TYPE_NIL: { - return &nil; - } break; } ERR_FAIL_V_MSG(nullptr, "Bad code! (unknown addressing mode)."); @@ -128,7 +69,10 @@ Variant *GDScriptFunction::_get_variant(int p_address, GDScriptInstance *p_insta #ifdef DEBUG_ENABLED static String _get_script_name(const Ref<Script> p_script) { - if (p_script->get_name().is_empty()) { + Ref<GDScript> gdscript = p_script; + if (gdscript.is_valid()) { + return gdscript->get_script_class_name(); + } else if (p_script->get_name().is_empty()) { return p_script->get_path().get_file(); } else { return p_script->get_name(); @@ -293,6 +237,10 @@ String GDScriptFunction::_get_call_error(const Callable::CallError &p_err, const &&OPCODE_JUMP_IF_NOT, \ &&OPCODE_JUMP_TO_DEF_ARGUMENT, \ &&OPCODE_RETURN, \ + &&OPCODE_RETURN_TYPED_BUILTIN, \ + &&OPCODE_RETURN_TYPED_ARRAY, \ + &&OPCODE_RETURN_TYPED_NATIVE, \ + &&OPCODE_RETURN_TYPED_SCRIPT, \ &&OPCODE_ITERATE_BEGIN, \ &&OPCODE_ITERATE_BEGIN_INT, \ &&OPCODE_ITERATE_BEGIN_FLOAT, \ @@ -333,6 +281,7 @@ String GDScriptFunction::_get_call_error(const Callable::CallError &p_err, const &&OPCODE_ITERATE_PACKED_VECTOR3_ARRAY, \ &&OPCODE_ITERATE_PACKED_COLOR_ARRAY, \ &&OPCODE_ITERATE_OBJECT, \ + &&OPCODE_STORE_NAMED_GLOBAL, \ &&OPCODE_ASSERT, \ &&OPCODE_BREAKPOINT, \ &&OPCODE_LINE, \ @@ -408,11 +357,9 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a r_err.error = Callable::CallError::CALL_OK; - Variant self; - Variant static_ref; Variant retvalue; Variant *stack = nullptr; - Variant **instruction_args; + Variant **instruction_args = nullptr; const void **call_args_ptr = nullptr; int defarg = 0; @@ -437,7 +384,6 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a script = p_state->script; p_instance = p_state->instance; defarg = p_state->defarg; - self = p_state->self; } else { if (p_argcount != _argument_count) { @@ -455,55 +401,49 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a } } - alloca_size = sizeof(Variant *) * _instruction_args_size + sizeof(Variant) * _stack_size; - - if (alloca_size) { - uint8_t *aptr = (uint8_t *)alloca(alloca_size); + // Add 3 here for self, class, and nil. + alloca_size = sizeof(Variant *) * 3 + sizeof(Variant *) * _instruction_args_size + sizeof(Variant) * _stack_size; - if (_stack_size) { - stack = (Variant *)aptr; - for (int i = 0; i < p_argcount; i++) { - if (!argument_types[i].has_type) { - memnew_placement(&stack[i], Variant(*p_args[i])); - continue; - } + uint8_t *aptr = (uint8_t *)alloca(alloca_size); + stack = (Variant *)aptr; - if (!argument_types[i].is_type(*p_args[i], true)) { - r_err.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_err.argument = i; - r_err.expected = argument_types[i].kind == GDScriptDataType::BUILTIN ? argument_types[i].builtin_type : Variant::OBJECT; - return Variant(); - } - if (argument_types[i].kind == GDScriptDataType::BUILTIN) { - Variant arg; - Variant::construct(argument_types[i].builtin_type, arg, &p_args[i], 1, r_err); - memnew_placement(&stack[i], Variant(arg)); - } else { - memnew_placement(&stack[i], Variant(*p_args[i])); - } - } - for (int i = p_argcount; i < _stack_size; i++) { - memnew_placement(&stack[i], Variant); - } - } else { - stack = nullptr; + for (int i = 0; i < p_argcount; i++) { + if (!argument_types[i].has_type) { + memnew_placement(&stack[i + 3], Variant(*p_args[i])); + continue; } - if (_instruction_args_size) { - instruction_args = (Variant **)&aptr[sizeof(Variant) * _stack_size]; + if (!argument_types[i].is_type(*p_args[i], true)) { + r_err.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_err.argument = i; + r_err.expected = argument_types[i].kind == GDScriptDataType::BUILTIN ? argument_types[i].builtin_type : Variant::OBJECT; + return Variant(); + } + if (argument_types[i].kind == GDScriptDataType::BUILTIN) { + Variant arg; + Variant::construct(argument_types[i].builtin_type, arg, &p_args[i], 1, r_err); + memnew_placement(&stack[i + 3], Variant(arg)); } else { - instruction_args = nullptr; + memnew_placement(&stack[i + 3], Variant(*p_args[i])); } + } + for (int i = p_argcount + 3; i < _stack_size; i++) { + memnew_placement(&stack[i], Variant); + } + + memnew_placement(&stack[ADDR_STACK_NIL], Variant); + if (_instruction_args_size) { + instruction_args = (Variant **)&aptr[sizeof(Variant) * _stack_size]; } else { - stack = nullptr; instruction_args = nullptr; } if (p_instance) { - self = p_instance->owner; + memnew_placement(&stack[ADDR_STACK_SELF], Variant(p_instance->owner)); script = p_instance->script.ptr(); } else { + memnew_placement(&stack[ADDR_STACK_SELF], Variant); script = _script; } } @@ -513,7 +453,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a call_args_ptr = nullptr; } - static_ref = script; + memnew_placement(&stack[ADDR_STACK_CLASS], Variant(script)); String err_text; @@ -534,10 +474,10 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a #define CHECK_SPACE(m_space) \ GD_ERR_BREAK((ip + m_space) > _code_size) -#define GET_VARIANT_PTR(m_v, m_code_ofs) \ - Variant *m_v; \ - m_v = _get_variant(_code_ptr[ip + m_code_ofs], p_instance, script, self, static_ref, stack, err_text); \ - if (unlikely(!m_v)) \ +#define GET_VARIANT_PTR(m_v, m_code_ofs) \ + Variant *m_v; \ + m_v = _get_variant(_code_ptr[ip + m_code_ofs], p_instance, stack, err_text); \ + if (unlikely(!m_v)) \ OPCODE_BREAK; #else @@ -545,7 +485,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a #define CHECK_SPACE(m_space) #define GET_VARIANT_PTR(m_v, m_code_ofs) \ Variant *m_v; \ - m_v = _get_variant(_code_ptr[ip + m_code_ofs], p_instance, script, self, static_ref, stack, err_text); + m_v = _get_variant(_code_ptr[ip + m_code_ofs], p_instance, stack, err_text); #endif @@ -569,7 +509,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a #ifdef DEBUG_ENABLED OPCODE_WHILE(ip < _code_size) { - int last_opcode = _code_ptr[ip]; + int last_opcode = _code_ptr[ip] & INSTR_MASK; #else OPCODE_WHILE(true) { #endif @@ -1113,14 +1053,14 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a #ifdef DEBUG_ENABLED err_text = "Trying to assign value of type '" + Variant::get_type_name(src->get_type()) + "' to a variable of type '" + +"'."; - OPCODE_BREAK; #endif + OPCODE_BREAK; } if (!dst_arr->typed_assign(*src)) { #ifdef DEBUG_ENABLED err_text = "Trying to assign a typed array with an array of different type.'"; - OPCODE_BREAK; #endif + OPCODE_BREAK; } ip += 3; @@ -2031,7 +1971,6 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a memnew_placement(&gdfs->state.stack.write[sizeof(Variant) * i], Variant(stack[i])); } gdfs->state.stack_size = _stack_size; - gdfs->state.self = self; gdfs->state.alloca_size = alloca_size; gdfs->state.ip = ip + 2; gdfs->state.line = line; @@ -2143,6 +2082,183 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a OPCODE_BREAK; } + OPCODE(OPCODE_RETURN_TYPED_BUILTIN) { + CHECK_SPACE(3); + GET_INSTRUCTION_ARG(r, 0); + + Variant::Type ret_type = (Variant::Type)_code_ptr[ip + 2]; + GD_ERR_BREAK(ret_type < 0 || ret_type >= Variant::VARIANT_MAX); + + if (r->get_type() != ret_type) { + if (Variant::can_convert_strict(r->get_type(), ret_type)) { + Callable::CallError ce; + Variant::construct(ret_type, retvalue, const_cast<const Variant **>(&r), 1, ce); + } else { +#ifdef DEBUG_ENABLED + err_text = vformat(R"(Trying to return value of type "%s" from a function which the return type is "%s".)", + Variant::get_type_name(r->get_type()), Variant::get_type_name(ret_type)); +#endif // DEBUG_ENABLED + + // Construct a base type anyway so type constraints are met. + Callable::CallError ce; + Variant::construct(ret_type, retvalue, nullptr, 0, ce); + OPCODE_BREAK; + } + } else { + retvalue = *r; + } +#ifdef DEBUG_ENABLED + exit_ok = true; +#endif // DEBUG_ENABLED + OPCODE_BREAK; + } + + OPCODE(OPCODE_RETURN_TYPED_ARRAY) { + CHECK_SPACE(5); + GET_INSTRUCTION_ARG(r, 0); + + GET_INSTRUCTION_ARG(script_type, 1); + Variant::Type builtin_type = (Variant::Type)_code_ptr[ip + 3]; + int native_type_idx = _code_ptr[ip + 4]; + GD_ERR_BREAK(native_type_idx < 0 || native_type_idx >= _global_names_count); + const StringName native_type = _global_names_ptr[native_type_idx]; + + if (r->get_type() != Variant::ARRAY) { +#ifdef DEBUG_ENABLED + err_text = vformat(R"(Trying to return value of type "%s" from a function which the return type is "Array[%s]".)", + Variant::get_type_name(r->get_type()), Variant::get_type_name(builtin_type)); +#endif + OPCODE_BREAK; + } + + Array array; + array.set_typed(builtin_type, native_type, script_type); + +#ifdef DEBUG_ENABLED + bool valid = array.typed_assign(*VariantInternal::get_array(r)); +#else + array.typed_assign(*VariantInternal::get_array(r)); +#endif // DEBUG_ENABLED + + // Assign the return value anyway since we want it to be the valid type. + retvalue = array; + +#ifdef DEBUG_ENABLED + if (!valid) { + err_text = "Trying to return a typed array with an array of different type.'"; + OPCODE_BREAK; + } + + exit_ok = true; +#endif // DEBUG_ENABLED + OPCODE_BREAK; + } + + OPCODE(OPCODE_RETURN_TYPED_NATIVE) { + CHECK_SPACE(3); + GET_INSTRUCTION_ARG(r, 0); + + GET_INSTRUCTION_ARG(type, 1); + GDScriptNativeClass *nc = Object::cast_to<GDScriptNativeClass>(type->operator Object *()); + GD_ERR_BREAK(!nc); + + if (r->get_type() != Variant::OBJECT && r->get_type() != Variant::NIL) { + err_text = vformat(R"(Trying to return value of type "%s" from a function which the return type is "%s".)", + Variant::get_type_name(r->get_type()), nc->get_name()); + OPCODE_BREAK; + } + +#ifdef DEBUG_ENABLED + bool freed = false; + Object *ret_obj = r->get_validated_object_with_check(freed); + + if (freed) { + err_text = "Trying to return a previously freed instance."; + OPCODE_BREAK; + } +#else + Object *ret_obj = r->operator Object *(); +#endif // DEBUG_ENABLED + if (ret_obj && !ClassDB::is_parent_class(ret_obj->get_class_name(), nc->get_name())) { +#ifdef DEBUG_ENABLED + err_text = vformat(R"(Trying to return value of type "%s" from a function which the return type is "%s".)", + ret_obj->get_class_name(), nc->get_name()); +#endif // DEBUG_ENABLED + OPCODE_BREAK; + } + retvalue = *r; + +#ifdef DEBUG_ENABLED + exit_ok = true; +#endif // DEBUG_ENABLED + OPCODE_BREAK; + } + + OPCODE(OPCODE_RETURN_TYPED_SCRIPT) { + CHECK_SPACE(3); + GET_INSTRUCTION_ARG(r, 0); + + GET_INSTRUCTION_ARG(type, 1); + Script *base_type = Object::cast_to<Script>(type->operator Object *()); + GD_ERR_BREAK(!base_type); + + if (r->get_type() != Variant::OBJECT && r->get_type() != Variant::NIL) { +#ifdef DEBUG_ENABLED + err_text = vformat(R"(Trying to return value of type "%s" from a function which the return type is "%s".)", + Variant::get_type_name(r->get_type()), _get_script_name(Ref<Script>(base_type))); +#endif // DEBUG_ENABLED + OPCODE_BREAK; + } + +#ifdef DEBUG_ENABLED + bool freed = false; + Object *ret_obj = r->get_validated_object_with_check(freed); + + if (freed) { + err_text = "Trying to return a previously freed instance."; + OPCODE_BREAK; + } +#else + Object *ret_obj = r->operator Object *(); +#endif // DEBUG_ENABLED + + if (ret_obj) { + ScriptInstance *ret_inst = ret_obj->get_script_instance(); + if (!ret_inst) { +#ifdef DEBUG_ENABLED + err_text = vformat(R"(Trying to return value of type "%s" from a function which the return type is "%s".)", + ret_obj->get_class_name(), _get_script_name(Ref<GDScript>(base_type))); +#endif // DEBUG_ENABLED + OPCODE_BREAK; + } + + Script *ret_type = ret_obj->get_script_instance()->get_script().ptr(); + bool valid = false; + + while (ret_type) { + if (ret_type == base_type) { + valid = true; + break; + } + ret_type = ret_type->get_base_script().ptr(); + } + + if (!valid) { +#ifdef DEBUG_ENABLED + err_text = vformat(R"(Trying to return value of type "%s" from a function which the return type is "%s".)", + _get_script_name(ret_obj->get_script_instance()->get_script()), _get_script_name(Ref<GDScript>(base_type))); +#endif // DEBUG_ENABLED + OPCODE_BREAK; + } + } + retvalue = *r; + +#ifdef DEBUG_ENABLED + exit_ok = true; +#endif // DEBUG_ENABLED + OPCODE_BREAK; + } + OPCODE(OPCODE_ITERATE_BEGIN) { CHECK_SPACE(8); // Space for this and a regular iterate. @@ -2844,6 +2960,19 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a } DISPATCH_OPCODE; + OPCODE(OPCODE_STORE_NAMED_GLOBAL) { + CHECK_SPACE(3); + int globalname_idx = _code_ptr[ip + 2]; + GD_ERR_BREAK(globalname_idx < 0 || globalname_idx >= _global_names_count); + const StringName *globalname = &_global_names_ptr[globalname_idx]; + + GET_INSTRUCTION_ARG(dst, 0); + *dst = GDScriptLanguage::get_singleton()->get_named_globals_map()[*globalname]; + + ip += 3; + } + DISPATCH_OPCODE; + OPCODE(OPCODE_ASSERT) { CHECK_SPACE(3); |