summaryrefslogtreecommitdiffstats
path: root/modules/gdscript/gdscript_analyzer.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'modules/gdscript/gdscript_analyzer.cpp')
-rw-r--r--modules/gdscript/gdscript_analyzer.cpp204
1 files changed, 162 insertions, 42 deletions
diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp
index bfc78d2605..e98cae765b 100644
--- a/modules/gdscript/gdscript_analyzer.cpp
+++ b/modules/gdscript/gdscript_analyzer.cpp
@@ -285,7 +285,7 @@ Error GDScriptAnalyzer::check_class_member_name_conflict(const GDScriptParser::C
return OK;
}
-void GDScriptAnalyzer::get_class_node_current_scope_classes(GDScriptParser::ClassNode *p_node, List<GDScriptParser::ClassNode *> *p_list) {
+void GDScriptAnalyzer::get_class_node_current_scope_classes(GDScriptParser::ClassNode *p_node, List<GDScriptParser::ClassNode *> *p_list, GDScriptParser::Node *p_source) {
ERR_FAIL_NULL(p_node);
ERR_FAIL_NULL(p_list);
@@ -299,11 +299,15 @@ void GDScriptAnalyzer::get_class_node_current_scope_classes(GDScriptParser::Clas
// Prioritize node base type over its outer class
if (p_node->base_type.class_type != nullptr) {
- get_class_node_current_scope_classes(p_node->base_type.class_type, p_list);
+ // TODO: 'ensure_cached_external_parser_for_class()' is only necessary because 'resolve_class_inheritance()' is not getting called here.
+ ensure_cached_external_parser_for_class(p_node->base_type.class_type, p_node, "Trying to fetch classes in the current scope", p_source);
+ get_class_node_current_scope_classes(p_node->base_type.class_type, p_list, p_source);
}
if (p_node->outer != nullptr) {
- get_class_node_current_scope_classes(p_node->outer, p_list);
+ // TODO: 'ensure_cached_external_parser_for_class()' is only necessary because 'resolve_class_inheritance()' is not getting called here.
+ ensure_cached_external_parser_for_class(p_node->outer, p_node, "Trying to fetch classes in the current scope", p_source);
+ get_class_node_current_scope_classes(p_node->outer, p_list, p_source);
}
}
@@ -312,6 +316,13 @@ Error GDScriptAnalyzer::resolve_class_inheritance(GDScriptParser::ClassNode *p_c
p_source = p_class;
}
+ Ref<GDScriptParserRef> parser_ref = ensure_cached_external_parser_for_class(p_class, nullptr, "Trying to resolve class inheritance", p_source);
+ Finally finally([&]() {
+ for (GDScriptParser::ClassNode *look_class = p_class; look_class != nullptr; look_class = look_class->base_type.class_type) {
+ ensure_cached_external_parser_for_class(look_class->base_type.class_type, look_class, "Trying to resolve class inheritance", p_source);
+ }
+ });
+
if (p_class->base_type.is_resolving()) {
push_error(vformat(R"(Could not resolve class "%s": Cyclic reference.)", type_from_metatype(p_class->get_datatype()).to_string()), p_source);
return ERR_PARSE_ERROR;
@@ -323,21 +334,17 @@ Error GDScriptAnalyzer::resolve_class_inheritance(GDScriptParser::ClassNode *p_c
}
if (!parser->has_class(p_class)) {
- String script_path = p_class->get_datatype().script_path;
- Ref<GDScriptParserRef> parser_ref = parser->get_depended_parser_for(script_path);
if (parser_ref.is_null()) {
- push_error(vformat(R"(Could not find script "%s".)", script_path), p_source);
+ // Error already pushed.
return ERR_PARSE_ERROR;
}
Error err = parser_ref->raise_status(GDScriptParserRef::PARSED);
if (err) {
- push_error(vformat(R"(Could not parse script "%s": %s.)", script_path, error_names[err]), p_source);
+ push_error(vformat(R"(Could not parse script "%s": %s.)", p_class->get_datatype().script_path, error_names[err]), p_source);
return ERR_PARSE_ERROR;
}
- ERR_FAIL_COND_V_MSG(!parser_ref->get_parser()->has_class(p_class), ERR_PARSE_ERROR, R"(Parser bug: Mismatched external parser.)");
-
GDScriptAnalyzer *other_analyzer = parser_ref->get_analyzer();
GDScriptParser *other_parser = parser_ref->get_parser();
@@ -471,7 +478,7 @@ Error GDScriptAnalyzer::resolve_class_inheritance(GDScriptParser::ClassNode *p_c
// Look for other classes in script.
bool found = false;
List<GDScriptParser::ClassNode *> script_classes;
- get_class_node_current_scope_classes(p_class, &script_classes);
+ get_class_node_current_scope_classes(p_class, &script_classes, id);
for (GDScriptParser::ClassNode *look_class : script_classes) {
if (look_class->identifier && look_class->identifier->name == name) {
if (!look_class->get_datatype().is_set()) {
@@ -763,7 +770,7 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type
// Classes in current scope.
List<GDScriptParser::ClassNode *> script_classes;
bool found = false;
- get_class_node_current_scope_classes(parser->current_class, &script_classes);
+ get_class_node_current_scope_classes(parser->current_class, &script_classes, p_type);
for (GDScriptParser::ClassNode *script_class : script_classes) {
if (found) {
break;
@@ -883,6 +890,15 @@ void GDScriptAnalyzer::resolve_class_member(GDScriptParser::ClassNode *p_class,
p_source = member.get_source_node();
}
+ Ref<GDScriptParserRef> parser_ref = ensure_cached_external_parser_for_class(p_class, nullptr, "Trying to resolve class member", p_source);
+ Finally finally([&]() {
+ ensure_cached_external_parser_for_class(member.get_datatype().class_type, p_class, "Trying to resolve datatype of class member", p_source);
+ GDScriptParser::DataType member_type = member.get_datatype();
+ if (member_type.has_container_element_type(0)) {
+ ensure_cached_external_parser_for_class(member_type.get_container_element_type(0).class_type, p_class, "Trying to resolve datatype of class member", p_source);
+ }
+ });
+
if (member.get_datatype().is_resolving()) {
push_error(vformat(R"(Could not resolve member "%s": Cyclic reference.)", member.get_name()), p_source);
return;
@@ -892,42 +908,39 @@ void GDScriptAnalyzer::resolve_class_member(GDScriptParser::ClassNode *p_class,
return;
}
+ // If it's already resolving, that's ok.
+ if (!p_class->base_type.is_resolving()) {
+ Error err = resolve_class_inheritance(p_class);
+ if (err) {
+ return;
+ }
+ }
+
if (!parser->has_class(p_class)) {
- String script_path = p_class->get_datatype().script_path;
- Ref<GDScriptParserRef> parser_ref = parser->get_depended_parser_for(script_path);
if (parser_ref.is_null()) {
- push_error(vformat(R"(Could not find script "%s" (While resolving "%s").)", script_path, member.get_name()), p_source);
+ // Error already pushed.
return;
}
Error err = parser_ref->raise_status(GDScriptParserRef::PARSED);
if (err) {
- push_error(vformat(R"(Could not resolve script "%s": %s (While resolving "%s").)", script_path, error_names[err], member.get_name()), p_source);
+ push_error(vformat(R"(Could not parse script "%s": %s (While resolving external class member "%s").)", p_class->get_datatype().script_path, error_names[err], member.get_name()), p_source);
return;
}
- ERR_FAIL_COND_MSG(!parser_ref->get_parser()->has_class(p_class), R"(Parser bug: Mismatched external parser.)");
-
GDScriptAnalyzer *other_analyzer = parser_ref->get_analyzer();
GDScriptParser *other_parser = parser_ref->get_parser();
int error_count = other_parser->errors.size();
other_analyzer->resolve_class_member(p_class, p_index);
if (other_parser->errors.size() > error_count) {
- push_error(vformat(R"(Could not resolve member "%s".)", member.get_name()), p_source);
+ push_error(vformat(R"(Could not resolve external class member "%s".)", member.get_name()), p_source);
+ return;
}
return;
}
- // If it's already resolving, that's ok.
- if (!p_class->base_type.is_resolving()) {
- Error err = resolve_class_inheritance(p_class);
- if (err) {
- return;
- }
- }
-
GDScriptParser::ClassNode *previous_class = parser->current_class;
parser->current_class = p_class;
@@ -1170,27 +1183,25 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas
p_source = p_class;
}
+ Ref<GDScriptParserRef> parser_ref = ensure_cached_external_parser_for_class(p_class, nullptr, "Trying to resolve class interface", p_source);
+
if (!p_class->resolved_interface) {
#ifdef DEBUG_ENABLED
bool has_static_data = p_class->has_static_data;
#endif
if (!parser->has_class(p_class)) {
- String script_path = p_class->get_datatype().script_path;
- Ref<GDScriptParserRef> parser_ref = parser->get_depended_parser_for(script_path);
if (parser_ref.is_null()) {
- push_error(vformat(R"(Could not find script "%s".)", script_path), p_source);
+ // Error already pushed.
return;
}
Error err = parser_ref->raise_status(GDScriptParserRef::PARSED);
if (err) {
- push_error(vformat(R"(Could not resolve script "%s": %s.)", script_path, error_names[err]), p_source);
+ push_error(vformat(R"(Could not parse script "%s": %s.)", p_class->get_datatype().script_path, error_names[err]), p_source);
return;
}
- ERR_FAIL_COND_MSG(!parser_ref->get_parser()->has_class(p_class), R"(Parser bug: Mismatched external parser.)");
-
GDScriptAnalyzer *other_analyzer = parser_ref->get_analyzer();
GDScriptParser *other_parser = parser_ref->get_parser();
@@ -1198,6 +1209,7 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas
other_analyzer->resolve_class_interface(p_class);
if (other_parser->errors.size() > error_count) {
push_error(vformat(R"(Could not resolve class "%s".)", p_class->fqcn), p_source);
+ return;
}
return;
@@ -1261,26 +1273,24 @@ void GDScriptAnalyzer::resolve_class_body(GDScriptParser::ClassNode *p_class, co
p_source = p_class;
}
+ Ref<GDScriptParserRef> parser_ref = ensure_cached_external_parser_for_class(p_class, nullptr, "Trying to resolve class body", p_source);
+
if (p_class->resolved_body) {
return;
}
if (!parser->has_class(p_class)) {
- String script_path = p_class->get_datatype().script_path;
- Ref<GDScriptParserRef> parser_ref = parser->get_depended_parser_for(script_path);
if (parser_ref.is_null()) {
- push_error(vformat(R"(Could not find script "%s".)", script_path), p_source);
+ // Error already pushed.
return;
}
Error err = parser_ref->raise_status(GDScriptParserRef::PARSED);
if (err) {
- push_error(vformat(R"(Could not resolve script "%s": %s.)", script_path, error_names[err]), p_source);
+ push_error(vformat(R"(Could not parse script "%s": %s.)", p_class->get_datatype().script_path, error_names[err]), p_source);
return;
}
- ERR_FAIL_COND_MSG(!parser_ref->get_parser()->has_class(p_class), R"(Parser bug: Mismatched external parser.)");
-
GDScriptAnalyzer *other_analyzer = parser_ref->get_analyzer();
GDScriptParser *other_parser = parser_ref->get_parser();
@@ -1288,6 +1298,7 @@ void GDScriptAnalyzer::resolve_class_body(GDScriptParser::ClassNode *p_class, co
other_analyzer->resolve_class_body(p_class);
if (other_parser->errors.size() > error_count) {
push_error(vformat(R"(Could not resolve class "%s".)", p_class->fqcn), p_source);
+ return;
}
return;
@@ -3645,12 +3656,116 @@ GDScriptParser::DataType GDScriptAnalyzer::make_global_class_meta_type(const Str
}
}
+Ref<GDScriptParserRef> GDScriptAnalyzer::ensure_cached_external_parser_for_class(const GDScriptParser::ClassNode *p_class, const GDScriptParser::ClassNode *p_from_class, const char *p_context, const GDScriptParser::Node *p_source) {
+ if (p_class == nullptr) {
+ return nullptr;
+ }
+
+ if (HashMap<const GDScriptParser::ClassNode *, Ref<GDScriptParserRef>>::Iterator E = external_class_parser_cache.find(p_class)) {
+ return E->value;
+ }
+
+ if (parser->has_class(p_class)) {
+ return nullptr;
+ }
+
+ if (p_from_class == nullptr) {
+ p_from_class = parser->head;
+ }
+
+ String script_path = p_class->get_datatype().script_path;
+
+ Ref<GDScriptParserRef> parser_ref;
+ for (const GDScriptParser::ClassNode *look_class = p_from_class; look_class != nullptr; look_class = look_class->base_type.class_type) {
+ if (parser->has_class(look_class)) {
+ parser_ref = find_cached_external_parser_for_class(p_class, parser);
+ if (parser_ref.is_valid()) {
+ break;
+ }
+ }
+
+ if (HashMap<const GDScriptParser::ClassNode *, Ref<GDScriptParserRef>>::Iterator E = external_class_parser_cache.find(look_class)) {
+ parser_ref = find_cached_external_parser_for_class(p_class, E->value);
+ if (parser_ref.is_valid()) {
+ break;
+ }
+ }
+
+ String look_class_script_path = look_class->get_datatype().script_path;
+ if (HashMap<String, Ref<GDScriptParserRef>>::Iterator E = parser->depended_parsers.find(look_class_script_path)) {
+ parser_ref = find_cached_external_parser_for_class(p_class, E->value);
+ if (parser_ref.is_valid()) {
+ break;
+ }
+ }
+ }
+
+ if (parser_ref.is_null()) {
+ push_error(vformat(R"(Parser bug (please report): Could not find external parser for class "%s". (%s))", p_class->fqcn, p_context), p_source);
+ // A null parser will be inserted into the cache, so this error won't spam for the same class.
+ // This is ok, the values of external_class_parser_cache are not assumed to be valid references.
+ }
+
+ external_class_parser_cache.insert(p_class, parser_ref);
+ return parser_ref;
+}
+
+Ref<GDScriptParserRef> GDScriptAnalyzer::find_cached_external_parser_for_class(const GDScriptParser::ClassNode *p_class, const Ref<GDScriptParserRef> &p_dependant_parser) {
+ if (p_dependant_parser.is_null()) {
+ return nullptr;
+ }
+
+ if (HashMap<const GDScriptParser::ClassNode *, Ref<GDScriptParserRef>>::Iterator E = p_dependant_parser->get_analyzer()->external_class_parser_cache.find(p_class)) {
+ if (E->value.is_valid()) {
+ // Silently ensure it's parsed.
+ E->value->raise_status(GDScriptParserRef::PARSED);
+ if (E->value->get_parser()->has_class(p_class)) {
+ return E->value;
+ }
+ }
+ }
+
+ if (p_dependant_parser->get_parser()->has_class(p_class)) {
+ return p_dependant_parser;
+ }
+
+ // Silently ensure it's parsed.
+ p_dependant_parser->raise_status(GDScriptParserRef::PARSED);
+ return find_cached_external_parser_for_class(p_class, p_dependant_parser->get_parser());
+}
+
+Ref<GDScriptParserRef> GDScriptAnalyzer::find_cached_external_parser_for_class(const GDScriptParser::ClassNode *p_class, GDScriptParser *p_dependant_parser) {
+ if (p_dependant_parser == nullptr) {
+ return nullptr;
+ }
+
+ String script_path = p_class->get_datatype().script_path;
+ if (HashMap<String, Ref<GDScriptParserRef>>::Iterator E = p_dependant_parser->depended_parsers.find(script_path)) {
+ if (E->value.is_valid()) {
+ // Silently ensure it's parsed.
+ E->value->raise_status(GDScriptParserRef::PARSED);
+ if (E->value->get_parser()->has_class(p_class)) {
+ return E->value;
+ }
+ }
+ }
+
+ return nullptr;
+}
+
+Ref<GDScript> GDScriptAnalyzer::get_depended_shallow_script(const String &p_path, Error &r_error) {
+ // To keep a local cache of the parser for resolving external nodes later.
+ parser->get_depended_parser_for(p_path);
+ Ref<GDScript> scr = GDScriptCache::get_shallow_script(p_path, r_error, parser->script_path);
+ return scr;
+}
+
void GDScriptAnalyzer::reduce_identifier_from_base_set_class(GDScriptParser::IdentifierNode *p_identifier, GDScriptParser::DataType p_identifier_datatype) {
ERR_FAIL_NULL(p_identifier);
p_identifier->set_datatype(p_identifier_datatype);
Error err = OK;
- Ref<GDScript> scr = GDScriptCache::get_shallow_script(p_identifier_datatype.script_path, err, parser->script_path);
+ Ref<GDScript> scr = get_depended_shallow_script(p_identifier_datatype.script_path, err);
if (err) {
push_error(vformat(R"(Error while getting cache for script "%s".)", p_identifier_datatype.script_path), p_identifier);
return;
@@ -3767,7 +3882,7 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
bool is_base = true;
if (base_class != nullptr) {
- get_class_node_current_scope_classes(base_class, &script_classes);
+ get_class_node_current_scope_classes(base_class, &script_classes, p_identifier);
}
bool is_constructor = base.is_meta_type && p_identifier->name == SNAME("new");
@@ -4340,7 +4455,7 @@ void GDScriptAnalyzer::reduce_preload(GDScriptParser::PreloadNode *p_preload) {
const String &res_type = ResourceLoader::get_resource_type(p_preload->resolved_path);
if (res_type == "GDScript") {
Error err = OK;
- Ref<GDScript> res = GDScriptCache::get_shallow_script(p_preload->resolved_path, err, parser->script_path);
+ Ref<GDScript> res = get_depended_shallow_script(p_preload->resolved_path, err);
p_preload->resource = res;
if (err != OK) {
push_error(vformat(R"(Could not preload resource script "%s".)", p_preload->resolved_path), p_preload->path);
@@ -4361,6 +4476,11 @@ void GDScriptAnalyzer::reduce_preload(GDScriptParser::PreloadNode *p_preload) {
p_preload->is_constant = true;
p_preload->reduced_value = p_preload->resource;
p_preload->set_datatype(type_from_variant(p_preload->reduced_value, p_preload));
+
+ // TODO: Not sure if this is necessary anymore.
+ // 'type_from_variant()' should call 'resolve_class_inheritance()' which would call 'ensure_cached_external_parser_for_class()'
+ // Better safe than sorry.
+ ensure_cached_external_parser_for_class(p_preload->get_datatype().class_type, nullptr, "Trying to resolve preload", p_preload);
}
void GDScriptAnalyzer::reduce_self(GDScriptParser::SelfNode *p_self) {
@@ -4916,7 +5036,7 @@ Array GDScriptAnalyzer::make_array_from_element_datatype(const GDScriptParser::D
Ref<Script> script_type = p_element_datatype.script_type;
if (p_element_datatype.kind == GDScriptParser::DataType::CLASS && script_type.is_null()) {
Error err = OK;
- Ref<GDScript> scr = GDScriptCache::get_shallow_script(p_element_datatype.script_path, err, parser->script_path);
+ Ref<GDScript> scr = get_depended_shallow_script(p_element_datatype.script_path, err);
if (err) {
push_error(vformat(R"(Error while getting cache for script "%s".)", p_element_datatype.script_path), p_source_node);
return array;