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