@gtkx/runtime 1.0.0-rc.3 → 1.0.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 (141) hide show
  1. package/README.md +8 -9
  2. package/dist/application-class.d.ts +32 -0
  3. package/dist/application-class.d.ts.map +1 -0
  4. package/dist/application-class.js +68 -0
  5. package/dist/application-class.js.map +1 -0
  6. package/dist/arg.d.ts +9 -3
  7. package/dist/arg.d.ts.map +1 -1
  8. package/dist/arg.js +4 -3
  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 +2 -1
  15. package/dist/callback.d.ts.map +1 -1
  16. package/dist/callback.js +119 -21
  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/descriptors.d.ts +105 -6
  23. package/dist/descriptors.d.ts.map +1 -1
  24. package/dist/descriptors.js +56 -12
  25. package/dist/descriptors.js.map +1 -1
  26. package/dist/error.d.ts +7 -0
  27. package/dist/error.d.ts.map +1 -1
  28. package/dist/error.js +1 -1
  29. package/dist/error.js.map +1 -1
  30. package/dist/fn.d.ts +24 -3
  31. package/dist/fn.d.ts.map +1 -1
  32. package/dist/fn.js +70 -33
  33. package/dist/fn.js.map +1 -1
  34. package/dist/index.d.ts +29 -7
  35. package/dist/index.d.ts.map +1 -1
  36. package/dist/index.js +26 -4
  37. package/dist/index.js.map +1 -1
  38. package/dist/internal.d.ts +1 -0
  39. package/dist/internal.d.ts.map +1 -1
  40. package/dist/internal.js +1 -0
  41. package/dist/internal.js.map +1 -1
  42. package/dist/library.d.ts +2 -1
  43. package/dist/library.d.ts.map +1 -1
  44. package/dist/library.js +2 -1
  45. package/dist/library.js.map +1 -1
  46. package/dist/lifecycle.d.ts +52 -10
  47. package/dist/lifecycle.d.ts.map +1 -1
  48. package/dist/lifecycle.js +62 -18
  49. package/dist/lifecycle.js.map +1 -1
  50. package/dist/listeners.d.ts +6 -0
  51. package/dist/listeners.d.ts.map +1 -1
  52. package/dist/listeners.js +3 -1
  53. package/dist/listeners.js.map +1 -1
  54. package/dist/mixin.d.ts +16 -2
  55. package/dist/mixin.d.ts.map +1 -1
  56. package/dist/mixin.js +18 -6
  57. package/dist/mixin.js.map +1 -1
  58. package/dist/native-value.d.ts +8 -0
  59. package/dist/native-value.d.ts.map +1 -1
  60. package/dist/native-value.js +12 -3
  61. package/dist/native-value.js.map +1 -1
  62. package/dist/object.d.ts +34 -6
  63. package/dist/object.d.ts.map +1 -1
  64. package/dist/object.js +89 -25
  65. package/dist/object.js.map +1 -1
  66. package/dist/param-spec.d.ts +11 -0
  67. package/dist/param-spec.d.ts.map +1 -0
  68. package/dist/param-spec.js +149 -0
  69. package/dist/param-spec.js.map +1 -0
  70. package/dist/promisify.d.ts +3 -5
  71. package/dist/promisify.d.ts.map +1 -1
  72. package/dist/promisify.js +4 -3
  73. package/dist/promisify.js.map +1 -1
  74. package/dist/properties.d.ts +39 -7
  75. package/dist/properties.d.ts.map +1 -1
  76. package/dist/properties.js +305 -42
  77. package/dist/properties.js.map +1 -1
  78. package/dist/register-class.d.ts +119 -14
  79. package/dist/register-class.d.ts.map +1 -1
  80. package/dist/register-class.js +109 -80
  81. package/dist/register-class.js.map +1 -1
  82. package/dist/registry.d.ts +81 -17
  83. package/dist/registry.d.ts.map +1 -1
  84. package/dist/registry.js +72 -26
  85. package/dist/registry.js.map +1 -1
  86. package/dist/signal.d.ts +22 -3
  87. package/dist/signal.d.ts.map +1 -1
  88. package/dist/signal.js +64 -8
  89. package/dist/signal.js.map +1 -1
  90. package/dist/t.d.ts +44 -0
  91. package/dist/t.d.ts.map +1 -1
  92. package/dist/t.js.map +1 -1
  93. package/dist/type.d.ts +2 -2
  94. package/dist/type.d.ts.map +1 -1
  95. package/dist/type.js +2 -0
  96. package/dist/type.js.map +1 -1
  97. package/dist/value.d.ts +10 -10
  98. package/dist/value.d.ts.map +1 -1
  99. package/dist/value.js +44 -22
  100. package/dist/value.js.map +1 -1
  101. package/dist/vfunc-call.d.ts +37 -0
  102. package/dist/vfunc-call.d.ts.map +1 -0
  103. package/dist/vfunc-call.js +168 -0
  104. package/dist/vfunc-call.js.map +1 -0
  105. package/dist/vfunc-seeds.d.ts +12 -0
  106. package/dist/vfunc-seeds.d.ts.map +1 -0
  107. package/dist/vfunc-seeds.js +19 -0
  108. package/dist/vfunc-seeds.js.map +1 -0
  109. package/dist/vfunc.d.ts +8 -0
  110. package/dist/vfunc.d.ts.map +1 -0
  111. package/dist/vfunc.js +23 -0
  112. package/dist/vfunc.js.map +1 -0
  113. package/package.json +4 -4
  114. package/src/application-class.ts +116 -0
  115. package/src/arg.ts +11 -5
  116. package/src/bind.ts +11 -0
  117. package/src/callback.ts +163 -23
  118. package/src/closure.ts +136 -0
  119. package/src/descriptors.ts +137 -41
  120. package/src/error.ts +8 -1
  121. package/src/fn.ts +104 -39
  122. package/src/index.ts +36 -7
  123. package/src/internal.ts +1 -0
  124. package/src/library.ts +2 -1
  125. package/src/lifecycle.ts +99 -22
  126. package/src/listeners.ts +9 -1
  127. package/src/mixin.ts +27 -8
  128. package/src/native-value.ts +13 -3
  129. package/src/object.ts +128 -34
  130. package/src/param-spec.ts +235 -0
  131. package/src/promisify.ts +8 -8
  132. package/src/properties.ts +459 -45
  133. package/src/register-class.ts +329 -114
  134. package/src/registry.ts +150 -38
  135. package/src/signal.ts +101 -17
  136. package/src/t.ts +44 -0
  137. package/src/type.ts +3 -2
  138. package/src/value.ts +57 -19
  139. package/src/vfunc-call.ts +243 -0
  140. package/src/vfunc-seeds.ts +36 -0
  141. package/src/vfunc.ts +31 -0
