@gtkx/runtime 1.0.0-rc.4 → 1.1.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 (156) hide show
  1. package/README.md +5 -5
  2. package/dist/application-class.d.ts +37 -0
  3. package/dist/application-class.d.ts.map +1 -0
  4. package/dist/application-class.js +78 -0
  5. package/dist/application-class.js.map +1 -0
  6. package/dist/arg.d.ts +2 -1
  7. package/dist/arg.d.ts.map +1 -1
  8. package/dist/arg.js +2 -1
  9. package/dist/arg.js.map +1 -1
  10. package/dist/bind.d.ts +11 -0
  11. package/dist/bind.d.ts.map +1 -1
  12. package/dist/bind.js +11 -0
  13. package/dist/bind.js.map +1 -1
  14. package/dist/callback.d.ts +4 -3
  15. package/dist/callback.d.ts.map +1 -1
  16. package/dist/callback.js +124 -29
  17. package/dist/callback.js.map +1 -1
  18. package/dist/closure.d.ts +26 -0
  19. package/dist/closure.d.ts.map +1 -0
  20. package/dist/closure.js +99 -0
  21. package/dist/closure.js.map +1 -0
  22. package/dist/default-application.d.ts +4 -0
  23. package/dist/default-application.d.ts.map +1 -0
  24. package/dist/default-application.js +17 -0
  25. package/dist/default-application.js.map +1 -0
  26. package/dist/descriptors.d.ts +98 -4
  27. package/dist/descriptors.d.ts.map +1 -1
  28. package/dist/descriptors.js +72 -6
  29. package/dist/descriptors.js.map +1 -1
  30. package/dist/error.d.ts +1 -0
  31. package/dist/error.d.ts.map +1 -1
  32. package/dist/error.js.map +1 -1
  33. package/dist/exit-hook.js +13 -0
  34. package/dist/exit-hook.js.map +1 -1
  35. package/dist/fn.d.ts +24 -2
  36. package/dist/fn.d.ts.map +1 -1
  37. package/dist/fn.js +67 -32
  38. package/dist/fn.js.map +1 -1
  39. package/dist/folded-lengths.d.ts +16 -0
  40. package/dist/folded-lengths.d.ts.map +1 -0
  41. package/dist/folded-lengths.js +38 -0
  42. package/dist/folded-lengths.js.map +1 -0
  43. package/dist/index.d.ts +14 -6
  44. package/dist/index.d.ts.map +1 -1
  45. package/dist/index.js +12 -4
  46. package/dist/index.js.map +1 -1
  47. package/dist/internal.d.ts +2 -0
  48. package/dist/internal.d.ts.map +1 -1
  49. package/dist/internal.js +2 -0
  50. package/dist/internal.js.map +1 -1
  51. package/dist/library.d.ts +3 -1
  52. package/dist/library.d.ts.map +1 -1
  53. package/dist/library.js +3 -1
  54. package/dist/library.js.map +1 -1
  55. package/dist/lifecycle.d.ts +58 -11
  56. package/dist/lifecycle.d.ts.map +1 -1
  57. package/dist/lifecycle.js +95 -22
  58. package/dist/lifecycle.js.map +1 -1
  59. package/dist/listeners.d.ts.map +1 -1
  60. package/dist/listeners.js +19 -5
  61. package/dist/listeners.js.map +1 -1
  62. package/dist/mixin.d.ts +3 -2
  63. package/dist/mixin.d.ts.map +1 -1
  64. package/dist/mixin.js +13 -1
  65. package/dist/mixin.js.map +1 -1
  66. package/dist/native-value.d.ts.map +1 -1
  67. package/dist/native-value.js +4 -3
  68. package/dist/native-value.js.map +1 -1
  69. package/dist/object.d.ts +40 -7
  70. package/dist/object.d.ts.map +1 -1
  71. package/dist/object.js +95 -26
  72. package/dist/object.js.map +1 -1
  73. package/dist/param-spec.d.ts +11 -0
  74. package/dist/param-spec.d.ts.map +1 -0
  75. package/dist/param-spec.js +147 -0
  76. package/dist/param-spec.js.map +1 -0
  77. package/dist/promisify.d.ts.map +1 -1
  78. package/dist/promisify.js +3 -2
  79. package/dist/promisify.js.map +1 -1
  80. package/dist/properties.d.ts +39 -7
  81. package/dist/properties.d.ts.map +1 -1
  82. package/dist/properties.js +310 -43
  83. package/dist/properties.js.map +1 -1
  84. package/dist/register-class.d.ts +147 -12
  85. package/dist/register-class.d.ts.map +1 -1
  86. package/dist/register-class.js +129 -83
  87. package/dist/register-class.js.map +1 -1
  88. package/dist/registry.d.ts +80 -21
  89. package/dist/registry.d.ts.map +1 -1
  90. package/dist/registry.js +86 -33
  91. package/dist/registry.js.map +1 -1
  92. package/dist/signal.d.ts +11 -4
  93. package/dist/signal.d.ts.map +1 -1
  94. package/dist/signal.js +85 -25
  95. package/dist/signal.js.map +1 -1
  96. package/dist/t.d.ts +2 -0
  97. package/dist/t.d.ts.map +1 -1
  98. package/dist/t.js +1 -0
  99. package/dist/t.js.map +1 -1
  100. package/dist/tuple.d.ts +6 -3
  101. package/dist/tuple.d.ts.map +1 -1
  102. package/dist/tuple.js +9 -7
  103. package/dist/tuple.js.map +1 -1
  104. package/dist/type.d.ts +2 -1
  105. package/dist/type.d.ts.map +1 -1
  106. package/dist/type.js +2 -0
  107. package/dist/type.js.map +1 -1
  108. package/dist/value.d.ts +10 -1
  109. package/dist/value.d.ts.map +1 -1
  110. package/dist/value.js +99 -24
  111. package/dist/value.js.map +1 -1
  112. package/dist/vfunc-call.d.ts +37 -0
  113. package/dist/vfunc-call.d.ts.map +1 -0
  114. package/dist/vfunc-call.js +172 -0
  115. package/dist/vfunc-call.js.map +1 -0
  116. package/dist/vfunc-seeds.d.ts +12 -0
  117. package/dist/vfunc-seeds.d.ts.map +1 -0
  118. package/dist/vfunc-seeds.js +19 -0
  119. package/dist/vfunc-seeds.js.map +1 -0
  120. package/dist/vfunc.d.ts +8 -0
  121. package/dist/vfunc.d.ts.map +1 -0
  122. package/dist/vfunc.js +29 -0
  123. package/dist/vfunc.js.map +1 -0
  124. package/package.json +14 -4
  125. package/src/application-class.ts +126 -0
  126. package/src/arg.ts +2 -1
  127. package/src/bind.ts +11 -0
  128. package/src/callback.ts +178 -34
  129. package/src/closure.ts +136 -0
  130. package/src/default-application.ts +21 -0
  131. package/src/descriptors.ts +150 -34
  132. package/src/error.ts +1 -0
  133. package/src/exit-hook.ts +18 -0
  134. package/src/fn.ts +107 -36
  135. package/src/folded-lengths.ts +64 -0
  136. package/src/index.ts +21 -6
  137. package/src/internal.ts +2 -0
  138. package/src/library.ts +3 -1
  139. package/src/lifecycle.ts +138 -25
  140. package/src/listeners.ts +25 -6
  141. package/src/mixin.ts +15 -2
  142. package/src/native-value.ts +5 -3
  143. package/src/object.ts +134 -35
  144. package/src/param-spec.ts +234 -0
  145. package/src/promisify.ts +4 -2
  146. package/src/properties.ts +473 -46
  147. package/src/register-class.ts +386 -114
  148. package/src/registry.ts +169 -49
  149. package/src/signal.ts +116 -42
  150. package/src/t.ts +3 -0
  151. package/src/tuple.ts +18 -12
  152. package/src/type.ts +3 -1
  153. package/src/value.ts +143 -23
  154. package/src/vfunc-call.ts +247 -0
  155. package/src/vfunc-seeds.ts +36 -0
  156. package/src/vfunc.ts +38 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vfunc-call.js","sourceRoot":"","sources":["../src/vfunc-call.ts"],"names":[],"mappings":"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","sourcesContent":["import type { BindVfuncOptions } from \"@gtkx/native\";\nimport type { AnyClass } from \"@gtkx/utils\";\nimport { bindVfunc } from \"@gtkx/native\";\nimport { type Arg, isCallerAllocatedArg, requiresInputArg } from \"./arg.js\";\nimport { buildNativeArgTypes, fromNativeCallable } from \"./fn.js\";\nimport { toNative } from \"./native-value.js\";\nimport {\n getClassType,\n getHandle,\n getInterfaceVfuncRegistry,\n getVfuncRegistry,\n instanceClassName,\n resolveWrapperType,\n type VfuncDescriptor,\n} from \"./registry.js\";\nimport { TYPE_INVALID, typeInterfaces, typeIsA, typeParent } from \"./type.js\";\nimport { type RefSeeds, seedsFor } from \"./vfunc-seeds.js\";\nimport { findClassVfuncDescriptor, findInterfaceVfuncDescriptor, vfuncArgs } from \"./vfunc.js\";\n\ntype Invoker = (instance: object, inputs: unknown[]) => unknown;\ntype InvokerCache = WeakMap<AnyClass, Map<string, Invoker>>;\ntype ResolvedSlot = { descriptor: VfuncDescriptor; interfaceType?: bigint };\n\nconst NO_BASELINE = -1;\n\nconst SEEDED_SLOTS: Record<string, RefSeeds> = {\n \"LayoutManagerClass.measure\": new Map([[6, NO_BASELINE], [7, NO_BASELINE]]),\n \"WidgetClass.measure\": new Map([[5, NO_BASELINE], [6, NO_BASELINE]]),\n};\n\nconst parentInvokers: InvokerCache = new WeakMap();\nconst vfuncInvokers: InvokerCache = new WeakMap();\n\nfunction toNativeInput(arg: Arg, input: unknown): unknown {\n return isCallerAllocatedArg(arg) ? input : toNative(arg.type, input);\n}\n\nfunction bindOptionsFor(\n slot: ResolvedSlot,\n instanceType: bigint | undefined,\n args: Arg[],\n label: string,\n): BindVfuncOptions {\n const { descriptor, interfaceType } = slot;\n\n const options: BindVfuncOptions = {\n byteOffset: descriptor.byteOffset,\n label,\n argDescriptors: buildNativeArgTypes(args, descriptor.canThrow === true),\n returnDescriptor: descriptor.returnDescriptor,\n };\n\n if (descriptor.vtableSize !== undefined) {\n options.vtableSize = descriptor.vtableSize;\n }\n\n if (instanceType !== undefined) {\n options.instanceType = instanceType;\n }\n\n if (interfaceType !== undefined) {\n options.interfaceType = interfaceType;\n }\n\n return options;\n}\n\nfunction buildInvoker(slot: ResolvedSlot, instanceType: bigint | undefined, caller: string): Invoker {\n const { descriptor } = slot;\n const args = vfuncArgs(descriptor);\n const canThrow = descriptor.canThrow === true;\n const label = `${descriptor.className}.${descriptor.vfuncName}`;\n const [, ...inputArgs] = args.filter(requiresInputArg);\n let pendingSeeds: RefSeeds | undefined;\n\n const takeRefSeeds = (): RefSeeds | undefined => {\n const seeds = pendingSeeds;\n pendingSeeds = undefined;\n\n return seeds;\n };\n\n const shaped = fromNativeCallable(\n bindVfunc(bindOptionsFor(slot, instanceType, args, label)),\n {\n args,\n returns: descriptor.returnDescriptor,\n canThrow,\n },\n takeRefSeeds,\n );\n\n return (instance, inputs) => {\n if (inputs.length !== inputArgs.length) {\n throw new Error(\n `${caller}: ${label} expects ${String(inputArgs.length)} arguments, ` +\n `received ${String(inputs.length)}`,\n );\n }\n\n const nativeInputs = inputArgs.map((arg, index) => toNativeInput(arg, inputs[index]));\n pendingSeeds = SEEDED_SLOTS[label] ?? seedsFor(descriptor.argDescriptors, instance);\n\n return shaped(getHandle(instance), ...nativeInputs);\n };\n}\n\nfunction cachedInvoker(cache: InvokerCache, owner: AnyClass, key: string, build: () => Invoker): Invoker {\n let byKey = cache.get(owner);\n\n if (byKey === undefined) {\n byKey = new Map();\n cache.set(owner, byKey);\n }\n\n let invoker = byKey.get(key);\n\n if (invoker === undefined) {\n invoker = build();\n byKey.set(key, invoker);\n }\n\n return invoker;\n}\n\nfunction resolveParentSlot(klass: AnyClass, parentType: bigint, methodName: string): ResolvedSlot | undefined {\n const classDescriptor = findClassVfuncDescriptor(klass, methodName);\n\n if (classDescriptor) {\n return { descriptor: classDescriptor };\n }\n\n for (const interfaceType of typeInterfaces(parentType)) {\n const descriptor = findInterfaceVfuncDescriptor(interfaceType, methodName);\n\n if (descriptor) {\n return { descriptor, interfaceType };\n }\n }\n\n return undefined;\n}\n\nfunction resolveParentType(klass: AnyClass, methodName: string): bigint {\n const gtype = getClassType(klass);\n\n if (gtype === TYPE_INVALID) {\n throw new Error(\n `callParent: cannot call '${methodName}' because ${klass.name} was never passed to registerClass`,\n );\n }\n\n return typeParent(gtype);\n}\n\nfunction buildParentInvoker(klass: AnyClass, methodName: string): Invoker {\n const parentType = resolveParentType(klass, methodName);\n const slot = resolveParentSlot(klass, parentType, methodName);\n\n if (!slot) {\n throw new Error(`callParent: ${klass.name} inherits no '${methodName}' vtable slot`);\n }\n\n return buildInvoker(slot, parentType, \"callParent\");\n}\n\n/**\n * Calls the implementation of a virtual function that the parent type of `klass` provides, so an\n * override can chain up to the behavior it replaces. `klass` is the class whose override is\n * running, named lexically rather than taken from `instance`, so each level of a hierarchy reaches\n * exactly one level up. Arguments and the return value follow the same convention the override\n * itself uses: pure out parameters are left out and returned instead, and a slot with several\n * outputs returns them as a tuple. Throws if `klass` was never registered, inherits no such slot,\n * or the parent type leaves the slot empty.\n *\n * @param klass The registered class whose override is chaining up.\n * @param methodName Name of the overridden method, such as `vfuncMeasure`.\n * @param instance The instance the override was invoked on.\n * @param inputs The arguments the override received.\n */\nfunction callParent(klass: AnyClass, methodName: string, instance: object, ...inputs: unknown[]): unknown {\n const invoker = cachedInvoker(parentInvokers, klass, methodName, () => buildParentInvoker(klass, methodName));\n\n return invoker(instance, inputs);\n}\n\nfunction resolveOwnerSlot(owner: AnyClass, key: string): ResolvedSlot {\n const classDescriptor = getVfuncRegistry(owner)?.[key];\n\n if (classDescriptor !== undefined) {\n return { descriptor: classDescriptor };\n }\n\n const interfaceType = getClassType(owner);\n const descriptor = getInterfaceVfuncRegistry(interfaceType)?.[key];\n\n if (descriptor === undefined) {\n throw new Error(`callVfunc: ${owner.name} declares no '${key}' vtable slot`);\n }\n\n return { descriptor, interfaceType };\n}\n\nfunction requiresDefaultVtable(slot: ResolvedSlot, instanceType: bigint): boolean {\n return slot.interfaceType !== undefined && !typeIsA(instanceType, slot.interfaceType);\n}\n\nfunction resolveInstanceType(key: string, instance: object, slot: ResolvedSlot): bigint | undefined {\n const instanceType = resolveWrapperType(instance);\n\n if (instanceType === TYPE_INVALID) {\n const name = instanceClassName(instance);\n throw new Error(`callVfunc: cannot call '${key}' because ${name} descends from no registered wrapper class`);\n }\n\n return requiresDefaultVtable(slot, instanceType) ? undefined : instanceType;\n}\n\n/**\n * Calls the implementation of a virtual function a wrapper class or interface declares, backing the\n * `vfunc`-prefixed members the generated bindings emit. The slot is read from the nearest generated\n * wrapper class in the instance's class chain rather than from the instance's own type, so an\n * override reaching it through `super` runs the implementation it replaced instead of re-entering\n * itself. When that wrapper class does not carry the interface the slot belongs to, because a class\n * registered below it adopted the interface, the slot comes from the interface's own default vtable\n * instead, which holds exactly what the adopting class replaced. Arguments and the return value\n * follow the same convention an override uses: pure out parameters are left out and returned\n * instead, and a slot with several outputs returns them as a tuple. Throws if the owner declares no\n * such slot, the instance descends from no wrapper class, or the resolved vtable leaves the slot\n * empty.\n *\n * @param owner The wrapper class or interface declaring the slot.\n * @param key Name of the generated member, such as `vfuncMeasure`.\n * @param instance The instance to invoke the slot on.\n * @param inputs The arguments the slot receives.\n */\nfunction callVfunc(owner: AnyClass, key: string, instance: object, inputs: unknown[]): unknown {\n const slot = resolveOwnerSlot(owner, key);\n const instanceType = resolveInstanceType(key, instance, slot);\n\n const invoker = cachedInvoker(vfuncInvokers, owner, `${key}:${String(instanceType)}`, () =>\n buildInvoker(slot, instanceType, \"callVfunc\"));\n\n return invoker(instance, inputs);\n}\n\nexport { callParent, callVfunc };\n"]}
