@gtkx/runtime 1.5.0 → 2.0.0-beta.1

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 (101) 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 +36 -36
  21. package/dist/descriptors.d.ts.map +1 -1
  22. package/dist/descriptors.js +65 -26
  23. package/dist/descriptors.js.map +1 -1
  24. package/dist/element-metadata.d.ts +9 -0
  25. package/dist/element-metadata.d.ts.map +1 -0
  26. package/dist/element-metadata.js +14 -0
  27. package/dist/element-metadata.js.map +1 -0
  28. package/dist/fn.d.ts +8 -2
  29. package/dist/fn.d.ts.map +1 -1
  30. package/dist/fn.js +43 -13
  31. package/dist/fn.js.map +1 -1
  32. package/dist/folded-lengths.d.ts +2 -1
  33. package/dist/folded-lengths.d.ts.map +1 -1
  34. package/dist/folded-lengths.js +17 -7
  35. package/dist/folded-lengths.js.map +1 -1
  36. package/dist/index.d.ts +4 -26
  37. package/dist/index.d.ts.map +1 -1
  38. package/dist/index.js +4 -26
  39. package/dist/index.js.map +1 -1
  40. package/dist/internal.d.ts +5 -2
  41. package/dist/internal.d.ts.map +1 -1
  42. package/dist/internal.js +5 -2
  43. package/dist/internal.js.map +1 -1
  44. package/dist/native-value.d.ts +9 -1
  45. package/dist/native-value.d.ts.map +1 -1
  46. package/dist/native-value.js +41 -5
  47. package/dist/native-value.js.map +1 -1
  48. package/dist/properties.d.ts +1 -1
  49. package/dist/properties.d.ts.map +1 -1
  50. package/dist/properties.js +2 -11
  51. package/dist/properties.js.map +1 -1
  52. package/dist/register-class.d.ts +45 -249
  53. package/dist/register-class.d.ts.map +1 -1
  54. package/dist/register-class.js +38 -1
  55. package/dist/register-class.js.map +1 -1
  56. package/dist/registry.d.ts +26 -3
  57. package/dist/registry.d.ts.map +1 -1
  58. package/dist/registry.js +75 -20
  59. package/dist/registry.js.map +1 -1
  60. package/dist/signal.d.ts.map +1 -1
  61. package/dist/signal.js +9 -22
  62. package/dist/signal.js.map +1 -1
  63. package/dist/t.d.ts +5 -21
  64. package/dist/t.d.ts.map +1 -1
  65. package/dist/t.js +1 -5
  66. package/dist/t.js.map +1 -1
  67. package/dist/type.d.ts +10 -1
  68. package/dist/type.d.ts.map +1 -1
  69. package/dist/type.js +21 -2
  70. package/dist/type.js.map +1 -1
  71. package/dist/value.d.ts.map +1 -1
  72. package/dist/value.js +26 -4
  73. package/dist/value.js.map +1 -1
  74. package/dist/variant.d.ts +2 -6
  75. package/dist/variant.d.ts.map +1 -1
  76. package/dist/variant.js.map +1 -1
  77. package/dist/vfunc-call.d.ts.map +1 -1
  78. package/dist/vfunc-call.js +2 -11
  79. package/dist/vfunc-call.js.map +1 -1
  80. package/package.json +15 -6
  81. package/src/application-class.ts +2 -2
  82. package/src/arg.ts +4 -1
  83. package/src/bind.ts +1 -12
  84. package/src/callback.ts +53 -29
  85. package/src/closure.ts +31 -6
  86. package/src/descriptors.ts +126 -44
  87. package/src/element-metadata.ts +24 -0
  88. package/src/fn.ts +64 -15
  89. package/src/folded-lengths.ts +23 -10
  90. package/src/index.ts +5 -24
  91. package/src/internal.ts +9 -2
  92. package/src/native-value.ts +54 -5
  93. package/src/properties.ts +3 -16
  94. package/src/register-class.ts +104 -249
  95. package/src/registry.ts +110 -19
  96. package/src/signal.ts +9 -34
  97. package/src/t.ts +5 -21
  98. package/src/type.ts +34 -1
  99. package/src/value.ts +37 -5
  100. package/src/variant.ts +2 -6
  101. package/src/vfunc-call.ts +3 -16
