diff options
Diffstat (limited to 'modules/gdscript')
| -rw-r--r-- | modules/gdscript/gdscript.cpp | 49 | ||||
| -rw-r--r-- | modules/gdscript/gdscript.h | 8 | ||||
| -rw-r--r-- | modules/gdscript/gdscript_function.cpp | 83 | ||||
| -rw-r--r-- | modules/gdscript/gdscript_function.h | 11 | ||||
| -rw-r--r-- | modules/gdscript/gdscript_functions.cpp | 5 | ||||
| -rw-r--r-- | modules/gdscript/gdscript_parser.cpp | 58 | ||||
| -rw-r--r-- | modules/gdscript/gdscript_parser.h | 3 | ||||
| -rw-r--r-- | modules/gdscript/gdscript_tokenizer.cpp | 24 |
8 files changed, 173 insertions, 68 deletions
diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp index 06ab9e226d..5f659f70a0 100644 --- a/modules/gdscript/gdscript.cpp +++ b/modules/gdscript/gdscript.cpp @@ -376,10 +376,15 @@ void GDScript::_update_exports_values(Map<StringName, Variant> &values, List<Pro } #endif -bool GDScript::_update_exports() { +bool GDScript::_update_exports(bool *r_err, bool p_recursive_call) { #ifdef TOOLS_ENABLED + static Vector<GDScript *> base_caches; + if (!p_recursive_call) + base_caches.clear(); + base_caches.append(this); + bool changed = false; if (source_changed_cache) { @@ -473,7 +478,22 @@ bool GDScript::_update_exports() { placeholder_fallback_enabled = false; if (base_cache.is_valid() && base_cache->is_valid()) { - if (base_cache->_update_exports()) { + for (int i = 0; i < base_caches.size(); i++) { + if (base_caches[i] == base_cache.ptr()) { + if (r_err) + *r_err = true; + valid = false; // to show error in the editor + base_cache->valid = false; + base_cache->inheriters_cache.clear(); // to prevent future stackoverflows + base_cache.unref(); + base.unref(); + _base = nullptr; + ERR_FAIL_V_MSG(false, "Cyclic inheritance in script class."); + } + } + if (base_cache->_update_exports(r_err, true)) { + if (r_err && *r_err) + return false; changed = true; } } @@ -501,7 +521,10 @@ void GDScript::update_exports() { #ifdef TOOLS_ENABLED - _update_exports(); + bool cyclic_error = false; + _update_exports(&cyclic_error); + if (cyclic_error) + return; Set<ObjectID> copy = inheriters_cache; //might get modified @@ -1052,6 +1075,16 @@ void GDScript::_init_rpc_methods_properties() { } GDScript::~GDScript() { + + { + MutexLock lock(GDScriptLanguage::get_singleton()->lock); + + while (SelfList<GDScriptFunctionState> *E = pending_func_states.first()) { + E->self()->_clear_stack(); + pending_func_states.remove(E); + } + } + for (Map<StringName, GDScriptFunction *>::Element *E = member_functions.front(); E; E = E->next()) { memdelete(E->get()); } @@ -1470,9 +1503,15 @@ GDScriptInstance::GDScriptInstance() { } GDScriptInstance::~GDScriptInstance() { - if (script.is_valid() && owner) { - MutexLock lock(GDScriptLanguage::singleton->lock); + MutexLock lock(GDScriptLanguage::get_singleton()->lock); + + while (SelfList<GDScriptFunctionState> *E = pending_func_states.first()) { + E->self()->_clear_stack(); + pending_func_states.remove(E); + } + + if (script.is_valid() && owner) { script->instances.erase(owner); } } diff --git a/modules/gdscript/gdscript.h b/modules/gdscript/gdscript.h index 5fdc25669f..b7ac2bd0c5 100644 --- a/modules/gdscript/gdscript.h +++ b/modules/gdscript/gdscript.h @@ -117,6 +117,8 @@ class GDScript : public Script { String fully_qualified_name; SelfList<GDScript> script_list; + SelfList<GDScriptFunctionState>::List pending_func_states; + GDScriptInstance *_create_instance(const Variant **p_args, int p_argcount, Object *p_owner, bool p_isref, Callable::CallError &r_error); void _set_subclass_path(Ref<GDScript> &p_sc, const String &p_path); @@ -133,7 +135,7 @@ class GDScript : public Script { #endif - bool _update_exports(); + bool _update_exports(bool *r_err = nullptr, bool p_recursive_call = false); void _save_orphaned_subclasses(); void _init_rpc_methods_properties(); @@ -254,6 +256,8 @@ class GDScriptInstance : public ScriptInstance { Vector<Variant> members; bool base_ref; + SelfList<GDScriptFunctionState>::List pending_func_states; + void _ml_call_reversed(GDScript *sptr, const StringName &p_method, const Variant **p_args, int p_argcount); public: @@ -347,6 +351,8 @@ struct GDScriptWarning { class GDScriptLanguage : public ScriptLanguage { + friend class GDScriptFunctionState; + static GDScriptLanguage *singleton; Variant *_global_array; diff --git a/modules/gdscript/gdscript_function.cpp b/modules/gdscript/gdscript_function.cpp index 44640411bb..df0fac956c 100644 --- a/modules/gdscript/gdscript_function.cpp +++ b/modules/gdscript/gdscript_function.cpp @@ -294,8 +294,8 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a line = p_state->line; ip = p_state->ip; alloca_size = p_state->stack.size(); - script = static_cast<GDScript *>(ObjectDB::get_instance(p_state->script_id)); - p_instance = p_state->instance_id.is_valid() ? static_cast<GDScriptInstance *>(ObjectDB::get_instance(p_state->instance_id)->get_script_instance()) : nullptr; + script = p_state->script; + p_instance = p_state->instance; defarg = p_state->defarg; self = p_state->self; @@ -1281,11 +1281,21 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a gdfs->state.alloca_size = alloca_size; gdfs->state.ip = ip + ipofs; gdfs->state.line = line; - gdfs->state.script_id = script->get_instance_id(); + 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 = NULL; + } + } #ifdef DEBUG_ENABLED + gdfs->state.function_name = name; gdfs->state.script_path = _script->get_path(); #endif - gdfs->state.instance_id = (p_instance && p_instance->get_owner()) ? p_instance->get_owner()->get_instance_id() : ObjectID(); gdfs->state.defarg = defarg; gdfs->function = this; @@ -1833,12 +1843,14 @@ bool GDScriptFunctionState::is_valid(bool p_extended_check) const { return false; if (p_extended_check) { - // Class instance gone? (Otherwise script is valid for sure, because the instance has a ref to the script) - if (state.instance_id.is_valid() && !ObjectDB::get_instance(state.instance_id)) { + MutexLock lock(GDScriptLanguage::get_singleton()->lock); + + // Script gone? + if (!scripts_list.in_list()) { return false; } - // Script gone? (Static method, so there's no instance whose ref to the script can ensure it's valid) - if (!ObjectDB::get_instance(state.script_id)) { + // Class instance gone? (if not static function) + if (state.instance && !instances_list.in_list()) { return false; } } @@ -1849,19 +1861,26 @@ bool GDScriptFunctionState::is_valid(bool p_extended_check) const { Variant GDScriptFunctionState::resume(const Variant &p_arg) { ERR_FAIL_COND_V(!function, Variant()); - if (state.instance_id.is_valid() && !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_path + ":" + itos(state.line)); + ERR_FAIL_V_MSG(Variant(), "Resumed function '" + state.function_name + "()' after yield, but script is gone. At script: " + state.script_path + ":" + itos(state.line)); #else - return Variant(); + return Variant(); #endif - } - if (!ObjectDB::get_instance(state.script_id)) { + } + if (state.instance && !instances_list.in_list()) { #ifdef DEBUG_ENABLED - ERR_FAIL_V_MSG(Variant(), "Resumed function '" + String(function->get_name()) + "()' after yield, but script is gone. At script: " + state.script_path + ":" + itos(state.line)); + ERR_FAIL_V_MSG(Variant(), "Resumed function '" + state.function_name + "()' after yield, but class instance is gone. At script: " + state.script_path + ":" + itos(state.line)); #else - return Variant(); + 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; @@ -1884,6 +1903,8 @@ Variant GDScriptFunctionState::resume(const Variant &p_arg) { state.result = Variant(); if (completed) { + _clear_stack(); + if (first_state.is_valid()) { first_state->emit_signal("completed", ret); } else { @@ -1893,18 +1914,22 @@ Variant GDScriptFunctionState::resume(const Variant &p_arg) { #ifdef DEBUG_ENABLED 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 } return ret; } +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())); @@ -1914,18 +1939,20 @@ void GDScriptFunctionState::_bind_methods() { ADD_SIGNAL(MethodInfo("completed", PropertyInfo(Variant::NIL, "result", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NIL_IS_VARIANT))); } -GDScriptFunctionState::GDScriptFunctionState() { +GDScriptFunctionState::GDScriptFunctionState() : + scripts_list(this), + instances_list(this) { function = nullptr; } GDScriptFunctionState::~GDScriptFunctionState() { - if (function != nullptr) { - //never called, deinitialize stack - for (int i = 0; i < state.stack_size; i++) { - Variant *v = (Variant *)&state.stack[sizeof(Variant) * i]; - v->~Variant(); - } + _clear_stack(); + + { + MutexLock lock(GDScriptLanguage::singleton->lock); + scripts_list.remove_from_list(); + instances_list.remove_from_list(); } } diff --git a/modules/gdscript/gdscript_function.h b/modules/gdscript/gdscript_function.h index 9d8e23d994..d38b6d0739 100644 --- a/modules/gdscript/gdscript_function.h +++ b/modules/gdscript/gdscript_function.h @@ -293,11 +293,12 @@ private: public: struct CallState { - ObjectID script_id; + GDScript *script; + GDScriptInstance *instance; #ifdef DEBUG_ENABLED + StringName function_name; String script_path; #endif - ObjectID instance_id; Vector<uint8_t> stack; int stack_size; Variant self; @@ -357,12 +358,18 @@ class GDScriptFunctionState : public Reference { Variant _signal_callback(const Variant **p_args, int p_argcount, Callable::CallError &r_error); Ref<GDScriptFunctionState> first_state; + SelfList<GDScriptFunctionState> scripts_list; + SelfList<GDScriptFunctionState> instances_list; + protected: static void _bind_methods(); public: bool is_valid(bool p_extended_check = false) const; Variant resume(const Variant &p_arg = Variant()); + + void _clear_stack(); + GDScriptFunctionState(); ~GDScriptFunctionState(); }; diff --git a/modules/gdscript/gdscript_functions.cpp b/modules/gdscript/gdscript_functions.cpp index 0199af642f..85a5d86ca0 100644 --- a/modules/gdscript/gdscript_functions.cpp +++ b/modules/gdscript/gdscript_functions.cpp @@ -1199,6 +1199,11 @@ void GDScriptFunctions::call(Function p_func, const Variant **p_args, int p_arg_ } r_ret = gdscr->_new(nullptr, -1 /*skip initializer*/, r_error); + if (r_error.error != Callable::CallError::CALL_OK) { + r_ret = Variant(); + return; + } + GDScriptInstance *ins = static_cast<GDScriptInstance *>(static_cast<Object *>(r_ret)->get_script_instance()); Ref<GDScript> gd_ref = ins->get_script(); diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp index 17077567c7..3280b1f8e3 100644 --- a/modules/gdscript/gdscript_parser.cpp +++ b/modules/gdscript/gdscript_parser.cpp @@ -72,6 +72,16 @@ bool GDScriptParser::_end_statement() { return false; } +void GDScriptParser::_set_end_statement_error(String p_name) { + String error_msg; + if (tokenizer->get_token() == GDScriptTokenizer::TK_IDENTIFIER) { + error_msg = vformat("Expected end of statement (\"%s\"), got %s (\"%s\") instead.", p_name, tokenizer->get_token_name(tokenizer->get_token()), tokenizer->get_token_identifier()); + } else { + error_msg = vformat("Expected end of statement (\"%s\"), got %s instead.", p_name, tokenizer->get_token_name(tokenizer->get_token())); + } + _set_error(error_msg); +} + bool GDScriptParser::_enter_indent_block(BlockNode *p_block) { if (tokenizer->get_token() != GDScriptTokenizer::TK_COLON) { @@ -2734,6 +2744,7 @@ void GDScriptParser::_transform_match_statment(MatchNode *p_match_statement) { IdentifierNode *id2 = alloc_node<IdentifierNode>(); id2->name = local_var->name; + id2->datatype = local_var->datatype; id2->declared_block = branch->body; id2->set_datatype(local_var->assign->get_datatype()); @@ -2937,7 +2948,7 @@ void GDScriptParser::_parse_block(BlockNode *p_block, bool p_static) { lv->assign = assigned; if (!_end_statement()) { - _set_error("Expected end of statement (\"var\")."); + _set_end_statement_error("var"); return; } @@ -3171,9 +3182,9 @@ void GDScriptParser::_parse_block(BlockNode *p_block, bool p_static) { ConstantNode *cn = alloc_node<ConstantNode>(); switch (args.size()) { - case 1: cn->value = (int)constants[0]; break; - case 2: cn->value = Vector2(constants[0], constants[1]); break; - case 3: cn->value = Vector3(constants[0], constants[1], constants[2]); break; + case 1: cn->value = (int64_t)constants[0]; break; + case 2: cn->value = Vector2i(constants[0], constants[1]); break; + case 3: cn->value = Vector3i(constants[0], constants[1], constants[2]); break; } cn->datatype = _type_from_variant(cn->value); container = cn; @@ -3186,8 +3197,8 @@ void GDScriptParser::_parse_block(BlockNode *p_block, bool p_static) { switch (args.size()) { case 1: tn->vtype = Variant::INT; break; - case 2: tn->vtype = Variant::VECTOR2; break; - case 3: tn->vtype = Variant::VECTOR3; break; + case 2: tn->vtype = Variant::VECTOR2I; break; + case 3: tn->vtype = Variant::VECTOR3I; break; } for (int i = 0; i < args.size(); i++) { @@ -3248,7 +3259,7 @@ void GDScriptParser::_parse_block(BlockNode *p_block, bool p_static) { cf_continue->cf_type = ControlFlowNode::CF_CONTINUE; p_block->statements.push_back(cf_continue); if (!_end_statement()) { - _set_error("Expected end of statement (\"continue\")."); + _set_end_statement_error("continue"); return; } } break; @@ -3260,7 +3271,7 @@ void GDScriptParser::_parse_block(BlockNode *p_block, bool p_static) { cf_break->cf_type = ControlFlowNode::CF_BREAK; p_block->statements.push_back(cf_break); if (!_end_statement()) { - _set_error("Expected end of statement (\"break\")."); + _set_end_statement_error("break"); return; } } break; @@ -3289,7 +3300,7 @@ void GDScriptParser::_parse_block(BlockNode *p_block, bool p_static) { cf_return->arguments.push_back(retexpr); p_block->statements.push_back(cf_return); if (!_end_statement()) { - _set_error("Expected end of statement after return expression."); + _set_end_statement_error("return"); return; } } @@ -3378,7 +3389,7 @@ void GDScriptParser::_parse_block(BlockNode *p_block, bool p_static) { p_block->statements.push_back(an); if (!_end_statement()) { - _set_error("Expected end of statement after \"assert\".", assert_line); + _set_end_statement_error("assert"); return; } } break; @@ -3389,7 +3400,7 @@ void GDScriptParser::_parse_block(BlockNode *p_block, bool p_static) { p_block->statements.push_back(bn); if (!_end_statement()) { - _set_error("Expected end of statement after \"breakpoint\"."); + _set_end_statement_error("breakpoint"); return; } } break; @@ -3408,7 +3419,7 @@ void GDScriptParser::_parse_block(BlockNode *p_block, bool p_static) { if (tokenizer->get_token() == GDScriptTokenizer::TK_COLON && tokenizer->get_token(1) == GDScriptTokenizer::TK_OP_ASSIGN) { _set_error("Unexpected ':=', use '=' instead. Expected end of statement after expression."); } else { - _set_error(String() + "Expected end of statement after expression, got " + tokenizer->get_token_name(tokenizer->get_token()) + " instead"); + _set_error(vformat("Expected end of statement after expression, got %s instead.", tokenizer->get_token_name(tokenizer->get_token()))); } return; } @@ -3598,7 +3609,7 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { if (error_set) return; if (!_end_statement()) { - _set_error("Expected end of statement after \"extends\"."); + _set_end_statement_error("extends"); return; } @@ -4103,7 +4114,7 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { p_class->_signals.push_back(sig); if (!_end_statement()) { - _set_error("Expected end of statement (\"signal\")."); + _set_end_statement_error("signal"); return; } } break; @@ -4960,6 +4971,7 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { IdentifierNode *id = alloc_node<IdentifierNode>(); id->name = member.identifier; + id->datatype = member.data_type; OperatorNode *op = alloc_node<OperatorNode>(); op->op = OperatorNode::OP_INIT_ASSIGN; @@ -5002,6 +5014,7 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { IdentifierNode *id = alloc_node<IdentifierNode>(); id->name = member.identifier; + id->datatype = member.data_type; OperatorNode *op = alloc_node<OperatorNode>(); op->op = OperatorNode::OP_INIT_ASSIGN; @@ -5044,7 +5057,7 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { p_class->variables.push_back(member); if (!_end_statement()) { - _set_error("Expected end of statement (\"continue\")."); + _set_end_statement_error("var"); return; } } break; @@ -5124,7 +5137,7 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { p_class->constant_expressions.insert(const_id, constant); if (!_end_statement()) { - _set_error("Expected end of statement (constant).", line); + _set_end_statement_error("const"); return; } @@ -5278,7 +5291,7 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { } if (!_end_statement()) { - _set_error("Expected end of statement (\"enum\")."); + _set_end_statement_error("enum"); return; } @@ -6647,6 +6660,7 @@ GDScriptParser::DataType GDScriptParser::_reduce_node_type(Node *p_node) { IdentifierNode *id = alloc_node<IdentifierNode>(); id->name = cn->value.operator StringName(); + id->datatype = cn->datatype; op->op = OperatorNode::OP_INDEX_NAMED; op->arguments.write[1] = id; @@ -7571,6 +7585,10 @@ GDScriptParser::DataType GDScriptParser::_reduce_identifier_type(const DataType } if (_get_member_type(base_type, p_identifier, member_type)) { + if (!p_base_type && current_function && current_function->_static) { + _set_error("Can't access member variable (\"" + p_identifier.operator String() + "\") from a static function.", p_line); + return DataType(); + } return member_type; } @@ -7802,7 +7820,7 @@ void GDScriptParser::_check_class_level_types(ClassNode *p_class) { ConstantNode *tgt_type = alloc_node<ConstantNode>(); tgt_type->line = v.line; - tgt_type->value = (int)v.data_type.builtin_type; + tgt_type->value = (int64_t)v.data_type.builtin_type; OperatorNode *convert_call = alloc_node<OperatorNode>(); convert_call->line = v.line; @@ -8179,7 +8197,7 @@ void GDScriptParser::_check_block_types(BlockNode *p_block) { ConstantNode *tgt_type = alloc_node<ConstantNode>(); tgt_type->line = lv->line; - tgt_type->value = (int)lv->datatype.builtin_type; + tgt_type->value = (int64_t)lv->datatype.builtin_type; tgt_type->datatype = _type_from_variant(tgt_type->value); OperatorNode *convert_call = alloc_node<OperatorNode>(); @@ -8784,7 +8802,7 @@ int GDScriptParser::get_completion_argument_index() { return completion_argument; } -int GDScriptParser::get_completion_identifier_is_function() { +bool GDScriptParser::get_completion_identifier_is_function() { return completion_ident_is_call; } diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h index f254352423..b345a88744 100644 --- a/modules/gdscript/gdscript_parser.h +++ b/modules/gdscript/gdscript_parser.h @@ -624,6 +624,7 @@ private: void _parse_extends(ClassNode *p_class); void _parse_class(ClassNode *p_class); bool _end_statement(); + void _set_end_statement_error(String p_name); void _determine_inheritance(ClassNode *p_class, bool p_recursive = true); bool _parse_type(DataType &r_type, bool p_can_be_void = false); @@ -683,7 +684,7 @@ public: BlockNode *get_completion_block(); FunctionNode *get_completion_function(); int get_completion_argument_index(); - int get_completion_identifier_is_function(); + bool get_completion_identifier_is_function(); const List<String> &get_dependencies() const { return dependencies; } diff --git a/modules/gdscript/gdscript_tokenizer.cpp b/modules/gdscript/gdscript_tokenizer.cpp index d42ca52731..76f42ead5f 100644 --- a/modules/gdscript/gdscript_tokenizer.cpp +++ b/modules/gdscript/gdscript_tokenizer.cpp @@ -803,19 +803,16 @@ void GDScriptTokenizerText::_advance() { switch (next) { - case 'a': res = 7; break; - case 'b': res = 8; break; - case 't': res = 9; break; - case 'n': res = 10; break; - case 'v': res = 11; break; - case 'f': res = 12; break; - case 'r': res = 13; break; + case 'a': res = '\a'; break; + case 'b': res = '\b'; break; + case 't': res = '\t'; break; + case 'n': res = '\n'; break; + case 'v': res = '\v'; break; + case 'f': res = '\f'; break; + case 'r': res = '\r'; break; case '\'': res = '\''; break; case '\"': res = '\"'; break; case '\\': res = '\\'; break; - case '/': - res = '/'; - break; //wtf case 'u': { // hex number @@ -847,6 +844,10 @@ void GDScriptTokenizerText::_advance() { i += 3; } break; + case '\n': { + line++; + column = 1; + } break; default: { _make_error("Invalid escape sequence"); @@ -854,7 +855,8 @@ void GDScriptTokenizerText::_advance() { } break; } - str += res; + if (next != '\n') + str += res; } else { if (CharType(GETCHAR(i)) == '\n') { |