diff options
21 files changed, 257 insertions, 141 deletions
diff --git a/.github/workflows/macos_builds.yml b/.github/workflows/macos_builds.yml index 04bdf971ca..70031ec4c3 100644 --- a/.github/workflows/macos_builds.yml +++ b/.github/workflows/macos_builds.yml @@ -14,7 +14,7 @@ concurrency: jobs: build-macos: - runs-on: "macos-12" + runs-on: "macos-latest" name: ${{ matrix.name }} strategy: fail-fast: false @@ -24,7 +24,7 @@ jobs: cache-name: macos-editor target: editor tests: true - bin: "./bin/godot.macos.editor.x86_64" + bin: "./bin/godot.macos.editor.universal" - name: Template (target=template_release) cache-name: macos-template @@ -50,16 +50,26 @@ jobs: run: | sh misc/scripts/install_vulkan_sdk_macos.sh - - name: Compilation + - name: Compilation (x86_64) uses: ./.github/actions/godot-build with: - sconsflags: ${{ env.SCONSFLAGS }} + sconsflags: ${{ env.SCONSFLAGS }} arch=x86_64 + platform: macos + target: ${{ matrix.target }} + tests: ${{ matrix.tests }} + + - name: Compilation (arm64) + uses: ./.github/actions/godot-build + with: + sconsflags: ${{ env.SCONSFLAGS }} arch=arm64 platform: macos target: ${{ matrix.target }} tests: ${{ matrix.tests }} - name: Prepare artifact run: | + lipo -create ./bin/godot.macos.${{ matrix.target }}.x86_64 ./bin/godot.macos.${{ matrix.target }}.arm64 -output ./bin/godot.macos.${{ matrix.target }}.universal + rm ./bin/godot.macos.${{ matrix.target }}.x86_64 ./bin/godot.macos.${{ matrix.target }}.arm64 strip bin/godot.* chmod +x bin/godot.* diff --git a/core/input/gamecontrollerdb.txt b/core/input/gamecontrollerdb.txt index a17e15a62c..7150911e75 100644 --- a/core/input/gamecontrollerdb.txt +++ b/core/input/gamecontrollerdb.txt @@ -917,6 +917,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 030000000d0f00003801000008010000,Hori PC Engine Mini Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,start:b9,platform:Mac OS X, 030000000d0f00009200000000010000,Hori Pokken Tournament DX Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X, 030000000d0f0000aa00000072050000,Hori Real Arcade Pro for Nintendo Switch,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Mac OS X, +030000000d0f00000002000017010000,Hori Split Pad Fit,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, 030000000d0f00000002000015010000,Hori Switch Split Pad Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, 030000000d0f00006e00000000010000,Horipad 4 PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, 030000000d0f00006600000000010000,Horipad 4 PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, @@ -1282,6 +1283,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 030000000d0f00006b00000011010000,Hori Real Arcade Pro 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 030000000d0f00001600000000010000,Hori Real Arcade Pro EXSE,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b2,y:b3,platform:Linux, 030000000d0f0000aa00000011010000,Hori Real Arcade Pro for Nintendo Switch,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux, +030000000d0f00008501000017010000,Hori Split Pad Fit,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000000d0f00008501000015010000,Hori Switch Split Pad Pro,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000000d0f00006e00000011010000,Horipad 4 PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 030000000d0f00006600000011010000,Horipad 4 PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, @@ -1696,6 +1698,8 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 060000005e040000120b00000f050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 050000005e040000130b000017050000,Xbox Series X Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 060000005e040000120b00000d050000,Xbox Series X Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000005e040000120b000011050000,Xbox Series X Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000005e040000120b000014050000,Xbox Series X Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 050000005e040000200b000013050000,Xbox Wireless Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 050000005e040000200b000017050000,Xbox Wireless Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 050000005e040000220b000017050000,Xbox Wireless Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, diff --git a/core/io/logger.cpp b/core/io/logger.cpp index 441df7f5d1..1476b8ccac 100644 --- a/core/io/logger.cpp +++ b/core/io/logger.cpp @@ -37,6 +37,8 @@ #include "core/os/time.h" #include "core/string/print_string.h" +#include "modules/modules_enabled.gen.h" // For regex. + #if defined(MINGW_ENABLED) || defined(_MSC_VER) #define sprintf sprintf_s #endif @@ -180,6 +182,12 @@ RotatedFileLogger::RotatedFileLogger(const String &p_base_path, int p_max_files) base_path(p_base_path.simplify_path()), max_files(p_max_files > 0 ? p_max_files : 1) { rotate_file(); + +#ifdef MODULE_REGEX_ENABLED + strip_ansi_regex.instantiate(); + strip_ansi_regex->detach_from_objectdb(); // Note: This RegEx instance will exist longer than ObjectDB, therefore can't be registered in ObjectDB. + strip_ansi_regex->compile("\u001b\\[((?:\\d|;)*)([a-zA-Z])"); +#endif // MODULE_REGEX_ENABLED } void RotatedFileLogger::logv(const char *p_format, va_list p_list, bool p_err) { @@ -199,7 +207,15 @@ void RotatedFileLogger::logv(const char *p_format, va_list p_list, bool p_err) { vsnprintf(buf, len + 1, p_format, list_copy); } va_end(list_copy); + +#ifdef MODULE_REGEX_ENABLED + // Strip ANSI escape codes (such as those inserted by `print_rich()`) + // before writing to file, as text editors cannot display those + // correctly. + file->store_string(strip_ansi_regex->sub(String(buf), "", true)); +#else file->store_buffer((uint8_t *)buf, len); +#endif // MODULE_REGEX_ENABLED if (len >= static_buf_size) { Memory::free_static(buf); diff --git a/core/io/logger.h b/core/io/logger.h index 3cd18965c5..85ef3031ec 100644 --- a/core/io/logger.h +++ b/core/io/logger.h @@ -34,6 +34,10 @@ #include "core/io/file_access.h" #include "core/string/ustring.h" #include "core/templates/vector.h" +#include "modules/modules_enabled.gen.h" // For regex. +#ifdef MODULE_REGEX_ENABLED +#include "modules/regex/regex.h" +#endif // MODULE_REGEX_ENABLED #include <stdarg.h> @@ -86,6 +90,10 @@ class RotatedFileLogger : public Logger { void clear_old_backups(); void rotate_file(); +#ifdef MODULE_REGEX_ENABLED + Ref<RegEx> strip_ansi_regex; +#endif // MODULE_REGEX_ENABLED + public: explicit RotatedFileLogger(const String &p_base_path, int p_max_files = 10); diff --git a/core/io/resource_loader.cpp b/core/io/resource_loader.cpp index eea6357084..c3c37aa89d 100644 --- a/core/io/resource_loader.cpp +++ b/core/io/resource_loader.cpp @@ -242,16 +242,20 @@ ResourceLoader::LoadToken::~LoadToken() { } Ref<Resource> ResourceLoader::_load(const String &p_path, const String &p_original_path, const String &p_type_hint, ResourceFormatLoader::CacheMode p_cache_mode, Error *r_error, bool p_use_sub_threads, float *r_progress) { + const String &original_path = p_original_path.is_empty() ? p_path : p_original_path; load_nesting++; if (load_paths_stack->size()) { thread_load_mutex.lock(); - HashMap<String, ThreadLoadTask>::Iterator E = thread_load_tasks.find(load_paths_stack->get(load_paths_stack->size() - 1)); - if (E) { + const String &parent_task_path = load_paths_stack->get(load_paths_stack->size() - 1); + HashMap<String, ThreadLoadTask>::Iterator E = thread_load_tasks.find(parent_task_path); + // Avoid double-tracking, for progress reporting, resources that boil down to a remapped path containing the real payload (e.g., imported resources). + bool is_remapped_load = original_path == parent_task_path; + if (E && !is_remapped_load) { E->value.sub_tasks.insert(p_original_path); } thread_load_mutex.unlock(); } - load_paths_stack->push_back(p_original_path); + load_paths_stack->push_back(original_path); // Try all loaders and pick the first match for the type hint bool found = false; @@ -261,7 +265,7 @@ Ref<Resource> ResourceLoader::_load(const String &p_path, const String &p_origin continue; } found = true; - res = loader[i]->load(p_path, !p_original_path.is_empty() ? p_original_path : p_path, r_error, p_use_sub_threads, r_progress, p_cache_mode); + res = loader[i]->load(p_path, original_path, r_error, p_use_sub_threads, r_progress, p_cache_mode); if (!res.is_null()) { break; } diff --git a/core/object/class_db.h b/core/object/class_db.h index adb525cbe8..37a864c109 100644 --- a/core/object/class_db.h +++ b/core/object/class_db.h @@ -195,7 +195,7 @@ public: template <typename T> static void register_class(bool p_virtual = false) { GLOBAL_LOCK_FUNCTION; - static_assert(types_are_same_v<typename T::self_type, T>, "Class not declared properly, please use GDCLASS."); + static_assert(std::is_same_v<typename T::self_type, T>, "Class not declared properly, please use GDCLASS."); T::initialize_class(); ClassInfo *t = classes.getptr(T::get_class_static()); ERR_FAIL_NULL(t); @@ -210,7 +210,7 @@ public: template <typename T> static void register_abstract_class() { GLOBAL_LOCK_FUNCTION; - static_assert(types_are_same_v<typename T::self_type, T>, "Class not declared properly, please use GDCLASS."); + static_assert(std::is_same_v<typename T::self_type, T>, "Class not declared properly, please use GDCLASS."); T::initialize_class(); ClassInfo *t = classes.getptr(T::get_class_static()); ERR_FAIL_NULL(t); @@ -223,7 +223,7 @@ public: template <typename T> static void register_internal_class() { GLOBAL_LOCK_FUNCTION; - static_assert(types_are_same_v<typename T::self_type, T>, "Class not declared properly, please use GDCLASS."); + static_assert(std::is_same_v<typename T::self_type, T>, "Class not declared properly, please use GDCLASS."); T::initialize_class(); ClassInfo *t = classes.getptr(T::get_class_static()); ERR_FAIL_NULL(t); @@ -238,7 +238,7 @@ public: template <typename T> static void register_runtime_class() { GLOBAL_LOCK_FUNCTION; - static_assert(types_are_same_v<typename T::self_type, T>, "Class not declared properly, please use GDCLASS."); + static_assert(std::is_same_v<typename T::self_type, T>, "Class not declared properly, please use GDCLASS."); T::initialize_class(); ClassInfo *t = classes.getptr(T::get_class_static()); ERR_FAIL_NULL(t); @@ -263,7 +263,7 @@ public: template <typename T> static void register_custom_instance_class() { GLOBAL_LOCK_FUNCTION; - static_assert(types_are_same_v<typename T::self_type, T>, "Class not declared properly, please use GDCLASS."); + static_assert(std::is_same_v<typename T::self_type, T>, "Class not declared properly, please use GDCLASS."); T::initialize_class(); ClassInfo *t = classes.getptr(T::get_class_static()); ERR_FAIL_NULL(t); diff --git a/core/variant/type_info.h b/core/variant/type_info.h index 9c52db3345..15cb6c9c1a 100644 --- a/core/variant/type_info.h +++ b/core/variant/type_info.h @@ -33,31 +33,7 @@ #include "core/typedefs.h" -template <bool C, typename T = void> -struct EnableIf { - typedef T type; -}; - -template <typename T> -struct EnableIf<false, T> { -}; - -template <typename, typename> -inline constexpr bool types_are_same_v = false; - -template <typename T> -inline constexpr bool types_are_same_v<T, T> = true; - -template <typename B, typename D> -struct TypeInherits { - static D *get_d(); - - static char (&test(B *))[1]; - static char (&test(...))[2]; - - static bool const value = sizeof(test(get_d())) == sizeof(char) && - !types_are_same_v<B volatile const, void volatile const>; -}; +#include <type_traits> namespace GodotTypeInfo { enum Metadata { @@ -223,16 +199,7 @@ MAKE_TEMPLATE_TYPE_INFO(Vector, Face3, Variant::PACKED_VECTOR3_ARRAY) MAKE_TEMPLATE_TYPE_INFO(Vector, StringName, Variant::PACKED_STRING_ARRAY) template <typename T> -struct GetTypeInfo<T *, typename EnableIf<TypeInherits<Object, T>::value>::type> { - static const Variant::Type VARIANT_TYPE = Variant::OBJECT; - static const GodotTypeInfo::Metadata METADATA = GodotTypeInfo::METADATA_NONE; - static inline PropertyInfo get_class_info() { - return PropertyInfo(StringName(T::get_class_static())); - } -}; - -template <typename T> -struct GetTypeInfo<const T *, typename EnableIf<TypeInherits<Object, T>::value>::type> { +struct GetTypeInfo<T *, std::enable_if_t<std::is_base_of_v<Object, T>>> { static const Variant::Type VARIANT_TYPE = Variant::OBJECT; static const GodotTypeInfo::Metadata METADATA = GodotTypeInfo::METADATA_NONE; static inline PropertyInfo get_class_info() { diff --git a/doc/classes/CanvasItem.xml b/doc/classes/CanvasItem.xml index 1cfd44467f..18413c4be5 100644 --- a/doc/classes/CanvasItem.xml +++ b/doc/classes/CanvasItem.xml @@ -8,6 +8,7 @@ Any [CanvasItem] can draw. For this, [method queue_redraw] is called by the engine, then [constant NOTIFICATION_DRAW] will be received on idle time to request a redraw. Because of this, canvas items don't need to be redrawn on every frame, improving the performance significantly. Several functions for drawing on the [CanvasItem] are provided (see [code]draw_*[/code] functions). However, they can only be used inside [method _draw], its corresponding [method Object._notification] or methods connected to the [signal draw] signal. Canvas items are drawn in tree order on their canvas layer. By default, children are on top of their parents, so a root [CanvasItem] will be drawn behind everything. This behavior can be changed on a per-item basis. A [CanvasItem] can be hidden, which will also hide its children. By adjusting various other properties of a [CanvasItem], you can also modulate its color (via [member modulate] or [member self_modulate]), change its Z-index, blend mode, and more. + Note that properties like transform, modulation, and visibility are only propagated to [i]direct[/i] [CanvasItem] child nodes. If there is a non-[CanvasItem] node in between, like [Node] or [AnimationPlayer], the [CanvasItem] nodes below will have an independent position and [member modulate] chain. See also [member top_level]. </description> <tutorials> <link title="Viewport and canvas transforms">$DOCS_URL/tutorials/2d/2d_transforms.html</link> diff --git a/editor/gui/editor_dir_dialog.cpp b/editor/gui/editor_dir_dialog.cpp index 9d5464210b..08612f419c 100644 --- a/editor/gui/editor_dir_dialog.cpp +++ b/editor/gui/editor_dir_dialog.cpp @@ -117,23 +117,13 @@ void EditorDirDialog::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: { FileSystemDock::get_singleton()->connect("folder_color_changed", callable_mp(this, &EditorDirDialog::reload).bind("")); - EditorFileSystem::get_singleton()->connect("filesystem_changed", callable_mp(this, &EditorDirDialog::reload).bind("")); reload(); - - if (!tree->is_connected("item_collapsed", callable_mp(this, &EditorDirDialog::_item_collapsed))) { - tree->connect("item_collapsed", callable_mp(this, &EditorDirDialog::_item_collapsed), CONNECT_DEFERRED); - } - - if (!EditorFileSystem::get_singleton()->is_connected("filesystem_changed", callable_mp(this, &EditorDirDialog::reload))) { - EditorFileSystem::get_singleton()->connect("filesystem_changed", callable_mp(this, &EditorDirDialog::reload).bind("")); - } } break; case NOTIFICATION_EXIT_TREE: { - if (EditorFileSystem::get_singleton()->is_connected("filesystem_changed", callable_mp(this, &EditorDirDialog::reload))) { - EditorFileSystem::get_singleton()->disconnect("filesystem_changed", callable_mp(this, &EditorDirDialog::reload)); - } + EditorFileSystem::get_singleton()->disconnect("filesystem_changed", callable_mp(this, &EditorDirDialog::reload)); + FileSystemDock::get_singleton()->disconnect("folder_color_changed", callable_mp(this, &EditorDirDialog::reload)); } break; case NOTIFICATION_VISIBILITY_CHANGED: { @@ -229,6 +219,7 @@ EditorDirDialog::EditorDirDialog() { vb->add_child(tree); tree->set_v_size_flags(Control::SIZE_EXPAND_FILL); tree->connect("item_activated", callable_mp(this, &EditorDirDialog::_item_activated)); + tree->connect("item_collapsed", callable_mp(this, &EditorDirDialog::_item_collapsed), CONNECT_DEFERRED); set_ok_button_text(TTR("Move")); diff --git a/editor/plugins/path_3d_editor_plugin.cpp b/editor/plugins/path_3d_editor_plugin.cpp index 1cffdb6454..4e317114a3 100644 --- a/editor/plugins/path_3d_editor_plugin.cpp +++ b/editor/plugins/path_3d_editor_plugin.cpp @@ -169,21 +169,17 @@ void Path3DGizmo::set_handle(int p_id, bool p_secondary, Camera3D *p_camera, con const Basis posture = c->get_point_baked_posture(idx); const Vector3 tangent = -posture.get_column(2); const Vector3 up = posture.get_column(1); - const Plane p_tilt = Plane(tangent, position); + const Plane tilt_plane_global = gt.xform(Plane(tangent, position)); Vector3 intersection; - if (p_tilt.intersects_ray(ray_from, ray_dir, &intersection)) { - Vector3 direction = intersection - position; - direction.normalize(); // FIXME: redundant? + if (tilt_plane_global.intersects_ray(ray_from, ray_dir, &intersection)) { + Vector3 direction = gi.xform(intersection) - position; real_t tilt_angle = up.signed_angle_to(direction, tangent); if (Node3DEditor::get_singleton()->is_snap_enabled()) { - real_t snap = Node3DEditor::get_singleton()->get_rotate_snap(); - - tilt_angle = Math::rad_to_deg(tilt_angle) + snap * 0.5; // Else it won't reach +180. - tilt_angle -= Math::fmod(tilt_angle, snap); - tilt_angle = Math::deg_to_rad(tilt_angle); + real_t snap_degrees = Node3DEditor::get_singleton()->get_rotate_snap(); + tilt_angle = Math::deg_to_rad(Math::snapped(Math::rad_to_deg(tilt_angle), snap_degrees)); } c->set_point_tilt(idx, tilt_angle); diff --git a/modules/gdscript/gdscript_byte_codegen.cpp b/modules/gdscript/gdscript_byte_codegen.cpp index 36b157521d..bfe090edb0 100644 --- a/modules/gdscript/gdscript_byte_codegen.cpp +++ b/modules/gdscript/gdscript_byte_codegen.cpp @@ -45,8 +45,8 @@ uint32_t GDScriptByteCodeGenerator::add_parameter(const StringName &p_name, bool } uint32_t GDScriptByteCodeGenerator::add_local(const StringName &p_name, const GDScriptDataType &p_type) { - int stack_pos = locals.size() + RESERVED_STACK; - locals.push_back(StackSlot(p_type.builtin_type)); + int stack_pos = locals.size() + GDScriptFunction::FIXED_ADDRESSES_MAX; + locals.push_back(StackSlot(p_type.builtin_type, p_type.can_contain_object())); add_stack_identifier(p_name, stack_pos); return stack_pos; } @@ -122,7 +122,7 @@ uint32_t GDScriptByteCodeGenerator::add_temporary(const GDScriptDataType &p_type List<int> &pool = temporaries_pool[temp_type]; if (pool.is_empty()) { - StackSlot new_temp(temp_type); + StackSlot new_temp(temp_type, p_type.can_contain_object()); int idx = temporaries.size(); pool.push_back(idx); temporaries.push_back(new_temp); @@ -136,15 +136,14 @@ uint32_t GDScriptByteCodeGenerator::add_temporary(const GDScriptDataType &p_type void GDScriptByteCodeGenerator::pop_temporary() { ERR_FAIL_COND(used_temporaries.is_empty()); int slot_idx = used_temporaries.back()->get(); - const StackSlot &slot = temporaries[slot_idx]; - if (slot.type == Variant::NIL) { + if (temporaries[slot_idx].can_contain_object) { // Avoid keeping in the stack long-lived references to objects, - // which may prevent RefCounted objects from being freed. + // which may prevent `RefCounted` objects from being freed. // However, the cleanup will be performed an the end of the // statement, to allow object references to survive chaining. - temporaries_pending_clear.push_back(slot_idx); + temporaries_pending_clear.insert(slot_idx); } - temporaries_pool[slot.type].push_back(slot_idx); + temporaries_pool[temporaries[slot_idx].type].push_back(slot_idx); used_temporaries.pop_back(); } @@ -187,7 +186,7 @@ GDScriptFunction *GDScriptByteCodeGenerator::write_end() { append_opcode(GDScriptFunction::OPCODE_END); for (int i = 0; i < temporaries.size(); i++) { - int stack_index = i + max_locals + RESERVED_STACK; + int stack_index = i + max_locals + GDScriptFunction::FIXED_ADDRESSES_MAX; for (int j = 0; j < temporaries[i].bytecode_indices.size(); j++) { opcodes.write[temporaries[i].bytecode_indices[j]] = stack_index | (GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS); } @@ -398,7 +397,7 @@ GDScriptFunction *GDScriptByteCodeGenerator::write_end() { if (debug_stack) { function->stack_debug = stack_debug; } - function->_stack_size = RESERVED_STACK + max_locals + temporaries.size(); + function->_stack_size = GDScriptFunction::FIXED_ADDRESSES_MAX + max_locals + temporaries.size(); function->_instruction_args_size = instr_args_max; #ifdef DEBUG_ENABLED @@ -945,6 +944,11 @@ void GDScriptByteCodeGenerator::write_assign(const Address &p_target, const Addr } } +void GDScriptByteCodeGenerator::write_assign_null(const Address &p_target) { + append_opcode(GDScriptFunction::OPCODE_ASSIGN_NULL); + append(p_target); +} + void GDScriptByteCodeGenerator::write_assign_true(const Address &p_target) { append_opcode(GDScriptFunction::OPCODE_ASSIGN_TRUE); append(p_target); @@ -1579,9 +1583,8 @@ void GDScriptByteCodeGenerator::write_for(const Address &p_variable, bool p_use_ if (p_use_conversion) { write_assign_with_conversion(p_variable, temp); - const GDScriptDataType &type = p_variable.type; - if (type.kind != GDScriptDataType::BUILTIN || type.builtin_type == Variant::ARRAY || type.builtin_type == Variant::DICTIONARY) { - write_assign_false(temp); // Can contain RefCounted, so clear it. + if (p_variable.type.can_contain_object()) { + clear_address(temp); // Can contain `RefCounted`, so clear it. } } } @@ -1746,23 +1749,56 @@ void GDScriptByteCodeGenerator::end_block() { pop_stack_identifiers(); } -void GDScriptByteCodeGenerator::clean_temporaries() { - List<int>::Element *E = temporaries_pending_clear.front(); - while (E) { - // The temporary may have been re-used as something else than an object - // since it was added to the list. In that case, there's no need to clear it. - int slot_idx = E->get(); - const StackSlot &slot = temporaries[slot_idx]; - if (slot.type == Variant::NIL) { - write_assign_false(Address(Address::TEMPORARY, slot_idx)); +void GDScriptByteCodeGenerator::clear_temporaries() { + for (int slot_idx : temporaries_pending_clear) { + // The temporary may have been re-used as something else since it was added to the list. + // In that case, there's **no** need to clear it. + if (temporaries[slot_idx].can_contain_object) { + clear_address(Address(Address::TEMPORARY, slot_idx)); // Can contain `RefCounted`, so clear it. + } + } + temporaries_pending_clear.clear(); +} + +void GDScriptByteCodeGenerator::clear_address(const Address &p_address) { + // Do not check `is_local_dirty()` here! Always clear the address since the codegen doesn't track the compiler. + // Also, this method is used to initialize local variables of built-in types, since they cannot be `null`. + + if (p_address.type.has_type && p_address.type.kind == GDScriptDataType::BUILTIN) { + switch (p_address.type.builtin_type) { + case Variant::BOOL: + write_assign_false(p_address); + break; + case Variant::ARRAY: + if (p_address.type.has_container_element_type(0)) { + write_construct_typed_array(p_address, p_address.type.get_container_element_type(0), Vector<GDScriptCodeGenerator::Address>()); + } else { + write_construct(p_address, p_address.type.builtin_type, Vector<GDScriptCodeGenerator::Address>()); + } + break; + case Variant::NIL: + case Variant::OBJECT: + write_assign_null(p_address); + break; + default: + write_construct(p_address, p_address.type.builtin_type, Vector<GDScriptCodeGenerator::Address>()); + break; } + } else { + write_assign_null(p_address); + } - List<int>::Element *next = E->next(); - E->erase(); - E = next; + if (p_address.mode == Address::LOCAL_VARIABLE) { + dirty_locals.erase(p_address.address); } } +// Returns `true` if the local has been re-used and not cleaned up with `clear_address()`. +bool GDScriptByteCodeGenerator::is_local_dirty(const Address &p_address) const { + ERR_FAIL_COND_V(p_address.mode != Address::LOCAL_VARIABLE, false); + return dirty_locals.has(p_address.address); +} + GDScriptByteCodeGenerator::~GDScriptByteCodeGenerator() { if (!ended && function != nullptr) { memdelete(function); diff --git a/modules/gdscript/gdscript_byte_codegen.h b/modules/gdscript/gdscript_byte_codegen.h index f902cb10cc..5a736b2554 100644 --- a/modules/gdscript/gdscript_byte_codegen.h +++ b/modules/gdscript/gdscript_byte_codegen.h @@ -38,15 +38,14 @@ class GDScriptByteCodeGenerator : public GDScriptCodeGenerator { struct StackSlot { Variant::Type type = Variant::NIL; + bool can_contain_object = true; Vector<int> bytecode_indices; StackSlot() = default; - StackSlot(Variant::Type p_type) : - type(p_type) {} + StackSlot(Variant::Type p_type, bool p_can_contain_object) : + type(p_type), can_contain_object(p_can_contain_object) {} }; - const static int RESERVED_STACK = 3; // For self, class, and nil. - struct CallTarget { Address target; bool is_new_temporary = false; @@ -85,9 +84,11 @@ class GDScriptByteCodeGenerator : public GDScriptCodeGenerator { RBMap<StringName, int> local_constants; Vector<StackSlot> locals; + HashSet<int> dirty_locals; + Vector<StackSlot> temporaries; List<int> used_temporaries; - List<int> temporaries_pending_clear; + HashSet<int> temporaries_pending_clear; RBMap<Variant::Type, List<int>> temporaries_pool; List<GDScriptFunction::StackDebug> stack_debug; @@ -193,6 +194,9 @@ class GDScriptByteCodeGenerator : public GDScriptCodeGenerator { ERR_PRINT("Leaving block with non-zero temporary variables: " + itos(used_temporaries.size())); } #endif + for (int i = current_locals; i < locals.size(); i++) { + dirty_locals.insert(i + GDScriptFunction::FIXED_ADDRESSES_MAX); + } locals.resize(current_locals); if (debug_stack) { for (const KeyValue<StringName, int> &E : block_identifiers) { @@ -455,7 +459,9 @@ public: virtual uint32_t add_or_get_name(const StringName &p_name) override; virtual uint32_t add_temporary(const GDScriptDataType &p_type) override; virtual void pop_temporary() override; - virtual void clean_temporaries() override; + virtual void clear_temporaries() override; + virtual void clear_address(const Address &p_address) override; + virtual bool is_local_dirty(const Address &p_address) const override; virtual void start_parameters() override; virtual void end_parameters() override; @@ -496,6 +502,7 @@ public: virtual void write_get_static_variable(const Address &p_target, const Address &p_class, int p_index) override; virtual void write_assign(const Address &p_target, const Address &p_source) override; virtual void write_assign_with_conversion(const Address &p_target, const Address &p_source) override; + virtual void write_assign_null(const Address &p_target) override; virtual void write_assign_true(const Address &p_target) override; virtual void write_assign_false(const Address &p_target) override; virtual void write_assign_default_parameter(const Address &p_dst, const Address &p_src, bool p_use_conversion) override; diff --git a/modules/gdscript/gdscript_codegen.h b/modules/gdscript/gdscript_codegen.h index 7ad8f841aa..4c33ed499a 100644 --- a/modules/gdscript/gdscript_codegen.h +++ b/modules/gdscript/gdscript_codegen.h @@ -73,7 +73,9 @@ public: virtual uint32_t add_or_get_name(const StringName &p_name) = 0; virtual uint32_t add_temporary(const GDScriptDataType &p_type) = 0; virtual void pop_temporary() = 0; - virtual void clean_temporaries() = 0; + virtual void clear_temporaries() = 0; + virtual void clear_address(const Address &p_address) = 0; + virtual bool is_local_dirty(const Address &p_address) const = 0; virtual void start_parameters() = 0; virtual void end_parameters() = 0; @@ -114,6 +116,7 @@ public: virtual void write_get_static_variable(const Address &p_target, const Address &p_class, int p_index) = 0; virtual void write_assign(const Address &p_target, const Address &p_source) = 0; virtual void write_assign_with_conversion(const Address &p_target, const Address &p_source) = 0; + virtual void write_assign_null(const Address &p_target) = 0; virtual void write_assign_true(const Address &p_target) = 0; virtual void write_assign_false(const Address &p_target) = 0; virtual void write_assign_default_parameter(const Address &dst, const Address &src, bool p_use_conversion) = 0; diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp index c526d9c0a4..734e37bc09 100644 --- a/modules/gdscript/gdscript_compiler.cpp +++ b/modules/gdscript/gdscript_compiler.cpp @@ -1826,7 +1826,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_match_pattern(CodeGen &c ERR_FAIL_V_MSG(p_previous_test, "Reaching the end of pattern compilation without matching a pattern."); } -List<GDScriptCodeGenerator::Address> GDScriptCompiler::_add_locals_in_block(CodeGen &codegen, const GDScriptParser::SuiteNode *p_block) { +List<GDScriptCodeGenerator::Address> GDScriptCompiler::_add_block_locals(CodeGen &codegen, const GDScriptParser::SuiteNode *p_block) { List<GDScriptCodeGenerator::Address> addresses; for (int i = 0; i < p_block->locals.size(); i++) { if (p_block->locals[i].type == GDScriptParser::SuiteNode::Local::PARAMETER || p_block->locals[i].type == GDScriptParser::SuiteNode::Local::FOR_VARIABLE) { @@ -1838,27 +1838,25 @@ List<GDScriptCodeGenerator::Address> GDScriptCompiler::_add_locals_in_block(Code return addresses; } -// Avoid keeping in the stack long-lived references to objects, which may prevent RefCounted objects from being freed. -void GDScriptCompiler::_clear_addresses(CodeGen &codegen, const List<GDScriptCodeGenerator::Address> &p_addresses) { - for (const List<GDScriptCodeGenerator::Address>::Element *E = p_addresses.front(); E; E = E->next()) { - GDScriptDataType type = E->get().type; - // If not an object and cannot contain an object, no need to clear. - if (type.kind != GDScriptDataType::BUILTIN || type.builtin_type == Variant::ARRAY || type.builtin_type == Variant::DICTIONARY) { - codegen.generator->write_assign_false(E->get()); +// Avoid keeping in the stack long-lived references to objects, which may prevent `RefCounted` objects from being freed. +void GDScriptCompiler::_clear_block_locals(CodeGen &codegen, const List<GDScriptCodeGenerator::Address> &p_locals) { + for (const GDScriptCodeGenerator::Address &local : p_locals) { + if (local.type.can_contain_object()) { + codegen.generator->clear_address(local); } } } -Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::SuiteNode *p_block, bool p_add_locals, bool p_reset_locals) { +Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::SuiteNode *p_block, bool p_add_locals, bool p_clear_locals) { Error err = OK; GDScriptCodeGenerator *gen = codegen.generator; List<GDScriptCodeGenerator::Address> block_locals; - gen->clean_temporaries(); + gen->clear_temporaries(); codegen.start_block(); if (p_add_locals) { - block_locals = _add_locals_in_block(codegen, p_block); + block_locals = _add_block_locals(codegen, p_block); } for (int i = 0; i < p_block->statements.size(); i++) { @@ -1873,7 +1871,7 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Sui case GDScriptParser::Node::MATCH: { const GDScriptParser::MatchNode *match = static_cast<const GDScriptParser::MatchNode *>(s); - codegen.start_block(); + codegen.start_block(); // Add an extra block, since the binding pattern and @special variables belong to the branch scope. // Evaluate the match expression. GDScriptCodeGenerator::Address value = codegen.add_local("@match_value", _gdtype_from_datatype(match->test->get_datatype(), codegen.script)); @@ -1914,7 +1912,7 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Sui codegen.start_block(); // Create an extra block around for binds. // Add locals in block before patterns, so temporaries don't use the stack address for binds. - List<GDScriptCodeGenerator::Address> branch_locals = _add_locals_in_block(codegen, branch->block); + List<GDScriptCodeGenerator::Address> branch_locals = _add_block_locals(codegen, branch->block); #ifdef DEBUG_ENABLED // Add a newline before each branch, since the debugger needs those. @@ -1961,7 +1959,7 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Sui return err; } - _clear_addresses(codegen, branch_locals); + _clear_block_locals(codegen, branch_locals); codegen.end_block(); // Get out of extra block. } @@ -2003,7 +2001,8 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Sui case GDScriptParser::Node::FOR: { const GDScriptParser::ForNode *for_n = static_cast<const GDScriptParser::ForNode *>(s); - codegen.start_block(); + codegen.start_block(); // Add an extra block, since the iterator and @special variables belong to the loop scope. + GDScriptCodeGenerator::Address iterator = codegen.add_local(for_n->variable->name, _gdtype_from_datatype(for_n->variable->get_datatype(), codegen.script)); gen->start_for(iterator.type, _gdtype_from_datatype(for_n->list->get_datatype(), codegen.script)); @@ -2021,14 +2020,21 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Sui gen->write_for(iterator, for_n->use_conversion_assign); - err = _parse_block(codegen, for_n->loop); + // Loop variables must be cleared even when `break`/`continue` is used. + List<GDScriptCodeGenerator::Address> loop_locals = _add_block_locals(codegen, for_n->loop); + + //_clear_block_locals(codegen, loop_locals); // Inside loop, before block - for `continue`. // TODO + + err = _parse_block(codegen, for_n->loop, false); // Don't add locals again. if (err) { return err; } gen->write_endfor(); - codegen.end_block(); + _clear_block_locals(codegen, loop_locals); // Outside loop, after block - for `break` and normal exit. + + codegen.end_block(); // Get out of extra block. } break; case GDScriptParser::Node::WHILE: { const GDScriptParser::WhileNode *while_n = static_cast<const GDScriptParser::WhileNode *>(s); @@ -2046,12 +2052,19 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Sui codegen.generator->pop_temporary(); } - err = _parse_block(codegen, while_n->loop); + // Loop variables must be cleared even when `break`/`continue` is used. + List<GDScriptCodeGenerator::Address> loop_locals = _add_block_locals(codegen, while_n->loop); + + //_clear_block_locals(codegen, loop_locals); // Inside loop, before block - for `continue`. // TODO + + err = _parse_block(codegen, while_n->loop, false); // Don't add locals again. if (err) { return err; } gen->write_endwhile(); + + _clear_block_locals(codegen, loop_locals); // Outside loop, after block - for `break` and normal exit. } break; case GDScriptParser::Node::BREAK: { gen->write_break(); @@ -2134,21 +2147,16 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Sui codegen.generator->pop_temporary(); } initialized = true; - } else if (local_type.has_type) { - // Initialize with default for type. - 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>()); - initialized = true; - } - // The `else` branch is for objects, in such case we leave it as `null`. + } else if ((local_type.has_type && local_type.kind == GDScriptDataType::BUILTIN) || codegen.generator->is_local_dirty(local)) { + // Initialize with default for the type. Built-in types must always be cleared (they cannot be `null`). + // Objects and untyped variables are assigned to `null` only if the stack address has been re-used and not cleared. + codegen.generator->clear_address(local); + initialized = true; } - // Assigns a null for the unassigned variables in loops. + // Don't check `is_local_dirty()` since the variable must be assigned to `null` **on each iteration**. if (!initialized && p_block->is_in_loop) { - codegen.generator->write_construct(local, Variant::NIL, Vector<GDScriptCodeGenerator::Address>()); + codegen.generator->clear_address(local); } } break; case GDScriptParser::Node::CONSTANT: { @@ -2180,11 +2188,11 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Sui } break; } - gen->clean_temporaries(); + gen->clear_temporaries(); } - if (p_add_locals && p_reset_locals) { - _clear_addresses(codegen, block_locals); + if (p_add_locals && p_clear_locals) { + _clear_block_locals(codegen, block_locals); } codegen.end_block(); diff --git a/modules/gdscript/gdscript_compiler.h b/modules/gdscript/gdscript_compiler.h index 637d61ca3b..45f0f9e19b 100644 --- a/modules/gdscript/gdscript_compiler.h +++ b/modules/gdscript/gdscript_compiler.h @@ -153,9 +153,9 @@ class GDScriptCompiler { GDScriptCodeGenerator::Address _parse_expression(CodeGen &codegen, Error &r_error, const GDScriptParser::ExpressionNode *p_expression, bool p_root = false, bool p_initializer = false); GDScriptCodeGenerator::Address _parse_match_pattern(CodeGen &codegen, Error &r_error, const GDScriptParser::PatternNode *p_pattern, const GDScriptCodeGenerator::Address &p_value_addr, const GDScriptCodeGenerator::Address &p_type_addr, const GDScriptCodeGenerator::Address &p_previous_test, bool p_is_first, bool p_is_nested); - List<GDScriptCodeGenerator::Address> _add_locals_in_block(CodeGen &codegen, const GDScriptParser::SuiteNode *p_block); - void _clear_addresses(CodeGen &codegen, const List<GDScriptCodeGenerator::Address> &p_addresses); - Error _parse_block(CodeGen &codegen, const GDScriptParser::SuiteNode *p_block, bool p_add_locals = true, bool p_reset_locals = true); + List<GDScriptCodeGenerator::Address> _add_block_locals(CodeGen &codegen, const GDScriptParser::SuiteNode *p_block); + void _clear_block_locals(CodeGen &codegen, const List<GDScriptCodeGenerator::Address> &p_locals); + Error _parse_block(CodeGen &codegen, const GDScriptParser::SuiteNode *p_block, bool p_add_locals = true, bool p_clear_locals = true); GDScriptFunction *_parse_function(Error &r_error, GDScript *p_script, const GDScriptParser::ClassNode *p_class, const GDScriptParser::FunctionNode *p_func, bool p_for_ready = false, bool p_for_lambda = false); GDScriptFunction *_make_static_initializer(Error &r_error, GDScript *p_script, const GDScriptParser::ClassNode *p_class); Error _parse_setter_getter(GDScript *p_script, const GDScriptParser::ClassNode *p_class, const GDScriptParser::VariableNode *p_variable, bool p_is_setter); diff --git a/modules/gdscript/gdscript_disassembler.cpp b/modules/gdscript/gdscript_disassembler.cpp index 26f7cb7537..c7873dcd52 100644 --- a/modules/gdscript/gdscript_disassembler.cpp +++ b/modules/gdscript/gdscript_disassembler.cpp @@ -360,6 +360,13 @@ void GDScriptFunction::disassemble(const Vector<String> &p_code_lines) const { incr += 3; } break; + case OPCODE_ASSIGN_NULL: { + text += "assign "; + text += DADDR(1); + text += " = null"; + + incr += 2; + } break; case OPCODE_ASSIGN_TRUE: { text += "assign "; text += DADDR(1); diff --git a/modules/gdscript/gdscript_function.h b/modules/gdscript/gdscript_function.h index 002fc159fa..184d256bcd 100644 --- a/modules/gdscript/gdscript_function.h +++ b/modules/gdscript/gdscript_function.h @@ -78,17 +78,17 @@ public: 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); + const GDScriptDataType &elem_type = container_element_types[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 = (array_container_type.kind == SCRIPT || array_container_type.kind == GDSCRIPT) && array_container_type.script_type == array_script_type_ref.ptr(); + valid = (elem_type.kind == SCRIPT || elem_type.kind == GDSCRIPT) && elem_type.script_type == array_script_type_ref.ptr(); } else if (array_native_type != StringName()) { - valid = array_container_type.kind == NATIVE && array_container_type.native_type == array_native_type; + valid = elem_type.kind == NATIVE && elem_type.native_type == array_native_type; } else { - valid = array_container_type.kind == BUILTIN && array_container_type.builtin_type == array_builtin_type; + valid = elem_type.kind == BUILTIN && elem_type.builtin_type == array_builtin_type; } } else { valid = false; @@ -147,6 +147,25 @@ public: return false; } + bool can_contain_object() const { + if (has_type && kind == BUILTIN) { + switch (builtin_type) { + case Variant::ARRAY: + if (has_container_element_type(0)) { + return container_element_types[0].can_contain_object(); + } + return true; + case Variant::DICTIONARY: + case Variant::NIL: + case Variant::OBJECT: + return true; + default: + return false; + } + } + return true; + } + 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()) { @@ -218,6 +237,7 @@ public: OPCODE_SET_STATIC_VARIABLE, // Only for GDScript. OPCODE_GET_STATIC_VARIABLE, // Only for GDScript. OPCODE_ASSIGN, + OPCODE_ASSIGN_NULL, OPCODE_ASSIGN_TRUE, OPCODE_ASSIGN_FALSE, OPCODE_ASSIGN_TYPED_BUILTIN, diff --git a/modules/gdscript/gdscript_vm.cpp b/modules/gdscript/gdscript_vm.cpp index 1a8c22cc11..3cb011b251 100644 --- a/modules/gdscript/gdscript_vm.cpp +++ b/modules/gdscript/gdscript_vm.cpp @@ -234,6 +234,7 @@ void (*type_init_function_table[])(Variant *) = { &&OPCODE_SET_STATIC_VARIABLE, \ &&OPCODE_GET_STATIC_VARIABLE, \ &&OPCODE_ASSIGN, \ + &&OPCODE_ASSIGN_NULL, \ &&OPCODE_ASSIGN_TRUE, \ &&OPCODE_ASSIGN_FALSE, \ &&OPCODE_ASSIGN_TYPED_BUILTIN, \ @@ -1256,6 +1257,16 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a } DISPATCH_OPCODE; + OPCODE(OPCODE_ASSIGN_NULL) { + CHECK_SPACE(2); + GET_VARIANT_PTR(dst, 0); + + *dst = Variant(); + + ip += 2; + } + DISPATCH_OPCODE; + OPCODE(OPCODE_ASSIGN_TRUE) { CHECK_SPACE(2); GET_VARIANT_PTR(dst, 0); diff --git a/modules/gdscript/tests/scripts/runtime/features/reset_uninit_local_vars.gd b/modules/gdscript/tests/scripts/runtime/features/reset_uninit_local_vars.gd new file mode 100644 index 0000000000..b676ece24b --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/features/reset_uninit_local_vars.gd @@ -0,0 +1,21 @@ +# GH-89958 + +func test(): + if true: + @warning_ignore("unused_variable") + var a = 1 + @warning_ignore("unused_variable") + var b := 1 + @warning_ignore("unused_variable") + var c := 1 + + if true: + @warning_ignore("unassigned_variable") + var a + print(a) + @warning_ignore("unassigned_variable") + var b + print(b) + @warning_ignore("unassigned_variable") + var c: Object + print(c) diff --git a/modules/gdscript/tests/scripts/runtime/features/reset_uninit_local_vars.out b/modules/gdscript/tests/scripts/runtime/features/reset_uninit_local_vars.out new file mode 100644 index 0000000000..279ea2d3b1 --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/features/reset_uninit_local_vars.out @@ -0,0 +1,4 @@ +GDTEST_OK +<null> +<null> +<null> diff --git a/tests/scene/test_navigation_region_3d.h b/tests/scene/test_navigation_region_3d.h index 0b20b3a1b2..f3d7f27361 100644 --- a/tests/scene/test_navigation_region_3d.h +++ b/tests/scene/test_navigation_region_3d.h @@ -65,7 +65,9 @@ TEST_SUITE("[Navigation]") { CHECK_EQ(navigation_mesh->get_vertices().size(), 0); SUBCASE("Synchronous bake should have immediate effects") { + ERR_PRINT_OFF; // Suppress warning about baking from visual meshes as source geometry. navigation_region->bake_navigation_mesh(false); + ERR_PRINT_ON; CHECK_FALSE(navigation_region->is_baking()); CHECK_NE(navigation_mesh->get_polygon_count(), 0); CHECK_NE(navigation_mesh->get_vertices().size(), 0); |