summaryrefslogtreecommitdiff
path: root/modules/gdscript
diff options
context:
space:
mode:
Diffstat (limited to 'modules/gdscript')
-rw-r--r--modules/gdscript/doc_classes/@GDScript.xml71
-rw-r--r--modules/gdscript/editor/gdscript_translation_parser_plugin.h2
-rw-r--r--modules/gdscript/gdscript.cpp13
-rw-r--r--modules/gdscript/gdscript.h2
-rw-r--r--modules/gdscript/gdscript_analyzer.cpp112
-rw-r--r--modules/gdscript/gdscript_analyzer.h9
-rw-r--r--modules/gdscript/gdscript_byte_codegen.cpp652
-rw-r--r--modules/gdscript/gdscript_byte_codegen.h166
-rw-r--r--modules/gdscript/gdscript_cache.cpp2
-rw-r--r--modules/gdscript/gdscript_cache.h6
-rw-r--r--modules/gdscript/gdscript_codegen.h9
-rw-r--r--modules/gdscript/gdscript_compiler.cpp83
-rw-r--r--modules/gdscript/gdscript_compiler.h16
-rw-r--r--modules/gdscript/gdscript_disassembler.cpp813
-rw-r--r--modules/gdscript/gdscript_editor.cpp54
-rw-r--r--modules/gdscript/gdscript_function.cpp2081
-rw-r--r--modules/gdscript/gdscript_function.h195
-rw-r--r--modules/gdscript/gdscript_functions.cpp82
-rw-r--r--modules/gdscript/gdscript_functions.h8
-rw-r--r--modules/gdscript/gdscript_parser.cpp54
-rw-r--r--modules/gdscript/gdscript_parser.h22
-rw-r--r--modules/gdscript/gdscript_tokenizer.cpp2
-rw-r--r--modules/gdscript/gdscript_tokenizer.h8
-rw-r--r--modules/gdscript/gdscript_vm.cpp2863
-rw-r--r--modules/gdscript/gdscript_warning.cpp2
-rw-r--r--modules/gdscript/gdscript_warning.h4
-rw-r--r--modules/gdscript/icons/GDScript.svg6
-rw-r--r--modules/gdscript/language_server/gdscript_extend_parser.h2
-rw-r--r--modules/gdscript/language_server/gdscript_language_protocol.cpp2
-rw-r--r--modules/gdscript/language_server/gdscript_text_document.h2
-rw-r--r--modules/gdscript/language_server/gdscript_workspace.cpp4
-rw-r--r--modules/gdscript/language_server/gdscript_workspace.h2
-rw-r--r--modules/gdscript/language_server/lsp.hpp5
-rw-r--r--modules/gdscript/register_types.cpp2
-rw-r--r--modules/gdscript/tests/test_gdscript.cpp79
-rw-r--r--modules/gdscript/tests/test_gdscript.h1
36 files changed, 4944 insertions, 2492 deletions
diff --git a/modules/gdscript/doc_classes/@GDScript.xml b/modules/gdscript/doc_classes/@GDScript.xml
index fc27892099..d90b3e52d0 100644
--- a/modules/gdscript/doc_classes/@GDScript.xml
+++ b/modules/gdscript/doc_classes/@GDScript.xml
@@ -7,6 +7,7 @@
List of core built-in GDScript functions. Math functions and other utilities. Everything else is provided by objects. (Keywords: builtin, built in, global functions.)
</description>
<tutorials>
+ <link title="Random number generation">https://docs.godotengine.org/en/latest/tutorials/math/random_number_generation.html</link>
</tutorials>
<methods>
<method name="Color8">
@@ -93,14 +94,15 @@
<argument index="1" name="message" type="String" default="&quot;&quot;">
</argument>
<description>
- Asserts that the [code]condition[/code] is [code]true[/code]. If the [code]condition[/code] is [code]false[/code], an error is generated and the program is halted until you resume it. Only executes in debug builds, or when running the game from the editor. Use it for debugging purposes, to make sure a statement is [code]true[/code] during development.
+ Asserts that the [code]condition[/code] is [code]true[/code]. If the [code]condition[/code] is [code]false[/code], an error is generated. When running from the editor, the running project will also be paused until you resume it. This can be used as a stronger form of [method push_error] for reporting errors to project developers or add-on users.
+ [b]Note:[/b] For performance reasons, the code inside [method assert] is only executed in debug builds or when running the project from the editor. Don't include code that has side effects in an [method assert] call. Otherwise, the project will behave differently when exported in release mode.
The optional [code]message[/code] argument, if given, is shown in addition to the generic "Assertion failed" message. You can use this to provide additional details about why the assertion failed.
[codeblock]
- # Imagine we always want speed to be between 0 and 20
- speed = -10
+ # Imagine we always want speed to be between 0 and 20.
+ var speed = -10
assert(speed &lt; 20) # True, the program will continue
assert(speed &gt;= 0) # False, the program will stop
- assert(speed &gt;= 0 &amp;&amp; speed &lt; 20) # You can also combine the two conditional statements in one check
+ assert(speed &gt;= 0 and speed &lt; 20) # You can also combine the two conditional statements in one check
assert(speed &lt; 20, "speed = %f, but the speed limit is 20" % speed) # Show a message with clarifying details
[/codeblock]
</description>
@@ -385,24 +387,6 @@
[/codeblock]
</description>
</method>
- <method name="funcref">
- <return type="FuncRef">
- </return>
- <argument index="0" name="instance" type="Object">
- </argument>
- <argument index="1" name="funcname" type="String">
- </argument>
- <description>
- Returns a reference to the specified function [code]funcname[/code] in the [code]instance[/code] node. As functions aren't first-class objects in GDscript, use [code]funcref[/code] to store a [FuncRef] in a variable and call it later.
- [codeblock]
- func foo():
- return("bar")
-
- a = funcref(self, "foo")
- print(a.call_func()) # Prints bar
- [/codeblock]
- </description>
- </method>
<method name="get_stack">
<return type="Array">
</return>
@@ -920,20 +904,6 @@
[/codeblock]
</description>
</method>
- <method name="rand_range">
- <return type="float">
- </return>
- <argument index="0" name="from" type="float">
- </argument>
- <argument index="1" name="to" type="float">
- </argument>
- <description>
- Random range, any floating point value between [code]from[/code] and [code]to[/code].
- [codeblock]
- prints(rand_range(0, 1), rand_range(0, 1)) # Prints e.g. 0.135591 0.405263
- [/codeblock]
- </description>
- </method>
<method name="rand_seed">
<return type="Array">
</return>
@@ -953,6 +923,20 @@
[/codeblock]
</description>
</method>
+ <method name="randf_range">
+ <return type="float">
+ </return>
+ <argument index="0" name="from" type="float">
+ </argument>
+ <argument index="1" name="to" type="float">
+ </argument>
+ <description>
+ Random range, any floating point value between [code]from[/code] and [code]to[/code].
+ [codeblock]
+ prints(randf_range(-10, 10), randf_range(-10, 10)) # Prints e.g. -3.844535 7.45315
+ [/codeblock]
+ </description>
+ </method>
<method name="randi">
<return type="int">
</return>
@@ -966,6 +950,21 @@
[/codeblock]
</description>
</method>
+ <method name="randi_range">
+ <return type="int">
+ </return>
+ <argument index="0" name="from" type="int">
+ </argument>
+ <argument index="1" name="to" type="int">
+ </argument>
+ <description>
+ Random range, any 32-bit integer value between [code]from[/code] and [code]to[/code] (inclusive). If [code]to[/code] is lesser than [code]from[/code] they are swapped.
+ [codeblock]
+ print(randi_range(0, 1)) # Prints 0 or 1
+ print(randi_range(-10, 1000)) # Prints any number from -10 to 1000
+ [/codeblock]
+ </description>
+ </method>
<method name="randomize">
<return type="void">
</return>
diff --git a/modules/gdscript/editor/gdscript_translation_parser_plugin.h b/modules/gdscript/editor/gdscript_translation_parser_plugin.h
index 5ea416d4cc..d763df01f5 100644
--- a/modules/gdscript/editor/gdscript_translation_parser_plugin.h
+++ b/modules/gdscript/editor/gdscript_translation_parser_plugin.h
@@ -31,7 +31,7 @@
#ifndef GDSCRIPT_TRANSLATION_PARSER_PLUGIN_H
#define GDSCRIPT_TRANSLATION_PARSER_PLUGIN_H
-#include "core/set.h"
+#include "core/templates/set.h"
#include "editor/editor_translation_parser.h"
#include "modules/gdscript/gdscript_parser.h"
#include "modules/regex/regex.h"
diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp
index e70e3f7272..53602f7a9b 100644
--- a/modules/gdscript/gdscript.cpp
+++ b/modules/gdscript/gdscript.cpp
@@ -32,13 +32,13 @@
#include <stdint.h>
+#include "core/config/engine.h"
+#include "core/config/project_settings.h"
+#include "core/core_constants.h"
#include "core/core_string_names.h"
-#include "core/engine.h"
-#include "core/global_constants.h"
#include "core/io/file_access_encrypted.h"
#include "core/os/file_access.h"
#include "core/os/os.h"
-#include "core/project_settings.h"
#include "gdscript_analyzer.h"
#include "gdscript_cache.h"
#include "gdscript_compiler.h"
@@ -1092,7 +1092,8 @@ bool GDScriptInstance::set(const StringName &p_name, const Variant &p_value) {
// Try conversion
Callable::CallError ce;
const Variant *value = &p_value;
- Variant converted = Variant::construct(member->data_type.builtin_type, &value, 1, ce);
+ Variant converted;
+ Variant::construct(member->data_type.builtin_type, converted, &value, 1, ce);
if (ce.error == Callable::CallError::CALL_OK) {
members.write[member->index] = converted;
return true;
@@ -1500,9 +1501,9 @@ void GDScriptLanguage::remove_named_global_constant(const StringName &p_name) {
void GDScriptLanguage::init() {
//populate global constants
- int gcc = GlobalConstants::get_global_constant_count();
+ int gcc = CoreConstants::get_global_constant_count();
for (int i = 0; i < gcc; i++) {
- _add_global(StaticCString::create(GlobalConstants::get_global_constant_name(i)), GlobalConstants::get_global_constant_value(i));
+ _add_global(StaticCString::create(CoreConstants::get_global_constant_name(i)), CoreConstants::get_global_constant_value(i));
}
_add_global(StaticCString::create("PI"), Math_PI);
diff --git a/modules/gdscript/gdscript.h b/modules/gdscript/gdscript.h
index 79317ff846..b69a6e39c0 100644
--- a/modules/gdscript/gdscript.h
+++ b/modules/gdscript/gdscript.h
@@ -35,7 +35,7 @@
#include "core/debugger/script_debugger.h"
#include "core/io/resource_loader.h"
#include "core/io/resource_saver.h"
-#include "core/script_language.h"
+#include "core/object/script_language.h"
#include "gdscript_function.h"
class GDScriptNativeClass : public Reference {
diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp
index 943a49060f..6b23ab1616 100644
--- a/modules/gdscript/gdscript_analyzer.cpp
+++ b/modules/gdscript/gdscript_analyzer.cpp
@@ -30,12 +30,12 @@
#include "gdscript_analyzer.h"
-#include "core/class_db.h"
-#include "core/hash_map.h"
+#include "core/config/project_settings.h"
#include "core/io/resource_loader.h"
+#include "core/object/class_db.h"
+#include "core/object/script_language.h"
#include "core/os/file_access.h"
-#include "core/project_settings.h"
-#include "core/script_language.h"
+#include "core/templates/hash_map.h"
#include "gdscript.h"
// TODO: Move this to a central location (maybe core?).
@@ -577,6 +577,12 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas
GDScriptParser::DataType datatype = member.constant->get_datatype();
if (member.constant->initializer) {
+ if (member.constant->initializer->type == GDScriptParser::Node::ARRAY) {
+ const_fold_array(static_cast<GDScriptParser::ArrayNode *>(member.constant->initializer));
+ } else if (member.constant->initializer->type == GDScriptParser::Node::DICTIONARY) {
+ const_fold_dictionary(static_cast<GDScriptParser::DictionaryNode *>(member.constant->initializer));
+ }
+
if (!member.constant->initializer->is_constant) {
push_error(R"(Initializer for a constant must be a constant expression.)", member.constant->initializer);
}
@@ -1113,6 +1119,11 @@ void GDScriptAnalyzer::resolve_constant(GDScriptParser::ConstantNode *p_constant
GDScriptParser::DataType type;
reduce_expression(p_constant->initializer);
+ if (p_constant->initializer->type == GDScriptParser::Node::ARRAY) {
+ const_fold_array(static_cast<GDScriptParser::ArrayNode *>(p_constant->initializer));
+ } else if (p_constant->initializer->type == GDScriptParser::Node::DICTIONARY) {
+ const_fold_dictionary(static_cast<GDScriptParser::DictionaryNode *>(p_constant->initializer));
+ }
if (!p_constant->initializer->is_constant) {
push_error(vformat(R"(Assigned value for constant "%s" isn't a constant expression.)", p_constant->identifier->name), p_constant->initializer);
@@ -1422,22 +1433,9 @@ void GDScriptAnalyzer::reduce_expression(GDScriptParser::ExpressionNode *p_expre
}
void GDScriptAnalyzer::reduce_array(GDScriptParser::ArrayNode *p_array) {
- bool all_is_constant = true;
-
for (int i = 0; i < p_array->elements.size(); i++) {
GDScriptParser::ExpressionNode *element = p_array->elements[i];
reduce_expression(element);
- all_is_constant = all_is_constant && element->is_constant;
- }
-
- if (all_is_constant) {
- Array array;
- array.resize(p_array->elements.size());
- for (int i = 0; i < p_array->elements.size(); i++) {
- array[i] = p_array->elements[i]->reduced_value;
- }
- p_array->is_constant = true;
- p_array->reduced_value = array;
}
// It's array in any case.
@@ -1713,7 +1711,8 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool is_awa
}
Callable::CallError err;
- Variant value = Variant::construct(builtin_type, (const Variant **)args.ptr(), args.size(), err);
+ Variant value;
+ Variant::construct(builtin_type, value, (const Variant **)args.ptr(), args.size(), err);
switch (err.error) {
case Callable::CallError::CALL_ERROR_INVALID_ARGUMENT:
@@ -1984,8 +1983,6 @@ void GDScriptAnalyzer::reduce_cast(GDScriptParser::CastNode *p_cast) {
}
void GDScriptAnalyzer::reduce_dictionary(GDScriptParser::DictionaryNode *p_dictionary) {
- bool all_is_constant = true;
-
HashMap<Variant, GDScriptParser::ExpressionNode *, VariantHasher, VariantComparator> elements;
for (int i = 0; i < p_dictionary->elements.size(); i++) {
@@ -1994,7 +1991,6 @@ void GDScriptAnalyzer::reduce_dictionary(GDScriptParser::DictionaryNode *p_dicti
reduce_expression(element.key);
}
reduce_expression(element.value);
- all_is_constant = all_is_constant && element.key->is_constant && element.value->is_constant;
if (element.key->is_constant) {
if (elements.has(element.key->reduced_value)) {
@@ -2005,16 +2001,6 @@ void GDScriptAnalyzer::reduce_dictionary(GDScriptParser::DictionaryNode *p_dicti
}
}
- if (all_is_constant) {
- Dictionary dict;
- for (int i = 0; i < p_dictionary->elements.size(); i++) {
- const GDScriptParser::DictionaryNode::Pair &element = p_dictionary->elements[i];
- dict[element.key->reduced_value] = element.value->reduced_value;
- }
- p_dictionary->is_constant = true;
- p_dictionary->reduced_value = dict;
- }
-
// It's dictionary in any case.
GDScriptParser::DataType dict_type;
dict_type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
@@ -2090,7 +2076,8 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
}
default: {
Callable::CallError temp;
- Variant dummy = Variant::construct(base.builtin_type, nullptr, 0, temp);
+ Variant dummy;
+ Variant::construct(base.builtin_type, dummy, nullptr, 0, temp);
List<PropertyInfo> properties;
dummy.get_property_list(&properties);
for (const List<PropertyInfo>::Element *E = properties.front(); E != nullptr; E = E->next()) {
@@ -2294,10 +2281,12 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident
StringName name = p_identifier->name;
p_identifier->source = GDScriptParser::IdentifierNode::UNDEFINED_SOURCE;
- // Check globals.
- if (GDScriptParser::get_builtin_type(name) < Variant::VARIANT_MAX) {
+ // Check globals. We make an exception for Variant::OBJECT because it's the base class for
+ // non-builtin types so we allow doing e.g. Object.new()
+ Variant::Type builtin_type = GDScriptParser::get_builtin_type(name);
+ if (builtin_type != Variant::OBJECT && builtin_type < Variant::VARIANT_MAX) {
if (can_be_builtin) {
- p_identifier->set_datatype(make_builtin_meta_type(GDScriptParser::get_builtin_type(name)));
+ p_identifier->set_datatype(make_builtin_meta_type(builtin_type));
return;
} else {
push_error(R"(Builtin type cannot be used as a name on its own.)", p_identifier);
@@ -2451,7 +2440,7 @@ void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscri
if (p_subscript->base->is_constant) {
// Just try to get it.
bool valid = false;
- Variant value = p_subscript->base->reduced_value.get_named(p_subscript->attribute->name, &valid);
+ Variant value = p_subscript->base->reduced_value.get_named(p_subscript->attribute->name, valid);
if (!valid) {
push_error(vformat(R"(Cannot get member "%s" from "%s".)", p_subscript->attribute->name, p_subscript->base->reduced_value), p_subscript->index);
} else {
@@ -2552,7 +2541,7 @@ void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscri
error = index_type.builtin_type != Variant::INT && index_type.builtin_type != Variant::STRING;
break;
// Don't support indexing, but we will check it later.
- case Variant::_RID:
+ case Variant::RID:
case Variant::BOOL:
case Variant::CALLABLE:
case Variant::FLOAT:
@@ -2585,7 +2574,7 @@ void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscri
switch (base_type.builtin_type) {
// Can't index at all.
- case Variant::_RID:
+ case Variant::RID:
case Variant::BOOL:
case Variant::CALLABLE:
case Variant::FLOAT:
@@ -2737,6 +2726,46 @@ void GDScriptAnalyzer::reduce_unary_op(GDScriptParser::UnaryOpNode *p_unary_op)
p_unary_op->set_datatype(result);
}
+void GDScriptAnalyzer::const_fold_array(GDScriptParser::ArrayNode *p_array) {
+ bool all_is_constant = true;
+
+ for (int i = 0; i < p_array->elements.size(); i++) {
+ GDScriptParser::ExpressionNode *element = p_array->elements[i];
+ all_is_constant = all_is_constant && element->is_constant;
+ if (!all_is_constant) {
+ return;
+ }
+ }
+
+ Array array;
+ array.resize(p_array->elements.size());
+ for (int i = 0; i < p_array->elements.size(); i++) {
+ array[i] = p_array->elements[i]->reduced_value;
+ }
+ p_array->is_constant = true;
+ p_array->reduced_value = array;
+}
+
+void GDScriptAnalyzer::const_fold_dictionary(GDScriptParser::DictionaryNode *p_dictionary) {
+ bool all_is_constant = true;
+
+ for (int i = 0; i < p_dictionary->elements.size(); i++) {
+ const GDScriptParser::DictionaryNode::Pair &element = p_dictionary->elements[i];
+ all_is_constant = all_is_constant && element.key->is_constant && element.value->is_constant;
+ if (!all_is_constant) {
+ return;
+ }
+ }
+
+ Dictionary dict;
+ for (int i = 0; i < p_dictionary->elements.size(); i++) {
+ const GDScriptParser::DictionaryNode::Pair &element = p_dictionary->elements[i];
+ dict[element.key->reduced_value] = element.value->reduced_value;
+ }
+ p_dictionary->is_constant = true;
+ p_dictionary->reduced_value = dict;
+}
+
GDScriptParser::DataType GDScriptAnalyzer::type_from_variant(const Variant &p_value, const GDScriptParser::Node *p_source) {
GDScriptParser::DataType result;
result.is_constant = true;
@@ -2842,7 +2871,8 @@ bool GDScriptAnalyzer::get_function_signature(GDScriptParser::Node *p_source, GD
if (p_base_type.kind == GDScriptParser::DataType::BUILTIN) {
// Construct a base type to get methods.
Callable::CallError err;
- Variant dummy = Variant::construct(p_base_type.builtin_type, nullptr, 0, err);
+ Variant dummy;
+ Variant::construct(p_base_type.builtin_type, dummy, nullptr, 0, err);
if (err.error != Callable::CallError::CALL_OK) {
ERR_FAIL_V_MSG(false, "Could not construct base Variant type.");
}
@@ -3068,7 +3098,7 @@ GDScriptParser::DataType GDScriptAnalyzer::get_operation_type(Variant::Operator
a = a_ref;
} else {
Callable::CallError err;
- a = Variant::construct(a_type, nullptr, 0, err);
+ Variant::construct(a_type, a, nullptr, 0, err);
if (err.error != Callable::CallError::CALL_OK) {
r_valid = false;
ERR_FAIL_V_MSG(result, vformat("Could not construct value of type %s", Variant::get_type_name(a_type)));
@@ -3081,7 +3111,7 @@ GDScriptParser::DataType GDScriptAnalyzer::get_operation_type(Variant::Operator
b = b_ref;
} else {
Callable::CallError err;
- b = Variant::construct(b_type, nullptr, 0, err);
+ Variant::construct(b_type, b, nullptr, 0, err);
if (err.error != Callable::CallError::CALL_OK) {
r_valid = false;
ERR_FAIL_V_MSG(result, vformat("Could not construct value of type %s", Variant::get_type_name(b_type)));
diff --git a/modules/gdscript/gdscript_analyzer.h b/modules/gdscript/gdscript_analyzer.h
index c3911cce76..0a952cc621 100644
--- a/modules/gdscript/gdscript_analyzer.h
+++ b/modules/gdscript/gdscript_analyzer.h
@@ -31,9 +31,9 @@
#ifndef GDSCRIPT_ANALYZER_H
#define GDSCRIPT_ANALYZER_H
-#include "core/object.h"
-#include "core/reference.h"
-#include "core/set.h"
+#include "core/object/object.h"
+#include "core/object/reference.h"
+#include "core/templates/set.h"
#include "gdscript_cache.h"
#include "gdscript_parser.h"
@@ -89,6 +89,9 @@ class GDScriptAnalyzer {
void reduce_ternary_op(GDScriptParser::TernaryOpNode *p_ternary_op);
void reduce_unary_op(GDScriptParser::UnaryOpNode *p_unary_op);
+ void const_fold_array(GDScriptParser::ArrayNode *p_array);
+ void const_fold_dictionary(GDScriptParser::DictionaryNode *p_dictionary);
+
// Helpers.
GDScriptParser::DataType type_from_variant(const Variant &p_value, const GDScriptParser::Node *p_source);
GDScriptParser::DataType type_from_metatype(const GDScriptParser::DataType &p_meta_type) const;
diff --git a/modules/gdscript/gdscript_byte_codegen.cpp b/modules/gdscript/gdscript_byte_codegen.cpp
index 8f0ce99de6..5f1c738207 100644
--- a/modules/gdscript/gdscript_byte_codegen.cpp
+++ b/modules/gdscript/gdscript_byte_codegen.cpp
@@ -111,7 +111,7 @@ void GDScriptByteCodeGenerator::write_start(GDScript *p_script, const StringName
}
GDScriptFunction *GDScriptByteCodeGenerator::write_end() {
- append(GDScriptFunction::OPCODE_END);
+ append(GDScriptFunction::OPCODE_END, 0);
if (constant_map.size()) {
function->_constant_count = constant_map.size();
@@ -158,11 +158,132 @@ GDScriptFunction *GDScriptByteCodeGenerator::write_end() {
function->_default_arg_ptr = nullptr;
}
+ if (operator_func_map.size()) {
+ function->operator_funcs.resize(operator_func_map.size());
+ function->_operator_funcs_count = function->operator_funcs.size();
+ function->_operator_funcs_ptr = function->operator_funcs.ptr();
+ for (const Map<Variant::ValidatedOperatorEvaluator, int>::Element *E = operator_func_map.front(); E; E = E->next()) {
+ function->operator_funcs.write[E->get()] = E->key();
+ }
+ } else {
+ function->_operator_funcs_count = 0;
+ function->_operator_funcs_ptr = nullptr;
+ }
+
+ if (setters_map.size()) {
+ function->setters.resize(setters_map.size());
+ function->_setters_count = function->setters.size();
+ function->_setters_ptr = function->setters.ptr();
+ for (const Map<Variant::ValidatedSetter, int>::Element *E = setters_map.front(); E; E = E->next()) {
+ function->setters.write[E->get()] = E->key();
+ }
+ } else {
+ function->_setters_count = 0;
+ function->_setters_ptr = nullptr;
+ }
+
+ if (getters_map.size()) {
+ function->getters.resize(getters_map.size());
+ function->_getters_count = function->getters.size();
+ function->_getters_ptr = function->getters.ptr();
+ for (const Map<Variant::ValidatedGetter, int>::Element *E = getters_map.front(); E; E = E->next()) {
+ function->getters.write[E->get()] = E->key();
+ }
+ } else {
+ function->_getters_count = 0;
+ function->_getters_ptr = nullptr;
+ }
+
+ if (keyed_setters_map.size()) {
+ function->keyed_setters.resize(keyed_setters_map.size());
+ function->_keyed_setters_count = function->keyed_setters.size();
+ function->_keyed_setters_ptr = function->keyed_setters.ptr();
+ for (const Map<Variant::ValidatedKeyedSetter, int>::Element *E = keyed_setters_map.front(); E; E = E->next()) {
+ function->keyed_setters.write[E->get()] = E->key();
+ }
+ } else {
+ function->_keyed_setters_count = 0;
+ function->_keyed_setters_ptr = nullptr;
+ }
+
+ if (keyed_getters_map.size()) {
+ function->keyed_getters.resize(keyed_getters_map.size());
+ function->_keyed_getters_count = function->keyed_getters.size();
+ function->_keyed_getters_ptr = function->keyed_getters.ptr();
+ for (const Map<Variant::ValidatedKeyedGetter, int>::Element *E = keyed_getters_map.front(); E; E = E->next()) {
+ function->keyed_getters.write[E->get()] = E->key();
+ }
+ } else {
+ function->_keyed_getters_count = 0;
+ function->_keyed_getters_ptr = nullptr;
+ }
+
+ if (indexed_setters_map.size()) {
+ function->indexed_setters.resize(indexed_setters_map.size());
+ function->_indexed_setters_count = function->indexed_setters.size();
+ function->_indexed_setters_ptr = function->indexed_setters.ptr();
+ for (const Map<Variant::ValidatedIndexedSetter, int>::Element *E = indexed_setters_map.front(); E; E = E->next()) {
+ function->indexed_setters.write[E->get()] = E->key();
+ }
+ } else {
+ function->_indexed_setters_count = 0;
+ function->_indexed_setters_ptr = nullptr;
+ }
+
+ if (indexed_getters_map.size()) {
+ function->indexed_getters.resize(indexed_getters_map.size());
+ function->_indexed_getters_count = function->indexed_getters.size();
+ function->_indexed_getters_ptr = function->indexed_getters.ptr();
+ for (const Map<Variant::ValidatedIndexedGetter, int>::Element *E = indexed_getters_map.front(); E; E = E->next()) {
+ function->indexed_getters.write[E->get()] = E->key();
+ }
+ } else {
+ function->_indexed_getters_count = 0;
+ function->_indexed_getters_ptr = nullptr;
+ }
+
+ if (builtin_method_map.size()) {
+ function->builtin_methods.resize(builtin_method_map.size());
+ function->_builtin_methods_ptr = function->builtin_methods.ptr();
+ function->_builtin_methods_count = builtin_method_map.size();
+ for (const Map<Variant::ValidatedBuiltInMethod, int>::Element *E = builtin_method_map.front(); E; E = E->next()) {
+ function->builtin_methods.write[E->get()] = E->key();
+ }
+ } else {
+ function->_builtin_methods_ptr = nullptr;
+ function->_builtin_methods_count = 0;
+ }
+
+ if (constructors_map.size()) {
+ function->constructors.resize(constructors_map.size());
+ function->_constructors_ptr = function->constructors.ptr();
+ function->_constructors_count = constructors_map.size();
+ for (const Map<Variant::ValidatedConstructor, int>::Element *E = constructors_map.front(); E; E = E->next()) {
+ function->constructors.write[E->get()] = E->key();
+ }
+ } else {
+ function->_constructors_ptr = nullptr;
+ function->_constructors_count = 0;
+ }
+
+ if (method_bind_map.size()) {
+ function->methods.resize(method_bind_map.size());
+ function->_methods_ptr = function->methods.ptrw();
+ function->_methods_count = method_bind_map.size();
+ for (const Map<MethodBind *, int>::Element *E = method_bind_map.front(); E; E = E->next()) {
+ function->methods.write[E->get()] = E->key();
+ }
+ } else {
+ function->_methods_ptr = nullptr;
+ function->_methods_count = 0;
+ }
+
if (debug_stack) {
function->stack_debug = stack_debug;
}
function->_stack_size = stack_max;
- function->_call_size = call_max;
+ function->_instruction_args_size = instr_args_max;
+ function->_ptrcall_args_size = ptrcall_max;
ended = true;
return function;
@@ -178,37 +299,56 @@ void GDScriptByteCodeGenerator::set_initial_line(int p_line) {
function->_initial_line = p_line;
}
+#define HAS_BUILTIN_TYPE(m_var) \
+ (m_var.type.has_type && m_var.type.kind == GDScriptDataType::BUILTIN)
+
+#define IS_BUILTIN_TYPE(m_var, m_type) \
+ (m_var.type.has_type && m_var.type.kind == GDScriptDataType::BUILTIN && m_var.type.builtin_type == m_type)
+
void GDScriptByteCodeGenerator::write_operator(const Address &p_target, Variant::Operator p_operator, const Address &p_left_operand, const Address &p_right_operand) {
- append(GDScriptFunction::OPCODE_OPERATOR);
- append(p_operator);
+ if (HAS_BUILTIN_TYPE(p_left_operand) && HAS_BUILTIN_TYPE(p_right_operand)) {
+ // Gather specific operator.
+ Variant::ValidatedOperatorEvaluator op_func = Variant::get_validated_operator_evaluator(p_operator, p_left_operand.type.builtin_type, p_right_operand.type.builtin_type);
+
+ append(GDScriptFunction::OPCODE_OPERATOR_VALIDATED, 3);
+ append(p_left_operand);
+ append(p_right_operand);
+ append(p_target);
+ append(op_func);
+ return;
+ }
+
+ // No specific types, perform variant evaluation.
+ append(GDScriptFunction::OPCODE_OPERATOR, 3);
append(p_left_operand);
append(p_right_operand);
append(p_target);
+ append(p_operator);
}
void GDScriptByteCodeGenerator::write_type_test(const Address &p_target, const Address &p_source, const Address &p_type) {
- append(GDScriptFunction::OPCODE_EXTENDS_TEST);
+ append(GDScriptFunction::OPCODE_EXTENDS_TEST, 3);
append(p_source);
append(p_type);
append(p_target);
}
void GDScriptByteCodeGenerator::write_type_test_builtin(const Address &p_target, const Address &p_source, Variant::Type p_type) {
- append(GDScriptFunction::OPCODE_IS_BUILTIN);
+ append(GDScriptFunction::OPCODE_IS_BUILTIN, 3);
append(p_source);
- append(p_type);
append(p_target);
+ append(p_type);
}
void GDScriptByteCodeGenerator::write_and_left_operand(const Address &p_left_operand) {
- append(GDScriptFunction::OPCODE_JUMP_IF_NOT);
+ append(GDScriptFunction::OPCODE_JUMP_IF_NOT, 1);
append(p_left_operand);
logic_op_jump_pos1.push_back(opcodes.size());
append(0); // Jump target, will be patched.
}
void GDScriptByteCodeGenerator::write_and_right_operand(const Address &p_right_operand) {
- append(GDScriptFunction::OPCODE_JUMP_IF_NOT);
+ append(GDScriptFunction::OPCODE_JUMP_IF_NOT, 1);
append(p_right_operand);
logic_op_jump_pos2.push_back(opcodes.size());
append(0); // Jump target, will be patched.
@@ -216,29 +356,29 @@ void GDScriptByteCodeGenerator::write_and_right_operand(const Address &p_right_o
void GDScriptByteCodeGenerator::write_end_and(const Address &p_target) {
// If here means both operands are true.
- append(GDScriptFunction::OPCODE_ASSIGN_TRUE);
+ append(GDScriptFunction::OPCODE_ASSIGN_TRUE, 1);
append(p_target);
// Jump away from the fail condition.
- append(GDScriptFunction::OPCODE_JUMP);
+ append(GDScriptFunction::OPCODE_JUMP, 0);
append(opcodes.size() + 3);
// Here it means one of operands is false.
patch_jump(logic_op_jump_pos1.back()->get());
patch_jump(logic_op_jump_pos2.back()->get());
logic_op_jump_pos1.pop_back();
logic_op_jump_pos2.pop_back();
- append(GDScriptFunction::OPCODE_ASSIGN_FALSE);
+ append(GDScriptFunction::OPCODE_ASSIGN_FALSE, 0);
append(p_target);
}
void GDScriptByteCodeGenerator::write_or_left_operand(const Address &p_left_operand) {
- append(GDScriptFunction::OPCODE_JUMP_IF);
+ append(GDScriptFunction::OPCODE_JUMP_IF, 1);
append(p_left_operand);
logic_op_jump_pos1.push_back(opcodes.size());
append(0); // Jump target, will be patched.
}
void GDScriptByteCodeGenerator::write_or_right_operand(const Address &p_right_operand) {
- append(GDScriptFunction::OPCODE_JUMP_IF);
+ append(GDScriptFunction::OPCODE_JUMP_IF, 1);
append(p_right_operand);
logic_op_jump_pos2.push_back(opcodes.size());
append(0); // Jump target, will be patched.
@@ -246,17 +386,17 @@ void GDScriptByteCodeGenerator::write_or_right_operand(const Address &p_right_op
void GDScriptByteCodeGenerator::write_end_or(const Address &p_target) {
// If here means both operands are false.
- append(GDScriptFunction::OPCODE_ASSIGN_FALSE);
+ append(GDScriptFunction::OPCODE_ASSIGN_FALSE, 1);
append(p_target);
// Jump away from the success condition.
- append(GDScriptFunction::OPCODE_JUMP);
+ append(GDScriptFunction::OPCODE_JUMP, 0);
append(opcodes.size() + 3);
// Here it means one of operands is false.
patch_jump(logic_op_jump_pos1.back()->get());
patch_jump(logic_op_jump_pos2.back()->get());
logic_op_jump_pos1.pop_back();
logic_op_jump_pos2.pop_back();
- append(GDScriptFunction::OPCODE_ASSIGN_TRUE);
+ append(GDScriptFunction::OPCODE_ASSIGN_TRUE, 1);
append(p_target);
}
@@ -265,18 +405,18 @@ void GDScriptByteCodeGenerator::write_start_ternary(const Address &p_target) {
}
void GDScriptByteCodeGenerator::write_ternary_condition(const Address &p_condition) {
- append(GDScriptFunction::OPCODE_JUMP_IF_NOT);
+ append(GDScriptFunction::OPCODE_JUMP_IF_NOT, 1);
append(p_condition);
ternary_jump_fail_pos.push_back(opcodes.size());
append(0); // Jump target, will be patched.
}
void GDScriptByteCodeGenerator::write_ternary_true_expr(const Address &p_expr) {
- append(GDScriptFunction::OPCODE_ASSIGN);
+ append(GDScriptFunction::OPCODE_ASSIGN, 2);
append(ternary_result.back()->get());
append(p_expr);
// Jump away from the false path.
- append(GDScriptFunction::OPCODE_JUMP);
+ append(GDScriptFunction::OPCODE_JUMP, 0);
ternary_jump_skip_pos.push_back(opcodes.size());
append(0);
// Fail must jump here.
@@ -285,7 +425,7 @@ void GDScriptByteCodeGenerator::write_ternary_true_expr(const Address &p_expr) {
}
void GDScriptByteCodeGenerator::write_ternary_false_expr(const Address &p_expr) {
- append(GDScriptFunction::OPCODE_ASSIGN);
+ append(GDScriptFunction::OPCODE_ASSIGN, 2);
append(ternary_result.back()->get());
append(p_expr);
}
@@ -296,43 +436,100 @@ void GDScriptByteCodeGenerator::write_end_ternary() {
}
void GDScriptByteCodeGenerator::write_set(const Address &p_target, const Address &p_index, const Address &p_source) {
- append(GDScriptFunction::OPCODE_SET);
+ if (HAS_BUILTIN_TYPE(p_target)) {
+ if (IS_BUILTIN_TYPE(p_index, Variant::INT) && Variant::get_member_validated_indexed_setter(p_target.type.builtin_type)) {
+ // Use indexed setter instead.
+ Variant::ValidatedIndexedSetter setter = Variant::get_member_validated_indexed_setter(p_target.type.builtin_type);
+ append(GDScriptFunction::OPCODE_SET_INDEXED_VALIDATED, 3);
+ append(p_target);
+ append(p_index);
+ append(p_source);
+ append(setter);
+ return;
+ } else if (Variant::get_member_validated_keyed_setter(p_target.type.builtin_type)) {
+ Variant::ValidatedKeyedSetter setter = Variant::get_member_validated_keyed_setter(p_target.type.builtin_type);
+ append(GDScriptFunction::OPCODE_SET_KEYED_VALIDATED, 3);
+ append(p_target);
+ append(p_index);
+ append(p_source);
+ append(setter);
+ return;
+ }
+ }
+
+ append(GDScriptFunction::OPCODE_SET_KEYED, 3);
append(p_target);
append(p_index);
append(p_source);
}
void GDScriptByteCodeGenerator::write_get(const Address &p_target, const Address &p_index, const Address &p_source) {
- append(GDScriptFunction::OPCODE_GET);
+ if (HAS_BUILTIN_TYPE(p_source)) {
+ if (IS_BUILTIN_TYPE(p_index, Variant::INT) && Variant::get_member_validated_indexed_getter(p_source.type.builtin_type)) {
+ // Use indexed getter instead.
+ Variant::ValidatedIndexedGetter getter = Variant::get_member_validated_indexed_getter(p_source.type.builtin_type);
+ append(GDScriptFunction::OPCODE_GET_INDEXED_VALIDATED, 3);
+ append(p_source);
+ append(p_index);
+ append(p_target);
+ append(getter);
+ return;
+ } else if (Variant::get_member_validated_keyed_getter(p_source.type.builtin_type)) {
+ Variant::ValidatedKeyedGetter getter = Variant::get_member_validated_keyed_getter(p_source.type.builtin_type);
+ append(GDScriptFunction::OPCODE_GET_KEYED_VALIDATED, 3);
+ append(p_source);
+ append(p_index);
+ append(p_target);
+ append(getter);
+ return;
+ }
+ }
+ append(GDScriptFunction::OPCODE_GET_KEYED, 3);
append(p_source);
append(p_index);
append(p_target);
}
void GDScriptByteCodeGenerator::write_set_named(const Address &p_target, const StringName &p_name, const Address &p_source) {
- append(GDScriptFunction::OPCODE_SET_NAMED);
+ if (HAS_BUILTIN_TYPE(p_target) && Variant::get_member_validated_setter(p_target.type.builtin_type, p_name)) {
+ Variant::ValidatedSetter setter = Variant::get_member_validated_setter(p_target.type.builtin_type, p_name);
+ append(GDScriptFunction::OPCODE_SET_NAMED_VALIDATED, 2);
+ append(p_target);
+ append(p_source);
+ append(setter);
+ return;
+ }
+ append(GDScriptFunction::OPCODE_SET_NAMED, 2);
append(p_target);
- append(p_name);
append(p_source);
+ append(p_name);
}
void GDScriptByteCodeGenerator::write_get_named(const Address &p_target, const StringName &p_name, const Address &p_source) {
- append(GDScriptFunction::OPCODE_GET_NAMED);
+ if (HAS_BUILTIN_TYPE(p_source) && Variant::get_member_validated_getter(p_source.type.builtin_type, p_name)) {
+ Variant::ValidatedGetter getter = Variant::get_member_validated_getter(p_source.type.builtin_type, p_name);
+ append(GDScriptFunction::OPCODE_GET_NAMED_VALIDATED, 2);
+ append(p_source);
+ append(p_target);
+ append(getter);
+ return;
+ }
+ append(GDScriptFunction::OPCODE_GET_NAMED, 2);
append(p_source);
- append(p_name);
append(p_target);
+ append(p_name);
}
void GDScriptByteCodeGenerator::write_set_member(const Address &p_value, const StringName &p_name) {
- append(GDScriptFunction::OPCODE_SET_MEMBER);
- append(p_name);
+ append(GDScriptFunction::OPCODE_SET_MEMBER, 1);
append(p_value);
+ append(p_name);
}
void GDScriptByteCodeGenerator::write_get_member(const Address &p_target, const StringName &p_name) {
- append(GDScriptFunction::OPCODE_GET_MEMBER);
- append(p_name);
+ append(GDScriptFunction::OPCODE_GET_MEMBER, 1);
append(p_target);
+ append(p_name);
}
void GDScriptByteCodeGenerator::write_assign(const Address &p_target, const Address &p_source) {
@@ -340,34 +537,35 @@ void GDScriptByteCodeGenerator::write_assign(const Address &p_target, const Addr
// Typed assignment.
switch (p_target.type.kind) {
case GDScriptDataType::BUILTIN: {
- append(GDScriptFunction::OPCODE_ASSIGN_TYPED_BUILTIN);
- append(p_target.type.builtin_type);
+ append(GDScriptFunction::OPCODE_ASSIGN_TYPED_BUILTIN, 2);
append(p_target);
append(p_source);
+ append(p_target.type.builtin_type);
} break;
case GDScriptDataType::NATIVE: {
int class_idx = GDScriptLanguage::get_singleton()->get_global_map()[p_target.type.native_type];
class_idx |= (GDScriptFunction::ADDR_TYPE_GLOBAL << GDScriptFunction::ADDR_BITS);
- append(GDScriptFunction::OPCODE_ASSIGN_TYPED_NATIVE);
- append(class_idx);
+ append(GDScriptFunction::OPCODE_ASSIGN_TYPED_NATIVE, 3);
append(p_target);
append(p_source);
+ append(class_idx);
} break;
case GDScriptDataType::SCRIPT:
case GDScriptDataType::GDSCRIPT: {
Variant script = p_target.type.script_type;
int idx = get_constant_pos(script);
+ idx |= (GDScriptFunction::ADDR_TYPE_LOCAL_CONSTANT << GDScriptFunction::ADDR_BITS);
- append(GDScriptFunction::OPCODE_ASSIGN_TYPED_SCRIPT);
- append(idx);
+ append(GDScriptFunction::OPCODE_ASSIGN_TYPED_SCRIPT, 3);
append(p_target);
append(p_source);
+ append(idx);
} break;
default: {
ERR_PRINT("Compiler bug: unresolved assign.");
// Shouldn't get here, but fail-safe to a regular assignment
- append(GDScriptFunction::OPCODE_ASSIGN);
+ append(GDScriptFunction::OPCODE_ASSIGN, 2);
append(p_target);
append(p_source);
}
@@ -375,13 +573,13 @@ void GDScriptByteCodeGenerator::write_assign(const Address &p_target, const Addr
} else {
if (p_target.type.kind == GDScriptDataType::BUILTIN && p_source.type.kind == GDScriptDataType::BUILTIN && p_target.type.builtin_type != p_source.type.builtin_type) {
// Need conversion..
- append(GDScriptFunction::OPCODE_ASSIGN_TYPED_BUILTIN);
- append(p_target.type.builtin_type);
+ append(GDScriptFunction::OPCODE_ASSIGN_TYPED_BUILTIN, 2);
append(p_target);
append(p_source);
+ append(p_target.type.builtin_type);
} else {
// Either untyped assignment or already type-checked by the parser
- append(GDScriptFunction::OPCODE_ASSIGN);
+ append(GDScriptFunction::OPCODE_ASSIGN, 2);
append(p_target);
append(p_source);
}
@@ -389,34 +587,37 @@ void GDScriptByteCodeGenerator::write_assign(const Address &p_target, const Addr
}
void GDScriptByteCodeGenerator::write_assign_true(const Address &p_target) {
- append(GDScriptFunction::OPCODE_ASSIGN_TRUE);
+ append(GDScriptFunction::OPCODE_ASSIGN_TRUE, 1);
append(p_target);
}
void GDScriptByteCodeGenerator::write_assign_false(const Address &p_target) {
- append(GDScriptFunction::OPCODE_ASSIGN_FALSE);
+ append(GDScriptFunction::OPCODE_ASSIGN_FALSE, 1);
append(p_target);
}
void GDScriptByteCodeGenerator::write_cast(const Address &p_target, const Address &p_source, const GDScriptDataType &p_type) {
+ int index = 0;
+
switch (p_type.kind) {
case GDScriptDataType::BUILTIN: {
- append(GDScriptFunction::OPCODE_CAST_TO_BUILTIN);
- append(p_type.builtin_type);
+ append(GDScriptFunction::OPCODE_CAST_TO_BUILTIN, 2);
+ index = p_type.builtin_type;
} break;
case GDScriptDataType::NATIVE: {
int class_idx = GDScriptLanguage::get_singleton()->get_global_map()[p_type.native_type];
class_idx |= (GDScriptFunction::ADDR_TYPE_GLOBAL << GDScriptFunction::ADDR_BITS);
- append(GDScriptFunction::OPCODE_CAST_TO_NATIVE);
- append(class_idx);
+ append(GDScriptFunction::OPCODE_CAST_TO_NATIVE, 3);
+ index = class_idx;
} break;
case GDScriptDataType::SCRIPT:
case GDScriptDataType::GDSCRIPT: {
Variant script = p_type.script_type;
int idx = get_constant_pos(script);
+ idx |= (GDScriptFunction::ADDR_TYPE_LOCAL_CONSTANT << GDScriptFunction::ADDR_BITS);
- append(GDScriptFunction::OPCODE_CAST_TO_SCRIPT);
- append(idx);
+ append(GDScriptFunction::OPCODE_CAST_TO_SCRIPT, 3);
+ index = idx;
} break;
default: {
return;
@@ -425,146 +626,272 @@ void GDScriptByteCodeGenerator::write_cast(const Address &p_target, const Addres
append(p_source);
append(p_target);
+ append(index);
}
void GDScriptByteCodeGenerator::write_call(const Address &p_target, const Address &p_base, const StringName &p_function_name, const Vector<Address> &p_arguments) {
- append(p_target.mode == Address::NIL ? GDScriptFunction::OPCODE_CALL : GDScriptFunction::OPCODE_CALL_RETURN);
- append(p_arguments.size());
- append(p_base);
- append(p_function_name);
+ append(p_target.mode == Address::NIL ? GDScriptFunction::OPCODE_CALL : GDScriptFunction::OPCODE_CALL_RETURN, 2 + p_arguments.size());
for (int i = 0; i < p_arguments.size(); i++) {
append(p_arguments[i]);
}
+ append(p_base);
append(p_target);
- alloc_call(p_arguments.size());
+ append(p_arguments.size());
+ append(p_function_name);
}
void GDScriptByteCodeGenerator::write_super_call(const Address &p_target, const StringName &p_function_name, const Vector<Address> &p_arguments) {
- append(GDScriptFunction::OPCODE_CALL_SELF_BASE);
- append(p_function_name);
- append(p_arguments.size());
+ append(GDScriptFunction::OPCODE_CALL_SELF_BASE, 1 + p_arguments.size());
for (int i = 0; i < p_arguments.size(); i++) {
append(p_arguments[i]);
}
append(p_target);
- alloc_call(p_arguments.size());
+ append(p_arguments.size());
+ append(p_function_name);
}
void GDScriptByteCodeGenerator::write_call_async(const Address &p_target, const Address &p_base, const StringName &p_function_name, const Vector<Address> &p_arguments) {
- append(GDScriptFunction::OPCODE_CALL_ASYNC);
- append(p_arguments.size());
- append(p_base);
- append(p_function_name);
+ append(GDScriptFunction::OPCODE_CALL_ASYNC, 2 + p_arguments.size());
for (int i = 0; i < p_arguments.size(); i++) {
append(p_arguments[i]);
}
+ append(p_base);
append(p_target);
- alloc_call(p_arguments.size());
+ append(p_arguments.size());
+ append(p_function_name);
}
void GDScriptByteCodeGenerator::write_call_builtin(const Address &p_target, GDScriptFunctions::Function p_function, const Vector<Address> &p_arguments) {
- append(GDScriptFunction::OPCODE_CALL_BUILT_IN);
- append(p_function);
- append(p_arguments.size());
+ append(GDScriptFunction::OPCODE_CALL_BUILT_IN, 1 + p_arguments.size());
for (int i = 0; i < p_arguments.size(); i++) {
append(p_arguments[i]);
}
append(p_target);
- alloc_call(p_arguments.size());
+ append(p_arguments.size());
+ append(p_function);
}
-void GDScriptByteCodeGenerator::write_call_method_bind(const Address &p_target, const Address &p_base, const MethodBind *p_method, const Vector<Address> &p_arguments) {
- append(p_target.mode == Address::NIL ? GDScriptFunction::OPCODE_CALL : GDScriptFunction::OPCODE_CALL_RETURN);
- append(p_arguments.size());
- append(p_base);
- append(p_method->get_name());
+void GDScriptByteCodeGenerator::write_call_builtin_type(const Address &p_target, const Address &p_base, Variant::Type p_type, const StringName &p_method, const Vector<Address> &p_arguments) {
+ bool is_validated = false;
+
+ // Check if all types are correct.
+ if (Variant::is_builtin_method_vararg(p_type, p_method)) {
+ is_validated = true; // Vararg works fine with any argument, since they can be any type.
+ } else if (p_arguments.size() == Variant::get_builtin_method_argument_count(p_type, p_method)) {
+ bool all_types_exact = true;
+ for (int i = 0; i < p_arguments.size(); i++) {
+ if (!IS_BUILTIN_TYPE(p_arguments[i], Variant::get_builtin_method_argument_type(p_type, p_method, i))) {
+ all_types_exact = false;
+ break;
+ }
+ }
+
+ is_validated = all_types_exact;
+ }
+
+ if (!is_validated) {
+ // Perform regular call.
+ write_call(p_target, p_base, p_method, p_arguments);
+ return;
+ }
+
+ append(GDScriptFunction::OPCODE_CALL_BUILTIN_TYPE_VALIDATED, 2 + p_arguments.size());
+
for (int i = 0; i < p_arguments.size(); i++) {
append(p_arguments[i]);
}
+ append(p_base);
append(p_target);
- alloc_call(p_arguments.size());
+ append(p_arguments.size());
+ append(Variant::get_validated_builtin_method(p_type, p_method));
}
-void GDScriptByteCodeGenerator::write_call_ptrcall(const Address &p_target, const Address &p_base, const MethodBind *p_method, const Vector<Address> &p_arguments) {
- append(p_target.mode == Address::NIL ? GDScriptFunction::OPCODE_CALL : GDScriptFunction::OPCODE_CALL_RETURN);
- append(p_arguments.size());
+void GDScriptByteCodeGenerator::write_call_method_bind(const Address &p_target, const Address &p_base, MethodBind *p_method, const Vector<Address> &p_arguments) {
+ append(p_target.mode == Address::NIL ? GDScriptFunction::OPCODE_CALL_METHOD_BIND : GDScriptFunction::OPCODE_CALL_METHOD_BIND_RET, 2 + p_arguments.size());
+ for (int i = 0; i < p_arguments.size(); i++) {
+ append(p_arguments[i]);
+ }
append(p_base);
- append(p_method->get_name());
+ append(p_target);
+ append(p_arguments.size());
+ append(p_method);
+}
+
+void GDScriptByteCodeGenerator::write_call_ptrcall(const Address &p_target, const Address &p_base, MethodBind *p_method, const Vector<Address> &p_arguments) {
+#define CASE_TYPE(m_type) \
+ case Variant::m_type: \
+ append(GDScriptFunction::OPCODE_CALL_PTRCALL_##m_type, 2 + p_arguments.size()); \
+ break
+
+ bool is_ptrcall = true;
+
+ if (p_method->has_return()) {
+ MethodInfo info;
+ ClassDB::get_method_info(p_method->get_instance_class(), p_method->get_name(), &info);
+ switch (info.return_val.type) {
+ CASE_TYPE(BOOL);
+ CASE_TYPE(INT);
+ CASE_TYPE(FLOAT);
+ CASE_TYPE(STRING);
+ CASE_TYPE(VECTOR2);
+ CASE_TYPE(VECTOR2I);
+ CASE_TYPE(RECT2);
+ CASE_TYPE(RECT2I);
+ CASE_TYPE(VECTOR3);
+ CASE_TYPE(VECTOR3I);
+ CASE_TYPE(TRANSFORM2D);
+ CASE_TYPE(PLANE);
+ CASE_TYPE(AABB);
+ CASE_TYPE(BASIS);
+ CASE_TYPE(TRANSFORM);
+ CASE_TYPE(COLOR);
+ CASE_TYPE(STRING_NAME);
+ CASE_TYPE(NODE_PATH);
+ CASE_TYPE(RID);
+ CASE_TYPE(QUAT);
+ CASE_TYPE(OBJECT);
+ CASE_TYPE(CALLABLE);
+ CASE_TYPE(SIGNAL);
+ CASE_TYPE(DICTIONARY);
+ CASE_TYPE(ARRAY);
+ CASE_TYPE(PACKED_BYTE_ARRAY);
+ CASE_TYPE(PACKED_INT32_ARRAY);
+ CASE_TYPE(PACKED_INT64_ARRAY);
+ CASE_TYPE(PACKED_FLOAT32_ARRAY);
+ CASE_TYPE(PACKED_FLOAT64_ARRAY);
+ CASE_TYPE(PACKED_STRING_ARRAY);
+ CASE_TYPE(PACKED_VECTOR2_ARRAY);
+ CASE_TYPE(PACKED_VECTOR3_ARRAY);
+ CASE_TYPE(PACKED_COLOR_ARRAY);
+ default:
+ append(p_target.mode == Address::NIL ? GDScriptFunction::OPCODE_CALL_METHOD_BIND : GDScriptFunction::OPCODE_CALL_METHOD_BIND_RET, 2 + p_arguments.size());
+ is_ptrcall = false;
+ break;
+ }
+ } else {
+ append(GDScriptFunction::OPCODE_CALL_PTRCALL_NO_RETURN, 2 + p_arguments.size());
+ }
+
for (int i = 0; i < p_arguments.size(); i++) {
append(p_arguments[i]);
}
+ append(p_base);
append(p_target);
- alloc_call(p_arguments.size());
+ append(p_arguments.size());
+ append(p_method);
+ if (is_ptrcall) {
+ alloc_ptrcall(p_arguments.size());
+ }
+
+#undef CASE_TYPE
}
void GDScriptByteCodeGenerator::write_call_self(const Address &p_target, const StringName &p_function_name, const Vector<Address> &p_arguments) {
- append(p_target.mode == Address::NIL ? GDScriptFunction::OPCODE_CALL : GDScriptFunction::OPCODE_CALL_RETURN);
- append(p_arguments.size());
- append(GDScriptFunction::ADDR_TYPE_SELF << GDScriptFunction::ADDR_BITS);
- append(p_function_name);
+ append(p_target.mode == Address::NIL ? GDScriptFunction::OPCODE_CALL : GDScriptFunction::OPCODE_CALL_RETURN, 2 + p_arguments.size());
for (int i = 0; i < p_arguments.size(); i++) {
append(p_arguments[i]);
}
+ append(GDScriptFunction::ADDR_TYPE_SELF << GDScriptFunction::ADDR_BITS);
append(p_target);
- alloc_call(p_arguments.size());
+ append(p_arguments.size());
+ append(p_function_name);
}
void GDScriptByteCodeGenerator::write_call_script_function(const Address &p_target, const Address &p_base, const StringName &p_function_name, const Vector<Address> &p_arguments) {
- append(p_target.mode == Address::NIL ? GDScriptFunction::OPCODE_CALL : GDScriptFunction::OPCODE_CALL_RETURN);
- append(p_arguments.size());
- append(p_base);
- append(p_function_name);
+ append(p_target.mode == Address::NIL ? GDScriptFunction::OPCODE_CALL : GDScriptFunction::OPCODE_CALL_RETURN, 2 + p_arguments.size());
for (int i = 0; i < p_arguments.size(); i++) {
append(p_arguments[i]);
}
+ append(p_base);
append(p_target);
- alloc_call(p_arguments.size());
+ append(p_arguments.size());
+ append(p_function_name);
}
void GDScriptByteCodeGenerator::write_construct(const Address &p_target, Variant::Type p_type, const Vector<Address> &p_arguments) {
- append(GDScriptFunction::OPCODE_CONSTRUCT);
- append(p_type);
- append(p_arguments.size());
+ // Try to find an appropriate constructor.
+ bool all_have_type = true;
+ Vector<Variant::Type> arg_types;
+ for (int i = 0; i < p_arguments.size(); i++) {
+ if (!HAS_BUILTIN_TYPE(p_arguments[i])) {
+ all_have_type = false;
+ break;
+ }
+ arg_types.push_back(p_arguments[i].type.builtin_type);
+ }
+ if (all_have_type) {
+ int valid_constructor = -1;
+ for (int i = 0; i < Variant::get_constructor_count(p_type); i++) {
+ if (Variant::get_constructor_argument_count(p_type, i) != p_arguments.size()) {
+ continue;
+ }
+ int types_correct = true;
+ for (int j = 0; j < arg_types.size(); j++) {
+ if (arg_types[j] != Variant::get_constructor_argument_type(p_type, i, j)) {
+ types_correct = false;
+ break;
+ }
+ }
+ if (types_correct) {
+ valid_constructor = i;
+ break;
+ }
+ }
+ if (valid_constructor >= 0) {
+ append(GDScriptFunction::OPCODE_CONSTRUCT_VALIDATED, 1 + p_arguments.size());
+ for (int i = 0; i < p_arguments.size(); i++) {
+ append(p_arguments[i]);
+ }
+ append(p_target);
+ append(p_arguments.size());
+ append(Variant::get_validated_constructor(p_type, valid_constructor));
+ return;
+ }
+ }
+
+ append(GDScriptFunction::OPCODE_CONSTRUCT, 1 + p_arguments.size());
for (int i = 0; i < p_arguments.size(); i++) {
append(p_arguments[i]);
}
append(p_target);
+ append(p_arguments.size());
+ append(p_type);
}
void GDScriptByteCodeGenerator::write_construct_array(const Address &p_target, const Vector<Address> &p_arguments) {
- append(GDScriptFunction::OPCODE_CONSTRUCT_ARRAY);
- append(p_arguments.size());
+ append(GDScriptFunction::OPCODE_CONSTRUCT_ARRAY, 1 + p_arguments.size());
for (int i = 0; i < p_arguments.size(); i++) {
append(p_arguments[i]);
}
append(p_target);
+ append(p_arguments.size());
}
void GDScriptByteCodeGenerator::write_construct_dictionary(const Address &p_target, const Vector<Address> &p_arguments) {
- append(GDScriptFunction::OPCODE_CONSTRUCT_DICTIONARY);
- append(p_arguments.size() / 2); // This is number of key-value pairs, so only half of actual arguments.
+ append(GDScriptFunction::OPCODE_CONSTRUCT_DICTIONARY, 1 + p_arguments.size());
for (int i = 0; i < p_arguments.size(); i++) {
append(p_arguments[i]);
}
append(p_target);
+ append(p_arguments.size() / 2); // This is number of key-value pairs, so only half of actual arguments.
}
void GDScriptByteCodeGenerator::write_await(const Address &p_target, const Address &p_operand) {
- append(GDScriptFunction::OPCODE_AWAIT);
+ append(GDScriptFunction::OPCODE_AWAIT, 1);
append(p_operand);
- append(GDScriptFunction::OPCODE_AWAIT_RESUME);
+ append(GDScriptFunction::OPCODE_AWAIT_RESUME, 1);
append(p_target);
}
void GDScriptByteCodeGenerator::write_if(const Address &p_condition) {
- append(GDScriptFunction::OPCODE_JUMP_IF_NOT);
+ append(GDScriptFunction::OPCODE_JUMP_IF_NOT, 1);
append(p_condition);
if_jmp_addrs.push_back(opcodes.size());
append(0); // Jump destination, will be patched.
}
void GDScriptByteCodeGenerator::write_else() {
- append(GDScriptFunction::OPCODE_JUMP); // Jump from true if block;
+ append(GDScriptFunction::OPCODE_JUMP, 0); // Jump from true if block;
int else_jmp_addr = opcodes.size();
append(0); // Jump destination, will be patched.
@@ -579,40 +906,127 @@ void GDScriptByteCodeGenerator::write_endif() {
}
void GDScriptByteCodeGenerator::write_for(const Address &p_variable, const Address &p_list) {
- int counter_pos = increase_stack() | (GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS);
- int container_pos = increase_stack() | (GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS);
+ int counter_pos = add_temporary() | (GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS);
+ int container_pos = add_temporary() | (GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS);
current_breaks_to_patch.push_back(List<int>());
// Assign container.
- append(GDScriptFunction::OPCODE_ASSIGN);
+ append(GDScriptFunction::OPCODE_ASSIGN, 2);
append(container_pos);
append(p_list);
+ GDScriptFunction::Opcode begin_opcode = GDScriptFunction::OPCODE_ITERATE_BEGIN;
+ GDScriptFunction::Opcode iterate_opcode = GDScriptFunction::OPCODE_ITERATE;
+
+ if (p_list.type.has_type) {
+ if (p_list.type.kind == GDScriptDataType::BUILTIN) {
+ switch (p_list.type.builtin_type) {
+ case Variant::INT:
+ begin_opcode = GDScriptFunction::OPCODE_ITERATE_BEGIN_INT;
+ iterate_opcode = GDScriptFunction::OPCODE_ITERATE_INT;
+ break;
+ case Variant::FLOAT:
+ begin_opcode = GDScriptFunction::OPCODE_ITERATE_BEGIN_FLOAT;
+ iterate_opcode = GDScriptFunction::OPCODE_ITERATE_FLOAT;
+ break;
+ case Variant::VECTOR2:
+ begin_opcode = GDScriptFunction::OPCODE_ITERATE_BEGIN_VECTOR2;
+ iterate_opcode = GDScriptFunction::OPCODE_ITERATE_VECTOR2;
+ break;
+ case Variant::VECTOR2I:
+ begin_opcode = GDScriptFunction::OPCODE_ITERATE_BEGIN_VECTOR2I;
+ iterate_opcode = GDScriptFunction::OPCODE_ITERATE_VECTOR2I;
+ break;
+ case Variant::VECTOR3:
+ begin_opcode = GDScriptFunction::OPCODE_ITERATE_BEGIN_VECTOR3;
+ iterate_opcode = GDScriptFunction::OPCODE_ITERATE_VECTOR3;
+ break;
+ case Variant::VECTOR3I:
+ begin_opcode = GDScriptFunction::OPCODE_ITERATE_BEGIN_VECTOR3I;
+ iterate_opcode = GDScriptFunction::OPCODE_ITERATE_VECTOR3I;
+ break;
+ case Variant::STRING:
+ begin_opcode = GDScriptFunction::OPCODE_ITERATE_BEGIN_STRING;
+ iterate_opcode = GDScriptFunction::OPCODE_ITERATE_STRING;
+ break;
+ case Variant::DICTIONARY:
+ begin_opcode = GDScriptFunction::OPCODE_ITERATE_BEGIN_DICTIONARY;
+ iterate_opcode = GDScriptFunction::OPCODE_ITERATE_DICTIONARY;
+ break;
+ case Variant::ARRAY:
+ begin_opcode = GDScriptFunction::OPCODE_ITERATE_BEGIN_ARRAY;
+ iterate_opcode = GDScriptFunction::OPCODE_ITERATE_ARRAY;
+ break;
+ case Variant::PACKED_BYTE_ARRAY:
+ begin_opcode = GDScriptFunction::OPCODE_ITERATE_BEGIN_PACKED_BYTE_ARRAY;
+ iterate_opcode = GDScriptFunction::OPCODE_ITERATE_PACKED_BYTE_ARRAY;
+ break;
+ case Variant::PACKED_INT32_ARRAY:
+ begin_opcode = GDScriptFunction::OPCODE_ITERATE_BEGIN_PACKED_INT32_ARRAY;
+ iterate_opcode = GDScriptFunction::OPCODE_ITERATE_PACKED_INT32_ARRAY;
+ break;
+ case Variant::PACKED_INT64_ARRAY:
+ begin_opcode = GDScriptFunction::OPCODE_ITERATE_BEGIN_PACKED_INT64_ARRAY;
+ iterate_opcode = GDScriptFunction::OPCODE_ITERATE_PACKED_INT64_ARRAY;
+ break;
+ case Variant::PACKED_FLOAT32_ARRAY:
+ begin_opcode = GDScriptFunction::OPCODE_ITERATE_BEGIN_PACKED_FLOAT32_ARRAY;
+ iterate_opcode = GDScriptFunction::OPCODE_ITERATE_PACKED_FLOAT32_ARRAY;
+ break;
+ case Variant::PACKED_FLOAT64_ARRAY:
+ begin_opcode = GDScriptFunction::OPCODE_ITERATE_BEGIN_PACKED_FLOAT64_ARRAY;
+ iterate_opcode = GDScriptFunction::OPCODE_ITERATE_PACKED_FLOAT64_ARRAY;
+ break;
+ case Variant::PACKED_STRING_ARRAY:
+ begin_opcode = GDScriptFunction::OPCODE_ITERATE_BEGIN_PACKED_STRING_ARRAY;
+ iterate_opcode = GDScriptFunction::OPCODE_ITERATE_PACKED_STRING_ARRAY;
+ break;
+ case Variant::PACKED_VECTOR2_ARRAY:
+ begin_opcode = GDScriptFunction::OPCODE_ITERATE_BEGIN_PACKED_VECTOR2_ARRAY;
+ iterate_opcode = GDScriptFunction::OPCODE_ITERATE_PACKED_VECTOR2_ARRAY;
+ break;
+ case Variant::PACKED_VECTOR3_ARRAY:
+ begin_opcode = GDScriptFunction::OPCODE_ITERATE_BEGIN_PACKED_VECTOR3_ARRAY;
+ iterate_opcode = GDScriptFunction::OPCODE_ITERATE_PACKED_VECTOR3_ARRAY;
+ break;
+ case Variant::PACKED_COLOR_ARRAY:
+ begin_opcode = GDScriptFunction::OPCODE_ITERATE_BEGIN_PACKED_COLOR_ARRAY;
+ iterate_opcode = GDScriptFunction::OPCODE_ITERATE_PACKED_COLOR_ARRAY;
+ break;
+ default:
+ break;
+ }
+ } else {
+ begin_opcode = GDScriptFunction::OPCODE_ITERATE_BEGIN_OBJECT;
+ iterate_opcode = GDScriptFunction::OPCODE_ITERATE_OBJECT;
+ }
+ }
+
// Begin loop.
- append(GDScriptFunction::OPCODE_ITERATE_BEGIN);
+ append(begin_opcode, 3);
append(counter_pos);
append(container_pos);
+ append(p_variable);
for_jmp_addrs.push_back(opcodes.size());
append(0); // End of loop address, will be patched.
- append(p_variable);
- append(GDScriptFunction::OPCODE_JUMP);
+ append(GDScriptFunction::OPCODE_JUMP, 0);
append(opcodes.size() + 6); // Skip over 'continue' code.
// Next iteration.
int continue_addr = opcodes.size();
continue_addrs.push_back(continue_addr);
- append(GDScriptFunction::OPCODE_ITERATE);
+ append(iterate_opcode, 3);
append(counter_pos);
append(container_pos);
+ append(p_variable);
for_jmp_addrs.push_back(opcodes.size());
append(0); // Jump destination, will be patched.
- append(p_variable);
}
void GDScriptByteCodeGenerator::write_endfor() {
// Jump back to loop check.
- append(GDScriptFunction::OPCODE_JUMP);
+ append(GDScriptFunction::OPCODE_JUMP, 0);
append(continue_addrs.back()->get());
continue_addrs.pop_back();
@@ -628,7 +1042,9 @@ void GDScriptByteCodeGenerator::write_endfor() {
}
current_breaks_to_patch.pop_back();
- current_stack_size -= 2; // Remove loop temporaries.
+ // Remove loop temporaries.
+ pop_temporary();
+ pop_temporary();
}
void GDScriptByteCodeGenerator::start_while_condition() {
@@ -638,7 +1054,7 @@ void GDScriptByteCodeGenerator::start_while_condition() {
void GDScriptByteCodeGenerator::write_while(const Address &p_condition) {
// Condition check.
- append(GDScriptFunction::OPCODE_JUMP_IF_NOT);
+ append(GDScriptFunction::OPCODE_JUMP_IF_NOT, 1);
append(p_condition);
while_jmp_addrs.push_back(opcodes.size());
append(0); // End of loop address, will be patched.
@@ -646,7 +1062,7 @@ void GDScriptByteCodeGenerator::write_while(const Address &p_condition) {
void GDScriptByteCodeGenerator::write_endwhile() {
// Jump back to loop check.
- append(GDScriptFunction::OPCODE_JUMP);
+ append(GDScriptFunction::OPCODE_JUMP, 0);
append(continue_addrs.back()->get());
continue_addrs.pop_back();
@@ -684,39 +1100,39 @@ void GDScriptByteCodeGenerator::end_match() {
}
void GDScriptByteCodeGenerator::write_break() {
- append(GDScriptFunction::OPCODE_JUMP);
+ append(GDScriptFunction::OPCODE_JUMP, 0);
current_breaks_to_patch.back()->get().push_back(opcodes.size());
append(0);
}
void GDScriptByteCodeGenerator::write_continue() {
- append(GDScriptFunction::OPCODE_JUMP);
+ append(GDScriptFunction::OPCODE_JUMP, 0);
append(continue_addrs.back()->get());
}
void GDScriptByteCodeGenerator::write_continue_match() {
- append(GDScriptFunction::OPCODE_JUMP);
+ append(GDScriptFunction::OPCODE_JUMP, 0);
match_continues_to_patch.back()->get().push_back(opcodes.size());
append(0);
}
void GDScriptByteCodeGenerator::write_breakpoint() {
- append(GDScriptFunction::OPCODE_BREAKPOINT);
+ append(GDScriptFunction::OPCODE_BREAKPOINT, 0);
}
void GDScriptByteCodeGenerator::write_newline(int p_line) {
- append(GDScriptFunction::OPCODE_LINE);
+ append(GDScriptFunction::OPCODE_LINE, 0);
append(p_line);
current_line = p_line;
}
void GDScriptByteCodeGenerator::write_return(const Address &p_return_value) {
- append(GDScriptFunction::OPCODE_RETURN);
+ append(GDScriptFunction::OPCODE_RETURN, 1);
append(p_return_value);
}
void GDScriptByteCodeGenerator::write_assert(const Address &p_test, const Address &p_message) {
- append(GDScriptFunction::OPCODE_ASSERT);
+ append(GDScriptFunction::OPCODE_ASSERT, 2);
append(p_test);
append(p_message);
}
diff --git a/modules/gdscript/gdscript_byte_codegen.h b/modules/gdscript/gdscript_byte_codegen.h
index 62438b6dd2..a546920fb2 100644
--- a/modules/gdscript/gdscript_byte_codegen.h
+++ b/modules/gdscript/gdscript_byte_codegen.h
@@ -33,6 +33,8 @@
#include "gdscript_codegen.h"
+#include "gdscript_function.h"
+
class GDScriptByteCodeGenerator : public GDScriptCodeGenerator {
bool ended = false;
GDScriptFunction *function = nullptr;
@@ -49,15 +51,26 @@ class GDScriptByteCodeGenerator : public GDScriptCodeGenerator {
int current_stack_size = 0;
int current_temporaries = 0;
+ int current_line = 0;
+ int stack_max = 0;
+ int instr_args_max = 0;
+ int ptrcall_max = 0;
HashMap<Variant, int, VariantHasher, VariantComparator> constant_map;
Map<StringName, int> name_map;
#ifdef TOOLS_ENABLED
Vector<StringName> named_globals;
#endif
- int current_line = 0;
- int stack_max = 0;
- int call_max = 0;
+ Map<Variant::ValidatedOperatorEvaluator, int> operator_func_map;
+ Map<Variant::ValidatedSetter, int> setters_map;
+ Map<Variant::ValidatedGetter, int> getters_map;
+ Map<Variant::ValidatedKeyedSetter, int> keyed_setters_map;
+ Map<Variant::ValidatedKeyedGetter, int> keyed_getters_map;
+ Map<Variant::ValidatedIndexedSetter, int> indexed_setters_map;
+ Map<Variant::ValidatedIndexedGetter, int> indexed_getters_map;
+ Map<Variant::ValidatedBuiltInMethod, int> builtin_method_map;
+ Map<Variant::ValidatedConstructor, int> constructors_map;
+ Map<MethodBind *, int> method_bind_map;
List<int> if_jmp_addrs; // List since this can be nested.
List<int> for_jmp_addrs;
@@ -134,22 +147,105 @@ class GDScriptByteCodeGenerator : public GDScriptCodeGenerator {
return pos;
}
+ int get_operation_pos(const Variant::ValidatedOperatorEvaluator p_operation) {
+ if (operator_func_map.has(p_operation))
+ return operator_func_map[p_operation];
+ int pos = operator_func_map.size();
+ operator_func_map[p_operation] = pos;
+ return pos;
+ }
+
+ int get_setter_pos(const Variant::ValidatedSetter p_setter) {
+ if (setters_map.has(p_setter))
+ return setters_map[p_setter];
+ int pos = setters_map.size();
+ setters_map[p_setter] = pos;
+ return pos;
+ }
+
+ int get_getter_pos(const Variant::ValidatedGetter p_getter) {
+ if (getters_map.has(p_getter))
+ return getters_map[p_getter];
+ int pos = getters_map.size();
+ getters_map[p_getter] = pos;
+ return pos;
+ }
+
+ int get_keyed_setter_pos(const Variant::ValidatedKeyedSetter p_keyed_setter) {
+ if (keyed_setters_map.has(p_keyed_setter))
+ return keyed_setters_map[p_keyed_setter];
+ int pos = keyed_setters_map.size();
+ keyed_setters_map[p_keyed_setter] = pos;
+ return pos;
+ }
+
+ int get_keyed_getter_pos(const Variant::ValidatedKeyedGetter p_keyed_getter) {
+ if (keyed_getters_map.has(p_keyed_getter))
+ return keyed_getters_map[p_keyed_getter];
+ int pos = keyed_getters_map.size();
+ keyed_getters_map[p_keyed_getter] = pos;
+ return pos;
+ }
+
+ int get_indexed_setter_pos(const Variant::ValidatedIndexedSetter p_indexed_setter) {
+ if (indexed_setters_map.has(p_indexed_setter))
+ return indexed_setters_map[p_indexed_setter];
+ int pos = indexed_setters_map.size();
+ indexed_setters_map[p_indexed_setter] = pos;
+ return pos;
+ }
+
+ int get_indexed_getter_pos(const Variant::ValidatedIndexedGetter p_indexed_getter) {
+ if (indexed_getters_map.has(p_indexed_getter))
+ return indexed_getters_map[p_indexed_getter];
+ int pos = indexed_getters_map.size();
+ indexed_getters_map[p_indexed_getter] = pos;
+ return pos;
+ }
+
+ int get_builtin_method_pos(const Variant::ValidatedBuiltInMethod p_method) {
+ if (builtin_method_map.has(p_method)) {
+ return builtin_method_map[p_method];
+ }
+ int pos = builtin_method_map.size();
+ builtin_method_map[p_method] = pos;
+ return pos;
+ }
+
+ int get_constructor_pos(const Variant::ValidatedConstructor p_constructor) {
+ if (constructors_map.has(p_constructor)) {
+ return constructors_map[p_constructor];
+ }
+ int pos = constructors_map.size();
+ constructors_map[p_constructor] = pos;
+ return pos;
+ }
+
+ int get_method_bind_pos(MethodBind *p_method) {
+ if (method_bind_map.has(p_method)) {
+ return method_bind_map[p_method];
+ }
+ int pos = method_bind_map.size();
+ method_bind_map[p_method] = pos;
+ return pos;
+ }
+
void alloc_stack(int p_level) {
if (p_level >= stack_max)
stack_max = p_level + 1;
}
- void alloc_call(int p_params) {
- if (p_params >= call_max)
- call_max = p_params;
- }
-
int increase_stack() {
int top = current_stack_size++;
alloc_stack(current_stack_size);
return top;
}
+ void alloc_ptrcall(int p_params) {
+ if (p_params >= ptrcall_max)
+ ptrcall_max = p_params;
+ }
+
int address_of(const Address &p_address) {
switch (p_address.mode) {
case Address::SELF:
@@ -177,8 +273,13 @@ class GDScriptByteCodeGenerator : public GDScriptCodeGenerator {
return -1; // Unreachable.
}
- void append(int code) {
- opcodes.push_back(code);
+ void append(GDScriptFunction::Opcode p_code, int p_argument_count) {
+ opcodes.push_back((p_code & GDScriptFunction::INSTR_MASK) | (p_argument_count << GDScriptFunction::INSTR_BITS));
+ instr_args_max = MAX(instr_args_max, p_argument_count);
+ }
+
+ void append(int p_code) {
+ opcodes.push_back(p_code);
}
void append(const Address &p_address) {
@@ -189,6 +290,46 @@ class GDScriptByteCodeGenerator : public GDScriptCodeGenerator {
opcodes.push_back(get_name_map_pos(p_name));
}
+ void append(const Variant::ValidatedOperatorEvaluator p_operation) {
+ opcodes.push_back(get_operation_pos(p_operation));
+ }
+
+ void append(const Variant::ValidatedSetter p_setter) {
+ opcodes.push_back(get_setter_pos(p_setter));
+ }
+
+ void append(const Variant::ValidatedGetter p_getter) {
+ opcodes.push_back(get_getter_pos(p_getter));
+ }
+
+ void append(const Variant::ValidatedKeyedSetter p_keyed_setter) {
+ opcodes.push_back(get_keyed_setter_pos(p_keyed_setter));
+ }
+
+ void append(const Variant::ValidatedKeyedGetter p_keyed_getter) {
+ opcodes.push_back(get_keyed_getter_pos(p_keyed_getter));
+ }
+
+ void append(const Variant::ValidatedIndexedSetter p_indexed_setter) {
+ opcodes.push_back(get_indexed_setter_pos(p_indexed_setter));
+ }
+
+ void append(const Variant::ValidatedIndexedGetter p_indexed_getter) {
+ opcodes.push_back(get_indexed_getter_pos(p_indexed_getter));
+ }
+
+ void append(const Variant::ValidatedBuiltInMethod p_method) {
+ opcodes.push_back(get_builtin_method_pos(p_method));
+ }
+
+ void append(const Variant::ValidatedConstructor p_constructor) {
+ opcodes.push_back(get_constructor_pos(p_constructor));
+ }
+
+ void append(MethodBind *p_method) {
+ opcodes.push_back(get_method_bind_pos(p_method));
+ }
+
void patch_jump(int p_address) {
opcodes.write[p_address] = opcodes.size();
}
@@ -244,8 +385,9 @@ public:
virtual void write_super_call(const Address &p_target, const StringName &p_function_name, const Vector<Address> &p_arguments) override;
virtual void write_call_async(const Address &p_target, const Address &p_base, const StringName &p_function_name, const Vector<Address> &p_arguments) override;
virtual void write_call_builtin(const Address &p_target, GDScriptFunctions::Function p_function, const Vector<Address> &p_arguments) override;
- virtual void write_call_method_bind(const Address &p_target, const Address &p_base, const MethodBind *p_method, const Vector<Address> &p_arguments) override;
- virtual void write_call_ptrcall(const Address &p_target, const Address &p_base, const MethodBind *p_method, const Vector<Address> &p_arguments) override;
+ virtual void write_call_builtin_type(const Address &p_target, const Address &p_base, Variant::Type p_type, const StringName &p_method, const Vector<Address> &p_arguments) override;
+ virtual void write_call_method_bind(const Address &p_target, const Address &p_base, MethodBind *p_method, const Vector<Address> &p_arguments) override;
+ virtual void write_call_ptrcall(const Address &p_target, const Address &p_base, MethodBind *p_method, const Vector<Address> &p_arguments) override;
virtual void write_call_self(const Address &p_target, const StringName &p_function_name, const Vector<Address> &p_arguments) override;
virtual void write_call_script_function(const Address &p_target, const Address &p_base, const StringName &p_function_name, const Vector<Address> &p_arguments) override;
virtual void write_construct(const Address &p_target, Variant::Type p_type, const Vector<Address> &p_arguments) override;
diff --git a/modules/gdscript/gdscript_cache.cpp b/modules/gdscript/gdscript_cache.cpp
index 57b95f5b21..95d24a8b08 100644
--- a/modules/gdscript/gdscript_cache.cpp
+++ b/modules/gdscript/gdscript_cache.cpp
@@ -31,7 +31,7 @@
#include "gdscript_cache.h"
#include "core/os/file_access.h"
-#include "core/vector.h"
+#include "core/templates/vector.h"
#include "gdscript.h"
#include "gdscript_analyzer.h"
#include "gdscript_parser.h"
diff --git a/modules/gdscript/gdscript_cache.h b/modules/gdscript/gdscript_cache.h
index 865df34051..90c5884985 100644
--- a/modules/gdscript/gdscript_cache.h
+++ b/modules/gdscript/gdscript_cache.h
@@ -31,10 +31,10 @@
#ifndef GDSCRIPT_CACHE_H
#define GDSCRIPT_CACHE_H
-#include "core/hash_map.h"
+#include "core/object/reference.h"
#include "core/os/mutex.h"
-#include "core/reference.h"
-#include "core/set.h"
+#include "core/templates/hash_map.h"
+#include "core/templates/set.h"
#include "gdscript.h"
class GDScriptAnalyzer;
diff --git a/modules/gdscript/gdscript_codegen.h b/modules/gdscript/gdscript_codegen.h
index 31e1e6ba23..2616a34719 100644
--- a/modules/gdscript/gdscript_codegen.h
+++ b/modules/gdscript/gdscript_codegen.h
@@ -32,8 +32,8 @@
#define GDSCRIPT_CODEGEN
#include "core/io/multiplayer_api.h"
-#include "core/string_name.h"
-#include "core/variant.h"
+#include "core/string/string_name.h"
+#include "core/variant/variant.h"
#include "gdscript_function.h"
#include "gdscript_functions.h"
@@ -126,8 +126,9 @@ public:
virtual void write_super_call(const Address &p_target, const StringName &p_function_name, const Vector<Address> &p_arguments) = 0;
virtual void write_call_async(const Address &p_target, const Address &p_base, const StringName &p_function_name, const Vector<Address> &p_arguments) = 0;
virtual void write_call_builtin(const Address &p_target, GDScriptFunctions::Function p_function, const Vector<Address> &p_arguments) = 0;
- virtual void write_call_method_bind(const Address &p_target, const Address &p_base, const MethodBind *p_method, const Vector<Address> &p_arguments) = 0;
- virtual void write_call_ptrcall(const Address &p_target, const Address &p_base, const MethodBind *p_method, const Vector<Address> &p_arguments) = 0;
+ virtual void write_call_builtin_type(const Address &p_target, const Address &p_base, Variant::Type p_type, const StringName &p_method, const Vector<Address> &p_arguments) = 0;
+ virtual void write_call_method_bind(const Address &p_target, const Address &p_base, MethodBind *p_method, const Vector<Address> &p_arguments) = 0;
+ virtual void write_call_ptrcall(const Address &p_target, const Address &p_base, MethodBind *p_method, const Vector<Address> &p_arguments) = 0;
virtual void write_call_self(const Address &p_target, const StringName &p_function_name, const Vector<Address> &p_arguments) = 0;
virtual void write_call_script_function(const Address &p_target, const Address &p_base, const StringName &p_function_name, const Vector<Address> &p_arguments) = 0;
virtual void write_construct(const Address &p_target, Variant::Type p_type, const Vector<Address> &p_arguments) = 0;
diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp
index bad450c9f9..69fe6d27cc 100644
--- a/modules/gdscript/gdscript_compiler.cpp
+++ b/modules/gdscript/gdscript_compiler.cpp
@@ -158,6 +158,48 @@ GDScriptDataType GDScriptCompiler::_gdtype_from_datatype(const GDScriptParser::D
return result;
}
+static bool _is_exact_type(const PropertyInfo &p_par_type, const GDScriptDataType &p_arg_type) {
+ if (!p_arg_type.has_type) {
+ return false;
+ }
+ if (p_par_type.type == Variant::NIL) {
+ return false;
+ }
+ if (p_par_type.type == Variant::OBJECT) {
+ if (p_arg_type.kind == GDScriptDataType::BUILTIN) {
+ return false;
+ }
+ StringName class_name;
+ if (p_arg_type.kind == GDScriptDataType::NATIVE) {
+ class_name = p_arg_type.native_type;
+ } else {
+ class_name = p_arg_type.native_type == StringName() ? p_arg_type.script_type->get_instance_base_type() : p_arg_type.native_type;
+ }
+ return p_par_type.class_name == class_name || ClassDB::is_parent_class(class_name, p_par_type.class_name);
+ } else {
+ if (p_arg_type.kind != GDScriptDataType::BUILTIN) {
+ return false;
+ }
+ return p_par_type.type == p_arg_type.builtin_type;
+ }
+}
+
+static bool _have_exact_arguments(const MethodBind *p_method, const Vector<GDScriptCodeGenerator::Address> &p_arguments) {
+ if (p_method->get_argument_count() != p_arguments.size()) {
+ // ptrcall won't work with default arguments.
+ return false;
+ }
+ MethodInfo info;
+ ClassDB::get_method_info(p_method->get_instance_class(), p_method->get_name(), &info);
+ for (int i = 0; i < p_arguments.size(); i++) {
+ const PropertyInfo &prop = info.arguments[i];
+ if (!_is_exact_type(prop, p_arguments[i].type)) {
+ return false;
+ }
+ }
+ return true;
+}
+
GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &codegen, Error &r_error, const GDScriptParser::ExpressionNode *p_expression, bool p_root, bool p_initializer, const GDScriptCodeGenerator::Address &p_index_addr) {
if (p_expression->is_constant) {
return codegen.add_constant(p_expression->reduced_value);
@@ -430,7 +472,20 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
} else {
if (callee->type == GDScriptParser::Node::IDENTIFIER) {
// Self function call.
- if ((codegen.function_node && codegen.function_node->is_static) || call->function_name == "new") {
+ if (ClassDB::has_method(codegen.script->native->get_name(), call->function_name)) {
+ // Native method, use faster path.
+ GDScriptCodeGenerator::Address self;
+ self.mode = GDScriptCodeGenerator::Address::SELF;
+ MethodBind *method = ClassDB::get_method(codegen.script->native->get_name(), call->function_name);
+
+ if (_have_exact_arguments(method, arguments)) {
+ // Exact arguments, use ptrcall.
+ gen->write_call_ptrcall(result, self, method, arguments);
+ } else {
+ // Not exact arguments, but still can use method bind call.
+ gen->write_call_method_bind(result, self, method, arguments);
+ }
+ } else if ((codegen.function_node && codegen.function_node->is_static) || call->function_name == "new") {
GDScriptCodeGenerator::Address self;
self.mode = GDScriptCodeGenerator::Address::CLASS;
gen->write_call(result, self, call->function_name, arguments);
@@ -447,6 +502,28 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
}
if (within_await) {
gen->write_call_async(result, base, call->function_name, arguments);
+ } else if (base.type.has_type && base.type.kind != GDScriptDataType::BUILTIN) {
+ // Native method, use faster path.
+ StringName class_name;
+ if (base.type.kind == GDScriptDataType::NATIVE) {
+ class_name = base.type.native_type;
+ } else {
+ class_name = base.type.native_type == StringName() ? base.type.script_type->get_instance_base_type() : base.type.native_type;
+ }
+ if (ClassDB::class_exists(class_name) && ClassDB::has_method(class_name, call->function_name)) {
+ MethodBind *method = ClassDB::get_method(class_name, call->function_name);
+ if (_have_exact_arguments(method, arguments)) {
+ // Exact arguments, use ptrcall.
+ gen->write_call_ptrcall(result, base, method, arguments);
+ } else {
+ // Not exact arguments, but still can use method bind call.
+ gen->write_call_method_bind(result, base, method, arguments);
+ }
+ } else {
+ gen->write_call(result, base, call->function_name, arguments);
+ }
+ } else if (base.type.has_type && base.type.kind == GDScriptDataType::BUILTIN) {
+ gen->write_call_builtin_type(result, base, base.type.builtin_type, call->function_name, arguments);
} else {
gen->write_call(result, base, call->function_name, arguments);
}
@@ -493,7 +570,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
GDScriptCodeGenerator::Address result = codegen.add_temporary(_gdtype_from_datatype(get_node->get_datatype()));
MethodBind *get_node_method = ClassDB::get_method("Node", "get_node");
- gen->write_call_method_bind(result, GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::SELF), get_node_method, args);
+ gen->write_call_ptrcall(result, GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::SELF), get_node_method, args);
return result;
} break;
@@ -1963,6 +2040,8 @@ Error GDScriptCompiler::_parse_class_level(GDScript *p_script, const GDScriptPar
}
p_script->member_indices = base->member_indices;
+ native = base->native;
+ p_script->native = native;
} break;
default: {
_set_error("Parser bug: invalid inheritance.", p_class);
diff --git a/modules/gdscript/gdscript_compiler.h b/modules/gdscript/gdscript_compiler.h
index db02079d26..157c801f56 100644
--- a/modules/gdscript/gdscript_compiler.h
+++ b/modules/gdscript/gdscript_compiler.h
@@ -31,7 +31,7 @@
#ifndef GDSCRIPT_COMPILER_H
#define GDSCRIPT_COMPILER_H
-#include "core/set.h"
+#include "core/templates/set.h"
#include "gdscript.h"
#include "gdscript_codegen.h"
#include "gdscript_function.h"
@@ -51,12 +51,11 @@ class GDScriptCompiler {
GDScriptCodeGenerator *generator = nullptr;
Map<StringName, GDScriptCodeGenerator::Address> parameters;
Map<StringName, GDScriptCodeGenerator::Address> locals;
- List<Set<StringName>> locals_in_scope;
+ List<Map<StringName, GDScriptCodeGenerator::Address>> locals_stack;
GDScriptCodeGenerator::Address add_local(const StringName &p_name, const GDScriptDataType &p_type) {
uint32_t addr = generator->add_local(p_name, p_type);
locals[p_name] = GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::LOCAL_VARIABLE, addr, p_type);
- locals_in_scope.back()->get().insert(p_name);
return locals[p_name];
}
@@ -102,17 +101,14 @@ class GDScriptCompiler {
}
void start_block() {
- Set<StringName> scope;
- locals_in_scope.push_back(scope);
+ Map<StringName, GDScriptCodeGenerator::Address> old_locals = locals;
+ locals_stack.push_back(old_locals);
generator->start_block();
}
void end_block() {
- Set<StringName> &scope = locals_in_scope.back()->get();
- for (Set<StringName>::Element *E = scope.front(); E; E = E->next()) {
- locals.erase(E->get());
- }
- locals_in_scope.pop_back();
+ locals = locals_stack.back()->get();
+ locals_stack.pop_back();
generator->end_block();
}
};
diff --git a/modules/gdscript/gdscript_disassembler.cpp b/modules/gdscript/gdscript_disassembler.cpp
new file mode 100644
index 0000000000..c918251772
--- /dev/null
+++ b/modules/gdscript/gdscript_disassembler.cpp
@@ -0,0 +1,813 @@
+/*************************************************************************/
+/* gdscript_disassembler.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifdef DEBUG_ENABLED
+
+#include "gdscript_function.h"
+
+#include "core/string/string_builder.h"
+#include "gdscript.h"
+#include "gdscript_functions.h"
+
+static String _get_variant_string(const Variant &p_variant) {
+ String txt;
+ if (p_variant.get_type() == Variant::STRING) {
+ txt = "\"" + String(p_variant) + "\"";
+ } else if (p_variant.get_type() == Variant::STRING_NAME) {
+ txt = "&\"" + String(p_variant) + "\"";
+ } else if (p_variant.get_type() == Variant::NODE_PATH) {
+ txt = "^\"" + String(p_variant) + "\"";
+ } else if (p_variant.get_type() == Variant::OBJECT) {
+ Object *obj = p_variant;
+ if (!obj) {
+ txt = "null";
+ } else {
+ GDScriptNativeClass *cls = Object::cast_to<GDScriptNativeClass>(obj);
+ if (cls) {
+ txt += cls->get_name();
+ txt += " (class)";
+ } else {
+ txt = obj->get_class();
+ if (obj->get_script_instance()) {
+ txt += "(" + obj->get_script_instance()->get_script()->get_path() + ")";
+ }
+ }
+ }
+ } else {
+ txt = p_variant;
+ }
+ return txt;
+}
+
+static String _disassemble_address(const GDScript *p_script, const GDScriptFunction &p_function, int p_address) {
+ int addr = p_address & GDScriptFunction::ADDR_MASK;
+
+ switch (p_address >> GDScriptFunction::ADDR_BITS) {
+ case GDScriptFunction::ADDR_TYPE_SELF: {
+ return "self";
+ } break;
+ case GDScriptFunction::ADDR_TYPE_CLASS: {
+ return "class";
+ } break;
+ case GDScriptFunction::ADDR_TYPE_MEMBER: {
+ return "member(" + p_script->debug_get_member_by_index(addr) + ")";
+ } break;
+ case GDScriptFunction::ADDR_TYPE_CLASS_CONSTANT: {
+ return "class_const(" + p_function.get_global_name(addr) + ")";
+ } break;
+ case GDScriptFunction::ADDR_TYPE_LOCAL_CONSTANT: {
+ return "const(" + _get_variant_string(p_function.get_constant(addr)) + ")";
+ } break;
+ case GDScriptFunction::ADDR_TYPE_STACK: {
+ return "stack(" + itos(addr) + ")";
+ } break;
+ case GDScriptFunction::ADDR_TYPE_STACK_VARIABLE: {
+ return "var_stack(" + itos(addr) + ")";
+ } break;
+ case GDScriptFunction::ADDR_TYPE_GLOBAL: {
+ return "global(" + _get_variant_string(GDScriptLanguage::get_singleton()->get_global_array()[addr]) + ")";
+ } break;
+ case GDScriptFunction::ADDR_TYPE_NAMED_GLOBAL: {
+ return "named_global(" + p_function.get_global_name(addr) + ")";
+ } break;
+ case GDScriptFunction::ADDR_TYPE_NIL: {
+ return "nil";
+ } break;
+ }
+
+ return "<err>";
+}
+
+void GDScriptFunction::disassemble(const Vector<String> &p_code_lines) const {
+#define DADDR(m_ip) (_disassemble_address(_script, *this, _code_ptr[ip + m_ip]))
+
+ for (int ip = 0; ip < _code_size;) {
+ StringBuilder text;
+ int incr = 0;
+
+ text += " ";
+ text += itos(ip);
+ text += ": ";
+
+ // This makes the compiler complain if some opcode is unchecked in the switch.
+ Opcode code = Opcode(_code_ptr[ip] & INSTR_MASK);
+ int instr_var_args = (_code_ptr[ip] & INSTR_ARGS_MASK) >> INSTR_BITS;
+
+ switch (code) {
+ case OPCODE_OPERATOR: {
+ int operation = _code_ptr[ip + 4];
+
+ text += "operator ";
+
+ text += DADDR(3);
+ text += " = ";
+ text += DADDR(1);
+ text += " ";
+ text += Variant::get_operator_name(Variant::Operator(operation));
+ text += " ";
+ text += DADDR(2);
+
+ incr += 5;
+ } break;
+ case OPCODE_OPERATOR_VALIDATED: {
+ text += "validated operator ";
+
+ text += DADDR(3);
+ text += " = ";
+ text += DADDR(1);
+ text += " <operator function> ";
+ text += DADDR(2);
+
+ incr += 5;
+ } break;
+ case OPCODE_EXTENDS_TEST: {
+ text += "is object ";
+ text += DADDR(3);
+ text += " = ";
+ text += DADDR(1);
+ text += " is ";
+ text += DADDR(2);
+
+ incr += 4;
+ } break;
+ case OPCODE_IS_BUILTIN: {
+ text += "is builtin ";
+ text += DADDR(2);
+ text += " = ";
+ text += DADDR(1);
+ text += " is ";
+ text += Variant::get_type_name(Variant::Type(_code_ptr[ip + 3]));
+
+ incr += 4;
+ } break;
+ case OPCODE_SET_KEYED: {
+ text += "set keyed ";
+ text += DADDR(1);
+ text += "[";
+ text += DADDR(2);
+ text += "] = ";
+ text += DADDR(3);
+
+ incr += 4;
+ } break;
+ case OPCODE_SET_KEYED_VALIDATED: {
+ text += "set keyed validated ";
+ text += DADDR(1);
+ text += "[";
+ text += DADDR(2);
+ text += "] = ";
+ text += DADDR(3);
+
+ incr += 5;
+ } break;
+ case OPCODE_SET_INDEXED_VALIDATED: {
+ text += "set indexed validated ";
+ text += DADDR(1);
+ text += "[";
+ text += DADDR(2);
+ text += "] = ";
+ text += DADDR(3);
+
+ incr += 5;
+ } break;
+ case OPCODE_GET_KEYED: {
+ text += "get keyed ";
+ text += DADDR(3);
+ text += " = ";
+ text += DADDR(1);
+ text += "[";
+ text += DADDR(2);
+ text += "]";
+
+ incr += 4;
+ } break;
+ case OPCODE_GET_KEYED_VALIDATED: {
+ text += "get keyed validated ";
+ text += DADDR(3);
+ text += " = ";
+ text += DADDR(1);
+ text += "[";
+ text += DADDR(2);
+ text += "]";
+
+ incr += 5;
+ } break;
+ case OPCODE_GET_INDEXED_VALIDATED: {
+ text += "get indexed validated ";
+ text += DADDR(3);
+ text += " = ";
+ text += DADDR(1);
+ text += "[";
+ text += DADDR(2);
+ text += "]";
+
+ incr += 5;
+ } break;
+ case OPCODE_SET_NAMED: {
+ text += "set_named ";
+ text += DADDR(1);
+ text += "[\"";
+ text += _global_names_ptr[_code_ptr[ip + 3]];
+ text += "\"] = ";
+ text += DADDR(2);
+
+ incr += 4;
+ } break;
+ case OPCODE_SET_NAMED_VALIDATED: {
+ text += "set_named validated ";
+ text += DADDR(1);
+ text += "[\"";
+ text += "<unknown name>";
+ text += "\"] = ";
+ text += DADDR(2);
+
+ incr += 4;
+ } break;
+ case OPCODE_GET_NAMED: {
+ text += "get_named ";
+ text += DADDR(2);
+ text += " = ";
+ text += DADDR(1);
+ text += "[\"";
+ text += _global_names_ptr[_code_ptr[ip + 3]];
+ text += "\"]";
+
+ incr += 4;
+ } break;
+ case OPCODE_GET_NAMED_VALIDATED: {
+ text += "get_named validated ";
+ text += DADDR(2);
+ text += " = ";
+ text += DADDR(1);
+ text += "[\"";
+ text += "<unknown name>";
+ text += "\"]";
+
+ incr += 4;
+ } break;
+ case OPCODE_SET_MEMBER: {
+ text += "set_member ";
+ text += "[\"";
+ text += _global_names_ptr[_code_ptr[ip + 2]];
+ text += "\"] = ";
+ text += DADDR(1);
+
+ incr += 3;
+ } break;
+ case OPCODE_GET_MEMBER: {
+ text += "get_member ";
+ text += DADDR(1);
+ text += " = ";
+ text += "[\"";
+ text += _global_names_ptr[_code_ptr[ip + 2]];
+ text += "\"]";
+
+ incr += 3;
+ } break;
+ case OPCODE_ASSIGN: {
+ text += "assign ";
+ text += DADDR(1);
+ text += " = ";
+ text += DADDR(2);
+
+ incr += 3;
+ } break;
+ case OPCODE_ASSIGN_TRUE: {
+ text += "assign ";
+ text += DADDR(1);
+ text += " = true";
+
+ incr += 2;
+ } break;
+ case OPCODE_ASSIGN_FALSE: {
+ text += "assign ";
+ text += DADDR(1);
+ text += " = false";
+
+ incr += 2;
+ } break;
+ case OPCODE_ASSIGN_TYPED_BUILTIN: {
+ text += "assign typed builtin (";
+ text += Variant::get_type_name((Variant::Type)_code_ptr[ip + 3]);
+ text += ") ";
+ text += DADDR(1);
+ text += " = ";
+ text += DADDR(2);
+
+ incr += 4;
+ } break;
+ case OPCODE_ASSIGN_TYPED_NATIVE: {
+ Variant class_name = _constants_ptr[_code_ptr[ip + 3]];
+ GDScriptNativeClass *nc = Object::cast_to<GDScriptNativeClass>(class_name.operator Object *());
+
+ text += "assign typed native (";
+ text += nc->get_name().operator String();
+ text += ") ";
+ text += DADDR(1);
+ text += " = ";
+ text += DADDR(2);
+
+ incr += 4;
+ } break;
+ case OPCODE_ASSIGN_TYPED_SCRIPT: {
+ Variant script = _constants_ptr[_code_ptr[ip + 3]];
+ Script *sc = Object::cast_to<Script>(script.operator Object *());
+
+ text += "assign typed script (";
+ text += sc->get_path();
+ text += ") ";
+ text += DADDR(1);
+ text += " = ";
+ text += DADDR(2);
+
+ incr += 4;
+ } break;
+ case OPCODE_CAST_TO_BUILTIN: {
+ text += "cast builtin ";
+ text += DADDR(2);
+ text += " = ";
+ text += DADDR(1);
+ text += " as ";
+ text += Variant::get_type_name(Variant::Type(_code_ptr[ip + 1]));
+
+ incr += 4;
+ } break;
+ case OPCODE_CAST_TO_NATIVE: {
+ Variant class_name = _constants_ptr[_code_ptr[ip + 1]];
+ GDScriptNativeClass *nc = Object::cast_to<GDScriptNativeClass>(class_name.operator Object *());
+
+ text += "cast native ";
+ text += DADDR(2);
+ text += " = ";
+ text += DADDR(1);
+ text += " as ";
+ text += nc->get_name();
+
+ incr += 4;
+ } break;
+ case OPCODE_CAST_TO_SCRIPT: {
+ text += "cast ";
+ text += DADDR(2);
+ text += " = ";
+ text += DADDR(1);
+ text += " as ";
+ text += DADDR(3);
+
+ incr += 4;
+ } break;
+ case OPCODE_CONSTRUCT: {
+ Variant::Type t = Variant::Type(_code_ptr[ip + 3 + instr_var_args]);
+ int argc = _code_ptr[ip + 1 + instr_var_args];
+
+ text += "construct ";
+ text += DADDR(1 + argc);
+ text += " = ";
+
+ text += Variant::get_type_name(t) + "(";
+ for (int i = 0; i < argc; i++) {
+ if (i > 0)
+ text += ", ";
+ text += DADDR(i + 1);
+ }
+ text += ")";
+
+ incr = 3 + instr_var_args;
+ } break;
+ case OPCODE_CONSTRUCT_VALIDATED: {
+ int argc = _code_ptr[ip + 1 + instr_var_args];
+
+ text += "construct validated ";
+ text += DADDR(1 + argc);
+ text += " = ";
+
+ text += "<unkown type>(";
+ for (int i = 0; i < argc; i++) {
+ if (i > 0)
+ text += ", ";
+ text += DADDR(i + 1);
+ }
+ text += ")";
+
+ incr = 3 + instr_var_args;
+ } break;
+ case OPCODE_CONSTRUCT_ARRAY: {
+ int argc = _code_ptr[ip + 1 + instr_var_args];
+ text += " make_array ";
+ text += DADDR(1 + argc);
+ text += " = [";
+
+ for (int i = 0; i < argc; i++) {
+ if (i > 0)
+ text += ", ";
+ text += DADDR(1 + i);
+ }
+
+ text += "]";
+
+ incr += 3 + argc;
+ } break;
+ case OPCODE_CONSTRUCT_DICTIONARY: {
+ int argc = _code_ptr[ip + 1 + instr_var_args];
+ text += "make_dict ";
+ text += DADDR(1 + argc * 2);
+ text += " = {";
+
+ for (int i = 0; i < argc; i++) {
+ if (i > 0)
+ text += ", ";
+ text += DADDR(1 + i * 2 + 0);
+ text += ": ";
+ text += DADDR(1 + i * 2 + 1);
+ }
+
+ text += "}";
+
+ incr += 3 + argc * 2;
+ } break;
+ case OPCODE_CALL:
+ case OPCODE_CALL_RETURN:
+ case OPCODE_CALL_ASYNC: {
+ bool ret = (_code_ptr[ip] & INSTR_MASK) == OPCODE_CALL_RETURN;
+ bool async = (_code_ptr[ip] & INSTR_MASK) == OPCODE_CALL_ASYNC;
+
+ if (ret) {
+ text += "call-ret ";
+ } else if (async) {
+ text += "call-async ";
+ } else {
+ text += "call ";
+ }
+
+ int argc = _code_ptr[ip + 1 + instr_var_args];
+ if (ret || async) {
+ text += DADDR(2 + argc) + " = ";
+ }
+
+ text += DADDR(1 + argc) + ".";
+ text += String(_global_names_ptr[_code_ptr[ip + 2 + instr_var_args]]);
+ text += "(";
+
+ for (int i = 0; i < argc; i++) {
+ if (i > 0)
+ text += ", ";
+ text += DADDR(1 + i);
+ }
+ text += ")";
+
+ incr = 5 + argc;
+ } break;
+ case OPCODE_CALL_METHOD_BIND:
+ case OPCODE_CALL_METHOD_BIND_RET: {
+ bool ret = (_code_ptr[ip] & INSTR_MASK) == OPCODE_CALL_METHOD_BIND_RET;
+
+ if (ret) {
+ text += "call-method_bind-ret ";
+ } else {
+ text += "call-method_bind ";
+ }
+
+ MethodBind *method = _methods_ptr[_code_ptr[ip + 2 + instr_var_args]];
+
+ int argc = _code_ptr[ip + 1 + instr_var_args];
+ if (ret) {
+ text += DADDR(2 + argc) + " = ";
+ }
+
+ text += DADDR(1 + argc) + ".";
+ text += method->get_name();
+ text += "(";
+
+ for (int i = 0; i < argc; i++) {
+ if (i > 0)
+ text += ", ";
+ text += DADDR(1 + i);
+ }
+ text += ")";
+
+ incr = 5 + argc;
+ } break;
+ case OPCODE_CALL_PTRCALL_NO_RETURN: {
+ text += "call-ptrcall (no return) ";
+
+ MethodBind *method = _methods_ptr[_code_ptr[ip + 2 + instr_var_args]];
+
+ int argc = _code_ptr[ip + 1 + instr_var_args];
+
+ text += DADDR(1 + argc) + ".";
+ text += method->get_name();
+ text += "(";
+
+ for (int i = 0; i < argc; i++) {
+ if (i > 0)
+ text += ", ";
+ text += DADDR(1 + i);
+ }
+ text += ")";
+
+ incr = 5 + argc;
+ } break;
+
+#define DISASSEMBLE_PTRCALL(m_type) \
+ case OPCODE_CALL_PTRCALL_##m_type: { \
+ text += "call-ptrcall (return "; \
+ text += #m_type; \
+ text += ") "; \
+ MethodBind *method = _methods_ptr[_code_ptr[ip + 2 + instr_var_args]]; \
+ int argc = _code_ptr[ip + 1 + instr_var_args]; \
+ text += DADDR(2 + argc) + " = "; \
+ text += DADDR(1 + argc) + "."; \
+ text += method->get_name(); \
+ text += "("; \
+ for (int i = 0; i < argc; i++) { \
+ if (i > 0) \
+ text += ", "; \
+ text += DADDR(1 + i); \
+ } \
+ text += ")"; \
+ incr = 5 + argc; \
+ } break
+
+ DISASSEMBLE_PTRCALL(BOOL);
+ DISASSEMBLE_PTRCALL(INT);
+ DISASSEMBLE_PTRCALL(FLOAT);
+ DISASSEMBLE_PTRCALL(STRING);
+ DISASSEMBLE_PTRCALL(VECTOR2);
+ DISASSEMBLE_PTRCALL(VECTOR2I);
+ DISASSEMBLE_PTRCALL(RECT2);
+ DISASSEMBLE_PTRCALL(RECT2I);
+ DISASSEMBLE_PTRCALL(VECTOR3);
+ DISASSEMBLE_PTRCALL(VECTOR3I);
+ DISASSEMBLE_PTRCALL(TRANSFORM2D);
+ DISASSEMBLE_PTRCALL(PLANE);
+ DISASSEMBLE_PTRCALL(AABB);
+ DISASSEMBLE_PTRCALL(BASIS);
+ DISASSEMBLE_PTRCALL(TRANSFORM);
+ DISASSEMBLE_PTRCALL(COLOR);
+ DISASSEMBLE_PTRCALL(STRING_NAME);
+ DISASSEMBLE_PTRCALL(NODE_PATH);
+ DISASSEMBLE_PTRCALL(RID);
+ DISASSEMBLE_PTRCALL(QUAT);
+ DISASSEMBLE_PTRCALL(OBJECT);
+ DISASSEMBLE_PTRCALL(CALLABLE);
+ DISASSEMBLE_PTRCALL(SIGNAL);
+ DISASSEMBLE_PTRCALL(DICTIONARY);
+ DISASSEMBLE_PTRCALL(ARRAY);
+ DISASSEMBLE_PTRCALL(PACKED_BYTE_ARRAY);
+ DISASSEMBLE_PTRCALL(PACKED_INT32_ARRAY);
+ DISASSEMBLE_PTRCALL(PACKED_INT64_ARRAY);
+ DISASSEMBLE_PTRCALL(PACKED_FLOAT32_ARRAY);
+ DISASSEMBLE_PTRCALL(PACKED_FLOAT64_ARRAY);
+ DISASSEMBLE_PTRCALL(PACKED_STRING_ARRAY);
+ DISASSEMBLE_PTRCALL(PACKED_VECTOR2_ARRAY);
+ DISASSEMBLE_PTRCALL(PACKED_VECTOR3_ARRAY);
+ DISASSEMBLE_PTRCALL(PACKED_COLOR_ARRAY);
+
+ case OPCODE_CALL_BUILTIN_TYPE_VALIDATED: {
+ int argc = _code_ptr[ip + 1 + instr_var_args];
+
+ text += "call-builtin-method validated ";
+
+ text += DADDR(2 + argc) + " = ";
+
+ text += DADDR(1) + ".";
+ text += "<unknown method>";
+
+ text += "(";
+
+ for (int i = 0; i < argc; i++) {
+ if (i > 0)
+ text += ", ";
+ text += DADDR(1 + i);
+ }
+ text += ")";
+
+ incr = 5 + argc;
+ } break;
+ case OPCODE_CALL_BUILT_IN: {
+ text += "call-built-in ";
+
+ int argc = _code_ptr[ip + 1 + instr_var_args];
+ text += DADDR(1 + argc) + " = ";
+
+ text += GDScriptFunctions::get_func_name(GDScriptFunctions::Function(_code_ptr[ip + 2 + instr_var_args]));
+ text += "(";
+
+ for (int i = 0; i < argc; i++) {
+ if (i > 0)
+ text += ", ";
+ text += DADDR(1 + i);
+ }
+ text += ")";
+
+ incr = 4 + argc;
+ } break;
+ case OPCODE_CALL_SELF_BASE: {
+ text += "call-self-base ";
+
+ int argc = _code_ptr[ip + 1 + instr_var_args];
+ text += DADDR(2 + argc) + " = ";
+
+ text += _global_names_ptr[_code_ptr[ip + 2 + instr_var_args]];
+ text += "(";
+
+ for (int i = 0; i < argc; i++) {
+ if (i > 0)
+ text += ", ";
+ text += DADDR(1 + i);
+ }
+ text += ")";
+
+ incr = 4 + argc;
+ } break;
+ case OPCODE_AWAIT: {
+ text += "await ";
+ text += DADDR(1);
+
+ incr += 2;
+ } break;
+ case OPCODE_AWAIT_RESUME: {
+ text += "await resume ";
+ text += DADDR(1);
+
+ incr = 2;
+ } break;
+ case OPCODE_JUMP: {
+ text += "jump ";
+ text += itos(_code_ptr[ip + 1]);
+
+ incr = 2;
+ } break;
+ case OPCODE_JUMP_IF: {
+ text += "jump-if ";
+ text += DADDR(1);
+ text += " to ";
+ text += itos(_code_ptr[ip + 2]);
+
+ incr = 3;
+ } break;
+ case OPCODE_JUMP_IF_NOT: {
+ text += "jump-if-not ";
+ text += DADDR(1);
+ text += " to ";
+ text += itos(_code_ptr[ip + 2]);
+
+ incr = 3;
+ } break;
+ case OPCODE_JUMP_TO_DEF_ARGUMENT: {
+ text += "jump-to-default-argument ";
+
+ incr = 1;
+ } break;
+ case OPCODE_RETURN: {
+ text += "return ";
+ text += DADDR(1);
+
+ incr = 2;
+ } break;
+
+#define DISASSEMBLE_ITERATE(m_type) \
+ case OPCODE_ITERATE_##m_type: { \
+ text += "for-loop (typed "; \
+ text += #m_type; \
+ text += ") "; \
+ text += DADDR(3); \
+ text += " in "; \
+ text += DADDR(2); \
+ text += " counter "; \
+ text += DADDR(1); \
+ text += " end "; \
+ text += itos(_code_ptr[ip + 4]); \
+ incr += 5; \
+ } break
+
+#define DISASSEMBLE_ITERATE_BEGIN(m_type) \
+ case OPCODE_ITERATE_BEGIN_##m_type: { \
+ text += "for-init (typed "; \
+ text += #m_type; \
+ text += ") "; \
+ text += DADDR(3); \
+ text += " in "; \
+ text += DADDR(2); \
+ text += " counter "; \
+ text += DADDR(1); \
+ text += " end "; \
+ text += itos(_code_ptr[ip + 4]); \
+ incr += 5; \
+ } break
+
+#define DISASSEMBLE_ITERATE_TYPES(m_macro) \
+ m_macro(INT); \
+ m_macro(FLOAT); \
+ m_macro(VECTOR2); \
+ m_macro(VECTOR2I); \
+ m_macro(VECTOR3); \
+ m_macro(VECTOR3I); \
+ m_macro(STRING); \
+ m_macro(DICTIONARY); \
+ m_macro(ARRAY); \
+ m_macro(PACKED_BYTE_ARRAY); \
+ m_macro(PACKED_INT32_ARRAY); \
+ m_macro(PACKED_INT64_ARRAY); \
+ m_macro(PACKED_FLOAT32_ARRAY); \
+ m_macro(PACKED_FLOAT64_ARRAY); \
+ m_macro(PACKED_STRING_ARRAY); \
+ m_macro(PACKED_VECTOR2_ARRAY); \
+ m_macro(PACKED_VECTOR3_ARRAY); \
+ m_macro(PACKED_COLOR_ARRAY); \
+ m_macro(OBJECT)
+
+ case OPCODE_ITERATE_BEGIN: {
+ text += "for-init ";
+ text += DADDR(3);
+ text += " in ";
+ text += DADDR(2);
+ text += " counter ";
+ text += DADDR(1);
+ text += " end ";
+ text += itos(_code_ptr[ip + 4]);
+
+ incr += 5;
+ } break;
+ DISASSEMBLE_ITERATE_TYPES(DISASSEMBLE_ITERATE_BEGIN);
+ case OPCODE_ITERATE: {
+ text += "for-loop ";
+ text += DADDR(2);
+ text += " in ";
+ text += DADDR(2);
+ text += " counter ";
+ text += DADDR(1);
+ text += " end ";
+ text += itos(_code_ptr[ip + 4]);
+
+ incr += 5;
+ } break;
+ DISASSEMBLE_ITERATE_TYPES(DISASSEMBLE_ITERATE);
+ case OPCODE_LINE: {
+ int line = _code_ptr[ip + 1] - 1;
+ if (line >= 0 && line < p_code_lines.size()) {
+ text += "line ";
+ text += itos(line + 1);
+ text += ": ";
+ text += p_code_lines[line];
+ } else {
+ text += "";
+ }
+
+ incr += 2;
+ } break;
+ case OPCODE_ASSERT: {
+ text += "assert (";
+ text += DADDR(1);
+ text += ", ";
+ text += DADDR(2);
+ text += ")";
+
+ incr += 3;
+ } break;
+ case OPCODE_BREAKPOINT: {
+ text += "breakpoint";
+
+ incr += 1;
+ } break;
+ case OPCODE_END: {
+ text += "== END ==";
+
+ incr += 1;
+ } break;
+ }
+
+ ip += incr;
+ if (text.get_string_length() > 0) {
+ print_line(text.as_string());
+ }
+ }
+}
+
+#endif
diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp
index 2e372575da..a426046797 100644
--- a/modules/gdscript/gdscript_editor.cpp
+++ b/modules/gdscript/gdscript_editor.cpp
@@ -30,8 +30,8 @@
#include "gdscript.h"
-#include "core/engine.h"
-#include "core/global_constants.h"
+#include "core/config/engine.h"
+#include "core/core_constants.h"
#include "core/os/file_access.h"
#include "gdscript_analyzer.h"
#include "gdscript_compiler.h"
@@ -39,7 +39,7 @@
#include "gdscript_tokenizer.h"
#ifdef TOOLS_ENABLED
-#include "core/project_settings.h"
+#include "core/config/project_settings.h"
#include "editor/editor_file_system.h"
#include "editor/editor_settings.h"
#endif
@@ -383,8 +383,8 @@ void GDScriptLanguage::debug_get_globals(List<String> *p_globals, List<Variant>
}
bool skip = false;
- for (int i = 0; i < GlobalConstants::get_global_constant_count(); i++) {
- if (E->key() == GlobalConstants::get_global_constant_name(i)) {
+ for (int i = 0; i < CoreConstants::get_global_constant_count(); i++) {
+ if (E->key() == CoreConstants::get_global_constant_name(i)) {
skip = true;
break;
}
@@ -792,6 +792,9 @@ static void _find_identifiers_in_class(const GDScriptParser::ClassNode *p_class,
continue;
}
option = ScriptCodeCompletionOption(member.constant->identifier->name, ScriptCodeCompletionOption::KIND_CONSTANT);
+ if (member.constant->initializer) {
+ option.default_value = member.constant->initializer->reduced_value;
+ }
break;
case GDScriptParser::ClassNode::Member::CLASS:
if (p_only_functions) {
@@ -970,7 +973,8 @@ static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base
} break;
case GDScriptParser::DataType::BUILTIN: {
Callable::CallError err;
- Variant tmp = Variant::construct(base_type.builtin_type, nullptr, 0, err);
+ Variant tmp;
+ Variant::construct(base_type.builtin_type, tmp, nullptr, 0, err);
if (err.error != Callable::CallError::CALL_OK) {
return;
}
@@ -1520,7 +1524,8 @@ static bool _guess_expression_type(GDScriptParser::CompletionContext &p_context,
found = _guess_identifier_type_from_base(c, base, id, r_type);
} else if (!found && index.type.kind == GDScriptParser::DataType::BUILTIN) {
Callable::CallError err;
- Variant base_val = Variant::construct(base.type.builtin_type, nullptr, 0, err);
+ Variant base_val;
+ Variant::construct(base.type.builtin_type, base_val, nullptr, 0, err);
bool valid = false;
Variant res = base_val.get(index.value, &valid);
if (valid) {
@@ -1557,9 +1562,14 @@ static bool _guess_expression_type(GDScriptParser::CompletionContext &p_context,
Callable::CallError ce;
bool v1_use_value = p1.value.get_type() != Variant::NIL && p1.value.get_type() != Variant::OBJECT;
- Variant v1 = (v1_use_value) ? p1.value : Variant::construct(p1.type.builtin_type, nullptr, 0, ce);
+ Variant d1;
+ Variant::construct(p1.type.builtin_type, d1, nullptr, 0, ce);
+ Variant d2;
+ Variant::construct(p2.type.builtin_type, d2, nullptr, 0, ce);
+
+ Variant v1 = (v1_use_value) ? p1.value : d1;
bool v2_use_value = p2.value.get_type() != Variant::NIL && p2.value.get_type() != Variant::OBJECT;
- Variant v2 = (v2_use_value) ? p2.value : Variant::construct(p2.type.builtin_type, nullptr, 0, ce);
+ Variant v2 = (v2_use_value) ? p2.value : d2;
// avoid potential invalid ops
if ((op->variant_op == Variant::OP_DIVIDE || op->variant_op == Variant::OP_MODULE) && v2.get_type() == Variant::INT) {
v2 = 1;
@@ -1949,7 +1959,8 @@ static bool _guess_identifier_type_from_base(GDScriptParser::CompletionContext &
} break;
case GDScriptParser::DataType::BUILTIN: {
Callable::CallError err;
- Variant tmp = Variant::construct(base_type.builtin_type, nullptr, 0, err);
+ Variant tmp;
+ Variant::construct(base_type.builtin_type, tmp, nullptr, 0, err);
if (err.error != Callable::CallError::CALL_OK) {
return false;
@@ -2099,7 +2110,8 @@ static bool _guess_method_return_type_from_base(GDScriptParser::CompletionContex
} break;
case GDScriptParser::DataType::BUILTIN: {
Callable::CallError err;
- Variant tmp = Variant::construct(base_type.builtin_type, nullptr, 0, err);
+ Variant tmp;
+ Variant::construct(base_type.builtin_type, tmp, nullptr, 0, err);
if (err.error != Callable::CallError::CALL_OK) {
return false;
}
@@ -2136,9 +2148,9 @@ static void _find_enumeration_candidates(GDScriptParser::CompletionContext &p_co
r_result.insert(option.display, option);
}
} else {
- for (int i = 0; i < GlobalConstants::get_global_constant_count(); i++) {
- if (GlobalConstants::get_global_constant_enum(i) == current_enum) {
- ScriptCodeCompletionOption option(GlobalConstants::get_global_constant_name(i), ScriptCodeCompletionOption::KIND_ENUM);
+ for (int i = 0; i < CoreConstants::get_global_constant_count(); i++) {
+ if (CoreConstants::get_global_constant_enum(i) == current_enum) {
+ ScriptCodeCompletionOption option(CoreConstants::get_global_constant_name(i), ScriptCodeCompletionOption::KIND_ENUM);
r_result.insert(option.display, option);
}
}
@@ -2254,7 +2266,7 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c
case GDScriptParser::DataType::BUILTIN: {
if (base.get_type() == Variant::NIL) {
Callable::CallError err;
- base = Variant::construct(base_type.builtin_type, nullptr, 0, err);
+ Variant::construct(base_type.builtin_type, base, nullptr, 0, err);
if (err.error != Callable::CallError::CALL_OK) {
return;
}
@@ -2303,11 +2315,6 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c
if (GDScriptParser::get_builtin_function(call->function_name) < GDScriptFunctions::FUNC_MAX) {
MethodInfo info = GDScriptFunctions::get_info(GDScriptParser::get_builtin_function(call->function_name));
-
- if ((info.name == "load" || info.name == "preload") && bool(EditorSettings::get_singleton()->get("text_editor/completion/complete_file_paths"))) {
- _get_directory_contents(EditorFileSystem::get_singleton()->get_filesystem(), r_result);
- }
-
r_arghint = _make_arguments_hint(info, p_argidx);
return;
} else if (GDScriptParser::get_builtin_type(call->function_name) < Variant::VARIANT_MAX) {
@@ -2404,6 +2411,11 @@ Error GDScriptLanguage::complete_code(const String &p_code, const String &p_path
Variant::get_constants_for_type(completion_context.builtin_type, &constants);
for (const List<StringName>::Element *E = constants.front(); E != nullptr; E = E->next()) {
ScriptCodeCompletionOption option(E->get(), ScriptCodeCompletionOption::KIND_CONSTANT);
+ bool valid = false;
+ Variant default_value = Variant::get_constant_value(completion_context.builtin_type, E->get(), &valid);
+ if (valid) {
+ option.default_value = default_value;
+ }
options.insert(option.display, option);
}
} break;
@@ -2875,7 +2887,7 @@ static Error _lookup_symbol_from_base(const GDScriptParser::DataType &p_base, co
v = v_ref;
} else {
Callable::CallError err;
- v = Variant::construct(base_type.builtin_type, NULL, 0, err);
+ Variant::construct(base_type.builtin_type, v, NULL, 0, err);
if (err.error != Callable::CallError::CALL_OK) {
break;
}
diff --git a/modules/gdscript/gdscript_function.cpp b/modules/gdscript/gdscript_function.cpp
index e59f99fc56..32372439c5 100644
--- a/modules/gdscript/gdscript_function.cpp
+++ b/modules/gdscript/gdscript_function.cpp
@@ -30,1565 +30,7 @@
#include "gdscript_function.h"
-#include "core/os/os.h"
#include "gdscript.h"
-#include "gdscript_functions.h"
-
-#ifdef DEBUG_ENABLED
-#include "core/string_builder.h"
-#endif
-
-Variant *GDScriptFunction::_get_variant(int p_address, GDScriptInstance *p_instance, GDScript *p_script, Variant &self, Variant &static_ref, Variant *p_stack, String &r_error) const {
- int address = p_address & ADDR_MASK;
-
- //sequential table (jump table generated by compiler)
- switch ((p_address & ADDR_TYPE_MASK) >> ADDR_BITS) {
- case ADDR_TYPE_SELF: {
-#ifdef DEBUG_ENABLED
- if (unlikely(!p_instance)) {
- r_error = "Cannot access self without instance.";
- return nullptr;
- }
-#endif
- return &self;
- } break;
- case ADDR_TYPE_CLASS: {
- return &static_ref;
- } break;
- case ADDR_TYPE_MEMBER: {
-#ifdef DEBUG_ENABLED
- if (unlikely(!p_instance)) {
- r_error = "Cannot access member without instance.";
- return nullptr;
- }
-#endif
- //member indexing is O(1)
- return &p_instance->members.write[address];
- } break;
- case ADDR_TYPE_CLASS_CONSTANT: {
- //todo change to index!
- GDScript *s = p_script;
-#ifdef DEBUG_ENABLED
- ERR_FAIL_INDEX_V(address, _global_names_count, nullptr);
-#endif
- const StringName *sn = &_global_names_ptr[address];
-
- while (s) {
- GDScript *o = s;
- while (o) {
- Map<StringName, Variant>::Element *E = o->constants.find(*sn);
- if (E) {
- return &E->get();
- }
- o = o->_owner;
- }
- s = s->_base;
- }
-
- ERR_FAIL_V_MSG(nullptr, "GDScriptCompiler bug.");
- } break;
- case ADDR_TYPE_LOCAL_CONSTANT: {
-#ifdef DEBUG_ENABLED
- ERR_FAIL_INDEX_V(address, _constant_count, nullptr);
-#endif
- return &_constants_ptr[address];
- } break;
- case ADDR_TYPE_STACK:
- case ADDR_TYPE_STACK_VARIABLE: {
-#ifdef DEBUG_ENABLED
- ERR_FAIL_INDEX_V(address, _stack_size, nullptr);
-#endif
- return &p_stack[address];
- } break;
- case ADDR_TYPE_GLOBAL: {
-#ifdef DEBUG_ENABLED
- ERR_FAIL_INDEX_V(address, GDScriptLanguage::get_singleton()->get_global_array_size(), nullptr);
-#endif
- return &GDScriptLanguage::get_singleton()->get_global_array()[address];
- } break;
-#ifdef TOOLS_ENABLED
- case ADDR_TYPE_NAMED_GLOBAL: {
-#ifdef DEBUG_ENABLED
- ERR_FAIL_INDEX_V(address, _global_names_count, nullptr);
-#endif
- StringName id = _global_names_ptr[address];
-
- if (GDScriptLanguage::get_singleton()->get_named_globals_map().has(id)) {
- return (Variant *)&GDScriptLanguage::get_singleton()->get_named_globals_map()[id];
- } else {
- r_error = "Autoload singleton '" + String(id) + "' has been removed.";
- return nullptr;
- }
- } break;
-#endif
- case ADDR_TYPE_NIL: {
- return &nil;
- } break;
- }
-
- ERR_FAIL_V_MSG(nullptr, "Bad code! (unknown addressing mode).");
- return nullptr;
-}
-
-#ifdef DEBUG_ENABLED
-static String _get_var_type(const Variant *p_var) {
- String basestr;
-
- if (p_var->get_type() == Variant::OBJECT) {
- bool was_freed;
- Object *bobj = p_var->get_validated_object_with_check(was_freed);
- if (!bobj) {
- if (was_freed) {
- basestr = "null instance";
- } else {
- basestr = "previously freed";
- }
- } else {
- if (bobj->get_script_instance()) {
- basestr = bobj->get_class() + " (" + bobj->get_script_instance()->get_script()->get_path().get_file() + ")";
- } else {
- basestr = bobj->get_class();
- }
- }
-
- } else {
- basestr = Variant::get_type_name(p_var->get_type());
- }
-
- return basestr;
-}
-#endif // DEBUG_ENABLED
-
-String GDScriptFunction::_get_call_error(const Callable::CallError &p_err, const String &p_where, const Variant **argptrs) const {
- String err_text;
-
- if (p_err.error == Callable::CallError::CALL_ERROR_INVALID_ARGUMENT) {
- int errorarg = p_err.argument;
- // Handle the Object to Object case separately as we don't have further class details.
-#ifdef DEBUG_ENABLED
- if (p_err.expected == Variant::OBJECT && argptrs[errorarg]->get_type() == p_err.expected) {
- err_text = "Invalid type in " + p_where + ". The Object-derived class of argument " + itos(errorarg + 1) + " (" + _get_var_type(argptrs[errorarg]) + ") is not a subclass of the expected argument class.";
- } else
-#endif // DEBUG_ENABLED
- {
- err_text = "Invalid type in " + p_where + ". Cannot convert argument " + itos(errorarg + 1) + " from " + Variant::get_type_name(argptrs[errorarg]->get_type()) + " to " + Variant::get_type_name(Variant::Type(p_err.expected)) + ".";
- }
- } else if (p_err.error == Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS) {
- err_text = "Invalid call to " + p_where + ". Expected " + itos(p_err.argument) + " arguments.";
- } else if (p_err.error == Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS) {
- err_text = "Invalid call to " + p_where + ". Expected " + itos(p_err.argument) + " arguments.";
- } else if (p_err.error == Callable::CallError::CALL_ERROR_INVALID_METHOD) {
- err_text = "Invalid call. Nonexistent " + p_where + ".";
- } else if (p_err.error == Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL) {
- err_text = "Attempt to call " + p_where + " on a null instance.";
- } else {
- err_text = "Bug, call error: #" + itos(p_err.error);
- }
-
- return err_text;
-}
-
-#if defined(__GNUC__)
-#define OPCODES_TABLE \
- static const void *switch_table_ops[] = { \
- &&OPCODE_OPERATOR, \
- &&OPCODE_EXTENDS_TEST, \
- &&OPCODE_IS_BUILTIN, \
- &&OPCODE_SET, \
- &&OPCODE_GET, \
- &&OPCODE_SET_NAMED, \
- &&OPCODE_GET_NAMED, \
- &&OPCODE_SET_MEMBER, \
- &&OPCODE_GET_MEMBER, \
- &&OPCODE_ASSIGN, \
- &&OPCODE_ASSIGN_TRUE, \
- &&OPCODE_ASSIGN_FALSE, \
- &&OPCODE_ASSIGN_TYPED_BUILTIN, \
- &&OPCODE_ASSIGN_TYPED_NATIVE, \
- &&OPCODE_ASSIGN_TYPED_SCRIPT, \
- &&OPCODE_CAST_TO_BUILTIN, \
- &&OPCODE_CAST_TO_NATIVE, \
- &&OPCODE_CAST_TO_SCRIPT, \
- &&OPCODE_CONSTRUCT, \
- &&OPCODE_CONSTRUCT_ARRAY, \
- &&OPCODE_CONSTRUCT_DICTIONARY, \
- &&OPCODE_CALL, \
- &&OPCODE_CALL_RETURN, \
- &&OPCODE_CALL_ASYNC, \
- &&OPCODE_CALL_BUILT_IN, \
- &&OPCODE_CALL_SELF_BASE, \
- &&OPCODE_AWAIT, \
- &&OPCODE_AWAIT_RESUME, \
- &&OPCODE_JUMP, \
- &&OPCODE_JUMP_IF, \
- &&OPCODE_JUMP_IF_NOT, \
- &&OPCODE_JUMP_TO_DEF_ARGUMENT, \
- &&OPCODE_RETURN, \
- &&OPCODE_ITERATE_BEGIN, \
- &&OPCODE_ITERATE, \
- &&OPCODE_ASSERT, \
- &&OPCODE_BREAKPOINT, \
- &&OPCODE_LINE, \
- &&OPCODE_END \
- }; \
- static_assert((sizeof(switch_table_ops) / sizeof(switch_table_ops[0]) == (OPCODE_END + 1)), "Opcodes in jump table aren't the same as opcodes in enum.");
-
-#define OPCODE(m_op) \
- m_op:
-#define OPCODE_WHILE(m_test)
-#define OPCODES_END \
- OPSEXIT:
-#define OPCODES_OUT \
- OPSOUT:
-#define DISPATCH_OPCODE goto *switch_table_ops[_code_ptr[ip]]
-#define OPCODE_SWITCH(m_test) DISPATCH_OPCODE;
-#define OPCODE_BREAK goto OPSEXIT
-#define OPCODE_OUT goto OPSOUT
-#else
-#define OPCODES_TABLE
-#define OPCODE(m_op) case m_op:
-#define OPCODE_WHILE(m_test) while (m_test)
-#define OPCODES_END
-#define OPCODES_OUT
-#define DISPATCH_OPCODE continue
-#define OPCODE_SWITCH(m_test) switch (m_test)
-#define OPCODE_BREAK break
-#define OPCODE_OUT break
-#endif
-
-Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_args, int p_argcount, Callable::CallError &r_err, CallState *p_state) {
- OPCODES_TABLE;
-
- if (!_code_ptr) {
- return Variant();
- }
-
- r_err.error = Callable::CallError::CALL_OK;
-
- Variant self;
- Variant static_ref;
- Variant retvalue;
- Variant *stack = nullptr;
- Variant **call_args;
- int defarg = 0;
-
-#ifdef DEBUG_ENABLED
-
- //GDScriptLanguage::get_singleton()->calls++;
-
-#endif
-
- uint32_t alloca_size = 0;
- GDScript *script;
- int ip = 0;
- int line = _initial_line;
-
- if (p_state) {
- //use existing (supplied) state (awaited)
- stack = (Variant *)p_state->stack.ptr();
- call_args = (Variant **)&p_state->stack.ptr()[sizeof(Variant) * p_state->stack_size]; //ptr() to avoid bounds check
- line = p_state->line;
- ip = p_state->ip;
- alloca_size = p_state->stack.size();
- script = p_state->script;
- p_instance = p_state->instance;
- defarg = p_state->defarg;
- self = p_state->self;
-
- } else {
- if (p_argcount != _argument_count) {
- if (p_argcount > _argument_count) {
- r_err.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS;
- r_err.argument = _argument_count;
-
- return Variant();
- } else if (p_argcount < _argument_count - _default_arg_count) {
- r_err.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
- r_err.argument = _argument_count - _default_arg_count;
- return Variant();
- } else {
- defarg = _argument_count - p_argcount;
- }
- }
-
- alloca_size = sizeof(Variant *) * _call_size + sizeof(Variant) * _stack_size;
-
- if (alloca_size) {
- uint8_t *aptr = (uint8_t *)alloca(alloca_size);
-
- if (_stack_size) {
- stack = (Variant *)aptr;
- for (int i = 0; i < p_argcount; i++) {
- if (!argument_types[i].has_type) {
- memnew_placement(&stack[i], Variant(*p_args[i]));
- continue;
- }
-
- if (!argument_types[i].is_type(*p_args[i], true)) {
- r_err.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_err.argument = i;
- r_err.expected = argument_types[i].kind == GDScriptDataType::BUILTIN ? argument_types[i].builtin_type : Variant::OBJECT;
- return Variant();
- }
- if (argument_types[i].kind == GDScriptDataType::BUILTIN) {
- Variant arg = Variant::construct(argument_types[i].builtin_type, &p_args[i], 1, r_err);
- memnew_placement(&stack[i], Variant(arg));
- } else {
- memnew_placement(&stack[i], Variant(*p_args[i]));
- }
- }
- for (int i = p_argcount; i < _stack_size; i++) {
- memnew_placement(&stack[i], Variant);
- }
- } else {
- stack = nullptr;
- }
-
- if (_call_size) {
- call_args = (Variant **)&aptr[sizeof(Variant) * _stack_size];
- } else {
- call_args = nullptr;
- }
-
- } else {
- stack = nullptr;
- call_args = nullptr;
- }
-
- if (p_instance) {
- if (p_instance->base_ref && static_cast<Reference *>(p_instance->owner)->is_referenced()) {
- self = REF(static_cast<Reference *>(p_instance->owner));
- } else {
- self = p_instance->owner;
- }
- script = p_instance->script.ptr();
- } else {
- script = _script;
- }
- }
-
- static_ref = script;
-
- String err_text;
-
-#ifdef DEBUG_ENABLED
-
- if (EngineDebugger::is_active()) {
- GDScriptLanguage::get_singleton()->enter_function(p_instance, this, stack, &ip, &line);
- }
-
-#define GD_ERR_BREAK(m_cond) \
- { \
- if (unlikely(m_cond)) { \
- _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition ' " _STR(m_cond) " ' is true. Breaking..:"); \
- OPCODE_BREAK; \
- } \
- }
-
-#define CHECK_SPACE(m_space) \
- GD_ERR_BREAK((ip + m_space) > _code_size)
-
-#define GET_VARIANT_PTR(m_v, m_code_ofs) \
- Variant *m_v; \
- m_v = _get_variant(_code_ptr[ip + m_code_ofs], p_instance, script, self, static_ref, stack, err_text); \
- if (unlikely(!m_v)) \
- OPCODE_BREAK;
-
-#else
-#define GD_ERR_BREAK(m_cond)
-#define CHECK_SPACE(m_space)
-#define GET_VARIANT_PTR(m_v, m_code_ofs) \
- Variant *m_v; \
- m_v = _get_variant(_code_ptr[ip + m_code_ofs], p_instance, script, self, static_ref, stack, err_text);
-
-#endif
-
-#ifdef DEBUG_ENABLED
-
- uint64_t function_start_time = 0;
- uint64_t function_call_time = 0;
-
- if (GDScriptLanguage::get_singleton()->profiling) {
- function_start_time = OS::get_singleton()->get_ticks_usec();
- function_call_time = 0;
- profile.call_count++;
- profile.frame_call_count++;
- }
- bool exit_ok = false;
- bool awaited = false;
-#endif
-
-#ifdef DEBUG_ENABLED
- OPCODE_WHILE(ip < _code_size) {
- int last_opcode = _code_ptr[ip];
-#else
- OPCODE_WHILE(true) {
-#endif
-
- OPCODE_SWITCH(_code_ptr[ip]) {
- OPCODE(OPCODE_OPERATOR) {
- CHECK_SPACE(5);
-
- bool valid;
- Variant::Operator op = (Variant::Operator)_code_ptr[ip + 1];
- GD_ERR_BREAK(op >= Variant::OP_MAX);
-
- GET_VARIANT_PTR(a, 2);
- GET_VARIANT_PTR(b, 3);
- GET_VARIANT_PTR(dst, 4);
-
-#ifdef DEBUG_ENABLED
-
- Variant ret;
- Variant::evaluate(op, *a, *b, ret, valid);
-#else
- Variant::evaluate(op, *a, *b, *dst, valid);
-#endif
-#ifdef DEBUG_ENABLED
- if (!valid) {
- if (ret.get_type() == Variant::STRING) {
- //return a string when invalid with the error
- err_text = ret;
- err_text += " in operator '" + Variant::get_operator_name(op) + "'.";
- } else {
- err_text = "Invalid operands '" + Variant::get_type_name(a->get_type()) + "' and '" + Variant::get_type_name(b->get_type()) + "' in operator '" + Variant::get_operator_name(op) + "'.";
- }
- OPCODE_BREAK;
- }
- *dst = ret;
-#endif
- ip += 5;
- }
- DISPATCH_OPCODE;
-
- OPCODE(OPCODE_EXTENDS_TEST) {
- CHECK_SPACE(4);
-
- GET_VARIANT_PTR(a, 1);
- GET_VARIANT_PTR(b, 2);
- GET_VARIANT_PTR(dst, 3);
-
-#ifdef DEBUG_ENABLED
- if (b->get_type() != Variant::OBJECT || b->operator Object *() == nullptr) {
- err_text = "Right operand of 'is' is not a class.";
- OPCODE_BREAK;
- }
-#endif
-
- bool extends_ok = false;
- if (a->get_type() == Variant::OBJECT && a->operator Object *() != nullptr) {
-#ifdef DEBUG_ENABLED
- bool was_freed;
- Object *obj_A = a->get_validated_object_with_check(was_freed);
-
- if (was_freed) {
- err_text = "Left operand of 'is' is a previously freed instance.";
- OPCODE_BREAK;
- }
-
- Object *obj_B = b->get_validated_object_with_check(was_freed);
-
- if (was_freed) {
- err_text = "Right operand of 'is' is a previously freed instance.";
- OPCODE_BREAK;
- }
-#else
-
- Object *obj_A = *a;
- Object *obj_B = *b;
-#endif // DEBUG_ENABLED
-
- GDScript *scr_B = Object::cast_to<GDScript>(obj_B);
-
- if (scr_B) {
- //if B is a script, the only valid condition is that A has an instance which inherits from the script
- //in other situation, this shoul return false.
-
- if (obj_A->get_script_instance() && obj_A->get_script_instance()->get_language() == GDScriptLanguage::get_singleton()) {
- GDScript *cmp = static_cast<GDScript *>(obj_A->get_script_instance()->get_script().ptr());
- //bool found=false;
- while (cmp) {
- if (cmp == scr_B) {
- //inherits from script, all ok
- extends_ok = true;
- break;
- }
-
- cmp = cmp->_base;
- }
- }
-
- } else {
- GDScriptNativeClass *nc = Object::cast_to<GDScriptNativeClass>(obj_B);
-
-#ifdef DEBUG_ENABLED
- if (!nc) {
- err_text = "Right operand of 'is' is not a class (type: '" + obj_B->get_class() + "').";
- OPCODE_BREAK;
- }
-#endif
- extends_ok = ClassDB::is_parent_class(obj_A->get_class_name(), nc->get_name());
- }
- }
-
- *dst = extends_ok;
- ip += 4;
- }
- DISPATCH_OPCODE;
-
- OPCODE(OPCODE_IS_BUILTIN) {
- CHECK_SPACE(4);
-
- GET_VARIANT_PTR(value, 1);
- Variant::Type var_type = (Variant::Type)_code_ptr[ip + 2];
- GET_VARIANT_PTR(dst, 3);
-
- GD_ERR_BREAK(var_type < 0 || var_type >= Variant::VARIANT_MAX);
-
- *dst = value->get_type() == var_type;
- ip += 4;
- }
- DISPATCH_OPCODE;
-
- OPCODE(OPCODE_SET) {
- CHECK_SPACE(3);
-
- GET_VARIANT_PTR(dst, 1);
- GET_VARIANT_PTR(index, 2);
- GET_VARIANT_PTR(value, 3);
-
- bool valid;
- dst->set(*index, *value, &valid);
-
-#ifdef DEBUG_ENABLED
- if (!valid) {
- String v = index->operator String();
- if (v != "") {
- v = "'" + v + "'";
- } else {
- v = "of type '" + _get_var_type(index) + "'";
- }
- err_text = "Invalid set index " + v + " (on base: '" + _get_var_type(dst) + "') with value of type '" + _get_var_type(value) + "'";
- OPCODE_BREAK;
- }
-#endif
- ip += 4;
- }
- DISPATCH_OPCODE;
-
- OPCODE(OPCODE_GET) {
- CHECK_SPACE(3);
-
- GET_VARIANT_PTR(src, 1);
- GET_VARIANT_PTR(index, 2);
- GET_VARIANT_PTR(dst, 3);
-
- bool valid;
-#ifdef DEBUG_ENABLED
- //allow better error message in cases where src and dst are the same stack position
- Variant ret = src->get(*index, &valid);
-#else
- *dst = src->get(*index, &valid);
-
-#endif
-#ifdef DEBUG_ENABLED
- if (!valid) {
- String v = index->operator String();
- if (v != "") {
- v = "'" + v + "'";
- } else {
- v = "of type '" + _get_var_type(index) + "'";
- }
- err_text = "Invalid get index " + v + " (on base: '" + _get_var_type(src) + "').";
- OPCODE_BREAK;
- }
- *dst = ret;
-#endif
- ip += 4;
- }
- DISPATCH_OPCODE;
-
- OPCODE(OPCODE_SET_NAMED) {
- CHECK_SPACE(3);
-
- GET_VARIANT_PTR(dst, 1);
- GET_VARIANT_PTR(value, 3);
-
- int indexname = _code_ptr[ip + 2];
-
- GD_ERR_BREAK(indexname < 0 || indexname >= _global_names_count);
- const StringName *index = &_global_names_ptr[indexname];
-
- bool valid;
- dst->set_named(*index, *value, &valid);
-
-#ifdef DEBUG_ENABLED
- if (!valid) {
- String err_type;
- err_text = "Invalid set index '" + String(*index) + "' (on base: '" + _get_var_type(dst) + "') with value of type '" + _get_var_type(value) + "'.";
- OPCODE_BREAK;
- }
-#endif
- ip += 4;
- }
- DISPATCH_OPCODE;
-
- OPCODE(OPCODE_GET_NAMED) {
- CHECK_SPACE(4);
-
- GET_VARIANT_PTR(src, 1);
- GET_VARIANT_PTR(dst, 3);
-
- int indexname = _code_ptr[ip + 2];
-
- GD_ERR_BREAK(indexname < 0 || indexname >= _global_names_count);
- const StringName *index = &_global_names_ptr[indexname];
-
- bool valid;
-#ifdef DEBUG_ENABLED
- //allow better error message in cases where src and dst are the same stack position
- Variant ret = src->get_named(*index, &valid);
-
-#else
- *dst = src->get_named(*index, &valid);
-#endif
-#ifdef DEBUG_ENABLED
- if (!valid) {
- if (src->has_method(*index)) {
- err_text = "Invalid get index '" + index->operator String() + "' (on base: '" + _get_var_type(src) + "'). Did you mean '." + index->operator String() + "()' or funcref(obj, \"" + index->operator String() + "\") ?";
- } else {
- err_text = "Invalid get index '" + index->operator String() + "' (on base: '" + _get_var_type(src) + "').";
- }
- OPCODE_BREAK;
- }
- *dst = ret;
-#endif
- ip += 4;
- }
- DISPATCH_OPCODE;
-
- OPCODE(OPCODE_SET_MEMBER) {
- CHECK_SPACE(3);
- int indexname = _code_ptr[ip + 1];
- GD_ERR_BREAK(indexname < 0 || indexname >= _global_names_count);
- const StringName *index = &_global_names_ptr[indexname];
- GET_VARIANT_PTR(src, 2);
-
- bool valid;
-#ifndef DEBUG_ENABLED
- ClassDB::set_property(p_instance->owner, *index, *src, &valid);
-#else
- bool ok = ClassDB::set_property(p_instance->owner, *index, *src, &valid);
- if (!ok) {
- err_text = "Internal error setting property: " + String(*index);
- OPCODE_BREAK;
- } else if (!valid) {
- err_text = "Error setting property '" + String(*index) + "' with value of type " + Variant::get_type_name(src->get_type()) + ".";
- OPCODE_BREAK;
- }
-#endif
- ip += 3;
- }
- DISPATCH_OPCODE;
-
- OPCODE(OPCODE_GET_MEMBER) {
- CHECK_SPACE(3);
- int indexname = _code_ptr[ip + 1];
- GD_ERR_BREAK(indexname < 0 || indexname >= _global_names_count);
- const StringName *index = &_global_names_ptr[indexname];
- GET_VARIANT_PTR(dst, 2);
-
-#ifndef DEBUG_ENABLED
- ClassDB::get_property(p_instance->owner, *index, *dst);
-#else
- bool ok = ClassDB::get_property(p_instance->owner, *index, *dst);
- if (!ok) {
- err_text = "Internal error getting property: " + String(*index);
- OPCODE_BREAK;
- }
-#endif
- ip += 3;
- }
- DISPATCH_OPCODE;
-
- OPCODE(OPCODE_ASSIGN) {
- CHECK_SPACE(3);
- GET_VARIANT_PTR(dst, 1);
- GET_VARIANT_PTR(src, 2);
-
- *dst = *src;
-
- ip += 3;
- }
- DISPATCH_OPCODE;
-
- OPCODE(OPCODE_ASSIGN_TRUE) {
- CHECK_SPACE(2);
- GET_VARIANT_PTR(dst, 1);
-
- *dst = true;
-
- ip += 2;
- }
- DISPATCH_OPCODE;
-
- OPCODE(OPCODE_ASSIGN_FALSE) {
- CHECK_SPACE(2);
- GET_VARIANT_PTR(dst, 1);
-
- *dst = false;
-
- ip += 2;
- }
- DISPATCH_OPCODE;
-
- OPCODE(OPCODE_ASSIGN_TYPED_BUILTIN) {
- CHECK_SPACE(4);
- GET_VARIANT_PTR(dst, 2);
- GET_VARIANT_PTR(src, 3);
-
- Variant::Type var_type = (Variant::Type)_code_ptr[ip + 1];
- GD_ERR_BREAK(var_type < 0 || var_type >= Variant::VARIANT_MAX);
-
- if (src->get_type() != var_type) {
-#ifdef DEBUG_ENABLED
- if (Variant::can_convert_strict(src->get_type(), var_type)) {
-#endif // DEBUG_ENABLED
- Callable::CallError ce;
- *dst = Variant::construct(var_type, const_cast<const Variant **>(&src), 1, ce);
- } else {
-#ifdef DEBUG_ENABLED
- err_text = "Trying to assign value of type '" + Variant::get_type_name(src->get_type()) +
- "' to a variable of type '" + Variant::get_type_name(var_type) + "'.";
- OPCODE_BREAK;
- }
- } else {
-#endif // DEBUG_ENABLED
- *dst = *src;
- }
-
- ip += 4;
- }
- DISPATCH_OPCODE;
-
- OPCODE(OPCODE_ASSIGN_TYPED_NATIVE) {
- CHECK_SPACE(4);
- GET_VARIANT_PTR(dst, 2);
- GET_VARIANT_PTR(src, 3);
-
-#ifdef DEBUG_ENABLED
- GET_VARIANT_PTR(type, 1);
- GDScriptNativeClass *nc = Object::cast_to<GDScriptNativeClass>(type->operator Object *());
- GD_ERR_BREAK(!nc);
- if (src->get_type() != Variant::OBJECT && src->get_type() != Variant::NIL) {
- err_text = "Trying to assign value of type '" + Variant::get_type_name(src->get_type()) +
- "' to a variable of type '" + nc->get_name() + "'.";
- OPCODE_BREAK;
- }
- Object *src_obj = src->operator Object *();
-
- if (src_obj && !ClassDB::is_parent_class(src_obj->get_class_name(), nc->get_name())) {
- err_text = "Trying to assign value of type '" + src_obj->get_class_name() +
- "' to a variable of type '" + nc->get_name() + "'.";
- OPCODE_BREAK;
- }
-#endif // DEBUG_ENABLED
- *dst = *src;
-
- ip += 4;
- }
- DISPATCH_OPCODE;
-
- OPCODE(OPCODE_ASSIGN_TYPED_SCRIPT) {
- CHECK_SPACE(4);
- GET_VARIANT_PTR(dst, 2);
- GET_VARIANT_PTR(src, 3);
-
-#ifdef DEBUG_ENABLED
- GET_VARIANT_PTR(type, 1);
- Script *base_type = Object::cast_to<Script>(type->operator Object *());
-
- GD_ERR_BREAK(!base_type);
-
- if (src->get_type() != Variant::OBJECT && src->get_type() != Variant::NIL) {
- err_text = "Trying to assign a non-object value to a variable of type '" + base_type->get_path().get_file() + "'.";
- OPCODE_BREAK;
- }
-
- if (src->get_type() != Variant::NIL && src->operator Object *() != nullptr) {
- ScriptInstance *scr_inst = src->operator Object *()->get_script_instance();
- if (!scr_inst) {
- err_text = "Trying to assign value of type '" + src->operator Object *()->get_class_name() +
- "' to a variable of type '" + base_type->get_path().get_file() + "'.";
- OPCODE_BREAK;
- }
-
- Script *src_type = src->operator Object *()->get_script_instance()->get_script().ptr();
- bool valid = false;
-
- while (src_type) {
- if (src_type == base_type) {
- valid = true;
- break;
- }
- src_type = src_type->get_base_script().ptr();
- }
-
- if (!valid) {
- err_text = "Trying to assign value of type '" + src->operator Object *()->get_script_instance()->get_script()->get_path().get_file() +
- "' to a variable of type '" + base_type->get_path().get_file() + "'.";
- OPCODE_BREAK;
- }
- }
-#endif // DEBUG_ENABLED
-
- *dst = *src;
-
- ip += 4;
- }
- DISPATCH_OPCODE;
-
- OPCODE(OPCODE_CAST_TO_BUILTIN) {
- CHECK_SPACE(4);
- Variant::Type to_type = (Variant::Type)_code_ptr[ip + 1];
- GET_VARIANT_PTR(src, 2);
- GET_VARIANT_PTR(dst, 3);
-
- GD_ERR_BREAK(to_type < 0 || to_type >= Variant::VARIANT_MAX);
-
- Callable::CallError err;
- *dst = Variant::construct(to_type, (const Variant **)&src, 1, err);
-
-#ifdef DEBUG_ENABLED
- if (err.error != Callable::CallError::CALL_OK) {
- err_text = "Invalid cast: could not convert value to '" + Variant::get_type_name(to_type) + "'.";
- OPCODE_BREAK;
- }
-#endif
-
- ip += 4;
- }
- DISPATCH_OPCODE;
-
- OPCODE(OPCODE_CAST_TO_NATIVE) {
- CHECK_SPACE(4);
- GET_VARIANT_PTR(to_type, 1);
- GET_VARIANT_PTR(src, 2);
- GET_VARIANT_PTR(dst, 3);
-
- GDScriptNativeClass *nc = Object::cast_to<GDScriptNativeClass>(to_type->operator Object *());
- GD_ERR_BREAK(!nc);
-
-#ifdef DEBUG_ENABLED
- if (src->get_type() != Variant::OBJECT && src->get_type() != Variant::NIL) {
- err_text = "Invalid cast: can't convert a non-object value to an object type.";
- OPCODE_BREAK;
- }
-#endif
- Object *src_obj = src->operator Object *();
-
- if (src_obj && !ClassDB::is_parent_class(src_obj->get_class_name(), nc->get_name())) {
- *dst = Variant(); // invalid cast, assign NULL
- } else {
- *dst = *src;
- }
-
- ip += 4;
- }
- DISPATCH_OPCODE;
-
- OPCODE(OPCODE_CAST_TO_SCRIPT) {
- CHECK_SPACE(4);
- GET_VARIANT_PTR(to_type, 1);
- GET_VARIANT_PTR(src, 2);
- GET_VARIANT_PTR(dst, 3);
-
- Script *base_type = Object::cast_to<Script>(to_type->operator Object *());
-
- GD_ERR_BREAK(!base_type);
-
-#ifdef DEBUG_ENABLED
- if (src->get_type() != Variant::OBJECT && src->get_type() != Variant::NIL) {
- err_text = "Trying to assign a non-object value to a variable of type '" + base_type->get_path().get_file() + "'.";
- OPCODE_BREAK;
- }
-#endif
-
- bool valid = false;
-
- if (src->get_type() != Variant::NIL && src->operator Object *() != nullptr) {
- ScriptInstance *scr_inst = src->operator Object *()->get_script_instance();
-
- if (scr_inst) {
- Script *src_type = src->operator Object *()->get_script_instance()->get_script().ptr();
-
- while (src_type) {
- if (src_type == base_type) {
- valid = true;
- break;
- }
- src_type = src_type->get_base_script().ptr();
- }
- }
- }
-
- if (valid) {
- *dst = *src; // Valid cast, copy the source object
- } else {
- *dst = Variant(); // invalid cast, assign NULL
- }
-
- ip += 4;
- }
- DISPATCH_OPCODE;
-
- OPCODE(OPCODE_CONSTRUCT) {
- CHECK_SPACE(2);
- Variant::Type t = Variant::Type(_code_ptr[ip + 1]);
- int argc = _code_ptr[ip + 2];
- CHECK_SPACE(argc + 2);
- Variant **argptrs = call_args;
- for (int i = 0; i < argc; i++) {
- GET_VARIANT_PTR(v, 3 + i);
- argptrs[i] = v;
- }
-
- GET_VARIANT_PTR(dst, 3 + argc);
- Callable::CallError err;
- *dst = Variant::construct(t, (const Variant **)argptrs, argc, err);
-
-#ifdef DEBUG_ENABLED
- if (err.error != Callable::CallError::CALL_OK) {
- err_text = _get_call_error(err, "'" + Variant::get_type_name(t) + "' constructor", (const Variant **)argptrs);
- OPCODE_BREAK;
- }
-#endif
-
- ip += 4 + argc;
- //construct a basic type
- }
- DISPATCH_OPCODE;
-
- OPCODE(OPCODE_CONSTRUCT_ARRAY) {
- CHECK_SPACE(1);
- int argc = _code_ptr[ip + 1];
- Array array; //arrays are always shared
- array.resize(argc);
- CHECK_SPACE(argc + 2);
-
- for (int i = 0; i < argc; i++) {
- GET_VARIANT_PTR(v, 2 + i);
- array[i] = *v;
- }
-
- GET_VARIANT_PTR(dst, 2 + argc);
-
- *dst = array;
-
- ip += 3 + argc;
- }
- DISPATCH_OPCODE;
-
- OPCODE(OPCODE_CONSTRUCT_DICTIONARY) {
- CHECK_SPACE(1);
- int argc = _code_ptr[ip + 1];
- Dictionary dict; //arrays are always shared
-
- CHECK_SPACE(argc * 2 + 2);
-
- for (int i = 0; i < argc; i++) {
- GET_VARIANT_PTR(k, 2 + i * 2 + 0);
- GET_VARIANT_PTR(v, 2 + i * 2 + 1);
- dict[*k] = *v;
- }
-
- GET_VARIANT_PTR(dst, 2 + argc * 2);
-
- *dst = dict;
-
- ip += 3 + argc * 2;
- }
- DISPATCH_OPCODE;
-
- OPCODE(OPCODE_CALL_ASYNC)
- OPCODE(OPCODE_CALL_RETURN)
- OPCODE(OPCODE_CALL) {
- CHECK_SPACE(4);
- bool call_ret = _code_ptr[ip] != OPCODE_CALL;
-#ifdef DEBUG_ENABLED
- bool call_async = _code_ptr[ip] == OPCODE_CALL_ASYNC;
-#endif
-
- int argc = _code_ptr[ip + 1];
- GET_VARIANT_PTR(base, 2);
- int nameg = _code_ptr[ip + 3];
-
- GD_ERR_BREAK(nameg < 0 || nameg >= _global_names_count);
- const StringName *methodname = &_global_names_ptr[nameg];
-
- GD_ERR_BREAK(argc < 0);
- ip += 4;
- CHECK_SPACE(argc + 1);
- Variant **argptrs = call_args;
-
- for (int i = 0; i < argc; i++) {
- GET_VARIANT_PTR(v, i);
- argptrs[i] = v;
- }
-
-#ifdef DEBUG_ENABLED
- uint64_t call_time = 0;
-
- if (GDScriptLanguage::get_singleton()->profiling) {
- call_time = OS::get_singleton()->get_ticks_usec();
- }
-
-#endif
- Callable::CallError err;
- if (call_ret) {
- GET_VARIANT_PTR(ret, argc);
- base->call_ptr(*methodname, (const Variant **)argptrs, argc, ret, err);
-#ifdef DEBUG_ENABLED
- if (!call_async && ret->get_type() == Variant::OBJECT) {
- // Check if getting a function state without await.
- bool was_freed = false;
- Object *obj = ret->get_validated_object_with_check(was_freed);
-
- if (was_freed) {
- err_text = "Got a freed object as a result of the call.";
- OPCODE_BREAK;
- }
- if (obj && obj->is_class_ptr(GDScriptFunctionState::get_class_ptr_static())) {
- err_text = R"(Trying to call an async function without "await".)";
- OPCODE_BREAK;
- }
- }
-#endif
- } else {
- base->call_ptr(*methodname, (const Variant **)argptrs, argc, nullptr, err);
- }
-#ifdef DEBUG_ENABLED
- if (GDScriptLanguage::get_singleton()->profiling) {
- function_call_time += OS::get_singleton()->get_ticks_usec() - call_time;
- }
-
- if (err.error != Callable::CallError::CALL_OK) {
- String methodstr = *methodname;
- String basestr = _get_var_type(base);
-
- if (methodstr == "call") {
- if (argc >= 1) {
- methodstr = String(*argptrs[0]) + " (via call)";
- if (err.error == Callable::CallError::CALL_ERROR_INVALID_ARGUMENT) {
- err.argument += 1;
- }
- }
- } else if (methodstr == "free") {
- if (err.error == Callable::CallError::CALL_ERROR_INVALID_METHOD) {
- if (base->is_ref()) {
- err_text = "Attempted to free a reference.";
- OPCODE_BREAK;
- } else if (base->get_type() == Variant::OBJECT) {
- err_text = "Attempted to free a locked object (calling or emitting).";
- OPCODE_BREAK;
- }
- }
- }
- err_text = _get_call_error(err, "function '" + methodstr + "' in base '" + basestr + "'", (const Variant **)argptrs);
- OPCODE_BREAK;
- }
-#endif
-
- //_call_func(nullptr,base,*methodname,ip,argc,p_instance,stack);
- ip += argc + 1;
- }
- DISPATCH_OPCODE;
-
- OPCODE(OPCODE_CALL_BUILT_IN) {
- CHECK_SPACE(4);
-
- GDScriptFunctions::Function func = GDScriptFunctions::Function(_code_ptr[ip + 1]);
- int argc = _code_ptr[ip + 2];
- GD_ERR_BREAK(argc < 0);
-
- ip += 3;
- CHECK_SPACE(argc + 1);
- Variant **argptrs = call_args;
-
- for (int i = 0; i < argc; i++) {
- GET_VARIANT_PTR(v, i);
- argptrs[i] = v;
- }
-
- GET_VARIANT_PTR(dst, argc);
-
- Callable::CallError err;
-
- GDScriptFunctions::call(func, (const Variant **)argptrs, argc, *dst, err);
-
-#ifdef DEBUG_ENABLED
- if (err.error != Callable::CallError::CALL_OK) {
- String methodstr = GDScriptFunctions::get_func_name(func);
- if (dst->get_type() == Variant::STRING) {
- //call provided error string
- err_text = "Error calling built-in function '" + methodstr + "': " + String(*dst);
- } else {
- err_text = _get_call_error(err, "built-in function '" + methodstr + "'", (const Variant **)argptrs);
- }
- OPCODE_BREAK;
- }
-#endif
- ip += argc + 1;
- }
- DISPATCH_OPCODE;
-
- OPCODE(OPCODE_CALL_SELF_BASE) {
- CHECK_SPACE(2);
- int self_fun = _code_ptr[ip + 1];
-
-#ifdef DEBUG_ENABLED
- if (self_fun < 0 || self_fun >= _global_names_count) {
- err_text = "compiler bug, function name not found";
- OPCODE_BREAK;
- }
-#endif
- const StringName *methodname = &_global_names_ptr[self_fun];
-
- int argc = _code_ptr[ip + 2];
-
- CHECK_SPACE(2 + argc + 1);
-
- Variant **argptrs = call_args;
-
- for (int i = 0; i < argc; i++) {
- GET_VARIANT_PTR(v, i + 3);
- argptrs[i] = v;
- }
-
- GET_VARIANT_PTR(dst, argc + 3);
-
- const GDScript *gds = _script;
-
- const Map<StringName, GDScriptFunction *>::Element *E = nullptr;
- while (gds->base.ptr()) {
- gds = gds->base.ptr();
- E = gds->member_functions.find(*methodname);
- if (E) {
- break;
- }
- }
-
- Callable::CallError err;
-
- if (E) {
- *dst = E->get()->call(p_instance, (const Variant **)argptrs, argc, err);
- } else if (gds->native.ptr()) {
- if (*methodname != GDScriptLanguage::get_singleton()->strings._init) {
- MethodBind *mb = ClassDB::get_method(gds->native->get_name(), *methodname);
- if (!mb) {
- err.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
- } else {
- *dst = mb->call(p_instance->owner, (const Variant **)argptrs, argc, err);
- }
- } else {
- err.error = Callable::CallError::CALL_OK;
- }
- } else {
- if (*methodname != GDScriptLanguage::get_singleton()->strings._init) {
- err.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
- } else {
- err.error = Callable::CallError::CALL_OK;
- }
- }
-
- if (err.error != Callable::CallError::CALL_OK) {
- String methodstr = *methodname;
- err_text = _get_call_error(err, "function '" + methodstr + "'", (const Variant **)argptrs);
-
- OPCODE_BREAK;
- }
-
- ip += 4 + argc;
- }
- DISPATCH_OPCODE;
-
- OPCODE(OPCODE_AWAIT) {
- CHECK_SPACE(2);
-
- //do the oneshot connect
- GET_VARIANT_PTR(argobj, 1);
-
- Signal sig;
- bool is_signal = true;
-
- {
- Variant result = *argobj;
-
- if (argobj->get_type() == Variant::OBJECT) {
- bool was_freed = false;
- Object *obj = argobj->get_validated_object_with_check(was_freed);
-
- if (was_freed) {
- err_text = "Trying to await on a freed object.";
- OPCODE_BREAK;
- }
-
- // Is this even possible to be null at this point?
- if (obj) {
- if (obj->is_class_ptr(GDScriptFunctionState::get_class_ptr_static())) {
- static StringName completed = _scs_create("completed");
- result = Signal(obj, completed);
- }
- }
- }
-
- if (result.get_type() != Variant::SIGNAL) {
- ip += 4; // Skip OPCODE_AWAIT_RESUME and its data.
- // The stack pointer should be the same, so we don't need to set a return value.
- is_signal = false;
- } else {
- sig = result;
- }
- }
-
- if (is_signal) {
- Ref<GDScriptFunctionState> gdfs = memnew(GDScriptFunctionState);
- gdfs->function = this;
-
- gdfs->state.stack.resize(alloca_size);
- //copy variant stack
- for (int i = 0; i < _stack_size; i++) {
- memnew_placement(&gdfs->state.stack.write[sizeof(Variant) * i], Variant(stack[i]));
- }
- gdfs->state.stack_size = _stack_size;
- gdfs->state.self = self;
- gdfs->state.alloca_size = alloca_size;
- gdfs->state.ip = ip + 2;
- gdfs->state.line = line;
- gdfs->state.script = _script;
- {
- MutexLock lock(GDScriptLanguage::get_singleton()->lock);
- _script->pending_func_states.add(&gdfs->scripts_list);
- if (p_instance) {
- gdfs->state.instance = p_instance;
- p_instance->pending_func_states.add(&gdfs->instances_list);
- } else {
- gdfs->state.instance = nullptr;
- }
- }
-#ifdef DEBUG_ENABLED
- gdfs->state.function_name = name;
- gdfs->state.script_path = _script->get_path();
-#endif
- gdfs->state.defarg = defarg;
- gdfs->function = this;
-
- retvalue = gdfs;
-
- Error err = sig.connect(Callable(gdfs.ptr(), "_signal_callback"), varray(gdfs), Object::CONNECT_ONESHOT);
- if (err != OK) {
- err_text = "Error connecting to signal: " + sig.get_name() + " during await.";
- OPCODE_BREAK;
- }
-
-#ifdef DEBUG_ENABLED
- exit_ok = true;
- awaited = true;
-#endif
- OPCODE_BREAK;
- }
- }
- DISPATCH_OPCODE; // Needed for synchronous calls (when result is immediately available).
-
- OPCODE(OPCODE_AWAIT_RESUME) {
- CHECK_SPACE(2);
-#ifdef DEBUG_ENABLED
- if (!p_state) {
- err_text = ("Invalid Resume (bug?)");
- OPCODE_BREAK;
- }
-#endif
- GET_VARIANT_PTR(result, 1);
- *result = p_state->result;
- ip += 2;
- }
- DISPATCH_OPCODE;
-
- OPCODE(OPCODE_JUMP) {
- CHECK_SPACE(2);
- int to = _code_ptr[ip + 1];
-
- GD_ERR_BREAK(to < 0 || to > _code_size);
- ip = to;
- }
- DISPATCH_OPCODE;
-
- OPCODE(OPCODE_JUMP_IF) {
- CHECK_SPACE(3);
-
- GET_VARIANT_PTR(test, 1);
-
- bool result = test->booleanize();
-
- if (result) {
- int to = _code_ptr[ip + 2];
- GD_ERR_BREAK(to < 0 || to > _code_size);
- ip = to;
- } else {
- ip += 3;
- }
- }
- DISPATCH_OPCODE;
-
- OPCODE(OPCODE_JUMP_IF_NOT) {
- CHECK_SPACE(3);
-
- GET_VARIANT_PTR(test, 1);
-
- bool result = test->booleanize();
-
- if (!result) {
- int to = _code_ptr[ip + 2];
- GD_ERR_BREAK(to < 0 || to > _code_size);
- ip = to;
- } else {
- ip += 3;
- }
- }
- DISPATCH_OPCODE;
-
- OPCODE(OPCODE_JUMP_TO_DEF_ARGUMENT) {
- CHECK_SPACE(2);
- ip = _default_arg_ptr[defarg];
- }
- DISPATCH_OPCODE;
-
- OPCODE(OPCODE_RETURN) {
- CHECK_SPACE(2);
- GET_VARIANT_PTR(r, 1);
- retvalue = *r;
-#ifdef DEBUG_ENABLED
- exit_ok = true;
-#endif
- OPCODE_BREAK;
- }
-
- OPCODE(OPCODE_ITERATE_BEGIN) {
- CHECK_SPACE(8); //space for this a regular iterate
-
- GET_VARIANT_PTR(counter, 1);
- GET_VARIANT_PTR(container, 2);
-
- bool valid;
- if (!container->iter_init(*counter, valid)) {
-#ifdef DEBUG_ENABLED
- if (!valid) {
- err_text = "Unable to iterate on object of type '" + Variant::get_type_name(container->get_type()) + "'.";
- OPCODE_BREAK;
- }
-#endif
- int jumpto = _code_ptr[ip + 3];
- GD_ERR_BREAK(jumpto < 0 || jumpto > _code_size);
- ip = jumpto;
- } else {
- GET_VARIANT_PTR(iterator, 4);
-
- *iterator = container->iter_get(*counter, valid);
-#ifdef DEBUG_ENABLED
- if (!valid) {
- err_text = "Unable to obtain iterator object of type '" + Variant::get_type_name(container->get_type()) + "'.";
- OPCODE_BREAK;
- }
-#endif
- ip += 5; //skip regular iterate which is always next
- }
- }
- DISPATCH_OPCODE;
-
- OPCODE(OPCODE_ITERATE) {
- CHECK_SPACE(4);
-
- GET_VARIANT_PTR(counter, 1);
- GET_VARIANT_PTR(container, 2);
-
- bool valid;
- if (!container->iter_next(*counter, valid)) {
-#ifdef DEBUG_ENABLED
- if (!valid) {
- err_text = "Unable to iterate on object of type '" + Variant::get_type_name(container->get_type()) + "' (type changed since first iteration?).";
- OPCODE_BREAK;
- }
-#endif
- int jumpto = _code_ptr[ip + 3];
- GD_ERR_BREAK(jumpto < 0 || jumpto > _code_size);
- ip = jumpto;
- } else {
- GET_VARIANT_PTR(iterator, 4);
-
- *iterator = container->iter_get(*counter, valid);
-#ifdef DEBUG_ENABLED
- if (!valid) {
- err_text = "Unable to obtain iterator object of type '" + Variant::get_type_name(container->get_type()) + "' (but was obtained on first iteration?).";
- OPCODE_BREAK;
- }
-#endif
- ip += 5; //loop again
- }
- }
- DISPATCH_OPCODE;
-
- OPCODE(OPCODE_ASSERT) {
- CHECK_SPACE(3);
-
-#ifdef DEBUG_ENABLED
- GET_VARIANT_PTR(test, 1);
- bool result = test->booleanize();
-
- if (!result) {
- String message_str;
- if (_code_ptr[ip + 2] != 0) {
- GET_VARIANT_PTR(message, 2);
- message_str = *message;
- }
- if (message_str.empty()) {
- err_text = "Assertion failed.";
- } else {
- err_text = "Assertion failed: " + message_str;
- }
- OPCODE_BREAK;
- }
-
-#endif
- ip += 3;
- }
- DISPATCH_OPCODE;
-
- OPCODE(OPCODE_BREAKPOINT) {
-#ifdef DEBUG_ENABLED
- if (EngineDebugger::is_active()) {
- GDScriptLanguage::get_singleton()->debug_break("Breakpoint Statement", true);
- }
-#endif
- ip += 1;
- }
- DISPATCH_OPCODE;
-
- OPCODE(OPCODE_LINE) {
- CHECK_SPACE(2);
-
- line = _code_ptr[ip + 1];
- ip += 2;
-
- if (EngineDebugger::is_active()) {
- // line
- bool do_break = false;
-
- if (EngineDebugger::get_script_debugger()->get_lines_left() > 0) {
- if (EngineDebugger::get_script_debugger()->get_depth() <= 0) {
- EngineDebugger::get_script_debugger()->set_lines_left(EngineDebugger::get_script_debugger()->get_lines_left() - 1);
- }
- if (EngineDebugger::get_script_debugger()->get_lines_left() <= 0) {
- do_break = true;
- }
- }
-
- if (EngineDebugger::get_script_debugger()->is_breakpoint(line, source)) {
- do_break = true;
- }
-
- if (do_break) {
- GDScriptLanguage::get_singleton()->debug_break("Breakpoint", true);
- }
-
- EngineDebugger::get_singleton()->line_poll();
- }
- }
- DISPATCH_OPCODE;
-
- OPCODE(OPCODE_END) {
-#ifdef DEBUG_ENABLED
- exit_ok = true;
-#endif
- OPCODE_BREAK;
- }
-
-#if 0 // Enable for debugging.
- default: {
- err_text = "Illegal opcode " + itos(_code_ptr[ip]) + " at address " + itos(ip);
- OPCODE_BREAK;
- }
-#endif
- }
-
- OPCODES_END
-#ifdef DEBUG_ENABLED
- if (exit_ok) {
- OPCODE_OUT;
- }
- //error
- // function, file, line, error, explanation
- String err_file;
- if (p_instance && ObjectDB::get_instance(p_instance->owner_id) != nullptr && p_instance->script->is_valid() && p_instance->script->path != "") {
- err_file = p_instance->script->path;
- } else if (script) {
- err_file = script->path;
- }
- if (err_file == "") {
- err_file = "<built-in>";
- }
- String err_func = name;
- if (p_instance && ObjectDB::get_instance(p_instance->owner_id) != nullptr && p_instance->script->is_valid() && p_instance->script->name != "") {
- err_func = p_instance->script->name + "." + err_func;
- }
- int err_line = line;
- if (err_text == "") {
- err_text = "Internal Script Error! - opcode #" + itos(last_opcode) + " (report please).";
- }
-
- if (!GDScriptLanguage::get_singleton()->debug_break(err_text, false)) {
- // debugger break did not happen
-
- _err_print_error(err_func.utf8().get_data(), err_file.utf8().get_data(), err_line, err_text.utf8().get_data(), ERR_HANDLER_SCRIPT);
- }
-
-#endif
- OPCODE_OUT;
- }
-
- OPCODES_OUT
-#ifdef DEBUG_ENABLED
- if (GDScriptLanguage::get_singleton()->profiling) {
- uint64_t time_taken = OS::get_singleton()->get_ticks_usec() - function_start_time;
- profile.total_time += time_taken;
- profile.self_time += time_taken - function_call_time;
- profile.frame_total_time += time_taken;
- profile.frame_self_time += time_taken - function_call_time;
- GDScriptLanguage::get_singleton()->script_frame_time += time_taken - function_call_time;
- }
-
- // Check if this is the last time the function is resuming from await
- // Will be true if never awaited as well
- // When it's the last resume it will postpone the exit from stack,
- // so the debugger knows which function triggered the resume of the next function (if any)
- if (!p_state || awaited) {
- if (EngineDebugger::is_active()) {
- GDScriptLanguage::get_singleton()->exit_function();
- }
-#endif
-
- if (_stack_size) {
- //free stack
- for (int i = 0; i < _stack_size; i++) {
- stack[i].~Variant();
- }
- }
-
-#ifdef DEBUG_ENABLED
- }
-#endif
-
- return retvalue;
-}
const int *GDScriptFunction::get_code() const {
return _code_ptr;
@@ -1697,31 +139,13 @@ void GDScriptFunction::debug_get_stack_member_state(int p_line, List<Pair<String
}
}
-GDScriptFunction::GDScriptFunction() :
- function_list(this) {
- _stack_size = 0;
- _call_size = 0;
- rpc_mode = MultiplayerAPI::RPC_MODE_DISABLED;
+GDScriptFunction::GDScriptFunction() {
name = "<anonymous>";
#ifdef DEBUG_ENABLED
- _func_cname = nullptr;
-
{
MutexLock lock(GDScriptLanguage::get_singleton()->lock);
-
GDScriptLanguage::get_singleton()->function_list.add(&function_list);
}
-
- profile.call_count = 0;
- profile.self_time = 0;
- profile.total_time = 0;
- profile.frame_call_count = 0;
- profile.frame_self_time = 0;
- profile.frame_total_time = 0;
- profile.last_frame_call_count = 0;
- profile.last_frame_self_time = 0;
- profile.last_frame_total_time = 0;
-
#endif
}
@@ -1882,506 +306,3 @@ GDScriptFunctionState::~GDScriptFunctionState() {
instances_list.remove_from_list();
}
}
-
-#ifdef DEBUG_ENABLED
-static String _get_variant_string(const Variant &p_variant) {
- String txt;
- if (p_variant.get_type() == Variant::STRING) {
- txt = "\"" + String(p_variant) + "\"";
- } else if (p_variant.get_type() == Variant::STRING_NAME) {
- txt = "&\"" + String(p_variant) + "\"";
- } else if (p_variant.get_type() == Variant::NODE_PATH) {
- txt = "^\"" + String(p_variant) + "\"";
- } else if (p_variant.get_type() == Variant::OBJECT) {
- Object *obj = p_variant;
- if (!obj) {
- txt = "null";
- } else {
- GDScriptNativeClass *cls = Object::cast_to<GDScriptNativeClass>(obj);
- if (cls) {
- txt += cls->get_name();
- txt += " (class)";
- } else {
- txt = obj->get_class();
- if (obj->get_script_instance()) {
- txt += "(" + obj->get_script_instance()->get_script()->get_path() + ")";
- }
- }
- }
- } else {
- txt = p_variant;
- }
- return txt;
-}
-
-static String _disassemble_address(const GDScript *p_script, const GDScriptFunction &p_function, int p_address) {
- int addr = p_address & GDScriptFunction::ADDR_MASK;
-
- switch (p_address >> GDScriptFunction::ADDR_BITS) {
- case GDScriptFunction::ADDR_TYPE_SELF: {
- return "self";
- } break;
- case GDScriptFunction::ADDR_TYPE_CLASS: {
- return "class";
- } break;
- case GDScriptFunction::ADDR_TYPE_MEMBER: {
- return "member(" + p_script->debug_get_member_by_index(addr) + ")";
- } break;
- case GDScriptFunction::ADDR_TYPE_CLASS_CONSTANT: {
- return "class_const(" + p_function.get_global_name(addr) + ")";
- } break;
- case GDScriptFunction::ADDR_TYPE_LOCAL_CONSTANT: {
- return "const(" + _get_variant_string(p_function.get_constant(addr)) + ")";
- } break;
- case GDScriptFunction::ADDR_TYPE_STACK: {
- return "stack(" + itos(addr) + ")";
- } break;
- case GDScriptFunction::ADDR_TYPE_STACK_VARIABLE: {
- return "var_stack(" + itos(addr) + ")";
- } break;
- case GDScriptFunction::ADDR_TYPE_GLOBAL: {
- return "global(" + _get_variant_string(GDScriptLanguage::get_singleton()->get_global_array()[addr]) + ")";
- } break;
- case GDScriptFunction::ADDR_TYPE_NAMED_GLOBAL: {
- return "named_global(" + p_function.get_global_name(addr) + ")";
- } break;
- case GDScriptFunction::ADDR_TYPE_NIL: {
- return "nil";
- } break;
- }
-
- return "<err>";
-}
-
-void GDScriptFunction::disassemble(const Vector<String> &p_code_lines) const {
-#define DADDR(m_ip) (_disassemble_address(_script, *this, _code_ptr[ip + m_ip]))
-
- for (int ip = 0; ip < _code_size;) {
- StringBuilder text;
- int incr = 0;
-
- text += " ";
- text += itos(ip);
- text += ": ";
-
- // This makes the compiler complain if some opcode is unchecked in the switch.
- Opcode code = Opcode(_code_ptr[ip]);
-
- switch (code) {
- case OPCODE_OPERATOR: {
- int operation = _code_ptr[ip + 1];
-
- text += "operator ";
-
- text += DADDR(4);
- text += " = ";
- text += DADDR(2);
- text += " ";
- text += Variant::get_operator_name(Variant::Operator(operation));
- text += " ";
- text += DADDR(3);
-
- incr += 5;
- } break;
- case OPCODE_EXTENDS_TEST: {
- text += "is object ";
- text += DADDR(3);
- text += " = ";
- text += DADDR(1);
- text += " is ";
- text += DADDR(2);
-
- incr += 4;
- } break;
- case OPCODE_IS_BUILTIN: {
- text += "is builtin ";
- text += DADDR(3);
- text += " = ";
- text += DADDR(1);
- text += " is ";
- text += Variant::get_type_name(Variant::Type(_code_ptr[ip + 2]));
-
- incr += 4;
- } break;
- case OPCODE_SET: {
- text += "set ";
- text += DADDR(1);
- text += "[";
- text += DADDR(2);
- text += "] = ";
- text += DADDR(3);
-
- incr += 4;
- } break;
- case OPCODE_GET: {
- text += "get ";
- text += DADDR(3);
- text += " = ";
- text += DADDR(1);
- text += "[";
- text += DADDR(2);
- text += "]";
-
- incr += 4;
- } break;
- case OPCODE_SET_NAMED: {
- text += "set_named ";
- text += DADDR(1);
- text += "[\"";
- text += _global_names_ptr[_code_ptr[ip + 2]];
- text += "\"] = ";
- text += DADDR(3);
-
- incr += 4;
- } break;
- case OPCODE_GET_NAMED: {
- text += "get_named ";
- text += DADDR(3);
- text += " = ";
- text += DADDR(1);
- text += "[\"";
- text += _global_names_ptr[_code_ptr[ip + 2]];
- text += "\"]";
-
- incr += 4;
- } break;
- case OPCODE_SET_MEMBER: {
- text += "set_member ";
- text += "[\"";
- text += _global_names_ptr[_code_ptr[ip + 1]];
- text += "\"] = ";
- text += DADDR(2);
-
- incr += 3;
- } break;
- case OPCODE_GET_MEMBER: {
- text += "get_member ";
- text += DADDR(2);
- text += " = ";
- text += "[\"";
- text += _global_names_ptr[_code_ptr[ip + 1]];
- text += "\"]";
-
- incr += 3;
- } break;
- case OPCODE_ASSIGN: {
- text += "assign ";
- text += DADDR(1);
- text += " = ";
- text += DADDR(2);
-
- incr += 3;
- } break;
- case OPCODE_ASSIGN_TRUE: {
- text += "assign ";
- text += DADDR(1);
- text += " = true";
-
- incr += 2;
- } break;
- case OPCODE_ASSIGN_FALSE: {
- text += "assign ";
- text += DADDR(1);
- text += " = false";
-
- incr += 2;
- } break;
- case OPCODE_ASSIGN_TYPED_BUILTIN: {
- text += "assign typed builtin (";
- text += Variant::get_type_name((Variant::Type)_code_ptr[ip + 1]);
- text += ") ";
- text += DADDR(2);
- text += " = ";
- text += DADDR(3);
-
- incr += 4;
- } break;
- case OPCODE_ASSIGN_TYPED_NATIVE: {
- Variant class_name = _constants_ptr[_code_ptr[ip + 1]];
- GDScriptNativeClass *nc = Object::cast_to<GDScriptNativeClass>(class_name.operator Object *());
-
- text += "assign typed native (";
- text += nc->get_name().operator String();
- text += ") ";
- text += DADDR(2);
- text += " = ";
- text += DADDR(3);
-
- incr += 4;
- } break;
- case OPCODE_ASSIGN_TYPED_SCRIPT: {
- Variant script = _constants_ptr[_code_ptr[ip + 1]];
- Script *sc = Object::cast_to<Script>(script.operator Object *());
-
- text += "assign typed script (";
- text += sc->get_path();
- text += ") ";
- text += DADDR(2);
- text += " = ";
- text += DADDR(3);
-
- incr += 4;
- } break;
- case OPCODE_CAST_TO_BUILTIN: {
- text += "cast builtin ";
- text += DADDR(3);
- text += " = ";
- text += DADDR(2);
- text += " as ";
- text += Variant::get_type_name(Variant::Type(_code_ptr[ip + 1]));
-
- incr += 4;
- } break;
- case OPCODE_CAST_TO_NATIVE: {
- Variant class_name = _constants_ptr[_code_ptr[ip + 1]];
- GDScriptNativeClass *nc = Object::cast_to<GDScriptNativeClass>(class_name.operator Object *());
-
- text += "cast native ";
- text += DADDR(3);
- text += " = ";
- text += DADDR(2);
- text += " as ";
- text += nc->get_name();
-
- incr += 4;
- } break;
- case OPCODE_CAST_TO_SCRIPT: {
- text += "cast ";
- text += DADDR(3);
- text += " = ";
- text += DADDR(2);
- text += " as ";
- text += DADDR(1);
-
- incr += 4;
- } break;
- case OPCODE_CONSTRUCT: {
- Variant::Type t = Variant::Type(_code_ptr[ip + 1]);
- int argc = _code_ptr[ip + 2];
-
- text += "construct ";
- text += DADDR(3 + argc);
- text += " = ";
-
- text += Variant::get_type_name(t) + "(";
- for (int i = 0; i < argc; i++) {
- if (i > 0)
- text += ", ";
- text += DADDR(i + 3);
- }
- text += ")";
-
- incr = 4 + argc;
- } break;
- case OPCODE_CONSTRUCT_ARRAY: {
- int argc = _code_ptr[ip + 1];
- text += " make_array ";
- text += DADDR(2 + argc);
- text += " = [";
-
- for (int i = 0; i < argc; i++) {
- if (i > 0)
- text += ", ";
- text += DADDR(2 + i);
- }
-
- text += "]";
-
- incr += 3 + argc;
- } break;
- case OPCODE_CONSTRUCT_DICTIONARY: {
- int argc = _code_ptr[ip + 1];
- text += "make_dict ";
- text += DADDR(2 + argc * 2);
- text += " = {";
-
- for (int i = 0; i < argc; i++) {
- if (i > 0)
- text += ", ";
- text += DADDR(2 + i * 2 + 0);
- text += ": ";
- text += DADDR(2 + i * 2 + 1);
- }
-
- text += "}";
-
- incr += 3 + argc * 2;
- } break;
- case OPCODE_CALL:
- case OPCODE_CALL_RETURN:
- case OPCODE_CALL_ASYNC: {
- bool ret = _code_ptr[ip] == OPCODE_CALL_RETURN;
- bool async = _code_ptr[ip] == OPCODE_CALL_ASYNC;
-
- if (ret) {
- text += "call-ret ";
- } else if (async) {
- text += "call-async ";
- } else {
- text += "call ";
- }
-
- int argc = _code_ptr[ip + 1];
- if (ret || async) {
- text += DADDR(4 + argc) + " = ";
- }
-
- text += DADDR(2) + ".";
- text += String(_global_names_ptr[_code_ptr[ip + 3]]);
- text += "(";
-
- for (int i = 0; i < argc; i++) {
- if (i > 0)
- text += ", ";
- text += DADDR(4 + i);
- }
- text += ")";
-
- incr = 5 + argc;
- } break;
- case OPCODE_CALL_BUILT_IN: {
- text += "call-built-in ";
-
- int argc = _code_ptr[ip + 2];
- text += DADDR(3 + argc) + " = ";
-
- text += GDScriptFunctions::get_func_name(GDScriptFunctions::Function(_code_ptr[ip + 1]));
- text += "(";
-
- for (int i = 0; i < argc; i++) {
- if (i > 0)
- text += ", ";
- text += DADDR(3 + i);
- }
- text += ")";
-
- incr = 4 + argc;
- } break;
- case OPCODE_CALL_SELF_BASE: {
- text += "call-self-base ";
-
- int argc = _code_ptr[ip + 2];
- text += DADDR(3 + argc) + " = ";
-
- text += _global_names_ptr[_code_ptr[ip + 1]];
- text += "(";
-
- for (int i = 0; i < argc; i++) {
- if (i > 0)
- text += ", ";
- text += DADDR(3 + i);
- }
- text += ")";
-
- incr = 4 + argc;
- } break;
- case OPCODE_AWAIT: {
- text += "await ";
- text += DADDR(1);
-
- incr += 2;
- } break;
- case OPCODE_AWAIT_RESUME: {
- text += "await resume ";
- text += DADDR(1);
-
- incr = 2;
- } break;
- case OPCODE_JUMP: {
- text += "jump ";
- text += itos(_code_ptr[ip + 1]);
-
- incr = 2;
- } break;
- case OPCODE_JUMP_IF: {
- text += "jump-if ";
- text += DADDR(1);
- text += " to ";
- text += itos(_code_ptr[ip + 2]);
-
- incr = 3;
- } break;
- case OPCODE_JUMP_IF_NOT: {
- text += "jump-if-not ";
- text += DADDR(1);
- text += " to ";
- text += itos(_code_ptr[ip + 2]);
-
- incr = 3;
- } break;
- case OPCODE_JUMP_TO_DEF_ARGUMENT: {
- text += "jump-to-default-argument ";
-
- incr = 1;
- } break;
- case OPCODE_RETURN: {
- text += "return ";
- text += DADDR(1);
-
- incr = 2;
- } break;
- case OPCODE_ITERATE_BEGIN: {
- text += "for-init ";
- text += DADDR(4);
- text += " in ";
- text += DADDR(2);
- text += " counter ";
- text += DADDR(1);
- text += " end ";
- text += itos(_code_ptr[ip + 3]);
-
- incr += 5;
- } break;
- case OPCODE_ITERATE: {
- text += "for-loop ";
- text += DADDR(4);
- text += " in ";
- text += DADDR(2);
- text += " counter ";
- text += DADDR(1);
- text += " end ";
- text += itos(_code_ptr[ip + 3]);
-
- incr += 5;
- } break;
- case OPCODE_LINE: {
- int line = _code_ptr[ip + 1] - 1;
- if (line >= 0 && line < p_code_lines.size()) {
- text += "line ";
- text += itos(line + 1);
- text += ": ";
- text += p_code_lines[line];
- } else {
- text += "";
- }
-
- incr += 2;
- } break;
- case OPCODE_ASSERT: {
- text += "assert (";
- text += DADDR(1);
- text += ", ";
- text += DADDR(2);
- text += ")";
-
- incr += 3;
- } break;
- case OPCODE_BREAKPOINT: {
- text += "breakpoint";
-
- incr += 1;
- } break;
- case OPCODE_END: {
- text += "== END ==";
-
- incr += 1;
- } break;
- }
-
- ip += incr;
- if (text.get_string_length() > 0) {
- print_line(text.as_string());
- }
- }
-}
-#endif
diff --git a/modules/gdscript/gdscript_function.h b/modules/gdscript/gdscript_function.h
index c98ac09310..bb5cc1284d 100644
--- a/modules/gdscript/gdscript_function.h
+++ b/modules/gdscript/gdscript_function.h
@@ -31,13 +31,13 @@
#ifndef GDSCRIPT_FUNCTION_H
#define GDSCRIPT_FUNCTION_H
+#include "core/object/reference.h"
+#include "core/object/script_language.h"
#include "core/os/thread.h"
-#include "core/pair.h"
-#include "core/reference.h"
-#include "core/script_language.h"
-#include "core/self_list.h"
-#include "core/string_name.h"
-#include "core/variant.h"
+#include "core/string/string_name.h"
+#include "core/templates/pair.h"
+#include "core/templates/self_list.h"
+#include "core/variant/variant.h"
class GDScriptInstance;
class GDScript;
@@ -159,12 +159,19 @@ class GDScriptFunction {
public:
enum Opcode {
OPCODE_OPERATOR,
+ OPCODE_OPERATOR_VALIDATED,
OPCODE_EXTENDS_TEST,
OPCODE_IS_BUILTIN,
- OPCODE_SET,
- OPCODE_GET,
+ OPCODE_SET_KEYED,
+ OPCODE_SET_KEYED_VALIDATED,
+ OPCODE_SET_INDEXED_VALIDATED,
+ OPCODE_GET_KEYED,
+ OPCODE_GET_KEYED_VALIDATED,
+ OPCODE_GET_INDEXED_VALIDATED,
OPCODE_SET_NAMED,
+ OPCODE_SET_NAMED_VALIDATED,
OPCODE_GET_NAMED,
+ OPCODE_GET_NAMED_VALIDATED,
OPCODE_SET_MEMBER,
OPCODE_GET_MEMBER,
OPCODE_ASSIGN,
@@ -176,14 +183,54 @@ public:
OPCODE_CAST_TO_BUILTIN,
OPCODE_CAST_TO_NATIVE,
OPCODE_CAST_TO_SCRIPT,
- OPCODE_CONSTRUCT, //only for basic types!!
+ OPCODE_CONSTRUCT, // Only for basic types!
+ OPCODE_CONSTRUCT_VALIDATED, // Only for basic types!
OPCODE_CONSTRUCT_ARRAY,
OPCODE_CONSTRUCT_DICTIONARY,
OPCODE_CALL,
OPCODE_CALL_RETURN,
OPCODE_CALL_ASYNC,
OPCODE_CALL_BUILT_IN,
+ OPCODE_CALL_BUILTIN_TYPE_VALIDATED,
OPCODE_CALL_SELF_BASE,
+ OPCODE_CALL_METHOD_BIND,
+ OPCODE_CALL_METHOD_BIND_RET,
+ // ptrcall have one instruction per return type.
+ OPCODE_CALL_PTRCALL_NO_RETURN,
+ OPCODE_CALL_PTRCALL_BOOL,
+ OPCODE_CALL_PTRCALL_INT,
+ OPCODE_CALL_PTRCALL_FLOAT,
+ OPCODE_CALL_PTRCALL_STRING,
+ OPCODE_CALL_PTRCALL_VECTOR2,
+ OPCODE_CALL_PTRCALL_VECTOR2I,
+ OPCODE_CALL_PTRCALL_RECT2,
+ OPCODE_CALL_PTRCALL_RECT2I,
+ OPCODE_CALL_PTRCALL_VECTOR3,
+ OPCODE_CALL_PTRCALL_VECTOR3I,
+ OPCODE_CALL_PTRCALL_TRANSFORM2D,
+ OPCODE_CALL_PTRCALL_PLANE,
+ OPCODE_CALL_PTRCALL_QUAT,
+ OPCODE_CALL_PTRCALL_AABB,
+ OPCODE_CALL_PTRCALL_BASIS,
+ OPCODE_CALL_PTRCALL_TRANSFORM,
+ OPCODE_CALL_PTRCALL_COLOR,
+ OPCODE_CALL_PTRCALL_STRING_NAME,
+ OPCODE_CALL_PTRCALL_NODE_PATH,
+ OPCODE_CALL_PTRCALL_RID,
+ OPCODE_CALL_PTRCALL_OBJECT,
+ OPCODE_CALL_PTRCALL_CALLABLE,
+ OPCODE_CALL_PTRCALL_SIGNAL,
+ OPCODE_CALL_PTRCALL_DICTIONARY,
+ OPCODE_CALL_PTRCALL_ARRAY,
+ OPCODE_CALL_PTRCALL_PACKED_BYTE_ARRAY,
+ OPCODE_CALL_PTRCALL_PACKED_INT32_ARRAY,
+ OPCODE_CALL_PTRCALL_PACKED_INT64_ARRAY,
+ OPCODE_CALL_PTRCALL_PACKED_FLOAT32_ARRAY,
+ OPCODE_CALL_PTRCALL_PACKED_FLOAT64_ARRAY,
+ OPCODE_CALL_PTRCALL_PACKED_STRING_ARRAY,
+ OPCODE_CALL_PTRCALL_PACKED_VECTOR2_ARRAY,
+ OPCODE_CALL_PTRCALL_PACKED_VECTOR3_ARRAY,
+ OPCODE_CALL_PTRCALL_PACKED_COLOR_ARRAY,
OPCODE_AWAIT,
OPCODE_AWAIT_RESUME,
OPCODE_JUMP,
@@ -192,7 +239,45 @@ public:
OPCODE_JUMP_TO_DEF_ARGUMENT,
OPCODE_RETURN,
OPCODE_ITERATE_BEGIN,
+ OPCODE_ITERATE_BEGIN_INT,
+ OPCODE_ITERATE_BEGIN_FLOAT,
+ OPCODE_ITERATE_BEGIN_VECTOR2,
+ OPCODE_ITERATE_BEGIN_VECTOR2I,
+ OPCODE_ITERATE_BEGIN_VECTOR3,
+ OPCODE_ITERATE_BEGIN_VECTOR3I,
+ OPCODE_ITERATE_BEGIN_STRING,
+ OPCODE_ITERATE_BEGIN_DICTIONARY,
+ OPCODE_ITERATE_BEGIN_ARRAY,
+ OPCODE_ITERATE_BEGIN_PACKED_BYTE_ARRAY,
+ OPCODE_ITERATE_BEGIN_PACKED_INT32_ARRAY,
+ OPCODE_ITERATE_BEGIN_PACKED_INT64_ARRAY,
+ OPCODE_ITERATE_BEGIN_PACKED_FLOAT32_ARRAY,
+ OPCODE_ITERATE_BEGIN_PACKED_FLOAT64_ARRAY,
+ OPCODE_ITERATE_BEGIN_PACKED_STRING_ARRAY,
+ OPCODE_ITERATE_BEGIN_PACKED_VECTOR2_ARRAY,
+ OPCODE_ITERATE_BEGIN_PACKED_VECTOR3_ARRAY,
+ OPCODE_ITERATE_BEGIN_PACKED_COLOR_ARRAY,
+ OPCODE_ITERATE_BEGIN_OBJECT,
OPCODE_ITERATE,
+ OPCODE_ITERATE_INT,
+ OPCODE_ITERATE_FLOAT,
+ OPCODE_ITERATE_VECTOR2,
+ OPCODE_ITERATE_VECTOR2I,
+ OPCODE_ITERATE_VECTOR3,
+ OPCODE_ITERATE_VECTOR3I,
+ OPCODE_ITERATE_STRING,
+ OPCODE_ITERATE_DICTIONARY,
+ OPCODE_ITERATE_ARRAY,
+ OPCODE_ITERATE_PACKED_BYTE_ARRAY,
+ OPCODE_ITERATE_PACKED_INT32_ARRAY,
+ OPCODE_ITERATE_PACKED_INT64_ARRAY,
+ OPCODE_ITERATE_PACKED_FLOAT32_ARRAY,
+ OPCODE_ITERATE_PACKED_FLOAT64_ARRAY,
+ OPCODE_ITERATE_PACKED_STRING_ARRAY,
+ OPCODE_ITERATE_PACKED_VECTOR2_ARRAY,
+ OPCODE_ITERATE_PACKED_VECTOR3_ARRAY,
+ OPCODE_ITERATE_PACKED_COLOR_ARRAY,
+ OPCODE_ITERATE_OBJECT,
OPCODE_ASSERT,
OPCODE_BREAKPOINT,
OPCODE_LINE,
@@ -215,6 +300,12 @@ public:
ADDR_TYPE_NIL = 9
};
+ enum Instruction {
+ INSTR_BITS = 20,
+ INSTR_MASK = ((1 << INSTR_BITS) - 1),
+ INSTR_ARGS_MASK = ~INSTR_MASK,
+ };
+
struct StackDebug {
int line;
int pos;
@@ -229,27 +320,59 @@ private:
StringName source;
mutable Variant nil;
- mutable Variant *_constants_ptr;
- int _constant_count;
- const StringName *_global_names_ptr;
- int _global_names_count;
- const int *_default_arg_ptr;
- int _default_arg_count;
- const int *_code_ptr;
- int _code_size;
- int _argument_count;
- int _stack_size;
- int _call_size;
- int _initial_line;
- bool _static;
- MultiplayerAPI::RPCMode rpc_mode;
-
- GDScript *_script;
+ mutable Variant *_constants_ptr = nullptr;
+ int _constant_count = 0;
+ const StringName *_global_names_ptr = nullptr;
+ int _global_names_count = 0;
+ const int *_default_arg_ptr = nullptr;
+ int _default_arg_count = 0;
+ int _operator_funcs_count = 0;
+ const Variant::ValidatedOperatorEvaluator *_operator_funcs_ptr = nullptr;
+ int _setters_count = 0;
+ const Variant::ValidatedSetter *_setters_ptr = nullptr;
+ int _getters_count = 0;
+ const Variant::ValidatedGetter *_getters_ptr = nullptr;
+ int _keyed_setters_count = 0;
+ const Variant::ValidatedKeyedSetter *_keyed_setters_ptr = nullptr;
+ int _keyed_getters_count = 0;
+ const Variant::ValidatedKeyedGetter *_keyed_getters_ptr = nullptr;
+ int _indexed_setters_count = 0;
+ const Variant::ValidatedIndexedSetter *_indexed_setters_ptr = nullptr;
+ int _indexed_getters_count = 0;
+ const Variant::ValidatedIndexedGetter *_indexed_getters_ptr = nullptr;
+ int _builtin_methods_count = 0;
+ const Variant::ValidatedBuiltInMethod *_builtin_methods_ptr = nullptr;
+ int _constructors_count = 0;
+ const Variant::ValidatedConstructor *_constructors_ptr = nullptr;
+ int _methods_count = 0;
+ MethodBind **_methods_ptr = nullptr;
+ const int *_code_ptr = nullptr;
+ int _code_size = 0;
+ int _argument_count = 0;
+ int _stack_size = 0;
+ int _instruction_args_size = 0;
+ int _ptrcall_args_size = 0;
+
+ int _initial_line = 0;
+ bool _static = false;
+ MultiplayerAPI::RPCMode rpc_mode = MultiplayerAPI::RPC_MODE_DISABLED;
+
+ GDScript *_script = nullptr;
StringName name;
Vector<Variant> constants;
Vector<StringName> global_names;
Vector<int> default_arguments;
+ Vector<Variant::ValidatedOperatorEvaluator> operator_funcs;
+ Vector<Variant::ValidatedSetter> setters;
+ Vector<Variant::ValidatedGetter> getters;
+ Vector<Variant::ValidatedKeyedSetter> keyed_setters;
+ Vector<Variant::ValidatedKeyedGetter> keyed_getters;
+ Vector<Variant::ValidatedIndexedSetter> indexed_setters;
+ Vector<Variant::ValidatedIndexedGetter> indexed_getters;
+ Vector<Variant::ValidatedBuiltInMethod> builtin_methods;
+ Vector<Variant::ValidatedConstructor> constructors;
+ Vector<MethodBind *> methods;
Vector<int> code;
Vector<GDScriptDataType> argument_types;
GDScriptDataType return_type;
@@ -265,22 +388,22 @@ private:
friend class GDScriptLanguage;
- SelfList<GDScriptFunction> function_list;
+ SelfList<GDScriptFunction> function_list{ this };
#ifdef DEBUG_ENABLED
CharString func_cname;
- const char *_func_cname;
+ const char *_func_cname = nullptr;
struct Profile {
StringName signature;
- uint64_t call_count;
- uint64_t self_time;
- uint64_t total_time;
- uint64_t frame_call_count;
- uint64_t frame_self_time;
- uint64_t frame_total_time;
- uint64_t last_frame_call_count;
- uint64_t last_frame_self_time;
- uint64_t last_frame_total_time;
+ uint64_t call_count = 0;
+ uint64_t self_time = 0;
+ uint64_t total_time = 0;
+ uint64_t frame_call_count = 0;
+ uint64_t frame_self_time = 0;
+ uint64_t frame_total_time = 0;
+ uint64_t last_frame_call_count = 0;
+ uint64_t last_frame_self_time = 0;
+ uint64_t last_frame_total_time = 0;
} profile;
#endif
diff --git a/modules/gdscript/gdscript_functions.cpp b/modules/gdscript/gdscript_functions.cpp
index 31ce63bc6e..3a7c1a8676 100644
--- a/modules/gdscript/gdscript_functions.cpp
+++ b/modules/gdscript/gdscript_functions.cpp
@@ -30,14 +30,13 @@
#include "gdscript_functions.h"
-#include "core/class_db.h"
-#include "core/func_ref.h"
#include "core/io/json.h"
#include "core/io/marshalls.h"
#include "core/math/math_funcs.h"
+#include "core/object/class_db.h"
+#include "core/object/reference.h"
#include "core/os/os.h"
-#include "core/reference.h"
-#include "core/variant_parser.h"
+#include "core/variant/variant_parser.h"
#include "gdscript.h"
const char *GDScriptFunctions::get_func_name(Function p_func) {
@@ -83,7 +82,8 @@ const char *GDScriptFunctions::get_func_name(Function p_func) {
"randomize",
"randi",
"randf",
- "rand_range",
+ "randf_range",
+ "randi_range",
"seed",
"rand_seed",
"deg2rad",
@@ -99,7 +99,6 @@ const char *GDScriptFunctions::get_func_name(Function p_func) {
"clamp",
"nearest_po2",
"weakref",
- "funcref",
"convert",
"typeof",
"type_exists",
@@ -286,7 +285,7 @@ void GDScriptFunctions::call(Function p_func, const Variant **p_args, int p_arg_
int64_t i = *p_args[0];
r_ret = i < 0 ? -1 : (i > 0 ? +1 : 0);
} else if (p_args[0]->get_type() == Variant::FLOAT) {
- real_t r = *p_args[0];
+ double r = *p_args[0];
r_ret = r < 0.0 ? -1.0 : (r > 0.0 ? +1.0 : 0.0);
} else {
r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
@@ -419,7 +418,7 @@ void GDScriptFunctions::call(Function p_func, const Variant **p_args, int p_arg_
Math::randomize();
r_ret = Variant();
} break;
- case MATH_RAND: {
+ case MATH_RANDI: {
VALIDATE_ARG_COUNT(0);
r_ret = Math::rand();
} break;
@@ -427,12 +426,18 @@ void GDScriptFunctions::call(Function p_func, const Variant **p_args, int p_arg_
VALIDATE_ARG_COUNT(0);
r_ret = Math::randf();
} break;
- case MATH_RANDOM: {
+ case MATH_RANDF_RANGE: {
VALIDATE_ARG_COUNT(2);
VALIDATE_ARG_NUM(0);
VALIDATE_ARG_NUM(1);
r_ret = Math::random((double)*p_args[0], (double)*p_args[1]);
} break;
+ case MATH_RANDI_RANGE: {
+ VALIDATE_ARG_COUNT(2);
+ VALIDATE_ARG_NUM(0);
+ VALIDATE_ARG_NUM(1);
+ r_ret = Math::random((int)*p_args[0], (int)*p_args[1]);
+ } break;
case MATH_SEED: {
VALIDATE_ARG_COUNT(1);
VALIDATE_ARG_NUM(0);
@@ -505,8 +510,8 @@ void GDScriptFunctions::call(Function p_func, const Variant **p_args, int p_arg_
VALIDATE_ARG_NUM(0);
VALIDATE_ARG_NUM(1);
- real_t a = *p_args[0];
- real_t b = *p_args[1];
+ double a = *p_args[0];
+ double b = *p_args[1];
r_ret = MAX(a, b);
}
@@ -522,8 +527,8 @@ void GDScriptFunctions::call(Function p_func, const Variant **p_args, int p_arg_
VALIDATE_ARG_NUM(0);
VALIDATE_ARG_NUM(1);
- real_t a = *p_args[0];
- real_t b = *p_args[1];
+ double a = *p_args[0];
+ double b = *p_args[1];
r_ret = MIN(a, b);
}
@@ -540,9 +545,9 @@ void GDScriptFunctions::call(Function p_func, const Variant **p_args, int p_arg_
VALIDATE_ARG_NUM(1);
VALIDATE_ARG_NUM(2);
- real_t a = *p_args[0];
- real_t b = *p_args[1];
- real_t c = *p_args[2];
+ double a = *p_args[0];
+ double b = *p_args[1];
+ double c = *p_args[2];
r_ret = CLAMP(a, b, c);
}
@@ -582,31 +587,6 @@ void GDScriptFunctions::call(Function p_func, const Variant **p_args, int p_arg_
return;
}
} break;
- case FUNC_FUNCREF: {
- VALIDATE_ARG_COUNT(2);
- 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;
- r_ret = Variant();
- return;
- }
- if (p_args[1]->get_type() != Variant::STRING && p_args[1]->get_type() != Variant::NODE_PATH) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 1;
- r_error.expected = Variant::STRING;
- r_ret = Variant();
- return;
- }
-
- Ref<FuncRef> fr = memnew(FuncRef);
-
- fr->set_instance(*p_args[0]);
- fr->set_function(*p_args[1]);
-
- r_ret = fr;
-
- } break;
case TYPE_CONVERT: {
VALIDATE_ARG_COUNT(2);
VALIDATE_ARG_NUM(1);
@@ -619,7 +599,7 @@ void GDScriptFunctions::call(Function p_func, const Variant **p_args, int p_arg_
return;
} else {
- r_ret = Variant::construct(Variant::Type(type), p_args, 1, r_error);
+ Variant::construct(Variant::Type(type), r_ret, p_args, 1, r_error);
}
} break;
case TYPE_OF: {
@@ -1655,7 +1635,7 @@ MethodInfo GDScriptFunctions::get_info(Function p_func) {
mi.return_val.type = Variant::NIL;
return mi;
} break;
- case MATH_RAND: {
+ case MATH_RANDI: {
MethodInfo mi("randi");
mi.return_val.type = Variant::INT;
return mi;
@@ -1665,11 +1645,16 @@ MethodInfo GDScriptFunctions::get_info(Function p_func) {
mi.return_val.type = Variant::FLOAT;
return mi;
} break;
- case MATH_RANDOM: {
- MethodInfo mi("rand_range", PropertyInfo(Variant::FLOAT, "from"), PropertyInfo(Variant::FLOAT, "to"));
+ case MATH_RANDF_RANGE: {
+ MethodInfo mi("randf_range", PropertyInfo(Variant::FLOAT, "from"), PropertyInfo(Variant::FLOAT, "to"));
mi.return_val.type = Variant::FLOAT;
return mi;
} break;
+ case MATH_RANDI_RANGE: {
+ MethodInfo mi("randi_range", PropertyInfo(Variant::INT, "from"), PropertyInfo(Variant::INT, "to"));
+ mi.return_val.type = Variant::INT;
+ return mi;
+ } break;
case MATH_SEED: {
MethodInfo mi("seed", PropertyInfo(Variant::INT, "seed"));
mi.return_val.type = Variant::NIL;
@@ -1749,13 +1734,6 @@ MethodInfo GDScriptFunctions::get_info(Function p_func) {
return mi;
} break;
- case FUNC_FUNCREF: {
- MethodInfo mi("funcref", PropertyInfo(Variant::OBJECT, "instance"), PropertyInfo(Variant::STRING, "funcname"));
- mi.return_val.type = Variant::OBJECT;
- mi.return_val.class_name = "FuncRef";
- return mi;
-
- } break;
case TYPE_CONVERT: {
MethodInfo mi("convert", PropertyInfo(Variant::NIL, "what", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_NIL_IS_VARIANT), PropertyInfo(Variant::INT, "type"));
mi.return_val.type = Variant::NIL;
diff --git a/modules/gdscript/gdscript_functions.h b/modules/gdscript/gdscript_functions.h
index 2c6dc02913..005b49c5da 100644
--- a/modules/gdscript/gdscript_functions.h
+++ b/modules/gdscript/gdscript_functions.h
@@ -31,7 +31,7 @@
#ifndef GDSCRIPT_FUNCTIONS_H
#define GDSCRIPT_FUNCTIONS_H
-#include "core/variant.h"
+#include "core/variant/variant.h"
class GDScriptFunctions {
public:
@@ -73,9 +73,10 @@ public:
MATH_MOVE_TOWARD,
MATH_DECTIME,
MATH_RANDOMIZE,
- MATH_RAND,
+ MATH_RANDI,
MATH_RANDF,
- MATH_RANDOM,
+ MATH_RANDF_RANGE,
+ MATH_RANDI_RANGE,
MATH_SEED,
MATH_RANDSEED,
MATH_DEG2RAD,
@@ -91,7 +92,6 @@ public:
LOGIC_CLAMP,
LOGIC_NEAREST_PO2,
OBJ_WEAKREF,
- FUNC_FUNCREF,
TYPE_CONVERT,
TYPE_OF,
TYPE_EXISTS,
diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp
index 2a69db130b..fde3662d66 100644
--- a/modules/gdscript/gdscript_parser.cpp
+++ b/modules/gdscript/gdscript_parser.cpp
@@ -30,15 +30,15 @@
#include "gdscript_parser.h"
+#include "core/config/project_settings.h"
#include "core/io/resource_loader.h"
#include "core/math/math_defs.h"
#include "core/os/file_access.h"
-#include "core/project_settings.h"
#include "gdscript.h"
#ifdef DEBUG_ENABLED
#include "core/os/os.h"
-#include "core/string_builder.h"
+#include "core/string/string_builder.h"
#endif // DEBUG_ENABLED
#ifdef TOOLS_ENABLED
@@ -65,7 +65,7 @@ Variant::Type GDScriptParser::get_builtin_type(const StringName &p_type) {
builtin_types["Basis"] = Variant::BASIS;
builtin_types["Transform"] = Variant::TRANSFORM;
builtin_types["Color"] = Variant::COLOR;
- builtin_types["RID"] = Variant::_RID;
+ builtin_types["RID"] = Variant::RID;
builtin_types["Object"] = Variant::OBJECT;
builtin_types["StringName"] = Variant::STRING_NAME;
builtin_types["NodePath"] = Variant::NODE_PATH;
@@ -2486,26 +2486,28 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_call(ExpressionNode *p_pre
}
}
- if (!check(GDScriptTokenizer::Token::PARENTHESIS_CLOSE)) {
- // Arguments.
- push_completion_call(call);
- make_completion_context(COMPLETION_CALL_ARGUMENTS, call, 0, true);
- int argument_index = 0;
- do {
- make_completion_context(COMPLETION_CALL_ARGUMENTS, call, argument_index++, true);
- if (check(GDScriptTokenizer::Token::PARENTHESIS_CLOSE)) {
- // Allow for trailing comma.
- break;
- }
- ExpressionNode *argument = parse_expression(false);
- if (argument == nullptr) {
- push_error(R"(Expected expression as the function argument.)");
- } else {
- call->arguments.push_back(argument);
- }
- } while (match(GDScriptTokenizer::Token::COMMA));
- pop_completion_call();
+ // Arguments.
+ CompletionType ct = COMPLETION_CALL_ARGUMENTS;
+ if (get_builtin_function(call->function_name) == GDScriptFunctions::RESOURCE_LOAD) {
+ ct = COMPLETION_RESOURCE_PATH;
}
+ push_completion_call(call);
+ int argument_index = 0;
+ do {
+ make_completion_context(ct, call, argument_index++, true);
+ if (check(GDScriptTokenizer::Token::PARENTHESIS_CLOSE)) {
+ // Allow for trailing comma.
+ break;
+ }
+ ExpressionNode *argument = parse_expression(false);
+ if (argument == nullptr) {
+ push_error(R"(Expected expression as the function argument.)");
+ } else {
+ call->arguments.push_back(argument);
+ }
+ ct = COMPLETION_CALL_ARGUMENTS;
+ } while (match(GDScriptTokenizer::Token::COMMA));
+ pop_completion_call();
pop_multiline();
consume(GDScriptTokenizer::Token::PARENTHESIS_CLOSE, R"*(Expected closing ")" after call arguments.)*");
@@ -2802,7 +2804,9 @@ bool GDScriptParser::validate_annotation_arguments(AnnotationNode *p_annotation)
Callable::CallError error;
Vector<Variant> args = varray(string->name);
const Variant *name = args.ptr();
- p_annotation->resolved_arguments.push_back(Variant::construct(parameter.type, &(name), 1, error));
+ Variant r;
+ Variant::construct(parameter.type, r, &(name), 1, error);
+ p_annotation->resolved_arguments.push_back(r);
if (error.error != Callable::CallError::CALL_OK) {
push_error(vformat(R"(Expected %s as argument %d of annotation "%s").)", Variant::get_type_name(parameter.type), i + 1, p_annotation->name));
p_annotation->resolved_arguments.remove(p_annotation->resolved_arguments.size() - 1);
@@ -2824,7 +2828,9 @@ bool GDScriptParser::validate_annotation_arguments(AnnotationNode *p_annotation)
}
Callable::CallError error;
const Variant *args = &value;
- p_annotation->resolved_arguments.push_back(Variant::construct(parameter.type, &(args), 1, error));
+ Variant r;
+ Variant::construct(parameter.type, r, &(args), 1, error);
+ p_annotation->resolved_arguments.push_back(r);
if (error.error != Callable::CallError::CALL_OK) {
push_error(vformat(R"(Expected %s as argument %d of annotation "%s").)", Variant::get_type_name(parameter.type), i + 1, p_annotation->name));
p_annotation->resolved_arguments.remove(p_annotation->resolved_arguments.size() - 1);
diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h
index 4c9473c7bd..b24acc4778 100644
--- a/modules/gdscript/gdscript_parser.h
+++ b/modules/gdscript/gdscript_parser.h
@@ -31,23 +31,23 @@
#ifndef GDSCRIPT_PARSER_H
#define GDSCRIPT_PARSER_H
-#include "core/hash_map.h"
#include "core/io/multiplayer_api.h"
-#include "core/list.h"
-#include "core/map.h"
-#include "core/reference.h"
-#include "core/resource.h"
-#include "core/script_language.h"
-#include "core/string_name.h"
-#include "core/ustring.h"
-#include "core/variant.h"
-#include "core/vector.h"
+#include "core/io/resource.h"
+#include "core/object/reference.h"
+#include "core/object/script_language.h"
+#include "core/string/string_name.h"
+#include "core/string/ustring.h"
+#include "core/templates/hash_map.h"
+#include "core/templates/list.h"
+#include "core/templates/map.h"
+#include "core/templates/vector.h"
+#include "core/variant/variant.h"
#include "gdscript_cache.h"
#include "gdscript_functions.h"
#include "gdscript_tokenizer.h"
#ifdef DEBUG_ENABLED
-#include "core/string_builder.h"
+#include "core/string/string_builder.h"
#include "gdscript_warning.h"
#endif // DEBUG_ENABLED
diff --git a/modules/gdscript/gdscript_tokenizer.cpp b/modules/gdscript/gdscript_tokenizer.cpp
index 9a40aa50ac..b91777ede1 100644
--- a/modules/gdscript/gdscript_tokenizer.cpp
+++ b/modules/gdscript/gdscript_tokenizer.cpp
@@ -30,7 +30,7 @@
#include "gdscript_tokenizer.h"
-#include "core/error_macros.h"
+#include "core/error/error_macros.h"
#ifdef TOOLS_ENABLED
#include "editor/editor_settings.h"
diff --git a/modules/gdscript/gdscript_tokenizer.h b/modules/gdscript/gdscript_tokenizer.h
index 4453982d08..d51f1f250f 100644
--- a/modules/gdscript/gdscript_tokenizer.h
+++ b/modules/gdscript/gdscript_tokenizer.h
@@ -31,10 +31,10 @@
#ifndef GDSCRIPT_TOKENIZER_H
#define GDSCRIPT_TOKENIZER_H
-#include "core/list.h"
-#include "core/set.h"
-#include "core/variant.h"
-#include "core/vector.h"
+#include "core/templates/list.h"
+#include "core/templates/set.h"
+#include "core/templates/vector.h"
+#include "core/variant/variant.h"
class GDScriptTokenizer {
public:
diff --git a/modules/gdscript/gdscript_vm.cpp b/modules/gdscript/gdscript_vm.cpp
new file mode 100644
index 0000000000..7c8bfcd944
--- /dev/null
+++ b/modules/gdscript/gdscript_vm.cpp
@@ -0,0 +1,2863 @@
+/*************************************************************************/
+/* gdscript_vm.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "gdscript_function.h"
+
+#include "core/core_string_names.h"
+#include "core/os/os.h"
+#include "gdscript.h"
+#include "gdscript_functions.h"
+
+Variant *GDScriptFunction::_get_variant(int p_address, GDScriptInstance *p_instance, GDScript *p_script, Variant &self, Variant &static_ref, Variant *p_stack, String &r_error) const {
+ int address = p_address & ADDR_MASK;
+
+ //sequential table (jump table generated by compiler)
+ switch ((p_address & ADDR_TYPE_MASK) >> ADDR_BITS) {
+ case ADDR_TYPE_SELF: {
+#ifdef DEBUG_ENABLED
+ if (unlikely(!p_instance)) {
+ r_error = "Cannot access self without instance.";
+ return nullptr;
+ }
+#endif
+ return &self;
+ } break;
+ case ADDR_TYPE_CLASS: {
+ return &static_ref;
+ } break;
+ case ADDR_TYPE_MEMBER: {
+#ifdef DEBUG_ENABLED
+ if (unlikely(!p_instance)) {
+ r_error = "Cannot access member without instance.";
+ return nullptr;
+ }
+#endif
+ //member indexing is O(1)
+ return &p_instance->members.write[address];
+ } break;
+ case ADDR_TYPE_CLASS_CONSTANT: {
+ //todo change to index!
+ GDScript *s = p_script;
+#ifdef DEBUG_ENABLED
+ ERR_FAIL_INDEX_V(address, _global_names_count, nullptr);
+#endif
+ const StringName *sn = &_global_names_ptr[address];
+
+ while (s) {
+ GDScript *o = s;
+ while (o) {
+ Map<StringName, Variant>::Element *E = o->constants.find(*sn);
+ if (E) {
+ return &E->get();
+ }
+ o = o->_owner;
+ }
+ s = s->_base;
+ }
+
+ ERR_FAIL_V_MSG(nullptr, "GDScriptCompiler bug.");
+ } break;
+ case ADDR_TYPE_LOCAL_CONSTANT: {
+#ifdef DEBUG_ENABLED
+ ERR_FAIL_INDEX_V(address, _constant_count, nullptr);
+#endif
+ return &_constants_ptr[address];
+ } break;
+ case ADDR_TYPE_STACK:
+ case ADDR_TYPE_STACK_VARIABLE: {
+#ifdef DEBUG_ENABLED
+ ERR_FAIL_INDEX_V(address, _stack_size, nullptr);
+#endif
+ return &p_stack[address];
+ } break;
+ case ADDR_TYPE_GLOBAL: {
+#ifdef DEBUG_ENABLED
+ ERR_FAIL_INDEX_V(address, GDScriptLanguage::get_singleton()->get_global_array_size(), nullptr);
+#endif
+ return &GDScriptLanguage::get_singleton()->get_global_array()[address];
+ } break;
+#ifdef TOOLS_ENABLED
+ case ADDR_TYPE_NAMED_GLOBAL: {
+#ifdef DEBUG_ENABLED
+ ERR_FAIL_INDEX_V(address, _global_names_count, nullptr);
+#endif
+ StringName id = _global_names_ptr[address];
+
+ if (GDScriptLanguage::get_singleton()->get_named_globals_map().has(id)) {
+ return (Variant *)&GDScriptLanguage::get_singleton()->get_named_globals_map()[id];
+ } else {
+ r_error = "Autoload singleton '" + String(id) + "' has been removed.";
+ return nullptr;
+ }
+ } break;
+#endif
+ case ADDR_TYPE_NIL: {
+ return &nil;
+ } break;
+ }
+
+ ERR_FAIL_V_MSG(nullptr, "Bad code! (unknown addressing mode).");
+ return nullptr;
+}
+
+#ifdef DEBUG_ENABLED
+static String _get_var_type(const Variant *p_var) {
+ String basestr;
+
+ if (p_var->get_type() == Variant::OBJECT) {
+ bool was_freed;
+ Object *bobj = p_var->get_validated_object_with_check(was_freed);
+ if (!bobj) {
+ if (was_freed) {
+ basestr = "null instance";
+ } else {
+ basestr = "previously freed";
+ }
+ } else {
+ if (bobj->get_script_instance()) {
+ basestr = bobj->get_class() + " (" + bobj->get_script_instance()->get_script()->get_path().get_file() + ")";
+ } else {
+ basestr = bobj->get_class();
+ }
+ }
+
+ } else {
+ basestr = Variant::get_type_name(p_var->get_type());
+ }
+
+ return basestr;
+}
+#endif // DEBUG_ENABLED
+
+String GDScriptFunction::_get_call_error(const Callable::CallError &p_err, const String &p_where, const Variant **argptrs) const {
+ String err_text;
+
+ if (p_err.error == Callable::CallError::CALL_ERROR_INVALID_ARGUMENT) {
+ int errorarg = p_err.argument;
+ // Handle the Object to Object case separately as we don't have further class details.
+#ifdef DEBUG_ENABLED
+ if (p_err.expected == Variant::OBJECT && argptrs[errorarg]->get_type() == p_err.expected) {
+ err_text = "Invalid type in " + p_where + ". The Object-derived class of argument " + itos(errorarg + 1) + " (" + _get_var_type(argptrs[errorarg]) + ") is not a subclass of the expected argument class.";
+ } else
+#endif // DEBUG_ENABLED
+ {
+ err_text = "Invalid type in " + p_where + ". Cannot convert argument " + itos(errorarg + 1) + " from " + Variant::get_type_name(argptrs[errorarg]->get_type()) + " to " + Variant::get_type_name(Variant::Type(p_err.expected)) + ".";
+ }
+ } else if (p_err.error == Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS) {
+ err_text = "Invalid call to " + p_where + ". Expected " + itos(p_err.argument) + " arguments.";
+ } else if (p_err.error == Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS) {
+ err_text = "Invalid call to " + p_where + ". Expected " + itos(p_err.argument) + " arguments.";
+ } else if (p_err.error == Callable::CallError::CALL_ERROR_INVALID_METHOD) {
+ err_text = "Invalid call. Nonexistent " + p_where + ".";
+ } else if (p_err.error == Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL) {
+ err_text = "Attempt to call " + p_where + " on a null instance.";
+ } else {
+ err_text = "Bug, call error: #" + itos(p_err.error);
+ }
+
+ return err_text;
+}
+
+#if defined(__GNUC__)
+#define OPCODES_TABLE \
+ static const void *switch_table_ops[] = { \
+ &&OPCODE_OPERATOR, \
+ &&OPCODE_OPERATOR_VALIDATED, \
+ &&OPCODE_EXTENDS_TEST, \
+ &&OPCODE_IS_BUILTIN, \
+ &&OPCODE_SET_KEYED, \
+ &&OPCODE_SET_KEYED_VALIDATED, \
+ &&OPCODE_SET_INDEXED_VALIDATED, \
+ &&OPCODE_GET_KEYED, \
+ &&OPCODE_GET_KEYED_VALIDATED, \
+ &&OPCODE_GET_INDEXED_VALIDATED, \
+ &&OPCODE_SET_NAMED, \
+ &&OPCODE_SET_NAMED_VALIDATED, \
+ &&OPCODE_GET_NAMED, \
+ &&OPCODE_GET_NAMED_VALIDATED, \
+ &&OPCODE_SET_MEMBER, \
+ &&OPCODE_GET_MEMBER, \
+ &&OPCODE_ASSIGN, \
+ &&OPCODE_ASSIGN_TRUE, \
+ &&OPCODE_ASSIGN_FALSE, \
+ &&OPCODE_ASSIGN_TYPED_BUILTIN, \
+ &&OPCODE_ASSIGN_TYPED_NATIVE, \
+ &&OPCODE_ASSIGN_TYPED_SCRIPT, \
+ &&OPCODE_CAST_TO_BUILTIN, \
+ &&OPCODE_CAST_TO_NATIVE, \
+ &&OPCODE_CAST_TO_SCRIPT, \
+ &&OPCODE_CONSTRUCT, \
+ &&OPCODE_CONSTRUCT_VALIDATED, \
+ &&OPCODE_CONSTRUCT_ARRAY, \
+ &&OPCODE_CONSTRUCT_DICTIONARY, \
+ &&OPCODE_CALL, \
+ &&OPCODE_CALL_RETURN, \
+ &&OPCODE_CALL_ASYNC, \
+ &&OPCODE_CALL_BUILT_IN, \
+ &&OPCODE_CALL_BUILTIN_TYPE_VALIDATED, \
+ &&OPCODE_CALL_SELF_BASE, \
+ &&OPCODE_CALL_METHOD_BIND, \
+ &&OPCODE_CALL_METHOD_BIND_RET, \
+ &&OPCODE_CALL_PTRCALL_NO_RETURN, \
+ &&OPCODE_CALL_PTRCALL_BOOL, \
+ &&OPCODE_CALL_PTRCALL_INT, \
+ &&OPCODE_CALL_PTRCALL_FLOAT, \
+ &&OPCODE_CALL_PTRCALL_STRING, \
+ &&OPCODE_CALL_PTRCALL_VECTOR2, \
+ &&OPCODE_CALL_PTRCALL_VECTOR2I, \
+ &&OPCODE_CALL_PTRCALL_RECT2, \
+ &&OPCODE_CALL_PTRCALL_RECT2I, \
+ &&OPCODE_CALL_PTRCALL_VECTOR3, \
+ &&OPCODE_CALL_PTRCALL_VECTOR3I, \
+ &&OPCODE_CALL_PTRCALL_TRANSFORM2D, \
+ &&OPCODE_CALL_PTRCALL_PLANE, \
+ &&OPCODE_CALL_PTRCALL_QUAT, \
+ &&OPCODE_CALL_PTRCALL_AABB, \
+ &&OPCODE_CALL_PTRCALL_BASIS, \
+ &&OPCODE_CALL_PTRCALL_TRANSFORM, \
+ &&OPCODE_CALL_PTRCALL_COLOR, \
+ &&OPCODE_CALL_PTRCALL_STRING_NAME, \
+ &&OPCODE_CALL_PTRCALL_NODE_PATH, \
+ &&OPCODE_CALL_PTRCALL_RID, \
+ &&OPCODE_CALL_PTRCALL_OBJECT, \
+ &&OPCODE_CALL_PTRCALL_CALLABLE, \
+ &&OPCODE_CALL_PTRCALL_SIGNAL, \
+ &&OPCODE_CALL_PTRCALL_DICTIONARY, \
+ &&OPCODE_CALL_PTRCALL_ARRAY, \
+ &&OPCODE_CALL_PTRCALL_PACKED_BYTE_ARRAY, \
+ &&OPCODE_CALL_PTRCALL_PACKED_INT32_ARRAY, \
+ &&OPCODE_CALL_PTRCALL_PACKED_INT64_ARRAY, \
+ &&OPCODE_CALL_PTRCALL_PACKED_FLOAT32_ARRAY, \
+ &&OPCODE_CALL_PTRCALL_PACKED_FLOAT64_ARRAY, \
+ &&OPCODE_CALL_PTRCALL_PACKED_STRING_ARRAY, \
+ &&OPCODE_CALL_PTRCALL_PACKED_VECTOR2_ARRAY, \
+ &&OPCODE_CALL_PTRCALL_PACKED_VECTOR3_ARRAY, \
+ &&OPCODE_CALL_PTRCALL_PACKED_COLOR_ARRAY, \
+ &&OPCODE_AWAIT, \
+ &&OPCODE_AWAIT_RESUME, \
+ &&OPCODE_JUMP, \
+ &&OPCODE_JUMP_IF, \
+ &&OPCODE_JUMP_IF_NOT, \
+ &&OPCODE_JUMP_TO_DEF_ARGUMENT, \
+ &&OPCODE_RETURN, \
+ &&OPCODE_ITERATE_BEGIN, \
+ &&OPCODE_ITERATE_BEGIN_INT, \
+ &&OPCODE_ITERATE_BEGIN_FLOAT, \
+ &&OPCODE_ITERATE_BEGIN_VECTOR2, \
+ &&OPCODE_ITERATE_BEGIN_VECTOR2I, \
+ &&OPCODE_ITERATE_BEGIN_VECTOR3, \
+ &&OPCODE_ITERATE_BEGIN_VECTOR3I, \
+ &&OPCODE_ITERATE_BEGIN_STRING, \
+ &&OPCODE_ITERATE_BEGIN_DICTIONARY, \
+ &&OPCODE_ITERATE_BEGIN_ARRAY, \
+ &&OPCODE_ITERATE_BEGIN_PACKED_BYTE_ARRAY, \
+ &&OPCODE_ITERATE_BEGIN_PACKED_INT32_ARRAY, \
+ &&OPCODE_ITERATE_BEGIN_PACKED_INT64_ARRAY, \
+ &&OPCODE_ITERATE_BEGIN_PACKED_FLOAT32_ARRAY, \
+ &&OPCODE_ITERATE_BEGIN_PACKED_FLOAT64_ARRAY, \
+ &&OPCODE_ITERATE_BEGIN_PACKED_STRING_ARRAY, \
+ &&OPCODE_ITERATE_BEGIN_PACKED_VECTOR2_ARRAY, \
+ &&OPCODE_ITERATE_BEGIN_PACKED_VECTOR3_ARRAY, \
+ &&OPCODE_ITERATE_BEGIN_PACKED_COLOR_ARRAY, \
+ &&OPCODE_ITERATE_BEGIN_OBJECT, \
+ &&OPCODE_ITERATE, \
+ &&OPCODE_ITERATE_INT, \
+ &&OPCODE_ITERATE_FLOAT, \
+ &&OPCODE_ITERATE_VECTOR2, \
+ &&OPCODE_ITERATE_VECTOR2I, \
+ &&OPCODE_ITERATE_VECTOR3, \
+ &&OPCODE_ITERATE_VECTOR3I, \
+ &&OPCODE_ITERATE_STRING, \
+ &&OPCODE_ITERATE_DICTIONARY, \
+ &&OPCODE_ITERATE_ARRAY, \
+ &&OPCODE_ITERATE_PACKED_BYTE_ARRAY, \
+ &&OPCODE_ITERATE_PACKED_INT32_ARRAY, \
+ &&OPCODE_ITERATE_PACKED_INT64_ARRAY, \
+ &&OPCODE_ITERATE_PACKED_FLOAT32_ARRAY, \
+ &&OPCODE_ITERATE_PACKED_FLOAT64_ARRAY, \
+ &&OPCODE_ITERATE_PACKED_STRING_ARRAY, \
+ &&OPCODE_ITERATE_PACKED_VECTOR2_ARRAY, \
+ &&OPCODE_ITERATE_PACKED_VECTOR3_ARRAY, \
+ &&OPCODE_ITERATE_PACKED_COLOR_ARRAY, \
+ &&OPCODE_ITERATE_OBJECT, \
+ &&OPCODE_ASSERT, \
+ &&OPCODE_BREAKPOINT, \
+ &&OPCODE_LINE, \
+ &&OPCODE_END \
+ }; \
+ static_assert((sizeof(switch_table_ops) / sizeof(switch_table_ops[0]) == (OPCODE_END + 1)), "Opcodes in jump table aren't the same as opcodes in enum.");
+
+#define OPCODE(m_op) \
+ m_op:
+#define OPCODE_WHILE(m_test) \
+ OPSWHILE:
+#define OPCODES_END \
+ OPSEXIT:
+#define OPCODES_OUT \
+ OPSOUT:
+#define DISPATCH_OPCODE goto OPSWHILE
+#define OPCODE_SWITCH(m_test) goto *switch_table_ops[m_test];
+#define OPCODE_BREAK goto OPSEXIT
+#define OPCODE_OUT goto OPSOUT
+#else
+#define OPCODES_TABLE
+#define OPCODE(m_op) case m_op:
+#define OPCODE_WHILE(m_test) while (m_test)
+#define OPCODES_END
+#define OPCODES_OUT
+#define DISPATCH_OPCODE continue
+#define OPCODE_SWITCH(m_test) switch (m_test)
+#define OPCODE_BREAK break
+#define OPCODE_OUT break
+#endif
+
+// Helpers for VariantInternal methods in macros.
+#define OP_GET_BOOL get_bool
+#define OP_GET_INT get_int
+#define OP_GET_FLOAT get_float
+#define OP_GET_VECTOR2 get_vector2
+#define OP_GET_VECTOR2I get_vector2i
+#define OP_GET_VECTOR3 get_vector3
+#define OP_GET_VECTOR3I get_vector3i
+#define OP_GET_RECT2 get_rect2
+#define OP_GET_RECT2I get_rect2i
+#define OP_GET_QUAT get_quat
+#define OP_GET_COLOR get_color
+#define OP_GET_STRING get_string
+#define OP_GET_STRING_NAME get_string_name
+#define OP_GET_NODE_PATH get_node_path
+#define OP_GET_CALLABLE get_callable
+#define OP_GET_SIGNAL get_signal
+#define OP_GET_ARRAY get_array
+#define OP_GET_DICTIONARY get_dictionary
+#define OP_GET_PACKED_BYTE_ARRAY get_byte_array
+#define OP_GET_PACKED_INT32_ARRAY get_int32_array
+#define OP_GET_PACKED_INT64_ARRAY get_int64_array
+#define OP_GET_PACKED_FLOAT32_ARRAY get_float32_array
+#define OP_GET_PACKED_FLOAT64_ARRAY get_float64_array
+#define OP_GET_PACKED_STRING_ARRAY get_string_array
+#define OP_GET_PACKED_VECTOR2_ARRAY get_vector2_array
+#define OP_GET_PACKED_VECTOR3_ARRAY get_vector3_array
+#define OP_GET_PACKED_COLOR_ARRAY get_color_array
+#define OP_GET_TRANSFORM get_transform
+#define OP_GET_TRANSFORM2D get_transform2d
+#define OP_GET_PLANE get_plane
+#define OP_GET_AABB get_aabb
+#define OP_GET_BASIS get_basis
+#define OP_GET_RID get_rid
+
+Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_args, int p_argcount, Callable::CallError &r_err, CallState *p_state) {
+ OPCODES_TABLE;
+
+ if (!_code_ptr) {
+ return Variant();
+ }
+
+ r_err.error = Callable::CallError::CALL_OK;
+
+ Variant self;
+ Variant static_ref;
+ Variant retvalue;
+ Variant *stack = nullptr;
+ Variant **instruction_args;
+ const void **call_args_ptr = nullptr;
+ int defarg = 0;
+
+#ifdef DEBUG_ENABLED
+
+ //GDScriptLanguage::get_singleton()->calls++;
+
+#endif
+
+ uint32_t alloca_size = 0;
+ GDScript *script;
+ int ip = 0;
+ int line = _initial_line;
+
+ if (p_state) {
+ //use existing (supplied) state (awaited)
+ stack = (Variant *)p_state->stack.ptr();
+ instruction_args = (Variant **)&p_state->stack.ptr()[sizeof(Variant) * p_state->stack_size]; //ptr() to avoid bounds check
+ line = p_state->line;
+ ip = p_state->ip;
+ alloca_size = p_state->stack.size();
+ script = p_state->script;
+ p_instance = p_state->instance;
+ defarg = p_state->defarg;
+ self = p_state->self;
+
+ } else {
+ if (p_argcount != _argument_count) {
+ if (p_argcount > _argument_count) {
+ r_err.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS;
+ r_err.argument = _argument_count;
+
+ return Variant();
+ } else if (p_argcount < _argument_count - _default_arg_count) {
+ r_err.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
+ r_err.argument = _argument_count - _default_arg_count;
+ return Variant();
+ } else {
+ defarg = _argument_count - p_argcount;
+ }
+ }
+
+ alloca_size = sizeof(Variant *) * _instruction_args_size + sizeof(Variant) * _stack_size;
+
+ if (alloca_size) {
+ uint8_t *aptr = (uint8_t *)alloca(alloca_size);
+
+ if (_stack_size) {
+ stack = (Variant *)aptr;
+ for (int i = 0; i < p_argcount; i++) {
+ if (!argument_types[i].has_type) {
+ memnew_placement(&stack[i], Variant(*p_args[i]));
+ continue;
+ }
+
+ if (!argument_types[i].is_type(*p_args[i], true)) {
+ r_err.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_err.argument = i;
+ r_err.expected = argument_types[i].kind == GDScriptDataType::BUILTIN ? argument_types[i].builtin_type : Variant::OBJECT;
+ return Variant();
+ }
+ if (argument_types[i].kind == GDScriptDataType::BUILTIN) {
+ Variant arg;
+ Variant::construct(argument_types[i].builtin_type, arg, &p_args[i], 1, r_err);
+ memnew_placement(&stack[i], Variant(arg));
+ } else {
+ memnew_placement(&stack[i], Variant(*p_args[i]));
+ }
+ }
+ for (int i = p_argcount; i < _stack_size; i++) {
+ memnew_placement(&stack[i], Variant);
+ }
+ } else {
+ stack = nullptr;
+ }
+
+ if (_instruction_args_size) {
+ instruction_args = (Variant **)&aptr[sizeof(Variant) * _stack_size];
+ } else {
+ instruction_args = nullptr;
+ }
+
+ } else {
+ stack = nullptr;
+ instruction_args = nullptr;
+ }
+
+ if (p_instance) {
+ if (p_instance->base_ref && static_cast<Reference *>(p_instance->owner)->is_referenced()) {
+ self = REF(static_cast<Reference *>(p_instance->owner));
+ } else {
+ self = p_instance->owner;
+ }
+ script = p_instance->script.ptr();
+ } else {
+ script = _script;
+ }
+ }
+ if (_ptrcall_args_size) {
+ call_args_ptr = (const void **)alloca(_ptrcall_args_size * sizeof(void *));
+ } else {
+ call_args_ptr = nullptr;
+ }
+
+ static_ref = script;
+
+ String err_text;
+
+#ifdef DEBUG_ENABLED
+
+ if (EngineDebugger::is_active()) {
+ GDScriptLanguage::get_singleton()->enter_function(p_instance, this, stack, &ip, &line);
+ }
+
+#define GD_ERR_BREAK(m_cond) \
+ { \
+ if (unlikely(m_cond)) { \
+ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition ' " _STR(m_cond) " ' is true. Breaking..:"); \
+ OPCODE_BREAK; \
+ } \
+ }
+
+#define CHECK_SPACE(m_space) \
+ GD_ERR_BREAK((ip + m_space) > _code_size)
+
+#define GET_VARIANT_PTR(m_v, m_code_ofs) \
+ Variant *m_v; \
+ m_v = _get_variant(_code_ptr[ip + m_code_ofs], p_instance, script, self, static_ref, stack, err_text); \
+ if (unlikely(!m_v)) \
+ OPCODE_BREAK;
+
+#else
+#define GD_ERR_BREAK(m_cond)
+#define CHECK_SPACE(m_space)
+#define GET_VARIANT_PTR(m_v, m_code_ofs) \
+ Variant *m_v; \
+ m_v = _get_variant(_code_ptr[ip + m_code_ofs], p_instance, script, self, static_ref, stack, err_text);
+
+#endif
+
+#define GET_INSTRUCTION_ARG(m_v, m_idx) \
+ Variant *m_v = instruction_args[m_idx]
+
+#ifdef DEBUG_ENABLED
+
+ uint64_t function_start_time = 0;
+ uint64_t function_call_time = 0;
+
+ if (GDScriptLanguage::get_singleton()->profiling) {
+ function_start_time = OS::get_singleton()->get_ticks_usec();
+ function_call_time = 0;
+ profile.call_count++;
+ profile.frame_call_count++;
+ }
+ bool exit_ok = false;
+ bool awaited = false;
+#endif
+
+#ifdef DEBUG_ENABLED
+ OPCODE_WHILE(ip < _code_size) {
+ int last_opcode = _code_ptr[ip];
+#else
+ OPCODE_WHILE(true) {
+#endif
+ // Load arguments for the instruction before each instruction.
+ int instr_arg_count = ((_code_ptr[ip]) & INSTR_ARGS_MASK) >> INSTR_BITS;
+ for (int i = 0; i < instr_arg_count; i++) {
+ GET_VARIANT_PTR(v, i + 1);
+ instruction_args[i] = v;
+ }
+
+ OPCODE_SWITCH(_code_ptr[ip] & INSTR_MASK) {
+ OPCODE(OPCODE_OPERATOR) {
+ CHECK_SPACE(5);
+
+ bool valid;
+ Variant::Operator op = (Variant::Operator)_code_ptr[ip + 4];
+ GD_ERR_BREAK(op >= Variant::OP_MAX);
+
+ GET_INSTRUCTION_ARG(a, 0);
+ GET_INSTRUCTION_ARG(b, 1);
+ GET_INSTRUCTION_ARG(dst, 2);
+
+#ifdef DEBUG_ENABLED
+
+ Variant ret;
+ Variant::evaluate(op, *a, *b, ret, valid);
+#else
+ Variant::evaluate(op, *a, *b, *dst, valid);
+#endif
+#ifdef DEBUG_ENABLED
+ if (!valid) {
+ if (ret.get_type() == Variant::STRING) {
+ //return a string when invalid with the error
+ err_text = ret;
+ err_text += " in operator '" + Variant::get_operator_name(op) + "'.";
+ } else {
+ err_text = "Invalid operands '" + Variant::get_type_name(a->get_type()) + "' and '" + Variant::get_type_name(b->get_type()) + "' in operator '" + Variant::get_operator_name(op) + "'.";
+ }
+ OPCODE_BREAK;
+ }
+ *dst = ret;
+#endif
+ ip += 5;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_OPERATOR_VALIDATED) {
+ CHECK_SPACE(5);
+
+ int operator_idx = _code_ptr[ip + 4];
+ GD_ERR_BREAK(operator_idx < 0 || operator_idx >= _operator_funcs_count);
+ Variant::ValidatedOperatorEvaluator operator_func = _operator_funcs_ptr[operator_idx];
+
+ GET_INSTRUCTION_ARG(a, 0);
+ GET_INSTRUCTION_ARG(b, 1);
+ GET_INSTRUCTION_ARG(dst, 2);
+
+ operator_func(a, b, dst);
+
+ ip += 5;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_EXTENDS_TEST) {
+ CHECK_SPACE(4);
+
+ GET_INSTRUCTION_ARG(a, 0);
+ GET_INSTRUCTION_ARG(b, 1);
+ GET_INSTRUCTION_ARG(dst, 2);
+
+#ifdef DEBUG_ENABLED
+ if (b->get_type() != Variant::OBJECT || b->operator Object *() == nullptr) {
+ err_text = "Right operand of 'is' is not a class.";
+ OPCODE_BREAK;
+ }
+#endif
+
+ bool extends_ok = false;
+ if (a->get_type() == Variant::OBJECT && a->operator Object *() != nullptr) {
+#ifdef DEBUG_ENABLED
+ bool was_freed;
+ Object *obj_A = a->get_validated_object_with_check(was_freed);
+
+ if (was_freed) {
+ err_text = "Left operand of 'is' is a previously freed instance.";
+ OPCODE_BREAK;
+ }
+
+ Object *obj_B = b->get_validated_object_with_check(was_freed);
+
+ if (was_freed) {
+ err_text = "Right operand of 'is' is a previously freed instance.";
+ OPCODE_BREAK;
+ }
+#else
+
+ Object *obj_A = *a;
+ Object *obj_B = *b;
+#endif // DEBUG_ENABLED
+
+ GDScript *scr_B = Object::cast_to<GDScript>(obj_B);
+
+ if (scr_B) {
+ //if B is a script, the only valid condition is that A has an instance which inherits from the script
+ //in other situation, this shoul return false.
+
+ if (obj_A->get_script_instance() && obj_A->get_script_instance()->get_language() == GDScriptLanguage::get_singleton()) {
+ GDScript *cmp = static_cast<GDScript *>(obj_A->get_script_instance()->get_script().ptr());
+ //bool found=false;
+ while (cmp) {
+ if (cmp == scr_B) {
+ //inherits from script, all ok
+ extends_ok = true;
+ break;
+ }
+
+ cmp = cmp->_base;
+ }
+ }
+
+ } else {
+ GDScriptNativeClass *nc = Object::cast_to<GDScriptNativeClass>(obj_B);
+
+#ifdef DEBUG_ENABLED
+ if (!nc) {
+ err_text = "Right operand of 'is' is not a class (type: '" + obj_B->get_class() + "').";
+ OPCODE_BREAK;
+ }
+#endif
+ extends_ok = ClassDB::is_parent_class(obj_A->get_class_name(), nc->get_name());
+ }
+ }
+
+ *dst = extends_ok;
+ ip += 4;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_IS_BUILTIN) {
+ CHECK_SPACE(4);
+
+ GET_INSTRUCTION_ARG(value, 0);
+ GET_INSTRUCTION_ARG(dst, 1);
+ Variant::Type var_type = (Variant::Type)_code_ptr[ip + 3];
+
+ GD_ERR_BREAK(var_type < 0 || var_type >= Variant::VARIANT_MAX);
+
+ *dst = value->get_type() == var_type;
+ ip += 4;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_SET_KEYED) {
+ CHECK_SPACE(3);
+
+ GET_INSTRUCTION_ARG(dst, 0);
+ GET_INSTRUCTION_ARG(index, 1);
+ GET_INSTRUCTION_ARG(value, 2);
+
+ bool valid;
+ dst->set(*index, *value, &valid);
+
+#ifdef DEBUG_ENABLED
+ if (!valid) {
+ String v = index->operator String();
+ if (v != "") {
+ v = "'" + v + "'";
+ } else {
+ v = "of type '" + _get_var_type(index) + "'";
+ }
+ err_text = "Invalid set index " + v + " (on base: '" + _get_var_type(dst) + "') with value of type '" + _get_var_type(value) + "'";
+ OPCODE_BREAK;
+ }
+#endif
+ ip += 4;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_SET_KEYED_VALIDATED) {
+ CHECK_SPACE(4);
+
+ GET_INSTRUCTION_ARG(dst, 0);
+ GET_INSTRUCTION_ARG(index, 1);
+ GET_INSTRUCTION_ARG(value, 2);
+
+ int index_setter = _code_ptr[ip + 4];
+ GD_ERR_BREAK(index_setter < 0 || index_setter >= _keyed_setters_count);
+ const Variant::ValidatedKeyedSetter setter = _keyed_setters_ptr[index_setter];
+
+ bool valid;
+ setter(dst, index, value, valid);
+
+#ifdef DEBUG_ENABLED
+ if (!valid) {
+ String v = index->operator String();
+ if (v != "") {
+ v = "'" + v + "'";
+ } else {
+ v = "of type '" + _get_var_type(index) + "'";
+ }
+ err_text = "Invalid set index " + v + " (on base: '" + _get_var_type(dst) + "') with value of type '" + _get_var_type(value) + "'";
+ OPCODE_BREAK;
+ }
+#endif
+ ip += 5;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_SET_INDEXED_VALIDATED) {
+ CHECK_SPACE(4);
+
+ GET_INSTRUCTION_ARG(dst, 0);
+ GET_INSTRUCTION_ARG(index, 1);
+ GET_INSTRUCTION_ARG(value, 2);
+
+ int index_setter = _code_ptr[ip + 4];
+ GD_ERR_BREAK(index_setter < 0 || index_setter >= _indexed_setters_count);
+ const Variant::ValidatedIndexedSetter setter = _indexed_setters_ptr[index_setter];
+
+ int64_t int_index = *VariantInternal::get_int(index);
+
+ bool oob;
+ setter(dst, int_index, value, oob);
+
+#ifdef DEBUG_ENABLED
+ if (oob) {
+ String v = index->operator String();
+ if (v != "") {
+ v = "'" + v + "'";
+ } else {
+ v = "of type '" + _get_var_type(index) + "'";
+ }
+ err_text = "Out of bounds set index " + v + " (on base: '" + _get_var_type(dst) + "')";
+ OPCODE_BREAK;
+ }
+#endif
+ ip += 5;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_GET_KEYED) {
+ CHECK_SPACE(3);
+
+ GET_INSTRUCTION_ARG(src, 0);
+ GET_INSTRUCTION_ARG(index, 1);
+ GET_INSTRUCTION_ARG(dst, 2);
+
+ bool valid;
+#ifdef DEBUG_ENABLED
+ // Allow better error message in cases where src and dst are the same stack position.
+ Variant ret = src->get(*index, &valid);
+#else
+ *dst = src->get(*index, &valid);
+
+#endif
+#ifdef DEBUG_ENABLED
+ if (!valid) {
+ String v = index->operator String();
+ if (v != "") {
+ v = "'" + v + "'";
+ } else {
+ v = "of type '" + _get_var_type(index) + "'";
+ }
+ err_text = "Invalid get index " + v + " (on base: '" + _get_var_type(src) + "').";
+ OPCODE_BREAK;
+ }
+ *dst = ret;
+#endif
+ ip += 4;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_GET_KEYED_VALIDATED) {
+ CHECK_SPACE(4);
+
+ GET_INSTRUCTION_ARG(src, 0);
+ GET_INSTRUCTION_ARG(key, 1);
+ GET_INSTRUCTION_ARG(dst, 2);
+
+ int index_getter = _code_ptr[ip + 4];
+ GD_ERR_BREAK(index_getter < 0 || index_getter >= _keyed_getters_count);
+ const Variant::ValidatedKeyedGetter getter = _keyed_getters_ptr[index_getter];
+
+ bool valid;
+#ifdef DEBUG_ENABLED
+ // Allow better error message in cases where src and dst are the same stack position.
+ Variant ret;
+ getter(src, key, &ret, valid);
+#else
+ getter(src, key, dst, valid);
+#endif
+#ifdef DEBUG_ENABLED
+ if (!valid) {
+ String v = key->operator String();
+ if (v != "") {
+ v = "'" + v + "'";
+ } else {
+ v = "of type '" + _get_var_type(key) + "'";
+ }
+ err_text = "Invalid get index " + v + " (on base: '" + _get_var_type(src) + "').";
+ OPCODE_BREAK;
+ }
+ *dst = ret;
+#endif
+ ip += 5;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_GET_INDEXED_VALIDATED) {
+ CHECK_SPACE(4);
+
+ GET_INSTRUCTION_ARG(src, 0);
+ GET_INSTRUCTION_ARG(index, 1);
+ GET_INSTRUCTION_ARG(dst, 2);
+
+ int index_getter = _code_ptr[ip + 4];
+ GD_ERR_BREAK(index_getter < 0 || index_getter >= _indexed_getters_count);
+ const Variant::ValidatedIndexedGetter getter = _indexed_getters_ptr[index_getter];
+
+ int64_t int_index = *VariantInternal::get_int(index);
+
+ bool oob;
+ getter(src, int_index, dst, oob);
+
+#ifdef DEBUG_ENABLED
+ if (oob) {
+ String v = index->operator String();
+ if (v != "") {
+ v = "'" + v + "'";
+ } else {
+ v = "of type '" + _get_var_type(index) + "'";
+ }
+ err_text = "Out of bounds get index " + v + " (on base: '" + _get_var_type(src) + "')";
+ OPCODE_BREAK;
+ }
+#endif
+ ip += 5;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_SET_NAMED) {
+ CHECK_SPACE(3);
+
+ GET_INSTRUCTION_ARG(dst, 0);
+ GET_INSTRUCTION_ARG(value, 1);
+
+ int indexname = _code_ptr[ip + 3];
+
+ GD_ERR_BREAK(indexname < 0 || indexname >= _global_names_count);
+ const StringName *index = &_global_names_ptr[indexname];
+
+ bool valid;
+ dst->set_named(*index, *value, valid);
+
+#ifdef DEBUG_ENABLED
+ if (!valid) {
+ String err_type;
+ err_text = "Invalid set index '" + String(*index) + "' (on base: '" + _get_var_type(dst) + "') with value of type '" + _get_var_type(value) + "'.";
+ OPCODE_BREAK;
+ }
+#endif
+ ip += 4;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_SET_NAMED_VALIDATED) {
+ CHECK_SPACE(3);
+
+ GET_INSTRUCTION_ARG(dst, 0);
+ GET_INSTRUCTION_ARG(value, 1);
+
+ int index_setter = _code_ptr[ip + 3];
+ GD_ERR_BREAK(index_setter < 0 || index_setter >= _setters_count);
+ const Variant::ValidatedSetter setter = _setters_ptr[index_setter];
+
+ setter(dst, value);
+ ip += 4;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_GET_NAMED) {
+ CHECK_SPACE(4);
+
+ GET_INSTRUCTION_ARG(src, 0);
+ GET_INSTRUCTION_ARG(dst, 1);
+
+ int indexname = _code_ptr[ip + 3];
+
+ GD_ERR_BREAK(indexname < 0 || indexname >= _global_names_count);
+ const StringName *index = &_global_names_ptr[indexname];
+
+ bool valid;
+#ifdef DEBUG_ENABLED
+ //allow better error message in cases where src and dst are the same stack position
+ Variant ret = src->get_named(*index, valid);
+
+#else
+ *dst = src->get_named(*index, valid);
+#endif
+#ifdef DEBUG_ENABLED
+ if (!valid) {
+ if (src->has_method(*index)) {
+ err_text = "Invalid get index '" + index->operator String() + "' (on base: '" + _get_var_type(src) + "'). Did you mean '." + index->operator String() + "()' or funcref(obj, \"" + index->operator String() + "\") ?";
+ } else {
+ err_text = "Invalid get index '" + index->operator String() + "' (on base: '" + _get_var_type(src) + "').";
+ }
+ OPCODE_BREAK;
+ }
+ *dst = ret;
+#endif
+ ip += 4;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_GET_NAMED_VALIDATED) {
+ CHECK_SPACE(3);
+
+ GET_INSTRUCTION_ARG(src, 0);
+ GET_INSTRUCTION_ARG(dst, 1);
+
+ int index_getter = _code_ptr[ip + 3];
+ GD_ERR_BREAK(index_getter < 0 || index_getter >= _getters_count);
+ const Variant::ValidatedGetter getter = _getters_ptr[index_getter];
+
+ getter(src, dst);
+ ip += 4;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_SET_MEMBER) {
+ CHECK_SPACE(3);
+ GET_INSTRUCTION_ARG(src, 0);
+ int indexname = _code_ptr[ip + 2];
+ GD_ERR_BREAK(indexname < 0 || indexname >= _global_names_count);
+ const StringName *index = &_global_names_ptr[indexname];
+
+ bool valid;
+#ifndef DEBUG_ENABLED
+ ClassDB::set_property(p_instance->owner, *index, *src, &valid);
+#else
+ bool ok = ClassDB::set_property(p_instance->owner, *index, *src, &valid);
+ if (!ok) {
+ err_text = "Internal error setting property: " + String(*index);
+ OPCODE_BREAK;
+ } else if (!valid) {
+ err_text = "Error setting property '" + String(*index) + "' with value of type " + Variant::get_type_name(src->get_type()) + ".";
+ OPCODE_BREAK;
+ }
+#endif
+ ip += 3;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_GET_MEMBER) {
+ CHECK_SPACE(3);
+ GET_INSTRUCTION_ARG(dst, 0);
+ int indexname = _code_ptr[ip + 2];
+ GD_ERR_BREAK(indexname < 0 || indexname >= _global_names_count);
+ const StringName *index = &_global_names_ptr[indexname];
+#ifndef DEBUG_ENABLED
+ ClassDB::get_property(p_instance->owner, *index, *dst);
+#else
+ bool ok = ClassDB::get_property(p_instance->owner, *index, *dst);
+ if (!ok) {
+ err_text = "Internal error getting property: " + String(*index);
+ OPCODE_BREAK;
+ }
+#endif
+ ip += 3;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_ASSIGN) {
+ CHECK_SPACE(3);
+ GET_INSTRUCTION_ARG(dst, 0);
+ GET_INSTRUCTION_ARG(src, 1);
+
+ *dst = *src;
+
+ ip += 3;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_ASSIGN_TRUE) {
+ CHECK_SPACE(2);
+ GET_INSTRUCTION_ARG(dst, 0);
+
+ *dst = true;
+
+ ip += 2;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_ASSIGN_FALSE) {
+ CHECK_SPACE(2);
+ GET_INSTRUCTION_ARG(dst, 0);
+
+ *dst = false;
+
+ ip += 2;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_ASSIGN_TYPED_BUILTIN) {
+ CHECK_SPACE(4);
+ GET_INSTRUCTION_ARG(dst, 0);
+ GET_INSTRUCTION_ARG(src, 1);
+
+ Variant::Type var_type = (Variant::Type)_code_ptr[ip + 3];
+ GD_ERR_BREAK(var_type < 0 || var_type >= Variant::VARIANT_MAX);
+
+ if (src->get_type() != var_type) {
+#ifdef DEBUG_ENABLED
+ if (Variant::can_convert_strict(src->get_type(), var_type)) {
+#endif // DEBUG_ENABLED
+ Callable::CallError ce;
+ Variant::construct(var_type, *dst, const_cast<const Variant **>(&src), 1, ce);
+ } else {
+#ifdef DEBUG_ENABLED
+ err_text = "Trying to assign value of type '" + Variant::get_type_name(src->get_type()) +
+ "' to a variable of type '" + Variant::get_type_name(var_type) + "'.";
+ OPCODE_BREAK;
+ }
+ } else {
+#endif // DEBUG_ENABLED
+ *dst = *src;
+ }
+
+ ip += 4;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_ASSIGN_TYPED_NATIVE) {
+ CHECK_SPACE(4);
+ GET_INSTRUCTION_ARG(dst, 0);
+ GET_INSTRUCTION_ARG(src, 1);
+
+#ifdef DEBUG_ENABLED
+ GET_INSTRUCTION_ARG(type, 2);
+ GDScriptNativeClass *nc = Object::cast_to<GDScriptNativeClass>(type->operator Object *());
+ GD_ERR_BREAK(!nc);
+ if (src->get_type() != Variant::OBJECT && src->get_type() != Variant::NIL) {
+ err_text = "Trying to assign value of type '" + Variant::get_type_name(src->get_type()) +
+ "' to a variable of type '" + nc->get_name() + "'.";
+ OPCODE_BREAK;
+ }
+ Object *src_obj = src->operator Object *();
+
+ if (src_obj && !ClassDB::is_parent_class(src_obj->get_class_name(), nc->get_name())) {
+ err_text = "Trying to assign value of type '" + src_obj->get_class_name() +
+ "' to a variable of type '" + nc->get_name() + "'.";
+ OPCODE_BREAK;
+ }
+#endif // DEBUG_ENABLED
+ *dst = *src;
+
+ ip += 4;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_ASSIGN_TYPED_SCRIPT) {
+ CHECK_SPACE(4);
+ GET_INSTRUCTION_ARG(dst, 0);
+ GET_INSTRUCTION_ARG(src, 1);
+
+#ifdef DEBUG_ENABLED
+ GET_INSTRUCTION_ARG(type, 2);
+ Script *base_type = Object::cast_to<Script>(type->operator Object *());
+
+ GD_ERR_BREAK(!base_type);
+
+ if (src->get_type() != Variant::OBJECT && src->get_type() != Variant::NIL) {
+ err_text = "Trying to assign a non-object value to a variable of type '" + base_type->get_path().get_file() + "'.";
+ OPCODE_BREAK;
+ }
+
+ if (src->get_type() != Variant::NIL && src->operator Object *() != nullptr) {
+ ScriptInstance *scr_inst = src->operator Object *()->get_script_instance();
+ if (!scr_inst) {
+ err_text = "Trying to assign value of type '" + src->operator Object *()->get_class_name() +
+ "' to a variable of type '" + base_type->get_path().get_file() + "'.";
+ OPCODE_BREAK;
+ }
+
+ Script *src_type = src->operator Object *()->get_script_instance()->get_script().ptr();
+ bool valid = false;
+
+ while (src_type) {
+ if (src_type == base_type) {
+ valid = true;
+ break;
+ }
+ src_type = src_type->get_base_script().ptr();
+ }
+
+ if (!valid) {
+ err_text = "Trying to assign value of type '" + src->operator Object *()->get_script_instance()->get_script()->get_path().get_file() +
+ "' to a variable of type '" + base_type->get_path().get_file() + "'.";
+ OPCODE_BREAK;
+ }
+ }
+#endif // DEBUG_ENABLED
+
+ *dst = *src;
+
+ ip += 4;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_CAST_TO_BUILTIN) {
+ CHECK_SPACE(4);
+ GET_INSTRUCTION_ARG(src, 0);
+ GET_INSTRUCTION_ARG(dst, 1);
+ Variant::Type to_type = (Variant::Type)_code_ptr[ip + 3];
+
+ GD_ERR_BREAK(to_type < 0 || to_type >= Variant::VARIANT_MAX);
+
+ Callable::CallError err;
+ Variant::construct(to_type, *dst, (const Variant **)&src, 1, err);
+
+#ifdef DEBUG_ENABLED
+ if (err.error != Callable::CallError::CALL_OK) {
+ err_text = "Invalid cast: could not convert value to '" + Variant::get_type_name(to_type) + "'.";
+ OPCODE_BREAK;
+ }
+#endif
+
+ ip += 4;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_CAST_TO_NATIVE) {
+ CHECK_SPACE(4);
+ GET_INSTRUCTION_ARG(src, 0);
+ GET_INSTRUCTION_ARG(dst, 1);
+ GET_INSTRUCTION_ARG(to_type, 2);
+
+ GDScriptNativeClass *nc = Object::cast_to<GDScriptNativeClass>(to_type->operator Object *());
+ GD_ERR_BREAK(!nc);
+
+#ifdef DEBUG_ENABLED
+ if (src->get_type() != Variant::OBJECT && src->get_type() != Variant::NIL) {
+ err_text = "Invalid cast: can't convert a non-object value to an object type.";
+ OPCODE_BREAK;
+ }
+#endif
+ Object *src_obj = src->operator Object *();
+
+ if (src_obj && !ClassDB::is_parent_class(src_obj->get_class_name(), nc->get_name())) {
+ *dst = Variant(); // invalid cast, assign NULL
+ } else {
+ *dst = *src;
+ }
+
+ ip += 4;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_CAST_TO_SCRIPT) {
+ CHECK_SPACE(4);
+ GET_INSTRUCTION_ARG(src, 0);
+ GET_INSTRUCTION_ARG(dst, 1);
+ GET_INSTRUCTION_ARG(to_type, 2);
+
+ Script *base_type = Object::cast_to<Script>(to_type->operator Object *());
+
+ GD_ERR_BREAK(!base_type);
+
+#ifdef DEBUG_ENABLED
+ if (src->get_type() != Variant::OBJECT && src->get_type() != Variant::NIL) {
+ err_text = "Trying to assign a non-object value to a variable of type '" + base_type->get_path().get_file() + "'.";
+ OPCODE_BREAK;
+ }
+#endif
+
+ bool valid = false;
+
+ if (src->get_type() != Variant::NIL && src->operator Object *() != nullptr) {
+ ScriptInstance *scr_inst = src->operator Object *()->get_script_instance();
+
+ if (scr_inst) {
+ Script *src_type = src->operator Object *()->get_script_instance()->get_script().ptr();
+
+ while (src_type) {
+ if (src_type == base_type) {
+ valid = true;
+ break;
+ }
+ src_type = src_type->get_base_script().ptr();
+ }
+ }
+ }
+
+ if (valid) {
+ *dst = *src; // Valid cast, copy the source object
+ } else {
+ *dst = Variant(); // invalid cast, assign NULL
+ }
+
+ ip += 4;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_CONSTRUCT) {
+ CHECK_SPACE(2 + instr_arg_count);
+
+ ip += instr_arg_count;
+
+ int argc = _code_ptr[ip + 1];
+
+ Variant::Type t = Variant::Type(_code_ptr[ip + 2]);
+ Variant **argptrs = instruction_args;
+
+ GET_INSTRUCTION_ARG(dst, argc);
+
+ Callable::CallError err;
+ Variant::construct(t, *dst, (const Variant **)argptrs, argc, err);
+
+#ifdef DEBUG_ENABLED
+ if (err.error != Callable::CallError::CALL_OK) {
+ err_text = _get_call_error(err, "'" + Variant::get_type_name(t) + "' constructor", (const Variant **)argptrs);
+ OPCODE_BREAK;
+ }
+#endif
+
+ ip += 3;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_CONSTRUCT_VALIDATED) {
+ CHECK_SPACE(2 + instr_arg_count);
+
+ ip += instr_arg_count;
+
+ int argc = _code_ptr[ip + 1];
+
+ int constructor_idx = _code_ptr[ip + 2];
+ GD_ERR_BREAK(constructor_idx < 0 || constructor_idx >= _constructors_count);
+ Variant::ValidatedConstructor constructor = _constructors_ptr[constructor_idx];
+
+ Variant **argptrs = instruction_args;
+
+ GET_INSTRUCTION_ARG(dst, argc);
+
+ constructor(*dst, (const Variant **)argptrs);
+
+ ip += 3;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_CONSTRUCT_ARRAY) {
+ CHECK_SPACE(1 + instr_arg_count);
+ ip += instr_arg_count;
+
+ int argc = _code_ptr[ip + 1];
+ Array array;
+ array.resize(argc);
+
+ for (int i = 0; i < argc; i++) {
+ array[i] = *(instruction_args[i]);
+ }
+
+ GET_INSTRUCTION_ARG(dst, argc);
+
+ *dst = array;
+
+ ip += 2;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_CONSTRUCT_DICTIONARY) {
+ CHECK_SPACE(2 + instr_arg_count);
+
+ ip += instr_arg_count;
+
+ int argc = _code_ptr[ip + 1];
+ Dictionary dict;
+
+ for (int i = 0; i < argc; i++) {
+ GET_INSTRUCTION_ARG(k, i * 2 + 0);
+ GET_INSTRUCTION_ARG(v, i * 2 + 1);
+ dict[*k] = *v;
+ }
+
+ GET_INSTRUCTION_ARG(dst, argc * 2);
+
+ *dst = dict;
+
+ ip += 2;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_CALL_ASYNC)
+ OPCODE(OPCODE_CALL_RETURN)
+ OPCODE(OPCODE_CALL) {
+ CHECK_SPACE(3 + instr_arg_count);
+ bool call_ret = (_code_ptr[ip] & INSTR_MASK) != OPCODE_CALL;
+#ifdef DEBUG_ENABLED
+ bool call_async = (_code_ptr[ip] & INSTR_MASK) == OPCODE_CALL_ASYNC;
+#endif
+
+ ip += instr_arg_count;
+
+ int argc = _code_ptr[ip + 1];
+ GD_ERR_BREAK(argc < 0);
+
+ int methodname_idx = _code_ptr[ip + 2];
+ GD_ERR_BREAK(methodname_idx < 0 || methodname_idx >= _global_names_count);
+ const StringName *methodname = &_global_names_ptr[methodname_idx];
+
+ GET_INSTRUCTION_ARG(base, argc);
+ Variant **argptrs = instruction_args;
+
+#ifdef DEBUG_ENABLED
+ uint64_t call_time = 0;
+
+ if (GDScriptLanguage::get_singleton()->profiling) {
+ call_time = OS::get_singleton()->get_ticks_usec();
+ }
+
+#endif
+ Callable::CallError err;
+ if (call_ret) {
+ GET_INSTRUCTION_ARG(ret, argc + 1);
+ base->call(*methodname, (const Variant **)argptrs, argc, *ret, err);
+#ifdef DEBUG_ENABLED
+ if (!call_async && ret->get_type() == Variant::OBJECT) {
+ // Check if getting a function state without await.
+ bool was_freed = false;
+ Object *obj = ret->get_validated_object_with_check(was_freed);
+
+ if (was_freed) {
+ err_text = "Got a freed object as a result of the call.";
+ OPCODE_BREAK;
+ }
+ if (obj && obj->is_class_ptr(GDScriptFunctionState::get_class_ptr_static())) {
+ err_text = R"(Trying to call an async function without "await".)";
+ OPCODE_BREAK;
+ }
+ }
+#endif
+ } else {
+ Variant ret;
+ base->call(*methodname, (const Variant **)argptrs, argc, ret, err);
+ }
+#ifdef DEBUG_ENABLED
+ if (GDScriptLanguage::get_singleton()->profiling) {
+ function_call_time += OS::get_singleton()->get_ticks_usec() - call_time;
+ }
+
+ if (err.error != Callable::CallError::CALL_OK) {
+ String methodstr = *methodname;
+ String basestr = _get_var_type(base);
+
+ if (methodstr == "call") {
+ if (argc >= 1) {
+ methodstr = String(*argptrs[0]) + " (via call)";
+ if (err.error == Callable::CallError::CALL_ERROR_INVALID_ARGUMENT) {
+ err.argument += 1;
+ }
+ }
+ } else if (methodstr == "free") {
+ if (err.error == Callable::CallError::CALL_ERROR_INVALID_METHOD) {
+ if (base->is_ref()) {
+ err_text = "Attempted to free a reference.";
+ OPCODE_BREAK;
+ } else if (base->get_type() == Variant::OBJECT) {
+ err_text = "Attempted to free a locked object (calling or emitting).";
+ OPCODE_BREAK;
+ }
+ }
+ } else if (methodstr == "call_recursive" && basestr == "TreeItem") {
+ if (argc >= 1) {
+ methodstr = String(*argptrs[0]) + " (via TreeItem.call_recursive)";
+ if (err.error == Callable::CallError::CALL_ERROR_INVALID_ARGUMENT) {
+ err.argument += 1;
+ }
+ }
+ }
+ err_text = _get_call_error(err, "function '" + methodstr + "' in base '" + basestr + "'", (const Variant **)argptrs);
+ OPCODE_BREAK;
+ }
+#endif
+
+ ip += 3;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_CALL_METHOD_BIND)
+ OPCODE(OPCODE_CALL_METHOD_BIND_RET) {
+ CHECK_SPACE(3 + instr_arg_count);
+ bool call_ret = (_code_ptr[ip] & INSTR_MASK) == OPCODE_CALL_METHOD_BIND_RET;
+
+ ip += instr_arg_count;
+
+ int argc = _code_ptr[ip + 1];
+ GD_ERR_BREAK(argc < 0);
+ GD_ERR_BREAK(_code_ptr[ip + 2] < 0 || _code_ptr[ip + 2] >= _methods_count);
+ MethodBind *method = _methods_ptr[_code_ptr[ip + 2]];
+
+ GET_INSTRUCTION_ARG(base, argc);
+
+#ifdef DEBUG_ENABLED
+ bool freed = false;
+ Object *base_obj = base->get_validated_object_with_check(freed);
+ if (freed) {
+ err_text = "Trying to call a function on a previously freed instance.";
+ OPCODE_BREAK;
+ } else if (!base_obj) {
+ err_text = "Trying to call a function on a null value.";
+ OPCODE_BREAK;
+ }
+#else
+ Object *base_obj = base->operator Object *();
+#endif
+ Variant **argptrs = instruction_args;
+
+#ifdef DEBUG_ENABLED
+ uint64_t call_time = 0;
+
+ if (GDScriptLanguage::get_singleton()->profiling) {
+ call_time = OS::get_singleton()->get_ticks_usec();
+ }
+#endif
+
+ Callable::CallError err;
+ if (call_ret) {
+ GET_INSTRUCTION_ARG(ret, argc + 1);
+ *ret = method->call(base_obj, (const Variant **)argptrs, argc, err);
+ } else {
+ method->call(base_obj, (const Variant **)argptrs, argc, err);
+ }
+
+#ifdef DEBUG_ENABLED
+ if (GDScriptLanguage::get_singleton()->profiling) {
+ function_call_time += OS::get_singleton()->get_ticks_usec() - call_time;
+ }
+
+ if (err.error != Callable::CallError::CALL_OK) {
+ String methodstr = method->get_name();
+ String basestr = _get_var_type(base);
+
+ if (methodstr == "call") {
+ if (argc >= 1) {
+ methodstr = String(*argptrs[0]) + " (via call)";
+ if (err.error == Callable::CallError::CALL_ERROR_INVALID_ARGUMENT) {
+ err.argument += 1;
+ }
+ }
+ } else if (methodstr == "free") {
+ if (err.error == Callable::CallError::CALL_ERROR_INVALID_METHOD) {
+ if (base->is_ref()) {
+ err_text = "Attempted to free a reference.";
+ OPCODE_BREAK;
+ } else if (base->get_type() == Variant::OBJECT) {
+ err_text = "Attempted to free a locked object (calling or emitting).";
+ OPCODE_BREAK;
+ }
+ }
+ }
+ err_text = _get_call_error(err, "function '" + methodstr + "' in base '" + basestr + "'", (const Variant **)argptrs);
+ OPCODE_BREAK;
+ }
+#endif
+ ip += 3;
+ }
+ DISPATCH_OPCODE;
+
+#ifdef DEBUG_ENABLED
+#define OPCODE_CALL_PTR(m_type) \
+ OPCODE(OPCODE_CALL_PTRCALL_##m_type) { \
+ CHECK_SPACE(3 + instr_arg_count); \
+ ip += instr_arg_count; \
+ int argc = _code_ptr[ip + 1]; \
+ GD_ERR_BREAK(argc < 0); \
+ GET_INSTRUCTION_ARG(base, argc); \
+ GD_ERR_BREAK(_code_ptr[ip + 2] < 0 || _code_ptr[ip + 2] >= _methods_count); \
+ MethodBind *method = _methods_ptr[_code_ptr[ip + 2]]; \
+ bool freed = false; \
+ Object *base_obj = base->get_validated_object_with_check(freed); \
+ if (freed) { \
+ err_text = "Trying to call a function on a previously freed instance."; \
+ OPCODE_BREAK; \
+ } else if (!base_obj) { \
+ err_text = "Trying to call a function on a null value."; \
+ OPCODE_BREAK; \
+ } \
+ const void **argptrs = call_args_ptr; \
+ for (int i = 0; i < argc; i++) { \
+ GET_INSTRUCTION_ARG(v, i); \
+ argptrs[i] = VariantInternal::get_opaque_pointer((const Variant *)v); \
+ } \
+ uint64_t call_time = 0; \
+ if (GDScriptLanguage::get_singleton()->profiling) { \
+ call_time = OS::get_singleton()->get_ticks_usec(); \
+ } \
+ GET_INSTRUCTION_ARG(ret, argc + 1); \
+ VariantInternal::initialize(ret, Variant::m_type); \
+ void *ret_opaque = VariantInternal::OP_GET_##m_type(ret); \
+ method->ptrcall(base_obj, argptrs, ret_opaque); \
+ if (GDScriptLanguage::get_singleton()->profiling) { \
+ function_call_time += OS::get_singleton()->get_ticks_usec() - call_time; \
+ } \
+ ip += 3; \
+ } \
+ DISPATCH_OPCODE
+#else
+#define OPCODE_CALL_PTR(m_type) \
+ OPCODE(OPCODE_CALL_PTRCALL_##m_type) { \
+ CHECK_SPACE(3 + instr_arg_count); \
+ int argc = _code_ptr[ip + 1]; \
+ GET_INSTRUCTION_ARG(base, argc); \
+ MethodBind *method = _methods_ptr[_code_ptr[ip + 2]]; \
+ Object *base_obj = *VariantInternal::get_object(base); \
+ const void **argptrs = call_args_ptr; \
+ for (int i = 0; i < argc; i++) { \
+ GET_INSTRUCTION_ARG(v, i); \
+ argptrs[i] = VariantInternal::get_opaque_pointer((const Variant *)v); \
+ } \
+ GET_INSTRUCTION_ARG(ret, argc + 1); \
+ VariantInternal::initialize(ret, Variant::m_type); \
+ void *ret_opaque = VariantInternal::OP_GET_##m_type(ret); \
+ method->ptrcall(base_obj, argptrs, ret_opaque); \
+ ip += 3; \
+ } \
+ DISPATCH_OPCODE
+#endif
+
+ OPCODE_CALL_PTR(BOOL);
+ OPCODE_CALL_PTR(INT);
+ OPCODE_CALL_PTR(FLOAT);
+ OPCODE_CALL_PTR(STRING);
+ OPCODE_CALL_PTR(VECTOR2);
+ OPCODE_CALL_PTR(VECTOR2I);
+ OPCODE_CALL_PTR(RECT2);
+ OPCODE_CALL_PTR(RECT2I);
+ OPCODE_CALL_PTR(VECTOR3);
+ OPCODE_CALL_PTR(VECTOR3I);
+ OPCODE_CALL_PTR(TRANSFORM2D);
+ OPCODE_CALL_PTR(PLANE);
+ OPCODE_CALL_PTR(QUAT);
+ OPCODE_CALL_PTR(AABB);
+ OPCODE_CALL_PTR(BASIS);
+ OPCODE_CALL_PTR(TRANSFORM);
+ OPCODE_CALL_PTR(COLOR);
+ OPCODE_CALL_PTR(STRING_NAME);
+ OPCODE_CALL_PTR(NODE_PATH);
+ OPCODE_CALL_PTR(RID);
+ OPCODE_CALL_PTR(CALLABLE);
+ OPCODE_CALL_PTR(SIGNAL);
+ OPCODE_CALL_PTR(DICTIONARY);
+ OPCODE_CALL_PTR(ARRAY);
+ OPCODE_CALL_PTR(PACKED_BYTE_ARRAY);
+ OPCODE_CALL_PTR(PACKED_INT32_ARRAY);
+ OPCODE_CALL_PTR(PACKED_INT64_ARRAY);
+ OPCODE_CALL_PTR(PACKED_FLOAT32_ARRAY);
+ OPCODE_CALL_PTR(PACKED_FLOAT64_ARRAY);
+ OPCODE_CALL_PTR(PACKED_STRING_ARRAY);
+ OPCODE_CALL_PTR(PACKED_VECTOR2_ARRAY);
+ OPCODE_CALL_PTR(PACKED_VECTOR3_ARRAY);
+ OPCODE_CALL_PTR(PACKED_COLOR_ARRAY);
+ OPCODE(OPCODE_CALL_PTRCALL_OBJECT) {
+ CHECK_SPACE(3 + instr_arg_count);
+
+ ip += instr_arg_count;
+
+ int argc = _code_ptr[ip + 1];
+ GD_ERR_BREAK(argc < 0);
+
+ GD_ERR_BREAK(_code_ptr[ip + 2] < 0 || _code_ptr[ip + 2] >= _methods_count);
+ MethodBind *method = _methods_ptr[_code_ptr[ip + 2]];
+
+ GET_INSTRUCTION_ARG(base, argc);
+#ifdef DEBUG_ENABLED
+ bool freed = false;
+ Object *base_obj = base->get_validated_object_with_check(freed);
+ if (freed) {
+ err_text = "Trying to call a function on a previously freed instance.";
+ OPCODE_BREAK;
+ } else if (!base_obj) {
+ err_text = "Trying to call a function on a null value.";
+ OPCODE_BREAK;
+ }
+#else
+ Object *base_obj = *VariantInternal::get_object(base);
+#endif
+
+ const void **argptrs = call_args_ptr;
+
+ for (int i = 0; i < argc; i++) {
+ GET_INSTRUCTION_ARG(v, i);
+ argptrs[i] = VariantInternal::get_opaque_pointer((const Variant *)v);
+ }
+#ifdef DEBUG_ENABLED
+ uint64_t call_time = 0;
+
+ if (GDScriptLanguage::get_singleton()->profiling) {
+ call_time = OS::get_singleton()->get_ticks_usec();
+ }
+#endif
+
+ GET_INSTRUCTION_ARG(ret, argc + 1);
+ VariantInternal::initialize(ret, Variant::OBJECT);
+ Object **ret_opaque = VariantInternal::get_object(ret);
+ method->ptrcall(base_obj, argptrs, ret_opaque);
+ VariantInternal::set_object(ret, *ret_opaque);
+
+#ifdef DEBUG_ENABLED
+ if (GDScriptLanguage::get_singleton()->profiling) {
+ function_call_time += OS::get_singleton()->get_ticks_usec() - call_time;
+ }
+#endif
+ ip += 3;
+ }
+ DISPATCH_OPCODE;
+ OPCODE(OPCODE_CALL_PTRCALL_NO_RETURN) {
+ CHECK_SPACE(3 + instr_arg_count);
+
+ ip += instr_arg_count;
+
+ int argc = _code_ptr[ip + 1];
+ GD_ERR_BREAK(argc < 0);
+
+ GD_ERR_BREAK(_code_ptr[ip + 2] < 0 || _code_ptr[ip + 2] >= _methods_count);
+ MethodBind *method = _methods_ptr[_code_ptr[ip + 2]];
+
+ GET_INSTRUCTION_ARG(base, argc);
+#ifdef DEBUG_ENABLED
+ bool freed = false;
+ Object *base_obj = base->get_validated_object_with_check(freed);
+ if (freed) {
+ err_text = "Trying to call a function on a previously freed instance.";
+ OPCODE_BREAK;
+ } else if (!base_obj) {
+ err_text = "Trying to call a function on a null value.";
+ OPCODE_BREAK;
+ }
+#else
+ Object *base_obj = *VariantInternal::get_object(base);
+#endif
+ const void **argptrs = call_args_ptr;
+
+ for (int i = 0; i < argc; i++) {
+ GET_INSTRUCTION_ARG(v, i);
+ argptrs[i] = VariantInternal::get_opaque_pointer((const Variant *)v);
+ }
+#ifdef DEBUG_ENABLED
+ uint64_t call_time = 0;
+
+ if (GDScriptLanguage::get_singleton()->profiling) {
+ call_time = OS::get_singleton()->get_ticks_usec();
+ }
+#endif
+
+ GET_INSTRUCTION_ARG(ret, argc + 1);
+ VariantInternal::initialize(ret, Variant::NIL);
+ method->ptrcall(base_obj, argptrs, nullptr);
+
+#ifdef DEBUG_ENABLED
+ if (GDScriptLanguage::get_singleton()->profiling) {
+ function_call_time += OS::get_singleton()->get_ticks_usec() - call_time;
+ }
+#endif
+ ip += 3;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_CALL_BUILTIN_TYPE_VALIDATED) {
+ CHECK_SPACE(3 + instr_arg_count);
+
+ ip += instr_arg_count;
+
+ int argc = _code_ptr[ip + 1];
+ GD_ERR_BREAK(argc < 0);
+
+ GET_INSTRUCTION_ARG(base, argc);
+
+ GD_ERR_BREAK(_code_ptr[ip + 2] < 0 || _code_ptr[ip + 2] >= _builtin_methods_count);
+ Variant::ValidatedBuiltInMethod method = _builtin_methods_ptr[_code_ptr[ip + 2]];
+ Variant **argptrs = instruction_args;
+
+#ifdef DEBUG_ENABLED
+ uint64_t call_time = 0;
+ if (GDScriptLanguage::get_singleton()->profiling) {
+ call_time = OS::get_singleton()->get_ticks_usec();
+ }
+#endif
+
+ GET_INSTRUCTION_ARG(ret, argc + 1);
+ method(base, (const Variant **)argptrs, argc, ret);
+
+#ifdef DEBUG_ENABLED
+ if (GDScriptLanguage::get_singleton()->profiling) {
+ function_call_time += OS::get_singleton()->get_ticks_usec() - call_time;
+ }
+#endif
+
+ ip += 3;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_CALL_BUILT_IN) {
+ CHECK_SPACE(3 + instr_arg_count);
+
+ ip += instr_arg_count;
+
+ int argc = _code_ptr[ip + 1];
+ GD_ERR_BREAK(argc < 0);
+
+ GDScriptFunctions::Function func = GDScriptFunctions::Function(_code_ptr[ip + 2]);
+ Variant **argptrs = instruction_args;
+
+ GET_INSTRUCTION_ARG(dst, argc);
+
+ Callable::CallError err;
+ GDScriptFunctions::call(func, (const Variant **)argptrs, argc, *dst, err);
+
+#ifdef DEBUG_ENABLED
+ if (err.error != Callable::CallError::CALL_OK) {
+ String methodstr = GDScriptFunctions::get_func_name(func);
+ if (dst->get_type() == Variant::STRING) {
+ //call provided error string
+ err_text = "Error calling built-in function '" + methodstr + "': " + String(*dst);
+ } else {
+ err_text = _get_call_error(err, "built-in function '" + methodstr + "'", (const Variant **)argptrs);
+ }
+ OPCODE_BREAK;
+ }
+#endif
+ ip += 3;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_CALL_SELF_BASE) {
+ CHECK_SPACE(3 + instr_arg_count);
+
+ ip += instr_arg_count;
+
+ int self_fun = _code_ptr[ip + 1];
+#ifdef DEBUG_ENABLED
+ if (self_fun < 0 || self_fun >= _global_names_count) {
+ err_text = "compiler bug, function name not found";
+ OPCODE_BREAK;
+ }
+#endif
+ const StringName *methodname = &_global_names_ptr[self_fun];
+
+ int argc = _code_ptr[ip + 2];
+ GD_ERR_BREAK(argc < 0);
+
+ Variant **argptrs = instruction_args;
+
+ GET_INSTRUCTION_ARG(dst, argc);
+
+ const GDScript *gds = _script;
+
+ const Map<StringName, GDScriptFunction *>::Element *E = nullptr;
+ while (gds->base.ptr()) {
+ gds = gds->base.ptr();
+ E = gds->member_functions.find(*methodname);
+ if (E) {
+ break;
+ }
+ }
+
+ Callable::CallError err;
+
+ if (E) {
+ *dst = E->get()->call(p_instance, (const Variant **)argptrs, argc, err);
+ } else if (gds->native.ptr()) {
+ if (*methodname != GDScriptLanguage::get_singleton()->strings._init) {
+ MethodBind *mb = ClassDB::get_method(gds->native->get_name(), *methodname);
+ if (!mb) {
+ err.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
+ } else {
+ *dst = mb->call(p_instance->owner, (const Variant **)argptrs, argc, err);
+ }
+ } else {
+ err.error = Callable::CallError::CALL_OK;
+ }
+ } else {
+ if (*methodname != GDScriptLanguage::get_singleton()->strings._init) {
+ err.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
+ } else {
+ err.error = Callable::CallError::CALL_OK;
+ }
+ }
+
+ if (err.error != Callable::CallError::CALL_OK) {
+ String methodstr = *methodname;
+ err_text = _get_call_error(err, "function '" + methodstr + "'", (const Variant **)argptrs);
+
+ OPCODE_BREAK;
+ }
+
+ ip += 3;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_AWAIT) {
+ CHECK_SPACE(2);
+
+ // Do the oneshot connect.
+ GET_INSTRUCTION_ARG(argobj, 0);
+
+ Signal sig;
+ bool is_signal = true;
+
+ {
+ Variant result = *argobj;
+
+ if (argobj->get_type() == Variant::OBJECT) {
+ bool was_freed = false;
+ Object *obj = argobj->get_validated_object_with_check(was_freed);
+
+ if (was_freed) {
+ err_text = "Trying to await on a freed object.";
+ OPCODE_BREAK;
+ }
+
+ // Is this even possible to be null at this point?
+ if (obj) {
+ if (obj->is_class_ptr(GDScriptFunctionState::get_class_ptr_static())) {
+ static StringName completed = _scs_create("completed");
+ result = Signal(obj, completed);
+ }
+ }
+ }
+
+ if (result.get_type() != Variant::SIGNAL) {
+ ip += 4; // Skip OPCODE_AWAIT_RESUME and its data.
+ // The stack pointer should be the same, so we don't need to set a return value.
+ is_signal = false;
+ } else {
+ sig = result;
+ }
+ }
+
+ if (is_signal) {
+ Ref<GDScriptFunctionState> gdfs = memnew(GDScriptFunctionState);
+ gdfs->function = this;
+
+ gdfs->state.stack.resize(alloca_size);
+ //copy variant stack
+ for (int i = 0; i < _stack_size; i++) {
+ memnew_placement(&gdfs->state.stack.write[sizeof(Variant) * i], Variant(stack[i]));
+ }
+ gdfs->state.stack_size = _stack_size;
+ gdfs->state.self = self;
+ gdfs->state.alloca_size = alloca_size;
+ gdfs->state.ip = ip + 2;
+ gdfs->state.line = line;
+ gdfs->state.script = _script;
+ {
+ MutexLock lock(GDScriptLanguage::get_singleton()->lock);
+ _script->pending_func_states.add(&gdfs->scripts_list);
+ if (p_instance) {
+ gdfs->state.instance = p_instance;
+ p_instance->pending_func_states.add(&gdfs->instances_list);
+ } else {
+ gdfs->state.instance = nullptr;
+ }
+ }
+#ifdef DEBUG_ENABLED
+ gdfs->state.function_name = name;
+ gdfs->state.script_path = _script->get_path();
+#endif
+ gdfs->state.defarg = defarg;
+ gdfs->function = this;
+
+ retvalue = gdfs;
+
+ Error err = sig.connect(Callable(gdfs.ptr(), "_signal_callback"), varray(gdfs), Object::CONNECT_ONESHOT);
+ if (err != OK) {
+ err_text = "Error connecting to signal: " + sig.get_name() + " during await.";
+ OPCODE_BREAK;
+ }
+
+#ifdef DEBUG_ENABLED
+ exit_ok = true;
+ awaited = true;
+#endif
+ OPCODE_BREAK;
+ }
+ }
+ DISPATCH_OPCODE; // Needed for synchronous calls (when result is immediately available).
+
+ OPCODE(OPCODE_AWAIT_RESUME) {
+ CHECK_SPACE(2);
+#ifdef DEBUG_ENABLED
+ if (!p_state) {
+ err_text = ("Invalid Resume (bug?)");
+ OPCODE_BREAK;
+ }
+#endif
+ GET_INSTRUCTION_ARG(result, 0);
+ *result = p_state->result;
+ ip += 2;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_JUMP) {
+ CHECK_SPACE(2);
+ int to = _code_ptr[ip + 1];
+
+ GD_ERR_BREAK(to < 0 || to > _code_size);
+ ip = to;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_JUMP_IF) {
+ CHECK_SPACE(3);
+
+ GET_INSTRUCTION_ARG(test, 0);
+
+ bool result = test->booleanize();
+
+ if (result) {
+ int to = _code_ptr[ip + 2];
+ GD_ERR_BREAK(to < 0 || to > _code_size);
+ ip = to;
+ } else {
+ ip += 3;
+ }
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_JUMP_IF_NOT) {
+ CHECK_SPACE(3);
+
+ GET_INSTRUCTION_ARG(test, 0);
+
+ bool result = test->booleanize();
+
+ if (!result) {
+ int to = _code_ptr[ip + 2];
+ GD_ERR_BREAK(to < 0 || to > _code_size);
+ ip = to;
+ } else {
+ ip += 3;
+ }
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_JUMP_TO_DEF_ARGUMENT) {
+ CHECK_SPACE(2);
+ ip = _default_arg_ptr[defarg];
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_RETURN) {
+ CHECK_SPACE(2);
+ GET_INSTRUCTION_ARG(r, 0);
+ retvalue = *r;
+#ifdef DEBUG_ENABLED
+ exit_ok = true;
+#endif
+ OPCODE_BREAK;
+ }
+
+ OPCODE(OPCODE_ITERATE_BEGIN) {
+ CHECK_SPACE(8); // Space for this and a regular iterate.
+
+ GET_INSTRUCTION_ARG(counter, 0);
+ GET_INSTRUCTION_ARG(container, 1);
+
+ bool valid;
+ if (!container->iter_init(*counter, valid)) {
+#ifdef DEBUG_ENABLED
+ if (!valid) {
+ err_text = "Unable to iterate on object of type '" + Variant::get_type_name(container->get_type()) + "'.";
+ OPCODE_BREAK;
+ }
+#endif
+ int jumpto = _code_ptr[ip + 4];
+ GD_ERR_BREAK(jumpto < 0 || jumpto > _code_size);
+ ip = jumpto;
+ } else {
+ GET_INSTRUCTION_ARG(iterator, 2);
+
+ *iterator = container->iter_get(*counter, valid);
+#ifdef DEBUG_ENABLED
+ if (!valid) {
+ err_text = "Unable to obtain iterator object of type '" + Variant::get_type_name(container->get_type()) + "'.";
+ OPCODE_BREAK;
+ }
+#endif
+ ip += 5; // Skip regular iterate which is always next.
+ }
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_ITERATE_BEGIN_INT) {
+ CHECK_SPACE(8); // Check space for iterate instruction too.
+
+ GET_INSTRUCTION_ARG(counter, 0);
+ GET_INSTRUCTION_ARG(container, 1);
+
+ int64_t size = *VariantInternal::get_int(container);
+
+ VariantInternal::initialize(counter, Variant::INT);
+ *VariantInternal::get_int(counter) = 0;
+
+ if (size > 0) {
+ GET_INSTRUCTION_ARG(iterator, 2);
+ VariantInternal::initialize(iterator, Variant::INT);
+ *VariantInternal::get_int(iterator) = 0;
+
+ // Skip regular iterate.
+ ip += 5;
+ } else {
+ // Jump to end of loop.
+ int jumpto = _code_ptr[ip + 4];
+ GD_ERR_BREAK(jumpto < 0 || jumpto > _code_size);
+ ip = jumpto;
+ }
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_ITERATE_BEGIN_FLOAT) {
+ CHECK_SPACE(8); // Check space for iterate instruction too.
+
+ GET_INSTRUCTION_ARG(counter, 0);
+ GET_INSTRUCTION_ARG(container, 1);
+
+ double size = *VariantInternal::get_float(container);
+
+ VariantInternal::initialize(counter, Variant::FLOAT);
+ *VariantInternal::get_float(counter) = 0.0;
+
+ if (size > 0) {
+ GET_INSTRUCTION_ARG(iterator, 2);
+ VariantInternal::initialize(iterator, Variant::FLOAT);
+ *VariantInternal::get_float(iterator) = 0;
+
+ // Skip regular iterate.
+ ip += 5;
+ } else {
+ // Jump to end of loop.
+ int jumpto = _code_ptr[ip + 4];
+ GD_ERR_BREAK(jumpto < 0 || jumpto > _code_size);
+ ip = jumpto;
+ }
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_ITERATE_BEGIN_VECTOR2) {
+ CHECK_SPACE(8); // Check space for iterate instruction too.
+
+ GET_INSTRUCTION_ARG(counter, 0);
+ GET_INSTRUCTION_ARG(container, 1);
+
+ Vector2 *bounds = VariantInternal::get_vector2(container);
+
+ VariantInternal::initialize(counter, Variant::FLOAT);
+ *VariantInternal::get_float(counter) = bounds->x;
+
+ if (bounds->x < bounds->y) {
+ GET_INSTRUCTION_ARG(iterator, 2);
+ VariantInternal::initialize(iterator, Variant::FLOAT);
+ *VariantInternal::get_float(iterator) = bounds->x;
+
+ // Skip regular iterate.
+ ip += 5;
+ } else {
+ // Jump to end of loop.
+ int jumpto = _code_ptr[ip + 4];
+ GD_ERR_BREAK(jumpto < 0 || jumpto > _code_size);
+ ip = jumpto;
+ }
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_ITERATE_BEGIN_VECTOR2I) {
+ CHECK_SPACE(8); // Check space for iterate instruction too.
+
+ GET_INSTRUCTION_ARG(counter, 0);
+ GET_INSTRUCTION_ARG(container, 1);
+
+ Vector2i *bounds = VariantInternal::get_vector2i(container);
+
+ VariantInternal::initialize(counter, Variant::FLOAT);
+ *VariantInternal::get_int(counter) = bounds->x;
+
+ if (bounds->x < bounds->y) {
+ GET_INSTRUCTION_ARG(iterator, 2);
+ VariantInternal::initialize(iterator, Variant::INT);
+ *VariantInternal::get_int(iterator) = bounds->x;
+
+ // Skip regular iterate.
+ ip += 5;
+ } else {
+ // Jump to end of loop.
+ int jumpto = _code_ptr[ip + 4];
+ GD_ERR_BREAK(jumpto < 0 || jumpto > _code_size);
+ ip = jumpto;
+ }
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_ITERATE_BEGIN_VECTOR3) {
+ CHECK_SPACE(8); // Check space for iterate instruction too.
+
+ GET_INSTRUCTION_ARG(counter, 0);
+ GET_INSTRUCTION_ARG(container, 1);
+
+ Vector3 *bounds = VariantInternal::get_vector3(container);
+ double from = bounds->x;
+ double to = bounds->y;
+ double step = bounds->z;
+
+ VariantInternal::initialize(counter, Variant::FLOAT);
+ *VariantInternal::get_float(counter) = from;
+
+ bool do_continue = from == to ? false : (from < to ? step > 0 : step < 0);
+
+ if (do_continue) {
+ GET_INSTRUCTION_ARG(iterator, 2);
+ VariantInternal::initialize(iterator, Variant::FLOAT);
+ *VariantInternal::get_float(iterator) = from;
+
+ // Skip regular iterate.
+ ip += 5;
+ } else {
+ // Jump to end of loop.
+ int jumpto = _code_ptr[ip + 4];
+ GD_ERR_BREAK(jumpto < 0 || jumpto > _code_size);
+ ip = jumpto;
+ }
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_ITERATE_BEGIN_VECTOR3I) {
+ CHECK_SPACE(8); // Check space for iterate instruction too.
+
+ GET_INSTRUCTION_ARG(counter, 0);
+ GET_INSTRUCTION_ARG(container, 1);
+
+ Vector3i *bounds = VariantInternal::get_vector3i(container);
+ int64_t from = bounds->x;
+ int64_t to = bounds->y;
+ int64_t step = bounds->z;
+
+ VariantInternal::initialize(counter, Variant::INT);
+ *VariantInternal::get_int(counter) = from;
+
+ bool do_continue = from == to ? false : (from < to ? step > 0 : step < 0);
+
+ if (do_continue) {
+ GET_INSTRUCTION_ARG(iterator, 2);
+ VariantInternal::initialize(iterator, Variant::INT);
+ *VariantInternal::get_int(iterator) = from;
+
+ // Skip regular iterate.
+ ip += 5;
+ } else {
+ // Jump to end of loop.
+ int jumpto = _code_ptr[ip + 4];
+ GD_ERR_BREAK(jumpto < 0 || jumpto > _code_size);
+ ip = jumpto;
+ }
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_ITERATE_BEGIN_STRING) {
+ CHECK_SPACE(8); // Check space for iterate instruction too.
+
+ GET_INSTRUCTION_ARG(counter, 0);
+ GET_INSTRUCTION_ARG(container, 1);
+
+ String *str = VariantInternal::get_string(container);
+
+ VariantInternal::initialize(counter, Variant::INT);
+ *VariantInternal::get_int(counter) = 0;
+
+ if (!str->empty()) {
+ GET_INSTRUCTION_ARG(iterator, 2);
+ VariantInternal::initialize(iterator, Variant::STRING);
+ *VariantInternal::get_string(iterator) = str->substr(0, 1);
+
+ // Skip regular iterate.
+ ip += 5;
+ } else {
+ // Jump to end of loop.
+ int jumpto = _code_ptr[ip + 4];
+ GD_ERR_BREAK(jumpto < 0 || jumpto > _code_size);
+ ip = jumpto;
+ }
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_ITERATE_BEGIN_DICTIONARY) {
+ CHECK_SPACE(8); // Check space for iterate instruction too.
+
+ GET_INSTRUCTION_ARG(counter, 0);
+ GET_INSTRUCTION_ARG(container, 1);
+
+ Dictionary *dict = VariantInternal::get_dictionary(container);
+ const Variant *next = dict->next(nullptr);
+ *counter = *next;
+
+ if (!dict->empty()) {
+ GET_INSTRUCTION_ARG(iterator, 2);
+ *iterator = *next;
+
+ // Skip regular iterate.
+ ip += 5;
+ } else {
+ // Jump to end of loop.
+ int jumpto = _code_ptr[ip + 4];
+ GD_ERR_BREAK(jumpto < 0 || jumpto > _code_size);
+ ip = jumpto;
+ }
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_ITERATE_BEGIN_ARRAY) {
+ CHECK_SPACE(8); // Check space for iterate instruction too.
+
+ GET_INSTRUCTION_ARG(counter, 0);
+ GET_INSTRUCTION_ARG(container, 1);
+
+ Array *array = VariantInternal::get_array(container);
+
+ VariantInternal::initialize(counter, Variant::INT);
+ *VariantInternal::get_int(counter) = 0;
+
+ if (!array->empty()) {
+ GET_INSTRUCTION_ARG(iterator, 2);
+ *iterator = array->get(0);
+
+ // Skip regular iterate.
+ ip += 5;
+ } else {
+ // Jump to end of loop.
+ int jumpto = _code_ptr[ip + 4];
+ GD_ERR_BREAK(jumpto < 0 || jumpto > _code_size);
+ ip = jumpto;
+ }
+ }
+ DISPATCH_OPCODE;
+
+#define OPCODE_ITERATE_BEGIN_PACKED_ARRAY(m_var_type, m_elem_type, m_get_func, m_var_ret_type, m_ret_type, m_ret_get_func) \
+ OPCODE(OPCODE_ITERATE_BEGIN_PACKED_##m_var_type##_ARRAY) { \
+ CHECK_SPACE(8); \
+ GET_INSTRUCTION_ARG(counter, 0); \
+ GET_INSTRUCTION_ARG(container, 1); \
+ Vector<m_elem_type> *array = VariantInternal::m_get_func(container); \
+ VariantInternal::initialize(counter, Variant::INT); \
+ *VariantInternal::get_int(counter) = 0; \
+ if (!array->empty()) { \
+ GET_INSTRUCTION_ARG(iterator, 2); \
+ VariantInternal::initialize(iterator, Variant::m_var_ret_type); \
+ m_ret_type *it = VariantInternal::m_ret_get_func(iterator); \
+ *it = array->get(0); \
+ ip += 5; \
+ } else { \
+ int jumpto = _code_ptr[ip + 4]; \
+ GD_ERR_BREAK(jumpto<0 || jumpto> _code_size); \
+ ip = jumpto; \
+ } \
+ } \
+ DISPATCH_OPCODE
+
+ OPCODE_ITERATE_BEGIN_PACKED_ARRAY(BYTE, uint8_t, get_byte_array, INT, int64_t, get_int);
+ OPCODE_ITERATE_BEGIN_PACKED_ARRAY(INT32, int32_t, get_int32_array, INT, int64_t, get_int);
+ OPCODE_ITERATE_BEGIN_PACKED_ARRAY(INT64, int64_t, get_int64_array, INT, int64_t, get_int);
+ OPCODE_ITERATE_BEGIN_PACKED_ARRAY(FLOAT32, float, get_float32_array, FLOAT, double, get_float);
+ OPCODE_ITERATE_BEGIN_PACKED_ARRAY(FLOAT64, double, get_float64_array, FLOAT, double, get_float);
+ OPCODE_ITERATE_BEGIN_PACKED_ARRAY(STRING, String, get_string_array, STRING, String, get_string);
+ OPCODE_ITERATE_BEGIN_PACKED_ARRAY(VECTOR2, Vector2, get_vector2_array, VECTOR2, Vector2, get_vector2);
+ OPCODE_ITERATE_BEGIN_PACKED_ARRAY(VECTOR3, Vector3, get_vector3_array, VECTOR3, Vector3, get_vector3);
+ OPCODE_ITERATE_BEGIN_PACKED_ARRAY(COLOR, Color, get_color_array, COLOR, Color, get_color);
+
+ OPCODE(OPCODE_ITERATE_BEGIN_OBJECT) {
+ CHECK_SPACE(4);
+
+ GET_INSTRUCTION_ARG(counter, 0);
+ GET_INSTRUCTION_ARG(container, 1);
+
+#ifdef DEBUG_ENABLED
+ bool freed = false;
+ Object *obj = container->get_validated_object_with_check(freed);
+ if (freed) {
+ err_text = "Trying to iterate on a previously freed object.";
+ OPCODE_BREAK;
+ } else if (!obj) {
+ err_text = "Trying to iterate on a null value.";
+ OPCODE_BREAK;
+ }
+#else
+ Object *obj = *VariantInternal::get_object(container);
+#endif
+ Array ref;
+ ref.push_back(*counter);
+ Variant vref;
+ VariantInternal::initialize(&vref, Variant::ARRAY);
+ *VariantInternal::get_array(&vref) = ref;
+
+ Variant **args = instruction_args; // Overriding an instruction argument, but we don't need access to that anymore.
+ args[0] = &vref;
+
+ Callable::CallError ce;
+ Variant has_next = obj->call(CoreStringNames::get_singleton()->_iter_init, (const Variant **)args, 1, ce);
+
+#ifdef DEBUG_ENABLED
+ if (ce.error != Callable::CallError::CALL_OK) {
+ err_text = vformat(R"(There was an error calling "_iter_next" on iterator object of type %s.)", *container);
+ OPCODE_BREAK;
+ }
+#endif
+ if (!has_next.booleanize()) {
+ int jumpto = _code_ptr[ip + 4];
+ GD_ERR_BREAK(jumpto < 0 || jumpto > _code_size);
+ ip = jumpto;
+ } else {
+ GET_INSTRUCTION_ARG(iterator, 2);
+ *iterator = obj->call(CoreStringNames::get_singleton()->_iter_get, (const Variant **)args, 1, ce);
+#ifdef DEBUG_ENABLED
+ if (ce.error != Callable::CallError::CALL_OK) {
+ err_text = vformat(R"(There was an error calling "_iter_get" on iterator object of type %s.)", *container);
+ OPCODE_BREAK;
+ }
+#endif
+
+ ip += 5; // Loop again.
+ }
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_ITERATE) {
+ CHECK_SPACE(4);
+
+ GET_INSTRUCTION_ARG(counter, 0);
+ GET_INSTRUCTION_ARG(container, 1);
+
+ bool valid;
+ if (!container->iter_next(*counter, valid)) {
+#ifdef DEBUG_ENABLED
+ if (!valid) {
+ err_text = "Unable to iterate on object of type '" + Variant::get_type_name(container->get_type()) + "' (type changed since first iteration?).";
+ OPCODE_BREAK;
+ }
+#endif
+ int jumpto = _code_ptr[ip + 4];
+ GD_ERR_BREAK(jumpto < 0 || jumpto > _code_size);
+ ip = jumpto;
+ } else {
+ GET_INSTRUCTION_ARG(iterator, 2);
+
+ *iterator = container->iter_get(*counter, valid);
+#ifdef DEBUG_ENABLED
+ if (!valid) {
+ err_text = "Unable to obtain iterator object of type '" + Variant::get_type_name(container->get_type()) + "' (but was obtained on first iteration?).";
+ OPCODE_BREAK;
+ }
+#endif
+ ip += 5; //loop again
+ }
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_ITERATE_INT) {
+ CHECK_SPACE(4);
+
+ GET_INSTRUCTION_ARG(counter, 0);
+ GET_INSTRUCTION_ARG(container, 1);
+
+ int64_t size = *VariantInternal::get_int(container);
+ int64_t *count = VariantInternal::get_int(counter);
+
+ (*count)++;
+
+ if (*count >= size) {
+ int jumpto = _code_ptr[ip + 4];
+ GD_ERR_BREAK(jumpto < 0 || jumpto > _code_size);
+ ip = jumpto;
+ } else {
+ GET_INSTRUCTION_ARG(iterator, 2);
+ *VariantInternal::get_int(iterator) = *count;
+
+ ip += 5; // Loop again.
+ }
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_ITERATE_FLOAT) {
+ CHECK_SPACE(4);
+
+ GET_INSTRUCTION_ARG(counter, 0);
+ GET_INSTRUCTION_ARG(container, 1);
+
+ double size = *VariantInternal::get_float(container);
+ double *count = VariantInternal::get_float(counter);
+
+ (*count)++;
+
+ if (*count >= size) {
+ int jumpto = _code_ptr[ip + 4];
+ GD_ERR_BREAK(jumpto < 0 || jumpto > _code_size);
+ ip = jumpto;
+ } else {
+ GET_INSTRUCTION_ARG(iterator, 2);
+ *VariantInternal::get_float(iterator) = *count;
+
+ ip += 5; // Loop again.
+ }
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_ITERATE_VECTOR2) {
+ CHECK_SPACE(4);
+
+ GET_INSTRUCTION_ARG(counter, 0);
+ GET_INSTRUCTION_ARG(container, 1);
+
+ const Vector2 *bounds = VariantInternal::get_vector2((const Variant *)container);
+ double *count = VariantInternal::get_float(counter);
+
+ (*count)++;
+
+ if (*count >= bounds->y) {
+ int jumpto = _code_ptr[ip + 4];
+ GD_ERR_BREAK(jumpto < 0 || jumpto > _code_size);
+ ip = jumpto;
+ } else {
+ GET_INSTRUCTION_ARG(iterator, 2);
+ *VariantInternal::get_float(iterator) = *count;
+
+ ip += 5; // Loop again.
+ }
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_ITERATE_VECTOR2I) {
+ CHECK_SPACE(4);
+
+ GET_INSTRUCTION_ARG(counter, 0);
+ GET_INSTRUCTION_ARG(container, 1);
+
+ const Vector2i *bounds = VariantInternal::get_vector2i((const Variant *)container);
+ int64_t *count = VariantInternal::get_int(counter);
+
+ (*count)++;
+
+ if (*count >= bounds->y) {
+ int jumpto = _code_ptr[ip + 4];
+ GD_ERR_BREAK(jumpto < 0 || jumpto > _code_size);
+ ip = jumpto;
+ } else {
+ GET_INSTRUCTION_ARG(iterator, 2);
+ *VariantInternal::get_int(iterator) = *count;
+
+ ip += 5; // Loop again.
+ }
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_ITERATE_VECTOR3) {
+ CHECK_SPACE(4);
+
+ GET_INSTRUCTION_ARG(counter, 0);
+ GET_INSTRUCTION_ARG(container, 1);
+
+ const Vector3 *bounds = VariantInternal::get_vector3((const Variant *)container);
+ double *count = VariantInternal::get_float(counter);
+
+ *count += bounds->z;
+
+ if ((bounds->z < 0 && *count <= bounds->y) || (bounds->z > 0 && *count >= bounds->y)) {
+ int jumpto = _code_ptr[ip + 4];
+ GD_ERR_BREAK(jumpto < 0 || jumpto > _code_size);
+ ip = jumpto;
+ } else {
+ GET_INSTRUCTION_ARG(iterator, 2);
+ *VariantInternal::get_float(iterator) = *count;
+
+ ip += 5; // Loop again.
+ }
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_ITERATE_VECTOR3I) {
+ CHECK_SPACE(4);
+
+ GET_INSTRUCTION_ARG(counter, 0);
+ GET_INSTRUCTION_ARG(container, 1);
+
+ const Vector3i *bounds = VariantInternal::get_vector3i((const Variant *)container);
+ int64_t *count = VariantInternal::get_int(counter);
+
+ *count += bounds->z;
+
+ if ((bounds->z < 0 && *count <= bounds->y) || (bounds->z > 0 && *count >= bounds->y)) {
+ int jumpto = _code_ptr[ip + 4];
+ GD_ERR_BREAK(jumpto < 0 || jumpto > _code_size);
+ ip = jumpto;
+ } else {
+ GET_INSTRUCTION_ARG(iterator, 2);
+ *VariantInternal::get_int(iterator) = *count;
+
+ ip += 5; // Loop again.
+ }
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_ITERATE_STRING) {
+ CHECK_SPACE(4);
+
+ GET_INSTRUCTION_ARG(counter, 0);
+ GET_INSTRUCTION_ARG(container, 1);
+
+ const String *str = VariantInternal::get_string((const Variant *)container);
+ int64_t *idx = VariantInternal::get_int(counter);
+ (*idx)++;
+
+ if (*idx >= str->length()) {
+ int jumpto = _code_ptr[ip + 4];
+ GD_ERR_BREAK(jumpto < 0 || jumpto > _code_size);
+ ip = jumpto;
+ } else {
+ GET_INSTRUCTION_ARG(iterator, 2);
+ *VariantInternal::get_string(iterator) = str->substr(*idx, 1);
+
+ ip += 5; // Loop again.
+ }
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_ITERATE_DICTIONARY) {
+ CHECK_SPACE(4);
+
+ GET_INSTRUCTION_ARG(counter, 0);
+ GET_INSTRUCTION_ARG(container, 1);
+
+ const Dictionary *dict = VariantInternal::get_dictionary((const Variant *)container);
+ const Variant *next = dict->next(counter);
+
+ if (!next) {
+ int jumpto = _code_ptr[ip + 4];
+ GD_ERR_BREAK(jumpto < 0 || jumpto > _code_size);
+ ip = jumpto;
+ } else {
+ GET_INSTRUCTION_ARG(iterator, 2);
+ *counter = *next;
+ *iterator = *next;
+
+ ip += 5; // Loop again.
+ }
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_ITERATE_ARRAY) {
+ CHECK_SPACE(4);
+
+ GET_INSTRUCTION_ARG(counter, 0);
+ GET_INSTRUCTION_ARG(container, 1);
+
+ const Array *array = VariantInternal::get_array((const Variant *)container);
+ int64_t *idx = VariantInternal::get_int(counter);
+ (*idx)++;
+
+ if (*idx >= array->size()) {
+ int jumpto = _code_ptr[ip + 4];
+ GD_ERR_BREAK(jumpto < 0 || jumpto > _code_size);
+ ip = jumpto;
+ } else {
+ GET_INSTRUCTION_ARG(iterator, 2);
+ *iterator = array->get(*idx);
+
+ ip += 5; // Loop again.
+ }
+ }
+ DISPATCH_OPCODE;
+
+#define OPCODE_ITERATE_PACKED_ARRAY(m_var_type, m_elem_type, m_get_func, m_ret_get_func) \
+ OPCODE(OPCODE_ITERATE_PACKED_##m_var_type##_ARRAY) { \
+ CHECK_SPACE(4); \
+ GET_INSTRUCTION_ARG(counter, 0); \
+ GET_INSTRUCTION_ARG(container, 1); \
+ const Vector<m_elem_type> *array = VariantInternal::m_get_func((const Variant *)container); \
+ int64_t *idx = VariantInternal::get_int(counter); \
+ (*idx)++; \
+ if (*idx >= array->size()) { \
+ int jumpto = _code_ptr[ip + 4]; \
+ GD_ERR_BREAK(jumpto<0 || jumpto> _code_size); \
+ ip = jumpto; \
+ } else { \
+ GET_INSTRUCTION_ARG(iterator, 2); \
+ *VariantInternal::m_ret_get_func(iterator) = array->get(*idx); \
+ ip += 5; \
+ } \
+ } \
+ DISPATCH_OPCODE
+
+ OPCODE_ITERATE_PACKED_ARRAY(BYTE, uint8_t, get_byte_array, get_int);
+ OPCODE_ITERATE_PACKED_ARRAY(INT32, int32_t, get_int32_array, get_int);
+ OPCODE_ITERATE_PACKED_ARRAY(INT64, int64_t, get_int64_array, get_int);
+ OPCODE_ITERATE_PACKED_ARRAY(FLOAT32, float, get_float32_array, get_float);
+ OPCODE_ITERATE_PACKED_ARRAY(FLOAT64, double, get_float64_array, get_float);
+ OPCODE_ITERATE_PACKED_ARRAY(STRING, String, get_string_array, get_string);
+ OPCODE_ITERATE_PACKED_ARRAY(VECTOR2, Vector2, get_vector2_array, get_vector2);
+ OPCODE_ITERATE_PACKED_ARRAY(VECTOR3, Vector3, get_vector3_array, get_vector3);
+ OPCODE_ITERATE_PACKED_ARRAY(COLOR, Color, get_color_array, get_color);
+
+ OPCODE(OPCODE_ITERATE_OBJECT) {
+ CHECK_SPACE(4);
+
+ GET_INSTRUCTION_ARG(counter, 0);
+ GET_INSTRUCTION_ARG(container, 1);
+
+#ifdef DEBUG_ENABLED
+ bool freed = false;
+ Object *obj = container->get_validated_object_with_check(freed);
+ if (freed) {
+ err_text = "Trying to iterate on a previously freed object.";
+ OPCODE_BREAK;
+ } else if (!obj) {
+ err_text = "Trying to iterate on a null value.";
+ OPCODE_BREAK;
+ }
+#else
+ Object *obj = *VariantInternal::get_object(container);
+#endif
+ Array ref;
+ ref.push_back(*counter);
+ Variant vref;
+ VariantInternal::initialize(&vref, Variant::ARRAY);
+ *VariantInternal::get_array(&vref) = ref;
+
+ Variant **args = instruction_args; // Overriding an instruction argument, but we don't need access to that anymore.
+ args[0] = &vref;
+
+ Callable::CallError ce;
+ Variant has_next = obj->call(CoreStringNames::get_singleton()->_iter_next, (const Variant **)args, 1, ce);
+
+#ifdef DEBUG_ENABLED
+ if (ce.error != Callable::CallError::CALL_OK) {
+ err_text = vformat(R"(There was an error calling "_iter_next" on iterator object of type %s.)", *container);
+ OPCODE_BREAK;
+ }
+#endif
+ if (!has_next.booleanize()) {
+ int jumpto = _code_ptr[ip + 4];
+ GD_ERR_BREAK(jumpto < 0 || jumpto > _code_size);
+ ip = jumpto;
+ } else {
+ GET_INSTRUCTION_ARG(iterator, 2);
+ *iterator = obj->call(CoreStringNames::get_singleton()->_iter_get, (const Variant **)args, 1, ce);
+#ifdef DEBUG_ENABLED
+ if (ce.error != Callable::CallError::CALL_OK) {
+ err_text = vformat(R"(There was an error calling "_iter_get" on iterator object of type %s.)", *container);
+ OPCODE_BREAK;
+ }
+#endif
+
+ ip += 5; // Loop again.
+ }
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_ASSERT) {
+ CHECK_SPACE(3);
+
+#ifdef DEBUG_ENABLED
+ GET_INSTRUCTION_ARG(test, 0);
+ bool result = test->booleanize();
+
+ if (!result) {
+ String message_str;
+ if (_code_ptr[ip + 2] != 0) {
+ GET_INSTRUCTION_ARG(message, 1);
+ message_str = *message;
+ }
+ if (message_str.empty()) {
+ err_text = "Assertion failed.";
+ } else {
+ err_text = "Assertion failed: " + message_str;
+ }
+ OPCODE_BREAK;
+ }
+
+#endif
+ ip += 3;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_BREAKPOINT) {
+#ifdef DEBUG_ENABLED
+ if (EngineDebugger::is_active()) {
+ GDScriptLanguage::get_singleton()->debug_break("Breakpoint Statement", true);
+ }
+#endif
+ ip += 1;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_LINE) {
+ CHECK_SPACE(2);
+
+ line = _code_ptr[ip + 1];
+ ip += 2;
+
+ if (EngineDebugger::is_active()) {
+ // line
+ bool do_break = false;
+
+ if (EngineDebugger::get_script_debugger()->get_lines_left() > 0) {
+ if (EngineDebugger::get_script_debugger()->get_depth() <= 0) {
+ EngineDebugger::get_script_debugger()->set_lines_left(EngineDebugger::get_script_debugger()->get_lines_left() - 1);
+ }
+ if (EngineDebugger::get_script_debugger()->get_lines_left() <= 0) {
+ do_break = true;
+ }
+ }
+
+ if (EngineDebugger::get_script_debugger()->is_breakpoint(line, source)) {
+ do_break = true;
+ }
+
+ if (do_break) {
+ GDScriptLanguage::get_singleton()->debug_break("Breakpoint", true);
+ }
+
+ EngineDebugger::get_singleton()->line_poll();
+ }
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_END) {
+#ifdef DEBUG_ENABLED
+ exit_ok = true;
+#endif
+ OPCODE_BREAK;
+ }
+
+#if 0 // Enable for debugging.
+ default: {
+ err_text = "Illegal opcode " + itos(_code_ptr[ip]) + " at address " + itos(ip);
+ OPCODE_BREAK;
+ }
+#endif
+ }
+
+ OPCODES_END
+#ifdef DEBUG_ENABLED
+ if (exit_ok) {
+ OPCODE_OUT;
+ }
+ //error
+ // function, file, line, error, explanation
+ String err_file;
+ if (p_instance && ObjectDB::get_instance(p_instance->owner_id) != nullptr && p_instance->script->is_valid() && p_instance->script->path != "") {
+ err_file = p_instance->script->path;
+ } else if (script) {
+ err_file = script->path;
+ }
+ if (err_file == "") {
+ err_file = "<built-in>";
+ }
+ String err_func = name;
+ if (p_instance && ObjectDB::get_instance(p_instance->owner_id) != nullptr && p_instance->script->is_valid() && p_instance->script->name != "") {
+ err_func = p_instance->script->name + "." + err_func;
+ }
+ int err_line = line;
+ if (err_text == "") {
+ err_text = "Internal Script Error! - opcode #" + itos(last_opcode) + " (report please).";
+ }
+
+ if (!GDScriptLanguage::get_singleton()->debug_break(err_text, false)) {
+ // debugger break did not happen
+
+ _err_print_error(err_func.utf8().get_data(), err_file.utf8().get_data(), err_line, err_text.utf8().get_data(), ERR_HANDLER_SCRIPT);
+ }
+
+#endif
+ OPCODE_OUT;
+ }
+
+ OPCODES_OUT
+#ifdef DEBUG_ENABLED
+ if (GDScriptLanguage::get_singleton()->profiling) {
+ uint64_t time_taken = OS::get_singleton()->get_ticks_usec() - function_start_time;
+ profile.total_time += time_taken;
+ profile.self_time += time_taken - function_call_time;
+ profile.frame_total_time += time_taken;
+ profile.frame_self_time += time_taken - function_call_time;
+ GDScriptLanguage::get_singleton()->script_frame_time += time_taken - function_call_time;
+ }
+
+ // Check if this is the last time the function is resuming from await
+ // Will be true if never awaited as well
+ // When it's the last resume it will postpone the exit from stack,
+ // so the debugger knows which function triggered the resume of the next function (if any)
+ if (!p_state || awaited) {
+ if (EngineDebugger::is_active()) {
+ GDScriptLanguage::get_singleton()->exit_function();
+ }
+#endif
+
+ if (_stack_size) {
+ //free stack
+ for (int i = 0; i < _stack_size; i++) {
+ stack[i].~Variant();
+ }
+ }
+
+#ifdef DEBUG_ENABLED
+ }
+#endif
+
+ return retvalue;
+}
diff --git a/modules/gdscript/gdscript_warning.cpp b/modules/gdscript/gdscript_warning.cpp
index 105facd9d0..56704d3e0a 100644
--- a/modules/gdscript/gdscript_warning.cpp
+++ b/modules/gdscript/gdscript_warning.cpp
@@ -30,7 +30,7 @@
#include "gdscript_warning.h"
-#include "core/variant.h"
+#include "core/variant/variant.h"
#ifdef DEBUG_ENABLED
diff --git a/modules/gdscript/gdscript_warning.h b/modules/gdscript/gdscript_warning.h
index e183d6f302..e0857703d8 100644
--- a/modules/gdscript/gdscript_warning.h
+++ b/modules/gdscript/gdscript_warning.h
@@ -33,8 +33,8 @@
#ifdef DEBUG_ENABLED
-#include "core/ustring.h"
-#include "core/vector.h"
+#include "core/string/ustring.h"
+#include "core/templates/vector.h"
class GDScriptWarning {
public:
diff --git a/modules/gdscript/icons/GDScript.svg b/modules/gdscript/icons/GDScript.svg
index 953bb9ae9e..aa59125ea9 100644
--- a/modules/gdscript/icons/GDScript.svg
+++ b/modules/gdscript/icons/GDScript.svg
@@ -1,5 +1 @@
-<svg width="16" height="16" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
-<g transform="translate(0 -1036.4)">
-<path transform="translate(0 1036.4)" d="m7 1l-0.56445 2.2578a5 5 0 0 0 -0.68945 0.2793l-1.9883-1.1934-1.4141 1.4141 1.1953 1.9941a5 5 0 0 0 -0.28516 0.68555l-2.2539 0.5625v2l2.2578 0.56445a5 5 0 0 0 0.2793 0.6875l-1.1934 1.9902 1.4141 1.4141 1.9941-1.1953a5 5 0 0 0 0.68555 0.28516l0.5625 2.2539h2l0.56445-2.2578a5 5 0 0 0 0.6875 -0.2793l1.9902 1.1934 1.4141-1.4141-1.1953-1.9941a5 5 0 0 0 0.28516 -0.68555l2.2539-0.5625v-2l-2.2578-0.56445a5 5 0 0 0 -0.2793 -0.6875l1.1934-1.9902-1.4141-1.4141-1.9941 1.1953a5 5 0 0 0 -0.68555 -0.28516l-0.5625-2.2539h-2zm1 5a2 2 0 0 1 2 2 2 2 0 0 1 -2 2 2 2 0 0 1 -2 -2 2 2 0 0 1 2 -2z" fill="#e0e0e0"/>
-</g>
-</svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m7 1-.56445 2.2578a5 5 0 0 0 -.68945.2793l-1.9883-1.1934-1.4141 1.4141 1.1953 1.9941a5 5 0 0 0 -.28516.68555l-2.2539.5625v2l2.2578.56445a5 5 0 0 0 .2793.6875l-1.1934 1.9902 1.4141 1.4141 1.9941-1.1953a5 5 0 0 0 .68555.28516l.5625 2.2539h2l.56445-2.2578a5 5 0 0 0 .6875-.2793l1.9902 1.1934 1.4141-1.4141-1.1953-1.9941a5 5 0 0 0 .28516-.68555l2.2539-.5625v-2l-2.2578-.56445a5 5 0 0 0 -.2793-.6875l1.1934-1.9902-1.4141-1.4141-1.9941 1.1953a5 5 0 0 0 -.68555-.28516l-.5625-2.2539h-2zm1 5a2 2 0 0 1 2 2 2 2 0 0 1 -2 2 2 2 0 0 1 -2-2 2 2 0 0 1 2-2z" fill="#e0e0e0"/></svg>
diff --git a/modules/gdscript/language_server/gdscript_extend_parser.h b/modules/gdscript/language_server/gdscript_extend_parser.h
index 0c031d7883..bb02d3dc99 100644
--- a/modules/gdscript/language_server/gdscript_extend_parser.h
+++ b/modules/gdscript/language_server/gdscript_extend_parser.h
@@ -32,7 +32,7 @@
#define GDSCRIPT_EXTEND_PARSER_H
#include "../gdscript_parser.h"
-#include "core/variant.h"
+#include "core/variant/variant.h"
#include "lsp.hpp"
#ifndef LINE_NUMBER_TO_INDEX
diff --git a/modules/gdscript/language_server/gdscript_language_protocol.cpp b/modules/gdscript/language_server/gdscript_language_protocol.cpp
index 2a67d2ff4f..6ddb0d149e 100644
--- a/modules/gdscript/language_server/gdscript_language_protocol.cpp
+++ b/modules/gdscript/language_server/gdscript_language_protocol.cpp
@@ -30,9 +30,9 @@
#include "gdscript_language_protocol.h"
+#include "core/config/project_settings.h"
#include "core/io/json.h"
#include "core/os/copymem.h"
-#include "core/project_settings.h"
#include "editor/editor_log.h"
#include "editor/editor_node.h"
diff --git a/modules/gdscript/language_server/gdscript_text_document.h b/modules/gdscript/language_server/gdscript_text_document.h
index b2fd0c31f9..2a5755bec6 100644
--- a/modules/gdscript/language_server/gdscript_text_document.h
+++ b/modules/gdscript/language_server/gdscript_text_document.h
@@ -31,8 +31,8 @@
#ifndef GDSCRIPT_TEXT_DOCUMENT_H
#define GDSCRIPT_TEXT_DOCUMENT_H
+#include "core/object/reference.h"
#include "core/os/file_access.h"
-#include "core/reference.h"
#include "lsp.hpp"
class GDScriptTextDocument : public Reference {
diff --git a/modules/gdscript/language_server/gdscript_workspace.cpp b/modules/gdscript/language_server/gdscript_workspace.cpp
index 776193e37c..f6643d07f9 100644
--- a/modules/gdscript/language_server/gdscript_workspace.cpp
+++ b/modules/gdscript/language_server/gdscript_workspace.cpp
@@ -32,8 +32,8 @@
#include "../gdscript.h"
#include "../gdscript_parser.h"
-#include "core/project_settings.h"
-#include "core/script_language.h"
+#include "core/config/project_settings.h"
+#include "core/object/script_language.h"
#include "editor/editor_file_system.h"
#include "editor/editor_help.h"
#include "editor/editor_node.h"
diff --git a/modules/gdscript/language_server/gdscript_workspace.h b/modules/gdscript/language_server/gdscript_workspace.h
index e45b06747d..fc244c6357 100644
--- a/modules/gdscript/language_server/gdscript_workspace.h
+++ b/modules/gdscript/language_server/gdscript_workspace.h
@@ -32,7 +32,7 @@
#define GDSCRIPT_WORKSPACE_H
#include "../gdscript_parser.h"
-#include "core/variant.h"
+#include "core/variant/variant.h"
#include "editor/editor_file_system.h"
#include "gdscript_extend_parser.h"
#include "lsp.hpp"
diff --git a/modules/gdscript/language_server/lsp.hpp b/modules/gdscript/language_server/lsp.hpp
index cf27a1578c..288fd41c87 100644
--- a/modules/gdscript/language_server/lsp.hpp
+++ b/modules/gdscript/language_server/lsp.hpp
@@ -31,8 +31,8 @@
#ifndef GODOT_LSP_H
#define GODOT_LSP_H
-#include "core/class_db.h"
-#include "core/list.h"
+#include "core/object/class_db.h"
+#include "core/templates/list.h"
#include "editor/doc_data.h"
namespace lsp {
@@ -1781,7 +1781,6 @@ static String marked_documentation(const String &p_bbcode) {
}
return markdown;
}
-
} // namespace lsp
#endif
diff --git a/modules/gdscript/register_types.cpp b/modules/gdscript/register_types.cpp
index da4cbe34c7..065f01e654 100644
--- a/modules/gdscript/register_types.cpp
+++ b/modules/gdscript/register_types.cpp
@@ -59,7 +59,7 @@ GDScriptCache *gdscript_cache = nullptr;
#include "editor/gdscript_translation_parser_plugin.h"
#ifndef GDSCRIPT_NO_LSP
-#include "core/engine.h"
+#include "core/config/engine.h"
#include "language_server/gdscript_language_server.h"
#endif // !GDSCRIPT_NO_LSP
diff --git a/modules/gdscript/tests/test_gdscript.cpp b/modules/gdscript/tests/test_gdscript.cpp
index 68d9984b43..643c2f10a2 100644
--- a/modules/gdscript/tests/test_gdscript.cpp
+++ b/modules/gdscript/tests/test_gdscript.cpp
@@ -30,10 +30,13 @@
#include "test_gdscript.h"
+#include "core/config/project_settings.h"
+#include "core/io/file_access_pack.h"
#include "core/os/file_access.h"
#include "core/os/main_loop.h"
#include "core/os/os.h"
-#include "core/string_builder.h"
+#include "core/string/string_builder.h"
+#include "scene/resources/packed_scene.h"
#include "modules/gdscript/gdscript_analyzer.h"
#include "modules/gdscript/gdscript_compiler.h"
@@ -179,6 +182,60 @@ static void test_compiler(const String &p_code, const String &p_script_path, con
}
}
+void init_autoloads() {
+ Map<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list();
+
+ // First pass, add the constants so they exist before any script is loaded.
+ for (Map<StringName, ProjectSettings::AutoloadInfo>::Element *E = autoloads.front(); E; E = E->next()) {
+ const ProjectSettings::AutoloadInfo &info = E->get();
+
+ if (info.is_singleton) {
+ for (int i = 0; i < ScriptServer::get_language_count(); i++) {
+ ScriptServer::get_language(i)->add_global_constant(info.name, Variant());
+ }
+ }
+ }
+
+ // Second pass, load into global constants.
+ for (Map<StringName, ProjectSettings::AutoloadInfo>::Element *E = autoloads.front(); E; E = E->next()) {
+ const ProjectSettings::AutoloadInfo &info = E->get();
+
+ if (!info.is_singleton) {
+ // Skip non-singletons since we don't have a scene tree here anyway.
+ continue;
+ }
+
+ RES res = ResourceLoader::load(info.path);
+ ERR_CONTINUE_MSG(res.is_null(), "Can't autoload: " + info.path);
+ Node *n = nullptr;
+ if (res->is_class("PackedScene")) {
+ Ref<PackedScene> ps = res;
+ n = ps->instance();
+ } else if (res->is_class("Script")) {
+ Ref<Script> script_res = res;
+ StringName ibt = script_res->get_instance_base_type();
+ bool valid_type = ClassDB::is_parent_class(ibt, "Node");
+ ERR_CONTINUE_MSG(!valid_type, "Script does not inherit a Node: " + info.path);
+
+ Object *obj = ClassDB::instance(ibt);
+
+ ERR_CONTINUE_MSG(obj == nullptr,
+ "Cannot instance script for autoload, expected 'Node' inheritance, got: " +
+ String(ibt));
+
+ n = Object::cast_to<Node>(obj);
+ n->set_script(script_res);
+ }
+
+ ERR_CONTINUE_MSG(!n, "Path in autoload not a node or script: " + info.path);
+ n->set_name(info.name);
+
+ for (int i = 0; i < ScriptServer::get_language_count(); i++) {
+ ScriptServer::get_language(i)->add_global_constant(info.name, n);
+ }
+ }
+}
+
void test(TestType p_type) {
List<String> cmdlargs = OS::get_singleton()->get_cmdline_args();
@@ -195,6 +252,21 @@ void test(TestType p_type) {
FileAccessRef fa = FileAccess::open(test, FileAccess::READ);
ERR_FAIL_COND_MSG(!fa, "Could not open file: " + test);
+ // Init PackedData since it's used by ProjectSettings.
+ PackedData *packed_data = memnew(PackedData);
+
+ // Setup project settings since it's needed by the languages to get the global scripts.
+ // This also sets up the base resource path.
+ Error err = ProjectSettings::get_singleton()->setup(fa->get_path_absolute().get_base_dir(), String(), true);
+ if (err) {
+ print_line("Could not load project settings.");
+ // Keep going since some scripts still work without this.
+ }
+
+ // Initialize the language for the test routine.
+ ScriptServer::init_languages();
+ init_autoloads();
+
Vector<uint8_t> buf;
int flen = fa->get_len();
buf.resize(fa->get_len() + 1);
@@ -226,6 +298,9 @@ void test(TestType p_type) {
case TEST_BYTECODE:
print_line("Not implemented.");
}
-}
+ // Destroy stuff we set up earlier.
+ ScriptServer::finish_languages();
+ memdelete(packed_data);
+}
} // namespace TestGDScript
diff --git a/modules/gdscript/tests/test_gdscript.h b/modules/gdscript/tests/test_gdscript.h
index 5aa962dcf8..6182629802 100644
--- a/modules/gdscript/tests/test_gdscript.h
+++ b/modules/gdscript/tests/test_gdscript.h
@@ -41,7 +41,6 @@ enum TestType {
};
void test(TestType p_type);
-
} // namespace TestGDScript
#endif // TEST_GDSCRIPT_H