summaryrefslogtreecommitdiff
path: root/modules/gdscript
diff options
context:
space:
mode:
Diffstat (limited to 'modules/gdscript')
-rw-r--r--modules/gdscript/doc_classes/@GDScript.xml106
-rw-r--r--modules/gdscript/doc_classes/GDScript.xml6
-rw-r--r--modules/gdscript/editor/gdscript_highlighter.cpp65
-rw-r--r--modules/gdscript/editor/gdscript_highlighter.h2
-rw-r--r--modules/gdscript/editor/gdscript_translation_parser_plugin.h1
-rw-r--r--modules/gdscript/gdscript.cpp268
-rw-r--r--modules/gdscript/gdscript.h52
-rw-r--r--modules/gdscript/gdscript_analyzer.cpp211
-rw-r--r--modules/gdscript/gdscript_analyzer.h4
-rw-r--r--modules/gdscript/gdscript_byte_codegen.cpp221
-rw-r--r--modules/gdscript/gdscript_byte_codegen.h19
-rw-r--r--modules/gdscript/gdscript_cache.cpp10
-rw-r--r--modules/gdscript/gdscript_cache.h4
-rw-r--r--modules/gdscript/gdscript_codegen.h5
-rw-r--r--modules/gdscript/gdscript_compiler.cpp194
-rw-r--r--modules/gdscript/gdscript_compiler.h2
-rw-r--r--modules/gdscript/gdscript_disassembler.cpp53
-rw-r--r--modules/gdscript/gdscript_editor.cpp216
-rw-r--r--modules/gdscript/gdscript_function.cpp17
-rw-r--r--modules/gdscript/gdscript_function.h23
-rw-r--r--modules/gdscript/gdscript_lambda_callable.cpp95
-rw-r--r--modules/gdscript/gdscript_lambda_callable.h65
-rw-r--r--modules/gdscript/gdscript_parser.cpp362
-rw-r--r--modules/gdscript/gdscript_parser.h61
-rw-r--r--modules/gdscript/gdscript_tokenizer.cpp14
-rw-r--r--modules/gdscript/gdscript_tokenizer.h3
-rw-r--r--modules/gdscript/gdscript_utility_functions.cpp4
-rw-r--r--modules/gdscript/gdscript_vm.cpp154
-rw-r--r--modules/gdscript/language_server/gdscript_extend_parser.cpp122
-rw-r--r--modules/gdscript/language_server/gdscript_language_protocol.cpp18
-rw-r--r--modules/gdscript/language_server/gdscript_language_protocol.h18
-rw-r--r--modules/gdscript/language_server/gdscript_language_server.cpp10
-rw-r--r--modules/gdscript/language_server/gdscript_text_document.cpp57
-rw-r--r--modules/gdscript/language_server/gdscript_text_document.h11
-rw-r--r--modules/gdscript/language_server/gdscript_workspace.cpp154
-rw-r--r--modules/gdscript/language_server/gdscript_workspace.h8
-rw-r--r--modules/gdscript/language_server/lsp.hpp230
-rw-r--r--modules/gdscript/register_types.cpp16
-rw-r--r--modules/gdscript/tests/gdscript_test_runner.cpp37
-rw-r--r--modules/gdscript/tests/gdscript_test_runner_suite.h21
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/call_self_get_name.gd9
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/call_self_get_name.out3
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/missing_argument.gd (renamed from modules/gdscript/tests/scripts/parser-errors/missing-argument.gd)0
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/missing_argument.out (renamed from modules/gdscript/tests/scripts/parser-errors/missing-argument.out)0
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/missing_closing_expr_paren.gd (renamed from modules/gdscript/tests/scripts/parser-errors/missing-closing-expr-paren.gd)0
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/missing_closing_expr_paren.out (renamed from modules/gdscript/tests/scripts/parser-errors/missing-closing-expr-paren.out)0
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/missing_colon.gd (renamed from modules/gdscript/tests/scripts/parser-errors/missing-colon.gd)0
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/missing_colon.out (renamed from modules/gdscript/tests/scripts/parser-errors/missing-colon.out)0
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/missing_paren_after_args.gd (renamed from modules/gdscript/tests/scripts/parser-errors/missing-paren-after-args.gd)0
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/missing_paren_after_args.out (renamed from modules/gdscript/tests/scripts/parser-errors/missing-paren-after-args.out)0
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/mixing_tabs_spaces.gd (renamed from modules/gdscript/tests/scripts/parser-errors/mixing-tabs-spaces.gd)0
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/mixing_tabs_spaces.out (renamed from modules/gdscript/tests/scripts/parser-errors/mixing-tabs-spaces.out)0
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/nothing_after_dollar.gd (renamed from modules/gdscript/tests/scripts/parser-errors/nothing-after-dollar.gd)0
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/nothing_after_dollar.out (renamed from modules/gdscript/tests/scripts/parser-errors/nothing-after-dollar.out)0
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/wrong_value_after_dollar.gd (renamed from modules/gdscript/tests/scripts/parser-errors/wrong-value-after-dollar.gd)0
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/wrong_value_after_dollar.out (renamed from modules/gdscript/tests/scripts/parser-errors/wrong-value-after-dollar.out)0
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/wrong_value_after_dollar_slash.gd (renamed from modules/gdscript/tests/scripts/parser-errors/wrong-value-after-dollar-slash.gd)0
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/wrong_value_after_dollar_slash.out (renamed from modules/gdscript/tests/scripts/parser-errors/wrong-value-after-dollar-slash.out)0
-rw-r--r--modules/gdscript/tests/scripts/parser/features/semicolon_as_end_statement.gd (renamed from modules/gdscript/tests/scripts/parser-features/semicolon-as-end-statement.gd)0
-rw-r--r--modules/gdscript/tests/scripts/parser/features/semicolon_as_end_statement.out (renamed from modules/gdscript/tests/scripts/parser-features/semicolon-as-end-statement.out)0
-rw-r--r--modules/gdscript/tests/scripts/parser/features/trailing_comma_in_function_args.gd (renamed from modules/gdscript/tests/scripts/parser-features/trailing-comma-in-function-args.gd)0
-rw-r--r--modules/gdscript/tests/scripts/parser/features/trailing_comma_in_function_args.out (renamed from modules/gdscript/tests/scripts/parser-features/trailing-comma-in-function-args.out)0
-rw-r--r--modules/gdscript/tests/scripts/parser/features/variable_declaration.gd (renamed from modules/gdscript/tests/scripts/parser-features/variable-declaration.gd)0
-rw-r--r--modules/gdscript/tests/scripts/parser/features/variable_declaration.out (renamed from modules/gdscript/tests/scripts/parser-features/variable-declaration.out)0
-rw-r--r--modules/gdscript/tests/scripts/parser/warnings/unused_variable.gd (renamed from modules/gdscript/tests/scripts/parser-warnings/unused-variable.gd)0
-rw-r--r--modules/gdscript/tests/scripts/parser/warnings/unused_variable.out (renamed from modules/gdscript/tests/scripts/parser-warnings/unused-variable.out)0
-rw-r--r--modules/gdscript/tests/scripts/runtime/errors/callable_call_after_free_object.gd5
-rw-r--r--modules/gdscript/tests/scripts/runtime/errors/callable_call_after_free_object.out6
-rw-r--r--modules/gdscript/tests/test_gdscript.cpp30
69 files changed, 2072 insertions, 920 deletions
diff --git a/modules/gdscript/doc_classes/@GDScript.xml b/modules/gdscript/doc_classes/@GDScript.xml
index 9e974b6fdc..51b3452a3a 100644
--- a/modules/gdscript/doc_classes/@GDScript.xml
+++ b/modules/gdscript/doc_classes/@GDScript.xml
@@ -11,16 +11,11 @@
</tutorials>
<methods>
<method name="Color8">
- <return type="Color">
- </return>
- <argument index="0" name="r8" type="int">
- </argument>
- <argument index="1" name="g8" type="int">
- </argument>
- <argument index="2" name="b8" type="int">
- </argument>
- <argument index="3" name="a8" type="int" default="255">
- </argument>
+ <return type="Color" />
+ <argument index="0" name="r8" type="int" />
+ <argument index="1" name="g8" type="int" />
+ <argument index="2" name="b8" type="int" />
+ <argument index="3" name="a8" type="int" default="255" />
<description>
Returns a color constructed from integer red, green, blue, and alpha channels. Each channel should have 8 bits of information ranging from 0 to 255.
[code]r8[/code] red channel
@@ -33,12 +28,9 @@
</description>
</method>
<method name="assert">
- <return type="void">
- </return>
- <argument index="0" name="condition" type="bool">
- </argument>
- <argument index="1" name="message" type="String" default="&quot;&quot;">
- </argument>
+ <return type="void" />
+ <argument index="0" name="condition" type="bool" />
+ <argument index="1" name="message" type="String" default="&quot;&quot;" />
<description>
Asserts that the [code]condition[/code] is [code]true[/code]. If the [code]condition[/code] is [code]false[/code], an error is generated. When running from the editor, the running project will also be paused until you resume it. This can be used as a stronger form of [method @GlobalScope.push_error] for reporting errors to project developers or add-on users.
[b]Note:[/b] For performance reasons, the code inside [method assert] is only executed in debug builds or when running the project from the editor. Don't include code that has side effects in an [method assert] call. Otherwise, the project will behave differently when exported in release mode.
@@ -54,10 +46,8 @@
</description>
</method>
<method name="char">
- <return type="String">
- </return>
- <argument index="0" name="char" type="int">
- </argument>
+ <return type="String" />
+ <argument index="0" name="char" type="int" />
<description>
Returns a character as a String of the given Unicode code point (which is compatible with ASCII code).
[codeblock]
@@ -68,12 +58,9 @@
</description>
</method>
<method name="convert">
- <return type="Variant">
- </return>
- <argument index="0" name="what" type="Variant">
- </argument>
- <argument index="1" name="type" type="int">
- </argument>
+ <return type="Variant" />
+ <argument index="0" name="what" type="Variant" />
+ <argument index="1" name="type" type="int" />
<description>
Converts from a type to another in the best way possible. The [code]type[/code] parameter uses the [enum Variant.Type] values.
[codeblock]
@@ -87,17 +74,14 @@
</description>
</method>
<method name="dict2inst">
- <return type="Object">
- </return>
- <argument index="0" name="dictionary" type="Dictionary">
- </argument>
+ <return type="Object" />
+ <argument index="0" name="dictionary" type="Dictionary" />
<description>
Converts a dictionary (previously created with [method inst2dict]) back to an instance. Useful for deserializing.
</description>
</method>
<method name="get_stack">
- <return type="Array">
- </return>
+ <return type="Array" />
<description>
Returns an array of dictionaries representing the current call stack.
[codeblock]
@@ -117,10 +101,8 @@
</description>
</method>
<method name="inst2dict">
- <return type="Dictionary">
- </return>
- <argument index="0" name="instance" type="Object">
- </argument>
+ <return type="Dictionary" />
+ <argument index="0" name="instance" type="Object" />
<description>
Returns the passed instance converted to a dictionary (useful for serializing).
[codeblock]
@@ -138,10 +120,8 @@
</description>
</method>
<method name="len">
- <return type="int">
- </return>
- <argument index="0" name="var" type="Variant">
- </argument>
+ <return type="int" />
+ <argument index="0" name="var" type="Variant" />
<description>
Returns length of Variant [code]var[/code]. Length is the character count of String, element count of Array, size of Dictionary, etc.
[b]Note:[/b] Generates a fatal error if Variant can not provide a length.
@@ -152,10 +132,8 @@
</description>
</method>
<method name="load">
- <return type="Resource">
- </return>
- <argument index="0" name="path" type="String">
- </argument>
+ <return type="Resource" />
+ <argument index="0" name="path" type="String" />
<description>
Loads a resource from the filesystem located at [code]path[/code]. The resource is loaded on the method call (unless it's referenced already elsewhere, e.g. in another script or in the scene), which might cause slight delay, especially when loading scenes. To avoid unnecessary delays when loading something multiple times, either store the resource in a variable or use [method preload].
[b]Note:[/b] Resource paths can be obtained by right-clicking on a resource in the FileSystem dock and choosing "Copy Path" or by dragging the file from the FileSystem dock into the script.
@@ -168,29 +146,25 @@
</description>
</method>
<method name="preload">
- <return type="Resource">
- </return>
- <argument index="0" name="path" type="String">
- </argument>
+ <return type="Resource" />
+ <argument index="0" name="path" type="String" />
<description>
Returns a [Resource] from the filesystem located at [code]path[/code]. The resource is loaded during script parsing, i.e. is loaded with the script and [method preload] effectively acts as a reference to that resource. Note that the method requires a constant path. If you want to load a resource from a dynamic/variable path, use [method load].
[b]Note:[/b] Resource paths can be obtained by right clicking on a resource in the Assets Panel and choosing "Copy Path" or by dragging the file from the FileSystem dock into the script.
[codeblock]
# Instance a scene.
- var diamond = preload("res://diamond.tscn").instance()
+ var diamond = preload("res://diamond.tscn").instantiate()
[/codeblock]
</description>
</method>
<method name="print_debug" qualifiers="vararg">
- <return type="void">
- </return>
+ <return type="void" />
<description>
Like [method @GlobalScope.print], but prints only when used in debug mode.
</description>
</method>
<method name="print_stack">
- <return type="void">
- </return>
+ <return type="void" />
<description>
Prints a stack track at code location, only works when running with debugger turned on.
Output in the console would look something like this:
@@ -200,8 +174,7 @@
</description>
</method>
<method name="range" qualifiers="vararg">
- <return type="Array">
- </return>
+ <return type="Array" />
<description>
Returns an array with the given range. Range can be 1 argument N (0 to N-1), two arguments (initial, final-1) or three arguments (initial, final-1, increment).
[codeblock]
@@ -218,8 +191,7 @@
</description>
</method>
<method name="str" qualifiers="vararg">
- <return type="String">
- </return>
+ <return type="String" />
<description>
Converts one or more arguments to string in the best way possible.
[codeblock]
@@ -231,26 +203,26 @@
</description>
</method>
<method name="type_exists">
- <return type="bool">
- </return>
- <argument index="0" name="type" type="StringName">
- </argument>
+ <return type="bool" />
+ <argument index="0" name="type" type="StringName" />
<description>
</description>
</method>
</methods>
<constants>
- <constant name="PI" value="3.141593">
- Constant that represents how many times the diameter of a circle fits around its perimeter. This is equivalent to [code]TAU / 2[/code].
+ <constant name="PI" value="3.14159265358979">
+ Constant that represents how many times the diameter of a circle fits around its perimeter. This is equivalent to [code]TAU / 2[/code], or 180 degrees in rotations.
</constant>
- <constant name="TAU" value="6.283185">
- The circle constant, the circumference of the unit circle in radians.
+ <constant name="TAU" value="6.28318530717959">
+ The circle constant, the circumference of the unit circle in radians. This is equivalent to [code]PI * 2[/code], or 360 degrees in rotations.
</constant>
<constant name="INF" value="inf">
- Positive infinity. For negative infinity, use -INF.
+ Positive floating-point infinity. This is the result of floating-point division when the divisor is [code]0.0[/code]. For negative infinity, use [code]-INF[/code]. Dividing by [code]-0.0[/code] will result in negative infinity if the numerator is positive, so dividing by [code]0.0[/code] is not the same as dividing by [code]-0.0[/code] (despite [code]0.0 == -0.0[/code] returning [code]true[/code]).
+ [b]Note:[/b] Numeric infinity is only a concept with floating-point numbers, and has no equivalent for integers. Dividing an integer number by [code]0[/code] will not result in [constant INF] and will result in a run-time error instead.
</constant>
<constant name="NAN" value="nan">
- "Not a Number", an invalid value. [code]NaN[/code] has special properties, including that it is not equal to itself. It is output by some invalid operations, such as dividing zero by zero.
+ "Not a Number", an invalid floating-point value. [constant NAN] has special properties, including that it is not equal to itself ([code]NAN == NAN[/code] returns [code]false[/code]). It is output by some invalid operations, such as dividing floating-point [code]0.0[/code] by [code]0.0[/code].
+ [b]Note:[/b] "Not a Number" is only a concept with floating-point numbers, and has no equivalent for integers. Dividing an integer [code]0[/code] by [code]0[/code] will not result in [constant NAN] and will result in a run-time error instead.
</constant>
</constants>
</class>
diff --git a/modules/gdscript/doc_classes/GDScript.xml b/modules/gdscript/doc_classes/GDScript.xml
index 631a102130..72738f027a 100644
--- a/modules/gdscript/doc_classes/GDScript.xml
+++ b/modules/gdscript/doc_classes/GDScript.xml
@@ -12,15 +12,13 @@
</tutorials>
<methods>
<method name="get_as_byte_code" qualifiers="const">
- <return type="PackedByteArray">
- </return>
+ <return type="PackedByteArray" />
<description>
Returns byte code for the script source code.
</description>
</method>
<method name="new" qualifiers="vararg">
- <return type="Variant">
- </return>
+ <return type="Variant" />
<description>
Returns a new instance of the script.
For example:
diff --git a/modules/gdscript/editor/gdscript_highlighter.cpp b/modules/gdscript/editor/gdscript_highlighter.cpp
index ccc942d86b..ed8b0a4690 100644
--- a/modules/gdscript/editor/gdscript_highlighter.cpp
+++ b/modules/gdscript/editor/gdscript_highlighter.cpp
@@ -64,6 +64,7 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting(int p_line)
bool in_function_args = false;
bool in_member_variable = false;
bool in_node_path = false;
+ bool in_annotation = false;
bool is_hex_notation = false;
bool is_bin_notation = false;
bool expect_type = false;
@@ -357,9 +358,18 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting(int p_line)
in_node_path = false;
}
+ if (!in_annotation && in_region == -1 && str[j] == '@') {
+ in_annotation = true;
+ } else if (in_region != -1 || is_a_symbol) {
+ in_annotation = false;
+ }
+
if (in_node_path) {
next_type = NODE_PATH;
color = node_path_color;
+ } else if (in_annotation) {
+ next_type = ANNOTATION;
+ color = annotation_color;
} else if (in_keyword) {
next_type = KEYWORD;
color = keyword_color;
@@ -438,7 +448,7 @@ void GDScriptSyntaxHighlighter::_update_cache() {
color_regions.clear();
color_region_cache.clear();
- font_color = text_edit->get_theme_color("font_color");
+ font_color = text_edit->get_theme_color(SNAME("font_color"));
symbol_color = EDITOR_GET("text_editor/highlighting/symbol_color");
function_color = EDITOR_GET("text_editor/highlighting/function_color");
number_color = EDITOR_GET("text_editor/highlighting/number_color");
@@ -448,8 +458,8 @@ void GDScriptSyntaxHighlighter::_update_cache() {
const Color types_color = EDITOR_GET("text_editor/highlighting/engine_type_color");
List<StringName> types;
ClassDB::get_class_list(&types);
- for (List<StringName>::Element *E = types.front(); E; E = E->next()) {
- String n = E->get();
+ for (const StringName &E : types) {
+ String n = E;
if (n.begins_with("_")) {
n = n.substr(1, n.length());
}
@@ -460,8 +470,8 @@ void GDScriptSyntaxHighlighter::_update_cache() {
const Color usertype_color = EDITOR_GET("text_editor/highlighting/user_type_color");
List<StringName> global_classes;
ScriptServer::get_global_class_list(&global_classes);
- for (List<StringName>::Element *E = global_classes.front(); E; E = E->next()) {
- keywords[String(E->get())] = usertype_color;
+ for (const StringName &E : global_classes) {
+ keywords[String(E)] = usertype_color;
}
/* Autoloads. */
@@ -479,24 +489,28 @@ void GDScriptSyntaxHighlighter::_update_cache() {
const Color basetype_color = EDITOR_GET("text_editor/highlighting/base_type_color");
List<String> core_types;
gdscript->get_core_type_words(&core_types);
- for (List<String>::Element *E = core_types.front(); E; E = E->next()) {
- keywords[E->get()] = basetype_color;
+ for (const String &E : core_types) {
+ keywords[E] = basetype_color;
}
/* Reserved words. */
const Color keyword_color = EDITOR_GET("text_editor/highlighting/keyword_color");
+ const Color control_flow_keyword_color = EDITOR_GET("text_editor/highlighting/control_flow_keyword_color");
List<String> keyword_list;
gdscript->get_reserved_words(&keyword_list);
- for (List<String>::Element *E = keyword_list.front(); E; E = E->next()) {
- keywords[E->get()] = keyword_color;
+ for (const String &E : keyword_list) {
+ if (gdscript->is_control_flow_keyword(E)) {
+ keywords[E] = control_flow_keyword_color;
+ } else {
+ keywords[E] = keyword_color;
+ }
}
/* Comments */
const Color comment_color = EDITOR_GET("text_editor/highlighting/comment_color");
List<String> comments;
gdscript->get_comment_delimiters(&comments);
- for (List<String>::Element *E = comments.front(); E; E = E->next()) {
- String comment = E->get();
+ for (const String &comment : comments) {
String beg = comment.get_slice(" ", 0);
String end = comment.get_slice_count(" ") > 1 ? comment.get_slice(" ", 1) : String();
add_color_region(beg, end, comment_color, end == "");
@@ -506,8 +520,7 @@ void GDScriptSyntaxHighlighter::_update_cache() {
const Color string_color = EDITOR_GET("text_editor/highlighting/string_color");
List<String> strings;
gdscript->get_string_delimiters(&strings);
- for (List<String>::Element *E = strings.front(); E; E = E->next()) {
- String string = E->get();
+ for (const String &string : strings) {
String beg = string.get_slice(" ", 0);
String end = string.get_slice_count(" ") > 1 ? string.get_slice(" ", 1) : String();
add_color_region(beg, end, string_color, end == "");
@@ -521,9 +534,9 @@ void GDScriptSyntaxHighlighter::_update_cache() {
if (instance_base != StringName()) {
List<PropertyInfo> plist;
ClassDB::get_property_list(instance_base, &plist);
- for (List<PropertyInfo>::Element *E = plist.front(); E; E = E->next()) {
- String name = E->get().name;
- if (E->get().usage & PROPERTY_USAGE_CATEGORY || E->get().usage & PROPERTY_USAGE_GROUP || E->get().usage & PROPERTY_USAGE_SUBGROUP) {
+ for (const PropertyInfo &E : plist) {
+ String name = E.name;
+ if (E.usage & PROPERTY_USAGE_CATEGORY || E.usage & PROPERTY_USAGE_GROUP || E.usage & PROPERTY_USAGE_SUBGROUP) {
continue;
}
if (name.find("/") != -1) {
@@ -534,26 +547,29 @@ void GDScriptSyntaxHighlighter::_update_cache() {
List<String> clist;
ClassDB::get_integer_constant_list(instance_base, &clist);
- for (List<String>::Element *E = clist.front(); E; E = E->next()) {
- member_keywords[E->get()] = member_variable_color;
+ for (const String &E : clist) {
+ member_keywords[E] = member_variable_color;
}
}
}
const String text_edit_color_theme = EditorSettings::get_singleton()->get("text_editor/theme/color_theme");
- const bool default_theme = text_edit_color_theme == "Default";
+ const bool godot_2_theme = text_edit_color_theme == "Godot 2";
- if (default_theme || EditorSettings::get_singleton()->is_dark_theme()) {
+ if (godot_2_theme || EditorSettings::get_singleton()->is_dark_theme()) {
function_definition_color = Color(0.4, 0.9, 1.0);
node_path_color = Color(0.39, 0.76, 0.35);
+ annotation_color = Color(1.0, 0.7, 0.45);
} else {
function_definition_color = Color(0.0, 0.65, 0.73);
node_path_color = Color(0.32, 0.55, 0.29);
+ annotation_color = Color(0.8, 0.5, 0.25);
}
EDITOR_DEF("text_editor/highlighting/gdscript/function_definition_color", function_definition_color);
EDITOR_DEF("text_editor/highlighting/gdscript/node_path_color", node_path_color);
- if (text_edit_color_theme == "Adaptive" || default_theme) {
+ EDITOR_DEF("text_editor/highlighting/gdscript/annotation_color", annotation_color);
+ if (text_edit_color_theme == "Default" || godot_2_theme) {
EditorSettings::get_singleton()->set_initial_value(
"text_editor/highlighting/gdscript/function_definition_color",
function_definition_color,
@@ -562,10 +578,15 @@ void GDScriptSyntaxHighlighter::_update_cache() {
"text_editor/highlighting/gdscript/node_path_color",
node_path_color,
true);
+ EditorSettings::get_singleton()->set_initial_value(
+ "text_editor/highlighting/gdscript/annotation_color",
+ annotation_color,
+ true);
}
function_definition_color = EDITOR_GET("text_editor/highlighting/gdscript/function_definition_color");
node_path_color = EDITOR_GET("text_editor/highlighting/gdscript/node_path_color");
+ annotation_color = EDITOR_GET("text_editor/highlighting/gdscript/annotation_color");
type_color = EDITOR_GET("text_editor/highlighting/base_type_color");
}
@@ -599,6 +620,6 @@ void GDScriptSyntaxHighlighter::add_color_region(const String &p_start_key, cons
Ref<EditorSyntaxHighlighter> GDScriptSyntaxHighlighter::_create() const {
Ref<GDScriptSyntaxHighlighter> syntax_highlighter;
- syntax_highlighter.instance();
+ syntax_highlighter.instantiate();
return syntax_highlighter;
}
diff --git a/modules/gdscript/editor/gdscript_highlighter.h b/modules/gdscript/editor/gdscript_highlighter.h
index 1b57cb1923..d07c182aa6 100644
--- a/modules/gdscript/editor/gdscript_highlighter.h
+++ b/modules/gdscript/editor/gdscript_highlighter.h
@@ -54,6 +54,7 @@ private:
NONE,
REGION,
NODE_PATH,
+ ANNOTATION,
SYMBOL,
NUMBER,
FUNCTION,
@@ -72,6 +73,7 @@ private:
Color number_color;
Color member_color;
Color node_path_color;
+ Color annotation_color;
Color type_color;
void add_color_region(const String &p_start_key, const String &p_end_key, const Color &p_color, bool p_line_only = false);
diff --git a/modules/gdscript/editor/gdscript_translation_parser_plugin.h b/modules/gdscript/editor/gdscript_translation_parser_plugin.h
index fcf438422a..caa80fc24c 100644
--- a/modules/gdscript/editor/gdscript_translation_parser_plugin.h
+++ b/modules/gdscript/editor/gdscript_translation_parser_plugin.h
@@ -34,7 +34,6 @@
#include "core/templates/set.h"
#include "editor/editor_translation_parser.h"
#include "modules/gdscript/gdscript_parser.h"
-#include "modules/regex/regex.h"
class GDScriptEditorTranslationParserPlugin : public EditorTranslationParserPlugin {
GDCLASS(GDScriptEditorTranslationParserPlugin, EditorTranslationParserPlugin);
diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp
index 5f590383d0..8957b00a1b 100644
--- a/modules/gdscript/gdscript.cpp
+++ b/modules/gdscript/gdscript.cpp
@@ -36,8 +36,8 @@
#include "core/config/project_settings.h"
#include "core/core_constants.h"
#include "core/core_string_names.h"
+#include "core/io/file_access.h"
#include "core/io/file_access_encrypted.h"
-#include "core/os/file_access.h"
#include "core/os/os.h"
#include "gdscript_analyzer.h"
#include "gdscript_cache.h"
@@ -72,19 +72,19 @@ void GDScriptNativeClass::_bind_methods() {
}
Variant GDScriptNativeClass::_new() {
- Object *o = instance();
+ Object *o = instantiate();
ERR_FAIL_COND_V_MSG(!o, Variant(), "Class type: '" + String(name) + "' is not instantiable.");
- Reference *ref = Object::cast_to<Reference>(o);
- if (ref) {
- return REF(ref);
+ RefCounted *rc = Object::cast_to<RefCounted>(o);
+ if (rc) {
+ return REF(rc);
} else {
return o;
}
}
-Object *GDScriptNativeClass::instance() {
- return ClassDB::instance(name);
+Object *GDScriptNativeClass::instantiate() {
+ return ClassDB::instantiate(name);
}
void GDScript::_super_implicit_constructor(GDScript *p_script, GDScriptInstance *p_instance, Callable::CallError &r_error) {
@@ -98,11 +98,11 @@ void GDScript::_super_implicit_constructor(GDScript *p_script, GDScriptInstance
p_script->implicit_initializer->call(p_instance, nullptr, 0, r_error);
}
-GDScriptInstance *GDScript::_create_instance(const Variant **p_args, int p_argcount, Object *p_owner, bool p_isref, Callable::CallError &r_error) {
+GDScriptInstance *GDScript::_create_instance(const Variant **p_args, int p_argcount, Object *p_owner, bool p_is_ref_counted, Callable::CallError &r_error) {
/* STEP 1, CREATE */
GDScriptInstance *instance = memnew(GDScriptInstance);
- instance->base_ref = p_isref;
+ instance->base_ref_counted = p_is_ref_counted;
instance->members.resize(member_indices.size());
instance->script = Ref<GDScript>(this);
instance->owner = p_owner;
@@ -170,13 +170,13 @@ Variant GDScript::_new(const Variant **p_args, int p_argcount, Callable::CallErr
ERR_FAIL_COND_V(_baseptr->native.is_null(), Variant());
if (_baseptr->native.ptr()) {
- owner = _baseptr->native->instance();
+ owner = _baseptr->native->instantiate();
} else {
- owner = memnew(Reference); //by default, no base means use reference
+ owner = memnew(RefCounted); //by default, no base means use reference
}
ERR_FAIL_COND_V_MSG(!owner, Variant(), "Can't inherit from a virtual class.");
- Reference *r = Object::cast_to<Reference>(owner);
+ RefCounted *r = Object::cast_to<RefCounted>(owner);
if (r) {
ref = REF(r);
}
@@ -196,7 +196,7 @@ Variant GDScript::_new(const Variant **p_args, int p_argcount, Callable::CallErr
}
}
-bool GDScript::can_instance() const {
+bool GDScript::can_instantiate() const {
#ifdef TOOLS_ENABLED
return valid && (tool || ScriptServer::is_scripting_enabled());
#else
@@ -291,8 +291,8 @@ void GDScript::_get_script_property_list(List<PropertyInfo> *r_list, bool p_incl
sptr = sptr->_base;
}
- for (List<PropertyInfo>::Element *E = props.front(); E; E = E->next()) {
- r_list->push_back(E->get());
+ for (const PropertyInfo &E : props) {
+ r_list->push_back(E);
}
}
@@ -346,21 +346,21 @@ ScriptInstance *GDScript::instance_create(Object *p_this) {
if (top->native.is_valid()) {
if (!ClassDB::is_parent_class(p_this->get_class_name(), top->native->get_name())) {
if (EngineDebugger::is_active()) {
- GDScriptLanguage::get_singleton()->debug_break_parse(get_path(), 1, "Script inherits from native type '" + String(top->native->get_name()) + "', so it can't be instanced in object of type: '" + p_this->get_class() + "'");
+ GDScriptLanguage::get_singleton()->debug_break_parse(get_path(), 1, "Script inherits from native type '" + String(top->native->get_name()) + "', so it can't be instantiated in object of type: '" + p_this->get_class() + "'");
}
- ERR_FAIL_V_MSG(nullptr, "Script inherits from native type '" + String(top->native->get_name()) + "', so it can't be instanced in object of type '" + p_this->get_class() + "'" + ".");
+ ERR_FAIL_V_MSG(nullptr, "Script inherits from native type '" + String(top->native->get_name()) + "', so it can't be instantiated in object of type '" + p_this->get_class() + "'" + ".");
}
}
Callable::CallError unchecked_error;
- return _create_instance(nullptr, 0, p_this, Object::cast_to<Reference>(p_this) != nullptr, unchecked_error);
+ return _create_instance(nullptr, 0, p_this, Object::cast_to<RefCounted>(p_this) != nullptr, unchecked_error);
}
PlaceHolderScriptInstance *GDScript::placeholder_instance_create(Object *p_this) {
#ifdef TOOLS_ENABLED
PlaceHolderScriptInstance *si = memnew(PlaceHolderScriptInstance(GDScriptLanguage::get_singleton(), Ref<Script>(this), p_this));
placeholders.insert(si);
- _update_exports();
+ _update_exports(nullptr, false, si);
return si;
#else
return nullptr;
@@ -401,8 +401,8 @@ void GDScript::_update_exports_values(Map<StringName, Variant> &values, List<Pro
values[E->key()] = E->get();
}
- for (List<PropertyInfo>::Element *E = members_cache.front(); E; E = E->next()) {
- propnames.push_back(E->get());
+ for (const PropertyInfo &E : members_cache) {
+ propnames.push_back(E);
}
}
@@ -481,7 +481,7 @@ void GDScript::_update_doc() {
methods[i].return_val.class_name = _get_gdscript_reference_class_name(Object::cast_to<GDScript>(return_type.script_type));
}
- // Change class name if argumetn is script reference.
+ // Change class name if argument is script reference.
for (int j = 0; j < fn->get_argument_count(); j++) {
GDScriptDataType arg_type = fn->get_argument_type(j);
if (arg_type.kind == GDScriptDataType::GDSCRIPT) {
@@ -584,7 +584,7 @@ void GDScript::_update_doc() {
}
#endif
-bool GDScript::_update_exports(bool *r_err, bool p_recursive_call) {
+bool GDScript::_update_exports(bool *r_err, bool p_recursive_call, PlaceHolderScriptInstance *p_instance_to_update) {
#ifdef TOOLS_ENABLED
static Vector<GDScript *> base_caches;
@@ -721,15 +721,19 @@ bool GDScript::_update_exports(bool *r_err, bool p_recursive_call) {
}
}
- if (placeholders.size()) { //hm :(
+ if ((changed || p_instance_to_update) && placeholders.size()) { //hm :(
// update placeholders if any
Map<StringName, Variant> values;
List<PropertyInfo> propnames;
_update_exports_values(values, propnames);
- for (Set<PlaceHolderScriptInstance *>::Element *E = placeholders.front(); E; E = E->next()) {
- E->get()->update(propnames, values);
+ if (changed) {
+ for (Set<PlaceHolderScriptInstance *>::Element *E = placeholders.front(); E; E = E->next()) {
+ E->get()->update(propnames, values);
+ }
+ } else {
+ p_instance_to_update->update(propnames, values);
}
}
@@ -857,8 +861,7 @@ Error GDScript::reload(bool p_keep_state) {
}
}
#ifdef DEBUG_ENABLED
- for (const List<GDScriptWarning>::Element *E = parser.get_warnings().front(); E; E = E->next()) {
- const GDScriptWarning &warning = E->get();
+ for (const GDScriptWarning &warning : parser.get_warnings()) {
if (EngineDebugger::is_active()) {
Vector<ScriptLanguage::StackInfo> si;
EngineDebugger::get_script_debugger()->send_error("", get_path(), warning.start_line, warning.get_name(), warning.get_message(), ERR_HANDLER_WARNING, si);
@@ -897,68 +900,10 @@ void GDScript::get_members(Set<StringName> *p_members) {
}
}
-Vector<ScriptNetData> GDScript::get_rpc_methods() const {
+const Vector<MultiplayerAPI::RPCConfig> GDScript::get_rpc_methods() const {
return rpc_functions;
}
-uint16_t GDScript::get_rpc_method_id(const StringName &p_method) const {
- for (int i = 0; i < rpc_functions.size(); i++) {
- if (rpc_functions[i].name == p_method) {
- return i;
- }
- }
- return UINT16_MAX;
-}
-
-StringName GDScript::get_rpc_method(const uint16_t p_rpc_method_id) const {
- if (p_rpc_method_id >= rpc_functions.size()) {
- return StringName();
- }
- return rpc_functions[p_rpc_method_id].name;
-}
-
-MultiplayerAPI::RPCMode GDScript::get_rpc_mode_by_id(const uint16_t p_rpc_method_id) const {
- if (p_rpc_method_id >= rpc_functions.size()) {
- return MultiplayerAPI::RPC_MODE_DISABLED;
- }
- return rpc_functions[p_rpc_method_id].mode;
-}
-
-MultiplayerAPI::RPCMode GDScript::get_rpc_mode(const StringName &p_method) const {
- return get_rpc_mode_by_id(get_rpc_method_id(p_method));
-}
-
-Vector<ScriptNetData> GDScript::get_rset_properties() const {
- return rpc_variables;
-}
-
-uint16_t GDScript::get_rset_property_id(const StringName &p_variable) const {
- for (int i = 0; i < rpc_variables.size(); i++) {
- if (rpc_variables[i].name == p_variable) {
- return i;
- }
- }
- return UINT16_MAX;
-}
-
-StringName GDScript::get_rset_property(const uint16_t p_rset_member_id) const {
- if (p_rset_member_id >= rpc_variables.size()) {
- return StringName();
- }
- return rpc_variables[p_rset_member_id].name;
-}
-
-MultiplayerAPI::RPCMode GDScript::get_rset_mode_by_id(const uint16_t p_rset_member_id) const {
- if (p_rset_member_id >= rpc_variables.size()) {
- return MultiplayerAPI::RPC_MODE_DISABLED;
- }
- return rpc_variables[p_rset_member_id].mode;
-}
-
-MultiplayerAPI::RPCMode GDScript::get_rset_mode(const StringName &p_variable) const {
- return get_rset_mode_by_id(get_rset_property_id(p_variable));
-}
-
Variant GDScript::call(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
GDScript *top = this;
while (top) {
@@ -1045,10 +990,10 @@ Error GDScript::load_source_code(const String &p_path) {
ERR_FAIL_COND_V(err, err);
}
- int len = f->get_len();
+ uint64_t len = f->get_length();
sourcef.resize(len + 1);
uint8_t *w = sourcef.ptrw();
- int r = f->get_buffer(w, len);
+ uint64_t r = f->get_buffer(w, len);
f->close();
memdelete(f);
ERR_FAIL_COND_V(r != len, ERR_CANT_OPEN);
@@ -1210,10 +1155,8 @@ void GDScript::_save_orphaned_subclasses() {
void GDScript::_init_rpc_methods_properties() {
// Copy the base rpc methods so we don't mask their IDs.
rpc_functions.clear();
- rpc_variables.clear();
if (base.is_valid()) {
rpc_functions = base->rpc_functions;
- rpc_variables = base->rpc_variables;
}
GDScript *cscript = this;
@@ -1221,23 +1164,11 @@ void GDScript::_init_rpc_methods_properties() {
while (cscript) {
// RPC Methods
for (Map<StringName, GDScriptFunction *>::Element *E = cscript->member_functions.front(); E; E = E->next()) {
- if (E->get()->get_rpc_mode() != MultiplayerAPI::RPC_MODE_DISABLED) {
- ScriptNetData nd;
- nd.name = E->key();
- nd.mode = E->get()->get_rpc_mode();
- if (-1 == rpc_functions.find(nd)) {
- rpc_functions.push_back(nd);
- }
- }
- }
- // RSet
- for (Map<StringName, MemberInfo>::Element *E = cscript->member_indices.front(); E; E = E->next()) {
- if (E->get().rpc_mode != MultiplayerAPI::RPC_MODE_DISABLED) {
- ScriptNetData nd;
- nd.name = E->key();
- nd.mode = E->get().rpc_mode;
- if (-1 == rpc_variables.find(nd)) {
- rpc_variables.push_back(nd);
+ MultiplayerAPI::RPCConfig config = E->get()->get_rpc_config();
+ if (config.rpc_mode != MultiplayerAPI::RPC_MODE_DISABLED) {
+ config.name = E->get()->get_name();
+ if (rpc_functions.find(config) == -1) {
+ rpc_functions.push_back(config);
}
}
}
@@ -1254,8 +1185,7 @@ void GDScript::_init_rpc_methods_properties() {
}
// Sort so we are 100% that they are always the same.
- rpc_functions.sort_custom<SortNetData>();
- rpc_variables.sort_custom<SortNetData>();
+ rpc_functions.sort_custom<MultiplayerAPI::SortRPCConfig>();
}
GDScript::~GDScript() {
@@ -1514,8 +1444,8 @@ void GDScriptInstance::get_property_list(List<PropertyInfo> *p_properties) const
sptr = sptr->_base;
}
- for (List<PropertyInfo>::Element *E = props.front(); E; E = E->next()) {
- p_properties->push_back(E->get());
+ for (const PropertyInfo &E : props) {
+ p_properties->push_back(E);
}
}
@@ -1611,46 +1541,10 @@ ScriptLanguage *GDScriptInstance::get_language() {
return GDScriptLanguage::get_singleton();
}
-Vector<ScriptNetData> GDScriptInstance::get_rpc_methods() const {
+const Vector<MultiplayerAPI::RPCConfig> GDScriptInstance::get_rpc_methods() const {
return script->get_rpc_methods();
}
-uint16_t GDScriptInstance::get_rpc_method_id(const StringName &p_method) const {
- return script->get_rpc_method_id(p_method);
-}
-
-StringName GDScriptInstance::get_rpc_method(const uint16_t p_rpc_method_id) const {
- return script->get_rpc_method(p_rpc_method_id);
-}
-
-MultiplayerAPI::RPCMode GDScriptInstance::get_rpc_mode_by_id(const uint16_t p_rpc_method_id) const {
- return script->get_rpc_mode_by_id(p_rpc_method_id);
-}
-
-MultiplayerAPI::RPCMode GDScriptInstance::get_rpc_mode(const StringName &p_method) const {
- return script->get_rpc_mode(p_method);
-}
-
-Vector<ScriptNetData> GDScriptInstance::get_rset_properties() const {
- return script->get_rset_properties();
-}
-
-uint16_t GDScriptInstance::get_rset_property_id(const StringName &p_variable) const {
- return script->get_rset_property_id(p_variable);
-}
-
-StringName GDScriptInstance::get_rset_property(const uint16_t p_rset_member_id) const {
- return script->get_rset_property(p_rset_member_id);
-}
-
-MultiplayerAPI::RPCMode GDScriptInstance::get_rset_mode_by_id(const uint16_t p_rset_member_id) const {
- return script->get_rset_mode_by_id(p_rset_member_id);
-}
-
-MultiplayerAPI::RPCMode GDScriptInstance::get_rset_mode(const StringName &p_variable) const {
- return script->get_rset_mode(p_variable);
-}
-
void GDScriptInstance::reload_members() {
#ifdef DEBUG_ENABLED
@@ -1681,7 +1575,7 @@ void GDScriptInstance::reload_members() {
GDScriptInstance::GDScriptInstance() {
owner = nullptr;
- base_ref = false;
+ base_ref_counted = false;
}
GDScriptInstance::~GDScriptInstance() {
@@ -1742,33 +1636,32 @@ void GDScriptLanguage::init() {
_add_global(StaticCString::create("PI"), Math_PI);
_add_global(StaticCString::create("TAU"), Math_TAU);
- _add_global(StaticCString::create("INF"), Math_INF);
- _add_global(StaticCString::create("NAN"), Math_NAN);
+ _add_global(StaticCString::create("INF"), INFINITY);
+ _add_global(StaticCString::create("NAN"), NAN);
//populate native classes
List<StringName> class_list;
ClassDB::get_class_list(&class_list);
- for (List<StringName>::Element *E = class_list.front(); E; E = E->next()) {
- StringName n = E->get();
+ for (const StringName &n : class_list) {
String s = String(n);
if (s.begins_with("_")) {
- n = s.substr(1, s.length());
+ s = s.substr(1, s.length());
}
- if (globals.has(n)) {
+ if (globals.has(s)) {
continue;
}
- Ref<GDScriptNativeClass> nc = memnew(GDScriptNativeClass(E->get()));
- _add_global(n, nc);
+ Ref<GDScriptNativeClass> nc = memnew(GDScriptNativeClass(n));
+ _add_global(s, nc);
}
//populate singletons
List<Engine::Singleton> singletons;
Engine::get_singleton()->get_singletons(&singletons);
- for (List<Engine::Singleton>::Element *E = singletons.front(); E; E = E->next()) {
- _add_global(E->get().name, E->get().ptr);
+ for (const Engine::Singleton &E : singletons) {
+ _add_global(E.name, E.ptr);
}
#ifdef TESTS_ENABLED
@@ -1911,10 +1804,10 @@ void GDScriptLanguage::reload_all_scripts() {
scripts.sort_custom<GDScriptDepSort>(); //update in inheritance dependency order
- for (List<Ref<GDScript>>::Element *E = scripts.front(); E; E = E->next()) {
- print_verbose("GDScript: Reloading: " + E->get()->get_path());
- E->get()->load_source_code(E->get()->get_path());
- E->get()->reload(true);
+ for (Ref<GDScript> &script : scripts) {
+ print_verbose("GDScript: Reloading: " + script->get_path());
+ script->load_source_code(script->get_path());
+ script->reload(true);
}
#endif
}
@@ -1943,21 +1836,21 @@ void GDScriptLanguage::reload_tool_script(const Ref<Script> &p_script, bool p_so
scripts.sort_custom<GDScriptDepSort>(); //update in inheritance dependency order
- for (List<Ref<GDScript>>::Element *E = scripts.front(); E; E = E->next()) {
- bool reload = E->get() == p_script || to_reload.has(E->get()->get_base());
+ for (Ref<GDScript> &script : scripts) {
+ bool reload = script == p_script || to_reload.has(script->get_base());
if (!reload) {
continue;
}
- to_reload.insert(E->get(), Map<ObjectID, List<Pair<StringName, Variant>>>());
+ to_reload.insert(script, Map<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[E->get()];
+ Map<ObjectID, List<Pair<StringName, Variant>>> &map = to_reload[script];
- while (E->get()->instances.front()) {
- Object *obj = E->get()->instances.front()->get();
+ while (script->instances.front()) {
+ Object *obj = script->instances.front()->get();
//save instance info
List<Pair<StringName, Variant>> state;
if (obj->get_script_instance()) {
@@ -1970,8 +1863,8 @@ void GDScriptLanguage::reload_tool_script(const Ref<Script> &p_script, bool p_so
//same thing for placeholders
#ifdef TOOLS_ENABLED
- while (E->get()->placeholders.size()) {
- Object *obj = E->get()->placeholders.front()->get()->get_owner();
+ while (script->placeholders.size()) {
+ Object *obj = script->placeholders.front()->get()->get_owner();
//save instance info
if (obj->get_script_instance()) {
@@ -1981,13 +1874,13 @@ 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
- E->get()->placeholders.erase(E->get()->placeholders.front()->get());
+ script->placeholders.erase(script->placeholders.front()->get());
}
}
#endif
- for (Map<ObjectID, List<Pair<StringName, Variant>>>::Element *F = E->get()->pending_reload_state.front(); F; F = F->next()) {
+ for (Map<ObjectID, List<Pair<StringName, Variant>>>::Element *F = script->pending_reload_state.front(); F; F = F->next()) {
map[F->key()] = F->get(); //pending to reload, use this one instead
}
}
@@ -2130,11 +2023,24 @@ void GDScriptLanguage::get_reserved_words(List<String> *p_words) const {
List<StringName> functions;
GDScriptUtilityFunctions::get_function_list(&functions);
- for (const List<StringName>::Element *E = functions.front(); E; E = E->next()) {
- p_words->push_back(String(E->get()));
+ for (const StringName &E : functions) {
+ p_words->push_back(String(E));
}
}
+bool GDScriptLanguage::is_control_flow_keyword(String p_keyword) const {
+ return p_keyword == "break" ||
+ p_keyword == "continue" ||
+ p_keyword == "elif" ||
+ p_keyword == "else" ||
+ p_keyword == "if" ||
+ p_keyword == "for" ||
+ p_keyword == "match" ||
+ p_keyword == "pass" ||
+ p_keyword == "return" ||
+ p_keyword == "while";
+}
+
bool GDScriptLanguage::handles_global_class_type(const String &p_type) const {
return p_type == "GDScript";
}
@@ -2156,7 +2062,7 @@ String GDScriptLanguage::get_global_class_name(const String &p_path, String *r_b
if (err == OK) {
const GDScriptParser::ClassNode *c = parser.get_tree();
if (r_icon_path) {
- if (c->icon_path.is_empty() || c->icon_path.is_abs_path()) {
+ if (c->icon_path.is_empty() || c->icon_path.is_absolute_path()) {
*r_icon_path = c->icon_path;
} else if (c->icon_path.is_rel_path()) {
*r_icon_path = p_path.get_base_dir().plus_file(c->icon_path).simplify_path();
@@ -2224,7 +2130,7 @@ String GDScriptLanguage::get_global_class_name(const String &p_path, String *r_b
break;
}
} else {
- *r_base_type = "Reference";
+ *r_base_type = "RefCounted";
subclass = nullptr;
}
}
@@ -2344,7 +2250,7 @@ RES ResourceFormatLoaderGDScript::load(const String &p_path, const String &p_ori
if (script.is_null()) {
// Don't fail loading because of parsing error.
- script.instance();
+ script.instantiate();
}
if (r_error) {
@@ -2388,8 +2294,8 @@ void ResourceFormatLoaderGDScript::get_dependencies(const String &p_path, List<S
return;
}
- for (const List<String>::Element *E = parser.get_dependencies().front(); E; E = E->next()) {
- p_dependencies->push_back(E->get());
+ for (const String &E : parser.get_dependencies()) {
+ p_dependencies->push_back(E);
}
}
diff --git a/modules/gdscript/gdscript.h b/modules/gdscript/gdscript.h
index 12c909fd4f..24809ad5fd 100644
--- a/modules/gdscript/gdscript.h
+++ b/modules/gdscript/gdscript.h
@@ -39,8 +39,8 @@
#include "core/object/script_language.h"
#include "gdscript_function.h"
-class GDScriptNativeClass : public Reference {
- GDCLASS(GDScriptNativeClass, Reference);
+class GDScriptNativeClass : public RefCounted {
+ GDCLASS(GDScriptNativeClass, RefCounted);
StringName name;
@@ -51,7 +51,7 @@ protected:
public:
_FORCE_INLINE_ const StringName &get_name() const { return name; }
Variant _new();
- Object *instance();
+ Object *instantiate();
GDScriptNativeClass(const StringName &p_name);
};
@@ -64,7 +64,6 @@ class GDScript : public Script {
int index = 0;
StringName setter;
StringName getter;
- MultiplayerAPI::RPCMode rpc_mode;
GDScriptDataType data_type;
};
@@ -80,14 +79,13 @@ class GDScript : public Script {
GDScript *_base = nullptr; //fast pointer access
GDScript *_owner = nullptr; //for subclasses
- Set<StringName> members; //members are just indices to the instanced script.
+ 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 instanced script.
+ Map<StringName, MemberInfo> member_indices; //members are just indices to the instantiated script.
Map<StringName, Ref<GDScript>> subclasses;
Map<StringName, Vector<StringName>> _signals;
- Vector<ScriptNetData> rpc_functions;
- Vector<ScriptNetData> rpc_variables;
+ Vector<MultiplayerAPI::RPCConfig> rpc_functions;
#ifdef TOOLS_ENABLED
@@ -133,7 +131,7 @@ class GDScript : public Script {
SelfList<GDScriptFunctionState>::List pending_func_states;
void _super_implicit_constructor(GDScript *p_script, GDScriptInstance *p_instance, Callable::CallError &r_error);
- GDScriptInstance *_create_instance(const Variant **p_args, int p_argcount, Object *p_owner, bool p_isref, Callable::CallError &r_error);
+ GDScriptInstance *_create_instance(const Variant **p_args, int p_argcount, Object *p_owner, bool p_is_ref_counted, Callable::CallError &r_error);
void _set_subclass_path(Ref<GDScript> &p_sc, const String &p_path);
@@ -149,7 +147,7 @@ class GDScript : public Script {
#endif
- bool _update_exports(bool *r_err = nullptr, bool p_recursive_call = false);
+ bool _update_exports(bool *r_err = nullptr, bool p_recursive_call = false, PlaceHolderScriptInstance *p_instance_to_update = nullptr);
void _save_orphaned_subclasses();
void _init_rpc_methods_properties();
@@ -158,7 +156,7 @@ class GDScript : public Script {
void _get_script_method_list(List<MethodInfo> *r_list, bool p_include_base) const;
void _get_script_signal_list(List<MethodInfo> *r_list, bool p_include_base) const;
- // This method will map the class name from "Reference" to "MyClass.InnerClass".
+ // This method will map the class name from "RefCounted" to "MyClass.InnerClass".
static String _get_gdscript_reference_class_name(const GDScript *p_gdscript);
protected:
@@ -197,7 +195,7 @@ public:
StringName debug_get_member_by_index(int p_idx) const;
Variant _new(const Variant **p_args, int p_argcount, Callable::CallError &r_error);
- virtual bool can_instance() const override;
+ virtual bool can_instantiate() const override;
virtual Ref<Script> get_base_script() const override;
@@ -247,17 +245,7 @@ public:
virtual void get_constants(Map<StringName, Variant> *p_constants) override;
virtual void get_members(Set<StringName> *p_members) override;
- virtual Vector<ScriptNetData> get_rpc_methods() const override;
- virtual uint16_t get_rpc_method_id(const StringName &p_method) const override;
- virtual StringName get_rpc_method(const uint16_t p_rpc_method_id) const override;
- virtual MultiplayerAPI::RPCMode get_rpc_mode_by_id(const uint16_t p_rpc_method_id) const override;
- virtual MultiplayerAPI::RPCMode get_rpc_mode(const StringName &p_method) const override;
-
- virtual Vector<ScriptNetData> get_rset_properties() const override;
- virtual uint16_t get_rset_property_id(const StringName &p_variable) const override;
- virtual StringName get_rset_property(const uint16_t p_variable_id) const override;
- virtual MultiplayerAPI::RPCMode get_rset_mode_by_id(const uint16_t p_variable_id) const override;
- virtual MultiplayerAPI::RPCMode get_rset_mode(const StringName &p_variable) const override;
+ virtual const Vector<MultiplayerAPI::RPCConfig> get_rpc_methods() const override;
#ifdef TOOLS_ENABLED
virtual bool is_placeholder_fallback_enabled() const override { return placeholder_fallback_enabled; }
@@ -270,6 +258,7 @@ public:
class GDScriptInstance : public ScriptInstance {
friend class GDScript;
friend class GDScriptFunction;
+ friend class GDScriptLambdaCallable;
friend class GDScriptCompiler;
friend struct GDScriptUtilityFunctionsDefinitions;
@@ -280,7 +269,7 @@ class GDScriptInstance : public ScriptInstance {
Map<StringName, int> member_indices_cache; //used only for hot script reloading
#endif
Vector<Variant> members;
- bool base_ref;
+ bool base_ref_counted;
SelfList<GDScriptFunctionState>::List pending_func_states;
@@ -309,17 +298,7 @@ public:
void reload_members();
- virtual Vector<ScriptNetData> get_rpc_methods() const;
- virtual uint16_t get_rpc_method_id(const StringName &p_method) const;
- virtual StringName get_rpc_method(const uint16_t p_rpc_method_id) const;
- virtual MultiplayerAPI::RPCMode get_rpc_mode_by_id(const uint16_t p_rpc_method_id) const;
- virtual MultiplayerAPI::RPCMode get_rpc_mode(const StringName &p_method) const;
-
- virtual Vector<ScriptNetData> get_rset_properties() const;
- virtual uint16_t get_rset_property_id(const StringName &p_variable) const;
- virtual StringName get_rset_property(const uint16_t p_variable_id) const;
- virtual MultiplayerAPI::RPCMode get_rset_mode_by_id(const uint16_t p_variable_id) const;
- virtual MultiplayerAPI::RPCMode get_rset_mode(const StringName &p_variable) const;
+ virtual const Vector<MultiplayerAPI::RPCConfig> get_rpc_methods() const;
GDScriptInstance();
~GDScriptInstance();
@@ -460,13 +439,14 @@ public:
/* EDITOR FUNCTIONS */
virtual void get_reserved_words(List<String> *p_words) const;
+ virtual bool is_control_flow_keyword(String p_keywords) const;
virtual void get_comment_delimiters(List<String> *p_delimiters) const;
virtual void get_string_delimiters(List<String> *p_delimiters) const;
virtual String _get_processed_template(const String &p_template, const String &p_base_class_name) const;
virtual Ref<Script> get_template(const String &p_class_name, const String &p_base_class_name) const;
virtual bool is_using_templates();
virtual void make_template(const String &p_class_name, const String &p_base_class_name, Ref<Script> &p_script);
- virtual bool validate(const String &p_script, int &r_line_error, int &r_col_error, String &r_test_error, const String &p_path = "", List<String> *r_functions = nullptr, List<ScriptLanguage::Warning> *r_warnings = nullptr, Set<int> *r_safe_lines = nullptr) const;
+ 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;
virtual Script *create_script() const;
virtual bool has_named_classes() const;
virtual bool supports_builtin_mode() const;
diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp
index 5da2bb5cc1..ab37e54cf1 100644
--- a/modules/gdscript/gdscript_analyzer.cpp
+++ b/modules/gdscript/gdscript_analyzer.cpp
@@ -31,10 +31,10 @@
#include "gdscript_analyzer.h"
#include "core/config/project_settings.h"
+#include "core/io/file_access.h"
#include "core/io/resource_loader.h"
#include "core/object/class_db.h"
#include "core/object/script_language.h"
-#include "core/os/file_access.h"
#include "core/templates/hash_map.h"
#include "gdscript.h"
#include "gdscript_utility_functions.h"
@@ -115,8 +115,8 @@ static GDScriptParser::DataType make_native_enum_type(const StringName &p_native
StringName real_native_name = GDScriptParser::get_real_class_name(p_native_class);
ClassDB::get_enum_constants(real_native_name, p_enum_name, &enum_values);
- for (const List<StringName>::Element *E = enum_values.front(); E != nullptr; E = E->next()) {
- type.enum_values[E->get()] = ClassDB::get_integer_constant(real_native_name, E->get());
+ for (const StringName &E : enum_values) {
+ type.enum_values[E] = ClassDB::get_integer_constant(real_native_name, E);
}
return type;
@@ -163,7 +163,7 @@ Error GDScriptAnalyzer::resolve_inheritance(GDScriptParser::ClassNode *p_class,
if (!p_class->extends_used) {
result.type_source = GDScriptParser::DataType::ANNOTATED_INFERRED;
result.kind = GDScriptParser::DataType::NATIVE;
- result.native_type = "Reference";
+ result.native_type = "RefCounted";
} else {
result.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
@@ -229,7 +229,7 @@ Error GDScriptAnalyzer::resolve_inheritance(GDScriptParser::ClassNode *p_class,
push_error(vformat(R"(Could not resolve super class inheritance from "%s".)", name), p_class);
return err;
}
- } else if (class_exists(name) && ClassDB::can_instance(GDScriptParser::get_real_class_name(name))) {
+ } else if (class_exists(name) && ClassDB::can_instantiate(GDScriptParser::get_real_class_name(name))) {
base.kind = GDScriptParser::DataType::NATIVE;
base.native_type = name;
} else {
@@ -543,6 +543,7 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas
} else {
// TODO: Add warning.
mark_node_unsafe(member.variable->initializer);
+ member.variable->use_conversion_assign = true;
}
} else if (datatype.builtin_type == Variant::INT && member.variable->initializer->get_datatype().builtin_type == Variant::FLOAT) {
#ifdef DEBUG_ENABLED
@@ -552,6 +553,7 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas
if (member.variable->initializer->get_datatype().is_variant()) {
// TODO: Warn unsafe assign.
mark_node_unsafe(member.variable->initializer);
+ member.variable->use_conversion_assign = true;
}
}
} else if (member.variable->infer_datatype) {
@@ -571,8 +573,8 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas
member.variable->set_datatype(datatype);
// Apply annotations.
- for (List<GDScriptParser::AnnotationNode *>::Element *E = member.variable->annotations.front(); E; E = E->next()) {
- E->get()->apply(parser, member.variable);
+ for (GDScriptParser::AnnotationNode *&E : member.variable->annotations) {
+ E->apply(parser, member.variable);
}
} break;
case GDScriptParser::ClassNode::Member::CONSTANT: {
@@ -620,8 +622,8 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas
member.constant->set_datatype(datatype);
// Apply annotations.
- for (List<GDScriptParser::AnnotationNode *>::Element *E = member.constant->annotations.front(); E; E = E->next()) {
- E->get()->apply(parser, member.constant);
+ for (GDScriptParser::AnnotationNode *&E : member.constant->annotations) {
+ E->apply(parser, member.constant);
}
} break;
case GDScriptParser::ClassNode::Member::SIGNAL: {
@@ -639,8 +641,8 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas
member.signal->set_datatype(signal_type);
// Apply annotations.
- for (List<GDScriptParser::AnnotationNode *>::Element *E = member.signal->annotations.front(); E; E = E->next()) {
- E->get()->apply(parser, member.signal);
+ for (GDScriptParser::AnnotationNode *&E : member.signal->annotations) {
+ E->apply(parser, member.signal);
}
} break;
case GDScriptParser::ClassNode::Member::ENUM: {
@@ -686,8 +688,8 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas
member.m_enum->set_datatype(enum_type);
// Apply annotations.
- for (List<GDScriptParser::AnnotationNode *>::Element *E = member.m_enum->annotations.front(); E; E = E->next()) {
- E->get()->apply(parser, member.m_enum);
+ for (GDScriptParser::AnnotationNode *&E : member.m_enum->annotations) {
+ E->apply(parser, member.m_enum);
}
} break;
case GDScriptParser::ClassNode::Member::FUNCTION:
@@ -759,8 +761,8 @@ void GDScriptAnalyzer::resolve_class_body(GDScriptParser::ClassNode *p_class) {
resolve_function_body(member.function);
// Apply annotations.
- for (List<GDScriptParser::AnnotationNode *>::Element *E = member.function->annotations.front(); E; E = E->next()) {
- E->get()->apply(parser, member.function);
+ for (GDScriptParser::AnnotationNode *&E : member.function->annotations) {
+ E->apply(parser, member.function);
}
}
@@ -856,6 +858,7 @@ void GDScriptAnalyzer::resolve_node(GDScriptParser::Node *p_node) {
case GDScriptParser::Node::DICTIONARY:
case GDScriptParser::Node::GET_NODE:
case GDScriptParser::Node::IDENTIFIER:
+ case GDScriptParser::Node::LAMBDA:
case GDScriptParser::Node::LITERAL:
case GDScriptParser::Node::PRELOAD:
case GDScriptParser::Node::SELF:
@@ -1144,6 +1147,7 @@ void GDScriptAnalyzer::resolve_variable(GDScriptParser::VariableNode *p_variable
} else {
// TODO: Add warning.
mark_node_unsafe(p_variable->initializer);
+ p_variable->use_conversion_assign = true;
}
#ifdef DEBUG_ENABLED
} else if (type.builtin_type == Variant::INT && p_variable->initializer->get_datatype().builtin_type == Variant::FLOAT) {
@@ -1153,6 +1157,7 @@ void GDScriptAnalyzer::resolve_variable(GDScriptParser::VariableNode *p_variable
if (p_variable->initializer->get_datatype().is_variant()) {
// TODO: Warn unsafe assign.
mark_node_unsafe(p_variable->initializer);
+ p_variable->use_conversion_assign = true;
}
}
} else if (p_variable->infer_datatype) {
@@ -1458,6 +1463,9 @@ void GDScriptAnalyzer::reduce_expression(GDScriptParser::ExpressionNode *p_expre
case GDScriptParser::Node::IDENTIFIER:
reduce_identifier(static_cast<GDScriptParser::IdentifierNode *>(p_expression));
break;
+ case GDScriptParser::Node::LAMBDA:
+ reduce_lambda(static_cast<GDScriptParser::LambdaNode *>(p_expression));
+ break;
case GDScriptParser::Node::LITERAL:
reduce_literal(static_cast<GDScriptParser::LiteralNode *>(p_expression));
break;
@@ -1604,10 +1612,12 @@ void GDScriptAnalyzer::reduce_assignment(GDScriptParser::AssignmentNode *p_assig
} else {
// TODO: Add warning.
mark_node_unsafe(p_assignment);
+ p_assignment->use_conversion_assign = true;
}
} else {
// TODO: Warning in this case.
mark_node_unsafe(p_assignment);
+ p_assignment->use_conversion_assign = true;
}
}
} else {
@@ -1617,6 +1627,9 @@ void GDScriptAnalyzer::reduce_assignment(GDScriptParser::AssignmentNode *p_assig
if (assignee_type.has_no_type() || assigned_value_type.is_variant()) {
mark_node_unsafe(p_assignment);
+ if (assignee_type.is_hard_type()) {
+ p_assignment->use_conversion_assign = true;
+ }
}
if (p_assignment->assignee->type == GDScriptParser::Node::IDENTIFIER) {
@@ -1766,10 +1779,10 @@ void GDScriptAnalyzer::reduce_binary_op(GDScriptParser::BinaryOpNode *p_binary_o
} else {
if (p_binary_op->variant_op < Variant::OP_MAX) {
bool valid = false;
- result = get_operation_type(p_binary_op->variant_op, p_binary_op->left_operand->get_datatype(), right_type, valid, p_binary_op);
+ result = get_operation_type(p_binary_op->variant_op, left_type, right_type, valid, p_binary_op);
if (!valid) {
- push_error(vformat(R"(Invalid operands "%s" and "%s" for "%s" operator.)", p_binary_op->left_operand->get_datatype().to_string(), right_type.to_string(), Variant::get_operator_name(p_binary_op->variant_op)), p_binary_op);
+ push_error(vformat(R"(Invalid operands "%s" and "%s" for "%s" operator.)", left_type.to_string(), right_type.to_string(), Variant::get_operator_name(p_binary_op->variant_op)), p_binary_op);
}
} else {
if (p_binary_op->operation == GDScriptParser::BinaryOpNode::OP_TYPE_TEST) {
@@ -1914,9 +1927,7 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool is_awa
Variant::get_constructor_list(builtin_type, &constructors);
bool match = false;
- for (const List<MethodInfo>::Element *E = constructors.front(); E != nullptr; E = E->next()) {
- const MethodInfo &info = E->get();
-
+ for (const MethodInfo &info : constructors) {
if (p_call->arguments.size() < info.arguments.size() - info.default_arguments.size()) {
continue;
}
@@ -2055,12 +2066,20 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool is_awa
if (p_call->is_super) {
base_type = parser->current_class->base_type;
+ base_type.is_meta_type = false;
is_self = true;
} else if (callee_type == GDScriptParser::Node::IDENTIFIER) {
base_type = parser->current_class->get_datatype();
+ base_type.is_meta_type = false;
is_self = true;
} else if (callee_type == GDScriptParser::Node::SUBSCRIPT) {
GDScriptParser::SubscriptNode *subscript = static_cast<GDScriptParser::SubscriptNode *>(p_call->callee);
+ if (subscript->base == nullptr) {
+ // Invalid syntax, error already set on parser.
+ p_call->set_datatype(call_type);
+ mark_node_unsafe(p_call);
+ return;
+ }
if (!subscript->is_attribute) {
// Invalid call. Error already sent in parser.
// TODO: Could check if Callable here.
@@ -2068,9 +2087,23 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool is_awa
mark_node_unsafe(p_call);
return;
}
- reduce_expression(subscript->base);
+ if (subscript->attribute == nullptr) {
+ // Invalid call. Error already sent in parser.
+ p_call->set_datatype(call_type);
+ mark_node_unsafe(p_call);
+ return;
+ }
- base_type = subscript->base->get_datatype();
+ GDScriptParser::IdentifierNode *base_id = nullptr;
+ if (subscript->base->type == GDScriptParser::Node::IDENTIFIER) {
+ base_id = static_cast<GDScriptParser::IdentifierNode *>(subscript->base);
+ }
+ if (base_id && GDScriptParser::get_builtin_type(base_id->name) < Variant::VARIANT_MAX) {
+ base_type = make_builtin_meta_type(GDScriptParser::get_builtin_type(base_id->name));
+ } else {
+ reduce_expression(subscript->base);
+ base_type = subscript->base->get_datatype();
+ }
} else {
// Invalid call. Error already sent in parser.
// TODO: Could check if Callable here too.
@@ -2097,6 +2130,8 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool is_awa
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);
+ } 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);
}
call_type = return_type;
@@ -2219,6 +2254,8 @@ void GDScriptAnalyzer::reduce_get_node(GDScriptParser::GetNodeNode *p_get_node)
if (!ClassDB::is_parent_class(GDScriptParser::get_real_class_name(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);
}
p_get_node->set_datatype(result);
@@ -2287,8 +2324,7 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
Variant::construct(base.builtin_type, dummy, nullptr, 0, temp);
List<PropertyInfo> properties;
dummy.get_property_list(&properties);
- for (const List<PropertyInfo>::Element *E = properties.front(); E != nullptr; E = E->next()) {
- const PropertyInfo &prop = E->get();
+ for (const PropertyInfo &prop : properties) {
if (prop.name == name) {
p_identifier->set_datatype(type_from_property(prop));
return;
@@ -2310,6 +2346,7 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
GDScriptParser::DataType result;
result.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
result.kind = GDScriptParser::DataType::ENUM_VALUE;
+ result.builtin_type = base.builtin_type;
result.native_type = base.native_type;
result.enum_type = name;
p_identifier->set_datatype(result);
@@ -2346,6 +2383,7 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
case GDScriptParser::ClassNode::Member::ENUM_VALUE:
p_identifier->is_constant = true;
p_identifier->reduced_value = member.enum_value.value;
+ p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_CONSTANT;
break;
case GDScriptParser::ClassNode::Member::VARIABLE:
p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_VARIABLE;
@@ -2446,42 +2484,65 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident
}
}
+ bool found_source = false;
// Check if identifier is local.
// If that's the case, the declaration already was solved before.
switch (p_identifier->source) {
case GDScriptParser::IdentifierNode::FUNCTION_PARAMETER:
p_identifier->set_datatype(p_identifier->parameter_source->get_datatype());
- return;
+ found_source = true;
+ break;
case GDScriptParser::IdentifierNode::LOCAL_CONSTANT:
case GDScriptParser::IdentifierNode::MEMBER_CONSTANT:
p_identifier->set_datatype(p_identifier->constant_source->get_datatype());
p_identifier->is_constant = true;
// TODO: Constant should have a value on the node itself.
p_identifier->reduced_value = p_identifier->constant_source->initializer->reduced_value;
- return;
+ found_source = true;
+ break;
case GDScriptParser::IdentifierNode::MEMBER_VARIABLE:
p_identifier->variable_source->usages++;
[[fallthrough]];
case GDScriptParser::IdentifierNode::LOCAL_VARIABLE:
p_identifier->set_datatype(p_identifier->variable_source->get_datatype());
- return;
+ found_source = true;
+ break;
case GDScriptParser::IdentifierNode::LOCAL_ITERATOR:
p_identifier->set_datatype(p_identifier->bind_source->get_datatype());
- return;
+ found_source = true;
+ break;
case GDScriptParser::IdentifierNode::LOCAL_BIND: {
GDScriptParser::DataType result = p_identifier->bind_source->get_datatype();
result.is_constant = true;
p_identifier->set_datatype(result);
- return;
- }
+ found_source = true;
+ } break;
case GDScriptParser::IdentifierNode::UNDEFINED_SOURCE:
break;
}
// Not a local, so check members.
- reduce_identifier_from_base(p_identifier);
- if (p_identifier->get_datatype().is_set()) {
- // Found.
+ if (!found_source) {
+ reduce_identifier_from_base(p_identifier);
+ if (p_identifier->source != GDScriptParser::IdentifierNode::UNDEFINED_SOURCE || p_identifier->get_datatype().is_set()) {
+ // Found.
+ found_source = true;
+ }
+ }
+
+ 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;
+ }
+
+ 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;
+ }
return;
}
@@ -2563,6 +2624,57 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident
p_identifier->set_datatype(dummy); // Just so type is set to something.
}
+void GDScriptAnalyzer::reduce_lambda(GDScriptParser::LambdaNode *p_lambda) {
+ // Lambda is always a Callable.
+ GDScriptParser::DataType lambda_type;
+ lambda_type.type_source = GDScriptParser::DataType::ANNOTATED_INFERRED;
+ lambda_type.kind = GDScriptParser::DataType::BUILTIN;
+ lambda_type.builtin_type = Variant::CALLABLE;
+ p_lambda->set_datatype(lambda_type);
+
+ if (p_lambda->function == nullptr) {
+ return;
+ }
+
+ GDScriptParser::FunctionNode *previous_function = parser->current_function;
+ parser->current_function = p_lambda->function;
+
+ lambda_stack.push_back(p_lambda);
+
+ for (int i = 0; i < p_lambda->function->parameters.size(); i++) {
+ resolve_parameter(p_lambda->function->parameters[i]);
+ }
+
+ resolve_suite(p_lambda->function->body);
+
+ int captures_amount = p_lambda->captures.size();
+ if (captures_amount > 0) {
+ // Create space for lambda parameters.
+ // At the beginning to not mess with optional parameters.
+ int param_count = p_lambda->function->parameters.size();
+ p_lambda->function->parameters.resize(param_count + captures_amount);
+ for (int i = param_count - 1; i >= 0; i--) {
+ p_lambda->function->parameters.write[i + captures_amount] = p_lambda->function->parameters[i];
+ p_lambda->function->parameters_indices[p_lambda->function->parameters[i]->identifier->name] = i + captures_amount;
+ }
+
+ // Add captures as extra parameters at the beginning.
+ for (int i = 0; i < p_lambda->captures.size(); i++) {
+ GDScriptParser::IdentifierNode *capture = p_lambda->captures[i];
+ GDScriptParser::ParameterNode *capture_param = parser->alloc_node<GDScriptParser::ParameterNode>();
+ capture_param->identifier = capture;
+ capture_param->usages = capture->usages;
+ capture_param->set_datatype(capture->get_datatype());
+
+ p_lambda->function->parameters.write[i] = capture_param;
+ p_lambda->function->parameters_indices[capture->name] = i;
+ }
+ }
+
+ lambda_stack.pop_back();
+ parser->current_function = previous_function;
+}
+
void GDScriptAnalyzer::reduce_literal(GDScriptParser::LiteralNode *p_literal) {
p_literal->reduced_value = p_literal->value;
p_literal->is_constant = true;
@@ -2711,7 +2823,7 @@ void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscri
case Variant::RECT2:
case Variant::RECT2I:
case Variant::PLANE:
- case Variant::QUAT:
+ case Variant::QUATERNION:
case Variant::AABB:
case Variant::OBJECT:
error = index_type.builtin_type != Variant::STRING;
@@ -2722,8 +2834,8 @@ void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscri
case Variant::VECTOR2I:
case Variant::VECTOR3:
case Variant::VECTOR3I:
- case Variant::TRANSFORM:
case Variant::TRANSFORM2D:
+ case Variant::TRANSFORM3D:
error = index_type.builtin_type != Variant::INT && index_type.builtin_type != Variant::FLOAT &&
index_type.builtin_type != Variant::STRING;
break;
@@ -2790,7 +2902,7 @@ void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscri
case Variant::PACKED_FLOAT64_ARRAY:
case Variant::VECTOR2:
case Variant::VECTOR3:
- case Variant::QUAT:
+ case Variant::QUATERNION:
result_type.builtin_type = Variant::FLOAT;
break;
// Return Color.
@@ -2819,7 +2931,7 @@ void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscri
result_type.builtin_type = Variant::VECTOR3;
break;
// Depends on the index.
- case Variant::TRANSFORM:
+ case Variant::TRANSFORM3D:
case Variant::PLANE:
case Variant::COLOR:
case Variant::DICTIONARY:
@@ -3010,8 +3122,8 @@ GDScriptParser::DataType GDScriptAnalyzer::type_from_variant(const Variant &p_va
GDScriptParser::ClassNode *found = ref->get_parser()->head;
// It should be okay to assume this exists, since we have a complete script already.
- for (const List<StringName>::Element *E = class_chain.front(); E; E = E->next()) {
- found = found->get_member(E->get()).m_class;
+ for (const StringName &E : class_chain) {
+ found = found->get_member(E).m_class;
}
result.class_type = found;
@@ -3107,9 +3219,9 @@ bool GDScriptAnalyzer::get_function_signature(GDScriptParser::Node *p_source, GD
List<MethodInfo> methods;
dummy.get_method_list(&methods);
- for (const List<MethodInfo>::Element *E = methods.front(); E != nullptr; E = E->next()) {
- if (E->get().name == p_function) {
- return function_signature_from_info(E->get(), r_return_type, r_par_types, r_default_arg_count, r_static, r_vararg);
+ for (const MethodInfo &E : methods) {
+ if (E.name == p_function) {
+ return function_signature_from_info(E, r_return_type, r_par_types, r_default_arg_count, r_static, r_vararg);
}
}
@@ -3206,8 +3318,8 @@ bool GDScriptAnalyzer::function_signature_from_info(const MethodInfo &p_info, GD
r_default_arg_count = p_info.default_arguments.size();
r_vararg = (p_info.flags & METHOD_FLAG_VARARG) != 0;
- for (const List<PropertyInfo>::Element *E = p_info.arguments.front(); E != nullptr; E = E->next()) {
- r_par_types.push_back(type_from_property(E->get()));
+ for (const PropertyInfo &E : p_info.arguments) {
+ r_par_types.push_back(type_from_property(E));
}
return true;
}
@@ -3215,8 +3327,8 @@ bool GDScriptAnalyzer::function_signature_from_info(const MethodInfo &p_info, GD
bool GDScriptAnalyzer::validate_call_arg(const MethodInfo &p_method, const GDScriptParser::CallNode *p_call) {
List<GDScriptParser::DataType> arg_types;
- for (const List<PropertyInfo>::Element *E = p_method.arguments.front(); E != nullptr; E = E->next()) {
- arg_types.push_back(type_from_property(E->get()));
+ for (const PropertyInfo &E : p_method.arguments) {
+ arg_types.push_back(type_from_property(E));
}
return validate_call_arg(arg_types, p_method.default_arguments.size(), (p_method.flags & METHOD_FLAG_VARARG) != 0, p_call);
@@ -3332,6 +3444,7 @@ GDScriptParser::DataType GDScriptAnalyzer::get_operation_type(Variant::Operator
}
r_valid = true;
+ result.type_source = GDScriptParser::DataType::ANNOTATED_INFERRED;
result.kind = GDScriptParser::DataType::BUILTIN;
result.builtin_type = Variant::get_operator_return_type(p_operation, a_type, b_type);
@@ -3542,11 +3655,11 @@ Error GDScriptAnalyzer::resolve_program() {
List<String> parser_keys;
depended_parsers.get_key_list(&parser_keys);
- for (const List<String>::Element *E = parser_keys.front(); E != nullptr; E = E->next()) {
- if (depended_parsers[E->get()].is_null()) {
+ for (const String &E : parser_keys) {
+ if (depended_parsers[E].is_null()) {
return ERR_PARSE_ERROR;
}
- depended_parsers[E->get()]->raise_status(GDScriptParserRef::FULLY_SOLVED);
+ depended_parsers[E]->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 8430d3f4a5..8cd3fcf837 100644
--- a/modules/gdscript/gdscript_analyzer.h
+++ b/modules/gdscript/gdscript_analyzer.h
@@ -32,7 +32,7 @@
#define GDSCRIPT_ANALYZER_H
#include "core/object/object.h"
-#include "core/object/reference.h"
+#include "core/object/ref_counted.h"
#include "core/templates/set.h"
#include "gdscript_cache.h"
#include "gdscript_parser.h"
@@ -42,6 +42,7 @@ class GDScriptAnalyzer {
HashMap<String, Ref<GDScriptParserRef>> depended_parsers;
const GDScriptParser::EnumNode *current_enum = nullptr;
+ List<const GDScriptParser::LambdaNode *> lambda_stack;
Error resolve_inheritance(GDScriptParser::ClassNode *p_class, bool p_recursive = true);
GDScriptParser::DataType resolve_datatype(GDScriptParser::TypeNode *p_type);
@@ -82,6 +83,7 @@ class GDScriptAnalyzer {
void reduce_get_node(GDScriptParser::GetNodeNode *p_get_node);
void reduce_identifier(GDScriptParser::IdentifierNode *p_identifier, bool can_be_builtin = false);
void reduce_identifier_from_base(GDScriptParser::IdentifierNode *p_identifier, GDScriptParser::DataType *p_base = nullptr);
+ void reduce_lambda(GDScriptParser::LambdaNode *p_lambda);
void reduce_literal(GDScriptParser::LiteralNode *p_literal);
void reduce_preload(GDScriptParser::PreloadNode *p_preload);
void reduce_self(GDScriptParser::SelfNode *p_self);
diff --git a/modules/gdscript/gdscript_byte_codegen.cpp b/modules/gdscript/gdscript_byte_codegen.cpp
index 89c5f5482b..5958326315 100644
--- a/modules/gdscript/gdscript_byte_codegen.cpp
+++ b/modules/gdscript/gdscript_byte_codegen.cpp
@@ -85,10 +85,10 @@ uint32_t GDScriptByteCodeGenerator::add_temporary(const GDScriptDataType &p_type
case Variant::VECTOR3I:
case Variant::TRANSFORM2D:
case Variant::PLANE:
- case Variant::QUAT:
+ case Variant::QUATERNION:
case Variant::AABB:
case Variant::BASIS:
- case Variant::TRANSFORM:
+ case Variant::TRANSFORM3D:
case Variant::COLOR:
case Variant::STRING_NAME:
case Variant::NODE_PATH:
@@ -129,12 +129,6 @@ uint32_t GDScriptByteCodeGenerator::add_temporary(const GDScriptDataType &p_type
int idx = temporaries.size();
pool.push_back(idx);
temporaries.push_back(new_temp);
-
- // First time using this, so adjust to the proper type.
- if (temp_type != Variant::NIL) {
- Address addr(Address::TEMPORARY, idx, p_type);
- write_type_adjust(addr, temp_type);
- }
}
int slot = pool.front()->get();
pool.pop_front();
@@ -161,7 +155,7 @@ void GDScriptByteCodeGenerator::end_parameters() {
function->default_arguments.reverse();
}
-void GDScriptByteCodeGenerator::write_start(GDScript *p_script, const StringName &p_function_name, bool p_static, MultiplayerAPI::RPCMode p_rpc_mode, const GDScriptDataType &p_return_type) {
+void GDScriptByteCodeGenerator::write_start(GDScript *p_script, const StringName &p_function_name, bool p_static, MultiplayerAPI::RPCConfig p_rpc_config, const GDScriptDataType &p_return_type) {
function = memnew(GDScriptFunction);
debug_stack = EngineDebugger::is_active();
@@ -176,7 +170,7 @@ void GDScriptByteCodeGenerator::write_start(GDScript *p_script, const StringName
function->_static = p_static;
function->return_type = p_return_type;
- function->rpc_mode = p_rpc_mode;
+ function->rpc_config = p_rpc_config;
function->_argument_count = 0;
}
@@ -189,8 +183,12 @@ GDScriptFunction *GDScriptByteCodeGenerator::write_end() {
append(GDScriptFunction::OPCODE_END, 0);
for (int i = 0; i < temporaries.size(); i++) {
+ int stack_index = i + max_locals + RESERVED_STACK;
for (int j = 0; j < temporaries[i].bytecode_indices.size(); j++) {
- opcodes.write[temporaries[i].bytecode_indices[j]] = (i + max_locals + RESERVED_STACK) | (GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS);
+ opcodes.write[temporaries[i].bytecode_indices[j]] = stack_index | (GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS);
+ }
+ if (temporaries[i].type != Variant::NIL) {
+ function->temporary_slots[stack_index] = temporaries[i].type;
}
}
@@ -383,6 +381,18 @@ GDScriptFunction *GDScriptByteCodeGenerator::write_end() {
function->_methods_count = 0;
}
+ if (lambdas_map.size()) {
+ function->lambdas.resize(lambdas_map.size());
+ function->_lambdas_ptr = function->lambdas.ptrw();
+ function->_lambdas_count = lambdas_map.size();
+ for (const Map<GDScriptFunction *, int>::Element *E = lambdas_map.front(); E; E = E->next()) {
+ function->lambdas.write[E->get()] = E->key();
+ }
+ } else {
+ function->_lambdas_ptr = nullptr;
+ function->_lambdas_count = 0;
+ }
+
if (debug_stack) {
function->stack_debug = stack_debug;
}
@@ -448,8 +458,8 @@ void GDScriptByteCodeGenerator::write_type_adjust(const Address &p_target, Varia
case Variant::PLANE:
append(GDScriptFunction::OPCODE_TYPE_ADJUST_PLANE, 1);
break;
- case Variant::QUAT:
- append(GDScriptFunction::OPCODE_TYPE_ADJUST_QUAT, 1);
+ case Variant::QUATERNION:
+ append(GDScriptFunction::OPCODE_TYPE_ADJUST_QUATERNION, 1);
break;
case Variant::AABB:
append(GDScriptFunction::OPCODE_TYPE_ADJUST_AABB, 1);
@@ -457,7 +467,7 @@ void GDScriptByteCodeGenerator::write_type_adjust(const Address &p_target, Varia
case Variant::BASIS:
append(GDScriptFunction::OPCODE_TYPE_ADJUST_BASIS, 1);
break;
- case Variant::TRANSFORM:
+ case Variant::TRANSFORM3D:
append(GDScriptFunction::OPCODE_TYPE_ADJUST_TRANSFORM, 1);
break;
case Variant::COLOR:
@@ -544,6 +554,14 @@ void GDScriptByteCodeGenerator::write_unary_operator(const Address &p_target, Va
void GDScriptByteCodeGenerator::write_binary_operator(const Address &p_target, Variant::Operator p_operator, const Address &p_left_operand, const Address &p_right_operand) {
if (HAS_BUILTIN_TYPE(p_left_operand) && HAS_BUILTIN_TYPE(p_right_operand)) {
+ if (p_target.mode == Address::TEMPORARY) {
+ Variant::Type result_type = Variant::get_operator_return_type(p_operator, p_left_operand.type.builtin_type, p_right_operand.type.builtin_type);
+ Variant::Type temp_type = temporaries[p_target.address].type;
+ if (result_type != temp_type) {
+ write_type_adjust(p_target, result_type);
+ }
+ }
+
// Gather specific operator.
Variant::ValidatedOperatorEvaluator op_func = Variant::get_validated_operator_evaluator(p_operator, p_left_operand.type.builtin_type, p_right_operand.type.builtin_type);
@@ -769,63 +787,43 @@ void GDScriptByteCodeGenerator::write_get_member(const Address &p_target, const
append(p_name);
}
-void GDScriptByteCodeGenerator::write_assign(const Address &p_target, const Address &p_source) {
- if (p_target.type.has_type && !p_source.type.has_type) {
- // Typed assignment.
- switch (p_target.type.kind) {
- case GDScriptDataType::BUILTIN: {
- if (p_target.type.builtin_type == Variant::ARRAY && p_target.type.has_container_element_type()) {
- append(GDScriptFunction::OPCODE_ASSIGN_TYPED_ARRAY, 2);
- append(p_target);
- append(p_source);
- } else {
- append(GDScriptFunction::OPCODE_ASSIGN_TYPED_BUILTIN, 2);
- append(p_target);
- append(p_source);
- append(p_target.type.builtin_type);
- }
- } break;
- case GDScriptDataType::NATIVE: {
- int class_idx = GDScriptLanguage::get_singleton()->get_global_map()[p_target.type.native_type];
- Variant nc = GDScriptLanguage::get_singleton()->get_global_array()[class_idx];
- class_idx = get_constant_pos(nc) | (GDScriptFunction::ADDR_TYPE_CONSTANT << GDScriptFunction::ADDR_BITS);
- append(GDScriptFunction::OPCODE_ASSIGN_TYPED_NATIVE, 3);
- append(p_target);
- append(p_source);
- append(class_idx);
- } break;
- case GDScriptDataType::SCRIPT:
- case GDScriptDataType::GDSCRIPT: {
- Variant script = p_target.type.script_type;
- int idx = get_constant_pos(script) | (GDScriptFunction::ADDR_TYPE_CONSTANT << GDScriptFunction::ADDR_BITS);
-
- append(GDScriptFunction::OPCODE_ASSIGN_TYPED_SCRIPT, 3);
+void GDScriptByteCodeGenerator::write_assign_with_conversion(const Address &p_target, const Address &p_source) {
+ switch (p_target.type.kind) {
+ case GDScriptDataType::BUILTIN: {
+ if (p_target.type.builtin_type == Variant::ARRAY && p_target.type.has_container_element_type()) {
+ append(GDScriptFunction::OPCODE_ASSIGN_TYPED_ARRAY, 2);
append(p_target);
append(p_source);
- append(idx);
- } break;
- default: {
- ERR_PRINT("Compiler bug: unresolved assign.");
-
- // Shouldn't get here, but fail-safe to a regular assignment
- append(GDScriptFunction::OPCODE_ASSIGN, 2);
+ } else {
+ append(GDScriptFunction::OPCODE_ASSIGN_TYPED_BUILTIN, 2);
append(p_target);
append(p_source);
+ append(p_target.type.builtin_type);
}
- }
- } else {
- if (p_target.type.kind == GDScriptDataType::BUILTIN && p_target.type.builtin_type == Variant::ARRAY && p_target.type.has_container_element_type()) {
- append(GDScriptFunction::OPCODE_ASSIGN_TYPED_ARRAY, 2);
+ } break;
+ case GDScriptDataType::NATIVE: {
+ int class_idx = GDScriptLanguage::get_singleton()->get_global_map()[p_target.type.native_type];
+ Variant nc = GDScriptLanguage::get_singleton()->get_global_array()[class_idx];
+ class_idx = get_constant_pos(nc) | (GDScriptFunction::ADDR_TYPE_CONSTANT << GDScriptFunction::ADDR_BITS);
+ append(GDScriptFunction::OPCODE_ASSIGN_TYPED_NATIVE, 3);
append(p_target);
append(p_source);
- } else if (p_target.type.kind == GDScriptDataType::BUILTIN && p_source.type.kind == GDScriptDataType::BUILTIN && p_target.type.builtin_type != p_source.type.builtin_type) {
- // Need conversion..
- append(GDScriptFunction::OPCODE_ASSIGN_TYPED_BUILTIN, 2);
+ append(class_idx);
+ } break;
+ case GDScriptDataType::SCRIPT:
+ case GDScriptDataType::GDSCRIPT: {
+ Variant script = p_target.type.script_type;
+ int idx = get_constant_pos(script) | (GDScriptFunction::ADDR_TYPE_CONSTANT << GDScriptFunction::ADDR_BITS);
+
+ append(GDScriptFunction::OPCODE_ASSIGN_TYPED_SCRIPT, 3);
append(p_target);
append(p_source);
- append(p_target.type.builtin_type);
- } else {
- // Either untyped assignment or already type-checked by the parser
+ append(idx);
+ } break;
+ default: {
+ ERR_PRINT("Compiler bug: unresolved assign.");
+
+ // Shouldn't get here, but fail-safe to a regular assignment
append(GDScriptFunction::OPCODE_ASSIGN, 2);
append(p_target);
append(p_source);
@@ -833,6 +831,24 @@ void GDScriptByteCodeGenerator::write_assign(const Address &p_target, const Addr
}
}
+void GDScriptByteCodeGenerator::write_assign(const Address &p_target, const Address &p_source) {
+ if (p_target.type.kind == GDScriptDataType::BUILTIN && p_target.type.builtin_type == Variant::ARRAY && p_target.type.has_container_element_type()) {
+ append(GDScriptFunction::OPCODE_ASSIGN_TYPED_ARRAY, 2);
+ append(p_target);
+ append(p_source);
+ } else if (p_target.type.kind == GDScriptDataType::BUILTIN && p_source.type.kind == GDScriptDataType::BUILTIN && p_target.type.builtin_type != p_source.type.builtin_type) {
+ // Need conversion.
+ append(GDScriptFunction::OPCODE_ASSIGN_TYPED_BUILTIN, 2);
+ append(p_target);
+ append(p_source);
+ append(p_target.type.builtin_type);
+ } else {
+ append(GDScriptFunction::OPCODE_ASSIGN, 2);
+ append(p_target);
+ append(p_source);
+ }
+}
+
void GDScriptByteCodeGenerator::write_assign_true(const Address &p_target) {
append(GDScriptFunction::OPCODE_ASSIGN_TRUE, 1);
append(p_target);
@@ -1005,6 +1021,56 @@ void GDScriptByteCodeGenerator::write_call_builtin_type(const Address &p_target,
append(Variant::get_validated_builtin_method(p_type, p_method));
}
+void GDScriptByteCodeGenerator::write_call_builtin_type_static(const Address &p_target, Variant::Type p_type, const StringName &p_method, const Vector<Address> &p_arguments) {
+ bool is_validated = false;
+
+ // Check if all types are correct.
+ if (Variant::is_builtin_method_vararg(p_type, p_method)) {
+ is_validated = true; // Vararg works fine with any argument, since they can be any type.
+ } else if (p_arguments.size() == Variant::get_builtin_method_argument_count(p_type, p_method)) {
+ bool all_types_exact = true;
+ for (int i = 0; i < p_arguments.size(); i++) {
+ if (!IS_BUILTIN_TYPE(p_arguments[i], Variant::get_builtin_method_argument_type(p_type, p_method, i))) {
+ all_types_exact = false;
+ break;
+ }
+ }
+
+ is_validated = all_types_exact;
+ }
+
+ if (!is_validated) {
+ // Perform regular call.
+ append(GDScriptFunction::OPCODE_CALL_BUILTIN_STATIC, p_arguments.size() + 1);
+ for (int i = 0; i < p_arguments.size(); i++) {
+ append(p_arguments[i]);
+ }
+ append(p_target);
+ append(p_type);
+ append(p_method);
+ append(p_arguments.size());
+ return;
+ }
+
+ if (p_target.mode == Address::TEMPORARY) {
+ Variant::Type result_type = Variant::get_builtin_method_return_type(p_type, p_method);
+ Variant::Type temp_type = temporaries[p_target.address].type;
+ if (result_type != temp_type) {
+ write_type_adjust(p_target, result_type);
+ }
+ }
+
+ append(GDScriptFunction::OPCODE_CALL_BUILTIN_TYPE_VALIDATED, 2 + p_arguments.size());
+
+ for (int i = 0; i < p_arguments.size(); i++) {
+ append(p_arguments[i]);
+ }
+ append(Address()); // No base since it's static.
+ append(p_target);
+ append(p_arguments.size());
+ append(Variant::get_validated_builtin_method(p_type, p_method));
+}
+
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++) {
@@ -1042,12 +1108,12 @@ void GDScriptByteCodeGenerator::write_call_ptrcall(const Address &p_target, cons
CASE_TYPE(PLANE);
CASE_TYPE(AABB);
CASE_TYPE(BASIS);
- CASE_TYPE(TRANSFORM);
+ CASE_TYPE(TRANSFORM3D);
CASE_TYPE(COLOR);
CASE_TYPE(STRING_NAME);
CASE_TYPE(NODE_PATH);
CASE_TYPE(RID);
- CASE_TYPE(QUAT);
+ CASE_TYPE(QUATERNION);
CASE_TYPE(OBJECT);
CASE_TYPE(CALLABLE);
CASE_TYPE(SIGNAL);
@@ -1118,6 +1184,17 @@ 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());
+ for (int i = 0; i < p_captures.size(); i++) {
+ append(p_captures[i]);
+ }
+
+ append(p_target);
+ append(p_captures.size());
+ append(p_function);
+}
+
void GDScriptByteCodeGenerator::write_construct(const Address &p_target, Variant::Type p_type, const Vector<Address> &p_arguments) {
// Try to find an appropriate constructor.
bool all_have_type = true;
@@ -1382,8 +1459,8 @@ void GDScriptByteCodeGenerator::write_endfor() {
}
// Patch break statements.
- for (const List<int>::Element *E = current_breaks_to_patch.back()->get().front(); E; E = E->next()) {
- patch_jump(E->get());
+ for (const int &E : current_breaks_to_patch.back()->get()) {
+ patch_jump(E);
}
current_breaks_to_patch.pop_back();
@@ -1417,8 +1494,8 @@ void GDScriptByteCodeGenerator::write_endwhile() {
while_jmp_addrs.pop_back();
// Patch break statements.
- for (const List<int>::Element *E = current_breaks_to_patch.back()->get().front(); E; E = E->next()) {
- patch_jump(E->get());
+ for (const int &E : current_breaks_to_patch.back()->get()) {
+ patch_jump(E);
}
current_breaks_to_patch.pop_back();
}
@@ -1429,8 +1506,8 @@ void GDScriptByteCodeGenerator::start_match() {
void GDScriptByteCodeGenerator::start_match_branch() {
// Patch continue statements.
- for (const List<int>::Element *E = match_continues_to_patch.back()->get().front(); E; E = E->next()) {
- patch_jump(E->get());
+ for (const int &E : match_continues_to_patch.back()->get()) {
+ patch_jump(E);
}
match_continues_to_patch.pop_back();
// Start a new list for next branch.
@@ -1439,8 +1516,8 @@ void GDScriptByteCodeGenerator::start_match_branch() {
void GDScriptByteCodeGenerator::end_match() {
// Patch continue statements.
- for (const List<int>::Element *E = match_continues_to_patch.back()->get().front(); E; E = E->next()) {
- patch_jump(E->get());
+ for (const int &E : match_continues_to_patch.back()->get()) {
+ patch_jump(E);
}
match_continues_to_patch.pop_back();
}
diff --git a/modules/gdscript/gdscript_byte_codegen.h b/modules/gdscript/gdscript_byte_codegen.h
index 17d681d7bb..3d6fb291ad 100644
--- a/modules/gdscript/gdscript_byte_codegen.h
+++ b/modules/gdscript/gdscript_byte_codegen.h
@@ -93,6 +93,7 @@ class GDScriptByteCodeGenerator : public GDScriptCodeGenerator {
Map<Variant::ValidatedUtilityFunction, int> utilities_map;
Map<GDScriptUtilityFunctions::FunctionPtr, int> gds_utilities_map;
Map<MethodBind *, int> method_bind_map;
+ Map<GDScriptFunction *, int> lambdas_map;
// Lists since these can be nested.
List<int> if_jmp_addrs;
@@ -293,6 +294,15 @@ class GDScriptByteCodeGenerator : public GDScriptCodeGenerator {
return pos;
}
+ int get_lambda_function_pos(GDScriptFunction *p_lambda_function) {
+ if (lambdas_map.has(p_lambda_function)) {
+ return lambdas_map[p_lambda_function];
+ }
+ int pos = lambdas_map.size();
+ lambdas_map[p_lambda_function] = pos;
+ return pos;
+ }
+
void alloc_ptrcall(int p_params) {
if (p_params >= ptrcall_max) {
ptrcall_max = p_params;
@@ -386,6 +396,10 @@ class GDScriptByteCodeGenerator : public GDScriptCodeGenerator {
opcodes.push_back(get_method_bind_pos(p_method));
}
+ void append(GDScriptFunction *p_lambda_function) {
+ opcodes.push_back(get_lambda_function_pos(p_lambda_function));
+ }
+
void patch_jump(int p_address) {
opcodes.write[p_address] = opcodes.size();
}
@@ -405,7 +419,7 @@ public:
virtual void start_block() override;
virtual void end_block() override;
- virtual void write_start(GDScript *p_script, const StringName &p_function_name, bool p_static, MultiplayerAPI::RPCMode p_rpc_mode, const GDScriptDataType &p_return_type) override;
+ virtual void write_start(GDScript *p_script, const StringName &p_function_name, bool p_static, MultiplayerAPI::RPCConfig p_rpc_config, const GDScriptDataType &p_return_type) override;
virtual GDScriptFunction *write_end() override;
#ifdef DEBUG_ENABLED
@@ -436,6 +450,7 @@ public:
virtual void write_set_member(const Address &p_value, const StringName &p_name) override;
virtual void write_get_member(const Address &p_target, const StringName &p_name) override;
virtual void write_assign(const Address &p_target, const Address &p_source) override;
+ virtual void write_assign_with_conversion(const Address &p_target, const Address &p_source) override;
virtual void write_assign_true(const Address &p_target) override;
virtual void write_assign_false(const Address &p_target) override;
virtual void write_assign_default_parameter(const Address &p_dst, const Address &p_src) override;
@@ -447,11 +462,13 @@ public:
virtual void write_call_utility(const Address &p_target, const StringName &p_function, const Vector<Address> &p_arguments) override;
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_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_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 113d36be98..1a844bf241 100644
--- a/modules/gdscript/gdscript_cache.cpp
+++ b/modules/gdscript/gdscript_cache.cpp
@@ -30,7 +30,7 @@
#include "gdscript_cache.h"
-#include "core/os/file_access.h"
+#include "core/io/file_access.h"
#include "core/templates/vector.h"
#include "gdscript.h"
#include "gdscript_analyzer.h"
@@ -134,7 +134,7 @@ Ref<GDScriptParserRef> GDScriptCache::get_parser(const String &p_path, GDScriptP
return ref;
}
GDScriptParser *parser = memnew(GDScriptParser);
- ref.instance();
+ ref.instantiate();
ref->parser = parser;
ref->path = p_path;
singleton->parser_map[p_path] = ref.ptr();
@@ -153,9 +153,9 @@ String GDScriptCache::get_source_code(const String &p_path) {
ERR_FAIL_COND_V(err, "");
}
- int len = f->get_len();
+ uint64_t len = f->get_length();
source_file.resize(len + 1);
- int r = f->get_buffer(source_file.ptrw(), len);
+ uint64_t r = f->get_buffer(source_file.ptrw(), len);
f->close();
ERR_FAIL_COND_V(r != len, "");
source_file.write[len] = 0;
@@ -180,7 +180,7 @@ Ref<GDScript> GDScriptCache::get_shallow_script(const String &p_path, const Stri
}
Ref<GDScript> script;
- script.instance();
+ script.instantiate();
script->set_path(p_path, true);
script->set_script_path(p_path);
script->load_source_code(p_path);
diff --git a/modules/gdscript/gdscript_cache.h b/modules/gdscript/gdscript_cache.h
index d1d2a2abbf..943638d29f 100644
--- a/modules/gdscript/gdscript_cache.h
+++ b/modules/gdscript/gdscript_cache.h
@@ -31,7 +31,7 @@
#ifndef GDSCRIPT_CACHE_H
#define GDSCRIPT_CACHE_H
-#include "core/object/reference.h"
+#include "core/object/ref_counted.h"
#include "core/os/mutex.h"
#include "core/templates/hash_map.h"
#include "core/templates/set.h"
@@ -40,7 +40,7 @@
class GDScriptAnalyzer;
class GDScriptParser;
-class GDScriptParserRef : public Reference {
+class GDScriptParserRef : public RefCounted {
public:
enum Status {
EMPTY,
diff --git a/modules/gdscript/gdscript_codegen.h b/modules/gdscript/gdscript_codegen.h
index b377beefdb..ecc86c37f3 100644
--- a/modules/gdscript/gdscript_codegen.h
+++ b/modules/gdscript/gdscript_codegen.h
@@ -80,7 +80,7 @@ public:
virtual void start_block() = 0;
virtual void end_block() = 0;
- virtual void write_start(GDScript *p_script, const StringName &p_function_name, bool p_static, MultiplayerAPI::RPCMode p_rpc_mode, const GDScriptDataType &p_return_type) = 0;
+ virtual void write_start(GDScript *p_script, const StringName &p_function_name, bool p_static, MultiplayerAPI::RPCConfig p_rpc_config, const GDScriptDataType &p_return_type) = 0;
virtual GDScriptFunction *write_end() = 0;
#ifdef DEBUG_ENABLED
@@ -111,6 +111,7 @@ public:
virtual void write_set_member(const Address &p_value, const StringName &p_name) = 0;
virtual void write_get_member(const Address &p_target, const StringName &p_name) = 0;
virtual void write_assign(const Address &p_target, const Address &p_source) = 0;
+ virtual void write_assign_with_conversion(const Address &p_target, const Address &p_source) = 0;
virtual void write_assign_true(const Address &p_target) = 0;
virtual void write_assign_false(const Address &p_target) = 0;
virtual void write_assign_default_parameter(const Address &dst, const Address &src) = 0;
@@ -122,11 +123,13 @@ public:
virtual void write_call_utility(const Address &p_target, const StringName &p_function, const Vector<Address> &p_arguments) = 0;
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_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_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 9b718db7cf..fe827a5b72 100644
--- a/modules/gdscript/gdscript_compiler.cpp
+++ b/modules/gdscript/gdscript_compiler.cpp
@@ -107,7 +107,7 @@ GDScriptDataType GDScriptCompiler::_gdtype_from_datatype(const GDScriptParser::D
// Locate class by constructing the path to it and following that path
GDScriptParser::ClassNode *class_type = p_datatype.class_type;
if (class_type) {
- if (class_type->fqcn.begins_with(main_script->path) || (!main_script->name.is_empty() && class_type->fqcn.begins_with(main_script->name))) {
+ if ((!main_script->path.is_empty() && class_type->fqcn.begins_with(main_script->path)) || (!main_script->name.is_empty() && class_type->fqcn.begins_with(main_script->name))) {
// Local class.
List<StringName> names;
while (class_type->outer) {
@@ -537,39 +537,44 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
const GDScriptParser::SubscriptNode *subscript = static_cast<const GDScriptParser::SubscriptNode *>(call->callee);
if (subscript->is_attribute) {
- GDScriptCodeGenerator::Address base = _parse_expression(codegen, r_error, subscript->base);
- if (r_error) {
- return GDScriptCodeGenerator::Address();
- }
- if (within_await) {
- gen->write_call_async(result, base, call->function_name, arguments);
- } else if (base.type.has_type && base.type.kind != GDScriptDataType::BUILTIN) {
- // Native method, use faster path.
- StringName class_name;
- if (base.type.kind == GDScriptDataType::NATIVE) {
- class_name = base.type.native_type;
- } else {
- class_name = base.type.native_type == StringName() ? base.type.script_type->get_instance_base_type() : base.type.native_type;
+ // 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 {
+ GDScriptCodeGenerator::Address base = _parse_expression(codegen, r_error, subscript->base);
+ if (r_error) {
+ return GDScriptCodeGenerator::Address();
}
- if (ClassDB::class_exists(class_name) && ClassDB::has_method(class_name, call->function_name)) {
- MethodBind *method = ClassDB::get_method(class_name, call->function_name);
- if (_have_exact_arguments(method, arguments)) {
- // Exact arguments, use ptrcall.
- gen->write_call_ptrcall(result, base, method, arguments);
+ if (within_await) {
+ gen->write_call_async(result, base, call->function_name, arguments);
+ } else if (base.type.has_type && base.type.kind != GDScriptDataType::BUILTIN) {
+ // Native method, use faster path.
+ StringName class_name;
+ if (base.type.kind == GDScriptDataType::NATIVE) {
+ class_name = base.type.native_type;
} else {
- // Not exact arguments, but still can use method bind call.
- gen->write_call_method_bind(result, base, method, arguments);
+ class_name = base.type.native_type == StringName() ? base.type.script_type->get_instance_base_type() : base.type.native_type;
}
+ if (ClassDB::class_exists(class_name) && ClassDB::has_method(class_name, call->function_name)) {
+ MethodBind *method = ClassDB::get_method(class_name, call->function_name);
+ if (_have_exact_arguments(method, arguments)) {
+ // Exact arguments, use ptrcall.
+ gen->write_call_ptrcall(result, base, method, arguments);
+ } else {
+ // Not exact arguments, but still can use method bind call.
+ gen->write_call_method_bind(result, base, method, arguments);
+ }
+ } else {
+ gen->write_call(result, base, call->function_name, arguments);
+ }
+ } else if (base.type.has_type && base.type.kind == GDScriptDataType::BUILTIN) {
+ gen->write_call_builtin_type(result, base, base.type.builtin_type, call->function_name, arguments);
} else {
gen->write_call(result, base, call->function_name, arguments);
}
- } else if (base.type.has_type && base.type.kind == GDScriptDataType::BUILTIN) {
- gen->write_call_builtin_type(result, base, base.type.builtin_type, call->function_name, arguments);
- } else {
- gen->write_call(result, base, call->function_name, arguments);
- }
- if (base.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
- gen->pop_temporary();
+ if (base.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
+ gen->pop_temporary();
+ }
}
} else {
_set_error("Cannot call something that isn't a function.", call->callee);
@@ -966,6 +971,9 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
} else {
gen->write_set(prev_base, key, assigned);
}
+ if (key.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
+ gen->pop_temporary();
+ }
if (assigned.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
gen->pop_temporary();
}
@@ -973,8 +981,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
assigned = prev_base;
// Set back the values into their bases.
- for (List<ChainInfo>::Element *E = set_chain.front(); E; E = E->next()) {
- const ChainInfo &info = E->get();
+ for (const ChainInfo &info : set_chain) {
if (!info.is_named) {
gen->write_set(info.base, info.key, assigned);
if (info.key.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
@@ -1076,7 +1083,11 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
gen->write_call(GDScriptCodeGenerator::Address(), GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::SELF), setter_function, args);
} else {
// Just assign.
- gen->write_assign(target, op_result);
+ if (assignment->use_conversion_assign) {
+ gen->write_assign_with_conversion(target, op_result);
+ } else {
+ gen->write_assign(target, op_result);
+ }
}
if (op_result.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
@@ -1091,6 +1102,34 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
}
return GDScriptCodeGenerator::Address(); // Assignment does not return a value.
} break;
+ case GDScriptParser::Node::LAMBDA: {
+ const GDScriptParser::LambdaNode *lambda = static_cast<const GDScriptParser::LambdaNode *>(p_expression);
+ GDScriptCodeGenerator::Address result = codegen.add_temporary(_gdtype_from_datatype(lambda->get_datatype()));
+
+ Vector<GDScriptCodeGenerator::Address> captures;
+ captures.resize(lambda->captures.size());
+ for (int i = 0; i < lambda->captures.size(); i++) {
+ captures.write[i] = _parse_expression(codegen, r_error, lambda->captures[i]);
+ if (r_error) {
+ return GDScriptCodeGenerator::Address();
+ }
+ }
+
+ GDScriptFunction *function = _parse_function(r_error, codegen.script, codegen.class_node, lambda->function, false, true);
+ if (r_error) {
+ return GDScriptCodeGenerator::Address();
+ }
+
+ gen->write_lambda(result, function, captures);
+
+ for (int i = 0; i < captures.size(); i++) {
+ if (captures[i].mode == GDScriptCodeGenerator::Address::TEMPORARY) {
+ gen->pop_temporary();
+ }
+ }
+
+ return result;
+ } break;
default: {
ERR_FAIL_V_MSG(GDScriptCodeGenerator::Address(), "Bug in bytecode compiler, unexpected node in parse tree while parsing expression."); // Unreachable code.
} break;
@@ -1756,7 +1795,11 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Sui
if (error) {
return error;
}
- gen->write_assign(local, src_address);
+ if (lv->use_conversion_assign) {
+ gen->write_assign_with_conversion(local, src_address);
+ } else {
+ gen->write_assign(local, src_address);
+ }
if (src_address.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
codegen.generator->pop_temporary();
}
@@ -1804,8 +1847,8 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Sui
return OK;
}
-Error GDScriptCompiler::_parse_function(GDScript *p_script, const GDScriptParser::ClassNode *p_class, const GDScriptParser::FunctionNode *p_func, bool p_for_ready) {
- Error error = OK;
+GDScriptFunction *GDScriptCompiler::_parse_function(Error &r_error, GDScript *p_script, const GDScriptParser::ClassNode *p_class, const GDScriptParser::FunctionNode *p_func, bool p_for_ready, bool p_for_lambda) {
+ r_error = OK;
CodeGen codegen;
codegen.generator = memnew(GDScriptByteCodeGenerator);
@@ -1815,16 +1858,20 @@ Error GDScriptCompiler::_parse_function(GDScript *p_script, const GDScriptParser
StringName func_name;
bool is_static = false;
- MultiplayerAPI::RPCMode rpc_mode = MultiplayerAPI::RPC_MODE_DISABLED;
+ MultiplayerAPI::RPCConfig rpc_config;
GDScriptDataType return_type;
return_type.has_type = true;
return_type.kind = GDScriptDataType::BUILTIN;
return_type.builtin_type = Variant::NIL;
if (p_func) {
- func_name = p_func->identifier->name;
+ if (p_func->identifier) {
+ func_name = p_func->identifier->name;
+ } else {
+ func_name = "<anonymous lambda>";
+ }
is_static = p_func->is_static;
- rpc_mode = p_func->rpc_mode;
+ rpc_config = p_func->rpc_config;
return_type = _gdtype_from_datatype(p_func->get_datatype(), p_script);
} else {
if (p_for_ready) {
@@ -1835,7 +1882,7 @@ Error GDScriptCompiler::_parse_function(GDScript *p_script, const GDScriptParser
}
codegen.function_name = func_name;
- codegen.generator->write_start(p_script, func_name, is_static, rpc_mode, return_type);
+ codegen.generator->write_start(p_script, func_name, is_static, rpc_config, return_type);
int optional_parameters = 0;
@@ -1853,11 +1900,11 @@ Error GDScriptCompiler::_parse_function(GDScript *p_script, const GDScriptParser
}
// Parse initializer if applies.
- bool is_implicit_initializer = !p_for_ready && !p_func;
- bool is_initializer = p_func && String(p_func->identifier->name) == GDScriptLanguage::get_singleton()->strings._init;
- bool is_for_ready = p_for_ready || (p_func && String(p_func->identifier->name) == "_ready");
+ bool is_implicit_initializer = !p_for_ready && !p_func && !p_for_lambda;
+ bool is_initializer = p_func && !p_for_lambda && String(p_func->identifier->name) == GDScriptLanguage::get_singleton()->strings._init;
+ bool is_for_ready = p_for_ready || (p_func && !p_for_lambda && String(p_func->identifier->name) == "_ready");
- if (is_implicit_initializer || is_for_ready) {
+ if (!p_for_lambda && (is_implicit_initializer || is_for_ready)) {
// Initialize class fields.
for (int i = 0; i < p_class->members.size(); i++) {
if (p_class->members[i].type != GDScriptParser::ClassNode::Member::VARIABLE) {
@@ -1884,13 +1931,17 @@ Error GDScriptCompiler::_parse_function(GDScript *p_script, const GDScriptParser
codegen.generator->write_construct_array(dst_address, Vector<GDScriptCodeGenerator::Address>());
}
}
- GDScriptCodeGenerator::Address src_address = _parse_expression(codegen, error, field->initializer, false, true);
- if (error) {
+ GDScriptCodeGenerator::Address src_address = _parse_expression(codegen, r_error, field->initializer, false, true);
+ if (r_error) {
memdelete(codegen.generator);
- return error;
+ return nullptr;
}
- codegen.generator->write_assign(dst_address, src_address);
+ if (field->use_conversion_assign) {
+ codegen.generator->write_assign_with_conversion(dst_address, src_address);
+ } else {
+ codegen.generator->write_assign(dst_address, src_address);
+ }
if (src_address.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
codegen.generator->pop_temporary();
}
@@ -1914,10 +1965,10 @@ Error GDScriptCompiler::_parse_function(GDScript *p_script, const GDScriptParser
codegen.generator->start_parameters();
for (int i = p_func->parameters.size() - optional_parameters; i < p_func->parameters.size(); i++) {
const GDScriptParser::ParameterNode *parameter = p_func->parameters[i];
- GDScriptCodeGenerator::Address src_addr = _parse_expression(codegen, error, parameter->default_value, true);
- if (error) {
+ GDScriptCodeGenerator::Address src_addr = _parse_expression(codegen, r_error, parameter->default_value, true);
+ if (r_error) {
memdelete(codegen.generator);
- return error;
+ return nullptr;
}
GDScriptCodeGenerator::Address dst_addr = codegen.parameters[parameter->identifier->name];
codegen.generator->write_assign_default_parameter(dst_addr, src_addr);
@@ -1928,10 +1979,10 @@ Error GDScriptCompiler::_parse_function(GDScript *p_script, const GDScriptParser
codegen.generator->end_parameters();
}
- Error err = _parse_block(codegen, p_func->body);
- if (err) {
+ r_error = _parse_block(codegen, p_func->body);
+ if (r_error) {
memdelete(codegen.generator);
- return err;
+ return nullptr;
}
}
@@ -1957,6 +2008,10 @@ Error GDScriptCompiler::_parse_function(GDScript *p_script, const GDScriptParser
signature += "::" + String(func_name);
}
+ if (p_for_lambda) {
+ signature += "(lambda)";
+ }
+
codegen.generator->set_signature(signature);
}
#endif
@@ -1964,8 +2019,10 @@ Error GDScriptCompiler::_parse_function(GDScript *p_script, const GDScriptParser
if (p_func) {
codegen.generator->set_initial_line(p_func->start_line);
#ifdef TOOLS_ENABLED
- p_script->member_lines[func_name] = p_func->start_line;
- p_script->doc_functions[func_name] = p_func->doc_description;
+ if (!p_for_lambda) {
+ p_script->member_lines[func_name] = p_func->start_line;
+ p_script->doc_functions[func_name] = p_func->doc_description;
+ }
#endif
} else {
codegen.generator->set_initial_line(0);
@@ -1994,11 +2051,13 @@ Error GDScriptCompiler::_parse_function(GDScript *p_script, const GDScriptParser
#endif
}
- p_script->member_functions[func_name] = gd_function;
+ if (!p_for_lambda) {
+ p_script->member_functions[func_name] = gd_function;
+ }
memdelete(codegen.generator);
- return OK;
+ return gd_function;
}
Error GDScriptCompiler::_parse_setter_getter(GDScript *p_script, const GDScriptParser::ClassNode *p_class, const GDScriptParser::VariableNode *p_variable, bool p_is_setter) {
@@ -2028,7 +2087,7 @@ Error GDScriptCompiler::_parse_setter_getter(GDScript *p_script, const GDScriptP
return_type = _gdtype_from_datatype(p_variable->get_datatype(), p_script);
}
- codegen.generator->write_start(p_script, func_name, false, p_variable->rpc_mode, return_type);
+ codegen.generator->write_start(p_script, func_name, false, MultiplayerAPI::RPCConfig(), return_type);
if (p_is_setter) {
uint32_t par_addr = codegen.generator->add_parameter(p_variable->setter_parameter->name, false, _gdtype_from_datatype(p_variable->get_datatype()));
@@ -2163,7 +2222,7 @@ Error GDScriptCompiler::_parse_class_level(GDScript *p_script, const GDScriptPar
if (err) {
return err;
}
- if (base.is_null() && !base->is_valid()) {
+ if (base.is_null() || !base->is_valid()) {
return ERR_COMPILATION_FAILED;
}
}
@@ -2208,7 +2267,6 @@ Error GDScriptCompiler::_parse_class_level(GDScript *p_script, const GDScriptPar
}
break;
}
- minfo.rpc_mode = variable->rpc_mode;
minfo.data_type = _gdtype_from_datatype(variable->get_datatype(), p_script);
PropertyInfo prop_info = minfo.data_type;
@@ -2222,9 +2280,10 @@ Error GDScriptCompiler::_parse_class_level(GDScript *p_script, const GDScriptPar
}
prop_info.hint = export_info.hint;
prop_info.hint_string = export_info.hint_string;
- prop_info.usage = export_info.usage;
+ prop_info.usage = export_info.usage | PROPERTY_USAGE_SCRIPT_VARIABLE;
+ } else {
+ prop_info.usage = PROPERTY_USAGE_SCRIPT_VARIABLE;
}
- prop_info.usage |= PROPERTY_USAGE_SCRIPT_VARIABLE;
#ifdef TOOLS_ENABLED
p_script->doc_variables[name] = variable->doc_description;
#endif
@@ -2391,7 +2450,8 @@ Error GDScriptCompiler::_parse_class_blocks(GDScript *p_script, const GDScriptPa
if (!has_ready && function->identifier->name == "_ready") {
has_ready = true;
}
- Error err = _parse_function(p_script, p_class, function);
+ Error err = OK;
+ _parse_function(err, p_script, p_class, function);
if (err) {
return err;
}
@@ -2416,7 +2476,8 @@ Error GDScriptCompiler::_parse_class_blocks(GDScript *p_script, const GDScriptPa
{
// Create an implicit constructor in any case.
- Error err = _parse_function(p_script, p_class, nullptr);
+ Error err = OK;
+ _parse_function(err, p_script, p_class, nullptr);
if (err) {
return err;
}
@@ -2424,7 +2485,8 @@ Error GDScriptCompiler::_parse_class_blocks(GDScript *p_script, const GDScriptPa
if (!has_ready && p_class->onready_used) {
//create a _ready constructor
- Error err = _parse_function(p_script, p_class, nullptr, true);
+ Error err = OK;
+ _parse_function(err, p_script, p_class, nullptr, true);
if (err) {
return err;
}
@@ -2448,7 +2510,7 @@ Error GDScriptCompiler::_parse_class_blocks(GDScript *p_script, const GDScriptPa
p_script->placeholders.erase(psi); //remove placeholder
GDScriptInstance *instance = memnew(GDScriptInstance);
- instance->base_ref = Object::cast_to<Reference>(E->get());
+ instance->base_ref_counted = Object::cast_to<RefCounted>(E->get());
instance->members.resize(p_script->member_indices.size());
instance->script = Ref<GDScript>(p_script);
instance->owner = E->get();
@@ -2523,7 +2585,7 @@ void GDScriptCompiler::_make_scripts(GDScript *p_script, const GDScriptParser::C
if (orphan_subclass.is_valid()) {
subclass = orphan_subclass;
} else {
- subclass.instance();
+ subclass.instantiate();
}
}
diff --git a/modules/gdscript/gdscript_compiler.h b/modules/gdscript/gdscript_compiler.h
index c405eadb07..7d5bee93ac 100644
--- a/modules/gdscript/gdscript_compiler.h
+++ b/modules/gdscript/gdscript_compiler.h
@@ -128,7 +128,7 @@ class GDScriptCompiler {
GDScriptCodeGenerator::Address _parse_match_pattern(CodeGen &codegen, Error &r_error, const GDScriptParser::PatternNode *p_pattern, const GDScriptCodeGenerator::Address &p_value_addr, const GDScriptCodeGenerator::Address &p_type_addr, const GDScriptCodeGenerator::Address &p_previous_test, bool p_is_first, bool p_is_nested);
void _add_locals_in_block(CodeGen &codegen, const GDScriptParser::SuiteNode *p_block);
Error _parse_block(CodeGen &codegen, const GDScriptParser::SuiteNode *p_block, bool p_add_locals = true);
- Error _parse_function(GDScript *p_script, const GDScriptParser::ClassNode *p_class, const GDScriptParser::FunctionNode *p_func, bool p_for_ready = false);
+ GDScriptFunction *_parse_function(Error &r_error, GDScript *p_script, const GDScriptParser::ClassNode *p_class, const GDScriptParser::FunctionNode *p_func, bool p_for_ready = false, bool p_for_lambda = false);
Error _parse_setter_getter(GDScript *p_script, const GDScriptParser::ClassNode *p_class, const GDScriptParser::VariableNode *p_variable, bool p_is_setter);
Error _parse_class_level(GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state);
Error _parse_class_blocks(GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state);
diff --git a/modules/gdscript/gdscript_disassembler.cpp b/modules/gdscript/gdscript_disassembler.cpp
index 0d0afcc741..1acb9ceddc 100644
--- a/modules/gdscript/gdscript_disassembler.cpp
+++ b/modules/gdscript/gdscript_disassembler.cpp
@@ -397,7 +397,7 @@ void GDScriptFunction::disassemble(const Vector<String> &p_code_lines) const {
text += DADDR(1 + argc);
text += " = ";
- text += "<unkown type>(";
+ text += "<unknown type>(";
for (int i = 0; i < argc; i++) {
if (i > 0) {
text += ", ";
@@ -542,6 +542,28 @@ void GDScriptFunction::disassemble(const Vector<String> &p_code_lines) const {
incr = 5 + argc;
} break;
+ case OPCODE_CALL_BUILTIN_STATIC: {
+ Variant::Type type = (Variant::Type)_code_ptr[ip + 1 + instr_var_args];
+ int argc = _code_ptr[ip + 3 + instr_var_args];
+
+ text += "call built-in method static ";
+ text += DADDR(1 + argc);
+ text += " = ";
+ text += Variant::get_type_name(type);
+ text += ".";
+ text += _global_names_ptr[_code_ptr[ip + 2 + instr_var_args]].operator String();
+ text += "(";
+
+ for (int i = 0; i < argc; i++) {
+ if (i > 0) {
+ text += ", ";
+ }
+ text += DADDR(1 + i);
+ }
+ text += ")";
+
+ incr += 5 + argc;
+ } break;
case OPCODE_CALL_PTRCALL_NO_RETURN: {
text += "call-ptrcall (no return) ";
@@ -598,12 +620,12 @@ void GDScriptFunction::disassemble(const Vector<String> &p_code_lines) const {
DISASSEMBLE_PTRCALL(PLANE);
DISASSEMBLE_PTRCALL(AABB);
DISASSEMBLE_PTRCALL(BASIS);
- DISASSEMBLE_PTRCALL(TRANSFORM);
+ DISASSEMBLE_PTRCALL(TRANSFORM3D);
DISASSEMBLE_PTRCALL(COLOR);
DISASSEMBLE_PTRCALL(STRING_NAME);
DISASSEMBLE_PTRCALL(NODE_PATH);
DISASSEMBLE_PTRCALL(RID);
- DISASSEMBLE_PTRCALL(QUAT);
+ DISASSEMBLE_PTRCALL(QUATERNION);
DISASSEMBLE_PTRCALL(OBJECT);
DISASSEMBLE_PTRCALL(CALLABLE);
DISASSEMBLE_PTRCALL(SIGNAL);
@@ -666,7 +688,7 @@ void GDScriptFunction::disassemble(const Vector<String> &p_code_lines) const {
int argc = _code_ptr[ip + 1 + instr_var_args];
text += DADDR(1 + argc) + " = ";
- text += "<unkown function>";
+ text += "<unknown function>";
text += "(";
for (int i = 0; i < argc; i++) {
@@ -721,7 +743,7 @@ void GDScriptFunction::disassemble(const Vector<String> &p_code_lines) const {
text += "await ";
text += DADDR(1);
- incr += 2;
+ incr = 2;
} break;
case OPCODE_AWAIT_RESUME: {
text += "await resume ";
@@ -729,6 +751,25 @@ void GDScriptFunction::disassemble(const Vector<String> &p_code_lines) const {
incr = 2;
} break;
+ case OPCODE_CREATE_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 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]);
@@ -916,7 +957,7 @@ void GDScriptFunction::disassemble(const Vector<String> &p_code_lines) const {
DISASSEMBLE_TYPE_ADJUST(VECTOR3I);
DISASSEMBLE_TYPE_ADJUST(TRANSFORM2D);
DISASSEMBLE_TYPE_ADJUST(PLANE);
- DISASSEMBLE_TYPE_ADJUST(QUAT);
+ DISASSEMBLE_TYPE_ADJUST(QUATERNION);
DISASSEMBLE_TYPE_ADJUST(AABB);
DISASSEMBLE_TYPE_ADJUST(BASIS);
DISASSEMBLE_TYPE_ADJUST(TRANSFORM);
diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp
index ae3b16a9d7..2e570d5a5b 100644
--- a/modules/gdscript/gdscript_editor.cpp
+++ b/modules/gdscript/gdscript_editor.cpp
@@ -32,7 +32,7 @@
#include "core/config/engine.h"
#include "core/core_constants.h"
-#include "core/os/file_access.h"
+#include "core/io/file_access.h"
#include "gdscript_analyzer.h"
#include "gdscript_compiler.h"
#include "gdscript_parser.h"
@@ -104,7 +104,7 @@ Ref<Script> GDScriptLanguage::get_template(const String &p_class_name, const Str
_template = _get_processed_template(_template, p_base_class_name);
Ref<GDScript> script;
- script.instance();
+ script.instantiate();
script->set_source_code(_template);
return script;
@@ -131,7 +131,7 @@ static void get_function_names_recursively(const GDScriptParser::ClassNode *p_cl
}
}
-bool GDScriptLanguage::validate(const String &p_script, int &r_line_error, int &r_col_error, String &r_test_error, const String &p_path, List<String> *r_functions, 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, Set<int> *r_safe_lines) const {
GDScriptParser parser;
GDScriptAnalyzer analyzer(&parser);
@@ -141,8 +141,8 @@ bool GDScriptLanguage::validate(const String &p_script, int &r_line_error, int &
}
#ifdef DEBUG_ENABLED
if (r_warnings) {
- for (const List<GDScriptWarning>::Element *E = parser.get_warnings().front(); E; E = E->next()) {
- const GDScriptWarning &warn = E->get();
+ for (const GDScriptWarning &E : parser.get_warnings()) {
+ const GDScriptWarning &warn = E;
ScriptLanguage::Warning w;
w.start_line = warn.start_line;
w.end_line = warn.end_line;
@@ -156,10 +156,16 @@ bool GDScriptLanguage::validate(const String &p_script, int &r_line_error, int &
}
#endif
if (err) {
- GDScriptParser::ParserError parse_error = parser.get_errors().front()->get();
- r_line_error = parse_error.line;
- r_col_error = parse_error.column;
- r_test_error = parse_error.message;
+ if (r_errors) {
+ for (const GDScriptParser::ParserError &E : parser.get_errors()) {
+ const GDScriptParser::ParserError &pe = E;
+ ScriptLanguage::ScriptError e;
+ e.line = pe.line;
+ e.column = pe.column;
+ e.message = pe.message;
+ r_errors->push_back(e);
+ }
+ }
return false;
} else {
const GDScriptParser::ClassNode *cl = parser.get_tree();
@@ -313,9 +319,9 @@ void GDScriptLanguage::debug_get_stack_level_locals(int p_level, List<String> *p
List<Pair<StringName, int>> locals;
f->debug_get_stack_member_state(*_call_stack[l].line, &locals);
- for (List<Pair<StringName, int>>::Element *E = locals.front(); E; E = E->next()) {
- p_locals->push_back(E->get().first);
- p_values->push_back(_call_stack[l].stack[E->get().second]);
+ for (const Pair<StringName, int> &E : locals) {
+ p_locals->push_back(E.first);
+ p_values->push_back(_call_stack[l].stack[E.second]);
}
}
@@ -415,8 +421,8 @@ void GDScriptLanguage::get_public_functions(List<MethodInfo> *p_functions) const
List<StringName> functions;
GDScriptUtilityFunctions::get_function_list(&functions);
- for (const List<StringName>::Element *E = functions.front(); E; E = E->next()) {
- p_functions->push_back(GDScriptUtilityFunctions::get_function_info(E->get()));
+ for (const StringName &E : functions) {
+ p_functions->push_back(GDScriptUtilityFunctions::get_function_info(E));
}
// Not really "functions", but show in documentation.
@@ -451,12 +457,12 @@ void GDScriptLanguage::get_public_constants(List<Pair<String, Variant>> *p_const
Pair<String, Variant> infinity;
infinity.first = "INF";
- infinity.second = Math_INF;
+ infinity.second = INFINITY;
p_constants->push_back(infinity);
Pair<String, Variant> nan;
nan.first = "NAN";
- nan.second = Math_NAN;
+ nan.second = NAN;
p_constants->push_back(nan);
}
@@ -542,7 +548,7 @@ static String _make_arguments_hint(const MethodInfo &p_info, int p_arg_idx, bool
int def_args = p_info.arguments.size() - p_info.default_arguments.size();
int i = 0;
- for (const List<PropertyInfo>::Element *E = p_info.arguments.front(); E; E = E->next()) {
+ for (const PropertyInfo &E : p_info.arguments) {
if (i > 0) {
arghint += ", ";
}
@@ -550,7 +556,7 @@ static String _make_arguments_hint(const MethodInfo &p_info, int p_arg_idx, bool
if (i == p_arg_idx) {
arghint += String::chr(0xFFFF);
}
- arghint += E->get().name + ": " + _get_visual_datatype(E->get(), true);
+ arghint += E.name + ": " + _get_visual_datatype(E, true);
if (i - def_args >= 0) {
arghint += String(" = ") + p_info.default_arguments[i - def_args].get_construct_string();
@@ -656,11 +662,11 @@ static void _find_annotation_arguments(const GDScriptParser::AnnotationNode *p_a
r_result.insert(node.display, node);
List<StringName> node_types;
ClassDB::get_inheriters_from_class("Node", &node_types);
- for (const List<StringName>::Element *E = node_types.front(); E != nullptr; E = E->next()) {
- if (!ClassDB::is_class_exposed(E->get())) {
+ for (const StringName &E : node_types) {
+ if (!ClassDB::is_class_exposed(E)) {
continue;
}
- ScriptCodeCompletionOption option(E->get(), ScriptCodeCompletionOption::KIND_CLASS);
+ ScriptCodeCompletionOption option(E, ScriptCodeCompletionOption::KIND_CLASS);
r_result.insert(option.display, option);
}
}
@@ -669,9 +675,9 @@ static void _find_annotation_arguments(const GDScriptParser::AnnotationNode *p_a
static void _list_available_types(bool p_inherit_only, GDScriptParser::CompletionContext &p_context, Map<String, ScriptCodeCompletionOption> &r_result) {
List<StringName> native_types;
ClassDB::get_class_list(&native_types);
- for (const List<StringName>::Element *E = native_types.front(); E != nullptr; E = E->next()) {
- if (ClassDB::is_class_exposed(E->get()) && !Engine::get_singleton()->has_singleton(E->get())) {
- ScriptCodeCompletionOption option(E->get(), ScriptCodeCompletionOption::KIND_CLASS);
+ for (const StringName &E : native_types) {
+ if (ClassDB::is_class_exposed(E) && !Engine::get_singleton()->has_singleton(E)) {
+ ScriptCodeCompletionOption option(E, ScriptCodeCompletionOption::KIND_CLASS);
r_result.insert(option.display, option);
}
}
@@ -681,8 +687,8 @@ static void _list_available_types(bool p_inherit_only, GDScriptParser::Completio
// Native enums from base class
List<StringName> enums;
ClassDB::get_enum_list(p_context.current_class->base_type.native_type, &enums);
- for (const List<StringName>::Element *E = enums.front(); E != nullptr; E = E->next()) {
- ScriptCodeCompletionOption option(E->get(), ScriptCodeCompletionOption::KIND_ENUM);
+ for (const StringName &E : enums) {
+ ScriptCodeCompletionOption option(E, ScriptCodeCompletionOption::KIND_ENUM);
r_result.insert(option.display, option);
}
}
@@ -719,8 +725,8 @@ static void _list_available_types(bool p_inherit_only, GDScriptParser::Completio
// Global scripts
List<StringName> global_classes;
ScriptServer::get_global_class_list(&global_classes);
- for (const List<StringName>::Element *E = global_classes.front(); E != nullptr; E = E->next()) {
- ScriptCodeCompletionOption option(E->get(), ScriptCodeCompletionOption::KIND_CLASS);
+ for (const StringName &E : global_classes) {
+ ScriptCodeCompletionOption option(E, ScriptCodeCompletionOption::KIND_CLASS);
r_result.insert(option.display, option);
}
@@ -738,7 +744,13 @@ static void _list_available_types(bool p_inherit_only, GDScriptParser::Completio
static void _find_identifiers_in_suite(const GDScriptParser::SuiteNode *p_suite, Map<String, ScriptCodeCompletionOption> &r_result) {
for (int i = 0; i < p_suite->locals.size(); i++) {
- ScriptCodeCompletionOption option(p_suite->locals[i].name, ScriptCodeCompletionOption::KIND_VARIABLE);
+ ScriptCodeCompletionOption option;
+ if (p_suite->locals[i].type == GDScriptParser::SuiteNode::Local::CONSTANT) {
+ option = ScriptCodeCompletionOption(p_suite->locals[i].name, ScriptCodeCompletionOption::KIND_CONSTANT);
+ option.default_value = p_suite->locals[i].constant->initializer->reduced_value;
+ } else {
+ option = ScriptCodeCompletionOption(p_suite->locals[i].name, ScriptCodeCompletionOption::KIND_VARIABLE);
+ }
r_result.insert(option.display, option);
}
if (p_suite->parent_block) {
@@ -853,8 +865,8 @@ static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base
if (!_static) {
List<PropertyInfo> members;
scr->get_script_property_list(&members);
- for (List<PropertyInfo>::Element *E = members.front(); E; E = E->next()) {
- ScriptCodeCompletionOption option(E->get().name, ScriptCodeCompletionOption::KIND_MEMBER);
+ for (const PropertyInfo &E : members) {
+ ScriptCodeCompletionOption option(E.name, ScriptCodeCompletionOption::KIND_MEMBER);
r_result.insert(option.display, option);
}
}
@@ -867,20 +879,20 @@ static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base
List<MethodInfo> signals;
scr->get_script_signal_list(&signals);
- for (List<MethodInfo>::Element *E = signals.front(); E; E = E->next()) {
- ScriptCodeCompletionOption option(E->get().name, ScriptCodeCompletionOption::KIND_SIGNAL);
+ for (const MethodInfo &E : signals) {
+ ScriptCodeCompletionOption option(E.name, ScriptCodeCompletionOption::KIND_SIGNAL);
r_result.insert(option.display, option);
}
}
List<MethodInfo> methods;
scr->get_script_method_list(&methods);
- for (List<MethodInfo>::Element *E = methods.front(); E; E = E->next()) {
- if (E->get().name.begins_with("@")) {
+ for (const MethodInfo &E : methods) {
+ if (E.name.begins_with("@")) {
continue;
}
- ScriptCodeCompletionOption option(E->get().name, ScriptCodeCompletionOption::KIND_FUNCTION);
- if (E->get().arguments.size()) {
+ ScriptCodeCompletionOption option(E.name, ScriptCodeCompletionOption::KIND_FUNCTION);
+ if (E.arguments.size()) {
option.insert_text += "(";
} else {
option.insert_text += "()";
@@ -908,22 +920,22 @@ static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base
if (!p_only_functions) {
List<String> constants;
ClassDB::get_integer_constant_list(type, &constants);
- for (List<String>::Element *E = constants.front(); E; E = E->next()) {
- ScriptCodeCompletionOption option(E->get(), ScriptCodeCompletionOption::KIND_CONSTANT);
+ for (const String &E : constants) {
+ ScriptCodeCompletionOption option(E, ScriptCodeCompletionOption::KIND_CONSTANT);
r_result.insert(option.display, option);
}
if (!_static || Engine::get_singleton()->has_singleton(type)) {
List<PropertyInfo> pinfo;
ClassDB::get_property_list(type, &pinfo);
- for (List<PropertyInfo>::Element *E = pinfo.front(); E; E = E->next()) {
- if (E->get().usage & (PROPERTY_USAGE_GROUP | PROPERTY_USAGE_CATEGORY)) {
+ for (const PropertyInfo &E : pinfo) {
+ if (E.usage & (PROPERTY_USAGE_GROUP | PROPERTY_USAGE_CATEGORY)) {
continue;
}
- if (E->get().name.find("/") != -1) {
+ if (E.name.find("/") != -1) {
continue;
}
- ScriptCodeCompletionOption option(E->get().name, ScriptCodeCompletionOption::KIND_MEMBER);
+ ScriptCodeCompletionOption option(E.name, ScriptCodeCompletionOption::KIND_MEMBER);
r_result.insert(option.display, option);
}
}
@@ -933,12 +945,12 @@ static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base
List<MethodInfo> methods;
bool is_autocompleting_getters = GLOBAL_GET("debug/gdscript/completion/autocomplete_setters_and_getters").booleanize();
ClassDB::get_method_list(type, &methods, false, !is_autocompleting_getters);
- for (List<MethodInfo>::Element *E = methods.front(); E; E = E->next()) {
- if (E->get().name.begins_with("_")) {
+ for (const MethodInfo &E : methods) {
+ if (E.name.begins_with("_")) {
continue;
}
- ScriptCodeCompletionOption option(E->get().name, ScriptCodeCompletionOption::KIND_FUNCTION);
- if (E->get().arguments.size()) {
+ ScriptCodeCompletionOption option(E.name, ScriptCodeCompletionOption::KIND_FUNCTION);
+ if (E.arguments.size()) {
option.insert_text += "(";
} else {
option.insert_text += "()";
@@ -965,9 +977,9 @@ static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base
tmp.get_property_list(&members);
}
- for (List<PropertyInfo>::Element *E = members.front(); E; E = E->next()) {
- if (String(E->get().name).find("/") == -1) {
- ScriptCodeCompletionOption option(E->get().name, ScriptCodeCompletionOption::KIND_MEMBER);
+ for (const PropertyInfo &E : members) {
+ if (String(E.name).find("/") == -1) {
+ ScriptCodeCompletionOption option(E.name, ScriptCodeCompletionOption::KIND_MEMBER);
r_result.insert(option.display, option);
}
}
@@ -975,9 +987,9 @@ static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base
List<MethodInfo> methods;
tmp.get_method_list(&methods);
- for (const List<MethodInfo>::Element *E = methods.front(); E; E = E->next()) {
- ScriptCodeCompletionOption option(E->get().name, ScriptCodeCompletionOption::KIND_FUNCTION);
- if (E->get().arguments.size()) {
+ for (const MethodInfo &E : methods) {
+ ScriptCodeCompletionOption option(E.name, ScriptCodeCompletionOption::KIND_FUNCTION);
+ if (E.arguments.size()) {
option.insert_text += "(";
} else {
option.insert_text += "()";
@@ -1007,9 +1019,9 @@ static void _find_identifiers(GDScriptParser::CompletionContext &p_context, bool
List<StringName> functions;
GDScriptUtilityFunctions::get_function_list(&functions);
- for (const List<StringName>::Element *E = functions.front(); E; E = E->next()) {
- MethodInfo function = GDScriptUtilityFunctions::get_function_info(E->get());
- ScriptCodeCompletionOption option(String(E->get()), ScriptCodeCompletionOption::KIND_FUNCTION);
+ for (const StringName &E : functions) {
+ MethodInfo function = GDScriptUtilityFunctions::get_function_info(E);
+ ScriptCodeCompletionOption option(String(E), ScriptCodeCompletionOption::KIND_FUNCTION);
if (function.arguments.size() || (function.flags & METHOD_FLAG_VARARG)) {
option.insert_text += "(";
} else {
@@ -1023,7 +1035,7 @@ static void _find_identifiers(GDScriptParser::CompletionContext &p_context, bool
}
static const char *_type_names[Variant::VARIANT_MAX] = {
- "null", "bool", "int", "float", "String", "StringName", "Vector2", "Vector2i", "Rect2", "Rect2i", "Vector3", "Vector3i", "Transform2D", "Plane", "Quat", "AABB", "Basis", "Transform",
+ "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"
};
@@ -1756,9 +1768,9 @@ static bool _guess_identifier_type(GDScriptParser::CompletionContext &p_context,
StringName real_native = GDScriptParser::get_real_class_name(base_type.native_type);
MethodInfo info;
if (ClassDB::get_method_info(real_native, p_context.current_function->identifier->name, &info)) {
- for (const List<PropertyInfo>::Element *E = info.arguments.front(); E; E = E->next()) {
- if (E->get().name == p_identifier) {
- r_type = _type_from_property(E->get());
+ for (const PropertyInfo &E : info.arguments) {
+ if (E.name == p_identifier) {
+ r_type = _type_from_property(E);
return true;
}
}
@@ -1920,8 +1932,7 @@ static bool _guess_identifier_type_from_base(GDScriptParser::CompletionContext &
if (!is_static) {
List<PropertyInfo> members;
scr->get_script_property_list(&members);
- for (const List<PropertyInfo>::Element *E = members.front(); E; E = E->next()) {
- const PropertyInfo &prop = E->get();
+ for (const PropertyInfo &prop : members) {
if (prop.name == p_identifier) {
r_type = _type_from_property(prop);
return true;
@@ -2084,8 +2095,7 @@ static bool _guess_method_return_type_from_base(GDScriptParser::CompletionContex
if (scr.is_valid()) {
List<MethodInfo> methods;
scr->get_script_method_list(&methods);
- for (List<MethodInfo>::Element *E = methods.front(); E; E = E->next()) {
- MethodInfo &mi = E->get();
+ for (const MethodInfo &mi : methods) {
if (mi.name == p_method) {
r_type = _type_from_property(mi.return_val);
return true;
@@ -2125,8 +2135,7 @@ static bool _guess_method_return_type_from_base(GDScriptParser::CompletionContex
List<MethodInfo> methods;
tmp.get_method_list(&methods);
- for (List<MethodInfo>::Element *E = methods.front(); E; E = E->next()) {
- MethodInfo &mi = E->get();
+ for (const MethodInfo &mi : methods) {
if (mi.name == p_method) {
r_type = _type_from_property(mi.return_val);
return true;
@@ -2171,8 +2180,8 @@ static void _find_enumeration_candidates(GDScriptParser::CompletionContext &p_co
List<StringName> enum_constants;
ClassDB::get_enum_constants(class_name, enum_name, &enum_constants);
- for (List<StringName>::Element *E = enum_constants.front(); E; E = E->next()) {
- String candidate = class_name + "." + E->get();
+ for (const StringName &E : enum_constants) {
+ String candidate = class_name + "." + E;
ScriptCodeCompletionOption option(candidate, ScriptCodeCompletionOption::KIND_ENUM);
r_result.insert(option.display, option);
}
@@ -2216,8 +2225,8 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c
if (obj) {
List<String> options;
obj->get_argument_options(p_method, p_argidx, &options);
- for (List<String>::Element *F = options.front(); F; F = F->next()) {
- ScriptCodeCompletionOption option(F->get(), ScriptCodeCompletionOption::KIND_FUNCTION);
+ for (const String &F : options) {
+ ScriptCodeCompletionOption option(F, ScriptCodeCompletionOption::KIND_FUNCTION);
r_result.insert(option.display, option);
}
}
@@ -2238,8 +2247,8 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c
List<PropertyInfo> props;
ProjectSettings::get_singleton()->get_property_list(&props);
- for (List<PropertyInfo>::Element *E = props.front(); E; E = E->next()) {
- String s = E->get().name;
+ for (const PropertyInfo &E : props) {
+ String s = E.name;
if (!s.begins_with("autoload/")) {
continue;
}
@@ -2254,8 +2263,8 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c
// Get input actions
List<PropertyInfo> props;
ProjectSettings::get_singleton()->get_property_list(&props);
- for (List<PropertyInfo>::Element *E = props.front(); E; E = E->next()) {
- String s = E->get().name;
+ for (const PropertyInfo &E : props) {
+ String s = E.name;
if (!s.begins_with("input/")) {
continue;
}
@@ -2279,9 +2288,9 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c
List<MethodInfo> methods;
base.get_method_list(&methods);
- for (List<MethodInfo>::Element *E = methods.front(); E; E = E->next()) {
- if (E->get().name == p_method) {
- r_arghint = _make_arguments_hint(E->get(), p_argidx);
+ for (const MethodInfo &E : methods) {
+ if (E.name == p_method) {
+ r_arghint = _make_arguments_hint(E, p_argidx);
return;
}
}
@@ -2328,14 +2337,14 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c
Variant::get_constructor_list(GDScriptParser::get_builtin_type(call->function_name), &constructors);
int i = 0;
- for (List<MethodInfo>::Element *E = constructors.front(); E; E = E->next()) {
- if (p_argidx >= E->get().arguments.size()) {
+ for (const MethodInfo &E : constructors) {
+ if (p_argidx >= E.arguments.size()) {
continue;
}
if (i > 0) {
r_arghint += "\n";
}
- r_arghint += _make_arguments_hint(E->get(), p_argidx);
+ r_arghint += _make_arguments_hint(E, p_argidx);
i++;
}
return;
@@ -2372,7 +2381,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<ScriptCodeCompletionOption> *r_options, bool &r_forced, String &r_call_hint) {
const String quote_style = EDITOR_DEF("text_editor/completion/use_single_quotes", false) ? "'" : "\"";
GDScriptParser parser;
@@ -2394,9 +2403,9 @@ Error GDScriptLanguage::complete_code(const String &p_code, const String &p_path
case GDScriptParser::COMPLETION_ANNOTATION: {
List<MethodInfo> annotations;
parser.get_annotation_list(&annotations);
- for (const List<MethodInfo>::Element *E = annotations.front(); E != nullptr; E = E->next()) {
- ScriptCodeCompletionOption option(E->get().name.substr(1), ScriptCodeCompletionOption::KIND_PLAIN_TEXT);
- if (E->get().arguments.size() > 0) {
+ for (const MethodInfo &E : annotations) {
+ ScriptCodeCompletionOption option(E.name.substr(1), ScriptCodeCompletionOption::KIND_PLAIN_TEXT);
+ if (E.arguments.size() > 0) {
option.insert_text += "(";
}
options.insert(option.display, option);
@@ -2414,10 +2423,10 @@ Error GDScriptLanguage::complete_code(const String &p_code, const String &p_path
case GDScriptParser::COMPLETION_BUILT_IN_TYPE_CONSTANT: {
List<StringName> constants;
Variant::get_constants_for_type(completion_context.builtin_type, &constants);
- for (const List<StringName>::Element *E = constants.front(); E != nullptr; E = E->next()) {
- ScriptCodeCompletionOption option(E->get(), ScriptCodeCompletionOption::KIND_CONSTANT);
+ 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->get(), &valid);
+ Variant default_value = Variant::get_constant_value(completion_context.builtin_type, E, &valid);
if (valid) {
option.default_value = default_value;
}
@@ -2594,8 +2603,7 @@ Error GDScriptLanguage::complete_code(const String &p_code, const String &p_path
List<MethodInfo> virtual_methods;
ClassDB::get_virtual_methods(class_name, &virtual_methods);
- for (List<MethodInfo>::Element *E = virtual_methods.front(); E; E = E->next()) {
- MethodInfo &mi = E->get();
+ for (const MethodInfo &mi : virtual_methods) {
String method_hint = mi.name;
if (method_hint.find(":") != -1) {
method_hint = method_hint.get_slice(":", 0);
@@ -2644,8 +2652,8 @@ Error GDScriptLanguage::complete_code(const String &p_code, const String &p_path
List<String> opts;
p_owner->get_argument_options("get_node", 0, &opts);
- for (List<String>::Element *E = opts.front(); E; E = E->next()) {
- String opt = E->get().strip_edges();
+ for (const String &E : opts) {
+ String opt = E.strip_edges();
if (opt.is_quoted()) {
r_forced = true;
String idopt = opt.unquote();
@@ -2787,6 +2795,7 @@ static Error _lookup_symbol_from_base(const GDScriptParser::DataType &p_base, co
if (base_type.class_type->has_member(p_symbol)) {
r_result.type = ScriptLanguage::LookupResult::RESULT_SCRIPT_LOCATION;
r_result.location = base_type.class_type->get_member(p_symbol).get_line();
+ r_result.class_path = base_type.script_path;
return OK;
}
base_type = base_type.class_type->base_type;
@@ -2829,8 +2838,8 @@ static Error _lookup_symbol_from_base(const GDScriptParser::DataType &p_base, co
List<MethodInfo> virtual_methods;
ClassDB::get_virtual_methods(class_name, &virtual_methods, true);
- for (List<MethodInfo>::Element *E = virtual_methods.front(); E; E = E->next()) {
- if (E->get().name == p_symbol) {
+ for (const MethodInfo &E : virtual_methods) {
+ if (E.name == p_symbol) {
r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS_METHOD;
r_result.class_name = base_type.native_type;
r_result.class_member = p_symbol;
@@ -2848,8 +2857,8 @@ static Error _lookup_symbol_from_base(const GDScriptParser::DataType &p_base, co
List<String> constants;
ClassDB::get_integer_constant_list(class_name, &constants, true);
- for (List<String>::Element *E = constants.front(); E; E = E->next()) {
- if (E->get() == p_symbol) {
+ for (const String &E : constants) {
+ if (E == p_symbol) {
r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS_CONSTANT;
r_result.class_name = base_type.native_type;
r_result.class_member = p_symbol;
@@ -2867,7 +2876,7 @@ static Error _lookup_symbol_from_base(const GDScriptParser::DataType &p_base, co
StringName parent = ClassDB::get_parent_class(class_name);
if (parent != StringName()) {
if (String(parent).begins_with("_")) {
- base_type.native_type = String(parent).right(1);
+ base_type.native_type = String(parent).substr(1);
} else {
base_type.native_type = parent;
}
@@ -2888,7 +2897,7 @@ static Error _lookup_symbol_from_base(const GDScriptParser::DataType &p_base, co
Variant v;
REF v_ref;
if (base_type.builtin_type == Variant::OBJECT) {
- v_ref.instance();
+ v_ref.instantiate();
v = v_ref;
} else {
Callable::CallError err;
@@ -2923,7 +2932,7 @@ static Error _lookup_symbol_from_base(const GDScriptParser::DataType &p_base, co
return ERR_CANT_RESOLVE;
}
-Error GDScriptLanguage::lookup_code(const String &p_code, const String &p_symbol, const String &p_path, Object *p_owner, LookupResult &r_result) {
+::Error GDScriptLanguage::lookup_code(const String &p_code, const String &p_symbol, const String &p_path, Object *p_owner, LookupResult &r_result) {
//before parsing, try the usual stuff
if (ClassDB::class_exists(p_symbol)) {
r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS;
@@ -2993,6 +3002,7 @@ Error GDScriptLanguage::lookup_code(const String &p_code, const String &p_symbol
is_function = true;
[[fallthrough]];
}
+ case GDScriptParser::COMPLETION_CALL_ARGUMENTS:
case GDScriptParser::COMPLETION_IDENTIFIER: {
GDScriptParser::DataType base_type;
if (context.current_class) {
@@ -3025,9 +3035,9 @@ Error GDScriptLanguage::lookup_code(const String &p_code, const String &p_symbol
if (!is_function) {
// Guess in autoloads as singletons.
if (ProjectSettings::get_singleton()->has_autoload(p_symbol)) {
- const ProjectSettings::AutoloadInfo &singleton = ProjectSettings::get_singleton()->get_autoload(p_symbol);
- if (singleton.is_singleton) {
- String script = singleton.path;
+ const ProjectSettings::AutoloadInfo &autoload = ProjectSettings::get_singleton()->get_autoload(p_symbol);
+ if (autoload.is_singleton) {
+ String script = autoload.path;
if (!script.ends_with(".gd")) {
// Not a script, try find the script anyway,
// may have some success.
@@ -3060,7 +3070,7 @@ Error GDScriptLanguage::lookup_code(const String &p_code, const String &p_symbol
// proxy class remove the underscore.
if (r_result.class_name.begins_with("_")) {
- r_result.class_name = r_result.class_name.right(1);
+ r_result.class_name = r_result.class_name.substr(1);
}
return OK;
}
@@ -3070,7 +3080,7 @@ Error GDScriptLanguage::lookup_code(const String &p_code, const String &p_symbol
// We cannot determine the exact nature of the identifier here
// Otherwise these codes would work
StringName enumName = ClassDB::get_integer_constant_enum("@GlobalScope", p_symbol, true);
- if (enumName != NULL) {
+ if (enumName != nullptr) {
r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS_ENUM;
r_result.class_name = "@GlobalScope";
r_result.class_member = enumName;
diff --git a/modules/gdscript/gdscript_function.cpp b/modules/gdscript/gdscript_function.cpp
index 7b37aa40a2..876c508689 100644
--- a/modules/gdscript/gdscript_function.cpp
+++ b/modules/gdscript/gdscript_function.cpp
@@ -94,8 +94,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;
- for (const List<StackDebug>::Element *E = stack_debug.front(); E; E = E->next()) {
- const StackDebug &sd = E->get();
+ for (const StackDebug &sd : stack_debug) {
if (sd.line > p_line) {
break;
}
@@ -131,10 +130,10 @@ void GDScriptFunction::debug_get_stack_member_state(int p_line, List<Pair<String
stackpositions.sort();
- for (List<_GDFKCS>::Element *E = stackpositions.front(); E; E = E->next()) {
+ for (_GDFKCS &E : stackpositions) {
Pair<StringName, int> p;
- p.first = E->get().id;
- p.second = E->get().pos;
+ p.first = E.id;
+ p.second = E.pos;
r_stackvars->push_back(p);
}
}
@@ -150,6 +149,10 @@ GDScriptFunction::GDScriptFunction() {
}
GDScriptFunction::~GDScriptFunction() {
+ for (int i = 0; i < lambdas.size(); i++) {
+ memdelete(lambdas[i]);
+ }
+
#ifdef DEBUG_ENABLED
MutexLock lock(GDScriptLanguage::get_singleton()->lock);
@@ -258,9 +261,9 @@ Variant GDScriptFunctionState::resume(const Variant &p_arg) {
if (completed) {
if (first_state.is_valid()) {
- first_state->emit_signal("completed", ret);
+ first_state->emit_signal(SNAME("completed"), ret);
} else {
- emit_signal("completed", ret);
+ emit_signal(SNAME("completed"), ret);
}
#ifdef DEBUG_ENABLED
diff --git a/modules/gdscript/gdscript_function.h b/modules/gdscript/gdscript_function.h
index fbec734a28..9e5ef0f632 100644
--- a/modules/gdscript/gdscript_function.h
+++ b/modules/gdscript/gdscript_function.h
@@ -31,7 +31,7 @@
#ifndef GDSCRIPT_FUNCTION_H
#define GDSCRIPT_FUNCTION_H
-#include "core/object/reference.h"
+#include "core/object/ref_counted.h"
#include "core/object/script_language.h"
#include "core/os/thread.h"
#include "core/string/string_name.h"
@@ -263,6 +263,7 @@ public:
OPCODE_CALL_SELF_BASE,
OPCODE_CALL_METHOD_BIND,
OPCODE_CALL_METHOD_BIND_RET,
+ OPCODE_CALL_BUILTIN_STATIC,
// ptrcall have one instruction per return type.
OPCODE_CALL_PTRCALL_NO_RETURN,
OPCODE_CALL_PTRCALL_BOOL,
@@ -277,10 +278,10 @@ public:
OPCODE_CALL_PTRCALL_VECTOR3I,
OPCODE_CALL_PTRCALL_TRANSFORM2D,
OPCODE_CALL_PTRCALL_PLANE,
- OPCODE_CALL_PTRCALL_QUAT,
+ OPCODE_CALL_PTRCALL_QUATERNION,
OPCODE_CALL_PTRCALL_AABB,
OPCODE_CALL_PTRCALL_BASIS,
- OPCODE_CALL_PTRCALL_TRANSFORM,
+ OPCODE_CALL_PTRCALL_TRANSFORM3D,
OPCODE_CALL_PTRCALL_COLOR,
OPCODE_CALL_PTRCALL_STRING_NAME,
OPCODE_CALL_PTRCALL_NODE_PATH,
@@ -301,6 +302,7 @@ public:
OPCODE_CALL_PTRCALL_PACKED_COLOR_ARRAY,
OPCODE_AWAIT,
OPCODE_AWAIT_RESUME,
+ OPCODE_CREATE_LAMBDA,
OPCODE_JUMP,
OPCODE_JUMP_IF,
OPCODE_JUMP_IF_NOT,
@@ -363,7 +365,7 @@ public:
OPCODE_TYPE_ADJUST_VECTOR3I,
OPCODE_TYPE_ADJUST_TRANSFORM2D,
OPCODE_TYPE_ADJUST_PLANE,
- OPCODE_TYPE_ADJUST_QUAT,
+ OPCODE_TYPE_ADJUST_QUATERNION,
OPCODE_TYPE_ADJUST_AABB,
OPCODE_TYPE_ADJUST_BASIS,
OPCODE_TYPE_ADJUST_TRANSFORM,
@@ -459,6 +461,8 @@ private:
const GDScriptUtilityFunctions::FunctionPtr *_gds_utilities_ptr = nullptr;
int _methods_count = 0;
MethodBind **_methods_ptr = nullptr;
+ int _lambdas_count = 0;
+ GDScriptFunction **_lambdas_ptr = nullptr;
const int *_code_ptr = nullptr;
int _code_size = 0;
int _argument_count = 0;
@@ -468,7 +472,7 @@ private:
int _initial_line = 0;
bool _static = false;
- MultiplayerAPI::RPCMode rpc_mode = MultiplayerAPI::RPC_MODE_DISABLED;
+ MultiplayerAPI::RPCConfig rpc_config;
GDScript *_script = nullptr;
@@ -488,10 +492,13 @@ private:
Vector<Variant::ValidatedUtilityFunction> utilities;
Vector<GDScriptUtilityFunctions::FunctionPtr> gds_utilities;
Vector<MethodBind *> methods;
+ Vector<GDScriptFunction *> lambdas;
Vector<int> code;
Vector<GDScriptDataType> argument_types;
GDScriptDataType return_type;
+ Map<int, Variant::Type> temporary_slots;
+
#ifdef TOOLS_ENABLED
Vector<StringName> arg_names;
Vector<Variant> default_arg_values;
@@ -585,13 +592,13 @@ public:
void disassemble(const Vector<String> &p_code_lines) const;
#endif
- _FORCE_INLINE_ MultiplayerAPI::RPCMode get_rpc_mode() const { return rpc_mode; }
+ _FORCE_INLINE_ MultiplayerAPI::RPCConfig get_rpc_config() const { return rpc_config; }
GDScriptFunction();
~GDScriptFunction();
};
-class GDScriptFunctionState : public Reference {
- GDCLASS(GDScriptFunctionState, Reference);
+class GDScriptFunctionState : public RefCounted {
+ GDCLASS(GDScriptFunctionState, RefCounted);
friend class GDScriptFunction;
GDScriptFunction *function = nullptr;
GDScriptFunction::CallState state;
diff --git a/modules/gdscript/gdscript_lambda_callable.cpp b/modules/gdscript/gdscript_lambda_callable.cpp
new file mode 100644
index 0000000000..0bc109b6e1
--- /dev/null
+++ b/modules/gdscript/gdscript_lambda_callable.cpp
@@ -0,0 +1,95 @@
+/*************************************************************************/
+/* gdscript_lambda_callable.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "gdscript_lambda_callable.h"
+
+#include "core/templates/hashfuncs.h"
+#include "gdscript.h"
+
+bool GDScriptLambdaCallable::compare_equal(const CallableCustom *p_a, const CallableCustom *p_b) {
+ // Lambda callables are only compared by reference.
+ return p_a == p_b;
+}
+
+bool GDScriptLambdaCallable::compare_less(const CallableCustom *p_a, const CallableCustom *p_b) {
+ // Lambda callables are only compared by reference.
+ return p_a < p_b;
+}
+
+uint32_t GDScriptLambdaCallable::hash() const {
+ return h;
+}
+
+String GDScriptLambdaCallable::get_as_text() const {
+ if (function->get_name() != StringName()) {
+ return function->get_name().operator String() + "(lambda)";
+ }
+ return "(anonymous lambda)";
+}
+
+CallableCustom::CompareEqualFunc GDScriptLambdaCallable::get_compare_equal_func() const {
+ return compare_equal;
+}
+
+CallableCustom::CompareLessFunc GDScriptLambdaCallable::get_compare_less_func() const {
+ return compare_less;
+}
+
+ObjectID GDScriptLambdaCallable::get_object() const {
+ return script->get_instance_id();
+}
+
+void GDScriptLambdaCallable::call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const {
+ 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(nullptr, args.ptrw(), args.size(), r_call_error);
+ r_call_error.argument -= captures_amount;
+ } else {
+ r_return_value = function->call(nullptr, p_arguments, p_argcount, r_call_error);
+ }
+}
+
+GDScriptLambdaCallable::GDScriptLambdaCallable(Ref<GDScript> p_script, GDScriptFunction *p_function, const Vector<Variant> &p_captures) {
+ script = p_script;
+ 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
new file mode 100644
index 0000000000..336778d549
--- /dev/null
+++ b/modules/gdscript/gdscript_lambda_callable.h
@@ -0,0 +1,65 @@
+/*************************************************************************/
+/* gdscript_lambda_callable.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef GDSCRIPT_LAMBDA_CALLABLE
+#define GDSCRIPT_LAMBDA_CALLABLE
+
+#include "core/object/ref_counted.h"
+#include "core/templates/vector.h"
+#include "core/variant/callable.h"
+#include "core/variant/variant.h"
+
+class GDScript;
+class GDScriptFunction;
+class GDScriptInstance;
+
+class GDScriptLambdaCallable : public CallableCustom {
+ GDScriptFunction *function = nullptr;
+ Ref<GDScript> script;
+ 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;
+
+ GDScriptLambdaCallable(Ref<GDScript> p_script, GDScriptFunction *p_function, const Vector<Variant> &p_captures);
+ virtual ~GDScriptLambdaCallable() = default;
+};
+
+#endif // GDSCRIPT_LAMBDA_CALLABLE
diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp
index d910137510..a21167ad95 100644
--- a/modules/gdscript/gdscript_parser.cpp
+++ b/modules/gdscript/gdscript_parser.cpp
@@ -31,9 +31,9 @@
#include "gdscript_parser.h"
#include "core/config/project_settings.h"
+#include "core/io/file_access.h"
#include "core/io/resource_loader.h"
#include "core/math/math_defs.h"
-#include "core/os/file_access.h"
#include "gdscript.h"
#ifdef DEBUG_ENABLED
@@ -61,9 +61,9 @@ Variant::Type GDScriptParser::get_builtin_type(const StringName &p_type) {
builtin_types["Vector3i"] = Variant::VECTOR3I;
builtin_types["AABB"] = Variant::AABB;
builtin_types["Plane"] = Variant::PLANE;
- builtin_types["Quat"] = Variant::QUAT;
+ builtin_types["Quaternion"] = Variant::QUATERNION;
builtin_types["Basis"] = Variant::BASIS;
- builtin_types["Transform"] = Variant::TRANSFORM;
+ builtin_types["Transform3D"] = Variant::TRANSFORM3D;
builtin_types["Color"] = Variant::COLOR;
builtin_types["RID"] = Variant::RID;
builtin_types["Object"] = Variant::OBJECT;
@@ -136,8 +136,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 List<StringName>::Element *E = keys.front(); E != nullptr; E = E->next()) {
- r_annotations->push_back(valid_annotations[E->get()].info);
+ for (const StringName &E : keys) {
+ r_annotations->push_back(valid_annotations[E].info);
}
}
@@ -157,7 +157,6 @@ GDScriptParser::GDScriptParser() {
register_annotation(MethodInfo("@export_multiline"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_MULTILINE_TEXT, Variant::STRING>);
register_annotation(MethodInfo("@export_placeholder"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_PLACEHOLDER_TEXT, Variant::STRING>);
register_annotation(MethodInfo("@export_range", { Variant::FLOAT, "min" }, { Variant::FLOAT, "max" }, { Variant::FLOAT, "step" }, { Variant::STRING, "slider1" }, { Variant::STRING, "slider2" }), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_RANGE, Variant::FLOAT>, 3);
- register_annotation(MethodInfo("@export_exp_range", { Variant::FLOAT, "min" }, { Variant::FLOAT, "max" }, { Variant::FLOAT, "step" }, { Variant::STRING, "slider1" }, { Variant::STRING, "slider2" }), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_EXP_RANGE, Variant::FLOAT>, 3);
register_annotation(MethodInfo("@export_exp_easing", { Variant::STRING, "hint1" }, { Variant::STRING, "hint2" }), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_EXP_EASING, Variant::FLOAT>, 2);
register_annotation(MethodInfo("@export_color_no_alpha"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_COLOR_NO_ALPHA, Variant::COLOR>);
register_annotation(MethodInfo("@export_node_path", { Variant::STRING, "type" }), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_NODE_PATH_VALID_TYPES, Variant::NODE_PATH>, 1, true);
@@ -169,12 +168,7 @@ GDScriptParser::GDScriptParser() {
register_annotation(MethodInfo("@export_flags_3d_physics"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_LAYERS_3D_PHYSICS, Variant::INT>);
register_annotation(MethodInfo("@export_flags_3d_navigation"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_LAYERS_3D_NAVIGATION, Variant::INT>);
// Networking.
- register_annotation(MethodInfo("@remote"), AnnotationInfo::VARIABLE | AnnotationInfo::FUNCTION, &GDScriptParser::network_annotations<MultiplayerAPI::RPC_MODE_REMOTE>);
- register_annotation(MethodInfo("@master"), AnnotationInfo::VARIABLE | AnnotationInfo::FUNCTION, &GDScriptParser::network_annotations<MultiplayerAPI::RPC_MODE_MASTER>);
- register_annotation(MethodInfo("@puppet"), AnnotationInfo::VARIABLE | AnnotationInfo::FUNCTION, &GDScriptParser::network_annotations<MultiplayerAPI::RPC_MODE_PUPPET>);
- register_annotation(MethodInfo("@remotesync"), AnnotationInfo::VARIABLE | AnnotationInfo::FUNCTION, &GDScriptParser::network_annotations<MultiplayerAPI::RPC_MODE_REMOTESYNC>);
- register_annotation(MethodInfo("@mastersync"), AnnotationInfo::VARIABLE | AnnotationInfo::FUNCTION, &GDScriptParser::network_annotations<MultiplayerAPI::RPC_MODE_MASTERSYNC>);
- register_annotation(MethodInfo("@puppetsync"), AnnotationInfo::VARIABLE | AnnotationInfo::FUNCTION, &GDScriptParser::network_annotations<MultiplayerAPI::RPC_MODE_PUPPETSYNC>);
+ register_annotation(MethodInfo("@rpc", { Variant::STRING, "mode" }, { Variant::STRING, "sync" }, { Variant::STRING, "transfer_mode" }, { Variant::INT, "transfer_channel" }), AnnotationInfo::FUNCTION, &GDScriptParser::network_annotations<MultiplayerAPI::RPC_MODE_PUPPET>, 4, true);
// TODO: Warning annotations.
}
@@ -254,7 +248,7 @@ void GDScriptParser::push_warning(const Node *p_source, GDScriptWarning::Code p_
warning.rightmost_column = p_source->rightmost_column;
List<GDScriptWarning>::Element *before = nullptr;
- for (List<GDScriptWarning>::Element *E = warnings.front(); E != nullptr; E = E->next()) {
+ for (List<GDScriptWarning>::Element *E = warnings.front(); E; E = E->next()) {
if (E->get().start_line > warning.start_line) {
break;
}
@@ -402,6 +396,8 @@ Error GDScriptParser::parse(const String &p_source_code, const String &p_script_
}
GDScriptTokenizer::Token GDScriptParser::advance() {
+ lambda_ended = false; // Empty marker since we're past the end in any case.
+
if (current.type == GDScriptTokenizer::Token::TK_EOF) {
ERR_FAIL_COND_V_MSG(current.type == GDScriptTokenizer::Token::TK_EOF, current, "GDScript parser bug: Trying to advance past the end of stream.");
}
@@ -428,7 +424,7 @@ bool GDScriptParser::match(GDScriptTokenizer::Token::Type p_token_type) {
return true;
}
-bool GDScriptParser::check(GDScriptTokenizer::Token::Type p_token_type) {
+bool GDScriptParser::check(GDScriptTokenizer::Token::Type p_token_type) const {
if (p_token_type == GDScriptTokenizer::Token::IDENTIFIER) {
return current.is_identifier();
}
@@ -443,7 +439,7 @@ bool GDScriptParser::consume(GDScriptTokenizer::Token::Type p_token_type, const
return false;
}
-bool GDScriptParser::is_at_end() {
+bool GDScriptParser::is_at_end() const {
return check(GDScriptTokenizer::Token::TK_EOF);
}
@@ -494,16 +490,34 @@ void GDScriptParser::pop_multiline() {
tokenizer.set_multiline_mode(multiline_stack.size() > 0 ? multiline_stack.back()->get() : false);
}
-bool GDScriptParser::is_statement_end() {
+bool GDScriptParser::is_statement_end_token() const {
return check(GDScriptTokenizer::Token::NEWLINE) || check(GDScriptTokenizer::Token::SEMICOLON) || check(GDScriptTokenizer::Token::TK_EOF);
}
+bool GDScriptParser::is_statement_end() const {
+ return lambda_ended || in_lambda || is_statement_end_token();
+}
+
void GDScriptParser::end_statement(const String &p_context) {
bool found = false;
while (is_statement_end() && !is_at_end()) {
// Remove sequential newlines/semicolons.
+ if (is_statement_end_token()) {
+ // Only consume if this is an actual token.
+ advance();
+ } else if (lambda_ended) {
+ lambda_ended = false; // Consume this "token".
+ found = true;
+ break;
+ } else {
+ if (!found) {
+ lambda_ended = true; // Mark the lambda as done since we found something else to end the statement.
+ found = true;
+ }
+ break;
+ }
+
found = true;
- advance();
}
if (!found && !is_at_end()) {
push_error(vformat(R"(Expected end of statement after %s, found "%s" instead.)", p_context, current.get_name()));
@@ -1153,7 +1167,7 @@ GDScriptParser::EnumNode *GDScriptParser::parse_enum() {
consume(GDScriptTokenizer::Token::BRACE_CLOSE, R"(Expected closing "}" for enum.)");
#ifdef TOOLS_ENABLED
- // Enum values documentaion.
+ // Enum values documentation.
for (int i = 0; i < enum_node->values.size(); i++) {
if (i == enum_node->values.size() - 1) {
// If close bracket is same line as last value.
@@ -1182,36 +1196,7 @@ GDScriptParser::EnumNode *GDScriptParser::parse_enum() {
return enum_node;
}
-GDScriptParser::FunctionNode *GDScriptParser::parse_function() {
- bool _static = false;
- if (previous.type == GDScriptTokenizer::Token::STATIC) {
- // TODO: Improve message if user uses "static" with "var" or "const"
- if (!consume(GDScriptTokenizer::Token::FUNC, R"(Expected "func" after "static".)")) {
- return nullptr;
- }
- _static = true;
- }
-
- FunctionNode *function = alloc_node<FunctionNode>();
- make_completion_context(COMPLETION_OVERRIDE_METHOD, function);
-
- if (!consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected function name after "func".)")) {
- return nullptr;
- }
-
- FunctionNode *previous_function = current_function;
- current_function = function;
-
- function->identifier = parse_identifier();
- function->is_static = _static;
-
- push_multiline(true);
- consume(GDScriptTokenizer::Token::PARENTHESIS_OPEN, R"(Expected opening "(" after function name.)");
-
- SuiteNode *body = alloc_node<SuiteNode>();
- SuiteNode *previous_suite = current_suite;
- current_suite = body;
-
+void GDScriptParser::parse_function_signature(FunctionNode *p_function, SuiteNode *p_body, const String &p_type) {
if (!check(GDScriptTokenizer::Token::PARENTHESIS_CLOSE) && !is_at_end()) {
bool default_used = false;
do {
@@ -1231,29 +1216,61 @@ GDScriptParser::FunctionNode *GDScriptParser::parse_function() {
continue;
}
}
- if (function->parameters_indices.has(parameter->identifier->name)) {
- push_error(vformat(R"(Parameter with name "%s" was already declared for this function.)", parameter->identifier->name));
+ if (p_function->parameters_indices.has(parameter->identifier->name)) {
+ push_error(vformat(R"(Parameter with name "%s" was already declared for this %s.)", parameter->identifier->name, p_type));
} else {
- function->parameters_indices[parameter->identifier->name] = function->parameters.size();
- function->parameters.push_back(parameter);
- body->add_local(parameter);
+ p_function->parameters_indices[parameter->identifier->name] = p_function->parameters.size();
+ p_function->parameters.push_back(parameter);
+ p_body->add_local(parameter, current_function);
}
} while (match(GDScriptTokenizer::Token::COMMA));
}
pop_multiline();
- consume(GDScriptTokenizer::Token::PARENTHESIS_CLOSE, R"*(Expected closing ")" after function parameters.)*");
+ consume(GDScriptTokenizer::Token::PARENTHESIS_CLOSE, vformat(R"*(Expected closing ")" after %s parameters.)*", p_type));
if (match(GDScriptTokenizer::Token::FORWARD_ARROW)) {
- make_completion_context(COMPLETION_TYPE_NAME_OR_VOID, function);
- function->return_type = parse_type(true);
- if (function->return_type == nullptr) {
+ make_completion_context(COMPLETION_TYPE_NAME_OR_VOID, p_function);
+ p_function->return_type = parse_type(true);
+ if (p_function->return_type == nullptr) {
push_error(R"(Expected return type or "void" after "->".)");
}
}
// TODO: Improve token consumption so it synchronizes to a statement boundary. This way we can get into the function body with unrecognized tokens.
- consume(GDScriptTokenizer::Token::COLON, R"(Expected ":" after function declaration.)");
+ consume(GDScriptTokenizer::Token::COLON, vformat(R"(Expected ":" after %s declaration.)", p_type));
+}
+
+GDScriptParser::FunctionNode *GDScriptParser::parse_function() {
+ bool _static = false;
+ if (previous.type == GDScriptTokenizer::Token::STATIC) {
+ // TODO: Improve message if user uses "static" with "var" or "const"
+ if (!consume(GDScriptTokenizer::Token::FUNC, R"(Expected "func" after "static".)")) {
+ return nullptr;
+ }
+ _static = true;
+ }
+
+ FunctionNode *function = alloc_node<FunctionNode>();
+ make_completion_context(COMPLETION_OVERRIDE_METHOD, function);
+
+ if (!consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected function name after "func".)")) {
+ return nullptr;
+ }
+
+ FunctionNode *previous_function = current_function;
+ current_function = function;
+
+ function->identifier = parse_identifier();
+ function->is_static = _static;
+
+ SuiteNode *body = alloc_node<SuiteNode>();
+ SuiteNode *previous_suite = current_suite;
+ current_suite = body;
+
+ push_multiline(true);
+ consume(GDScriptTokenizer::Token::PARENTHESIS_OPEN, R"(Expected opening "(" after function name.)");
+ parse_function_signature(function, body, "function");
current_suite = previous_suite;
function->body = parse_suite("function declaration", body);
@@ -1315,8 +1332,7 @@ GDScriptParser::AnnotationNode *GDScriptParser::parse_annotation(uint32_t p_vali
}
void GDScriptParser::clear_unused_annotations() {
- for (const List<AnnotationNode *>::Element *E = annotation_stack.front(); E != nullptr; E = E->next()) {
- AnnotationNode *annotation = E->get();
+ for (const AnnotationNode *annotation : annotation_stack) {
push_error(vformat(R"(Annotation "%s" does not precedes a valid target, so it will have no effect.)", annotation->name), annotation);
}
@@ -1339,29 +1355,34 @@ bool GDScriptParser::register_annotation(const MethodInfo &p_info, uint32_t p_ta
return true;
}
-GDScriptParser::SuiteNode *GDScriptParser::parse_suite(const String &p_context, SuiteNode *p_suite) {
+GDScriptParser::SuiteNode *GDScriptParser::parse_suite(const String &p_context, SuiteNode *p_suite, bool p_for_lambda) {
SuiteNode *suite = p_suite != nullptr ? p_suite : alloc_node<SuiteNode>();
suite->parent_block = current_suite;
+ suite->parent_function = current_function;
current_suite = suite;
bool multiline = false;
- if (check(GDScriptTokenizer::Token::NEWLINE)) {
+ if (match(GDScriptTokenizer::Token::NEWLINE)) {
multiline = true;
}
if (multiline) {
- consume(GDScriptTokenizer::Token::NEWLINE, vformat(R"(Expected newline after %s.)", p_context));
-
if (!consume(GDScriptTokenizer::Token::INDENT, vformat(R"(Expected indented block after %s.)", p_context))) {
current_suite = suite->parent_block;
return suite;
}
}
+ int error_count = 0;
+
do {
Node *statement = parse_statement();
if (statement == nullptr) {
+ if (error_count++ > 100) {
+ push_error("Too many statement errors.", suite);
+ break;
+ }
continue;
}
suite->statements.push_back(statement);
@@ -1374,7 +1395,7 @@ GDScriptParser::SuiteNode *GDScriptParser::parse_suite(const String &p_context,
if (local.type != SuiteNode::Local::UNDEFINED) {
push_error(vformat(R"(There is already a %s named "%s" declared in this scope.)", local.get_name(), variable->identifier->name));
}
- current_suite->add_local(variable);
+ current_suite->add_local(variable, current_function);
break;
}
case Node::CONSTANT: {
@@ -1389,19 +1410,29 @@ GDScriptParser::SuiteNode *GDScriptParser::parse_suite(const String &p_context,
}
push_error(vformat(R"(There is already a %s named "%s" declared in this scope.)", name, constant->identifier->name));
}
- current_suite->add_local(constant);
+ current_suite->add_local(constant, current_function);
break;
}
default:
break;
}
- } while (multiline && !check(GDScriptTokenizer::Token::DEDENT) && !is_at_end());
+ } while (multiline && !check(GDScriptTokenizer::Token::DEDENT) && !lambda_ended && !is_at_end());
if (multiline) {
- consume(GDScriptTokenizer::Token::DEDENT, vformat(R"(Missing unindent at the end of %s.)", p_context));
+ if (!lambda_ended) {
+ consume(GDScriptTokenizer::Token::DEDENT, vformat(R"(Missing unindent at the end of %s.)", p_context));
+
+ } else {
+ match(GDScriptTokenizer::Token::DEDENT);
+ }
+ } else if (previous.type == GDScriptTokenizer::Token::SEMICOLON) {
+ consume(GDScriptTokenizer::Token::NEWLINE, vformat(R"(Expected newline after ";" at the end of %s.)", p_context));
}
+ if (p_for_lambda) {
+ lambda_ended = true;
+ }
current_suite = suite->parent_block;
return suite;
}
@@ -1458,6 +1489,10 @@ GDScriptParser::Node *GDScriptParser::parse_statement() {
push_error(R"(Constructor cannot return a value.)");
}
n_return->return_value = parse_expression(false);
+ } else if (in_lambda && !is_statement_end_token()) {
+ // Try to parse it anyway as this might not be the statement end in a lambda.
+ // If this fails the expression will be nullptr, but that's the same as no return, so it's fine.
+ n_return->return_value = parse_expression(false);
}
result = n_return;
@@ -1486,10 +1521,18 @@ GDScriptParser::Node *GDScriptParser::parse_statement() {
default: {
// Expression statement.
ExpressionNode *expression = parse_expression(true); // Allow assignment here.
+ bool has_ended_lambda = false;
if (expression == nullptr) {
- push_error(vformat(R"(Expected statement, found "%s" instead.)", previous.get_name()));
+ if (in_lambda) {
+ // If it's not a valid expression beginning, it might be the continuation of the outer expression where this lambda is.
+ lambda_ended = true;
+ has_ended_lambda = true;
+ } else {
+ push_error(vformat(R"(Expected statement, found "%s" instead.)", previous.get_name()));
+ }
}
end_statement("expression");
+ lambda_ended = lambda_ended || has_ended_lambda;
result = expression;
#ifdef DEBUG_ENABLED
@@ -1513,7 +1556,7 @@ GDScriptParser::Node *GDScriptParser::parse_statement() {
if (unreachable && result != nullptr) {
current_suite->has_unreachable_code = true;
if (current_function) {
- push_warning(result, GDScriptWarning::UNREACHABLE_CODE, current_function->identifier->name);
+ push_warning(result, GDScriptWarning::UNREACHABLE_CODE, current_function->identifier ? current_function->identifier->name : "<anonymous lambda>");
} else {
// TODO: Properties setters and getters with unreachable code are not being warned
}
@@ -1598,7 +1641,7 @@ GDScriptParser::ForNode *GDScriptParser::parse_for() {
SuiteNode *suite = alloc_node<SuiteNode>();
if (n_for->variable) {
- suite->add_local(SuiteNode::Local(n_for->variable));
+ suite->add_local(SuiteNode::Local(n_for->variable, current_function));
}
suite->parent_for = n_for;
@@ -1752,8 +1795,8 @@ GDScriptParser::MatchBranchNode *GDScriptParser::parse_match_branch() {
List<StringName> binds;
branch->patterns[0]->binds.get_key_list(&binds);
- for (List<StringName>::Element *E = binds.front(); E != nullptr; E = E->next()) {
- SuiteNode::Local local(branch->patterns[0]->binds[E->get()]);
+ for (const StringName &E : binds) {
+ SuiteNode::Local local(branch->patterns[0]->binds[E], current_function);
suite->add_local(local);
}
}
@@ -1953,7 +1996,7 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_precedence(Precedence p_pr
// Completion can appear whenever an expression is expected.
make_completion_context(COMPLETION_IDENTIFIER, nullptr);
- GDScriptTokenizer::Token token = advance();
+ GDScriptTokenizer::Token token = current;
ParseFunction prefix_rule = get_rule(token.type)->prefix;
if (prefix_rule == nullptr) {
@@ -1961,6 +2004,8 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_precedence(Precedence p_pr
return nullptr;
}
+ advance(); // Only consume the token if there's a valid rule.
+
ExpressionNode *previous_operand = (this->*prefix_rule)(nullptr, p_can_assign);
while (p_precedence <= get_rule(current.type)->precedence) {
@@ -2002,6 +2047,8 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_identifier(ExpressionNode
if (current_suite != nullptr && current_suite->has_local(identifier->name)) {
const SuiteNode::Local &declaration = current_suite->get_local(identifier->name);
+
+ identifier->source_function = declaration.source_function;
switch (declaration.type) {
case SuiteNode::Local::CONSTANT:
identifier->source = IdentifierNode::LOCAL_CONSTANT;
@@ -2055,6 +2102,9 @@ 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;
@@ -2072,10 +2122,10 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_builtin_constant(Expressio
constant->value = Math_TAU;
break;
case GDScriptTokenizer::Token::CONST_INF:
- constant->value = Math_INF;
+ constant->value = INFINITY;
break;
case GDScriptTokenizer::Token::CONST_NAN:
- constant->value = Math_NAN;
+ constant->value = NAN;
break;
default:
return nullptr; // Unreachable.
@@ -2430,7 +2480,9 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_dictionary(ExpressionNode
switch (dictionary->style) {
case DictionaryNode::LUA_TABLE:
if (key != nullptr && key->type != Node::IDENTIFIER) {
- push_error("Expected identifier as dictionary key.");
+ push_error("Expected identifier as LUA-style dictionary key.");
+ advance();
+ break;
}
if (!match(GDScriptTokenizer::Token::EQUAL)) {
if (match(GDScriptTokenizer::Token::COLON)) {
@@ -2488,7 +2540,7 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_attribute(ExpressionNode *
if (for_completion) {
bool is_builtin = false;
- if (p_previous_operand->type == Node::IDENTIFIER) {
+ if (p_previous_operand && p_previous_operand->type == Node::IDENTIFIER) {
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) {
@@ -2675,6 +2727,65 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_preload(ExpressionNode *p_
return preload;
}
+GDScriptParser::ExpressionNode *GDScriptParser::parse_lambda(ExpressionNode *p_previous_operand, bool p_can_assign) {
+ LambdaNode *lambda = alloc_node<LambdaNode>();
+ lambda->parent_function = current_function;
+ FunctionNode *function = alloc_node<FunctionNode>();
+ function->source_lambda = lambda;
+
+ function->is_static = current_function != nullptr ? current_function->is_static : false;
+
+ if (match(GDScriptTokenizer::Token::IDENTIFIER)) {
+ function->identifier = parse_identifier();
+ }
+
+ bool multiline_context = multiline_stack.back()->get();
+
+ // Reset the multiline stack since we don't want the multiline mode one in the lambda body.
+ push_multiline(false);
+ if (multiline_context) {
+ tokenizer.push_expression_indented_block();
+ }
+
+ push_multiline(true); // For the parameters.
+ if (function->identifier) {
+ consume(GDScriptTokenizer::Token::PARENTHESIS_OPEN, R"(Expected opening "(" after lambda name.)");
+ } else {
+ consume(GDScriptTokenizer::Token::PARENTHESIS_OPEN, R"(Expected opening "(" after "func".)");
+ }
+
+ FunctionNode *previous_function = current_function;
+ current_function = function;
+
+ SuiteNode *body = alloc_node<SuiteNode>();
+ SuiteNode *previous_suite = current_suite;
+ current_suite = body;
+
+ parse_function_signature(function, body, "lambda");
+
+ current_suite = previous_suite;
+
+ bool previous_in_lambda = in_lambda;
+ in_lambda = true;
+
+ function->body = parse_suite("lambda declaration", body, true);
+
+ pop_multiline();
+
+ if (multiline_context) {
+ // If we're in multiline mode, we want to skip the spurious DEDENT and NEWLINE tokens.
+ while (check(GDScriptTokenizer::Token::DEDENT) || check(GDScriptTokenizer::Token::INDENT) || check(GDScriptTokenizer::Token::NEWLINE)) {
+ current = tokenizer.scan(); // Not advance() since we don't want to change the previous token.
+ }
+ tokenizer.pop_expression_indented_block();
+ }
+
+ current_function = previous_function;
+ in_lambda = previous_in_lambda;
+ lambda->function = function;
+ return lambda;
+}
+
GDScriptParser::ExpressionNode *GDScriptParser::parse_invalid_token(ExpressionNode *p_previous_operand, bool p_can_assign) {
// Just for better error messages.
GDScriptTokenizer::Token::Type invalid = previous.type;
@@ -3019,7 +3130,7 @@ GDScriptParser::ParseRule *GDScriptParser::get_rule(GDScriptTokenizer::Token::Ty
{ nullptr, nullptr, PREC_NONE }, // CONST,
{ nullptr, nullptr, PREC_NONE }, // ENUM,
{ nullptr, nullptr, PREC_NONE }, // EXTENDS,
- { nullptr, nullptr, PREC_NONE }, // FUNC,
+ { &GDScriptParser::parse_lambda, nullptr, PREC_NONE }, // FUNC,
{ nullptr, &GDScriptParser::parse_binary_operator, PREC_CONTENT_TEST }, // IN,
{ nullptr, &GDScriptParser::parse_binary_operator, PREC_TYPE_TEST }, // IS,
{ nullptr, nullptr, PREC_NONE }, // NAMESPACE,
@@ -3315,27 +3426,60 @@ template <MultiplayerAPI::RPCMode t_mode>
bool GDScriptParser::network_annotations(const AnnotationNode *p_annotation, Node *p_node) {
ERR_FAIL_COND_V_MSG(p_node->type != Node::VARIABLE && p_node->type != Node::FUNCTION, false, vformat(R"("%s" annotation can only be applied to variables and functions.)", p_annotation->name));
- switch (p_node->type) {
- case Node::VARIABLE: {
- VariableNode *variable = static_cast<VariableNode *>(p_node);
- if (variable->rpc_mode != MultiplayerAPI::RPC_MODE_DISABLED) {
- push_error(R"(RPC annotations can only be used once per variable.)", p_annotation);
+ MultiplayerAPI::RPCConfig rpc_config;
+ rpc_config.rpc_mode = t_mode;
+ for (int i = 0; i < p_annotation->resolved_arguments.size(); i++) {
+ if (i == 0) {
+ String mode = p_annotation->resolved_arguments[i].operator String();
+ if (mode == "any") {
+ rpc_config.rpc_mode = MultiplayerAPI::RPC_MODE_REMOTE;
+ } else if (mode == "master") {
+ rpc_config.rpc_mode = MultiplayerAPI::RPC_MODE_MASTER;
+ } else if (mode == "puppet") {
+ rpc_config.rpc_mode = MultiplayerAPI::RPC_MODE_PUPPET;
+ } else {
+ push_error(R"(Invalid RPC mode. Must be one of: 'any', 'master', or 'puppet')", p_annotation);
+ return false;
}
- variable->rpc_mode = t_mode;
- break;
+ } else if (i == 1) {
+ String sync = p_annotation->resolved_arguments[i].operator String();
+ if (sync == "sync") {
+ rpc_config.sync = true;
+ } else if (sync == "nosync") {
+ rpc_config.sync = false;
+ } else {
+ push_error(R"(Invalid RPC sync mode. Must be one of: 'sync' or 'nosync')", p_annotation);
+ return false;
+ }
+ } else if (i == 2) {
+ String mode = p_annotation->resolved_arguments[i].operator String();
+ if (mode == "reliable") {
+ rpc_config.transfer_mode = MultiplayerPeer::TRANSFER_MODE_RELIABLE;
+ } else if (mode == "unreliable") {
+ rpc_config.transfer_mode = MultiplayerPeer::TRANSFER_MODE_UNRELIABLE;
+ } else if (mode == "ordered") {
+ rpc_config.transfer_mode = MultiplayerPeer::TRANSFER_MODE_UNRELIABLE_ORDERED;
+ } else {
+ push_error(R"(Invalid RPC transfer mode. Must be one of: 'reliable', 'unreliable', 'ordered')", p_annotation);
+ return false;
+ }
+ } else if (i == 3) {
+ rpc_config.channel = p_annotation->resolved_arguments[i].operator int();
}
+ }
+ switch (p_node->type) {
case Node::FUNCTION: {
FunctionNode *function = static_cast<FunctionNode *>(p_node);
- if (function->rpc_mode != MultiplayerAPI::RPC_MODE_DISABLED) {
+ if (function->rpc_config.rpc_mode != MultiplayerAPI::RPC_MODE_DISABLED) {
push_error(R"(RPC annotations can only be used once per function.)", p_annotation);
+ return false;
}
- function->rpc_mode = t_mode;
+ function->rpc_config = rpc_config;
break;
}
default:
return false; // Unreachable.
}
-
return true;
}
@@ -3475,7 +3619,7 @@ void GDScriptParser::TreePrinter::push_text(const String &p_text) {
printed += p_text;
}
-void GDScriptParser::TreePrinter::print_annotation(AnnotationNode *p_annotation) {
+void GDScriptParser::TreePrinter::print_annotation(const AnnotationNode *p_annotation) {
push_text(p_annotation->name);
push_text(" (");
for (int i = 0; i < p_annotation->arguments.size(); i++) {
@@ -3755,6 +3899,10 @@ void GDScriptParser::TreePrinter::print_dictionary(DictionaryNode *p_dictionary)
}
void GDScriptParser::TreePrinter::print_expression(ExpressionNode *p_expression) {
+ if (p_expression == nullptr) {
+ push_text("<invalid expression>");
+ return;
+ }
switch (p_expression->type) {
case Node::ARRAY:
print_array(static_cast<ArrayNode *>(p_expression));
@@ -3783,6 +3931,9 @@ void GDScriptParser::TreePrinter::print_expression(ExpressionNode *p_expression)
case Node::IDENTIFIER:
print_identifier(static_cast<IdentifierNode *>(p_expression));
break;
+ case Node::LAMBDA:
+ print_lambda(static_cast<LambdaNode *>(p_expression));
+ break;
case Node::LITERAL:
print_literal(static_cast<LiteralNode *>(p_expression));
break;
@@ -3842,12 +3993,17 @@ void GDScriptParser::TreePrinter::print_for(ForNode *p_for) {
decrease_indent();
}
-void GDScriptParser::TreePrinter::print_function(FunctionNode *p_function) {
- for (const List<AnnotationNode *>::Element *E = p_function->annotations.front(); E != nullptr; E = E->next()) {
- print_annotation(E->get());
+void GDScriptParser::TreePrinter::print_function(FunctionNode *p_function, const String &p_context) {
+ for (const AnnotationNode *E : p_function->annotations) {
+ print_annotation(E);
+ }
+ push_text(p_context);
+ push_text(" ");
+ if (p_function->identifier) {
+ print_identifier(p_function->identifier);
+ } else {
+ push_text("<anonymous>");
}
- push_text("Function ");
- print_identifier(p_function->identifier);
push_text("( ");
for (int i = 0; i < p_function->parameters.size(); i++) {
if (i > 0) {
@@ -3901,6 +4057,18 @@ void GDScriptParser::TreePrinter::print_if(IfNode *p_if, bool p_is_elif) {
}
}
+void GDScriptParser::TreePrinter::print_lambda(LambdaNode *p_lambda) {
+ print_function(p_lambda->function, "Lambda");
+ push_text("| captures [ ");
+ for (int i = 0; i < p_lambda->captures.size(); i++) {
+ if (i > 0) {
+ push_text(" , ");
+ }
+ push_text(p_lambda->captures[i]->name.operator String());
+ }
+ push_line(" ]");
+}
+
void GDScriptParser::TreePrinter::print_literal(LiteralNode *p_literal) {
// Prefix for string types.
switch (p_literal->value.get_type()) {
@@ -4166,8 +4334,8 @@ void GDScriptParser::TreePrinter::print_unary_op(UnaryOpNode *p_unary_op) {
}
void GDScriptParser::TreePrinter::print_variable(VariableNode *p_variable) {
- for (const List<AnnotationNode *>::Element *E = p_variable->annotations.front(); E != nullptr; E = E->next()) {
- print_annotation(E->get());
+ for (const AnnotationNode *E : p_variable->annotations) {
+ print_annotation(E);
}
push_text("Variable ");
diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h
index 272d21ffce..6a227a55e5 100644
--- a/modules/gdscript/gdscript_parser.h
+++ b/modules/gdscript/gdscript_parser.h
@@ -33,7 +33,7 @@
#include "core/io/multiplayer_api.h"
#include "core/io/resource.h"
-#include "core/object/reference.h"
+#include "core/object/ref_counted.h"
#include "core/object/script_language.h"
#include "core/string/string_name.h"
#include "core/string/ustring.h"
@@ -76,6 +76,7 @@ public:
struct GetNodeNode;
struct IdentifierNode;
struct IfNode;
+ struct LambdaNode;
struct LiteralNode;
struct MatchNode;
struct MatchBranchNode;
@@ -267,6 +268,7 @@ public:
GET_NODE,
IDENTIFIER,
IF,
+ LAMBDA,
LITERAL,
MATCH,
MATCH_BRANCH,
@@ -368,6 +370,7 @@ public:
Variant::Operator variant_op = Variant::OP_MAX;
ExpressionNode *assignee = nullptr;
ExpressionNode *assigned_value = nullptr;
+ bool use_conversion_assign = false;
AssignmentNode() {
type = ASSIGNMENT;
@@ -726,8 +729,9 @@ public:
SuiteNode *body = nullptr;
bool is_static = false;
bool is_coroutine = false;
- MultiplayerAPI::RPCMode rpc_mode = MultiplayerAPI::RPC_MODE_DISABLED;
+ MultiplayerAPI::RPCConfig rpc_config;
MethodInfo info;
+ LambdaNode *source_lambda = nullptr;
#ifdef TOOLS_ENABLED
Vector<Variant> default_arg_values;
String doc_description;
@@ -771,6 +775,7 @@ public:
VariableNode *variable_source;
IdentifierNode *bind_source;
};
+ FunctionNode *source_function = nullptr;
int usages = 0; // Useful for binds/iterator variable.
@@ -789,6 +794,21 @@ public:
}
};
+ struct LambdaNode : public ExpressionNode {
+ FunctionNode *function = nullptr;
+ FunctionNode *parent_function = nullptr;
+ Vector<IdentifierNode *> captures;
+ Map<StringName, int> captures_indices;
+
+ bool has_name() const {
+ return function && function->identifier;
+ }
+
+ LambdaNode() {
+ type = LAMBDA;
+ }
+ };
+
struct LiteralNode : public ExpressionNode {
Variant value;
@@ -942,6 +962,7 @@ public:
IdentifierNode *bind;
};
StringName name;
+ FunctionNode *source_function = nullptr;
int start_line = 0, end_line = 0;
int start_column = 0, end_column = 0;
@@ -951,10 +972,11 @@ public:
String get_name() const;
Local() {}
- Local(ConstantNode *p_constant) {
+ Local(ConstantNode *p_constant, FunctionNode *p_source_function) {
type = CONSTANT;
constant = p_constant;
name = p_constant->identifier->name;
+ source_function = p_source_function;
start_line = p_constant->start_line;
end_line = p_constant->end_line;
@@ -963,10 +985,11 @@ public:
leftmost_column = p_constant->leftmost_column;
rightmost_column = p_constant->rightmost_column;
}
- Local(VariableNode *p_variable) {
+ Local(VariableNode *p_variable, FunctionNode *p_source_function) {
type = VARIABLE;
variable = p_variable;
name = p_variable->identifier->name;
+ source_function = p_source_function;
start_line = p_variable->start_line;
end_line = p_variable->end_line;
@@ -975,10 +998,11 @@ public:
leftmost_column = p_variable->leftmost_column;
rightmost_column = p_variable->rightmost_column;
}
- Local(ParameterNode *p_parameter) {
+ Local(ParameterNode *p_parameter, FunctionNode *p_source_function) {
type = PARAMETER;
parameter = p_parameter;
name = p_parameter->identifier->name;
+ source_function = p_source_function;
start_line = p_parameter->start_line;
end_line = p_parameter->end_line;
@@ -987,10 +1011,11 @@ public:
leftmost_column = p_parameter->leftmost_column;
rightmost_column = p_parameter->rightmost_column;
}
- Local(IdentifierNode *p_identifier) {
+ Local(IdentifierNode *p_identifier, FunctionNode *p_source_function) {
type = FOR_VARIABLE;
bind = p_identifier;
name = p_identifier->name;
+ source_function = p_source_function;
start_line = p_identifier->start_line;
end_line = p_identifier->end_line;
@@ -1015,9 +1040,9 @@ public:
bool has_local(const StringName &p_name) const;
const Local &get_local(const StringName &p_name) const;
template <class T>
- void add_local(T *p_local) {
+ void add_local(T *p_local, FunctionNode *p_source_function) {
locals_indices[p_local->identifier->name] = locals.size();
- locals.push_back(Local(p_local));
+ locals.push_back(Local(p_local, p_source_function));
}
void add_local(const Local &p_local) {
locals_indices[p_local.name] = locals.size();
@@ -1092,9 +1117,9 @@ public:
bool exported = false;
bool onready = false;
PropertyInfo export_info;
- MultiplayerAPI::RPCMode rpc_mode = MultiplayerAPI::RPC_MODE_DISABLED;
int assignments = 0;
int usages = 0;
+ bool use_conversion_assign = false;
#ifdef TOOLS_ENABLED
String doc_description;
#endif // TOOLS_ENABLED
@@ -1191,6 +1216,8 @@ private:
CompletionCall completion_call;
List<CompletionCall> completion_call_stack;
bool passed_cursor = false;
+ bool in_lambda = false;
+ bool lambda_ended = false; // Marker for when a lambda ends, to apply an end of statement if needed.
typedef bool (GDScriptParser::*AnnotationAction)(const AnnotationNode *p_annotation, Node *p_target);
struct AnnotationInfo {
@@ -1278,10 +1305,11 @@ private:
GDScriptTokenizer::Token advance();
bool match(GDScriptTokenizer::Token::Type p_token_type);
- bool check(GDScriptTokenizer::Token::Type p_token_type);
+ bool check(GDScriptTokenizer::Token::Type p_token_type) const;
bool consume(GDScriptTokenizer::Token::Type p_token_type, const String &p_error_message);
- bool is_at_end();
- bool is_statement_end();
+ bool is_at_end() const;
+ bool is_statement_end_token() const;
+ bool is_statement_end() const;
void end_statement(const String &p_context);
void synchronize();
void push_multiline(bool p_state);
@@ -1299,7 +1327,8 @@ private:
EnumNode *parse_enum();
ParameterNode *parse_parameter();
FunctionNode *parse_function();
- SuiteNode *parse_suite(const String &p_context, SuiteNode *p_suite = nullptr);
+ void parse_function_signature(FunctionNode *p_function, SuiteNode *p_body, const String &p_type);
+ SuiteNode *parse_suite(const String &p_context, SuiteNode *p_suite = nullptr, bool p_for_lambda = false);
// Annotations
AnnotationNode *parse_annotation(uint32_t p_valid_targets);
bool register_annotation(const MethodInfo &p_info, uint32_t p_target_kinds, AnnotationAction p_apply, int p_optional_arguments = 0, bool p_is_vararg = false);
@@ -1354,6 +1383,7 @@ private:
ExpressionNode *parse_await(ExpressionNode *p_previous_operand, bool p_can_assign);
ExpressionNode *parse_attribute(ExpressionNode *p_previous_operand, bool p_can_assign);
ExpressionNode *parse_subscript(ExpressionNode *p_previous_operand, bool p_can_assign);
+ ExpressionNode *parse_lambda(ExpressionNode *p_previous_operand, bool p_can_assign);
ExpressionNode *parse_invalid_token(ExpressionNode *p_previous_operand, bool p_can_assign);
TypeNode *parse_type(bool p_allow_void = false);
#ifdef TOOLS_ENABLED
@@ -1401,7 +1431,7 @@ public:
void push_line(const String &p_line = String());
void push_text(const String &p_text);
- void print_annotation(AnnotationNode *p_annotation);
+ void print_annotation(const AnnotationNode *p_annotation);
void print_array(ArrayNode *p_array);
void print_assert(AssertNode *p_assert);
void print_assignment(AssignmentNode *p_assignment);
@@ -1415,10 +1445,11 @@ public:
void print_expression(ExpressionNode *p_expression);
void print_enum(EnumNode *p_enum);
void print_for(ForNode *p_for);
- void print_function(FunctionNode *p_function);
+ void print_function(FunctionNode *p_function, const String &p_context = "Function");
void print_get_node(GetNodeNode *p_get_node);
void print_if(IfNode *p_if, bool p_is_elif = false);
void print_identifier(IdentifierNode *p_identifier);
+ void print_lambda(LambdaNode *p_lambda);
void print_literal(LiteralNode *p_literal);
void print_match(MatchNode *p_match);
void print_match_branch(MatchBranchNode *p_match_branch);
diff --git a/modules/gdscript/gdscript_tokenizer.cpp b/modules/gdscript/gdscript_tokenizer.cpp
index e432dfc891..3f14156dfa 100644
--- a/modules/gdscript/gdscript_tokenizer.cpp
+++ b/modules/gdscript/gdscript_tokenizer.cpp
@@ -242,6 +242,16 @@ void GDScriptTokenizer::set_multiline_mode(bool p_state) {
multiline_mode = p_state;
}
+void GDScriptTokenizer::push_expression_indented_block() {
+ indent_stack_stack.push_back(indent_stack);
+}
+
+void GDScriptTokenizer::pop_expression_indented_block() {
+ ERR_FAIL_COND(indent_stack_stack.size() == 0);
+ indent_stack = indent_stack_stack.back()->get();
+ indent_stack_stack.pop_back();
+}
+
int GDScriptTokenizer::get_cursor_line() const {
return cursor_line;
}
@@ -633,6 +643,8 @@ GDScriptTokenizer::Token GDScriptTokenizer::number() {
push_error(error);
}
previous_was_underscore = true;
+ } else {
+ previous_was_underscore = false;
}
_advance();
}
@@ -704,6 +716,8 @@ GDScriptTokenizer::Token GDScriptTokenizer::number() {
push_error(error);
}
previous_was_underscore = true;
+ } else {
+ previous_was_underscore = false;
}
_advance();
}
diff --git a/modules/gdscript/gdscript_tokenizer.h b/modules/gdscript/gdscript_tokenizer.h
index bea4b14019..84b82c07f0 100644
--- a/modules/gdscript/gdscript_tokenizer.h
+++ b/modules/gdscript/gdscript_tokenizer.h
@@ -217,6 +217,7 @@ private:
Token last_newline;
int pending_indents = 0;
List<int> indent_stack;
+ List<List<int>> indent_stack_stack; // For lambdas, which require manipulating the indentation point.
List<char32_t> paren_stack;
char32_t indent_char = '\0';
int position = 0;
@@ -263,6 +264,8 @@ public:
void set_multiline_mode(bool p_state);
bool is_past_cursor() const;
static String get_token_name(Token::Type p_token_type);
+ void push_expression_indented_block(); // For lambdas, or blocks inside expressions.
+ void pop_expression_indented_block(); // For lambdas, or blocks inside expressions.
GDScriptTokenizer();
};
diff --git a/modules/gdscript/gdscript_utility_functions.cpp b/modules/gdscript/gdscript_utility_functions.cpp
index 64c629662c..62531473c3 100644
--- a/modules/gdscript/gdscript_utility_functions.cpp
+++ b/modules/gdscript/gdscript_utility_functions.cpp
@@ -706,8 +706,8 @@ bool GDScriptUtilityFunctions::function_exists(const StringName &p_function) {
}
void GDScriptUtilityFunctions::get_function_list(List<StringName> *r_functions) {
- for (const List<StringName>::Element *E = utility_function_name_table.front(); E; E = E->next()) {
- r_functions->push_back(E->get());
+ for (const StringName &E : utility_function_name_table) {
+ r_functions->push_back(E);
}
}
diff --git a/modules/gdscript/gdscript_vm.cpp b/modules/gdscript/gdscript_vm.cpp
index b47a4eb992..8a261a88e3 100644
--- a/modules/gdscript/gdscript_vm.cpp
+++ b/modules/gdscript/gdscript_vm.cpp
@@ -33,6 +33,7 @@
#include "core/core_string_names.h"
#include "core/os/os.h"
#include "gdscript.h"
+#include "gdscript_lambda_callable.h"
Variant *GDScriptFunction::_get_variant(int p_address, GDScriptInstance *p_instance, Variant *p_stack, String &r_error) const {
int address = p_address & ADDR_MASK;
@@ -151,6 +152,44 @@ String GDScriptFunction::_get_call_error(const Callable::CallError &p_err, const
return err_text;
}
+void (*type_init_function_table[])(Variant *) = {
+ nullptr, // NIL (shouldn't be called).
+ &VariantInitializer<bool>::init, // BOOL.
+ &VariantInitializer<int64_t>::init, // INT.
+ &VariantInitializer<double>::init, // FLOAT.
+ &VariantInitializer<String>::init, // STRING.
+ &VariantInitializer<Vector2>::init, // VECTOR2.
+ &VariantInitializer<Vector2i>::init, // VECTOR2I.
+ &VariantInitializer<Rect2>::init, // RECT2.
+ &VariantInitializer<Rect2i>::init, // RECT2I.
+ &VariantInitializer<Vector3>::init, // VECTOR3.
+ &VariantInitializer<Vector3i>::init, // VECTOR3I.
+ &VariantInitializer<Transform2D>::init, // TRANSFORM2D.
+ &VariantInitializer<Plane>::init, // PLANE.
+ &VariantInitializer<Quaternion>::init, // QUATERNION.
+ &VariantInitializer<AABB>::init, // AABB.
+ &VariantInitializer<Basis>::init, // BASIS.
+ &VariantInitializer<Transform3D>::init, // TRANSFORM3D.
+ &VariantInitializer<Color>::init, // COLOR.
+ &VariantInitializer<StringName>::init, // STRING_NAME.
+ &VariantInitializer<NodePath>::init, // NODE_PATH.
+ &VariantInitializer<RID>::init, // RID.
+ &VariantTypeAdjust<Object *>::adjust, // OBJECT.
+ &VariantInitializer<Callable>::init, // CALLABLE.
+ &VariantInitializer<Signal>::init, // SIGNAL.
+ &VariantInitializer<Dictionary>::init, // DICTIONARY.
+ &VariantInitializer<Array>::init, // ARRAY.
+ &VariantInitializer<PackedByteArray>::init, // PACKED_BYTE_ARRAY.
+ &VariantInitializer<PackedInt32Array>::init, // PACKED_INT32_ARRAY.
+ &VariantInitializer<PackedInt64Array>::init, // PACKED_INT64_ARRAY.
+ &VariantInitializer<PackedFloat32Array>::init, // PACKED_FLOAT32_ARRAY.
+ &VariantInitializer<PackedFloat64Array>::init, // PACKED_FLOAT64_ARRAY.
+ &VariantInitializer<PackedStringArray>::init, // PACKED_STRING_ARRAY.
+ &VariantInitializer<PackedVector2Array>::init, // PACKED_VECTOR2_ARRAY.
+ &VariantInitializer<PackedVector3Array>::init, // PACKED_VECTOR3_ARRAY.
+ &VariantInitializer<PackedColorArray>::init, // PACKED_COLOR_ARRAY.
+};
+
#if defined(__GNUC__)
#define OPCODES_TABLE \
static const void *switch_table_ops[] = { \
@@ -195,6 +234,7 @@ String GDScriptFunction::_get_call_error(const Callable::CallError &p_err, const
&&OPCODE_CALL_SELF_BASE, \
&&OPCODE_CALL_METHOD_BIND, \
&&OPCODE_CALL_METHOD_BIND_RET, \
+ &&OPCODE_CALL_BUILTIN_STATIC, \
&&OPCODE_CALL_PTRCALL_NO_RETURN, \
&&OPCODE_CALL_PTRCALL_BOOL, \
&&OPCODE_CALL_PTRCALL_INT, \
@@ -208,10 +248,10 @@ String GDScriptFunction::_get_call_error(const Callable::CallError &p_err, const
&&OPCODE_CALL_PTRCALL_VECTOR3I, \
&&OPCODE_CALL_PTRCALL_TRANSFORM2D, \
&&OPCODE_CALL_PTRCALL_PLANE, \
- &&OPCODE_CALL_PTRCALL_QUAT, \
+ &&OPCODE_CALL_PTRCALL_QUATERNION, \
&&OPCODE_CALL_PTRCALL_AABB, \
&&OPCODE_CALL_PTRCALL_BASIS, \
- &&OPCODE_CALL_PTRCALL_TRANSFORM, \
+ &&OPCODE_CALL_PTRCALL_TRANSFORM3D, \
&&OPCODE_CALL_PTRCALL_COLOR, \
&&OPCODE_CALL_PTRCALL_STRING_NAME, \
&&OPCODE_CALL_PTRCALL_NODE_PATH, \
@@ -232,6 +272,7 @@ String GDScriptFunction::_get_call_error(const Callable::CallError &p_err, const
&&OPCODE_CALL_PTRCALL_PACKED_COLOR_ARRAY, \
&&OPCODE_AWAIT, \
&&OPCODE_AWAIT_RESUME, \
+ &&OPCODE_CREATE_LAMBDA, \
&&OPCODE_JUMP, \
&&OPCODE_JUMP_IF, \
&&OPCODE_JUMP_IF_NOT, \
@@ -294,7 +335,7 @@ String GDScriptFunction::_get_call_error(const Callable::CallError &p_err, const
&&OPCODE_TYPE_ADJUST_VECTOR3I, \
&&OPCODE_TYPE_ADJUST_TRANSFORM2D, \
&&OPCODE_TYPE_ADJUST_PLANE, \
- &&OPCODE_TYPE_ADJUST_QUAT, \
+ &&OPCODE_TYPE_ADJUST_QUATERNION, \
&&OPCODE_TYPE_ADJUST_AABB, \
&&OPCODE_TYPE_ADJUST_BASIS, \
&&OPCODE_TYPE_ADJUST_TRANSFORM, \
@@ -357,7 +398,7 @@ String GDScriptFunction::_get_call_error(const Callable::CallError &p_err, const
#define OP_GET_VECTOR3I get_vector3i
#define OP_GET_RECT2 get_rect2
#define OP_GET_RECT2I get_rect2i
-#define OP_GET_QUAT get_quat
+#define OP_GET_QUATERNION get_quaternion
#define OP_GET_COLOR get_color
#define OP_GET_STRING get_string
#define OP_GET_STRING_NAME get_string_name
@@ -375,7 +416,7 @@ String GDScriptFunction::_get_call_error(const Callable::CallError &p_err, const
#define OP_GET_PACKED_VECTOR2_ARRAY get_vector2_array
#define OP_GET_PACKED_VECTOR3_ARRAY get_vector3_array
#define OP_GET_PACKED_COLOR_ARRAY get_color_array
-#define OP_GET_TRANSFORM get_transform
+#define OP_GET_TRANSFORM3D get_transform
#define OP_GET_TRANSFORM2D get_transform2d
#define OP_GET_PLANE get_plane
#define OP_GET_AABB get_aabb
@@ -489,6 +530,10 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
memnew_placement(&stack[ADDR_STACK_CLASS], Variant(script));
+ for (const Map<int, Variant::Type>::Element *E = temporary_slots.front(); E; E = E->next()) {
+ type_init_function_table[E->get()](&stack[E->key()]);
+ }
+
String err_text;
#ifdef DEBUG_ENABLED
@@ -1452,13 +1497,17 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
if (err.error != Callable::CallError::CALL_OK) {
String methodstr = *methodname;
String basestr = _get_var_type(base);
+ bool is_callable = false;
if (methodstr == "call") {
- if (argc >= 1) {
+ if (argc >= 1 && base->get_type() != Variant::CALLABLE) {
methodstr = String(*argptrs[0]) + " (via call)";
if (err.error == Callable::CallError::CALL_ERROR_INVALID_ARGUMENT) {
err.argument += 1;
}
+ } else {
+ methodstr = base->operator String() + " (Callable)";
+ is_callable = true;
}
} else if (methodstr == "free") {
if (err.error == Callable::CallError::CALL_ERROR_INVALID_METHOD) {
@@ -1478,7 +1527,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
}
}
}
- err_text = _get_call_error(err, "function '" + methodstr + "' in base '" + basestr + "'", (const Variant **)argptrs);
+ err_text = _get_call_error(err, "function '" + methodstr + (is_callable ? "" : "' in base '" + basestr) + "'", (const Variant **)argptrs);
OPCODE_BREAK;
}
#endif
@@ -1567,6 +1616,51 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
}
DISPATCH_OPCODE;
+ OPCODE(OPCODE_CALL_BUILTIN_STATIC) {
+ CHECK_SPACE(4 + instr_arg_count);
+
+ ip += instr_arg_count;
+
+ GD_ERR_BREAK(_code_ptr[ip + 1] < 0 || _code_ptr[ip + 1] >= Variant::VARIANT_MAX);
+ Variant::Type builtin_type = (Variant::Type)_code_ptr[ip + 1];
+
+ int methodname_idx = _code_ptr[ip + 2];
+ GD_ERR_BREAK(methodname_idx < 0 || methodname_idx >= _global_names_count);
+ const StringName *methodname = &_global_names_ptr[methodname_idx];
+
+ int argc = _code_ptr[ip + 3];
+ 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;
+ Variant::call_static(builtin_type, *methodname, argptrs, argc, *ret, 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 '" + methodname->operator String() + "' in type '" + Variant::get_type_name(builtin_type) + "'", argptrs);
+ OPCODE_BREAK;
+ }
+#endif
+
+ ip += 4;
+ }
+ DISPATCH_OPCODE;
+
#ifdef DEBUG_ENABLED
#define OPCODE_CALL_PTR(m_type) \
OPCODE(OPCODE_CALL_PTRCALL_##m_type) { \
@@ -1639,10 +1733,10 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
OPCODE_CALL_PTR(VECTOR3I);
OPCODE_CALL_PTR(TRANSFORM2D);
OPCODE_CALL_PTR(PLANE);
- OPCODE_CALL_PTR(QUAT);
+ OPCODE_CALL_PTR(QUATERNION);
OPCODE_CALL_PTR(AABB);
OPCODE_CALL_PTR(BASIS);
- OPCODE_CALL_PTR(TRANSFORM);
+ OPCODE_CALL_PTR(TRANSFORM3D);
OPCODE_CALL_PTR(COLOR);
OPCODE_CALL_PTR(STRING_NAME);
OPCODE_CALL_PTR(NODE_PATH);
@@ -1876,7 +1970,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
#ifdef DEBUG_ENABLED
if (err.error != Callable::CallError::CALL_OK) {
// TODO: Add this information in debug.
- String methodstr = "<unkown function>";
+ String methodstr = "<unknown function>";
if (dst->get_type() == Variant::STRING) {
// Call provided error string.
err_text = "Error calling GDScript utility function '" + methodstr + "': " + String(*dst);
@@ -1895,7 +1989,10 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
ip += instr_arg_count;
- int self_fun = _code_ptr[ip + 1];
+ int argc = _code_ptr[ip + 1];
+ GD_ERR_BREAK(argc < 0);
+
+ int self_fun = _code_ptr[ip + 2];
#ifdef DEBUG_ENABLED
if (self_fun < 0 || self_fun >= _global_names_count) {
err_text = "compiler bug, function name not found";
@@ -1904,9 +2001,6 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
#endif
const StringName *methodname = &_global_names_ptr[self_fun];
- int argc = _code_ptr[ip + 2];
- GD_ERR_BREAK(argc < 0);
-
Variant **argptrs = instruction_args;
GET_INSTRUCTION_ARG(dst, argc);
@@ -2057,6 +2151,34 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
}
DISPATCH_OPCODE;
+ OPCODE(OPCODE_CREATE_LAMBDA) {
+ CHECK_SPACE(2 + instr_arg_count);
+
+ 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;
+ }
+
+ GDScriptLambdaCallable *callable = memnew(GDScriptLambdaCallable(Ref<GDScript>(script), 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];
@@ -3028,10 +3150,10 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
OPCODE_TYPE_ADJUST(VECTOR3I, Vector3i);
OPCODE_TYPE_ADJUST(TRANSFORM2D, Transform2D);
OPCODE_TYPE_ADJUST(PLANE, Plane);
- OPCODE_TYPE_ADJUST(QUAT, Quat);
+ OPCODE_TYPE_ADJUST(QUATERNION, Quaternion);
OPCODE_TYPE_ADJUST(AABB, AABB);
OPCODE_TYPE_ADJUST(BASIS, Basis);
- OPCODE_TYPE_ADJUST(TRANSFORM, Transform);
+ OPCODE_TYPE_ADJUST(TRANSFORM, Transform3D);
OPCODE_TYPE_ADJUST(COLOR, Color);
OPCODE_TYPE_ADJUST(STRING_NAME, StringName);
OPCODE_TYPE_ADJUST(NODE_PATH, NodePath);
diff --git a/modules/gdscript/language_server/gdscript_extend_parser.cpp b/modules/gdscript/language_server/gdscript_extend_parser.cpp
index e63b6ab20e..d106b3b541 100644
--- a/modules/gdscript/language_server/gdscript_extend_parser.cpp
+++ b/modules/gdscript/language_server/gdscript_extend_parser.cpp
@@ -32,7 +32,6 @@
#include "../gdscript.h"
#include "../gdscript_analyzer.h"
-#include "core/io/json.h"
#include "gdscript_language_protocol.h"
#include "gdscript_workspace.h"
@@ -40,8 +39,7 @@ void ExtendGDScriptParser::update_diagnostics() {
diagnostics.clear();
const List<ParserError> &errors = get_errors();
- for (const List<ParserError>::Element *E = errors.front(); E != nullptr; E = E->next()) {
- const ParserError &error = E->get();
+ for (const ParserError &error : errors) {
lsp::Diagnostic diagnostic;
diagnostic.severity = lsp::DiagnosticSeverity::Error;
diagnostic.message = error.message;
@@ -49,8 +47,9 @@ void ExtendGDScriptParser::update_diagnostics() {
diagnostic.code = -1;
lsp::Range range;
lsp::Position pos;
- int line = LINE_NUMBER_TO_INDEX(error.line);
- const String &line_text = get_lines()[line];
+ const PackedStringArray lines = get_lines();
+ int line = CLAMP(LINE_NUMBER_TO_INDEX(error.line), 0, lines.size() - 1);
+ const String &line_text = lines[line];
pos.line = line;
pos.character = line_text.length() - line_text.strip_edges(true, false).length();
range.start = pos;
@@ -61,8 +60,7 @@ void ExtendGDScriptParser::update_diagnostics() {
}
const List<GDScriptWarning> &warnings = get_warnings();
- for (const List<GDScriptWarning>::Element *E = warnings.front(); E; E = E->next()) {
- const GDScriptWarning &warning = E->get();
+ for (const GDScriptWarning &warning : warnings) {
lsp::Diagnostic diagnostic;
diagnostic.severity = lsp::DiagnosticSeverity::Warning;
diagnostic.message = "(" + warning.get_name() + "): " + warning.get_message();
@@ -152,9 +150,9 @@ void ExtendGDScriptParser::parse_class_symbol(const GDScriptParser::ClassNode *p
}
r_symbol.kind = lsp::SymbolKind::Class;
r_symbol.deprecated = false;
- r_symbol.range.start.line = LINE_NUMBER_TO_INDEX(p_class->start_line);
- r_symbol.range.start.character = LINE_NUMBER_TO_INDEX(p_class->start_column);
- r_symbol.range.end.line = LINE_NUMBER_TO_INDEX(p_class->end_line);
+ r_symbol.range.start.line = p_class->start_line;
+ r_symbol.range.start.character = p_class->start_column;
+ r_symbol.range.end.line = lines.size();
r_symbol.selectionRange.start.line = r_symbol.range.start.line;
r_symbol.detail = "class " + r_symbol.name;
bool is_root_class = &r_symbol == &class_symbol;
@@ -167,7 +165,7 @@ void ExtendGDScriptParser::parse_class_symbol(const GDScriptParser::ClassNode *p
case ClassNode::Member::VARIABLE: {
lsp::DocumentSymbol symbol;
symbol.name = m.variable->identifier->name;
- symbol.kind = lsp::SymbolKind::Variable;
+ symbol.kind = m.variable->property == VariableNode::PropertyStyle::PROP_NONE ? lsp::SymbolKind::Variable : lsp::SymbolKind::Property;
symbol.deprecated = false;
symbol.range.start.line = LINE_NUMBER_TO_INDEX(m.variable->start_line);
symbol.range.start.character = LINE_NUMBER_TO_INDEX(m.variable->start_column);
@@ -182,7 +180,7 @@ void ExtendGDScriptParser::parse_class_symbol(const GDScriptParser::ClassNode *p
symbol.detail += ": " + m.get_datatype().to_string();
}
if (m.variable->initializer != nullptr && m.variable->initializer->is_constant) {
- symbol.detail += " = " + JSON::print(m.variable->initializer->reduced_value);
+ symbol.detail += " = " + m.variable->initializer->reduced_value.to_json_string();
}
symbol.documentation = parse_documentation(LINE_NUMBER_TO_INDEX(m.variable->start_line));
@@ -223,10 +221,10 @@ void ExtendGDScriptParser::parse_class_symbol(const GDScriptParser::ClassNode *p
}
}
} else {
- value_text = JSON::print(default_value);
+ value_text = default_value.to_json_string();
}
} else {
- value_text = JSON::print(default_value);
+ value_text = default_value.to_json_string();
}
if (!value_text.is_empty()) {
symbol.detail += " = " + value_text;
@@ -319,7 +317,7 @@ void ExtendGDScriptParser::parse_function_symbol(const GDScriptParser::FunctionN
const String uri = get_uri();
r_symbol.name = p_func->identifier->name;
- r_symbol.kind = lsp::SymbolKind::Function;
+ r_symbol.kind = p_func->is_static ? lsp::SymbolKind::Function : lsp::SymbolKind::Method;
r_symbol.detail = "func " + String(p_func->identifier->name) + "(";
r_symbol.deprecated = false;
r_symbol.range.start.line = LINE_NUMBER_TO_INDEX(p_func->start_line);
@@ -352,8 +350,7 @@ void ExtendGDScriptParser::parse_function_symbol(const GDScriptParser::FunctionN
parameters += ": " + parameter->get_datatype().to_string();
}
if (parameter->default_value != nullptr) {
- String value = JSON::print(parameter->default_value->reduced_value);
- parameters += " = " + value;
+ parameters += " = " + parameter->default_value->reduced_value.to_json_string();
}
}
r_symbol.detail += parameters + ")";
@@ -361,24 +358,73 @@ void ExtendGDScriptParser::parse_function_symbol(const GDScriptParser::FunctionN
r_symbol.detail += " -> " + p_func->get_datatype().to_string();
}
- for (int i = 0; i < p_func->body->locals.size(); i++) {
- const SuiteNode::Local &local = p_func->body->locals[i];
- lsp::DocumentSymbol symbol;
- symbol.name = local.name;
- symbol.kind = local.type == SuiteNode::Local::CONSTANT ? lsp::SymbolKind::Constant : lsp::SymbolKind::Variable;
- symbol.range.start.line = LINE_NUMBER_TO_INDEX(local.start_line);
- symbol.range.start.character = LINE_NUMBER_TO_INDEX(local.start_column);
- symbol.range.end.line = LINE_NUMBER_TO_INDEX(local.end_line);
- symbol.range.end.character = LINE_NUMBER_TO_INDEX(local.end_column);
- symbol.uri = uri;
- symbol.script_path = path;
- symbol.detail = SuiteNode::Local::CONSTANT ? "const " : "var ";
- symbol.detail += symbol.name;
- if (local.get_datatype().is_hard_type()) {
- symbol.detail += ": " + local.get_datatype().to_string();
+ List<GDScriptParser::SuiteNode *> function_nodes;
+
+ List<GDScriptParser::Node *> node_stack;
+ node_stack.push_back(p_func->body);
+
+ while (!node_stack.is_empty()) {
+ GDScriptParser::Node *node = node_stack[0];
+ node_stack.pop_front();
+
+ switch (node->type) {
+ case GDScriptParser::TypeNode::IF: {
+ GDScriptParser::IfNode *if_node = (GDScriptParser::IfNode *)node;
+ node_stack.push_back(if_node->true_block);
+ if (if_node->false_block) {
+ node_stack.push_back(if_node->false_block);
+ }
+ } break;
+
+ case GDScriptParser::TypeNode::FOR: {
+ GDScriptParser::ForNode *for_node = (GDScriptParser::ForNode *)node;
+ node_stack.push_back(for_node->loop);
+ } break;
+
+ case GDScriptParser::TypeNode::WHILE: {
+ GDScriptParser::WhileNode *while_node = (GDScriptParser::WhileNode *)node;
+ node_stack.push_back(while_node->loop);
+ } break;
+
+ case GDScriptParser::TypeNode::MATCH_BRANCH: {
+ GDScriptParser::MatchBranchNode *match_node = (GDScriptParser::MatchBranchNode *)node;
+ node_stack.push_back(match_node->block);
+ } break;
+
+ case GDScriptParser::TypeNode::SUITE: {
+ GDScriptParser::SuiteNode *suite_node = (GDScriptParser::SuiteNode *)node;
+ function_nodes.push_back(suite_node);
+ for (int i = 0; i < suite_node->statements.size(); ++i) {
+ node_stack.push_back(suite_node->statements[i]);
+ }
+ } break;
+
+ default:
+ continue;
+ }
+ }
+
+ for (List<GDScriptParser::SuiteNode *>::Element *N = function_nodes.front(); N; N = N->next()) {
+ const GDScriptParser::SuiteNode *suite_node = N->get();
+ for (int i = 0; i < suite_node->locals.size(); i++) {
+ const SuiteNode::Local &local = suite_node->locals[i];
+ lsp::DocumentSymbol symbol;
+ symbol.name = local.name;
+ symbol.kind = local.type == SuiteNode::Local::CONSTANT ? lsp::SymbolKind::Constant : lsp::SymbolKind::Variable;
+ symbol.range.start.line = LINE_NUMBER_TO_INDEX(local.start_line);
+ symbol.range.start.character = LINE_NUMBER_TO_INDEX(local.start_column);
+ symbol.range.end.line = LINE_NUMBER_TO_INDEX(local.end_line);
+ symbol.range.end.character = LINE_NUMBER_TO_INDEX(local.end_column);
+ symbol.uri = uri;
+ symbol.script_path = path;
+ symbol.detail = local.type == SuiteNode::Local::CONSTANT ? "const " : "var ";
+ symbol.detail += symbol.name;
+ if (local.get_datatype().is_hard_type()) {
+ symbol.detail += ": " + local.get_datatype().to_string();
+ }
+ symbol.documentation = parse_documentation(LINE_NUMBER_TO_INDEX(local.start_line));
+ r_symbol.children.push_back(symbol);
}
- symbol.documentation = parse_documentation(LINE_NUMBER_TO_INDEX(local.start_line));
- r_symbol.children.push_back(symbol);
}
}
@@ -419,8 +465,8 @@ String ExtendGDScriptParser::parse_documentation(int p_line, bool p_docs_down) {
}
String doc;
- for (List<String>::Element *E = doc_lines.front(); E; E = E->next()) {
- doc += E->get() + "\n";
+ for (const String &E : doc_lines) {
+ doc += E + "\n";
}
return doc;
}
@@ -647,7 +693,9 @@ Dictionary ExtendGDScriptParser::dump_function_api(const GDScriptParser::Functio
ERR_FAIL_NULL_V(p_func, func);
func["name"] = p_func->identifier->name;
func["return_type"] = p_func->get_datatype().to_string();
- func["rpc_mode"] = p_func->rpc_mode;
+ func["rpc_mode"] = p_func->rpc_config.rpc_mode;
+ func["rpc_transfer_mode"] = p_func->rpc_config.transfer_mode;
+ func["rpc_transfer_channel"] = p_func->rpc_config.channel;
Array parameters;
for (int i = 0; i < p_func->parameters.size(); i++) {
Dictionary arg;
diff --git a/modules/gdscript/language_server/gdscript_language_protocol.cpp b/modules/gdscript/language_server/gdscript_language_protocol.cpp
index 912c9a174e..b6c48468f5 100644
--- a/modules/gdscript/language_server/gdscript_language_protocol.cpp
+++ b/modules/gdscript/language_server/gdscript_language_protocol.cpp
@@ -31,8 +31,6 @@
#include "gdscript_language_protocol.h"
#include "core/config/project_settings.h"
-#include "core/io/json.h"
-#include "core/os/copymem.h"
#include "editor/doc_tools.h"
#include "editor/editor_log.h"
#include "editor/editor_node.h"
@@ -130,13 +128,13 @@ Error GDScriptLanguageProtocol::on_client_connected() {
peer->connection = tcp_peer;
clients.set(next_client_id, peer);
next_client_id++;
- EditorNode::get_log()->add_message("Connection Taken", EditorLog::MSG_TYPE_EDITOR);
+ EditorNode::get_log()->add_message("[LSP] Connection Taken", EditorLog::MSG_TYPE_EDITOR);
return OK;
}
void GDScriptLanguageProtocol::on_client_disconnected(const int &p_client_id) {
clients.erase(p_client_id);
- EditorNode::get_log()->add_message("Disconnected", EditorLog::MSG_TYPE_EDITOR);
+ EditorNode::get_log()->add_message("[LSP] Disconnected", EditorLog::MSG_TYPE_EDITOR);
}
String GDScriptLanguageProtocol::process_message(const String &p_text) {
@@ -195,7 +193,7 @@ Dictionary GDScriptLanguageProtocol::initialize(const Dictionary &p_params) {
vformat("GDScriptLanguageProtocol: Can't initialize invalid peer '%d'.", latest_client_id));
Ref<LSPeer> peer = clients.get(latest_client_id);
if (peer != nullptr) {
- String msg = JSON::print(request);
+ String msg = Variant(request).to_json_string();
msg = format_output(msg);
(*peer)->res_queue.push_back(msg.utf8());
}
@@ -256,7 +254,7 @@ void GDScriptLanguageProtocol::poll() {
}
}
-Error GDScriptLanguageProtocol::start(int p_port, const IP_Address &p_bind_ip) {
+Error GDScriptLanguageProtocol::start(int p_port, const IPAddress &p_bind_ip) {
return server->listen(p_port, p_bind_ip);
}
@@ -281,7 +279,7 @@ void GDScriptLanguageProtocol::notify_client(const String &p_method, const Varia
ERR_FAIL_COND(peer == nullptr);
Dictionary message = make_notification(p_method, p_params);
- String msg = JSON::print(message);
+ String msg = Variant(message).to_json_string();
msg = format_output(msg);
peer->res_queue.push_back(msg.utf8());
}
@@ -295,10 +293,10 @@ bool GDScriptLanguageProtocol::is_goto_native_symbols_enabled() const {
}
GDScriptLanguageProtocol::GDScriptLanguageProtocol() {
- server.instance();
+ server.instantiate();
singleton = this;
- workspace.instance();
- text_document.instance();
+ workspace.instantiate();
+ text_document.instantiate();
set_scope("textDocument", text_document.ptr());
set_scope("completionItem", text_document.ptr());
set_scope("workspace", workspace.ptr());
diff --git a/modules/gdscript/language_server/gdscript_language_protocol.h b/modules/gdscript/language_server/gdscript_language_protocol.h
index 8b08ae0655..5a2dd55c46 100644
--- a/modules/gdscript/language_server/gdscript_language_protocol.h
+++ b/modules/gdscript/language_server/gdscript_language_protocol.h
@@ -28,8 +28,8 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef GDSCRIPT_PROTOCAL_SERVER_H
-#define GDSCRIPT_PROTOCAL_SERVER_H
+#ifndef GDSCRIPT_LANGUAGE_PROTOCOL_H
+#define GDSCRIPT_LANGUAGE_PROTOCOL_H
#include "core/io/stream_peer.h"
#include "core/io/stream_peer_tcp.h"
@@ -37,7 +37,13 @@
#include "gdscript_text_document.h"
#include "gdscript_workspace.h"
#include "lsp.hpp"
+
+#include "modules/modules_enabled.gen.h"
+#ifdef MODULE_JSONRPC_ENABLED
#include "modules/jsonrpc/jsonrpc.h"
+#else
+#error "Can't build GDScript LSP without JSONRPC module."
+#endif
#define LSP_MAX_BUFFER_SIZE 4194304
#define LSP_MAX_CLIENTS 8
@@ -46,7 +52,7 @@ class GDScriptLanguageProtocol : public JSONRPC {
GDCLASS(GDScriptLanguageProtocol, JSONRPC)
private:
- struct LSPeer : Reference {
+ struct LSPeer : RefCounted {
Ref<StreamPeerTCP> connection;
uint8_t req_buf[LSP_MAX_BUFFER_SIZE];
@@ -69,7 +75,7 @@ private:
static GDScriptLanguageProtocol *singleton;
HashMap<int, Ref<LSPeer>> clients;
- Ref<TCP_Server> server;
+ Ref<TCPServer> server;
int latest_client_id = 0;
int next_client_id = 0;
@@ -97,7 +103,7 @@ public:
_FORCE_INLINE_ bool is_initialized() const { return _initialized; }
void poll();
- Error start(int p_port, const IP_Address &p_bind_ip);
+ Error start(int p_port, const IPAddress &p_bind_ip);
void stop();
void notify_client(const String &p_method, const Variant &p_params = Variant(), int p_client_id = -1);
@@ -108,4 +114,4 @@ public:
GDScriptLanguageProtocol();
};
-#endif
+#endif // GDSCRIPT_LANGUAGE_PROTOCOL_H
diff --git a/modules/gdscript/language_server/gdscript_language_server.cpp b/modules/gdscript/language_server/gdscript_language_server.cpp
index 98ada9de4d..c47164d95b 100644
--- a/modules/gdscript/language_server/gdscript_language_server.cpp
+++ b/modules/gdscript/language_server/gdscript_language_server.cpp
@@ -30,7 +30,7 @@
#include "gdscript_language_server.h"
-#include "core/os/file_access.h"
+#include "core/io/file_access.h"
#include "core/os/os.h"
#include "editor/editor_log.h"
#include "editor/editor_node.h"
@@ -78,7 +78,7 @@ void GDScriptLanguageServer::thread_main(void *p_userdata) {
void GDScriptLanguageServer::start() {
port = (int)_EDITOR_GET("network/language_server/remote_port");
use_thread = (bool)_EDITOR_GET("network/language_server/use_thread");
- if (protocol.start(port, IP_Address("127.0.0.1")) == OK) {
+ if (protocol.start(port, IPAddress("127.0.0.1")) == OK) {
EditorNode::get_log()->add_message("--- GDScript language server started ---", EditorLog::MSG_TYPE_EDITOR);
if (use_thread) {
thread_running = true;
@@ -101,7 +101,7 @@ void GDScriptLanguageServer::stop() {
}
void register_lsp_types() {
- ClassDB::register_class<GDScriptLanguageProtocol>();
- ClassDB::register_class<GDScriptTextDocument>();
- ClassDB::register_class<GDScriptWorkspace>();
+ GDREGISTER_CLASS(GDScriptLanguageProtocol);
+ GDREGISTER_CLASS(GDScriptTextDocument);
+ GDREGISTER_CLASS(GDScriptWorkspace);
}
diff --git a/modules/gdscript/language_server/gdscript_text_document.cpp b/modules/gdscript/language_server/gdscript_text_document.cpp
index 030633274c..9574c765bc 100644
--- a/modules/gdscript/language_server/gdscript_text_document.cpp
+++ b/modules/gdscript/language_server/gdscript_text_document.cpp
@@ -40,11 +40,14 @@
void GDScriptTextDocument::_bind_methods() {
ClassDB::bind_method(D_METHOD("didOpen"), &GDScriptTextDocument::didOpen);
+ ClassDB::bind_method(D_METHOD("didClose"), &GDScriptTextDocument::didClose);
ClassDB::bind_method(D_METHOD("didChange"), &GDScriptTextDocument::didChange);
+ ClassDB::bind_method(D_METHOD("didSave"), &GDScriptTextDocument::didSave);
ClassDB::bind_method(D_METHOD("nativeSymbol"), &GDScriptTextDocument::nativeSymbol);
ClassDB::bind_method(D_METHOD("documentSymbol"), &GDScriptTextDocument::documentSymbol);
ClassDB::bind_method(D_METHOD("completion"), &GDScriptTextDocument::completion);
ClassDB::bind_method(D_METHOD("resolve"), &GDScriptTextDocument::resolve);
+ ClassDB::bind_method(D_METHOD("rename"), &GDScriptTextDocument::rename);
ClassDB::bind_method(D_METHOD("foldingRange"), &GDScriptTextDocument::foldingRange);
ClassDB::bind_method(D_METHOD("codeLens"), &GDScriptTextDocument::codeLens);
ClassDB::bind_method(D_METHOD("documentLink"), &GDScriptTextDocument::documentLink);
@@ -61,6 +64,11 @@ void GDScriptTextDocument::didOpen(const Variant &p_param) {
sync_script_content(doc.uri, doc.text);
}
+void GDScriptTextDocument::didClose(const Variant &p_param) {
+ // Left empty on purpose. Godot does nothing special on closing a document,
+ // but it satisfies LSP clients that require didClose be implemented.
+}
+
void GDScriptTextDocument::didChange(const Variant &p_param) {
lsp::TextDocumentItem doc = load_document_item(p_param);
Dictionary dict = p_param;
@@ -73,6 +81,20 @@ void GDScriptTextDocument::didChange(const Variant &p_param) {
sync_script_content(doc.uri, doc.text);
}
+void GDScriptTextDocument::didSave(const Variant &p_param) {
+ lsp::TextDocumentItem doc = load_document_item(p_param);
+ Dictionary dict = p_param;
+ String text = dict["text"];
+
+ sync_script_content(doc.uri, text);
+
+ /*String path = GDScriptLanguageProtocol::get_singleton()->get_workspace()->get_file_path(doc.uri);
+
+ Ref<GDScript> script = ResourceLoader::load(path);
+ script->load_source_code(path);
+ script->reload(true);*/
+}
+
lsp::TextDocumentItem GDScriptTextDocument::load_document_item(const Variant &p_param) {
lsp::TextDocumentItem doc;
Dictionary params = p_param;
@@ -151,8 +173,7 @@ Array GDScriptTextDocument::completion(const Dictionary &p_params) {
int i = 0;
arr.resize(options.size());
- for (const List<ScriptCodeCompletionOption>::Element *E = options.front(); E; E = E->next()) {
- const ScriptCodeCompletionOption &option = E->get();
+ for (const ScriptCodeCompletionOption &option : options) {
lsp::CompletionItem item;
item.label = option.display;
item.data = request_data;
@@ -210,6 +231,14 @@ Array GDScriptTextDocument::completion(const Dictionary &p_params) {
return arr;
}
+Dictionary GDScriptTextDocument::rename(const Dictionary &p_params) {
+ lsp::TextDocumentPositionParams params;
+ params.load(p_params);
+ String new_name = p_params["newName"];
+
+ return GDScriptLanguageProtocol::get_singleton()->get_workspace()->rename(params, new_name);
+}
+
Dictionary GDScriptTextDocument::resolve(const Dictionary &p_params) {
lsp::CompletionItem item;
item.load(p_params);
@@ -288,8 +317,8 @@ Array GDScriptTextDocument::documentLink(const Dictionary &p_params) {
List<lsp::DocumentLink> links;
GDScriptLanguageProtocol::get_singleton()->get_workspace()->resolve_document_links(params.textDocument.uri, links);
- for (const List<lsp::DocumentLink>::Element *E = links.front(); E; E = E->next()) {
- ret.push_back(E->get().to_json());
+ for (const lsp::DocumentLink &E : links) {
+ ret.push_back(E.to_json());
}
return ret;
}
@@ -316,8 +345,8 @@ Variant GDScriptTextDocument::hover(const Dictionary &p_params) {
Array contents;
List<const lsp::DocumentSymbol *> list;
GDScriptLanguageProtocol::get_singleton()->get_workspace()->resolve_related_symbols(params, list);
- for (List<const lsp::DocumentSymbol *>::Element *E = list.front(); E; E = E->next()) {
- if (const lsp::DocumentSymbol *s = E->get()) {
+ for (const lsp::DocumentSymbol *&E : list) {
+ if (const lsp::DocumentSymbol *s = E) {
contents.push_back(s->render().value);
}
}
@@ -367,7 +396,7 @@ Variant GDScriptTextDocument::declaration(const Dictionary &p_params) {
id = "class_global:" + symbol->native_class + ":" + symbol->name;
break;
}
- call_deferred("show_native_symbol_in_editor", id);
+ call_deferred(SNAME("show_native_symbol_in_editor"), id);
} else {
notify_client_show_symbol(symbol);
}
@@ -400,11 +429,19 @@ GDScriptTextDocument::~GDScriptTextDocument() {
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);
+
EditorFileSystem::get_singleton()->update_file(path);
+ Error error;
+ Ref<GDScript> script = ResourceLoader::load(path, "", ResourceFormatLoader::CACHE_MODE_REUSE, &error);
+ if (error == OK) {
+ if (script->load_source_code(path) == OK) {
+ script->reload(true);
+ }
+ }
}
void GDScriptTextDocument::show_native_symbol_in_editor(const String &p_symbol_id) {
- ScriptEditor::get_singleton()->call_deferred("_help_class_goto", p_symbol_id);
+ ScriptEditor::get_singleton()->call_deferred(SNAME("_help_class_goto"), p_symbol_id);
DisplayServer::get_singleton()->window_move_to_foreground();
}
@@ -424,8 +461,8 @@ Array GDScriptTextDocument::find_symbols(const lsp::TextDocumentPositionParams &
} else if (GDScriptLanguageProtocol::get_singleton()->is_smart_resolve_enabled()) {
List<const lsp::DocumentSymbol *> list;
GDScriptLanguageProtocol::get_singleton()->get_workspace()->resolve_related_symbols(p_location, list);
- for (List<const lsp::DocumentSymbol *>::Element *E = list.front(); E; E = E->next()) {
- if (const lsp::DocumentSymbol *s = E->get()) {
+ for (const lsp::DocumentSymbol *&E : list) {
+ if (const lsp::DocumentSymbol *s = E) {
if (!s->uri.is_empty()) {
lsp::Location location;
location.uri = s->uri;
diff --git a/modules/gdscript/language_server/gdscript_text_document.h b/modules/gdscript/language_server/gdscript_text_document.h
index 792e601bc1..9021c84a3f 100644
--- a/modules/gdscript/language_server/gdscript_text_document.h
+++ b/modules/gdscript/language_server/gdscript_text_document.h
@@ -31,19 +31,21 @@
#ifndef GDSCRIPT_TEXT_DOCUMENT_H
#define GDSCRIPT_TEXT_DOCUMENT_H
-#include "core/object/reference.h"
-#include "core/os/file_access.h"
+#include "core/io/file_access.h"
+#include "core/object/ref_counted.h"
#include "lsp.hpp"
-class GDScriptTextDocument : public Reference {
- GDCLASS(GDScriptTextDocument, Reference)
+class GDScriptTextDocument : public RefCounted {
+ GDCLASS(GDScriptTextDocument, RefCounted)
protected:
static void _bind_methods();
FileAccess *file_checker;
void didOpen(const Variant &p_param);
+ void didClose(const Variant &p_param);
void didChange(const Variant &p_param);
+ void didSave(const Variant &p_param);
void sync_script_content(const String &p_path, const String &p_content);
void show_native_symbol_in_editor(const String &p_symbol_id);
@@ -60,6 +62,7 @@ public:
Array documentSymbol(const Dictionary &p_params);
Array completion(const Dictionary &p_params);
Dictionary resolve(const Dictionary &p_params);
+ Dictionary rename(const Dictionary &p_params);
Array foldingRange(const Dictionary &p_params);
Array codeLens(const Dictionary &p_params);
Array documentLink(const Dictionary &p_params);
diff --git a/modules/gdscript/language_server/gdscript_workspace.cpp b/modules/gdscript/language_server/gdscript_workspace.cpp
index 69cad1a335..1512b4bb89 100644
--- a/modules/gdscript/language_server/gdscript_workspace.cpp
+++ b/modules/gdscript/language_server/gdscript_workspace.cpp
@@ -42,6 +42,7 @@
#include "scene/resources/packed_scene.h"
void GDScriptWorkspace::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("didDeleteFiles"), &GDScriptWorkspace::did_delete_files);
ClassDB::bind_method(D_METHOD("symbol"), &GDScriptWorkspace::symbol);
ClassDB::bind_method(D_METHOD("parse_script", "path", "content"), &GDScriptWorkspace::parse_script);
ClassDB::bind_method(D_METHOD("parse_local_script", "path"), &GDScriptWorkspace::parse_local_script);
@@ -51,6 +52,16 @@ void GDScriptWorkspace::_bind_methods() {
ClassDB::bind_method(D_METHOD("generate_script_api", "path"), &GDScriptWorkspace::generate_script_api);
}
+void GDScriptWorkspace::did_delete_files(const Dictionary &p_params) {
+ Array files = p_params["files"];
+ for (int i = 0; i < files.size(); ++i) {
+ Dictionary file = files[i];
+ String uri = file["uri"];
+ String path = get_file_path(uri);
+ parse_script(path, "");
+ }
+}
+
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);
@@ -105,11 +116,40 @@ const lsp::DocumentSymbol *GDScriptWorkspace::get_script_symbol(const String &p_
return nullptr;
}
+const lsp::DocumentSymbol *GDScriptWorkspace::get_parameter_symbol(const lsp::DocumentSymbol *p_parent, const String &symbol_identifier) {
+ for (int i = 0; i < p_parent->children.size(); ++i) {
+ const lsp::DocumentSymbol *parameter_symbol = &p_parent->children[i];
+ if (!parameter_symbol->detail.is_empty() && parameter_symbol->name == symbol_identifier) {
+ return parameter_symbol;
+ }
+ }
+
+ return nullptr;
+}
+
+const lsp::DocumentSymbol *GDScriptWorkspace::get_local_symbol(const ExtendGDScriptParser *p_parser, const String &p_symbol_identifier) {
+ const lsp::DocumentSymbol *class_symbol = &p_parser->get_symbols();
+
+ for (int i = 0; i < class_symbol->children.size(); ++i) {
+ if (class_symbol->children[i].kind == lsp::SymbolKind::Function || class_symbol->children[i].kind == lsp::SymbolKind::Class) {
+ const lsp::DocumentSymbol *function_symbol = &class_symbol->children[i];
+
+ for (int l = 0; l < function_symbol->children.size(); ++l) {
+ const lsp::DocumentSymbol *local = &function_symbol->children[l];
+ if (!local->detail.is_empty() && local->name == p_symbol_identifier) {
+ return local;
+ }
+ }
+ }
+ }
+
+ return nullptr;
+}
+
void GDScriptWorkspace::reload_all_workspace_scripts() {
List<String> paths;
list_script_files("res://", paths);
- for (List<String>::Element *E = paths.front(); E; E = E->next()) {
- const String &path = E->get();
+ for (const String &path : paths) {
Error err;
String content = FileAccess::get_file_as_string(path, &err);
ERR_CONTINUE(err != OK);
@@ -177,7 +217,9 @@ Array GDScriptWorkspace::symbol(const Dictionary &p_params) {
E->get()->get_symbols().symbol_tree_as_list(E->key(), script_symbols);
for (int i = 0; i < script_symbols.size(); ++i) {
if (query.is_subsequence_ofi(script_symbols[i].name)) {
- arr.push_back(script_symbols[i].to_json());
+ lsp::DocumentedSymbolInformation symbol = script_symbols[i];
+ symbol.location.uri = get_file_uri(symbol.location.uri);
+ arr.push_back(symbol.to_json());
}
}
}
@@ -219,18 +261,13 @@ Error GDScriptWorkspace::initialize() {
class_symbol.children.push_back(symbol);
}
- Vector<DocData::PropertyDoc> properties;
- properties.append_array(class_data.properties);
- const int theme_prop_start_idx = properties.size();
- properties.append_array(class_data.theme_properties);
-
for (int i = 0; i < class_data.properties.size(); i++) {
const DocData::PropertyDoc &data = class_data.properties[i];
lsp::DocumentSymbol symbol;
symbol.name = data.name;
symbol.native_class = class_name;
symbol.kind = lsp::SymbolKind::Property;
- symbol.detail = String(i >= theme_prop_start_idx ? "<Theme> var" : "var") + " " + class_name + "." + data.name;
+ symbol.detail = "var " + class_name + "." + data.name;
if (data.enumeration.length()) {
symbol.detail += ": " + data.enumeration;
} else {
@@ -240,6 +277,17 @@ Error GDScriptWorkspace::initialize() {
class_symbol.children.push_back(symbol);
}
+ for (int i = 0; i < class_data.theme_properties.size(); i++) {
+ const DocData::ThemeItemDoc &data = class_data.theme_properties[i];
+ lsp::DocumentSymbol symbol;
+ symbol.name = data.name;
+ symbol.native_class = class_name;
+ symbol.kind = lsp::SymbolKind::Property;
+ symbol.detail = "<Theme> var " + class_name + "." + data.name + ": " + data.type;
+ symbol.documentation = data.description;
+ class_symbol.children.push_back(symbol);
+ }
+
Vector<DocData::MethodDoc> methods_signals;
methods_signals.append_array(class_data.methods);
const int signal_start_idx = methods_signals.size();
@@ -338,6 +386,50 @@ Error GDScriptWorkspace::parse_script(const String &p_path, const String &p_cont
return err;
}
+Dictionary GDScriptWorkspace::rename(const lsp::TextDocumentPositionParams &p_doc_pos, const String &new_name) {
+ Error err;
+ String path = get_file_path(p_doc_pos.textDocument.uri);
+
+ lsp::WorkspaceEdit edit;
+
+ List<String> paths;
+ list_script_files("res://", paths);
+
+ const lsp::DocumentSymbol *reference_symbol = resolve_symbol(p_doc_pos);
+ if (reference_symbol) {
+ String identifier = reference_symbol->name;
+
+ for (List<String>::Element *PE = paths.front(); PE; PE = PE->next()) {
+ PackedStringArray content = FileAccess::get_file_as_string(PE->get(), &err).split("\n");
+ for (int i = 0; i < content.size(); ++i) {
+ String line = content[i];
+
+ int character = line.find(identifier);
+ while (character > -1) {
+ lsp::TextDocumentPositionParams params;
+
+ lsp::TextDocumentIdentifier text_doc;
+ text_doc.uri = get_file_uri(PE->get());
+
+ params.textDocument = text_doc;
+ params.position.line = i;
+ params.position.character = character;
+
+ const lsp::DocumentSymbol *other_symbol = resolve_symbol(params);
+
+ if (other_symbol == reference_symbol) {
+ edit.add_change(text_doc.uri, i, character, character + identifier.length(), new_name);
+ }
+
+ character = line.find(identifier, character + 1);
+ }
+ }
+ }
+ }
+
+ return edit.to_json();
+}
+
Error GDScriptWorkspace::parse_local_script(const String &p_path) {
Error err;
String content = FileAccess::get_file_as_string(p_path, &err);
@@ -413,7 +505,7 @@ Node *GDScriptWorkspace::_get_owner_scene_node(String p_path) {
RES 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->instance();
+ owner_scene_node = owner_packed_scene->instantiate();
break;
}
}
@@ -428,8 +520,31 @@ void GDScriptWorkspace::completion(const lsp::CompletionParams &p_params, List<S
if (const ExtendGDScriptParser *parser = get_parse_result(path)) {
Node *owner_scene_node = _get_owner_scene_node(path);
+
+ Array stack;
+ Node *current = nullptr;
+ if (owner_scene_node != nullptr) {
+ stack.push_back(owner_scene_node);
+
+ while (!stack.is_empty()) {
+ current = stack.pop_back();
+ Ref<GDScript> script = current->get_script();
+ if (script.is_valid() && script->get_path() == path) {
+ break;
+ }
+ for (int i = 0; i < current->get_child_count(); ++i) {
+ stack.push_back(current->get_child(i));
+ }
+ }
+
+ Ref<GDScript> script = current->get_script();
+ if (!script.is_valid() || script->get_path() != path) {
+ current = owner_scene_node;
+ }
+ }
+
String code = parser->get_text_for_completion(p_params.position);
- GDScriptLanguage::get_singleton()->complete_code(code, path, owner_scene_node, r_options, forced, call_hint);
+ GDScriptLanguage::get_singleton()->complete_code(code, path, current, r_options, forced, call_hint);
if (owner_scene_node) {
memdelete(owner_scene_node);
}
@@ -466,10 +581,16 @@ const lsp::DocumentSymbol *GDScriptWorkspace::resolve_symbol(const lsp::TextDocu
String target_script_path = path;
if (!ret.script.is_null()) {
target_script_path = ret.script->get_path();
+ } else if (!ret.class_path.is_empty()) {
+ target_script_path = ret.class_path;
}
if (const ExtendGDScriptParser *target_parser = get_parse_result(target_script_path)) {
symbol = target_parser->get_symbol_defined_at_line(LINE_NUMBER_TO_INDEX(ret.location));
+
+ if (symbol && symbol->kind == lsp::SymbolKind::Function && symbol->name != symbol_identifier) {
+ symbol = get_parameter_symbol(symbol, symbol_identifier);
+ }
}
} else {
@@ -481,6 +602,10 @@ const lsp::DocumentSymbol *GDScriptWorkspace::resolve_symbol(const lsp::TextDocu
}
} else {
symbol = parser->get_member_symbol(symbol_identifier);
+
+ if (!symbol) {
+ symbol = get_local_symbol(parser, symbol_identifier);
+ }
}
}
}
@@ -546,8 +671,8 @@ const lsp::DocumentSymbol *GDScriptWorkspace::resolve_native_symbol(const lsp::N
void GDScriptWorkspace::resolve_document_links(const String &p_uri, List<lsp::DocumentLink> &r_list) {
if (const ExtendGDScriptParser *parser = get_parse_successed_script(get_file_path(p_uri))) {
const List<lsp::DocumentLink> &links = parser->get_document_links();
- for (const List<lsp::DocumentLink>::Element *E = links.front(); E; E = E->next()) {
- r_list.push_back(E->get());
+ for (const lsp::DocumentLink &E : links) {
+ r_list.push_back(E);
}
}
}
@@ -574,8 +699,7 @@ Error GDScriptWorkspace::resolve_signature(const lsp::TextDocumentPositionParams
GDScriptLanguageProtocol::get_singleton()->get_workspace()->resolve_related_symbols(text_pos, symbols);
}
- for (List<const lsp::DocumentSymbol *>::Element *E = symbols.front(); E; E = E->next()) {
- const lsp::DocumentSymbol *symbol = E->get();
+ for (const lsp::DocumentSymbol *const &symbol : symbols) {
if (symbol->kind == lsp::SymbolKind::Method || symbol->kind == lsp::SymbolKind::Function) {
lsp::SignatureInformation signature_info;
signature_info.label = symbol->detail;
diff --git a/modules/gdscript/language_server/gdscript_workspace.h b/modules/gdscript/language_server/gdscript_workspace.h
index 7fd8bfcf20..9496677449 100644
--- a/modules/gdscript/language_server/gdscript_workspace.h
+++ b/modules/gdscript/language_server/gdscript_workspace.h
@@ -37,8 +37,8 @@
#include "gdscript_extend_parser.h"
#include "lsp.hpp"
-class GDScriptWorkspace : public Reference {
- GDCLASS(GDScriptWorkspace, Reference);
+class GDScriptWorkspace : public RefCounted {
+ GDCLASS(GDScriptWorkspace, RefCounted);
private:
void _get_owners(EditorFileSystemDirectory *efsd, String p_path, List<String> &owners);
@@ -52,6 +52,8 @@ protected:
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;
+ const lsp::DocumentSymbol *get_parameter_symbol(const lsp::DocumentSymbol *p_parent, const String &symbol_identifier);
+ const lsp::DocumentSymbol *get_local_symbol(const ExtendGDScriptParser *p_parser, const String &p_symbol_identifier);
void reload_all_workspace_scripts();
@@ -89,6 +91,8 @@ public:
void resolve_document_links(const String &p_uri, List<lsp::DocumentLink> &r_list);
Dictionary generate_script_api(const String &p_path);
Error resolve_signature(const lsp::TextDocumentPositionParams &p_doc_pos, lsp::SignatureHelp &r_signature);
+ void did_delete_files(const Dictionary &p_params);
+ Dictionary rename(const lsp::TextDocumentPositionParams &p_doc_pos, const String &new_name);
GDScriptWorkspace();
~GDScriptWorkspace();
diff --git a/modules/gdscript/language_server/lsp.hpp b/modules/gdscript/language_server/lsp.hpp
index 6635098be2..9ac6c6bd4e 100644
--- a/modules/gdscript/language_server/lsp.hpp
+++ b/modules/gdscript/language_server/lsp.hpp
@@ -255,6 +255,62 @@ struct TextEdit {
};
/**
+ * The edits to be applied.
+ */
+struct WorkspaceEdit {
+ /**
+ * Holds changes to existing resources.
+ */
+ Map<String, Vector<TextEdit>> changes;
+
+ _FORCE_INLINE_ Dictionary to_json() const {
+ Dictionary dict;
+
+ Dictionary out_changes;
+ for (Map<String, Vector<TextEdit>>::Element *E = changes.front(); E; E = E->next()) {
+ Array edits;
+ for (int i = 0; i < E->get().size(); ++i) {
+ Dictionary text_edit;
+ text_edit["range"] = E->get()[i].range.to_json();
+ text_edit["newText"] = E->get()[i].newText;
+ edits.push_back(text_edit);
+ }
+ out_changes[E->key()] = edits;
+ }
+ dict["changes"] = out_changes;
+
+ return dict;
+ }
+
+ _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();
+ for (int i = 0; i < edit_list.size(); ++i) {
+ TextEdit edit = edit_list[i];
+ if (edit.range.start.character == start_character) {
+ return;
+ }
+ }
+ }
+
+ TextEdit new_edit;
+ new_edit.newText = new_text;
+ new_edit.range.start.line = line;
+ new_edit.range.start.character = start_character;
+ 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);
+ } else {
+ Vector<TextEdit> edit_list;
+ edit_list.push_back(new_edit);
+ changes.insert(uri, edit_list);
+ }
+ }
+};
+
+/**
* Represents a reference to a command.
* Provides a title which will be used to represent a command in the UI.
* Commands are identified by a string identifier.
@@ -486,7 +542,7 @@ struct TextDocumentSyncOptions {
* If present save notifications are sent to the server. If omitted the notification should not be
* sent.
*/
- bool save = false;
+ SaveOptions save;
Dictionary to_json() {
Dictionary dict;
@@ -494,7 +550,7 @@ struct TextDocumentSyncOptions {
dict["willSave"] = willSave;
dict["openClose"] = openClose;
dict["change"] = change;
- dict["save"] = save;
+ dict["save"] = save.to_json();
return dict;
}
};
@@ -766,7 +822,7 @@ struct MarkupContent {
// Use namespace instead of enumeration to follow the LSP specifications
// lsp::EnumName::EnumValue is OK but lsp::EnumValue is not
-// And here C++ compilers are unhappy with our enumeration name like Color, File, Reference etc.
+// And here C++ compilers are unhappy with our enumeration name like Color, File, RefCounted etc.
/**
* The kind of a completion entry.
*/
@@ -788,7 +844,7 @@ static const int Keyword = 14;
static const int Snippet = 15;
static const int Color = 16;
static const int File = 17;
-static const int Reference = 18;
+static const int RefCounted = 18;
static const int Folder = 19;
static const int EnumMember = 20;
static const int Constant = 21;
@@ -1018,32 +1074,32 @@ struct CompletionList {
* A symbol kind.
*/
namespace SymbolKind {
-static const int File = 0;
-static const int Module = 1;
-static const int Namespace = 2;
-static const int Package = 3;
-static const int Class = 4;
-static const int Method = 5;
-static const int Property = 6;
-static const int Field = 7;
-static const int Constructor = 8;
-static const int Enum = 9;
-static const int Interface = 10;
-static const int Function = 11;
-static const int Variable = 12;
-static const int Constant = 13;
-static const int String = 14;
-static const int Number = 15;
-static const int Boolean = 16;
-static const int Array = 17;
-static const int Object = 18;
-static const int Key = 19;
-static const int Null = 20;
-static const int EnumMember = 21;
-static const int Struct = 22;
-static const int Event = 23;
-static const int Operator = 24;
-static const int TypeParameter = 25;
+static const int File = 1;
+static const int Module = 2;
+static const int Namespace = 3;
+static const int Package = 4;
+static const int Class = 5;
+static const int Method = 6;
+static const int Property = 7;
+static const int Field = 8;
+static const int Constructor = 9;
+static const int Enum = 10;
+static const int Interface = 11;
+static const int Function = 12;
+static const int Variable = 13;
+static const int Constant = 14;
+static const int String = 15;
+static const int Number = 16;
+static const int Boolean = 17;
+static const int Array = 18;
+static const int Object = 19;
+static const int Key = 20;
+static const int Null = 21;
+static const int EnumMember = 22;
+static const int Struct = 23;
+static const int Event = 24;
+static const int Operator = 25;
+static const int TypeParameter = 26;
}; // namespace SymbolKind
/**
@@ -1528,6 +1584,114 @@ struct SignatureHelp {
}
};
+/**
+ * A pattern to describe in which file operation requests or notifications
+ * the server is interested in.
+ */
+struct FileOperationPattern {
+ /**
+ * The glob pattern to match.
+ */
+ String glob = "**/*.gd";
+
+ /**
+ * Whether to match `file`s or `folder`s with this pattern.
+ *
+ * Matches both if undefined.
+ */
+ String matches = "file";
+
+ Dictionary to_json() const {
+ Dictionary dict;
+
+ dict["glob"] = glob;
+ dict["matches"] = matches;
+
+ return dict;
+ }
+};
+
+/**
+ * A filter to describe in which file operation requests or notifications
+ * the server is interested in.
+ */
+struct FileOperationFilter {
+ /**
+ * The actual file operation pattern.
+ */
+ FileOperationPattern pattern;
+
+ Dictionary to_json() const {
+ Dictionary dict;
+
+ dict["pattern"] = pattern.to_json();
+
+ return dict;
+ }
+};
+
+/**
+ * The options to register for file operations.
+ */
+struct FileOperationRegistrationOptions {
+ /**
+ * The actual filters.
+ */
+ Vector<FileOperationFilter> filters;
+
+ FileOperationRegistrationOptions() {
+ filters.push_back(FileOperationFilter());
+ }
+
+ Dictionary to_json() const {
+ Dictionary dict;
+
+ Array filts;
+ for (int i = 0; i < filters.size(); i++) {
+ filts.push_back(filters[i].to_json());
+ }
+ dict["filters"] = filts;
+
+ return dict;
+ }
+};
+
+/**
+ * The server is interested in file notifications/requests.
+ */
+struct FileOperations {
+ /**
+ * The server is interested in receiving didDeleteFiles file notifications.
+ */
+ FileOperationRegistrationOptions didDelete;
+
+ Dictionary to_json() const {
+ Dictionary dict;
+
+ dict["didDelete"] = didDelete.to_json();
+
+ return dict;
+ }
+};
+
+/**
+ * Workspace specific server capabilities
+ */
+struct Workspace {
+ /**
+ * The server is interested in file notifications/requests.
+ */
+ FileOperations fileOperations;
+
+ Dictionary to_json() const {
+ Dictionary dict;
+
+ dict["fileOperations"] = fileOperations.to_json();
+
+ return dict;
+ }
+};
+
struct ServerCapabilities {
/**
* Defines how text documents are synced. Is either a detailed structure defining each notification or
@@ -1590,6 +1754,11 @@ struct ServerCapabilities {
bool workspaceSymbolProvider = true;
/**
+ * The server supports workspace folder.
+ */
+ Workspace workspace;
+
+ /**
* The server provides code actions. The `CodeActionOptions` return type is only
* valid if the client signals code action literal support via the property
* `textDocument.codeAction.codeActionLiteralSupport`.
@@ -1676,6 +1845,7 @@ struct ServerCapabilities {
dict["documentHighlightProvider"] = documentHighlightProvider;
dict["documentSymbolProvider"] = documentSymbolProvider;
dict["workspaceSymbolProvider"] = workspaceSymbolProvider;
+ dict["workspace"] = workspace.to_json();
dict["codeActionProvider"] = codeActionProvider;
dict["documentFormattingProvider"] = documentFormattingProvider;
dict["documentRangeFormattingProvider"] = documentRangeFormattingProvider;
diff --git a/modules/gdscript/register_types.cpp b/modules/gdscript/register_types.cpp
index 2d2f94f5e0..c2b1981f31 100644
--- a/modules/gdscript/register_types.cpp
+++ b/modules/gdscript/register_types.cpp
@@ -30,10 +30,10 @@
#include "register_types.h"
+#include "core/io/dir_access.h"
+#include "core/io/file_access.h"
#include "core/io/file_access_encrypted.h"
#include "core/io/resource_loader.h"
-#include "core/os/dir_access.h"
-#include "core/os/file_access.h"
#include "gdscript.h"
#include "gdscript_analyzer.h"
#include "gdscript_cache.h"
@@ -92,12 +92,12 @@ public:
static void _editor_init() {
Ref<EditorExportGDScript> gd_export;
- gd_export.instance();
+ gd_export.instantiate();
EditorExport::get_singleton()->add_export_plugin(gd_export);
#ifdef TOOLS_ENABLED
Ref<GDScriptSyntaxHighlighter> gdscript_syntax_highlighter;
- gdscript_syntax_highlighter.instance();
+ gdscript_syntax_highlighter.instantiate();
ScriptEditor::get_singleton()->register_syntax_highlighter(gdscript_syntax_highlighter);
#endif
@@ -112,15 +112,15 @@ static void _editor_init() {
#endif // TOOLS_ENABLED
void register_gdscript_types() {
- ClassDB::register_class<GDScript>();
+ GDREGISTER_CLASS(GDScript);
script_language_gd = memnew(GDScriptLanguage);
ScriptServer::register_language(script_language_gd);
- resource_loader_gd.instance();
+ resource_loader_gd.instantiate();
ResourceLoader::add_resource_format_loader(resource_loader_gd);
- resource_saver_gd.instance();
+ resource_saver_gd.instantiate();
ResourceSaver::add_resource_format_saver(resource_saver_gd);
gdscript_cache = memnew(GDScriptCache);
@@ -128,7 +128,7 @@ void register_gdscript_types() {
#ifdef TOOLS_ENABLED
EditorNode::add_init_callback(_editor_init);
- gdscript_translation_parser_plugin.instance();
+ gdscript_translation_parser_plugin.instantiate();
EditorTranslationParser::get_singleton()->add_parser(gdscript_translation_parser_plugin, EditorTranslationParser::STANDARD);
#endif // TOOLS_ENABLED
diff --git a/modules/gdscript/tests/gdscript_test_runner.cpp b/modules/gdscript/tests/gdscript_test_runner.cpp
index 76ae43e792..03a48bf071 100644
--- a/modules/gdscript/tests/gdscript_test_runner.cpp
+++ b/modules/gdscript/tests/gdscript_test_runner.cpp
@@ -37,8 +37,8 @@
#include "core/config/project_settings.h"
#include "core/core_string_names.h"
+#include "core/io/dir_access.h"
#include "core/io/file_access_pack.h"
-#include "core/os/dir_access.h"
#include "core/os/os.h"
#include "core/string/string_builder.h"
#include "scene/resources/packed_scene.h"
@@ -75,14 +75,14 @@ void init_autoloads() {
Node *n = nullptr;
if (res->is_class("PackedScene")) {
Ref<PackedScene> ps = res;
- n = ps->instance();
+ n = ps->instantiate();
} else if (res->is_class("Script")) {
Ref<Script> script_res = res;
StringName ibt = script_res->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);
- Object *obj = ClassDB::instance(ibt);
+ Object *obj = ClassDB::instantiate(ibt);
ERR_CONTINUE_MSG(obj == nullptr,
"Cannot instance script for autoload, expected 'Node' inheritance, got: " +
@@ -257,6 +257,7 @@ bool GDScriptTestRunner::make_tests() {
ERR_FAIL_COND_V_MSG(err != OK, false, "Could not open specified test directory.");
+ source_dir = dir->get_current_dir() + "/"; // Make it absolute path.
return make_tests_for_dir(dir->get_current_dir());
}
@@ -294,7 +295,7 @@ void GDScriptTestRunner::handle_cmdline() {
String test_cmd = "--gdscript-test";
String gen_cmd = "--gdscript-generate-tests";
- for (List<String>::Element *E = cmdline_args.front(); E != nullptr; E = E->next()) {
+ for (List<String>::Element *E = cmdline_args.front(); E; E = E->next()) {
String &cmd = E->get();
if (cmd == test_cmd || cmd == gen_cmd) {
if (E->next() == nullptr) {
@@ -361,11 +362,9 @@ void GDScriptTest::error_handler(void *p_this, const char *p_function, const cha
break;
}
- builder.append("\n>> ");
- builder.append(p_function);
- builder.append("\n>> ");
+ builder.append("\n>> on function: ");
builder.append(p_function);
- builder.append("\n>> ");
+ builder.append("()\n>> ");
builder.append(String(p_file).trim_prefix(self->base_dir));
builder.append("\n>> ");
builder.append(itos(p_line));
@@ -421,7 +420,7 @@ GDScriptTest::TestResult GDScriptTest::execute_test_code(bool p_is_generating) {
// Create script.
Ref<GDScript> script;
- script.instance();
+ script.instantiate();
script->set_path(source_file);
script->set_script_path(source_file);
err = script->load_source_code(source_file);
@@ -441,8 +440,8 @@ GDScriptTest::TestResult GDScriptTest::execute_test_code(bool p_is_generating) {
result.output = get_text_for_status(result.status) + "\n";
const List<GDScriptParser::ParserError> &errors = parser.get_errors();
- for (const List<GDScriptParser::ParserError>::Element *E = errors.front(); E; E = E->next()) {
- result.output += E->get().message + "\n"; // TODO: line, column?
+ for (const GDScriptParser::ParserError &E : errors) {
+ result.output += E.message + "\n"; // TODO: line, column?
break; // Only the first error since the following might be cascading.
}
if (!p_is_generating) {
@@ -460,8 +459,8 @@ GDScriptTest::TestResult GDScriptTest::execute_test_code(bool p_is_generating) {
result.output = get_text_for_status(result.status) + "\n";
const List<GDScriptParser::ParserError> &errors = parser.get_errors();
- for (const List<GDScriptParser::ParserError>::Element *E = errors.front(); E; E = E->next()) {
- result.output += E->get().message + "\n"; // TODO: line, column?
+ for (const GDScriptParser::ParserError &E : errors) {
+ result.output += E.message + "\n"; // TODO: line, column?
break; // Only the first error since the following might be cascading.
}
if (!p_is_generating) {
@@ -471,8 +470,8 @@ GDScriptTest::TestResult GDScriptTest::execute_test_code(bool p_is_generating) {
}
StringBuilder warning_string;
- for (const List<GDScriptWarning>::Element *E = parser.get_warnings().front(); E != nullptr; E = E->next()) {
- const GDScriptWarning warning = E->get();
+ for (const GDScriptWarning &E : parser.get_warnings()) {
+ const GDScriptWarning warning = E;
warning_string.append(">> WARNING");
warning_string.append("\n>> Line: ");
warning_string.append(itos(warning.start_line));
@@ -511,10 +510,10 @@ GDScriptTest::TestResult GDScriptTest::execute_test_code(bool p_is_generating) {
script->reload();
// Create object instance for test.
- Object *obj = ClassDB::instance(script->get_native()->get_name());
- Ref<Reference> obj_ref;
- if (obj->is_reference()) {
- obj_ref = Ref<Reference>(Object::cast_to<Reference>(obj));
+ Object *obj = ClassDB::instantiate(script->get_native()->get_name());
+ Ref<RefCounted> obj_ref;
+ if (obj->is_ref_counted()) {
+ obj_ref = Ref<RefCounted>(Object::cast_to<RefCounted>(obj));
}
obj->set_script(script);
GDScriptInstance *instance = static_cast<GDScriptInstance *>(obj->get_script_instance());
diff --git a/modules/gdscript/tests/gdscript_test_runner_suite.h b/modules/gdscript/tests/gdscript_test_runner_suite.h
index 136907b316..cf4e61f07d 100644
--- a/modules/gdscript/tests/gdscript_test_runner_suite.h
+++ b/modules/gdscript/tests/gdscript_test_runner_suite.h
@@ -48,6 +48,27 @@ TEST_SUITE("[Modules][GDScript]") {
}
}
+TEST_CASE("[Modules][GDScript] Load source code dynamically and run it") {
+ Ref<GDScript> gdscript = memnew(GDScript);
+ gdscript->set_source_code(R"(
+extends RefCounted
+
+func _init():
+ set_meta("result", 42)
+)");
+ // A spurious `Condition "err" is true` message is printed (despite parsing being successful and returning `OK`).
+ // Silence it.
+ ERR_PRINT_OFF;
+ const Error error = gdscript->reload();
+ ERR_PRINT_ON;
+ CHECK_MESSAGE(error == OK, "The script should parse successfully.");
+
+ // Run the script by assigning it to a reference-counted object.
+ Ref<RefCounted> ref_counted = memnew(RefCounted);
+ ref_counted->set_script(gdscript);
+ CHECK_MESSAGE(int(ref_counted->get_meta("result")) == 42, "The script should assign object metadata successfully.");
+}
+
} // namespace GDScriptTests
#endif // GDSCRIPT_TEST_RUNNER_SUITE_H
diff --git a/modules/gdscript/tests/scripts/analyzer/features/call_self_get_name.gd b/modules/gdscript/tests/scripts/analyzer/features/call_self_get_name.gd
new file mode 100644
index 0000000000..d21d8bce96
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/call_self_get_name.gd
@@ -0,0 +1,9 @@
+extends Node
+
+func test():
+ set_name("TestNodeName")
+ if get_name() == &"TestNodeName":
+ print("Name is equal")
+ else:
+ print("Name is not equal")
+ print(get_name() is StringName)
diff --git a/modules/gdscript/tests/scripts/analyzer/features/call_self_get_name.out b/modules/gdscript/tests/scripts/analyzer/features/call_self_get_name.out
new file mode 100644
index 0000000000..dc4348d9c3
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/call_self_get_name.out
@@ -0,0 +1,3 @@
+GDTEST_OK
+Name is equal
+True
diff --git a/modules/gdscript/tests/scripts/parser-errors/missing-argument.gd b/modules/gdscript/tests/scripts/parser/errors/missing_argument.gd
index c56ad94095..c56ad94095 100644
--- a/modules/gdscript/tests/scripts/parser-errors/missing-argument.gd
+++ b/modules/gdscript/tests/scripts/parser/errors/missing_argument.gd
diff --git a/modules/gdscript/tests/scripts/parser-errors/missing-argument.out b/modules/gdscript/tests/scripts/parser/errors/missing_argument.out
index fc2a891109..fc2a891109 100644
--- a/modules/gdscript/tests/scripts/parser-errors/missing-argument.out
+++ b/modules/gdscript/tests/scripts/parser/errors/missing_argument.out
diff --git a/modules/gdscript/tests/scripts/parser-errors/missing-closing-expr-paren.gd b/modules/gdscript/tests/scripts/parser/errors/missing_closing_expr_paren.gd
index a1077e1985..a1077e1985 100644
--- a/modules/gdscript/tests/scripts/parser-errors/missing-closing-expr-paren.gd
+++ b/modules/gdscript/tests/scripts/parser/errors/missing_closing_expr_paren.gd
diff --git a/modules/gdscript/tests/scripts/parser-errors/missing-closing-expr-paren.out b/modules/gdscript/tests/scripts/parser/errors/missing_closing_expr_paren.out
index 7326afa33d..7326afa33d 100644
--- a/modules/gdscript/tests/scripts/parser-errors/missing-closing-expr-paren.out
+++ b/modules/gdscript/tests/scripts/parser/errors/missing_closing_expr_paren.out
diff --git a/modules/gdscript/tests/scripts/parser-errors/missing-colon.gd b/modules/gdscript/tests/scripts/parser/errors/missing_colon.gd
index 62cb633e9e..62cb633e9e 100644
--- a/modules/gdscript/tests/scripts/parser-errors/missing-colon.gd
+++ b/modules/gdscript/tests/scripts/parser/errors/missing_colon.gd
diff --git a/modules/gdscript/tests/scripts/parser-errors/missing-colon.out b/modules/gdscript/tests/scripts/parser/errors/missing_colon.out
index 687b963bc8..687b963bc8 100644
--- a/modules/gdscript/tests/scripts/parser-errors/missing-colon.out
+++ b/modules/gdscript/tests/scripts/parser/errors/missing_colon.out
diff --git a/modules/gdscript/tests/scripts/parser-errors/missing-paren-after-args.gd b/modules/gdscript/tests/scripts/parser/errors/missing_paren_after_args.gd
index 116b0151da..116b0151da 100644
--- a/modules/gdscript/tests/scripts/parser-errors/missing-paren-after-args.gd
+++ b/modules/gdscript/tests/scripts/parser/errors/missing_paren_after_args.gd
diff --git a/modules/gdscript/tests/scripts/parser-errors/missing-paren-after-args.out b/modules/gdscript/tests/scripts/parser/errors/missing_paren_after_args.out
index 34ea7ac323..34ea7ac323 100644
--- a/modules/gdscript/tests/scripts/parser-errors/missing-paren-after-args.out
+++ b/modules/gdscript/tests/scripts/parser/errors/missing_paren_after_args.out
diff --git a/modules/gdscript/tests/scripts/parser-errors/mixing-tabs-spaces.gd b/modules/gdscript/tests/scripts/parser/errors/mixing_tabs_spaces.gd
index 9ad77f1432..9ad77f1432 100644
--- a/modules/gdscript/tests/scripts/parser-errors/mixing-tabs-spaces.gd
+++ b/modules/gdscript/tests/scripts/parser/errors/mixing_tabs_spaces.gd
diff --git a/modules/gdscript/tests/scripts/parser-errors/mixing-tabs-spaces.out b/modules/gdscript/tests/scripts/parser/errors/mixing_tabs_spaces.out
index 6390de9788..6390de9788 100644
--- a/modules/gdscript/tests/scripts/parser-errors/mixing-tabs-spaces.out
+++ b/modules/gdscript/tests/scripts/parser/errors/mixing_tabs_spaces.out
diff --git a/modules/gdscript/tests/scripts/parser-errors/nothing-after-dollar.gd b/modules/gdscript/tests/scripts/parser/errors/nothing_after_dollar.gd
index 3875ce3936..3875ce3936 100644
--- a/modules/gdscript/tests/scripts/parser-errors/nothing-after-dollar.gd
+++ b/modules/gdscript/tests/scripts/parser/errors/nothing_after_dollar.gd
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..b3dc181a22 100644
--- a/modules/gdscript/tests/scripts/parser-errors/nothing-after-dollar.out
+++ b/modules/gdscript/tests/scripts/parser/errors/nothing_after_dollar.out
diff --git a/modules/gdscript/tests/scripts/parser-errors/wrong-value-after-dollar.gd b/modules/gdscript/tests/scripts/parser/errors/wrong_value_after_dollar.gd
index 6fd2692d47..6fd2692d47 100644
--- a/modules/gdscript/tests/scripts/parser-errors/wrong-value-after-dollar.gd
+++ b/modules/gdscript/tests/scripts/parser/errors/wrong_value_after_dollar.gd
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..b3dc181a22 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
diff --git a/modules/gdscript/tests/scripts/parser-errors/wrong-value-after-dollar-slash.gd b/modules/gdscript/tests/scripts/parser/errors/wrong_value_after_dollar_slash.gd
index 1836d42226..1836d42226 100644
--- a/modules/gdscript/tests/scripts/parser-errors/wrong-value-after-dollar-slash.gd
+++ b/modules/gdscript/tests/scripts/parser/errors/wrong_value_after_dollar_slash.gd
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..dcb4ccecb0 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
diff --git a/modules/gdscript/tests/scripts/parser-features/semicolon-as-end-statement.gd b/modules/gdscript/tests/scripts/parser/features/semicolon_as_end_statement.gd
index 08f2eedb2d..08f2eedb2d 100644
--- a/modules/gdscript/tests/scripts/parser-features/semicolon-as-end-statement.gd
+++ b/modules/gdscript/tests/scripts/parser/features/semicolon_as_end_statement.gd
diff --git a/modules/gdscript/tests/scripts/parser-features/semicolon-as-end-statement.out b/modules/gdscript/tests/scripts/parser/features/semicolon_as_end_statement.out
index fc03f3efe8..fc03f3efe8 100644
--- a/modules/gdscript/tests/scripts/parser-features/semicolon-as-end-statement.out
+++ b/modules/gdscript/tests/scripts/parser/features/semicolon_as_end_statement.out
diff --git a/modules/gdscript/tests/scripts/parser-features/trailing-comma-in-function-args.gd b/modules/gdscript/tests/scripts/parser/features/trailing_comma_in_function_args.gd
index 6097b11b10..6097b11b10 100644
--- a/modules/gdscript/tests/scripts/parser-features/trailing-comma-in-function-args.gd
+++ b/modules/gdscript/tests/scripts/parser/features/trailing_comma_in_function_args.gd
diff --git a/modules/gdscript/tests/scripts/parser-features/trailing-comma-in-function-args.out b/modules/gdscript/tests/scripts/parser/features/trailing_comma_in_function_args.out
index 94e2ec2af8..94e2ec2af8 100644
--- a/modules/gdscript/tests/scripts/parser-features/trailing-comma-in-function-args.out
+++ b/modules/gdscript/tests/scripts/parser/features/trailing_comma_in_function_args.out
diff --git a/modules/gdscript/tests/scripts/parser-features/variable-declaration.gd b/modules/gdscript/tests/scripts/parser/features/variable_declaration.gd
index 3b48f10ca7..3b48f10ca7 100644
--- a/modules/gdscript/tests/scripts/parser-features/variable-declaration.gd
+++ b/modules/gdscript/tests/scripts/parser/features/variable_declaration.gd
diff --git a/modules/gdscript/tests/scripts/parser-features/variable-declaration.out b/modules/gdscript/tests/scripts/parser/features/variable_declaration.out
index 2e0a63c024..2e0a63c024 100644
--- a/modules/gdscript/tests/scripts/parser-features/variable-declaration.out
+++ b/modules/gdscript/tests/scripts/parser/features/variable_declaration.out
diff --git a/modules/gdscript/tests/scripts/parser-warnings/unused-variable.gd b/modules/gdscript/tests/scripts/parser/warnings/unused_variable.gd
index 68e3bd424f..68e3bd424f 100644
--- a/modules/gdscript/tests/scripts/parser-warnings/unused-variable.gd
+++ b/modules/gdscript/tests/scripts/parser/warnings/unused_variable.gd
diff --git a/modules/gdscript/tests/scripts/parser-warnings/unused-variable.out b/modules/gdscript/tests/scripts/parser/warnings/unused_variable.out
index 270e0e69c0..270e0e69c0 100644
--- a/modules/gdscript/tests/scripts/parser-warnings/unused-variable.out
+++ b/modules/gdscript/tests/scripts/parser/warnings/unused_variable.out
diff --git a/modules/gdscript/tests/scripts/runtime/errors/callable_call_after_free_object.gd b/modules/gdscript/tests/scripts/runtime/errors/callable_call_after_free_object.gd
new file mode 100644
index 0000000000..10780b5379
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/errors/callable_call_after_free_object.gd
@@ -0,0 +1,5 @@
+func test():
+ var node := Node.new()
+ var inside_tree = node.is_inside_tree
+ node.free()
+ inside_tree.call()
diff --git a/modules/gdscript/tests/scripts/runtime/errors/callable_call_after_free_object.out b/modules/gdscript/tests/scripts/runtime/errors/callable_call_after_free_object.out
new file mode 100644
index 0000000000..e585c374e2
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/errors/callable_call_after_free_object.out
@@ -0,0 +1,6 @@
+GDTEST_RUNTIME_ERROR
+>> SCRIPT ERROR
+>> on function: test()
+>> runtime/errors/callable_call_after_free_object.gd
+>> 5
+>> Attempt to call function 'null::is_inside_tree (Callable)' on a null instance.
diff --git a/modules/gdscript/tests/test_gdscript.cpp b/modules/gdscript/tests/test_gdscript.cpp
index e70f221c0a..52e9d92223 100644
--- a/modules/gdscript/tests/test_gdscript.cpp
+++ b/modules/gdscript/tests/test_gdscript.cpp
@@ -31,8 +31,8 @@
#include "test_gdscript.h"
#include "core/config/project_settings.h"
+#include "core/io/file_access.h"
#include "core/io/file_access_pack.h"
-#include "core/os/file_access.h"
#include "core/os/main_loop.h"
#include "core/os/os.h"
#include "core/string/string_builder.h"
@@ -66,7 +66,7 @@ static void test_tokenizer(const String &p_code, const Vector<String> &p_lines)
StringBuilder token;
token += " --> "; // Padding for line number.
- for (int l = current.start_line; l <= current.end_line; l++) {
+ for (int l = current.start_line; l <= current.end_line && l <= p_lines.size(); l++) {
print_line(vformat("%04d %s", l, p_lines[l - 1]).replace("\t", tab));
}
@@ -113,11 +113,21 @@ static void test_parser(const String &p_code, const String &p_script_path, const
if (err != OK) {
const List<GDScriptParser::ParserError> &errors = parser.get_errors();
- for (const List<GDScriptParser::ParserError>::Element *E = errors.front(); E != nullptr; E = E->next()) {
- const GDScriptParser::ParserError &error = E->get();
+ for (const GDScriptParser::ParserError &error : errors) {
print_line(vformat("%02d:%02d: %s", error.line, error.column, error.message));
}
}
+
+ GDScriptAnalyzer analyzer(&parser);
+ analyzer.analyze();
+
+ if (err != OK) {
+ const List<GDScriptParser::ParserError> &errors = parser.get_errors();
+ for (const GDScriptParser::ParserError &error : errors) {
+ print_line(vformat("%02d:%02d: %s", error.line, error.column, error.message));
+ }
+ }
+
#ifdef TOOLS_ENABLED
GDScriptParser::TreePrinter printer;
printer.print_tree(parser);
@@ -131,8 +141,7 @@ static void test_compiler(const String &p_code, const String &p_script_path, con
if (err != OK) {
print_line("Error in parser:");
const List<GDScriptParser::ParserError> &errors = parser.get_errors();
- for (const List<GDScriptParser::ParserError>::Element *E = errors.front(); E != nullptr; E = E->next()) {
- const GDScriptParser::ParserError &error = E->get();
+ for (const GDScriptParser::ParserError &error : errors) {
print_line(vformat("%02d:%02d: %s", error.line, error.column, error.message));
}
return;
@@ -144,8 +153,7 @@ static void test_compiler(const String &p_code, const String &p_script_path, con
if (err != OK) {
print_line("Error in analyzer:");
const List<GDScriptParser::ParserError> &errors = parser.get_errors();
- for (const List<GDScriptParser::ParserError>::Element *E = errors.front(); E != nullptr; E = E->next()) {
- const GDScriptParser::ParserError &error = E->get();
+ for (const GDScriptParser::ParserError &error : errors) {
print_line(vformat("%02d:%02d: %s", error.line, error.column, error.message));
}
return;
@@ -153,7 +161,7 @@ static void test_compiler(const String &p_code, const String &p_script_path, con
GDScriptCompiler compiler;
Ref<GDScript> script;
- script.instance();
+ script.instantiate();
script->set_path(p_script_path);
err = compiler.compile(&parser, script.ptr(), false);
@@ -203,8 +211,8 @@ void test(TestType p_type) {
init_language(fa->get_path_absolute().get_base_dir());
Vector<uint8_t> buf;
- int flen = fa->get_len();
- buf.resize(fa->get_len() + 1);
+ uint64_t flen = fa->get_length();
+ buf.resize(flen + 1);
fa->get_buffer(buf.ptrw(), flen);
buf.write[flen] = 0;