diff options
Diffstat (limited to 'modules/gdscript')
43 files changed, 644 insertions, 192 deletions
diff --git a/modules/gdscript/doc_classes/@GDScript.xml b/modules/gdscript/doc_classes/@GDScript.xml index d9fab01dce..70151c4d21 100644 --- a/modules/gdscript/doc_classes/@GDScript.xml +++ b/modules/gdscript/doc_classes/@GDScript.xml @@ -184,27 +184,24 @@ <method name="range" qualifiers="vararg"> <return type="Array" /> <description> - Returns an array with the given range. Range can be 1 argument [code]N[/code] (0 to [code]N[/code] - 1), two arguments ([code]initial[/code], [code]final - 1[/code]) or three arguments ([code]initial[/code], [code]final - 1[/code], [code]increment[/code]). Returns an empty array if the range isn't valid (e.g. [code]range(2, 5, -1)[/code] or [code]range(5, 5, 1)[/code]). - Returns an array with the given range. [code]range()[/code] can have 1 argument N ([code]0[/code] to [code]N - 1[/code]), two arguments ([code]initial[/code], [code]final - 1[/code]) or three arguments ([code]initial[/code], [code]final - 1[/code], [code]increment[/code]). [code]increment[/code] can be negative. If [code]increment[/code] is negative, [code]final - 1[/code] will become [code]final + 1[/code]. Also, the initial value must be greater than the final value for the loop to run. - [code]range()(/code] converts all arguments to [int] before processing. + Returns an array with the given range. [method range] can be called in three ways: + [code]range(n: int)[/code]: Starts from 0, increases by steps of 1, and stops [i]before[/i] [code]n[/code]. The argument [code]n[/code] is [b]exclusive[/b]. + [code]range(b: int, n: int)[/code]: Starts from [code]b[/code], increases by steps of 1, and stops [i]before[/i] [code]n[/code]. The arguments [code]b[/code] and [code]n[/code] are [b]inclusive[/b] and [b]exclusive[/b], respectively. + [code]range(b: int, n: int, s: int)[/code]: Starts from [code]b[/code], increases/decreases by steps of [code]s[/code], and stops [i]before[/i] [code]n[/code]. The arguments [code]b[/code] and [code]n[/code] are [b]inclusive[/b] and [b]exclusive[/b], respectively. The argument [code]s[/code] [b]can[/b] be negative, but not [code]0[/code]. If [code]s[/code] is [code]0[/code], an error message is printed. + [method range] converts all arguments to [int] before processing. + [b]Note:[/b] Returns an empty array if no value meets the value constraint (e.g. [code]range(2, 5, -1)[/code] or [code]range(5, 5, 1)[/code]). + Examples: [codeblock] - print(range(4)) - print(range(2, 5)) - print(range(0, 6, 2)) - [/codeblock] - Output: - [codeblock] - [0, 1, 2, 3] - [2, 3, 4] - [0, 2, 4] + print(range(4)) # Prints [0, 1, 2, 3] + print(range(2, 5)) # Prints [2, 3, 4] + print(range(0, 6, 2)) # Prints [0, 2, 4] + print(range(4, 1, -1)) # Prints [4, 3, 2] [/codeblock] To iterate over an [Array] backwards, use: [codeblock] var array = [3, 6, 9] - var i := array.size() - 1 - while i >= 0: - print(array[i]) - i -= 1 + for i in range(array.size(), 0, -1): + print(array[i - 1]) [/codeblock] Output: [codeblock] diff --git a/modules/gdscript/editor/gdscript_translation_parser_plugin.cpp b/modules/gdscript/editor/gdscript_translation_parser_plugin.cpp index a8f4483cf4..9b540b16f2 100644 --- a/modules/gdscript/editor/gdscript_translation_parser_plugin.cpp +++ b/modules/gdscript/editor/gdscript_translation_parser_plugin.cpp @@ -44,7 +44,7 @@ Error GDScriptEditorTranslationParserPlugin::parse_file(const String &p_path, Ve // Search strings in AssignmentNode -> text = "__", hint_tooltip = "__" etc. Error err; - RES loaded_res = ResourceLoader::load(p_path, "", ResourceFormatLoader::CACHE_MODE_REUSE, &err); + Ref<Resource> loaded_res = ResourceLoader::load(p_path, "", ResourceFormatLoader::CACHE_MODE_REUSE, &err); if (err) { ERR_PRINT("Failed to load " + p_path); return err; diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp index 3a0e320e9b..1b4711804c 100644 --- a/modules/gdscript/gdscript.cpp +++ b/modules/gdscript/gdscript.cpp @@ -82,7 +82,7 @@ Variant GDScriptNativeClass::_new() { RefCounted *rc = Object::cast_to<RefCounted>(o); if (rc) { - return REF(rc); + return Ref<RefCounted>(rc); } else { return o; } @@ -92,6 +92,21 @@ Object *GDScriptNativeClass::instantiate() { return ClassDB::instantiate(name); } +Variant GDScriptNativeClass::callp(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) { + if (p_method == SNAME("new")) { + // Constructor. + return Object::callp(p_method, p_args, p_argcount, r_error); + } + MethodBind *method = ClassDB::get_method(name, p_method); + if (method) { + // Native static method. + return method->call(nullptr, p_args, p_argcount, r_error); + } + + r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; + return Variant(); +} + GDScriptFunction *GDScript::_super_constructor(GDScript *p_script) { if (p_script->initializer) { return p_script->initializer; @@ -180,7 +195,7 @@ Variant GDScript::_new(const Variant **p_args, int p_argcount, Callable::CallErr } r_error.error = Callable::CallError::CALL_OK; - REF ref; + Ref<RefCounted> ref; Object *owner = nullptr; GDScript *_baseptr = this; @@ -198,7 +213,7 @@ Variant GDScript::_new(const Variant **p_args, int p_argcount, Callable::CallErr RefCounted *r = Object::cast_to<RefCounted>(owner); if (r) { - ref = REF(r); + ref = Ref<RefCounted>(r); } GDScriptInstance *instance = _create_instance(p_args, p_argcount, owner, r != nullptr, r_error); @@ -1015,17 +1030,21 @@ Error GDScript::load_byte_code(const String &p_path) { Error GDScript::load_source_code(const String &p_path) { Vector<uint8_t> sourcef; Error err; - FileAccess *f = FileAccess::open(p_path, FileAccess::READ, &err); + Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ, &err); if (err) { - ERR_FAIL_COND_V(err, err); + const char *err_name; + if (err < 0 || err >= ERR_MAX) { + err_name = "(invalid error code)"; + } else { + err_name = error_names[err]; + } + ERR_FAIL_COND_V_MSG(err, err, "Attempt to open script '" + p_path + "' resulted in error '" + err_name + "'."); } uint64_t len = f->get_length(); sourcef.resize(len + 1); uint8_t *w = sourcef.ptrw(); uint64_t r = f->get_buffer(w, len); - f->close(); - memdelete(f); ERR_FAIL_COND_V(r != len, ERR_CANT_OPEN); w[len] = 0; @@ -2084,7 +2103,7 @@ bool GDScriptLanguage::handles_global_class_type(const String &p_type) const { String GDScriptLanguage::get_global_class_name(const String &p_path, String *r_base_type, String *r_icon_path) const { Vector<uint8_t> sourcef; Error err; - FileAccessRef f = FileAccess::open(p_path, FileAccess::READ, &err); + Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ, &err); if (err) { return String(); } @@ -2118,8 +2137,8 @@ String GDScriptLanguage::get_global_class_name(const String &p_path, String *r_b } else { Vector<StringName> extend_classes = subclass->extends; - FileAccessRef subfile = FileAccess::open(subclass->extends_path, FileAccess::READ); - if (!subfile) { + Ref<FileAccess> subfile = FileAccess::open(subclass->extends_path, FileAccess::READ); + if (subfile.is_null()) { break; } String subsource = subfile->get_as_utf8_string(); @@ -2273,7 +2292,7 @@ Ref<GDScript> GDScriptLanguage::get_orphan_subclass(const String &p_qualified_na /*************** RESOURCE ***************/ -RES ResourceFormatLoaderGDScript::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, CacheMode p_cache_mode) { +Ref<Resource> ResourceFormatLoaderGDScript::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, CacheMode p_cache_mode) { if (r_error) { *r_error = ERR_FILE_CANT_OPEN; } @@ -2316,8 +2335,8 @@ String ResourceFormatLoaderGDScript::get_resource_type(const String &p_path) con } void ResourceFormatLoaderGDScript::get_dependencies(const String &p_path, List<String> *p_dependencies, bool p_add_types) { - FileAccessRef file = FileAccess::open(p_path, FileAccess::READ); - ERR_FAIL_COND_MSG(!file, "Cannot open file '" + p_path + "'."); + Ref<FileAccess> file = FileAccess::open(p_path, FileAccess::READ); + ERR_FAIL_COND_MSG(file.is_null(), "Cannot open file '" + p_path + "'."); String source = file->get_as_utf8_string(); if (source.is_empty()) { @@ -2334,24 +2353,23 @@ void ResourceFormatLoaderGDScript::get_dependencies(const String &p_path, List<S } } -Error ResourceFormatSaverGDScript::save(const String &p_path, const RES &p_resource, uint32_t p_flags) { +Error ResourceFormatSaverGDScript::save(const String &p_path, const Ref<Resource> &p_resource, uint32_t p_flags) { Ref<GDScript> sqscr = p_resource; ERR_FAIL_COND_V(sqscr.is_null(), ERR_INVALID_PARAMETER); String source = sqscr->get_source_code(); - Error err; - FileAccess *file = FileAccess::open(p_path, FileAccess::WRITE, &err); + { + Error err; + Ref<FileAccess> file = FileAccess::open(p_path, FileAccess::WRITE, &err); - ERR_FAIL_COND_V_MSG(err, err, "Cannot save GDScript file '" + p_path + "'."); + ERR_FAIL_COND_V_MSG(err, err, "Cannot save GDScript file '" + p_path + "'."); - file->store_string(source); - if (file->get_error() != OK && file->get_error() != ERR_FILE_EOF) { - memdelete(file); - return ERR_CANT_CREATE; + file->store_string(source); + if (file->get_error() != OK && file->get_error() != ERR_FILE_EOF) { + return ERR_CANT_CREATE; + } } - file->close(); - memdelete(file); if (ScriptServer::is_reload_scripts_on_save_enabled()) { GDScriptLanguage::get_singleton()->reload_tool_script(p_resource, false); @@ -2360,12 +2378,12 @@ Error ResourceFormatSaverGDScript::save(const String &p_path, const RES &p_resou return OK; } -void ResourceFormatSaverGDScript::get_recognized_extensions(const RES &p_resource, List<String> *p_extensions) const { +void ResourceFormatSaverGDScript::get_recognized_extensions(const Ref<Resource> &p_resource, List<String> *p_extensions) const { if (Object::cast_to<GDScript>(*p_resource)) { p_extensions->push_back("gd"); } } -bool ResourceFormatSaverGDScript::recognize(const RES &p_resource) const { +bool ResourceFormatSaverGDScript::recognize(const Ref<Resource> &p_resource) const { return Object::cast_to<GDScript>(*p_resource) != nullptr; } diff --git a/modules/gdscript/gdscript.h b/modules/gdscript/gdscript.h index f7fa967883..a20f3b2fca 100644 --- a/modules/gdscript/gdscript.h +++ b/modules/gdscript/gdscript.h @@ -52,6 +52,7 @@ public: _FORCE_INLINE_ const StringName &get_name() const { return name; } Variant _new(); Object *instantiate(); + virtual Variant callp(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) override; GDScriptNativeClass(const StringName &p_name); }; @@ -261,11 +262,12 @@ class GDScriptInstance : public ScriptInstance { friend class GDScript; friend class GDScriptFunction; friend class GDScriptLambdaCallable; + friend class GDScriptLambdaSelfCallable; friend class GDScriptCompiler; friend struct GDScriptUtilityFunctionsDefinitions; ObjectID owner_id; - Object *owner; + Object *owner = nullptr; Ref<GDScript> script; #ifdef DEBUG_ENABLED Map<StringName, int> member_indices_cache; //used only for hot script reloading @@ -311,17 +313,17 @@ class GDScriptLanguage : public ScriptLanguage { static GDScriptLanguage *singleton; - Variant *_global_array; + Variant *_global_array = nullptr; Vector<Variant> global_array; Map<StringName, int> globals; Map<StringName, Variant> named_globals; struct CallLevel { - Variant *stack; - GDScriptFunction *function; - GDScriptInstance *instance; - int *ip; - int *line; + Variant *stack = nullptr; + GDScriptFunction *function = nullptr; + GDScriptInstance *instance = nullptr; + int *ip = nullptr; + int *line = nullptr; }; int _debug_parse_err_line; @@ -329,7 +331,7 @@ class GDScriptLanguage : public ScriptLanguage { String _debug_error; int _debug_call_stack_pos; int _debug_max_call_stack; - CallLevel *_call_stack; + CallLevel *_call_stack = nullptr; void _add_global(const StringName &p_name, const Variant &p_value); @@ -510,7 +512,7 @@ public: class ResourceFormatLoaderGDScript : public ResourceFormatLoader { public: - virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, CacheMode p_cache_mode = CACHE_MODE_REUSE); + virtual Ref<Resource> load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, CacheMode p_cache_mode = CACHE_MODE_REUSE); virtual void get_recognized_extensions(List<String> *p_extensions) const; virtual bool handles_type(const String &p_type) const; virtual String get_resource_type(const String &p_path) const; @@ -519,9 +521,9 @@ public: class ResourceFormatSaverGDScript : public ResourceFormatSaver { public: - virtual Error save(const String &p_path, const RES &p_resource, uint32_t p_flags = 0); - virtual void get_recognized_extensions(const RES &p_resource, List<String> *p_extensions) const; - virtual bool recognize(const RES &p_resource) const; + virtual Error save(const String &p_path, const Ref<Resource> &p_resource, uint32_t p_flags = 0); + virtual void get_recognized_extensions(const Ref<Resource> &p_resource, List<String> *p_extensions) const; + virtual bool recognize(const Ref<Resource> &p_resource) const; }; #endif // GDSCRIPT_H diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp index 00931961b7..d346264933 100644 --- a/modules/gdscript/gdscript_analyzer.cpp +++ b/modules/gdscript/gdscript_analyzer.cpp @@ -2498,18 +2498,24 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a } if (is_self && parser->current_function != nullptr && parser->current_function->is_static && !is_static) { - push_error(vformat(R"*(Cannot call non-static function "%s()" from static function "%s()".)*", p_call->function_name, parser->current_function->identifier->name), p_call); + // Get the parent function above any lambda. + GDScriptParser::FunctionNode *parent_function = parser->current_function; + while (parent_function->source_lambda) { + parent_function = parent_function->source_lambda->parent_function; + } + push_error(vformat(R"*(Cannot call non-static function "%s()" from static function "%s()".)*", p_call->function_name, parent_function->identifier->name), p_call); } else if (!is_self && base_type.is_meta_type && !is_static) { base_type.is_meta_type = false; // For `to_string()`. push_error(vformat(R"*(Cannot call non-static function "%s()" on the class "%s" directly. Make an instance instead.)*", p_call->function_name, base_type.to_string()), p_call); - } else if (is_self && !is_static && !lambda_stack.is_empty()) { - push_error(vformat(R"*(Cannot call non-static function "%s()" from a lambda function.)*", p_call->function_name), p_call); + } else if (is_self && !is_static) { + mark_lambda_use_self(); } call_type = return_type; } else { - // Check if the name exists as something else. bool found = false; + + // Check if the name exists as something else. if (!p_call->is_super && callee_type != GDScriptParser::Node::NONE) { GDScriptParser::IdentifierNode *callee_id; if (callee_type == GDScriptParser::Node::IDENTIFIER) { @@ -2539,6 +2545,8 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a if (!found && (is_self || (base_type.is_hard_type() && base_type.kind == GDScriptParser::DataType::BUILTIN))) { String base_name = is_self && !p_call->is_super ? "self" : base_type.to_string(); push_error(vformat(R"*(Function "%s()" not found in base %s.)*", p_call->function_name, base_name), p_call->is_super ? p_call : p_call->callee); + } else if (!found && (!p_call->is_super && base_type.is_hard_type() && base_type.kind == GDScriptParser::DataType::NATIVE && base_type.is_meta_type)) { + push_error(vformat(R"*(Static function "%s()" not found in base "%s".)*", p_call->function_name, base_type.native_type.operator String()), p_call); } } @@ -2633,10 +2641,10 @@ void GDScriptAnalyzer::reduce_get_node(GDScriptParser::GetNodeNode *p_get_node) if (!ClassDB::is_parent_class(parser->current_class->base_type.native_type, result.native_type)) { push_error(R"*(Cannot use shorthand "get_node()" notation ("$") on a class that isn't a node.)*", p_get_node); - } else if (!lambda_stack.is_empty()) { - push_error(R"*(Cannot use shorthand "get_node()" notation ("$") inside a lambda. Use a captured variable instead.)*", p_get_node); } + mark_lambda_use_self(); + p_get_node->set_datatype(result); } @@ -2851,21 +2859,25 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod MethodBind *getter = ClassDB::get_method(native, getter_name); if (getter != nullptr) { p_identifier->set_datatype(type_from_property(getter->get_return_info())); + p_identifier->source = GDScriptParser::IdentifierNode::INHERITED_VARIABLE; } return; } if (ClassDB::get_method_info(native, name, &method_info)) { // Method is callable. p_identifier->set_datatype(make_callable_type(method_info)); + p_identifier->source = GDScriptParser::IdentifierNode::INHERITED_VARIABLE; return; } if (ClassDB::get_signal(native, name, &method_info)) { // Signal is a type too. p_identifier->set_datatype(make_signal_type(method_info)); + p_identifier->source = GDScriptParser::IdentifierNode::INHERITED_VARIABLE; return; } if (ClassDB::has_enum(native, name)) { p_identifier->set_datatype(make_native_enum_type(native, name)); + p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_CONSTANT; return; } bool valid = false; @@ -2874,6 +2886,7 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod p_identifier->is_constant = true; p_identifier->reduced_value = int_constant; p_identifier->set_datatype(type_from_variant(int_constant, p_identifier)); + p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_CONSTANT; return; } } @@ -2924,7 +2937,11 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident p_identifier->reduced_value = p_identifier->constant_source->initializer->reduced_value; found_source = true; break; + case GDScriptParser::IdentifierNode::INHERITED_VARIABLE: + mark_lambda_use_self(); + break; case GDScriptParser::IdentifierNode::MEMBER_VARIABLE: + mark_lambda_use_self(); p_identifier->variable_source->usages++; [[fallthrough]]; case GDScriptParser::IdentifierNode::LOCAL_VARIABLE: @@ -2955,18 +2972,37 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident } if (found_source) { - // If the identifier is local, check if it's any kind of capture by comparing their source function. - // Only capture locals and members and enum values. Constants are still accessible from the lambda using the script reference. - if (p_identifier->source == GDScriptParser::IdentifierNode::UNDEFINED_SOURCE || p_identifier->source == GDScriptParser::IdentifierNode::MEMBER_CONSTANT || lambda_stack.is_empty()) { - return; + if ((p_identifier->source == GDScriptParser::IdentifierNode::MEMBER_VARIABLE || p_identifier->source == GDScriptParser::IdentifierNode::INHERITED_VARIABLE) && parser->current_function && parser->current_function->is_static) { + // Get the parent function above any lambda. + GDScriptParser::FunctionNode *parent_function = parser->current_function; + while (parent_function->source_lambda) { + parent_function = parent_function->source_lambda->parent_function; + } + push_error(vformat(R"*(Cannot access instance variable "%s" from the static function "%s()".)*", p_identifier->name, parent_function->identifier->name), p_identifier); } - GDScriptParser::FunctionNode *function_test = lambda_stack.back()->get()->function; - while (function_test != nullptr && function_test != p_identifier->source_function && function_test->source_lambda != nullptr && !function_test->source_lambda->captures_indices.has(p_identifier->name)) { - function_test->source_lambda->captures_indices[p_identifier->name] = function_test->source_lambda->captures.size(); - function_test->source_lambda->captures.push_back(p_identifier); - function_test = function_test->source_lambda->parent_function; + if (!lambda_stack.is_empty()) { + // If the identifier is a member variable (including the native class properties), we consider the lambda to be using `self`, so we keep a reference to the current instance. + if (p_identifier->source == GDScriptParser::IdentifierNode::MEMBER_VARIABLE || p_identifier->source == GDScriptParser::IdentifierNode::INHERITED_VARIABLE) { + mark_lambda_use_self(); + return; // No need to capture. + } + // If the identifier is local, check if it's any kind of capture by comparing their source function. + // Only capture locals and enum values. Constants are still accessible from the lambda using the script reference. If not, this method is done. + if (p_identifier->source == GDScriptParser::IdentifierNode::UNDEFINED_SOURCE || p_identifier->source == GDScriptParser::IdentifierNode::MEMBER_CONSTANT) { + return; + } + + GDScriptParser::FunctionNode *function_test = lambda_stack.back()->get()->function; + // Make sure we aren't capturing variable in the same lambda. + // This also add captures for nested lambdas. + while (function_test != nullptr && function_test != p_identifier->source_function && function_test->source_lambda != nullptr && !function_test->source_lambda->captures_indices.has(p_identifier->name)) { + function_test->source_lambda->captures_indices[p_identifier->name] = function_test->source_lambda->captures.size(); + function_test->source_lambda->captures.push_back(p_identifier); + function_test = function_test->source_lambda->parent_function; + } } + return; } @@ -3146,6 +3182,7 @@ void GDScriptAnalyzer::reduce_preload(GDScriptParser::PreloadNode *p_preload) { void GDScriptAnalyzer::reduce_self(GDScriptParser::SelfNode *p_self) { p_self->is_constant = false; p_self->set_datatype(type_from_metatype(parser->current_class->get_datatype())); + mark_lambda_use_self(); } void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscript) { @@ -3156,6 +3193,12 @@ void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscri reduce_identifier(static_cast<GDScriptParser::IdentifierNode *>(p_subscript->base), true); } else { reduce_expression(p_subscript->base); + + if (p_subscript->base->type == GDScriptParser::Node::ARRAY) { + const_fold_array(static_cast<GDScriptParser::ArrayNode *>(p_subscript->base)); + } else if (p_subscript->base->type == GDScriptParser::Node::DICTIONARY) { + const_fold_dictionary(static_cast<GDScriptParser::DictionaryNode *>(p_subscript->base)); + } } GDScriptParser::DataType result_type; @@ -3473,6 +3516,13 @@ void GDScriptAnalyzer::const_fold_array(GDScriptParser::ArrayNode *p_array) { for (int i = 0; i < p_array->elements.size(); i++) { GDScriptParser::ExpressionNode *element = p_array->elements[i]; + + if (element->type == GDScriptParser::Node::ARRAY) { + const_fold_array(static_cast<GDScriptParser::ArrayNode *>(element)); + } else if (element->type == GDScriptParser::Node::DICTIONARY) { + const_fold_dictionary(static_cast<GDScriptParser::DictionaryNode *>(element)); + } + all_is_constant = all_is_constant && element->is_constant; if (!all_is_constant) { return; @@ -3493,6 +3543,13 @@ void GDScriptAnalyzer::const_fold_dictionary(GDScriptParser::DictionaryNode *p_d for (int i = 0; i < p_dictionary->elements.size(); i++) { const GDScriptParser::DictionaryNode::Pair &element = p_dictionary->elements[i]; + + if (element.value->type == GDScriptParser::Node::ARRAY) { + const_fold_array(static_cast<GDScriptParser::ArrayNode *>(element.value)); + } else if (element.value->type == GDScriptParser::Node::DICTIONARY) { + const_fold_dictionary(static_cast<GDScriptParser::DictionaryNode *>(element.value)); + } + all_is_constant = all_is_constant && element.key->is_constant && element.value->is_constant; if (!all_is_constant) { return; @@ -3773,6 +3830,7 @@ bool GDScriptAnalyzer::function_signature_from_info(const MethodInfo &p_info, GD r_return_type = type_from_property(p_info.return_val); r_default_arg_count = p_info.default_arguments.size(); r_vararg = (p_info.flags & METHOD_FLAG_VARARG) != 0; + r_static = (p_info.flags & METHOD_FLAG_STATIC) != 0; for (const PropertyInfo &E : p_info.arguments) { r_par_types.push_back(type_from_property(E)); @@ -4117,6 +4175,12 @@ void GDScriptAnalyzer::mark_node_unsafe(const GDScriptParser::Node *p_node) { #endif } +void GDScriptAnalyzer::mark_lambda_use_self() { + for (GDScriptParser::LambdaNode *lambda : lambda_stack) { + lambda->use_self = true; + } +} + bool GDScriptAnalyzer::class_exists(const StringName &p_class) const { return ClassDB::class_exists(p_class) && ClassDB::is_class_exposed(p_class); } diff --git a/modules/gdscript/gdscript_analyzer.h b/modules/gdscript/gdscript_analyzer.h index 7b8883e1d3..519e1975c4 100644 --- a/modules/gdscript/gdscript_analyzer.h +++ b/modules/gdscript/gdscript_analyzer.h @@ -42,7 +42,7 @@ class GDScriptAnalyzer { HashMap<String, Ref<GDScriptParserRef>> depended_parsers; const GDScriptParser::EnumNode *current_enum = nullptr; - List<const GDScriptParser::LambdaNode *> lambda_stack; + List<GDScriptParser::LambdaNode *> lambda_stack; // Tests for detecting invalid overloading of script members static _FORCE_INLINE_ bool has_member_name_conflict_in_script_class(const StringName &p_name, const GDScriptParser::ClassNode *p_current_class_node); @@ -115,6 +115,7 @@ class GDScriptAnalyzer { bool is_type_compatible(const GDScriptParser::DataType &p_target, const GDScriptParser::DataType &p_source, bool p_allow_implicit_conversion = false, const GDScriptParser::Node *p_source_node = nullptr); void push_error(const String &p_message, const GDScriptParser::Node *p_origin); void mark_node_unsafe(const GDScriptParser::Node *p_node); + void mark_lambda_use_self(); bool class_exists(const StringName &p_class) const; Ref<GDScriptParserRef> get_parser_for(const String &p_path); #ifdef DEBUG_ENABLED diff --git a/modules/gdscript/gdscript_byte_codegen.cpp b/modules/gdscript/gdscript_byte_codegen.cpp index 82aa14795e..e72069bcd5 100644 --- a/modules/gdscript/gdscript_byte_codegen.cpp +++ b/modules/gdscript/gdscript_byte_codegen.cpp @@ -468,7 +468,7 @@ void GDScriptByteCodeGenerator::write_type_adjust(const Address &p_target, Varia append(GDScriptFunction::OPCODE_TYPE_ADJUST_BASIS, 1); break; case Variant::TRANSFORM3D: - append(GDScriptFunction::OPCODE_TYPE_ADJUST_TRANSFORM, 1); + append(GDScriptFunction::OPCODE_TYPE_ADJUST_TRANSFORM3D, 1); break; case Variant::COLOR: append(GDScriptFunction::OPCODE_TYPE_ADJUST_COLOR, 1); @@ -1080,6 +1080,24 @@ void GDScriptByteCodeGenerator::write_call_builtin_type_static(const Address &p_ append(Variant::get_validated_builtin_method(p_type, p_method)); } +void GDScriptByteCodeGenerator::write_call_native_static(const Address &p_target, const StringName &p_class, const StringName &p_method, const Vector<Address> &p_arguments) { + bool is_validated = false; + + MethodBind *method = ClassDB::get_method(p_class, p_method); + + if (!is_validated) { + // Perform regular call. + append(GDScriptFunction::OPCODE_CALL_NATIVE_STATIC, p_arguments.size() + 1); + for (int i = 0; i < p_arguments.size(); i++) { + append(p_arguments[i]); + } + append(p_target); + append(method); + append(p_arguments.size()); + return; + } +} + void GDScriptByteCodeGenerator::write_call_method_bind(const Address &p_target, const Address &p_base, MethodBind *p_method, const Vector<Address> &p_arguments) { append(p_target.mode == Address::NIL ? GDScriptFunction::OPCODE_CALL_METHOD_BIND : GDScriptFunction::OPCODE_CALL_METHOD_BIND_RET, 2 + p_arguments.size()); for (int i = 0; i < p_arguments.size(); i++) { @@ -1193,8 +1211,8 @@ void GDScriptByteCodeGenerator::write_call_script_function(const Address &p_targ append(p_function_name); } -void GDScriptByteCodeGenerator::write_lambda(const Address &p_target, GDScriptFunction *p_function, const Vector<Address> &p_captures) { - append(GDScriptFunction::OPCODE_CREATE_LAMBDA, 1 + p_captures.size()); +void GDScriptByteCodeGenerator::write_lambda(const Address &p_target, GDScriptFunction *p_function, const Vector<Address> &p_captures, bool p_use_self) { + append(p_use_self ? GDScriptFunction::OPCODE_CREATE_SELF_LAMBDA : GDScriptFunction::OPCODE_CREATE_LAMBDA, 1 + p_captures.size()); for (int i = 0; i < p_captures.size(); i++) { append(p_captures[i]); } diff --git a/modules/gdscript/gdscript_byte_codegen.h b/modules/gdscript/gdscript_byte_codegen.h index db15dc55ef..0503f66161 100644 --- a/modules/gdscript/gdscript_byte_codegen.h +++ b/modules/gdscript/gdscript_byte_codegen.h @@ -464,12 +464,13 @@ public: virtual void write_call_gdscript_utility(const Address &p_target, GDScriptUtilityFunctions::FunctionPtr p_function, const Vector<Address> &p_arguments) override; virtual void write_call_builtin_type(const Address &p_target, const Address &p_base, Variant::Type p_type, const StringName &p_method, const Vector<Address> &p_arguments) override; virtual void write_call_builtin_type_static(const Address &p_target, Variant::Type p_type, const StringName &p_method, const Vector<Address> &p_arguments) override; + virtual void write_call_native_static(const Address &p_target, const StringName &p_class, const StringName &p_method, const Vector<Address> &p_arguments) override; virtual void write_call_method_bind(const Address &p_target, const Address &p_base, MethodBind *p_method, const Vector<Address> &p_arguments) override; virtual void write_call_ptrcall(const Address &p_target, const Address &p_base, MethodBind *p_method, const Vector<Address> &p_arguments) override; virtual void write_call_self(const Address &p_target, const StringName &p_function_name, const Vector<Address> &p_arguments) override; virtual void write_call_self_async(const Address &p_target, const StringName &p_function_name, const Vector<Address> &p_arguments) override; virtual void write_call_script_function(const Address &p_target, const Address &p_base, const StringName &p_function_name, const Vector<Address> &p_arguments) override; - virtual void write_lambda(const Address &p_target, GDScriptFunction *p_function, const Vector<Address> &p_captures) override; + virtual void write_lambda(const Address &p_target, GDScriptFunction *p_function, const Vector<Address> &p_captures, bool p_use_self) override; virtual void write_construct(const Address &p_target, Variant::Type p_type, const Vector<Address> &p_arguments) override; virtual void write_construct_array(const Address &p_target, const Vector<Address> &p_arguments) override; virtual void write_construct_typed_array(const Address &p_target, const GDScriptDataType &p_element_type, const Vector<Address> &p_arguments) override; diff --git a/modules/gdscript/gdscript_cache.cpp b/modules/gdscript/gdscript_cache.cpp index 6ada7d36f5..8c198345c2 100644 --- a/modules/gdscript/gdscript_cache.cpp +++ b/modules/gdscript/gdscript_cache.cpp @@ -145,7 +145,7 @@ Ref<GDScriptParserRef> GDScriptCache::get_parser(const String &p_path, GDScriptP String GDScriptCache::get_source_code(const String &p_path) { Vector<uint8_t> source_file; Error err; - FileAccessRef f = FileAccess::open(p_path, FileAccess::READ, &err); + Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ, &err); if (err) { ERR_FAIL_COND_V(err, ""); } @@ -153,7 +153,6 @@ String GDScriptCache::get_source_code(const String &p_path) { uint64_t len = f->get_length(); source_file.resize(len + 1); uint64_t r = f->get_buffer(source_file.ptrw(), len); - f->close(); ERR_FAIL_COND_V(r != len, ""); source_file.write[len] = 0; diff --git a/modules/gdscript/gdscript_codegen.h b/modules/gdscript/gdscript_codegen.h index 4542dd94ae..326b66a295 100644 --- a/modules/gdscript/gdscript_codegen.h +++ b/modules/gdscript/gdscript_codegen.h @@ -125,12 +125,13 @@ public: virtual void write_call_gdscript_utility(const Address &p_target, GDScriptUtilityFunctions::FunctionPtr p_function, const Vector<Address> &p_arguments) = 0; virtual void write_call_builtin_type(const Address &p_target, const Address &p_base, Variant::Type p_type, const StringName &p_method, const Vector<Address> &p_arguments) = 0; virtual void write_call_builtin_type_static(const Address &p_target, Variant::Type p_type, const StringName &p_method, const Vector<Address> &p_arguments) = 0; + virtual void write_call_native_static(const Address &p_target, const StringName &p_class, const StringName &p_method, const Vector<Address> &p_arguments) = 0; virtual void write_call_method_bind(const Address &p_target, const Address &p_base, MethodBind *p_method, const Vector<Address> &p_arguments) = 0; virtual void write_call_ptrcall(const Address &p_target, const Address &p_base, MethodBind *p_method, const Vector<Address> &p_arguments) = 0; virtual void write_call_self(const Address &p_target, const StringName &p_function_name, const Vector<Address> &p_arguments) = 0; virtual void write_call_self_async(const Address &p_target, const StringName &p_function_name, const Vector<Address> &p_arguments) = 0; virtual void write_call_script_function(const Address &p_target, const Address &p_base, const StringName &p_function_name, const Vector<Address> &p_arguments) = 0; - virtual void write_lambda(const Address &p_target, GDScriptFunction *p_function, const Vector<Address> &p_captures) = 0; + virtual void write_lambda(const Address &p_target, GDScriptFunction *p_function, const Vector<Address> &p_captures, bool p_use_self) = 0; virtual void write_construct(const Address &p_target, Variant::Type p_type, const Vector<Address> &p_arguments) = 0; virtual void write_construct_array(const Address &p_target, const Vector<Address> &p_arguments) = 0; virtual void write_construct_typed_array(const Address &p_target, const GDScriptDataType &p_element_type, const Vector<Address> &p_arguments) = 0; diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp index 8190eecbc7..37a988ee4c 100644 --- a/modules/gdscript/gdscript_compiler.cpp +++ b/modules/gdscript/gdscript_compiler.cpp @@ -35,6 +35,7 @@ #include "gdscript_cache.h" #include "gdscript_utility_functions.h" +#include "core/config/engine.h" #include "core/config/project_settings.h" bool GDScriptCompiler::_is_class_member_property(CodeGen &codegen, const StringName &p_name) { @@ -355,7 +356,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code class_node = class_node->outer; } - RES res; + Ref<Resource> res; if (class_node->identifier && class_node->identifier->name == identifier) { res = Ref<GDScript>(main_script); @@ -575,6 +576,10 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code // May be static built-in method call. if (!call->is_super && subscript->base->type == GDScriptParser::Node::IDENTIFIER && GDScriptParser::get_builtin_type(static_cast<GDScriptParser::IdentifierNode *>(subscript->base)->name) < Variant::VARIANT_MAX) { gen->write_call_builtin_type_static(result, GDScriptParser::get_builtin_type(static_cast<GDScriptParser::IdentifierNode *>(subscript->base)->name), subscript->attribute->name, arguments); + } else if (!call->is_super && subscript->base->type == GDScriptParser::Node::IDENTIFIER && call->function_name != SNAME("new") && + ClassDB::class_exists(static_cast<GDScriptParser::IdentifierNode *>(subscript->base)->name) && !Engine::get_singleton()->has_singleton(static_cast<GDScriptParser::IdentifierNode *>(subscript->base)->name)) { + // It's a static native method call. + gen->write_call_native_static(result, static_cast<GDScriptParser::IdentifierNode *>(subscript->base)->name, subscript->attribute->name, arguments); } else { GDScriptCodeGenerator::Address base = _parse_expression(codegen, r_error, subscript->base); if (r_error) { @@ -1192,7 +1197,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code return GDScriptCodeGenerator::Address(); } - gen->write_lambda(result, function, captures); + gen->write_lambda(result, function, captures, lambda->use_self); for (int i = 0; i < captures.size(); i++) { if (captures[i].mode == GDScriptCodeGenerator::Address::TEMPORARY) { diff --git a/modules/gdscript/gdscript_disassembler.cpp b/modules/gdscript/gdscript_disassembler.cpp index cc0be94a9e..dc114f2eff 100644 --- a/modules/gdscript/gdscript_disassembler.cpp +++ b/modules/gdscript/gdscript_disassembler.cpp @@ -564,6 +564,28 @@ void GDScriptFunction::disassemble(const Vector<String> &p_code_lines) const { incr += 5 + argc; } break; + case OPCODE_CALL_NATIVE_STATIC: { + MethodBind *method = _methods_ptr[_code_ptr[ip + 1 + instr_var_args]]; + int argc = _code_ptr[ip + 2 + instr_var_args]; + + text += "call native method static "; + text += DADDR(1 + argc); + text += " = "; + text += method->get_instance_class(); + text += "."; + text += method->get_name(); + text += "("; + + for (int i = 0; i < argc; i++) { + if (i > 0) { + text += ", "; + } + text += DADDR(1 + i); + } + text += ")"; + + incr += 4 + argc; + } break; case OPCODE_CALL_PTRCALL_NO_RETURN: { text += "call-ptrcall (no return) "; @@ -770,6 +792,25 @@ void GDScriptFunction::disassemble(const Vector<String> &p_code_lines) const { incr = 3 + captures_count; } break; + case OPCODE_CREATE_SELF_LAMBDA: { + int captures_count = _code_ptr[ip + 1 + instr_var_args]; + GDScriptFunction *lambda = _lambdas_ptr[_code_ptr[ip + 2 + instr_var_args]]; + + text += DADDR(1 + captures_count); + text += "create self lambda from "; + text += lambda->name.operator String(); + text += "function, captures ("; + + for (int i = 0; i < captures_count; i++) { + if (i > 0) { + text += ", "; + } + text += DADDR(1 + i); + } + text += ")"; + + incr = 3 + captures_count; + } break; case OPCODE_JUMP: { text += "jump "; text += itos(_code_ptr[ip + 1]); @@ -968,7 +1009,7 @@ void GDScriptFunction::disassemble(const Vector<String> &p_code_lines) const { DISASSEMBLE_TYPE_ADJUST(QUATERNION); DISASSEMBLE_TYPE_ADJUST(AABB); DISASSEMBLE_TYPE_ADJUST(BASIS); - DISASSEMBLE_TYPE_ADJUST(TRANSFORM); + DISASSEMBLE_TYPE_ADJUST(TRANSFORM3D); DISASSEMBLE_TYPE_ADJUST(COLOR); DISASSEMBLE_TYPE_ADJUST(STRING_NAME); DISASSEMBLE_TYPE_ADJUST(NODE_PATH); diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp index 7f0ffb4586..0197bf9ea3 100644 --- a/modules/gdscript/gdscript_editor.cpp +++ b/modules/gdscript/gdscript_editor.cpp @@ -777,7 +777,22 @@ static void _find_annotation_arguments(const GDScriptParser::AnnotationNode *p_a } } +static void _find_built_in_variants(Map<String, ScriptLanguage::CodeCompletionOption> &r_result, bool exclude_nil = false) { + for (int i = 0; i < Variant::VARIANT_MAX; i++) { + if (!exclude_nil && Variant::Type(i) == Variant::Type::NIL) { + ScriptLanguage::CodeCompletionOption option("null", ScriptLanguage::CODE_COMPLETION_KIND_CLASS); + r_result.insert(option.display, option); + } else { + ScriptLanguage::CodeCompletionOption option(Variant::get_type_name(Variant::Type(i)), ScriptLanguage::CODE_COMPLETION_KIND_CLASS); + r_result.insert(option.display, option); + } + } +} + static void _list_available_types(bool p_inherit_only, GDScriptParser::CompletionContext &p_context, Map<String, ScriptLanguage::CodeCompletionOption> &r_result) { + // Built-in Variant Types + _find_built_in_variants(r_result, true); + List<StringName> native_types; ClassDB::get_class_list(&native_types); for (const StringName &E : native_types) { @@ -957,7 +972,7 @@ static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base bool _static = base_type.is_meta_type; if (_static && base_type.kind != GDScriptParser::DataType::BUILTIN) { - ScriptLanguage::CodeCompletionOption option("new", ScriptLanguage::CODE_COMPLETION_KIND_FUNCTION); + ScriptLanguage::CodeCompletionOption option("new", ScriptLanguage::CODE_COMPLETION_KIND_FUNCTION, ScriptLanguage::LOCATION_LOCAL); option.insert_text += "("; r_result.insert(option.display, option); } @@ -1058,22 +1073,25 @@ static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base } } - if (!_static || Engine::get_singleton()->has_singleton(type)) { - List<MethodInfo> methods; - ClassDB::get_method_list(type, &methods, false, true); - for (const MethodInfo &E : methods) { - if (E.name.begins_with("_")) { - continue; - } - int location = p_recursion_depth + _get_method_location(type, E.name); - ScriptLanguage::CodeCompletionOption option(E.name, ScriptLanguage::CODE_COMPLETION_KIND_FUNCTION, location); - if (E.arguments.size()) { - option.insert_text += "("; - } else { - option.insert_text += "()"; - } - r_result.insert(option.display, option); + bool only_static = _static && !Engine::get_singleton()->has_singleton(type); + + List<MethodInfo> methods; + ClassDB::get_method_list(type, &methods, false, true); + for (const MethodInfo &E : methods) { + if (only_static && (E.flags & METHOD_FLAG_STATIC) == 0) { + continue; + } + if (E.name.begins_with("_")) { + continue; + } + int location = p_recursion_depth + _get_method_location(type, E.name); + ScriptLanguage::CodeCompletionOption option(E.name, ScriptLanguage::CODE_COMPLETION_KIND_FUNCTION, location); + if (E.arguments.size()) { + option.insert_text += "("; + } else { + option.insert_text += "()"; } + r_result.insert(option.display, option); } return; } break; @@ -1122,7 +1140,7 @@ static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base } } -static void _find_identifiers(GDScriptParser::CompletionContext &p_context, bool p_only_functions, Map<String, ScriptLanguage::CodeCompletionOption> &r_result, int p_recursion_depth) { +static void _find_identifiers(const GDScriptParser::CompletionContext &p_context, bool p_only_functions, Map<String, ScriptLanguage::CodeCompletionOption> &r_result, int p_recursion_depth) { if (!p_only_functions && p_context.current_suite) { // This includes function parameters, since they are also locals. _find_identifiers_in_suite(p_context.current_suite, r_result); @@ -1150,17 +1168,7 @@ static void _find_identifiers(GDScriptParser::CompletionContext &p_context, bool return; } - static const char *_type_names[Variant::VARIANT_MAX] = { - "null", "bool", "int", "float", "String", "StringName", "Vector2", "Vector2i", "Rect2", "Rect2i", "Vector3", "Vector3i", "Transform2D", "Plane", "Quaternion", "AABB", "Basis", "Transform3D", - "Color", "NodePath", "RID", "Signal", "Callable", "Object", "Dictionary", "Array", "PackedByteArray", "PackedInt32Array", "PackedInt64Array", "PackedFloat32Array", "PackedFloat64Array", "PackedStringArray", - "PackedVector2Array", "PackedVector3Array", "PackedColorArray" - }; - static_assert((sizeof(_type_names) / sizeof(*_type_names)) == Variant::VARIANT_MAX, "Completion for builtin types is incomplete"); - - for (int i = 0; i < Variant::VARIANT_MAX; i++) { - ScriptLanguage::CodeCompletionOption option(_type_names[i], ScriptLanguage::CODE_COMPLETION_KIND_CLASS); - r_result.insert(option.display, option); - } + _find_built_in_variants(r_result); static const char *_keywords[] = { "false", "PI", "TAU", "INF", "NAN", "self", "true", "breakpoint", "tool", "super", @@ -3098,7 +3106,7 @@ static Error _lookup_symbol_from_base(const GDScriptParser::DataType &p_base, co } Variant v; - REF v_ref; + Ref<RefCounted> v_ref; if (base_type.builtin_type == Variant::OBJECT) { v_ref.instantiate(); v = v_ref; diff --git a/modules/gdscript/gdscript_function.h b/modules/gdscript/gdscript_function.h index 3ee664c76d..ba0d51c5cc 100644 --- a/modules/gdscript/gdscript_function.h +++ b/modules/gdscript/gdscript_function.h @@ -259,6 +259,7 @@ public: OPCODE_CALL_METHOD_BIND, OPCODE_CALL_METHOD_BIND_RET, OPCODE_CALL_BUILTIN_STATIC, + OPCODE_CALL_NATIVE_STATIC, // ptrcall have one instruction per return type. OPCODE_CALL_PTRCALL_NO_RETURN, OPCODE_CALL_PTRCALL_BOOL, @@ -298,6 +299,7 @@ public: OPCODE_AWAIT, OPCODE_AWAIT_RESUME, OPCODE_CREATE_LAMBDA, + OPCODE_CREATE_SELF_LAMBDA, OPCODE_JUMP, OPCODE_JUMP_IF, OPCODE_JUMP_IF_NOT, @@ -364,7 +366,7 @@ public: OPCODE_TYPE_ADJUST_QUATERNION, OPCODE_TYPE_ADJUST_AABB, OPCODE_TYPE_ADJUST_BASIS, - OPCODE_TYPE_ADJUST_TRANSFORM, + OPCODE_TYPE_ADJUST_TRANSFORM3D, OPCODE_TYPE_ADJUST_COLOR, OPCODE_TYPE_ADJUST_STRING_NAME, OPCODE_TYPE_ADJUST_NODE_PATH, diff --git a/modules/gdscript/gdscript_lambda_callable.cpp b/modules/gdscript/gdscript_lambda_callable.cpp index baf93e1098..c43fa12c8c 100644 --- a/modules/gdscript/gdscript_lambda_callable.cpp +++ b/modules/gdscript/gdscript_lambda_callable.cpp @@ -93,3 +93,81 @@ GDScriptLambdaCallable::GDScriptLambdaCallable(Ref<GDScript> p_script, GDScriptF h = (uint32_t)hash_djb2_one_64((uint64_t)this); } + +bool GDScriptLambdaSelfCallable::compare_equal(const CallableCustom *p_a, const CallableCustom *p_b) { + // Lambda callables are only compared by reference. + return p_a == p_b; +} + +bool GDScriptLambdaSelfCallable::compare_less(const CallableCustom *p_a, const CallableCustom *p_b) { + // Lambda callables are only compared by reference. + return p_a < p_b; +} + +uint32_t GDScriptLambdaSelfCallable::hash() const { + return h; +} + +String GDScriptLambdaSelfCallable::get_as_text() const { + if (function->get_name() != StringName()) { + return function->get_name().operator String() + "(lambda)"; + } + return "(anonymous lambda)"; +} + +CallableCustom::CompareEqualFunc GDScriptLambdaSelfCallable::get_compare_equal_func() const { + return compare_equal; +} + +CallableCustom::CompareLessFunc GDScriptLambdaSelfCallable::get_compare_less_func() const { + return compare_less; +} + +ObjectID GDScriptLambdaSelfCallable::get_object() const { + return object->get_instance_id(); +} + +void GDScriptLambdaSelfCallable::call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const { +#ifdef DEBUG_ENABLED + if (object->get_script_instance() == nullptr || object->get_script_instance()->get_language() != GDScriptLanguage::get_singleton()) { + ERR_PRINT("Trying to call a lambda with an invalid instance."); + r_call_error.error = Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL; + return; + } +#endif + + int captures_amount = captures.size(); + + if (captures_amount > 0) { + Vector<const Variant *> args; + args.resize(p_argcount + captures_amount); + for (int i = 0; i < captures_amount; i++) { + args.write[i] = &captures[i]; + } + for (int i = 0; i < p_argcount; i++) { + args.write[i + captures_amount] = p_arguments[i]; + } + + r_return_value = function->call(static_cast<GDScriptInstance *>(object->get_script_instance()), args.ptrw(), args.size(), r_call_error); + r_call_error.argument -= captures_amount; + } else { + r_return_value = function->call(static_cast<GDScriptInstance *>(object->get_script_instance()), p_arguments, p_argcount, r_call_error); + } +} + +GDScriptLambdaSelfCallable::GDScriptLambdaSelfCallable(Ref<RefCounted> p_self, GDScriptFunction *p_function, const Vector<Variant> &p_captures) { + reference = p_self; + object = p_self.ptr(); + function = p_function; + captures = p_captures; + + h = (uint32_t)hash_djb2_one_64((uint64_t)this); +} + +GDScriptLambdaSelfCallable::GDScriptLambdaSelfCallable(Object *p_self, GDScriptFunction *p_function, const Vector<Variant> &p_captures) { + object = p_self; + function = p_function; + captures = p_captures; + + h = (uint32_t)hash_djb2_one_64((uint64_t)this); +} diff --git a/modules/gdscript/gdscript_lambda_callable.h b/modules/gdscript/gdscript_lambda_callable.h index f6a54a1a2f..248176e32c 100644 --- a/modules/gdscript/gdscript_lambda_callable.h +++ b/modules/gdscript/gdscript_lambda_callable.h @@ -62,4 +62,29 @@ public: virtual ~GDScriptLambdaCallable() = default; }; +// Lambda callable that references a particular object, so it can use `self` in the body. +class GDScriptLambdaSelfCallable : public CallableCustom { + GDScriptFunction *function = nullptr; + 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; + + Vector<Variant> captures; + + static bool compare_equal(const CallableCustom *p_a, const CallableCustom *p_b); + static bool compare_less(const CallableCustom *p_a, const CallableCustom *p_b); + +public: + uint32_t hash() const override; + String get_as_text() const override; + CompareEqualFunc get_compare_equal_func() const override; + CompareLessFunc get_compare_less_func() const override; + ObjectID get_object() const override; + void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const override; + + GDScriptLambdaSelfCallable(Ref<RefCounted> p_self, GDScriptFunction *p_function, const Vector<Variant> &p_captures); + GDScriptLambdaSelfCallable(Object *p_self, GDScriptFunction *p_function, const Vector<Variant> &p_captures); + virtual ~GDScriptLambdaSelfCallable() = default; +}; + #endif // GDSCRIPT_LAMBDA_CALLABLE diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp index 7ae58c4476..62330d23ed 100644 --- a/modules/gdscript/gdscript_parser.cpp +++ b/modules/gdscript/gdscript_parser.cpp @@ -2201,9 +2201,6 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_self(ExpressionNode *p_pre if (current_function && current_function->is_static) { push_error(R"(Cannot use "self" inside a static function.)"); } - if (in_lambda) { - push_error(R"(Cannot use "self" inside a lambda.)"); - } SelfNode *self = alloc_node<SelfNode>(); self->current_class = current_class; return self; diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h index 0da1896936..10474db02f 100644 --- a/modules/gdscript/gdscript_parser.h +++ b/modules/gdscript/gdscript_parser.h @@ -312,7 +312,7 @@ public: bool is_constant = false; Variant reduced_value; - virtual bool is_expression() const { return true; } + virtual bool is_expression() const override { return true; } virtual ~ExpressionNode() {} protected: @@ -767,6 +767,7 @@ public: LOCAL_BIND, // Pattern bind. MEMBER_VARIABLE, MEMBER_CONSTANT, + INHERITED_VARIABLE, }; Source source = UNDEFINED_SOURCE; @@ -800,6 +801,7 @@ public: FunctionNode *parent_function = nullptr; Vector<IdentifierNode *> captures; Map<StringName, int> captures_indices; + bool use_self = false; bool has_name() const { return function && function->identifier; diff --git a/modules/gdscript/gdscript_tokenizer.cpp b/modules/gdscript/gdscript_tokenizer.cpp index d3287ab345..63fad0d9bb 100644 --- a/modules/gdscript/gdscript_tokenizer.cpp +++ b/modules/gdscript/gdscript_tokenizer.cpp @@ -1493,7 +1493,7 @@ GDScriptTokenizer::Token GDScriptTokenizer::scan() { } default: - return make_error(vformat(R"(Unknown character "%s".")", String(&c, 1))); + return make_error(vformat(R"(Unknown character "%s".)", String(&c, 1))); } } diff --git a/modules/gdscript/gdscript_utility_functions.cpp b/modules/gdscript/gdscript_utility_functions.cpp index 16b2dac343..a914374985 100644 --- a/modules/gdscript/gdscript_utility_functions.cpp +++ b/modules/gdscript/gdscript_utility_functions.cpp @@ -432,21 +432,21 @@ struct GDScriptUtilityFunctionsDefinitions { } static inline void print_debug(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { - String str; + String s; for (int i = 0; i < p_arg_count; i++) { - str += p_args[i]->operator String(); + s += p_args[i]->operator String(); } if (Thread::get_caller_id() == Thread::get_main_id()) { ScriptLanguage *script = GDScriptLanguage::get_singleton(); if (script->debug_get_stack_level_count() > 0) { - str += "\n At: " + script->debug_get_stack_level_source(0) + ":" + itos(script->debug_get_stack_level_line(0)) + ":" + script->debug_get_stack_level_function(0) + "()"; + s += "\n At: " + script->debug_get_stack_level_source(0) + ":" + itos(script->debug_get_stack_level_line(0)) + ":" + script->debug_get_stack_level_function(0) + "()"; } } else { - str += "\n At: Cannot retrieve debug info outside the main thread. Thread ID: " + itos(Thread::get_caller_id()); + s += "\n At: Cannot retrieve debug info outside the main thread. Thread ID: " + itos(Thread::get_caller_id()); } - print_line(str); + print_line(s); *r_ret = Variant(); } @@ -545,7 +545,7 @@ struct GDScriptUtilityFunctionsDefinitions { }; struct GDScriptUtilityFunctionInfo { - GDScriptUtilityFunctions::FunctionPtr function; + GDScriptUtilityFunctions::FunctionPtr function = nullptr; MethodInfo info; bool is_constant = false; }; diff --git a/modules/gdscript/gdscript_vm.cpp b/modules/gdscript/gdscript_vm.cpp index 41c59c7703..e28dd26c28 100644 --- a/modules/gdscript/gdscript_vm.cpp +++ b/modules/gdscript/gdscript_vm.cpp @@ -93,9 +93,13 @@ static String _get_var_type(const Variant *p_var) { basestr = "null instance"; } } else { - basestr = bobj->get_class(); - if (bobj->get_script_instance()) { - basestr += " (" + _get_script_name(bobj->get_script_instance()->get_script()) + ")"; + if (bobj->is_class_ptr(GDScriptNativeClass::get_class_ptr_static())) { + basestr = Object::cast_to<GDScriptNativeClass>(bobj)->get_name(); + } else { + basestr = bobj->get_class(); + if (bobj->get_script_instance()) { + basestr += " (" + _get_script_name(bobj->get_script_instance()->get_script()) + ")"; + } } } @@ -263,6 +267,7 @@ void (*type_init_function_table[])(Variant *) = { &&OPCODE_CALL_METHOD_BIND, \ &&OPCODE_CALL_METHOD_BIND_RET, \ &&OPCODE_CALL_BUILTIN_STATIC, \ + &&OPCODE_CALL_NATIVE_STATIC, \ &&OPCODE_CALL_PTRCALL_NO_RETURN, \ &&OPCODE_CALL_PTRCALL_BOOL, \ &&OPCODE_CALL_PTRCALL_INT, \ @@ -301,6 +306,7 @@ void (*type_init_function_table[])(Variant *) = { &&OPCODE_AWAIT, \ &&OPCODE_AWAIT_RESUME, \ &&OPCODE_CREATE_LAMBDA, \ + &&OPCODE_CREATE_SELF_LAMBDA, \ &&OPCODE_JUMP, \ &&OPCODE_JUMP_IF, \ &&OPCODE_JUMP_IF_NOT, \ @@ -367,7 +373,7 @@ void (*type_init_function_table[])(Variant *) = { &&OPCODE_TYPE_ADJUST_QUATERNION, \ &&OPCODE_TYPE_ADJUST_AABB, \ &&OPCODE_TYPE_ADJUST_BASIS, \ - &&OPCODE_TYPE_ADJUST_TRANSFORM, \ + &&OPCODE_TYPE_ADJUST_TRANSFORM3D, \ &&OPCODE_TYPE_ADJUST_COLOR, \ &&OPCODE_TYPE_ADJUST_STRING_NAME, \ &&OPCODE_TYPE_ADJUST_NODE_PATH, \ @@ -1710,6 +1716,47 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a } DISPATCH_OPCODE; + OPCODE(OPCODE_CALL_NATIVE_STATIC) { + CHECK_SPACE(3 + instr_arg_count); + + ip += instr_arg_count; + + GD_ERR_BREAK(_code_ptr[ip + 1] < 0 || _code_ptr[ip + 1] >= _methods_count); + MethodBind *method = _methods_ptr[_code_ptr[ip + 1]]; + + int argc = _code_ptr[ip + 2]; + GD_ERR_BREAK(argc < 0); + + GET_INSTRUCTION_ARG(ret, argc); + + 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; + *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 (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; + } + DISPATCH_OPCODE; + #ifdef DEBUG_ENABLED #define OPCODE_CALL_PTR(m_type) \ OPCODE(OPCODE_CALL_PTRCALL_##m_type) { \ @@ -2231,6 +2278,41 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a } DISPATCH_OPCODE; + OPCODE(OPCODE_CREATE_SELF_LAMBDA) { + CHECK_SPACE(2 + instr_arg_count); + + GD_ERR_BREAK(p_instance == nullptr); + + ip += instr_arg_count; + + int captures_count = _code_ptr[ip + 1]; + GD_ERR_BREAK(captures_count < 0); + + int lambda_index = _code_ptr[ip + 2]; + GD_ERR_BREAK(lambda_index < 0 || lambda_index >= _lambdas_count); + GDScriptFunction *lambda = _lambdas_ptr[lambda_index]; + + Vector<Variant> captures; + captures.resize(captures_count); + for (int i = 0; i < captures_count; i++) { + GET_INSTRUCTION_ARG(arg, i); + captures.write[i] = *arg; + } + + GDScriptLambdaSelfCallable *callable; + if (Object::cast_to<RefCounted>(p_instance->owner)) { + callable = memnew(GDScriptLambdaSelfCallable(Ref<RefCounted>(Object::cast_to<RefCounted>(p_instance->owner)), lambda, captures)); + } else { + callable = memnew(GDScriptLambdaSelfCallable(p_instance->owner, lambda, captures)); + } + + GET_INSTRUCTION_ARG(result, captures_count); + *result = Callable(callable); + + ip += 3; + } + DISPATCH_OPCODE; + OPCODE(OPCODE_JUMP) { CHECK_SPACE(2); int to = _code_ptr[ip + 1]; @@ -3217,7 +3299,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a OPCODE_TYPE_ADJUST(QUATERNION, Quaternion); OPCODE_TYPE_ADJUST(AABB, AABB); OPCODE_TYPE_ADJUST(BASIS, Basis); - OPCODE_TYPE_ADJUST(TRANSFORM, Transform3D); + OPCODE_TYPE_ADJUST(TRANSFORM3D, Transform3D); OPCODE_TYPE_ADJUST(COLOR, Color); OPCODE_TYPE_ADJUST(STRING_NAME, StringName); OPCODE_TYPE_ADJUST(NODE_PATH, NodePath); diff --git a/modules/gdscript/language_server/gdscript_extend_parser.cpp b/modules/gdscript/language_server/gdscript_extend_parser.cpp index 17886181d5..5516f59fe9 100644 --- a/modules/gdscript/language_server/gdscript_extend_parser.cpp +++ b/modules/gdscript/language_server/gdscript_extend_parser.cpp @@ -108,7 +108,7 @@ void ExtendGDScriptParser::update_document_links(const String &p_code) { document_links.clear(); GDScriptTokenizer tokenizer; - FileAccessRef fs = FileAccess::create(FileAccess::ACCESS_RESOURCES); + Ref<FileAccess> fs = FileAccess::create(FileAccess::ACCESS_RESOURCES); tokenizer.set_source_code(p_code); while (true) { GDScriptTokenizer::Token token = tokenizer.scan(); @@ -212,7 +212,7 @@ void ExtendGDScriptParser::parse_class_symbol(const GDScriptParser::ClassNode *p const Variant &default_value = m.constant->initializer->reduced_value; String value_text; if (default_value.get_type() == Variant::OBJECT) { - RES res = default_value; + Ref<Resource> res = default_value; if (res.is_valid() && !res->get_path().is_empty()) { value_text = "preload(\"" + res->get_path() + "\")"; if (symbol.documentation.is_empty()) { diff --git a/modules/gdscript/language_server/gdscript_text_document.cpp b/modules/gdscript/language_server/gdscript_text_document.cpp index d5ef8fed74..c42bd58aeb 100644 --- a/modules/gdscript/language_server/gdscript_text_document.cpp +++ b/modules/gdscript/language_server/gdscript_text_document.cpp @@ -424,10 +424,6 @@ GDScriptTextDocument::GDScriptTextDocument() { file_checker = FileAccess::create(FileAccess::ACCESS_RESOURCES); } -GDScriptTextDocument::~GDScriptTextDocument() { - memdelete(file_checker); -} - void GDScriptTextDocument::sync_script_content(const String &p_path, const String &p_content) { String path = GDScriptLanguageProtocol::get_singleton()->get_workspace()->get_file_path(p_path); GDScriptLanguageProtocol::get_singleton()->get_workspace()->parse_script(path, p_content); diff --git a/modules/gdscript/language_server/gdscript_text_document.h b/modules/gdscript/language_server/gdscript_text_document.h index eb7b2c0240..9732765f34 100644 --- a/modules/gdscript/language_server/gdscript_text_document.h +++ b/modules/gdscript/language_server/gdscript_text_document.h @@ -40,7 +40,7 @@ class GDScriptTextDocument : public RefCounted { protected: static void _bind_methods(); - FileAccess *file_checker; + Ref<FileAccess> file_checker; void didOpen(const Variant &p_param); void didClose(const Variant &p_param); @@ -75,7 +75,6 @@ public: void initialize(); GDScriptTextDocument(); - virtual ~GDScriptTextDocument(); }; #endif diff --git a/modules/gdscript/language_server/gdscript_workspace.cpp b/modules/gdscript/language_server/gdscript_workspace.cpp index 1bf0b40842..89ee6b35e5 100644 --- a/modules/gdscript/language_server/gdscript_workspace.cpp +++ b/modules/gdscript/language_server/gdscript_workspace.cpp @@ -221,7 +221,7 @@ void GDScriptWorkspace::reload_all_workspace_scripts() { void GDScriptWorkspace::list_script_files(const String &p_root_dir, List<String> &r_files) { Error err; - DirAccessRef dir = DirAccess::open(p_root_dir, &err); + Ref<DirAccess> dir = DirAccess::open(p_root_dir, &err); if (OK == err) { dir->list_dir_begin(); String file_name = dir->get_next(); @@ -560,7 +560,7 @@ Node *GDScriptWorkspace::_get_owner_scene_node(String p_path) { for (int i = 0; i < owners.size(); i++) { NodePath owner_path = owners[i]; - RES owner_res = ResourceLoader::load(owner_path); + Ref<Resource> owner_res = ResourceLoader::load(owner_path); if (Object::cast_to<PackedScene>(owner_res.ptr())) { Ref<PackedScene> owner_packed_scene = Ref<PackedScene>(Object::cast_to<PackedScene>(*owner_res)); owner_scene_node = owner_packed_scene->instantiate(); diff --git a/modules/gdscript/register_types.cpp b/modules/gdscript/register_types.cpp index fcf122f567..59acb1c064 100644 --- a/modules/gdscript/register_types.cpp +++ b/modules/gdscript/register_types.cpp @@ -111,54 +111,62 @@ static void _editor_init() { #endif // TOOLS_ENABLED -void register_gdscript_types() { - GDREGISTER_CLASS(GDScript); +void initialize_gdscript_module(ModuleInitializationLevel p_level) { + if (p_level == MODULE_INITIALIZATION_LEVEL_SERVERS) { + GDREGISTER_CLASS(GDScript); - script_language_gd = memnew(GDScriptLanguage); - ScriptServer::register_language(script_language_gd); + script_language_gd = memnew(GDScriptLanguage); + ScriptServer::register_language(script_language_gd); - resource_loader_gd.instantiate(); - ResourceLoader::add_resource_format_loader(resource_loader_gd); + resource_loader_gd.instantiate(); + ResourceLoader::add_resource_format_loader(resource_loader_gd); - resource_saver_gd.instantiate(); - ResourceSaver::add_resource_format_saver(resource_saver_gd); + resource_saver_gd.instantiate(); + ResourceSaver::add_resource_format_saver(resource_saver_gd); - gdscript_cache = memnew(GDScriptCache); + gdscript_cache = memnew(GDScriptCache); + + GDScriptUtilityFunctions::register_functions(); + } #ifdef TOOLS_ENABLED - EditorNode::add_init_callback(_editor_init); + if (p_level == MODULE_INITIALIZATION_LEVEL_SERVERS) { + EditorNode::add_init_callback(_editor_init); - gdscript_translation_parser_plugin.instantiate(); - EditorTranslationParser::get_singleton()->add_parser(gdscript_translation_parser_plugin, EditorTranslationParser::STANDARD); + gdscript_translation_parser_plugin.instantiate(); + EditorTranslationParser::get_singleton()->add_parser(gdscript_translation_parser_plugin, EditorTranslationParser::STANDARD); + } #endif // TOOLS_ENABLED - - GDScriptUtilityFunctions::register_functions(); } -void unregister_gdscript_types() { - ScriptServer::unregister_language(script_language_gd); +void uninitialize_gdscript_module(ModuleInitializationLevel p_level) { + if (p_level == MODULE_INITIALIZATION_LEVEL_SERVERS) { + ScriptServer::unregister_language(script_language_gd); - if (gdscript_cache) { - memdelete(gdscript_cache); - } + if (gdscript_cache) { + memdelete(gdscript_cache); + } - if (script_language_gd) { - memdelete(script_language_gd); - } + if (script_language_gd) { + memdelete(script_language_gd); + } + + ResourceLoader::remove_resource_format_loader(resource_loader_gd); + resource_loader_gd.unref(); - ResourceLoader::remove_resource_format_loader(resource_loader_gd); - resource_loader_gd.unref(); + ResourceSaver::remove_resource_format_saver(resource_saver_gd); + resource_saver_gd.unref(); - ResourceSaver::remove_resource_format_saver(resource_saver_gd); - resource_saver_gd.unref(); + GDScriptParser::cleanup(); + GDScriptUtilityFunctions::unregister_functions(); + } #ifdef TOOLS_ENABLED - EditorTranslationParser::get_singleton()->remove_parser(gdscript_translation_parser_plugin, EditorTranslationParser::STANDARD); - gdscript_translation_parser_plugin.unref(); + if (p_level == MODULE_INITIALIZATION_LEVEL_EDITOR) { + EditorTranslationParser::get_singleton()->remove_parser(gdscript_translation_parser_plugin, EditorTranslationParser::STANDARD); + gdscript_translation_parser_plugin.unref(); + } #endif // TOOLS_ENABLED - - GDScriptParser::cleanup(); - GDScriptUtilityFunctions::unregister_functions(); } #ifdef TESTS_ENABLED diff --git a/modules/gdscript/register_types.h b/modules/gdscript/register_types.h index baa7dcbbd1..a7e6b02dcf 100644 --- a/modules/gdscript/register_types.h +++ b/modules/gdscript/register_types.h @@ -31,7 +31,9 @@ #ifndef GDSCRIPT_REGISTER_TYPES_H #define GDSCRIPT_REGISTER_TYPES_H -void register_gdscript_types(); -void unregister_gdscript_types(); +#include "modules/register_module_types.h" + +void initialize_gdscript_module(ModuleInitializationLevel p_level); +void uninitialize_gdscript_module(ModuleInitializationLevel p_level); #endif // GDSCRIPT_REGISTER_TYPES_H diff --git a/modules/gdscript/tests/gdscript_test_runner.cpp b/modules/gdscript/tests/gdscript_test_runner.cpp index e8ddf90836..ea51990237 100644 --- a/modules/gdscript/tests/gdscript_test_runner.cpp +++ b/modules/gdscript/tests/gdscript_test_runner.cpp @@ -70,7 +70,7 @@ void init_autoloads() { continue; } - RES res = ResourceLoader::load(info.path); + Ref<Resource> res = ResourceLoader::load(info.path); ERR_CONTINUE_MSG(res.is_null(), "Can't autoload: " + info.path); Node *n = nullptr; Ref<PackedScene> scn = res; @@ -80,7 +80,7 @@ void init_autoloads() { } else if (script.is_valid()) { StringName ibt = script->get_instance_base_type(); bool valid_type = ClassDB::is_parent_class(ibt, "Node"); - ERR_CONTINUE_MSG(!valid_type, "Script does not inherit a Node: " + info.path); + ERR_CONTINUE_MSG(!valid_type, "Script does not inherit from Node: " + info.path); Object *obj = ClassDB::instantiate(ibt); @@ -229,7 +229,7 @@ bool GDScriptTestRunner::generate_outputs() { bool GDScriptTestRunner::make_tests_for_dir(const String &p_dir) { Error err = OK; - DirAccessRef dir(DirAccess::open(p_dir, &err)); + Ref<DirAccess> dir(DirAccess::open(p_dir, &err)); if (err != OK) { return false; @@ -254,7 +254,7 @@ bool GDScriptTestRunner::make_tests_for_dir(const String &p_dir) { #ifndef DEBUG_ENABLED // On release builds, skip tests marked as debug only. Error open_err = OK; - FileAccessRef script_file(FileAccess::open(current_dir.plus_file(next), FileAccess::READ, &open_err)); + Ref<FileAccess> script_file(FileAccess::open(current_dir.plus_file(next), FileAccess::READ, &open_err)); if (open_err != OK) { ERR_PRINT(vformat(R"(Couldn't open test file "%s".)", next)); next = dir->get_next(); @@ -286,7 +286,7 @@ bool GDScriptTestRunner::make_tests_for_dir(const String &p_dir) { bool GDScriptTestRunner::make_tests() { Error err = OK; - DirAccessRef dir(DirAccess::open(source_dir, &err)); + Ref<DirAccess> dir(DirAccess::open(source_dir, &err)); ERR_FAIL_COND_V_MSG(err != OK, false, "Could not open specified test directory."); @@ -611,7 +611,7 @@ bool GDScriptTest::generate_output() { } Error err = OK; - FileAccessRef out_file = FileAccess::open(output_file, FileAccess::WRITE, &err); + Ref<FileAccess> out_file = FileAccess::open(output_file, FileAccess::WRITE, &err); if (err != OK) { return false; } @@ -620,7 +620,6 @@ bool GDScriptTest::generate_output() { output += "\n"; // Make sure to insert newline for CI static checks. out_file->store_string(output); - out_file->close(); return true; } diff --git a/modules/gdscript/tests/gdscript_test_runner.h b/modules/gdscript/tests/gdscript_test_runner.h index 1a950c6898..d6c6419e21 100644 --- a/modules/gdscript/tests/gdscript_test_runner.h +++ b/modules/gdscript/tests/gdscript_test_runner.h @@ -63,8 +63,8 @@ public: private: struct ErrorHandlerData { - TestResult *result; - GDScriptTest *self; + TestResult *result = nullptr; + GDScriptTest *self = nullptr; ErrorHandlerData(TestResult *p_result, GDScriptTest *p_this) { result = p_result; self = p_this; diff --git a/modules/gdscript/tests/scripts/analyzer/errors/invalid_array_index.out b/modules/gdscript/tests/scripts/analyzer/errors/invalid_array_index.out index 015ad756f8..6f7f0783f0 100644 --- a/modules/gdscript/tests/scripts/analyzer/errors/invalid_array_index.out +++ b/modules/gdscript/tests/scripts/analyzer/errors/invalid_array_index.out @@ -1,2 +1,2 @@ GDTEST_ANALYZER_ERROR -Invalid index type "bool" for a base of type "Array". +Cannot get index "true" from "[0, 1]". diff --git a/modules/gdscript/tests/scripts/analyzer/features/function_match_parent_signature_with_default_dict_void.gd b/modules/gdscript/tests/scripts/analyzer/features/function_match_parent_signature_with_default_dict_void.gd new file mode 100644 index 0000000000..631e7be5ce --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/function_match_parent_signature_with_default_dict_void.gd @@ -0,0 +1,14 @@ +func test(): + var instance := Parent.new() + instance.my_function({"a": 1}) + instance = Child.new() + instance.my_function({"a": 1}) + print("No failure") + +class Parent: + func my_function(_par1: Dictionary = {}) -> void: + pass + +class Child extends Parent: + func my_function(_par1: Dictionary = {}) -> void: + pass diff --git a/modules/gdscript/tests/scripts/analyzer/features/function_match_parent_signature_with_default_dict_void.out b/modules/gdscript/tests/scripts/analyzer/features/function_match_parent_signature_with_default_dict_void.out new file mode 100644 index 0000000000..67f0045867 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/function_match_parent_signature_with_default_dict_void.out @@ -0,0 +1,2 @@ +GDTEST_OK +No failure diff --git a/modules/gdscript/tests/scripts/parser/features/advanced_expression_matching.out b/modules/gdscript/tests/scripts/parser/features/advanced_expression_matching.out index 67c7e28046..3cdafb04a9 100644 --- a/modules/gdscript/tests/scripts/parser/features/advanced_expression_matching.out +++ b/modules/gdscript/tests/scripts/parser/features/advanced_expression_matching.out @@ -10,5 +10,5 @@ wildcard [1,2,[1,{1:2,2:var z,..}]] 3 [1,2,[1,{1:2,2:var z,..}]] -[1, 3, 5, 123] +[1, 3, 5, "123"] wildcard diff --git a/modules/gdscript/tests/scripts/parser/features/arrays_dictionaries_nested_const.gd b/modules/gdscript/tests/scripts/parser/features/arrays_dictionaries_nested_const.gd new file mode 100644 index 0000000000..cc78309ae4 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/features/arrays_dictionaries_nested_const.gd @@ -0,0 +1,58 @@ +# https://github.com/godotengine/godot/issues/50285 + +@warning_ignore(unused_local_constant) +func test(): + const CONST_INNER_DICTIONARY = { "key": true } + const CONST_NESTED_DICTIONARY_OLD_WORKAROUND = { + "key1": "value1", + "key2": CONST_INNER_DICTIONARY + } + # All of these should be valid + const CONST_NESTED_DICTIONARY = { + "key1": "value1", + "key2": { "key": true } + } + + + const CONST_DICTIONARY_WITH_ARRAY = { + "key1": [1,2,3,4] + } + + const CONST_NESTED_ARRAY = [[],[2],[1,2,3]] + const CONST_ARRAY_WITH_DICT = [{"key1": 3}, {"key2": 5}] + + const THREE_DIMENSIONAL_ARRAY = [[[],[],[]],[[],[],[]],[[],[],[]]] + const MANY_NESTED_DICT = { + "key1": { + "key11": { + "key111": {}, + "key112": {}, + }, + "key12": { + "key121": {}, + "key122": {}, + }, + }, + "key2": { + "key21": { + "key211": {}, + "key212": {}, + }, + "key22": { + "key221": {}, + "key222": {}, + }, + } + } + + + const CONST_ARRAY_ACCESS = [1,2,3][0] + const CONST_DICT_ACCESS = {"key1": 5}["key1"] + + const CONST_ARRAY_NESTED_ACCESS = [[1,2,3],[4,5,6],[8,9,10]][0][1] + const CONST_DICT_NESTED_ACCESS = {"key1": {"key2": 1}}["key1"]["key2"] + + print(CONST_ARRAY_ACCESS) + print(CONST_DICT_ACCESS) + print(CONST_ARRAY_NESTED_ACCESS) + print(CONST_DICT_NESTED_ACCESS) diff --git a/modules/gdscript/tests/scripts/parser/features/arrays_dictionaries_nested_const.out b/modules/gdscript/tests/scripts/parser/features/arrays_dictionaries_nested_const.out new file mode 100644 index 0000000000..5883fc5c18 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/features/arrays_dictionaries_nested_const.out @@ -0,0 +1,5 @@ +GDTEST_OK +1 +5 +2 +1 diff --git a/modules/gdscript/tests/scripts/parser/features/dictionary.out b/modules/gdscript/tests/scripts/parser/features/dictionary.out index 54083c1afc..5f999f573a 100644 --- a/modules/gdscript/tests/scripts/parser/features/dictionary.out +++ b/modules/gdscript/tests/scripts/parser/features/dictionary.out @@ -7,8 +7,8 @@ null false empty array zero Vector2i -{22:{4:[nesting, arrays]}} -{4:[nesting, arrays]} -[nesting, arrays] +{22:{4:["nesting", "arrays"]}} +{4:["nesting", "arrays"]} +["nesting", "arrays"] nesting arrays diff --git a/modules/gdscript/tests/scripts/parser/features/dictionary_lua_style.out b/modules/gdscript/tests/scripts/parser/features/dictionary_lua_style.out index 5b0ea9df43..5143d040a9 100644 --- a/modules/gdscript/tests/scripts/parser/features/dictionary_lua_style.out +++ b/modules/gdscript/tests/scripts/parser/features/dictionary_lua_style.out @@ -1,2 +1,2 @@ GDTEST_OK -{a:1, b:2, with spaces:3, 2:4} +{"a":1, "b":2, "with spaces":3, "2":4} diff --git a/modules/gdscript/tests/scripts/parser/features/dictionary_mixed_syntax.out b/modules/gdscript/tests/scripts/parser/features/dictionary_mixed_syntax.out index 62be807a1f..dd28609850 100644 --- a/modules/gdscript/tests/scripts/parser/features/dictionary_mixed_syntax.out +++ b/modules/gdscript/tests/scripts/parser/features/dictionary_mixed_syntax.out @@ -1,2 +1,2 @@ GDTEST_OK -{hello:{world:{is:beautiful}}} +{"hello":{"world":{"is":"beautiful"}}} diff --git a/modules/gdscript/tests/scripts/parser/features/nested_dictionary.out b/modules/gdscript/tests/scripts/parser/features/nested_dictionary.out index 4009160439..8b8c33202f 100644 --- a/modules/gdscript/tests/scripts/parser/features/nested_dictionary.out +++ b/modules/gdscript/tests/scripts/parser/features/nested_dictionary.out @@ -1,5 +1,5 @@ GDTEST_OK -{8:{key:value}} -{key:value} +{8:{"key":"value"}} +{"key":"value"} value value diff --git a/modules/gdscript/tests/scripts/runtime/features/lambda_use_self.gd b/modules/gdscript/tests/scripts/runtime/features/lambda_use_self.gd new file mode 100644 index 0000000000..3d063869ab --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/features/lambda_use_self.gd @@ -0,0 +1,23 @@ +var member = "foo" + +func bar(): + print("bar") + +func test(): + var lambda1 = func(): + print(member) + lambda1.call() + + var lambda2 = func(): + var nested = func(): + print(member) + nested.call() + lambda2.call() + + var lambda3 = func(): + bar() + lambda3.call() + + var lambda4 = func(): + return self + print(lambda4.call() == self) diff --git a/modules/gdscript/tests/scripts/runtime/features/lambda_use_self.out b/modules/gdscript/tests/scripts/runtime/features/lambda_use_self.out new file mode 100644 index 0000000000..53d602b234 --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/features/lambda_use_self.out @@ -0,0 +1,5 @@ +GDTEST_OK +foo +foo +bar +true diff --git a/modules/gdscript/tests/scripts/runtime/features/stringify.out b/modules/gdscript/tests/scripts/runtime/features/stringify.out index 7670fc0128..d4468737a5 100644 --- a/modules/gdscript/tests/scripts/runtime/features/stringify.out +++ b/modules/gdscript/tests/scripts/runtime/features/stringify.out @@ -21,14 +21,14 @@ hello/world RID(0) Node::get_name Node::[signal]property_list_changed -{hello:123} -[hello, 123] +{"hello":123} +["hello", 123] [255, 0, 1] [-1, 0, 1] [-1, 0, 1] [-1, 0, 1] [-1, 0, 1] -[hello, world] +["hello", "world"] [(1, 1), (0, 0)] [(1, 1, 1), (0, 0, 0)] [(1, 0, 0, 1), (0, 0, 1, 1), (0, 1, 0, 1)] diff --git a/modules/gdscript/tests/test_gdscript.cpp b/modules/gdscript/tests/test_gdscript.cpp index 4255030b4e..d8f60d5e9b 100644 --- a/modules/gdscript/tests/test_gdscript.cpp +++ b/modules/gdscript/tests/test_gdscript.cpp @@ -204,8 +204,8 @@ void test(TestType p_type) { return; } - FileAccessRef fa = FileAccess::open(test, FileAccess::READ); - ERR_FAIL_COND_MSG(!fa, "Could not open file: " + test); + Ref<FileAccess> fa = FileAccess::open(test, FileAccess::READ); + ERR_FAIL_COND_MSG(fa.is_null(), "Could not open file: " + test); // Initialize the language for the test routine. init_language(fa->get_path_absolute().get_base_dir()); |