@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.
- package/README.md +8 -9
- package/dist/application-class.d.ts +32 -0
- package/dist/application-class.d.ts.map +1 -0
- package/dist/application-class.js +68 -0
- package/dist/application-class.js.map +1 -0
- package/dist/arg.d.ts +9 -3
- package/dist/arg.d.ts.map +1 -1
- package/dist/arg.js +4 -3
- package/dist/arg.js.map +1 -1
- package/dist/bind.d.ts +11 -0
- package/dist/bind.d.ts.map +1 -1
- package/dist/bind.js +11 -0
- package/dist/bind.js.map +1 -1
- package/dist/callback.d.ts +2 -1
- package/dist/callback.d.ts.map +1 -1
- package/dist/callback.js +119 -21
- package/dist/callback.js.map +1 -1
- package/dist/closure.d.ts +26 -0
- package/dist/closure.d.ts.map +1 -0
- package/dist/closure.js +99 -0
- package/dist/closure.js.map +1 -0
- package/dist/descriptors.d.ts +105 -6
- package/dist/descriptors.d.ts.map +1 -1
- package/dist/descriptors.js +56 -12
- package/dist/descriptors.js.map +1 -1
- package/dist/error.d.ts +7 -0
- package/dist/error.d.ts.map +1 -1
- package/dist/error.js +1 -1
- package/dist/error.js.map +1 -1
- package/dist/fn.d.ts +24 -3
- package/dist/fn.d.ts.map +1 -1
- package/dist/fn.js +70 -33
- package/dist/fn.js.map +1 -1
- package/dist/index.d.ts +29 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +26 -4
- package/dist/index.js.map +1 -1
- package/dist/internal.d.ts +1 -0
- package/dist/internal.d.ts.map +1 -1
- package/dist/internal.js +1 -0
- package/dist/internal.js.map +1 -1
- package/dist/library.d.ts +2 -1
- package/dist/library.d.ts.map +1 -1
- package/dist/library.js +2 -1
- package/dist/library.js.map +1 -1
- package/dist/lifecycle.d.ts +52 -10
- package/dist/lifecycle.d.ts.map +1 -1
- package/dist/lifecycle.js +62 -18
- package/dist/lifecycle.js.map +1 -1
- package/dist/listeners.d.ts +6 -0
- package/dist/listeners.d.ts.map +1 -1
- package/dist/listeners.js +3 -1
- package/dist/listeners.js.map +1 -1
- package/dist/mixin.d.ts +16 -2
- package/dist/mixin.d.ts.map +1 -1
- package/dist/mixin.js +18 -6
- package/dist/mixin.js.map +1 -1
- package/dist/native-value.d.ts +8 -0
- package/dist/native-value.d.ts.map +1 -1
- package/dist/native-value.js +12 -3
- package/dist/native-value.js.map +1 -1
- package/dist/object.d.ts +34 -6
- package/dist/object.d.ts.map +1 -1
- package/dist/object.js +89 -25
- package/dist/object.js.map +1 -1
- package/dist/param-spec.d.ts +11 -0
- package/dist/param-spec.d.ts.map +1 -0
- package/dist/param-spec.js +149 -0
- package/dist/param-spec.js.map +1 -0
- package/dist/promisify.d.ts +3 -5
- package/dist/promisify.d.ts.map +1 -1
- package/dist/promisify.js +4 -3
- package/dist/promisify.js.map +1 -1
- package/dist/properties.d.ts +39 -7
- package/dist/properties.d.ts.map +1 -1
- package/dist/properties.js +305 -42
- package/dist/properties.js.map +1 -1
- package/dist/register-class.d.ts +119 -14
- package/dist/register-class.d.ts.map +1 -1
- package/dist/register-class.js +109 -80
- package/dist/register-class.js.map +1 -1
- package/dist/registry.d.ts +81 -17
- package/dist/registry.d.ts.map +1 -1
- package/dist/registry.js +72 -26
- package/dist/registry.js.map +1 -1
- package/dist/signal.d.ts +22 -3
- package/dist/signal.d.ts.map +1 -1
- package/dist/signal.js +64 -8
- package/dist/signal.js.map +1 -1
- package/dist/t.d.ts +44 -0
- package/dist/t.d.ts.map +1 -1
- package/dist/t.js.map +1 -1
- package/dist/type.d.ts +2 -2
- package/dist/type.d.ts.map +1 -1
- package/dist/type.js +2 -0
- package/dist/type.js.map +1 -1
- package/dist/value.d.ts +10 -10
- package/dist/value.d.ts.map +1 -1
- package/dist/value.js +44 -22
- package/dist/value.js.map +1 -1
- package/dist/vfunc-call.d.ts +37 -0
- package/dist/vfunc-call.d.ts.map +1 -0
- package/dist/vfunc-call.js +168 -0
- package/dist/vfunc-call.js.map +1 -0
- package/dist/vfunc-seeds.d.ts +12 -0
- package/dist/vfunc-seeds.d.ts.map +1 -0
- package/dist/vfunc-seeds.js +19 -0
- package/dist/vfunc-seeds.js.map +1 -0
- package/dist/vfunc.d.ts +8 -0
- package/dist/vfunc.d.ts.map +1 -0
- package/dist/vfunc.js +23 -0
- package/dist/vfunc.js.map +1 -0
- package/package.json +4 -4
- package/src/application-class.ts +116 -0
- package/src/arg.ts +11 -5
- package/src/bind.ts +11 -0
- package/src/callback.ts +163 -23
- package/src/closure.ts +136 -0
- package/src/descriptors.ts +137 -41
- package/src/error.ts +8 -1
- package/src/fn.ts +104 -39
- package/src/index.ts +36 -7
- package/src/internal.ts +1 -0
- package/src/library.ts +2 -1
- package/src/lifecycle.ts +99 -22
- package/src/listeners.ts +9 -1
- package/src/mixin.ts +27 -8
- package/src/native-value.ts +13 -3
- package/src/object.ts +128 -34
- package/src/param-spec.ts +235 -0
- package/src/promisify.ts +8 -8
- package/src/properties.ts +459 -45
- package/src/register-class.ts +329 -114
- package/src/registry.ts +150 -38
- package/src/signal.ts +101 -17
- package/src/t.ts +44 -0
- package/src/type.ts +3 -2
- package/src/value.ts +57 -19
- package/src/vfunc-call.ts +243 -0
- package/src/vfunc-seeds.ts +36 -0
- 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.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
67
|
+
out: { params: OutParam[]; argIndex: number },
|
|
63
68
|
): void => {
|
|
64
69
|
if (descriptor?.kind === "ref") {
|
|
65
|
-
collectRefArg(descriptor, wrappedValue, inputs,
|
|
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
|
-
|
|
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
|
|
88
|
+
const params: OutParam[] = [];
|
|
89
|
+
const collected = { params, argIndex: start };
|
|
84
90
|
|
|
85
91
|
for (let i = start; i < effectiveTypes.length; i++) {
|
|
86
|
-
|
|
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
|
|
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 (
|
|
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(
|
|
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
|
-
|
|
110
|
-
const thisArg = getThisArg(plan.receiver,
|
|
111
|
-
|
|
112
|
-
|
|
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 {
|
|
119
|
-
|
|
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
|
|
278
|
+
effectiveTypes,
|
|
141
279
|
returnDescriptor: spec.returnDescriptor,
|
|
142
|
-
start
|
|
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 };
|