summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPhilip Chimento <philip.chimento@gmail.com>2022-11-14 22:01:59 -0800
committerPhilip Chimento <philip.chimento@gmail.com>2023-02-20 23:14:27 -0800
commit26ddbcfa4b3f24e4b7e8fcaec6e5662b946d4368 (patch)
tree3c2949cd95ca97ce50d038bcb876164c1ce1294d
parent4bfdaa1218c7ef9a57b1349d02da78fdb76c56f9 (diff)
downloadgjs-26ddbcfa4b3f24e4b7e8fcaec6e5662b946d4368.tar.gz
gobject: Guard against null JS wrapper in set/get property
The wrapper object may be disassociated from the GObject if dispose has been run. In that case, the pointers in the get/set property vfuncs may be null. Handle that case with a warning and don't get or set the property. Closes: #510
-rw-r--r--gi/gobject.cpp12
-rw-r--r--installed-tests/js/testIntrospection.js35
2 files changed, 47 insertions, 0 deletions
diff --git a/gi/gobject.cpp b/gi/gobject.cpp
index 914f6176..809e24cf 100644
--- a/gi/gobject.cpp
+++ b/gi/gobject.cpp
@@ -203,6 +203,12 @@ static void gjs_object_set_gproperty(GObject* object,
unsigned property_id [[maybe_unused]],
const GValue* value, GParamSpec* pspec) {
auto* priv = ObjectInstance::for_gobject(object);
+ if (!priv) {
+ g_warning("Wrapper for GObject %p was disposed, cannot set property %s",
+ object, g_param_spec_get_name(pspec));
+ return;
+ }
+
JSContext* cx = current_js_context();
JS::RootedObject js_obj(cx, priv->wrapper());
@@ -216,6 +222,12 @@ static void gjs_object_get_gproperty(GObject* object,
unsigned property_id [[maybe_unused]],
GValue* value, GParamSpec* pspec) {
auto* priv = ObjectInstance::for_gobject(object);
+ if (!priv) {
+ g_warning("Wrapper for GObject %p was disposed, cannot get property %s",
+ object, g_param_spec_get_name(pspec));
+ return;
+ }
+
JSContext* cx = current_js_context();
JS::RootedObject js_obj(cx, priv->wrapper());
diff --git a/installed-tests/js/testIntrospection.js b/installed-tests/js/testIntrospection.js
index 1037b50a..bdd4ddd4 100644
--- a/installed-tests/js/testIntrospection.js
+++ b/installed-tests/js/testIntrospection.js
@@ -140,6 +140,41 @@ describe('Garbage collection of introspected objects', function () {
System.gc();
GLib.idle_add(GLib.PRIORITY_LOW, () => done());
});
+
+ // This tests a race condition that would crash; it should warn instead
+ it('handles setting a property from C on an object whose JS wrapper has been collected', function (done) {
+ class SomeObject extends GObject.Object {
+ static [GObject.properties] = {
+ 'screenfull': GObject.ParamSpec.boolean('screenfull', '', '',
+ GObject.ParamFlags.READWRITE,
+ false),
+ };
+
+ static {
+ GObject.registerClass(this);
+ }
+ }
+
+ GLib.test_expect_message('Gjs', GLib.LogLevelFlags.LEVEL_WARNING,
+ '*property screenfull*');
+
+ const settings = new Gio.Settings({schema: 'org.gnome.GjsTest'});
+ let obj = new SomeObject();
+ settings.bind('fullscreen', obj, 'screenfull', Gio.SettingsBindFlags.DEFAULT);
+ const handler = settings.connect('changed::fullscreen', () => {
+ obj.run_dispose();
+ obj = null;
+ settings.disconnect(handler);
+ GLib.idle_add(GLib.PRIORITY_LOW, () => {
+ GLib.test_assert_expected_messages_internal('Gjs',
+ 'testIntrospection.js', 0,
+ 'Warn about setting property on disposed JS object');
+ done();
+ });
+ });
+ settings.set_boolean('fullscreen', !settings.get_boolean('fullscreen'));
+ settings.reset('fullscreen');
+ });
});
describe('Gdk.Atom', function () {