@gtkx/codegen 1.2.1 → 1.3.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.
Files changed (213) hide show
  1. package/README.md +2 -1
  2. package/dist/analysis/descriptor-render.d.ts +7 -1
  3. package/dist/analysis/descriptor-render.d.ts.map +1 -1
  4. package/dist/analysis/descriptor-render.js +36 -15
  5. package/dist/analysis/descriptor-render.js.map +1 -1
  6. package/dist/analysis/descriptor.d.ts +4 -1
  7. package/dist/analysis/descriptor.d.ts.map +1 -1
  8. package/dist/analysis/descriptor.js +11 -2
  9. package/dist/analysis/descriptor.js.map +1 -1
  10. package/dist/analysis/param-capability.d.ts +5 -2
  11. package/dist/analysis/param-capability.d.ts.map +1 -1
  12. package/dist/analysis/param-capability.js +36 -4
  13. package/dist/analysis/param-capability.js.map +1 -1
  14. package/dist/analysis/param-structure.d.ts +1 -1
  15. package/dist/analysis/param-structure.d.ts.map +1 -1
  16. package/dist/analysis/param-structure.js.map +1 -1
  17. package/dist/analysis/ts-type.d.ts +1 -1
  18. package/dist/analysis/ts-type.d.ts.map +1 -1
  19. package/dist/analysis/ts-type.js +28 -10
  20. package/dist/analysis/ts-type.js.map +1 -1
  21. package/dist/compile.js +1 -1
  22. package/dist/compile.js.map +1 -1
  23. package/dist/docs/api-reference.d.ts +6 -0
  24. package/dist/docs/api-reference.d.ts.map +1 -1
  25. package/dist/docs/api-reference.js +11 -2
  26. package/dist/docs/api-reference.js.map +1 -1
  27. package/dist/docs/pipeline.d.ts +3 -0
  28. package/dist/docs/pipeline.d.ts.map +1 -1
  29. package/dist/docs/pipeline.js +10 -1
  30. package/dist/docs/pipeline.js.map +1 -1
  31. package/dist/fingerprint.d.ts +5 -0
  32. package/dist/fingerprint.d.ts.map +1 -1
  33. package/dist/fingerprint.js +107 -15
  34. package/dist/fingerprint.js.map +1 -1
  35. package/dist/gi.d.ts +3 -0
  36. package/dist/gi.d.ts.map +1 -1
  37. package/dist/gi.js +29 -7
  38. package/dist/gi.js.map +1 -1
  39. package/dist/gir/external-namespaces.d.ts +8 -0
  40. package/dist/gir/external-namespaces.d.ts.map +1 -0
  41. package/dist/gir/external-namespaces.js +4 -0
  42. package/dist/gir/external-namespaces.js.map +1 -0
  43. package/dist/gir/finish-overrides.d.ts +3 -0
  44. package/dist/gir/finish-overrides.d.ts.map +1 -0
  45. package/dist/gir/finish-overrides.js +5 -0
  46. package/dist/gir/finish-overrides.js.map +1 -0
  47. package/dist/gir/function.d.ts +5 -2
  48. package/dist/gir/function.d.ts.map +1 -1
  49. package/dist/gir/function.js +34 -5
  50. package/dist/gir/function.js.map +1 -1
  51. package/dist/gir/hidden-symbols.d.ts +3 -0
  52. package/dist/gir/hidden-symbols.d.ts.map +1 -0
  53. package/dist/gir/hidden-symbols.js +39 -0
  54. package/dist/gir/hidden-symbols.js.map +1 -0
  55. package/dist/gir/library.d.ts +22 -2
  56. package/dist/gir/library.d.ts.map +1 -1
  57. package/dist/gir/library.js +21 -3
  58. package/dist/gir/library.js.map +1 -1
  59. package/dist/gir/ucs4-overrides.d.ts +3 -0
  60. package/dist/gir/ucs4-overrides.d.ts.map +1 -0
  61. package/dist/gir/ucs4-overrides.js +7 -0
  62. package/dist/gir/ucs4-overrides.js.map +1 -0
  63. package/dist/jsx.d.ts.map +1 -1
  64. package/dist/jsx.js +27 -8
  65. package/dist/jsx.js.map +1 -1
  66. package/dist/runner.d.ts +8 -0
  67. package/dist/runner.d.ts.map +1 -1
  68. package/dist/runner.js +9 -3
  69. package/dist/runner.js.map +1 -1
  70. package/dist/store/external-package-links.d.ts +6 -0
  71. package/dist/store/external-package-links.d.ts.map +1 -0
  72. package/dist/store/external-package-links.js +158 -0
  73. package/dist/store/external-package-links.js.map +1 -0
  74. package/dist/store/gi/callable-doc.d.ts.map +1 -1
  75. package/dist/store/gi/callable-doc.js +4 -4
  76. package/dist/store/gi/callable-doc.js.map +1 -1
  77. package/dist/store/gi/callables.d.ts +1 -1
  78. package/dist/store/gi/callables.d.ts.map +1 -1
  79. package/dist/store/gi/callables.js +4 -4
  80. package/dist/store/gi/callables.js.map +1 -1
  81. package/dist/store/gi/class.d.ts.map +1 -1
  82. package/dist/store/gi/class.js +25 -15
  83. package/dist/store/gi/class.js.map +1 -1
  84. package/dist/store/gi/constructor-props.js +1 -1
  85. package/dist/store/gi/constructor-props.js.map +1 -1
  86. package/dist/store/gi/function.d.ts.map +1 -1
  87. package/dist/store/gi/function.js +2 -1
  88. package/dist/store/gi/function.js.map +1 -1
  89. package/dist/store/gi/gtype-binding.d.ts +2 -2
  90. package/dist/store/gi/gtype-binding.d.ts.map +1 -1
  91. package/dist/store/gi/gtype-binding.js +6 -13
  92. package/dist/store/gi/gtype-binding.js.map +1 -1
  93. package/dist/store/gi/interface.js +2 -2
  94. package/dist/store/gi/interface.js.map +1 -1
  95. package/dist/store/gi/method.d.ts +7 -2
  96. package/dist/store/gi/method.d.ts.map +1 -1
  97. package/dist/store/gi/method.js +126 -30
  98. package/dist/store/gi/method.js.map +1 -1
  99. package/dist/store/gi/param-marshal.d.ts +8 -1
  100. package/dist/store/gi/param-marshal.d.ts.map +1 -1
  101. package/dist/store/gi/param-marshal.js +52 -4
  102. package/dist/store/gi/param-marshal.js.map +1 -1
  103. package/dist/store/gi/record-constructor.d.ts.map +1 -1
  104. package/dist/store/gi/record-constructor.js +2 -3
  105. package/dist/store/gi/record-constructor.js.map +1 -1
  106. package/dist/store/gi/record-field-accessor.d.ts +2 -1
  107. package/dist/store/gi/record-field-accessor.d.ts.map +1 -1
  108. package/dist/store/gi/record-field-accessor.js +142 -16
  109. package/dist/store/gi/record-field-accessor.js.map +1 -1
  110. package/dist/store/gi/record-layout.d.ts +5 -2
  111. package/dist/store/gi/record-layout.d.ts.map +1 -1
  112. package/dist/store/gi/record-layout.js +2 -1
  113. package/dist/store/gi/record-layout.js.map +1 -1
  114. package/dist/store/gi/record.d.ts.map +1 -1
  115. package/dist/store/gi/record.js +9 -5
  116. package/dist/store/gi/record.js.map +1 -1
  117. package/dist/store/gi/runtime-override.js +1 -1
  118. package/dist/store/gi/runtime-override.js.map +1 -1
  119. package/dist/store/gi/signal.js +2 -2
  120. package/dist/store/gi/signal.js.map +1 -1
  121. package/dist/store/gi/vtable.d.ts.map +1 -1
  122. package/dist/store/gi/vtable.js +82 -12
  123. package/dist/store/gi/vtable.js.map +1 -1
  124. package/dist/store/gi-store.d.ts +7 -2
  125. package/dist/store/gi-store.d.ts.map +1 -1
  126. package/dist/store/gi-store.js +19 -4
  127. package/dist/store/gi-store.js.map +1 -1
  128. package/dist/store/jsx/jsx.d.ts.map +1 -1
  129. package/dist/store/jsx/jsx.js +3 -1
  130. package/dist/store/jsx/jsx.js.map +1 -1
  131. package/dist/store/jsx-store.d.ts +8 -1
  132. package/dist/store/jsx-store.d.ts.map +1 -1
  133. package/dist/store/jsx-store.js +9 -2
  134. package/dist/store/jsx-store.js.map +1 -1
  135. package/dist/store/resolve-store.d.ts +2 -1
  136. package/dist/store/resolve-store.d.ts.map +1 -1
  137. package/dist/store/resolve-store.js +2 -1
  138. package/dist/store/resolve-store.js.map +1 -1
  139. package/dist/store/store-fs.d.ts +2 -1
  140. package/dist/store/store-fs.d.ts.map +1 -1
  141. package/dist/store/store-fs.js +8 -3
  142. package/dist/store/store-fs.js.map +1 -1
  143. package/dist/writer/context.d.ts.map +1 -1
  144. package/dist/writer/context.js +8 -0
  145. package/dist/writer/context.js.map +1 -1
  146. package/dist/writer/gtk-doc.d.ts.map +1 -1
  147. package/dist/writer/gtk-doc.js +27 -1
  148. package/dist/writer/gtk-doc.js.map +1 -1
  149. package/overrides/gdk/rgba.ts.ejs +15 -1
  150. package/overrides/glib/index.ts.ejs +3 -0
  151. package/overrides/glib/regex.ts.ejs +66 -0
  152. package/overrides/gobject/index.ts.ejs +3 -0
  153. package/overrides/gobject/object-class.ts.ejs +27 -0
  154. package/overrides/gobject/param-spec-getters.ts.ejs +57 -0
  155. package/overrides/gobject/param-spec.ts.ejs +21 -0
  156. package/overrides/gobject/value.ts.ejs +6 -1
  157. package/overrides/graphene/structs.ts.ejs +38 -3
  158. package/overrides/gtk/index.ts.ejs +2 -0
  159. package/overrides/gtk/widget-class.ts.ejs +28 -0
  160. package/package.json +6 -5
  161. package/src/analysis/descriptor-render.ts +59 -16
  162. package/src/analysis/descriptor.ts +15 -3
  163. package/src/analysis/param-capability.ts +68 -5
  164. package/src/analysis/param-structure.ts +1 -1
  165. package/src/analysis/ts-type.ts +45 -11
  166. package/src/compile.ts +1 -1
  167. package/src/docs/api-reference.ts +20 -2
  168. package/src/docs/pipeline.ts +15 -1
  169. package/src/fingerprint.ts +144 -16
  170. package/src/gi.ts +44 -9
  171. package/src/gir/external-namespaces.ts +8 -0
  172. package/src/gir/finish-overrides.ts +5 -0
  173. package/src/gir/function.ts +52 -14
  174. package/src/gir/hidden-symbols.ts +39 -0
  175. package/src/gir/library.ts +36 -3
  176. package/src/gir/ucs4-overrides.ts +7 -0
  177. package/src/jsx.ts +33 -8
  178. package/src/runner.ts +17 -3
  179. package/src/store/external-package-links.ts +226 -0
  180. package/src/store/gi/callable-doc.ts +8 -4
  181. package/src/store/gi/callables.ts +5 -5
  182. package/src/store/gi/class.ts +42 -32
  183. package/src/store/gi/constructor-props.ts +1 -1
  184. package/src/store/gi/function.ts +2 -1
  185. package/src/store/gi/gtype-binding.ts +7 -15
  186. package/src/store/gi/interface.ts +2 -2
  187. package/src/store/gi/method.ts +189 -36
  188. package/src/store/gi/param-marshal.ts +67 -4
  189. package/src/store/gi/record-constructor.ts +8 -3
  190. package/src/store/gi/record-field-accessor.ts +209 -16
  191. package/src/store/gi/record-layout.ts +11 -1
  192. package/src/store/gi/record.ts +17 -13
  193. package/src/store/gi/runtime-override.ts +1 -1
  194. package/src/store/gi/signal.ts +2 -2
  195. package/src/store/gi/vtable.ts +118 -11
  196. package/src/store/gi-store.ts +33 -3
  197. package/src/store/jsx/jsx.ts +3 -1
  198. package/src/store/jsx-store.ts +23 -7
  199. package/src/store/resolve-store.ts +2 -1
  200. package/src/store/store-fs.ts +10 -2
  201. package/src/writer/context.ts +11 -0
  202. package/src/writer/gtk-doc.ts +38 -1
  203. package/overrides/cairo/context.ts.ejs +0 -1269
  204. package/overrides/cairo/font-face.ts.ejs +0 -151
  205. package/overrides/cairo/font-options.ts.ejs +0 -199
  206. package/overrides/cairo/image-surface.ts.ejs +0 -132
  207. package/overrides/cairo/index.ts.ejs +0 -22
  208. package/overrides/cairo/matrix.ts.ejs +0 -171
  209. package/overrides/cairo/pattern.ts.ejs +0 -476
  210. package/overrides/cairo/recording-surface.ts.ejs +0 -102
  211. package/overrides/cairo/region.ts.ejs +0 -237
  212. package/overrides/cairo/scaled-font.ts.ejs +0 -330
  213. package/overrides/cairo/surface.ts.ejs +0 -296
