diff options
Diffstat (limited to 'modules/gdscript/gdscript_function.cpp')
-rw-r--r-- | modules/gdscript/gdscript_function.cpp | 1127 |
1 files changed, 800 insertions, 327 deletions
diff --git a/modules/gdscript/gdscript_function.cpp b/modules/gdscript/gdscript_function.cpp index 452b1933eb..e59f99fc56 100644 --- a/modules/gdscript/gdscript_function.cpp +++ b/modules/gdscript/gdscript_function.cpp @@ -34,49 +34,48 @@ #include "gdscript.h" #include "gdscript_functions.h" -Variant *GDScriptFunction::_get_variant(int p_address, GDScriptInstance *p_instance, GDScript *p_script, Variant &self, Variant *p_stack, String &r_error) const { +#ifdef DEBUG_ENABLED +#include "core/string_builder.h" +#endif +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 { 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: { #ifdef DEBUG_ENABLED if (unlikely(!p_instance)) { r_error = "Cannot access self without instance."; - return NULL; + return nullptr; } #endif return &self; } break; case ADDR_TYPE_CLASS: { - - return &p_script->_static_ref; + return &static_ref; } break; case ADDR_TYPE_MEMBER: { #ifdef DEBUG_ENABLED if (unlikely(!p_instance)) { r_error = "Cannot access member without instance."; - return NULL; + return nullptr; } #endif //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, NULL); + 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(); @@ -86,39 +85,39 @@ Variant *GDScriptFunction::_get_variant(int p_address, GDScriptInstance *p_insta s = s->_base; } - ERR_FAIL_V_MSG(NULL, "GDScriptCompiler bug."); + ERR_FAIL_V_MSG(nullptr, "GDScriptCompiler bug."); } break; case ADDR_TYPE_LOCAL_CONSTANT: { #ifdef DEBUG_ENABLED - ERR_FAIL_INDEX_V(address, _constant_count, NULL); + 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, NULL); + 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(), NULL); + 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, _named_globals_count, NULL); + ERR_FAIL_INDEX_V(address, _global_names_count, nullptr); #endif - StringName id = _named_globals_ptr[address]; + 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 NULL; + return nullptr; } } break; #endif @@ -127,27 +126,28 @@ Variant *GDScriptFunction::_get_variant(int p_address, GDScriptInstance *p_insta } break; } - ERR_FAIL_V_MSG(NULL, "Bad code! (unknown addressing mode)."); - return NULL; + ERR_FAIL_V_MSG(nullptr, "Bad code! (unknown addressing mode)."); + return nullptr; } #ifdef DEBUG_ENABLED static String _get_var_type(const Variant *p_var) { - String basestr; if (p_var->get_type() == Variant::OBJECT) { - Object *bobj = *p_var; + bool was_freed; + Object *bobj = p_var->get_validated_object_with_check(was_freed); if (!bobj) { - basestr = "null instance"; + if (was_freed) { + basestr = "null instance"; + } else { + basestr = "previously freed"; + } } else { - if (ObjectDB::instance_validate(bobj)) { - if (bobj->get_script_instance()) - basestr = bobj->get_class() + " (" + bobj->get_script_instance()->get_script()->get_path().get_file() + ")"; - else - basestr = bobj->get_class(); + if (bobj->get_script_instance()) { + basestr = bobj->get_class() + " (" + bobj->get_script_instance()->get_script()->get_path().get_file() + ")"; } else { - basestr = "previously freed instance"; + basestr = bobj->get_class(); } } @@ -159,11 +159,10 @@ static String _get_var_type(const Variant *p_var) { } #endif // DEBUG_ENABLED -String GDScriptFunction::_get_call_error(const Variant::CallError &p_err, const String &p_where, const Variant **argptrs) const { - +String GDScriptFunction::_get_call_error(const Callable::CallError &p_err, const String &p_where, const Variant **argptrs) const { String err_text; - if (p_err.error == Variant::CallError::CALL_ERROR_INVALID_ARGUMENT) { + if (p_err.error == Callable::CallError::CALL_ERROR_INVALID_ARGUMENT) { int errorarg = p_err.argument; // Handle the Object to Object case separately as we don't have further class details. #ifdef DEBUG_ENABLED @@ -172,15 +171,15 @@ String GDScriptFunction::_get_call_error(const Variant::CallError &p_err, const } else #endif // DEBUG_ENABLED { - err_text = "Invalid type in " + p_where + ". Cannot convert argument " + itos(errorarg + 1) + " from " + Variant::get_type_name(argptrs[errorarg]->get_type()) + " to " + Variant::get_type_name(p_err.expected) + "."; + err_text = "Invalid type in " + p_where + ". Cannot convert argument " + itos(errorarg + 1) + " from " + Variant::get_type_name(argptrs[errorarg]->get_type()) + " to " + Variant::get_type_name(Variant::Type(p_err.expected)) + "."; } - } else if (p_err.error == Variant::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS) { + } else if (p_err.error == Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS) { err_text = "Invalid call to " + p_where + ". Expected " + itos(p_err.argument) + " arguments."; - } else if (p_err.error == Variant::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS) { + } else if (p_err.error == Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS) { err_text = "Invalid call to " + p_where + ". Expected " + itos(p_err.argument) + " arguments."; - } else if (p_err.error == Variant::CallError::CALL_ERROR_INVALID_METHOD) { + } else if (p_err.error == Callable::CallError::CALL_ERROR_INVALID_METHOD) { err_text = "Invalid call. Nonexistent " + p_where + "."; - } else if (p_err.error == Variant::CallError::CALL_ERROR_INSTANCE_IS_NULL) { + } else if (p_err.error == Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL) { err_text = "Attempt to call " + p_where + " on a null instance."; } else { err_text = "Bug, call error: #" + itos(p_err.error); @@ -215,12 +214,11 @@ String GDScriptFunction::_get_call_error(const Variant::CallError &p_err, const &&OPCODE_CONSTRUCT_DICTIONARY, \ &&OPCODE_CALL, \ &&OPCODE_CALL_RETURN, \ + &&OPCODE_CALL_ASYNC, \ &&OPCODE_CALL_BUILT_IN, \ - &&OPCODE_CALL_SELF, \ &&OPCODE_CALL_SELF_BASE, \ - &&OPCODE_YIELD, \ - &&OPCODE_YIELD_SIGNAL, \ - &&OPCODE_YIELD_RESUME, \ + &&OPCODE_AWAIT, \ + &&OPCODE_AWAIT_RESUME, \ &&OPCODE_JUMP, \ &&OPCODE_JUMP_IF, \ &&OPCODE_JUMP_IF_NOT, \ @@ -232,7 +230,8 @@ String GDScriptFunction::_get_call_error(const Variant::CallError &p_err, const &&OPCODE_BREAKPOINT, \ &&OPCODE_LINE, \ &&OPCODE_END \ - }; + }; \ + static_assert((sizeof(switch_table_ops) / sizeof(switch_table_ops[0]) == (OPCODE_END + 1)), "Opcodes in jump table aren't the same as opcodes in enum."); #define OPCODE(m_op) \ m_op: @@ -257,20 +256,19 @@ String GDScriptFunction::_get_call_error(const Variant::CallError &p_err, const #define OPCODE_OUT break #endif -Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_args, int p_argcount, Variant::CallError &r_err, CallState *p_state) { - +Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_args, int p_argcount, Callable::CallError &r_err, CallState *p_state) { OPCODES_TABLE; if (!_code_ptr) { - return Variant(); } - r_err.error = Variant::CallError::CALL_OK; + r_err.error = Callable::CallError::CALL_OK; Variant self; + Variant static_ref; Variant retvalue; - Variant *stack = NULL; + Variant *stack = nullptr; Variant **call_args; int defarg = 0; @@ -286,35 +284,29 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a int line = _initial_line; if (p_state) { - //use existing (supplied) state (yielded) + //use existing (supplied) state (awaited) stack = (Variant *)p_state->stack.ptr(); call_args = (Variant **)&p_state->stack.ptr()[sizeof(Variant) * p_state->stack_size]; //ptr() to avoid bounds check line = p_state->line; ip = p_state->ip; alloca_size = p_state->stack.size(); - script = p_state->script.ptr(); + script = p_state->script; p_instance = p_state->instance; defarg = p_state->defarg; self = p_state->self; - //stack[p_state->result_pos]=p_state->result; //assign stack with result } else { - if (p_argcount != _argument_count) { - if (p_argcount > _argument_count) { - - r_err.error = Variant::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS; + r_err.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS; r_err.argument = _argument_count; return Variant(); } else if (p_argcount < _argument_count - _default_arg_count) { - - r_err.error = Variant::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; + r_err.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; r_err.argument = _argument_count - _default_arg_count; return Variant(); } else { - defarg = _argument_count - p_argcount; } } @@ -322,11 +314,9 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a alloca_size = sizeof(Variant *) * _call_size + sizeof(Variant) * _stack_size; if (alloca_size) { - uint8_t *aptr = (uint8_t *)alloca(alloca_size); if (_stack_size) { - stack = (Variant *)aptr; for (int i = 0; i < p_argcount; i++) { if (!argument_types[i].has_type) { @@ -335,15 +325,10 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a } if (!argument_types[i].is_type(*p_args[i], true)) { - if (argument_types[i].is_type(Variant(), true)) { - memnew_placement(&stack[i], Variant); - continue; - } else { - r_err.error = Variant::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(); - } + 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, &p_args[i], 1, r_err); @@ -356,25 +341,22 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a memnew_placement(&stack[i], Variant); } } else { - stack = NULL; + stack = nullptr; } if (_call_size) { - call_args = (Variant **)&aptr[sizeof(Variant) * _stack_size]; } else { - - call_args = NULL; + call_args = nullptr; } } else { - stack = NULL; - call_args = NULL; + stack = nullptr; + call_args = nullptr; } if (p_instance) { if (p_instance->base_ref && static_cast<Reference *>(p_instance->owner)->is_referenced()) { - self = REF(static_cast<Reference *>(p_instance->owner)); } else { self = p_instance->owner; @@ -385,12 +367,15 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a } } + static_ref = script; + String err_text; #ifdef DEBUG_ENABLED - if (ScriptDebugger::get_singleton()) + if (EngineDebugger::is_active()) { GDScriptLanguage::get_singleton()->enter_function(p_instance, this, stack, &ip, &line); + } #define GD_ERR_BREAK(m_cond) \ { \ @@ -403,10 +388,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, 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, script, self, static_ref, stack, err_text); \ + if (unlikely(!m_v)) \ OPCODE_BREAK; #else @@ -414,7 +399,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, stack, err_text); + m_v = _get_variant(_code_ptr[ip + m_code_ofs], p_instance, script, self, static_ref, stack, err_text); #endif @@ -430,7 +415,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a profile.frame_call_count++; } bool exit_ok = false; - bool yielded = false; + bool awaited = false; #endif #ifdef DEBUG_ENABLED @@ -441,9 +426,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a #endif OPCODE_SWITCH(_code_ptr[ip]) { - OPCODE(OPCODE_OPERATOR) { - CHECK_SPACE(5); bool valid; @@ -463,7 +446,6 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a #endif #ifdef DEBUG_ENABLED if (!valid) { - if (ret.get_type() == Variant::STRING) { //return a string when invalid with the error err_text = ret; @@ -480,7 +462,6 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a DISPATCH_OPCODE; OPCODE(OPCODE_EXTENDS_TEST) { - CHECK_SPACE(4); GET_VARIANT_PTR(a, 1); @@ -488,23 +469,33 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a GET_VARIANT_PTR(dst, 3); #ifdef DEBUG_ENABLED - if (b->get_type() != Variant::OBJECT || b->operator Object *() == NULL) { - + if (b->get_type() != Variant::OBJECT || b->operator Object *() == nullptr) { err_text = "Right operand of 'is' is not a class."; OPCODE_BREAK; } #endif bool extends_ok = false; - if (a->get_type() == Variant::OBJECT && a->operator Object *() != NULL) { - Object *obj_A = *a; - Object *obj_B = *b; - + if (a->get_type() == Variant::OBJECT && a->operator Object *() != nullptr) { #ifdef DEBUG_ENABLED - if (!ObjectDB::instance_validate(obj_A)) { - err_text = "Left operand of 'is' was already freed."; + bool was_freed; + Object *obj_A = a->get_validated_object_with_check(was_freed); + + if (was_freed) { + err_text = "Left operand of 'is' is a previously freed instance."; OPCODE_BREAK; } + + Object *obj_B = b->get_validated_object_with_check(was_freed); + + if (was_freed) { + err_text = "Right operand of 'is' is a previously freed instance."; + OPCODE_BREAK; + } +#else + + Object *obj_A = *a; + Object *obj_B = *b; #endif // DEBUG_ENABLED GDScript *scr_B = Object::cast_to<GDScript>(obj_B); @@ -514,11 +505,9 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a //in other situation, this shoul return false. 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; @@ -530,12 +519,10 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a } } else { - GDScriptNativeClass *nc = Object::cast_to<GDScriptNativeClass>(obj_B); #ifdef DEBUG_ENABLED if (!nc) { - err_text = "Right operand of 'is' is not a class (type: '" + obj_B->get_class() + "')."; OPCODE_BREAK; } @@ -550,7 +537,6 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a DISPATCH_OPCODE; OPCODE(OPCODE_IS_BUILTIN) { - CHECK_SPACE(4); GET_VARIANT_PTR(value, 1); @@ -565,7 +551,6 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a DISPATCH_OPCODE; OPCODE(OPCODE_SET) { - CHECK_SPACE(3); GET_VARIANT_PTR(dst, 1); @@ -592,7 +577,6 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a DISPATCH_OPCODE; OPCODE(OPCODE_GET) { - CHECK_SPACE(3); GET_VARIANT_PTR(src, 1); @@ -625,7 +609,6 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a DISPATCH_OPCODE; OPCODE(OPCODE_SET_NAMED) { - CHECK_SPACE(3); GET_VARIANT_PTR(dst, 1); @@ -651,7 +634,6 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a DISPATCH_OPCODE; OPCODE(OPCODE_GET_NAMED) { - CHECK_SPACE(4); GET_VARIANT_PTR(src, 1); @@ -686,7 +668,6 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a DISPATCH_OPCODE; OPCODE(OPCODE_SET_MEMBER) { - CHECK_SPACE(3); int indexname = _code_ptr[ip + 1]; GD_ERR_BREAK(indexname < 0 || indexname >= _global_names_count); @@ -711,7 +692,6 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a DISPATCH_OPCODE; OPCODE(OPCODE_GET_MEMBER) { - CHECK_SPACE(3); int indexname = _code_ptr[ip + 1]; GD_ERR_BREAK(indexname < 0 || indexname >= _global_names_count); @@ -732,7 +712,6 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a DISPATCH_OPCODE; OPCODE(OPCODE_ASSIGN) { - CHECK_SPACE(3); GET_VARIANT_PTR(dst, 1); GET_VARIANT_PTR(src, 2); @@ -744,7 +723,6 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a DISPATCH_OPCODE; OPCODE(OPCODE_ASSIGN_TRUE) { - CHECK_SPACE(2); GET_VARIANT_PTR(dst, 1); @@ -755,7 +733,6 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a DISPATCH_OPCODE; OPCODE(OPCODE_ASSIGN_FALSE) { - CHECK_SPACE(2); GET_VARIANT_PTR(dst, 1); @@ -766,7 +743,6 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a DISPATCH_OPCODE; OPCODE(OPCODE_ASSIGN_TYPED_BUILTIN) { - CHECK_SPACE(4); GET_VARIANT_PTR(dst, 2); GET_VARIANT_PTR(src, 3); @@ -778,7 +754,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a #ifdef DEBUG_ENABLED if (Variant::can_convert_strict(src->get_type(), var_type)) { #endif // DEBUG_ENABLED - Variant::CallError ce; + Callable::CallError ce; *dst = Variant::construct(var_type, const_cast<const Variant **>(&src), 1, ce); } else { #ifdef DEBUG_ENABLED @@ -796,7 +772,6 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a DISPATCH_OPCODE; OPCODE(OPCODE_ASSIGN_TYPED_NATIVE) { - CHECK_SPACE(4); GET_VARIANT_PTR(dst, 2); GET_VARIANT_PTR(src, 3); @@ -825,7 +800,6 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a DISPATCH_OPCODE; OPCODE(OPCODE_ASSIGN_TYPED_SCRIPT) { - CHECK_SPACE(4); GET_VARIANT_PTR(dst, 2); GET_VARIANT_PTR(src, 3); @@ -841,8 +815,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a OPCODE_BREAK; } - if (src->get_type() != Variant::NIL && src->operator Object *() != NULL) { - + 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() + @@ -876,7 +849,6 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a DISPATCH_OPCODE; OPCODE(OPCODE_CAST_TO_BUILTIN) { - CHECK_SPACE(4); Variant::Type to_type = (Variant::Type)_code_ptr[ip + 1]; GET_VARIANT_PTR(src, 2); @@ -884,11 +856,11 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a GD_ERR_BREAK(to_type < 0 || to_type >= Variant::VARIANT_MAX); - Variant::CallError err; + Callable::CallError err; *dst = Variant::construct(to_type, (const Variant **)&src, 1, err); #ifdef DEBUG_ENABLED - if (err.error != Variant::CallError::CALL_OK) { + if (err.error != Callable::CallError::CALL_OK) { err_text = "Invalid cast: could not convert value to '" + Variant::get_type_name(to_type) + "'."; OPCODE_BREAK; } @@ -899,7 +871,6 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a DISPATCH_OPCODE; OPCODE(OPCODE_CAST_TO_NATIVE) { - CHECK_SPACE(4); GET_VARIANT_PTR(to_type, 1); GET_VARIANT_PTR(src, 2); @@ -927,7 +898,6 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a DISPATCH_OPCODE; OPCODE(OPCODE_CAST_TO_SCRIPT) { - CHECK_SPACE(4); GET_VARIANT_PTR(to_type, 1); GET_VARIANT_PTR(src, 2); @@ -946,12 +916,10 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a bool valid = false; - if (src->get_type() != Variant::NIL && src->operator Object *() != NULL) { - + if (src->get_type() != Variant::NIL && src->operator Object *() != nullptr) { ScriptInstance *scr_inst = src->operator Object *()->get_script_instance(); if (scr_inst) { - Script *src_type = src->operator Object *()->get_script_instance()->get_script().ptr(); while (src_type) { @@ -975,7 +943,6 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a DISPATCH_OPCODE; OPCODE(OPCODE_CONSTRUCT) { - CHECK_SPACE(2); Variant::Type t = Variant::Type(_code_ptr[ip + 1]); int argc = _code_ptr[ip + 2]; @@ -987,12 +954,11 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a } GET_VARIANT_PTR(dst, 3 + argc); - Variant::CallError err; + Callable::CallError err; *dst = Variant::construct(t, (const Variant **)argptrs, argc, err); #ifdef DEBUG_ENABLED - if (err.error != Variant::CallError::CALL_OK) { - + if (err.error != Callable::CallError::CALL_OK) { err_text = _get_call_error(err, "'" + Variant::get_type_name(t) + "' constructor", (const Variant **)argptrs); OPCODE_BREAK; } @@ -1004,7 +970,6 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a DISPATCH_OPCODE; OPCODE(OPCODE_CONSTRUCT_ARRAY) { - CHECK_SPACE(1); int argc = _code_ptr[ip + 1]; Array array; //arrays are always shared @@ -1025,7 +990,6 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a DISPATCH_OPCODE; OPCODE(OPCODE_CONSTRUCT_DICTIONARY) { - CHECK_SPACE(1); int argc = _code_ptr[ip + 1]; Dictionary dict; //arrays are always shared @@ -1033,7 +997,6 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a CHECK_SPACE(argc * 2 + 2); for (int i = 0; i < argc; i++) { - GET_VARIANT_PTR(k, 2 + i * 2 + 0); GET_VARIANT_PTR(v, 2 + i * 2 + 1); dict[*k] = *v; @@ -1047,11 +1010,14 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a } DISPATCH_OPCODE; + OPCODE(OPCODE_CALL_ASYNC) OPCODE(OPCODE_CALL_RETURN) OPCODE(OPCODE_CALL) { - CHECK_SPACE(4); - bool call_ret = _code_ptr[ip] == OPCODE_CALL_RETURN; + bool call_ret = _code_ptr[ip] != OPCODE_CALL; +#ifdef DEBUG_ENABLED + bool call_async = _code_ptr[ip] == OPCODE_CALL_ASYNC; +#endif int argc = _code_ptr[ip + 1]; GET_VARIANT_PTR(base, 2); @@ -1078,41 +1044,51 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a } #endif - Variant::CallError err; + Callable::CallError err; if (call_ret) { - GET_VARIANT_PTR(ret, argc); base->call_ptr(*methodname, (const Variant **)argptrs, argc, ret, err); - } else { +#ifdef DEBUG_ENABLED + if (!call_async && ret->get_type() == Variant::OBJECT) { + // Check if getting a function state without await. + bool was_freed = false; + Object *obj = ret->get_validated_object_with_check(was_freed); - base->call_ptr(*methodname, (const Variant **)argptrs, argc, NULL, err); + 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; + } + } +#endif + } else { + base->call_ptr(*methodname, (const Variant **)argptrs, argc, nullptr, err); } #ifdef DEBUG_ENABLED if (GDScriptLanguage::get_singleton()->profiling) { function_call_time += OS::get_singleton()->get_ticks_usec() - call_time; } - if (err.error != Variant::CallError::CALL_OK) { - + if (err.error != Callable::CallError::CALL_OK) { String methodstr = *methodname; String basestr = _get_var_type(base); if (methodstr == "call") { if (argc >= 1) { methodstr = String(*argptrs[0]) + " (via call)"; - if (err.error == Variant::CallError::CALL_ERROR_INVALID_ARGUMENT) { + if (err.error == Callable::CallError::CALL_ERROR_INVALID_ARGUMENT) { err.argument += 1; } } } else if (methodstr == "free") { - - if (err.error == Variant::CallError::CALL_ERROR_INVALID_METHOD) { - + if (err.error == Callable::CallError::CALL_ERROR_INVALID_METHOD) { if (base->is_ref()) { err_text = "Attempted to free a reference."; OPCODE_BREAK; } else if (base->get_type() == Variant::OBJECT) { - err_text = "Attempted to free a locked object (calling or emitting)."; OPCODE_BREAK; } @@ -1123,13 +1099,12 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a } #endif - //_call_func(NULL,base,*methodname,ip,argc,p_instance,stack); + //_call_func(nullptr,base,*methodname,ip,argc,p_instance,stack); ip += argc + 1; } DISPATCH_OPCODE; OPCODE(OPCODE_CALL_BUILT_IN) { - CHECK_SPACE(4); GDScriptFunctions::Function func = GDScriptFunctions::Function(_code_ptr[ip + 1]); @@ -1147,13 +1122,12 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a GET_VARIANT_PTR(dst, argc); - Variant::CallError err; + Callable::CallError err; GDScriptFunctions::call(func, (const Variant **)argptrs, argc, *dst, err); #ifdef DEBUG_ENABLED - if (err.error != Variant::CallError::CALL_OK) { - + if (err.error != Callable::CallError::CALL_OK) { String methodstr = GDScriptFunctions::get_func_name(func); if (dst->get_type() == Variant::STRING) { //call provided error string @@ -1168,19 +1142,12 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a } DISPATCH_OPCODE; - OPCODE(OPCODE_CALL_SELF) { - - OPCODE_BREAK; - } - OPCODE(OPCODE_CALL_SELF_BASE) { - CHECK_SPACE(2); int self_fun = _code_ptr[ip + 1]; #ifdef DEBUG_ENABLED if (self_fun < 0 || self_fun >= _global_names_count) { - err_text = "compiler bug, function name not found"; OPCODE_BREAK; } @@ -1202,43 +1169,39 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a const GDScript *gds = _script; - const Map<StringName, GDScriptFunction *>::Element *E = NULL; + const Map<StringName, GDScriptFunction *>::Element *E = nullptr; while (gds->base.ptr()) { gds = gds->base.ptr(); E = gds->member_functions.find(*methodname); - if (E) + if (E) { break; + } } - Variant::CallError err; + Callable::CallError err; if (E) { - *dst = E->get()->call(p_instance, (const Variant **)argptrs, argc, err); } else if (gds->native.ptr()) { - if (*methodname != GDScriptLanguage::get_singleton()->strings._init) { - MethodBind *mb = ClassDB::get_method(gds->native->get_name(), *methodname); if (!mb) { - err.error = Variant::CallError::CALL_ERROR_INVALID_METHOD; + err.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; } else { *dst = mb->call(p_instance->owner, (const Variant **)argptrs, argc, err); } } else { - err.error = Variant::CallError::CALL_OK; + err.error = Callable::CallError::CALL_OK; } } else { - if (*methodname != GDScriptLanguage::get_singleton()->strings._init) { - err.error = Variant::CallError::CALL_ERROR_INVALID_METHOD; + err.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; } else { - err.error = Variant::CallError::CALL_OK; + err.error = Callable::CallError::CALL_OK; } } - if (err.error != Variant::CallError::CALL_OK) { - + if (err.error != Callable::CallError::CALL_OK) { String methodstr = *methodname; err_text = _get_call_error(err, "function '" + methodstr + "'", (const Variant **)argptrs); @@ -1249,94 +1212,95 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a } DISPATCH_OPCODE; - OPCODE(OPCODE_YIELD) - OPCODE(OPCODE_YIELD_SIGNAL) { + OPCODE(OPCODE_AWAIT) { + CHECK_SPACE(2); - int ipofs = 1; - if (_code_ptr[ip] == OPCODE_YIELD_SIGNAL) { - CHECK_SPACE(4); - ipofs += 2; - } else { - CHECK_SPACE(2); - } + //do the oneshot connect + GET_VARIANT_PTR(argobj, 1); - Ref<GDScriptFunctionState> gdfs = memnew(GDScriptFunctionState); - gdfs->function = this; + Signal sig; + bool is_signal = true; - gdfs->state.stack.resize(alloca_size); - //copy variant stack - for (int i = 0; i < _stack_size; i++) { - 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.script = Ref<GDScript>(_script); - gdfs->state.ip = ip + ipofs; - gdfs->state.line = line; - gdfs->state.instance_id = (p_instance && p_instance->get_owner()) ? p_instance->get_owner()->get_instance_id() : 0; - //gdfs->state.result_pos=ip+ipofs-1; - gdfs->state.defarg = defarg; - gdfs->state.instance = p_instance; - gdfs->function = this; - - retvalue = gdfs; - - if (_code_ptr[ip] == OPCODE_YIELD_SIGNAL) { - //do the oneshot connect - GET_VARIANT_PTR(argobj, 1); - GET_VARIANT_PTR(argname, 2); + { + Variant result = *argobj; -#ifdef DEBUG_ENABLED - if (argobj->get_type() != Variant::OBJECT) { - err_text = "First argument of yield() not of type object."; - OPCODE_BREAK; + if (argobj->get_type() == Variant::OBJECT) { + bool was_freed = false; + Object *obj = argobj->get_validated_object_with_check(was_freed); + + if (was_freed) { + err_text = "Trying to await on a freed object."; + OPCODE_BREAK; + } + + // Is this even possible to be null at this point? + if (obj) { + if (obj->is_class_ptr(GDScriptFunctionState::get_class_ptr_static())) { + static StringName completed = _scs_create("completed"); + result = Signal(obj, completed); + } + } } - if (argname->get_type() != Variant::STRING) { - err_text = "Second argument of yield() not a string (for signal name)."; - OPCODE_BREAK; + + if (result.get_type() != Variant::SIGNAL) { + ip += 4; // Skip OPCODE_AWAIT_RESUME and its data. + // The stack pointer should be the same, so we don't need to set a return value. + is_signal = false; + } else { + sig = result; } -#endif + } - Object *obj = argobj->operator Object *(); - String signal = argname->operator String(); + if (is_signal) { + Ref<GDScriptFunctionState> gdfs = memnew(GDScriptFunctionState); + gdfs->function = this; -#ifdef DEBUG_ENABLED - if (!obj) { - err_text = "First argument of yield() is null."; - OPCODE_BREAK; + gdfs->state.stack.resize(alloca_size); + //copy variant stack + for (int i = 0; i < _stack_size; i++) { + memnew_placement(&gdfs->state.stack.write[sizeof(Variant) * i], Variant(stack[i])); } - if (ScriptDebugger::get_singleton()) { - if (!ObjectDB::instance_validate(obj)) { - err_text = "First argument of yield() is a previously freed instance."; - OPCODE_BREAK; + 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; + gdfs->state.script = _script; + { + MutexLock lock(GDScriptLanguage::get_singleton()->lock); + _script->pending_func_states.add(&gdfs->scripts_list); + if (p_instance) { + gdfs->state.instance = p_instance; + p_instance->pending_func_states.add(&gdfs->instances_list); + } else { + gdfs->state.instance = nullptr; } } - if (signal.length() == 0) { +#ifdef DEBUG_ENABLED + gdfs->state.function_name = name; + gdfs->state.script_path = _script->get_path(); +#endif + gdfs->state.defarg = defarg; + gdfs->function = this; - err_text = "Second argument of yield() is an empty string (for signal name)."; - OPCODE_BREAK; - } + retvalue = gdfs; - Error err = obj->connect(signal, gdfs.ptr(), "_signal_callback", varray(gdfs), Object::CONNECT_ONESHOT); + Error err = sig.connect(Callable(gdfs.ptr(), "_signal_callback"), varray(gdfs), Object::CONNECT_ONESHOT); if (err != OK) { - err_text = "Error connecting to signal: " + signal + " during yield()."; + err_text = "Error connecting to signal: " + sig.get_name() + " during await."; OPCODE_BREAK; } -#else - obj->connect(signal, gdfs.ptr(), "_signal_callback", varray(gdfs), Object::CONNECT_ONESHOT); -#endif - } #ifdef DEBUG_ENABLED - exit_ok = true; - yielded = true; + exit_ok = true; + awaited = true; #endif - OPCODE_BREAK; + OPCODE_BREAK; + } } + DISPATCH_OPCODE; // Needed for synchronous calls (when result is immediately available). - OPCODE(OPCODE_YIELD_RESUME) { - + OPCODE(OPCODE_AWAIT_RESUME) { CHECK_SPACE(2); #ifdef DEBUG_ENABLED if (!p_state) { @@ -1351,7 +1315,6 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a DISPATCH_OPCODE; OPCODE(OPCODE_JUMP) { - CHECK_SPACE(2); int to = _code_ptr[ip + 1]; @@ -1361,7 +1324,6 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a DISPATCH_OPCODE; OPCODE(OPCODE_JUMP_IF) { - CHECK_SPACE(3); GET_VARIANT_PTR(test, 1); @@ -1379,7 +1341,6 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a DISPATCH_OPCODE; OPCODE(OPCODE_JUMP_IF_NOT) { - CHECK_SPACE(3); GET_VARIANT_PTR(test, 1); @@ -1397,14 +1358,12 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a DISPATCH_OPCODE; OPCODE(OPCODE_JUMP_TO_DEF_ARGUMENT) { - CHECK_SPACE(2); ip = _default_arg_ptr[defarg]; } DISPATCH_OPCODE; OPCODE(OPCODE_RETURN) { - CHECK_SPACE(2); GET_VARIANT_PTR(r, 1); retvalue = *r; @@ -1415,7 +1374,6 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a } OPCODE(OPCODE_ITERATE_BEGIN) { - CHECK_SPACE(8); //space for this a regular iterate GET_VARIANT_PTR(counter, 1); @@ -1448,7 +1406,6 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a DISPATCH_OPCODE; OPCODE(OPCODE_ITERATE) { - CHECK_SPACE(4); GET_VARIANT_PTR(counter, 1); @@ -1485,11 +1442,14 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a #ifdef DEBUG_ENABLED GET_VARIANT_PTR(test, 1); - GET_VARIANT_PTR(message, 2); bool result = test->booleanize(); if (!result) { - const String &message_str = *message; + String message_str; + if (_code_ptr[ip + 2] != 0) { + GET_VARIANT_PTR(message, 2); + message_str = *message; + } if (message_str.empty()) { err_text = "Assertion failed."; } else { @@ -1505,7 +1465,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a OPCODE(OPCODE_BREAKPOINT) { #ifdef DEBUG_ENABLED - if (ScriptDebugger::get_singleton()) { + if (EngineDebugger::is_active()) { GDScriptLanguage::get_singleton()->debug_break("Breakpoint Statement", true); } #endif @@ -1519,26 +1479,28 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a line = _code_ptr[ip + 1]; ip += 2; - if (ScriptDebugger::get_singleton()) { + if (EngineDebugger::is_active()) { // line bool do_break = false; - if (ScriptDebugger::get_singleton()->get_lines_left() > 0) { - - if (ScriptDebugger::get_singleton()->get_depth() <= 0) - ScriptDebugger::get_singleton()->set_lines_left(ScriptDebugger::get_singleton()->get_lines_left() - 1); - if (ScriptDebugger::get_singleton()->get_lines_left() <= 0) + if (EngineDebugger::get_script_debugger()->get_lines_left() > 0) { + if (EngineDebugger::get_script_debugger()->get_depth() <= 0) { + EngineDebugger::get_script_debugger()->set_lines_left(EngineDebugger::get_script_debugger()->get_lines_left() - 1); + } + if (EngineDebugger::get_script_debugger()->get_lines_left() <= 0) { do_break = true; + } } - if (ScriptDebugger::get_singleton()->is_breakpoint(line, source)) + if (EngineDebugger::get_script_debugger()->is_breakpoint(line, source)) { do_break = true; + } if (do_break) { GDScriptLanguage::get_singleton()->debug_break("Breakpoint", true); } - ScriptDebugger::get_singleton()->line_poll(); + EngineDebugger::get_singleton()->line_poll(); } } DISPATCH_OPCODE; @@ -1560,20 +1522,24 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a OPCODES_END #ifdef DEBUG_ENABLED - if (exit_ok) + if (exit_ok) { OPCODE_OUT; + } //error // function, file, line, error, explanation String err_file; - if (p_instance && ObjectDB::instance_validate(p_instance->owner) && p_instance->script->is_valid() && p_instance->script->path != "") + if (p_instance && ObjectDB::get_instance(p_instance->owner_id) != nullptr && p_instance->script->is_valid() && p_instance->script->path != "") { err_file = p_instance->script->path; - else if (script) + } else if (script) { err_file = script->path; - if (err_file == "") + } + if (err_file == "") { err_file = "<built-in>"; + } String err_func = name; - if (p_instance && ObjectDB::instance_validate(p_instance->owner) && p_instance->script->is_valid() && p_instance->script->name != "") + if (p_instance && ObjectDB::get_instance(p_instance->owner_id) != nullptr && p_instance->script->is_valid() && p_instance->script->name != "") { err_func = p_instance->script->name + "." + err_func; + } int err_line = line; if (err_text == "") { err_text = "Internal Script Error! - opcode #" + itos(last_opcode) + " (report please)."; @@ -1600,19 +1566,21 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a GDScriptLanguage::get_singleton()->script_frame_time += time_taken - function_call_time; } - // Check if this is the last time the function is resuming from yield - // Will be true if never yielded as well + // Check if this is the last time the function is resuming from await + // Will be true if never awaited as well // When it's the last resume it will postpone the exit from stack, // so the debugger knows which function triggered the resume of the next function (if any) - if (!p_state || yielded) { - if (ScriptDebugger::get_singleton()) + if (!p_state || awaited) { + if (EngineDebugger::is_active()) { GDScriptLanguage::get_singleton()->exit_function(); + } #endif if (_stack_size) { //free stack - for (int i = 0; i < _stack_size; i++) + for (int i = 0; i < _stack_size; i++) { stack[i].~Variant(); + } } #ifdef DEBUG_ENABLED @@ -1623,32 +1591,28 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a } const int *GDScriptFunction::get_code() const { - return _code_ptr; } -int GDScriptFunction::get_code_size() const { +int GDScriptFunction::get_code_size() const { return _code_size; } Variant GDScriptFunction::get_constant(int p_idx) const { - ERR_FAIL_INDEX_V(p_idx, constants.size(), "<errconst>"); return constants[p_idx]; } StringName GDScriptFunction::get_global_name(int p_idx) const { - ERR_FAIL_INDEX_V(p_idx, global_names.size(), "<errgname>"); return global_names[p_idx]; } int GDScriptFunction::get_default_argument_count() const { - return _default_arg_count; } -int GDScriptFunction::get_default_argument_addr(int p_idx) const { +int GDScriptFunction::get_default_argument_addr(int p_idx) const { ERR_FAIL_INDEX_V(p_idx, default_arguments.size(), -1); return default_arguments[p_idx]; } @@ -1663,45 +1627,38 @@ GDScriptDataType GDScriptFunction::get_argument_type(int p_idx) const { } StringName GDScriptFunction::get_name() const { - return name; } int GDScriptFunction::get_max_stack_size() const { - return _stack_size; } struct _GDFKC { - int order; List<int> pos; }; struct _GDFKCS { - int order; StringName id; int pos; bool operator<(const _GDFKCS &p_r) const { - return order < p_r.order; } }; -void GDScriptFunction::debug_get_stack_member_state(int p_line, List<Pair<StringName, int> > *r_stackvars) const { - +void GDScriptFunction::debug_get_stack_member_state(int p_line, List<Pair<StringName, int>> *r_stackvars) const { int oc = 0; Map<StringName, _GDFKC> sdmap; for (const List<StackDebug>::Element *E = stack_debug.front(); E; E = E->next()) { - const StackDebug &sd = E->get(); - if (sd.line > p_line) + if (sd.line > p_line) { break; + } if (sd.added) { - if (!sdmap.has(sd.identifier)) { _GDFKC d; d.order = oc++; @@ -1712,18 +1669,17 @@ void GDScriptFunction::debug_get_stack_member_state(int p_line, List<Pair<String sdmap[sd.identifier].pos.push_back(sd.pos); } } else { - ERR_CONTINUE(!sdmap.has(sd.identifier)); sdmap[sd.identifier].pos.pop_back(); - if (sdmap[sd.identifier].pos.empty()) + if (sdmap[sd.identifier].pos.empty()) { sdmap.erase(sd.identifier); + } } } List<_GDFKCS> stackpositions; for (Map<StringName, _GDFKC>::Element *E = sdmap.front(); E; E = E->next()) { - _GDFKCS spp; spp.id = E->key(); spp.order = E->get().order; @@ -1734,7 +1690,6 @@ void GDScriptFunction::debug_get_stack_member_state(int p_line, List<Pair<String stackpositions.sort(); for (List<_GDFKCS>::Element *E = stackpositions.front(); E; E = E->next()) { - Pair<StringName, int> p; p.first = E->get().id; p.second = E->get().pos; @@ -1744,21 +1699,17 @@ void GDScriptFunction::debug_get_stack_member_state(int p_line, List<Pair<String GDScriptFunction::GDScriptFunction() : function_list(this) { - _stack_size = 0; _call_size = 0; rpc_mode = MultiplayerAPI::RPC_MODE_DISABLED; name = "<anonymous>"; #ifdef DEBUG_ENABLED - _func_cname = NULL; + _func_cname = nullptr; - if (GDScriptLanguage::get_singleton()->lock) { - GDScriptLanguage::get_singleton()->lock->lock(); - } - GDScriptLanguage::get_singleton()->function_list.add(&function_list); + { + MutexLock lock(GDScriptLanguage::get_singleton()->lock); - if (GDScriptLanguage::get_singleton()->lock) { - GDScriptLanguage::get_singleton()->lock->unlock(); + GDScriptLanguage::get_singleton()->function_list.add(&function_list); } profile.call_count = 0; @@ -1776,26 +1727,21 @@ GDScriptFunction::GDScriptFunction() : GDScriptFunction::~GDScriptFunction() { #ifdef DEBUG_ENABLED - if (GDScriptLanguage::get_singleton()->lock) { - GDScriptLanguage::get_singleton()->lock->lock(); - } - GDScriptLanguage::get_singleton()->function_list.remove(&function_list); - if (GDScriptLanguage::get_singleton()->lock) { - GDScriptLanguage::get_singleton()->lock->unlock(); - } + MutexLock lock(GDScriptLanguage::get_singleton()->lock); + + GDScriptLanguage::get_singleton()->function_list.remove(&function_list); #endif } ///////////////////// -Variant GDScriptFunctionState::_signal_callback(const Variant **p_args, int p_argcount, Variant::CallError &r_error) { - +Variant GDScriptFunctionState::_signal_callback(const Variant **p_args, int p_argcount, Callable::CallError &r_error) { Variant arg; - r_error.error = Variant::CallError::CALL_OK; + r_error.error = Callable::CallError::CALL_OK; if (p_argcount == 0) { - r_error.error = Variant::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; + r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; r_error.argument = 1; return Variant(); } else if (p_argcount == 1) { @@ -1813,7 +1759,7 @@ Variant GDScriptFunctionState::_signal_callback(const Variant **p_args, int p_ar Ref<GDScriptFunctionState> self = *p_args[p_argcount - 1]; if (self.is_null()) { - r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; r_error.argument = p_argcount - 1; r_error.expected = Variant::OBJECT; return Variant(); @@ -1823,38 +1769,58 @@ Variant GDScriptFunctionState::_signal_callback(const Variant **p_args, int p_ar } bool GDScriptFunctionState::is_valid(bool p_extended_check) const { - - if (function == NULL) + if (function == nullptr) { return false; + } if (p_extended_check) { - //class instance gone? - if (state.instance_id && !ObjectDB::get_instance(state.instance_id)) + MutexLock lock(GDScriptLanguage::get_singleton()->lock); + + // Script gone? + if (!scripts_list.in_list()) { + return false; + } + // Class instance gone? (if not static function) + if (state.instance && !instances_list.in_list()) { return false; + } } return true; } Variant GDScriptFunctionState::resume(const Variant &p_arg) { - ERR_FAIL_COND_V(!function, Variant()); - if (state.instance_id && !ObjectDB::get_instance(state.instance_id)) { + { + MutexLock lock(GDScriptLanguage::singleton->lock); + + if (!scripts_list.in_list()) { #ifdef DEBUG_ENABLED - ERR_FAIL_V_MSG(Variant(), "Resumed function '" + String(function->get_name()) + "()' after yield, but class instance is gone. At script: " + state.script->get_path() + ":" + itos(state.line)); + ERR_FAIL_V_MSG(Variant(), "Resumed function '" + state.function_name + "()' after await, but script is gone. At script: " + state.script_path + ":" + itos(state.line)); #else - return Variant(); + return Variant(); +#endif + } + if (state.instance && !instances_list.in_list()) { +#ifdef DEBUG_ENABLED + ERR_FAIL_V_MSG(Variant(), "Resumed function '" + state.function_name + "()' after await, but class instance is gone. At script: " + state.script_path + ":" + itos(state.line)); +#else + return Variant(); #endif + } + // Do these now to avoid locking again after the call + scripts_list.remove_from_list(); + instances_list.remove_from_list(); } state.result = p_arg; - Variant::CallError err; - Variant ret = function->call(NULL, NULL, 0, err, &state); + Callable::CallError err; + Variant ret = function->call(nullptr, nullptr, 0, err, &state); bool completed = true; // If the return value is a GDScriptFunctionState reference, - // then the function did yield again after resuming. + // then the function did awaited again after resuming. if (ret.is_ref()) { GDScriptFunctionState *gdfs = Object::cast_to<GDScriptFunctionState>(ret); if (gdfs && gdfs->function == function) { @@ -1863,7 +1829,7 @@ Variant GDScriptFunctionState::resume(const Variant &p_arg) { } } - function = NULL; //cleaned up; + function = nullptr; //cleaned up; state.result = Variant(); if (completed) { @@ -1874,13 +1840,8 @@ Variant GDScriptFunctionState::resume(const Variant &p_arg) { } #ifdef DEBUG_ENABLED - if (ScriptDebugger::get_singleton()) + if (EngineDebugger::is_active()) { GDScriptLanguage::get_singleton()->exit_function(); - if (state.stack_size) { - //free stack - Variant *stack = (Variant *)state.stack.ptr(); - for (int i = 0; i < state.stack_size; i++) - stack[i].~Variant(); } #endif } @@ -1888,8 +1849,17 @@ Variant GDScriptFunctionState::resume(const Variant &p_arg) { return ret; } -void GDScriptFunctionState::_bind_methods() { +void GDScriptFunctionState::_clear_stack() { + if (state.stack_size) { + Variant *stack = (Variant *)state.stack.ptr(); + for (int i = 0; i < state.stack_size; i++) { + stack[i].~Variant(); + } + state.stack_size = 0; + } +} +void GDScriptFunctionState::_bind_methods() { ClassDB::bind_method(D_METHOD("resume", "arg"), &GDScriptFunctionState::resume, DEFVAL(Variant())); ClassDB::bind_method(D_METHOD("is_valid", "extended_check"), &GDScriptFunctionState::is_valid, DEFVAL(false)); ClassDB::bind_vararg_method(METHOD_FLAGS_DEFAULT, "_signal_callback", &GDScriptFunctionState::_signal_callback, MethodInfo("_signal_callback")); @@ -1897,18 +1867,521 @@ void GDScriptFunctionState::_bind_methods() { ADD_SIGNAL(MethodInfo("completed", PropertyInfo(Variant::NIL, "result", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NIL_IS_VARIANT))); } -GDScriptFunctionState::GDScriptFunctionState() { - - function = NULL; +GDScriptFunctionState::GDScriptFunctionState() : + scripts_list(this), + instances_list(this) { + function = nullptr; } GDScriptFunctionState::~GDScriptFunctionState() { + _clear_stack(); - if (function != NULL) { - //never called, deinitialize stack - for (int i = 0; i < state.stack_size; i++) { - Variant *v = (Variant *)&state.stack[sizeof(Variant) * i]; - v->~Variant(); + { + MutexLock lock(GDScriptLanguage::singleton->lock); + scripts_list.remove_from_list(); + instances_list.remove_from_list(); + } +} + +#ifdef DEBUG_ENABLED +static String _get_variant_string(const Variant &p_variant) { + String txt; + if (p_variant.get_type() == Variant::STRING) { + txt = "\"" + String(p_variant) + "\""; + } else if (p_variant.get_type() == Variant::STRING_NAME) { + txt = "&\"" + String(p_variant) + "\""; + } else if (p_variant.get_type() == Variant::NODE_PATH) { + txt = "^\"" + String(p_variant) + "\""; + } else if (p_variant.get_type() == Variant::OBJECT) { + Object *obj = p_variant; + if (!obj) { + txt = "null"; + } else { + GDScriptNativeClass *cls = Object::cast_to<GDScriptNativeClass>(obj); + if (cls) { + txt += cls->get_name(); + txt += " (class)"; + } else { + txt = obj->get_class(); + if (obj->get_script_instance()) { + txt += "(" + obj->get_script_instance()->get_script()->get_path() + ")"; + } + } } + } else { + txt = p_variant; + } + return txt; +} + +static String _disassemble_address(const GDScript *p_script, const GDScriptFunction &p_function, int p_address) { + int addr = p_address & GDScriptFunction::ADDR_MASK; + + switch (p_address >> GDScriptFunction::ADDR_BITS) { + case GDScriptFunction::ADDR_TYPE_SELF: { + return "self"; + } break; + case GDScriptFunction::ADDR_TYPE_CLASS: { + return "class"; + } break; + case GDScriptFunction::ADDR_TYPE_MEMBER: { + return "member(" + p_script->debug_get_member_by_index(addr) + ")"; + } break; + case GDScriptFunction::ADDR_TYPE_CLASS_CONSTANT: { + return "class_const(" + p_function.get_global_name(addr) + ")"; + } break; + case GDScriptFunction::ADDR_TYPE_LOCAL_CONSTANT: { + return "const(" + _get_variant_string(p_function.get_constant(addr)) + ")"; + } break; + case GDScriptFunction::ADDR_TYPE_STACK: { + return "stack(" + itos(addr) + ")"; + } break; + case GDScriptFunction::ADDR_TYPE_STACK_VARIABLE: { + return "var_stack(" + itos(addr) + ")"; + } break; + case GDScriptFunction::ADDR_TYPE_GLOBAL: { + return "global(" + _get_variant_string(GDScriptLanguage::get_singleton()->get_global_array()[addr]) + ")"; + } break; + case GDScriptFunction::ADDR_TYPE_NAMED_GLOBAL: { + return "named_global(" + p_function.get_global_name(addr) + ")"; + } break; + case GDScriptFunction::ADDR_TYPE_NIL: { + return "nil"; + } break; } + + return "<err>"; } + +void GDScriptFunction::disassemble(const Vector<String> &p_code_lines) const { +#define DADDR(m_ip) (_disassemble_address(_script, *this, _code_ptr[ip + m_ip])) + + for (int ip = 0; ip < _code_size;) { + StringBuilder text; + int incr = 0; + + text += " "; + text += itos(ip); + text += ": "; + + // This makes the compiler complain if some opcode is unchecked in the switch. + Opcode code = Opcode(_code_ptr[ip]); + + switch (code) { + case OPCODE_OPERATOR: { + int operation = _code_ptr[ip + 1]; + + text += "operator "; + + text += DADDR(4); + text += " = "; + text += DADDR(2); + text += " "; + text += Variant::get_operator_name(Variant::Operator(operation)); + text += " "; + text += DADDR(3); + + incr += 5; + } break; + case OPCODE_EXTENDS_TEST: { + text += "is object "; + text += DADDR(3); + text += " = "; + text += DADDR(1); + text += " is "; + text += DADDR(2); + + incr += 4; + } break; + case OPCODE_IS_BUILTIN: { + text += "is builtin "; + text += DADDR(3); + text += " = "; + text += DADDR(1); + text += " is "; + text += Variant::get_type_name(Variant::Type(_code_ptr[ip + 2])); + + incr += 4; + } break; + case OPCODE_SET: { + text += "set "; + text += DADDR(1); + text += "["; + text += DADDR(2); + text += "] = "; + text += DADDR(3); + + incr += 4; + } break; + case OPCODE_GET: { + text += "get "; + text += DADDR(3); + text += " = "; + text += DADDR(1); + text += "["; + text += DADDR(2); + text += "]"; + + incr += 4; + } break; + case OPCODE_SET_NAMED: { + text += "set_named "; + text += DADDR(1); + text += "[\""; + text += _global_names_ptr[_code_ptr[ip + 2]]; + text += "\"] = "; + text += DADDR(3); + + incr += 4; + } break; + case OPCODE_GET_NAMED: { + text += "get_named "; + text += DADDR(3); + text += " = "; + text += DADDR(1); + text += "[\""; + text += _global_names_ptr[_code_ptr[ip + 2]]; + text += "\"]"; + + incr += 4; + } break; + case OPCODE_SET_MEMBER: { + text += "set_member "; + text += "[\""; + text += _global_names_ptr[_code_ptr[ip + 1]]; + text += "\"] = "; + text += DADDR(2); + + incr += 3; + } break; + case OPCODE_GET_MEMBER: { + text += "get_member "; + text += DADDR(2); + text += " = "; + text += "[\""; + text += _global_names_ptr[_code_ptr[ip + 1]]; + text += "\"]"; + + incr += 3; + } break; + case OPCODE_ASSIGN: { + text += "assign "; + text += DADDR(1); + text += " = "; + text += DADDR(2); + + incr += 3; + } break; + case OPCODE_ASSIGN_TRUE: { + text += "assign "; + text += DADDR(1); + text += " = true"; + + incr += 2; + } break; + case OPCODE_ASSIGN_FALSE: { + text += "assign "; + text += DADDR(1); + text += " = false"; + + incr += 2; + } break; + case OPCODE_ASSIGN_TYPED_BUILTIN: { + text += "assign typed builtin ("; + text += Variant::get_type_name((Variant::Type)_code_ptr[ip + 1]); + text += ") "; + text += DADDR(2); + text += " = "; + text += DADDR(3); + + incr += 4; + } break; + case OPCODE_ASSIGN_TYPED_NATIVE: { + Variant class_name = _constants_ptr[_code_ptr[ip + 1]]; + GDScriptNativeClass *nc = Object::cast_to<GDScriptNativeClass>(class_name.operator Object *()); + + text += "assign typed native ("; + text += nc->get_name().operator String(); + text += ") "; + text += DADDR(2); + text += " = "; + text += DADDR(3); + + incr += 4; + } break; + case OPCODE_ASSIGN_TYPED_SCRIPT: { + Variant script = _constants_ptr[_code_ptr[ip + 1]]; + Script *sc = Object::cast_to<Script>(script.operator Object *()); + + text += "assign typed script ("; + text += sc->get_path(); + text += ") "; + text += DADDR(2); + text += " = "; + text += DADDR(3); + + incr += 4; + } break; + case OPCODE_CAST_TO_BUILTIN: { + text += "cast builtin "; + text += DADDR(3); + text += " = "; + text += DADDR(2); + text += " as "; + text += Variant::get_type_name(Variant::Type(_code_ptr[ip + 1])); + + incr += 4; + } break; + case OPCODE_CAST_TO_NATIVE: { + Variant class_name = _constants_ptr[_code_ptr[ip + 1]]; + GDScriptNativeClass *nc = Object::cast_to<GDScriptNativeClass>(class_name.operator Object *()); + + text += "cast native "; + text += DADDR(3); + text += " = "; + text += DADDR(2); + text += " as "; + text += nc->get_name(); + + incr += 4; + } break; + case OPCODE_CAST_TO_SCRIPT: { + text += "cast "; + text += DADDR(3); + text += " = "; + text += DADDR(2); + text += " as "; + text += DADDR(1); + + incr += 4; + } break; + case OPCODE_CONSTRUCT: { + Variant::Type t = Variant::Type(_code_ptr[ip + 1]); + int argc = _code_ptr[ip + 2]; + + text += "construct "; + text += DADDR(3 + argc); + text += " = "; + + text += Variant::get_type_name(t) + "("; + for (int i = 0; i < argc; i++) { + if (i > 0) + text += ", "; + text += DADDR(i + 3); + } + text += ")"; + + incr = 4 + argc; + } break; + case OPCODE_CONSTRUCT_ARRAY: { + int argc = _code_ptr[ip + 1]; + text += " make_array "; + text += DADDR(2 + argc); + text += " = ["; + + for (int i = 0; i < argc; i++) { + if (i > 0) + text += ", "; + text += DADDR(2 + i); + } + + text += "]"; + + incr += 3 + argc; + } break; + case OPCODE_CONSTRUCT_DICTIONARY: { + int argc = _code_ptr[ip + 1]; + text += "make_dict "; + text += DADDR(2 + argc * 2); + text += " = {"; + + for (int i = 0; i < argc; i++) { + if (i > 0) + text += ", "; + text += DADDR(2 + i * 2 + 0); + text += ": "; + text += DADDR(2 + i * 2 + 1); + } + + text += "}"; + + incr += 3 + argc * 2; + } break; + case OPCODE_CALL: + case OPCODE_CALL_RETURN: + case OPCODE_CALL_ASYNC: { + bool ret = _code_ptr[ip] == OPCODE_CALL_RETURN; + bool async = _code_ptr[ip] == OPCODE_CALL_ASYNC; + + if (ret) { + text += "call-ret "; + } else if (async) { + text += "call-async "; + } else { + text += "call "; + } + + int argc = _code_ptr[ip + 1]; + if (ret || async) { + text += DADDR(4 + argc) + " = "; + } + + text += DADDR(2) + "."; + text += String(_global_names_ptr[_code_ptr[ip + 3]]); + text += "("; + + for (int i = 0; i < argc; i++) { + if (i > 0) + text += ", "; + text += DADDR(4 + i); + } + text += ")"; + + incr = 5 + argc; + } break; + case OPCODE_CALL_BUILT_IN: { + text += "call-built-in "; + + int argc = _code_ptr[ip + 2]; + text += DADDR(3 + argc) + " = "; + + text += GDScriptFunctions::get_func_name(GDScriptFunctions::Function(_code_ptr[ip + 1])); + text += "("; + + for (int i = 0; i < argc; i++) { + if (i > 0) + text += ", "; + text += DADDR(3 + i); + } + text += ")"; + + incr = 4 + argc; + } break; + case OPCODE_CALL_SELF_BASE: { + text += "call-self-base "; + + int argc = _code_ptr[ip + 2]; + text += DADDR(3 + argc) + " = "; + + text += _global_names_ptr[_code_ptr[ip + 1]]; + text += "("; + + for (int i = 0; i < argc; i++) { + if (i > 0) + text += ", "; + text += DADDR(3 + i); + } + text += ")"; + + incr = 4 + argc; + } break; + case OPCODE_AWAIT: { + text += "await "; + text += DADDR(1); + + incr += 2; + } break; + case OPCODE_AWAIT_RESUME: { + text += "await resume "; + text += DADDR(1); + + incr = 2; + } break; + case OPCODE_JUMP: { + text += "jump "; + text += itos(_code_ptr[ip + 1]); + + incr = 2; + } break; + case OPCODE_JUMP_IF: { + text += "jump-if "; + text += DADDR(1); + text += " to "; + text += itos(_code_ptr[ip + 2]); + + incr = 3; + } break; + case OPCODE_JUMP_IF_NOT: { + text += "jump-if-not "; + text += DADDR(1); + text += " to "; + text += itos(_code_ptr[ip + 2]); + + incr = 3; + } break; + case OPCODE_JUMP_TO_DEF_ARGUMENT: { + text += "jump-to-default-argument "; + + incr = 1; + } break; + case OPCODE_RETURN: { + text += "return "; + text += DADDR(1); + + incr = 2; + } break; + case OPCODE_ITERATE_BEGIN: { + text += "for-init "; + text += DADDR(4); + text += " in "; + text += DADDR(2); + text += " counter "; + text += DADDR(1); + text += " end "; + text += itos(_code_ptr[ip + 3]); + + incr += 5; + } break; + case OPCODE_ITERATE: { + text += "for-loop "; + text += DADDR(4); + text += " in "; + text += DADDR(2); + text += " counter "; + text += DADDR(1); + text += " end "; + text += itos(_code_ptr[ip + 3]); + + incr += 5; + } break; + case OPCODE_LINE: { + int line = _code_ptr[ip + 1] - 1; + if (line >= 0 && line < p_code_lines.size()) { + text += "line "; + text += itos(line + 1); + text += ": "; + text += p_code_lines[line]; + } else { + text += ""; + } + + incr += 2; + } break; + case OPCODE_ASSERT: { + text += "assert ("; + text += DADDR(1); + text += ", "; + text += DADDR(2); + text += ")"; + + incr += 3; + } break; + case OPCODE_BREAKPOINT: { + text += "breakpoint"; + + incr += 1; + } break; + case OPCODE_END: { + text += "== END =="; + + incr += 1; + } break; + } + + ip += incr; + if (text.get_string_length() > 0) { + print_line(text.as_string()); + } + } +} +#endif |