diff --git a/gi/object.cpp b/gi/object.cpp index 64a9a867207ec8e96d6cc7cd4314d2ffbf6c3263..a0fc16a97c5244bd98d07d62d75d10332f69fc89 100644 --- a/gi/object.cpp +++ b/gi/object.cpp @@ -1668,6 +1668,16 @@ real_connect_func(JSContext *context, priv->info ? g_base_info_get_name( (GIBaseInfo*) priv->info) : g_type_name(priv->gtype)); return false; } + if (priv->g_object_finalized) { + g_critical("Object %s.%s (%p), has been already deallocated - impossible to connect to signal. " + "This might be caused by the fact that the object has been destroyed from C " + "code using something such as destroy(), dispose(), or remove() vfuncs", + priv->info ? g_base_info_get_namespace( (GIBaseInfo*) priv->info) : "", + priv->info ? g_base_info_get_name( (GIBaseInfo*) priv->info) : g_type_name(priv->gtype), + priv->gobj); + gjs_dumpstack(); + return true; + } if (argc != 2 || !argv[0].isString() || !JS::IsCallable(&argv[1].toObject())) { gjs_throw(context, "connect() takes two args, the signal name and the callback"); @@ -1751,6 +1761,17 @@ emit_func(JSContext *context, return false; } + if (priv->g_object_finalized) { + g_critical("Object %s.%s (%p), has been already deallocated - impossible to emit signal. " + "This might be caused by the fact that the object has been destroyed from C " + "code using something such as destroy(), dispose(), or remove() vfuncs", + priv->info ? g_base_info_get_namespace( (GIBaseInfo*) priv->info) : "", + priv->info ? g_base_info_get_name( (GIBaseInfo*) priv->info) : g_type_name(priv->gtype), + priv->gobj); + gjs_dumpstack(); + return true; + } + if (argc < 1 || !argv[0].isString()) { gjs_throw(context, "emit() first arg is the signal name"); return false; @@ -1841,7 +1862,9 @@ to_string_func(JSContext *context, return false; /* wrong class passed in */ } - return _gjs_proxy_to_string_func(context, obj, "object", + return _gjs_proxy_to_string_func(context, obj, + (priv->g_object_finalized) ? + "object (FINALIZED)" : "object", (GIBaseInfo*)priv->info, priv->gtype, priv->gobj, rec.rval()); } diff --git a/gi/proxyutils.cpp b/gi/proxyutils.cpp index 3894ec838bd5943ad2645166cb0c33ed999aed82..d90aa4d01c4dadcf2dfa9212176abbd34b56dc91 100644 --- a/gi/proxyutils.cpp +++ b/gi/proxyutils.cpp @@ -63,7 +63,7 @@ _gjs_proxy_to_string_func(JSContext *context, g_string_append_printf(buf, " jsobj@%p", this_obj); if (native_address != NULL) g_string_append_printf(buf, " native@%p", native_address); - + g_string_append_c(buf, ']'); if (!gjs_string_from_utf8(context, buf->str, rval)) diff --git a/installed-tests/js/testGObjectDestructionAccess.js b/installed-tests/js/testGObjectDestructionAccess.js index 3e30e6e484038d282289de223c5836cfd6bbcce7..e479ff9abe59d22100efd21a2954bc153ba157d6 100644 --- a/installed-tests/js/testGObjectDestructionAccess.js +++ b/installed-tests/js/testGObjectDestructionAccess.js @@ -55,4 +55,51 @@ describe('Access to destroyed GObject', () => { GLib.test_assert_expected_messages_internal('Gjs', 'testGObjectDestructionAccess.js', 0, 'testExceptionInDestroyedObjectMethodSet'); }); + + it('Proto function connect', () => { + GLib.test_expect_message('Gjs', GLib.LogLevelFlags.LEVEL_CRITICAL, + 'Object Gtk.Window (0x*'); + + destroyedWindow.connect('foo-signal', () => {}); + + GLib.test_assert_expected_messages_internal('Gjs', 'testGObjectDestructionAccess.js', 0, + 'testExceptionInDestroyedObjectConnect'); + }); + + it('Proto function connect_after', () => { + GLib.test_expect_message('Gjs', GLib.LogLevelFlags.LEVEL_CRITICAL, + 'Object Gtk.Window (0x*'); + + destroyedWindow.connect_after('foo-signal', () => {}); + + GLib.test_assert_expected_messages_internal('Gjs', 'testGObjectDestructionAccess.js', 0, + 'testExceptionInDestroyedObjectConnectAfter'); + }); + + it('Proto function emit', () => { + GLib.test_expect_message('Gjs', GLib.LogLevelFlags.LEVEL_CRITICAL, + 'Object Gtk.Window (0x*'); + + destroyedWindow.emit('foo-signal'); + + GLib.test_assert_expected_messages_internal('Gjs', 'testGObjectDestructionAccess.js', 0, + 'testExceptionInDestroyedObjectEmit'); + }); + + it('Proto function toString', () => { + expect(destroyedWindow.toString()).toMatch( + /\[object \(FINALIZED\) instance proxy GIName:Gtk.Window jsobj@0x[a-f0-9]+ native@0x[a-f0-9]+\]/); + }); + + it('Porto function toString before/after', () => { + var validWindow = new Gtk.Window({type: Gtk.WindowType.TOPLEVEL}); + + expect(validWindow.toString()).toMatch( + /\[object instance proxy GIName:Gtk.Window jsobj@0x[a-f0-9]+ native@0x[a-f0-9]+\]/); + + validWindow.destroy(); + + expect(validWindow.toString()).toMatch( + /\[object \(FINALIZED\) instance proxy GIName:Gtk.Window jsobj@0x[a-f0-9]+ native@0x[a-f0-9]+\]/); + }); });