@@ -33,16 +33,25 @@ type FlagsDescriptor = Extract<Descriptor, { kind: "flags" }>;
33
33
  type BooleanDescriptor = Extract<Descriptor, { kind: "boolean" }>;
34
34
  /** Descriptor variant for a C string. */
35
35
  type StringDescriptor = Extract<Descriptor, { kind: "string" }>;
36
- /** Descriptor variant for a `GObject`. */
37
- type ObjectDescriptor = Extract<Descriptor, { kind: "object" }>;
36
+
37
+ /** Descriptor variant for a `GObject`, extended with the statically declared type of its value. */
38
+ type ObjectDescriptor = Extract<Descriptor, { kind: "object" }> & {
39
+ /** Retains and supplies the declared wrapper when no more-derived wrapper is registered. */
40
+ fallbackClass?: () => AnyClass;
41
+ };
42
+
38
43
  /** Descriptor variant for a `gunichar`. */
39
44
  type UnicharDescriptor = Extract<Descriptor, { kind: "unichar" }>;
40
45
  /** Descriptor variant for the absence of a value. */
41
46
  type VoidDescriptor = Extract<Descriptor, { kind: "void" }>;
42
47
  /** Descriptor variant for an opaque `gpointer`. */
43
48
  type BufferDescriptor = Extract<Descriptor, { kind: "buffer" }>;
44
- /** Descriptor variant for a `GBoxed` value. */
45
- type BoxedDescriptor = Extract<Descriptor, { kind: "boxed" }>;
49
+
50
+ /** Descriptor variant for a `GBoxed` value, extended with the statically declared type of its value. */
51
+ type BoxedDescriptor = Extract<Descriptor, { kind: "boxed" }> & {
52
+ /** Retains and supplies the declared wrapper when none is registered. */
53
+ fallbackClass?: () => AnyClass;
54
+ };
46
55
 
47
56
  /** Descriptor variant for a plain C struct, extended with the class its decoded value is wrapped in. */
