summaryrefslogtreecommitdiffstats
path: root/modules/gdscript
diff options
context:
space:
mode:
Diffstat (limited to 'modules/gdscript')
-rw-r--r--modules/gdscript/SCsub2
-rw-r--r--modules/gdscript/editor/gdscript_translation_parser_plugin.cpp2
-rw-r--r--modules/gdscript/gdscript.cpp109
-rw-r--r--modules/gdscript/gdscript.h14
-rw-r--r--modules/gdscript/gdscript_cache.cpp25
-rw-r--r--modules/gdscript/gdscript_lambda_callable.cpp4
-rw-r--r--modules/gdscript/gdscript_lambda_callable.h4
-rw-r--r--modules/gdscript/gdscript_parser.h4
-rw-r--r--modules/gdscript/gdscript_vm.cpp8
9 files changed, 141 insertions, 31 deletions
diff --git a/modules/gdscript/SCsub b/modules/gdscript/SCsub
index 1dc4768186..61accd4fc9 100644
--- a/modules/gdscript/SCsub
+++ b/modules/gdscript/SCsub
@@ -19,6 +19,8 @@ if env.editor_build:
# Using a define in the disabled case, to avoid having an extra define
# in regular builds where all modules are enabled.
env_gdscript.Append(CPPDEFINES=["GDSCRIPT_NO_LSP"])
+ # Also needed in main env to unexpose --lsp-port option.
+ env.Append(CPPDEFINES=["GDSCRIPT_NO_LSP"])
if env["tests"]:
diff --git a/modules/gdscript/editor/gdscript_translation_parser_plugin.cpp b/modules/gdscript/editor/gdscript_translation_parser_plugin.cpp
index becc2876f9..9128f104b8 100644
--- a/modules/gdscript/editor/gdscript_translation_parser_plugin.cpp
+++ b/modules/gdscript/editor/gdscript_translation_parser_plugin.cpp
@@ -40,7 +40,7 @@ void GDScriptEditorTranslationParserPlugin::get_recognized_extensions(List<Strin
}
Error GDScriptEditorTranslationParserPlugin::parse_file(const String &p_path, Vector<String> *r_ids, Vector<Vector<String>> *r_ids_ctx_plural) {
- // Extract all translatable strings using the parsed tree from GDSriptParser.
+ // Extract all translatable strings using the parsed tree from GDScriptParser.
// The strategy is to find all ExpressionNode and AssignmentNode from the tree and extract strings if relevant, i.e
// Search strings in ExpressionNode -> CallNode -> tr(), set_text(), set_placeholder() etc.
// Search strings in AssignmentNode -> text = "__", tooltip_text = "__" etc.
diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp
index 6245cc85a0..4accdb4d21 100644
--- a/modules/gdscript/gdscript.cpp
+++ b/modules/gdscript/gdscript.cpp
@@ -992,6 +992,7 @@ void GDScript::set_path(const String &p_path, bool p_take_over) {
String old_path = path;
path = p_path;
+ path_valid = true;
GDScriptCache::move_script(old_path, p_path);
for (KeyValue<StringName, Ref<GDScript>> &kv : subclasses) {
@@ -1000,6 +1001,9 @@ void GDScript::set_path(const String &p_path, bool p_take_over) {
}
String GDScript::get_script_path() const {
+ if (!path_valid && !get_path().is_empty()) {
+ return get_path();
+ }
return path;
}
@@ -1035,6 +1039,7 @@ Error GDScript::load_source_code(const String &p_path) {
source = s;
path = p_path;
+ path_valid = true;
#ifdef TOOLS_ENABLED
source_changed_cache = true;
set_edited(false);
@@ -1387,33 +1392,106 @@ String GDScript::debug_get_script_name(const Ref<Script> &p_script) {
#endif
thread_local GDScript::UpdatableFuncPtr GDScript::func_ptrs_to_update_thread_local;
+GDScript::UpdatableFuncPtr *GDScript::func_ptrs_to_update_main_thread = &func_ptrs_to_update_thread_local;
+
+GDScript::UpdatableFuncPtrElement *GDScript::_add_func_ptr_to_update(GDScriptFunction **p_func_ptr_ptr) {
+ MutexLock lock(func_ptrs_to_update_mutex);
-GDScript::UpdatableFuncPtrElement GDScript::_add_func_ptr_to_update(GDScriptFunction **p_func_ptr_ptr) {
- UpdatableFuncPtrElement result = {};
+ List<UpdatableFuncPtrElement>::Element *result = func_ptrs_to_update_elems.push_back(UpdatableFuncPtrElement());
{
- MutexLock lock(func_ptrs_to_update_thread_local.mutex);
- result.element = func_ptrs_to_update_thread_local.ptrs.push_back(p_func_ptr_ptr);
- result.mutex = &func_ptrs_to_update_thread_local.mutex;
+ MutexLock lock2(func_ptrs_to_update_thread_local.mutex);
+ result->get().element = func_ptrs_to_update_thread_local.ptrs.push_back(p_func_ptr_ptr);
+ result->get().mutex = &func_ptrs_to_update_thread_local.mutex;
if (likely(func_ptrs_to_update_thread_local.initialized)) {
- return result;
+ return &result->get();
}
func_ptrs_to_update_thread_local.initialized = true;
}
- MutexLock lock(func_ptrs_to_update_mutex);
func_ptrs_to_update.push_back(&func_ptrs_to_update_thread_local);
- return result;
+ return &result->get();
+}
+
+void GDScript::_remove_func_ptr_to_update(const UpdatableFuncPtrElement *p_func_ptr_element) {
+ // None of these checks should ever fail, unless there's a bug.
+ // They can be removed once we are sure they never catch anything.
+ // Left here now due to extra safety needs late in the release cycle.
+ ERR_FAIL_NULL(p_func_ptr_element);
+ MutexLock lock(*p_func_ptr_element->mutex);
+ ERR_FAIL_NULL(p_func_ptr_element->element);
+ ERR_FAIL_NULL(p_func_ptr_element->mutex);
+ p_func_ptr_element->element->erase();
}
-void GDScript::_remove_func_ptr_to_update(const UpdatableFuncPtrElement p_func_ptr_element) {
- ERR_FAIL_NULL(p_func_ptr_element.element);
- ERR_FAIL_NULL(p_func_ptr_element.mutex);
- MutexLock lock(*p_func_ptr_element.mutex);
- p_func_ptr_element.element->erase();
+void GDScript::_fixup_thread_function_bookkeeping() {
+ // Transfer the ownership of these update items to the main thread,
+ // because the current one is dying, leaving theirs orphan, dangling.
+
+ HashSet<GDScript *> scripts;
+
+ DEV_ASSERT(!Thread::is_main_thread());
+ MutexLock lock(func_ptrs_to_update_main_thread->mutex);
+
+ {
+ MutexLock lock2(func_ptrs_to_update_thread_local.mutex);
+
+ while (!func_ptrs_to_update_thread_local.ptrs.is_empty()) {
+ // Transfer the thread-to-script records from the dying thread to the main one.
+
+ List<GDScriptFunction **>::Element *E = func_ptrs_to_update_thread_local.ptrs.front();
+ List<GDScriptFunction **>::Element *new_E = func_ptrs_to_update_main_thread->ptrs.push_front(E->get());
+
+ GDScript *script = (*E->get())->get_script();
+ if (!scripts.has(script)) {
+ scripts.insert(script);
+
+ // Replace dying thread by the main thread in the script-to-thread records.
+
+ MutexLock lock3(script->func_ptrs_to_update_mutex);
+ DEV_ASSERT(script->func_ptrs_to_update.find(&func_ptrs_to_update_thread_local));
+ {
+ for (List<UpdatableFuncPtrElement>::Element *F = script->func_ptrs_to_update_elems.front(); F; F = F->next()) {
+ bool is_dying_thread_entry = F->get().mutex == &func_ptrs_to_update_thread_local.mutex;
+ if (is_dying_thread_entry) {
+ // This may lead to multiple main-thread entries, but that's not a problem
+ // and allows to reuse the element, which is needed, since it's tracked by pointer.
+ F->get().element = new_E;
+ F->get().mutex = &func_ptrs_to_update_main_thread->mutex;
+ }
+ }
+ }
+ }
+
+ E->erase();
+ }
+ }
+ func_ptrs_to_update_main_thread->initialized = true;
+
+ {
+ // Remove orphan thread-to-script entries from every script.
+ // FIXME: This involves iterating through every script whenever a thread dies.
+ // While it's OK that thread creation/destruction are heavy operations,
+ // additional bookkeeping can be used to outperform this brute-force approach.
+
+ GDScriptLanguage *gd_lang = GDScriptLanguage::get_singleton();
+
+ MutexLock lock2(gd_lang->mutex);
+
+ for (SelfList<GDScript> *s = gd_lang->script_list.first(); s; s = s->next()) {
+ GDScript *script = s->self();
+ for (List<UpdatableFuncPtr *>::Element *E = script->func_ptrs_to_update.front(); E; E = E->next()) {
+ bool is_dying_thread_entry = &E->get()->mutex == &func_ptrs_to_update_thread_local.mutex;
+ if (is_dying_thread_entry) {
+ E->erase();
+ break;
+ }
+ }
+ }
+ }
}
void GDScript::clear(ClearData *p_clear_data) {
@@ -1441,6 +1519,7 @@ void GDScript::clear(ClearData *p_clear_data) {
*func_ptr_ptr = nullptr;
}
}
+ func_ptrs_to_update_elems.clear();
}
RBSet<GDScript *> must_clear_dependencies = get_must_clear_dependencies();
@@ -2060,6 +2139,10 @@ void GDScriptLanguage::remove_named_global_constant(const StringName &p_name) {
named_globals.erase(p_name);
}
+void GDScriptLanguage::thread_exit() {
+ GDScript::_fixup_thread_function_bookkeeping();
+}
+
void GDScriptLanguage::init() {
//populate global constants
int gcc = CoreConstants::get_global_constant_count();
diff --git a/modules/gdscript/gdscript.h b/modules/gdscript/gdscript.h
index 04b0a1d786..9b99f5ca0b 100644
--- a/modules/gdscript/gdscript.h
+++ b/modules/gdscript/gdscript.h
@@ -128,11 +128,16 @@ class GDScript : public Script {
Mutex *mutex = nullptr;
};
static thread_local UpdatableFuncPtr func_ptrs_to_update_thread_local;
+ static thread_local LocalVector<List<UpdatableFuncPtr *>::Element> func_ptrs_to_update_entries_thread_local;
+ static UpdatableFuncPtr *func_ptrs_to_update_main_thread;
List<UpdatableFuncPtr *> func_ptrs_to_update;
+ List<UpdatableFuncPtrElement> func_ptrs_to_update_elems;
Mutex func_ptrs_to_update_mutex;
- UpdatableFuncPtrElement _add_func_ptr_to_update(GDScriptFunction **p_func_ptr_ptr);
- static void _remove_func_ptr_to_update(const UpdatableFuncPtrElement p_func_ptr_element);
+ UpdatableFuncPtrElement *_add_func_ptr_to_update(GDScriptFunction **p_func_ptr_ptr);
+ static void _remove_func_ptr_to_update(const UpdatableFuncPtrElement *p_func_ptr_element);
+
+ static void _fixup_thread_function_bookkeeping();
#ifdef TOOLS_ENABLED
// For static data storage during hot-reloading.
@@ -171,6 +176,7 @@ class GDScript : public Script {
//exported members
String source;
String path;
+ bool path_valid = false; // False if using default path.
StringName local_name; // Inner class identifier or `class_name`.
StringName global_name; // `class_name`.
String fully_qualified_name;
@@ -553,6 +559,10 @@ public:
virtual void add_named_global_constant(const StringName &p_name, const Variant &p_value) override;
virtual void remove_named_global_constant(const StringName &p_name) override;
+ /* MULTITHREAD FUNCTIONS */
+
+ virtual void thread_exit() override;
+
/* DEBUGGER FUNCTIONS */
virtual String debug_get_error() const override;
diff --git a/modules/gdscript/gdscript_cache.cpp b/modules/gdscript/gdscript_cache.cpp
index 26f01ec218..76f4e69ab9 100644
--- a/modules/gdscript/gdscript_cache.cpp
+++ b/modules/gdscript/gdscript_cache.cpp
@@ -364,28 +364,33 @@ void GDScriptCache::remove_static_script(const String &p_fqcn) {
Ref<PackedScene> GDScriptCache::get_packed_scene(const String &p_path, Error &r_error, const String &p_owner) {
MutexLock lock(singleton->mutex);
- if (singleton->packed_scene_cache.has(p_path)) {
- singleton->packed_scene_dependencies[p_path].insert(p_owner);
- return singleton->packed_scene_cache[p_path];
+ String path = p_path;
+ if (path.begins_with("uid://")) {
+ path = ResourceUID::get_singleton()->get_id_path(ResourceUID::get_singleton()->text_to_id(path));
}
- Ref<PackedScene> scene = ResourceCache::get_ref(p_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[p_path] = scene;
- singleton->packed_scene_dependencies[p_path].insert(p_owner);
+ singleton->packed_scene_cache[path] = scene;
+ singleton->packed_scene_dependencies[path].insert(p_owner);
return scene;
}
scene.instantiate();
r_error = OK;
- if (p_path.is_empty()) {
+ if (path.is_empty()) {
r_error = ERR_FILE_BAD_PATH;
return scene;
}
- scene->set_path(p_path);
- singleton->packed_scene_cache[p_path] = scene;
- singleton->packed_scene_dependencies[p_path].insert(p_owner);
+ scene->set_path(path);
+ singleton->packed_scene_cache[path] = scene;
+ singleton->packed_scene_dependencies[path].insert(p_owner);
scene->reload_from_file();
return scene;
diff --git a/modules/gdscript/gdscript_lambda_callable.cpp b/modules/gdscript/gdscript_lambda_callable.cpp
index 547f5607d3..339d1ac08e 100644
--- a/modules/gdscript/gdscript_lambda_callable.cpp
+++ b/modules/gdscript/gdscript_lambda_callable.cpp
@@ -296,5 +296,7 @@ GDScriptLambdaSelfCallable::GDScriptLambdaSelfCallable(Object *p_self, GDScriptF
}
GDScriptLambdaSelfCallable::~GDScriptLambdaSelfCallable() {
- GDScript::_remove_func_ptr_to_update(updatable_func_ptr_element);
+ if (updatable_func_ptr_element) {
+ GDScript::_remove_func_ptr_to_update(updatable_func_ptr_element);
+ }
}
diff --git a/modules/gdscript/gdscript_lambda_callable.h b/modules/gdscript/gdscript_lambda_callable.h
index ee7d547544..d961f18852 100644
--- a/modules/gdscript/gdscript_lambda_callable.h
+++ b/modules/gdscript/gdscript_lambda_callable.h
@@ -45,7 +45,7 @@ class GDScriptLambdaCallable : public CallableCustom {
GDScriptFunction *function = nullptr;
Ref<GDScript> script;
uint32_t h;
- GDScript::UpdatableFuncPtrElement updatable_func_ptr_element;
+ GDScript::UpdatableFuncPtrElement *updatable_func_ptr_element = nullptr;
Vector<Variant> captures;
@@ -72,7 +72,7 @@ class GDScriptLambdaSelfCallable : public CallableCustom {
Ref<RefCounted> reference; // For objects that are RefCounted, keep a reference.
Object *object = nullptr; // For non RefCounted objects, use a direct pointer.
uint32_t h;
- GDScript::UpdatableFuncPtrElement updatable_func_ptr_element;
+ GDScript::UpdatableFuncPtrElement *updatable_func_ptr_element = nullptr;
Vector<Variant> captures;
diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h
index 9067864dfe..4b46b98baa 100644
--- a/modules/gdscript/gdscript_parser.h
+++ b/modules/gdscript/gdscript_parser.h
@@ -178,11 +178,11 @@ public:
bool operator==(const DataType &p_other) const {
if (type_source == UNDETECTED || p_other.type_source == UNDETECTED) {
- return true; // Can be consireded equal for parsing purposes.
+ return true; // Can be considered equal for parsing purposes.
}
if (type_source == INFERRED || p_other.type_source == INFERRED) {
- return true; // Can be consireded equal for parsing purposes.
+ return true; // Can be considered equal for parsing purposes.
}
if (kind != p_other.kind) {
diff --git a/modules/gdscript/gdscript_vm.cpp b/modules/gdscript/gdscript_vm.cpp
index b723ecc185..d31411b26b 100644
--- a/modules/gdscript/gdscript_vm.cpp
+++ b/modules/gdscript/gdscript_vm.cpp
@@ -662,6 +662,14 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
uint32_t op_signature = _code_ptr[ip + 5];
uint32_t actual_signature = (a->get_type() << 8) | (b->get_type());
+#ifdef DEBUG_ENABLED
+ if (op == Variant::OP_DIVIDE || op == Variant::OP_MODULE) {
+ // Don't optimize division and modulo since there's not check for division by zero with validated calls.
+ op_signature = 0xFFFF;
+ _code_ptr[ip + 5] = op_signature;
+ }
+#endif
+
// Check if this is the first run. If so, store the current signature for the optimized path.
if (unlikely(op_signature == 0)) {
static Mutex initializer_mutex;