Skip to content

Commit 36a3b25

Browse files
beicauseIvorforce
andcommitted
Free script and extension instance before object deconstructing
Co-Authored-By: Lukas Tenbrink <[email protected]>
1 parent 112627a commit 36a3b25

File tree

2 files changed

+46
-39
lines changed

2 files changed

+46
-39
lines changed

core/object/object.cpp

Lines changed: 38 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -254,11 +254,45 @@ Object::Connection::Connection(const Variant &p_variant) {
254254
bool Object::_predelete() {
255255
_predelete_ok = 1;
256256
notification(NOTIFICATION_PREDELETE, true);
257-
if (_predelete_ok) {
258-
_class_name_ptr = nullptr; // Must restore, so constructors/destructors have proper class name access at each stage.
259-
notification(NOTIFICATION_PREDELETE_CLEANUP, true);
257+
if (!_predelete_ok) {
258+
return false;
259+
}
260+
261+
_class_name_ptr = nullptr; // Must restore, so constructors/destructors have proper class name access at each stage.
262+
notification(NOTIFICATION_PREDELETE_CLEANUP, true);
263+
264+
// Destruction order starts with the most derived class, and progresses towards the base Object class:
265+
// Script subclasses -> GDExtension subclasses -> C++ subclasses -> Object
266+
if (script_instance) {
267+
memdelete(script_instance);
260268
}
261-
return _predelete_ok;
269+
script_instance = nullptr;
270+
271+
if (_extension) {
272+
#ifdef TOOLS_ENABLED
273+
if (_extension->untrack_instance) {
274+
_extension->untrack_instance(_extension->tracking_userdata, this);
275+
}
276+
#endif
277+
if (_extension->free_instance) {
278+
_extension->free_instance(_extension->class_userdata, _extension_instance);
279+
}
280+
_extension = nullptr;
281+
_extension_instance = nullptr;
282+
}
283+
#ifdef TOOLS_ENABLED
284+
else if (_instance_bindings != nullptr) {
285+
Engine *engine = Engine::get_singleton();
286+
GDExtensionManager *gdextension_manager = GDExtensionManager::get_singleton();
287+
if (engine && gdextension_manager && engine->is_extension_reloading_enabled()) {
288+
for (uint32_t i = 0; i < _instance_binding_count; i++) {
289+
gdextension_manager->untrack_instance_binding(_instance_bindings[i].token, this);
290+
}
291+
}
292+
}
293+
#endif
294+
295+
return true;
262296
}
263297

264298
void Object::cancel_free() {
@@ -2273,35 +2307,6 @@ void Object::assign_class_name_static(const Span<char> &p_name, StringName &r_ta
22732307
}
22742308

22752309
Object::~Object() {
2276-
if (script_instance) {
2277-
memdelete(script_instance);
2278-
}
2279-
script_instance = nullptr;
2280-
2281-
if (_extension) {
2282-
#ifdef TOOLS_ENABLED
2283-
if (_extension->untrack_instance) {
2284-
_extension->untrack_instance(_extension->tracking_userdata, this);
2285-
}
2286-
#endif
2287-
if (_extension->free_instance) {
2288-
_extension->free_instance(_extension->class_userdata, _extension_instance);
2289-
}
2290-
_extension = nullptr;
2291-
_extension_instance = nullptr;
2292-
}
2293-
#ifdef TOOLS_ENABLED
2294-
else if (_instance_bindings != nullptr) {
2295-
Engine *engine = Engine::get_singleton();
2296-
GDExtensionManager *gdextension_manager = GDExtensionManager::get_singleton();
2297-
if (engine && gdextension_manager && engine->is_extension_reloading_enabled()) {
2298-
for (uint32_t i = 0; i < _instance_binding_count; i++) {
2299-
gdextension_manager->untrack_instance_binding(_instance_bindings[i].token, this);
2300-
}
2301-
}
2302-
}
2303-
#endif
2304-
23052310
if (_emitting) {
23062311
//@todo this may need to actually reach the debugger prioritarily somehow because it may crash before
23072312
ERR_PRINT(vformat("Object '%s' was freed or unreferenced while a signal is being emitted from it. Try connecting to the signal using 'CONNECT_DEFERRED' flag, or use queue_free() to free the object (if this object is a Node) to avoid this error and potential crashes.", to_string()));

tests/core/object/test_object.h

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -215,12 +215,12 @@ TEST_CASE("[Object] Construction") {
215215
}
216216

217217
TEST_CASE("[Object] Script instance property setter") {
218-
Object object;
218+
Object *object = memnew(Object);
219219
_MockScriptInstance *script_instance = memnew(_MockScriptInstance);
220-
object.set_script_instance(script_instance);
220+
object->set_script_instance(script_instance);
221221

222222
bool valid = false;
223-
object.set("some_name", 100, &valid);
223+
object->set("some_name", 100, &valid);
224224
CHECK(valid);
225225
Variant actual_value;
226226
CHECK_MESSAGE(
@@ -229,20 +229,22 @@ TEST_CASE("[Object] Script instance property setter") {
229229
CHECK_MESSAGE(
230230
actual_value == Variant(100),
231231
"The returned value should equal the one which was set by the object.");
232+
memdelete(object);
232233
}
233234

234235
TEST_CASE("[Object] Script instance property getter") {
235-
Object object;
236+
Object *object = memnew(Object);
236237
_MockScriptInstance *script_instance = memnew(_MockScriptInstance);
237238
script_instance->set("some_name", 100); // Make sure script instance has the property
238-
object.set_script_instance(script_instance);
239+
object->set_script_instance(script_instance);
239240

240241
bool valid = false;
241-
const Variant &actual_value = object.get("some_name", &valid);
242+
const Variant &actual_value = object->get("some_name", &valid);
242243
CHECK(valid);
243244
CHECK_MESSAGE(
244245
actual_value == Variant(100),
245246
"The returned value should equal the one which was set by the script instance.");
247+
memdelete(object);
246248
}
247249

248250
TEST_CASE("[Object] Built-in property setter") {

0 commit comments

Comments
 (0)