@@ -0,0 +1,12 @@
1
+ import type { Descriptor } from "@gtkx/native";
2
+ type RefSeeds = Map<number, unknown>;
3
+ type SeedFrame = {
4
+ argDescriptors: Descriptor[];
5
+ instance: unknown;
6
+ seeds: RefSeeds;
7
+ };
8
+ declare const pushSeedFrame: (frame: SeedFrame) => void;
9
+ declare const popSeedFrame: () => void;
10
+ declare const seedsFor: (argDescriptors: Descriptor[], instance: unknown) => RefSeeds | undefined;
11
+ export { popSeedFrame, pushSeedFrame, type RefSeeds, seedsFor };
12
+ //# sourceMappingURL=vfunc-seeds.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vfunc-seeds.d.ts","sourceRoot":"","sources":["../src/vfunc-seeds.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE/C,KAAK,QAAQ,GAAG,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAErC,KAAK,SAAS,GAAG;IACb,cAAc,EAAE,UAAU,EAAE,CAAC;IAC7B,QAAQ,EAAE,OAAO,CAAC;IAClB,KAAK,EAAE,QAAQ,CAAC;CACnB,CAAC;AAIF,QAAA,MAAM,aAAa,GAAI,OAAO,SAAS,KAAG,IAEzC,CAAC;AAEF,QAAA,MAAM,YAAY,QAAO,IAExB,CAAC;AAKF,QAAA,MAAM,QAAQ,GAAI,gBAAgB,UAAU,EAAE,EAAE,UAAU,OAAO,KAAG,QAAQ,GAAG,SAU9E,CAAC;AAEF,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,KAAK,QAAQ,EAAE,QAAQ,EAAE,CAAC"}
@@ -0,0 +1,19 @@
1
+ const frames = [];
2
+ const pushSeedFrame = (frame) => {
3
+ frames.push(frame);
4
+ };
5
+ const popSeedFrame = () => {
6
+ frames.pop();
7
+ };
8
+ const isMatchingFrame = (frame, argDescriptors, instance) => frame.argDescriptors === argDescriptors && frame.instance === instance;
9
+ const seedsFor = (argDescriptors, instance) => {
10
+ for (let index = frames.length - 1; index >= 0; index--) {
11
+ const frame = frames[index];
12
+ if (frame !== undefined && isMatchingFrame(frame, argDescriptors, instance)) {
13
+ return frame.seeds;
14
+ }
15
+ }
16
+ return undefined;
17
+ };
18
+ export { popSeedFrame, pushSeedFrame, seedsFor };
19
+ //# sourceMappingURL=vfunc-seeds.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vfunc-seeds.js","sourceRoot":"","sources":["../src/vfunc-seeds.ts"],"names":[],"mappings":"AAUA,MAAM,MAAM,GAAgB,EAAE,CAAC;AAE/B,MAAM,aAAa,GAAG,CAAC,KAAgB,EAAQ,EAAE;IAC7C,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;AACvB,CAAC,CAAC;AAEF,MAAM,YAAY,GAAG,GAAS,EAAE;IAC5B,MAAM,CAAC,GAAG,EAAE,CAAC;AACjB,CAAC,CAAC;AAEF,MAAM,eAAe,GAAG,CAAC,KAAgB,EAAE,cAA4B,EAAE,QAAiB,EAAW,EAAE,CACnG,KAAK,CAAC,cAAc,KAAK,cAAc,IAAI,KAAK,CAAC,QAAQ,KAAK,QAAQ,CAAC;AAE3E,MAAM,QAAQ,GAAG,CAAC,cAA4B,EAAE,QAAiB,EAAwB,EAAE;IACvF,KAAK,IAAI,KAAK,GAAG,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,KAAK,IAAI,CAAC,EAAE,KAAK,EAAE,EAAE,CAAC;QACtD,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;QAE5B,IAAI,KAAK,KAAK,SAAS,IAAI,eAAe,CAAC,KAAK,EAAE,cAAc,EAAE,QAAQ,CAAC,EAAE,CAAC;YAC1E,OAAO,KAAK,CAAC,KAAK,CAAC;QACvB,CAAC;IACL,CAAC;IAED,OAAO,SAAS,CAAC;AACrB,CAAC,CAAC;AAEF,OAAO,EAAE,YAAY,EAAE,aAAa,EAAiB,QAAQ,EAAE,CAAC","sourcesContent":["import type { Descriptor } from \"@gtkx/native\";\n\ntype RefSeeds = Map<number, unknown>;\n\ntype SeedFrame = {\n argDescriptors: Descriptor[];\n instance: unknown;\n seeds: RefSeeds;\n};\n\nconst frames: SeedFrame[] = [];\n\nconst pushSeedFrame = (frame: SeedFrame): void => {\n frames.push(frame);\n};\n\nconst popSeedFrame = (): void => {\n frames.pop();\n};\n\nconst isMatchingFrame = (frame: SeedFrame, argDescriptors: Descriptor[], instance: unknown): boolean =>\n frame.argDescriptors === argDescriptors && frame.instance === instance;\n\nconst seedsFor = (argDescriptors: Descriptor[], instance: unknown): RefSeeds | undefined => {\n for (let index = frames.length - 1; index >= 0; index--) {\n const frame = frames[index];\n\n if (frame !== undefined && isMatchingFrame(frame, argDescriptors, instance)) {\n return frame.seeds;\n }\n }\n\n return undefined;\n};\n\nexport { popSeedFrame, pushSeedFrame, type RefSeeds, seedsFor };\n"]}
@@ -0,0 +1,8 @@
1
+ import { type AnyClass } from "@gtkx/utils";
2
+ import type { Arg } from "./arg.js";
3
+ import { type VfuncDescriptor } from "./registry.js";
4
+ declare function findClassVfuncDescriptor(klass: AnyClass, methodName: string): VfuncDescriptor | null;
5
+ declare function findInterfaceVfuncDescriptor(interfaceGtype: bigint, methodName: string): VfuncDescriptor | undefined;
6
+ declare function vfuncArgs(descriptor: VfuncDescriptor): Arg[];
7
+ export { findClassVfuncDescriptor, findInterfaceVfuncDescriptor, vfuncArgs };
8
+ //# sourceMappingURL=vfunc.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vfunc.d.ts","sourceRoot":"","sources":["../src/vfunc.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,QAAQ,EAAkC,MAAM,aAAa,CAAC;AAC5E,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAGpC,OAAO,EAA+C,KAAK,eAAe,EAAE,MAAM,eAAe,CAAC;AAElG,iBAAS,wBAAwB,CAAC,KAAK,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,GAAG,eAAe,GAAG,IAAI,CAE7F;AAED,iBAAS,4BAA4B,CAAC,cAAc,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,eAAe,GAAG,SAAS,CAE7G;AAkBD,iBAAS,SAAS,CAAC,UAAU,EAAE,eAAe,GAAG,GAAG,EAAE,CAIrD;AAED,OAAO,EAAE,wBAAwB,EAAE,4BAA4B,EAAE,SAAS,EAAE,CAAC"}
package/dist/vfunc.js ADDED
@@ -0,0 +1,29 @@
1
+ import { getParentClass, walkClassChain } from "@gtkx/utils";
2
+ import { isCallerAllocatedOut } from "./callback.js";
3
+ import { foldedLengthSources } from "./folded-lengths.js";
4
+ import { getInterfaceVfuncRegistry, getVfuncRegistry } from "./registry.js";
5
+ function findClassVfuncDescriptor(klass, methodName) {
6
+ return walkClassChain(getParentClass(klass), (cls) => getVfuncRegistry(cls)?.[methodName]) ?? null;
7
+ }
8
+ function findInterfaceVfuncDescriptor(interfaceGtype, methodName) {
9
+ return getInterfaceVfuncRegistry(interfaceGtype)?.[methodName];
10
+ }
11
+ function vfuncArg(descriptor, isFoldedLength) {
12
+ if (descriptor.kind === "ref") {
13
+ return {
14
+ type: descriptor.innerDescriptor,
15
+ direction: descriptor.inout === true ? "inout" : "out",
16
+ isConsumed: isFoldedLength,
17
+ };
18
+ }
19
+ if (isCallerAllocatedOut(descriptor)) {
20
+ return { type: descriptor, direction: "out", isCallerAllocated: true };
21
+ }
22
+ return { type: descriptor };
23
+ }
24
+ function vfuncArgs(descriptor) {
25
+ const lengths = foldedLengthSources(descriptor);
26
+ return descriptor.argDescriptors.map((argDescriptor, index) => vfuncArg(argDescriptor, lengths.has(index)));
27
+ }
28
+ export { findClassVfuncDescriptor, findInterfaceVfuncDescriptor, vfuncArgs };
29
+ //# sourceMappingURL=vfunc.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vfunc.js","sourceRoot":"","sources":["../src/vfunc.ts"],"names":[],"mappings":"AACA,OAAO,EAAiB,cAAc,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAE5E,OAAO,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AACrD,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAC1D,OAAO,EAAE,yBAAyB,EAAE,gBAAgB,EAAwB,MAAM,eAAe,CAAC;AAElG,SAAS,wBAAwB,CAAC,KAAe,EAAE,UAAkB;IACjE,OAAO,cAAc,CAAC,cAAc,CAAC,KAAK,CAAC,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,gBAAgB,CAAC,GAAG,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,IAAI,IAAI,CAAC;AACvG,CAAC;AAED,SAAS,4BAA4B,CAAC,cAAsB,EAAE,UAAkB;IAC5E,OAAO,yBAAyB,CAAC,cAAc,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC;AACnE,CAAC;AAED,SAAS,QAAQ,CAAC,UAAsB,EAAE,cAAuB;IAC7D,IAAI,UAAU,CAAC,IAAI,KAAK,KAAK,EAAE,CAAC;QAC5B,OAAO;YACH,IAAI,EAAE,UAAU,CAAC,eAAe;YAChC,SAAS,EAAE,UAAU,CAAC,KAAK,KAAK,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK;YACtD,UAAU,EAAE,cAAc;SAC7B,CAAC;IACN,CAAC;IAED,IAAI,oBAAoB,CAAC,UAAU,CAAC,EAAE,CAAC;QACnC,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,SAAS,EAAE,KAAK,EAAE,iBAAiB,EAAE,IAAI,EAAE,CAAC;IAC3E,CAAC;IAED,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC;AAChC,CAAC;AAED,SAAS,SAAS,CAAC,UAA2B;IAC1C,MAAM,OAAO,GAAG,mBAAmB,CAAC,UAAU,CAAC,CAAC;IAEhD,OAAO,UAAU,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,aAAa,EAAE,KAAK,EAAE,EAAE,CAAC,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AAChH,CAAC;AAED,OAAO,EAAE,wBAAwB,EAAE,4BAA4B,EAAE,SAAS,EAAE,CAAC","sourcesContent":["import type { Descriptor } from \"@gtkx/native\";\nimport { type AnyClass, getParentClass, walkClassChain } from \"@gtkx/utils\";\nimport type { Arg } from \"./arg.js\";\nimport { isCallerAllocatedOut } from \"./callback.js\";\nimport { foldedLengthSources } from \"./folded-lengths.js\";\nimport { getInterfaceVfuncRegistry, getVfuncRegistry, type VfuncDescriptor } from \"./registry.js\";\n\nfunction findClassVfuncDescriptor(klass: AnyClass, methodName: string): VfuncDescriptor | null {\n return walkClassChain(getParentClass(klass), (cls) => getVfuncRegistry(cls)?.[methodName]) ?? null;\n}\n\nfunction findInterfaceVfuncDescriptor(interfaceGtype: bigint, methodName: string): VfuncDescriptor | undefined {\n return getInterfaceVfuncRegistry(interfaceGtype)?.[methodName];\n}\n\nfunction vfuncArg(descriptor: Descriptor, isFoldedLength: boolean): Arg {\n if (descriptor.kind === \"ref\") {\n return {\n type: descriptor.innerDescriptor,\n direction: descriptor.inout === true ? \"inout\" : \"out\",\n isConsumed: isFoldedLength,\n };\n }\n\n if (isCallerAllocatedOut(descriptor)) {\n return { type: descriptor, direction: \"out\", isCallerAllocated: true };\n }\n\n return { type: descriptor };\n}\n\nfunction vfuncArgs(descriptor: VfuncDescriptor): Arg[] {\n const lengths = foldedLengthSources(descriptor);\n\n return descriptor.argDescriptors.map((argDescriptor, index) => vfuncArg(argDescriptor, lengths.has(index)));\n}\n\nexport { findClassVfuncDescriptor, findInterfaceVfuncDescriptor, vfuncArgs };\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gtkx/runtime",
3
- "version": "1.0.0-rc.4",
3
+ "version": "1.1.0",
4
4
  "description": "FFI runtime the generated GTKX bindings call into: type descriptors, GValue marshalling, signals, GObject lifecycle",
