mirror of
https://github.com/godotengine/godot.git
synced 2025-10-19 16:03:29 +00:00
Free script and extension instance before object deconstructing
Co-Authored-By: Lukas Tenbrink <lukas.tenbrink@gmail.com>
This commit is contained in:
parent
ecfe34f56c
commit
727066fe1f
2 changed files with 46 additions and 39 deletions
|
@ -254,11 +254,45 @@ Object::Connection::Connection(const Variant &p_variant) {
|
||||||
bool Object::_predelete() {
|
bool Object::_predelete() {
|
||||||
_predelete_ok = 1;
|
_predelete_ok = 1;
|
||||||
notification(NOTIFICATION_PREDELETE, true);
|
notification(NOTIFICATION_PREDELETE, true);
|
||||||
if (_predelete_ok) {
|
if (!_predelete_ok) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
_class_name_ptr = nullptr; // Must restore, so constructors/destructors have proper class name access at each stage.
|
_class_name_ptr = nullptr; // Must restore, so constructors/destructors have proper class name access at each stage.
|
||||||
notification(NOTIFICATION_PREDELETE_CLEANUP, true);
|
notification(NOTIFICATION_PREDELETE_CLEANUP, true);
|
||||||
|
|
||||||
|
// Destruction order starts with the most derived class, and progresses towards the base Object class:
|
||||||
|
// Script subclasses -> GDExtension subclasses -> C++ subclasses -> Object
|
||||||
|
if (script_instance) {
|
||||||
|
memdelete(script_instance);
|
||||||
}
|
}
|
||||||
return _predelete_ok;
|
script_instance = nullptr;
|
||||||
|
|
||||||
|
if (_extension) {
|
||||||
|
#ifdef TOOLS_ENABLED
|
||||||
|
if (_extension->untrack_instance) {
|
||||||
|
_extension->untrack_instance(_extension->tracking_userdata, this);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
if (_extension->free_instance) {
|
||||||
|
_extension->free_instance(_extension->class_userdata, _extension_instance);
|
||||||
|
}
|
||||||
|
_extension = nullptr;
|
||||||
|
_extension_instance = nullptr;
|
||||||
|
}
|
||||||
|
#ifdef TOOLS_ENABLED
|
||||||
|
else if (_instance_bindings != nullptr) {
|
||||||
|
Engine *engine = Engine::get_singleton();
|
||||||
|
GDExtensionManager *gdextension_manager = GDExtensionManager::get_singleton();
|
||||||
|
if (engine && gdextension_manager && engine->is_extension_reloading_enabled()) {
|
||||||
|
for (uint32_t i = 0; i < _instance_binding_count; i++) {
|
||||||
|
gdextension_manager->untrack_instance_binding(_instance_bindings[i].token, this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Object::cancel_free() {
|
void Object::cancel_free() {
|
||||||
|
@ -2273,35 +2307,6 @@ void Object::assign_class_name_static(const Span<char> &p_name, StringName &r_ta
|
||||||
}
|
}
|
||||||
|
|
||||||
Object::~Object() {
|
Object::~Object() {
|
||||||
if (script_instance) {
|
|
||||||
memdelete(script_instance);
|
|
||||||
}
|
|
||||||
script_instance = nullptr;
|
|
||||||
|
|
||||||
if (_extension) {
|
|
||||||
#ifdef TOOLS_ENABLED
|
|
||||||
if (_extension->untrack_instance) {
|
|
||||||
_extension->untrack_instance(_extension->tracking_userdata, this);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
if (_extension->free_instance) {
|
|
||||||
_extension->free_instance(_extension->class_userdata, _extension_instance);
|
|
||||||
}
|
|
||||||
_extension = nullptr;
|
|
||||||
_extension_instance = nullptr;
|
|
||||||
}
|
|
||||||
#ifdef TOOLS_ENABLED
|
|
||||||
else if (_instance_bindings != nullptr) {
|
|
||||||
Engine *engine = Engine::get_singleton();
|
|
||||||
GDExtensionManager *gdextension_manager = GDExtensionManager::get_singleton();
|
|
||||||
if (engine && gdextension_manager && engine->is_extension_reloading_enabled()) {
|
|
||||||
for (uint32_t i = 0; i < _instance_binding_count; i++) {
|
|
||||||
gdextension_manager->untrack_instance_binding(_instance_bindings[i].token, this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (_emitting) {
|
if (_emitting) {
|
||||||
//@todo this may need to actually reach the debugger prioritarily somehow because it may crash before
|
//@todo this may need to actually reach the debugger prioritarily somehow because it may crash before
|
||||||
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()));
|
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()));
|
||||||
|
|
|
@ -215,12 +215,12 @@ TEST_CASE("[Object] Construction") {
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("[Object] Script instance property setter") {
|
TEST_CASE("[Object] Script instance property setter") {
|
||||||
Object object;
|
Object *object = memnew(Object);
|
||||||
_MockScriptInstance *script_instance = memnew(_MockScriptInstance);
|
_MockScriptInstance *script_instance = memnew(_MockScriptInstance);
|
||||||
object.set_script_instance(script_instance);
|
object->set_script_instance(script_instance);
|
||||||
|
|
||||||
bool valid = false;
|
bool valid = false;
|
||||||
object.set("some_name", 100, &valid);
|
object->set("some_name", 100, &valid);
|
||||||
CHECK(valid);
|
CHECK(valid);
|
||||||
Variant actual_value;
|
Variant actual_value;
|
||||||
CHECK_MESSAGE(
|
CHECK_MESSAGE(
|
||||||
|
@ -229,20 +229,22 @@ TEST_CASE("[Object] Script instance property setter") {
|
||||||
CHECK_MESSAGE(
|
CHECK_MESSAGE(
|
||||||
actual_value == Variant(100),
|
actual_value == Variant(100),
|
||||||
"The returned value should equal the one which was set by the object.");
|
"The returned value should equal the one which was set by the object.");
|
||||||
|
memdelete(object);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("[Object] Script instance property getter") {
|
TEST_CASE("[Object] Script instance property getter") {
|
||||||
Object object;
|
Object *object = memnew(Object);
|
||||||
_MockScriptInstance *script_instance = memnew(_MockScriptInstance);
|
_MockScriptInstance *script_instance = memnew(_MockScriptInstance);
|
||||||
script_instance->set("some_name", 100); // Make sure script instance has the property
|
script_instance->set("some_name", 100); // Make sure script instance has the property
|
||||||
object.set_script_instance(script_instance);
|
object->set_script_instance(script_instance);
|
||||||
|
|
||||||
bool valid = false;
|
bool valid = false;
|
||||||
const Variant &actual_value = object.get("some_name", &valid);
|
const Variant &actual_value = object->get("some_name", &valid);
|
||||||
CHECK(valid);
|
CHECK(valid);
|
||||||
CHECK_MESSAGE(
|
CHECK_MESSAGE(
|
||||||
actual_value == Variant(100),
|
actual_value == Variant(100),
|
||||||
"The returned value should equal the one which was set by the script instance.");
|
"The returned value should equal the one which was set by the script instance.");
|
||||||
|
memdelete(object);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("[Object] Built-in property setter") {
|
TEST_CASE("[Object] Built-in property setter") {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue