From 05edbec8279f232c5cb38107f328dc2e39444a79 Mon Sep 17 00:00:00 2001 From: PJB3005 Date: Mon, 18 May 2026 02:06:38 +0200 Subject: [PATCH 1/3] Tag operator new overloads with noexcept According to the C++ standard, regular operator new must always return a valid pointer, and allocation failure should throw an exception. The original game did not use exceptions, and instead had its operator new return null on alloc failure. Clang and GCC seem to be enforcing the standard here, and this is causing crashes on non-Windows platforms like https://sentry.twilitrealm.dev/organizations/twilitrealm/issues/952/, where a JAISe allocation failure (custom pooled operator new overload) crashes the game when it should be handled gracefully. MSVC seems to not make use of this opportunity, meaning the code works as intended. Tagging the operators with noexcept seems to satisfy GCC, but I admit cppreference is very light on details here. --- .../include/JSystem/JAudio2/JASHeapCtrl.h | 10 +++--- .../JSystem/include/JSystem/JKernel/JKRHeap.h | 34 +++++++++++-------- 2 files changed, 24 insertions(+), 20 deletions(-) diff --git a/libs/JSystem/include/JSystem/JAudio2/JASHeapCtrl.h b/libs/JSystem/include/JSystem/JAudio2/JASHeapCtrl.h index 7a3ae12470..0378dc36b8 100644 --- a/libs/JSystem/include/JSystem/JAudio2/JASHeapCtrl.h +++ b/libs/JSystem/include/JSystem/JAudio2/JASHeapCtrl.h @@ -287,28 +287,28 @@ template class JASPoolAllocObject { public: #if TARGET_PC - static void* operator new(size_t n, JKRHeapToken) { + static void* operator new(size_t n, JKRHeapToken) IF_DUSK(noexcept) { return operator new(n); } #endif - static void* operator new(size_t n) { + static void* operator new(size_t n) IF_DUSK(noexcept) { #if PLATFORM_GCN JASMemPool& memPool_ = getMemPool_(); #endif return memPool_.alloc(n); } - static void* operator new(size_t n, void* ptr) { + static void* operator new(size_t n, void* ptr) IF_DUSK(noexcept) { return ptr; } #if TARGET_PC - static void operator delete(void* ptr, size_t n, JKRHeapToken) { + static void operator delete(void* ptr, size_t n, JKRHeapToken) IF_DUSK(noexcept) { operator delete(ptr, n); } #endif - static void operator delete(void* ptr, size_t n) { + static void operator delete(void* ptr, size_t n) IF_DUSK(noexcept) { #if PLATFORM_GCN JASMemPool& memPool_ = getMemPool_(); #endif diff --git a/libs/JSystem/include/JSystem/JKernel/JKRHeap.h b/libs/JSystem/include/JSystem/JKernel/JKRHeap.h index d3710bd5df..d1b27a0fd0 100644 --- a/libs/JSystem/include/JSystem/JKernel/JKRHeap.h +++ b/libs/JSystem/include/JSystem/JKernel/JKRHeap.h @@ -237,11 +237,11 @@ enum class JKRHeapToken { Dummy }; -inline void* operator new(size_t, JKRHeapToken, void* where) { +inline void* operator new(size_t, JKRHeapToken, void* where) noexcept { return where; } -inline void* operator new[](size_t, JKRHeapToken, void* where) { +inline void* operator new[](size_t, JKRHeapToken, void* where) noexcept { return where; } @@ -264,21 +264,21 @@ inline void* operator new[](size_t, JKRHeapToken, void* where) { #define JKR_HEAP_TOKEN_PARAM #endif -void* operator new(size_t size JKR_HEAP_TOKEN_PARAM); -void* operator new(size_t size JKR_HEAP_TOKEN_PARAM, int alignment); -void* operator new(size_t size JKR_HEAP_TOKEN_PARAM, JKRHeap* heap, int alignment); +void* operator new(size_t size JKR_HEAP_TOKEN_PARAM) IF_DUSK(noexcept); +void* operator new(size_t size JKR_HEAP_TOKEN_PARAM, int alignment) IF_DUSK(noexcept); +void* operator new(size_t size JKR_HEAP_TOKEN_PARAM, JKRHeap* heap, int alignment) IF_DUSK(noexcept); // On PC, these new[] overloads are only used to catch usages of JKR_NEW with []. -void* operator new[](size_t size JKR_HEAP_TOKEN_PARAM); -void* operator new[](size_t size JKR_HEAP_TOKEN_PARAM, int alignment); -void* operator new[](size_t size JKR_HEAP_TOKEN_PARAM, JKRHeap* heap, int alignment); +void* operator new[](size_t size JKR_HEAP_TOKEN_PARAM) IF_DUSK(noexcept); +void* operator new[](size_t size JKR_HEAP_TOKEN_PARAM, int alignment) IF_DUSK(noexcept); +void* operator new[](size_t size JKR_HEAP_TOKEN_PARAM, JKRHeap* heap, int alignment) IF_DUSK(noexcept); -void operator delete(void* ptr JKR_HEAP_TOKEN_PARAM); -void operator delete[](void* ptr JKR_HEAP_TOKEN_PARAM); +void operator delete(void* ptr JKR_HEAP_TOKEN_PARAM) IF_DUSK(noexcept); +void operator delete[](void* ptr JKR_HEAP_TOKEN_PARAM) IF_DUSK(noexcept); #if TARGET_PC template -void jkrDelete(T* ptr) { +void jkrDelete(T* ptr) IF_DUSK(noexcept) { if (ptr == nullptr) { return; } @@ -298,7 +298,7 @@ void jkrDelete(T* ptr) { } template<> -inline void jkrDelete(void* ptr) { +inline void jkrDelete(void* ptr) IF_DUSK(noexcept) { if (ptr == nullptr) { return; } @@ -322,7 +322,7 @@ constexpr bool newArgsHasCustomAlignment() { } template -T* jkrNewArray(size_t count, std::in_place_type_t, Args&&... args) { +T* jkrNewArray(size_t count, std::in_place_type_t, Args&&... args) IF_DUSK(noexcept) { size_t allocSize = count * sizeof(T); if constexpr (!std::is_trivially_destructible()) { static_assert( @@ -333,6 +333,10 @@ T* jkrNewArray(size_t count, std::in_place_type_t, Args&&... args) { } void* ptr = operator new(allocSize, JKRHeapToken::Dummy, args...); + if (!ptr) { + return nullptr; + } + T* dataPtr; if constexpr (!std::is_trivially_destructible()) { auto length = static_cast(ptr); @@ -352,7 +356,7 @@ T* jkrNewArray(size_t count, std::in_place_type_t, Args&&... args) { } template -void jkrDeleteArray(T* pointer) { +void jkrDeleteArray(T* pointer) IF_DUSK(noexcept) { if (pointer == nullptr) { return; } @@ -372,7 +376,7 @@ void jkrDeleteArray(T* pointer) { } template<> -inline void jkrDeleteArray(void* pointer) { +inline void jkrDeleteArray(void* pointer) IF_DUSK(noexcept) { if (pointer == nullptr) { return; } From 831a34ac8f2163315ee363c40546c4fc3e6bc1c9 Mon Sep 17 00:00:00 2001 From: PJB3005 Date: Mon, 18 May 2026 02:15:12 +0200 Subject: [PATCH 2/3] Right, the definition needs the specifier too --- libs/JSystem/src/JKernel/JKRHeap.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/libs/JSystem/src/JKernel/JKRHeap.cpp b/libs/JSystem/src/JKernel/JKRHeap.cpp index ce1899a4ba..c4849437ac 100644 --- a/libs/JSystem/src/JKernel/JKRHeap.cpp +++ b/libs/JSystem/src/JKernel/JKRHeap.cpp @@ -559,7 +559,7 @@ void* operator new(size_t size) { return JKRHeap::alloc(size, 4, NULL); } #else -void* operator new(size_t size JKR_HEAP_TOKEN_PARAM) { +void* operator new(size_t size JKR_HEAP_TOKEN_PARAM) noexcept { if (sCurrentHeap == NULL) { return fallback_alloc(size, 0, false); } @@ -576,7 +576,7 @@ void* operator new(size_t size, int alignment) { return JKRHeap::alloc(size, alignment, NULL); } #else -void* operator new(size_t size JKR_HEAP_TOKEN_PARAM, int alignment) { +void* operator new(size_t size JKR_HEAP_TOKEN_PARAM, int alignment) noexcept { void* mem = JKRHeap::alloc(size, alignment, nullptr); if (mem == nullptr) { return fallback_alloc(size, abs(alignment), true); @@ -585,7 +585,7 @@ void* operator new(size_t size JKR_HEAP_TOKEN_PARAM, int alignment) { } #endif -void* operator new(size_t size JKR_HEAP_TOKEN_PARAM, JKRHeap* heap, int alignment) { +void* operator new(size_t size JKR_HEAP_TOKEN_PARAM, JKRHeap* heap, int alignment) IF_DUSK(noexcept) { void* mem = JKRHeap::alloc(size, alignment, heap); #if TARGET_PC if (mem == nullptr) { @@ -600,7 +600,7 @@ void* operator new[](size_t size) { return JKRHeap::alloc(size, 4, NULL); } #else -void* operator new[](size_t JKR_HEAP_TOKEN_PARAM) { +void* operator new[](size_t JKR_HEAP_TOKEN_PARAM) IF_DUSK(noexcept) { OSPanic(__FILE__, __LINE__, "Allocation should go through JKR_NEW_ARRAY instead"); } #endif @@ -610,12 +610,12 @@ void* operator new[](size_t size, int alignment) { return JKRHeap::alloc(size, alignment, NULL); } #else -void* operator new[](size_t JKR_HEAP_TOKEN_PARAM, int) { +void* operator new[](size_t JKR_HEAP_TOKEN_PARAM, int) IF_DUSK(noexcept) { OSPanic(__FILE__, __LINE__, "Allocation should go through JKR_NEW_ARRAY instead"); } #endif -void* operator new[](size_t JKR_HEAP_TOKEN_PARAM, JKRHeap*, int) { +void* operator new[](size_t JKR_HEAP_TOKEN_PARAM, JKRHeap*, int) IF_DUSK(noexcept) { OSPanic(__FILE__, __LINE__, "Allocation should go through JKR_NEW_ARRAY instead"); } @@ -624,7 +624,7 @@ void operator delete(void* ptr) { JKRHeap::free(ptr, NULL); } #else -void operator delete(void* ptr JKR_HEAP_TOKEN_PARAM) { +void operator delete(void* ptr JKR_HEAP_TOKEN_PARAM) IF_DUSK(noexcept) { if (ptr == NULL) return; JKRHeap* heap = JKRHeap::findFromRoot(ptr); @@ -645,7 +645,7 @@ void operator delete[](void* ptr) { JKRHeap::free(ptr, NULL); } #else -void operator delete[](void* ptr JKR_HEAP_TOKEN_PARAM) { +void operator delete[](void* ptr JKR_HEAP_TOKEN_PARAM) IF_DUSK(noexcept) { if (ptr == NULL) return; JKRHeap* heap = JKRHeap::findFromRoot(ptr); From 629cd2f9d3897a41fdb86eba9c53bda7c121e33c Mon Sep 17 00:00:00 2001 From: PJB3005 Date: Mon, 18 May 2026 02:16:43 +0200 Subject: [PATCH 3/3] Missed these in JASHeapCtrl.h --- libs/JSystem/include/JSystem/JAudio2/JASHeapCtrl.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/libs/JSystem/include/JSystem/JAudio2/JASHeapCtrl.h b/libs/JSystem/include/JSystem/JAudio2/JASHeapCtrl.h index 0378dc36b8..996fef6fef 100644 --- a/libs/JSystem/include/JSystem/JAudio2/JASHeapCtrl.h +++ b/libs/JSystem/include/JSystem/JAudio2/JASHeapCtrl.h @@ -402,28 +402,28 @@ template class JASPoolAllocObject_MultiThreaded { public: #if TARGET_PC - static void* operator new(size_t n, JKRHeapToken) { + static void* operator new(size_t n, JKRHeapToken) IF_DUSK(noexcept) { return operator new(n); } #endif - static void* operator new(size_t n) { + static void* operator new(size_t n) IF_DUSK(noexcept) { #if PLATFORM_GCN JASMemPool_MultiThreaded& memPool_ = getMemPool(); #endif return memPool_.alloc(n); } - static void* operator new(size_t n, void* ptr) { + static void* operator new(size_t n, void* ptr) IF_DUSK(noexcept) { return ptr; } #if TARGET_PC - static void operator delete(void* ptr, size_t n, JKRHeapToken) { + static void operator delete(void* ptr, size_t n, JKRHeapToken) IF_DUSK(noexcept) { return operator delete(ptr, n); } #endif - static void operator delete(void* ptr, size_t n) { + static void operator delete(void* ptr, size_t n) IF_DUSK(noexcept) { #if PLATFORM_GCN JASMemPool_MultiThreaded& memPool_ = getMemPool(); #endif