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());
}