[gjs/ewlsh/fix-interface-overrides] gi: Store interface function overrides on the calling this object
- From: Evan Welsh <ewlsh src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gjs/ewlsh/fix-interface-overrides] gi: Store interface function overrides on the calling this object
- Date: Sat, 26 Feb 2022 16:41:27 +0000 (UTC)
commit e58d083d35752eef7a9603b4d6435e6c5ac98b67
Author: Evan Welsh <contact evanwelsh com>
Date: Sat Feb 26 08:21:45 2022 -0800
gi: Store interface function overrides on the calling this object
Fixes #467
gi/object.cpp | 47 ++++++++++++++++++++++++++----
installed-tests/js/testGObjectInterface.js | 14 +++++++++
2 files changed, 56 insertions(+), 5 deletions(-)
---
diff --git a/gi/object.cpp b/gi/object.cpp
index 4c5a9efd5..a53f9e525 100644
--- a/gi/object.cpp
+++ b/gi/object.cpp
@@ -34,6 +34,8 @@
#include <js/MemoryFunctions.h> // for AddAssociatedMemory, RemoveAssoci...
#include <js/Object.h>
#include <js/PropertyDescriptor.h> // for JSPROP_PERMANENT, JSPROP_READONLY
+#include <js/String.h>
+#include <js/Symbol.h>
#include <js/TypeDecls.h>
#include <js/Utility.h> // for UniqueChars
#include <js/Value.h>
@@ -662,11 +664,32 @@ static bool interface_getter(JSContext* cx, unsigned argc, JS::Value* vp) {
const GjsAtoms& atoms = GjsContextPrivate::atoms(cx);
// Check if an override value has been set
- bool has_override = false;
- if (!JS_HasPropertyById(cx, accessor, atoms.override(), &has_override))
+ bool has_override_symbol = false;
+ if (!JS_HasPropertyById(cx, accessor, atoms.override(),
+ &has_override_symbol))
return false;
- if (has_override)
- return JS_GetPropertyById(cx, accessor, atoms.override(), args.rval());
+
+ if (has_override_symbol) {
+ JS::RootedValue v_override_symbol(cx);
+ if (!JS_GetPropertyById(cx, accessor, atoms.override(),
+ &v_override_symbol))
+ return false;
+ g_assert(v_override_symbol.isSymbol() &&
+ "override symbol must be a symbol");
+ JS::RootedSymbol override_symbol(cx, v_override_symbol.toSymbol());
+ JS::RootedId override_id(cx, SYMBOL_TO_JSID(override_symbol));
+
+ JS::RootedObject this_obj(cx);
+ if (!args.computeThis(cx, &this_obj))
+ return false;
+
+ bool has_override = false;
+ if (!JS_HasPropertyById(cx, this_obj, override_id, &has_override))
+ return false;
+
+ if (has_override)
+ return JS_GetPropertyById(cx, this_obj, override_id, args.rval());
+ }
JS::RootedValue v_prototype(cx);
if (!JS_GetPropertyById(cx, accessor, atoms.prototype(), &v_prototype))
@@ -684,9 +707,23 @@ static bool interface_setter(JSContext* cx, unsigned argc, JS::Value* vp) {
JS::RootedValue v_accessor(
cx, js::GetFunctionNativeReserved(&args.callee(), ACCESSOR_SLOT));
JS::RootedObject accessor(cx, &v_accessor.toObject());
+ JS::RootedString description(
+ cx, JS_AtomizeAndPinString(cx, "Private interface function setter"));
+ JS::RootedSymbol symbol(cx, JS::NewSymbol(cx, description));
+ JS::RootedValue v_symbol(cx, JS::SymbolValue(symbol));
const GjsAtoms& atoms = GjsContextPrivate::atoms(cx);
- return JS_SetPropertyById(cx, accessor, atoms.override(), args[0]);
+ if (!JS_SetPropertyById(cx, accessor, atoms.override(), v_symbol))
+ return false;
+
+ args.rval().setUndefined();
+
+ JS::RootedObject this_obj(cx);
+ if (!args.computeThis(cx, &this_obj))
+ return false;
+ JS::RootedId override_id(cx, SYMBOL_TO_JSID(symbol));
+
+ return JS_SetPropertyById(cx, this_obj, override_id, args[0]);
}
static bool resolve_on_interface_prototype(JSContext* cx,
diff --git a/installed-tests/js/testGObjectInterface.js b/installed-tests/js/testGObjectInterface.js
index 72ee657ac..40f5eca82 100644
--- a/installed-tests/js/testGObjectInterface.js
+++ b/installed-tests/js/testGObjectInterface.js
@@ -385,6 +385,20 @@ describe('GObject interface', function () {
expect(file.dup).toBe(originalDup);
});
+ it('overrides cannot be changed by instances of child classes', function () {
+ spyOn(Gio.File.prototype, 'dup');
+
+ expect(file).toBeInstanceOf(Gio.File);
+ expect(file).toBeInstanceOf(Gio._LocalFilePrototype.constructor);
+
+ file.dup = 5;
+ expect(Gio.File.prototype.dup).not.toBe(5);
+ expect(Gio._LocalFilePrototype.dup).not.toBe(5);
+
+ file.dup = originalDup;
+ expect(file.dup).toBe(originalDup);
+ });
+
it('unknown properties are inherited by implementing classes', function () {
Gio.File.prototype._originalDup = originalDup;
expect(file._originalDup).toBe(originalDup);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]