How to undo monkey-patching a native vfunc?

Monkey-patching a native vfunc can be done the same way as for a JS vfunc but it can’t be undone the same way.

  • Will a reviewer accept an extension that, when disabled, leaves a JS wrapper around the native vfunc?
  • Is there a better way?

Shell versions: 42, 43.

const {
    gobject_prototype_symbol: PROTO,
    hook_up_vfunc_symbol: HOOK,
} = imports._gi;
const {Clutter, GObject} = imports.gi;

const W = "get_preferred_width";
const H = "get_preferred_height";
const VW = "vfunc_" + W;
const VH = "vfunc_" + H;

const B = GObject.registerClass(
class B extends Clutter.Actor {
    vfunc_get_preferred_width() {return [1, 1]} // JS vfunc.
    // vfunc_get_preferred_height is native vfunc.
});
const BVW = B.prototype[VW];
const BH = B.prototype[H];
const BVH = B.prototype[VH];

function init() {
    log(BH !== BVH); // true
    log(BH === B.prototype[PROTO][H]); // true
    log(BVH === B.prototype[PROTO][VH]); // true
    log(BH === Clutter.Actor.prototype[H]); // true
    log(BVH === Clutter.Actor.prototype[VH]); // true
}

function enable() {
    logSize(Clutter.Actor); // 0,0,0,0
    logSize(B); // 1,0,1,0

    // Works: Hook up the monkey-patch vfuncs.
    B.prototype[PROTO][HOOK](W, function () {return [2, 2]});
    B.prototype[PROTO][HOOK](H, function () {return [3, 3]});
    logSize(B); // 2,3,2,3
}

function disable() {
    // Works: Hook up the original JS vfunc.
    B.prototype[PROTO][HOOK](W, BVW);
    logSize(B); // 1,3,1,3

    // Fails: Hook up the original native vfunc.
    for (const func of [BH, BVH]) {
        try {
            B.prototype[PROTO][HOOK](H, func);
        } catch (e) {
            /* func=method Clutter.Actor.get_preferred_height(for_width) {
              /* wrapper for native symbol
            }: Error: Tried to deal with a vfunc that wasn't a function */
            logError(e, `func=${func}`);
        }
        logSize(B); // 1,3,1,3
    }

    // Works: Hook up the original native vfunc wrapped in JS.
    B.prototype[PROTO][HOOK](H, function (...args) {
        return BVH.call(this, ...args);
    });
    logSize(B); // 1,0,1,0
}

function logSize(class_) {
    log(new class_().get_preferred_size());
}

Probably not, although it depends.

Is this a subclass you are creating, or are you trying to monkey-patch the vfunc’s on an existing class?

If this is a subclass, then you don’t need to worry about unpatching because it’s your class. If this is a built-in class in GNOME Shell, you probably shouldn’t do this and instead subclass, override and chain-up as normal.

  • I am monkey-patching Workspace and WorkspacesView.
  • If I subclassed them, I would need to monkey-patch the four methods of other classes where they are instantiated.
  • My extension won’t interest many people so I won’t pursue this further.

Thank you!

1 Like

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.