diff --git a/0001-GObject-Don-t-autogenerate-accessors-for-CONSTRUCT_O.patch b/0001-GObject-Don-t-autogenerate-accessors-for-CONSTRUCT_O.patch new file mode 100644 index 0000000..0d54750 --- /dev/null +++ b/0001-GObject-Don-t-autogenerate-accessors-for-CONSTRUCT_O.patch @@ -0,0 +1,58 @@ +From 1572849a875632a84bec664b2acb41fee54a72c2 Mon Sep 17 00:00:00 2001 +From: Philip Chimento +Date: Sun, 21 Mar 2021 11:32:52 -0700 +Subject: [PATCH 2/6] GObject: Don't autogenerate accessors for CONSTRUCT_ONLY + properties + +Since we redefine CONSTRUCT_ONLY properties as readonly data properties +when they are set, the autogenerated accessors would be wrong. + +See: #391 +--- + installed-tests/js/testGObjectClass.js | 7 +++++++ + modules/core/_common.js | 2 ++ + 2 files changed, 9 insertions(+) + +diff --git a/installed-tests/js/testGObjectClass.js b/installed-tests/js/testGObjectClass.js +index 4cf3a867..f0a57a84 100644 +--- a/installed-tests/js/testGObjectClass.js ++++ b/installed-tests/js/testGObjectClass.js +@@ -766,6 +766,10 @@ describe('Auto accessor generation', function () { + 'Long-named property', GObject.ParamFlags.READWRITE, 0, 100, 48), + 'construct': GObject.ParamSpec.int('construct', 'Construct', 'Construct', + GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT, 0, 100, 96), ++ 'construct-only': GObject.ParamSpec.int('construct-only', 'Construct only', ++ 'Construct-only property', ++ GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY, ++ 0, 100, 80), + 'snake-name': GObject.ParamSpec.int('snake-name', 'Snake name', + 'Snake-cased property', GObject.ParamFlags.READWRITE, 0, 100, 36), + 'camel-name': GObject.ParamSpec.int('camel-name', 'Camel name', +@@ -844,8 +848,11 @@ describe('Auto accessor generation', function () { + + it("initial value is the param spec's default value", function () { + expect(a.simple).toEqual(24); ++ expect(a.long_long_name).toEqual(48); ++ expect(a.longLongName).toEqual(48); + expect(a['long-long-name']).toEqual(48); + expect(a.construct).toEqual(96); ++ expect(a.construct_only).toEqual(80); + }); + + it('notify when the property changes', function () { +diff --git a/modules/core/_common.js b/modules/core/_common.js +index 45bbefb7..edc70215 100644 +--- a/modules/core/_common.js ++++ b/modules/core/_common.js +@@ -59,6 +59,8 @@ function _generateAccessors(pspec, propdesc, GObject) { + + function _checkAccessors(proto, pspec, GObject) { + const {name, flags} = pspec; ++ if (flags & GObject.ParamFlags.CONSTRUCT_ONLY) ++ return; + + const underscoreName = name.replace(/-/g, '_'); + const camelName = name.replace(/-([a-z])/g, match => match[1].toUpperCase()); +-- +2.30.2 + diff --git a/0002-GObject-Define-camel-and-kebab-variants-of-CONSTRUCT.patch b/0002-GObject-Define-camel-and-kebab-variants-of-CONSTRUCT.patch new file mode 100644 index 0000000..b203743 --- /dev/null +++ b/0002-GObject-Define-camel-and-kebab-variants-of-CONSTRUCT.patch @@ -0,0 +1,124 @@ +From c4231d5917b1a06d1e3b788322c71cfdb41a0249 Mon Sep 17 00:00:00 2001 +From: Philip Chimento +Date: Sun, 21 Mar 2021 11:37:58 -0700 +Subject: [PATCH 3/6] GObject: Define camel and kebab variants of + CONSTRUCT_ONLY properties + +Since we redefine CONSTRUCT_ONLY properties to be readonly data properties +when they are set, we must also define camelCase and kebab-case variations +in order to be consistent with the other property accessors. + +Closes: #391 +--- + gi/gobject.cpp | 11 +++++++---- + gjs/jsapi-util-string.cpp | 21 +++++++++++++++++++++ + gjs/jsapi-util.h | 1 + + installed-tests/js/testGObjectClass.js | 19 +++++++++++++++++++ + 4 files changed, 48 insertions(+), 4 deletions(-) + +diff --git a/gi/gobject.cpp b/gi/gobject.cpp +index 27c7d13c..65ed6638 100644 +--- a/gi/gobject.cpp ++++ b/gi/gobject.cpp +@@ -55,10 +55,13 @@ static bool jsobj_set_gproperty(JSContext* cx, JS::HandleObject object, + + GjsAutoChar underscore_name = gjs_hyphen_to_underscore(pspec->name); + +- if (pspec->flags & G_PARAM_CONSTRUCT_ONLY) +- return JS_DefineProperty( +- cx, object, underscore_name, jsvalue, +- GJS_MODULE_PROP_FLAGS | JSPROP_READONLY); ++ if (pspec->flags & G_PARAM_CONSTRUCT_ONLY) { ++ unsigned flags = GJS_MODULE_PROP_FLAGS | JSPROP_READONLY; ++ GjsAutoChar camel_name = gjs_hyphen_to_camel(pspec->name); ++ return JS_DefineProperty(cx, object, underscore_name, jsvalue, flags) && ++ JS_DefineProperty(cx, object, camel_name, jsvalue, flags) && ++ JS_DefineProperty(cx, object, pspec->name, jsvalue, flags); ++ } + + return JS_SetProperty(cx, object, underscore_name, jsvalue); + } +diff --git a/gjs/jsapi-util-string.cpp b/gjs/jsapi-util-string.cpp +index e318b514..45f297a7 100644 +--- a/gjs/jsapi-util-string.cpp ++++ b/gjs/jsapi-util-string.cpp +@@ -4,6 +4,7 @@ + + #include + ++#include // for toupper + #include + #include // for size_t, strlen + #include // for ssize_t +@@ -53,6 +54,26 @@ char* gjs_hyphen_to_underscore(const char* str) { + return retval; + } + ++GjsAutoChar gjs_hyphen_to_camel(const char* str) { ++ GjsAutoChar retval = static_cast(g_malloc(strlen(str) + 1)); ++ const char* input_iter = str; ++ char* output_iter = retval.get(); ++ bool uppercase_next = false; ++ while (*input_iter != '\0') { ++ if (*input_iter == '-') { ++ uppercase_next = true; ++ } else if (uppercase_next) { ++ *output_iter++ = toupper(*input_iter); ++ uppercase_next = false; ++ } else { ++ *output_iter++ = *input_iter; ++ } ++ input_iter++; ++ } ++ *output_iter = '\0'; ++ return retval; ++} ++ + /** + * gjs_string_to_utf8: + * @cx: JSContext +diff --git a/gjs/jsapi-util.h b/gjs/jsapi-util.h +index 11c23776..a6b66261 100644 +--- a/gjs/jsapi-util.h ++++ b/gjs/jsapi-util.h +@@ -542,6 +542,7 @@ bool gjs_object_require_converted_property(JSContext *context, + [[nodiscard]] std::string gjs_debug_id(jsid id); + + [[nodiscard]] char* gjs_hyphen_to_underscore(const char* str); ++[[nodiscard]] GjsAutoChar gjs_hyphen_to_camel(const char* str); + + #if defined(G_OS_WIN32) && (defined(_MSC_VER) && (_MSC_VER >= 1900)) + [[nodiscard]] std::wstring gjs_win32_vc140_utf8_to_utf16(const char* str); +diff --git a/installed-tests/js/testGObjectClass.js b/installed-tests/js/testGObjectClass.js +index f0a57a84..7073ccba 100644 +--- a/installed-tests/js/testGObjectClass.js ++++ b/installed-tests/js/testGObjectClass.js +@@ -853,6 +853,25 @@ describe('Auto accessor generation', function () { + expect(a['long-long-name']).toEqual(48); + expect(a.construct).toEqual(96); + expect(a.construct_only).toEqual(80); ++ expect(a.constructOnly).toEqual(80); ++ expect(a['construct-only']).toEqual(80); ++ }); ++ ++ it('set properties at construct time', function () { ++ a = new AutoAccessors({ ++ simple: 1, ++ longLongName: 1, ++ construct: 1, ++ 'construct-only': 1, ++ }); ++ expect(a.simple).toEqual(1); ++ expect(a.long_long_name).toEqual(1); ++ expect(a.longLongName).toEqual(1); ++ expect(a['long-long-name']).toEqual(1); ++ expect(a.construct).toEqual(1); ++ expect(a.construct_only).toEqual(1); ++ expect(a.constructOnly).toEqual(1); ++ expect(a['construct-only']).toEqual(1); + }); + + it('notify when the property changes', function () { +-- +2.30.2 + diff --git a/0003-overrides-Gio-Fix-_LocalFilePrototype.patch b/0003-overrides-Gio-Fix-_LocalFilePrototype.patch new file mode 100644 index 0000000..7dfcabf --- /dev/null +++ b/0003-overrides-Gio-Fix-_LocalFilePrototype.patch @@ -0,0 +1,27 @@ +From ec9385b89cb2bce0615093c3c957cbbb5ea6b769 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Florian=20M=C3=BCllner?= +Date: Wed, 24 Mar 2021 23:30:19 +0100 +Subject: [PATCH 4/6] overrides/Gio: Fix _LocalFilePrototype + +Recent GIO versions return a GDummyFile for '', which isn't the prototype +people are expecting to promisify when using _LocalFilePrototype. +--- + modules/core/overrides/Gio.js | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/modules/core/overrides/Gio.js b/modules/core/overrides/Gio.js +index 10872efb..d51738e0 100644 +--- a/modules/core/overrides/Gio.js ++++ b/modules/core/overrides/Gio.js +@@ -495,7 +495,7 @@ function _init() { + Gio._promisify = _promisify; + + // Temporary Gio.File.prototype fix +- Gio._LocalFilePrototype = Gio.File.new_for_path('').constructor.prototype; ++ Gio._LocalFilePrototype = Gio.File.new_for_path('/').constructor.prototype; + + // Override Gio.Settings and Gio.SettingsSchema - the C API asserts if + // trying to access a nonexistent schema or key, which is not handy for +-- +2.30.2 + diff --git a/0004-object-Return-undefined-and-not-the-actual-function-.patch b/0004-object-Return-undefined-and-not-the-actual-function-.patch new file mode 100644 index 0000000..cc8a14c --- /dev/null +++ b/0004-object-Return-undefined-and-not-the-actual-function-.patch @@ -0,0 +1,176 @@ +From 78bfccd3125d54caf8e1c0b8d2b84643e717a8b1 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= +Date: Wed, 24 Mar 2021 18:06:06 +0100 +Subject: [PATCH 6/6] object: Return undefined and not the actual function on + disposed objects + +When calling a proto function on a disposed object we return true not to +throw, however when doing this we implicitly return to JS the actual +underlying function pointer and that may cause use the return value to +be used to wrongly set a variable or to be wrongly evaluated. + +To avoid this and be consistent, return undefined instead. + +Adapt tests for this and add more for uncovered methods. + +Fixes #396 +--- + gi/object.cpp | 17 ++++++-- + .../js/testGObjectDestructionAccess.js | 41 ++++++++++++++++--- + 2 files changed, 49 insertions(+), 9 deletions(-) + +diff --git a/gi/object.cpp b/gi/object.cpp +index 598e6bb0..39ce7f5f 100644 +--- a/gi/object.cpp ++++ b/gi/object.cpp +@@ -327,8 +327,10 @@ bool ObjectBase::prop_getter(JSContext* cx, unsigned argc, JS::Value* vp) { + + bool ObjectInstance::prop_getter_impl(JSContext* cx, JS::HandleString name, + JS::MutableHandleValue rval) { +- if (!check_gobject_disposed("get any property from")) ++ if (!check_gobject_disposed("get any property from")) { ++ rval.setUndefined(); + return true; ++ } + + GValue gvalue = { 0, }; + +@@ -1877,8 +1879,10 @@ ObjectInstance::connect_impl(JSContext *context, + + gjs_debug_gsignal("connect obj %p priv %p", m_wrapper.get(), this); + +- if (!check_gobject_disposed("connect to any signal on")) ++ if (!check_gobject_disposed("connect to any signal on")) { ++ args.rval().setInt32(0); + return true; ++ } + + JS::UniqueChars signal_name; + JS::RootedObject callback(context); +@@ -1940,8 +1944,10 @@ ObjectInstance::emit_impl(JSContext *context, + gjs_debug_gsignal("emit obj %p priv %p argc %d", m_wrapper.get(), this, + argv.length()); + +- if (!check_gobject_disposed("emit any signal on")) ++ if (!check_gobject_disposed("emit any signal on")) { ++ argv.rval().setUndefined(); + return true; ++ } + + JS::UniqueChars signal_name; + if (!gjs_parse_call_args(context, "emit", argv, "!s", +@@ -2104,8 +2110,10 @@ bool ObjectInstance::signal_find_impl(JSContext* cx, const JS::CallArgs& args) { + gjs_debug_gsignal("[Gi.signal_find_symbol]() obj %p priv %p argc %d", + m_wrapper.get(), this, args.length()); + +- if (!check_gobject_disposed("find any signal on")) ++ if (!check_gobject_disposed("find any signal on")) { ++ args.rval().setInt32(0); + return true; ++ } + + JS::RootedObject match(cx); + if (!gjs_parse_call_args(cx, "[Gi.signal_find_symbol]", args, "o", "match", +@@ -2179,6 +2187,7 @@ bool ObjectInstance::signals_action_impl(JSContext* cx, + m_wrapper.get(), this, args.length()); + + if (!check_gobject_disposed((action_name + " any signal on").c_str())) { ++ args.rval().setInt32(0); + return true; + } + JS::RootedObject match(cx); +diff --git a/installed-tests/js/testGObjectDestructionAccess.js b/installed-tests/js/testGObjectDestructionAccess.js +index ed1d6bb6..0b35d859 100644 +--- a/installed-tests/js/testGObjectDestructionAccess.js ++++ b/installed-tests/js/testGObjectDestructionAccess.js +@@ -5,6 +5,7 @@ + imports.gi.versions.Gtk = '3.0'; + + const GLib = imports.gi.GLib; ++const GObject = imports.gi.GObject; + const Gtk = imports.gi.Gtk; + + describe('Access to destroyed GObject', function () { +@@ -23,7 +24,7 @@ describe('Access to destroyed GObject', function () { + GLib.test_expect_message('Gjs', GLib.LogLevelFlags.LEVEL_CRITICAL, + 'Object Gtk.Window (0x*'); + +- void destroyedWindow.title; ++ expect(destroyedWindow.title).toBeUndefined(); + + GLib.test_assert_expected_messages_internal('Gjs', 'testGObjectDestructionAccess.js', 0, + 'testExceptionInDestroyedObjectPropertyGet'); +@@ -45,7 +46,7 @@ describe('Access to destroyed GObject', function () { + GLib.test_expect_message('Gtk', GLib.LogLevelFlags.LEVEL_CRITICAL, + '*GTK_IS_WINDOW*'); + +- void destroyedWindow.get_title(); ++ expect(destroyedWindow.get_title()).toBeNull(); + + GLib.test_assert_expected_messages_internal('Gjs', 'testGObjectDestructionAccess.js', 0, + 'testExceptionInDestroyedObjectMethodGet'); +@@ -67,7 +68,7 @@ describe('Access to destroyed GObject', function () { + GLib.test_expect_message('Gjs', GLib.LogLevelFlags.LEVEL_CRITICAL, + 'Object Gtk.Window (0x*'); + +- destroyedWindow.connect('foo-signal', () => {}); ++ expect(destroyedWindow.connect('foo-signal', () => {})).toBe(0); + + GLib.test_assert_expected_messages_internal('Gjs', 'testGObjectDestructionAccess.js', 0, + 'testExceptionInDestroyedObjectConnect'); +@@ -77,7 +78,7 @@ describe('Access to destroyed GObject', function () { + GLib.test_expect_message('Gjs', GLib.LogLevelFlags.LEVEL_CRITICAL, + 'Object Gtk.Window (0x*'); + +- destroyedWindow.connect_after('foo-signal', () => {}); ++ expect(destroyedWindow.connect_after('foo-signal', () => {})).toBe(0); + + GLib.test_assert_expected_messages_internal('Gjs', 'testGObjectDestructionAccess.js', 0, + 'testExceptionInDestroyedObjectConnectAfter'); +@@ -87,12 +88,42 @@ describe('Access to destroyed GObject', function () { + GLib.test_expect_message('Gjs', GLib.LogLevelFlags.LEVEL_CRITICAL, + 'Object Gtk.Window (0x*'); + +- destroyedWindow.emit('foo-signal'); ++ expect(destroyedWindow.emit('foo-signal')).toBeUndefined(); + + GLib.test_assert_expected_messages_internal('Gjs', 'testGObjectDestructionAccess.js', 0, + 'testExceptionInDestroyedObjectEmit'); + }); + ++ it('Proto function signals_disconnect', function () { ++ GLib.test_expect_message('Gjs', GLib.LogLevelFlags.LEVEL_CRITICAL, ++ 'Object Gtk.Window (0x*'); ++ ++ expect(GObject.signal_handlers_disconnect_by_func(destroyedWindow, () => {})).toBe(0); ++ ++ GLib.test_assert_expected_messages_internal('Gjs', 'testGObjectDestructionAccess.js', 0, ++ 'testExceptionInDestroyedObjectSignalsDisconnect'); ++ }); ++ ++ it('Proto function signals_block', function () { ++ GLib.test_expect_message('Gjs', GLib.LogLevelFlags.LEVEL_CRITICAL, ++ 'Object Gtk.Window (0x*'); ++ ++ expect(GObject.signal_handlers_block_by_func(destroyedWindow, () => {})).toBe(0); ++ ++ GLib.test_assert_expected_messages_internal('Gjs', 'testGObjectDestructionAccess.js', 0, ++ 'testExceptionInDestroyedObjectSignalsBlock'); ++ }); ++ ++ it('Proto function signals_unblock', function () { ++ GLib.test_expect_message('Gjs', GLib.LogLevelFlags.LEVEL_CRITICAL, ++ 'Object Gtk.Window (0x*'); ++ ++ expect(GObject.signal_handlers_unblock_by_func(destroyedWindow, () => {})).toBe(0); ++ ++ GLib.test_assert_expected_messages_internal('Gjs', 'testGObjectDestructionAccess.js', 0, ++ 'testExceptionInDestroyedObjectSignalsUnblock'); ++ }); ++ + it('Proto function toString', function () { + expect(destroyedWindow.toString()).toMatch( + /\[object \(FINALIZED\) instance wrapper GIName:Gtk.Window jsobj@0x[a-f0-9]+ native@0x[a-f0-9]+\]/); +-- +2.30.2 + diff --git a/gjs.spec b/gjs.spec index b622f1b..c42916c 100644 --- a/gjs.spec +++ b/gjs.spec @@ -4,7 +4,7 @@ Name: gjs Version: 1.68.0 -Release: 3%{?dist} +Release: 4%{?dist} Summary: Javascript Bindings for GNOME # The following files contain code from Mozilla which @@ -14,6 +14,12 @@ Summary: Javascript Bindings for GNOME License: MIT and (MPLv1.1 or GPLv2+ or LGPLv2+) URL: https://wiki.gnome.org/Projects/Gjs Source0: https://download.gnome.org/sources/%{name}/1.68/%{name}-%{version}.tar.xz +# Several bugfix backports from upstream main +Patch0001: 0001-GObject-Don-t-autogenerate-accessors-for-CONSTRUCT_O.patch +Patch0002: 0002-GObject-Define-camel-and-kebab-variants-of-CONSTRUCT.patch +Patch0003: 0003-overrides-Gio-Fix-_LocalFilePrototype.patch +Patch0004: 0004-object-Return-undefined-and-not-the-actual-function-.patch + BuildRequires: cairo-gobject-devel BuildRequires: dbus-daemon @@ -96,6 +102,9 @@ the functionality of the installed gjs package. %{_datadir}/installed-tests/ %changelog +* Mon Mar 29 2021 Adam Williamson - 1.68.0-4 +- Backport several bugfixes from upstream main branch + * Fri Mar 26 2021 Kalev Lember - 1.68.0-3 - Rebuild to fix sysprof-capture symbols leaking into libraries consuming it