summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/macos_builds.yml18
-rw-r--r--core/input/gamecontrollerdb.txt4
-rw-r--r--core/io/logger.cpp16
-rw-r--r--core/io/logger.h8
-rw-r--r--core/io/resource_loader.cpp12
-rw-r--r--core/object/class_db.h10
-rw-r--r--core/variant/type_info.h37
-rw-r--r--doc/classes/CanvasItem.xml1
-rw-r--r--editor/gui/editor_dir_dialog.cpp15
-rw-r--r--editor/plugins/path_3d_editor_plugin.cpp14
-rw-r--r--modules/gdscript/gdscript_byte_codegen.cpp86
-rw-r--r--modules/gdscript/gdscript_byte_codegen.h19
-rw-r--r--modules/gdscript/gdscript_codegen.h5
-rw-r--r--modules/gdscript/gdscript_compiler.cpp74
-rw-r--r--modules/gdscript/gdscript_compiler.h6
-rw-r--r--modules/gdscript/gdscript_disassembler.cpp7
-rw-r--r--modules/gdscript/gdscript_function.h28
-rw-r--r--modules/gdscript/gdscript_vm.cpp11
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/reset_uninit_local_vars.gd21
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/reset_uninit_local_vars.out4
-rw-r--r--tests/scene/test_navigation_region_3d.h2
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);