@@ -1,6 +1,7 @@
1
1
  import { toCamelIdentifier } from "@gtkx/utils";
2
2
  import type { GirAnnotations } from "../../gir/annotations.js";
3
3
  import type { GirField } from "../../gir/field.js";
4
+ import type { PrimitiveCategory } from "../../gir/primitives.js";
4
5
  import type { GirRecord } from "../../gir/record.js";
5
6
  import type { FieldSlot } from "../../gir/size.js";
6
7
  import type { TypeId } from "../../gir/type-id.js";
@@ -9,10 +10,16 @@ import type { ModuleContext } from "../../writer/context.js";
9
10
  import { isInlineCallbackRef, renderDescriptor } from "../../analysis/descriptor-render.js";
10
11
  import { tStruct } from "../../analysis/descriptor.js";
11
12
  import { renderTsType } from "../../analysis/ts-type.js";
12
- import { isUnboundedArray } from "../../analysis/type-shape.js";
13
+ import { isByteSequence, isUnboundedArray } from "../../analysis/type-shape.js";
13
14
  import { indent, renderBlock } from "../../writer/emit.js";
14
15
  import { getDoc } from "./doc-spec.js";
15
- import { bitMask, computeRecordFieldSlots, mergeBitfield, type RecordFieldSlot } from "./record-layout.js";
16
+ import {
17
+ bitMask,
18
+ computeRecordFieldSlots,
19
+ inlineArrayStride,
20
+ mergeBitfield,
21
+ type RecordFieldSlot,
22
+ } from "./record-layout.js";
16
23
  import { wrapReturnValue } from "./return-wrap.js";
