summaryrefslogtreecommitdiff
path: root/modules/gdscript
diff options
context:
space:
mode:
Diffstat (limited to 'modules/gdscript')
-rw-r--r--modules/gdscript/config.py1
-rw-r--r--modules/gdscript/doc_classes/@GDScript.xml1131
-rw-r--r--modules/gdscript/doc_classes/GDScriptFunctionState.xml45
-rw-r--r--modules/gdscript/editor/gdscript_highlighter.cpp18
-rw-r--r--modules/gdscript/editor/gdscript_highlighter.h6
-rw-r--r--modules/gdscript/editor/gdscript_translation_parser_plugin.cpp6
-rw-r--r--modules/gdscript/editor/gdscript_translation_parser_plugin.h8
-rw-r--r--modules/gdscript/gdscript.cpp331
-rw-r--r--modules/gdscript/gdscript.h59
-rw-r--r--modules/gdscript/gdscript_analyzer.cpp627
-rw-r--r--modules/gdscript/gdscript_analyzer.h12
-rw-r--r--modules/gdscript/gdscript_byte_codegen.cpp850
-rw-r--r--modules/gdscript/gdscript_byte_codegen.h240
-rw-r--r--modules/gdscript/gdscript_cache.cpp4
-rw-r--r--modules/gdscript/gdscript_cache.h4
-rw-r--r--modules/gdscript/gdscript_codegen.h24
-rw-r--r--modules/gdscript/gdscript_compiler.cpp410
-rw-r--r--modules/gdscript/gdscript_compiler.h8
-rw-r--r--modules/gdscript/gdscript_disassembler.cpp883
-rw-r--r--modules/gdscript/gdscript_editor.cpp139
-rw-r--r--modules/gdscript/gdscript_function.cpp2105
-rw-r--r--modules/gdscript/gdscript_function.h291
-rw-r--r--modules/gdscript/gdscript_functions.cpp1942
-rw-r--r--modules/gdscript/gdscript_parser.cpp520
-rw-r--r--modules/gdscript/gdscript_parser.h119
-rw-r--r--modules/gdscript/gdscript_tokenizer.cpp35
-rw-r--r--modules/gdscript/gdscript_tokenizer.h27
-rw-r--r--modules/gdscript/gdscript_utility_functions.cpp718
-rw-r--r--modules/gdscript/gdscript_utility_functions.h (renamed from modules/gdscript/gdscript_functions.h)127
-rw-r--r--modules/gdscript/gdscript_vm.cpp2998
-rw-r--r--modules/gdscript/gdscript_warning.cpp4
-rw-r--r--modules/gdscript/gdscript_warning.h4
-rw-r--r--modules/gdscript/language_server/gdscript_extend_parser.cpp22
-rw-r--r--modules/gdscript/language_server/gdscript_extend_parser.h4
-rw-r--r--modules/gdscript/language_server/gdscript_language_protocol.cpp15
-rw-r--r--modules/gdscript/language_server/gdscript_language_protocol.h4
-rw-r--r--modules/gdscript/language_server/gdscript_language_server.cpp19
-rw-r--r--modules/gdscript/language_server/gdscript_language_server.h14
-rw-r--r--modules/gdscript/language_server/gdscript_text_document.cpp12
-rw-r--r--modules/gdscript/language_server/gdscript_text_document.h4
-rw-r--r--modules/gdscript/language_server/gdscript_workspace.cpp29
-rw-r--r--modules/gdscript/language_server/gdscript_workspace.h4
-rw-r--r--modules/gdscript/language_server/lsp.hpp27
-rw-r--r--modules/gdscript/register_types.cpp12
-rw-r--r--modules/gdscript/register_types.h4
-rw-r--r--modules/gdscript/tests/test_gdscript.cpp14
-rw-r--r--modules/gdscript/tests/test_gdscript.h5
47 files changed, 7705 insertions, 6180 deletions
diff --git a/modules/gdscript/config.py b/modules/gdscript/config.py
index 6fc227e7f5..61ce6185a5 100644
--- a/modules/gdscript/config.py
+++ b/modules/gdscript/config.py
@@ -10,7 +10,6 @@ def get_doc_classes():
return [
"@GDScript",
"GDScript",
- "GDScriptFunctionState",
]
diff --git a/modules/gdscript/doc_classes/@GDScript.xml b/modules/gdscript/doc_classes/@GDScript.xml
index d90b3e52d0..9e974b6fdc 100644
--- a/modules/gdscript/doc_classes/@GDScript.xml
+++ b/modules/gdscript/doc_classes/@GDScript.xml
@@ -32,60 +32,6 @@
[/codeblock]
</description>
</method>
- <method name="ColorN">
- <return type="Color">
- </return>
- <argument index="0" name="name" type="String">
- </argument>
- <argument index="1" name="alpha" type="float" default="1.0">
- </argument>
- <description>
- Returns a color according to the standardized [code]name[/code] with [code]alpha[/code] ranging from 0 to 1.
- [codeblock]
- red = ColorN("red", 1)
- [/codeblock]
- Supported color names are the same as the constants defined in [Color].
- </description>
- </method>
- <method name="abs">
- <return type="float">
- </return>
- <argument index="0" name="s" type="float">
- </argument>
- <description>
- Returns the absolute value of parameter [code]s[/code] (i.e. positive value).
- [codeblock]
- # a is 1
- a = abs(-1)
- [/codeblock]
- </description>
- </method>
- <method name="acos">
- <return type="float">
- </return>
- <argument index="0" name="s" type="float">
- </argument>
- <description>
- Returns the arc cosine of [code]s[/code] in radians. Use to get the angle of cosine [code]s[/code].
- [codeblock]
- # c is 0.523599 or 30 degrees if converted with rad2deg(s)
- c = acos(0.866025)
- [/codeblock]
- </description>
- </method>
- <method name="asin">
- <return type="float">
- </return>
- <argument index="0" name="s" type="float">
- </argument>
- <description>
- Returns the arc sine of [code]s[/code] in radians. Use to get the angle of sine [code]s[/code].
- [codeblock]
- # s is 0.523599 or 30 degrees if converted with rad2deg(s)
- s = asin(0.5)
- [/codeblock]
- </description>
- </method>
<method name="assert">
<return type="void">
</return>
@@ -94,7 +40,7 @@
<argument index="1" name="message" type="String" default="&quot;&quot;">
</argument>
<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 push_error] for reporting errors to project developers or add-on users.
+ 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.
The optional [code]message[/code] argument, if given, is shown in addition to the generic "Assertion failed" message. You can use this to provide additional details about why the assertion failed.
[codeblock]
@@ -107,75 +53,10 @@
[/codeblock]
</description>
</method>
- <method name="atan">
- <return type="float">
- </return>
- <argument index="0" name="s" type="float">
- </argument>
- <description>
- Returns the arc tangent of [code]s[/code] in radians. Use it to get the angle from an angle's tangent in trigonometry: [code]atan(tan(angle)) == angle[/code].
- The method cannot know in which quadrant the angle should fall. See [method atan2] if you have both [code]y[/code] and [code]x[/code].
- [codeblock]
- a = atan(0.5) # a is 0.463648
- [/codeblock]
- </description>
- </method>
- <method name="atan2">
- <return type="float">
- </return>
- <argument index="0" name="y" type="float">
- </argument>
- <argument index="1" name="x" type="float">
- </argument>
- <description>
- Returns the arc tangent of [code]y/x[/code] in radians. Use to get the angle of tangent [code]y/x[/code]. To compute the value, the method takes into account the sign of both arguments in order to determine the quadrant.
- Important note: The Y coordinate comes first, by convention.
- [codeblock]
- a = atan2(0, -1) # a is 3.141593
- [/codeblock]
- </description>
- </method>
- <method name="bytes2var">
- <return type="Variant">
- </return>
- <argument index="0" name="bytes" type="PackedByteArray">
- </argument>
- <argument index="1" name="allow_objects" type="bool" default="false">
- </argument>
- <description>
- Decodes a byte array back to a value. When [code]allow_objects[/code] is [code]true[/code] decoding objects is allowed.
- [b]WARNING:[/b] Deserialized object can contain code which gets executed. Do not use this option if the serialized object comes from untrusted sources to avoid potential security threats (remote code execution).
- </description>
- </method>
- <method name="cartesian2polar">
- <return type="Vector2">
- </return>
- <argument index="0" name="x" type="float">
- </argument>
- <argument index="1" name="y" type="float">
- </argument>
- <description>
- Converts a 2D point expressed in the cartesian coordinate system (X and Y axis) to the polar coordinate system (a distance from the origin and an angle).
- </description>
- </method>
- <method name="ceil">
- <return type="float">
- </return>
- <argument index="0" name="s" type="float">
- </argument>
- <description>
- Rounds [code]s[/code] upward (towards positive infinity), returning the smallest whole number that is not less than [code]s[/code].
- [codeblock]
- i = ceil(1.45) # i is 2
- i = ceil(1.001) # i is 2
- [/codeblock]
- See also [method floor], [method round], and [method stepify].
- </description>
- </method>
<method name="char">
<return type="String">
</return>
- <argument index="0" name="code" type="int">
+ <argument index="0" name="char" type="int">
</argument>
<description>
Returns a character as a String of the given Unicode code point (which is compatible with ASCII code).
@@ -184,29 +65,6 @@
a = char(65 + 32) # a is "a"
a = char(8364) # a is "€"
[/codeblock]
- This is the inverse of [method ord].
- </description>
- </method>
- <method name="clamp">
- <return type="float">
- </return>
- <argument index="0" name="value" type="float">
- </argument>
- <argument index="1" name="min" type="float">
- </argument>
- <argument index="2" name="max" type="float">
- </argument>
- <description>
- Clamps [code]value[/code] and returns a value not less than [code]min[/code] and not more than [code]max[/code].
- [codeblock]
- speed = 1000
- # a is 20
- a = clamp(speed, 1, 20)
-
- speed = -10
- # a is 1
- a = clamp(speed, 1, 20)
- [/codeblock]
</description>
</method>
<method name="convert">
@@ -228,163 +86,13 @@
[/codeblock]
</description>
</method>
- <method name="cos">
- <return type="float">
- </return>
- <argument index="0" name="s" type="float">
- </argument>
- <description>
- Returns the cosine of angle [code]s[/code] in radians.
- [codeblock]
- # Prints 1 then -1
- print(cos(PI * 2))
- print(cos(PI))
- [/codeblock]
- </description>
- </method>
- <method name="cosh">
- <return type="float">
- </return>
- <argument index="0" name="s" type="float">
- </argument>
- <description>
- Returns the hyperbolic cosine of [code]s[/code] in radians.
- [codeblock]
- # Prints 1.543081
- print(cosh(1))
- [/codeblock]
- </description>
- </method>
- <method name="db2linear">
- <return type="float">
- </return>
- <argument index="0" name="db" type="float">
- </argument>
- <description>
- Converts from decibels to linear energy (audio).
- </description>
- </method>
- <method name="dectime">
- <return type="float">
- </return>
- <argument index="0" name="value" type="float">
- </argument>
- <argument index="1" name="amount" type="float">
- </argument>
- <argument index="2" name="step" type="float">
- </argument>
- <description>
- Returns the result of [code]value[/code] decreased by [code]step[/code] * [code]amount[/code].
- [codeblock]
- # a = 59
- a = dectime(60, 10, 0.1))
- [/codeblock]
- </description>
- </method>
- <method name="deg2rad">
- <return type="float">
- </return>
- <argument index="0" name="deg" type="float">
- </argument>
- <description>
- Converts an angle expressed in degrees to radians.
- [codeblock]
- # r is 3.141593
- r = deg2rad(180)
- [/codeblock]
- </description>
- </method>
<method name="dict2inst">
<return type="Object">
</return>
- <argument index="0" name="dict" type="Dictionary">
- </argument>
- <description>
- Converts a previously converted instance to a dictionary, back into an instance. Useful for deserializing.
- </description>
- </method>
- <method name="ease">
- <return type="float">
- </return>
- <argument index="0" name="s" type="float">
- </argument>
- <argument index="1" name="curve" type="float">
+ <argument index="0" name="dictionary" type="Dictionary">
</argument>
<description>
- Easing function, based on exponent. The curve values are: 0 is constant, 1 is linear, 0 to 1 is ease-in, 1+ is ease out. Negative values are in-out/out in.
- </description>
- </method>
- <method name="exp">
- <return type="float">
- </return>
- <argument index="0" name="s" type="float">
- </argument>
- <description>
- The natural exponential function. It raises the mathematical constant [b]e[/b] to the power of [code]s[/code] and returns it.
- [b]e[/b] has an approximate value of 2.71828, and can be obtained with [code]exp(1)[/code].
- For exponents to other bases use the method [method pow].
- [codeblock]
- a = exp(2) # Approximately 7.39
- [/codeblock]
- </description>
- </method>
- <method name="floor">
- <return type="float">
- </return>
- <argument index="0" name="s" type="float">
- </argument>
- <description>
- Rounds [code]s[/code] downward (towards negative infinity), returning the largest whole number that is not more than [code]s[/code].
- [codeblock]
- # a is 2.0
- a = floor(2.99)
- # a is -3.0
- a = floor(-2.99)
- [/codeblock]
- See also [method ceil], [method round], and [method stepify].
- [b]Note:[/b] This method returns a float. If you need an integer, you can use [code]int(s)[/code] directly.
- </description>
- </method>
- <method name="fmod">
- <return type="float">
- </return>
- <argument index="0" name="a" type="float">
- </argument>
- <argument index="1" name="b" type="float">
- </argument>
- <description>
- Returns the floating-point remainder of [code]a/b[/code], keeping the sign of [code]a[/code].
- [codeblock]
- # Remainder is 1.5
- var remainder = fmod(7, 5.5)
- [/codeblock]
- For the integer remainder operation, use the % operator.
- </description>
- </method>
- <method name="fposmod">
- <return type="float">
- </return>
- <argument index="0" name="a" type="float">
- </argument>
- <argument index="1" name="b" type="float">
- </argument>
- <description>
- Returns the floating-point modulus of [code]a/b[/code] that wraps equally in positive and negative.
- [codeblock]
- for i in 7:
- var x = 0.5 * i - 1.5
- print("%4.1f %4.1f %4.1f" % [x, fmod(x, 1.5), fposmod(x, 1.5)])
- [/codeblock]
- Produces:
- [codeblock]
- -1.5 -0.0 0.0
- -1.0 -1.0 0.5
- -0.5 -0.5 1.0
- 0.0 0.0 0.0
- 0.5 0.5 0.5
- 1.0 1.0 1.0
- 1.5 0.0 0.0
- [/codeblock]
+ Converts a dictionary (previously created with [method inst2dict]) back to an instance. Useful for deserializing.
</description>
</method>
<method name="get_stack">
@@ -408,22 +116,10 @@
[/codeblock]
</description>
</method>
- <method name="hash">
- <return type="int">
- </return>
- <argument index="0" name="var" type="Variant">
- </argument>
- <description>
- Returns the integer hash of the variable passed.
- [codeblock]
- print(hash("a")) # Prints 177670
- [/codeblock]
- </description>
- </method>
<method name="inst2dict">
<return type="Dictionary">
</return>
- <argument index="0" name="inst" type="Object">
+ <argument index="0" name="instance" type="Object">
</argument>
<description>
Returns the passed instance converted to a dictionary (useful for serializing).
@@ -441,92 +137,6 @@
[/codeblock]
</description>
</method>
- <method name="instance_from_id">
- <return type="Object">
- </return>
- <argument index="0" name="instance_id" type="int">
- </argument>
- <description>
- Returns the Object that corresponds to [code]instance_id[/code]. All Objects have a unique instance ID.
- [codeblock]
- var foo = "bar"
- func _ready():
- var id = get_instance_id()
- var inst = instance_from_id(id)
- print(inst.foo) # Prints bar
- [/codeblock]
- </description>
- </method>
- <method name="inverse_lerp">
- <return type="float">
- </return>
- <argument index="0" name="from" type="float">
- </argument>
- <argument index="1" name="to" type="float">
- </argument>
- <argument index="2" name="weight" type="float">
- </argument>
- <description>
- Returns a normalized value considering the given range. This is the opposite of [method lerp].
- [codeblock]
- var middle = lerp(20, 30, 0.75)
- # `middle` is now 27.5.
- # Now, we pretend to have forgotten the original ratio and want to get it back.
- var ratio = inverse_lerp(20, 30, 27.5)
- # `ratio` is now 0.75.
- [/codeblock]
- </description>
- </method>
- <method name="is_equal_approx">
- <return type="bool">
- </return>
- <argument index="0" name="a" type="float">
- </argument>
- <argument index="1" name="b" type="float">
- </argument>
- <description>
- Returns [code]true[/code] if [code]a[/code] and [code]b[/code] are approximately equal to each other.
- Here, approximately equal means that [code]a[/code] and [code]b[/code] are within a small internal epsilon of each other, which scales with the magnitude of the numbers.
- Infinity values of the same sign are considered equal.
- </description>
- </method>
- <method name="is_inf">
- <return type="bool">
- </return>
- <argument index="0" name="s" type="float">
- </argument>
- <description>
- Returns whether [code]s[/code] is an infinity value (either positive infinity or negative infinity).
- </description>
- </method>
- <method name="is_instance_valid">
- <return type="bool">
- </return>
- <argument index="0" name="instance" type="Object">
- </argument>
- <description>
- Returns whether [code]instance[/code] is a valid object (e.g. has not been deleted from memory).
- </description>
- </method>
- <method name="is_nan">
- <return type="bool">
- </return>
- <argument index="0" name="s" type="float">
- </argument>
- <description>
- Returns whether [code]s[/code] is a NaN ("Not a Number" or invalid) value.
- </description>
- </method>
- <method name="is_zero_approx">
- <return type="bool">
- </return>
- <argument index="0" name="s" type="float">
- </argument>
- <description>
- Returns [code]true[/code] if [code]s[/code] is zero or almost zero.
- This method is faster than using [method is_equal_approx] with one value as zero.
- </description>
- </method>
<method name="len">
<return type="int">
</return>
@@ -541,63 +151,6 @@
[/codeblock]
</description>
</method>
- <method name="lerp">
- <return type="Variant">
- </return>
- <argument index="0" name="from" type="Variant">
- </argument>
- <argument index="1" name="to" type="Variant">
- </argument>
- <argument index="2" name="weight" type="float">
- </argument>
- <description>
- Linearly interpolates between two values by a normalized value. This is the opposite of [method inverse_lerp].
- If the [code]from[/code] and [code]to[/code] arguments are of type [int] or [float], the return value is a [float].
- If both are of the same vector type ([Vector2], [Vector3] or [Color]), the return value will be of the same type ([code]lerp[/code] then calls the vector type's [code]lerp[/code] method).
- [codeblock]
- lerp(0, 4, 0.75) # Returns 3.0
- lerp(Vector2(1, 5), Vector2(3, 2), 0.5) # Returns Vector2(2, 3.5)
- [/codeblock]
- </description>
- </method>
- <method name="lerp_angle">
- <return type="float">
- </return>
- <argument index="0" name="from" type="float">
- </argument>
- <argument index="1" name="to" type="float">
- </argument>
- <argument index="2" name="weight" type="float">
- </argument>
- <description>
- Linearly interpolates between two angles (in radians) by a normalized value.
- Similar to [method lerp], but interpolates correctly when the angles wrap around [constant @GDScript.TAU].
- [codeblock]
- extends Sprite
- var elapsed = 0.0
- func _process(delta):
- var min_angle = deg2rad(0.0)
- var max_angle = deg2rad(90.0)
- rotation = lerp_angle(min_angle, max_angle, elapsed)
- elapsed += delta
- [/codeblock]
- </description>
- </method>
- <method name="linear2db">
- <return type="float">
- </return>
- <argument index="0" name="nrg" type="float">
- </argument>
- <description>
- Converts from linear energy to decibels (audio). This can be used to implement volume sliders that behave as expected (since volume isn't linear). Example:
- [codeblock]
- # "Slider" refers to a node that inherits Range such as HSlider or VSlider.
- # Its range must be configured to go from 0 to 1.
- # Change the bus name if you'd like to change the volume of a specific bus only.
- AudioServer.set_bus_volume_db(AudioServer.get_bus_index("Master"), linear2db($Slider.value))
- [/codeblock]
- </description>
- </method>
<method name="load">
<return type="Resource">
</return>
@@ -614,172 +167,6 @@
This method is a simplified version of [method ResourceLoader.load], which can be used for more advanced scenarios.
</description>
</method>
- <method name="log">
- <return type="float">
- </return>
- <argument index="0" name="s" type="float">
- </argument>
- <description>
- Natural logarithm. The amount of time needed to reach a certain level of continuous growth.
- [b]Note:[/b] This is not the same as the "log" function on most calculators, which uses a base 10 logarithm.
- [codeblock]
- log(10) # Returns 2.302585
- [/codeblock]
- [b]Note:[/b] The logarithm of [code]0[/code] returns [code]-inf[/code], while negative values return [code]-nan[/code].
- </description>
- </method>
- <method name="max">
- <return type="float">
- </return>
- <argument index="0" name="a" type="float">
- </argument>
- <argument index="1" name="b" type="float">
- </argument>
- <description>
- Returns the maximum of two values.
- [codeblock]
- max(1, 2) # Returns 2
- max(-3.99, -4) # Returns -3.99
- [/codeblock]
- </description>
- </method>
- <method name="min">
- <return type="float">
- </return>
- <argument index="0" name="a" type="float">
- </argument>
- <argument index="1" name="b" type="float">
- </argument>
- <description>
- Returns the minimum of two values.
- [codeblock]
- min(1, 2) # Returns 1
- min(-3.99, -4) # Returns -4
- [/codeblock]
- </description>
- </method>
- <method name="move_toward">
- <return type="float">
- </return>
- <argument index="0" name="from" type="float">
- </argument>
- <argument index="1" name="to" type="float">
- </argument>
- <argument index="2" name="delta" type="float">
- </argument>
- <description>
- Moves [code]from[/code] toward [code]to[/code] by the [code]delta[/code] value.
- Use a negative [code]delta[/code] value to move away.
- [codeblock]
- move_toward(5, 10, 4) # Returns 9
- move_toward(10, 5, 4) # Returns 6
- move_toward(10, 5, -1.5) # Returns 11.5
- [/codeblock]
- </description>
- </method>
- <method name="nearest_po2">
- <return type="int">
- </return>
- <argument index="0" name="value" type="int">
- </argument>
- <description>
- Returns the nearest equal or larger power of 2 for integer [code]value[/code].
- In other words, returns the smallest value [code]a[/code] where [code]a = pow(2, n)[/code] such that [code]value &lt;= a[/code] for some non-negative integer [code]n[/code].
- [codeblock]
- nearest_po2(3) # Returns 4
- nearest_po2(4) # Returns 4
- nearest_po2(5) # Returns 8
-
- nearest_po2(0) # Returns 0 (this may not be what you expect)
- nearest_po2(-1) # Returns 0 (this may not be what you expect)
- [/codeblock]
- [b]WARNING:[/b] Due to the way it is implemented, this function returns [code]0[/code] rather than [code]1[/code] for non-positive values of [code]value[/code] (in reality, 1 is the smallest integer power of 2).
- </description>
- </method>
- <method name="ord">
- <return type="int">
- </return>
- <argument index="0" name="char" type="String">
- </argument>
- <description>
- Returns an integer representing the Unicode code point of the given Unicode character [code]char[/code].
- [codeblock]
- a = ord("A") # a is 65
- a = ord("a") # a is 97
- a = ord("€") # a is 8364
- [/codeblock]
- This is the inverse of [method char].
- </description>
- </method>
- <method name="parse_json">
- <return type="Variant">
- </return>
- <argument index="0" name="json" type="String">
- </argument>
- <description>
- Parse JSON text to a Variant. (Use [method typeof] to check if the Variant's type is what you expect.)
- [b]Note:[/b] The JSON specification does not define integer or float types, but only a [i]number[/i] type. Therefore, parsing a JSON text will convert all numerical values to [float] types.
- [b]Note:[/b] JSON objects do not preserve key order like Godot dictionaries, thus, you should not rely on keys being in a certain order if a dictionary is constructed from JSON. In contrast, JSON arrays retain the order of their elements:
- [codeblock]
- var p = JSON.parse('["hello", "world", "!"]')
- if typeof(p.result) == TYPE_ARRAY:
- print(p.result[0]) # Prints "hello"
- else:
- push_error("Unexpected results.")
- [/codeblock]
- See also [JSON] for an alternative way to parse JSON text.
- </description>
- </method>
- <method name="polar2cartesian">
- <return type="Vector2">
- </return>
- <argument index="0" name="r" type="float">
- </argument>
- <argument index="1" name="th" type="float">
- </argument>
- <description>
- Converts a 2D point expressed in the polar coordinate system (a distance from the origin [code]r[/code] and an angle [code]th[/code]) to the cartesian coordinate system (X and Y axis).
- </description>
- </method>
- <method name="posmod">
- <return type="int">
- </return>
- <argument index="0" name="a" type="int">
- </argument>
- <argument index="1" name="b" type="int">
- </argument>
- <description>
- Returns the integer modulus of [code]a/b[/code] that wraps equally in positive and negative.
- [codeblock]
- for i in range(-3, 4):
- print("%2d %2d %2d" % [i, i % 3, posmod(i, 3)])
- [/codeblock]
- Produces:
- [codeblock]
- -3 0 0
- -2 -2 1
- -1 -1 2
- 0 0 0
- 1 1 1
- 2 2 2
- 3 0 0
- [/codeblock]
- </description>
- </method>
- <method name="pow">
- <return type="float">
- </return>
- <argument index="0" name="base" type="float">
- </argument>
- <argument index="1" name="exp" type="float">
- </argument>
- <description>
- Returns the result of [code]x[/code] raised to the power of [code]y[/code].
- [codeblock]
- pow(2, 5) # Returns 32
- [/codeblock]
- </description>
- </method>
<method name="preload">
<return type="Resource">
</return>
@@ -794,23 +181,11 @@
[/codeblock]
</description>
</method>
- <method name="print" qualifiers="vararg">
- <return type="void">
- </return>
- <description>
- Converts one or more arguments to strings in the best way possible and prints them to the console.
- [codeblock]
- a = [1, 2, 3]
- print("a", "b", a) # Prints ab[1, 2, 3]
- [/codeblock]
- [b]Note:[/b] Consider using [method push_error] and [method push_warning] to print error and warning messages instead of [method print]. This distinguishes them from print messages used for debugging purposes, while also displaying a stack trace when an error or warning is printed.
- </description>
- </method>
<method name="print_debug" qualifiers="vararg">
<return type="void">
</return>
<description>
- Like [method print], but prints only when used in debug mode.
+ Like [method @GlobalScope.print], but prints only when used in debug mode.
</description>
</method>
<method name="print_stack">
@@ -824,158 +199,6 @@
[/codeblock]
</description>
</method>
- <method name="printerr" qualifiers="vararg">
- <return type="void">
- </return>
- <description>
- Prints one or more arguments to strings in the best way possible to standard error line.
- [codeblock]
- printerr("prints to stderr")
- [/codeblock]
- </description>
- </method>
- <method name="printraw" qualifiers="vararg">
- <return type="void">
- </return>
- <description>
- Prints one or more arguments to strings in the best way possible to console. No newline is added at the end.
- [codeblock]
- printraw("A")
- printraw("B")
- # Prints AB
- [/codeblock]
- [b]Note:[/b] Due to limitations with Godot's built-in console, this only prints to the terminal. If you need to print in the editor, use another method, such as [method print].
- </description>
- </method>
- <method name="prints" qualifiers="vararg">
- <return type="void">
- </return>
- <description>
- Prints one or more arguments to the console with a space between each argument.
- [codeblock]
- prints("A", "B", "C") # Prints A B C
- [/codeblock]
- </description>
- </method>
- <method name="printt" qualifiers="vararg">
- <return type="void">
- </return>
- <description>
- Prints one or more arguments to the console with a tab between each argument.
- [codeblock]
- printt("A", "B", "C") # Prints A B C
- [/codeblock]
- </description>
- </method>
- <method name="push_error">
- <return type="void">
- </return>
- <argument index="0" name="message" type="String">
- </argument>
- <description>
- Pushes an error message to Godot's built-in debugger and to the OS terminal.
- [codeblock]
- push_error("test error") # Prints "test error" to debugger and terminal as error call
- [/codeblock]
- [b]Note:[/b] Errors printed this way will not pause project execution. To print an error message and pause project execution in debug builds, use [code]assert(false, "test error")[/code] instead.
- </description>
- </method>
- <method name="push_warning">
- <return type="void">
- </return>
- <argument index="0" name="message" type="String">
- </argument>
- <description>
- Pushes a warning message to Godot's built-in debugger and to the OS terminal.
- [codeblock]
- push_warning("test warning") # Prints "test warning" to debugger and terminal as warning call
- [/codeblock]
- </description>
- </method>
- <method name="rad2deg">
- <return type="float">
- </return>
- <argument index="0" name="rad" type="float">
- </argument>
- <description>
- Converts an angle expressed in radians to degrees.
- [codeblock]
- rad2deg(0.523599) # Returns 30
- [/codeblock]
- </description>
- </method>
- <method name="rand_seed">
- <return type="Array">
- </return>
- <argument index="0" name="seed" type="int">
- </argument>
- <description>
- Random from seed: pass a [code]seed[/code], and an array with both number and new seed is returned. "Seed" here refers to the internal state of the pseudo random number generator. The internal state of the current implementation is 64 bits.
- </description>
- </method>
- <method name="randf">
- <return type="float">
- </return>
- <description>
- Returns a random floating point value on the interval [code][0, 1][/code].
- [codeblock]
- randf() # Returns e.g. 0.375671
- [/codeblock]
- </description>
- </method>
- <method name="randf_range">
- <return type="float">
- </return>
- <argument index="0" name="from" type="float">
- </argument>
- <argument index="1" name="to" type="float">
- </argument>
- <description>
- Random range, any floating point value between [code]from[/code] and [code]to[/code].
- [codeblock]
- prints(randf_range(-10, 10), randf_range(-10, 10)) # Prints e.g. -3.844535 7.45315
- [/codeblock]
- </description>
- </method>
- <method name="randi">
- <return type="int">
- </return>
- <description>
- Returns a random unsigned 32 bit integer. Use remainder to obtain a random value in the interval [code][0, N - 1][/code] (where N is smaller than 2^32).
- [codeblock]
- randi() # Returns random integer between 0 and 2^32 - 1
- randi() % 20 # Returns random integer between 0 and 19
- randi() % 100 # Returns random integer between 0 and 99
- randi() % 100 + 1 # Returns random integer between 1 and 100
- [/codeblock]
- </description>
- </method>
- <method name="randi_range">
- <return type="int">
- </return>
- <argument index="0" name="from" type="int">
- </argument>
- <argument index="1" name="to" type="int">
- </argument>
- <description>
- Random range, any 32-bit integer value between [code]from[/code] and [code]to[/code] (inclusive). If [code]to[/code] is lesser than [code]from[/code] they are swapped.
- [codeblock]
- print(randi_range(0, 1)) # Prints 0 or 1
- print(randi_range(-10, 1000)) # Prints any number from -10 to 1000
- [/codeblock]
- </description>
- </method>
- <method name="randomize">
- <return type="void">
- </return>
- <description>
- Randomizes the seed (or the internal state) of the random number generator. Current implementation reseeds using a number based on time.
- [codeblock]
- func _ready():
- randomize()
- [/codeblock]
- </description>
- </method>
<method name="range" qualifiers="vararg">
<return type="Array">
</return>
@@ -994,158 +217,6 @@
[/codeblock]
</description>
</method>
- <method name="range_lerp">
- <return type="float">
- </return>
- <argument index="0" name="value" type="float">
- </argument>
- <argument index="1" name="istart" type="float">
- </argument>
- <argument index="2" name="istop" type="float">
- </argument>
- <argument index="3" name="ostart" type="float">
- </argument>
- <argument index="4" name="ostop" type="float">
- </argument>
- <description>
- Maps a [code]value[/code] from range [code][istart, istop][/code] to [code][ostart, ostop][/code].
- [codeblock]
- range_lerp(75, 0, 100, -1, 1) # Returns 0.5
- [/codeblock]
- </description>
- </method>
- <method name="round">
- <return type="float">
- </return>
- <argument index="0" name="s" type="float">
- </argument>
- <description>
- Rounds [code]s[/code] to the nearest whole number, with halfway cases rounded away from zero.
- [codeblock]
- round(2.6) # Returns 3
- [/codeblock]
- See also [method floor], [method ceil], and [method stepify].
- </description>
- </method>
- <method name="seed">
- <return type="void">
- </return>
- <argument index="0" name="seed" type="int">
- </argument>
- <description>
- Sets seed for the random number generator.
- [codeblock]
- my_seed = "Godot Rocks"
- seed(my_seed.hash())
- [/codeblock]
- </description>
- </method>
- <method name="sign">
- <return type="float">
- </return>
- <argument index="0" name="s" type="float">
- </argument>
- <description>
- Returns the sign of [code]s[/code]: -1 or 1. Returns 0 if [code]s[/code] is 0.
- [codeblock]
- sign(-6) # Returns -1
- sign(0) # Returns 0
- sign(6) # Returns 1
- [/codeblock]
- </description>
- </method>
- <method name="sin">
- <return type="float">
- </return>
- <argument index="0" name="s" type="float">
- </argument>
- <description>
- Returns the sine of angle [code]s[/code] in radians.
- [codeblock]
- sin(0.523599) # Returns 0.5
- [/codeblock]
- </description>
- </method>
- <method name="sinh">
- <return type="float">
- </return>
- <argument index="0" name="s" type="float">
- </argument>
- <description>
- Returns the hyperbolic sine of [code]s[/code].
- [codeblock]
- a = log(2.0) # Returns 0.693147
- sinh(a) # Returns 0.75
- [/codeblock]
- </description>
- </method>
- <method name="smoothstep">
- <return type="float">
- </return>
- <argument index="0" name="from" type="float">
- </argument>
- <argument index="1" name="to" type="float">
- </argument>
- <argument index="2" name="s" type="float">
- </argument>
- <description>
- Returns the result of smoothly interpolating the value of [code]s[/code] between [code]0[/code] and [code]1[/code], based on the where [code]s[/code] lies with respect to the edges [code]from[/code] and [code]to[/code].
- The return value is [code]0[/code] if [code]s &lt;= from[/code], and [code]1[/code] if [code]s &gt;= to[/code]. If [code]s[/code] lies between [code]from[/code] and [code]to[/code], the returned value follows an S-shaped curve that maps [code]s[/code] between [code]0[/code] and [code]1[/code].
- This S-shaped curve is the cubic Hermite interpolator, given by [code]f(s) = 3*s^2 - 2*s^3[/code].
- [codeblock]
- smoothstep(0, 2, -5.0) # Returns 0.0
- smoothstep(0, 2, 0.5) # Returns 0.15625
- smoothstep(0, 2, 1.0) # Returns 0.5
- smoothstep(0, 2, 2.0) # Returns 1.0
- [/codeblock]
- </description>
- </method>
- <method name="sqrt">
- <return type="float">
- </return>
- <argument index="0" name="s" type="float">
- </argument>
- <description>
- Returns the square root of [code]s[/code], where [code]s[/code] is a non-negative number.
- [codeblock]
- sqrt(9) # Returns 3
- [/codeblock]
- [b]Note:[/b]Negative values of [code]s[/code] return NaN. If you need negative inputs, use [code]System.Numerics.Complex[/code] in C#.
- </description>
- </method>
- <method name="step_decimals">
- <return type="int">
- </return>
- <argument index="0" name="step" type="float">
- </argument>
- <description>
- Returns the position of the first non-zero digit, after the decimal point. Note that the maximum return value is 10, which is a design decision in the implementation.
- [codeblock]
- # n is 0
- n = step_decimals(5)
- # n is 4
- n = step_decimals(1.0005)
- # n is 9
- n = step_decimals(0.000000005)
- [/codeblock]
- </description>
- </method>
- <method name="stepify">
- <return type="float">
- </return>
- <argument index="0" name="s" type="float">
- </argument>
- <argument index="1" name="step" type="float">
- </argument>
- <description>
- Snaps float value [code]s[/code] to a given [code]step[/code]. This can also be used to round a floating point number to an arbitrary number of decimals.
- [codeblock]
- stepify(100, 32) # Returns 96
- stepify(3.14159, 0.01) # Returns 3.14
- [/codeblock]
- See also [method ceil], [method floor], and [method round].
- </description>
- </method>
<method name="str" qualifiers="vararg">
<return type="String">
</return>
@@ -1159,200 +230,12 @@
[/codeblock]
</description>
</method>
- <method name="str2var">
- <return type="Variant">
- </return>
- <argument index="0" name="string" type="String">
- </argument>
- <description>
- Converts a formatted string that was returned by [method var2str] to the original value.
- [codeblock]
- a = '{ "a": 1, "b": 2 }'
- b = str2var(a)
- print(b["a"]) # Prints 1
- [/codeblock]
- </description>
- </method>
- <method name="tan">
- <return type="float">
- </return>
- <argument index="0" name="s" type="float">
- </argument>
- <description>
- Returns the tangent of angle [code]s[/code] in radians.
- [codeblock]
- tan(deg2rad(45)) # Returns 1
- [/codeblock]
- </description>
- </method>
- <method name="tanh">
- <return type="float">
- </return>
- <argument index="0" name="s" type="float">
- </argument>
- <description>
- Returns the hyperbolic tangent of [code]s[/code].
- [codeblock]
- a = log(2.0) # Returns 0.693147
- tanh(a) # Returns 0.6
- [/codeblock]
- </description>
- </method>
- <method name="to_json">
- <return type="String">
- </return>
- <argument index="0" name="var" type="Variant">
- </argument>
- <description>
- Converts a [Variant] [code]var[/code] to JSON text and return the result. Useful for serializing data to store or send over the network.
- [codeblock]
- # Both numbers below are integers.
- a = { "a": 1, "b": 2 }
- b = to_json(a)
- print(b) # {"a":1, "b":2}
- # Both numbers above are floats, even if they display without any decimal places.
- [/codeblock]
- [b]Note:[/b] The JSON specification does not define integer or float types, but only a [i]number[/i] type. Therefore, converting a [Variant] to JSON text will convert all numerical values to [float] types.
- See also [JSON] for an alternative way to convert a [Variant] to JSON text.
- </description>
- </method>
<method name="type_exists">
<return type="bool">
</return>
- <argument index="0" name="type" type="String">
- </argument>
- <description>
- Returns whether the given class exists in [ClassDB].
- [codeblock]
- type_exists("Sprite2D") # Returns true
- type_exists("Variant") # Returns false
- [/codeblock]
- </description>
- </method>
- <method name="typeof">
- <return type="int">
- </return>
- <argument index="0" name="what" type="Variant">
- </argument>
- <description>
- Returns the internal type of the given Variant object, using the [enum Variant.Type] values.
- [codeblock]
- p = parse_json('["a", "b", "c"]')
- if typeof(p) == TYPE_ARRAY:
- print(p[0]) # Prints a
- else:
- print("unexpected results")
- [/codeblock]
- </description>
- </method>
- <method name="validate_json">
- <return type="String">
- </return>
- <argument index="0" name="json" type="String">
- </argument>
- <description>
- Checks that [code]json[/code] is valid JSON data. Returns an empty string if valid, or an error message otherwise.
- [codeblock]
- j = to_json([1, 2, 3])
- v = validate_json(j)
- if not v:
- print("Valid JSON.")
- else:
- push_error("Invalid JSON: " + v)
- [/codeblock]
- </description>
- </method>
- <method name="var2bytes">
- <return type="PackedByteArray">
- </return>
- <argument index="0" name="var" type="Variant">
- </argument>
- <argument index="1" name="full_objects" type="bool" default="false">
- </argument>
- <description>
- Encodes a variable value to a byte array. When [code]full_objects[/code] is [code]true[/code] encoding objects is allowed (and can potentially include code).
- </description>
- </method>
- <method name="var2str">
- <return type="String">
- </return>
- <argument index="0" name="var" type="Variant">
- </argument>
- <description>
- Converts a Variant [code]var[/code] to a formatted string that can later be parsed using [method str2var].
- [codeblock]
- a = { "a": 1, "b": 2 }
- print(var2str(a))
- [/codeblock]
- prints
- [codeblock]
- {
- "a": 1,
- "b": 2
- }
- [/codeblock]
- </description>
- </method>
- <method name="weakref">
- <return type="WeakRef">
- </return>
- <argument index="0" name="obj" type="Object">
- </argument>
- <description>
- Returns a weak reference to an object.
- A weak reference to an object is not enough to keep the object alive: when the only remaining references to a referent are weak references, garbage collection is free to destroy the referent and reuse its memory for something else. However, until the object is actually destroyed the weak reference may return the object even if there are no strong references to it.
- </description>
- </method>
- <method name="wrapf">
- <return type="float">
- </return>
- <argument index="0" name="value" type="float">
- </argument>
- <argument index="1" name="min" type="float">
- </argument>
- <argument index="2" name="max" type="float">
+ <argument index="0" name="type" type="StringName">
</argument>
<description>
- Wraps float [code]value[/code] between [code]min[/code] and [code]max[/code].
- Usable for creating loop-alike behavior or infinite surfaces.
- [codeblock]
- # Infinite loop between 5.0 and 9.9
- value = wrapf(value + 0.1, 5.0, 10.0)
- [/codeblock]
- [codeblock]
- # Infinite rotation (in radians)
- angle = wrapf(angle + 0.1, 0.0, TAU)
- [/codeblock]
- [codeblock]
- # Infinite rotation (in radians)
- angle = wrapf(angle + 0.1, -PI, PI)
- [/codeblock]
- [b]Note:[/b] If [code]min[/code] is [code]0[/code], this is equivalent to [method fposmod], so prefer using that instead.
- [code]wrapf[/code] is more flexible than using the [method fposmod] approach by giving the user control over the minimum value.
- </description>
- </method>
- <method name="wrapi">
- <return type="int">
- </return>
- <argument index="0" name="value" type="int">
- </argument>
- <argument index="1" name="min" type="int">
- </argument>
- <argument index="2" name="max" type="int">
- </argument>
- <description>
- Wraps integer [code]value[/code] between [code]min[/code] and [code]max[/code].
- Usable for creating loop-alike behavior or infinite surfaces.
- [codeblock]
- # Infinite loop between 5 and 9
- frame = wrapi(frame + 1, 5, 10)
- [/codeblock]
- [codeblock]
- # result is -2
- var result = wrapi(-6, -5, -1)
- [/codeblock]
- [b]Note:[/b] If [code]min[/code] is [code]0[/code], this is equivalent to [method posmod], so prefer using that instead.
- [code]wrapi[/code] is more flexible than using the [method posmod] approach by giving the user control over the minimum value.
</description>
</method>
</methods>
diff --git a/modules/gdscript/doc_classes/GDScriptFunctionState.xml b/modules/gdscript/doc_classes/GDScriptFunctionState.xml
deleted file mode 100644
index 5e369b32d9..0000000000
--- a/modules/gdscript/doc_classes/GDScriptFunctionState.xml
+++ /dev/null
@@ -1,45 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<class name="GDScriptFunctionState" inherits="Reference" version="4.0">
- <brief_description>
- State of a function call after yielding.
- </brief_description>
- <description>
- FIXME: Outdated docs as of GDScript rewrite in 4.0.
- Calling [code]yield[/code] within a function will cause that function to yield and return its current state as an object of this type. The yielded function call can then be resumed later by calling [method resume] on this state object.
- </description>
- <tutorials>
- </tutorials>
- <methods>
- <method name="is_valid" qualifiers="const">
- <return type="bool">
- </return>
- <argument index="0" name="extended_check" type="bool" default="false">
- </argument>
- <description>
- Check whether the function call may be resumed. This is not the case if the function state was already resumed.
- If [code]extended_check[/code] is enabled, it also checks if the associated script and object still exist. The extended check is done in debug mode as part of [method GDScriptFunctionState.resume], but you can use this if you know you may be trying to resume without knowing for sure the object and/or script have survived up to that point.
- </description>
- </method>
- <method name="resume">
- <return type="Variant">
- </return>
- <argument index="0" name="arg" type="Variant" default="null">
- </argument>
- <description>
- Resume execution of the yielded function call.
- If handed an argument, return the argument from the [code]yield[/code] call in the yielded function call. You can pass e.g. an [Array] to hand multiple arguments.
- This function returns what the resumed function call returns, possibly another function state if yielded again.
- </description>
- </method>
- </methods>
- <signals>
- <signal name="completed">
- <argument index="0" name="result" type="Variant">
- </argument>
- <description>
- </description>
- </signal>
- </signals>
- <constants>
- </constants>
-</class>
diff --git a/modules/gdscript/editor/gdscript_highlighter.cpp b/modules/gdscript/editor/gdscript_highlighter.cpp
index 7f7410a92c..ccc942d86b 100644
--- a/modules/gdscript/editor/gdscript_highlighter.cpp
+++ b/modules/gdscript/editor/gdscript_highlighter.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -269,19 +269,21 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting(int p_line)
col = keywords[word];
} else if (member_keywords.has(word)) {
col = member_keywords[word];
+ }
+
+ if (col != Color()) {
for (int k = j - 1; k >= 0; k--) {
if (str[k] == '.') {
- col = Color(); //member indexing not allowed
+ col = Color(); // keyword & member indexing not allowed
break;
} else if (str[k] > 32) {
break;
}
}
- }
-
- if (col != Color()) {
- in_keyword = true;
- keyword_color = col;
+ if (col != Color()) {
+ in_keyword = true;
+ keyword_color = col;
+ }
}
}
diff --git a/modules/gdscript/editor/gdscript_highlighter.h b/modules/gdscript/editor/gdscript_highlighter.h
index 49357f3d2e..1b57cb1923 100644
--- a/modules/gdscript/editor/gdscript_highlighter.h
+++ b/modules/gdscript/editor/gdscript_highlighter.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -42,7 +42,7 @@ private:
Color color;
String start_key;
String end_key;
- bool line_only;
+ bool line_only = false;
};
Vector<ColorRegion> color_regions;
Map<int, int> color_region_cache;
diff --git a/modules/gdscript/editor/gdscript_translation_parser_plugin.cpp b/modules/gdscript/editor/gdscript_translation_parser_plugin.cpp
index 944ed859f5..9d0d91162c 100644
--- a/modules/gdscript/editor/gdscript_translation_parser_plugin.cpp
+++ b/modules/gdscript/editor/gdscript_translation_parser_plugin.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -44,7 +44,7 @@ Error GDScriptEditorTranslationParserPlugin::parse_file(const String &p_path, Ve
// Search strings in AssignmentNode -> text = "__", hint_tooltip = "__" etc.
Error err;
- RES loaded_res = ResourceLoader::load(p_path, "", false, &err);
+ RES loaded_res = ResourceLoader::load(p_path, "", ResourceFormatLoader::CACHE_MODE_REUSE, &err);
if (err) {
ERR_PRINT("Failed to load " + p_path);
return err;
diff --git a/modules/gdscript/editor/gdscript_translation_parser_plugin.h b/modules/gdscript/editor/gdscript_translation_parser_plugin.h
index d763df01f5..fcf438422a 100644
--- a/modules/gdscript/editor/gdscript_translation_parser_plugin.h
+++ b/modules/gdscript/editor/gdscript_translation_parser_plugin.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -39,8 +39,8 @@
class GDScriptEditorTranslationParserPlugin : public EditorTranslationParserPlugin {
GDCLASS(GDScriptEditorTranslationParserPlugin, EditorTranslationParserPlugin);
- Vector<String> *ids;
- Vector<Vector<String>> *ids_ctx_plural;
+ Vector<String> *ids = nullptr;
+ Vector<Vector<String>> *ids_ctx_plural = nullptr;
// List of patterns used for extracting translation strings.
StringName tr_func = "tr";
diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp
index 53602f7a9b..aea86cce54 100644
--- a/modules/gdscript/gdscript.cpp
+++ b/modules/gdscript/gdscript.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -219,7 +219,7 @@ StringName GDScript::get_instance_base_type() const {
}
struct _GDScriptMemberSort {
- int index;
+ int index = 0;
StringName name;
_FORCE_INLINE_ bool operator<(const _GDScriptMemberSort &p_member) const { return index < p_member.index; }
};
@@ -231,7 +231,7 @@ void GDScript::_placeholder_erased(PlaceHolderScriptInstance *p_placeholder) {
}
#endif
-void GDScript::get_script_method_list(List<MethodInfo> *p_list) const {
+void GDScript::_get_script_method_list(List<MethodInfo> *r_list, bool p_include_base) const {
const GDScript *current = this;
while (current) {
for (const Map<StringName, GDScriptFunction *>::Element *E = current->member_functions.front(); E; E = E->next()) {
@@ -239,18 +239,29 @@ void GDScript::get_script_method_list(List<MethodInfo> *p_list) const {
MethodInfo mi;
mi.name = E->key();
for (int i = 0; i < func->get_argument_count(); i++) {
- mi.arguments.push_back(func->get_argument_type(i));
+ PropertyInfo arginfo = func->get_argument_type(i);
+#ifdef TOOLS_ENABLED
+ arginfo.name = func->get_argument_name(i);
+#endif
+ mi.arguments.push_back(arginfo);
}
mi.return_val = func->get_return_type();
- p_list->push_back(mi);
+ r_list->push_back(mi);
+ }
+ if (!p_include_base) {
+ return;
}
current = current->_base;
}
}
-void GDScript::get_script_property_list(List<PropertyInfo> *p_list) const {
+void GDScript::get_script_method_list(List<MethodInfo> *r_list) const {
+ _get_script_method_list(r_list, true);
+}
+
+void GDScript::_get_script_property_list(List<PropertyInfo> *r_list, bool p_include_base) const {
const GDScript *sptr = this;
List<PropertyInfo> props;
@@ -269,15 +280,22 @@ void GDScript::get_script_property_list(List<PropertyInfo> *p_list) const {
for (int i = 0; i < msort.size(); i++) {
props.push_front(sptr->member_info[msort[i].name]);
}
+ if (!p_include_base) {
+ break;
+ }
sptr = sptr->_base;
}
for (List<PropertyInfo>::Element *E = props.front(); E; E = E->next()) {
- p_list->push_back(E->get());
+ r_list->push_back(E->get());
}
}
+void GDScript::get_script_property_list(List<PropertyInfo> *r_list) const {
+ _get_script_property_list(r_list, true);
+}
+
bool GDScript::has_method(const StringName &p_method) const {
return member_functions.has(p_method);
}
@@ -383,6 +401,183 @@ void GDScript::_update_exports_values(Map<StringName, Variant> &values, List<Pro
propnames.push_back(E->get());
}
}
+
+void GDScript::_add_doc(const DocData::ClassDoc &p_inner_class) {
+ if (_owner) {
+ _owner->_add_doc(p_inner_class);
+ } else {
+ for (int i = 0; i < docs.size(); i++) {
+ if (docs[i].name == p_inner_class.name) {
+ docs.remove(i);
+ break;
+ }
+ }
+ docs.append(p_inner_class);
+ }
+}
+
+void GDScript::_clear_doc() {
+ docs.clear();
+ doc = DocData::ClassDoc();
+}
+
+void GDScript::_update_doc() {
+ _clear_doc();
+
+ doc.script_path = "\"" + get_path().get_slice("://", 1) + "\"";
+ if (!name.is_empty()) {
+ doc.name = name;
+ } else {
+ doc.name = doc.script_path;
+ }
+
+ if (_owner) {
+ doc.name = _owner->doc.name + "." + doc.name;
+ doc.script_path = doc.script_path + "." + doc.name;
+ }
+
+ doc.is_script_doc = true;
+
+ if (base.is_valid() && base->is_valid()) {
+ if (base->doc.name != String()) {
+ doc.inherits = base->doc.name;
+ } else {
+ doc.inherits = base->get_instance_base_type();
+ }
+ } else if (native.is_valid()) {
+ doc.inherits = native->get_name();
+ }
+
+ doc.brief_description = doc_brief_description;
+ doc.description = doc_description;
+ doc.tutorials = doc_tutorials;
+
+ for (Map<String, DocData::EnumDoc>::Element *E = doc_enums.front(); E; E = E->next()) {
+ if (E->value().description != "") {
+ doc.enums[E->key()] = E->value().description;
+ }
+ }
+
+ List<MethodInfo> methods;
+ _get_script_method_list(&methods, false);
+ for (int i = 0; i < methods.size(); i++) {
+ // Ignore internal methods.
+ if (methods[i].name[0] == '@') {
+ continue;
+ }
+
+ DocData::MethodDoc method_doc;
+ const String &class_name = methods[i].name;
+ if (member_functions.has(class_name)) {
+ GDScriptFunction *fn = member_functions[class_name];
+
+ // Change class name if return type is script reference.
+ GDScriptDataType return_type = fn->get_return_type();
+ if (return_type.kind == GDScriptDataType::GDSCRIPT) {
+ 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.
+ for (int j = 0; j < fn->get_argument_count(); j++) {
+ GDScriptDataType arg_type = fn->get_argument_type(j);
+ if (arg_type.kind == GDScriptDataType::GDSCRIPT) {
+ methods[i].arguments[j].class_name = _get_gdscript_reference_class_name(Object::cast_to<GDScript>(arg_type.script_type));
+ }
+ }
+ }
+ if (doc_functions.has(methods[i].name)) {
+ DocData::method_doc_from_methodinfo(method_doc, methods[i], doc_functions[methods[i].name]);
+ } else {
+ DocData::method_doc_from_methodinfo(method_doc, methods[i], String());
+ }
+ doc.methods.push_back(method_doc);
+ }
+
+ List<PropertyInfo> props;
+ _get_script_property_list(&props, false);
+ for (int i = 0; i < props.size(); i++) {
+ ScriptMemberInfo scr_member_info;
+ scr_member_info.propinfo = props[i];
+ scr_member_info.propinfo.usage |= PROPERTY_USAGE_NIL_IS_VARIANT;
+ if (member_indices.has(props[i].name)) {
+ const MemberInfo &mi = member_indices[props[i].name];
+ scr_member_info.setter = mi.setter;
+ scr_member_info.getter = mi.getter;
+ if (mi.data_type.kind == GDScriptDataType::GDSCRIPT) {
+ scr_member_info.propinfo.class_name = _get_gdscript_reference_class_name(
+ Object::cast_to<GDScript>(mi.data_type.script_type));
+ }
+ }
+ if (member_default_values.has(props[i].name)) {
+ scr_member_info.has_default_value = true;
+ scr_member_info.default_value = member_default_values[props[i].name];
+ }
+ if (doc_variables.has(props[i].name)) {
+ scr_member_info.doc_string = doc_variables[props[i].name];
+ }
+
+ DocData::PropertyDoc prop_doc;
+ DocData::property_doc_from_scriptmemberinfo(prop_doc, scr_member_info);
+ doc.properties.push_back(prop_doc);
+ }
+
+ List<MethodInfo> signals;
+ _get_script_signal_list(&signals, false);
+ for (int i = 0; i < signals.size(); i++) {
+ DocData::MethodDoc signal_doc;
+ if (doc_signals.has(signals[i].name)) {
+ DocData::signal_doc_from_methodinfo(signal_doc, signals[i], signals[i].name);
+ } else {
+ DocData::signal_doc_from_methodinfo(signal_doc, signals[i], String());
+ }
+ doc.signals.push_back(signal_doc);
+ }
+
+ for (Map<StringName, Variant>::Element *E = constants.front(); E; E = E->next()) {
+ if (subclasses.has(E->key())) {
+ continue;
+ }
+
+ // Enums.
+ bool is_enum = false;
+ if (E->value().get_type() == Variant::DICTIONARY) {
+ if (doc_enums.has(E->key())) {
+ is_enum = true;
+ for (int i = 0; i < doc_enums[E->key()].values.size(); i++) {
+ doc_enums[E->key()].values.write[i].enumeration = E->key();
+ doc.constants.push_back(doc_enums[E->key()].values[i]);
+ }
+ }
+ }
+ if (!is_enum && doc_enums.has("@unnamed_enums")) {
+ for (int i = 0; i < doc_enums["@unnamed_enums"].values.size(); i++) {
+ if (E->key() == doc_enums["@unnamed_enums"].values[i].name) {
+ is_enum = true;
+ DocData::ConstantDoc constant_doc;
+ constant_doc.enumeration = "@unnamed_enums";
+ DocData::constant_doc_from_variant(constant_doc, E->key(), E->value(), doc_enums["@unnamed_enums"].values[i].description);
+ doc.constants.push_back(constant_doc);
+ break;
+ }
+ }
+ }
+ if (!is_enum) {
+ DocData::ConstantDoc constant_doc;
+ String doc_description;
+ if (doc_constants.has(E->key())) {
+ doc_description = doc_constants[E->key()];
+ }
+ DocData::constant_doc_from_variant(constant_doc, E->key(), E->value(), doc_description);
+ doc.constants.push_back(constant_doc);
+ }
+ }
+
+ for (Map<StringName, Ref<GDScript>>::Element *E = subclasses.front(); E; E = E->next()) {
+ E->get()->_update_doc();
+ }
+
+ _add_doc(doc);
+}
#endif
bool GDScript::_update_exports(bool *r_err, bool p_recursive_call) {
@@ -598,10 +793,10 @@ Error GDScript::reload(bool p_keep_state) {
{
String source_path = path;
- if (source_path.empty()) {
+ if (source_path.is_empty()) {
source_path = get_path();
}
- if (!source_path.empty()) {
+ if (!source_path.is_empty()) {
MutexLock lock(GDScriptCache::singleton->lock);
if (!GDScriptCache::singleton->shallow_gdscript_cache.has(source_path)) {
GDScriptCache::singleton->shallow_gdscript_cache[source_path] = this;
@@ -617,7 +812,7 @@ Error GDScript::reload(bool p_keep_state) {
GDScriptLanguage::get_singleton()->debug_break_parse(get_path(), parser.get_errors().front()->get().line, "Parser Error: " + parser.get_errors().front()->get().message);
}
// TODO: Show all error messages.
- _err_print_error("GDScript::reload", path.empty() ? "built-in" : (const char *)path.utf8().get_data(), parser.get_errors().front()->get().line, ("Parse Error: " + parser.get_errors().front()->get().message).utf8().get_data(), ERR_HANDLER_SCRIPT);
+ _err_print_error("GDScript::reload", path.is_empty() ? "built-in" : (const char *)path.utf8().get_data(), parser.get_errors().front()->get().line, ("Parse Error: " + parser.get_errors().front()->get().message).utf8().get_data(), ERR_HANDLER_SCRIPT);
ERR_FAIL_V(ERR_PARSE_ERROR);
}
@@ -628,8 +823,12 @@ Error GDScript::reload(bool p_keep_state) {
if (EngineDebugger::is_active()) {
GDScriptLanguage::get_singleton()->debug_break_parse(get_path(), parser.get_errors().front()->get().line, "Parser Error: " + parser.get_errors().front()->get().message);
}
- // TODO: Show all error messages.
- _err_print_error("GDScript::reload", path.empty() ? "built-in" : (const char *)path.utf8().get_data(), parser.get_errors().front()->get().line, ("Parse Error: " + parser.get_errors().front()->get().message).utf8().get_data(), ERR_HANDLER_SCRIPT);
+
+ const List<GDScriptParser::ParserError>::Element *e = parser.get_errors().front();
+ while (e != nullptr) {
+ _err_print_error("GDScript::reload", path.is_empty() ? "built-in" : (const char *)path.utf8().get_data(), e->get().line, ("Parse Error: " + e->get().message).utf8().get_data(), ERR_HANDLER_SCRIPT);
+ e = e->next();
+ }
ERR_FAIL_V(ERR_PARSE_ERROR);
}
@@ -638,12 +837,16 @@ Error GDScript::reload(bool p_keep_state) {
GDScriptCompiler compiler;
err = compiler.compile(&parser, this, p_keep_state);
+#ifdef TOOLS_ENABLED
+ _update_doc();
+#endif
+
if (err) {
if (can_run) {
if (EngineDebugger::is_active()) {
GDScriptLanguage::get_singleton()->debug_break_parse(get_path(), compiler.get_error_line(), "Parser Error: " + compiler.get_error());
}
- _err_print_error("GDScript::reload", path.empty() ? "built-in" : (const char *)path.utf8().get_data(), compiler.get_error_line(), ("Compile Error: " + compiler.get_error()).utf8().get_data(), ERR_HANDLER_SCRIPT);
+ _err_print_error("GDScript::reload", path.is_empty() ? "built-in" : (const char *)path.utf8().get_data(), compiler.get_error_line(), ("Compile Error: " + compiler.get_error()).utf8().get_data(), ERR_HANDLER_SCRIPT);
ERR_FAIL_V(ERR_COMPILATION_FAILED);
} else {
return err;
@@ -911,7 +1114,7 @@ bool GDScript::has_script_signal(const StringName &p_signal) const {
return false;
}
-void GDScript::get_script_signal_list(List<MethodInfo> *r_signals) const {
+void GDScript::_get_script_signal_list(List<MethodInfo> *r_list, bool p_include_base) const {
for (const Map<StringName, Vector<StringName>>::Element *E = _signals.front(); E; E = E->next()) {
MethodInfo mi;
mi.name = E->key();
@@ -920,33 +1123,45 @@ void GDScript::get_script_signal_list(List<MethodInfo> *r_signals) const {
arg.name = E->get()[i];
mi.arguments.push_back(arg);
}
- r_signals->push_back(mi);
+ r_list->push_back(mi);
+ }
+
+ if (!p_include_base) {
+ return;
}
if (base.is_valid()) {
- base->get_script_signal_list(r_signals);
+ base->get_script_signal_list(r_list);
}
#ifdef TOOLS_ENABLED
else if (base_cache.is_valid()) {
- base_cache->get_script_signal_list(r_signals);
+ base_cache->get_script_signal_list(r_list);
}
#endif
}
+void GDScript::get_script_signal_list(List<MethodInfo> *r_signals) const {
+ _get_script_signal_list(r_signals, true);
+}
+
+String GDScript::_get_gdscript_reference_class_name(const GDScript *p_gdscript) {
+ ERR_FAIL_NULL_V(p_gdscript, String());
+
+ String class_name;
+ while (p_gdscript) {
+ if (class_name == "") {
+ class_name = p_gdscript->get_script_class_name();
+ } else {
+ class_name = p_gdscript->get_script_class_name() + "." + class_name;
+ }
+ p_gdscript = p_gdscript->_owner;
+ }
+ return class_name;
+}
+
GDScript::GDScript() :
script_list(this) {
- valid = false;
- subclass_count = 0;
- initializer = nullptr;
- _base = nullptr;
- _owner = nullptr;
- tool = false;
-#ifdef TOOLS_ENABLED
- source_changed_cache = false;
- placeholder_fallback_enabled = false;
-#endif
-
#ifdef DEBUG_ENABLED
{
MutexLock lock(GDScriptLanguage::get_singleton()->lock);
@@ -1061,6 +1276,13 @@ GDScript::~GDScript() {
_save_orphaned_subclasses();
+#ifdef TOOLS_ENABLED
+ // Clearing inner class doc, script doc only cleared when the script source deleted.
+ if (_owner) {
+ _clear_doc();
+ }
+#endif
+
#ifdef DEBUG_ENABLED
{
MutexLock lock(GDScriptLanguage::get_singleton()->lock);
@@ -1088,21 +1310,29 @@ bool GDScriptInstance::set(const StringName &p_name, const Variant &p_value) {
return true; //function exists, call was successful
}
} else {
- if (!member->data_type.is_type(p_value)) {
- // Try conversion
- Callable::CallError ce;
- const Variant *value = &p_value;
- Variant converted;
- Variant::construct(member->data_type.builtin_type, converted, &value, 1, ce);
- if (ce.error == Callable::CallError::CALL_OK) {
- members.write[member->index] = converted;
- return true;
- } else {
- return false;
+ if (member->data_type.has_type) {
+ if (member->data_type.builtin_type == Variant::ARRAY && member->data_type.has_container_element_type()) {
+ // Typed array.
+ if (p_value.get_type() == Variant::ARRAY) {
+ return VariantInternal::get_array(&members.write[member->index])->typed_assign(p_value);
+ } else {
+ return false;
+ }
+ } else if (!member->data_type.is_type(p_value)) {
+ // Try conversion
+ Callable::CallError ce;
+ const Variant *value = &p_value;
+ Variant converted;
+ Variant::construct(member->data_type.builtin_type, converted, &value, 1, ce);
+ if (ce.error == Callable::CallError::CALL_OK) {
+ members.write[member->index] = converted;
+ return true;
+ } else {
+ return false;
+ }
}
- } else {
- members.write[member->index] = p_value;
}
+ members.write[member->index] = p_value;
}
return true;
}
@@ -1889,8 +2119,11 @@ void GDScriptLanguage::get_reserved_words(List<String> *p_words) const {
w++;
}
- for (int i = 0; i < GDScriptFunctions::FUNC_MAX; i++) {
- p_words->push_back(GDScriptFunctions::get_func_name(GDScriptFunctions::Function(i)));
+ 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()));
}
}
@@ -1915,7 +2148,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.empty() || c->icon_path.is_abs_path()) {
+ if (c->icon_path.is_empty() || c->icon_path.is_abs_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();
@@ -1927,7 +2160,7 @@ String GDScriptLanguage::get_global_class_name(const String &p_path, String *r_b
GDScriptParser subparser;
while (subclass) {
if (subclass->extends_used) {
- if (!subclass->extends_path.empty()) {
+ if (!subclass->extends_path.is_empty()) {
if (subclass->extends.size() == 0) {
get_global_class_name(subclass->extends_path, r_base_type);
subclass = nullptr;
@@ -1941,7 +2174,7 @@ String GDScriptLanguage::get_global_class_name(const String &p_path, String *r_b
}
String subsource = subfile->get_as_utf8_string();
- if (subsource.empty()) {
+ if (subsource.is_empty()) {
break;
}
String subpath = subclass->extends_path;
@@ -2091,7 +2324,7 @@ Ref<GDScript> GDScriptLanguage::get_orphan_subclass(const String &p_qualified_na
/*************** RESOURCE ***************/
-RES ResourceFormatLoaderGDScript::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, bool p_no_cache) {
+RES ResourceFormatLoaderGDScript::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, CacheMode p_cache_mode) {
if (r_error) {
*r_error = ERR_FILE_CANT_OPEN;
}
@@ -2138,7 +2371,7 @@ void ResourceFormatLoaderGDScript::get_dependencies(const String &p_path, List<S
ERR_FAIL_COND_MSG(!file, "Cannot open file '" + p_path + "'.");
String source = file->get_as_utf8_string();
- if (source.empty()) {
+ if (source.is_empty()) {
return;
}
diff --git a/modules/gdscript/gdscript.h b/modules/gdscript/gdscript.h
index b69a6e39c0..12c909fd4f 100644
--- a/modules/gdscript/gdscript.h
+++ b/modules/gdscript/gdscript.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -33,6 +33,7 @@
#include "core/debugger/engine_debugger.h"
#include "core/debugger/script_debugger.h"
+#include "core/doc_data.h"
#include "core/io/resource_loader.h"
#include "core/io/resource_saver.h"
#include "core/object/script_language.h"
@@ -56,11 +57,11 @@ public:
class GDScript : public Script {
GDCLASS(GDScript, Script);
- bool tool;
- bool valid;
+ bool tool = false;
+ bool valid = false;
struct MemberInfo {
- int index;
+ int index = 0;
StringName setter;
StringName getter;
MultiplayerAPI::RPCMode rpc_mode;
@@ -71,13 +72,13 @@ class GDScript : public Script {
friend class GDScriptFunction;
friend class GDScriptAnalyzer;
friend class GDScriptCompiler;
- friend class GDScriptFunctions;
friend class GDScriptLanguage;
+ friend struct GDScriptUtilityFunctionsDefinitions;
Ref<GDScriptNativeClass> native;
Ref<GDScript> base;
- GDScript *_base; //fast pointer access
- GDScript *_owner; //for subclasses
+ GDScript *_base = nullptr; //fast pointer access
+ GDScript *_owner = nullptr; //for subclasses
Set<StringName> members; //members are just indices to the instanced script.
Map<StringName, Variant> constants;
@@ -91,24 +92,36 @@ class GDScript : public Script {
#ifdef TOOLS_ENABLED
Map<StringName, int> member_lines;
-
Map<StringName, Variant> member_default_values;
-
List<PropertyInfo> members_cache;
Map<StringName, Variant> member_default_values_cache;
Ref<GDScript> base_cache;
Set<ObjectID> inheriters_cache;
- bool source_changed_cache;
- bool placeholder_fallback_enabled;
+ bool source_changed_cache = false;
+ bool placeholder_fallback_enabled = false;
void _update_exports_values(Map<StringName, Variant> &values, List<PropertyInfo> &propnames);
+ DocData::ClassDoc doc;
+ Vector<DocData::ClassDoc> docs;
+ String doc_brief_description;
+ String doc_description;
+ Vector<DocData::TutorialDoc> doc_tutorials;
+ Map<String, String> doc_functions;
+ Map<String, String> doc_variables;
+ Map<String, String> doc_constants;
+ Map<String, String> doc_signals;
+ Map<String, DocData::EnumDoc> doc_enums;
+ void _clear_doc();
+ void _update_doc();
+ void _add_doc(const DocData::ClassDoc &p_inner_class);
+
#endif
Map<StringName, PropertyInfo> member_info;
GDScriptFunction *implicit_initializer = nullptr;
GDScriptFunction *initializer = nullptr; //direct pointer to new , faster to locate
- int subclass_count;
+ int subclass_count = 0;
Set<Object *> instances;
//exported members
String source;
@@ -141,6 +154,13 @@ class GDScript : public Script {
void _save_orphaned_subclasses();
void _init_rpc_methods_properties();
+ void _get_script_property_list(List<PropertyInfo> *r_list, bool p_include_base) const;
+ 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".
+ static String _get_gdscript_reference_class_name(const GDScript *p_gdscript);
+
protected:
bool _get(const StringName &p_name, Variant &r_ret) const;
bool _set(const StringName &p_name, const Variant &p_value);
@@ -191,6 +211,12 @@ public:
virtual void set_source_code(const String &p_code) override;
virtual void update_exports() override;
+#ifdef TOOLS_ENABLED
+ virtual const Vector<DocData::ClassDoc> &get_documentation() const override {
+ return docs;
+ }
+#endif // TOOLS_ENABLED
+
virtual Error reload(bool p_keep_state = false) override;
void set_script_path(const String &p_path) { path = p_path; } //because subclasses need a path too...
@@ -244,8 +270,8 @@ public:
class GDScriptInstance : public ScriptInstance {
friend class GDScript;
friend class GDScriptFunction;
- friend class GDScriptFunctions;
friend class GDScriptCompiler;
+ friend struct GDScriptUtilityFunctionsDefinitions;
ObjectID owner_id;
Object *owner;
@@ -444,7 +470,8 @@ public:
virtual Script *create_script() const;
virtual bool has_named_classes() const;
virtual bool supports_builtin_mode() const;
- virtual bool can_inherit_from_file() { return true; }
+ virtual bool supports_documentation() const;
+ virtual bool can_inherit_from_file() const { return true; }
virtual int find_function(const String &p_function, const String &p_code) const;
virtual String make_function(const String &p_class, const String &p_name, const PackedStringArray &p_args) const;
virtual Error complete_code(const String &p_code, const String &p_path, Object *p_owner, List<ScriptCodeCompletionOption> *r_options, bool &r_forced, String &r_call_hint);
@@ -502,7 +529,7 @@ public:
class ResourceFormatLoaderGDScript : public ResourceFormatLoader {
public:
- virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, bool p_no_cache = false);
+ virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, CacheMode p_cache_mode = CACHE_MODE_REUSE);
virtual void get_recognized_extensions(List<String> *p_extensions) const;
virtual bool handles_type(const String &p_type) const;
virtual String get_resource_type(const String &p_path) const;
diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp
index 6b23ab1616..bdca64c146 100644
--- a/modules/gdscript/gdscript_analyzer.cpp
+++ b/modules/gdscript/gdscript_analyzer.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -37,43 +37,39 @@
#include "core/os/file_access.h"
#include "core/templates/hash_map.h"
#include "gdscript.h"
+#include "gdscript_utility_functions.h"
-// TODO: Move this to a central location (maybe core?).
-static HashMap<StringName, StringName> underscore_map;
-static const char *underscore_classes[] = {
- "ClassDB",
- "Directory",
- "Engine",
- "File",
- "Geometry",
- "GodotSharp",
- "JSON",
- "Marshalls",
- "Mutex",
- "OS",
- "ResourceLoader",
- "ResourceSaver",
- "Semaphore",
- "Thread",
- "VisualScriptEditor",
- nullptr,
-};
-static StringName get_real_class_name(const StringName &p_source) {
- if (underscore_map.empty()) {
- const char **class_name = underscore_classes;
- while (*class_name != nullptr) {
- underscore_map[*class_name] = String("_") + *class_name;
- class_name++;
- }
- }
- if (underscore_map.has(p_source)) {
- return underscore_map[p_source];
- }
- return p_source;
-}
-
-void GDScriptAnalyzer::cleanup() {
- underscore_map.clear();
+static MethodInfo info_from_utility_func(const StringName &p_function) {
+ ERR_FAIL_COND_V(!Variant::has_utility_function(p_function), MethodInfo());
+
+ MethodInfo info(p_function);
+
+ if (Variant::has_utility_function_return_value(p_function)) {
+ info.return_val.type = Variant::get_utility_function_return_type(p_function);
+ if (info.return_val.type == Variant::NIL) {
+ info.return_val.usage |= PROPERTY_USAGE_NIL_IS_VARIANT;
+ }
+ }
+
+ if (Variant::is_utility_function_vararg(p_function)) {
+ info.flags |= METHOD_FLAG_VARARG;
+ } else {
+ for (int i = 0; i < Variant::get_utility_function_argument_count(p_function); i++) {
+ PropertyInfo pi;
+#ifdef DEBUG_METHODS_ENABLED
+ pi.name = Variant::get_utility_function_argument_name(p_function, i);
+#else
+ pi.name = "arg" + itos(i + 1);
+#endif
+ pi.type = Variant::get_utility_function_argument_type(p_function, i);
+ if (pi.type == Variant::NIL) {
+ pi.usage |= PROPERTY_USAGE_NIL_IS_VARIANT;
+ }
+ info.arguments.push_back(pi);
+ }
+ }
+
+ return info;
}
static GDScriptParser::DataType make_callable_type(const MethodInfo &p_info) {
@@ -116,7 +112,7 @@ static GDScriptParser::DataType make_native_enum_type(const StringName &p_native
type.is_meta_type = true;
List<StringName> enum_values;
- StringName real_native_name = get_real_class_name(p_native_class);
+ 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()) {
@@ -175,7 +171,7 @@ Error GDScriptAnalyzer::resolve_inheritance(GDScriptParser::ClassNode *p_class,
int extends_index = 0;
- if (!p_class->extends_path.empty()) {
+ if (!p_class->extends_path.is_empty()) {
Ref<GDScriptParserRef> parser = get_parser_for(p_class->extends_path);
if (parser.is_null()) {
push_error(vformat(R"(Could not resolve super class path "%s".)", p_class->extends_path), p_class);
@@ -190,7 +186,7 @@ Error GDScriptAnalyzer::resolve_inheritance(GDScriptParser::ClassNode *p_class,
base = parser->get_parser()->head->get_datatype();
} else {
- if (p_class->extends.empty()) {
+ if (p_class->extends.is_empty()) {
return ERR_PARSE_ERROR;
}
const StringName &name = p_class->extends[extends_index++];
@@ -233,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(get_real_class_name(name))) {
+ } else if (class_exists(name) && ClassDB::can_instance(GDScriptParser::get_real_class_name(name))) {
base.kind = GDScriptParser::DataType::NATIVE;
base.native_type = name;
} else {
@@ -342,7 +338,7 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type
result.type_source = result.ANNOTATED_EXPLICIT;
result.builtin_type = Variant::OBJECT;
- if (p_type->type_chain.empty()) {
+ if (p_type->type_chain.is_empty()) {
// void.
result.kind = GDScriptParser::DataType::BUILTIN;
result.builtin_type = Variant::NIL;
@@ -379,6 +375,14 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type
}
result.kind = GDScriptParser::DataType::BUILTIN;
result.builtin_type = GDScriptParser::get_builtin_type(first);
+
+ if (result.builtin_type == Variant::ARRAY) {
+ GDScriptParser::DataType container_type = resolve_datatype(p_type->container_type);
+
+ if (container_type.kind != GDScriptParser::DataType::VARIANT) {
+ result.set_container_element_type(container_type);
+ }
+ }
} else if (class_exists(first)) {
// Native engine classes.
result.kind = GDScriptParser::DataType::NATIVE;
@@ -402,7 +406,7 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type
return GDScriptParser::DataType();
}
result = ref->get_parser()->head->get_datatype();
- } else if (ClassDB::has_enum(get_real_class_name(parser->current_class->base_type.native_type), first)) {
+ } else if (ClassDB::has_enum(GDScriptParser::get_real_class_name(parser->current_class->base_type.native_type), first)) {
// Native enum in current class.
result = make_native_enum_type(parser->current_class->base_type.native_type, first);
} else {
@@ -465,7 +469,7 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type
}
} else if (result.kind == GDScriptParser::DataType::NATIVE) {
// Only enums allowed for native.
- if (ClassDB::has_enum(get_real_class_name(result.native_type), p_type->type_chain[1]->name)) {
+ if (ClassDB::has_enum(GDScriptParser::get_real_class_name(result.native_type), p_type->type_chain[1]->name)) {
if (p_type->type_chain.size() > 2) {
push_error(R"(Enums cannot contain nested types.)", p_type->type_chain[2]);
} else {
@@ -479,6 +483,10 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type
}
}
+ if (result.builtin_type != Variant::ARRAY && p_type->container_type != nullptr) {
+ push_error("Only arrays can specify the collection element type.", p_type);
+ }
+
p_type->set_datatype(result);
return result;
}
@@ -501,9 +509,23 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas
datatype.kind = GDScriptParser::DataType::VARIANT;
datatype.type_source = GDScriptParser::DataType::UNDETECTED;
+ GDScriptParser::DataType specified_type;
+ if (member.variable->datatype_specifier != nullptr) {
+ specified_type = resolve_datatype(member.variable->datatype_specifier);
+ specified_type.is_meta_type = false;
+ }
+
if (member.variable->initializer != nullptr) {
member.variable->set_datatype(datatype); // Allow recursive usage.
reduce_expression(member.variable->initializer);
+ if ((member.variable->infer_datatype || (member.variable->datatype_specifier != nullptr && specified_type.has_container_element_type())) && member.variable->initializer->type == GDScriptParser::Node::ARRAY) {
+ // Typed array.
+ GDScriptParser::ArrayNode *array = static_cast<GDScriptParser::ArrayNode *>(member.variable->initializer);
+ // Can only infer typed array if it has elements.
+ if ((member.variable->infer_datatype && array->elements.size() > 0) || member.variable->datatype_specifier != nullptr) {
+ update_array_literal_element_type(specified_type, array);
+ }
+ }
datatype = member.variable->initializer->get_datatype();
if (datatype.type_source != GDScriptParser::DataType::UNDETECTED) {
datatype.type_source = GDScriptParser::DataType::INFERRED;
@@ -511,8 +533,7 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas
}
if (member.variable->datatype_specifier != nullptr) {
- datatype = resolve_datatype(member.variable->datatype_specifier);
- datatype.is_meta_type = false;
+ datatype = specified_type;
if (member.variable->initializer != nullptr) {
if (!is_type_compatible(datatype, member.variable->initializer->get_datatype(), true)) {
@@ -548,37 +569,32 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas
datatype.is_constant = false;
member.variable->set_datatype(datatype);
- if (!datatype.has_no_type()) {
- // TODO: Move this out into a routine specific to validate annotations.
- if (member.variable->export_info.hint == PROPERTY_HINT_TYPE_STRING) {
- // @export annotation.
- switch (datatype.kind) {
- case GDScriptParser::DataType::BUILTIN:
- member.variable->export_info.hint_string = Variant::get_type_name(datatype.builtin_type);
- break;
- case GDScriptParser::DataType::NATIVE:
- if (ClassDB::is_parent_class(get_real_class_name(datatype.native_type), "Resource")) {
- member.variable->export_info.hint = PROPERTY_HINT_RESOURCE_TYPE;
- member.variable->export_info.hint_string = get_real_class_name(datatype.native_type);
- } else {
- push_error(R"(Export type can only be built-in or a resource.)", member.variable);
- }
- break;
- default:
- // TODO: Allow custom user resources.
- push_error(R"(Export type can only be built-in or a resource.)", member.variable);
- break;
- }
- }
+
+ // Apply annotations.
+ for (List<GDScriptParser::AnnotationNode *>::Element *E = member.variable->annotations.front(); E; E = E->next()) {
+ E->get()->apply(parser, member.variable);
}
} break;
case GDScriptParser::ClassNode::Member::CONSTANT: {
reduce_expression(member.constant->initializer);
+ GDScriptParser::DataType specified_type;
+
+ if (member.constant->datatype_specifier != nullptr) {
+ specified_type = resolve_datatype(member.constant->datatype_specifier);
+ specified_type.is_meta_type = false;
+ }
+
GDScriptParser::DataType datatype = member.constant->get_datatype();
if (member.constant->initializer) {
if (member.constant->initializer->type == GDScriptParser::Node::ARRAY) {
- const_fold_array(static_cast<GDScriptParser::ArrayNode *>(member.constant->initializer));
+ GDScriptParser::ArrayNode *array = static_cast<GDScriptParser::ArrayNode *>(member.constant->initializer);
+ const_fold_array(array);
+
+ // Can only infer typed array if it has elements.
+ if (array->elements.size() > 0 || (member.constant->datatype_specifier != nullptr && specified_type.has_container_element_type())) {
+ update_array_literal_element_type(specified_type, array);
+ }
} else if (member.constant->initializer->type == GDScriptParser::Node::DICTIONARY) {
const_fold_dictionary(static_cast<GDScriptParser::DictionaryNode *>(member.constant->initializer));
}
@@ -588,8 +604,7 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas
}
if (member.constant->datatype_specifier != nullptr) {
- datatype = resolve_datatype(member.constant->datatype_specifier);
- datatype.is_meta_type = false;
+ datatype = specified_type;
if (!is_type_compatible(datatype, member.constant->initializer->get_datatype(), true)) {
push_error(vformat(R"(Value of type "%s" cannot be initialized to constant of type "%s".)", member.constant->initializer->get_datatype().to_string(), datatype.to_string()), member.constant->initializer);
@@ -603,6 +618,11 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas
datatype.is_constant = true;
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);
+ }
} break;
case GDScriptParser::ClassNode::Member::SIGNAL: {
for (int j = 0; j < member.signal->parameters.size(); j++) {
@@ -617,6 +637,11 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas
signal_type.builtin_type = Variant::SIGNAL;
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);
+ }
} break;
case GDScriptParser::ClassNode::Member::ENUM: {
GDScriptParser::DataType enum_type;
@@ -659,6 +684,11 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas
current_enum = nullptr;
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);
+ }
} break;
case GDScriptParser::ClassNode::Member::FUNCTION:
resolve_function_signature(member.function);
@@ -727,6 +757,11 @@ 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);
+ }
}
parser->current_class = previous_class;
@@ -860,7 +895,12 @@ void GDScriptAnalyzer::resolve_function_signature(GDScriptParser::FunctionNode *
parser->push_warning(p_function->parameters[i]->identifier, GDScriptWarning::UNUSED_PARAMETER, p_function->identifier->name, p_function->parameters[i]->identifier->name);
}
is_shadowing(p_function->parameters[i]->identifier, "function parameter");
-#endif
+#endif // DEBUG_ENABLED
+#ifdef TOOLS_ENABLED
+ if (p_function->parameters[i]->default_value && p_function->parameters[i]->default_value->is_constant) {
+ p_function->default_arg_values.push_back(p_function->parameters[i]->default_value->reduced_value);
+ }
+#endif // TOOLS_ENABLED
}
if (p_function->identifier->name == "_init") {
@@ -978,17 +1018,19 @@ void GDScriptAnalyzer::resolve_for(GDScriptParser::ForNode *p_for) {
if (!call->arguments[i]->is_constant) {
all_is_constant = false;
- } else {
+ } else if (all_is_constant) {
args.write[i] = call->arguments[i]->reduced_value;
}
GDScriptParser::DataType arg_type = call->arguments[i]->get_datatype();
- if (arg_type.kind != GDScriptParser::DataType::BUILTIN) {
- all_is_constant = false;
- push_error(vformat(R"*(Invalid argument for "range()" call. Argument %d should be int or float but "%s" was given.)*", i + 1, arg_type.to_string()), call->arguments[i]);
- } else if (arg_type.builtin_type != Variant::INT && arg_type.builtin_type != Variant::FLOAT) {
- all_is_constant = false;
- push_error(vformat(R"*(Invalid argument for "range()" call. Argument %d should be int or float but "%s" was given.)*", i + 1, arg_type.to_string()), call->arguments[i]);
+ if (!arg_type.is_variant()) {
+ if (arg_type.kind != GDScriptParser::DataType::BUILTIN) {
+ all_is_constant = false;
+ push_error(vformat(R"*(Invalid argument for "range()" call. Argument %d should be int or float but "%s" was given.)*", i + 1, arg_type.to_string()), call->arguments[i]);
+ } else if (arg_type.builtin_type != Variant::INT && arg_type.builtin_type != Variant::FLOAT) {
+ all_is_constant = false;
+ push_error(vformat(R"*(Invalid argument for "range()" call. Argument %d should be int or float but "%s" was given.)*", i + 1, arg_type.to_string()), call->arguments[i]);
+ }
}
}
@@ -1011,11 +1053,15 @@ void GDScriptAnalyzer::resolve_for(GDScriptParser::ForNode *p_for) {
}
}
- GDScriptParser::DataType list_type;
- list_type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
- list_type.kind = GDScriptParser::DataType::BUILTIN;
- list_type.builtin_type = Variant::ARRAY;
- p_for->list->set_datatype(list_type);
+ if (p_for->list->is_constant) {
+ p_for->list->set_datatype(type_from_variant(p_for->list->reduced_value, p_for->list));
+ } else {
+ GDScriptParser::DataType list_type;
+ list_type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
+ list_type.kind = GDScriptParser::DataType::BUILTIN;
+ list_type.builtin_type = Variant::ARRAY;
+ p_for->list->set_datatype(list_type);
+ }
}
}
}
@@ -1047,8 +1093,23 @@ void GDScriptAnalyzer::resolve_variable(GDScriptParser::VariableNode *p_variable
GDScriptParser::DataType type;
type.kind = GDScriptParser::DataType::VARIANT; // By default.
+ GDScriptParser::DataType specified_type;
+ if (p_variable->datatype_specifier != nullptr) {
+ specified_type = resolve_datatype(p_variable->datatype_specifier);
+ specified_type.is_meta_type = false;
+ }
+
if (p_variable->initializer != nullptr) {
reduce_expression(p_variable->initializer);
+ if ((p_variable->infer_datatype || (p_variable->datatype_specifier != nullptr && specified_type.has_container_element_type())) && p_variable->initializer->type == GDScriptParser::Node::ARRAY) {
+ // Typed array.
+ GDScriptParser::ArrayNode *array = static_cast<GDScriptParser::ArrayNode *>(p_variable->initializer);
+ // Can only infer typed array if it has elements.
+ if ((p_variable->infer_datatype && array->elements.size() > 0) || p_variable->datatype_specifier != nullptr) {
+ update_array_literal_element_type(specified_type, array);
+ }
+ }
+
type = p_variable->initializer->get_datatype();
if (p_variable->infer_datatype) {
@@ -1072,7 +1133,7 @@ void GDScriptAnalyzer::resolve_variable(GDScriptParser::VariableNode *p_variable
}
if (p_variable->datatype_specifier != nullptr) {
- type = resolve_datatype(p_variable->datatype_specifier);
+ type = specified_type;
type.is_meta_type = false;
if (p_variable->initializer != nullptr) {
@@ -1118,24 +1179,26 @@ void GDScriptAnalyzer::resolve_variable(GDScriptParser::VariableNode *p_variable
void GDScriptAnalyzer::resolve_constant(GDScriptParser::ConstantNode *p_constant) {
GDScriptParser::DataType type;
- reduce_expression(p_constant->initializer);
- if (p_constant->initializer->type == GDScriptParser::Node::ARRAY) {
- const_fold_array(static_cast<GDScriptParser::ArrayNode *>(p_constant->initializer));
- } else if (p_constant->initializer->type == GDScriptParser::Node::DICTIONARY) {
- const_fold_dictionary(static_cast<GDScriptParser::DictionaryNode *>(p_constant->initializer));
- }
+ if (p_constant->initializer != nullptr) {
+ reduce_expression(p_constant->initializer);
+ if (p_constant->initializer->type == GDScriptParser::Node::ARRAY) {
+ const_fold_array(static_cast<GDScriptParser::ArrayNode *>(p_constant->initializer));
+ } else if (p_constant->initializer->type == GDScriptParser::Node::DICTIONARY) {
+ const_fold_dictionary(static_cast<GDScriptParser::DictionaryNode *>(p_constant->initializer));
+ }
- if (!p_constant->initializer->is_constant) {
- push_error(vformat(R"(Assigned value for constant "%s" isn't a constant expression.)", p_constant->identifier->name), p_constant->initializer);
- }
+ if (!p_constant->initializer->is_constant) {
+ push_error(vformat(R"(Assigned value for constant "%s" isn't a constant expression.)", p_constant->identifier->name), p_constant->initializer);
+ }
- type = p_constant->initializer->get_datatype();
+ type = p_constant->initializer->get_datatype();
#ifdef DEBUG_ENABLED
- if (p_constant->initializer->type == GDScriptParser::Node::CALL && type.kind == GDScriptParser::DataType::BUILTIN && type.builtin_type == Variant::NIL) {
- parser->push_warning(p_constant->initializer, GDScriptWarning::VOID_ASSIGNMENT, static_cast<GDScriptParser::CallNode *>(p_constant->initializer)->function_name);
- }
+ if (p_constant->initializer->type == GDScriptParser::Node::CALL && type.kind == GDScriptParser::DataType::BUILTIN && type.builtin_type == Variant::NIL) {
+ parser->push_warning(p_constant->initializer, GDScriptWarning::VOID_ASSIGNMENT, static_cast<GDScriptParser::CallNode *>(p_constant->initializer)->function_name);
+ }
#endif
+ }
if (p_constant->datatype_specifier != nullptr) {
GDScriptParser::DataType explicit_type = resolve_datatype(p_constant->datatype_specifier);
@@ -1170,7 +1233,10 @@ void GDScriptAnalyzer::resolve_constant(GDScriptParser::ConstantNode *p_constant
void GDScriptAnalyzer::resolve_assert(GDScriptParser::AssertNode *p_assert) {
reduce_expression(p_assert->condition);
if (p_assert->message != nullptr) {
- reduce_literal(p_assert->message);
+ reduce_expression(p_assert->message);
+ if (!p_assert->message->is_constant || p_assert->message->reduced_value.get_type() != Variant::STRING) {
+ push_error(R"(Expected constant string for assert error message.)", p_assert->message);
+ }
}
p_assert->set_datatype(p_assert->condition->get_datatype());
@@ -1312,6 +1378,12 @@ void GDScriptAnalyzer::resolve_return(GDScriptParser::ReturnNode *p_return) {
if (p_return->return_value != nullptr) {
reduce_expression(p_return->return_value);
+ if (p_return->return_value->type == GDScriptParser::Node::ARRAY) {
+ // Check if assigned value is an array literal, so we can make it a typed array too if appropriate.
+ if (parser->current_function->get_datatype().has_container_element_type() && p_return->return_value->type == GDScriptParser::Node::ARRAY) {
+ update_array_literal_element_type(parser->current_function->get_datatype(), static_cast<GDScriptParser::ArrayNode *>(p_return->return_value));
+ }
+ }
result = p_return->return_value->get_datatype();
} else {
// Return type is null by default.
@@ -1448,6 +1520,52 @@ void GDScriptAnalyzer::reduce_array(GDScriptParser::ArrayNode *p_array) {
p_array->set_datatype(arr_type);
}
+// When an array literal is stored (or passed as function argument) to a typed context, we then assume the array is typed.
+// This function determines which type is that (if any).
+void GDScriptAnalyzer::update_array_literal_element_type(const GDScriptParser::DataType &p_base_type, GDScriptParser::ArrayNode *p_array_literal) {
+ GDScriptParser::DataType array_type = p_array_literal->get_datatype();
+ if (p_array_literal->elements.size() == 0) {
+ // Empty array literal, just make the same type as the storage.
+ array_type.set_container_element_type(p_base_type.get_container_element_type());
+ } else {
+ // Check if elements match.
+ bool all_same_type = true;
+ bool all_have_type = true;
+
+ GDScriptParser::DataType element_type;
+ for (int i = 0; i < p_array_literal->elements.size(); i++) {
+ if (i == 0) {
+ element_type = p_array_literal->elements[0]->get_datatype();
+ } else {
+ GDScriptParser::DataType this_element_type = p_array_literal->elements[i]->get_datatype();
+ if (this_element_type.has_no_type()) {
+ all_same_type = false;
+ all_have_type = false;
+ break;
+ } else if (element_type != this_element_type) {
+ if (!is_type_compatible(element_type, this_element_type, false)) {
+ if (is_type_compatible(this_element_type, element_type, false)) {
+ // This element is a super-type to the previous type, so we use the super-type.
+ element_type = this_element_type;
+ } else {
+ // It's incompatible.
+ all_same_type = false;
+ break;
+ }
+ }
+ }
+ }
+ }
+ if (all_same_type) {
+ array_type.set_container_element_type(element_type);
+ } else if (all_have_type) {
+ push_error(vformat(R"(Variant array is not compatible with an array of type "%s".)", p_base_type.get_container_element_type().to_string()), p_array_literal);
+ }
+ }
+ // Update the type on the value itself.
+ p_array_literal->set_datatype(array_type);
+}
+
void GDScriptAnalyzer::reduce_assignment(GDScriptParser::AssignmentNode *p_assignment) {
reduce_expression(p_assignment->assignee);
reduce_expression(p_assignment->assigned_value);
@@ -1456,24 +1574,33 @@ void GDScriptAnalyzer::reduce_assignment(GDScriptParser::AssignmentNode *p_assig
return;
}
- if (p_assignment->assignee->get_datatype().is_constant) {
+ GDScriptParser::DataType assignee_type = p_assignment->assignee->get_datatype();
+
+ // Check if assigned value is an array literal, so we can make it a typed array too if appropriate.
+ if (assignee_type.has_container_element_type() && p_assignment->assigned_value->type == GDScriptParser::Node::ARRAY) {
+ update_array_literal_element_type(assignee_type, static_cast<GDScriptParser::ArrayNode *>(p_assignment->assigned_value));
+ }
+
+ GDScriptParser::DataType assigned_value_type = p_assignment->assigned_value->get_datatype();
+
+ if (assignee_type.is_constant) {
push_error("Cannot assign a new value to a constant.", p_assignment->assignee);
}
- if (!p_assignment->assignee->get_datatype().is_variant() && !p_assignment->assigned_value->get_datatype().is_variant()) {
+ if (!assignee_type.is_variant() && !assigned_value_type.is_variant()) {
bool compatible = true;
- GDScriptParser::DataType op_type = p_assignment->assigned_value->get_datatype();
+ GDScriptParser::DataType op_type = assigned_value_type;
if (p_assignment->operation != GDScriptParser::AssignmentNode::OP_NONE) {
- op_type = get_operation_type(p_assignment->variant_op, p_assignment->assignee->get_datatype(), p_assignment->assigned_value->get_datatype(), compatible, p_assignment->assigned_value);
+ op_type = get_operation_type(p_assignment->variant_op, assignee_type, assigned_value_type, compatible, p_assignment->assigned_value);
}
if (compatible) {
- compatible = is_type_compatible(p_assignment->assignee->get_datatype(), op_type, true);
+ compatible = is_type_compatible(assignee_type, op_type, true);
if (!compatible) {
- if (p_assignment->assignee->get_datatype().is_hard_type()) {
+ if (assignee_type.is_hard_type()) {
// Try reverse test since it can be a masked subtype.
- if (!is_type_compatible(op_type, p_assignment->assignee->get_datatype(), true)) {
- push_error(vformat(R"(Cannot assign a value of type "%s" to a target of type "%s".)", p_assignment->assigned_value->get_datatype().to_string(), p_assignment->assignee->get_datatype().to_string()), p_assignment->assigned_value);
+ if (!is_type_compatible(op_type, assignee_type, true)) {
+ push_error(vformat(R"(Cannot assign a value of type "%s" to a target of type "%s".)", assigned_value_type.to_string(), assignee_type.to_string()), p_assignment->assigned_value);
} else {
// TODO: Add warning.
mark_node_unsafe(p_assignment);
@@ -1484,11 +1611,11 @@ void GDScriptAnalyzer::reduce_assignment(GDScriptParser::AssignmentNode *p_assig
}
}
} else {
- push_error(vformat(R"(Invalid operands "%s" and "%s" for assignment operator.)", p_assignment->assignee->get_datatype().to_string(), p_assignment->assigned_value->get_datatype().to_string()), p_assignment);
+ push_error(vformat(R"(Invalid operands "%s" and "%s" for assignment operator.)", assignee_type.to_string(), assigned_value_type.to_string()), p_assignment);
}
}
- if (p_assignment->assignee->get_datatype().has_no_type() || p_assignment->assigned_value->get_datatype().is_variant()) {
+ if (assignee_type.has_no_type() || assigned_value_type.is_variant()) {
mark_node_unsafe(p_assignment);
}
@@ -1508,7 +1635,7 @@ void GDScriptAnalyzer::reduce_assignment(GDScriptParser::AssignmentNode *p_assig
case GDScriptParser::IdentifierNode::LOCAL_VARIABLE: {
GDScriptParser::DataType id_type = identifier->variable_source->get_datatype();
if (!id_type.is_hard_type()) {
- id_type = p_assignment->assigned_value->get_datatype();
+ id_type = assigned_value_type;
id_type.type_source = GDScriptParser::DataType::INFERRED;
id_type.is_constant = false;
identifier->variable_source->set_datatype(id_type);
@@ -1517,7 +1644,7 @@ void GDScriptAnalyzer::reduce_assignment(GDScriptParser::AssignmentNode *p_assig
case GDScriptParser::IdentifierNode::LOCAL_ITERATOR: {
GDScriptParser::DataType id_type = identifier->bind_source->get_datatype();
if (!id_type.is_hard_type()) {
- id_type = p_assignment->assigned_value->get_datatype();
+ id_type = assigned_value_type;
id_type.type_source = GDScriptParser::DataType::INFERRED;
id_type.is_constant = false;
identifier->variable_source->set_datatype(id_type);
@@ -1529,12 +1656,10 @@ void GDScriptAnalyzer::reduce_assignment(GDScriptParser::AssignmentNode *p_assig
}
}
- GDScriptParser::DataType assignee_type = p_assignment->assignee->get_datatype();
- GDScriptParser::DataType assigned_type = p_assignment->assigned_value->get_datatype();
#ifdef DEBUG_ENABLED
- if (p_assignment->assigned_value->type == GDScriptParser::Node::CALL && assigned_type.kind == GDScriptParser::DataType::BUILTIN && assigned_type.builtin_type == Variant::NIL) {
+ if (p_assignment->assigned_value->type == GDScriptParser::Node::CALL && assigned_value_type.kind == GDScriptParser::DataType::BUILTIN && assigned_value_type.builtin_type == Variant::NIL) {
parser->push_warning(p_assignment->assigned_value, GDScriptWarning::VOID_ASSIGNMENT, static_cast<GDScriptParser::CallNode *>(p_assignment->assigned_value)->function_name);
- } else if (assignee_type.is_hard_type() && assignee_type.builtin_type == Variant::INT && assigned_type.builtin_type == Variant::FLOAT) {
+ } else if (assignee_type.is_hard_type() && assignee_type.builtin_type == Variant::INT && assigned_value_type.builtin_type == Variant::FLOAT) {
parser->push_warning(p_assignment->assigned_value, GDScriptWarning::NARROWING_CONVERSION);
}
#endif
@@ -1605,7 +1730,7 @@ void GDScriptAnalyzer::reduce_binary_op(GDScriptParser::BinaryOpNode *p_binary_o
if (p_binary_op->reduced_value.get_type() == Variant::STRING) {
push_error(vformat(R"(%s in operator %s.)", p_binary_op->reduced_value, Variant::get_operator_name(p_binary_op->variant_op)), p_binary_op);
} else {
- push_error(vformat(R"(Invalid operands to operator %s, %s and %s.".)",
+ push_error(vformat(R"(Invalid operands to operator %s, %s and %s.)",
Variant::get_operator_name(p_binary_op->variant_op),
Variant::get_type_name(p_binary_op->left_operand->reduced_value.get_type()),
Variant::get_type_name(p_binary_op->right_operand->reduced_value.get_type())),
@@ -1678,8 +1803,12 @@ void GDScriptAnalyzer::reduce_binary_op(GDScriptParser::BinaryOpNode *p_binary_o
void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool is_await) {
bool all_is_constant = true;
+ Map<int, GDScriptParser::ArrayNode *> arrays; // For array literal to potentially type when passing.
for (int i = 0; i < p_call->arguments.size(); i++) {
reduce_expression(p_call->arguments[i]);
+ if (p_call->arguments[i]->type == GDScriptParser::Node::ARRAY) {
+ arrays[i] = static_cast<GDScriptParser::ArrayNode *>(p_call->arguments[i]);
+ }
all_is_constant = all_is_constant && p_call->arguments[i]->is_constant;
}
@@ -1690,7 +1819,6 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool is_awa
// Call to name directly.
StringName function_name = p_call->function_name;
Variant::Type builtin_type = GDScriptParser::get_builtin_type(function_name);
- GDScriptFunctions::Function builtin_function = GDScriptParser::get_builtin_function(function_name);
if (builtin_type < Variant::VARIANT_MAX) {
// Is a builtin constructor.
@@ -1703,7 +1831,29 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool is_awa
call_type.native_type = function_name; // "Object".
}
- if (all_is_constant) {
+ bool safe_to_fold = true;
+ switch (builtin_type) {
+ // Those are stored by reference so not suited for compile-time construction.
+ // Because in this case they would be the same reference in all constructed values.
+ case Variant::OBJECT:
+ case Variant::DICTIONARY:
+ case Variant::ARRAY:
+ case Variant::PACKED_BYTE_ARRAY:
+ case Variant::PACKED_INT32_ARRAY:
+ case Variant::PACKED_INT64_ARRAY:
+ case Variant::PACKED_FLOAT32_ARRAY:
+ case Variant::PACKED_FLOAT64_ARRAY:
+ case Variant::PACKED_STRING_ARRAY:
+ case Variant::PACKED_VECTOR2_ARRAY:
+ case Variant::PACKED_VECTOR3_ARRAY:
+ case Variant::PACKED_COLOR_ARRAY:
+ safe_to_fold = false;
+ break;
+ default:
+ break;
+ }
+
+ if (all_is_constant && safe_to_fold) {
// Construct here.
Vector<const Variant *> args;
for (int i = 0; i < p_call->arguments.size(); i++) {
@@ -1812,10 +1962,52 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool is_awa
}
p_call->set_datatype(call_type);
return;
- } else if (builtin_function < GDScriptFunctions::FUNC_MAX) {
- MethodInfo function_info = GDScriptFunctions::get_info(builtin_function);
+ } else if (GDScriptUtilityFunctions::function_exists(function_name)) {
+ MethodInfo function_info = GDScriptUtilityFunctions::get_function_info(function_name);
+
+ if (all_is_constant && GDScriptUtilityFunctions::is_function_constant(function_name)) {
+ // Can call on compilation.
+ Vector<const Variant *> args;
+ for (int i = 0; i < p_call->arguments.size(); i++) {
+ args.push_back(&(p_call->arguments[i]->reduced_value));
+ }
+
+ Variant value;
+ Callable::CallError err;
+ GDScriptUtilityFunctions::get_function(function_name)(&value, (const Variant **)args.ptr(), args.size(), err);
+
+ switch (err.error) {
+ case Callable::CallError::CALL_ERROR_INVALID_ARGUMENT: {
+ PropertyInfo wrong_arg = function_info.arguments[err.argument];
+ push_error(vformat(R"*(Invalid argument for "%s()" function: argument %d should be %s but is %s.)*", function_name, err.argument + 1,
+ type_from_property(wrong_arg).to_string(), p_call->arguments[err.argument]->get_datatype().to_string()),
+ p_call->arguments[err.argument]);
+ } break;
+ case Callable::CallError::CALL_ERROR_INVALID_METHOD:
+ push_error(vformat(R"(Invalid call for function "%s".)", function_name), p_call);
+ break;
+ case Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS:
+ push_error(vformat(R"*(Too many arguments for "%s()" call. Expected at most %d but received %d.)*", function_name, err.expected, p_call->arguments.size()), p_call);
+ break;
+ case Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS:
+ push_error(vformat(R"*(Too few arguments for "%s()" call. Expected at least %d but received %d.)*", function_name, err.expected, p_call->arguments.size()), p_call);
+ break;
+ case Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL:
+ break; // Can't happen in a builtin constructor.
+ case Callable::CallError::CALL_OK:
+ p_call->is_constant = true;
+ p_call->reduced_value = value;
+ break;
+ }
+ } else {
+ validate_call_arg(function_info, p_call);
+ }
+ p_call->set_datatype(type_from_property(function_info.return_val));
+ return;
+ } else if (Variant::has_utility_function(function_name)) {
+ MethodInfo function_info = info_from_utility_func(function_name);
- if (all_is_constant && GDScriptFunctions::is_deterministic(builtin_function)) {
+ if (all_is_constant && Variant::get_utility_function_type(function_name) == Variant::UTILITY_FUNC_TYPE_MATH) {
// Can call on compilation.
Vector<const Variant *> args;
for (int i = 0; i < p_call->arguments.size(); i++) {
@@ -1824,23 +2016,23 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool is_awa
Variant value;
Callable::CallError err;
- GDScriptFunctions::call(builtin_function, (const Variant **)args.ptr(), args.size(), value, err);
+ Variant::call_utility_function(function_name, &value, (const Variant **)args.ptr(), args.size(), err);
switch (err.error) {
case Callable::CallError::CALL_ERROR_INVALID_ARGUMENT: {
PropertyInfo wrong_arg = function_info.arguments[err.argument];
- push_error(vformat(R"*(Invalid argument for "%s()" function: argument %d should be %s but is %s.)*", GDScriptFunctions::get_func_name(builtin_function), err.argument + 1,
+ push_error(vformat(R"*(Invalid argument for "%s()" function: argument %d should be %s but is %s.)*", function_name, err.argument + 1,
type_from_property(wrong_arg).to_string(), p_call->arguments[err.argument]->get_datatype().to_string()),
p_call->arguments[err.argument]);
} break;
case Callable::CallError::CALL_ERROR_INVALID_METHOD:
- push_error(vformat(R"(Invalid call for function "%s".)", GDScriptFunctions::get_func_name(builtin_function)), p_call);
+ push_error(vformat(R"(Invalid call for function "%s".)", function_name), p_call);
break;
case Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS:
- push_error(vformat(R"*(Too many arguments for "%s()" call. Expected at most %d but received %d.)*", GDScriptFunctions::get_func_name(builtin_function), err.expected, p_call->arguments.size()), p_call);
+ push_error(vformat(R"*(Too many arguments for "%s()" call. Expected at most %d but received %d.)*", function_name, err.expected, p_call->arguments.size()), p_call);
break;
case Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS:
- push_error(vformat(R"*(Too few arguments for "%s()" call. Expected at least %d but received %d.)*", GDScriptFunctions::get_func_name(builtin_function), err.expected, p_call->arguments.size()), p_call);
+ push_error(vformat(R"*(Too few arguments for "%s()" call. Expected at least %d but received %d.)*", function_name, err.expected, p_call->arguments.size()), p_call);
break;
case Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL:
break; // Can't happen in a builtin constructor.
@@ -1894,6 +2086,13 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool is_awa
List<GDScriptParser::DataType> par_types;
if (get_function_signature(p_call, base_type, p_call->function_name, return_type, par_types, default_arg_count, is_static, is_vararg)) {
+ // If the function require typed arrays we must make literals be typed.
+ for (Map<int, GDScriptParser::ArrayNode *>::Element *E = arrays.front(); E; E = E->next()) {
+ int index = E->key();
+ if (index < par_types.size() && par_types[index].has_container_element_type()) {
+ update_array_literal_element_type(par_types[index], E->get());
+ }
+ }
validate_call_arg(par_types, default_arg_count, is_vararg, p_call);
if (is_self && parser->current_function != nullptr && parser->current_function->is_static && !is_static) {
@@ -1923,14 +2122,14 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool is_awa
push_error(vformat(R"*(Name "%s" called as a function but is a "%s".)*", p_call->function_name, callee_datatype.to_string()), p_call->callee);
}
#ifdef DEBUG_ENABLED
- } else if (!is_self) {
+ } else if (!is_self && !(base_type.is_hard_type() && base_type.kind == GDScriptParser::DataType::BUILTIN)) {
parser->push_warning(p_call, GDScriptWarning::UNSAFE_METHOD_ACCESS, p_call->function_name, base_type.to_string());
mark_node_unsafe(p_call);
#endif
}
}
}
- if (!found && is_self) {
+ if (!found && (is_self || (base_type.is_hard_type() && base_type.kind == GDScriptParser::DataType::BUILTIN))) {
String base_name = is_self && !p_call->is_super ? "self" : base_type.to_string();
push_error(vformat(R"*(Function "%s()" not found in base %s.)*", p_call->function_name, base_name), p_call->is_super ? p_call : p_call->callee);
}
@@ -2018,16 +2217,24 @@ void GDScriptAnalyzer::reduce_get_node(GDScriptParser::GetNodeNode *p_get_node)
result.native_type = "Node";
result.builtin_type = Variant::OBJECT;
- if (!ClassDB::is_parent_class(get_real_class_name(parser->current_class->base_type.native_type), result.native_type)) {
+ 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);
}
p_get_node->set_datatype(result);
}
-GDScriptParser::DataType GDScriptAnalyzer::make_global_class_meta_type(const StringName &p_class_name) {
+GDScriptParser::DataType GDScriptAnalyzer::make_global_class_meta_type(const StringName &p_class_name, const GDScriptParser::Node *p_source) {
Ref<GDScriptParserRef> ref = get_parser_for(ScriptServer::get_global_class_path(p_class_name));
- ref->raise_status(GDScriptParserRef::INTERFACE_SOLVED);
+ Error err = ref->raise_status(GDScriptParserRef::INTERFACE_SOLVED);
+
+ if (err) {
+ push_error(vformat(R"(Could not resolve class "%s", because of a parser error.)", p_class_name), p_source);
+ GDScriptParser::DataType type;
+ type.type_source = GDScriptParser::DataType::UNDETECTED;
+ type.kind = GDScriptParser::DataType::VARIANT;
+ return type;
+ }
GDScriptParser::DataType type;
type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
@@ -2176,7 +2383,7 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
}
// Check native members.
- const StringName &native = get_real_class_name(base.native_type);
+ const StringName &native = GDScriptParser::get_real_class_name(base.native_type);
if (class_exists(native)) {
PropertyInfo prop_info;
@@ -2299,7 +2506,7 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident
}
if (ScriptServer::is_global_class(name)) {
- p_identifier->set_datatype(make_global_class_meta_type(name));
+ p_identifier->set_datatype(make_global_class_meta_type(name, p_identifier));
return;
}
@@ -2346,7 +2553,7 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident
// Not found.
// Check if it's a builtin function.
- if (parser->get_builtin_function(name) < GDScriptFunctions::FUNC_MAX) {
+ if (GDScriptUtilityFunctions::function_exists(name)) {
push_error(vformat(R"(Built-in function "%s" cannot be used as an identifier.)", name), p_identifier);
} else {
push_error(vformat(R"(Identifier "%s" not declared in the current scope.)", name), p_identifier);
@@ -2631,11 +2838,20 @@ void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscri
case Variant::TRANSFORM:
case Variant::PLANE:
case Variant::COLOR:
- case Variant::ARRAY:
case Variant::DICTIONARY:
result_type.kind = GDScriptParser::DataType::VARIANT;
result_type.type_source = GDScriptParser::DataType::UNDETECTED;
break;
+ // Can have an element type.
+ case Variant::ARRAY:
+ if (base_type.has_container_element_type()) {
+ result_type = base_type.get_container_element_type();
+ result_type.type_source = base_type.type_source;
+ } else {
+ result_type.kind = GDScriptParser::DataType::VARIANT;
+ result_type.type_source = GDScriptParser::DataType::UNDETECTED;
+ }
+ break;
// Here for completeness.
case Variant::OBJECT:
case Variant::VARIANT_MAX:
@@ -2716,7 +2932,7 @@ void GDScriptAnalyzer::reduce_unary_op(GDScriptParser::UnaryOpNode *p_unary_op)
mark_node_unsafe(p_unary_op);
} else {
bool valid = false;
- result = get_operation_type(p_unary_op->variant_op, p_unary_op->operand->get_datatype(), p_unary_op->operand->get_datatype(), valid, p_unary_op);
+ result = get_operation_type(p_unary_op->variant_op, p_unary_op->operand->get_datatype(), valid, p_unary_op);
if (!valid) {
push_error(vformat(R"(Invalid operand of type "%s" for unary operator "%s".)", p_unary_op->operand->get_datatype().to_string(), Variant::get_operator_name(p_unary_op->variant_op)), p_unary_op->operand);
@@ -2858,6 +3074,34 @@ GDScriptParser::DataType GDScriptAnalyzer::type_from_property(const PropertyInfo
result.native_type = p_property.class_name == StringName() ? "Object" : p_property.class_name;
} else {
result.kind = GDScriptParser::DataType::BUILTIN;
+ result.builtin_type = p_property.type;
+ if (p_property.type == Variant::ARRAY && p_property.hint == PROPERTY_HINT_ARRAY_TYPE) {
+ // Check element type.
+ StringName elem_type_name = p_property.hint_string;
+ GDScriptParser::DataType elem_type;
+ elem_type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
+
+ Variant::Type elem_builtin_type = GDScriptParser::get_builtin_type(elem_type_name);
+ if (elem_builtin_type < Variant::VARIANT_MAX) {
+ // Builtin type.
+ elem_type.kind = GDScriptParser::DataType::BUILTIN;
+ elem_type.builtin_type = elem_builtin_type;
+ } else if (class_exists(elem_type_name)) {
+ elem_type.kind = GDScriptParser::DataType::NATIVE;
+ elem_type.builtin_type = Variant::OBJECT;
+ elem_type.native_type = p_property.hint_string;
+ } else if (ScriptServer::is_global_class(elem_type_name)) {
+ // Just load this as it shouldn't be a GDScript.
+ Ref<Script> script = ResourceLoader::load(ScriptServer::get_global_class_path(elem_type_name));
+ elem_type.kind = GDScriptParser::DataType::SCRIPT;
+ elem_type.builtin_type = Variant::OBJECT;
+ elem_type.native_type = script->get_instance_base_type();
+ elem_type.script_type = script;
+ } else {
+ ERR_FAIL_V_MSG(result, "Could not find element type from property hint of a typed array.");
+ }
+ result.set_container_element_type(elem_type);
+ }
}
return result;
}
@@ -2963,7 +3207,7 @@ bool GDScriptAnalyzer::get_function_signature(GDScriptParser::Node *p_source, GD
return true;
}
- StringName real_native = get_real_class_name(base_native);
+ StringName real_native = GDScriptParser::get_real_class_name(base_native);
MethodInfo info;
if (ClassDB::get_method_info(real_native, function_name, &info)) {
@@ -3058,7 +3302,7 @@ bool GDScriptAnalyzer::is_shadowing(GDScriptParser::IdentifierNode *p_local, con
StringName parent = base_native;
while (parent != StringName()) {
- StringName real_class_name = get_real_class_name(parent);
+ StringName real_class_name = GDScriptParser::get_real_class_name(parent);
if (ClassDB::has_method(real_class_name, name, true)) {
parser->push_warning(p_local, GDScriptWarning::SHADOWED_VARIABLE_BASE_CLASS, p_context, p_local->name, "method", parent);
return true;
@@ -3082,81 +3326,31 @@ bool GDScriptAnalyzer::is_shadowing(GDScriptParser::IdentifierNode *p_local, con
}
#endif
-GDScriptParser::DataType GDScriptAnalyzer::get_operation_type(Variant::Operator p_operation, const GDScriptParser::DataType &p_a, const GDScriptParser::DataType &p_b, bool &r_valid, const GDScriptParser::Node *p_source) {
- // This function creates dummy variant values and apply the operation to those. Less error-prone than keeping a table of valid operations.
+GDScriptParser::DataType GDScriptAnalyzer::get_operation_type(Variant::Operator p_operation, const GDScriptParser::DataType &p_a, bool &r_valid, const GDScriptParser::Node *p_source) {
+ // Unary version.
+ GDScriptParser::DataType nil_type;
+ nil_type.builtin_type = Variant::NIL;
+ return get_operation_type(p_operation, p_a, nil_type, r_valid, p_source);
+}
+GDScriptParser::DataType GDScriptAnalyzer::get_operation_type(Variant::Operator p_operation, const GDScriptParser::DataType &p_a, const GDScriptParser::DataType &p_b, bool &r_valid, const GDScriptParser::Node *p_source) {
GDScriptParser::DataType result;
result.kind = GDScriptParser::DataType::VARIANT;
Variant::Type a_type = p_a.builtin_type;
Variant::Type b_type = p_b.builtin_type;
- Variant a;
- REF a_ref;
- if (a_type == Variant::OBJECT) {
- a_ref.instance();
- a = a_ref;
- } else {
- Callable::CallError err;
- Variant::construct(a_type, a, nullptr, 0, err);
- if (err.error != Callable::CallError::CALL_OK) {
- r_valid = false;
- ERR_FAIL_V_MSG(result, vformat("Could not construct value of type %s", Variant::get_type_name(a_type)));
- }
- }
- Variant b;
- REF b_ref;
- if (b_type == Variant::OBJECT) {
- b_ref.instance();
- b = b_ref;
- } else {
- Callable::CallError err;
- Variant::construct(b_type, b, nullptr, 0, err);
- if (err.error != Callable::CallError::CALL_OK) {
- r_valid = false;
- ERR_FAIL_V_MSG(result, vformat("Could not construct value of type %s", Variant::get_type_name(b_type)));
- }
- }
-
- // Avoid division by zero.
- switch (b_type) {
- case Variant::INT:
- b = 1;
- break;
- case Variant::FLOAT:
- b = 1.0;
- break;
- case Variant::VECTOR2:
- b = Vector2(1.0, 1.0);
- break;
- case Variant::VECTOR2I:
- b = Vector2i(1, 1);
- break;
- case Variant::VECTOR3:
- b = Vector3(1.0, 1.0, 1.0);
- break;
- case Variant::VECTOR3I:
- b = Vector3i(1, 1, 1);
- break;
- case Variant::COLOR:
- b = Color(1.0, 1.0, 1.0, 1.0);
- break;
- default:
- // No change needed.
- break;
- }
+ Variant::ValidatedOperatorEvaluator op_eval = Variant::get_validated_operator_evaluator(p_operation, a_type, b_type);
- // Avoid error in formatting operator (%) where it doesn't find a placeholder.
- if (a_type == Variant::STRING && b_type != Variant::ARRAY) {
- a = String("%s");
+ if (op_eval == nullptr) {
+ r_valid = false;
+ return result;
}
- Variant ret;
- Variant::evaluate(p_operation, a, b, ret, r_valid);
+ r_valid = true;
- if (r_valid) {
- return type_from_variant(ret, p_source);
- }
+ result.kind = GDScriptParser::DataType::BUILTIN;
+ result.builtin_type = Variant::get_operator_return_type(p_operation, a_type, b_type);
return result;
}
@@ -3186,6 +3380,18 @@ bool GDScriptAnalyzer::is_type_compatible(const GDScriptParser::DataType &p_targ
// Enum value is also integer.
valid = true;
}
+ if (valid && p_target.builtin_type == Variant::ARRAY && p_source.builtin_type == Variant::ARRAY) {
+ // Check the element type.
+ if (p_target.has_container_element_type()) {
+ if (!p_source.has_container_element_type()) {
+ // TODO: Maybe this is valid but unsafe?
+ // Variant array can't be appended to typed array.
+ valid = false;
+ } else {
+ valid = is_type_compatible(p_target.get_container_element_type(), p_source.get_container_element_type(), false);
+ }
+ }
+ }
return valid;
}
@@ -3258,14 +3464,14 @@ bool GDScriptAnalyzer::is_type_compatible(const GDScriptParser::DataType &p_targ
}
// Get underscore-prefixed version for some classes.
- src_native = get_real_class_name(src_native);
+ src_native = GDScriptParser::get_real_class_name(src_native);
switch (p_target.kind) {
case GDScriptParser::DataType::NATIVE: {
if (p_target.is_meta_type) {
return ClassDB::is_parent_class(src_native, GDScriptNativeClass::get_class_static());
}
- StringName tgt_native = get_real_class_name(p_target.native_type);
+ StringName tgt_native = GDScriptParser::get_real_class_name(p_target.native_type);
return ClassDB::is_parent_class(src_native, tgt_native);
}
case GDScriptParser::DataType::SCRIPT:
@@ -3314,8 +3520,8 @@ void GDScriptAnalyzer::mark_node_unsafe(const GDScriptParser::Node *p_node) {
#endif
}
-bool GDScriptAnalyzer::class_exists(const StringName &p_class) {
- StringName real_name = get_real_class_name(p_class);
+bool GDScriptAnalyzer::class_exists(const StringName &p_class) const {
+ StringName real_name = GDScriptParser::get_real_class_name(p_class);
return ClassDB::class_exists(real_name) && ClassDB::is_class_exposed(real_name);
}
@@ -3338,12 +3544,12 @@ Error GDScriptAnalyzer::resolve_inheritance() {
Error GDScriptAnalyzer::resolve_interface() {
resolve_class_interface(parser->head);
- return parser->errors.empty() ? OK : ERR_PARSE_ERROR;
+ return parser->errors.is_empty() ? OK : ERR_PARSE_ERROR;
}
Error GDScriptAnalyzer::resolve_body() {
resolve_class_body(parser->head);
- return parser->errors.empty() ? OK : ERR_PARSE_ERROR;
+ return parser->errors.is_empty() ? OK : ERR_PARSE_ERROR;
}
Error GDScriptAnalyzer::resolve_program() {
@@ -3358,8 +3564,7 @@ Error GDScriptAnalyzer::resolve_program() {
}
depended_parsers[E->get()]->raise_status(GDScriptParserRef::FULLY_SOLVED);
}
- depended_parsers.clear();
- return parser->errors.empty() ? OK : ERR_PARSE_ERROR;
+ return parser->errors.is_empty() ? OK : ERR_PARSE_ERROR;
}
Error GDScriptAnalyzer::analyze() {
diff --git a/modules/gdscript/gdscript_analyzer.h b/modules/gdscript/gdscript_analyzer.h
index 0a952cc621..8430d3f4a5 100644
--- a/modules/gdscript/gdscript_analyzer.h
+++ b/modules/gdscript/gdscript_analyzer.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -96,16 +96,18 @@ class GDScriptAnalyzer {
GDScriptParser::DataType type_from_variant(const Variant &p_value, const GDScriptParser::Node *p_source);
GDScriptParser::DataType type_from_metatype(const GDScriptParser::DataType &p_meta_type) const;
GDScriptParser::DataType type_from_property(const PropertyInfo &p_property) const;
- GDScriptParser::DataType make_global_class_meta_type(const StringName &p_class_name);
+ GDScriptParser::DataType make_global_class_meta_type(const StringName &p_class_name, const GDScriptParser::Node *p_source);
bool get_function_signature(GDScriptParser::Node *p_source, GDScriptParser::DataType base_type, const StringName &p_function, GDScriptParser::DataType &r_return_type, List<GDScriptParser::DataType> &r_par_types, int &r_default_arg_count, bool &r_static, bool &r_vararg);
bool function_signature_from_info(const MethodInfo &p_info, GDScriptParser::DataType &r_return_type, List<GDScriptParser::DataType> &r_par_types, int &r_default_arg_count, bool &r_static, bool &r_vararg);
bool validate_call_arg(const List<GDScriptParser::DataType> &p_par_types, int p_default_args_count, bool p_is_vararg, const GDScriptParser::CallNode *p_call);
bool validate_call_arg(const MethodInfo &p_method, const GDScriptParser::CallNode *p_call);
GDScriptParser::DataType get_operation_type(Variant::Operator p_operation, const GDScriptParser::DataType &p_a, const GDScriptParser::DataType &p_b, bool &r_valid, const GDScriptParser::Node *p_source);
+ GDScriptParser::DataType get_operation_type(Variant::Operator p_operation, const GDScriptParser::DataType &p_a, bool &r_valid, const GDScriptParser::Node *p_source);
+ void update_array_literal_element_type(const GDScriptParser::DataType &p_base_type, GDScriptParser::ArrayNode *p_array_literal);
bool is_type_compatible(const GDScriptParser::DataType &p_target, const GDScriptParser::DataType &p_source, bool p_allow_implicit_conversion = false) const;
void push_error(const String &p_message, const GDScriptParser::Node *p_origin);
void mark_node_unsafe(const GDScriptParser::Node *p_node);
- bool class_exists(const StringName &p_class);
+ bool class_exists(const StringName &p_class) const;
Ref<GDScriptParserRef> get_parser_for(const String &p_path);
#ifdef DEBUG_ENABLED
bool is_shadowing(GDScriptParser::IdentifierNode *p_local, const String &p_context);
@@ -118,8 +120,6 @@ public:
Error analyze();
GDScriptAnalyzer(GDScriptParser *p_parser);
-
- static void cleanup();
};
#endif // GDSCRIPT_ANALYZER_H
diff --git a/modules/gdscript/gdscript_byte_codegen.cpp b/modules/gdscript/gdscript_byte_codegen.cpp
index cc9e87b882..e27329d18b 100644
--- a/modules/gdscript/gdscript_byte_codegen.cpp
+++ b/modules/gdscript/gdscript_byte_codegen.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -40,10 +40,6 @@ uint32_t GDScriptByteCodeGenerator::add_parameter(const StringName &p_name, bool
function->_argument_count++;
function->argument_types.push_back(p_type);
if (p_is_optional) {
- if (function->_default_arg_count == 0) {
- append(GDScriptFunction::OPCODE_JUMP_TO_DEF_ARGUMENT);
- }
- function->default_arguments.push_back(opcodes.size());
function->_default_arg_count++;
}
@@ -77,15 +73,31 @@ uint32_t GDScriptByteCodeGenerator::add_or_get_name(const StringName &p_name) {
uint32_t GDScriptByteCodeGenerator::add_temporary() {
current_temporaries++;
- return increase_stack();
+ int idx = increase_stack();
+#ifdef DEBUG_ENABLED
+ temp_stack.push_back(idx);
+#endif
+ return idx;
}
void GDScriptByteCodeGenerator::pop_temporary() {
+ ERR_FAIL_COND(current_temporaries == 0);
current_stack_size--;
+#ifdef DEBUG_ENABLED
+ if (temp_stack.back()->get() != current_stack_size) {
+ ERR_PRINT("Mismatched popping of temporary value");
+ }
+ temp_stack.pop_back();
+#endif
current_temporaries--;
}
-void GDScriptByteCodeGenerator::start_parameters() {}
+void GDScriptByteCodeGenerator::start_parameters() {
+ if (function->_default_arg_count > 0) {
+ append(GDScriptFunction::OPCODE_JUMP_TO_DEF_ARGUMENT);
+ function->default_arguments.push_back(opcodes.size());
+ }
+}
void GDScriptByteCodeGenerator::end_parameters() {
function->default_arguments.invert();
@@ -111,7 +123,12 @@ void GDScriptByteCodeGenerator::write_start(GDScript *p_script, const StringName
}
GDScriptFunction *GDScriptByteCodeGenerator::write_end() {
- append(GDScriptFunction::OPCODE_END);
+#ifdef DEBUG_ENABLED
+ if (current_temporaries != 0) {
+ ERR_PRINT("Non-zero temporary variables at end of function: " + itos(current_temporaries));
+ }
+#endif
+ append(GDScriptFunction::OPCODE_END, 0);
if (constant_map.size()) {
function->_constant_count = constant_map.size();
@@ -151,18 +168,163 @@ GDScriptFunction *GDScriptByteCodeGenerator::write_end() {
}
if (function->default_arguments.size()) {
- function->_default_arg_count = function->default_arguments.size();
+ function->_default_arg_count = function->default_arguments.size() - 1;
function->_default_arg_ptr = &function->default_arguments[0];
} else {
function->_default_arg_count = 0;
function->_default_arg_ptr = nullptr;
}
+ if (operator_func_map.size()) {
+ function->operator_funcs.resize(operator_func_map.size());
+ function->_operator_funcs_count = function->operator_funcs.size();
+ function->_operator_funcs_ptr = function->operator_funcs.ptr();
+ for (const Map<Variant::ValidatedOperatorEvaluator, int>::Element *E = operator_func_map.front(); E; E = E->next()) {
+ function->operator_funcs.write[E->get()] = E->key();
+ }
+ } else {
+ function->_operator_funcs_count = 0;
+ function->_operator_funcs_ptr = nullptr;
+ }
+
+ if (setters_map.size()) {
+ function->setters.resize(setters_map.size());
+ function->_setters_count = function->setters.size();
+ function->_setters_ptr = function->setters.ptr();
+ for (const Map<Variant::ValidatedSetter, int>::Element *E = setters_map.front(); E; E = E->next()) {
+ function->setters.write[E->get()] = E->key();
+ }
+ } else {
+ function->_setters_count = 0;
+ function->_setters_ptr = nullptr;
+ }
+
+ if (getters_map.size()) {
+ function->getters.resize(getters_map.size());
+ function->_getters_count = function->getters.size();
+ function->_getters_ptr = function->getters.ptr();
+ for (const Map<Variant::ValidatedGetter, int>::Element *E = getters_map.front(); E; E = E->next()) {
+ function->getters.write[E->get()] = E->key();
+ }
+ } else {
+ function->_getters_count = 0;
+ function->_getters_ptr = nullptr;
+ }
+
+ if (keyed_setters_map.size()) {
+ function->keyed_setters.resize(keyed_setters_map.size());
+ function->_keyed_setters_count = function->keyed_setters.size();
+ function->_keyed_setters_ptr = function->keyed_setters.ptr();
+ for (const Map<Variant::ValidatedKeyedSetter, int>::Element *E = keyed_setters_map.front(); E; E = E->next()) {
+ function->keyed_setters.write[E->get()] = E->key();
+ }
+ } else {
+ function->_keyed_setters_count = 0;
+ function->_keyed_setters_ptr = nullptr;
+ }
+
+ if (keyed_getters_map.size()) {
+ function->keyed_getters.resize(keyed_getters_map.size());
+ function->_keyed_getters_count = function->keyed_getters.size();
+ function->_keyed_getters_ptr = function->keyed_getters.ptr();
+ for (const Map<Variant::ValidatedKeyedGetter, int>::Element *E = keyed_getters_map.front(); E; E = E->next()) {
+ function->keyed_getters.write[E->get()] = E->key();
+ }
+ } else {
+ function->_keyed_getters_count = 0;
+ function->_keyed_getters_ptr = nullptr;
+ }
+
+ if (indexed_setters_map.size()) {
+ function->indexed_setters.resize(indexed_setters_map.size());
+ function->_indexed_setters_count = function->indexed_setters.size();
+ function->_indexed_setters_ptr = function->indexed_setters.ptr();
+ for (const Map<Variant::ValidatedIndexedSetter, int>::Element *E = indexed_setters_map.front(); E; E = E->next()) {
+ function->indexed_setters.write[E->get()] = E->key();
+ }
+ } else {
+ function->_indexed_setters_count = 0;
+ function->_indexed_setters_ptr = nullptr;
+ }
+
+ if (indexed_getters_map.size()) {
+ function->indexed_getters.resize(indexed_getters_map.size());
+ function->_indexed_getters_count = function->indexed_getters.size();
+ function->_indexed_getters_ptr = function->indexed_getters.ptr();
+ for (const Map<Variant::ValidatedIndexedGetter, int>::Element *E = indexed_getters_map.front(); E; E = E->next()) {
+ function->indexed_getters.write[E->get()] = E->key();
+ }
+ } else {
+ function->_indexed_getters_count = 0;
+ function->_indexed_getters_ptr = nullptr;
+ }
+
+ if (builtin_method_map.size()) {
+ function->builtin_methods.resize(builtin_method_map.size());
+ function->_builtin_methods_ptr = function->builtin_methods.ptr();
+ function->_builtin_methods_count = builtin_method_map.size();
+ for (const Map<Variant::ValidatedBuiltInMethod, int>::Element *E = builtin_method_map.front(); E; E = E->next()) {
+ function->builtin_methods.write[E->get()] = E->key();
+ }
+ } else {
+ function->_builtin_methods_ptr = nullptr;
+ function->_builtin_methods_count = 0;
+ }
+
+ if (constructors_map.size()) {
+ function->constructors.resize(constructors_map.size());
+ function->_constructors_ptr = function->constructors.ptr();
+ function->_constructors_count = constructors_map.size();
+ for (const Map<Variant::ValidatedConstructor, int>::Element *E = constructors_map.front(); E; E = E->next()) {
+ function->constructors.write[E->get()] = E->key();
+ }
+ } else {
+ function->_constructors_ptr = nullptr;
+ function->_constructors_count = 0;
+ }
+
+ if (utilities_map.size()) {
+ function->utilities.resize(utilities_map.size());
+ function->_utilities_ptr = function->utilities.ptr();
+ function->_utilities_count = utilities_map.size();
+ for (const Map<Variant::ValidatedUtilityFunction, int>::Element *E = utilities_map.front(); E; E = E->next()) {
+ function->utilities.write[E->get()] = E->key();
+ }
+ } else {
+ function->_utilities_ptr = nullptr;
+ function->_utilities_count = 0;
+ }
+
+ if (gds_utilities_map.size()) {
+ function->gds_utilities.resize(gds_utilities_map.size());
+ function->_gds_utilities_ptr = function->gds_utilities.ptr();
+ function->_gds_utilities_count = gds_utilities_map.size();
+ for (const Map<GDScriptUtilityFunctions::FunctionPtr, int>::Element *E = gds_utilities_map.front(); E; E = E->next()) {
+ function->gds_utilities.write[E->get()] = E->key();
+ }
+ } else {
+ function->_gds_utilities_ptr = nullptr;
+ function->_gds_utilities_count = 0;
+ }
+
+ if (method_bind_map.size()) {
+ function->methods.resize(method_bind_map.size());
+ function->_methods_ptr = function->methods.ptrw();
+ function->_methods_count = method_bind_map.size();
+ for (const Map<MethodBind *, int>::Element *E = method_bind_map.front(); E; E = E->next()) {
+ function->methods.write[E->get()] = E->key();
+ }
+ } else {
+ function->_methods_ptr = nullptr;
+ function->_methods_count = 0;
+ }
+
if (debug_stack) {
function->stack_debug = stack_debug;
}
function->_stack_size = stack_max;
- function->_call_size = call_max;
+ function->_instruction_args_size = instr_args_max;
+ function->_ptrcall_args_size = ptrcall_max;
ended = true;
return function;
@@ -178,37 +340,77 @@ void GDScriptByteCodeGenerator::set_initial_line(int p_line) {
function->_initial_line = p_line;
}
-void GDScriptByteCodeGenerator::write_operator(const Address &p_target, Variant::Operator p_operator, const Address &p_left_operand, const Address &p_right_operand) {
- append(GDScriptFunction::OPCODE_OPERATOR);
+#define HAS_BUILTIN_TYPE(m_var) \
+ (m_var.type.has_type && m_var.type.kind == GDScriptDataType::BUILTIN)
+
+#define IS_BUILTIN_TYPE(m_var, m_type) \
+ (m_var.type.has_type && m_var.type.kind == GDScriptDataType::BUILTIN && m_var.type.builtin_type == m_type)
+
+void GDScriptByteCodeGenerator::write_unary_operator(const Address &p_target, Variant::Operator p_operator, const Address &p_left_operand) {
+ if (HAS_BUILTIN_TYPE(p_left_operand)) {
+ // Gather specific operator.
+ Variant::ValidatedOperatorEvaluator op_func = Variant::get_validated_operator_evaluator(p_operator, p_left_operand.type.builtin_type, Variant::NIL);
+
+ append(GDScriptFunction::OPCODE_OPERATOR_VALIDATED, 3);
+ append(p_left_operand);
+ append(Address());
+ append(p_target);
+ append(op_func);
+ return;
+ }
+
+ // No specific types, perform variant evaluation.
+ append(GDScriptFunction::OPCODE_OPERATOR, 3);
+ append(p_left_operand);
+ append(Address());
+ append(p_target);
append(p_operator);
+}
+
+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)) {
+ // 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);
+
+ append(GDScriptFunction::OPCODE_OPERATOR_VALIDATED, 3);
+ append(p_left_operand);
+ append(p_right_operand);
+ append(p_target);
+ append(op_func);
+ return;
+ }
+
+ // No specific types, perform variant evaluation.
+ append(GDScriptFunction::OPCODE_OPERATOR, 3);
append(p_left_operand);
append(p_right_operand);
append(p_target);
+ append(p_operator);
}
void GDScriptByteCodeGenerator::write_type_test(const Address &p_target, const Address &p_source, const Address &p_type) {
- append(GDScriptFunction::OPCODE_EXTENDS_TEST);
+ append(GDScriptFunction::OPCODE_EXTENDS_TEST, 3);
append(p_source);
append(p_type);
append(p_target);
}
void GDScriptByteCodeGenerator::write_type_test_builtin(const Address &p_target, const Address &p_source, Variant::Type p_type) {
- append(GDScriptFunction::OPCODE_IS_BUILTIN);
+ append(GDScriptFunction::OPCODE_IS_BUILTIN, 3);
append(p_source);
- append(p_type);
append(p_target);
+ append(p_type);
}
void GDScriptByteCodeGenerator::write_and_left_operand(const Address &p_left_operand) {
- append(GDScriptFunction::OPCODE_JUMP_IF_NOT);
+ append(GDScriptFunction::OPCODE_JUMP_IF_NOT, 1);
append(p_left_operand);
logic_op_jump_pos1.push_back(opcodes.size());
append(0); // Jump target, will be patched.
}
void GDScriptByteCodeGenerator::write_and_right_operand(const Address &p_right_operand) {
- append(GDScriptFunction::OPCODE_JUMP_IF_NOT);
+ append(GDScriptFunction::OPCODE_JUMP_IF_NOT, 1);
append(p_right_operand);
logic_op_jump_pos2.push_back(opcodes.size());
append(0); // Jump target, will be patched.
@@ -216,29 +418,29 @@ void GDScriptByteCodeGenerator::write_and_right_operand(const Address &p_right_o
void GDScriptByteCodeGenerator::write_end_and(const Address &p_target) {
// If here means both operands are true.
- append(GDScriptFunction::OPCODE_ASSIGN_TRUE);
+ append(GDScriptFunction::OPCODE_ASSIGN_TRUE, 1);
append(p_target);
// Jump away from the fail condition.
- append(GDScriptFunction::OPCODE_JUMP);
+ append(GDScriptFunction::OPCODE_JUMP, 0);
append(opcodes.size() + 3);
// Here it means one of operands is false.
patch_jump(logic_op_jump_pos1.back()->get());
patch_jump(logic_op_jump_pos2.back()->get());
logic_op_jump_pos1.pop_back();
logic_op_jump_pos2.pop_back();
- append(GDScriptFunction::OPCODE_ASSIGN_FALSE);
+ append(GDScriptFunction::OPCODE_ASSIGN_FALSE, 1);
append(p_target);
}
void GDScriptByteCodeGenerator::write_or_left_operand(const Address &p_left_operand) {
- append(GDScriptFunction::OPCODE_JUMP_IF);
+ append(GDScriptFunction::OPCODE_JUMP_IF, 1);
append(p_left_operand);
logic_op_jump_pos1.push_back(opcodes.size());
append(0); // Jump target, will be patched.
}
void GDScriptByteCodeGenerator::write_or_right_operand(const Address &p_right_operand) {
- append(GDScriptFunction::OPCODE_JUMP_IF);
+ append(GDScriptFunction::OPCODE_JUMP_IF, 1);
append(p_right_operand);
logic_op_jump_pos2.push_back(opcodes.size());
append(0); // Jump target, will be patched.
@@ -246,17 +448,17 @@ void GDScriptByteCodeGenerator::write_or_right_operand(const Address &p_right_op
void GDScriptByteCodeGenerator::write_end_or(const Address &p_target) {
// If here means both operands are false.
- append(GDScriptFunction::OPCODE_ASSIGN_FALSE);
+ append(GDScriptFunction::OPCODE_ASSIGN_FALSE, 1);
append(p_target);
// Jump away from the success condition.
- append(GDScriptFunction::OPCODE_JUMP);
+ append(GDScriptFunction::OPCODE_JUMP, 0);
append(opcodes.size() + 3);
- // Here it means one of operands is false.
+ // Here it means one of operands is true.
patch_jump(logic_op_jump_pos1.back()->get());
patch_jump(logic_op_jump_pos2.back()->get());
logic_op_jump_pos1.pop_back();
logic_op_jump_pos2.pop_back();
- append(GDScriptFunction::OPCODE_ASSIGN_TRUE);
+ append(GDScriptFunction::OPCODE_ASSIGN_TRUE, 1);
append(p_target);
}
@@ -265,18 +467,18 @@ void GDScriptByteCodeGenerator::write_start_ternary(const Address &p_target) {
}
void GDScriptByteCodeGenerator::write_ternary_condition(const Address &p_condition) {
- append(GDScriptFunction::OPCODE_JUMP_IF_NOT);
+ append(GDScriptFunction::OPCODE_JUMP_IF_NOT, 1);
append(p_condition);
ternary_jump_fail_pos.push_back(opcodes.size());
append(0); // Jump target, will be patched.
}
void GDScriptByteCodeGenerator::write_ternary_true_expr(const Address &p_expr) {
- append(GDScriptFunction::OPCODE_ASSIGN);
+ append(GDScriptFunction::OPCODE_ASSIGN, 2);
append(ternary_result.back()->get());
append(p_expr);
// Jump away from the false path.
- append(GDScriptFunction::OPCODE_JUMP);
+ append(GDScriptFunction::OPCODE_JUMP, 0);
ternary_jump_skip_pos.push_back(opcodes.size());
append(0);
// Fail must jump here.
@@ -285,7 +487,7 @@ void GDScriptByteCodeGenerator::write_ternary_true_expr(const Address &p_expr) {
}
void GDScriptByteCodeGenerator::write_ternary_false_expr(const Address &p_expr) {
- append(GDScriptFunction::OPCODE_ASSIGN);
+ append(GDScriptFunction::OPCODE_ASSIGN, 2);
append(ternary_result.back()->get());
append(p_expr);
}
@@ -296,43 +498,100 @@ void GDScriptByteCodeGenerator::write_end_ternary() {
}
void GDScriptByteCodeGenerator::write_set(const Address &p_target, const Address &p_index, const Address &p_source) {
- append(GDScriptFunction::OPCODE_SET);
+ if (HAS_BUILTIN_TYPE(p_target)) {
+ if (IS_BUILTIN_TYPE(p_index, Variant::INT) && Variant::get_member_validated_indexed_setter(p_target.type.builtin_type)) {
+ // Use indexed setter instead.
+ Variant::ValidatedIndexedSetter setter = Variant::get_member_validated_indexed_setter(p_target.type.builtin_type);
+ append(GDScriptFunction::OPCODE_SET_INDEXED_VALIDATED, 3);
+ append(p_target);
+ append(p_index);
+ append(p_source);
+ append(setter);
+ return;
+ } else if (Variant::get_member_validated_keyed_setter(p_target.type.builtin_type)) {
+ Variant::ValidatedKeyedSetter setter = Variant::get_member_validated_keyed_setter(p_target.type.builtin_type);
+ append(GDScriptFunction::OPCODE_SET_KEYED_VALIDATED, 3);
+ append(p_target);
+ append(p_index);
+ append(p_source);
+ append(setter);
+ return;
+ }
+ }
+
+ append(GDScriptFunction::OPCODE_SET_KEYED, 3);
append(p_target);
append(p_index);
append(p_source);
}
void GDScriptByteCodeGenerator::write_get(const Address &p_target, const Address &p_index, const Address &p_source) {
- append(GDScriptFunction::OPCODE_GET);
+ if (HAS_BUILTIN_TYPE(p_source)) {
+ if (IS_BUILTIN_TYPE(p_index, Variant::INT) && Variant::get_member_validated_indexed_getter(p_source.type.builtin_type)) {
+ // Use indexed getter instead.
+ Variant::ValidatedIndexedGetter getter = Variant::get_member_validated_indexed_getter(p_source.type.builtin_type);
+ append(GDScriptFunction::OPCODE_GET_INDEXED_VALIDATED, 3);
+ append(p_source);
+ append(p_index);
+ append(p_target);
+ append(getter);
+ return;
+ } else if (Variant::get_member_validated_keyed_getter(p_source.type.builtin_type)) {
+ Variant::ValidatedKeyedGetter getter = Variant::get_member_validated_keyed_getter(p_source.type.builtin_type);
+ append(GDScriptFunction::OPCODE_GET_KEYED_VALIDATED, 3);
+ append(p_source);
+ append(p_index);
+ append(p_target);
+ append(getter);
+ return;
+ }
+ }
+ append(GDScriptFunction::OPCODE_GET_KEYED, 3);
append(p_source);
append(p_index);
append(p_target);
}
void GDScriptByteCodeGenerator::write_set_named(const Address &p_target, const StringName &p_name, const Address &p_source) {
- append(GDScriptFunction::OPCODE_SET_NAMED);
+ if (HAS_BUILTIN_TYPE(p_target) && Variant::get_member_validated_setter(p_target.type.builtin_type, p_name)) {
+ Variant::ValidatedSetter setter = Variant::get_member_validated_setter(p_target.type.builtin_type, p_name);
+ append(GDScriptFunction::OPCODE_SET_NAMED_VALIDATED, 2);
+ append(p_target);
+ append(p_source);
+ append(setter);
+ return;
+ }
+ append(GDScriptFunction::OPCODE_SET_NAMED, 2);
append(p_target);
- append(p_name);
append(p_source);
+ append(p_name);
}
void GDScriptByteCodeGenerator::write_get_named(const Address &p_target, const StringName &p_name, const Address &p_source) {
- append(GDScriptFunction::OPCODE_GET_NAMED);
+ if (HAS_BUILTIN_TYPE(p_source) && Variant::get_member_validated_getter(p_source.type.builtin_type, p_name)) {
+ Variant::ValidatedGetter getter = Variant::get_member_validated_getter(p_source.type.builtin_type, p_name);
+ append(GDScriptFunction::OPCODE_GET_NAMED_VALIDATED, 2);
+ append(p_source);
+ append(p_target);
+ append(getter);
+ return;
+ }
+ append(GDScriptFunction::OPCODE_GET_NAMED, 2);
append(p_source);
- append(p_name);
append(p_target);
+ append(p_name);
}
void GDScriptByteCodeGenerator::write_set_member(const Address &p_value, const StringName &p_name) {
- append(GDScriptFunction::OPCODE_SET_MEMBER);
- append(p_name);
+ append(GDScriptFunction::OPCODE_SET_MEMBER, 1);
append(p_value);
+ append(p_name);
}
void GDScriptByteCodeGenerator::write_get_member(const Address &p_target, const StringName &p_name) {
- append(GDScriptFunction::OPCODE_GET_MEMBER);
- append(p_name);
+ append(GDScriptFunction::OPCODE_GET_MEMBER, 1);
append(p_target);
+ append(p_name);
}
void GDScriptByteCodeGenerator::write_assign(const Address &p_target, const Address &p_source) {
@@ -340,48 +599,59 @@ void GDScriptByteCodeGenerator::write_assign(const Address &p_target, const Addr
// Typed assignment.
switch (p_target.type.kind) {
case GDScriptDataType::BUILTIN: {
- append(GDScriptFunction::OPCODE_ASSIGN_TYPED_BUILTIN);
- append(p_target.type.builtin_type);
- append(p_target);
- append(p_source);
+ 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];
class_idx |= (GDScriptFunction::ADDR_TYPE_GLOBAL << GDScriptFunction::ADDR_BITS);
- append(GDScriptFunction::OPCODE_ASSIGN_TYPED_NATIVE);
- append(class_idx);
+ 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);
+ idx |= (GDScriptFunction::ADDR_TYPE_LOCAL_CONSTANT << GDScriptFunction::ADDR_BITS);
- append(GDScriptFunction::OPCODE_ASSIGN_TYPED_SCRIPT);
- append(idx);
+ append(GDScriptFunction::OPCODE_ASSIGN_TYPED_SCRIPT, 3);
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);
+ append(GDScriptFunction::OPCODE_ASSIGN, 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) {
+ 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);
- append(p_target.type.builtin_type);
+ append(GDScriptFunction::OPCODE_ASSIGN_TYPED_BUILTIN, 2);
append(p_target);
append(p_source);
+ append(p_target.type.builtin_type);
} else {
// Either untyped assignment or already type-checked by the parser
- append(GDScriptFunction::OPCODE_ASSIGN);
+ append(GDScriptFunction::OPCODE_ASSIGN, 2);
append(p_target);
append(p_source);
}
@@ -389,34 +659,42 @@ void GDScriptByteCodeGenerator::write_assign(const Address &p_target, const Addr
}
void GDScriptByteCodeGenerator::write_assign_true(const Address &p_target) {
- append(GDScriptFunction::OPCODE_ASSIGN_TRUE);
+ append(GDScriptFunction::OPCODE_ASSIGN_TRUE, 1);
append(p_target);
}
void GDScriptByteCodeGenerator::write_assign_false(const Address &p_target) {
- append(GDScriptFunction::OPCODE_ASSIGN_FALSE);
+ append(GDScriptFunction::OPCODE_ASSIGN_FALSE, 1);
append(p_target);
}
+void GDScriptByteCodeGenerator::write_assign_default_parameter(const Address &p_dst, const Address &p_src) {
+ write_assign(p_dst, p_src);
+ function->default_arguments.push_back(opcodes.size());
+}
+
void GDScriptByteCodeGenerator::write_cast(const Address &p_target, const Address &p_source, const GDScriptDataType &p_type) {
+ int index = 0;
+
switch (p_type.kind) {
case GDScriptDataType::BUILTIN: {
- append(GDScriptFunction::OPCODE_CAST_TO_BUILTIN);
- append(p_type.builtin_type);
+ append(GDScriptFunction::OPCODE_CAST_TO_BUILTIN, 2);
+ index = p_type.builtin_type;
} break;
case GDScriptDataType::NATIVE: {
int class_idx = GDScriptLanguage::get_singleton()->get_global_map()[p_type.native_type];
class_idx |= (GDScriptFunction::ADDR_TYPE_GLOBAL << GDScriptFunction::ADDR_BITS);
- append(GDScriptFunction::OPCODE_CAST_TO_NATIVE);
- append(class_idx);
+ append(GDScriptFunction::OPCODE_CAST_TO_NATIVE, 3);
+ index = class_idx;
} break;
case GDScriptDataType::SCRIPT:
case GDScriptDataType::GDSCRIPT: {
Variant script = p_type.script_type;
int idx = get_constant_pos(script);
+ idx |= (GDScriptFunction::ADDR_TYPE_LOCAL_CONSTANT << GDScriptFunction::ADDR_BITS);
- append(GDScriptFunction::OPCODE_CAST_TO_SCRIPT);
- append(idx);
+ append(GDScriptFunction::OPCODE_CAST_TO_SCRIPT, 3);
+ index = idx;
} break;
default: {
return;
@@ -425,147 +703,337 @@ void GDScriptByteCodeGenerator::write_cast(const Address &p_target, const Addres
append(p_source);
append(p_target);
+ append(index);
}
void GDScriptByteCodeGenerator::write_call(const Address &p_target, const Address &p_base, const StringName &p_function_name, const Vector<Address> &p_arguments) {
- append(p_target.mode == Address::NIL ? GDScriptFunction::OPCODE_CALL : GDScriptFunction::OPCODE_CALL_RETURN);
- append(p_arguments.size());
- append(p_base);
- append(p_function_name);
+ append(p_target.mode == Address::NIL ? GDScriptFunction::OPCODE_CALL : GDScriptFunction::OPCODE_CALL_RETURN, 2 + p_arguments.size());
for (int i = 0; i < p_arguments.size(); i++) {
append(p_arguments[i]);
}
+ append(p_base);
append(p_target);
- alloc_call(p_arguments.size());
+ append(p_arguments.size());
+ append(p_function_name);
}
void GDScriptByteCodeGenerator::write_super_call(const Address &p_target, const StringName &p_function_name, const Vector<Address> &p_arguments) {
- append(GDScriptFunction::OPCODE_CALL_SELF_BASE);
- append(p_function_name);
- append(p_arguments.size());
+ append(GDScriptFunction::OPCODE_CALL_SELF_BASE, 1 + p_arguments.size());
for (int i = 0; i < p_arguments.size(); i++) {
append(p_arguments[i]);
}
append(p_target);
- alloc_call(p_arguments.size());
+ append(p_arguments.size());
+ append(p_function_name);
}
void GDScriptByteCodeGenerator::write_call_async(const Address &p_target, const Address &p_base, const StringName &p_function_name, const Vector<Address> &p_arguments) {
- append(GDScriptFunction::OPCODE_CALL_ASYNC);
- append(p_arguments.size());
- append(p_base);
- append(p_function_name);
+ append(GDScriptFunction::OPCODE_CALL_ASYNC, 2 + p_arguments.size());
for (int i = 0; i < p_arguments.size(); i++) {
append(p_arguments[i]);
}
+ append(p_base);
append(p_target);
- alloc_call(p_arguments.size());
+ append(p_arguments.size());
+ append(p_function_name);
}
-void GDScriptByteCodeGenerator::write_call_builtin(const Address &p_target, GDScriptFunctions::Function p_function, const Vector<Address> &p_arguments) {
- append(GDScriptFunction::OPCODE_CALL_BUILT_IN);
- append(p_function);
- append(p_arguments.size());
+void GDScriptByteCodeGenerator::write_call_gdscript_utility(const Address &p_target, GDScriptUtilityFunctions::FunctionPtr p_function, const Vector<Address> &p_arguments) {
+ append(GDScriptFunction::OPCODE_CALL_GDSCRIPT_UTILITY, 1 + p_arguments.size());
for (int i = 0; i < p_arguments.size(); i++) {
append(p_arguments[i]);
}
append(p_target);
- alloc_call(p_arguments.size());
+ append(p_arguments.size());
+ append(p_function);
}
-void GDScriptByteCodeGenerator::write_call_method_bind(const Address &p_target, const Address &p_base, const MethodBind *p_method, const Vector<Address> &p_arguments) {
- append(p_target.mode == Address::NIL ? GDScriptFunction::OPCODE_CALL : GDScriptFunction::OPCODE_CALL_RETURN);
- append(p_arguments.size());
- append(p_base);
- append(p_method->get_name());
+void GDScriptByteCodeGenerator::write_call_utility(const Address &p_target, const StringName &p_function, const Vector<Address> &p_arguments) {
+ bool is_validated = true;
+ if (Variant::is_utility_function_vararg(p_function)) {
+ is_validated = true; // Vararg works fine with any argument, since they can be any type.
+ } else if (p_arguments.size() == Variant::get_utility_function_argument_count(p_function)) {
+ bool all_types_exact = true;
+ for (int i = 0; i < p_arguments.size(); i++) {
+ if (!IS_BUILTIN_TYPE(p_arguments[i], Variant::get_utility_function_argument_type(p_function, i))) {
+ all_types_exact = false;
+ break;
+ }
+ }
+
+ is_validated = all_types_exact;
+ }
+
+ if (is_validated) {
+ append(GDScriptFunction::OPCODE_CALL_UTILITY_VALIDATED, 1 + p_arguments.size());
+ for (int i = 0; i < p_arguments.size(); i++) {
+ append(p_arguments[i]);
+ }
+ append(p_target);
+ append(p_arguments.size());
+ append(Variant::get_validated_utility_function(p_function));
+ } else {
+ append(GDScriptFunction::OPCODE_CALL_UTILITY, 1 + p_arguments.size());
+ for (int i = 0; i < p_arguments.size(); i++) {
+ append(p_arguments[i]);
+ }
+ append(p_target);
+ append(p_arguments.size());
+ append(p_function);
+ }
+}
+
+void GDScriptByteCodeGenerator::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) {
+ 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.
+ write_call(p_target, p_base, p_method, p_arguments);
+ return;
+ }
+
+ 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(p_base);
append(p_target);
- alloc_call(p_arguments.size());
+ append(p_arguments.size());
+ append(Variant::get_validated_builtin_method(p_type, p_method));
}
-void GDScriptByteCodeGenerator::write_call_ptrcall(const Address &p_target, const Address &p_base, const MethodBind *p_method, const Vector<Address> &p_arguments) {
- append(p_target.mode == Address::NIL ? GDScriptFunction::OPCODE_CALL : GDScriptFunction::OPCODE_CALL_RETURN);
- append(p_arguments.size());
+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++) {
+ append(p_arguments[i]);
+ }
append(p_base);
- append(p_method->get_name());
+ append(p_target);
+ append(p_arguments.size());
+ append(p_method);
+}
+
+void GDScriptByteCodeGenerator::write_call_ptrcall(const Address &p_target, const Address &p_base, MethodBind *p_method, const Vector<Address> &p_arguments) {
+#define CASE_TYPE(m_type) \
+ case Variant::m_type: \
+ append(GDScriptFunction::OPCODE_CALL_PTRCALL_##m_type, 2 + p_arguments.size()); \
+ break
+
+ bool is_ptrcall = true;
+
+ if (p_method->has_return()) {
+ MethodInfo info;
+ ClassDB::get_method_info(p_method->get_instance_class(), p_method->get_name(), &info);
+ switch (info.return_val.type) {
+ CASE_TYPE(BOOL);
+ CASE_TYPE(INT);
+ CASE_TYPE(FLOAT);
+ CASE_TYPE(STRING);
+ CASE_TYPE(VECTOR2);
+ CASE_TYPE(VECTOR2I);
+ CASE_TYPE(RECT2);
+ CASE_TYPE(RECT2I);
+ CASE_TYPE(VECTOR3);
+ CASE_TYPE(VECTOR3I);
+ CASE_TYPE(TRANSFORM2D);
+ CASE_TYPE(PLANE);
+ CASE_TYPE(AABB);
+ CASE_TYPE(BASIS);
+ CASE_TYPE(TRANSFORM);
+ CASE_TYPE(COLOR);
+ CASE_TYPE(STRING_NAME);
+ CASE_TYPE(NODE_PATH);
+ CASE_TYPE(RID);
+ CASE_TYPE(QUAT);
+ CASE_TYPE(OBJECT);
+ CASE_TYPE(CALLABLE);
+ CASE_TYPE(SIGNAL);
+ CASE_TYPE(DICTIONARY);
+ CASE_TYPE(ARRAY);
+ CASE_TYPE(PACKED_BYTE_ARRAY);
+ CASE_TYPE(PACKED_INT32_ARRAY);
+ CASE_TYPE(PACKED_INT64_ARRAY);
+ CASE_TYPE(PACKED_FLOAT32_ARRAY);
+ CASE_TYPE(PACKED_FLOAT64_ARRAY);
+ CASE_TYPE(PACKED_STRING_ARRAY);
+ CASE_TYPE(PACKED_VECTOR2_ARRAY);
+ CASE_TYPE(PACKED_VECTOR3_ARRAY);
+ CASE_TYPE(PACKED_COLOR_ARRAY);
+ default:
+ append(p_target.mode == Address::NIL ? GDScriptFunction::OPCODE_CALL_METHOD_BIND : GDScriptFunction::OPCODE_CALL_METHOD_BIND_RET, 2 + p_arguments.size());
+ is_ptrcall = false;
+ break;
+ }
+ } else {
+ append(GDScriptFunction::OPCODE_CALL_PTRCALL_NO_RETURN, 2 + p_arguments.size());
+ }
+
for (int i = 0; i < p_arguments.size(); i++) {
append(p_arguments[i]);
}
+ append(p_base);
append(p_target);
- alloc_call(p_arguments.size());
+ append(p_arguments.size());
+ append(p_method);
+ if (is_ptrcall) {
+ alloc_ptrcall(p_arguments.size());
+ }
+
+#undef CASE_TYPE
}
void GDScriptByteCodeGenerator::write_call_self(const Address &p_target, const StringName &p_function_name, const Vector<Address> &p_arguments) {
- append(p_target.mode == Address::NIL ? GDScriptFunction::OPCODE_CALL : GDScriptFunction::OPCODE_CALL_RETURN);
- append(p_arguments.size());
+ append(p_target.mode == Address::NIL ? GDScriptFunction::OPCODE_CALL : GDScriptFunction::OPCODE_CALL_RETURN, 2 + p_arguments.size());
+ for (int i = 0; i < p_arguments.size(); i++) {
+ append(p_arguments[i]);
+ }
append(GDScriptFunction::ADDR_TYPE_SELF << GDScriptFunction::ADDR_BITS);
+ append(p_target);
+ append(p_arguments.size());
append(p_function_name);
+}
+
+void GDScriptByteCodeGenerator::write_call_self_async(const Address &p_target, const StringName &p_function_name, const Vector<Address> &p_arguments) {
+ append(GDScriptFunction::OPCODE_CALL_ASYNC, 2 + p_arguments.size());
for (int i = 0; i < p_arguments.size(); i++) {
append(p_arguments[i]);
}
+ append(GDScriptFunction::ADDR_TYPE_SELF << GDScriptFunction::ADDR_BITS);
append(p_target);
- alloc_call(p_arguments.size());
+ append(p_arguments.size());
+ append(p_function_name);
}
void GDScriptByteCodeGenerator::write_call_script_function(const Address &p_target, const Address &p_base, const StringName &p_function_name, const Vector<Address> &p_arguments) {
- append(p_target.mode == Address::NIL ? GDScriptFunction::OPCODE_CALL : GDScriptFunction::OPCODE_CALL_RETURN);
- append(p_arguments.size());
- append(p_base);
- append(p_function_name);
+ append(p_target.mode == Address::NIL ? GDScriptFunction::OPCODE_CALL : GDScriptFunction::OPCODE_CALL_RETURN, 2 + p_arguments.size());
for (int i = 0; i < p_arguments.size(); i++) {
append(p_arguments[i]);
}
+ append(p_base);
append(p_target);
- alloc_call(p_arguments.size());
+ append(p_arguments.size());
+ append(p_function_name);
}
void GDScriptByteCodeGenerator::write_construct(const Address &p_target, Variant::Type p_type, const Vector<Address> &p_arguments) {
- append(GDScriptFunction::OPCODE_CONSTRUCT);
- append(p_type);
- append(p_arguments.size());
+ // Try to find an appropriate constructor.
+ bool all_have_type = true;
+ Vector<Variant::Type> arg_types;
+ for (int i = 0; i < p_arguments.size(); i++) {
+ if (!HAS_BUILTIN_TYPE(p_arguments[i])) {
+ all_have_type = false;
+ break;
+ }
+ arg_types.push_back(p_arguments[i].type.builtin_type);
+ }
+ if (all_have_type) {
+ int valid_constructor = -1;
+ for (int i = 0; i < Variant::get_constructor_count(p_type); i++) {
+ if (Variant::get_constructor_argument_count(p_type, i) != p_arguments.size()) {
+ continue;
+ }
+ int types_correct = true;
+ for (int j = 0; j < arg_types.size(); j++) {
+ if (arg_types[j] != Variant::get_constructor_argument_type(p_type, i, j)) {
+ types_correct = false;
+ break;
+ }
+ }
+ if (types_correct) {
+ valid_constructor = i;
+ break;
+ }
+ }
+ if (valid_constructor >= 0) {
+ append(GDScriptFunction::OPCODE_CONSTRUCT_VALIDATED, 1 + p_arguments.size());
+ for (int i = 0; i < p_arguments.size(); i++) {
+ append(p_arguments[i]);
+ }
+ append(p_target);
+ append(p_arguments.size());
+ append(Variant::get_validated_constructor(p_type, valid_constructor));
+ return;
+ }
+ }
+
+ append(GDScriptFunction::OPCODE_CONSTRUCT, 1 + p_arguments.size());
for (int i = 0; i < p_arguments.size(); i++) {
append(p_arguments[i]);
}
append(p_target);
- alloc_call(p_arguments.size());
+ append(p_arguments.size());
+ append(p_type);
}
void GDScriptByteCodeGenerator::write_construct_array(const Address &p_target, const Vector<Address> &p_arguments) {
- append(GDScriptFunction::OPCODE_CONSTRUCT_ARRAY);
+ append(GDScriptFunction::OPCODE_CONSTRUCT_ARRAY, 1 + p_arguments.size());
+ for (int i = 0; i < p_arguments.size(); i++) {
+ append(p_arguments[i]);
+ }
+ append(p_target);
append(p_arguments.size());
+}
+
+void GDScriptByteCodeGenerator::write_construct_typed_array(const Address &p_target, const GDScriptDataType &p_element_type, const Vector<Address> &p_arguments) {
+ append(GDScriptFunction::OPCODE_CONSTRUCT_TYPED_ARRAY, 2 + p_arguments.size());
for (int i = 0; i < p_arguments.size(); i++) {
append(p_arguments[i]);
}
append(p_target);
+ if (p_element_type.script_type) {
+ Variant script_type = Ref<Script>(p_element_type.script_type);
+ int addr = get_constant_pos(script_type);
+ addr |= GDScriptFunction::ADDR_TYPE_LOCAL_CONSTANT << GDScriptFunction::ADDR_BITS;
+ append(addr);
+ } else {
+ append(Address()); // null.
+ }
+ append(p_arguments.size());
+ append(p_element_type.builtin_type);
+ append(p_element_type.native_type);
}
void GDScriptByteCodeGenerator::write_construct_dictionary(const Address &p_target, const Vector<Address> &p_arguments) {
- append(GDScriptFunction::OPCODE_CONSTRUCT_DICTIONARY);
- append(p_arguments.size() / 2); // This is number of key-value pairs, so only half of actual arguments.
+ append(GDScriptFunction::OPCODE_CONSTRUCT_DICTIONARY, 1 + p_arguments.size());
for (int i = 0; i < p_arguments.size(); i++) {
append(p_arguments[i]);
}
append(p_target);
+ append(p_arguments.size() / 2); // This is number of key-value pairs, so only half of actual arguments.
}
void GDScriptByteCodeGenerator::write_await(const Address &p_target, const Address &p_operand) {
- append(GDScriptFunction::OPCODE_AWAIT);
+ append(GDScriptFunction::OPCODE_AWAIT, 1);
append(p_operand);
- append(GDScriptFunction::OPCODE_AWAIT_RESUME);
+ append(GDScriptFunction::OPCODE_AWAIT_RESUME, 1);
append(p_target);
}
void GDScriptByteCodeGenerator::write_if(const Address &p_condition) {
- append(GDScriptFunction::OPCODE_JUMP_IF_NOT);
+ append(GDScriptFunction::OPCODE_JUMP_IF_NOT, 1);
append(p_condition);
if_jmp_addrs.push_back(opcodes.size());
append(0); // Jump destination, will be patched.
}
void GDScriptByteCodeGenerator::write_else() {
- append(GDScriptFunction::OPCODE_JUMP); // Jump from true if block;
+ append(GDScriptFunction::OPCODE_JUMP, 0); // Jump from true if block;
int else_jmp_addr = opcodes.size();
append(0); // Jump destination, will be patched.
@@ -579,41 +1047,144 @@ void GDScriptByteCodeGenerator::write_endif() {
if_jmp_addrs.pop_back();
}
-void GDScriptByteCodeGenerator::write_for(const Address &p_variable, const Address &p_list) {
- int counter_pos = add_temporary() | (GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS);
- int container_pos = add_temporary() | (GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS);
+void GDScriptByteCodeGenerator::start_for(const GDScriptDataType &p_iterator_type, const GDScriptDataType &p_list_type) {
+ Address counter(Address::LOCAL_VARIABLE, add_local("@counter_pos", p_iterator_type), p_iterator_type);
+ Address container(Address::LOCAL_VARIABLE, add_local("@container_pos", p_list_type), p_list_type);
- current_breaks_to_patch.push_back(List<int>());
+ // Store state.
+ for_counter_variables.push_back(counter);
+ for_container_variables.push_back(container);
+}
+
+void GDScriptByteCodeGenerator::write_for_assignment(const Address &p_variable, const Address &p_list) {
+ const Address &container = for_container_variables.back()->get();
// Assign container.
- append(GDScriptFunction::OPCODE_ASSIGN);
- append(container_pos);
+ append(GDScriptFunction::OPCODE_ASSIGN, 2);
+ append(container);
append(p_list);
+ for_iterator_variables.push_back(p_variable);
+}
+
+void GDScriptByteCodeGenerator::write_for() {
+ const Address &iterator = for_iterator_variables.back()->get();
+ const Address &counter = for_counter_variables.back()->get();
+ const Address &container = for_container_variables.back()->get();
+
+ current_breaks_to_patch.push_back(List<int>());
+
+ GDScriptFunction::Opcode begin_opcode = GDScriptFunction::OPCODE_ITERATE_BEGIN;
+ GDScriptFunction::Opcode iterate_opcode = GDScriptFunction::OPCODE_ITERATE;
+
+ if (container.type.has_type) {
+ if (container.type.kind == GDScriptDataType::BUILTIN) {
+ switch (container.type.builtin_type) {
+ case Variant::INT:
+ begin_opcode = GDScriptFunction::OPCODE_ITERATE_BEGIN_INT;
+ iterate_opcode = GDScriptFunction::OPCODE_ITERATE_INT;
+ break;
+ case Variant::FLOAT:
+ begin_opcode = GDScriptFunction::OPCODE_ITERATE_BEGIN_FLOAT;
+ iterate_opcode = GDScriptFunction::OPCODE_ITERATE_FLOAT;
+ break;
+ case Variant::VECTOR2:
+ begin_opcode = GDScriptFunction::OPCODE_ITERATE_BEGIN_VECTOR2;
+ iterate_opcode = GDScriptFunction::OPCODE_ITERATE_VECTOR2;
+ break;
+ case Variant::VECTOR2I:
+ begin_opcode = GDScriptFunction::OPCODE_ITERATE_BEGIN_VECTOR2I;
+ iterate_opcode = GDScriptFunction::OPCODE_ITERATE_VECTOR2I;
+ break;
+ case Variant::VECTOR3:
+ begin_opcode = GDScriptFunction::OPCODE_ITERATE_BEGIN_VECTOR3;
+ iterate_opcode = GDScriptFunction::OPCODE_ITERATE_VECTOR3;
+ break;
+ case Variant::VECTOR3I:
+ begin_opcode = GDScriptFunction::OPCODE_ITERATE_BEGIN_VECTOR3I;
+ iterate_opcode = GDScriptFunction::OPCODE_ITERATE_VECTOR3I;
+ break;
+ case Variant::STRING:
+ begin_opcode = GDScriptFunction::OPCODE_ITERATE_BEGIN_STRING;
+ iterate_opcode = GDScriptFunction::OPCODE_ITERATE_STRING;
+ break;
+ case Variant::DICTIONARY:
+ begin_opcode = GDScriptFunction::OPCODE_ITERATE_BEGIN_DICTIONARY;
+ iterate_opcode = GDScriptFunction::OPCODE_ITERATE_DICTIONARY;
+ break;
+ case Variant::ARRAY:
+ begin_opcode = GDScriptFunction::OPCODE_ITERATE_BEGIN_ARRAY;
+ iterate_opcode = GDScriptFunction::OPCODE_ITERATE_ARRAY;
+ break;
+ case Variant::PACKED_BYTE_ARRAY:
+ begin_opcode = GDScriptFunction::OPCODE_ITERATE_BEGIN_PACKED_BYTE_ARRAY;
+ iterate_opcode = GDScriptFunction::OPCODE_ITERATE_PACKED_BYTE_ARRAY;
+ break;
+ case Variant::PACKED_INT32_ARRAY:
+ begin_opcode = GDScriptFunction::OPCODE_ITERATE_BEGIN_PACKED_INT32_ARRAY;
+ iterate_opcode = GDScriptFunction::OPCODE_ITERATE_PACKED_INT32_ARRAY;
+ break;
+ case Variant::PACKED_INT64_ARRAY:
+ begin_opcode = GDScriptFunction::OPCODE_ITERATE_BEGIN_PACKED_INT64_ARRAY;
+ iterate_opcode = GDScriptFunction::OPCODE_ITERATE_PACKED_INT64_ARRAY;
+ break;
+ case Variant::PACKED_FLOAT32_ARRAY:
+ begin_opcode = GDScriptFunction::OPCODE_ITERATE_BEGIN_PACKED_FLOAT32_ARRAY;
+ iterate_opcode = GDScriptFunction::OPCODE_ITERATE_PACKED_FLOAT32_ARRAY;
+ break;
+ case Variant::PACKED_FLOAT64_ARRAY:
+ begin_opcode = GDScriptFunction::OPCODE_ITERATE_BEGIN_PACKED_FLOAT64_ARRAY;
+ iterate_opcode = GDScriptFunction::OPCODE_ITERATE_PACKED_FLOAT64_ARRAY;
+ break;
+ case Variant::PACKED_STRING_ARRAY:
+ begin_opcode = GDScriptFunction::OPCODE_ITERATE_BEGIN_PACKED_STRING_ARRAY;
+ iterate_opcode = GDScriptFunction::OPCODE_ITERATE_PACKED_STRING_ARRAY;
+ break;
+ case Variant::PACKED_VECTOR2_ARRAY:
+ begin_opcode = GDScriptFunction::OPCODE_ITERATE_BEGIN_PACKED_VECTOR2_ARRAY;
+ iterate_opcode = GDScriptFunction::OPCODE_ITERATE_PACKED_VECTOR2_ARRAY;
+ break;
+ case Variant::PACKED_VECTOR3_ARRAY:
+ begin_opcode = GDScriptFunction::OPCODE_ITERATE_BEGIN_PACKED_VECTOR3_ARRAY;
+ iterate_opcode = GDScriptFunction::OPCODE_ITERATE_PACKED_VECTOR3_ARRAY;
+ break;
+ case Variant::PACKED_COLOR_ARRAY:
+ begin_opcode = GDScriptFunction::OPCODE_ITERATE_BEGIN_PACKED_COLOR_ARRAY;
+ iterate_opcode = GDScriptFunction::OPCODE_ITERATE_PACKED_COLOR_ARRAY;
+ break;
+ default:
+ break;
+ }
+ } else {
+ begin_opcode = GDScriptFunction::OPCODE_ITERATE_BEGIN_OBJECT;
+ iterate_opcode = GDScriptFunction::OPCODE_ITERATE_OBJECT;
+ }
+ }
+
// Begin loop.
- append(GDScriptFunction::OPCODE_ITERATE_BEGIN);
- append(counter_pos);
- append(container_pos);
+ append(begin_opcode, 3);
+ append(counter);
+ append(container);
+ append(iterator);
for_jmp_addrs.push_back(opcodes.size());
append(0); // End of loop address, will be patched.
- append(p_variable);
- append(GDScriptFunction::OPCODE_JUMP);
+ append(GDScriptFunction::OPCODE_JUMP, 0);
append(opcodes.size() + 6); // Skip over 'continue' code.
// Next iteration.
int continue_addr = opcodes.size();
continue_addrs.push_back(continue_addr);
- append(GDScriptFunction::OPCODE_ITERATE);
- append(counter_pos);
- append(container_pos);
+ append(iterate_opcode, 3);
+ append(counter);
+ append(container);
+ append(iterator);
for_jmp_addrs.push_back(opcodes.size());
append(0); // Jump destination, will be patched.
- append(p_variable);
}
void GDScriptByteCodeGenerator::write_endfor() {
// Jump back to loop check.
- append(GDScriptFunction::OPCODE_JUMP);
+ append(GDScriptFunction::OPCODE_JUMP, 0);
append(continue_addrs.back()->get());
continue_addrs.pop_back();
@@ -629,9 +1200,10 @@ void GDScriptByteCodeGenerator::write_endfor() {
}
current_breaks_to_patch.pop_back();
- // Remove loop temporaries.
- pop_temporary();
- pop_temporary();
+ // Pop state.
+ for_iterator_variables.pop_back();
+ for_counter_variables.pop_back();
+ for_container_variables.pop_back();
}
void GDScriptByteCodeGenerator::start_while_condition() {
@@ -641,7 +1213,7 @@ void GDScriptByteCodeGenerator::start_while_condition() {
void GDScriptByteCodeGenerator::write_while(const Address &p_condition) {
// Condition check.
- append(GDScriptFunction::OPCODE_JUMP_IF_NOT);
+ append(GDScriptFunction::OPCODE_JUMP_IF_NOT, 1);
append(p_condition);
while_jmp_addrs.push_back(opcodes.size());
append(0); // End of loop address, will be patched.
@@ -649,7 +1221,7 @@ void GDScriptByteCodeGenerator::write_while(const Address &p_condition) {
void GDScriptByteCodeGenerator::write_endwhile() {
// Jump back to loop check.
- append(GDScriptFunction::OPCODE_JUMP);
+ append(GDScriptFunction::OPCODE_JUMP, 0);
append(continue_addrs.back()->get());
continue_addrs.pop_back();
@@ -687,39 +1259,39 @@ void GDScriptByteCodeGenerator::end_match() {
}
void GDScriptByteCodeGenerator::write_break() {
- append(GDScriptFunction::OPCODE_JUMP);
+ append(GDScriptFunction::OPCODE_JUMP, 0);
current_breaks_to_patch.back()->get().push_back(opcodes.size());
append(0);
}
void GDScriptByteCodeGenerator::write_continue() {
- append(GDScriptFunction::OPCODE_JUMP);
+ append(GDScriptFunction::OPCODE_JUMP, 0);
append(continue_addrs.back()->get());
}
void GDScriptByteCodeGenerator::write_continue_match() {
- append(GDScriptFunction::OPCODE_JUMP);
+ append(GDScriptFunction::OPCODE_JUMP, 0);
match_continues_to_patch.back()->get().push_back(opcodes.size());
append(0);
}
void GDScriptByteCodeGenerator::write_breakpoint() {
- append(GDScriptFunction::OPCODE_BREAKPOINT);
+ append(GDScriptFunction::OPCODE_BREAKPOINT, 0);
}
void GDScriptByteCodeGenerator::write_newline(int p_line) {
- append(GDScriptFunction::OPCODE_LINE);
+ append(GDScriptFunction::OPCODE_LINE, 0);
append(p_line);
current_line = p_line;
}
void GDScriptByteCodeGenerator::write_return(const Address &p_return_value) {
- append(GDScriptFunction::OPCODE_RETURN);
+ append(GDScriptFunction::OPCODE_RETURN, 1);
append(p_return_value);
}
void GDScriptByteCodeGenerator::write_assert(const Address &p_test, const Address &p_message) {
- append(GDScriptFunction::OPCODE_ASSERT);
+ append(GDScriptFunction::OPCODE_ASSERT, 2);
append(p_test);
append(p_message);
}
diff --git a/modules/gdscript/gdscript_byte_codegen.h b/modules/gdscript/gdscript_byte_codegen.h
index 62438b6dd2..52d631c430 100644
--- a/modules/gdscript/gdscript_byte_codegen.h
+++ b/modules/gdscript/gdscript_byte_codegen.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -33,6 +33,9 @@
#include "gdscript_codegen.h"
+#include "gdscript_function.h"
+#include "gdscript_utility_functions.h"
+
class GDScriptByteCodeGenerator : public GDScriptCodeGenerator {
bool ended = false;
GDScriptFunction *function = nullptr;
@@ -41,6 +44,7 @@ class GDScriptByteCodeGenerator : public GDScriptCodeGenerator {
Vector<int> opcodes;
List<Map<StringName, int>> stack_id_stack;
Map<StringName, int> stack_identifiers;
+ List<int> stack_identifiers_counts;
Map<StringName, int> local_constants;
List<GDScriptFunction::StackDebug> stack_debug;
@@ -49,18 +53,40 @@ class GDScriptByteCodeGenerator : public GDScriptCodeGenerator {
int current_stack_size = 0;
int current_temporaries = 0;
+ int current_locals = 0;
+ int current_line = 0;
+ int stack_max = 0;
+ int instr_args_max = 0;
+ int ptrcall_max = 0;
+
+#ifdef DEBUG_ENABLED
+ List<int> temp_stack;
+#endif
HashMap<Variant, int, VariantHasher, VariantComparator> constant_map;
Map<StringName, int> name_map;
#ifdef TOOLS_ENABLED
Vector<StringName> named_globals;
#endif
- int current_line = 0;
- int stack_max = 0;
- int call_max = 0;
-
- List<int> if_jmp_addrs; // List since this can be nested.
+ Map<Variant::ValidatedOperatorEvaluator, int> operator_func_map;
+ Map<Variant::ValidatedSetter, int> setters_map;
+ Map<Variant::ValidatedGetter, int> getters_map;
+ Map<Variant::ValidatedKeyedSetter, int> keyed_setters_map;
+ Map<Variant::ValidatedKeyedGetter, int> keyed_getters_map;
+ Map<Variant::ValidatedIndexedSetter, int> indexed_setters_map;
+ Map<Variant::ValidatedIndexedGetter, int> indexed_getters_map;
+ Map<Variant::ValidatedBuiltInMethod, int> builtin_method_map;
+ Map<Variant::ValidatedConstructor, int> constructors_map;
+ Map<Variant::ValidatedUtilityFunction, int> utilities_map;
+ Map<GDScriptUtilityFunctions::FunctionPtr, int> gds_utilities_map;
+ Map<MethodBind *, int> method_bind_map;
+
+ // Lists since these can be nested.
+ List<int> if_jmp_addrs;
List<int> for_jmp_addrs;
+ List<Address> for_iterator_variables;
+ List<Address> for_counter_variables;
+ List<Address> for_container_variables;
List<int> while_jmp_addrs;
List<int> continue_addrs;
@@ -76,6 +102,7 @@ class GDScriptByteCodeGenerator : public GDScriptCodeGenerator {
List<List<int>> match_continues_to_patch;
void add_stack_identifier(const StringName &p_id, int p_stackpos) {
+ current_locals++;
stack_identifiers[p_id] = p_stackpos;
if (debug_stack) {
block_identifiers[p_id] = p_stackpos;
@@ -89,17 +116,26 @@ class GDScriptByteCodeGenerator : public GDScriptCodeGenerator {
}
void push_stack_identifiers() {
+ stack_identifiers_counts.push_back(current_locals);
stack_id_stack.push_back(stack_identifiers);
if (debug_stack) {
- block_identifier_stack.push_back(block_identifiers);
+ Map<StringName, int> block_ids(block_identifiers);
+ block_identifier_stack.push_back(block_ids);
block_identifiers.clear();
}
}
void pop_stack_identifiers() {
+ current_locals = stack_identifiers_counts.back()->get();
+ stack_identifiers_counts.pop_back();
stack_identifiers = stack_id_stack.back()->get();
- current_stack_size = stack_identifiers.size() + current_temporaries;
stack_id_stack.pop_back();
+#ifdef DEBUG_ENABLED
+ if (current_temporaries != 0) {
+ ERR_PRINT("Leaving block with non-zero temporary variables: " + itos(current_temporaries));
+ }
+#endif
+ current_stack_size = current_locals;
if (debug_stack) {
for (Map<StringName, int>::Element *E = block_identifiers.front(); E; E = E->next()) {
@@ -134,22 +170,123 @@ class GDScriptByteCodeGenerator : public GDScriptCodeGenerator {
return pos;
}
+ int get_operation_pos(const Variant::ValidatedOperatorEvaluator p_operation) {
+ if (operator_func_map.has(p_operation))
+ return operator_func_map[p_operation];
+ int pos = operator_func_map.size();
+ operator_func_map[p_operation] = pos;
+ return pos;
+ }
+
+ int get_setter_pos(const Variant::ValidatedSetter p_setter) {
+ if (setters_map.has(p_setter))
+ return setters_map[p_setter];
+ int pos = setters_map.size();
+ setters_map[p_setter] = pos;
+ return pos;
+ }
+
+ int get_getter_pos(const Variant::ValidatedGetter p_getter) {
+ if (getters_map.has(p_getter))
+ return getters_map[p_getter];
+ int pos = getters_map.size();
+ getters_map[p_getter] = pos;
+ return pos;
+ }
+
+ int get_keyed_setter_pos(const Variant::ValidatedKeyedSetter p_keyed_setter) {
+ if (keyed_setters_map.has(p_keyed_setter))
+ return keyed_setters_map[p_keyed_setter];
+ int pos = keyed_setters_map.size();
+ keyed_setters_map[p_keyed_setter] = pos;
+ return pos;
+ }
+
+ int get_keyed_getter_pos(const Variant::ValidatedKeyedGetter p_keyed_getter) {
+ if (keyed_getters_map.has(p_keyed_getter))
+ return keyed_getters_map[p_keyed_getter];
+ int pos = keyed_getters_map.size();
+ keyed_getters_map[p_keyed_getter] = pos;
+ return pos;
+ }
+
+ int get_indexed_setter_pos(const Variant::ValidatedIndexedSetter p_indexed_setter) {
+ if (indexed_setters_map.has(p_indexed_setter))
+ return indexed_setters_map[p_indexed_setter];
+ int pos = indexed_setters_map.size();
+ indexed_setters_map[p_indexed_setter] = pos;
+ return pos;
+ }
+
+ int get_indexed_getter_pos(const Variant::ValidatedIndexedGetter p_indexed_getter) {
+ if (indexed_getters_map.has(p_indexed_getter))
+ return indexed_getters_map[p_indexed_getter];
+ int pos = indexed_getters_map.size();
+ indexed_getters_map[p_indexed_getter] = pos;
+ return pos;
+ }
+
+ int get_builtin_method_pos(const Variant::ValidatedBuiltInMethod p_method) {
+ if (builtin_method_map.has(p_method)) {
+ return builtin_method_map[p_method];
+ }
+ int pos = builtin_method_map.size();
+ builtin_method_map[p_method] = pos;
+ return pos;
+ }
+
+ int get_constructor_pos(const Variant::ValidatedConstructor p_constructor) {
+ if (constructors_map.has(p_constructor)) {
+ return constructors_map[p_constructor];
+ }
+ int pos = constructors_map.size();
+ constructors_map[p_constructor] = pos;
+ return pos;
+ }
+
+ int get_utility_pos(const Variant::ValidatedUtilityFunction p_utility) {
+ if (utilities_map.has(p_utility)) {
+ return utilities_map[p_utility];
+ }
+ int pos = utilities_map.size();
+ utilities_map[p_utility] = pos;
+ return pos;
+ }
+
+ int get_gds_utility_pos(const GDScriptUtilityFunctions::FunctionPtr p_gds_utility) {
+ if (gds_utilities_map.has(p_gds_utility)) {
+ return gds_utilities_map[p_gds_utility];
+ }
+ int pos = gds_utilities_map.size();
+ gds_utilities_map[p_gds_utility] = pos;
+ return pos;
+ }
+
+ int get_method_bind_pos(MethodBind *p_method) {
+ if (method_bind_map.has(p_method)) {
+ return method_bind_map[p_method];
+ }
+ int pos = method_bind_map.size();
+ method_bind_map[p_method] = pos;
+ return pos;
+ }
+
void alloc_stack(int p_level) {
if (p_level >= stack_max)
stack_max = p_level + 1;
}
- void alloc_call(int p_params) {
- if (p_params >= call_max)
- call_max = p_params;
- }
-
int increase_stack() {
int top = current_stack_size++;
alloc_stack(current_stack_size);
return top;
}
+ void alloc_ptrcall(int p_params) {
+ if (p_params >= ptrcall_max)
+ ptrcall_max = p_params;
+ }
+
int address_of(const Address &p_address) {
switch (p_address.mode) {
case Address::SELF:
@@ -177,8 +314,13 @@ class GDScriptByteCodeGenerator : public GDScriptCodeGenerator {
return -1; // Unreachable.
}
- void append(int code) {
- opcodes.push_back(code);
+ void append(GDScriptFunction::Opcode p_code, int p_argument_count) {
+ opcodes.push_back((p_code & GDScriptFunction::INSTR_MASK) | (p_argument_count << GDScriptFunction::INSTR_BITS));
+ instr_args_max = MAX(instr_args_max, p_argument_count);
+ }
+
+ void append(int p_code) {
+ opcodes.push_back(p_code);
}
void append(const Address &p_address) {
@@ -189,6 +331,54 @@ class GDScriptByteCodeGenerator : public GDScriptCodeGenerator {
opcodes.push_back(get_name_map_pos(p_name));
}
+ void append(const Variant::ValidatedOperatorEvaluator p_operation) {
+ opcodes.push_back(get_operation_pos(p_operation));
+ }
+
+ void append(const Variant::ValidatedSetter p_setter) {
+ opcodes.push_back(get_setter_pos(p_setter));
+ }
+
+ void append(const Variant::ValidatedGetter p_getter) {
+ opcodes.push_back(get_getter_pos(p_getter));
+ }
+
+ void append(const Variant::ValidatedKeyedSetter p_keyed_setter) {
+ opcodes.push_back(get_keyed_setter_pos(p_keyed_setter));
+ }
+
+ void append(const Variant::ValidatedKeyedGetter p_keyed_getter) {
+ opcodes.push_back(get_keyed_getter_pos(p_keyed_getter));
+ }
+
+ void append(const Variant::ValidatedIndexedSetter p_indexed_setter) {
+ opcodes.push_back(get_indexed_setter_pos(p_indexed_setter));
+ }
+
+ void append(const Variant::ValidatedIndexedGetter p_indexed_getter) {
+ opcodes.push_back(get_indexed_getter_pos(p_indexed_getter));
+ }
+
+ void append(const Variant::ValidatedBuiltInMethod p_method) {
+ opcodes.push_back(get_builtin_method_pos(p_method));
+ }
+
+ void append(const Variant::ValidatedConstructor p_constructor) {
+ opcodes.push_back(get_constructor_pos(p_constructor));
+ }
+
+ void append(const Variant::ValidatedUtilityFunction p_utility) {
+ opcodes.push_back(get_utility_pos(p_utility));
+ }
+
+ void append(const GDScriptUtilityFunctions::FunctionPtr p_gds_utility) {
+ opcodes.push_back(get_gds_utility_pos(p_gds_utility));
+ }
+
+ void append(MethodBind *p_method) {
+ opcodes.push_back(get_method_bind_pos(p_method));
+ }
+
void patch_jump(int p_address) {
opcodes.write[p_address] = opcodes.size();
}
@@ -216,7 +406,8 @@ public:
#endif
virtual void set_initial_line(int p_line) override;
- virtual void write_operator(const Address &p_target, Variant::Operator p_operator, const Address &p_left_operand, const Address &p_right_operand) override;
+ virtual void write_unary_operator(const Address &p_target, Variant::Operator p_operator, const Address &p_left_operand) override;
+ virtual void write_binary_operator(const Address &p_target, Variant::Operator p_operator, const Address &p_left_operand, const Address &p_right_operand) override;
virtual void write_type_test(const Address &p_target, const Address &p_source, const Address &p_type) override;
virtual void write_type_test_builtin(const Address &p_target, const Address &p_source, Variant::Type p_type) override;
virtual void write_and_left_operand(const Address &p_left_operand) override;
@@ -239,23 +430,30 @@ public:
virtual void write_assign(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;
virtual void write_cast(const Address &p_target, const Address &p_source, const GDScriptDataType &p_type) override;
virtual void write_call(const Address &p_target, const Address &p_base, const StringName &p_function_name, const Vector<Address> &p_arguments) override;
virtual void write_super_call(const Address &p_target, const StringName &p_function_name, const Vector<Address> &p_arguments) override;
virtual void write_call_async(const Address &p_target, const Address &p_base, const StringName &p_function_name, const Vector<Address> &p_arguments) override;
- virtual void write_call_builtin(const Address &p_target, GDScriptFunctions::Function p_function, const Vector<Address> &p_arguments) override;
- virtual void write_call_method_bind(const Address &p_target, const Address &p_base, const MethodBind *p_method, const Vector<Address> &p_arguments) override;
- virtual void write_call_ptrcall(const Address &p_target, const Address &p_base, const MethodBind *p_method, const Vector<Address> &p_arguments) override;
+ 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_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_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;
virtual void write_construct_dictionary(const Address &p_target, const Vector<Address> &p_arguments) override;
virtual void write_await(const Address &p_target, const Address &p_operand) override;
virtual void write_if(const Address &p_condition) override;
virtual void write_else() override;
virtual void write_endif() override;
- virtual void write_for(const Address &p_variable, const Address &p_list) override;
+ virtual void start_for(const GDScriptDataType &p_iterator_type, const GDScriptDataType &p_list_type) override;
+ virtual void write_for_assignment(const Address &p_variable, const Address &p_list) override;
+ virtual void write_for() override;
virtual void write_endfor() override;
virtual void start_while_condition() override;
virtual void write_while(const Address &p_condition) override;
diff --git a/modules/gdscript/gdscript_cache.cpp b/modules/gdscript/gdscript_cache.cpp
index 95d24a8b08..113d36be98 100644
--- a/modules/gdscript/gdscript_cache.cpp
+++ b/modules/gdscript/gdscript_cache.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/modules/gdscript/gdscript_cache.h b/modules/gdscript/gdscript_cache.h
index 90c5884985..d1d2a2abbf 100644
--- a/modules/gdscript/gdscript_cache.h
+++ b/modules/gdscript/gdscript_cache.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/modules/gdscript/gdscript_codegen.h b/modules/gdscript/gdscript_codegen.h
index 9872a61423..3c05f14cf7 100644
--- a/modules/gdscript/gdscript_codegen.h
+++ b/modules/gdscript/gdscript_codegen.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -35,7 +35,7 @@
#include "core/string/string_name.h"
#include "core/variant/variant.h"
#include "gdscript_function.h"
-#include "gdscript_functions.h"
+#include "gdscript_utility_functions.h"
class GDScriptCodeGenerator {
public:
@@ -98,7 +98,8 @@ public:
// virtual void alloc_stack(int p_level) = 0; // Is this needed?
// virtual void alloc_call(int p_arg_count) = 0; // This might be automatic from other functions.
- virtual void write_operator(const Address &p_target, Variant::Operator p_operator, const Address &p_left_operand, const Address &p_right_operand) = 0;
+ virtual void write_unary_operator(const Address &p_target, Variant::Operator p_operator, const Address &p_left_operand) = 0;
+ virtual void write_binary_operator(const Address &p_target, Variant::Operator p_operator, const Address &p_left_operand, const Address &p_right_operand) = 0;
virtual void write_type_test(const Address &p_target, const Address &p_source, const Address &p_type) = 0;
virtual void write_type_test_builtin(const Address &p_target, const Address &p_source, Variant::Type p_type) = 0;
virtual void write_and_left_operand(const Address &p_left_operand) = 0;
@@ -121,24 +122,31 @@ public:
virtual void write_assign(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;
virtual void write_cast(const Address &p_target, const Address &p_source, const GDScriptDataType &p_type) = 0;
virtual void write_call(const Address &p_target, const Address &p_base, const StringName &p_function_name, const Vector<Address> &p_arguments) = 0;
virtual void write_super_call(const Address &p_target, const StringName &p_function_name, const Vector<Address> &p_arguments) = 0;
virtual void write_call_async(const Address &p_target, const Address &p_base, const StringName &p_function_name, const Vector<Address> &p_arguments) = 0;
- virtual void write_call_builtin(const Address &p_target, GDScriptFunctions::Function p_function, const Vector<Address> &p_arguments) = 0;
- virtual void write_call_method_bind(const Address &p_target, const Address &p_base, const MethodBind *p_method, const Vector<Address> &p_arguments) = 0;
- virtual void write_call_ptrcall(const Address &p_target, const Address &p_base, const MethodBind *p_method, const Vector<Address> &p_arguments) = 0;
+ 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_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_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;
virtual void write_construct_dictionary(const Address &p_target, const Vector<Address> &p_arguments) = 0;
virtual void write_await(const Address &p_target, const Address &p_operand) = 0;
virtual void write_if(const Address &p_condition) = 0;
// virtual void write_elseif(const Address &p_condition) = 0; This kind of makes things more difficult for no real benefit.
virtual void write_else() = 0;
virtual void write_endif() = 0;
- virtual void write_for(const Address &p_variable, const Address &p_list) = 0;
+ virtual void start_for(const GDScriptDataType &p_iterator_type, const GDScriptDataType &p_list_type) = 0;
+ virtual void write_for_assignment(const Address &p_variable, const Address &p_list) = 0;
+ virtual void write_for() = 0;
virtual void write_endfor() = 0;
virtual void start_while_condition() = 0; // Used to allow a jump to the expression evaluation.
virtual void write_while(const Address &p_condition) = 0;
diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp
index a64a05fcba..62bb6a9f9e 100644
--- a/modules/gdscript/gdscript_compiler.cpp
+++ b/modules/gdscript/gdscript_compiler.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -33,6 +33,7 @@
#include "gdscript.h"
#include "gdscript_byte_codegen.h"
#include "gdscript_cache.h"
+#include "gdscript_utility_functions.h"
bool GDScriptCompiler::_is_class_member_property(CodeGen &codegen, const StringName &p_name) {
if (codegen.function_node && codegen.function_node->is_static) {
@@ -98,14 +99,15 @@ GDScriptDataType GDScriptCompiler::_gdtype_from_datatype(const GDScriptParser::D
} break;
case GDScriptParser::DataType::SCRIPT: {
result.kind = GDScriptDataType::SCRIPT;
- result.script_type = Ref<Script>(p_datatype.script_type).ptr();
+ result.script_type_ref = Ref<Script>(p_datatype.script_type);
+ result.script_type = result.script_type_ref.ptr();
result.native_type = result.script_type->get_instance_base_type();
} break;
case GDScriptParser::DataType::CLASS: {
// Locate class by constructing the path to it and following that path
GDScriptParser::ClassNode *class_type = p_datatype.class_type;
if (class_type) {
- if (class_type->fqcn.begins_with(main_script->path) || (!main_script->name.empty() && class_type->fqcn.begins_with(main_script->name))) {
+ if (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) {
@@ -124,40 +126,84 @@ GDScriptDataType GDScriptCompiler::_gdtype_from_datatype(const GDScriptParser::D
names.pop_back();
}
result.kind = GDScriptDataType::GDSCRIPT;
- result.script_type = script.ptr();
+ result.script_type_ref = script;
+ result.script_type = result.script_type_ref.ptr();
result.native_type = script->get_instance_base_type();
} else {
result.kind = GDScriptDataType::GDSCRIPT;
- result.script_type = GDScriptCache::get_shallow_script(p_datatype.script_path, main_script->path).ptr();
+ result.script_type_ref = GDScriptCache::get_shallow_script(p_datatype.script_path, main_script->path);
+ result.script_type = result.script_type_ref.ptr();
result.native_type = p_datatype.native_type;
}
}
} break;
+ case GDScriptParser::DataType::ENUM:
case GDScriptParser::DataType::ENUM_VALUE:
result.has_type = true;
result.kind = GDScriptDataType::BUILTIN;
result.builtin_type = Variant::INT;
break;
- case GDScriptParser::DataType::ENUM:
- result.has_type = true;
- result.kind = GDScriptDataType::BUILTIN;
- result.builtin_type = Variant::DICTIONARY;
- break;
case GDScriptParser::DataType::UNRESOLVED: {
ERR_PRINT("Parser bug: converting unresolved type.");
return GDScriptDataType();
}
}
+ if (p_datatype.has_container_element_type()) {
+ result.set_container_element_type(_gdtype_from_datatype(p_datatype.get_container_element_type()));
+ }
+
// Only hold strong reference to the script if it's not the owner of the
// element qualified with this type, to avoid cyclic references (leaks).
- if (result.script_type && result.script_type != p_owner) {
- result.script_type_ref = Ref<Script>(result.script_type);
+ if (result.script_type && result.script_type == p_owner) {
+ result.script_type_ref = Ref<Script>();
}
return result;
}
+static bool _is_exact_type(const PropertyInfo &p_par_type, const GDScriptDataType &p_arg_type) {
+ if (!p_arg_type.has_type) {
+ return false;
+ }
+ if (p_par_type.type == Variant::NIL) {
+ return false;
+ }
+ if (p_par_type.type == Variant::OBJECT) {
+ if (p_arg_type.kind == GDScriptDataType::BUILTIN) {
+ return false;
+ }
+ StringName class_name;
+ if (p_arg_type.kind == GDScriptDataType::NATIVE) {
+ class_name = p_arg_type.native_type;
+ } else {
+ class_name = p_arg_type.native_type == StringName() ? p_arg_type.script_type->get_instance_base_type() : p_arg_type.native_type;
+ }
+ return p_par_type.class_name == class_name || ClassDB::is_parent_class(class_name, p_par_type.class_name);
+ } else {
+ if (p_arg_type.kind != GDScriptDataType::BUILTIN) {
+ return false;
+ }
+ return p_par_type.type == p_arg_type.builtin_type;
+ }
+}
+
+static bool _have_exact_arguments(const MethodBind *p_method, const Vector<GDScriptCodeGenerator::Address> &p_arguments) {
+ if (p_method->get_argument_count() != p_arguments.size()) {
+ // ptrcall won't work with default arguments.
+ return false;
+ }
+ MethodInfo info;
+ ClassDB::get_method_info(p_method->get_instance_class(), p_method->get_name(), &info);
+ for (int i = 0; i < p_arguments.size(); i++) {
+ const PropertyInfo &prop = info.arguments[i];
+ if (!_is_exact_type(prop, p_arguments[i].type)) {
+ return false;
+ }
+ }
+ return true;
+}
+
GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &codegen, Error &r_error, const GDScriptParser::ExpressionNode *p_expression, bool p_root, bool p_initializer, const GDScriptCodeGenerator::Address &p_index_addr) {
if (p_expression->is_constant) {
return codegen.add_constant(p_expression->reduced_value);
@@ -209,36 +255,59 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
}
// Try class constants.
- GDScript *owner = codegen.script;
- while (owner) {
- GDScript *scr = owner;
- GDScriptNativeClass *nc = nullptr;
- while (scr) {
- if (scr->constants.has(identifier)) {
- return GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::CLASS_CONSTANT, gen->add_or_get_name(identifier)); // TODO: Get type here.
+ {
+ GDScript *owner = codegen.script;
+ while (owner) {
+ GDScript *scr = owner;
+ GDScriptNativeClass *nc = nullptr;
+ while (scr) {
+ if (scr->constants.has(identifier)) {
+ return GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::CLASS_CONSTANT, gen->add_or_get_name(identifier)); // TODO: Get type here.
+ }
+ if (scr->native.is_valid()) {
+ nc = scr->native.ptr();
+ }
+ scr = scr->_base;
}
- if (scr->native.is_valid()) {
- nc = scr->native.ptr();
+
+ // Class C++ integer constant.
+ if (nc) {
+ bool success = false;
+ int constant = ClassDB::get_integer_constant(nc->get_name(), identifier, &success);
+ if (success) {
+ return codegen.add_constant(constant);
+ }
}
- scr = scr->_base;
+
+ owner = owner->_owner;
}
+ }
+
+ // Try signals and methods (can be made callables).
+ {
+ if (codegen.class_node->members_indices.has(identifier)) {
+ const GDScriptParser::ClassNode::Member &member = codegen.class_node->members[codegen.class_node->members_indices[identifier]];
+ if (member.type == GDScriptParser::ClassNode::Member::FUNCTION || member.type == GDScriptParser::ClassNode::Member::SIGNAL) {
+ // Get like it was a property.
+ GDScriptCodeGenerator::Address temp = codegen.add_temporary(); // TODO: Get type here.
+ GDScriptCodeGenerator::Address self(GDScriptCodeGenerator::Address::SELF);
- // Class C++ integer constant.
- if (nc) {
- bool success = false;
- int constant = ClassDB::get_integer_constant(nc->get_name(), identifier, &success);
- if (success) {
- return codegen.add_constant(constant);
+ gen->write_get_named(temp, identifier, self);
+ return temp;
}
}
- owner = owner->_owner;
- }
+ // Try in native base.
+ GDScript *scr = codegen.script;
+ GDScriptNativeClass *nc = nullptr;
+ while (scr) {
+ if (scr->native.is_valid()) {
+ nc = scr->native.ptr();
+ }
+ scr = scr->_base;
+ }
- // Try signals and methods (can be made callables);
- if (codegen.class_node->members_indices.has(identifier)) {
- const GDScriptParser::ClassNode::Member &member = codegen.class_node->members[codegen.class_node->members_indices[identifier]];
- if (member.type == GDScriptParser::ClassNode::Member::FUNCTION || member.type == GDScriptParser::ClassNode::Member::SIGNAL) {
+ if (nc && (ClassDB::has_signal(nc->get_name(), identifier) || ClassDB::has_method(nc->get_name(), identifier))) {
// Get like it was a property.
GDScriptCodeGenerator::Address temp = codegen.add_temporary(); // TODO: Get type here.
GDScriptCodeGenerator::Address self(GDScriptCodeGenerator::Address::SELF);
@@ -307,10 +376,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
Vector<GDScriptCodeGenerator::Address> values;
// Create the result temporary first since it's the last to be killed.
- GDScriptDataType array_type;
- array_type.has_type = true;
- array_type.kind = GDScriptDataType::BUILTIN;
- array_type.builtin_type = Variant::ARRAY;
+ GDScriptDataType array_type = _gdtype_from_datatype(an->get_datatype());
GDScriptCodeGenerator::Address result = codegen.add_temporary(array_type);
for (int i = 0; i < an->elements.size(); i++) {
@@ -321,7 +387,11 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
values.push_back(val);
}
- gen->write_construct_array(result, values);
+ if (array_type.has_container_element_type()) {
+ gen->write_construct_typed_array(result, array_type.get_container_element_type(), values);
+ } else {
+ gen->write_construct_array(result, values);
+ }
for (int i = 0; i < values.size(); i++) {
if (values[i].mode == GDScriptCodeGenerator::Address::TEMPORARY) {
@@ -395,7 +465,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
gen->pop_temporary();
}
- return source;
+ return result;
} break;
case GDScriptParser::Node::CALL: {
const GDScriptParser::CallNode *call = static_cast<const GDScriptParser::CallNode *>(p_expression);
@@ -411,15 +481,17 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
arguments.push_back(arg);
}
- if (!call->is_super && call->callee->type == GDScriptParser::Node::IDENTIFIER && GDScriptParser::get_builtin_type(static_cast<GDScriptParser::IdentifierNode *>(call->callee)->name) != Variant::VARIANT_MAX) {
+ if (!call->is_super && call->callee->type == GDScriptParser::Node::IDENTIFIER && GDScriptParser::get_builtin_type(call->function_name) != Variant::VARIANT_MAX) {
// Construct a built-in type.
Variant::Type vtype = GDScriptParser::get_builtin_type(static_cast<GDScriptParser::IdentifierNode *>(call->callee)->name);
gen->write_construct(result, vtype, arguments);
- } else if (!call->is_super && call->callee->type == GDScriptParser::Node::IDENTIFIER && GDScriptParser::get_builtin_function(static_cast<GDScriptParser::IdentifierNode *>(call->callee)->name) != GDScriptFunctions::FUNC_MAX) {
- // Built-in function.
- GDScriptFunctions::Function func = GDScriptParser::get_builtin_function(static_cast<GDScriptParser::IdentifierNode *>(call->callee)->name);
- gen->write_call_builtin(result, func, arguments);
+ } else if (!call->is_super && call->callee->type == GDScriptParser::Node::IDENTIFIER && Variant::has_utility_function(call->function_name)) {
+ // Variant utility function.
+ gen->write_call_utility(result, call->function_name, arguments);
+ } else if (!call->is_super && call->callee->type == GDScriptParser::Node::IDENTIFIER && GDScriptUtilityFunctions::function_exists(call->function_name)) {
+ // GDScript utility function.
+ gen->write_call_gdscript_utility(result, GDScriptUtilityFunctions::get_function(call->function_name), arguments);
} else {
// Regular function.
const GDScriptParser::ExpressionNode *callee = call->callee;
@@ -430,12 +502,33 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
} else {
if (callee->type == GDScriptParser::Node::IDENTIFIER) {
// Self function call.
- if ((codegen.function_node && codegen.function_node->is_static) || call->function_name == "new") {
+ if (ClassDB::has_method(codegen.script->native->get_name(), call->function_name)) {
+ // Native method, use faster path.
+ GDScriptCodeGenerator::Address self;
+ self.mode = GDScriptCodeGenerator::Address::SELF;
+ MethodBind *method = ClassDB::get_method(codegen.script->native->get_name(), call->function_name);
+
+ if (_have_exact_arguments(method, arguments)) {
+ // Exact arguments, use ptrcall.
+ gen->write_call_ptrcall(result, self, method, arguments);
+ } else {
+ // Not exact arguments, but still can use method bind call.
+ gen->write_call_method_bind(result, self, method, arguments);
+ }
+ } else if ((codegen.function_node && codegen.function_node->is_static) || call->function_name == "new") {
GDScriptCodeGenerator::Address self;
self.mode = GDScriptCodeGenerator::Address::CLASS;
- gen->write_call(result, self, call->function_name, arguments);
+ if (within_await) {
+ gen->write_call_async(result, self, call->function_name, arguments);
+ } else {
+ gen->write_call(result, self, call->function_name, arguments);
+ }
} else {
- gen->write_call_self(result, call->function_name, arguments);
+ if (within_await) {
+ gen->write_call_self_async(result, call->function_name, arguments);
+ } else {
+ gen->write_call_self(result, call->function_name, arguments);
+ }
}
} else if (callee->type == GDScriptParser::Node::SUBSCRIPT) {
const GDScriptParser::SubscriptNode *subscript = static_cast<const GDScriptParser::SubscriptNode *>(call->callee);
@@ -447,6 +540,28 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
}
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;
+ }
+ 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);
}
@@ -493,7 +608,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
GDScriptCodeGenerator::Address result = codegen.add_temporary(_gdtype_from_datatype(get_node->get_datatype()));
MethodBind *get_node_method = ClassDB::get_method("Node", "get_node");
- gen->write_call_method_bind(result, GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::SELF), get_node_method, args);
+ gen->write_call_ptrcall(result, GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::SELF), get_node_method, args);
return result;
} break;
@@ -600,7 +715,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
return GDScriptCodeGenerator::Address();
}
- gen->write_operator(result, unary->variant_op, operand, GDScriptCodeGenerator::Address());
+ gen->write_unary_operator(result, unary->variant_op, operand);
if (operand.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
gen->pop_temporary();
@@ -668,7 +783,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
GDScriptCodeGenerator::Address left_operand = _parse_expression(codegen, r_error, binary->left_operand);
GDScriptCodeGenerator::Address right_operand = _parse_expression(codegen, r_error, binary->right_operand);
- gen->write_operator(result, binary->variant_op, left_operand, right_operand);
+ gen->write_binary_operator(result, binary->variant_op, left_operand, right_operand);
if (right_operand.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
gen->pop_temporary();
@@ -831,7 +946,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
} else {
gen->write_get(value, key, prev_base);
}
- gen->write_operator(value, assignment->variant_op, value, assigned);
+ gen->write_binary_operator(value, assignment->variant_op, value, assigned);
if (assigned.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
gen->pop_temporary();
}
@@ -889,7 +1004,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
if (assignment->operation != GDScriptParser::AssignmentNode::OP_NONE) {
GDScriptCodeGenerator::Address member = codegen.add_temporary();
gen->write_get_member(member, name);
- gen->write_operator(assigned, assignment->variant_op, member, assigned);
+ gen->write_binary_operator(assigned, assignment->variant_op, member, assigned);
gen->pop_temporary();
}
@@ -939,7 +1054,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
if (assignment->operation != GDScriptParser::AssignmentNode::OP_NONE) {
// Perform operation.
op_result = codegen.add_temporary();
- gen->write_operator(op_result, assignment->variant_op, target, assigned);
+ gen->write_binary_operator(op_result, assignment->variant_op, target, assigned);
} else {
op_result = assigned;
assigned = GDScriptCodeGenerator::Address();
@@ -995,7 +1110,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_match_pattern(CodeGen &c
// Check type equality.
GDScriptCodeGenerator::Address type_equality_addr = codegen.add_temporary(equality_type);
- codegen.generator->write_operator(type_equality_addr, Variant::OP_EQUAL, p_type_addr, literal_type_addr);
+ codegen.generator->write_binary_operator(type_equality_addr, Variant::OP_EQUAL, p_type_addr, literal_type_addr);
codegen.generator->write_and_left_operand(type_equality_addr);
// Get literal.
@@ -1006,7 +1121,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_match_pattern(CodeGen &c
// Check value equality.
GDScriptCodeGenerator::Address equality_addr = codegen.add_temporary(equality_type);
- codegen.generator->write_operator(equality_addr, Variant::OP_EQUAL, p_value_addr, literal_addr);
+ codegen.generator->write_binary_operator(equality_addr, Variant::OP_EQUAL, p_value_addr, literal_addr);
codegen.generator->write_and_right_operand(equality_addr);
// AND both together (reuse temporary location).
@@ -1055,14 +1170,14 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_match_pattern(CodeGen &c
// Evaluate expression type.
Vector<GDScriptCodeGenerator::Address> typeof_args;
typeof_args.push_back(expr_addr);
- codegen.generator->write_call_builtin(result_addr, GDScriptFunctions::TYPE_OF, typeof_args);
+ codegen.generator->write_call_utility(result_addr, "typeof", typeof_args);
// Check type equality.
- codegen.generator->write_operator(result_addr, Variant::OP_EQUAL, p_type_addr, result_addr);
+ codegen.generator->write_binary_operator(result_addr, Variant::OP_EQUAL, p_type_addr, result_addr);
codegen.generator->write_and_left_operand(result_addr);
// Check value equality.
- codegen.generator->write_operator(result_addr, Variant::OP_EQUAL, p_value_addr, expr_addr);
+ codegen.generator->write_binary_operator(equality_test_addr, Variant::OP_EQUAL, p_value_addr, expr_addr);
codegen.generator->write_and_right_operand(equality_test_addr);
// AND both type and value equality.
@@ -1108,7 +1223,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_match_pattern(CodeGen &c
// Check type equality.
GDScriptCodeGenerator::Address result_addr = codegen.add_temporary(temp_type);
- codegen.generator->write_operator(result_addr, Variant::OP_EQUAL, p_type_addr, array_type_addr);
+ codegen.generator->write_binary_operator(result_addr, Variant::OP_EQUAL, p_type_addr, array_type_addr);
codegen.generator->write_and_left_operand(result_addr);
// Store pattern length in constant map.
@@ -1119,12 +1234,12 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_match_pattern(CodeGen &c
GDScriptCodeGenerator::Address value_length_addr = codegen.add_temporary(temp_type);
Vector<GDScriptCodeGenerator::Address> len_args;
len_args.push_back(p_value_addr);
- codegen.generator->write_call_builtin(value_length_addr, GDScriptFunctions::LEN, len_args);
+ codegen.generator->write_call_gdscript_utility(value_length_addr, GDScriptUtilityFunctions::get_function("len"), len_args);
// Test length compatibility.
temp_type.builtin_type = Variant::BOOL;
GDScriptCodeGenerator::Address length_compat_addr = codegen.add_temporary(temp_type);
- codegen.generator->write_operator(length_compat_addr, p_pattern->rest_used ? Variant::OP_GREATER_EQUAL : Variant::OP_EQUAL, value_length_addr, array_length_addr);
+ codegen.generator->write_binary_operator(length_compat_addr, p_pattern->rest_used ? Variant::OP_GREATER_EQUAL : Variant::OP_EQUAL, value_length_addr, array_length_addr);
codegen.generator->write_and_right_operand(length_compat_addr);
// AND type and length check.
@@ -1173,7 +1288,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_match_pattern(CodeGen &c
// Also get type of element.
Vector<GDScriptCodeGenerator::Address> typeof_args;
typeof_args.push_back(element_addr);
- codegen.generator->write_call_builtin(element_type_addr, GDScriptFunctions::TYPE_OF, typeof_args);
+ codegen.generator->write_call_utility(element_type_addr, "typeof", typeof_args);
// Try the pattern inside the element.
test_addr = _parse_match_pattern(codegen, r_error, p_pattern->array[i], element_addr, element_type_addr, p_previous_test, false, true);
@@ -1207,7 +1322,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_match_pattern(CodeGen &c
// Check type equality.
GDScriptCodeGenerator::Address result_addr = codegen.add_temporary(temp_type);
- codegen.generator->write_operator(result_addr, Variant::OP_EQUAL, p_type_addr, dict_type_addr);
+ codegen.generator->write_binary_operator(result_addr, Variant::OP_EQUAL, p_type_addr, dict_type_addr);
codegen.generator->write_and_left_operand(result_addr);
// Store pattern length in constant map.
@@ -1218,12 +1333,12 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_match_pattern(CodeGen &c
GDScriptCodeGenerator::Address value_length_addr = codegen.add_temporary(temp_type);
Vector<GDScriptCodeGenerator::Address> func_args;
func_args.push_back(p_value_addr);
- codegen.generator->write_call_builtin(value_length_addr, GDScriptFunctions::LEN, func_args);
+ codegen.generator->write_call_gdscript_utility(value_length_addr, GDScriptUtilityFunctions::get_function("len"), func_args);
// Test length compatibility.
temp_type.builtin_type = Variant::BOOL;
GDScriptCodeGenerator::Address length_compat_addr = codegen.add_temporary(temp_type);
- codegen.generator->write_operator(length_compat_addr, p_pattern->rest_used ? Variant::OP_GREATER_EQUAL : Variant::OP_EQUAL, value_length_addr, dict_length_addr);
+ codegen.generator->write_binary_operator(length_compat_addr, p_pattern->rest_used ? Variant::OP_GREATER_EQUAL : Variant::OP_EQUAL, value_length_addr, dict_length_addr);
codegen.generator->write_and_right_operand(length_compat_addr);
// AND type and length check.
@@ -1287,7 +1402,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_match_pattern(CodeGen &c
// Also get type of value.
func_args.clear();
func_args.push_back(element_addr);
- codegen.generator->write_call_builtin(element_type_addr, GDScriptFunctions::TYPE_OF, func_args);
+ codegen.generator->write_call_utility(element_type_addr, "typeof", func_args);
// Try the pattern inside the value.
test_addr = _parse_match_pattern(codegen, r_error, element.value_pattern, element_addr, element_type_addr, test_addr, false, true);
@@ -1397,16 +1512,30 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Sui
codegen.start_block();
// Evaluate the match expression.
- GDScriptCodeGenerator::Address value = _parse_expression(codegen, error, match->test);
+ GDScriptCodeGenerator::Address value = codegen.add_local("@match_value", _gdtype_from_datatype(match->test->get_datatype()));
+ GDScriptCodeGenerator::Address value_expr = _parse_expression(codegen, error, match->test);
if (error) {
return error;
}
+ // Assign to local.
+ // TODO: This can be improved by passing the target to parse_expression().
+ gen->write_assign(value, value_expr);
+
+ if (value_expr.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
+ codegen.generator->pop_temporary();
+ }
+
// Then, let's save the type of the value in the stack too, so we can reuse for later comparisons.
- GDScriptCodeGenerator::Address type = codegen.add_temporary();
+ GDScriptDataType typeof_type;
+ typeof_type.has_type = true;
+ typeof_type.kind = GDScriptDataType::BUILTIN;
+ typeof_type.builtin_type = Variant::INT;
+ GDScriptCodeGenerator::Address type = codegen.add_local("@match_type", typeof_type);
+
Vector<GDScriptCodeGenerator::Address> typeof_args;
typeof_args.push_back(value);
- gen->write_call_builtin(type, GDScriptFunctions::TYPE_OF, typeof_args);
+ gen->write_call_utility(type, "typeof", typeof_args);
// Now we can actually start testing.
// For each branch.
@@ -1457,12 +1586,6 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Sui
gen->write_endif();
}
- gen->pop_temporary();
-
- if (value.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
- codegen.generator->pop_temporary();
- }
-
gen->end_match();
} break;
case GDScriptParser::Node::IF: {
@@ -1500,12 +1623,20 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Sui
codegen.start_block();
GDScriptCodeGenerator::Address iterator = codegen.add_local(for_n->variable->name, _gdtype_from_datatype(for_n->variable->get_datatype()));
+ gen->start_for(iterator.type, _gdtype_from_datatype(for_n->list->get_datatype()));
+
GDScriptCodeGenerator::Address list = _parse_expression(codegen, error, for_n->list);
if (error) {
return error;
}
- gen->write_for(iterator, list);
+ gen->write_for_assignment(iterator, list);
+
+ if (list.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
+ codegen.generator->pop_temporary();
+ }
+
+ gen->write_for();
error = _parse_block(codegen, for_n->loop);
if (error) {
@@ -1514,10 +1645,6 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Sui
gen->write_endfor();
- if (list.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
- codegen.generator->pop_temporary();
- }
-
codegen.end_block();
} break;
case GDScriptParser::Node::WHILE: {
@@ -1607,8 +1734,17 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Sui
const GDScriptParser::VariableNode *lv = static_cast<const GDScriptParser::VariableNode *>(s);
// Should be already in stack when the block began.
GDScriptCodeGenerator::Address local = codegen.locals[lv->identifier->name];
+ GDScriptParser::DataType local_type = lv->get_datatype();
if (lv->initializer != nullptr) {
+ // For typed arrays we need to make sure this is already initialized correctly so typed assignment work.
+ if (local_type.is_hard_type() && local_type.builtin_type == Variant::ARRAY) {
+ if (local_type.has_container_element_type()) {
+ codegen.generator->write_construct_typed_array(local, _gdtype_from_datatype(local_type.get_container_element_type(), codegen.script), Vector<GDScriptCodeGenerator::Address>());
+ } else {
+ codegen.generator->write_construct_array(local, Vector<GDScriptCodeGenerator::Address>());
+ }
+ }
GDScriptCodeGenerator::Address src_address = _parse_expression(codegen, error, lv->initializer);
if (error) {
return error;
@@ -1617,6 +1753,14 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Sui
if (src_address.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
codegen.generator->pop_temporary();
}
+ } else if (lv->get_datatype().is_hard_type()) {
+ // Initialize with default for type.
+ if (local_type.has_container_element_type()) {
+ codegen.generator->write_construct_typed_array(local, _gdtype_from_datatype(local_type.get_container_element_type(), codegen.script), Vector<GDScriptCodeGenerator::Address>());
+ } else if (local_type.kind == GDScriptParser::DataType::BUILTIN) {
+ codegen.generator->write_construct(local, local_type.builtin_type, Vector<GDScriptCodeGenerator::Address>());
+ }
+ // The `else` branch is for objects, in such case we leave it as `null`.
}
} break;
case GDScriptParser::Node::CONSTANT: {
@@ -1718,21 +1862,41 @@ Error GDScriptCompiler::_parse_function(GDScript *p_script, const GDScriptParser
continue;
}
+ GDScriptParser::DataType field_type = field->get_datatype();
+
+ GDScriptCodeGenerator::Address dst_address(GDScriptCodeGenerator::Address::MEMBER, codegen.script->member_indices[field->identifier->name].index, _gdtype_from_datatype(field->get_datatype()));
if (field->initializer) {
// Emit proper line change.
codegen.generator->write_newline(field->initializer->start_line);
+ // For typed arrays we need to make sure this is already initialized correctly so typed assignment work.
+ if (field_type.is_hard_type() && field_type.builtin_type == Variant::ARRAY && field_type.has_container_element_type()) {
+ if (field_type.has_container_element_type()) {
+ codegen.generator->write_construct_typed_array(dst_address, _gdtype_from_datatype(field_type.get_container_element_type(), codegen.script), Vector<GDScriptCodeGenerator::Address>());
+ } else {
+ codegen.generator->write_construct_array(dst_address, Vector<GDScriptCodeGenerator::Address>());
+ }
+ }
GDScriptCodeGenerator::Address src_address = _parse_expression(codegen, error, field->initializer, false, true);
if (error) {
memdelete(codegen.generator);
return error;
}
- GDScriptCodeGenerator::Address dst_address(GDScriptCodeGenerator::Address::MEMBER, codegen.script->member_indices[field->identifier->name].index, _gdtype_from_datatype(field->get_datatype()));
codegen.generator->write_assign(dst_address, src_address);
if (src_address.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
codegen.generator->pop_temporary();
}
+ } else if (field->get_datatype().is_hard_type()) {
+ codegen.generator->write_newline(field->start_line);
+
+ // Initialize with default for type.
+ if (field_type.has_container_element_type()) {
+ codegen.generator->write_construct_typed_array(dst_address, _gdtype_from_datatype(field_type.get_container_element_type(), codegen.script), Vector<GDScriptCodeGenerator::Address>());
+ } else if (field_type.kind == GDScriptParser::DataType::BUILTIN) {
+ codegen.generator->write_construct(dst_address, field_type.builtin_type, Vector<GDScriptCodeGenerator::Address>());
+ }
+ // The `else` branch is for objects, in such case we leave it as `null`.
}
}
}
@@ -1749,7 +1913,7 @@ Error GDScriptCompiler::_parse_function(GDScript *p_script, const GDScriptParser
return error;
}
GDScriptCodeGenerator::Address dst_addr = codegen.parameters[parameter->identifier->name];
- codegen.generator->write_assign(dst_addr, src_addr);
+ codegen.generator->write_assign_default_parameter(dst_addr, src_addr);
if (src_addr.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
codegen.generator->pop_temporary();
}
@@ -1794,6 +1958,7 @@ Error GDScriptCompiler::_parse_function(GDScript *p_script, const GDScriptParser
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;
#endif
} else {
codegen.generator->set_initial_line(0);
@@ -1807,6 +1972,21 @@ Error GDScriptCompiler::_parse_function(GDScript *p_script, const GDScriptParser
p_script->implicit_initializer = gd_function;
}
+ if (p_func) {
+ // if no return statement -> return type is void not unresolved Variant
+ if (p_func->body->has_return) {
+ gd_function->return_type = _gdtype_from_datatype(p_func->get_datatype());
+ } else {
+ gd_function->return_type = GDScriptDataType();
+ gd_function->return_type.has_type = true;
+ gd_function->return_type.kind = GDScriptDataType::BUILTIN;
+ gd_function->return_type.builtin_type = Variant::NIL;
+ }
+#ifdef TOOLS_ENABLED
+ gd_function->default_arg_values = p_func->default_arg_values;
+#endif
+ }
+
p_script->member_functions[func_name] = gd_function;
memdelete(codegen.generator);
@@ -1904,6 +2084,24 @@ Error GDScriptCompiler::_parse_class_level(GDScript *p_script, const GDScriptPar
}
}
+#ifdef TOOLS_ENABLED
+ p_script->doc_functions.clear();
+ p_script->doc_variables.clear();
+ p_script->doc_constants.clear();
+ p_script->doc_enums.clear();
+ p_script->doc_signals.clear();
+ p_script->doc_tutorials.clear();
+
+ p_script->doc_brief_description = p_class->doc_brief_description;
+ p_script->doc_description = p_class->doc_description;
+ for (int i = 0; i < p_class->doc_tutorials.size(); i++) {
+ DocData::TutorialDoc td;
+ td.title = p_class->doc_tutorials[i].first;
+ td.link = p_class->doc_tutorials[i].second;
+ p_script->doc_tutorials.append(td);
+ }
+#endif
+
p_script->native = Ref<GDScriptNativeClass>();
p_script->base = Ref<GDScript>();
p_script->_base = nullptr;
@@ -2016,20 +2214,22 @@ 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 |= PROPERTY_USAGE_SCRIPT_VARIABLE;
#ifdef TOOLS_ENABLED
- if (variable->initializer != nullptr && variable->initializer->type == GDScriptParser::Node::LITERAL) {
- p_script->member_default_values[name] = static_cast<const GDScriptParser::LiteralNode *>(variable->initializer)->value;
- }
+ p_script->doc_variables[name] = variable->doc_description;
#endif
- } else {
- prop_info.usage = PROPERTY_USAGE_SCRIPT_VARIABLE;
- }
p_script->member_info[name] = prop_info;
p_script->member_indices[name] = minfo;
p_script->members.insert(name);
#ifdef TOOLS_ENABLED
+ if (variable->initializer != nullptr && variable->initializer->is_constant) {
+ p_script->member_default_values[name] = variable->initializer->reduced_value;
+ } else {
+ p_script->member_default_values.erase(name);
+ }
p_script->member_lines[name] = variable->start_line;
#endif
} break;
@@ -2040,8 +2240,10 @@ Error GDScriptCompiler::_parse_class_level(GDScript *p_script, const GDScriptPar
p_script->constants.insert(name, constant->initializer->reduced_value);
#ifdef TOOLS_ENABLED
-
p_script->member_lines[name] = constant->start_line;
+ if (constant->doc_description != String()) {
+ p_script->doc_constants[name] = constant->doc_description;
+ }
#endif
} break;
@@ -2052,6 +2254,15 @@ Error GDScriptCompiler::_parse_class_level(GDScript *p_script, const GDScriptPar
p_script->constants.insert(name, enum_value.value);
#ifdef TOOLS_ENABLED
p_script->member_lines[name] = enum_value.identifier->start_line;
+ if (!p_script->doc_enums.has("@unnamed_enums")) {
+ p_script->doc_enums["@unnamed_enums"] = DocData::EnumDoc();
+ p_script->doc_enums["@unnamed_enums"].name = "@unnamed_enums";
+ }
+ DocData::ConstantDoc const_doc;
+ const_doc.name = enum_value.identifier->name;
+ const_doc.value = Variant(enum_value.value).operator String(); // TODO-DOC: enum value currently is int.
+ const_doc.description = enum_value.doc_description;
+ p_script->doc_enums["@unnamed_enums"].values.push_back(const_doc);
#endif
} break;
@@ -2087,6 +2298,11 @@ Error GDScriptCompiler::_parse_class_level(GDScript *p_script, const GDScriptPar
parameters_names.write[j] = signal->parameters[j]->identifier->name;
}
p_script->_signals[name] = parameters_names;
+#ifdef TOOLS_ENABLED
+ if (!signal->doc_description.is_empty()) {
+ p_script->doc_signals[name] = signal->doc_description;
+ }
+#endif
} break;
case GDScriptParser::ClassNode::Member::ENUM: {
@@ -2103,6 +2319,16 @@ Error GDScriptCompiler::_parse_class_level(GDScript *p_script, const GDScriptPar
p_script->constants.insert(enum_n->identifier->name, new_enum);
#ifdef TOOLS_ENABLED
p_script->member_lines[enum_n->identifier->name] = enum_n->start_line;
+ p_script->doc_enums[enum_n->identifier->name] = DocData::EnumDoc();
+ p_script->doc_enums[enum_n->identifier->name].name = enum_n->identifier->name;
+ p_script->doc_enums[enum_n->identifier->name].description = enum_n->doc_description;
+ for (int j = 0; j < enum_n->values.size(); j++) {
+ DocData::ConstantDoc const_doc;
+ const_doc.name = enum_n->values[j].identifier->name;
+ const_doc.value = Variant(enum_n->values[j].value).operator String();
+ const_doc.description = enum_n->values[j].doc_description;
+ p_script->doc_enums[enum_n->identifier->name].values.push_back(const_doc);
+ }
#endif
} break;
default:
@@ -2230,7 +2456,7 @@ Error GDScriptCompiler::_parse_class_blocks(GDScript *p_script, const GDScriptPa
p_script->initializer->call(instance, nullptr, 0, ce);
if (ce.error != Callable::CallError::CALL_OK) {
- //well, tough luck, not goinna do anything here
+ //well, tough luck, not gonna do anything here
}
}
#endif
diff --git a/modules/gdscript/gdscript_compiler.h b/modules/gdscript/gdscript_compiler.h
index 157c801f56..651391f972 100644
--- a/modules/gdscript/gdscript_compiler.h
+++ b/modules/gdscript/gdscript_compiler.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -133,8 +133,8 @@ class GDScriptCompiler {
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);
void _make_scripts(GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state);
- int err_line;
- int err_column;
+ int err_line = 0;
+ int err_column = 0;
StringName source;
String error;
bool within_await = false;
diff --git a/modules/gdscript/gdscript_disassembler.cpp b/modules/gdscript/gdscript_disassembler.cpp
new file mode 100644
index 0000000000..32adca29ed
--- /dev/null
+++ b/modules/gdscript/gdscript_disassembler.cpp
@@ -0,0 +1,883 @@
+/*************************************************************************/
+/* gdscript_disassembler.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. */
+/*************************************************************************/
+
+#ifdef DEBUG_ENABLED
+
+#include "gdscript_function.h"
+
+#include "core/string/string_builder.h"
+#include "gdscript.h"
+
+static String _get_variant_string(const Variant &p_variant) {
+ String txt;
+ if (p_variant.get_type() == Variant::STRING) {
+ txt = "\"" + String(p_variant) + "\"";
+ } else if (p_variant.get_type() == Variant::STRING_NAME) {
+ txt = "&\"" + String(p_variant) + "\"";
+ } else if (p_variant.get_type() == Variant::NODE_PATH) {
+ txt = "^\"" + String(p_variant) + "\"";
+ } else if (p_variant.get_type() == Variant::OBJECT) {
+ Object *obj = p_variant;
+ if (!obj) {
+ txt = "null";
+ } else {
+ GDScriptNativeClass *cls = Object::cast_to<GDScriptNativeClass>(obj);
+ if (cls) {
+ txt += cls->get_name();
+ txt += " (class)";
+ } else {
+ txt = obj->get_class();
+ if (obj->get_script_instance()) {
+ txt += "(" + obj->get_script_instance()->get_script()->get_path() + ")";
+ }
+ }
+ }
+ } else {
+ txt = p_variant;
+ }
+ return txt;
+}
+
+static String _disassemble_address(const GDScript *p_script, const GDScriptFunction &p_function, int p_address) {
+ int addr = p_address & GDScriptFunction::ADDR_MASK;
+
+ switch (p_address >> GDScriptFunction::ADDR_BITS) {
+ case GDScriptFunction::ADDR_TYPE_SELF: {
+ return "self";
+ } break;
+ case GDScriptFunction::ADDR_TYPE_CLASS: {
+ return "class";
+ } break;
+ case GDScriptFunction::ADDR_TYPE_MEMBER: {
+ return "member(" + p_script->debug_get_member_by_index(addr) + ")";
+ } break;
+ case GDScriptFunction::ADDR_TYPE_CLASS_CONSTANT: {
+ return "class_const(" + p_function.get_global_name(addr) + ")";
+ } break;
+ case GDScriptFunction::ADDR_TYPE_LOCAL_CONSTANT: {
+ return "const(" + _get_variant_string(p_function.get_constant(addr)) + ")";
+ } break;
+ case GDScriptFunction::ADDR_TYPE_STACK: {
+ return "stack(" + itos(addr) + ")";
+ } break;
+ case GDScriptFunction::ADDR_TYPE_STACK_VARIABLE: {
+ return "var_stack(" + itos(addr) + ")";
+ } break;
+ case GDScriptFunction::ADDR_TYPE_GLOBAL: {
+ return "global(" + _get_variant_string(GDScriptLanguage::get_singleton()->get_global_array()[addr]) + ")";
+ } break;
+ case GDScriptFunction::ADDR_TYPE_NAMED_GLOBAL: {
+ return "named_global(" + p_function.get_global_name(addr) + ")";
+ } break;
+ case GDScriptFunction::ADDR_TYPE_NIL: {
+ return "nil";
+ } break;
+ }
+
+ return "<err>";
+}
+
+void GDScriptFunction::disassemble(const Vector<String> &p_code_lines) const {
+#define DADDR(m_ip) (_disassemble_address(_script, *this, _code_ptr[ip + m_ip]))
+
+ for (int ip = 0; ip < _code_size;) {
+ StringBuilder text;
+ int incr = 0;
+
+ text += " ";
+ text += itos(ip);
+ text += ": ";
+
+ // This makes the compiler complain if some opcode is unchecked in the switch.
+ Opcode code = Opcode(_code_ptr[ip] & INSTR_MASK);
+ int instr_var_args = (_code_ptr[ip] & INSTR_ARGS_MASK) >> INSTR_BITS;
+
+ switch (code) {
+ case OPCODE_OPERATOR: {
+ int operation = _code_ptr[ip + 4];
+
+ text += "operator ";
+
+ text += DADDR(3);
+ text += " = ";
+ text += DADDR(1);
+ text += " ";
+ text += Variant::get_operator_name(Variant::Operator(operation));
+ text += " ";
+ text += DADDR(2);
+
+ incr += 5;
+ } break;
+ case OPCODE_OPERATOR_VALIDATED: {
+ text += "validated operator ";
+
+ text += DADDR(3);
+ text += " = ";
+ text += DADDR(1);
+ text += " <operator function> ";
+ text += DADDR(2);
+
+ incr += 5;
+ } break;
+ case OPCODE_EXTENDS_TEST: {
+ text += "is object ";
+ text += DADDR(3);
+ text += " = ";
+ text += DADDR(1);
+ text += " is ";
+ text += DADDR(2);
+
+ incr += 4;
+ } break;
+ case OPCODE_IS_BUILTIN: {
+ text += "is builtin ";
+ text += DADDR(2);
+ text += " = ";
+ text += DADDR(1);
+ text += " is ";
+ text += Variant::get_type_name(Variant::Type(_code_ptr[ip + 3]));
+
+ incr += 4;
+ } break;
+ case OPCODE_SET_KEYED: {
+ text += "set keyed ";
+ text += DADDR(1);
+ text += "[";
+ text += DADDR(2);
+ text += "] = ";
+ text += DADDR(3);
+
+ incr += 4;
+ } break;
+ case OPCODE_SET_KEYED_VALIDATED: {
+ text += "set keyed validated ";
+ text += DADDR(1);
+ text += "[";
+ text += DADDR(2);
+ text += "] = ";
+ text += DADDR(3);
+
+ incr += 5;
+ } break;
+ case OPCODE_SET_INDEXED_VALIDATED: {
+ text += "set indexed validated ";
+ text += DADDR(1);
+ text += "[";
+ text += DADDR(2);
+ text += "] = ";
+ text += DADDR(3);
+
+ incr += 5;
+ } break;
+ case OPCODE_GET_KEYED: {
+ text += "get keyed ";
+ text += DADDR(3);
+ text += " = ";
+ text += DADDR(1);
+ text += "[";
+ text += DADDR(2);
+ text += "]";
+
+ incr += 4;
+ } break;
+ case OPCODE_GET_KEYED_VALIDATED: {
+ text += "get keyed validated ";
+ text += DADDR(3);
+ text += " = ";
+ text += DADDR(1);
+ text += "[";
+ text += DADDR(2);
+ text += "]";
+
+ incr += 5;
+ } break;
+ case OPCODE_GET_INDEXED_VALIDATED: {
+ text += "get indexed validated ";
+ text += DADDR(3);
+ text += " = ";
+ text += DADDR(1);
+ text += "[";
+ text += DADDR(2);
+ text += "]";
+
+ incr += 5;
+ } break;
+ case OPCODE_SET_NAMED: {
+ text += "set_named ";
+ text += DADDR(1);
+ text += "[\"";
+ text += _global_names_ptr[_code_ptr[ip + 3]];
+ text += "\"] = ";
+ text += DADDR(2);
+
+ incr += 4;
+ } break;
+ case OPCODE_SET_NAMED_VALIDATED: {
+ text += "set_named validated ";
+ text += DADDR(1);
+ text += "[\"";
+ text += "<unknown name>";
+ text += "\"] = ";
+ text += DADDR(2);
+
+ incr += 4;
+ } break;
+ case OPCODE_GET_NAMED: {
+ text += "get_named ";
+ text += DADDR(2);
+ text += " = ";
+ text += DADDR(1);
+ text += "[\"";
+ text += _global_names_ptr[_code_ptr[ip + 3]];
+ text += "\"]";
+
+ incr += 4;
+ } break;
+ case OPCODE_GET_NAMED_VALIDATED: {
+ text += "get_named validated ";
+ text += DADDR(2);
+ text += " = ";
+ text += DADDR(1);
+ text += "[\"";
+ text += "<unknown name>";
+ text += "\"]";
+
+ incr += 4;
+ } break;
+ case OPCODE_SET_MEMBER: {
+ text += "set_member ";
+ text += "[\"";
+ text += _global_names_ptr[_code_ptr[ip + 2]];
+ text += "\"] = ";
+ text += DADDR(1);
+
+ incr += 3;
+ } break;
+ case OPCODE_GET_MEMBER: {
+ text += "get_member ";
+ text += DADDR(1);
+ text += " = ";
+ text += "[\"";
+ text += _global_names_ptr[_code_ptr[ip + 2]];
+ text += "\"]";
+
+ incr += 3;
+ } break;
+ case OPCODE_ASSIGN: {
+ text += "assign ";
+ text += DADDR(1);
+ text += " = ";
+ text += DADDR(2);
+
+ incr += 3;
+ } break;
+ case OPCODE_ASSIGN_TRUE: {
+ text += "assign ";
+ text += DADDR(1);
+ text += " = true";
+
+ incr += 2;
+ } break;
+ case OPCODE_ASSIGN_FALSE: {
+ text += "assign ";
+ text += DADDR(1);
+ text += " = false";
+
+ incr += 2;
+ } break;
+ case OPCODE_ASSIGN_TYPED_BUILTIN: {
+ text += "assign typed builtin (";
+ text += Variant::get_type_name((Variant::Type)_code_ptr[ip + 3]);
+ text += ") ";
+ text += DADDR(1);
+ text += " = ";
+ text += DADDR(2);
+
+ incr += 4;
+ } break;
+ case OPCODE_ASSIGN_TYPED_ARRAY: {
+ text += "assign typed array ";
+ text += DADDR(1);
+ text += " = ";
+ text += DADDR(2);
+
+ incr += 3;
+ } break;
+ case OPCODE_ASSIGN_TYPED_NATIVE: {
+ text += "assign typed native (";
+ text += DADDR(3);
+ text += ") ";
+ text += DADDR(1);
+ text += " = ";
+ text += DADDR(2);
+
+ incr += 4;
+ } break;
+ case OPCODE_ASSIGN_TYPED_SCRIPT: {
+ Variant script = _constants_ptr[_code_ptr[ip + 3]];
+ Script *sc = Object::cast_to<Script>(script.operator Object *());
+
+ text += "assign typed script (";
+ text += sc->get_path();
+ text += ") ";
+ text += DADDR(1);
+ text += " = ";
+ text += DADDR(2);
+
+ incr += 4;
+ } break;
+ case OPCODE_CAST_TO_BUILTIN: {
+ text += "cast builtin ";
+ text += DADDR(2);
+ text += " = ";
+ text += DADDR(1);
+ text += " as ";
+ text += Variant::get_type_name(Variant::Type(_code_ptr[ip + 1]));
+
+ incr += 4;
+ } break;
+ case OPCODE_CAST_TO_NATIVE: {
+ text += "cast native ";
+ text += DADDR(2);
+ text += " = ";
+ text += DADDR(1);
+ text += " as ";
+ text += DADDR(3);
+
+ incr += 4;
+ } break;
+ case OPCODE_CAST_TO_SCRIPT: {
+ text += "cast ";
+ text += DADDR(2);
+ text += " = ";
+ text += DADDR(1);
+ text += " as ";
+ text += DADDR(3);
+
+ incr += 4;
+ } break;
+ case OPCODE_CONSTRUCT: {
+ Variant::Type t = Variant::Type(_code_ptr[ip + 3 + instr_var_args]);
+ int argc = _code_ptr[ip + 1 + instr_var_args];
+
+ text += "construct ";
+ text += DADDR(1 + argc);
+ text += " = ";
+
+ text += Variant::get_type_name(t) + "(";
+ for (int i = 0; i < argc; i++) {
+ if (i > 0)
+ text += ", ";
+ text += DADDR(i + 1);
+ }
+ text += ")";
+
+ incr = 3 + instr_var_args;
+ } break;
+ case OPCODE_CONSTRUCT_VALIDATED: {
+ int argc = _code_ptr[ip + 1 + instr_var_args];
+
+ text += "construct validated ";
+ text += DADDR(1 + argc);
+ text += " = ";
+
+ text += "<unkown type>(";
+ for (int i = 0; i < argc; i++) {
+ if (i > 0)
+ text += ", ";
+ text += DADDR(i + 1);
+ }
+ text += ")";
+
+ incr = 3 + instr_var_args;
+ } break;
+ case OPCODE_CONSTRUCT_ARRAY: {
+ int argc = _code_ptr[ip + 1 + instr_var_args];
+ text += " make_array ";
+ text += DADDR(1 + argc);
+ text += " = [";
+
+ for (int i = 0; i < argc; i++) {
+ if (i > 0)
+ text += ", ";
+ text += DADDR(1 + i);
+ }
+
+ text += "]";
+
+ incr += 3 + argc;
+ } break;
+ case OPCODE_CONSTRUCT_TYPED_ARRAY: {
+ int argc = _code_ptr[ip + 1 + instr_var_args];
+
+ Ref<Script> script_type = get_constant(_code_ptr[ip + argc + 2]);
+ Variant::Type builtin_type = (Variant::Type)_code_ptr[ip + argc + 4];
+ StringName native_type = get_global_name(_code_ptr[ip + argc + 5]);
+
+ String type_name;
+ if (script_type.is_valid() && script_type->is_valid()) {
+ type_name = script_type->get_path();
+ } else if (native_type != StringName()) {
+ type_name = native_type;
+ } else {
+ type_name = Variant::get_type_name(builtin_type);
+ }
+
+ text += " make_typed_array (";
+ text += type_name;
+ text += ") ";
+
+ text += DADDR(1 + argc);
+ text += " = [";
+
+ for (int i = 0; i < argc; i++) {
+ if (i > 0)
+ text += ", ";
+ text += DADDR(1 + i);
+ }
+
+ text += "]";
+
+ incr += 3 + argc;
+ } break;
+ case OPCODE_CONSTRUCT_DICTIONARY: {
+ int argc = _code_ptr[ip + 1 + instr_var_args];
+ text += "make_dict ";
+ text += DADDR(1 + argc * 2);
+ text += " = {";
+
+ for (int i = 0; i < argc; i++) {
+ if (i > 0)
+ text += ", ";
+ text += DADDR(1 + i * 2 + 0);
+ text += ": ";
+ text += DADDR(1 + i * 2 + 1);
+ }
+
+ text += "}";
+
+ incr += 3 + argc * 2;
+ } break;
+ case OPCODE_CALL:
+ case OPCODE_CALL_RETURN:
+ case OPCODE_CALL_ASYNC: {
+ bool ret = (_code_ptr[ip] & INSTR_MASK) == OPCODE_CALL_RETURN;
+ bool async = (_code_ptr[ip] & INSTR_MASK) == OPCODE_CALL_ASYNC;
+
+ if (ret) {
+ text += "call-ret ";
+ } else if (async) {
+ text += "call-async ";
+ } else {
+ text += "call ";
+ }
+
+ int argc = _code_ptr[ip + 1 + instr_var_args];
+ if (ret || async) {
+ text += DADDR(2 + argc) + " = ";
+ }
+
+ text += DADDR(1 + argc) + ".";
+ text += String(_global_names_ptr[_code_ptr[ip + 2 + instr_var_args]]);
+ text += "(";
+
+ for (int i = 0; i < argc; i++) {
+ if (i > 0)
+ text += ", ";
+ text += DADDR(1 + i);
+ }
+ text += ")";
+
+ incr = 5 + argc;
+ } break;
+ case OPCODE_CALL_METHOD_BIND:
+ case OPCODE_CALL_METHOD_BIND_RET: {
+ bool ret = (_code_ptr[ip] & INSTR_MASK) == OPCODE_CALL_METHOD_BIND_RET;
+
+ if (ret) {
+ text += "call-method_bind-ret ";
+ } else {
+ text += "call-method_bind ";
+ }
+
+ MethodBind *method = _methods_ptr[_code_ptr[ip + 2 + instr_var_args]];
+
+ int argc = _code_ptr[ip + 1 + instr_var_args];
+ if (ret) {
+ text += DADDR(2 + argc) + " = ";
+ }
+
+ text += DADDR(1 + argc) + ".";
+ text += method->get_name();
+ 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) ";
+
+ MethodBind *method = _methods_ptr[_code_ptr[ip + 2 + instr_var_args]];
+
+ int argc = _code_ptr[ip + 1 + instr_var_args];
+
+ text += DADDR(1 + argc) + ".";
+ text += method->get_name();
+ text += "(";
+
+ for (int i = 0; i < argc; i++) {
+ if (i > 0)
+ text += ", ";
+ text += DADDR(1 + i);
+ }
+ text += ")";
+
+ incr = 5 + argc;
+ } break;
+
+#define DISASSEMBLE_PTRCALL(m_type) \
+ case OPCODE_CALL_PTRCALL_##m_type: { \
+ text += "call-ptrcall (return "; \
+ text += #m_type; \
+ text += ") "; \
+ MethodBind *method = _methods_ptr[_code_ptr[ip + 2 + instr_var_args]]; \
+ int argc = _code_ptr[ip + 1 + instr_var_args]; \
+ text += DADDR(2 + argc) + " = "; \
+ text += DADDR(1 + argc) + "."; \
+ text += method->get_name(); \
+ text += "("; \
+ for (int i = 0; i < argc; i++) { \
+ if (i > 0) \
+ text += ", "; \
+ text += DADDR(1 + i); \
+ } \
+ text += ")"; \
+ incr = 5 + argc; \
+ } break
+
+ DISASSEMBLE_PTRCALL(BOOL);
+ DISASSEMBLE_PTRCALL(INT);
+ DISASSEMBLE_PTRCALL(FLOAT);
+ DISASSEMBLE_PTRCALL(STRING);
+ DISASSEMBLE_PTRCALL(VECTOR2);
+ DISASSEMBLE_PTRCALL(VECTOR2I);
+ DISASSEMBLE_PTRCALL(RECT2);
+ DISASSEMBLE_PTRCALL(RECT2I);
+ DISASSEMBLE_PTRCALL(VECTOR3);
+ DISASSEMBLE_PTRCALL(VECTOR3I);
+ DISASSEMBLE_PTRCALL(TRANSFORM2D);
+ DISASSEMBLE_PTRCALL(PLANE);
+ DISASSEMBLE_PTRCALL(AABB);
+ DISASSEMBLE_PTRCALL(BASIS);
+ DISASSEMBLE_PTRCALL(TRANSFORM);
+ DISASSEMBLE_PTRCALL(COLOR);
+ DISASSEMBLE_PTRCALL(STRING_NAME);
+ DISASSEMBLE_PTRCALL(NODE_PATH);
+ DISASSEMBLE_PTRCALL(RID);
+ DISASSEMBLE_PTRCALL(QUAT);
+ DISASSEMBLE_PTRCALL(OBJECT);
+ DISASSEMBLE_PTRCALL(CALLABLE);
+ DISASSEMBLE_PTRCALL(SIGNAL);
+ DISASSEMBLE_PTRCALL(DICTIONARY);
+ DISASSEMBLE_PTRCALL(ARRAY);
+ DISASSEMBLE_PTRCALL(PACKED_BYTE_ARRAY);
+ DISASSEMBLE_PTRCALL(PACKED_INT32_ARRAY);
+ DISASSEMBLE_PTRCALL(PACKED_INT64_ARRAY);
+ DISASSEMBLE_PTRCALL(PACKED_FLOAT32_ARRAY);
+ DISASSEMBLE_PTRCALL(PACKED_FLOAT64_ARRAY);
+ DISASSEMBLE_PTRCALL(PACKED_STRING_ARRAY);
+ DISASSEMBLE_PTRCALL(PACKED_VECTOR2_ARRAY);
+ DISASSEMBLE_PTRCALL(PACKED_VECTOR3_ARRAY);
+ DISASSEMBLE_PTRCALL(PACKED_COLOR_ARRAY);
+
+ case OPCODE_CALL_BUILTIN_TYPE_VALIDATED: {
+ int argc = _code_ptr[ip + 1 + instr_var_args];
+
+ text += "call-builtin-method validated ";
+
+ text += DADDR(2 + argc) + " = ";
+
+ text += DADDR(1) + ".";
+ text += "<unknown method>";
+
+ text += "(";
+
+ for (int i = 0; i < argc; i++) {
+ if (i > 0)
+ text += ", ";
+ text += DADDR(1 + i);
+ }
+ text += ")";
+
+ incr = 5 + argc;
+ } break;
+ case OPCODE_CALL_UTILITY: {
+ text += "call-utility ";
+
+ int argc = _code_ptr[ip + 1 + instr_var_args];
+ text += DADDR(1 + argc) + " = ";
+
+ text += _global_names_ptr[_code_ptr[ip + 2 + instr_var_args]];
+ text += "(";
+
+ for (int i = 0; i < argc; i++) {
+ if (i > 0)
+ text += ", ";
+ text += DADDR(1 + i);
+ }
+ text += ")";
+
+ incr = 4 + argc;
+ } break;
+ case OPCODE_CALL_UTILITY_VALIDATED: {
+ text += "call-utility ";
+
+ int argc = _code_ptr[ip + 1 + instr_var_args];
+ text += DADDR(1 + argc) + " = ";
+
+ text += "<unkown function>";
+ text += "(";
+
+ for (int i = 0; i < argc; i++) {
+ if (i > 0)
+ text += ", ";
+ text += DADDR(1 + i);
+ }
+ text += ")";
+
+ incr = 4 + argc;
+ } break;
+ case OPCODE_CALL_GDSCRIPT_UTILITY: {
+ text += "call-gscript-utility ";
+
+ int argc = _code_ptr[ip + 1 + instr_var_args];
+ text += DADDR(1 + argc) + " = ";
+
+ text += "<unknown function>";
+ text += "(";
+
+ for (int i = 0; i < argc; i++) {
+ if (i > 0)
+ text += ", ";
+ text += DADDR(1 + i);
+ }
+ text += ")";
+
+ incr = 4 + argc;
+ } break;
+ case OPCODE_CALL_SELF_BASE: {
+ text += "call-self-base ";
+
+ int argc = _code_ptr[ip + 1 + instr_var_args];
+ text += DADDR(2 + argc) + " = ";
+
+ text += _global_names_ptr[_code_ptr[ip + 2 + instr_var_args]];
+ text += "(";
+
+ for (int i = 0; i < argc; i++) {
+ if (i > 0)
+ text += ", ";
+ text += DADDR(1 + i);
+ }
+ text += ")";
+
+ incr = 4 + argc;
+ } break;
+ case OPCODE_AWAIT: {
+ text += "await ";
+ text += DADDR(1);
+
+ incr += 2;
+ } break;
+ case OPCODE_AWAIT_RESUME: {
+ text += "await resume ";
+ text += DADDR(1);
+
+ incr = 2;
+ } break;
+ case OPCODE_JUMP: {
+ text += "jump ";
+ text += itos(_code_ptr[ip + 1]);
+
+ incr = 2;
+ } break;
+ case OPCODE_JUMP_IF: {
+ text += "jump-if ";
+ text += DADDR(1);
+ text += " to ";
+ text += itos(_code_ptr[ip + 2]);
+
+ incr = 3;
+ } break;
+ case OPCODE_JUMP_IF_NOT: {
+ text += "jump-if-not ";
+ text += DADDR(1);
+ text += " to ";
+ text += itos(_code_ptr[ip + 2]);
+
+ incr = 3;
+ } break;
+ case OPCODE_JUMP_TO_DEF_ARGUMENT: {
+ text += "jump-to-default-argument ";
+
+ incr = 1;
+ } break;
+ case OPCODE_RETURN: {
+ text += "return ";
+ text += DADDR(1);
+
+ incr = 2;
+ } break;
+
+#define DISASSEMBLE_ITERATE(m_type) \
+ case OPCODE_ITERATE_##m_type: { \
+ text += "for-loop (typed "; \
+ text += #m_type; \
+ text += ") "; \
+ text += DADDR(3); \
+ text += " in "; \
+ text += DADDR(2); \
+ text += " counter "; \
+ text += DADDR(1); \
+ text += " end "; \
+ text += itos(_code_ptr[ip + 4]); \
+ incr += 5; \
+ } break
+
+#define DISASSEMBLE_ITERATE_BEGIN(m_type) \
+ case OPCODE_ITERATE_BEGIN_##m_type: { \
+ text += "for-init (typed "; \
+ text += #m_type; \
+ text += ") "; \
+ text += DADDR(3); \
+ text += " in "; \
+ text += DADDR(2); \
+ text += " counter "; \
+ text += DADDR(1); \
+ text += " end "; \
+ text += itos(_code_ptr[ip + 4]); \
+ incr += 5; \
+ } break
+
+#define DISASSEMBLE_ITERATE_TYPES(m_macro) \
+ m_macro(INT); \
+ m_macro(FLOAT); \
+ m_macro(VECTOR2); \
+ m_macro(VECTOR2I); \
+ m_macro(VECTOR3); \
+ m_macro(VECTOR3I); \
+ m_macro(STRING); \
+ m_macro(DICTIONARY); \
+ m_macro(ARRAY); \
+ m_macro(PACKED_BYTE_ARRAY); \
+ m_macro(PACKED_INT32_ARRAY); \
+ m_macro(PACKED_INT64_ARRAY); \
+ m_macro(PACKED_FLOAT32_ARRAY); \
+ m_macro(PACKED_FLOAT64_ARRAY); \
+ m_macro(PACKED_STRING_ARRAY); \
+ m_macro(PACKED_VECTOR2_ARRAY); \
+ m_macro(PACKED_VECTOR3_ARRAY); \
+ m_macro(PACKED_COLOR_ARRAY); \
+ m_macro(OBJECT)
+
+ case OPCODE_ITERATE_BEGIN: {
+ text += "for-init ";
+ text += DADDR(3);
+ text += " in ";
+ text += DADDR(2);
+ text += " counter ";
+ text += DADDR(1);
+ text += " end ";
+ text += itos(_code_ptr[ip + 4]);
+
+ incr += 5;
+ } break;
+ DISASSEMBLE_ITERATE_TYPES(DISASSEMBLE_ITERATE_BEGIN);
+ case OPCODE_ITERATE: {
+ text += "for-loop ";
+ text += DADDR(2);
+ text += " in ";
+ text += DADDR(2);
+ text += " counter ";
+ text += DADDR(1);
+ text += " end ";
+ text += itos(_code_ptr[ip + 4]);
+
+ incr += 5;
+ } break;
+ DISASSEMBLE_ITERATE_TYPES(DISASSEMBLE_ITERATE);
+ case OPCODE_LINE: {
+ int line = _code_ptr[ip + 1] - 1;
+ if (line >= 0 && line < p_code_lines.size()) {
+ text += "line ";
+ text += itos(line + 1);
+ text += ": ";
+ text += p_code_lines[line];
+ } else {
+ text += "";
+ }
+
+ incr += 2;
+ } break;
+ case OPCODE_ASSERT: {
+ text += "assert (";
+ text += DADDR(1);
+ text += ", ";
+ text += DADDR(2);
+ text += ")";
+
+ incr += 3;
+ } break;
+ case OPCODE_BREAKPOINT: {
+ text += "breakpoint";
+
+ incr += 1;
+ } break;
+ case OPCODE_END: {
+ text += "== END ==";
+
+ incr += 1;
+ } break;
+ }
+
+ ip += incr;
+ if (text.get_string_length() > 0) {
+ print_line(text.as_string());
+ }
+ }
+}
+
+#endif
diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp
index a426046797..d560718dda 100644
--- a/modules/gdscript/gdscript_editor.cpp
+++ b/modules/gdscript/gdscript_editor.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -37,6 +37,7 @@
#include "gdscript_compiler.h"
#include "gdscript_parser.h"
#include "gdscript_tokenizer.h"
+#include "gdscript_utility_functions.h"
#ifdef TOOLS_ENABLED
#include "core/config/project_settings.h"
@@ -122,10 +123,10 @@ static void get_function_names_recursively(const GDScriptParser::ClassNode *p_cl
for (int i = 0; i < p_class->members.size(); i++) {
if (p_class->members[i].type == GDScriptParser::ClassNode::Member::FUNCTION) {
const GDScriptParser::FunctionNode *function = p_class->members[i].function;
- r_funcs[function->start_line] = p_prefix.empty() ? String(function->identifier->name) : p_prefix + "." + String(function->identifier->name);
+ r_funcs[function->start_line] = p_prefix.is_empty() ? String(function->identifier->name) : p_prefix + "." + String(function->identifier->name);
} else if (p_class->members[i].type == GDScriptParser::ClassNode::Member::CLASS) {
String new_prefix = p_class->members[i].m_class->identifier->name;
- get_function_names_recursively(p_class->members[i].m_class, p_prefix.empty() ? new_prefix : p_prefix + "." + new_prefix, r_funcs);
+ get_function_names_recursively(p_class->members[i].m_class, p_prefix.is_empty() ? new_prefix : p_prefix + "." + new_prefix, r_funcs);
}
}
}
@@ -193,6 +194,10 @@ bool GDScriptLanguage::supports_builtin_mode() const {
return true;
}
+bool GDScriptLanguage::supports_documentation() const {
+ return true;
+}
+
int GDScriptLanguage::find_function(const String &p_function, const String &p_code) const {
GDScriptTokenizer tokenizer;
tokenizer.set_source_code(p_code);
@@ -407,11 +412,14 @@ void GDScriptLanguage::get_recognized_extensions(List<String> *p_extensions) con
}
void GDScriptLanguage::get_public_functions(List<MethodInfo> *p_functions) const {
- for (int i = 0; i < GDScriptFunctions::FUNC_MAX; i++) {
- p_functions->push_back(GDScriptFunctions::get_info(GDScriptFunctions::Function(i)));
+ 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()));
}
- //not really "functions", but..
+ // Not really "functions", but show in documentation.
{
MethodInfo mi;
mi.name = "preload";
@@ -468,7 +476,7 @@ String GDScriptLanguage::make_function(const String &p_class, const String &p_na
s += p_args[i].get_slice(":", 0);
if (th) {
String type = p_args[i].get_slice(":", 1);
- if (!type.empty() && type != "var") {
+ if (!type.is_empty() && type != "var") {
s += ": " + type;
}
}
@@ -492,36 +500,6 @@ struct GDScriptCompletionIdentifier {
const GDScriptParser::ExpressionNode *assigned_expression = nullptr;
};
-// TODO: Move this to a central location (maybe core?).
-static const char *underscore_classes[] = {
- "ClassDB",
- "Directory",
- "Engine",
- "File",
- "Geometry",
- "GodotSharp",
- "JSON",
- "Marshalls",
- "Mutex",
- "OS",
- "ResourceLoader",
- "ResourceSaver",
- "Semaphore",
- "Thread",
- "VisualScriptEditor",
- nullptr,
-};
-static StringName _get_real_class_name(const StringName &p_source) {
- const char **class_name = underscore_classes;
- while (*class_name != nullptr) {
- if (p_source == *class_name) {
- return String("_") + p_source;
- }
- class_name++;
- }
- return p_source;
-}
-
static String _get_visual_datatype(const PropertyInfo &p_info, bool p_is_arg = true) {
if (p_info.usage & PROPERTY_USAGE_CLASS_IS_ENUM) {
String enum_name = p_info.class_name;
@@ -922,7 +900,7 @@ static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base
}
} break;
case GDScriptParser::DataType::NATIVE: {
- StringName type = _get_real_class_name(base_type.native_type);
+ StringName type = GDScriptParser::get_real_class_name(base_type.native_type);
if (!ClassDB::class_exists(type)) {
return;
}
@@ -1026,9 +1004,12 @@ static void _find_identifiers(GDScriptParser::CompletionContext &p_context, bool
_find_identifiers_in_class(p_context.current_class, p_only_functions, (!p_context.current_function || p_context.current_function->is_static), false, r_result, p_recursion_depth + 1);
}
- for (int i = 0; i < GDScriptFunctions::FUNC_MAX; i++) {
- MethodInfo function = GDScriptFunctions::get_info(GDScriptFunctions::Function(i));
- ScriptCodeCompletionOption option(String(GDScriptFunctions::get_func_name(GDScriptFunctions::Function(i))), ScriptCodeCompletionOption::KIND_FUNCTION);
+ 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);
if (function.arguments.size() || (function.flags & METHOD_FLAG_VARARG)) {
option.insert_text += "(";
} else {
@@ -1054,10 +1035,8 @@ static void _find_identifiers(GDScriptParser::CompletionContext &p_context, bool
}
static const char *_keywords[] = {
- "and", "in", "not", "or", "false", "PI", "TAU", "INF", "NAN", "self", "true", "as", "assert",
- "breakpoint", "class", "extends", "is", "func", "preload", "signal", "tool", "await",
- "const", "enum", "static", "super", "var", "break", "continue", "if", "elif",
- "else", "for", "pass", "return", "match", "while",
+ "false", "PI", "TAU", "INF", "NAN", "self", "true", "breakpoint", "tool", "super",
+ "break", "continue", "pass", "return",
0
};
@@ -1068,6 +1047,33 @@ static void _find_identifiers(GDScriptParser::CompletionContext &p_context, bool
kw++;
}
+ static const char *_keywords_with_space[] = {
+ "and", "in", "not", "or", "as", "class", "extends", "is", "func", "signal", "await",
+ "const", "enum", "static", "var", "if", "elif", "else", "for", "match", "while",
+ 0
+ };
+
+ const char **kws = _keywords_with_space;
+ while (*kws) {
+ ScriptCodeCompletionOption option(*kws, ScriptCodeCompletionOption::KIND_PLAIN_TEXT);
+ option.insert_text += " ";
+ r_result.insert(option.display, option);
+ kws++;
+ }
+
+ static const char *_keywords_with_args[] = {
+ "assert", "preload",
+ 0
+ };
+
+ const char **kwa = _keywords_with_args;
+ while (*kwa) {
+ ScriptCodeCompletionOption option(*kwa, ScriptCodeCompletionOption::KIND_FUNCTION);
+ option.insert_text += "(";
+ r_result.insert(option.display, option);
+ kwa++;
+ }
+
Map<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list();
for (const Map<StringName, ProjectSettings::AutoloadInfo>::Element *E = autoloads.front(); E != nullptr; E = E->next()) {
if (!E->value().is_singleton) {
@@ -1259,8 +1265,8 @@ static bool _guess_expression_type(GDScriptParser::CompletionContext &p_context,
r_type.type.builtin_type = GDScriptParser::get_builtin_type(call->function_name);
found = true;
break;
- } else if (GDScriptParser::get_builtin_function(call->function_name) < GDScriptFunctions::FUNC_MAX) {
- MethodInfo mi = GDScriptFunctions::get_info(GDScriptParser::get_builtin_function(call->function_name));
+ } else if (GDScriptUtilityFunctions::function_exists(call->function_name)) {
+ MethodInfo mi = GDScriptUtilityFunctions::get_function_info(call->function_name);
r_type = _type_from_property(mi.return_val);
found = true;
break;
@@ -1747,7 +1753,7 @@ static bool _guess_identifier_type(GDScriptParser::CompletionContext &p_context,
base_type = GDScriptParser::DataType();
break;
}
- StringName real_native = _get_real_class_name(base_type.native_type);
+ 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()) {
@@ -1818,7 +1824,7 @@ static bool _guess_identifier_type(GDScriptParser::CompletionContext &p_context,
}
// Check ClassDB.
- StringName class_name = _get_real_class_name(p_identifier);
+ StringName class_name = GDScriptParser::get_real_class_name(p_identifier);
if (ClassDB::class_exists(class_name) && ClassDB::is_class_exposed(class_name)) {
r_type.type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
r_type.type.kind = GDScriptParser::DataType::NATIVE;
@@ -1934,7 +1940,7 @@ static bool _guess_identifier_type_from_base(GDScriptParser::CompletionContext &
}
} break;
case GDScriptParser::DataType::NATIVE: {
- StringName class_name = _get_real_class_name(base_type.native_type);
+ StringName class_name = GDScriptParser::get_real_class_name(base_type.native_type);
if (!ClassDB::class_exists(class_name)) {
return false;
}
@@ -2097,7 +2103,7 @@ static bool _guess_method_return_type_from_base(GDScriptParser::CompletionContex
}
} break;
case GDScriptParser::DataType::NATIVE: {
- StringName native = _get_real_class_name(base_type.native_type);
+ StringName native = GDScriptParser::get_real_class_name(base_type.native_type);
if (!ClassDB::class_exists(native)) {
return false;
}
@@ -2194,7 +2200,7 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c
base_type = base_type.class_type->base_type;
} break;
case GDScriptParser::DataType::NATIVE: {
- StringName class_name = _get_real_class_name(base_type.native_type);
+ StringName class_name = GDScriptParser::get_real_class_name(base_type.native_type);
if (!ClassDB::class_exists(class_name)) {
base_type.kind = GDScriptParser::DataType::UNRESOLVED;
break;
@@ -2225,10 +2231,9 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c
}
r_arghint = _make_arguments_hint(info, p_argidx);
- return;
}
- if (ClassDB::is_parent_class(class_name, "Node") && (p_method == "get_node" || p_method == "has_node") && p_argidx == 0) {
+ if (p_argidx == 0 && ClassDB::is_parent_class(class_name, "Node") && (p_method == "get_node" || p_method == "has_node")) {
// Get autoloads
List<PropertyInfo> props;
ProjectSettings::get_singleton()->get_property_list(&props);
@@ -2309,12 +2314,12 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c
GDScriptParser::DataType base_type;
bool _static = false;
const GDScriptParser::CallNode *call = static_cast<const GDScriptParser::CallNode *>(p_call);
- GDScriptParser::Node::Type callee_type = GDScriptParser::Node::NONE;
+ GDScriptParser::Node::Type callee_type = call->get_callee_type();
GDScriptCompletionIdentifier connect_base;
- if (GDScriptParser::get_builtin_function(call->function_name) < GDScriptFunctions::FUNC_MAX) {
- MethodInfo info = GDScriptFunctions::get_info(GDScriptParser::get_builtin_function(call->function_name));
+ if (GDScriptUtilityFunctions::function_exists(call->function_name)) {
+ MethodInfo info = GDScriptUtilityFunctions::get_function_info(call->function_name);
r_arghint = _make_arguments_hint(info, p_argidx);
return;
} else if (GDScriptParser::get_builtin_type(call->function_name) < Variant::VARIANT_MAX) {
@@ -2475,7 +2480,7 @@ Error GDScriptLanguage::complete_code(const String &p_code, const String &p_path
break;
}
- if (!type.enumeration.empty()) {
+ if (!type.enumeration.is_empty()) {
_find_enumeration_candidates(completion_context, type.enumeration, options);
r_forced = options.size() > 0;
} else {
@@ -2580,7 +2585,7 @@ Error GDScriptLanguage::complete_code(const String &p_code, const String &p_path
break;
}
- StringName class_name = _get_real_class_name(native_type.native_type);
+ StringName class_name = GDScriptParser::get_real_class_name(native_type.native_type);
if (!ClassDB::class_exists(class_name)) {
break;
}
@@ -2809,7 +2814,7 @@ static Error _lookup_symbol_from_base(const GDScriptParser::DataType &p_base, co
}
} break;
case GDScriptParser::DataType::NATIVE: {
- StringName class_name = _get_real_class_name(base_type.native_type);
+ StringName class_name = GDScriptParser::get_real_class_name(base_type.native_type);
if (!ClassDB::class_exists(class_name)) {
base_type.kind = GDScriptParser::DataType::UNRESOLVED;
break;
@@ -2942,13 +2947,11 @@ Error GDScriptLanguage::lookup_code(const String &p_code, const String &p_symbol
}
}
- for (int i = 0; i < GDScriptFunctions::FUNC_MAX; i++) {
- if (GDScriptFunctions::get_func_name(GDScriptFunctions::Function(i)) == p_symbol) {
- r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS_METHOD;
- r_result.class_name = "@GDScript";
- r_result.class_member = p_symbol;
- return OK;
- }
+ if (GDScriptUtilityFunctions::function_exists(p_symbol)) {
+ r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS_METHOD;
+ r_result.class_name = "@GDScript";
+ r_result.class_member = p_symbol;
+ return OK;
}
if ("PI" == p_symbol || "TAU" == p_symbol || "INF" == p_symbol || "NAN" == p_symbol) {
diff --git a/modules/gdscript/gdscript_function.cpp b/modules/gdscript/gdscript_function.cpp
index 8372672cf7..7b37aa40a2 100644
--- a/modules/gdscript/gdscript_function.cpp
+++ b/modules/gdscript/gdscript_function.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -30,1574 +30,7 @@
#include "gdscript_function.h"
-#include "core/os/os.h"
#include "gdscript.h"
-#include "gdscript_functions.h"
-
-#ifdef DEBUG_ENABLED
-#include "core/string/string_builder.h"
-#endif
-
-Variant *GDScriptFunction::_get_variant(int p_address, GDScriptInstance *p_instance, GDScript *p_script, Variant &self, Variant &static_ref, Variant *p_stack, String &r_error) const {
- int address = p_address & ADDR_MASK;
-
- //sequential table (jump table generated by compiler)
- switch ((p_address & ADDR_TYPE_MASK) >> ADDR_BITS) {
- case ADDR_TYPE_SELF: {
-#ifdef DEBUG_ENABLED
- if (unlikely(!p_instance)) {
- r_error = "Cannot access self without instance.";
- return nullptr;
- }
-#endif
- return &self;
- } break;
- case ADDR_TYPE_CLASS: {
- return &static_ref;
- } break;
- case ADDR_TYPE_MEMBER: {
-#ifdef DEBUG_ENABLED
- if (unlikely(!p_instance)) {
- r_error = "Cannot access member without instance.";
- return nullptr;
- }
-#endif
- //member indexing is O(1)
- return &p_instance->members.write[address];
- } break;
- case ADDR_TYPE_CLASS_CONSTANT: {
- //todo change to index!
- GDScript *s = p_script;
-#ifdef DEBUG_ENABLED
- ERR_FAIL_INDEX_V(address, _global_names_count, nullptr);
-#endif
- const StringName *sn = &_global_names_ptr[address];
-
- while (s) {
- GDScript *o = s;
- while (o) {
- Map<StringName, Variant>::Element *E = o->constants.find(*sn);
- if (E) {
- return &E->get();
- }
- o = o->_owner;
- }
- s = s->_base;
- }
-
- ERR_FAIL_V_MSG(nullptr, "GDScriptCompiler bug.");
- } break;
- case ADDR_TYPE_LOCAL_CONSTANT: {
-#ifdef DEBUG_ENABLED
- ERR_FAIL_INDEX_V(address, _constant_count, nullptr);
-#endif
- return &_constants_ptr[address];
- } break;
- case ADDR_TYPE_STACK:
- case ADDR_TYPE_STACK_VARIABLE: {
-#ifdef DEBUG_ENABLED
- ERR_FAIL_INDEX_V(address, _stack_size, nullptr);
-#endif
- return &p_stack[address];
- } break;
- case ADDR_TYPE_GLOBAL: {
-#ifdef DEBUG_ENABLED
- ERR_FAIL_INDEX_V(address, GDScriptLanguage::get_singleton()->get_global_array_size(), nullptr);
-#endif
- return &GDScriptLanguage::get_singleton()->get_global_array()[address];
- } break;
-#ifdef TOOLS_ENABLED
- case ADDR_TYPE_NAMED_GLOBAL: {
-#ifdef DEBUG_ENABLED
- ERR_FAIL_INDEX_V(address, _global_names_count, nullptr);
-#endif
- StringName id = _global_names_ptr[address];
-
- if (GDScriptLanguage::get_singleton()->get_named_globals_map().has(id)) {
- return (Variant *)&GDScriptLanguage::get_singleton()->get_named_globals_map()[id];
- } else {
- r_error = "Autoload singleton '" + String(id) + "' has been removed.";
- return nullptr;
- }
- } break;
-#endif
- case ADDR_TYPE_NIL: {
- return &nil;
- } break;
- }
-
- ERR_FAIL_V_MSG(nullptr, "Bad code! (unknown addressing mode).");
- return nullptr;
-}
-
-#ifdef DEBUG_ENABLED
-static String _get_var_type(const Variant *p_var) {
- String basestr;
-
- if (p_var->get_type() == Variant::OBJECT) {
- bool was_freed;
- Object *bobj = p_var->get_validated_object_with_check(was_freed);
- if (!bobj) {
- if (was_freed) {
- basestr = "null instance";
- } else {
- basestr = "previously freed";
- }
- } else {
- if (bobj->get_script_instance()) {
- basestr = bobj->get_class() + " (" + bobj->get_script_instance()->get_script()->get_path().get_file() + ")";
- } else {
- basestr = bobj->get_class();
- }
- }
-
- } else {
- basestr = Variant::get_type_name(p_var->get_type());
- }
-
- return basestr;
-}
-#endif // DEBUG_ENABLED
-
-String GDScriptFunction::_get_call_error(const Callable::CallError &p_err, const String &p_where, const Variant **argptrs) const {
- String err_text;
-
- if (p_err.error == Callable::CallError::CALL_ERROR_INVALID_ARGUMENT) {
- int errorarg = p_err.argument;
- // Handle the Object to Object case separately as we don't have further class details.
-#ifdef DEBUG_ENABLED
- if (p_err.expected == Variant::OBJECT && argptrs[errorarg]->get_type() == p_err.expected) {
- err_text = "Invalid type in " + p_where + ". The Object-derived class of argument " + itos(errorarg + 1) + " (" + _get_var_type(argptrs[errorarg]) + ") is not a subclass of the expected argument class.";
- } else
-#endif // DEBUG_ENABLED
- {
- err_text = "Invalid type in " + p_where + ". Cannot convert argument " + itos(errorarg + 1) + " from " + Variant::get_type_name(argptrs[errorarg]->get_type()) + " to " + Variant::get_type_name(Variant::Type(p_err.expected)) + ".";
- }
- } else if (p_err.error == Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS) {
- err_text = "Invalid call to " + p_where + ". Expected " + itos(p_err.argument) + " arguments.";
- } else if (p_err.error == Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS) {
- err_text = "Invalid call to " + p_where + ". Expected " + itos(p_err.argument) + " arguments.";
- } else if (p_err.error == Callable::CallError::CALL_ERROR_INVALID_METHOD) {
- err_text = "Invalid call. Nonexistent " + p_where + ".";
- } else if (p_err.error == Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL) {
- err_text = "Attempt to call " + p_where + " on a null instance.";
- } else {
- err_text = "Bug, call error: #" + itos(p_err.error);
- }
-
- return err_text;
-}
-
-#if defined(__GNUC__)
-#define OPCODES_TABLE \
- static const void *switch_table_ops[] = { \
- &&OPCODE_OPERATOR, \
- &&OPCODE_EXTENDS_TEST, \
- &&OPCODE_IS_BUILTIN, \
- &&OPCODE_SET, \
- &&OPCODE_GET, \
- &&OPCODE_SET_NAMED, \
- &&OPCODE_GET_NAMED, \
- &&OPCODE_SET_MEMBER, \
- &&OPCODE_GET_MEMBER, \
- &&OPCODE_ASSIGN, \
- &&OPCODE_ASSIGN_TRUE, \
- &&OPCODE_ASSIGN_FALSE, \
- &&OPCODE_ASSIGN_TYPED_BUILTIN, \
- &&OPCODE_ASSIGN_TYPED_NATIVE, \
- &&OPCODE_ASSIGN_TYPED_SCRIPT, \
- &&OPCODE_CAST_TO_BUILTIN, \
- &&OPCODE_CAST_TO_NATIVE, \
- &&OPCODE_CAST_TO_SCRIPT, \
- &&OPCODE_CONSTRUCT, \
- &&OPCODE_CONSTRUCT_ARRAY, \
- &&OPCODE_CONSTRUCT_DICTIONARY, \
- &&OPCODE_CALL, \
- &&OPCODE_CALL_RETURN, \
- &&OPCODE_CALL_ASYNC, \
- &&OPCODE_CALL_BUILT_IN, \
- &&OPCODE_CALL_SELF_BASE, \
- &&OPCODE_AWAIT, \
- &&OPCODE_AWAIT_RESUME, \
- &&OPCODE_JUMP, \
- &&OPCODE_JUMP_IF, \
- &&OPCODE_JUMP_IF_NOT, \
- &&OPCODE_JUMP_TO_DEF_ARGUMENT, \
- &&OPCODE_RETURN, \
- &&OPCODE_ITERATE_BEGIN, \
- &&OPCODE_ITERATE, \
- &&OPCODE_ASSERT, \
- &&OPCODE_BREAKPOINT, \
- &&OPCODE_LINE, \
- &&OPCODE_END \
- }; \
- static_assert((sizeof(switch_table_ops) / sizeof(switch_table_ops[0]) == (OPCODE_END + 1)), "Opcodes in jump table aren't the same as opcodes in enum.");
-
-#define OPCODE(m_op) \
- m_op:
-#define OPCODE_WHILE(m_test)
-#define OPCODES_END \
- OPSEXIT:
-#define OPCODES_OUT \
- OPSOUT:
-#define DISPATCH_OPCODE goto *switch_table_ops[_code_ptr[ip]]
-#define OPCODE_SWITCH(m_test) DISPATCH_OPCODE;
-#define OPCODE_BREAK goto OPSEXIT
-#define OPCODE_OUT goto OPSOUT
-#else
-#define OPCODES_TABLE
-#define OPCODE(m_op) case m_op:
-#define OPCODE_WHILE(m_test) while (m_test)
-#define OPCODES_END
-#define OPCODES_OUT
-#define DISPATCH_OPCODE continue
-#define OPCODE_SWITCH(m_test) switch (m_test)
-#define OPCODE_BREAK break
-#define OPCODE_OUT break
-#endif
-
-Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_args, int p_argcount, Callable::CallError &r_err, CallState *p_state) {
- OPCODES_TABLE;
-
- if (!_code_ptr) {
- return Variant();
- }
-
- r_err.error = Callable::CallError::CALL_OK;
-
- Variant self;
- Variant static_ref;
- Variant retvalue;
- Variant *stack = nullptr;
- Variant **call_args;
- int defarg = 0;
-
-#ifdef DEBUG_ENABLED
-
- //GDScriptLanguage::get_singleton()->calls++;
-
-#endif
-
- uint32_t alloca_size = 0;
- GDScript *script;
- int ip = 0;
- int line = _initial_line;
-
- if (p_state) {
- //use existing (supplied) state (awaited)
- stack = (Variant *)p_state->stack.ptr();
- call_args = (Variant **)&p_state->stack.ptr()[sizeof(Variant) * p_state->stack_size]; //ptr() to avoid bounds check
- line = p_state->line;
- ip = p_state->ip;
- alloca_size = p_state->stack.size();
- script = p_state->script;
- p_instance = p_state->instance;
- defarg = p_state->defarg;
- self = p_state->self;
-
- } else {
- if (p_argcount != _argument_count) {
- if (p_argcount > _argument_count) {
- r_err.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS;
- r_err.argument = _argument_count;
-
- return Variant();
- } else if (p_argcount < _argument_count - _default_arg_count) {
- r_err.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
- r_err.argument = _argument_count - _default_arg_count;
- return Variant();
- } else {
- defarg = _argument_count - p_argcount;
- }
- }
-
- alloca_size = sizeof(Variant *) * _call_size + sizeof(Variant) * _stack_size;
-
- if (alloca_size) {
- uint8_t *aptr = (uint8_t *)alloca(alloca_size);
-
- if (_stack_size) {
- stack = (Variant *)aptr;
- for (int i = 0; i < p_argcount; i++) {
- if (!argument_types[i].has_type) {
- memnew_placement(&stack[i], Variant(*p_args[i]));
- continue;
- }
-
- if (!argument_types[i].is_type(*p_args[i], true)) {
- r_err.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_err.argument = i;
- r_err.expected = argument_types[i].kind == GDScriptDataType::BUILTIN ? argument_types[i].builtin_type : Variant::OBJECT;
- return Variant();
- }
- if (argument_types[i].kind == GDScriptDataType::BUILTIN) {
- Variant arg;
- Variant::construct(argument_types[i].builtin_type, arg, &p_args[i], 1, r_err);
- memnew_placement(&stack[i], Variant(arg));
- } else {
- memnew_placement(&stack[i], Variant(*p_args[i]));
- }
- }
- for (int i = p_argcount; i < _stack_size; i++) {
- memnew_placement(&stack[i], Variant);
- }
- } else {
- stack = nullptr;
- }
-
- if (_call_size) {
- call_args = (Variant **)&aptr[sizeof(Variant) * _stack_size];
- } else {
- call_args = nullptr;
- }
-
- } else {
- stack = nullptr;
- call_args = nullptr;
- }
-
- if (p_instance) {
- if (p_instance->base_ref && static_cast<Reference *>(p_instance->owner)->is_referenced()) {
- self = REF(static_cast<Reference *>(p_instance->owner));
- } else {
- self = p_instance->owner;
- }
- script = p_instance->script.ptr();
- } else {
- script = _script;
- }
- }
-
- static_ref = script;
-
- String err_text;
-
-#ifdef DEBUG_ENABLED
-
- if (EngineDebugger::is_active()) {
- GDScriptLanguage::get_singleton()->enter_function(p_instance, this, stack, &ip, &line);
- }
-
-#define GD_ERR_BREAK(m_cond) \
- { \
- if (unlikely(m_cond)) { \
- _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition ' " _STR(m_cond) " ' is true. Breaking..:"); \
- OPCODE_BREAK; \
- } \
- }
-
-#define CHECK_SPACE(m_space) \
- GD_ERR_BREAK((ip + m_space) > _code_size)
-
-#define GET_VARIANT_PTR(m_v, m_code_ofs) \
- Variant *m_v; \
- m_v = _get_variant(_code_ptr[ip + m_code_ofs], p_instance, script, self, static_ref, stack, err_text); \
- if (unlikely(!m_v)) \
- OPCODE_BREAK;
-
-#else
-#define GD_ERR_BREAK(m_cond)
-#define CHECK_SPACE(m_space)
-#define GET_VARIANT_PTR(m_v, m_code_ofs) \
- Variant *m_v; \
- m_v = _get_variant(_code_ptr[ip + m_code_ofs], p_instance, script, self, static_ref, stack, err_text);
-
-#endif
-
-#ifdef DEBUG_ENABLED
-
- uint64_t function_start_time = 0;
- uint64_t function_call_time = 0;
-
- if (GDScriptLanguage::get_singleton()->profiling) {
- function_start_time = OS::get_singleton()->get_ticks_usec();
- function_call_time = 0;
- profile.call_count++;
- profile.frame_call_count++;
- }
- bool exit_ok = false;
- bool awaited = false;
-#endif
-
-#ifdef DEBUG_ENABLED
- OPCODE_WHILE(ip < _code_size) {
- int last_opcode = _code_ptr[ip];
-#else
- OPCODE_WHILE(true) {
-#endif
-
- OPCODE_SWITCH(_code_ptr[ip]) {
- OPCODE(OPCODE_OPERATOR) {
- CHECK_SPACE(5);
-
- bool valid;
- Variant::Operator op = (Variant::Operator)_code_ptr[ip + 1];
- GD_ERR_BREAK(op >= Variant::OP_MAX);
-
- GET_VARIANT_PTR(a, 2);
- GET_VARIANT_PTR(b, 3);
- GET_VARIANT_PTR(dst, 4);
-
-#ifdef DEBUG_ENABLED
-
- Variant ret;
- Variant::evaluate(op, *a, *b, ret, valid);
-#else
- Variant::evaluate(op, *a, *b, *dst, valid);
-#endif
-#ifdef DEBUG_ENABLED
- if (!valid) {
- if (ret.get_type() == Variant::STRING) {
- //return a string when invalid with the error
- err_text = ret;
- err_text += " in operator '" + Variant::get_operator_name(op) + "'.";
- } else {
- err_text = "Invalid operands '" + Variant::get_type_name(a->get_type()) + "' and '" + Variant::get_type_name(b->get_type()) + "' in operator '" + Variant::get_operator_name(op) + "'.";
- }
- OPCODE_BREAK;
- }
- *dst = ret;
-#endif
- ip += 5;
- }
- DISPATCH_OPCODE;
-
- OPCODE(OPCODE_EXTENDS_TEST) {
- CHECK_SPACE(4);
-
- GET_VARIANT_PTR(a, 1);
- GET_VARIANT_PTR(b, 2);
- GET_VARIANT_PTR(dst, 3);
-
-#ifdef DEBUG_ENABLED
- if (b->get_type() != Variant::OBJECT || b->operator Object *() == nullptr) {
- err_text = "Right operand of 'is' is not a class.";
- OPCODE_BREAK;
- }
-#endif
-
- bool extends_ok = false;
- if (a->get_type() == Variant::OBJECT && a->operator Object *() != nullptr) {
-#ifdef DEBUG_ENABLED
- bool was_freed;
- Object *obj_A = a->get_validated_object_with_check(was_freed);
-
- if (was_freed) {
- err_text = "Left operand of 'is' is a previously freed instance.";
- OPCODE_BREAK;
- }
-
- Object *obj_B = b->get_validated_object_with_check(was_freed);
-
- if (was_freed) {
- err_text = "Right operand of 'is' is a previously freed instance.";
- OPCODE_BREAK;
- }
-#else
-
- Object *obj_A = *a;
- Object *obj_B = *b;
-#endif // DEBUG_ENABLED
-
- GDScript *scr_B = Object::cast_to<GDScript>(obj_B);
-
- if (scr_B) {
- //if B is a script, the only valid condition is that A has an instance which inherits from the script
- //in other situation, this shoul return false.
-
- if (obj_A->get_script_instance() && obj_A->get_script_instance()->get_language() == GDScriptLanguage::get_singleton()) {
- GDScript *cmp = static_cast<GDScript *>(obj_A->get_script_instance()->get_script().ptr());
- //bool found=false;
- while (cmp) {
- if (cmp == scr_B) {
- //inherits from script, all ok
- extends_ok = true;
- break;
- }
-
- cmp = cmp->_base;
- }
- }
-
- } else {
- GDScriptNativeClass *nc = Object::cast_to<GDScriptNativeClass>(obj_B);
-
-#ifdef DEBUG_ENABLED
- if (!nc) {
- err_text = "Right operand of 'is' is not a class (type: '" + obj_B->get_class() + "').";
- OPCODE_BREAK;
- }
-#endif
- extends_ok = ClassDB::is_parent_class(obj_A->get_class_name(), nc->get_name());
- }
- }
-
- *dst = extends_ok;
- ip += 4;
- }
- DISPATCH_OPCODE;
-
- OPCODE(OPCODE_IS_BUILTIN) {
- CHECK_SPACE(4);
-
- GET_VARIANT_PTR(value, 1);
- Variant::Type var_type = (Variant::Type)_code_ptr[ip + 2];
- GET_VARIANT_PTR(dst, 3);
-
- GD_ERR_BREAK(var_type < 0 || var_type >= Variant::VARIANT_MAX);
-
- *dst = value->get_type() == var_type;
- ip += 4;
- }
- DISPATCH_OPCODE;
-
- OPCODE(OPCODE_SET) {
- CHECK_SPACE(3);
-
- GET_VARIANT_PTR(dst, 1);
- GET_VARIANT_PTR(index, 2);
- GET_VARIANT_PTR(value, 3);
-
- bool valid;
- dst->set(*index, *value, &valid);
-
-#ifdef DEBUG_ENABLED
- if (!valid) {
- String v = index->operator String();
- if (v != "") {
- v = "'" + v + "'";
- } else {
- v = "of type '" + _get_var_type(index) + "'";
- }
- err_text = "Invalid set index " + v + " (on base: '" + _get_var_type(dst) + "') with value of type '" + _get_var_type(value) + "'";
- OPCODE_BREAK;
- }
-#endif
- ip += 4;
- }
- DISPATCH_OPCODE;
-
- OPCODE(OPCODE_GET) {
- CHECK_SPACE(3);
-
- GET_VARIANT_PTR(src, 1);
- GET_VARIANT_PTR(index, 2);
- GET_VARIANT_PTR(dst, 3);
-
- bool valid;
-#ifdef DEBUG_ENABLED
- //allow better error message in cases where src and dst are the same stack position
- Variant ret = src->get(*index, &valid);
-#else
- *dst = src->get(*index, &valid);
-
-#endif
-#ifdef DEBUG_ENABLED
- if (!valid) {
- String v = index->operator String();
- if (v != "") {
- v = "'" + v + "'";
- } else {
- v = "of type '" + _get_var_type(index) + "'";
- }
- err_text = "Invalid get index " + v + " (on base: '" + _get_var_type(src) + "').";
- OPCODE_BREAK;
- }
- *dst = ret;
-#endif
- ip += 4;
- }
- DISPATCH_OPCODE;
-
- OPCODE(OPCODE_SET_NAMED) {
- CHECK_SPACE(3);
-
- GET_VARIANT_PTR(dst, 1);
- GET_VARIANT_PTR(value, 3);
-
- int indexname = _code_ptr[ip + 2];
-
- GD_ERR_BREAK(indexname < 0 || indexname >= _global_names_count);
- const StringName *index = &_global_names_ptr[indexname];
-
- bool valid;
- dst->set_named(*index, *value, valid);
-
-#ifdef DEBUG_ENABLED
- if (!valid) {
- String err_type;
- err_text = "Invalid set index '" + String(*index) + "' (on base: '" + _get_var_type(dst) + "') with value of type '" + _get_var_type(value) + "'.";
- OPCODE_BREAK;
- }
-#endif
- ip += 4;
- }
- DISPATCH_OPCODE;
-
- OPCODE(OPCODE_GET_NAMED) {
- CHECK_SPACE(4);
-
- GET_VARIANT_PTR(src, 1);
- GET_VARIANT_PTR(dst, 3);
-
- int indexname = _code_ptr[ip + 2];
-
- GD_ERR_BREAK(indexname < 0 || indexname >= _global_names_count);
- const StringName *index = &_global_names_ptr[indexname];
-
- bool valid;
-#ifdef DEBUG_ENABLED
- //allow better error message in cases where src and dst are the same stack position
- Variant ret = src->get_named(*index, valid);
-
-#else
- *dst = src->get_named(*index, valid);
-#endif
-#ifdef DEBUG_ENABLED
- if (!valid) {
- if (src->has_method(*index)) {
- err_text = "Invalid get index '" + index->operator String() + "' (on base: '" + _get_var_type(src) + "'). Did you mean '." + index->operator String() + "()' or funcref(obj, \"" + index->operator String() + "\") ?";
- } else {
- err_text = "Invalid get index '" + index->operator String() + "' (on base: '" + _get_var_type(src) + "').";
- }
- OPCODE_BREAK;
- }
- *dst = ret;
-#endif
- ip += 4;
- }
- DISPATCH_OPCODE;
-
- OPCODE(OPCODE_SET_MEMBER) {
- CHECK_SPACE(3);
- int indexname = _code_ptr[ip + 1];
- GD_ERR_BREAK(indexname < 0 || indexname >= _global_names_count);
- const StringName *index = &_global_names_ptr[indexname];
- GET_VARIANT_PTR(src, 2);
-
- bool valid;
-#ifndef DEBUG_ENABLED
- ClassDB::set_property(p_instance->owner, *index, *src, &valid);
-#else
- bool ok = ClassDB::set_property(p_instance->owner, *index, *src, &valid);
- if (!ok) {
- err_text = "Internal error setting property: " + String(*index);
- OPCODE_BREAK;
- } else if (!valid) {
- err_text = "Error setting property '" + String(*index) + "' with value of type " + Variant::get_type_name(src->get_type()) + ".";
- OPCODE_BREAK;
- }
-#endif
- ip += 3;
- }
- DISPATCH_OPCODE;
-
- OPCODE(OPCODE_GET_MEMBER) {
- CHECK_SPACE(3);
- int indexname = _code_ptr[ip + 1];
- GD_ERR_BREAK(indexname < 0 || indexname >= _global_names_count);
- const StringName *index = &_global_names_ptr[indexname];
- GET_VARIANT_PTR(dst, 2);
-
-#ifndef DEBUG_ENABLED
- ClassDB::get_property(p_instance->owner, *index, *dst);
-#else
- bool ok = ClassDB::get_property(p_instance->owner, *index, *dst);
- if (!ok) {
- err_text = "Internal error getting property: " + String(*index);
- OPCODE_BREAK;
- }
-#endif
- ip += 3;
- }
- DISPATCH_OPCODE;
-
- OPCODE(OPCODE_ASSIGN) {
- CHECK_SPACE(3);
- GET_VARIANT_PTR(dst, 1);
- GET_VARIANT_PTR(src, 2);
-
- *dst = *src;
-
- ip += 3;
- }
- DISPATCH_OPCODE;
-
- OPCODE(OPCODE_ASSIGN_TRUE) {
- CHECK_SPACE(2);
- GET_VARIANT_PTR(dst, 1);
-
- *dst = true;
-
- ip += 2;
- }
- DISPATCH_OPCODE;
-
- OPCODE(OPCODE_ASSIGN_FALSE) {
- CHECK_SPACE(2);
- GET_VARIANT_PTR(dst, 1);
-
- *dst = false;
-
- ip += 2;
- }
- DISPATCH_OPCODE;
-
- OPCODE(OPCODE_ASSIGN_TYPED_BUILTIN) {
- CHECK_SPACE(4);
- GET_VARIANT_PTR(dst, 2);
- GET_VARIANT_PTR(src, 3);
-
- Variant::Type var_type = (Variant::Type)_code_ptr[ip + 1];
- GD_ERR_BREAK(var_type < 0 || var_type >= Variant::VARIANT_MAX);
-
- if (src->get_type() != var_type) {
-#ifdef DEBUG_ENABLED
- if (Variant::can_convert_strict(src->get_type(), var_type)) {
-#endif // DEBUG_ENABLED
- Callable::CallError ce;
- Variant::construct(var_type, *dst, const_cast<const Variant **>(&src), 1, ce);
- } else {
-#ifdef DEBUG_ENABLED
- err_text = "Trying to assign value of type '" + Variant::get_type_name(src->get_type()) +
- "' to a variable of type '" + Variant::get_type_name(var_type) + "'.";
- OPCODE_BREAK;
- }
- } else {
-#endif // DEBUG_ENABLED
- *dst = *src;
- }
-
- ip += 4;
- }
- DISPATCH_OPCODE;
-
- OPCODE(OPCODE_ASSIGN_TYPED_NATIVE) {
- CHECK_SPACE(4);
- GET_VARIANT_PTR(dst, 2);
- GET_VARIANT_PTR(src, 3);
-
-#ifdef DEBUG_ENABLED
- GET_VARIANT_PTR(type, 1);
- GDScriptNativeClass *nc = Object::cast_to<GDScriptNativeClass>(type->operator Object *());
- GD_ERR_BREAK(!nc);
- if (src->get_type() != Variant::OBJECT && src->get_type() != Variant::NIL) {
- err_text = "Trying to assign value of type '" + Variant::get_type_name(src->get_type()) +
- "' to a variable of type '" + nc->get_name() + "'.";
- OPCODE_BREAK;
- }
- Object *src_obj = src->operator Object *();
-
- if (src_obj && !ClassDB::is_parent_class(src_obj->get_class_name(), nc->get_name())) {
- err_text = "Trying to assign value of type '" + src_obj->get_class_name() +
- "' to a variable of type '" + nc->get_name() + "'.";
- OPCODE_BREAK;
- }
-#endif // DEBUG_ENABLED
- *dst = *src;
-
- ip += 4;
- }
- DISPATCH_OPCODE;
-
- OPCODE(OPCODE_ASSIGN_TYPED_SCRIPT) {
- CHECK_SPACE(4);
- GET_VARIANT_PTR(dst, 2);
- GET_VARIANT_PTR(src, 3);
-
-#ifdef DEBUG_ENABLED
- GET_VARIANT_PTR(type, 1);
- Script *base_type = Object::cast_to<Script>(type->operator Object *());
-
- GD_ERR_BREAK(!base_type);
-
- if (src->get_type() != Variant::OBJECT && src->get_type() != Variant::NIL) {
- err_text = "Trying to assign a non-object value to a variable of type '" + base_type->get_path().get_file() + "'.";
- OPCODE_BREAK;
- }
-
- if (src->get_type() != Variant::NIL && src->operator Object *() != nullptr) {
- ScriptInstance *scr_inst = src->operator Object *()->get_script_instance();
- if (!scr_inst) {
- err_text = "Trying to assign value of type '" + src->operator Object *()->get_class_name() +
- "' to a variable of type '" + base_type->get_path().get_file() + "'.";
- OPCODE_BREAK;
- }
-
- Script *src_type = src->operator Object *()->get_script_instance()->get_script().ptr();
- bool valid = false;
-
- while (src_type) {
- if (src_type == base_type) {
- valid = true;
- break;
- }
- src_type = src_type->get_base_script().ptr();
- }
-
- if (!valid) {
- err_text = "Trying to assign value of type '" + src->operator Object *()->get_script_instance()->get_script()->get_path().get_file() +
- "' to a variable of type '" + base_type->get_path().get_file() + "'.";
- OPCODE_BREAK;
- }
- }
-#endif // DEBUG_ENABLED
-
- *dst = *src;
-
- ip += 4;
- }
- DISPATCH_OPCODE;
-
- OPCODE(OPCODE_CAST_TO_BUILTIN) {
- CHECK_SPACE(4);
- Variant::Type to_type = (Variant::Type)_code_ptr[ip + 1];
- GET_VARIANT_PTR(src, 2);
- GET_VARIANT_PTR(dst, 3);
-
- GD_ERR_BREAK(to_type < 0 || to_type >= Variant::VARIANT_MAX);
-
- Callable::CallError err;
- Variant::construct(to_type, *dst, (const Variant **)&src, 1, err);
-
-#ifdef DEBUG_ENABLED
- if (err.error != Callable::CallError::CALL_OK) {
- err_text = "Invalid cast: could not convert value to '" + Variant::get_type_name(to_type) + "'.";
- OPCODE_BREAK;
- }
-#endif
-
- ip += 4;
- }
- DISPATCH_OPCODE;
-
- OPCODE(OPCODE_CAST_TO_NATIVE) {
- CHECK_SPACE(4);
- GET_VARIANT_PTR(to_type, 1);
- GET_VARIANT_PTR(src, 2);
- GET_VARIANT_PTR(dst, 3);
-
- GDScriptNativeClass *nc = Object::cast_to<GDScriptNativeClass>(to_type->operator Object *());
- GD_ERR_BREAK(!nc);
-
-#ifdef DEBUG_ENABLED
- if (src->get_type() != Variant::OBJECT && src->get_type() != Variant::NIL) {
- err_text = "Invalid cast: can't convert a non-object value to an object type.";
- OPCODE_BREAK;
- }
-#endif
- Object *src_obj = src->operator Object *();
-
- if (src_obj && !ClassDB::is_parent_class(src_obj->get_class_name(), nc->get_name())) {
- *dst = Variant(); // invalid cast, assign NULL
- } else {
- *dst = *src;
- }
-
- ip += 4;
- }
- DISPATCH_OPCODE;
-
- OPCODE(OPCODE_CAST_TO_SCRIPT) {
- CHECK_SPACE(4);
- GET_VARIANT_PTR(to_type, 1);
- GET_VARIANT_PTR(src, 2);
- GET_VARIANT_PTR(dst, 3);
-
- Script *base_type = Object::cast_to<Script>(to_type->operator Object *());
-
- GD_ERR_BREAK(!base_type);
-
-#ifdef DEBUG_ENABLED
- if (src->get_type() != Variant::OBJECT && src->get_type() != Variant::NIL) {
- err_text = "Trying to assign a non-object value to a variable of type '" + base_type->get_path().get_file() + "'.";
- OPCODE_BREAK;
- }
-#endif
-
- bool valid = false;
-
- if (src->get_type() != Variant::NIL && src->operator Object *() != nullptr) {
- ScriptInstance *scr_inst = src->operator Object *()->get_script_instance();
-
- if (scr_inst) {
- Script *src_type = src->operator Object *()->get_script_instance()->get_script().ptr();
-
- while (src_type) {
- if (src_type == base_type) {
- valid = true;
- break;
- }
- src_type = src_type->get_base_script().ptr();
- }
- }
- }
-
- if (valid) {
- *dst = *src; // Valid cast, copy the source object
- } else {
- *dst = Variant(); // invalid cast, assign NULL
- }
-
- ip += 4;
- }
- DISPATCH_OPCODE;
-
- OPCODE(OPCODE_CONSTRUCT) {
- CHECK_SPACE(2);
- Variant::Type t = Variant::Type(_code_ptr[ip + 1]);
- int argc = _code_ptr[ip + 2];
- CHECK_SPACE(argc + 2);
- Variant **argptrs = call_args;
- for (int i = 0; i < argc; i++) {
- GET_VARIANT_PTR(v, 3 + i);
- argptrs[i] = v;
- }
-
- GET_VARIANT_PTR(dst, 3 + argc);
- Callable::CallError err;
- Variant::construct(t, *dst, (const Variant **)argptrs, argc, err);
-
-#ifdef DEBUG_ENABLED
- if (err.error != Callable::CallError::CALL_OK) {
- err_text = _get_call_error(err, "'" + Variant::get_type_name(t) + "' constructor", (const Variant **)argptrs);
- OPCODE_BREAK;
- }
-#endif
-
- ip += 4 + argc;
- //construct a basic type
- }
- DISPATCH_OPCODE;
-
- OPCODE(OPCODE_CONSTRUCT_ARRAY) {
- CHECK_SPACE(1);
- int argc = _code_ptr[ip + 1];
- Array array; //arrays are always shared
- array.resize(argc);
- CHECK_SPACE(argc + 2);
-
- for (int i = 0; i < argc; i++) {
- GET_VARIANT_PTR(v, 2 + i);
- array[i] = *v;
- }
-
- GET_VARIANT_PTR(dst, 2 + argc);
-
- *dst = array;
-
- ip += 3 + argc;
- }
- DISPATCH_OPCODE;
-
- OPCODE(OPCODE_CONSTRUCT_DICTIONARY) {
- CHECK_SPACE(1);
- int argc = _code_ptr[ip + 1];
- Dictionary dict; //arrays are always shared
-
- CHECK_SPACE(argc * 2 + 2);
-
- for (int i = 0; i < argc; i++) {
- GET_VARIANT_PTR(k, 2 + i * 2 + 0);
- GET_VARIANT_PTR(v, 2 + i * 2 + 1);
- dict[*k] = *v;
- }
-
- GET_VARIANT_PTR(dst, 2 + argc * 2);
-
- *dst = dict;
-
- ip += 3 + argc * 2;
- }
- DISPATCH_OPCODE;
-
- OPCODE(OPCODE_CALL_ASYNC)
- OPCODE(OPCODE_CALL_RETURN)
- OPCODE(OPCODE_CALL) {
- CHECK_SPACE(4);
- bool call_ret = _code_ptr[ip] != OPCODE_CALL;
-#ifdef DEBUG_ENABLED
- bool call_async = _code_ptr[ip] == OPCODE_CALL_ASYNC;
-#endif
-
- int argc = _code_ptr[ip + 1];
- GET_VARIANT_PTR(base, 2);
- int nameg = _code_ptr[ip + 3];
-
- GD_ERR_BREAK(nameg < 0 || nameg >= _global_names_count);
- const StringName *methodname = &_global_names_ptr[nameg];
-
- GD_ERR_BREAK(argc < 0);
- ip += 4;
- CHECK_SPACE(argc + 1);
- Variant **argptrs = call_args;
-
- for (int i = 0; i < argc; i++) {
- GET_VARIANT_PTR(v, i);
- argptrs[i] = v;
- }
-
-#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;
- if (call_ret) {
- GET_VARIANT_PTR(ret, argc);
- base->call(*methodname, (const Variant **)argptrs, argc, *ret, err);
-#ifdef DEBUG_ENABLED
- if (!call_async && ret->get_type() == Variant::OBJECT) {
- // Check if getting a function state without await.
- bool was_freed = false;
- Object *obj = ret->get_validated_object_with_check(was_freed);
-
- if (was_freed) {
- err_text = "Got a freed object as a result of the call.";
- OPCODE_BREAK;
- }
- if (obj && obj->is_class_ptr(GDScriptFunctionState::get_class_ptr_static())) {
- err_text = R"(Trying to call an async function without "await".)";
- OPCODE_BREAK;
- }
- }
-#endif
- } else {
- Variant ret;
- base->call(*methodname, (const Variant **)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) {
- String methodstr = *methodname;
- String basestr = _get_var_type(base);
-
- if (methodstr == "call") {
- if (argc >= 1) {
- methodstr = String(*argptrs[0]) + " (via call)";
- if (err.error == Callable::CallError::CALL_ERROR_INVALID_ARGUMENT) {
- err.argument += 1;
- }
- }
- } else if (methodstr == "free") {
- if (err.error == Callable::CallError::CALL_ERROR_INVALID_METHOD) {
- if (base->is_ref()) {
- err_text = "Attempted to free a reference.";
- OPCODE_BREAK;
- } else if (base->get_type() == Variant::OBJECT) {
- err_text = "Attempted to free a locked object (calling or emitting).";
- OPCODE_BREAK;
- }
- }
- } else if (methodstr == "call_recursive" && basestr == "TreeItem") {
- if (argc >= 1) {
- methodstr = String(*argptrs[0]) + " (via TreeItem.call_recursive)";
- if (err.error == Callable::CallError::CALL_ERROR_INVALID_ARGUMENT) {
- err.argument += 1;
- }
- }
- }
- err_text = _get_call_error(err, "function '" + methodstr + "' in base '" + basestr + "'", (const Variant **)argptrs);
- OPCODE_BREAK;
- }
-#endif
-
- //_call_func(nullptr,base,*methodname,ip,argc,p_instance,stack);
- ip += argc + 1;
- }
- DISPATCH_OPCODE;
-
- OPCODE(OPCODE_CALL_BUILT_IN) {
- CHECK_SPACE(4);
-
- GDScriptFunctions::Function func = GDScriptFunctions::Function(_code_ptr[ip + 1]);
- int argc = _code_ptr[ip + 2];
- GD_ERR_BREAK(argc < 0);
-
- ip += 3;
- CHECK_SPACE(argc + 1);
- Variant **argptrs = call_args;
-
- for (int i = 0; i < argc; i++) {
- GET_VARIANT_PTR(v, i);
- argptrs[i] = v;
- }
-
- GET_VARIANT_PTR(dst, argc);
-
- Callable::CallError err;
-
- GDScriptFunctions::call(func, (const Variant **)argptrs, argc, *dst, err);
-
-#ifdef DEBUG_ENABLED
- if (err.error != Callable::CallError::CALL_OK) {
- String methodstr = GDScriptFunctions::get_func_name(func);
- if (dst->get_type() == Variant::STRING) {
- //call provided error string
- err_text = "Error calling built-in function '" + methodstr + "': " + String(*dst);
- } else {
- err_text = _get_call_error(err, "built-in function '" + methodstr + "'", (const Variant **)argptrs);
- }
- OPCODE_BREAK;
- }
-#endif
- ip += argc + 1;
- }
- DISPATCH_OPCODE;
-
- OPCODE(OPCODE_CALL_SELF_BASE) {
- CHECK_SPACE(2);
- int self_fun = _code_ptr[ip + 1];
-
-#ifdef DEBUG_ENABLED
- if (self_fun < 0 || self_fun >= _global_names_count) {
- err_text = "compiler bug, function name not found";
- OPCODE_BREAK;
- }
-#endif
- const StringName *methodname = &_global_names_ptr[self_fun];
-
- int argc = _code_ptr[ip + 2];
-
- CHECK_SPACE(2 + argc + 1);
-
- Variant **argptrs = call_args;
-
- for (int i = 0; i < argc; i++) {
- GET_VARIANT_PTR(v, i + 3);
- argptrs[i] = v;
- }
-
- GET_VARIANT_PTR(dst, argc + 3);
-
- const GDScript *gds = _script;
-
- const Map<StringName, GDScriptFunction *>::Element *E = nullptr;
- while (gds->base.ptr()) {
- gds = gds->base.ptr();
- E = gds->member_functions.find(*methodname);
- if (E) {
- break;
- }
- }
-
- Callable::CallError err;
-
- if (E) {
- *dst = E->get()->call(p_instance, (const Variant **)argptrs, argc, err);
- } else if (gds->native.ptr()) {
- if (*methodname != GDScriptLanguage::get_singleton()->strings._init) {
- MethodBind *mb = ClassDB::get_method(gds->native->get_name(), *methodname);
- if (!mb) {
- err.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
- } else {
- *dst = mb->call(p_instance->owner, (const Variant **)argptrs, argc, err);
- }
- } else {
- err.error = Callable::CallError::CALL_OK;
- }
- } else {
- if (*methodname != GDScriptLanguage::get_singleton()->strings._init) {
- err.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
- } else {
- err.error = Callable::CallError::CALL_OK;
- }
- }
-
- if (err.error != Callable::CallError::CALL_OK) {
- String methodstr = *methodname;
- err_text = _get_call_error(err, "function '" + methodstr + "'", (const Variant **)argptrs);
-
- OPCODE_BREAK;
- }
-
- ip += 4 + argc;
- }
- DISPATCH_OPCODE;
-
- OPCODE(OPCODE_AWAIT) {
- CHECK_SPACE(2);
-
- //do the oneshot connect
- GET_VARIANT_PTR(argobj, 1);
-
- Signal sig;
- bool is_signal = true;
-
- {
- Variant result = *argobj;
-
- if (argobj->get_type() == Variant::OBJECT) {
- bool was_freed = false;
- Object *obj = argobj->get_validated_object_with_check(was_freed);
-
- if (was_freed) {
- err_text = "Trying to await on a freed object.";
- OPCODE_BREAK;
- }
-
- // Is this even possible to be null at this point?
- if (obj) {
- if (obj->is_class_ptr(GDScriptFunctionState::get_class_ptr_static())) {
- static StringName completed = _scs_create("completed");
- result = Signal(obj, completed);
- }
- }
- }
-
- if (result.get_type() != Variant::SIGNAL) {
- ip += 4; // Skip OPCODE_AWAIT_RESUME and its data.
- // The stack pointer should be the same, so we don't need to set a return value.
- is_signal = false;
- } else {
- sig = result;
- }
- }
-
- if (is_signal) {
- Ref<GDScriptFunctionState> gdfs = memnew(GDScriptFunctionState);
- gdfs->function = this;
-
- gdfs->state.stack.resize(alloca_size);
- //copy variant stack
- for (int i = 0; i < _stack_size; i++) {
- memnew_placement(&gdfs->state.stack.write[sizeof(Variant) * i], Variant(stack[i]));
- }
- gdfs->state.stack_size = _stack_size;
- gdfs->state.self = self;
- gdfs->state.alloca_size = alloca_size;
- gdfs->state.ip = ip + 2;
- gdfs->state.line = line;
- gdfs->state.script = _script;
- {
- MutexLock lock(GDScriptLanguage::get_singleton()->lock);
- _script->pending_func_states.add(&gdfs->scripts_list);
- if (p_instance) {
- gdfs->state.instance = p_instance;
- p_instance->pending_func_states.add(&gdfs->instances_list);
- } else {
- gdfs->state.instance = nullptr;
- }
- }
-#ifdef DEBUG_ENABLED
- gdfs->state.function_name = name;
- gdfs->state.script_path = _script->get_path();
-#endif
- gdfs->state.defarg = defarg;
- gdfs->function = this;
-
- retvalue = gdfs;
-
- Error err = sig.connect(Callable(gdfs.ptr(), "_signal_callback"), varray(gdfs), Object::CONNECT_ONESHOT);
- if (err != OK) {
- err_text = "Error connecting to signal: " + sig.get_name() + " during await.";
- OPCODE_BREAK;
- }
-
-#ifdef DEBUG_ENABLED
- exit_ok = true;
- awaited = true;
-#endif
- OPCODE_BREAK;
- }
- }
- DISPATCH_OPCODE; // Needed for synchronous calls (when result is immediately available).
-
- OPCODE(OPCODE_AWAIT_RESUME) {
- CHECK_SPACE(2);
-#ifdef DEBUG_ENABLED
- if (!p_state) {
- err_text = ("Invalid Resume (bug?)");
- OPCODE_BREAK;
- }
-#endif
- GET_VARIANT_PTR(result, 1);
- *result = p_state->result;
- ip += 2;
- }
- DISPATCH_OPCODE;
-
- OPCODE(OPCODE_JUMP) {
- CHECK_SPACE(2);
- int to = _code_ptr[ip + 1];
-
- GD_ERR_BREAK(to < 0 || to > _code_size);
- ip = to;
- }
- DISPATCH_OPCODE;
-
- OPCODE(OPCODE_JUMP_IF) {
- CHECK_SPACE(3);
-
- GET_VARIANT_PTR(test, 1);
-
- bool result = test->booleanize();
-
- if (result) {
- int to = _code_ptr[ip + 2];
- GD_ERR_BREAK(to < 0 || to > _code_size);
- ip = to;
- } else {
- ip += 3;
- }
- }
- DISPATCH_OPCODE;
-
- OPCODE(OPCODE_JUMP_IF_NOT) {
- CHECK_SPACE(3);
-
- GET_VARIANT_PTR(test, 1);
-
- bool result = test->booleanize();
-
- if (!result) {
- int to = _code_ptr[ip + 2];
- GD_ERR_BREAK(to < 0 || to > _code_size);
- ip = to;
- } else {
- ip += 3;
- }
- }
- DISPATCH_OPCODE;
-
- OPCODE(OPCODE_JUMP_TO_DEF_ARGUMENT) {
- CHECK_SPACE(2);
- ip = _default_arg_ptr[defarg];
- }
- DISPATCH_OPCODE;
-
- OPCODE(OPCODE_RETURN) {
- CHECK_SPACE(2);
- GET_VARIANT_PTR(r, 1);
- retvalue = *r;
-#ifdef DEBUG_ENABLED
- exit_ok = true;
-#endif
- OPCODE_BREAK;
- }
-
- OPCODE(OPCODE_ITERATE_BEGIN) {
- CHECK_SPACE(8); //space for this a regular iterate
-
- GET_VARIANT_PTR(counter, 1);
- GET_VARIANT_PTR(container, 2);
-
- bool valid;
- if (!container->iter_init(*counter, valid)) {
-#ifdef DEBUG_ENABLED
- if (!valid) {
- err_text = "Unable to iterate on object of type '" + Variant::get_type_name(container->get_type()) + "'.";
- OPCODE_BREAK;
- }
-#endif
- int jumpto = _code_ptr[ip + 3];
- GD_ERR_BREAK(jumpto < 0 || jumpto > _code_size);
- ip = jumpto;
- } else {
- GET_VARIANT_PTR(iterator, 4);
-
- *iterator = container->iter_get(*counter, valid);
-#ifdef DEBUG_ENABLED
- if (!valid) {
- err_text = "Unable to obtain iterator object of type '" + Variant::get_type_name(container->get_type()) + "'.";
- OPCODE_BREAK;
- }
-#endif
- ip += 5; //skip regular iterate which is always next
- }
- }
- DISPATCH_OPCODE;
-
- OPCODE(OPCODE_ITERATE) {
- CHECK_SPACE(4);
-
- GET_VARIANT_PTR(counter, 1);
- GET_VARIANT_PTR(container, 2);
-
- bool valid;
- if (!container->iter_next(*counter, valid)) {
-#ifdef DEBUG_ENABLED
- if (!valid) {
- err_text = "Unable to iterate on object of type '" + Variant::get_type_name(container->get_type()) + "' (type changed since first iteration?).";
- OPCODE_BREAK;
- }
-#endif
- int jumpto = _code_ptr[ip + 3];
- GD_ERR_BREAK(jumpto < 0 || jumpto > _code_size);
- ip = jumpto;
- } else {
- GET_VARIANT_PTR(iterator, 4);
-
- *iterator = container->iter_get(*counter, valid);
-#ifdef DEBUG_ENABLED
- if (!valid) {
- err_text = "Unable to obtain iterator object of type '" + Variant::get_type_name(container->get_type()) + "' (but was obtained on first iteration?).";
- OPCODE_BREAK;
- }
-#endif
- ip += 5; //loop again
- }
- }
- DISPATCH_OPCODE;
-
- OPCODE(OPCODE_ASSERT) {
- CHECK_SPACE(3);
-
-#ifdef DEBUG_ENABLED
- GET_VARIANT_PTR(test, 1);
- bool result = test->booleanize();
-
- if (!result) {
- String message_str;
- if (_code_ptr[ip + 2] != 0) {
- GET_VARIANT_PTR(message, 2);
- message_str = *message;
- }
- if (message_str.empty()) {
- err_text = "Assertion failed.";
- } else {
- err_text = "Assertion failed: " + message_str;
- }
- OPCODE_BREAK;
- }
-
-#endif
- ip += 3;
- }
- DISPATCH_OPCODE;
-
- OPCODE(OPCODE_BREAKPOINT) {
-#ifdef DEBUG_ENABLED
- if (EngineDebugger::is_active()) {
- GDScriptLanguage::get_singleton()->debug_break("Breakpoint Statement", true);
- }
-#endif
- ip += 1;
- }
- DISPATCH_OPCODE;
-
- OPCODE(OPCODE_LINE) {
- CHECK_SPACE(2);
-
- line = _code_ptr[ip + 1];
- ip += 2;
-
- if (EngineDebugger::is_active()) {
- // line
- bool do_break = false;
-
- if (EngineDebugger::get_script_debugger()->get_lines_left() > 0) {
- if (EngineDebugger::get_script_debugger()->get_depth() <= 0) {
- EngineDebugger::get_script_debugger()->set_lines_left(EngineDebugger::get_script_debugger()->get_lines_left() - 1);
- }
- if (EngineDebugger::get_script_debugger()->get_lines_left() <= 0) {
- do_break = true;
- }
- }
-
- if (EngineDebugger::get_script_debugger()->is_breakpoint(line, source)) {
- do_break = true;
- }
-
- if (do_break) {
- GDScriptLanguage::get_singleton()->debug_break("Breakpoint", true);
- }
-
- EngineDebugger::get_singleton()->line_poll();
- }
- }
- DISPATCH_OPCODE;
-
- OPCODE(OPCODE_END) {
-#ifdef DEBUG_ENABLED
- exit_ok = true;
-#endif
- OPCODE_BREAK;
- }
-
-#if 0 // Enable for debugging.
- default: {
- err_text = "Illegal opcode " + itos(_code_ptr[ip]) + " at address " + itos(ip);
- OPCODE_BREAK;
- }
-#endif
- }
-
- OPCODES_END
-#ifdef DEBUG_ENABLED
- if (exit_ok) {
- OPCODE_OUT;
- }
- //error
- // function, file, line, error, explanation
- String err_file;
- if (p_instance && ObjectDB::get_instance(p_instance->owner_id) != nullptr && p_instance->script->is_valid() && p_instance->script->path != "") {
- err_file = p_instance->script->path;
- } else if (script) {
- err_file = script->path;
- }
- if (err_file == "") {
- err_file = "<built-in>";
- }
- String err_func = name;
- if (p_instance && ObjectDB::get_instance(p_instance->owner_id) != nullptr && p_instance->script->is_valid() && p_instance->script->name != "") {
- err_func = p_instance->script->name + "." + err_func;
- }
- int err_line = line;
- if (err_text == "") {
- err_text = "Internal Script Error! - opcode #" + itos(last_opcode) + " (report please).";
- }
-
- if (!GDScriptLanguage::get_singleton()->debug_break(err_text, false)) {
- // debugger break did not happen
-
- _err_print_error(err_func.utf8().get_data(), err_file.utf8().get_data(), err_line, err_text.utf8().get_data(), ERR_HANDLER_SCRIPT);
- }
-
-#endif
- OPCODE_OUT;
- }
-
- OPCODES_OUT
-#ifdef DEBUG_ENABLED
- if (GDScriptLanguage::get_singleton()->profiling) {
- uint64_t time_taken = OS::get_singleton()->get_ticks_usec() - function_start_time;
- profile.total_time += time_taken;
- profile.self_time += time_taken - function_call_time;
- profile.frame_total_time += time_taken;
- profile.frame_self_time += time_taken - function_call_time;
- GDScriptLanguage::get_singleton()->script_frame_time += time_taken - function_call_time;
- }
-
- // Check if this is the last time the function is resuming from await
- // Will be true if never awaited as well
- // When it's the last resume it will postpone the exit from stack,
- // so the debugger knows which function triggered the resume of the next function (if any)
- if (!p_state || awaited) {
- if (EngineDebugger::is_active()) {
- GDScriptLanguage::get_singleton()->exit_function();
- }
-#endif
-
- if (_stack_size) {
- //free stack
- for (int i = 0; i < _stack_size; i++) {
- stack[i].~Variant();
- }
- }
-
-#ifdef DEBUG_ENABLED
- }
-#endif
-
- return retvalue;
-}
const int *GDScriptFunction::get_code() const {
return _code_ptr;
@@ -1644,14 +77,14 @@ int GDScriptFunction::get_max_stack_size() const {
}
struct _GDFKC {
- int order;
+ int order = 0;
List<int> pos;
};
struct _GDFKCS {
- int order;
+ int order = 0;
StringName id;
- int pos;
+ int pos = 0;
bool operator<(const _GDFKCS &p_r) const {
return order < p_r.order;
@@ -1681,7 +114,7 @@ void GDScriptFunction::debug_get_stack_member_state(int p_line, List<Pair<String
ERR_CONTINUE(!sdmap.has(sd.identifier));
sdmap[sd.identifier].pos.pop_back();
- if (sdmap[sd.identifier].pos.empty()) {
+ if (sdmap[sd.identifier].pos.is_empty()) {
sdmap.erase(sd.identifier);
}
}
@@ -1706,31 +139,13 @@ void GDScriptFunction::debug_get_stack_member_state(int p_line, List<Pair<String
}
}
-GDScriptFunction::GDScriptFunction() :
- function_list(this) {
- _stack_size = 0;
- _call_size = 0;
- rpc_mode = MultiplayerAPI::RPC_MODE_DISABLED;
+GDScriptFunction::GDScriptFunction() {
name = "<anonymous>";
#ifdef DEBUG_ENABLED
- _func_cname = nullptr;
-
{
MutexLock lock(GDScriptLanguage::get_singleton()->lock);
-
GDScriptLanguage::get_singleton()->function_list.add(&function_list);
}
-
- profile.call_count = 0;
- profile.self_time = 0;
- profile.total_time = 0;
- profile.frame_call_count = 0;
- profile.frame_self_time = 0;
- profile.frame_total_time = 0;
- profile.last_frame_call_count = 0;
- profile.last_frame_self_time = 0;
- profile.last_frame_total_time = 0;
-
#endif
}
@@ -1829,7 +244,7 @@ Variant GDScriptFunctionState::resume(const Variant &p_arg) {
bool completed = true;
// If the return value is a GDScriptFunctionState reference,
- // then the function did awaited again after resuming.
+ // then the function did await again after resuming.
if (ret.is_ref()) {
GDScriptFunctionState *gdfs = Object::cast_to<GDScriptFunctionState>(ret);
if (gdfs && gdfs->function == function) {
@@ -1879,7 +294,6 @@ void GDScriptFunctionState::_bind_methods() {
GDScriptFunctionState::GDScriptFunctionState() :
scripts_list(this),
instances_list(this) {
- function = nullptr;
}
GDScriptFunctionState::~GDScriptFunctionState() {
@@ -1891,506 +305,3 @@ GDScriptFunctionState::~GDScriptFunctionState() {
instances_list.remove_from_list();
}
}
-
-#ifdef DEBUG_ENABLED
-static String _get_variant_string(const Variant &p_variant) {
- String txt;
- if (p_variant.get_type() == Variant::STRING) {
- txt = "\"" + String(p_variant) + "\"";
- } else if (p_variant.get_type() == Variant::STRING_NAME) {
- txt = "&\"" + String(p_variant) + "\"";
- } else if (p_variant.get_type() == Variant::NODE_PATH) {
- txt = "^\"" + String(p_variant) + "\"";
- } else if (p_variant.get_type() == Variant::OBJECT) {
- Object *obj = p_variant;
- if (!obj) {
- txt = "null";
- } else {
- GDScriptNativeClass *cls = Object::cast_to<GDScriptNativeClass>(obj);
- if (cls) {
- txt += cls->get_name();
- txt += " (class)";
- } else {
- txt = obj->get_class();
- if (obj->get_script_instance()) {
- txt += "(" + obj->get_script_instance()->get_script()->get_path() + ")";
- }
- }
- }
- } else {
- txt = p_variant;
- }
- return txt;
-}
-
-static String _disassemble_address(const GDScript *p_script, const GDScriptFunction &p_function, int p_address) {
- int addr = p_address & GDScriptFunction::ADDR_MASK;
-
- switch (p_address >> GDScriptFunction::ADDR_BITS) {
- case GDScriptFunction::ADDR_TYPE_SELF: {
- return "self";
- } break;
- case GDScriptFunction::ADDR_TYPE_CLASS: {
- return "class";
- } break;
- case GDScriptFunction::ADDR_TYPE_MEMBER: {
- return "member(" + p_script->debug_get_member_by_index(addr) + ")";
- } break;
- case GDScriptFunction::ADDR_TYPE_CLASS_CONSTANT: {
- return "class_const(" + p_function.get_global_name(addr) + ")";
- } break;
- case GDScriptFunction::ADDR_TYPE_LOCAL_CONSTANT: {
- return "const(" + _get_variant_string(p_function.get_constant(addr)) + ")";
- } break;
- case GDScriptFunction::ADDR_TYPE_STACK: {
- return "stack(" + itos(addr) + ")";
- } break;
- case GDScriptFunction::ADDR_TYPE_STACK_VARIABLE: {
- return "var_stack(" + itos(addr) + ")";
- } break;
- case GDScriptFunction::ADDR_TYPE_GLOBAL: {
- return "global(" + _get_variant_string(GDScriptLanguage::get_singleton()->get_global_array()[addr]) + ")";
- } break;
- case GDScriptFunction::ADDR_TYPE_NAMED_GLOBAL: {
- return "named_global(" + p_function.get_global_name(addr) + ")";
- } break;
- case GDScriptFunction::ADDR_TYPE_NIL: {
- return "nil";
- } break;
- }
-
- return "<err>";
-}
-
-void GDScriptFunction::disassemble(const Vector<String> &p_code_lines) const {
-#define DADDR(m_ip) (_disassemble_address(_script, *this, _code_ptr[ip + m_ip]))
-
- for (int ip = 0; ip < _code_size;) {
- StringBuilder text;
- int incr = 0;
-
- text += " ";
- text += itos(ip);
- text += ": ";
-
- // This makes the compiler complain if some opcode is unchecked in the switch.
- Opcode code = Opcode(_code_ptr[ip]);
-
- switch (code) {
- case OPCODE_OPERATOR: {
- int operation = _code_ptr[ip + 1];
-
- text += "operator ";
-
- text += DADDR(4);
- text += " = ";
- text += DADDR(2);
- text += " ";
- text += Variant::get_operator_name(Variant::Operator(operation));
- text += " ";
- text += DADDR(3);
-
- incr += 5;
- } break;
- case OPCODE_EXTENDS_TEST: {
- text += "is object ";
- text += DADDR(3);
- text += " = ";
- text += DADDR(1);
- text += " is ";
- text += DADDR(2);
-
- incr += 4;
- } break;
- case OPCODE_IS_BUILTIN: {
- text += "is builtin ";
- text += DADDR(3);
- text += " = ";
- text += DADDR(1);
- text += " is ";
- text += Variant::get_type_name(Variant::Type(_code_ptr[ip + 2]));
-
- incr += 4;
- } break;
- case OPCODE_SET: {
- text += "set ";
- text += DADDR(1);
- text += "[";
- text += DADDR(2);
- text += "] = ";
- text += DADDR(3);
-
- incr += 4;
- } break;
- case OPCODE_GET: {
- text += "get ";
- text += DADDR(3);
- text += " = ";
- text += DADDR(1);
- text += "[";
- text += DADDR(2);
- text += "]";
-
- incr += 4;
- } break;
- case OPCODE_SET_NAMED: {
- text += "set_named ";
- text += DADDR(1);
- text += "[\"";
- text += _global_names_ptr[_code_ptr[ip + 2]];
- text += "\"] = ";
- text += DADDR(3);
-
- incr += 4;
- } break;
- case OPCODE_GET_NAMED: {
- text += "get_named ";
- text += DADDR(3);
- text += " = ";
- text += DADDR(1);
- text += "[\"";
- text += _global_names_ptr[_code_ptr[ip + 2]];
- text += "\"]";
-
- incr += 4;
- } break;
- case OPCODE_SET_MEMBER: {
- text += "set_member ";
- text += "[\"";
- text += _global_names_ptr[_code_ptr[ip + 1]];
- text += "\"] = ";
- text += DADDR(2);
-
- incr += 3;
- } break;
- case OPCODE_GET_MEMBER: {
- text += "get_member ";
- text += DADDR(2);
- text += " = ";
- text += "[\"";
- text += _global_names_ptr[_code_ptr[ip + 1]];
- text += "\"]";
-
- incr += 3;
- } break;
- case OPCODE_ASSIGN: {
- text += "assign ";
- text += DADDR(1);
- text += " = ";
- text += DADDR(2);
-
- incr += 3;
- } break;
- case OPCODE_ASSIGN_TRUE: {
- text += "assign ";
- text += DADDR(1);
- text += " = true";
-
- incr += 2;
- } break;
- case OPCODE_ASSIGN_FALSE: {
- text += "assign ";
- text += DADDR(1);
- text += " = false";
-
- incr += 2;
- } break;
- case OPCODE_ASSIGN_TYPED_BUILTIN: {
- text += "assign typed builtin (";
- text += Variant::get_type_name((Variant::Type)_code_ptr[ip + 1]);
- text += ") ";
- text += DADDR(2);
- text += " = ";
- text += DADDR(3);
-
- incr += 4;
- } break;
- case OPCODE_ASSIGN_TYPED_NATIVE: {
- Variant class_name = _constants_ptr[_code_ptr[ip + 1]];
- GDScriptNativeClass *nc = Object::cast_to<GDScriptNativeClass>(class_name.operator Object *());
-
- text += "assign typed native (";
- text += nc->get_name().operator String();
- text += ") ";
- text += DADDR(2);
- text += " = ";
- text += DADDR(3);
-
- incr += 4;
- } break;
- case OPCODE_ASSIGN_TYPED_SCRIPT: {
- Variant script = _constants_ptr[_code_ptr[ip + 1]];
- Script *sc = Object::cast_to<Script>(script.operator Object *());
-
- text += "assign typed script (";
- text += sc->get_path();
- text += ") ";
- text += DADDR(2);
- text += " = ";
- text += DADDR(3);
-
- incr += 4;
- } break;
- case OPCODE_CAST_TO_BUILTIN: {
- text += "cast builtin ";
- text += DADDR(3);
- text += " = ";
- text += DADDR(2);
- text += " as ";
- text += Variant::get_type_name(Variant::Type(_code_ptr[ip + 1]));
-
- incr += 4;
- } break;
- case OPCODE_CAST_TO_NATIVE: {
- Variant class_name = _constants_ptr[_code_ptr[ip + 1]];
- GDScriptNativeClass *nc = Object::cast_to<GDScriptNativeClass>(class_name.operator Object *());
-
- text += "cast native ";
- text += DADDR(3);
- text += " = ";
- text += DADDR(2);
- text += " as ";
- text += nc->get_name();
-
- incr += 4;
- } break;
- case OPCODE_CAST_TO_SCRIPT: {
- text += "cast ";
- text += DADDR(3);
- text += " = ";
- text += DADDR(2);
- text += " as ";
- text += DADDR(1);
-
- incr += 4;
- } break;
- case OPCODE_CONSTRUCT: {
- Variant::Type t = Variant::Type(_code_ptr[ip + 1]);
- int argc = _code_ptr[ip + 2];
-
- text += "construct ";
- text += DADDR(3 + argc);
- text += " = ";
-
- text += Variant::get_type_name(t) + "(";
- for (int i = 0; i < argc; i++) {
- if (i > 0)
- text += ", ";
- text += DADDR(i + 3);
- }
- text += ")";
-
- incr = 4 + argc;
- } break;
- case OPCODE_CONSTRUCT_ARRAY: {
- int argc = _code_ptr[ip + 1];
- text += " make_array ";
- text += DADDR(2 + argc);
- text += " = [";
-
- for (int i = 0; i < argc; i++) {
- if (i > 0)
- text += ", ";
- text += DADDR(2 + i);
- }
-
- text += "]";
-
- incr += 3 + argc;
- } break;
- case OPCODE_CONSTRUCT_DICTIONARY: {
- int argc = _code_ptr[ip + 1];
- text += "make_dict ";
- text += DADDR(2 + argc * 2);
- text += " = {";
-
- for (int i = 0; i < argc; i++) {
- if (i > 0)
- text += ", ";
- text += DADDR(2 + i * 2 + 0);
- text += ": ";
- text += DADDR(2 + i * 2 + 1);
- }
-
- text += "}";
-
- incr += 3 + argc * 2;
- } break;
- case OPCODE_CALL:
- case OPCODE_CALL_RETURN:
- case OPCODE_CALL_ASYNC: {
- bool ret = _code_ptr[ip] == OPCODE_CALL_RETURN;
- bool async = _code_ptr[ip] == OPCODE_CALL_ASYNC;
-
- if (ret) {
- text += "call-ret ";
- } else if (async) {
- text += "call-async ";
- } else {
- text += "call ";
- }
-
- int argc = _code_ptr[ip + 1];
- if (ret || async) {
- text += DADDR(4 + argc) + " = ";
- }
-
- text += DADDR(2) + ".";
- text += String(_global_names_ptr[_code_ptr[ip + 3]]);
- text += "(";
-
- for (int i = 0; i < argc; i++) {
- if (i > 0)
- text += ", ";
- text += DADDR(4 + i);
- }
- text += ")";
-
- incr = 5 + argc;
- } break;
- case OPCODE_CALL_BUILT_IN: {
- text += "call-built-in ";
-
- int argc = _code_ptr[ip + 2];
- text += DADDR(3 + argc) + " = ";
-
- text += GDScriptFunctions::get_func_name(GDScriptFunctions::Function(_code_ptr[ip + 1]));
- text += "(";
-
- for (int i = 0; i < argc; i++) {
- if (i > 0)
- text += ", ";
- text += DADDR(3 + i);
- }
- text += ")";
-
- incr = 4 + argc;
- } break;
- case OPCODE_CALL_SELF_BASE: {
- text += "call-self-base ";
-
- int argc = _code_ptr[ip + 2];
- text += DADDR(3 + argc) + " = ";
-
- text += _global_names_ptr[_code_ptr[ip + 1]];
- text += "(";
-
- for (int i = 0; i < argc; i++) {
- if (i > 0)
- text += ", ";
- text += DADDR(3 + i);
- }
- text += ")";
-
- incr = 4 + argc;
- } break;
- case OPCODE_AWAIT: {
- text += "await ";
- text += DADDR(1);
-
- incr += 2;
- } break;
- case OPCODE_AWAIT_RESUME: {
- text += "await resume ";
- text += DADDR(1);
-
- incr = 2;
- } break;
- case OPCODE_JUMP: {
- text += "jump ";
- text += itos(_code_ptr[ip + 1]);
-
- incr = 2;
- } break;
- case OPCODE_JUMP_IF: {
- text += "jump-if ";
- text += DADDR(1);
- text += " to ";
- text += itos(_code_ptr[ip + 2]);
-
- incr = 3;
- } break;
- case OPCODE_JUMP_IF_NOT: {
- text += "jump-if-not ";
- text += DADDR(1);
- text += " to ";
- text += itos(_code_ptr[ip + 2]);
-
- incr = 3;
- } break;
- case OPCODE_JUMP_TO_DEF_ARGUMENT: {
- text += "jump-to-default-argument ";
-
- incr = 1;
- } break;
- case OPCODE_RETURN: {
- text += "return ";
- text += DADDR(1);
-
- incr = 2;
- } break;
- case OPCODE_ITERATE_BEGIN: {
- text += "for-init ";
- text += DADDR(4);
- text += " in ";
- text += DADDR(2);
- text += " counter ";
- text += DADDR(1);
- text += " end ";
- text += itos(_code_ptr[ip + 3]);
-
- incr += 5;
- } break;
- case OPCODE_ITERATE: {
- text += "for-loop ";
- text += DADDR(4);
- text += " in ";
- text += DADDR(2);
- text += " counter ";
- text += DADDR(1);
- text += " end ";
- text += itos(_code_ptr[ip + 3]);
-
- incr += 5;
- } break;
- case OPCODE_LINE: {
- int line = _code_ptr[ip + 1] - 1;
- if (line >= 0 && line < p_code_lines.size()) {
- text += "line ";
- text += itos(line + 1);
- text += ": ";
- text += p_code_lines[line];
- } else {
- text += "";
- }
-
- incr += 2;
- } break;
- case OPCODE_ASSERT: {
- text += "assert (";
- text += DADDR(1);
- text += ", ";
- text += DADDR(2);
- text += ")";
-
- incr += 3;
- } break;
- case OPCODE_BREAKPOINT: {
- text += "breakpoint";
-
- incr += 1;
- } break;
- case OPCODE_END: {
- text += "== END ==";
-
- incr += 1;
- } break;
- }
-
- ip += incr;
- if (text.get_string_length() > 0) {
- print_line(text.as_string());
- }
- }
-}
-#endif
diff --git a/modules/gdscript/gdscript_function.h b/modules/gdscript/gdscript_function.h
index 50eadcaf86..41080ef371 100644
--- a/modules/gdscript/gdscript_function.h
+++ b/modules/gdscript/gdscript_function.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -38,11 +38,16 @@
#include "core/templates/pair.h"
#include "core/templates/self_list.h"
#include "core/variant/variant.h"
+#include "gdscript_utility_functions.h"
class GDScriptInstance;
class GDScript;
-struct GDScriptDataType {
+class GDScriptDataType {
+private:
+ GDScriptDataType *container_element_type = nullptr;
+
+public:
enum Kind {
UNINITIALIZED,
BUILTIN,
@@ -70,7 +75,24 @@ struct GDScriptDataType {
case BUILTIN: {
Variant::Type var_type = p_variant.get_type();
bool valid = builtin_type == var_type;
- if (!valid && p_allow_implicit_conversion) {
+ if (valid && builtin_type == Variant::ARRAY && has_container_element_type()) {
+ Array array = p_variant;
+ if (array.is_typed()) {
+ Variant::Type array_builtin_type = (Variant::Type)array.get_typed_builtin();
+ StringName array_native_type = array.get_typed_class_name();
+ Ref<Script> array_script_type_ref = array.get_typed_script();
+
+ if (array_script_type_ref.is_valid()) {
+ valid = (container_element_type->kind == SCRIPT || container_element_type->kind == GDSCRIPT) && container_element_type->script_type == array_script_type_ref.ptr();
+ } else if (array_native_type != StringName()) {
+ valid = container_element_type->kind == NATIVE && container_element_type->native_type == array_native_type;
+ } else {
+ valid = container_element_type->kind == BUILTIN && container_element_type->builtin_type == array_builtin_type;
+ }
+ } else {
+ valid = false;
+ }
+ } else if (!valid && p_allow_implicit_conversion) {
valid = Variant::can_convert_strict(var_type, builtin_type);
}
return valid;
@@ -152,38 +174,131 @@ struct GDScriptDataType {
return info;
}
- GDScriptDataType() {}
+ void set_container_element_type(const GDScriptDataType &p_element_type) {
+ container_element_type = memnew(GDScriptDataType(p_element_type));
+ }
+
+ GDScriptDataType get_container_element_type() const {
+ ERR_FAIL_COND_V(container_element_type == nullptr, GDScriptDataType());
+ return *container_element_type;
+ }
+
+ bool has_container_element_type() const {
+ return container_element_type != nullptr;
+ }
+
+ void unset_container_element_type() {
+ if (container_element_type) {
+ memdelete(container_element_type);
+ }
+ container_element_type = nullptr;
+ }
+
+ GDScriptDataType() = default;
+
+ GDScriptDataType &operator=(const GDScriptDataType &p_other) {
+ kind = p_other.kind;
+ has_type = p_other.has_type;
+ builtin_type = p_other.builtin_type;
+ native_type = p_other.native_type;
+ script_type = p_other.script_type;
+ script_type_ref = p_other.script_type_ref;
+ unset_container_element_type();
+ if (p_other.has_container_element_type()) {
+ set_container_element_type(p_other.get_container_element_type());
+ }
+ return *this;
+ }
+
+ GDScriptDataType(const GDScriptDataType &p_other) {
+ *this = p_other;
+ }
+
+ ~GDScriptDataType() {
+ unset_container_element_type();
+ }
};
class GDScriptFunction {
public:
enum Opcode {
OPCODE_OPERATOR,
+ OPCODE_OPERATOR_VALIDATED,
OPCODE_EXTENDS_TEST,
OPCODE_IS_BUILTIN,
- OPCODE_SET,
- OPCODE_GET,
+ OPCODE_SET_KEYED,
+ OPCODE_SET_KEYED_VALIDATED,
+ OPCODE_SET_INDEXED_VALIDATED,
+ OPCODE_GET_KEYED,
+ OPCODE_GET_KEYED_VALIDATED,
+ OPCODE_GET_INDEXED_VALIDATED,
OPCODE_SET_NAMED,
+ OPCODE_SET_NAMED_VALIDATED,
OPCODE_GET_NAMED,
+ OPCODE_GET_NAMED_VALIDATED,
OPCODE_SET_MEMBER,
OPCODE_GET_MEMBER,
OPCODE_ASSIGN,
OPCODE_ASSIGN_TRUE,
OPCODE_ASSIGN_FALSE,
OPCODE_ASSIGN_TYPED_BUILTIN,
+ OPCODE_ASSIGN_TYPED_ARRAY,
OPCODE_ASSIGN_TYPED_NATIVE,
OPCODE_ASSIGN_TYPED_SCRIPT,
OPCODE_CAST_TO_BUILTIN,
OPCODE_CAST_TO_NATIVE,
OPCODE_CAST_TO_SCRIPT,
- OPCODE_CONSTRUCT, //only for basic types!!
+ OPCODE_CONSTRUCT, // Only for basic types!
+ OPCODE_CONSTRUCT_VALIDATED, // Only for basic types!
OPCODE_CONSTRUCT_ARRAY,
+ OPCODE_CONSTRUCT_TYPED_ARRAY,
OPCODE_CONSTRUCT_DICTIONARY,
OPCODE_CALL,
OPCODE_CALL_RETURN,
OPCODE_CALL_ASYNC,
- OPCODE_CALL_BUILT_IN,
+ OPCODE_CALL_UTILITY,
+ OPCODE_CALL_UTILITY_VALIDATED,
+ OPCODE_CALL_GDSCRIPT_UTILITY,
+ OPCODE_CALL_BUILTIN_TYPE_VALIDATED,
OPCODE_CALL_SELF_BASE,
+ OPCODE_CALL_METHOD_BIND,
+ OPCODE_CALL_METHOD_BIND_RET,
+ // ptrcall have one instruction per return type.
+ OPCODE_CALL_PTRCALL_NO_RETURN,
+ OPCODE_CALL_PTRCALL_BOOL,
+ OPCODE_CALL_PTRCALL_INT,
+ OPCODE_CALL_PTRCALL_FLOAT,
+ OPCODE_CALL_PTRCALL_STRING,
+ OPCODE_CALL_PTRCALL_VECTOR2,
+ OPCODE_CALL_PTRCALL_VECTOR2I,
+ OPCODE_CALL_PTRCALL_RECT2,
+ OPCODE_CALL_PTRCALL_RECT2I,
+ OPCODE_CALL_PTRCALL_VECTOR3,
+ OPCODE_CALL_PTRCALL_VECTOR3I,
+ OPCODE_CALL_PTRCALL_TRANSFORM2D,
+ OPCODE_CALL_PTRCALL_PLANE,
+ OPCODE_CALL_PTRCALL_QUAT,
+ OPCODE_CALL_PTRCALL_AABB,
+ OPCODE_CALL_PTRCALL_BASIS,
+ OPCODE_CALL_PTRCALL_TRANSFORM,
+ OPCODE_CALL_PTRCALL_COLOR,
+ OPCODE_CALL_PTRCALL_STRING_NAME,
+ OPCODE_CALL_PTRCALL_NODE_PATH,
+ OPCODE_CALL_PTRCALL_RID,
+ OPCODE_CALL_PTRCALL_OBJECT,
+ OPCODE_CALL_PTRCALL_CALLABLE,
+ OPCODE_CALL_PTRCALL_SIGNAL,
+ OPCODE_CALL_PTRCALL_DICTIONARY,
+ OPCODE_CALL_PTRCALL_ARRAY,
+ OPCODE_CALL_PTRCALL_PACKED_BYTE_ARRAY,
+ OPCODE_CALL_PTRCALL_PACKED_INT32_ARRAY,
+ OPCODE_CALL_PTRCALL_PACKED_INT64_ARRAY,
+ OPCODE_CALL_PTRCALL_PACKED_FLOAT32_ARRAY,
+ OPCODE_CALL_PTRCALL_PACKED_FLOAT64_ARRAY,
+ OPCODE_CALL_PTRCALL_PACKED_STRING_ARRAY,
+ OPCODE_CALL_PTRCALL_PACKED_VECTOR2_ARRAY,
+ OPCODE_CALL_PTRCALL_PACKED_VECTOR3_ARRAY,
+ OPCODE_CALL_PTRCALL_PACKED_COLOR_ARRAY,
OPCODE_AWAIT,
OPCODE_AWAIT_RESUME,
OPCODE_JUMP,
@@ -192,7 +307,45 @@ public:
OPCODE_JUMP_TO_DEF_ARGUMENT,
OPCODE_RETURN,
OPCODE_ITERATE_BEGIN,
+ OPCODE_ITERATE_BEGIN_INT,
+ OPCODE_ITERATE_BEGIN_FLOAT,
+ OPCODE_ITERATE_BEGIN_VECTOR2,
+ OPCODE_ITERATE_BEGIN_VECTOR2I,
+ OPCODE_ITERATE_BEGIN_VECTOR3,
+ OPCODE_ITERATE_BEGIN_VECTOR3I,
+ OPCODE_ITERATE_BEGIN_STRING,
+ OPCODE_ITERATE_BEGIN_DICTIONARY,
+ OPCODE_ITERATE_BEGIN_ARRAY,
+ OPCODE_ITERATE_BEGIN_PACKED_BYTE_ARRAY,
+ OPCODE_ITERATE_BEGIN_PACKED_INT32_ARRAY,
+ OPCODE_ITERATE_BEGIN_PACKED_INT64_ARRAY,
+ OPCODE_ITERATE_BEGIN_PACKED_FLOAT32_ARRAY,
+ OPCODE_ITERATE_BEGIN_PACKED_FLOAT64_ARRAY,
+ OPCODE_ITERATE_BEGIN_PACKED_STRING_ARRAY,
+ OPCODE_ITERATE_BEGIN_PACKED_VECTOR2_ARRAY,
+ OPCODE_ITERATE_BEGIN_PACKED_VECTOR3_ARRAY,
+ OPCODE_ITERATE_BEGIN_PACKED_COLOR_ARRAY,
+ OPCODE_ITERATE_BEGIN_OBJECT,
OPCODE_ITERATE,
+ OPCODE_ITERATE_INT,
+ OPCODE_ITERATE_FLOAT,
+ OPCODE_ITERATE_VECTOR2,
+ OPCODE_ITERATE_VECTOR2I,
+ OPCODE_ITERATE_VECTOR3,
+ OPCODE_ITERATE_VECTOR3I,
+ OPCODE_ITERATE_STRING,
+ OPCODE_ITERATE_DICTIONARY,
+ OPCODE_ITERATE_ARRAY,
+ OPCODE_ITERATE_PACKED_BYTE_ARRAY,
+ OPCODE_ITERATE_PACKED_INT32_ARRAY,
+ OPCODE_ITERATE_PACKED_INT64_ARRAY,
+ OPCODE_ITERATE_PACKED_FLOAT32_ARRAY,
+ OPCODE_ITERATE_PACKED_FLOAT64_ARRAY,
+ OPCODE_ITERATE_PACKED_STRING_ARRAY,
+ OPCODE_ITERATE_PACKED_VECTOR2_ARRAY,
+ OPCODE_ITERATE_PACKED_VECTOR3_ARRAY,
+ OPCODE_ITERATE_PACKED_COLOR_ARRAY,
+ OPCODE_ITERATE_OBJECT,
OPCODE_ASSERT,
OPCODE_BREAKPOINT,
OPCODE_LINE,
@@ -215,6 +368,12 @@ public:
ADDR_TYPE_NIL = 9
};
+ enum Instruction {
+ INSTR_BITS = 20,
+ INSTR_MASK = ((1 << INSTR_BITS) - 1),
+ INSTR_ARGS_MASK = ~INSTR_MASK,
+ };
+
struct StackDebug {
int line;
int pos;
@@ -229,33 +388,72 @@ private:
StringName source;
mutable Variant nil;
- mutable Variant *_constants_ptr;
- int _constant_count;
- const StringName *_global_names_ptr;
- int _global_names_count;
- const int *_default_arg_ptr;
- int _default_arg_count;
- const int *_code_ptr;
- int _code_size;
- int _argument_count;
- int _stack_size;
- int _call_size;
- int _initial_line;
- bool _static;
- MultiplayerAPI::RPCMode rpc_mode;
-
- GDScript *_script;
+ mutable Variant *_constants_ptr = nullptr;
+ int _constant_count = 0;
+ const StringName *_global_names_ptr = nullptr;
+ int _global_names_count = 0;
+ const int *_default_arg_ptr = nullptr;
+ int _default_arg_count = 0;
+ int _operator_funcs_count = 0;
+ const Variant::ValidatedOperatorEvaluator *_operator_funcs_ptr = nullptr;
+ int _setters_count = 0;
+ const Variant::ValidatedSetter *_setters_ptr = nullptr;
+ int _getters_count = 0;
+ const Variant::ValidatedGetter *_getters_ptr = nullptr;
+ int _keyed_setters_count = 0;
+ const Variant::ValidatedKeyedSetter *_keyed_setters_ptr = nullptr;
+ int _keyed_getters_count = 0;
+ const Variant::ValidatedKeyedGetter *_keyed_getters_ptr = nullptr;
+ int _indexed_setters_count = 0;
+ const Variant::ValidatedIndexedSetter *_indexed_setters_ptr = nullptr;
+ int _indexed_getters_count = 0;
+ const Variant::ValidatedIndexedGetter *_indexed_getters_ptr = nullptr;
+ int _builtin_methods_count = 0;
+ const Variant::ValidatedBuiltInMethod *_builtin_methods_ptr = nullptr;
+ int _constructors_count = 0;
+ const Variant::ValidatedConstructor *_constructors_ptr = nullptr;
+ int _utilities_count = 0;
+ const Variant::ValidatedUtilityFunction *_utilities_ptr = nullptr;
+ int _gds_utilities_count = 0;
+ const GDScriptUtilityFunctions::FunctionPtr *_gds_utilities_ptr = nullptr;
+ int _methods_count = 0;
+ MethodBind **_methods_ptr = nullptr;
+ const int *_code_ptr = nullptr;
+ int _code_size = 0;
+ int _argument_count = 0;
+ int _stack_size = 0;
+ int _instruction_args_size = 0;
+ int _ptrcall_args_size = 0;
+
+ int _initial_line = 0;
+ bool _static = false;
+ MultiplayerAPI::RPCMode rpc_mode = MultiplayerAPI::RPC_MODE_DISABLED;
+
+ GDScript *_script = nullptr;
StringName name;
Vector<Variant> constants;
Vector<StringName> global_names;
Vector<int> default_arguments;
+ Vector<Variant::ValidatedOperatorEvaluator> operator_funcs;
+ Vector<Variant::ValidatedSetter> setters;
+ Vector<Variant::ValidatedGetter> getters;
+ Vector<Variant::ValidatedKeyedSetter> keyed_setters;
+ Vector<Variant::ValidatedKeyedGetter> keyed_getters;
+ Vector<Variant::ValidatedIndexedSetter> indexed_setters;
+ Vector<Variant::ValidatedIndexedGetter> indexed_getters;
+ Vector<Variant::ValidatedBuiltInMethod> builtin_methods;
+ Vector<Variant::ValidatedConstructor> constructors;
+ Vector<Variant::ValidatedUtilityFunction> utilities;
+ Vector<GDScriptUtilityFunctions::FunctionPtr> gds_utilities;
+ Vector<MethodBind *> methods;
Vector<int> code;
Vector<GDScriptDataType> argument_types;
GDScriptDataType return_type;
#ifdef TOOLS_ENABLED
Vector<StringName> arg_names;
+ Vector<Variant> default_arg_values;
#endif
List<StackDebug> stack_debug;
@@ -265,41 +463,41 @@ private:
friend class GDScriptLanguage;
- SelfList<GDScriptFunction> function_list;
+ SelfList<GDScriptFunction> function_list{ this };
#ifdef DEBUG_ENABLED
CharString func_cname;
- const char *_func_cname;
+ const char *_func_cname = nullptr;
struct Profile {
StringName signature;
- uint64_t call_count;
- uint64_t self_time;
- uint64_t total_time;
- uint64_t frame_call_count;
- uint64_t frame_self_time;
- uint64_t frame_total_time;
- uint64_t last_frame_call_count;
- uint64_t last_frame_self_time;
- uint64_t last_frame_total_time;
+ uint64_t call_count = 0;
+ uint64_t self_time = 0;
+ uint64_t total_time = 0;
+ uint64_t frame_call_count = 0;
+ uint64_t frame_self_time = 0;
+ uint64_t frame_total_time = 0;
+ uint64_t last_frame_call_count = 0;
+ uint64_t last_frame_self_time = 0;
+ uint64_t last_frame_total_time = 0;
} profile;
#endif
public:
struct CallState {
- GDScript *script;
- GDScriptInstance *instance;
+ GDScript *script = nullptr;
+ GDScriptInstance *instance = nullptr;
#ifdef DEBUG_ENABLED
StringName function_name;
String script_path;
#endif
Vector<uint8_t> stack;
- int stack_size;
+ int stack_size = 0;
Variant self;
- uint32_t alloca_size;
- int ip;
- int line;
- int defarg;
+ uint32_t alloca_size = 0;
+ int ip = 0;
+ int line = 0;
+ int defarg = 0;
Variant result;
};
@@ -335,6 +533,11 @@ public:
ERR_FAIL_INDEX_V(p_idx, default_arguments.size(), Variant());
return default_arguments[p_idx];
}
+#ifdef TOOLS_ENABLED
+ const Vector<Variant> &get_default_arg_values() const {
+ return default_arg_values;
+ }
+#endif // TOOLS_ENABLED
Variant call(GDScriptInstance *p_instance, const Variant **p_args, int p_argcount, Callable::CallError &r_err, CallState *p_state = nullptr);
@@ -350,7 +553,7 @@ public:
class GDScriptFunctionState : public Reference {
GDCLASS(GDScriptFunctionState, Reference);
friend class GDScriptFunction;
- GDScriptFunction *function;
+ GDScriptFunction *function = nullptr;
GDScriptFunction::CallState state;
Variant _signal_callback(const Variant **p_args, int p_argcount, Callable::CallError &r_error);
Ref<GDScriptFunctionState> first_state;
diff --git a/modules/gdscript/gdscript_functions.cpp b/modules/gdscript/gdscript_functions.cpp
deleted file mode 100644
index 3a7c1a8676..0000000000
--- a/modules/gdscript/gdscript_functions.cpp
+++ /dev/null
@@ -1,1942 +0,0 @@
-/*************************************************************************/
-/* gdscript_functions.cpp */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 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_functions.h"
-
-#include "core/io/json.h"
-#include "core/io/marshalls.h"
-#include "core/math/math_funcs.h"
-#include "core/object/class_db.h"
-#include "core/object/reference.h"
-#include "core/os/os.h"
-#include "core/variant/variant_parser.h"
-#include "gdscript.h"
-
-const char *GDScriptFunctions::get_func_name(Function p_func) {
- ERR_FAIL_INDEX_V(p_func, FUNC_MAX, "");
-
- static const char *_names[FUNC_MAX] = {
- "sin",
- "cos",
- "tan",
- "sinh",
- "cosh",
- "tanh",
- "asin",
- "acos",
- "atan",
- "atan2",
- "sqrt",
- "fmod",
- "fposmod",
- "posmod",
- "floor",
- "ceil",
- "round",
- "abs",
- "sign",
- "pow",
- "log",
- "exp",
- "is_nan",
- "is_inf",
- "is_equal_approx",
- "is_zero_approx",
- "ease",
- "step_decimals",
- "stepify",
- "lerp",
- "lerp_angle",
- "inverse_lerp",
- "range_lerp",
- "smoothstep",
- "move_toward",
- "dectime",
- "randomize",
- "randi",
- "randf",
- "randf_range",
- "randi_range",
- "seed",
- "rand_seed",
- "deg2rad",
- "rad2deg",
- "linear2db",
- "db2linear",
- "polar2cartesian",
- "cartesian2polar",
- "wrapi",
- "wrapf",
- "max",
- "min",
- "clamp",
- "nearest_po2",
- "weakref",
- "convert",
- "typeof",
- "type_exists",
- "char",
- "ord",
- "str",
- "print",
- "printt",
- "prints",
- "printerr",
- "printraw",
- "print_debug",
- "push_error",
- "push_warning",
- "var2str",
- "str2var",
- "var2bytes",
- "bytes2var",
- "range",
- "load",
- "inst2dict",
- "dict2inst",
- "validate_json",
- "parse_json",
- "to_json",
- "hash",
- "Color8",
- "ColorN",
- "print_stack",
- "get_stack",
- "instance_from_id",
- "len",
- "is_instance_valid",
- };
-
- return _names[p_func];
-}
-
-void GDScriptFunctions::call(Function p_func, const Variant **p_args, int p_arg_count, Variant &r_ret, Callable::CallError &r_error) {
- r_error.error = Callable::CallError::CALL_OK;
-#ifdef DEBUG_ENABLED
-
-#define VALIDATE_ARG_COUNT(m_count) \
- if (p_arg_count < m_count) { \
- r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; \
- r_error.argument = m_count; \
- r_error.expected = m_count; \
- r_ret = Variant(); \
- return; \
- } \
- if (p_arg_count > m_count) { \
- r_error.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS; \
- r_error.argument = m_count; \
- r_error.expected = m_count; \
- r_ret = Variant(); \
- return; \
- }
-
-#define VALIDATE_ARG_NUM(m_arg) \
- if (!p_args[m_arg]->is_num()) { \
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; \
- r_error.argument = m_arg; \
- r_error.expected = Variant::FLOAT; \
- r_ret = Variant(); \
- return; \
- }
-
-#else
-
-#define VALIDATE_ARG_COUNT(m_count)
-#define VALIDATE_ARG_NUM(m_arg)
-#endif
-
- //using a switch, so the compiler generates a jumptable
-
- switch (p_func) {
- case MATH_SIN: {
- VALIDATE_ARG_COUNT(1);
- VALIDATE_ARG_NUM(0);
- r_ret = Math::sin((double)*p_args[0]);
- } break;
- case MATH_COS: {
- VALIDATE_ARG_COUNT(1);
- VALIDATE_ARG_NUM(0);
- r_ret = Math::cos((double)*p_args[0]);
- } break;
- case MATH_TAN: {
- VALIDATE_ARG_COUNT(1);
- VALIDATE_ARG_NUM(0);
- r_ret = Math::tan((double)*p_args[0]);
- } break;
- case MATH_SINH: {
- VALIDATE_ARG_COUNT(1);
- VALIDATE_ARG_NUM(0);
- r_ret = Math::sinh((double)*p_args[0]);
- } break;
- case MATH_COSH: {
- VALIDATE_ARG_COUNT(1);
- VALIDATE_ARG_NUM(0);
- r_ret = Math::cosh((double)*p_args[0]);
- } break;
- case MATH_TANH: {
- VALIDATE_ARG_COUNT(1);
- VALIDATE_ARG_NUM(0);
- r_ret = Math::tanh((double)*p_args[0]);
- } break;
- case MATH_ASIN: {
- VALIDATE_ARG_COUNT(1);
- VALIDATE_ARG_NUM(0);
- r_ret = Math::asin((double)*p_args[0]);
- } break;
- case MATH_ACOS: {
- VALIDATE_ARG_COUNT(1);
- VALIDATE_ARG_NUM(0);
- r_ret = Math::acos((double)*p_args[0]);
- } break;
- case MATH_ATAN: {
- VALIDATE_ARG_COUNT(1);
- VALIDATE_ARG_NUM(0);
- r_ret = Math::atan((double)*p_args[0]);
- } break;
- case MATH_ATAN2: {
- VALIDATE_ARG_COUNT(2);
- VALIDATE_ARG_NUM(0);
- VALIDATE_ARG_NUM(1);
- r_ret = Math::atan2((double)*p_args[0], (double)*p_args[1]);
- } break;
- case MATH_SQRT: {
- VALIDATE_ARG_COUNT(1);
- VALIDATE_ARG_NUM(0);
- r_ret = Math::sqrt((double)*p_args[0]);
- } break;
- case MATH_FMOD: {
- VALIDATE_ARG_COUNT(2);
- VALIDATE_ARG_NUM(0);
- VALIDATE_ARG_NUM(1);
- r_ret = Math::fmod((double)*p_args[0], (double)*p_args[1]);
- } break;
- case MATH_FPOSMOD: {
- VALIDATE_ARG_COUNT(2);
- VALIDATE_ARG_NUM(0);
- VALIDATE_ARG_NUM(1);
- r_ret = Math::fposmod((double)*p_args[0], (double)*p_args[1]);
- } break;
- case MATH_POSMOD: {
- VALIDATE_ARG_COUNT(2);
- VALIDATE_ARG_NUM(0);
- VALIDATE_ARG_NUM(1);
- r_ret = Math::posmod((int)*p_args[0], (int)*p_args[1]);
- } break;
- case MATH_FLOOR: {
- VALIDATE_ARG_COUNT(1);
- VALIDATE_ARG_NUM(0);
- r_ret = Math::floor((double)*p_args[0]);
- } break;
- case MATH_CEIL: {
- VALIDATE_ARG_COUNT(1);
- VALIDATE_ARG_NUM(0);
- r_ret = Math::ceil((double)*p_args[0]);
- } break;
- case MATH_ROUND: {
- VALIDATE_ARG_COUNT(1);
- VALIDATE_ARG_NUM(0);
- r_ret = Math::round((double)*p_args[0]);
- } break;
- case MATH_ABS: {
- VALIDATE_ARG_COUNT(1);
- if (p_args[0]->get_type() == Variant::INT) {
- int64_t i = *p_args[0];
- r_ret = ABS(i);
- } else if (p_args[0]->get_type() == Variant::FLOAT) {
- double r = *p_args[0];
- r_ret = Math::abs(r);
- } else {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 0;
- r_error.expected = Variant::FLOAT;
- r_ret = Variant();
- }
- } break;
- case MATH_SIGN: {
- VALIDATE_ARG_COUNT(1);
- if (p_args[0]->get_type() == Variant::INT) {
- int64_t i = *p_args[0];
- r_ret = i < 0 ? -1 : (i > 0 ? +1 : 0);
- } else if (p_args[0]->get_type() == Variant::FLOAT) {
- double r = *p_args[0];
- r_ret = r < 0.0 ? -1.0 : (r > 0.0 ? +1.0 : 0.0);
- } else {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 0;
- r_error.expected = Variant::FLOAT;
- r_ret = Variant();
- }
- } break;
- case MATH_POW: {
- VALIDATE_ARG_COUNT(2);
- VALIDATE_ARG_NUM(0);
- VALIDATE_ARG_NUM(1);
- r_ret = Math::pow((double)*p_args[0], (double)*p_args[1]);
- } break;
- case MATH_LOG: {
- VALIDATE_ARG_COUNT(1);
- VALIDATE_ARG_NUM(0);
- r_ret = Math::log((double)*p_args[0]);
- } break;
- case MATH_EXP: {
- VALIDATE_ARG_COUNT(1);
- VALIDATE_ARG_NUM(0);
- r_ret = Math::exp((double)*p_args[0]);
- } break;
- case MATH_ISNAN: {
- VALIDATE_ARG_COUNT(1);
- VALIDATE_ARG_NUM(0);
- r_ret = Math::is_nan((double)*p_args[0]);
- } break;
- case MATH_ISINF: {
- VALIDATE_ARG_COUNT(1);
- VALIDATE_ARG_NUM(0);
- r_ret = Math::is_inf((double)*p_args[0]);
- } break;
- case MATH_ISEQUALAPPROX: {
- VALIDATE_ARG_COUNT(2);
- VALIDATE_ARG_NUM(0);
- VALIDATE_ARG_NUM(1);
- r_ret = Math::is_equal_approx((real_t)*p_args[0], (real_t)*p_args[1]);
- } break;
- case MATH_ISZEROAPPROX: {
- VALIDATE_ARG_COUNT(1);
- VALIDATE_ARG_NUM(0);
- r_ret = Math::is_zero_approx((real_t)*p_args[0]);
- } break;
- case MATH_EASE: {
- VALIDATE_ARG_COUNT(2);
- VALIDATE_ARG_NUM(0);
- VALIDATE_ARG_NUM(1);
- r_ret = Math::ease((double)*p_args[0], (double)*p_args[1]);
- } break;
- case MATH_STEP_DECIMALS: {
- VALIDATE_ARG_COUNT(1);
- VALIDATE_ARG_NUM(0);
- r_ret = Math::step_decimals((double)*p_args[0]);
- } break;
- case MATH_STEPIFY: {
- VALIDATE_ARG_COUNT(2);
- VALIDATE_ARG_NUM(0);
- VALIDATE_ARG_NUM(1);
- r_ret = Math::stepify((double)*p_args[0], (double)*p_args[1]);
- } break;
- case MATH_LERP: {
- VALIDATE_ARG_COUNT(3);
- VALIDATE_ARG_NUM(2);
- const double t = (double)*p_args[2];
- switch (p_args[0]->get_type() == p_args[1]->get_type() ? p_args[0]->get_type() : Variant::FLOAT) {
- case Variant::VECTOR2: {
- r_ret = ((Vector2)*p_args[0]).lerp((Vector2)*p_args[1], t);
- } break;
- case Variant::VECTOR3: {
- r_ret = (p_args[0]->operator Vector3()).lerp(p_args[1]->operator Vector3(), t);
- } break;
- case Variant::COLOR: {
- r_ret = ((Color)*p_args[0]).lerp((Color)*p_args[1], t);
- } break;
- default: {
- VALIDATE_ARG_NUM(0);
- VALIDATE_ARG_NUM(1);
- r_ret = Math::lerp((double)*p_args[0], (double)*p_args[1], t);
- } break;
- }
- } break;
- case MATH_LERP_ANGLE: {
- VALIDATE_ARG_COUNT(3);
- VALIDATE_ARG_NUM(0);
- VALIDATE_ARG_NUM(1);
- VALIDATE_ARG_NUM(2);
- r_ret = Math::lerp_angle((double)*p_args[0], (double)*p_args[1], (double)*p_args[2]);
- } break;
- case MATH_INVERSE_LERP: {
- VALIDATE_ARG_COUNT(3);
- VALIDATE_ARG_NUM(0);
- VALIDATE_ARG_NUM(1);
- VALIDATE_ARG_NUM(2);
- r_ret = Math::inverse_lerp((double)*p_args[0], (double)*p_args[1], (double)*p_args[2]);
- } break;
- case MATH_RANGE_LERP: {
- VALIDATE_ARG_COUNT(5);
- VALIDATE_ARG_NUM(0);
- VALIDATE_ARG_NUM(1);
- VALIDATE_ARG_NUM(2);
- VALIDATE_ARG_NUM(3);
- VALIDATE_ARG_NUM(4);
- r_ret = Math::range_lerp((double)*p_args[0], (double)*p_args[1], (double)*p_args[2], (double)*p_args[3], (double)*p_args[4]);
- } break;
- case MATH_SMOOTHSTEP: {
- VALIDATE_ARG_COUNT(3);
- VALIDATE_ARG_NUM(0);
- VALIDATE_ARG_NUM(1);
- VALIDATE_ARG_NUM(2);
- r_ret = Math::smoothstep((double)*p_args[0], (double)*p_args[1], (double)*p_args[2]);
- } break;
- case MATH_MOVE_TOWARD: {
- VALIDATE_ARG_COUNT(3);
- VALIDATE_ARG_NUM(0);
- VALIDATE_ARG_NUM(1);
- VALIDATE_ARG_NUM(2);
- r_ret = Math::move_toward((double)*p_args[0], (double)*p_args[1], (double)*p_args[2]);
- } break;
- case MATH_DECTIME: {
- VALIDATE_ARG_COUNT(3);
- VALIDATE_ARG_NUM(0);
- VALIDATE_ARG_NUM(1);
- VALIDATE_ARG_NUM(2);
- r_ret = Math::dectime((double)*p_args[0], (double)*p_args[1], (double)*p_args[2]);
- } break;
- case MATH_RANDOMIZE: {
- VALIDATE_ARG_COUNT(0);
- Math::randomize();
- r_ret = Variant();
- } break;
- case MATH_RANDI: {
- VALIDATE_ARG_COUNT(0);
- r_ret = Math::rand();
- } break;
- case MATH_RANDF: {
- VALIDATE_ARG_COUNT(0);
- r_ret = Math::randf();
- } break;
- case MATH_RANDF_RANGE: {
- VALIDATE_ARG_COUNT(2);
- VALIDATE_ARG_NUM(0);
- VALIDATE_ARG_NUM(1);
- r_ret = Math::random((double)*p_args[0], (double)*p_args[1]);
- } break;
- case MATH_RANDI_RANGE: {
- VALIDATE_ARG_COUNT(2);
- VALIDATE_ARG_NUM(0);
- VALIDATE_ARG_NUM(1);
- r_ret = Math::random((int)*p_args[0], (int)*p_args[1]);
- } break;
- case MATH_SEED: {
- VALIDATE_ARG_COUNT(1);
- VALIDATE_ARG_NUM(0);
- uint64_t seed = *p_args[0];
- Math::seed(seed);
- r_ret = Variant();
- } break;
- case MATH_RANDSEED: {
- VALIDATE_ARG_COUNT(1);
- VALIDATE_ARG_NUM(0);
- uint64_t seed = *p_args[0];
- int ret = Math::rand_from_seed(&seed);
- Array reta;
- reta.push_back(ret);
- reta.push_back(seed);
- r_ret = reta;
-
- } break;
- case MATH_DEG2RAD: {
- VALIDATE_ARG_COUNT(1);
- VALIDATE_ARG_NUM(0);
- r_ret = Math::deg2rad((double)*p_args[0]);
- } break;
- case MATH_RAD2DEG: {
- VALIDATE_ARG_COUNT(1);
- VALIDATE_ARG_NUM(0);
- r_ret = Math::rad2deg((double)*p_args[0]);
- } break;
- case MATH_LINEAR2DB: {
- VALIDATE_ARG_COUNT(1);
- VALIDATE_ARG_NUM(0);
- r_ret = Math::linear2db((double)*p_args[0]);
- } break;
- case MATH_DB2LINEAR: {
- VALIDATE_ARG_COUNT(1);
- VALIDATE_ARG_NUM(0);
- r_ret = Math::db2linear((double)*p_args[0]);
- } break;
- case MATH_POLAR2CARTESIAN: {
- VALIDATE_ARG_COUNT(2);
- VALIDATE_ARG_NUM(0);
- VALIDATE_ARG_NUM(1);
- double r = *p_args[0];
- double th = *p_args[1];
- r_ret = Vector2(r * Math::cos(th), r * Math::sin(th));
- } break;
- case MATH_CARTESIAN2POLAR: {
- VALIDATE_ARG_COUNT(2);
- VALIDATE_ARG_NUM(0);
- VALIDATE_ARG_NUM(1);
- double x = *p_args[0];
- double y = *p_args[1];
- r_ret = Vector2(Math::sqrt(x * x + y * y), Math::atan2(y, x));
- } break;
- case MATH_WRAP: {
- VALIDATE_ARG_COUNT(3);
- r_ret = Math::wrapi((int64_t)*p_args[0], (int64_t)*p_args[1], (int64_t)*p_args[2]);
- } break;
- case MATH_WRAPF: {
- VALIDATE_ARG_COUNT(3);
- r_ret = Math::wrapf((double)*p_args[0], (double)*p_args[1], (double)*p_args[2]);
- } break;
- case LOGIC_MAX: {
- VALIDATE_ARG_COUNT(2);
- if (p_args[0]->get_type() == Variant::INT && p_args[1]->get_type() == Variant::INT) {
- int64_t a = *p_args[0];
- int64_t b = *p_args[1];
- r_ret = MAX(a, b);
- } else {
- VALIDATE_ARG_NUM(0);
- VALIDATE_ARG_NUM(1);
-
- double a = *p_args[0];
- double b = *p_args[1];
-
- r_ret = MAX(a, b);
- }
-
- } break;
- case LOGIC_MIN: {
- VALIDATE_ARG_COUNT(2);
- if (p_args[0]->get_type() == Variant::INT && p_args[1]->get_type() == Variant::INT) {
- int64_t a = *p_args[0];
- int64_t b = *p_args[1];
- r_ret = MIN(a, b);
- } else {
- VALIDATE_ARG_NUM(0);
- VALIDATE_ARG_NUM(1);
-
- double a = *p_args[0];
- double b = *p_args[1];
-
- r_ret = MIN(a, b);
- }
- } break;
- case LOGIC_CLAMP: {
- VALIDATE_ARG_COUNT(3);
- if (p_args[0]->get_type() == Variant::INT && p_args[1]->get_type() == Variant::INT && p_args[2]->get_type() == Variant::INT) {
- int64_t a = *p_args[0];
- int64_t b = *p_args[1];
- int64_t c = *p_args[2];
- r_ret = CLAMP(a, b, c);
- } else {
- VALIDATE_ARG_NUM(0);
- VALIDATE_ARG_NUM(1);
- VALIDATE_ARG_NUM(2);
-
- double a = *p_args[0];
- double b = *p_args[1];
- double c = *p_args[2];
-
- r_ret = CLAMP(a, b, c);
- }
- } break;
- case LOGIC_NEAREST_PO2: {
- VALIDATE_ARG_COUNT(1);
- VALIDATE_ARG_NUM(0);
- int64_t num = *p_args[0];
- r_ret = next_power_of_2(num);
- } break;
- case OBJ_WEAKREF: {
- VALIDATE_ARG_COUNT(1);
- if (p_args[0]->get_type() == Variant::OBJECT) {
- if (p_args[0]->is_ref()) {
- Ref<WeakRef> wref = memnew(WeakRef);
- REF r = *p_args[0];
- if (r.is_valid()) {
- wref->set_ref(r);
- }
- r_ret = wref;
- } else {
- Ref<WeakRef> wref = memnew(WeakRef);
- Object *obj = *p_args[0];
- if (obj) {
- wref->set_obj(obj);
- }
- r_ret = wref;
- }
- } else if (p_args[0]->get_type() == Variant::NIL) {
- Ref<WeakRef> wref = memnew(WeakRef);
- r_ret = wref;
- } else {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 0;
- r_error.expected = Variant::OBJECT;
- r_ret = Variant();
- return;
- }
- } break;
- case TYPE_CONVERT: {
- VALIDATE_ARG_COUNT(2);
- VALIDATE_ARG_NUM(1);
- int type = *p_args[1];
- if (type < 0 || type >= Variant::VARIANT_MAX) {
- r_ret = RTR("Invalid type argument to convert(), use TYPE_* constants.");
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 0;
- r_error.expected = Variant::INT;
- return;
-
- } else {
- Variant::construct(Variant::Type(type), r_ret, p_args, 1, r_error);
- }
- } break;
- case TYPE_OF: {
- VALIDATE_ARG_COUNT(1);
- r_ret = p_args[0]->get_type();
-
- } break;
- case TYPE_EXISTS: {
- VALIDATE_ARG_COUNT(1);
- r_ret = ClassDB::class_exists(*p_args[0]);
-
- } break;
- case TEXT_CHAR: {
- VALIDATE_ARG_COUNT(1);
- VALIDATE_ARG_NUM(0);
- char32_t result[2] = { *p_args[0], 0 };
- r_ret = String(result);
- } break;
- case TEXT_ORD: {
- VALIDATE_ARG_COUNT(1);
-
- if (p_args[0]->get_type() != Variant::STRING) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 0;
- r_error.expected = Variant::STRING;
- r_ret = Variant();
- return;
- }
-
- String str = p_args[0]->operator String();
-
- if (str.length() != 1) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 0;
- r_error.expected = Variant::STRING;
- r_ret = RTR("Expected a string of length 1 (a character).");
- return;
- }
-
- r_ret = str.get(0);
-
- } break;
- case TEXT_STR: {
- if (p_arg_count < 1) {
- r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
- r_error.argument = 1;
- r_ret = Variant();
-
- return;
- }
- String str;
- for (int i = 0; i < p_arg_count; i++) {
- String os = p_args[i]->operator String();
-
- if (i == 0) {
- str = os;
- } else {
- str += os;
- }
- }
-
- r_ret = str;
-
- } break;
- case TEXT_PRINT: {
- String str;
- for (int i = 0; i < p_arg_count; i++) {
- str += p_args[i]->operator String();
- }
-
- print_line(str);
- r_ret = Variant();
-
- } break;
- case TEXT_PRINT_TABBED: {
- String str;
- for (int i = 0; i < p_arg_count; i++) {
- if (i) {
- str += "\t";
- }
- str += p_args[i]->operator String();
- }
-
- print_line(str);
- r_ret = Variant();
-
- } break;
- case TEXT_PRINT_SPACED: {
- String str;
- for (int i = 0; i < p_arg_count; i++) {
- if (i) {
- str += " ";
- }
- str += p_args[i]->operator String();
- }
-
- print_line(str);
- r_ret = Variant();
-
- } break;
-
- case TEXT_PRINTERR: {
- String str;
- for (int i = 0; i < p_arg_count; i++) {
- str += p_args[i]->operator String();
- }
-
- print_error(str);
- r_ret = Variant();
-
- } break;
- case TEXT_PRINTRAW: {
- String str;
- for (int i = 0; i < p_arg_count; i++) {
- str += p_args[i]->operator String();
- }
-
- OS::get_singleton()->print("%s", str.utf8().get_data());
- r_ret = Variant();
-
- } break;
- case TEXT_PRINT_DEBUG: {
- String str;
- for (int i = 0; i < p_arg_count; i++) {
- str += p_args[i]->operator String();
- }
-
- ScriptLanguage *script = GDScriptLanguage::get_singleton();
- if (script->debug_get_stack_level_count() > 0) {
- str += "\n At: " + script->debug_get_stack_level_source(0) + ":" + itos(script->debug_get_stack_level_line(0)) + ":" + script->debug_get_stack_level_function(0) + "()";
- }
-
- print_line(str);
- r_ret = Variant();
- } break;
- case PUSH_ERROR: {
- VALIDATE_ARG_COUNT(1);
- if (p_args[0]->get_type() != Variant::STRING) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 0;
- r_error.expected = Variant::STRING;
- r_ret = Variant();
- break;
- }
-
- String message = *p_args[0];
- ERR_PRINT(message);
- r_ret = Variant();
- } break;
- case PUSH_WARNING: {
- VALIDATE_ARG_COUNT(1);
- if (p_args[0]->get_type() != Variant::STRING) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 0;
- r_error.expected = Variant::STRING;
- r_ret = Variant();
- break;
- }
-
- String message = *p_args[0];
- WARN_PRINT(message);
- r_ret = Variant();
- } break;
- case VAR_TO_STR: {
- VALIDATE_ARG_COUNT(1);
- String vars;
- VariantWriter::write_to_string(*p_args[0], vars);
- r_ret = vars;
- } break;
- case STR_TO_VAR: {
- VALIDATE_ARG_COUNT(1);
- if (p_args[0]->get_type() != Variant::STRING) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 0;
- r_error.expected = Variant::STRING;
- r_ret = Variant();
- return;
- }
- r_ret = *p_args[0];
-
- VariantParser::StreamString ss;
- ss.s = *p_args[0];
-
- String errs;
- int line;
- (void)VariantParser::parse(&ss, r_ret, errs, line);
- } break;
- case VAR_TO_BYTES: {
- bool full_objects = false;
- if (p_arg_count < 1) {
- r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
- r_error.argument = 1;
- r_ret = Variant();
- return;
- } else if (p_arg_count > 2) {
- r_error.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS;
- r_error.argument = 2;
- r_ret = Variant();
- } else if (p_arg_count == 2) {
- if (p_args[1]->get_type() != Variant::BOOL) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 1;
- r_error.expected = Variant::BOOL;
- r_ret = Variant();
- return;
- }
- full_objects = *p_args[1];
- }
-
- PackedByteArray barr;
- int len;
- Error err = encode_variant(*p_args[0], nullptr, len, full_objects);
- if (err) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 0;
- r_error.expected = Variant::NIL;
- r_ret = "Unexpected error encoding variable to bytes, likely unserializable type found (Object or RID).";
- return;
- }
-
- barr.resize(len);
- {
- uint8_t *w = barr.ptrw();
- encode_variant(*p_args[0], w, len, full_objects);
- }
- r_ret = barr;
- } break;
- case BYTES_TO_VAR: {
- bool allow_objects = false;
- if (p_arg_count < 1) {
- r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
- r_error.argument = 1;
- r_ret = Variant();
- return;
- } else if (p_arg_count > 2) {
- r_error.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS;
- r_error.argument = 2;
- r_ret = Variant();
- } else if (p_arg_count == 2) {
- if (p_args[1]->get_type() != Variant::BOOL) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 1;
- r_error.expected = Variant::BOOL;
- r_ret = Variant();
- return;
- }
- allow_objects = *p_args[1];
- }
-
- if (p_args[0]->get_type() != Variant::PACKED_BYTE_ARRAY) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 1;
- r_error.expected = Variant::PACKED_BYTE_ARRAY;
- r_ret = Variant();
- return;
- }
-
- PackedByteArray varr = *p_args[0];
- Variant ret;
- {
- const uint8_t *r = varr.ptr();
- Error err = decode_variant(ret, r, varr.size(), nullptr, allow_objects);
- if (err != OK) {
- r_ret = RTR("Not enough bytes for decoding bytes, or invalid format.");
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 0;
- r_error.expected = Variant::PACKED_BYTE_ARRAY;
- return;
- }
- }
-
- r_ret = ret;
-
- } break;
- case GEN_RANGE: {
- switch (p_arg_count) {
- case 0: {
- r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
- r_error.argument = 1;
- r_error.expected = 1;
- r_ret = Variant();
-
- } break;
- case 1: {
- VALIDATE_ARG_NUM(0);
- int count = *p_args[0];
- Array arr;
- if (count <= 0) {
- r_ret = arr;
- return;
- }
- Error err = arr.resize(count);
- if (err != OK) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
- r_ret = Variant();
- return;
- }
-
- for (int i = 0; i < count; i++) {
- arr[i] = i;
- }
-
- r_ret = arr;
- } break;
- case 2: {
- VALIDATE_ARG_NUM(0);
- VALIDATE_ARG_NUM(1);
-
- int from = *p_args[0];
- int to = *p_args[1];
-
- Array arr;
- if (from >= to) {
- r_ret = arr;
- return;
- }
- Error err = arr.resize(to - from);
- if (err != OK) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
- r_ret = Variant();
- return;
- }
- for (int i = from; i < to; i++) {
- arr[i - from] = i;
- }
- r_ret = arr;
- } break;
- case 3: {
- VALIDATE_ARG_NUM(0);
- VALIDATE_ARG_NUM(1);
- VALIDATE_ARG_NUM(2);
-
- int from = *p_args[0];
- int to = *p_args[1];
- int incr = *p_args[2];
- if (incr == 0) {
- r_ret = RTR("Step argument is zero!");
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
- return;
- }
-
- Array arr;
- if (from >= to && incr > 0) {
- r_ret = arr;
- return;
- }
- if (from <= to && incr < 0) {
- r_ret = arr;
- return;
- }
-
- //calculate how many
- int count = 0;
- if (incr > 0) {
- count = ((to - from - 1) / incr) + 1;
- } else {
- count = ((from - to - 1) / -incr) + 1;
- }
-
- Error err = arr.resize(count);
-
- if (err != OK) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
- r_ret = Variant();
- return;
- }
-
- if (incr > 0) {
- int idx = 0;
- for (int i = from; i < to; i += incr) {
- arr[idx++] = i;
- }
- } else {
- int idx = 0;
- for (int i = from; i > to; i += incr) {
- arr[idx++] = i;
- }
- }
-
- r_ret = arr;
- } break;
- default: {
- r_error.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS;
- r_error.argument = 3;
- r_error.expected = 3;
- r_ret = Variant();
-
- } break;
- }
-
- } break;
- case RESOURCE_LOAD: {
- VALIDATE_ARG_COUNT(1);
- if (p_args[0]->get_type() != Variant::STRING) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 0;
- r_error.expected = Variant::STRING;
- r_ret = Variant();
- } else {
- r_ret = ResourceLoader::load(*p_args[0]);
- }
-
- } break;
- case INST2DICT: {
- VALIDATE_ARG_COUNT(1);
-
- if (p_args[0]->get_type() == Variant::NIL) {
- r_ret = Variant();
- } else if (p_args[0]->get_type() != Variant::OBJECT) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 0;
- r_ret = Variant();
- } else {
- Object *obj = *p_args[0];
- if (!obj) {
- r_ret = Variant();
-
- } else if (!obj->get_script_instance() || obj->get_script_instance()->get_language() != GDScriptLanguage::get_singleton()) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 0;
- r_error.expected = Variant::DICTIONARY;
- r_ret = RTR("Not a script with an instance");
- return;
- } else {
- GDScriptInstance *ins = static_cast<GDScriptInstance *>(obj->get_script_instance());
- Ref<GDScript> base = ins->get_script();
- if (base.is_null()) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 0;
- r_error.expected = Variant::DICTIONARY;
- r_ret = RTR("Not based on a script");
- return;
- }
-
- GDScript *p = base.ptr();
- Vector<StringName> sname;
-
- while (p->_owner) {
- sname.push_back(p->name);
- p = p->_owner;
- }
- sname.invert();
-
- if (!p->path.is_resource_file()) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 0;
- r_error.expected = Variant::DICTIONARY;
- r_ret = Variant();
-
- r_ret = RTR("Not based on a resource file");
-
- return;
- }
-
- NodePath cp(sname, Vector<StringName>(), false);
-
- Dictionary d;
- d["@subpath"] = cp;
- d["@path"] = p->get_path();
-
- for (Map<StringName, GDScript::MemberInfo>::Element *E = base->member_indices.front(); E; E = E->next()) {
- if (!d.has(E->key())) {
- d[E->key()] = ins->members[E->get().index];
- }
- }
- r_ret = d;
- }
- }
-
- } break;
- case DICT2INST: {
- VALIDATE_ARG_COUNT(1);
-
- if (p_args[0]->get_type() != Variant::DICTIONARY) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 0;
- r_error.expected = Variant::DICTIONARY;
- r_ret = Variant();
-
- return;
- }
-
- Dictionary d = *p_args[0];
-
- if (!d.has("@path")) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 0;
- r_error.expected = Variant::OBJECT;
- r_ret = RTR("Invalid instance dictionary format (missing @path)");
-
- return;
- }
-
- Ref<Script> scr = ResourceLoader::load(d["@path"]);
- if (!scr.is_valid()) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 0;
- r_error.expected = Variant::OBJECT;
- r_ret = RTR("Invalid instance dictionary format (can't load script at @path)");
- return;
- }
-
- Ref<GDScript> gdscr = scr;
-
- if (!gdscr.is_valid()) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 0;
- r_error.expected = Variant::OBJECT;
- r_ret = Variant();
- r_ret = RTR("Invalid instance dictionary format (invalid script at @path)");
- return;
- }
-
- NodePath sub;
- if (d.has("@subpath")) {
- sub = d["@subpath"];
- }
-
- for (int i = 0; i < sub.get_name_count(); i++) {
- gdscr = gdscr->subclasses[sub.get_name(i)];
- if (!gdscr.is_valid()) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 0;
- r_error.expected = Variant::OBJECT;
- r_ret = Variant();
- r_ret = RTR("Invalid instance dictionary (invalid subclasses)");
- return;
- }
- }
- r_ret = gdscr->_new(nullptr, -1 /*skip initializer*/, r_error);
-
- if (r_error.error != Callable::CallError::CALL_OK) {
- r_ret = Variant();
- return;
- }
-
- GDScriptInstance *ins = static_cast<GDScriptInstance *>(static_cast<Object *>(r_ret)->get_script_instance());
- Ref<GDScript> gd_ref = ins->get_script();
-
- for (Map<StringName, GDScript::MemberInfo>::Element *E = gd_ref->member_indices.front(); E; E = E->next()) {
- if (d.has(E->key())) {
- ins->members.write[E->get().index] = d[E->key()];
- }
- }
-
- } break;
- case VALIDATE_JSON: {
- VALIDATE_ARG_COUNT(1);
-
- if (p_args[0]->get_type() != Variant::STRING) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 0;
- r_error.expected = Variant::STRING;
- r_ret = Variant();
- return;
- }
-
- String errs;
- int errl;
-
- Error err = JSON::parse(*p_args[0], r_ret, errs, errl);
-
- if (err != OK) {
- r_ret = itos(errl) + ":" + errs;
- } else {
- r_ret = "";
- }
-
- } break;
- case PARSE_JSON: {
- VALIDATE_ARG_COUNT(1);
-
- if (p_args[0]->get_type() != Variant::STRING) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 0;
- r_error.expected = Variant::STRING;
- r_ret = Variant();
- return;
- }
-
- String errs;
- int errl;
-
- Error err = JSON::parse(*p_args[0], r_ret, errs, errl);
-
- if (err != OK) {
- r_ret = Variant();
- ERR_PRINT(vformat("Error parsing JSON at line %s: %s", errl, errs));
- }
-
- } break;
- case TO_JSON: {
- VALIDATE_ARG_COUNT(1);
-
- r_ret = JSON::print(*p_args[0]);
- } break;
- case HASH: {
- VALIDATE_ARG_COUNT(1);
- r_ret = p_args[0]->hash();
-
- } break;
- case COLOR8: {
- if (p_arg_count < 3) {
- r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
- r_error.argument = 3;
- r_ret = Variant();
-
- return;
- }
- if (p_arg_count > 4) {
- r_error.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS;
- r_error.argument = 4;
- r_ret = Variant();
-
- return;
- }
-
- VALIDATE_ARG_NUM(0);
- VALIDATE_ARG_NUM(1);
- VALIDATE_ARG_NUM(2);
-
- Color color((float)*p_args[0] / 255.0f, (float)*p_args[1] / 255.0f, (float)*p_args[2] / 255.0f);
-
- if (p_arg_count == 4) {
- VALIDATE_ARG_NUM(3);
- color.a = (float)*p_args[3] / 255.0f;
- }
-
- r_ret = color;
-
- } break;
- case COLORN: {
- if (p_arg_count < 1) {
- r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
- r_error.argument = 1;
- r_ret = Variant();
- return;
- }
-
- if (p_arg_count > 2) {
- r_error.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS;
- r_error.argument = 2;
- r_ret = Variant();
- return;
- }
-
- if (p_args[0]->get_type() != Variant::STRING) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 0;
- r_ret = Variant();
- } else {
- Color color = Color::named(*p_args[0]);
- if (p_arg_count == 2) {
- VALIDATE_ARG_NUM(1);
- color.a = *p_args[1];
- }
- r_ret = color;
- }
-
- } break;
-
- case PRINT_STACK: {
- VALIDATE_ARG_COUNT(0);
-
- ScriptLanguage *script = GDScriptLanguage::get_singleton();
- for (int i = 0; i < script->debug_get_stack_level_count(); i++) {
- print_line("Frame " + itos(i) + " - " + script->debug_get_stack_level_source(i) + ":" + itos(script->debug_get_stack_level_line(i)) + " in function '" + script->debug_get_stack_level_function(i) + "'");
- };
- } break;
-
- case GET_STACK: {
- VALIDATE_ARG_COUNT(0);
-
- ScriptLanguage *script = GDScriptLanguage::get_singleton();
- Array ret;
- for (int i = 0; i < script->debug_get_stack_level_count(); i++) {
- Dictionary frame;
- frame["source"] = script->debug_get_stack_level_source(i);
- frame["function"] = script->debug_get_stack_level_function(i);
- frame["line"] = script->debug_get_stack_level_line(i);
- ret.push_back(frame);
- };
- r_ret = ret;
- } break;
-
- case INSTANCE_FROM_ID: {
- VALIDATE_ARG_COUNT(1);
- if (p_args[0]->get_type() != Variant::INT && p_args[0]->get_type() != Variant::FLOAT) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 0;
- r_error.expected = Variant::INT;
- r_ret = Variant();
- break;
- }
-
- ObjectID id = *p_args[0];
- r_ret = ObjectDB::get_instance(id);
-
- } break;
- case LEN: {
- VALIDATE_ARG_COUNT(1);
- switch (p_args[0]->get_type()) {
- case Variant::STRING: {
- String d = *p_args[0];
- r_ret = d.length();
- } break;
- case Variant::DICTIONARY: {
- Dictionary d = *p_args[0];
- r_ret = d.size();
- } break;
- case Variant::ARRAY: {
- Array d = *p_args[0];
- r_ret = d.size();
- } break;
- case Variant::PACKED_BYTE_ARRAY: {
- Vector<uint8_t> d = *p_args[0];
- r_ret = d.size();
- } break;
- case Variant::PACKED_INT32_ARRAY: {
- Vector<int32_t> d = *p_args[0];
- r_ret = d.size();
- } break;
- case Variant::PACKED_INT64_ARRAY: {
- Vector<int64_t> d = *p_args[0];
- r_ret = d.size();
- } break;
- case Variant::PACKED_FLOAT32_ARRAY: {
- Vector<float> d = *p_args[0];
- r_ret = d.size();
- } break;
- case Variant::PACKED_FLOAT64_ARRAY: {
- Vector<double> d = *p_args[0];
- r_ret = d.size();
- } break;
- case Variant::PACKED_STRING_ARRAY: {
- Vector<String> d = *p_args[0];
- r_ret = d.size();
- } break;
- case Variant::PACKED_VECTOR2_ARRAY: {
- Vector<Vector2> d = *p_args[0];
- r_ret = d.size();
- } break;
- case Variant::PACKED_VECTOR3_ARRAY: {
- Vector<Vector3> d = *p_args[0];
- r_ret = d.size();
- } break;
- case Variant::PACKED_COLOR_ARRAY: {
- Vector<Color> d = *p_args[0];
- r_ret = d.size();
- } break;
- default: {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 0;
- r_error.expected = Variant::OBJECT;
- r_ret = Variant();
- r_ret = RTR("Object can't provide a length.");
- }
- }
-
- } break;
- case IS_INSTANCE_VALID: {
- VALIDATE_ARG_COUNT(1);
- if (p_args[0]->get_type() != Variant::OBJECT) {
- r_ret = false;
- } else {
- Object *obj = p_args[0]->get_validated_object();
- r_ret = obj != nullptr;
- }
-
- } break;
- case FUNC_MAX: {
- ERR_FAIL();
- } break;
- }
-}
-
-bool GDScriptFunctions::is_deterministic(Function p_func) {
- //man i couldn't have chosen a worse function name,
- //way too controversial..
-
- switch (p_func) {
- case MATH_SIN:
- case MATH_COS:
- case MATH_TAN:
- case MATH_SINH:
- case MATH_COSH:
- case MATH_TANH:
- case MATH_ASIN:
- case MATH_ACOS:
- case MATH_ATAN:
- case MATH_ATAN2:
- case MATH_SQRT:
- case MATH_FMOD:
- case MATH_FPOSMOD:
- case MATH_POSMOD:
- case MATH_FLOOR:
- case MATH_CEIL:
- case MATH_ROUND:
- case MATH_ABS:
- case MATH_SIGN:
- case MATH_POW:
- case MATH_LOG:
- case MATH_EXP:
- case MATH_ISNAN:
- case MATH_ISINF:
- case MATH_EASE:
- case MATH_STEP_DECIMALS:
- case MATH_STEPIFY:
- case MATH_LERP:
- case MATH_INVERSE_LERP:
- case MATH_RANGE_LERP:
- case MATH_SMOOTHSTEP:
- case MATH_MOVE_TOWARD:
- case MATH_DECTIME:
- case MATH_DEG2RAD:
- case MATH_RAD2DEG:
- case MATH_LINEAR2DB:
- case MATH_DB2LINEAR:
- case MATH_POLAR2CARTESIAN:
- case MATH_CARTESIAN2POLAR:
- case MATH_WRAP:
- case MATH_WRAPF:
- case LOGIC_MAX:
- case LOGIC_MIN:
- case LOGIC_CLAMP:
- case LOGIC_NEAREST_PO2:
- case TYPE_CONVERT:
- case TYPE_OF:
- case TYPE_EXISTS:
- case TEXT_CHAR:
- case TEXT_ORD:
- case TEXT_STR:
- case COLOR8:
- case LEN:
- // enable for debug only, otherwise not desirable - case GEN_RANGE:
- return true;
- default:
- return false;
- }
-
- return false;
-}
-
-MethodInfo GDScriptFunctions::get_info(Function p_func) {
-#ifdef DEBUG_ENABLED
- //using a switch, so the compiler generates a jumptable
-
- switch (p_func) {
- case MATH_SIN: {
- MethodInfo mi("sin", PropertyInfo(Variant::FLOAT, "s"));
- mi.return_val.type = Variant::FLOAT;
- return mi;
-
- } break;
- case MATH_COS: {
- MethodInfo mi("cos", PropertyInfo(Variant::FLOAT, "s"));
- mi.return_val.type = Variant::FLOAT;
- return mi;
- } break;
- case MATH_TAN: {
- MethodInfo mi("tan", PropertyInfo(Variant::FLOAT, "s"));
- mi.return_val.type = Variant::FLOAT;
- return mi;
- } break;
- case MATH_SINH: {
- MethodInfo mi("sinh", PropertyInfo(Variant::FLOAT, "s"));
- mi.return_val.type = Variant::FLOAT;
- return mi;
- } break;
- case MATH_COSH: {
- MethodInfo mi("cosh", PropertyInfo(Variant::FLOAT, "s"));
- mi.return_val.type = Variant::FLOAT;
- return mi;
- } break;
- case MATH_TANH: {
- MethodInfo mi("tanh", PropertyInfo(Variant::FLOAT, "s"));
- mi.return_val.type = Variant::FLOAT;
- return mi;
- } break;
- case MATH_ASIN: {
- MethodInfo mi("asin", PropertyInfo(Variant::FLOAT, "s"));
- mi.return_val.type = Variant::FLOAT;
- return mi;
- } break;
- case MATH_ACOS: {
- MethodInfo mi("acos", PropertyInfo(Variant::FLOAT, "s"));
- mi.return_val.type = Variant::FLOAT;
- return mi;
- } break;
- case MATH_ATAN: {
- MethodInfo mi("atan", PropertyInfo(Variant::FLOAT, "s"));
- mi.return_val.type = Variant::FLOAT;
- return mi;
- } break;
- case MATH_ATAN2: {
- MethodInfo mi("atan2", PropertyInfo(Variant::FLOAT, "y"), PropertyInfo(Variant::FLOAT, "x"));
- mi.return_val.type = Variant::FLOAT;
- return mi;
- } break;
- case MATH_SQRT: {
- MethodInfo mi("sqrt", PropertyInfo(Variant::FLOAT, "s"));
- mi.return_val.type = Variant::FLOAT;
- return mi;
- } break;
- case MATH_FMOD: {
- MethodInfo mi("fmod", PropertyInfo(Variant::FLOAT, "a"), PropertyInfo(Variant::FLOAT, "b"));
- mi.return_val.type = Variant::FLOAT;
- return mi;
- } break;
- case MATH_FPOSMOD: {
- MethodInfo mi("fposmod", PropertyInfo(Variant::FLOAT, "a"), PropertyInfo(Variant::FLOAT, "b"));
- mi.return_val.type = Variant::FLOAT;
- return mi;
- } break;
- case MATH_POSMOD: {
- MethodInfo mi("posmod", PropertyInfo(Variant::INT, "a"), PropertyInfo(Variant::INT, "b"));
- mi.return_val.type = Variant::INT;
- return mi;
- } break;
- case MATH_FLOOR: {
- MethodInfo mi("floor", PropertyInfo(Variant::FLOAT, "s"));
- mi.return_val.type = Variant::FLOAT;
- return mi;
- } break;
- case MATH_CEIL: {
- MethodInfo mi("ceil", PropertyInfo(Variant::FLOAT, "s"));
- mi.return_val.type = Variant::FLOAT;
- return mi;
- } break;
- case MATH_ROUND: {
- MethodInfo mi("round", PropertyInfo(Variant::FLOAT, "s"));
- mi.return_val.type = Variant::FLOAT;
- return mi;
- } break;
- case MATH_ABS: {
- MethodInfo mi("abs", PropertyInfo(Variant::FLOAT, "s"));
- mi.return_val.type = Variant::FLOAT;
- return mi;
- } break;
- case MATH_SIGN: {
- MethodInfo mi("sign", PropertyInfo(Variant::FLOAT, "s"));
- mi.return_val.type = Variant::FLOAT;
- return mi;
- } break;
- case MATH_POW: {
- MethodInfo mi("pow", PropertyInfo(Variant::FLOAT, "base"), PropertyInfo(Variant::FLOAT, "exp"));
- mi.return_val.type = Variant::FLOAT;
- return mi;
- } break;
- case MATH_LOG: {
- MethodInfo mi("log", PropertyInfo(Variant::FLOAT, "s"));
- mi.return_val.type = Variant::FLOAT;
- return mi;
- } break;
- case MATH_EXP: {
- MethodInfo mi("exp", PropertyInfo(Variant::FLOAT, "s"));
- mi.return_val.type = Variant::FLOAT;
- return mi;
- } break;
- case MATH_ISNAN: {
- MethodInfo mi("is_nan", PropertyInfo(Variant::FLOAT, "s"));
- mi.return_val.type = Variant::BOOL;
- return mi;
- } break;
- case MATH_ISINF: {
- MethodInfo mi("is_inf", PropertyInfo(Variant::FLOAT, "s"));
- mi.return_val.type = Variant::BOOL;
- return mi;
- } break;
- case MATH_ISEQUALAPPROX: {
- MethodInfo mi("is_equal_approx", PropertyInfo(Variant::FLOAT, "a"), PropertyInfo(Variant::FLOAT, "b"));
- mi.return_val.type = Variant::BOOL;
- return mi;
- } break;
- case MATH_ISZEROAPPROX: {
- MethodInfo mi("is_zero_approx", PropertyInfo(Variant::FLOAT, "s"));
- mi.return_val.type = Variant::BOOL;
- return mi;
- } break;
- case MATH_EASE: {
- MethodInfo mi("ease", PropertyInfo(Variant::FLOAT, "s"), PropertyInfo(Variant::FLOAT, "curve"));
- mi.return_val.type = Variant::FLOAT;
- return mi;
- } break;
- case MATH_STEP_DECIMALS: {
- MethodInfo mi("step_decimals", PropertyInfo(Variant::FLOAT, "step"));
- mi.return_val.type = Variant::INT;
- return mi;
- } break;
- case MATH_STEPIFY: {
- MethodInfo mi("stepify", PropertyInfo(Variant::FLOAT, "s"), PropertyInfo(Variant::FLOAT, "step"));
- mi.return_val.type = Variant::FLOAT;
- return mi;
- } break;
- case MATH_LERP: {
- MethodInfo mi("lerp", PropertyInfo(Variant::NIL, "from"), PropertyInfo(Variant::NIL, "to"), PropertyInfo(Variant::FLOAT, "weight"));
- mi.return_val.type = Variant::NIL;
- mi.return_val.usage |= PROPERTY_USAGE_NIL_IS_VARIANT;
- return mi;
- } break;
- case MATH_LERP_ANGLE: {
- MethodInfo mi("lerp_angle", PropertyInfo(Variant::FLOAT, "from"), PropertyInfo(Variant::FLOAT, "to"), PropertyInfo(Variant::FLOAT, "weight"));
- mi.return_val.type = Variant::FLOAT;
- return mi;
- } break;
- case MATH_INVERSE_LERP: {
- MethodInfo mi("inverse_lerp", PropertyInfo(Variant::FLOAT, "from"), PropertyInfo(Variant::FLOAT, "to"), PropertyInfo(Variant::FLOAT, "weight"));
- mi.return_val.type = Variant::FLOAT;
- return mi;
- } break;
- case MATH_RANGE_LERP: {
- MethodInfo mi("range_lerp", PropertyInfo(Variant::FLOAT, "value"), PropertyInfo(Variant::FLOAT, "istart"), PropertyInfo(Variant::FLOAT, "istop"), PropertyInfo(Variant::FLOAT, "ostart"), PropertyInfo(Variant::FLOAT, "ostop"));
- mi.return_val.type = Variant::FLOAT;
- return mi;
- } break;
- case MATH_SMOOTHSTEP: {
- MethodInfo mi("smoothstep", PropertyInfo(Variant::FLOAT, "from"), PropertyInfo(Variant::FLOAT, "to"), PropertyInfo(Variant::FLOAT, "s"));
- mi.return_val.type = Variant::FLOAT;
- return mi;
- } break;
- case MATH_MOVE_TOWARD: {
- MethodInfo mi("move_toward", PropertyInfo(Variant::FLOAT, "from"), PropertyInfo(Variant::FLOAT, "to"), PropertyInfo(Variant::FLOAT, "delta"));
- mi.return_val.type = Variant::FLOAT;
- return mi;
- } break;
- case MATH_DECTIME: {
- MethodInfo mi("dectime", PropertyInfo(Variant::FLOAT, "value"), PropertyInfo(Variant::FLOAT, "amount"), PropertyInfo(Variant::FLOAT, "step"));
- mi.return_val.type = Variant::FLOAT;
- return mi;
- } break;
- case MATH_RANDOMIZE: {
- MethodInfo mi("randomize");
- mi.return_val.type = Variant::NIL;
- return mi;
- } break;
- case MATH_RANDI: {
- MethodInfo mi("randi");
- mi.return_val.type = Variant::INT;
- return mi;
- } break;
- case MATH_RANDF: {
- MethodInfo mi("randf");
- mi.return_val.type = Variant::FLOAT;
- return mi;
- } break;
- case MATH_RANDF_RANGE: {
- MethodInfo mi("randf_range", PropertyInfo(Variant::FLOAT, "from"), PropertyInfo(Variant::FLOAT, "to"));
- mi.return_val.type = Variant::FLOAT;
- return mi;
- } break;
- case MATH_RANDI_RANGE: {
- MethodInfo mi("randi_range", PropertyInfo(Variant::INT, "from"), PropertyInfo(Variant::INT, "to"));
- mi.return_val.type = Variant::INT;
- return mi;
- } break;
- case MATH_SEED: {
- MethodInfo mi("seed", PropertyInfo(Variant::INT, "seed"));
- mi.return_val.type = Variant::NIL;
- return mi;
- } break;
- case MATH_RANDSEED: {
- MethodInfo mi("rand_seed", PropertyInfo(Variant::INT, "seed"));
- mi.return_val.type = Variant::ARRAY;
- return mi;
- } break;
- case MATH_DEG2RAD: {
- MethodInfo mi("deg2rad", PropertyInfo(Variant::FLOAT, "deg"));
- mi.return_val.type = Variant::FLOAT;
- return mi;
- } break;
- case MATH_RAD2DEG: {
- MethodInfo mi("rad2deg", PropertyInfo(Variant::FLOAT, "rad"));
- mi.return_val.type = Variant::FLOAT;
- return mi;
- } break;
- case MATH_LINEAR2DB: {
- MethodInfo mi("linear2db", PropertyInfo(Variant::FLOAT, "nrg"));
- mi.return_val.type = Variant::FLOAT;
- return mi;
- } break;
- case MATH_DB2LINEAR: {
- MethodInfo mi("db2linear", PropertyInfo(Variant::FLOAT, "db"));
- mi.return_val.type = Variant::FLOAT;
- return mi;
- } break;
- case MATH_POLAR2CARTESIAN: {
- MethodInfo mi("polar2cartesian", PropertyInfo(Variant::FLOAT, "r"), PropertyInfo(Variant::FLOAT, "th"));
- mi.return_val.type = Variant::VECTOR2;
- return mi;
- } break;
- case MATH_CARTESIAN2POLAR: {
- MethodInfo mi("cartesian2polar", PropertyInfo(Variant::FLOAT, "x"), PropertyInfo(Variant::FLOAT, "y"));
- mi.return_val.type = Variant::VECTOR2;
- return mi;
- } break;
- case MATH_WRAP: {
- MethodInfo mi("wrapi", PropertyInfo(Variant::INT, "value"), PropertyInfo(Variant::INT, "min"), PropertyInfo(Variant::INT, "max"));
- mi.return_val.type = Variant::INT;
- return mi;
- } break;
- case MATH_WRAPF: {
- MethodInfo mi("wrapf", PropertyInfo(Variant::FLOAT, "value"), PropertyInfo(Variant::FLOAT, "min"), PropertyInfo(Variant::FLOAT, "max"));
- mi.return_val.type = Variant::FLOAT;
- return mi;
- } break;
- case LOGIC_MAX: {
- MethodInfo mi("max", PropertyInfo(Variant::FLOAT, "a"), PropertyInfo(Variant::FLOAT, "b"));
- mi.return_val.type = Variant::FLOAT;
- return mi;
-
- } break;
- case LOGIC_MIN: {
- MethodInfo mi("min", PropertyInfo(Variant::FLOAT, "a"), PropertyInfo(Variant::FLOAT, "b"));
- mi.return_val.type = Variant::FLOAT;
- return mi;
- } break;
- case LOGIC_CLAMP: {
- MethodInfo mi("clamp", PropertyInfo(Variant::FLOAT, "value"), PropertyInfo(Variant::FLOAT, "min"), PropertyInfo(Variant::FLOAT, "max"));
- mi.return_val.type = Variant::FLOAT;
- return mi;
- } break;
- case LOGIC_NEAREST_PO2: {
- MethodInfo mi("nearest_po2", PropertyInfo(Variant::INT, "value"));
- mi.return_val.type = Variant::INT;
- return mi;
- } break;
- case OBJ_WEAKREF: {
- MethodInfo mi("weakref", PropertyInfo(Variant::OBJECT, "obj"));
- mi.return_val.type = Variant::OBJECT;
- mi.return_val.class_name = "WeakRef";
-
- return mi;
-
- } break;
- case TYPE_CONVERT: {
- MethodInfo mi("convert", PropertyInfo(Variant::NIL, "what", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_NIL_IS_VARIANT), PropertyInfo(Variant::INT, "type"));
- mi.return_val.type = Variant::NIL;
- mi.return_val.usage |= PROPERTY_USAGE_NIL_IS_VARIANT;
- return mi;
- } break;
- case TYPE_OF: {
- MethodInfo mi("typeof", PropertyInfo(Variant::NIL, "what", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_NIL_IS_VARIANT));
- mi.return_val.type = Variant::INT;
- return mi;
-
- } break;
- case TYPE_EXISTS: {
- MethodInfo mi("type_exists", PropertyInfo(Variant::STRING, "type"));
- mi.return_val.type = Variant::BOOL;
- return mi;
-
- } break;
- case TEXT_CHAR: {
- MethodInfo mi("char", PropertyInfo(Variant::INT, "code"));
- mi.return_val.type = Variant::STRING;
- return mi;
-
- } break;
- case TEXT_ORD: {
- MethodInfo mi("ord", PropertyInfo(Variant::STRING, "char"));
- mi.return_val.type = Variant::INT;
- return mi;
-
- } break;
- case TEXT_STR: {
- MethodInfo mi("str");
- mi.return_val.type = Variant::STRING;
- mi.flags |= METHOD_FLAG_VARARG;
- return mi;
-
- } break;
- case TEXT_PRINT: {
- MethodInfo mi("print");
- mi.return_val.type = Variant::NIL;
- mi.flags |= METHOD_FLAG_VARARG;
- return mi;
-
- } break;
- case TEXT_PRINT_TABBED: {
- MethodInfo mi("printt");
- mi.return_val.type = Variant::NIL;
- mi.flags |= METHOD_FLAG_VARARG;
- return mi;
-
- } break;
- case TEXT_PRINT_SPACED: {
- MethodInfo mi("prints");
- mi.return_val.type = Variant::NIL;
- mi.flags |= METHOD_FLAG_VARARG;
- return mi;
-
- } break;
- case TEXT_PRINTERR: {
- MethodInfo mi("printerr");
- mi.return_val.type = Variant::NIL;
- mi.flags |= METHOD_FLAG_VARARG;
- return mi;
-
- } break;
- case TEXT_PRINTRAW: {
- MethodInfo mi("printraw");
- mi.return_val.type = Variant::NIL;
- mi.flags |= METHOD_FLAG_VARARG;
- return mi;
-
- } break;
- case TEXT_PRINT_DEBUG: {
- MethodInfo mi("print_debug");
- mi.return_val.type = Variant::NIL;
- mi.flags |= METHOD_FLAG_VARARG;
- return mi;
-
- } break;
- case PUSH_ERROR: {
- MethodInfo mi(Variant::NIL, "push_error", PropertyInfo(Variant::STRING, "message"));
- mi.return_val.type = Variant::NIL;
- return mi;
-
- } break;
- case PUSH_WARNING: {
- MethodInfo mi(Variant::NIL, "push_warning", PropertyInfo(Variant::STRING, "message"));
- mi.return_val.type = Variant::NIL;
- return mi;
-
- } break;
- case VAR_TO_STR: {
- MethodInfo mi("var2str", PropertyInfo(Variant::NIL, "var", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_NIL_IS_VARIANT));
- mi.return_val.type = Variant::STRING;
- return mi;
- } break;
- case STR_TO_VAR: {
- MethodInfo mi(Variant::NIL, "str2var", PropertyInfo(Variant::STRING, "string"));
- mi.return_val.type = Variant::NIL;
- mi.return_val.usage |= PROPERTY_USAGE_NIL_IS_VARIANT;
- return mi;
- } break;
- case VAR_TO_BYTES: {
- MethodInfo mi("var2bytes", PropertyInfo(Variant::NIL, "var", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_NIL_IS_VARIANT), PropertyInfo(Variant::BOOL, "full_objects"));
- mi.default_arguments.push_back(false);
- mi.return_val.type = Variant::PACKED_BYTE_ARRAY;
- return mi;
- } break;
- case BYTES_TO_VAR: {
- MethodInfo mi(Variant::NIL, "bytes2var", PropertyInfo(Variant::PACKED_BYTE_ARRAY, "bytes"), PropertyInfo(Variant::BOOL, "allow_objects"));
- mi.default_arguments.push_back(false);
- mi.return_val.type = Variant::NIL;
- mi.return_val.usage |= PROPERTY_USAGE_NIL_IS_VARIANT;
- return mi;
- } break;
- case GEN_RANGE: {
- MethodInfo mi("range");
- mi.return_val.type = Variant::ARRAY;
- mi.flags |= METHOD_FLAG_VARARG;
- return mi;
- } break;
- case RESOURCE_LOAD: {
- MethodInfo mi("load", PropertyInfo(Variant::STRING, "path"));
- mi.return_val.type = Variant::OBJECT;
- mi.return_val.class_name = "Resource";
- return mi;
- } break;
- case INST2DICT: {
- MethodInfo mi("inst2dict", PropertyInfo(Variant::OBJECT, "inst"));
- mi.return_val.type = Variant::DICTIONARY;
- return mi;
- } break;
- case DICT2INST: {
- MethodInfo mi("dict2inst", PropertyInfo(Variant::DICTIONARY, "dict"));
- mi.return_val.type = Variant::OBJECT;
- return mi;
- } break;
- case VALIDATE_JSON: {
- MethodInfo mi("validate_json", PropertyInfo(Variant::STRING, "json"));
- mi.return_val.type = Variant::STRING;
- return mi;
- } break;
- case PARSE_JSON: {
- MethodInfo mi(Variant::NIL, "parse_json", PropertyInfo(Variant::STRING, "json"));
- mi.return_val.type = Variant::NIL;
- mi.return_val.usage |= PROPERTY_USAGE_NIL_IS_VARIANT;
- return mi;
- } break;
- case TO_JSON: {
- MethodInfo mi("to_json", PropertyInfo(Variant::NIL, "var", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_NIL_IS_VARIANT));
- mi.return_val.type = Variant::STRING;
- return mi;
- } break;
- case HASH: {
- MethodInfo mi("hash", PropertyInfo(Variant::NIL, "var", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_NIL_IS_VARIANT));
- mi.return_val.type = Variant::INT;
- return mi;
- } break;
- case COLOR8: {
- MethodInfo mi("Color8", PropertyInfo(Variant::INT, "r8"), PropertyInfo(Variant::INT, "g8"), PropertyInfo(Variant::INT, "b8"), PropertyInfo(Variant::INT, "a8"));
- mi.default_arguments.push_back(255);
- mi.return_val.type = Variant::COLOR;
- return mi;
- } break;
- case COLORN: {
- MethodInfo mi("ColorN", PropertyInfo(Variant::STRING, "name"), PropertyInfo(Variant::FLOAT, "alpha"));
- mi.default_arguments.push_back(1.0f);
- mi.return_val.type = Variant::COLOR;
- return mi;
- } break;
-
- case PRINT_STACK: {
- MethodInfo mi("print_stack");
- mi.return_val.type = Variant::NIL;
- return mi;
- } break;
- case GET_STACK: {
- MethodInfo mi("get_stack");
- mi.return_val.type = Variant::ARRAY;
- return mi;
- } break;
-
- case INSTANCE_FROM_ID: {
- MethodInfo mi("instance_from_id", PropertyInfo(Variant::INT, "instance_id"));
- mi.return_val.type = Variant::OBJECT;
- return mi;
- } break;
- case LEN: {
- MethodInfo mi("len", PropertyInfo(Variant::NIL, "var", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_NIL_IS_VARIANT));
- mi.return_val.type = Variant::INT;
- return mi;
- } break;
- case IS_INSTANCE_VALID: {
- MethodInfo mi("is_instance_valid", PropertyInfo(Variant::OBJECT, "instance"));
- mi.return_val.type = Variant::BOOL;
- return mi;
- } break;
- default: {
- ERR_FAIL_V(MethodInfo());
- } break;
- }
-#endif
- MethodInfo mi;
- mi.return_val.usage |= PROPERTY_USAGE_NIL_IS_VARIANT;
- return mi;
-}
diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp
index fde3662d66..695154e9a9 100644
--- a/modules/gdscript/gdscript_parser.cpp
+++ b/modules/gdscript/gdscript_parser.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -47,7 +47,7 @@
static HashMap<StringName, Variant::Type> builtin_types;
Variant::Type GDScriptParser::get_builtin_type(const StringName &p_type) {
- if (builtin_types.empty()) {
+ if (builtin_types.is_empty()) {
builtin_types["bool"] = Variant::BOOL;
builtin_types["int"] = Variant::INT;
builtin_types["float"] = Variant::FLOAT;
@@ -94,17 +94,43 @@ Variant::Type GDScriptParser::get_builtin_type(const StringName &p_type) {
return Variant::VARIANT_MAX;
}
-void GDScriptParser::cleanup() {
- builtin_types.clear();
-}
-
-GDScriptFunctions::Function GDScriptParser::get_builtin_function(const StringName &p_name) {
- for (int i = 0; i < GDScriptFunctions::FUNC_MAX; i++) {
- if (p_name == GDScriptFunctions::get_func_name(GDScriptFunctions::Function(i))) {
- return GDScriptFunctions::Function(i);
+// TODO: Move this to a central location (maybe core?).
+static HashMap<StringName, StringName> underscore_map;
+static const char *underscore_classes[] = {
+ "ClassDB",
+ "Directory",
+ "Engine",
+ "File",
+ "Geometry",
+ "GodotSharp",
+ "JSON",
+ "Marshalls",
+ "Mutex",
+ "OS",
+ "ResourceLoader",
+ "ResourceSaver",
+ "Semaphore",
+ "Thread",
+ "VisualScriptEditor",
+ nullptr,
+};
+StringName GDScriptParser::get_real_class_name(const StringName &p_source) {
+ if (underscore_map.is_empty()) {
+ const char **class_name = underscore_classes;
+ while (*class_name != nullptr) {
+ underscore_map[*class_name] = String("_") + *class_name;
+ class_name++;
}
}
- return GDScriptFunctions::FUNC_MAX;
+ if (underscore_map.has(p_source)) {
+ return underscore_map[p_source];
+ }
+ return p_source;
+}
+
+void GDScriptParser::cleanup() {
+ builtin_types.clear();
+ underscore_map.clear();
}
void GDScriptParser::get_annotation_list(List<MethodInfo> *r_annotations) const {
@@ -118,12 +144,11 @@ void GDScriptParser::get_annotation_list(List<MethodInfo> *r_annotations) const
GDScriptParser::GDScriptParser() {
// Register valid annotations.
// TODO: Should this be static?
- // TODO: Validate applicable types (e.g. a VARIABLE annotation that only applies to string variables).
register_annotation(MethodInfo("@tool"), AnnotationInfo::SCRIPT, &GDScriptParser::tool_annotation);
register_annotation(MethodInfo("@icon", { Variant::STRING, "icon_path" }), AnnotationInfo::SCRIPT, &GDScriptParser::icon_annotation);
register_annotation(MethodInfo("@onready"), AnnotationInfo::VARIABLE, &GDScriptParser::onready_annotation);
// Export annotations.
- register_annotation(MethodInfo("@export"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_TYPE_STRING, Variant::NIL>);
+ register_annotation(MethodInfo("@export"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_NONE, Variant::NIL>);
register_annotation(MethodInfo("@export_enum", { Variant::STRING, "names" }), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_ENUM, Variant::INT>, 0, true);
register_annotation(MethodInfo("@export_file", { Variant::STRING, "filter" }), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_FILE, Variant::STRING>, 1, true);
register_annotation(MethodInfo("@export_dir"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_DIR, Variant::STRING>);
@@ -139,8 +164,10 @@ GDScriptParser::GDScriptParser() {
register_annotation(MethodInfo("@export_flags", { Variant::STRING, "names" }), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_FLAGS, Variant::INT>, 0, true);
register_annotation(MethodInfo("@export_flags_2d_render"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_LAYERS_2D_RENDER, Variant::INT>);
register_annotation(MethodInfo("@export_flags_2d_physics"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_LAYERS_2D_PHYSICS, Variant::INT>);
+ register_annotation(MethodInfo("@export_flags_2d_navigation"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_LAYERS_2D_NAVIGATION, Variant::INT>);
register_annotation(MethodInfo("@export_flags_3d_render"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_LAYERS_3D_RENDER, Variant::INT>);
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>);
@@ -184,23 +211,25 @@ void GDScriptParser::push_error(const String &p_message, const Node *p_origin) {
#ifdef DEBUG_ENABLED
void GDScriptParser::push_warning(const Node *p_source, GDScriptWarning::Code p_code, const String &p_symbol1, const String &p_symbol2, const String &p_symbol3, const String &p_symbol4) {
+ ERR_FAIL_COND(p_source == nullptr);
Vector<String> symbols;
- if (!p_symbol1.empty()) {
+ if (!p_symbol1.is_empty()) {
symbols.push_back(p_symbol1);
}
- if (!p_symbol2.empty()) {
+ if (!p_symbol2.is_empty()) {
symbols.push_back(p_symbol2);
}
- if (!p_symbol3.empty()) {
+ if (!p_symbol3.is_empty()) {
symbols.push_back(p_symbol3);
}
- if (!p_symbol4.empty()) {
+ if (!p_symbol4.is_empty()) {
symbols.push_back(p_symbol4);
}
push_warning(p_source, p_code, symbols);
}
void GDScriptParser::push_warning(const Node *p_source, GDScriptWarning::Code p_code, const Vector<String> &p_symbols) {
+ ERR_FAIL_COND(p_source == nullptr);
if (is_ignoring_warnings) {
return;
}
@@ -291,7 +320,7 @@ void GDScriptParser::pop_completion_call() {
if (!for_completion) {
return;
}
- ERR_FAIL_COND_MSG(completion_call_stack.empty(), "Trying to pop empty completion call stack");
+ ERR_FAIL_COND_MSG(completion_call_stack.is_empty(), "Trying to pop empty completion call stack");
completion_call_stack.pop_back();
}
@@ -299,7 +328,7 @@ void GDScriptParser::set_last_completion_call_arg(int p_argument) {
if (!for_completion || passed_cursor) {
return;
}
- ERR_FAIL_COND_MSG(completion_call_stack.empty(), "Trying to set argument on empty completion call stack");
+ ERR_FAIL_COND_MSG(completion_call_stack.is_empty(), "Trying to set argument on empty completion call stack");
completion_call_stack.back()->get().argument = p_argument;
}
@@ -365,7 +394,7 @@ Error GDScriptParser::parse(const String &p_source_code, const String &p_script_
}
#endif
- if (errors.empty()) {
+ if (errors.is_empty()) {
return OK;
} else {
return ERR_PARSE_ERROR;
@@ -376,7 +405,7 @@ GDScriptTokenizer::Token GDScriptParser::advance() {
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.");
}
- if (for_completion && !completion_call_stack.empty()) {
+ if (for_completion && !completion_call_stack.is_empty()) {
if (completion_call.call == nullptr && tokenizer.is_past_cursor()) {
completion_call = completion_call_stack.back()->get();
passed_cursor = true;
@@ -507,7 +536,7 @@ void GDScriptParser::parse_program() {
// Order here doesn't matter, but there should be only one of each at most.
switch (current.type) {
case GDScriptTokenizer::Token::CLASS_NAME:
- if (!annotation_stack.empty()) {
+ if (!annotation_stack.is_empty()) {
push_error(R"("class_name" should be used before annotations.)");
}
advance();
@@ -518,7 +547,7 @@ void GDScriptParser::parse_program() {
}
break;
case GDScriptTokenizer::Token::EXTENDS:
- if (!annotation_stack.empty()) {
+ if (!annotation_stack.is_empty()) {
push_error(R"("extends" should be used before annotations.)");
}
advance();
@@ -556,6 +585,17 @@ void GDScriptParser::parse_program() {
parse_class_body();
+#ifdef TOOLS_ENABLED
+ for (Map<int, GDScriptTokenizer::CommentData>::Element *E = tokenizer.get_comments().front(); E; E = E->next()) {
+ if (E->get().new_line && E->get().comment.begins_with("##")) {
+ class_doc_line = MIN(class_doc_line, E->key());
+ }
+ }
+ if (has_comment(class_doc_line)) {
+ get_class_doc_comment(class_doc_line, head->doc_brief_description, head->doc_description, head->doc_tutorials, false);
+ }
+#endif // TOOLS_ENABLED
+
if (!check(GDScriptTokenizer::Token::TK_EOF)) {
push_error("Expected end of file.");
}
@@ -666,11 +706,14 @@ void GDScriptParser::parse_class_member(T *(GDScriptParser::*p_parse_function)()
if (member == nullptr) {
return;
}
+#ifdef TOOLS_ENABLED
+ int doc_comment_line = member->start_line - 1;
+#endif // TOOLS_ENABLED
+
// Consume annotations.
- while (!annotation_stack.empty()) {
+ while (!annotation_stack.is_empty()) {
AnnotationNode *last_annotation = annotation_stack.back()->get();
if (last_annotation->applies_to(p_target)) {
- last_annotation->apply(this, member);
member->annotations.push_front(last_annotation);
annotation_stack.pop_back();
} else {
@@ -678,7 +721,25 @@ void GDScriptParser::parse_class_member(T *(GDScriptParser::*p_parse_function)()
clear_unused_annotations();
return;
}
+#ifdef TOOLS_ENABLED
+ if (last_annotation->start_line == doc_comment_line) {
+ doc_comment_line--;
+ }
+#endif // TOOLS_ENABLED
+ }
+
+#ifdef TOOLS_ENABLED
+ // Consume doc comments.
+ class_doc_line = MIN(class_doc_line, doc_comment_line - 1);
+ if (has_comment(doc_comment_line)) {
+ if constexpr (std::is_same_v<T, ClassNode>) {
+ get_class_doc_comment(doc_comment_line, member->doc_brief_description, member->doc_description, member->doc_tutorials, true);
+ } else {
+ member->doc_description = get_doc_comment(doc_comment_line);
+ }
}
+#endif // TOOLS_ENABLED
+
if (member->identifier != nullptr) {
// Enums may be unnamed.
// TODO: Consider names in outer scope too, for constants and classes (and static functions?)
@@ -783,6 +844,9 @@ GDScriptParser::VariableNode *GDScriptParser::parse_variable(bool p_allow_proper
if (match(GDScriptTokenizer::Token::EQUAL)) {
// Initializer.
variable->initializer = parse_expression(false);
+ if (variable->initializer == nullptr) {
+ push_error(R"(Expected expression for variable initial value after "=".)");
+ }
variable->assignments++;
}
@@ -950,6 +1014,8 @@ GDScriptParser::ConstantNode *GDScriptParser::parse_constant() {
push_error(R"(Expected initializer expression for constant.)");
return nullptr;
}
+ } else {
+ return nullptr;
}
end_statement("constant declaration");
@@ -1048,6 +1114,7 @@ GDScriptParser::EnumNode *GDScriptParser::parse_enum() {
item.parent_enum = enum_node;
item.line = previous.start_line;
item.leftmost_column = previous.leftmost_column;
+ item.rightmost_column = previous.rightmost_column;
if (elements.has(item.identifier->name)) {
push_error(vformat(R"(Name "%s" was already in this enum (at line %d).)", item.identifier->name, elements[item.identifier->name]), item.identifier);
@@ -1086,6 +1153,31 @@ GDScriptParser::EnumNode *GDScriptParser::parse_enum() {
pop_multiline();
consume(GDScriptTokenizer::Token::BRACE_CLOSE, R"(Expected closing "}" for enum.)");
+#ifdef TOOLS_ENABLED
+ // Enum values documentaion.
+ 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.
+ if (enum_node->values[i].line != previous.start_line && has_comment(enum_node->values[i].line)) {
+ if (named) {
+ enum_node->values.write[i].doc_description = get_doc_comment(enum_node->values[i].line, true);
+ } else {
+ current_class->set_enum_value_doc(enum_node->values[i].identifier->name, get_doc_comment(enum_node->values[i].line, true));
+ }
+ }
+ } else {
+ // If two values are same line.
+ if (enum_node->values[i].line != enum_node->values[i + 1].line && has_comment(enum_node->values[i].line)) {
+ if (named) {
+ enum_node->values.write[i].doc_description = get_doc_comment(enum_node->values[i].line, true);
+ } else {
+ current_class->set_enum_value_doc(enum_node->values[i].identifier->name, get_doc_comment(enum_node->values[i].line, true));
+ }
+ }
+ }
+ }
+#endif // TOOLS_ENABLED
+
end_statement("enum");
return enum_node;
@@ -1419,9 +1511,13 @@ GDScriptParser::Node *GDScriptParser::parse_statement() {
}
#ifdef DEBUG_ENABLED
- if (unreachable) {
+ if (unreachable && result != nullptr) {
current_suite->has_unreachable_code = true;
- push_warning(result, GDScriptWarning::UNREACHABLE_CODE, current_function->identifier->name);
+ if (current_function) {
+ push_warning(result, GDScriptWarning::UNREACHABLE_CODE, current_function->identifier->name);
+ } else {
+ // TODO: Properties setters and getters with unreachable code are not being warned
+ }
}
#endif
@@ -1445,12 +1541,9 @@ GDScriptParser::AssertNode *GDScriptParser::parse_assert() {
if (match(GDScriptTokenizer::Token::COMMA)) {
// Error message.
- if (consume(GDScriptTokenizer::Token::LITERAL, R"(Expected error message for assert after ",".)")) {
- assert->message = parse_literal();
- if (assert->message->value.get_type() != Variant::STRING) {
- push_error(R"(Expected string for assert error message.)");
- }
- } else {
+ assert->message = parse_expression(false);
+ if (assert->message == nullptr) {
+ push_error(R"(Expected error message for assert after ",".)");
return nullptr;
}
}
@@ -1642,7 +1735,7 @@ GDScriptParser::MatchBranchNode *GDScriptParser::parse_match_branch() {
branch->patterns.push_back(pattern);
} while (match(GDScriptTokenizer::Token::COMMA));
- if (branch->patterns.empty()) {
+ if (branch->patterns.is_empty()) {
push_error(R"(No pattern found for "match" branch.)");
}
@@ -2025,6 +2118,17 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_unary_operator(ExpressionN
return operation;
}
+GDScriptParser::ExpressionNode *GDScriptParser::parse_binary_not_in_operator(ExpressionNode *p_previous_operand, bool p_can_assign) {
+ // check that NOT is followed by IN by consuming it before calling parse_binary_operator which will only receive a plain IN
+ consume(GDScriptTokenizer::Token::IN, R"(Expected "in" after "not" in content-test operator.)");
+ ExpressionNode *in_operation = parse_binary_operator(p_previous_operand, p_can_assign);
+ UnaryOpNode *operation = alloc_node<UnaryOpNode>();
+ operation->operation = UnaryOpNode::OP_LOGIC_NOT;
+ operation->variant_op = Variant::OP_NOT;
+ operation->operand = in_operation;
+ return operation;
+}
+
GDScriptParser::ExpressionNode *GDScriptParser::parse_binary_operator(ExpressionNode *p_previous_operand, bool p_can_assign) {
GDScriptTokenizer::Token op = previous;
BinaryOpNode *operation = alloc_node<BinaryOpNode>();
@@ -2488,7 +2592,7 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_call(ExpressionNode *p_pre
// Arguments.
CompletionType ct = COMPLETION_CALL_ARGUMENTS;
- if (get_builtin_function(call->function_name) == GDScriptFunctions::RESOURCE_LOAD) {
+ if (call->function_name == "load") {
ct = COMPLETION_RESOURCE_PATH;
}
push_completion_call(call);
@@ -2606,6 +2710,19 @@ GDScriptParser::TypeNode *GDScriptParser::parse_type(bool p_allow_void) {
type->type_chain.push_back(type_element);
+ if (match(GDScriptTokenizer::Token::BRACKET_OPEN)) {
+ // Typed collection (like Array[int]).
+ type->container_type = parse_type(false); // Don't allow void for array element type.
+ if (type->container_type == nullptr) {
+ push_error(R"(Expected type for collection after "[".)");
+ type = nullptr;
+ } else if (type->container_type->container_type != nullptr) {
+ push_error("Nested typed collections are not supported.");
+ }
+ consume(GDScriptTokenizer::Token::BRACKET_CLOSE, R"(Expected closing "]" after collection type.)");
+ return type;
+ }
+
int chain_index = 1;
while (match(GDScriptTokenizer::Token::PERIOD)) {
make_completion_context(COMPLETION_TYPE_ATTRIBUTE, type, chain_index++);
@@ -2618,6 +2735,218 @@ GDScriptParser::TypeNode *GDScriptParser::parse_type(bool p_allow_void) {
return type;
}
+#ifdef TOOLS_ENABLED
+static bool _in_codeblock(String p_line, bool p_already_in, int *r_block_begins = nullptr) {
+ int start_block = p_line.rfind("[codeblock]");
+ int end_block = p_line.rfind("[/codeblock]");
+
+ if (start_block != -1 && r_block_begins) {
+ *r_block_begins = start_block;
+ }
+
+ if (p_already_in) {
+ if (end_block == -1) {
+ return true;
+ } else if (start_block == -1) {
+ return false;
+ } else {
+ return start_block > end_block;
+ }
+ } else {
+ if (start_block == -1) {
+ return false;
+ } else if (end_block == -1) {
+ return true;
+ } else {
+ return start_block > end_block;
+ }
+ }
+}
+
+bool GDScriptParser::has_comment(int p_line) {
+ return tokenizer.get_comments().has(p_line);
+}
+
+String GDScriptParser::get_doc_comment(int p_line, bool p_single_line) {
+ const Map<int, GDScriptTokenizer::CommentData> &comments = tokenizer.get_comments();
+ ERR_FAIL_COND_V(!comments.has(p_line), String());
+
+ if (p_single_line) {
+ if (comments[p_line].comment.begins_with("##")) {
+ return comments[p_line].comment.trim_prefix("##").strip_edges();
+ }
+ return "";
+ }
+
+ String doc;
+
+ int line = p_line;
+ bool in_codeblock = false;
+
+ while (comments.has(line - 1)) {
+ if (!comments[line - 1].new_line || !comments[line - 1].comment.begins_with("##")) {
+ break;
+ }
+ line--;
+ }
+
+ int codeblock_begins = 0;
+ while (comments.has(line)) {
+ if (!comments[line].new_line || !comments[line].comment.begins_with("##")) {
+ break;
+ }
+ String doc_line = comments[line].comment.trim_prefix("##");
+
+ in_codeblock = _in_codeblock(doc_line, in_codeblock, &codeblock_begins);
+
+ if (in_codeblock) {
+ int i = 0;
+ for (; i < codeblock_begins; i++) {
+ if (doc_line[i] != ' ') {
+ break;
+ }
+ }
+ doc_line = doc_line.substr(i);
+ } else {
+ doc_line = doc_line.strip_edges();
+ }
+ String line_join = (in_codeblock) ? "\n" : " ";
+
+ doc = (doc.is_empty()) ? doc_line : doc + line_join + doc_line;
+ line++;
+ }
+
+ return doc;
+}
+
+void GDScriptParser::get_class_doc_comment(int p_line, String &p_brief, String &p_desc, Vector<Pair<String, String>> &p_tutorials, bool p_inner_class) {
+ const Map<int, GDScriptTokenizer::CommentData> &comments = tokenizer.get_comments();
+ if (!comments.has(p_line)) {
+ return;
+ }
+ ERR_FAIL_COND(p_brief != "" || p_desc != "" || p_tutorials.size() != 0);
+
+ int line = p_line;
+ bool in_codeblock = false;
+ enum Mode {
+ BRIEF,
+ DESC,
+ TUTORIALS,
+ DONE,
+ };
+ Mode mode = BRIEF;
+
+ if (p_inner_class) {
+ while (comments.has(line - 1)) {
+ if (!comments[line - 1].new_line || !comments[line - 1].comment.begins_with("##")) {
+ break;
+ }
+ line--;
+ }
+ }
+
+ int codeblock_begins = 0;
+ while (comments.has(line)) {
+ if (!comments[line].new_line || !comments[line].comment.begins_with("##")) {
+ break;
+ }
+
+ String title, link; // For tutorials.
+ String doc_line = comments[line++].comment.trim_prefix("##");
+ String striped_line = doc_line.strip_edges();
+
+ // Set the read mode.
+ if (striped_line.begins_with("@desc:") && p_desc == "") {
+ mode = DESC;
+ striped_line = striped_line.trim_prefix("@desc:");
+ in_codeblock = _in_codeblock(doc_line, in_codeblock);
+
+ } else if (striped_line.begins_with("@tutorial")) {
+ int begin_scan = String("@tutorial").length();
+ if (begin_scan >= striped_line.length()) {
+ continue; // invalid syntax.
+ }
+
+ if (striped_line[begin_scan] == ':') { // No title.
+ // Syntax: ## @tutorial: https://godotengine.org/ // The title argument is optional.
+ title = "";
+ link = striped_line.trim_prefix("@tutorial:").strip_edges();
+
+ } else {
+ /* Syntax:
+ @tutorial ( The Title Here ) : http://the.url/
+ ^ open ^ close ^ colon ^ url
+ */
+ int open_bracket_pos = begin_scan, close_bracket_pos = 0;
+ while (open_bracket_pos < striped_line.length() && (striped_line[open_bracket_pos] == ' ' || striped_line[open_bracket_pos] == '\t')) {
+ open_bracket_pos++;
+ }
+ if (open_bracket_pos == striped_line.length() || striped_line[open_bracket_pos++] != '(') {
+ continue; // invalid syntax.
+ }
+ close_bracket_pos = open_bracket_pos;
+ while (close_bracket_pos < striped_line.length() && striped_line[close_bracket_pos] != ')') {
+ close_bracket_pos++;
+ }
+ if (close_bracket_pos == striped_line.length()) {
+ continue; // invalid syntax.
+ }
+
+ int colon_pos = close_bracket_pos + 1;
+ while (colon_pos < striped_line.length() && (striped_line[colon_pos] == ' ' || striped_line[colon_pos] == '\t')) {
+ colon_pos++;
+ }
+ if (colon_pos == striped_line.length() || striped_line[colon_pos++] != ':') {
+ continue; // invalid syntax.
+ }
+
+ title = striped_line.substr(open_bracket_pos, close_bracket_pos - open_bracket_pos).strip_edges();
+ link = striped_line.substr(colon_pos).strip_edges();
+ }
+
+ mode = TUTORIALS;
+ in_codeblock = false;
+ } else if (striped_line.is_empty()) {
+ continue;
+ } else {
+ // Tutorial docs are single line, we need a @tag after it.
+ if (mode == TUTORIALS) {
+ mode = DONE;
+ }
+
+ in_codeblock = _in_codeblock(doc_line, in_codeblock, &codeblock_begins);
+ }
+
+ if (in_codeblock) {
+ int i = 0;
+ for (; i < codeblock_begins; i++) {
+ if (doc_line[i] != ' ') {
+ break;
+ }
+ }
+ doc_line = doc_line.substr(i);
+ } else {
+ doc_line = striped_line;
+ }
+ String line_join = (in_codeblock) ? "\n" : " ";
+
+ switch (mode) {
+ case BRIEF:
+ p_brief = (p_brief.length() == 0) ? doc_line : p_brief + line_join + doc_line;
+ break;
+ case DESC:
+ p_desc = (p_desc.length() == 0) ? doc_line : p_desc + line_join + doc_line;
+ break;
+ case TUTORIALS:
+ p_tutorials.append(Pair<String, String>(title, link));
+ break;
+ case DONE:
+ return;
+ }
+ }
+}
+#endif // TOOLS_ENABLED
+
GDScriptParser::ParseRule *GDScriptParser::get_rule(GDScriptTokenizer::Token::Type p_token_type) {
// Function table for expression parsing.
// clang-format destroys the alignment here, so turn off for the table.
@@ -2639,7 +2968,7 @@ GDScriptParser::ParseRule *GDScriptParser::get_rule(GDScriptTokenizer::Token::Ty
// Logical
{ nullptr, &GDScriptParser::parse_binary_operator, PREC_LOGIC_AND }, // AND,
{ nullptr, &GDScriptParser::parse_binary_operator, PREC_LOGIC_OR }, // OR,
- { &GDScriptParser::parse_unary_operator, nullptr, PREC_NONE }, // NOT,
+ { &GDScriptParser::parse_unary_operator, &GDScriptParser::parse_binary_not_in_operator, PREC_CONTENT_TEST }, // NOT,
{ nullptr, &GDScriptParser::parse_binary_operator, PREC_LOGIC_AND }, // AMPERSAND_AMPERSAND,
{ nullptr, &GDScriptParser::parse_binary_operator, PREC_LOGIC_OR }, // PIPE_PIPE,
{ &GDScriptParser::parse_unary_operator, nullptr, PREC_NONE }, // BANG,
@@ -2651,8 +2980,8 @@ GDScriptParser::ParseRule *GDScriptParser::get_rule(GDScriptTokenizer::Token::Ty
{ nullptr, &GDScriptParser::parse_binary_operator, PREC_BIT_SHIFT }, // LESS_LESS,
{ nullptr, &GDScriptParser::parse_binary_operator, PREC_BIT_SHIFT }, // GREATER_GREATER,
// Math
- { &GDScriptParser::parse_unary_operator, &GDScriptParser::parse_binary_operator, PREC_ADDITION }, // PLUS,
- { &GDScriptParser::parse_unary_operator, &GDScriptParser::parse_binary_operator, PREC_SUBTRACTION }, // MINUS,
+ { &GDScriptParser::parse_unary_operator, &GDScriptParser::parse_binary_operator, PREC_ADDITION_SUBTRACTION }, // PLUS,
+ { &GDScriptParser::parse_unary_operator, &GDScriptParser::parse_binary_operator, PREC_ADDITION_SUBTRACTION }, // MINUS,
{ nullptr, &GDScriptParser::parse_binary_operator, PREC_FACTOR }, // STAR,
{ nullptr, &GDScriptParser::parse_binary_operator, PREC_FACTOR }, // SLASH,
{ nullptr, &GDScriptParser::parse_binary_operator, PREC_FACTOR }, // PERCENT,
@@ -2738,7 +3067,7 @@ GDScriptParser::ParseRule *GDScriptParser::get_rule(GDScriptTokenizer::Token::Ty
// Avoid desync.
static_assert(sizeof(rules) / sizeof(rules[0]) == GDScriptTokenizer::Token::TK_MAX, "Amount of parse rules don't match the amount of token types.");
- // Let's assume this this never invalid, since nothing generates a TK_MAX.
+ // Let's assume this is never invalid, since nothing generates a TK_MAX.
return &rules[p_token_type];
}
@@ -2880,24 +3209,10 @@ bool GDScriptParser::export_annotations(const AnnotationNode *p_annotation, Node
}
variable->exported = true;
- // TODO: Improving setting type, especially for range hints, which can be int or float.
+
variable->export_info.type = t_type;
variable->export_info.hint = t_hint;
- if (p_annotation->name == "@export") {
- if (variable->datatype_specifier == nullptr) {
- if (variable->initializer == nullptr) {
- push_error(R"(Cannot use "@export" annotation with variable without type or initializer, since type can't be inferred.)", p_annotation);
- return false;
- }
- if (variable->initializer->type != Node::LITERAL) {
- push_error(R"(To use "@export" annotation with type-less variable, the default value must be a literal.)", p_annotation);
- return false;
- }
- variable->export_info.type = static_cast<LiteralNode *>(variable->initializer)->value.get_type();
- } // else: Actual type will be set by the analyzer, which can infer the proper type.
- }
-
String hint_string;
for (int i = 0; i < p_annotation->resolved_arguments.size(); i++) {
if (i > 0) {
@@ -2908,6 +3223,86 @@ bool GDScriptParser::export_annotations(const AnnotationNode *p_annotation, Node
variable->export_info.hint_string = hint_string;
+ // This is called after tne analyzer is done finding the type, so this should be set here.
+ DataType export_type = variable->get_datatype();
+
+ if (p_annotation->name == "@export") {
+ if (variable->datatype_specifier == nullptr && variable->initializer == nullptr) {
+ push_error(R"(Cannot use simple "@export" annotation with variable without type or initializer, since type can't be inferred.)", p_annotation);
+ return false;
+ }
+
+ bool is_array = false;
+
+ if (export_type.builtin_type == Variant::ARRAY && export_type.has_container_element_type()) {
+ export_type = export_type.get_container_element_type(); // Use inner type for.
+ is_array = true;
+ }
+
+ if (export_type.is_variant() || export_type.has_no_type()) {
+ push_error(R"(Cannot use simple "@export" annotation because the type of the initialized value can't be inferred.)", p_annotation);
+ return false;
+ }
+
+ switch (export_type.kind) {
+ case GDScriptParser::DataType::BUILTIN:
+ variable->export_info.type = export_type.builtin_type;
+ variable->export_info.hint = PROPERTY_HINT_NONE;
+ variable->export_info.hint_string = Variant::get_type_name(export_type.builtin_type);
+ break;
+ case GDScriptParser::DataType::NATIVE:
+ if (ClassDB::is_parent_class(get_real_class_name(export_type.native_type), "Resource")) {
+ variable->export_info.type = Variant::OBJECT;
+ variable->export_info.hint = PROPERTY_HINT_RESOURCE_TYPE;
+ variable->export_info.hint_string = get_real_class_name(export_type.native_type);
+ } else {
+ push_error(R"(Export type can only be built-in, a resource, or an enum.)", variable);
+ return false;
+ }
+ break;
+ case GDScriptParser::DataType::ENUM: {
+ variable->export_info.type = Variant::INT;
+ variable->export_info.hint = PROPERTY_HINT_ENUM;
+
+ String enum_hint_string;
+ for (const Map<StringName, int>::Element *E = export_type.enum_values.front(); E; E = E->next()) {
+ enum_hint_string += E->key().operator String().camelcase_to_underscore(true).capitalize().xml_escape();
+ enum_hint_string += ":";
+ enum_hint_string += String::num_int64(E->get()).xml_escape();
+
+ if (E->next()) {
+ enum_hint_string += ",";
+ }
+ }
+
+ variable->export_info.hint_string = enum_hint_string;
+ } break;
+ default:
+ // TODO: Allow custom user resources.
+ push_error(R"(Export type can only be built-in, a resource, or an enum.)", variable);
+ break;
+ }
+
+ if (is_array) {
+ String hint_prefix = itos(variable->export_info.type);
+ if (variable->export_info.hint) {
+ hint_prefix += "/" + itos(variable->export_info.hint);
+ }
+ variable->export_info.hint = PROPERTY_HINT_TYPE_STRING;
+ variable->export_info.hint_string = hint_prefix + ":" + variable->export_info.hint_string;
+ variable->export_info.type = Variant::ARRAY;
+ }
+ } else {
+ // Validate variable type with export.
+ if (!export_type.is_variant() && (export_type.kind != DataType::BUILTIN || export_type.builtin_type != t_type)) {
+ // Allow float/int conversion.
+ if ((t_type != Variant::FLOAT || export_type.builtin_type != Variant::INT) && (t_type != Variant::INT || export_type.builtin_type != Variant::FLOAT)) {
+ push_error(vformat(R"("%s" annotation requires a variable of type "%s" but type "%s" was given instead.)", p_annotation->name.operator String(), Variant::get_type_name(t_type), export_type.to_string()), variable);
+ return false;
+ }
+ }
+ }
+
return true;
}
@@ -2993,6 +3388,9 @@ String GDScriptParser::DataType::to_string() const {
if (builtin_type == Variant::NIL) {
return "null";
}
+ if (builtin_type == Variant::ARRAY && has_container_element_type()) {
+ return vformat("Array[%s]", container_element_type->to_string());
+ }
return Variant::get_type_name(builtin_type);
case NATIVE:
if (is_meta_type) {
@@ -3012,11 +3410,11 @@ String GDScriptParser::DataType::to_string() const {
return script_type->get_class_name().operator String();
}
String name = script_type->get_name();
- if (!name.empty()) {
+ if (!name.is_empty()) {
return name;
}
name = script_path;
- if (!name.empty()) {
+ if (!name.is_empty()) {
return name;
}
return native_type.operator String();
@@ -3061,7 +3459,7 @@ void GDScriptParser::TreePrinter::decrease_indent() {
}
void GDScriptParser::TreePrinter::push_line(const String &p_line) {
- if (!p_line.empty()) {
+ if (!p_line.is_empty()) {
push_text(p_line);
}
printed += "\n";
@@ -3270,7 +3668,7 @@ void GDScriptParser::TreePrinter::print_class(ClassNode *p_class) {
if (p_class->extends_used) {
bool first = true;
push_text(" Extends ");
- if (!p_class->extends_path.empty()) {
+ if (!p_class->extends_path.is_empty()) {
push_text(vformat(R"("%s")", p_class->extends_path));
first = false;
}
@@ -3732,7 +4130,7 @@ void GDScriptParser::TreePrinter::print_ternary_op(TernaryOpNode *p_ternary_op)
}
void GDScriptParser::TreePrinter::print_type(TypeNode *p_type) {
- if (p_type->type_chain.empty()) {
+ if (p_type->type_chain.is_empty()) {
push_text("Void");
} else {
for (int i = 0; i < p_type->type_chain.size(); i++) {
@@ -3852,7 +4250,7 @@ void GDScriptParser::TreePrinter::print_tree(const GDScriptParser &p_parser) {
if (p_parser.is_tool()) {
push_line("@tool");
}
- if (!p_parser.get_tree()->icon_path.empty()) {
+ if (!p_parser.get_tree()->icon_path.is_empty()) {
push_text(R"(@icon (")");
push_text(p_parser.get_tree()->icon_path);
push_line("\")");
diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h
index b24acc4778..272d21ffce 100644
--- a/modules/gdscript/gdscript_parser.h
+++ b/modules/gdscript/gdscript_parser.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -43,7 +43,6 @@
#include "core/templates/vector.h"
#include "core/variant/variant.h"
#include "gdscript_cache.h"
-#include "gdscript_functions.h"
#include "gdscript_tokenizer.h"
#ifdef DEBUG_ENABLED
@@ -95,7 +94,12 @@ public:
struct VariableNode;
struct WhileNode;
- struct DataType {
+ class DataType {
+ private:
+ // Private access so we can control memory management.
+ DataType *container_element_type = nullptr;
+
+ public:
enum Kind {
BUILTIN,
NATIVE,
@@ -105,7 +109,6 @@ public:
ENUM_VALUE, // Value from enumeration.
VARIANT, // Can be any type.
UNRESOLVED,
- // TODO: Enum
};
Kind kind = UNRESOLVED;
@@ -129,7 +132,7 @@ public:
ClassNode *class_type = nullptr;
MethodInfo method_info; // For callable/signals.
- HashMap<StringName, int> enum_values; // For enums.
+ Map<StringName, int> enum_values; // For enums.
_FORCE_INLINE_ bool is_set() const { return kind != UNRESOLVED; }
_FORCE_INLINE_ bool has_no_type() const { return type_source == UNDETECTED; }
@@ -137,6 +140,26 @@ public:
_FORCE_INLINE_ bool is_hard_type() const { return type_source > INFERRED; }
String to_string() const;
+ _FORCE_INLINE_ void set_container_element_type(const DataType &p_type) {
+ container_element_type = memnew(DataType(p_type));
+ }
+
+ _FORCE_INLINE_ DataType get_container_element_type() const {
+ ERR_FAIL_COND_V(container_element_type == nullptr, DataType());
+ return *container_element_type;
+ }
+
+ _FORCE_INLINE_ bool has_container_element_type() const {
+ return container_element_type != nullptr;
+ }
+
+ _FORCE_INLINE_ void unset_container_element_type() {
+ if (container_element_type) {
+ memdelete(container_element_type);
+ };
+ container_element_type = nullptr;
+ }
+
bool operator==(const DataType &p_other) const {
if (type_source == UNDETECTED || p_other.type_source == UNDETECTED) {
return true; // Can be consireded equal for parsing purposes.
@@ -174,6 +197,37 @@ public:
bool operator!=(const DataType &p_other) const {
return !(this->operator==(p_other));
}
+
+ DataType &operator=(const DataType &p_other) {
+ kind = p_other.kind;
+ type_source = p_other.type_source;
+ is_constant = p_other.is_constant;
+ is_meta_type = p_other.is_meta_type;
+ is_coroutine = p_other.is_coroutine;
+ builtin_type = p_other.builtin_type;
+ native_type = p_other.native_type;
+ enum_type = p_other.enum_type;
+ script_type = p_other.script_type;
+ script_path = p_other.script_path;
+ class_type = p_other.class_type;
+ method_info = p_other.method_info;
+ enum_values = p_other.enum_values;
+ unset_container_element_type();
+ if (p_other.has_container_element_type()) {
+ set_container_element_type(p_other.get_container_element_type());
+ }
+ return *this;
+ }
+
+ DataType() = default;
+
+ DataType(const DataType &p_other) {
+ *this = p_other;
+ }
+
+ ~DataType() {
+ unset_container_element_type();
+ }
};
struct ParserError {
@@ -287,7 +341,7 @@ public:
struct AssertNode : public Node {
ExpressionNode *condition = nullptr;
- LiteralNode *message = nullptr;
+ ExpressionNode *message = nullptr;
AssertNode() {
type = ASSERT;
@@ -352,7 +406,7 @@ public:
OP_COMP_GREATER_EQUAL,
};
- OpType operation;
+ OpType operation = OpType::OP_ADDITION;
Variant::Operator variant_op = Variant::OP_MAX;
ExpressionNode *left_operand = nullptr;
ExpressionNode *right_operand = nullptr;
@@ -413,9 +467,16 @@ public:
int line = 0;
int leftmost_column = 0;
int rightmost_column = 0;
+#ifdef TOOLS_ENABLED
+ String doc_description;
+#endif // TOOLS_ENABLED
};
+
IdentifierNode *identifier = nullptr;
Vector<Value> values;
+#ifdef TOOLS_ENABLED
+ String doc_description;
+#endif // TOOLS_ENABLED
EnumNode() {
type = ENUM;
@@ -568,6 +629,17 @@ public:
Vector<StringName> extends; // List for indexing: extends A.B.C
DataType base_type;
String fqcn; // Fully-qualified class name. Identifies uniquely any class in the project.
+#ifdef TOOLS_ENABLED
+ String doc_description;
+ String doc_brief_description;
+ Vector<Pair<String, String>> doc_tutorials;
+
+ // EnumValue docs are parsed after itself, so we need a method to add/modify the doc property later.
+ void set_enum_value_doc(const StringName &p_name, const String &p_doc_description) {
+ ERR_FAIL_INDEX(members_indices[p_name], members.size());
+ members.write[members_indices[p_name]].enum_value.doc_description = p_doc_description;
+ }
+#endif // TOOLS_ENABLED
bool resolved_interface = false;
bool resolved_body = false;
@@ -602,6 +674,9 @@ public:
TypeNode *datatype_specifier = nullptr;
bool infer_datatype = false;
int usages = 0;
+#ifdef TOOLS_ENABLED
+ String doc_description;
+#endif // TOOLS_ENABLED
ConstantNode() {
type = CONSTANT;
@@ -653,6 +728,10 @@ public:
bool is_coroutine = false;
MultiplayerAPI::RPCMode rpc_mode = MultiplayerAPI::RPC_MODE_DISABLED;
MethodInfo info;
+#ifdef TOOLS_ENABLED
+ Vector<Variant> default_arg_values;
+ String doc_description;
+#endif // TOOLS_ENABLED
bool resolved_signature = false;
bool resolved_body = false;
@@ -729,7 +808,7 @@ public:
struct MatchBranchNode : public Node {
Vector<PatternNode *> patterns;
- SuiteNode *block;
+ SuiteNode *block = nullptr;
bool has_wildcard = false;
MatchBranchNode() {
@@ -820,6 +899,9 @@ public:
IdentifierNode *identifier = nullptr;
Vector<ParameterNode *> parameters;
HashMap<StringName, int> parameters_indices;
+#ifdef TOOLS_ENABLED
+ String doc_description;
+#endif // TOOLS_ENABLED
SignalNode() {
type = SIGNAL;
@@ -960,6 +1042,7 @@ public:
struct TypeNode : public Node {
Vector<IdentifierNode *> type_chain;
+ TypeNode *container_type = nullptr;
TypeNode() {
type = TYPE;
@@ -974,7 +1057,7 @@ public:
OP_LOGIC_NOT,
};
- OpType operation;
+ OpType operation = OP_POSITIVE;
Variant::Operator variant_op = Variant::OP_MAX;
ExpressionNode *operand = nullptr;
@@ -1012,6 +1095,9 @@ public:
MultiplayerAPI::RPCMode rpc_mode = MultiplayerAPI::RPC_MODE_DISABLED;
int assignments = 0;
int usages = 0;
+#ifdef TOOLS_ENABLED
+ String doc_description;
+#endif // TOOLS_ENABLED
VariableNode() {
type = VARIABLE;
@@ -1142,8 +1228,7 @@ private:
PREC_BIT_XOR,
PREC_BIT_AND,
PREC_BIT_SHIFT,
- PREC_SUBTRACTION,
- PREC_ADDITION,
+ PREC_ADDITION_SUBTRACTION,
PREC_FACTOR,
PREC_SIGN,
PREC_BIT_NOT,
@@ -1256,6 +1341,7 @@ private:
ExpressionNode *parse_builtin_constant(ExpressionNode *p_previous_operand, bool p_can_assign);
ExpressionNode *parse_unary_operator(ExpressionNode *p_previous_operand, bool p_can_assign);
ExpressionNode *parse_binary_operator(ExpressionNode *p_previous_operand, bool p_can_assign);
+ ExpressionNode *parse_binary_not_in_operator(ExpressionNode *p_previous_operand, bool p_can_assign);
ExpressionNode *parse_ternary_operator(ExpressionNode *p_previous_operand, bool p_can_assign);
ExpressionNode *parse_assignment(ExpressionNode *p_previous_operand, bool p_can_assign);
ExpressionNode *parse_array(ExpressionNode *p_previous_operand, bool p_can_assign);
@@ -1270,13 +1356,20 @@ private:
ExpressionNode *parse_subscript(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
+ // Doc comments.
+ int class_doc_line = 0x7FFFFFFF;
+ bool has_comment(int p_line);
+ String get_doc_comment(int p_line, bool p_single_line = false);
+ void get_class_doc_comment(int p_line, String &p_brief, String &p_desc, Vector<Pair<String, String>> &p_tutorials, bool p_inner_class);
+#endif // TOOLS_ENABLED
public:
Error parse(const String &p_source_code, const String &p_script_path, bool p_for_completion);
ClassNode *get_tree() const { return head; }
bool is_tool() const { return _is_tool; }
static Variant::Type get_builtin_type(const StringName &p_type);
- static GDScriptFunctions::Function get_builtin_function(const StringName &p_name);
+ static StringName get_real_class_name(const StringName &p_source);
CompletionContext get_completion_context() const { return completion_context; }
CompletionCall get_completion_call() const { return completion_call; }
diff --git a/modules/gdscript/gdscript_tokenizer.cpp b/modules/gdscript/gdscript_tokenizer.cpp
index b91777ede1..e432dfc891 100644
--- a/modules/gdscript/gdscript_tokenizer.cpp
+++ b/modules/gdscript/gdscript_tokenizer.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -221,7 +221,7 @@ String GDScriptTokenizer::get_token_name(Token::Type p_token_type) {
void GDScriptTokenizer::set_source_code(const String &p_source_code) {
source = p_source_code;
- if (source.empty()) {
+ if (source.is_empty()) {
_source = U"";
} else {
_source = source.ptr();
@@ -287,7 +287,7 @@ void GDScriptTokenizer::push_paren(char32_t p_char) {
}
bool GDScriptTokenizer::pop_paren(char32_t p_expected) {
- if (paren_stack.empty()) {
+ if (paren_stack.is_empty()) {
return false;
}
char32_t actual = paren_stack.back()->get();
@@ -405,7 +405,7 @@ void GDScriptTokenizer::push_error(const Token &p_error) {
}
GDScriptTokenizer::Token GDScriptTokenizer::make_paren_error(char32_t p_paren) {
- if (paren_stack.empty()) {
+ if (paren_stack.is_empty()) {
return make_error(vformat("Closing \"%c\" doesn't have an opening counterpart.", p_paren));
}
Token error = make_error(vformat("Closing \"%c\" doesn't match the opening \"%c\".", p_paren, paren_stack.back()->get()));
@@ -898,6 +898,9 @@ GDScriptTokenizer::Token GDScriptTokenizer::string() {
_advance();
_advance();
break;
+ } else {
+ // Not a multiline string termination, add consumed quote.
+ result += quote_char;
}
} else {
// Ended single-line string.
@@ -1014,9 +1017,17 @@ void GDScriptTokenizer::check_indent() {
}
if (_peek() == '#') {
// Comment. Advance to the next line.
+#ifdef TOOLS_ENABLED
+ String comment;
+ while (_peek() != '\n' && !_is_at_end()) {
+ comment += _advance();
+ }
+ comments[line] = CommentData(comment, true);
+#else
while (_peek() != '\n' && !_is_at_end()) {
_advance();
}
+#endif // TOOLS_ENABLED
if (_is_at_end()) {
// Reached the end with an empty line, so just dedent as much as needed.
pending_indents -= indent_level();
@@ -1125,18 +1136,26 @@ void GDScriptTokenizer::_skip_whitespace() {
newline(!is_bol); // Don't create new line token if line is empty.
check_indent();
break;
- case '#':
+ case '#': {
// Comment.
+#ifdef TOOLS_ENABLED
+ String comment;
+ while (_peek() != '\n' && !_is_at_end()) {
+ comment += _advance();
+ }
+ comments[line] = CommentData(comment, is_bol);
+#else
while (_peek() != '\n' && !_is_at_end()) {
_advance();
}
+#endif // TOOLS_ENABLED
if (_is_at_end()) {
return;
}
_advance(); // Consume '\n'
newline(!is_bol);
check_indent();
- break;
+ } break;
default:
return;
}
@@ -1153,7 +1172,7 @@ GDScriptTokenizer::Token GDScriptTokenizer::scan() {
if (pending_newline) {
pending_newline = false;
if (!multiline_mode) {
- // Don't return newline tokens on multine mode.
+ // Don't return newline tokens on multiline mode.
return last_newline;
}
}
diff --git a/modules/gdscript/gdscript_tokenizer.h b/modules/gdscript/gdscript_tokenizer.h
index d51f1f250f..bea4b14019 100644
--- a/modules/gdscript/gdscript_tokenizer.h
+++ b/modules/gdscript/gdscript_tokenizer.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -32,6 +32,7 @@
#define GDSCRIPT_TOKENIZER_H
#include "core/templates/list.h"
+#include "core/templates/map.h"
#include "core/templates/set.h"
#include "core/templates/vector.h"
#include "core/variant/variant.h"
@@ -177,10 +178,24 @@ public:
}
Token() {
- type = EMPTY;
}
};
+#ifdef TOOLS_ENABLED
+ struct CommentData {
+ String comment;
+ bool new_line = false;
+ CommentData() {}
+ CommentData(const String &p_comment, bool p_new_line) {
+ comment = p_comment;
+ new_line = p_new_line;
+ }
+ };
+ const Map<int, CommentData> &get_comments() const {
+ return comments;
+ }
+#endif // TOOLS_ENABLED
+
private:
String source;
const char32_t *_source = nullptr;
@@ -207,10 +222,14 @@ private:
int position = 0;
int length = 0;
+#ifdef TOOLS_ENABLED
+ Map<int, CommentData> comments;
+#endif // TOOLS_ENABLED
+
_FORCE_INLINE_ bool _is_at_end() { return position >= length; }
_FORCE_INLINE_ char32_t _peek(int p_offset = 0) { return position + p_offset >= 0 && position + p_offset < length ? _current[p_offset] : '\0'; }
int indent_level() const { return indent_stack.size(); }
- bool has_error() const { return !error_stack.empty(); }
+ bool has_error() const { return !error_stack.is_empty(); }
Token pop_error();
char32_t _advance();
void _skip_whitespace();
diff --git a/modules/gdscript/gdscript_utility_functions.cpp b/modules/gdscript/gdscript_utility_functions.cpp
new file mode 100644
index 0000000000..348d221352
--- /dev/null
+++ b/modules/gdscript/gdscript_utility_functions.cpp
@@ -0,0 +1,718 @@
+/*************************************************************************/
+/* gdscript_utility_functions.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_utility_functions.h"
+
+#include "core/io/resource_loader.h"
+#include "core/object/class_db.h"
+#include "core/object/method_bind.h"
+#include "core/object/object.h"
+#include "core/templates/oa_hash_map.h"
+#include "core/templates/vector.h"
+#include "gdscript.h"
+
+#ifdef DEBUG_ENABLED
+
+#define VALIDATE_ARG_COUNT(m_count) \
+ if (p_arg_count < m_count) { \
+ r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; \
+ r_error.argument = m_count; \
+ r_error.expected = m_count; \
+ *r_ret = Variant(); \
+ return; \
+ } \
+ if (p_arg_count > m_count) { \
+ r_error.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS; \
+ r_error.argument = m_count; \
+ r_error.expected = m_count; \
+ *r_ret = Variant(); \
+ return; \
+ }
+
+#define VALIDATE_ARG_INT(m_arg) \
+ if (p_args[m_arg]->get_type() != Variant::INT) { \
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; \
+ r_error.argument = m_arg; \
+ r_error.expected = Variant::INT; \
+ *r_ret = Variant(); \
+ return; \
+ }
+
+#define VALIDATE_ARG_NUM(m_arg) \
+ if (!p_args[m_arg]->is_num()) { \
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; \
+ r_error.argument = m_arg; \
+ r_error.expected = Variant::FLOAT; \
+ *r_ret = Variant(); \
+ return; \
+ }
+
+#else
+
+#define VALIDATE_ARG_COUNT(m_count)
+#define VALIDATE_ARG_INT(m_arg)
+#define VALIDATE_ARG_NUM(m_arg)
+
+#endif
+
+struct GDScriptUtilityFunctionsDefinitions {
+ static inline void convert(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
+ VALIDATE_ARG_COUNT(2);
+ VALIDATE_ARG_INT(1);
+ int type = *p_args[1];
+ if (type < 0 || type >= Variant::VARIANT_MAX) {
+ *r_ret = RTR("Invalid type argument to convert(), use TYPE_* constants.");
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument = 0;
+ r_error.expected = Variant::INT;
+ return;
+
+ } else {
+ Variant::construct(Variant::Type(type), *r_ret, p_args, 1, r_error);
+ }
+ }
+
+ static inline void type_exists(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
+ VALIDATE_ARG_COUNT(1);
+ *r_ret = ClassDB::class_exists(*p_args[0]);
+ }
+
+ static inline void _char(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
+ VALIDATE_ARG_COUNT(1);
+ VALIDATE_ARG_INT(0);
+ char32_t result[2] = { *p_args[0], 0 };
+ *r_ret = String(result);
+ }
+
+ static inline void str(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
+ if (p_arg_count < 1) {
+ r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
+ r_error.argument = 1;
+ *r_ret = Variant();
+ return;
+ }
+
+ String str;
+ for (int i = 0; i < p_arg_count; i++) {
+ String os = p_args[i]->operator String();
+
+ if (i == 0) {
+ str = os;
+ } else {
+ str += os;
+ }
+ }
+ *r_ret = str;
+ }
+
+ static inline void range(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
+ switch (p_arg_count) {
+ case 0: {
+ r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
+ r_error.argument = 1;
+ r_error.expected = 1;
+ *r_ret = Variant();
+ } break;
+ case 1: {
+ VALIDATE_ARG_NUM(0);
+ int count = *p_args[0];
+ Array arr;
+ if (count <= 0) {
+ *r_ret = arr;
+ return;
+ }
+ Error err = arr.resize(count);
+ if (err != OK) {
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
+ *r_ret = Variant();
+ return;
+ }
+
+ for (int i = 0; i < count; i++) {
+ arr[i] = i;
+ }
+
+ *r_ret = arr;
+ } break;
+ case 2: {
+ VALIDATE_ARG_NUM(0);
+ VALIDATE_ARG_NUM(1);
+
+ int from = *p_args[0];
+ int to = *p_args[1];
+
+ Array arr;
+ if (from >= to) {
+ *r_ret = arr;
+ return;
+ }
+ Error err = arr.resize(to - from);
+ if (err != OK) {
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
+ *r_ret = Variant();
+ return;
+ }
+ for (int i = from; i < to; i++) {
+ arr[i - from] = i;
+ }
+ *r_ret = arr;
+ } break;
+ case 3: {
+ VALIDATE_ARG_NUM(0);
+ VALIDATE_ARG_NUM(1);
+ VALIDATE_ARG_NUM(2);
+
+ int from = *p_args[0];
+ int to = *p_args[1];
+ int incr = *p_args[2];
+ if (incr == 0) {
+ *r_ret = RTR("Step argument is zero!");
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
+ return;
+ }
+
+ Array arr;
+ if (from >= to && incr > 0) {
+ *r_ret = arr;
+ return;
+ }
+ if (from <= to && incr < 0) {
+ *r_ret = arr;
+ return;
+ }
+
+ // Calculate how many.
+ int count = 0;
+ if (incr > 0) {
+ count = ((to - from - 1) / incr) + 1;
+ } else {
+ count = ((from - to - 1) / -incr) + 1;
+ }
+
+ Error err = arr.resize(count);
+
+ if (err != OK) {
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
+ *r_ret = Variant();
+ return;
+ }
+
+ if (incr > 0) {
+ int idx = 0;
+ for (int i = from; i < to; i += incr) {
+ arr[idx++] = i;
+ }
+ } else {
+ int idx = 0;
+ for (int i = from; i > to; i += incr) {
+ arr[idx++] = i;
+ }
+ }
+
+ *r_ret = arr;
+ } break;
+ default: {
+ r_error.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS;
+ r_error.argument = 3;
+ r_error.expected = 3;
+ *r_ret = Variant();
+
+ } break;
+ }
+ }
+
+ static inline void load(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
+ VALIDATE_ARG_COUNT(1);
+ if (p_args[0]->get_type() != Variant::STRING) {
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument = 0;
+ r_error.expected = Variant::STRING;
+ *r_ret = Variant();
+ } else {
+ *r_ret = ResourceLoader::load(*p_args[0]);
+ }
+ }
+
+ static inline void inst2dict(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
+ VALIDATE_ARG_COUNT(1);
+
+ if (p_args[0]->get_type() == Variant::NIL) {
+ *r_ret = Variant();
+ } else if (p_args[0]->get_type() != Variant::OBJECT) {
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument = 0;
+ *r_ret = Variant();
+ } else {
+ Object *obj = *p_args[0];
+ if (!obj) {
+ *r_ret = Variant();
+
+ } else if (!obj->get_script_instance() || obj->get_script_instance()->get_language() != GDScriptLanguage::get_singleton()) {
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument = 0;
+ r_error.expected = Variant::DICTIONARY;
+ *r_ret = RTR("Not a script with an instance");
+ return;
+ } else {
+ GDScriptInstance *ins = static_cast<GDScriptInstance *>(obj->get_script_instance());
+ Ref<GDScript> base = ins->get_script();
+ if (base.is_null()) {
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument = 0;
+ r_error.expected = Variant::DICTIONARY;
+ *r_ret = RTR("Not based on a script");
+ return;
+ }
+
+ GDScript *p = base.ptr();
+ Vector<StringName> sname;
+
+ while (p->_owner) {
+ sname.push_back(p->name);
+ p = p->_owner;
+ }
+ sname.invert();
+
+ if (!p->path.is_resource_file()) {
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument = 0;
+ r_error.expected = Variant::DICTIONARY;
+ *r_ret = Variant();
+
+ *r_ret = RTR("Not based on a resource file");
+
+ return;
+ }
+
+ NodePath cp(sname, Vector<StringName>(), false);
+
+ Dictionary d;
+ d["@subpath"] = cp;
+ d["@path"] = p->get_path();
+
+ for (Map<StringName, GDScript::MemberInfo>::Element *E = base->member_indices.front(); E; E = E->next()) {
+ if (!d.has(E->key())) {
+ d[E->key()] = ins->members[E->get().index];
+ }
+ }
+ *r_ret = d;
+ }
+ }
+ }
+
+ static inline void dict2inst(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
+ VALIDATE_ARG_COUNT(1);
+
+ if (p_args[0]->get_type() != Variant::DICTIONARY) {
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument = 0;
+ r_error.expected = Variant::DICTIONARY;
+ *r_ret = Variant();
+
+ return;
+ }
+
+ Dictionary d = *p_args[0];
+
+ if (!d.has("@path")) {
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument = 0;
+ r_error.expected = Variant::OBJECT;
+ *r_ret = RTR("Invalid instance dictionary format (missing @path)");
+
+ return;
+ }
+
+ Ref<Script> scr = ResourceLoader::load(d["@path"]);
+ if (!scr.is_valid()) {
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument = 0;
+ r_error.expected = Variant::OBJECT;
+ *r_ret = RTR("Invalid instance dictionary format (can't load script at @path)");
+ return;
+ }
+
+ Ref<GDScript> gdscr = scr;
+
+ if (!gdscr.is_valid()) {
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument = 0;
+ r_error.expected = Variant::OBJECT;
+ *r_ret = Variant();
+ *r_ret = RTR("Invalid instance dictionary format (invalid script at @path)");
+ return;
+ }
+
+ NodePath sub;
+ if (d.has("@subpath")) {
+ sub = d["@subpath"];
+ }
+
+ for (int i = 0; i < sub.get_name_count(); i++) {
+ gdscr = gdscr->subclasses[sub.get_name(i)];
+ if (!gdscr.is_valid()) {
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument = 0;
+ r_error.expected = Variant::OBJECT;
+ *r_ret = Variant();
+ *r_ret = RTR("Invalid instance dictionary (invalid subclasses)");
+ return;
+ }
+ }
+ *r_ret = gdscr->_new(nullptr, -1 /*skip initializer*/, r_error);
+
+ if (r_error.error != Callable::CallError::CALL_OK) {
+ *r_ret = Variant();
+ return;
+ }
+
+ GDScriptInstance *ins = static_cast<GDScriptInstance *>(static_cast<Object *>(*r_ret)->get_script_instance());
+ Ref<GDScript> gd_ref = ins->get_script();
+
+ for (Map<StringName, GDScript::MemberInfo>::Element *E = gd_ref->member_indices.front(); E; E = E->next()) {
+ if (d.has(E->key())) {
+ ins->members.write[E->get().index] = d[E->key()];
+ }
+ }
+ }
+
+ static inline void Color8(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
+ if (p_arg_count < 3) {
+ r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
+ r_error.argument = 3;
+ *r_ret = Variant();
+ return;
+ }
+ if (p_arg_count > 4) {
+ r_error.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS;
+ r_error.argument = 4;
+ *r_ret = Variant();
+ return;
+ }
+
+ VALIDATE_ARG_INT(0);
+ VALIDATE_ARG_INT(1);
+ VALIDATE_ARG_INT(2);
+
+ Color color((int64_t)*p_args[0] / 255.0f, (int64_t)*p_args[1] / 255.0f, (int64_t)*p_args[2] / 255.0f);
+
+ if (p_arg_count == 4) {
+ VALIDATE_ARG_INT(3);
+ color.a = (int64_t)*p_args[3] / 255.0f;
+ }
+
+ *r_ret = color;
+ }
+
+ static inline void print_debug(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
+ String str;
+ for (int i = 0; i < p_arg_count; i++) {
+ str += p_args[i]->operator String();
+ }
+
+ ScriptLanguage *script = GDScriptLanguage::get_singleton();
+ if (script->debug_get_stack_level_count() > 0) {
+ str += "\n At: " + script->debug_get_stack_level_source(0) + ":" + itos(script->debug_get_stack_level_line(0)) + ":" + script->debug_get_stack_level_function(0) + "()";
+ }
+
+ print_line(str);
+ *r_ret = Variant();
+ }
+
+ static inline void print_stack(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
+ VALIDATE_ARG_COUNT(0);
+
+ ScriptLanguage *script = GDScriptLanguage::get_singleton();
+ for (int i = 0; i < script->debug_get_stack_level_count(); i++) {
+ print_line("Frame " + itos(i) + " - " + script->debug_get_stack_level_source(i) + ":" + itos(script->debug_get_stack_level_line(i)) + " in function '" + script->debug_get_stack_level_function(i) + "'");
+ };
+ }
+
+ static inline void get_stack(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
+ VALIDATE_ARG_COUNT(0);
+
+ ScriptLanguage *script = GDScriptLanguage::get_singleton();
+ Array ret;
+ for (int i = 0; i < script->debug_get_stack_level_count(); i++) {
+ Dictionary frame;
+ frame["source"] = script->debug_get_stack_level_source(i);
+ frame["function"] = script->debug_get_stack_level_function(i);
+ frame["line"] = script->debug_get_stack_level_line(i);
+ ret.push_back(frame);
+ };
+ *r_ret = ret;
+ }
+
+ static inline void len(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
+ VALIDATE_ARG_COUNT(1);
+ switch (p_args[0]->get_type()) {
+ case Variant::STRING: {
+ String d = *p_args[0];
+ *r_ret = d.length();
+ } break;
+ case Variant::DICTIONARY: {
+ Dictionary d = *p_args[0];
+ *r_ret = d.size();
+ } break;
+ case Variant::ARRAY: {
+ Array d = *p_args[0];
+ *r_ret = d.size();
+ } break;
+ case Variant::PACKED_BYTE_ARRAY: {
+ Vector<uint8_t> d = *p_args[0];
+ *r_ret = d.size();
+ } break;
+ case Variant::PACKED_INT32_ARRAY: {
+ Vector<int32_t> d = *p_args[0];
+ *r_ret = d.size();
+ } break;
+ case Variant::PACKED_INT64_ARRAY: {
+ Vector<int64_t> d = *p_args[0];
+ *r_ret = d.size();
+ } break;
+ case Variant::PACKED_FLOAT32_ARRAY: {
+ Vector<float> d = *p_args[0];
+ *r_ret = d.size();
+ } break;
+ case Variant::PACKED_FLOAT64_ARRAY: {
+ Vector<double> d = *p_args[0];
+ *r_ret = d.size();
+ } break;
+ case Variant::PACKED_STRING_ARRAY: {
+ Vector<String> d = *p_args[0];
+ *r_ret = d.size();
+ } break;
+ case Variant::PACKED_VECTOR2_ARRAY: {
+ Vector<Vector2> d = *p_args[0];
+ *r_ret = d.size();
+ } break;
+ case Variant::PACKED_VECTOR3_ARRAY: {
+ Vector<Vector3> d = *p_args[0];
+ *r_ret = d.size();
+ } break;
+ case Variant::PACKED_COLOR_ARRAY: {
+ Vector<Color> d = *p_args[0];
+ *r_ret = d.size();
+ } break;
+ default: {
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument = 0;
+ r_error.expected = Variant::NIL;
+ *r_ret = vformat(RTR("Value of type '%s' can't provide a length."), Variant::get_type_name(p_args[0]->get_type()));
+ }
+ }
+ }
+};
+
+struct GDScriptUtilityFunctionInfo {
+ GDScriptUtilityFunctions::FunctionPtr function;
+ MethodInfo info;
+ bool is_constant = false;
+};
+
+static OAHashMap<StringName, GDScriptUtilityFunctionInfo> utility_function_table;
+static List<StringName> utility_function_name_table;
+
+static void _register_function(const String &p_name, const MethodInfo &p_method_info, GDScriptUtilityFunctions::FunctionPtr p_function, bool p_is_const) {
+ StringName sname(p_name);
+
+ ERR_FAIL_COND(utility_function_table.has(sname));
+
+ GDScriptUtilityFunctionInfo function;
+ function.function = p_function;
+ function.info = p_method_info;
+ function.is_constant = p_is_const;
+
+ utility_function_table.insert(sname, function);
+ utility_function_name_table.push_back(sname);
+}
+
+#define REGISTER_FUNC(m_func, m_is_const, m_return_type, ...) \
+ { \
+ String name(#m_func); \
+ if (name.begins_with("_")) { \
+ name = name.substr(1, name.length() - 1); \
+ } \
+ MethodInfo info = MethodInfo(name, __VA_ARGS__); \
+ info.return_val.type = m_return_type; \
+ _register_function(name, info, GDScriptUtilityFunctionsDefinitions::m_func, m_is_const); \
+ }
+
+#define REGISTER_FUNC_NO_ARGS(m_func, m_is_const, m_return_type) \
+ { \
+ String name(#m_func); \
+ if (name.begins_with("_")) { \
+ name = name.substr(1, name.length() - 1); \
+ } \
+ MethodInfo info = MethodInfo(name); \
+ info.return_val.type = m_return_type; \
+ _register_function(name, info, GDScriptUtilityFunctionsDefinitions::m_func, m_is_const); \
+ }
+
+#define REGISTER_VARARG_FUNC(m_func, m_is_const, m_return_type) \
+ { \
+ String name(#m_func); \
+ if (name.begins_with("_")) { \
+ name = name.substr(1, name.length() - 1); \
+ } \
+ MethodInfo info = MethodInfo(name); \
+ info.return_val.type = m_return_type; \
+ info.flags |= METHOD_FLAG_VARARG; \
+ _register_function(name, info, GDScriptUtilityFunctionsDefinitions::m_func, m_is_const); \
+ }
+
+#define REGISTER_VARIANT_FUNC(m_func, m_is_const, ...) \
+ { \
+ String name(#m_func); \
+ if (name.begins_with("_")) { \
+ name = name.substr(1, name.length() - 1); \
+ } \
+ MethodInfo info = MethodInfo(name, __VA_ARGS__); \
+ info.return_val.type = Variant::NIL; \
+ info.return_val.usage |= PROPERTY_USAGE_NIL_IS_VARIANT; \
+ _register_function(name, info, GDScriptUtilityFunctionsDefinitions::m_func, m_is_const); \
+ }
+
+#define REGISTER_CLASS_FUNC(m_func, m_is_const, m_return_type, ...) \
+ { \
+ String name(#m_func); \
+ if (name.begins_with("_")) { \
+ name = name.substr(1, name.length() - 1); \
+ } \
+ MethodInfo info = MethodInfo(name, __VA_ARGS__); \
+ info.return_val.type = Variant::OBJECT; \
+ info.return_val.hint = PROPERTY_HINT_RESOURCE_TYPE; \
+ info.return_val.class_name = m_return_type; \
+ _register_function(name, info, GDScriptUtilityFunctionsDefinitions::m_func, m_is_const); \
+ }
+
+#define REGISTER_FUNC_DEF(m_func, m_is_const, m_default, m_return_type, ...) \
+ { \
+ String name(#m_func); \
+ if (name.begins_with("_")) { \
+ name = name.substr(1, name.length() - 1); \
+ } \
+ MethodInfo info = MethodInfo(name, __VA_ARGS__); \
+ info.return_val.type = m_return_type; \
+ info.default_arguments.push_back(m_default); \
+ _register_function(name, info, GDScriptUtilityFunctionsDefinitions::m_func, m_is_const); \
+ }
+
+#define ARG(m_name, m_type) \
+ PropertyInfo(m_type, m_name)
+
+#define VARARG(m_name) \
+ PropertyInfo(Variant::NIL, m_name, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_NIL_IS_VARIANT)
+
+void GDScriptUtilityFunctions::register_functions() {
+ REGISTER_VARIANT_FUNC(convert, true, VARARG("what"), ARG("type", Variant::INT));
+ REGISTER_FUNC(type_exists, true, Variant::BOOL, ARG("type", Variant::STRING_NAME));
+ REGISTER_FUNC(_char, true, Variant::STRING, ARG("char", Variant::INT));
+ REGISTER_VARARG_FUNC(str, true, Variant::STRING);
+ REGISTER_VARARG_FUNC(range, false, Variant::ARRAY);
+ REGISTER_CLASS_FUNC(load, false, "Resource", ARG("path", Variant::STRING));
+ REGISTER_FUNC(inst2dict, false, Variant::DICTIONARY, ARG("instance", Variant::OBJECT));
+ REGISTER_FUNC(dict2inst, false, Variant::OBJECT, ARG("dictionary", Variant::DICTIONARY));
+ REGISTER_FUNC_DEF(Color8, true, 255, Variant::COLOR, ARG("r8", Variant::INT), ARG("g8", Variant::INT), ARG("b8", Variant::INT), ARG("a8", Variant::INT));
+ REGISTER_VARARG_FUNC(print_debug, false, Variant::NIL);
+ REGISTER_FUNC_NO_ARGS(print_stack, false, Variant::NIL);
+ REGISTER_FUNC_NO_ARGS(get_stack, false, Variant::ARRAY);
+ REGISTER_FUNC(len, true, Variant::INT, VARARG("var"));
+}
+
+void GDScriptUtilityFunctions::unregister_functions() {
+ utility_function_name_table.clear();
+ utility_function_table.clear();
+}
+
+GDScriptUtilityFunctions::FunctionPtr GDScriptUtilityFunctions::get_function(const StringName &p_function) {
+ GDScriptUtilityFunctionInfo *info = utility_function_table.lookup_ptr(p_function);
+ ERR_FAIL_COND_V(!info, nullptr);
+ return info->function;
+}
+
+bool GDScriptUtilityFunctions::has_function_return_value(const StringName &p_function) {
+ GDScriptUtilityFunctionInfo *info = utility_function_table.lookup_ptr(p_function);
+ ERR_FAIL_COND_V(!info, false);
+ return info->info.return_val.type != Variant::NIL || bool(info->info.return_val.usage & PROPERTY_USAGE_NIL_IS_VARIANT);
+}
+
+Variant::Type GDScriptUtilityFunctions::get_function_return_type(const StringName &p_function) {
+ GDScriptUtilityFunctionInfo *info = utility_function_table.lookup_ptr(p_function);
+ ERR_FAIL_COND_V(!info, Variant::NIL);
+ return info->info.return_val.type;
+}
+
+StringName GDScriptUtilityFunctions::get_function_return_class(const StringName &p_function) {
+ GDScriptUtilityFunctionInfo *info = utility_function_table.lookup_ptr(p_function);
+ ERR_FAIL_COND_V(!info, StringName());
+ return info->info.return_val.class_name;
+}
+
+Variant::Type GDScriptUtilityFunctions::get_function_argument_type(const StringName &p_function, int p_arg) {
+ GDScriptUtilityFunctionInfo *info = utility_function_table.lookup_ptr(p_function);
+ ERR_FAIL_COND_V(!info, Variant::NIL);
+ ERR_FAIL_COND_V(p_arg >= info->info.arguments.size(), Variant::NIL);
+ return info->info.arguments[p_arg].type;
+}
+
+int GDScriptUtilityFunctions::get_function_argument_count(const StringName &p_function, int p_arg) {
+ GDScriptUtilityFunctionInfo *info = utility_function_table.lookup_ptr(p_function);
+ ERR_FAIL_COND_V(!info, 0);
+ return info->info.arguments.size();
+}
+
+bool GDScriptUtilityFunctions::is_function_vararg(const StringName &p_function) {
+ GDScriptUtilityFunctionInfo *info = utility_function_table.lookup_ptr(p_function);
+ ERR_FAIL_COND_V(!info, false);
+ return (bool)(info->info.flags & METHOD_FLAG_VARARG);
+}
+
+bool GDScriptUtilityFunctions::is_function_constant(const StringName &p_function) {
+ GDScriptUtilityFunctionInfo *info = utility_function_table.lookup_ptr(p_function);
+ ERR_FAIL_COND_V(!info, false);
+ return info->is_constant;
+}
+
+bool GDScriptUtilityFunctions::function_exists(const StringName &p_function) {
+ return utility_function_table.has(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());
+ }
+}
+
+MethodInfo GDScriptUtilityFunctions::get_function_info(const StringName &p_function) {
+ GDScriptUtilityFunctionInfo *info = utility_function_table.lookup_ptr(p_function);
+ ERR_FAIL_COND_V(!info, MethodInfo());
+ return info->info;
+}
diff --git a/modules/gdscript/gdscript_functions.h b/modules/gdscript/gdscript_utility_functions.h
index 005b49c5da..c6d3718844 100644
--- a/modules/gdscript/gdscript_functions.h
+++ b/modules/gdscript/gdscript_utility_functions.h
@@ -1,12 +1,12 @@
/*************************************************************************/
-/* gdscript_functions.h */
+/* gdscript_utility_functions.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -28,110 +28,31 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef GDSCRIPT_FUNCTIONS_H
-#define GDSCRIPT_FUNCTIONS_H
+#ifndef GDSCRIPT_UTILITY_FUNCTIONS_H
+#define GDSCRIPT_UTILITY_FUNCTIONS_H
+#include "core/string/string_name.h"
#include "core/variant/variant.h"
-class GDScriptFunctions {
+class GDScriptUtilityFunctions {
public:
- enum Function {
- MATH_SIN,
- MATH_COS,
- MATH_TAN,
- MATH_SINH,
- MATH_COSH,
- MATH_TANH,
- MATH_ASIN,
- MATH_ACOS,
- MATH_ATAN,
- MATH_ATAN2,
- MATH_SQRT,
- MATH_FMOD,
- MATH_FPOSMOD,
- MATH_POSMOD,
- MATH_FLOOR,
- MATH_CEIL,
- MATH_ROUND,
- MATH_ABS,
- MATH_SIGN,
- MATH_POW,
- MATH_LOG,
- MATH_EXP,
- MATH_ISNAN,
- MATH_ISINF,
- MATH_ISEQUALAPPROX,
- MATH_ISZEROAPPROX,
- MATH_EASE,
- MATH_STEP_DECIMALS,
- MATH_STEPIFY,
- MATH_LERP,
- MATH_LERP_ANGLE,
- MATH_INVERSE_LERP,
- MATH_RANGE_LERP,
- MATH_SMOOTHSTEP,
- MATH_MOVE_TOWARD,
- MATH_DECTIME,
- MATH_RANDOMIZE,
- MATH_RANDI,
- MATH_RANDF,
- MATH_RANDF_RANGE,
- MATH_RANDI_RANGE,
- MATH_SEED,
- MATH_RANDSEED,
- MATH_DEG2RAD,
- MATH_RAD2DEG,
- MATH_LINEAR2DB,
- MATH_DB2LINEAR,
- MATH_POLAR2CARTESIAN,
- MATH_CARTESIAN2POLAR,
- MATH_WRAP,
- MATH_WRAPF,
- LOGIC_MAX,
- LOGIC_MIN,
- LOGIC_CLAMP,
- LOGIC_NEAREST_PO2,
- OBJ_WEAKREF,
- TYPE_CONVERT,
- TYPE_OF,
- TYPE_EXISTS,
- TEXT_CHAR,
- TEXT_ORD,
- TEXT_STR,
- TEXT_PRINT,
- TEXT_PRINT_TABBED,
- TEXT_PRINT_SPACED,
- TEXT_PRINTERR,
- TEXT_PRINTRAW,
- TEXT_PRINT_DEBUG,
- PUSH_ERROR,
- PUSH_WARNING,
- VAR_TO_STR,
- STR_TO_VAR,
- VAR_TO_BYTES,
- BYTES_TO_VAR,
- GEN_RANGE,
- RESOURCE_LOAD,
- INST2DICT,
- DICT2INST,
- VALIDATE_JSON,
- PARSE_JSON,
- TO_JSON,
- HASH,
- COLOR8,
- COLORN,
- PRINT_STACK,
- GET_STACK,
- INSTANCE_FROM_ID,
- LEN,
- IS_INSTANCE_VALID,
- FUNC_MAX
- };
+ typedef void (*FunctionPtr)(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error);
- static const char *get_func_name(Function p_func);
- static void call(Function p_func, const Variant **p_args, int p_arg_count, Variant &r_ret, Callable::CallError &r_error);
- static bool is_deterministic(Function p_func);
- static MethodInfo get_info(Function p_func);
+ static FunctionPtr get_function(const StringName &p_function);
+ static bool has_function_return_value(const StringName &p_function);
+ static Variant::Type get_function_return_type(const StringName &p_function);
+ static StringName get_function_return_class(const StringName &p_function);
+ static Variant::Type get_function_argument_type(const StringName &p_function, int p_arg);
+ static int get_function_argument_count(const StringName &p_function, int p_arg);
+ static bool is_function_vararg(const StringName &p_function);
+ static bool is_function_constant(const StringName &p_function);
+
+ static bool function_exists(const StringName &p_function);
+ static void get_function_list(List<StringName> *r_functions);
+ static MethodInfo get_function_info(const StringName &p_function);
+
+ static void register_functions();
+ static void unregister_functions();
};
-#endif // GDSCRIPT_FUNCTIONS_H
+#endif // GDSCRIPT_UTILITY_FUNCTIONS_H
diff --git a/modules/gdscript/gdscript_vm.cpp b/modules/gdscript/gdscript_vm.cpp
new file mode 100644
index 0000000000..ffc871bdcb
--- /dev/null
+++ b/modules/gdscript/gdscript_vm.cpp
@@ -0,0 +1,2998 @@
+/*************************************************************************/
+/* gdscript_vm.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_function.h"
+
+#include "core/core_string_names.h"
+#include "core/os/os.h"
+#include "gdscript.h"
+
+Variant *GDScriptFunction::_get_variant(int p_address, GDScriptInstance *p_instance, GDScript *p_script, Variant &self, Variant &static_ref, Variant *p_stack, String &r_error) const {
+ int address = p_address & ADDR_MASK;
+
+ //sequential table (jump table generated by compiler)
+ switch ((p_address & ADDR_TYPE_MASK) >> ADDR_BITS) {
+ case ADDR_TYPE_SELF: {
+#ifdef DEBUG_ENABLED
+ if (unlikely(!p_instance)) {
+ r_error = "Cannot access self without instance.";
+ return nullptr;
+ }
+#endif
+ return &self;
+ } break;
+ case ADDR_TYPE_CLASS: {
+ return &static_ref;
+ } break;
+ case ADDR_TYPE_MEMBER: {
+#ifdef DEBUG_ENABLED
+ if (unlikely(!p_instance)) {
+ r_error = "Cannot access member without instance.";
+ return nullptr;
+ }
+#endif
+ //member indexing is O(1)
+ return &p_instance->members.write[address];
+ } break;
+ case ADDR_TYPE_CLASS_CONSTANT: {
+ //todo change to index!
+ GDScript *s = p_script;
+#ifdef DEBUG_ENABLED
+ ERR_FAIL_INDEX_V(address, _global_names_count, nullptr);
+#endif
+ const StringName *sn = &_global_names_ptr[address];
+
+ while (s) {
+ GDScript *o = s;
+ while (o) {
+ Map<StringName, Variant>::Element *E = o->constants.find(*sn);
+ if (E) {
+ return &E->get();
+ }
+ o = o->_owner;
+ }
+ s = s->_base;
+ }
+
+ ERR_FAIL_V_MSG(nullptr, "GDScriptCompiler bug.");
+ } break;
+ case ADDR_TYPE_LOCAL_CONSTANT: {
+#ifdef DEBUG_ENABLED
+ ERR_FAIL_INDEX_V(address, _constant_count, nullptr);
+#endif
+ return &_constants_ptr[address];
+ } break;
+ case ADDR_TYPE_STACK:
+ case ADDR_TYPE_STACK_VARIABLE: {
+#ifdef DEBUG_ENABLED
+ ERR_FAIL_INDEX_V(address, _stack_size, nullptr);
+#endif
+ return &p_stack[address];
+ } break;
+ case ADDR_TYPE_GLOBAL: {
+#ifdef DEBUG_ENABLED
+ ERR_FAIL_INDEX_V(address, GDScriptLanguage::get_singleton()->get_global_array_size(), nullptr);
+#endif
+ return &GDScriptLanguage::get_singleton()->get_global_array()[address];
+ } break;
+#ifdef TOOLS_ENABLED
+ case ADDR_TYPE_NAMED_GLOBAL: {
+#ifdef DEBUG_ENABLED
+ ERR_FAIL_INDEX_V(address, _global_names_count, nullptr);
+#endif
+ StringName id = _global_names_ptr[address];
+
+ if (GDScriptLanguage::get_singleton()->get_named_globals_map().has(id)) {
+ return (Variant *)&GDScriptLanguage::get_singleton()->get_named_globals_map()[id];
+ } else {
+ r_error = "Autoload singleton '" + String(id) + "' has been removed.";
+ return nullptr;
+ }
+ } break;
+#endif
+ case ADDR_TYPE_NIL: {
+ return &nil;
+ } break;
+ }
+
+ ERR_FAIL_V_MSG(nullptr, "Bad code! (unknown addressing mode).");
+ return nullptr;
+}
+
+#ifdef DEBUG_ENABLED
+static String _get_script_name(const Ref<Script> p_script) {
+ if (p_script->get_name().is_empty()) {
+ return p_script->get_path().get_file();
+ } else {
+ return p_script->get_name();
+ }
+}
+
+static String _get_var_type(const Variant *p_var) {
+ String basestr;
+
+ if (p_var->get_type() == Variant::OBJECT) {
+ bool was_freed;
+ Object *bobj = p_var->get_validated_object_with_check(was_freed);
+ if (!bobj) {
+ if (was_freed) {
+ basestr = "null instance";
+ } else {
+ basestr = "previously freed";
+ }
+ } else {
+ basestr = bobj->get_class();
+ if (bobj->get_script_instance()) {
+ basestr += " (" + _get_script_name(bobj->get_script_instance()->get_script()) + ")";
+ }
+ }
+
+ } else {
+ if (p_var->get_type() == Variant::ARRAY) {
+ basestr = "Array";
+ const Array *p_array = VariantInternal::get_array(p_var);
+ Variant::Type builtin_type = (Variant::Type)p_array->get_typed_builtin();
+ StringName native_type = p_array->get_typed_class_name();
+ Ref<Script> script_type = p_array->get_typed_script();
+
+ if (script_type.is_valid() && script_type->is_valid()) {
+ basestr += "[" + _get_script_name(script_type) + "]";
+ } else if (native_type != StringName()) {
+ basestr += "[" + native_type.operator String() + "]";
+ } else if (builtin_type != Variant::NIL) {
+ basestr += "[" + Variant::get_type_name(builtin_type) + "]";
+ }
+ } else {
+ basestr = Variant::get_type_name(p_var->get_type());
+ }
+ }
+
+ return basestr;
+}
+#endif // DEBUG_ENABLED
+
+String GDScriptFunction::_get_call_error(const Callable::CallError &p_err, const String &p_where, const Variant **argptrs) const {
+ String err_text;
+
+ if (p_err.error == Callable::CallError::CALL_ERROR_INVALID_ARGUMENT) {
+ int errorarg = p_err.argument;
+ // Handle the Object to Object case separately as we don't have further class details.
+#ifdef DEBUG_ENABLED
+ if (p_err.expected == Variant::OBJECT && argptrs[errorarg]->get_type() == p_err.expected) {
+ err_text = "Invalid type in " + p_where + ". The Object-derived class of argument " + itos(errorarg + 1) + " (" + _get_var_type(argptrs[errorarg]) + ") is not a subclass of the expected argument class.";
+ } else
+#endif // DEBUG_ENABLED
+ {
+ err_text = "Invalid type in " + p_where + ". Cannot convert argument " + itos(errorarg + 1) + " from " + Variant::get_type_name(argptrs[errorarg]->get_type()) + " to " + Variant::get_type_name(Variant::Type(p_err.expected)) + ".";
+ }
+ } else if (p_err.error == Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS) {
+ err_text = "Invalid call to " + p_where + ". Expected " + itos(p_err.argument) + " arguments.";
+ } else if (p_err.error == Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS) {
+ err_text = "Invalid call to " + p_where + ". Expected " + itos(p_err.argument) + " arguments.";
+ } else if (p_err.error == Callable::CallError::CALL_ERROR_INVALID_METHOD) {
+ err_text = "Invalid call. Nonexistent " + p_where + ".";
+ } else if (p_err.error == Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL) {
+ err_text = "Attempt to call " + p_where + " on a null instance.";
+ } else {
+ err_text = "Bug, call error: #" + itos(p_err.error);
+ }
+
+ return err_text;
+}
+
+#if defined(__GNUC__)
+#define OPCODES_TABLE \
+ static const void *switch_table_ops[] = { \
+ &&OPCODE_OPERATOR, \
+ &&OPCODE_OPERATOR_VALIDATED, \
+ &&OPCODE_EXTENDS_TEST, \
+ &&OPCODE_IS_BUILTIN, \
+ &&OPCODE_SET_KEYED, \
+ &&OPCODE_SET_KEYED_VALIDATED, \
+ &&OPCODE_SET_INDEXED_VALIDATED, \
+ &&OPCODE_GET_KEYED, \
+ &&OPCODE_GET_KEYED_VALIDATED, \
+ &&OPCODE_GET_INDEXED_VALIDATED, \
+ &&OPCODE_SET_NAMED, \
+ &&OPCODE_SET_NAMED_VALIDATED, \
+ &&OPCODE_GET_NAMED, \
+ &&OPCODE_GET_NAMED_VALIDATED, \
+ &&OPCODE_SET_MEMBER, \
+ &&OPCODE_GET_MEMBER, \
+ &&OPCODE_ASSIGN, \
+ &&OPCODE_ASSIGN_TRUE, \
+ &&OPCODE_ASSIGN_FALSE, \
+ &&OPCODE_ASSIGN_TYPED_BUILTIN, \
+ &&OPCODE_ASSIGN_TYPED_ARRAY, \
+ &&OPCODE_ASSIGN_TYPED_NATIVE, \
+ &&OPCODE_ASSIGN_TYPED_SCRIPT, \
+ &&OPCODE_CAST_TO_BUILTIN, \
+ &&OPCODE_CAST_TO_NATIVE, \
+ &&OPCODE_CAST_TO_SCRIPT, \
+ &&OPCODE_CONSTRUCT, \
+ &&OPCODE_CONSTRUCT_VALIDATED, \
+ &&OPCODE_CONSTRUCT_ARRAY, \
+ &&OPCODE_CONSTRUCT_TYPED_ARRAY, \
+ &&OPCODE_CONSTRUCT_DICTIONARY, \
+ &&OPCODE_CALL, \
+ &&OPCODE_CALL_RETURN, \
+ &&OPCODE_CALL_ASYNC, \
+ &&OPCODE_CALL_UTILITY, \
+ &&OPCODE_CALL_UTILITY_VALIDATED, \
+ &&OPCODE_CALL_GDSCRIPT_UTILITY, \
+ &&OPCODE_CALL_BUILTIN_TYPE_VALIDATED, \
+ &&OPCODE_CALL_SELF_BASE, \
+ &&OPCODE_CALL_METHOD_BIND, \
+ &&OPCODE_CALL_METHOD_BIND_RET, \
+ &&OPCODE_CALL_PTRCALL_NO_RETURN, \
+ &&OPCODE_CALL_PTRCALL_BOOL, \
+ &&OPCODE_CALL_PTRCALL_INT, \
+ &&OPCODE_CALL_PTRCALL_FLOAT, \
+ &&OPCODE_CALL_PTRCALL_STRING, \
+ &&OPCODE_CALL_PTRCALL_VECTOR2, \
+ &&OPCODE_CALL_PTRCALL_VECTOR2I, \
+ &&OPCODE_CALL_PTRCALL_RECT2, \
+ &&OPCODE_CALL_PTRCALL_RECT2I, \
+ &&OPCODE_CALL_PTRCALL_VECTOR3, \
+ &&OPCODE_CALL_PTRCALL_VECTOR3I, \
+ &&OPCODE_CALL_PTRCALL_TRANSFORM2D, \
+ &&OPCODE_CALL_PTRCALL_PLANE, \
+ &&OPCODE_CALL_PTRCALL_QUAT, \
+ &&OPCODE_CALL_PTRCALL_AABB, \
+ &&OPCODE_CALL_PTRCALL_BASIS, \
+ &&OPCODE_CALL_PTRCALL_TRANSFORM, \
+ &&OPCODE_CALL_PTRCALL_COLOR, \
+ &&OPCODE_CALL_PTRCALL_STRING_NAME, \
+ &&OPCODE_CALL_PTRCALL_NODE_PATH, \
+ &&OPCODE_CALL_PTRCALL_RID, \
+ &&OPCODE_CALL_PTRCALL_OBJECT, \
+ &&OPCODE_CALL_PTRCALL_CALLABLE, \
+ &&OPCODE_CALL_PTRCALL_SIGNAL, \
+ &&OPCODE_CALL_PTRCALL_DICTIONARY, \
+ &&OPCODE_CALL_PTRCALL_ARRAY, \
+ &&OPCODE_CALL_PTRCALL_PACKED_BYTE_ARRAY, \
+ &&OPCODE_CALL_PTRCALL_PACKED_INT32_ARRAY, \
+ &&OPCODE_CALL_PTRCALL_PACKED_INT64_ARRAY, \
+ &&OPCODE_CALL_PTRCALL_PACKED_FLOAT32_ARRAY, \
+ &&OPCODE_CALL_PTRCALL_PACKED_FLOAT64_ARRAY, \
+ &&OPCODE_CALL_PTRCALL_PACKED_STRING_ARRAY, \
+ &&OPCODE_CALL_PTRCALL_PACKED_VECTOR2_ARRAY, \
+ &&OPCODE_CALL_PTRCALL_PACKED_VECTOR3_ARRAY, \
+ &&OPCODE_CALL_PTRCALL_PACKED_COLOR_ARRAY, \
+ &&OPCODE_AWAIT, \
+ &&OPCODE_AWAIT_RESUME, \
+ &&OPCODE_JUMP, \
+ &&OPCODE_JUMP_IF, \
+ &&OPCODE_JUMP_IF_NOT, \
+ &&OPCODE_JUMP_TO_DEF_ARGUMENT, \
+ &&OPCODE_RETURN, \
+ &&OPCODE_ITERATE_BEGIN, \
+ &&OPCODE_ITERATE_BEGIN_INT, \
+ &&OPCODE_ITERATE_BEGIN_FLOAT, \
+ &&OPCODE_ITERATE_BEGIN_VECTOR2, \
+ &&OPCODE_ITERATE_BEGIN_VECTOR2I, \
+ &&OPCODE_ITERATE_BEGIN_VECTOR3, \
+ &&OPCODE_ITERATE_BEGIN_VECTOR3I, \
+ &&OPCODE_ITERATE_BEGIN_STRING, \
+ &&OPCODE_ITERATE_BEGIN_DICTIONARY, \
+ &&OPCODE_ITERATE_BEGIN_ARRAY, \
+ &&OPCODE_ITERATE_BEGIN_PACKED_BYTE_ARRAY, \
+ &&OPCODE_ITERATE_BEGIN_PACKED_INT32_ARRAY, \
+ &&OPCODE_ITERATE_BEGIN_PACKED_INT64_ARRAY, \
+ &&OPCODE_ITERATE_BEGIN_PACKED_FLOAT32_ARRAY, \
+ &&OPCODE_ITERATE_BEGIN_PACKED_FLOAT64_ARRAY, \
+ &&OPCODE_ITERATE_BEGIN_PACKED_STRING_ARRAY, \
+ &&OPCODE_ITERATE_BEGIN_PACKED_VECTOR2_ARRAY, \
+ &&OPCODE_ITERATE_BEGIN_PACKED_VECTOR3_ARRAY, \
+ &&OPCODE_ITERATE_BEGIN_PACKED_COLOR_ARRAY, \
+ &&OPCODE_ITERATE_BEGIN_OBJECT, \
+ &&OPCODE_ITERATE, \
+ &&OPCODE_ITERATE_INT, \
+ &&OPCODE_ITERATE_FLOAT, \
+ &&OPCODE_ITERATE_VECTOR2, \
+ &&OPCODE_ITERATE_VECTOR2I, \
+ &&OPCODE_ITERATE_VECTOR3, \
+ &&OPCODE_ITERATE_VECTOR3I, \
+ &&OPCODE_ITERATE_STRING, \
+ &&OPCODE_ITERATE_DICTIONARY, \
+ &&OPCODE_ITERATE_ARRAY, \
+ &&OPCODE_ITERATE_PACKED_BYTE_ARRAY, \
+ &&OPCODE_ITERATE_PACKED_INT32_ARRAY, \
+ &&OPCODE_ITERATE_PACKED_INT64_ARRAY, \
+ &&OPCODE_ITERATE_PACKED_FLOAT32_ARRAY, \
+ &&OPCODE_ITERATE_PACKED_FLOAT64_ARRAY, \
+ &&OPCODE_ITERATE_PACKED_STRING_ARRAY, \
+ &&OPCODE_ITERATE_PACKED_VECTOR2_ARRAY, \
+ &&OPCODE_ITERATE_PACKED_VECTOR3_ARRAY, \
+ &&OPCODE_ITERATE_PACKED_COLOR_ARRAY, \
+ &&OPCODE_ITERATE_OBJECT, \
+ &&OPCODE_ASSERT, \
+ &&OPCODE_BREAKPOINT, \
+ &&OPCODE_LINE, \
+ &&OPCODE_END \
+ }; \
+ static_assert((sizeof(switch_table_ops) / sizeof(switch_table_ops[0]) == (OPCODE_END + 1)), "Opcodes in jump table aren't the same as opcodes in enum.");
+
+#define OPCODE(m_op) \
+ m_op:
+#define OPCODE_WHILE(m_test) \
+ OPSWHILE:
+#define OPCODES_END \
+ OPSEXIT:
+#define OPCODES_OUT \
+ OPSOUT:
+#define DISPATCH_OPCODE goto OPSWHILE
+#define OPCODE_SWITCH(m_test) goto *switch_table_ops[m_test];
+#define OPCODE_BREAK goto OPSEXIT
+#define OPCODE_OUT goto OPSOUT
+#else
+#define OPCODES_TABLE
+#define OPCODE(m_op) case m_op:
+#define OPCODE_WHILE(m_test) while (m_test)
+#define OPCODES_END
+#define OPCODES_OUT
+#define DISPATCH_OPCODE continue
+#define OPCODE_SWITCH(m_test) switch (m_test)
+#define OPCODE_BREAK break
+#define OPCODE_OUT break
+#endif
+
+// Helpers for VariantInternal methods in macros.
+#define OP_GET_BOOL get_bool
+#define OP_GET_INT get_int
+#define OP_GET_FLOAT get_float
+#define OP_GET_VECTOR2 get_vector2
+#define OP_GET_VECTOR2I get_vector2i
+#define OP_GET_VECTOR3 get_vector3
+#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_COLOR get_color
+#define OP_GET_STRING get_string
+#define OP_GET_STRING_NAME get_string_name
+#define OP_GET_NODE_PATH get_node_path
+#define OP_GET_CALLABLE get_callable
+#define OP_GET_SIGNAL get_signal
+#define OP_GET_ARRAY get_array
+#define OP_GET_DICTIONARY get_dictionary
+#define OP_GET_PACKED_BYTE_ARRAY get_byte_array
+#define OP_GET_PACKED_INT32_ARRAY get_int32_array
+#define OP_GET_PACKED_INT64_ARRAY get_int64_array
+#define OP_GET_PACKED_FLOAT32_ARRAY get_float32_array
+#define OP_GET_PACKED_FLOAT64_ARRAY get_float64_array
+#define OP_GET_PACKED_STRING_ARRAY get_string_array
+#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_TRANSFORM2D get_transform2d
+#define OP_GET_PLANE get_plane
+#define OP_GET_AABB get_aabb
+#define OP_GET_BASIS get_basis
+#define OP_GET_RID get_rid
+
+Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_args, int p_argcount, Callable::CallError &r_err, CallState *p_state) {
+ OPCODES_TABLE;
+
+ if (!_code_ptr) {
+ return Variant();
+ }
+
+ r_err.error = Callable::CallError::CALL_OK;
+
+ Variant self;
+ Variant static_ref;
+ Variant retvalue;
+ Variant *stack = nullptr;
+ Variant **instruction_args;
+ const void **call_args_ptr = nullptr;
+ int defarg = 0;
+
+#ifdef DEBUG_ENABLED
+
+ //GDScriptLanguage::get_singleton()->calls++;
+
+#endif
+
+ uint32_t alloca_size = 0;
+ GDScript *script;
+ int ip = 0;
+ int line = _initial_line;
+
+ if (p_state) {
+ //use existing (supplied) state (awaited)
+ stack = (Variant *)p_state->stack.ptr();
+ instruction_args = (Variant **)&p_state->stack.ptr()[sizeof(Variant) * p_state->stack_size]; //ptr() to avoid bounds check
+ line = p_state->line;
+ ip = p_state->ip;
+ alloca_size = p_state->stack.size();
+ script = p_state->script;
+ p_instance = p_state->instance;
+ defarg = p_state->defarg;
+ self = p_state->self;
+
+ } else {
+ if (p_argcount != _argument_count) {
+ if (p_argcount > _argument_count) {
+ r_err.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS;
+ r_err.argument = _argument_count;
+
+ return Variant();
+ } else if (p_argcount < _argument_count - _default_arg_count) {
+ r_err.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
+ r_err.argument = _argument_count - _default_arg_count;
+ return Variant();
+ } else {
+ defarg = _argument_count - p_argcount;
+ }
+ }
+
+ alloca_size = sizeof(Variant *) * _instruction_args_size + sizeof(Variant) * _stack_size;
+
+ if (alloca_size) {
+ uint8_t *aptr = (uint8_t *)alloca(alloca_size);
+
+ if (_stack_size) {
+ stack = (Variant *)aptr;
+ for (int i = 0; i < p_argcount; i++) {
+ if (!argument_types[i].has_type) {
+ memnew_placement(&stack[i], Variant(*p_args[i]));
+ continue;
+ }
+
+ if (!argument_types[i].is_type(*p_args[i], true)) {
+ r_err.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_err.argument = i;
+ r_err.expected = argument_types[i].kind == GDScriptDataType::BUILTIN ? argument_types[i].builtin_type : Variant::OBJECT;
+ return Variant();
+ }
+ if (argument_types[i].kind == GDScriptDataType::BUILTIN) {
+ Variant arg;
+ Variant::construct(argument_types[i].builtin_type, arg, &p_args[i], 1, r_err);
+ memnew_placement(&stack[i], Variant(arg));
+ } else {
+ memnew_placement(&stack[i], Variant(*p_args[i]));
+ }
+ }
+ for (int i = p_argcount; i < _stack_size; i++) {
+ memnew_placement(&stack[i], Variant);
+ }
+ } else {
+ stack = nullptr;
+ }
+
+ if (_instruction_args_size) {
+ instruction_args = (Variant **)&aptr[sizeof(Variant) * _stack_size];
+ } else {
+ instruction_args = nullptr;
+ }
+
+ } else {
+ stack = nullptr;
+ instruction_args = nullptr;
+ }
+
+ if (p_instance) {
+ self = p_instance->owner;
+ script = p_instance->script.ptr();
+ } else {
+ script = _script;
+ }
+ }
+ if (_ptrcall_args_size) {
+ call_args_ptr = (const void **)alloca(_ptrcall_args_size * sizeof(void *));
+ } else {
+ call_args_ptr = nullptr;
+ }
+
+ static_ref = script;
+
+ String err_text;
+
+#ifdef DEBUG_ENABLED
+
+ if (EngineDebugger::is_active()) {
+ GDScriptLanguage::get_singleton()->enter_function(p_instance, this, stack, &ip, &line);
+ }
+
+#define GD_ERR_BREAK(m_cond) \
+ { \
+ if (unlikely(m_cond)) { \
+ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition ' " _STR(m_cond) " ' is true. Breaking..:"); \
+ OPCODE_BREAK; \
+ } \
+ }
+
+#define CHECK_SPACE(m_space) \
+ GD_ERR_BREAK((ip + m_space) > _code_size)
+
+#define GET_VARIANT_PTR(m_v, m_code_ofs) \
+ Variant *m_v; \
+ m_v = _get_variant(_code_ptr[ip + m_code_ofs], p_instance, script, self, static_ref, stack, err_text); \
+ if (unlikely(!m_v)) \
+ OPCODE_BREAK;
+
+#else
+#define GD_ERR_BREAK(m_cond)
+#define CHECK_SPACE(m_space)
+#define GET_VARIANT_PTR(m_v, m_code_ofs) \
+ Variant *m_v; \
+ m_v = _get_variant(_code_ptr[ip + m_code_ofs], p_instance, script, self, static_ref, stack, err_text);
+
+#endif
+
+#define GET_INSTRUCTION_ARG(m_v, m_idx) \
+ Variant *m_v = instruction_args[m_idx]
+
+#ifdef DEBUG_ENABLED
+
+ uint64_t function_start_time = 0;
+ uint64_t function_call_time = 0;
+
+ if (GDScriptLanguage::get_singleton()->profiling) {
+ function_start_time = OS::get_singleton()->get_ticks_usec();
+ function_call_time = 0;
+ profile.call_count++;
+ profile.frame_call_count++;
+ }
+ bool exit_ok = false;
+ bool awaited = false;
+#endif
+
+#ifdef DEBUG_ENABLED
+ OPCODE_WHILE(ip < _code_size) {
+ int last_opcode = _code_ptr[ip];
+#else
+ OPCODE_WHILE(true) {
+#endif
+ // Load arguments for the instruction before each instruction.
+ int instr_arg_count = ((_code_ptr[ip]) & INSTR_ARGS_MASK) >> INSTR_BITS;
+ for (int i = 0; i < instr_arg_count; i++) {
+ GET_VARIANT_PTR(v, i + 1);
+ instruction_args[i] = v;
+ }
+
+ OPCODE_SWITCH(_code_ptr[ip] & INSTR_MASK) {
+ OPCODE(OPCODE_OPERATOR) {
+ CHECK_SPACE(5);
+
+ bool valid;
+ Variant::Operator op = (Variant::Operator)_code_ptr[ip + 4];
+ GD_ERR_BREAK(op >= Variant::OP_MAX);
+
+ GET_INSTRUCTION_ARG(a, 0);
+ GET_INSTRUCTION_ARG(b, 1);
+ GET_INSTRUCTION_ARG(dst, 2);
+
+#ifdef DEBUG_ENABLED
+
+ Variant ret;
+ Variant::evaluate(op, *a, *b, ret, valid);
+#else
+ Variant::evaluate(op, *a, *b, *dst, valid);
+#endif
+#ifdef DEBUG_ENABLED
+ if (!valid) {
+ if (ret.get_type() == Variant::STRING) {
+ //return a string when invalid with the error
+ err_text = ret;
+ err_text += " in operator '" + Variant::get_operator_name(op) + "'.";
+ } else {
+ err_text = "Invalid operands '" + Variant::get_type_name(a->get_type()) + "' and '" + Variant::get_type_name(b->get_type()) + "' in operator '" + Variant::get_operator_name(op) + "'.";
+ }
+ OPCODE_BREAK;
+ }
+ *dst = ret;
+#endif
+ ip += 5;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_OPERATOR_VALIDATED) {
+ CHECK_SPACE(5);
+
+ int operator_idx = _code_ptr[ip + 4];
+ GD_ERR_BREAK(operator_idx < 0 || operator_idx >= _operator_funcs_count);
+ Variant::ValidatedOperatorEvaluator operator_func = _operator_funcs_ptr[operator_idx];
+
+ GET_INSTRUCTION_ARG(a, 0);
+ GET_INSTRUCTION_ARG(b, 1);
+ GET_INSTRUCTION_ARG(dst, 2);
+
+ operator_func(a, b, dst);
+
+ ip += 5;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_EXTENDS_TEST) {
+ CHECK_SPACE(4);
+
+ GET_INSTRUCTION_ARG(a, 0);
+ GET_INSTRUCTION_ARG(b, 1);
+ GET_INSTRUCTION_ARG(dst, 2);
+
+#ifdef DEBUG_ENABLED
+ if (b->get_type() != Variant::OBJECT || b->operator Object *() == nullptr) {
+ err_text = "Right operand of 'is' is not a class.";
+ OPCODE_BREAK;
+ }
+#endif
+
+ bool extends_ok = false;
+ if (a->get_type() == Variant::OBJECT && a->operator Object *() != nullptr) {
+#ifdef DEBUG_ENABLED
+ bool was_freed;
+ Object *obj_A = a->get_validated_object_with_check(was_freed);
+
+ if (was_freed) {
+ err_text = "Left operand of 'is' is a previously freed instance.";
+ OPCODE_BREAK;
+ }
+
+ Object *obj_B = b->get_validated_object_with_check(was_freed);
+
+ if (was_freed) {
+ err_text = "Right operand of 'is' is a previously freed instance.";
+ OPCODE_BREAK;
+ }
+#else
+
+ Object *obj_A = *a;
+ Object *obj_B = *b;
+#endif // DEBUG_ENABLED
+
+ GDScript *scr_B = Object::cast_to<GDScript>(obj_B);
+
+ if (scr_B) {
+ //if B is a script, the only valid condition is that A has an instance which inherits from the script
+ //in other situation, this should return false.
+
+ if (obj_A->get_script_instance() && obj_A->get_script_instance()->get_language() == GDScriptLanguage::get_singleton()) {
+ GDScript *cmp = static_cast<GDScript *>(obj_A->get_script_instance()->get_script().ptr());
+ //bool found=false;
+ while (cmp) {
+ if (cmp == scr_B) {
+ //inherits from script, all ok
+ extends_ok = true;
+ break;
+ }
+
+ cmp = cmp->_base;
+ }
+ }
+
+ } else {
+ GDScriptNativeClass *nc = Object::cast_to<GDScriptNativeClass>(obj_B);
+
+#ifdef DEBUG_ENABLED
+ if (!nc) {
+ err_text = "Right operand of 'is' is not a class (type: '" + obj_B->get_class() + "').";
+ OPCODE_BREAK;
+ }
+#endif
+ extends_ok = ClassDB::is_parent_class(obj_A->get_class_name(), nc->get_name());
+ }
+ }
+
+ *dst = extends_ok;
+ ip += 4;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_IS_BUILTIN) {
+ CHECK_SPACE(4);
+
+ GET_INSTRUCTION_ARG(value, 0);
+ GET_INSTRUCTION_ARG(dst, 1);
+ Variant::Type var_type = (Variant::Type)_code_ptr[ip + 3];
+
+ GD_ERR_BREAK(var_type < 0 || var_type >= Variant::VARIANT_MAX);
+
+ *dst = value->get_type() == var_type;
+ ip += 4;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_SET_KEYED) {
+ CHECK_SPACE(3);
+
+ GET_INSTRUCTION_ARG(dst, 0);
+ GET_INSTRUCTION_ARG(index, 1);
+ GET_INSTRUCTION_ARG(value, 2);
+
+ bool valid;
+ dst->set(*index, *value, &valid);
+
+#ifdef DEBUG_ENABLED
+ if (!valid) {
+ String v = index->operator String();
+ if (v != "") {
+ v = "'" + v + "'";
+ } else {
+ v = "of type '" + _get_var_type(index) + "'";
+ }
+ err_text = "Invalid set index " + v + " (on base: '" + _get_var_type(dst) + "') with value of type '" + _get_var_type(value) + "'";
+ OPCODE_BREAK;
+ }
+#endif
+ ip += 4;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_SET_KEYED_VALIDATED) {
+ CHECK_SPACE(4);
+
+ GET_INSTRUCTION_ARG(dst, 0);
+ GET_INSTRUCTION_ARG(index, 1);
+ GET_INSTRUCTION_ARG(value, 2);
+
+ int index_setter = _code_ptr[ip + 4];
+ GD_ERR_BREAK(index_setter < 0 || index_setter >= _keyed_setters_count);
+ const Variant::ValidatedKeyedSetter setter = _keyed_setters_ptr[index_setter];
+
+ bool valid;
+ setter(dst, index, value, &valid);
+
+#ifdef DEBUG_ENABLED
+ if (!valid) {
+ String v = index->operator String();
+ if (v != "") {
+ v = "'" + v + "'";
+ } else {
+ v = "of type '" + _get_var_type(index) + "'";
+ }
+ err_text = "Invalid set index " + v + " (on base: '" + _get_var_type(dst) + "') with value of type '" + _get_var_type(value) + "'";
+ OPCODE_BREAK;
+ }
+#endif
+ ip += 5;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_SET_INDEXED_VALIDATED) {
+ CHECK_SPACE(4);
+
+ GET_INSTRUCTION_ARG(dst, 0);
+ GET_INSTRUCTION_ARG(index, 1);
+ GET_INSTRUCTION_ARG(value, 2);
+
+ int index_setter = _code_ptr[ip + 4];
+ GD_ERR_BREAK(index_setter < 0 || index_setter >= _indexed_setters_count);
+ const Variant::ValidatedIndexedSetter setter = _indexed_setters_ptr[index_setter];
+
+ int64_t int_index = *VariantInternal::get_int(index);
+
+ bool oob;
+ setter(dst, int_index, value, &oob);
+
+#ifdef DEBUG_ENABLED
+ if (oob) {
+ String v = index->operator String();
+ if (v != "") {
+ v = "'" + v + "'";
+ } else {
+ v = "of type '" + _get_var_type(index) + "'";
+ }
+ err_text = "Out of bounds set index " + v + " (on base: '" + _get_var_type(dst) + "')";
+ OPCODE_BREAK;
+ }
+#endif
+ ip += 5;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_GET_KEYED) {
+ CHECK_SPACE(3);
+
+ GET_INSTRUCTION_ARG(src, 0);
+ GET_INSTRUCTION_ARG(index, 1);
+ GET_INSTRUCTION_ARG(dst, 2);
+
+ bool valid;
+#ifdef DEBUG_ENABLED
+ // Allow better error message in cases where src and dst are the same stack position.
+ Variant ret = src->get(*index, &valid);
+#else
+ *dst = src->get(*index, &valid);
+
+#endif
+#ifdef DEBUG_ENABLED
+ if (!valid) {
+ String v = index->operator String();
+ if (v != "") {
+ v = "'" + v + "'";
+ } else {
+ v = "of type '" + _get_var_type(index) + "'";
+ }
+ err_text = "Invalid get index " + v + " (on base: '" + _get_var_type(src) + "').";
+ OPCODE_BREAK;
+ }
+ *dst = ret;
+#endif
+ ip += 4;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_GET_KEYED_VALIDATED) {
+ CHECK_SPACE(4);
+
+ GET_INSTRUCTION_ARG(src, 0);
+ GET_INSTRUCTION_ARG(key, 1);
+ GET_INSTRUCTION_ARG(dst, 2);
+
+ int index_getter = _code_ptr[ip + 4];
+ GD_ERR_BREAK(index_getter < 0 || index_getter >= _keyed_getters_count);
+ const Variant::ValidatedKeyedGetter getter = _keyed_getters_ptr[index_getter];
+
+ bool valid;
+#ifdef DEBUG_ENABLED
+ // Allow better error message in cases where src and dst are the same stack position.
+ Variant ret;
+ getter(src, key, &ret, &valid);
+#else
+ getter(src, key, dst, &valid);
+#endif
+#ifdef DEBUG_ENABLED
+ if (!valid) {
+ String v = key->operator String();
+ if (v != "") {
+ v = "'" + v + "'";
+ } else {
+ v = "of type '" + _get_var_type(key) + "'";
+ }
+ err_text = "Invalid get index " + v + " (on base: '" + _get_var_type(src) + "').";
+ OPCODE_BREAK;
+ }
+ *dst = ret;
+#endif
+ ip += 5;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_GET_INDEXED_VALIDATED) {
+ CHECK_SPACE(4);
+
+ GET_INSTRUCTION_ARG(src, 0);
+ GET_INSTRUCTION_ARG(index, 1);
+ GET_INSTRUCTION_ARG(dst, 2);
+
+ int index_getter = _code_ptr[ip + 4];
+ GD_ERR_BREAK(index_getter < 0 || index_getter >= _indexed_getters_count);
+ const Variant::ValidatedIndexedGetter getter = _indexed_getters_ptr[index_getter];
+
+ int64_t int_index = *VariantInternal::get_int(index);
+
+ bool oob;
+ getter(src, int_index, dst, &oob);
+
+#ifdef DEBUG_ENABLED
+ if (oob) {
+ String v = index->operator String();
+ if (v != "") {
+ v = "'" + v + "'";
+ } else {
+ v = "of type '" + _get_var_type(index) + "'";
+ }
+ err_text = "Out of bounds get index " + v + " (on base: '" + _get_var_type(src) + "')";
+ OPCODE_BREAK;
+ }
+#endif
+ ip += 5;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_SET_NAMED) {
+ CHECK_SPACE(3);
+
+ GET_INSTRUCTION_ARG(dst, 0);
+ GET_INSTRUCTION_ARG(value, 1);
+
+ int indexname = _code_ptr[ip + 3];
+
+ GD_ERR_BREAK(indexname < 0 || indexname >= _global_names_count);
+ const StringName *index = &_global_names_ptr[indexname];
+
+ bool valid;
+ dst->set_named(*index, *value, valid);
+
+#ifdef DEBUG_ENABLED
+ if (!valid) {
+ String err_type;
+ err_text = "Invalid set index '" + String(*index) + "' (on base: '" + _get_var_type(dst) + "') with value of type '" + _get_var_type(value) + "'.";
+ OPCODE_BREAK;
+ }
+#endif
+ ip += 4;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_SET_NAMED_VALIDATED) {
+ CHECK_SPACE(3);
+
+ GET_INSTRUCTION_ARG(dst, 0);
+ GET_INSTRUCTION_ARG(value, 1);
+
+ int index_setter = _code_ptr[ip + 3];
+ GD_ERR_BREAK(index_setter < 0 || index_setter >= _setters_count);
+ const Variant::ValidatedSetter setter = _setters_ptr[index_setter];
+
+ setter(dst, value);
+ ip += 4;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_GET_NAMED) {
+ CHECK_SPACE(4);
+
+ GET_INSTRUCTION_ARG(src, 0);
+ GET_INSTRUCTION_ARG(dst, 1);
+
+ int indexname = _code_ptr[ip + 3];
+
+ GD_ERR_BREAK(indexname < 0 || indexname >= _global_names_count);
+ const StringName *index = &_global_names_ptr[indexname];
+
+ bool valid;
+#ifdef DEBUG_ENABLED
+ //allow better error message in cases where src and dst are the same stack position
+ Variant ret = src->get_named(*index, valid);
+
+#else
+ *dst = src->get_named(*index, valid);
+#endif
+#ifdef DEBUG_ENABLED
+ if (!valid) {
+ if (src->has_method(*index)) {
+ err_text = "Invalid get index '" + index->operator String() + "' (on base: '" + _get_var_type(src) + "'). Did you mean '." + index->operator String() + "()' or funcref(obj, \"" + index->operator String() + "\") ?";
+ } else {
+ err_text = "Invalid get index '" + index->operator String() + "' (on base: '" + _get_var_type(src) + "').";
+ }
+ OPCODE_BREAK;
+ }
+ *dst = ret;
+#endif
+ ip += 4;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_GET_NAMED_VALIDATED) {
+ CHECK_SPACE(3);
+
+ GET_INSTRUCTION_ARG(src, 0);
+ GET_INSTRUCTION_ARG(dst, 1);
+
+ int index_getter = _code_ptr[ip + 3];
+ GD_ERR_BREAK(index_getter < 0 || index_getter >= _getters_count);
+ const Variant::ValidatedGetter getter = _getters_ptr[index_getter];
+
+ getter(src, dst);
+ ip += 4;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_SET_MEMBER) {
+ CHECK_SPACE(3);
+ GET_INSTRUCTION_ARG(src, 0);
+ int indexname = _code_ptr[ip + 2];
+ GD_ERR_BREAK(indexname < 0 || indexname >= _global_names_count);
+ const StringName *index = &_global_names_ptr[indexname];
+
+ bool valid;
+#ifndef DEBUG_ENABLED
+ ClassDB::set_property(p_instance->owner, *index, *src, &valid);
+#else
+ bool ok = ClassDB::set_property(p_instance->owner, *index, *src, &valid);
+ if (!ok) {
+ err_text = "Internal error setting property: " + String(*index);
+ OPCODE_BREAK;
+ } else if (!valid) {
+ err_text = "Error setting property '" + String(*index) + "' with value of type " + Variant::get_type_name(src->get_type()) + ".";
+ OPCODE_BREAK;
+ }
+#endif
+ ip += 3;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_GET_MEMBER) {
+ CHECK_SPACE(3);
+ GET_INSTRUCTION_ARG(dst, 0);
+ int indexname = _code_ptr[ip + 2];
+ GD_ERR_BREAK(indexname < 0 || indexname >= _global_names_count);
+ const StringName *index = &_global_names_ptr[indexname];
+#ifndef DEBUG_ENABLED
+ ClassDB::get_property(p_instance->owner, *index, *dst);
+#else
+ bool ok = ClassDB::get_property(p_instance->owner, *index, *dst);
+ if (!ok) {
+ err_text = "Internal error getting property: " + String(*index);
+ OPCODE_BREAK;
+ }
+#endif
+ ip += 3;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_ASSIGN) {
+ CHECK_SPACE(3);
+ GET_INSTRUCTION_ARG(dst, 0);
+ GET_INSTRUCTION_ARG(src, 1);
+
+ *dst = *src;
+
+ ip += 3;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_ASSIGN_TRUE) {
+ CHECK_SPACE(2);
+ GET_INSTRUCTION_ARG(dst, 0);
+
+ *dst = true;
+
+ ip += 2;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_ASSIGN_FALSE) {
+ CHECK_SPACE(2);
+ GET_INSTRUCTION_ARG(dst, 0);
+
+ *dst = false;
+
+ ip += 2;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_ASSIGN_TYPED_BUILTIN) {
+ CHECK_SPACE(4);
+ GET_INSTRUCTION_ARG(dst, 0);
+ GET_INSTRUCTION_ARG(src, 1);
+
+ Variant::Type var_type = (Variant::Type)_code_ptr[ip + 3];
+ GD_ERR_BREAK(var_type < 0 || var_type >= Variant::VARIANT_MAX);
+
+ if (src->get_type() != var_type) {
+#ifdef DEBUG_ENABLED
+ if (Variant::can_convert_strict(src->get_type(), var_type)) {
+#endif // DEBUG_ENABLED
+ Callable::CallError ce;
+ Variant::construct(var_type, *dst, const_cast<const Variant **>(&src), 1, ce);
+ } else {
+#ifdef DEBUG_ENABLED
+ err_text = "Trying to assign value of type '" + Variant::get_type_name(src->get_type()) +
+ "' to a variable of type '" + Variant::get_type_name(var_type) + "'.";
+ OPCODE_BREAK;
+ }
+ } else {
+#endif // DEBUG_ENABLED
+ *dst = *src;
+ }
+
+ ip += 4;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_ASSIGN_TYPED_ARRAY) {
+ CHECK_SPACE(3);
+ GET_INSTRUCTION_ARG(dst, 0);
+ GET_INSTRUCTION_ARG(src, 1);
+
+ Array *dst_arr = VariantInternal::get_array(dst);
+
+ if (src->get_type() != Variant::ARRAY) {
+#ifdef DEBUG_ENABLED
+ err_text = "Trying to assign value of type '" + Variant::get_type_name(src->get_type()) +
+ "' to a variable of type '" + +"'.";
+ OPCODE_BREAK;
+#endif
+ }
+ if (!dst_arr->typed_assign(*src)) {
+#ifdef DEBUG_ENABLED
+ err_text = "Trying to assign a typed array with an array of different type.'";
+ OPCODE_BREAK;
+#endif
+ }
+
+ ip += 3;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_ASSIGN_TYPED_NATIVE) {
+ CHECK_SPACE(4);
+ GET_INSTRUCTION_ARG(dst, 0);
+ GET_INSTRUCTION_ARG(src, 1);
+
+#ifdef DEBUG_ENABLED
+ GET_INSTRUCTION_ARG(type, 2);
+ GDScriptNativeClass *nc = Object::cast_to<GDScriptNativeClass>(type->operator Object *());
+ GD_ERR_BREAK(!nc);
+ if (src->get_type() != Variant::OBJECT && src->get_type() != Variant::NIL) {
+ err_text = "Trying to assign value of type '" + Variant::get_type_name(src->get_type()) +
+ "' to a variable of type '" + nc->get_name() + "'.";
+ OPCODE_BREAK;
+ }
+ Object *src_obj = src->operator Object *();
+
+ if (src_obj && !ClassDB::is_parent_class(src_obj->get_class_name(), nc->get_name())) {
+ err_text = "Trying to assign value of type '" + src_obj->get_class_name() +
+ "' to a variable of type '" + nc->get_name() + "'.";
+ OPCODE_BREAK;
+ }
+#endif // DEBUG_ENABLED
+ *dst = *src;
+
+ ip += 4;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_ASSIGN_TYPED_SCRIPT) {
+ CHECK_SPACE(4);
+ GET_INSTRUCTION_ARG(dst, 0);
+ GET_INSTRUCTION_ARG(src, 1);
+
+#ifdef DEBUG_ENABLED
+ GET_INSTRUCTION_ARG(type, 2);
+ Script *base_type = Object::cast_to<Script>(type->operator Object *());
+
+ GD_ERR_BREAK(!base_type);
+
+ if (src->get_type() != Variant::OBJECT && src->get_type() != Variant::NIL) {
+ err_text = "Trying to assign a non-object value to a variable of type '" + base_type->get_path().get_file() + "'.";
+ OPCODE_BREAK;
+ }
+
+ if (src->get_type() != Variant::NIL && src->operator Object *() != nullptr) {
+ ScriptInstance *scr_inst = src->operator Object *()->get_script_instance();
+ if (!scr_inst) {
+ err_text = "Trying to assign value of type '" + src->operator Object *()->get_class_name() +
+ "' to a variable of type '" + base_type->get_path().get_file() + "'.";
+ OPCODE_BREAK;
+ }
+
+ Script *src_type = src->operator Object *()->get_script_instance()->get_script().ptr();
+ bool valid = false;
+
+ while (src_type) {
+ if (src_type == base_type) {
+ valid = true;
+ break;
+ }
+ src_type = src_type->get_base_script().ptr();
+ }
+
+ if (!valid) {
+ err_text = "Trying to assign value of type '" + src->operator Object *()->get_script_instance()->get_script()->get_path().get_file() +
+ "' to a variable of type '" + base_type->get_path().get_file() + "'.";
+ OPCODE_BREAK;
+ }
+ }
+#endif // DEBUG_ENABLED
+
+ *dst = *src;
+
+ ip += 4;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_CAST_TO_BUILTIN) {
+ CHECK_SPACE(4);
+ GET_INSTRUCTION_ARG(src, 0);
+ GET_INSTRUCTION_ARG(dst, 1);
+ Variant::Type to_type = (Variant::Type)_code_ptr[ip + 3];
+
+ GD_ERR_BREAK(to_type < 0 || to_type >= Variant::VARIANT_MAX);
+
+ Callable::CallError err;
+ Variant::construct(to_type, *dst, (const Variant **)&src, 1, err);
+
+#ifdef DEBUG_ENABLED
+ if (err.error != Callable::CallError::CALL_OK) {
+ err_text = "Invalid cast: could not convert value to '" + Variant::get_type_name(to_type) + "'.";
+ OPCODE_BREAK;
+ }
+#endif
+
+ ip += 4;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_CAST_TO_NATIVE) {
+ CHECK_SPACE(4);
+ GET_INSTRUCTION_ARG(src, 0);
+ GET_INSTRUCTION_ARG(dst, 1);
+ GET_INSTRUCTION_ARG(to_type, 2);
+
+ GDScriptNativeClass *nc = Object::cast_to<GDScriptNativeClass>(to_type->operator Object *());
+ GD_ERR_BREAK(!nc);
+
+#ifdef DEBUG_ENABLED
+ if (src->get_type() != Variant::OBJECT && src->get_type() != Variant::NIL) {
+ err_text = "Invalid cast: can't convert a non-object value to an object type.";
+ OPCODE_BREAK;
+ }
+#endif
+ Object *src_obj = src->operator Object *();
+
+ if (src_obj && !ClassDB::is_parent_class(src_obj->get_class_name(), nc->get_name())) {
+ *dst = Variant(); // invalid cast, assign NULL
+ } else {
+ *dst = *src;
+ }
+
+ ip += 4;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_CAST_TO_SCRIPT) {
+ CHECK_SPACE(4);
+ GET_INSTRUCTION_ARG(src, 0);
+ GET_INSTRUCTION_ARG(dst, 1);
+ GET_INSTRUCTION_ARG(to_type, 2);
+
+ Script *base_type = Object::cast_to<Script>(to_type->operator Object *());
+
+ GD_ERR_BREAK(!base_type);
+
+#ifdef DEBUG_ENABLED
+ if (src->get_type() != Variant::OBJECT && src->get_type() != Variant::NIL) {
+ err_text = "Trying to assign a non-object value to a variable of type '" + base_type->get_path().get_file() + "'.";
+ OPCODE_BREAK;
+ }
+#endif
+
+ bool valid = false;
+
+ if (src->get_type() != Variant::NIL && src->operator Object *() != nullptr) {
+ ScriptInstance *scr_inst = src->operator Object *()->get_script_instance();
+
+ if (scr_inst) {
+ Script *src_type = src->operator Object *()->get_script_instance()->get_script().ptr();
+
+ while (src_type) {
+ if (src_type == base_type) {
+ valid = true;
+ break;
+ }
+ src_type = src_type->get_base_script().ptr();
+ }
+ }
+ }
+
+ if (valid) {
+ *dst = *src; // Valid cast, copy the source object
+ } else {
+ *dst = Variant(); // invalid cast, assign NULL
+ }
+
+ ip += 4;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_CONSTRUCT) {
+ CHECK_SPACE(2 + instr_arg_count);
+
+ ip += instr_arg_count;
+
+ int argc = _code_ptr[ip + 1];
+
+ Variant::Type t = Variant::Type(_code_ptr[ip + 2]);
+ Variant **argptrs = instruction_args;
+
+ GET_INSTRUCTION_ARG(dst, argc);
+
+ Callable::CallError err;
+ Variant::construct(t, *dst, (const Variant **)argptrs, argc, err);
+
+#ifdef DEBUG_ENABLED
+ if (err.error != Callable::CallError::CALL_OK) {
+ err_text = _get_call_error(err, "'" + Variant::get_type_name(t) + "' constructor", (const Variant **)argptrs);
+ OPCODE_BREAK;
+ }
+#endif
+
+ ip += 3;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_CONSTRUCT_VALIDATED) {
+ CHECK_SPACE(2 + instr_arg_count);
+
+ ip += instr_arg_count;
+
+ int argc = _code_ptr[ip + 1];
+
+ int constructor_idx = _code_ptr[ip + 2];
+ GD_ERR_BREAK(constructor_idx < 0 || constructor_idx >= _constructors_count);
+ Variant::ValidatedConstructor constructor = _constructors_ptr[constructor_idx];
+
+ Variant **argptrs = instruction_args;
+
+ GET_INSTRUCTION_ARG(dst, argc);
+
+ constructor(dst, (const Variant **)argptrs);
+
+ ip += 3;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_CONSTRUCT_ARRAY) {
+ CHECK_SPACE(1 + instr_arg_count);
+ ip += instr_arg_count;
+
+ int argc = _code_ptr[ip + 1];
+ Array array;
+ array.resize(argc);
+
+ for (int i = 0; i < argc; i++) {
+ array[i] = *(instruction_args[i]);
+ }
+
+ GET_INSTRUCTION_ARG(dst, argc);
+ *dst = Variant(); // Clear potential previous typed array.
+
+ *dst = array;
+
+ ip += 2;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_CONSTRUCT_TYPED_ARRAY) {
+ CHECK_SPACE(3 + instr_arg_count);
+ ip += instr_arg_count;
+
+ int argc = _code_ptr[ip + 1];
+
+ GET_INSTRUCTION_ARG(script_type, argc + 1);
+ Variant::Type builtin_type = (Variant::Type)_code_ptr[ip + 2];
+ int native_type_idx = _code_ptr[ip + 3];
+ GD_ERR_BREAK(native_type_idx < 0 || native_type_idx >= _global_names_count);
+ const StringName native_type = _global_names_ptr[native_type_idx];
+
+ Array array;
+ array.set_typed(builtin_type, native_type, script_type);
+ array.resize(argc);
+
+ for (int i = 0; i < argc; i++) {
+ array[i] = *(instruction_args[i]);
+ }
+
+ GET_INSTRUCTION_ARG(dst, argc);
+ *dst = Variant(); // Clear potential previous typed array.
+
+ *dst = array;
+
+ ip += 4;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_CONSTRUCT_DICTIONARY) {
+ CHECK_SPACE(2 + instr_arg_count);
+
+ ip += instr_arg_count;
+
+ int argc = _code_ptr[ip + 1];
+ Dictionary dict;
+
+ for (int i = 0; i < argc; i++) {
+ GET_INSTRUCTION_ARG(k, i * 2 + 0);
+ GET_INSTRUCTION_ARG(v, i * 2 + 1);
+ dict[*k] = *v;
+ }
+
+ GET_INSTRUCTION_ARG(dst, argc * 2);
+
+ *dst = dict;
+
+ ip += 2;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_CALL_ASYNC)
+ OPCODE(OPCODE_CALL_RETURN)
+ OPCODE(OPCODE_CALL) {
+ CHECK_SPACE(3 + instr_arg_count);
+ bool call_ret = (_code_ptr[ip] & INSTR_MASK) != OPCODE_CALL;
+#ifdef DEBUG_ENABLED
+ bool call_async = (_code_ptr[ip] & INSTR_MASK) == OPCODE_CALL_ASYNC;
+#endif
+
+ ip += instr_arg_count;
+
+ int argc = _code_ptr[ip + 1];
+ GD_ERR_BREAK(argc < 0);
+
+ 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];
+
+ GET_INSTRUCTION_ARG(base, argc);
+ Variant **argptrs = 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;
+ if (call_ret) {
+ GET_INSTRUCTION_ARG(ret, argc + 1);
+ base->call(*methodname, (const Variant **)argptrs, argc, *ret, err);
+#ifdef DEBUG_ENABLED
+ if (!call_async && ret->get_type() == Variant::OBJECT) {
+ // Check if getting a function state without await.
+ bool was_freed = false;
+ Object *obj = ret->get_validated_object_with_check(was_freed);
+
+ if (was_freed) {
+ err_text = "Got a freed object as a result of the call.";
+ OPCODE_BREAK;
+ }
+ if (obj && obj->is_class_ptr(GDScriptFunctionState::get_class_ptr_static())) {
+ err_text = R"(Trying to call an async function without "await".)";
+ OPCODE_BREAK;
+ }
+ }
+#endif
+ } else {
+ Variant ret;
+ base->call(*methodname, (const Variant **)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) {
+ String methodstr = *methodname;
+ String basestr = _get_var_type(base);
+
+ if (methodstr == "call") {
+ if (argc >= 1) {
+ methodstr = String(*argptrs[0]) + " (via call)";
+ if (err.error == Callable::CallError::CALL_ERROR_INVALID_ARGUMENT) {
+ err.argument += 1;
+ }
+ }
+ } else if (methodstr == "free") {
+ if (err.error == Callable::CallError::CALL_ERROR_INVALID_METHOD) {
+ if (base->is_ref()) {
+ err_text = "Attempted to free a reference.";
+ OPCODE_BREAK;
+ } else if (base->get_type() == Variant::OBJECT) {
+ err_text = "Attempted to free a locked object (calling or emitting).";
+ OPCODE_BREAK;
+ }
+ }
+ } else if (methodstr == "call_recursive" && basestr == "TreeItem") {
+ if (argc >= 1) {
+ methodstr = String(*argptrs[0]) + " (via TreeItem.call_recursive)";
+ if (err.error == Callable::CallError::CALL_ERROR_INVALID_ARGUMENT) {
+ err.argument += 1;
+ }
+ }
+ }
+ err_text = _get_call_error(err, "function '" + methodstr + "' in base '" + basestr + "'", (const Variant **)argptrs);
+ OPCODE_BREAK;
+ }
+#endif
+
+ ip += 3;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_CALL_METHOD_BIND)
+ OPCODE(OPCODE_CALL_METHOD_BIND_RET) {
+ CHECK_SPACE(3 + instr_arg_count);
+ bool call_ret = (_code_ptr[ip] & INSTR_MASK) == OPCODE_CALL_METHOD_BIND_RET;
+
+ ip += instr_arg_count;
+
+ int argc = _code_ptr[ip + 1];
+ GD_ERR_BREAK(argc < 0);
+ GD_ERR_BREAK(_code_ptr[ip + 2] < 0 || _code_ptr[ip + 2] >= _methods_count);
+ MethodBind *method = _methods_ptr[_code_ptr[ip + 2]];
+
+ GET_INSTRUCTION_ARG(base, argc);
+
+#ifdef DEBUG_ENABLED
+ bool freed = false;
+ Object *base_obj = base->get_validated_object_with_check(freed);
+ if (freed) {
+ err_text = "Trying to call a function on a previously freed instance.";
+ OPCODE_BREAK;
+ } else if (!base_obj) {
+ err_text = "Trying to call a function on a null value.";
+ OPCODE_BREAK;
+ }
+#else
+ Object *base_obj = base->operator Object *();
+#endif
+ Variant **argptrs = 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;
+ if (call_ret) {
+ GET_INSTRUCTION_ARG(ret, argc + 1);
+ *ret = method->call(base_obj, (const Variant **)argptrs, argc, err);
+ } else {
+ method->call(base_obj, (const Variant **)argptrs, argc, err);
+ }
+
+#ifdef DEBUG_ENABLED
+ if (GDScriptLanguage::get_singleton()->profiling) {
+ function_call_time += OS::get_singleton()->get_ticks_usec() - call_time;
+ }
+
+ if (err.error != Callable::CallError::CALL_OK) {
+ String methodstr = method->get_name();
+ String basestr = _get_var_type(base);
+
+ if (methodstr == "call") {
+ if (argc >= 1) {
+ methodstr = String(*argptrs[0]) + " (via call)";
+ if (err.error == Callable::CallError::CALL_ERROR_INVALID_ARGUMENT) {
+ err.argument += 1;
+ }
+ }
+ } else if (methodstr == "free") {
+ if (err.error == Callable::CallError::CALL_ERROR_INVALID_METHOD) {
+ if (base->is_ref()) {
+ err_text = "Attempted to free a reference.";
+ OPCODE_BREAK;
+ } else if (base->get_type() == Variant::OBJECT) {
+ err_text = "Attempted to free a locked object (calling or emitting).";
+ OPCODE_BREAK;
+ }
+ }
+ }
+ err_text = _get_call_error(err, "function '" + methodstr + "' in base '" + basestr + "'", (const Variant **)argptrs);
+ OPCODE_BREAK;
+ }
+#endif
+ ip += 3;
+ }
+ DISPATCH_OPCODE;
+
+#ifdef DEBUG_ENABLED
+#define OPCODE_CALL_PTR(m_type) \
+ OPCODE(OPCODE_CALL_PTRCALL_##m_type) { \
+ CHECK_SPACE(3 + instr_arg_count); \
+ ip += instr_arg_count; \
+ int argc = _code_ptr[ip + 1]; \
+ GD_ERR_BREAK(argc < 0); \
+ GET_INSTRUCTION_ARG(base, argc); \
+ GD_ERR_BREAK(_code_ptr[ip + 2] < 0 || _code_ptr[ip + 2] >= _methods_count); \
+ MethodBind *method = _methods_ptr[_code_ptr[ip + 2]]; \
+ bool freed = false; \
+ Object *base_obj = base->get_validated_object_with_check(freed); \
+ if (freed) { \
+ err_text = "Trying to call a function on a previously freed instance."; \
+ OPCODE_BREAK; \
+ } else if (!base_obj) { \
+ err_text = "Trying to call a function on a null value."; \
+ OPCODE_BREAK; \
+ } \
+ const void **argptrs = call_args_ptr; \
+ for (int i = 0; i < argc; i++) { \
+ GET_INSTRUCTION_ARG(v, i); \
+ argptrs[i] = VariantInternal::get_opaque_pointer((const Variant *)v); \
+ } \
+ uint64_t call_time = 0; \
+ if (GDScriptLanguage::get_singleton()->profiling) { \
+ call_time = OS::get_singleton()->get_ticks_usec(); \
+ } \
+ GET_INSTRUCTION_ARG(ret, argc + 1); \
+ VariantInternal::initialize(ret, Variant::m_type); \
+ void *ret_opaque = VariantInternal::OP_GET_##m_type(ret); \
+ method->ptrcall(base_obj, argptrs, ret_opaque); \
+ if (GDScriptLanguage::get_singleton()->profiling) { \
+ function_call_time += OS::get_singleton()->get_ticks_usec() - call_time; \
+ } \
+ ip += 3; \
+ } \
+ DISPATCH_OPCODE
+#else
+#define OPCODE_CALL_PTR(m_type) \
+ OPCODE(OPCODE_CALL_PTRCALL_##m_type) { \
+ CHECK_SPACE(3 + instr_arg_count); \
+ int argc = _code_ptr[ip + 1]; \
+ GET_INSTRUCTION_ARG(base, argc); \
+ MethodBind *method = _methods_ptr[_code_ptr[ip + 2]]; \
+ Object *base_obj = *VariantInternal::get_object(base); \
+ const void **argptrs = call_args_ptr; \
+ for (int i = 0; i < argc; i++) { \
+ GET_INSTRUCTION_ARG(v, i); \
+ argptrs[i] = VariantInternal::get_opaque_pointer((const Variant *)v); \
+ } \
+ GET_INSTRUCTION_ARG(ret, argc + 1); \
+ VariantInternal::initialize(ret, Variant::m_type); \
+ void *ret_opaque = VariantInternal::OP_GET_##m_type(ret); \
+ method->ptrcall(base_obj, argptrs, ret_opaque); \
+ ip += 3; \
+ } \
+ DISPATCH_OPCODE
+#endif
+
+ OPCODE_CALL_PTR(BOOL);
+ OPCODE_CALL_PTR(INT);
+ OPCODE_CALL_PTR(FLOAT);
+ OPCODE_CALL_PTR(STRING);
+ OPCODE_CALL_PTR(VECTOR2);
+ OPCODE_CALL_PTR(VECTOR2I);
+ OPCODE_CALL_PTR(RECT2);
+ OPCODE_CALL_PTR(RECT2I);
+ OPCODE_CALL_PTR(VECTOR3);
+ OPCODE_CALL_PTR(VECTOR3I);
+ OPCODE_CALL_PTR(TRANSFORM2D);
+ OPCODE_CALL_PTR(PLANE);
+ OPCODE_CALL_PTR(QUAT);
+ OPCODE_CALL_PTR(AABB);
+ OPCODE_CALL_PTR(BASIS);
+ OPCODE_CALL_PTR(TRANSFORM);
+ OPCODE_CALL_PTR(COLOR);
+ OPCODE_CALL_PTR(STRING_NAME);
+ OPCODE_CALL_PTR(NODE_PATH);
+ OPCODE_CALL_PTR(RID);
+ OPCODE_CALL_PTR(CALLABLE);
+ OPCODE_CALL_PTR(SIGNAL);
+ OPCODE_CALL_PTR(DICTIONARY);
+ OPCODE_CALL_PTR(ARRAY);
+ OPCODE_CALL_PTR(PACKED_BYTE_ARRAY);
+ OPCODE_CALL_PTR(PACKED_INT32_ARRAY);
+ OPCODE_CALL_PTR(PACKED_INT64_ARRAY);
+ OPCODE_CALL_PTR(PACKED_FLOAT32_ARRAY);
+ OPCODE_CALL_PTR(PACKED_FLOAT64_ARRAY);
+ OPCODE_CALL_PTR(PACKED_STRING_ARRAY);
+ OPCODE_CALL_PTR(PACKED_VECTOR2_ARRAY);
+ OPCODE_CALL_PTR(PACKED_VECTOR3_ARRAY);
+ OPCODE_CALL_PTR(PACKED_COLOR_ARRAY);
+ OPCODE(OPCODE_CALL_PTRCALL_OBJECT) {
+ CHECK_SPACE(3 + instr_arg_count);
+
+ ip += instr_arg_count;
+
+ int argc = _code_ptr[ip + 1];
+ GD_ERR_BREAK(argc < 0);
+
+ GD_ERR_BREAK(_code_ptr[ip + 2] < 0 || _code_ptr[ip + 2] >= _methods_count);
+ MethodBind *method = _methods_ptr[_code_ptr[ip + 2]];
+
+ GET_INSTRUCTION_ARG(base, argc);
+#ifdef DEBUG_ENABLED
+ bool freed = false;
+ Object *base_obj = base->get_validated_object_with_check(freed);
+ if (freed) {
+ err_text = "Trying to call a function on a previously freed instance.";
+ OPCODE_BREAK;
+ } else if (!base_obj) {
+ err_text = "Trying to call a function on a null value.";
+ OPCODE_BREAK;
+ }
+#else
+ Object *base_obj = *VariantInternal::get_object(base);
+#endif
+
+ const void **argptrs = call_args_ptr;
+
+ for (int i = 0; i < argc; i++) {
+ GET_INSTRUCTION_ARG(v, i);
+ argptrs[i] = VariantInternal::get_opaque_pointer((const Variant *)v);
+ }
+#ifdef DEBUG_ENABLED
+ uint64_t call_time = 0;
+
+ if (GDScriptLanguage::get_singleton()->profiling) {
+ call_time = OS::get_singleton()->get_ticks_usec();
+ }
+#endif
+
+ GET_INSTRUCTION_ARG(ret, argc + 1);
+ VariantInternal::initialize(ret, Variant::OBJECT);
+ Object **ret_opaque = VariantInternal::get_object(ret);
+ method->ptrcall(base_obj, argptrs, ret_opaque);
+ VariantInternal::object_assign(ret, *ret_opaque); // Set so ID is correct too.
+
+#ifdef DEBUG_ENABLED
+ if (GDScriptLanguage::get_singleton()->profiling) {
+ function_call_time += OS::get_singleton()->get_ticks_usec() - call_time;
+ }
+#endif
+ ip += 3;
+ }
+ DISPATCH_OPCODE;
+ OPCODE(OPCODE_CALL_PTRCALL_NO_RETURN) {
+ CHECK_SPACE(3 + instr_arg_count);
+
+ ip += instr_arg_count;
+
+ int argc = _code_ptr[ip + 1];
+ GD_ERR_BREAK(argc < 0);
+
+ GD_ERR_BREAK(_code_ptr[ip + 2] < 0 || _code_ptr[ip + 2] >= _methods_count);
+ MethodBind *method = _methods_ptr[_code_ptr[ip + 2]];
+
+ GET_INSTRUCTION_ARG(base, argc);
+#ifdef DEBUG_ENABLED
+ bool freed = false;
+ Object *base_obj = base->get_validated_object_with_check(freed);
+ if (freed) {
+ err_text = "Trying to call a function on a previously freed instance.";
+ OPCODE_BREAK;
+ } else if (!base_obj) {
+ err_text = "Trying to call a function on a null value.";
+ OPCODE_BREAK;
+ }
+#else
+ Object *base_obj = *VariantInternal::get_object(base);
+#endif
+ const void **argptrs = call_args_ptr;
+
+ for (int i = 0; i < argc; i++) {
+ GET_INSTRUCTION_ARG(v, i);
+ argptrs[i] = VariantInternal::get_opaque_pointer((const Variant *)v);
+ }
+#ifdef DEBUG_ENABLED
+ uint64_t call_time = 0;
+
+ if (GDScriptLanguage::get_singleton()->profiling) {
+ call_time = OS::get_singleton()->get_ticks_usec();
+ }
+#endif
+
+ GET_INSTRUCTION_ARG(ret, argc + 1);
+ VariantInternal::initialize(ret, Variant::NIL);
+ method->ptrcall(base_obj, argptrs, nullptr);
+
+#ifdef DEBUG_ENABLED
+ if (GDScriptLanguage::get_singleton()->profiling) {
+ function_call_time += OS::get_singleton()->get_ticks_usec() - call_time;
+ }
+#endif
+ ip += 3;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_CALL_BUILTIN_TYPE_VALIDATED) {
+ CHECK_SPACE(3 + instr_arg_count);
+
+ ip += instr_arg_count;
+
+ int argc = _code_ptr[ip + 1];
+ GD_ERR_BREAK(argc < 0);
+
+ GET_INSTRUCTION_ARG(base, argc);
+
+ GD_ERR_BREAK(_code_ptr[ip + 2] < 0 || _code_ptr[ip + 2] >= _builtin_methods_count);
+ Variant::ValidatedBuiltInMethod method = _builtin_methods_ptr[_code_ptr[ip + 2]];
+ Variant **argptrs = instruction_args;
+
+#ifdef DEBUG_ENABLED
+ uint64_t call_time = 0;
+ if (GDScriptLanguage::get_singleton()->profiling) {
+ call_time = OS::get_singleton()->get_ticks_usec();
+ }
+#endif
+
+ GET_INSTRUCTION_ARG(ret, argc + 1);
+ method(base, (const Variant **)argptrs, argc, ret);
+
+#ifdef DEBUG_ENABLED
+ if (GDScriptLanguage::get_singleton()->profiling) {
+ function_call_time += OS::get_singleton()->get_ticks_usec() - call_time;
+ }
+#endif
+
+ ip += 3;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_CALL_UTILITY) {
+ CHECK_SPACE(3 + instr_arg_count);
+
+ ip += instr_arg_count;
+
+ int argc = _code_ptr[ip + 1];
+ GD_ERR_BREAK(argc < 0);
+
+ GD_ERR_BREAK(_code_ptr[ip + 2] < 0 || _code_ptr[ip + 2] >= _global_names_count);
+ StringName function = _global_names_ptr[_code_ptr[ip + 2]];
+
+ Variant **argptrs = instruction_args;
+
+ GET_INSTRUCTION_ARG(dst, argc);
+
+ Callable::CallError err;
+ Variant::call_utility_function(function, dst, (const Variant **)argptrs, argc, err);
+
+#ifdef DEBUG_ENABLED
+ if (err.error != Callable::CallError::CALL_OK) {
+ String methodstr = function;
+ if (dst->get_type() == Variant::STRING) {
+ // Call provided error string.
+ err_text = "Error calling utility function '" + methodstr + "': " + String(*dst);
+ } else {
+ err_text = _get_call_error(err, "utility function '" + methodstr + "'", (const Variant **)argptrs);
+ }
+ OPCODE_BREAK;
+ }
+#endif
+ ip += 3;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_CALL_UTILITY_VALIDATED) {
+ CHECK_SPACE(3 + instr_arg_count);
+
+ ip += instr_arg_count;
+
+ int argc = _code_ptr[ip + 1];
+ GD_ERR_BREAK(argc < 0);
+
+ GD_ERR_BREAK(_code_ptr[ip + 2] < 0 || _code_ptr[ip + 2] >= _utilities_count);
+ Variant::ValidatedUtilityFunction function = _utilities_ptr[_code_ptr[ip + 2]];
+
+ Variant **argptrs = instruction_args;
+
+ GET_INSTRUCTION_ARG(dst, argc);
+
+ function(dst, (const Variant **)argptrs, argc);
+
+ ip += 3;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_CALL_GDSCRIPT_UTILITY) {
+ CHECK_SPACE(3 + instr_arg_count);
+
+ ip += instr_arg_count;
+
+ int argc = _code_ptr[ip + 1];
+ GD_ERR_BREAK(argc < 0);
+
+ GD_ERR_BREAK(_code_ptr[ip + 2] < 0 || _code_ptr[ip + 2] >= _gds_utilities_count);
+ GDScriptUtilityFunctions::FunctionPtr function = _gds_utilities_ptr[_code_ptr[ip + 2]];
+
+ Variant **argptrs = instruction_args;
+
+ GET_INSTRUCTION_ARG(dst, argc);
+
+ Callable::CallError err;
+ function(dst, (const Variant **)argptrs, argc, err);
+
+#ifdef DEBUG_ENABLED
+ if (err.error != Callable::CallError::CALL_OK) {
+ // TODO: Add this information in debug.
+ String methodstr = "<unkown function>";
+ if (dst->get_type() == Variant::STRING) {
+ // Call provided error string.
+ err_text = "Error calling GDScript utility function '" + methodstr + "': " + String(*dst);
+ } else {
+ err_text = _get_call_error(err, "GDScript utility function '" + methodstr + "'", (const Variant **)argptrs);
+ }
+ OPCODE_BREAK;
+ }
+#endif
+ ip += 3;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_CALL_SELF_BASE) {
+ CHECK_SPACE(3 + instr_arg_count);
+
+ ip += instr_arg_count;
+
+ int self_fun = _code_ptr[ip + 1];
+#ifdef DEBUG_ENABLED
+ if (self_fun < 0 || self_fun >= _global_names_count) {
+ err_text = "compiler bug, function name not found";
+ OPCODE_BREAK;
+ }
+#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);
+
+ const GDScript *gds = _script;
+
+ const Map<StringName, GDScriptFunction *>::Element *E = nullptr;
+ while (gds->base.ptr()) {
+ gds = gds->base.ptr();
+ E = gds->member_functions.find(*methodname);
+ if (E) {
+ break;
+ }
+ }
+
+ Callable::CallError err;
+
+ if (E) {
+ *dst = E->get()->call(p_instance, (const Variant **)argptrs, argc, err);
+ } else if (gds->native.ptr()) {
+ if (*methodname != GDScriptLanguage::get_singleton()->strings._init) {
+ MethodBind *mb = ClassDB::get_method(gds->native->get_name(), *methodname);
+ if (!mb) {
+ err.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
+ } else {
+ *dst = mb->call(p_instance->owner, (const Variant **)argptrs, argc, err);
+ }
+ } else {
+ err.error = Callable::CallError::CALL_OK;
+ }
+ } else {
+ if (*methodname != GDScriptLanguage::get_singleton()->strings._init) {
+ err.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
+ } else {
+ err.error = Callable::CallError::CALL_OK;
+ }
+ }
+
+ if (err.error != Callable::CallError::CALL_OK) {
+ String methodstr = *methodname;
+ err_text = _get_call_error(err, "function '" + methodstr + "'", (const Variant **)argptrs);
+
+ OPCODE_BREAK;
+ }
+
+ ip += 3;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_AWAIT) {
+ CHECK_SPACE(2);
+
+ // Do the oneshot connect.
+ GET_INSTRUCTION_ARG(argobj, 0);
+
+ Signal sig;
+ bool is_signal = true;
+
+ {
+ Variant result = *argobj;
+
+ if (argobj->get_type() == Variant::OBJECT) {
+ bool was_freed = false;
+ Object *obj = argobj->get_validated_object_with_check(was_freed);
+
+ if (was_freed) {
+ err_text = "Trying to await on a freed object.";
+ OPCODE_BREAK;
+ }
+
+ // Is this even possible to be null at this point?
+ if (obj) {
+ if (obj->is_class_ptr(GDScriptFunctionState::get_class_ptr_static())) {
+ static StringName completed = _scs_create("completed");
+ result = Signal(obj, completed);
+ }
+ }
+ }
+
+ if (result.get_type() != Variant::SIGNAL) {
+ ip += 4; // Skip OPCODE_AWAIT_RESUME and its data.
+ // The stack pointer should be the same, so we don't need to set a return value.
+ is_signal = false;
+ } else {
+ sig = result;
+ }
+ }
+
+ if (is_signal) {
+ Ref<GDScriptFunctionState> gdfs = memnew(GDScriptFunctionState);
+ gdfs->function = this;
+
+ gdfs->state.stack.resize(alloca_size);
+ //copy variant stack
+ for (int i = 0; i < _stack_size; i++) {
+ memnew_placement(&gdfs->state.stack.write[sizeof(Variant) * i], Variant(stack[i]));
+ }
+ gdfs->state.stack_size = _stack_size;
+ gdfs->state.self = self;
+ gdfs->state.alloca_size = alloca_size;
+ gdfs->state.ip = ip + 2;
+ gdfs->state.line = line;
+ gdfs->state.script = _script;
+ {
+ MutexLock lock(GDScriptLanguage::get_singleton()->lock);
+ _script->pending_func_states.add(&gdfs->scripts_list);
+ if (p_instance) {
+ gdfs->state.instance = p_instance;
+ p_instance->pending_func_states.add(&gdfs->instances_list);
+ } else {
+ gdfs->state.instance = nullptr;
+ }
+ }
+#ifdef DEBUG_ENABLED
+ gdfs->state.function_name = name;
+ gdfs->state.script_path = _script->get_path();
+#endif
+ gdfs->state.defarg = defarg;
+ gdfs->function = this;
+
+ retvalue = gdfs;
+
+ Error err = sig.connect(Callable(gdfs.ptr(), "_signal_callback"), varray(gdfs), Object::CONNECT_ONESHOT);
+ if (err != OK) {
+ err_text = "Error connecting to signal: " + sig.get_name() + " during await.";
+ OPCODE_BREAK;
+ }
+
+#ifdef DEBUG_ENABLED
+ exit_ok = true;
+ awaited = true;
+#endif
+ OPCODE_BREAK;
+ }
+ }
+ DISPATCH_OPCODE; // Needed for synchronous calls (when result is immediately available).
+
+ OPCODE(OPCODE_AWAIT_RESUME) {
+ CHECK_SPACE(2);
+#ifdef DEBUG_ENABLED
+ if (!p_state) {
+ err_text = ("Invalid Resume (bug?)");
+ OPCODE_BREAK;
+ }
+#endif
+ GET_INSTRUCTION_ARG(result, 0);
+ *result = p_state->result;
+ ip += 2;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_JUMP) {
+ CHECK_SPACE(2);
+ int to = _code_ptr[ip + 1];
+
+ GD_ERR_BREAK(to < 0 || to > _code_size);
+ ip = to;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_JUMP_IF) {
+ CHECK_SPACE(3);
+
+ GET_INSTRUCTION_ARG(test, 0);
+
+ bool result = test->booleanize();
+
+ if (result) {
+ int to = _code_ptr[ip + 2];
+ GD_ERR_BREAK(to < 0 || to > _code_size);
+ ip = to;
+ } else {
+ ip += 3;
+ }
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_JUMP_IF_NOT) {
+ CHECK_SPACE(3);
+
+ GET_INSTRUCTION_ARG(test, 0);
+
+ bool result = test->booleanize();
+
+ if (!result) {
+ int to = _code_ptr[ip + 2];
+ GD_ERR_BREAK(to < 0 || to > _code_size);
+ ip = to;
+ } else {
+ ip += 3;
+ }
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_JUMP_TO_DEF_ARGUMENT) {
+ CHECK_SPACE(2);
+ ip = _default_arg_ptr[defarg];
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_RETURN) {
+ CHECK_SPACE(2);
+ GET_INSTRUCTION_ARG(r, 0);
+ retvalue = *r;
+#ifdef DEBUG_ENABLED
+ exit_ok = true;
+#endif
+ OPCODE_BREAK;
+ }
+
+ OPCODE(OPCODE_ITERATE_BEGIN) {
+ CHECK_SPACE(8); // Space for this and a regular iterate.
+
+ GET_INSTRUCTION_ARG(counter, 0);
+ GET_INSTRUCTION_ARG(container, 1);
+
+ bool valid;
+ if (!container->iter_init(*counter, valid)) {
+#ifdef DEBUG_ENABLED
+ if (!valid) {
+ err_text = "Unable to iterate on object of type '" + Variant::get_type_name(container->get_type()) + "'.";
+ OPCODE_BREAK;
+ }
+#endif
+ int jumpto = _code_ptr[ip + 4];
+ GD_ERR_BREAK(jumpto < 0 || jumpto > _code_size);
+ ip = jumpto;
+ } else {
+ GET_INSTRUCTION_ARG(iterator, 2);
+
+ *iterator = container->iter_get(*counter, valid);
+#ifdef DEBUG_ENABLED
+ if (!valid) {
+ err_text = "Unable to obtain iterator object of type '" + Variant::get_type_name(container->get_type()) + "'.";
+ OPCODE_BREAK;
+ }
+#endif
+ ip += 5; // Skip regular iterate which is always next.
+ }
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_ITERATE_BEGIN_INT) {
+ CHECK_SPACE(8); // Check space for iterate instruction too.
+
+ GET_INSTRUCTION_ARG(counter, 0);
+ GET_INSTRUCTION_ARG(container, 1);
+
+ int64_t size = *VariantInternal::get_int(container);
+
+ VariantInternal::initialize(counter, Variant::INT);
+ *VariantInternal::get_int(counter) = 0;
+
+ if (size > 0) {
+ GET_INSTRUCTION_ARG(iterator, 2);
+ VariantInternal::initialize(iterator, Variant::INT);
+ *VariantInternal::get_int(iterator) = 0;
+
+ // Skip regular iterate.
+ ip += 5;
+ } else {
+ // Jump to end of loop.
+ int jumpto = _code_ptr[ip + 4];
+ GD_ERR_BREAK(jumpto < 0 || jumpto > _code_size);
+ ip = jumpto;
+ }
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_ITERATE_BEGIN_FLOAT) {
+ CHECK_SPACE(8); // Check space for iterate instruction too.
+
+ GET_INSTRUCTION_ARG(counter, 0);
+ GET_INSTRUCTION_ARG(container, 1);
+
+ double size = *VariantInternal::get_float(container);
+
+ VariantInternal::initialize(counter, Variant::FLOAT);
+ *VariantInternal::get_float(counter) = 0.0;
+
+ if (size > 0) {
+ GET_INSTRUCTION_ARG(iterator, 2);
+ VariantInternal::initialize(iterator, Variant::FLOAT);
+ *VariantInternal::get_float(iterator) = 0;
+
+ // Skip regular iterate.
+ ip += 5;
+ } else {
+ // Jump to end of loop.
+ int jumpto = _code_ptr[ip + 4];
+ GD_ERR_BREAK(jumpto < 0 || jumpto > _code_size);
+ ip = jumpto;
+ }
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_ITERATE_BEGIN_VECTOR2) {
+ CHECK_SPACE(8); // Check space for iterate instruction too.
+
+ GET_INSTRUCTION_ARG(counter, 0);
+ GET_INSTRUCTION_ARG(container, 1);
+
+ Vector2 *bounds = VariantInternal::get_vector2(container);
+
+ VariantInternal::initialize(counter, Variant::FLOAT);
+ *VariantInternal::get_float(counter) = bounds->x;
+
+ if (bounds->x < bounds->y) {
+ GET_INSTRUCTION_ARG(iterator, 2);
+ VariantInternal::initialize(iterator, Variant::FLOAT);
+ *VariantInternal::get_float(iterator) = bounds->x;
+
+ // Skip regular iterate.
+ ip += 5;
+ } else {
+ // Jump to end of loop.
+ int jumpto = _code_ptr[ip + 4];
+ GD_ERR_BREAK(jumpto < 0 || jumpto > _code_size);
+ ip = jumpto;
+ }
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_ITERATE_BEGIN_VECTOR2I) {
+ CHECK_SPACE(8); // Check space for iterate instruction too.
+
+ GET_INSTRUCTION_ARG(counter, 0);
+ GET_INSTRUCTION_ARG(container, 1);
+
+ Vector2i *bounds = VariantInternal::get_vector2i(container);
+
+ VariantInternal::initialize(counter, Variant::FLOAT);
+ *VariantInternal::get_int(counter) = bounds->x;
+
+ if (bounds->x < bounds->y) {
+ GET_INSTRUCTION_ARG(iterator, 2);
+ VariantInternal::initialize(iterator, Variant::INT);
+ *VariantInternal::get_int(iterator) = bounds->x;
+
+ // Skip regular iterate.
+ ip += 5;
+ } else {
+ // Jump to end of loop.
+ int jumpto = _code_ptr[ip + 4];
+ GD_ERR_BREAK(jumpto < 0 || jumpto > _code_size);
+ ip = jumpto;
+ }
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_ITERATE_BEGIN_VECTOR3) {
+ CHECK_SPACE(8); // Check space for iterate instruction too.
+
+ GET_INSTRUCTION_ARG(counter, 0);
+ GET_INSTRUCTION_ARG(container, 1);
+
+ Vector3 *bounds = VariantInternal::get_vector3(container);
+ double from = bounds->x;
+ double to = bounds->y;
+ double step = bounds->z;
+
+ VariantInternal::initialize(counter, Variant::FLOAT);
+ *VariantInternal::get_float(counter) = from;
+
+ bool do_continue = from == to ? false : (from < to ? step > 0 : step < 0);
+
+ if (do_continue) {
+ GET_INSTRUCTION_ARG(iterator, 2);
+ VariantInternal::initialize(iterator, Variant::FLOAT);
+ *VariantInternal::get_float(iterator) = from;
+
+ // Skip regular iterate.
+ ip += 5;
+ } else {
+ // Jump to end of loop.
+ int jumpto = _code_ptr[ip + 4];
+ GD_ERR_BREAK(jumpto < 0 || jumpto > _code_size);
+ ip = jumpto;
+ }
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_ITERATE_BEGIN_VECTOR3I) {
+ CHECK_SPACE(8); // Check space for iterate instruction too.
+
+ GET_INSTRUCTION_ARG(counter, 0);
+ GET_INSTRUCTION_ARG(container, 1);
+
+ Vector3i *bounds = VariantInternal::get_vector3i(container);
+ int64_t from = bounds->x;
+ int64_t to = bounds->y;
+ int64_t step = bounds->z;
+
+ VariantInternal::initialize(counter, Variant::INT);
+ *VariantInternal::get_int(counter) = from;
+
+ bool do_continue = from == to ? false : (from < to ? step > 0 : step < 0);
+
+ if (do_continue) {
+ GET_INSTRUCTION_ARG(iterator, 2);
+ VariantInternal::initialize(iterator, Variant::INT);
+ *VariantInternal::get_int(iterator) = from;
+
+ // Skip regular iterate.
+ ip += 5;
+ } else {
+ // Jump to end of loop.
+ int jumpto = _code_ptr[ip + 4];
+ GD_ERR_BREAK(jumpto < 0 || jumpto > _code_size);
+ ip = jumpto;
+ }
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_ITERATE_BEGIN_STRING) {
+ CHECK_SPACE(8); // Check space for iterate instruction too.
+
+ GET_INSTRUCTION_ARG(counter, 0);
+ GET_INSTRUCTION_ARG(container, 1);
+
+ String *str = VariantInternal::get_string(container);
+
+ VariantInternal::initialize(counter, Variant::INT);
+ *VariantInternal::get_int(counter) = 0;
+
+ if (!str->is_empty()) {
+ GET_INSTRUCTION_ARG(iterator, 2);
+ VariantInternal::initialize(iterator, Variant::STRING);
+ *VariantInternal::get_string(iterator) = str->substr(0, 1);
+
+ // Skip regular iterate.
+ ip += 5;
+ } else {
+ // Jump to end of loop.
+ int jumpto = _code_ptr[ip + 4];
+ GD_ERR_BREAK(jumpto < 0 || jumpto > _code_size);
+ ip = jumpto;
+ }
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_ITERATE_BEGIN_DICTIONARY) {
+ CHECK_SPACE(8); // Check space for iterate instruction too.
+
+ GET_INSTRUCTION_ARG(counter, 0);
+ GET_INSTRUCTION_ARG(container, 1);
+
+ Dictionary *dict = VariantInternal::get_dictionary(container);
+ const Variant *next = dict->next(nullptr);
+
+ if (!dict->is_empty()) {
+ GET_INSTRUCTION_ARG(iterator, 2);
+ *counter = *next;
+ *iterator = *next;
+
+ // Skip regular iterate.
+ ip += 5;
+ } else {
+ // Jump to end of loop.
+ int jumpto = _code_ptr[ip + 4];
+ GD_ERR_BREAK(jumpto < 0 || jumpto > _code_size);
+ ip = jumpto;
+ }
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_ITERATE_BEGIN_ARRAY) {
+ CHECK_SPACE(8); // Check space for iterate instruction too.
+
+ GET_INSTRUCTION_ARG(counter, 0);
+ GET_INSTRUCTION_ARG(container, 1);
+
+ Array *array = VariantInternal::get_array(container);
+
+ VariantInternal::initialize(counter, Variant::INT);
+ *VariantInternal::get_int(counter) = 0;
+
+ if (!array->is_empty()) {
+ GET_INSTRUCTION_ARG(iterator, 2);
+ *iterator = array->get(0);
+
+ // Skip regular iterate.
+ ip += 5;
+ } else {
+ // Jump to end of loop.
+ int jumpto = _code_ptr[ip + 4];
+ GD_ERR_BREAK(jumpto < 0 || jumpto > _code_size);
+ ip = jumpto;
+ }
+ }
+ DISPATCH_OPCODE;
+
+#define OPCODE_ITERATE_BEGIN_PACKED_ARRAY(m_var_type, m_elem_type, m_get_func, m_var_ret_type, m_ret_type, m_ret_get_func) \
+ OPCODE(OPCODE_ITERATE_BEGIN_PACKED_##m_var_type##_ARRAY) { \
+ CHECK_SPACE(8); \
+ GET_INSTRUCTION_ARG(counter, 0); \
+ GET_INSTRUCTION_ARG(container, 1); \
+ Vector<m_elem_type> *array = VariantInternal::m_get_func(container); \
+ VariantInternal::initialize(counter, Variant::INT); \
+ *VariantInternal::get_int(counter) = 0; \
+ if (!array->is_empty()) { \
+ GET_INSTRUCTION_ARG(iterator, 2); \
+ VariantInternal::initialize(iterator, Variant::m_var_ret_type); \
+ m_ret_type *it = VariantInternal::m_ret_get_func(iterator); \
+ *it = array->get(0); \
+ ip += 5; \
+ } else { \
+ int jumpto = _code_ptr[ip + 4]; \
+ GD_ERR_BREAK(jumpto<0 || jumpto> _code_size); \
+ ip = jumpto; \
+ } \
+ } \
+ DISPATCH_OPCODE
+
+ OPCODE_ITERATE_BEGIN_PACKED_ARRAY(BYTE, uint8_t, get_byte_array, INT, int64_t, get_int);
+ OPCODE_ITERATE_BEGIN_PACKED_ARRAY(INT32, int32_t, get_int32_array, INT, int64_t, get_int);
+ OPCODE_ITERATE_BEGIN_PACKED_ARRAY(INT64, int64_t, get_int64_array, INT, int64_t, get_int);
+ OPCODE_ITERATE_BEGIN_PACKED_ARRAY(FLOAT32, float, get_float32_array, FLOAT, double, get_float);
+ OPCODE_ITERATE_BEGIN_PACKED_ARRAY(FLOAT64, double, get_float64_array, FLOAT, double, get_float);
+ OPCODE_ITERATE_BEGIN_PACKED_ARRAY(STRING, String, get_string_array, STRING, String, get_string);
+ OPCODE_ITERATE_BEGIN_PACKED_ARRAY(VECTOR2, Vector2, get_vector2_array, VECTOR2, Vector2, get_vector2);
+ OPCODE_ITERATE_BEGIN_PACKED_ARRAY(VECTOR3, Vector3, get_vector3_array, VECTOR3, Vector3, get_vector3);
+ OPCODE_ITERATE_BEGIN_PACKED_ARRAY(COLOR, Color, get_color_array, COLOR, Color, get_color);
+
+ OPCODE(OPCODE_ITERATE_BEGIN_OBJECT) {
+ CHECK_SPACE(4);
+
+ GET_INSTRUCTION_ARG(counter, 0);
+ GET_INSTRUCTION_ARG(container, 1);
+
+#ifdef DEBUG_ENABLED
+ bool freed = false;
+ Object *obj = container->get_validated_object_with_check(freed);
+ if (freed) {
+ err_text = "Trying to iterate on a previously freed object.";
+ OPCODE_BREAK;
+ } else if (!obj) {
+ err_text = "Trying to iterate on a null value.";
+ OPCODE_BREAK;
+ }
+#else
+ Object *obj = *VariantInternal::get_object(container);
+#endif
+ Array ref;
+ ref.push_back(*counter);
+ Variant vref;
+ VariantInternal::initialize(&vref, Variant::ARRAY);
+ *VariantInternal::get_array(&vref) = ref;
+
+ Variant **args = instruction_args; // Overriding an instruction argument, but we don't need access to that anymore.
+ args[0] = &vref;
+
+ Callable::CallError ce;
+ Variant has_next = obj->call(CoreStringNames::get_singleton()->_iter_init, (const Variant **)args, 1, ce);
+
+#ifdef DEBUG_ENABLED
+ if (ce.error != Callable::CallError::CALL_OK) {
+ err_text = vformat(R"(There was an error calling "_iter_next" on iterator object of type %s.)", *container);
+ OPCODE_BREAK;
+ }
+#endif
+ if (!has_next.booleanize()) {
+ int jumpto = _code_ptr[ip + 4];
+ GD_ERR_BREAK(jumpto < 0 || jumpto > _code_size);
+ ip = jumpto;
+ } else {
+ GET_INSTRUCTION_ARG(iterator, 2);
+ *iterator = obj->call(CoreStringNames::get_singleton()->_iter_get, (const Variant **)args, 1, ce);
+#ifdef DEBUG_ENABLED
+ if (ce.error != Callable::CallError::CALL_OK) {
+ err_text = vformat(R"(There was an error calling "_iter_get" on iterator object of type %s.)", *container);
+ OPCODE_BREAK;
+ }
+#endif
+
+ ip += 5; // Loop again.
+ }
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_ITERATE) {
+ CHECK_SPACE(4);
+
+ GET_INSTRUCTION_ARG(counter, 0);
+ GET_INSTRUCTION_ARG(container, 1);
+
+ bool valid;
+ if (!container->iter_next(*counter, valid)) {
+#ifdef DEBUG_ENABLED
+ if (!valid) {
+ err_text = "Unable to iterate on object of type '" + Variant::get_type_name(container->get_type()) + "' (type changed since first iteration?).";
+ OPCODE_BREAK;
+ }
+#endif
+ int jumpto = _code_ptr[ip + 4];
+ GD_ERR_BREAK(jumpto < 0 || jumpto > _code_size);
+ ip = jumpto;
+ } else {
+ GET_INSTRUCTION_ARG(iterator, 2);
+
+ *iterator = container->iter_get(*counter, valid);
+#ifdef DEBUG_ENABLED
+ if (!valid) {
+ err_text = "Unable to obtain iterator object of type '" + Variant::get_type_name(container->get_type()) + "' (but was obtained on first iteration?).";
+ OPCODE_BREAK;
+ }
+#endif
+ ip += 5; //loop again
+ }
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_ITERATE_INT) {
+ CHECK_SPACE(4);
+
+ GET_INSTRUCTION_ARG(counter, 0);
+ GET_INSTRUCTION_ARG(container, 1);
+
+ int64_t size = *VariantInternal::get_int(container);
+ int64_t *count = VariantInternal::get_int(counter);
+
+ (*count)++;
+
+ if (*count >= size) {
+ int jumpto = _code_ptr[ip + 4];
+ GD_ERR_BREAK(jumpto < 0 || jumpto > _code_size);
+ ip = jumpto;
+ } else {
+ GET_INSTRUCTION_ARG(iterator, 2);
+ *VariantInternal::get_int(iterator) = *count;
+
+ ip += 5; // Loop again.
+ }
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_ITERATE_FLOAT) {
+ CHECK_SPACE(4);
+
+ GET_INSTRUCTION_ARG(counter, 0);
+ GET_INSTRUCTION_ARG(container, 1);
+
+ double size = *VariantInternal::get_float(container);
+ double *count = VariantInternal::get_float(counter);
+
+ (*count)++;
+
+ if (*count >= size) {
+ int jumpto = _code_ptr[ip + 4];
+ GD_ERR_BREAK(jumpto < 0 || jumpto > _code_size);
+ ip = jumpto;
+ } else {
+ GET_INSTRUCTION_ARG(iterator, 2);
+ *VariantInternal::get_float(iterator) = *count;
+
+ ip += 5; // Loop again.
+ }
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_ITERATE_VECTOR2) {
+ CHECK_SPACE(4);
+
+ GET_INSTRUCTION_ARG(counter, 0);
+ GET_INSTRUCTION_ARG(container, 1);
+
+ const Vector2 *bounds = VariantInternal::get_vector2((const Variant *)container);
+ double *count = VariantInternal::get_float(counter);
+
+ (*count)++;
+
+ if (*count >= bounds->y) {
+ int jumpto = _code_ptr[ip + 4];
+ GD_ERR_BREAK(jumpto < 0 || jumpto > _code_size);
+ ip = jumpto;
+ } else {
+ GET_INSTRUCTION_ARG(iterator, 2);
+ *VariantInternal::get_float(iterator) = *count;
+
+ ip += 5; // Loop again.
+ }
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_ITERATE_VECTOR2I) {
+ CHECK_SPACE(4);
+
+ GET_INSTRUCTION_ARG(counter, 0);
+ GET_INSTRUCTION_ARG(container, 1);
+
+ const Vector2i *bounds = VariantInternal::get_vector2i((const Variant *)container);
+ int64_t *count = VariantInternal::get_int(counter);
+
+ (*count)++;
+
+ if (*count >= bounds->y) {
+ int jumpto = _code_ptr[ip + 4];
+ GD_ERR_BREAK(jumpto < 0 || jumpto > _code_size);
+ ip = jumpto;
+ } else {
+ GET_INSTRUCTION_ARG(iterator, 2);
+ *VariantInternal::get_int(iterator) = *count;
+
+ ip += 5; // Loop again.
+ }
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_ITERATE_VECTOR3) {
+ CHECK_SPACE(4);
+
+ GET_INSTRUCTION_ARG(counter, 0);
+ GET_INSTRUCTION_ARG(container, 1);
+
+ const Vector3 *bounds = VariantInternal::get_vector3((const Variant *)container);
+ double *count = VariantInternal::get_float(counter);
+
+ *count += bounds->z;
+
+ if ((bounds->z < 0 && *count <= bounds->y) || (bounds->z > 0 && *count >= bounds->y)) {
+ int jumpto = _code_ptr[ip + 4];
+ GD_ERR_BREAK(jumpto < 0 || jumpto > _code_size);
+ ip = jumpto;
+ } else {
+ GET_INSTRUCTION_ARG(iterator, 2);
+ *VariantInternal::get_float(iterator) = *count;
+
+ ip += 5; // Loop again.
+ }
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_ITERATE_VECTOR3I) {
+ CHECK_SPACE(4);
+
+ GET_INSTRUCTION_ARG(counter, 0);
+ GET_INSTRUCTION_ARG(container, 1);
+
+ const Vector3i *bounds = VariantInternal::get_vector3i((const Variant *)container);
+ int64_t *count = VariantInternal::get_int(counter);
+
+ *count += bounds->z;
+
+ if ((bounds->z < 0 && *count <= bounds->y) || (bounds->z > 0 && *count >= bounds->y)) {
+ int jumpto = _code_ptr[ip + 4];
+ GD_ERR_BREAK(jumpto < 0 || jumpto > _code_size);
+ ip = jumpto;
+ } else {
+ GET_INSTRUCTION_ARG(iterator, 2);
+ *VariantInternal::get_int(iterator) = *count;
+
+ ip += 5; // Loop again.
+ }
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_ITERATE_STRING) {
+ CHECK_SPACE(4);
+
+ GET_INSTRUCTION_ARG(counter, 0);
+ GET_INSTRUCTION_ARG(container, 1);
+
+ const String *str = VariantInternal::get_string((const Variant *)container);
+ int64_t *idx = VariantInternal::get_int(counter);
+ (*idx)++;
+
+ if (*idx >= str->length()) {
+ int jumpto = _code_ptr[ip + 4];
+ GD_ERR_BREAK(jumpto < 0 || jumpto > _code_size);
+ ip = jumpto;
+ } else {
+ GET_INSTRUCTION_ARG(iterator, 2);
+ *VariantInternal::get_string(iterator) = str->substr(*idx, 1);
+
+ ip += 5; // Loop again.
+ }
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_ITERATE_DICTIONARY) {
+ CHECK_SPACE(4);
+
+ GET_INSTRUCTION_ARG(counter, 0);
+ GET_INSTRUCTION_ARG(container, 1);
+
+ const Dictionary *dict = VariantInternal::get_dictionary((const Variant *)container);
+ const Variant *next = dict->next(counter);
+
+ if (!next) {
+ int jumpto = _code_ptr[ip + 4];
+ GD_ERR_BREAK(jumpto < 0 || jumpto > _code_size);
+ ip = jumpto;
+ } else {
+ GET_INSTRUCTION_ARG(iterator, 2);
+ *counter = *next;
+ *iterator = *next;
+
+ ip += 5; // Loop again.
+ }
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_ITERATE_ARRAY) {
+ CHECK_SPACE(4);
+
+ GET_INSTRUCTION_ARG(counter, 0);
+ GET_INSTRUCTION_ARG(container, 1);
+
+ const Array *array = VariantInternal::get_array((const Variant *)container);
+ int64_t *idx = VariantInternal::get_int(counter);
+ (*idx)++;
+
+ if (*idx >= array->size()) {
+ int jumpto = _code_ptr[ip + 4];
+ GD_ERR_BREAK(jumpto < 0 || jumpto > _code_size);
+ ip = jumpto;
+ } else {
+ GET_INSTRUCTION_ARG(iterator, 2);
+ *iterator = array->get(*idx);
+
+ ip += 5; // Loop again.
+ }
+ }
+ DISPATCH_OPCODE;
+
+#define OPCODE_ITERATE_PACKED_ARRAY(m_var_type, m_elem_type, m_get_func, m_ret_get_func) \
+ OPCODE(OPCODE_ITERATE_PACKED_##m_var_type##_ARRAY) { \
+ CHECK_SPACE(4); \
+ GET_INSTRUCTION_ARG(counter, 0); \
+ GET_INSTRUCTION_ARG(container, 1); \
+ const Vector<m_elem_type> *array = VariantInternal::m_get_func((const Variant *)container); \
+ int64_t *idx = VariantInternal::get_int(counter); \
+ (*idx)++; \
+ if (*idx >= array->size()) { \
+ int jumpto = _code_ptr[ip + 4]; \
+ GD_ERR_BREAK(jumpto<0 || jumpto> _code_size); \
+ ip = jumpto; \
+ } else { \
+ GET_INSTRUCTION_ARG(iterator, 2); \
+ *VariantInternal::m_ret_get_func(iterator) = array->get(*idx); \
+ ip += 5; \
+ } \
+ } \
+ DISPATCH_OPCODE
+
+ OPCODE_ITERATE_PACKED_ARRAY(BYTE, uint8_t, get_byte_array, get_int);
+ OPCODE_ITERATE_PACKED_ARRAY(INT32, int32_t, get_int32_array, get_int);
+ OPCODE_ITERATE_PACKED_ARRAY(INT64, int64_t, get_int64_array, get_int);
+ OPCODE_ITERATE_PACKED_ARRAY(FLOAT32, float, get_float32_array, get_float);
+ OPCODE_ITERATE_PACKED_ARRAY(FLOAT64, double, get_float64_array, get_float);
+ OPCODE_ITERATE_PACKED_ARRAY(STRING, String, get_string_array, get_string);
+ OPCODE_ITERATE_PACKED_ARRAY(VECTOR2, Vector2, get_vector2_array, get_vector2);
+ OPCODE_ITERATE_PACKED_ARRAY(VECTOR3, Vector3, get_vector3_array, get_vector3);
+ OPCODE_ITERATE_PACKED_ARRAY(COLOR, Color, get_color_array, get_color);
+
+ OPCODE(OPCODE_ITERATE_OBJECT) {
+ CHECK_SPACE(4);
+
+ GET_INSTRUCTION_ARG(counter, 0);
+ GET_INSTRUCTION_ARG(container, 1);
+
+#ifdef DEBUG_ENABLED
+ bool freed = false;
+ Object *obj = container->get_validated_object_with_check(freed);
+ if (freed) {
+ err_text = "Trying to iterate on a previously freed object.";
+ OPCODE_BREAK;
+ } else if (!obj) {
+ err_text = "Trying to iterate on a null value.";
+ OPCODE_BREAK;
+ }
+#else
+ Object *obj = *VariantInternal::get_object(container);
+#endif
+ Array ref;
+ ref.push_back(*counter);
+ Variant vref;
+ VariantInternal::initialize(&vref, Variant::ARRAY);
+ *VariantInternal::get_array(&vref) = ref;
+
+ Variant **args = instruction_args; // Overriding an instruction argument, but we don't need access to that anymore.
+ args[0] = &vref;
+
+ Callable::CallError ce;
+ Variant has_next = obj->call(CoreStringNames::get_singleton()->_iter_next, (const Variant **)args, 1, ce);
+
+#ifdef DEBUG_ENABLED
+ if (ce.error != Callable::CallError::CALL_OK) {
+ err_text = vformat(R"(There was an error calling "_iter_next" on iterator object of type %s.)", *container);
+ OPCODE_BREAK;
+ }
+#endif
+ if (!has_next.booleanize()) {
+ int jumpto = _code_ptr[ip + 4];
+ GD_ERR_BREAK(jumpto < 0 || jumpto > _code_size);
+ ip = jumpto;
+ } else {
+ GET_INSTRUCTION_ARG(iterator, 2);
+ *iterator = obj->call(CoreStringNames::get_singleton()->_iter_get, (const Variant **)args, 1, ce);
+#ifdef DEBUG_ENABLED
+ if (ce.error != Callable::CallError::CALL_OK) {
+ err_text = vformat(R"(There was an error calling "_iter_get" on iterator object of type %s.)", *container);
+ OPCODE_BREAK;
+ }
+#endif
+
+ ip += 5; // Loop again.
+ }
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_ASSERT) {
+ CHECK_SPACE(3);
+
+#ifdef DEBUG_ENABLED
+ GET_INSTRUCTION_ARG(test, 0);
+ bool result = test->booleanize();
+
+ if (!result) {
+ String message_str;
+ if (_code_ptr[ip + 2] != 0) {
+ GET_INSTRUCTION_ARG(message, 1);
+ message_str = *message;
+ }
+ if (message_str.is_empty()) {
+ err_text = "Assertion failed.";
+ } else {
+ err_text = "Assertion failed: " + message_str;
+ }
+ OPCODE_BREAK;
+ }
+
+#endif
+ ip += 3;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_BREAKPOINT) {
+#ifdef DEBUG_ENABLED
+ if (EngineDebugger::is_active()) {
+ GDScriptLanguage::get_singleton()->debug_break("Breakpoint Statement", true);
+ }
+#endif
+ ip += 1;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_LINE) {
+ CHECK_SPACE(2);
+
+ line = _code_ptr[ip + 1];
+ ip += 2;
+
+ if (EngineDebugger::is_active()) {
+ // line
+ bool do_break = false;
+
+ if (EngineDebugger::get_script_debugger()->get_lines_left() > 0) {
+ if (EngineDebugger::get_script_debugger()->get_depth() <= 0) {
+ EngineDebugger::get_script_debugger()->set_lines_left(EngineDebugger::get_script_debugger()->get_lines_left() - 1);
+ }
+ if (EngineDebugger::get_script_debugger()->get_lines_left() <= 0) {
+ do_break = true;
+ }
+ }
+
+ if (EngineDebugger::get_script_debugger()->is_breakpoint(line, source)) {
+ do_break = true;
+ }
+
+ if (do_break) {
+ GDScriptLanguage::get_singleton()->debug_break("Breakpoint", true);
+ }
+
+ EngineDebugger::get_singleton()->line_poll();
+ }
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_END) {
+#ifdef DEBUG_ENABLED
+ exit_ok = true;
+#endif
+ OPCODE_BREAK;
+ }
+
+#if 0 // Enable for debugging.
+ default: {
+ err_text = "Illegal opcode " + itos(_code_ptr[ip]) + " at address " + itos(ip);
+ OPCODE_BREAK;
+ }
+#endif
+ }
+
+ OPCODES_END
+#ifdef DEBUG_ENABLED
+ if (exit_ok) {
+ OPCODE_OUT;
+ }
+ //error
+ // function, file, line, error, explanation
+ String err_file;
+ if (p_instance && ObjectDB::get_instance(p_instance->owner_id) != nullptr && p_instance->script->is_valid() && p_instance->script->path != "") {
+ err_file = p_instance->script->path;
+ } else if (script) {
+ err_file = script->path;
+ }
+ if (err_file == "") {
+ err_file = "<built-in>";
+ }
+ String err_func = name;
+ if (p_instance && ObjectDB::get_instance(p_instance->owner_id) != nullptr && p_instance->script->is_valid() && p_instance->script->name != "") {
+ err_func = p_instance->script->name + "." + err_func;
+ }
+ int err_line = line;
+ if (err_text == "") {
+ err_text = "Internal Script Error! - opcode #" + itos(last_opcode) + " (report please).";
+ }
+
+ if (!GDScriptLanguage::get_singleton()->debug_break(err_text, false)) {
+ // debugger break did not happen
+
+ _err_print_error(err_func.utf8().get_data(), err_file.utf8().get_data(), err_line, err_text.utf8().get_data(), ERR_HANDLER_SCRIPT);
+ }
+
+#endif
+ OPCODE_OUT;
+ }
+
+ OPCODES_OUT
+#ifdef DEBUG_ENABLED
+ if (GDScriptLanguage::get_singleton()->profiling) {
+ uint64_t time_taken = OS::get_singleton()->get_ticks_usec() - function_start_time;
+ profile.total_time += time_taken;
+ profile.self_time += time_taken - function_call_time;
+ profile.frame_total_time += time_taken;
+ profile.frame_self_time += time_taken - function_call_time;
+ GDScriptLanguage::get_singleton()->script_frame_time += time_taken - function_call_time;
+ }
+
+ // Check if this is the last time the function is resuming from await
+ // Will be true if never awaited as well
+ // When it's the last resume it will postpone the exit from stack,
+ // so the debugger knows which function triggered the resume of the next function (if any)
+ if (!p_state || awaited) {
+ if (EngineDebugger::is_active()) {
+ GDScriptLanguage::get_singleton()->exit_function();
+ }
+#endif
+
+ if (_stack_size) {
+ //free stack
+ for (int i = 0; i < _stack_size; i++) {
+ stack[i].~Variant();
+ }
+ }
+
+#ifdef DEBUG_ENABLED
+ }
+#endif
+
+ return retvalue;
+}
diff --git a/modules/gdscript/gdscript_warning.cpp b/modules/gdscript/gdscript_warning.cpp
index 56704d3e0a..ad41b60a4e 100644
--- a/modules/gdscript/gdscript_warning.cpp
+++ b/modules/gdscript/gdscript_warning.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/modules/gdscript/gdscript_warning.h b/modules/gdscript/gdscript_warning.h
index e0857703d8..4b295b5eb8 100644
--- a/modules/gdscript/gdscript_warning.h
+++ b/modules/gdscript/gdscript_warning.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/modules/gdscript/language_server/gdscript_extend_parser.cpp b/modules/gdscript/language_server/gdscript_extend_parser.cpp
index 668dfd4835..e63b6ab20e 100644
--- a/modules/gdscript/language_server/gdscript_extend_parser.cpp
+++ b/modules/gdscript/language_server/gdscript_extend_parser.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -147,7 +147,7 @@ void ExtendGDScriptParser::parse_class_symbol(const GDScriptParser::ClassNode *p
r_symbol.script_path = path;
r_symbol.children.clear();
r_symbol.name = p_class->identifier != nullptr ? String(p_class->identifier->name) : String();
- if (r_symbol.name.empty()) {
+ if (r_symbol.name.is_empty()) {
r_symbol.name = path.get_file();
}
r_symbol.kind = lsp::SymbolKind::Class;
@@ -215,9 +215,9 @@ void ExtendGDScriptParser::parse_class_symbol(const GDScriptParser::ClassNode *p
String value_text;
if (default_value.get_type() == Variant::OBJECT) {
RES res = default_value;
- if (res.is_valid() && !res->get_path().empty()) {
+ if (res.is_valid() && !res->get_path().is_empty()) {
value_text = "preload(\"" + res->get_path() + "\")";
- if (symbol.documentation.empty()) {
+ if (symbol.documentation.is_empty()) {
if (Map<String, ExtendGDScriptParser *>::Element *S = GDScriptLanguageProtocol::get_singleton()->get_workspace()->scripts.find(res->get_path())) {
symbol.documentation = S->get()->class_symbol.documentation;
}
@@ -228,7 +228,7 @@ void ExtendGDScriptParser::parse_class_symbol(const GDScriptParser::ClassNode *p
} else {
value_text = JSON::print(default_value);
}
- if (!value_text.empty()) {
+ if (!value_text.is_empty()) {
symbol.detail += " = " + value_text;
}
@@ -453,7 +453,7 @@ String ExtendGDScriptParser::get_text_for_lookup_symbol(const lsp::Position &p_c
String line = lines[i];
String first_part = line.substr(0, p_cursor.character);
String last_part = line.substr(p_cursor.character + 1, lines[i].length());
- if (!p_symbol.empty()) {
+ if (!p_symbol.is_empty()) {
String left_cursor_text;
for (int c = p_cursor.character - 1; c >= 0; c--) {
left_cursor_text = line.substr(c, p_cursor.character - c);
@@ -589,7 +589,7 @@ const lsp::DocumentSymbol *ExtendGDScriptParser::get_symbol_defined_at_line(int
}
const lsp::DocumentSymbol *ExtendGDScriptParser::get_member_symbol(const String &p_name, const String &p_subclass) const {
- if (p_subclass.empty()) {
+ if (p_subclass.is_empty()) {
const lsp::DocumentSymbol *const *ptr = members.getptr(p_name);
if (ptr) {
return *ptr;
@@ -611,7 +611,7 @@ const List<lsp::DocumentLink> &ExtendGDScriptParser::get_document_links() const
}
const Array &ExtendGDScriptParser::get_member_completions() {
- if (member_completions.empty()) {
+ if (member_completions.is_empty()) {
const String *name = members.next(nullptr);
while (name) {
const lsp::DocumentSymbol *symbol = members.get(*name);
@@ -723,8 +723,8 @@ Dictionary ExtendGDScriptParser::dump_class_api(const GDScriptParser::ClassNode
} break;
case ClassNode::Member::ENUM: {
Dictionary enum_dict;
- for (int j = 0; j < m.m_enum->values.size(); i++) {
- enum_dict[m.m_enum->values[i].identifier->name] = m.m_enum->values[i].value;
+ for (int j = 0; j < m.m_enum->values.size(); j++) {
+ enum_dict[m.m_enum->values[j].identifier->name] = m.m_enum->values[j].value;
}
Dictionary api;
diff --git a/modules/gdscript/language_server/gdscript_extend_parser.h b/modules/gdscript/language_server/gdscript_extend_parser.h
index bb02d3dc99..28b9b3c82a 100644
--- a/modules/gdscript/language_server/gdscript_extend_parser.h
+++ b/modules/gdscript/language_server/gdscript_extend_parser.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/modules/gdscript/language_server/gdscript_language_protocol.cpp b/modules/gdscript/language_server/gdscript_language_protocol.cpp
index 6ddb0d149e..912c9a174e 100644
--- a/modules/gdscript/language_server/gdscript_language_protocol.cpp
+++ b/modules/gdscript/language_server/gdscript_language_protocol.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -33,6 +33,7 @@
#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"
@@ -95,7 +96,7 @@ Error GDScriptLanguageProtocol::LSPeer::handle_data() {
// Response
String output = GDScriptLanguageProtocol::get_singleton()->process_message(msg);
- if (!output.empty()) {
+ if (!output.is_empty()) {
res_queue.push_back(output.utf8());
}
}
@@ -104,7 +105,7 @@ Error GDScriptLanguageProtocol::LSPeer::handle_data() {
Error GDScriptLanguageProtocol::LSPeer::send_data() {
int sent = 0;
- if (!res_queue.empty()) {
+ if (!res_queue.is_empty()) {
CharString c_res = res_queue[0];
if (res_sent < c_res.size()) {
Error err = connection->put_partial_data((const uint8_t *)c_res.get_data() + res_sent, c_res.size() - res_sent - 1, sent);
@@ -140,7 +141,7 @@ void GDScriptLanguageProtocol::on_client_disconnected(const int &p_client_id) {
String GDScriptLanguageProtocol::process_message(const String &p_text) {
String ret = process_string(p_text);
- if (ret.empty()) {
+ if (ret.is_empty()) {
return ret;
} else {
return format_output(ret);
@@ -162,7 +163,7 @@ void GDScriptLanguageProtocol::_bind_methods() {
ClassDB::bind_method(D_METHOD("initialized", "params"), &GDScriptLanguageProtocol::initialized);
ClassDB::bind_method(D_METHOD("on_client_connected"), &GDScriptLanguageProtocol::on_client_connected);
ClassDB::bind_method(D_METHOD("on_client_disconnected"), &GDScriptLanguageProtocol::on_client_disconnected);
- ClassDB::bind_method(D_METHOD("notify_client", "method", "params"), &GDScriptLanguageProtocol::notify_client, DEFVAL(Variant()), DEFVAL(-1));
+ ClassDB::bind_method(D_METHOD("notify_client", "method", "params", "client_id"), &GDScriptLanguageProtocol::notify_client, DEFVAL(Variant()), DEFVAL(-1));
ClassDB::bind_method(D_METHOD("is_smart_resolve_enabled"), &GDScriptLanguageProtocol::is_smart_resolve_enabled);
ClassDB::bind_method(D_METHOD("get_text_document"), &GDScriptLanguageProtocol::get_text_document);
ClassDB::bind_method(D_METHOD("get_workspace"), &GDScriptLanguageProtocol::get_workspace);
@@ -212,7 +213,7 @@ Dictionary GDScriptLanguageProtocol::initialize(const Dictionary &p_params) {
void GDScriptLanguageProtocol::initialized(const Variant &p_params) {
lsp::GodotCapabilities capabilities;
- DocData *doc = EditorHelp::get_doc_data();
+ DocTools *doc = EditorHelp::get_doc_data();
for (Map<String, DocData::ClassDoc>::Element *E = doc->class_list.front(); E; E = E->next()) {
lsp::GodotNativeClassInfo gdclass;
gdclass.name = E->get().name;
diff --git a/modules/gdscript/language_server/gdscript_language_protocol.h b/modules/gdscript/language_server/gdscript_language_protocol.h
index cf5242e8c5..8b08ae0655 100644
--- a/modules/gdscript/language_server/gdscript_language_protocol.h
+++ b/modules/gdscript/language_server/gdscript_language_protocol.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/modules/gdscript/language_server/gdscript_language_server.cpp b/modules/gdscript/language_server/gdscript_language_server.cpp
index 3387d262f8..98ada9de4d 100644
--- a/modules/gdscript/language_server/gdscript_language_server.cpp
+++ b/modules/gdscript/language_server/gdscript_language_server.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -36,12 +36,6 @@
#include "editor/editor_node.h"
GDScriptLanguageServer::GDScriptLanguageServer() {
- thread = nullptr;
- thread_running = false;
- started = false;
-
- use_thread = false;
- port = 6008;
_EDITOR_DEF("network/language_server/remote_port", port);
_EDITOR_DEF("network/language_server/enable_smart_resolve", true);
_EDITOR_DEF("network/language_server/show_native_symbols_in_editor", false);
@@ -87,9 +81,8 @@ void GDScriptLanguageServer::start() {
if (protocol.start(port, IP_Address("127.0.0.1")) == OK) {
EditorNode::get_log()->add_message("--- GDScript language server started ---", EditorLog::MSG_TYPE_EDITOR);
if (use_thread) {
- ERR_FAIL_COND(thread != nullptr);
thread_running = true;
- thread = Thread::create(GDScriptLanguageServer::thread_main, this);
+ thread.start(GDScriptLanguageServer::thread_main, this);
}
set_process_internal(!use_thread);
started = true;
@@ -98,11 +91,9 @@ void GDScriptLanguageServer::start() {
void GDScriptLanguageServer::stop() {
if (use_thread) {
- ERR_FAIL_COND(nullptr == thread);
+ ERR_FAIL_COND(!thread.is_started());
thread_running = false;
- Thread::wait_to_finish(thread);
- memdelete(thread);
- thread = nullptr;
+ thread.wait_to_finish();
}
protocol.stop();
started = false;
diff --git a/modules/gdscript/language_server/gdscript_language_server.h b/modules/gdscript/language_server/gdscript_language_server.h
index 228d29bf42..29c5ddd70e 100644
--- a/modules/gdscript/language_server/gdscript_language_server.h
+++ b/modules/gdscript/language_server/gdscript_language_server.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -40,11 +40,11 @@ class GDScriptLanguageServer : public EditorPlugin {
GDScriptLanguageProtocol protocol;
- Thread *thread;
- bool thread_running;
- bool started;
- bool use_thread;
- int port;
+ Thread thread;
+ bool thread_running = false;
+ bool started = false;
+ bool use_thread = false;
+ int port = 6008;
static void thread_main(void *p_userdata);
private:
diff --git a/modules/gdscript/language_server/gdscript_text_document.cpp b/modules/gdscript/language_server/gdscript_text_document.cpp
index c6fe3169dc..9f2373bf56 100644
--- a/modules/gdscript/language_server/gdscript_text_document.cpp
+++ b/modules/gdscript/language_server/gdscript_text_document.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -147,7 +147,7 @@ Array GDScriptTextDocument::completion(const Dictionary &p_params) {
List<ScriptCodeCompletionOption> options;
GDScriptLanguageProtocol::get_singleton()->get_workspace()->completion(params, &options);
- if (!options.empty()) {
+ if (!options.is_empty()) {
int i = 0;
arr.resize(options.size());
@@ -257,7 +257,7 @@ Dictionary GDScriptTextDocument::resolve(const Dictionary &p_params) {
if ((item.kind == lsp::CompletionItemKind::Method || item.kind == lsp::CompletionItemKind::Function) && !item.label.ends_with("):")) {
item.insertText = item.label + "(";
- if (symbol && symbol->children.empty()) {
+ if (symbol && symbol->children.is_empty()) {
item.insertText += ")";
}
} else if (item.kind == lsp::CompletionItemKind::Event) {
@@ -341,7 +341,7 @@ Variant GDScriptTextDocument::declaration(const Dictionary &p_params) {
params.load(p_params);
List<const lsp::DocumentSymbol *> symbols;
Array arr = this->find_symbols(params, symbols);
- if (arr.empty() && !symbols.empty() && !symbols.front()->get()->native_class.empty()) { // Find a native symbol
+ if (arr.is_empty() && !symbols.is_empty() && !symbols.front()->get()->native_class.is_empty()) { // Find a native symbol
const lsp::DocumentSymbol *symbol = symbols.front()->get();
if (GDScriptLanguageProtocol::get_singleton()->is_goto_native_symbols_enabled()) {
String id;
@@ -425,7 +425,7 @@ Array GDScriptTextDocument::find_symbols(const lsp::TextDocumentPositionParams &
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()) {
- if (!s->uri.empty()) {
+ if (!s->uri.is_empty()) {
lsp::Location location;
location.uri = s->uri;
location.range = s->range;
diff --git a/modules/gdscript/language_server/gdscript_text_document.h b/modules/gdscript/language_server/gdscript_text_document.h
index 2a5755bec6..792e601bc1 100644
--- a/modules/gdscript/language_server/gdscript_text_document.h
+++ b/modules/gdscript/language_server/gdscript_text_document.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/modules/gdscript/language_server/gdscript_workspace.cpp b/modules/gdscript/language_server/gdscript_workspace.cpp
index f6643d07f9..69cad1a335 100644
--- a/modules/gdscript/language_server/gdscript_workspace.cpp
+++ b/modules/gdscript/language_server/gdscript_workspace.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -34,6 +34,7 @@
#include "../gdscript_parser.h"
#include "core/config/project_settings.h"
#include "core/object/script_language.h"
+#include "editor/doc_tools.h"
#include "editor/editor_file_system.h"
#include "editor/editor_help.h"
#include "editor/editor_node.h"
@@ -79,7 +80,7 @@ const lsp::DocumentSymbol *GDScriptWorkspace::get_native_symbol(const String &p_
if (const Map<StringName, lsp::DocumentSymbol>::Element *E = native_symbols.find(class_name)) {
const lsp::DocumentSymbol &class_symbol = E->value();
- if (p_member.empty()) {
+ if (p_member.is_empty()) {
return &class_symbol;
} else {
for (int i = 0; i < class_symbol.children.size(); i++) {
@@ -170,7 +171,7 @@ ExtendGDScriptParser *GDScriptWorkspace::get_parse_result(const String &p_path)
Array GDScriptWorkspace::symbol(const Dictionary &p_params) {
String query = p_params["query"];
Array arr;
- if (!query.empty()) {
+ if (!query.is_empty()) {
for (Map<String, ExtendGDScriptParser *>::Element *E = scripts.front(); E; E = E->next()) {
Vector<lsp::DocumentedSymbolInformation> script_symbols;
E->get()->get_symbols().symbol_tree_as_list(E->key(), script_symbols);
@@ -189,7 +190,7 @@ Error GDScriptWorkspace::initialize() {
return OK;
}
- DocData *doc = EditorHelp::get_doc_data();
+ DocTools *doc = EditorHelp::get_doc_data();
for (Map<String, DocData::ClassDoc>::Element *E = doc->class_list.front(); E; E = E->next()) {
const DocData::ClassDoc &class_data = E->value();
lsp::DocumentSymbol class_symbol;
@@ -198,7 +199,7 @@ Error GDScriptWorkspace::initialize() {
class_symbol.native_class = class_name;
class_symbol.kind = lsp::SymbolKind::Class;
class_symbol.detail = String("<Native> class ") + class_name;
- if (!class_data.inherits.empty()) {
+ if (!class_data.inherits.is_empty()) {
class_symbol.detail += " extends " + class_data.inherits;
}
class_symbol.documentation = class_data.brief_description + "\n" + class_data.description;
@@ -262,7 +263,7 @@ Error GDScriptWorkspace::initialize() {
symbol_arg.kind = lsp::SymbolKind::Variable;
symbol_arg.detail = arg.type;
- if (!arg_default_value_started && !arg.default_value.empty()) {
+ if (!arg_default_value_started && !arg.default_value.is_empty()) {
arg_default_value_started = true;
}
String arg_str = arg.name + ": " + arg.type;
@@ -277,11 +278,11 @@ Error GDScriptWorkspace::initialize() {
symbol.children.push_back(symbol_arg);
}
if (data.qualifiers.find("vararg") != -1) {
- params += params.empty() ? "..." : ", ...";
+ params += params.is_empty() ? "..." : ", ...";
}
String return_type = data.return_type;
- if (return_type.empty()) {
+ if (return_type.is_empty()) {
return_type = "void";
}
symbol.detail = "func " + class_name + "." + data.name + "(" + params + ") -> " + return_type;
@@ -349,7 +350,7 @@ Error GDScriptWorkspace::parse_local_script(const String &p_path) {
String GDScriptWorkspace::get_file_path(const String &p_uri) const {
String path = p_uri;
path = path.replace(root_uri + "/", "res://");
- path = path.http_unescape();
+ path = path.uri_decode();
return path;
}
@@ -447,13 +448,13 @@ const lsp::DocumentSymbol *GDScriptWorkspace::resolve_symbol(const lsp::TextDocu
}
lsp::Position pos = p_doc_pos.position;
- if (symbol_identifier.empty()) {
+ if (symbol_identifier.is_empty()) {
Vector2i offset;
symbol_identifier = parser->get_identifier_under_position(p_doc_pos.position, offset);
pos.character += offset.y;
}
- if (!symbol_identifier.empty()) {
+ if (!symbol_identifier.is_empty()) {
if (ScriptServer::is_global_class(symbol_identifier)) {
String class_path = ScriptServer::get_global_class_path(symbol_identifier);
symbol = get_script_symbol(class_path);
@@ -473,7 +474,7 @@ const lsp::DocumentSymbol *GDScriptWorkspace::resolve_symbol(const lsp::TextDocu
} else {
String member = ret.class_member;
- if (member.empty() && symbol_identifier != ret.class_name) {
+ if (member.is_empty() && symbol_identifier != ret.class_name) {
member = symbol_identifier;
}
symbol = get_native_symbol(ret.class_name, member);
@@ -528,7 +529,7 @@ void GDScriptWorkspace::resolve_related_symbols(const lsp::TextDocumentPositionP
const lsp::DocumentSymbol *GDScriptWorkspace::resolve_native_symbol(const lsp::NativeSymbolInspectParams &p_params) {
if (Map<StringName, lsp::DocumentSymbol>::Element *E = native_symbols.find(p_params.native_class)) {
const lsp::DocumentSymbol &symbol = E->get();
- if (p_params.symbol_name.empty() || p_params.symbol_name == symbol.name) {
+ if (p_params.symbol_name.is_empty() || p_params.symbol_name == symbol.name) {
return &symbol;
}
diff --git a/modules/gdscript/language_server/gdscript_workspace.h b/modules/gdscript/language_server/gdscript_workspace.h
index fc244c6357..7fd8bfcf20 100644
--- a/modules/gdscript/language_server/gdscript_workspace.h
+++ b/modules/gdscript/language_server/gdscript_workspace.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/modules/gdscript/language_server/lsp.hpp b/modules/gdscript/language_server/lsp.hpp
index bf32c1c978..6635098be2 100644
--- a/modules/gdscript/language_server/lsp.hpp
+++ b/modules/gdscript/language_server/lsp.hpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -31,9 +31,9 @@
#ifndef GODOT_LSP_H
#define GODOT_LSP_H
+#include "core/doc_data.h"
#include "core/object/class_db.h"
#include "core/templates/list.h"
-#include "editor/doc_data.h"
namespace lsp {
@@ -547,7 +547,7 @@ struct TextDocumentItem {
* The version number of this document (it will increase after each
* change, including undo/redo).
*/
- int version;
+ int version = 0;
/**
* The content of the opened text document.
@@ -584,7 +584,7 @@ struct TextDocumentContentChangeEvent {
/**
* The length of the range that got replaced.
*/
- int rangeLength;
+ int rangeLength = 0;
/**
* The new text of the range/document.
@@ -656,12 +656,12 @@ struct Diagnostic {
* The diagnostic's severity. Can be omitted. If omitted it is up to the
* client to interpret diagnostics as error, warning, info or hint.
*/
- int severity;
+ int severity = 0;
/**
* The diagnostic's code, which might appear in the user interface.
*/
- int code;
+ int code = 0;
/**
* A human-readable string describing the source of this
@@ -687,7 +687,7 @@ struct Diagnostic {
dict["severity"] = severity;
dict["message"] = message;
dict["source"] = source;
- if (!relatedInformation.empty()) {
+ if (!relatedInformation.is_empty()) {
Array arr;
arr.resize(relatedInformation.size());
for (int i = 0; i < relatedInformation.size(); i++) {
@@ -833,7 +833,7 @@ struct CompletionItem {
* an icon is chosen by the editor. The standardized set
* of available values is defined in `CompletionItemKind`.
*/
- int kind;
+ int kind = 0;
/**
* A human-readable string with additional information
@@ -891,7 +891,7 @@ struct CompletionItem {
* The format of the insert text. The format applies to both the `insertText` property
* and the `newText` property of a provided `textEdit`.
*/
- int insertTextFormat;
+ int insertTextFormat = 0;
/**
* An edit which is applied to a document when selecting this completion. When an edit is provided the value of
@@ -1003,7 +1003,7 @@ struct CompletionList {
* This list it not complete. Further typing should result in recomputing
* this list.
*/
- bool isIncomplete;
+ bool isIncomplete = false;
/**
* The completion items.
@@ -1191,7 +1191,7 @@ struct DocumentSymbol {
void symbol_tree_as_list(const String &p_uri, Vector<DocumentedSymbolInformation> &r_list, const String &p_container = "", bool p_join_name = false) const {
DocumentedSymbolInformation si;
- if (p_join_name && !p_container.empty()) {
+ if (p_join_name && !p_container.is_empty()) {
si.name = p_container + ">" + name;
} else {
si.name = name;
@@ -1661,7 +1661,7 @@ struct ServerCapabilities {
signatureHelpProvider.triggerCharacters.push_back(",");
signatureHelpProvider.triggerCharacters.push_back("(");
dict["signatureHelpProvider"] = signatureHelpProvider.to_json();
- dict["codeLensProvider"] = false; // codeLensProvider.to_json();
+ //dict["codeLensProvider"] = codeLensProvider.to_json();
dict["documentOnTypeFormattingProvider"] = documentOnTypeFormattingProvider.to_json();
dict["renameProvider"] = renameProvider.to_json();
dict["documentLinkProvider"] = documentLinkProvider.to_json();
@@ -1781,7 +1781,6 @@ static String marked_documentation(const String &p_bbcode) {
}
return markdown;
}
-
} // namespace lsp
#endif
diff --git a/modules/gdscript/register_types.cpp b/modules/gdscript/register_types.cpp
index 065f01e654..19fd3daf20 100644
--- a/modules/gdscript/register_types.cpp
+++ b/modules/gdscript/register_types.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -38,6 +38,7 @@
#include "gdscript_analyzer.h"
#include "gdscript_cache.h"
#include "gdscript_tokenizer.h"
+#include "gdscript_utility_functions.h"
#ifdef TESTS_ENABLED
#include "tests/test_gdscript.h"
@@ -84,7 +85,7 @@ public:
return;
}
- // TODO: Readd compiled GDScript on export.
+ // TODO: Re-add compiled GDScript on export.
return;
}
};
@@ -112,7 +113,6 @@ static void _editor_init() {
void register_gdscript_types() {
ClassDB::register_class<GDScript>();
- ClassDB::register_virtual_class<GDScriptFunctionState>();
script_language_gd = memnew(GDScriptLanguage);
ScriptServer::register_language(script_language_gd);
@@ -131,6 +131,8 @@ void register_gdscript_types() {
gdscript_translation_parser_plugin.instance();
EditorTranslationParser::get_singleton()->add_parser(gdscript_translation_parser_plugin, EditorTranslationParser::STANDARD);
#endif // TOOLS_ENABLED
+
+ GDScriptUtilityFunctions::register_functions();
}
void unregister_gdscript_types() {
@@ -156,7 +158,7 @@ void unregister_gdscript_types() {
#endif // TOOLS_ENABLED
GDScriptParser::cleanup();
- GDScriptAnalyzer::cleanup();
+ GDScriptUtilityFunctions::unregister_functions();
}
#ifdef TESTS_ENABLED
diff --git a/modules/gdscript/register_types.h b/modules/gdscript/register_types.h
index 18e57c1211..ce1c03d1d0 100644
--- a/modules/gdscript/register_types.h
+++ b/modules/gdscript/register_types.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/modules/gdscript/tests/test_gdscript.cpp b/modules/gdscript/tests/test_gdscript.cpp
index 50b3783388..3cc0eee672 100644
--- a/modules/gdscript/tests/test_gdscript.cpp
+++ b/modules/gdscript/tests/test_gdscript.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -118,10 +118,10 @@ static void test_parser(const String &p_code, const String &p_script_path, const
print_line(vformat("%02d:%02d: %s", error.line, error.column, error.message));
}
}
-
+#ifdef TOOLS_ENABLED
GDScriptParser::TreePrinter printer;
-
printer.print_tree(parser);
+#endif
}
static void test_compiler(const String &p_code, const String &p_script_path, const Vector<String> &p_lines) {
@@ -175,8 +175,9 @@ static void test_compiler(const String &p_code, const String &p_script_path, con
signature += func->get_argument_name(i);
}
print_line(signature + ")");
-
+#ifdef TOOLS_ENABLED
func->disassemble(p_lines);
+#endif
print_line("");
print_line("");
}
@@ -239,7 +240,7 @@ void init_autoloads() {
void test(TestType p_type) {
List<String> cmdlargs = OS::get_singleton()->get_cmdline_args();
- if (cmdlargs.empty()) {
+ if (cmdlargs.is_empty()) {
return;
}
@@ -303,5 +304,4 @@ void test(TestType p_type) {
ScriptServer::finish_languages();
memdelete(packed_data);
}
-
} // namespace TestGDScript
diff --git a/modules/gdscript/tests/test_gdscript.h b/modules/gdscript/tests/test_gdscript.h
index 5aa962dcf8..bbda46cdad 100644
--- a/modules/gdscript/tests/test_gdscript.h
+++ b/modules/gdscript/tests/test_gdscript.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -41,7 +41,6 @@ enum TestType {
};
void test(TestType p_type);
-
} // namespace TestGDScript
#endif // TEST_GDSCRIPT_H