package/src/callback.ts CHANGED
@@ -4,10 +4,13 @@ import { fromNative, toNative } from "./native-value.js";
4
4
  import { getHandle } from "./registry.js";
5
5
  import { splitTupleResult } from "./tuple.js";
6
6
  import { copyValue } from "./value.js";
7
+ import { popSeedFrame, pushSeedFrame, type RefSeeds } from "./vfunc-seeds.js";
7
8
 
8
9
  type Callback = (...args: unknown[]) => unknown;
9
10
  type CallbackReceiver = "this" | "emitter" | "none";
10
- type OutParam = { value: unknown; descriptor: Descriptor };
11
+ type OutParam = { value: unknown; descriptor: Descriptor; argIndex: number };
12
+ type LengthLink = { target: OutParam; sourceIndex: number };
13
+ type OutParamGroups = { lengthLinks: LengthLink[]; valueParams: OutParam[] };
11
14
 
12
15
  type CallbackSpec = {
13
16
  argDescriptors: Descriptor[];
@@ -21,6 +24,8 @@ type CallbackPlan = {
21
24
  returnDescriptor: Descriptor;
22
25
  start: number;
23
26
  receiver: CallbackReceiver;
27
+ hasOutParams: boolean;
28
+ hasRefOutParams: boolean;
24
29
  };
25
30
 
26
31
  const fillCallerAllocatedBuffer = (descriptor: Descriptor, target: object, source: object): void => {
@@ -40,29 +45,29 @@ const fillCallerAllocatedBuffer = (descriptor: Descriptor, target: object, sourc
40
45
  };
41
46
 
42
47
  const isCallerAllocatedOut = (descriptor: Descriptor): boolean =>
43
- (descriptor.kind === "boxed" || descriptor.kind === "struct") && descriptor.callerAllocated === true;
48
+ (descriptor.kind === "boxed" || descriptor.kind === "struct") && descriptor.isCallerAllocated === true;
44
49
 
45
50
  const collectRefArg = (
46
51
  descriptor: RefDescriptor,
47
52
  wrappedValue: unknown,
48
53
  inputs: unknown[],
49
- outParams: OutParam[],
54
+ out: { params: OutParam[]; argIndex: number },
50
55
  ): void => {
51
56
  if (descriptor.inout === true) {
52
57
  inputs.push((wrappedValue as { value: unknown }).value);
53
58
  }
54
59
 
55
- outParams.push({ value: wrappedValue, descriptor });
60
+ out.params.push({ value: wrappedValue, descriptor, argIndex: out.argIndex });
56
61
  };
57
62
 
58
63
  const collectCallbackArg = (
59
64
  descriptor: Descriptor | undefined,
60
65
  wrappedValue: unknown,
61
66
  inputs: unknown[],
62
- outParams: OutParam[],
67
+ out: { params: OutParam[]; argIndex: number },
63
68
  ): void => {
64
69
  if (descriptor?.kind === "ref") {
65
- collectRefArg(descriptor, wrappedValue, inputs, outParams);
70
+ collectRefArg(descriptor, wrappedValue, inputs, out);
66
71
 
67
72
  return;
68
73
  }
@@ -70,7 +75,7 @@ const collectCallbackArg = (
70
75
  inputs.push(wrappedValue);
71
76
 
72
77
  if (descriptor !== undefined && isCallerAllocatedOut(descriptor)) {
73
- outParams.push({ value: wrappedValue, descriptor });
78
+ out.params.push({ value: wrappedValue, descriptor, argIndex: out.argIndex });
74
79
  }
75
80
  };
76
81
 
@@ -80,43 +85,173 @@ const partitionCallbackArgs = (
80
85
  start: number,
81
86
  ): { inputs: unknown[]; outParams: OutParam[] } => {
82
87
  const inputs: unknown[] = [];
83
- const outParams: OutParam[] = [];
88
+ const params: OutParam[] = [];
89
+ const collected = { params, argIndex: start };
84
90
 
85
91
  for (let i = start; i < effectiveTypes.length; i++) {
86
- collectCallbackArg(effectiveTypes[i], wrapped[i], inputs, outParams);
92
+ collected.argIndex = i;
93
+ collectCallbackArg(effectiveTypes[i], wrapped[i], inputs, collected);
87
94
  }
88
95
 
89
- return { inputs, outParams };
96
+ return { inputs, outParams: params };
90
97
  };
91
98
 
92
- const writeOutParams = (outParams: OutParam[], outValues: unknown[]): void => {
99
+ const hasOutParamArg = (descriptor: Descriptor | undefined): boolean =>
100
+ descriptor !== undefined && (descriptor.kind === "ref" || isCallerAllocatedOut(descriptor));
101
+
102
+ const hasRefOutParamArg = (descriptor: Descriptor | undefined): boolean =>
103
+ descriptor?.kind === "ref" && descriptor.inout !== true;
104
+
105
+ const haveOutParamArgs = (effectiveTypes: Descriptor[], start: number): boolean =>
106
+ effectiveTypes.slice(start).some((descriptor) => hasOutParamArg(descriptor));
107
+
108
+ const haveRefOutParamArgs = (effectiveTypes: Descriptor[], start: number): boolean =>
109
+ effectiveTypes.slice(start).some((descriptor) => hasRefOutParamArg(descriptor));
110
+
111
+ const sizeParamIndexFor = (descriptor: Descriptor): number | undefined => {
112
+ if (descriptor.kind !== "ref") {
113
+ return undefined;
114
+ }
115
+
116
+ const { innerDescriptor } = descriptor;
117
+
118
+ return innerDescriptor.kind === "array" ? innerDescriptor.sizeParamIndex : undefined;
119
+ };
120
+
121
+ const lengthOutParamIndices = (outParams: OutParam[]): Map<number, number> => {
122
+ const indices: Map<number, number> = new Map();
123
+
124
+ for (const outParam of outParams) {
125
+ const sizeParamIndex = sizeParamIndexFor(outParam.descriptor);
126
+
127
+ if (sizeParamIndex !== undefined) {
128
+ indices.set(sizeParamIndex, outParam.argIndex);
129
+ }
130
+ }
131
+
132
+ return indices;
133
+ };
134
+
135
+ const writeOutParams = (outParams: OutParam[], outValues: unknown[]): Map<number, unknown> => {
136
+ const written: Map<number, unknown> = new Map();
137
+
93
138
  for (const [position, outParam] of outParams.entries()) {
94
139
  const outValue = outValues[position];
140
+ const { descriptor } = outParam;
141
+ written.set(outParam.argIndex, outValue);
95
142
 
96
- if (outParam.descriptor.kind === "ref") {
97
- (outParam.value as { value: unknown }).value = outValue;
143
+ if (descriptor.kind === "ref") {
144
+ (outParam.value as { value: unknown }).value = toNative(descriptor.innerDescriptor, outValue);
98
145
  } else if (outValue != null && outParam.value != null) {
99
- fillCallerAllocatedBuffer(outParam.descriptor, outParam.value, outValue);
146
+ fillCallerAllocatedBuffer(descriptor, outParam.value, outValue);
100
147
  }
101
148
  }
149
+
150
+ return written;
151
+ };
152
+
153
+ const groupOutParams = (outParams: OutParam[]): OutParamGroups => {
154
+ const lengths = lengthOutParamIndices(outParams);
155
+ const groups: OutParamGroups = { lengthLinks: [], valueParams: [] };
156
+
157
+ for (const outParam of outParams) {
158
+ const sourceIndex = lengths.get(outParam.argIndex);
159
+
160
+ if (sourceIndex === undefined) {
161
+ groups.valueParams.push(outParam);
162
+ } else {
163
+ groups.lengthLinks.push({ target: outParam, sourceIndex });
164
+ }
165
+ }
166
+
167
+ return groups;
168
+ };
169
+
170
+ const getFoldedLength = (source: unknown): number => {
171
+ const length = (source as { length?: unknown } | null | undefined)?.length;
172
+
173
+ return typeof length === "number" ? length : 0;
174
+ };
175
+
176
+ const writeFoldedLengths = (lengthLinks: LengthLink[], written: Map<number, unknown>): void => {
177
+ for (const link of lengthLinks) {
178
+ (link.target.value as { value: unknown }).value = getFoldedLength(written.get(link.sourceIndex));
179
+ }
180
+ };
181
+
182
+ const collectRefSeeds = (outParams: OutParam[]): RefSeeds => {
183
+ const seeds: RefSeeds = new Map();
184
+
185
+ for (const outParam of outParams) {
186
+ const seed = (outParam.value as { value?: unknown } | null)?.value;
187
+
188
+ if (seed != null && hasRefOutParamArg(outParam.descriptor)) {
189
+ seeds.set(outParam.argIndex, seed);
190
+ }
191
+ }
192
+
193
+ return seeds;
194
+ };
195
+
196
+ const applyCallback = (plan: CallbackPlan, thisArg: unknown, inputs: unknown[], outParams: OutParam[]): unknown => {
197
+ if (!plan.hasRefOutParams) {
198
+ return plan.fn.apply(thisArg, inputs);
199
+ }
200
+
201
+ pushSeedFrame({ argDescriptors: plan.effectiveTypes, instance: thisArg, seeds: collectRefSeeds(outParams) });
202
+
203
+ try {
204
+ return plan.fn.apply(thisArg, inputs);
205
+ } finally {
206
+ popSeedFrame();
207
+ }
102
208
  };
103
209
 
104
210
  const getThisArg = (receiver: CallbackReceiver, wrapped: unknown[]): unknown =>
105
211
  receiver === "this" ? (wrapped[0] ?? null) : null;
106
212
 
213
+ const wrapCallbackArgs = (effectiveTypes: Descriptor[], rawArgs: unknown[]): void => {
214
+ let index = 0;
215
+
216
+ for (const descriptor of effectiveTypes) {
217
+ rawArgs[index] = fromNative(descriptor, rawArgs[index]);
218
+ index += 1;
219
+ }
220
+ };
221
+
222
+ const trimCallbackInputs = (plan: CallbackPlan, wrapped: unknown[]): unknown[] => {
223
+ const count = plan.effectiveTypes.length;
224
+
225
+ if (wrapped.length > count) {
226
+ wrapped.length = count;
227
+ }
228
+
229
+ for (let index = 0; index < plan.start; index++) {
230
+ wrapped.shift();
231
+ }
232
+
233
+ return wrapped;
234
+ };
235
+
107
236
  const runCallback = (plan: CallbackPlan, rawArgs: unknown[]): unknown => {
108
237
  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);
238
+ wrapCallbackArgs(effectiveTypes, rawArgs);
239
+ const thisArg = getThisArg(plan.receiver, rawArgs);
240
+
241
+ if (!plan.hasOutParams) {
242
+ return toNative(returnDescriptor, plan.fn.apply(thisArg, trimCallbackInputs(plan, rawArgs)));
243
+ }
244
+
245
+ const { inputs, outParams } = partitionCallbackArgs(effectiveTypes, rawArgs, plan.start);
246
+ const result = applyCallback(plan, thisArg, inputs, outParams);
113
247
 
114
248
  if (outParams.length === 0) {
115
249
  return toNative(returnDescriptor, result);
116
250
  }
117
251
 
118
- const { primary, outValues } = splitTupleResult(result, returnDescriptor.kind !== "void", outParams.length);
119
- writeOutParams(outParams, outValues);
252
+ const { lengthLinks, valueParams } = groupOutParams(outParams);
253
+ const { primary, outValues } = splitTupleResult(result, returnDescriptor.kind !== "void", valueParams.length);
254
+ writeFoldedLengths(lengthLinks, writeOutParams(valueParams, outValues));
120
255
 
121
256
  return toNative(returnDescriptor, primary);
122
257
  };
@@ -135,15 +270,20 @@ const wrapCallbackValue = (spec: CallbackDescriptor, callback: unknown): unknown
135
270
  callback == null ? callback : wrapCallback(callback as Callback, spec, "none");
136
271
 
137
272
  function wrapCallback(fn: Callback, spec: CallbackSpec, receiver: CallbackReceiver): Callback {
273
+ const effectiveTypes = getEffectiveTypes(spec);
274
+ const start = receiver === "none" ? 0 : 1;
275
+
138
276
  const plan: CallbackPlan = {
139
277
  fn,
140
- effectiveTypes: getEffectiveTypes(spec),
278
+ effectiveTypes,
141
279
  returnDescriptor: spec.returnDescriptor,
142
- start: receiver === "none" ? 0 : 1,
280
+ start,
143
281
  receiver,
282
+ hasOutParams: haveOutParamArgs(effectiveTypes, start),
283
+ hasRefOutParams: haveRefOutParamArgs(effectiveTypes, start),
144
284
  };
145
285
 
146
286
  return (...rawArgs: unknown[]): unknown => runCallback(plan, rawArgs);
147
287
  }
148
288
 
149
- export { wrapCallbackValue, wrapCallback };
289
+ export { isCallerAllocatedOut, wrapCallbackValue, wrapCallback };
package/src/closure.ts ADDED
@@ -0,0 +1,136 @@
1
+ import { type ExternalObject, type Handle, read } from "@gtkx/native";
2
+ import { type AnyClass } from "@gtkx/utils";
3
+ import { bind } from "./bind.js";
4
+ import { boxedT, callbackT, sizedArrayT, structT, uint32T, uint64T, voidT } from "./descriptors.js";
5
+ import { CLOSURE_SIZE, LIB, VALUE_SIZE, VALUE_T } from "./library.js";
6
+ import { getClassType, getHandle, getWrapperClass, instanceClassName, wrapHandle } from "./registry.js";
7
+ import { resolveBoxedType, typeIsA } from "./type.js";
8
+ import { fromValue, getValueType, intoValue } from "./value.js";
9
+
10
+ /**
11
+ * Handler backing a `GClosure`. It receives the closure's parameters already converted from their
12
+ * `GValue`s, and its result is written back into the closure's return `GValue` when it has one.
13
+ *
14
+ * A parameter that is itself a `GValue`, such as the target a property binding transform fills in,
15
+ * arrives as the caller's own `GObject.Value` rather than a copy, so writing to it is how the
16
+ * handler produces that parameter's value. It stops being valid once the handler returns.
17
+ */
18
+ type ClosureCallback = (...args: never[]) => unknown;
19
+
20
+ const CCLOSURE_CALLBACK_OFFSET = CLOSURE_SIZE;
21
+ const N_PARAM_VALUES_INDEX = 2;
22
+ const MARSHAL_DATA_INDEX = 5;
23
+ const CLOSURE_T = boxedT("GClosure", { sharedLibrary: LIB, getTypeFnName: "g_closure_get_type" });
24
+
25
+ const OWNED_CLOSURE_T = boxedT("GClosure", {
26
+ ownership: "full",
27
+ sharedLibrary: LIB,
28
+ getTypeFnName: "g_closure_get_type",
29
+ });
30
+
31
+ const MARSHAL_VALUE_T = boxedT("GValue", {
32
+ sharedLibrary: LIB,
33
+ getTypeFnName: "g_value_get_type",
34
+ size: VALUE_SIZE,
35
+ isCallerAllocated: true,
36
+ });
37
+
38
+ const MARSHAL_T = callbackT(
39
+ [
40
+ CLOSURE_T,
41
+ MARSHAL_VALUE_T,
42
+ uint32T,
43
+ sizedArrayT(MARSHAL_VALUE_T, N_PARAM_VALUES_INDEX, "borrowed", VALUE_SIZE),
44
+ uint64T,
45
+ uint64T,
46
+ ],
47
+ voidT,
48
+ {
49
+ hasUserData: true,
50
+ userDataIndex: MARSHAL_DATA_INDEX,
51
+ hasDestroy: true,
52
+ destroyKind: "closureNotify",
53
+ scope: "notified",
54
+ },
55
+ );
56
+
57
+ const NESTED_VALUE_T = structT("borrowed");
58
+ const gCclosureNew = bind(LIB, "g_cclosure_new", [MARSHAL_T], OWNED_CLOSURE_T);
59
+ const gValueGetBoxed = bind(LIB, "g_value_get_boxed", [VALUE_T], NESTED_VALUE_T);
60
+ const gClosureRef = bind(LIB, "g_closure_ref", [CLOSURE_T], uint64T);
61
+ const gClosureSink = bind(LIB, "g_closure_sink", [CLOSURE_T], voidT);
62
+ const gClosureSetMarshal = bind(LIB, "g_closure_set_marshal", [CLOSURE_T, uint64T], voidT);
63
+
64
+ const isClosureInstance = (value: object): boolean =>
65
+ typeIsA(getClassType(value.constructor as AnyClass), resolveBoxedType(CLOSURE_T));
66
+
67
+ const getNestedValue = (param: ExternalObject<Handle>): object | null =>
68
+ wrapHandle(gValueGetBoxed(param) as ExternalObject<Handle> | null, getWrapperClass(resolveBoxedType(VALUE_T)));
69
+
70
+ const fromParamValue = (param: ExternalObject<Handle>): unknown =>
71
+ getValueType(param) === resolveBoxedType(VALUE_T) ? getNestedValue(param) : fromValue(param);
72
+
73
+ function describeValue(value: unknown): string {
74
+ if (value === null) {
75
+ return "null";
76
+ }
77
+
78
+ if (typeof value !== "object") {
79
+ return typeof value;
80
+ }
81
+
82
+ return instanceClassName(value);
83
+ }
84
+
85
+ function marshalFor(callback: ClosureCallback): (...args: unknown[]) => void {
86
+ return (_closure: unknown, returnValue: unknown, _count: unknown, paramValues: unknown): void => {
87
+ const values = paramValues as ExternalObject<Handle>[];
88
+ const args = values.map((value) => fromParamValue(value));
89
+ const result = (callback as (...values: unknown[]) => unknown)(...(args as never[]));
90
+
91
+ if (returnValue !== null) {
92
+ intoValue(returnValue as ExternalObject<Handle>, result);
93
+ }
94
+ };
95
+ }
96
+
97
+ function newClosure(callback: ClosureCallback): ExternalObject<Handle> {
98
+ const handle = gCclosureNew(marshalFor(callback)) as ExternalObject<Handle>;
99
+ const marshal = read(handle, uint64T, CCLOSURE_CALLBACK_OFFSET);
100
+ gClosureRef(handle);
101
+ gClosureSink(handle);
102
+ gClosureSetMarshal(handle, marshal);
103
+
104
+ return handle;
105
+ }
106
+
107
+ /**
108
+ * Marshals a value passed where a `GObject.Closure` is expected into a closure handle: a function
109
+ * becomes a new `GClosure` dispatching into it, and an existing closure yields its own handle.
110
+ * @param value Function to wrap, or an already-built closure.
111
+ * @throws {ClosureMarshalError} When the value is neither.
112
+ */
113
+ function toClosure(value: unknown): ExternalObject<Handle> {
114
+ if (typeof value === "function") {
115
+ return newClosure(value as ClosureCallback);
116
+ }
117
+
118
+ if (typeof value === "object" && value !== null && isClosureInstance(value)) {
119
+ return getHandle(value);
120
+ }
121
+
122
+ throw new ClosureMarshalError(`Cannot marshal ${describeValue(value)} into a GObject.Closure`);
123
+ }
124
+
125
+ /** Same as {@link toClosure}, but passes a null or undefined value through as no closure at all. */
126
+ function tryToClosure(value: unknown): ExternalObject<Handle> | undefined {
127
+ return value == null ? undefined : toClosure(value);
128
+ }
129
+
130
+ /** Thrown when a value passed where a `GObject.Closure` is expected cannot be marshalled into one. */
131
+ class ClosureMarshalError extends TypeError {
132
+ /** Name callers match on when the error is caught as a plain `TypeError`. */
133
+ public override name = "ClosureMarshalError";
134
+ }
135
+
136
+ export { type ClosureCallback, ClosureMarshalError, toClosure, tryToClosure };