17
24
  import { isValueMarshalable } from "./value-marshalable.js";
18
25
 
@@ -99,6 +106,22 @@ type AccessorOptions = {
99
106
  fieldType: TypeId;
100
107
  };
101
108
 
109
+ type InlineArrayResolution = {
110
+ element: TypeId;
111
+ count: number;
112
+ stride: number;
113
+ isTypedByteArray: boolean;
114
+ };
115
+
116
+ type InlineArrayAccessorOptions = {
117
+ context: ModuleContext;
118
+ jsName: string;
119
+ tsType: string;
120
+ descriptor: string;
121
+ offset: number;
122
+ resolution: InlineArrayResolution;
123
+ };
124
+
102
125
  const isMarshalableField = (context: ModuleContext, field: GirField): boolean => {
103
126
  if (field.cType?.endsWith("*") === true) {
104
127
  return true;
@@ -187,6 +210,9 @@ const isStorableFieldType = (context: ModuleContext, ref: TypeId): boolean => {
187
210
  return kind !== "carray" && kind !== "list" && kind !== "hashtable";
188
211
  };
189
212
 
213
+ const hasArrayAccessor = (context: ModuleContext, target: StructArrayTarget): boolean =>
214
+ resolveInlineArray(context, target) !== undefined || resolveStructArray(context, target) !== undefined;
215
+
190
216
  const admitAccessibleField = (
191
217
  context: ModuleContext,
192
218
  slot: RecordFieldSlot,
@@ -201,9 +227,9 @@ const admitAccessibleField = (
201
227
 
202
228
  const { field, jsName } = admitted;
203
229
  const target: StructArrayTarget = { field, jsName, slot: slot.slot, siblingFields };
204
- const hasStructArray = resolveStructArray(context, target) !== undefined;
205
- const hasAccessor = hasStructArray || isAccessibleFieldType(context, field.type);
206
- const isSettable = field.writable && (hasStructArray || isStorableFieldType(context, field.type));
230
+ const hasArray = hasArrayAccessor(context, target);
231
+ const hasAccessor = hasArray || isAccessibleFieldType(context, field.type);
232
+ const isSettable = field.writable && (hasArray || isStorableFieldType(context, field.type));
207
233
 
208
234
  return hasAccessor ? { ...admitted, isSettable } : undefined;
209
235
  };
@@ -222,13 +248,21 @@ const resolveRecordFieldEntry = (
222
248
 
223
249
  return {
224
250
  jsName: admitted.jsName,
225
- tsType: renderTsType(context, admitted.field.type, false),
251
+ tsType: fieldTsType(context, admitted.field.type),
226
252
  isWritable: admitted.isSettable,
227
253
  doc: admitted.field.doc,
228
254
  annotations: admitted.field.annotations,
229
255
  };
230
256
  };
231
257
 
258
+ const fieldTsType = (context: ModuleContext, ref: TypeId | undefined, isNullable = false): string => {
259
+ if (ref !== undefined && context.library.typeFor(ref)?.kind === "callback") {
260
+ return isNullable ? "bigint | null" : "bigint";
261
+ }
262
+
263
+ return renderTsType(context, ref, isNullable);
264
+ };
265
+
232
266
  const renderRecordFieldAccessor = (
233
267
  context: ModuleContext,
234
268
  slot: RecordFieldSlot,
@@ -243,17 +277,18 @@ const renderRecordFieldAccessor = (
243
277
 
244
278
  const { field, jsName } = admitted;
245
279
  const doc = getDoc(field);
246
- const structArray = renderStructArrayAccessor(context, { field, jsName, slot: slot.slot, siblingFields });
280
+ const target: StructArrayTarget = { field, jsName, slot: slot.slot, siblingFields };
281
+ const arrayAccessor = renderInlineArrayAccessor(context, target) ?? renderStructArrayAccessor(context, target);
247
282
 
248
- if (structArray !== undefined) {
249
- return `${doc}${structArray}`;
283
+ if (arrayAccessor !== undefined) {
284
+ return `${doc}${arrayAccessor}`;
250
285
  }
251
286
 
252
287
  const descriptor = context.hoistDescriptor(
253
288
  renderDescriptor(context, field.type, "none", { isInline: isInlineField(context, field) }),
254
289
  );
255
290
 
256
- const tsType = renderTsType(context, field.type, false);
291
+ const tsType = fieldTsType(context, field.type);
257
292
 
258
293
  const accessorOptions: AccessorOptions = {
259
294
  context,
@@ -295,7 +330,7 @@ const isAccessorEligibleType = (context: ModuleContext, ref: TypeId): boolean =>
295
330
  return context.library.nameFor(ref) !== undefined;
296
331
  }
297
332
  case "carray": {
298
- return type.fixedSize !== undefined;
333
+ return type.fixedSize !== undefined && type.arrayCType?.endsWith("*") === true;
299
334
  }
300
335
  case "list":
301
336
  case "hashtable":
@@ -338,15 +373,96 @@ const resolveInlineStructFields = (
338
373
  return type.value.fields;
339
374
  };
340
375
 
376
+ const isInlineScalarCategory = (category: PrimitiveCategory, isBehindPointer: boolean): boolean => {
377
+ if (category === "pointer") {
378
+ return true;
379
+ }
380
+
381
+ return !isBehindPointer && category !== "void" && category !== "unichar" && category !== "string";
382
+ };
383
+
384
+ const isInlineArrayElement = (context: ModuleContext, ref: TypeId | undefined, cType: string | undefined): boolean => {
385
+ const type = ref === undefined ? undefined : context.library.typeFor(ref);
386
+
387
+ if (type === undefined) {
388
+ return false;
389
+ }
390
+
391
+ const isBehindPointer = cType?.endsWith("*") === true;
392
+
393
+ switch (type.kind) {
394
+ case "primitive": {
395
+ return isInlineScalarCategory(type.category, isBehindPointer);
396
+ }
397
+ case "enum": {
398
+ return !isBehindPointer;
399
+ }
400
+ case "record": {
401
+ return (
402
+ resolveInlineStructFields(context, ref, cType) !== undefined &&
403
+ isValueMarshalable(context, type.namespace.name, type.value)
404
+ );
405
+ }
406
+ case "alias": {
407
+ return isInlineArrayElement(context, type.value.target, cType ?? type.value.targetCType);
408
+ }
409
+ case "callback":
410
+ case "carray":
411
+ case "class":
412
+ case "hashtable":
413
+ case "interface":
414
+ case "list":
415
+ case "varargs": {
416
+ return false;
417
+ }
418
+ }
419
+ };
420
+
421
+ const inlineArrayType = (
422
+ context: ModuleContext,
423
+ target: StructArrayTarget,
424
+ ): Extract<GirType, { kind: "carray" }> | undefined => {
425
+ const { field, slot } = target;
426
+
427
+ if (field.type === undefined || slot.bitWidth !== undefined) {
428
+ return undefined;
429
+ }
430
+
431
+ const type = context.library.typeFor(field.type);
432
+
433
+ if (type?.kind !== "carray" || type.fixedSize === undefined) {
434
+ return undefined;
435
+ }
436
+
437
+ return type.arrayCType?.endsWith("*") === true ? undefined : type;
438
+ };
439
+
440
+ const resolveInlineArray = (context: ModuleContext, target: StructArrayTarget): InlineArrayResolution | undefined => {
441
+ const type = inlineArrayType(context, target);
442
+
443
+ if (type?.fixedSize === undefined || !isInlineArrayElement(context, type.element, type.elementCType)) {
444
+ return undefined;
445
+ }
446
+
447
+ const stride = inlineArrayStride(context, type);
448
+
449
+ if (stride === 0) {
450
+ return undefined;
451
+ }
452
+
453
+ return {
454
+ element: type.element,
455
+ count: type.fixedSize,
456
+ stride,
457
+ isTypedByteArray: context.library.isByteArrayTyped && isByteSequence(context.library, type),
458
+ };
459
+ };
460
+
341
461
  const arrayLengthExpression = (
342
462
  arrayRef: Extract<GirType, { kind: "carray" }>,
343
463
  siblingFields: GirField[],
344
464
  ): string | undefined => {
345
- if (arrayRef.fixedSize !== undefined) {
346
- return String(arrayRef.fixedSize);
347
- }
348
-
349
- if (arrayRef.lengthParameterIndex === undefined) {
465
+ if (arrayRef.fixedSize !== undefined || arrayRef.lengthParameterIndex === undefined) {
350
466
  return undefined;
351
467
  }
352
468
 
@@ -606,6 +722,82 @@ const resolveStructArray = (context: ModuleContext, target: StructArrayTarget):
606
722
  };
607
723
  };
608
724
 
725
+ const inlineArrayElementOffset = (options: InlineArrayAccessorOptions): string =>
726
+ `${String(options.offset)} + __index * ${String(options.resolution.stride)}`;
727
+
728
+ const inlineArrayGetterBlock = (options: InlineArrayAccessorOptions): string => {
729
+ const { context, jsName, tsType, descriptor, resolution } = options;
730
+ const container = resolution.isTypedByteArray ? `new ${tsType}(${String(resolution.count)})` : "[]";
731
+
732
+ const element = wrapReturnValue(context, {
733
+ ref: resolution.element,
734
+ isNullable: false,
735
+ valueExpression: `read(getHandle(this), ${descriptor}, ${inlineArrayElementOffset(options)})`,
736
+ });
737
+
738
+ const body = [
739
+ `const __result: ${tsType} = ${container};`,
740
+ `for (let __index = 0; __index < ${String(resolution.count)}; __index++) {`,
741
+ indent(`__result[__index] = ${element};`, 1),
742
+ "}",
743
+ "return __result;",
744
+ ].join("\n");
745
+
746
+ return renderBlock(`get ${jsName}(): ${tsType}`, body);
747
+ };
748
+
749
+ const inlineArraySetterBlock = (options: InlineArrayAccessorOptions): string => {
750
+ const { jsName, tsType, descriptor, resolution } = options;
751
+
752
+ const write =
753
+ `write(getHandle(this), ${descriptor}, ${inlineArrayElementOffset(options)}, ` +
754
+ `toNative(${descriptor}, __element));`;
755
+
756
+ const body = [
757
+ "for (const [__index, __element] of __value.entries()) {",
758
+ indent(`if (__index >= ${String(resolution.count)}) {\n break;\n}\n\n${write}`, 1),
759
+ "}",
760
+ ].join("\n");
761
+
762
+ return renderBlock(`set ${jsName}(__value: ${tsType})`, body);
763
+ };
764
+
765
+ const renderInlineArrayAccessor = (context: ModuleContext, target: StructArrayTarget): string | undefined => {
766
+ const resolution = resolveInlineArray(context, target);
767
+
768
+ if (resolution === undefined) {
769
+ return undefined;
770
+ }
771
+
772
+ const { field, jsName, slot } = target;
773
+ context.addRuntimeImport("read");
774
+ context.addRuntimeImport("getHandle");
775
+ context.addRuntimeImport("t");
776
+
777
+ const options: InlineArrayAccessorOptions = {
778
+ context,
779
+ jsName,
780
+ tsType: renderTsType(context, field.type, false),
781
+ descriptor: context.hoistDescriptor(renderDescriptor(context, resolution.element, "none", { isInline: true })),
782
+ offset: slot.byteOffset,
783
+ resolution,
784
+ };
785
+
786
+ const blocks: string[] = [];
787
+
788
+ if (field.readable) {
789
+ blocks.push(inlineArrayGetterBlock(options));
790
+ }
791
+
792
+ if (field.writable) {
793
+ context.addRuntimeImport("write");
794
+ context.addRuntimeImport("toNative");
795
+ blocks.push(inlineArraySetterBlock(options));
796
+ }
797
+
798
+ return blocks.length === 0 ? undefined : blocks.join("\n\n");
799
+ };
800
+
609
801
  const renderStructArrayAccessor = (context: ModuleContext, target: StructArrayTarget): string | undefined => {
610
802
  const resolution = resolveStructArray(context, target);
611
803
 
@@ -688,6 +880,7 @@ const setterBlock = (options: AccessorOptions): string =>
688
880
  );
689
881
 
690
882
  export {
883
+ fieldTsType,
691
884
  isEmittableField,
692
885
  isInlineField,
693
886
  isStorableFieldType,
@@ -56,6 +56,9 @@ const recordInlineSize = (context: ModuleContext, record: ResolvedRecordValue):
56
56
  return size > 0 ? size : undefined;
57
57
  };
58
58
 
59
+ const inlineArrayStride = (context: ModuleContext, array: Extract<GirType, { kind: "carray" }>): number =>
60
+ layoutOfType(context, array.element, array.elementCType, new Set()).size;
61
+
59
62
  const bitMask = (width: number): number => {
60
63
  if (width >= 32) {
61
64
  return 0xFF_FF_FF_FF;
@@ -224,4 +227,11 @@ const resolveAliasLayout = (
224
227
  return layoutOfType(context, ref, resolved.value.targetCType, visited);
225
228
  };
226
229
 
227
- export { computeRecordFieldSlots, recordInlineSize, bitMask, mergeBitfield, type RecordFieldSlot };
230
+ export {
231
+ computeRecordFieldSlots,
232
+ inlineArrayStride,
233
+ recordInlineSize,
234
+ bitMask,
235
+ mergeBitfield,
236
+ type RecordFieldSlot,
237
+ };
@@ -12,14 +12,6 @@ import { computeRecordFieldSlots } from "./record-layout.js";
12
12
  import { appendWrapperClassRegistration } from "./registration.js";
13
13
  import { isConstructibleRecord } from "./value-marshalable.js";
14
14
 
15
- type RecordMembersOptions = {
16
- context: ModuleContext;
17
- record: GirRecord;
18
- className: string;
19
- callables: Callables;
20
- gtypeExpr: string | undefined;
21
- };
22
-
23
15
  const isGErrorRecord = (context: ModuleContext, record: GirRecord): boolean =>
24
16
  context.namespace.name === "GLib" && record.glibGetType === "g_error_get_type";
25
17
 
@@ -38,8 +30,7 @@ const generateRecord = (context: ModuleContext, record: GirRecord): void => {
38
30
  };
39
31
 
40
32
  generateBindings(context, callables);
41
- const gtypeExpr = renderSourceGtype(context, record);
42
- const members = renderRecordMembers({ context, record, className, callables, gtypeExpr });
33
+ const members = renderRecordMembers(context, record, className, callables);
43
34
  const body = indentMembers(members);
44
35
  const heritage = isErrorSubclass ? " extends globalThis.Error" : "";
45
36
  const doc = getDoc(record);
@@ -59,16 +50,29 @@ const generateRecord = (context: ModuleContext, record: GirRecord): void => {
59
50
  });
60
51
  }
61
52
 
53
+ const gtypeExpr = renderSourceGtype(context, record);
54
+
62
55
  appendWrapperClassRegistration(context, {
63
56
  className,
64
57
  gtypeExpr,
65
58
  });
66
59
  };
67
60
 
68
- const renderRecordMembers = (options: RecordMembersOptions): string[] => {
69
- const { context, record, className, callables, gtypeExpr } = options;
61
+ const renderRecordMembers = (
62
+ context: ModuleContext,
63
+ record: GirRecord,
64
+ className: string,
65
+ callables: Callables,
66
+ ): string[] => {
70
67
  const isErrorSubclass = isGErrorRecord(context, record);
71
- const { members, claimedNames } = renderPlainTypeMembers({ context, className, callables, gtypeExpr });
68
+
69
+ const { members, claimedNames } = renderPlainTypeMembers({
70
+ context,
71
+ className,
72
+ callables,
73
+ hasGtype: record.glibGetType !== undefined,
74
+ });
75
+
72
76
  members.unshift(renderRecordConstructor(context, { record, className, callables, isErrorSubclass }));
73
77
  const { slots } = computeRecordFieldSlots(context, record.fields, record.isUnion);
74
78
 
@@ -21,7 +21,7 @@ const RUNTIME_OVERRIDES: Map<string, RuntimeOverride> = new Map([
21
21
  [
22
22
  "g_value_set_boxed",
23
23
  {
24
- signature: "boxed: object | null",
24
+ signature: "boxed: object | string | number | bigint | boolean | null",
25
25
  returnType: "void",
26
26
  body: 'throw new Error("g_value_set_boxed: runtime override not installed");',
27
27
  renames: [["v_boxed", "boxed"]],
@@ -17,7 +17,7 @@ import {
17
17
  resolvePrerequisiteReference,
18
18
  } from "../../analysis/inheritance.js";
19
19
  import { renderHandlerParameters, renderHandlerResultType } from "../../analysis/param-structure.js";
20
- import { renderTsType } from "../../analysis/ts-type.js";
20
+ import { renderParameterTsType, renderTsType } from "../../analysis/ts-type.js";
21
21
  import { resolveInterfaces } from "../../gir/ancestry.js";
22
22
  import { isCallerAllocatedOut, isOutParameter } from "../../gir/parameter.js";
23
23
  import { renderJsDoc } from "../../writer/doc.js";
@@ -259,7 +259,7 @@ const renderResultType = (
259
259
  const renderSignalEmitEntry = (context: ModuleContext, signal: GirCallable): string => {
260
260
  const args = renderHandlerParameters(
261
261
  signal.parameters,
262
- (ref, nullable) => renderTsType(context, ref, nullable),
262
+ (ref, nullable) => renderParameterTsType(context, ref, nullable),
263
263
  isCallerAllocatedOut,
264
264
  );
265
265
 
@@ -11,6 +11,7 @@ import type { JsDocSpec } from "../../writer/doc.js";
11
11
  import {
12
12
  isInlineCallbackRef,
13
13
  isScalarRef,
14
+ renderCallbackType,
14
15
  renderDescriptor,
15
16
  renderParamDescriptor,
16
17
  } from "../../analysis/descriptor-render.js";
@@ -19,7 +20,6 @@ import {
19
20
  handlerParameters,
20
21
  parameterIdentifier,
21
22
  renamesWithInstance,
22
- renderHandlerParameters,
23
23
  renderHandlerResultType,
24
24
  } from "../../analysis/param-structure.js";
25
25
  import { renderTsType } from "../../analysis/ts-type.js";
@@ -74,6 +74,12 @@ type Vtable = {
74
74
  slots: VtableSlot[];
75
75
  };
76
76
 
77
+ type SlotParamPlan = {
78
+ parameters: GirParameter[];
79
+ argIndexMap: Map<number, number>;
80
+ decoded: Set<GirParameter>;
81
+ };
82
+
77
83
  const PADDING_FIELD_NAME = /^(?:reserved|padding)\d*$/i;
78
84
 
79
85
  const UNCALLABLE_SLOT_KEYS: Set<string> = new Set([
@@ -86,6 +92,7 @@ const UNCALLABLE_SLOT_KEYS: Set<string> = new Set([
86
92
  const VTABLE_CACHE: WeakMap<GirClass, Vtable> = new WeakMap();
87
93
  const PROTECTED_SLOT_NOTE = "It is `protected`, so only a subclass chaining up reaches it.";
88
94
  const PUBLIC_SLOT_NOTE = "Calling it from anywhere else re-enters the slot on a live instance.";
95
+ const OPAQUE_CALLBACK_NOTE = "Received as the raw address of the C function pointer.";
89
96
 
90
97
  const vfuncMemberName = (fieldName: string): string => `vfunc${pascalCase(fieldName)}`;
91
98
 
@@ -125,6 +132,79 @@ const vtableCallbackType = (context: ModuleContext, field: GirField): GirCallbac
125
132
  return type.value;
126
133
  };
127
134
 
135
+ const isCallbackParam = (context: ModuleContext, parameter: GirParameter): boolean =>
136
+ parameter.type !== undefined && context.library.typeFor(parameter.type)?.kind === "callback";
137
+
138
+ const isDecodedCallbackParam = (context: ModuleContext, parameter: GirParameter, index: number): boolean =>
139
+ isCallbackParam(context, parameter) &&
140
+ parameter.closureIndex === index + 1 &&
141
+ parameter.destroyIndex === undefined;
142
+
143
+ const decodedSlotParams = (context: ModuleContext, callback: GirCallback): Set<GirParameter> => {
144
+ const decoded: Set<GirParameter> = new Set();
145
+
146
+ for (const [index, parameter] of callback.parameters.entries()) {
147
+ if (isDecodedCallbackParam(context, parameter, index)) {
148
+ decoded.add(parameter);
149
+ }
150
+ }
151
+
152
+ return decoded;
153
+ };
154
+
155
+ const companionIndices = (decoded: Set<GirParameter>): Set<number> => {
156
+ const companions: Set<number> = new Set();
157
+
158
+ for (const parameter of decoded) {
159
+ if (parameter.closureIndex !== undefined) {
160
+ companions.add(parameter.closureIndex);
161
+ }
162
+ }
163
+
164
+ return companions;
165
+ };
166
+
167
+ const slotParamPlan = (context: ModuleContext, callback: GirCallback): SlotParamPlan => {
168
+ const decoded = decodedSlotParams(context, callback);
169
+ const companions = companionIndices(decoded);
170
+ const parameters: GirParameter[] = [];
171
+ const argIndexMap: Map<number, number> = new Map();
172
+
173
+ for (const [index, parameter] of callback.parameters.entries()) {
174
+ if (companions.has(index)) {
175
+ continue;
176
+ }
177
+
178
+ argIndexMap.set(index, parameters.length);
179
+ parameters.push(parameter);
180
+ }
181
+
182
+ return { parameters, argIndexMap, decoded };
183
+ };
184
+
185
+ const isOpaqueCallbackParam = (context: ModuleContext, plan: SlotParamPlan, parameter: GirParameter): boolean =>
186
+ isCallbackParam(context, parameter) && !plan.decoded.has(parameter);
187
+
188
+ const renderSlotParamDescriptor = (context: ModuleContext, parameter: GirParameter, plan: SlotParamPlan): string => {
189
+ if (plan.decoded.has(parameter)) {
190
+ const callbackDescriptor = renderCallbackType(context, parameter.type, parameter);
191
+
192
+ if (callbackDescriptor !== undefined) {
193
+ return callbackDescriptor;
194
+ }
195
+ }
196
+
197
+ return renderParamDescriptor(context, parameter, parameter.type, { argIndexMap: plan.argIndexMap });
198
+ };
199
+
200
+ const renderSlotParamTsType = (context: ModuleContext, plan: SlotParamPlan, parameter: GirParameter): string => {
201
+ if (isOpaqueCallbackParam(context, plan, parameter)) {
202
+ return parameter.nullable ? "bigint | null" : "bigint";
203
+ }
204
+
205
+ return renderTsType(context, parameter.type, parameter.nullable);
206
+ };
207
+
128
208
  const vtableSlotEntry = (
129
209
  context: ModuleContext,
130
210
  field: GirField,
@@ -387,15 +467,35 @@ const hasCallableVfuncSlots = (context: ModuleContext, namespaceName: string, kl
387
467
 
388
468
  const slotDoc = (slot: VtableSlot): string | undefined => slot.vfunc?.doc ?? slot.field.doc ?? slot.callback.doc;
389
469
 
390
- const slotDocParameters = (slot: VtableSlot): GirParameter[] => {
470
+ const slotParamDoc = (
471
+ context: ModuleContext,
472
+ plan: SlotParamPlan,
473
+ parameter: GirParameter,
474
+ doc: string | undefined,
475
+ ): string | undefined => {
476
+ if (!isOpaqueCallbackParam(context, plan, parameter)) {
477
+ return doc;
478
+ }
479
+
480
+ return doc === undefined ? OPAQUE_CALLBACK_NOTE : `${doc} ${OPAQUE_CALLBACK_NOTE}`;
481
+ };
482
+
483
+ const slotDocParameters = (context: ModuleContext, slot: VtableSlot): GirParameter[] => {
484
+ const plan = slotParamPlan(context, slot.callback);
391
485
  const [, ...parameters] = slot.callback.parameters;
392
486
  const vfuncParameters = slot.vfunc?.parameters ?? [];
393
487
 
394
- return parameters.map((parameter, index) => ({ ...parameter, doc: vfuncParameters[index]?.doc ?? parameter.doc }));
488
+ return parameters
489
+ .map((parameter, index) => ({ parameter, index }))
490
+ .filter(({ index }) => plan.argIndexMap.has(index + 1))
491
+ .map(({ parameter, index }) => ({
492
+ ...parameter,
493
+ doc: slotParamDoc(context, plan, parameter, vfuncParameters[index]?.doc ?? parameter.doc),
494
+ }));
395
495
  };
396
496
 
397
- const slotDocSpec = (slot: VtableSlot): JsDocSpec => {
398
- const parameters = slotDocParameters(slot);
497
+ const slotDocSpec = (context: ModuleContext, slot: VtableSlot): JsDocSpec => {
498
+ const parameters = slotDocParameters(context, slot);
399
499
  const source = slot.vfunc ?? slot.callback;
400
500
 
401
501
  return {
@@ -446,12 +546,18 @@ const isCallableSlot = (context: ModuleContext, slot: VtableSlot): boolean =>
446
546
  !UNCALLABLE_SLOT_KEYS.has(slot.key) && !hasUnannotatedPointerParam(context, slot);
447
547
 
448
548
  const vfuncSlotSignature = (context: ModuleContext, slot: VtableSlot, isOptional = false): VfuncSignature => {
449
- const [, ...parameters] = slot.callback.parameters;
549
+ const plan = slotParamPlan(context, slot.callback);
550
+ const [, ...parameters] = plan.parameters;
450
551
 
451
552
  const renderType = (ref: TypeId | undefined, isNullable: boolean): string =>
452
553
  renderTsType(context, ref, isNullable);
453
554
 
454
- const signature = renderHandlerParameters(parameters, renderType).join(", ");
555
+ const signature = handlerParameters(parameters)
556
+ .map(
557
+ (parameter, index) =>
558
+ `${parameterIdentifier(parameter, index)}: ${renderSlotParamTsType(context, plan, parameter)}`)
559
+ .join(", ");
560
+
455
561
  const folded = foldedLengthParameters(context.library, slot.callback);
456
562
 
457
563
  const returnType = renderHandlerResultType({
@@ -470,13 +576,13 @@ const renderVfuncMember = (options: VfuncMemberOptions): string => {
470
576
  const { context, ownerRef, slot, mode, isProtected } = options;
471
577
  const { header, returnType } = vfuncSlotSignature(context, slot, mode === "requirement");
472
578
  const declaration = `${isProtected ? "protected " : ""}${header}`;
473
- const doc = renderJsDoc(slotDoc(slot), vfuncMemberNote(options), slotDocSpec(slot));
579
+ const doc = renderJsDoc(slotDoc(slot), vfuncMemberNote(options), slotDocSpec(context, slot));
474
580
 
475
581
  if (mode !== "implementation") {
476
582
  return `${doc}${declaration};`;
477
583
  }
478
584
 
479
- const [, ...parameters] = slot.callback.parameters;
585
+ const [, ...parameters] = slotParamPlan(context, slot.callback).parameters;
480
586
 
481
587
  const inputs = handlerParameters(parameters).map((parameter, index) =>
482
588
  parameterIdentifier(parameter, index));
@@ -511,9 +617,10 @@ const isVtableSlotEligible = (context: ModuleContext, callback: GirCallback): bo
511
617
 
512
618
  const renderVtableSlotDescriptor = (context: ModuleContext, vtable: Vtable, slot: VtableSlot): string => {
513
619
  const { key, field, callback, byteOffset } = slot;
620
+ const plan = slotParamPlan(context, callback);
514
621
 
515
- const argDescriptors = callback.parameters
516
- .map((param) => renderParamDescriptor(context, param, param.type))
622
+ const argDescriptors = plan.parameters
623
+ .map((param) => renderSlotParamDescriptor(context, param, plan))
517
624
  .join(", ");
518
625
 
519
626
  const returnDescriptor = renderDescriptor(