Skip to content

Commit 9667916

Browse files
authored
Destructor fix (#1017)
* doesnt work rn, idk how to replace dllimported functions unlike other platforms * fix for windows * make the implementation less hacky & add dll2lib py
1 parent 20f710c commit 9667916

File tree

7 files changed

+170
-102
lines changed

7 files changed

+170
-102
lines changed

bindings/2.2074/Cocos2d.bro

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1948,6 +1948,7 @@ class cocos2d::CCFadeTo : cocos2d::CCActionInterval {
19481948
class cocos2d::CCFileUtils : cocos2d::TypeInfo {
19491949
// CCFileUtils(); // one of these two is 0x15bfa8
19501950
// CCFileUtils(cocos2d::CCFileUtils const&);
1951+
~CCFileUtils();
19511952
virtual void addSearchPath(char const*) = m1 0x3a42d4, imac 0x4294e0, ios 0x155a30;
19521953
virtual void addSearchResolutionsOrder(char const*) = imac 0x428f30, m1 0x3a3d24, ios 0x155538;
19531954
virtual gd::string addSuffix(gd::string, gd::string) = imac 0x4273f0, m1 0x3a21f4, ios 0x15459c;
@@ -2269,6 +2270,7 @@ class cocos2d::CCScheduler : cocos2d::CCObject {
22692270

22702271
// CCScheduler(cocos2d::CCScheduler const&);
22712272
// CCScheduler();
2273+
~CCScheduler();
22722274

22732275
void appendIn(cocos2d::_listEntry**, cocos2d::CCObject*, bool);
22742276
bool isTargetPaused(cocos2d::CCObject*);

codegen/src/BindingGen.cpp

Lines changed: 27 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -93,21 +93,32 @@ std::string generateAddressDocs(T const& f, PlatformNumber pn) {
9393
for (auto platform : {Platform::MacArm, Platform::MacIntel, Platform::Windows, Platform::iOS, Platform::Android}) {
9494
auto status = codegen::getStatusWithPlatform(platform, f);
9595

96-
if (status == BindStatus::NeedsBinding) {
97-
ret += fmt::format(" * @note[short] {}: 0x{:x}\n",
98-
nameForPlatform(platform),
99-
codegen::platformNumberWithPlatform(platform, pn)
100-
);
101-
}
102-
else if (status == BindStatus::Binded) {
103-
ret += fmt::format(" * @note[short] {}\n",
104-
nameForPlatform(platform)
105-
);
106-
}
107-
else if (status == BindStatus::Inlined) {
108-
ret += fmt::format(" * @note[short] {}: Out of line\n",
109-
nameForPlatform(platform)
110-
);
96+
auto num = codegen::platformNumberWithPlatform(platform, pn);
97+
98+
switch (status) {
99+
case BindStatus::NeedsBinding:
100+
ret += fmt::format(" * @note[short] {}: 0x{:x}\n",
101+
nameForPlatform(platform),
102+
num
103+
);
104+
break;
105+
case BindStatus::NeedsRebinding:
106+
ret += fmt::format(" * @note[short] {}: Rebinded\n",
107+
nameForPlatform(platform)
108+
);
109+
break;
110+
case BindStatus::Binded:
111+
ret += fmt::format(" * @note[short] {}\n",
112+
nameForPlatform(platform)
113+
);
114+
break;
115+
case BindStatus::Inlined:
116+
ret += fmt::format(" * @note[short] {}: Out of line\n",
117+
nameForPlatform(platform)
118+
);
119+
break;
120+
default:
121+
break;
111122
}
112123
}
113124

@@ -267,7 +278,7 @@ std::string generateBindingHeader(Root const& root, std::filesystem::path const&
267278

268279
fb = &fn->prototype;
269280

270-
if (codegen::platformNumber(fn->binds) == -1 && codegen::getStatus(*fn) != BindStatus::Binded && fb->type != FunctionType::Normal) {
281+
if (codegen::platformNumber(fn->binds) == -1 && codegen::getStatus(*fn) == BindStatus::NeedsBinding) {
271282
continue;
272283
}
273284

codegen/src/JsonInterfaceGen.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ std::string generateTextInterface(Root const& root) {
2828
// skip unbinded functions
2929
continue;
3030
}
31-
else if (status != BindStatus::NeedsBinding && !codegen::shouldAndroidBind(fn)) {
31+
else if (status != BindStatus::NeedsBinding) {
3232
continue;
3333
}
3434
output += fmt::format("{}::{} - {:#x}\n", c.name, fn->prototype.name, codegen::platformNumber(fn->binds));

codegen/src/ModifyGen.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ std::string generateModifyHeader(Root const& root, std::filesystem::path const&
158158
else if (status == BindStatus::Inlined && fn->prototype.type == FunctionType::Normal || codegen::platformNumber(fn->binds) == 0x9999999) {
159159
format_string = format_strings::apply_error_inline;
160160
}
161-
else if ((status == BindStatus::NeedsBinding && codegen::platformNumber(f) > 0) || status == BindStatus::Binded) {
161+
else if ((status == BindStatus::NeedsBinding && codegen::platformNumber(f) > 0) || status == BindStatus::Binded || status == BindStatus::NeedsRebinding) {
162162
// only if has an address
163163
// allow bound functions (including ctors/dtors)
164164
switch (fn->prototype.type) {

codegen/src/Shared.hpp

Lines changed: 58 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,12 @@ inline bool is_cocos_class(std::string const& str) {
6161
return can_find(str, "cocos2d") || can_find(str, "pugi::") || str == "DS_Dictionary" || str == "ObjectDecoder" || str == "ObjectDecoderDelegate" || str == "CCContentManager";
6262
}
6363

64+
inline bool is_in_extensions_dll(std::string const& str) {
65+
return can_find(str, "cocos2d::extension");
66+
}
67+
6468
inline bool is_in_cocos_dll(std::string const& str) {
65-
return is_cocos_class(str) && !can_find(str, "CCLightning");
69+
return is_cocos_class(str) && !can_find(str, "CCLightning") && !is_in_extensions_dll(str);
6670
}
6771

6872
inline bool is_fmod_class(std::string const& str) {
@@ -79,6 +83,7 @@ enum class BindStatus {
7983
NeedsBinding,
8084
Unbindable,
8185
Missing,
86+
NeedsRebinding,
8287
};
8388

8489
struct codegen_error : std::runtime_error {
@@ -207,7 +212,17 @@ namespace codegen {
207212
if (platformNumberWithPlatform(p, fn.binds) == -2) return BindStatus::Inlined;
208213

209214
if ((fn.prototype.attributes.missing & p) != Platform::None || codegen::sdkVersion < fn.prototype.attributes.since) return BindStatus::Missing;
210-
if ((fn.prototype.attributes.links & p) != Platform::None) return BindStatus::Binded;
215+
if ((fn.prototype.attributes.links & p) != Platform::None) {
216+
if (fn.prototype.type != FunctionType::Normal) return BindStatus::NeedsRebinding;
217+
218+
if ((int)p & (int)Platform::Android) {
219+
for (auto& [type, name] : fn.prototype.args) {
220+
if (can_find(type.name, "gd::")) return BindStatus::NeedsRebinding;
221+
}
222+
}
223+
224+
return BindStatus::Binded;
225+
}
211226

212227
if (platformNumberWithPlatform(p, fn.binds) != -1) return BindStatus::NeedsBinding;
213228

@@ -225,17 +240,6 @@ namespace codegen {
225240
return BindStatus::Unbindable;
226241
}
227242

228-
inline bool shouldAndroidBind(const FunctionBindField* fn) {
229-
if (codegen::platform == Platform::Android32 || codegen::platform == Platform::Android64) {
230-
if (sdkVersion < fn->prototype.attributes.since) return false;
231-
if (fn->prototype.type != FunctionType::Normal) return true;
232-
for (auto& [type, name] : fn->prototype.args) {
233-
if (can_find(type.name, "gd::")) return true;
234-
}
235-
}
236-
return false;
237-
}
238-
239243
inline BindStatus getStatus(FunctionBindField const& fn) {
240244
return getStatusWithPlatform(codegen::platform, fn);
241245
}
@@ -285,14 +289,14 @@ namespace codegen {
285289
if (codegen::platformArch == PlatformArch::x86) {
286290
// for windows x86, aka versions before 2.206
287291
if (fn->prototype.is_static) {
288-
if (status == BindStatus::Binded) return "Cdecl";
292+
if (status == BindStatus::Binded || status == BindStatus::NeedsRebinding) return "Cdecl";
289293
else return "Optcall";
290294
}
291295
else if (fn->prototype.is_virtual || fn->prototype.is_callback) {
292296
return "Thiscall";
293297
}
294298
else {
295-
if (status == BindStatus::Binded) return "Thiscall";
299+
if (status == BindStatus::Binded || status == BindStatus::NeedsRebinding) return "Thiscall";
296300
else return "Membercall";
297301
}
298302
} else {
@@ -350,35 +354,50 @@ namespace codegen {
350354

351355
inline std::string getAddressString(Class const& c, Field const& field) {
352356
if (auto fn = field.get_as<FunctionBindField>()) {
353-
const auto isWindowsCocosCtor = [&] {
354-
return codegen::platform == Platform::Windows
355-
&& is_cocos_class(field.parent)
356-
// && codegen::getStatus(field) == BindStatus::Binded
357-
&& fn->prototype.type != FunctionType::Normal;
358-
};
359-
360-
if (codegen::getStatus(*fn) == BindStatus::NeedsBinding || codegen::platformNumber(field) != -1) {
361-
if (is_in_cocos_dll(field.parent) && codegen::platform == Platform::Windows) {
362-
return fmt::format("base::getCocos() + 0x{:x}", codegen::platformNumber(fn->binds));
357+
if (codegen::getStatus(*fn) == BindStatus::NeedsRebinding) {
358+
if ((int)codegen::platform & (int)Platform::Android) {
359+
auto const mangled = generateAndroidSymbol(c, fn);
360+
return fmt::format( // thumb
361+
"reinterpret_cast<uintptr_t>(dlsym(dlopen(\"libcocos2dcpp.so\", RTLD_NOW), \"{}\"))",
362+
mangled
363+
);
364+
}
365+
else if (codegen::platform == Platform::Windows) {
366+
auto const mangled = generateWindowsSymbol(c, fn);
367+
if (is_in_cocos_dll(field.parent)) {
368+
return fmt::format(
369+
"reinterpret_cast<uintptr_t>(GetProcAddress((HMODULE)base::getCocos(), \"{}\"))",
370+
mangled
371+
);
372+
}
373+
else if (is_in_extensions_dll(field.parent)) {
374+
return fmt::format(
375+
"reinterpret_cast<uintptr_t>(GetProcAddress((HMODULE)base::getExtensions(), \"{}\"))",
376+
mangled
377+
);
378+
}
379+
else {
380+
// Uhhhh im not sure
381+
return "(void*)0x9911991122";
382+
}
383+
}
384+
}
385+
else if (codegen::getStatus(*fn) == BindStatus::NeedsBinding || codegen::platformNumber(field) != -1) {
386+
if (codegen::platform == Platform::Windows) {
387+
if (is_in_cocos_dll(field.parent)) {
388+
return fmt::format("base::getCocos() + 0x{:x}", codegen::platformNumber(fn->binds));
389+
}
390+
else if (is_in_extensions_dll(field.parent)) {
391+
return fmt::format("base::getExtensions() + 0x{:x}", codegen::platformNumber(fn->binds));
392+
}
393+
else {
394+
return fmt::format("base::get() + 0x{:x}", codegen::platformNumber(fn->binds));
395+
}
363396
}
364397
else {
365398
return fmt::format("base::get() + 0x{:x}", codegen::platformNumber(fn->binds));
366399
}
367400
}
368-
else if (codegen::shouldAndroidBind(fn)) {
369-
auto const mangled = generateAndroidSymbol(c, fn);
370-
return fmt::format( // thumb
371-
"reinterpret_cast<uintptr_t>(dlsym(dlopen(\"libcocos2dcpp.so\", RTLD_NOW), \"{}\"))",
372-
mangled
373-
);
374-
}
375-
else if (isWindowsCocosCtor()) {
376-
auto const mangled = generateWindowsSymbol(c, fn);
377-
return fmt::format(
378-
"reinterpret_cast<uintptr_t>(GetProcAddress(GetModuleHandleA(\"libcocos2d.dll\"), \"{}\"))",
379-
mangled
380-
);
381-
}
382401
else if (codegen::getStatus(*fn) == BindStatus::Binded && fn->prototype.type == FunctionType::Normal) {
383402
return fmt::format(
384403
"addresser::get{}Virtual(Resolve<{}>::func(&{}::{}))",

codegen/src/SourceGen.cpp

Lines changed: 34 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -10,22 +10,6 @@ namespace { namespace format_strings {
1010
1111
using namespace geode;
1212
using namespace geode::modifier;
13-
using cocos2d::CCDestructor;
14-
15-
std::unordered_map<void*, bool>& CCDestructor::destructorLock() {{
16-
static thread_local std::unordered_map<void*, bool> ret;
17-
return ret;
18-
}}
19-
bool& CCDestructor::globalLock() {{
20-
static thread_local bool ret = false;
21-
return ret;
22-
}}
23-
bool& CCDestructor::lock(void* self) {
24-
return destructorLock()[self];
25-
}
26-
CCDestructor::~CCDestructor() {{
27-
destructorLock().erase(this);
28-
}}
2913
3014
auto wrapFunction(uintptr_t address, tulip::hook::WrapperMetadata const& metadata) {
3115
auto wrapped = geode::hook::createWrapper(reinterpret_cast<void*>(address), metadata);
@@ -34,11 +18,6 @@ auto wrapFunction(uintptr_t address, tulip::hook::WrapperMetadata const& metadat
3418
}}
3519
return wrapped.unwrap();
3620
}
37-
38-
// So apparently Clang considers cdecl to return floats through ST0, whereas
39-
// MSVC thinks they are returned through XMM0. This has caused a lot of pain
40-
// and misery for me
41-
4221
)GEN";
4322

4423
constexpr char const* declare_member = R"GEN(
@@ -79,32 +58,37 @@ auto {class_name}::{function_name}({parameters}){const} -> decltype({function_na
7958
{class_name}::{function_name}({parameters}) {{
8059
// basically we destruct it once by calling the gd function,
8160
// then lock it, so that other gd destructors dont get called
82-
if (CCDestructor::lock(this)) return;
83-
using FunctionType = void(*)({class_name}*{parameter_comma}{parameter_types});
84-
static auto func = wrapFunction({address_inline}, tulip::hook::WrapperMetadata{{
85-
.m_convention = geode::hook::createConvention(tulip::hook::TulipConvention::{convention}),
86-
.m_abstract = tulip::hook::AbstractFunction::from(FunctionType(nullptr)),
87-
}});
88-
reinterpret_cast<FunctionType>(func)(this{parameter_comma}{arguments});
89-
// we need to construct it back so that it uhhh ummm doesnt crash
90-
// while going to the child destructors
91-
auto thing = new (this) {class_name}(geode::CutoffConstructor, sizeof({class_name}));
92-
CCDestructor::lock(this) = true;
61+
if (!geode::DestructorLock::isLocked(this)) {{
62+
using FunctionType = void(*)({class_name}*{parameter_comma}{parameter_types});
63+
static auto func = wrapFunction({address_inline}, tulip::hook::WrapperMetadata{{
64+
.m_convention = geode::hook::createConvention(tulip::hook::TulipConvention::{convention}),
65+
.m_abstract = tulip::hook::AbstractFunction::from(FunctionType(nullptr)),
66+
}});
67+
reinterpret_cast<FunctionType>(func)(this{parameter_comma}{arguments});
68+
69+
// we need to construct it back so that it uhhh ummm doesnt crash
70+
// while going to the child destructors
71+
auto thing = new (this) {class_name}(geode::CutoffConstructor, sizeof({class_name}));
72+
geode::DestructorLock::addLock(this);
73+
}}
9374
}}
9475
)GEN";
9576

9677
constexpr char const* declare_destructor_baseless = R"GEN(
9778
{class_name}::{function_name}({parameters}) {{
9879
// basically we destruct it once by calling the gd function,
99-
// then lock it, so that other gd destructors dont get called
100-
if (CCDestructor::lock(this)) return;
101-
using FunctionType = void(*)({class_name}*{parameter_comma}{parameter_types});
102-
static auto func = wrapFunction({address_inline}, tulip::hook::WrapperMetadata{{
103-
.m_convention = geode::hook::createConvention(tulip::hook::TulipConvention::{convention}),
104-
.m_abstract = tulip::hook::AbstractFunction::from(FunctionType(nullptr)),
105-
}});
106-
reinterpret_cast<FunctionType>(func)(this{parameter_comma}{arguments});
107-
CCDestructor::lock(this) = true;
80+
// then we release the lock because there are no other destructors after this
81+
if (!geode::DestructorLock::isLocked(this)) {{
82+
using FunctionType = void(*)({class_name}*{parameter_comma}{parameter_types});
83+
static auto func = wrapFunction({address_inline}, tulip::hook::WrapperMetadata{{
84+
.m_convention = geode::hook::createConvention(tulip::hook::TulipConvention::{convention}),
85+
.m_abstract = tulip::hook::AbstractFunction::from(FunctionType(nullptr)),
86+
}});
87+
reinterpret_cast<FunctionType>(func)(this{parameter_comma}{arguments});
88+
}}
89+
else {{
90+
geode::DestructorLock::removeLock(this);
91+
}}
10892
}}
10993
)GEN";
11094

@@ -113,8 +97,9 @@ auto {class_name}::{function_name}({parameters}){const} -> decltype({function_na
11397
// here we construct it as normal as we can, then destruct it
11498
// using the generated functions. this ensures no memory gets leaked
11599
// no crashes :pray:
116-
CCDestructor::lock(this) = true;
100+
geode::DestructorLock::addLock(this);
117101
{class_name}::~{unqualified_class_name}();
102+
118103
using FunctionType = void(*)({class_name}*{parameter_comma}{parameter_types});
119104
static auto func = wrapFunction({address_inline}, tulip::hook::WrapperMetadata{{
120105
.m_convention = geode::hook::createConvention(tulip::hook::TulipConvention::{convention}),
@@ -165,6 +150,10 @@ auto {class_name}::{function_name}({parameters}){const} -> decltype({function_na
165150
)GEN";
166151
}}
167152

153+
bool areSuperclassesEmpty(Class const& c) {
154+
return c.superclasses.empty() || (c.superclasses.size() == 1 && c.superclasses[0].find("CCCopying") != std::string::npos);
155+
}
156+
168157
std::string generateBindingSource(Root const& root, bool skipPugixml) {
169158
std::string output(format_strings::source_start);
170159

@@ -237,7 +226,7 @@ std::string generateBindingSource(Root const& root, bool skipPugixml) {
237226
if (codegen::platformNumber(fn->binds) == 0x9999999) {
238227
used_declare_format = format_strings::declare_unimplemented_error;
239228
}
240-
else if (codegen::getStatus(*fn) != BindStatus::NeedsBinding && !codegen::shouldAndroidBind(fn)) {
229+
else if (codegen::getStatus(*fn) != BindStatus::NeedsBinding && codegen::getStatus(*fn) != BindStatus::NeedsRebinding) {
241230
continue;
242231
}
243232

@@ -247,15 +236,15 @@ std::string generateBindingSource(Root const& root, bool skipPugixml) {
247236
used_declare_format = format_strings::declare_member;
248237
break;
249238
case FunctionType::Ctor:
250-
if (c.superclasses.empty()) {
239+
if (areSuperclassesEmpty(c)) {
251240
used_declare_format = format_strings::declare_constructor_begin;
252241
}
253242
else {
254243
used_declare_format = format_strings::declare_constructor;
255244
}
256245
break;
257246
case FunctionType::Dtor:
258-
used_declare_format = c.superclasses.empty() ? format_strings::declare_destructor_baseless : format_strings::declare_destructor;
247+
used_declare_format = areSuperclassesEmpty(c) ? format_strings::declare_destructor_baseless : format_strings::declare_destructor;
259248
break;
260249
}
261250

0 commit comments

Comments
 (0)