5
5
  "keywords": [
6
6
  "gtkx",
@@ -41,15 +41,25 @@
41
41
  "src",
42
42
  "!**/*.tsbuildinfo"
43
43
  ],
44
+ "nx": {
45
+ "targets": {
46
+ "lint": {
47
+ "options": {
48
+ "cwd": ".",
49
+ "command": "eslint {projectRoot}"
50
+ }
51
+ }
52
+ }
53
+ },
44
54
  "engines": {
45
55
  "node": ">=24"
46
56
  },
47
57
  "dependencies": {
48
- "@gtkx/native": "1.0.0-rc.4",
49
- "@gtkx/utils": "1.0.0-rc.4"
58
+ "@gtkx/native": "1.1.0",
59
+ "@gtkx/utils": "1.1.0"
50
60
  },
51
61
  "devDependencies": {
52
- "@gtkx/vitest": "1.0.0-rc.4"
62
+ "@gtkx/vitest": "1.1.0"
53
63
  },
54
64
  "scripts": {
55
65
  "release": "tsx ../../scripts/release-package.ts"
@@ -0,0 +1,126 @@
1
+ import { type AnyClass, getOrInsert } from "@gtkx/utils";
2
+ import { releaseDefaultApplication } from "./default-application.js";
3
+ import { registerClass } from "./register-class.js";
4
+ import { getClassType } from "./registry.js";
5
+ import { typeName } from "./type.js";
6
+ import { callParent } from "./vfunc-call.js";
7
+
8
+ type CommandLineResult = [boolean, string[], number];
9
+
10
+ /**
11
+ * The GIO application surface {@link createApplication} builds on, which `Gio.Application` and
12
+ * every subclass of it satisfies structurally.
13
+ */
14
+ type CommandLineApplication = {
15
+ /** Marks the application as quitting, so GLib's own `run` returns without driving a main loop. */
16
+ quit(): void;
17
+ };
18
+
19
+ type LocalCommandLineApplication = {
20
+ runLocalCommandLine(argv: string[]): CommandLineResult;
21
+ };
22
+
23
+ type ShutdownApplication = {
24
+ run(argv: string[]): number;
25
+ };
26
+
27
+ /** An application class together with the construct properties it accepts. */
28
+ type ApplicationClass<T extends CommandLineApplication, P> = AnyClass<T> & (new (props: P) => T);
29
+
30
+ const derivedClasses: WeakMap<AnyClass, AnyClass> = new WeakMap();
31
+ const derivedApplicationClasses: Set<AnyClass> = new Set();
32
+ const quitApplications: WeakSet<object> = new WeakSet();
33
+ const shuttingDownApplications: WeakSet<object> = new WeakSet();
34
+
35
+ const derivedTypeName = (base: AnyClass): string => `Gtkx${typeName(getClassType(base)) ?? base.name}`;
36
+
37
+ const buildApplicationClass = (base: AnyClass<CommandLineApplication>): AnyClass<CommandLineApplication> => {
38
+ class DerivedApplication extends base {
39
+ runLocalCommandLine(argv: string[]): CommandLineResult {
40
+ return this.vfuncLocalCommandLine(argv);
41
+ }
42
+
43
+ protected vfuncLocalCommandLine(argv: string[]): CommandLineResult {
44
+ if (shuttingDownApplications.has(this)) {
45
+ this.quit();
46
+
47
+ return [true, argv, 0];
48
+ }
49
+
50
+ return callParent(DerivedApplication, "vfuncLocalCommandLine", this, argv) as CommandLineResult;
51
+ }
52
+
53
+ protected vfuncNameLost(): boolean {
54
+ quitApplications.add(this);
55
+
56
+ return callParent(DerivedApplication, "vfuncNameLost", this) as boolean;
57
+ }
58
+
59
+ override quit(): void {
60
+ quitApplications.add(this);
61
+ super.quit();
62
+ }
63
+ }
64
+
65
+ const derived = registerClass(DerivedApplication, { typeName: derivedTypeName(base) });
66
+ derivedApplicationClasses.add(derived);
67
+
68
+ return derived;
69
+ };
70
+
71
+ const isDerivedApplication = (application: object): application is LocalCommandLineApplication => {
72
+ for (const derived of derivedApplicationClasses) {
73
+ if (application instanceof derived) {
74
+ return true;
75
+ }
76
+ }
77
+
78
+ return false;
79
+ };
80
+
81
+ const shutDownThroughRun = (application: ShutdownApplication): void => {
82
+ if (!isDerivedApplication(application) || quitApplications.has(application)) {
83
+ return;
84
+ }
85
+
86
+ shuttingDownApplications.add(application);
87
+
88
+ try {
89
+ application.run([]);
90
+ } finally {
91
+ shuttingDownApplications.delete(application);
92
+ }
93
+ };
94
+
95
+ const deriveApplicationClass = <T extends CommandLineApplication>(base: AnyClass<T>): AnyClass<T> =>
96
+ getOrInsert(derivedClasses, base, () => buildApplicationClass(base)) as AnyClass<T>;
97
+
98
+ /**
99
+ * Constructs an application GTKX can shut down. GLib parses an application's command line at most
100
+ * once per instance and crashes on a second parse, so {@link runApplication} and
101
+ * {@link quitApplication} only accept an application built here.
102
+ *
103
+ * Whatever `g_application_constructed` assigned to the process-wide default is handed straight back,
104
+ * so merely building an application never makes it what `Gio.Application.getDefault()` returns.
105
+ * {@link runApplication} is the only place that claims that default, which keeps it pointing at an
106
+ * application GTKX has actually started rather than at one that is still unregistered.
107
+ *
108
+ * @param base The application class to construct, such as `Gtk.Application`.
109
+ * @param props Construct properties, passed through unchanged.
110
+ * @returns An instance of a class derived from `base`, registered once per base class as its own GType.
111
+ */
112
+ const createApplication = <T extends CommandLineApplication, P>(base: ApplicationClass<T, P>, props: P): T => {
113
+ const application = new (deriveApplicationClass(base) as new (props: P) => T)(props);
114
+ releaseDefaultApplication(application);
115
+
116
+ return application;
117
+ };
118
+
119
+ export {
120
+ createApplication,
121
+ isDerivedApplication,
122
+ shutDownThroughRun,
123
+ type ApplicationClass,
124
+ type CommandLineApplication,
125
+ type LocalCommandLineApplication,
126
+ };
package/src/arg.ts CHANGED
@@ -16,5 +16,6 @@ const isOutputArg = (arg: Arg): boolean => arg.direction !== undefined;
16
16
  const isInoutArg = (arg: Arg): boolean => arg.direction === "inout";
17
17
  const isCallerAllocatedArg = (arg: Arg): boolean => arg.isCallerAllocated === true;
18
18
  const isRefArg = (arg: Arg): boolean => arg.direction !== undefined && arg.isCallerAllocated !== true;
19
+ const requiresInputArg = (arg: Arg): boolean => !isRefArg(arg) || isInoutArg(arg);
19
20
 
20
- export { isOutputArg, isInoutArg, isCallerAllocatedArg, isRefArg, type Arg };
21
+ export { isOutputArg, isInoutArg, isCallerAllocatedArg, isRefArg, requiresInputArg, type Arg };
package/src/bind.ts CHANGED
@@ -1,5 +1,16 @@
1
1
  import { call, type Descriptor, bind as nativeBind } from "@gtkx/native";
2
2
 
3
+ /**
4
+ * Precompiles a call to a C function, marshalling the values it is given through the argument
5
+ * descriptors and the result through the return one. The symbol is resolved in the shared library
6
+ * on the first call.
7
+ *
8
+ * @param sharedLibrary The shared library to resolve the symbol in.
9
+ * @param symbol The name of the C function to bind.
10
+ * @param argDescriptors Describe the C arguments, in declaration order.
11
+ * @param returnDescriptor Describes the C return value.
12
+ * @returns A function calling the bound symbol.
13
+ */
3
14
  function bind(
4
15
  sharedLibrary: string,
5
16
  symbol: string,
package/src/callback.ts CHANGED
@@ -1,13 +1,19 @@
1
1
  import { copy, type Descriptor } from "@gtkx/native";
2
2
  import type { CallbackDescriptor, RefDescriptor } from "./descriptors.js";
3
+ import { foldedLengthSources, type LengthSource, type LengthSources } from "./folded-lengths.js";
3
4
  import { fromNative, toNative } from "./native-value.js";
4
5
  import { getHandle } from "./registry.js";
5
6
  import { splitTupleResult } from "./tuple.js";
6
7
  import { copyValue } from "./value.js";
8
+ import { popSeedFrame, pushSeedFrame, type RefSeeds } from "./vfunc-seeds.js";
7
9
 
8
10
  type Callback = (...args: unknown[]) => unknown;
9
- type CallbackReceiver = "this" | "emitter" | "none";
10
- type OutParam = { value: unknown; descriptor: Descriptor };
11
+ type CallbackKind = "vfunc" | "signal" | "callback";
12
+ type CallbackTraits = { isInstanceBound: boolean; hasInstanceArg: boolean; hasFoldedLengths: boolean };
13
+ type OutParam = { value: unknown; descriptor: Descriptor; argIndex: number };
14
+ type LengthLink = { target: OutParam; sources: LengthSource[] };
15
+ type OutParamGroups = { lengthLinks: LengthLink[]; valueParams: OutParam[] };
16
+ type OutValues = Map<number, unknown>;
11
17
 
12
18
  type CallbackSpec = {
13
19
  argDescriptors: Descriptor[];
@@ -19,8 +25,18 @@ type CallbackPlan = {
19
25
  fn: Callback;
20
26
  effectiveTypes: Descriptor[];
21
27
  returnDescriptor: Descriptor;
28
+ hasPrimary: boolean;
22
29
  start: number;
23
- receiver: CallbackReceiver;
30
+ isInstanceBound: boolean;
31
+ lengthSources: LengthSources;
32
+ hasOutParams: boolean;
33
+ hasRefOutParams: boolean;
34
+ };
35
+
36
+ const CALLBACK_TRAITS: Record<CallbackKind, CallbackTraits> = {
37
+ callback: { isInstanceBound: false, hasInstanceArg: false, hasFoldedLengths: true },
38
+ signal: { isInstanceBound: false, hasInstanceArg: true, hasFoldedLengths: false },
39
+ vfunc: { isInstanceBound: true, hasInstanceArg: true, hasFoldedLengths: true },
24
40
  };
25
41
 
26
42
  const fillCallerAllocatedBuffer = (descriptor: Descriptor, target: object, source: object): void => {
@@ -46,23 +62,23 @@ const collectRefArg = (
46
62
  descriptor: RefDescriptor,
47
63
  wrappedValue: unknown,
48
64
  inputs: unknown[],
49
- outParams: OutParam[],
65
+ out: { params: OutParam[]; argIndex: number },
50
66
  ): void => {
51
67
  if (descriptor.inout === true) {
52
68
  inputs.push((wrappedValue as { value: unknown }).value);
53
69
  }
54
70
 
55
- outParams.push({ value: wrappedValue, descriptor });
71
+ out.params.push({ value: wrappedValue, descriptor, argIndex: out.argIndex });
56
72
  };
57
73
 
58
74
  const collectCallbackArg = (
59
75
  descriptor: Descriptor | undefined,
60
76
  wrappedValue: unknown,
61
77
  inputs: unknown[],
62
- outParams: OutParam[],
78
+ out: { params: OutParam[]; argIndex: number },
63
79
  ): void => {
64
80
  if (descriptor?.kind === "ref") {
65
- collectRefArg(descriptor, wrappedValue, inputs, outParams);
81
+ collectRefArg(descriptor, wrappedValue, inputs, out);
66
82
 
67
83
  return;
68
84
  }
@@ -70,7 +86,7 @@ const collectCallbackArg = (
70
86
  inputs.push(wrappedValue);
71
87
 
72
88
  if (descriptor !== undefined && isCallerAllocatedOut(descriptor)) {
73
- outParams.push({ value: wrappedValue, descriptor });
89
+ out.params.push({ value: wrappedValue, descriptor, argIndex: out.argIndex });
74
90
  }
75
91
  };
76
92
 
@@ -80,47 +96,167 @@ const partitionCallbackArgs = (
80
96
  start: number,
81
97
  ): { inputs: unknown[]; outParams: OutParam[] } => {
82
98
  const inputs: unknown[] = [];
83
- const outParams: OutParam[] = [];
99
+ const params: OutParam[] = [];
100
+ const collected = { params, argIndex: start };
84
101
 
85
102
  for (let i = start; i < effectiveTypes.length; i++) {
86
- collectCallbackArg(effectiveTypes[i], wrapped[i], inputs, outParams);
103
+ collected.argIndex = i;
104
+ collectCallbackArg(effectiveTypes[i], wrapped[i], inputs, collected);
105
+ }
106
+
107
+ return { inputs, outParams: params };
108
+ };
109
+
110
+ const hasOutParamArg = (descriptor: Descriptor | undefined): boolean =>
111
+ descriptor !== undefined && (descriptor.kind === "ref" || isCallerAllocatedOut(descriptor));
112
+
113
+ const hasRefOutParamArg = (descriptor: Descriptor | undefined): boolean =>
114
+ descriptor?.kind === "ref" && descriptor.inout !== true;
115
+
116
+ const haveOutParamArgs = (effectiveTypes: Descriptor[], start: number): boolean =>
117
+ effectiveTypes.slice(start).some((descriptor) => hasOutParamArg(descriptor));
118
+
119
+ const haveRefOutParamArgs = (effectiveTypes: Descriptor[], start: number): boolean =>
120
+ effectiveTypes.slice(start).some((descriptor) => hasRefOutParamArg(descriptor));
121
+
122
+ const groupOutParams = (outParams: OutParam[], lengthSources: LengthSources): OutParamGroups => {
123
+ const groups: OutParamGroups = { lengthLinks: [], valueParams: [] };
124
+
125
+ for (const outParam of outParams) {
126
+ const sources = lengthSources.get(outParam.argIndex);
127
+
128
+ if (sources === undefined) {
129
+ groups.valueParams.push(outParam);
130
+ } else {
131
+ groups.lengthLinks.push({ target: outParam, sources });
132
+ }
133
+ }
134
+
135
+ return groups;
136
+ };
137
+
138
+ const getLength = (value: unknown): number => {
139
+ const length = (value as { length?: unknown } | null | undefined)?.length;
140
+
141
+ return typeof length === "number" ? length : 0;
142
+ };
143
+
144
+ const lengthSourceValue = (source: LengthSource, outValues: OutValues, primary: unknown): unknown =>
145
+ source.kind === "return" ? primary : outValues.get(source.argIndex);
146
+
147
+ const foldedLength = (sources: LengthSource[], outValues: OutValues, primary: unknown): number =>
148
+ Math.min(...sources.map((source) => getLength(lengthSourceValue(source, outValues, primary))));
149
+
150
+ const resolveOutValues = (groups: OutParamGroups, values: unknown[], primary: unknown): OutValues => {
151
+ const outValues: OutValues = new Map();
152
+
153
+ for (const [position, outParam] of groups.valueParams.entries()) {
154
+ outValues.set(outParam.argIndex, values[position]);
155
+ }
156
+
157
+ for (const link of groups.lengthLinks) {
158
+ outValues.set(link.target.argIndex, foldedLength(link.sources, outValues, primary));
87
159
  }
88
160
 
89
- return { inputs, outParams };
161
+ return outValues;
90
162
  };
91
163
 
92
- const writeOutParams = (outParams: OutParam[], outValues: unknown[]): void => {
93
- for (const [position, outParam] of outParams.entries()) {
94
- const outValue = outValues[position];
164
+ const writeOutParams = (outParams: OutParam[], outValues: OutValues): void => {
165
+ for (const outParam of outParams) {
166
+ const { descriptor } = outParam;
167
+ const outValue = outValues.get(outParam.argIndex);
95
168
 
96
- if (outParam.descriptor.kind === "ref") {
97
- (outParam.value as { value: unknown }).value = outValue;
169
+ if (descriptor.kind === "ref") {
170
+ (outParam.value as { value: unknown }).value = toNative(descriptor.innerDescriptor, outValue);
98
171
  } else if (outValue != null && outParam.value != null) {
99
- fillCallerAllocatedBuffer(outParam.descriptor, outParam.value, outValue);
172
+ fillCallerAllocatedBuffer(descriptor, outParam.value, outValue);
100
173
  }
101
174
  }
102
175
  };
103
176
 
104
- const getThisArg = (receiver: CallbackReceiver, wrapped: unknown[]): unknown =>
105
- receiver === "this" ? (wrapped[0] ?? null) : null;
177
+ const collectRefSeeds = (outParams: OutParam[]): RefSeeds => {
178
+ const seeds: RefSeeds = new Map();
179
+
180
+ for (const outParam of outParams) {
181
+ const seed = (outParam.value as { value?: unknown } | null)?.value;
182
+
183
+ if (seed != null && hasRefOutParamArg(outParam.descriptor)) {
184
+ seeds.set(outParam.argIndex, seed);
185
+ }
186
+ }
187
+
188
+ return seeds;
189
+ };
190
+
191
+ const applyCallback = (plan: CallbackPlan, thisArg: unknown, inputs: unknown[], outParams: OutParam[]): unknown => {
192
+ if (!plan.hasRefOutParams) {
193
+ return plan.fn.apply(thisArg, inputs);
194
+ }
195
+
196
+ pushSeedFrame({ argDescriptors: plan.effectiveTypes, instance: thisArg, seeds: collectRefSeeds(outParams) });
197
+
198
+ try {
199
+ return plan.fn.apply(thisArg, inputs);
200
+ } finally {
201
+ popSeedFrame();
202
+ }
203
+ };
204
+
205
+ const getThisArg = (isInstanceBound: boolean, wrapped: unknown[]): unknown =>
206
+ isInstanceBound ? (wrapped[0] ?? null) : null;
207
+
208
+ const wrapCallbackArgs = (effectiveTypes: Descriptor[], rawArgs: unknown[]): void => {
209
+ let index = 0;
210
+
211
+ for (const descriptor of effectiveTypes) {
212
+ rawArgs[index] = fromNative(descriptor, rawArgs[index]);
213
+ index += 1;
214
+ }
215
+ };
216
+
217
+ const trimCallbackInputs = (plan: CallbackPlan, wrapped: unknown[]): unknown[] => {
218
+ const count = plan.effectiveTypes.length;
219
+
220
+ if (wrapped.length > count) {
221
+ wrapped.length = count;
222
+ }
223
+
224
+ for (let index = 0; index < plan.start; index++) {
225
+ wrapped.shift();
226
+ }
227
+
228
+ return wrapped;
229
+ };
230
+
231
+ const nativeReturn = (plan: CallbackPlan, primary: unknown): unknown =>
232
+ toNative(plan.returnDescriptor, plan.hasPrimary ? primary : undefined);
106
233
 
107
234
  const runCallback = (plan: CallbackPlan, rawArgs: unknown[]): unknown => {
108
- const { effectiveTypes, returnDescriptor } = plan;
109
- const wrapped = effectiveTypes.map((descriptor, i) => fromNative(descriptor, rawArgs[i]));
110
- const thisArg = getThisArg(plan.receiver, wrapped);
111
- const { inputs, outParams } = partitionCallbackArgs(effectiveTypes, wrapped, plan.start);
112
- const result = plan.fn.apply(thisArg, inputs);
235
+ const { effectiveTypes } = plan;
236
+ wrapCallbackArgs(effectiveTypes, rawArgs);
237
+ const thisArg = getThisArg(plan.isInstanceBound, rawArgs);
238
+
239
+ if (!plan.hasOutParams) {
240
+ return nativeReturn(plan, plan.fn.apply(thisArg, trimCallbackInputs(plan, rawArgs)));
241
+ }
242
+
243
+ const { inputs, outParams } = partitionCallbackArgs(effectiveTypes, rawArgs, plan.start);
244
+ const result = applyCallback(plan, thisArg, inputs, outParams);
113
245
 
114
246
  if (outParams.length === 0) {
115
- return toNative(returnDescriptor, result);
247
+ return nativeReturn(plan, result);
116
248
  }
117
249
 
118
- const { primary, outValues } = splitTupleResult(result, returnDescriptor.kind !== "void", outParams.length);
119
- writeOutParams(outParams, outValues);
250
+ const groups = groupOutParams(outParams, plan.lengthSources);
251
+ const { primary, outValues } = splitTupleResult(result, plan.hasPrimary, groups.valueParams.length);
252
+ writeOutParams(outParams, resolveOutValues(groups, outValues, primary));
120
253
 
121
- return toNative(returnDescriptor, primary);
254
+ return nativeReturn(plan, primary);
122
255
  };
123
256
 
257
+ const planLengthSources = (spec: CallbackSpec, hasFoldedLengths: boolean): LengthSources =>
258
+ hasFoldedLengths ? foldedLengthSources(spec) : new Map<number, LengthSource[]>();
259
+
124
260
  const getEffectiveTypes = (spec: CallbackSpec): Descriptor[] => {
125
261
  const { userDataIndex } = spec;
126
262
 
@@ -132,18 +268,26 @@ const getEffectiveTypes = (spec: CallbackSpec): Descriptor[] => {
132
268
  };
133
269
 
134
270
  const wrapCallbackValue = (spec: CallbackDescriptor, callback: unknown): unknown =>
135
- callback == null ? callback : wrapCallback(callback as Callback, spec, "none");
271
+ callback == null ? callback : wrapCallback(callback as Callback, spec, "callback");
272
+
273
+ function wrapCallback(fn: Callback, spec: CallbackSpec, kind: CallbackKind): Callback {
274
+ const effectiveTypes = getEffectiveTypes(spec);
275
+ const { isInstanceBound, hasInstanceArg, hasFoldedLengths } = CALLBACK_TRAITS[kind];
276
+ const start = hasInstanceArg ? 1 : 0;
136
277
 
137
- function wrapCallback(fn: Callback, spec: CallbackSpec, receiver: CallbackReceiver): Callback {
138
278
  const plan: CallbackPlan = {
139
279
  fn,
140
- effectiveTypes: getEffectiveTypes(spec),
280
+ effectiveTypes,
141
281
  returnDescriptor: spec.returnDescriptor,
142
- start: receiver === "none" ? 0 : 1,
143
- receiver,
282
+ hasPrimary: spec.returnDescriptor.kind !== "void",
283
+ start,
284
+ isInstanceBound,
285
+ lengthSources: planLengthSources(spec, hasFoldedLengths),
286
+ hasOutParams: haveOutParamArgs(effectiveTypes, start),
287
+ hasRefOutParams: haveRefOutParamArgs(effectiveTypes, start),
144
288
  };
145
289
 
146
290
  return (...rawArgs: unknown[]): unknown => runCallback(plan, rawArgs);
147
291
  }
148
292
 
149
- export { wrapCallbackValue, wrapCallback };
293
+ export { isCallerAllocatedOut, wrapCallbackValue, wrapCallback };