summaryrefslogtreecommitdiffstats
path: root/modules/gdscript
diff options
context:
space:
mode:
Diffstat (limited to 'modules/gdscript')
-rw-r--r--modules/gdscript/doc_classes/@GDScript.xml4
-rw-r--r--modules/gdscript/editor/gdscript_docgen.cpp4
-rw-r--r--modules/gdscript/editor/gdscript_highlighter.cpp2
-rw-r--r--modules/gdscript/editor/gdscript_translation_parser_plugin.cpp124
-rw-r--r--modules/gdscript/editor/gdscript_translation_parser_plugin.h1
-rw-r--r--modules/gdscript/gdscript.cpp285
-rw-r--r--modules/gdscript/gdscript.h24
-rw-r--r--modules/gdscript/gdscript_analyzer.cpp130
-rw-r--r--modules/gdscript/gdscript_byte_codegen.cpp20
-rw-r--r--modules/gdscript/gdscript_compiler.cpp28
-rw-r--r--modules/gdscript/gdscript_editor.cpp97
-rw-r--r--modules/gdscript/gdscript_function.h63
-rw-r--r--modules/gdscript/gdscript_lambda_callable.cpp4
-rw-r--r--modules/gdscript/gdscript_lambda_callable.h4
-rw-r--r--modules/gdscript/gdscript_parser.cpp133
-rw-r--r--modules/gdscript/gdscript_parser.h64
-rw-r--r--modules/gdscript/gdscript_tokenizer.h6
-rw-r--r--modules/gdscript/gdscript_utility_functions.cpp7
-rw-r--r--modules/gdscript/gdscript_vm.cpp114
-rw-r--r--modules/gdscript/language_server/gdscript_text_document.cpp5
-rw-r--r--modules/gdscript/tests/gdscript_test_runner.cpp11
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/builtin_method_as_callable.gd6
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/builtin_method_as_callable.out3
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/static_method_as_callable.gd8
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/static_method_as_callable.out2
25 files changed, 722 insertions, 427 deletions
diff --git a/modules/gdscript/doc_classes/@GDScript.xml b/modules/gdscript/doc_classes/@GDScript.xml
index 3da6bcf10c..933bfba5ba 100644
--- a/modules/gdscript/doc_classes/@GDScript.xml
+++ b/modules/gdscript/doc_classes/@GDScript.xml
@@ -148,7 +148,7 @@
<return type="int" />
<param index="0" name="var" type="Variant" />
<description>
- Returns the length of the given Variant [param var]. The length can be the character count of a [String], the element count of any array type or the size of a [Dictionary]. For every other Variant type, a run-time error is generated and execution is stopped.
+ Returns the length of the given Variant [param var]. The length can be the character count of a [String] or [StringName], the element count of any array type, or the size of a [Dictionary]. For every other Variant type, a run-time error is generated and execution is stopped.
[codeblock]
a = [1, 2, 3, 4]
len(a) # Returns 4
@@ -162,7 +162,7 @@
<return type="Resource" />
<param index="0" name="path" type="String" />
<description>
- Returns a [Resource] from the filesystem located at the absolute [param path]. Unless it's already referenced elsewhere (such as in another script or in the scene), the resource is loaded from disk on function call, which might cause a slight delay, especially when loading large scenes. To avoid unnecessary delays when loading something multiple times, either store the resource in a variable or use [method preload].
+ Returns a [Resource] from the filesystem located at the absolute [param path]. Unless it's already referenced elsewhere (such as in another script or in the scene), the resource is loaded from disk on function call, which might cause a slight delay, especially when loading large scenes. To avoid unnecessary delays when loading something multiple times, either store the resource in a variable or use [method preload]. This method is equivalent of using [method ResourceLoader.load] with [constant ResourceLoader.CACHE_MODE_REUSE].
[b]Note:[/b] Resource paths can be obtained by right-clicking on a resource in the FileSystem dock and choosing "Copy Path", or by dragging the file from the FileSystem dock into the current script.
[codeblock]
# Load a scene called "main" located in the root of the project directory and cache it in a variable.
diff --git a/modules/gdscript/editor/gdscript_docgen.cpp b/modules/gdscript/editor/gdscript_docgen.cpp
index c3979dd290..00179109a3 100644
--- a/modules/gdscript/editor/gdscript_docgen.cpp
+++ b/modules/gdscript/editor/gdscript_docgen.cpp
@@ -64,8 +64,8 @@ void GDScriptDocGen::_doctype_from_gdtype(const GDType &p_gdtype, String &r_type
r_type = p_is_return ? "void" : "null";
return;
}
- if (p_gdtype.builtin_type == Variant::ARRAY && p_gdtype.has_container_element_type()) {
- _doctype_from_gdtype(p_gdtype.get_container_element_type(), r_type, r_enum);
+ if (p_gdtype.builtin_type == Variant::ARRAY && p_gdtype.has_container_element_type(0)) {
+ _doctype_from_gdtype(p_gdtype.get_container_element_type(0), r_type, r_enum);
if (!r_enum.is_empty()) {
r_type = "int[]";
r_enum += "[]";
diff --git a/modules/gdscript/editor/gdscript_highlighter.cpp b/modules/gdscript/editor/gdscript_highlighter.cpp
index 8dbd262b22..3df07f9794 100644
--- a/modules/gdscript/editor/gdscript_highlighter.cpp
+++ b/modules/gdscript/editor/gdscript_highlighter.cpp
@@ -494,7 +494,7 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_l
in_function_arg_dicts = 0;
}
- if (expect_type && (prev_is_char || str[j] == '=') && str[j] != '[' && str[j] != '.') {
+ if (expect_type && (prev_is_char || str[j] == '=') && str[j] != '[' && str[j] != ',' && str[j] != '.') {
expect_type = false;
}
diff --git a/modules/gdscript/editor/gdscript_translation_parser_plugin.cpp b/modules/gdscript/editor/gdscript_translation_parser_plugin.cpp
index 9128f104b8..f55b00ebe1 100644
--- a/modules/gdscript/editor/gdscript_translation_parser_plugin.cpp
+++ b/modules/gdscript/editor/gdscript_translation_parser_plugin.cpp
@@ -80,7 +80,7 @@ bool GDScriptEditorTranslationParserPlugin::_is_constant_string(const GDScriptPa
void GDScriptEditorTranslationParserPlugin::_traverse_class(const GDScriptParser::ClassNode *p_class) {
for (int i = 0; i < p_class->members.size(); i++) {
const GDScriptParser::ClassNode::Member &m = p_class->members[i];
- // There are 7 types of Member, but only class, function and variable can contain translatable strings.
+ // Other member types can't contain translatable strings.
switch (m.type) {
case GDScriptParser::ClassNode::Member::CLASS:
_traverse_class(m.m_class);
@@ -89,7 +89,11 @@ void GDScriptEditorTranslationParserPlugin::_traverse_class(const GDScriptParser
_traverse_function(m.function);
break;
case GDScriptParser::ClassNode::Member::VARIABLE:
- _read_variable(m.variable);
+ _assess_expression(m.variable->initializer);
+ if (m.variable->property == GDScriptParser::VariableNode::PROP_INLINE) {
+ _traverse_function(m.variable->setter);
+ _traverse_function(m.variable->getter);
+ }
break;
default:
break;
@@ -98,11 +102,14 @@ void GDScriptEditorTranslationParserPlugin::_traverse_class(const GDScriptParser
}
void GDScriptEditorTranslationParserPlugin::_traverse_function(const GDScriptParser::FunctionNode *p_func) {
- _traverse_block(p_func->body);
-}
+ if (!p_func) {
+ return;
+ }
-void GDScriptEditorTranslationParserPlugin::_read_variable(const GDScriptParser::VariableNode *p_var) {
- _assess_expression(p_var->initializer);
+ for (int i = 0; i < p_func->parameters.size(); i++) {
+ _assess_expression(p_func->parameters[i]->initializer);
+ }
+ _traverse_block(p_func->body);
}
void GDScriptEditorTranslationParserPlugin::_traverse_block(const GDScriptParser::SuiteNode *p_suite) {
@@ -114,53 +121,51 @@ void GDScriptEditorTranslationParserPlugin::_traverse_block(const GDScriptParser
for (int i = 0; i < statements.size(); i++) {
const GDScriptParser::Node *statement = statements[i];
- // Statements with Node type constant, break, continue, pass, breakpoint are skipped because they can't contain translatable strings.
+ // BREAK, BREAKPOINT, CONSTANT, CONTINUE, and PASS are skipped because they can't contain translatable strings.
switch (statement->type) {
- case GDScriptParser::Node::VARIABLE:
- _assess_expression(static_cast<const GDScriptParser::VariableNode *>(statement)->initializer);
- break;
+ case GDScriptParser::Node::ASSERT: {
+ const GDScriptParser::AssertNode *assert_node = static_cast<const GDScriptParser::AssertNode *>(statement);
+ _assess_expression(assert_node->condition);
+ _assess_expression(assert_node->message);
+ } break;
+ case GDScriptParser::Node::ASSIGNMENT: {
+ _assess_assignment(static_cast<const GDScriptParser::AssignmentNode *>(statement));
+ } break;
+ case GDScriptParser::Node::FOR: {
+ const GDScriptParser::ForNode *for_node = static_cast<const GDScriptParser::ForNode *>(statement);
+ _assess_expression(for_node->list);
+ _traverse_block(for_node->loop);
+ } break;
case GDScriptParser::Node::IF: {
const GDScriptParser::IfNode *if_node = static_cast<const GDScriptParser::IfNode *>(statement);
_assess_expression(if_node->condition);
- //FIXME : if the elif logic is changed in GDScriptParser, then this probably will have to change as well. See GDScriptParser::TreePrinter::print_if().
_traverse_block(if_node->true_block);
_traverse_block(if_node->false_block);
- break;
- }
- case GDScriptParser::Node::FOR: {
- const GDScriptParser::ForNode *for_node = static_cast<const GDScriptParser::ForNode *>(statement);
- _assess_expression(for_node->list);
- _traverse_block(for_node->loop);
- break;
- }
- case GDScriptParser::Node::WHILE: {
- const GDScriptParser::WhileNode *while_node = static_cast<const GDScriptParser::WhileNode *>(statement);
- _assess_expression(while_node->condition);
- _traverse_block(while_node->loop);
- break;
- }
+ } break;
case GDScriptParser::Node::MATCH: {
const GDScriptParser::MatchNode *match_node = static_cast<const GDScriptParser::MatchNode *>(statement);
_assess_expression(match_node->test);
for (int j = 0; j < match_node->branches.size(); j++) {
+ _traverse_block(match_node->branches[j]->guard_body);
_traverse_block(match_node->branches[j]->block);
}
- break;
- }
- case GDScriptParser::Node::RETURN:
+ } break;
+ case GDScriptParser::Node::RETURN: {
_assess_expression(static_cast<const GDScriptParser::ReturnNode *>(statement)->return_value);
- break;
- case GDScriptParser::Node::ASSERT:
- _assess_expression((static_cast<const GDScriptParser::AssertNode *>(statement))->condition);
- break;
- case GDScriptParser::Node::ASSIGNMENT:
- _assess_assignment(static_cast<const GDScriptParser::AssignmentNode *>(statement));
- break;
- default:
+ } break;
+ case GDScriptParser::Node::VARIABLE: {
+ _assess_expression(static_cast<const GDScriptParser::VariableNode *>(statement)->initializer);
+ } break;
+ case GDScriptParser::Node::WHILE: {
+ const GDScriptParser::WhileNode *while_node = static_cast<const GDScriptParser::WhileNode *>(statement);
+ _assess_expression(while_node->condition);
+ _traverse_block(while_node->loop);
+ } break;
+ default: {
if (statement->is_expression()) {
_assess_expression(static_cast<const GDScriptParser::ExpressionNode *>(statement));
}
- break;
+ } break;
}
}
}
@@ -172,25 +177,25 @@ void GDScriptEditorTranslationParserPlugin::_assess_expression(const GDScriptPar
return;
}
- // ExpressionNode of type await, cast, get_node, identifier, literal, preload, self, subscript, unary are ignored as they can't be CallNode
- // containing translation strings.
+ // GET_NODE, IDENTIFIER, LITERAL, PRELOAD, SELF, and TYPE are skipped because they can't contain translatable strings.
switch (p_expression->type) {
case GDScriptParser::Node::ARRAY: {
const GDScriptParser::ArrayNode *array_node = static_cast<const GDScriptParser::ArrayNode *>(p_expression);
for (int i = 0; i < array_node->elements.size(); i++) {
_assess_expression(array_node->elements[i]);
}
- break;
- }
- case GDScriptParser::Node::ASSIGNMENT:
+ } break;
+ case GDScriptParser::Node::ASSIGNMENT: {
_assess_assignment(static_cast<const GDScriptParser::AssignmentNode *>(p_expression));
- break;
+ } break;
+ case GDScriptParser::Node::AWAIT: {
+ _assess_expression(static_cast<const GDScriptParser::AwaitNode *>(p_expression)->to_await);
+ } break;
case GDScriptParser::Node::BINARY_OPERATOR: {
const GDScriptParser::BinaryOpNode *binary_op_node = static_cast<const GDScriptParser::BinaryOpNode *>(p_expression);
_assess_expression(binary_op_node->left_operand);
_assess_expression(binary_op_node->right_operand);
- break;
- }
+ } break;
case GDScriptParser::Node::CALL: {
const GDScriptParser::CallNode *call_node = static_cast<const GDScriptParser::CallNode *>(p_expression);
_extract_from_call(call_node);
@@ -198,23 +203,40 @@ void GDScriptEditorTranslationParserPlugin::_assess_expression(const GDScriptPar
_assess_expression(call_node->arguments[i]);
}
} break;
+ case GDScriptParser::Node::CAST: {
+ _assess_expression(static_cast<const GDScriptParser::CastNode *>(p_expression)->operand);
+ } break;
case GDScriptParser::Node::DICTIONARY: {
const GDScriptParser::DictionaryNode *dict_node = static_cast<const GDScriptParser::DictionaryNode *>(p_expression);
for (int i = 0; i < dict_node->elements.size(); i++) {
_assess_expression(dict_node->elements[i].key);
_assess_expression(dict_node->elements[i].value);
}
- break;
- }
+ } break;
+ case GDScriptParser::Node::LAMBDA: {
+ _traverse_function(static_cast<const GDScriptParser::LambdaNode *>(p_expression)->function);
+ } break;
+ case GDScriptParser::Node::SUBSCRIPT: {
+ const GDScriptParser::SubscriptNode *subscript_node = static_cast<const GDScriptParser::SubscriptNode *>(p_expression);
+ _assess_expression(subscript_node->base);
+ if (!subscript_node->is_attribute) {
+ _assess_expression(subscript_node->index);
+ }
+ } break;
case GDScriptParser::Node::TERNARY_OPERATOR: {
const GDScriptParser::TernaryOpNode *ternary_op_node = static_cast<const GDScriptParser::TernaryOpNode *>(p_expression);
_assess_expression(ternary_op_node->condition);
_assess_expression(ternary_op_node->true_expr);
_assess_expression(ternary_op_node->false_expr);
- break;
- }
- default:
- break;
+ } break;
+ case GDScriptParser::Node::TYPE_TEST: {
+ _assess_expression(static_cast<const GDScriptParser::TypeTestNode *>(p_expression)->operand);
+ } break;
+ case GDScriptParser::Node::UNARY_OPERATOR: {
+ _assess_expression(static_cast<const GDScriptParser::UnaryOpNode *>(p_expression)->operand);
+ } break;
+ default: {
+ } break;
}
}
diff --git a/modules/gdscript/editor/gdscript_translation_parser_plugin.h b/modules/gdscript/editor/gdscript_translation_parser_plugin.h
index 580c2a80cd..fab79a925f 100644
--- a/modules/gdscript/editor/gdscript_translation_parser_plugin.h
+++ b/modules/gdscript/editor/gdscript_translation_parser_plugin.h
@@ -59,7 +59,6 @@ class GDScriptEditorTranslationParserPlugin : public EditorTranslationParserPlug
void _traverse_function(const GDScriptParser::FunctionNode *p_func);
void _traverse_block(const GDScriptParser::SuiteNode *p_suite);
- void _read_variable(const GDScriptParser::VariableNode *p_var);
void _assess_expression(const GDScriptParser::ExpressionNode *p_expression);
void _assess_assignment(const GDScriptParser::AssignmentNode *p_assignment);
void _extract_from_call(const GDScriptParser::CallNode *p_call);
diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp
index 4accdb4d21..a999acd1bd 100644
--- a/modules/gdscript/gdscript.cpp
+++ b/modules/gdscript/gdscript.cpp
@@ -1117,8 +1117,7 @@ GDScript *GDScript::find_class(const String &p_qualified_name) {
// Starts at index 1 because index 0 was handled above.
for (int i = 1; result != nullptr && i < class_names.size(); i++) {
- String current_name = class_names[i];
- if (HashMap<StringName, Ref<GDScript>>::Iterator E = result->subclasses.find(current_name)) {
+ if (HashMap<StringName, Ref<GDScript>>::Iterator E = result->subclasses.find(class_names[i])) {
result = E->value.ptr();
} else {
// Couldn't find inner class.
@@ -1156,8 +1155,8 @@ RBSet<GDScript *> GDScript::get_dependencies() {
return dependencies;
}
-RBSet<GDScript *> GDScript::get_inverted_dependencies() {
- RBSet<GDScript *> inverted_dependencies;
+HashMap<GDScript *, RBSet<GDScript *>> GDScript::get_all_dependencies() {
+ HashMap<GDScript *, RBSet<GDScript *>> all_dependencies;
List<GDScript *> scripts;
{
@@ -1171,51 +1170,42 @@ RBSet<GDScript *> GDScript::get_inverted_dependencies() {
}
for (GDScript *scr : scripts) {
- if (scr == nullptr || scr == this || scr->destructing) {
+ if (scr == nullptr || scr->destructing) {
continue;
}
-
- RBSet<GDScript *> scr_dependencies = scr->get_dependencies();
- if (scr_dependencies.has(this)) {
- inverted_dependencies.insert(scr);
- }
+ all_dependencies.insert(scr, scr->get_dependencies());
}
- return inverted_dependencies;
+ return all_dependencies;
}
RBSet<GDScript *> GDScript::get_must_clear_dependencies() {
RBSet<GDScript *> dependencies = get_dependencies();
RBSet<GDScript *> must_clear_dependencies;
- HashMap<GDScript *, RBSet<GDScript *>> inverted_dependencies;
-
- for (GDScript *E : dependencies) {
- inverted_dependencies.insert(E, E->get_inverted_dependencies());
- }
+ HashMap<GDScript *, RBSet<GDScript *>> all_dependencies = get_all_dependencies();
RBSet<GDScript *> cant_clear;
- for (KeyValue<GDScript *, RBSet<GDScript *>> &E : inverted_dependencies) {
+ for (KeyValue<GDScript *, RBSet<GDScript *>> &E : all_dependencies) {
+ if (dependencies.has(E.key)) {
+ continue;
+ }
for (GDScript *F : E.value) {
- if (!dependencies.has(F)) {
- cant_clear.insert(E.key);
- for (GDScript *G : E.key->get_dependencies()) {
- cant_clear.insert(G);
- }
- break;
+ if (dependencies.has(F)) {
+ cant_clear.insert(F);
}
}
}
- for (KeyValue<GDScript *, RBSet<GDScript *>> &E : inverted_dependencies) {
- if (cant_clear.has(E.key) || ScriptServer::is_global_class(E.key->get_fully_qualified_name())) {
+ for (GDScript *E : dependencies) {
+ if (cant_clear.has(E) || ScriptServer::is_global_class(E->get_fully_qualified_name())) {
continue;
}
- must_clear_dependencies.insert(E.key);
+ must_clear_dependencies.insert(E);
}
cant_clear.clear();
dependencies.clear();
- inverted_dependencies.clear();
+ all_dependencies.clear();
return must_clear_dependencies;
}
@@ -1391,106 +1381,51 @@ String GDScript::debug_get_script_name(const Ref<Script> &p_script) {
}
#endif
-thread_local GDScript::UpdatableFuncPtr GDScript::func_ptrs_to_update_thread_local;
-GDScript::UpdatableFuncPtr *GDScript::func_ptrs_to_update_main_thread = &func_ptrs_to_update_thread_local;
+GDScript::UpdatableFuncPtr GDScript::func_ptrs_to_update_main_thread;
+thread_local GDScript::UpdatableFuncPtr *GDScript::func_ptrs_to_update_thread_local = nullptr;
-GDScript::UpdatableFuncPtrElement *GDScript::_add_func_ptr_to_update(GDScriptFunction **p_func_ptr_ptr) {
- MutexLock lock(func_ptrs_to_update_mutex);
-
- List<UpdatableFuncPtrElement>::Element *result = func_ptrs_to_update_elems.push_back(UpdatableFuncPtrElement());
+GDScript::UpdatableFuncPtrElement GDScript::_add_func_ptr_to_update(GDScriptFunction **p_func_ptr_ptr) {
+ UpdatableFuncPtrElement result = {};
{
- MutexLock lock2(func_ptrs_to_update_thread_local.mutex);
- result->get().element = func_ptrs_to_update_thread_local.ptrs.push_back(p_func_ptr_ptr);
- result->get().mutex = &func_ptrs_to_update_thread_local.mutex;
+ MutexLock lock(func_ptrs_to_update_thread_local->mutex);
+ result.element = func_ptrs_to_update_thread_local->ptrs.push_back(p_func_ptr_ptr);
+ result.func_ptr = func_ptrs_to_update_thread_local;
- if (likely(func_ptrs_to_update_thread_local.initialized)) {
- return &result->get();
+ if (likely(func_ptrs_to_update_thread_local->initialized)) {
+ return result;
}
- func_ptrs_to_update_thread_local.initialized = true;
+ func_ptrs_to_update_thread_local->initialized = true;
}
- func_ptrs_to_update.push_back(&func_ptrs_to_update_thread_local);
+ MutexLock lock(func_ptrs_to_update_mutex);
+ func_ptrs_to_update.push_back(func_ptrs_to_update_thread_local);
+ func_ptrs_to_update_thread_local->rc++;
- return &result->get();
+ return result;
}
-void GDScript::_remove_func_ptr_to_update(const UpdatableFuncPtrElement *p_func_ptr_element) {
- // None of these checks should ever fail, unless there's a bug.
- // They can be removed once we are sure they never catch anything.
- // Left here now due to extra safety needs late in the release cycle.
- ERR_FAIL_NULL(p_func_ptr_element);
- MutexLock lock(*p_func_ptr_element->mutex);
- ERR_FAIL_NULL(p_func_ptr_element->element);
- ERR_FAIL_NULL(p_func_ptr_element->mutex);
- p_func_ptr_element->element->erase();
+void GDScript::_remove_func_ptr_to_update(const UpdatableFuncPtrElement &p_func_ptr_element) {
+ ERR_FAIL_NULL(p_func_ptr_element.element);
+ ERR_FAIL_NULL(p_func_ptr_element.func_ptr);
+ MutexLock lock(p_func_ptr_element.func_ptr->mutex);
+ p_func_ptr_element.element->erase();
}
void GDScript::_fixup_thread_function_bookkeeping() {
// Transfer the ownership of these update items to the main thread,
// because the current one is dying, leaving theirs orphan, dangling.
- HashSet<GDScript *> scripts;
-
DEV_ASSERT(!Thread::is_main_thread());
- MutexLock lock(func_ptrs_to_update_main_thread->mutex);
- {
- MutexLock lock2(func_ptrs_to_update_thread_local.mutex);
-
- while (!func_ptrs_to_update_thread_local.ptrs.is_empty()) {
- // Transfer the thread-to-script records from the dying thread to the main one.
-
- List<GDScriptFunction **>::Element *E = func_ptrs_to_update_thread_local.ptrs.front();
- List<GDScriptFunction **>::Element *new_E = func_ptrs_to_update_main_thread->ptrs.push_front(E->get());
-
- GDScript *script = (*E->get())->get_script();
- if (!scripts.has(script)) {
- scripts.insert(script);
-
- // Replace dying thread by the main thread in the script-to-thread records.
-
- MutexLock lock3(script->func_ptrs_to_update_mutex);
- DEV_ASSERT(script->func_ptrs_to_update.find(&func_ptrs_to_update_thread_local));
- {
- for (List<UpdatableFuncPtrElement>::Element *F = script->func_ptrs_to_update_elems.front(); F; F = F->next()) {
- bool is_dying_thread_entry = F->get().mutex == &func_ptrs_to_update_thread_local.mutex;
- if (is_dying_thread_entry) {
- // This may lead to multiple main-thread entries, but that's not a problem
- // and allows to reuse the element, which is needed, since it's tracked by pointer.
- F->get().element = new_E;
- F->get().mutex = &func_ptrs_to_update_main_thread->mutex;
- }
- }
- }
- }
+ MutexLock lock(func_ptrs_to_update_main_thread.mutex);
+ MutexLock lock2(func_ptrs_to_update_thread_local->mutex);
- E->erase();
- }
- }
- func_ptrs_to_update_main_thread->initialized = true;
-
- {
- // Remove orphan thread-to-script entries from every script.
- // FIXME: This involves iterating through every script whenever a thread dies.
- // While it's OK that thread creation/destruction are heavy operations,
- // additional bookkeeping can be used to outperform this brute-force approach.
-
- GDScriptLanguage *gd_lang = GDScriptLanguage::get_singleton();
-
- MutexLock lock2(gd_lang->mutex);
-
- for (SelfList<GDScript> *s = gd_lang->script_list.first(); s; s = s->next()) {
- GDScript *script = s->self();
- for (List<UpdatableFuncPtr *>::Element *E = script->func_ptrs_to_update.front(); E; E = E->next()) {
- bool is_dying_thread_entry = &E->get()->mutex == &func_ptrs_to_update_thread_local.mutex;
- if (is_dying_thread_entry) {
- E->erase();
- break;
- }
- }
- }
+ while (!func_ptrs_to_update_thread_local->ptrs.is_empty()) {
+ List<GDScriptFunction **>::Element *E = func_ptrs_to_update_thread_local->ptrs.front();
+ E->transfer_to_back(&func_ptrs_to_update_main_thread.ptrs);
+ func_ptrs_to_update_thread_local->transferred = true;
}
}
@@ -1514,12 +1449,29 @@ void GDScript::clear(ClearData *p_clear_data) {
{
MutexLock outer_lock(func_ptrs_to_update_mutex);
for (UpdatableFuncPtr *updatable : func_ptrs_to_update) {
- MutexLock inner_lock(updatable->mutex);
- for (GDScriptFunction **func_ptr_ptr : updatable->ptrs) {
- *func_ptr_ptr = nullptr;
+ bool destroy = false;
+ {
+ MutexLock inner_lock(updatable->mutex);
+ if (updatable->transferred) {
+ func_ptrs_to_update_main_thread.mutex.lock();
+ }
+ for (GDScriptFunction **func_ptr_ptr : updatable->ptrs) {
+ *func_ptr_ptr = nullptr;
+ }
+ DEV_ASSERT(updatable->rc != 0);
+ updatable->rc--;
+ if (updatable->rc == 0) {
+ destroy = true;
+ }
+ if (updatable->transferred) {
+ func_ptrs_to_update_main_thread.mutex.unlock();
+ }
+ }
+ if (destroy) {
+ DEV_ASSERT(updatable != &func_ptrs_to_update_main_thread);
+ memdelete(updatable);
}
}
- func_ptrs_to_update_elems.clear();
}
RBSet<GDScript *> must_clear_dependencies = get_must_clear_dependencies();
@@ -2139,8 +2091,31 @@ void GDScriptLanguage::remove_named_global_constant(const StringName &p_name) {
named_globals.erase(p_name);
}
+void GDScriptLanguage::thread_enter() {
+ GDScript::func_ptrs_to_update_thread_local = memnew(GDScript::UpdatableFuncPtr);
+}
+
void GDScriptLanguage::thread_exit() {
+ // This thread may have been created before GDScript was up
+ // (which also means it can't have run any GDScript code at all).
+ if (!GDScript::func_ptrs_to_update_thread_local) {
+ return;
+ }
+
GDScript::_fixup_thread_function_bookkeeping();
+
+ bool destroy = false;
+ {
+ MutexLock lock(GDScript::func_ptrs_to_update_thread_local->mutex);
+ DEV_ASSERT(GDScript::func_ptrs_to_update_thread_local->rc != 0);
+ GDScript::func_ptrs_to_update_thread_local->rc--;
+ if (GDScript::func_ptrs_to_update_thread_local->rc == 0) {
+ destroy = true;
+ }
+ }
+ if (destroy) {
+ memdelete(GDScript::func_ptrs_to_update_thread_local);
+ }
}
void GDScriptLanguage::init() {
@@ -2175,6 +2150,8 @@ void GDScriptLanguage::init() {
_add_global(E.name, E.ptr);
}
+ GDScript::func_ptrs_to_update_thread_local = &GDScript::func_ptrs_to_update_main_thread;
+
#ifdef TESTS_ENABLED
GDScriptTests::GDScriptTestRunner::handle_cmdline();
#endif
@@ -2224,6 +2201,8 @@ void GDScriptLanguage::finish() {
}
script_list.clear();
function_list.clear();
+
+ DEV_ASSERT(GDScript::func_ptrs_to_update_main_thread.rc == 1);
}
void GDScriptLanguage::profiling_start() {
@@ -2241,6 +2220,8 @@ void GDScriptLanguage::profiling_start() {
elem->self()->profile.last_frame_call_count = 0;
elem->self()->profile.last_frame_self_time = 0;
elem->self()->profile.last_frame_total_time = 0;
+ elem->self()->profile.native_calls.clear();
+ elem->self()->profile.last_native_calls.clear();
elem = elem->next();
}
@@ -2248,6 +2229,13 @@ void GDScriptLanguage::profiling_start() {
#endif
}
+void GDScriptLanguage::profiling_set_save_native_calls(bool p_enable) {
+#ifdef DEBUG_ENABLED
+ MutexLock lock(mutex);
+ profile_native_calls = p_enable;
+#endif
+}
+
void GDScriptLanguage::profiling_stop() {
#ifdef DEBUG_ENABLED
MutexLock lock(this->mutex);
@@ -2262,17 +2250,32 @@ int GDScriptLanguage::profiling_get_accumulated_data(ProfilingInfo *p_info_arr,
MutexLock lock(this->mutex);
+ profiling_collate_native_call_data(true);
SelfList<GDScriptFunction> *elem = function_list.first();
while (elem) {
if (current >= p_info_max) {
break;
}
+ int last_non_internal = current;
p_info_arr[current].call_count = elem->self()->profile.call_count.get();
p_info_arr[current].self_time = elem->self()->profile.self_time.get();
p_info_arr[current].total_time = elem->self()->profile.total_time.get();
p_info_arr[current].signature = elem->self()->profile.signature;
- elem = elem->next();
current++;
+
+ int nat_time = 0;
+ HashMap<String, GDScriptFunction::Profile::NativeProfile>::ConstIterator nat_calls = elem->self()->profile.native_calls.begin();
+ while (nat_calls) {
+ p_info_arr[current].call_count = nat_calls->value.call_count;
+ p_info_arr[current].total_time = nat_calls->value.total_time;
+ p_info_arr[current].self_time = nat_calls->value.total_time;
+ p_info_arr[current].signature = nat_calls->value.signature;
+ nat_time += nat_calls->value.total_time;
+ current++;
+ ++nat_calls;
+ }
+ p_info_arr[last_non_internal].internal_time = nat_time;
+ elem = elem->next();
}
#endif
@@ -2285,17 +2288,33 @@ int GDScriptLanguage::profiling_get_frame_data(ProfilingInfo *p_info_arr, int p_
#ifdef DEBUG_ENABLED
MutexLock lock(this->mutex);
+ profiling_collate_native_call_data(false);
SelfList<GDScriptFunction> *elem = function_list.first();
while (elem) {
if (current >= p_info_max) {
break;
}
if (elem->self()->profile.last_frame_call_count > 0) {
+ int last_non_internal = current;
p_info_arr[current].call_count = elem->self()->profile.last_frame_call_count;
p_info_arr[current].self_time = elem->self()->profile.last_frame_self_time;
p_info_arr[current].total_time = elem->self()->profile.last_frame_total_time;
p_info_arr[current].signature = elem->self()->profile.signature;
current++;
+
+ int nat_time = 0;
+ HashMap<String, GDScriptFunction::Profile::NativeProfile>::ConstIterator nat_calls = elem->self()->profile.last_native_calls.begin();
+ while (nat_calls) {
+ p_info_arr[current].call_count = nat_calls->value.call_count;
+ p_info_arr[current].total_time = nat_calls->value.total_time;
+ p_info_arr[current].self_time = nat_calls->value.total_time;
+ p_info_arr[current].internal_time = nat_calls->value.total_time;
+ p_info_arr[current].signature = nat_calls->value.signature;
+ nat_time += nat_calls->value.total_time;
+ current++;
+ ++nat_calls;
+ }
+ p_info_arr[last_non_internal].internal_time = nat_time;
}
elem = elem->next();
}
@@ -2304,6 +2323,33 @@ int GDScriptLanguage::profiling_get_frame_data(ProfilingInfo *p_info_arr, int p_
return current;
}
+void GDScriptLanguage::profiling_collate_native_call_data(bool p_accumulated) {
+#ifdef DEBUG_ENABLED
+ // The same native call can be called from multiple functions, so join them together here.
+ // Only use the name of the function (ie signature.split[2]).
+ HashMap<String, GDScriptFunction::Profile::NativeProfile *> seen_nat_calls;
+ SelfList<GDScriptFunction> *elem = function_list.first();
+ while (elem) {
+ HashMap<String, GDScriptFunction::Profile::NativeProfile> *nat_calls = p_accumulated ? &elem->self()->profile.native_calls : &elem->self()->profile.last_native_calls;
+ HashMap<String, GDScriptFunction::Profile::NativeProfile>::Iterator it = nat_calls->begin();
+
+ while (it != nat_calls->end()) {
+ Vector<String> sig = it->value.signature.split("::");
+ HashMap<String, GDScriptFunction::Profile::NativeProfile *>::ConstIterator already_found = seen_nat_calls.find(sig[2]);
+ if (already_found) {
+ already_found->value->total_time += it->value.total_time;
+ already_found->value->call_count += it->value.call_count;
+ elem->self()->profile.last_native_calls.remove(it);
+ } else {
+ seen_nat_calls.insert(sig[2], &it->value);
+ }
+ ++it;
+ }
+ elem = elem->next();
+ }
+#endif
+}
+
struct GDScriptDepSort {
//must support sorting so inheritance works properly (parent must be reloaded first)
bool operator()(const Ref<GDScript> &A, const Ref<GDScript> &B) const {
@@ -2340,6 +2386,19 @@ void GDScriptLanguage::reload_all_scripts() {
}
elem = elem->next();
}
+
+#ifdef TOOLS_ENABLED
+ if (Engine::get_singleton()->is_editor_hint()) {
+ // Reload all pointers to existing singletons so that tool scripts can work with the reloaded extensions.
+ List<Engine::Singleton> singletons;
+ Engine::get_singleton()->get_singletons(&singletons);
+ for (const Engine::Singleton &E : singletons) {
+ if (globals.has(E.name)) {
+ _add_global(E.name, E.ptr);
+ }
+ }
+ }
+#endif
}
//as scripts are going to be reloaded, must proceed without locking here
@@ -2490,9 +2549,11 @@ void GDScriptLanguage::frame() {
elem->self()->profile.last_frame_call_count = elem->self()->profile.frame_call_count.get();
elem->self()->profile.last_frame_self_time = elem->self()->profile.frame_self_time.get();
elem->self()->profile.last_frame_total_time = elem->self()->profile.frame_total_time.get();
+ elem->self()->profile.last_native_calls = elem->self()->profile.native_calls;
elem->self()->profile.frame_call_count.set(0);
elem->self()->profile.frame_self_time.set(0);
elem->self()->profile.frame_total_time.set(0);
+ elem->self()->profile.native_calls.clear();
elem = elem->next();
}
}
diff --git a/modules/gdscript/gdscript.h b/modules/gdscript/gdscript.h
index 9b99f5ca0b..31811bba47 100644
--- a/modules/gdscript/gdscript.h
+++ b/modules/gdscript/gdscript.h
@@ -121,21 +121,23 @@ class GDScript : public Script {
struct UpdatableFuncPtr {
List<GDScriptFunction **> ptrs;
Mutex mutex;
- bool initialized = false;
+ bool initialized : 1;
+ bool transferred : 1;
+ uint32_t rc = 1;
+ UpdatableFuncPtr() :
+ initialized(false), transferred(false) {}
};
struct UpdatableFuncPtrElement {
List<GDScriptFunction **>::Element *element = nullptr;
- Mutex *mutex = nullptr;
+ UpdatableFuncPtr *func_ptr = nullptr;
};
- static thread_local UpdatableFuncPtr func_ptrs_to_update_thread_local;
- static thread_local LocalVector<List<UpdatableFuncPtr *>::Element> func_ptrs_to_update_entries_thread_local;
- static UpdatableFuncPtr *func_ptrs_to_update_main_thread;
+ static UpdatableFuncPtr func_ptrs_to_update_main_thread;
+ static thread_local UpdatableFuncPtr *func_ptrs_to_update_thread_local;
List<UpdatableFuncPtr *> func_ptrs_to_update;
- List<UpdatableFuncPtrElement> func_ptrs_to_update_elems;
Mutex func_ptrs_to_update_mutex;
- UpdatableFuncPtrElement *_add_func_ptr_to_update(GDScriptFunction **p_func_ptr_ptr);
- static void _remove_func_ptr_to_update(const UpdatableFuncPtrElement *p_func_ptr_element);
+ UpdatableFuncPtrElement _add_func_ptr_to_update(GDScriptFunction **p_func_ptr_ptr);
+ static void _remove_func_ptr_to_update(const UpdatableFuncPtrElement &p_func_ptr_element);
static void _fixup_thread_function_bookkeeping();
@@ -252,7 +254,7 @@ public:
const Ref<GDScriptNativeClass> &get_native() const { return native; }
RBSet<GDScript *> get_dependencies();
- RBSet<GDScript *> get_inverted_dependencies();
+ HashMap<GDScript *, RBSet<GDScript *>> get_all_dependencies();
RBSet<GDScript *> get_must_clear_dependencies();
virtual bool has_script_signal(const StringName &p_signal) const override;
@@ -437,6 +439,7 @@ class GDScriptLanguage : public ScriptLanguage {
SelfList<GDScriptFunction>::List function_list;
bool profiling;
+ bool profile_native_calls;
uint64_t script_frame_time;
HashMap<String, ObjectID> orphan_subclasses;
@@ -561,6 +564,7 @@ public:
/* MULTITHREAD FUNCTIONS */
+ virtual void thread_enter() override;
virtual void thread_exit() override;
/* DEBUGGER FUNCTIONS */
@@ -587,6 +591,8 @@ public:
virtual void profiling_start() override;
virtual void profiling_stop() override;
+ virtual void profiling_set_save_native_calls(bool p_enable) override;
+ void profiling_collate_native_call_data(bool p_accumulated);
virtual int profiling_get_accumulated_data(ProfilingInfo *p_info_arr, int p_info_max) override;
virtual int profiling_get_frame_data(ProfilingInfo *p_info_arr, int p_info_max) override;
diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp
index 983a19470a..cf537bde16 100644
--- a/modules/gdscript/gdscript_analyzer.cpp
+++ b/modules/gdscript/gdscript_analyzer.cpp
@@ -685,10 +685,10 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type
result.builtin_type = GDScriptParser::get_builtin_type(first);
if (result.builtin_type == Variant::ARRAY) {
- GDScriptParser::DataType container_type = type_from_metatype(resolve_datatype(p_type->container_type));
+ GDScriptParser::DataType container_type = type_from_metatype(resolve_datatype(p_type->get_container_type_or_null(0)));
if (container_type.kind != GDScriptParser::DataType::VARIANT) {
container_type.is_constant = false;
- result.set_container_element_type(container_type);
+ result.set_container_element_type(0, container_type);
}
}
} else if (class_exists(first)) {
@@ -829,8 +829,16 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type
}
}
- if (result.builtin_type != Variant::ARRAY && p_type->container_type != nullptr) {
- push_error("Only arrays can specify the collection element type.", p_type);
+ if (!p_type->container_types.is_empty()) {
+ if (result.builtin_type == Variant::ARRAY) {
+ if (p_type->container_types.size() != 1) {
+ push_error("Arrays require exactly one collection element type.", p_type);
+ return bad_type;
+ }
+ } else {
+ push_error("Only arrays can specify collection element types.", p_type);
+ return bad_type;
+ }
}
p_type->set_datatype(result);
@@ -1891,8 +1899,8 @@ void GDScriptAnalyzer::resolve_assignable(GDScriptParser::AssignableNode *p_assi
if (p_assignable->initializer->type == GDScriptParser::Node::ARRAY) {
GDScriptParser::ArrayNode *array = static_cast<GDScriptParser::ArrayNode *>(p_assignable->initializer);
- if (has_specified_type && specified_type.has_container_element_type()) {
- update_array_literal_element_type(array, specified_type.get_container_element_type());
+ if (has_specified_type && specified_type.has_container_element_type(0)) {
+ update_array_literal_element_type(array, specified_type.get_container_element_type(0));
}
}
@@ -1955,7 +1963,7 @@ void GDScriptAnalyzer::resolve_assignable(GDScriptParser::AssignableNode *p_assi
} else {
push_error(vformat(R"(Cannot assign a value of type %s to %s "%s" with specified type %s.)", initializer_type.to_string(), p_kind, p_assignable->identifier->name, specified_type.to_string()), p_assignable->initializer);
}
- } else if (specified_type.has_container_element_type() && !initializer_type.has_container_element_type()) {
+ } else if (specified_type.has_container_element_type(0) && !initializer_type.has_container_element_type(0)) {
mark_node_unsafe(p_assignable->initializer);
#ifdef DEBUG_ENABLED
} else if (specified_type.builtin_type == Variant::INT && initializer_type.builtin_type == Variant::FLOAT) {
@@ -2127,8 +2135,8 @@ void GDScriptAnalyzer::resolve_for(GDScriptParser::ForNode *p_for) {
if (list_type.is_variant()) {
variable_type.kind = GDScriptParser::DataType::VARIANT;
mark_node_unsafe(p_for->list);
- } else if (list_type.has_container_element_type()) {
- variable_type = list_type.get_container_element_type();
+ } else if (list_type.has_container_element_type(0)) {
+ variable_type = list_type.get_container_element_type(0);
variable_type.type_source = list_type.type_source;
} else if (list_type.is_typed_container_type()) {
variable_type = list_type.get_typed_container_type();
@@ -2377,8 +2385,8 @@ void GDScriptAnalyzer::resolve_return(GDScriptParser::ReturnNode *p_return) {
result.builtin_type = Variant::NIL;
result.is_constant = true;
} else {
- if (p_return->return_value->type == GDScriptParser::Node::ARRAY && has_expected_type && expected_type.has_container_element_type()) {
- update_array_literal_element_type(static_cast<GDScriptParser::ArrayNode *>(p_return->return_value), expected_type.get_container_element_type());
+ if (p_return->return_value->type == GDScriptParser::Node::ARRAY && has_expected_type && expected_type.has_container_element_type(0)) {
+ update_array_literal_element_type(static_cast<GDScriptParser::ArrayNode *>(p_return->return_value), expected_type.get_container_element_type(0));
}
if (has_expected_type && expected_type.is_hard_type() && p_return->return_value->is_constant) {
update_const_expression_builtin_type(p_return->return_value, expected_type, "return");
@@ -2598,7 +2606,7 @@ void GDScriptAnalyzer::update_const_expression_builtin_type(GDScriptParser::Expr
// This function determines which type is that (if any).
void GDScriptAnalyzer::update_array_literal_element_type(GDScriptParser::ArrayNode *p_array, const GDScriptParser::DataType &p_element_type) {
GDScriptParser::DataType expected_type = p_element_type;
- expected_type.unset_container_element_type(); // Nested types (like `Array[Array[int]]`) are not currently supported.
+ expected_type.container_element_types.clear(); // Nested types (like `Array[Array[int]]`) are not currently supported.
for (int i = 0; i < p_array->elements.size(); i++) {
GDScriptParser::ExpressionNode *element_node = p_array->elements[i];
@@ -2621,7 +2629,7 @@ void GDScriptAnalyzer::update_array_literal_element_type(GDScriptParser::ArrayNo
}
GDScriptParser::DataType array_type = p_array->get_datatype();
- array_type.set_container_element_type(expected_type);
+ array_type.set_container_element_type(0, expected_type);
p_array->set_datatype(array_type);
}
@@ -2668,8 +2676,8 @@ void GDScriptAnalyzer::reduce_assignment(GDScriptParser::AssignmentNode *p_assig
}
// Check if assigned value is an array literal, so we can make it a typed array too if appropriate.
- if (p_assignment->assigned_value->type == GDScriptParser::Node::ARRAY && assignee_type.is_hard_type() && assignee_type.has_container_element_type()) {
- update_array_literal_element_type(static_cast<GDScriptParser::ArrayNode *>(p_assignment->assigned_value), assignee_type.get_container_element_type());
+ if (p_assignment->assigned_value->type == GDScriptParser::Node::ARRAY && assignee_type.is_hard_type() && assignee_type.has_container_element_type(0)) {
+ update_array_literal_element_type(static_cast<GDScriptParser::ArrayNode *>(p_assignment->assigned_value), assignee_type.get_container_element_type(0));
}
if (p_assignment->operation == GDScriptParser::AssignmentNode::OP_NONE && assignee_type.is_hard_type() && p_assignment->assigned_value->is_constant) {
@@ -2747,7 +2755,7 @@ void GDScriptAnalyzer::reduce_assignment(GDScriptParser::AssignmentNode *p_assig
// weak non-variant assignee and incompatible result
downgrades_assignee = true;
}
- } else if (assignee_type.has_container_element_type() && !op_type.has_container_element_type()) {
+ } else if (assignee_type.has_container_element_type(0) && !op_type.has_container_element_type(0)) {
// typed array assignee and untyped array result
mark_node_unsafe(p_assignment);
}
@@ -3311,8 +3319,8 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
// If the function requires typed arrays we must make literals be typed.
for (const KeyValue<int, GDScriptParser::ArrayNode *> &E : arrays) {
int index = E.key;
- if (index < par_types.size() && par_types[index].is_hard_type() && par_types[index].has_container_element_type()) {
- update_array_literal_element_type(E.value, par_types[index].get_container_element_type());
+ if (index < par_types.size() && par_types[index].is_hard_type() && par_types[index].has_container_element_type(0)) {
+ update_array_literal_element_type(E.value, par_types[index].get_container_element_type(0));
}
}
validate_call_arg(par_types, default_arg_count, method_flags.has_flag(METHOD_FLAG_VARARG), p_call);
@@ -3444,8 +3452,8 @@ void GDScriptAnalyzer::reduce_cast(GDScriptParser::CastNode *p_cast) {
}
}
- if (p_cast->operand->type == GDScriptParser::Node::ARRAY && cast_type.has_container_element_type()) {
- update_array_literal_element_type(static_cast<GDScriptParser::ArrayNode *>(p_cast->operand), cast_type.get_container_element_type());
+ if (p_cast->operand->type == GDScriptParser::Node::ARRAY && cast_type.has_container_element_type(0)) {
+ update_array_literal_element_type(static_cast<GDScriptParser::ArrayNode *>(p_cast->operand), cast_type.get_container_element_type(0));
}
if (!cast_type.is_variant()) {
@@ -3650,6 +3658,10 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
return;
}
}
+ if (Variant::has_builtin_method(base.builtin_type, name)) {
+ p_identifier->set_datatype(make_callable_type(Variant::get_builtin_method_info(base.builtin_type, name)));
+ return;
+ }
if (base.is_hard_type()) {
#ifdef SUGGEST_GODOT4_RENAMES
String rename_hint = String();
@@ -3769,6 +3781,60 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
}
}
+ // Check non-GDScript scripts.
+ Ref<Script> script_type = base.script_type;
+
+ if (base_class == nullptr && script_type.is_valid()) {
+ List<PropertyInfo> property_list;
+ script_type->get_script_property_list(&property_list);
+
+ for (const PropertyInfo &property_info : property_list) {
+ if (property_info.name != p_identifier->name) {
+ continue;
+ }
+
+ const GDScriptParser::DataType property_type = GDScriptAnalyzer::type_from_property(property_info, false, false);
+
+ p_identifier->set_datatype(property_type);
+ p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_VARIABLE;
+ return;
+ }
+
+ MethodInfo method_info = script_type->get_method_info(p_identifier->name);
+
+ if (method_info.name == p_identifier->name) {
+ p_identifier->set_datatype(make_callable_type(method_info));
+ p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_FUNCTION;
+ return;
+ }
+
+ List<MethodInfo> signal_list;
+ script_type->get_script_signal_list(&signal_list);
+
+ for (const MethodInfo &signal_info : signal_list) {
+ if (signal_info.name != p_identifier->name) {
+ continue;
+ }
+
+ const GDScriptParser::DataType signal_type = make_signal_type(signal_info);
+
+ p_identifier->set_datatype(signal_type);
+ p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_SIGNAL;
+ return;
+ }
+
+ HashMap<StringName, Variant> constant_map;
+ script_type->get_constants(&constant_map);
+
+ if (constant_map.has(p_identifier->name)) {
+ Variant constant = constant_map.get(p_identifier->name);
+
+ p_identifier->set_datatype(make_builtin_meta_type(constant.get_type()));
+ p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_CONSTANT;
+ return;
+ }
+ }
+
// Check native members. No need for native class recursion because Node exposes all Object's properties.
const StringName &native = base.native_type;
@@ -4432,8 +4498,8 @@ void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscri
break;
// Can have an element type.
case Variant::ARRAY:
- if (base_type.has_container_element_type()) {
- result_type = base_type.get_container_element_type();
+ if (base_type.has_container_element_type(0)) {
+ result_type = base_type.get_container_element_type(0);
result_type.type_source = base_type.type_source;
} else {
result_type.kind = GDScriptParser::DataType::VARIANT;
@@ -4597,7 +4663,7 @@ Variant GDScriptAnalyzer::make_expression_reduced_value(GDScriptParser::Expressi
}
Variant GDScriptAnalyzer::make_array_reduced_value(GDScriptParser::ArrayNode *p_array, bool &is_reduced) {
- Array array = p_array->get_datatype().has_container_element_type() ? make_array_from_element_datatype(p_array->get_datatype().get_container_element_type()) : Array();
+ Array array = p_array->get_datatype().has_container_element_type(0) ? make_array_from_element_datatype(p_array->get_datatype().get_container_element_type(0)) : Array();
array.resize(p_array->elements.size());
for (int i = 0; i < p_array->elements.size(); i++) {
@@ -4719,8 +4785,8 @@ Variant GDScriptAnalyzer::make_variable_default_value(GDScriptParser::VariableNo
GDScriptParser::DataType datatype = p_variable->get_datatype();
if (datatype.is_hard_type()) {
if (datatype.kind == GDScriptParser::DataType::BUILTIN && datatype.builtin_type != Variant::OBJECT) {
- if (datatype.builtin_type == Variant::ARRAY && datatype.has_container_element_type()) {
- result = make_array_from_element_datatype(datatype.get_container_element_type());
+ if (datatype.builtin_type == Variant::ARRAY && datatype.has_container_element_type(0)) {
+ result = make_array_from_element_datatype(datatype.get_container_element_type(0));
} else {
VariantInternal::initialize(&result, datatype.builtin_type);
}
@@ -4747,11 +4813,11 @@ GDScriptParser::DataType GDScriptAnalyzer::type_from_variant(const Variant &p_va
if (p_value.get_type() == Variant::ARRAY) {
const Array &array = p_value;
if (array.get_typed_script()) {
- result.set_container_element_type(type_from_metatype(make_script_meta_type(array.get_typed_script())));
+ result.set_container_element_type(0, type_from_metatype(make_script_meta_type(array.get_typed_script())));
} else if (array.get_typed_class_name()) {
- result.set_container_element_type(type_from_metatype(make_native_meta_type(array.get_typed_class_name())));
+ result.set_container_element_type(0, type_from_metatype(make_native_meta_type(array.get_typed_class_name())));
} else if (array.get_typed_builtin() != Variant::NIL) {
- result.set_container_element_type(type_from_metatype(make_builtin_meta_type((Variant::Type)array.get_typed_builtin())));
+ result.set_container_element_type(0, type_from_metatype(make_builtin_meta_type((Variant::Type)array.get_typed_builtin())));
}
} else if (p_value.get_type() == Variant::OBJECT) {
// Object is treated as a native type, not a builtin type.
@@ -4873,7 +4939,7 @@ GDScriptParser::DataType GDScriptAnalyzer::type_from_property(const PropertyInfo
ERR_FAIL_V_MSG(result, "Could not find element type from property hint of a typed array.");
}
elem_type.is_constant = false;
- result.set_container_element_type(elem_type);
+ result.set_container_element_type(0, elem_type);
} else if (p_property.type == Variant::INT) {
// Check if it's enum.
if ((p_property.usage & PROPERTY_USAGE_CLASS_IS_ENUM) && p_property.class_name != StringName()) {
@@ -5225,7 +5291,7 @@ GDScriptParser::DataType GDScriptAnalyzer::get_operation_type(Variant::Operator
bool hard_operation = p_a.is_hard_type() && p_b.is_hard_type();
if (p_operation == Variant::OP_ADD && a_type == Variant::ARRAY && b_type == Variant::ARRAY) {
- if (p_a.has_container_element_type() && p_b.has_container_element_type() && p_a.get_container_element_type() == p_b.get_container_element_type()) {
+ if (p_a.has_container_element_type(0) && p_b.has_container_element_type(0) && p_a.get_container_element_type(0) == p_b.get_container_element_type(0)) {
r_valid = true;
result = p_a;
result.type_source = hard_operation ? GDScriptParser::DataType::ANNOTATED_INFERRED : GDScriptParser::DataType::INFERRED;
@@ -5276,8 +5342,8 @@ bool GDScriptAnalyzer::is_type_compatible(const GDScriptParser::DataType &p_targ
}
if (valid && p_target.builtin_type == Variant::ARRAY && p_source.builtin_type == Variant::ARRAY) {
// Check the element type.
- if (p_target.has_container_element_type() && p_source.has_container_element_type()) {
- valid = p_target.get_container_element_type() == p_source.get_container_element_type();
+ if (p_target.has_container_element_type(0) && p_source.has_container_element_type(0)) {
+ valid = p_target.get_container_element_type(0) == p_source.get_container_element_type(0);
}
}
return valid;
diff --git a/modules/gdscript/gdscript_byte_codegen.cpp b/modules/gdscript/gdscript_byte_codegen.cpp
index 25e20c0e76..27766115d5 100644
--- a/modules/gdscript/gdscript_byte_codegen.cpp
+++ b/modules/gdscript/gdscript_byte_codegen.cpp
@@ -623,8 +623,8 @@ void GDScriptByteCodeGenerator::write_binary_operator(const Address &p_target, V
void GDScriptByteCodeGenerator::write_type_test(const Address &p_target, const Address &p_source, const GDScriptDataType &p_type) {
switch (p_type.kind) {
case GDScriptDataType::BUILTIN: {
- if (p_type.builtin_type == Variant::ARRAY && p_type.has_container_element_type()) {
- const GDScriptDataType &element_type = p_type.get_container_element_type();
+ if (p_type.builtin_type == Variant::ARRAY && p_type.has_container_element_type(0)) {
+ const GDScriptDataType &element_type = p_type.get_container_element_type(0);
append_opcode(GDScriptFunction::OPCODE_TYPE_TEST_ARRAY);
append(p_target);
append(p_source);
@@ -878,8 +878,8 @@ void GDScriptByteCodeGenerator::write_get_static_variable(const Address &p_targe
void GDScriptByteCodeGenerator::write_assign_with_conversion(const Address &p_target, const Address &p_source) {
switch (p_target.type.kind) {
case GDScriptDataType::BUILTIN: {
- if (p_target.type.builtin_type == Variant::ARRAY && p_target.type.has_container_element_type()) {
- const GDScriptDataType &element_type = p_target.type.get_container_element_type();
+ if (p_target.type.builtin_type == Variant::ARRAY && p_target.type.has_container_element_type(0)) {
+ const GDScriptDataType &element_type = p_target.type.get_container_element_type(0);
append_opcode(GDScriptFunction::OPCODE_ASSIGN_TYPED_ARRAY);
append(p_target);
append(p_source);
@@ -924,8 +924,8 @@ void GDScriptByteCodeGenerator::write_assign_with_conversion(const Address &p_ta
}
void GDScriptByteCodeGenerator::write_assign(const Address &p_target, const Address &p_source) {
- if (p_target.type.kind == GDScriptDataType::BUILTIN && p_target.type.builtin_type == Variant::ARRAY && p_target.type.has_container_element_type()) {
- const GDScriptDataType &element_type = p_target.type.get_container_element_type();
+ if (p_target.type.kind == GDScriptDataType::BUILTIN && p_target.type.builtin_type == Variant::ARRAY && p_target.type.has_container_element_type(0)) {
+ const GDScriptDataType &element_type = p_target.type.get_container_element_type(0);
append_opcode(GDScriptFunction::OPCODE_ASSIGN_TYPED_ARRAY);
append(p_target);
append(p_source);
@@ -1666,9 +1666,9 @@ void GDScriptByteCodeGenerator::write_return(const Address &p_return_value) {
// If this is a typed function, then we need to check for potential conversions.
if (function->return_type.has_type) {
- if (function->return_type.kind == GDScriptDataType::BUILTIN && function->return_type.builtin_type == Variant::ARRAY && function->return_type.has_container_element_type()) {
+ if (function->return_type.kind == GDScriptDataType::BUILTIN && function->return_type.builtin_type == Variant::ARRAY && function->return_type.has_container_element_type(0)) {
// Typed array.
- const GDScriptDataType &element_type = function->return_type.get_container_element_type();
+ const GDScriptDataType &element_type = function->return_type.get_container_element_type(0);
append_opcode(GDScriptFunction::OPCODE_RETURN_TYPED_ARRAY);
append(p_return_value);
append(get_constant_pos(element_type.script_type) | (GDScriptFunction::ADDR_TYPE_CONSTANT << GDScriptFunction::ADDR_BITS));
@@ -1691,8 +1691,8 @@ void GDScriptByteCodeGenerator::write_return(const Address &p_return_value) {
} else {
switch (function->return_type.kind) {
case GDScriptDataType::BUILTIN: {
- if (function->return_type.builtin_type == Variant::ARRAY && function->return_type.has_container_element_type()) {
- const GDScriptDataType &element_type = function->return_type.get_container_element_type();
+ if (function->return_type.builtin_type == Variant::ARRAY && function->return_type.has_container_element_type(0)) {
+ const GDScriptDataType &element_type = function->return_type.get_container_element_type(0);
append_opcode(GDScriptFunction::OPCODE_RETURN_TYPED_ARRAY);
append(p_return_value);
append(get_constant_pos(element_type.script_type) | (GDScriptFunction::ADDR_TYPE_CONSTANT << GDScriptFunction::ADDR_BITS));
diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp
index 7980f020b8..ee360e581b 100644
--- a/modules/gdscript/gdscript_compiler.cpp
+++ b/modules/gdscript/gdscript_compiler.cpp
@@ -196,8 +196,8 @@ GDScriptDataType GDScriptCompiler::_gdtype_from_datatype(const GDScriptParser::D
}
}
- if (p_datatype.has_container_element_type()) {
- result.set_container_element_type(_gdtype_from_datatype(p_datatype.get_container_element_type(), p_owner, false));
+ for (int i = 0; i < p_datatype.container_element_types.size(); i++) {
+ result.set_container_element_type(i, _gdtype_from_datatype(p_datatype.get_container_element_type_or_variant(i), p_owner, false));
}
return result;
@@ -322,9 +322,13 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
if (member.type == GDScriptParser::ClassNode::Member::FUNCTION || member.type == GDScriptParser::ClassNode::Member::SIGNAL) {
// Get like it was a property.
GDScriptCodeGenerator::Address temp = codegen.add_temporary(); // TODO: Get type here.
- GDScriptCodeGenerator::Address self(GDScriptCodeGenerator::Address::SELF);
- gen->write_get_named(temp, identifier, self);
+ GDScriptCodeGenerator::Address base(GDScriptCodeGenerator::Address::SELF);
+ if (member.type == GDScriptParser::ClassNode::Member::FUNCTION && member.function->is_static) {
+ base = GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::CLASS);
+ }
+
+ gen->write_get_named(temp, identifier, base);
return temp;
}
}
@@ -507,8 +511,8 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
values.push_back(val);
}
- if (array_type.has_container_element_type()) {
- gen->write_construct_typed_array(result, array_type.get_container_element_type(), values);
+ if (array_type.has_container_element_type(0)) {
+ gen->write_construct_typed_array(result, array_type.get_container_element_type(0), values);
} else {
gen->write_construct_array(result, values);
}
@@ -2133,8 +2137,8 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Sui
initialized = true;
} else if (local_type.has_type) {
// Initialize with default for type.
- if (local_type.has_container_element_type()) {
- codegen.generator->write_construct_typed_array(local, local_type.get_container_element_type(), Vector<GDScriptCodeGenerator::Address>());
+ if (local_type.has_container_element_type(0)) {
+ codegen.generator->write_construct_typed_array(local, local_type.get_container_element_type(0), Vector<GDScriptCodeGenerator::Address>());
initialized = true;
} else if (local_type.kind == GDScriptDataType::BUILTIN) {
codegen.generator->write_construct(local, local_type.builtin_type, Vector<GDScriptCodeGenerator::Address>());
@@ -2276,8 +2280,8 @@ GDScriptFunction *GDScriptCompiler::_parse_function(Error &r_error, GDScript *p_
GDScriptCodeGenerator::Address dst_address(GDScriptCodeGenerator::Address::MEMBER, codegen.script->member_indices[field->identifier->name].index, field_type);
- if (field_type.has_container_element_type()) {
- codegen.generator->write_construct_typed_array(dst_address, field_type.get_container_element_type(), Vector<GDScriptCodeGenerator::Address>());
+ if (field_type.has_container_element_type(0)) {
+ codegen.generator->write_construct_typed_array(dst_address, field_type.get_container_element_type(0), Vector<GDScriptCodeGenerator::Address>());
} else if (field_type.kind == GDScriptDataType::BUILTIN) {
codegen.generator->write_construct(dst_address, field_type.builtin_type, Vector<GDScriptCodeGenerator::Address>());
}
@@ -2466,9 +2470,9 @@ GDScriptFunction *GDScriptCompiler::_make_static_initializer(Error &r_error, GDS
if (field_type.has_type) {
codegen.generator->write_newline(field->start_line);
- if (field_type.has_container_element_type()) {
+ if (field_type.has_container_element_type(0)) {
GDScriptCodeGenerator::Address temp = codegen.add_temporary(field_type);
- codegen.generator->write_construct_typed_array(temp, field_type.get_container_element_type(), Vector<GDScriptCodeGenerator::Address>());
+ codegen.generator->write_construct_typed_array(temp, field_type.get_container_element_type(0), Vector<GDScriptCodeGenerator::Address>());
codegen.generator->write_set_static_variable(temp, class_addr, p_script->static_variables_indices[field->identifier->name].index);
codegen.generator->pop_temporary();
} else if (field_type.kind == GDScriptDataType::BUILTIN) {
diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp
index 724715d9e5..9eb6fe8744 100644
--- a/modules/gdscript/gdscript_editor.cpp
+++ b/modules/gdscript/gdscript_editor.cpp
@@ -1083,6 +1083,12 @@ static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base
List<PropertyInfo> members;
scr->get_script_property_list(&members);
for (const PropertyInfo &E : members) {
+ if (E.usage & (PROPERTY_USAGE_CATEGORY | PROPERTY_USAGE_GROUP | PROPERTY_USAGE_SUBGROUP)) {
+ continue;
+ }
+ if (E.name.contains("/")) {
+ continue;
+ }
int location = p_recursion_depth + _get_property_location(scr->get_class_name(), E.name);
ScriptLanguage::CodeCompletionOption option(E.name, ScriptLanguage::CODE_COMPLETION_KIND_MEMBER, location);
r_result.insert(option.display, option);
@@ -1152,7 +1158,7 @@ static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base
List<PropertyInfo> pinfo;
ClassDB::get_property_list(type, &pinfo);
for (const PropertyInfo &E : pinfo) {
- if (E.usage & (PROPERTY_USAGE_GROUP | PROPERTY_USAGE_CATEGORY)) {
+ if (E.usage & (PROPERTY_USAGE_CATEGORY | PROPERTY_USAGE_GROUP | PROPERTY_USAGE_SUBGROUP)) {
continue;
}
if (E.name.contains("/")) {
@@ -1213,6 +1219,9 @@ static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base
}
for (const PropertyInfo &E : members) {
+ if (E.usage & (PROPERTY_USAGE_CATEGORY | PROPERTY_USAGE_GROUP | PROPERTY_USAGE_SUBGROUP)) {
+ continue;
+ }
if (!String(E.name).contains("/")) {
ScriptLanguage::CodeCompletionOption option(E.name, ScriptLanguage::CODE_COMPLETION_KIND_MEMBER);
if (GDScriptParser::theme_color_names.has(E.name)) {
@@ -1485,11 +1494,8 @@ static bool _guess_expression_type(GDScriptParser::CompletionContext &p_context,
} break;
case GDScriptParser::Node::SELF: {
if (p_context.current_class) {
- if (p_context.type != GDScriptParser::COMPLETION_SUPER_METHOD) {
- r_type.type = p_context.current_class->get_datatype();
- } else {
- r_type.type = p_context.current_class->base_type;
- }
+ r_type.type = p_context.current_class->get_datatype();
+ r_type.type.is_meta_type = false;
found = true;
}
} break;
@@ -2601,6 +2607,64 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c
r_arghint = _make_arguments_hint(info, p_argidx);
}
+ if (p_argidx == 1 && p_context.node && p_context.node->type == GDScriptParser::Node::CALL && ClassDB::is_parent_class(class_name, SNAME("Tween")) && p_method == SNAME("tween_property")) {
+ // Get tweened objects properties.
+ GDScriptParser::ExpressionNode *tweened_object = static_cast<GDScriptParser::CallNode *>(p_context.node)->arguments[0];
+ StringName native_type = tweened_object->datatype.native_type;
+ switch (tweened_object->datatype.kind) {
+ case GDScriptParser::DataType::SCRIPT: {
+ Ref<Script> script = tweened_object->datatype.script_type;
+ native_type = script->get_instance_base_type();
+ int n = 0;
+ while (script.is_valid()) {
+ List<PropertyInfo> properties;
+ script->get_script_property_list(&properties);
+ for (const PropertyInfo &E : properties) {
+ if (E.usage & (PROPERTY_USAGE_SUBGROUP | PROPERTY_USAGE_GROUP | PROPERTY_USAGE_CATEGORY | PROPERTY_USAGE_INTERNAL)) {
+ continue;
+ }
+ ScriptLanguage::CodeCompletionOption option(E.name.quote(quote_style), ScriptLanguage::CODE_COMPLETION_KIND_MEMBER, ScriptLanguage::CodeCompletionLocation::LOCATION_LOCAL + n);
+ r_result.insert(option.display, option);
+ }
+ script = script->get_base_script();
+ n++;
+ }
+ } break;
+ case GDScriptParser::DataType::CLASS: {
+ GDScriptParser::ClassNode *clss = tweened_object->datatype.class_type;
+ native_type = clss->base_type.native_type;
+ int n = 0;
+ while (clss) {
+ for (GDScriptParser::ClassNode::Member member : clss->members) {
+ if (member.type == GDScriptParser::ClassNode::Member::VARIABLE) {
+ ScriptLanguage::CodeCompletionOption option(member.get_name().quote(quote_style), ScriptLanguage::CODE_COMPLETION_KIND_MEMBER, ScriptLanguage::CodeCompletionLocation::LOCATION_LOCAL + n);
+ r_result.insert(option.display, option);
+ }
+ }
+ if (clss->base_type.kind == GDScriptParser::DataType::Kind::CLASS) {
+ clss = clss->base_type.class_type;
+ n++;
+ } else {
+ native_type = clss->base_type.native_type;
+ clss = nullptr;
+ }
+ }
+ } break;
+ default:
+ break;
+ }
+
+ List<PropertyInfo> properties;
+ ClassDB::get_property_list(native_type, &properties);
+ for (const PropertyInfo &E : properties) {
+ if (E.usage & (PROPERTY_USAGE_SUBGROUP | PROPERTY_USAGE_GROUP | PROPERTY_USAGE_CATEGORY | PROPERTY_USAGE_INTERNAL)) {
+ continue;
+ }
+ ScriptLanguage::CodeCompletionOption option(E.name.quote(quote_style), ScriptLanguage::CODE_COMPLETION_KIND_MEMBER);
+ r_result.insert(option.display, option);
+ }
+ }
+
if (p_argidx == 0 && ClassDB::is_parent_class(class_name, SNAME("Node")) && (p_method == SNAME("get_node") || p_method == SNAME("has_node"))) {
// Get autoloads
List<PropertyInfo> props;
@@ -2667,6 +2731,7 @@ static bool _get_subscript_type(GDScriptParser::CompletionContext &p_context, co
if (p_context.base == nullptr) {
return false;
}
+
const GDScriptParser::GetNodeNode *get_node = nullptr;
switch (p_subscript->base->type) {
@@ -2675,6 +2740,11 @@ static bool _get_subscript_type(GDScriptParser::CompletionContext &p_context, co
} break;
case GDScriptParser::Node::IDENTIFIER: {
+ if (p_subscript->base->datatype.type_source == GDScriptParser::DataType::ANNOTATED_EXPLICIT) {
+ // Annotated type takes precedence.
+ return false;
+ }
+
const GDScriptParser::IdentifierNode *identifier_node = static_cast<GDScriptParser::IdentifierNode *>(p_subscript->base);
switch (identifier_node->source) {
@@ -2715,10 +2785,19 @@ static bool _get_subscript_type(GDScriptParser::CompletionContext &p_context, co
if (r_base != nullptr) {
*r_base = node;
}
- r_base_type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
- r_base_type.kind = GDScriptParser::DataType::NATIVE;
+
+ r_base_type.type_source = GDScriptParser::DataType::INFERRED;
r_base_type.builtin_type = Variant::OBJECT;
r_base_type.native_type = node->get_class_name();
+
+ Ref<Script> scr = node->get_script();
+ if (scr.is_null()) {
+ r_base_type.kind = GDScriptParser::DataType::NATIVE;
+ } else {
+ r_base_type.kind = GDScriptParser::DataType::SCRIPT;
+ r_base_type.script_type = scr;
+ }
+
return true;
}
}
@@ -2745,8 +2824,6 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c
const GDScriptParser::CallNode *call = static_cast<const GDScriptParser::CallNode *>(p_call);
GDScriptParser::Node::Type callee_type = call->get_callee_type();
- GDScriptCompletionIdentifier connect_base;
-
if (callee_type == GDScriptParser::Node::SUBSCRIPT) {
const GDScriptParser::SubscriptNode *subscript = static_cast<const GDScriptParser::SubscriptNode *>(call->callee);
diff --git a/modules/gdscript/gdscript_function.h b/modules/gdscript/gdscript_function.h
index c9b543fbb9..177c68533e 100644
--- a/modules/gdscript/gdscript_function.h
+++ b/modules/gdscript/gdscript_function.h
@@ -45,10 +45,9 @@ class GDScriptInstance;
class GDScript;
class GDScriptDataType {
-private:
- GDScriptDataType *container_element_type = nullptr;
-
public:
+ Vector<GDScriptDataType> container_element_types;
+
enum Kind {
UNINITIALIZED,
BUILTIN,
@@ -76,19 +75,20 @@ public:
case BUILTIN: {
Variant::Type var_type = p_variant.get_type();
bool valid = builtin_type == var_type;
- if (valid && builtin_type == Variant::ARRAY && has_container_element_type()) {
+ if (valid && builtin_type == Variant::ARRAY && has_container_element_type(0)) {
Array array = p_variant;
if (array.is_typed()) {
+ GDScriptDataType array_container_type = get_container_element_type(0);
Variant::Type array_builtin_type = (Variant::Type)array.get_typed_builtin();
StringName array_native_type = array.get_typed_class_name();
Ref<Script> array_script_type_ref = array.get_typed_script();
if (array_script_type_ref.is_valid()) {
- valid = (container_element_type->kind == SCRIPT || container_element_type->kind == GDSCRIPT) && container_element_type->script_type == array_script_type_ref.ptr();
+ valid = (array_container_type.kind == SCRIPT || array_container_type.kind == GDSCRIPT) && array_container_type.script_type == array_script_type_ref.ptr();
} else if (array_native_type != StringName()) {
- valid = container_element_type->kind == NATIVE && container_element_type->native_type == array_native_type;
+ valid = array_container_type.kind == NATIVE && array_container_type.native_type == array_native_type;
} else {
- valid = container_element_type->kind == BUILTIN && container_element_type->builtin_type == array_builtin_type;
+ valid = array_container_type.kind == BUILTIN && array_container_type.builtin_type == array_builtin_type;
}
} else {
valid = false;
@@ -147,24 +147,32 @@ public:
return false;
}
- void set_container_element_type(const GDScriptDataType &p_element_type) {
- container_element_type = memnew(GDScriptDataType(p_element_type));
+ void set_container_element_type(int p_index, const GDScriptDataType &p_element_type) {
+ ERR_FAIL_COND(p_index < 0);
+ while (p_index >= container_element_types.size()) {
+ container_element_types.push_back(GDScriptDataType());
+ }
+ container_element_types.write[p_index] = GDScriptDataType(p_element_type);
+ }
+
+ GDScriptDataType get_container_element_type(int p_index) const {
+ ERR_FAIL_INDEX_V(p_index, container_element_types.size(), GDScriptDataType());
+ return container_element_types[p_index];
}
- GDScriptDataType get_container_element_type() const {
- ERR_FAIL_NULL_V(container_element_type, GDScriptDataType());
- return *container_element_type;
+ GDScriptDataType get_container_element_type_or_variant(int p_index) const {
+ if (p_index < 0 || p_index >= container_element_types.size()) {
+ return GDScriptDataType();
+ }
+ return container_element_types[p_index];
}
- bool has_container_element_type() const {
- return container_element_type != nullptr;
+ bool has_container_element_type(int p_index) const {
+ return p_index >= 0 && p_index < container_element_types.size();
}
- void unset_container_element_type() {
- if (container_element_type) {
- memdelete(container_element_type);
- }
- container_element_type = nullptr;
+ bool has_container_element_types() const {
+ return !container_element_types.is_empty();
}
GDScriptDataType() = default;
@@ -176,19 +184,14 @@ public:
native_type = p_other.native_type;
script_type = p_other.script_type;
script_type_ref = p_other.script_type_ref;
- unset_container_element_type();
- if (p_other.has_container_element_type()) {
- set_container_element_type(p_other.get_container_element_type());
- }
+ container_element_types = p_other.container_element_types;
}
GDScriptDataType(const GDScriptDataType &p_other) {
*this = p_other;
}
- ~GDScriptDataType() {
- unset_container_element_type();
- }
+ ~GDScriptDataType() {}
};
class GDScriptFunction {
@@ -471,6 +474,13 @@ private:
uint64_t last_frame_call_count = 0;
uint64_t last_frame_self_time = 0;
uint64_t last_frame_total_time = 0;
+ typedef struct NativeProfile {
+ uint64_t call_count;
+ uint64_t total_time;
+ String signature;
+ } NativeProfile;
+ HashMap<String, NativeProfile> native_calls;
+ HashMap<String, NativeProfile> last_native_calls;
} profile;
#endif
@@ -511,6 +521,7 @@ public:
void debug_get_stack_member_state(int p_line, List<Pair<StringName, int>> *r_stackvars) const;
#ifdef DEBUG_ENABLED
+ void _profile_native_call(uint64_t p_t_taken, const String &p_function_name, const String &p_instance_class_name = String());
void disassemble(const Vector<String> &p_code_lines) const;
#endif
diff --git a/modules/gdscript/gdscript_lambda_callable.cpp b/modules/gdscript/gdscript_lambda_callable.cpp
index 339d1ac08e..547f5607d3 100644
--- a/modules/gdscript/gdscript_lambda_callable.cpp
+++ b/modules/gdscript/gdscript_lambda_callable.cpp
@@ -296,7 +296,5 @@ GDScriptLambdaSelfCallable::GDScriptLambdaSelfCallable(Object *p_self, GDScriptF
}
GDScriptLambdaSelfCallable::~GDScriptLambdaSelfCallable() {
- if (updatable_func_ptr_element) {
- GDScript::_remove_func_ptr_to_update(updatable_func_ptr_element);
- }
+ GDScript::_remove_func_ptr_to_update(updatable_func_ptr_element);
}
diff --git a/modules/gdscript/gdscript_lambda_callable.h b/modules/gdscript/gdscript_lambda_callable.h
index d961f18852..ee7d547544 100644
--- a/modules/gdscript/gdscript_lambda_callable.h
+++ b/modules/gdscript/gdscript_lambda_callable.h
@@ -45,7 +45,7 @@ class GDScriptLambdaCallable : public CallableCustom {
GDScriptFunction *function = nullptr;
Ref<GDScript> script;
uint32_t h;
- GDScript::UpdatableFuncPtrElement *updatable_func_ptr_element = nullptr;
+ GDScript::UpdatableFuncPtrElement updatable_func_ptr_element;
Vector<Variant> captures;
@@ -72,7 +72,7 @@ class GDScriptLambdaSelfCallable : public CallableCustom {
Ref<RefCounted> reference; // For objects that are RefCounted, keep a reference.
Object *object = nullptr; // For non RefCounted objects, use a direct pointer.
uint32_t h;
- GDScript::UpdatableFuncPtrElement *updatable_func_ptr_element = nullptr;
+ GDScript::UpdatableFuncPtrElement updatable_func_ptr_element;
Vector<Variant> captures;
diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp
index db7b3e7ace..f3a4f2eaa6 100644
--- a/modules/gdscript/gdscript_parser.cpp
+++ b/modules/gdscript/gdscript_parser.cpp
@@ -73,8 +73,11 @@ Variant::Type GDScriptParser::get_builtin_type(const StringName &p_type) {
HashMap<String, String> GDScriptParser::theme_color_names;
#endif
+HashMap<StringName, GDScriptParser::AnnotationInfo> GDScriptParser::valid_annotations;
+
void GDScriptParser::cleanup() {
builtin_types.clear();
+ valid_annotations.clear();
}
void GDScriptParser::get_annotation_list(List<MethodInfo> *r_annotations) const {
@@ -89,41 +92,42 @@ bool GDScriptParser::annotation_exists(const String &p_annotation_name) const {
GDScriptParser::GDScriptParser() {
// Register valid annotations.
- // TODO: Should this be static?
- register_annotation(MethodInfo("@tool"), AnnotationInfo::SCRIPT, &GDScriptParser::tool_annotation);
- register_annotation(MethodInfo("@icon", PropertyInfo(Variant::STRING, "icon_path")), AnnotationInfo::SCRIPT, &GDScriptParser::icon_annotation);
- register_annotation(MethodInfo("@static_unload"), AnnotationInfo::SCRIPT, &GDScriptParser::static_unload_annotation);
-
- register_annotation(MethodInfo("@onready"), AnnotationInfo::VARIABLE, &GDScriptParser::onready_annotation);
- // Export annotations.
- register_annotation(MethodInfo("@export"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_NONE, Variant::NIL>);
- register_annotation(MethodInfo("@export_enum", PropertyInfo(Variant::STRING, "names")), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_ENUM, Variant::NIL>, varray(), true);
- register_annotation(MethodInfo("@export_file", PropertyInfo(Variant::STRING, "filter")), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_FILE, Variant::STRING>, varray(""), true);
- register_annotation(MethodInfo("@export_dir"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_DIR, Variant::STRING>);
- register_annotation(MethodInfo("@export_global_file", PropertyInfo(Variant::STRING, "filter")), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_GLOBAL_FILE, Variant::STRING>, varray(""), true);
- register_annotation(MethodInfo("@export_global_dir"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_GLOBAL_DIR, Variant::STRING>);
- register_annotation(MethodInfo("@export_multiline"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_MULTILINE_TEXT, Variant::STRING>);
- register_annotation(MethodInfo("@export_placeholder", PropertyInfo(Variant::STRING, "placeholder")), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_PLACEHOLDER_TEXT, Variant::STRING>);
- register_annotation(MethodInfo("@export_range", PropertyInfo(Variant::FLOAT, "min"), PropertyInfo(Variant::FLOAT, "max"), PropertyInfo(Variant::FLOAT, "step"), PropertyInfo(Variant::STRING, "extra_hints")), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_RANGE, Variant::FLOAT>, varray(1.0, ""), true);
- register_annotation(MethodInfo("@export_exp_easing", PropertyInfo(Variant::STRING, "hints")), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_EXP_EASING, Variant::FLOAT>, varray(""), true);
- register_annotation(MethodInfo("@export_color_no_alpha"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_COLOR_NO_ALPHA, Variant::COLOR>);
- register_annotation(MethodInfo("@export_node_path", PropertyInfo(Variant::STRING, "type")), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_NODE_PATH_VALID_TYPES, Variant::NODE_PATH>, varray(""), true);
- register_annotation(MethodInfo("@export_flags", PropertyInfo(Variant::STRING, "names")), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_FLAGS, Variant::INT>, varray(), true);
- register_annotation(MethodInfo("@export_flags_2d_render"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_LAYERS_2D_RENDER, Variant::INT>);
- register_annotation(MethodInfo("@export_flags_2d_physics"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_LAYERS_2D_PHYSICS, Variant::INT>);
- register_annotation(MethodInfo("@export_flags_2d_navigation"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_LAYERS_2D_NAVIGATION, Variant::INT>);
- register_annotation(MethodInfo("@export_flags_3d_render"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_LAYERS_3D_RENDER, Variant::INT>);
- register_annotation(MethodInfo("@export_flags_3d_physics"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_LAYERS_3D_PHYSICS, Variant::INT>);
- register_annotation(MethodInfo("@export_flags_3d_navigation"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_LAYERS_3D_NAVIGATION, Variant::INT>);
- register_annotation(MethodInfo("@export_flags_avoidance"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_LAYERS_AVOIDANCE, Variant::INT>);
- // Export grouping annotations.
- register_annotation(MethodInfo("@export_category", PropertyInfo(Variant::STRING, "name")), AnnotationInfo::STANDALONE, &GDScriptParser::export_group_annotations<PROPERTY_USAGE_CATEGORY>);
- register_annotation(MethodInfo("@export_group", PropertyInfo(Variant::STRING, "name"), PropertyInfo(Variant::STRING, "prefix")), AnnotationInfo::STANDALONE, &GDScriptParser::export_group_annotations<PROPERTY_USAGE_GROUP>, varray(""));
- register_annotation(MethodInfo("@export_subgroup", PropertyInfo(Variant::STRING, "name"), PropertyInfo(Variant::STRING, "prefix")), AnnotationInfo::STANDALONE, &GDScriptParser::export_group_annotations<PROPERTY_USAGE_SUBGROUP>, varray(""));
- // Warning annotations.
- register_annotation(MethodInfo("@warning_ignore", PropertyInfo(Variant::STRING, "warning")), AnnotationInfo::CLASS | AnnotationInfo::VARIABLE | AnnotationInfo::SIGNAL | AnnotationInfo::CONSTANT | AnnotationInfo::FUNCTION | AnnotationInfo::STATEMENT, &GDScriptParser::warning_annotations, varray(), true);
- // Networking.
- register_annotation(MethodInfo("@rpc", PropertyInfo(Variant::STRING, "mode"), PropertyInfo(Variant::STRING, "sync"), PropertyInfo(Variant::STRING, "transfer_mode"), PropertyInfo(Variant::INT, "transfer_channel")), AnnotationInfo::FUNCTION, &GDScriptParser::rpc_annotation, varray("authority", "call_remote", "unreliable", 0));
+ if (unlikely(valid_annotations.is_empty())) {
+ register_annotation(MethodInfo("@tool"), AnnotationInfo::SCRIPT, &GDScriptParser::tool_annotation);
+ register_annotation(MethodInfo("@icon", PropertyInfo(Variant::STRING, "icon_path")), AnnotationInfo::SCRIPT, &GDScriptParser::icon_annotation);
+ register_annotation(MethodInfo("@static_unload"), AnnotationInfo::SCRIPT, &GDScriptParser::static_unload_annotation);
+
+ register_annotation(MethodInfo("@onready"), AnnotationInfo::VARIABLE, &GDScriptParser::onready_annotation);
+ // Export annotations.
+ register_annotation(MethodInfo("@export"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_NONE, Variant::NIL>);
+ register_annotation(MethodInfo("@export_enum", PropertyInfo(Variant::STRING, "names")), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_ENUM, Variant::NIL>, varray(), true);
+ register_annotation(MethodInfo("@export_file", PropertyInfo(Variant::STRING, "filter")), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_FILE, Variant::STRING>, varray(""), true);
+ register_annotation(MethodInfo("@export_dir"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_DIR, Variant::STRING>);
+ register_annotation(MethodInfo("@export_global_file", PropertyInfo(Variant::STRING, "filter")), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_GLOBAL_FILE, Variant::STRING>, varray(""), true);
+ register_annotation(MethodInfo("@export_global_dir"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_GLOBAL_DIR, Variant::STRING>);
+ register_annotation(MethodInfo("@export_multiline"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_MULTILINE_TEXT, Variant::STRING>);
+ register_annotation(MethodInfo("@export_placeholder", PropertyInfo(Variant::STRING, "placeholder")), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_PLACEHOLDER_TEXT, Variant::STRING>);
+ register_annotation(MethodInfo("@export_range", PropertyInfo(Variant::FLOAT, "min"), PropertyInfo(Variant::FLOAT, "max"), PropertyInfo(Variant::FLOAT, "step"), PropertyInfo(Variant::STRING, "extra_hints")), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_RANGE, Variant::FLOAT>, varray(1.0, ""), true);
+ register_annotation(MethodInfo("@export_exp_easing", PropertyInfo(Variant::STRING, "hints")), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_EXP_EASING, Variant::FLOAT>, varray(""), true);
+ register_annotation(MethodInfo("@export_color_no_alpha"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_COLOR_NO_ALPHA, Variant::COLOR>);
+ register_annotation(MethodInfo("@export_node_path", PropertyInfo(Variant::STRING, "type")), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_NODE_PATH_VALID_TYPES, Variant::NODE_PATH>, varray(""), true);
+ register_annotation(MethodInfo("@export_flags", PropertyInfo(Variant::STRING, "names")), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_FLAGS, Variant::INT>, varray(), true);
+ register_annotation(MethodInfo("@export_flags_2d_render"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_LAYERS_2D_RENDER, Variant::INT>);
+ register_annotation(MethodInfo("@export_flags_2d_physics"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_LAYERS_2D_PHYSICS, Variant::INT>);
+ register_annotation(MethodInfo("@export_flags_2d_navigation"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_LAYERS_2D_NAVIGATION, Variant::INT>);
+ register_annotation(MethodInfo("@export_flags_3d_render"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_LAYERS_3D_RENDER, Variant::INT>);
+ register_annotation(MethodInfo("@export_flags_3d_physics"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_LAYERS_3D_PHYSICS, Variant::INT>);
+ register_annotation(MethodInfo("@export_flags_3d_navigation"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_LAYERS_3D_NAVIGATION, Variant::INT>);
+ register_annotation(MethodInfo("@export_flags_avoidance"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_LAYERS_AVOIDANCE, Variant::INT>);
+ // Export grouping annotations.
+ register_annotation(MethodInfo("@export_category", PropertyInfo(Variant::STRING, "name")), AnnotationInfo::STANDALONE, &GDScriptParser::export_group_annotations<PROPERTY_USAGE_CATEGORY>);
+ register_annotation(MethodInfo("@export_group", PropertyInfo(Variant::STRING, "name"), PropertyInfo(Variant::STRING, "prefix")), AnnotationInfo::STANDALONE, &GDScriptParser::export_group_annotations<PROPERTY_USAGE_GROUP>, varray(""));
+ register_annotation(MethodInfo("@export_subgroup", PropertyInfo(Variant::STRING, "name"), PropertyInfo(Variant::STRING, "prefix")), AnnotationInfo::STANDALONE, &GDScriptParser::export_group_annotations<PROPERTY_USAGE_SUBGROUP>, varray(""));
+ // Warning annotations.
+ register_annotation(MethodInfo("@warning_ignore", PropertyInfo(Variant::STRING, "warning")), AnnotationInfo::CLASS | AnnotationInfo::VARIABLE | AnnotationInfo::SIGNAL | AnnotationInfo::CONSTANT | AnnotationInfo::FUNCTION | AnnotationInfo::STATEMENT, &GDScriptParser::warning_annotations, varray(), true);
+ // Networking.
+ register_annotation(MethodInfo("@rpc", PropertyInfo(Variant::STRING, "mode"), PropertyInfo(Variant::STRING, "sync"), PropertyInfo(Variant::STRING, "transfer_mode"), PropertyInfo(Variant::INT, "transfer_channel")), AnnotationInfo::FUNCTION, &GDScriptParser::rpc_annotation, varray("authority", "call_remote", "unreliable", 0));
+ }
#ifdef DEBUG_ENABLED
is_ignoring_warnings = !(bool)GLOBAL_GET("debug/gdscript/warnings/enable");
@@ -3337,14 +3341,21 @@ GDScriptParser::TypeNode *GDScriptParser::parse_type(bool p_allow_void) {
if (match(GDScriptTokenizer::Token::BRACKET_OPEN)) {
// Typed collection (like Array[int]).
- type->container_type = parse_type(false); // Don't allow void for array element type.
- if (type->container_type == nullptr) {
- push_error(R"(Expected type for collection after "[".)");
- complete_extents(type);
- type = nullptr;
- } else if (type->container_type->container_type != nullptr) {
- push_error("Nested typed collections are not supported.");
- }
+ bool first_pass = true;
+ do {
+ TypeNode *container_type = parse_type(false); // Don't allow void for element type.
+ if (container_type == nullptr) {
+ push_error(vformat(R"(Expected type for collection after "%s".)", first_pass ? "[" : ","));
+ complete_extents(type);
+ type = nullptr;
+ break;
+ } else if (container_type->container_types.size() > 0) {
+ push_error("Nested typed collections are not supported.");
+ } else {
+ type->container_types.append(container_type);
+ }
+ first_pass = false;
+ } while (match(GDScriptTokenizer::Token::COMMA));
consume(GDScriptTokenizer::Token::BRACKET_CLOSE, R"(Expected closing "]" after collection type.)");
if (type != nullptr) {
complete_extents(type);
@@ -3996,8 +4007,8 @@ bool GDScriptParser::export_annotations(const AnnotationNode *p_annotation, Node
variable->export_info.type = Variant::INT;
}
} else if (p_annotation->name == SNAME("@export_multiline")) {
- if (export_type.builtin_type == Variant::ARRAY && export_type.has_container_element_type()) {
- DataType inner_type = export_type.get_container_element_type();
+ if (export_type.builtin_type == Variant::ARRAY && export_type.has_container_element_type(0)) {
+ DataType inner_type = export_type.get_container_element_type(0);
if (inner_type.builtin_type != Variant::STRING) {
push_error(vformat(R"("%s" annotation on arrays requires a string type but type "%s" was given instead.)", p_annotation->name.operator String(), inner_type.to_string()), variable);
return false;
@@ -4033,8 +4044,8 @@ bool GDScriptParser::export_annotations(const AnnotationNode *p_annotation, Node
bool is_array = false;
- if (export_type.builtin_type == Variant::ARRAY && export_type.has_container_element_type()) {
- export_type = export_type.get_container_element_type(); // Use inner type for.
+ if (export_type.builtin_type == Variant::ARRAY && export_type.has_container_element_type(0)) {
+ export_type = export_type.get_container_element_type(0); // Use inner type for.
is_array = true;
}
@@ -4344,8 +4355,8 @@ String GDScriptParser::DataType::to_string() const {
if (builtin_type == Variant::NIL) {
return "null";
}
- if (builtin_type == Variant::ARRAY && has_container_element_type()) {
- return vformat("Array[%s]", container_element_type->to_string());
+ if (builtin_type == Variant::ARRAY && has_container_element_type(0)) {
+ return vformat("Array[%s]", container_element_types[0].to_string());
}
return Variant::get_type_name(builtin_type);
case NATIVE:
@@ -4398,36 +4409,36 @@ PropertyInfo GDScriptParser::DataType::to_property_info(const String &p_name) co
switch (kind) {
case BUILTIN:
result.type = builtin_type;
- if (builtin_type == Variant::ARRAY && has_container_element_type()) {
- const DataType *elem_type = container_element_type;
- switch (elem_type->kind) {
+ if (builtin_type == Variant::ARRAY && has_container_element_type(0)) {
+ const DataType elem_type = get_container_element_type(0);
+ switch (elem_type.kind) {
case BUILTIN:
result.hint = PROPERTY_HINT_ARRAY_TYPE;
- result.hint_string = Variant::get_type_name(elem_type->builtin_type);
+ result.hint_string = Variant::get_type_name(elem_type.builtin_type);
break;
case NATIVE:
result.hint = PROPERTY_HINT_ARRAY_TYPE;
- result.hint_string = elem_type->native_type;
+ result.hint_string = elem_type.native_type;
break;
case SCRIPT:
result.hint = PROPERTY_HINT_ARRAY_TYPE;
- if (elem_type->script_type.is_valid() && elem_type->script_type->get_global_name() != StringName()) {
- result.hint_string = elem_type->script_type->get_global_name();
+ if (elem_type.script_type.is_valid() && elem_type.script_type->get_global_name() != StringName()) {
+ result.hint_string = elem_type.script_type->get_global_name();
} else {
- result.hint_string = elem_type->native_type;
+ result.hint_string = elem_type.native_type;
}
break;
case CLASS:
result.hint = PROPERTY_HINT_ARRAY_TYPE;
- if (elem_type->class_type != nullptr && elem_type->class_type->get_global_name() != StringName()) {
- result.hint_string = elem_type->class_type->get_global_name();
+ if (elem_type.class_type != nullptr && elem_type.class_type->get_global_name() != StringName()) {
+ result.hint_string = elem_type.class_type->get_global_name();
} else {
- result.hint_string = elem_type->native_type;
+ result.hint_string = elem_type.native_type;
}
break;
case ENUM:
result.hint = PROPERTY_HINT_ARRAY_TYPE;
- result.hint_string = String(elem_type->native_type).replace("::", ".");
+ result.hint_string = String(elem_type.native_type).replace("::", ".");
break;
case VARIANT:
case RESOLVING:
diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h
index 4b46b98baa..88b5bdc43f 100644
--- a/modules/gdscript/gdscript_parser.h
+++ b/modules/gdscript/gdscript_parser.h
@@ -101,11 +101,9 @@ public:
struct WhileNode;
class DataType {
- private:
- // Private access so we can control memory management.
- DataType *container_element_type = nullptr;
-
public:
+ Vector<DataType> container_element_types;
+
enum Kind {
BUILTIN,
NATIVE,
@@ -152,24 +150,39 @@ public:
_FORCE_INLINE_ String to_string_strict() const { return is_hard_type() ? to_string() : "Variant"; }
PropertyInfo to_property_info(const String &p_name) const;
- _FORCE_INLINE_ void set_container_element_type(const DataType &p_type) {
- container_element_type = memnew(DataType(p_type));
+ _FORCE_INLINE_ static DataType get_variant_type() { // Default DataType for container elements.
+ DataType datatype;
+ datatype.kind = VARIANT;
+ datatype.type_source = INFERRED;
+ return datatype;
}
- _FORCE_INLINE_ DataType get_container_element_type() const {
- ERR_FAIL_NULL_V(container_element_type, DataType());
- return *container_element_type;
+ _FORCE_INLINE_ void set_container_element_type(int p_index, const DataType &p_type) {
+ ERR_FAIL_COND(p_index < 0);
+ while (p_index >= container_element_types.size()) {
+ container_element_types.push_back(get_variant_type());
+ }
+ container_element_types.write[p_index] = DataType(p_type);
}
- _FORCE_INLINE_ bool has_container_element_type() const {
- return container_element_type != nullptr;
+ _FORCE_INLINE_ DataType get_container_element_type(int p_index) const {
+ ERR_FAIL_INDEX_V(p_index, container_element_types.size(), get_variant_type());
+ return container_element_types[p_index];
}
- _FORCE_INLINE_ void unset_container_element_type() {
- if (container_element_type) {
- memdelete(container_element_type);
- };
- container_element_type = nullptr;
+ _FORCE_INLINE_ DataType get_container_element_type_or_variant(int p_index) const {
+ if (p_index < 0 || p_index >= container_element_types.size()) {
+ return get_variant_type();
+ }
+ return container_element_types[p_index];
+ }
+
+ _FORCE_INLINE_ bool has_container_element_type(int p_index) const {
+ return p_index >= 0 && p_index < container_element_types.size();
+ }
+
+ _FORCE_INLINE_ bool has_container_element_types() const {
+ return !container_element_types.is_empty();
}
bool is_typed_container_type() const;
@@ -229,10 +242,7 @@ public:
class_type = p_other.class_type;
method_info = p_other.method_info;
enum_values = p_other.enum_values;
- unset_container_element_type();
- if (p_other.has_container_element_type()) {
- set_container_element_type(p_other.get_container_element_type());
- }
+ container_element_types = p_other.container_element_types;
}
DataType() = default;
@@ -241,9 +251,7 @@ public:
*this = p_other;
}
- ~DataType() {
- unset_container_element_type();
- }
+ ~DataType() {}
};
struct ParserError {
@@ -1183,7 +1191,11 @@ public:
struct TypeNode : public Node {
Vector<IdentifierNode *> type_chain;
- TypeNode *container_type = nullptr;
+ Vector<TypeNode *> container_types;
+
+ TypeNode *get_container_type_or_null(int p_index) const {
+ return p_index >= 0 && p_index < container_types.size() ? container_types[p_index] : nullptr;
+ }
TypeNode() {
type = TYPE;
@@ -1358,7 +1370,7 @@ private:
AnnotationAction apply = nullptr;
MethodInfo info;
};
- HashMap<StringName, AnnotationInfo> valid_annotations;
+ static HashMap<StringName, AnnotationInfo> valid_annotations;
List<AnnotationNode *> annotation_stack;
typedef ExpressionNode *(GDScriptParser::*ParseFunction)(ExpressionNode *p_previous_operand, bool p_can_assign);
@@ -1458,7 +1470,7 @@ private:
SuiteNode *parse_suite(const String &p_context, SuiteNode *p_suite = nullptr, bool p_for_lambda = false);
// Annotations
AnnotationNode *parse_annotation(uint32_t p_valid_targets);
- bool register_annotation(const MethodInfo &p_info, uint32_t p_target_kinds, AnnotationAction p_apply, const Vector<Variant> &p_default_arguments = Vector<Variant>(), bool p_is_vararg = false);
+ static bool register_annotation(const MethodInfo &p_info, uint32_t p_target_kinds, AnnotationAction p_apply, const Vector<Variant> &p_default_arguments = Vector<Variant>(), bool p_is_vararg = false);
bool validate_annotation_arguments(AnnotationNode *p_annotation);
void clear_unused_annotations();
bool tool_annotation(const AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class);
diff --git a/modules/gdscript/gdscript_tokenizer.h b/modules/gdscript/gdscript_tokenizer.h
index 6dd8a98652..a64aaf6820 100644
--- a/modules/gdscript/gdscript_tokenizer.h
+++ b/modules/gdscript/gdscript_tokenizer.h
@@ -37,6 +37,12 @@
#include "core/templates/vector.h"
#include "core/variant/variant.h"
+#ifdef MINGW_ENABLED
+#undef CONST
+#undef IN
+#undef VOID
+#endif
+
class GDScriptTokenizer {
public:
enum CursorPlace {
diff --git a/modules/gdscript/gdscript_utility_functions.cpp b/modules/gdscript/gdscript_utility_functions.cpp
index 40c564c36b..f8cb460e40 100644
--- a/modules/gdscript/gdscript_utility_functions.cpp
+++ b/modules/gdscript/gdscript_utility_functions.cpp
@@ -194,9 +194,9 @@ struct GDScriptUtilityFunctionsDefinitions {
// Calculate how many.
int count = 0;
if (incr > 0) {
- count = ((to - from - 1) / incr) + 1;
+ count = Math::division_round_up(to - from, incr);
} else {
- count = ((from - to - 1) / -incr) + 1;
+ count = Math::division_round_up(from - to, -incr);
}
Error err = arr.resize(count);
@@ -470,7 +470,8 @@ struct GDScriptUtilityFunctionsDefinitions {
static inline void len(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
VALIDATE_ARG_COUNT(1);
switch (p_args[0]->get_type()) {
- case Variant::STRING: {
+ case Variant::STRING:
+ case Variant::STRING_NAME: {
String d = *p_args[0];
*r_ret = d.length();
} break;
diff --git a/modules/gdscript/gdscript_vm.cpp b/modules/gdscript/gdscript_vm.cpp
index d31411b26b..3abfc7f8e3 100644
--- a/modules/gdscript/gdscript_vm.cpp
+++ b/modules/gdscript/gdscript_vm.cpp
@@ -36,6 +36,18 @@
#include "core/os/os.h"
#ifdef DEBUG_ENABLED
+
+static bool _profile_count_as_native(const Object *p_base_obj, const StringName &p_methodname) {
+ if (!p_base_obj) {
+ return false;
+ }
+ StringName cname = p_base_obj->get_class_name();
+ if ((p_methodname == "new" && cname == "GDScript") || p_methodname == "call") {
+ return false;
+ }
+ return ClassDB::class_exists(cname) && ClassDB::has_method(cname, p_methodname, false);
+}
+
static String _get_element_type(Variant::Type builtin_type, const StringName &native_type, const Ref<Script> &script_type) {
if (script_type.is_valid() && script_type->is_valid()) {
return GDScript::debug_get_script_name(script_type);
@@ -84,6 +96,18 @@ static String _get_var_type(const Variant *p_var) {
return basestr;
}
+
+void GDScriptFunction::_profile_native_call(uint64_t p_t_taken, const String &p_func_name, const String &p_instance_class_name) {
+ HashMap<String, Profile::NativeProfile>::Iterator inner_prof = profile.native_calls.find(p_func_name);
+ if (inner_prof) {
+ inner_prof->value.call_count += 1;
+ } else {
+ String sig = vformat("%s::0::%s%s%s", get_script()->get_script_path(), p_instance_class_name, p_instance_class_name.is_empty() ? "" : ".", p_func_name);
+ inner_prof = profile.native_calls.insert(p_func_name, Profile::NativeProfile{ 1, 0, sig });
+ }
+ inner_prof->value.total_time += p_t_taken;
+}
+
#endif // DEBUG_ENABLED
Variant GDScriptFunction::_get_default_variant_for_data_type(const GDScriptDataType &p_data_type) {
@@ -91,8 +115,8 @@ Variant GDScriptFunction::_get_default_variant_for_data_type(const GDScriptDataT
if (p_data_type.builtin_type == Variant::ARRAY) {
Array array;
// Typed array.
- if (p_data_type.has_container_element_type()) {
- const GDScriptDataType &element_type = p_data_type.get_container_element_type();
+ if (p_data_type.has_container_element_type(0)) {
+ const GDScriptDataType &element_type = p_data_type.get_container_element_type(0);
array.set_typed(element_type.builtin_type, element_type.native_type, element_type.script_type);
}
@@ -631,9 +655,6 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
}
bool exit_ok = false;
bool awaited = false;
-#endif
-
-#ifdef DEBUG_ENABLED
int variant_address_limits[ADDR_TYPE_MAX] = { _stack_size, _constant_count, p_instance ? p_instance->members.size() : 0 };
#endif
@@ -1661,16 +1682,14 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
if (GDScriptLanguage::get_singleton()->profiling) {
call_time = OS::get_singleton()->get_ticks_usec();
}
-
+ Variant::Type base_type = base->get_type();
+ Object *base_obj = base->get_validated_object();
+ StringName base_class = base_obj ? base_obj->get_class_name() : StringName();
#endif
+
Callable::CallError err;
if (call_ret) {
GET_INSTRUCTION_ARG(ret, argc + 1);
-#ifdef DEBUG_ENABLED
- Variant::Type base_type = base->get_type();
- Object *base_obj = base->get_validated_object();
- StringName base_class = base_obj ? base_obj->get_class_name() : StringName();
-#endif
base->callp(*methodname, (const Variant **)argptrs, argc, *ret, err);
#ifdef DEBUG_ENABLED
if (ret->get_type() == Variant::NIL) {
@@ -1704,8 +1723,13 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
base->callp(*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;
+ uint64_t t_taken = OS::get_singleton()->get_ticks_usec() - call_time;
+ if (GDScriptLanguage::get_singleton()->profile_native_calls && _profile_count_as_native(base_obj, *methodname)) {
+ _profile_native_call(t_taken, *methodname, base_class);
+ }
+ function_call_time += t_taken;
}
if (err.error != Callable::CallError::CALL_OK) {
@@ -1782,8 +1806,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
#ifdef DEBUG_ENABLED
uint64_t call_time = 0;
-
- if (GDScriptLanguage::get_singleton()->profiling) {
+ if (GDScriptLanguage::get_singleton()->profiling && GDScriptLanguage::get_singleton()->profile_native_calls) {
call_time = OS::get_singleton()->get_ticks_usec();
}
#endif
@@ -1797,8 +1820,11 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
}
#ifdef DEBUG_ENABLED
- if (GDScriptLanguage::get_singleton()->profiling) {
- function_call_time += OS::get_singleton()->get_ticks_usec() - call_time;
+
+ if (GDScriptLanguage::get_singleton()->profiling && GDScriptLanguage::get_singleton()->profile_native_calls) {
+ uint64_t t_taken = OS::get_singleton()->get_ticks_usec() - call_time;
+ _profile_native_call(t_taken, method->get_name(), method->get_instance_class());
+ function_call_time += t_taken;
}
if (err.error != Callable::CallError::CALL_OK) {
@@ -1851,22 +1877,10 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
const Variant **argptrs = const_cast<const Variant **>(instruction_args);
-#ifdef DEBUG_ENABLED
- uint64_t call_time = 0;
-
- if (GDScriptLanguage::get_singleton()->profiling) {
- call_time = OS::get_singleton()->get_ticks_usec();
- }
-#endif
-
Callable::CallError err;
Variant::call_static(builtin_type, *methodname, argptrs, argc, *ret, err);
#ifdef DEBUG_ENABLED
- if (GDScriptLanguage::get_singleton()->profiling) {
- function_call_time += OS::get_singleton()->get_ticks_usec() - call_time;
- }
-
if (err.error != Callable::CallError::CALL_OK) {
err_text = _get_call_error(err, "static function '" + methodname->operator String() + "' in type '" + Variant::get_type_name(builtin_type) + "'", argptrs);
OPCODE_BREAK;
@@ -1895,8 +1909,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
#ifdef DEBUG_ENABLED
uint64_t call_time = 0;
-
- if (GDScriptLanguage::get_singleton()->profiling) {
+ if (GDScriptLanguage::get_singleton()->profiling && GDScriptLanguage::get_singleton()->profile_native_calls) {
call_time = OS::get_singleton()->get_ticks_usec();
}
#endif
@@ -1905,15 +1918,17 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
*ret = method->call(nullptr, argptrs, argc, err);
#ifdef DEBUG_ENABLED
- if (GDScriptLanguage::get_singleton()->profiling) {
- function_call_time += OS::get_singleton()->get_ticks_usec() - call_time;
+ if (GDScriptLanguage::get_singleton()->profiling && GDScriptLanguage::get_singleton()->profile_native_calls) {
+ uint64_t t_taken = OS::get_singleton()->get_ticks_usec() - call_time;
+ _profile_native_call(t_taken, method->get_name(), method->get_instance_class());
+ function_call_time += t_taken;
}
+#endif
if (err.error != Callable::CallError::CALL_OK) {
err_text = _get_call_error(err, "static function '" + method->get_name().operator String() + "' in type '" + method->get_instance_class().operator String() + "'", argptrs);
OPCODE_BREAK;
}
-#endif
ip += 3;
}
@@ -1951,8 +1966,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
#ifdef DEBUG_ENABLED
uint64_t call_time = 0;
-
- if (GDScriptLanguage::get_singleton()->profiling) {
+ if (GDScriptLanguage::get_singleton()->profiling && GDScriptLanguage::get_singleton()->profile_native_calls) {
call_time = OS::get_singleton()->get_ticks_usec();
}
#endif
@@ -1961,10 +1975,13 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
method->validated_call(base_obj, (const Variant **)argptrs, ret);
#ifdef DEBUG_ENABLED
- if (GDScriptLanguage::get_singleton()->profiling) {
- function_call_time += OS::get_singleton()->get_ticks_usec() - call_time;
+ if (GDScriptLanguage::get_singleton()->profiling && GDScriptLanguage::get_singleton()->profile_native_calls) {
+ uint64_t t_taken = OS::get_singleton()->get_ticks_usec() - call_time;
+ _profile_native_call(t_taken, method->get_name(), method->get_instance_class());
+ function_call_time += t_taken;
}
#endif
+
ip += 3;
}
DISPATCH_OPCODE;
@@ -1998,8 +2015,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
Variant **argptrs = instruction_args;
#ifdef DEBUG_ENABLED
uint64_t call_time = 0;
-
- if (GDScriptLanguage::get_singleton()->profiling) {
+ if (GDScriptLanguage::get_singleton()->profiling && GDScriptLanguage::get_singleton()->profile_native_calls) {
call_time = OS::get_singleton()->get_ticks_usec();
}
#endif
@@ -2009,10 +2025,13 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
method->validated_call(base_obj, (const Variant **)argptrs, nullptr);
#ifdef DEBUG_ENABLED
- if (GDScriptLanguage::get_singleton()->profiling) {
- function_call_time += OS::get_singleton()->get_ticks_usec() - call_time;
+ if (GDScriptLanguage::get_singleton()->profiling && GDScriptLanguage::get_singleton()->profile_native_calls) {
+ uint64_t t_taken = OS::get_singleton()->get_ticks_usec() - call_time;
+ _profile_native_call(t_taken, method->get_name(), method->get_instance_class());
+ function_call_time += t_taken;
}
#endif
+
ip += 3;
}
DISPATCH_OPCODE;
@@ -2033,22 +2052,9 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
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;
diff --git a/modules/gdscript/language_server/gdscript_text_document.cpp b/modules/gdscript/language_server/gdscript_text_document.cpp
index 44f605232d..0b1371851b 100644
--- a/modules/gdscript/language_server/gdscript_text_document.cpp
+++ b/modules/gdscript/language_server/gdscript_text_document.cpp
@@ -315,9 +315,8 @@ Dictionary GDScriptTextDocument::resolve(const Dictionary &p_params) {
Vector<String> param_symbols = query.split(SYMBOL_SEPERATOR, false);
if (param_symbols.size() >= 2) {
- String class_ = param_symbols[0];
- StringName class_name = class_;
- String member_name = param_symbols[param_symbols.size() - 1];
+ StringName class_name = param_symbols[0];
+ const String &member_name = param_symbols[param_symbols.size() - 1];
String inner_class_name;
if (param_symbols.size() >= 3) {
inner_class_name = param_symbols[1];
diff --git a/modules/gdscript/tests/gdscript_test_runner.cpp b/modules/gdscript/tests/gdscript_test_runner.cpp
index f91dc83f2c..361ca276bb 100644
--- a/modules/gdscript/tests/gdscript_test_runner.cpp
+++ b/modules/gdscript/tests/gdscript_test_runner.cpp
@@ -78,31 +78,30 @@ void init_autoloads() {
scn.instantiate();
scn->set_path(info.path);
scn->reload_from_file();
- ERR_CONTINUE_MSG(!scn.is_valid(), vformat("Can't autoload: %s.", info.path));
+ ERR_CONTINUE_MSG(!scn.is_valid(), vformat("Failed to instantiate an autoload, can't load from path: %s.", info.path));
if (scn.is_valid()) {
n = scn->instantiate();
}
} else {
Ref<Resource> res = ResourceLoader::load(info.path);
- ERR_CONTINUE_MSG(res.is_null(), vformat("Can't autoload: %s.", info.path));
+ ERR_CONTINUE_MSG(res.is_null(), vformat("Failed to instantiate an autoload, can't load from path: %s.", info.path));
Ref<Script> scr = res;
if (scr.is_valid()) {
StringName ibt = scr->get_instance_base_type();
bool valid_type = ClassDB::is_parent_class(ibt, "Node");
- ERR_CONTINUE_MSG(!valid_type, vformat("Script does not inherit from Node: %s.", info.path));
+ ERR_CONTINUE_MSG(!valid_type, vformat("Failed to instantiate an autoload, script '%s' does not inherit from 'Node'.", info.path));
Object *obj = ClassDB::instantiate(ibt);
-
- ERR_CONTINUE_MSG(!obj, vformat("Cannot instance script for Autoload, expected 'Node' inheritance, got: %s.", ibt));
+ ERR_CONTINUE_MSG(!obj, vformat("Failed to instantiate an autoload, cannot instantiate '%s'.", ibt));
n = Object::cast_to<Node>(obj);
n->set_script(scr);
}
}
- ERR_CONTINUE_MSG(!n, vformat("Path in autoload not a node or script: %s.", info.path));
+ ERR_CONTINUE_MSG(!n, vformat("Failed to instantiate an autoload, path is not pointing to a scene or a script: %s.", info.path));
n->set_name(info.name);
for (int i = 0; i < ScriptServer::get_language_count(); i++) {
diff --git a/modules/gdscript/tests/scripts/runtime/features/builtin_method_as_callable.gd b/modules/gdscript/tests/scripts/runtime/features/builtin_method_as_callable.gd
new file mode 100644
index 0000000000..e4016c0119
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/builtin_method_as_callable.gd
@@ -0,0 +1,6 @@
+func test():
+ var array: Array = [1, 2, 3]
+ print(array)
+ var callable: Callable = array.clear
+ callable.call()
+ print(array)
diff --git a/modules/gdscript/tests/scripts/runtime/features/builtin_method_as_callable.out b/modules/gdscript/tests/scripts/runtime/features/builtin_method_as_callable.out
new file mode 100644
index 0000000000..c4182b38e9
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/builtin_method_as_callable.out
@@ -0,0 +1,3 @@
+GDTEST_OK
+[1, 2, 3]
+[]
diff --git a/modules/gdscript/tests/scripts/runtime/features/static_method_as_callable.gd b/modules/gdscript/tests/scripts/runtime/features/static_method_as_callable.gd
index f6aa58737f..97e9da3b26 100644
--- a/modules/gdscript/tests/scripts/runtime/features/static_method_as_callable.gd
+++ b/modules/gdscript/tests/scripts/runtime/features/static_method_as_callable.gd
@@ -1,12 +1,18 @@
-# GH-79521
+# GH-79521, GH-86032
class_name TestStaticMethodAsCallable
static func static_func() -> String:
return "Test"
+static func another_static_func():
+ prints("another_static_func:", static_func.call(), static_func.is_valid())
+
func test():
var a: Callable = TestStaticMethodAsCallable.static_func
var b: Callable = static_func
prints(a.call(), a.is_valid())
prints(b.call(), b.is_valid())
+ @warning_ignore("static_called_on_instance")
+ another_static_func()
+ TestStaticMethodAsCallable.another_static_func()
diff --git a/modules/gdscript/tests/scripts/runtime/features/static_method_as_callable.out b/modules/gdscript/tests/scripts/runtime/features/static_method_as_callable.out
index e6d461b8f9..2b773ce8ee 100644
--- a/modules/gdscript/tests/scripts/runtime/features/static_method_as_callable.out
+++ b/modules/gdscript/tests/scripts/runtime/features/static_method_as_callable.out
@@ -1,3 +1,5 @@
GDTEST_OK
Test true
Test true
+another_static_func: Test true
+another_static_func: Test true