@gjsify/node-gi 0.43.0 → 0.45.0
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 +121 -1092
- package/gi.js +101 -9
- package/gtk-runtime.d.ts +4 -1
- package/gtk-runtime.js +67 -8
- package/index.d.ts +39 -0
- package/index.js +3 -0
- 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-x64/node_gi.node +0 -0
- package/src/addon.cc +3 -0
- package/src/calls.cc +202 -66
- package/src/class.cc +240 -64
- package/src/common.h +11 -1
- package/src/marshal.cc +68 -1
package/gi.js
CHANGED
|
@@ -70,6 +70,26 @@ function unwrapArg(value) {
|
|
|
70
70
|
if (value !== null && typeof value === 'object' && value[HANDLE] !== undefined) {
|
|
71
71
|
return value[HANDLE];
|
|
72
72
|
}
|
|
73
|
+
// An ARRAY is a container whose ELEMENTS need the same unwrapping, and until this
|
|
74
|
+
// line they did not get it: only the top-level argument was unwrapped, so an array
|
|
75
|
+
// of GObjects reached the engine as an array of JS wrappers and
|
|
76
|
+
// `NodeGiToGIArgument`'s GI_TYPE_TAG_INTERFACE branch — which accepts nothing but
|
|
77
|
+
// an External carrying the handle — threw
|
|
78
|
+
// `expected a GObject handle as a container element`. That is EVERY GI method
|
|
79
|
+
// taking an array of objects, not one method: measured on
|
|
80
|
+
// `Gio.ListStore.splice(0, n, [a, b])`, where `append(a)` on the same store
|
|
81
|
+
// succeeded because one GObject as a top-level arg was always unwrapped.
|
|
82
|
+
//
|
|
83
|
+
// `Array.isArray`, not `typeof value === 'object'`: a Uint8Array is a
|
|
84
|
+
// GByteArray/guint8[] argument the engine reads directly, and mapping over it here
|
|
85
|
+
// would turn it into a plain Array and lose that.
|
|
86
|
+
//
|
|
87
|
+
// Recursive, so a nested array and a GObject inside one are handled by the same
|
|
88
|
+
// rule rather than by a second one that drifts — and this is now the same shape
|
|
89
|
+
// `wrapReturn` has carried all along for the OTHER direction. That asymmetry is
|
|
90
|
+
// why the gap stayed invisible: a container coming BACK had its elements wrapped,
|
|
91
|
+
// a container going IN did not, and nothing read both lines at once.
|
|
92
|
+
if (Array.isArray(value)) return value.map(unwrapArg);
|
|
73
93
|
if (typeof value === 'function') return wrapCallbackFn(value);
|
|
74
94
|
return value;
|
|
75
95
|
}
|
|
@@ -1142,6 +1162,26 @@ function protoForInstance(handle) {
|
|
|
1142
1162
|
return proto === null ? undefined : proto;
|
|
1143
1163
|
}
|
|
1144
1164
|
|
|
1165
|
+
// Attach a class prototype to a wrapper — as its [[Prototype]] AND as the USER_PROTO
|
|
1166
|
+
// symbol the traps resolve members through.
|
|
1167
|
+
//
|
|
1168
|
+
// The symbol alone is not the link. `constructor` is RESERVED, so the get trap hands
|
|
1169
|
+
// it straight to the bare target, whose [[Prototype]] was Object.prototype: every
|
|
1170
|
+
// introspected instance answered `Object` — carrying no `$gtype`, so
|
|
1171
|
+
// `GObject.type_name(inst.constructor.$gtype)` was `null` and @gjsify/gtk-host's
|
|
1172
|
+
// adopt() / nearestRegistered() / every descriptor lookup failed with
|
|
1173
|
+
// "No descriptor registered for null" (99 rows of its node leg). On gjs an instance's
|
|
1174
|
+
// [[Prototype]] IS its class's prototype and `constructor` is the class object itself
|
|
1175
|
+
// (measured on gjs 1.88.1), which is what the class prototype's own `constructor`
|
|
1176
|
+
// back-link — the makeClass Proxy, the object `requireGi` returns — now supplies.
|
|
1177
|
+
// It is also what puts a class prototype on the chain findProtoDescriptor walks for
|
|
1178
|
+
// `vfunc_*` (see makeClassPrototype). Works on the wrapper Proxy too: neither
|
|
1179
|
+
// setPrototypeOf nor the symbol write is trapped, so both reach the target.
|
|
1180
|
+
function linkInstanceProto(objOrProxy, proto) {
|
|
1181
|
+
objOrProxy[USER_PROTO] = proto;
|
|
1182
|
+
Object.setPrototypeOf(objOrProxy, proto);
|
|
1183
|
+
}
|
|
1184
|
+
|
|
1145
1185
|
// Wrap a live GObject handle as a GJS-shaped instance. The wrapper resolves its
|
|
1146
1186
|
// class prototype chain FIRST (a registerClass subclass's own methods, anything
|
|
1147
1187
|
// the program put on the introspected prototype) — so `inst.myMethod()` runs the
|
|
@@ -1164,14 +1204,14 @@ function wrapInstance(handle, userProto) {
|
|
|
1164
1204
|
if (userProto !== undefined && userProto !== cached[USER_PROTO]) {
|
|
1165
1205
|
const current = cached[USER_PROTO];
|
|
1166
1206
|
if (current === undefined || isProtoSameOrDescendantOf(userProto, current)) {
|
|
1167
|
-
cached
|
|
1207
|
+
linkInstanceProto(cached, userProto);
|
|
1168
1208
|
}
|
|
1169
1209
|
}
|
|
1170
1210
|
return cached;
|
|
1171
1211
|
}
|
|
1172
1212
|
const target = { [HANDLE]: handle };
|
|
1173
1213
|
const proto = userProto ?? protoForInstance(handle);
|
|
1174
|
-
if (proto !== undefined) target
|
|
1214
|
+
if (proto !== undefined) linkInstanceProto(target, proto);
|
|
1175
1215
|
const proxy = new Proxy(target, {
|
|
1176
1216
|
get(t, prop) {
|
|
1177
1217
|
if (prop === HANDLE) return handle;
|
|
@@ -1380,11 +1420,33 @@ function prototypeMethodHandle(self, where) {
|
|
|
1380
1420
|
// The invocable form of an introspected instance method, as it sits on a class
|
|
1381
1421
|
// prototype: `this` supplies the GObject, so ONE function serves every instance of
|
|
1382
1422
|
// the class and `inst.m === Cls.prototype.m` holds (gjs parity).
|
|
1383
|
-
|
|
1423
|
+
//
|
|
1424
|
+
// `arity` becomes the function's `length`, as on gjs (m_js_in_argc — the
|
|
1425
|
+
// JS-visible IN args, not the C arg count). A rest-args thunk reports 0, and
|
|
1426
|
+
// @gjsify/gtk-host's descriptor conformance reads `.length` to hold the widget
|
|
1427
|
+
// table against the installed GTK — `add_titled() takes 3 argument(s), but
|
|
1428
|
+
// GtkStack's takes 0` was this thunk, not the typelib.
|
|
1429
|
+
function makeGiMethod(where, method, arity) {
|
|
1384
1430
|
const fn = function (...args) {
|
|
1385
1431
|
return wrapReturn(native.callMethod(prototypeMethodHandle(this, where), method, unwrapArgs(args)));
|
|
1386
1432
|
};
|
|
1387
1433
|
Object.defineProperty(fn, 'name', { value: method, configurable: true });
|
|
1434
|
+
Object.defineProperty(fn, 'length', { value: arity, configurable: true });
|
|
1435
|
+
return fn;
|
|
1436
|
+
}
|
|
1437
|
+
|
|
1438
|
+
// The invocable form of an introspected VFUNC as it sits on a class prototype. Like
|
|
1439
|
+
// makeGiMethod, but the engine resolves the vtable slot of THIS class (not a captured
|
|
1440
|
+
// parent pointer), so it works on a plain `new Ns.Class()` — which is what gjs does
|
|
1441
|
+
// and what `vfunc_add_child` (a GtkBuildable slot with no introspected method form)
|
|
1442
|
+
// has no other route to.
|
|
1443
|
+
function makeGiVFunc(namespace, typeName, vfuncName, where) {
|
|
1444
|
+
const fn = function (...args) {
|
|
1445
|
+
return wrapReturn(
|
|
1446
|
+
native.callClassVfunc(prototypeMethodHandle(this, where), namespace, typeName, vfuncName, unwrapArgs(args)),
|
|
1447
|
+
);
|
|
1448
|
+
};
|
|
1449
|
+
Object.defineProperty(fn, 'name', { value: `vfunc_${vfuncName}`, configurable: true });
|
|
1388
1450
|
return fn;
|
|
1389
1451
|
}
|
|
1390
1452
|
|
|
@@ -1407,7 +1469,7 @@ function makeClassPrototype(namespace, typeName) {
|
|
|
1407
1469
|
configurable: true,
|
|
1408
1470
|
});
|
|
1409
1471
|
const where = `${namespace}.${typeName}.prototype`;
|
|
1410
|
-
const resolved = new Map(); // accessor name ->
|
|
1472
|
+
const resolved = new Map(); // accessor name -> method arity (-1 = absent) or vfunc addressability
|
|
1411
1473
|
// Writable + configurable: a program REPLACES a method to instrument it and puts
|
|
1412
1474
|
// the original back afterwards, which is the whole point of the prototype.
|
|
1413
1475
|
// Enumerable, like the methods gjs's resolve hook defines.
|
|
@@ -1421,6 +1483,25 @@ function makeClassPrototype(namespace, typeName) {
|
|
|
1421
1483
|
const materialize = (prop) => {
|
|
1422
1484
|
if (typeof prop !== 'string' || RESERVED.has(prop)) return false;
|
|
1423
1485
|
if (Object.prototype.hasOwnProperty.call(target, prop)) return true;
|
|
1486
|
+
// A vfunc slot. Defining it HERE — rather than leaving it to the vfuncChainProto
|
|
1487
|
+
// Proxy below the prototype — is what makes it reachable from an INSTANCE: the
|
|
1488
|
+
// wrapper resolves members by descriptor (findProtoDescriptor), and a Proxy that
|
|
1489
|
+
// traps only `get` reports no descriptor and no `in`, so `new Gtk.Box()
|
|
1490
|
+
// .vfunc_add_child` was `undefined` while `Gtk.Box.prototype.vfunc_add_child` was
|
|
1491
|
+
// a function. Gated on the engine being able to ADDRESS the slot, which gives gjs
|
|
1492
|
+
// parity two ways: an unknown vfunc name stays `undefined` instead of becoming a
|
|
1493
|
+
// throw-on-call thunk, and a name the engine cannot address (GObject's own vfuncs
|
|
1494
|
+
// report GI_UNKNOWN as their struct offset) still falls through to the chain-up
|
|
1495
|
+
// thunk, so `super.vfunc_dispose()` keeps working.
|
|
1496
|
+
if (prop.startsWith('vfunc_')) {
|
|
1497
|
+
const vfuncName = prop.slice('vfunc_'.length);
|
|
1498
|
+
let addressable = resolved.get(prop);
|
|
1499
|
+
if (addressable === undefined) {
|
|
1500
|
+
addressable = native.hasClassVfunc(namespace, typeName, vfuncName);
|
|
1501
|
+
resolved.set(prop, addressable);
|
|
1502
|
+
}
|
|
1503
|
+
return addressable && define(prop, makeGiVFunc(namespace, typeName, vfuncName, `${where}.${prop}`));
|
|
1504
|
+
}
|
|
1424
1505
|
// gjs's GObject.Object.prototype members are JS shims over namespace-level
|
|
1425
1506
|
// functions (see objectPrototypeShim), and `bind_property_full` even HAS an
|
|
1426
1507
|
// introspected GIR shadow — bind_with_closures, whose GValue write-back a
|
|
@@ -1431,12 +1512,14 @@ function makeClassPrototype(namespace, typeName) {
|
|
|
1431
1512
|
return shim(prototypeMethodHandle(this, `${where}.${prop}`), args);
|
|
1432
1513
|
});
|
|
1433
1514
|
}
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1515
|
+
// ONE native call answers both presence (arity >= 0) and the function's
|
|
1516
|
+
// `length` — see classMethodArity in calls.cc.
|
|
1517
|
+
let arity = resolved.get(prop);
|
|
1518
|
+
if (arity === undefined) {
|
|
1519
|
+
arity = native.classMethodArity(namespace, typeName, prop);
|
|
1520
|
+
resolved.set(prop, arity);
|
|
1438
1521
|
}
|
|
1439
|
-
return
|
|
1522
|
+
return arity >= 0 && define(prop, makeGiMethod(where, prop, arity));
|
|
1440
1523
|
};
|
|
1441
1524
|
return new Proxy(target, {
|
|
1442
1525
|
get(t, prop, receiver) {
|
|
@@ -2064,6 +2147,15 @@ function collectVfuncs(klass) {
|
|
|
2064
2147
|
const vfuncs = {};
|
|
2065
2148
|
const seen = new Set();
|
|
2066
2149
|
for (let p = klass.prototype; p !== null && p !== Object.prototype; p = Object.getPrototypeOf(p)) {
|
|
2150
|
+
// The INTROSPECTED base's prototype declares no user override, and since it
|
|
2151
|
+
// materializes its own `vfunc_*` members on first look-up (makeClassPrototype)
|
|
2152
|
+
// it now CARRIES such names: without this boundary a program that read
|
|
2153
|
+
// `base.vfunc_activate` before registering a subclass had that vfunc collected
|
|
2154
|
+
// as the subclass's own override, installing a trampoline for a slot the user
|
|
2155
|
+
// never wrote ("registerClass vfunc 'activate' not found on any ancestor").
|
|
2156
|
+
// CLASS_INFO is stamped only by makeClassPrototype, and only OWN counts — a
|
|
2157
|
+
// registered subclass's prototype inherits it from its base.
|
|
2158
|
+
if (p !== klass.prototype && Object.prototype.hasOwnProperty.call(p, CLASS_INFO)) break;
|
|
2067
2159
|
const owner = Object.getOwnPropertyDescriptor(p, 'constructor')?.value;
|
|
2068
2160
|
if (owner !== undefined && owner !== klass && registeredClasses.has(owner)) break;
|
|
2069
2161
|
for (const key of Object.getOwnPropertyNames(p)) {
|
package/gtk-runtime.d.ts
CHANGED
|
@@ -19,7 +19,10 @@ export function activateBundledGtkRuntime(native: {
|
|
|
19
19
|
prependLibraryPath?: (p: string) => void;
|
|
20
20
|
}): GtkRuntimeBundle | null;
|
|
21
21
|
|
|
22
|
-
/**
|
|
22
|
+
/** Absolute directories from `GJSIFY_GI_LIBRARY_PATH` — where an app carries its OWN GI libraries. */
|
|
23
|
+
export function appGiLibraryDirs(opts?: { platform?: NodeJS.Platform | string; env?: NodeJS.ProcessEnv }): string[];
|
|
24
|
+
|
|
25
|
+
/** Prepend GI's shared-library search path: the app's own dirs, then the GTK the policy chose. */
|
|
23
26
|
export function activateGiLibraryPath(native: { prependLibraryPath?: (p: string) => void }): string[];
|
|
24
27
|
/** TEST-ONLY: allow the activation to run again. */
|
|
25
28
|
export function resetGiLibraryPathForTests(): void;
|
package/gtk-runtime.js
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
// uses the host's GTK exactly as before).
|
|
10
10
|
import { createRequire } from 'node:module';
|
|
11
11
|
import { fileURLToPath } from 'node:url';
|
|
12
|
-
import { dirname, join } from 'node:path';
|
|
12
|
+
import { dirname, join, posix, win32 } from 'node:path';
|
|
13
13
|
import { existsSync } from 'node:fs';
|
|
14
14
|
import { composeDyldFallback, pathCovers, splitSearchPath, systemGiLibraryDirs } from './system-gi.js';
|
|
15
15
|
import { nativeCandidates, prebuildAddonPath } from './native-paths.js';
|
|
@@ -469,9 +469,49 @@ function gstScannerLeaf() {
|
|
|
469
469
|
|
|
470
470
|
let libraryPathActivated = false;
|
|
471
471
|
|
|
472
|
+
/**
|
|
473
|
+
* Where an APPLICATION carries the shared libraries ITS OWN typelibs name by bare
|
|
474
|
+
* leaf — `GJSIFY_GI_LIBRARY_PATH`, `:`-separated (`;` on win32).
|
|
475
|
+
*
|
|
476
|
+
* THE GAP THIS CLOSES is a different one from the note below. An app shipping a GI
|
|
477
|
+
* library of its own (`gjsify.ship.bundledTypelibs`) stages the typelib and its
|
|
478
|
+
* backer side by side and points `GI_TYPELIB_PATH` there, so GI finds the typelib
|
|
479
|
+
* and then `g_module_open`s a bare leaf nothing has located. The launcher's
|
|
480
|
+
* `LD_LIBRARY_PATH` covers that on linux; on macOS no environment variable can,
|
|
481
|
+
* because dyld strips every `DYLD_*` from a restricted process and a signed,
|
|
482
|
+
* hardened `.app` IS restricted — so a launcher depending on one works unsigned and
|
|
483
|
+
* breaks at signing time. The dirs below do not cover it either: they answer "where
|
|
484
|
+
* is the GTK stack", never "where are this application's own libraries". Neither
|
|
485
|
+
* does `dirname(GI_TYPELIB_PATH entry)`: {@link systemGiLibraryDirs} derives a
|
|
486
|
+
* libdir that way because GI's INSTALL layout puts typelibs in
|
|
487
|
+
* `<libdir>/girepository-1.0/`, and an app stages the pair FLAT, so that derivation
|
|
488
|
+
* yields the directory's PARENT — a real path holding nothing.
|
|
489
|
+
*
|
|
490
|
+
* Only ABSOLUTE entries are kept: a relative one resolves against the working
|
|
491
|
+
* directory, which for a double-clicked `.app` is `/`. PURE in its inputs, so the
|
|
492
|
+
* win32 dialect is executable from any host.
|
|
493
|
+
* @param {object} [opts]
|
|
494
|
+
* @param {NodeJS.Platform | string} [opts.platform]
|
|
495
|
+
* @param {NodeJS.ProcessEnv} [opts.env]
|
|
496
|
+
* @returns {string[]} absolute directories, in the order the variable listed them
|
|
497
|
+
*/
|
|
498
|
+
export function appGiLibraryDirs({ platform = process.platform, env = process.env } = {}) {
|
|
499
|
+
const isAbsolute = platform === 'win32' ? win32.isAbsolute : posix.isAbsolute;
|
|
500
|
+
const out = [];
|
|
501
|
+
for (const dir of splitSearchPath(env.GJSIFY_GI_LIBRARY_PATH, platform === 'win32' ? ';' : ':')) {
|
|
502
|
+
if (isAbsolute(dir) && !out.includes(dir)) out.push(dir);
|
|
503
|
+
}
|
|
504
|
+
return out;
|
|
505
|
+
}
|
|
506
|
+
|
|
472
507
|
/**
|
|
473
508
|
* Tell GI where the shared libraries its typelibs name by BARE LEAF actually are
|
|
474
|
-
* — for whichever GTK the policy chose
|
|
509
|
+
* — for whichever GTK the policy chose plus whatever the app carries itself, and
|
|
510
|
+
* WITHOUT a loader environment variable.
|
|
511
|
+
*
|
|
512
|
+
* `GJSIFY_GI_LIBRARY_PATH` is not a counter-example: it is read by THIS process, in
|
|
513
|
+
* JS, and handed to GI through the binding. dyld never sees it — which is the whole
|
|
514
|
+
* point, because dyld is what refuses to see `DYLD_*` in a signed, hardened app.
|
|
475
515
|
*
|
|
476
516
|
* THE GAP THIS CLOSES. A typelib records its backer as `libgtk-4.1.dylib`, and on
|
|
477
517
|
* macOS neither a relocated bundle's `lib` dir nor Homebrew's `/usr/local/lib` is
|
|
@@ -488,10 +528,23 @@ let libraryPathActivated = false;
|
|
|
488
528
|
* still cover a dylib pulled in by ANOTHER dylib's link closure, which never
|
|
489
529
|
* passes through GI.
|
|
490
530
|
*
|
|
491
|
-
* The dirs come from `gtkSource()`, not a second opinion: the bundle's
|
|
492
|
-
* for `bundle`, `systemGiLibraryDirs()` for `system` — the pair the
|
|
493
|
-
* composes. Mixing them is the two-copies hazard #920 records.
|
|
494
|
-
*
|
|
531
|
+
* The GTK dirs come from `gtkSource()`, not a second opinion: the bundle's
|
|
532
|
+
* `libDir` for `bundle`, `systemGiLibraryDirs()` for `system` — the pair the
|
|
533
|
+
* re-exec composes. Mixing them is the two-copies hazard #920 records. The
|
|
534
|
+
* application's own dirs ({@link appGiLibraryDirs}) are added on TOP of whichever
|
|
535
|
+
* of those the policy picked, and independently of it: an app carrying its own GI
|
|
536
|
+
* library needs it found whether the GTK behind it is a bundle, the host's, or —
|
|
537
|
+
* `source === 'none'` — neither.
|
|
538
|
+
*
|
|
539
|
+
* ORDER: the app's entries come FIRST. That only decides an OVERLAP — a leaf both
|
|
540
|
+
* places hold — since a name only one of them has is found either way. An
|
|
541
|
+
* explicitly-set variable is consent and outranks an inferred location (the
|
|
542
|
+
* precedence `GJSIFY_GTK_RUNTIME` already has over the probed bundle candidates),
|
|
543
|
+
* and on an overlap the app's copy is the one its OWN typelib was compiled against:
|
|
544
|
+
* the pair ships as a unit, and splitting it is not a missing library but an ABI
|
|
545
|
+
* mismatch — #910's failure shape, the expensive one.
|
|
546
|
+
*
|
|
547
|
+
* Idempotent, and never fatal: an addon predating the binding changes nothing.
|
|
495
548
|
* @param {{ prependLibraryPath?: (p: string) => void }} native the loaded addon
|
|
496
549
|
* @returns {string[]} the directories handed to GI (empty when there was nothing to add)
|
|
497
550
|
*/
|
|
@@ -501,12 +554,18 @@ export function activateGiLibraryPath(native) {
|
|
|
501
554
|
if (typeof native?.prependLibraryPath !== 'function') return [];
|
|
502
555
|
|
|
503
556
|
const source = gtkSource();
|
|
504
|
-
const
|
|
557
|
+
const gtkDirs =
|
|
505
558
|
source === 'bundle' ? [resolveGtkRuntimeBundle()?.libDir] : source === 'system' ? systemGiLibraryDirs() : [];
|
|
559
|
+
const dirs = [];
|
|
560
|
+
// Deduplicated: an app that names a directory the policy already found would
|
|
561
|
+
// otherwise grow GI's search path by a second, identical entry.
|
|
562
|
+
for (const dir of [...appGiLibraryDirs(), ...gtkDirs.filter(Boolean)]) {
|
|
563
|
+
if (!dirs.includes(dir)) dirs.push(dir);
|
|
564
|
+
}
|
|
506
565
|
const applied = [];
|
|
507
566
|
// LAST wins with a prepend, so walking in reverse leaves the array's own
|
|
508
567
|
// order intact in GI's search path.
|
|
509
|
-
for (const dir of dirs.
|
|
568
|
+
for (const dir of dirs.reverse()) {
|
|
510
569
|
native.prependLibraryPath(dir);
|
|
511
570
|
applied.unshift(dir);
|
|
512
571
|
}
|
package/index.d.ts
CHANGED
|
@@ -108,6 +108,16 @@ export function hasMethod(handle: GObjectHandle, methodName: string): boolean;
|
|
|
108
108
|
*/
|
|
109
109
|
export function hasClassMethod(namespace: string, typeName: string, methodName: string): boolean;
|
|
110
110
|
|
|
111
|
+
/**
|
|
112
|
+
* The JS-visible IN-arg count of an instance method — what gjs reports as the
|
|
113
|
+
* materialized function's `length` (IN/INOUT args minus array-length and
|
|
114
|
+
* callback user_data/destroy-notify slots) — or -1 when the name does not
|
|
115
|
+
* resolve to an invocable instance method. Resolution is shared with
|
|
116
|
+
* {@link hasClassMethod}, so `classMethodArity(...) >= 0` answers both
|
|
117
|
+
* questions in one call.
|
|
118
|
+
*/
|
|
119
|
+
export function classMethodArity(namespace: string, typeName: string, methodName: string): number;
|
|
120
|
+
|
|
111
121
|
/**
|
|
112
122
|
* Invoke a type-level constructor/static function (e.g. `Gio.File.new_for_path`,
|
|
113
123
|
* `Gtk.Label.new`) — a function found on a type but taking no instance. The Node
|
|
@@ -232,6 +242,34 @@ export function registerClass(
|
|
|
232
242
|
*/
|
|
233
243
|
export function callParentVfunc(handle: GObjectHandle, vfuncName: string, args?: unknown[]): unknown;
|
|
234
244
|
|
|
245
|
+
/**
|
|
246
|
+
* Whether `Ns.Type.prototype.vfunc_<vfuncName>` can be dispatched directly — i.e.
|
|
247
|
+
* an ancestor of `typeName` (or one of the interfaces it implements) declares that
|
|
248
|
+
* virtual function AND girepository can locate its slot in the class's vtable.
|
|
249
|
+
* The gate the L1 class prototype asks before materializing a vfunc member: `false`
|
|
250
|
+
* leaves the name `undefined` (gjs parity for an unknown vfunc) or, for a declared
|
|
251
|
+
* one whose struct offset girepository reports as unknown — GObject's own vfuncs —
|
|
252
|
+
* lets it fall through to the {@link callParentVfunc} chain-up thunk.
|
|
253
|
+
*/
|
|
254
|
+
export function hasClassVfunc(namespace: string, typeName: string, vfuncName: string): boolean;
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Invoke the virtual function `Ns.Type` carries in its vtable, with `handle` as the
|
|
258
|
+
* instance — what `new Gtk.Box().vfunc_add_child(builder, child, null)` does on gjs,
|
|
259
|
+
* and the ONLY route to a `Gtk.Buildable` adder (`add_child` is introspected as a
|
|
260
|
+
* vfunc, never as a method). Unlike {@link callParentVfunc} this needs no
|
|
261
|
+
* {@link registerClass} override: the slot comes from the class itself. Same
|
|
262
|
+
* marshalling and same `[returnValue?, ...outArgs]` return convention. Throws if the
|
|
263
|
+
* vfunc is not addressable on that class, or if `handle` is not an instance of it.
|
|
264
|
+
*/
|
|
265
|
+
export function callClassVfunc(
|
|
266
|
+
handle: GObjectHandle,
|
|
267
|
+
namespace: string,
|
|
268
|
+
typeName: string,
|
|
269
|
+
vfuncName: string,
|
|
270
|
+
args?: unknown[],
|
|
271
|
+
): unknown;
|
|
272
|
+
|
|
235
273
|
/**
|
|
236
274
|
* Construct a GObject of a registered type handle (from {@link registerClass})
|
|
237
275
|
* with optional construct/settable properties. For a templated type the engine
|
|
@@ -544,6 +582,7 @@ declare const native: {
|
|
|
544
582
|
callMethod: typeof callMethod;
|
|
545
583
|
hasMethod: typeof hasMethod;
|
|
546
584
|
hasClassMethod: typeof hasClassMethod;
|
|
585
|
+
classMethodArity: typeof classMethodArity;
|
|
547
586
|
callStaticMethod: typeof callStaticMethod;
|
|
548
587
|
constructStruct: typeof constructStruct;
|
|
549
588
|
newObject: typeof newObject;
|
package/index.js
CHANGED
|
@@ -198,6 +198,7 @@ export const callFunction = native.callFunction;
|
|
|
198
198
|
export const callMethod = native.callMethod;
|
|
199
199
|
export const hasMethod = native.hasMethod;
|
|
200
200
|
export const hasClassMethod = native.hasClassMethod;
|
|
201
|
+
export const classMethodArity = native.classMethodArity;
|
|
201
202
|
export const callStaticMethod = native.callStaticMethod;
|
|
202
203
|
export const constructStruct = native.constructStruct;
|
|
203
204
|
export const newObject = native.newObject;
|
|
@@ -212,6 +213,8 @@ export const registerClassFromGType = native.registerClassFromGType;
|
|
|
212
213
|
|
|
213
214
|
export const constructType = native.constructType;
|
|
214
215
|
export const callParentVfunc = native.callParentVfunc;
|
|
216
|
+
export const hasClassVfunc = native.hasClassVfunc;
|
|
217
|
+
export const callClassVfunc = native.callClassVfunc;
|
|
215
218
|
export const getTemplateChild = native.getTemplateChild;
|
|
216
219
|
export const getProperty = native.getProperty;
|
|
217
220
|
export const setProperty = native.setProperty;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gjsify/node-gi",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.45.0",
|
|
4
4
|
"description": "GObject-Introspection runtime for Node.js — load gi:// namespaces (GLib/GObject/Gio/…) with GJS-compatible semantics. Vendored + rewritten from node-gtk under MIT.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/src/addon.cc
CHANGED
|
@@ -79,6 +79,7 @@ static Napi::Object Init(Napi::Env env, Napi::Object exports) {
|
|
|
79
79
|
exports.Set("callMethod", Napi::Function::New(env, CallMethod));
|
|
80
80
|
exports.Set("hasMethod", Napi::Function::New(env, HasMethod));
|
|
81
81
|
exports.Set("hasClassMethod", Napi::Function::New(env, HasClassMethod));
|
|
82
|
+
exports.Set("classMethodArity", Napi::Function::New(env, ClassMethodArity));
|
|
82
83
|
exports.Set("callStaticMethod", Napi::Function::New(env, CallStaticMethod));
|
|
83
84
|
exports.Set("constructStruct", Napi::Function::New(env, ConstructStruct));
|
|
84
85
|
exports.Set("newObject", Napi::Function::New(env, NewObject));
|
|
@@ -86,6 +87,8 @@ static Napi::Object Init(Napi::Env env, Napi::Object exports) {
|
|
|
86
87
|
exports.Set("registerClassFromGType", Napi::Function::New(env, RegisterClassFromGType));
|
|
87
88
|
exports.Set("constructType", Napi::Function::New(env, ConstructType));
|
|
88
89
|
exports.Set("callParentVfunc", Napi::Function::New(env, CallParentVfunc));
|
|
90
|
+
exports.Set("hasClassVfunc", Napi::Function::New(env, HasClassVfunc));
|
|
91
|
+
exports.Set("callClassVfunc", Napi::Function::New(env, CallClassVfunc));
|
|
89
92
|
exports.Set("getTemplateChild", Napi::Function::New(env, GetTemplateChild));
|
|
90
93
|
exports.Set("getProperty", Napi::Function::New(env, GetProperty));
|
|
91
94
|
exports.Set("setProperty", Napi::Function::New(env, SetProperty));
|