@gtkx/runtime 1.6.0 → 2.0.0-beta.2

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.
Files changed (120) hide show
  1. package/README.md +5 -5
  2. package/dist/application-class.d.ts +1 -1
  3. package/dist/application-class.d.ts.map +1 -1
  4. package/dist/application-class.js +1 -2
  5. package/dist/application-class.js.map +1 -1
  6. package/dist/arg.d.ts +4 -1
  7. package/dist/arg.d.ts.map +1 -1
  8. package/dist/arg.js.map +1 -1
  9. package/dist/bind.d.ts.map +1 -1
  10. package/dist/bind.js +1 -9
  11. package/dist/bind.js.map +1 -1
  12. package/dist/callback.d.ts +6 -1
  13. package/dist/callback.d.ts.map +1 -1
  14. package/dist/callback.js +33 -21
  15. package/dist/callback.js.map +1 -1
  16. package/dist/closure.d.ts +3 -1
  17. package/dist/closure.d.ts.map +1 -1
  18. package/dist/closure.js +17 -6
  19. package/dist/closure.js.map +1 -1
  20. package/dist/descriptors.d.ts +22 -51
  21. package/dist/descriptors.d.ts.map +1 -1
  22. package/dist/descriptors.js +53 -27
  23. package/dist/descriptors.js.map +1 -1
  24. package/dist/fn.d.ts.map +1 -1
  25. package/dist/fn.js +23 -9
  26. package/dist/fn.js.map +1 -1
  27. package/dist/folded-lengths.d.ts +2 -1
  28. package/dist/folded-lengths.d.ts.map +1 -1
  29. package/dist/folded-lengths.js +17 -7
  30. package/dist/folded-lengths.js.map +1 -1
  31. package/dist/index.d.ts +2 -24
  32. package/dist/index.d.ts.map +1 -1
  33. package/dist/index.js +2 -24
  34. package/dist/index.js.map +1 -1
  35. package/dist/internal.d.ts +18 -2
  36. package/dist/internal.d.ts.map +1 -1
  37. package/dist/internal.js +14 -2
  38. package/dist/internal.js.map +1 -1
  39. package/dist/listeners.d.ts +3 -13
  40. package/dist/listeners.d.ts.map +1 -1
  41. package/dist/listeners.js +12 -12
  42. package/dist/listeners.js.map +1 -1
  43. package/dist/mixin.d.ts +5 -3
  44. package/dist/mixin.d.ts.map +1 -1
  45. package/dist/mixin.js +36 -9
  46. package/dist/mixin.js.map +1 -1
  47. package/dist/native-value.d.ts +9 -1
  48. package/dist/native-value.d.ts.map +1 -1
  49. package/dist/native-value.js +20 -3
  50. package/dist/native-value.js.map +1 -1
  51. package/dist/object.d.ts +11 -0
  52. package/dist/object.d.ts.map +1 -1
  53. package/dist/object.js +18 -25
  54. package/dist/object.js.map +1 -1
  55. package/dist/properties.d.ts +5 -3
  56. package/dist/properties.d.ts.map +1 -1
  57. package/dist/properties.js +67 -23
  58. package/dist/properties.js.map +1 -1
  59. package/dist/property-brand.d.ts +5 -0
  60. package/dist/property-brand.d.ts.map +1 -0
  61. package/dist/property-brand.js +5 -0
  62. package/dist/property-brand.js.map +1 -0
  63. package/dist/register-class.d.ts +142 -264
  64. package/dist/register-class.d.ts.map +1 -1
  65. package/dist/register-class.js +19 -37
  66. package/dist/register-class.js.map +1 -1
  67. package/dist/registry.d.ts +1 -1
  68. package/dist/registry.d.ts.map +1 -1
  69. package/dist/registry.js +4 -4
  70. package/dist/registry.js.map +1 -1
  71. package/dist/signal-brand.d.ts +12 -0
  72. package/dist/signal-brand.d.ts.map +1 -0
  73. package/dist/signal-brand.js +6 -0
  74. package/dist/signal-brand.js.map +1 -0
  75. package/dist/signal.d.ts +39 -2
  76. package/dist/signal.d.ts.map +1 -1
  77. package/dist/signal.js +84 -27
  78. package/dist/signal.js.map +1 -1
  79. package/dist/t.d.ts +5 -21
  80. package/dist/t.d.ts.map +1 -1
  81. package/dist/t.js +1 -5
  82. package/dist/t.js.map +1 -1
  83. package/dist/type.d.ts.map +1 -1
  84. package/dist/type.js +8 -2
  85. package/dist/type.js.map +1 -1
  86. package/dist/value.d.ts.map +1 -1
  87. package/dist/value.js +7 -1
  88. package/dist/value.js.map +1 -1
  89. package/dist/variant.d.ts +2 -6
  90. package/dist/variant.d.ts.map +1 -1
  91. package/dist/variant.js.map +1 -1
  92. package/dist/vfunc-call.d.ts.map +1 -1
  93. package/dist/vfunc-call.js +2 -11
  94. package/dist/vfunc-call.js.map +1 -1
  95. package/package.json +9 -5
  96. package/src/application-class.ts +2 -2
  97. package/src/arg.ts +4 -1
  98. package/src/bind.ts +1 -12
  99. package/src/callback.ts +53 -29
  100. package/src/closure.ts +31 -6
  101. package/src/descriptors.ts +95 -59
  102. package/src/fn.ts +31 -10
  103. package/src/folded-lengths.ts +23 -10
  104. package/src/index.ts +2 -24
  105. package/src/internal.ts +43 -2
  106. package/src/listeners.ts +21 -26
  107. package/src/mixin.ts +55 -9
  108. package/src/native-value.ts +24 -3
  109. package/src/object.ts +55 -5
  110. package/src/properties.ts +90 -26
  111. package/src/property-brand.ts +5 -0
  112. package/src/register-class.ts +338 -341
  113. package/src/registry.ts +4 -4
  114. package/src/signal-brand.ts +29 -0
  115. package/src/signal.ts +185 -39
  116. package/src/t.ts +5 -21
  117. package/src/type.ts +10 -2
  118. package/src/value.ts +9 -1
  119. package/src/variant.ts +2 -6
  120. package/src/vfunc-call.ts +3 -16
