summaryrefslogtreecommitdiff
path: root/modules/gdscript
diff options
context:
space:
mode:
Diffstat (limited to 'modules/gdscript')
-rw-r--r--modules/gdscript/editor/gdscript_highlighter.cpp2
-rw-r--r--modules/gdscript/gdscript.cpp140
-rw-r--r--modules/gdscript/gdscript.h7
-rw-r--r--modules/gdscript/gdscript_analyzer.cpp10
-rw-r--r--modules/gdscript/gdscript_cache.cpp40
-rw-r--r--modules/gdscript/gdscript_cache.h3
-rw-r--r--modules/gdscript/gdscript_compiler.cpp319
-rw-r--r--modules/gdscript/gdscript_compiler.h8
-rw-r--r--modules/gdscript/gdscript_editor.cpp61
-rw-r--r--modules/gdscript/gdscript_parser.cpp7
-rw-r--r--modules/gdscript/gdscript_parser.h2
-rw-r--r--modules/gdscript/tests/gdscript_test_runner.cpp1
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/external_inner_base.gd4
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/external_inner_base.out3
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/inner_base.gd18
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/inner_base.out4
16 files changed, 342 insertions, 287 deletions
diff --git a/modules/gdscript/editor/gdscript_highlighter.cpp b/modules/gdscript/editor/gdscript_highlighter.cpp
index 420401cb79..0ebdbf090d 100644
--- a/modules/gdscript/editor/gdscript_highlighter.cpp
+++ b/modules/gdscript/editor/gdscript_highlighter.cpp
@@ -322,7 +322,7 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_l
}
String word = str.substr(j, to - j);
- Color col = Color();
+ Color col;
if (global_functions.has(word)) {
// "assert" and "preload" are reserved, so highlight even if not followed by a bracket.
if (word == GDScriptTokenizer::get_token_name(GDScriptTokenizer::Token::ASSERT) || word == GDScriptTokenizer::get_token_name(GDScriptTokenizer::Token::PRELOAD)) {
diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp
index b4da9c1224..bd6cef0b6e 100644
--- a/modules/gdscript/gdscript.cpp
+++ b/modules/gdscript/gdscript.cpp
@@ -629,10 +629,6 @@ void GDScript::_update_doc() {
}
}
- for (KeyValue<StringName, Ref<GDScript>> &E : subclasses) {
- E.value->_update_doc();
- }
-
_add_doc(doc);
}
#endif
@@ -674,36 +670,14 @@ bool GDScript::_update_exports(bool *r_err, bool p_recursive_call, PlaceHolderSc
base_cache = Ref<GDScript>();
}
- if (c->extends_used) {
- String ext_path = "";
- if (String(c->extends_path) != "" && String(c->extends_path) != get_path()) {
- ext_path = c->extends_path;
- if (ext_path.is_relative_path()) {
- String base_path = get_path();
- if (base_path.is_empty() || base_path.is_relative_path()) {
- ERR_PRINT(("Could not resolve relative path for parent class: " + ext_path).utf8().get_data());
- } else {
- ext_path = base_path.get_base_dir().path_join(ext_path);
- }
- }
- } else if (c->extends.size() != 0) {
- const StringName &base_class = c->extends[0];
-
- if (ScriptServer::is_global_class(base_class)) {
- ext_path = ScriptServer::get_global_class_path(base_class);
- }
- }
-
- if (!ext_path.is_empty()) {
- if (ext_path != get_path()) {
- Ref<GDScript> bf = ResourceLoader::load(ext_path);
-
- if (bf.is_valid()) {
- base_cache = bf;
- bf->inheriters_cache.insert(get_instance_id());
- }
- } else {
- ERR_PRINT(("Path extending itself in " + ext_path).utf8().get_data());
+ GDScriptParser::DataType base_type = parser.get_tree()->base_type;
+ if (base_type.kind == GDScriptParser::DataType::CLASS) {
+ Ref<GDScript> bf = GDScriptCache::get_full_script(base_type.script_path, err, path);
+ if (err == OK) {
+ bf = Ref<GDScript>(bf->find_class(base_type.class_type->fqcn));
+ if (bf.is_valid()) {
+ base_cache = bf;
+ bf->inheriters_cache.insert(get_instance_id());
}
}
}
@@ -825,13 +799,6 @@ void GDScript::update_exports() {
#endif
}
-void GDScript::_set_subclass_path(Ref<GDScript> &p_sc, const String &p_path) {
- p_sc->path = p_path;
- for (KeyValue<StringName, Ref<GDScript>> &E : p_sc->subclasses) {
- _set_subclass_path(E.value, p_path);
- }
-}
-
String GDScript::_get_debug_path() const {
if (is_built_in() && !get_name().is_empty()) {
return get_name() + " (" + get_path() + ")";
@@ -860,7 +827,7 @@ Error GDScript::reload(bool p_keep_state) {
basedir = basedir.get_base_dir();
}
-// Loading a template, don't parse.
+ // Loading a template, don't parse.
#ifdef TOOLS_ENABLED
if (EditorPaths::get_singleton() && basedir.begins_with(EditorPaths::get_singleton()->get_project_script_templates_dir())) {
return OK;
@@ -913,10 +880,6 @@ Error GDScript::reload(bool p_keep_state) {
GDScriptCompiler compiler;
err = compiler.compile(&parser, this, p_keep_state);
-#ifdef TOOLS_ENABLED
- _update_doc();
-#endif
-
if (err) {
if (can_run) {
if (EngineDebugger::is_active()) {
@@ -937,14 +900,6 @@ Error GDScript::reload(bool p_keep_state) {
}
#endif
- valid = true;
-
- for (KeyValue<StringName, Ref<GDScript>> &E : subclasses) {
- _set_subclass_path(E.value, path);
- }
-
- _init_rpc_methods_properties();
-
return OK;
}
@@ -1051,8 +1006,13 @@ Error GDScript::load_byte_code(const String &p_path) {
}
void GDScript::set_path(const String &p_path, bool p_take_over) {
- Script::set_path(p_path, p_take_over);
+ if (is_root_script()) {
+ Script::set_path(p_path, p_take_over);
+ }
this->path = p_path;
+ for (KeyValue<StringName, Ref<GDScript>> &kv : subclasses) {
+ kv.value->set_path(p_path, p_take_over);
+ }
}
Error GDScript::load_source_code(const String &p_path) {
@@ -1127,6 +1087,52 @@ bool GDScript::inherits_script(const Ref<Script> &p_script) const {
return false;
}
+GDScript *GDScript::find_class(const String &p_qualified_name) {
+ String first = p_qualified_name.get_slice("::", 0);
+
+ GDScript *result = nullptr;
+ if (first.is_empty() || first == name) {
+ result = this;
+ } else if (first == get_root_script()->path) {
+ result = get_root_script();
+ } else if (HashMap<StringName, Ref<GDScript>>::Iterator E = subclasses.find(first)) {
+ result = E->value.ptr();
+ } else if (_owner != nullptr) {
+ // Check parent scope.
+ return _owner->find_class(p_qualified_name);
+ }
+
+ int name_count = p_qualified_name.get_slice_count("::");
+ for (int i = 1; result != nullptr && i < name_count; i++) {
+ String current_name = p_qualified_name.get_slice("::", i);
+ if (HashMap<StringName, Ref<GDScript>>::Iterator E = result->subclasses.find(current_name)) {
+ result = E->value.ptr();
+ } else {
+ // Couldn't find inner class.
+ return nullptr;
+ }
+ }
+
+ return result;
+}
+
+bool GDScript::is_subclass(const GDScript *p_script) {
+ String fqn = p_script->fully_qualified_name;
+ if (!fqn.is_empty() && fqn != fully_qualified_name && fqn.begins_with(fully_qualified_name)) {
+ String fqn_rest = fqn.substr(fully_qualified_name.length());
+ return find_class(fqn_rest) == p_script;
+ }
+ return false;
+}
+
+GDScript *GDScript::get_root_script() {
+ GDScript *result = this;
+ while (result->_owner) {
+ result = result->_owner;
+ }
+ return result;
+}
+
bool GDScript::has_script_signal(const StringName &p_signal) const {
if (_signals.has(p_signal)) {
return true;
@@ -1238,25 +1244,11 @@ void GDScript::_init_rpc_methods_properties() {
rpc_config = base->rpc_config.duplicate();
}
- GDScript *cscript = this;
- HashMap<StringName, Ref<GDScript>>::Iterator sub_E = subclasses.begin();
- while (cscript) {
- // RPC Methods
- for (KeyValue<StringName, GDScriptFunction *> &E : cscript->member_functions) {
- Variant config = E.value->get_rpc_config();
- if (config.get_type() != Variant::NIL) {
- rpc_config[E.value->get_name()] = config;
- }
- }
-
- if (cscript != this) {
- ++sub_E;
- }
-
- if (sub_E) {
- cscript = sub_E->value.ptr();
- } else {
- cscript = nullptr;
+ // RPC Methods
+ for (KeyValue<StringName, GDScriptFunction *> &E : member_functions) {
+ Variant config = E.value->get_rpc_config();
+ if (config.get_type() != Variant::NIL) {
+ rpc_config[E.value->get_name()] = config;
}
}
}
diff --git a/modules/gdscript/gdscript.h b/modules/gdscript/gdscript.h
index 72ad890fbc..61600b1258 100644
--- a/modules/gdscript/gdscript.h
+++ b/modules/gdscript/gdscript.h
@@ -137,7 +137,6 @@ class GDScript : public Script {
void _super_implicit_constructor(GDScript *p_script, GDScriptInstance *p_instance, Callable::CallError &r_error);
GDScriptInstance *_create_instance(const Variant **p_args, int p_argcount, Object *p_owner, bool p_is_ref_counted, Callable::CallError &r_error);
- void _set_subclass_path(Ref<GDScript> &p_sc, const String &p_path);
String _get_debug_path() const;
#ifdef TOOLS_ENABLED
@@ -178,6 +177,11 @@ public:
bool inherits_script(const Ref<Script> &p_script) const override;
+ GDScript *find_class(const String &p_qualified_name);
+ bool is_subclass(const GDScript *p_script);
+ GDScript *get_root_script();
+ bool is_root_script() const { return _owner == nullptr; }
+ String get_fully_qualified_name() const { return fully_qualified_name; }
const HashMap<StringName, Ref<GDScript>> &get_subclasses() const { return subclasses; }
const HashMap<StringName, Variant> &get_constants() const { return constants; }
const HashSet<StringName> &get_members() const { return members; }
@@ -223,7 +227,6 @@ public:
virtual Error reload(bool p_keep_state = false) override;
virtual void set_path(const String &p_path, bool p_take_over = false) override;
- void set_script_path(const String &p_path) { path = p_path; } //because subclasses need a path too...
Error load_source_code(const String &p_path);
Error load_byte_code(const String &p_path);
diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp
index 704dda8045..8da77b9e5b 100644
--- a/modules/gdscript/gdscript_analyzer.cpp
+++ b/modules/gdscript/gdscript_analyzer.cpp
@@ -213,16 +213,6 @@ Error GDScriptAnalyzer::resolve_inheritance(GDScriptParser::ClassNode *p_class,
return OK;
}
- if (p_class == parser->head) {
- if (p_class->identifier) {
- p_class->fqcn = p_class->identifier->name;
- } else {
- p_class->fqcn = parser->script_path;
- }
- } else {
- p_class->fqcn = p_class->outer->fqcn + "::" + String(p_class->identifier->name);
- }
-
if (p_class->identifier) {
StringName class_name = p_class->identifier->name;
if (class_exists(class_name)) {
diff --git a/modules/gdscript/gdscript_cache.cpp b/modules/gdscript/gdscript_cache.cpp
index 271296c2f9..03a101b9fc 100644
--- a/modules/gdscript/gdscript_cache.cpp
+++ b/modules/gdscript/gdscript_cache.cpp
@@ -34,6 +34,7 @@
#include "core/templates/vector.h"
#include "gdscript.h"
#include "gdscript_analyzer.h"
+#include "gdscript_compiler.h"
#include "gdscript_parser.h"
bool GDScriptParserRef::is_valid() const {
@@ -161,7 +162,7 @@ String GDScriptCache::get_source_code(const String &p_path) {
return source;
}
-Ref<GDScript> GDScriptCache::get_shallow_script(const String &p_path, const String &p_owner) {
+Ref<GDScript> GDScriptCache::get_shallow_script(const String &p_path, Error &r_error, const String &p_owner) {
MutexLock lock(singleton->lock);
if (!p_owner.is_empty()) {
singleton->dependencies[p_owner].insert(p_path);
@@ -173,11 +174,16 @@ Ref<GDScript> GDScriptCache::get_shallow_script(const String &p_path, const Stri
return singleton->shallow_gdscript_cache[p_path];
}
+ Ref<GDScriptParserRef> parser_ref = get_parser(p_path, GDScriptParserRef::PARSED, r_error);
+ if (r_error != OK) {
+ return Ref<GDScript>();
+ }
+
Ref<GDScript> script;
script.instantiate();
script->set_path(p_path, true);
- script->set_script_path(p_path);
script->load_source_code(p_path);
+ GDScriptCompiler::make_scripts(script.ptr(), parser_ref->get_parser()->get_tree(), true);
singleton->shallow_gdscript_cache[p_path] = script.ptr();
return script;
@@ -200,17 +206,21 @@ Ref<GDScript> GDScriptCache::get_full_script(const String &p_path, Error &r_erro
}
if (script.is_null()) {
- script = get_shallow_script(p_path);
- ERR_FAIL_COND_V(script.is_null(), Ref<GDScript>());
+ script = get_shallow_script(p_path, r_error);
+ if (r_error) {
+ return script;
+ }
}
- r_error = script->load_source_code(p_path);
+ if (p_update_from_disk) {
+ r_error = script->load_source_code(p_path);
+ }
if (r_error) {
return script;
}
- r_error = script->reload();
+ r_error = script->reload(true);
if (r_error) {
return script;
}
@@ -221,9 +231,25 @@ Ref<GDScript> GDScriptCache::get_full_script(const String &p_path, Error &r_erro
return script;
}
+Ref<GDScript> GDScriptCache::get_cached_script(const String &p_path) {
+ MutexLock lock(singleton->lock);
+
+ if (singleton->full_gdscript_cache.has(p_path)) {
+ return singleton->full_gdscript_cache[p_path];
+ }
+
+ if (singleton->shallow_gdscript_cache.has(p_path)) {
+ return singleton->shallow_gdscript_cache[p_path];
+ }
+
+ return Ref<GDScript>();
+}
+
Error GDScriptCache::finish_compiling(const String &p_owner) {
+ MutexLock lock(singleton->lock);
+
// Mark this as compiled.
- Ref<GDScript> script = get_shallow_script(p_owner);
+ Ref<GDScript> script = get_cached_script(p_owner);
singleton->full_gdscript_cache[p_owner] = script.ptr();
singleton->shallow_gdscript_cache.erase(p_owner);
diff --git a/modules/gdscript/gdscript_cache.h b/modules/gdscript/gdscript_cache.h
index 3d111ea229..fcd240ba8d 100644
--- a/modules/gdscript/gdscript_cache.h
+++ b/modules/gdscript/gdscript_cache.h
@@ -87,8 +87,9 @@ class GDScriptCache {
public:
static Ref<GDScriptParserRef> get_parser(const String &p_path, GDScriptParserRef::Status status, Error &r_error, const String &p_owner = String());
static String get_source_code(const String &p_path);
- static Ref<GDScript> get_shallow_script(const String &p_path, const String &p_owner = String());
+ static Ref<GDScript> get_shallow_script(const String &p_path, Error &r_error, const String &p_owner = String());
static Ref<GDScript> get_full_script(const String &p_path, Error &r_error, const String &p_owner = String(), bool p_update_from_disk = false);
+ static Ref<GDScript> get_cached_script(const String &p_path);
static Error finish_compiling(const String &p_owner);
GDScriptCache();
diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp
index 7acd1cdb96..aa1356d8c0 100644
--- a/modules/gdscript/gdscript_compiler.cpp
+++ b/modules/gdscript/gdscript_compiler.cpp
@@ -80,7 +80,7 @@ void GDScriptCompiler::_set_error(const String &p_error, const GDScriptParser::N
}
}
-GDScriptDataType GDScriptCompiler::_gdtype_from_datatype(const GDScriptParser::DataType &p_datatype, GDScript *p_owner) const {
+GDScriptDataType GDScriptCompiler::_gdtype_from_datatype(const GDScriptParser::DataType &p_datatype, GDScript *p_owner) {
if (!p_datatype.is_set() || !p_datatype.is_hard_type()) {
return GDScriptDataType();
}
@@ -103,74 +103,43 @@ GDScriptDataType GDScriptCompiler::_gdtype_from_datatype(const GDScriptParser::D
} break;
case GDScriptParser::DataType::SCRIPT: {
result.kind = GDScriptDataType::SCRIPT;
- result.script_type_ref = Ref<Script>(p_datatype.script_type);
+ result.builtin_type = p_datatype.builtin_type;
+ result.script_type_ref = p_datatype.script_type;
result.script_type = result.script_type_ref.ptr();
- result.native_type = result.script_type->get_instance_base_type();
+ result.native_type = p_datatype.native_type;
} break;
case GDScriptParser::DataType::CLASS: {
- // Locate class by constructing the path to it and following that path.
- GDScriptParser::ClassNode *class_type = p_datatype.class_type;
- if (class_type) {
- result.kind = GDScriptDataType::GDSCRIPT;
- result.builtin_type = p_datatype.builtin_type;
-
- String class_name = class_type->fqcn.split("::")[0];
- const bool is_inner_by_path = (!main_script->path.is_empty()) && (class_name == main_script->path);
- const bool is_inner_by_name = (!main_script->name.is_empty()) && (class_name == main_script->name);
- if (is_inner_by_path || is_inner_by_name) {
- // Local class.
- List<StringName> names;
- while (class_type->outer) {
- names.push_back(class_type->identifier->name);
- class_type = class_type->outer;
- }
+ result.kind = GDScriptDataType::GDSCRIPT;
+ result.builtin_type = p_datatype.builtin_type;
+ result.native_type = p_datatype.native_type;
- Ref<GDScript> script = Ref<GDScript>(main_script);
- while (names.back()) {
- if (!script->subclasses.has(names.back()->get())) {
- ERR_PRINT("Parser bug: Cannot locate datatype class.");
- result.has_type = false;
- return GDScriptDataType();
- }
- script = script->subclasses[names.back()->get()];
- names.pop_back();
- }
- result.script_type = script.ptr();
- result.native_type = script->get_instance_base_type();
- } else {
- // Inner class.
- PackedStringArray classes = class_type->fqcn.split("::");
- if (!classes.is_empty()) {
- for (GDScript *script : parsed_classes) {
- // Checking of inheritance structure of inner class to find a correct script link.
- if (script->name == classes[classes.size() - 1]) {
- PackedStringArray classes2 = script->fully_qualified_name.split("::");
- bool valid = true;
- if (classes.size() != classes2.size()) {
- valid = false;
- } else {
- for (int i = 0; i < classes.size(); i++) {
- if (classes[i] != classes2[i]) {
- valid = false;
- break;
- }
- }
- }
- if (!valid) {
- continue;
- }
- result.script_type_ref = Ref<GDScript>(script);
- break;
- }
- }
- }
- if (result.script_type_ref.is_null()) {
- result.script_type_ref = GDScriptCache::get_shallow_script(p_datatype.script_path, main_script->path);
- }
+ String root_name = p_datatype.class_type->fqcn.get_slice("::", 0);
+ bool is_local_class = !root_name.is_empty() && root_name == main_script->fully_qualified_name;
+
+ Ref<GDScript> script;
+ if (is_local_class) {
+ script = Ref<GDScript>(main_script);
+ } else {
+ Error err = OK;
+ script = GDScriptCache::get_shallow_script(p_datatype.script_path, err, p_owner->path);
+ if (err) {
+ _set_error(vformat(R"(Could not find script "%s": %s)", p_datatype.script_path, error_names[err]), nullptr);
+ }
+ }
- result.script_type = result.script_type_ref.ptr();
- result.native_type = p_datatype.native_type;
+ if (script.is_valid()) {
+ script = Ref<GDScript>(script->find_class(p_datatype.class_type->fqcn));
+ }
+
+ if (script.is_null()) {
+ _set_error(vformat(R"(Could not find class "%s" in "%s".)", p_datatype.class_type->fqcn, p_datatype.script_path), nullptr);
+ } else {
+ // Only hold a strong reference if the owner of the element qualified with this type is not local, to avoid cyclic references (leaks).
+ // TODO: Might lead to use after free if script_type is a subclass and is used after its parent is freed.
+ if (!is_local_class) {
+ result.script_type_ref = script;
}
+ result.script_type = script.ptr();
}
} break;
case GDScriptParser::DataType::ENUM:
@@ -189,13 +158,7 @@ GDScriptDataType GDScriptCompiler::_gdtype_from_datatype(const GDScriptParser::D
}
if (p_datatype.has_container_element_type()) {
- result.set_container_element_type(_gdtype_from_datatype(p_datatype.get_container_element_type()));
- }
-
- // Only hold strong reference to the script if it's not the owner of the
- // element qualified with this type, to avoid cyclic references (leaks).
- if (result.script_type && result.script_type == p_owner) {
- result.script_type_ref = Ref<Script>();
+ result.set_container_element_type(_gdtype_from_datatype(p_datatype.get_container_element_type(), p_owner));
}
return result;
@@ -367,7 +330,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
// This is so one autoload doesn't try to load another before it's compiled.
HashMap<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list();
if (autoloads.has(identifier) && autoloads[identifier].is_singleton) {
- GDScriptCodeGenerator::Address global = codegen.add_temporary(_gdtype_from_datatype(in->get_datatype()));
+ GDScriptCodeGenerator::Address global = codegen.add_temporary(_gdtype_from_datatype(in->get_datatype(), codegen.script));
int idx = GDScriptLanguage::get_singleton()->get_global_map()[identifier];
gen->write_store_global(global, idx);
return global;
@@ -434,7 +397,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
Vector<GDScriptCodeGenerator::Address> values;
// Create the result temporary first since it's the last to be killed.
- GDScriptDataType array_type = _gdtype_from_datatype(an->get_datatype());
+ GDScriptDataType array_type = _gdtype_from_datatype(an->get_datatype(), codegen.script);
GDScriptCodeGenerator::Address result = codegen.add_temporary(array_type);
for (int i = 0; i < an->elements.size(); i++) {
@@ -511,7 +474,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
case GDScriptParser::Node::CAST: {
const GDScriptParser::CastNode *cn = static_cast<const GDScriptParser::CastNode *>(p_expression);
GDScriptParser::DataType og_cast_type = cn->cast_type->get_datatype();
- GDScriptDataType cast_type = _gdtype_from_datatype(og_cast_type);
+ GDScriptDataType cast_type = _gdtype_from_datatype(og_cast_type, codegen.script);
if (og_cast_type.kind == GDScriptParser::DataType::ENUM) {
// Enum types are usually treated as dictionaries, but in this case we want to cast to an integer.
@@ -534,7 +497,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
} break;
case GDScriptParser::Node::CALL: {
const GDScriptParser::CallNode *call = static_cast<const GDScriptParser::CallNode *>(p_expression);
- GDScriptDataType type = _gdtype_from_datatype(call->get_datatype());
+ GDScriptDataType type = _gdtype_from_datatype(call->get_datatype(), codegen.script);
GDScriptCodeGenerator::Address result = codegen.add_temporary(type);
GDScriptCodeGenerator::Address nil = GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::NIL);
@@ -670,7 +633,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
Vector<GDScriptCodeGenerator::Address> args;
args.push_back(codegen.add_constant(NodePath(get_node->full_path)));
- GDScriptCodeGenerator::Address result = codegen.add_temporary(_gdtype_from_datatype(get_node->get_datatype()));
+ GDScriptCodeGenerator::Address result = codegen.add_temporary(_gdtype_from_datatype(get_node->get_datatype(), codegen.script));
MethodBind *get_node_method = ClassDB::get_method("Node", "get_node");
gen->write_call_ptrcall(result, GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::SELF), get_node_method, args);
@@ -686,7 +649,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
case GDScriptParser::Node::AWAIT: {
const GDScriptParser::AwaitNode *await = static_cast<const GDScriptParser::AwaitNode *>(p_expression);
- GDScriptCodeGenerator::Address result = codegen.add_temporary(_gdtype_from_datatype(p_expression->get_datatype()));
+ GDScriptCodeGenerator::Address result = codegen.add_temporary(_gdtype_from_datatype(p_expression->get_datatype(), codegen.script));
within_await = true;
GDScriptCodeGenerator::Address argument = _parse_expression(codegen, r_error, await->to_await);
within_await = false;
@@ -705,7 +668,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
// Indexing operator.
case GDScriptParser::Node::SUBSCRIPT: {
const GDScriptParser::SubscriptNode *subscript = static_cast<const GDScriptParser::SubscriptNode *>(p_expression);
- GDScriptCodeGenerator::Address result = codegen.add_temporary(_gdtype_from_datatype(subscript->get_datatype()));
+ GDScriptCodeGenerator::Address result = codegen.add_temporary(_gdtype_from_datatype(subscript->get_datatype(), codegen.script));
GDScriptCodeGenerator::Address base = _parse_expression(codegen, r_error, subscript->base);
if (r_error) {
@@ -735,7 +698,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
// Remove result temp as we don't need it.
gen->pop_temporary();
// Faster than indexing self (as if no self. had been used).
- return GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::MEMBER, MI->value.index, _gdtype_from_datatype(subscript->get_datatype()));
+ return GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::MEMBER, MI->value.index, _gdtype_from_datatype(subscript->get_datatype(), codegen.script));
}
}
@@ -773,7 +736,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
case GDScriptParser::Node::UNARY_OPERATOR: {
const GDScriptParser::UnaryOpNode *unary = static_cast<const GDScriptParser::UnaryOpNode *>(p_expression);
- GDScriptCodeGenerator::Address result = codegen.add_temporary(_gdtype_from_datatype(unary->get_datatype()));
+ GDScriptCodeGenerator::Address result = codegen.add_temporary(_gdtype_from_datatype(unary->get_datatype(), codegen.script));
GDScriptCodeGenerator::Address operand = _parse_expression(codegen, r_error, unary->operand);
if (r_error) {
@@ -791,7 +754,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
case GDScriptParser::Node::BINARY_OPERATOR: {
const GDScriptParser::BinaryOpNode *binary = static_cast<const GDScriptParser::BinaryOpNode *>(p_expression);
- GDScriptCodeGenerator::Address result = codegen.add_temporary(_gdtype_from_datatype(binary->get_datatype()));
+ GDScriptCodeGenerator::Address result = codegen.add_temporary(_gdtype_from_datatype(binary->get_datatype(), codegen.script));
switch (binary->operation) {
case GDScriptParser::BinaryOpNode::OP_LOGIC_AND: {
@@ -867,7 +830,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
case GDScriptParser::Node::TERNARY_OPERATOR: {
// x IF a ELSE y operator with early out on failure.
const GDScriptParser::TernaryOpNode *ternary = static_cast<const GDScriptParser::TernaryOpNode *>(p_expression);
- GDScriptCodeGenerator::Address result = codegen.add_temporary(_gdtype_from_datatype(ternary->get_datatype()));
+ GDScriptCodeGenerator::Address result = codegen.add_temporary(_gdtype_from_datatype(ternary->get_datatype(), codegen.script));
gen->write_start_ternary(result);
@@ -985,7 +948,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
break;
}
const GDScriptParser::SubscriptNode *subscript_elem = E->get();
- GDScriptCodeGenerator::Address value = codegen.add_temporary(_gdtype_from_datatype(subscript_elem->get_datatype()));
+ GDScriptCodeGenerator::Address value = codegen.add_temporary(_gdtype_from_datatype(subscript_elem->get_datatype(), codegen.script));
GDScriptCodeGenerator::Address key;
StringName name;
@@ -1024,8 +987,8 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
// Perform operator if any.
if (assignment->operation != GDScriptParser::AssignmentNode::OP_NONE) {
- GDScriptCodeGenerator::Address op_result = codegen.add_temporary(_gdtype_from_datatype(assignment->get_datatype()));
- GDScriptCodeGenerator::Address value = codegen.add_temporary(_gdtype_from_datatype(subscript->get_datatype()));
+ GDScriptCodeGenerator::Address op_result = codegen.add_temporary(_gdtype_from_datatype(assignment->get_datatype(), codegen.script));
+ GDScriptCodeGenerator::Address value = codegen.add_temporary(_gdtype_from_datatype(subscript->get_datatype(), codegen.script));
if (subscript->is_attribute) {
gen->write_get_named(value, name, prev_base);
} else {
@@ -1130,8 +1093,8 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
StringName name = static_cast<GDScriptParser::IdentifierNode *>(assignment->assignee)->name;
if (has_operation) {
- GDScriptCodeGenerator::Address op_result = codegen.add_temporary(_gdtype_from_datatype(assignment->get_datatype()));
- GDScriptCodeGenerator::Address member = codegen.add_temporary(_gdtype_from_datatype(assignment->assignee->get_datatype()));
+ GDScriptCodeGenerator::Address op_result = codegen.add_temporary(_gdtype_from_datatype(assignment->get_datatype(), codegen.script));
+ GDScriptCodeGenerator::Address member = codegen.add_temporary(_gdtype_from_datatype(assignment->assignee->get_datatype(), codegen.script));
gen->write_get_member(member, name);
gen->write_binary_operator(op_result, assignment->variant_op, member, assigned_value);
gen->pop_temporary(); // Pop member temp.
@@ -1184,7 +1147,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
bool has_operation = assignment->operation != GDScriptParser::AssignmentNode::OP_NONE;
if (has_operation) {
// Perform operation.
- GDScriptCodeGenerator::Address op_result = codegen.add_temporary(_gdtype_from_datatype(assignment->get_datatype()));
+ GDScriptCodeGenerator::Address op_result = codegen.add_temporary(_gdtype_from_datatype(assignment->get_datatype(), codegen.script));
GDScriptCodeGenerator::Address og_value = _parse_expression(codegen, r_error, assignment->assignee);
gen->write_binary_operator(op_result, assignment->variant_op, og_value, assigned_value);
to_assign = op_result;
@@ -1196,7 +1159,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
to_assign = assigned_value;
}
- GDScriptDataType assign_type = _gdtype_from_datatype(assignment->assignee->get_datatype());
+ GDScriptDataType assign_type = _gdtype_from_datatype(assignment->assignee->get_datatype(), codegen.script);
if (has_setter && !is_in_setter) {
// Call setter.
@@ -1226,7 +1189,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
} break;
case GDScriptParser::Node::LAMBDA: {
const GDScriptParser::LambdaNode *lambda = static_cast<const GDScriptParser::LambdaNode *>(p_expression);
- GDScriptCodeGenerator::Address result = codegen.add_temporary(_gdtype_from_datatype(lambda->get_datatype()));
+ GDScriptCodeGenerator::Address result = codegen.add_temporary(_gdtype_from_datatype(lambda->get_datatype(), codegen.script));
Vector<GDScriptCodeGenerator::Address> captures;
captures.resize(lambda->captures.size());
@@ -1645,7 +1608,7 @@ void GDScriptCompiler::_add_locals_in_block(CodeGen &codegen, const GDScriptPars
// Parameters are added directly from function and loop variables are declared explicitly.
continue;
}
- codegen.add_local(p_block->locals[i].name, _gdtype_from_datatype(p_block->locals[i].get_datatype()));
+ codegen.add_local(p_block->locals[i].name, _gdtype_from_datatype(p_block->locals[i].get_datatype(), codegen.script));
}
}
@@ -1675,7 +1638,7 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Sui
codegen.start_block();
// Evaluate the match expression.
- GDScriptCodeGenerator::Address value = codegen.add_local("@match_value", _gdtype_from_datatype(match->test->get_datatype()));
+ GDScriptCodeGenerator::Address value = codegen.add_local("@match_value", _gdtype_from_datatype(match->test->get_datatype(), codegen.script));
GDScriptCodeGenerator::Address value_expr = _parse_expression(codegen, err, match->test);
if (err) {
return err;
@@ -1784,9 +1747,9 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Sui
const GDScriptParser::ForNode *for_n = static_cast<const GDScriptParser::ForNode *>(s);
codegen.start_block();
- GDScriptCodeGenerator::Address iterator = codegen.add_local(for_n->variable->name, _gdtype_from_datatype(for_n->variable->get_datatype()));
+ GDScriptCodeGenerator::Address iterator = codegen.add_local(for_n->variable->name, _gdtype_from_datatype(for_n->variable->get_datatype(), codegen.script));
- gen->start_for(iterator.type, _gdtype_from_datatype(for_n->list->get_datatype()));
+ gen->start_for(iterator.type, _gdtype_from_datatype(for_n->list->get_datatype(), codegen.script));
GDScriptCodeGenerator::Address list = _parse_expression(codegen, err, for_n->list);
if (err) {
@@ -1897,13 +1860,13 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Sui
const GDScriptParser::VariableNode *lv = static_cast<const GDScriptParser::VariableNode *>(s);
// Should be already in stack when the block began.
GDScriptCodeGenerator::Address local = codegen.locals[lv->identifier->name];
- GDScriptParser::DataType local_type = lv->get_datatype();
+ GDScriptDataType local_type = _gdtype_from_datatype(lv->get_datatype(), codegen.script);
if (lv->initializer != nullptr) {
// For typed arrays we need to make sure this is already initialized correctly so typed assignment work.
- if (local_type.is_hard_type() && local_type.builtin_type == Variant::ARRAY) {
+ if (local_type.has_type && local_type.builtin_type == Variant::ARRAY) {
if (local_type.has_container_element_type()) {
- codegen.generator->write_construct_typed_array(local, _gdtype_from_datatype(local_type.get_container_element_type(), codegen.script), Vector<GDScriptCodeGenerator::Address>());
+ codegen.generator->write_construct_typed_array(local, local_type.get_container_element_type(), Vector<GDScriptCodeGenerator::Address>());
} else {
codegen.generator->write_construct_array(local, Vector<GDScriptCodeGenerator::Address>());
}
@@ -1920,11 +1883,11 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Sui
if (src_address.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
codegen.generator->pop_temporary();
}
- } else if (lv->get_datatype().is_hard_type()) {
+ } else if (local_type.has_type) {
// Initialize with default for type.
if (local_type.has_container_element_type()) {
- codegen.generator->write_construct_typed_array(local, _gdtype_from_datatype(local_type.get_container_element_type(), codegen.script), Vector<GDScriptCodeGenerator::Address>());
- } else if (local_type.kind == GDScriptParser::DataType::BUILTIN) {
+ codegen.generator->write_construct_typed_array(local, local_type.get_container_element_type(), Vector<GDScriptCodeGenerator::Address>());
+ } else if (local_type.kind == GDScriptDataType::BUILTIN) {
codegen.generator->write_construct(local, local_type.builtin_type, Vector<GDScriptCodeGenerator::Address>());
}
// The `else` branch is for objects, in such case we leave it as `null`.
@@ -2033,17 +1996,17 @@ GDScriptFunction *GDScriptCompiler::_parse_function(Error &r_error, GDScript *p_
continue;
}
- GDScriptParser::DataType field_type = field->get_datatype();
+ GDScriptDataType field_type = _gdtype_from_datatype(field->get_datatype(), codegen.script);
- GDScriptCodeGenerator::Address dst_address(GDScriptCodeGenerator::Address::MEMBER, codegen.script->member_indices[field->identifier->name].index, _gdtype_from_datatype(field->get_datatype()));
+ GDScriptCodeGenerator::Address dst_address(GDScriptCodeGenerator::Address::MEMBER, codegen.script->member_indices[field->identifier->name].index, field_type);
if (field->initializer) {
// Emit proper line change.
codegen.generator->write_newline(field->initializer->start_line);
// For typed arrays we need to make sure this is already initialized correctly so typed assignment work.
- if (field_type.is_hard_type() && field_type.builtin_type == Variant::ARRAY) {
+ if (field_type.has_type && field_type.builtin_type == Variant::ARRAY) {
if (field_type.has_container_element_type()) {
- codegen.generator->write_construct_typed_array(dst_address, _gdtype_from_datatype(field_type.get_container_element_type(), codegen.script), Vector<GDScriptCodeGenerator::Address>());
+ codegen.generator->write_construct_typed_array(dst_address, field_type.get_container_element_type(), Vector<GDScriptCodeGenerator::Address>());
} else {
codegen.generator->write_construct_array(dst_address, Vector<GDScriptCodeGenerator::Address>());
}
@@ -2062,13 +2025,13 @@ GDScriptFunction *GDScriptCompiler::_parse_function(Error &r_error, GDScript *p_
if (src_address.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
codegen.generator->pop_temporary();
}
- } else if (field->get_datatype().is_hard_type()) {
+ } else if (field_type.has_type) {
codegen.generator->write_newline(field->start_line);
// Initialize with default for type.
if (field_type.has_container_element_type()) {
- codegen.generator->write_construct_typed_array(dst_address, _gdtype_from_datatype(field_type.get_container_element_type(), codegen.script), Vector<GDScriptCodeGenerator::Address>());
- } else if (field_type.kind == GDScriptParser::DataType::BUILTIN) {
+ codegen.generator->write_construct_typed_array(dst_address, field_type.get_container_element_type(), Vector<GDScriptCodeGenerator::Address>());
+ } else if (field_type.kind == GDScriptDataType::BUILTIN) {
codegen.generator->write_construct(dst_address, field_type.builtin_type, Vector<GDScriptCodeGenerator::Address>());
}
// The `else` branch is for objects, in such case we leave it as `null`.
@@ -2195,23 +2158,19 @@ Error GDScriptCompiler::_parse_setter_getter(GDScript *p_script, const GDScriptP
return err;
}
-Error GDScriptCompiler::_parse_class_level(GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state) {
- parsing_classes.insert(p_script);
+Error GDScriptCompiler::_populate_class_members(GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state) {
+ if (parsed_classes.has(p_script)) {
+ return OK;
+ }
- if (p_class->outer && p_class->outer->outer) {
- // Owner is not root
- if (!parsed_classes.has(p_script->_owner)) {
- if (parsing_classes.has(p_script->_owner)) {
- _set_error("Cyclic class reference for '" + String(p_class->identifier->name) + "'.", p_class);
- return ERR_PARSE_ERROR;
- }
- Error err = _parse_class_level(p_script->_owner, p_class->outer, p_keep_state);
- if (err) {
- return err;
- }
- }
+ if (parsing_classes.has(p_script)) {
+ String class_name = p_class->identifier ? String(p_class->identifier->name) : p_class->fqcn;
+ _set_error(vformat(R"(Cyclic class reference for "%s".)", class_name), p_class);
+ return ERR_PARSE_ERROR;
}
+ parsing_classes.insert(p_script);
+
#ifdef TOOLS_ENABLED
p_script->doc_functions.clear();
p_script->doc_variables.clear();
@@ -2253,7 +2212,6 @@ Error GDScriptCompiler::_parse_class_level(GDScript *p_script, const GDScriptPar
p_script->implicit_ready = nullptr;
p_script->tool = parser->is_tool();
- p_script->name = p_class->identifier ? p_class->identifier->name : "";
if (!p_script->name.is_empty()) {
if (ClassDB::class_exists(p_script->name) && ClassDB::is_class_exposed(p_script->name)) {
@@ -2262,53 +2220,50 @@ Error GDScriptCompiler::_parse_class_level(GDScript *p_script, const GDScriptPar
}
}
- Ref<GDScriptNativeClass> native;
+ GDScriptDataType base_type = _gdtype_from_datatype(p_class->base_type, p_script);
- GDScriptDataType base_type = _gdtype_from_datatype(p_class->base_type);
// Inheritance
switch (base_type.kind) {
case GDScriptDataType::NATIVE: {
int native_idx = GDScriptLanguage::get_singleton()->get_global_map()[base_type.native_type];
- native = GDScriptLanguage::get_singleton()->get_global_array()[native_idx];
- ERR_FAIL_COND_V(native.is_null(), ERR_BUG);
- p_script->native = native;
+ p_script->native = GDScriptLanguage::get_singleton()->get_global_array()[native_idx];
+ ERR_FAIL_COND_V(p_script->native.is_null(), ERR_BUG);
} break;
case GDScriptDataType::GDSCRIPT: {
Ref<GDScript> base = Ref<GDScript>(base_type.script_type);
- p_script->base = base;
- p_script->_base = base.ptr();
+ if (base.is_null()) {
+ return ERR_COMPILATION_FAILED;
+ }
- if (p_class->base_type.kind == GDScriptParser::DataType::CLASS && p_class->base_type.class_type != nullptr) {
- if (p_class->base_type.script_path == main_script->path) {
- if (!parsed_classes.has(p_script->_base)) {
- if (parsing_classes.has(p_script->_base)) {
- String class_name = p_class->identifier ? p_class->identifier->name : "<main>";
- _set_error("Cyclic class reference for '" + class_name + "'.", p_class);
- return ERR_PARSE_ERROR;
- }
- Error err = _parse_class_level(p_script->_base, p_class->base_type.class_type, p_keep_state);
- if (err) {
- return err;
- }
- }
- } else {
- Error err = OK;
- base = GDScriptCache::get_full_script(p_class->base_type.script_path, err, main_script->path);
- if (err) {
- return err;
- }
- if (base.is_null() || !base->is_valid()) {
- return ERR_COMPILATION_FAILED;
- }
+ if (base.ptr() == main_script || main_script->is_subclass(base.ptr())) {
+ Error err = _populate_class_members(base.ptr(), p_class->base_type.class_type, p_keep_state);
+ if (err) {
+ return err;
+ }
+ } else if (!base->is_valid()) {
+ Error err = OK;
+ Ref<GDScript> base_root = GDScriptCache::get_full_script(base->path, err, p_script->path);
+ if (err) {
+ _set_error(vformat(R"(Could not compile base class "%s" from "%s": %s)", base->fully_qualified_name, base->path, error_names[err]), nullptr);
+ return err;
}
+ if (base_root.is_valid()) {
+ base = Ref<GDScript>(base_root->find_class(base->fully_qualified_name));
+ }
+ if (base.is_null()) {
+ _set_error(vformat(R"(Could not find class "%s" in "%s".)", base->fully_qualified_name, base->path), nullptr);
+ return ERR_COMPILATION_FAILED;
+ }
+ ERR_FAIL_COND_V(!base->is_valid(), ERR_BUG);
}
+ p_script->base = base;
+ p_script->_base = base.ptr();
p_script->member_indices = base->member_indices;
- native = base->native;
- p_script->native = native;
+ p_script->native = base->native;
} break;
default: {
- _set_error("Parser bug: invalid inheritance.", p_class);
+ _set_error("Parser bug: invalid inheritance.", nullptr);
return ERR_BUG;
} break;
}
@@ -2432,9 +2387,8 @@ Error GDScriptCompiler::_parse_class_level(GDScript *p_script, const GDScriptPar
// TODO: Make enums not be just a dictionary?
Dictionary new_enum;
for (int j = 0; j < enum_n->values.size(); j++) {
- int value = enum_n->values[j].value;
// Needs to be string because Variant::get will convert to String.
- new_enum[String(enum_n->values[j].identifier->name)] = value;
+ new_enum[String(enum_n->values[j].identifier->name)] = enum_n->values[j].value;
}
p_script->constants.insert(enum_n->identifier->name, new_enum);
@@ -2479,8 +2433,7 @@ Error GDScriptCompiler::_parse_class_level(GDScript *p_script, const GDScriptPar
parsed_classes.insert(p_script);
parsing_classes.erase(p_script);
- //parse sub-classes
-
+ // Populate sub-classes.
for (int i = 0; i < p_class->members.size(); i++) {
const GDScriptParser::ClassNode::Member &member = p_class->members[i];
if (member.type != member.CLASS) {
@@ -2492,8 +2445,8 @@ Error GDScriptCompiler::_parse_class_level(GDScript *p_script, const GDScriptPar
GDScript *subclass_ptr = subclass.ptr();
// Subclass might still be parsing, just skip it
- if (!parsed_classes.has(subclass_ptr) && !parsing_classes.has(subclass_ptr)) {
- Error err = _parse_class_level(subclass_ptr, inner_class, p_keep_state);
+ if (!parsing_classes.has(subclass_ptr)) {
+ Error err = _populate_class_members(subclass_ptr, inner_class, p_keep_state);
if (err) {
return err;
}
@@ -2510,9 +2463,8 @@ Error GDScriptCompiler::_parse_class_level(GDScript *p_script, const GDScriptPar
return OK;
}
-Error GDScriptCompiler::_parse_class_blocks(GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state) {
- //parse methods
-
+Error GDScriptCompiler::_compile_class(GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state) {
+ // Compile member functions, getters, and setters.
for (int i = 0; i < p_class->members.size(); i++) {
const GDScriptParser::ClassNode::Member &member = p_class->members[i];
if (member.type == member.FUNCTION) {
@@ -2616,17 +2568,26 @@ Error GDScriptCompiler::_parse_class_blocks(GDScript *p_script, const GDScriptPa
StringName name = inner_class->identifier->name;
GDScript *subclass = p_script->subclasses[name].ptr();
- Error err = _parse_class_blocks(subclass, inner_class, p_keep_state);
+ Error err = _compile_class(subclass, inner_class, p_keep_state);
if (err) {
return err;
}
}
+#ifdef TOOLS_ENABLED
+ p_script->_update_doc();
+#endif
+
+ p_script->_init_rpc_methods_properties();
+
p_script->valid = true;
return OK;
}
-void GDScriptCompiler::_make_scripts(GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state) {
+void GDScriptCompiler::make_scripts(GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state) {
+ p_script->fully_qualified_name = p_class->fqcn;
+ p_script->name = p_class->identifier ? p_class->identifier->name : "";
+
HashMap<StringName, Ref<GDScript>> old_subclasses;
if (p_keep_state) {
@@ -2643,24 +2604,22 @@ void GDScriptCompiler::_make_scripts(GDScript *p_script, const GDScriptParser::C
StringName name = inner_class->identifier->name;
Ref<GDScript> subclass;
- String fully_qualified_name = p_script->fully_qualified_name + "::" + name;
if (old_subclasses.has(name)) {
subclass = old_subclasses[name];
} else {
- Ref<GDScript> orphan_subclass = GDScriptLanguage::get_singleton()->get_orphan_subclass(fully_qualified_name);
- if (orphan_subclass.is_valid()) {
- subclass = orphan_subclass;
- } else {
- subclass.instantiate();
- }
+ subclass = GDScriptLanguage::get_singleton()->get_orphan_subclass(inner_class->fqcn);
+ }
+
+ if (subclass.is_null()) {
+ subclass.instantiate();
}
subclass->_owner = p_script;
- subclass->fully_qualified_name = fully_qualified_name;
+ subclass->path = p_script->path;
p_script->subclasses.insert(name, subclass);
- _make_scripts(subclass.ptr(), inner_class, false);
+ make_scripts(subclass.ptr(), inner_class, p_keep_state);
}
}
@@ -2674,26 +2633,22 @@ Error GDScriptCompiler::compile(const GDScriptParser *p_parser, GDScript *p_scri
source = p_script->get_path();
- // The best fully qualified name for a base level script is its file path
- p_script->fully_qualified_name = p_script->path;
-
// Create scripts for subclasses beforehand so they can be referenced
- _make_scripts(p_script, root, p_keep_state);
+ make_scripts(p_script, root, p_keep_state);
- p_script->_owner = nullptr;
- Error err = _parse_class_level(p_script, root, p_keep_state);
+ main_script->_owner = nullptr;
+ Error err = _populate_class_members(main_script, parser->get_tree(), p_keep_state);
if (err) {
return err;
}
- err = _parse_class_blocks(p_script, root, p_keep_state);
-
+ err = _compile_class(main_script, root, p_keep_state);
if (err) {
return err;
}
- return GDScriptCache::finish_compiling(p_script->get_path());
+ return GDScriptCache::finish_compiling(main_script->get_path());
}
String GDScriptCompiler::get_error() const {
diff --git a/modules/gdscript/gdscript_compiler.h b/modules/gdscript/gdscript_compiler.h
index e4264ea55b..45ca4fe342 100644
--- a/modules/gdscript/gdscript_compiler.h
+++ b/modules/gdscript/gdscript_compiler.h
@@ -121,7 +121,7 @@ class GDScriptCompiler {
Error _create_binary_operator(CodeGen &codegen, const GDScriptParser::BinaryOpNode *on, Variant::Operator op, bool p_initializer = false, const GDScriptCodeGenerator::Address &p_index_addr = GDScriptCodeGenerator::Address());
Error _create_binary_operator(CodeGen &codegen, const GDScriptParser::ExpressionNode *p_left_operand, const GDScriptParser::ExpressionNode *p_right_operand, Variant::Operator op, bool p_initializer = false, const GDScriptCodeGenerator::Address &p_index_addr = GDScriptCodeGenerator::Address());
- GDScriptDataType _gdtype_from_datatype(const GDScriptParser::DataType &p_datatype, GDScript *p_owner = nullptr) const;
+ GDScriptDataType _gdtype_from_datatype(const GDScriptParser::DataType &p_datatype, GDScript *p_owner);
GDScriptCodeGenerator::Address _parse_assign_right_expression(CodeGen &codegen, Error &r_error, const GDScriptParser::AssignmentNode *p_assignmentint, const GDScriptCodeGenerator::Address &p_index_addr = GDScriptCodeGenerator::Address());
GDScriptCodeGenerator::Address _parse_expression(CodeGen &codegen, Error &r_error, const GDScriptParser::ExpressionNode *p_expression, bool p_root = false, bool p_initializer = false, const GDScriptCodeGenerator::Address &p_index_addr = GDScriptCodeGenerator::Address());
@@ -130,9 +130,8 @@ class GDScriptCompiler {
Error _parse_block(CodeGen &codegen, const GDScriptParser::SuiteNode *p_block, bool p_add_locals = true);
GDScriptFunction *_parse_function(Error &r_error, GDScript *p_script, const GDScriptParser::ClassNode *p_class, const GDScriptParser::FunctionNode *p_func, bool p_for_ready = false, bool p_for_lambda = false);
Error _parse_setter_getter(GDScript *p_script, const GDScriptParser::ClassNode *p_class, const GDScriptParser::VariableNode *p_variable, bool p_is_setter);
- Error _parse_class_level(GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state);
- Error _parse_class_blocks(GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state);
- void _make_scripts(GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state);
+ Error _populate_class_members(GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state);
+ Error _compile_class(GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state);
int err_line = 0;
int err_column = 0;
StringName source;
@@ -140,6 +139,7 @@ class GDScriptCompiler {
bool within_await = false;
public:
+ static void make_scripts(GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state);
Error compile(const GDScriptParser *p_parser, GDScript *p_script, bool p_keep_state = false);
String get_error() const;
diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp
index f33d1409bc..68508b20da 100644
--- a/modules/gdscript/gdscript_editor.cpp
+++ b/modules/gdscript/gdscript_editor.cpp
@@ -2765,7 +2765,61 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c
const GDScriptParser::SubscriptNode *attr = static_cast<const GDScriptParser::SubscriptNode *>(completion_context.node);
if (attr->base) {
GDScriptCompletionIdentifier base;
- if (!_guess_expression_type(completion_context, attr->base, base)) {
+ bool found_type = false;
+
+ if (p_owner != nullptr && attr->base->type == GDScriptParser::Node::IDENTIFIER) {
+ const GDScriptParser::GetNodeNode *get_node = nullptr;
+ const GDScriptParser::IdentifierNode *identifier_node = static_cast<GDScriptParser::IdentifierNode *>(attr->base);
+
+ switch (identifier_node->source) {
+ case GDScriptParser::IdentifierNode::Source::MEMBER_VARIABLE: {
+ if (completion_context.current_class != nullptr) {
+ const StringName &member_name = identifier_node->name;
+ const GDScriptParser::ClassNode *current_class = completion_context.current_class;
+
+ if (current_class->has_member(member_name)) {
+ const GDScriptParser::ClassNode::Member &member = current_class->get_member(member_name);
+
+ if (member.type == GDScriptParser::ClassNode::Member::VARIABLE) {
+ const GDScriptParser::VariableNode *variable = static_cast<GDScriptParser::VariableNode *>(member.variable);
+
+ if (variable->initializer && variable->initializer->type == GDScriptParser::Node::GET_NODE) {
+ get_node = static_cast<GDScriptParser::GetNodeNode *>(variable->initializer);
+ }
+ }
+ }
+ }
+ } break;
+ case GDScriptParser::IdentifierNode::Source::LOCAL_VARIABLE: {
+ if (identifier_node->next != nullptr && identifier_node->next->type == GDScriptParser::ClassNode::Node::GET_NODE) {
+ get_node = static_cast<GDScriptParser::GetNodeNode *>(identifier_node->next);
+ }
+ } break;
+ default:
+ break;
+ }
+
+ if (get_node != nullptr) {
+ const Object *node = p_owner->call("get_node_or_null", NodePath(get_node->full_path));
+ if (node != nullptr) {
+ found_type = true;
+
+ GDScriptParser::DataType type;
+ type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
+ type.kind = GDScriptParser::DataType::NATIVE;
+ type.native_type = node->get_class_name();
+ type.builtin_type = Variant::OBJECT;
+
+ base.type = type;
+ }
+
+ if (!found_type) {
+ break;
+ }
+ }
+ }
+
+ if (!found_type && !_guess_expression_type(completion_context, attr->base, base)) {
break;
}
@@ -3051,8 +3105,9 @@ static Error _lookup_symbol_from_base(const GDScriptParser::DataType &p_base, co
r_result.type = ScriptLanguage::LOOKUP_RESULT_SCRIPT_LOCATION;
r_result.location = base_type.class_type->get_member(p_symbol).get_line();
r_result.class_path = base_type.script_path;
- r_result.script = GDScriptCache::get_shallow_script(r_result.class_path);
- return OK;
+ Error err = OK;
+ r_result.script = GDScriptCache::get_shallow_script(r_result.class_path, err);
+ return err;
}
base_type = base_type.class_type->base_type;
}
diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp
index 4279edf394..71cedb4f38 100644
--- a/modules/gdscript/gdscript_parser.cpp
+++ b/modules/gdscript/gdscript_parser.cpp
@@ -534,6 +534,7 @@ void GDScriptParser::end_statement(const String &p_context) {
void GDScriptParser::parse_program() {
head = alloc_node<ClassNode>();
+ head->fqcn = script_path;
current_class = head;
// If we happen to parse an annotation before extends or class_name keywords, track it.
@@ -646,6 +647,9 @@ GDScriptParser::ClassNode *GDScriptParser::parse_class() {
if (consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected identifier for the class name after "class".)")) {
n_class->identifier = parse_identifier();
+ if (n_class->outer) {
+ n_class->fqcn = n_class->outer->fqcn + "::" + n_class->identifier->name;
+ }
}
if (match(GDScriptTokenizer::Token::EXTENDS)) {
@@ -684,6 +688,7 @@ GDScriptParser::ClassNode *GDScriptParser::parse_class() {
void GDScriptParser::parse_class_name() {
if (consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected identifier for the global class name after "class_name".)")) {
current_class->identifier = parse_identifier();
+ current_class->fqcn = String(current_class->identifier->name);
}
if (match(GDScriptTokenizer::Token::EXTENDS)) {
@@ -3802,7 +3807,7 @@ bool GDScriptParser::export_annotations(const AnnotationNode *p_annotation, Node
String enum_hint_string;
bool first = true;
- for (const KeyValue<StringName, int> &E : export_type.enum_values) {
+ for (const KeyValue<StringName, int64_t> &E : export_type.enum_values) {
if (!first) {
enum_hint_string += ",";
} else {
diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h
index f40887ddb8..d8f5b866aa 100644
--- a/modules/gdscript/gdscript_parser.h
+++ b/modules/gdscript/gdscript_parser.h
@@ -131,7 +131,7 @@ public:
ClassNode *class_type = nullptr;
MethodInfo method_info; // For callable/signals.
- HashMap<StringName, int> enum_values; // For enums.
+ HashMap<StringName, int64_t> enum_values; // For enums.
_FORCE_INLINE_ bool is_set() const { return kind != UNRESOLVED; }
_FORCE_INLINE_ bool has_no_type() const { return type_source == UNDETECTED; }
diff --git a/modules/gdscript/tests/gdscript_test_runner.cpp b/modules/gdscript/tests/gdscript_test_runner.cpp
index 15131afde7..1ccbf9d150 100644
--- a/modules/gdscript/tests/gdscript_test_runner.cpp
+++ b/modules/gdscript/tests/gdscript_test_runner.cpp
@@ -461,7 +461,6 @@ GDScriptTest::TestResult GDScriptTest::execute_test_code(bool p_is_generating) {
Ref<GDScript> script;
script.instantiate();
script->set_path(source_file);
- script->set_script_path(source_file);
err = script->load_source_code(source_file);
if (err != OK) {
enable_stdout();
diff --git a/modules/gdscript/tests/scripts/analyzer/features/external_inner_base.gd b/modules/gdscript/tests/scripts/analyzer/features/external_inner_base.gd
new file mode 100644
index 0000000000..3f9bfe189c
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/external_inner_base.gd
@@ -0,0 +1,4 @@
+extends "inner_base.gd".InnerA.InnerAB
+
+func test():
+ super.test()
diff --git a/modules/gdscript/tests/scripts/analyzer/features/external_inner_base.out b/modules/gdscript/tests/scripts/analyzer/features/external_inner_base.out
new file mode 100644
index 0000000000..62f1383392
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/external_inner_base.out
@@ -0,0 +1,3 @@
+GDTEST_OK
+InnerA.InnerAB.test
+InnerB.test
diff --git a/modules/gdscript/tests/scripts/analyzer/features/inner_base.gd b/modules/gdscript/tests/scripts/analyzer/features/inner_base.gd
new file mode 100644
index 0000000000..a825b59255
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/inner_base.gd
@@ -0,0 +1,18 @@
+extends InnerA
+
+func test():
+ super.test()
+
+class InnerA extends InnerAB:
+ func test():
+ print("InnerA.test")
+ super.test()
+
+ class InnerAB extends InnerB:
+ func test():
+ print("InnerA.InnerAB.test")
+ super.test()
+
+class InnerB:
+ func test():
+ print("InnerB.test")
diff --git a/modules/gdscript/tests/scripts/analyzer/features/inner_base.out b/modules/gdscript/tests/scripts/analyzer/features/inner_base.out
new file mode 100644
index 0000000000..ddd5ffcfd3
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/inner_base.out
@@ -0,0 +1,4 @@
+GDTEST_OK
+InnerA.test
+InnerA.InnerAB.test
+InnerB.test