48
57
  type StructDescriptor = Extract<Descriptor, { kind: "struct" }> & {
@@ -54,6 +63,8 @@ type StructDescriptor = Extract<Descriptor, { kind: "struct" }> & {
54
63
  type FundamentalDescriptor = Extract<Descriptor, { kind: "fundamental" }> & {
55
64
  /** Class a decoded value is wrapped in; without it the wrapper comes from the type named by `typeName`. */
56
65
  wrapperClass?: AnyClass;
66
+ /** Retains and supplies the declared wrapper after checking the runtime-type registry. */
67
+ fallbackClass?: () => AnyClass;
57
68
  };
58
69
 
59
70
  /** Descriptor variant for an array of items in one of the supported container layouts. */
@@ -87,12 +98,11 @@ type BoxedOptions = {
87
98
  freeFnName?: string;
88
99
  /** Byte size of the value, needed to copy it into an inline field. */
89
100
  size?: number;
101
+ /** Returns the wrapper class of the declared type, used when none is registered for the GType. */
102
+ fallbackClass?: () => AnyClass;
90
103
  };
91
104
 
92
- /**
93
- * What the bindings do with a callback's return value, how the callee takes its closure, and how
94
- * long that closure has to stay alive.
95
- */
105
+ /** Callback result, closure ownership, and lifetime options. */
96
106
  type CallbackOptions = {
97
107
  /** The callee also takes a destroy notify, which frees the closure once it is done with it. */
98
108
  hasDestroy?: boolean;
@@ -102,10 +112,7 @@ type CallbackOptions = {
102
112
  hasUserData?: boolean;
103
113
  /** Position of `user_data` among the callback's own arguments, dropped before the closure is called. */
104
114
  userDataIndex?: number;
105
- /**
106
- * The callback's C signature ends with a `GError**`, which receives a `GError` built from
107
- * whatever the JavaScript function throws while the callback returns its failure value.
108
- */
115
+ /** Converts a thrown value to the callback's trailing `GError**`. */
109
116
  canThrow?: boolean;
110
117
  /** Lifetime of the closure; defaults to `notified` when `hasDestroy` is set and `call` otherwise. */
111
118
  scope?: CallbackDescriptor["scope"];
@@ -123,12 +130,10 @@ type ArrayOptions = {
123
130
  fixedSize?: number | undefined;
124
131
  /** Whether the array carries raw bytes, and so decodes to a `Uint8Array` rather than to numbers. */
125
132
  isBytes?: boolean | undefined;
126
- /**
127
- * Whether the caller supplies the storage for a fixed-length out array: the runtime allocates
128
- * a buffer of the element stride times the fixed element count, passes its pointer, and
129
- * decodes the elements the callee wrote into it.
130
- */
133
+ /** Allocates and decodes caller-owned storage for a fixed-length out array. */
131
134
  isCallerAllocated?: boolean | undefined;
135
+ /** Adds a zero element after the declared length. */
136
+ isZeroTerminated?: boolean | undefined;
132
137
  };
133
138
 
134
139
  /** Where a cursor array's base buffer and total length come from. */
@@ -147,6 +152,8 @@ type FundamentalOptions = {
147
152
  typeName?: string;
148
153
  /** Class a decoded value is wrapped in, instead of the one registered for `typeName`. */
149
154
  wrapperClass?: AnyClass;
155
+ /** Returns the wrapper class of the declared type, used when none is registered for the value's type. */
156
+ fallbackClass?: () => AnyClass;
150
157
  /** The caller owns the storage the callee fills, so a decoded value is borrowed instead of acquired. */
151
158
  isCallerAllocated?: boolean;
152
159
  /** The value is embedded in the containing struct rather than reached through a pointer. */
@@ -169,6 +176,12 @@ type StructOptions = {
169
176
  size?: number;
170
177
  /** Class a decoded value is wrapped in, instead of the one registered for its GType. */
171
178
  wrapperClass?: AnyClass;
179
+ /** Library the struct's declared copy and free functions are resolved from; without it neither is used. */
180
+ sharedLibrary?: string;
181
+ /** Function duplicating an instance, used instead of a byte copy when the struct declares one. */
182
+ copyFnName?: string;
183
+ /** Function releasing an instance, used instead of `g_free` when the struct declares one. */
184
+ freeFnName?: string;
172
185
  };
173
186
 
174
187
  /** Descriptor for a `gint8`, marshalled as a number. */
@@ -210,15 +223,43 @@ const fundamentalLifecycles: Map<string, FundamentalLifecycle> = new Map();
210
223
  const isGtypeDescriptor = (descriptor: Descriptor): descriptor is TypeDescriptor =>
211
224
  descriptor.kind === "biguint64" && "type" in descriptor;
212
225
 
213
- /**
214
- * Builds a descriptor for a C string, whose optional length sizes the caller-allocated buffer
215
- * used when the string is passed by reference.
216
- */
217
- const stringT = (ownership: Ownership = "borrowed", length?: number): StringDescriptor =>
218
- length === undefined ? { kind: "string", ownership } : { kind: "string", ownership, length };
226
+ /** Builds a C-string descriptor, including optional buffer length and record-owned storage. */
227
+ const stringT = (
228
+ ownership: Ownership = "borrowed",
229
+ length?: number,
230
+ hasOwnedStorage?: boolean,
231
+ ): StringDescriptor => {
232
+ const result: StringDescriptor = { kind: "string", ownership };
233
+
234
+ if (length !== undefined) {
235
+ result.length = length;
236
+ }
237
+
238
+ if (hasOwnedStorage === true) {
239
+ result.hasOwnedStorage = true;
240
+ }
241
+
242
+ return result;
243
+ };
244
+
245
+ /** Builds a runtime-typed `GObject` descriptor with an optional retained fallback class. */
246
+ const objectT = (
247
+ ownership: Ownership = "borrowed",
248
+ fallbackClass?: () => AnyClass,
249
+ typeName?: string,
250
+ ): ObjectDescriptor => {
251
+ const result: ObjectDescriptor = { kind: "object", ownership };
252
+
253
+ if (fallbackClass !== undefined) {
254
+ result.fallbackClass = fallbackClass;
255
+ }
256
+
257
+ if (typeName !== undefined) {
258
+ result.typeName = typeName;
259
+ }
219
260
 
220
- /** Builds a descriptor for a `GObject`, wrapped in the class registered for its runtime GType. */
221
- const objectT = (ownership: Ownership = "borrowed"): ObjectDescriptor => ({ kind: "object", ownership });
261
+ return result;
262
+ };
222
263
 
223
264
  /** Wraps a descriptor in a pointer to it, for an output or inout argument. */
224
265
  const refT = (innerDescriptor: Descriptor, isInout = false): RefDescriptor =>
@@ -236,19 +277,28 @@ const hashTableT = (
236
277
  ownership,
237
278
  });
238
279
 
239
- /** Builds a descriptor for an enumeration, resolving its GType from the named `get_type` function. */
240
- const enumT = (sharedLibrary: string, typeFnName: string, isSigned: boolean): EnumDescriptor => ({
241
- kind: "enum",
242
- sharedLibrary,
243
- getTypeFnName: typeFnName,
244
- isSigned,
245
- });
280
+ /** Builds an enum descriptor from a GType function or an explicit set of members. */
281
+ const enumT = (
282
+ sharedLibrary: string,
283
+ typeFnName: string,
284
+ isSigned: boolean,
285
+ members?: number[],
286
+ ): EnumDescriptor => {
287
+ const result: EnumDescriptor = {
288
+ kind: "enum",
289
+ sharedLibrary,
290
+ getTypeFnName: typeFnName,
291
+ isSigned,
292
+ };
246
293
 
247
- /**
248
- * Builds a descriptor for a flags type, resolving its GType from the named `get_type` function.
249
- * For flags without a registered GType, pass empty library and function names and supply `mask`,
250
- * the union of all valid bits, which invalid combinations are rejected against.
251
- */
294
+ if (members !== undefined) {
295
+ result.members = members;
296
+ }
297
+
298
+ return result;
299
+ };
300
+
301
+ /** Builds a flags descriptor from a GType function or an explicit bit mask. */
252
302
  const flagsT = (sharedLibrary: string, typeFnName: string, isSigned: boolean, mask?: number): FlagsDescriptor => {
253
303
  const result: FlagsDescriptor = {
254
304
  kind: "flags",
@@ -281,6 +331,10 @@ const applyBoxedNames = (result: BoxedDescriptor, options: BoxedOptions): void =
281
331
  const applyBoxedOptions = (result: BoxedDescriptor, options: BoxedOptions): void => {
282
332
  applyBoxedNames(result, options);
283
333
 
334
+ if (options.fallbackClass !== undefined) {
335
+ result.fallbackClass = options.fallbackClass;
336
+ }
337
+
284
338
  if (options.isCallerAllocated) {
285
339
  result.isCallerAllocated = true;
286
340
  }
@@ -307,6 +361,22 @@ const boxedT = (typeName: string, options: BoxedOptions = {}): BoxedDescriptor =
307
361
  return result;
308
362
  };
309
363
 
364
+ const applyStructLifecycle = (result: StructDescriptor, options: StructOptions): void => {
365
+ if (options.sharedLibrary === undefined) {
366
+ return;
367
+ }
368
+
369
+ result.sharedLibrary = options.sharedLibrary;
370
+
371
+ if (options.copyFnName !== undefined) {
372
+ result.copyFnName = options.copyFnName;
373
+ }
374
+
375
+ if (options.freeFnName !== undefined) {
376
+ result.freeFnName = options.freeFnName;
377
+ }
378
+ };
379
+
310
380
  /** Builds a descriptor for a plain C struct. */
311
381
  const structT = (ownership: Ownership = "borrowed", options: StructOptions = {}): StructDescriptor => {
312
382
  const result: StructDescriptor = { kind: "struct", ownership };
@@ -327,6 +397,8 @@ const structT = (ownership: Ownership = "borrowed", options: StructOptions = {})
327
397
  result.isInline = true;
328
398
  }
329
399
 
400
+ applyStructLifecycle(result, options);
401
+
330
402
  return result;
331
403
  };
332
404
 
@@ -339,6 +411,16 @@ const recordFundamentalLifecycle = (typeName: string, lifecycle: FundamentalLife
339
411
  const fundamentalLifecycleFor = (typeName: string): FundamentalLifecycle | undefined =>
340
412
  fundamentalLifecycles.get(typeName);
341
413
 
414
+ const applyFundamentalClasses = (result: FundamentalDescriptor, options: FundamentalOptions): void => {
415
+ if (options.wrapperClass !== undefined) {
416
+ result.wrapperClass = options.wrapperClass;
417
+ }
418
+
419
+ if (options.fallbackClass !== undefined) {
420
+ result.fallbackClass = options.fallbackClass;
421
+ }
422
+ };
423
+
342
424
  /** Builds a descriptor for a fundamental type whose lifetime is managed by named ref and unref functions. */
343
425
  const fundamentalT = (
344
426
  sharedLibrary: string,
@@ -354,9 +436,7 @@ const fundamentalT = (
354
436
  recordFundamentalLifecycle(options.typeName, { sharedLibrary, refFnName, unrefFnName });
355
437
  }
356
438
 
357
- if (options.wrapperClass !== undefined) {
358
- result.wrapperClass = options.wrapperClass;
359
- }
439
+ applyFundamentalClasses(result, options);
360
440
 
361
441
  if (options.isCallerAllocated) {
362
442
  result.isCallerAllocated = true;
@@ -410,6 +490,10 @@ const arrayT = (
410
490
  result.isCallerAllocated = true;
411
491
  }
412
492
 
493
+ if (options.isZeroTerminated === true) {
494
+ result.isZeroTerminated = true;
495
+ }
496
+
413
497
  return result;
414
498
  };
415
499
 
@@ -453,10 +537,7 @@ const sizedArrayT = (
453
537
  options: ArrayOptions = {},
454
538
  ): ArrayDescriptor => arrayT(itemDescriptor, "sized", ownership, { ...options, sizeParamIndex });
455
539
 
456
- /**
457
- * Builds a descriptor for an out pointer into the buffer another argument supplied, decoded as the
458
- * elements from where it points to the end of that buffer.
459
- */
540
+ /** Builds an out-array cursor into another argument's buffer. */
460
541
  const cursorArrayT = (
461
542
  itemDescriptor: Descriptor,
462
543
  bounds: CursorBounds,
@@ -559,6 +640,7 @@ export {
559
640
  cursorArrayT,
560
641
  callbackT,
561
642
  type BoxedDescriptor,
643
+ type ObjectDescriptor,
562
644
  type StructDescriptor,
563
645
  type FundamentalDescriptor,
564
646
  type ArrayDescriptor,
@@ -0,0 +1,24 @@
1
+ type ElementPropertyEntry = [name: string, flags: number, defaultValue?: unknown];
2
+
3
+ const registeredElementSignals: Record<string, Record<string, string>> = {};
4
+ const registeredElementProperties: Record<string, Record<string, ElementPropertyEntry>> = {};
5
+ const state = { version: 0 };
6
+
7
+ const registerElementMetadata = (
8
+ name: string,
9
+ _parent: string | undefined,
10
+ ownSignals: Record<string, string>,
11
+ ownProperties: Record<string, ElementPropertyEntry>,
12
+ ): string => {
13
+ registeredElementSignals[name] = ownSignals;
14
+ registeredElementProperties[name] = ownProperties;
15
+ state.version += 1;
16
+
17
+ return name;
18
+ };
19
+
20
+ const elementMetadataVersion = (): number => state.version;
21
+
22
+ /** @internal */
23
+ export { type ElementPropertyEntry, registerElementMetadata };
24
+ export { elementMetadataVersion, registeredElementProperties, registeredElementSignals };
package/src/fn.ts CHANGED
@@ -158,12 +158,33 @@ const isPassThroughPlan = (plan: ArgSpec, index: number): boolean =>
158
158
  !plan.carriesGtype &&
159
159
  plan.arg.type.kind !== "callback";
160
160
 
161
- const requiredInputIndices = (plans: ArgSpec[]): number[] =>
162
- plans.filter((plan) => plan.requiresInput && plan.arg.isRequired === true).map((plan) => plan.inputIndex);
161
+ const KINDS_PASSED_AS_POINTER: ReadonlySet<Descriptor["kind"]> = new Set<Descriptor["kind"]>([
162
+ "array",
163
+ "boxed",
164
+ "buffer",
165
+ "callback",
166
+ "fundamental",
167
+ "hashtable",
168
+ "object",
169
+ "string",
170
+ "struct",
171
+ ]);
172
+
173
+ type RequiredInput = {
174
+ index: number;
175
+ rejectsNull: boolean;
176
+ };
177
+
178
+ const requiredInputs = (plans: ArgSpec[]): RequiredInput[] =>
179
+ plans
180
+ .filter((plan) => plan.requiresInput && plan.arg.isRequired === true)
181
+ .map((plan) => ({ index: plan.inputIndex, rejectsNull: KINDS_PASSED_AS_POINTER.has(plan.arg.type.kind) }));
163
182
 
164
- const assertRequiredInputs = (requiredIndices: number[], inputs: unknown[]): void => {
165
- for (const index of requiredIndices) {
166
- if (inputs[index] === undefined) {
183
+ const assertRequiredInputs = (required: RequiredInput[], inputs: unknown[]): void => {
184
+ for (const { index, rejectsNull } of required) {
185
+ const input = inputs[index];
186
+
187
+ if (input === undefined || (rejectsNull && input === null)) {
167
188
  throw new TypeError(`Missing required argument at position ${String(index + 1)}`);
168
189
  }
169
190
  }
@@ -193,10 +214,10 @@ const directCallable = (
193
214
  plans: ArgSpec[],
194
215
  ): ((...inputs: unknown[]) => unknown) => {
195
216
  const argCount = plans.length;
196
- const requiredIndices = requiredInputIndices(plans);
217
+ const required = requiredInputs(plans);
197
218
 
198
219
  const marshal = (inputs: unknown[]): unknown => {
199
- assertRequiredInputs(requiredIndices, inputs);
220
+ assertRequiredInputs(required, inputs);
200
221
 
201
222
  return readReturn(call(descriptor, resizeInputs(inputs, argCount)));
202
223
  };
@@ -226,14 +247,14 @@ function fromNativeCallable(
226
247
  return directCallable(descriptor, readReturn, hasPrimary, plans);
227
248
  }
228
249
 
229
- const requiredIndices = requiredInputIndices(plans);
250
+ const required = requiredInputs(plans);
230
251
 
231
252
  const shape = (inputs: unknown[], nativeValues: unknown[], nativeResult: unknown): unknown =>
232
253
  packTupleResult(readOutParams(outPlans, inputs, nativeValues), readReturn(nativeResult), hasPrimary);
233
254
 
234
255
  if (canThrow) {
235
256
  return (...inputs) => {
236
- assertRequiredInputs(requiredIndices, inputs);
257
+ assertRequiredInputs(required, inputs);
237
258
  const nativeValues = buildNativeValues(plans, inputs, getRefSeeds?.());
238
259
  const errorRef: Ref = { value: null };
239
260
  nativeValues.push(errorRef);
@@ -245,26 +266,54 @@ function fromNativeCallable(
245
266
  }
246
267
 
247
268
  return (...inputs) => {
248
- assertRequiredInputs(requiredIndices, inputs);
269
+ assertRequiredInputs(required, inputs);
249
270
  const nativeValues = buildNativeValues(plans, inputs, getRefSeeds?.());
250
271
 
251
272
  return shape(inputs, nativeValues, call(descriptor, nativeValues));
252
273
  };
253
274
  }
254
275
 
276
+ const bindNativeCallable = (
277
+ sharedLibrary: string,
278
+ symbol: string,
279
+ spec: FnSpec,
280
+ ): ((...inputs: unknown[]) => unknown) => {
281
+ const nativeArgTypes = buildNativeArgTypes(spec.args, spec.canThrow ?? false);
282
+ const descriptor = nativeBind(sharedLibrary, symbol, nativeArgTypes, spec.returns, spec.fixedArgCount);
283
+
284
+ return fromNativeCallable(descriptor, spec);
285
+ };
286
+
255
287
  /**
256
288
  * Binds a symbol in a shared library to a callable that marshals its inputs and packs output
257
289
  * arguments into the result. When the spec sets `canThrow`, the reported `GError` is thrown.
258
290
  *
291
+ * A plain spec is bound when `fn` is called, so a malformed descriptor throws at binding time.
292
+ * Passing a function returning the spec instead defers building the descriptors and the binding
293
+ * to the first call, so describing such a binding costs nothing until something calls it and a
294
+ * module full of them loads without touching the shared library.
295
+ *
259
296
  * @param sharedLibrary Shared library the symbol is looked up in.
260
297
  * @param symbol Name of the C symbol to bind.
261
- * @param spec Argument and return descriptors the call is marshalled through.
298
+ * @param spec Argument and return descriptors the call is marshalled through, or a function
299
+ * returning them, which defers building the descriptors and the binding to the first call.
262
300
  */
263
- function fn(sharedLibrary: string, symbol: string, spec: FnSpec): (...inputs: unknown[]) => unknown {
264
- const nativeArgTypes = buildNativeArgTypes(spec.args, spec.canThrow ?? false);
265
- const descriptor = nativeBind(sharedLibrary, symbol, nativeArgTypes, spec.returns, spec.fixedArgCount);
301
+ function fn(
302
+ sharedLibrary: string,
303
+ symbol: string,
304
+ spec: FnSpec | (() => FnSpec),
305
+ ): (...inputs: unknown[]) => unknown {
306
+ if (typeof spec !== "function") {
307
+ return bindNativeCallable(sharedLibrary, symbol, spec);
308
+ }
266
309
 
267
- return fromNativeCallable(descriptor, spec);
310
+ let bound: ((...inputs: unknown[]) => unknown) | undefined;
311
+
312
+ return (...inputs) => {
313
+ bound ??= bindNativeCallable(sharedLibrary, symbol, spec());
314
+
315
+ return bound(...inputs);
316
+ };
268
317
  }
269
318
 
270
319
  export { buildNativeArgTypes, fn, fromNativeCallable };
@@ -28,15 +28,7 @@ const addLengthSource = (sources: LengthSources, index: number | undefined, sour
28
28
  return;
29
29
  }
30
30
 
31
- const existing = sources.get(index);
32
-
33
- if (existing === undefined) {
34
- sources.set(index, [source]);
35
-
36
- return;
37
- }
38
-
39
- existing.push(source);
31
+ sources.getOrInsertComputed(index, () => []).push(source);
40
32
  };
41
33
 
42
34
  const addOutArgLengthSources = (sources: LengthSources, spec: FoldedLengthSpec): void => {
@@ -52,6 +44,27 @@ const addOutArgLengthSources = (sources: LengthSources, spec: FoldedLengthSpec):
52
44
  }
53
45
  };
54
46
 
47
+ const foldedLengthArgIndices = (spec: FoldedLengthSpec): ReadonlySet<number> => {
48
+ const indices: Set<number> = new Set();
49
+
50
+ const add = (declaredIndex: number | undefined): void => {
51
+ const argIndex = effectiveArgIndex(declaredIndex, spec.userDataIndex);
52
+
53
+ if (argIndex !== undefined) {
54
+ indices.add(argIndex);
55
+ }
56
+ };
57
+
58
+ add(sizedArrayLengthIndex(spec.returnDescriptor));
59
+
60
+ for (const descriptor of spec.argDescriptors) {
61
+ add(sizedArrayLengthIndex(descriptor));
62
+ add(outArgLengthIndex(descriptor));
63
+ }
64
+
65
+ return indices;
66
+ };
67
+
55
68
  const foldedLengthSources = (spec: FoldedLengthSpec): LengthSources => {
56
69
  const sources: LengthSources = new Map();
57
70
  const returnLengthIndex = effectiveArgIndex(sizedArrayLengthIndex(spec.returnDescriptor), spec.userDataIndex);
@@ -61,4 +74,4 @@ const foldedLengthSources = (spec: FoldedLengthSpec): LengthSources => {
61
74
  return sources;
62
75
  };
63
76
 
64
- export { foldedLengthSources, type LengthSource, type LengthSources };
77
+ export { foldedLengthArgIndices, foldedLengthSources, type LengthSource, type LengthSources };
package/src/index.ts CHANGED
@@ -1,22 +1,14 @@
1
1
  import "./exit-hook.js";
2
2
 
3
- /** @public */
4
3
  export { type ApplicationClass, type CommandLineApplication, createApplication } from "./application-class.js";
5
- /** @public */
4
+ export { CallbackMarshalError } from "./callback.js";
6
5
  export { type ClosureCallback, ClosureMarshalError, toClosure, tryToClosure } from "./closure.js";
7
- /** @public */
8
6
  export { createErrorDomain, type ErrorDomain } from "./error.js";
9
- /** @public */
10
7
  export { type Field, type StridedField } from "./field.js";
11
- /** @public */
12
8
  export { onExit, quit, quitApplication, runApplication, type RunApplicationResult } from "./lifecycle.js";
13
- /** @public */
14
9
  export { offSignal, onceSignal, onSignal } from "./listeners.js";
15
- /** @public */
16
10
  export { installMixins, type Mixin } from "./mixin.js";
17
- /** @public */
18
- export { fromNative, toNative } from "./native-value.js";
19
- /** @public */
11
+ export { fromNative, toHashTableEntries, toNative } from "./native-value.js";
20
12
  export {
21
13
  type ConstructBinding,
22
14
  type ConstructBindings,
@@ -25,27 +17,22 @@ export {
25
17
  registerConstructProperties,
26
18
  setObjectProperty,
27
19
  } from "./object.js";
28
- /** @public */
29
20
  export { getParamSpecFlags, getParamSpecOwnerType, getParamSpecValueType } from "./param-spec.js";
30
- /** @public */
31
21
  export { promisify, trimFinish } from "./promisify.js";
32
- /** @public */
33
22
  export {
34
23
  coerceObjectProperty,
35
24
  getDeclaredPropertyName,
36
25
  isReadableProperty,
37
26
  newParamSpecOverride,
38
27
  } from "./properties.js";
39
- /** @public */
40
28
  export { matchAllRegex, matchRegex } from "./regex.js";
41
- /** @public */
42
29
  export { type Interface, registerClass, type SignalGType, type SignalSpec } from "./register-class.js";
43
- /** @public */
44
30
  export {
45
31
  getClassType,
46
32
  getHandle,
47
33
  getInstanceType,
48
34
  getWrapperClass,
35
+ installInterfaces,
49
36
  peekTypeClass,
50
37
  registerClassStruct,
51
38
  registerInterface,
@@ -54,9 +41,9 @@ export {
54
41
  type StaticBase,
55
42
  setHandle,
56
43
  wrapHandle,
44
+ type WrapperClass,
57
45
  type WrapperClassResolver,
58
46
  } from "./registry.js";
59
- /** @public */
60
47
  export {
61
48
  connectSignal,
62
49
  disconnectSignal,
@@ -65,9 +52,7 @@ export {
65
52
  type SignalHandler,
66
53
  signalForHandlerName,
67
54
  } from "./signal.js";
68
- /** @public */
69
55
  export { t } from "./t.js";
70
- /** @public */
71
56
  export {
72
57
  resolveType,
73
58
  TYPE_BOOLEAN,
@@ -95,6 +80,7 @@ export {
95
80
  TYPE_UNICHAR,
96
81
  TYPE_VARIANT,
97
82
  type TypedClass,
83
+ retainWrapperClasses,
98
84
  typeFromName,
99
85
  typeInterfaces,
100
86
  typeIsA,
@@ -102,7 +88,6 @@ export {
102
88
  typeParent,
103
89
  valueIsA,
104
90
  } from "./type.js";
105
- /** @public */
106
91
  export {
107
92
  fromValue,
108
93
  getBoxedValue,
@@ -112,7 +97,6 @@ export {
112
97
  tryToValueHandle,
113
98
  ValueMarshalError,
114
99
  } from "./value.js";
115
- /** @public */
116
100
  export {
117
101
  type ByteArray,
118
102
  type FromVariantOptions,
@@ -123,9 +107,6 @@ export {
123
107
  type VariantInput,
124
108
  type VariantValue,
125
109
  } from "./variant.js";
126
- /** @public */
127
110
  export { callParent, callVfunc } from "./vfunc-call.js";
128
- /** @public */
129
111
  export { alloc, type ExternalObject, type Handle, read, write } from "@gtkx/native";
130
- /** @public */
131
112
  export { type AnyClass } from "@gtkx/utils";
package/src/internal.ts CHANGED
@@ -1,6 +1,13 @@
1
- export { checkError, createErrorDomain } from "./error.js";
1
+ /** @internal */
2
+ export { registerElementMetadata } from "./element-metadata.js";
3
+ export {
4
+ elementMetadataVersion,
5
+ registeredElementProperties,
6
+ registeredElementSignals,
7
+ } from "./element-metadata.js";
8
+ export { createErrorDomain } from "./error.js";
2
9
  export { type ApplicationInstance, getApplicationInstance } from "./lifecycle.js";
3
- export { registerClassType, resolveWrapperClass, wrapHandle } from "./registry.js";
10
+ export { getExactWrapperClass, resolveWrapperClass } from "./registry.js";
4
11
  export { hasSignalListener } from "./signal.js";
5
12
  export { resolveType } from "./type.js";
6
13
  export {