package/src/registry.ts CHANGED
@@ -10,7 +10,7 @@ import {
10
10
  setWrapper,
11
11
  } from "@gtkx/native";
12
12
  import { type AnyClass, walkClassChain } from "@gtkx/utils";
13
- import { copyLayerMembers, installMixins, type Mixin, type MixinReceiver } from "./mixin.js";
13
+ import { copyLayerMembers, createMixinLayer, installMixins, type Mixin, type MixinReceiver } from "./mixin.js";
14
14
  import {
15
15
  TYPE_INVALID,
16
16
  TYPE_OBJECT,
@@ -402,7 +402,7 @@ function registerInterface(cls: AnyClass, type: bigint, mixin: Mixin, layout?: I
402
402
  * @param interfaces Registered interface classes to adopt, in order.
403
403
  * @throws If an entry is not a registered interface.
404
404
  */
405
- function installInterfaces(cls: AnyClass, interfaces: AnyClass[]): void {
405
+ function installInterfaces(cls: AnyClass, interfaces: AnyClass[], inheritedOverrides: string[] = []): void {
406
406
  const mixins = interfaces.map((iface) => {
407
407
  const mixin = getInterfaceMixin(getClassType(iface));
408
408
 
@@ -413,7 +413,7 @@ function installInterfaces(cls: AnyClass, interfaces: AnyClass[]): void {
413
413
  return mixin;
414
414
  });
415
415
 
416
- installMixins(cls, mixins);
416
+ installMixins(cls, mixins, inheritedOverrides);
417
417
  }
418
418
 
419
419
  /**
@@ -535,7 +535,7 @@ function applyInterfaceMixin(cls: AnyClass, type: bigint, baseType: bigint, appl
535
535
 
536
536
  applied.add(type);
537
537
 
538
- return mixin(cls as AnyClass<MixinReceiver>);
538
+ return createMixinLayer(cls as AnyClass<MixinReceiver>, mixin, new Set());
539
539
  }
540
540
 
541
541
  function createComposedClass(base: AnyClass, runtimeType: bigint): AnyClass {
@@ -0,0 +1,29 @@
1
+ const naturalSignalMember: unique symbol = Symbol("gtkx.naturalSignalMember");
2
+ const classSignalMember: unique symbol = Symbol("gtkx.classSignalMember");
3
+ const signalMapOverride: unique symbol = Symbol("gtkx.signalMapOverride");
4
+ const signalEmitMapOverride: unique symbol = Symbol("gtkx.signalEmitMapOverride");
5
+
6
+ type ResolvedSignalMap<T, TFallback> = T extends {
7
+ [signalMapOverride]?: infer TResolver;
8
+ }
9
+ ? TResolver extends () => infer TMap
10
+ ? NonNullable<TMap>
11
+ : TFallback
12
+ : TFallback;
13
+
14
+ type ResolvedSignalEmitMap<T, TFallback> = T extends {
15
+ [signalEmitMapOverride]?: infer TResolver;
16
+ }
17
+ ? TResolver extends () => infer TMap
18
+ ? NonNullable<TMap>
19
+ : TFallback
20
+ : TFallback;
21
+
22
+ export {
23
+ classSignalMember,
24
+ naturalSignalMember,
25
+ signalEmitMapOverride,
26
+ type ResolvedSignalEmitMap,
27
+ type ResolvedSignalMap,
28
+ signalMapOverride,
29
+ };
package/src/signal.ts CHANGED
@@ -1,9 +1,10 @@
1
1
  import type { Descriptor, ExternalObject, Handle } from "@gtkx/native";
2
- import { getOrInsert, toCamelIdentifier, upperFirst } from "@gtkx/utils";
2
+ import { type AnyClass, toCamelIdentifier, upperFirst } from "@gtkx/utils";
3
+ import type { ResolvedSignalEmitMap, ResolvedSignalMap } from "./signal-brand.js";
3
4
  import { type Arg, isCallerAllocatedArg, isInoutArg, isOutputArg } from "./arg.js";
4
- import { bind, createBindCache } from "./bind.js";
5
+ import { bind } from "./bind.js";
5
6
  import { wrapCallback } from "./callback.js";
6
- import { toClosure } from "./closure.js";
7
+ import { newCCallbackClosure, toClosure } from "./closure.js";
7
8
  import {
8
9
  arrayT,
9
10
  biguint64T,
@@ -19,7 +20,7 @@ import {
19
20
  voidT,
20
21
  } from "./descriptors.js";
21
22
  import { LIB, VALUE_SIZE, VALUE_T } from "./library.js";
22
- import { getHandle } from "./registry.js";
23
+ import { getClassType, getHandle, getInstanceType } from "./registry.js";
23
24
  import { packTupleResult } from "./tuple.js";
24
25
  import { TYPE_INVALID, type TypedClass, typeInterfaces, typeParent } from "./type.js";
25
26
  import {
@@ -37,6 +38,55 @@ import {
37
38
  /** Function invoked when a connected GObject signal is emitted. */
38
39
  type SignalHandler = (...args: unknown[]) => unknown;
39
40
 
41
+ const isSignalHandler = (value: unknown): value is SignalHandler => typeof value === "function";
42
+
43
+ type DeclaredSignalMap<T> = T extends { __signals__?: infer TSignals } ? NonNullable<TSignals> : never;
44
+ type SignalMap<T> = ResolvedSignalMap<T, DeclaredSignalMap<T>>;
45
+ type SignalName<T> = Extract<keyof SignalMap<T>, string>;
46
+ type DeclaredSignalEmitMap<T> = T extends { __signalEmit__?: infer TSignals }
47
+ ? NonNullable<TSignals>
48
+ : T extends { __signals__?: infer TSignals }
49
+ ? {
50
+ [K in keyof NonNullable<TSignals>]: NonNullable<TSignals>[K] extends (
51
+ ...args: infer TArgs
52
+ ) => infer TResult
53
+ ? { args: TArgs; result: TResult }
54
+ : never;
55
+ }
56
+ : never;
57
+ type SignalEmitMap<T> = ResolvedSignalEmitMap<T, DeclaredSignalEmitMap<T>>;
58
+ type SignalEmitName<T> = Extract<keyof SignalEmitMap<T>, string>;
59
+ type SignalEmitArguments<T, K extends SignalEmitName<T>> = SignalEmitMap<T>[K] extends {
60
+ args: infer TArgs extends unknown[];
61
+ }
62
+ ? TArgs
63
+ : never;
64
+ type SignalEmitResult<T, K extends SignalEmitName<T>> = SignalEmitMap<T>[K] extends {
65
+ result: infer TResult;
66
+ }
67
+ ? TResult
68
+ : never;
69
+
70
+ type SignalConnector = (
71
+ instance: object,
72
+ signal: string,
73
+ handler: SignalHandler,
74
+ isAfter?: boolean,
75
+ ) => number;
76
+ type SignalEmitter = (instance: object, signal: string, args: unknown[]) => unknown;
77
+ type SignalDispatch = {
78
+ connect: SignalConnector;
79
+ emit: SignalEmitter;
80
+ };
81
+ type SignalDispatchSpec = {
82
+ connect: SignalConnector;
83
+ emit: SignalEmitter;
84
+ };
85
+ type PendingSignalDispatch = {
86
+ ownerType: bigint;
87
+ spec: SignalDispatchSpec;
88
+ };
89
+
40
90
  type DeclaredSignalTypes = {
41
91
  paramTypes: bigint[];
42
92
  returnType?: bigint;
@@ -69,8 +119,9 @@ type EmitArg = Arg & {
69
119
  value?: unknown;
70
120
  };
71
121
 
72
- const connectCache = createBindCache();
73
122
  const connectionTable: WeakMap<object, Map<string, Set<number>>> = new WeakMap();
123
+ const signalDispatchTable: Map<number, SignalDispatch> = new Map();
124
+ const pendingSignalDispatches: Map<string, PendingSignalDispatch[]> = new Map();
74
125
  const gQuarkFromString = bind(LIB, "g_quark_from_string", [stringT("borrowed")], uint32T);
75
126
  const gSignalLookup = bind(LIB, "g_signal_lookup", [stringT("borrowed"), biguint64T], uint32T);
76
127
  const gSignalName = bind(LIB, "g_signal_name", [uint32T], stringT("borrowed"));
@@ -115,6 +166,15 @@ const getSignalBaseName = (signal: string): string => {
115
166
  return detailIndex === -1 ? signal : signal.slice(0, detailIndex);
116
167
  };
117
168
 
169
+ const canonicalSignalName = (signal: string): string => getSignalBaseName(signal).replaceAll("_", "-");
170
+
171
+ const canonicalDetailedSignalName = (signal: string): string => {
172
+ const detailIndex = signal.indexOf("::");
173
+ const base = canonicalSignalName(signal);
174
+
175
+ return detailIndex === -1 ? base : `${base}${signal.slice(detailIndex)}`;
176
+ };
177
+
118
178
  function getSignalDetailQuark(signal: string): number {
119
179
  const detailIndex = signal.indexOf("::");
120
180
 
@@ -129,21 +189,8 @@ const isSignalHandlerConnected = (instance: object, handlerId: number): boolean
129
189
  gSignalHandlerIsConnected(getHandle(instance), handlerId) as boolean;
130
190
 
131
191
  const trackConnection = (instance: object, signal: string, handlerId: number): void => {
132
- let bySignal = connectionTable.get(instance);
133
-
134
- if (!bySignal) {
135
- bySignal = new Map();
136
- connectionTable.set(instance, bySignal);
137
- }
138
-
139
- let handlerIds = bySignal.get(signal);
140
-
141
- if (!handlerIds) {
142
- handlerIds = new Set();
143
- bySignal.set(signal, handlerIds);
144
- }
145
-
146
- handlerIds.add(handlerId);
192
+ const bySignal = connectionTable.getOrInsertComputed(instance, () => new Map<string, Set<number>>());
193
+ bySignal.getOrInsertComputed(signal, () => new Set<number>()).add(handlerId);
147
194
  };
148
195
 
149
196
  const untrackConnection = (instance: object, handlerId: number): void => {
@@ -194,7 +241,7 @@ function hasSignalListener(instance: object, signals?: string[]): boolean {
194
241
  }
195
242
 
196
243
  const names =
197
- signals === undefined ? bySignal.keys().toArray() : signals.map((signal) => getSignalBaseName(signal));
244
+ signals === undefined ? bySignal.keys().toArray() : signals.map((signal) => canonicalSignalName(signal));
198
245
 
199
246
  return names.some((name) => {
200
247
  const handlerIds = bySignal.get(name);
@@ -238,23 +285,11 @@ const buildSignalNames = (type: bigint): string[] => {
238
285
  return [...names];
239
286
  };
240
287
 
241
- const signalNamesFor = (type: bigint): string[] => getOrInsert(signalNameCache, type, buildSignalNames);
288
+ const signalNamesFor = (type: bigint): string[] => signalNameCache.getOrInsertComputed(type, buildSignalNames);
242
289
 
243
290
  const getSignalId = (instance: object, signal: string): number =>
244
291
  signalIdFor((instance as TypedClass).__type__, signal);
245
292
 
246
- function connectBind(type: bigint, signal: string, callback: CallbackDescriptor): (...values: unknown[]) => unknown {
247
- const key = `${String(type)}\0${getSignalBaseName(signal)}`;
248
-
249
- return connectCache(
250
- key,
251
- LIB,
252
- "g_signal_connect_data",
253
- [objectT("borrowed"), stringT("borrowed"), callback, uint32T],
254
- uint64T,
255
- );
256
- }
257
-
258
293
  /**
259
294
  * Connects a handler to a GObject signal on an instance and returns the handler id.
260
295
  * @param instance Emitter to connect to.
@@ -265,9 +300,10 @@ function connectSignal(instance: object, signal: string, spec: SignalConnectSpec
265
300
  const { callback, handler, isAfter } = spec;
266
301
  const wrapped = wrapCallback(handler, callback, "signal");
267
302
  const type: bigint = (instance as TypedClass).__type__;
268
- const connect = connectBind(type, signal, callback);
269
- const handlerId = connect(getHandle(instance), signal, wrapped, isAfter ? 1 : 0) as number;
270
- trackConnection(instance, getSignalBaseName(signal), handlerId);
303
+ const key = `${String(type)}\0${getSignalBaseName(signal)}`;
304
+ const closure = newCCallbackClosure(key, callback, wrapped);
305
+ const handlerId = gSignalConnectClosure(getHandle(instance), signal, closure, isAfter) as number;
306
+ trackConnection(instance, canonicalSignalName(signal), handlerId);
271
307
 
272
308
  return handlerId;
273
309
  }
@@ -279,7 +315,7 @@ function overrideSignalClassClosure(type: bigint, signalId: number, handler: Sig
279
315
  function connectClosureSignal(instance: object, signal: string, handler: SignalHandler, isAfter: boolean): number {
280
316
  const closure = toClosure((...args: unknown[]) => Reflect.apply(handler, null, args.slice(1)));
281
317
  const handlerId = gSignalConnectClosure(getHandle(instance), signal, closure, isAfter) as number;
282
- trackConnection(instance, getSignalBaseName(signal), handlerId);
318
+ trackConnection(instance, canonicalSignalName(signal), handlerId);
283
319
 
284
320
  return handlerId;
285
321
  }
@@ -375,19 +411,129 @@ function emitSignal(instance: object, signal: string, args: EmitArg[], returns?:
375
411
  return packTupleResult(readEmitOutputs(reads), fromValue(returnValue), true);
376
412
  }
377
413
 
414
+ function installSignalDispatch(
415
+ klass: AnyClass,
416
+ names: readonly string[],
417
+ spec: SignalDispatchSpec,
418
+ ): void {
419
+ const ownerType = getClassType(klass);
420
+
421
+ for (const rawName of names) {
422
+ const name = canonicalSignalName(rawName);
423
+ pendingSignalDispatches.getOrInsertComputed(name, () => []).push({ ownerType, spec });
424
+ }
425
+ }
426
+
427
+ const resolvePendingSignalDispatch = (signalId: number, name: string): SignalDispatch | undefined => {
428
+ const pending = pendingSignalDispatches.get(name) ?? [];
429
+
430
+ for (let index = pending.length - 1; index >= 0; index -= 1) {
431
+ const entry = pending[index];
432
+
433
+ if (
434
+ entry !== undefined &&
435
+ entry.ownerType !== TYPE_INVALID &&
436
+ signalIdFor(entry.ownerType, name) === signalId
437
+ ) {
438
+ signalDispatchTable.set(signalId, entry.spec);
439
+
440
+ return entry.spec;
441
+ }
442
+ }
443
+
444
+ return undefined;
445
+ };
446
+
447
+ const signalDispatchFor = (instance: object, signal: string): SignalDispatch | undefined => {
448
+ const name = canonicalSignalName(signal);
449
+ const instanceType = getInstanceType(instance);
450
+
451
+ if (instanceType === TYPE_INVALID) {
452
+ return undefined;
453
+ }
454
+
455
+ const signalId = signalIdFor(instanceType, name);
456
+
457
+ if (signalId === 0) {
458
+ return undefined;
459
+ }
460
+
461
+ return signalDispatchTable.get(signalId) ?? resolvePendingSignalDispatch(signalId, name);
462
+ };
463
+
464
+ const connectDispatcherFor = (instance: object, signal: string): SignalConnector => {
465
+ const dispatch = signalDispatchFor(instance, signal);
466
+
467
+ if (dispatch === undefined) {
468
+ throw new TypeError("connectSignal: unknown signal");
469
+ }
470
+
471
+ return dispatch.connect;
472
+ };
473
+
474
+ const emitDispatcherFor = (instance: object, signal: string): SignalEmitter => {
475
+ const dispatch = signalDispatchFor(instance, signal);
476
+
477
+ if (dispatch === undefined) {
478
+ throw new TypeError("emitSignal: unknown signal");
479
+ }
480
+
481
+ return dispatch.emit;
482
+ };
483
+
484
+ function signalConnect<T extends object, K extends SignalName<NoInfer<T>>>(
485
+ instance: T,
486
+ signal: K,
487
+ handler: SignalMap<NoInfer<T>>[K],
488
+ isAfter?: boolean,
489
+ ): number {
490
+ return connectSignalByName(instance, signal, handler, isAfter);
491
+ }
492
+
493
+ function connectSignalByName(
494
+ instance: object,
495
+ signal: string,
496
+ handler: unknown,
497
+ isAfter?: boolean,
498
+ ): number {
499
+ if (!isSignalHandler(handler)) {
500
+ throw new TypeError("connectSignal: handler must be a function");
501
+ }
502
+
503
+ return connectDispatcherFor(instance, signal)(instance, signal, handler, isAfter);
504
+ }
505
+
506
+ function emitSignalByName(instance: object, signal: string, args: unknown[]): unknown {
507
+ return emitDispatcherFor(instance, signal)(instance, signal, args);
508
+ }
509
+
510
+ function signalEmit<T extends object, K extends SignalEmitName<NoInfer<T>>>(
511
+ instance: T,
512
+ signal: K,
513
+ ...args: SignalEmitArguments<NoInfer<T>, K>
514
+ ): SignalEmitResult<NoInfer<T>, K> {
515
+ return emitSignalByName(instance, signal, args) as SignalEmitResult<NoInfer<T>, K>;
516
+ }
517
+
378
518
  export {
519
+ canonicalDetailedSignalName,
520
+ canonicalSignalName,
379
521
  signalForHandlerName,
380
522
  getSignalBaseName,
381
523
  signalIdFor,
382
524
  connectClosureSignal,
383
525
  connectSignal,
526
+ connectSignalByName,
384
527
  type DeclaredSignalTypes,
385
528
  disconnectSignal,
386
529
  emitDeclaredSignal,
387
530
  emitSignal,
531
+ emitSignalByName,
388
532
  hasSignalListener,
389
533
  isSignalHandlerConnected,
534
+ installSignalDispatch,
390
535
  overrideSignalClassClosure,
391
- untrackConnection,
536
+ signalConnect,
537
+ signalEmit,
392
538
  type SignalHandler,
393
539
  };
package/src/t.ts CHANGED
@@ -41,10 +41,7 @@ type T = {
41
41
  unichar: typeof helpers.unicharT;
42
42
  /** Descriptor for an opaque `gpointer` argument, taken from a typed array's memory or a numeric address. */
43
43
  buffer: typeof helpers.bufferT;
44
- /**
45
- * Builds a descriptor for a C string, whose optional length sizes the caller-allocated buffer
46
- * used when the string is passed by reference.
47
- */
44
+ /** Builds a C-string descriptor with optional caller-allocated length. */
48
45
  string: typeof helpers.stringT;
49
46
  /** Builds a descriptor for a `GObject`, wrapped in the class registered for its runtime GType. */
50
47
  object: typeof helpers.objectT;
@@ -82,28 +79,15 @@ type T = {
82
79
  cursorArray: typeof helpers.cursorArrayT;
83
80
  /** Builds a descriptor for a function pointer, marshalling a JavaScript function into a native closure. */
84
81
  callback: typeof helpers.callbackT;
85
- /**
86
- * Binds a native function, wiring up argument directions, `GError` checking, and packing output
87
- * arguments into the result.
88
- */
82
+ /** Binds a native function with argument directions, errors, and packed outputs. */
89
83
  fn: typeof fn;
90
- /**
91
- * Binds a struct field at a fixed offset, compiling its descriptor once into an accessor that
92
- * reads and writes it.
93
- */
84
+ /** Binds a struct field at a fixed offset. */
94
85
  field: typeof field;
95
- /**
96
- * Binds a struct field whose offset is supplied per access, for walking records stored at a
97
- * stride in a buffer.
98
- */
86
+ /** Binds a struct field whose offset is supplied per access. */
99
87
  fieldAt: typeof fieldAt;
100
88
  };
101
89
 
102
- /**
103
- * Type descriptor builder: a collection of factory helpers that describe C types for
104
- * FFI marshalling, plus `bind` to bind a native function and `fn` to describe a
105
- * function signature.
106
- */
90
+ /** FFI descriptor factories and native function binders. */
107
91
  const t: T = {
108
92
  bind,
109
93
  int8: helpers.int8T,
package/src/type.ts CHANGED
@@ -86,7 +86,7 @@ const TYPE_VARIANT: bigint = typeFromName("GVariant");
86
86
  /** GType tag for a Unicode character stored as `guint`. */
87
87
  const TYPE_UNICHAR: bigint = typeFromName("guint");
88
88
  const getErrorType: () => bigint = lazyType("GError");
89
- const getStrvType: () => bigint = lazyType("GStrv");
89
+ const getStrvType = (): bigint => resolveType(LIB, "g_strv_get_type");
90
90
 
91
91
  const PLAIN_DESCRIPTOR_TYPES: Partial<Record<Descriptor["kind"], bigint>> = {
92
92
  boolean: TYPE_BOOLEAN,
@@ -244,6 +244,14 @@ function isResolvableDescriptor(descriptor: Descriptor): descriptor is Resolvabl
244
244
  return RESOLVABLE_DESCRIPTOR_KINDS.has(descriptor.kind);
245
245
  }
246
246
 
247
+ function resolveEnumOrFlagsType(descriptor: Extract<Descriptor, { kind: "enum" | "flags" }>): bigint {
248
+ if (descriptor.getTypeFnName === "") {
249
+ return descriptor.isSigned ? TYPE_INT : TYPE_UINT;
250
+ }
251
+
252
+ return resolveType(descriptor.sharedLibrary, descriptor.getTypeFnName);
253
+ }
254
+
247
255
  function resolveDescriptorType(descriptor: Descriptor): bigint {
248
256
  if (descriptor.kind === "biguint64" && "type" in descriptor) {
249
257
  return TYPE_GTYPE;
@@ -262,7 +270,7 @@ function resolveDescriptorType(descriptor: Descriptor): bigint {
262
270
  switch (descriptor.kind) {
263
271
  case "enum":
264
272
  case "flags": {
265
- return resolveType(descriptor.sharedLibrary, descriptor.getTypeFnName);
273
+ return resolveEnumOrFlagsType(descriptor);
266
274
  }
267
275
  case "boxed": {
268
276
  return resolveBoxedType(descriptor);
package/src/value.ts CHANGED
@@ -422,6 +422,14 @@ const arrayValueType = (descriptor: ArrayDescriptor): ValueType => {
422
422
  throw new Error(`Unsupported array type ${descriptor.arrayKind} of ${descriptor.itemDescriptor.kind}`);
423
423
  };
424
424
 
425
+ const resolveEnumOrFlagsValueType = (descriptor: Extract<Descriptor, { kind: "enum" | "flags" }>): ValueType => {
426
+ if (descriptor.getTypeFnName === "") {
427
+ return descriptor.kind === "flags" ? flagsValueType : enumValueType;
428
+ }
429
+
430
+ return enumOrFlagsValueType(resolveType(descriptor.sharedLibrary, descriptor.getTypeFnName));
431
+ };
432
+
425
433
  const resolveValueType = (descriptor: Descriptor): ValueType => {
426
434
  if (descriptor.kind === "biguint64" && "type" in descriptor) {
427
435
  return typeValueType;
@@ -440,7 +448,7 @@ const resolveValueType = (descriptor: Descriptor): ValueType => {
440
448
  switch (descriptor.kind) {
441
449
  case "enum":
442
450
  case "flags": {
443
- return enumOrFlagsValueType(resolveType(descriptor.sharedLibrary, descriptor.getTypeFnName));
451
+ return resolveEnumOrFlagsValueType(descriptor);
444
452
  }
445
453
  case "boxed": {
446
454
  return boxedValueType(resolveBoxedType(descriptor));
package/src/variant.ts CHANGED
@@ -34,10 +34,7 @@ type BasicValueMap<Nested> = {
34
34
 
35
35
  /** Single-character code of one of the GVariant basic types in {@link BasicValueMap}. */
36
36
  type BasicCode = keyof BasicValueMap<unknown>;
37
- /**
38
- * JavaScript type a byte array (`ay`) unpacks to: the same one the generated bindings use for byte sequences, so a
39
- * `Uint8Array` under the `v2ByteArrays` future flag and a `number[]` otherwise.
40
- */
37
+ /** JavaScript type a byte array (`ay`) unpacks to. */
41
38
  type ByteArray = ReturnType<GLib.Bytes["unrefToArray"]>;
42
39
  /** Values a byte array (`ay`) packs from. */
43
40
  type ByteArrayInput = Uint8Array | number[];
@@ -503,8 +500,7 @@ const toVariant = <S extends string>(typeString: S, value: VariantInput<S>): GLi
503
500
  /**
504
501
  * Unpacks a `GLib.Variant` into the JavaScript value its GVariant type describes, the inverse of
505
502
  * {@link toVariant}. A dictionary keyed by strings unpacks to a record and one keyed by anything
506
- * else to a `Map`, an array to an array, a byte array (`ay`) to the byte array type the generated
507
- * bindings use (`Uint8Array` under the `v2ByteArrays` future flag, `number[]` otherwise), a tuple
503
+ * else to a `Map`, an array to an array, a byte array (`ay`) to a `Uint8Array`, a tuple
508
504
  * to an array of its members, a maybe to its value or `null`, and a nested variant to the
509
505
  * `GLib.Variant` itself unless `recursive` is set, in which case it is unwrapped all the way down.
510
506
  *
package/src/vfunc-call.ts CHANGED
@@ -1,6 +1,5 @@
1
- import type { BindVfuncOptions } from "@gtkx/native";
2
1
  import type { AnyClass } from "@gtkx/utils";
3
- import { bindVfunc } from "@gtkx/native";
2
+ import { bindVfunc, type BindVfuncOptions } from "@gtkx/native";
4
3
  import { type Arg, isCallerAllocatedArg, requiresInputArg } from "./arg.js";
5
4
  import { buildNativeArgTypes, fromNativeCallable } from "./fn.js";
6
5
  import { toNative } from "./native-value.js";
@@ -106,21 +105,9 @@ function buildInvoker(slot: ResolvedSlot, instanceType: bigint | undefined, call
106
105
  }
107
106
 
108
107
  function cachedInvoker(cache: InvokerCache, owner: AnyClass, key: string, build: () => Invoker): Invoker {
109
- let byKey = cache.get(owner);
108
+ const byKey = cache.getOrInsertComputed(owner, () => new Map<string, Invoker>());
110
109
 
111
- if (byKey === undefined) {
112
- byKey = new Map();
113
- cache.set(owner, byKey);
114
- }
115
-
116
- let invoker = byKey.get(key);
117
-
118
- if (invoker === undefined) {
119
- invoker = build();
120
- byKey.set(key, invoker);
121
- }
122
-
123
- return invoker;
110
+ return byKey.getOrInsertComputed(key, build);
124
111
  }
125
112
 
126
113
  function resolveParentSlot(klass: AnyClass, parentType: bigint, methodName: string): ResolvedSlot | undefined {