[gjs/ewlsh/fix-interface-overrides] gi: Store interface function overrides on the calling this object




commit d5602b596ad8c066e4cce12c198bd9fd3634ea02
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                              | 45 ++++++++++++++++++++++++++----
 installed-tests/js/testGObjectInterface.js | 14 ++++++++++
 2 files changed, 54 insertions(+), 5 deletions(-)
---
diff --git a/gi/object.cpp b/gi/object.cpp
index 4c5a9efd5..6d8643bde 100644
--- a/gi/object.cpp
+++ b/gi/object.cpp
@@ -662,11 +662,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 +705,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]