diff options
Diffstat (limited to 'modules/gdscript')
47 files changed, 2179 insertions, 3632 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=""""> </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 <= 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 <= from[/code], and [code]1[/code] if [code]s >= 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..b792ff54d6 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 */ 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..6e930b6bf4 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 */ diff --git a/modules/gdscript/editor/gdscript_translation_parser_plugin.h b/modules/gdscript/editor/gdscript_translation_parser_plugin.h index d763df01f5..5358a77140 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 */ diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp index 53602f7a9b..502e294275 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 */ @@ -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,20 +1123,43 @@ 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; @@ -1061,6 +1287,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); @@ -1889,8 +2122,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 +2151,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 +2163,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 +2177,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; @@ -2138,7 +2374,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..37f01b2571 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" @@ -71,8 +72,8 @@ 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; @@ -91,9 +92,7 @@ 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; @@ -102,6 +101,20 @@ class GDScript : public Script { bool placeholder_fallback_enabled; 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; @@ -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); diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp index 320778c635..a6138cc564 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,6 +37,7 @@ #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; @@ -59,7 +60,7 @@ static const char *underscore_classes[] = { nullptr, }; static StringName get_real_class_name(const StringName &p_source) { - if (underscore_map.empty()) { + if (underscore_map.is_empty()) { const char **class_name = underscore_classes; while (*class_name != nullptr) { underscore_map[*class_name] = String("_") + *class_name; @@ -72,6 +73,39 @@ static StringName get_real_class_name(const StringName &p_source) { return p_source; } +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; +} + void GDScriptAnalyzer::cleanup() { underscore_map.clear(); } @@ -175,7 +209,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 +224,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++]; @@ -342,7 +376,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; @@ -860,7 +894,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") { @@ -983,12 +1022,14 @@ void GDScriptAnalyzer::resolve_for(GDScriptParser::ForNode *p_for) { } 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]); + } } } @@ -1122,24 +1163,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); @@ -1174,7 +1217,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()); @@ -1609,7 +1655,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())), @@ -1694,7 +1740,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. @@ -1707,7 +1752,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++) { @@ -1816,10 +1883,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++) { @@ -1828,23 +1937,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. @@ -1927,14 +2036,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); } @@ -2358,7 +2467,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); @@ -2728,7 +2837,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); @@ -3094,81 +3203,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; } @@ -3350,12 +3409,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() { @@ -3370,7 +3429,7 @@ Error GDScriptAnalyzer::resolve_program() { } depended_parsers[E->get()]->raise_status(GDScriptParserRef::FULLY_SOLVED); } - 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 4fb9c633f1..dab5b032a3 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 */ @@ -102,6 +102,7 @@ class GDScriptAnalyzer { 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); 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); diff --git a/modules/gdscript/gdscript_byte_codegen.cpp b/modules/gdscript/gdscript_byte_codegen.cpp index a81b3298ef..58c6b31a77 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++; } @@ -96,7 +92,12 @@ void GDScriptByteCodeGenerator::pop_temporary() { 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(); @@ -167,7 +168,7 @@ 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; @@ -282,6 +283,30 @@ GDScriptFunction *GDScriptByteCodeGenerator::write_end() { 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(); @@ -321,7 +346,28 @@ void GDScriptByteCodeGenerator::set_initial_line(int p_line) { #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_operator(const Address &p_target, Variant::Operator p_operator, const Address &p_left_operand, const Address &p_right_operand) { +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); @@ -382,7 +428,7 @@ void GDScriptByteCodeGenerator::write_end_and(const Address &p_target) { 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, 0); + append(GDScriptFunction::OPCODE_ASSIGN_FALSE, 1); append(p_target); } @@ -407,7 +453,7 @@ void GDScriptByteCodeGenerator::write_end_or(const Address &p_target) { // Jump away from the success condition. 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(); @@ -612,6 +658,11 @@ void GDScriptByteCodeGenerator::write_assign_false(const Address &p_target) { 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; @@ -677,8 +728,8 @@ void GDScriptByteCodeGenerator::write_call_async(const Address &p_target, const 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, 1 + 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]); } @@ -687,6 +738,41 @@ void GDScriptByteCodeGenerator::write_call_builtin(const Address &p_target, GDSc append(p_function); } +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; @@ -813,6 +899,17 @@ void GDScriptByteCodeGenerator::write_call_self(const Address &p_target, const S 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); + 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, 2 + p_arguments.size()); for (int i = 0; i < p_arguments.size(); i++) { diff --git a/modules/gdscript/gdscript_byte_codegen.h b/modules/gdscript/gdscript_byte_codegen.h index 0173b7f820..1e66af269a 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 */ @@ -34,6 +34,7 @@ #include "gdscript_codegen.h" #include "gdscript_function.h" +#include "gdscript_utility_functions.h" class GDScriptByteCodeGenerator : public GDScriptCodeGenerator { bool ended = false; @@ -76,6 +77,8 @@ class GDScriptByteCodeGenerator : public GDScriptCodeGenerator { 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. @@ -241,6 +244,24 @@ class GDScriptByteCodeGenerator : public GDScriptCodeGenerator { 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]; @@ -346,6 +367,14 @@ class GDScriptByteCodeGenerator : public GDScriptCodeGenerator { 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)); } @@ -377,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; @@ -400,15 +430,18 @@ 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_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; 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 decab3df0b..d72bd12033 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,15 +122,18 @@ 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_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; diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp index 3d327cebeb..b491440d4c 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,11 +126,13 @@ 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; } } @@ -151,8 +155,8 @@ GDScriptDataType GDScriptCompiler::_gdtype_from_datatype(const GDScriptParser::D // 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; @@ -453,15 +457,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; @@ -488,9 +494,17 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code } 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); @@ -677,7 +691,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(); @@ -745,7 +759,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(); @@ -908,7 +922,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(); } @@ -966,7 +980,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(); } @@ -1016,7 +1030,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(); @@ -1072,7 +1086,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. @@ -1083,7 +1097,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). @@ -1132,14 +1146,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(result_addr, Variant::OP_EQUAL, p_value_addr, expr_addr); codegen.generator->write_and_right_operand(equality_test_addr); // AND both type and value equality. @@ -1185,7 +1199,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. @@ -1196,12 +1210,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. @@ -1250,7 +1264,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); @@ -1284,7 +1298,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. @@ -1295,12 +1309,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. @@ -1364,7 +1378,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); @@ -1497,7 +1511,7 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Sui 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. @@ -1838,7 +1852,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(); } @@ -1883,6 +1897,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); @@ -1896,6 +1911,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); @@ -1993,6 +2023,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; @@ -2105,20 +2153,23 @@ 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; -#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; - } -#endif } else { prop_info.usage = PROPERTY_USAGE_SCRIPT_VARIABLE; } +#ifdef TOOLS_ENABLED + p_script->doc_variables[name] = variable->doc_description; +#endif 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; @@ -2129,8 +2180,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; @@ -2141,6 +2194,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; @@ -2176,6 +2238,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: { @@ -2192,6 +2259,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: diff --git a/modules/gdscript/gdscript_compiler.h b/modules/gdscript/gdscript_compiler.h index 157c801f56..00953ad752 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 */ diff --git a/modules/gdscript/gdscript_disassembler.cpp b/modules/gdscript/gdscript_disassembler.cpp index 92a44c57f8..17cb5e3c96 100644 --- a/modules/gdscript/gdscript_disassembler.cpp +++ b/modules/gdscript/gdscript_disassembler.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,7 +34,6 @@ #include "core/string/string_builder.h" #include "gdscript.h" -#include "gdscript_functions.h" static String _get_variant_string(const Variant &p_variant) { String txt; @@ -324,11 +323,8 @@ void GDScriptFunction::disassemble(const Vector<String> &p_code_lines) const { incr += 4; } break; case OPCODE_ASSIGN_TYPED_NATIVE: { - Variant class_name = _constants_ptr[_code_ptr[ip + 3]]; - GDScriptNativeClass *nc = Object::cast_to<GDScriptNativeClass>(class_name.operator Object *()); - text += "assign typed native ("; - text += nc->get_name().operator String(); + text += DADDR(3); text += ") "; text += DADDR(1); text += " = "; @@ -607,13 +603,49 @@ void GDScriptFunction::disassemble(const Vector<String> &p_code_lines) const { incr = 5 + argc; } break; - case OPCODE_CALL_BUILT_IN: { - text += "call-built-in "; + 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 += GDScriptFunctions::get_func_name(GDScriptFunctions::Function(_code_ptr[ip + 2 + instr_var_args])); + text += "<unknown function>"; text += "("; for (int i = 0; i < argc; i++) { diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp index a426046797..b17971cf93 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; } } @@ -1026,9 +1034,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 +1065,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 +1077,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 +1295,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; @@ -2309,12 +2345,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 +2511,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 { @@ -2942,13 +2978,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 32372439c5..2171426e6f 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 */ @@ -114,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); } } diff --git a/modules/gdscript/gdscript_function.h b/modules/gdscript/gdscript_function.h index bb5cc1284d..6c791836b9 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,6 +38,7 @@ #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; @@ -190,7 +191,9 @@ public: 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, @@ -344,6 +347,10 @@ private: 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; @@ -372,6 +379,8 @@ private: 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; @@ -379,6 +388,7 @@ private: #ifdef TOOLS_ENABLED Vector<StringName> arg_names; + Vector<Variant> default_arg_values; #endif List<StackDebug> stack_debug; @@ -458,6 +468,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); 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..a77fb14064 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; @@ -98,15 +98,6 @@ 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); - } - } - return GDScriptFunctions::FUNC_MAX; -} - void GDScriptParser::get_annotation_list(List<MethodInfo> *r_annotations) const { List<StringName> keys; valid_annotations.get_key_list(&keys); @@ -184,23 +175,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 +284,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 +292,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 +358,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 +369,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 +500,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 +511,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 +549,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,8 +670,12 @@ 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); @@ -678,7 +686,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?) @@ -950,6 +976,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 +1076,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 +1115,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 +1473,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 +1503,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 +1697,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.)"); } @@ -2488,7 +2543,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); @@ -2618,6 +2673,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. @@ -2651,8 +2918,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, @@ -3012,11 +3279,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 +3328,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 +3537,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 +3999,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 +4119,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..f43708b81f 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 @@ -287,7 +286,7 @@ public: struct AssertNode : public Node { ExpressionNode *condition = nullptr; - LiteralNode *message = nullptr; + ExpressionNode *message = nullptr; AssertNode() { type = ASSERT; @@ -413,9 +412,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 +574,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 +619,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 +673,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; @@ -820,6 +844,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; @@ -1012,6 +1039,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 +1172,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, @@ -1270,13 +1299,19 @@ 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); 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..315b8ee3b4 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())); @@ -1014,9 +1014,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 +1133,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; } diff --git a/modules/gdscript/gdscript_tokenizer.h b/modules/gdscript/gdscript_tokenizer.h index d51f1f250f..cdb0072294 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" @@ -181,6 +182,21 @@ public: } }; +#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 +223,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 index 7c8bfcd944..4e098d7a6d 100644 --- a/modules/gdscript/gdscript_vm.cpp +++ b/modules/gdscript/gdscript_vm.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,7 +33,6 @@ #include "core/core_string_names.h" #include "core/os/os.h" #include "gdscript.h" -#include "gdscript_functions.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; @@ -220,7 +219,9 @@ String GDScriptFunction::_get_call_error(const Callable::CallError &p_err, const &&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, \ @@ -475,11 +476,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a } 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; - } + self = p_instance->owner; script = p_instance->script.ptr(); } else { script = _script; @@ -738,7 +735,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a const Variant::ValidatedKeyedSetter setter = _keyed_setters_ptr[index_setter]; bool valid; - setter(dst, index, value, valid); + setter(dst, index, value, &valid); #ifdef DEBUG_ENABLED if (!valid) { @@ -770,7 +767,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a int64_t int_index = *VariantInternal::get_int(index); bool oob; - setter(dst, int_index, value, oob); + setter(dst, int_index, value, &oob); #ifdef DEBUG_ENABLED if (oob) { @@ -835,9 +832,9 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a #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); + getter(src, key, &ret, &valid); #else - getter(src, key, dst, valid); + getter(src, key, dst, &valid); #endif #ifdef DEBUG_ENABLED if (!valid) { @@ -870,7 +867,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a int64_t int_index = *VariantInternal::get_int(index); bool oob; - getter(src, int_index, dst, oob); + getter(src, int_index, dst, &oob); #ifdef DEBUG_ENABLED if (oob) { @@ -1292,7 +1289,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a GET_INSTRUCTION_ARG(dst, argc); - constructor(*dst, (const Variant **)argptrs); + constructor(dst, (const Variant **)argptrs); ip += 3; } @@ -1653,7 +1650,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a VariantInternal::initialize(ret, Variant::OBJECT); Object **ret_opaque = VariantInternal::get_object(ret); method->ptrcall(base_obj, argptrs, ret_opaque); - VariantInternal::set_object(ret, *ret_opaque); + VariantInternal::object_assign(ret, *ret_opaque); // Set so ID is correct too. #ifdef DEBUG_ENABLED if (GDScriptLanguage::get_singleton()->profiling) { @@ -1749,7 +1746,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a } DISPATCH_OPCODE; - OPCODE(OPCODE_CALL_BUILT_IN) { + OPCODE(OPCODE_CALL_UTILITY) { CHECK_SPACE(3 + instr_arg_count); ip += instr_arg_count; @@ -1757,22 +1754,80 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a int argc = _code_ptr[ip + 1]; GD_ERR_BREAK(argc < 0); - GDScriptFunctions::Function func = GDScriptFunctions::Function(_code_ptr[ip + 2]); + 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; - GDScriptFunctions::call(func, (const Variant **)argptrs, argc, *dst, err); + Variant::call_utility_function(function, dst, (const Variant **)argptrs, argc, err); #ifdef DEBUG_ENABLED if (err.error != Callable::CallError::CALL_OK) { - String methodstr = GDScriptFunctions::get_func_name(func); + String methodstr = function; if (dst->get_type() == Variant::STRING) { - //call provided error string - err_text = "Error calling built-in function '" + methodstr + "': " + String(*dst); + // Call provided error string. + err_text = "Error calling utility function '" + methodstr + "': " + String(*dst); } else { - err_text = _get_call_error(err, "built-in function '" + methodstr + "'", (const Variant **)argptrs); + 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; } @@ -2223,7 +2278,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a VariantInternal::initialize(counter, Variant::INT); *VariantInternal::get_int(counter) = 0; - if (!str->empty()) { + if (!str->is_empty()) { GET_INSTRUCTION_ARG(iterator, 2); VariantInternal::initialize(iterator, Variant::STRING); *VariantInternal::get_string(iterator) = str->substr(0, 1); @@ -2247,10 +2302,10 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a Dictionary *dict = VariantInternal::get_dictionary(container); const Variant *next = dict->next(nullptr); - *counter = *next; - if (!dict->empty()) { + if (!dict->is_empty()) { GET_INSTRUCTION_ARG(iterator, 2); + *counter = *next; *iterator = *next; // Skip regular iterate. @@ -2275,7 +2330,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a VariantInternal::initialize(counter, Variant::INT); *VariantInternal::get_int(counter) = 0; - if (!array->empty()) { + if (!array->is_empty()) { GET_INSTRUCTION_ARG(iterator, 2); *iterator = array->get(0); @@ -2298,7 +2353,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a Vector<m_elem_type> *array = VariantInternal::m_get_func(container); \ VariantInternal::initialize(counter, Variant::INT); \ *VariantInternal::get_int(counter) = 0; \ - if (!array->empty()) { \ + 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); \ @@ -2722,7 +2777,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a GET_INSTRUCTION_ARG(message, 1); message_str = *message; } - if (message_str.empty()) { + if (message_str.is_empty()) { err_text = "Assertion failed."; } else { err_text = "Assertion failed: " + message_str; 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..5e3d6213d3 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); @@ -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..aac9cb7fd7 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 */ diff --git a/modules/gdscript/language_server/gdscript_language_server.h b/modules/gdscript/language_server/gdscript_language_server.h index 228d29bf42..218f42199e 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 */ 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..7b502f079b 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; @@ -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 288fd41c87..6a913edbbf 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 { @@ -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++) { @@ -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; diff --git a/modules/gdscript/register_types.cpp b/modules/gdscript/register_types.cpp index 065f01e654..e90475a60e 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" @@ -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() { @@ -157,6 +159,7 @@ void unregister_gdscript_types() { 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 643c2f10a2..898ac653f5 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 */ @@ -239,7 +239,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; } diff --git a/modules/gdscript/tests/test_gdscript.h b/modules/gdscript/tests/test_gdscript.h index 6182629802..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 */ |