diff options
Diffstat (limited to 'modules/gdscript')
67 files changed, 1350 insertions, 428 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 4d6320d8c3..d0926d317b 100644 --- a/modules/gdscript/doc_classes/@GDScript.xml +++ b/modules/gdscript/doc_classes/@GDScript.xml @@ -186,6 +186,7 @@ <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. [codeblock] print(range(4)) print(range(2, 5)) @@ -211,6 +212,17 @@ 6 3 [/codeblock] + To iterate over [float], convert them in the loop. + [codeblock] + for i in range (3, 0, -1): + print(i / 10.0) + [/codeblock] + Output: + [codeblock] + 0.3 + 0.2 + 0.1 + [/codeblock] </description> </method> <method name="str" qualifiers="vararg"> 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_templates/CharacterBody2D/basic_movement.gd b/modules/gdscript/editor/script_templates/CharacterBody2D/basic_movement.gd index edaccae018..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") @@ -12,18 +13,18 @@ var gravity: int = ProjectSettings.get_setting("physics/2d/default_gravity") func _physics_process(delta: float) -> void: # Add the gravity. if not is_on_floor(): - motion_velocity.y += gravity * delta + velocity.y += gravity * delta # Handle Jump. if Input.is_action_just_pressed("ui_accept") and is_on_floor(): - motion_velocity.y = JUMP_VELOCITY + velocity.y = JUMP_VELOCITY # Get the input direction and handle the movement/deceleration. # As good practice, you should replace UI actions with custom gameplay actions. var direction := Input.get_axis("ui_left", "ui_right") if direction: - motion_velocity.x = direction * SPEED + velocity.x = direction * SPEED else: - motion_velocity.x = move_toward(motion_velocity.x, 0, SPEED) + velocity.x = move_toward(velocity.x, 0, SPEED) move_and_slide() diff --git a/modules/gdscript/editor_templates/CharacterBody3D/basic_movement.gd b/modules/gdscript/editor/script_templates/CharacterBody3D/basic_movement.gd index e191e5451a..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") @@ -12,21 +13,21 @@ var gravity: float = ProjectSettings.get_setting("physics/3d/default_gravity") func _physics_process(delta: float) -> void: # Add the gravity. if not is_on_floor(): - motion_velocity.y -= gravity * delta + velocity.y -= gravity * delta # Handle Jump. if Input.is_action_just_pressed("ui_accept") and is_on_floor(): - motion_velocity.y = JUMP_VELOCITY + velocity.y = JUMP_VELOCITY # Get the input direction and handle the movement/deceleration. # As good practice, you should replace UI actions with custom gameplay actions. var input_dir := Input.get_vector("ui_left", "ui_right", "ui_up", "ui_down") var direction := (transform.basis * Vector3(input_dir.x, 0, input_dir.y)).normalized() if direction: - motion_velocity.x = direction.x * SPEED - motion_velocity.z = direction.z * SPEED + velocity.x = direction.x * SPEED + velocity.z = direction.z * SPEED else: - motion_velocity.x = move_toward(motion_velocity.x, 0, SPEED) - motion_velocity.z = move_toward(motion_velocity.z, 0, SPEED) + velocity.x = move_toward(velocity.x, 0, SPEED) + velocity.z = move_toward(velocity.z, 0, SPEED) move_and_slide() 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 d71f2592fe..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: Shader.Mode, type: VisualShader.Type) -> 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 58a788e255..1b4711804c 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); @@ -796,7 +811,7 @@ void GDScript::_set_subclass_path(Ref<GDScript> &p_sc, const String &p_path) { String GDScript::_get_debug_path() const { if (is_built_in() && !get_name().is_empty()) { - return get_name() + " (" + get_path().get_slice("::", 0) + ")"; + return get_name() + " (" + get_path() + ")"; } else { return get_path(); } @@ -824,11 +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())) { - return OK; - } -#else - if (source.contains("_BASE_")) { + if (EditorSettings::get_singleton() && basedir.begins_with(EditorSettings::get_singleton()->get_project_script_templates_dir())) { return OK; } #endif @@ -938,7 +949,7 @@ const Vector<Multiplayer::RPCConfig> GDScript::get_rpc_methods() const { return rpc_functions; } -Variant GDScript::call(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) { +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); @@ -952,7 +963,7 @@ Variant GDScript::call(const StringName &p_method, const Variant **p_args, int p //none found, regular - return Script::call(p_method, p_args, p_argcount, r_error); + return Script::callp(p_method, p_args, p_argcount, r_error); } bool GDScript::_get(const StringName &p_name, Variant &r_ret) const { @@ -1019,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; @@ -1273,7 +1288,7 @@ bool GDScriptInstance::set(const StringName &p_name, const Variant &p_value) { if (member->setter) { const Variant *val = &p_value; Callable::CallError err; - call(member->setter, &val, 1, err); + callp(member->setter, &val, 1, err); if (err.error == Callable::CallError::CALL_OK) { return true; //function exists, call was successful } else { @@ -1335,7 +1350,7 @@ bool GDScriptInstance::get(const StringName &p_name, Variant &r_ret) const { if (E) { if (E->get().getter) { Callable::CallError err; - r_ret = const_cast<GDScriptInstance *>(this)->call(E->get().getter, nullptr, 0, err); + r_ret = const_cast<GDScriptInstance *>(this)->callp(E->get().getter, nullptr, 0, err); if (err.error == Callable::CallError::CALL_OK) { return true; } @@ -1520,7 +1535,7 @@ bool GDScriptInstance::has_method(const StringName &p_method) const { return false; } -Variant GDScriptInstance::call(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) { +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); @@ -1555,7 +1570,7 @@ void GDScriptInstance::notification(int p_notification) { String GDScriptInstance::to_string(bool *r_valid) { if (has_method(CoreStringNames::get_singleton()->_to_string)) { Callable::CallError ce; - Variant ret = call(CoreStringNames::get_singleton()->_to_string, nullptr, 0, ce); + Variant ret = callp(CoreStringNames::get_singleton()->_to_string, nullptr, 0, ce); if (ce.error == Callable::CallError::CALL_OK) { if (ret.get_type() != Variant::STRING) { if (r_valid) { @@ -2088,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(); } @@ -2122,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(); @@ -2277,7 +2292,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; } @@ -2320,8 +2335,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()) { @@ -2338,24 +2353,23 @@ 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); @@ -2364,12 +2378,12 @@ Error ResourceFormatSaverGDScript::save(const String &p_path, const RES &p_resou 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 2b43e6d21b..a20f3b2fca 100644 --- a/modules/gdscript/gdscript.h +++ b/modules/gdscript/gdscript.h @@ -52,6 +52,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); }; @@ -166,7 +167,7 @@ protected: bool _set(const StringName &p_name, const Variant &p_value); void _get_property_list(List<PropertyInfo> *p_properties) const; - Variant call(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) override; + Variant callp(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) override; static void _bind_methods(); @@ -212,7 +213,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 @@ -261,11 +262,12 @@ 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 @@ -285,7 +287,7 @@ public: virtual void get_method_list(List<MethodInfo> *p_list) const; virtual bool has_method(const StringName &p_method) const; - virtual Variant call(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error); + virtual Variant callp(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error); Variant debug_get_member_by_index(int p_idx) const { return members[p_idx]; } @@ -311,17 +313,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; 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 +331,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); @@ -455,7 +457,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 +512,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 +521,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 94daba4bf6..d346264933 100644 --- a/modules/gdscript/gdscript_analyzer.cpp +++ b/modules/gdscript/gdscript_analyzer.cpp @@ -277,6 +277,7 @@ Error GDScriptAnalyzer::resolve_inheritance(GDScriptParser::ClassNode *p_class, base = parser->get_parser()->head->get_datatype(); } else { if (p_class->extends.is_empty()) { + push_error("Could not resolve an empty super class path.", p_class); return ERR_PARSE_ERROR; } const StringName &name = p_class->extends[extends_index++]; @@ -646,41 +647,51 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas } } - if (member.variable->datatype_specifier != nullptr) { - datatype = specified_type; + // 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); + push_error(vformat(R"(Identifier "%s" must be declared above current variable.)", initializer_identifier->name), member.variable->initializer); + } else { + ERR_PRINT("Parser bug (please report): tried to assign unset node without an identifier."); + } + } else { + if (member.variable->datatype_specifier != nullptr) { + datatype = specified_type; - if (member.variable->initializer != nullptr) { - if (!is_type_compatible(datatype, member.variable->initializer->get_datatype(), true, member.variable->initializer)) { - // Try reverse test since it can be a masked subtype. - if (!is_type_compatible(member.variable->initializer->get_datatype(), datatype, true, member.variable->initializer)) { - push_error(vformat(R"(Value of type "%s" cannot be assigned to a variable of type "%s".)", member.variable->initializer->get_datatype().to_string(), datatype.to_string()), member.variable->initializer); - } else { - // TODO: Add warning. + if (member.variable->initializer != nullptr) { + if (!is_type_compatible(datatype, member.variable->initializer->get_datatype(), true, member.variable->initializer)) { + // Try reverse test since it can be a masked subtype. + if (!is_type_compatible(member.variable->initializer->get_datatype(), datatype, true, member.variable->initializer)) { + push_error(vformat(R"(Value of type "%s" cannot be assigned to a variable of type "%s".)", member.variable->initializer->get_datatype().to_string(), datatype.to_string()), member.variable->initializer); + } else { + // TODO: Add warning. + mark_node_unsafe(member.variable->initializer); + member.variable->use_conversion_assign = true; + } + } else if (datatype.builtin_type == Variant::INT && member.variable->initializer->get_datatype().builtin_type == Variant::FLOAT) { +#ifdef DEBUG_ENABLED + parser->push_warning(member.variable->initializer, GDScriptWarning::NARROWING_CONVERSION); +#endif + } + if (member.variable->initializer->get_datatype().is_variant()) { + // TODO: Warn unsafe assign. mark_node_unsafe(member.variable->initializer); member.variable->use_conversion_assign = true; } - } else if (datatype.builtin_type == Variant::INT && member.variable->initializer->get_datatype().builtin_type == Variant::FLOAT) { -#ifdef DEBUG_ENABLED - parser->push_warning(member.variable->initializer, GDScriptWarning::NARROWING_CONVERSION); -#endif } - if (member.variable->initializer->get_datatype().is_variant()) { - // TODO: Warn unsafe assign. - mark_node_unsafe(member.variable->initializer); - member.variable->use_conversion_assign = true; + } else if (member.variable->infer_datatype) { + if (member.variable->initializer == nullptr) { + push_error(vformat(R"(Cannot infer the type of "%s" variable because there's no default value.)", member.variable->identifier->name), member.variable->identifier); + } else if (!datatype.is_set() || datatype.has_no_type()) { + push_error(vformat(R"(Cannot infer the type of "%s" variable because the initial value doesn't have a set type.)", member.variable->identifier->name), member.variable->initializer); + } else if (datatype.is_variant()) { + push_error(vformat(R"(Cannot infer the type of "%s" variable because the initial value is Variant. Use explicit "Variant" type if this is intended.)", member.variable->identifier->name), member.variable->initializer); + } else if (datatype.builtin_type == Variant::NIL) { + push_error(vformat(R"(Cannot infer the type of "%s" variable because the initial value is "null".)", member.variable->identifier->name), member.variable->initializer); } + datatype.type_source = GDScriptParser::DataType::ANNOTATED_INFERRED; } - } else if (member.variable->infer_datatype) { - if (member.variable->initializer == nullptr) { - push_error(vformat(R"(Cannot infer the type of "%s" variable because there's no default value.)", member.variable->identifier->name), member.variable->identifier); - } else if (!datatype.is_set() || datatype.has_no_type()) { - push_error(vformat(R"(Cannot infer the type of "%s" variable because the initial value doesn't have a set type.)", member.variable->identifier->name), member.variable->initializer); - } else if (datatype.is_variant()) { - push_error(vformat(R"(Cannot infer the type of "%s" variable because the initial value is Variant. Use explicit "Variant" type if this is intended.)", member.variable->identifier->name), member.variable->initializer); - } else if (datatype.builtin_type == Variant::NIL) { - push_error(vformat(R"(Cannot infer the type of "%s" variable because the initial value is "null".)", member.variable->identifier->name), member.variable->initializer); - } - datatype.type_source = GDScriptParser::DataType::ANNOTATED_INFERRED; } datatype.is_constant = false; @@ -1114,6 +1125,10 @@ void GDScriptAnalyzer::resolve_function_signature(GDScriptParser::FunctionNode * GDScriptParser::FunctionNode *previous_function = parser->current_function; parser->current_function = p_function; +#ifdef TOOLS_ENABLED + int default_value_count = 0; +#endif // TOOLS_ENABLED + for (int i = 0; i < p_function->parameters.size(); i++) { resolve_parameter(p_function->parameters[i]); #ifdef DEBUG_ENABLED @@ -1123,13 +1138,17 @@ void GDScriptAnalyzer::resolve_function_signature(GDScriptParser::FunctionNode * is_shadowing(p_function->parameters[i]->identifier, "function parameter"); #endif // DEBUG_ENABLED #ifdef TOOLS_ENABLED - if (p_function->parameters[i]->default_value && p_function->parameters[i]->default_value->is_constant) { - p_function->default_arg_values.push_back(p_function->parameters[i]->default_value->reduced_value); + if (p_function->parameters[i]->default_value) { + default_value_count++; + + if (p_function->parameters[i]->default_value->is_constant) { + p_function->default_arg_values.push_back(p_function->parameters[i]->default_value->reduced_value); + } } #endif // TOOLS_ENABLED } - if (p_function->identifier->name == "_init") { + if (p_function->identifier->name == GDScriptLanguage::get_singleton()->strings._init) { // Constructor. GDScriptParser::DataType return_type = parser->current_class->get_datatype(); return_type.is_meta_type = false; @@ -1143,6 +1162,57 @@ void GDScriptAnalyzer::resolve_function_signature(GDScriptParser::FunctionNode * } else { GDScriptParser::DataType return_type = resolve_datatype(p_function->return_type); 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. + // Not for the constructor which can vary in signature. + GDScriptParser::DataType base_type = parser->current_class->base_type; + GDScriptParser::DataType parent_return_type; + List<GDScriptParser::DataType> parameters_types; + int default_par_count = 0; + bool is_static = false; + bool is_vararg = false; + if (get_function_signature(p_function, false, base_type, p_function->identifier->name, parent_return_type, parameters_types, default_par_count, is_static, is_vararg)) { + bool valid = p_function->is_static == is_static; + valid = valid && parent_return_type == p_function->get_datatype(); + + int par_count_diff = p_function->parameters.size() - parameters_types.size(); + valid = valid && par_count_diff >= 0; + valid = valid && default_value_count >= default_par_count + par_count_diff; + + int i = 0; + for (const GDScriptParser::DataType &par_type : parameters_types) { + valid = valid && par_type == p_function->parameters[i++]->get_datatype(); + } + + if (!valid) { + // Compute parent signature as a string to show in the error message. + String parent_signature = parent_return_type.is_hard_type() ? parent_return_type.to_string() : "Variant"; + if (parent_signature == "null") { + parent_signature = "void"; + } + parent_signature += " " + p_function->identifier->name.operator String() + "("; + int j = 0; + for (const GDScriptParser::DataType &par_type : parameters_types) { + if (j > 0) { + parent_signature += ", "; + } + String parameter = par_type.to_string(); + if (parameter == "null") { + parameter = "Variant"; + } + parent_signature += parameter; + if (j == parameters_types.size() - default_par_count) { + parent_signature += " = default"; + } + + j++; + } + parent_signature += ")"; + push_error(vformat(R"(The function signature doesn't match the parent. Parent signature is "%s".)", parent_signature), p_function); + } + } +#endif // TOOLS_ENABLED } parser->current_function = previous_function; @@ -1241,7 +1311,7 @@ void GDScriptAnalyzer::resolve_for(GDScriptParser::ForNode *p_for) { bool list_resolved = false; // Optimize constant range() call to not allocate an array. - // Use int, Vector2, Vector3 instead, which also can be used as range iterators. + // Use int, Vector2i, Vector3i instead, which also can be used as range iterators. if (p_for->list && p_for->list->type == GDScriptParser::Node::CALL) { GDScriptParser::CallNode *call = static_cast<GDScriptParser::CallNode *>(p_for->list); GDScriptParser::Node::Type callee_type = call->get_callee_type(); @@ -1414,7 +1484,7 @@ void GDScriptAnalyzer::resolve_variable(GDScriptParser::VariableNode *p_variable parser->push_warning(p_variable->initializer, GDScriptWarning::NARROWING_CONVERSION); #endif } - if (p_variable->initializer->get_datatype().is_variant()) { + if (p_variable->initializer->get_datatype().is_variant() && !type.is_variant()) { // TODO: Warn unsafe assign. mark_node_unsafe(p_variable->initializer); p_variable->use_conversion_assign = true; @@ -2233,7 +2303,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 @@ -2356,6 +2426,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; @@ -2406,7 +2480,9 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a GDScriptParser::DataType return_type; List<GDScriptParser::DataType> par_types; - if (get_function_signature(p_call, base_type, p_call->function_name, return_type, par_types, default_arg_count, is_static, is_vararg)) { + bool is_constructor = (base_type.is_meta_type || (p_call->callee && p_call->callee->type == GDScriptParser::Node::IDENTIFIER)) && p_call->function_name == SNAME("new"); + + if (get_function_signature(p_call, is_constructor, base_type, p_call->function_name, return_type, par_types, default_arg_count, is_static, is_vararg)) { // If the function require typed arrays we must make literals be typed. for (const KeyValue<int, GDScriptParser::ArrayNode *> &E : arrays) { int index = E.key; @@ -2422,18 +2498,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) { @@ -2463,11 +2545,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); @@ -2557,26 +2641,32 @@ 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); } GDScriptParser::DataType GDScriptAnalyzer::make_global_class_meta_type(const StringName &p_class_name, const GDScriptParser::Node *p_source) { + GDScriptParser::DataType type; + Ref<GDScriptParserRef> ref = get_parser_for(ScriptServer::get_global_class_path(p_class_name)); - Error err = ref->raise_status(GDScriptParserRef::INTERFACE_SOLVED); + if (ref.is_null()) { + push_error(vformat(R"(Could not find script for class "%s".)", p_class_name), p_source); + type.type_source = GDScriptParser::DataType::UNDETECTED; + type.kind = GDScriptParser::DataType::VARIANT; + return type; + } + Error err = ref->raise_status(GDScriptParserRef::INTERFACE_SOLVED); if (err) { push_error(vformat(R"(Could not resolve class "%s", because of a parser error.)", p_class_name), p_source); - GDScriptParser::DataType type; type.type_source = GDScriptParser::DataType::UNDETECTED; type.kind = GDScriptParser::DataType::VARIANT; return type; } - GDScriptParser::DataType type; type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT; type.kind = GDScriptParser::DataType::CLASS; type.builtin_type = Variant::OBJECT; @@ -2769,21 +2859,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; @@ -2792,6 +2886,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; } } @@ -2842,7 +2937,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: @@ -2873,18 +2972,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; } @@ -3064,6 +3182,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) { @@ -3074,6 +3193,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; @@ -3391,6 +3516,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; @@ -3411,6 +3543,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; @@ -3560,7 +3699,7 @@ GDScriptParser::DataType GDScriptAnalyzer::type_from_property(const PropertyInfo return result; } -bool GDScriptAnalyzer::get_function_signature(GDScriptParser::CallNode *p_source, GDScriptParser::DataType p_base_type, const StringName &p_function, GDScriptParser::DataType &r_return_type, List<GDScriptParser::DataType> &r_par_types, int &r_default_arg_count, bool &r_static, bool &r_vararg) { +bool GDScriptAnalyzer::get_function_signature(GDScriptParser::Node *p_source, bool p_is_constructor, GDScriptParser::DataType p_base_type, const StringName &p_function, GDScriptParser::DataType &r_return_type, List<GDScriptParser::DataType> &r_par_types, int &r_default_arg_count, bool &r_static, bool &r_vararg) { r_static = false; r_vararg = false; r_default_arg_count = 0; @@ -3600,8 +3739,7 @@ bool GDScriptAnalyzer::get_function_signature(GDScriptParser::CallNode *p_source return false; } - bool is_constructor = (p_base_type.is_meta_type || (p_source->callee && p_source->callee->type == GDScriptParser::Node::IDENTIFIER)) && p_function == StaticCString::create("new"); - if (is_constructor) { + if (p_is_constructor) { function_name = "_init"; r_static = true; } @@ -3622,7 +3760,7 @@ bool GDScriptAnalyzer::get_function_signature(GDScriptParser::CallNode *p_source } if (found_function != nullptr) { - r_static = is_constructor || found_function->is_static; + r_static = p_is_constructor || found_function->is_static; for (int i = 0; i < found_function->parameters.size(); i++) { r_par_types.push_back(found_function->parameters[i]->get_datatype()); if (found_function->parameters[i]->default_value != nullptr) { @@ -3648,7 +3786,7 @@ bool GDScriptAnalyzer::get_function_signature(GDScriptParser::CallNode *p_source } // If the base is a script, it might be trying to access members of the Script class itself. - if (p_base_type.is_meta_type && !is_constructor && (p_base_type.kind == GDScriptParser::DataType::SCRIPT || p_base_type.kind == GDScriptParser::DataType::CLASS)) { + if (p_base_type.is_meta_type && !p_is_constructor && (p_base_type.kind == GDScriptParser::DataType::SCRIPT || p_base_type.kind == GDScriptParser::DataType::CLASS)) { MethodInfo info; StringName script_class = p_base_type.kind == GDScriptParser::DataType::SCRIPT ? p_base_type.script_type->get_class_name() : StringName(GDScript::get_class_static()); @@ -3668,7 +3806,7 @@ bool GDScriptAnalyzer::get_function_signature(GDScriptParser::CallNode *p_source } #endif - if (is_constructor) { + if (p_is_constructor) { // Native types always have a default constructor. r_return_type = p_base_type; r_return_type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT; @@ -3692,6 +3830,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)); @@ -4036,6 +4175,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); } diff --git a/modules/gdscript/gdscript_analyzer.h b/modules/gdscript/gdscript_analyzer.h index 2697a6ec2b..519e1975c4 100644 --- a/modules/gdscript/gdscript_analyzer.h +++ b/modules/gdscript/gdscript_analyzer.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); @@ -105,7 +105,7 @@ class GDScriptAnalyzer { GDScriptParser::DataType type_from_metatype(const GDScriptParser::DataType &p_meta_type) const; GDScriptParser::DataType type_from_property(const PropertyInfo &p_property) const; GDScriptParser::DataType make_global_class_meta_type(const StringName &p_class_name, const GDScriptParser::Node *p_source); - bool get_function_signature(GDScriptParser::CallNode *p_source, GDScriptParser::DataType base_type, const StringName &p_function, GDScriptParser::DataType &r_return_type, List<GDScriptParser::DataType> &r_par_types, int &r_default_arg_count, bool &r_static, bool &r_vararg); + bool get_function_signature(GDScriptParser::Node *p_source, bool p_is_constructor, GDScriptParser::DataType base_type, const StringName &p_function, GDScriptParser::DataType &r_return_type, List<GDScriptParser::DataType> &r_par_types, int &r_default_arg_count, bool &r_static, bool &r_vararg); bool function_signature_from_info(const MethodInfo &p_info, GDScriptParser::DataType &r_return_type, List<GDScriptParser::DataType> &r_par_types, int &r_default_arg_count, bool &r_static, bool &r_vararg); bool validate_call_arg(const List<GDScriptParser::DataType> &p_par_types, int p_default_args_count, bool p_is_vararg, const GDScriptParser::CallNode *p_call); bool validate_call_arg(const MethodInfo &p_method, const GDScriptParser::CallNode *p_call); @@ -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..e72069bcd5 100644 --- a/modules/gdscript/gdscript_byte_codegen.cpp +++ b/modules/gdscript/gdscript_byte_codegen.cpp @@ -468,7 +468,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 +1080,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 +1211,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..0503f66161 100644 --- a/modules/gdscript/gdscript_byte_codegen.h +++ b/modules/gdscript/gdscript_byte_codegen.h @@ -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..8c198345c2 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; 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 108c988add..37a988ee4c 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) { @@ -98,6 +99,7 @@ GDScriptDataType GDScriptCompiler::_gdtype_from_datatype(const GDScriptParser::D case GDScriptParser::DataType::NATIVE: { result.kind = GDScriptDataType::NATIVE; result.native_type = p_datatype.native_type; + result.builtin_type = p_datatype.builtin_type; } break; case GDScriptParser::DataType::SCRIPT: { result.kind = GDScriptDataType::SCRIPT; @@ -132,11 +134,13 @@ GDScriptDataType GDScriptCompiler::_gdtype_from_datatype(const GDScriptParser::D 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); result.script_type = result.script_type_ref.ptr(); result.native_type = p_datatype.native_type; + result.builtin_type = p_datatype.builtin_type; } } } break; @@ -291,16 +295,21 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code // Try signals and methods (can be made callables). { - if (codegen.class_node->members_indices.has(identifier)) { - const GDScriptParser::ClassNode::Member &member = codegen.class_node->members[codegen.class_node->members_indices[identifier]]; - if (member.type == GDScriptParser::ClassNode::Member::FUNCTION || member.type == GDScriptParser::ClassNode::Member::SIGNAL) { - // Get like it was a property. - GDScriptCodeGenerator::Address temp = codegen.add_temporary(); // TODO: Get type here. - GDScriptCodeGenerator::Address self(GDScriptCodeGenerator::Address::SELF); - - gen->write_get_named(temp, identifier, self); - return temp; + // Search upwards through parent classes: + const GDScriptParser::ClassNode *base_class = codegen.class_node; + while (base_class != nullptr) { + if (base_class->has_member(identifier)) { + const GDScriptParser::ClassNode::Member &member = base_class->get_member(identifier); + if (member.type == GDScriptParser::ClassNode::Member::FUNCTION || member.type == GDScriptParser::ClassNode::Member::SIGNAL) { + // Get like it was a property. + GDScriptCodeGenerator::Address temp = codegen.add_temporary(); // TODO: Get type here. + GDScriptCodeGenerator::Address self(GDScriptCodeGenerator::Address::SELF); + + gen->write_get_named(temp, identifier, self); + return temp; + } } + base_class = base_class->base_type.class_type; } // Try in native base. @@ -347,7 +356,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); @@ -567,6 +576,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) { @@ -1184,7 +1197,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) { 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 f0dc830ed8..0197bf9ea3 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_DEF("text_editor/completion/add_type_hints", false)) { + 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,11 +88,13 @@ 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; } @@ -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, Map<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, Map<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(Map<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, Map<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,7 +846,7 @@ 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); } @@ -757,19 +857,19 @@ static void _list_available_types(bool p_inherit_only, GDScriptParser::Completio 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, Map<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 +878,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, Map<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, Map<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 +906,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 +915,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 +944,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 +953,7 @@ static void _find_identifiers_in_class(const GDScriptParser::ClassNode *p_class, } outer = true; clss = clss->outer; + classes_processed++; } } @@ -862,14 +965,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, Map<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 +992,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; 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 +1020,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 +1051,8 @@ 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); } @@ -958,29 +1066,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 +1113,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 +1122,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 +1140,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, Map<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 +1155,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 +1168,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 +1178,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 +1191,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 +1204,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,7 +1214,7 @@ 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); } @@ -1122,17 +1224,17 @@ static void _find_identifiers(GDScriptParser::CompletionContext &p_context, bool 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); } @@ -1198,6 +1300,27 @@ static bool _guess_identifier_type(GDScriptParser::CompletionContext &p_context, static bool _guess_identifier_type_from_base(GDScriptParser::CompletionContext &p_context, const GDScriptCompletionIdentifier &p_base, const StringName &p_identifier, GDScriptCompletionIdentifier &r_type); static bool _guess_method_return_type_from_base(GDScriptParser::CompletionContext &p_context, const GDScriptCompletionIdentifier &p_base, const StringName &p_method, GDScriptCompletionIdentifier &r_type); +static bool _is_expression_named_identifier(const GDScriptParser::ExpressionNode *p_expression, const StringName &p_name) { + if (p_expression) { + switch (p_expression->type) { + case GDScriptParser::Node::IDENTIFIER: { + const GDScriptParser::IdentifierNode *id = static_cast<const GDScriptParser::IdentifierNode *>(p_expression); + if (id->name == p_name) { + return true; + } + } break; + case GDScriptParser::Node::CAST: { + const GDScriptParser::CastNode *cn = static_cast<const GDScriptParser::CastNode *>(p_expression); + return _is_expression_named_identifier(cn->operand, p_name); + } break; + default: + break; + } + } + + return false; +} + static bool _guess_expression_type(GDScriptParser::CompletionContext &p_context, const GDScriptParser::ExpressionNode *p_expression, GDScriptCompletionIdentifier &r_type) { bool found = false; @@ -1904,6 +2027,14 @@ static bool _guess_identifier_type_from_base(GDScriptParser::CompletionContext & return true; } else if (init->start_line == p_context.current_line) { return false; + // Detects if variable is assigned to itself + } else if (_is_expression_named_identifier(init, member.variable->identifier->name)) { + if (member.variable->initializer->get_datatype().is_set()) { + r_type.type = member.variable->initializer->get_datatype(); + } else if (member.variable->get_datatype().is_set() && !member.variable->get_datatype().is_variant()) { + r_type.type = member.variable->get_datatype(); + } + return true; } else if (_guess_expression_type(p_context, init, r_type)) { return true; } else if (init->get_datatype().is_set() && !init->get_datatype().is_variant()) { @@ -2181,20 +2312,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, Map<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); } } @@ -2211,13 +2342,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, Map<String, ScriptLanguage::CodeCompletionOption> &r_result, String &r_arghint) { Variant base = p_base.value; GDScriptParser::DataType base_type = p_base.type; @@ -2258,7 +2390,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); } } @@ -2285,7 +2417,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); } @@ -2301,7 +2433,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); } @@ -2336,7 +2468,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, Map<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); @@ -2366,7 +2498,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); @@ -2392,6 +2524,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)) { @@ -2415,7 +2573,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; @@ -2425,7 +2583,7 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c analyzer.analyze(); r_forced = false; - Map<String, ScriptCodeCompletionOption> options; + Map<String, ScriptLanguage::CodeCompletionOption> options; GDScriptParser::CompletionContext completion_context = parser.get_completion_context(); completion_context.base = p_owner; @@ -2438,7 +2596,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 += "("; } @@ -2454,17 +2612,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: { @@ -2472,7 +2649,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]]; @@ -2482,16 +2659,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; @@ -2507,7 +2684,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; @@ -2677,7 +2854,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; @@ -2700,7 +2877,7 @@ 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); } @@ -2709,7 +2886,7 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c 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); + ScriptLanguage::CodeCompletionOption option(path.quote(quote_style), ScriptLanguage::CODE_COMPLETION_KIND_NODE_PATH); options.insert(option.display, option); } } @@ -2722,7 +2899,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); } @@ -2731,7 +2908,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; } @@ -2742,10 +2919,10 @@ Error GDScriptLanguage::complete_code(const String &p_code, const String &p_path String GDScriptLanguage::_get_indentation() const { #ifdef TOOLS_ENABLED if (Engine::get_singleton()->is_editor_hint()) { - bool use_space_indentation = EDITOR_DEF("text_editor/behavior/indent/type", false); + bool use_space_indentation = EDITOR_GET("text_editor/behavior/indent/type"); if (use_space_indentation) { - int indent_size = EDITOR_DEF("text_editor/behavior/indent/size", 4); + int indent_size = EDITOR_GET("text_editor/behavior/indent/size"); String space_indent = ""; for (int i = 0; i < indent_size; i++) { @@ -2830,7 +3007,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); @@ -2844,7 +3021,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; @@ -2868,7 +3045,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; @@ -2878,7 +3055,7 @@ 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; @@ -2887,7 +3064,7 @@ static Error _lookup_symbol_from_base(const GDScriptParser::DataType &p_base, co 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; @@ -2897,7 +3074,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; @@ -2905,7 +3082,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; @@ -2922,14 +3099,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; @@ -2942,7 +3119,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; @@ -2951,7 +3128,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; @@ -2969,7 +3146,7 @@ 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 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; } @@ -2977,21 +3154,21 @@ 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; + 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; @@ -3008,7 +3185,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; @@ -3018,11 +3195,21 @@ 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: { @@ -3047,7 +3234,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; } @@ -3072,7 +3259,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; @@ -3088,10 +3275,10 @@ static Error _lookup_symbol_from_base(const GDScriptParser::DataType &p_base, co 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(); } @@ -3108,18 +3295,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; @@ -3128,7 +3315,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 9424de9d22..3d708955ed 100644 --- a/modules/gdscript/gdscript_function.cpp +++ b/modules/gdscript/gdscript_function.cpp @@ -95,7 +95,7 @@ void GDScriptFunction::debug_get_stack_member_state(int p_line, List<Pair<String int oc = 0; Map<StringName, _GDFKC> sdmap; for (const StackDebug &sd : stack_debug) { - if (sd.line > p_line) { + if (sd.line >= p_line) { break; } diff --git a/modules/gdscript/gdscript_function.h b/modules/gdscript/gdscript_function.h index db663ca48f..ba0d51c5cc 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, @@ -502,6 +504,8 @@ private: List<StackDebug> stack_debug; + Variant _get_default_variant_for_data_type(const GDScriptDataType &p_data_type); + _FORCE_INLINE_ Variant *_get_variant(int p_address, GDScriptInstance *p_instance, Variant *p_stack, String &r_error) const; _FORCE_INLINE_ String _get_call_error(const Callable::CallError &p_err, const String &p_where, const Variant **argptrs) const; 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 8e4e457ec1..67f6b61f14 100644 --- a/modules/gdscript/gdscript_parser.cpp +++ b/modules/gdscript/gdscript_parser.cpp @@ -1380,6 +1380,7 @@ GDScriptParser::AnnotationNode *GDScriptParser::parse_annotation(uint32_t p_vali push_completion_call(annotation); make_completion_context(COMPLETION_ANNOTATION_ARGUMENTS, annotation, 0, true); if (!check(GDScriptTokenizer::Token::PARENTHESIS_CLOSE) && !is_at_end()) { + push_multiline(true); int argument_index = 0; do { make_completion_context(COMPLETION_ANNOTATION_ARGUMENTS, annotation, argument_index, true); @@ -1391,6 +1392,7 @@ GDScriptParser::AnnotationNode *GDScriptParser::parse_annotation(uint32_t p_vali } annotation->arguments.push_back(argument); } while (match(GDScriptTokenizer::Token::COMMA)); + pop_multiline(); consume(GDScriptTokenizer::Token::PARENTHESIS_CLOSE, R"*(Expected ")" after annotation arguments.)*"); } @@ -2198,9 +2200,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; @@ -2683,7 +2682,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; } } @@ -2692,12 +2691,13 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_attribute(ExpressionNode * } } - attribute->is_attribute = true; attribute->base = p_previous_operand; if (!consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected identifier after "." for attribute access.)")) { return attribute; } + + attribute->is_attribute = true; attribute->attribute = parse_identifier(); return attribute; @@ -2749,7 +2749,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); @@ -3479,6 +3483,15 @@ template <PropertyHint t_hint, Variant::Type t_type> bool GDScriptParser::export_annotations(const AnnotationNode *p_annotation, Node *p_node) { ERR_FAIL_COND_V_MSG(p_node->type != Node::VARIABLE, false, vformat(R"("%s" annotation can only be applied to variables.)", p_annotation->name)); + { + const int max_flags = 32; + + if (t_hint == PropertyHint::PROPERTY_HINT_FLAGS && p_annotation->resolved_arguments.size() > max_flags) { + push_error(vformat(R"(The argument count limit for "@export_flags" is exceeded (%d/%d).)", p_annotation->resolved_arguments.size(), max_flags), p_annotation); + return false; + } + } + VariableNode *variable = static_cast<VariableNode *>(p_node); if (variable->exported) { push_error(vformat(R"(Annotation "%s" cannot be used with another "@export" annotation.)", p_annotation->name), p_annotation); diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h index c09b07282f..10474db02f 100644 --- a/modules/gdscript/gdscript_parser.h +++ b/modules/gdscript/gdscript_parser.h @@ -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: @@ -767,6 +767,7 @@ public: LOCAL_BIND, // Pattern bind. MEMBER_VARIABLE, MEMBER_CONSTANT, + INHERITED_VARIABLE, }; Source source = UNDEFINED_SOURCE; @@ -800,6 +801,7 @@ public: FunctionNode *parent_function = nullptr; Vector<IdentifierNode *> captures; Map<StringName, int> captures_indices; + bool use_self = false; bool has_name() const { return function && function->identifier; @@ -1146,7 +1148,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. diff --git a/modules/gdscript/gdscript_rpc_callable.cpp b/modules/gdscript/gdscript_rpc_callable.cpp index 07e5ed4171..07ef5aefcb 100644 --- a/modules/gdscript/gdscript_rpc_callable.cpp +++ b/modules/gdscript/gdscript_rpc_callable.cpp @@ -64,7 +64,7 @@ ObjectID GDScriptRPCCallable::get_object() const { } void GDScriptRPCCallable::call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const { - r_return_value = object->call(method, p_arguments, p_argcount, r_call_error); + r_return_value = object->callp(method, p_arguments, p_argcount, r_call_error); } GDScriptRPCCallable::GDScriptRPCCallable(Object *p_object, const StringName &p_method) { diff --git a/modules/gdscript/gdscript_tokenizer.cpp b/modules/gdscript/gdscript_tokenizer.cpp index d3287ab345..63fad0d9bb 100644 --- a/modules/gdscript/gdscript_tokenizer.cpp +++ b/modules/gdscript/gdscript_tokenizer.cpp @@ -1493,7 +1493,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_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 95122714f9..e28dd26c28 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()) + ")"; + } } } @@ -123,6 +127,34 @@ static String _get_var_type(const Variant *p_var) { } #endif // DEBUG_ENABLED +Variant GDScriptFunction::_get_default_variant_for_data_type(const GDScriptDataType &p_data_type) { + if (p_data_type.kind == GDScriptDataType::BUILTIN) { + if (p_data_type.builtin_type == Variant::ARRAY) { + Array array; + // Typed array. + if (p_data_type.has_container_element_type()) { + const GDScriptDataType &element_type = p_data_type.get_container_element_type(); + array.set_typed( + element_type.kind == GDScriptDataType::BUILTIN ? element_type.builtin_type : Variant::OBJECT, + element_type.native_type, + element_type.script_type); + } + + return array; + } else { + Callable::CallError ce; + Variant variant; + Variant::construct(p_data_type.builtin_type, variant, nullptr, 0, ce); + + ERR_FAIL_COND_V(ce.error != Callable::CallError::CALL_OK, Variant()); + + return variant; + } + } + + return Variant(); +} + String GDScriptFunction::_get_call_error(const Callable::CallError &p_err, const String &p_where, const Variant **argptrs) const { String err_text; @@ -235,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, \ @@ -273,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, \ @@ -339,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, \ @@ -428,7 +462,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a OPCODES_TABLE; if (!_code_ptr) { - return Variant(); + return _get_default_variant_for_data_type(return_type); } r_err.error = Callable::CallError::CALL_OK; @@ -467,11 +501,11 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a r_err.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS; r_err.argument = _argument_count; - return Variant(); + return _get_default_variant_for_data_type(return_type); } else if (p_argcount < _argument_count - _default_arg_count) { r_err.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; r_err.argument = _argument_count - _default_arg_count; - return Variant(); + return _get_default_variant_for_data_type(return_type); } else { defarg = _argument_count - p_argcount; } @@ -498,7 +532,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a r_err.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; r_err.argument = i; r_err.expected = argument_types[i].kind == GDScriptDataType::BUILTIN ? argument_types[i].builtin_type : Variant::OBJECT; - return Variant(); + return _get_default_variant_for_data_type(return_type); } if (argument_types[i].kind == GDScriptDataType::BUILTIN) { Variant arg; @@ -1419,7 +1453,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a const StringName native_type = _global_names_ptr[native_type_idx]; Array array; - array.set_typed(builtin_type, native_type, script_type); + array.set_typed(builtin_type, native_type, *script_type); array.resize(argc); for (int i = 0; i < argc; i++) { @@ -1489,7 +1523,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a Callable::CallError err; if (call_ret) { GET_INSTRUCTION_ARG(ret, argc + 1); - base->call(*methodname, (const Variant **)argptrs, argc, *ret, err); + base->callp(*methodname, (const Variant **)argptrs, argc, *ret, err); #ifdef DEBUG_ENABLED if (!call_async && ret->get_type() == Variant::OBJECT) { // Check if getting a function state without await. @@ -1508,7 +1542,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a #endif } else { Variant ret; - base->call(*methodname, (const Variant **)argptrs, argc, ret, err); + base->callp(*methodname, (const Variant **)argptrs, argc, ret, err); } #ifdef DEBUG_ENABLED if (GDScriptLanguage::get_singleton()->profiling) { @@ -1682,6 +1716,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) { \ @@ -2203,6 +2278,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]; @@ -2312,7 +2422,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a } Array array; - array.set_typed(builtin_type, native_type, script_type); + array.set_typed(builtin_type, native_type, *script_type); #ifdef DEBUG_ENABLED bool valid = array.typed_assign(*VariantInternal::get_array(r)); @@ -2782,7 +2892,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a args[0] = &vref; Callable::CallError ce; - Variant has_next = obj->call(CoreStringNames::get_singleton()->_iter_init, (const Variant **)args, 1, ce); + Variant has_next = obj->callp(CoreStringNames::get_singleton()->_iter_init, (const Variant **)args, 1, ce); #ifdef DEBUG_ENABLED if (ce.error != Callable::CallError::CALL_OK) { @@ -2796,7 +2906,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a ip = jumpto; } else { GET_INSTRUCTION_ARG(iterator, 2); - *iterator = obj->call(CoreStringNames::get_singleton()->_iter_get, (const Variant **)args, 1, ce); + *iterator = obj->callp(CoreStringNames::get_singleton()->_iter_get, (const Variant **)args, 1, ce); #ifdef DEBUG_ENABLED if (ce.error != Callable::CallError::CALL_OK) { err_text = vformat(R"(There was an error calling "_iter_get" on iterator object of type %s.)", *container); @@ -3113,7 +3223,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a args[0] = &vref; Callable::CallError ce; - Variant has_next = obj->call(CoreStringNames::get_singleton()->_iter_next, (const Variant **)args, 1, ce); + Variant has_next = obj->callp(CoreStringNames::get_singleton()->_iter_next, (const Variant **)args, 1, ce); #ifdef DEBUG_ENABLED if (ce.error != Callable::CallError::CALL_OK) { @@ -3127,7 +3237,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a ip = jumpto; } else { GET_INSTRUCTION_ARG(iterator, 2); - *iterator = obj->call(CoreStringNames::get_singleton()->_iter_get, (const Variant **)args, 1, ce); + *iterator = obj->callp(CoreStringNames::get_singleton()->_iter_get, (const Variant **)args, 1, ce); #ifdef DEBUG_ENABLED if (ce.error != Callable::CallError::CALL_OK) { err_text = vformat(R"(There was an error calling "_iter_get" on iterator object of type %s.)", *container); @@ -3189,7 +3299,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); @@ -3324,6 +3434,9 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a } #endif + // Get a default return type in case of failure + retvalue = _get_default_variant_for_data_type(return_type); + OPCODE_OUT; } diff --git a/modules/gdscript/language_server/gdscript_extend_parser.cpp b/modules/gdscript/language_server/gdscript_extend_parser.cpp index 17886181d5..5516f59fe9 100644 --- a/modules/gdscript/language_server/gdscript_extend_parser.cpp +++ b/modules/gdscript/language_server/gdscript_extend_parser.cpp @@ -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,7 +212,7 @@ 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()) { diff --git a/modules/gdscript/language_server/gdscript_text_document.cpp b/modules/gdscript/language_server/gdscript_text_document.cpp index 961295b076..c42bd58aeb 100644 --- a/modules/gdscript/language_server/gdscript_text_document.cpp +++ b/modules/gdscript/language_server/gdscript_text_document.cpp @@ -166,49 +166,51 @@ 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; 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(); @@ -422,10 +424,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..89ee6b35e5 100644 --- a/modules/gdscript/language_server/gdscript_workspace.cpp +++ b/modules/gdscript/language_server/gdscript_workspace.cpp @@ -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(); @@ -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(); diff --git a/modules/gdscript/language_server/gdscript_workspace.h b/modules/gdscript/language_server/gdscript_workspace.h index ce5bba5f00..92e78f8992 100644 --- a/modules/gdscript/language_server/gdscript_workspace.h +++ b/modules/gdscript/language_server/gdscript_workspace.h @@ -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/register_types.cpp b/modules/gdscript/register_types.cpp index fcf122f567..59acb1c064 100644 --- a/modules/gdscript/register_types.cpp +++ b/modules/gdscript/register_types.cpp @@ -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 c2bb2caa29..ea51990237 100644 --- a/modules/gdscript/tests/gdscript_test_runner.cpp +++ b/modules/gdscript/tests/gdscript_test_runner.cpp @@ -70,7 +70,7 @@ void init_autoloads() { 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."); @@ -573,7 +573,7 @@ GDScriptTest::TestResult GDScriptTest::execute_test_code(bool p_is_generating) { // Call test function. Callable::CallError call_err; - instance->call(GDScriptTestRunner::test_function_name, nullptr, 0, call_err); + instance->callp(GDScriptTestRunner::test_function_name, nullptr, 0, call_err); // Tear down output handlers. remove_print_handler(&_print_handler); @@ -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/function_dont_match_parent_signature_parameter_count_less.gd b/modules/gdscript/tests/scripts/analyzer/errors/function_dont_match_parent_signature_parameter_count_less.gd new file mode 100644 index 0000000000..435711fcaf --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/function_dont_match_parent_signature_parameter_count_less.gd @@ -0,0 +1,10 @@ +func test(): + print("Shouldn't reach this") + +class Parent: + func my_function(_par1: int) -> int: + return 0 + +class Child extends Parent: + func my_function() -> int: + return 0 diff --git a/modules/gdscript/tests/scripts/analyzer/errors/function_dont_match_parent_signature_parameter_count_less.out b/modules/gdscript/tests/scripts/analyzer/errors/function_dont_match_parent_signature_parameter_count_less.out new file mode 100644 index 0000000000..3baeb17066 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/function_dont_match_parent_signature_parameter_count_less.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +The function signature doesn't match the parent. Parent signature is "int my_function(int)". diff --git a/modules/gdscript/tests/scripts/analyzer/errors/function_dont_match_parent_signature_parameter_count_more.gd b/modules/gdscript/tests/scripts/analyzer/errors/function_dont_match_parent_signature_parameter_count_more.gd new file mode 100644 index 0000000000..2bd392e8f8 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/function_dont_match_parent_signature_parameter_count_more.gd @@ -0,0 +1,10 @@ +func test(): + print("Shouldn't reach this") + +class Parent: + func my_function(_par1: int) -> int: + return 0 + +class Child extends Parent: + func my_function(_pary1: int, _par2: int) -> int: + return 0 diff --git a/modules/gdscript/tests/scripts/analyzer/errors/function_dont_match_parent_signature_parameter_count_more.out b/modules/gdscript/tests/scripts/analyzer/errors/function_dont_match_parent_signature_parameter_count_more.out new file mode 100644 index 0000000000..3baeb17066 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/function_dont_match_parent_signature_parameter_count_more.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +The function signature doesn't match the parent. Parent signature is "int my_function(int)". diff --git a/modules/gdscript/tests/scripts/analyzer/errors/function_dont_match_parent_signature_parameter_default_values.gd b/modules/gdscript/tests/scripts/analyzer/errors/function_dont_match_parent_signature_parameter_default_values.gd new file mode 100644 index 0000000000..49ec82ce2d --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/function_dont_match_parent_signature_parameter_default_values.gd @@ -0,0 +1,10 @@ +func test(): + print("Shouldn't reach this") + +class Parent: + func my_function(_par1: int = 0) -> int: + return 0 + +class Child extends Parent: + func my_function(_par1: int) -> int: + return 0 diff --git a/modules/gdscript/tests/scripts/analyzer/errors/function_dont_match_parent_signature_parameter_default_values.out b/modules/gdscript/tests/scripts/analyzer/errors/function_dont_match_parent_signature_parameter_default_values.out new file mode 100644 index 0000000000..665c229339 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/function_dont_match_parent_signature_parameter_default_values.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +The function signature doesn't match the parent. Parent signature is "int my_function(int = default)". diff --git a/modules/gdscript/tests/scripts/analyzer/errors/function_dont_match_parent_signature_parameter_type.gd b/modules/gdscript/tests/scripts/analyzer/errors/function_dont_match_parent_signature_parameter_type.gd new file mode 100644 index 0000000000..4a17a7831f --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/function_dont_match_parent_signature_parameter_type.gd @@ -0,0 +1,10 @@ +func test(): + print("Shouldn't reach this") + +class Parent: + func my_function(_par1: int) -> int: + return 0 + +class Child extends Parent: + func my_function(_par1: Vector2) -> int: + return 0 diff --git a/modules/gdscript/tests/scripts/analyzer/errors/function_dont_match_parent_signature_parameter_type.out b/modules/gdscript/tests/scripts/analyzer/errors/function_dont_match_parent_signature_parameter_type.out new file mode 100644 index 0000000000..3baeb17066 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/function_dont_match_parent_signature_parameter_type.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +The function signature doesn't match the parent. Parent signature is "int my_function(int)". diff --git a/modules/gdscript/tests/scripts/analyzer/errors/function_dont_match_parent_signature_return_type.gd b/modules/gdscript/tests/scripts/analyzer/errors/function_dont_match_parent_signature_return_type.gd new file mode 100644 index 0000000000..b205ec96ef --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/function_dont_match_parent_signature_return_type.gd @@ -0,0 +1,10 @@ +func test(): + print("Shouldn't reach this") + +class Parent: + func my_function() -> int: + return 0 + +class Child extends Parent: + func my_function() -> Vector2: + return Vector2() diff --git a/modules/gdscript/tests/scripts/analyzer/errors/function_dont_match_parent_signature_return_type.out b/modules/gdscript/tests/scripts/analyzer/errors/function_dont_match_parent_signature_return_type.out new file mode 100644 index 0000000000..5b22739a93 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/function_dont_match_parent_signature_return_type.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +The function signature doesn't match the parent. Parent signature is "int my_function()". 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/function_match_parent_signature_with_extra_parameters.gd b/modules/gdscript/tests/scripts/analyzer/features/function_match_parent_signature_with_extra_parameters.gd new file mode 100644 index 0000000000..d678f3acfc --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/function_match_parent_signature_with_extra_parameters.gd @@ -0,0 +1,17 @@ +func test(): + var instance := Parent.new() + var result := instance.my_function(1) + print(result) + assert(result == 1) + instance = Child.new() + result = instance.my_function(2) + print(result) + assert(result == 0) + +class Parent: + func my_function(par1: int) -> int: + return par1 + +class Child extends Parent: + func my_function(_par1: int, par2: int = 0) -> int: + return par2 diff --git a/modules/gdscript/tests/scripts/analyzer/features/function_match_parent_signature_with_extra_parameters.out b/modules/gdscript/tests/scripts/analyzer/features/function_match_parent_signature_with_extra_parameters.out new file mode 100644 index 0000000000..fc5315a501 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/function_match_parent_signature_with_extra_parameters.out @@ -0,0 +1,3 @@ +GDTEST_OK +1 +0 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/class_inheritance_access.gd b/modules/gdscript/tests/scripts/parser/features/class_inheritance_access.gd new file mode 100644 index 0000000000..eb392672eb --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/features/class_inheritance_access.gd @@ -0,0 +1,40 @@ +# Test access visibility of parent elements in nested class architectures. +class Parent: + const parent_const := 1 + + var parent_variable := 2 + + signal parent_signal + + var parent_attribute: int: + get: + return 3 + + func parent_func(): + return 4 + + class Nested: + const nested_const := 5 + + +class Child extends Parent: + func child_test(): + print(parent_const) + print(self.parent_const) + print(parent_variable) + print(self.parent_variable) + print(parent_signal.get_name()) + print(self.parent_signal.get_name()) + print(parent_attribute) + print(self.parent_attribute) + print(parent_func.get_method()) + print(self.parent_func.get_method()) + print(parent_func()) + print(self.parent_func()) + print(Nested.nested_const) + print(self.Nested.nested_const) + print(Parent.Nested.nested_const) + + +func test(): + Child.new().child_test() diff --git a/modules/gdscript/tests/scripts/parser/features/class_inheritance_access.out b/modules/gdscript/tests/scripts/parser/features/class_inheritance_access.out new file mode 100644 index 0000000000..09e87bccfa --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/features/class_inheritance_access.out @@ -0,0 +1,16 @@ +GDTEST_OK +1 +1 +2 +2 +parent_signal +parent_signal +3 +3 +parent_func +parent_func +4 +4 +5 +5 +5 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/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/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()); |