@gjsify/node-gi 0.64.1 → 0.64.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -0
- package/cairo.d.ts +38 -0
- package/cairo.js +95 -17
- package/gettext.js +29 -7
- package/gi.js +514 -34
- package/host.nativescript.js +19 -0
- package/host.node.js +16 -0
- package/index.d.ts +12 -0
- package/index.js +245 -76
- package/package.json +1 -1
- package/prebuilds/darwin-arm64/node_gi.node +0 -0
- package/prebuilds/darwin-x64/node_gi.node +0 -0
- package/prebuilds/linux-arm64/node_gi.node +0 -0
- package/prebuilds/linux-x64/node_gi.node +0 -0
- package/prebuilds/win32-arm64/node_gi.node +0 -0
- package/prebuilds/win32-x64/node_gi.node +0 -0
- package/src/cairo.cc +96 -0
- package/src/class.cc +15 -0
- package/src/common.h +21 -3
- package/src/marshal.cc +39 -17
- package/src/signals.cc +45 -22
package/gi.js
CHANGED
|
@@ -7,10 +7,18 @@
|
|
|
7
7
|
// Reference: gjs/modules/esm/gi.js for the require shape; node-gtk (romgrk, MIT) for the
|
|
8
8
|
// binding lineage. Hand-authored JS — the package ships no build step.
|
|
9
9
|
import * as native from './index.js';
|
|
10
|
+
import { ensureNative, onNativeLoaded } from './index.js';
|
|
10
11
|
// Timers, `process` and the windowing env record come from the host seam (host.node.js
|
|
11
12
|
// explains why they are the runtime's own, not the globals; host.nativescript.js is the
|
|
12
13
|
// NativeScript Android variant).
|
|
13
|
-
import {
|
|
14
|
+
import {
|
|
15
|
+
setImmediate,
|
|
16
|
+
setInterval,
|
|
17
|
+
clearInterval,
|
|
18
|
+
runtimeProcess,
|
|
19
|
+
windowingEnvWrites,
|
|
20
|
+
FOREIGN_PARENT_CLASSES,
|
|
21
|
+
} from '#host';
|
|
14
22
|
import { createGioDBus } from './overrides/gio-dbus.js';
|
|
15
23
|
import { createGioSettings } from './overrides/gio-settings.js';
|
|
16
24
|
|
|
@@ -360,17 +368,19 @@ function makeErrorDomainClass(namespace, typeName, members, quark) {
|
|
|
360
368
|
// Called by the engine on a failed GI invoke, with ownership of the GError* already
|
|
361
369
|
// transferred to the boxed handle. Named, so the position properties report the
|
|
362
370
|
// application frame rather than this one.
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
371
|
+
onNativeLoaded(() =>
|
|
372
|
+
native.setErrorBuilder(function buildThrownGError(handle, quark, code, message) {
|
|
373
|
+
// Same cross-version pairing: an addon older than this JS calls the builder with
|
|
374
|
+
// the GError's FIELDS (domainName, quark, code, message) rather than a handle, and
|
|
375
|
+
// there is no second `errorClass` argument to give it. Rebuilding the GError from
|
|
376
|
+
// the fields keeps the ONE shape on that pairing too — it costs only the original
|
|
377
|
+
// GError's address, which nothing can read.
|
|
378
|
+
if (typeof handle === 'string') {
|
|
379
|
+
return wrapGError(newErrorLiteral(quark, code, message), true, buildThrownGError);
|
|
380
|
+
}
|
|
381
|
+
return wrapGError(handle, true, buildThrownGError);
|
|
382
|
+
}),
|
|
383
|
+
);
|
|
374
384
|
|
|
375
385
|
// Stamps a makeClass prototype with its { namespace, typeName } so Gio._promisify can
|
|
376
386
|
// record WHICH introspected class a registration belongs to — matched against the
|
|
@@ -431,7 +441,7 @@ function wrapSignalCallback(cb) {
|
|
|
431
441
|
// means `this` is the full userProto-carrying wrapper — the SAME cached proxy the user
|
|
432
442
|
// constructed. Emitter at param 0, as in wrapSignalCallback.
|
|
433
443
|
function resolveTemplateCallback(handle, handlerName) {
|
|
434
|
-
|
|
444
|
+
const dispatcher = (...args) => {
|
|
435
445
|
const proxy = wrapInstance(handle);
|
|
436
446
|
const userProto = proxy[USER_PROTO];
|
|
437
447
|
const desc = userProto !== undefined ? findProtoDescriptor(userProto, handlerName) : undefined;
|
|
@@ -442,8 +452,16 @@ function resolveTemplateCallback(handle, handlerName) {
|
|
|
442
452
|
}
|
|
443
453
|
return desc.value.apply(proxy, args.map(wrapReturn));
|
|
444
454
|
};
|
|
455
|
+
// The native closure holds this dispatcher WEAKLY (src/signals.cc), and the
|
|
456
|
+
// dispatcher captures `handle` — so without a handle-keyed retain it would be
|
|
457
|
+
// garbage the moment create_closure returns, and the widget would be immortal.
|
|
458
|
+
// GJS's Gtk override reaches the same lifetime with
|
|
459
|
+
// `Gi.associateClosure(lifetimeObject, _createClosure(…))`. A fresh key per
|
|
460
|
+
// closure: one handler name can back several `<signal>` elements.
|
|
461
|
+
native.retainClosure(handle, Symbol(handlerName), dispatcher);
|
|
462
|
+
return dispatcher;
|
|
445
463
|
}
|
|
446
|
-
native.setTemplateCallbackResolver(resolveTemplateCallback);
|
|
464
|
+
onNativeLoaded(() => native.setTemplateCallbackResolver(resolveTemplateCallback));
|
|
447
465
|
|
|
448
466
|
// Run a registered class's JS constructor for a GObject the ENGINE instantiated from C
|
|
449
467
|
// — typically a GtkBuilder composite-template InternalChild, which has no JS-`new` to
|
|
@@ -461,12 +479,17 @@ function runCtorForCObject(handle, gtypeName) {
|
|
|
461
479
|
try {
|
|
462
480
|
// No props: GtkBuilder already applied the construct properties to the C object;
|
|
463
481
|
// the ctor's `params` arg is conventionally optional for a template child.
|
|
464
|
-
Reflect.construct(cls, []);
|
|
482
|
+
const instance = Reflect.construct(cls, []);
|
|
483
|
+
// A FOREIGN-PARENT class has no makeClass base ctor to consume the latch, and its
|
|
484
|
+
// attach is lazy — so if the ctor body touched nothing, `adoptHandle` would still
|
|
485
|
+
// be set here and a later attach would build a SECOND GObject, orphaning the one
|
|
486
|
+
// C already created. Force the attach while the latch is still live.
|
|
487
|
+
if (adoptHandle !== undefined && isForeignInstance(instance)) foreignHandle(instance);
|
|
465
488
|
} finally {
|
|
466
489
|
adoptHandle = prev;
|
|
467
490
|
}
|
|
468
491
|
}
|
|
469
|
-
native.setConstructCallback(runCtorForCObject);
|
|
492
|
+
onNativeLoaded(() => native.setConstructCallback(runCtorForCObject));
|
|
470
493
|
|
|
471
494
|
// A non-GObject GObject-fundamental (GskRenderNode, GdkEvent) as an OPAQUE,
|
|
472
495
|
// round-trippable handle: the native tagged External already carries the pointer and
|
|
@@ -927,13 +950,50 @@ function decorateGioNamespace(baseNs) {
|
|
|
927
950
|
});
|
|
928
951
|
}
|
|
929
952
|
|
|
953
|
+
// The internals of the PROXY links on an introspected prototype chain: the Proxy
|
|
954
|
+
// itself -> its target plus the materializer its getOwnPropertyDescriptor trap runs
|
|
955
|
+
// (`null` for a Proxy that traps no descriptor read). Registered by
|
|
956
|
+
// makeClassPrototype and vfuncChainProto; read only by findProtoDescriptor.
|
|
957
|
+
//
|
|
958
|
+
// Why: a descriptor read THROUGH a Proxy costs ~110 ns of V8 invariant machinery on
|
|
959
|
+
// top of the handler, and an introspected chain is mostly Proxies. Measured on the
|
|
960
|
+
// bench's depth-3 subject, a full MISS walk took 328 ns — 154 ns on the
|
|
961
|
+
// makeClassPrototype link, 65 ns on vfuncChainProto, 12 ns on the one plain link.
|
|
962
|
+
// With the target in hand the same answer costs 27 ns, because the trap body is
|
|
963
|
+
// exactly `materialize(prop); return Reflect.getOwnPropertyDescriptor(target, prop)`
|
|
964
|
+
// and running it directly skips only the machinery, never a step of the computation.
|
|
965
|
+
//
|
|
966
|
+
// This is NOT a cache of the ANSWER: nothing is remembered between calls. The target
|
|
967
|
+
// is the object every mutation of the prototype lands on — none of the handlers trap
|
|
968
|
+
// `set`, `defineProperty`, `deleteProperty` or `setPrototypeOf`, so
|
|
969
|
+
// `Cls.prototype.m = f`, `Object.defineProperty(…)`, `delete` and a spliced chain
|
|
970
|
+
// write through to it — and the descriptor still comes from a fresh read of it. That
|
|
971
|
+
// has to hold, because GJS pins nothing: on gjs 1.88.1 every one of those mutations
|
|
972
|
+
// is observable on an already-wrapped instance (measured), so an answer cache would
|
|
973
|
+
// need an invalidation signal JS does not offer. test/proto-descriptor-liveness.test.mjs
|
|
974
|
+
// is the proof obligation.
|
|
975
|
+
const protoInternals = new WeakMap();
|
|
976
|
+
|
|
930
977
|
// Walk a JS prototype chain (excluding Object.prototype) for an own property
|
|
931
978
|
// descriptor of `prop` — used to surface a registerClass subclass's own methods
|
|
932
|
-
// /getters/setters on the GObject wrapper.
|
|
979
|
+
// /getters/setters on the GObject wrapper. One full walk runs per property GET, per
|
|
980
|
+
// SET and per `emit`, so the Proxy bypass above is on the hottest path there is.
|
|
933
981
|
function findProtoDescriptor(proto, prop) {
|
|
934
|
-
for (let p = proto; p !== null && p !== Object.prototype;
|
|
935
|
-
const
|
|
982
|
+
for (let p = proto; p !== null && p !== Object.prototype;) {
|
|
983
|
+
const internals = protoInternals.get(p);
|
|
984
|
+
if (internals === undefined) {
|
|
985
|
+
const d = Object.getOwnPropertyDescriptor(p, prop);
|
|
986
|
+
if (d !== undefined) return d;
|
|
987
|
+
p = Object.getPrototypeOf(p);
|
|
988
|
+
continue;
|
|
989
|
+
}
|
|
990
|
+
const { target, materialize } = internals;
|
|
991
|
+
if (materialize !== null) materialize(prop);
|
|
992
|
+
const d = Object.getOwnPropertyDescriptor(target, prop);
|
|
936
993
|
if (d !== undefined) return d;
|
|
994
|
+
// The target's prototype, not the Proxy's: neither handler traps
|
|
995
|
+
// getPrototypeOf, so these are the same link — one fewer trap to pay.
|
|
996
|
+
p = Object.getPrototypeOf(target);
|
|
937
997
|
}
|
|
938
998
|
return undefined;
|
|
939
999
|
}
|
|
@@ -1088,6 +1148,13 @@ function assignTemplateChildren(instance, handle, reg) {
|
|
|
1088
1148
|
// outside what either runtime honours.
|
|
1089
1149
|
const propertySettersByProto = new WeakMap();
|
|
1090
1150
|
|
|
1151
|
+
// The property setters THIS file generated for a foreign-parent class (route D, see the
|
|
1152
|
+
// FOREIGN-PARENT section). They must be invisible to resolvePropertySetter: a generated
|
|
1153
|
+
// setter's whole body is `native.setProperty`, and the engine answers every custom-property
|
|
1154
|
+
// store by calling runJsPropertySetter — which would resolve the generated setter and store
|
|
1155
|
+
// again, forever. Only a setter the CLASS declared is a JS setter worth replaying.
|
|
1156
|
+
const generatedForeignSetters = new WeakSet();
|
|
1157
|
+
|
|
1091
1158
|
function findPropertySetter(userProto, propertyName) {
|
|
1092
1159
|
let perProto = propertySettersByProto.get(userProto);
|
|
1093
1160
|
if (perProto === undefined) propertySettersByProto.set(userProto, (perProto = new Map()));
|
|
@@ -1104,7 +1171,9 @@ function resolvePropertySetter(userProto, propertyName) {
|
|
|
1104
1171
|
const camel = propertyName.replace(/-([a-z])/g, (m) => m[1].toUpperCase());
|
|
1105
1172
|
for (const spelling of [propertyName, underscored, camel]) {
|
|
1106
1173
|
const desc = findProtoDescriptor(userProto, spelling);
|
|
1107
|
-
if (desc !== undefined && typeof desc.set === 'function'
|
|
1174
|
+
if (desc !== undefined && typeof desc.set === 'function' && !generatedForeignSetters.has(desc.set)) {
|
|
1175
|
+
return desc.set;
|
|
1176
|
+
}
|
|
1108
1177
|
}
|
|
1109
1178
|
return undefined;
|
|
1110
1179
|
}
|
|
@@ -1131,9 +1200,9 @@ function runJsPropertySetter(handle, propertyName) {
|
|
|
1131
1200
|
// function` until the next release republishes the binary. Every native call added
|
|
1132
1201
|
// here degrades instead; without the callback, a post-construction set simply does
|
|
1133
1202
|
// not reach the JS setter, which is what 0.50.0 did.
|
|
1134
|
-
|
|
1135
|
-
native.setPropertySetCallback(runJsPropertySetter);
|
|
1136
|
-
}
|
|
1203
|
+
onNativeLoaded(() => {
|
|
1204
|
+
if (typeof native.setPropertySetCallback === 'function') native.setPropertySetCallback(runJsPropertySetter);
|
|
1205
|
+
});
|
|
1137
1206
|
|
|
1138
1207
|
// Which properties the construct-time replay must run.
|
|
1139
1208
|
//
|
|
@@ -1342,6 +1411,7 @@ export function startMainContextPump() {
|
|
|
1342
1411
|
// GLib's default main context — a JS-timer pump would only iterate the
|
|
1343
1412
|
// context recursively from within its own dispatch.
|
|
1344
1413
|
if (hostPumpsItself()) return () => {};
|
|
1414
|
+
ensureNative();
|
|
1345
1415
|
pumpRefCount++;
|
|
1346
1416
|
if (pumpTimer === null) {
|
|
1347
1417
|
if (pendingView === null) pendingView = native.makePumpPendingCount();
|
|
@@ -1546,7 +1616,11 @@ function wrapInstance(handle, userProto) {
|
|
|
1546
1616
|
// the leaf's OWN methods resolve). Never DOWNGRADE: a more-derived cached proto is
|
|
1547
1617
|
// kept. The userProto lives on the target (read via the proxy here), so the
|
|
1548
1618
|
// upgrade is in place — identity (===) and any expando fields are preserved.
|
|
1549
|
-
|
|
1619
|
+
// NEVER re-link a foreign-parent instance (route D): linkInstanceProto does
|
|
1620
|
+
// `Object.setPrototypeOf`, and that object's [[Prototype]] IS its NativeScript
|
|
1621
|
+
// widget chain — replacing it would turn a real Label into something `_addView`
|
|
1622
|
+
// rejects. Its user prototype is already the registered one, fixed at attach.
|
|
1623
|
+
if (userProto !== undefined && userProto !== cached[USER_PROTO] && !isForeignInstance(cached)) {
|
|
1550
1624
|
const current = cached[USER_PROTO];
|
|
1551
1625
|
if (current === undefined || isProtoSameOrDescendantOf(userProto, current)) {
|
|
1552
1626
|
linkInstanceProto(cached, userProto);
|
|
@@ -1812,7 +1886,8 @@ function wrapInstance(handle, userProto) {
|
|
|
1812
1886
|
// the introspected parent's `vfunc_x` is a thunk into the actual C vtable entry.
|
|
1813
1887
|
// Non-`vfunc_` lookups fall through to Object.prototype (so the base prototype
|
|
1814
1888
|
// keeps its normal object behaviour).
|
|
1815
|
-
const
|
|
1889
|
+
const vfuncChainTarget = Object.create(Object.prototype);
|
|
1890
|
+
const vfuncChainProto = new Proxy(vfuncChainTarget, {
|
|
1816
1891
|
get(target, prop, receiver) {
|
|
1817
1892
|
if (typeof prop === 'string' && prop.startsWith('vfunc_')) {
|
|
1818
1893
|
const vfuncName = prop.slice('vfunc_'.length);
|
|
@@ -1827,6 +1902,10 @@ const vfuncChainProto = new Proxy(Object.create(Object.prototype), {
|
|
|
1827
1902
|
return Reflect.get(target, prop, receiver);
|
|
1828
1903
|
},
|
|
1829
1904
|
});
|
|
1905
|
+
// No descriptor trap here: a `vfunc_*` access is synthesized by `get` alone and was
|
|
1906
|
+
// never reported as a descriptor, so the walk's answer for this link always came from
|
|
1907
|
+
// the target. `null` materializer = "read the target, that is the whole trap".
|
|
1908
|
+
protoInternals.set(vfuncChainProto, { target: vfuncChainTarget, materialize: null });
|
|
1830
1909
|
|
|
1831
1910
|
// Surface a lazy `$gtype` getter on an introspected class/struct ctor: the real
|
|
1832
1911
|
// GObject GType (a node-gi GType handle), resolved on first read via the engine
|
|
@@ -1994,7 +2073,7 @@ function makeClassPrototype(namespace, typeName) {
|
|
|
1994
2073
|
}
|
|
1995
2074
|
return arity >= 0 && define(prop, makeGiMethod(where, prop, arity));
|
|
1996
2075
|
};
|
|
1997
|
-
|
|
2076
|
+
const proto = new Proxy(target, {
|
|
1998
2077
|
get(t, prop, receiver) {
|
|
1999
2078
|
materialize(prop);
|
|
2000
2079
|
return Reflect.get(t, prop, receiver);
|
|
@@ -2007,6 +2086,10 @@ function makeClassPrototype(namespace, typeName) {
|
|
|
2007
2086
|
return materialize(prop) || Reflect.has(t, prop);
|
|
2008
2087
|
},
|
|
2009
2088
|
});
|
|
2089
|
+
// Let findProtoDescriptor run the descriptor trap's body directly on the target —
|
|
2090
|
+
// same two steps, without V8's per-trap invariant checks (see protoInternals).
|
|
2091
|
+
protoInternals.set(proto, { target, materialize });
|
|
2092
|
+
return proto;
|
|
2010
2093
|
}
|
|
2011
2094
|
|
|
2012
2095
|
function makeClass(namespace, typeName) {
|
|
@@ -2526,6 +2609,8 @@ function paramSpecGTyped(type) {
|
|
|
2526
2609
|
const ParamSpec = Object.freeze({
|
|
2527
2610
|
string: paramSpecPlain('string'),
|
|
2528
2611
|
boolean: paramSpecPlain('boolean'),
|
|
2612
|
+
char: paramSpecRanged('char'),
|
|
2613
|
+
uchar: paramSpecRanged('uchar'),
|
|
2529
2614
|
int: paramSpecRanged('int'),
|
|
2530
2615
|
uint: paramSpecRanged('uint'),
|
|
2531
2616
|
int64: paramSpecRanged('int64'),
|
|
@@ -2560,7 +2645,11 @@ function findParentGType(klass) {
|
|
|
2560
2645
|
for (let c = Object.getPrototypeOf(klass); typeof c === 'function'; c = Object.getPrototypeOf(c)) {
|
|
2561
2646
|
const reg = registeredClasses.get(c);
|
|
2562
2647
|
if (reg !== undefined) {
|
|
2563
|
-
|
|
2648
|
+
// `parentForeign` carries the FOREIGN-PARENT marker down the chain: a subclass
|
|
2649
|
+
// of `registerClass(…, class extends Label {})` has a perfectly ordinary
|
|
2650
|
+
// registered parent by this test, yet ITS instances are still NativeScript
|
|
2651
|
+
// objects that need the attach path rather than the wrapper Proxy.
|
|
2652
|
+
return { parentGTypeHandle: reg.typeHandle, parentForeign: reg.foreign === true };
|
|
2564
2653
|
}
|
|
2565
2654
|
const gt = c.$gtypeName;
|
|
2566
2655
|
if (typeof gt === 'string') {
|
|
@@ -2568,9 +2657,19 @@ function findParentGType(klass) {
|
|
|
2568
2657
|
if (dot > 0) {
|
|
2569
2658
|
return { parentNamespace: gt.slice(0, dot), parentType: gt.slice(dot + 1) };
|
|
2570
2659
|
}
|
|
2571
|
-
// A dotless `$gtypeName`
|
|
2572
|
-
//
|
|
2573
|
-
//
|
|
2660
|
+
// A dotless `$gtypeName` and no registry entry, with the name INHERITED: `c` is
|
|
2661
|
+
// an ordinary unregistered subclass of a registered ancestor further up, reading
|
|
2662
|
+
// that ancestor's static through the class-side prototype chain. Keep walking —
|
|
2663
|
+
// `c` contributes no GType, and the registered ancestor above it is the parent.
|
|
2664
|
+
// gjs answers the same way (measured, 1.88.1): `registerClass(C extends B)` where
|
|
2665
|
+
// `class B extends RegisteredA {}` gives `type_parent(C) === A`. Without this the
|
|
2666
|
+
// walk threw on its own inherited value, which is every port class that puts a
|
|
2667
|
+
// plain subclass between two registrations — `AdwSwitchRow` between the two
|
|
2668
|
+
// route-D mixins (ADR 0105 § 2) is the case that found it.
|
|
2669
|
+
if (!Object.prototype.hasOwnProperty.call(c, '$gtypeName')) continue;
|
|
2670
|
+
// An OWN dotless `$gtypeName` with no registry entry should never occur (only
|
|
2671
|
+
// registered types are dotless, and they are in the registry); be explicit
|
|
2672
|
+
// rather than mis-resolving the namespace split.
|
|
2574
2673
|
throw new TypeError("GObject.registerClass: parent '" + gt + "' has no resolvable GType");
|
|
2575
2674
|
}
|
|
2576
2675
|
}
|
|
@@ -2735,6 +2834,335 @@ function collectVfuncs(klass) {
|
|
|
2735
2834
|
* @param {Function} [maybeClass]
|
|
2736
2835
|
* @returns {Function} the same `klass` (now registered)
|
|
2737
2836
|
*/
|
|
2837
|
+
// ---- FOREIGN-PARENT classes: a registered GObject attached to a host widget ----
|
|
2838
|
+
//
|
|
2839
|
+
// THE ONE DELIBERATE DIVERGENCE FROM GJS, and it only exists where GJS cannot run.
|
|
2840
|
+
// Gated on `FOREIGN_PARENT_CLASSES` (#host): true on NativeScript, false everywhere
|
|
2841
|
+
// else, where registerClass keeps throwing GJS's invalid-base error verbatim. See
|
|
2842
|
+
// host.nativescript.js for why that gate is sound, ADR 0105 § 2 for the decision and
|
|
2843
|
+
// docs/node-gi-gjs-surface.md for the surface this adds.
|
|
2844
|
+
//
|
|
2845
|
+
// What it buys: `registerClass({GTypeName, Properties, Signals}, class MyLabel extends
|
|
2846
|
+
// Label {})` where `Label` is a `@nativescript/core` widget. The JS object stays a REAL
|
|
2847
|
+
// Label — its [[Prototype]] is the untouched NativeScript chain — so `stack.addChild(w)`,
|
|
2848
|
+
// `ContentView.content = w` and `_addChildFromBuilder` pass their hard `instanceof
|
|
2849
|
+
// ViewBase` gates with no `.view` unwrap anywhere in consumer code. Beside it sits a real
|
|
2850
|
+
// GObject of a registered GType whose GType PARENT is GObject.Object (the GType parent is
|
|
2851
|
+
// independent of the JS prototype chain), so the object also answers `instanceof
|
|
2852
|
+
// GObject.Object`, carries custom properties with `notify::`, and emits registered signals.
|
|
2853
|
+
//
|
|
2854
|
+
// ATTACH, never wrap. Everywhere else node-gi hands back a Proxy whose get/set traps
|
|
2855
|
+
// resolve the GObject surface dynamically; here there is no Proxy to put traps on, so:
|
|
2856
|
+
// • `[HANDLE]`/`[USER_PROTO]` become non-enumerable OWN props of the widget object
|
|
2857
|
+
// (`Symbol.hasInstance` resolves `instanceof` through `instance[HANDLE]`, which is
|
|
2858
|
+
// what makes the widget a GObject.Object);
|
|
2859
|
+
// • everything the traps would have answered — connect/emit/notify, the property
|
|
2860
|
+
// accessors, the GObject.Object methods — is installed as OWN members of
|
|
2861
|
+
// `klass.prototype`, once, at registerClass time;
|
|
2862
|
+
// • `Object.setPrototypeOf` is never called on such an object (see linkInstanceProto).
|
|
2863
|
+
//
|
|
2864
|
+
// LAZY. The GObject is constructed on the first access that needs it (`foreignHandle`),
|
|
2865
|
+
// because nothing in a NativeScript widget's construction runs node-gi code: there is no
|
|
2866
|
+
// base ctor to route `new` through the way makeClass does. Every entry point goes through
|
|
2867
|
+
// `foreignHandle`, including `unwrapArg` reading `[HANDLE]` through the prototype getter,
|
|
2868
|
+
// so by the time anything can OBSERVE the GObject it exists — `new MyLabel() instanceof
|
|
2869
|
+
// GObject.Object` attaches and answers true. Construct properties are the one thing this
|
|
2870
|
+
// cannot carry (the widget ctor owns its own arguments); set them after construction.
|
|
2871
|
+
//
|
|
2872
|
+
// NOT supported, deliberately: `vfunc_*` overrides. collectVfuncs finds none on a widget
|
|
2873
|
+
// chain, and a vfunc trampoline would need to re-link the instance prototype — the one
|
|
2874
|
+
// thing this path must never do.
|
|
2875
|
+
const FOREIGN_MATERIALISED = Symbol('nodeGiForeignMaterialised');
|
|
2876
|
+
|
|
2877
|
+
// A registered foreign-parent class's PROTOTYPE → its registration. The instance carries
|
|
2878
|
+
// no class pointer we can trust (its `constructor` is the widget's), so the attach walks
|
|
2879
|
+
// the real prototype chain and takes the nearest entry — which is the LEAF's, since a
|
|
2880
|
+
// subclass's own prototype comes first.
|
|
2881
|
+
const foreignRegByProto = new WeakMap();
|
|
2882
|
+
|
|
2883
|
+
// Gated first, so on a host where foreign parents cannot exist this is one boolean read
|
|
2884
|
+
// rather than a prototype-chain walk — it sits on wrapInstance's upgrade path.
|
|
2885
|
+
function isForeignInstance(obj) {
|
|
2886
|
+
return FOREIGN_PARENT_CLASSES && foreignRegFor(obj) !== undefined;
|
|
2887
|
+
}
|
|
2888
|
+
|
|
2889
|
+
function foreignRegFor(obj) {
|
|
2890
|
+
for (let p = Object.getPrototypeOf(obj); p !== null; p = Object.getPrototypeOf(p)) {
|
|
2891
|
+
const reg = foreignRegByProto.get(p);
|
|
2892
|
+
if (reg !== undefined) return reg;
|
|
2893
|
+
}
|
|
2894
|
+
return undefined;
|
|
2895
|
+
}
|
|
2896
|
+
|
|
2897
|
+
// Construct this object's GObject and bind the two together. Not a wrap: the object is
|
|
2898
|
+
// handed back unchanged apart from three non-enumerable own symbols.
|
|
2899
|
+
function attachForeignGObject(obj, reg) {
|
|
2900
|
+
let handle;
|
|
2901
|
+
if (adoptHandle !== undefined) {
|
|
2902
|
+
// A C-created instance (runCtorForCObject) — the engine already built the GObject.
|
|
2903
|
+
// Consume the latch atomically, exactly as the makeClass base ctor does.
|
|
2904
|
+
handle = adoptHandle;
|
|
2905
|
+
adoptHandle = undefined;
|
|
2906
|
+
} else {
|
|
2907
|
+
handle = native.constructType(reg.typeHandle, {});
|
|
2908
|
+
}
|
|
2909
|
+
// HANDLE first: it shadows the prototype getter, so anything the flush below runs can
|
|
2910
|
+
// re-enter `foreignHandle` without constructing a second GObject.
|
|
2911
|
+
Object.defineProperty(obj, HANDLE, { value: handle, configurable: true });
|
|
2912
|
+
Object.defineProperty(obj, USER_PROTO, { value: reg.foreignProto, configurable: true });
|
|
2913
|
+
instanceCache.set(handle, obj);
|
|
2914
|
+
flushPropertiesToJsSetters(obj, handle, reg);
|
|
2915
|
+
return handle;
|
|
2916
|
+
}
|
|
2917
|
+
|
|
2918
|
+
// This object's GObject handle, attaching on first demand. Every member installed below
|
|
2919
|
+
// goes through here, as does `unwrapArg` (via the prototype [HANDLE] getter).
|
|
2920
|
+
function foreignHandle(obj) {
|
|
2921
|
+
if (Object.hasOwn(obj, HANDLE)) return obj[HANDLE];
|
|
2922
|
+
const reg = foreignRegFor(obj);
|
|
2923
|
+
if (reg === undefined) {
|
|
2924
|
+
throw new TypeError('node-gi: this object is not an instance of a GObject.registerClass()-registered class');
|
|
2925
|
+
}
|
|
2926
|
+
return attachForeignGObject(obj, reg);
|
|
2927
|
+
}
|
|
2928
|
+
|
|
2929
|
+
// Which custom properties of this instance hold a materialised value — the state GJS's
|
|
2930
|
+
// generated accessors keep in their private field, and what the equal-value guard
|
|
2931
|
+
// compares against. Same rule as the wrapper's `materialise` (see wrapInstance): seeded
|
|
2932
|
+
// from what the engine has stored, then extended by every read and write.
|
|
2933
|
+
function foreignMaterialise(obj, name) {
|
|
2934
|
+
let seen = obj[FOREIGN_MATERIALISED];
|
|
2935
|
+
if (seen === undefined) {
|
|
2936
|
+
seen = new Set(typeof native.storedPropertyNames === 'function' ? native.storedPropertyNames(obj[HANDLE]) : []);
|
|
2937
|
+
Object.defineProperty(obj, FOREIGN_MATERIALISED, { value: seen, configurable: true });
|
|
2938
|
+
}
|
|
2939
|
+
const had = seen.has(name);
|
|
2940
|
+
seen.add(name);
|
|
2941
|
+
return had;
|
|
2942
|
+
}
|
|
2943
|
+
|
|
2944
|
+
// The GObject.Object instance methods installed as callMethod thunks. A MODEST list on
|
|
2945
|
+
// purpose — only introspectable, GJS-reachable members with no gpointer/GValue-array
|
|
2946
|
+
// argument (`g_object_get_data`, `getv` and friends are deliberately absent rather than
|
|
2947
|
+
// present and throwing). The wrapper Proxy answers these from introspection dynamically;
|
|
2948
|
+
// this path has to name them.
|
|
2949
|
+
const FOREIGN_OBJECT_METHODS = [
|
|
2950
|
+
'get_property',
|
|
2951
|
+
'set_property',
|
|
2952
|
+
'bind_property',
|
|
2953
|
+
'notify_by_pspec',
|
|
2954
|
+
'freeze_notify',
|
|
2955
|
+
'thaw_notify',
|
|
2956
|
+
'run_dispose',
|
|
2957
|
+
'ref',
|
|
2958
|
+
'unref',
|
|
2959
|
+
'ref_sink',
|
|
2960
|
+
'force_floating',
|
|
2961
|
+
'is_floating',
|
|
2962
|
+
];
|
|
2963
|
+
|
|
2964
|
+
function snakeToCamel(name) {
|
|
2965
|
+
return name.replace(/_([a-z])/g, (m) => m[1].toUpperCase());
|
|
2966
|
+
}
|
|
2967
|
+
|
|
2968
|
+
/** The distinct spellings a member is installed under: one entry for an underscore-free name. */
|
|
2969
|
+
function memberSpellings(name) {
|
|
2970
|
+
const camel = snakeToCamel(name);
|
|
2971
|
+
return camel === name ? [name] : [name, camel];
|
|
2972
|
+
}
|
|
2973
|
+
|
|
2974
|
+
// Install a member under `name` unless the host chain already answers under it — a
|
|
2975
|
+
// widget's own method ALWAYS wins, because consumer code and NativeScript's own
|
|
2976
|
+
// internals call it. Returns false (and the caller records the skip) when it loses.
|
|
2977
|
+
function defineForeignMember(proto, name, descriptor, skipped) {
|
|
2978
|
+
if (findProtoDescriptor(proto, name) !== undefined) {
|
|
2979
|
+
skipped.push(name);
|
|
2980
|
+
return false;
|
|
2981
|
+
}
|
|
2982
|
+
Object.defineProperty(proto, name, { configurable: true, enumerable: false, ...descriptor });
|
|
2983
|
+
return true;
|
|
2984
|
+
}
|
|
2985
|
+
|
|
2986
|
+
// Everything the instance wrapper's traps would have resolved, as own members of a
|
|
2987
|
+
// foreign-parent class's prototype. Returns the names the host chain already owned, so a
|
|
2988
|
+
// program (and the route-D spec) can see exactly what it did NOT get.
|
|
2989
|
+
function installForeignGObjectSurface(proto, properties) {
|
|
2990
|
+
const skipped = [];
|
|
2991
|
+
|
|
2992
|
+
// `[HANDLE]` as a lazily-attaching GETTER is the whole load-bearing trick: it makes
|
|
2993
|
+
// `instanceof GObject.Object`, `unwrapArg` and every member below attach on demand,
|
|
2994
|
+
// with no construction hook on the widget side. An attach replaces it with an own
|
|
2995
|
+
// data property, so this getter runs at most once per instance.
|
|
2996
|
+
Object.defineProperty(proto, HANDLE, {
|
|
2997
|
+
configurable: true,
|
|
2998
|
+
get() {
|
|
2999
|
+
return foreignHandle(this);
|
|
3000
|
+
},
|
|
3001
|
+
});
|
|
3002
|
+
|
|
3003
|
+
defineForeignMember(
|
|
3004
|
+
proto,
|
|
3005
|
+
'$typeName',
|
|
3006
|
+
{
|
|
3007
|
+
get() {
|
|
3008
|
+
return native.getTypeName(foreignHandle(this));
|
|
3009
|
+
},
|
|
3010
|
+
},
|
|
3011
|
+
skipped,
|
|
3012
|
+
);
|
|
3013
|
+
|
|
3014
|
+
// Signal API. The same four thunks the wrapper builds, including the (fn → ids)
|
|
3015
|
+
// bookkeeping signal_handlers_*_by_func resolves through.
|
|
3016
|
+
defineForeignMember(
|
|
3017
|
+
proto,
|
|
3018
|
+
'connect',
|
|
3019
|
+
{
|
|
3020
|
+
value: function connect(signal, cb) {
|
|
3021
|
+
const handle = foreignHandle(this);
|
|
3022
|
+
const id = native.connectSignal(handle, signal, wrapSignalCallback(cb), false);
|
|
3023
|
+
recordSignalHandler(handle, cb, id);
|
|
3024
|
+
return id;
|
|
3025
|
+
},
|
|
3026
|
+
},
|
|
3027
|
+
skipped,
|
|
3028
|
+
);
|
|
3029
|
+
const connectAfter = function connect_after(signal, cb) {
|
|
3030
|
+
const handle = foreignHandle(this);
|
|
3031
|
+
const id = native.connectSignal(handle, signal, wrapSignalCallback(cb), true);
|
|
3032
|
+
recordSignalHandler(handle, cb, id);
|
|
3033
|
+
return id;
|
|
3034
|
+
};
|
|
3035
|
+
defineForeignMember(proto, 'connect_after', { value: connectAfter }, skipped);
|
|
3036
|
+
defineForeignMember(proto, 'connectAfter', { value: connectAfter }, skipped);
|
|
3037
|
+
defineForeignMember(
|
|
3038
|
+
proto,
|
|
3039
|
+
'disconnect',
|
|
3040
|
+
{
|
|
3041
|
+
value: function disconnect(id) {
|
|
3042
|
+
const handle = foreignHandle(this);
|
|
3043
|
+
forgetSignalHandlerId(handle, id);
|
|
3044
|
+
return native.disconnectSignal(handle, id);
|
|
3045
|
+
},
|
|
3046
|
+
},
|
|
3047
|
+
skipped,
|
|
3048
|
+
);
|
|
3049
|
+
defineForeignMember(
|
|
3050
|
+
proto,
|
|
3051
|
+
'emit',
|
|
3052
|
+
{
|
|
3053
|
+
value: function emit(signal, ...args) {
|
|
3054
|
+
return wrapReturn(native.emitSignal(foreignHandle(this), signal, unwrapArgs(args)));
|
|
3055
|
+
},
|
|
3056
|
+
},
|
|
3057
|
+
skipped,
|
|
3058
|
+
);
|
|
3059
|
+
|
|
3060
|
+
// `notify` is the ONE name that cannot be skipped and cannot be taken: NativeScript's
|
|
3061
|
+
// Observable.prototype.notify({eventName, object}) is called by the widget machinery
|
|
3062
|
+
// itself (and by consumer code), while GObject's notify(propertyName) is what a
|
|
3063
|
+
// custom property's setter needs. They are told apart by ARGUMENT TYPE — a string is
|
|
3064
|
+
// a GObject property name, anything else is a NativeScript event payload — and the
|
|
3065
|
+
// inherited implementation is captured HERE, before this definition shadows it, so
|
|
3066
|
+
// both keep working on one object. This is the only collision in the
|
|
3067
|
+
// @nativescript/core 9.1 Label chain (measured: Observable.prototype.notify is the
|
|
3068
|
+
// sole hit for the whole installed surface).
|
|
3069
|
+
const inheritedNotify = findProtoDescriptor(proto, 'notify');
|
|
3070
|
+
const hostNotify = inheritedNotify !== undefined ? inheritedNotify.value : undefined;
|
|
3071
|
+
Object.defineProperty(proto, 'notify', {
|
|
3072
|
+
configurable: true,
|
|
3073
|
+
enumerable: false,
|
|
3074
|
+
value: function notify(arg, ...rest) {
|
|
3075
|
+
if (typeof arg === 'string') {
|
|
3076
|
+
return wrapReturn(native.callMethod(foreignHandle(this), 'notify', [arg]));
|
|
3077
|
+
}
|
|
3078
|
+
if (typeof hostNotify === 'function') return hostNotify.call(this, arg, ...rest);
|
|
3079
|
+
throw new TypeError('notify(): expected a GObject property name');
|
|
3080
|
+
},
|
|
3081
|
+
});
|
|
3082
|
+
|
|
3083
|
+
for (const name of FOREIGN_OBJECT_METHODS) {
|
|
3084
|
+
const thunk = function giMethod(...args) {
|
|
3085
|
+
return wrapReturn(native.callMethod(foreignHandle(this), name, unwrapArgs(args)));
|
|
3086
|
+
};
|
|
3087
|
+
// `memberSpellings(name)` and not `[name, snakeToCamel(name)]`: a name with no
|
|
3088
|
+
// underscore camel-cases to ITSELF (`ref`, `unref`), so the second call would
|
|
3089
|
+
// find the descriptor the first one just installed and record a skip against
|
|
3090
|
+
// the host for a member the host never had. `$foreignSkipped` is read as "what
|
|
3091
|
+
// did the widget already own", so a self-skip there is a false statement.
|
|
3092
|
+
for (const spelling of memberSpellings(name)) {
|
|
3093
|
+
defineForeignMember(proto, spelling, { value: thunk }, skipped);
|
|
3094
|
+
}
|
|
3095
|
+
}
|
|
3096
|
+
|
|
3097
|
+
// gjs's GObject.Object.prototype shims over namespace-level g_signal_* /
|
|
3098
|
+
// bind_property_full — not introspected instance methods, so named explicitly here
|
|
3099
|
+
// just as the wrapper's get trap routes them explicitly.
|
|
3100
|
+
for (const name of [
|
|
3101
|
+
'block_signal_handler',
|
|
3102
|
+
'unblock_signal_handler',
|
|
3103
|
+
'stop_emission_by_name',
|
|
3104
|
+
'bind_property_full',
|
|
3105
|
+
]) {
|
|
3106
|
+
for (const spelling of memberSpellings(name)) {
|
|
3107
|
+
const shim = objectPrototypeShim(spelling);
|
|
3108
|
+
defineForeignMember(
|
|
3109
|
+
proto,
|
|
3110
|
+
spelling,
|
|
3111
|
+
{
|
|
3112
|
+
value: function shimmed(...args) {
|
|
3113
|
+
return shim(foreignHandle(this), args);
|
|
3114
|
+
},
|
|
3115
|
+
},
|
|
3116
|
+
skipped,
|
|
3117
|
+
);
|
|
3118
|
+
}
|
|
3119
|
+
}
|
|
3120
|
+
|
|
3121
|
+
installForeignPropertyAccessors(proto, properties, skipped);
|
|
3122
|
+
return skipped;
|
|
3123
|
+
}
|
|
3124
|
+
|
|
3125
|
+
// GJS-style generated accessors for the DECLARED custom properties, under all three
|
|
3126
|
+
// spellings GJS makes equivalent (dash, underscore, camel — `_checkAccessors` mirrors the
|
|
3127
|
+
// declared one onto the other two). Elsewhere node-gi installs none and resolves the
|
|
3128
|
+
// spellings at the point of use instead (findPropertySetter), because the wrapper Proxy
|
|
3129
|
+
// can; this path has no Proxy, so the accessors have to exist. A ROUTE-D-ONLY divergence
|
|
3130
|
+
// from the rest of node-gi, and the reason the equal-value guard is replicated here.
|
|
3131
|
+
//
|
|
3132
|
+
// A spelling the host chain already answers is left ALONE: NativeScript defines `text`,
|
|
3133
|
+
// `width`, … on its own prototypes with real setters driving the native view, and
|
|
3134
|
+
// shadowing one would break the widget. The engine's store still routes to it —
|
|
3135
|
+
// runJsPropertySetter resolves the class-or-chain setter — so declaring a GObject
|
|
3136
|
+
// property named after a widget property composes instead of colliding.
|
|
3137
|
+
function installForeignPropertyAccessors(proto, properties, skipped) {
|
|
3138
|
+
for (const spec of properties) {
|
|
3139
|
+
const name = spec.name;
|
|
3140
|
+
if (typeof name !== 'string' || name.length === 0) continue;
|
|
3141
|
+
const spellings = new Set([
|
|
3142
|
+
name,
|
|
3143
|
+
name.replace(/-/g, '_'),
|
|
3144
|
+
name.replace(/-([a-z])/g, (m) => m[1].toUpperCase()),
|
|
3145
|
+
]);
|
|
3146
|
+
const get = function () {
|
|
3147
|
+
const handle = foreignHandle(this);
|
|
3148
|
+
foreignMaterialise(this, name);
|
|
3149
|
+
return wrapReturn(native.getProperty(handle, name));
|
|
3150
|
+
};
|
|
3151
|
+
const set = function (value) {
|
|
3152
|
+
const handle = foreignHandle(this);
|
|
3153
|
+
// Equal-value guard, GJS's _generateAccessors setter verbatim — see the same
|
|
3154
|
+
// guard in wrapInstance's set trap for why it is only armed once the value
|
|
3155
|
+
// has been materialised.
|
|
3156
|
+
if (foreignMaterialise(this, name) && wrapReturn(native.getProperty(handle, name)) === value) return;
|
|
3157
|
+
native.setProperty(handle, name, unwrapArg(value));
|
|
3158
|
+
};
|
|
3159
|
+
generatedForeignSetters.add(set);
|
|
3160
|
+
for (const spelling of spellings) {
|
|
3161
|
+
defineForeignMember(proto, spelling, { get, set }, skipped);
|
|
3162
|
+
}
|
|
3163
|
+
}
|
|
3164
|
+
}
|
|
3165
|
+
|
|
2738
3166
|
// GJS's meta FIELD form: a class may declare its meta as static symbol fields
|
|
2739
3167
|
// (`Klass[GObject.GTypeName] = 'X'`) instead of passing a meta object, and GJS treats
|
|
2740
3168
|
// the symbols as the real source — `registerClass(meta, klass)` only copies meta onto
|
|
@@ -2774,13 +3202,19 @@ function registerClass(metaOrClass, maybeClass) {
|
|
|
2774
3202
|
meta = { ...fieldMeta, ...meta };
|
|
2775
3203
|
|
|
2776
3204
|
const parent = findParentGType(klass);
|
|
2777
|
-
|
|
3205
|
+
// A parent that is not a node-gi GObject class at all. GJS throws here
|
|
3206
|
+
// ("registerClass() used with invalid base class", refs/gjs GObject.js) and so does
|
|
3207
|
+
// every host but NativeScript — where the FOREIGN-PARENT path instead registers the
|
|
3208
|
+
// GType under GObject.Object and ATTACHES it to the host widget object. See the
|
|
3209
|
+
// FOREIGN-PARENT section above.
|
|
3210
|
+
if (parent === undefined && !FOREIGN_PARENT_CLASSES) {
|
|
2778
3211
|
throw new TypeError(
|
|
2779
3212
|
'GObject.registerClass: ' +
|
|
2780
3213
|
(klass.name || 'the class') +
|
|
2781
3214
|
' must extend a node-gi GObject class (e.g. GObject.Object)',
|
|
2782
3215
|
);
|
|
2783
3216
|
}
|
|
3217
|
+
const foreign = parent === undefined || parent.parentForeign === true;
|
|
2784
3218
|
|
|
2785
3219
|
const properties = [];
|
|
2786
3220
|
if (meta.Properties !== null && typeof meta.Properties === 'object') {
|
|
@@ -2822,10 +3256,16 @@ function registerClass(metaOrClass, maybeClass) {
|
|
|
2822
3256
|
// from its runtime GType handle (it has no introspection entry); an INTROSPECTED
|
|
2823
3257
|
// parent is resolved by namespace+type. Construction (the leaf's registered GType
|
|
2824
3258
|
// via the makeClass new.target routing) is already multi-level-correct either way.
|
|
3259
|
+
// A FOREIGN parent contributes no GType at all (it is a host widget class), so the
|
|
3260
|
+
// registered type is rooted at GObject.Object — the GType parent is independent of
|
|
3261
|
+
// the JS prototype chain, which is exactly what makes route D possible. A foreign
|
|
3262
|
+
// SUBCLASS still has a registered parent and takes the ordinary by-GType branch.
|
|
2825
3263
|
const typeHandle =
|
|
2826
|
-
parent
|
|
2827
|
-
? native.
|
|
2828
|
-
:
|
|
3264
|
+
parent === undefined
|
|
3265
|
+
? native.registerClass(gtypeName, 'GObject', 'Object', options)
|
|
3266
|
+
: parent.parentGTypeHandle !== undefined
|
|
3267
|
+
? native.registerClassFromGType(gtypeName, parent.parentGTypeHandle, options)
|
|
3268
|
+
: native.registerClass(gtypeName, parent.parentNamespace, parent.parentType, options);
|
|
2829
3269
|
|
|
2830
3270
|
// G3: register the GType for the GIVEN class IN PLACE (no throwaway Subclass) and
|
|
2831
3271
|
// return that same class. `$gtypeName`/`$gtype` are defined as OWN properties:
|
|
@@ -2865,6 +3305,8 @@ function registerClass(metaOrClass, maybeClass) {
|
|
|
2865
3305
|
children: children.length > 0 ? children : undefined,
|
|
2866
3306
|
internalChildren: internalChildren.length > 0 ? internalChildren : undefined,
|
|
2867
3307
|
constructPropertyNames: constructPropertyNames.length > 0 ? constructPropertyNames : undefined,
|
|
3308
|
+
foreign: foreign ? true : undefined,
|
|
3309
|
+
foreignProto: foreign ? klass.prototype : undefined,
|
|
2868
3310
|
});
|
|
2869
3311
|
// Which properties the equal-value guard owns: the ones DECLARED here, union the
|
|
2870
3312
|
// registered parent's (a G2 subclass inherits its parent's custom properties and
|
|
@@ -2881,6 +3323,36 @@ function registerClass(metaOrClass, maybeClass) {
|
|
|
2881
3323
|
// Reverse index for runCtorForCObject: the engine identifies a C-created instance
|
|
2882
3324
|
// by its GType name (= gtypeName, what native.registerClass registered).
|
|
2883
3325
|
classesByGType.set(gtypeName, klass);
|
|
3326
|
+
// FOREIGN parent: install the GObject surface as own members of this prototype and
|
|
3327
|
+
// index the prototype, so an instance can find ITS leaf registration by walking the
|
|
3328
|
+
// real (host-widget) prototype chain. Both before type_class_ref, like every record
|
|
3329
|
+
// above it. `foreignSkipped` names the members the host chain already owned — the
|
|
3330
|
+
// honest answer to "what did this class NOT get", which the route-D spec reports.
|
|
3331
|
+
if (foreign) {
|
|
3332
|
+
const reg = registeredClasses.get(klass);
|
|
3333
|
+
reg.foreignSkipped = installForeignGObjectSurface(klass.prototype, properties);
|
|
3334
|
+
foreignRegByProto.set(klass.prototype, reg);
|
|
3335
|
+
}
|
|
3336
|
+
// `$foreignSkipped`, readable from outside, because "which GObject spelling did the
|
|
3337
|
+
// host widget already own?" is the one question this route cannot answer in the
|
|
3338
|
+
// abstract: it depends on the parent class, so it has to be reported per
|
|
3339
|
+
// registration rather than documented once.
|
|
3340
|
+
//
|
|
3341
|
+
// On EVERY registration of this host, not only the foreign ones, and an empty array
|
|
3342
|
+
// where nothing was skipped. A class's static side is a Proxy that answers any
|
|
3343
|
+
// unknown name with a static-method thunk, so `X.$foreignSkipped` is already
|
|
3344
|
+
// truthy — a FUNCTION — wherever the property is absent, and `if
|
|
3345
|
+
// (X.$foreignSkipped.length)` would then throw rather than read "nothing". Defining
|
|
3346
|
+
// it is what makes the read mean what it says. Gated on the host flag all the same,
|
|
3347
|
+
// so the default host's registerClass keeps exactly GJS's static surface.
|
|
3348
|
+
if (FOREIGN_PARENT_CLASSES) {
|
|
3349
|
+
Object.defineProperty(klass, '$foreignSkipped', {
|
|
3350
|
+
value: Object.freeze([...(registeredClasses.get(klass).foreignSkipped ?? [])]),
|
|
3351
|
+
configurable: true,
|
|
3352
|
+
enumerable: false,
|
|
3353
|
+
writable: false,
|
|
3354
|
+
});
|
|
3355
|
+
}
|
|
2884
3356
|
// GJS parity: registration must be COMPLETE when registerClass returns. The
|
|
2885
3357
|
// engine installs the custom properties, signals and vfunc trampolines in
|
|
2886
3358
|
// class_init (src/class.cc), which GObject runs lazily on the first
|
|
@@ -2973,6 +3445,12 @@ function signalEmitByName(object, ...nameAndArgs) {
|
|
|
2973
3445
|
// makeClass). Merged enums are cached so identity is stable.
|
|
2974
3446
|
const OVERLAY_NAMES = new Set([
|
|
2975
3447
|
'registerClass',
|
|
3448
|
+
// Whether THIS host registers a class over a non-GObject parent (the FOREIGN-PARENT
|
|
3449
|
+
// section above) or throws on one. A caller that wants route D has no other way to
|
|
3450
|
+
// ask: the only alternative is to register and catch, which registers a GType as a
|
|
3451
|
+
// side effect of a question and turns "this host cannot" into the same outcome as
|
|
3452
|
+
// "this registration was wrong". Read by @gjsify/adwaita-gobject-native's engine.
|
|
3453
|
+
'$foreignParentClasses',
|
|
2976
3454
|
'GTypeName',
|
|
2977
3455
|
'properties',
|
|
2978
3456
|
'signals',
|
|
@@ -3042,6 +3520,7 @@ function decorateGObjectNamespace(baseNs) {
|
|
|
3042
3520
|
if (cache.has(prop)) return cache.get(prop);
|
|
3043
3521
|
let value;
|
|
3044
3522
|
if (prop === 'registerClass') value = registerClass;
|
|
3523
|
+
else if (prop === '$foreignParentClasses') value = FOREIGN_PARENT_CLASSES;
|
|
3045
3524
|
else if (prop === 'GTypeName') value = GTypeNameSymbol;
|
|
3046
3525
|
else if (prop === 'properties') value = propertiesSymbol;
|
|
3047
3526
|
else if (prop === 'signals') value = signalsSymbol;
|
|
@@ -3190,6 +3669,7 @@ function mirrorWindowingEnvIntoCrt() {
|
|
|
3190
3669
|
* @returns {Record<string, unknown>}
|
|
3191
3670
|
*/
|
|
3192
3671
|
export function requireGi(namespace, version) {
|
|
3672
|
+
ensureNative();
|
|
3193
3673
|
native.requireNamespace(namespace, version);
|
|
3194
3674
|
// The one moment the win32 CRT-environment mirror can run: the addon is loaded, so there
|
|
3195
3675
|
// is a GLib to call, and nothing has built a font map or loaded a fontconfig
|