feat: updated engine version to 4.4-rc1
This commit is contained in:
parent
ee00efde1f
commit
21ba8e33af
5459 changed files with 1128836 additions and 198305 deletions
|
|
@ -1,8 +0,0 @@
|
|||
[*.gd]
|
||||
indent_style = tab
|
||||
indent_size = 4
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[*.out]
|
||||
insert_final_newline = true
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
#!/usr/bin/env python
|
||||
from misc.utility.scons_hints import *
|
||||
|
||||
Import("env")
|
||||
Import("env_modules")
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ def get_doc_classes():
|
|||
return [
|
||||
"@GDScript",
|
||||
"GDScript",
|
||||
"GDScriptSyntaxHighlighter",
|
||||
]
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -4,14 +4,14 @@
|
|||
Built-in GDScript constants, functions, and annotations.
|
||||
</brief_description>
|
||||
<description>
|
||||
A list of GDScript-specific utility functions and annotations accessible from any script.
|
||||
For the list of the global functions and constants see [@GlobalScope].
|
||||
A list of utility functions and annotations accessible from any script written in GDScript.
|
||||
For the list of global functions and constants that can be accessed in any scripting language, see [@GlobalScope].
|
||||
</description>
|
||||
<tutorials>
|
||||
<link title="GDScript exports">$DOCS_URL/tutorials/scripting/gdscript/gdscript_exports.html</link>
|
||||
</tutorials>
|
||||
<methods>
|
||||
<method name="Color8">
|
||||
<method name="Color8" deprecated="Use [method Color.from_rgba8] instead.">
|
||||
<return type="Color" />
|
||||
<param index="0" name="r8" type="int" />
|
||||
<param index="1" name="g8" type="int" />
|
||||
|
|
@ -52,16 +52,16 @@
|
|||
<description>
|
||||
Returns a single character (as a [String]) of the given Unicode code point (which is compatible with ASCII code).
|
||||
[codeblock]
|
||||
a = char(65) # a is "A"
|
||||
a = char(65 + 32) # a is "a"
|
||||
a = char(8364) # a is "€"
|
||||
var upper = char(65) # upper is "A"
|
||||
var lower = char(65 + 32) # lower is "a"
|
||||
var euro = char(8364) # euro is "€"
|
||||
[/codeblock]
|
||||
</description>
|
||||
</method>
|
||||
<method name="convert" deprecated="Use [method @GlobalScope.type_convert] instead.">
|
||||
<return type="Variant" />
|
||||
<param index="0" name="what" type="Variant" />
|
||||
<param index="1" name="type" type="int" />
|
||||
<param index="1" name="type" type="int" enum="Variant.Type" />
|
||||
<description>
|
||||
Converts [param what] to [param type] in the best way possible. The [param type] uses the [enum Variant.Type] values.
|
||||
[codeblock]
|
||||
|
|
@ -74,7 +74,7 @@
|
|||
[/codeblock]
|
||||
</description>
|
||||
</method>
|
||||
<method name="dict_to_inst">
|
||||
<method name="dict_to_inst" deprecated="Consider using [method JSON.to_native] or [method Object.get_property_list] instead.">
|
||||
<return type="Object" />
|
||||
<param index="0" name="dictionary" type="Dictionary" />
|
||||
<description>
|
||||
|
|
@ -103,12 +103,11 @@
|
|||
[b]Note:[/b] Calling this function from a [Thread] is not supported. Doing so will return an empty array.
|
||||
</description>
|
||||
</method>
|
||||
<method name="inst_to_dict">
|
||||
<method name="inst_to_dict" deprecated="Consider using [method JSON.from_native] or [method Object.get_property_list] instead.">
|
||||
<return type="Dictionary" />
|
||||
<param index="0" name="instance" type="Object" />
|
||||
<description>
|
||||
Returns the passed [param instance] converted to a Dictionary. Can be useful for serializing.
|
||||
[b]Note:[/b] Cannot be used to serialize objects with built-in scripts attached or objects allocated within built-in scripts.
|
||||
[codeblock]
|
||||
var foo = "bar"
|
||||
func _ready():
|
||||
|
|
@ -121,6 +120,8 @@
|
|||
[@subpath, @path, foo]
|
||||
[, res://test.gd, bar]
|
||||
[/codeblock]
|
||||
[b]Note:[/b] This function can only be used to serialize objects with an attached [GDScript] stored in a separate file. Objects without an attached script, with a script written in another language, or with a built-in script are not supported.
|
||||
[b]Note:[/b] This function is not recursive, which means that nested objects will not be represented as dictionaries. Also, properties passed by reference ([Object], [Dictionary], [Array], and packed arrays) are copied by reference, not duplicated.
|
||||
</description>
|
||||
</method>
|
||||
<method name="is_instance_of">
|
||||
|
|
@ -133,7 +134,7 @@
|
|||
- An [Object]-derived class which exists in [ClassDB], for example [Node].
|
||||
- A [Script] (you can use any class, including inner one).
|
||||
Unlike the right operand of the [code]is[/code] operator, [param type] can be a non-constant value. The [code]is[/code] operator supports more features (such as typed arrays). Use the operator instead of this method if you do not need dynamic type checking.
|
||||
Examples:
|
||||
[b]Examples:[/b]
|
||||
[codeblock]
|
||||
print(is_instance_of(a, TYPE_INT))
|
||||
print(is_instance_of(a, Node))
|
||||
|
|
@ -150,10 +151,10 @@
|
|||
<description>
|
||||
Returns the length of the given Variant [param var]. The length can be the character count of a [String] or [StringName], the element count of any array type, or the size of a [Dictionary]. For every other Variant type, a run-time error is generated and execution is stopped.
|
||||
[codeblock]
|
||||
a = [1, 2, 3, 4]
|
||||
var a = [1, 2, 3, 4]
|
||||
len(a) # Returns 4
|
||||
|
||||
b = "Hello!"
|
||||
var b = "Hello!"
|
||||
len(b) # Returns 6
|
||||
[/codeblock]
|
||||
</description>
|
||||
|
|
@ -220,7 +221,7 @@
|
|||
[code]range(b: int, n: int, s: int)[/code]: Starts from [code]b[/code], increases/decreases by steps of [code]s[/code], and stops [i]before[/i] [code]n[/code]. The arguments [code]b[/code] and [code]n[/code] are [b]inclusive[/b] and [b]exclusive[/b], respectively. The argument [code]s[/code] [b]can[/b] be negative, but not [code]0[/code]. If [code]s[/code] is [code]0[/code], an error message is printed.
|
||||
[method range] converts all arguments to [int] before processing.
|
||||
[b]Note:[/b] Returns an empty array if no value meets the value constraint (e.g. [code]range(2, 5, -1)[/code] or [code]range(5, 5, 1)[/code]).
|
||||
Examples:
|
||||
[b]Examples:[/b]
|
||||
[codeblock]
|
||||
print(range(4)) # Prints [0, 1, 2, 3]
|
||||
print(range(2, 5)) # Prints [2, 3, 4]
|
||||
|
|
@ -666,7 +667,54 @@
|
|||
@export var car_label = "Speedy"
|
||||
@export var car_number = 3
|
||||
[/codeblock]
|
||||
[b]Note:[/b] Subgroups cannot be nested, they only provide one extra level of depth. Just like the next group ends the previous group, so do the subsequent subgroups.
|
||||
[b]Note:[/b] Subgroups cannot be nested, but you can use the slash separator ([code]/[/code]) to achieve the desired effect:
|
||||
[codeblock]
|
||||
@export_group("Car Properties")
|
||||
@export_subgroup("Wheels", "wheel_")
|
||||
@export_subgroup("Wheels/Front", "front_wheel_")
|
||||
@export var front_wheel_strength = 10
|
||||
@export var front_wheel_mobility = 5
|
||||
@export_subgroup("Wheels/Rear", "rear_wheel_")
|
||||
@export var rear_wheel_strength = 8
|
||||
@export var rear_wheel_mobility = 3
|
||||
@export_subgroup("Wheels", "wheel_")
|
||||
@export var wheel_material: PhysicsMaterial
|
||||
[/codeblock]
|
||||
</description>
|
||||
</annotation>
|
||||
<annotation name="@export_tool_button">
|
||||
<return type="void" />
|
||||
<param index="0" name="text" type="String" />
|
||||
<param index="1" name="icon" type="String" default="""" />
|
||||
<description>
|
||||
Export a [Callable] property as a clickable button with the label [param text]. When the button is pressed, the callable is called.
|
||||
If [param icon] is specified, it is used to fetch an icon for the button via [method Control.get_theme_icon], from the [code]"EditorIcons"[/code] theme type. If [param icon] is omitted, the default [code]"Callable"[/code] icon is used instead.
|
||||
Consider using the [EditorUndoRedoManager] to allow the action to be reverted safely.
|
||||
See also [constant PROPERTY_HINT_TOOL_BUTTON].
|
||||
[codeblock]
|
||||
@tool
|
||||
extends Sprite2D
|
||||
|
||||
@export_tool_button("Hello") var hello_action = hello
|
||||
@export_tool_button("Randomize the color!", "ColorRect")
|
||||
var randomize_color_action = randomize_color
|
||||
|
||||
func hello():
|
||||
print("Hello world!")
|
||||
|
||||
func randomize_color():
|
||||
var undo_redo = EditorInterface.get_editor_undo_redo()
|
||||
undo_redo.create_action("Randomized Sprite2D Color")
|
||||
undo_redo.add_do_property(self, &"self_modulate", Color(randf(), randf(), randf()))
|
||||
undo_redo.add_undo_property(self, &"self_modulate", self_modulate)
|
||||
undo_redo.commit_action()
|
||||
[/codeblock]
|
||||
[b]Note:[/b] The property is exported without the [constant PROPERTY_USAGE_STORAGE] flag because a [Callable] cannot be properly serialized and stored in a file.
|
||||
[b]Note:[/b] In an exported project neither [EditorInterface] nor [EditorUndoRedoManager] exist, which may cause some scripts to break. To prevent this, you can use [method Engine.get_singleton] and omit the static type from the variable declaration:
|
||||
[codeblock]
|
||||
var undo_redo = Engine.get_singleton(&"EditorInterface").get_editor_undo_redo()
|
||||
[/codeblock]
|
||||
[b]Note:[/b] Avoid storing lambda callables in member variables of [RefCounted]-based classes (e.g. resources), as this can lead to memory leaks. Use only method callables and optionally [method Callable.bind] or [method Callable.unbind].
|
||||
</description>
|
||||
</annotation>
|
||||
<annotation name="@icon">
|
||||
|
|
@ -679,7 +727,7 @@
|
|||
[/codeblock]
|
||||
[b]Note:[/b] Only the script can have a custom icon. Inner classes are not supported.
|
||||
[b]Note:[/b] As annotations describe their subject, the [annotation @icon] annotation must be placed before the class definition and inheritance.
|
||||
[b]Note:[/b] Unlike other annotations, the argument of the [annotation @icon] annotation must be a string literal (constant expressions are not supported).
|
||||
[b]Note:[/b] Unlike most other annotations, the argument of the [annotation @icon] annotation must be a string literal (constant expressions are not supported).
|
||||
</description>
|
||||
</annotation>
|
||||
<annotation name="@onready">
|
||||
|
|
@ -687,7 +735,7 @@
|
|||
<description>
|
||||
Mark the following property as assigned when the [Node] is ready. Values for these properties are not assigned immediately when the node is initialized ([method Object._init]), and instead are computed and stored right before [method Node._ready].
|
||||
[codeblock]
|
||||
@onready var character_name: Label = $Label
|
||||
@onready var character_name = $Label
|
||||
[/codeblock]
|
||||
</description>
|
||||
</annotation>
|
||||
|
|
@ -747,6 +795,33 @@
|
|||
@warning_ignore("unreachable_code")
|
||||
print("unreachable")
|
||||
[/codeblock]
|
||||
See also [annotation @warning_ignore_start] and [annotation @warning_ignore_restore].
|
||||
</description>
|
||||
</annotation>
|
||||
<annotation name="@warning_ignore_restore" qualifiers="vararg">
|
||||
<return type="void" />
|
||||
<param index="0" name="warning" type="String" />
|
||||
<description>
|
||||
Stops ignoring the listed warning types after [annotation @warning_ignore_start]. Ignoring the specified warning types will be reset to Project Settings. This annotation can be omitted to ignore the warning types until the end of the file.
|
||||
[b]Note:[/b] Unlike most other annotations, arguments of the [annotation @warning_ignore_restore] annotation must be string literals (constant expressions are not supported).
|
||||
</description>
|
||||
</annotation>
|
||||
<annotation name="@warning_ignore_start" qualifiers="vararg">
|
||||
<return type="void" />
|
||||
<param index="0" name="warning" type="String" />
|
||||
<description>
|
||||
Starts ignoring the listed warning types until the end of the file or the [annotation @warning_ignore_restore] annotation with the given warning type.
|
||||
[codeblock]
|
||||
func test():
|
||||
var a = 1 # Warning (if enabled in the Project Settings).
|
||||
@warning_ignore_start("unused_variable")
|
||||
var b = 2 # No warning.
|
||||
var c = 3 # No warning.
|
||||
@warning_ignore_restore("unused_variable")
|
||||
var d = 4 # Warning (if enabled in the Project Settings).
|
||||
[/codeblock]
|
||||
[b]Note:[/b] To suppress a single warning, use [annotation @warning_ignore] instead.
|
||||
[b]Note:[/b] Unlike most other annotations, arguments of the [annotation @warning_ignore_start] annotation must be string literals (constant expressions are not supported).
|
||||
</description>
|
||||
</annotation>
|
||||
</annotations>
|
||||
|
|
|
|||
|
|
@ -16,11 +16,10 @@
|
|||
<return type="Variant" />
|
||||
<description>
|
||||
Returns a new instance of the script.
|
||||
For example:
|
||||
[codeblock]
|
||||
var MyClass = load("myclass.gd")
|
||||
var instance = MyClass.new()
|
||||
assert(instance.get_script() == MyClass)
|
||||
print(instance.get_script() == MyClass) # Prints true
|
||||
[/codeblock]
|
||||
</description>
|
||||
</method>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,23 @@
|
|||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<class name="GDScriptSyntaxHighlighter" inherits="EditorSyntaxHighlighter" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
|
||||
<brief_description>
|
||||
A GDScript syntax highlighter that can be used with [TextEdit] and [CodeEdit] nodes.
|
||||
</brief_description>
|
||||
<description>
|
||||
[b]Note:[/b] This class can only be used for editor plugins because it relies on editor settings.
|
||||
[codeblocks]
|
||||
[gdscript]
|
||||
var code_preview = TextEdit.new()
|
||||
var highlighter = GDScriptSyntaxHighlighter.new()
|
||||
code_preview.syntax_highlighter = highlighter
|
||||
[/gdscript]
|
||||
[csharp]
|
||||
var codePreview = new TextEdit();
|
||||
var highlighter = new GDScriptSyntaxHighlighter();
|
||||
codePreview.SyntaxHighlighter = highlighter;
|
||||
[/csharp]
|
||||
[/codeblocks]
|
||||
</description>
|
||||
<tutorials>
|
||||
</tutorials>
|
||||
</class>
|
||||
|
|
@ -84,6 +84,15 @@ void GDScriptDocGen::_doctype_from_gdtype(const GDType &p_gdtype, String &r_type
|
|||
return;
|
||||
}
|
||||
}
|
||||
if (p_gdtype.builtin_type == Variant::DICTIONARY && p_gdtype.has_container_element_types()) {
|
||||
String key, value;
|
||||
_doctype_from_gdtype(p_gdtype.get_container_element_type_or_variant(0), key, r_enum);
|
||||
_doctype_from_gdtype(p_gdtype.get_container_element_type_or_variant(1), value, r_enum);
|
||||
if (key != "Variant" || value != "Variant") {
|
||||
r_type = "Dictionary[" + key + ", " + value + "]";
|
||||
return;
|
||||
}
|
||||
}
|
||||
r_type = Variant::get_type_name(p_gdtype.builtin_type);
|
||||
return;
|
||||
case GDType::NATIVE:
|
||||
|
|
@ -130,10 +139,11 @@ void GDScriptDocGen::_doctype_from_gdtype(const GDType &p_gdtype, String &r_type
|
|||
r_type = "int";
|
||||
r_enum = String(p_gdtype.native_type).replace("::", ".");
|
||||
if (r_enum.begins_with("res://")) {
|
||||
r_enum = r_enum.trim_prefix("res://");
|
||||
int dot_pos = r_enum.rfind(".");
|
||||
int dot_pos = r_enum.rfind_char('.');
|
||||
if (dot_pos >= 0) {
|
||||
r_enum = r_enum.left(dot_pos).quote() + r_enum.substr(dot_pos);
|
||||
r_enum = _get_script_name(r_enum.left(dot_pos)) + r_enum.substr(dot_pos);
|
||||
} else {
|
||||
r_enum = _get_script_name(r_enum);
|
||||
}
|
||||
}
|
||||
return;
|
||||
|
|
@ -155,34 +165,82 @@ String GDScriptDocGen::_docvalue_from_variant(const Variant &p_variant, int p_re
|
|||
return "<Object>";
|
||||
case Variant::DICTIONARY: {
|
||||
const Dictionary dict = p_variant;
|
||||
String result;
|
||||
|
||||
if (dict.is_typed()) {
|
||||
result += "Dictionary[";
|
||||
|
||||
Ref<Script> key_script = dict.get_typed_key_script();
|
||||
if (key_script.is_valid()) {
|
||||
if (key_script->get_global_name() != StringName()) {
|
||||
result += key_script->get_global_name();
|
||||
} else if (!key_script->get_path().get_file().is_empty()) {
|
||||
result += key_script->get_path().get_file();
|
||||
} else {
|
||||
result += dict.get_typed_key_class_name();
|
||||
}
|
||||
} else if (dict.get_typed_key_class_name() != StringName()) {
|
||||
result += dict.get_typed_key_class_name();
|
||||
} else if (dict.is_typed_key()) {
|
||||
result += Variant::get_type_name((Variant::Type)dict.get_typed_key_builtin());
|
||||
} else {
|
||||
result += "Variant";
|
||||
}
|
||||
|
||||
result += ", ";
|
||||
|
||||
Ref<Script> value_script = dict.get_typed_value_script();
|
||||
if (value_script.is_valid()) {
|
||||
if (value_script->get_global_name() != StringName()) {
|
||||
result += value_script->get_global_name();
|
||||
} else if (!value_script->get_path().get_file().is_empty()) {
|
||||
result += value_script->get_path().get_file();
|
||||
} else {
|
||||
result += dict.get_typed_value_class_name();
|
||||
}
|
||||
} else if (dict.get_typed_value_class_name() != StringName()) {
|
||||
result += dict.get_typed_value_class_name();
|
||||
} else if (dict.is_typed_value()) {
|
||||
result += Variant::get_type_name((Variant::Type)dict.get_typed_value_builtin());
|
||||
} else {
|
||||
result += "Variant";
|
||||
}
|
||||
|
||||
result += "](";
|
||||
}
|
||||
|
||||
if (dict.is_empty()) {
|
||||
return "{}";
|
||||
}
|
||||
result += "{}";
|
||||
} else if (p_recursion_level > MAX_RECURSION_LEVEL) {
|
||||
result += "{...}";
|
||||
} else {
|
||||
result += "{";
|
||||
|
||||
if (p_recursion_level > MAX_RECURSION_LEVEL) {
|
||||
return "{...}";
|
||||
}
|
||||
List<Variant> keys;
|
||||
dict.get_key_list(&keys);
|
||||
keys.sort_custom<StringLikeVariantOrder>();
|
||||
|
||||
List<Variant> keys;
|
||||
dict.get_key_list(&keys);
|
||||
keys.sort();
|
||||
|
||||
String data;
|
||||
for (List<Variant>::Element *E = keys.front(); E; E = E->next()) {
|
||||
if (E->prev()) {
|
||||
data += ", ";
|
||||
for (List<Variant>::Element *E = keys.front(); E; E = E->next()) {
|
||||
if (E->prev()) {
|
||||
result += ", ";
|
||||
}
|
||||
result += _docvalue_from_variant(E->get(), p_recursion_level + 1) + ": " + _docvalue_from_variant(dict[E->get()], p_recursion_level + 1);
|
||||
}
|
||||
data += _docvalue_from_variant(E->get(), p_recursion_level + 1) + ": " + _docvalue_from_variant(dict[E->get()], p_recursion_level + 1);
|
||||
|
||||
result += "}";
|
||||
}
|
||||
|
||||
return "{" + data + "}";
|
||||
if (dict.is_typed()) {
|
||||
result += ")";
|
||||
}
|
||||
|
||||
return result;
|
||||
} break;
|
||||
case Variant::ARRAY: {
|
||||
const Array array = p_variant;
|
||||
String result;
|
||||
|
||||
if (array.get_typed_builtin() != Variant::NIL) {
|
||||
if (array.is_typed()) {
|
||||
result += "Array[";
|
||||
|
||||
Ref<Script> script = array.get_typed_script();
|
||||
|
|
@ -209,16 +267,18 @@ String GDScriptDocGen::_docvalue_from_variant(const Variant &p_variant, int p_re
|
|||
result += "[...]";
|
||||
} else {
|
||||
result += "[";
|
||||
|
||||
for (int i = 0; i < array.size(); i++) {
|
||||
if (i > 0) {
|
||||
result += ", ";
|
||||
}
|
||||
result += _docvalue_from_variant(array[i], p_recursion_level + 1);
|
||||
}
|
||||
|
||||
result += "]";
|
||||
}
|
||||
|
||||
if (array.get_typed_builtin() != Variant::NIL) {
|
||||
if (array.is_typed()) {
|
||||
result += ")";
|
||||
}
|
||||
|
||||
|
|
@ -229,7 +289,7 @@ String GDScriptDocGen::_docvalue_from_variant(const Variant &p_variant, int p_re
|
|||
}
|
||||
}
|
||||
|
||||
String GDScriptDocGen::_docvalue_from_expression(const GDP::ExpressionNode *p_expression) {
|
||||
String GDScriptDocGen::docvalue_from_expression(const GDP::ExpressionNode *p_expression) {
|
||||
ERR_FAIL_NULL_V(p_expression, String());
|
||||
|
||||
if (p_expression->is_constant) {
|
||||
|
|
@ -325,6 +385,7 @@ void GDScriptDocGen::_generate_docs(GDScript *p_script, const GDP::ClassNode *p_
|
|||
const_doc.name = const_name;
|
||||
const_doc.value = _docvalue_from_variant(m_const->initializer->reduced_value);
|
||||
const_doc.is_value_valid = true;
|
||||
_doctype_from_gdtype(m_const->get_datatype(), const_doc.type, const_doc.enumeration);
|
||||
const_doc.description = m_const->doc_data.description;
|
||||
const_doc.is_deprecated = m_const->doc_data.is_deprecated;
|
||||
const_doc.deprecated_message = m_const->doc_data.deprecated_message;
|
||||
|
|
@ -348,7 +409,9 @@ void GDScriptDocGen::_generate_docs(GDScript *p_script, const GDP::ClassNode *p_
|
|||
method_doc.experimental_message = m_func->doc_data.experimental_message;
|
||||
method_doc.qualifiers = m_func->is_static ? "static" : "";
|
||||
|
||||
if (m_func->return_type) {
|
||||
if (func_name == "_init") {
|
||||
method_doc.return_type = "void";
|
||||
} else if (m_func->return_type) {
|
||||
// `m_func->return_type->get_datatype()` is a metatype.
|
||||
_doctype_from_gdtype(m_func->get_datatype(), method_doc.return_type, method_doc.return_enum, true);
|
||||
} else if (!m_func->body->has_return) {
|
||||
|
|
@ -363,7 +426,7 @@ void GDScriptDocGen::_generate_docs(GDScript *p_script, const GDP::ClassNode *p_
|
|||
arg_doc.name = p->identifier->name;
|
||||
_doctype_from_gdtype(p->get_datatype(), arg_doc.type, arg_doc.enumeration);
|
||||
if (p->initializer != nullptr) {
|
||||
arg_doc.default_value = _docvalue_from_expression(p->initializer);
|
||||
arg_doc.default_value = docvalue_from_expression(p->initializer);
|
||||
}
|
||||
method_doc.arguments.push_back(arg_doc);
|
||||
}
|
||||
|
|
@ -432,7 +495,7 @@ void GDScriptDocGen::_generate_docs(GDScript *p_script, const GDP::ClassNode *p_
|
|||
}
|
||||
|
||||
if (m_var->initializer != nullptr) {
|
||||
prop_doc.default_value = _docvalue_from_expression(m_var->initializer);
|
||||
prop_doc.default_value = docvalue_from_expression(m_var->initializer);
|
||||
}
|
||||
|
||||
prop_doc.overridden = false;
|
||||
|
|
@ -459,6 +522,7 @@ void GDScriptDocGen::_generate_docs(GDScript *p_script, const GDP::ClassNode *p_
|
|||
const_doc.name = val.identifier->name;
|
||||
const_doc.value = _docvalue_from_variant(val.value);
|
||||
const_doc.is_value_valid = true;
|
||||
const_doc.type = "int";
|
||||
const_doc.enumeration = name;
|
||||
const_doc.description = val.doc_data.description;
|
||||
const_doc.is_deprecated = val.doc_data.is_deprecated;
|
||||
|
|
@ -481,6 +545,7 @@ void GDScriptDocGen::_generate_docs(GDScript *p_script, const GDP::ClassNode *p_
|
|||
const_doc.name = name;
|
||||
const_doc.value = _docvalue_from_variant(m_enum_val.value);
|
||||
const_doc.is_value_valid = true;
|
||||
const_doc.type = "int";
|
||||
const_doc.enumeration = "@unnamed_enums";
|
||||
const_doc.description = m_enum_val.doc_data.description;
|
||||
const_doc.is_deprecated = m_enum_val.doc_data.is_deprecated;
|
||||
|
|
@ -508,3 +573,14 @@ void GDScriptDocGen::generate_docs(GDScript *p_script, const GDP::ClassNode *p_c
|
|||
_generate_docs(p_script, p_class);
|
||||
singletons.clear();
|
||||
}
|
||||
|
||||
// This method is needed for the editor, since during autocompletion the script is not compiled, only analyzed.
|
||||
void GDScriptDocGen::doctype_from_gdtype(const GDType &p_gdtype, String &r_type, String &r_enum, bool p_is_return) {
|
||||
for (const KeyValue<StringName, ProjectSettings::AutoloadInfo> &E : ProjectSettings::get_singleton()->get_autoload_list()) {
|
||||
if (E.value.is_singleton) {
|
||||
singletons[E.value.path] = E.key;
|
||||
}
|
||||
}
|
||||
_doctype_from_gdtype(p_gdtype, r_type, r_enum, p_is_return);
|
||||
singletons.clear();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,11 +45,12 @@ class GDScriptDocGen {
|
|||
static String _get_class_name(const GDP::ClassNode &p_class);
|
||||
static void _doctype_from_gdtype(const GDType &p_gdtype, String &r_type, String &r_enum, bool p_is_return = false);
|
||||
static String _docvalue_from_variant(const Variant &p_variant, int p_recursion_level = 1);
|
||||
static String _docvalue_from_expression(const GDP::ExpressionNode *p_expression);
|
||||
static void _generate_docs(GDScript *p_script, const GDP::ClassNode *p_class);
|
||||
|
||||
public:
|
||||
static void generate_docs(GDScript *p_script, const GDP::ClassNode *p_class);
|
||||
static void doctype_from_gdtype(const GDType &p_gdtype, String &r_type, String &r_enum, bool p_is_return = false);
|
||||
static String docvalue_from_expression(const GDP::ExpressionNode *p_expression);
|
||||
};
|
||||
|
||||
#endif // GDSCRIPT_DOCGEN_H
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@
|
|||
#include "core/config/project_settings.h"
|
||||
#include "editor/editor_settings.h"
|
||||
#include "editor/themes/editor_theme_manager.h"
|
||||
#include "scene/gui/text_edit.h"
|
||||
|
||||
Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_line) {
|
||||
Dictionary color_map;
|
||||
|
|
@ -93,7 +94,7 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_l
|
|||
in_region = color_region_cache[p_line - 1];
|
||||
}
|
||||
|
||||
const String &str = text_edit->get_line(p_line);
|
||||
const String &str = text_edit->get_line_with_ime(p_line);
|
||||
const int line_length = str.length();
|
||||
Color prev_color;
|
||||
|
||||
|
|
@ -163,7 +164,7 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_l
|
|||
}
|
||||
if (from + end_key_length > line_length) {
|
||||
// If it's key length and there is a '\', dont skip to highlight esc chars.
|
||||
if (str.find("\\", from) >= 0) {
|
||||
if (str.find_char('\\', from) >= 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -236,7 +237,7 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_l
|
|||
for (; from < line_length; from++) {
|
||||
if (line_length - from < end_key_length) {
|
||||
// Don't break if '\' to highlight esc chars.
|
||||
if (str.find("\\", from) < 0) {
|
||||
if (str.find_char('\\', from) < 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -350,15 +351,15 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_l
|
|||
|
||||
// Special cases for numbers.
|
||||
if (in_number && !is_a_digit) {
|
||||
if (str[j] == 'b' && str[j - 1] == '0') {
|
||||
if ((str[j] == 'b' || str[j] == 'B') && str[j - 1] == '0') {
|
||||
is_bin_notation = true;
|
||||
} else if (str[j] == 'x' && str[j - 1] == '0') {
|
||||
} else if ((str[j] == 'x' || str[j] == 'X') && str[j - 1] == '0') {
|
||||
is_hex_notation = true;
|
||||
} else if (!((str[j] == '-' || str[j] == '+') && str[j - 1] == 'e' && !prev_is_digit) &&
|
||||
!(str[j] == '_' && (prev_is_digit || str[j - 1] == 'b' || str[j - 1] == 'x' || str[j - 1] == '.')) &&
|
||||
!(str[j] == 'e' && (prev_is_digit || str[j - 1] == '_')) &&
|
||||
} else if (!((str[j] == '-' || str[j] == '+') && (str[j - 1] == 'e' || str[j - 1] == 'E') && !prev_is_digit) &&
|
||||
!(str[j] == '_' && (prev_is_digit || str[j - 1] == 'b' || str[j - 1] == 'B' || str[j - 1] == 'x' || str[j - 1] == 'X' || str[j - 1] == '.')) &&
|
||||
!((str[j] == 'e' || str[j] == 'E') && (prev_is_digit || str[j - 1] == '_')) &&
|
||||
!(str[j] == '.' && (prev_is_digit || (!prev_is_binary_op && (j > 0 && (str[j - 1] == '_' || str[j - 1] == '-' || str[j - 1] == '+' || str[j - 1] == '~'))))) &&
|
||||
!((str[j] == '-' || str[j] == '+' || str[j] == '~') && !is_binary_op && !prev_is_binary_op && str[j - 1] != 'e')) {
|
||||
!((str[j] == '-' || str[j] == '+' || str[j] == '~') && !is_binary_op && !prev_is_binary_op && str[j - 1] != 'e' && str[j - 1] != 'E')) {
|
||||
/* This condition continues number highlighting in special cases.
|
||||
1st row: '+' or '-' after scientific notation (like 3e-4);
|
||||
2nd row: '_' as a numeric separator;
|
||||
|
|
@ -560,12 +561,17 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_l
|
|||
}
|
||||
}
|
||||
|
||||
// Keep symbol color for binary '&&'. In the case of '&&&' use StringName color for the last ampersand.
|
||||
// Set color of StringName, keeping symbol color for binary '&&' and '&'.
|
||||
if (!in_string_name && in_region == -1 && str[j] == '&' && !is_binary_op) {
|
||||
if (j >= 2 && str[j - 1] == '&' && str[j - 2] != '&' && prev_is_binary_op) {
|
||||
is_binary_op = true;
|
||||
} else if (j == 0 || (j > 0 && str[j - 1] != '&') || prev_is_binary_op) {
|
||||
if (j + 1 <= line_length - 1 && (str[j + 1] == '\'' || str[j + 1] == '"')) {
|
||||
in_string_name = true;
|
||||
// Cover edge cases of i.e. '+&""' and '&&&""', so the StringName is properly colored.
|
||||
if (prev_is_binary_op && j >= 2 && str[j - 1] == '&' && str[j - 2] != '&') {
|
||||
in_string_name = false;
|
||||
is_binary_op = true;
|
||||
}
|
||||
} else {
|
||||
is_binary_op = true;
|
||||
}
|
||||
} else if (in_region != -1 || is_a_symbol) {
|
||||
in_string_name = false;
|
||||
|
|
@ -701,7 +707,9 @@ void GDScriptSyntaxHighlighter::_update_cache() {
|
|||
List<StringName> types;
|
||||
ClassDB::get_class_list(&types);
|
||||
for (const StringName &E : types) {
|
||||
class_names[E] = types_color;
|
||||
if (ClassDB::is_class_exposed(E)) {
|
||||
class_names[E] = types_color;
|
||||
}
|
||||
}
|
||||
|
||||
/* User types. */
|
||||
|
|
@ -813,7 +821,7 @@ void GDScriptSyntaxHighlighter::_update_cache() {
|
|||
if (E.usage & PROPERTY_USAGE_CATEGORY || E.usage & PROPERTY_USAGE_GROUP || E.usage & PROPERTY_USAGE_SUBGROUP) {
|
||||
continue;
|
||||
}
|
||||
if (prop_name.contains("/")) {
|
||||
if (prop_name.contains_char('/')) {
|
||||
continue;
|
||||
}
|
||||
member_keywords[prop_name] = member_variable_color;
|
||||
|
|
@ -852,6 +860,7 @@ void GDScriptSyntaxHighlighter::_update_cache() {
|
|||
comment_marker_colors[COMMENT_MARKER_NOTICE] = Color(0.24, 0.54, 0.09);
|
||||
}
|
||||
|
||||
// TODO: Move to editor_settings.cpp
|
||||
EDITOR_DEF("text_editor/theme/highlighting/gdscript/function_definition_color", function_definition_color);
|
||||
EDITOR_DEF("text_editor/theme/highlighting/gdscript/global_function_color", global_function_color);
|
||||
EDITOR_DEF("text_editor/theme/highlighting/gdscript/node_path_color", node_path_color);
|
||||
|
|
|
|||
|
|
@ -32,7 +32,6 @@
|
|||
#define GDSCRIPT_HIGHLIGHTER_H
|
||||
|
||||
#include "editor/plugins/script_editor_plugin.h"
|
||||
#include "scene/gui/text_edit.h"
|
||||
|
||||
class GDScriptSyntaxHighlighter : public EditorSyntaxHighlighter {
|
||||
GDCLASS(GDScriptSyntaxHighlighter, EditorSyntaxHighlighter)
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ void GDScriptEditorTranslationParserPlugin::get_recognized_extensions(List<Strin
|
|||
GDScriptLanguage::get_singleton()->get_recognized_extensions(r_extensions);
|
||||
}
|
||||
|
||||
Error GDScriptEditorTranslationParserPlugin::parse_file(const String &p_path, Vector<String> *r_ids, Vector<Vector<String>> *r_ids_ctx_plural) {
|
||||
Error GDScriptEditorTranslationParserPlugin::parse_file(const String &p_path, Vector<Vector<String>> *r_translations) {
|
||||
// Extract all translatable strings using the parsed tree from GDScriptParser.
|
||||
// The strategy is to find all ExpressionNode and AssignmentNode from the tree and extract strings if relevant, i.e
|
||||
// Search strings in ExpressionNode -> CallNode -> tr(), set_text(), set_placeholder() etc.
|
||||
|
|
@ -49,8 +49,8 @@ Error GDScriptEditorTranslationParserPlugin::parse_file(const String &p_path, Ve
|
|||
Ref<Resource> loaded_res = ResourceLoader::load(p_path, "", ResourceFormatLoader::CACHE_MODE_REUSE, &err);
|
||||
ERR_FAIL_COND_V_MSG(err, err, "Failed to load " + p_path);
|
||||
|
||||
ids = r_ids;
|
||||
ids_ctx_plural = r_ids_ctx_plural;
|
||||
translations = r_translations;
|
||||
|
||||
Ref<GDScript> gdscript = loaded_res;
|
||||
String source_code = gdscript->get_source_code();
|
||||
|
||||
|
|
@ -62,16 +62,81 @@ Error GDScriptEditorTranslationParserPlugin::parse_file(const String &p_path, Ve
|
|||
err = analyzer.analyze();
|
||||
ERR_FAIL_COND_V_MSG(err, err, "Failed to analyze GDScript with GDScriptAnalyzer.");
|
||||
|
||||
comment_data = &parser.comment_data;
|
||||
|
||||
// Traverse through the parsed tree from GDScriptParser.
|
||||
GDScriptParser::ClassNode *c = parser.get_tree();
|
||||
_traverse_class(c);
|
||||
|
||||
comment_data = nullptr;
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
bool GDScriptEditorTranslationParserPlugin::_is_constant_string(const GDScriptParser::ExpressionNode *p_expression) {
|
||||
ERR_FAIL_NULL_V(p_expression, false);
|
||||
return p_expression->is_constant && (p_expression->reduced_value.get_type() == Variant::STRING || p_expression->reduced_value.get_type() == Variant::STRING_NAME);
|
||||
return p_expression->is_constant && p_expression->reduced_value.is_string();
|
||||
}
|
||||
|
||||
String GDScriptEditorTranslationParserPlugin::_parse_comment(int p_line, bool &r_skip) const {
|
||||
// Parse inline comment.
|
||||
if (comment_data->has(p_line)) {
|
||||
const String stripped_comment = comment_data->get(p_line).comment.trim_prefix("#").strip_edges();
|
||||
|
||||
if (stripped_comment.begins_with("TRANSLATORS:")) {
|
||||
return stripped_comment.trim_prefix("TRANSLATORS:").strip_edges(true, false);
|
||||
}
|
||||
if (stripped_comment == "NO_TRANSLATE" || stripped_comment.begins_with("NO_TRANSLATE:")) {
|
||||
r_skip = true;
|
||||
return String();
|
||||
}
|
||||
}
|
||||
|
||||
// Parse multiline comment.
|
||||
String multiline_comment;
|
||||
for (int line = p_line - 1; comment_data->has(line) && comment_data->get(line).new_line; line--) {
|
||||
const String stripped_comment = comment_data->get(line).comment.trim_prefix("#").strip_edges();
|
||||
|
||||
if (stripped_comment.is_empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (multiline_comment.is_empty()) {
|
||||
multiline_comment = stripped_comment;
|
||||
} else {
|
||||
multiline_comment = stripped_comment + "\n" + multiline_comment;
|
||||
}
|
||||
|
||||
if (stripped_comment.begins_with("TRANSLATORS:")) {
|
||||
return multiline_comment.trim_prefix("TRANSLATORS:").strip_edges(true, false);
|
||||
}
|
||||
if (stripped_comment == "NO_TRANSLATE" || stripped_comment.begins_with("NO_TRANSLATE:")) {
|
||||
r_skip = true;
|
||||
return String();
|
||||
}
|
||||
}
|
||||
|
||||
return String();
|
||||
}
|
||||
|
||||
void GDScriptEditorTranslationParserPlugin::_add_id(const String &p_id, int p_line) {
|
||||
bool skip = false;
|
||||
const String comment = _parse_comment(p_line, skip);
|
||||
if (skip) {
|
||||
return;
|
||||
}
|
||||
|
||||
translations->push_back({ p_id, String(), String(), comment });
|
||||
}
|
||||
|
||||
void GDScriptEditorTranslationParserPlugin::_add_id_ctx_plural(const Vector<String> &p_id_ctx_plural, int p_line) {
|
||||
bool skip = false;
|
||||
const String comment = _parse_comment(p_line, skip);
|
||||
if (skip) {
|
||||
return;
|
||||
}
|
||||
|
||||
translations->push_back({ p_id_ctx_plural[0], p_id_ctx_plural[1], p_id_ctx_plural[2], comment });
|
||||
}
|
||||
|
||||
void GDScriptEditorTranslationParserPlugin::_traverse_class(const GDScriptParser::ClassNode *p_class) {
|
||||
|
|
@ -253,7 +318,7 @@ void GDScriptEditorTranslationParserPlugin::_assess_assignment(const GDScriptPar
|
|||
|
||||
if (assignee_name != StringName() && assignment_patterns.has(assignee_name) && _is_constant_string(p_assignment->assigned_value)) {
|
||||
// If the assignment is towards one of the extract patterns (text, tooltip_text etc.), and the value is a constant string, we collect the string.
|
||||
ids->push_back(p_assignment->assigned_value->reduced_value);
|
||||
_add_id(p_assignment->assigned_value->reduced_value, p_assignment->assigned_value->start_line);
|
||||
} else if (assignee_name == fd_filters) {
|
||||
// Extract from `get_node("FileDialog").filters = <filter array>`.
|
||||
_extract_fd_filter_array(p_assignment->assigned_value);
|
||||
|
|
@ -287,7 +352,7 @@ void GDScriptEditorTranslationParserPlugin::_assess_call(const GDScriptParser::C
|
|||
}
|
||||
}
|
||||
if (extract_id_ctx_plural) {
|
||||
ids_ctx_plural->push_back(id_ctx_plural);
|
||||
_add_id_ctx_plural(id_ctx_plural, p_call->start_line);
|
||||
}
|
||||
} else if (function_name == trn_func || function_name == atrn_func) {
|
||||
// Extract from `tr_n(id, plural, n, ctx)` or `atr_n(id, plural, n, ctx)`.
|
||||
|
|
@ -307,20 +372,20 @@ void GDScriptEditorTranslationParserPlugin::_assess_call(const GDScriptParser::C
|
|||
}
|
||||
}
|
||||
if (extract_id_ctx_plural) {
|
||||
ids_ctx_plural->push_back(id_ctx_plural);
|
||||
_add_id_ctx_plural(id_ctx_plural, p_call->start_line);
|
||||
}
|
||||
} else if (first_arg_patterns.has(function_name)) {
|
||||
if (!p_call->arguments.is_empty() && _is_constant_string(p_call->arguments[0])) {
|
||||
ids->push_back(p_call->arguments[0]->reduced_value);
|
||||
_add_id(p_call->arguments[0]->reduced_value, p_call->arguments[0]->start_line);
|
||||
}
|
||||
} else if (second_arg_patterns.has(function_name)) {
|
||||
if (p_call->arguments.size() > 1 && _is_constant_string(p_call->arguments[1])) {
|
||||
ids->push_back(p_call->arguments[1]->reduced_value);
|
||||
_add_id(p_call->arguments[1]->reduced_value, p_call->arguments[1]->start_line);
|
||||
}
|
||||
} else if (function_name == fd_add_filter) {
|
||||
// Extract the 'JPE Images' in this example - get_node("FileDialog").add_filter("*.jpg; JPE Images").
|
||||
if (!p_call->arguments.is_empty()) {
|
||||
_extract_fd_filter_string(p_call->arguments[0]);
|
||||
_extract_fd_filter_string(p_call->arguments[0], p_call->arguments[0]->start_line);
|
||||
}
|
||||
} else if (function_name == fd_set_filter) {
|
||||
// Extract from `get_node("FileDialog").set_filters(<filter array>)`.
|
||||
|
|
@ -330,12 +395,12 @@ void GDScriptEditorTranslationParserPlugin::_assess_call(const GDScriptParser::C
|
|||
}
|
||||
}
|
||||
|
||||
void GDScriptEditorTranslationParserPlugin::_extract_fd_filter_string(const GDScriptParser::ExpressionNode *p_expression) {
|
||||
void GDScriptEditorTranslationParserPlugin::_extract_fd_filter_string(const GDScriptParser::ExpressionNode *p_expression, int p_line) {
|
||||
// Extract the name in "extension ; name".
|
||||
if (_is_constant_string(p_expression)) {
|
||||
PackedStringArray arr = p_expression->reduced_value.operator String().split(";", true);
|
||||
ERR_FAIL_COND_MSG(arr.size() != 2, "Argument for setting FileDialog has bad format.");
|
||||
ids->push_back(arr[1].strip_edges());
|
||||
_add_id(arr[1].strip_edges(), p_line);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -355,7 +420,7 @@ void GDScriptEditorTranslationParserPlugin::_extract_fd_filter_array(const GDScr
|
|||
|
||||
if (array_node) {
|
||||
for (int i = 0; i < array_node->elements.size(); i++) {
|
||||
_extract_fd_filter_string(array_node->elements[i]);
|
||||
_extract_fd_filter_string(array_node->elements[i], array_node->elements[i]->start_line);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,15 +32,18 @@
|
|||
#define GDSCRIPT_TRANSLATION_PARSER_PLUGIN_H
|
||||
|
||||
#include "../gdscript_parser.h"
|
||||
#include "../gdscript_tokenizer.h"
|
||||
|
||||
#include "core/templates/hash_map.h"
|
||||
#include "core/templates/hash_set.h"
|
||||
#include "editor/editor_translation_parser.h"
|
||||
|
||||
class GDScriptEditorTranslationParserPlugin : public EditorTranslationParserPlugin {
|
||||
GDCLASS(GDScriptEditorTranslationParserPlugin, EditorTranslationParserPlugin);
|
||||
|
||||
Vector<String> *ids = nullptr;
|
||||
Vector<Vector<String>> *ids_ctx_plural = nullptr;
|
||||
const HashMap<int, GDScriptTokenizer::CommentData> *comment_data = nullptr;
|
||||
|
||||
Vector<Vector<String>> *translations = nullptr;
|
||||
|
||||
// List of patterns used for extracting translation strings.
|
||||
StringName tr_func = "tr";
|
||||
|
|
@ -57,6 +60,11 @@ class GDScriptEditorTranslationParserPlugin : public EditorTranslationParserPlug
|
|||
|
||||
static bool _is_constant_string(const GDScriptParser::ExpressionNode *p_expression);
|
||||
|
||||
String _parse_comment(int p_line, bool &r_skip) const;
|
||||
|
||||
void _add_id(const String &p_id, int p_line);
|
||||
void _add_id_ctx_plural(const Vector<String> &p_id_ctx_plural, int p_line);
|
||||
|
||||
void _traverse_class(const GDScriptParser::ClassNode *p_class);
|
||||
void _traverse_function(const GDScriptParser::FunctionNode *p_func);
|
||||
void _traverse_block(const GDScriptParser::SuiteNode *p_suite);
|
||||
|
|
@ -65,11 +73,11 @@ class GDScriptEditorTranslationParserPlugin : public EditorTranslationParserPlug
|
|||
void _assess_assignment(const GDScriptParser::AssignmentNode *p_assignment);
|
||||
void _assess_call(const GDScriptParser::CallNode *p_call);
|
||||
|
||||
void _extract_fd_filter_string(const GDScriptParser::ExpressionNode *p_expression);
|
||||
void _extract_fd_filter_string(const GDScriptParser::ExpressionNode *p_expression, int p_line);
|
||||
void _extract_fd_filter_array(const GDScriptParser::ExpressionNode *p_expression);
|
||||
|
||||
public:
|
||||
virtual Error parse_file(const String &p_path, Vector<String> *r_ids, Vector<Vector<String>> *r_ids_ctx_plural) override;
|
||||
virtual Error parse_file(const String &p_path, Vector<Vector<String>> *r_translations) override;
|
||||
virtual void get_recognized_extensions(List<String> *r_extensions) const override;
|
||||
|
||||
GDScriptEditorTranslationParserPlugin();
|
||||
|
|
|
|||
|
|
@ -1,16 +1,8 @@
|
|||
#!/usr/bin/env python
|
||||
from misc.utility.scons_hints import *
|
||||
|
||||
Import("env")
|
||||
|
||||
import editor.template_builders as build_template_gd
|
||||
|
||||
env["BUILDERS"]["MakeGDTemplateBuilder"] = Builder(
|
||||
action=env.Run(build_template_gd.make_templates),
|
||||
suffix=".h",
|
||||
src_suffix=".gd",
|
||||
)
|
||||
|
||||
# Template files
|
||||
templates_sources = Glob("*/*.gd")
|
||||
|
||||
env.Alias("editor_template_gd", [env.MakeGDTemplateBuilder("templates.gen.h", templates_sources)])
|
||||
env.CommandNoCache("templates.gen.h", Glob("*/*.gd"), env.Run(build_template_gd.make_templates))
|
||||
|
|
|
|||
|
|
@ -50,12 +50,12 @@
|
|||
#include "core/config/project_settings.h"
|
||||
#include "core/core_constants.h"
|
||||
#include "core/io/file_access.h"
|
||||
#include "core/io/file_access_encrypted.h"
|
||||
#include "core/os/os.h"
|
||||
|
||||
#include "scene/resources/packed_scene.h"
|
||||
#include "scene/scene_string_names.h"
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
#include "core/extension/gdextension_manager.h"
|
||||
#include "editor/editor_paths.h"
|
||||
#endif
|
||||
|
||||
|
|
@ -74,9 +74,16 @@ bool GDScriptNativeClass::_get(const StringName &p_name, Variant &r_ret) const {
|
|||
if (ok) {
|
||||
r_ret = v;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
MethodBind *method = ClassDB::get_method(name, p_name);
|
||||
if (method && method->is_static()) {
|
||||
// Native static method.
|
||||
r_ret = Callable(this, p_name);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void GDScriptNativeClass::_bind_methods() {
|
||||
|
|
@ -136,7 +143,7 @@ void GDScript::_super_implicit_constructor(GDScript *p_script, GDScriptInstance
|
|||
}
|
||||
}
|
||||
ERR_FAIL_NULL(p_script->implicit_initializer);
|
||||
if (likely(valid)) {
|
||||
if (likely(p_script->valid)) {
|
||||
p_script->implicit_initializer->call(p_instance, nullptr, 0, r_error);
|
||||
} else {
|
||||
r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
|
||||
|
|
@ -247,7 +254,7 @@ Variant GDScript::_new(const Variant **p_args, int p_argcount, Callable::CallErr
|
|||
|
||||
bool GDScript::can_instantiate() const {
|
||||
#ifdef TOOLS_ENABLED
|
||||
return valid && (tool || ScriptServer::is_scripting_enabled());
|
||||
return valid && (tool || ScriptServer::is_scripting_enabled()) && !Engine::get_singleton()->is_recovery_mode_hint();
|
||||
#else
|
||||
return valid;
|
||||
#endif
|
||||
|
|
@ -470,23 +477,25 @@ void GDScript::_update_exports_values(HashMap<StringName, Variant> &values, List
|
|||
}
|
||||
}
|
||||
|
||||
void GDScript::_add_doc(const DocData::ClassDoc &p_inner_class) {
|
||||
if (_owner) { // Only the top-level class stores doc info
|
||||
_owner->_add_doc(p_inner_class);
|
||||
} else { // Remove old docs, add new
|
||||
void GDScript::_add_doc(const DocData::ClassDoc &p_doc) {
|
||||
doc_class_name = p_doc.name;
|
||||
if (_owner) { // Only the top-level class stores doc info.
|
||||
_owner->_add_doc(p_doc);
|
||||
} else { // Remove old docs, add new.
|
||||
for (int i = 0; i < docs.size(); i++) {
|
||||
if (docs[i].name == p_inner_class.name) {
|
||||
if (docs[i].name == p_doc.name) {
|
||||
docs.remove_at(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
docs.append(p_inner_class);
|
||||
docs.append(p_doc);
|
||||
}
|
||||
}
|
||||
|
||||
void GDScript::_clear_doc() {
|
||||
docs.clear();
|
||||
doc_class_name = StringName();
|
||||
doc = DocData::ClassDoc();
|
||||
docs.clear();
|
||||
}
|
||||
|
||||
String GDScript::get_class_icon_path() const {
|
||||
|
|
@ -691,10 +700,16 @@ void GDScript::_static_default_init() {
|
|||
continue;
|
||||
}
|
||||
if (type.builtin_type == Variant::ARRAY && type.has_container_element_type(0)) {
|
||||
const GDScriptDataType element_type = type.get_container_element_type(0);
|
||||
Array default_value;
|
||||
const GDScriptDataType &element_type = type.get_container_element_type(0);
|
||||
default_value.set_typed(element_type.builtin_type, element_type.native_type, element_type.script_type);
|
||||
static_variables.write[E.value.index] = default_value;
|
||||
} else if (type.builtin_type == Variant::DICTIONARY && type.has_container_element_types()) {
|
||||
const GDScriptDataType key_type = type.get_container_element_type_or_variant(0);
|
||||
const GDScriptDataType value_type = type.get_container_element_type_or_variant(1);
|
||||
Dictionary default_value;
|
||||
default_value.set_typed(key_type.builtin_type, key_type.native_type, key_type.script_type, value_type.builtin_type, value_type.native_type, value_type.script_type);
|
||||
static_variables.write[E.value.index] = default_value;
|
||||
} else {
|
||||
Variant default_value;
|
||||
Callable::CallError err;
|
||||
|
|
@ -878,6 +893,11 @@ Error GDScript::reload(bool p_keep_state) {
|
|||
if (can_run && p_keep_state) {
|
||||
_restore_old_static_data();
|
||||
}
|
||||
|
||||
if (p_keep_state) {
|
||||
// Update the properties in the inspector.
|
||||
update_exports();
|
||||
}
|
||||
#endif
|
||||
|
||||
reloading = false;
|
||||
|
|
@ -904,7 +924,7 @@ void GDScript::get_members(HashSet<StringName> *p_members) {
|
|||
}
|
||||
}
|
||||
|
||||
const Variant GDScript::get_rpc_config() const {
|
||||
Variant GDScript::get_rpc_config() const {
|
||||
return rpc_config;
|
||||
}
|
||||
|
||||
|
|
@ -952,7 +972,8 @@ bool GDScript::_get(const StringName &p_name, Variant &r_ret) const {
|
|||
if (E) {
|
||||
if (likely(top->valid) && E->value.getter) {
|
||||
Callable::CallError ce;
|
||||
r_ret = const_cast<GDScript *>(this)->callp(E->value.getter, nullptr, 0, ce);
|
||||
const Variant ret = const_cast<GDScript *>(this)->callp(E->value.getter, nullptr, 0, ce);
|
||||
r_ret = (ce.error == Callable::CallError::CALL_OK) ? ret : Variant();
|
||||
return true;
|
||||
}
|
||||
r_ret = top->static_variables[E->value.index];
|
||||
|
|
@ -1102,7 +1123,7 @@ Error GDScript::load_source_code(const String &p_path) {
|
|||
w[len] = 0;
|
||||
|
||||
String s;
|
||||
if (s.parse_utf8((const char *)w) != OK) {
|
||||
if (s.parse_utf8((const char *)w, len) != OK) {
|
||||
ERR_FAIL_V_MSG(ERR_INVALID_DATA, "Script '" + p_path + "' contains invalid unicode (UTF-8), so it was not loaded. Please ensure that scripts are saved in valid UTF-8 unicode.");
|
||||
}
|
||||
|
||||
|
|
@ -1725,10 +1746,9 @@ bool GDScriptInstance::get(const StringName &p_name, Variant &r_ret) const {
|
|||
if (E) {
|
||||
if (likely(script->valid) && E->value.getter) {
|
||||
Callable::CallError err;
|
||||
r_ret = const_cast<GDScriptInstance *>(this)->callp(E->value.getter, nullptr, 0, err);
|
||||
if (err.error == Callable::CallError::CALL_OK) {
|
||||
return true;
|
||||
}
|
||||
const Variant ret = const_cast<GDScriptInstance *>(this)->callp(E->value.getter, nullptr, 0, err);
|
||||
r_ret = (err.error == Callable::CallError::CALL_OK) ? ret : Variant();
|
||||
return true;
|
||||
}
|
||||
r_ret = members[E->value.index];
|
||||
return true;
|
||||
|
|
@ -1750,7 +1770,8 @@ bool GDScriptInstance::get(const StringName &p_name, Variant &r_ret) const {
|
|||
if (E) {
|
||||
if (likely(sptr->valid) && E->value.getter) {
|
||||
Callable::CallError ce;
|
||||
r_ret = const_cast<GDScript *>(sptr)->callp(E->value.getter, nullptr, 0, ce);
|
||||
const Variant ret = const_cast<GDScript *>(sptr)->callp(E->value.getter, nullptr, 0, ce);
|
||||
r_ret = (ce.error == Callable::CallError::CALL_OK) ? ret : Variant();
|
||||
return true;
|
||||
}
|
||||
r_ret = sptr->static_variables[E->value.index];
|
||||
|
|
@ -1793,7 +1814,7 @@ bool GDScriptInstance::get(const StringName &p_name, Variant &r_ret) const {
|
|||
const Variant *args[1] = { &name };
|
||||
|
||||
Callable::CallError err;
|
||||
Variant ret = const_cast<GDScriptFunction *>(E->value)->call(const_cast<GDScriptInstance *>(this), (const Variant **)args, 1, err);
|
||||
Variant ret = E->value->call(const_cast<GDScriptInstance *>(this), (const Variant **)args, 1, err);
|
||||
if (err.error == Callable::CallError::CALL_OK && ret.get_type() != Variant::NIL) {
|
||||
r_ret = ret;
|
||||
return true;
|
||||
|
|
@ -1821,14 +1842,14 @@ Variant::Type GDScriptInstance::get_property_type(const StringName &p_name, bool
|
|||
}
|
||||
|
||||
void GDScriptInstance::validate_property(PropertyInfo &p_property) const {
|
||||
Variant property = (Dictionary)p_property;
|
||||
const Variant *args[1] = { &property };
|
||||
|
||||
const GDScript *sptr = script.ptr();
|
||||
while (sptr) {
|
||||
if (likely(sptr->valid)) {
|
||||
HashMap<StringName, GDScriptFunction *>::ConstIterator E = sptr->member_functions.find(GDScriptLanguage::get_singleton()->strings._validate_property);
|
||||
if (E) {
|
||||
Variant property = (Dictionary)p_property;
|
||||
const Variant *args[1] = { &property };
|
||||
|
||||
Callable::CallError err;
|
||||
Variant ret = E->value->call(const_cast<GDScriptInstance *>(this), args, 1, err);
|
||||
if (err.error == Callable::CallError::CALL_OK) {
|
||||
|
|
@ -1852,7 +1873,7 @@ void GDScriptInstance::get_property_list(List<PropertyInfo> *p_properties) const
|
|||
HashMap<StringName, GDScriptFunction *>::ConstIterator E = sptr->member_functions.find(GDScriptLanguage::get_singleton()->strings._get_property_list);
|
||||
if (E) {
|
||||
Callable::CallError err;
|
||||
Variant ret = const_cast<GDScriptFunction *>(E->value)->call(const_cast<GDScriptInstance *>(this), nullptr, 0, err);
|
||||
Variant ret = E->value->call(const_cast<GDScriptInstance *>(this), nullptr, 0, err);
|
||||
if (err.error == Callable::CallError::CALL_OK) {
|
||||
ERR_FAIL_COND_MSG(ret.get_type() != Variant::ARRAY, "Wrong type for _get_property_list, must be an array of dictionaries.");
|
||||
|
||||
|
|
@ -2176,9 +2197,26 @@ void GDScriptLanguage::_add_global(const StringName &p_name, const Variant &p_va
|
|||
global_array.write[globals[p_name]] = p_value;
|
||||
return;
|
||||
}
|
||||
globals[p_name] = global_array.size();
|
||||
global_array.push_back(p_value);
|
||||
_global_array = global_array.ptrw();
|
||||
|
||||
if (global_array_empty_indexes.size()) {
|
||||
int index = global_array_empty_indexes[global_array_empty_indexes.size() - 1];
|
||||
globals[p_name] = index;
|
||||
global_array.write[index] = p_value;
|
||||
global_array_empty_indexes.resize(global_array_empty_indexes.size() - 1);
|
||||
} else {
|
||||
globals[p_name] = global_array.size();
|
||||
global_array.push_back(p_value);
|
||||
_global_array = global_array.ptrw();
|
||||
}
|
||||
}
|
||||
|
||||
void GDScriptLanguage::_remove_global(const StringName &p_name) {
|
||||
if (!globals.has(p_name)) {
|
||||
return;
|
||||
}
|
||||
global_array_empty_indexes.push_back(globals[p_name]);
|
||||
global_array.write[globals[p_name]] = Variant::NIL;
|
||||
globals.erase(p_name);
|
||||
}
|
||||
|
||||
void GDScriptLanguage::add_global_constant(const StringName &p_variable, const Variant &p_value) {
|
||||
|
|
@ -2236,11 +2274,40 @@ void GDScriptLanguage::init() {
|
|||
_add_global(E.name, E.ptr);
|
||||
}
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
if (Engine::get_singleton()->is_editor_hint()) {
|
||||
GDExtensionManager::get_singleton()->connect("extension_loaded", callable_mp(this, &GDScriptLanguage::_extension_loaded));
|
||||
GDExtensionManager::get_singleton()->connect("extension_unloading", callable_mp(this, &GDScriptLanguage::_extension_unloading));
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef TESTS_ENABLED
|
||||
GDScriptTests::GDScriptTestRunner::handle_cmdline();
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
void GDScriptLanguage::_extension_loaded(const Ref<GDExtension> &p_extension) {
|
||||
List<StringName> class_list;
|
||||
ClassDB::get_extension_class_list(p_extension, &class_list);
|
||||
for (const StringName &n : class_list) {
|
||||
if (globals.has(n)) {
|
||||
continue;
|
||||
}
|
||||
Ref<GDScriptNativeClass> nc = memnew(GDScriptNativeClass(n));
|
||||
_add_global(n, nc);
|
||||
}
|
||||
}
|
||||
|
||||
void GDScriptLanguage::_extension_unloading(const Ref<GDExtension> &p_extension) {
|
||||
List<StringName> class_list;
|
||||
ClassDB::get_extension_class_list(p_extension, &class_list);
|
||||
for (const StringName &n : class_list) {
|
||||
_remove_global(n);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
String GDScriptLanguage::get_type() const {
|
||||
return "GDScript";
|
||||
}
|
||||
|
|
@ -2486,11 +2553,11 @@ void GDScriptLanguage::reload_all_scripts() {
|
|||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endif // TOOLS_ENABLED
|
||||
}
|
||||
|
||||
reload_scripts(scripts, true);
|
||||
#endif
|
||||
#endif // DEBUG_ENABLED
|
||||
}
|
||||
|
||||
void GDScriptLanguage::reload_scripts(const Array &p_scripts, bool p_soft_reload) {
|
||||
|
|
@ -2503,7 +2570,7 @@ void GDScriptLanguage::reload_scripts(const Array &p_scripts, bool p_soft_reload
|
|||
SelfList<GDScript> *elem = script_list.first();
|
||||
while (elem) {
|
||||
// Scripts will reload all subclasses, so only reload root scripts.
|
||||
if (elem->self()->is_root_script() && elem->self()->get_path().is_resource_file()) {
|
||||
if (elem->self()->is_root_script() && !elem->self()->get_path().is_empty()) {
|
||||
scripts.push_back(Ref<GDScript>(elem->self())); //cast to gdscript to avoid being erased by accident
|
||||
}
|
||||
elem = elem->next();
|
||||
|
|
@ -2560,7 +2627,7 @@ void GDScriptLanguage::reload_scripts(const Array &p_scripts, bool p_soft_reload
|
|||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif // TOOLS_ENABLED
|
||||
|
||||
for (const KeyValue<ObjectID, List<Pair<StringName, Variant>>> &F : scr->pending_reload_state) {
|
||||
map[F.key] = F.value; //pending to reload, use this one instead
|
||||
|
|
@ -2571,7 +2638,19 @@ void GDScriptLanguage::reload_scripts(const Array &p_scripts, bool p_soft_reload
|
|||
for (KeyValue<Ref<GDScript>, HashMap<ObjectID, List<Pair<StringName, Variant>>>> &E : to_reload) {
|
||||
Ref<GDScript> scr = E.key;
|
||||
print_verbose("GDScript: Reloading: " + scr->get_path());
|
||||
scr->load_source_code(scr->get_path());
|
||||
if (scr->is_built_in()) {
|
||||
// TODO: It would be nice to do it more efficiently than loading the whole scene again.
|
||||
Ref<PackedScene> scene = ResourceLoader::load(scr->get_path().get_slice("::", 0), "", ResourceFormatLoader::CACHE_MODE_IGNORE_DEEP);
|
||||
ERR_CONTINUE(scene.is_null());
|
||||
|
||||
Ref<SceneState> state = scene->get_state();
|
||||
Ref<GDScript> fresh = state->get_sub_resource(scr->get_path());
|
||||
ERR_CONTINUE(fresh.is_null());
|
||||
|
||||
scr->set_source_code(fresh->get_source_code());
|
||||
} else {
|
||||
scr->load_source_code(scr->get_path());
|
||||
}
|
||||
scr->reload(p_soft_reload);
|
||||
|
||||
//restore state if saved
|
||||
|
|
@ -2616,7 +2695,7 @@ void GDScriptLanguage::reload_scripts(const Array &p_scripts, bool p_soft_reload
|
|||
//if instance states were saved, set them!
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif // DEBUG_ENABLED
|
||||
}
|
||||
|
||||
void GDScriptLanguage::reload_tool_script(const Ref<Script> &p_script, bool p_soft_reload) {
|
||||
|
|
@ -2626,8 +2705,6 @@ void GDScriptLanguage::reload_tool_script(const Ref<Script> &p_script, bool p_so
|
|||
}
|
||||
|
||||
void GDScriptLanguage::frame() {
|
||||
calls = 0;
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
if (profiling) {
|
||||
MutexLock lock(mutex);
|
||||
|
|
@ -2734,7 +2811,7 @@ bool GDScriptLanguage::handles_global_class_type(const String &p_type) const {
|
|||
return p_type == "GDScript";
|
||||
}
|
||||
|
||||
String GDScriptLanguage::get_global_class_name(const String &p_path, String *r_base_type, String *r_icon_path) const {
|
||||
String GDScriptLanguage::get_global_class_name(const String &p_path, String *r_base_type, String *r_icon_path, bool *r_is_abstract, bool *r_is_tool) const {
|
||||
Error err;
|
||||
Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ, &err);
|
||||
if (err) {
|
||||
|
|
@ -2835,13 +2912,18 @@ String GDScriptLanguage::get_global_class_name(const String &p_path, String *r_b
|
|||
if (r_icon_path) {
|
||||
*r_icon_path = c->simplified_icon_path;
|
||||
}
|
||||
if (r_is_abstract) {
|
||||
*r_is_abstract = false;
|
||||
}
|
||||
if (r_is_tool) {
|
||||
*r_is_tool = parser.is_tool();
|
||||
}
|
||||
return c->identifier != nullptr ? String(c->identifier->name) : String();
|
||||
}
|
||||
|
||||
thread_local GDScriptLanguage::CallStack GDScriptLanguage::_call_stack;
|
||||
|
||||
GDScriptLanguage::GDScriptLanguage() {
|
||||
calls = 0;
|
||||
ERR_FAIL_COND(singleton);
|
||||
singleton = this;
|
||||
strings._init = StaticCString::create("_init");
|
||||
|
|
@ -2857,8 +2939,11 @@ GDScriptLanguage::GDScriptLanguage() {
|
|||
_debug_parse_err_line = -1;
|
||||
_debug_parse_err_file = "";
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
profiling = false;
|
||||
profile_native_calls = false;
|
||||
script_frame_time = 0;
|
||||
#endif
|
||||
|
||||
int dmcs = GLOBAL_DEF(PropertyInfo(Variant::INT, "debug/settings/gdscript/max_call_stack", PROPERTY_HINT_RANGE, "512," + itos(GDScriptFunction::MAX_CALL_DEPTH - 1) + ",1"), 1024);
|
||||
|
||||
|
|
@ -2873,6 +2958,7 @@ GDScriptLanguage::GDScriptLanguage() {
|
|||
#ifdef DEBUG_ENABLED
|
||||
GLOBAL_DEF("debug/gdscript/warnings/enable", true);
|
||||
GLOBAL_DEF("debug/gdscript/warnings/exclude_addons", true);
|
||||
GLOBAL_DEF("debug/gdscript/warnings/renamed_in_godot_4_hint", true);
|
||||
for (int i = 0; i < (int)GDScriptWarning::WARNING_MAX; i++) {
|
||||
GDScriptWarning::Code code = (GDScriptWarning::Code)i;
|
||||
Variant default_enabled = GDScriptWarning::get_default_value(code);
|
||||
|
|
|
|||
|
|
@ -110,11 +110,13 @@ class GDScript : public Script {
|
|||
HashMap<StringName, MethodInfo> _signals;
|
||||
Dictionary rpc_config;
|
||||
|
||||
public:
|
||||
struct LambdaInfo {
|
||||
int capture_count;
|
||||
bool use_self;
|
||||
};
|
||||
|
||||
private:
|
||||
HashMap<GDScriptFunction *, LambdaInfo> lambda_info;
|
||||
|
||||
public:
|
||||
|
|
@ -157,16 +159,18 @@ private:
|
|||
bool placeholder_fallback_enabled = false;
|
||||
void _update_exports_values(HashMap<StringName, Variant> &values, List<PropertyInfo> &propnames);
|
||||
|
||||
StringName doc_class_name;
|
||||
DocData::ClassDoc doc;
|
||||
Vector<DocData::ClassDoc> docs;
|
||||
void _add_doc(const DocData::ClassDoc &p_doc);
|
||||
void _clear_doc();
|
||||
void _add_doc(const DocData::ClassDoc &p_inner_class);
|
||||
#endif
|
||||
|
||||
GDScriptFunction *implicit_initializer = nullptr;
|
||||
GDScriptFunction *initializer = nullptr; //direct pointer to new , faster to locate
|
||||
GDScriptFunction *implicit_ready = nullptr;
|
||||
GDScriptFunction *static_initializer = nullptr;
|
||||
GDScriptFunction *initializer = nullptr; // Direct pointer to `new()`/`_init()` member function, faster to locate.
|
||||
|
||||
GDScriptFunction *implicit_initializer = nullptr; // `@implicit_new()` special function.
|
||||
GDScriptFunction *implicit_ready = nullptr; // `@implicit_ready()` special function.
|
||||
GDScriptFunction *static_initializer = nullptr; // `@static_initializer()` special function.
|
||||
|
||||
Error _static_init();
|
||||
void _static_default_init(); // Initialize static variables with default values based on their types.
|
||||
|
|
@ -257,9 +261,15 @@ public:
|
|||
CRASH_COND(!member_indices.has(p_member));
|
||||
return member_indices[p_member].data_type;
|
||||
}
|
||||
const HashMap<StringName, GDScriptFunction *> &get_member_functions() const { return member_functions; }
|
||||
const Ref<GDScriptNativeClass> &get_native() const { return native; }
|
||||
|
||||
_FORCE_INLINE_ const HashMap<StringName, GDScriptFunction *> &get_member_functions() const { return member_functions; }
|
||||
_FORCE_INLINE_ const HashMap<GDScriptFunction *, LambdaInfo> &get_lambda_info() const { return lambda_info; }
|
||||
|
||||
_FORCE_INLINE_ const GDScriptFunction *get_implicit_initializer() const { return implicit_initializer; }
|
||||
_FORCE_INLINE_ const GDScriptFunction *get_implicit_ready() const { return implicit_ready; }
|
||||
_FORCE_INLINE_ const GDScriptFunction *get_static_initializer() const { return static_initializer; }
|
||||
|
||||
RBSet<GDScript *> get_dependencies();
|
||||
HashMap<GDScript *, RBSet<GDScript *>> get_all_dependencies();
|
||||
RBSet<GDScript *> get_must_clear_dependencies();
|
||||
|
|
@ -292,9 +302,8 @@ public:
|
|||
virtual void update_exports() override;
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
virtual Vector<DocData::ClassDoc> get_documentation() const override {
|
||||
return docs;
|
||||
}
|
||||
virtual StringName get_doc_class_name() const override { return doc_class_name; }
|
||||
virtual Vector<DocData::ClassDoc> get_documentation() const override { return docs; }
|
||||
virtual String get_class_icon_path() const override;
|
||||
#endif // TOOLS_ENABLED
|
||||
|
||||
|
|
@ -334,7 +343,7 @@ public:
|
|||
virtual void get_constants(HashMap<StringName, Variant> *p_constants) override;
|
||||
virtual void get_members(HashSet<StringName> *p_members) override;
|
||||
|
||||
virtual const Variant get_rpc_config() const override;
|
||||
virtual Variant get_rpc_config() const override;
|
||||
|
||||
void unload_static() const;
|
||||
|
||||
|
|
@ -417,6 +426,7 @@ class GDScriptLanguage : public ScriptLanguage {
|
|||
Vector<Variant> global_array;
|
||||
HashMap<StringName, int> globals;
|
||||
HashMap<StringName, Variant> named_globals;
|
||||
Vector<int> global_array_empty_indexes;
|
||||
|
||||
struct CallLevel {
|
||||
Variant *stack = nullptr;
|
||||
|
|
@ -448,6 +458,7 @@ class GDScriptLanguage : public ScriptLanguage {
|
|||
int _debug_max_call_stack = 0;
|
||||
|
||||
void _add_global(const StringName &p_name, const Variant &p_value);
|
||||
void _remove_global(const StringName &p_name);
|
||||
|
||||
friend class GDScriptInstance;
|
||||
|
||||
|
|
@ -459,15 +470,20 @@ class GDScriptLanguage : public ScriptLanguage {
|
|||
friend class GDScriptFunction;
|
||||
|
||||
SelfList<GDScriptFunction>::List function_list;
|
||||
#ifdef DEBUG_ENABLED
|
||||
bool profiling;
|
||||
bool profile_native_calls;
|
||||
uint64_t script_frame_time;
|
||||
#endif
|
||||
|
||||
HashMap<String, ObjectID> orphan_subclasses;
|
||||
|
||||
public:
|
||||
int calls;
|
||||
#ifdef TOOLS_ENABLED
|
||||
void _extension_loaded(const Ref<GDExtension> &p_extension);
|
||||
void _extension_unloading(const Ref<GDExtension> &p_extension);
|
||||
#endif
|
||||
|
||||
public:
|
||||
bool debug_break(const String &p_error, bool p_allow_continue = true);
|
||||
bool debug_break_parse(const String &p_file, int p_line, const String &p_error);
|
||||
|
||||
|
|
@ -621,7 +637,7 @@ public:
|
|||
/* GLOBAL CLASSES */
|
||||
|
||||
virtual bool handles_global_class_type(const String &p_type) const override;
|
||||
virtual String get_global_class_name(const String &p_path, String *r_base_type = nullptr, String *r_icon_path = nullptr) const override;
|
||||
virtual String get_global_class_name(const String &p_path, String *r_base_type = nullptr, String *r_icon_path = nullptr, bool *r_is_abstract = nullptr, bool *r_is_tool = nullptr) const override;
|
||||
|
||||
void add_orphan_subclass(const String &p_qualified_name, const ObjectID &p_subclass);
|
||||
Ref<GDScript> get_orphan_subclass(const String &p_qualified_name);
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@
|
|||
#include "core/object/class_db.h"
|
||||
#include "core/object/script_language.h"
|
||||
#include "core/templates/hash_map.h"
|
||||
#include "scene/resources/packed_scene.h"
|
||||
#include "scene/main/node.h"
|
||||
|
||||
#if defined(TOOLS_ENABLED) && !defined(DISABLE_DEPRECATED)
|
||||
#define SUGGEST_GODOT4_RENAMES
|
||||
|
|
@ -148,6 +148,15 @@ static GDScriptParser::DataType make_enum_type(const StringName &p_enum_name, co
|
|||
return type;
|
||||
}
|
||||
|
||||
static GDScriptParser::DataType make_class_enum_type(const StringName &p_enum_name, GDScriptParser::ClassNode *p_class, const String &p_script_path, bool p_meta = true) {
|
||||
GDScriptParser::DataType type = make_enum_type(p_enum_name, p_class->fqcn, p_meta);
|
||||
|
||||
type.class_type = p_class;
|
||||
type.script_path = p_script_path;
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
static GDScriptParser::DataType make_native_enum_type(const StringName &p_enum_name, const StringName &p_native_class, bool p_meta = true) {
|
||||
// Find out which base class declared the enum, so the name is always the same even when coming from other contexts.
|
||||
StringName native_base = p_native_class;
|
||||
|
|
@ -160,7 +169,9 @@ static GDScriptParser::DataType make_native_enum_type(const StringName &p_enum_n
|
|||
|
||||
GDScriptParser::DataType type = make_enum_type(p_enum_name, native_base, p_meta);
|
||||
if (p_meta) {
|
||||
type.builtin_type = Variant::NIL; // Native enum types are not Dictionaries.
|
||||
// Native enum types are not dictionaries.
|
||||
type.builtin_type = Variant::NIL;
|
||||
type.is_pseudo_type = true;
|
||||
}
|
||||
|
||||
List<StringName> enum_values;
|
||||
|
|
@ -173,10 +184,29 @@ static GDScriptParser::DataType make_native_enum_type(const StringName &p_enum_n
|
|||
return type;
|
||||
}
|
||||
|
||||
static GDScriptParser::DataType make_builtin_enum_type(const StringName &p_enum_name, Variant::Type p_type, bool p_meta = true) {
|
||||
GDScriptParser::DataType type = make_enum_type(p_enum_name, Variant::get_type_name(p_type), p_meta);
|
||||
if (p_meta) {
|
||||
// Built-in enum types are not dictionaries.
|
||||
type.builtin_type = Variant::NIL;
|
||||
type.is_pseudo_type = true;
|
||||
}
|
||||
|
||||
List<StringName> enum_values;
|
||||
Variant::get_enumerations_for_enum(p_type, p_enum_name, &enum_values);
|
||||
|
||||
for (const StringName &E : enum_values) {
|
||||
type.enum_values[E] = Variant::get_enum_value(p_type, p_enum_name, E);
|
||||
}
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
static GDScriptParser::DataType make_global_enum_type(const StringName &p_enum_name, const StringName &p_base, bool p_meta = true) {
|
||||
GDScriptParser::DataType type = make_enum_type(p_enum_name, p_base, p_meta);
|
||||
if (p_meta) {
|
||||
type.builtin_type = Variant::NIL; // Native enum types are not Dictionaries.
|
||||
// Global enum types are not dictionaries.
|
||||
type.builtin_type = Variant::NIL;
|
||||
type.is_pseudo_type = true;
|
||||
}
|
||||
|
||||
|
|
@ -418,6 +448,12 @@ Error GDScriptAnalyzer::resolve_class_inheritance(GDScriptParser::ClassNode *p_c
|
|||
return err;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
if (!parser->_is_tool && ext_parser->get_parser()->_is_tool) {
|
||||
parser->push_warning(p_class, GDScriptWarning::MISSING_TOOL);
|
||||
}
|
||||
#endif
|
||||
|
||||
base = ext_parser->get_parser()->head->get_datatype();
|
||||
} else {
|
||||
if (p_class->extends.is_empty()) {
|
||||
|
|
@ -445,6 +481,13 @@ Error GDScriptAnalyzer::resolve_class_inheritance(GDScriptParser::ClassNode *p_c
|
|||
push_error(vformat(R"(Could not resolve super class inheritance from "%s".)", name), id);
|
||||
return err;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
if (!parser->_is_tool && base_parser->get_parser()->_is_tool) {
|
||||
parser->push_warning(p_class, GDScriptWarning::MISSING_TOOL);
|
||||
}
|
||||
#endif
|
||||
|
||||
base = base_parser->get_parser()->head->get_datatype();
|
||||
}
|
||||
} else if (ProjectSettings::get_singleton()->has_autoload(name) && ProjectSettings::get_singleton()->get_autoload(name).is_singleton) {
|
||||
|
|
@ -465,6 +508,13 @@ Error GDScriptAnalyzer::resolve_class_inheritance(GDScriptParser::ClassNode *p_c
|
|||
push_error(vformat(R"(Could not resolve super class inheritance from "%s".)", name), id);
|
||||
return err;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
if (!parser->_is_tool && info_parser->get_parser()->_is_tool) {
|
||||
parser->push_warning(p_class, GDScriptWarning::MISSING_TOOL);
|
||||
}
|
||||
#endif
|
||||
|
||||
base = info_parser->get_parser()->head->get_datatype();
|
||||
} else if (class_exists(name)) {
|
||||
if (Engine::get_singleton()->has_singleton(name)) {
|
||||
|
|
@ -674,8 +724,8 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type
|
|||
if (first == SNAME("Variant")) {
|
||||
if (p_type->type_chain.size() == 2) {
|
||||
// May be nested enum.
|
||||
StringName enum_name = p_type->type_chain[1]->name;
|
||||
StringName qualified_name = String(first) + ENUM_SEPARATOR + String(p_type->type_chain[1]->name);
|
||||
const StringName enum_name = p_type->type_chain[1]->name;
|
||||
const StringName qualified_name = String(first) + ENUM_SEPARATOR + String(p_type->type_chain[1]->name);
|
||||
if (CoreConstants::is_global_enum(qualified_name)) {
|
||||
result = make_global_enum_type(enum_name, first, true);
|
||||
return result;
|
||||
|
|
@ -690,20 +740,45 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type
|
|||
result.kind = GDScriptParser::DataType::VARIANT;
|
||||
} else if (GDScriptParser::get_builtin_type(first) < Variant::VARIANT_MAX) {
|
||||
// Built-in types.
|
||||
if (p_type->type_chain.size() > 1) {
|
||||
push_error(R"(Built-in types don't contain nested types.)", p_type->type_chain[1]);
|
||||
const Variant::Type builtin_type = GDScriptParser::get_builtin_type(first);
|
||||
|
||||
if (p_type->type_chain.size() == 2) {
|
||||
// May be nested enum.
|
||||
const StringName enum_name = p_type->type_chain[1]->name;
|
||||
if (Variant::has_enum(builtin_type, enum_name)) {
|
||||
result = make_builtin_enum_type(enum_name, builtin_type, true);
|
||||
return result;
|
||||
} else {
|
||||
push_error(vformat(R"(Name "%s" is not a nested type of "%s".)", enum_name, first), p_type->type_chain[1]);
|
||||
return bad_type;
|
||||
}
|
||||
} else if (p_type->type_chain.size() > 2) {
|
||||
push_error(R"(Built-in types only contain enum types, which do not have nested types.)", p_type->type_chain[2]);
|
||||
return bad_type;
|
||||
}
|
||||
result.kind = GDScriptParser::DataType::BUILTIN;
|
||||
result.builtin_type = GDScriptParser::get_builtin_type(first);
|
||||
|
||||
if (result.builtin_type == Variant::ARRAY) {
|
||||
result.kind = GDScriptParser::DataType::BUILTIN;
|
||||
result.builtin_type = builtin_type;
|
||||
|
||||
if (builtin_type == Variant::ARRAY) {
|
||||
GDScriptParser::DataType container_type = type_from_metatype(resolve_datatype(p_type->get_container_type_or_null(0)));
|
||||
if (container_type.kind != GDScriptParser::DataType::VARIANT) {
|
||||
container_type.is_constant = false;
|
||||
result.set_container_element_type(0, container_type);
|
||||
}
|
||||
}
|
||||
if (builtin_type == Variant::DICTIONARY) {
|
||||
GDScriptParser::DataType key_type = type_from_metatype(resolve_datatype(p_type->get_container_type_or_null(0)));
|
||||
if (key_type.kind != GDScriptParser::DataType::VARIANT) {
|
||||
key_type.is_constant = false;
|
||||
result.set_container_element_type(0, key_type);
|
||||
}
|
||||
GDScriptParser::DataType value_type = type_from_metatype(resolve_datatype(p_type->get_container_type_or_null(1)));
|
||||
if (value_type.kind != GDScriptParser::DataType::VARIANT) {
|
||||
value_type.is_constant = false;
|
||||
result.set_container_element_type(1, value_type);
|
||||
}
|
||||
}
|
||||
} else if (class_exists(first)) {
|
||||
// Native engine classes.
|
||||
result.kind = GDScriptParser::DataType::NATIVE;
|
||||
|
|
@ -717,7 +792,7 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type
|
|||
String ext = path.get_extension();
|
||||
if (ext == GDScriptLanguage::get_singleton()->get_extension()) {
|
||||
Ref<GDScriptParserRef> ref = parser->get_depended_parser_for(path);
|
||||
if (!ref.is_valid() || ref->raise_status(GDScriptParserRef::INHERITANCE_SOLVED) != OK) {
|
||||
if (ref.is_null() || ref->raise_status(GDScriptParserRef::INHERITANCE_SOLVED) != OK) {
|
||||
push_error(vformat(R"(Could not parse global class "%s" from "%s".)", first, ScriptServer::get_global_class_path(first)), p_type);
|
||||
return bad_type;
|
||||
}
|
||||
|
|
@ -864,11 +939,16 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type
|
|||
if (!p_type->container_types.is_empty()) {
|
||||
if (result.builtin_type == Variant::ARRAY) {
|
||||
if (p_type->container_types.size() != 1) {
|
||||
push_error("Arrays require exactly one collection element type.", p_type);
|
||||
push_error(R"(Typed arrays require exactly one collection element type.)", p_type);
|
||||
return bad_type;
|
||||
}
|
||||
} else if (result.builtin_type == Variant::DICTIONARY) {
|
||||
if (p_type->container_types.size() != 2) {
|
||||
push_error(R"(Typed dictionaries require exactly two collection element types.)", p_type);
|
||||
return bad_type;
|
||||
}
|
||||
} else {
|
||||
push_error("Only arrays can specify collection element types.", p_type);
|
||||
push_error(R"(Only arrays and dictionaries can specify collection element types.)", p_type);
|
||||
return bad_type;
|
||||
}
|
||||
}
|
||||
|
|
@ -894,8 +974,8 @@ void GDScriptAnalyzer::resolve_class_member(GDScriptParser::ClassNode *p_class,
|
|||
Finally finally([&]() {
|
||||
ensure_cached_external_parser_for_class(member.get_datatype().class_type, p_class, "Trying to resolve datatype of class member", p_source);
|
||||
GDScriptParser::DataType member_type = member.get_datatype();
|
||||
if (member_type.has_container_element_type(0)) {
|
||||
ensure_cached_external_parser_for_class(member_type.get_container_element_type(0).class_type, p_class, "Trying to resolve datatype of class member", p_source);
|
||||
for (int i = 0; i < member_type.get_container_element_type_count(); ++i) {
|
||||
ensure_cached_external_parser_for_class(member_type.get_container_element_type(i).class_type, p_class, "Trying to resolve datatype of class member", p_source);
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -1013,7 +1093,12 @@ void GDScriptAnalyzer::resolve_class_member(GDScriptParser::ClassNode *p_class,
|
|||
}
|
||||
}
|
||||
if (is_get_node) {
|
||||
parser->push_warning(member.variable, GDScriptWarning::GET_NODE_DEFAULT_WITHOUT_ONREADY, is_using_shorthand ? "$" : "get_node()");
|
||||
String offending_syntax = "get_node()";
|
||||
if (is_using_shorthand) {
|
||||
GDScriptParser::GetNodeNode *get_node_node = static_cast<GDScriptParser::GetNodeNode *>(expr);
|
||||
offending_syntax = get_node_node->use_dollar ? "$" : "%";
|
||||
}
|
||||
parser->push_warning(member.variable, GDScriptWarning::GET_NODE_DEFAULT_WITHOUT_ONREADY, offending_syntax);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1064,7 +1149,7 @@ void GDScriptAnalyzer::resolve_class_member(GDScriptParser::ClassNode *p_class,
|
|||
check_class_member_name_conflict(p_class, member.m_enum->identifier->name, member.m_enum);
|
||||
|
||||
member.m_enum->set_datatype(resolving_datatype);
|
||||
GDScriptParser::DataType enum_type = make_enum_type(member.m_enum->identifier->name, p_class->fqcn, true);
|
||||
GDScriptParser::DataType enum_type = make_class_enum_type(member.m_enum->identifier->name, p_class, parser->script_path, true);
|
||||
|
||||
const GDScriptParser::EnumNode *prev_enum = current_enum;
|
||||
current_enum = member.m_enum;
|
||||
|
|
@ -1157,7 +1242,7 @@ void GDScriptAnalyzer::resolve_class_member(GDScriptParser::ClassNode *p_class,
|
|||
// Also update the original references.
|
||||
member.enum_value.parent_enum->values.set(member.enum_value.index, member.enum_value);
|
||||
|
||||
member.enum_value.identifier->set_datatype(make_enum_type(UNNAMED_ENUM, p_class->fqcn, false));
|
||||
member.enum_value.identifier->set_datatype(make_class_enum_type(UNNAMED_ENUM, p_class, parser->script_path, false));
|
||||
} break;
|
||||
case GDScriptParser::ClassNode::Member::CLASS:
|
||||
check_class_member_name_conflict(p_class, member.m_class->identifier->name, member.m_class);
|
||||
|
|
@ -1906,6 +1991,11 @@ void GDScriptAnalyzer::resolve_assignable(GDScriptParser::AssignableNode *p_assi
|
|||
if (has_specified_type && specified_type.has_container_element_type(0)) {
|
||||
update_array_literal_element_type(array, specified_type.get_container_element_type(0));
|
||||
}
|
||||
} else if (p_assignable->initializer->type == GDScriptParser::Node::DICTIONARY) {
|
||||
GDScriptParser::DictionaryNode *dictionary = static_cast<GDScriptParser::DictionaryNode *>(p_assignable->initializer);
|
||||
if (has_specified_type && specified_type.has_container_element_types()) {
|
||||
update_dictionary_literal_element_type(dictionary, specified_type.get_container_element_type_or_variant(0), specified_type.get_container_element_type_or_variant(1));
|
||||
}
|
||||
}
|
||||
|
||||
if (is_constant && !p_assignable->initializer->is_constant) {
|
||||
|
|
@ -1967,7 +2057,7 @@ void GDScriptAnalyzer::resolve_assignable(GDScriptParser::AssignableNode *p_assi
|
|||
} else {
|
||||
push_error(vformat(R"(Cannot assign a value of type %s to %s "%s" with specified type %s.)", initializer_type.to_string(), p_kind, p_assignable->identifier->name, specified_type.to_string()), p_assignable->initializer);
|
||||
}
|
||||
} else if (specified_type.has_container_element_type(0) && !initializer_type.has_container_element_type(0)) {
|
||||
} else if ((specified_type.has_container_element_type(0) && !initializer_type.has_container_element_type(0)) || (specified_type.has_container_element_type(1) && !initializer_type.has_container_element_type(1))) {
|
||||
mark_node_unsafe(p_assignable->initializer);
|
||||
#ifdef DEBUG_ENABLED
|
||||
} else if (specified_type.builtin_type == Variant::INT && initializer_type.builtin_type == Variant::FLOAT) {
|
||||
|
|
@ -2209,8 +2299,12 @@ void GDScriptAnalyzer::resolve_for(GDScriptParser::ForNode *p_for) {
|
|||
} else if (!is_type_compatible(specified_type, variable_type)) {
|
||||
p_for->use_conversion_assign = true;
|
||||
}
|
||||
if (p_for->list && p_for->list->type == GDScriptParser::Node::ARRAY) {
|
||||
update_array_literal_element_type(static_cast<GDScriptParser::ArrayNode *>(p_for->list), specified_type);
|
||||
if (p_for->list) {
|
||||
if (p_for->list->type == GDScriptParser::Node::ARRAY) {
|
||||
update_array_literal_element_type(static_cast<GDScriptParser::ArrayNode *>(p_for->list), specified_type);
|
||||
} else if (p_for->list->type == GDScriptParser::Node::DICTIONARY) {
|
||||
update_dictionary_literal_element_type(static_cast<GDScriptParser::DictionaryNode *>(p_for->list), specified_type, GDScriptParser::DataType::get_variant_type());
|
||||
}
|
||||
}
|
||||
}
|
||||
p_for->variable->set_datatype(specified_type);
|
||||
|
|
@ -2412,6 +2506,9 @@ void GDScriptAnalyzer::resolve_return(GDScriptParser::ReturnNode *p_return) {
|
|||
} else {
|
||||
if (p_return->return_value->type == GDScriptParser::Node::ARRAY && has_expected_type && expected_type.has_container_element_type(0)) {
|
||||
update_array_literal_element_type(static_cast<GDScriptParser::ArrayNode *>(p_return->return_value), expected_type.get_container_element_type(0));
|
||||
} else if (p_return->return_value->type == GDScriptParser::Node::DICTIONARY && has_expected_type && expected_type.has_container_element_types()) {
|
||||
update_dictionary_literal_element_type(static_cast<GDScriptParser::DictionaryNode *>(p_return->return_value),
|
||||
expected_type.get_container_element_type_or_variant(0), expected_type.get_container_element_type_or_variant(1));
|
||||
}
|
||||
if (has_expected_type && expected_type.is_hard_type() && p_return->return_value->is_constant) {
|
||||
update_const_expression_builtin_type(p_return->return_value, expected_type, "return");
|
||||
|
|
@ -2658,6 +2755,54 @@ void GDScriptAnalyzer::update_array_literal_element_type(GDScriptParser::ArrayNo
|
|||
p_array->set_datatype(array_type);
|
||||
}
|
||||
|
||||
// When a dictionary literal is stored (or passed as function argument) to a typed context, we then assume the dictionary is typed.
|
||||
// This function determines which type is that (if any).
|
||||
void GDScriptAnalyzer::update_dictionary_literal_element_type(GDScriptParser::DictionaryNode *p_dictionary, const GDScriptParser::DataType &p_key_element_type, const GDScriptParser::DataType &p_value_element_type) {
|
||||
GDScriptParser::DataType expected_key_type = p_key_element_type;
|
||||
GDScriptParser::DataType expected_value_type = p_value_element_type;
|
||||
expected_key_type.container_element_types.clear(); // Nested types (like `Dictionary[String, Array[int]]`) are not currently supported.
|
||||
expected_value_type.container_element_types.clear();
|
||||
|
||||
for (int i = 0; i < p_dictionary->elements.size(); i++) {
|
||||
GDScriptParser::ExpressionNode *key_element_node = p_dictionary->elements[i].key;
|
||||
if (key_element_node->is_constant) {
|
||||
update_const_expression_builtin_type(key_element_node, expected_key_type, "include");
|
||||
}
|
||||
const GDScriptParser::DataType &actual_key_type = key_element_node->get_datatype();
|
||||
if (actual_key_type.has_no_type() || actual_key_type.is_variant() || !actual_key_type.is_hard_type()) {
|
||||
mark_node_unsafe(key_element_node);
|
||||
} else if (!is_type_compatible(expected_key_type, actual_key_type, true, p_dictionary)) {
|
||||
if (is_type_compatible(actual_key_type, expected_key_type)) {
|
||||
mark_node_unsafe(key_element_node);
|
||||
} else {
|
||||
push_error(vformat(R"(Cannot have a key of type "%s" in a dictionary of type "Dictionary[%s, %s]".)", actual_key_type.to_string(), expected_key_type.to_string(), expected_value_type.to_string()), key_element_node);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
GDScriptParser::ExpressionNode *value_element_node = p_dictionary->elements[i].value;
|
||||
if (value_element_node->is_constant) {
|
||||
update_const_expression_builtin_type(value_element_node, expected_value_type, "include");
|
||||
}
|
||||
const GDScriptParser::DataType &actual_value_type = value_element_node->get_datatype();
|
||||
if (actual_value_type.has_no_type() || actual_value_type.is_variant() || !actual_value_type.is_hard_type()) {
|
||||
mark_node_unsafe(value_element_node);
|
||||
} else if (!is_type_compatible(expected_value_type, actual_value_type, true, p_dictionary)) {
|
||||
if (is_type_compatible(actual_value_type, expected_value_type)) {
|
||||
mark_node_unsafe(value_element_node);
|
||||
} else {
|
||||
push_error(vformat(R"(Cannot have a value of type "%s" in a dictionary of type "Dictionary[%s, %s]".)", actual_value_type.to_string(), expected_key_type.to_string(), expected_value_type.to_string()), value_element_node);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GDScriptParser::DataType dictionary_type = p_dictionary->get_datatype();
|
||||
dictionary_type.set_container_element_type(0, expected_key_type);
|
||||
dictionary_type.set_container_element_type(1, expected_value_type);
|
||||
p_dictionary->set_datatype(dictionary_type);
|
||||
}
|
||||
|
||||
void GDScriptAnalyzer::reduce_assignment(GDScriptParser::AssignmentNode *p_assignment) {
|
||||
reduce_expression(p_assignment->assigned_value);
|
||||
|
||||
|
|
@ -2750,9 +2895,12 @@ void GDScriptAnalyzer::reduce_assignment(GDScriptParser::AssignmentNode *p_assig
|
|||
}
|
||||
}
|
||||
|
||||
// Check if assigned value is an array literal, so we can make it a typed array too if appropriate.
|
||||
// Check if assigned value is an array/dictionary literal, so we can make it a typed container too if appropriate.
|
||||
if (p_assignment->assigned_value->type == GDScriptParser::Node::ARRAY && assignee_type.is_hard_type() && assignee_type.has_container_element_type(0)) {
|
||||
update_array_literal_element_type(static_cast<GDScriptParser::ArrayNode *>(p_assignment->assigned_value), assignee_type.get_container_element_type(0));
|
||||
} else if (p_assignment->assigned_value->type == GDScriptParser::Node::DICTIONARY && assignee_type.is_hard_type() && assignee_type.has_container_element_types()) {
|
||||
update_dictionary_literal_element_type(static_cast<GDScriptParser::DictionaryNode *>(p_assignment->assigned_value),
|
||||
assignee_type.get_container_element_type_or_variant(0), assignee_type.get_container_element_type_or_variant(1));
|
||||
}
|
||||
|
||||
if (p_assignment->operation == GDScriptParser::AssignmentNode::OP_NONE && assignee_type.is_hard_type() && p_assignment->assigned_value->is_constant) {
|
||||
|
|
@ -2830,8 +2978,8 @@ void GDScriptAnalyzer::reduce_assignment(GDScriptParser::AssignmentNode *p_assig
|
|||
// weak non-variant assignee and incompatible result
|
||||
downgrades_assignee = true;
|
||||
}
|
||||
} else if (assignee_type.has_container_element_type(0) && !op_type.has_container_element_type(0)) {
|
||||
// typed array assignee and untyped array result
|
||||
} else if ((assignee_type.has_container_element_type(0) && !op_type.has_container_element_type(0)) || (assignee_type.has_container_element_type(1) && !op_type.has_container_element_type(1))) {
|
||||
// Typed assignee and untyped result.
|
||||
mark_node_unsafe(p_assignment);
|
||||
}
|
||||
}
|
||||
|
|
@ -3029,10 +3177,13 @@ const char *check_for_renamed_identifier(String identifier, GDScriptParser::Node
|
|||
void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_await, bool p_is_root) {
|
||||
bool all_is_constant = true;
|
||||
HashMap<int, GDScriptParser::ArrayNode *> arrays; // For array literal to potentially type when passing.
|
||||
HashMap<int, GDScriptParser::DictionaryNode *> dictionaries; // Same, but for dictionaries.
|
||||
for (int i = 0; i < p_call->arguments.size(); i++) {
|
||||
reduce_expression(p_call->arguments[i]);
|
||||
if (p_call->arguments[i]->type == GDScriptParser::Node::ARRAY) {
|
||||
arrays[i] = static_cast<GDScriptParser::ArrayNode *>(p_call->arguments[i]);
|
||||
} else if (p_call->arguments[i]->type == GDScriptParser::Node::DICTIONARY) {
|
||||
dictionaries[i] = static_cast<GDScriptParser::DictionaryNode *>(p_call->arguments[i]);
|
||||
}
|
||||
all_is_constant = all_is_constant && p_call->arguments[i]->is_constant;
|
||||
}
|
||||
|
|
@ -3225,6 +3376,26 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
|
|||
push_error(vformat(R"(No constructor of "%s" matches the signature "%s".)", Variant::get_type_name(builtin_type), signature), p_call);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
// Consider `Signal(self, "my_signal")` as an implicit use of the signal.
|
||||
if (builtin_type == Variant::SIGNAL && p_call->arguments.size() >= 2) {
|
||||
const GDScriptParser::ExpressionNode *object_arg = p_call->arguments[0];
|
||||
if (object_arg && object_arg->type == GDScriptParser::Node::SELF) {
|
||||
const GDScriptParser::ExpressionNode *signal_arg = p_call->arguments[1];
|
||||
if (signal_arg && signal_arg->is_constant) {
|
||||
const StringName &signal_name = signal_arg->reduced_value;
|
||||
if (parser->current_class->has_member(signal_name)) {
|
||||
const GDScriptParser::ClassNode::Member &member = parser->current_class->get_member(signal_name);
|
||||
if (member.type == GDScriptParser::ClassNode::Member::SIGNAL) {
|
||||
member.signal->usages++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
p_call->set_datatype(call_type);
|
||||
return;
|
||||
} else if (GDScriptUtilityFunctions::function_exists(function_name)) {
|
||||
|
|
@ -3417,6 +3588,14 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
|
|||
update_array_literal_element_type(E.value, par_types.get(index).get_container_element_type(0));
|
||||
}
|
||||
}
|
||||
for (const KeyValue<int, GDScriptParser::DictionaryNode *> &E : dictionaries) {
|
||||
int index = E.key;
|
||||
if (index < par_types.size() && par_types.get(index).is_hard_type() && par_types.get(index).has_container_element_types()) {
|
||||
GDScriptParser::DataType key = par_types.get(index).get_container_element_type_or_variant(0);
|
||||
GDScriptParser::DataType value = par_types.get(index).get_container_element_type_or_variant(1);
|
||||
update_dictionary_literal_element_type(E.value, key, value);
|
||||
}
|
||||
}
|
||||
validate_call_arg(par_types, default_arg_count, method_flags.has_flag(METHOD_FLAG_VARARG), p_call);
|
||||
|
||||
if (base_type.kind == GDScriptParser::DataType::ENUM && base_type.is_meta_type) {
|
||||
|
|
@ -3459,6 +3638,20 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
|
|||
|
||||
parser->push_warning(p_call, GDScriptWarning::STATIC_CALLED_ON_INSTANCE, p_call->function_name, caller_type);
|
||||
}
|
||||
|
||||
// Consider `emit_signal()`, `connect()`, and `disconnect()` as implicit uses of the signal.
|
||||
if (is_self && (p_call->function_name == SNAME("emit_signal") || p_call->function_name == SNAME("connect") || p_call->function_name == SNAME("disconnect")) && !p_call->arguments.is_empty()) {
|
||||
const GDScriptParser::ExpressionNode *signal_arg = p_call->arguments[0];
|
||||
if (signal_arg && signal_arg->is_constant) {
|
||||
const StringName &signal_name = signal_arg->reduced_value;
|
||||
if (parser->current_class->has_member(signal_name)) {
|
||||
const GDScriptParser::ClassNode::Member &member = parser->current_class->get_member(signal_name);
|
||||
if (member.type == GDScriptParser::ClassNode::Member::SIGNAL) {
|
||||
member.signal->usages++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // DEBUG_ENABLED
|
||||
|
||||
call_type = return_type;
|
||||
|
|
@ -3502,7 +3695,7 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
|
|||
String base_name = is_self && !p_call->is_super ? "self" : base_type.to_string();
|
||||
#ifdef SUGGEST_GODOT4_RENAMES
|
||||
String rename_hint;
|
||||
if (GLOBAL_GET(GDScriptWarning::get_settings_path_from_code(GDScriptWarning::RENAMED_IN_GODOT_4_HINT)).booleanize()) {
|
||||
if (GLOBAL_GET("debug/gdscript/warnings/renamed_in_godot_4_hint")) {
|
||||
const char *renamed_function_name = check_for_renamed_identifier(p_call->function_name, p_call->type);
|
||||
if (renamed_function_name) {
|
||||
rename_hint = " " + vformat(R"(Did you mean to use "%s"?)", String(renamed_function_name) + "()");
|
||||
|
|
@ -3547,6 +3740,11 @@ void GDScriptAnalyzer::reduce_cast(GDScriptParser::CastNode *p_cast) {
|
|||
update_array_literal_element_type(static_cast<GDScriptParser::ArrayNode *>(p_cast->operand), cast_type.get_container_element_type(0));
|
||||
}
|
||||
|
||||
if (p_cast->operand->type == GDScriptParser::Node::DICTIONARY && cast_type.has_container_element_types()) {
|
||||
update_dictionary_literal_element_type(static_cast<GDScriptParser::DictionaryNode *>(p_cast->operand),
|
||||
cast_type.get_container_element_type_or_variant(0), cast_type.get_container_element_type_or_variant(1));
|
||||
}
|
||||
|
||||
if (!cast_type.is_variant()) {
|
||||
GDScriptParser::DataType op_type = p_cast->operand->get_datatype();
|
||||
if (op_type.is_variant() || !op_type.is_hard_type()) {
|
||||
|
|
@ -3657,6 +3855,12 @@ GDScriptParser::DataType GDScriptAnalyzer::make_global_class_meta_type(const Str
|
|||
}
|
||||
|
||||
Ref<GDScriptParserRef> GDScriptAnalyzer::ensure_cached_external_parser_for_class(const GDScriptParser::ClassNode *p_class, const GDScriptParser::ClassNode *p_from_class, const char *p_context, const GDScriptParser::Node *p_source) {
|
||||
// Delicate piece of code that intentionally doesn't use the GDScript cache or `get_depended_parser_for`.
|
||||
// Search dependencies for the parser that owns `p_class` and make a cache entry for it.
|
||||
// Required for how we store pointers to classes owned by other parser trees and need to call `resolve_class_member` and such on the same parser tree.
|
||||
// Since https://github.com/godotengine/godot/pull/94871 there can technically be multiple parsers for the same script in the same parser tree.
|
||||
// Even if unlikely, getting the wrong parser could lead to strange undefined behavior without errors.
|
||||
|
||||
if (p_class == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
|
@ -3673,8 +3877,6 @@ Ref<GDScriptParserRef> GDScriptAnalyzer::ensure_cached_external_parser_for_class
|
|||
p_from_class = parser->head;
|
||||
}
|
||||
|
||||
String script_path = p_class->get_datatype().script_path;
|
||||
|
||||
Ref<GDScriptParserRef> parser_ref;
|
||||
for (const GDScriptParser::ClassNode *look_class = p_from_class; look_class != nullptr; look_class = look_class->base_type.class_type) {
|
||||
if (parser->has_class(look_class)) {
|
||||
|
|
@ -3755,8 +3957,9 @@ Ref<GDScriptParserRef> GDScriptAnalyzer::find_cached_external_parser_for_class(c
|
|||
|
||||
Ref<GDScript> GDScriptAnalyzer::get_depended_shallow_script(const String &p_path, Error &r_error) {
|
||||
// To keep a local cache of the parser for resolving external nodes later.
|
||||
parser->get_depended_parser_for(p_path);
|
||||
Ref<GDScript> scr = GDScriptCache::get_shallow_script(p_path, r_error, parser->script_path);
|
||||
const String path = ResourceUID::ensure_path(p_path);
|
||||
parser->get_depended_parser_for(path);
|
||||
Ref<GDScript> scr = GDScriptCache::get_shallow_script(path, r_error, parser->script_path);
|
||||
return scr;
|
||||
}
|
||||
|
||||
|
|
@ -3807,24 +4010,47 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
|
|||
|
||||
if (base.kind == GDScriptParser::DataType::BUILTIN) {
|
||||
if (base.is_meta_type) {
|
||||
bool valid = true;
|
||||
Variant result = Variant::get_constant_value(base.builtin_type, name, &valid);
|
||||
if (valid) {
|
||||
bool valid = false;
|
||||
|
||||
if (Variant::has_constant(base.builtin_type, name)) {
|
||||
valid = true;
|
||||
|
||||
const Variant constant_value = Variant::get_constant_value(base.builtin_type, name);
|
||||
|
||||
p_identifier->is_constant = true;
|
||||
p_identifier->reduced_value = result;
|
||||
p_identifier->set_datatype(type_from_variant(result, p_identifier));
|
||||
} else if (base.is_hard_type()) {
|
||||
p_identifier->reduced_value = constant_value;
|
||||
p_identifier->set_datatype(type_from_variant(constant_value, p_identifier));
|
||||
}
|
||||
|
||||
if (!valid) {
|
||||
const StringName enum_name = Variant::get_enum_for_enumeration(base.builtin_type, name);
|
||||
if (enum_name != StringName()) {
|
||||
valid = true;
|
||||
|
||||
p_identifier->is_constant = true;
|
||||
p_identifier->reduced_value = Variant::get_enum_value(base.builtin_type, enum_name, name);
|
||||
p_identifier->set_datatype(make_builtin_enum_type(enum_name, base.builtin_type, false));
|
||||
}
|
||||
}
|
||||
|
||||
if (!valid && Variant::has_enum(base.builtin_type, name)) {
|
||||
valid = true;
|
||||
|
||||
p_identifier->set_datatype(make_builtin_enum_type(name, base.builtin_type, true));
|
||||
}
|
||||
|
||||
if (!valid && base.is_hard_type()) {
|
||||
#ifdef SUGGEST_GODOT4_RENAMES
|
||||
String rename_hint;
|
||||
if (GLOBAL_GET(GDScriptWarning::get_settings_path_from_code(GDScriptWarning::RENAMED_IN_GODOT_4_HINT)).booleanize()) {
|
||||
if (GLOBAL_GET("debug/gdscript/warnings/renamed_in_godot_4_hint")) {
|
||||
const char *renamed_identifier_name = check_for_renamed_identifier(name, p_identifier->type);
|
||||
if (renamed_identifier_name) {
|
||||
rename_hint = " " + vformat(R"(Did you mean to use "%s"?)", renamed_identifier_name);
|
||||
}
|
||||
}
|
||||
push_error(vformat(R"(Cannot find constant "%s" on base "%s".%s)", name, base.to_string(), rename_hint), p_identifier);
|
||||
push_error(vformat(R"(Cannot find member "%s" in base "%s".%s)", name, base.to_string(), rename_hint), p_identifier);
|
||||
#else
|
||||
push_error(vformat(R"(Cannot find constant "%s" on base "%s".)", name, base.to_string()), p_identifier);
|
||||
push_error(vformat(R"(Cannot find member "%s" in base "%s".)", name, base.to_string()), p_identifier);
|
||||
#endif // SUGGEST_GODOT4_RENAMES
|
||||
}
|
||||
} else {
|
||||
|
|
@ -3860,15 +4086,15 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
|
|||
if (base.is_hard_type()) {
|
||||
#ifdef SUGGEST_GODOT4_RENAMES
|
||||
String rename_hint;
|
||||
if (GLOBAL_GET(GDScriptWarning::get_settings_path_from_code(GDScriptWarning::RENAMED_IN_GODOT_4_HINT)).booleanize()) {
|
||||
if (GLOBAL_GET("debug/gdscript/warnings/renamed_in_godot_4_hint")) {
|
||||
const char *renamed_identifier_name = check_for_renamed_identifier(name, p_identifier->type);
|
||||
if (renamed_identifier_name) {
|
||||
rename_hint = " " + vformat(R"(Did you mean to use "%s"?)", renamed_identifier_name);
|
||||
}
|
||||
}
|
||||
push_error(vformat(R"(Cannot find property "%s" on base "%s".%s)", name, base.to_string(), rename_hint), p_identifier);
|
||||
push_error(vformat(R"(Cannot find member "%s" in base "%s".%s)", name, base.to_string(), rename_hint), p_identifier);
|
||||
#else
|
||||
push_error(vformat(R"(Cannot find property "%s" on base "%s".)", name, base.to_string()), p_identifier);
|
||||
push_error(vformat(R"(Cannot find member "%s" in base "%s".)", name, base.to_string()), p_identifier);
|
||||
#endif // SUGGEST_GODOT4_RENAMES
|
||||
}
|
||||
}
|
||||
|
|
@ -4099,7 +4325,7 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident
|
|||
const GDScriptParser::EnumNode::Value &element = current_enum->values[i];
|
||||
if (element.identifier->name == p_identifier->name) {
|
||||
StringName enum_name = current_enum->identifier ? current_enum->identifier->name : UNNAMED_ENUM;
|
||||
GDScriptParser::DataType type = make_enum_type(enum_name, parser->current_class->fqcn, false);
|
||||
GDScriptParser::DataType type = make_class_enum_type(enum_name, parser->current_class, parser->script_path, false);
|
||||
if (element.parent_enum->identifier) {
|
||||
type.enum_type = element.parent_enum->identifier->name;
|
||||
}
|
||||
|
|
@ -4288,11 +4514,11 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident
|
|||
result.builtin_type = Variant::OBJECT;
|
||||
result.native_type = SNAME("Node");
|
||||
if (ResourceLoader::get_resource_type(autoload.path) == "GDScript") {
|
||||
Ref<GDScriptParserRef> singl_parser = parser->get_depended_parser_for(autoload.path);
|
||||
if (singl_parser.is_valid()) {
|
||||
Error err = singl_parser->raise_status(GDScriptParserRef::INHERITANCE_SOLVED);
|
||||
Ref<GDScriptParserRef> single_parser = parser->get_depended_parser_for(autoload.path);
|
||||
if (single_parser.is_valid()) {
|
||||
Error err = single_parser->raise_status(GDScriptParserRef::INHERITANCE_SOLVED);
|
||||
if (err == OK) {
|
||||
result = type_from_metatype(singl_parser->get_parser()->head->get_datatype());
|
||||
result = type_from_metatype(single_parser->get_parser()->head->get_datatype());
|
||||
}
|
||||
}
|
||||
} else if (ResourceLoader::get_resource_type(autoload.path) == "PackedScene") {
|
||||
|
|
@ -4302,11 +4528,11 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident
|
|||
if (node != nullptr) {
|
||||
Ref<GDScript> scr = node->get_script();
|
||||
if (scr.is_valid()) {
|
||||
Ref<GDScriptParserRef> singl_parser = parser->get_depended_parser_for(scr->get_script_path());
|
||||
if (singl_parser.is_valid()) {
|
||||
Error err = singl_parser->raise_status(GDScriptParserRef::INHERITANCE_SOLVED);
|
||||
Ref<GDScriptParserRef> single_parser = parser->get_depended_parser_for(scr->get_script_path());
|
||||
if (single_parser.is_valid()) {
|
||||
Error err = single_parser->raise_status(GDScriptParserRef::INHERITANCE_SOLVED);
|
||||
if (err == OK) {
|
||||
result = type_from_metatype(singl_parser->get_parser()->head->get_datatype());
|
||||
result = type_from_metatype(single_parser->get_parser()->head->get_datatype());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -4376,7 +4602,7 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident
|
|||
// Not found.
|
||||
#ifdef SUGGEST_GODOT4_RENAMES
|
||||
String rename_hint;
|
||||
if (GLOBAL_GET(GDScriptWarning::get_settings_path_from_code(GDScriptWarning::RENAMED_IN_GODOT_4_HINT)).booleanize()) {
|
||||
if (GLOBAL_GET("debug/gdscript/warnings/renamed_in_godot_4_hint")) {
|
||||
const char *renamed_identifier_name = check_for_renamed_identifier(name, p_identifier->type);
|
||||
if (renamed_identifier_name) {
|
||||
rename_hint = " " + vformat(R"(Did you mean to use "%s"?)", renamed_identifier_name);
|
||||
|
|
@ -4571,10 +4797,23 @@ void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscri
|
|||
reduce_identifier_from_base(p_subscript->attribute, &base_type);
|
||||
GDScriptParser::DataType attr_type = p_subscript->attribute->get_datatype();
|
||||
if (attr_type.is_set()) {
|
||||
valid = !attr_type.is_pseudo_type || p_can_be_pseudo_type;
|
||||
result_type = attr_type;
|
||||
p_subscript->is_constant = p_subscript->attribute->is_constant;
|
||||
p_subscript->reduced_value = p_subscript->attribute->reduced_value;
|
||||
if (base_type.builtin_type == Variant::DICTIONARY && base_type.has_container_element_types()) {
|
||||
Variant::Type key_type = base_type.get_container_element_type_or_variant(0).builtin_type;
|
||||
valid = key_type == Variant::NIL || key_type == Variant::STRING || key_type == Variant::STRING_NAME;
|
||||
if (base_type.has_container_element_type(1)) {
|
||||
result_type = base_type.get_container_element_type(1);
|
||||
result_type.type_source = base_type.type_source;
|
||||
} else {
|
||||
result_type.builtin_type = Variant::NIL;
|
||||
result_type.kind = GDScriptParser::DataType::VARIANT;
|
||||
result_type.type_source = GDScriptParser::DataType::UNDETECTED;
|
||||
}
|
||||
} else {
|
||||
valid = !attr_type.is_pseudo_type || p_can_be_pseudo_type;
|
||||
result_type = attr_type;
|
||||
p_subscript->is_constant = p_subscript->attribute->is_constant;
|
||||
p_subscript->reduced_value = p_subscript->attribute->reduced_value;
|
||||
}
|
||||
} else if (!base_type.is_meta_type || !base_type.is_constant) {
|
||||
valid = base_type.kind != GDScriptParser::DataType::BUILTIN;
|
||||
#ifdef DEBUG_ENABLED
|
||||
|
|
@ -4681,8 +4920,40 @@ void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscri
|
|||
case Variant::SIGNAL:
|
||||
case Variant::STRING_NAME:
|
||||
break;
|
||||
// Here for completeness.
|
||||
// Support depends on if the dictionary has a typed key, otherwise anything is valid.
|
||||
case Variant::DICTIONARY:
|
||||
if (base_type.has_container_element_type(0)) {
|
||||
GDScriptParser::DataType key_type = base_type.get_container_element_type(0);
|
||||
switch (index_type.builtin_type) {
|
||||
// Null value will be treated as an empty object, allow.
|
||||
case Variant::NIL:
|
||||
error = key_type.builtin_type != Variant::OBJECT;
|
||||
break;
|
||||
// Objects are parsed for validity in a similar manner to container types.
|
||||
case Variant::OBJECT:
|
||||
if (key_type.builtin_type == Variant::OBJECT) {
|
||||
error = !key_type.can_reference(index_type);
|
||||
} else {
|
||||
error = key_type.builtin_type != Variant::NIL;
|
||||
}
|
||||
break;
|
||||
// String and StringName interchangeable in this context.
|
||||
case Variant::STRING:
|
||||
case Variant::STRING_NAME:
|
||||
error = key_type.builtin_type != Variant::STRING_NAME && key_type.builtin_type != Variant::STRING;
|
||||
break;
|
||||
// Ints are valid indices for floats, but not the other way around.
|
||||
case Variant::INT:
|
||||
error = key_type.builtin_type != Variant::INT && key_type.builtin_type != Variant::FLOAT;
|
||||
break;
|
||||
// All other cases require the types to match exactly.
|
||||
default:
|
||||
error = key_type.builtin_type != index_type.builtin_type;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
// Here for completeness.
|
||||
case Variant::VARIANT_MAX:
|
||||
break;
|
||||
}
|
||||
|
|
@ -4771,7 +5042,6 @@ void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscri
|
|||
case Variant::PROJECTION:
|
||||
case Variant::PLANE:
|
||||
case Variant::COLOR:
|
||||
case Variant::DICTIONARY:
|
||||
case Variant::OBJECT:
|
||||
result_type.kind = GDScriptParser::DataType::VARIANT;
|
||||
result_type.type_source = GDScriptParser::DataType::UNDETECTED;
|
||||
|
|
@ -4786,6 +5056,16 @@ void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscri
|
|||
result_type.type_source = GDScriptParser::DataType::UNDETECTED;
|
||||
}
|
||||
break;
|
||||
// Can have two element types, but we only care about the value.
|
||||
case Variant::DICTIONARY:
|
||||
if (base_type.has_container_element_type(1)) {
|
||||
result_type = base_type.get_container_element_type(1);
|
||||
result_type.type_source = base_type.type_source;
|
||||
} else {
|
||||
result_type.kind = GDScriptParser::DataType::VARIANT;
|
||||
result_type.type_source = GDScriptParser::DataType::UNDETECTED;
|
||||
}
|
||||
break;
|
||||
// Here for completeness.
|
||||
case Variant::VARIANT_MAX:
|
||||
break;
|
||||
|
|
@ -4965,7 +5245,9 @@ Variant GDScriptAnalyzer::make_array_reduced_value(GDScriptParser::ArrayNode *p_
|
|||
}
|
||||
|
||||
Variant GDScriptAnalyzer::make_dictionary_reduced_value(GDScriptParser::DictionaryNode *p_dictionary, bool &is_reduced) {
|
||||
Dictionary dictionary;
|
||||
Dictionary dictionary = p_dictionary->get_datatype().has_container_element_types()
|
||||
? make_dictionary_from_element_datatype(p_dictionary->get_datatype().get_container_element_type_or_variant(0), p_dictionary->get_datatype().get_container_element_type_or_variant(1))
|
||||
: Dictionary();
|
||||
|
||||
for (int i = 0; i < p_dictionary->elements.size(); i++) {
|
||||
const GDScriptParser::DictionaryNode::Pair &element = p_dictionary->elements[i];
|
||||
|
|
@ -5052,6 +5334,49 @@ Array GDScriptAnalyzer::make_array_from_element_datatype(const GDScriptParser::D
|
|||
return array;
|
||||
}
|
||||
|
||||
Dictionary GDScriptAnalyzer::make_dictionary_from_element_datatype(const GDScriptParser::DataType &p_key_element_datatype, const GDScriptParser::DataType &p_value_element_datatype, const GDScriptParser::Node *p_source_node) {
|
||||
Dictionary dictionary;
|
||||
StringName key_name;
|
||||
Variant key_script;
|
||||
StringName value_name;
|
||||
Variant value_script;
|
||||
|
||||
if (p_key_element_datatype.builtin_type == Variant::OBJECT) {
|
||||
Ref<Script> script_type = p_key_element_datatype.script_type;
|
||||
if (p_key_element_datatype.kind == GDScriptParser::DataType::CLASS && script_type.is_null()) {
|
||||
Error err = OK;
|
||||
Ref<GDScript> scr = get_depended_shallow_script(p_key_element_datatype.script_path, err);
|
||||
if (err) {
|
||||
push_error(vformat(R"(Error while getting cache for script "%s".)", p_key_element_datatype.script_path), p_source_node);
|
||||
return dictionary;
|
||||
}
|
||||
script_type.reference_ptr(scr->find_class(p_key_element_datatype.class_type->fqcn));
|
||||
}
|
||||
|
||||
key_name = p_key_element_datatype.native_type;
|
||||
key_script = script_type;
|
||||
}
|
||||
|
||||
if (p_value_element_datatype.builtin_type == Variant::OBJECT) {
|
||||
Ref<Script> script_type = p_value_element_datatype.script_type;
|
||||
if (p_value_element_datatype.kind == GDScriptParser::DataType::CLASS && script_type.is_null()) {
|
||||
Error err = OK;
|
||||
Ref<GDScript> scr = get_depended_shallow_script(p_value_element_datatype.script_path, err);
|
||||
if (err) {
|
||||
push_error(vformat(R"(Error while getting cache for script "%s".)", p_value_element_datatype.script_path), p_source_node);
|
||||
return dictionary;
|
||||
}
|
||||
script_type.reference_ptr(scr->find_class(p_value_element_datatype.class_type->fqcn));
|
||||
}
|
||||
|
||||
value_name = p_value_element_datatype.native_type;
|
||||
value_script = script_type;
|
||||
}
|
||||
|
||||
dictionary.set_typed(p_key_element_datatype.builtin_type, key_name, key_script, p_value_element_datatype.builtin_type, value_name, value_script);
|
||||
return dictionary;
|
||||
}
|
||||
|
||||
Variant GDScriptAnalyzer::make_variable_default_value(GDScriptParser::VariableNode *p_variable) {
|
||||
Variant result = Variant();
|
||||
|
||||
|
|
@ -5067,6 +5392,10 @@ Variant GDScriptAnalyzer::make_variable_default_value(GDScriptParser::VariableNo
|
|||
if (datatype.kind == GDScriptParser::DataType::BUILTIN && datatype.builtin_type != Variant::OBJECT) {
|
||||
if (datatype.builtin_type == Variant::ARRAY && datatype.has_container_element_type(0)) {
|
||||
result = make_array_from_element_datatype(datatype.get_container_element_type(0));
|
||||
} else if (datatype.builtin_type == Variant::DICTIONARY && datatype.has_container_element_types()) {
|
||||
GDScriptParser::DataType key = datatype.get_container_element_type_or_variant(0);
|
||||
GDScriptParser::DataType value = datatype.get_container_element_type_or_variant(1);
|
||||
result = make_dictionary_from_element_datatype(key, value);
|
||||
} else {
|
||||
VariantInternal::initialize(&result, datatype.builtin_type);
|
||||
}
|
||||
|
|
@ -5095,6 +5424,22 @@ GDScriptParser::DataType GDScriptAnalyzer::type_from_variant(const Variant &p_va
|
|||
} else if (array.get_typed_builtin() != Variant::NIL) {
|
||||
result.set_container_element_type(0, type_from_metatype(make_builtin_meta_type((Variant::Type)array.get_typed_builtin())));
|
||||
}
|
||||
} else if (p_value.get_type() == Variant::DICTIONARY) {
|
||||
const Dictionary &dict = p_value;
|
||||
if (dict.get_typed_key_script()) {
|
||||
result.set_container_element_type(0, type_from_metatype(make_script_meta_type(dict.get_typed_key_script())));
|
||||
} else if (dict.get_typed_key_class_name()) {
|
||||
result.set_container_element_type(0, type_from_metatype(make_native_meta_type(dict.get_typed_key_class_name())));
|
||||
} else if (dict.get_typed_key_builtin() != Variant::NIL) {
|
||||
result.set_container_element_type(0, type_from_metatype(make_builtin_meta_type((Variant::Type)dict.get_typed_key_builtin())));
|
||||
}
|
||||
if (dict.get_typed_value_script()) {
|
||||
result.set_container_element_type(1, type_from_metatype(make_script_meta_type(dict.get_typed_value_script())));
|
||||
} else if (dict.get_typed_value_class_name()) {
|
||||
result.set_container_element_type(1, type_from_metatype(make_native_meta_type(dict.get_typed_value_class_name())));
|
||||
} else if (dict.get_typed_value_builtin() != Variant::NIL) {
|
||||
result.set_container_element_type(1, type_from_metatype(make_builtin_meta_type((Variant::Type)dict.get_typed_value_builtin())));
|
||||
}
|
||||
} else if (p_value.get_type() == Variant::OBJECT) {
|
||||
// Object is treated as a native type, not a builtin type.
|
||||
result.kind = GDScriptParser::DataType::NATIVE;
|
||||
|
|
@ -5227,6 +5572,60 @@ GDScriptParser::DataType GDScriptAnalyzer::type_from_property(const PropertyInfo
|
|||
}
|
||||
elem_type.is_constant = false;
|
||||
result.set_container_element_type(0, elem_type);
|
||||
} else if (p_property.type == Variant::DICTIONARY && p_property.hint == PROPERTY_HINT_DICTIONARY_TYPE) {
|
||||
// Check element type.
|
||||
StringName key_elem_type_name = p_property.hint_string.get_slice(";", 0);
|
||||
GDScriptParser::DataType key_elem_type;
|
||||
key_elem_type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
|
||||
|
||||
Variant::Type key_elem_builtin_type = GDScriptParser::get_builtin_type(key_elem_type_name);
|
||||
if (key_elem_builtin_type < Variant::VARIANT_MAX) {
|
||||
// Builtin type.
|
||||
key_elem_type.kind = GDScriptParser::DataType::BUILTIN;
|
||||
key_elem_type.builtin_type = key_elem_builtin_type;
|
||||
} else if (class_exists(key_elem_type_name)) {
|
||||
key_elem_type.kind = GDScriptParser::DataType::NATIVE;
|
||||
key_elem_type.builtin_type = Variant::OBJECT;
|
||||
key_elem_type.native_type = key_elem_type_name;
|
||||
} else if (ScriptServer::is_global_class(key_elem_type_name)) {
|
||||
// Just load this as it shouldn't be a GDScript.
|
||||
Ref<Script> script = ResourceLoader::load(ScriptServer::get_global_class_path(key_elem_type_name));
|
||||
key_elem_type.kind = GDScriptParser::DataType::SCRIPT;
|
||||
key_elem_type.builtin_type = Variant::OBJECT;
|
||||
key_elem_type.native_type = script->get_instance_base_type();
|
||||
key_elem_type.script_type = script;
|
||||
} else {
|
||||
ERR_FAIL_V_MSG(result, "Could not find element type from property hint of a typed dictionary.");
|
||||
}
|
||||
key_elem_type.is_constant = false;
|
||||
|
||||
StringName value_elem_type_name = p_property.hint_string.get_slice(";", 1);
|
||||
GDScriptParser::DataType value_elem_type;
|
||||
value_elem_type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
|
||||
|
||||
Variant::Type value_elem_builtin_type = GDScriptParser::get_builtin_type(value_elem_type_name);
|
||||
if (value_elem_builtin_type < Variant::VARIANT_MAX) {
|
||||
// Builtin type.
|
||||
value_elem_type.kind = GDScriptParser::DataType::BUILTIN;
|
||||
value_elem_type.builtin_type = value_elem_builtin_type;
|
||||
} else if (class_exists(value_elem_type_name)) {
|
||||
value_elem_type.kind = GDScriptParser::DataType::NATIVE;
|
||||
value_elem_type.builtin_type = Variant::OBJECT;
|
||||
value_elem_type.native_type = value_elem_type_name;
|
||||
} else if (ScriptServer::is_global_class(value_elem_type_name)) {
|
||||
// Just load this as it shouldn't be a GDScript.
|
||||
Ref<Script> script = ResourceLoader::load(ScriptServer::get_global_class_path(value_elem_type_name));
|
||||
value_elem_type.kind = GDScriptParser::DataType::SCRIPT;
|
||||
value_elem_type.builtin_type = Variant::OBJECT;
|
||||
value_elem_type.native_type = script->get_instance_base_type();
|
||||
value_elem_type.script_type = script;
|
||||
} else {
|
||||
ERR_FAIL_V_MSG(result, "Could not find element type from property hint of a typed dictionary.");
|
||||
}
|
||||
value_elem_type.is_constant = false;
|
||||
|
||||
result.set_container_element_type(0, key_elem_type);
|
||||
result.set_container_element_type(1, value_elem_type);
|
||||
} else if (p_property.type == Variant::INT) {
|
||||
// Check if it's enum.
|
||||
if ((p_property.usage & PROPERTY_USAGE_CLASS_IS_ENUM) && p_property.class_name != StringName()) {
|
||||
|
|
@ -5236,7 +5635,7 @@ GDScriptParser::DataType GDScriptAnalyzer::type_from_property(const PropertyInfo
|
|||
} else {
|
||||
Vector<String> names = String(p_property.class_name).split(ENUM_SEPARATOR);
|
||||
if (names.size() == 2) {
|
||||
result = make_native_enum_type(names[1], names[0], false);
|
||||
result = make_enum_type(names[1], names[0], false);
|
||||
result.is_constant = false;
|
||||
}
|
||||
}
|
||||
|
|
@ -5473,8 +5872,6 @@ void GDScriptAnalyzer::validate_call_arg(const List<GDScriptParser::DataType> &p
|
|||
#ifdef DEBUG_ENABLED
|
||||
void GDScriptAnalyzer::is_shadowing(GDScriptParser::IdentifierNode *p_identifier, const String &p_context, const bool p_in_local_scope) {
|
||||
const StringName &name = p_identifier->name;
|
||||
GDScriptParser::DataType base = parser->current_class->get_datatype();
|
||||
GDScriptParser::ClassNode *base_class = base.class_type;
|
||||
|
||||
{
|
||||
List<MethodInfo> gdscript_funcs;
|
||||
|
|
@ -5502,40 +5899,56 @@ void GDScriptAnalyzer::is_shadowing(GDScriptParser::IdentifierNode *p_identifier
|
|||
}
|
||||
}
|
||||
|
||||
const GDScriptParser::DataType current_class_type = parser->current_class->get_datatype();
|
||||
if (p_in_local_scope) {
|
||||
while (base_class != nullptr) {
|
||||
GDScriptParser::ClassNode *base_class = current_class_type.class_type;
|
||||
|
||||
if (base_class != nullptr) {
|
||||
if (base_class->has_member(name)) {
|
||||
parser->push_warning(p_identifier, GDScriptWarning::SHADOWED_VARIABLE, p_context, p_identifier->name, base_class->get_member(name).get_type_name(), itos(base_class->get_member(name).get_line()));
|
||||
return;
|
||||
}
|
||||
base_class = base_class->base_type.class_type;
|
||||
}
|
||||
|
||||
while (base_class != nullptr) {
|
||||
if (base_class->has_member(name)) {
|
||||
String base_class_name = base_class->get_global_name();
|
||||
if (base_class_name.is_empty()) {
|
||||
base_class_name = base_class->fqcn;
|
||||
}
|
||||
|
||||
parser->push_warning(p_identifier, GDScriptWarning::SHADOWED_VARIABLE_BASE_CLASS, p_context, p_identifier->name, base_class->get_member(name).get_type_name(), itos(base_class->get_member(name).get_line()), base_class_name);
|
||||
return;
|
||||
}
|
||||
base_class = base_class->base_type.class_type;
|
||||
}
|
||||
}
|
||||
|
||||
StringName parent = base.native_type;
|
||||
while (parent != StringName()) {
|
||||
ERR_FAIL_COND_MSG(!class_exists(parent), "Non-existent native base class.");
|
||||
StringName native_base_class = current_class_type.native_type;
|
||||
while (native_base_class != StringName()) {
|
||||
ERR_FAIL_COND_MSG(!class_exists(native_base_class), "Non-existent native base class.");
|
||||
|
||||
if (ClassDB::has_method(parent, name, true)) {
|
||||
parser->push_warning(p_identifier, GDScriptWarning::SHADOWED_VARIABLE_BASE_CLASS, p_context, p_identifier->name, "method", parent);
|
||||
if (ClassDB::has_method(native_base_class, name, true)) {
|
||||
parser->push_warning(p_identifier, GDScriptWarning::SHADOWED_VARIABLE_BASE_CLASS, p_context, p_identifier->name, "method", native_base_class);
|
||||
return;
|
||||
} else if (ClassDB::has_signal(parent, name, true)) {
|
||||
parser->push_warning(p_identifier, GDScriptWarning::SHADOWED_VARIABLE_BASE_CLASS, p_context, p_identifier->name, "signal", parent);
|
||||
} else if (ClassDB::has_signal(native_base_class, name, true)) {
|
||||
parser->push_warning(p_identifier, GDScriptWarning::SHADOWED_VARIABLE_BASE_CLASS, p_context, p_identifier->name, "signal", native_base_class);
|
||||
return;
|
||||
} else if (ClassDB::has_property(parent, name, true)) {
|
||||
parser->push_warning(p_identifier, GDScriptWarning::SHADOWED_VARIABLE_BASE_CLASS, p_context, p_identifier->name, "property", parent);
|
||||
} else if (ClassDB::has_property(native_base_class, name, true)) {
|
||||
parser->push_warning(p_identifier, GDScriptWarning::SHADOWED_VARIABLE_BASE_CLASS, p_context, p_identifier->name, "property", native_base_class);
|
||||
return;
|
||||
} else if (ClassDB::has_integer_constant(parent, name, true)) {
|
||||
parser->push_warning(p_identifier, GDScriptWarning::SHADOWED_VARIABLE_BASE_CLASS, p_context, p_identifier->name, "constant", parent);
|
||||
} else if (ClassDB::has_integer_constant(native_base_class, name, true)) {
|
||||
parser->push_warning(p_identifier, GDScriptWarning::SHADOWED_VARIABLE_BASE_CLASS, p_context, p_identifier->name, "constant", native_base_class);
|
||||
return;
|
||||
} else if (ClassDB::has_enum(parent, name, true)) {
|
||||
parser->push_warning(p_identifier, GDScriptWarning::SHADOWED_VARIABLE_BASE_CLASS, p_context, p_identifier->name, "enum", parent);
|
||||
} else if (ClassDB::has_enum(native_base_class, name, true)) {
|
||||
parser->push_warning(p_identifier, GDScriptWarning::SHADOWED_VARIABLE_BASE_CLASS, p_context, p_identifier->name, "enum", native_base_class);
|
||||
return;
|
||||
}
|
||||
parent = ClassDB::get_parent_class(parent);
|
||||
native_base_class = ClassDB::get_parent_class(native_base_class);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endif // DEBUG_ENABLED
|
||||
|
||||
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.
|
||||
|
|
@ -5647,6 +6060,15 @@ bool GDScriptAnalyzer::check_type_compatibility(const GDScriptParser::DataType &
|
|||
valid = p_target.get_container_element_type(0) == p_source.get_container_element_type(0);
|
||||
}
|
||||
}
|
||||
if (valid && p_target.builtin_type == Variant::DICTIONARY && p_source.builtin_type == Variant::DICTIONARY) {
|
||||
// Check the element types.
|
||||
if (p_target.has_container_element_type(0) && p_source.has_container_element_type(0)) {
|
||||
valid = p_target.get_container_element_type(0) == p_source.get_container_element_type(0);
|
||||
}
|
||||
if (valid && p_target.has_container_element_type(1) && p_source.has_container_element_type(1)) {
|
||||
valid = p_target.get_container_element_type(1) == p_source.get_container_element_type(1);
|
||||
}
|
||||
}
|
||||
return valid;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -36,7 +36,6 @@
|
|||
|
||||
#include "core/object/object.h"
|
||||
#include "core/object/ref_counted.h"
|
||||
#include "core/templates/hash_set.h"
|
||||
|
||||
class GDScriptAnalyzer {
|
||||
GDScriptParser *parser = nullptr;
|
||||
|
|
@ -125,8 +124,8 @@ class GDScriptAnalyzer {
|
|||
|
||||
// Helpers.
|
||||
Array make_array_from_element_datatype(const GDScriptParser::DataType &p_element_datatype, const GDScriptParser::Node *p_source_node = nullptr);
|
||||
Dictionary make_dictionary_from_element_datatype(const GDScriptParser::DataType &p_key_element_datatype, const GDScriptParser::DataType &p_value_element_datatype, const GDScriptParser::Node *p_source_node = nullptr);
|
||||
GDScriptParser::DataType type_from_variant(const Variant &p_value, const GDScriptParser::Node *p_source);
|
||||
static GDScriptParser::DataType type_from_metatype(const GDScriptParser::DataType &p_meta_type);
|
||||
GDScriptParser::DataType type_from_property(const PropertyInfo &p_property, bool p_is_arg = false, bool p_is_readonly = false) const;
|
||||
GDScriptParser::DataType make_global_class_meta_type(const StringName &p_class_name, const GDScriptParser::Node *p_source);
|
||||
bool get_function_signature(GDScriptParser::Node *p_source, bool p_is_constructor, GDScriptParser::DataType base_type, const StringName &p_function, GDScriptParser::DataType &r_return_type, List<GDScriptParser::DataType> &r_par_types, int &r_default_arg_count, BitField<MethodFlags> &r_method_flags, StringName *r_native_class = nullptr);
|
||||
|
|
@ -137,6 +136,7 @@ class GDScriptAnalyzer {
|
|||
GDScriptParser::DataType get_operation_type(Variant::Operator p_operation, const GDScriptParser::DataType &p_a, bool &r_valid, const GDScriptParser::Node *p_source);
|
||||
void update_const_expression_builtin_type(GDScriptParser::ExpressionNode *p_expression, const GDScriptParser::DataType &p_type, const char *p_usage, bool p_is_cast = false);
|
||||
void update_array_literal_element_type(GDScriptParser::ArrayNode *p_array, const GDScriptParser::DataType &p_element_type);
|
||||
void update_dictionary_literal_element_type(GDScriptParser::DictionaryNode *p_dictionary, const GDScriptParser::DataType &p_key_element_type, const GDScriptParser::DataType &p_value_element_type);
|
||||
bool is_type_compatible(const GDScriptParser::DataType &p_target, const GDScriptParser::DataType &p_source, bool p_allow_implicit_conversion = false, const GDScriptParser::Node *p_source_node = nullptr);
|
||||
void push_error(const String &p_message, const GDScriptParser::Node *p_origin = nullptr);
|
||||
void mark_node_unsafe(const GDScriptParser::Node *p_node);
|
||||
|
|
@ -161,7 +161,9 @@ public:
|
|||
Error analyze();
|
||||
|
||||
Variant make_variable_default_value(GDScriptParser::VariableNode *p_variable);
|
||||
|
||||
static bool check_type_compatibility(const GDScriptParser::DataType &p_target, const GDScriptParser::DataType &p_source, bool p_allow_implicit_conversion = false, const GDScriptParser::Node *p_source_node = nullptr);
|
||||
static GDScriptParser::DataType type_from_metatype(const GDScriptParser::DataType &p_meta_type);
|
||||
|
||||
GDScriptAnalyzer(GDScriptParser *p_parser);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -585,8 +585,25 @@ void GDScriptByteCodeGenerator::write_unary_operator(const Address &p_target, Va
|
|||
}
|
||||
|
||||
void GDScriptByteCodeGenerator::write_binary_operator(const Address &p_target, Variant::Operator p_operator, const Address &p_left_operand, const Address &p_right_operand) {
|
||||
// Avoid validated evaluator for modulo and division when operands are int, since there's no check for division by zero.
|
||||
if (HAS_BUILTIN_TYPE(p_left_operand) && HAS_BUILTIN_TYPE(p_right_operand) && ((p_operator != Variant::OP_DIVIDE && p_operator != Variant::OP_MODULE) || p_left_operand.type.builtin_type != Variant::INT || p_right_operand.type.builtin_type != Variant::INT)) {
|
||||
bool valid = HAS_BUILTIN_TYPE(p_left_operand) && HAS_BUILTIN_TYPE(p_right_operand);
|
||||
|
||||
// Avoid validated evaluator for modulo and division when operands are int or integer vector, since there's no check for division by zero.
|
||||
if (valid && (p_operator == Variant::OP_DIVIDE || p_operator == Variant::OP_MODULE)) {
|
||||
switch (p_left_operand.type.builtin_type) {
|
||||
case Variant::INT:
|
||||
valid = p_right_operand.type.builtin_type != Variant::INT;
|
||||
break;
|
||||
case Variant::VECTOR2I:
|
||||
case Variant::VECTOR3I:
|
||||
case Variant::VECTOR4I:
|
||||
valid = p_right_operand.type.builtin_type != Variant::INT && p_right_operand.type.builtin_type != p_left_operand.type.builtin_type;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (valid) {
|
||||
if (p_target.mode == Address::TEMPORARY) {
|
||||
Variant::Type result_type = Variant::get_operator_return_type(p_operator, p_left_operand.type.builtin_type, p_right_operand.type.builtin_type);
|
||||
Variant::Type temp_type = temporaries[p_target.address].type;
|
||||
|
|
@ -634,6 +651,18 @@ void GDScriptByteCodeGenerator::write_type_test(const Address &p_target, const A
|
|||
append(get_constant_pos(element_type.script_type) | (GDScriptFunction::ADDR_TYPE_CONSTANT << GDScriptFunction::ADDR_BITS));
|
||||
append(element_type.builtin_type);
|
||||
append(element_type.native_type);
|
||||
} else if (p_type.builtin_type == Variant::DICTIONARY && p_type.has_container_element_types()) {
|
||||
const GDScriptDataType &key_element_type = p_type.get_container_element_type_or_variant(0);
|
||||
const GDScriptDataType &value_element_type = p_type.get_container_element_type_or_variant(1);
|
||||
append_opcode(GDScriptFunction::OPCODE_TYPE_TEST_DICTIONARY);
|
||||
append(p_target);
|
||||
append(p_source);
|
||||
append(get_constant_pos(key_element_type.script_type) | (GDScriptFunction::ADDR_TYPE_CONSTANT << GDScriptFunction::ADDR_BITS));
|
||||
append(get_constant_pos(value_element_type.script_type) | (GDScriptFunction::ADDR_TYPE_CONSTANT << GDScriptFunction::ADDR_BITS));
|
||||
append(key_element_type.builtin_type);
|
||||
append(key_element_type.native_type);
|
||||
append(value_element_type.builtin_type);
|
||||
append(value_element_type.native_type);
|
||||
} else {
|
||||
append_opcode(GDScriptFunction::OPCODE_TYPE_TEST_BUILTIN);
|
||||
append(p_target);
|
||||
|
|
@ -889,6 +918,18 @@ void GDScriptByteCodeGenerator::write_assign_with_conversion(const Address &p_ta
|
|||
append(get_constant_pos(element_type.script_type) | (GDScriptFunction::ADDR_TYPE_CONSTANT << GDScriptFunction::ADDR_BITS));
|
||||
append(element_type.builtin_type);
|
||||
append(element_type.native_type);
|
||||
} else if (p_target.type.builtin_type == Variant::DICTIONARY && p_target.type.has_container_element_types()) {
|
||||
const GDScriptDataType &key_type = p_target.type.get_container_element_type_or_variant(0);
|
||||
const GDScriptDataType &value_type = p_target.type.get_container_element_type_or_variant(1);
|
||||
append_opcode(GDScriptFunction::OPCODE_ASSIGN_TYPED_DICTIONARY);
|
||||
append(p_target);
|
||||
append(p_source);
|
||||
append(get_constant_pos(key_type.script_type) | (GDScriptFunction::ADDR_TYPE_CONSTANT << GDScriptFunction::ADDR_BITS));
|
||||
append(get_constant_pos(value_type.script_type) | (GDScriptFunction::ADDR_TYPE_CONSTANT << GDScriptFunction::ADDR_BITS));
|
||||
append(key_type.builtin_type);
|
||||
append(key_type.native_type);
|
||||
append(value_type.builtin_type);
|
||||
append(value_type.native_type);
|
||||
} else {
|
||||
append_opcode(GDScriptFunction::OPCODE_ASSIGN_TYPED_BUILTIN);
|
||||
append(p_target);
|
||||
|
|
@ -935,6 +976,18 @@ void GDScriptByteCodeGenerator::write_assign(const Address &p_target, const Addr
|
|||
append(get_constant_pos(element_type.script_type) | (GDScriptFunction::ADDR_TYPE_CONSTANT << GDScriptFunction::ADDR_BITS));
|
||||
append(element_type.builtin_type);
|
||||
append(element_type.native_type);
|
||||
} else if (p_target.type.kind == GDScriptDataType::BUILTIN && p_target.type.builtin_type == Variant::DICTIONARY && p_target.type.has_container_element_types()) {
|
||||
const GDScriptDataType &key_type = p_target.type.get_container_element_type_or_variant(0);
|
||||
const GDScriptDataType &value_type = p_target.type.get_container_element_type_or_variant(1);
|
||||
append_opcode(GDScriptFunction::OPCODE_ASSIGN_TYPED_DICTIONARY);
|
||||
append(p_target);
|
||||
append(p_source);
|
||||
append(get_constant_pos(key_type.script_type) | (GDScriptFunction::ADDR_TYPE_CONSTANT << GDScriptFunction::ADDR_BITS));
|
||||
append(get_constant_pos(value_type.script_type) | (GDScriptFunction::ADDR_TYPE_CONSTANT << GDScriptFunction::ADDR_BITS));
|
||||
append(key_type.builtin_type);
|
||||
append(key_type.native_type);
|
||||
append(value_type.builtin_type);
|
||||
append(value_type.native_type);
|
||||
} else if (p_target.type.kind == GDScriptDataType::BUILTIN && p_source.type.kind == GDScriptDataType::BUILTIN && p_target.type.builtin_type != p_source.type.builtin_type) {
|
||||
// Need conversion.
|
||||
append_opcode(GDScriptFunction::OPCODE_ASSIGN_TYPED_BUILTIN);
|
||||
|
|
@ -1434,6 +1487,23 @@ void GDScriptByteCodeGenerator::write_construct_dictionary(const Address &p_targ
|
|||
ct.cleanup();
|
||||
}
|
||||
|
||||
void GDScriptByteCodeGenerator::write_construct_typed_dictionary(const Address &p_target, const GDScriptDataType &p_key_type, const GDScriptDataType &p_value_type, const Vector<Address> &p_arguments) {
|
||||
append_opcode_and_argcount(GDScriptFunction::OPCODE_CONSTRUCT_TYPED_DICTIONARY, 3 + p_arguments.size());
|
||||
for (int i = 0; i < p_arguments.size(); i++) {
|
||||
append(p_arguments[i]);
|
||||
}
|
||||
CallTarget ct = get_call_target(p_target);
|
||||
append(ct.target);
|
||||
append(get_constant_pos(p_key_type.script_type) | (GDScriptFunction::ADDR_TYPE_CONSTANT << GDScriptFunction::ADDR_BITS));
|
||||
append(get_constant_pos(p_value_type.script_type) | (GDScriptFunction::ADDR_TYPE_CONSTANT << GDScriptFunction::ADDR_BITS));
|
||||
append(p_arguments.size() / 2); // This is number of key-value pairs, so only half of actual arguments.
|
||||
append(p_key_type.builtin_type);
|
||||
append(p_key_type.native_type);
|
||||
append(p_value_type.builtin_type);
|
||||
append(p_value_type.native_type);
|
||||
ct.cleanup();
|
||||
}
|
||||
|
||||
void GDScriptByteCodeGenerator::write_await(const Address &p_target, const Address &p_operand) {
|
||||
append_opcode(GDScriptFunction::OPCODE_AWAIT);
|
||||
append(p_operand);
|
||||
|
|
@ -1711,6 +1781,19 @@ void GDScriptByteCodeGenerator::write_return(const Address &p_return_value) {
|
|||
append(get_constant_pos(element_type.script_type) | (GDScriptFunction::ADDR_TYPE_CONSTANT << GDScriptFunction::ADDR_BITS));
|
||||
append(element_type.builtin_type);
|
||||
append(element_type.native_type);
|
||||
} else if (function->return_type.kind == GDScriptDataType::BUILTIN && function->return_type.builtin_type == Variant::DICTIONARY &&
|
||||
function->return_type.has_container_element_types()) {
|
||||
// Typed dictionary.
|
||||
const GDScriptDataType &key_type = function->return_type.get_container_element_type_or_variant(0);
|
||||
const GDScriptDataType &value_type = function->return_type.get_container_element_type_or_variant(1);
|
||||
append_opcode(GDScriptFunction::OPCODE_RETURN_TYPED_DICTIONARY);
|
||||
append(p_return_value);
|
||||
append(get_constant_pos(key_type.script_type) | (GDScriptFunction::ADDR_TYPE_CONSTANT << GDScriptFunction::ADDR_BITS));
|
||||
append(get_constant_pos(value_type.script_type) | (GDScriptFunction::ADDR_TYPE_CONSTANT << GDScriptFunction::ADDR_BITS));
|
||||
append(key_type.builtin_type);
|
||||
append(key_type.native_type);
|
||||
append(value_type.builtin_type);
|
||||
append(value_type.native_type);
|
||||
} else if (function->return_type.kind == GDScriptDataType::BUILTIN && p_return_value.type.kind == GDScriptDataType::BUILTIN && function->return_type.builtin_type != p_return_value.type.builtin_type) {
|
||||
// Add conversion.
|
||||
append_opcode(GDScriptFunction::OPCODE_RETURN_TYPED_BUILTIN);
|
||||
|
|
@ -1735,6 +1818,17 @@ void GDScriptByteCodeGenerator::write_return(const Address &p_return_value) {
|
|||
append(get_constant_pos(element_type.script_type) | (GDScriptFunction::ADDR_TYPE_CONSTANT << GDScriptFunction::ADDR_BITS));
|
||||
append(element_type.builtin_type);
|
||||
append(element_type.native_type);
|
||||
} else if (function->return_type.builtin_type == Variant::DICTIONARY && function->return_type.has_container_element_types()) {
|
||||
const GDScriptDataType &key_type = function->return_type.get_container_element_type_or_variant(0);
|
||||
const GDScriptDataType &value_type = function->return_type.get_container_element_type_or_variant(1);
|
||||
append_opcode(GDScriptFunction::OPCODE_RETURN_TYPED_DICTIONARY);
|
||||
append(p_return_value);
|
||||
append(get_constant_pos(key_type.script_type) | (GDScriptFunction::ADDR_TYPE_CONSTANT << GDScriptFunction::ADDR_BITS));
|
||||
append(get_constant_pos(value_type.script_type) | (GDScriptFunction::ADDR_TYPE_CONSTANT << GDScriptFunction::ADDR_BITS));
|
||||
append(key_type.builtin_type);
|
||||
append(key_type.native_type);
|
||||
append(value_type.builtin_type);
|
||||
append(value_type.native_type);
|
||||
} else {
|
||||
append_opcode(GDScriptFunction::OPCODE_RETURN_TYPED_BUILTIN);
|
||||
append(p_return_value);
|
||||
|
|
@ -1785,7 +1879,7 @@ void GDScriptByteCodeGenerator::end_block() {
|
|||
|
||||
void GDScriptByteCodeGenerator::clear_temporaries() {
|
||||
for (int slot_idx : temporaries_pending_clear) {
|
||||
// The temporary may have been re-used as something else since it was added to the list.
|
||||
// The temporary may have been reused as something else since it was added to the list.
|
||||
// In that case, there's **no** need to clear it.
|
||||
if (temporaries[slot_idx].can_contain_object) {
|
||||
clear_address(Address(Address::TEMPORARY, slot_idx)); // Can contain `RefCounted`, so clear it.
|
||||
|
|
@ -1803,6 +1897,13 @@ void GDScriptByteCodeGenerator::clear_address(const Address &p_address) {
|
|||
case Variant::BOOL:
|
||||
write_assign_false(p_address);
|
||||
break;
|
||||
case Variant::DICTIONARY:
|
||||
if (p_address.type.has_container_element_types()) {
|
||||
write_construct_typed_dictionary(p_address, p_address.type.get_container_element_type_or_variant(0), p_address.type.get_container_element_type_or_variant(1), Vector<GDScriptCodeGenerator::Address>());
|
||||
} else {
|
||||
write_construct(p_address, p_address.type.builtin_type, Vector<GDScriptCodeGenerator::Address>());
|
||||
}
|
||||
break;
|
||||
case Variant::ARRAY:
|
||||
if (p_address.type.has_container_element_type(0)) {
|
||||
write_construct_typed_array(p_address, p_address.type.get_container_element_type(0), Vector<GDScriptCodeGenerator::Address>());
|
||||
|
|
@ -1827,7 +1928,7 @@ void GDScriptByteCodeGenerator::clear_address(const Address &p_address) {
|
|||
}
|
||||
}
|
||||
|
||||
// Returns `true` if the local has been re-used and not cleaned up with `clear_address()`.
|
||||
// Returns `true` if the local has been reused and not cleaned up with `clear_address()`.
|
||||
bool GDScriptByteCodeGenerator::is_local_dirty(const Address &p_address) const {
|
||||
ERR_FAIL_COND_V(p_address.mode != Address::LOCAL_VARIABLE, false);
|
||||
return dirty_locals.has(p_address.address);
|
||||
|
|
|
|||
|
|
@ -529,6 +529,7 @@ public:
|
|||
virtual void write_construct_array(const Address &p_target, const Vector<Address> &p_arguments) override;
|
||||
virtual void write_construct_typed_array(const Address &p_target, const GDScriptDataType &p_element_type, const Vector<Address> &p_arguments) override;
|
||||
virtual void write_construct_dictionary(const Address &p_target, const Vector<Address> &p_arguments) override;
|
||||
virtual void write_construct_typed_dictionary(const Address &p_target, const GDScriptDataType &p_key_type, const GDScriptDataType &p_value_type, const Vector<Address> &p_arguments) override;
|
||||
virtual void write_await(const Address &p_target, const Address &p_operand) override;
|
||||
virtual void write_if(const Address &p_condition) override;
|
||||
virtual void write_else() override;
|
||||
|
|
|
|||
|
|
@ -144,6 +144,14 @@ GDScriptParserRef::~GDScriptParserRef() {
|
|||
|
||||
GDScriptCache *GDScriptCache::singleton = nullptr;
|
||||
|
||||
SafeBinaryMutex<GDScriptCache::BINARY_MUTEX_TAG> &_get_gdscript_cache_mutex() {
|
||||
return GDScriptCache::mutex;
|
||||
}
|
||||
|
||||
template <>
|
||||
thread_local SafeBinaryMutex<GDScriptCache::BINARY_MUTEX_TAG>::TLSData SafeBinaryMutex<GDScriptCache::BINARY_MUTEX_TAG>::tls_data(_get_gdscript_cache_mutex());
|
||||
SafeBinaryMutex<GDScriptCache::BINARY_MUTEX_TAG> GDScriptCache::mutex;
|
||||
|
||||
void GDScriptCache::move_script(const String &p_from, const String &p_to) {
|
||||
if (singleton == nullptr || p_from == p_to) {
|
||||
return;
|
||||
|
|
@ -267,7 +275,7 @@ String GDScriptCache::get_source_code(const String &p_path) {
|
|||
source_file.write[len] = 0;
|
||||
|
||||
String source;
|
||||
if (source.parse_utf8((const char *)source_file.ptr()) != OK) {
|
||||
if (source.parse_utf8((const char *)source_file.ptr(), len) != OK) {
|
||||
ERR_FAIL_V_MSG("", "Script '" + p_path + "' contains invalid unicode (UTF-8), so it was not loaded. Please ensure that scripts are saved in valid UTF-8 unicode.");
|
||||
}
|
||||
return source;
|
||||
|
|
@ -289,6 +297,7 @@ Vector<uint8_t> GDScriptCache::get_binary_tokens(const String &p_path) {
|
|||
|
||||
Ref<GDScript> GDScriptCache::get_shallow_script(const String &p_path, Error &r_error, const String &p_owner) {
|
||||
MutexLock lock(singleton->mutex);
|
||||
|
||||
if (!p_owner.is_empty()) {
|
||||
singleton->dependencies[p_owner].insert(p_path);
|
||||
}
|
||||
|
|
@ -299,7 +308,7 @@ Ref<GDScript> GDScriptCache::get_shallow_script(const String &p_path, Error &r_e
|
|||
return singleton->shallow_gdscript_cache[p_path];
|
||||
}
|
||||
|
||||
String remapped_path = ResourceLoader::path_remap(p_path);
|
||||
const String remapped_path = ResourceLoader::path_remap(p_path);
|
||||
|
||||
Ref<GDScript> script;
|
||||
script.instantiate();
|
||||
|
|
@ -324,6 +333,7 @@ Ref<GDScript> GDScriptCache::get_shallow_script(const String &p_path, Error &r_e
|
|||
}
|
||||
|
||||
singleton->shallow_gdscript_cache[p_path] = script;
|
||||
|
||||
return script;
|
||||
}
|
||||
|
||||
|
|
@ -351,16 +361,18 @@ Ref<GDScript> GDScriptCache::get_full_script(const String &p_path, Error &r_erro
|
|||
}
|
||||
}
|
||||
|
||||
const String remapped_path = ResourceLoader::path_remap(p_path);
|
||||
|
||||
if (p_update_from_disk) {
|
||||
if (p_path.get_extension().to_lower() == "gdc") {
|
||||
Vector<uint8_t> buffer = get_binary_tokens(p_path);
|
||||
if (remapped_path.get_extension().to_lower() == "gdc") {
|
||||
Vector<uint8_t> buffer = get_binary_tokens(remapped_path);
|
||||
if (buffer.is_empty()) {
|
||||
r_error = ERR_FILE_CANT_READ;
|
||||
return script;
|
||||
}
|
||||
script->set_binary_tokens_source(buffer);
|
||||
} else {
|
||||
r_error = script->load_source_code(p_path);
|
||||
r_error = script->load_source_code(remapped_path);
|
||||
if (r_error) {
|
||||
return script;
|
||||
}
|
||||
|
|
@ -369,7 +381,7 @@ Ref<GDScript> GDScriptCache::get_full_script(const String &p_path, Error &r_erro
|
|||
|
||||
// Allowing lifting the lock might cause a script to be reloaded multiple times,
|
||||
// which, as a last resort deadlock prevention strategy, is a good tradeoff.
|
||||
uint32_t allowance_id = WorkerThreadPool::thread_enter_unlock_allowance_zone(&singleton->mutex);
|
||||
uint32_t allowance_id = WorkerThreadPool::thread_enter_unlock_allowance_zone(singleton->mutex);
|
||||
r_error = script->reload(true);
|
||||
WorkerThreadPool::thread_exit_unlock_allowance_zone(allowance_id);
|
||||
if (r_error) {
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@
|
|||
#include "gdscript.h"
|
||||
|
||||
#include "core/object/ref_counted.h"
|
||||
#include "core/os/mutex.h"
|
||||
#include "core/os/safe_binary_mutex.h"
|
||||
#include "core/templates/hash_map.h"
|
||||
#include "core/templates/hash_set.h"
|
||||
|
||||
|
|
@ -95,7 +95,12 @@ class GDScriptCache {
|
|||
|
||||
bool cleared = false;
|
||||
|
||||
Mutex mutex;
|
||||
public:
|
||||
static const int BINARY_MUTEX_TAG = 2;
|
||||
|
||||
private:
|
||||
static SafeBinaryMutex<BINARY_MUTEX_TAG> mutex;
|
||||
friend SafeBinaryMutex<BINARY_MUTEX_TAG> &_get_gdscript_cache_mutex();
|
||||
|
||||
public:
|
||||
static void move_script(const String &p_from, const String &p_to);
|
||||
|
|
|
|||
|
|
@ -142,6 +142,7 @@ public:
|
|||
virtual void write_construct_array(const Address &p_target, const Vector<Address> &p_arguments) = 0;
|
||||
virtual void write_construct_typed_array(const Address &p_target, const GDScriptDataType &p_element_type, const Vector<Address> &p_arguments) = 0;
|
||||
virtual void write_construct_dictionary(const Address &p_target, const Vector<Address> &p_arguments) = 0;
|
||||
virtual void write_construct_typed_dictionary(const Address &p_target, const GDScriptDataType &p_key_type, const GDScriptDataType &p_value_type, const Vector<Address> &p_arguments) = 0;
|
||||
virtual void write_await(const Address &p_target, const Address &p_operand) = 0;
|
||||
virtual void write_if(const Address &p_condition) = 0;
|
||||
virtual void write_else() = 0;
|
||||
|
|
|
|||
|
|
@ -120,8 +120,8 @@ GDScriptDataType GDScriptCompiler::_gdtype_from_datatype(const GDScriptParser::D
|
|||
|
||||
#ifdef DEBUG_ENABLED
|
||||
if (unlikely(!GDScriptLanguage::get_singleton()->get_global_map().has(result.native_type))) {
|
||||
ERR_PRINT(vformat(R"(GDScript bug: Native class "%s" not found.)", result.native_type));
|
||||
result.native_type = Object::get_class_static();
|
||||
_set_error(vformat(R"(GDScript bug (please report): Native class "%s" not found.)", result.native_type), nullptr);
|
||||
return GDScriptDataType();
|
||||
}
|
||||
#endif
|
||||
} break;
|
||||
|
|
@ -161,6 +161,7 @@ GDScriptDataType GDScriptCompiler::_gdtype_from_datatype(const GDScriptParser::D
|
|||
script = GDScriptCache::get_shallow_script(p_datatype.script_path, err, p_owner->path);
|
||||
if (err) {
|
||||
_set_error(vformat(R"(Could not find script "%s": %s)", p_datatype.script_path, error_names[err]), nullptr);
|
||||
return GDScriptDataType();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -193,7 +194,7 @@ GDScriptDataType GDScriptCompiler::_gdtype_from_datatype(const GDScriptParser::D
|
|||
break;
|
||||
case GDScriptParser::DataType::RESOLVING:
|
||||
case GDScriptParser::DataType::UNRESOLVED: {
|
||||
ERR_PRINT("Parser bug: converting unresolved type.");
|
||||
_set_error("Parser bug (please report): converting unresolved type.", nullptr);
|
||||
return GDScriptDataType();
|
||||
}
|
||||
}
|
||||
|
|
@ -491,7 +492,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
|
|||
case GDScriptParser::Node::SELF: {
|
||||
//return constant
|
||||
if (codegen.function_node && codegen.function_node->is_static) {
|
||||
_set_error("'self' not present in static function!", p_expression);
|
||||
_set_error("'self' not present in static function.", p_expression);
|
||||
r_error = ERR_COMPILATION_FAILED;
|
||||
return GDScriptCodeGenerator::Address();
|
||||
}
|
||||
|
|
@ -532,10 +533,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
|
|||
Vector<GDScriptCodeGenerator::Address> elements;
|
||||
|
||||
// Create the result temporary first since it's the last to be killed.
|
||||
GDScriptDataType dict_type;
|
||||
dict_type.has_type = true;
|
||||
dict_type.kind = GDScriptDataType::BUILTIN;
|
||||
dict_type.builtin_type = Variant::DICTIONARY;
|
||||
GDScriptDataType dict_type = _gdtype_from_datatype(dn->get_datatype(), codegen.script);
|
||||
GDScriptCodeGenerator::Address result = codegen.add_temporary(dict_type);
|
||||
|
||||
for (int i = 0; i < dn->elements.size(); i++) {
|
||||
|
|
@ -566,7 +564,11 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
|
|||
elements.push_back(element);
|
||||
}
|
||||
|
||||
gen->write_construct_dictionary(result, elements);
|
||||
if (dict_type.has_container_element_types()) {
|
||||
gen->write_construct_typed_dictionary(result, dict_type.get_container_element_type_or_variant(0), dict_type.get_container_element_type_or_variant(1), elements);
|
||||
} else {
|
||||
gen->write_construct_dictionary(result, elements);
|
||||
}
|
||||
|
||||
for (int i = 0; i < elements.size(); i++) {
|
||||
if (elements[i].mode == GDScriptCodeGenerator::Address::TEMPORARY) {
|
||||
|
|
@ -725,6 +727,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
|
|||
return GDScriptCodeGenerator::Address();
|
||||
}
|
||||
} else {
|
||||
_set_error("Compiler bug (please report): incorrect callee type in call node.", call->callee);
|
||||
r_error = ERR_COMPILATION_FAILED;
|
||||
return GDScriptCodeGenerator::Address();
|
||||
}
|
||||
|
|
@ -1275,7 +1278,11 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
|
|||
}
|
||||
} else {
|
||||
// Regular assignment.
|
||||
ERR_FAIL_COND_V_MSG(assignment->assignee->type != GDScriptParser::Node::IDENTIFIER, GDScriptCodeGenerator::Address(), "Expected the assignee to be an identifier here.");
|
||||
if (assignment->assignee->type != GDScriptParser::Node::IDENTIFIER) {
|
||||
_set_error("Compiler bug (please report): Expected the assignee to be an identifier here.", assignment->assignee);
|
||||
r_error = ERR_COMPILATION_FAILED;
|
||||
return GDScriptCodeGenerator::Address();
|
||||
}
|
||||
GDScriptCodeGenerator::Address member;
|
||||
bool is_member = false;
|
||||
bool has_setter = false;
|
||||
|
|
@ -1416,7 +1423,9 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
|
|||
return result;
|
||||
} break;
|
||||
default: {
|
||||
ERR_FAIL_V_MSG(GDScriptCodeGenerator::Address(), "Bug in bytecode compiler, unexpected node in parse tree while parsing expression."); // Unreachable code.
|
||||
_set_error("Compiler bug (please report): Unexpected node in parse tree while parsing expression.", p_expression); // Unreachable code.
|
||||
r_error = ERR_COMPILATION_FAILED;
|
||||
return GDScriptCodeGenerator::Address();
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
|
@ -1853,7 +1862,10 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_match_pattern(CodeGen &c
|
|||
}
|
||||
return p_previous_test;
|
||||
}
|
||||
ERR_FAIL_V_MSG(p_previous_test, "Reaching the end of pattern compilation without matching a pattern.");
|
||||
|
||||
_set_error("Compiler bug (please report): Reaching the end of pattern compilation without matching a pattern.", p_pattern);
|
||||
r_error = ERR_COMPILATION_FAILED;
|
||||
return p_previous_test;
|
||||
}
|
||||
|
||||
List<GDScriptCodeGenerator::Address> GDScriptCompiler::_add_block_locals(CodeGen &codegen, const GDScriptParser::SuiteNode *p_block) {
|
||||
|
|
@ -2187,7 +2199,7 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Sui
|
|||
initialized = true;
|
||||
} else if ((local_type.has_type && local_type.kind == GDScriptDataType::BUILTIN) || codegen.generator->is_local_dirty(local)) {
|
||||
// Initialize with default for the type. Built-in types must always be cleared (they cannot be `null`).
|
||||
// Objects and untyped variables are assigned to `null` only if the stack address has been re-used and not cleared.
|
||||
// Objects and untyped variables are assigned to `null` only if the stack address has been reused and not cleared.
|
||||
codegen.generator->clear_address(local);
|
||||
initialized = true;
|
||||
}
|
||||
|
|
@ -2221,7 +2233,8 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Sui
|
|||
codegen.generator->pop_temporary();
|
||||
}
|
||||
} else {
|
||||
ERR_FAIL_V_MSG(ERR_INVALID_DATA, "Bug in bytecode compiler, unexpected node in parse tree while parsing statement."); // Unreachable code.
|
||||
_set_error("Compiler bug (please report): unexpected node in parse tree while parsing statement.", s); // Unreachable code.
|
||||
return ERR_INVALID_DATA;
|
||||
}
|
||||
} break;
|
||||
}
|
||||
|
|
@ -2265,7 +2278,7 @@ GDScriptFunction *GDScriptCompiler::_parse_function(Error &r_error, GDScript *p_
|
|||
return_type = _gdtype_from_datatype(p_func->get_datatype(), p_script);
|
||||
} else {
|
||||
if (p_for_ready) {
|
||||
func_name = SceneStringName(_ready);
|
||||
func_name = "@implicit_ready";
|
||||
} else {
|
||||
func_name = "@implicit_new";
|
||||
}
|
||||
|
|
@ -2325,8 +2338,11 @@ GDScriptFunction *GDScriptCompiler::_parse_function(Error &r_error, GDScript *p_
|
|||
|
||||
GDScriptCodeGenerator::Address dst_address(GDScriptCodeGenerator::Address::MEMBER, codegen.script->member_indices[field->identifier->name].index, field_type);
|
||||
|
||||
if (field_type.has_container_element_type(0)) {
|
||||
if (field_type.builtin_type == Variant::ARRAY && field_type.has_container_element_type(0)) {
|
||||
codegen.generator->write_construct_typed_array(dst_address, field_type.get_container_element_type(0), Vector<GDScriptCodeGenerator::Address>());
|
||||
} else if (field_type.builtin_type == Variant::DICTIONARY && field_type.has_container_element_types()) {
|
||||
codegen.generator->write_construct_typed_dictionary(dst_address, field_type.get_container_element_type_or_variant(0),
|
||||
field_type.get_container_element_type_or_variant(1), Vector<GDScriptCodeGenerator::Address>());
|
||||
} else if (field_type.kind == GDScriptDataType::BUILTIN) {
|
||||
codegen.generator->write_construct(dst_address, field_type.builtin_type, Vector<GDScriptCodeGenerator::Address>());
|
||||
}
|
||||
|
|
@ -2347,7 +2363,7 @@ GDScriptFunction *GDScriptCompiler::_parse_function(Error &r_error, GDScript *p_
|
|||
}
|
||||
|
||||
if (field->onready != is_implicit_ready) {
|
||||
// Only initialize in @implicit_ready.
|
||||
// Only initialize in `@implicit_ready()`.
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
@ -2515,11 +2531,17 @@ GDScriptFunction *GDScriptCompiler::_make_static_initializer(Error &r_error, GDS
|
|||
if (field_type.has_type) {
|
||||
codegen.generator->write_newline(field->start_line);
|
||||
|
||||
if (field_type.has_container_element_type(0)) {
|
||||
if (field_type.builtin_type == Variant::ARRAY && field_type.has_container_element_type(0)) {
|
||||
GDScriptCodeGenerator::Address temp = codegen.add_temporary(field_type);
|
||||
codegen.generator->write_construct_typed_array(temp, field_type.get_container_element_type(0), Vector<GDScriptCodeGenerator::Address>());
|
||||
codegen.generator->write_set_static_variable(temp, class_addr, p_script->static_variables_indices[field->identifier->name].index);
|
||||
codegen.generator->pop_temporary();
|
||||
} else if (field_type.builtin_type == Variant::DICTIONARY && field_type.has_container_element_types()) {
|
||||
GDScriptCodeGenerator::Address temp = codegen.add_temporary(field_type);
|
||||
codegen.generator->write_construct_typed_dictionary(temp, field_type.get_container_element_type_or_variant(0),
|
||||
field_type.get_container_element_type_or_variant(1), Vector<GDScriptCodeGenerator::Address>());
|
||||
codegen.generator->write_set_static_variable(temp, class_addr, p_script->static_variables_indices[field->identifier->name].index);
|
||||
codegen.generator->pop_temporary();
|
||||
} else if (field_type.kind == GDScriptDataType::BUILTIN) {
|
||||
GDScriptCodeGenerator::Address temp = codegen.add_temporary(field_type);
|
||||
codegen.generator->write_construct(temp, field_type.builtin_type, Vector<GDScriptCodeGenerator::Address>());
|
||||
|
|
@ -2619,9 +2641,10 @@ Error GDScriptCompiler::_parse_setter_getter(GDScript *p_script, const GDScriptP
|
|||
return err;
|
||||
}
|
||||
|
||||
// Prepares given script, and inner class scripts, for compilation. It populates class members and initializes method
|
||||
// RPC info for its base classes first, then for itself, then for inner classes.
|
||||
// Warning: this function cannot initiate compilation of other classes, or it will result in cyclic dependency issues.
|
||||
// Prepares given script, and inner class scripts, for compilation. It populates class members and
|
||||
// initializes method RPC info for its base classes first, then for itself, then for inner classes.
|
||||
// WARNING: This function cannot initiate compilation of other classes, or it will result in
|
||||
// cyclic dependency issues.
|
||||
Error GDScriptCompiler::_prepare_compilation(GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state) {
|
||||
if (parsed_classes.has(p_script)) {
|
||||
return OK;
|
||||
|
|
@ -2696,12 +2719,18 @@ Error GDScriptCompiler::_prepare_compilation(GDScript *p_script, const GDScriptP
|
|||
|
||||
GDScriptDataType base_type = _gdtype_from_datatype(p_class->base_type, p_script, false);
|
||||
|
||||
ERR_FAIL_COND_V_MSG(base_type.native_type == StringName(), ERR_BUG, vformat(R"(Failed to get base class for "%s")", p_script->path));
|
||||
if (base_type.native_type == StringName()) {
|
||||
_set_error(vformat(R"(Parser bug (please report): Empty native type in base class "%s")", p_script->path), p_class);
|
||||
return ERR_BUG;
|
||||
}
|
||||
|
||||
int native_idx = GDScriptLanguage::get_singleton()->get_global_map()[base_type.native_type];
|
||||
|
||||
p_script->native = GDScriptLanguage::get_singleton()->get_global_array()[native_idx];
|
||||
ERR_FAIL_COND_V(p_script->native.is_null(), ERR_BUG);
|
||||
if (p_script->native.is_null()) {
|
||||
_set_error("Compiler bug (please report): script native type is null.", nullptr);
|
||||
return ERR_BUG;
|
||||
}
|
||||
|
||||
// Inheritance
|
||||
switch (base_type.kind) {
|
||||
|
|
@ -2711,7 +2740,8 @@ Error GDScriptCompiler::_prepare_compilation(GDScript *p_script, const GDScriptP
|
|||
case GDScriptDataType::GDSCRIPT: {
|
||||
Ref<GDScript> base = Ref<GDScript>(base_type.script_type);
|
||||
if (base.is_null()) {
|
||||
return ERR_COMPILATION_FAILED;
|
||||
_set_error("Compiler bug (please report): base script type is null.", nullptr);
|
||||
return ERR_BUG;
|
||||
}
|
||||
|
||||
if (main_script->has_class(base.ptr())) {
|
||||
|
|
@ -2746,7 +2776,7 @@ Error GDScriptCompiler::_prepare_compilation(GDScript *p_script, const GDScriptP
|
|||
p_script->member_indices = base->member_indices;
|
||||
} break;
|
||||
default: {
|
||||
_set_error("Parser bug: invalid inheritance.", nullptr);
|
||||
_set_error("Parser bug (please report): invalid inheritance.", nullptr);
|
||||
return ERR_BUG;
|
||||
} break;
|
||||
}
|
||||
|
|
@ -2942,7 +2972,7 @@ Error GDScriptCompiler::_compile_class(GDScript *p_script, const GDScriptParser:
|
|||
}
|
||||
|
||||
{
|
||||
// Create an implicit constructor in any case.
|
||||
// Create `@implicit_new()` special function in any case.
|
||||
Error err = OK;
|
||||
_parse_function(err, p_script, p_class, nullptr);
|
||||
if (err) {
|
||||
|
|
@ -2951,7 +2981,7 @@ Error GDScriptCompiler::_compile_class(GDScript *p_script, const GDScriptParser:
|
|||
}
|
||||
|
||||
if (p_class->onready_used) {
|
||||
// Create an implicit_ready constructor.
|
||||
// Create `@implicit_ready()` special function.
|
||||
Error err = OK;
|
||||
_parse_function(err, p_script, p_class, nullptr, true);
|
||||
if (err) {
|
||||
|
|
@ -3048,9 +3078,9 @@ void GDScriptCompiler::convert_to_initializer_type(Variant &p_variant, const GDS
|
|||
if (member_t.is_hard_type() && init_t.is_hard_type() &&
|
||||
member_t.kind == GDScriptParser::DataType::BUILTIN && init_t.kind == GDScriptParser::DataType::BUILTIN) {
|
||||
if (Variant::can_convert_strict(init_t.builtin_type, member_t.builtin_type)) {
|
||||
Variant *v = &p_node->initializer->reduced_value;
|
||||
const Variant *v = &p_node->initializer->reduced_value;
|
||||
Callable::CallError ce;
|
||||
Variant::construct(member_t.builtin_type, p_variant, const_cast<const Variant **>(&v), 1, ce);
|
||||
Variant::construct(member_t.builtin_type, p_variant, &v, 1, ce);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3259,7 +3289,11 @@ Error GDScriptCompiler::compile(const GDScriptParser *p_parser, GDScript *p_scri
|
|||
GDScriptCache::add_static_script(p_script);
|
||||
}
|
||||
|
||||
return GDScriptCache::finish_compiling(main_script->path);
|
||||
err = GDScriptCache::finish_compiling(main_script->path);
|
||||
if (err) {
|
||||
_set_error(R"(Failed to compile depended scripts.)", nullptr);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
String GDScriptCompiler::get_error() const {
|
||||
|
|
|
|||
|
|
@ -176,6 +176,47 @@ void GDScriptFunction::disassemble(const Vector<String> &p_code_lines) const {
|
|||
|
||||
incr += 6;
|
||||
} break;
|
||||
case OPCODE_TYPE_TEST_DICTIONARY: {
|
||||
text += "type test ";
|
||||
text += DADDR(1);
|
||||
text += " = ";
|
||||
text += DADDR(2);
|
||||
text += " is Dictionary[";
|
||||
|
||||
Ref<Script> key_script_type = get_constant(_code_ptr[ip + 3] & ADDR_MASK);
|
||||
Variant::Type key_builtin_type = (Variant::Type)_code_ptr[ip + 5];
|
||||
StringName key_native_type = get_global_name(_code_ptr[ip + 6]);
|
||||
|
||||
if (key_script_type.is_valid() && key_script_type->is_valid()) {
|
||||
text += "script(";
|
||||
text += GDScript::debug_get_script_name(key_script_type);
|
||||
text += ")";
|
||||
} else if (key_native_type != StringName()) {
|
||||
text += key_native_type;
|
||||
} else {
|
||||
text += Variant::get_type_name(key_builtin_type);
|
||||
}
|
||||
|
||||
text += ", ";
|
||||
|
||||
Ref<Script> value_script_type = get_constant(_code_ptr[ip + 4] & ADDR_MASK);
|
||||
Variant::Type value_builtin_type = (Variant::Type)_code_ptr[ip + 7];
|
||||
StringName value_native_type = get_global_name(_code_ptr[ip + 8]);
|
||||
|
||||
if (value_script_type.is_valid() && value_script_type->is_valid()) {
|
||||
text += "script(";
|
||||
text += GDScript::debug_get_script_name(value_script_type);
|
||||
text += ")";
|
||||
} else if (value_native_type != StringName()) {
|
||||
text += value_native_type;
|
||||
} else {
|
||||
text += Variant::get_type_name(value_builtin_type);
|
||||
}
|
||||
|
||||
text += "]";
|
||||
|
||||
incr += 9;
|
||||
} break;
|
||||
case OPCODE_TYPE_TEST_NATIVE: {
|
||||
text += "type test ";
|
||||
text += DADDR(1);
|
||||
|
|
@ -321,7 +362,12 @@ void GDScriptFunction::disassemble(const Vector<String> &p_code_lines) const {
|
|||
incr += 3;
|
||||
} break;
|
||||
case OPCODE_SET_STATIC_VARIABLE: {
|
||||
Ref<GDScript> gdscript = get_constant(_code_ptr[ip + 2] & ADDR_MASK);
|
||||
Ref<GDScript> gdscript;
|
||||
if (_code_ptr[ip + 2] == ADDR_CLASS) {
|
||||
gdscript = Ref<GDScript>(_script);
|
||||
} else {
|
||||
gdscript = get_constant(_code_ptr[ip + 2] & ADDR_MASK);
|
||||
}
|
||||
|
||||
text += "set_static_variable script(";
|
||||
text += GDScript::debug_get_script_name(gdscript);
|
||||
|
|
@ -337,7 +383,12 @@ void GDScriptFunction::disassemble(const Vector<String> &p_code_lines) const {
|
|||
incr += 4;
|
||||
} break;
|
||||
case OPCODE_GET_STATIC_VARIABLE: {
|
||||
Ref<GDScript> gdscript = get_constant(_code_ptr[ip + 2] & ADDR_MASK);
|
||||
Ref<GDScript> gdscript;
|
||||
if (_code_ptr[ip + 2] == ADDR_CLASS) {
|
||||
gdscript = Ref<GDScript>(_script);
|
||||
} else {
|
||||
gdscript = get_constant(_code_ptr[ip + 2] & ADDR_MASK);
|
||||
}
|
||||
|
||||
text += "get_static_variable ";
|
||||
text += DADDR(1);
|
||||
|
|
@ -399,6 +450,14 @@ void GDScriptFunction::disassemble(const Vector<String> &p_code_lines) const {
|
|||
|
||||
incr += 6;
|
||||
} break;
|
||||
case OPCODE_ASSIGN_TYPED_DICTIONARY: {
|
||||
text += "assign typed dictionary ";
|
||||
text += DADDR(1);
|
||||
text += " = ";
|
||||
text += DADDR(2);
|
||||
|
||||
incr += 9;
|
||||
} break;
|
||||
case OPCODE_ASSIGN_TYPED_NATIVE: {
|
||||
text += "assign typed native (";
|
||||
text += DADDR(3);
|
||||
|
|
@ -564,6 +623,58 @@ void GDScriptFunction::disassemble(const Vector<String> &p_code_lines) const {
|
|||
|
||||
incr += 3 + argc * 2;
|
||||
} break;
|
||||
case OPCODE_CONSTRUCT_TYPED_DICTIONARY: {
|
||||
int instr_var_args = _code_ptr[++ip];
|
||||
int argc = _code_ptr[ip + 1 + instr_var_args];
|
||||
|
||||
Ref<Script> key_script_type = get_constant(_code_ptr[ip + argc * 2 + 2] & ADDR_MASK);
|
||||
Variant::Type key_builtin_type = (Variant::Type)_code_ptr[ip + argc * 2 + 5];
|
||||
StringName key_native_type = get_global_name(_code_ptr[ip + argc * 2 + 6]);
|
||||
|
||||
String key_type_name;
|
||||
if (key_script_type.is_valid() && key_script_type->is_valid()) {
|
||||
key_type_name = "script(" + GDScript::debug_get_script_name(key_script_type) + ")";
|
||||
} else if (key_native_type != StringName()) {
|
||||
key_type_name = key_native_type;
|
||||
} else {
|
||||
key_type_name = Variant::get_type_name(key_builtin_type);
|
||||
}
|
||||
|
||||
Ref<Script> value_script_type = get_constant(_code_ptr[ip + argc * 2 + 3] & ADDR_MASK);
|
||||
Variant::Type value_builtin_type = (Variant::Type)_code_ptr[ip + argc * 2 + 7];
|
||||
StringName value_native_type = get_global_name(_code_ptr[ip + argc * 2 + 8]);
|
||||
|
||||
String value_type_name;
|
||||
if (value_script_type.is_valid() && value_script_type->is_valid()) {
|
||||
value_type_name = "script(" + GDScript::debug_get_script_name(value_script_type) + ")";
|
||||
} else if (value_native_type != StringName()) {
|
||||
value_type_name = value_native_type;
|
||||
} else {
|
||||
value_type_name = Variant::get_type_name(value_builtin_type);
|
||||
}
|
||||
|
||||
text += "make_typed_dict (";
|
||||
text += key_type_name;
|
||||
text += ", ";
|
||||
text += value_type_name;
|
||||
text += ") ";
|
||||
|
||||
text += DADDR(1 + argc * 2);
|
||||
text += " = {";
|
||||
|
||||
for (int i = 0; i < argc; i++) {
|
||||
if (i > 0) {
|
||||
text += ", ";
|
||||
}
|
||||
text += DADDR(1 + i * 2 + 0);
|
||||
text += ": ";
|
||||
text += DADDR(1 + i * 2 + 1);
|
||||
}
|
||||
|
||||
text += "}";
|
||||
|
||||
incr += 9 + argc * 2;
|
||||
} break;
|
||||
case OPCODE_CALL:
|
||||
case OPCODE_CALL_RETURN:
|
||||
case OPCODE_CALL_ASYNC: {
|
||||
|
|
@ -689,8 +800,9 @@ void GDScriptFunction::disassemble(const Vector<String> &p_code_lines) const {
|
|||
text += method->get_name();
|
||||
text += "(";
|
||||
for (int i = 0; i < argc; i++) {
|
||||
if (i > 0)
|
||||
if (i > 0) {
|
||||
text += ", ";
|
||||
}
|
||||
text += DADDR(1 + i);
|
||||
}
|
||||
text += ")";
|
||||
|
|
@ -732,8 +844,9 @@ void GDScriptFunction::disassemble(const Vector<String> &p_code_lines) const {
|
|||
text += method->get_name();
|
||||
text += "(";
|
||||
for (int i = 0; i < argc; i++) {
|
||||
if (i > 0)
|
||||
if (i > 0) {
|
||||
text += ", ";
|
||||
}
|
||||
text += DADDR(1 + i);
|
||||
}
|
||||
text += ")";
|
||||
|
|
@ -978,6 +1091,12 @@ void GDScriptFunction::disassemble(const Vector<String> &p_code_lines) const {
|
|||
|
||||
incr += 5;
|
||||
} break;
|
||||
case OPCODE_RETURN_TYPED_DICTIONARY: {
|
||||
text += "return typed dictionary ";
|
||||
text += DADDR(1);
|
||||
|
||||
incr += 8;
|
||||
} break;
|
||||
case OPCODE_RETURN_TYPED_NATIVE: {
|
||||
text += "return typed native (";
|
||||
text += DADDR(2);
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -93,6 +93,41 @@ public:
|
|||
} else {
|
||||
valid = false;
|
||||
}
|
||||
} else if (valid && builtin_type == Variant::DICTIONARY && has_container_element_types()) {
|
||||
Dictionary dictionary = p_variant;
|
||||
if (dictionary.is_typed()) {
|
||||
if (dictionary.is_typed_key()) {
|
||||
GDScriptDataType key = get_container_element_type_or_variant(0);
|
||||
Variant::Type key_builtin_type = (Variant::Type)dictionary.get_typed_key_builtin();
|
||||
StringName key_native_type = dictionary.get_typed_key_class_name();
|
||||
Ref<Script> key_script_type_ref = dictionary.get_typed_key_script();
|
||||
|
||||
if (key_script_type_ref.is_valid()) {
|
||||
valid = (key.kind == SCRIPT || key.kind == GDSCRIPT) && key.script_type == key_script_type_ref.ptr();
|
||||
} else if (key_native_type != StringName()) {
|
||||
valid = key.kind == NATIVE && key.native_type == key_native_type;
|
||||
} else {
|
||||
valid = key.kind == BUILTIN && key.builtin_type == key_builtin_type;
|
||||
}
|
||||
}
|
||||
|
||||
if (valid && dictionary.is_typed_value()) {
|
||||
GDScriptDataType value = get_container_element_type_or_variant(1);
|
||||
Variant::Type value_builtin_type = (Variant::Type)dictionary.get_typed_value_builtin();
|
||||
StringName value_native_type = dictionary.get_typed_value_class_name();
|
||||
Ref<Script> value_script_type_ref = dictionary.get_typed_value_script();
|
||||
|
||||
if (value_script_type_ref.is_valid()) {
|
||||
valid = (value.kind == SCRIPT || value.kind == GDSCRIPT) && value.script_type == value_script_type_ref.ptr();
|
||||
} else if (value_native_type != StringName()) {
|
||||
valid = value.kind == NATIVE && value.native_type == value_native_type;
|
||||
} else {
|
||||
valid = value.kind == BUILTIN && value.builtin_type == value_builtin_type;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
valid = false;
|
||||
}
|
||||
} else if (!valid && p_allow_implicit_conversion) {
|
||||
valid = Variant::can_convert_strict(var_type, builtin_type);
|
||||
}
|
||||
|
|
@ -156,6 +191,10 @@ public:
|
|||
}
|
||||
return true;
|
||||
case Variant::DICTIONARY:
|
||||
if (has_container_element_types()) {
|
||||
return get_container_element_type_or_variant(0).can_contain_object() || get_container_element_type_or_variant(1).can_contain_object();
|
||||
}
|
||||
return true;
|
||||
case Variant::NIL:
|
||||
case Variant::OBJECT:
|
||||
return true;
|
||||
|
|
@ -220,6 +259,7 @@ public:
|
|||
OPCODE_OPERATOR_VALIDATED,
|
||||
OPCODE_TYPE_TEST_BUILTIN,
|
||||
OPCODE_TYPE_TEST_ARRAY,
|
||||
OPCODE_TYPE_TEST_DICTIONARY,
|
||||
OPCODE_TYPE_TEST_NATIVE,
|
||||
OPCODE_TYPE_TEST_SCRIPT,
|
||||
OPCODE_SET_KEYED,
|
||||
|
|
@ -242,6 +282,7 @@ public:
|
|||
OPCODE_ASSIGN_FALSE,
|
||||
OPCODE_ASSIGN_TYPED_BUILTIN,
|
||||
OPCODE_ASSIGN_TYPED_ARRAY,
|
||||
OPCODE_ASSIGN_TYPED_DICTIONARY,
|
||||
OPCODE_ASSIGN_TYPED_NATIVE,
|
||||
OPCODE_ASSIGN_TYPED_SCRIPT,
|
||||
OPCODE_CAST_TO_BUILTIN,
|
||||
|
|
@ -252,6 +293,7 @@ public:
|
|||
OPCODE_CONSTRUCT_ARRAY,
|
||||
OPCODE_CONSTRUCT_TYPED_ARRAY,
|
||||
OPCODE_CONSTRUCT_DICTIONARY,
|
||||
OPCODE_CONSTRUCT_TYPED_DICTIONARY,
|
||||
OPCODE_CALL,
|
||||
OPCODE_CALL_RETURN,
|
||||
OPCODE_CALL_ASYNC,
|
||||
|
|
@ -280,6 +322,7 @@ public:
|
|||
OPCODE_RETURN,
|
||||
OPCODE_RETURN_TYPED_BUILTIN,
|
||||
OPCODE_RETURN_TYPED_ARRAY,
|
||||
OPCODE_RETURN_TYPED_DICTIONARY,
|
||||
OPCODE_RETURN_TYPED_NATIVE,
|
||||
OPCODE_RETURN_TYPED_SCRIPT,
|
||||
OPCODE_ITERATE_BEGIN,
|
||||
|
|
@ -509,7 +552,7 @@ private:
|
|||
} profile;
|
||||
#endif
|
||||
|
||||
_FORCE_INLINE_ String _get_call_error(const Callable::CallError &p_err, const String &p_where, const Variant **argptrs) const;
|
||||
_FORCE_INLINE_ String _get_call_error(const String &p_where, const Variant **p_argptrs, const Variant &p_ret, const Callable::CallError &p_err) const;
|
||||
Variant _get_default_variant_for_data_type(const GDScriptDataType &p_data_type);
|
||||
|
||||
public:
|
||||
|
|
|
|||
|
|
@ -97,25 +97,25 @@ void GDScriptLambdaCallable::call(const Variant **p_arguments, int p_argcount, V
|
|||
}
|
||||
|
||||
if (captures_amount > 0) {
|
||||
Vector<const Variant *> args;
|
||||
args.resize(p_argcount + captures_amount);
|
||||
const int total_argcount = p_argcount + captures_amount;
|
||||
const Variant **args = (const Variant **)alloca(sizeof(Variant *) * total_argcount);
|
||||
for (int i = 0; i < captures_amount; i++) {
|
||||
args.write[i] = &captures[i];
|
||||
args[i] = &captures[i];
|
||||
if (captures[i].get_type() == Variant::OBJECT) {
|
||||
bool was_freed = false;
|
||||
captures[i].get_validated_object_with_check(was_freed);
|
||||
if (was_freed) {
|
||||
ERR_PRINT(vformat(R"(Lambda capture at index %d was freed. Passed "null" instead.)", i));
|
||||
static Variant nil;
|
||||
args.write[i] = &nil;
|
||||
args[i] = &nil;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < p_argcount; i++) {
|
||||
args.write[i + captures_amount] = p_arguments[i];
|
||||
args[i + captures_amount] = p_arguments[i];
|
||||
}
|
||||
|
||||
r_return_value = function->call(nullptr, args.ptrw(), args.size(), r_call_error);
|
||||
r_return_value = function->call(nullptr, args, total_argcount, r_call_error);
|
||||
switch (r_call_error.error) {
|
||||
case Callable::CallError::CALL_ERROR_INVALID_ARGUMENT:
|
||||
r_call_error.argument -= captures_amount;
|
||||
|
|
@ -150,7 +150,7 @@ void GDScriptLambdaCallable::call(const Variant **p_arguments, int p_argcount, V
|
|||
|
||||
GDScriptLambdaCallable::GDScriptLambdaCallable(Ref<GDScript> p_script, GDScriptFunction *p_function, const Vector<Variant> &p_captures) :
|
||||
function(p_function) {
|
||||
ERR_FAIL_NULL(p_script.ptr());
|
||||
ERR_FAIL_COND(p_script.is_null());
|
||||
ERR_FAIL_NULL(p_function);
|
||||
script = p_script;
|
||||
captures = p_captures;
|
||||
|
|
@ -178,12 +178,12 @@ uint32_t GDScriptLambdaSelfCallable::hash() const {
|
|||
|
||||
String GDScriptLambdaSelfCallable::get_as_text() const {
|
||||
if (function == nullptr) {
|
||||
return "<invalid lambda>";
|
||||
return "<invalid self lambda>";
|
||||
}
|
||||
if (function->get_name() != StringName()) {
|
||||
return function->get_name().operator String() + "(lambda)";
|
||||
return function->get_name().operator String() + "(self lambda)";
|
||||
}
|
||||
return "(anonymous lambda)";
|
||||
return "(anonymous self lambda)";
|
||||
}
|
||||
|
||||
CallableCustom::CompareEqualFunc GDScriptLambdaSelfCallable::get_compare_equal_func() const {
|
||||
|
|
@ -229,25 +229,25 @@ void GDScriptLambdaSelfCallable::call(const Variant **p_arguments, int p_argcoun
|
|||
}
|
||||
|
||||
if (captures_amount > 0) {
|
||||
Vector<const Variant *> args;
|
||||
args.resize(p_argcount + captures_amount);
|
||||
const int total_argcount = p_argcount + captures_amount;
|
||||
const Variant **args = (const Variant **)alloca(sizeof(Variant *) * total_argcount);
|
||||
for (int i = 0; i < captures_amount; i++) {
|
||||
args.write[i] = &captures[i];
|
||||
args[i] = &captures[i];
|
||||
if (captures[i].get_type() == Variant::OBJECT) {
|
||||
bool was_freed = false;
|
||||
captures[i].get_validated_object_with_check(was_freed);
|
||||
if (was_freed) {
|
||||
ERR_PRINT(vformat(R"(Lambda capture at index %d was freed. Passed "null" instead.)", i));
|
||||
static Variant nil;
|
||||
args.write[i] = &nil;
|
||||
args[i] = &nil;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < p_argcount; i++) {
|
||||
args.write[i + captures_amount] = p_arguments[i];
|
||||
args[i + captures_amount] = p_arguments[i];
|
||||
}
|
||||
|
||||
r_return_value = function->call(static_cast<GDScriptInstance *>(object->get_script_instance()), args.ptrw(), args.size(), r_call_error);
|
||||
r_return_value = function->call(static_cast<GDScriptInstance *>(object->get_script_instance()), args, total_argcount, r_call_error);
|
||||
switch (r_call_error.error) {
|
||||
case Callable::CallError::CALL_ERROR_INVALID_ARGUMENT:
|
||||
r_call_error.argument -= captures_amount;
|
||||
|
|
@ -282,7 +282,7 @@ void GDScriptLambdaSelfCallable::call(const Variant **p_arguments, int p_argcoun
|
|||
|
||||
GDScriptLambdaSelfCallable::GDScriptLambdaSelfCallable(Ref<RefCounted> p_self, GDScriptFunction *p_function, const Vector<Variant> &p_captures) :
|
||||
function(p_function) {
|
||||
ERR_FAIL_NULL(p_self.ptr());
|
||||
ERR_FAIL_COND(p_self.is_null());
|
||||
ERR_FAIL_NULL(p_function);
|
||||
reference = p_self;
|
||||
object = p_self.ptr();
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -45,7 +45,6 @@
|
|||
#include "core/string/ustring.h"
|
||||
#include "core/templates/hash_map.h"
|
||||
#include "core/templates/list.h"
|
||||
#include "core/templates/rb_map.h"
|
||||
#include "core/templates/vector.h"
|
||||
#include "core/variant/variant.h"
|
||||
|
||||
|
|
@ -165,6 +164,10 @@ public:
|
|||
container_element_types.write[p_index] = DataType(p_type);
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ int get_container_element_type_count() const {
|
||||
return container_element_types.size();
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ DataType get_container_element_type(int p_index) const {
|
||||
ERR_FAIL_INDEX_V(p_index, container_element_types.size(), get_variant_type());
|
||||
return container_element_types[p_index];
|
||||
|
|
@ -189,6 +192,8 @@ public:
|
|||
|
||||
GDScriptParser::DataType get_typed_container_type() const;
|
||||
|
||||
bool can_reference(const DataType &p_other) const;
|
||||
|
||||
bool operator==(const DataType &p_other) const {
|
||||
if (type_source == UNDETECTED || p_other.type_source == UNDETECTED) {
|
||||
return true; // Can be considered equal for parsing purposes.
|
||||
|
|
@ -367,6 +372,7 @@ public:
|
|||
Vector<ExpressionNode *> arguments;
|
||||
Vector<Variant> resolved_arguments;
|
||||
|
||||
/** Information of the annotation. Might be null for unknown annotations. */
|
||||
AnnotationInfo *info = nullptr;
|
||||
PropertyInfo export_info;
|
||||
bool is_resolved = false;
|
||||
|
|
@ -856,6 +862,7 @@ public:
|
|||
Vector<Variant> default_arg_values;
|
||||
#ifdef TOOLS_ENABLED
|
||||
MemberDocData doc_data;
|
||||
int min_local_doc_line = 0;
|
||||
#endif // TOOLS_ENABLED
|
||||
|
||||
bool resolved_signature = false;
|
||||
|
|
@ -1352,6 +1359,7 @@ private:
|
|||
List<GDScriptWarning> warnings;
|
||||
List<PendingWarning> pending_warnings;
|
||||
HashSet<int> warning_ignored_lines[GDScriptWarning::WARNING_MAX];
|
||||
int warning_ignore_start_lines[GDScriptWarning::WARNING_MAX];
|
||||
HashSet<int> unsafe_lines;
|
||||
#endif
|
||||
|
||||
|
|
@ -1371,7 +1379,7 @@ private:
|
|||
bool in_lambda = false;
|
||||
bool lambda_ended = false; // Marker for when a lambda ends, to apply an end of statement if needed.
|
||||
|
||||
typedef bool (GDScriptParser::*AnnotationAction)(const AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class);
|
||||
typedef bool (GDScriptParser::*AnnotationAction)(AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class);
|
||||
struct AnnotationInfo {
|
||||
enum TargetKind {
|
||||
NONE = 0,
|
||||
|
|
@ -1455,9 +1463,14 @@ private:
|
|||
}
|
||||
void apply_pending_warnings();
|
||||
#endif
|
||||
|
||||
void make_completion_context(CompletionType p_type, Node *p_node, int p_argument = -1, bool p_force = false);
|
||||
void make_completion_context(CompletionType p_type, Variant::Type p_builtin_type, bool p_force = false);
|
||||
// Setting p_force to false will prevent the completion context from being update if a context was already set before.
|
||||
// This should only be done when we push context before we consumed any tokens for the corresponding structure.
|
||||
// See parse_precedence for an example.
|
||||
void make_completion_context(CompletionType p_type, Node *p_node, int p_argument = -1, bool p_force = true);
|
||||
void make_completion_context(CompletionType p_type, Variant::Type p_builtin_type, bool p_force = true);
|
||||
// In some cases it might become necessary to alter the completion context after parsing a subexpression.
|
||||
// For example to not override COMPLETE_CALL_ARGUMENTS with COMPLETION_NONE from string literals.
|
||||
void override_completion_context(const Node *p_for_node, CompletionType p_type, Node *p_node, int p_argument = -1);
|
||||
void push_completion_call(Node *p_call);
|
||||
void pop_completion_call();
|
||||
void set_last_completion_call_arg(int p_argument);
|
||||
|
|
@ -1493,18 +1506,20 @@ private:
|
|||
static bool register_annotation(const MethodInfo &p_info, uint32_t p_target_kinds, AnnotationAction p_apply, const Vector<Variant> &p_default_arguments = Vector<Variant>(), bool p_is_vararg = false);
|
||||
bool validate_annotation_arguments(AnnotationNode *p_annotation);
|
||||
void clear_unused_annotations();
|
||||
bool tool_annotation(const AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class);
|
||||
bool icon_annotation(const AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class);
|
||||
bool onready_annotation(const AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class);
|
||||
bool tool_annotation(AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class);
|
||||
bool icon_annotation(AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class);
|
||||
bool static_unload_annotation(AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class);
|
||||
bool onready_annotation(AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class);
|
||||
template <PropertyHint t_hint, Variant::Type t_type>
|
||||
bool export_annotations(const AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class);
|
||||
bool export_storage_annotation(const AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class);
|
||||
bool export_custom_annotation(const AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class);
|
||||
bool export_annotations(AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class);
|
||||
bool export_storage_annotation(AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class);
|
||||
bool export_custom_annotation(AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class);
|
||||
bool export_tool_button_annotation(AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class);
|
||||
template <PropertyUsageFlags t_usage>
|
||||
bool export_group_annotations(const AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class);
|
||||
bool warning_annotations(const AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class);
|
||||
bool rpc_annotation(const AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class);
|
||||
bool static_unload_annotation(const AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class);
|
||||
bool export_group_annotations(AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class);
|
||||
bool warning_ignore_annotation(AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class);
|
||||
bool warning_ignore_region_annotations(AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class);
|
||||
bool rpc_annotation(AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class);
|
||||
// Statements.
|
||||
Node *parse_statement();
|
||||
VariableNode *parse_variable(bool p_is_static);
|
||||
|
|
@ -1589,6 +1604,8 @@ public:
|
|||
|
||||
#ifdef TOOLS_ENABLED
|
||||
static HashMap<String, String> theme_color_names;
|
||||
|
||||
HashMap<int, GDScriptTokenizer::CommentData> comment_data;
|
||||
#endif // TOOLS_ENABLED
|
||||
|
||||
GDScriptParser();
|
||||
|
|
|
|||
|
|
@ -49,7 +49,14 @@ uint32_t GDScriptRPCCallable::hash() const {
|
|||
String GDScriptRPCCallable::get_as_text() const {
|
||||
String class_name = object->get_class();
|
||||
Ref<Script> script = object->get_script();
|
||||
return class_name + "(" + script->get_path().get_file() + ")::" + String(method) + " (rpc)";
|
||||
if (script.is_valid()) {
|
||||
if (!script->get_global_name().is_empty()) {
|
||||
class_name += "(" + script->get_global_name() + ")";
|
||||
} else if (script->get_path().is_resource_file()) {
|
||||
class_name += "(" + script->get_path().get_file() + ")";
|
||||
}
|
||||
}
|
||||
return class_name + "::" + String(method) + " (rpc)";
|
||||
}
|
||||
|
||||
CallableCustom::CompareEqualFunc GDScriptRPCCallable::get_compare_equal_func() const {
|
||||
|
|
|
|||
|
|
@ -164,6 +164,15 @@ const char *GDScriptTokenizer::Token::get_name() const {
|
|||
return token_names[type];
|
||||
}
|
||||
|
||||
String GDScriptTokenizer::Token::get_debug_name() const {
|
||||
switch (type) {
|
||||
case IDENTIFIER:
|
||||
return vformat(R"(identifier "%s")", source);
|
||||
default:
|
||||
return vformat(R"("%s")", get_name());
|
||||
}
|
||||
}
|
||||
|
||||
bool GDScriptTokenizer::Token::can_precede_bin_op() const {
|
||||
switch (type) {
|
||||
case IDENTIFIER:
|
||||
|
|
@ -687,13 +696,13 @@ GDScriptTokenizer::Token GDScriptTokenizerText::number() {
|
|||
if (_peek(-1) == '.') {
|
||||
has_decimal = true;
|
||||
} else if (_peek(-1) == '0') {
|
||||
if (_peek() == 'x') {
|
||||
if (_peek() == 'x' || _peek() == 'X') {
|
||||
// Hexadecimal.
|
||||
base = 16;
|
||||
digit_check_func = is_hex_digit;
|
||||
need_digits = true;
|
||||
_advance();
|
||||
} else if (_peek() == 'b') {
|
||||
} else if (_peek() == 'b' || _peek() == 'B') {
|
||||
// Binary.
|
||||
base = 2;
|
||||
digit_check_func = is_binary_digit;
|
||||
|
|
|
|||
|
|
@ -32,7 +32,6 @@
|
|||
#define GDSCRIPT_TOKENIZER_H
|
||||
|
||||
#include "core/templates/hash_map.h"
|
||||
#include "core/templates/hash_set.h"
|
||||
#include "core/templates/list.h"
|
||||
#include "core/templates/vector.h"
|
||||
#include "core/variant/variant.h"
|
||||
|
|
@ -178,6 +177,7 @@ public:
|
|||
String source;
|
||||
|
||||
const char *get_name() const;
|
||||
String get_debug_name() const;
|
||||
bool can_precede_bin_op() const;
|
||||
bool is_identifier() const;
|
||||
bool is_node_name() const;
|
||||
|
|
|
|||
|
|
@ -296,6 +296,7 @@ Vector<uint8_t> GDScriptTokenizerBuffer::parse_code_string(const String &p_code,
|
|||
encode_uint32(identifier_map.size(), &contents.write[0]);
|
||||
encode_uint32(constant_map.size(), &contents.write[4]);
|
||||
encode_uint32(token_lines.size(), &contents.write[8]);
|
||||
encode_uint32(0, &contents.write[12]); // Unused, kept for compatibility. Please remove at next `TOKENIZER_VERSION` increment.
|
||||
encode_uint32(token_counter, &contents.write[16]);
|
||||
|
||||
int buf_pos = 20;
|
||||
|
|
|
|||
|
|
@ -79,7 +79,7 @@ public:
|
|||
virtual bool is_past_cursor() const override;
|
||||
virtual void push_expression_indented_block() override; // For lambdas, or blocks inside expressions.
|
||||
virtual void pop_expression_indented_block() override; // For lambdas, or blocks inside expressions.
|
||||
virtual bool is_text() override { return false; };
|
||||
virtual bool is_text() override { return false; }
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
virtual const HashMap<int, CommentData> &get_comments() const override {
|
||||
|
|
|
|||
|
|
@ -30,8 +30,6 @@
|
|||
|
||||
#include "gdscript_utility_callable.h"
|
||||
|
||||
#include "core/templates/hashfuncs.h"
|
||||
|
||||
bool GDScriptUtilityCallable::compare_equal(const CallableCustom *p_a, const CallableCustom *p_b) {
|
||||
return p_a->hash() == p_b->hash();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,7 +34,6 @@
|
|||
|
||||
#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"
|
||||
|
|
@ -42,101 +41,105 @@
|
|||
|
||||
#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.expected = m_count; \
|
||||
#define DEBUG_VALIDATE_ARG_COUNT(m_min_count, m_max_count) \
|
||||
if (unlikely(p_arg_count < m_min_count)) { \
|
||||
*r_ret = Variant(); \
|
||||
r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; \
|
||||
r_error.expected = m_min_count; \
|
||||
return; \
|
||||
} \
|
||||
if (p_arg_count > m_count) { \
|
||||
r_error.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS; \
|
||||
r_error.expected = m_count; \
|
||||
if (unlikely(p_arg_count > m_max_count)) { \
|
||||
*r_ret = Variant(); \
|
||||
r_error.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS; \
|
||||
r_error.expected = m_max_count; \
|
||||
return; \
|
||||
}
|
||||
|
||||
#define VALIDATE_ARG_INT(m_arg) \
|
||||
if (p_args[m_arg]->get_type() != Variant::INT) { \
|
||||
#define DEBUG_VALIDATE_ARG_TYPE(m_arg, m_type) \
|
||||
if (unlikely(!Variant::can_convert_strict(p_args[m_arg]->get_type(), m_type))) { \
|
||||
*r_ret = Variant(); \
|
||||
r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; \
|
||||
r_error.argument = m_arg; \
|
||||
r_error.expected = m_type; \
|
||||
return; \
|
||||
}
|
||||
|
||||
#define DEBUG_VALIDATE_ARG_CUSTOM(m_arg, m_type, m_cond, m_msg) \
|
||||
if (unlikely(m_cond)) { \
|
||||
*r_ret = m_msg; \
|
||||
r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; \
|
||||
r_error.argument = m_arg; \
|
||||
r_error.expected = Variant::INT; \
|
||||
*r_ret = Variant(); \
|
||||
r_error.expected = m_type; \
|
||||
return; \
|
||||
}
|
||||
|
||||
#define VALIDATE_ARG_NUM(m_arg) \
|
||||
if (!p_args[m_arg]->is_num()) { \
|
||||
#else // !DEBUG_ENABLED
|
||||
|
||||
#define DEBUG_VALIDATE_ARG_COUNT(m_min_count, m_max_count)
|
||||
#define DEBUG_VALIDATE_ARG_TYPE(m_arg, m_type)
|
||||
#define DEBUG_VALIDATE_ARG_CUSTOM(m_arg, m_type, m_cond, m_msg)
|
||||
|
||||
#endif // DEBUG_ENABLED
|
||||
|
||||
#define VALIDATE_ARG_CUSTOM(m_arg, m_type, m_cond, m_msg) \
|
||||
if (unlikely(m_cond)) { \
|
||||
*r_ret = m_msg; \
|
||||
r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; \
|
||||
r_error.argument = m_arg; \
|
||||
r_error.expected = Variant::FLOAT; \
|
||||
*r_ret = Variant(); \
|
||||
r_error.expected = m_type; \
|
||||
return; \
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#define VALIDATE_ARG_COUNT(m_count)
|
||||
#define VALIDATE_ARG_INT(m_arg)
|
||||
#define VALIDATE_ARG_NUM(m_arg)
|
||||
|
||||
#endif
|
||||
#define GDFUNC_FAIL_COND_MSG(m_cond, m_msg) \
|
||||
if (unlikely(m_cond)) { \
|
||||
*r_ret = m_msg; \
|
||||
r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; \
|
||||
return; \
|
||||
}
|
||||
|
||||
struct GDScriptUtilityFunctionsDefinitions {
|
||||
#ifndef DISABLE_DEPRECATED
|
||||
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;
|
||||
DEBUG_VALIDATE_ARG_COUNT(2, 2);
|
||||
DEBUG_VALIDATE_ARG_TYPE(1, Variant::INT);
|
||||
|
||||
} else {
|
||||
Variant::construct(Variant::Type(type), *r_ret, p_args, 1, r_error);
|
||||
if (r_error.error != Callable::CallError::CALL_OK) {
|
||||
*r_ret = vformat(RTR(R"(Cannot convert "%s" to "%s".)"), Variant::get_type_name(p_args[0]->get_type()), Variant::get_type_name(Variant::Type(type)));
|
||||
}
|
||||
}
|
||||
int type = *p_args[1];
|
||||
DEBUG_VALIDATE_ARG_CUSTOM(1, Variant::INT, type < 0 || type >= Variant::VARIANT_MAX,
|
||||
RTR("Invalid type argument to convert(), use TYPE_* constants."));
|
||||
|
||||
Variant::construct(Variant::Type(type), *r_ret, p_args, 1, r_error);
|
||||
}
|
||||
#endif // DISABLE_DEPRECATED
|
||||
|
||||
static inline void type_exists(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
|
||||
VALIDATE_ARG_COUNT(1);
|
||||
DEBUG_VALIDATE_ARG_COUNT(1, 1);
|
||||
DEBUG_VALIDATE_ARG_TYPE(0, Variant::STRING_NAME);
|
||||
*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);
|
||||
DEBUG_VALIDATE_ARG_COUNT(1, 1);
|
||||
DEBUG_VALIDATE_ARG_TYPE(0, Variant::INT);
|
||||
char32_t result[2] = { *p_args[0], 0 };
|
||||
*r_ret = String(result);
|
||||
}
|
||||
|
||||
static inline void range(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
|
||||
DEBUG_VALIDATE_ARG_COUNT(1, 3);
|
||||
switch (p_arg_count) {
|
||||
case 0: {
|
||||
r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
|
||||
r_error.expected = 1;
|
||||
*r_ret = Variant();
|
||||
} break;
|
||||
case 1: {
|
||||
VALIDATE_ARG_NUM(0);
|
||||
DEBUG_VALIDATE_ARG_TYPE(0, Variant::INT);
|
||||
|
||||
int count = *p_args[0];
|
||||
|
||||
Array arr;
|
||||
if (count <= 0) {
|
||||
*r_ret = arr;
|
||||
return;
|
||||
}
|
||||
|
||||
Error err = arr.resize(count);
|
||||
if (err != OK) {
|
||||
*r_ret = RTR("Cannot resize array.");
|
||||
r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
|
||||
return;
|
||||
}
|
||||
GDFUNC_FAIL_COND_MSG(err != OK, RTR("Cannot resize array."));
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
arr[i] = i;
|
||||
|
|
@ -145,8 +148,8 @@ struct GDScriptUtilityFunctionsDefinitions {
|
|||
*r_ret = arr;
|
||||
} break;
|
||||
case 2: {
|
||||
VALIDATE_ARG_NUM(0);
|
||||
VALIDATE_ARG_NUM(1);
|
||||
DEBUG_VALIDATE_ARG_TYPE(0, Variant::INT);
|
||||
DEBUG_VALIDATE_ARG_TYPE(1, Variant::INT);
|
||||
|
||||
int from = *p_args[0];
|
||||
int to = *p_args[1];
|
||||
|
|
@ -156,30 +159,26 @@ struct GDScriptUtilityFunctionsDefinitions {
|
|||
*r_ret = arr;
|
||||
return;
|
||||
}
|
||||
|
||||
Error err = arr.resize(to - from);
|
||||
if (err != OK) {
|
||||
*r_ret = RTR("Cannot resize array.");
|
||||
r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
|
||||
return;
|
||||
}
|
||||
GDFUNC_FAIL_COND_MSG(err != OK, RTR("Cannot resize array."));
|
||||
|
||||
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);
|
||||
DEBUG_VALIDATE_ARG_TYPE(0, Variant::INT);
|
||||
DEBUG_VALIDATE_ARG_TYPE(1, Variant::INT);
|
||||
DEBUG_VALIDATE_ARG_TYPE(2, Variant::INT);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
VALIDATE_ARG_CUSTOM(2, Variant::INT, incr == 0, RTR("Step argument is zero!"));
|
||||
|
||||
Array arr;
|
||||
if (from >= to && incr > 0) {
|
||||
|
|
@ -200,12 +199,7 @@ struct GDScriptUtilityFunctionsDefinitions {
|
|||
}
|
||||
|
||||
Error err = arr.resize(count);
|
||||
|
||||
if (err != OK) {
|
||||
*r_ret = RTR("Cannot resize array.");
|
||||
r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
|
||||
return;
|
||||
}
|
||||
GDFUNC_FAIL_COND_MSG(err != OK, RTR("Cannot resize array."));
|
||||
|
||||
if (incr > 0) {
|
||||
int idx = 0;
|
||||
|
|
@ -221,138 +215,81 @@ struct GDScriptUtilityFunctionsDefinitions {
|
|||
|
||||
*r_ret = arr;
|
||||
} break;
|
||||
default: {
|
||||
r_error.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS;
|
||||
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]);
|
||||
}
|
||||
DEBUG_VALIDATE_ARG_COUNT(1, 1);
|
||||
DEBUG_VALIDATE_ARG_TYPE(0, Variant::STRING);
|
||||
*r_ret = ResourceLoader::load(*p_args[0]);
|
||||
}
|
||||
|
||||
#ifndef DISABLE_DEPRECATED
|
||||
|
||||
static inline void inst_to_dict(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
|
||||
VALIDATE_ARG_COUNT(1);
|
||||
DEBUG_VALIDATE_ARG_COUNT(1, 1);
|
||||
DEBUG_VALIDATE_ARG_TYPE(0, Variant::OBJECT);
|
||||
|
||||
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_error.expected = Variant::OBJECT;
|
||||
return;
|
||||
}
|
||||
|
||||
Object *obj = *p_args[0];
|
||||
if (!obj) {
|
||||
*r_ret = Variant();
|
||||
} else {
|
||||
Object *obj = *p_args[0];
|
||||
if (!obj) {
|
||||
*r_ret = Variant();
|
||||
return;
|
||||
}
|
||||
|
||||
} 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;
|
||||
}
|
||||
VALIDATE_ARG_CUSTOM(0, Variant::OBJECT,
|
||||
!obj->get_script_instance() || obj->get_script_instance()->get_language() != GDScriptLanguage::get_singleton(),
|
||||
RTR("Not a script with an instance."));
|
||||
|
||||
GDScript *p = base.ptr();
|
||||
String path = p->get_script_path();
|
||||
Vector<StringName> sname;
|
||||
GDScriptInstance *inst = static_cast<GDScriptInstance *>(obj->get_script_instance());
|
||||
|
||||
while (p->_owner) {
|
||||
sname.push_back(p->local_name);
|
||||
p = p->_owner;
|
||||
}
|
||||
sname.reverse();
|
||||
Ref<GDScript> base = inst->get_script();
|
||||
VALIDATE_ARG_CUSTOM(0, Variant::OBJECT, base.is_null(), RTR("Not based on a script."));
|
||||
|
||||
if (!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();
|
||||
GDScript *p = base.ptr();
|
||||
String path = p->get_script_path();
|
||||
Vector<StringName> sname;
|
||||
|
||||
*r_ret = RTR("Not based on a resource file");
|
||||
while (p->_owner) {
|
||||
sname.push_back(p->local_name);
|
||||
p = p->_owner;
|
||||
}
|
||||
sname.reverse();
|
||||
|
||||
return;
|
||||
}
|
||||
VALIDATE_ARG_CUSTOM(0, Variant::OBJECT, !path.is_resource_file(), RTR("Not based on a resource file."));
|
||||
|
||||
NodePath cp(sname, Vector<StringName>(), false);
|
||||
NodePath cp(sname, Vector<StringName>(), false);
|
||||
|
||||
Dictionary d;
|
||||
d["@subpath"] = cp;
|
||||
d["@path"] = path;
|
||||
Dictionary d;
|
||||
d["@subpath"] = cp;
|
||||
d["@path"] = path;
|
||||
|
||||
for (const KeyValue<StringName, GDScript::MemberInfo> &E : base->member_indices) {
|
||||
if (!d.has(E.key)) {
|
||||
d[E.key] = ins->members[E.value.index];
|
||||
}
|
||||
}
|
||||
*r_ret = d;
|
||||
for (const KeyValue<StringName, GDScript::MemberInfo> &E : base->member_indices) {
|
||||
if (!d.has(E.key)) {
|
||||
d[E.key] = inst->members[E.value.index];
|
||||
}
|
||||
}
|
||||
|
||||
*r_ret = d;
|
||||
}
|
||||
|
||||
static inline void dict_to_inst(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;
|
||||
}
|
||||
DEBUG_VALIDATE_ARG_COUNT(1, 1);
|
||||
DEBUG_VALIDATE_ARG_TYPE(0, Variant::DICTIONARY);
|
||||
|
||||
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;
|
||||
}
|
||||
VALIDATE_ARG_CUSTOM(0, Variant::DICTIONARY, !d.has("@path"), RTR("Invalid instance dictionary format (missing @path)."));
|
||||
|
||||
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;
|
||||
}
|
||||
VALIDATE_ARG_CUSTOM(0, Variant::DICTIONARY, scr.is_null(), RTR("Invalid instance dictionary format (can't load script at @path)."));
|
||||
|
||||
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;
|
||||
}
|
||||
VALIDATE_ARG_CUSTOM(0, Variant::DICTIONARY, gdscr.is_null(), RTR("Invalid instance dictionary format (invalid script at @path)."));
|
||||
|
||||
NodePath sub;
|
||||
if (d.has("@subpath")) {
|
||||
|
|
@ -361,60 +298,42 @@ struct GDScriptUtilityFunctionsDefinitions {
|
|||
|
||||
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;
|
||||
}
|
||||
VALIDATE_ARG_CUSTOM(0, Variant::DICTIONARY, gdscr.is_null(), RTR("Invalid instance dictionary (invalid subclasses)."));
|
||||
}
|
||||
*r_ret = gdscr->_new(nullptr, -1 /*skip initializer*/, r_error);
|
||||
|
||||
*r_ret = gdscr->_new(nullptr, -1 /* skip initializer */, r_error);
|
||||
if (r_error.error != Callable::CallError::CALL_OK) {
|
||||
*r_ret = RTR("Cannot instantiate GDScript class.");
|
||||
return;
|
||||
}
|
||||
|
||||
GDScriptInstance *ins = static_cast<GDScriptInstance *>(static_cast<Object *>(*r_ret)->get_script_instance());
|
||||
Ref<GDScript> gd_ref = ins->get_script();
|
||||
GDScriptInstance *inst = static_cast<GDScriptInstance *>(static_cast<Object *>(*r_ret)->get_script_instance());
|
||||
Ref<GDScript> gd_ref = inst->get_script();
|
||||
|
||||
for (KeyValue<StringName, GDScript::MemberInfo> &E : gd_ref->member_indices) {
|
||||
if (d.has(E.key)) {
|
||||
ins->members.write[E.value.index] = d[E.key];
|
||||
inst->members.write[E.value.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.expected = 3;
|
||||
*r_ret = Variant();
|
||||
return;
|
||||
}
|
||||
if (p_arg_count > 4) {
|
||||
r_error.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS;
|
||||
r_error.expected = 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);
|
||||
DEBUG_VALIDATE_ARG_COUNT(3, 4);
|
||||
DEBUG_VALIDATE_ARG_TYPE(0, Variant::INT);
|
||||
DEBUG_VALIDATE_ARG_TYPE(1, Variant::INT);
|
||||
DEBUG_VALIDATE_ARG_TYPE(2, Variant::INT);
|
||||
|
||||
int64_t alpha = 255;
|
||||
if (p_arg_count == 4) {
|
||||
VALIDATE_ARG_INT(3);
|
||||
color.a = (int64_t)*p_args[3] / 255.0f;
|
||||
DEBUG_VALIDATE_ARG_TYPE(3, Variant::INT);
|
||||
alpha = *p_args[3];
|
||||
}
|
||||
|
||||
*r_ret = color;
|
||||
*r_ret = Color::from_rgba8(*p_args[0], *p_args[1], *p_args[2], alpha);
|
||||
}
|
||||
|
||||
#endif // DISABLE_DEPRECATED
|
||||
|
||||
static inline void print_debug(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
|
||||
String s;
|
||||
for (int i = 0; i < p_arg_count; i++) {
|
||||
|
|
@ -435,7 +354,8 @@ struct GDScriptUtilityFunctionsDefinitions {
|
|||
}
|
||||
|
||||
static inline void print_stack(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
|
||||
VALIDATE_ARG_COUNT(0);
|
||||
DEBUG_VALIDATE_ARG_COUNT(0, 0);
|
||||
|
||||
if (Thread::get_caller_id() != Thread::get_main_id()) {
|
||||
print_line("Cannot retrieve debug info outside the main thread. Thread ID: " + itos(Thread::get_caller_id()));
|
||||
return;
|
||||
|
|
@ -449,7 +369,8 @@ struct GDScriptUtilityFunctionsDefinitions {
|
|||
}
|
||||
|
||||
static inline void get_stack(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
|
||||
VALIDATE_ARG_COUNT(0);
|
||||
DEBUG_VALIDATE_ARG_COUNT(0, 0);
|
||||
|
||||
if (Thread::get_caller_id() != Thread::get_main_id()) {
|
||||
*r_ret = TypedArray<Dictionary>();
|
||||
return;
|
||||
|
|
@ -468,7 +389,7 @@ struct GDScriptUtilityFunctionsDefinitions {
|
|||
}
|
||||
|
||||
static inline void len(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
|
||||
VALIDATE_ARG_COUNT(1);
|
||||
DEBUG_VALIDATE_ARG_COUNT(1, 1);
|
||||
switch (p_args[0]->get_type()) {
|
||||
case Variant::STRING:
|
||||
case Variant::STRING_NAME: {
|
||||
|
|
@ -524,56 +445,34 @@ struct GDScriptUtilityFunctionsDefinitions {
|
|||
*r_ret = d.size();
|
||||
} break;
|
||||
default: {
|
||||
*r_ret = vformat(RTR("Value of type '%s' can't provide a length."), Variant::get_type_name(p_args[0]->get_type()));
|
||||
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()));
|
||||
}
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void is_instance_of(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
|
||||
VALIDATE_ARG_COUNT(2);
|
||||
DEBUG_VALIDATE_ARG_COUNT(2, 2);
|
||||
|
||||
if (p_args[1]->get_type() == Variant::INT) {
|
||||
int builtin_type = *p_args[1];
|
||||
if (builtin_type < 0 || builtin_type >= Variant::VARIANT_MAX) {
|
||||
*r_ret = RTR("Invalid type argument for is_instance_of(), use TYPE_* constants for built-in types.");
|
||||
r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
|
||||
r_error.argument = 1;
|
||||
r_error.expected = Variant::NIL;
|
||||
return;
|
||||
}
|
||||
DEBUG_VALIDATE_ARG_CUSTOM(1, Variant::NIL, builtin_type < 0 || builtin_type >= Variant::VARIANT_MAX,
|
||||
RTR("Invalid type argument for is_instance_of(), use TYPE_* constants for built-in types."));
|
||||
*r_ret = p_args[0]->get_type() == builtin_type;
|
||||
return;
|
||||
}
|
||||
|
||||
bool was_type_freed = false;
|
||||
Object *type_object = p_args[1]->get_validated_object_with_check(was_type_freed);
|
||||
if (was_type_freed) {
|
||||
*r_ret = RTR("Type argument is a previously freed instance.");
|
||||
r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
|
||||
r_error.argument = 1;
|
||||
r_error.expected = Variant::NIL;
|
||||
return;
|
||||
}
|
||||
if (!type_object) {
|
||||
*r_ret = RTR("Invalid type argument for is_instance_of(), should be a TYPE_* constant, a class or a script.");
|
||||
r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
|
||||
r_error.argument = 1;
|
||||
r_error.expected = Variant::NIL;
|
||||
return;
|
||||
}
|
||||
VALIDATE_ARG_CUSTOM(1, Variant::NIL, was_type_freed, RTR("Type argument is a previously freed instance."));
|
||||
VALIDATE_ARG_CUSTOM(1, Variant::NIL, !type_object,
|
||||
RTR("Invalid type argument for is_instance_of(), should be a TYPE_* constant, a class or a script."));
|
||||
|
||||
bool was_value_freed = false;
|
||||
Object *value_object = p_args[0]->get_validated_object_with_check(was_value_freed);
|
||||
if (was_value_freed) {
|
||||
*r_ret = RTR("Value argument is a previously freed instance.");
|
||||
r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
|
||||
r_error.argument = 0;
|
||||
r_error.expected = Variant::NIL;
|
||||
return;
|
||||
}
|
||||
VALIDATE_ARG_CUSTOM(0, Variant::NIL, was_value_freed, RTR("Value argument is a previously freed instance."));
|
||||
if (!value_object) {
|
||||
*r_ret = false;
|
||||
return;
|
||||
|
|
@ -618,113 +517,79 @@ struct GDScriptUtilityFunctionInfo {
|
|||
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));
|
||||
static void _register_function(const StringName &p_name, const MethodInfo &p_method_info, GDScriptUtilityFunctions::FunctionPtr p_function, bool p_is_const) {
|
||||
ERR_FAIL_COND(utility_function_table.has(p_name));
|
||||
|
||||
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);
|
||||
utility_function_table.insert(p_name, function);
|
||||
utility_function_name_table.push_back(p_name);
|
||||
}
|
||||
|
||||
#define REGISTER_FUNC(m_func, m_is_const, m_return_type, ...) \
|
||||
#define REGISTER_FUNC(m_func, m_is_const, m_return, m_args, m_is_vararg, m_default_args) \
|
||||
{ \
|
||||
String name(#m_func); \
|
||||
if (name.begins_with("_")) { \
|
||||
name = name.substr(1, name.length() - 1); \
|
||||
name = name.substr(1); \
|
||||
} \
|
||||
MethodInfo info = m_args; \
|
||||
info.name = name; \
|
||||
info.return_val = m_return; \
|
||||
info.default_arguments = m_default_args; \
|
||||
if (m_is_vararg) { \
|
||||
info.flags |= METHOD_FLAG_VARARG; \
|
||||
} \
|
||||
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 RET(m_type) \
|
||||
PropertyInfo(Variant::m_type, "")
|
||||
|
||||
#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 RETVAR \
|
||||
PropertyInfo(Variant::NIL, "", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NIL_IS_VARIANT)
|
||||
|
||||
#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 RETCLS(m_class) \
|
||||
PropertyInfo(Variant::OBJECT, "", PROPERTY_HINT_RESOURCE_TYPE, m_class)
|
||||
|
||||
#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 NOARGS \
|
||||
MethodInfo()
|
||||
|
||||
#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 ARGS(...) \
|
||||
MethodInfo("", __VA_ARGS__)
|
||||
|
||||
#define ARG(m_name, m_type) \
|
||||
PropertyInfo(m_type, m_name)
|
||||
PropertyInfo(Variant::m_type, m_name)
|
||||
|
||||
#define VARARG(m_name) \
|
||||
PropertyInfo(Variant::NIL, m_name, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_NIL_IS_VARIANT)
|
||||
#define ARGVAR(m_name) \
|
||||
PropertyInfo(Variant::NIL, m_name, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NIL_IS_VARIANT)
|
||||
|
||||
#define ARGTYPE(m_name) \
|
||||
PropertyInfo(Variant::INT, m_name, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_CLASS_IS_ENUM, "Variant.Type")
|
||||
|
||||
void GDScriptUtilityFunctions::register_functions() {
|
||||
/* clang-format off */
|
||||
#ifndef DISABLE_DEPRECATED
|
||||
REGISTER_VARIANT_FUNC(convert, true, VARARG("what"), ARG("type", Variant::INT));
|
||||
REGISTER_FUNC( convert, true, RETVAR, ARGS( ARGVAR("what"), ARGTYPE("type") ), false, varray( ));
|
||||
#endif // DISABLE_DEPRECATED
|
||||
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(range, false, Variant::ARRAY);
|
||||
REGISTER_CLASS_FUNC(load, false, "Resource", ARG("path", Variant::STRING));
|
||||
REGISTER_FUNC(inst_to_dict, false, Variant::DICTIONARY, ARG("instance", Variant::OBJECT));
|
||||
REGISTER_FUNC(dict_to_inst, 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"));
|
||||
REGISTER_FUNC(is_instance_of, true, Variant::BOOL, VARARG("value"), VARARG("type"));
|
||||
REGISTER_FUNC( type_exists, true, RET(BOOL), ARGS( ARG("type", STRING_NAME) ), false, varray( ));
|
||||
REGISTER_FUNC( _char, true, RET(STRING), ARGS( ARG("char", INT) ), false, varray( ));
|
||||
REGISTER_FUNC( range, false, RET(ARRAY), NOARGS, true, varray( ));
|
||||
REGISTER_FUNC( load, false, RETCLS("Resource"), ARGS( ARG("path", STRING) ), false, varray( ));
|
||||
#ifndef DISABLE_DEPRECATED
|
||||
REGISTER_FUNC( inst_to_dict, false, RET(DICTIONARY), ARGS( ARG("instance", OBJECT) ), false, varray( ));
|
||||
REGISTER_FUNC( dict_to_inst, false, RET(OBJECT), ARGS( ARG("dictionary", DICTIONARY) ), false, varray( ));
|
||||
REGISTER_FUNC( Color8, true, RET(COLOR), ARGS( ARG("r8", INT), ARG("g8", INT),
|
||||
ARG("b8", INT), ARG("a8", INT) ), false, varray( 255 ));
|
||||
#endif // DISABLE_DEPRECATED
|
||||
REGISTER_FUNC( print_debug, false, RET(NIL), NOARGS, true, varray( ));
|
||||
REGISTER_FUNC( print_stack, false, RET(NIL), NOARGS, false, varray( ));
|
||||
REGISTER_FUNC( get_stack, false, RET(ARRAY), NOARGS, false, varray( ));
|
||||
REGISTER_FUNC( len, true, RET(INT), ARGS( ARGVAR("var") ), false, varray( ));
|
||||
REGISTER_FUNC( is_instance_of, true, RET(BOOL), ARGS( ARGVAR("value"), ARGVAR("type") ), false, varray( ));
|
||||
/* clang-format on */
|
||||
}
|
||||
|
||||
void GDScriptUtilityFunctions::unregister_functions() {
|
||||
|
|
|
|||
|
|
@ -84,9 +84,15 @@ static String _get_var_type(const Variant *p_var) {
|
|||
if (p_var->get_type() == Variant::ARRAY) {
|
||||
basestr = "Array";
|
||||
const Array *p_array = VariantInternal::get_array(p_var);
|
||||
Variant::Type builtin_type = (Variant::Type)p_array->get_typed_builtin();
|
||||
if (builtin_type != Variant::NIL) {
|
||||
basestr += "[" + _get_element_type(builtin_type, p_array->get_typed_class_name(), p_array->get_typed_script()) + "]";
|
||||
if (p_array->is_typed()) {
|
||||
basestr += "[" + _get_element_type((Variant::Type)p_array->get_typed_builtin(), p_array->get_typed_class_name(), p_array->get_typed_script()) + "]";
|
||||
}
|
||||
} else if (p_var->get_type() == Variant::DICTIONARY) {
|
||||
basestr = "Dictionary";
|
||||
const Dictionary *p_dictionary = VariantInternal::get_dictionary(p_var);
|
||||
if (p_dictionary->is_typed()) {
|
||||
basestr += "[" + _get_element_type((Variant::Type)p_dictionary->get_typed_key_builtin(), p_dictionary->get_typed_key_class_name(), p_dictionary->get_typed_key_script()) +
|
||||
", " + _get_element_type((Variant::Type)p_dictionary->get_typed_value_builtin(), p_dictionary->get_typed_value_class_name(), p_dictionary->get_typed_value_script()) + "]";
|
||||
}
|
||||
} else {
|
||||
basestr = Variant::get_type_name(p_var->get_type());
|
||||
|
|
@ -120,6 +126,16 @@ Variant GDScriptFunction::_get_default_variant_for_data_type(const GDScriptDataT
|
|||
}
|
||||
|
||||
return array;
|
||||
} else if (p_data_type.builtin_type == Variant::DICTIONARY) {
|
||||
Dictionary dict;
|
||||
// Typed dictionary.
|
||||
if (p_data_type.has_container_element_types()) {
|
||||
const GDScriptDataType &key_type = p_data_type.get_container_element_type_or_variant(0);
|
||||
const GDScriptDataType &value_type = p_data_type.get_container_element_type_or_variant(1);
|
||||
dict.set_typed(key_type.builtin_type, key_type.native_type, key_type.script_type, value_type.builtin_type, value_type.native_type, value_type.script_type);
|
||||
}
|
||||
|
||||
return dict;
|
||||
} else {
|
||||
Callable::CallError ce;
|
||||
Variant variant;
|
||||
|
|
@ -134,38 +150,39 @@ Variant GDScriptFunction::_get_default_variant_for_data_type(const GDScriptDataT
|
|||
return Variant();
|
||||
}
|
||||
|
||||
String GDScriptFunction::_get_call_error(const Callable::CallError &p_err, const String &p_where, const Variant **argptrs) const {
|
||||
String err_text;
|
||||
|
||||
if (p_err.error == Callable::CallError::CALL_ERROR_INVALID_ARGUMENT) {
|
||||
int errorarg = p_err.argument;
|
||||
ERR_FAIL_COND_V_MSG(errorarg < 0 || argptrs[errorarg] == nullptr, "GDScript bug (please report): Invalid CallError argument index or null pointer.", "Invalid CallError argument index or null pointer.");
|
||||
// Handle the Object to Object case separately as we don't have further class details.
|
||||
String GDScriptFunction::_get_call_error(const String &p_where, const Variant **p_argptrs, const Variant &p_ret, const Callable::CallError &p_err) const {
|
||||
switch (p_err.error) {
|
||||
case Callable::CallError::CALL_OK:
|
||||
return String();
|
||||
case Callable::CallError::CALL_ERROR_INVALID_METHOD:
|
||||
if (p_ret.get_type() == Variant::STRING && !p_ret.operator String().is_empty()) {
|
||||
return "Invalid call " + p_where + ": " + p_ret.operator String();
|
||||
}
|
||||
return "Invalid call. Nonexistent " + p_where + ".";
|
||||
case Callable::CallError::CALL_ERROR_INVALID_ARGUMENT:
|
||||
ERR_FAIL_COND_V_MSG(p_err.argument < 0 || p_argptrs[p_err.argument] == nullptr, "Bug: Invalid CallError argument index or null pointer.", "Bug: Invalid CallError argument index or null pointer.");
|
||||
// Handle the Object to Object case separately as we don't have further class details.
|
||||
#ifdef DEBUG_ENABLED
|
||||
if (p_err.expected == Variant::OBJECT && argptrs[errorarg]->get_type() == p_err.expected) {
|
||||
err_text = "Invalid type in " + p_where + ". The Object-derived class of argument " + itos(errorarg + 1) + " (" + _get_var_type(argptrs[errorarg]) + ") is not a subclass of the expected argument class.";
|
||||
} else if (p_err.expected == Variant::ARRAY && argptrs[errorarg]->get_type() == p_err.expected) {
|
||||
err_text = "Invalid type in " + p_where + ". The array of argument " + itos(errorarg + 1) + " (" + _get_var_type(argptrs[errorarg]) + ") does not have the same element type as the expected typed array argument.";
|
||||
} else
|
||||
if (p_err.expected == Variant::OBJECT && p_argptrs[p_err.argument]->get_type() == p_err.expected) {
|
||||
return "Invalid type in " + p_where + ". The Object-derived class of argument " + itos(p_err.argument + 1) + " (" + _get_var_type(p_argptrs[p_err.argument]) + ") is not a subclass of the expected argument class.";
|
||||
}
|
||||
if (p_err.expected == Variant::ARRAY && p_argptrs[p_err.argument]->get_type() == p_err.expected) {
|
||||
return "Invalid type in " + p_where + ". The array of argument " + itos(p_err.argument + 1) + " (" + _get_var_type(p_argptrs[p_err.argument]) + ") does not have the same element type as the expected typed array argument.";
|
||||
}
|
||||
if (p_err.expected == Variant::DICTIONARY && p_argptrs[p_err.argument]->get_type() == p_err.expected) {
|
||||
return "Invalid type in " + p_where + ". The dictionary of argument " + itos(p_err.argument + 1) + " (" + _get_var_type(p_argptrs[p_err.argument]) + ") does not have the same element type as the expected typed dictionary argument.";
|
||||
}
|
||||
#endif // DEBUG_ENABLED
|
||||
{
|
||||
err_text = "Invalid type in " + p_where + ". Cannot convert argument " + itos(errorarg + 1) + " from " + Variant::get_type_name(argptrs[errorarg]->get_type()) + " to " + Variant::get_type_name(Variant::Type(p_err.expected)) + ".";
|
||||
}
|
||||
} else if (p_err.error == Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS) {
|
||||
err_text = "Invalid call to " + p_where + ". Expected " + itos(p_err.expected) + " arguments.";
|
||||
} else if (p_err.error == Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS) {
|
||||
err_text = "Invalid call to " + p_where + ". Expected " + itos(p_err.expected) + " arguments.";
|
||||
} else if (p_err.error == Callable::CallError::CALL_ERROR_INVALID_METHOD) {
|
||||
err_text = "Invalid call. Nonexistent " + p_where + ".";
|
||||
} else if (p_err.error == Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL) {
|
||||
err_text = "Attempt to call " + p_where + " on a null instance.";
|
||||
} else if (p_err.error == Callable::CallError::CALL_ERROR_METHOD_NOT_CONST) {
|
||||
err_text = "Attempt to call " + p_where + " on a const instance.";
|
||||
} else {
|
||||
err_text = "Bug, call error: #" + itos(p_err.error);
|
||||
return "Invalid type in " + p_where + ". Cannot convert argument " + itos(p_err.argument + 1) + " from " + Variant::get_type_name(p_argptrs[p_err.argument]->get_type()) + " to " + Variant::get_type_name(Variant::Type(p_err.expected)) + ".";
|
||||
case Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS:
|
||||
case Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS:
|
||||
return "Invalid call to " + p_where + ". Expected " + itos(p_err.expected) + " arguments.";
|
||||
case Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL:
|
||||
return "Attempt to call " + p_where + " on a null instance.";
|
||||
case Callable::CallError::CALL_ERROR_METHOD_NOT_CONST:
|
||||
return "Attempt to call " + p_where + " on a const instance.";
|
||||
}
|
||||
|
||||
return err_text;
|
||||
return "Bug: Invalid call error code " + itos(p_err.error) + ".";
|
||||
}
|
||||
|
||||
void (*type_init_function_table[])(Variant *) = {
|
||||
|
|
@ -210,13 +227,14 @@ void (*type_init_function_table[])(Variant *) = {
|
|||
&VariantInitializer<PackedVector4Array>::init, // PACKED_VECTOR4_ARRAY.
|
||||
};
|
||||
|
||||
#if defined(__GNUC__)
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
#define OPCODES_TABLE \
|
||||
static const void *switch_table_ops[] = { \
|
||||
&&OPCODE_OPERATOR, \
|
||||
&&OPCODE_OPERATOR_VALIDATED, \
|
||||
&&OPCODE_TYPE_TEST_BUILTIN, \
|
||||
&&OPCODE_TYPE_TEST_ARRAY, \
|
||||
&&OPCODE_TYPE_TEST_DICTIONARY, \
|
||||
&&OPCODE_TYPE_TEST_NATIVE, \
|
||||
&&OPCODE_TYPE_TEST_SCRIPT, \
|
||||
&&OPCODE_SET_KEYED, \
|
||||
|
|
@ -239,6 +257,7 @@ void (*type_init_function_table[])(Variant *) = {
|
|||
&&OPCODE_ASSIGN_FALSE, \
|
||||
&&OPCODE_ASSIGN_TYPED_BUILTIN, \
|
||||
&&OPCODE_ASSIGN_TYPED_ARRAY, \
|
||||
&&OPCODE_ASSIGN_TYPED_DICTIONARY, \
|
||||
&&OPCODE_ASSIGN_TYPED_NATIVE, \
|
||||
&&OPCODE_ASSIGN_TYPED_SCRIPT, \
|
||||
&&OPCODE_CAST_TO_BUILTIN, \
|
||||
|
|
@ -249,6 +268,7 @@ void (*type_init_function_table[])(Variant *) = {
|
|||
&&OPCODE_CONSTRUCT_ARRAY, \
|
||||
&&OPCODE_CONSTRUCT_TYPED_ARRAY, \
|
||||
&&OPCODE_CONSTRUCT_DICTIONARY, \
|
||||
&&OPCODE_CONSTRUCT_TYPED_DICTIONARY, \
|
||||
&&OPCODE_CALL, \
|
||||
&&OPCODE_CALL_RETURN, \
|
||||
&&OPCODE_CALL_ASYNC, \
|
||||
|
|
@ -277,6 +297,7 @@ void (*type_init_function_table[])(Variant *) = {
|
|||
&&OPCODE_RETURN, \
|
||||
&&OPCODE_RETURN_TYPED_BUILTIN, \
|
||||
&&OPCODE_RETURN_TYPED_ARRAY, \
|
||||
&&OPCODE_RETURN_TYPED_DICTIONARY, \
|
||||
&&OPCODE_RETURN_TYPED_NATIVE, \
|
||||
&&OPCODE_RETURN_TYPED_SCRIPT, \
|
||||
&&OPCODE_ITERATE_BEGIN, \
|
||||
|
|
@ -376,32 +397,36 @@ void (*type_init_function_table[])(Variant *) = {
|
|||
#define OPCODES_OUT \
|
||||
OPSOUT:
|
||||
#define OPCODE_SWITCH(m_test) goto *switch_table_ops[m_test];
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
#define DISPATCH_OPCODE \
|
||||
last_opcode = _code_ptr[ip]; \
|
||||
goto *switch_table_ops[last_opcode]
|
||||
#else
|
||||
#else // !DEBUG_ENABLED
|
||||
#define DISPATCH_OPCODE goto *switch_table_ops[_code_ptr[ip]]
|
||||
#endif
|
||||
#endif // DEBUG_ENABLED
|
||||
|
||||
#define OPCODE_BREAK goto OPSEXIT
|
||||
#define OPCODE_OUT goto OPSOUT
|
||||
#else
|
||||
#else // !(defined(__GNUC__) || defined(__clang__))
|
||||
#define OPCODES_TABLE
|
||||
#define OPCODE(m_op) case m_op:
|
||||
#define OPCODE_WHILE(m_test) while (m_test)
|
||||
#define OPCODES_END
|
||||
#define OPCODES_OUT
|
||||
#define DISPATCH_OPCODE continue
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define OPCODE_SWITCH(m_test) \
|
||||
__assume(m_test <= OPCODE_END); \
|
||||
switch (m_test)
|
||||
#else
|
||||
#else // !_MSC_VER
|
||||
#define OPCODE_SWITCH(m_test) switch (m_test)
|
||||
#endif
|
||||
#endif // _MSC_VER
|
||||
|
||||
#define OPCODE_BREAK break
|
||||
#define OPCODE_OUT break
|
||||
#endif
|
||||
#endif // defined(__GNUC__) || defined(__clang__)
|
||||
|
||||
// Helpers for VariantInternal methods in macros.
|
||||
#define OP_GET_BOOL get_bool
|
||||
|
|
@ -486,12 +511,6 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
|
|||
Variant **instruction_args = nullptr;
|
||||
int defarg = 0;
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
|
||||
//GDScriptLanguage::get_singleton()->calls++;
|
||||
|
||||
#endif
|
||||
|
||||
uint32_t alloca_size = 0;
|
||||
GDScript *script;
|
||||
int ip = 0;
|
||||
|
|
@ -550,7 +569,12 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
|
|||
return _get_default_variant_for_data_type(return_type);
|
||||
}
|
||||
if (argument_types[i].kind == GDScriptDataType::BUILTIN) {
|
||||
if (argument_types[i].builtin_type == Variant::ARRAY && argument_types[i].has_container_element_type(0)) {
|
||||
if (argument_types[i].builtin_type == Variant::DICTIONARY && argument_types[i].has_container_element_types()) {
|
||||
const GDScriptDataType &arg_key_type = argument_types[i].get_container_element_type_or_variant(0);
|
||||
const GDScriptDataType &arg_value_type = argument_types[i].get_container_element_type_or_variant(1);
|
||||
Dictionary dict(p_args[i]->operator Dictionary(), arg_key_type.builtin_type, arg_key_type.native_type, arg_key_type.script_type, arg_value_type.builtin_type, arg_value_type.native_type, arg_value_type.script_type);
|
||||
memnew_placement(&stack[i + 3], Variant(dict));
|
||||
} else if (argument_types[i].builtin_type == Variant::ARRAY && argument_types[i].has_container_element_type(0)) {
|
||||
const GDScriptDataType &arg_type = argument_types[i].container_element_types[0];
|
||||
Array array(p_args[i]->operator Array(), arg_type.builtin_type, arg_type.native_type, arg_type.script_type);
|
||||
memnew_placement(&stack[i + 3], Variant(array));
|
||||
|
|
@ -637,7 +661,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
|
|||
OPCODE_BREAK; \
|
||||
}
|
||||
|
||||
#else
|
||||
#else // !DEBUG_ENABLED
|
||||
#define GD_ERR_BREAK(m_cond)
|
||||
#define CHECK_SPACE(m_space)
|
||||
|
||||
|
|
@ -650,7 +674,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
|
|||
OPCODE_BREAK; \
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif // DEBUG_ENABLED
|
||||
|
||||
#define LOAD_INSTRUCTION_ARGS \
|
||||
int instr_arg_count = _code_ptr[ip + 1]; \
|
||||
|
|
@ -829,6 +853,36 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
|
|||
}
|
||||
DISPATCH_OPCODE;
|
||||
|
||||
OPCODE(OPCODE_TYPE_TEST_DICTIONARY) {
|
||||
CHECK_SPACE(9);
|
||||
|
||||
GET_VARIANT_PTR(dst, 0);
|
||||
GET_VARIANT_PTR(value, 1);
|
||||
|
||||
GET_VARIANT_PTR(key_script_type, 2);
|
||||
Variant::Type key_builtin_type = (Variant::Type)_code_ptr[ip + 5];
|
||||
int key_native_type_idx = _code_ptr[ip + 6];
|
||||
GD_ERR_BREAK(key_native_type_idx < 0 || key_native_type_idx >= _global_names_count);
|
||||
const StringName key_native_type = _global_names_ptr[key_native_type_idx];
|
||||
|
||||
GET_VARIANT_PTR(value_script_type, 3);
|
||||
Variant::Type value_builtin_type = (Variant::Type)_code_ptr[ip + 7];
|
||||
int value_native_type_idx = _code_ptr[ip + 8];
|
||||
GD_ERR_BREAK(value_native_type_idx < 0 || value_native_type_idx >= _global_names_count);
|
||||
const StringName value_native_type = _global_names_ptr[value_native_type_idx];
|
||||
|
||||
bool result = false;
|
||||
if (value->get_type() == Variant::DICTIONARY) {
|
||||
Dictionary *dictionary = VariantInternal::get_dictionary(value);
|
||||
result = dictionary->get_typed_key_builtin() == ((uint32_t)key_builtin_type) && dictionary->get_typed_key_class_name() == key_native_type && dictionary->get_typed_key_script() == *key_script_type &&
|
||||
dictionary->get_typed_value_builtin() == ((uint32_t)value_builtin_type) && dictionary->get_typed_value_class_name() == value_native_type && dictionary->get_typed_value_script() == *value_script_type;
|
||||
}
|
||||
|
||||
*dst = result;
|
||||
ip += 9;
|
||||
}
|
||||
DISPATCH_OPCODE;
|
||||
|
||||
OPCODE(OPCODE_TYPE_TEST_NATIVE) {
|
||||
CHECK_SPACE(4);
|
||||
|
||||
|
|
@ -1386,6 +1440,50 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
|
|||
}
|
||||
DISPATCH_OPCODE;
|
||||
|
||||
OPCODE(OPCODE_ASSIGN_TYPED_DICTIONARY) {
|
||||
CHECK_SPACE(9);
|
||||
GET_VARIANT_PTR(dst, 0);
|
||||
GET_VARIANT_PTR(src, 1);
|
||||
|
||||
GET_VARIANT_PTR(key_script_type, 2);
|
||||
Variant::Type key_builtin_type = (Variant::Type)_code_ptr[ip + 5];
|
||||
int key_native_type_idx = _code_ptr[ip + 6];
|
||||
GD_ERR_BREAK(key_native_type_idx < 0 || key_native_type_idx >= _global_names_count);
|
||||
const StringName key_native_type = _global_names_ptr[key_native_type_idx];
|
||||
|
||||
GET_VARIANT_PTR(value_script_type, 3);
|
||||
Variant::Type value_builtin_type = (Variant::Type)_code_ptr[ip + 7];
|
||||
int value_native_type_idx = _code_ptr[ip + 8];
|
||||
GD_ERR_BREAK(value_native_type_idx < 0 || value_native_type_idx >= _global_names_count);
|
||||
const StringName value_native_type = _global_names_ptr[value_native_type_idx];
|
||||
|
||||
if (src->get_type() != Variant::DICTIONARY) {
|
||||
#ifdef DEBUG_ENABLED
|
||||
err_text = vformat(R"(Trying to assign a value of type "%s" to a variable of type "Dictionary[%s, %s]".)",
|
||||
_get_var_type(src), _get_element_type(key_builtin_type, key_native_type, *key_script_type),
|
||||
_get_element_type(value_builtin_type, value_native_type, *value_script_type));
|
||||
#endif // DEBUG_ENABLED
|
||||
OPCODE_BREAK;
|
||||
}
|
||||
|
||||
Dictionary *dictionary = VariantInternal::get_dictionary(src);
|
||||
|
||||
if (dictionary->get_typed_key_builtin() != ((uint32_t)key_builtin_type) || dictionary->get_typed_key_class_name() != key_native_type || dictionary->get_typed_key_script() != *key_script_type ||
|
||||
dictionary->get_typed_value_builtin() != ((uint32_t)value_builtin_type) || dictionary->get_typed_value_class_name() != value_native_type || dictionary->get_typed_value_script() != *value_script_type) {
|
||||
#ifdef DEBUG_ENABLED
|
||||
err_text = vformat(R"(Trying to assign a dictionary of type "%s" to a variable of type "Dictionary[%s, %s]".)",
|
||||
_get_var_type(src), _get_element_type(key_builtin_type, key_native_type, *key_script_type),
|
||||
_get_element_type(value_builtin_type, value_native_type, *value_script_type));
|
||||
#endif // DEBUG_ENABLED
|
||||
OPCODE_BREAK;
|
||||
}
|
||||
|
||||
*dst = *src;
|
||||
|
||||
ip += 9;
|
||||
}
|
||||
DISPATCH_OPCODE;
|
||||
|
||||
OPCODE(OPCODE_ASSIGN_TYPED_NATIVE) {
|
||||
CHECK_SPACE(4);
|
||||
GET_VARIANT_PTR(dst, 0);
|
||||
|
|
@ -1608,7 +1706,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
|
|||
|
||||
#ifdef DEBUG_ENABLED
|
||||
if (err.error != Callable::CallError::CALL_OK) {
|
||||
err_text = _get_call_error(err, "'" + Variant::get_type_name(t) + "' constructor", (const Variant **)argptrs);
|
||||
err_text = _get_call_error("'" + Variant::get_type_name(t) + "' constructor", (const Variant **)argptrs, *dst, err);
|
||||
OPCODE_BREAK;
|
||||
}
|
||||
#endif
|
||||
|
|
@ -1705,12 +1803,51 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
|
|||
|
||||
GET_INSTRUCTION_ARG(dst, argc * 2);
|
||||
|
||||
*dst = Variant(); // Clear potential previous typed dictionary.
|
||||
|
||||
*dst = dict;
|
||||
|
||||
ip += 2;
|
||||
}
|
||||
DISPATCH_OPCODE;
|
||||
|
||||
OPCODE(OPCODE_CONSTRUCT_TYPED_DICTIONARY) {
|
||||
LOAD_INSTRUCTION_ARGS
|
||||
CHECK_SPACE(6 + instr_arg_count);
|
||||
ip += instr_arg_count;
|
||||
|
||||
int argc = _code_ptr[ip + 1];
|
||||
|
||||
GET_INSTRUCTION_ARG(key_script_type, argc * 2 + 1);
|
||||
Variant::Type key_builtin_type = (Variant::Type)_code_ptr[ip + 2];
|
||||
int key_native_type_idx = _code_ptr[ip + 3];
|
||||
GD_ERR_BREAK(key_native_type_idx < 0 || key_native_type_idx >= _global_names_count);
|
||||
const StringName key_native_type = _global_names_ptr[key_native_type_idx];
|
||||
|
||||
GET_INSTRUCTION_ARG(value_script_type, argc * 2 + 2);
|
||||
Variant::Type value_builtin_type = (Variant::Type)_code_ptr[ip + 4];
|
||||
int value_native_type_idx = _code_ptr[ip + 5];
|
||||
GD_ERR_BREAK(value_native_type_idx < 0 || value_native_type_idx >= _global_names_count);
|
||||
const StringName value_native_type = _global_names_ptr[value_native_type_idx];
|
||||
|
||||
Dictionary dict;
|
||||
|
||||
for (int i = 0; i < argc; i++) {
|
||||
GET_INSTRUCTION_ARG(k, i * 2 + 0);
|
||||
GET_INSTRUCTION_ARG(v, i * 2 + 1);
|
||||
dict[*k] = *v;
|
||||
}
|
||||
|
||||
GET_INSTRUCTION_ARG(dst, argc * 2);
|
||||
|
||||
*dst = Variant(); // Clear potential previous typed dictionary.
|
||||
|
||||
*dst = Dictionary(dict, key_builtin_type, key_native_type, *key_script_type, value_builtin_type, value_native_type, *value_script_type);
|
||||
|
||||
ip += 6;
|
||||
}
|
||||
DISPATCH_OPCODE;
|
||||
|
||||
OPCODE(OPCODE_CALL_ASYNC)
|
||||
OPCODE(OPCODE_CALL_RETURN)
|
||||
OPCODE(OPCODE_CALL) {
|
||||
|
|
@ -1744,10 +1881,12 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
|
|||
StringName base_class = base_obj ? base_obj->get_class_name() : StringName();
|
||||
#endif
|
||||
|
||||
Variant temp_ret;
|
||||
Callable::CallError err;
|
||||
if (call_ret) {
|
||||
GET_INSTRUCTION_ARG(ret, argc + 1);
|
||||
base->callp(*methodname, (const Variant **)argptrs, argc, *ret, err);
|
||||
base->callp(*methodname, (const Variant **)argptrs, argc, temp_ret, err);
|
||||
*ret = temp_ret;
|
||||
#ifdef DEBUG_ENABLED
|
||||
if (ret->get_type() == Variant::NIL) {
|
||||
if (base_type == Variant::OBJECT) {
|
||||
|
|
@ -1776,8 +1915,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
|
|||
}
|
||||
#endif
|
||||
} else {
|
||||
Variant ret;
|
||||
base->callp(*methodname, (const Variant **)argptrs, argc, ret, err);
|
||||
base->callp(*methodname, (const Variant **)argptrs, argc, temp_ret, err);
|
||||
}
|
||||
#ifdef DEBUG_ENABLED
|
||||
|
||||
|
|
@ -1822,10 +1960,10 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
|
|||
}
|
||||
}
|
||||
}
|
||||
err_text = _get_call_error(err, "function '" + methodstr + (is_callable ? "" : "' in base '" + basestr) + "'", (const Variant **)argptrs);
|
||||
err_text = _get_call_error("function '" + methodstr + (is_callable ? "" : "' in base '" + basestr) + "'", (const Variant **)argptrs, temp_ret, err);
|
||||
OPCODE_BREAK;
|
||||
}
|
||||
#endif
|
||||
#endif // DEBUG_ENABLED
|
||||
|
||||
ip += 3;
|
||||
}
|
||||
|
|
@ -1868,12 +2006,14 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
|
|||
}
|
||||
#endif
|
||||
|
||||
Variant temp_ret;
|
||||
Callable::CallError err;
|
||||
if (call_ret) {
|
||||
GET_INSTRUCTION_ARG(ret, argc + 1);
|
||||
*ret = method->call(base_obj, (const Variant **)argptrs, argc, err);
|
||||
temp_ret = method->call(base_obj, (const Variant **)argptrs, argc, err);
|
||||
*ret = temp_ret;
|
||||
} else {
|
||||
method->call(base_obj, (const Variant **)argptrs, argc, err);
|
||||
temp_ret = method->call(base_obj, (const Variant **)argptrs, argc, err);
|
||||
}
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
|
|
@ -1906,7 +2046,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
|
|||
}
|
||||
}
|
||||
}
|
||||
err_text = _get_call_error(err, "function '" + methodstr + "' in base '" + basestr + "'", (const Variant **)argptrs);
|
||||
err_text = _get_call_error("function '" + methodstr + "' in base '" + basestr + "'", (const Variant **)argptrs, temp_ret, err);
|
||||
OPCODE_BREAK;
|
||||
}
|
||||
#endif
|
||||
|
|
@ -1939,7 +2079,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
|
|||
|
||||
#ifdef DEBUG_ENABLED
|
||||
if (err.error != Callable::CallError::CALL_OK) {
|
||||
err_text = _get_call_error(err, "static function '" + methodname->operator String() + "' in type '" + Variant::get_type_name(builtin_type) + "'", argptrs);
|
||||
err_text = _get_call_error("static function '" + methodname->operator String() + "' in type '" + Variant::get_type_name(builtin_type) + "'", argptrs, *ret, err);
|
||||
OPCODE_BREAK;
|
||||
}
|
||||
#endif
|
||||
|
|
@ -1983,7 +2123,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
|
|||
#endif
|
||||
|
||||
if (err.error != Callable::CallError::CALL_OK) {
|
||||
err_text = _get_call_error(err, "static function '" + method->get_name().operator String() + "' in type '" + method->get_instance_class().operator String() + "'", argptrs);
|
||||
err_text = _get_call_error("static function '" + method->get_name().operator String() + "' in type '" + method->get_instance_class().operator String() + "'", argptrs, *ret, err);
|
||||
OPCODE_BREAK;
|
||||
}
|
||||
|
||||
|
|
@ -2214,7 +2354,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
|
|||
// Call provided error string.
|
||||
err_text = vformat(R"*(Error calling utility function "%s()": %s)*", methodstr, *dst);
|
||||
} else {
|
||||
err_text = _get_call_error(err, vformat(R"*(utility function "%s()")*", methodstr), (const Variant **)argptrs);
|
||||
err_text = _get_call_error(vformat(R"*(utility function "%s()")*", methodstr), (const Variant **)argptrs, *dst, err);
|
||||
}
|
||||
OPCODE_BREAK;
|
||||
}
|
||||
|
|
@ -2271,7 +2411,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
|
|||
// Call provided error string.
|
||||
err_text = vformat(R"*(Error calling GDScript utility function "%s()": %s)*", methodstr, *dst);
|
||||
} else {
|
||||
err_text = _get_call_error(err, vformat(R"*(GDScript utility function "%s()")*", methodstr), (const Variant **)argptrs);
|
||||
err_text = _get_call_error(vformat(R"*(GDScript utility function "%s()")*", methodstr), (const Variant **)argptrs, *dst, err);
|
||||
}
|
||||
OPCODE_BREAK;
|
||||
}
|
||||
|
|
@ -2338,7 +2478,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
|
|||
|
||||
if (err.error != Callable::CallError::CALL_OK) {
|
||||
String methodstr = *methodname;
|
||||
err_text = _get_call_error(err, "function '" + methodstr + "'", (const Variant **)argptrs);
|
||||
err_text = _get_call_error("function '" + methodstr + "'", (const Variant **)argptrs, *dst, err);
|
||||
|
||||
OPCODE_BREAK;
|
||||
}
|
||||
|
|
@ -2650,6 +2790,51 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
|
|||
|
||||
retvalue = *array;
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
exit_ok = true;
|
||||
#endif // DEBUG_ENABLED
|
||||
OPCODE_BREAK;
|
||||
}
|
||||
|
||||
OPCODE(OPCODE_RETURN_TYPED_DICTIONARY) {
|
||||
CHECK_SPACE(8);
|
||||
GET_VARIANT_PTR(r, 0);
|
||||
|
||||
GET_VARIANT_PTR(key_script_type, 1);
|
||||
Variant::Type key_builtin_type = (Variant::Type)_code_ptr[ip + 4];
|
||||
int key_native_type_idx = _code_ptr[ip + 5];
|
||||
GD_ERR_BREAK(key_native_type_idx < 0 || key_native_type_idx >= _global_names_count);
|
||||
const StringName key_native_type = _global_names_ptr[key_native_type_idx];
|
||||
|
||||
GET_VARIANT_PTR(value_script_type, 2);
|
||||
Variant::Type value_builtin_type = (Variant::Type)_code_ptr[ip + 6];
|
||||
int value_native_type_idx = _code_ptr[ip + 7];
|
||||
GD_ERR_BREAK(value_native_type_idx < 0 || value_native_type_idx >= _global_names_count);
|
||||
const StringName value_native_type = _global_names_ptr[value_native_type_idx];
|
||||
|
||||
if (r->get_type() != Variant::DICTIONARY) {
|
||||
#ifdef DEBUG_ENABLED
|
||||
err_text = vformat(R"(Trying to return a value of type "%s" where expected return type is "Dictionary[%s, %s]".)",
|
||||
_get_var_type(r), _get_element_type(key_builtin_type, key_native_type, *key_script_type),
|
||||
_get_element_type(value_builtin_type, value_native_type, *value_script_type));
|
||||
#endif // DEBUG_ENABLED
|
||||
OPCODE_BREAK;
|
||||
}
|
||||
|
||||
Dictionary *dictionary = VariantInternal::get_dictionary(r);
|
||||
|
||||
if (dictionary->get_typed_key_builtin() != ((uint32_t)key_builtin_type) || dictionary->get_typed_key_class_name() != key_native_type || dictionary->get_typed_key_script() != *key_script_type ||
|
||||
dictionary->get_typed_value_builtin() != ((uint32_t)value_builtin_type) || dictionary->get_typed_value_class_name() != value_native_type || dictionary->get_typed_value_script() != *value_script_type) {
|
||||
#ifdef DEBUG_ENABLED
|
||||
err_text = vformat(R"(Trying to return a dictionary of type "%s" where expected return type is "Dictionary[%s, %s]".)",
|
||||
_get_var_type(r), _get_element_type(key_builtin_type, key_native_type, *key_script_type),
|
||||
_get_element_type(value_builtin_type, value_native_type, *value_script_type));
|
||||
#endif // DEBUG_ENABLED
|
||||
OPCODE_BREAK;
|
||||
}
|
||||
|
||||
retvalue = *dictionary;
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
exit_ok = true;
|
||||
#endif // DEBUG_ENABLED
|
||||
|
|
|
|||
|
|
@ -61,10 +61,13 @@ String GDScriptWarning::get_message() const {
|
|||
return vformat(R"(The signal "%s" is declared but never explicitly used in the class.)", symbols[0]);
|
||||
case SHADOWED_VARIABLE:
|
||||
CHECK_SYMBOLS(4);
|
||||
return vformat(R"(The local %s "%s" is shadowing an already-declared %s at line %s.)", symbols[0], symbols[1], symbols[2], symbols[3]);
|
||||
return vformat(R"(The local %s "%s" is shadowing an already-declared %s at line %s in the current class.)", symbols[0], symbols[1], symbols[2], symbols[3]);
|
||||
case SHADOWED_VARIABLE_BASE_CLASS:
|
||||
CHECK_SYMBOLS(4);
|
||||
return vformat(R"(The local %s "%s" is shadowing an already-declared %s at the base class "%s".)", symbols[0], symbols[1], symbols[2], symbols[3]);
|
||||
if (symbols.size() > 4) {
|
||||
return vformat(R"(The local %s "%s" is shadowing an already-declared %s at line %s in the base class "%s".)", symbols[0], symbols[1], symbols[2], symbols[3], symbols[4]);
|
||||
}
|
||||
return vformat(R"(The local %s "%s" is shadowing an already-declared %s in the base class "%s".)", symbols[0], symbols[1], symbols[2], symbols[3]);
|
||||
case SHADOWED_GLOBAL_IDENTIFIER:
|
||||
CHECK_SYMBOLS(3);
|
||||
return vformat(R"(The %s "%s" has the same name as a %s.)", symbols[0], symbols[1], symbols[2]);
|
||||
|
|
@ -109,6 +112,8 @@ String GDScriptWarning::get_message() const {
|
|||
case STATIC_CALLED_ON_INSTANCE:
|
||||
CHECK_SYMBOLS(2);
|
||||
return vformat(R"*(The function "%s()" is a static function but was called from an instance. Instead, it should be directly called from the type: "%s.%s()".)*", symbols[0], symbols[1], symbols[0]);
|
||||
case MISSING_TOOL:
|
||||
return R"(The base class script has the "@tool" annotation, but this script does not have it.)";
|
||||
case REDUNDANT_STATIC_UNLOAD:
|
||||
return R"(The "@static_unload" annotation is redundant because the file does not have a class with static variables.)";
|
||||
case REDUNDANT_AWAIT:
|
||||
|
|
@ -134,8 +139,6 @@ String GDScriptWarning::get_message() const {
|
|||
case DEPRECATED_KEYWORD:
|
||||
CHECK_SYMBOLS(2);
|
||||
return vformat(R"(The "%s" keyword is deprecated and will be removed in a future release, please replace its uses by "%s".)", symbols[0], symbols[1]);
|
||||
case RENAMED_IN_GODOT_4_HINT:
|
||||
break; // Renamed identifier hint is taken care of by the GDScriptAnalyzer. No message needed here.
|
||||
case CONFUSABLE_IDENTIFIER:
|
||||
CHECK_SYMBOLS(1);
|
||||
return vformat(R"(The identifier "%s" has misleading characters and might be confused with something else.)", symbols[0]);
|
||||
|
|
@ -180,10 +183,6 @@ int GDScriptWarning::get_default_value(Code p_code) {
|
|||
}
|
||||
|
||||
PropertyInfo GDScriptWarning::get_property_info(Code p_code) {
|
||||
// Making this a separate function in case a warning needs different PropertyInfo in the future.
|
||||
if (p_code == Code::RENAMED_IN_GODOT_4_HINT) {
|
||||
return PropertyInfo(Variant::BOOL, get_settings_path_from_code(p_code));
|
||||
}
|
||||
return PropertyInfo(Variant::INT, get_settings_path_from_code(p_code), PROPERTY_HINT_ENUM, "Ignore,Warn,Error");
|
||||
}
|
||||
|
||||
|
|
@ -219,6 +218,7 @@ String GDScriptWarning::get_name_from_code(Code p_code) {
|
|||
"UNSAFE_VOID_RETURN",
|
||||
"RETURN_VALUE_DISCARDED",
|
||||
"STATIC_CALLED_ON_INSTANCE",
|
||||
"MISSING_TOOL",
|
||||
"REDUNDANT_STATIC_UNLOAD",
|
||||
"REDUNDANT_AWAIT",
|
||||
"ASSERT_ALWAYS_TRUE",
|
||||
|
|
@ -230,7 +230,6 @@ String GDScriptWarning::get_name_from_code(Code p_code) {
|
|||
"ENUM_VARIABLE_WITHOUT_DEFAULT",
|
||||
"EMPTY_FILE",
|
||||
"DEPRECATED_KEYWORD",
|
||||
"RENAMED_IN_GODOT_4_HINT",
|
||||
"CONFUSABLE_IDENTIFIER",
|
||||
"CONFUSABLE_LOCAL_DECLARATION",
|
||||
"CONFUSABLE_LOCAL_USAGE",
|
||||
|
|
|
|||
|
|
@ -53,8 +53,8 @@ public:
|
|||
UNUSED_PRIVATE_CLASS_VARIABLE, // Class variable is declared private ("_" prefix) but never used in the class.
|
||||
UNUSED_PARAMETER, // Function parameter is never used.
|
||||
UNUSED_SIGNAL, // Signal is defined but never explicitly used in the class.
|
||||
SHADOWED_VARIABLE, // Variable name shadowed by other variable in same class.
|
||||
SHADOWED_VARIABLE_BASE_CLASS, // Variable name shadowed by other variable in some base class.
|
||||
SHADOWED_VARIABLE, // A local variable/constant shadows a current class member.
|
||||
SHADOWED_VARIABLE_BASE_CLASS, // A local variable/constant shadows a base class member.
|
||||
SHADOWED_GLOBAL_IDENTIFIER, // A global class or function has the same name as variable.
|
||||
UNREACHABLE_CODE, // Code after a return statement.
|
||||
UNREACHABLE_PATTERN, // Pattern in a match statement after a catch all pattern (wildcard or bind).
|
||||
|
|
@ -70,6 +70,7 @@ public:
|
|||
UNSAFE_VOID_RETURN, // Function returns void but returned a call to a function that can't be type checked.
|
||||
RETURN_VALUE_DISCARDED, // Function call returns something but the value isn't used.
|
||||
STATIC_CALLED_ON_INSTANCE, // A static method was called on an instance of a class instead of on the class itself.
|
||||
MISSING_TOOL, // The base class script has the "@tool" annotation, but this script does not have it.
|
||||
REDUNDANT_STATIC_UNLOAD, // The `@static_unload` annotation is used but the class does not have static data.
|
||||
REDUNDANT_AWAIT, // await is used but expression is synchronous (not a signal nor a coroutine).
|
||||
ASSERT_ALWAYS_TRUE, // Expression for assert argument is always true.
|
||||
|
|
@ -81,7 +82,6 @@ public:
|
|||
ENUM_VARIABLE_WITHOUT_DEFAULT, // A variable with an enum type does not have a default value. The default will be set to `0` instead of the first enum value.
|
||||
EMPTY_FILE, // A script file is empty.
|
||||
DEPRECATED_KEYWORD, // The keyword is deprecated and should be replaced.
|
||||
RENAMED_IN_GODOT_4_HINT, // A variable or function that could not be found has been renamed in Godot 4.
|
||||
CONFUSABLE_IDENTIFIER, // The identifier contains misleading characters that can be confused. E.g. "usеr" (has Cyrillic "е" instead of Latin "e").
|
||||
CONFUSABLE_LOCAL_DECLARATION, // The parent block declares an identifier with the same name below.
|
||||
CONFUSABLE_LOCAL_USAGE, // The identifier will be shadowed below in the block.
|
||||
|
|
@ -98,6 +98,10 @@ public:
|
|||
WARNING_MAX,
|
||||
};
|
||||
|
||||
#ifndef DISABLE_DEPRECATED
|
||||
static constexpr int FIRST_DEPRECATED_WARNING = PROPERTY_USED_AS_FUNCTION;
|
||||
#endif
|
||||
|
||||
constexpr static WarnLevel default_warning_levels[] = {
|
||||
WARN, // UNASSIGNED_VARIABLE
|
||||
WARN, // UNASSIGNED_VARIABLE_OP_ASSIGN
|
||||
|
|
@ -123,6 +127,7 @@ public:
|
|||
WARN, // UNSAFE_VOID_RETURN
|
||||
IGNORE, // RETURN_VALUE_DISCARDED // Too spammy by default on common cases (connect, Tween, etc.).
|
||||
WARN, // STATIC_CALLED_ON_INSTANCE
|
||||
WARN, // MISSING_TOOL
|
||||
WARN, // REDUNDANT_STATIC_UNLOAD
|
||||
WARN, // REDUNDANT_AWAIT
|
||||
WARN, // ASSERT_ALWAYS_TRUE
|
||||
|
|
@ -134,7 +139,6 @@ public:
|
|||
WARN, // ENUM_VARIABLE_WITHOUT_DEFAULT
|
||||
WARN, // EMPTY_FILE
|
||||
WARN, // DEPRECATED_KEYWORD
|
||||
WARN, // RENAMED_IN_GODOT_4_HINT
|
||||
WARN, // CONFUSABLE_IDENTIFIER
|
||||
WARN, // CONFUSABLE_LOCAL_DECLARATION
|
||||
WARN, // CONFUSABLE_LOCAL_USAGE
|
||||
|
|
|
|||
|
|
@ -57,11 +57,17 @@ lsp::Position GodotPosition::to_lsp(const Vector<String> &p_lines) const {
|
|||
return res;
|
||||
}
|
||||
res.line = line - 1;
|
||||
|
||||
// Special case: `column = 0` -> Starts at beginning of line.
|
||||
if (column <= 0) {
|
||||
return res;
|
||||
}
|
||||
|
||||
// Note: character outside of `pos_line.length()-1` is valid.
|
||||
res.character = column - 1;
|
||||
|
||||
String pos_line = p_lines[res.line];
|
||||
if (pos_line.contains("\t")) {
|
||||
if (pos_line.contains_char('\t')) {
|
||||
int tab_size = get_indent_size();
|
||||
|
||||
int in_col = 1;
|
||||
|
|
@ -238,9 +244,12 @@ void ExtendGDScriptParser::parse_class_symbol(const GDScriptParser::ClassNode *p
|
|||
r_symbol.kind = lsp::SymbolKind::Class;
|
||||
r_symbol.deprecated = false;
|
||||
r_symbol.range = range_of_node(p_class);
|
||||
r_symbol.range.start.line = MAX(r_symbol.range.start.line, 0);
|
||||
if (p_class->identifier) {
|
||||
r_symbol.selectionRange = range_of_node(p_class->identifier);
|
||||
} else {
|
||||
// No meaningful `selectionRange`, but we must ensure that it is inside of `range`.
|
||||
r_symbol.selectionRange.start = r_symbol.range.start;
|
||||
r_symbol.selectionRange.end = r_symbol.range.start;
|
||||
}
|
||||
r_symbol.detail = "class " + r_symbol.name;
|
||||
{
|
||||
|
|
|
|||
|
|
@ -37,18 +37,18 @@
|
|||
#include "core/variant/variant.h"
|
||||
|
||||
#ifndef LINE_NUMBER_TO_INDEX
|
||||
#define LINE_NUMBER_TO_INDEX(p_line) ((p_line)-1)
|
||||
#define LINE_NUMBER_TO_INDEX(p_line) ((p_line) - 1)
|
||||
#endif
|
||||
#ifndef COLUMN_NUMBER_TO_INDEX
|
||||
#define COLUMN_NUMBER_TO_INDEX(p_column) ((p_column)-1)
|
||||
#define COLUMN_NUMBER_TO_INDEX(p_column) ((p_column) - 1)
|
||||
#endif
|
||||
|
||||
#ifndef SYMBOL_SEPERATOR
|
||||
#define SYMBOL_SEPERATOR "::"
|
||||
#ifndef SYMBOL_SEPARATOR
|
||||
#define SYMBOL_SEPARATOR "::"
|
||||
#endif
|
||||
|
||||
#ifndef JOIN_SYMBOLS
|
||||
#define JOIN_SYMBOLS(p_path, name) ((p_path) + SYMBOL_SEPERATOR + (name))
|
||||
#define JOIN_SYMBOLS(p_path, name) ((p_path) + SYMBOL_SEPARATOR + (name))
|
||||
#endif
|
||||
|
||||
typedef HashMap<String, const lsp::DocumentSymbol *> ClassMembers;
|
||||
|
|
|
|||
|
|
@ -196,7 +196,7 @@ Dictionary GDScriptLanguageProtocol::initialize(const Dictionary &p_params) {
|
|||
ERR_FAIL_COND_V_MSG(!clients.has(latest_client_id), ret.to_json(),
|
||||
vformat("GDScriptLanguageProtocol: Can't initialize invalid peer '%d'.", latest_client_id));
|
||||
Ref<LSPeer> peer = clients.get(latest_client_id);
|
||||
if (peer != nullptr) {
|
||||
if (peer.is_valid()) {
|
||||
String msg = Variant(request).to_json_string();
|
||||
msg = format_output(msg);
|
||||
(*peer)->res_queue.push_back(msg.utf8());
|
||||
|
|
@ -298,7 +298,7 @@ void GDScriptLanguageProtocol::notify_client(const String &p_method, const Varia
|
|||
}
|
||||
ERR_FAIL_COND(!clients.has(p_client_id));
|
||||
Ref<LSPeer> peer = clients.get(p_client_id);
|
||||
ERR_FAIL_NULL(peer);
|
||||
ERR_FAIL_COND(peer.is_null());
|
||||
|
||||
Dictionary message = make_notification(p_method, p_params);
|
||||
String msg = Variant(message).to_json_string();
|
||||
|
|
@ -319,7 +319,7 @@ void GDScriptLanguageProtocol::request_client(const String &p_method, const Vari
|
|||
}
|
||||
ERR_FAIL_COND(!clients.has(p_client_id));
|
||||
Ref<LSPeer> peer = clients.get(p_client_id);
|
||||
ERR_FAIL_NULL(peer);
|
||||
ERR_FAIL_COND(peer.is_null());
|
||||
|
||||
Dictionary message = make_request(p_method, p_params, next_server_id);
|
||||
next_server_id++;
|
||||
|
|
|
|||
|
|
@ -33,9 +33,7 @@
|
|||
|
||||
#include "gdscript_text_document.h"
|
||||
#include "gdscript_workspace.h"
|
||||
#include "godot_lsp.h"
|
||||
|
||||
#include "core/io/stream_peer.h"
|
||||
#include "core/io/stream_peer_tcp.h"
|
||||
#include "core/io/tcp_server.h"
|
||||
|
||||
|
|
|
|||
|
|
@ -30,7 +30,6 @@
|
|||
|
||||
#include "gdscript_language_server.h"
|
||||
|
||||
#include "core/io/file_access.h"
|
||||
#include "core/os/os.h"
|
||||
#include "editor/editor_log.h"
|
||||
#include "editor/editor_node.h"
|
||||
|
|
@ -39,6 +38,7 @@
|
|||
int GDScriptLanguageServer::port_override = -1;
|
||||
|
||||
GDScriptLanguageServer::GDScriptLanguageServer() {
|
||||
// TODO: Move to editor_settings.cpp
|
||||
_EDITOR_DEF("network/language_server/remote_host", host);
|
||||
_EDITOR_DEF("network/language_server/remote_port", port);
|
||||
_EDITOR_DEF("network/language_server/enable_smart_resolve", true);
|
||||
|
|
|
|||
|
|
@ -31,7 +31,6 @@
|
|||
#ifndef GDSCRIPT_LANGUAGE_SERVER_H
|
||||
#define GDSCRIPT_LANGUAGE_SERVER_H
|
||||
|
||||
#include "../gdscript_parser.h"
|
||||
#include "gdscript_language_protocol.h"
|
||||
|
||||
#include "editor/plugins/editor_plugin.h"
|
||||
|
|
|
|||
|
|
@ -34,7 +34,6 @@
|
|||
#include "gdscript_extend_parser.h"
|
||||
#include "gdscript_language_protocol.h"
|
||||
|
||||
#include "core/os/os.h"
|
||||
#include "editor/editor_settings.h"
|
||||
#include "editor/plugins/script_text_editor.h"
|
||||
#include "servers/display_server.h"
|
||||
|
|
@ -112,12 +111,21 @@ void GDScriptTextDocument::didSave(const Variant &p_param) {
|
|||
}
|
||||
|
||||
scr->update_exports();
|
||||
ScriptEditor::get_singleton()->reload_scripts(true);
|
||||
ScriptEditor::get_singleton()->update_docs_from_script(scr);
|
||||
ScriptEditor::get_singleton()->trigger_live_script_reload(scr->get_path());
|
||||
|
||||
if (!Thread::is_main_thread()) {
|
||||
callable_mp(this, &GDScriptTextDocument::reload_script).call_deferred(scr);
|
||||
} else {
|
||||
reload_script(scr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GDScriptTextDocument::reload_script(Ref<GDScript> p_to_reload_script) {
|
||||
ScriptEditor::get_singleton()->reload_scripts(true);
|
||||
ScriptEditor::get_singleton()->update_docs_from_script(p_to_reload_script);
|
||||
ScriptEditor::get_singleton()->trigger_live_script_reload(p_to_reload_script->get_path());
|
||||
}
|
||||
|
||||
lsp::TextDocumentItem GDScriptTextDocument::load_document_item(const Variant &p_param) {
|
||||
lsp::TextDocumentItem doc;
|
||||
Dictionary params = p_param;
|
||||
|
|
@ -229,19 +237,6 @@ Array GDScriptTextDocument::completion(const Dictionary &p_params) {
|
|||
arr[i] = item.to_json();
|
||||
i++;
|
||||
}
|
||||
} else if (GDScriptLanguageProtocol::get_singleton()->is_smart_resolve_enabled()) {
|
||||
arr = native_member_completions.duplicate();
|
||||
|
||||
for (KeyValue<String, ExtendGDScriptParser *> &E : GDScriptLanguageProtocol::get_singleton()->get_workspace()->scripts) {
|
||||
ExtendGDScriptParser *scr = E.value;
|
||||
const Array &items = scr->get_member_completions();
|
||||
|
||||
const int start_size = arr.size();
|
||||
arr.resize(start_size + items.size());
|
||||
for (int i = start_size; i < arr.size(); i++) {
|
||||
arr[i] = items[i - start_size];
|
||||
}
|
||||
}
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
|
@ -309,10 +304,10 @@ Dictionary GDScriptTextDocument::resolve(const Dictionary &p_params) {
|
|||
params.load(p_params["data"]);
|
||||
symbol = GDScriptLanguageProtocol::get_singleton()->get_workspace()->resolve_symbol(params, item.label, item.kind == lsp::CompletionItemKind::Method || item.kind == lsp::CompletionItemKind::Function);
|
||||
|
||||
} else if (data.get_type() == Variant::STRING) {
|
||||
} else if (data.is_string()) {
|
||||
String query = data;
|
||||
|
||||
Vector<String> param_symbols = query.split(SYMBOL_SEPERATOR, false);
|
||||
Vector<String> param_symbols = query.split(SYMBOL_SEPARATOR, false);
|
||||
|
||||
if (param_symbols.size() >= 2) {
|
||||
StringName class_name = param_symbols[0];
|
||||
|
|
@ -485,8 +480,6 @@ GDScriptTextDocument::GDScriptTextDocument() {
|
|||
void GDScriptTextDocument::sync_script_content(const String &p_path, const String &p_content) {
|
||||
String path = GDScriptLanguageProtocol::get_singleton()->get_workspace()->get_file_path(p_path);
|
||||
GDScriptLanguageProtocol::get_singleton()->get_workspace()->parse_script(path, p_content);
|
||||
|
||||
EditorFileSystem::get_singleton()->update_file(path);
|
||||
}
|
||||
|
||||
void GDScriptTextDocument::show_native_symbol_in_editor(const String &p_symbol_id) {
|
||||
|
|
|
|||
|
|
@ -36,6 +36,8 @@
|
|||
#include "core/io/file_access.h"
|
||||
#include "core/object/ref_counted.h"
|
||||
|
||||
class GDScript;
|
||||
|
||||
class GDScriptTextDocument : public RefCounted {
|
||||
GDCLASS(GDScriptTextDocument, RefCounted)
|
||||
protected:
|
||||
|
|
@ -49,6 +51,7 @@ protected:
|
|||
void willSaveWaitUntil(const Variant &p_param);
|
||||
void didSave(const Variant &p_param);
|
||||
|
||||
void reload_script(Ref<GDScript> p_to_reload_script);
|
||||
void sync_script_content(const String &p_path, const String &p_content);
|
||||
void show_native_symbol_in_editor(const String &p_symbol_id);
|
||||
|
||||
|
|
|
|||
|
|
@ -657,7 +657,7 @@ void GDScriptWorkspace::completion(const lsp::CompletionParams &p_params, List<S
|
|||
}
|
||||
|
||||
Ref<GDScript> scr = current->get_script();
|
||||
if (!scr.is_valid() || !GDScript::is_canonically_equal_paths(scr->get_path(), path)) {
|
||||
if (scr.is_null() || !GDScript::is_canonically_equal_paths(scr->get_path(), path)) {
|
||||
current = owner_scene_node;
|
||||
}
|
||||
}
|
||||
|
|
@ -699,12 +699,12 @@ const lsp::DocumentSymbol *GDScriptWorkspace::resolve_symbol(const lsp::TextDocu
|
|||
symbol_identifier = "_init";
|
||||
}
|
||||
if (OK == GDScriptLanguage::get_singleton()->lookup_code(parser->get_text_for_lookup_symbol(pos, symbol_identifier, p_func_required), symbol_identifier, path, nullptr, ret)) {
|
||||
if (ret.type == ScriptLanguage::LOOKUP_RESULT_SCRIPT_LOCATION) {
|
||||
if (ret.location >= 0) {
|
||||
String target_script_path = path;
|
||||
if (!ret.script.is_null()) {
|
||||
if (ret.script.is_valid()) {
|
||||
target_script_path = ret.script->get_path();
|
||||
} else if (!ret.class_path.is_empty()) {
|
||||
target_script_path = ret.class_path;
|
||||
} else if (!ret.script_path.is_empty()) {
|
||||
target_script_path = ret.script_path;
|
||||
}
|
||||
|
||||
if (const ExtendGDScriptParser *target_parser = get_parse_result(target_script_path)) {
|
||||
|
|
@ -720,7 +720,6 @@ const lsp::DocumentSymbol *GDScriptWorkspace::resolve_symbol(const lsp::TextDocu
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
String member = ret.class_member;
|
||||
if (member.is_empty() && symbol_identifier != ret.class_name) {
|
||||
|
|
|
|||
|
|
@ -958,28 +958,30 @@ struct CompletionItem {
|
|||
|
||||
/**
|
||||
* A string that should be used when comparing this item
|
||||
* with other items. When `falsy` the label is used.
|
||||
* with other items. When omitted the label is used
|
||||
* as the filter text for this item.
|
||||
*/
|
||||
String sortText;
|
||||
|
||||
/**
|
||||
* A string that should be used when filtering a set of
|
||||
* completion items. When `falsy` the label is used.
|
||||
* completion items. When omitted the label is used as the
|
||||
* filter text for this item.
|
||||
*/
|
||||
String filterText;
|
||||
|
||||
/**
|
||||
* A string that should be inserted into a document when selecting
|
||||
* this completion. When `falsy` the label is used.
|
||||
* this completion. When omitted the label is used as the insert text
|
||||
* for this item.
|
||||
*
|
||||
* The `insertText` is subject to interpretation by the client side.
|
||||
* Some tools might not take the string literally. For example
|
||||
* VS Code when code complete is requested in this example `con<cursor position>`
|
||||
* and a completion item with an `insertText` of `console` is provided it
|
||||
* will only insert `sole`. Therefore it is recommended to use `textEdit` instead
|
||||
* since it avoids additional client side interpretation.
|
||||
*
|
||||
* @deprecated Use textEdit instead.
|
||||
* VS Code when code complete is requested in this example
|
||||
* `con<cursor position>` and a completion item with an `insertText` of
|
||||
* `console` is provided it will only insert `sole`. Therefore it is
|
||||
* recommended to use `textEdit` instead since it avoids additional client
|
||||
* side interpretation.
|
||||
*/
|
||||
String insertText;
|
||||
|
||||
|
|
@ -1034,14 +1036,20 @@ struct CompletionItem {
|
|||
dict["label"] = label;
|
||||
dict["kind"] = kind;
|
||||
dict["data"] = data;
|
||||
dict["insertText"] = insertText;
|
||||
if (!insertText.is_empty()) {
|
||||
dict["insertText"] = insertText;
|
||||
}
|
||||
if (resolved) {
|
||||
dict["detail"] = detail;
|
||||
dict["documentation"] = documentation.to_json();
|
||||
dict["deprecated"] = deprecated;
|
||||
dict["preselect"] = preselect;
|
||||
dict["sortText"] = sortText;
|
||||
dict["filterText"] = filterText;
|
||||
if (!sortText.is_empty()) {
|
||||
dict["sortText"] = sortText;
|
||||
}
|
||||
if (!filterText.is_empty()) {
|
||||
dict["filterText"] = filterText;
|
||||
}
|
||||
if (commitCharacters.size()) {
|
||||
dict["commitCharacters"] = commitCharacters;
|
||||
}
|
||||
|
|
@ -1064,7 +1072,7 @@ struct CompletionItem {
|
|||
}
|
||||
if (p_dict.has("documentation")) {
|
||||
Variant doc = p_dict["documentation"];
|
||||
if (doc.get_type() == Variant::STRING) {
|
||||
if (doc.is_string()) {
|
||||
documentation.value = doc;
|
||||
} else if (doc.get_type() == Variant::DICTIONARY) {
|
||||
Dictionary v = doc;
|
||||
|
|
|
|||
|
|
@ -31,9 +31,8 @@
|
|||
#include "register_types.h"
|
||||
|
||||
#include "gdscript.h"
|
||||
#include "gdscript_analyzer.h"
|
||||
#include "gdscript_cache.h"
|
||||
#include "gdscript_tokenizer.h"
|
||||
#include "gdscript_parser.h"
|
||||
#include "gdscript_tokenizer_buffer.h"
|
||||
#include "gdscript_utility_functions.h"
|
||||
|
||||
|
|
@ -50,14 +49,11 @@
|
|||
#include "tests/test_gdscript.h"
|
||||
#endif
|
||||
|
||||
#include "core/io/dir_access.h"
|
||||
#include "core/io/file_access.h"
|
||||
#include "core/io/file_access_encrypted.h"
|
||||
#include "core/io/resource_loader.h"
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
#include "editor/editor_node.h"
|
||||
#include "editor/editor_settings.h"
|
||||
#include "editor/editor_translation_parser.h"
|
||||
#include "editor/export/editor_export.h"
|
||||
|
||||
|
|
@ -165,6 +161,13 @@ void initialize_gdscript_module(ModuleInitializationLevel p_level) {
|
|||
|
||||
gdscript_translation_parser_plugin.instantiate();
|
||||
EditorTranslationParser::get_singleton()->add_parser(gdscript_translation_parser_plugin, EditorTranslationParser::STANDARD);
|
||||
} else if (p_level == MODULE_INITIALIZATION_LEVEL_EDITOR) {
|
||||
ClassDB::APIType prev_api = ClassDB::get_current_api();
|
||||
ClassDB::set_current_api(ClassDB::API_EDITOR);
|
||||
|
||||
GDREGISTER_CLASS(GDScriptSyntaxHighlighter);
|
||||
|
||||
ClassDB::set_current_api(prev_api);
|
||||
}
|
||||
#endif // TOOLS_ENABLED
|
||||
}
|
||||
|
|
|
|||
|
|
@ -78,7 +78,7 @@ void init_autoloads() {
|
|||
scn.instantiate();
|
||||
scn->set_path(info.path);
|
||||
scn->reload_from_file();
|
||||
ERR_CONTINUE_MSG(!scn.is_valid(), vformat("Failed to instantiate an autoload, can't load from path: %s.", info.path));
|
||||
ERR_CONTINUE_MSG(scn.is_null(), vformat("Failed to instantiate an autoload, can't load from path: %s.", info.path));
|
||||
|
||||
if (scn.is_valid()) {
|
||||
n = scn->instantiate();
|
||||
|
|
@ -176,7 +176,7 @@ static String strip_warnings(const String &p_expected) {
|
|||
// so it doesn't fail just because of difference in warnings.
|
||||
String expected_no_warnings;
|
||||
for (String line : p_expected.split("\n")) {
|
||||
if (line.begins_with(">> ")) {
|
||||
if (line.begins_with("~~ ")) {
|
||||
continue;
|
||||
}
|
||||
expected_no_warnings += line + "\n";
|
||||
|
|
@ -275,6 +275,7 @@ bool GDScriptTestRunner::make_tests_for_dir(const String &p_dir) {
|
|||
return false;
|
||||
}
|
||||
} else {
|
||||
// `*.notest.gd` files are skipped.
|
||||
if (next.ends_with(".notest.gd")) {
|
||||
next = dir->get_next();
|
||||
continue;
|
||||
|
|
@ -367,7 +368,9 @@ static bool generate_class_index_recursive(const String &p_dir) {
|
|||
}
|
||||
String base_type;
|
||||
String source_file = current_dir.path_join(next);
|
||||
String class_name = GDScriptLanguage::get_singleton()->get_global_class_name(source_file, &base_type);
|
||||
bool is_abstract = false;
|
||||
bool is_tool = false;
|
||||
String class_name = GDScriptLanguage::get_singleton()->get_global_class_name(source_file, &base_type, nullptr, &is_abstract, &is_tool);
|
||||
if (class_name.is_empty()) {
|
||||
next = dir->get_next();
|
||||
continue;
|
||||
|
|
@ -375,7 +378,7 @@ static bool generate_class_index_recursive(const String &p_dir) {
|
|||
ERR_FAIL_COND_V_MSG(ScriptServer::is_global_class(class_name), false,
|
||||
"Class name '" + class_name + "' from " + source_file + " is already used in " + ScriptServer::get_global_class_path(class_name));
|
||||
|
||||
ScriptServer::add_global_class(class_name, base_type, gdscript_name, source_file);
|
||||
ScriptServer::add_global_class(class_name, base_type, gdscript_name, source_file, is_abstract, is_tool);
|
||||
}
|
||||
|
||||
next = dir->get_next();
|
||||
|
|
@ -450,47 +453,43 @@ void GDScriptTest::error_handler(void *p_this, const char *p_function, const cha
|
|||
|
||||
result->status = GDTEST_RUNTIME_ERROR;
|
||||
|
||||
StringBuilder builder;
|
||||
builder.append(">> ");
|
||||
// Only include the function, file and line for script errors, otherwise the
|
||||
// test outputs changes based on the platform/compiler.
|
||||
// Only include the file, line, and function for script errors,
|
||||
// otherwise the test outputs changes based on the platform/compiler.
|
||||
String header;
|
||||
bool include_source_info = false;
|
||||
switch (p_type) {
|
||||
case ERR_HANDLER_ERROR:
|
||||
builder.append("ERROR");
|
||||
header = "ERROR";
|
||||
break;
|
||||
case ERR_HANDLER_WARNING:
|
||||
builder.append("WARNING");
|
||||
header = "WARNING";
|
||||
break;
|
||||
case ERR_HANDLER_SCRIPT:
|
||||
builder.append("SCRIPT ERROR");
|
||||
header = "SCRIPT ERROR";
|
||||
include_source_info = true;
|
||||
break;
|
||||
case ERR_HANDLER_SHADER:
|
||||
builder.append("SHADER ERROR");
|
||||
header = "SHADER ERROR";
|
||||
break;
|
||||
default:
|
||||
builder.append("Unknown error type");
|
||||
header = "UNKNOWN ERROR";
|
||||
break;
|
||||
}
|
||||
|
||||
if (include_source_info) {
|
||||
builder.append("\n>> on function: ");
|
||||
builder.append(String::utf8(p_function));
|
||||
builder.append("()\n>> ");
|
||||
builder.append(String::utf8(p_file).trim_prefix(self->base_dir).replace("\\", "/"));
|
||||
builder.append("\n>> ");
|
||||
builder.append(itos(p_line));
|
||||
header += vformat(" at %s:%d on %s()",
|
||||
String::utf8(p_file).trim_prefix(self->base_dir).replace("\\", "/"),
|
||||
p_line,
|
||||
String::utf8(p_function));
|
||||
}
|
||||
builder.append("\n>> ");
|
||||
builder.append(String::utf8(p_error));
|
||||
if (strlen(p_explanation) > 0) {
|
||||
builder.append("\n>> ");
|
||||
builder.append(String::utf8(p_explanation));
|
||||
}
|
||||
builder.append("\n");
|
||||
|
||||
result->output = builder.as_string();
|
||||
StringBuilder error_string;
|
||||
error_string.append(vformat(">> %s: %s\n", header, String::utf8(p_error)));
|
||||
if (strlen(p_explanation) > 0) {
|
||||
error_string.append(vformat(">> %s\n", String::utf8(p_explanation)));
|
||||
}
|
||||
|
||||
result->output += error_string.as_string();
|
||||
}
|
||||
|
||||
bool GDScriptTest::check_output(const String &p_output) const {
|
||||
|
|
@ -588,11 +587,11 @@ GDScriptTest::TestResult GDScriptTest::execute_test_code(bool p_is_generating) {
|
|||
result.status = GDTEST_ANALYZER_ERROR;
|
||||
result.output = get_text_for_status(result.status) + "\n";
|
||||
|
||||
const List<GDScriptParser::ParserError> &errors = parser.get_errors();
|
||||
if (!errors.is_empty()) {
|
||||
// Only the first error since the following might be cascading.
|
||||
result.output += errors.front()->get().message + "\n"; // TODO: line, column?
|
||||
StringBuilder error_string;
|
||||
for (const GDScriptParser::ParserError &error : parser.get_errors()) {
|
||||
error_string.append(vformat(">> ERROR at line %d: %s\n", error.line, error.message));
|
||||
}
|
||||
result.output += error_string.as_string();
|
||||
if (!p_is_generating) {
|
||||
result.passed = check_output(result.output);
|
||||
}
|
||||
|
|
@ -601,16 +600,8 @@ GDScriptTest::TestResult GDScriptTest::execute_test_code(bool p_is_generating) {
|
|||
|
||||
#ifdef DEBUG_ENABLED
|
||||
StringBuilder warning_string;
|
||||
for (const GDScriptWarning &E : parser.get_warnings()) {
|
||||
const GDScriptWarning warning = E;
|
||||
warning_string.append(">> WARNING");
|
||||
warning_string.append("\n>> Line: ");
|
||||
warning_string.append(itos(warning.start_line));
|
||||
warning_string.append("\n>> ");
|
||||
warning_string.append(warning.get_name());
|
||||
warning_string.append("\n>> ");
|
||||
warning_string.append(warning.get_message());
|
||||
warning_string.append("\n");
|
||||
for (const GDScriptWarning &warning : parser.get_warnings()) {
|
||||
warning_string.append(vformat("~~ WARNING at line %d: (%s) %s\n", warning.start_line, warning.get_name(), warning.get_message()));
|
||||
}
|
||||
result.output += warning_string.as_string();
|
||||
#endif
|
||||
|
|
@ -628,12 +619,18 @@ GDScriptTest::TestResult GDScriptTest::execute_test_code(bool p_is_generating) {
|
|||
}
|
||||
return result;
|
||||
}
|
||||
// Script files matching this pattern are allowed to not contain a test() function.
|
||||
if (source_file.match("*.notest.gd")) {
|
||||
|
||||
// `*.norun.gd` files are allowed to not contain a `test()` function (no runtime testing).
|
||||
if (source_file.ends_with(".norun.gd")) {
|
||||
enable_stdout();
|
||||
result.passed = check_output(result.output);
|
||||
result.status = GDTEST_OK;
|
||||
result.output = get_text_for_status(result.status) + "\n" + result.output;
|
||||
if (!p_is_generating) {
|
||||
result.passed = check_output(result.output);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Test running.
|
||||
const HashMap<StringName, GDScriptFunction *>::ConstIterator test_function_element = script->get_member_functions().find(GDScriptTestRunner::test_function_name);
|
||||
if (!test_function_element) {
|
||||
|
|
|
|||
11
engine/modules/gdscript/tests/scripts/.editorconfig
Normal file
11
engine/modules/gdscript/tests/scripts/.editorconfig
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
# Some tests handle invalid syntax deliberately; exclude relevant attributes.
|
||||
# See also the `file-format` section in `.pre-commit-config.yaml`.
|
||||
|
||||
[parser/features/mixed_indentation_on_blank_lines.gd]
|
||||
trim_trailing_whitespace = false
|
||||
|
||||
[parser/warnings/empty_file_newline.norun.gd]
|
||||
insert_final_newline = false
|
||||
|
||||
[parser/warnings/empty_file_newline_comment.norun.gd]
|
||||
insert_final_newline = false
|
||||
|
|
@ -1,2 +1,10 @@
|
|||
class A extends InstancePlaceholder:
|
||||
func _init():
|
||||
print('no')
|
||||
|
||||
class B extends A:
|
||||
pass
|
||||
|
||||
func test():
|
||||
InstancePlaceholder.new()
|
||||
B.new()
|
||||
|
|
|
|||
|
|
@ -1,2 +1,5 @@
|
|||
GDTEST_ANALYZER_ERROR
|
||||
Native class "InstancePlaceholder" cannot be constructed as it is abstract.
|
||||
>> ERROR at line 9: Native class "InstancePlaceholder" cannot be constructed as it is abstract.
|
||||
>> ERROR at line 9: Name "new" is a Callable. You can call it with "new.call()" instead.
|
||||
>> ERROR at line 10: Class "abstract_class_instantiate.gd::B" cannot be constructed as it is based on abstract native class "InstancePlaceholder".
|
||||
>> ERROR at line 10: Name "new" is a Callable. You can call it with "new.call()" instead.
|
||||
|
|
|
|||
|
|
@ -1,9 +0,0 @@
|
|||
class A extends InstancePlaceholder:
|
||||
func _init():
|
||||
print('no')
|
||||
|
||||
class B extends A:
|
||||
pass
|
||||
|
||||
func test():
|
||||
B.new()
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
GDTEST_ANALYZER_ERROR
|
||||
Class "abstract_script_instantiate.gd::B" cannot be constructed as it is based on abstract native class "InstancePlaceholder".
|
||||
|
|
@ -1,2 +1,2 @@
|
|||
GDTEST_ANALYZER_ERROR
|
||||
Argument 1 of annotation "@export_range" isn't a constant expression.
|
||||
>> ERROR at line 3: Argument 1 of annotation "@export_range" isn't a constant expression.
|
||||
|
|
|
|||
|
|
@ -1,2 +1,2 @@
|
|||
GDTEST_ANALYZER_ERROR
|
||||
Cannot assign a new value to a constant.
|
||||
>> ERROR at line 3: Cannot assign a new value to a constant.
|
||||
|
|
|
|||
|
|
@ -1,2 +1,2 @@
|
|||
GDTEST_ANALYZER_ERROR
|
||||
Cannot assign a new value to a constant.
|
||||
>> ERROR at line 3: Cannot assign a new value to a constant.
|
||||
|
|
|
|||
|
|
@ -1,2 +1,2 @@
|
|||
GDTEST_ANALYZER_ERROR
|
||||
Cannot assign a new value to a constant.
|
||||
>> ERROR at line 4: Cannot assign a new value to a constant.
|
||||
|
|
|
|||
|
|
@ -1,2 +1,2 @@
|
|||
GDTEST_ANALYZER_ERROR
|
||||
Cannot assign a new value to a read-only property.
|
||||
>> ERROR at line 3: Cannot assign a new value to a read-only property.
|
||||
|
|
|
|||
|
|
@ -1,2 +1,2 @@
|
|||
GDTEST_ANALYZER_ERROR
|
||||
Cannot assign a new value to a read-only property.
|
||||
>> ERROR at line 3: Cannot assign a new value to a read-only property.
|
||||
|
|
|
|||
|
|
@ -1,2 +1,3 @@
|
|||
GDTEST_ANALYZER_ERROR
|
||||
Cannot assign a value of type "Color" as "String".
|
||||
>> ERROR at line 2: Cannot assign a value of type "Color" as "String".
|
||||
>> ERROR at line 2: Cannot assign a value of type Color to variable "var_color" with specified type String.
|
||||
|
|
|
|||
|
|
@ -1,2 +1,2 @@
|
|||
GDTEST_ANALYZER_ERROR
|
||||
Cannot infer the type of "_a" variable because the value doesn't have a set type.
|
||||
>> ERROR at line 4: Cannot infer the type of "_a" variable because the value doesn't have a set type.
|
||||
|
|
|
|||
|
|
@ -1,2 +1,2 @@
|
|||
GDTEST_ANALYZER_ERROR
|
||||
Invalid operands to operator <<, float and int.
|
||||
>> ERROR at line 3: Invalid operands to operator <<, float and int.
|
||||
|
|
|
|||
|
|
@ -1,2 +1,2 @@
|
|||
GDTEST_ANALYZER_ERROR
|
||||
Invalid operands to operator >>, int and float.
|
||||
>> ERROR at line 3: Invalid operands to operator >>, int and float.
|
||||
|
|
|
|||
|
|
@ -1,2 +1,2 @@
|
|||
GDTEST_ANALYZER_ERROR
|
||||
Static function "not_existing_method()" not found in base "MyClass".
|
||||
>> ERROR at line 7: Static function "not_existing_method()" not found in base "MyClass".
|
||||
|
|
|
|||
|
|
@ -1,2 +1,2 @@
|
|||
GDTEST_ANALYZER_ERROR
|
||||
Invalid cast. Cannot convert from "int" to "Array".
|
||||
>> ERROR at line 3: Invalid cast. Cannot convert from "int" to "Array".
|
||||
|
|
|
|||
|
|
@ -1,2 +1,2 @@
|
|||
GDTEST_ANALYZER_ERROR
|
||||
Invalid cast. Cannot convert from "int" to "Node".
|
||||
>> ERROR at line 3: Invalid cast. Cannot convert from "int" to "Node".
|
||||
|
|
|
|||
|
|
@ -1,2 +1,2 @@
|
|||
GDTEST_ANALYZER_ERROR
|
||||
Invalid cast. Cannot convert from "RefCounted" to "int".
|
||||
>> ERROR at line 3: Invalid cast. Cannot convert from "RefCounted" to "int".
|
||||
|
|
|
|||
|
|
@ -1,2 +1,2 @@
|
|||
GDTEST_ANALYZER_ERROR
|
||||
Class "Vector2" hides a built-in type.
|
||||
>> ERROR at line 1: Class "Vector2" hides a built-in type.
|
||||
|
|
|
|||
|
|
@ -1,2 +1,2 @@
|
|||
GDTEST_ANALYZER_ERROR
|
||||
Cannot assign a new value to a constant.
|
||||
>> ERROR at line 5: Cannot assign a new value to a constant.
|
||||
|
|
|
|||
|
|
@ -1,2 +1,2 @@
|
|||
GDTEST_ANALYZER_ERROR
|
||||
Cannot assign a new value to a constant.
|
||||
>> ERROR at line 5: Cannot assign a new value to a constant.
|
||||
|
|
|
|||
|
|
@ -1,2 +1,2 @@
|
|||
GDTEST_ANALYZER_ERROR
|
||||
The member "Vector2" cannot have the same name as a builtin type.
|
||||
>> ERROR at line 1: The member "Vector2" cannot have the same name as a builtin type.
|
||||
|
|
|
|||
|
|
@ -1,2 +1,2 @@
|
|||
GDTEST_ANALYZER_ERROR
|
||||
Expression is of type "int" so it can't be of type "String".
|
||||
>> ERROR at line 5: Expression is of type "int" so it can't be of type "String".
|
||||
|
|
|
|||
|
|
@ -1,2 +1,3 @@
|
|||
GDTEST_ANALYZER_ERROR
|
||||
Member "CONSTANT" is not a function.
|
||||
>> ERROR at line 5: Member "CONSTANT" is not a function.
|
||||
>> ERROR at line 5: Name "CONSTANT" called as a function but is a "int".
|
||||
|
|
|
|||
|
|
@ -1,2 +1,2 @@
|
|||
GDTEST_ANALYZER_ERROR
|
||||
Expression is of type "B" so it can't be of type "C".
|
||||
>> ERROR at line 10: Expression is of type "B" so it can't be of type "C".
|
||||
|
|
|
|||
|
|
@ -1,2 +1,2 @@
|
|||
GDTEST_ANALYZER_ERROR
|
||||
Cyclic inheritance.
|
||||
>> ERROR at line 4: Cyclic inheritance.
|
||||
|
|
|
|||
|
|
@ -1,2 +1,3 @@
|
|||
GDTEST_ANALYZER_ERROR
|
||||
Could not resolve member "c1": Cyclic reference.
|
||||
>> ERROR at line 5: Could not resolve member "c1": Cyclic reference.
|
||||
>> ERROR at line 5: Could not resolve type for constant "c2".
|
||||
|
|
|
|||
|
|
@ -1,2 +1,3 @@
|
|||
GDTEST_ANALYZER_ERROR
|
||||
Could not resolve member "E1": Cyclic reference.
|
||||
>> ERROR at line 5: Could not resolve member "E1": Cyclic reference.
|
||||
>> ERROR at line 5: Enum values must be constant.
|
||||
|
|
|
|||
|
|
@ -1,2 +1,2 @@
|
|||
GDTEST_ANALYZER_ERROR
|
||||
Could not resolve member "EV1": Cyclic reference.
|
||||
>> ERROR at line 5: Could not resolve member "EV1": Cyclic reference.
|
||||
|
|
|
|||
|
|
@ -1,2 +1,3 @@
|
|||
GDTEST_ANALYZER_ERROR
|
||||
Could not resolve external class member "v".
|
||||
>> ERROR at line 4: Could not resolve external class member "v".
|
||||
>> ERROR at line 4: Cannot find member "v" in base "TestCyclicRefExternalA".
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
class_name TestCyclicRefExternalA
|
||||
|
||||
const B = preload("cyclic_ref_external.gd")
|
||||
|
||||
var v = B.v
|
||||
|
|
|
|||
|
|
@ -1,2 +1,3 @@
|
|||
GDTEST_ANALYZER_ERROR
|
||||
Could not resolve member "f1": Cyclic reference.
|
||||
>> ERROR at line 8: Could not resolve member "f1": Cyclic reference.
|
||||
>> ERROR at line 8: Cannot infer the type of "p" parameter because the value doesn't have a set type.
|
||||
|
|
|
|||
|
|
@ -1,2 +1,2 @@
|
|||
GDTEST_ANALYZER_ERROR
|
||||
Could not resolve member "f": Cyclic reference.
|
||||
>> ERROR at line 11: Could not resolve member "f": Cyclic reference.
|
||||
|
|
|
|||
|
|
@ -1,2 +1,3 @@
|
|||
GDTEST_ANALYZER_ERROR
|
||||
Could not resolve member "v1": Cyclic reference.
|
||||
>> ERROR at line 5: Could not resolve member "v1": Cyclic reference.
|
||||
>> ERROR at line 5: Cannot infer the type of "v2" variable because the value doesn't have a set type.
|
||||
|
|
|
|||
|
|
@ -1,2 +1,3 @@
|
|||
GDTEST_ANALYZER_ERROR
|
||||
Could not resolve member "v1": Cyclic reference.
|
||||
>> ERROR at line 1: Could not resolve member "v1": Cyclic reference.
|
||||
>> ERROR at line 1: Could not resolve type for variable "v1".
|
||||
|
|
|
|||
|
|
@ -1,2 +1,2 @@
|
|||
GDTEST_ANALYZER_ERROR
|
||||
Key "a" was already used in this dictionary (at line 3).
|
||||
>> ERROR at line 5: Key "a" was already used in this dictionary (at line 3).
|
||||
|
|
|
|||
|
|
@ -1,2 +1,2 @@
|
|||
GDTEST_ANALYZER_ERROR
|
||||
Key "a" was already used in this dictionary (at line 3).
|
||||
>> ERROR at line 5: Key "a" was already used in this dictionary (at line 3).
|
||||
|
|
|
|||
|
|
@ -1,2 +1,2 @@
|
|||
GDTEST_ANALYZER_ERROR
|
||||
Key "a" was already used in this dictionary (at line 3).
|
||||
>> ERROR at line 5: Key "a" was already used in this dictionary (at line 3).
|
||||
|
|
|
|||
|
|
@ -1,2 +1,2 @@
|
|||
GDTEST_ANALYZER_ERROR
|
||||
Key "key" was already used in this dictionary (at line 5).
|
||||
>> ERROR at line 6: Key "key" was already used in this dictionary (at line 5).
|
||||
|
|
|
|||
|
|
@ -1,2 +1,2 @@
|
|||
GDTEST_ANALYZER_ERROR
|
||||
Cannot construct native class "Time" because it is an engine singleton.
|
||||
>> ERROR at line 2: Cannot construct native class "Time" because it is an engine singleton.
|
||||
|
|
|
|||
|
|
@ -1,2 +1,2 @@
|
|||
GDTEST_ANALYZER_ERROR
|
||||
Cannot call non-const Dictionary function "clear()" on enum "Enum".
|
||||
>> ERROR at line 4: Cannot call non-const Dictionary function "clear()" on enum "Enum".
|
||||
|
|
|
|||
|
|
@ -1,2 +1,2 @@
|
|||
GDTEST_ANALYZER_ERROR
|
||||
Cannot find member "V3" in base "enum_bad_value.gd.Enum".
|
||||
>> ERROR at line 4: Cannot find member "V3" in base "enum_bad_value.gd.Enum".
|
||||
|
|
|
|||
|
|
@ -0,0 +1,2 @@
|
|||
func test():
|
||||
print(Vector3.Axis)
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
GDTEST_ANALYZER_ERROR
|
||||
>> ERROR at line 2: Type "Axis" in base "Vector3" cannot be used on its own.
|
||||
|
|
@ -1,2 +1,3 @@
|
|||
GDTEST_ANALYZER_ERROR
|
||||
Cannot assign a value of type "enum_class_var_assign_with_wrong_enum_type.gd.MyOtherEnum" as "enum_class_var_assign_with_wrong_enum_type.gd.MyEnum".
|
||||
>> ERROR at line 9: Cannot assign a value of type "enum_class_var_assign_with_wrong_enum_type.gd.MyOtherEnum" as "enum_class_var_assign_with_wrong_enum_type.gd.MyEnum".
|
||||
>> ERROR at line 9: Value of type "enum_class_var_assign_with_wrong_enum_type.gd.MyOtherEnum" cannot be assigned to a variable of type "enum_class_var_assign_with_wrong_enum_type.gd.MyEnum".
|
||||
|
|
|
|||
|
|
@ -1,2 +1,3 @@
|
|||
GDTEST_ANALYZER_ERROR
|
||||
Cannot assign a value of type "enum_class_var_init_with_wrong_enum_type.gd.MyOtherEnum" as "enum_class_var_init_with_wrong_enum_type.gd.MyEnum".
|
||||
>> ERROR at line 5: Cannot assign a value of type "enum_class_var_init_with_wrong_enum_type.gd.MyOtherEnum" as "enum_class_var_init_with_wrong_enum_type.gd.MyEnum".
|
||||
>> ERROR at line 5: Cannot assign a value of type enum_class_var_init_with_wrong_enum_type.gd.MyOtherEnum to variable "class_var" with specified type enum_class_var_init_with_wrong_enum_type.gd.MyEnum.
|
||||
|
|
|
|||
|
|
@ -1,2 +1,2 @@
|
|||
GDTEST_ANALYZER_ERROR
|
||||
Cannot call non-const Dictionary function "clear()" on enum "Enum".
|
||||
>> ERROR at line 5: Cannot call non-const Dictionary function "clear()" on enum "Enum".
|
||||
|
|
|
|||
|
|
@ -1,2 +1,2 @@
|
|||
GDTEST_ANALYZER_ERROR
|
||||
Enum values must be integers.
|
||||
>> ERROR at line 3: Enum values must be integers.
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue