summaryrefslogtreecommitdiffstats
path: root/modules/gdscript/gdscript.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'modules/gdscript/gdscript.cpp')
-rw-r--r--modules/gdscript/gdscript.cpp311
1 files changed, 162 insertions, 149 deletions
diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp
index a999acd1bd..8e74de4242 100644
--- a/modules/gdscript/gdscript.cpp
+++ b/modules/gdscript/gdscript.cpp
@@ -35,6 +35,7 @@
#include "gdscript_compiler.h"
#include "gdscript_parser.h"
#include "gdscript_rpc_callable.h"
+#include "gdscript_tokenizer_buffer.h"
#include "gdscript_warning.h"
#ifdef TOOLS_ENABLED
@@ -94,7 +95,7 @@ Variant GDScriptNativeClass::_new() {
}
Object *GDScriptNativeClass::instantiate() {
- return ClassDB::instantiate(name);
+ return ClassDB::instantiate_no_placeholders(name);
}
Variant GDScriptNativeClass::callp(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
@@ -162,13 +163,14 @@ GDScriptInstance *GDScript::_create_instance(const Variant **p_args, int p_argco
_super_implicit_constructor(this, instance, r_error);
if (r_error.error != Callable::CallError::CALL_OK) {
+ String error_text = Variant::get_call_error_text(instance->owner, "@implicit_new", nullptr, 0, r_error);
instance->script = Ref<GDScript>();
instance->owner->set_script_instance(nullptr);
{
MutexLock lock(GDScriptLanguage::singleton->mutex);
instances.erase(p_owner);
}
- ERR_FAIL_V_MSG(nullptr, "Error constructing a GDScriptInstance.");
+ ERR_FAIL_V_MSG(nullptr, "Error constructing a GDScriptInstance: " + error_text);
}
if (p_argcount < 0) {
@@ -179,13 +181,14 @@ GDScriptInstance *GDScript::_create_instance(const Variant **p_args, int p_argco
if (initializer != nullptr) {
initializer->call(instance, p_args, p_argcount, r_error);
if (r_error.error != Callable::CallError::CALL_OK) {
+ String error_text = Variant::get_call_error_text(instance->owner, "_init", p_args, p_argcount, r_error);
instance->script = Ref<GDScript>();
instance->owner->set_script_instance(nullptr);
{
MutexLock lock(GDScriptLanguage::singleton->mutex);
instances.erase(p_owner);
}
- ERR_FAIL_V_MSG(nullptr, "Error constructing a GDScriptInstance.");
+ ERR_FAIL_V_MSG(nullptr, "Error constructing a GDScriptInstance: " + error_text);
}
}
//@TODO make thread safe
@@ -351,6 +354,21 @@ bool GDScript::has_static_method(const StringName &p_method) const {
return member_functions.has(p_method) && member_functions[p_method]->is_static();
}
+int GDScript::get_script_method_argument_count(const StringName &p_method, bool *r_is_valid) const {
+ HashMap<StringName, GDScriptFunction *>::ConstIterator E = member_functions.find(p_method);
+ if (!E) {
+ if (r_is_valid) {
+ *r_is_valid = false;
+ }
+ return 0;
+ }
+
+ if (r_is_valid) {
+ *r_is_valid = true;
+ }
+ return E->value->get_argument_count();
+}
+
MethodInfo GDScript::get_method_info(const StringName &p_method) const {
HashMap<StringName, GDScriptFunction *>::ConstIterator E = member_functions.find(p_method);
if (!E) {
@@ -738,7 +756,12 @@ Error GDScript::reload(bool p_keep_state) {
valid = false;
GDScriptParser parser;
- Error err = parser.parse(source, path, false);
+ Error err;
+ if (!binary_tokens.is_empty()) {
+ err = parser.parse_binary(binary_tokens, path);
+ } else {
+ err = parser.parse(source, path, false);
+ }
if (err) {
if (EngineDebugger::is_active()) {
GDScriptLanguage::get_singleton()->debug_break_parse(_get_debug_path(), parser.get_errors().front()->get().line, "Parser Error: " + parser.get_errors().front()->get().message);
@@ -1048,6 +1071,19 @@ Error GDScript::load_source_code(const String &p_path) {
return OK;
}
+void GDScript::set_binary_tokens_source(const Vector<uint8_t> &p_binary_tokens) {
+ binary_tokens = p_binary_tokens;
+}
+
+const Vector<uint8_t> &GDScript::get_binary_tokens_source() const {
+ return binary_tokens;
+}
+
+Vector<uint8_t> GDScript::get_as_binary_tokens() const {
+ GDScriptTokenizerBuffer tokenizer;
+ return tokenizer.parse_code_string(source, GDScriptTokenizerBuffer::COMPRESS_NONE);
+}
+
const HashMap<StringName, GDScriptFunction *> &GDScript::debug_get_member_functions() const {
return member_functions;
}
@@ -1149,7 +1185,7 @@ GDScript *GDScript::get_root_script() {
RBSet<GDScript *> GDScript::get_dependencies() {
RBSet<GDScript *> dependencies;
- _get_dependencies(dependencies, this);
+ _collect_dependencies(dependencies, this);
dependencies.erase(this);
return dependencies;
@@ -1255,52 +1291,55 @@ GDScript *GDScript::_get_gdscript_from_variant(const Variant &p_variant) {
return Object::cast_to<GDScript>(obj);
}
-void GDScript::_get_dependencies(RBSet<GDScript *> &p_dependencies, const GDScript *p_except) {
+void GDScript::_collect_function_dependencies(GDScriptFunction *p_func, RBSet<GDScript *> &p_dependencies, const GDScript *p_except) {
+ if (p_func == nullptr) {
+ return;
+ }
+ for (GDScriptFunction *lambda : p_func->lambdas) {
+ _collect_function_dependencies(lambda, p_dependencies, p_except);
+ }
+ for (const Variant &V : p_func->constants) {
+ GDScript *scr = _get_gdscript_from_variant(V);
+ if (scr != nullptr && scr != p_except) {
+ scr->_collect_dependencies(p_dependencies, p_except);
+ }
+ }
+}
+
+void GDScript::_collect_dependencies(RBSet<GDScript *> &p_dependencies, const GDScript *p_except) {
if (p_dependencies.has(this)) {
return;
}
- p_dependencies.insert(this);
+ if (this != p_except) {
+ p_dependencies.insert(this);
+ }
for (const KeyValue<StringName, GDScriptFunction *> &E : member_functions) {
- if (E.value == nullptr) {
- continue;
- }
- for (const Variant &V : E.value->constants) {
- GDScript *scr = _get_gdscript_from_variant(V);
- if (scr != nullptr && scr != p_except) {
- scr->_get_dependencies(p_dependencies, p_except);
- }
- }
+ _collect_function_dependencies(E.value, p_dependencies, p_except);
}
if (implicit_initializer) {
- for (const Variant &V : implicit_initializer->constants) {
- GDScript *scr = _get_gdscript_from_variant(V);
- if (scr != nullptr && scr != p_except) {
- scr->_get_dependencies(p_dependencies, p_except);
- }
- }
+ _collect_function_dependencies(implicit_initializer, p_dependencies, p_except);
}
if (implicit_ready) {
- for (const Variant &V : implicit_ready->constants) {
- GDScript *scr = _get_gdscript_from_variant(V);
- if (scr != nullptr && scr != p_except) {
- scr->_get_dependencies(p_dependencies, p_except);
- }
- }
+ _collect_function_dependencies(implicit_ready, p_dependencies, p_except);
+ }
+
+ if (static_initializer) {
+ _collect_function_dependencies(static_initializer, p_dependencies, p_except);
}
for (KeyValue<StringName, Ref<GDScript>> &E : subclasses) {
if (E.value != p_except) {
- E.value->_get_dependencies(p_dependencies, p_except);
+ E.value->_collect_dependencies(p_dependencies, p_except);
}
}
for (const KeyValue<StringName, Variant> &E : constants) {
GDScript *scr = _get_gdscript_from_variant(E.value);
if (scr != nullptr && scr != p_except) {
- scr->_get_dependencies(p_dependencies, p_except);
+ scr->_collect_dependencies(p_dependencies, p_except);
}
}
}
@@ -1381,51 +1420,50 @@ String GDScript::debug_get_script_name(const Ref<Script> &p_script) {
}
#endif
-GDScript::UpdatableFuncPtr GDScript::func_ptrs_to_update_main_thread;
-thread_local GDScript::UpdatableFuncPtr *GDScript::func_ptrs_to_update_thread_local = nullptr;
-
-GDScript::UpdatableFuncPtrElement GDScript::_add_func_ptr_to_update(GDScriptFunction **p_func_ptr_ptr) {
- UpdatableFuncPtrElement result = {};
-
- {
- 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.func_ptr = func_ptrs_to_update_thread_local;
-
- if (likely(func_ptrs_to_update_thread_local->initialized)) {
- return result;
- }
-
- func_ptrs_to_update_thread_local->initialized = true;
+String GDScript::canonicalize_path(const String &p_path) {
+ if (p_path.get_extension() == "gdc") {
+ return p_path.get_basename() + ".gd";
}
+ return p_path;
+}
- MutexLock lock(func_ptrs_to_update_mutex);
- func_ptrs_to_update.push_back(func_ptrs_to_update_thread_local);
- func_ptrs_to_update_thread_local->rc++;
+GDScript::UpdatableFuncPtr::UpdatableFuncPtr(GDScriptFunction *p_function) {
+ if (p_function == nullptr) {
+ return;
+ }
- return result;
-}
+ ptr = p_function;
+ script = ptr->get_script();
+ ERR_FAIL_NULL(script);
-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.func_ptr);
- MutexLock lock(p_func_ptr_element.func_ptr->mutex);
- p_func_ptr_element.element->erase();
+ MutexLock script_lock(script->func_ptrs_to_update_mutex);
+ list_element = script->func_ptrs_to_update.push_back(this);
}
-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.
+GDScript::UpdatableFuncPtr::~UpdatableFuncPtr() {
+ ERR_FAIL_NULL(script);
- DEV_ASSERT(!Thread::is_main_thread());
+ if (list_element) {
+ MutexLock script_lock(script->func_ptrs_to_update_mutex);
+ list_element->erase();
+ list_element = nullptr;
+ }
+}
- MutexLock lock(func_ptrs_to_update_main_thread.mutex);
- MutexLock lock2(func_ptrs_to_update_thread_local->mutex);
+void GDScript::_recurse_replace_function_ptrs(const HashMap<GDScriptFunction *, GDScriptFunction *> &p_replacements) const {
+ MutexLock lock(func_ptrs_to_update_mutex);
+ for (UpdatableFuncPtr *updatable : func_ptrs_to_update) {
+ HashMap<GDScriptFunction *, GDScriptFunction *>::ConstIterator replacement = p_replacements.find(updatable->ptr);
+ if (replacement) {
+ updatable->ptr = replacement->value;
+ } else {
+ // Probably a lambda from another reload, ignore.
+ updatable->ptr = nullptr;
+ }
+ }
- while (!func_ptrs_to_update_thread_local->ptrs.is_empty()) {
- List<GDScriptFunction **>::Element *E = func_ptrs_to_update_thread_local->ptrs.front();
- E->transfer_to_back(&func_ptrs_to_update_main_thread.ptrs);
- func_ptrs_to_update_thread_local->transferred = true;
+ for (HashMap<StringName, Ref<GDScript>>::ConstIterator subscript = subclasses.begin(); subscript; ++subscript) {
+ subscript->value->_recurse_replace_function_ptrs(p_replacements);
}
}
@@ -1447,30 +1485,9 @@ void GDScript::clear(ClearData *p_clear_data) {
}
{
- MutexLock outer_lock(func_ptrs_to_update_mutex);
+ MutexLock lock(func_ptrs_to_update_mutex);
for (UpdatableFuncPtr *updatable : func_ptrs_to_update) {
- bool destroy = false;
- {
- MutexLock inner_lock(updatable->mutex);
- if (updatable->transferred) {
- func_ptrs_to_update_main_thread.mutex.lock();
- }
- for (GDScriptFunction **func_ptr_ptr : updatable->ptrs) {
- *func_ptr_ptr = nullptr;
- }
- DEV_ASSERT(updatable->rc != 0);
- updatable->rc--;
- if (updatable->rc == 0) {
- destroy = true;
- }
- if (updatable->transferred) {
- func_ptrs_to_update_main_thread.mutex.unlock();
- }
- }
- if (destroy) {
- DEV_ASSERT(updatable != &func_ptrs_to_update_main_thread);
- memdelete(updatable);
- }
+ updatable->ptr = nullptr;
}
}
@@ -1543,6 +1560,13 @@ GDScript::~GDScript() {
}
destructing = true;
+ if (is_print_verbose_enabled()) {
+ MutexLock lock(func_ptrs_to_update_mutex);
+ if (!func_ptrs_to_update.is_empty()) {
+ print_line(vformat("GDScript: %d orphaned lambdas becoming invalid at destruction of script '%s'.", func_ptrs_to_update.size(), fully_qualified_name));
+ }
+ }
+
clear();
{
@@ -1687,7 +1711,7 @@ bool GDScriptInstance::get(const StringName &p_name, Variant &r_ret) const {
{
HashMap<StringName, MethodInfo>::ConstIterator E = sptr->_signals.find(p_name);
if (E) {
- r_ret = Signal(this->owner, E->key);
+ r_ret = Signal(owner, E->key);
return true;
}
}
@@ -1696,9 +1720,9 @@ bool GDScriptInstance::get(const StringName &p_name, Variant &r_ret) const {
HashMap<StringName, GDScriptFunction *>::ConstIterator E = sptr->member_functions.find(p_name);
if (E) {
if (sptr->rpc_config.has(p_name)) {
- r_ret = Callable(memnew(GDScriptRPCCallable(this->owner, E->key)));
+ r_ret = Callable(memnew(GDScriptRPCCallable(owner, E->key)));
} else {
- r_ret = Callable(this->owner, E->key);
+ r_ret = Callable(owner, E->key);
}
return true;
}
@@ -1907,6 +1931,25 @@ bool GDScriptInstance::has_method(const StringName &p_method) const {
return false;
}
+int GDScriptInstance::get_method_argument_count(const StringName &p_method, bool *r_is_valid) const {
+ const GDScript *sptr = script.ptr();
+ while (sptr) {
+ HashMap<StringName, GDScriptFunction *>::ConstIterator E = sptr->member_functions.find(p_method);
+ if (E) {
+ if (r_is_valid) {
+ *r_is_valid = true;
+ }
+ return E->value->get_argument_count();
+ }
+ sptr = sptr->_base;
+ }
+
+ if (r_is_valid) {
+ *r_is_valid = false;
+ }
+ return 0;
+}
+
Variant GDScriptInstance::callp(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
GDScript *sptr = script.ptr();
if (unlikely(p_method == SNAME("_ready"))) {
@@ -2091,33 +2134,6 @@ void GDScriptLanguage::remove_named_global_constant(const StringName &p_name) {
named_globals.erase(p_name);
}
-void GDScriptLanguage::thread_enter() {
- GDScript::func_ptrs_to_update_thread_local = memnew(GDScript::UpdatableFuncPtr);
-}
-
-void GDScriptLanguage::thread_exit() {
- // This thread may have been created before GDScript was up
- // (which also means it can't have run any GDScript code at all).
- if (!GDScript::func_ptrs_to_update_thread_local) {
- return;
- }
-
- GDScript::_fixup_thread_function_bookkeeping();
-
- bool destroy = false;
- {
- MutexLock lock(GDScript::func_ptrs_to_update_thread_local->mutex);
- DEV_ASSERT(GDScript::func_ptrs_to_update_thread_local->rc != 0);
- GDScript::func_ptrs_to_update_thread_local->rc--;
- if (GDScript::func_ptrs_to_update_thread_local->rc == 0) {
- destroy = true;
- }
- }
- if (destroy) {
- memdelete(GDScript::func_ptrs_to_update_thread_local);
- }
-}
-
void GDScriptLanguage::init() {
//populate global constants
int gcc = CoreConstants::get_global_constant_count();
@@ -2150,8 +2166,6 @@ void GDScriptLanguage::init() {
_add_global(E.name, E.ptr);
}
- GDScript::func_ptrs_to_update_thread_local = &GDScript::func_ptrs_to_update_main_thread;
-
#ifdef TESTS_ENABLED
GDScriptTests::GDScriptTestRunner::handle_cmdline();
#endif
@@ -2201,13 +2215,11 @@ void GDScriptLanguage::finish() {
}
script_list.clear();
function_list.clear();
-
- DEV_ASSERT(GDScript::func_ptrs_to_update_main_thread.rc == 1);
}
void GDScriptLanguage::profiling_start() {
#ifdef DEBUG_ENABLED
- MutexLock lock(this->mutex);
+ MutexLock lock(mutex);
SelfList<GDScriptFunction> *elem = function_list.first();
while (elem) {
@@ -2238,7 +2250,7 @@ void GDScriptLanguage::profiling_set_save_native_calls(bool p_enable) {
void GDScriptLanguage::profiling_stop() {
#ifdef DEBUG_ENABLED
- MutexLock lock(this->mutex);
+ MutexLock lock(mutex);
profiling = false;
#endif
@@ -2248,7 +2260,7 @@ int GDScriptLanguage::profiling_get_accumulated_data(ProfilingInfo *p_info_arr,
int current = 0;
#ifdef DEBUG_ENABLED
- MutexLock lock(this->mutex);
+ MutexLock lock(mutex);
profiling_collate_native_call_data(true);
SelfList<GDScriptFunction> *elem = function_list.first();
@@ -2286,7 +2298,7 @@ int GDScriptLanguage::profiling_get_frame_data(ProfilingInfo *p_info_arr, int p_
int current = 0;
#ifdef DEBUG_ENABLED
- MutexLock lock(this->mutex);
+ MutexLock lock(mutex);
profiling_collate_native_call_data(false);
SelfList<GDScriptFunction> *elem = function_list.first();
@@ -2373,14 +2385,13 @@ struct GDScriptDepSort {
void GDScriptLanguage::reload_all_scripts() {
#ifdef DEBUG_ENABLED
print_verbose("GDScript: Reloading all scripts");
- List<Ref<GDScript>> scripts;
+ Array scripts;
{
- MutexLock lock(this->mutex);
+ MutexLock lock(mutex);
SelfList<GDScript> *elem = script_list.first();
while (elem) {
- // Scripts will reload all subclasses, so only reload root scripts.
- if (elem->self()->is_root_script() && elem->self()->get_path().is_resource_file()) {
+ if (elem->self()->get_path().is_resource_file()) {
print_verbose("GDScript: Found: " + elem->self()->get_path());
scripts.push_back(Ref<GDScript>(elem->self())); //cast to gdscript to avoid being erased by accident
}
@@ -2401,24 +2412,16 @@ void GDScriptLanguage::reload_all_scripts() {
#endif
}
- //as scripts are going to be reloaded, must proceed without locking here
-
- scripts.sort_custom<GDScriptDepSort>(); //update in inheritance dependency order
-
- for (Ref<GDScript> &scr : scripts) {
- print_verbose("GDScript: Reloading: " + scr->get_path());
- scr->load_source_code(scr->get_path());
- scr->reload(true);
- }
+ reload_scripts(scripts, true);
#endif
}
-void GDScriptLanguage::reload_tool_script(const Ref<Script> &p_script, bool p_soft_reload) {
+void GDScriptLanguage::reload_scripts(const Array &p_scripts, bool p_soft_reload) {
#ifdef DEBUG_ENABLED
List<Ref<GDScript>> scripts;
{
- MutexLock lock(this->mutex);
+ MutexLock lock(mutex);
SelfList<GDScript> *elem = script_list.first();
while (elem) {
@@ -2439,7 +2442,7 @@ void GDScriptLanguage::reload_tool_script(const Ref<Script> &p_script, bool p_so
scripts.sort_custom<GDScriptDepSort>(); //update in inheritance dependency order
for (Ref<GDScript> &scr : scripts) {
- bool reload = scr == p_script || to_reload.has(scr->get_base());
+ bool reload = p_scripts.has(scr) || to_reload.has(scr->get_base());
if (!reload) {
continue;
@@ -2462,7 +2465,7 @@ void GDScriptLanguage::reload_tool_script(const Ref<Script> &p_script, bool p_so
}
}
-//same thing for placeholders
+ //same thing for placeholders
#ifdef TOOLS_ENABLED
while (scr->placeholders.size()) {
@@ -2490,6 +2493,8 @@ void GDScriptLanguage::reload_tool_script(const Ref<Script> &p_script, bool p_so
for (KeyValue<Ref<GDScript>, HashMap<ObjectID, List<Pair<StringName, Variant>>>> &E : to_reload) {
Ref<GDScript> scr = E.key;
+ print_verbose("GDScript: Reloading: " + scr->get_path());
+ scr->load_source_code(scr->get_path());
scr->reload(p_soft_reload);
//restore state if saved
@@ -2537,12 +2542,18 @@ void GDScriptLanguage::reload_tool_script(const Ref<Script> &p_script, bool p_so
#endif
}
+void GDScriptLanguage::reload_tool_script(const Ref<Script> &p_script, bool p_soft_reload) {
+ Array scripts;
+ scripts.push_back(p_script);
+ reload_scripts(scripts, p_soft_reload);
+}
+
void GDScriptLanguage::frame() {
calls = 0;
#ifdef DEBUG_ENABLED
if (profiling) {
- MutexLock lock(this->mutex);
+ MutexLock lock(mutex);
SelfList<GDScriptFunction> *elem = function_list.first();
while (elem) {
@@ -2627,7 +2638,7 @@ void GDScriptLanguage::get_reserved_words(List<String> *p_words) const {
}
}
-bool GDScriptLanguage::is_control_flow_keyword(String p_keyword) const {
+bool GDScriptLanguage::is_control_flow_keyword(const String &p_keyword) const {
// Please keep alphabetical order.
return p_keyword == "break" ||
p_keyword == "continue" ||
@@ -2840,11 +2851,12 @@ Ref<GDScript> GDScriptLanguage::get_script_by_fully_qualified_name(const String
Ref<Resource> ResourceFormatLoaderGDScript::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, CacheMode p_cache_mode) {
Error err;
- Ref<GDScript> scr = GDScriptCache::get_full_script(p_path, err, "", p_cache_mode == CACHE_MODE_IGNORE);
+ bool ignoring = p_cache_mode == CACHE_MODE_IGNORE || p_cache_mode == CACHE_MODE_IGNORE_DEEP;
+ Ref<GDScript> scr = GDScriptCache::get_full_script(p_original_path, err, "", ignoring);
if (err && scr.is_valid()) {
// If !scr.is_valid(), the error was likely from scr->load_source_code(), which already generates an error.
- ERR_PRINT_ED(vformat(R"(Failed to load script "%s" with error "%s".)", p_path, error_names[err]));
+ ERR_PRINT_ED(vformat(R"(Failed to load script "%s" with error "%s".)", p_original_path, error_names[err]));
}
if (r_error) {
@@ -2857,6 +2869,7 @@ Ref<Resource> ResourceFormatLoaderGDScript::load(const String &p_path, const Str
void ResourceFormatLoaderGDScript::get_recognized_extensions(List<String> *p_extensions) const {
p_extensions->push_back("gd");
+ p_extensions->push_back("gdc");
}
bool ResourceFormatLoaderGDScript::handles_type(const String &p_type) const {
@@ -2865,7 +2878,7 @@ bool ResourceFormatLoaderGDScript::handles_type(const String &p_type) const {
String ResourceFormatLoaderGDScript::get_resource_type(const String &p_path) const {
String el = p_path.get_extension().to_lower();
- if (el == "gd") {
+ if (el == "gd" || el == "gdc") {
return "GDScript";
}
return "";