diff --git a/include/syntax/Field.hpp b/include/syntax/Field.hpp index 49040d2d..4826375c 100644 --- a/include/syntax/Field.hpp +++ b/include/syntax/Field.hpp @@ -1,5 +1,11 @@ #pragma once #include "Traits.hpp" +#include "../platform/platform.hpp" + +#ifdef GEODE_IS_WINDOWS +// for the manual virtualprotect +#include +#endif namespace geode::modifier { template @@ -77,20 +83,25 @@ namespace geode::modifier { return ret; } + using DestructorType = void(GEODE_WINDOWS(__thiscall)*)(void* GEODE_WINDOWS(, bool)); + template GEODE_NOINLINE inline auto& getOriginalDestructor() { - static void(*ret)(void*); + static DestructorType ret; return ret; } - template - GEODE_NOINLINE inline void fieldCleanup(T* self) {{ - for (auto& [mem, cont] : getAdditionalContainers()[self]) { - delete cont; - } - getAdditionalContainers().erase(self); - getOriginalDestructor()(self); - }} + // this is beautiful + struct FieldCleanupHook { + template + static GEODE_NOINLINE inline void GEODE_WINDOWS(__thiscall) fieldCleanup(T* self GEODE_WINDOWS(, bool arg)) {{ + for (auto& [mem, cont] : getAdditionalContainers()[self]) { + delete cont; + } + getAdditionalContainers().erase(self); + getOriginalDestructor()(self GEODE_WINDOWS(, arg)); + }} + }; template GEODE_NOINLINE T& operator->*(A* self, field& member) { @@ -102,10 +113,16 @@ namespace geode::modifier { // on first use if (destructor == nullptr) { - // takes the third element because all cocos classes have the destructor at third. - auto& vtableDestructor = 2[*(uintptr_t**)self]; // i love this - destructor = reinterpret_cast(vtableDestructor); - vtableDestructor = (uintptr_t)&fieldCleanup; + // classes inheriting CCObject always have their destructor(s) as the 2nd (and 3rd on itanium) element in the vtable + // choose 3rd elemnt (the second destructor) on itanium platforms because the first one gets inlined often + static constexpr size_t index = GEODE_WINDOWS(1) GEODE_MACOS(2) GEODE_IOS(2) GEODE_ANDROID(2); + auto& vtableDestructor = index[*reinterpret_cast(self)]; // i love this + destructor = reinterpret_cast(vtableDestructor); +#ifdef GEODE_IS_WINDOWS + DWORD old; + VirtualProtect(reinterpret_cast(&vtableDestructor), sizeof(void*), PAGE_EXECUTE_READWRITE, &old); +#endif + vtableDestructor = reinterpret_cast(&FieldCleanupHook::fieldCleanup); } if (containers[self].size() == 0) { void* obj = new A();