diff options
Diffstat (limited to 'modules/gdscript/gdscript_cache.cpp')
-rw-r--r-- | modules/gdscript/gdscript_cache.cpp | 255 |
1 files changed, 127 insertions, 128 deletions
diff --git a/modules/gdscript/gdscript_cache.cpp b/modules/gdscript/gdscript_cache.cpp index 76f4e69ab9..7c9fba799d 100644 --- a/modules/gdscript/gdscript_cache.cpp +++ b/modules/gdscript/gdscript_cache.cpp @@ -37,93 +37,105 @@ #include "core/io/file_access.h" #include "core/templates/vector.h" -#include "scene/resources/packed_scene.h" - -bool GDScriptParserRef::is_valid() const { - return parser != nullptr; -} GDScriptParserRef::Status GDScriptParserRef::get_status() const { return status; } -GDScriptParser *GDScriptParserRef::get_parser() const { +String GDScriptParserRef::get_path() const { + return path; +} + +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: + 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; - result = parser->parse(GDScriptCache::get_source_code(path), path, false); - break; + String remapped_path = ResourceLoader::path_remap(path); + if (remapped_path.get_extension().to_lower() == "gdc") { + 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 { + 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 = FULLY_SOLVED; - Error body_result = get_analyzer()->resolve_body(); - if (result == OK) { - result = body_result; - } + result = get_analyzer()->resolve_body(); } 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; + + parser = nullptr; + analyzer = nullptr; + status = EMPTY; + result = OK; + source_hash = 0; - if (parser != nullptr) { - memdelete(parser); + clearing = false; + + if (lanalyzer != nullptr) { + memdelete(lanalyzer); } - if (analyzer != nullptr) { - memdelete(analyzer); + if (lparser != nullptr) { + memdelete(lparser); } } GDScriptParserRef::~GDScriptParserRef() { clear(); - - MutexLock lock(GDScriptCache::singleton->mutex); - GDScriptCache::singleton->parser_map.erase(path); + GDScriptCache::remove_parser(path); } GDScriptCache *GDScriptCache::singleton = nullptr; @@ -139,18 +151,16 @@ void GDScriptCache::move_script(const String &p_from, const String &p_to) { return; } - for (KeyValue<String, HashSet<String>> &E : singleton->packed_scene_dependencies) { - if (E.value.has(p_from)) { - E.value.insert(p_to); - E.value.erase(p_from); - } - } - if (singleton->parser_map.has(p_from) && !p_from.is_empty()) { singleton->parser_map[p_to] = singleton->parser_map[p_from]; } singleton->parser_map.erase(p_from); + if (singleton->parser_inverse_dependencies.has(p_from) && !p_from.is_empty()) { + singleton->parser_inverse_dependencies[p_to] = singleton->parser_inverse_dependencies[p_from]; + } + singleton->parser_inverse_dependencies.erase(p_from); + if (singleton->shallow_gdscript_cache.has(p_from) && !p_from.is_empty()) { singleton->shallow_gdscript_cache[p_to] = singleton->shallow_gdscript_cache[p_from]; } @@ -173,20 +183,12 @@ void GDScriptCache::remove_script(const String &p_path) { return; } - for (KeyValue<String, HashSet<String>> &E : singleton->packed_scene_dependencies) { - if (!E.value.has(p_path)) { - continue; - } - E.value.erase(p_path); - } - - GDScriptCache::clear_unreferenced_packed_scenes(); - if (singleton->parser_map.has(p_path)) { singleton->parser_map[p_path]->clear(); - singleton->parser_map.erase(p_path); } + remove_parser(p_path); + singleton->dependencies.erase(p_path); singleton->shallow_gdscript_cache.erase(p_path); singleton->full_gdscript_cache.erase(p_path); @@ -197,6 +199,7 @@ Ref<GDScriptParserRef> GDScriptCache::get_parser(const String &p_path, GDScriptP Ref<GDScriptParserRef> ref; if (!p_owner.is_empty()) { singleton->dependencies[p_owner].insert(p_path); + singleton->parser_inverse_dependencies[p_path].insert(p_owner); } if (singleton->parser_map.has(p_path)) { ref = Ref<GDScriptParserRef>(singleton->parser_map[p_path]); @@ -205,13 +208,12 @@ Ref<GDScriptParserRef> GDScriptCache::get_parser(const String &p_path, GDScriptP return ref; } } else { - if (!FileAccess::exists(p_path)) { + String remapped_path = ResourceLoader::path_remap(p_path); + if (!FileAccess::exists(remapped_path)) { 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(); } @@ -220,6 +222,24 @@ 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); + + // Have to copy while iterating, because parser_inverse_dependencies is modified. + HashSet<String> ideps = singleton->parser_inverse_dependencies[p_path]; + singleton->parser_inverse_dependencies.erase(p_path); + for (String idep_path : ideps) { + remove_parser(idep_path); + } +} + String GDScriptCache::get_source_code(const String &p_path) { Vector<uint8_t> source_file; Error err; @@ -239,6 +259,20 @@ String GDScriptCache::get_source_code(const String &p_path) { return source; } +Vector<uint8_t> GDScriptCache::get_binary_tokens(const String &p_path) { + Vector<uint8_t> buffer; + Error err = OK; + Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ, &err); + ERR_FAIL_COND_V_MSG(err != OK, buffer, "Failed to open binary GDScript file '" + p_path + "'."); + + uint64_t len = f->get_length(); + buffer.resize(len); + uint64_t read = f->get_buffer(buffer.ptrw(), buffer.size()); + ERR_FAIL_COND_V_MSG(read != len, Vector<uint8_t>(), "Failed to read binary GDScript file '" + p_path + "'."); + + return buffer; +} + Ref<GDScript> GDScriptCache::get_shallow_script(const String &p_path, Error &r_error, const String &p_owner) { MutexLock lock(singleton->mutex); if (!p_owner.is_empty()) { @@ -251,10 +285,20 @@ Ref<GDScript> GDScriptCache::get_shallow_script(const String &p_path, Error &r_e return singleton->shallow_gdscript_cache[p_path]; } + String remapped_path = ResourceLoader::path_remap(p_path); + Ref<GDScript> script; script.instantiate(); script->set_path(p_path, true); - r_error = script->load_source_code(p_path); + if (remapped_path.get_extension().to_lower() == "gdc") { + Vector<uint8_t> buffer = get_binary_tokens(remapped_path); + if (buffer.is_empty()) { + r_error = ERR_FILE_CANT_READ; + } + script->set_binary_tokens_source(buffer); + } else { + r_error = script->load_source_code(remapped_path); + } if (r_error) { return Ref<GDScript>(); // Returns null and does not cache when the script fails to load. @@ -294,13 +338,26 @@ Ref<GDScript> GDScriptCache::get_full_script(const String &p_path, Error &r_erro } if (p_update_from_disk) { - r_error = script->load_source_code(p_path); - if (r_error) { - return script; + if (p_path.get_extension().to_lower() == "gdc") { + Vector<uint8_t> buffer = get_binary_tokens(p_path); + if (buffer.is_empty()) { + r_error = ERR_FILE_CANT_READ; + return script; + } + script->set_binary_tokens_source(buffer); + } else { + r_error = script->load_source_code(p_path); + if (r_error) { + return script; + } } } + // Allowing lifting the lock might cause a script to be reloaded multiple times, + // which, as a last resort deadlock prevention strategy, is a good tradeoff. + uint32_t allowance_id = WorkerThreadPool::thread_enter_unlock_allowance_zone(&singleton->mutex); r_error = script->reload(true); + WorkerThreadPool::thread_exit_unlock_allowance_zone(allowance_id); if (r_error) { return script; } @@ -361,62 +418,6 @@ void GDScriptCache::remove_static_script(const String &p_fqcn) { singleton->static_gdscript_cache.erase(p_fqcn); } -Ref<PackedScene> GDScriptCache::get_packed_scene(const String &p_path, Error &r_error, const String &p_owner) { - MutexLock lock(singleton->mutex); - - String path = p_path; - if (path.begins_with("uid://")) { - path = ResourceUID::get_singleton()->get_id_path(ResourceUID::get_singleton()->text_to_id(path)); - } - - if (singleton->packed_scene_cache.has(path)) { - singleton->packed_scene_dependencies[path].insert(p_owner); - return singleton->packed_scene_cache[path]; - } - - Ref<PackedScene> scene = ResourceCache::get_ref(path); - if (scene.is_valid()) { - singleton->packed_scene_cache[path] = scene; - singleton->packed_scene_dependencies[path].insert(p_owner); - return scene; - } - scene.instantiate(); - - r_error = OK; - if (path.is_empty()) { - r_error = ERR_FILE_BAD_PATH; - return scene; - } - - scene->set_path(path); - singleton->packed_scene_cache[path] = scene; - singleton->packed_scene_dependencies[path].insert(p_owner); - - scene->reload_from_file(); - return scene; -} - -void GDScriptCache::clear_unreferenced_packed_scenes() { - if (singleton == nullptr) { - return; - } - - MutexLock lock(singleton->mutex); - - if (singleton->cleared) { - return; - } - - for (KeyValue<String, HashSet<String>> &E : singleton->packed_scene_dependencies) { - if (E.value.size() > 0 || !ResourceLoader::is_imported(E.key)) { - continue; - } - - singleton->packed_scene_dependencies.erase(E.key); - singleton->packed_scene_cache.erase(E.key); - } -} - void GDScriptCache::clear() { if (singleton == nullptr) { return; @@ -429,26 +430,24 @@ void GDScriptCache::clear() { } singleton->cleared = true; + singleton->parser_inverse_dependencies.clear(); + RBSet<Ref<GDScriptParserRef>> parser_map_refs; for (KeyValue<String, GDScriptParserRef *> &E : singleton->parser_map) { 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(); + } } - singleton->packed_scene_dependencies.clear(); - singleton->packed_scene_cache.clear(); - parser_map_refs.clear(); - singleton->parser_map.clear(); singleton->shallow_gdscript_cache.clear(); singleton->full_gdscript_cache.clear(); - - singleton->packed_scene_cache.clear(); - singleton->packed_scene_dependencies.clear(); } GDScriptCache::GDScriptCache() { |