@gtkx/runtime 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.
- package/README.md +2 -1
- package/dist/arg.d.ts +6 -1
- package/dist/arg.d.ts.map +1 -1
- package/dist/arg.js +2 -1
- package/dist/arg.js.map +1 -1
- package/dist/closure.d.ts.map +1 -1
- package/dist/closure.js +2 -11
- package/dist/closure.js.map +1 -1
- package/dist/descriptors.d.ts +21 -3
- package/dist/descriptors.d.ts.map +1 -1
- package/dist/descriptors.js +50 -21
- package/dist/descriptors.js.map +1 -1
- package/dist/fn.d.ts +2 -0
- package/dist/fn.d.ts.map +1 -1
- package/dist/fn.js +50 -10
- package/dist/fn.js.map +1 -1
- package/dist/index.d.ts +11 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +10 -4
- package/dist/index.js.map +1 -1
- package/dist/mixin.d.ts +2 -1
- package/dist/mixin.d.ts.map +1 -1
- package/dist/mixin.js +1 -1
- package/dist/mixin.js.map +1 -1
- package/dist/native-value.d.ts +9 -3
- package/dist/native-value.d.ts.map +1 -1
- package/dist/native-value.js +86 -9
- package/dist/native-value.js.map +1 -1
- package/dist/object.d.ts +14 -6
- package/dist/object.d.ts.map +1 -1
- package/dist/object.js +21 -10
- package/dist/object.js.map +1 -1
- package/dist/param-spec.d.ts +31 -1
- package/dist/param-spec.d.ts.map +1 -1
- package/dist/param-spec.js +66 -5
- package/dist/param-spec.js.map +1 -1
- package/dist/promisify.d.ts +13 -1
- package/dist/promisify.d.ts.map +1 -1
- package/dist/promisify.js +14 -1
- package/dist/promisify.js.map +1 -1
- package/dist/properties.d.ts +28 -1
- package/dist/properties.d.ts.map +1 -1
- package/dist/properties.js +139 -8
- package/dist/properties.js.map +1 -1
- package/dist/regex.d.ts +25 -0
- package/dist/regex.d.ts.map +1 -0
- package/dist/regex.js +59 -0
- package/dist/regex.js.map +1 -0
- package/dist/register-class.d.ts +154 -11
- package/dist/register-class.d.ts.map +1 -1
- package/dist/register-class.js +157 -19
- package/dist/register-class.js.map +1 -1
- package/dist/registry.d.ts +48 -4
- package/dist/registry.d.ts.map +1 -1
- package/dist/registry.js +147 -3
- package/dist/registry.js.map +1 -1
- package/dist/signal.d.ts +8 -1
- package/dist/signal.d.ts.map +1 -1
- package/dist/signal.js +41 -3
- package/dist/signal.js.map +1 -1
- package/dist/type.d.ts +2 -1
- package/dist/type.d.ts.map +1 -1
- package/dist/type.js +5 -1
- package/dist/type.js.map +1 -1
- package/dist/value.d.ts +48 -4
- package/dist/value.d.ts.map +1 -1
- package/dist/value.js +172 -16
- package/dist/value.js.map +1 -1
- package/dist/variant.d.ts +75 -26
- package/dist/variant.d.ts.map +1 -1
- package/dist/variant.js +44 -29
- package/dist/variant.js.map +1 -1
- package/package.json +4 -4
- package/src/arg.ts +6 -1
- package/src/closure.ts +2 -14
- package/src/descriptors.ts +74 -21
- package/src/fn.ts +73 -17
- package/src/index.ts +31 -4
- package/src/mixin.ts +1 -1
- package/src/native-value.ts +146 -14
- package/src/object.ts +23 -11
- package/src/param-spec.ts +94 -8
- package/src/promisify.ts +27 -1
- package/src/properties.ts +184 -5
- package/src/regex.ts +92 -0
- package/src/register-class.ts +446 -38
- package/src/registry.ts +219 -4
- package/src/signal.ts +77 -0
- package/src/type.ts +7 -0
- package/src/value.ts +268 -14
- package/src/variant.ts +184 -53
package/src/descriptors.ts
CHANGED
|
@@ -102,6 +102,11 @@ type CallbackOptions = {
|
|
|
102
102
|
hasUserData?: boolean;
|
|
103
103
|
/** Position of `user_data` among the callback's own arguments, dropped before the closure is called. */
|
|
104
104
|
userDataIndex?: number;
|
|
105
|
+
/**
|
|
106
|
+
* The callback's C signature ends with a `GError**`, which receives a `GError` built from
|
|
107
|
+
* whatever the JavaScript function throws while the callback returns its failure value.
|
|
108
|
+
*/
|
|
109
|
+
canThrow?: boolean;
|
|
105
110
|
/** Lifetime of the closure; defaults to `notified` when `hasDestroy` is set and `call` otherwise. */
|
|
106
111
|
scope?: CallbackDescriptor["scope"];
|
|
107
112
|
};
|
|
@@ -118,6 +123,12 @@ type ArrayOptions = {
|
|
|
118
123
|
fixedSize?: number | undefined;
|
|
119
124
|
/** Whether the array carries raw bytes, and so decodes to a `Uint8Array` rather than to numbers. */
|
|
120
125
|
isBytes?: boolean | undefined;
|
|
126
|
+
/**
|
|
127
|
+
* Whether the caller supplies the storage for a fixed-length out array: the runtime allocates
|
|
128
|
+
* a buffer of the element stride times the fixed element count, passes its pointer, and
|
|
129
|
+
* decodes the elements the callee wrote into it.
|
|
130
|
+
*/
|
|
131
|
+
isCallerAllocated?: boolean | undefined;
|
|
121
132
|
};
|
|
122
133
|
|
|
123
134
|
/** Where a cursor array's base buffer and total length come from. */
|
|
@@ -136,6 +147,8 @@ type FundamentalOptions = {
|
|
|
136
147
|
typeName?: string;
|
|
137
148
|
/** Class a decoded value is wrapped in, instead of the one registered for `typeName`. */
|
|
138
149
|
wrapperClass?: AnyClass;
|
|
150
|
+
/** The caller owns the storage the callee fills, so a decoded value is borrowed instead of acquired. */
|
|
151
|
+
isCallerAllocated?: boolean;
|
|
139
152
|
/** The value is embedded in the containing struct rather than reached through a pointer. */
|
|
140
153
|
isInline?: boolean;
|
|
141
154
|
};
|
|
@@ -194,6 +207,9 @@ const unicharT: UnicharDescriptor = { kind: "unichar" };
|
|
|
194
207
|
const bufferT: BufferDescriptor = { kind: "buffer" };
|
|
195
208
|
const fundamentalLifecycles: Map<string, FundamentalLifecycle> = new Map();
|
|
196
209
|
|
|
210
|
+
const isGtypeDescriptor = (descriptor: Descriptor): descriptor is TypeDescriptor =>
|
|
211
|
+
descriptor.kind === "biguint64" && "type" in descriptor;
|
|
212
|
+
|
|
197
213
|
/**
|
|
198
214
|
* Builds a descriptor for a C string, whose optional length sizes the caller-allocated buffer
|
|
199
215
|
* used when the string is passed by reference.
|
|
@@ -228,13 +244,25 @@ const enumT = (sharedLibrary: string, typeFnName: string, isSigned: boolean): En
|
|
|
228
244
|
isSigned,
|
|
229
245
|
});
|
|
230
246
|
|
|
231
|
-
/**
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
247
|
+
/**
|
|
248
|
+
* Builds a descriptor for a flags type, resolving its GType from the named `get_type` function.
|
|
249
|
+
* For flags without a registered GType, pass empty library and function names and supply `mask`,
|
|
250
|
+
* the union of all valid bits, which invalid combinations are rejected against.
|
|
251
|
+
*/
|
|
252
|
+
const flagsT = (sharedLibrary: string, typeFnName: string, isSigned: boolean, mask?: number): FlagsDescriptor => {
|
|
253
|
+
const result: FlagsDescriptor = {
|
|
254
|
+
kind: "flags",
|
|
255
|
+
sharedLibrary,
|
|
256
|
+
getTypeFnName: typeFnName,
|
|
257
|
+
isSigned,
|
|
258
|
+
};
|
|
259
|
+
|
|
260
|
+
if (mask !== undefined) {
|
|
261
|
+
result.mask = mask;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
return result;
|
|
265
|
+
};
|
|
238
266
|
|
|
239
267
|
const applyBoxedNames = (result: BoxedDescriptor, options: BoxedOptions): void => {
|
|
240
268
|
if (options.sharedLibrary !== undefined) {
|
|
@@ -330,6 +358,10 @@ const fundamentalT = (
|
|
|
330
358
|
result.wrapperClass = options.wrapperClass;
|
|
331
359
|
}
|
|
332
360
|
|
|
361
|
+
if (options.isCallerAllocated) {
|
|
362
|
+
result.isCallerAllocated = true;
|
|
363
|
+
}
|
|
364
|
+
|
|
333
365
|
if (options.isInline) {
|
|
334
366
|
result.isInline = true;
|
|
335
367
|
}
|
|
@@ -337,6 +369,20 @@ const fundamentalT = (
|
|
|
337
369
|
return result;
|
|
338
370
|
};
|
|
339
371
|
|
|
372
|
+
const applyArrayBounds = (result: ArrayDescriptor, options: ArrayOptions): void => {
|
|
373
|
+
if (options.baseParamIndex !== undefined) {
|
|
374
|
+
result.baseParamIndex = options.baseParamIndex;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
if (options.sizeParamIndex !== undefined) {
|
|
378
|
+
result.sizeParamIndex = options.sizeParamIndex;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
if (options.fixedSize !== undefined) {
|
|
382
|
+
result.fixedSize = options.fixedSize;
|
|
383
|
+
}
|
|
384
|
+
};
|
|
385
|
+
|
|
340
386
|
/** Builds a descriptor for an array of items in one of the supported container layouts. */
|
|
341
387
|
const arrayT = (
|
|
342
388
|
itemDescriptor: Descriptor,
|
|
@@ -346,24 +392,22 @@ const arrayT = (
|
|
|
346
392
|
): ArrayDescriptor => {
|
|
347
393
|
const result: ArrayDescriptor = { kind: "array", itemDescriptor, arrayKind, ownership };
|
|
348
394
|
|
|
349
|
-
if (options
|
|
350
|
-
result
|
|
395
|
+
if (options === undefined) {
|
|
396
|
+
return result;
|
|
351
397
|
}
|
|
352
398
|
|
|
353
|
-
|
|
354
|
-
result.baseParamIndex = options.baseParamIndex;
|
|
355
|
-
}
|
|
399
|
+
applyArrayBounds(result, options);
|
|
356
400
|
|
|
357
|
-
if (options
|
|
358
|
-
result.
|
|
401
|
+
if (options.elementSize !== undefined) {
|
|
402
|
+
result.elementSize = options.elementSize;
|
|
359
403
|
}
|
|
360
404
|
|
|
361
|
-
if (options
|
|
362
|
-
result.
|
|
405
|
+
if (options.isBytes === true) {
|
|
406
|
+
result.isBytes = true;
|
|
363
407
|
}
|
|
364
408
|
|
|
365
|
-
if (options
|
|
366
|
-
result.
|
|
409
|
+
if (options.isCallerAllocated === true) {
|
|
410
|
+
result.isCallerAllocated = true;
|
|
367
411
|
}
|
|
368
412
|
|
|
369
413
|
return result;
|
|
@@ -428,7 +472,7 @@ const fixedArrayT = (
|
|
|
428
472
|
options: ArrayOptions = {},
|
|
429
473
|
): ArrayDescriptor => arrayT(itemDescriptor, "fixed", ownership, { ...options, fixedSize });
|
|
430
474
|
|
|
431
|
-
const
|
|
475
|
+
const applyClosureLifetimeOptions = (result: CallbackDescriptor, options: CallbackOptions): void => {
|
|
432
476
|
if (options.hasDestroy !== undefined) {
|
|
433
477
|
result.hasDestroy = options.hasDestroy;
|
|
434
478
|
}
|
|
@@ -437,6 +481,14 @@ const applyClosureOptions = (result: CallbackDescriptor, options: CallbackOption
|
|
|
437
481
|
result.destroyKind = options.destroyKind;
|
|
438
482
|
}
|
|
439
483
|
|
|
484
|
+
if (options.scope !== undefined) {
|
|
485
|
+
result.scope = options.scope;
|
|
486
|
+
}
|
|
487
|
+
};
|
|
488
|
+
|
|
489
|
+
const applyClosureOptions = (result: CallbackDescriptor, options: CallbackOptions): void => {
|
|
490
|
+
applyClosureLifetimeOptions(result, options);
|
|
491
|
+
|
|
440
492
|
if (options.hasUserData !== undefined) {
|
|
441
493
|
result.hasUserData = options.hasUserData;
|
|
442
494
|
}
|
|
@@ -445,8 +497,8 @@ const applyClosureOptions = (result: CallbackDescriptor, options: CallbackOption
|
|
|
445
497
|
result.userDataIndex = options.userDataIndex;
|
|
446
498
|
}
|
|
447
499
|
|
|
448
|
-
if (options.
|
|
449
|
-
result.
|
|
500
|
+
if (options.canThrow !== undefined) {
|
|
501
|
+
result.canThrow = options.canThrow;
|
|
450
502
|
}
|
|
451
503
|
};
|
|
452
504
|
|
|
@@ -479,6 +531,7 @@ export {
|
|
|
479
531
|
bigint64T,
|
|
480
532
|
biguint64T,
|
|
481
533
|
gtypeT,
|
|
534
|
+
isGtypeDescriptor,
|
|
482
535
|
float32T,
|
|
483
536
|
float64T,
|
|
484
537
|
booleanT,
|
package/src/fn.ts
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
|
-
import type { CallDescriptor, Descriptor, ExternalObject, Ref } from "@gtkx/native";
|
|
1
|
+
import type { CallDescriptor, Descriptor, ExternalObject, Handle, Ref } from "@gtkx/native";
|
|
2
2
|
import { call, bind as nativeBind } from "@gtkx/native";
|
|
3
3
|
import type { RefSeeds } from "./vfunc-seeds.js";
|
|
4
|
-
import { type Arg, isCallerAllocatedArg, isOutputArg, isRefArg, requiresInputArg } from "./arg.js";
|
|
4
|
+
import { type Arg, isCallerAllocatedArg, isOutputArg, isRefArg, isUnpackedArg, requiresInputArg } from "./arg.js";
|
|
5
5
|
import { wrapCallbackValue } from "./callback.js";
|
|
6
|
-
import { boxedT, refT } from "./descriptors.js";
|
|
6
|
+
import { boxedT, isGtypeDescriptor, refT } from "./descriptors.js";
|
|
7
7
|
import { checkError } from "./error.js";
|
|
8
8
|
import { LIB } from "./library.js";
|
|
9
|
-
import { fromNative } from "./native-value.js";
|
|
9
|
+
import { fromNative, toNative } from "./native-value.js";
|
|
10
10
|
import { getHandle } from "./registry.js";
|
|
11
11
|
import { hasSurfacedPrimary, packTupleResult } from "./tuple.js";
|
|
12
|
+
import { TYPE_INVALID } from "./type.js";
|
|
13
|
+
import { fromValue, getValueType } from "./value.js";
|
|
12
14
|
|
|
13
15
|
/** The signature a native function is bound against. */
|
|
14
16
|
type FnSpec = {
|
|
@@ -29,6 +31,8 @@ type FnSpec = {
|
|
|
29
31
|
* architectures.
|
|
30
32
|
*/
|
|
31
33
|
fixedArgCount?: number;
|
|
34
|
+
/** The callee returns a `GValue`, surfaced as what it holds rather than as the value itself. */
|
|
35
|
+
isReturnUnpacked?: boolean;
|
|
32
36
|
};
|
|
33
37
|
|
|
34
38
|
type ArgSpec = {
|
|
@@ -36,13 +40,18 @@ type ArgSpec = {
|
|
|
36
40
|
index: number;
|
|
37
41
|
isRef: boolean;
|
|
38
42
|
isCallerAllocated: boolean;
|
|
43
|
+
isUnpacked: boolean;
|
|
39
44
|
requiresInput: boolean;
|
|
40
45
|
inputIndex: number;
|
|
41
46
|
isOutParam: boolean;
|
|
47
|
+
carriesGtype: boolean;
|
|
42
48
|
};
|
|
43
49
|
|
|
44
50
|
const NO_OUT_PARAMS: unknown[] = [];
|
|
45
51
|
|
|
52
|
+
const hasGtypeInput = (descriptor: Descriptor): boolean =>
|
|
53
|
+
isGtypeDescriptor(descriptor) || (descriptor.kind === "array" && isGtypeDescriptor(descriptor.itemDescriptor));
|
|
54
|
+
|
|
46
55
|
const buildNativeArgTypes = (args: Arg[], canThrow: boolean): Descriptor[] => {
|
|
47
56
|
const nativeArgTypes = args.map((argSpec) =>
|
|
48
57
|
argSpec.direction !== undefined && argSpec.isCallerAllocated !== true ? refT(argSpec.type) : argSpec.type,
|
|
@@ -70,9 +79,11 @@ const buildArgSpecs = (args: Arg[]): ArgSpec[] => {
|
|
|
70
79
|
index,
|
|
71
80
|
isRef,
|
|
72
81
|
isCallerAllocated: isCallerAllocatedArg(arg),
|
|
82
|
+
isUnpacked: isUnpackedArg(arg),
|
|
73
83
|
requiresInput,
|
|
74
84
|
inputIndex: requiresInput ? inputCursor++ : -1,
|
|
75
85
|
isOutParam,
|
|
86
|
+
carriesGtype: hasGtypeInput(arg.type),
|
|
76
87
|
};
|
|
77
88
|
});
|
|
78
89
|
};
|
|
@@ -83,8 +94,11 @@ const resolveCallerAllocated = (inputs: unknown[], inputIndex: number): unknown
|
|
|
83
94
|
return wrapper == null ? wrapper : getHandle(wrapper);
|
|
84
95
|
};
|
|
85
96
|
|
|
97
|
+
const inputValueFor = (spec: ArgSpec, inputs: unknown[]): unknown =>
|
|
98
|
+
spec.carriesGtype ? toNative(spec.arg.type, inputs[spec.inputIndex]) : inputs[spec.inputIndex];
|
|
99
|
+
|
|
86
100
|
const buildRefValue = (spec: ArgSpec, inputs: unknown[], seeds: RefSeeds | undefined): Ref => ({
|
|
87
|
-
value: spec.requiresInput ?
|
|
101
|
+
value: spec.requiresInput ? inputValueFor(spec, inputs) : (seeds?.get(spec.index) ?? null),
|
|
88
102
|
});
|
|
89
103
|
|
|
90
104
|
const buildNativeValue = (spec: ArgSpec, inputs: unknown[], seeds: RefSeeds | undefined): unknown => {
|
|
@@ -102,12 +116,30 @@ const buildNativeValue = (spec: ArgSpec, inputs: unknown[], seeds: RefSeeds | un
|
|
|
102
116
|
return wrapCallbackValue(arg.type, inputs[inputIndex]);
|
|
103
117
|
}
|
|
104
118
|
|
|
105
|
-
return inputs
|
|
119
|
+
return inputValueFor(spec, inputs);
|
|
106
120
|
};
|
|
107
121
|
|
|
108
122
|
const buildNativeValues = (plans: ArgSpec[], inputs: unknown[], seeds: RefSeeds | undefined): unknown[] =>
|
|
109
123
|
plans.map((plan) => buildNativeValue(plan, inputs, seeds));
|
|
110
124
|
|
|
125
|
+
const unpackValueHandle = (handle: ExternalObject<Handle> | null): unknown => {
|
|
126
|
+
if (handle === null || getValueType(handle) === TYPE_INVALID) {
|
|
127
|
+
return null;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return fromValue(handle);
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
const readCallerAllocated = (plan: ArgSpec, inputs: unknown[]): unknown => {
|
|
134
|
+
const wrapper = inputs[plan.inputIndex];
|
|
135
|
+
|
|
136
|
+
if (!plan.isUnpacked) {
|
|
137
|
+
return wrapper;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return unpackValueHandle(wrapper == null ? null : getHandle(wrapper));
|
|
141
|
+
};
|
|
142
|
+
|
|
111
143
|
const readOutParams = (outPlans: ArgSpec[], inputs: unknown[], nativeValues: unknown[]): unknown[] => {
|
|
112
144
|
if (outPlans.length === 0) {
|
|
113
145
|
return NO_OUT_PARAMS;
|
|
@@ -115,7 +147,7 @@ const readOutParams = (outPlans: ArgSpec[], inputs: unknown[], nativeValues: unk
|
|
|
115
147
|
|
|
116
148
|
return Array.from(outPlans, (plan) =>
|
|
117
149
|
plan.isCallerAllocated
|
|
118
|
-
? inputs
|
|
150
|
+
? readCallerAllocated(plan, inputs)
|
|
119
151
|
: fromNative(plan.arg.type, (nativeValues[plan.index] as Ref).value));
|
|
120
152
|
};
|
|
121
153
|
|
|
@@ -123,8 +155,20 @@ const isPassThroughPlan = (plan: ArgSpec, index: number): boolean =>
|
|
|
123
155
|
plan.inputIndex === index &&
|
|
124
156
|
!plan.isRef &&
|
|
125
157
|
!plan.isCallerAllocated &&
|
|
158
|
+
!plan.carriesGtype &&
|
|
126
159
|
plan.arg.type.kind !== "callback";
|
|
127
160
|
|
|
161
|
+
const requiredInputIndices = (plans: ArgSpec[]): number[] =>
|
|
162
|
+
plans.filter((plan) => plan.requiresInput && plan.arg.isRequired === true).map((plan) => plan.inputIndex);
|
|
163
|
+
|
|
164
|
+
const assertRequiredInputs = (requiredIndices: number[], inputs: unknown[]): void => {
|
|
165
|
+
for (const index of requiredIndices) {
|
|
166
|
+
if (inputs[index] === undefined) {
|
|
167
|
+
throw new TypeError(`Missing required argument at position ${String(index + 1)}`);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
};
|
|
171
|
+
|
|
128
172
|
const resizeInputs = (inputs: unknown[], argCount: number): unknown[] => {
|
|
129
173
|
while (inputs.length < argCount) {
|
|
130
174
|
inputs.push(undefined);
|
|
@@ -137,14 +181,25 @@ const resizeInputs = (inputs: unknown[], argCount: number): unknown[] => {
|
|
|
137
181
|
return inputs;
|
|
138
182
|
};
|
|
139
183
|
|
|
184
|
+
const returnReader = (spec: FnSpec): ((nativeResult: unknown) => unknown) =>
|
|
185
|
+
spec.isReturnUnpacked === true
|
|
186
|
+
? (nativeResult) => unpackValueHandle((nativeResult ?? null) as ExternalObject<Handle> | null)
|
|
187
|
+
: (nativeResult) => fromNative(spec.returns, nativeResult);
|
|
188
|
+
|
|
140
189
|
const directCallable = (
|
|
141
190
|
descriptor: ExternalObject<CallDescriptor>,
|
|
142
|
-
|
|
191
|
+
readReturn: (nativeResult: unknown) => unknown,
|
|
143
192
|
hasPrimary: boolean,
|
|
144
|
-
|
|
193
|
+
plans: ArgSpec[],
|
|
145
194
|
): ((...inputs: unknown[]) => unknown) => {
|
|
146
|
-
const
|
|
147
|
-
|
|
195
|
+
const argCount = plans.length;
|
|
196
|
+
const requiredIndices = requiredInputIndices(plans);
|
|
197
|
+
|
|
198
|
+
const marshal = (inputs: unknown[]): unknown => {
|
|
199
|
+
assertRequiredInputs(requiredIndices, inputs);
|
|
200
|
+
|
|
201
|
+
return readReturn(call(descriptor, resizeInputs(inputs, argCount)));
|
|
202
|
+
};
|
|
148
203
|
|
|
149
204
|
if (hasPrimary) {
|
|
150
205
|
return (...inputs) => marshal(inputs);
|
|
@@ -165,20 +220,20 @@ function fromNativeCallable(
|
|
|
165
220
|
const plans = buildArgSpecs(args);
|
|
166
221
|
const outPlans = plans.filter((plan) => plan.isOutParam);
|
|
167
222
|
const arePassThrough = plans.every((plan, index) => isPassThroughPlan(plan, index));
|
|
223
|
+
const readReturn = returnReader(spec);
|
|
168
224
|
|
|
169
225
|
if (!canThrow && arePassThrough) {
|
|
170
|
-
return directCallable(descriptor,
|
|
226
|
+
return directCallable(descriptor, readReturn, hasPrimary, plans);
|
|
171
227
|
}
|
|
172
228
|
|
|
229
|
+
const requiredIndices = requiredInputIndices(plans);
|
|
230
|
+
|
|
173
231
|
const shape = (inputs: unknown[], nativeValues: unknown[], nativeResult: unknown): unknown =>
|
|
174
|
-
packTupleResult(
|
|
175
|
-
readOutParams(outPlans, inputs, nativeValues),
|
|
176
|
-
fromNative(returnDescriptor, nativeResult),
|
|
177
|
-
hasPrimary,
|
|
178
|
-
);
|
|
232
|
+
packTupleResult(readOutParams(outPlans, inputs, nativeValues), readReturn(nativeResult), hasPrimary);
|
|
179
233
|
|
|
180
234
|
if (canThrow) {
|
|
181
235
|
return (...inputs) => {
|
|
236
|
+
assertRequiredInputs(requiredIndices, inputs);
|
|
182
237
|
const nativeValues = buildNativeValues(plans, inputs, getRefSeeds?.());
|
|
183
238
|
const errorRef: Ref = { value: null };
|
|
184
239
|
nativeValues.push(errorRef);
|
|
@@ -190,6 +245,7 @@ function fromNativeCallable(
|
|
|
190
245
|
}
|
|
191
246
|
|
|
192
247
|
return (...inputs) => {
|
|
248
|
+
assertRequiredInputs(requiredIndices, inputs);
|
|
193
249
|
const nativeValues = buildNativeValues(plans, inputs, getRefSeeds?.());
|
|
194
250
|
|
|
195
251
|
return shape(inputs, nativeValues, call(descriptor, nativeValues));
|
package/src/index.ts
CHANGED
|
@@ -24,20 +24,30 @@ export {
|
|
|
24
24
|
setObjectProperty,
|
|
25
25
|
} from "./object.js";
|
|
26
26
|
/** @public */
|
|
27
|
-
export {
|
|
27
|
+
export { getParamSpecFlags, getParamSpecOwnerType, getParamSpecValueType } from "./param-spec.js";
|
|
28
28
|
/** @public */
|
|
29
|
-
export {
|
|
29
|
+
export { promisify, trimFinish } from "./promisify.js";
|
|
30
|
+
/** @public */
|
|
31
|
+
export { coerceObjectProperty, newParamSpecOverride } from "./properties.js";
|
|
32
|
+
/** @public */
|
|
33
|
+
export { matchAllRegex, matchRegex } from "./regex.js";
|
|
34
|
+
/** @public */
|
|
35
|
+
export { type Interface, registerClass, type SignalGType, type SignalSpec } from "./register-class.js";
|
|
30
36
|
/** @public */
|
|
31
37
|
export {
|
|
32
38
|
getClassType,
|
|
33
39
|
getHandle,
|
|
34
40
|
getInstanceType,
|
|
35
41
|
getWrapperClass,
|
|
42
|
+
peekTypeClass,
|
|
43
|
+
registerClassStruct,
|
|
36
44
|
registerInterface,
|
|
37
45
|
registerWrapperClass,
|
|
46
|
+
registerWrapperClassResolver,
|
|
38
47
|
type StaticBase,
|
|
39
48
|
setHandle,
|
|
40
49
|
wrapHandle,
|
|
50
|
+
type WrapperClassResolver,
|
|
41
51
|
} from "./registry.js";
|
|
42
52
|
/** @public */
|
|
43
53
|
export { connectSignal, disconnectSignal, emitSignal, getSignalBaseName, type SignalHandler } from "./signal.js";
|
|
@@ -79,9 +89,26 @@ export {
|
|
|
79
89
|
valueIsA,
|
|
80
90
|
} from "./type.js";
|
|
81
91
|
/** @public */
|
|
82
|
-
export {
|
|
92
|
+
export {
|
|
93
|
+
fromValue,
|
|
94
|
+
getBoxedValue,
|
|
95
|
+
type JsValue,
|
|
96
|
+
setBoxedValue,
|
|
97
|
+
toValueHandle,
|
|
98
|
+
tryToValueHandle,
|
|
99
|
+
ValueMarshalError,
|
|
100
|
+
} from "./value.js";
|
|
83
101
|
/** @public */
|
|
84
|
-
export {
|
|
102
|
+
export {
|
|
103
|
+
type ByteArray,
|
|
104
|
+
type FromVariantOptions,
|
|
105
|
+
fromVariant,
|
|
106
|
+
type RecursiveFromVariantOptions,
|
|
107
|
+
type RecursiveVariantValue,
|
|
108
|
+
toVariant,
|
|
109
|
+
type VariantInput,
|
|
110
|
+
type VariantValue,
|
|
111
|
+
} from "./variant.js";
|
|
85
112
|
/** @public */
|
|
86
113
|
export { callParent, callVfunc } from "./vfunc-call.js";
|
|
87
114
|
/** @public */
|
package/src/mixin.ts
CHANGED
|
@@ -89,4 +89,4 @@ function insertMixinLayer(target: AnyClass, mixin: Mixin, inheritedNames: Set<st
|
|
|
89
89
|
Object.setPrototypeOf(target, layer);
|
|
90
90
|
}
|
|
91
91
|
|
|
92
|
-
export { insertMixinLayer, installMixins, type MixinReceiver, type Mixin };
|
|
92
|
+
export { copyLayerMembers, insertMixinLayer, installMixins, type MixinReceiver, type Mixin };
|
package/src/native-value.ts
CHANGED
|
@@ -1,10 +1,41 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import
|
|
3
|
-
|
|
1
|
+
import type { AnyClass } from "@gtkx/utils";
|
|
2
|
+
import {
|
|
3
|
+
bindFunctionPointer,
|
|
4
|
+
call,
|
|
5
|
+
type CallDescriptor,
|
|
6
|
+
type Descriptor,
|
|
7
|
+
type ExternalObject,
|
|
8
|
+
getType,
|
|
9
|
+
type Handle,
|
|
10
|
+
type Ref,
|
|
11
|
+
} from "@gtkx/native";
|
|
12
|
+
import {
|
|
13
|
+
type ArrayDescriptor,
|
|
14
|
+
boxedT,
|
|
15
|
+
type CallbackDescriptor,
|
|
16
|
+
type FundamentalDescriptor,
|
|
17
|
+
type HashTableDescriptor,
|
|
18
|
+
isGtypeDescriptor,
|
|
19
|
+
refT,
|
|
20
|
+
type StructDescriptor,
|
|
21
|
+
} from "./descriptors.js";
|
|
22
|
+
import { checkError } from "./error.js";
|
|
23
|
+
import { LIB } from "./library.js";
|
|
24
|
+
import {
|
|
25
|
+
coerceGType,
|
|
26
|
+
getHandle,
|
|
27
|
+
getWrapperClass,
|
|
28
|
+
resolveWrapperClass,
|
|
29
|
+
wrapCallScopedObject,
|
|
30
|
+
wrapFundamentalHandle,
|
|
31
|
+
wrapHandle,
|
|
32
|
+
wrapObject,
|
|
33
|
+
} from "./registry.js";
|
|
4
34
|
import { resolveDescriptorType } from "./type.js";
|
|
5
35
|
|
|
6
36
|
type MarshalledKind = "object" | "struct" | "boxed" | "fundamental" | "array" | "hashtable";
|
|
7
37
|
type MarshalledDescriptor = Extract<Descriptor, { kind: MarshalledKind }>;
|
|
38
|
+
type DecodedCallbackTarget = { fnPtr: bigint; userData?: bigint | undefined };
|
|
8
39
|
|
|
9
40
|
const MARSHALLED_KINDS: Set<Descriptor["kind"]> = new Set<MarshalledKind>([
|
|
10
41
|
"object",
|
|
@@ -15,6 +46,8 @@ const MARSHALLED_KINDS: Set<Descriptor["kind"]> = new Set<MarshalledKind>([
|
|
|
15
46
|
"hashtable",
|
|
16
47
|
]);
|
|
17
48
|
|
|
49
|
+
const NULL_POINTER = 0n;
|
|
50
|
+
|
|
18
51
|
function isMarshalledDescriptor(descriptor: Descriptor): descriptor is MarshalledDescriptor {
|
|
19
52
|
return MARSHALLED_KINDS.has(descriptor.kind);
|
|
20
53
|
}
|
|
@@ -32,18 +65,103 @@ function collectionFromNative(descriptor: ArrayDescriptor, value: unknown): unkn
|
|
|
32
65
|
}
|
|
33
66
|
|
|
34
67
|
function boxedFromNative(descriptor: Descriptor, value: unknown): unknown {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
68
|
+
if (value == null) {
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const handle = value as ExternalObject<Handle>;
|
|
73
|
+
const type = resolveDescriptorType(descriptor);
|
|
74
|
+
|
|
75
|
+
return wrapHandle(handle, getWrapperClass(type));
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function fundamentalWrapperClass(descriptor: FundamentalDescriptor, handle: ExternalObject<Handle>): AnyClass {
|
|
79
|
+
if (descriptor.wrapperClass !== undefined) {
|
|
80
|
+
return descriptor.wrapperClass;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const declaredType = resolveDescriptorType(descriptor);
|
|
84
|
+
|
|
85
|
+
return resolveWrapperClass(getType(handle, declaredType)) ?? getWrapperClass(declaredType);
|
|
38
86
|
}
|
|
39
87
|
|
|
40
88
|
function fundamentalFromNative(descriptor: FundamentalDescriptor, value: unknown): unknown {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
89
|
+
if (value == null) {
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const handle = value as ExternalObject<Handle>;
|
|
94
|
+
|
|
95
|
+
return wrapFundamentalHandle(handle, fundamentalWrapperClass(descriptor, handle));
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const errorRefDescriptor = (): Descriptor =>
|
|
99
|
+
refT(boxedT("GError", { ownership: "full", sharedLibrary: LIB, getTypeFnName: "g_error_get_type" }));
|
|
100
|
+
|
|
101
|
+
function decodedCallbackValues(
|
|
102
|
+
descriptor: CallbackDescriptor,
|
|
103
|
+
target: DecodedCallbackTarget,
|
|
104
|
+
inputs: unknown[],
|
|
105
|
+
): unknown[] {
|
|
106
|
+
const values: unknown[] = [];
|
|
107
|
+
let cursor = 0;
|
|
108
|
+
|
|
109
|
+
for (const [index, argDescriptor] of descriptor.argDescriptors.entries()) {
|
|
110
|
+
if (index === descriptor.userDataIndex) {
|
|
111
|
+
values.push(target.userData ?? NULL_POINTER);
|
|
112
|
+
} else {
|
|
113
|
+
values.push(toNative(argDescriptor, inputs[cursor]));
|
|
114
|
+
cursor += 1;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return values;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function decodedCallbackCallable(
|
|
122
|
+
descriptor: CallbackDescriptor,
|
|
123
|
+
target: DecodedCallbackTarget,
|
|
124
|
+
): (...inputs: unknown[]) => unknown {
|
|
125
|
+
const canThrow = descriptor.canThrow === true;
|
|
126
|
+
const isOneShot = descriptor.scope === "async";
|
|
127
|
+
const argDescriptors = canThrow ? [...descriptor.argDescriptors, errorRefDescriptor()] : descriptor.argDescriptors;
|
|
128
|
+
let bound: ExternalObject<CallDescriptor> | undefined;
|
|
129
|
+
let isSpent = false;
|
|
130
|
+
|
|
131
|
+
return (...inputs) => {
|
|
132
|
+
if (isSpent) {
|
|
133
|
+
throw new Error("An async-scoped callback was already invoked; its native caller has released it");
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
bound ??= bindFunctionPointer(target.fnPtr, argDescriptors, descriptor.returnDescriptor, "decoded callback");
|
|
137
|
+
const values = decodedCallbackValues(descriptor, target, inputs);
|
|
138
|
+
isSpent = isOneShot;
|
|
139
|
+
|
|
140
|
+
if (!canThrow) {
|
|
141
|
+
return fromNative(descriptor.returnDescriptor, call(bound, values));
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const errorRef: Ref = { value: null };
|
|
145
|
+
values.push(errorRef);
|
|
146
|
+
const result = call(bound, values);
|
|
147
|
+
checkError(errorRef);
|
|
148
|
+
|
|
149
|
+
return fromNative(descriptor.returnDescriptor, result);
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function callbackFromNative(descriptor: CallbackDescriptor, value: unknown): unknown {
|
|
154
|
+
if (value == null) {
|
|
155
|
+
return value;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const target = value as Partial<DecodedCallbackTarget>;
|
|
159
|
+
|
|
160
|
+
if (typeof target.fnPtr !== "bigint") {
|
|
161
|
+
return value;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
return decodedCallbackCallable(descriptor, { fnPtr: target.fnPtr, userData: target.userData });
|
|
47
165
|
}
|
|
48
166
|
|
|
49
167
|
function hashTableFromNative(descriptor: HashTableDescriptor, value: unknown): unknown {
|
|
@@ -64,12 +182,21 @@ function hashTableFromNative(descriptor: HashTableDescriptor, value: unknown): u
|
|
|
64
182
|
/**
|
|
65
183
|
* Converts a raw value returned from native code into its JavaScript form,
|
|
66
184
|
* wrapping object, struct, boxed, and fundamental handles and recursively
|
|
67
|
-
* converting arrays and hash tables according to the descriptor.
|
|
185
|
+
* converting arrays and hash tables according to the descriptor. A callback
|
|
186
|
+
* decoded from a native function pointer and its bound user data becomes a
|
|
187
|
+
* callable function that invokes the native callback; it stays valid only as
|
|
188
|
+
* long as the native caller keeps the pointer pair alive, which for an
|
|
189
|
+
* async-scoped callback means until the first invocation — calling one a
|
|
190
|
+
* second time throws instead of reaching the released native closure.
|
|
68
191
|
*
|
|
69
192
|
* @param descriptor Describes the native type of the value.
|
|
70
193
|
* @param value The raw native value to convert.
|
|
71
194
|
*/
|
|
72
195
|
function fromNative(descriptor: Descriptor, value: unknown): unknown {
|
|
196
|
+
if (descriptor.kind === "callback") {
|
|
197
|
+
return callbackFromNative(descriptor, value);
|
|
198
|
+
}
|
|
199
|
+
|
|
73
200
|
if (!isMarshalledDescriptor(descriptor)) {
|
|
74
201
|
return value;
|
|
75
202
|
}
|
|
@@ -113,13 +240,18 @@ function hashTableToNative(descriptor: HashTableDescriptor, value: unknown): unk
|
|
|
113
240
|
|
|
114
241
|
/**
|
|
115
242
|
* Converts a JavaScript value into the raw form native code expects, unwrapping
|
|
116
|
-
* object, struct, boxed, and fundamental wrappers back to their handles
|
|
243
|
+
* object, struct, boxed, and fundamental wrappers back to their handles,
|
|
244
|
+
* resolving a class passed for a GType to the GType it was registered under, and
|
|
117
245
|
* recursively converting arrays and maps according to the descriptor.
|
|
118
246
|
*
|
|
119
247
|
* @param descriptor Describes the native type to convert to.
|
|
120
248
|
* @param value The JavaScript value to convert.
|
|
121
249
|
*/
|
|
122
250
|
function toNative(descriptor: Descriptor, value: unknown): unknown {
|
|
251
|
+
if (isGtypeDescriptor(descriptor)) {
|
|
252
|
+
return coerceGType(value);
|
|
253
|
+
}
|
|
254
|
+
|
|
123
255
|
if (!isMarshalledDescriptor(descriptor)) {
|
|
124
256
|
return value;
|
|
125
257
|
}
|