diff options
Diffstat (limited to 'modules/gdscript')
92 files changed, 1893 insertions, 874 deletions
diff --git a/modules/gdscript/SCsub b/modules/gdscript/SCsub index c6121ec7fe..2f507db548 100644 --- a/modules/gdscript/SCsub +++ b/modules/gdscript/SCsub @@ -10,6 +10,8 @@ env_gdscript.add_source_files(env.modules_sources, "*.cpp") if env["tools"]: env_gdscript.add_source_files(env.modules_sources, "./editor/*.cpp") + SConscript("editor/script_templates/SCsub") + # Those two modules are required for the language server protocol if env["module_jsonrpc_enabled"] and env["module_websocket_enabled"]: env_gdscript.add_source_files(env.modules_sources, "./language_server/*.cpp") @@ -18,8 +20,7 @@ if env["tools"]: # in regular builds where all modules are enabled. env_gdscript.Append(CPPDEFINES=["GDSCRIPT_NO_LSP"]) + if env["tests"]: env_gdscript.Append(CPPDEFINES=["TESTS_ENABLED"]) env_gdscript.add_source_files(env.modules_sources, "./tests/*.cpp") - -SConscript("editor_templates/SCsub") diff --git a/modules/gdscript/doc_classes/@GDScript.xml b/modules/gdscript/doc_classes/@GDScript.xml index d9fab01dce..70151c4d21 100644 --- a/modules/gdscript/doc_classes/@GDScript.xml +++ b/modules/gdscript/doc_classes/@GDScript.xml @@ -184,27 +184,24 @@ <method name="range" qualifiers="vararg"> <return type="Array" /> <description> - Returns an array with the given range. Range can be 1 argument [code]N[/code] (0 to [code]N[/code] - 1), two arguments ([code]initial[/code], [code]final - 1[/code]) or three arguments ([code]initial[/code], [code]final - 1[/code], [code]increment[/code]). Returns an empty array if the range isn't valid (e.g. [code]range(2, 5, -1)[/code] or [code]range(5, 5, 1)[/code]). - Returns an array with the given range. [code]range()[/code] can have 1 argument N ([code]0[/code] to [code]N - 1[/code]), two arguments ([code]initial[/code], [code]final - 1[/code]) or three arguments ([code]initial[/code], [code]final - 1[/code], [code]increment[/code]). [code]increment[/code] can be negative. If [code]increment[/code] is negative, [code]final - 1[/code] will become [code]final + 1[/code]. Also, the initial value must be greater than the final value for the loop to run. - [code]range()(/code] converts all arguments to [int] before processing. + Returns an array with the given range. [method range] can be called in three ways: + [code]range(n: int)[/code]: Starts from 0, increases by steps of 1, and stops [i]before[/i] [code]n[/code]. The argument [code]n[/code] is [b]exclusive[/b]. + [code]range(b: int, n: int)[/code]: Starts from [code]b[/code], increases by steps of 1, and stops [i]before[/i] [code]n[/code]. The arguments [code]b[/code] and [code]n[/code] are [b]inclusive[/b] and [b]exclusive[/b], respectively. + [code]range(b: int, n: int, s: int)[/code]: Starts from [code]b[/code], increases/decreases by steps of [code]s[/code], and stops [i]before[/i] [code]n[/code]. The arguments [code]b[/code] and [code]n[/code] are [b]inclusive[/b] and [b]exclusive[/b], respectively. The argument [code]s[/code] [b]can[/b] be negative, but not [code]0[/code]. If [code]s[/code] is [code]0[/code], an error message is printed. + [method range] converts all arguments to [int] before processing. + [b]Note:[/b] Returns an empty array if no value meets the value constraint (e.g. [code]range(2, 5, -1)[/code] or [code]range(5, 5, 1)[/code]). + Examples: [codeblock] - print(range(4)) - print(range(2, 5)) - print(range(0, 6, 2)) - [/codeblock] - Output: - [codeblock] - [0, 1, 2, 3] - [2, 3, 4] - [0, 2, 4] + print(range(4)) # Prints [0, 1, 2, 3] + print(range(2, 5)) # Prints [2, 3, 4] + print(range(0, 6, 2)) # Prints [0, 2, 4] + print(range(4, 1, -1)) # Prints [4, 3, 2] [/codeblock] To iterate over an [Array] backwards, use: [codeblock] var array = [3, 6, 9] - var i := array.size() - 1 - while i >= 0: - print(array[i]) - i -= 1 + for i in range(array.size(), 0, -1): + print(array[i - 1]) [/codeblock] Output: [codeblock] diff --git a/modules/gdscript/editor/gdscript_highlighter.cpp b/modules/gdscript/editor/gdscript_highlighter.cpp index e3f0ddfc35..b86e9b386d 100644 --- a/modules/gdscript/editor/gdscript_highlighter.cpp +++ b/modules/gdscript/editor/gdscript_highlighter.cpp @@ -387,9 +387,9 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_l in_member_variable = false; } - if (!in_node_path && in_region == -1 && str[j] == '$') { + if (!in_node_path && in_region == -1 && (str[j] == '$' || str[j] == '%')) { in_node_path = true; - } else if (in_region != -1 || (is_a_symbol && str[j] != '/')) { + } else if (in_region != -1 || (is_a_symbol && str[j] != '/' && str[j] != '%')) { in_node_path = false; } @@ -510,9 +510,8 @@ void GDScriptSyntaxHighlighter::_update_cache() { } /* Autoloads. */ - OrderedHashMap<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list(); - for (OrderedHashMap<StringName, ProjectSettings::AutoloadInfo>::Element E = autoloads.front(); E; E = E.next()) { - const ProjectSettings::AutoloadInfo &info = E.value(); + for (const KeyValue<StringName, ProjectSettings::AutoloadInfo> &E : ProjectSettings::get_singleton()->get_autoload_list()) { + const ProjectSettings::AutoloadInfo &info = E.value; if (info.is_singleton) { keywords[info.name] = usertype_color; } diff --git a/modules/gdscript/editor/gdscript_highlighter.h b/modules/gdscript/editor/gdscript_highlighter.h index 1ae0d72896..92764e3891 100644 --- a/modules/gdscript/editor/gdscript_highlighter.h +++ b/modules/gdscript/editor/gdscript_highlighter.h @@ -45,7 +45,7 @@ private: bool line_only = false; }; Vector<ColorRegion> color_regions; - Map<int, int> color_region_cache; + HashMap<int, int> color_region_cache; HashMap<StringName, Color> keywords; HashMap<StringName, Color> member_keywords; diff --git a/modules/gdscript/editor/gdscript_translation_parser_plugin.cpp b/modules/gdscript/editor/gdscript_translation_parser_plugin.cpp index a8f4483cf4..9b540b16f2 100644 --- a/modules/gdscript/editor/gdscript_translation_parser_plugin.cpp +++ b/modules/gdscript/editor/gdscript_translation_parser_plugin.cpp @@ -44,7 +44,7 @@ Error GDScriptEditorTranslationParserPlugin::parse_file(const String &p_path, Ve // Search strings in AssignmentNode -> text = "__", hint_tooltip = "__" etc. Error err; - RES loaded_res = ResourceLoader::load(p_path, "", ResourceFormatLoader::CACHE_MODE_REUSE, &err); + Ref<Resource> loaded_res = ResourceLoader::load(p_path, "", ResourceFormatLoader::CACHE_MODE_REUSE, &err); if (err) { ERR_PRINT("Failed to load " + p_path); return err; diff --git a/modules/gdscript/editor/gdscript_translation_parser_plugin.h b/modules/gdscript/editor/gdscript_translation_parser_plugin.h index e7b40aa367..969a50f48c 100644 --- a/modules/gdscript/editor/gdscript_translation_parser_plugin.h +++ b/modules/gdscript/editor/gdscript_translation_parser_plugin.h @@ -31,7 +31,7 @@ #ifndef GDSCRIPT_TRANSLATION_PARSER_PLUGIN_H #define GDSCRIPT_TRANSLATION_PARSER_PLUGIN_H -#include "core/templates/set.h" +#include "core/templates/hash_set.h" #include "editor/editor_translation_parser.h" #include "modules/gdscript/gdscript_parser.h" @@ -44,9 +44,9 @@ class GDScriptEditorTranslationParserPlugin : public EditorTranslationParserPlug // List of patterns used for extracting translation strings. StringName tr_func = "tr"; StringName trn_func = "tr_n"; - Set<StringName> assignment_patterns; - Set<StringName> first_arg_patterns; - Set<StringName> second_arg_patterns; + HashSet<StringName> assignment_patterns; + HashSet<StringName> first_arg_patterns; + HashSet<StringName> second_arg_patterns; // FileDialog patterns. StringName fd_add_filter = "add_filter"; StringName fd_set_filter = "set_filters"; diff --git a/modules/gdscript/editor_templates/CharacterBody2D/basic_movement.gd b/modules/gdscript/editor/script_templates/CharacterBody2D/basic_movement.gd index 34b5ba45b7..a379d915a9 100644 --- a/modules/gdscript/editor_templates/CharacterBody2D/basic_movement.gd +++ b/modules/gdscript/editor/script_templates/CharacterBody2D/basic_movement.gd @@ -2,8 +2,9 @@ extends _BASE_ -const SPEED: float = 300.0 -const JUMP_VELOCITY: float = -400.0 + +const SPEED = 300.0 +const JUMP_VELOCITY = -400.0 # Get the gravity from the project settings to be synced with RigidDynamicBody nodes. var gravity: int = ProjectSettings.get_setting("physics/2d/default_gravity") diff --git a/modules/gdscript/editor_templates/CharacterBody3D/basic_movement.gd b/modules/gdscript/editor/script_templates/CharacterBody3D/basic_movement.gd index cbc9cf1064..360b199e56 100644 --- a/modules/gdscript/editor_templates/CharacterBody3D/basic_movement.gd +++ b/modules/gdscript/editor/script_templates/CharacterBody3D/basic_movement.gd @@ -2,8 +2,9 @@ extends _BASE_ -const SPEED: float = 5.0 -const JUMP_VELOCITY: float = 4.5 + +const SPEED = 5.0 +const JUMP_VELOCITY = 4.5 # Get the gravity from the project settings to be synced with RigidDynamicBody nodes. var gravity: float = ProjectSettings.get_setting("physics/3d/default_gravity") diff --git a/modules/gdscript/editor_templates/EditorPlugin/plugin.gd b/modules/gdscript/editor/script_templates/EditorPlugin/plugin.gd index 8614bb8b17..b27b3e5655 100644 --- a/modules/gdscript/editor_templates/EditorPlugin/plugin.gd +++ b/modules/gdscript/editor/script_templates/EditorPlugin/plugin.gd @@ -2,10 +2,12 @@ @tool extends EditorPlugin + func _enter_tree() -> void: - # Initialization of the plugin goes here. - pass + # Initialization of the plugin goes here. + pass + func _exit_tree() -> void: - # Clean-up of the plugin goes here. - pass + # Clean-up of the plugin goes here. + pass diff --git a/modules/gdscript/editor_templates/EditorScript/basic_editor_script.gd b/modules/gdscript/editor/script_templates/EditorScript/basic_editor_script.gd index fdb174c7ed..fdb8550d43 100644 --- a/modules/gdscript/editor_templates/EditorScript/basic_editor_script.gd +++ b/modules/gdscript/editor/script_templates/EditorScript/basic_editor_script.gd @@ -2,6 +2,7 @@ @tool extends EditorScript + +# Called when the script is executed (using File -> Run in Script Editor). func _run() -> void: - # Called when the script is executed (using File -> Run in Script Editor). - pass + pass diff --git a/modules/gdscript/editor_templates/Node/default.gd b/modules/gdscript/editor/script_templates/Node/default.gd index ee5c0b99cc..cb96a21537 100644 --- a/modules/gdscript/editor_templates/Node/default.gd +++ b/modules/gdscript/editor/script_templates/Node/default.gd @@ -2,10 +2,12 @@ extends _BASE_ + # Called when the node enters the scene tree for the first time. func _ready() -> void: pass # Replace with function body. + # Called every frame. 'delta' is the elapsed time since the previous frame. func _process(delta: float) -> void: pass diff --git a/modules/gdscript/editor_templates/Object/empty.gd b/modules/gdscript/editor/script_templates/Object/empty.gd index 387786b0a4..387786b0a4 100644 --- a/modules/gdscript/editor_templates/Object/empty.gd +++ b/modules/gdscript/editor/script_templates/Object/empty.gd diff --git a/modules/gdscript/editor_templates/SCsub b/modules/gdscript/editor/script_templates/SCsub index 2266ef2d01..2266ef2d01 100644 --- a/modules/gdscript/editor_templates/SCsub +++ b/modules/gdscript/editor/script_templates/SCsub diff --git a/modules/gdscript/editor_templates/VisualShaderNodeCustom/basic.gd b/modules/gdscript/editor/script_templates/VisualShaderNodeCustom/basic.gd index cf6d68333d..283a95d3b4 100644 --- a/modules/gdscript/editor_templates/VisualShaderNodeCustom/basic.gd +++ b/modules/gdscript/editor/script_templates/VisualShaderNodeCustom/basic.gd @@ -1,38 +1,50 @@ # meta-description: Visual shader's node plugin template @tool -extends _BASE_ class_name VisualShaderNode_CLASS_ +extends _BASE_ + func _get_name() -> String: return "_CLASS_" + func _get_category() -> String: return "" + func _get_description() -> String: return "" + func _get_return_icon_type() -> int: return PORT_TYPE_SCALAR + func _get_input_port_count() -> int: return 0 + func _get_input_port_name(port: int) -> String: return "" + func _get_input_port_type(port: int) -> int: return PORT_TYPE_SCALAR + func _get_output_port_count() -> int: return 1 + func _get_output_port_name(port: int) -> String: return "result" + func _get_output_port_type(port: int) -> int: return PORT_TYPE_SCALAR -func _get_code(input_vars: Array[String], output_vars: Array[String], mode: int, type: int) -> String: + +func _get_code(input_vars: Array[String], output_vars: Array[String], + mode: int, type: int) -> String: return output_vars[0] + " = 0.0;" diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp index d415684d10..55a7e39dec 100644 --- a/modules/gdscript/gdscript.cpp +++ b/modules/gdscript/gdscript.cpp @@ -82,7 +82,7 @@ Variant GDScriptNativeClass::_new() { RefCounted *rc = Object::cast_to<RefCounted>(o); if (rc) { - return REF(rc); + return Ref<RefCounted>(rc); } else { return o; } @@ -92,6 +92,21 @@ Object *GDScriptNativeClass::instantiate() { return ClassDB::instantiate(name); } +Variant GDScriptNativeClass::callp(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) { + if (p_method == SNAME("new")) { + // Constructor. + return Object::callp(p_method, p_args, p_argcount, r_error); + } + MethodBind *method = ClassDB::get_method(name, p_method); + if (method) { + // Native static method. + return method->call(nullptr, p_args, p_argcount, r_error); + } + + r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; + return Variant(); +} + GDScriptFunction *GDScript::_super_constructor(GDScript *p_script) { if (p_script->initializer) { return p_script->initializer; @@ -180,7 +195,7 @@ Variant GDScript::_new(const Variant **p_args, int p_argcount, Callable::CallErr } r_error.error = Callable::CallError::CALL_OK; - REF ref; + Ref<RefCounted> ref; Object *owner = nullptr; GDScript *_baseptr = this; @@ -198,7 +213,7 @@ Variant GDScript::_new(const Variant **p_args, int p_argcount, Callable::CallErr RefCounted *r = Object::cast_to<RefCounted>(owner); if (r) { - ref = REF(r); + ref = Ref<RefCounted>(r); } GDScriptInstance *instance = _create_instance(p_args, p_argcount, owner, r != nullptr, r_error); @@ -325,14 +340,14 @@ bool GDScript::has_method(const StringName &p_method) const { } MethodInfo GDScript::get_method_info(const StringName &p_method) const { - const Map<StringName, GDScriptFunction *>::Element *E = member_functions.find(p_method); + HashMap<StringName, GDScriptFunction *>::ConstIterator E = member_functions.find(p_method); if (!E) { return MethodInfo(); } - GDScriptFunction *func = E->get(); + GDScriptFunction *func = E->value; MethodInfo mi; - mi.name = E->key(); + mi.name = E->key; for (int i = 0; i < func->get_argument_count(); i++) { mi.arguments.push_back(func->get_argument_type(i)); } @@ -344,9 +359,9 @@ MethodInfo GDScript::get_method_info(const StringName &p_method) const { bool GDScript::get_property_default_value(const StringName &p_property, Variant &r_value) const { #ifdef TOOLS_ENABLED - const Map<StringName, Variant>::Element *E = member_default_values_cache.find(p_property); + HashMap<StringName, Variant>::ConstIterator E = member_default_values_cache.find(p_property); if (E) { - r_value = E->get(); + r_value = E->value; return true; } @@ -412,7 +427,7 @@ void GDScript::set_source_code(const String &p_code) { } #ifdef TOOLS_ENABLED -void GDScript::_update_exports_values(Map<StringName, Variant> &values, List<PropertyInfo> &propnames) { +void GDScript::_update_exports_values(HashMap<StringName, Variant> &values, List<PropertyInfo> &propnames) { if (base_cache.is_valid()) { base_cache->_update_exports_values(values, propnames); } @@ -744,13 +759,13 @@ bool GDScript::_update_exports(bool *r_err, bool p_recursive_call, PlaceHolderSc if ((changed || p_instance_to_update) && placeholders.size()) { //hm :( // update placeholders if any - Map<StringName, Variant> values; + HashMap<StringName, Variant> values; List<PropertyInfo> propnames; _update_exports_values(values, propnames); if (changed) { - for (Set<PlaceHolderScriptInstance *>::Element *E = placeholders.front(); E; E = E->next()) { - E->get()->update(propnames, values); + for (PlaceHolderScriptInstance *E : placeholders) { + E->update(propnames, values); } } else { p_instance_to_update->update(propnames, values); @@ -773,10 +788,10 @@ void GDScript::update_exports() { return; } - Set<ObjectID> copy = inheriters_cache; //might get modified + HashSet<ObjectID> copy = inheriters_cache; //might get modified - for (Set<ObjectID>::Element *E = copy.front(); E; E = E->next()) { - Object *id = ObjectDB::get_instance(E->get()); + for (const ObjectID &E : copy) { + Object *id = ObjectDB::get_instance(E); GDScript *s = Object::cast_to<GDScript>(id); if (!s) { continue; @@ -824,7 +839,7 @@ Error GDScript::reload(bool p_keep_state) { // Loading a template, don't parse. #ifdef TOOLS_ENABLED - if (basedir.begins_with(EditorSettings::get_singleton()->get_project_script_templates_dir())) { + if (EditorSettings::get_singleton() && basedir.begins_with(EditorSettings::get_singleton()->get_project_script_templates_dir())) { return OK; } #endif @@ -914,7 +929,7 @@ ScriptLanguage *GDScript::get_language() const { return GDScriptLanguage::get_singleton(); } -void GDScript::get_constants(Map<StringName, Variant> *p_constants) { +void GDScript::get_constants(HashMap<StringName, Variant> *p_constants) { if (p_constants) { for (const KeyValue<StringName, Variant> &E : constants) { (*p_constants)[E.key] = E.value; @@ -922,10 +937,10 @@ void GDScript::get_constants(Map<StringName, Variant> *p_constants) { } } -void GDScript::get_members(Set<StringName> *p_members) { +void GDScript::get_members(HashSet<StringName> *p_members) { if (p_members) { - for (Set<StringName>::Element *E = members.front(); E; E = E->next()) { - p_members->insert(E->get()); + for (const StringName &E : members) { + p_members->insert(E); } } } @@ -937,11 +952,11 @@ const Vector<Multiplayer::RPCConfig> GDScript::get_rpc_methods() const { Variant GDScript::callp(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) { GDScript *top = this; while (top) { - Map<StringName, GDScriptFunction *>::Element *E = top->member_functions.find(p_method); + HashMap<StringName, GDScriptFunction *>::Iterator E = top->member_functions.find(p_method); if (E) { - ERR_FAIL_COND_V_MSG(!E->get()->is_static(), Variant(), "Can't call non-static function '" + String(p_method) + "' in script."); + ERR_FAIL_COND_V_MSG(!E->value->is_static(), Variant(), "Can't call non-static function '" + String(p_method) + "' in script."); - return E->get()->call(nullptr, p_args, p_argcount, r_error); + return E->value->call(nullptr, p_args, p_argcount, r_error); } top = top->_base; } @@ -956,17 +971,17 @@ bool GDScript::_get(const StringName &p_name, Variant &r_ret) const { const GDScript *top = this; while (top) { { - const Map<StringName, Variant>::Element *E = top->constants.find(p_name); + HashMap<StringName, Variant>::ConstIterator E = top->constants.find(p_name); if (E) { - r_ret = E->get(); + r_ret = E->value; return true; } } { - const Map<StringName, Ref<GDScript>>::Element *E = subclasses.find(p_name); + HashMap<StringName, Ref<GDScript>>::ConstIterator E = subclasses.find(p_name); if (E) { - r_ret = E->get(); + r_ret = E->value; return true; } } @@ -1015,17 +1030,21 @@ Error GDScript::load_byte_code(const String &p_path) { Error GDScript::load_source_code(const String &p_path) { Vector<uint8_t> sourcef; Error err; - FileAccess *f = FileAccess::open(p_path, FileAccess::READ, &err); + Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ, &err); if (err) { - ERR_FAIL_COND_V(err, err); + const char *err_name; + if (err < 0 || err >= ERR_MAX) { + err_name = "(invalid error code)"; + } else { + err_name = error_names[err]; + } + ERR_FAIL_COND_V_MSG(err, err, "Attempt to open script '" + p_path + "' resulted in error '" + err_name + "'."); } uint64_t len = f->get_length(); sourcef.resize(len + 1); uint8_t *w = sourcef.ptrw(); uint64_t r = f->get_buffer(w, len); - f->close(); - memdelete(f); ERR_FAIL_COND_V(r != len, ERR_CANT_OPEN); w[len] = 0; @@ -1042,7 +1061,7 @@ Error GDScript::load_source_code(const String &p_path) { return OK; } -const Map<StringName, GDScriptFunction *> &GDScript::debug_get_member_functions() const { +const HashMap<StringName, GDScriptFunction *> &GDScript::debug_get_member_functions() const { return member_functions; } @@ -1190,7 +1209,7 @@ void GDScript::_init_rpc_methods_properties() { } GDScript *cscript = this; - Map<StringName, Ref<GDScript>>::Element *sub_E = subclasses.front(); + HashMap<StringName, Ref<GDScript>>::Iterator sub_E = subclasses.begin(); while (cscript) { // RPC Methods for (KeyValue<StringName, GDScriptFunction *> &E : cscript->member_functions) { @@ -1204,11 +1223,11 @@ void GDScript::_init_rpc_methods_properties() { } if (cscript != this) { - sub_E = sub_E->next(); + ++sub_E; } if (sub_E) { - cscript = sub_E->get().ptr(); + cscript = sub_E->value.ptr(); } else { cscript = nullptr; } @@ -1263,9 +1282,9 @@ GDScript::~GDScript() { bool GDScriptInstance::set(const StringName &p_name, const Variant &p_value) { //member { - const Map<StringName, GDScript::MemberInfo>::Element *E = script->member_indices.find(p_name); + HashMap<StringName, GDScript::MemberInfo>::Iterator E = script->member_indices.find(p_name); if (E) { - const GDScript::MemberInfo *member = &E->get(); + const GDScript::MemberInfo *member = &E->value; if (member->setter) { const Variant *val = &p_value; Callable::CallError err; @@ -1306,13 +1325,13 @@ bool GDScriptInstance::set(const StringName &p_name, const Variant &p_value) { GDScript *sptr = script.ptr(); while (sptr) { - Map<StringName, GDScriptFunction *>::Element *E = sptr->member_functions.find(GDScriptLanguage::get_singleton()->strings._set); + HashMap<StringName, GDScriptFunction *>::Iterator E = sptr->member_functions.find(GDScriptLanguage::get_singleton()->strings._set); if (E) { Variant name = p_name; const Variant *args[2] = { &name, &p_value }; Callable::CallError err; - Variant ret = E->get()->call(this, (const Variant **)args, 2, err); + Variant ret = E->value->call(this, (const Variant **)args, 2, err); if (err.error == Callable::CallError::CALL_OK && ret.get_type() == Variant::BOOL && ret.operator bool()) { return true; } @@ -1327,16 +1346,16 @@ bool GDScriptInstance::get(const StringName &p_name, Variant &r_ret) const { const GDScript *sptr = script.ptr(); while (sptr) { { - const Map<StringName, GDScript::MemberInfo>::Element *E = script->member_indices.find(p_name); + HashMap<StringName, GDScript::MemberInfo>::ConstIterator E = script->member_indices.find(p_name); if (E) { - if (E->get().getter) { + if (E->value.getter) { Callable::CallError err; - r_ret = const_cast<GDScriptInstance *>(this)->callp(E->get().getter, nullptr, 0, err); + r_ret = const_cast<GDScriptInstance *>(this)->callp(E->value.getter, nullptr, 0, err); if (err.error == Callable::CallError::CALL_OK) { return true; } } - r_ret = members[E->get().index]; + r_ret = members[E->value.index]; return true; //index found } } @@ -1344,9 +1363,9 @@ bool GDScriptInstance::get(const StringName &p_name, Variant &r_ret) const { { const GDScript *sl = sptr; while (sl) { - const Map<StringName, Variant>::Element *E = sl->constants.find(p_name); + HashMap<StringName, Variant>::ConstIterator E = sl->constants.find(p_name); if (E) { - r_ret = E->get(); + r_ret = E->value; return true; //index found } sl = sl->_base; @@ -1357,9 +1376,9 @@ bool GDScriptInstance::get(const StringName &p_name, Variant &r_ret) const { // Signals. const GDScript *sl = sptr; while (sl) { - const Map<StringName, Vector<StringName>>::Element *E = sl->_signals.find(p_name); + HashMap<StringName, Vector<StringName>>::ConstIterator E = sl->_signals.find(p_name); if (E) { - r_ret = Signal(this->owner, E->key()); + r_ret = Signal(this->owner, E->key); return true; //index found } sl = sl->_base; @@ -1370,14 +1389,14 @@ bool GDScriptInstance::get(const StringName &p_name, Variant &r_ret) const { // Methods. const GDScript *sl = sptr; while (sl) { - const Map<StringName, GDScriptFunction *>::Element *E = sl->member_functions.find(p_name); + HashMap<StringName, GDScriptFunction *>::ConstIterator E = sl->member_functions.find(p_name); if (E) { Multiplayer::RPCConfig config; config.name = p_name; if (sptr->rpc_functions.find(config) != -1) { - r_ret = Callable(memnew(GDScriptRPCCallable(this->owner, E->key()))); + r_ret = Callable(memnew(GDScriptRPCCallable(this->owner, E->key))); } else { - r_ret = Callable(this->owner, E->key()); + r_ret = Callable(this->owner, E->key); } return true; //index found } @@ -1386,13 +1405,13 @@ bool GDScriptInstance::get(const StringName &p_name, Variant &r_ret) const { } { - const Map<StringName, GDScriptFunction *>::Element *E = sptr->member_functions.find(GDScriptLanguage::get_singleton()->strings._get); + HashMap<StringName, GDScriptFunction *>::ConstIterator E = sptr->member_functions.find(GDScriptLanguage::get_singleton()->strings._get); if (E) { Variant name = p_name; const Variant *args[1] = { &name }; Callable::CallError err; - Variant ret = const_cast<GDScriptFunction *>(E->get())->call(const_cast<GDScriptInstance *>(this), (const Variant **)args, 1, err); + Variant ret = const_cast<GDScriptFunction *>(E->value)->call(const_cast<GDScriptInstance *>(this), (const Variant **)args, 1, err); if (err.error == Callable::CallError::CALL_OK && ret.get_type() != Variant::NIL) { r_ret = ret; return true; @@ -1430,10 +1449,10 @@ void GDScriptInstance::get_property_list(List<PropertyInfo> *p_properties) const List<PropertyInfo> props; while (sptr) { - const Map<StringName, GDScriptFunction *>::Element *E = sptr->member_functions.find(GDScriptLanguage::get_singleton()->strings._get_property_list); + HashMap<StringName, GDScriptFunction *>::ConstIterator E = sptr->member_functions.find(GDScriptLanguage::get_singleton()->strings._get_property_list); if (E) { Callable::CallError err; - Variant ret = const_cast<GDScriptFunction *>(E->get())->call(const_cast<GDScriptInstance *>(this), nullptr, 0, err); + Variant ret = const_cast<GDScriptFunction *>(E->value)->call(const_cast<GDScriptInstance *>(this), nullptr, 0, err); if (err.error == Callable::CallError::CALL_OK) { ERR_FAIL_COND_MSG(ret.get_type() != Variant::ARRAY, "Wrong type for _get_property_list, must be an array of dictionaries."); @@ -1506,7 +1525,7 @@ void GDScriptInstance::get_method_list(List<MethodInfo> *p_list) const { bool GDScriptInstance::has_method(const StringName &p_method) const { const GDScript *sptr = script.ptr(); while (sptr) { - const Map<StringName, GDScriptFunction *>::Element *E = sptr->member_functions.find(p_method); + HashMap<StringName, GDScriptFunction *>::ConstIterator E = sptr->member_functions.find(p_method); if (E) { return true; } @@ -1519,9 +1538,9 @@ bool GDScriptInstance::has_method(const StringName &p_method) const { Variant GDScriptInstance::callp(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) { GDScript *sptr = script.ptr(); while (sptr) { - Map<StringName, GDScriptFunction *>::Element *E = sptr->member_functions.find(p_method); + HashMap<StringName, GDScriptFunction *>::Iterator E = sptr->member_functions.find(p_method); if (E) { - return E->get()->call(this, p_args, p_argcount, r_error); + return E->value->call(this, p_args, p_argcount, r_error); } sptr = sptr->_base; } @@ -1536,10 +1555,10 @@ void GDScriptInstance::notification(int p_notification) { GDScript *sptr = script.ptr(); while (sptr) { - Map<StringName, GDScriptFunction *>::Element *E = sptr->member_functions.find(GDScriptLanguage::get_singleton()->strings._notification); + HashMap<StringName, GDScriptFunction *>::Iterator E = sptr->member_functions.find(GDScriptLanguage::get_singleton()->strings._notification); if (E) { Callable::CallError err; - E->get()->call(this, args, 1, err); + E->value->call(this, args, 1, err); if (err.error != Callable::CallError::CALL_OK) { //print error about notification call } @@ -1863,7 +1882,7 @@ void GDScriptLanguage::reload_tool_script(const Ref<Script> &p_script, bool p_so //when someone asks you why dynamically typed languages are easier to write.... - Map<Ref<GDScript>, Map<ObjectID, List<Pair<StringName, Variant>>>> to_reload; + HashMap<Ref<GDScript>, HashMap<ObjectID, List<Pair<StringName, Variant>>>> to_reload; //as scripts are going to be reloaded, must proceed without locking here @@ -1876,11 +1895,11 @@ void GDScriptLanguage::reload_tool_script(const Ref<Script> &p_script, bool p_so continue; } - to_reload.insert(script, Map<ObjectID, List<Pair<StringName, Variant>>>()); + to_reload.insert(script, HashMap<ObjectID, List<Pair<StringName, Variant>>>()); if (!p_soft_reload) { //save state and remove script from instances - Map<ObjectID, List<Pair<StringName, Variant>>> &map = to_reload[script]; + HashMap<ObjectID, List<Pair<StringName, Variant>>> &map = to_reload[script]; while (script->instances.front()) { Object *obj = script->instances.front()->get(); @@ -1897,7 +1916,7 @@ void GDScriptLanguage::reload_tool_script(const Ref<Script> &p_script, bool p_so #ifdef TOOLS_ENABLED while (script->placeholders.size()) { - Object *obj = script->placeholders.front()->get()->get_owner(); + Object *obj = (*script->placeholders.begin())->get_owner(); //save instance info if (obj->get_script_instance()) { @@ -1907,7 +1926,7 @@ void GDScriptLanguage::reload_tool_script(const Ref<Script> &p_script, bool p_so obj->set_script(Variant()); } else { // no instance found. Let's remove it so we don't loop forever - script->placeholders.erase(script->placeholders.front()->get()); + script->placeholders.erase(*script->placeholders.begin()); } } @@ -1919,7 +1938,7 @@ void GDScriptLanguage::reload_tool_script(const Ref<Script> &p_script, bool p_so } } - for (KeyValue<Ref<GDScript>, Map<ObjectID, List<Pair<StringName, Variant>>>> &E : to_reload) { + for (KeyValue<Ref<GDScript>, HashMap<ObjectID, List<Pair<StringName, Variant>>>> &E : to_reload) { Ref<GDScript> scr = E.key; scr->reload(p_soft_reload); @@ -2084,7 +2103,7 @@ bool GDScriptLanguage::handles_global_class_type(const String &p_type) const { String GDScriptLanguage::get_global_class_name(const String &p_path, String *r_base_type, String *r_icon_path) const { Vector<uint8_t> sourcef; Error err; - FileAccessRef f = FileAccess::open(p_path, FileAccess::READ, &err); + Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ, &err); if (err) { return String(); } @@ -2118,8 +2137,8 @@ String GDScriptLanguage::get_global_class_name(const String &p_path, String *r_b } else { Vector<StringName> extend_classes = subclass->extends; - FileAccessRef subfile = FileAccess::open(subclass->extends_path, FileAccess::READ); - if (!subfile) { + Ref<FileAccess> subfile = FileAccess::open(subclass->extends_path, FileAccess::READ); + if (subfile.is_null()) { break; } String subsource = subfile->get_as_utf8_string(); @@ -2213,9 +2232,13 @@ GDScriptLanguage::GDScriptLanguage() { GLOBAL_DEF("debug/gdscript/warnings/treat_warnings_as_errors", false); GLOBAL_DEF("debug/gdscript/warnings/exclude_addons", true); for (int i = 0; i < (int)GDScriptWarning::WARNING_MAX; i++) { - String warning = GDScriptWarning::get_name_from_code((GDScriptWarning::Code)i).to_lower(); - bool default_enabled = !warning.begins_with("unsafe_"); - GLOBAL_DEF("debug/gdscript/warnings/" + warning, default_enabled); + GDScriptWarning::Code code = (GDScriptWarning::Code)i; + Variant default_enabled = GDScriptWarning::get_default_value(code); + String path = GDScriptWarning::get_settings_path_from_code(code); + GLOBAL_DEF(path, default_enabled); + + PropertyInfo property_info = GDScriptWarning::get_property_info(code); + ProjectSettings::get_singleton()->set_custom_property_info(path, property_info); } #endif // DEBUG_ENABLED } @@ -2258,13 +2281,13 @@ void GDScriptLanguage::add_orphan_subclass(const String &p_qualified_name, const } Ref<GDScript> GDScriptLanguage::get_orphan_subclass(const String &p_qualified_name) { - Map<String, ObjectID>::Element *orphan_subclass_element = orphan_subclasses.find(p_qualified_name); + HashMap<String, ObjectID>::Iterator orphan_subclass_element = orphan_subclasses.find(p_qualified_name); if (!orphan_subclass_element) { return Ref<GDScript>(); } - ObjectID orphan_subclass = orphan_subclass_element->get(); + ObjectID orphan_subclass = orphan_subclass_element->value; Object *obj = ObjectDB::get_instance(orphan_subclass); - orphan_subclasses.erase(orphan_subclass_element); + orphan_subclasses.remove(orphan_subclass_element); if (!obj) { return Ref<GDScript>(); } @@ -2273,7 +2296,7 @@ Ref<GDScript> GDScriptLanguage::get_orphan_subclass(const String &p_qualified_na /*************** RESOURCE ***************/ -RES 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) { +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) { if (r_error) { *r_error = ERR_FILE_CANT_OPEN; } @@ -2316,8 +2339,8 @@ String ResourceFormatLoaderGDScript::get_resource_type(const String &p_path) con } void ResourceFormatLoaderGDScript::get_dependencies(const String &p_path, List<String> *p_dependencies, bool p_add_types) { - FileAccessRef file = FileAccess::open(p_path, FileAccess::READ); - ERR_FAIL_COND_MSG(!file, "Cannot open file '" + p_path + "'."); + Ref<FileAccess> file = FileAccess::open(p_path, FileAccess::READ); + ERR_FAIL_COND_MSG(file.is_null(), "Cannot open file '" + p_path + "'."); String source = file->get_as_utf8_string(); if (source.is_empty()) { @@ -2334,38 +2357,37 @@ void ResourceFormatLoaderGDScript::get_dependencies(const String &p_path, List<S } } -Error ResourceFormatSaverGDScript::save(const String &p_path, const RES &p_resource, uint32_t p_flags) { +Error ResourceFormatSaverGDScript::save(const String &p_path, const Ref<Resource> &p_resource, uint32_t p_flags) { Ref<GDScript> sqscr = p_resource; ERR_FAIL_COND_V(sqscr.is_null(), ERR_INVALID_PARAMETER); String source = sqscr->get_source_code(); - Error err; - FileAccess *file = FileAccess::open(p_path, FileAccess::WRITE, &err); + { + Error err; + Ref<FileAccess> file = FileAccess::open(p_path, FileAccess::WRITE, &err); - ERR_FAIL_COND_V_MSG(err, err, "Cannot save GDScript file '" + p_path + "'."); + ERR_FAIL_COND_V_MSG(err, err, "Cannot save GDScript file '" + p_path + "'."); - file->store_string(source); - if (file->get_error() != OK && file->get_error() != ERR_FILE_EOF) { - memdelete(file); - return ERR_CANT_CREATE; + file->store_string(source); + if (file->get_error() != OK && file->get_error() != ERR_FILE_EOF) { + return ERR_CANT_CREATE; + } } - file->close(); - memdelete(file); if (ScriptServer::is_reload_scripts_on_save_enabled()) { - GDScriptLanguage::get_singleton()->reload_tool_script(p_resource, false); + GDScriptLanguage::get_singleton()->reload_tool_script(p_resource, true); } return OK; } -void ResourceFormatSaverGDScript::get_recognized_extensions(const RES &p_resource, List<String> *p_extensions) const { +void ResourceFormatSaverGDScript::get_recognized_extensions(const Ref<Resource> &p_resource, List<String> *p_extensions) const { if (Object::cast_to<GDScript>(*p_resource)) { p_extensions->push_back("gd"); } } -bool ResourceFormatSaverGDScript::recognize(const RES &p_resource) const { +bool ResourceFormatSaverGDScript::recognize(const Ref<Resource> &p_resource) const { return Object::cast_to<GDScript>(*p_resource) != nullptr; } diff --git a/modules/gdscript/gdscript.h b/modules/gdscript/gdscript.h index 30e60e2b91..80f187a375 100644 --- a/modules/gdscript/gdscript.h +++ b/modules/gdscript/gdscript.h @@ -37,6 +37,7 @@ #include "core/io/resource_loader.h" #include "core/io/resource_saver.h" #include "core/object/script_language.h" +#include "core/templates/rb_set.h" #include "gdscript_function.h" class GDScriptNativeClass : public RefCounted { @@ -52,6 +53,7 @@ public: _FORCE_INLINE_ const StringName &get_name() const { return name; } Variant _new(); Object *instantiate(); + virtual Variant callp(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) override; GDScriptNativeClass(const StringName &p_name); }; @@ -79,48 +81,48 @@ class GDScript : public Script { GDScript *_base = nullptr; //fast pointer access GDScript *_owner = nullptr; //for subclasses - Set<StringName> members; //members are just indices to the instantiated script. - Map<StringName, Variant> constants; - Map<StringName, GDScriptFunction *> member_functions; - Map<StringName, MemberInfo> member_indices; //members are just indices to the instantiated script. - Map<StringName, Ref<GDScript>> subclasses; - Map<StringName, Vector<StringName>> _signals; + HashSet<StringName> members; //members are just indices to the instantiated script. + HashMap<StringName, Variant> constants; + HashMap<StringName, GDScriptFunction *> member_functions; + HashMap<StringName, MemberInfo> member_indices; //members are just indices to the instantiated script. + HashMap<StringName, Ref<GDScript>> subclasses; + HashMap<StringName, Vector<StringName>> _signals; Vector<Multiplayer::RPCConfig> rpc_functions; #ifdef TOOLS_ENABLED - Map<StringName, int> member_lines; - Map<StringName, Variant> member_default_values; + HashMap<StringName, int> member_lines; + HashMap<StringName, Variant> member_default_values; List<PropertyInfo> members_cache; - Map<StringName, Variant> member_default_values_cache; + HashMap<StringName, Variant> member_default_values_cache; Ref<GDScript> base_cache; - Set<ObjectID> inheriters_cache; + HashSet<ObjectID> inheriters_cache; bool source_changed_cache = false; bool placeholder_fallback_enabled = false; - void _update_exports_values(Map<StringName, Variant> &values, List<PropertyInfo> &propnames); + void _update_exports_values(HashMap<StringName, Variant> &values, List<PropertyInfo> &propnames); DocData::ClassDoc doc; Vector<DocData::ClassDoc> docs; String doc_brief_description; String doc_description; Vector<DocData::TutorialDoc> doc_tutorials; - Map<String, String> doc_functions; - Map<String, String> doc_variables; - Map<String, String> doc_constants; - Map<String, String> doc_signals; - Map<String, DocData::EnumDoc> doc_enums; + HashMap<String, String> doc_functions; + HashMap<String, String> doc_variables; + HashMap<String, String> doc_constants; + HashMap<String, String> doc_signals; + HashMap<String, DocData::EnumDoc> doc_enums; void _clear_doc(); void _update_doc(); void _add_doc(const DocData::ClassDoc &p_inner_class); #endif - Map<StringName, PropertyInfo> member_info; + HashMap<StringName, PropertyInfo> member_info; GDScriptFunction *implicit_initializer = nullptr; GDScriptFunction *initializer = nullptr; //direct pointer to new , faster to locate int subclass_count = 0; - Set<Object *> instances; + RBSet<Object *> instances; //exported members String source; String path; @@ -138,14 +140,14 @@ class GDScript : public Script { String _get_debug_path() const; #ifdef TOOLS_ENABLED - Set<PlaceHolderScriptInstance *> placeholders; + HashSet<PlaceHolderScriptInstance *> placeholders; //void _update_placeholder(PlaceHolderScriptInstance *p_placeholder); virtual void _placeholder_erased(PlaceHolderScriptInstance *p_placeholder) override; #endif #ifdef DEBUG_ENABLED - Map<ObjectID, List<Pair<StringName, Variant>>> pending_reload_state; + HashMap<ObjectID, List<Pair<StringName, Variant>>> pending_reload_state; #endif @@ -175,14 +177,14 @@ public: bool inherits_script(const Ref<Script> &p_script) const override; - const Map<StringName, Ref<GDScript>> &get_subclasses() const { return subclasses; } - const Map<StringName, Variant> &get_constants() const { return constants; } - const Set<StringName> &get_members() const { return members; } + 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; } const GDScriptDataType &get_member_type(const StringName &p_member) const { CRASH_COND(!member_indices.has(p_member)); return member_indices[p_member].data_type; } - const Map<StringName, GDScriptFunction *> &get_member_functions() const { return member_functions; } + const HashMap<StringName, GDScriptFunction *> &get_member_functions() const { return member_functions; } const Ref<GDScriptNativeClass> &get_native() const { return native; } const String &get_script_class_name() const { return name; } @@ -192,8 +194,8 @@ public: bool is_tool() const override { return tool; } Ref<GDScript> get_base() const; - const Map<StringName, MemberInfo> &debug_get_member_indices() const { return member_indices; } - const Map<StringName, GDScriptFunction *> &debug_get_member_functions() const; //this is debug only + const HashMap<StringName, MemberInfo> &debug_get_member_indices() const { return member_indices; } + const HashMap<StringName, GDScriptFunction *> &debug_get_member_functions() const; //this is debug only StringName debug_get_member_by_index(int p_idx) const; Variant _new(const Variant **p_args, int p_argcount, Callable::CallError &r_error); @@ -212,7 +214,7 @@ public: virtual void update_exports() override; #ifdef TOOLS_ENABLED - virtual const Vector<DocData::ClassDoc> &get_documentation() const override { + virtual Vector<DocData::ClassDoc> get_documentation() const override { return docs; } #endif // TOOLS_ENABLED @@ -244,8 +246,8 @@ public: return -1; } - virtual void get_constants(Map<StringName, Variant> *p_constants) override; - virtual void get_members(Set<StringName> *p_members) override; + virtual void get_constants(HashMap<StringName, Variant> *p_constants) override; + virtual void get_members(HashSet<StringName> *p_members) override; virtual const Vector<Multiplayer::RPCConfig> get_rpc_methods() const override; @@ -261,14 +263,15 @@ class GDScriptInstance : public ScriptInstance { friend class GDScript; friend class GDScriptFunction; friend class GDScriptLambdaCallable; + friend class GDScriptLambdaSelfCallable; friend class GDScriptCompiler; friend struct GDScriptUtilityFunctionsDefinitions; ObjectID owner_id; - Object *owner; + Object *owner = nullptr; Ref<GDScript> script; #ifdef DEBUG_ENABLED - Map<StringName, int> member_indices_cache; //used only for hot script reloading + HashMap<StringName, int> member_indices_cache; //used only for hot script reloading #endif Vector<Variant> members; bool base_ref_counted; @@ -311,17 +314,17 @@ class GDScriptLanguage : public ScriptLanguage { static GDScriptLanguage *singleton; - Variant *_global_array; + Variant *_global_array = nullptr; Vector<Variant> global_array; - Map<StringName, int> globals; - Map<StringName, Variant> named_globals; + HashMap<StringName, int> globals; + HashMap<StringName, Variant> named_globals; struct CallLevel { - Variant *stack; - GDScriptFunction *function; - GDScriptInstance *instance; - int *ip; - int *line; + Variant *stack = nullptr; + GDScriptFunction *function = nullptr; + GDScriptInstance *instance = nullptr; + int *ip = nullptr; + int *line = nullptr; }; int _debug_parse_err_line; @@ -329,7 +332,7 @@ class GDScriptLanguage : public ScriptLanguage { String _debug_error; int _debug_call_stack_pos; int _debug_max_call_stack; - CallLevel *_call_stack; + CallLevel *_call_stack = nullptr; void _add_global(const StringName &p_name, const Variant &p_value); @@ -346,7 +349,7 @@ class GDScriptLanguage : public ScriptLanguage { bool profiling; uint64_t script_frame_time; - Map<String, ObjectID> orphan_subclasses; + HashMap<String, ObjectID> orphan_subclasses; public: int calls; @@ -425,8 +428,8 @@ public: _FORCE_INLINE_ int get_global_array_size() const { return global_array.size(); } _FORCE_INLINE_ Variant *get_global_array() { return _global_array; } - _FORCE_INLINE_ const Map<StringName, int> &get_global_map() const { return globals; } - _FORCE_INLINE_ const Map<StringName, Variant> &get_named_globals_map() const { return named_globals; } + _FORCE_INLINE_ const HashMap<StringName, int> &get_global_map() const { return globals; } + _FORCE_INLINE_ const HashMap<StringName, Variant> &get_named_globals_map() const { return named_globals; } _FORCE_INLINE_ static GDScriptLanguage *get_singleton() { return singleton; } @@ -447,7 +450,7 @@ public: virtual bool is_using_templates() override; virtual Ref<Script> make_template(const String &p_template, const String &p_class_name, const String &p_base_class_name) const override; virtual Vector<ScriptTemplate> get_built_in_templates(StringName p_object) override; - virtual bool validate(const String &p_script, const String &p_path = "", List<String> *r_functions = nullptr, List<ScriptLanguage::ScriptError> *r_errors = nullptr, List<ScriptLanguage::Warning> *r_warnings = nullptr, Set<int> *r_safe_lines = nullptr) const override; + virtual bool validate(const String &p_script, const String &p_path = "", List<String> *r_functions = nullptr, List<ScriptLanguage::ScriptError> *r_errors = nullptr, List<ScriptLanguage::Warning> *r_warnings = nullptr, HashSet<int> *r_safe_lines = nullptr) const override; virtual Script *create_script() const override; virtual bool has_named_classes() const override; virtual bool supports_builtin_mode() const override; @@ -455,7 +458,7 @@ public: virtual bool can_inherit_from_file() const override { return true; } virtual int find_function(const String &p_function, const String &p_code) const override; virtual String make_function(const String &p_class, const String &p_name, const PackedStringArray &p_args) const override; - virtual Error complete_code(const String &p_code, const String &p_path, Object *p_owner, List<ScriptCodeCompletionOption> *r_options, bool &r_forced, String &r_call_hint) override; + virtual Error complete_code(const String &p_code, const String &p_path, Object *p_owner, List<ScriptLanguage::CodeCompletionOption> *r_options, bool &r_forced, String &r_call_hint) override; #ifdef TOOLS_ENABLED virtual Error lookup_code(const String &p_code, const String &p_symbol, const String &p_path, Object *p_owner, LookupResult &r_result) override; #endif @@ -510,7 +513,7 @@ public: class ResourceFormatLoaderGDScript : public ResourceFormatLoader { public: - virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, CacheMode p_cache_mode = CACHE_MODE_REUSE); + virtual Ref<Resource> load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, CacheMode p_cache_mode = CACHE_MODE_REUSE); virtual void get_recognized_extensions(List<String> *p_extensions) const; virtual bool handles_type(const String &p_type) const; virtual String get_resource_type(const String &p_path) const; @@ -519,9 +522,9 @@ public: class ResourceFormatSaverGDScript : public ResourceFormatSaver { public: - virtual Error save(const String &p_path, const RES &p_resource, uint32_t p_flags = 0); - virtual void get_recognized_extensions(const RES &p_resource, List<String> *p_extensions) const; - virtual bool recognize(const RES &p_resource) const; + virtual Error save(const String &p_path, const Ref<Resource> &p_resource, uint32_t p_flags = 0); + virtual void get_recognized_extensions(const Ref<Resource> &p_resource, List<String> *p_extensions) const; + virtual bool recognize(const Ref<Resource> &p_resource) const; }; #endif // GDSCRIPT_H diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp index af3d65d4d6..a070d319f3 100644 --- a/modules/gdscript/gdscript_analyzer.cpp +++ b/modules/gdscript/gdscript_analyzer.cpp @@ -647,7 +647,7 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas } } - // Check if initalizer is an unset identifier (ie: a variable within scope, but declared below) + // Check if initializer is an unset identifier (ie: a variable within scope, but declared below) if (member.variable->initializer && !member.variable->initializer->get_datatype().is_set()) { if (member.variable->initializer->type == GDScriptParser::Node::IDENTIFIER) { GDScriptParser::IdentifierNode *initializer_identifier = static_cast<GDScriptParser::IdentifierNode *>(member.variable->initializer); @@ -898,7 +898,7 @@ void GDScriptAnalyzer::resolve_class_body(GDScriptParser::ClassNode *p_class) { } #ifdef DEBUG_ENABLED - Set<uint32_t> previously_ignored = parser->ignored_warning_codes; + HashSet<uint32_t> previously_ignored = parser->ignored_warning_codes; for (uint32_t ignored_warning : member.function->ignored_warnings) { parser->ignored_warning_codes.insert(ignored_warning); } @@ -947,7 +947,7 @@ void GDScriptAnalyzer::resolve_class_body(GDScriptParser::ClassNode *p_class) { GDScriptParser::ClassNode::Member member = p_class->members[i]; if (member.type == GDScriptParser::ClassNode::Member::VARIABLE) { #ifdef DEBUG_ENABLED - Set<uint32_t> previously_ignored = parser->ignored_warning_codes; + HashSet<uint32_t> previously_ignored = parser->ignored_warning_codes; for (uint32_t ignored_warning : member.function->ignored_warnings) { parser->ignored_warning_codes.insert(ignored_warning); } @@ -1160,8 +1160,16 @@ void GDScriptAnalyzer::resolve_function_signature(GDScriptParser::FunctionNode * } } } else { - GDScriptParser::DataType return_type = resolve_datatype(p_function->return_type); - p_function->set_datatype(return_type); + if (p_function->return_type != nullptr) { + p_function->set_datatype(resolve_datatype(p_function->return_type)); + } else { + // In case the function is not typed, we can safely assume it's a Variant, so it's okay to mark as "inferred" here. + // It's not "undetected" to not mix up with unknown functions. + GDScriptParser::DataType return_type; + return_type.type_source = GDScriptParser::DataType::INFERRED; + return_type.kind = GDScriptParser::DataType::VARIANT; + p_function->set_datatype(return_type); + } #ifdef TOOLS_ENABLED // Check if the function signature matches the parent. If not it's an error since it breaks polymorphism. @@ -1231,7 +1239,7 @@ void GDScriptAnalyzer::resolve_function_body(GDScriptParser::FunctionNode *p_fun GDScriptParser::DataType return_type = p_function->body->get_datatype(); - if (p_function->get_datatype().has_no_type() && return_type.is_set()) { + if (!p_function->get_datatype().is_hard_type() && return_type.is_set()) { // Use the suite inferred type if return isn't explicitly set. return_type.type_source = GDScriptParser::DataType::INFERRED; p_function->set_datatype(p_function->body->get_datatype()); @@ -1279,7 +1287,7 @@ void GDScriptAnalyzer::resolve_suite(GDScriptParser::SuiteNode *p_suite) { } #ifdef DEBUG_ENABLED - Set<uint32_t> previously_ignored = parser->ignored_warning_codes; + HashSet<uint32_t> previously_ignored = parser->ignored_warning_codes; for (uint32_t ignored_warning : stmt->ignored_warnings) { parser->ignored_warning_codes.insert(ignored_warning); } @@ -1514,10 +1522,22 @@ void GDScriptAnalyzer::resolve_variable(GDScriptParser::VariableNode *p_variable void GDScriptAnalyzer::resolve_constant(GDScriptParser::ConstantNode *p_constant) { GDScriptParser::DataType type; + GDScriptParser::DataType explicit_type; + if (p_constant->datatype_specifier != nullptr) { + explicit_type = resolve_datatype(p_constant->datatype_specifier); + explicit_type.is_meta_type = false; + } + if (p_constant->initializer != nullptr) { reduce_expression(p_constant->initializer); if (p_constant->initializer->type == GDScriptParser::Node::ARRAY) { - const_fold_array(static_cast<GDScriptParser::ArrayNode *>(p_constant->initializer)); + GDScriptParser::ArrayNode *array = static_cast<GDScriptParser::ArrayNode *>(p_constant->initializer); + const_fold_array(array); + + // Can only infer typed array if it has elements. + if (array->elements.size() > 0 || (p_constant->datatype_specifier != nullptr && explicit_type.has_container_element_type())) { + update_array_literal_element_type(explicit_type, array); + } } else if (p_constant->initializer->type == GDScriptParser::Node::DICTIONARY) { const_fold_dictionary(static_cast<GDScriptParser::DictionaryNode *>(p_constant->initializer)); } @@ -1536,8 +1556,6 @@ void GDScriptAnalyzer::resolve_constant(GDScriptParser::ConstantNode *p_constant } if (p_constant->datatype_specifier != nullptr) { - GDScriptParser::DataType explicit_type = resolve_datatype(p_constant->datatype_specifier); - explicit_type.is_meta_type = false; if (!is_type_compatible(explicit_type, type)) { push_error(vformat(R"(Assigned value for constant "%s" has type %s which is not compatible with defined type %s.)", p_constant->identifier->name, type.to_string(), explicit_type.to_string()), p_constant->initializer); #ifdef DEBUG_ENABLED @@ -1639,8 +1657,8 @@ void GDScriptAnalyzer::resolve_match_pattern(GDScriptParser::PatternNode *p_matc p_match_pattern->bind->set_datatype(result); #ifdef DEBUG_ENABLED is_shadowing(p_match_pattern->bind, "pattern bind"); - if (p_match_pattern->bind->usages == 0) { - parser->push_warning(p_match_pattern->bind, GDScriptWarning::UNASSIGNED_VARIABLE, p_match_pattern->bind->name); + if (p_match_pattern->bind->usages == 0 && !String(p_match_pattern->bind->name).begins_with("_")) { + parser->push_warning(p_match_pattern->bind, GDScriptWarning::UNUSED_VARIABLE, p_match_pattern->bind->name); } #endif break; @@ -2057,7 +2075,8 @@ void GDScriptAnalyzer::reduce_await(GDScriptParser::AwaitNode *p_await) { p_await->set_datatype(awaiting_type); #ifdef DEBUG_ENABLED - if (!awaiting_type.is_coroutine && awaiting_type.builtin_type != Variant::SIGNAL) { + awaiting_type = p_await->to_await->get_datatype(); + if (!(awaiting_type.has_no_type() || awaiting_type.is_coroutine || awaiting_type.builtin_type == Variant::SIGNAL)) { parser->push_warning(p_await, GDScriptWarning::REDUNDANT_AWAIT); } #endif @@ -2174,7 +2193,7 @@ void GDScriptAnalyzer::reduce_binary_op(GDScriptParser::BinaryOpNode *p_binary_o void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_await, bool p_is_root) { bool all_is_constant = true; - Map<int, GDScriptParser::ArrayNode *> arrays; // For array literal to potentially type when passing. + HashMap<int, GDScriptParser::ArrayNode *> arrays; // For array literal to potentially type when passing. for (int i = 0; i < p_call->arguments.size(); i++) { reduce_expression(p_call->arguments[i]); if (p_call->arguments[i]->type == GDScriptParser::Node::ARRAY) { @@ -2303,7 +2322,7 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a break; #ifdef DEBUG_ENABLED } else { - if (par_type.builtin_type == Variant::INT && p_call->arguments[i]->get_datatype().builtin_type == Variant::FLOAT) { + if (par_type.builtin_type == Variant::INT && p_call->arguments[i]->get_datatype().builtin_type == Variant::FLOAT && builtin_type != Variant::INT) { parser->push_warning(p_call, GDScriptWarning::NARROWING_CONVERSION, p_call->function_name); } #endif @@ -2426,6 +2445,10 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a base_type = parser->current_class->base_type; base_type.is_meta_type = false; is_self = true; + + if (p_call->callee == nullptr && !lambda_stack.is_empty()) { + push_error("Cannot use `super()` inside a lambda.", p_call); + } } else if (callee_type == GDScriptParser::Node::IDENTIFIER) { base_type = parser->current_class->get_datatype(); base_type.is_meta_type = false; @@ -2494,18 +2517,24 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a } if (is_self && parser->current_function != nullptr && parser->current_function->is_static && !is_static) { - push_error(vformat(R"*(Cannot call non-static function "%s()" from static function "%s()".)*", p_call->function_name, parser->current_function->identifier->name), p_call->callee); + // Get the parent function above any lambda. + GDScriptParser::FunctionNode *parent_function = parser->current_function; + while (parent_function->source_lambda) { + parent_function = parent_function->source_lambda->parent_function; + } + push_error(vformat(R"*(Cannot call non-static function "%s()" from static function "%s()".)*", p_call->function_name, parent_function->identifier->name), p_call); } else if (!is_self && base_type.is_meta_type && !is_static) { base_type.is_meta_type = false; // For `to_string()`. - push_error(vformat(R"*(Cannot call non-static function "%s()" on the class "%s" directly. Make an instance instead.)*", p_call->function_name, base_type.to_string()), p_call->callee); - } else if (is_self && !is_static && !lambda_stack.is_empty()) { - push_error(vformat(R"*(Cannot call non-static function "%s()" from a lambda function.)*", p_call->function_name), p_call->callee); + push_error(vformat(R"*(Cannot call non-static function "%s()" on the class "%s" directly. Make an instance instead.)*", p_call->function_name, base_type.to_string()), p_call); + } else if (is_self && !is_static) { + mark_lambda_use_self(); } call_type = return_type; } else { - // Check if the name exists as something else. bool found = false; + + // Check if the name exists as something else. if (!p_call->is_super && callee_type != GDScriptParser::Node::NONE) { GDScriptParser::IdentifierNode *callee_id; if (callee_type == GDScriptParser::Node::IDENTIFIER) { @@ -2535,11 +2564,13 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a if (!found && (is_self || (base_type.is_hard_type() && base_type.kind == GDScriptParser::DataType::BUILTIN))) { String base_name = is_self && !p_call->is_super ? "self" : base_type.to_string(); push_error(vformat(R"*(Function "%s()" not found in base %s.)*", p_call->function_name, base_name), p_call->is_super ? p_call : p_call->callee); + } else if (!found && (!p_call->is_super && base_type.is_hard_type() && base_type.kind == GDScriptParser::DataType::NATIVE && base_type.is_meta_type)) { + push_error(vformat(R"*(Static function "%s()" not found in base "%s".)*", p_call->function_name, base_type.native_type.operator String()), p_call); } } if (call_type.is_coroutine && !p_is_await && !p_is_root) { - push_error(vformat(R"*(Function "%s()" is a coroutine, so it must be called with "await".)*", p_call->function_name), p_call->callee); + push_error(vformat(R"*(Function "%s()" is a coroutine, so it must be called with "await".)*", p_call->function_name), p_call); } p_call->set_datatype(call_type); @@ -2629,10 +2660,10 @@ void GDScriptAnalyzer::reduce_get_node(GDScriptParser::GetNodeNode *p_get_node) if (!ClassDB::is_parent_class(parser->current_class->base_type.native_type, result.native_type)) { push_error(R"*(Cannot use shorthand "get_node()" notation ("$") on a class that isn't a node.)*", p_get_node); - } else if (!lambda_stack.is_empty()) { - push_error(R"*(Cannot use shorthand "get_node()" notation ("$") inside a lambda. Use a captured variable instead.)*", p_get_node); } + mark_lambda_use_self(); + p_get_node->set_datatype(result); } @@ -2847,21 +2878,25 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod MethodBind *getter = ClassDB::get_method(native, getter_name); if (getter != nullptr) { p_identifier->set_datatype(type_from_property(getter->get_return_info())); + p_identifier->source = GDScriptParser::IdentifierNode::INHERITED_VARIABLE; } return; } if (ClassDB::get_method_info(native, name, &method_info)) { // Method is callable. p_identifier->set_datatype(make_callable_type(method_info)); + p_identifier->source = GDScriptParser::IdentifierNode::INHERITED_VARIABLE; return; } if (ClassDB::get_signal(native, name, &method_info)) { // Signal is a type too. p_identifier->set_datatype(make_signal_type(method_info)); + p_identifier->source = GDScriptParser::IdentifierNode::INHERITED_VARIABLE; return; } if (ClassDB::has_enum(native, name)) { p_identifier->set_datatype(make_native_enum_type(native, name)); + p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_CONSTANT; return; } bool valid = false; @@ -2870,6 +2905,7 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod p_identifier->is_constant = true; p_identifier->reduced_value = int_constant; p_identifier->set_datatype(type_from_variant(int_constant, p_identifier)); + p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_CONSTANT; return; } } @@ -2920,7 +2956,11 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident p_identifier->reduced_value = p_identifier->constant_source->initializer->reduced_value; found_source = true; break; + case GDScriptParser::IdentifierNode::INHERITED_VARIABLE: + mark_lambda_use_self(); + break; case GDScriptParser::IdentifierNode::MEMBER_VARIABLE: + mark_lambda_use_self(); p_identifier->variable_source->usages++; [[fallthrough]]; case GDScriptParser::IdentifierNode::LOCAL_VARIABLE: @@ -2951,18 +2991,37 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident } if (found_source) { - // If the identifier is local, check if it's any kind of capture by comparing their source function. - // Only capture locals and members and enum values. Constants are still accessible from the lambda using the script reference. - if (p_identifier->source == GDScriptParser::IdentifierNode::UNDEFINED_SOURCE || p_identifier->source == GDScriptParser::IdentifierNode::MEMBER_CONSTANT || lambda_stack.is_empty()) { - return; + if ((p_identifier->source == GDScriptParser::IdentifierNode::MEMBER_VARIABLE || p_identifier->source == GDScriptParser::IdentifierNode::INHERITED_VARIABLE) && parser->current_function && parser->current_function->is_static) { + // Get the parent function above any lambda. + GDScriptParser::FunctionNode *parent_function = parser->current_function; + while (parent_function->source_lambda) { + parent_function = parent_function->source_lambda->parent_function; + } + push_error(vformat(R"*(Cannot access instance variable "%s" from the static function "%s()".)*", p_identifier->name, parent_function->identifier->name), p_identifier); } - GDScriptParser::FunctionNode *function_test = lambda_stack.back()->get()->function; - while (function_test != nullptr && function_test != p_identifier->source_function && function_test->source_lambda != nullptr && !function_test->source_lambda->captures_indices.has(p_identifier->name)) { - function_test->source_lambda->captures_indices[p_identifier->name] = function_test->source_lambda->captures.size(); - function_test->source_lambda->captures.push_back(p_identifier); - function_test = function_test->source_lambda->parent_function; + if (!lambda_stack.is_empty()) { + // If the identifier is a member variable (including the native class properties), we consider the lambda to be using `self`, so we keep a reference to the current instance. + if (p_identifier->source == GDScriptParser::IdentifierNode::MEMBER_VARIABLE || p_identifier->source == GDScriptParser::IdentifierNode::INHERITED_VARIABLE) { + mark_lambda_use_self(); + return; // No need to capture. + } + // If the identifier is local, check if it's any kind of capture by comparing their source function. + // Only capture locals and enum values. Constants are still accessible from the lambda using the script reference. If not, this method is done. + if (p_identifier->source == GDScriptParser::IdentifierNode::UNDEFINED_SOURCE || p_identifier->source == GDScriptParser::IdentifierNode::MEMBER_CONSTANT) { + return; + } + + GDScriptParser::FunctionNode *function_test = lambda_stack.back()->get()->function; + // Make sure we aren't capturing variable in the same lambda. + // This also add captures for nested lambdas. + while (function_test != nullptr && function_test != p_identifier->source_function && function_test->source_lambda != nullptr && !function_test->source_lambda->captures_indices.has(p_identifier->name)) { + function_test->source_lambda->captures_indices[p_identifier->name] = function_test->source_lambda->captures.size(); + function_test->source_lambda->captures.push_back(p_identifier); + function_test = function_test->source_lambda->parent_function; + } } + return; } @@ -3142,6 +3201,7 @@ void GDScriptAnalyzer::reduce_preload(GDScriptParser::PreloadNode *p_preload) { void GDScriptAnalyzer::reduce_self(GDScriptParser::SelfNode *p_self) { p_self->is_constant = false; p_self->set_datatype(type_from_metatype(parser->current_class->get_datatype())); + mark_lambda_use_self(); } void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscript) { @@ -3152,6 +3212,12 @@ void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscri reduce_identifier(static_cast<GDScriptParser::IdentifierNode *>(p_subscript->base), true); } else { reduce_expression(p_subscript->base); + + if (p_subscript->base->type == GDScriptParser::Node::ARRAY) { + const_fold_array(static_cast<GDScriptParser::ArrayNode *>(p_subscript->base)); + } else if (p_subscript->base->type == GDScriptParser::Node::DICTIONARY) { + const_fold_dictionary(static_cast<GDScriptParser::DictionaryNode *>(p_subscript->base)); + } } GDScriptParser::DataType result_type; @@ -3469,6 +3535,13 @@ void GDScriptAnalyzer::const_fold_array(GDScriptParser::ArrayNode *p_array) { for (int i = 0; i < p_array->elements.size(); i++) { GDScriptParser::ExpressionNode *element = p_array->elements[i]; + + if (element->type == GDScriptParser::Node::ARRAY) { + const_fold_array(static_cast<GDScriptParser::ArrayNode *>(element)); + } else if (element->type == GDScriptParser::Node::DICTIONARY) { + const_fold_dictionary(static_cast<GDScriptParser::DictionaryNode *>(element)); + } + all_is_constant = all_is_constant && element->is_constant; if (!all_is_constant) { return; @@ -3489,6 +3562,13 @@ void GDScriptAnalyzer::const_fold_dictionary(GDScriptParser::DictionaryNode *p_d for (int i = 0; i < p_dictionary->elements.size(); i++) { const GDScriptParser::DictionaryNode::Pair &element = p_dictionary->elements[i]; + + if (element.value->type == GDScriptParser::Node::ARRAY) { + const_fold_array(static_cast<GDScriptParser::ArrayNode *>(element.value)); + } else if (element.value->type == GDScriptParser::Node::DICTIONARY) { + const_fold_dictionary(static_cast<GDScriptParser::DictionaryNode *>(element.value)); + } + all_is_constant = all_is_constant && element.key->is_constant && element.value->is_constant; if (!all_is_constant) { return; @@ -3769,6 +3849,7 @@ bool GDScriptAnalyzer::function_signature_from_info(const MethodInfo &p_info, GD r_return_type = type_from_property(p_info.return_val); r_default_arg_count = p_info.default_arguments.size(); r_vararg = (p_info.flags & METHOD_FLAG_VARARG) != 0; + r_static = (p_info.flags & METHOD_FLAG_STATIC) != 0; for (const PropertyInfo &E : p_info.arguments) { r_par_types.push_back(type_from_property(E)); @@ -4113,6 +4194,12 @@ void GDScriptAnalyzer::mark_node_unsafe(const GDScriptParser::Node *p_node) { #endif } +void GDScriptAnalyzer::mark_lambda_use_self() { + for (GDScriptParser::LambdaNode *lambda : lambda_stack) { + lambda->use_self = true; + } +} + bool GDScriptAnalyzer::class_exists(const StringName &p_class) const { return ClassDB::class_exists(p_class) && ClassDB::is_class_exposed(p_class); } @@ -4150,13 +4237,11 @@ Error GDScriptAnalyzer::resolve_program() { resolve_class_interface(parser->head); resolve_class_body(parser->head); - List<String> parser_keys; - depended_parsers.get_key_list(&parser_keys); - for (const String &E : parser_keys) { - if (depended_parsers[E].is_null()) { + for (KeyValue<String, Ref<GDScriptParserRef>> &K : depended_parsers) { + if (K.value.is_null()) { return ERR_PARSE_ERROR; } - depended_parsers[E]->raise_status(GDScriptParserRef::FULLY_SOLVED); + K.value->raise_status(GDScriptParserRef::FULLY_SOLVED); } return parser->errors.is_empty() ? OK : ERR_PARSE_ERROR; } diff --git a/modules/gdscript/gdscript_analyzer.h b/modules/gdscript/gdscript_analyzer.h index 7b8883e1d3..3966b81b6e 100644 --- a/modules/gdscript/gdscript_analyzer.h +++ b/modules/gdscript/gdscript_analyzer.h @@ -33,7 +33,7 @@ #include "core/object/object.h" #include "core/object/ref_counted.h" -#include "core/templates/set.h" +#include "core/templates/hash_set.h" #include "gdscript_cache.h" #include "gdscript_parser.h" @@ -42,7 +42,7 @@ class GDScriptAnalyzer { HashMap<String, Ref<GDScriptParserRef>> depended_parsers; const GDScriptParser::EnumNode *current_enum = nullptr; - List<const GDScriptParser::LambdaNode *> lambda_stack; + List<GDScriptParser::LambdaNode *> lambda_stack; // Tests for detecting invalid overloading of script members static _FORCE_INLINE_ bool has_member_name_conflict_in_script_class(const StringName &p_name, const GDScriptParser::ClassNode *p_current_class_node); @@ -115,6 +115,7 @@ class GDScriptAnalyzer { bool is_type_compatible(const GDScriptParser::DataType &p_target, const GDScriptParser::DataType &p_source, bool p_allow_implicit_conversion = false, const GDScriptParser::Node *p_source_node = nullptr); void push_error(const String &p_message, const GDScriptParser::Node *p_origin); void mark_node_unsafe(const GDScriptParser::Node *p_node); + void mark_lambda_use_self(); bool class_exists(const StringName &p_class) const; Ref<GDScriptParserRef> get_parser_for(const String &p_path); #ifdef DEBUG_ENABLED diff --git a/modules/gdscript/gdscript_byte_codegen.cpp b/modules/gdscript/gdscript_byte_codegen.cpp index 82aa14795e..3d5a39bf38 100644 --- a/modules/gdscript/gdscript_byte_codegen.cpp +++ b/modules/gdscript/gdscript_byte_codegen.cpp @@ -196,10 +196,8 @@ GDScriptFunction *GDScriptByteCodeGenerator::write_end() { function->_constant_count = constant_map.size(); function->constants.resize(constant_map.size()); function->_constants_ptr = function->constants.ptrw(); - const Variant *K = nullptr; - while ((K = constant_map.next(K))) { - int idx = constant_map[*K]; - function->constants.write[idx] = *K; + for (const KeyValue<Variant, int> &K : constant_map) { + function->constants.write[K.value] = K.key; } } else { function->_constants_ptr = nullptr; @@ -468,7 +466,7 @@ void GDScriptByteCodeGenerator::write_type_adjust(const Address &p_target, Varia append(GDScriptFunction::OPCODE_TYPE_ADJUST_BASIS, 1); break; case Variant::TRANSFORM3D: - append(GDScriptFunction::OPCODE_TYPE_ADJUST_TRANSFORM, 1); + append(GDScriptFunction::OPCODE_TYPE_ADJUST_TRANSFORM3D, 1); break; case Variant::COLOR: append(GDScriptFunction::OPCODE_TYPE_ADJUST_COLOR, 1); @@ -1080,6 +1078,24 @@ void GDScriptByteCodeGenerator::write_call_builtin_type_static(const Address &p_ append(Variant::get_validated_builtin_method(p_type, p_method)); } +void GDScriptByteCodeGenerator::write_call_native_static(const Address &p_target, const StringName &p_class, const StringName &p_method, const Vector<Address> &p_arguments) { + bool is_validated = false; + + MethodBind *method = ClassDB::get_method(p_class, p_method); + + if (!is_validated) { + // Perform regular call. + append(GDScriptFunction::OPCODE_CALL_NATIVE_STATIC, p_arguments.size() + 1); + for (int i = 0; i < p_arguments.size(); i++) { + append(p_arguments[i]); + } + append(p_target); + append(method); + append(p_arguments.size()); + return; + } +} + void GDScriptByteCodeGenerator::write_call_method_bind(const Address &p_target, const Address &p_base, MethodBind *p_method, const Vector<Address> &p_arguments) { append(p_target.mode == Address::NIL ? GDScriptFunction::OPCODE_CALL_METHOD_BIND : GDScriptFunction::OPCODE_CALL_METHOD_BIND_RET, 2 + p_arguments.size()); for (int i = 0; i < p_arguments.size(); i++) { @@ -1193,8 +1209,8 @@ void GDScriptByteCodeGenerator::write_call_script_function(const Address &p_targ append(p_function_name); } -void GDScriptByteCodeGenerator::write_lambda(const Address &p_target, GDScriptFunction *p_function, const Vector<Address> &p_captures) { - append(GDScriptFunction::OPCODE_CREATE_LAMBDA, 1 + p_captures.size()); +void GDScriptByteCodeGenerator::write_lambda(const Address &p_target, GDScriptFunction *p_function, const Vector<Address> &p_captures, bool p_use_self) { + append(p_use_self ? GDScriptFunction::OPCODE_CREATE_SELF_LAMBDA : GDScriptFunction::OPCODE_CREATE_LAMBDA, 1 + p_captures.size()); for (int i = 0; i < p_captures.size(); i++) { append(p_captures[i]); } diff --git a/modules/gdscript/gdscript_byte_codegen.h b/modules/gdscript/gdscript_byte_codegen.h index db15dc55ef..6ee8fda533 100644 --- a/modules/gdscript/gdscript_byte_codegen.h +++ b/modules/gdscript/gdscript_byte_codegen.h @@ -53,19 +53,19 @@ class GDScriptByteCodeGenerator : public GDScriptCodeGenerator { bool debug_stack = false; Vector<int> opcodes; - List<Map<StringName, int>> stack_id_stack; - Map<StringName, int> stack_identifiers; + List<RBMap<StringName, int>> stack_id_stack; + RBMap<StringName, int> stack_identifiers; List<int> stack_identifiers_counts; - Map<StringName, int> local_constants; + RBMap<StringName, int> local_constants; Vector<StackSlot> locals; Vector<StackSlot> temporaries; List<int> used_temporaries; - Map<Variant::Type, List<int>> temporaries_pool; + RBMap<Variant::Type, List<int>> temporaries_pool; List<GDScriptFunction::StackDebug> stack_debug; - List<Map<StringName, int>> block_identifier_stack; - Map<StringName, int> block_identifiers; + List<RBMap<StringName, int>> block_identifier_stack; + RBMap<StringName, int> block_identifiers; int max_locals = 0; int current_line = 0; @@ -77,23 +77,23 @@ class GDScriptByteCodeGenerator : public GDScriptCodeGenerator { #endif HashMap<Variant, int, VariantHasher, VariantComparator> constant_map; - Map<StringName, int> name_map; + RBMap<StringName, int> name_map; #ifdef TOOLS_ENABLED Vector<StringName> named_globals; #endif - Map<Variant::ValidatedOperatorEvaluator, int> operator_func_map; - Map<Variant::ValidatedSetter, int> setters_map; - Map<Variant::ValidatedGetter, int> getters_map; - Map<Variant::ValidatedKeyedSetter, int> keyed_setters_map; - Map<Variant::ValidatedKeyedGetter, int> keyed_getters_map; - Map<Variant::ValidatedIndexedSetter, int> indexed_setters_map; - Map<Variant::ValidatedIndexedGetter, int> indexed_getters_map; - Map<Variant::ValidatedBuiltInMethod, int> builtin_method_map; - Map<Variant::ValidatedConstructor, int> constructors_map; - Map<Variant::ValidatedUtilityFunction, int> utilities_map; - Map<GDScriptUtilityFunctions::FunctionPtr, int> gds_utilities_map; - Map<MethodBind *, int> method_bind_map; - Map<GDScriptFunction *, int> lambdas_map; + RBMap<Variant::ValidatedOperatorEvaluator, int> operator_func_map; + RBMap<Variant::ValidatedSetter, int> setters_map; + RBMap<Variant::ValidatedGetter, int> getters_map; + RBMap<Variant::ValidatedKeyedSetter, int> keyed_setters_map; + RBMap<Variant::ValidatedKeyedGetter, int> keyed_getters_map; + RBMap<Variant::ValidatedIndexedSetter, int> indexed_setters_map; + RBMap<Variant::ValidatedIndexedGetter, int> indexed_getters_map; + RBMap<Variant::ValidatedBuiltInMethod, int> builtin_method_map; + RBMap<Variant::ValidatedConstructor, int> constructors_map; + RBMap<Variant::ValidatedUtilityFunction, int> utilities_map; + RBMap<GDScriptUtilityFunctions::FunctionPtr, int> gds_utilities_map; + RBMap<MethodBind *, int> method_bind_map; + RBMap<GDScriptFunction *, int> lambdas_map; // Lists since these can be nested. List<int> if_jmp_addrs; @@ -135,7 +135,7 @@ class GDScriptByteCodeGenerator : public GDScriptCodeGenerator { stack_identifiers_counts.push_back(locals.size()); stack_id_stack.push_back(stack_identifiers); if (debug_stack) { - Map<StringName, int> block_ids(block_identifiers); + RBMap<StringName, int> block_ids(block_identifiers); block_identifier_stack.push_back(block_ids); block_identifiers.clear(); } @@ -464,12 +464,13 @@ public: virtual void write_call_gdscript_utility(const Address &p_target, GDScriptUtilityFunctions::FunctionPtr p_function, const Vector<Address> &p_arguments) override; virtual void write_call_builtin_type(const Address &p_target, const Address &p_base, Variant::Type p_type, const StringName &p_method, const Vector<Address> &p_arguments) override; virtual void write_call_builtin_type_static(const Address &p_target, Variant::Type p_type, const StringName &p_method, const Vector<Address> &p_arguments) override; + virtual void write_call_native_static(const Address &p_target, const StringName &p_class, const StringName &p_method, const Vector<Address> &p_arguments) override; virtual void write_call_method_bind(const Address &p_target, const Address &p_base, MethodBind *p_method, const Vector<Address> &p_arguments) override; virtual void write_call_ptrcall(const Address &p_target, const Address &p_base, MethodBind *p_method, const Vector<Address> &p_arguments) override; virtual void write_call_self(const Address &p_target, const StringName &p_function_name, const Vector<Address> &p_arguments) override; virtual void write_call_self_async(const Address &p_target, const StringName &p_function_name, const Vector<Address> &p_arguments) override; virtual void write_call_script_function(const Address &p_target, const Address &p_base, const StringName &p_function_name, const Vector<Address> &p_arguments) override; - virtual void write_lambda(const Address &p_target, GDScriptFunction *p_function, const Vector<Address> &p_captures) override; + virtual void write_lambda(const Address &p_target, GDScriptFunction *p_function, const Vector<Address> &p_captures, bool p_use_self) override; virtual void write_construct(const Address &p_target, Variant::Type p_type, const Vector<Address> &p_arguments) override; virtual void write_construct_array(const Address &p_target, const Vector<Address> &p_arguments) override; virtual void write_construct_typed_array(const Address &p_target, const GDScriptDataType &p_element_type, const Vector<Address> &p_arguments) override; diff --git a/modules/gdscript/gdscript_cache.cpp b/modules/gdscript/gdscript_cache.cpp index 6ada7d36f5..4c15fca91e 100644 --- a/modules/gdscript/gdscript_cache.cpp +++ b/modules/gdscript/gdscript_cache.cpp @@ -145,7 +145,7 @@ Ref<GDScriptParserRef> GDScriptCache::get_parser(const String &p_path, GDScriptP String GDScriptCache::get_source_code(const String &p_path) { Vector<uint8_t> source_file; Error err; - FileAccessRef f = FileAccess::open(p_path, FileAccess::READ, &err); + Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ, &err); if (err) { ERR_FAIL_COND_V(err, ""); } @@ -153,7 +153,6 @@ String GDScriptCache::get_source_code(const String &p_path) { uint64_t len = f->get_length(); source_file.resize(len + 1); uint64_t r = f->get_buffer(source_file.ptrw(), len); - f->close(); ERR_FAIL_COND_V(r != len, ""); source_file.write[len] = 0; @@ -224,13 +223,13 @@ Error GDScriptCache::finish_compiling(const String &p_owner) { singleton->full_gdscript_cache[p_owner] = script.ptr(); singleton->shallow_gdscript_cache.erase(p_owner); - Set<String> depends = singleton->dependencies[p_owner]; + HashSet<String> depends = singleton->dependencies[p_owner]; Error err = OK; - for (const Set<String>::Element *E = depends.front(); E != nullptr; E = E->next()) { + for (const String &E : depends) { Error this_err = OK; // No need to save the script. We assume it's already referenced in the owner. - get_full_script(E->get(), this_err); + get_full_script(E, this_err); if (this_err != OK) { err = this_err; diff --git a/modules/gdscript/gdscript_cache.h b/modules/gdscript/gdscript_cache.h index 3ce976ee14..b971bdd984 100644 --- a/modules/gdscript/gdscript_cache.h +++ b/modules/gdscript/gdscript_cache.h @@ -34,7 +34,7 @@ #include "core/object/ref_counted.h" #include "core/os/mutex.h" #include "core/templates/hash_map.h" -#include "core/templates/set.h" +#include "core/templates/hash_set.h" #include "gdscript.h" class GDScriptAnalyzer; @@ -74,7 +74,7 @@ class GDScriptCache { HashMap<String, GDScriptParserRef *> parser_map; HashMap<String, GDScript *> shallow_gdscript_cache; HashMap<String, GDScript *> full_gdscript_cache; - HashMap<String, Set<String>> dependencies; + HashMap<String, HashSet<String>> dependencies; friend class GDScript; friend class GDScriptParserRef; diff --git a/modules/gdscript/gdscript_codegen.h b/modules/gdscript/gdscript_codegen.h index 4542dd94ae..326b66a295 100644 --- a/modules/gdscript/gdscript_codegen.h +++ b/modules/gdscript/gdscript_codegen.h @@ -125,12 +125,13 @@ public: virtual void write_call_gdscript_utility(const Address &p_target, GDScriptUtilityFunctions::FunctionPtr p_function, const Vector<Address> &p_arguments) = 0; virtual void write_call_builtin_type(const Address &p_target, const Address &p_base, Variant::Type p_type, const StringName &p_method, const Vector<Address> &p_arguments) = 0; virtual void write_call_builtin_type_static(const Address &p_target, Variant::Type p_type, const StringName &p_method, const Vector<Address> &p_arguments) = 0; + virtual void write_call_native_static(const Address &p_target, const StringName &p_class, const StringName &p_method, const Vector<Address> &p_arguments) = 0; virtual void write_call_method_bind(const Address &p_target, const Address &p_base, MethodBind *p_method, const Vector<Address> &p_arguments) = 0; virtual void write_call_ptrcall(const Address &p_target, const Address &p_base, MethodBind *p_method, const Vector<Address> &p_arguments) = 0; virtual void write_call_self(const Address &p_target, const StringName &p_function_name, const Vector<Address> &p_arguments) = 0; virtual void write_call_self_async(const Address &p_target, const StringName &p_function_name, const Vector<Address> &p_arguments) = 0; virtual void write_call_script_function(const Address &p_target, const Address &p_base, const StringName &p_function_name, const Vector<Address> &p_arguments) = 0; - virtual void write_lambda(const Address &p_target, GDScriptFunction *p_function, const Vector<Address> &p_captures) = 0; + virtual void write_lambda(const Address &p_target, GDScriptFunction *p_function, const Vector<Address> &p_captures, bool p_use_self) = 0; virtual void write_construct(const Address &p_target, Variant::Type p_type, const Vector<Address> &p_arguments) = 0; virtual void write_construct_array(const Address &p_target, const Vector<Address> &p_arguments) = 0; virtual void write_construct_typed_array(const Address &p_target, const GDScriptDataType &p_element_type, const Vector<Address> &p_arguments) = 0; diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp index 8190eecbc7..b2cce9d8ee 100644 --- a/modules/gdscript/gdscript_compiler.cpp +++ b/modules/gdscript/gdscript_compiler.cpp @@ -35,6 +35,7 @@ #include "gdscript_cache.h" #include "gdscript_utility_functions.h" +#include "core/config/engine.h" #include "core/config/project_settings.h" bool GDScriptCompiler::_is_class_member_property(CodeGen &codegen, const StringName &p_name) { @@ -107,11 +108,15 @@ GDScriptDataType GDScriptCompiler::_gdtype_from_datatype(const GDScriptParser::D result.native_type = result.script_type->get_instance_base_type(); } break; case GDScriptParser::DataType::CLASS: { - // Locate class by constructing the path to it and following that path + // Locate class by constructing the path to it and following that path. GDScriptParser::ClassNode *class_type = p_datatype.class_type; if (class_type) { - const bool is_inner_by_path = (!main_script->path.is_empty()) && (class_type->fqcn.split("::")[0] == main_script->path); - const bool is_inner_by_name = (!main_script->name.is_empty()) && (class_type->fqcn.split("::")[0] == main_script->name); + 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; @@ -130,16 +135,41 @@ GDScriptDataType GDScriptCompiler::_gdtype_from_datatype(const GDScriptParser::D script = script->subclasses[names.back()->get()]; names.pop_back(); } - result.kind = GDScriptDataType::GDSCRIPT; result.script_type = script.ptr(); result.native_type = script->get_instance_base_type(); - result.builtin_type = p_datatype.builtin_type; } else { - result.kind = GDScriptDataType::GDSCRIPT; - result.script_type_ref = GDScriptCache::get_shallow_script(p_datatype.script_path, main_script->path); + // 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); + } + result.script_type = result.script_type_ref.ptr(); result.native_type = p_datatype.native_type; - result.builtin_type = p_datatype.builtin_type; } } } break; @@ -335,7 +365,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code if (GDScriptLanguage::get_singleton()->get_global_map().has(identifier)) { // If it's an autoload singleton, we postpone to load it at runtime. // This is so one autoload doesn't try to load another before it's compiled. - OrderedHashMap<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list(); + 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())); int idx = GDScriptLanguage::get_singleton()->get_global_map()[identifier]; @@ -355,7 +385,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code class_node = class_node->outer; } - RES res; + Ref<Resource> res; if (class_node->identifier && class_node->identifier->name == identifier) { res = Ref<GDScript>(main_script); @@ -575,6 +605,10 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code // May be static built-in method call. if (!call->is_super && subscript->base->type == GDScriptParser::Node::IDENTIFIER && GDScriptParser::get_builtin_type(static_cast<GDScriptParser::IdentifierNode *>(subscript->base)->name) < Variant::VARIANT_MAX) { gen->write_call_builtin_type_static(result, GDScriptParser::get_builtin_type(static_cast<GDScriptParser::IdentifierNode *>(subscript->base)->name), subscript->attribute->name, arguments); + } else if (!call->is_super && subscript->base->type == GDScriptParser::Node::IDENTIFIER && call->function_name != SNAME("new") && + ClassDB::class_exists(static_cast<GDScriptParser::IdentifierNode *>(subscript->base)->name) && !Engine::get_singleton()->has_singleton(static_cast<GDScriptParser::IdentifierNode *>(subscript->base)->name)) { + // It's a static native method call. + gen->write_call_native_static(result, static_cast<GDScriptParser::IdentifierNode *>(subscript->base)->name, subscript->attribute->name, arguments); } else { GDScriptCodeGenerator::Address base = _parse_expression(codegen, r_error, subscript->base); if (r_error) { @@ -633,20 +667,8 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code case GDScriptParser::Node::GET_NODE: { const GDScriptParser::GetNodeNode *get_node = static_cast<const GDScriptParser::GetNodeNode *>(p_expression); - String node_name; - if (get_node->string != nullptr) { - node_name += String(get_node->string->value); - } else { - for (int i = 0; i < get_node->chain.size(); i++) { - if (i > 0) { - node_name += "/"; - } - node_name += get_node->chain[i]->name; - } - } - Vector<GDScriptCodeGenerator::Address> args; - args.push_back(codegen.add_constant(NodePath(node_name))); + args.push_back(codegen.add_constant(NodePath(get_node->full_path))); GDScriptCodeGenerator::Address result = codegen.add_temporary(_gdtype_from_datatype(get_node->get_datatype())); @@ -698,10 +720,10 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code } else if (subscript->is_attribute) { if (subscript->base->type == GDScriptParser::Node::SELF && codegen.script) { GDScriptParser::IdentifierNode *identifier = subscript->attribute; - const Map<StringName, GDScript::MemberInfo>::Element *MI = codegen.script->member_indices.find(identifier->name); + HashMap<StringName, GDScript::MemberInfo>::Iterator MI = codegen.script->member_indices.find(identifier->name); #ifdef DEBUG_ENABLED - if (MI && MI->get().getter == codegen.function_name) { + if (MI && MI->value.getter == codegen.function_name) { String n = identifier->name; _set_error("Must use '" + n + "' instead of 'self." + n + "' in getter.", identifier); r_error = ERR_COMPILATION_FAILED; @@ -709,11 +731,11 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code } #endif - if (MI && MI->get().getter == "") { + if (MI && MI->value.getter == "") { // 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->get().index, _gdtype_from_datatype(subscript->get_datatype())); + return GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::MEMBER, MI->value.index, _gdtype_from_datatype(subscript->get_datatype())); } } @@ -889,8 +911,8 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code const GDScriptParser::SubscriptNode *subscript = static_cast<GDScriptParser::SubscriptNode *>(assignment->assignee); #ifdef DEBUG_ENABLED if (subscript->is_attribute && subscript->base->type == GDScriptParser::Node::SELF && codegen.script) { - const Map<StringName, GDScript::MemberInfo>::Element *MI = codegen.script->member_indices.find(subscript->attribute->name); - if (MI && MI->get().setter == codegen.function_name) { + HashMap<StringName, GDScript::MemberInfo>::Iterator MI = codegen.script->member_indices.find(subscript->attribute->name); + if (MI && MI->value.setter == codegen.function_name) { String n = subscript->attribute->name; _set_error("Must use '" + n + "' instead of 'self." + n + "' in setter.", subscript); r_error = ERR_COMPILATION_FAILED; @@ -1192,7 +1214,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code return GDScriptCodeGenerator::Address(); } - gen->write_lambda(result, function, captures); + gen->write_lambda(result, function, captures, lambda->use_self); for (int i = 0; i < captures.size(); i++) { if (captures[i].mode == GDScriptCodeGenerator::Address::TEMPORARY) { @@ -1367,25 +1389,9 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_match_pattern(CodeGen &c codegen.generator->pop_temporary(); codegen.generator->pop_temporary(); - // If this isn't the first, we need to OR with the previous pattern. If it's nested, we use AND instead. - if (p_is_nested) { - // Use the previous value as target, since we only need one temporary variable. - codegen.generator->write_and_right_operand(result_addr); - codegen.generator->write_end_and(p_previous_test); - } else if (!p_is_first) { - // Use the previous value as target, since we only need one temporary variable. - codegen.generator->write_or_right_operand(result_addr); - codegen.generator->write_end_or(p_previous_test); - } else { - // Just assign this value to the accumulator temporary. - codegen.generator->write_assign(p_previous_test, result_addr); - } - codegen.generator->pop_temporary(); // Remove temp result addr. - // Create temporaries outside the loop so they can be reused. GDScriptCodeGenerator::Address element_addr = codegen.add_temporary(); GDScriptCodeGenerator::Address element_type_addr = codegen.add_temporary(); - GDScriptCodeGenerator::Address test_addr = p_previous_test; // Evaluate element by element. for (int i = 0; i < p_pattern->array.size(); i++) { @@ -1395,7 +1401,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_match_pattern(CodeGen &c } // Use AND here too, as we don't want to be checking elements if previous test failed (which means this might be an invalid get). - codegen.generator->write_and_left_operand(test_addr); + codegen.generator->write_and_left_operand(result_addr); // Add index to constant map. GDScriptCodeGenerator::Address index_addr = codegen.add_constant(i); @@ -1409,19 +1415,34 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_match_pattern(CodeGen &c codegen.generator->write_call_utility(element_type_addr, "typeof", typeof_args); // Try the pattern inside the element. - test_addr = _parse_match_pattern(codegen, r_error, p_pattern->array[i], element_addr, element_type_addr, p_previous_test, false, true); + result_addr = _parse_match_pattern(codegen, r_error, p_pattern->array[i], element_addr, element_type_addr, result_addr, false, true); if (r_error != OK) { return GDScriptCodeGenerator::Address(); } - codegen.generator->write_and_right_operand(test_addr); - codegen.generator->write_end_and(test_addr); + codegen.generator->write_and_right_operand(result_addr); + codegen.generator->write_end_and(result_addr); } // Remove element temporaries. codegen.generator->pop_temporary(); codegen.generator->pop_temporary(); - return test_addr; + // If this isn't the first, we need to OR with the previous pattern. If it's nested, we use AND instead. + if (p_is_nested) { + // Use the previous value as target, since we only need one temporary variable. + codegen.generator->write_and_right_operand(result_addr); + codegen.generator->write_end_and(p_previous_test); + } else if (!p_is_first) { + // Use the previous value as target, since we only need one temporary variable. + codegen.generator->write_or_right_operand(result_addr); + codegen.generator->write_end_or(p_previous_test); + } else { + // Just assign this value to the accumulator temporary. + codegen.generator->write_assign(p_previous_test, result_addr); + } + codegen.generator->pop_temporary(); // Remove temp result addr. + + return p_previous_test; } break; case GDScriptParser::PatternNode::PT_DICTIONARY: { if (p_is_nested) { @@ -1466,27 +1487,9 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_match_pattern(CodeGen &c codegen.generator->pop_temporary(); codegen.generator->pop_temporary(); - // If this isn't the first, we need to OR with the previous pattern. If it's nested, we use AND instead. - if (p_is_nested) { - // Use the previous value as target, since we only need one temporary variable. - codegen.generator->write_and_right_operand(result_addr); - codegen.generator->write_end_and(p_previous_test); - } else if (!p_is_first) { - // Use the previous value as target, since we only need one temporary variable. - codegen.generator->write_or_right_operand(result_addr); - codegen.generator->write_end_or(p_previous_test); - } else { - // Just assign this value to the accumulator temporary. - codegen.generator->write_assign(p_previous_test, result_addr); - } - codegen.generator->pop_temporary(); // Remove temp result addr. - // Create temporaries outside the loop so they can be reused. - temp_type.builtin_type = Variant::BOOL; - GDScriptCodeGenerator::Address test_result = codegen.add_temporary(temp_type); GDScriptCodeGenerator::Address element_addr = codegen.add_temporary(); GDScriptCodeGenerator::Address element_type_addr = codegen.add_temporary(); - GDScriptCodeGenerator::Address test_addr = p_previous_test; // Evaluate element by element. for (int i = 0; i < p_pattern->dictionary.size(); i++) { @@ -1497,7 +1500,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_match_pattern(CodeGen &c } // Use AND here too, as we don't want to be checking elements if previous test failed (which means this might be an invalid get). - codegen.generator->write_and_left_operand(test_addr); + codegen.generator->write_and_left_operand(result_addr); // Get the pattern key. GDScriptCodeGenerator::Address pattern_key_addr = _parse_expression(codegen, r_error, element.key); @@ -1508,11 +1511,11 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_match_pattern(CodeGen &c // Check if pattern key exists in user's dictionary. This will be AND-ed with next result. func_args.clear(); func_args.push_back(pattern_key_addr); - codegen.generator->write_call(test_result, p_value_addr, "has", func_args); + codegen.generator->write_call(result_addr, p_value_addr, "has", func_args); if (element.value_pattern != nullptr) { // Use AND here too, as we don't want to be checking elements if previous test failed (which means this might be an invalid get). - codegen.generator->write_and_left_operand(test_result); + codegen.generator->write_and_left_operand(result_addr); // Get actual value from user dictionary. codegen.generator->write_get(element_addr, pattern_key_addr, p_value_addr); @@ -1523,16 +1526,16 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_match_pattern(CodeGen &c codegen.generator->write_call_utility(element_type_addr, "typeof", func_args); // Try the pattern inside the value. - test_addr = _parse_match_pattern(codegen, r_error, element.value_pattern, element_addr, element_type_addr, test_addr, false, true); + result_addr = _parse_match_pattern(codegen, r_error, element.value_pattern, element_addr, element_type_addr, result_addr, false, true); if (r_error != OK) { return GDScriptCodeGenerator::Address(); } - codegen.generator->write_and_right_operand(test_addr); - codegen.generator->write_end_and(test_addr); + codegen.generator->write_and_right_operand(result_addr); + codegen.generator->write_end_and(result_addr); } - codegen.generator->write_and_right_operand(test_addr); - codegen.generator->write_end_and(test_addr); + codegen.generator->write_and_right_operand(result_addr); + codegen.generator->write_end_and(result_addr); // Remove pattern key temporary. if (pattern_key_addr.mode == GDScriptCodeGenerator::Address::TEMPORARY) { @@ -1543,9 +1546,23 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_match_pattern(CodeGen &c // Remove element temporaries. codegen.generator->pop_temporary(); codegen.generator->pop_temporary(); - codegen.generator->pop_temporary(); - return test_addr; + // If this isn't the first, we need to OR with the previous pattern. If it's nested, we use AND instead. + if (p_is_nested) { + // Use the previous value as target, since we only need one temporary variable. + codegen.generator->write_and_right_operand(result_addr); + codegen.generator->write_end_and(p_previous_test); + } else if (!p_is_first) { + // Use the previous value as target, since we only need one temporary variable. + codegen.generator->write_or_right_operand(result_addr); + codegen.generator->write_end_or(p_previous_test); + } else { + // Just assign this value to the accumulator temporary. + codegen.generator->write_assign(p_previous_test, result_addr); + } + codegen.generator->pop_temporary(); // Remove temp result addr. + + return p_previous_test; } break; case GDScriptParser::PatternNode::PT_REST: // Do nothing. @@ -2495,8 +2512,8 @@ Error GDScriptCompiler::_parse_class_blocks(GDScript *p_script, const GDScriptPa //validate instances if keeping state if (p_keep_state) { - for (Set<Object *>::Element *E = p_script->instances.front(); E;) { - Set<Object *>::Element *N = E->next(); + for (RBSet<Object *>::Element *E = p_script->instances.front(); E;) { + RBSet<Object *>::Element *N = E->next(); ScriptInstance *si = E->get()->get_script_instance(); if (si->is_placeholder()) { @@ -2558,7 +2575,7 @@ Error GDScriptCompiler::_parse_class_blocks(GDScript *p_script, const GDScriptPa } void GDScriptCompiler::_make_scripts(GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state) { - Map<StringName, Ref<GDScript>> old_subclasses; + HashMap<StringName, Ref<GDScript>> old_subclasses; if (p_keep_state) { old_subclasses = p_script->subclasses; diff --git a/modules/gdscript/gdscript_compiler.h b/modules/gdscript/gdscript_compiler.h index 8d71437344..4841884e2d 100644 --- a/modules/gdscript/gdscript_compiler.h +++ b/modules/gdscript/gdscript_compiler.h @@ -31,7 +31,7 @@ #ifndef GDSCRIPT_COMPILER_H #define GDSCRIPT_COMPILER_H -#include "core/templates/set.h" +#include "core/templates/hash_set.h" #include "gdscript.h" #include "gdscript_codegen.h" #include "gdscript_function.h" @@ -39,8 +39,8 @@ class GDScriptCompiler { const GDScriptParser *parser = nullptr; - Set<GDScript *> parsed_classes; - Set<GDScript *> parsing_classes; + HashSet<GDScript *> parsed_classes; + HashSet<GDScript *> parsing_classes; GDScript *main_script = nullptr; struct CodeGen { @@ -49,9 +49,9 @@ class GDScriptCompiler { const GDScriptParser::FunctionNode *function_node = nullptr; StringName function_name; GDScriptCodeGenerator *generator = nullptr; - Map<StringName, GDScriptCodeGenerator::Address> parameters; - Map<StringName, GDScriptCodeGenerator::Address> locals; - List<Map<StringName, GDScriptCodeGenerator::Address>> locals_stack; + HashMap<StringName, GDScriptCodeGenerator::Address> parameters; + HashMap<StringName, GDScriptCodeGenerator::Address> locals; + List<HashMap<StringName, GDScriptCodeGenerator::Address>> locals_stack; GDScriptCodeGenerator::Address add_local(const StringName &p_name, const GDScriptDataType &p_type) { uint32_t addr = generator->add_local(p_name, p_type); @@ -101,7 +101,7 @@ class GDScriptCompiler { } void start_block() { - Map<StringName, GDScriptCodeGenerator::Address> old_locals = locals; + HashMap<StringName, GDScriptCodeGenerator::Address> old_locals = locals; locals_stack.push_back(old_locals); generator->start_block(); } diff --git a/modules/gdscript/gdscript_disassembler.cpp b/modules/gdscript/gdscript_disassembler.cpp index cc0be94a9e..dc114f2eff 100644 --- a/modules/gdscript/gdscript_disassembler.cpp +++ b/modules/gdscript/gdscript_disassembler.cpp @@ -564,6 +564,28 @@ void GDScriptFunction::disassemble(const Vector<String> &p_code_lines) const { incr += 5 + argc; } break; + case OPCODE_CALL_NATIVE_STATIC: { + MethodBind *method = _methods_ptr[_code_ptr[ip + 1 + instr_var_args]]; + int argc = _code_ptr[ip + 2 + instr_var_args]; + + text += "call native method static "; + text += DADDR(1 + argc); + text += " = "; + text += method->get_instance_class(); + text += "."; + text += method->get_name(); + text += "("; + + for (int i = 0; i < argc; i++) { + if (i > 0) { + text += ", "; + } + text += DADDR(1 + i); + } + text += ")"; + + incr += 4 + argc; + } break; case OPCODE_CALL_PTRCALL_NO_RETURN: { text += "call-ptrcall (no return) "; @@ -770,6 +792,25 @@ void GDScriptFunction::disassemble(const Vector<String> &p_code_lines) const { incr = 3 + captures_count; } break; + case OPCODE_CREATE_SELF_LAMBDA: { + int captures_count = _code_ptr[ip + 1 + instr_var_args]; + GDScriptFunction *lambda = _lambdas_ptr[_code_ptr[ip + 2 + instr_var_args]]; + + text += DADDR(1 + captures_count); + text += "create self lambda from "; + text += lambda->name.operator String(); + text += "function, captures ("; + + for (int i = 0; i < captures_count; i++) { + if (i > 0) { + text += ", "; + } + text += DADDR(1 + i); + } + text += ")"; + + incr = 3 + captures_count; + } break; case OPCODE_JUMP: { text += "jump "; text += itos(_code_ptr[ip + 1]); @@ -968,7 +1009,7 @@ void GDScriptFunction::disassemble(const Vector<String> &p_code_lines) const { DISASSEMBLE_TYPE_ADJUST(QUATERNION); DISASSEMBLE_TYPE_ADJUST(AABB); DISASSEMBLE_TYPE_ADJUST(BASIS); - DISASSEMBLE_TYPE_ADJUST(TRANSFORM); + DISASSEMBLE_TYPE_ADJUST(TRANSFORM3D); DISASSEMBLE_TYPE_ADJUST(COLOR); DISASSEMBLE_TYPE_ADJUST(STRING_NAME); DISASSEMBLE_TYPE_ADJUST(NODE_PATH); diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp index 350962ba1b..202d1dcdf4 100644 --- a/modules/gdscript/gdscript_editor.cpp +++ b/modules/gdscript/gdscript_editor.cpp @@ -33,7 +33,6 @@ #include "core/config/engine.h" #include "core/core_constants.h" #include "core/io/file_access.h" -#include "editor_templates/templates.gen.h" #include "gdscript_analyzer.h" #include "gdscript_compiler.h" #include "gdscript_parser.h" @@ -44,6 +43,7 @@ #include "core/config/project_settings.h" #include "editor/editor_file_system.h" #include "editor/editor_settings.h" +#include "editor/script_templates/templates.gen.h" #endif void GDScriptLanguage::get_comment_delimiters(List<String> *p_delimiters) const { @@ -64,20 +64,20 @@ Ref<Script> GDScriptLanguage::make_template(const String &p_template, const Stri Ref<GDScript> script; script.instantiate(); String processed_template = p_template; + bool type_hints = false; #ifdef TOOLS_ENABLED - if (!EDITOR_GET("text_editor/completion/add_type_hints")) { + type_hints = EDITOR_GET("text_editor/completion/add_type_hints"); +#endif + if (!type_hints) { processed_template = processed_template.replace(": int", "") .replace(": String", "") + .replace(": Array[String]", "") .replace(": float", "") .replace(":=", "=") + .replace(" -> String", "") + .replace(" -> int", "") .replace(" -> void", ""); } -#else - processed_template = processed_template.replace(": int", "") - .replace(": String", "") - .replace(": float", "") - .replace(" -> void", ""); -#endif processed_template = processed_template.replace("_BASE_", p_base_class_name) .replace("_CLASS_", p_class_name) @@ -88,15 +88,17 @@ Ref<Script> GDScriptLanguage::make_template(const String &p_template, const Stri Vector<ScriptLanguage::ScriptTemplate> GDScriptLanguage::get_built_in_templates(StringName p_object) { Vector<ScriptLanguage::ScriptTemplate> templates; +#ifdef TOOLS_ENABLED for (int i = 0; i < TEMPLATES_ARRAY_SIZE; i++) { if (TEMPLATES[i].inherit == p_object) { templates.append(TEMPLATES[i]); } } +#endif return templates; } -static void get_function_names_recursively(const GDScriptParser::ClassNode *p_class, const String &p_prefix, Map<int, String> &r_funcs) { +static void get_function_names_recursively(const GDScriptParser::ClassNode *p_class, const String &p_prefix, HashMap<int, String> &r_funcs) { for (int i = 0; i < p_class->members.size(); i++) { if (p_class->members[i].type == GDScriptParser::ClassNode::Member::FUNCTION) { const GDScriptParser::FunctionNode *function = p_class->members[i].function; @@ -108,7 +110,7 @@ static void get_function_names_recursively(const GDScriptParser::ClassNode *p_cl } } -bool GDScriptLanguage::validate(const String &p_script, const String &p_path, List<String> *r_functions, List<ScriptLanguage::ScriptError> *r_errors, List<ScriptLanguage::Warning> *r_warnings, Set<int> *r_safe_lines) const { +bool GDScriptLanguage::validate(const String &p_script, const String &p_path, List<String> *r_functions, List<ScriptLanguage::ScriptError> *r_errors, List<ScriptLanguage::Warning> *r_warnings, HashSet<int> *r_safe_lines) const { GDScriptParser parser; GDScriptAnalyzer analyzer(&parser); @@ -146,7 +148,7 @@ bool GDScriptLanguage::validate(const String &p_script, const String &p_path, Li return false; } else { const GDScriptParser::ClassNode *cl = parser.get_tree(); - Map<int, String> funcs; + HashMap<int, String> funcs; get_function_names_recursively(cl, "", funcs); @@ -157,7 +159,7 @@ bool GDScriptLanguage::validate(const String &p_script, const String &p_path, Li #ifdef DEBUG_ENABLED if (r_safe_lines) { - const Set<int> &unsafe_lines = parser.get_unsafe_lines(); + const HashSet<int> &unsafe_lines = parser.get_unsafe_lines(); for (int i = 1; i <= parser.get_last_line_number(); i++) { if (!unsafe_lines.has(i)) { r_safe_lines->insert(i); @@ -319,7 +321,7 @@ void GDScriptLanguage::debug_get_stack_level_members(int p_level, List<String> * Ref<GDScript> script = instance->get_script(); ERR_FAIL_COND(script.is_null()); - const Map<StringName, GDScript::MemberInfo> &mi = script->debug_get_member_indices(); + const HashMap<StringName, GDScript::MemberInfo> &mi = script->debug_get_member_indices(); for (const KeyValue<StringName, GDScript::MemberInfo> &E : mi) { p_members->push_back(E.key); @@ -341,7 +343,7 @@ ScriptInstance *GDScriptLanguage::debug_get_stack_level_instance(int p_level) { } void GDScriptLanguage::debug_get_globals(List<String> *p_globals, List<Variant> *p_values, int p_max_subitems, int p_max_depth) { - const Map<StringName, int> &name_idx = GDScriptLanguage::get_singleton()->get_global_map(); + const HashMap<StringName, int> &name_idx = GDScriptLanguage::get_singleton()->get_global_map(); const Variant *globals = GDScriptLanguage::get_singleton()->get_global_array(); List<Pair<String, Variant>> cinfo; @@ -483,6 +485,89 @@ struct GDScriptCompletionIdentifier { const GDScriptParser::ExpressionNode *assigned_expression = nullptr; }; +// LOCATION METHODS +// These methods are used to populate the `CodeCompletionOption::location` integer. +// For these methods, the location is based on the depth in the inheritance chain that the property +// appears. For example, if you are completing code in a class that inherits Node2D, a property found on Node2D +// will have a "better" (lower) location "score" than a property that is found on CanvasItem. + +static int _get_property_location(StringName p_class, StringName p_property) { + if (!ClassDB::has_property(p_class, p_property)) { + return ScriptLanguage::LOCATION_OTHER; + } + + int depth = 0; + StringName class_test = p_class; + while (class_test && !ClassDB::has_property(class_test, p_property, true)) { + class_test = ClassDB::get_parent_class(class_test); + depth++; + } + + return depth | ScriptLanguage::LOCATION_PARENT_MASK; +} + +static int _get_constant_location(StringName p_class, StringName p_constant) { + if (!ClassDB::has_integer_constant(p_class, p_constant)) { + return ScriptLanguage::LOCATION_OTHER; + } + + int depth = 0; + StringName class_test = p_class; + while (class_test && !ClassDB::has_integer_constant(class_test, p_constant, true)) { + class_test = ClassDB::get_parent_class(class_test); + depth++; + } + + return depth | ScriptLanguage::LOCATION_PARENT_MASK; +} + +static int _get_signal_location(StringName p_class, StringName p_signal) { + if (!ClassDB::has_signal(p_class, p_signal)) { + return ScriptLanguage::LOCATION_OTHER; + } + + int depth = 0; + StringName class_test = p_class; + while (class_test && !ClassDB::has_signal(class_test, p_signal, true)) { + class_test = ClassDB::get_parent_class(class_test); + depth++; + } + + return depth | ScriptLanguage::LOCATION_PARENT_MASK; +} + +static int _get_method_location(StringName p_class, StringName p_method) { + if (!ClassDB::has_method(p_class, p_method)) { + return ScriptLanguage::LOCATION_OTHER; + } + + int depth = 0; + StringName class_test = p_class; + while (class_test && !ClassDB::has_method(class_test, p_method, true)) { + class_test = ClassDB::get_parent_class(class_test); + depth++; + } + + return depth | ScriptLanguage::LOCATION_PARENT_MASK; +} + +static int _get_enum_constant_location(StringName p_class, StringName p_enum_constant) { + if (!ClassDB::get_integer_constant_enum(p_class, p_enum_constant)) { + return ScriptLanguage::LOCATION_OTHER; + } + + int depth = 0; + StringName class_test = p_class; + while (class_test && !ClassDB::get_integer_constant_enum(class_test, p_enum_constant, true)) { + class_test = ClassDB::get_parent_class(class_test); + depth++; + } + + return depth | ScriptLanguage::LOCATION_PARENT_MASK; +} + +// END LOCATION METHODS + static String _get_visual_datatype(const PropertyInfo &p_info, bool p_is_arg = true) { if (p_info.usage & PROPERTY_USAGE_CLASS_IS_ENUM) { String enum_name = p_info.class_name; @@ -637,11 +722,11 @@ static String _make_arguments_hint(const GDScriptParser::FunctionNode *p_functio return arghint; } -static void _get_directory_contents(EditorFileSystemDirectory *p_dir, Map<String, ScriptCodeCompletionOption> &r_list) { +static void _get_directory_contents(EditorFileSystemDirectory *p_dir, HashMap<String, ScriptLanguage::CodeCompletionOption> &r_list) { const String quote_style = EDITOR_GET("text_editor/completion/use_single_quotes") ? "'" : "\""; for (int i = 0; i < p_dir->get_file_count(); i++) { - ScriptCodeCompletionOption option(p_dir->get_file_path(i), ScriptCodeCompletionOption::KIND_FILE_PATH); + ScriptLanguage::CodeCompletionOption option(p_dir->get_file_path(i), ScriptLanguage::CODE_COMPLETION_KIND_FILE_PATH); option.insert_text = option.display.quote(quote_style); r_list.insert(option.display, option); } @@ -651,29 +736,29 @@ static void _get_directory_contents(EditorFileSystemDirectory *p_dir, Map<String } } -static void _find_annotation_arguments(const GDScriptParser::AnnotationNode *p_annotation, int p_argument, const String p_quote_style, Map<String, ScriptCodeCompletionOption> &r_result) { +static void _find_annotation_arguments(const GDScriptParser::AnnotationNode *p_annotation, int p_argument, const String p_quote_style, HashMap<String, ScriptLanguage::CodeCompletionOption> &r_result) { if (p_annotation->name == SNAME("@export_range")) { if (p_argument == 3 || p_argument == 4) { // Slider hint. - ScriptCodeCompletionOption slider1("or_greater", ScriptCodeCompletionOption::KIND_PLAIN_TEXT); + ScriptLanguage::CodeCompletionOption slider1("or_greater", ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT); slider1.insert_text = slider1.display.quote(p_quote_style); r_result.insert(slider1.display, slider1); - ScriptCodeCompletionOption slider2("or_lesser", ScriptCodeCompletionOption::KIND_PLAIN_TEXT); + ScriptLanguage::CodeCompletionOption slider2("or_lesser", ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT); slider2.insert_text = slider2.display.quote(p_quote_style); r_result.insert(slider2.display, slider2); } } else if (p_annotation->name == SNAME("@export_exp_easing")) { if (p_argument == 0 || p_argument == 1) { // Easing hint. - ScriptCodeCompletionOption hint1("attenuation", ScriptCodeCompletionOption::KIND_PLAIN_TEXT); + ScriptLanguage::CodeCompletionOption hint1("attenuation", ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT); hint1.insert_text = hint1.display.quote(p_quote_style); r_result.insert(hint1.display, hint1); - ScriptCodeCompletionOption hint2("inout", ScriptCodeCompletionOption::KIND_PLAIN_TEXT); + ScriptLanguage::CodeCompletionOption hint2("inout", ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT); hint2.insert_text = hint2.display.quote(p_quote_style); r_result.insert(hint2.display, hint2); } } else if (p_annotation->name == SNAME("@export_node_path")) { - ScriptCodeCompletionOption node("Node", ScriptCodeCompletionOption::KIND_CLASS); + ScriptLanguage::CodeCompletionOption node("Node", ScriptLanguage::CODE_COMPLETION_KIND_CLASS); r_result.insert(node.display, node); List<StringName> node_types; ClassDB::get_inheriters_from_class("Node", &node_types); @@ -681,23 +766,38 @@ static void _find_annotation_arguments(const GDScriptParser::AnnotationNode *p_a if (!ClassDB::is_class_exposed(E)) { continue; } - ScriptCodeCompletionOption option(E, ScriptCodeCompletionOption::KIND_CLASS); + ScriptLanguage::CodeCompletionOption option(E, ScriptLanguage::CODE_COMPLETION_KIND_CLASS); r_result.insert(option.display, option); } } else if (p_annotation->name == SNAME("@warning_ignore")) { for (int warning_code = 0; warning_code < GDScriptWarning::WARNING_MAX; warning_code++) { - ScriptCodeCompletionOption warning(GDScriptWarning::get_name_from_code((GDScriptWarning::Code)warning_code).to_lower(), ScriptCodeCompletionOption::KIND_PLAIN_TEXT); + ScriptLanguage::CodeCompletionOption warning(GDScriptWarning::get_name_from_code((GDScriptWarning::Code)warning_code).to_lower(), ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT); r_result.insert(warning.display, warning); } } } -static void _list_available_types(bool p_inherit_only, GDScriptParser::CompletionContext &p_context, Map<String, ScriptCodeCompletionOption> &r_result) { +static void _find_built_in_variants(HashMap<String, ScriptLanguage::CodeCompletionOption> &r_result, bool exclude_nil = false) { + for (int i = 0; i < Variant::VARIANT_MAX; i++) { + if (!exclude_nil && Variant::Type(i) == Variant::Type::NIL) { + ScriptLanguage::CodeCompletionOption option("null", ScriptLanguage::CODE_COMPLETION_KIND_CLASS); + r_result.insert(option.display, option); + } else { + ScriptLanguage::CodeCompletionOption option(Variant::get_type_name(Variant::Type(i)), ScriptLanguage::CODE_COMPLETION_KIND_CLASS); + r_result.insert(option.display, option); + } + } +} + +static void _list_available_types(bool p_inherit_only, GDScriptParser::CompletionContext &p_context, HashMap<String, ScriptLanguage::CodeCompletionOption> &r_result) { + // Built-in Variant Types + _find_built_in_variants(r_result, true); + List<StringName> native_types; ClassDB::get_class_list(&native_types); for (const StringName &E : native_types) { if (ClassDB::is_class_exposed(E) && !Engine::get_singleton()->has_singleton(E)) { - ScriptCodeCompletionOption option(E, ScriptCodeCompletionOption::KIND_CLASS); + ScriptLanguage::CodeCompletionOption option(E, ScriptLanguage::CODE_COMPLETION_KIND_CLASS); r_result.insert(option.display, option); } } @@ -708,7 +808,7 @@ static void _list_available_types(bool p_inherit_only, GDScriptParser::Completio List<StringName> enums; ClassDB::get_enum_list(p_context.current_class->base_type.native_type, &enums); for (const StringName &E : enums) { - ScriptCodeCompletionOption option(E, ScriptCodeCompletionOption::KIND_ENUM); + ScriptLanguage::CodeCompletionOption option(E, ScriptLanguage::CODE_COMPLETION_KIND_ENUM); r_result.insert(option.display, option); } } @@ -719,18 +819,18 @@ static void _list_available_types(bool p_inherit_only, GDScriptParser::Completio const GDScriptParser::ClassNode::Member &member = current->members[i]; switch (member.type) { case GDScriptParser::ClassNode::Member::CLASS: { - ScriptCodeCompletionOption option(member.m_class->identifier->name, ScriptCodeCompletionOption::KIND_CLASS); + ScriptLanguage::CodeCompletionOption option(member.m_class->identifier->name, ScriptLanguage::CODE_COMPLETION_KIND_CLASS, ScriptLanguage::LOCATION_LOCAL); r_result.insert(option.display, option); } break; case GDScriptParser::ClassNode::Member::ENUM: { if (!p_inherit_only) { - ScriptCodeCompletionOption option(member.m_enum->identifier->name, ScriptCodeCompletionOption::KIND_ENUM); + ScriptLanguage::CodeCompletionOption option(member.m_enum->identifier->name, ScriptLanguage::CODE_COMPLETION_KIND_ENUM, ScriptLanguage::LOCATION_LOCAL); r_result.insert(option.display, option); } } break; case GDScriptParser::ClassNode::Member::CONSTANT: { if (member.constant->get_datatype().is_meta_type && p_context.current_class->outer != nullptr) { - ScriptCodeCompletionOption option(member.constant->identifier->name, ScriptCodeCompletionOption::KIND_CLASS); + ScriptLanguage::CodeCompletionOption option(member.constant->identifier->name, ScriptLanguage::CODE_COMPLETION_KIND_CLASS, ScriptLanguage::LOCATION_LOCAL); r_result.insert(option.display, option); } } break; @@ -746,30 +846,31 @@ static void _list_available_types(bool p_inherit_only, GDScriptParser::Completio List<StringName> global_classes; ScriptServer::get_global_class_list(&global_classes); for (const StringName &E : global_classes) { - ScriptCodeCompletionOption option(E, ScriptCodeCompletionOption::KIND_CLASS); + ScriptLanguage::CodeCompletionOption option(E, ScriptLanguage::CODE_COMPLETION_KIND_CLASS, ScriptLanguage::LOCATION_OTHER_USER_CODE); r_result.insert(option.display, option); } // Autoload singletons - OrderedHashMap<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list(); - for (OrderedHashMap<StringName, ProjectSettings::AutoloadInfo>::Element E = autoloads.front(); E; E = E.next()) { - const ProjectSettings::AutoloadInfo &info = E.get(); + HashMap<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list(); + + for (const KeyValue<StringName, ProjectSettings::AutoloadInfo> &E : autoloads) { + const ProjectSettings::AutoloadInfo &info = E.value; if (!info.is_singleton || info.path.get_extension().to_lower() != "gd") { continue; } - ScriptCodeCompletionOption option(info.name, ScriptCodeCompletionOption::KIND_CLASS); + ScriptLanguage::CodeCompletionOption option(info.name, ScriptLanguage::CODE_COMPLETION_KIND_CLASS, ScriptLanguage::LOCATION_OTHER_USER_CODE); r_result.insert(option.display, option); } } -static void _find_identifiers_in_suite(const GDScriptParser::SuiteNode *p_suite, Map<String, ScriptCodeCompletionOption> &r_result) { +static void _find_identifiers_in_suite(const GDScriptParser::SuiteNode *p_suite, HashMap<String, ScriptLanguage::CodeCompletionOption> &r_result) { for (int i = 0; i < p_suite->locals.size(); i++) { - ScriptCodeCompletionOption option; + ScriptLanguage::CodeCompletionOption option; if (p_suite->locals[i].type == GDScriptParser::SuiteNode::Local::CONSTANT) { - option = ScriptCodeCompletionOption(p_suite->locals[i].name, ScriptCodeCompletionOption::KIND_CONSTANT); + option = ScriptLanguage::CodeCompletionOption(p_suite->locals[i].name, ScriptLanguage::CODE_COMPLETION_KIND_CONSTANT, ScriptLanguage::LOCATION_LOCAL); option.default_value = p_suite->locals[i].constant->initializer->reduced_value; } else { - option = ScriptCodeCompletionOption(p_suite->locals[i].name, ScriptCodeCompletionOption::KIND_VARIABLE); + option = ScriptLanguage::CodeCompletionOption(p_suite->locals[i].name, ScriptLanguage::CODE_COMPLETION_KIND_VARIABLE, ScriptLanguage::LOCATION_LOCAL); } r_result.insert(option.display, option); } @@ -778,24 +879,26 @@ static void _find_identifiers_in_suite(const GDScriptParser::SuiteNode *p_suite, } } -static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base, bool p_only_functions, Map<String, ScriptCodeCompletionOption> &r_result, int p_recursion_depth); +static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base, bool p_only_functions, HashMap<String, ScriptLanguage::CodeCompletionOption> &r_result, int p_recursion_depth); -static void _find_identifiers_in_class(const GDScriptParser::ClassNode *p_class, bool p_only_functions, bool p_static, bool p_parent_only, Map<String, ScriptCodeCompletionOption> &r_result, int p_recursion_depth) { +static void _find_identifiers_in_class(const GDScriptParser::ClassNode *p_class, bool p_only_functions, bool p_static, bool p_parent_only, HashMap<String, ScriptLanguage::CodeCompletionOption> &r_result, int p_recursion_depth) { ERR_FAIL_COND(p_recursion_depth > COMPLETION_RECURSION_LIMIT); if (!p_parent_only) { bool outer = false; const GDScriptParser::ClassNode *clss = p_class; + int classes_processed = 0; while (clss) { for (int i = 0; i < clss->members.size(); i++) { + const int location = (classes_processed + p_recursion_depth) | ScriptLanguage::LOCATION_PARENT_MASK; const GDScriptParser::ClassNode::Member &member = clss->members[i]; - ScriptCodeCompletionOption option; + ScriptLanguage::CodeCompletionOption option; switch (member.type) { case GDScriptParser::ClassNode::Member::VARIABLE: if (p_only_functions || outer || (p_static)) { continue; } - option = ScriptCodeCompletionOption(member.variable->identifier->name, ScriptCodeCompletionOption::KIND_MEMBER); + option = ScriptLanguage::CodeCompletionOption(member.variable->identifier->name, ScriptLanguage::CODE_COMPLETION_KIND_MEMBER, location); break; case GDScriptParser::ClassNode::Member::CONSTANT: if (p_only_functions) { @@ -804,7 +907,7 @@ static void _find_identifiers_in_class(const GDScriptParser::ClassNode *p_class, if (r_result.has(member.constant->identifier->name)) { continue; } - option = ScriptCodeCompletionOption(member.constant->identifier->name, ScriptCodeCompletionOption::KIND_CONSTANT); + option = ScriptLanguage::CodeCompletionOption(member.constant->identifier->name, ScriptLanguage::CODE_COMPLETION_KIND_CONSTANT, location); if (member.constant->initializer) { option.default_value = member.constant->initializer->reduced_value; } @@ -813,25 +916,25 @@ static void _find_identifiers_in_class(const GDScriptParser::ClassNode *p_class, if (p_only_functions) { continue; } - option = ScriptCodeCompletionOption(member.m_class->identifier->name, ScriptCodeCompletionOption::KIND_CLASS); + option = ScriptLanguage::CodeCompletionOption(member.m_class->identifier->name, ScriptLanguage::CODE_COMPLETION_KIND_CLASS, location); break; case GDScriptParser::ClassNode::Member::ENUM_VALUE: if (p_only_functions) { continue; } - option = ScriptCodeCompletionOption(member.enum_value.identifier->name, ScriptCodeCompletionOption::KIND_CONSTANT); + option = ScriptLanguage::CodeCompletionOption(member.enum_value.identifier->name, ScriptLanguage::CODE_COMPLETION_KIND_CONSTANT, location); break; case GDScriptParser::ClassNode::Member::ENUM: if (p_only_functions) { continue; } - option = ScriptCodeCompletionOption(member.m_enum->identifier->name, ScriptCodeCompletionOption::KIND_ENUM); + option = ScriptLanguage::CodeCompletionOption(member.m_enum->identifier->name, ScriptLanguage::CODE_COMPLETION_KIND_ENUM, location); break; case GDScriptParser::ClassNode::Member::FUNCTION: if (outer || (p_static && !member.function->is_static) || member.function->identifier->name.operator String().begins_with("@")) { continue; } - option = ScriptCodeCompletionOption(member.function->identifier->name, ScriptCodeCompletionOption::KIND_FUNCTION); + option = ScriptLanguage::CodeCompletionOption(member.function->identifier->name, ScriptLanguage::CODE_COMPLETION_KIND_FUNCTION, location); if (member.function->parameters.size() > 0) { option.insert_text += "("; } else { @@ -842,7 +945,7 @@ static void _find_identifiers_in_class(const GDScriptParser::ClassNode *p_class, if (p_only_functions || outer) { continue; } - option = ScriptCodeCompletionOption(member.signal->identifier->name, ScriptCodeCompletionOption::KIND_SIGNAL); + option = ScriptLanguage::CodeCompletionOption(member.signal->identifier->name, ScriptLanguage::CODE_COMPLETION_KIND_SIGNAL, location); break; case GDScriptParser::ClassNode::Member::UNDEFINED: break; @@ -851,6 +954,7 @@ static void _find_identifiers_in_class(const GDScriptParser::ClassNode *p_class, } outer = true; clss = clss->outer; + classes_processed++; } } @@ -862,14 +966,14 @@ static void _find_identifiers_in_class(const GDScriptParser::ClassNode *p_class, _find_identifiers_in_base(base_type, p_only_functions, r_result, p_recursion_depth + 1); } -static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base, bool p_only_functions, Map<String, ScriptCodeCompletionOption> &r_result, int p_recursion_depth) { +static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base, bool p_only_functions, HashMap<String, ScriptLanguage::CodeCompletionOption> &r_result, int p_recursion_depth) { ERR_FAIL_COND(p_recursion_depth > COMPLETION_RECURSION_LIMIT); GDScriptParser::DataType base_type = p_base.type; bool _static = base_type.is_meta_type; if (_static && base_type.kind != GDScriptParser::DataType::BUILTIN) { - ScriptCodeCompletionOption option("new", ScriptCodeCompletionOption::KIND_FUNCTION); + ScriptLanguage::CodeCompletionOption option("new", ScriptLanguage::CODE_COMPLETION_KIND_FUNCTION, ScriptLanguage::LOCATION_LOCAL); option.insert_text += "("; r_result.insert(option.display, option); } @@ -889,21 +993,24 @@ static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base List<PropertyInfo> members; scr->get_script_property_list(&members); for (const PropertyInfo &E : members) { - ScriptCodeCompletionOption option(E.name, ScriptCodeCompletionOption::KIND_MEMBER); + int location = p_recursion_depth + _get_property_location(scr->get_class_name(), E.class_name); + ScriptLanguage::CodeCompletionOption option(E.name, ScriptLanguage::CODE_COMPLETION_KIND_MEMBER, location); r_result.insert(option.display, option); } } - Map<StringName, Variant> constants; + HashMap<StringName, Variant> constants; scr->get_constants(&constants); for (const KeyValue<StringName, Variant> &E : constants) { - ScriptCodeCompletionOption option(E.key.operator String(), ScriptCodeCompletionOption::KIND_CONSTANT); + int location = p_recursion_depth + _get_constant_location(scr->get_class_name(), E.key); + ScriptLanguage::CodeCompletionOption option(E.key.operator String(), ScriptLanguage::CODE_COMPLETION_KIND_CONSTANT, location); r_result.insert(option.display, option); } List<MethodInfo> signals; scr->get_script_signal_list(&signals); for (const MethodInfo &E : signals) { - ScriptCodeCompletionOption option(E.name, ScriptCodeCompletionOption::KIND_SIGNAL); + int location = p_recursion_depth + _get_signal_location(scr->get_class_name(), E.name); + ScriptLanguage::CodeCompletionOption option(E.name, ScriptLanguage::CODE_COMPLETION_KIND_SIGNAL, location); r_result.insert(option.display, option); } } @@ -914,7 +1021,8 @@ static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base if (E.name.begins_with("@")) { continue; } - ScriptCodeCompletionOption option(E.name, ScriptCodeCompletionOption::KIND_FUNCTION); + int location = p_recursion_depth + _get_method_location(scr->get_class_name(), E.name); + ScriptLanguage::CodeCompletionOption option(E.name, ScriptLanguage::CODE_COMPLETION_KIND_FUNCTION, location); if (E.arguments.size()) { option.insert_text += "("; } else { @@ -944,7 +1052,16 @@ static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base List<String> constants; ClassDB::get_integer_constant_list(type, &constants); for (const String &E : constants) { - ScriptCodeCompletionOption option(E, ScriptCodeCompletionOption::KIND_CONSTANT); + int location = p_recursion_depth + _get_constant_location(type, StringName(E)); + ScriptLanguage::CodeCompletionOption option(E, ScriptLanguage::CODE_COMPLETION_KIND_CONSTANT, location); + r_result.insert(option.display, option); + } + + List<MethodInfo> signals; + ClassDB::get_signal_list(type, &signals); + for (const MethodInfo &E : signals) { + int location = p_recursion_depth + _get_signal_location(type, StringName(E.name)); + ScriptLanguage::CodeCompletionOption option(E.name, ScriptLanguage::CODE_COMPLETION_KIND_SIGNAL, location); r_result.insert(option.display, option); } @@ -958,29 +1075,33 @@ static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base if (E.name.contains("/")) { continue; } - ScriptCodeCompletionOption option(E.name, ScriptCodeCompletionOption::KIND_MEMBER); + int location = p_recursion_depth + _get_property_location(type, E.class_name); + ScriptLanguage::CodeCompletionOption option(E.name, ScriptLanguage::CODE_COMPLETION_KIND_MEMBER, location); r_result.insert(option.display, option); } } } - if (!_static || Engine::get_singleton()->has_singleton(type)) { - List<MethodInfo> methods; - ClassDB::get_method_list(type, &methods, false, true); - for (const MethodInfo &E : methods) { - if (E.name.begins_with("_")) { - continue; - } - ScriptCodeCompletionOption option(E.name, ScriptCodeCompletionOption::KIND_FUNCTION); - if (E.arguments.size()) { - option.insert_text += "("; - } else { - option.insert_text += "()"; - } - r_result.insert(option.display, option); + bool only_static = _static && !Engine::get_singleton()->has_singleton(type); + + List<MethodInfo> methods; + ClassDB::get_method_list(type, &methods, false, true); + for (const MethodInfo &E : methods) { + if (only_static && (E.flags & METHOD_FLAG_STATIC) == 0) { + continue; } + if (E.name.begins_with("_")) { + continue; + } + int location = p_recursion_depth + _get_method_location(type, E.name); + ScriptLanguage::CodeCompletionOption option(E.name, ScriptLanguage::CODE_COMPLETION_KIND_FUNCTION, location); + if (E.arguments.size()) { + option.insert_text += "("; + } else { + option.insert_text += "()"; + } + r_result.insert(option.display, option); } - return; } break; case GDScriptParser::DataType::BUILTIN: { @@ -1001,7 +1122,7 @@ static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base for (const PropertyInfo &E : members) { if (!String(E.name).contains("/")) { - ScriptCodeCompletionOption option(E.name, ScriptCodeCompletionOption::KIND_MEMBER); + ScriptLanguage::CodeCompletionOption option(E.name, ScriptLanguage::CODE_COMPLETION_KIND_MEMBER); r_result.insert(option.display, option); } } @@ -1010,7 +1131,7 @@ static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base List<MethodInfo> methods; tmp.get_method_list(&methods); for (const MethodInfo &E : methods) { - ScriptCodeCompletionOption option(E.name, ScriptCodeCompletionOption::KIND_FUNCTION); + ScriptLanguage::CodeCompletionOption option(E.name, ScriptLanguage::CODE_COMPLETION_KIND_FUNCTION); if (E.arguments.size()) { option.insert_text += "("; } else { @@ -1028,7 +1149,7 @@ static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base } } -static void _find_identifiers(GDScriptParser::CompletionContext &p_context, bool p_only_functions, Map<String, ScriptCodeCompletionOption> &r_result, int p_recursion_depth) { +static void _find_identifiers(const GDScriptParser::CompletionContext &p_context, bool p_only_functions, HashMap<String, ScriptLanguage::CodeCompletionOption> &r_result, int p_recursion_depth) { if (!p_only_functions && p_context.current_suite) { // This includes function parameters, since they are also locals. _find_identifiers_in_suite(p_context.current_suite, r_result); @@ -1043,7 +1164,7 @@ static void _find_identifiers(GDScriptParser::CompletionContext &p_context, bool for (const StringName &E : functions) { MethodInfo function = GDScriptUtilityFunctions::get_function_info(E); - ScriptCodeCompletionOption option(String(E), ScriptCodeCompletionOption::KIND_FUNCTION); + ScriptLanguage::CodeCompletionOption option(String(E), ScriptLanguage::CODE_COMPLETION_KIND_FUNCTION); if (function.arguments.size() || (function.flags & METHOD_FLAG_VARARG)) { option.insert_text += "("; } else { @@ -1056,17 +1177,7 @@ static void _find_identifiers(GDScriptParser::CompletionContext &p_context, bool return; } - static const char *_type_names[Variant::VARIANT_MAX] = { - "null", "bool", "int", "float", "String", "StringName", "Vector2", "Vector2i", "Rect2", "Rect2i", "Vector3", "Vector3i", "Transform2D", "Plane", "Quaternion", "AABB", "Basis", "Transform3D", - "Color", "NodePath", "RID", "Signal", "Callable", "Object", "Dictionary", "Array", "PackedByteArray", "PackedInt32Array", "PackedInt64Array", "PackedFloat32Array", "PackedFloat64Array", "PackedStringArray", - "PackedVector2Array", "PackedVector3Array", "PackedColorArray" - }; - static_assert((sizeof(_type_names) / sizeof(*_type_names)) == Variant::VARIANT_MAX, "Completion for builtin types is incomplete"); - - for (int i = 0; i < Variant::VARIANT_MAX; i++) { - ScriptCodeCompletionOption option(_type_names[i], ScriptCodeCompletionOption::KIND_CLASS); - r_result.insert(option.display, option); - } + _find_built_in_variants(r_result); static const char *_keywords[] = { "false", "PI", "TAU", "INF", "NAN", "self", "true", "breakpoint", "tool", "super", @@ -1076,7 +1187,7 @@ static void _find_identifiers(GDScriptParser::CompletionContext &p_context, bool const char **kw = _keywords; while (*kw) { - ScriptCodeCompletionOption option(*kw, ScriptCodeCompletionOption::KIND_PLAIN_TEXT); + ScriptLanguage::CodeCompletionOption option(*kw, ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT); r_result.insert(option.display, option); kw++; } @@ -1089,7 +1200,7 @@ static void _find_identifiers(GDScriptParser::CompletionContext &p_context, bool const char **kws = _keywords_with_space; while (*kws) { - ScriptCodeCompletionOption option(*kws, ScriptCodeCompletionOption::KIND_PLAIN_TEXT); + ScriptLanguage::CodeCompletionOption option(*kws, ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT); option.insert_text += " "; r_result.insert(option.display, option); kws++; @@ -1102,7 +1213,7 @@ static void _find_identifiers(GDScriptParser::CompletionContext &p_context, bool const char **kwa = _keywords_with_args; while (*kwa) { - ScriptCodeCompletionOption option(*kwa, ScriptCodeCompletionOption::KIND_FUNCTION); + ScriptLanguage::CodeCompletionOption option(*kwa, ScriptLanguage::CODE_COMPLETION_KIND_FUNCTION); option.insert_text += "("; r_result.insert(option.display, option); kwa++; @@ -1112,27 +1223,26 @@ static void _find_identifiers(GDScriptParser::CompletionContext &p_context, bool Variant::get_utility_function_list(&utility_func_names); for (List<StringName>::Element *E = utility_func_names.front(); E; E = E->next()) { - ScriptCodeCompletionOption option(E->get(), ScriptCodeCompletionOption::KIND_FUNCTION); + ScriptLanguage::CodeCompletionOption option(E->get(), ScriptLanguage::CODE_COMPLETION_KIND_FUNCTION); option.insert_text += "("; r_result.insert(option.display, option); } - OrderedHashMap<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list(); - for (OrderedHashMap<StringName, ProjectSettings::AutoloadInfo>::Element E = autoloads.front(); E; E = E.next()) { - if (!E.value().is_singleton) { + for (const KeyValue<StringName, ProjectSettings::AutoloadInfo> &E : ProjectSettings::get_singleton()->get_autoload_list()) { + if (!E.value.is_singleton) { continue; } - ScriptCodeCompletionOption option(E.key(), ScriptCodeCompletionOption::KIND_CONSTANT); + ScriptLanguage::CodeCompletionOption option(E.key, ScriptLanguage::CODE_COMPLETION_KIND_CONSTANT); r_result.insert(option.display, option); } // Native classes and global constants. for (const KeyValue<StringName, int> &E : GDScriptLanguage::get_singleton()->get_global_map()) { - ScriptCodeCompletionOption option; + ScriptLanguage::CodeCompletionOption option; if (ClassDB::class_exists(E.key) || Engine::get_singleton()->has_singleton(E.key)) { - option = ScriptCodeCompletionOption(E.key.operator String(), ScriptCodeCompletionOption::KIND_CLASS); + option = ScriptLanguage::CodeCompletionOption(E.key.operator String(), ScriptLanguage::CODE_COMPLETION_KIND_CLASS); } else { - option = ScriptCodeCompletionOption(E.key.operator String(), ScriptCodeCompletionOption::KIND_CONSTANT); + option = ScriptLanguage::CodeCompletionOption(E.key.operator String(), ScriptLanguage::CODE_COMPLETION_KIND_CONSTANT); } r_result.insert(option.display, option); } @@ -1415,12 +1525,10 @@ static bool _guess_expression_type(GDScriptParser::CompletionContext &p_context, r_type = _type_from_variant(GDScriptLanguage::get_singleton()->get_named_globals_map()[which]); found = true; } else { - OrderedHashMap<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list(); - - for (OrderedHashMap<StringName, ProjectSettings::AutoloadInfo>::Element E = autoloads.front(); E; E = E.next()) { - String name = E.key(); + for (const KeyValue<StringName, ProjectSettings::AutoloadInfo> &E : ProjectSettings::get_singleton()->get_autoload_list()) { + String name = E.key; if (name == which) { - String script = E.value().path; + String script = E.value.path; if (!script.begins_with("res://")) { script = "res://" + script; @@ -1981,7 +2089,7 @@ static bool _guess_identifier_type_from_base(GDScriptParser::CompletionContext & case GDScriptParser::DataType::SCRIPT: { Ref<Script> scr = base_type.script_type; if (scr.is_valid()) { - Map<StringName, Variant> constants; + HashMap<StringName, Variant> constants; scr->get_constants(&constants); if (constants.has(p_identifier)) { r_type = _type_from_variant(constants[p_identifier]); @@ -2210,20 +2318,20 @@ static bool _guess_method_return_type_from_base(GDScriptParser::CompletionContex return false; } -static void _find_enumeration_candidates(GDScriptParser::CompletionContext &p_context, const String &p_enum_hint, Map<String, ScriptCodeCompletionOption> &r_result) { +static void _find_enumeration_candidates(GDScriptParser::CompletionContext &p_context, const String &p_enum_hint, HashMap<String, ScriptLanguage::CodeCompletionOption> &r_result) { if (!p_enum_hint.contains(".")) { // Global constant or in the current class. StringName current_enum = p_enum_hint; if (p_context.current_class && p_context.current_class->has_member(current_enum) && p_context.current_class->get_member(current_enum).type == GDScriptParser::ClassNode::Member::ENUM) { const GDScriptParser::EnumNode *_enum = p_context.current_class->get_member(current_enum).m_enum; for (int i = 0; i < _enum->values.size(); i++) { - ScriptCodeCompletionOption option(_enum->values[i].identifier->name, ScriptCodeCompletionOption::KIND_ENUM); + ScriptLanguage::CodeCompletionOption option(_enum->values[i].identifier->name, ScriptLanguage::CODE_COMPLETION_KIND_ENUM); r_result.insert(option.display, option); } } else { for (int i = 0; i < CoreConstants::get_global_constant_count(); i++) { if (CoreConstants::get_global_constant_enum(i) == current_enum) { - ScriptCodeCompletionOption option(CoreConstants::get_global_constant_name(i), ScriptCodeCompletionOption::KIND_ENUM); + ScriptLanguage::CodeCompletionOption option(CoreConstants::get_global_constant_name(i), ScriptLanguage::CODE_COMPLETION_KIND_ENUM); r_result.insert(option.display, option); } } @@ -2240,13 +2348,14 @@ static void _find_enumeration_candidates(GDScriptParser::CompletionContext &p_co ClassDB::get_enum_constants(class_name, enum_name, &enum_constants); for (const StringName &E : enum_constants) { String candidate = class_name + "." + E; - ScriptCodeCompletionOption option(candidate, ScriptCodeCompletionOption::KIND_ENUM); + int location = _get_enum_constant_location(class_name, E); + ScriptLanguage::CodeCompletionOption option(candidate, ScriptLanguage::CODE_COMPLETION_KIND_ENUM, location); r_result.insert(option.display, option); } } } -static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, const GDScriptCompletionIdentifier &p_base, const StringName &p_method, int p_argidx, bool p_static, Map<String, ScriptCodeCompletionOption> &r_result, String &r_arghint) { +static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, const GDScriptCompletionIdentifier &p_base, const StringName &p_method, int p_argidx, bool p_static, HashMap<String, ScriptLanguage::CodeCompletionOption> &r_result, String &r_arghint) { Variant base = p_base.value; GDScriptParser::DataType base_type = p_base.type; @@ -2287,7 +2396,7 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c if (opt.is_quoted()) { opt = opt.unquote().quote(quote_style); // Handle user preference. } - ScriptCodeCompletionOption option(opt, ScriptCodeCompletionOption::KIND_FUNCTION); + ScriptLanguage::CodeCompletionOption option(opt, ScriptLanguage::CODE_COMPLETION_KIND_FUNCTION); r_result.insert(option.display, option); } } @@ -2314,7 +2423,7 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c continue; } String name = s.get_slice("/", 1); - ScriptCodeCompletionOption option("/root/" + name, ScriptCodeCompletionOption::KIND_NODE_PATH); + ScriptLanguage::CodeCompletionOption option("/root/" + name, ScriptLanguage::CODE_COMPLETION_KIND_NODE_PATH); option.insert_text = option.display.quote(quote_style); r_result.insert(option.display, option); } @@ -2330,7 +2439,7 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c continue; } String name = s.get_slice("/", 1); - ScriptCodeCompletionOption option(name, ScriptCodeCompletionOption::KIND_CONSTANT); + ScriptLanguage::CodeCompletionOption option(name, ScriptLanguage::CODE_COMPLETION_KIND_CONSTANT); option.insert_text = option.display.quote(quote_style); r_result.insert(option.display, option); } @@ -2365,7 +2474,7 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c } } -static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, const GDScriptParser::Node *p_call, int p_argidx, Map<String, ScriptCodeCompletionOption> &r_result, bool &r_forced, String &r_arghint) { +static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, const GDScriptParser::Node *p_call, int p_argidx, HashMap<String, ScriptLanguage::CodeCompletionOption> &r_result, bool &r_forced, String &r_arghint) { if (p_call->type == GDScriptParser::Node::PRELOAD) { if (p_argidx == 0 && bool(EditorSettings::get_singleton()->get("text_editor/completion/complete_file_paths"))) { _get_directory_contents(EditorFileSystem::get_singleton()->get_filesystem(), r_result); @@ -2395,7 +2504,7 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c r_arghint = _make_arguments_hint(info, p_argidx); return; } else if (GDScriptParser::get_builtin_type(call->function_name) < Variant::VARIANT_MAX) { - // Complete constructor + // Complete constructor. List<MethodInfo> constructors; Variant::get_constructor_list(GDScriptParser::get_builtin_type(call->function_name), &constructors); @@ -2421,6 +2530,32 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c } else if (callee_type == GDScriptParser::Node::SUBSCRIPT) { const GDScriptParser::SubscriptNode *subscript = static_cast<const GDScriptParser::SubscriptNode *>(call->callee); + if (subscript->base != nullptr && subscript->base->type == GDScriptParser::Node::IDENTIFIER) { + const GDScriptParser::IdentifierNode *base_identifier = static_cast<const GDScriptParser::IdentifierNode *>(subscript->base); + + Variant::Type method_type = GDScriptParser::get_builtin_type(base_identifier->name); + if (method_type < Variant::VARIANT_MAX) { + Variant v; + Callable::CallError err; + Variant::construct(method_type, v, nullptr, 0, err); + if (err.error != Callable::CallError::CALL_OK) { + return; + } + List<MethodInfo> methods; + v.get_method_list(&methods); + + for (MethodInfo &E : methods) { + if (p_argidx >= E.arguments.size()) { + continue; + } + if (E.name == call->function_name) { + r_arghint += _make_arguments_hint(E, p_argidx); + return; + } + } + } + } + if (subscript->is_attribute) { GDScriptCompletionIdentifier ci; if (_guess_expression_type(p_context, subscript->base, ci)) { @@ -2444,7 +2579,7 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c r_forced = r_result.size() > 0; } -::Error GDScriptLanguage::complete_code(const String &p_code, const String &p_path, Object *p_owner, List<ScriptCodeCompletionOption> *r_options, bool &r_forced, String &r_call_hint) { +::Error GDScriptLanguage::complete_code(const String &p_code, const String &p_path, Object *p_owner, List<ScriptLanguage::CodeCompletionOption> *r_options, bool &r_forced, String &r_call_hint) { const String quote_style = EDITOR_GET("text_editor/completion/use_single_quotes") ? "'" : "\""; GDScriptParser parser; @@ -2454,7 +2589,7 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c analyzer.analyze(); r_forced = false; - Map<String, ScriptCodeCompletionOption> options; + HashMap<String, ScriptLanguage::CodeCompletionOption> options; GDScriptParser::CompletionContext completion_context = parser.get_completion_context(); completion_context.base = p_owner; @@ -2467,7 +2602,7 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c List<MethodInfo> annotations; parser.get_annotation_list(&annotations); for (const MethodInfo &E : annotations) { - ScriptCodeCompletionOption option(E.name.substr(1), ScriptCodeCompletionOption::KIND_PLAIN_TEXT); + ScriptLanguage::CodeCompletionOption option(E.name.substr(1), ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT); if (E.arguments.size() > 0) { option.insert_text += "("; } @@ -2483,17 +2618,36 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c _find_annotation_arguments(annotation, completion_context.current_argument, quote_style, options); r_forced = true; } break; - case GDScriptParser::COMPLETION_BUILT_IN_TYPE_CONSTANT: { - List<StringName> constants; - Variant::get_constants_for_type(completion_context.builtin_type, &constants); - for (const StringName &E : constants) { - ScriptCodeCompletionOption option(E, ScriptCodeCompletionOption::KIND_CONSTANT); - bool valid = false; - Variant default_value = Variant::get_constant_value(completion_context.builtin_type, E, &valid); - if (valid) { - option.default_value = default_value; + case GDScriptParser::COMPLETION_BUILT_IN_TYPE_CONSTANT_OR_STATIC_METHOD: { + // Constants. + { + List<StringName> constants; + Variant::get_constants_for_type(completion_context.builtin_type, &constants); + for (const StringName &E : constants) { + ScriptLanguage::CodeCompletionOption option(E, ScriptLanguage::CODE_COMPLETION_KIND_CONSTANT); + bool valid = false; + Variant default_value = Variant::get_constant_value(completion_context.builtin_type, E, &valid); + if (valid) { + option.default_value = default_value; + } + options.insert(option.display, option); + } + } + // Methods. + { + List<StringName> methods; + Variant::get_builtin_method_list(completion_context.builtin_type, &methods); + for (const StringName &E : methods) { + if (Variant::is_builtin_method_static(completion_context.builtin_type, E)) { + ScriptLanguage::CodeCompletionOption option(E, ScriptLanguage::CODE_COMPLETION_KIND_FUNCTION); + if (Variant::get_builtin_method_argument_count(completion_context.builtin_type, E) > 0 || Variant::is_builtin_method_vararg(completion_context.builtin_type, E)) { + option.insert_text += "("; + } else { + option.insert_text += "()"; + } + options.insert(option.display, option); + } } - options.insert(option.display, option); } } break; case GDScriptParser::COMPLETION_INHERIT_TYPE: { @@ -2501,7 +2655,7 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c r_forced = true; } break; case GDScriptParser::COMPLETION_TYPE_NAME_OR_VOID: { - ScriptCodeCompletionOption option("void", ScriptCodeCompletionOption::KIND_PLAIN_TEXT); + ScriptLanguage::CodeCompletionOption option("void", ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT); options.insert(option.display, option); } [[fallthrough]]; @@ -2511,16 +2665,16 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c } break; case GDScriptParser::COMPLETION_PROPERTY_DECLARATION_OR_TYPE: { _list_available_types(false, completion_context, options); - ScriptCodeCompletionOption get("get", ScriptCodeCompletionOption::KIND_PLAIN_TEXT); + ScriptLanguage::CodeCompletionOption get("get", ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT); options.insert(get.display, get); - ScriptCodeCompletionOption set("set", ScriptCodeCompletionOption::KIND_PLAIN_TEXT); + ScriptLanguage::CodeCompletionOption set("set", ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT); options.insert(set.display, set); r_forced = true; } break; case GDScriptParser::COMPLETION_PROPERTY_DECLARATION: { - ScriptCodeCompletionOption get("get", ScriptCodeCompletionOption::KIND_PLAIN_TEXT); + ScriptLanguage::CodeCompletionOption get("get", ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT); options.insert(get.display, get); - ScriptCodeCompletionOption set("set", ScriptCodeCompletionOption::KIND_PLAIN_TEXT); + ScriptLanguage::CodeCompletionOption set("set", ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT); options.insert(set.display, set); r_forced = true; } break; @@ -2536,7 +2690,7 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c if (member.function->is_static) { continue; } - ScriptCodeCompletionOption option(member.function->identifier->name, ScriptCodeCompletionOption::KIND_FUNCTION); + ScriptLanguage::CodeCompletionOption option(member.function->identifier->name, ScriptLanguage::CODE_COMPLETION_KIND_FUNCTION); options.insert(option.display, option); } r_forced = true; @@ -2706,7 +2860,7 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c } method_hint += ":"; - ScriptCodeCompletionOption option(method_hint, ScriptCodeCompletionOption::KIND_FUNCTION); + ScriptLanguage::CodeCompletionOption option(method_hint, ScriptLanguage::CODE_COMPLETION_KIND_FUNCTION); options.insert(option.display, option); } } break; @@ -2729,16 +2883,14 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c if (!opt.replace("/", "_").is_valid_identifier()) { opt = opt.quote(quote_style); // Handle user preference. } - ScriptCodeCompletionOption option(opt, ScriptCodeCompletionOption::KIND_NODE_PATH); + ScriptLanguage::CodeCompletionOption option(opt, ScriptLanguage::CODE_COMPLETION_KIND_NODE_PATH); options.insert(option.display, option); } // Get autoloads. - OrderedHashMap<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list(); - - for (OrderedHashMap<StringName, ProjectSettings::AutoloadInfo>::Element E = autoloads.front(); E; E = E.next()) { - String path = "/root/" + E.key(); - ScriptCodeCompletionOption option(path.quote(quote_style), ScriptCodeCompletionOption::KIND_NODE_PATH); + for (const KeyValue<StringName, ProjectSettings::AutoloadInfo> &E : ProjectSettings::get_singleton()->get_autoload_list()) { + String path = "/root/" + E.key; + ScriptLanguage::CodeCompletionOption option(path.quote(quote_style), ScriptLanguage::CODE_COMPLETION_KIND_NODE_PATH); options.insert(option.display, option); } } @@ -2751,7 +2903,7 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c } break; } - for (const KeyValue<String, ScriptCodeCompletionOption> &E : options) { + for (const KeyValue<String, ScriptLanguage::CodeCompletionOption> &E : options) { r_options->push_back(E.value); } @@ -2760,7 +2912,7 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c #else -Error GDScriptLanguage::complete_code(const String &p_code, const String &p_path, Object *p_owner, List<ScriptCodeCompletionOption> *r_options, bool &r_forced, String &r_call_hint) { +Error GDScriptLanguage::complete_code(const String &p_code, const String &p_path, Object *p_owner, List<ScriptLanguage::CodeCompletionOption> *r_options, bool &r_forced, String &r_call_hint) { return OK; } @@ -2859,7 +3011,7 @@ static Error _lookup_symbol_from_base(const GDScriptParser::DataType &p_base, co case GDScriptParser::DataType::CLASS: { if (base_type.class_type) { if (base_type.class_type->has_member(p_symbol)) { - r_result.type = ScriptLanguage::LookupResult::RESULT_SCRIPT_LOCATION; + 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); @@ -2873,7 +3025,7 @@ static Error _lookup_symbol_from_base(const GDScriptParser::DataType &p_base, co if (scr.is_valid()) { int line = scr->get_member_line(p_symbol); if (line >= 0) { - r_result.type = ScriptLanguage::LookupResult::RESULT_SCRIPT_LOCATION; + r_result.type = ScriptLanguage::LOOKUP_RESULT_SCRIPT_LOCATION; r_result.location = line; r_result.script = scr; return OK; @@ -2897,7 +3049,7 @@ static Error _lookup_symbol_from_base(const GDScriptParser::DataType &p_base, co } if (ClassDB::has_method(class_name, p_symbol, true)) { - r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS_METHOD; + r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_METHOD; r_result.class_name = base_type.native_type; r_result.class_member = p_symbol; return OK; @@ -2907,16 +3059,23 @@ static Error _lookup_symbol_from_base(const GDScriptParser::DataType &p_base, co ClassDB::get_virtual_methods(class_name, &virtual_methods, true); for (const MethodInfo &E : virtual_methods) { if (E.name == p_symbol) { - r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS_METHOD; + r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_METHOD; r_result.class_name = base_type.native_type; r_result.class_member = p_symbol; return OK; } } + if (ClassDB::has_signal(class_name, p_symbol, true)) { + r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_SIGNAL; + r_result.class_name = base_type.native_type; + r_result.class_member = p_symbol; + return OK; + } + StringName enum_name = ClassDB::get_integer_constant_enum(class_name, p_symbol, true); if (enum_name != StringName()) { - r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS_ENUM; + r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_ENUM; r_result.class_name = base_type.native_type; r_result.class_member = enum_name; return OK; @@ -2926,7 +3085,7 @@ static Error _lookup_symbol_from_base(const GDScriptParser::DataType &p_base, co ClassDB::get_integer_constant_list(class_name, &constants, true); for (const String &E : constants) { if (E == p_symbol) { - r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS_CONSTANT; + r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_CONSTANT; r_result.class_name = base_type.native_type; r_result.class_member = p_symbol; return OK; @@ -2934,7 +3093,7 @@ static Error _lookup_symbol_from_base(const GDScriptParser::DataType &p_base, co } if (ClassDB::has_property(class_name, p_symbol, true)) { - r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS_PROPERTY; + r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_PROPERTY; r_result.class_name = base_type.native_type; r_result.class_member = p_symbol; return OK; @@ -2951,14 +3110,14 @@ static Error _lookup_symbol_from_base(const GDScriptParser::DataType &p_base, co base_type.kind = GDScriptParser::DataType::UNRESOLVED; if (Variant::has_constant(base_type.builtin_type, p_symbol)) { - r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS_CONSTANT; + r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_CONSTANT; r_result.class_name = Variant::get_type_name(base_type.builtin_type); r_result.class_member = p_symbol; return OK; } Variant v; - REF v_ref; + Ref<RefCounted> v_ref; if (base_type.builtin_type == Variant::OBJECT) { v_ref.instantiate(); v = v_ref; @@ -2971,7 +3130,7 @@ static Error _lookup_symbol_from_base(const GDScriptParser::DataType &p_base, co } if (v.has_method(p_symbol)) { - r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS_METHOD; + r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_METHOD; r_result.class_name = Variant::get_type_name(base_type.builtin_type); r_result.class_member = p_symbol; return OK; @@ -2980,7 +3139,7 @@ static Error _lookup_symbol_from_base(const GDScriptParser::DataType &p_base, co bool valid = false; v.get(p_symbol, &valid); if (valid) { - r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS_PROPERTY; + r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_PROPERTY; r_result.class_name = Variant::get_type_name(base_type.builtin_type); r_result.class_member = p_symbol; return OK; @@ -2996,9 +3155,9 @@ static Error _lookup_symbol_from_base(const GDScriptParser::DataType &p_base, co } ::Error GDScriptLanguage::lookup_code(const String &p_code, const String &p_symbol, const String &p_path, Object *p_owner, LookupResult &r_result) { - // Before parsing, try the usual stuff + // Before parsing, try the usual stuff. if (ClassDB::class_exists(p_symbol)) { - r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS; + r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS; r_result.class_name = p_symbol; return OK; } @@ -3006,21 +3165,23 @@ static Error _lookup_symbol_from_base(const GDScriptParser::DataType &p_base, co for (int i = 0; i < Variant::VARIANT_MAX; i++) { Variant::Type t = Variant::Type(i); if (Variant::get_type_name(t) == p_symbol) { - r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS; + r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS; r_result.class_name = Variant::get_type_name(t); return OK; } } - if (GDScriptUtilityFunctions::function_exists(p_symbol)) { - r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS_METHOD; + // Need special checks for assert and preload as they are technically + // keywords, so are not registered in GDScriptUtilityFunctions. + if (GDScriptUtilityFunctions::function_exists(p_symbol) || "assert" == p_symbol || "preload" == p_symbol) { + r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_METHOD; r_result.class_name = "@GDScript"; r_result.class_member = p_symbol; return OK; } if ("PI" == p_symbol || "TAU" == p_symbol || "INF" == p_symbol || "NAN" == p_symbol) { - r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS_CONSTANT; + r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_CONSTANT; r_result.class_name = "@GDScript"; r_result.class_member = p_symbol; return OK; @@ -3037,7 +3198,7 @@ static Error _lookup_symbol_from_base(const GDScriptParser::DataType &p_base, co bool success = false; ClassDB::get_integer_constant(context.current_class->extends[0], p_symbol, &success); if (success) { - r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS_CONSTANT; + r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_CONSTANT; r_result.class_name = context.current_class->extends[0]; r_result.class_member = p_symbol; return OK; @@ -3047,17 +3208,28 @@ static Error _lookup_symbol_from_base(const GDScriptParser::DataType &p_base, co bool is_function = false; switch (context.type) { - case GDScriptParser::COMPLETION_BUILT_IN_TYPE_CONSTANT: { - r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS_CONSTANT; - r_result.class_name = Variant::get_type_name(context.builtin_type); - r_result.class_member = p_symbol; - return OK; + case GDScriptParser::COMPLETION_BUILT_IN_TYPE_CONSTANT_OR_STATIC_METHOD: { + if (!Variant::has_builtin_method(context.builtin_type, StringName(p_symbol))) { + // A constant. + r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_CONSTANT; + r_result.class_name = Variant::get_type_name(context.builtin_type); + r_result.class_member = p_symbol; + return OK; + } + // A method. + GDScriptParser::DataType base_type; + base_type.kind = GDScriptParser::DataType::BUILTIN; + base_type.builtin_type = context.builtin_type; + if (_lookup_symbol_from_base(base_type, p_symbol, true, r_result) == OK) { + return OK; + } } break; case GDScriptParser::COMPLETION_SUPER_METHOD: case GDScriptParser::COMPLETION_METHOD: { is_function = true; [[fallthrough]]; } + case GDScriptParser::COMPLETION_ASSIGN: case GDScriptParser::COMPLETION_CALL_ARGUMENTS: case GDScriptParser::COMPLETION_IDENTIFIER: { GDScriptParser::DataType base_type; @@ -3076,7 +3248,7 @@ static Error _lookup_symbol_from_base(const GDScriptParser::DataType &p_base, co const GDScriptParser::SuiteNode *suite = context.current_suite; while (suite) { if (suite->has_local(p_symbol)) { - r_result.type = ScriptLanguage::LookupResult::RESULT_SCRIPT_LOCATION; + r_result.type = ScriptLanguage::LOOKUP_RESULT_SCRIPT_LOCATION; r_result.location = suite->get_local(p_symbol).start_line; return OK; } @@ -3101,7 +3273,7 @@ static Error _lookup_symbol_from_base(const GDScriptParser::DataType &p_base, co } if (FileAccess::exists(script)) { - r_result.type = ScriptLanguage::LookupResult::RESULT_SCRIPT_LOCATION; + r_result.type = ScriptLanguage::LOOKUP_RESULT_SCRIPT_LOCATION; r_result.location = 0; r_result.script = ResourceLoader::load(script); return OK; @@ -3110,17 +3282,17 @@ static Error _lookup_symbol_from_base(const GDScriptParser::DataType &p_base, co } // Global. - Map<StringName, int> classes = GDScriptLanguage::get_singleton()->get_global_map(); + HashMap<StringName, int> classes = GDScriptLanguage::get_singleton()->get_global_map(); if (classes.has(p_symbol)) { Variant value = GDScriptLanguage::get_singleton()->get_global_array()[classes[p_symbol]]; if (value.get_type() == Variant::OBJECT) { Object *obj = value; if (obj) { if (Object::cast_to<GDScriptNativeClass>(obj)) { - r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS; + r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS; r_result.class_name = Object::cast_to<GDScriptNativeClass>(obj)->get_name(); } else { - r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS; + r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS; r_result.class_name = obj->get_class(); } @@ -3137,18 +3309,18 @@ static Error _lookup_symbol_from_base(const GDScriptParser::DataType &p_base, co // Otherwise these codes would work StringName enumName = ClassDB::get_integer_constant_enum("@GlobalScope", p_symbol, true); if (enumName != nullptr) { - r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS_ENUM; + r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_ENUM; r_result.class_name = "@GlobalScope"; r_result.class_member = enumName; return OK; } else { - r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS_CONSTANT; + r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_CONSTANT; r_result.class_name = "@GlobalScope"; r_result.class_member = p_symbol; return OK; }*/ - r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS_TBD_GLOBALSCOPE; + r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_TBD_GLOBALSCOPE; r_result.class_name = "@GlobalScope"; r_result.class_member = p_symbol; return OK; @@ -3157,7 +3329,7 @@ static Error _lookup_symbol_from_base(const GDScriptParser::DataType &p_base, co List<StringName> utility_functions; Variant::get_utility_function_list(&utility_functions); if (utility_functions.find(p_symbol) != nullptr) { - r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS_TBD_GLOBALSCOPE; + r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_TBD_GLOBALSCOPE; r_result.class_name = "@GlobalScope"; r_result.class_member = p_symbol; return OK; diff --git a/modules/gdscript/gdscript_function.cpp b/modules/gdscript/gdscript_function.cpp index 3d708955ed..cd3b7d69c5 100644 --- a/modules/gdscript/gdscript_function.cpp +++ b/modules/gdscript/gdscript_function.cpp @@ -93,7 +93,7 @@ struct _GDFKCS { void GDScriptFunction::debug_get_stack_member_state(int p_line, List<Pair<StringName, int>> *r_stackvars) const { int oc = 0; - Map<StringName, _GDFKC> sdmap; + HashMap<StringName, _GDFKC> sdmap; for (const StackDebug &sd : stack_debug) { if (sd.line >= p_line) { break; @@ -270,6 +270,8 @@ Variant GDScriptFunctionState::resume(const Variant &p_arg) { if (EngineDebugger::is_active()) { GDScriptLanguage::get_singleton()->exit_function(); } + + _clear_stack(); #endif } @@ -279,7 +281,8 @@ Variant GDScriptFunctionState::resume(const Variant &p_arg) { void GDScriptFunctionState::_clear_stack() { if (state.stack_size) { Variant *stack = (Variant *)state.stack.ptr(); - for (int i = 0; i < state.stack_size; i++) { + // The first 3 are special addresses and not copied to the state, so we skip them here. + for (int i = 3; i < state.stack_size; i++) { stack[i].~Variant(); } state.stack_size = 0; @@ -300,8 +303,6 @@ GDScriptFunctionState::GDScriptFunctionState() : } GDScriptFunctionState::~GDScriptFunctionState() { - _clear_stack(); - { MutexLock lock(GDScriptLanguage::singleton->lock); scripts_list.remove_from_list(); diff --git a/modules/gdscript/gdscript_function.h b/modules/gdscript/gdscript_function.h index 3ee664c76d..d2ca795977 100644 --- a/modules/gdscript/gdscript_function.h +++ b/modules/gdscript/gdscript_function.h @@ -259,6 +259,7 @@ public: OPCODE_CALL_METHOD_BIND, OPCODE_CALL_METHOD_BIND_RET, OPCODE_CALL_BUILTIN_STATIC, + OPCODE_CALL_NATIVE_STATIC, // ptrcall have one instruction per return type. OPCODE_CALL_PTRCALL_NO_RETURN, OPCODE_CALL_PTRCALL_BOOL, @@ -298,6 +299,7 @@ public: OPCODE_AWAIT, OPCODE_AWAIT_RESUME, OPCODE_CREATE_LAMBDA, + OPCODE_CREATE_SELF_LAMBDA, OPCODE_JUMP, OPCODE_JUMP_IF, OPCODE_JUMP_IF_NOT, @@ -364,7 +366,7 @@ public: OPCODE_TYPE_ADJUST_QUATERNION, OPCODE_TYPE_ADJUST_AABB, OPCODE_TYPE_ADJUST_BASIS, - OPCODE_TYPE_ADJUST_TRANSFORM, + OPCODE_TYPE_ADJUST_TRANSFORM3D, OPCODE_TYPE_ADJUST_COLOR, OPCODE_TYPE_ADJUST_STRING_NAME, OPCODE_TYPE_ADJUST_NODE_PATH, @@ -493,7 +495,7 @@ private: Vector<GDScriptDataType> argument_types; GDScriptDataType return_type; - Map<int, Variant::Type> temporary_slots; + HashMap<int, Variant::Type> temporary_slots; #ifdef TOOLS_ENABLED Vector<StringName> arg_names; diff --git a/modules/gdscript/gdscript_lambda_callable.cpp b/modules/gdscript/gdscript_lambda_callable.cpp index baf93e1098..c43fa12c8c 100644 --- a/modules/gdscript/gdscript_lambda_callable.cpp +++ b/modules/gdscript/gdscript_lambda_callable.cpp @@ -93,3 +93,81 @@ GDScriptLambdaCallable::GDScriptLambdaCallable(Ref<GDScript> p_script, GDScriptF h = (uint32_t)hash_djb2_one_64((uint64_t)this); } + +bool GDScriptLambdaSelfCallable::compare_equal(const CallableCustom *p_a, const CallableCustom *p_b) { + // Lambda callables are only compared by reference. + return p_a == p_b; +} + +bool GDScriptLambdaSelfCallable::compare_less(const CallableCustom *p_a, const CallableCustom *p_b) { + // Lambda callables are only compared by reference. + return p_a < p_b; +} + +uint32_t GDScriptLambdaSelfCallable::hash() const { + return h; +} + +String GDScriptLambdaSelfCallable::get_as_text() const { + if (function->get_name() != StringName()) { + return function->get_name().operator String() + "(lambda)"; + } + return "(anonymous lambda)"; +} + +CallableCustom::CompareEqualFunc GDScriptLambdaSelfCallable::get_compare_equal_func() const { + return compare_equal; +} + +CallableCustom::CompareLessFunc GDScriptLambdaSelfCallable::get_compare_less_func() const { + return compare_less; +} + +ObjectID GDScriptLambdaSelfCallable::get_object() const { + return object->get_instance_id(); +} + +void GDScriptLambdaSelfCallable::call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const { +#ifdef DEBUG_ENABLED + if (object->get_script_instance() == nullptr || object->get_script_instance()->get_language() != GDScriptLanguage::get_singleton()) { + ERR_PRINT("Trying to call a lambda with an invalid instance."); + r_call_error.error = Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL; + return; + } +#endif + + int captures_amount = captures.size(); + + if (captures_amount > 0) { + Vector<const Variant *> args; + args.resize(p_argcount + captures_amount); + for (int i = 0; i < captures_amount; i++) { + args.write[i] = &captures[i]; + } + for (int i = 0; i < p_argcount; i++) { + args.write[i + captures_amount] = p_arguments[i]; + } + + r_return_value = function->call(static_cast<GDScriptInstance *>(object->get_script_instance()), args.ptrw(), args.size(), r_call_error); + r_call_error.argument -= captures_amount; + } else { + r_return_value = function->call(static_cast<GDScriptInstance *>(object->get_script_instance()), p_arguments, p_argcount, r_call_error); + } +} + +GDScriptLambdaSelfCallable::GDScriptLambdaSelfCallable(Ref<RefCounted> p_self, GDScriptFunction *p_function, const Vector<Variant> &p_captures) { + reference = p_self; + object = p_self.ptr(); + function = p_function; + captures = p_captures; + + h = (uint32_t)hash_djb2_one_64((uint64_t)this); +} + +GDScriptLambdaSelfCallable::GDScriptLambdaSelfCallable(Object *p_self, GDScriptFunction *p_function, const Vector<Variant> &p_captures) { + object = p_self; + function = p_function; + captures = p_captures; + + h = (uint32_t)hash_djb2_one_64((uint64_t)this); +} diff --git a/modules/gdscript/gdscript_lambda_callable.h b/modules/gdscript/gdscript_lambda_callable.h index f6a54a1a2f..248176e32c 100644 --- a/modules/gdscript/gdscript_lambda_callable.h +++ b/modules/gdscript/gdscript_lambda_callable.h @@ -62,4 +62,29 @@ public: virtual ~GDScriptLambdaCallable() = default; }; +// Lambda callable that references a particular object, so it can use `self` in the body. +class GDScriptLambdaSelfCallable : public CallableCustom { + GDScriptFunction *function = nullptr; + Ref<RefCounted> reference; // For objects that are RefCounted, keep a reference. + Object *object = nullptr; // For non RefCounted objects, use a direct pointer. + uint32_t h; + + Vector<Variant> captures; + + static bool compare_equal(const CallableCustom *p_a, const CallableCustom *p_b); + static bool compare_less(const CallableCustom *p_a, const CallableCustom *p_b); + +public: + uint32_t hash() const override; + String get_as_text() const override; + CompareEqualFunc get_compare_equal_func() const override; + CompareLessFunc get_compare_less_func() const override; + ObjectID get_object() const override; + void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const override; + + GDScriptLambdaSelfCallable(Ref<RefCounted> p_self, GDScriptFunction *p_function, const Vector<Variant> &p_captures); + GDScriptLambdaSelfCallable(Object *p_self, GDScriptFunction *p_function, const Vector<Variant> &p_captures); + virtual ~GDScriptLambdaSelfCallable() = default; +}; + #endif // GDSCRIPT_LAMBDA_CALLABLE diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp index 10709d3667..bc225850c9 100644 --- a/modules/gdscript/gdscript_parser.cpp +++ b/modules/gdscript/gdscript_parser.cpp @@ -100,10 +100,8 @@ void GDScriptParser::cleanup() { } void GDScriptParser::get_annotation_list(List<MethodInfo> *r_annotations) const { - List<StringName> keys; - valid_annotations.get_key_list(&keys); - for (const StringName &E : keys) { - r_annotations->push_back(valid_annotations[E].info); + for (const KeyValue<StringName, AnnotationInfo> &E : valid_annotations) { + r_annotations->push_back(E.value.info); } } @@ -205,7 +203,8 @@ void GDScriptParser::push_warning(const Node *p_source, GDScriptWarning::Code p_ if (ignored_warnings.has(warn_name)) { return; } - if (!GLOBAL_GET("debug/gdscript/warnings/" + warn_name)) { + int warn_level = (int)GLOBAL_GET(GDScriptWarning::get_settings_path_from_code(p_code)); + if (!warn_level) { return; } @@ -217,6 +216,11 @@ void GDScriptParser::push_warning(const Node *p_source, GDScriptWarning::Code p_ warning.leftmost_column = p_source->leftmost_column; warning.rightmost_column = p_source->rightmost_column; + if (warn_level == GDScriptWarning::WarnLevel::ERROR) { + push_error(warning.get_message(), p_source); + return; + } + List<GDScriptWarning>::Element *before = nullptr; for (List<GDScriptWarning>::Element *E = warnings.front(); E; E = E->next()) { if (E->get().start_line > warning.start_line) { @@ -1626,6 +1630,10 @@ GDScriptParser::Node *GDScriptParser::parse_statement() { case Node::AWAIT: // Fine. break; + case Node::LAMBDA: + // Standalone lambdas can't be used, so make this an error. + push_error("Standalone lambdas cannot be accessed. Consider assigning it to a variable.", expression); + break; default: push_warning(expression, GDScriptWarning::STANDALONE_EXPRESSION); } @@ -1810,7 +1818,6 @@ GDScriptParser::MatchNode *GDScriptParser::parse_match() { #ifdef DEBUG_ENABLED bool all_have_return = true; bool have_wildcard = false; - bool wildcard_has_return = false; bool have_wildcard_without_continue = false; #endif @@ -1828,9 +1835,6 @@ GDScriptParser::MatchNode *GDScriptParser::parse_match() { if (branch->has_wildcard) { have_wildcard = true; - if (branch->block->has_return) { - wildcard_has_return = true; - } if (!branch->block->has_continue) { have_wildcard_without_continue = true; } @@ -1845,7 +1849,7 @@ GDScriptParser::MatchNode *GDScriptParser::parse_match() { consume(GDScriptTokenizer::Token::DEDENT, R"(Expected an indented block after "match" statement.)"); #ifdef DEBUG_ENABLED - if (wildcard_has_return || (all_have_return && have_wildcard)) { + if (all_have_return && have_wildcard) { current_suite->has_return = true; } #endif @@ -1863,7 +1867,7 @@ GDScriptParser::MatchBranchNode *GDScriptParser::parse_match_branch() { if (pattern == nullptr) { continue; } - if (pattern->pattern_type == PatternNode::PT_BIND) { + if (pattern->binds.size() > 0) { has_bind = true; } if (branch->patterns.size() > 0 && has_bind) { @@ -1894,11 +1898,9 @@ GDScriptParser::MatchBranchNode *GDScriptParser::parse_match_branch() { SuiteNode *suite = alloc_node<SuiteNode>(); if (branch->patterns.size() > 0) { - List<StringName> binds; - branch->patterns[0]->binds.get_key_list(&binds); - - for (const StringName &E : binds) { - SuiteNode::Local local(branch->patterns[0]->binds[E], current_function); + for (const KeyValue<StringName, IdentifierNode *> &E : branch->patterns[0]->binds) { + SuiteNode::Local local(E.value, current_function); + local.type = SuiteNode::Local::PATTERN_BIND; suite->add_local(local); } } @@ -2107,7 +2109,7 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_precedence(Precedence p_pr ExpressionNode *previous_operand = (this->*prefix_rule)(nullptr, p_can_assign); while (p_precedence <= get_rule(current.type)->precedence) { - if (previous_operand == nullptr || (p_stop_on_assign && current.type == GDScriptTokenizer::Token::EQUAL)) { + if (previous_operand == nullptr || (p_stop_on_assign && current.type == GDScriptTokenizer::Token::EQUAL) || (previous_operand->type == Node::LAMBDA && lambda_ended)) { return previous_operand; } // Also switch multiline mode on here for infix operators. @@ -2200,9 +2202,6 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_self(ExpressionNode *p_pre if (current_function && current_function->is_static) { push_error(R"(Cannot use "self" inside a static function.)"); } - if (in_lambda) { - push_error(R"(Cannot use "self" inside a lambda.)"); - } SelfNode *self = alloc_node<SelfNode>(); self->current_class = current_class; return self; @@ -2322,6 +2321,10 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_binary_operator(Expression operation->operation = BinaryOpNode::OP_MODULO; operation->variant_op = Variant::OP_MODULE; break; + case GDScriptTokenizer::Token::STAR_STAR: + operation->operation = BinaryOpNode::OP_POWER; + operation->variant_op = Variant::OP_POWER; + break; case GDScriptTokenizer::Token::LESS_LESS: operation->operation = BinaryOpNode::OP_BIT_LEFT_SHIFT; operation->variant_op = Variant::OP_SHIFT_LEFT; @@ -2485,6 +2488,10 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_assignment(ExpressionNode assignment->operation = AssignmentNode::OP_MULTIPLICATION; assignment->variant_op = Variant::OP_MULTIPLY; break; + case GDScriptTokenizer::Token::STAR_STAR_EQUAL: + assignment->operation = AssignmentNode::OP_POWER; + assignment->variant_op = Variant::OP_POWER; + break; case GDScriptTokenizer::Token::SLASH_EQUAL: assignment->operation = AssignmentNode::OP_DIVISION; assignment->variant_op = Variant::OP_DIVIDE; @@ -2685,7 +2692,7 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_attribute(ExpressionNode * const IdentifierNode *id = static_cast<const IdentifierNode *>(p_previous_operand); Variant::Type builtin_type = get_builtin_type(id->name); if (builtin_type < Variant::VARIANT_MAX) { - make_completion_context(COMPLETION_BUILT_IN_TYPE_CONSTANT, builtin_type, true); + make_completion_context(COMPLETION_BUILT_IN_TYPE_CONSTANT_OR_STATIC_METHOD, builtin_type, true); is_builtin = true; } } @@ -2752,7 +2759,11 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_call(ExpressionNode *p_pre pop_multiline(); return nullptr; } - call->function_name = current_function->identifier->name; + if (current_function->identifier) { + call->function_name = current_function->identifier->name; + } else { + call->function_name = SNAME("<anonymous>"); + } } else { consume(GDScriptTokenizer::Token::PERIOD, R"(Expected "." or "(" after "super".)"); make_completion_context(COMPLETION_SUPER_METHOD, call, true); @@ -2819,51 +2830,97 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_call(ExpressionNode *p_pre } GDScriptParser::ExpressionNode *GDScriptParser::parse_get_node(ExpressionNode *p_previous_operand, bool p_can_assign) { - if (match(GDScriptTokenizer::Token::LITERAL)) { - if (previous.literal.get_type() != Variant::STRING) { - push_error(R"(Expect node path as string or identifier after "$".)"); + if (!current.is_node_name() && !check(GDScriptTokenizer::Token::LITERAL) && !check(GDScriptTokenizer::Token::SLASH) && !check(GDScriptTokenizer::Token::PERCENT)) { + push_error(vformat(R"(Expected node path as string or identifier after "%s".)", previous.get_name())); + return nullptr; + } + + if (check(GDScriptTokenizer::Token::LITERAL)) { + if (current.literal.get_type() != Variant::STRING) { + push_error(vformat(R"(Expected node path as string or identifier after "%s".)", previous.get_name())); return nullptr; } - GetNodeNode *get_node = alloc_node<GetNodeNode>(); - make_completion_context(COMPLETION_GET_NODE, get_node); - get_node->string = parse_literal(); - return get_node; - } else if (current.is_node_name()) { - GetNodeNode *get_node = alloc_node<GetNodeNode>(); - int chain_position = 0; - do { - make_completion_context(COMPLETION_GET_NODE, get_node, chain_position++); - if (!current.is_node_name()) { - push_error(R"(Expect node path after "/".)"); + } + + GetNodeNode *get_node = alloc_node<GetNodeNode>(); + + // Store the last item in the path so the parser knows what to expect. + // Allow allows more specific error messages. + enum PathState { + PATH_STATE_START, + PATH_STATE_SLASH, + PATH_STATE_PERCENT, + PATH_STATE_NODE_NAME, + } path_state = PATH_STATE_START; + + if (previous.type == GDScriptTokenizer::Token::DOLLAR) { + // Detect initial slash, which will be handled in the loop if it matches. + match(GDScriptTokenizer::Token::SLASH); +#ifdef DEBUG_ENABLED + } else { + get_node->use_dollar = false; +#endif + } + + int context_argument = 0; + + do { + if (previous.type == GDScriptTokenizer::Token::PERCENT) { + if (path_state != PATH_STATE_START && path_state != PATH_STATE_SLASH) { + push_error(R"("%" is only valid in the beginning of a node name (either after "$" or after "/"))"); return nullptr; } - advance(); - IdentifierNode *identifier = alloc_node<IdentifierNode>(); - identifier->name = previous.get_identifier(); - get_node->chain.push_back(identifier); - } while (match(GDScriptTokenizer::Token::SLASH)); - return get_node; - } else if (match(GDScriptTokenizer::Token::SLASH)) { - GetNodeNode *get_node = alloc_node<GetNodeNode>(); - IdentifierNode *identifier_root = alloc_node<IdentifierNode>(); - get_node->chain.push_back(identifier_root); - int chain_position = 0; - do { - make_completion_context(COMPLETION_GET_NODE, get_node, chain_position++); - if (!current.is_node_name()) { - push_error(R"(Expect node path after "/".)"); + get_node->full_path += "%"; + + path_state = PATH_STATE_PERCENT; + } else if (previous.type == GDScriptTokenizer::Token::SLASH) { + if (path_state != PATH_STATE_START && path_state != PATH_STATE_NODE_NAME) { + push_error(R"("/" is only valid at the beginning of the path or after a node name.)"); + return nullptr; + } + + get_node->full_path += "/"; + + path_state = PATH_STATE_SLASH; + } + + make_completion_context(COMPLETION_GET_NODE, get_node, context_argument++); + + if (match(GDScriptTokenizer::Token::LITERAL)) { + if (previous.literal.get_type() != Variant::STRING) { + String previous_token; + switch (path_state) { + case PATH_STATE_START: + previous_token = "$"; + break; + case PATH_STATE_PERCENT: + previous_token = "%"; + break; + case PATH_STATE_SLASH: + previous_token = "/"; + break; + default: + break; + } + push_error(vformat(R"(Expected node path as string or identifier after "%s".)", previous_token)); return nullptr; } + + get_node->full_path += previous.literal.operator String(); + + path_state = PATH_STATE_NODE_NAME; + } else if (current.is_node_name()) { advance(); - IdentifierNode *identifier = alloc_node<IdentifierNode>(); - identifier->name = previous.get_identifier(); - get_node->chain.push_back(identifier); - } while (match(GDScriptTokenizer::Token::SLASH)); - return get_node; - } else { - push_error(R"(Expect node path as string or identifier after "$".)"); - return nullptr; - } + get_node->full_path += previous.get_identifier(); + + path_state = PATH_STATE_NODE_NAME; + } else if (!check(GDScriptTokenizer::Token::SLASH) && !check(GDScriptTokenizer::Token::PERCENT)) { + push_error(vformat(R"(Unexpected "%s" in node path.)", current.get_name())); + return nullptr; + } + } while (match(GDScriptTokenizer::Token::SLASH) || match(GDScriptTokenizer::Token::PERCENT)); + + return get_node; } GDScriptParser::ExpressionNode *GDScriptParser::parse_preload(ExpressionNode *p_previous_operand, bool p_can_assign) { @@ -2921,6 +2978,9 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_lambda(ExpressionNode *p_p current_function = function; SuiteNode *body = alloc_node<SuiteNode>(); + body->parent_function = current_function; + body->parent_block = current_suite; + SuiteNode *previous_suite = current_suite; current_suite = body; @@ -3048,7 +3108,7 @@ bool GDScriptParser::has_comment(int p_line) { } String GDScriptParser::get_doc_comment(int p_line, bool p_single_line) { - const Map<int, GDScriptTokenizer::CommentData> &comments = tokenizer.get_comments(); + const HashMap<int, GDScriptTokenizer::CommentData> &comments = tokenizer.get_comments(); ERR_FAIL_COND_V(!comments.has(p_line), String()); if (p_single_line) { @@ -3100,7 +3160,7 @@ String GDScriptParser::get_doc_comment(int p_line, bool p_single_line) { } void GDScriptParser::get_class_doc_comment(int p_line, String &p_brief, String &p_desc, Vector<Pair<String, String>> &p_tutorials, bool p_inner_class) { - const Map<int, GDScriptTokenizer::CommentData> &comments = tokenizer.get_comments(); + const HashMap<int, GDScriptTokenizer::CommentData> &comments = tokenizer.get_comments(); if (!comments.has(p_line)) { return; } @@ -3133,24 +3193,23 @@ void GDScriptParser::get_class_doc_comment(int p_line, String &p_brief, String & String title, link; // For tutorials. String doc_line = comments[line++].comment.trim_prefix("##"); - String striped_line = doc_line.strip_edges(); + String stripped_line = doc_line.strip_edges(); // Set the read mode. - if (striped_line.begins_with("@desc:") && p_desc.is_empty()) { + if (stripped_line.is_empty() && mode == BRIEF && !p_brief.is_empty()) { mode = DESC; - striped_line = striped_line.trim_prefix("@desc:"); - in_codeblock = _in_codeblock(doc_line, in_codeblock); + continue; - } else if (striped_line.begins_with("@tutorial")) { + } else if (stripped_line.begins_with("@tutorial")) { int begin_scan = String("@tutorial").length(); - if (begin_scan >= striped_line.length()) { + if (begin_scan >= stripped_line.length()) { continue; // invalid syntax. } - if (striped_line[begin_scan] == ':') { // No title. + if (stripped_line[begin_scan] == ':') { // No title. // Syntax: ## @tutorial: https://godotengine.org/ // The title argument is optional. title = ""; - link = striped_line.trim_prefix("@tutorial:").strip_edges(); + link = stripped_line.trim_prefix("@tutorial:").strip_edges(); } else { /* Syntax: @@ -3158,35 +3217,35 @@ void GDScriptParser::get_class_doc_comment(int p_line, String &p_brief, String & * ^ open ^ close ^ colon ^ url */ int open_bracket_pos = begin_scan, close_bracket_pos = 0; - while (open_bracket_pos < striped_line.length() && (striped_line[open_bracket_pos] == ' ' || striped_line[open_bracket_pos] == '\t')) { + while (open_bracket_pos < stripped_line.length() && (stripped_line[open_bracket_pos] == ' ' || stripped_line[open_bracket_pos] == '\t')) { open_bracket_pos++; } - if (open_bracket_pos == striped_line.length() || striped_line[open_bracket_pos++] != '(') { + if (open_bracket_pos == stripped_line.length() || stripped_line[open_bracket_pos++] != '(') { continue; // invalid syntax. } close_bracket_pos = open_bracket_pos; - while (close_bracket_pos < striped_line.length() && striped_line[close_bracket_pos] != ')') { + while (close_bracket_pos < stripped_line.length() && stripped_line[close_bracket_pos] != ')') { close_bracket_pos++; } - if (close_bracket_pos == striped_line.length()) { + if (close_bracket_pos == stripped_line.length()) { continue; // invalid syntax. } int colon_pos = close_bracket_pos + 1; - while (colon_pos < striped_line.length() && (striped_line[colon_pos] == ' ' || striped_line[colon_pos] == '\t')) { + while (colon_pos < stripped_line.length() && (stripped_line[colon_pos] == ' ' || stripped_line[colon_pos] == '\t')) { colon_pos++; } - if (colon_pos == striped_line.length() || striped_line[colon_pos++] != ':') { + if (colon_pos == stripped_line.length() || stripped_line[colon_pos++] != ':') { continue; // invalid syntax. } - title = striped_line.substr(open_bracket_pos, close_bracket_pos - open_bracket_pos).strip_edges(); - link = striped_line.substr(colon_pos).strip_edges(); + title = stripped_line.substr(open_bracket_pos, close_bracket_pos - open_bracket_pos).strip_edges(); + link = stripped_line.substr(colon_pos).strip_edges(); } mode = TUTORIALS; in_codeblock = false; - } else if (striped_line.is_empty()) { + } else if (stripped_line.is_empty()) { continue; } else { // Tutorial docs are single line, we need a @tag after it. @@ -3206,7 +3265,7 @@ void GDScriptParser::get_class_doc_comment(int p_line, String &p_brief, String & } doc_line = doc_line.substr(i); } else { - doc_line = striped_line; + doc_line = stripped_line; } String line_join = (in_codeblock) ? "\n" : " "; @@ -3263,13 +3322,15 @@ GDScriptParser::ParseRule *GDScriptParser::get_rule(GDScriptTokenizer::Token::Ty { &GDScriptParser::parse_unary_operator, &GDScriptParser::parse_binary_operator, PREC_ADDITION_SUBTRACTION }, // PLUS, { &GDScriptParser::parse_unary_operator, &GDScriptParser::parse_binary_operator, PREC_ADDITION_SUBTRACTION }, // MINUS, { nullptr, &GDScriptParser::parse_binary_operator, PREC_FACTOR }, // STAR, + { nullptr, &GDScriptParser::parse_binary_operator, PREC_POWER }, // STAR_STAR, { nullptr, &GDScriptParser::parse_binary_operator, PREC_FACTOR }, // SLASH, - { nullptr, &GDScriptParser::parse_binary_operator, PREC_FACTOR }, // PERCENT, + { &GDScriptParser::parse_get_node, &GDScriptParser::parse_binary_operator, PREC_FACTOR }, // PERCENT, // Assignment { nullptr, &GDScriptParser::parse_assignment, PREC_ASSIGNMENT }, // EQUAL, { nullptr, &GDScriptParser::parse_assignment, PREC_ASSIGNMENT }, // PLUS_EQUAL, { nullptr, &GDScriptParser::parse_assignment, PREC_ASSIGNMENT }, // MINUS_EQUAL, { nullptr, &GDScriptParser::parse_assignment, PREC_ASSIGNMENT }, // STAR_EQUAL, + { nullptr, &GDScriptParser::parse_assignment, PREC_ASSIGNMENT }, // STAR_STAR_EQUAL, { nullptr, &GDScriptParser::parse_assignment, PREC_ASSIGNMENT }, // SLASH_EQUAL, { nullptr, &GDScriptParser::parse_assignment, PREC_ASSIGNMENT }, // PERCENT_EQUAL, { nullptr, &GDScriptParser::parse_assignment, PREC_ASSIGNMENT }, // LESS_LESS_EQUAL, @@ -3554,14 +3615,16 @@ bool GDScriptParser::export_annotations(const AnnotationNode *p_annotation, Node variable->export_info.hint = PROPERTY_HINT_ENUM; String enum_hint_string; - for (OrderedHashMap<StringName, int>::Element E = export_type.enum_values.front(); E; E = E.next()) { - enum_hint_string += E.key().operator String().capitalize().xml_escape(); - enum_hint_string += ":"; - enum_hint_string += String::num_int64(E.value()).xml_escape(); - - if (E.next()) { + bool first = true; + for (const KeyValue<StringName, int> &E : export_type.enum_values) { + if (!first) { enum_hint_string += ","; + } else { + first = false; } + enum_hint_string += E.key.operator String().capitalize().xml_escape(); + enum_hint_string += ":"; + enum_hint_string += String::num_int64(E.value).xml_escape(); } variable->export_info.hint_string = enum_hint_string; @@ -3894,6 +3957,9 @@ void GDScriptParser::TreePrinter::print_assignment(AssignmentNode *p_assignment) case AssignmentNode::OP_MODULO: push_text("%"); break; + case AssignmentNode::OP_POWER: + push_text("**"); + break; case AssignmentNode::OP_BIT_SHIFT_LEFT: push_text("<<"); break; @@ -3942,6 +4008,9 @@ void GDScriptParser::TreePrinter::print_binary_op(BinaryOpNode *p_binary_op) { case BinaryOpNode::OP_MODULO: push_text(" % "); break; + case BinaryOpNode::OP_POWER: + push_text(" ** "); + break; case BinaryOpNode::OP_BIT_LEFT_SHIFT: push_text(" << "); break; @@ -4235,17 +4304,10 @@ void GDScriptParser::TreePrinter::print_function(FunctionNode *p_function, const } void GDScriptParser::TreePrinter::print_get_node(GetNodeNode *p_get_node) { - push_text("$"); - if (p_get_node->string != nullptr) { - print_literal(p_get_node->string); - } else { - for (int i = 0; i < p_get_node->chain.size(); i++) { - if (i > 0) { - push_text("/"); - } - print_identifier(p_get_node->chain[i]); - } + if (p_get_node->use_dollar) { + push_text("$"); } + push_text(p_get_node->full_path); } void GDScriptParser::TreePrinter::print_identifier(IdentifierNode *p_identifier) { diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h index c09b07282f..e3f8d4b8ba 100644 --- a/modules/gdscript/gdscript_parser.h +++ b/modules/gdscript/gdscript_parser.h @@ -39,7 +39,7 @@ #include "core/string/ustring.h" #include "core/templates/hash_map.h" #include "core/templates/list.h" -#include "core/templates/map.h" +#include "core/templates/rb_map.h" #include "core/templates/vector.h" #include "core/variant/variant.h" #include "gdscript_cache.h" @@ -132,7 +132,7 @@ public: ClassNode *class_type = nullptr; MethodInfo method_info; // For callable/signals. - OrderedHashMap<StringName, int> enum_values; // For enums. + HashMap<StringName, int> enum_values; // For enums. _FORCE_INLINE_ bool is_set() const { return kind != UNRESOLVED; } _FORCE_INLINE_ bool has_no_type() const { return type_source == UNDETECTED; } @@ -312,7 +312,7 @@ public: bool is_constant = false; Variant reduced_value; - virtual bool is_expression() const { return true; } + virtual bool is_expression() const override { return true; } virtual ~ExpressionNode() {} protected: @@ -360,6 +360,7 @@ public: OP_MULTIPLICATION, OP_DIVISION, OP_MODULO, + OP_POWER, OP_BIT_SHIFT_LEFT, OP_BIT_SHIFT_RIGHT, OP_BIT_AND, @@ -393,6 +394,7 @@ public: OP_MULTIPLICATION, OP_DIVISION, OP_MODULO, + OP_POWER, OP_BIT_LEFT_SHIFT, OP_BIT_RIGHT_SHIFT, OP_BIT_AND, @@ -747,8 +749,10 @@ public: }; struct GetNodeNode : public ExpressionNode { - LiteralNode *string = nullptr; - Vector<IdentifierNode *> chain; + String full_path; +#ifdef DEBUG_ENABLED + bool use_dollar = true; +#endif GetNodeNode() { type = GET_NODE; @@ -767,6 +771,7 @@ public: LOCAL_BIND, // Pattern bind. MEMBER_VARIABLE, MEMBER_CONSTANT, + INHERITED_VARIABLE, }; Source source = UNDEFINED_SOURCE; @@ -799,7 +804,8 @@ public: FunctionNode *function = nullptr; FunctionNode *parent_function = nullptr; Vector<IdentifierNode *> captures; - Map<StringName, int> captures_indices; + HashMap<StringName, int> captures_indices; + bool use_self = false; bool has_name() const { return function && function->identifier; @@ -1146,7 +1152,7 @@ public: COMPLETION_ASSIGN, // Assignment based on type (e.g. enum values). COMPLETION_ATTRIBUTE, // After id.| to look for members. COMPLETION_ATTRIBUTE_METHOD, // After id.| to look for methods. - COMPLETION_BUILT_IN_TYPE_CONSTANT, // Constants inside a built-in type (e.g. Color.blue). + COMPLETION_BUILT_IN_TYPE_CONSTANT_OR_STATIC_METHOD, // Constants inside a built-in type (e.g. Color.BLUE) or static methods (e.g. Color.html). COMPLETION_CALL_ARGUMENTS, // Complete with nodes, input actions, enum values (or usual expressions). // TODO: COMPLETION_DECLARATION, // Potential declaration (var, const, func). COMPLETION_GET_NODE, // Get node with $ notation. @@ -1201,9 +1207,9 @@ private: List<ParserError> errors; #ifdef DEBUG_ENABLED List<GDScriptWarning> warnings; - Set<String> ignored_warnings; - Set<uint32_t> ignored_warning_codes; - Set<int> unsafe_lines; + HashSet<String> ignored_warnings; + HashSet<uint32_t> ignored_warning_codes; + HashSet<int> unsafe_lines; #endif GDScriptTokenizer tokenizer; @@ -1261,6 +1267,7 @@ private: PREC_FACTOR, PREC_SIGN, PREC_BIT_NOT, + PREC_POWER, PREC_TYPE_TEST, PREC_AWAIT, PREC_CALL, @@ -1414,7 +1421,7 @@ public: } #ifdef DEBUG_ENABLED const List<GDScriptWarning> &get_warnings() const { return warnings; } - const Set<int> &get_unsafe_lines() const { return unsafe_lines; } + const HashSet<int> &get_unsafe_lines() const { return unsafe_lines; } int get_last_line_number() const { return current.end_line; } #endif diff --git a/modules/gdscript/gdscript_tokenizer.cpp b/modules/gdscript/gdscript_tokenizer.cpp index d3287ab345..6c17afe939 100644 --- a/modules/gdscript/gdscript_tokenizer.cpp +++ b/modules/gdscript/gdscript_tokenizer.cpp @@ -67,6 +67,7 @@ static const char *token_names[] = { "+", // PLUS, "-", // MINUS, "*", // STAR, + "**", // STAR_STAR, "/", // SLASH, "%", // PERCENT, // Assignment @@ -74,6 +75,7 @@ static const char *token_names[] = { "+=", // PLUS_EQUAL, "-=", // MINUS_EQUAL, "*=", // STAR_EQUAL, + "**=", // STAR_STAR_EQUAL, "/=", // SLASH_EQUAL, "%=", // PERCENT_EQUAL, "<<=", // LESS_LESS_EQUAL, @@ -1403,6 +1405,14 @@ GDScriptTokenizer::Token GDScriptTokenizer::scan() { if (_peek() == '=') { _advance(); return make_token(Token::STAR_EQUAL); + } else if (_peek() == '*') { + if (_peek(1) == '=') { + _advance(); + _advance(); // Advance both '*' and '=' + return make_token(Token::STAR_STAR_EQUAL); + } + _advance(); + return make_token(Token::STAR_STAR); } else { return make_token(Token::STAR); } @@ -1493,7 +1503,7 @@ GDScriptTokenizer::Token GDScriptTokenizer::scan() { } default: - return make_error(vformat(R"(Unknown character "%s".")", String(&c, 1))); + return make_error(vformat(R"(Unknown character "%s".)", String(&c, 1))); } } diff --git a/modules/gdscript/gdscript_tokenizer.h b/modules/gdscript/gdscript_tokenizer.h index abd090e381..7fb715f2c8 100644 --- a/modules/gdscript/gdscript_tokenizer.h +++ b/modules/gdscript/gdscript_tokenizer.h @@ -31,9 +31,9 @@ #ifndef GDSCRIPT_TOKENIZER_H #define GDSCRIPT_TOKENIZER_H +#include "core/templates/hash_map.h" +#include "core/templates/hash_set.h" #include "core/templates/list.h" -#include "core/templates/map.h" -#include "core/templates/set.h" #include "core/templates/vector.h" #include "core/variant/variant.h" @@ -78,6 +78,7 @@ public: PLUS, MINUS, STAR, + STAR_STAR, SLASH, PERCENT, // Assignment @@ -85,6 +86,7 @@ public: PLUS_EQUAL, MINUS_EQUAL, STAR_EQUAL, + STAR_STAR_EQUAL, SLASH_EQUAL, PERCENT_EQUAL, LESS_LESS_EQUAL, @@ -191,7 +193,7 @@ public: new_line = p_new_line; } }; - const Map<int, CommentData> &get_comments() const { + const HashMap<int, CommentData> &get_comments() const { return comments; } #endif // TOOLS_ENABLED @@ -224,7 +226,7 @@ private: int length = 0; #ifdef TOOLS_ENABLED - Map<int, CommentData> comments; + HashMap<int, CommentData> comments; #endif // TOOLS_ENABLED _FORCE_INLINE_ bool _is_at_end() { return position >= length; } diff --git a/modules/gdscript/gdscript_utility_functions.cpp b/modules/gdscript/gdscript_utility_functions.cpp index 16b2dac343..a914374985 100644 --- a/modules/gdscript/gdscript_utility_functions.cpp +++ b/modules/gdscript/gdscript_utility_functions.cpp @@ -432,21 +432,21 @@ struct GDScriptUtilityFunctionsDefinitions { } static inline void print_debug(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { - String str; + String s; for (int i = 0; i < p_arg_count; i++) { - str += p_args[i]->operator String(); + s += p_args[i]->operator String(); } if (Thread::get_caller_id() == Thread::get_main_id()) { ScriptLanguage *script = GDScriptLanguage::get_singleton(); if (script->debug_get_stack_level_count() > 0) { - str += "\n At: " + script->debug_get_stack_level_source(0) + ":" + itos(script->debug_get_stack_level_line(0)) + ":" + script->debug_get_stack_level_function(0) + "()"; + s += "\n At: " + script->debug_get_stack_level_source(0) + ":" + itos(script->debug_get_stack_level_line(0)) + ":" + script->debug_get_stack_level_function(0) + "()"; } } else { - str += "\n At: Cannot retrieve debug info outside the main thread. Thread ID: " + itos(Thread::get_caller_id()); + s += "\n At: Cannot retrieve debug info outside the main thread. Thread ID: " + itos(Thread::get_caller_id()); } - print_line(str); + print_line(s); *r_ret = Variant(); } @@ -545,7 +545,7 @@ struct GDScriptUtilityFunctionsDefinitions { }; struct GDScriptUtilityFunctionInfo { - GDScriptUtilityFunctions::FunctionPtr function; + GDScriptUtilityFunctions::FunctionPtr function = nullptr; MethodInfo info; bool is_constant = false; }; diff --git a/modules/gdscript/gdscript_vm.cpp b/modules/gdscript/gdscript_vm.cpp index 41c59c7703..55f4ebb1c5 100644 --- a/modules/gdscript/gdscript_vm.cpp +++ b/modules/gdscript/gdscript_vm.cpp @@ -93,9 +93,13 @@ static String _get_var_type(const Variant *p_var) { basestr = "null instance"; } } else { - basestr = bobj->get_class(); - if (bobj->get_script_instance()) { - basestr += " (" + _get_script_name(bobj->get_script_instance()->get_script()) + ")"; + if (bobj->is_class_ptr(GDScriptNativeClass::get_class_ptr_static())) { + basestr = Object::cast_to<GDScriptNativeClass>(bobj)->get_name(); + } else { + basestr = bobj->get_class(); + if (bobj->get_script_instance()) { + basestr += " (" + _get_script_name(bobj->get_script_instance()->get_script()) + ")"; + } } } @@ -263,6 +267,7 @@ void (*type_init_function_table[])(Variant *) = { &&OPCODE_CALL_METHOD_BIND, \ &&OPCODE_CALL_METHOD_BIND_RET, \ &&OPCODE_CALL_BUILTIN_STATIC, \ + &&OPCODE_CALL_NATIVE_STATIC, \ &&OPCODE_CALL_PTRCALL_NO_RETURN, \ &&OPCODE_CALL_PTRCALL_BOOL, \ &&OPCODE_CALL_PTRCALL_INT, \ @@ -301,6 +306,7 @@ void (*type_init_function_table[])(Variant *) = { &&OPCODE_AWAIT, \ &&OPCODE_AWAIT_RESUME, \ &&OPCODE_CREATE_LAMBDA, \ + &&OPCODE_CREATE_SELF_LAMBDA, \ &&OPCODE_JUMP, \ &&OPCODE_JUMP_IF, \ &&OPCODE_JUMP_IF_NOT, \ @@ -367,7 +373,7 @@ void (*type_init_function_table[])(Variant *) = { &&OPCODE_TYPE_ADJUST_QUATERNION, \ &&OPCODE_TYPE_ADJUST_AABB, \ &&OPCODE_TYPE_ADJUST_BASIS, \ - &&OPCODE_TYPE_ADJUST_TRANSFORM, \ + &&OPCODE_TYPE_ADJUST_TRANSFORM3D, \ &&OPCODE_TYPE_ADJUST_COLOR, \ &&OPCODE_TYPE_ADJUST_STRING_NAME, \ &&OPCODE_TYPE_ADJUST_NODE_PATH, \ @@ -540,33 +546,32 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a memnew_placement(&stack[i], Variant); } - memnew_placement(&stack[ADDR_STACK_NIL], Variant); - if (_instruction_args_size) { instruction_args = (Variant **)&aptr[sizeof(Variant) * _stack_size]; } else { instruction_args = nullptr; } - if (p_instance) { - memnew_placement(&stack[ADDR_STACK_SELF], Variant(p_instance->owner)); - script = p_instance->script.ptr(); - } else { - memnew_placement(&stack[ADDR_STACK_SELF], Variant); - script = _script; + for (const KeyValue<int, Variant::Type> &E : temporary_slots) { + type_init_function_table[E.value](&stack[E.key]); } } + if (_ptrcall_args_size) { call_args_ptr = (const void **)alloca(_ptrcall_args_size * sizeof(void *)); } else { call_args_ptr = nullptr; } - memnew_placement(&stack[ADDR_STACK_CLASS], Variant(script)); - - for (const KeyValue<int, Variant::Type> &E : temporary_slots) { - type_init_function_table[E.value](&stack[E.key]); + if (p_instance) { + memnew_placement(&stack[ADDR_STACK_SELF], Variant(p_instance->owner)); + script = p_instance->script.ptr(); + } else { + memnew_placement(&stack[ADDR_STACK_SELF], Variant); + script = _script; } + memnew_placement(&stack[ADDR_STACK_CLASS], Variant(script)); + memnew_placement(&stack[ADDR_STACK_NIL], Variant); String err_text; @@ -1710,6 +1715,47 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a } DISPATCH_OPCODE; + OPCODE(OPCODE_CALL_NATIVE_STATIC) { + CHECK_SPACE(3 + instr_arg_count); + + ip += instr_arg_count; + + GD_ERR_BREAK(_code_ptr[ip + 1] < 0 || _code_ptr[ip + 1] >= _methods_count); + MethodBind *method = _methods_ptr[_code_ptr[ip + 1]]; + + int argc = _code_ptr[ip + 2]; + GD_ERR_BREAK(argc < 0); + + GET_INSTRUCTION_ARG(ret, argc); + + const Variant **argptrs = const_cast<const Variant **>(instruction_args); + +#ifdef DEBUG_ENABLED + uint64_t call_time = 0; + + if (GDScriptLanguage::get_singleton()->profiling) { + call_time = OS::get_singleton()->get_ticks_usec(); + } +#endif + + Callable::CallError err; + *ret = method->call(nullptr, argptrs, argc, err); + +#ifdef DEBUG_ENABLED + if (GDScriptLanguage::get_singleton()->profiling) { + function_call_time += OS::get_singleton()->get_ticks_usec() - call_time; + } + + if (err.error != Callable::CallError::CALL_OK) { + err_text = _get_call_error(err, "static function '" + method->get_name().operator String() + "' in type '" + method->get_instance_class().operator String() + "'", argptrs); + OPCODE_BREAK; + } +#endif + + ip += 3; + } + DISPATCH_OPCODE; + #ifdef DEBUG_ENABLED #define OPCODE_CALL_PTR(m_type) \ OPCODE(OPCODE_CALL_PTRCALL_##m_type) { \ @@ -2057,7 +2103,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a const GDScript *gds = _script; - const Map<StringName, GDScriptFunction *>::Element *E = nullptr; + HashMap<StringName, GDScriptFunction *>::ConstIterator E; while (gds->base.ptr()) { gds = gds->base.ptr(); E = gds->member_functions.find(*methodname); @@ -2069,7 +2115,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a Callable::CallError err; if (E) { - *dst = E->get()->call(p_instance, (const Variant **)argptrs, argc, err); + *dst = E->value->call(p_instance, (const Variant **)argptrs, argc, err); } else if (gds->native.ptr()) { if (*methodname != GDScriptLanguage::get_singleton()->strings._init) { MethodBind *mb = ClassDB::get_method(gds->native->get_name(), *methodname); @@ -2124,8 +2170,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a // Is this even possible to be null at this point? if (obj) { if (obj->is_class_ptr(GDScriptFunctionState::get_class_ptr_static())) { - static StringName completed = _scs_create("completed"); - result = Signal(obj, completed); + result = Signal(obj, "completed"); } } } @@ -2146,8 +2191,9 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a gdfs->function = this; gdfs->state.stack.resize(alloca_size); - //copy variant stack - for (int i = 0; i < _stack_size; i++) { + + // First 3 stack addresses are special, so we just skip them here. + for (int i = 3; i < _stack_size; i++) { memnew_placement(&gdfs->state.stack.write[sizeof(Variant) * i], Variant(stack[i])); } gdfs->state.stack_size = _stack_size; @@ -2231,6 +2277,41 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a } DISPATCH_OPCODE; + OPCODE(OPCODE_CREATE_SELF_LAMBDA) { + CHECK_SPACE(2 + instr_arg_count); + + GD_ERR_BREAK(p_instance == nullptr); + + ip += instr_arg_count; + + int captures_count = _code_ptr[ip + 1]; + GD_ERR_BREAK(captures_count < 0); + + int lambda_index = _code_ptr[ip + 2]; + GD_ERR_BREAK(lambda_index < 0 || lambda_index >= _lambdas_count); + GDScriptFunction *lambda = _lambdas_ptr[lambda_index]; + + Vector<Variant> captures; + captures.resize(captures_count); + for (int i = 0; i < captures_count; i++) { + GET_INSTRUCTION_ARG(arg, i); + captures.write[i] = *arg; + } + + GDScriptLambdaSelfCallable *callable; + if (Object::cast_to<RefCounted>(p_instance->owner)) { + callable = memnew(GDScriptLambdaSelfCallable(Ref<RefCounted>(Object::cast_to<RefCounted>(p_instance->owner)), lambda, captures)); + } else { + callable = memnew(GDScriptLambdaSelfCallable(p_instance->owner, lambda, captures)); + } + + GET_INSTRUCTION_ARG(result, captures_count); + *result = Callable(callable); + + ip += 3; + } + DISPATCH_OPCODE; + OPCODE(OPCODE_JUMP) { CHECK_SPACE(2); int to = _code_ptr[ip + 1]; @@ -3217,7 +3298,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a OPCODE_TYPE_ADJUST(QUATERNION, Quaternion); OPCODE_TYPE_ADJUST(AABB, AABB); OPCODE_TYPE_ADJUST(BASIS, Basis); - OPCODE_TYPE_ADJUST(TRANSFORM, Transform3D); + OPCODE_TYPE_ADJUST(TRANSFORM3D, Transform3D); OPCODE_TYPE_ADJUST(COLOR, Color); OPCODE_TYPE_ADJUST(STRING_NAME, StringName); OPCODE_TYPE_ADJUST(NODE_PATH, NodePath); @@ -3369,26 +3450,27 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a GDScriptLanguage::get_singleton()->script_frame_time += time_taken - function_call_time; } - // Check if this is the last time the function is resuming from await - // Will be true if never awaited as well - // When it's the last resume it will postpone the exit from stack, - // so the debugger knows which function triggered the resume of the next function (if any) + // Check if this is not the last time it was interrupted by `await` or if it's the first time executing. + // If that is the case then we exit the function as normal. Otherwise we postpone it until the last `await` is completed. + // This ensures the call stack can be properly shown when using `await`, showing what resumed the function. if (!p_state || awaited) { if (EngineDebugger::is_active()) { GDScriptLanguage::get_singleton()->exit_function(); } #endif - if (_stack_size) { - //free stack - for (int i = 0; i < _stack_size; i++) { - stack[i].~Variant(); - } + // Free stack, except reserved addresses. + for (int i = 3; i < _stack_size; i++) { + stack[i].~Variant(); } - #ifdef DEBUG_ENABLED } #endif + // Always free reserved addresses, since they are never copied. + for (int i = 0; i < 3; i++) { + stack[i].~Variant(); + } + return retvalue; } diff --git a/modules/gdscript/gdscript_warning.cpp b/modules/gdscript/gdscript_warning.cpp index ad96e36640..1cae7bdfac 100644 --- a/modules/gdscript/gdscript_warning.cpp +++ b/modules/gdscript/gdscript_warning.cpp @@ -163,6 +163,18 @@ String GDScriptWarning::get_message() const { #undef CHECK_SYMBOLS } +int GDScriptWarning::get_default_value(Code p_code) { + if (get_name_from_code(p_code).to_lower().begins_with("unsafe_")) { + return WarnLevel::IGNORE; + } + return WarnLevel::WARN; +} + +PropertyInfo GDScriptWarning::get_property_info(Code p_code) { + // Making this a separate function in case a warning needs different PropertyInfo in the future. + return PropertyInfo(Variant::INT, get_settings_path_from_code(p_code), PROPERTY_HINT_ENUM, "Ignore,Warn,Error"); +} + String GDScriptWarning::get_name() const { return get_name_from_code(code); } @@ -210,6 +222,10 @@ String GDScriptWarning::get_name_from_code(Code p_code) { return names[(int)p_code]; } +String GDScriptWarning::get_settings_path_from_code(Code p_code) { + return "debug/gdscript/warnings/" + get_name_from_code(p_code).to_lower(); +} + GDScriptWarning::Code GDScriptWarning::get_code_from_name(const String &p_name) { for (int i = 0; i < WARNING_MAX; i++) { if (get_name_from_code((Code)i) == p_name) { diff --git a/modules/gdscript/gdscript_warning.h b/modules/gdscript/gdscript_warning.h index 82efe3568f..f47f31aedf 100644 --- a/modules/gdscript/gdscript_warning.h +++ b/modules/gdscript/gdscript_warning.h @@ -33,11 +33,18 @@ #ifdef DEBUG_ENABLED +#include "core/object/object.h" #include "core/string/ustring.h" #include "core/templates/vector.h" class GDScriptWarning { public: + enum WarnLevel { + IGNORE, + WARN, + ERROR + }; + enum Code { UNASSIGNED_VARIABLE, // Variable used but never assigned. UNASSIGNED_VARIABLE_OP_ASSIGN, // Variable never assigned but used in an assignment operation (+=, *=, etc). @@ -81,7 +88,10 @@ public: String get_name() const; String get_message() const; + static int get_default_value(Code p_code); + static PropertyInfo get_property_info(Code p_code); static String get_name_from_code(Code p_code); + static String get_settings_path_from_code(Code p_code); static Code get_code_from_name(const String &p_name); }; diff --git a/modules/gdscript/language_server/gdscript_extend_parser.cpp b/modules/gdscript/language_server/gdscript_extend_parser.cpp index 17886181d5..d3c5fed95a 100644 --- a/modules/gdscript/language_server/gdscript_extend_parser.cpp +++ b/modules/gdscript/language_server/gdscript_extend_parser.cpp @@ -89,16 +89,16 @@ void ExtendGDScriptParser::update_symbols() { for (int i = 0; i < class_symbol.children.size(); i++) { const lsp::DocumentSymbol &symbol = class_symbol.children[i]; - members.set(symbol.name, &symbol); + members.insert(symbol.name, &symbol); // cache level one inner classes if (symbol.kind == lsp::SymbolKind::Class) { ClassMembers inner_class; for (int j = 0; j < symbol.children.size(); j++) { const lsp::DocumentSymbol &s = symbol.children[j]; - inner_class.set(s.name, &s); + inner_class.insert(s.name, &s); } - inner_classes.set(symbol.name, inner_class); + inner_classes.insert(symbol.name, inner_class); } } } @@ -108,7 +108,7 @@ void ExtendGDScriptParser::update_document_links(const String &p_code) { document_links.clear(); GDScriptTokenizer tokenizer; - FileAccessRef fs = FileAccess::create(FileAccess::ACCESS_RESOURCES); + Ref<FileAccess> fs = FileAccess::create(FileAccess::ACCESS_RESOURCES); tokenizer.set_source_code(p_code); while (true) { GDScriptTokenizer::Token token = tokenizer.scan(); @@ -212,12 +212,12 @@ void ExtendGDScriptParser::parse_class_symbol(const GDScriptParser::ClassNode *p const Variant &default_value = m.constant->initializer->reduced_value; String value_text; if (default_value.get_type() == Variant::OBJECT) { - RES res = default_value; + Ref<Resource> res = default_value; if (res.is_valid() && !res->get_path().is_empty()) { value_text = "preload(\"" + res->get_path() + "\")"; if (symbol.documentation.is_empty()) { - if (Map<String, ExtendGDScriptParser *>::Element *S = GDScriptLanguageProtocol::get_singleton()->get_workspace()->scripts.find(res->get_path())) { - symbol.documentation = S->get()->class_symbol.documentation; + if (HashMap<String, ExtendGDScriptParser *>::Iterator S = GDScriptLanguageProtocol::get_singleton()->get_workspace()->scripts.find(res->get_path())) { + symbol.documentation = S->value->class_symbol.documentation; } } } else { @@ -661,30 +661,22 @@ const List<lsp::DocumentLink> &ExtendGDScriptParser::get_document_links() const const Array &ExtendGDScriptParser::get_member_completions() { if (member_completions.is_empty()) { - const String *name = members.next(nullptr); - while (name) { - const lsp::DocumentSymbol *symbol = members.get(*name); + for (const KeyValue<String, const lsp::DocumentSymbol *> &E : members) { + const lsp::DocumentSymbol *symbol = E.value; lsp::CompletionItem item = symbol->make_completion_item(); - item.data = JOIN_SYMBOLS(path, *name); + item.data = JOIN_SYMBOLS(path, E.key); member_completions.push_back(item.to_json()); - - name = members.next(name); } - const String *_class = inner_classes.next(nullptr); - while (_class) { - const ClassMembers *inner_class = inner_classes.getptr(*_class); - const String *member_name = inner_class->next(nullptr); - while (member_name) { - const lsp::DocumentSymbol *symbol = inner_class->get(*member_name); + for (const KeyValue<String, ClassMembers> &E : inner_classes) { + const ClassMembers *inner_class = &E.value; + + for (const KeyValue<String, const lsp::DocumentSymbol *> &F : *inner_class) { + const lsp::DocumentSymbol *symbol = F.value; lsp::CompletionItem item = symbol->make_completion_item(); - item.data = JOIN_SYMBOLS(path, JOIN_SYMBOLS(*_class, *member_name)); + item.data = JOIN_SYMBOLS(path, JOIN_SYMBOLS(E.key, F.key)); member_completions.push_back(item.to_json()); - - member_name = inner_class->next(member_name); } - - _class = inner_classes.next(_class); } } diff --git a/modules/gdscript/language_server/gdscript_language_protocol.cpp b/modules/gdscript/language_server/gdscript_language_protocol.cpp index cdddab319d..7460f8edff 100644 --- a/modules/gdscript/language_server/gdscript_language_protocol.cpp +++ b/modules/gdscript/language_server/gdscript_language_protocol.cpp @@ -126,7 +126,7 @@ Error GDScriptLanguageProtocol::on_client_connected() { ERR_FAIL_COND_V_MSG(clients.size() >= LSP_MAX_CLIENTS, FAILED, "Max client limits reached"); Ref<LSPeer> peer = memnew(LSPeer); peer->connection = tcp_peer; - clients.set(next_client_id, peer); + clients.insert(next_client_id, peer); next_client_id++; EditorNode::get_log()->add_message("[LSP] Connection Taken", EditorLog::MSG_TYPE_EDITOR); return OK; @@ -229,28 +229,33 @@ void GDScriptLanguageProtocol::poll() { if (server->is_connection_available()) { on_client_connected(); } - const int *id = nullptr; - while ((id = clients.next(id))) { - Ref<LSPeer> peer = clients.get(*id); + + HashMap<int, Ref<LSPeer>>::Iterator E = clients.begin(); + while (E != clients.end()) { + Ref<LSPeer> peer = E->value; StreamPeerTCP::Status status = peer->connection->get_status(); if (status == StreamPeerTCP::STATUS_NONE || status == StreamPeerTCP::STATUS_ERROR) { - on_client_disconnected(*id); - id = nullptr; + on_client_disconnected(E->key); + E = clients.begin(); + continue; } else { if (peer->connection->get_available_bytes() > 0) { - latest_client_id = *id; + latest_client_id = E->key; Error err = peer->handle_data(); if (err != OK && err != ERR_BUSY) { - on_client_disconnected(*id); - id = nullptr; + on_client_disconnected(E->key); + E = clients.begin(); + continue; } } Error err = peer->send_data(); if (err != OK && err != ERR_BUSY) { - on_client_disconnected(*id); - id = nullptr; + on_client_disconnected(E->key); + E = clients.begin(); + continue; } } + ++E; } } @@ -259,9 +264,8 @@ Error GDScriptLanguageProtocol::start(int p_port, const IPAddress &p_bind_ip) { } void GDScriptLanguageProtocol::stop() { - const int *id = nullptr; - while ((id = clients.next(id))) { - Ref<LSPeer> peer = clients.get(*id); + for (const KeyValue<int, Ref<LSPeer>> &E : clients) { + Ref<LSPeer> peer = clients.get(E.key); peer->connection->disconnect_from_host(); } diff --git a/modules/gdscript/language_server/gdscript_text_document.cpp b/modules/gdscript/language_server/gdscript_text_document.cpp index 344ef4885b..5ad9680ea0 100644 --- a/modules/gdscript/language_server/gdscript_text_document.cpp +++ b/modules/gdscript/language_server/gdscript_text_document.cpp @@ -109,23 +109,15 @@ void GDScriptTextDocument::notify_client_show_symbol(const lsp::DocumentSymbol * void GDScriptTextDocument::initialize() { if (GDScriptLanguageProtocol::get_singleton()->is_smart_resolve_enabled()) { - const HashMap<StringName, ClassMembers> &native_members = GDScriptLanguageProtocol::get_singleton()->get_workspace()->native_members; + for (const KeyValue<StringName, ClassMembers> &E : GDScriptLanguageProtocol::get_singleton()->get_workspace()->native_members) { + const ClassMembers &members = E.value; - const StringName *class_ptr = native_members.next(nullptr); - while (class_ptr) { - const ClassMembers &members = native_members.get(*class_ptr); - - const String *name = members.next(nullptr); - while (name) { - const lsp::DocumentSymbol *symbol = members.get(*name); + for (const KeyValue<String, const lsp::DocumentSymbol *> &F : members) { + const lsp::DocumentSymbol *symbol = members.get(F.key); lsp::CompletionItem item = symbol->make_completion_item(); - item.data = JOIN_SYMBOLS(String(*class_ptr), *name); + item.data = JOIN_SYMBOLS(String(E.key), F.key); native_member_completions.push_back(item.to_json()); - - name = members.next(name); } - - class_ptr = native_members.next(class_ptr); } } } @@ -149,9 +141,9 @@ Array GDScriptTextDocument::documentSymbol(const Dictionary &p_params) { String uri = params["uri"]; String path = GDScriptLanguageProtocol::get_singleton()->get_workspace()->get_file_path(uri); Array arr; - if (const Map<String, ExtendGDScriptParser *>::Element *parser = GDScriptLanguageProtocol::get_singleton()->get_workspace()->scripts.find(path)) { + if (HashMap<String, ExtendGDScriptParser *>::ConstIterator parser = GDScriptLanguageProtocol::get_singleton()->get_workspace()->scripts.find(path)) { Vector<lsp::DocumentedSymbolInformation> list; - parser->get()->get_symbols().symbol_tree_as_list(uri, list); + parser->value->get_symbols().symbol_tree_as_list(uri, list); for (int i = 0; i < list.size(); i++) { arr.push_back(list[i].to_json()); } @@ -166,50 +158,52 @@ Array GDScriptTextDocument::completion(const Dictionary &p_params) { params.load(p_params); Dictionary request_data = params.to_json(); - List<ScriptCodeCompletionOption> options; + List<ScriptLanguage::CodeCompletionOption> options; GDScriptLanguageProtocol::get_singleton()->get_workspace()->completion(params, &options); if (!options.is_empty()) { int i = 0; arr.resize(options.size()); - for (const ScriptCodeCompletionOption &option : options) { + for (const ScriptLanguage::CodeCompletionOption &option : options) { lsp::CompletionItem item; item.label = option.display; item.data = request_data; item.insertText = option.insert_text; switch (option.kind) { - case ScriptCodeCompletionOption::KIND_ENUM: + case ScriptLanguage::CODE_COMPLETION_KIND_ENUM: item.kind = lsp::CompletionItemKind::Enum; break; - case ScriptCodeCompletionOption::KIND_CLASS: + case ScriptLanguage::CODE_COMPLETION_KIND_CLASS: item.kind = lsp::CompletionItemKind::Class; break; - case ScriptCodeCompletionOption::KIND_MEMBER: + case ScriptLanguage::CODE_COMPLETION_KIND_MEMBER: item.kind = lsp::CompletionItemKind::Property; break; - case ScriptCodeCompletionOption::KIND_FUNCTION: + case ScriptLanguage::CODE_COMPLETION_KIND_FUNCTION: item.kind = lsp::CompletionItemKind::Method; break; - case ScriptCodeCompletionOption::KIND_SIGNAL: + case ScriptLanguage::CODE_COMPLETION_KIND_SIGNAL: item.kind = lsp::CompletionItemKind::Event; break; - case ScriptCodeCompletionOption::KIND_CONSTANT: + case ScriptLanguage::CODE_COMPLETION_KIND_CONSTANT: item.kind = lsp::CompletionItemKind::Constant; break; - case ScriptCodeCompletionOption::KIND_VARIABLE: + case ScriptLanguage::CODE_COMPLETION_KIND_VARIABLE: item.kind = lsp::CompletionItemKind::Variable; break; - case ScriptCodeCompletionOption::KIND_FILE_PATH: + case ScriptLanguage::CODE_COMPLETION_KIND_FILE_PATH: item.kind = lsp::CompletionItemKind::File; break; - case ScriptCodeCompletionOption::KIND_NODE_PATH: + case ScriptLanguage::CODE_COMPLETION_KIND_NODE_PATH: item.kind = lsp::CompletionItemKind::Snippet; break; - case ScriptCodeCompletionOption::KIND_PLAIN_TEXT: + case ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT: item.kind = lsp::CompletionItemKind::Text; break; + default: { + } } arr[i] = item.to_json(); @@ -274,8 +268,8 @@ Dictionary GDScriptTextDocument::resolve(const Dictionary &p_params) { } if (!symbol) { - if (const Map<String, ExtendGDScriptParser *>::Element *E = GDScriptLanguageProtocol::get_singleton()->get_workspace()->scripts.find(class_name)) { - symbol = E->get()->get_member_symbol(member_name, inner_class_name); + if (HashMap<String, ExtendGDScriptParser *>::ConstIterator E = GDScriptLanguageProtocol::get_singleton()->get_workspace()->scripts.find(class_name)) { + symbol = E->value->get_member_symbol(member_name, inner_class_name); } } } @@ -418,10 +412,6 @@ GDScriptTextDocument::GDScriptTextDocument() { file_checker = FileAccess::create(FileAccess::ACCESS_RESOURCES); } -GDScriptTextDocument::~GDScriptTextDocument() { - memdelete(file_checker); -} - void GDScriptTextDocument::sync_script_content(const String &p_path, const String &p_content) { String path = GDScriptLanguageProtocol::get_singleton()->get_workspace()->get_file_path(p_path); GDScriptLanguageProtocol::get_singleton()->get_workspace()->parse_script(path, p_content); diff --git a/modules/gdscript/language_server/gdscript_text_document.h b/modules/gdscript/language_server/gdscript_text_document.h index eb7b2c0240..9732765f34 100644 --- a/modules/gdscript/language_server/gdscript_text_document.h +++ b/modules/gdscript/language_server/gdscript_text_document.h @@ -40,7 +40,7 @@ class GDScriptTextDocument : public RefCounted { protected: static void _bind_methods(); - FileAccess *file_checker; + Ref<FileAccess> file_checker; void didOpen(const Variant &p_param); void didClose(const Variant &p_param); @@ -75,7 +75,6 @@ public: void initialize(); GDScriptTextDocument(); - virtual ~GDScriptTextDocument(); }; #endif diff --git a/modules/gdscript/language_server/gdscript_workspace.cpp b/modules/gdscript/language_server/gdscript_workspace.cpp index d20b243616..8d484a43b3 100644 --- a/modules/gdscript/language_server/gdscript_workspace.cpp +++ b/modules/gdscript/language_server/gdscript_workspace.cpp @@ -116,22 +116,22 @@ void GDScriptWorkspace::did_delete_files(const Dictionary &p_params) { } void GDScriptWorkspace::remove_cache_parser(const String &p_path) { - Map<String, ExtendGDScriptParser *>::Element *parser = parse_results.find(p_path); - Map<String, ExtendGDScriptParser *>::Element *script = scripts.find(p_path); + HashMap<String, ExtendGDScriptParser *>::Iterator parser = parse_results.find(p_path); + HashMap<String, ExtendGDScriptParser *>::Iterator script = scripts.find(p_path); if (parser && script) { - if (script->get() && script->get() == parser->get()) { - memdelete(script->get()); + if (script->value && script->value == parser->value) { + memdelete(script->value); } else { - memdelete(script->get()); - memdelete(parser->get()); + memdelete(script->value); + memdelete(parser->value); } parse_results.erase(p_path); scripts.erase(p_path); } else if (parser) { - memdelete(parser->get()); + memdelete(parser->value); parse_results.erase(p_path); } else if (script) { - memdelete(script->get()); + memdelete(script->value); scripts.erase(p_path); } } @@ -141,8 +141,8 @@ const lsp::DocumentSymbol *GDScriptWorkspace::get_native_symbol(const String &p_ StringName empty; while (class_name != empty) { - if (const Map<StringName, lsp::DocumentSymbol>::Element *E = native_symbols.find(class_name)) { - const lsp::DocumentSymbol &class_symbol = E->value(); + if (HashMap<StringName, lsp::DocumentSymbol>::ConstIterator E = native_symbols.find(class_name)) { + const lsp::DocumentSymbol &class_symbol = E->value; if (p_member.is_empty()) { return &class_symbol; @@ -162,9 +162,9 @@ const lsp::DocumentSymbol *GDScriptWorkspace::get_native_symbol(const String &p_ } const lsp::DocumentSymbol *GDScriptWorkspace::get_script_symbol(const String &p_path) const { - const Map<String, ExtendGDScriptParser *>::Element *S = scripts.find(p_path); + HashMap<String, ExtendGDScriptParser *>::ConstIterator S = scripts.find(p_path); if (S) { - return &(S->get()->get_symbols()); + return &(S->value->get_symbols()); } return nullptr; } @@ -209,10 +209,10 @@ void GDScriptWorkspace::reload_all_workspace_scripts() { err = parse_script(path, content); if (err != OK) { - Map<String, ExtendGDScriptParser *>::Element *S = parse_results.find(path); + HashMap<String, ExtendGDScriptParser *>::Iterator S = parse_results.find(path); String err_msg = "Failed parse script " + path; if (S) { - err_msg += "\n" + S->get()->get_errors()[0].message; + err_msg += "\n" + S->value->get_errors()[0].message; } ERR_CONTINUE_MSG(err != OK, err_msg); } @@ -221,7 +221,7 @@ void GDScriptWorkspace::reload_all_workspace_scripts() { void GDScriptWorkspace::list_script_files(const String &p_root_dir, List<String> &r_files) { Error err; - DirAccessRef dir = DirAccess::open(p_root_dir, &err); + Ref<DirAccess> dir = DirAccess::open(p_root_dir, &err); if (OK == err) { dir->list_dir_begin(); String file_name = dir->get_next(); @@ -238,25 +238,25 @@ void GDScriptWorkspace::list_script_files(const String &p_root_dir, List<String> } ExtendGDScriptParser *GDScriptWorkspace::get_parse_successed_script(const String &p_path) { - const Map<String, ExtendGDScriptParser *>::Element *S = scripts.find(p_path); + HashMap<String, ExtendGDScriptParser *>::Iterator S = scripts.find(p_path); if (!S) { parse_local_script(p_path); S = scripts.find(p_path); } if (S) { - return S->get(); + return S->value; } return nullptr; } ExtendGDScriptParser *GDScriptWorkspace::get_parse_result(const String &p_path) { - const Map<String, ExtendGDScriptParser *>::Element *S = parse_results.find(p_path); + HashMap<String, ExtendGDScriptParser *>::Iterator S = parse_results.find(p_path); if (!S) { parse_local_script(p_path); S = parse_results.find(p_path); } if (S) { - return S->get(); + return S->value; } return nullptr; } @@ -404,9 +404,9 @@ Error GDScriptWorkspace::initialize() { const lsp::DocumentSymbol &class_symbol = E.value; for (int i = 0; i < class_symbol.children.size(); i++) { const lsp::DocumentSymbol &symbol = class_symbol.children[i]; - members.set(symbol.name, &symbol); + members.insert(symbol.name, &symbol); } - native_members.set(E.key, members); + native_members.insert(E.key, members); } // cache member completions @@ -424,8 +424,8 @@ Error GDScriptWorkspace::initialize() { Error GDScriptWorkspace::parse_script(const String &p_path, const String &p_content) { ExtendGDScriptParser *parser = memnew(ExtendGDScriptParser); Error err = parser->parse(p_content, p_path); - Map<String, ExtendGDScriptParser *>::Element *last_parser = parse_results.find(p_path); - Map<String, ExtendGDScriptParser *>::Element *last_script = scripts.find(p_path); + HashMap<String, ExtendGDScriptParser *>::Iterator last_parser = parse_results.find(p_path); + HashMap<String, ExtendGDScriptParser *>::Iterator last_script = scripts.find(p_path); if (err == OK) { remove_cache_parser(p_path); @@ -433,8 +433,8 @@ Error GDScriptWorkspace::parse_script(const String &p_path, const String &p_cont scripts[p_path] = parser; } else { - if (last_parser && last_script && last_parser->get() != last_script->get()) { - memdelete(last_parser->get()); + if (last_parser && last_script && last_parser->value != last_script->value) { + memdelete(last_parser->value); } parse_results[p_path] = parser; } @@ -513,9 +513,9 @@ String GDScriptWorkspace::get_file_uri(const String &p_path) const { void GDScriptWorkspace::publish_diagnostics(const String &p_path) { Dictionary params; Array errors; - const Map<String, ExtendGDScriptParser *>::Element *ele = parse_results.find(p_path); + HashMap<String, ExtendGDScriptParser *>::ConstIterator ele = parse_results.find(p_path); if (ele) { - const Vector<lsp::Diagnostic> &list = ele->get()->get_diagnostics(); + const Vector<lsp::Diagnostic> &list = ele->value->get_diagnostics(); errors.resize(list.size()); for (int i = 0; i < list.size(); ++i) { errors[i] = list[i].to_json(); @@ -560,7 +560,7 @@ Node *GDScriptWorkspace::_get_owner_scene_node(String p_path) { for (int i = 0; i < owners.size(); i++) { NodePath owner_path = owners[i]; - RES owner_res = ResourceLoader::load(owner_path); + Ref<Resource> owner_res = ResourceLoader::load(owner_path); if (Object::cast_to<PackedScene>(owner_res.ptr())) { Ref<PackedScene> owner_packed_scene = Ref<PackedScene>(Object::cast_to<PackedScene>(*owner_res)); owner_scene_node = owner_packed_scene->instantiate(); @@ -571,7 +571,7 @@ Node *GDScriptWorkspace::_get_owner_scene_node(String p_path) { return owner_scene_node; } -void GDScriptWorkspace::completion(const lsp::CompletionParams &p_params, List<ScriptCodeCompletionOption> *r_options) { +void GDScriptWorkspace::completion(const lsp::CompletionParams &p_params, List<ScriptLanguage::CodeCompletionOption> *r_options) { String path = get_file_path(p_params.textDocument.uri); String call_hint; bool forced = false; @@ -638,7 +638,7 @@ const lsp::DocumentSymbol *GDScriptWorkspace::resolve_symbol(const lsp::TextDocu symbol_identifier = "_init"; } if (OK == GDScriptLanguage::get_singleton()->lookup_code(parser->get_text_for_lookup_symbol(pos, symbol_identifier, p_func_required), symbol_identifier, path, nullptr, ret)) { - if (ret.type == ScriptLanguage::LookupResult::RESULT_SCRIPT_LOCATION) { + if (ret.type == ScriptLanguage::LOOKUP_RESULT_SCRIPT_LOCATION) { String target_script_path = path; if (!ret.script.is_null()) { target_script_path = ret.script->get_path(); @@ -682,13 +682,11 @@ void GDScriptWorkspace::resolve_related_symbols(const lsp::TextDocumentPositionP Vector2i offset; symbol_identifier = parser->get_identifier_under_position(p_doc_pos.position, offset); - const StringName *class_ptr = native_members.next(nullptr); - while (class_ptr) { - const ClassMembers &members = native_members.get(*class_ptr); + for (const KeyValue<StringName, ClassMembers> &E : native_members) { + const ClassMembers &members = native_members.get(E.key); if (const lsp::DocumentSymbol *const *symbol = members.getptr(symbol_identifier)) { r_list.push_back(*symbol); } - class_ptr = native_members.next(class_ptr); } for (const KeyValue<String, ExtendGDScriptParser *> &E : scripts) { @@ -698,23 +696,19 @@ void GDScriptWorkspace::resolve_related_symbols(const lsp::TextDocumentPositionP r_list.push_back(*symbol); } - const HashMap<String, ClassMembers> &inner_classes = script->get_inner_classes(); - const String *_class = inner_classes.next(nullptr); - while (_class) { - const ClassMembers *inner_class = inner_classes.getptr(*_class); + for (const KeyValue<String, ClassMembers> &F : script->get_inner_classes()) { + const ClassMembers *inner_class = &F.value; if (const lsp::DocumentSymbol *const *symbol = inner_class->getptr(symbol_identifier)) { r_list.push_back(*symbol); } - - _class = inner_classes.next(_class); } } } } const lsp::DocumentSymbol *GDScriptWorkspace::resolve_native_symbol(const lsp::NativeSymbolInspectParams &p_params) { - if (Map<StringName, lsp::DocumentSymbol>::Element *E = native_symbols.find(p_params.native_class)) { - const lsp::DocumentSymbol &symbol = E->get(); + if (HashMap<StringName, lsp::DocumentSymbol>::Iterator E = native_symbols.find(p_params.native_class)) { + const lsp::DocumentSymbol &symbol = E->value; if (p_params.symbol_name.is_empty() || p_params.symbol_name == symbol.name) { return &symbol; } @@ -790,7 +784,7 @@ GDScriptWorkspace::GDScriptWorkspace() { } GDScriptWorkspace::~GDScriptWorkspace() { - Set<String> cached_parsers; + HashSet<String> cached_parsers; for (const KeyValue<String, ExtendGDScriptParser *> &E : parse_results) { cached_parsers.insert(E.key); @@ -800,7 +794,7 @@ GDScriptWorkspace::~GDScriptWorkspace() { cached_parsers.insert(E.key); } - for (Set<String>::Element *E = cached_parsers.front(); E; E = E->next()) { - remove_cache_parser(E->get()); + for (const String &E : cached_parsers) { + remove_cache_parser(E); } } diff --git a/modules/gdscript/language_server/gdscript_workspace.h b/modules/gdscript/language_server/gdscript_workspace.h index ce5bba5f00..7bff5db81f 100644 --- a/modules/gdscript/language_server/gdscript_workspace.h +++ b/modules/gdscript/language_server/gdscript_workspace.h @@ -48,7 +48,7 @@ protected: static void _bind_methods(); void remove_cache_parser(const String &p_path); bool initialized = false; - Map<StringName, lsp::DocumentSymbol> native_symbols; + HashMap<StringName, lsp::DocumentSymbol> native_symbols; const lsp::DocumentSymbol *get_native_symbol(const String &p_class, const String &p_member = "") const; const lsp::DocumentSymbol *get_script_symbol(const String &p_path) const; @@ -68,8 +68,8 @@ public: String root; String root_uri; - Map<String, ExtendGDScriptParser *> scripts; - Map<String, ExtendGDScriptParser *> parse_results; + HashMap<String, ExtendGDScriptParser *> scripts; + HashMap<String, ExtendGDScriptParser *> parse_results; HashMap<StringName, ClassMembers> native_members; public: @@ -85,7 +85,7 @@ public: String get_file_uri(const String &p_path) const; void publish_diagnostics(const String &p_path); - void completion(const lsp::CompletionParams &p_params, List<ScriptCodeCompletionOption> *r_options); + void completion(const lsp::CompletionParams &p_params, List<ScriptLanguage::CodeCompletionOption> *r_options); const lsp::DocumentSymbol *resolve_symbol(const lsp::TextDocumentPositionParams &p_doc_pos, const String &p_symbol_name = "", bool p_func_required = false); void resolve_related_symbols(const lsp::TextDocumentPositionParams &p_doc_pos, List<const lsp::DocumentSymbol *> &r_list); diff --git a/modules/gdscript/language_server/lsp.hpp b/modules/gdscript/language_server/lsp.hpp index ae6f2abb12..1c9349097f 100644 --- a/modules/gdscript/language_server/lsp.hpp +++ b/modules/gdscript/language_server/lsp.hpp @@ -261,7 +261,7 @@ struct WorkspaceEdit { /** * Holds changes to existing resources. */ - Map<String, Vector<TextEdit>> changes; + HashMap<String, Vector<TextEdit>> changes; _FORCE_INLINE_ void add_edit(const String &uri, const TextEdit &edit) { if (changes.has(uri)) { @@ -293,8 +293,8 @@ struct WorkspaceEdit { } _FORCE_INLINE_ void add_change(const String &uri, const int &line, const int &start_character, const int &end_character, const String &new_text) { - if (Map<String, Vector<TextEdit>>::Element *E = changes.find(uri)) { - Vector<TextEdit> edit_list = E->value(); + if (HashMap<String, Vector<TextEdit>>::Iterator E = changes.find(uri)) { + Vector<TextEdit> edit_list = E->value; for (int i = 0; i < edit_list.size(); ++i) { TextEdit edit = edit_list[i]; if (edit.range.start.character == start_character) { @@ -310,8 +310,8 @@ struct WorkspaceEdit { new_edit.range.end.line = line; new_edit.range.end.character = end_character; - if (Map<String, Vector<TextEdit>>::Element *E = changes.find(uri)) { - E->value().push_back(new_edit); + if (HashMap<String, Vector<TextEdit>>::Iterator E = changes.find(uri)) { + E->value.push_back(new_edit); } else { Vector<TextEdit> edit_list; edit_list.push_back(new_edit); diff --git a/modules/gdscript/register_types.cpp b/modules/gdscript/register_types.cpp index fcf122f567..b230c6ba36 100644 --- a/modules/gdscript/register_types.cpp +++ b/modules/gdscript/register_types.cpp @@ -70,7 +70,7 @@ class EditorExportGDScript : public EditorExportPlugin { GDCLASS(EditorExportGDScript, EditorExportPlugin); public: - virtual void _export_file(const String &p_path, const String &p_type, const Set<String> &p_features) override { + virtual void _export_file(const String &p_path, const String &p_type, const HashSet<String> &p_features) override { int script_mode = EditorExportPreset::MODE_SCRIPT_COMPILED; String script_key; @@ -111,54 +111,62 @@ static void _editor_init() { #endif // TOOLS_ENABLED -void register_gdscript_types() { - GDREGISTER_CLASS(GDScript); +void initialize_gdscript_module(ModuleInitializationLevel p_level) { + if (p_level == MODULE_INITIALIZATION_LEVEL_SERVERS) { + GDREGISTER_CLASS(GDScript); - script_language_gd = memnew(GDScriptLanguage); - ScriptServer::register_language(script_language_gd); + script_language_gd = memnew(GDScriptLanguage); + ScriptServer::register_language(script_language_gd); - resource_loader_gd.instantiate(); - ResourceLoader::add_resource_format_loader(resource_loader_gd); + resource_loader_gd.instantiate(); + ResourceLoader::add_resource_format_loader(resource_loader_gd); - resource_saver_gd.instantiate(); - ResourceSaver::add_resource_format_saver(resource_saver_gd); + resource_saver_gd.instantiate(); + ResourceSaver::add_resource_format_saver(resource_saver_gd); - gdscript_cache = memnew(GDScriptCache); + gdscript_cache = memnew(GDScriptCache); + + GDScriptUtilityFunctions::register_functions(); + } #ifdef TOOLS_ENABLED - EditorNode::add_init_callback(_editor_init); + if (p_level == MODULE_INITIALIZATION_LEVEL_SERVERS) { + EditorNode::add_init_callback(_editor_init); - gdscript_translation_parser_plugin.instantiate(); - EditorTranslationParser::get_singleton()->add_parser(gdscript_translation_parser_plugin, EditorTranslationParser::STANDARD); + gdscript_translation_parser_plugin.instantiate(); + EditorTranslationParser::get_singleton()->add_parser(gdscript_translation_parser_plugin, EditorTranslationParser::STANDARD); + } #endif // TOOLS_ENABLED - - GDScriptUtilityFunctions::register_functions(); } -void unregister_gdscript_types() { - ScriptServer::unregister_language(script_language_gd); +void uninitialize_gdscript_module(ModuleInitializationLevel p_level) { + if (p_level == MODULE_INITIALIZATION_LEVEL_SERVERS) { + ScriptServer::unregister_language(script_language_gd); - if (gdscript_cache) { - memdelete(gdscript_cache); - } + if (gdscript_cache) { + memdelete(gdscript_cache); + } - if (script_language_gd) { - memdelete(script_language_gd); - } + if (script_language_gd) { + memdelete(script_language_gd); + } + + ResourceLoader::remove_resource_format_loader(resource_loader_gd); + resource_loader_gd.unref(); - ResourceLoader::remove_resource_format_loader(resource_loader_gd); - resource_loader_gd.unref(); + ResourceSaver::remove_resource_format_saver(resource_saver_gd); + resource_saver_gd.unref(); - ResourceSaver::remove_resource_format_saver(resource_saver_gd); - resource_saver_gd.unref(); + GDScriptParser::cleanup(); + GDScriptUtilityFunctions::unregister_functions(); + } #ifdef TOOLS_ENABLED - EditorTranslationParser::get_singleton()->remove_parser(gdscript_translation_parser_plugin, EditorTranslationParser::STANDARD); - gdscript_translation_parser_plugin.unref(); + if (p_level == MODULE_INITIALIZATION_LEVEL_EDITOR) { + EditorTranslationParser::get_singleton()->remove_parser(gdscript_translation_parser_plugin, EditorTranslationParser::STANDARD); + gdscript_translation_parser_plugin.unref(); + } #endif // TOOLS_ENABLED - - GDScriptParser::cleanup(); - GDScriptUtilityFunctions::unregister_functions(); } #ifdef TESTS_ENABLED diff --git a/modules/gdscript/register_types.h b/modules/gdscript/register_types.h index baa7dcbbd1..a7e6b02dcf 100644 --- a/modules/gdscript/register_types.h +++ b/modules/gdscript/register_types.h @@ -31,7 +31,9 @@ #ifndef GDSCRIPT_REGISTER_TYPES_H #define GDSCRIPT_REGISTER_TYPES_H -void register_gdscript_types(); -void unregister_gdscript_types(); +#include "modules/register_module_types.h" + +void initialize_gdscript_module(ModuleInitializationLevel p_level); +void uninitialize_gdscript_module(ModuleInitializationLevel p_level); #endif // GDSCRIPT_REGISTER_TYPES_H diff --git a/modules/gdscript/tests/gdscript_test_runner.cpp b/modules/gdscript/tests/gdscript_test_runner.cpp index e8ddf90836..de5cd10e7c 100644 --- a/modules/gdscript/tests/gdscript_test_runner.cpp +++ b/modules/gdscript/tests/gdscript_test_runner.cpp @@ -48,11 +48,11 @@ namespace GDScriptTests { void init_autoloads() { - OrderedHashMap<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list(); + HashMap<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list(); // First pass, add the constants so they exist before any script is loaded. - for (OrderedHashMap<StringName, ProjectSettings::AutoloadInfo>::Element E = autoloads.front(); E; E = E.next()) { - const ProjectSettings::AutoloadInfo &info = E.get(); + for (const KeyValue<StringName, ProjectSettings::AutoloadInfo> &E : ProjectSettings::get_singleton()->get_autoload_list()) { + const ProjectSettings::AutoloadInfo &info = E.value; if (info.is_singleton) { for (int i = 0; i < ScriptServer::get_language_count(); i++) { @@ -62,15 +62,15 @@ void init_autoloads() { } // Second pass, load into global constants. - for (OrderedHashMap<StringName, ProjectSettings::AutoloadInfo>::Element E = autoloads.front(); E; E = E.next()) { - const ProjectSettings::AutoloadInfo &info = E.get(); + for (const KeyValue<StringName, ProjectSettings::AutoloadInfo> &E : ProjectSettings::get_singleton()->get_autoload_list()) { + const ProjectSettings::AutoloadInfo &info = E.value; if (!info.is_singleton) { // Skip non-singletons since we don't have a scene tree here anyway. continue; } - RES res = ResourceLoader::load(info.path); + Ref<Resource> res = ResourceLoader::load(info.path); ERR_CONTINUE_MSG(res.is_null(), "Can't autoload: " + info.path); Node *n = nullptr; Ref<PackedScene> scn = res; @@ -80,7 +80,7 @@ void init_autoloads() { } else if (script.is_valid()) { StringName ibt = script->get_instance_base_type(); bool valid_type = ClassDB::is_parent_class(ibt, "Node"); - ERR_CONTINUE_MSG(!valid_type, "Script does not inherit a Node: " + info.path); + ERR_CONTINUE_MSG(!valid_type, "Script does not inherit from Node: " + info.path); Object *obj = ClassDB::instantiate(ibt); @@ -229,7 +229,7 @@ bool GDScriptTestRunner::generate_outputs() { bool GDScriptTestRunner::make_tests_for_dir(const String &p_dir) { Error err = OK; - DirAccessRef dir(DirAccess::open(p_dir, &err)); + Ref<DirAccess> dir(DirAccess::open(p_dir, &err)); if (err != OK) { return false; @@ -254,7 +254,7 @@ bool GDScriptTestRunner::make_tests_for_dir(const String &p_dir) { #ifndef DEBUG_ENABLED // On release builds, skip tests marked as debug only. Error open_err = OK; - FileAccessRef script_file(FileAccess::open(current_dir.plus_file(next), FileAccess::READ, &open_err)); + Ref<FileAccess> script_file(FileAccess::open(current_dir.plus_file(next), FileAccess::READ, &open_err)); if (open_err != OK) { ERR_PRINT(vformat(R"(Couldn't open test file "%s".)", next)); next = dir->get_next(); @@ -286,7 +286,7 @@ bool GDScriptTestRunner::make_tests_for_dir(const String &p_dir) { bool GDScriptTestRunner::make_tests() { Error err = OK; - DirAccessRef dir(DirAccess::open(source_dir, &err)); + Ref<DirAccess> dir(DirAccess::open(source_dir, &err)); ERR_FAIL_COND_V_MSG(err != OK, false, "Could not open specified test directory."); @@ -543,8 +543,8 @@ GDScriptTest::TestResult GDScriptTest::execute_test_code(bool p_is_generating) { return result; } // Test running. - const Map<StringName, GDScriptFunction *>::Element *test_function_element = script->get_member_functions().find(GDScriptTestRunner::test_function_name); - if (test_function_element == nullptr) { + const HashMap<StringName, GDScriptFunction *>::ConstIterator test_function_element = script->get_member_functions().find(GDScriptTestRunner::test_function_name); + if (!test_function_element) { enable_stdout(); result.status = GDTEST_LOAD_ERROR; result.output = ""; @@ -611,7 +611,7 @@ bool GDScriptTest::generate_output() { } Error err = OK; - FileAccessRef out_file = FileAccess::open(output_file, FileAccess::WRITE, &err); + Ref<FileAccess> out_file = FileAccess::open(output_file, FileAccess::WRITE, &err); if (err != OK) { return false; } @@ -620,7 +620,6 @@ bool GDScriptTest::generate_output() { output += "\n"; // Make sure to insert newline for CI static checks. out_file->store_string(output); - out_file->close(); return true; } diff --git a/modules/gdscript/tests/gdscript_test_runner.h b/modules/gdscript/tests/gdscript_test_runner.h index 1a950c6898..d6c6419e21 100644 --- a/modules/gdscript/tests/gdscript_test_runner.h +++ b/modules/gdscript/tests/gdscript_test_runner.h @@ -63,8 +63,8 @@ public: private: struct ErrorHandlerData { - TestResult *result; - GDScriptTest *self; + TestResult *result = nullptr; + GDScriptTest *self = nullptr; ErrorHandlerData(TestResult *p_result, GDScriptTest *p_this) { result = p_result; self = p_this; diff --git a/modules/gdscript/tests/scripts/analyzer/errors/invalid_array_index.out b/modules/gdscript/tests/scripts/analyzer/errors/invalid_array_index.out index 015ad756f8..6f7f0783f0 100644 --- a/modules/gdscript/tests/scripts/analyzer/errors/invalid_array_index.out +++ b/modules/gdscript/tests/scripts/analyzer/errors/invalid_array_index.out @@ -1,2 +1,2 @@ GDTEST_ANALYZER_ERROR -Invalid index type "bool" for a base of type "Array". +Cannot get index "true" from "[0, 1]". diff --git a/modules/gdscript/tests/scripts/analyzer/features/await_with_signals_no_warning.gd b/modules/gdscript/tests/scripts/analyzer/features/await_with_signals_no_warning.gd new file mode 100644 index 0000000000..9a7c6a8250 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/await_with_signals_no_warning.gd @@ -0,0 +1,12 @@ +# https://github.com/godotengine/godot/issues/54589 +# https://github.com/godotengine/godot/issues/56265 + +extends Resource + +func test(): + print("okay") + await self.changed + await unknown(self) + +func unknown(arg): + await arg.changed diff --git a/modules/gdscript/tests/scripts/analyzer/features/await_with_signals_no_warning.out b/modules/gdscript/tests/scripts/analyzer/features/await_with_signals_no_warning.out new file mode 100644 index 0000000000..2dc04a363e --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/await_with_signals_no_warning.out @@ -0,0 +1,2 @@ +GDTEST_OK +okay diff --git a/modules/gdscript/tests/scripts/analyzer/features/function_match_parent_signature_with_default_dict_void.gd b/modules/gdscript/tests/scripts/analyzer/features/function_match_parent_signature_with_default_dict_void.gd new file mode 100644 index 0000000000..631e7be5ce --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/function_match_parent_signature_with_default_dict_void.gd @@ -0,0 +1,14 @@ +func test(): + var instance := Parent.new() + instance.my_function({"a": 1}) + instance = Child.new() + instance.my_function({"a": 1}) + print("No failure") + +class Parent: + func my_function(_par1: Dictionary = {}) -> void: + pass + +class Child extends Parent: + func my_function(_par1: Dictionary = {}) -> void: + pass diff --git a/modules/gdscript/tests/scripts/analyzer/features/function_match_parent_signature_with_default_dict_void.out b/modules/gdscript/tests/scripts/analyzer/features/function_match_parent_signature_with_default_dict_void.out new file mode 100644 index 0000000000..67f0045867 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/function_match_parent_signature_with_default_dict_void.out @@ -0,0 +1,2 @@ +GDTEST_OK +No failure diff --git a/modules/gdscript/tests/scripts/analyzer/typed_array_assignment.gd b/modules/gdscript/tests/scripts/analyzer/typed_array_assignment.gd new file mode 100644 index 0000000000..9f86d0531c --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/typed_array_assignment.gd @@ -0,0 +1,2 @@ +func test(): + const arr: Array[int] = ["Hello", "World"] diff --git a/modules/gdscript/tests/scripts/analyzer/typed_array_assignment.out b/modules/gdscript/tests/scripts/analyzer/typed_array_assignment.out new file mode 100644 index 0000000000..26b6e13d4f --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/typed_array_assignment.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Assigned value for constant "arr" has type Array[String] which is not compatible with defined type Array[int]. diff --git a/modules/gdscript/tests/scripts/parser/errors/dollar-assignment-bug-53696.out b/modules/gdscript/tests/scripts/parser/errors/dollar-assignment-bug-53696.out index b3dc181a22..9fafcb5a64 100644 --- a/modules/gdscript/tests/scripts/parser/errors/dollar-assignment-bug-53696.out +++ b/modules/gdscript/tests/scripts/parser/errors/dollar-assignment-bug-53696.out @@ -1,2 +1,2 @@ GDTEST_PARSER_ERROR -Expect node path as string or identifier after "$". +Expected node path as string or identifier after "$". diff --git a/modules/gdscript/tests/scripts/parser/errors/lambda_standalone.gd b/modules/gdscript/tests/scripts/parser/errors/lambda_standalone.gd new file mode 100644 index 0000000000..fa0a43094e --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/errors/lambda_standalone.gd @@ -0,0 +1,3 @@ +func test(): + func standalone(): + print("can't be accessed") diff --git a/modules/gdscript/tests/scripts/parser/errors/lambda_standalone.out b/modules/gdscript/tests/scripts/parser/errors/lambda_standalone.out new file mode 100644 index 0000000000..c6830c8258 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/errors/lambda_standalone.out @@ -0,0 +1,2 @@ +GDTEST_PARSER_ERROR +Standalone lambdas cannot be accessed. Consider assigning it to a variable. diff --git a/modules/gdscript/tests/scripts/parser/errors/match_multiple_variable_binds_in_branch.gd b/modules/gdscript/tests/scripts/parser/errors/match_multiple_variable_binds_in_branch.gd new file mode 100644 index 0000000000..4608c778aa --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/errors/match_multiple_variable_binds_in_branch.gd @@ -0,0 +1,4 @@ +func test(): + match 1: + [[[var a]]], 2: + pass diff --git a/modules/gdscript/tests/scripts/parser/errors/match_multiple_variable_binds_in_branch.out b/modules/gdscript/tests/scripts/parser/errors/match_multiple_variable_binds_in_branch.out new file mode 100644 index 0000000000..1cdc24683b --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/errors/match_multiple_variable_binds_in_branch.out @@ -0,0 +1,2 @@ +GDTEST_PARSER_ERROR +Cannot use a variable bind with multiple patterns. diff --git a/modules/gdscript/tests/scripts/parser/errors/nothing_after_dollar.out b/modules/gdscript/tests/scripts/parser/errors/nothing_after_dollar.out index b3dc181a22..9fafcb5a64 100644 --- a/modules/gdscript/tests/scripts/parser/errors/nothing_after_dollar.out +++ b/modules/gdscript/tests/scripts/parser/errors/nothing_after_dollar.out @@ -1,2 +1,2 @@ GDTEST_PARSER_ERROR -Expect node path as string or identifier after "$". +Expected node path as string or identifier after "$". diff --git a/modules/gdscript/tests/scripts/parser/errors/wrong_value_after_dollar.out b/modules/gdscript/tests/scripts/parser/errors/wrong_value_after_dollar.out index b3dc181a22..9fafcb5a64 100644 --- a/modules/gdscript/tests/scripts/parser/errors/wrong_value_after_dollar.out +++ b/modules/gdscript/tests/scripts/parser/errors/wrong_value_after_dollar.out @@ -1,2 +1,2 @@ GDTEST_PARSER_ERROR -Expect node path as string or identifier after "$". +Expected node path as string or identifier after "$". diff --git a/modules/gdscript/tests/scripts/parser/errors/wrong_value_after_dollar_slash.out b/modules/gdscript/tests/scripts/parser/errors/wrong_value_after_dollar_slash.out index dcb4ccecb0..3062f0be70 100644 --- a/modules/gdscript/tests/scripts/parser/errors/wrong_value_after_dollar_slash.out +++ b/modules/gdscript/tests/scripts/parser/errors/wrong_value_after_dollar_slash.out @@ -1,2 +1,2 @@ GDTEST_PARSER_ERROR -Expect node path after "/". +Expected node path as string or identifier after "/". diff --git a/modules/gdscript/tests/scripts/parser/features/advanced_expression_matching.out b/modules/gdscript/tests/scripts/parser/features/advanced_expression_matching.out index 67c7e28046..3cdafb04a9 100644 --- a/modules/gdscript/tests/scripts/parser/features/advanced_expression_matching.out +++ b/modules/gdscript/tests/scripts/parser/features/advanced_expression_matching.out @@ -10,5 +10,5 @@ wildcard [1,2,[1,{1:2,2:var z,..}]] 3 [1,2,[1,{1:2,2:var z,..}]] -[1, 3, 5, 123] +[1, 3, 5, "123"] wildcard diff --git a/modules/gdscript/tests/scripts/parser/features/arrays_dictionaries_nested_const.gd b/modules/gdscript/tests/scripts/parser/features/arrays_dictionaries_nested_const.gd new file mode 100644 index 0000000000..cc78309ae4 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/features/arrays_dictionaries_nested_const.gd @@ -0,0 +1,58 @@ +# https://github.com/godotengine/godot/issues/50285 + +@warning_ignore(unused_local_constant) +func test(): + const CONST_INNER_DICTIONARY = { "key": true } + const CONST_NESTED_DICTIONARY_OLD_WORKAROUND = { + "key1": "value1", + "key2": CONST_INNER_DICTIONARY + } + # All of these should be valid + const CONST_NESTED_DICTIONARY = { + "key1": "value1", + "key2": { "key": true } + } + + + const CONST_DICTIONARY_WITH_ARRAY = { + "key1": [1,2,3,4] + } + + const CONST_NESTED_ARRAY = [[],[2],[1,2,3]] + const CONST_ARRAY_WITH_DICT = [{"key1": 3}, {"key2": 5}] + + const THREE_DIMENSIONAL_ARRAY = [[[],[],[]],[[],[],[]],[[],[],[]]] + const MANY_NESTED_DICT = { + "key1": { + "key11": { + "key111": {}, + "key112": {}, + }, + "key12": { + "key121": {}, + "key122": {}, + }, + }, + "key2": { + "key21": { + "key211": {}, + "key212": {}, + }, + "key22": { + "key221": {}, + "key222": {}, + }, + } + } + + + const CONST_ARRAY_ACCESS = [1,2,3][0] + const CONST_DICT_ACCESS = {"key1": 5}["key1"] + + const CONST_ARRAY_NESTED_ACCESS = [[1,2,3],[4,5,6],[8,9,10]][0][1] + const CONST_DICT_NESTED_ACCESS = {"key1": {"key2": 1}}["key1"]["key2"] + + print(CONST_ARRAY_ACCESS) + print(CONST_DICT_ACCESS) + print(CONST_ARRAY_NESTED_ACCESS) + print(CONST_DICT_NESTED_ACCESS) diff --git a/modules/gdscript/tests/scripts/parser/features/arrays_dictionaries_nested_const.out b/modules/gdscript/tests/scripts/parser/features/arrays_dictionaries_nested_const.out new file mode 100644 index 0000000000..5883fc5c18 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/features/arrays_dictionaries_nested_const.out @@ -0,0 +1,5 @@ +GDTEST_OK +1 +5 +2 +1 diff --git a/modules/gdscript/tests/scripts/parser/features/dictionary.out b/modules/gdscript/tests/scripts/parser/features/dictionary.out index 54083c1afc..5f999f573a 100644 --- a/modules/gdscript/tests/scripts/parser/features/dictionary.out +++ b/modules/gdscript/tests/scripts/parser/features/dictionary.out @@ -7,8 +7,8 @@ null false empty array zero Vector2i -{22:{4:[nesting, arrays]}} -{4:[nesting, arrays]} -[nesting, arrays] +{22:{4:["nesting", "arrays"]}} +{4:["nesting", "arrays"]} +["nesting", "arrays"] nesting arrays diff --git a/modules/gdscript/tests/scripts/parser/features/dictionary_lua_style.out b/modules/gdscript/tests/scripts/parser/features/dictionary_lua_style.out index 5b0ea9df43..5143d040a9 100644 --- a/modules/gdscript/tests/scripts/parser/features/dictionary_lua_style.out +++ b/modules/gdscript/tests/scripts/parser/features/dictionary_lua_style.out @@ -1,2 +1,2 @@ GDTEST_OK -{a:1, b:2, with spaces:3, 2:4} +{"a":1, "b":2, "with spaces":3, "2":4} diff --git a/modules/gdscript/tests/scripts/parser/features/dictionary_mixed_syntax.out b/modules/gdscript/tests/scripts/parser/features/dictionary_mixed_syntax.out index 62be807a1f..dd28609850 100644 --- a/modules/gdscript/tests/scripts/parser/features/dictionary_mixed_syntax.out +++ b/modules/gdscript/tests/scripts/parser/features/dictionary_mixed_syntax.out @@ -1,2 +1,2 @@ GDTEST_OK -{hello:{world:{is:beautiful}}} +{"hello":{"world":{"is":"beautiful"}}} diff --git a/modules/gdscript/tests/scripts/parser/features/dollar_and_percent_get_node.gd b/modules/gdscript/tests/scripts/parser/features/dollar_and_percent_get_node.gd new file mode 100644 index 0000000000..f04f4de08d --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/features/dollar_and_percent_get_node.gd @@ -0,0 +1,49 @@ +extends Node + +func test(): + var child = Node.new() + child.name = "Child" + add_child(child) + child.owner = self + + var hey = Node.new() + hey.name = "Hey" + child.add_child(hey) + hey.owner = self + hey.unique_name_in_owner = true + + var fake_hey = Node.new() + fake_hey.name = "Hey" + add_child(fake_hey) + fake_hey.owner = self + + var sub_child = Node.new() + sub_child.name = "SubChild" + hey.add_child(sub_child) + sub_child.owner = self + + var howdy = Node.new() + howdy.name = "Howdy" + sub_child.add_child(howdy) + howdy.owner = self + howdy.unique_name_in_owner = true + + print(hey == $Child/Hey) + print(howdy == $Child/Hey/SubChild/Howdy) + + print(%Hey == hey) + print($%Hey == hey) + print(%"Hey" == hey) + print($"%Hey" == hey) + print($%"Hey" == hey) + print(%Hey/%Howdy == howdy) + print($%Hey/%Howdy == howdy) + print($"%Hey/%Howdy" == howdy) + print($"%Hey"/"%Howdy" == howdy) + print(%"Hey"/"%Howdy" == howdy) + print($%"Hey"/"%Howdy" == howdy) + print($"%Hey"/%"Howdy" == howdy) + print(%"Hey"/%"Howdy" == howdy) + print($%"Hey"/%"Howdy" == howdy) + print(%"Hey/%Howdy" == howdy) + print($%"Hey/%Howdy" == howdy) diff --git a/modules/gdscript/tests/scripts/parser/features/dollar_and_percent_get_node.out b/modules/gdscript/tests/scripts/parser/features/dollar_and_percent_get_node.out new file mode 100644 index 0000000000..041c4439b0 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/features/dollar_and_percent_get_node.out @@ -0,0 +1,19 @@ +GDTEST_OK +true +true +true +true +true +true +true +true +true +true +true +true +true +true +true +true +true +true diff --git a/modules/gdscript/tests/scripts/parser/features/if_after_lambda.gd b/modules/gdscript/tests/scripts/parser/features/if_after_lambda.gd new file mode 100644 index 0000000000..f5e26ab1ab --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/features/if_after_lambda.gd @@ -0,0 +1,7 @@ +# https://github.com/godotengine/godot/issues/61231 + +func test(): + var my_lambda = func(): + print("hello") + if 0 == 0: + my_lambda.call() diff --git a/modules/gdscript/tests/scripts/parser/features/if_after_lambda.out b/modules/gdscript/tests/scripts/parser/features/if_after_lambda.out new file mode 100644 index 0000000000..58774d2d3f --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/features/if_after_lambda.out @@ -0,0 +1,2 @@ +GDTEST_OK +hello diff --git a/modules/gdscript/tests/scripts/parser/features/lambda_default_parameter_capture.gd b/modules/gdscript/tests/scripts/parser/features/lambda_default_parameter_capture.gd new file mode 100644 index 0000000000..2140b6923e --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/features/lambda_default_parameter_capture.gd @@ -0,0 +1,7 @@ +# https://github.com/godotengine/godot/issues/56751 + +func test(): + var x = "local" + var lambda = func(param = x): + print(param) + lambda.call() diff --git a/modules/gdscript/tests/scripts/parser/features/lambda_default_parameter_capture.out b/modules/gdscript/tests/scripts/parser/features/lambda_default_parameter_capture.out new file mode 100644 index 0000000000..ce3241b94d --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/features/lambda_default_parameter_capture.out @@ -0,0 +1,2 @@ +GDTEST_OK +local diff --git a/modules/gdscript/tests/scripts/parser/features/match_bind_unused.gd b/modules/gdscript/tests/scripts/parser/features/match_bind_unused.gd new file mode 100644 index 0000000000..707e4532cc --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/features/match_bind_unused.gd @@ -0,0 +1,13 @@ +# https://github.com/godotengine/godot/pull/61666 + +func test(): + var dict := {"key": "value"} + match dict: + {"key": var value}: + print(value) # used, no warning + match dict: + {"key": var value}: + pass # unused, warning + match dict: + {"key": var _value}: + pass # unused, suppressed warning from underscore diff --git a/modules/gdscript/tests/scripts/parser/features/match_bind_unused.out b/modules/gdscript/tests/scripts/parser/features/match_bind_unused.out new file mode 100644 index 0000000000..057c1b11e5 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/features/match_bind_unused.out @@ -0,0 +1,6 @@ +GDTEST_OK +>> WARNING +>> Line: 9 +>> UNUSED_VARIABLE +>> The local variable 'value' is declared but never used in the block. If this is intended, prefix it with an underscore: '_value' +value diff --git a/modules/gdscript/tests/scripts/parser/features/match_dictionary.gd b/modules/gdscript/tests/scripts/parser/features/match_dictionary.gd new file mode 100644 index 0000000000..377dd25e9e --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/features/match_dictionary.gd @@ -0,0 +1,43 @@ +func foo(x): + match x: + {"key1": "value1", "key2": "value2"}: + print('{"key1": "value1", "key2": "value2"}') + {"key1": "value1", "key2"}: + print('{"key1": "value1", "key2"}') + {"key1", "key2": "value2"}: + print('{"key1", "key2": "value2"}') + {"key1", "key2"}: + print('{"key1", "key2"}') + {"key1": "value1"}: + print('{"key1": "value1"}') + {"key1"}: + print('{"key1"}') + _: + print("wildcard") + +func bar(x): + match x: + {0}: + print("0") + {1}: + print("1") + {2}: + print("2") + _: + print("wildcard") + +func test(): + foo({"key1": "value1", "key2": "value2"}) + foo({"key1": "value1", "key2": ""}) + foo({"key1": "", "key2": "value2"}) + foo({"key1": "", "key2": ""}) + foo({"key1": "value1"}) + foo({"key1": ""}) + foo({"key1": "value1", "key2": "value2", "key3": "value3"}) + foo({"key1": "value1", "key3": ""}) + foo({"key2": "value2"}) + foo({"key3": ""}) + bar({0: "0"}) + bar({1: "1"}) + bar({2: "2"}) + bar({3: "3"}) diff --git a/modules/gdscript/tests/scripts/parser/features/match_dictionary.out b/modules/gdscript/tests/scripts/parser/features/match_dictionary.out new file mode 100644 index 0000000000..4dee886927 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/features/match_dictionary.out @@ -0,0 +1,15 @@ +GDTEST_OK +{"key1": "value1", "key2": "value2"} +{"key1": "value1", "key2"} +{"key1", "key2": "value2"} +{"key1", "key2"} +{"key1": "value1"} +{"key1"} +wildcard +wildcard +wildcard +wildcard +0 +1 +2 +wildcard diff --git a/modules/gdscript/tests/scripts/parser/features/match_multiple_patterns_with_array.gd b/modules/gdscript/tests/scripts/parser/features/match_multiple_patterns_with_array.gd new file mode 100644 index 0000000000..dbe223f5f5 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/features/match_multiple_patterns_with_array.gd @@ -0,0 +1,26 @@ +func foo(x): + match x: + 1, [2]: + print('1, [2]') + _: + print('wildcard') + +func bar(x): + match x: + [1], [2], [3]: + print('[1], [2], [3]') + [4]: + print('[4]') + _: + print('wildcard') + +func test(): + foo(1) + foo([2]) + foo(2) + bar([1]) + bar([2]) + bar([3]) + bar([4]) + bar([5]) + diff --git a/modules/gdscript/tests/scripts/parser/features/match_multiple_patterns_with_array.out b/modules/gdscript/tests/scripts/parser/features/match_multiple_patterns_with_array.out new file mode 100644 index 0000000000..a12b934d67 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/features/match_multiple_patterns_with_array.out @@ -0,0 +1,9 @@ +GDTEST_OK +1, [2] +1, [2] +wildcard +[1], [2], [3] +[1], [2], [3] +[1], [2], [3] +[4] +wildcard diff --git a/modules/gdscript/tests/scripts/parser/features/match_multiple_variable_binds_in_pattern.gd b/modules/gdscript/tests/scripts/parser/features/match_multiple_variable_binds_in_pattern.gd new file mode 100644 index 0000000000..a0ae7fb17c --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/features/match_multiple_variable_binds_in_pattern.gd @@ -0,0 +1,6 @@ +func test(): + match [1, 2, 3]: + [var a, var b, var c]: + print(a == 1) + print(b == 2) + print(c == 3) diff --git a/modules/gdscript/tests/scripts/parser/features/match_multiple_variable_binds_in_pattern.out b/modules/gdscript/tests/scripts/parser/features/match_multiple_variable_binds_in_pattern.out new file mode 100644 index 0000000000..316db6d748 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/features/match_multiple_variable_binds_in_pattern.out @@ -0,0 +1,4 @@ +GDTEST_OK +true +true +true diff --git a/modules/gdscript/tests/scripts/parser/features/nested_dictionary.out b/modules/gdscript/tests/scripts/parser/features/nested_dictionary.out index 4009160439..8b8c33202f 100644 --- a/modules/gdscript/tests/scripts/parser/features/nested_dictionary.out +++ b/modules/gdscript/tests/scripts/parser/features/nested_dictionary.out @@ -1,5 +1,5 @@ GDTEST_OK -{8:{key:value}} -{key:value} +{8:{"key":"value"}} +{"key":"value"} value value diff --git a/modules/gdscript/tests/scripts/parser/warnings/unreachable_code_after_return_bug_55154.gd b/modules/gdscript/tests/scripts/parser/warnings/unreachable_code_after_return_bug_55154.gd new file mode 100644 index 0000000000..d00d483a73 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/warnings/unreachable_code_after_return_bug_55154.gd @@ -0,0 +1,16 @@ +func test(): + var foo := "bar" + match foo: + "baz": + return + _: + pass + match foo: + "baz": + return + match foo: + "bar": + pass + _: + return + print("reached") diff --git a/modules/gdscript/tests/scripts/parser/warnings/unreachable_code_after_return_bug_55154.out b/modules/gdscript/tests/scripts/parser/warnings/unreachable_code_after_return_bug_55154.out new file mode 100644 index 0000000000..47db6b631b --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/warnings/unreachable_code_after_return_bug_55154.out @@ -0,0 +1,2 @@ +GDTEST_OK +reached diff --git a/modules/gdscript/tests/scripts/runtime/features/lambda_use_self.gd b/modules/gdscript/tests/scripts/runtime/features/lambda_use_self.gd new file mode 100644 index 0000000000..3d063869ab --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/features/lambda_use_self.gd @@ -0,0 +1,23 @@ +var member = "foo" + +func bar(): + print("bar") + +func test(): + var lambda1 = func(): + print(member) + lambda1.call() + + var lambda2 = func(): + var nested = func(): + print(member) + nested.call() + lambda2.call() + + var lambda3 = func(): + bar() + lambda3.call() + + var lambda4 = func(): + return self + print(lambda4.call() == self) diff --git a/modules/gdscript/tests/scripts/runtime/features/lambda_use_self.out b/modules/gdscript/tests/scripts/runtime/features/lambda_use_self.out new file mode 100644 index 0000000000..53d602b234 --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/features/lambda_use_self.out @@ -0,0 +1,5 @@ +GDTEST_OK +foo +foo +bar +true diff --git a/modules/gdscript/tests/scripts/runtime/features/stringify.out b/modules/gdscript/tests/scripts/runtime/features/stringify.out index 7670fc0128..d4468737a5 100644 --- a/modules/gdscript/tests/scripts/runtime/features/stringify.out +++ b/modules/gdscript/tests/scripts/runtime/features/stringify.out @@ -21,14 +21,14 @@ hello/world RID(0) Node::get_name Node::[signal]property_list_changed -{hello:123} -[hello, 123] +{"hello":123} +["hello", 123] [255, 0, 1] [-1, 0, 1] [-1, 0, 1] [-1, 0, 1] [-1, 0, 1] -[hello, world] +["hello", "world"] [(1, 1), (0, 0)] [(1, 1, 1), (0, 0, 0)] [(1, 0, 0, 1), (0, 0, 1, 1), (0, 1, 0, 1)] diff --git a/modules/gdscript/tests/test_gdscript.cpp b/modules/gdscript/tests/test_gdscript.cpp index 4255030b4e..d8f60d5e9b 100644 --- a/modules/gdscript/tests/test_gdscript.cpp +++ b/modules/gdscript/tests/test_gdscript.cpp @@ -204,8 +204,8 @@ void test(TestType p_type) { return; } - FileAccessRef fa = FileAccess::open(test, FileAccess::READ); - ERR_FAIL_COND_MSG(!fa, "Could not open file: " + test); + Ref<FileAccess> fa = FileAccess::open(test, FileAccess::READ); + ERR_FAIL_COND_MSG(fa.is_null(), "Could not open file: " + test); // Initialize the language for the test routine. init_language(fa->get_path_absolute().get_base_dir()); |