summaryrefslogtreecommitdiffstats
path: root/modules/gdscript/gdscript_cache.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'modules/gdscript/gdscript_cache.cpp')
-rw-r--r--modules/gdscript/gdscript_cache.cpp255
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() {