diff options
author | rune-scape <allie.smith.epic@gmail.com> | 2024-04-12 16:13:25 -0700 |
---|---|---|
committer | rune-scape <allie.smith.epic@gmail.com> | 2024-04-18 14:05:58 -0700 |
commit | 6b88c86cec1f38934730beca613ee29df5643f6a (patch) | |
tree | 654403c3ab21a0544f36a9a3fd3c6b03081113e2 /modules | |
parent | 029aadef563fb69cf49aa9795b62f27171f8c3f4 (diff) | |
download | redot-engine-6b88c86cec1f38934730beca613ee29df5643f6a.tar.gz |
GDScript: invalidate GDScriptParserRef when reloading
Diffstat (limited to 'modules')
-rw-r--r-- | modules/gdscript/gdscript.cpp | 16 | ||||
-rw-r--r-- | modules/gdscript/gdscript_analyzer.cpp | 69 | ||||
-rw-r--r-- | modules/gdscript/gdscript_analyzer.h | 3 | ||||
-rw-r--r-- | modules/gdscript/gdscript_cache.cpp | 104 | ||||
-rw-r--r-- | modules/gdscript/gdscript_cache.h | 11 | ||||
-rw-r--r-- | modules/gdscript/gdscript_editor.cpp | 2 | ||||
-rw-r--r-- | modules/gdscript/gdscript_parser.cpp | 36 | ||||
-rw-r--r-- | modules/gdscript/gdscript_parser.h | 4 |
8 files changed, 139 insertions, 106 deletions
diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp index 8e74de4242..921ed0b416 100644 --- a/modules/gdscript/gdscript.cpp +++ b/modules/gdscript/gdscript.cpp @@ -739,10 +739,18 @@ Error GDScript::reload(bool p_keep_state) { if (source_path.is_empty()) { source_path = get_path(); } - Ref<GDScript> cached_script = GDScriptCache::get_cached_script(source_path); - if (!source_path.is_empty() && cached_script.is_null()) { - MutexLock lock(GDScriptCache::singleton->mutex); - GDScriptCache::singleton->shallow_gdscript_cache[source_path] = Ref<GDScript>(this); + if (!source_path.is_empty()) { + if (GDScriptCache::get_cached_script(source_path).is_null()) { + MutexLock lock(GDScriptCache::singleton->mutex); + GDScriptCache::singleton->shallow_gdscript_cache[source_path] = Ref<GDScript>(this); + } + if (GDScriptCache::has_parser(source_path)) { + Error err = OK; + Ref<GDScriptParserRef> parser_ref = GDScriptCache::get_parser(source_path, GDScriptParserRef::EMPTY, err); + if (parser_ref.is_valid() && parser_ref->get_source_hash() != source.hash()) { + GDScriptCache::remove_parser(source_path); + } + } } } diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp index a30a87f3a1..a641cc25d5 100644 --- a/modules/gdscript/gdscript_analyzer.cpp +++ b/modules/gdscript/gdscript_analyzer.cpp @@ -325,7 +325,7 @@ Error GDScriptAnalyzer::resolve_class_inheritance(GDScriptParser::ClassNode *p_c if (!parser->has_class(p_class)) { String script_path = p_class->get_datatype().script_path; - Ref<GDScriptParserRef> parser_ref = get_parser_for(script_path); + Ref<GDScriptParserRef> parser_ref = parser->get_depended_parser_for(script_path); if (parser_ref.is_null()) { push_error(vformat(R"(Could not find script "%s".)", script_path), p_source); return ERR_PARSE_ERROR; @@ -400,7 +400,7 @@ Error GDScriptAnalyzer::resolve_class_inheritance(GDScriptParser::ClassNode *p_c if (p_class->extends_path.is_relative_path()) { p_class->extends_path = class_type.script_path.get_base_dir().path_join(p_class->extends_path).simplify_path(); } - Ref<GDScriptParserRef> ext_parser = get_parser_for(p_class->extends_path); + Ref<GDScriptParserRef> ext_parser = parser->get_depended_parser_for(p_class->extends_path); if (ext_parser.is_null()) { push_error(vformat(R"(Could not resolve super class path "%s".)", p_class->extends_path), p_class); return ERR_PARSE_ERROR; @@ -428,7 +428,7 @@ Error GDScriptAnalyzer::resolve_class_inheritance(GDScriptParser::ClassNode *p_c if (GDScript::is_canonically_equal_paths(base_path, parser->script_path)) { base = parser->head->get_datatype(); } else { - Ref<GDScriptParserRef> base_parser = get_parser_for(base_path); + Ref<GDScriptParserRef> base_parser = parser->get_depended_parser_for(base_path); if (base_parser.is_null()) { push_error(vformat(R"(Could not resolve super class "%s".)", name), id); return ERR_PARSE_ERROR; @@ -448,7 +448,7 @@ Error GDScriptAnalyzer::resolve_class_inheritance(GDScriptParser::ClassNode *p_c return ERR_PARSE_ERROR; } - Ref<GDScriptParserRef> info_parser = get_parser_for(info.path); + Ref<GDScriptParserRef> info_parser = parser->get_depended_parser_for(info.path); if (info_parser.is_null()) { push_error(vformat(R"(Could not parse singleton from "%s".)", info.path), id); return ERR_PARSE_ERROR; @@ -644,7 +644,7 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type } else if (Ref<Script>(local.constant->initializer->reduced_value).is_valid()) { Ref<GDScript> gdscript = local.constant->initializer->reduced_value; if (gdscript.is_valid()) { - Ref<GDScriptParserRef> ref = get_parser_for(gdscript->get_script_path()); + Ref<GDScriptParserRef> ref = parser->get_depended_parser_for(gdscript->get_script_path()); if (ref->raise_status(GDScriptParserRef::INHERITANCE_SOLVED) != OK) { push_error(vformat(R"(Could not parse script from "%s".)", gdscript->get_script_path()), first_id); return bad_type; @@ -710,7 +710,7 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type String path = ScriptServer::get_global_class_path(first); String ext = path.get_extension(); if (ext == GDScriptLanguage::get_singleton()->get_extension()) { - Ref<GDScriptParserRef> ref = get_parser_for(path); + Ref<GDScriptParserRef> ref = parser->get_depended_parser_for(path); if (!ref.is_valid() || ref->raise_status(GDScriptParserRef::INHERITANCE_SOLVED) != OK) { push_error(vformat(R"(Could not parse global class "%s" from "%s".)", first, ScriptServer::get_global_class_path(first)), p_type); return bad_type; @@ -722,7 +722,7 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type } } else if (ProjectSettings::get_singleton()->has_autoload(first) && ProjectSettings::get_singleton()->get_autoload(first).is_singleton) { const ProjectSettings::AutoloadInfo &autoload = ProjectSettings::get_singleton()->get_autoload(first); - Ref<GDScriptParserRef> ref = get_parser_for(autoload.path); + Ref<GDScriptParserRef> ref = parser->get_depended_parser_for(autoload.path); if (ref.is_null()) { push_error(vformat(R"(The referenced autoload "%s" (from "%s") could not be loaded.)", first, autoload.path), p_type); return bad_type; @@ -776,7 +776,7 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type } else if (Ref<Script>(member.constant->initializer->reduced_value).is_valid()) { Ref<GDScript> gdscript = member.constant->initializer->reduced_value; if (gdscript.is_valid()) { - Ref<GDScriptParserRef> ref = get_parser_for(gdscript->get_script_path()); + Ref<GDScriptParserRef> ref = parser->get_depended_parser_for(gdscript->get_script_path()); if (ref->raise_status(GDScriptParserRef::INHERITANCE_SOLVED) != OK) { push_error(vformat(R"(Could not parse script from "%s".)", gdscript->get_script_path()), p_type); return bad_type; @@ -876,7 +876,7 @@ void GDScriptAnalyzer::resolve_class_member(GDScriptParser::ClassNode *p_class, if (!parser->has_class(p_class)) { String script_path = p_class->get_datatype().script_path; - Ref<GDScriptParserRef> parser_ref = get_parser_for(script_path); + Ref<GDScriptParserRef> parser_ref = parser->get_depended_parser_for(script_path); if (parser_ref.is_null()) { push_error(vformat(R"(Could not find script "%s" (While resolving "%s").)", script_path, member.get_name()), p_source); return; @@ -1159,7 +1159,7 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas if (!parser->has_class(p_class)) { String script_path = p_class->get_datatype().script_path; - Ref<GDScriptParserRef> parser_ref = get_parser_for(script_path); + Ref<GDScriptParserRef> parser_ref = parser->get_depended_parser_for(script_path); if (parser_ref.is_null()) { push_error(vformat(R"(Could not find script "%s".)", script_path), p_source); return; @@ -1249,7 +1249,7 @@ void GDScriptAnalyzer::resolve_class_body(GDScriptParser::ClassNode *p_class, co if (!parser->has_class(p_class)) { String script_path = p_class->get_datatype().script_path; - Ref<GDScriptParserRef> parser_ref = get_parser_for(script_path); + Ref<GDScriptParserRef> parser_ref = parser->get_depended_parser_for(script_path); if (parser_ref.is_null()) { push_error(vformat(R"(Could not find script "%s".)", script_path), p_source); return; @@ -3542,7 +3542,7 @@ GDScriptParser::DataType GDScriptAnalyzer::make_global_class_meta_type(const Str String path = ScriptServer::get_global_class_path(p_class_name); String ext = path.get_extension(); if (ext == GDScriptLanguage::get_singleton()->get_extension()) { - Ref<GDScriptParserRef> ref = get_parser_for(path); + Ref<GDScriptParserRef> ref = parser->get_depended_parser_for(path); if (ref.is_null()) { push_error(vformat(R"(Could not find script for class "%s".)", p_class_name), p_source); type.type_source = GDScriptParser::DataType::UNDETECTED; @@ -4064,7 +4064,7 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident result.builtin_type = Variant::OBJECT; result.native_type = SNAME("Node"); if (ResourceLoader::get_resource_type(autoload.path) == "GDScript") { - Ref<GDScriptParserRef> singl_parser = get_parser_for(autoload.path); + Ref<GDScriptParserRef> singl_parser = parser->get_depended_parser_for(autoload.path); if (singl_parser.is_valid()) { Error err = singl_parser->raise_status(GDScriptParserRef::INHERITANCE_SOLVED); if (err == OK) { @@ -4078,7 +4078,7 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident if (node != nullptr) { Ref<GDScript> scr = node->get_script(); if (scr.is_valid()) { - Ref<GDScriptParserRef> singl_parser = get_parser_for(scr->get_script_path()); + Ref<GDScriptParserRef> singl_parser = parser->get_depended_parser_for(scr->get_script_path()); if (singl_parser.is_valid()) { Error err = singl_parser->raise_status(GDScriptParserRef::INHERITANCE_SOLVED); if (err == OK) { @@ -4808,10 +4808,6 @@ Variant GDScriptAnalyzer::make_variable_default_value(GDScriptParser::VariableNo return result; } -const HashMap<String, Ref<GDScriptParserRef>> &GDScriptAnalyzer::get_depended_parsers() { - return depended_parsers; -} - GDScriptParser::DataType GDScriptAnalyzer::type_from_variant(const Variant &p_value, const GDScriptParser::Node *p_source) { GDScriptParser::DataType result; result.is_constant = true; @@ -4851,7 +4847,7 @@ GDScriptParser::DataType GDScriptAnalyzer::type_from_variant(const Variant &p_va // This might be an inner class, so we want to get the parser for the root. // But still get the inner class from that tree. String script_path = gds->get_script_path(); - Ref<GDScriptParserRef> ref = get_parser_for(script_path); + Ref<GDScriptParserRef> ref = parser->get_depended_parser_for(script_path); if (ref.is_null()) { push_error(vformat(R"(Could not find script "%s".)", script_path), p_source); GDScriptParser::DataType error_type; @@ -5605,21 +5601,6 @@ bool GDScriptAnalyzer::class_exists(const StringName &p_class) const { return ClassDB::class_exists(p_class) && ClassDB::is_class_exposed(p_class); } -Ref<GDScriptParserRef> GDScriptAnalyzer::get_parser_for(const String &p_path) { - Ref<GDScriptParserRef> ref; - if (depended_parsers.has(p_path)) { - ref = depended_parsers[p_path]; - } else { - Error err = OK; - ref = GDScriptCache::get_parser(p_path, GDScriptParserRef::EMPTY, err, parser->script_path); - if (ref.is_valid()) { - depended_parsers[p_path] = ref; - } - } - - return ref; -} - Error GDScriptAnalyzer::resolve_inheritance() { return resolve_class_inheritance(parser->head, true); } @@ -5631,11 +5612,17 @@ Error GDScriptAnalyzer::resolve_interface() { Error GDScriptAnalyzer::resolve_body() { resolve_class_body(parser->head, true); + +#ifdef DEBUG_ENABLED + // Apply here, after all `@warning_ignore`s have been resolved and applied. + parser->apply_pending_warnings(); +#endif + return parser->errors.is_empty() ? OK : ERR_PARSE_ERROR; } Error GDScriptAnalyzer::resolve_dependencies() { - for (KeyValue<String, Ref<GDScriptParserRef>> &K : depended_parsers) { + for (KeyValue<String, Ref<GDScriptParserRef>> &K : parser->depended_parsers) { if (K.value.is_null()) { return ERR_PARSE_ERROR; } @@ -5654,15 +5641,9 @@ Error GDScriptAnalyzer::analyze() { } resolve_interface(); - resolve_body(); - -#ifdef DEBUG_ENABLED - // Apply here, after all `@warning_ignore`s have been resolved and applied. - parser->apply_pending_warnings(); -#endif - - if (!parser->errors.is_empty()) { - return ERR_PARSE_ERROR; + err = resolve_body(); + if (err) { + return err; } return resolve_dependencies(); diff --git a/modules/gdscript/gdscript_analyzer.h b/modules/gdscript/gdscript_analyzer.h index e398ccfdbb..922000df52 100644 --- a/modules/gdscript/gdscript_analyzer.h +++ b/modules/gdscript/gdscript_analyzer.h @@ -40,7 +40,6 @@ class GDScriptAnalyzer { GDScriptParser *parser = nullptr; - HashMap<String, Ref<GDScriptParserRef>> depended_parsers; const GDScriptParser::EnumNode *current_enum = nullptr; GDScriptParser::LambdaNode *current_lambda = nullptr; @@ -132,7 +131,6 @@ class GDScriptAnalyzer { void mark_lambda_use_self(); void resolve_pending_lambda_bodies(); bool class_exists(const StringName &p_class) const; - Ref<GDScriptParserRef> get_parser_for(const String &p_path); void reduce_identifier_from_base_set_class(GDScriptParser::IdentifierNode *p_identifier, GDScriptParser::DataType p_identifier_datatype); #ifdef DEBUG_ENABLED void is_shadowing(GDScriptParser::IdentifierNode *p_identifier, const String &p_context, const bool p_in_local_scope); @@ -146,7 +144,6 @@ public: Error analyze(); Variant make_variable_default_value(GDScriptParser::VariableNode *p_variable); - const HashMap<String, Ref<GDScriptParserRef>> &get_depended_parsers(); static bool check_type_compatibility(const GDScriptParser::DataType &p_target, const GDScriptParser::DataType &p_source, bool p_allow_implicit_conversion = false, const GDScriptParser::Node *p_source_node = nullptr); GDScriptAnalyzer(GDScriptParser *p_parser); diff --git a/modules/gdscript/gdscript_cache.cpp b/modules/gdscript/gdscript_cache.cpp index 7c27127dce..ac6f5f05c6 100644 --- a/modules/gdscript/gdscript_cache.cpp +++ b/modules/gdscript/gdscript_cache.cpp @@ -38,88 +38,98 @@ #include "core/io/file_access.h" #include "core/templates/vector.h" -bool GDScriptParserRef::is_valid() const { - return parser != nullptr; -} - GDScriptParserRef::Status GDScriptParserRef::get_status() const { return status; } -GDScriptParser *GDScriptParserRef::get_parser() const { +uint32_t GDScriptParserRef::get_source_hash() const { + return source_hash; +} + +GDScriptParser *GDScriptParserRef::get_parser() { + if (parser == nullptr) { + parser = memnew(GDScriptParser); + } return parser; } GDScriptAnalyzer *GDScriptParserRef::get_analyzer() { if (analyzer == nullptr) { - analyzer = memnew(GDScriptAnalyzer(parser)); + analyzer = memnew(GDScriptAnalyzer(get_parser())); } return analyzer; } Error GDScriptParserRef::raise_status(Status p_new_status) { - ERR_FAIL_NULL_V(parser, ERR_INVALID_DATA); - - if (result != OK) { - return result; - } + ERR_FAIL_COND_V(clearing, ERR_BUG); + ERR_FAIL_COND_V(parser == nullptr && status != EMPTY, ERR_BUG); - while (p_new_status > status) { + while (result == OK && p_new_status > status) { switch (status) { case EMPTY: { + // Calling parse will clear the parser, which can destruct another GDScriptParserRef which can clear the last reference to the script with this path, calling remove_script, which clears this GDScriptParserRef. + // It's ok if its the first thing done here. + get_parser()->clear(); status = PARSED; String remapped_path = ResourceLoader::path_remap(path); if (remapped_path.get_extension().to_lower() == "gdc") { - result = parser->parse_binary(GDScriptCache::get_binary_tokens(remapped_path), path); + Vector<uint8_t> tokens = GDScriptCache::get_binary_tokens(remapped_path); + source_hash = hash_djb2_buffer(tokens.ptr(), tokens.size()); + result = get_parser()->parse_binary(tokens, path); } else { - result = parser->parse(GDScriptCache::get_source_code(remapped_path), path, false); + String source = GDScriptCache::get_source_code(remapped_path); + source_hash = source.hash(); + result = get_parser()->parse(source, path, false); } } break; case PARSED: { status = INHERITANCE_SOLVED; - Error inheritance_result = get_analyzer()->resolve_inheritance(); - if (result == OK) { - result = inheritance_result; - } + result = get_analyzer()->resolve_inheritance(); } break; case INHERITANCE_SOLVED: { status = INTERFACE_SOLVED; - Error interface_result = get_analyzer()->resolve_interface(); - if (result == OK) { - result = interface_result; - } + result = get_analyzer()->resolve_interface(); } break; case INTERFACE_SOLVED: { + status = BODY_SOLVED; + result = get_analyzer()->resolve_body(); + } break; + case BODY_SOLVED: { status = FULLY_SOLVED; - Error body_result = get_analyzer()->resolve_body(); - if (result == OK) { - result = body_result; - } + result = get_analyzer()->resolve_dependencies(); } break; case FULLY_SOLVED: { return result; } } - if (result != OK) { - return result; - } } return result; } void GDScriptParserRef::clear() { - if (cleared) { + if (clearing) { return; } - cleared = true; + clearing = true; + + GDScriptParser *lparser = parser; + GDScriptAnalyzer *lanalyzer = analyzer; - if (parser != nullptr) { - memdelete(parser); + parser = nullptr; + analyzer = nullptr; + status = EMPTY; + result = OK; + source_hash = 0; + + clearing = false; + + if (lanalyzer != nullptr) { + memdelete(lanalyzer); } - if (analyzer != nullptr) { - memdelete(analyzer); + if (lparser != nullptr) { + memdelete(lparser); } } @@ -171,8 +181,11 @@ void GDScriptCache::remove_script(const String &p_path) { } if (singleton->parser_map.has(p_path)) { - singleton->parser_map[p_path]->clear(); + // Keep a local reference until it goes out of scope. + // Clearing it can trigger a reference to itself to go out of scope, destructing it before clear finishes. + Ref<GDScriptParserRef> parser_ref = singleton->parser_map[p_path]; singleton->parser_map.erase(p_path); + parser_ref->clear(); } singleton->dependencies.erase(p_path); @@ -198,9 +211,7 @@ Ref<GDScriptParserRef> GDScriptCache::get_parser(const String &p_path, GDScriptP r_error = ERR_FILE_NOT_FOUND; return ref; } - GDScriptParser *parser = memnew(GDScriptParser); ref.instantiate(); - ref->parser = parser; ref->path = p_path; singleton->parser_map[p_path] = ref.ptr(); } @@ -209,6 +220,17 @@ Ref<GDScriptParserRef> GDScriptCache::get_parser(const String &p_path, GDScriptP return ref; } +bool GDScriptCache::has_parser(const String &p_path) { + MutexLock lock(singleton->mutex); + return singleton->parser_map.has(p_path); +} + +void GDScriptCache::remove_parser(const String &p_path) { + MutexLock lock(singleton->mutex); + // Can't clear the parser because some other parser might be currently using it in the chain of calls. + singleton->parser_map.erase(p_path); +} + String GDScriptCache::get_source_code(const String &p_path) { Vector<uint8_t> source_file; Error err; @@ -400,13 +422,15 @@ void GDScriptCache::clear() { parser_map_refs.insert(E.value); } + singleton->parser_map.clear(); + for (Ref<GDScriptParserRef> &E : parser_map_refs) { - if (E.is_valid()) + if (E.is_valid()) { E->clear(); + } } parser_map_refs.clear(); - singleton->parser_map.clear(); singleton->shallow_gdscript_cache.clear(); singleton->full_gdscript_cache.clear(); } diff --git a/modules/gdscript/gdscript_cache.h b/modules/gdscript/gdscript_cache.h index fc7abbd46e..c738233beb 100644 --- a/modules/gdscript/gdscript_cache.h +++ b/modules/gdscript/gdscript_cache.h @@ -48,6 +48,7 @@ public: PARSED, INHERITANCE_SOLVED, INTERFACE_SOLVED, + BODY_SOLVED, FULLY_SOLVED, }; @@ -57,14 +58,16 @@ private: Status status = EMPTY; Error result = OK; String path; - bool cleared = false; + uint32_t source_hash = 0; + bool clearing = false; friend class GDScriptCache; + friend class GDScript; public: - bool is_valid() const; Status get_status() const; - GDScriptParser *get_parser() const; + uint32_t get_source_hash() const; + GDScriptParser *get_parser(); GDScriptAnalyzer *get_analyzer(); Error raise_status(Status p_new_status); void clear(); @@ -95,6 +98,8 @@ public: static void move_script(const String &p_from, const String &p_to); static void remove_script(const String &p_path); static Ref<GDScriptParserRef> get_parser(const String &p_path, GDScriptParserRef::Status status, Error &r_error, const String &p_owner = String()); + static bool has_parser(const String &p_path); + static void remove_parser(const String &p_path); static String get_source_code(const String &p_path); static Vector<uint8_t> get_binary_tokens(const String &p_path); static Ref<GDScript> get_shallow_script(const String &p_path, Error &r_error, const String &p_owner = String()); diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp index eaef8a961c..74d383c57f 100644 --- a/modules/gdscript/gdscript_editor.cpp +++ b/modules/gdscript/gdscript_editor.cpp @@ -163,7 +163,7 @@ bool GDScriptLanguage::validate(const String &p_script, const String &p_path, Li r_errors->push_back(e); } - for (KeyValue<String, Ref<GDScriptParserRef>> E : analyzer.get_depended_parsers()) { + for (KeyValue<String, Ref<GDScriptParserRef>> E : parser.get_depended_parsers()) { GDScriptParser *depended_parser = E.value->get_parser(); for (const GDScriptParser::ParserError &pe : depended_parser->get_errors()) { ScriptLanguage::ScriptError e; diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp index bb5d6770c6..9799c6e610 100644 --- a/modules/gdscript/gdscript_parser.cpp +++ b/modules/gdscript/gdscript_parser.cpp @@ -147,23 +147,17 @@ GDScriptParser::GDScriptParser() { } GDScriptParser::~GDScriptParser() { - clear(); -} - -void GDScriptParser::clear() { while (list != nullptr) { Node *element = list; list = list->next; memdelete(element); } +} - head = nullptr; - list = nullptr; - _is_tool = false; - for_completion = false; - errors.clear(); - multiline_stack.clear(); - nodes_in_progress.clear(); +void GDScriptParser::clear() { + GDScriptParser tmp; + tmp = *this; + *this = GDScriptParser(); } void GDScriptParser::push_error(const String &p_message, const Node *p_origin) { @@ -709,6 +703,25 @@ void GDScriptParser::parse_program() { clear_unused_annotations(); } +Ref<GDScriptParserRef> GDScriptParser::get_depended_parser_for(const String &p_path) { + Ref<GDScriptParserRef> ref; + if (depended_parsers.has(p_path)) { + ref = depended_parsers[p_path]; + } else { + Error err = OK; + ref = GDScriptCache::get_parser(p_path, GDScriptParserRef::EMPTY, err, script_path); + if (ref.is_valid()) { + depended_parsers[p_path] = ref; + } + } + + return ref; +} + +const HashMap<String, Ref<GDScriptParserRef>> &GDScriptParser::get_depended_parsers() { + return depended_parsers; +} + GDScriptParser::ClassNode *GDScriptParser::find_class(const String &p_qualified_name) const { String first = p_qualified_name.get_slice("::", 0); @@ -4071,6 +4084,7 @@ bool GDScriptParser::onready_annotation(const AnnotationNode *p_annotation, Node if (current_class && !ClassDB::is_parent_class(current_class->get_datatype().native_type, SNAME("Node"))) { push_error(R"("@onready" can only be used in classes that inherit "Node".)", p_annotation); + return false; } VariableNode *variable = static_cast<VariableNode *>(p_target); diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h index d047fa8e46..77cb8136f8 100644 --- a/modules/gdscript/gdscript_parser.h +++ b/modules/gdscript/gdscript_parser.h @@ -1321,6 +1321,7 @@ public: private: friend class GDScriptAnalyzer; + friend class GDScriptParserRef; bool _is_tool = false; String script_path; @@ -1329,6 +1330,7 @@ private: bool can_break = false; bool can_continue = false; List<bool> multiline_stack; + HashMap<String, Ref<GDScriptParserRef>> depended_parsers; ClassNode *head = nullptr; Node *list = nullptr; @@ -1558,6 +1560,8 @@ public: Error parse_binary(const Vector<uint8_t> &p_binary, const String &p_script_path); ClassNode *get_tree() const { return head; } bool is_tool() const { return _is_tool; } + Ref<GDScriptParserRef> get_depended_parser_for(const String &p_path); + const HashMap<String, Ref<GDScriptParserRef>> &get_depended_parsers(); ClassNode *find_class(const String &p_qualified_name) const; bool has_class(const GDScriptParser::ClassNode *p_class) const; static Variant::Type get_builtin_type(const StringName &p_type); // Excluding `Variant::NIL` and `Variant::OBJECT`. |