@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/value.ts
CHANGED
|
@@ -9,16 +9,18 @@ import {
|
|
|
9
9
|
boxedT,
|
|
10
10
|
float32T,
|
|
11
11
|
float64T,
|
|
12
|
+
int8T,
|
|
12
13
|
int32T,
|
|
13
14
|
objectT,
|
|
14
15
|
stringT,
|
|
16
|
+
uint8T,
|
|
15
17
|
uint32T,
|
|
16
18
|
uint64T,
|
|
17
19
|
voidT,
|
|
18
20
|
} from "./descriptors.js";
|
|
19
21
|
import { LIB, PARAM_T, VALUE_SIZE, VALUE_T, VARIANT_T } from "./library.js";
|
|
20
22
|
import { toNative } from "./native-value.js";
|
|
21
|
-
import { getHandle, getWrapperClass, wrapHandle } from "./registry.js";
|
|
23
|
+
import { getHandle, getWrapperClass, wrapHandle, wrapObject } from "./registry.js";
|
|
22
24
|
import {
|
|
23
25
|
getStrvType,
|
|
24
26
|
isResolvableDescriptor,
|
|
@@ -28,6 +30,7 @@ import {
|
|
|
28
30
|
resolveType,
|
|
29
31
|
TYPE_BOOLEAN,
|
|
30
32
|
TYPE_BOXED,
|
|
33
|
+
TYPE_CHAR,
|
|
31
34
|
TYPE_DOUBLE,
|
|
32
35
|
TYPE_ENUM,
|
|
33
36
|
TYPE_FLAGS,
|
|
@@ -35,13 +38,17 @@ import {
|
|
|
35
38
|
TYPE_GTYPE,
|
|
36
39
|
TYPE_INT,
|
|
37
40
|
TYPE_INT64,
|
|
41
|
+
TYPE_INTERFACE,
|
|
38
42
|
TYPE_INVALID,
|
|
43
|
+
TYPE_LONG,
|
|
39
44
|
TYPE_OBJECT,
|
|
40
45
|
TYPE_PARAM,
|
|
41
46
|
TYPE_POINTER,
|
|
42
47
|
TYPE_STRING,
|
|
48
|
+
TYPE_UCHAR,
|
|
43
49
|
TYPE_UINT,
|
|
44
50
|
TYPE_UINT64,
|
|
51
|
+
TYPE_ULONG,
|
|
45
52
|
TYPE_VARIANT,
|
|
46
53
|
typeFundamental,
|
|
47
54
|
typeName,
|
|
@@ -53,6 +60,7 @@ type ValueType = {
|
|
|
53
60
|
};
|
|
54
61
|
|
|
55
62
|
type ValueGetter = (value: ExternalObject<Handle>) => unknown;
|
|
63
|
+
type ValueWriter = ValueType["set"];
|
|
56
64
|
|
|
57
65
|
const setBoxedCache = createBindCache();
|
|
58
66
|
const setStaticBoxedCache = createBindCache();
|
|
@@ -61,8 +69,12 @@ const gValueInit = bind(LIB, "g_value_init", [VALUE_T, biguint64T], voidT);
|
|
|
61
69
|
const gValueCopy = bind(LIB, "g_value_copy", [VALUE_T, VALUE_T], voidT);
|
|
62
70
|
const booleanValueType = bindValueType("boolean", booleanT);
|
|
63
71
|
const typeValueType = bindValueType("gtype", biguint64T);
|
|
72
|
+
const scharValueType = bindValueType("schar", int8T);
|
|
73
|
+
const ucharValueType = bindValueType("uchar", uint8T);
|
|
64
74
|
const intValueType = bindValueType("int", int32T);
|
|
65
75
|
const uintValueType = bindValueType("uint", uint32T);
|
|
76
|
+
const longValueType = bindValueType("long", bigint64T);
|
|
77
|
+
const ulongValueType = bindValueType("ulong", biguint64T);
|
|
66
78
|
const int64ValueType = bindValueType("int64", bigint64T);
|
|
67
79
|
const uint64ValueType = bindValueType("uint64", biguint64T);
|
|
68
80
|
const floatValueType = bindValueType("float", float32T);
|
|
@@ -101,8 +113,12 @@ const PLAIN_VALUE_TYPES: Partial<Record<Descriptor["kind"], ValueType>> = {
|
|
|
101
113
|
const PLAIN_VALUE_GETTERS: Map<bigint, ValueGetter> = new Map([
|
|
102
114
|
[TYPE_BOOLEAN, booleanValueType.get],
|
|
103
115
|
[TYPE_GTYPE, typeValueType.get],
|
|
116
|
+
[TYPE_CHAR, scharValueType.get],
|
|
117
|
+
[TYPE_UCHAR, ucharValueType.get],
|
|
104
118
|
[TYPE_INT, intValueType.get],
|
|
105
119
|
[TYPE_UINT, uintValueType.get],
|
|
120
|
+
[TYPE_LONG, longValueType.get],
|
|
121
|
+
[TYPE_ULONG, ulongValueType.get],
|
|
106
122
|
[TYPE_INT64, int64ValueType.get],
|
|
107
123
|
[TYPE_UINT64, uint64ValueType.get],
|
|
108
124
|
[TYPE_FLOAT, floatValueType.get],
|
|
@@ -114,8 +130,12 @@ const PLAIN_VALUE_GETTERS: Map<bigint, ValueGetter> = new Map([
|
|
|
114
130
|
const PLAIN_VALUE_SETTERS: Map<bigint, ValueType["set"]> = new Map([
|
|
115
131
|
[TYPE_BOOLEAN, booleanValueType.set],
|
|
116
132
|
[TYPE_GTYPE, typeValueType.set],
|
|
133
|
+
[TYPE_CHAR, scharValueType.set],
|
|
134
|
+
[TYPE_UCHAR, ucharValueType.set],
|
|
117
135
|
[TYPE_INT, intValueType.set],
|
|
118
136
|
[TYPE_UINT, uintValueType.set],
|
|
137
|
+
[TYPE_LONG, longValueType.set],
|
|
138
|
+
[TYPE_ULONG, ulongValueType.set],
|
|
119
139
|
[TYPE_INT64, int64ValueType.set],
|
|
120
140
|
[TYPE_UINT64, uint64ValueType.set],
|
|
121
141
|
[TYPE_FLOAT, floatValueType.set],
|
|
@@ -127,9 +147,11 @@ const PLAIN_VALUE_SETTERS: Map<bigint, ValueType["set"]> = new Map([
|
|
|
127
147
|
const WRAPPED_VALUE_SETTERS: Map<bigint, ValueType["set"]> = new Map([
|
|
128
148
|
[TYPE_STRING, setStringValue],
|
|
129
149
|
[TYPE_OBJECT, handleSetter(objectValueType)],
|
|
150
|
+
[TYPE_INTERFACE, handleSetter(objectValueType)],
|
|
130
151
|
[TYPE_PARAM, handleSetter(paramValueType)],
|
|
131
152
|
[TYPE_VARIANT, handleSetter(variantValueType)],
|
|
132
153
|
[TYPE_BOXED, setBoxedFromValue],
|
|
154
|
+
[TYPE_POINTER, setPointerValue],
|
|
133
155
|
]);
|
|
134
156
|
|
|
135
157
|
const getBoxedTypeName = (type: bigint): string => typeName(type) ?? "GBoxed";
|
|
@@ -189,7 +211,7 @@ function copyValue(dest: ExternalObject<Handle>, src: ExternalObject<Handle>): v
|
|
|
189
211
|
gValueCopy(src, dest);
|
|
190
212
|
}
|
|
191
213
|
|
|
192
|
-
const
|
|
214
|
+
const newValueForType = (type: bigint): ExternalObject<Handle> => {
|
|
193
215
|
const value = newValue();
|
|
194
216
|
gValueInit(value, type);
|
|
195
217
|
|
|
@@ -202,15 +224,15 @@ const newBoxedValue = (
|
|
|
202
224
|
resolveSetBind: (name: string) => ValueType["set"],
|
|
203
225
|
): ExternalObject<Handle> => {
|
|
204
226
|
const type = resolveDescriptorType(descriptor);
|
|
205
|
-
const value =
|
|
227
|
+
const value = newValueForType(type);
|
|
206
228
|
resolveSetBind(getBoxedTypeName(type))(value, getHandle(boxed));
|
|
207
229
|
|
|
208
230
|
return value;
|
|
209
231
|
};
|
|
210
232
|
|
|
211
233
|
/**
|
|
212
|
-
*
|
|
213
|
-
* class, or null when the
|
|
234
|
+
* Duplicates the boxed value held by a GValue and returns the copy wrapped in the
|
|
235
|
+
* class registered for its GType, or null when the GValue holds no boxed type.
|
|
214
236
|
*/
|
|
215
237
|
function getBoxedValue(value: ExternalObject<Handle>): object | null {
|
|
216
238
|
const type = getValueType(value);
|
|
@@ -276,8 +298,9 @@ const wrappedValueGetter = (fundamental: bigint): ValueGetter | undefined => {
|
|
|
276
298
|
case TYPE_STRING: {
|
|
277
299
|
return (value) => stringValueType.get(value) ?? null;
|
|
278
300
|
}
|
|
301
|
+
case TYPE_INTERFACE:
|
|
279
302
|
case TYPE_OBJECT: {
|
|
280
|
-
return (value) =>
|
|
303
|
+
return (value) => wrapObject(objectValueType.get(value));
|
|
281
304
|
}
|
|
282
305
|
case TYPE_PARAM: {
|
|
283
306
|
return (value) =>
|
|
@@ -309,6 +332,14 @@ function setStringValue(value: ExternalObject<Handle>, nativeValue: unknown): vo
|
|
|
309
332
|
stringValueType.set(value, nativeValue ?? null);
|
|
310
333
|
}
|
|
311
334
|
|
|
335
|
+
function setPointerValue(value: ExternalObject<Handle>, nativeValue: unknown): void {
|
|
336
|
+
if (nativeValue != null) {
|
|
337
|
+
throw new Error("G_TYPE_POINTER non-null values cannot be marshalled from JS");
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
pointerValueType.set(value, 0);
|
|
341
|
+
}
|
|
342
|
+
|
|
312
343
|
function handleSetter(target: ValueType): ValueType["set"] {
|
|
313
344
|
return (value, nativeValue) => {
|
|
314
345
|
target.set(value, nativeValue == null ? null : getHandle(nativeValue));
|
|
@@ -325,22 +356,26 @@ const resolveValueGetter = (fundamental: bigint): ValueGetter | undefined =>
|
|
|
325
356
|
const resolveValueSetter = (fundamental: bigint): ValueType["set"] | undefined =>
|
|
326
357
|
PLAIN_VALUE_SETTERS.get(fundamental) ?? WRAPPED_VALUE_SETTERS.get(fundamental);
|
|
327
358
|
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
359
|
+
function setStrvValue(value: ExternalObject<Handle>, jsValue: unknown): void {
|
|
360
|
+
strvValueType.set(value, jsValue ?? null);
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
function valueWriterFor(type: bigint): ValueWriter {
|
|
364
|
+
if (type === getStrvType()) {
|
|
365
|
+
return setStrvValue;
|
|
366
|
+
}
|
|
367
|
+
|
|
337
368
|
const set = resolveValueSetter(typeFundamental(type));
|
|
338
369
|
|
|
339
370
|
if (set === undefined) {
|
|
340
371
|
throw new Error(`Unsupported type for intoValue: ${typeName(type) ?? String(type)}`);
|
|
341
372
|
}
|
|
342
373
|
|
|
343
|
-
set
|
|
374
|
+
return set;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
function intoValue(value: ExternalObject<Handle>, jsValue: unknown): void {
|
|
378
|
+
valueWriterFor(getValueType(value))(value, jsValue);
|
|
344
379
|
}
|
|
345
380
|
|
|
346
381
|
const resolveNativeValue = (descriptor: Descriptor, value: unknown): unknown => {
|
|
@@ -364,7 +399,7 @@ const resolveValueGType = (descriptor: Descriptor, nativeValue: unknown): bigint
|
|
|
364
399
|
function toValue(descriptor: Descriptor, value: unknown): ExternalObject<Handle> {
|
|
365
400
|
const nativeValue = resolveNativeValue(descriptor, value);
|
|
366
401
|
const type = resolveValueGType(descriptor, nativeValue);
|
|
367
|
-
const gValue =
|
|
402
|
+
const gValue = newValueForType(type);
|
|
368
403
|
resolveValueType(descriptor).set(gValue, nativeValue);
|
|
369
404
|
|
|
370
405
|
return gValue;
|
|
@@ -387,7 +422,7 @@ function fromValue(value: ExternalObject<Handle>): unknown {
|
|
|
387
422
|
}
|
|
388
423
|
|
|
389
424
|
function newValueForDescriptor(descriptor: Descriptor): ExternalObject<Handle> {
|
|
390
|
-
return
|
|
425
|
+
return newValueForType(resolveDescriptorType(descriptor));
|
|
391
426
|
}
|
|
392
427
|
|
|
393
428
|
function outValueForDescriptor(
|
|
@@ -401,7 +436,7 @@ function outValueForDescriptor(
|
|
|
401
436
|
write(storage, descriptor, 0, initial);
|
|
402
437
|
}
|
|
403
438
|
|
|
404
|
-
const value =
|
|
439
|
+
const value = newValueForType(TYPE_POINTER);
|
|
405
440
|
pointerValueType.set(value, storage);
|
|
406
441
|
|
|
407
442
|
return { value, read: () => read(storage, descriptor, 0) };
|
|
@@ -424,7 +459,10 @@ export {
|
|
|
424
459
|
toValue,
|
|
425
460
|
fromValue,
|
|
426
461
|
newValueForDescriptor,
|
|
462
|
+
newValueForType,
|
|
427
463
|
outValueForDescriptor,
|
|
428
464
|
outValueForBoxedDescriptor,
|
|
429
465
|
inoutValueForBoxedDescriptor,
|
|
466
|
+
type ValueWriter,
|
|
467
|
+
valueWriterFor,
|
|
430
468
|
};
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
import type { BindVfuncOptions } from "@gtkx/native";
|
|
2
|
+
import type { AnyClass } from "@gtkx/utils";
|
|
3
|
+
import { bindVfunc } from "@gtkx/native";
|
|
4
|
+
import { type Arg, isCallerAllocatedArg, requiresInputArg } from "./arg.js";
|
|
5
|
+
import { buildNativeArgTypes, fromNativeCallable } from "./fn.js";
|
|
6
|
+
import { toNative } from "./native-value.js";
|
|
7
|
+
import {
|
|
8
|
+
getClassType,
|
|
9
|
+
getHandle,
|
|
10
|
+
getInterfaceVfuncRegistry,
|
|
11
|
+
getVfuncRegistry,
|
|
12
|
+
instanceClassName,
|
|
13
|
+
resolveWrapperType,
|
|
14
|
+
type VfuncDescriptor,
|
|
15
|
+
} from "./registry.js";
|
|
16
|
+
import { TYPE_INVALID, typeInterfaces, typeIsA, typeParent } from "./type.js";
|
|
17
|
+
import { type RefSeeds, seedsFor } from "./vfunc-seeds.js";
|
|
18
|
+
import { findClassVfuncDescriptor, findInterfaceVfuncDescriptor, vfuncArgs } from "./vfunc.js";
|
|
19
|
+
|
|
20
|
+
type Invoker = (instance: object, inputs: unknown[]) => unknown;
|
|
21
|
+
type InvokerCache = WeakMap<AnyClass, Map<string, Invoker>>;
|
|
22
|
+
type ResolvedSlot = { descriptor: VfuncDescriptor; interfaceType?: bigint };
|
|
23
|
+
|
|
24
|
+
const NO_BASELINE = -1;
|
|
25
|
+
|
|
26
|
+
const SEEDED_SLOTS: Record<string, RefSeeds> = {
|
|
27
|
+
"LayoutManagerClass.measure": new Map([[6, NO_BASELINE], [7, NO_BASELINE]]),
|
|
28
|
+
"WidgetClass.measure": new Map([[5, NO_BASELINE], [6, NO_BASELINE]]),
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const parentInvokers: InvokerCache = new WeakMap();
|
|
32
|
+
const vfuncInvokers: InvokerCache = new WeakMap();
|
|
33
|
+
|
|
34
|
+
function toNativeInput(arg: Arg, input: unknown): unknown {
|
|
35
|
+
return isCallerAllocatedArg(arg) ? input : toNative(arg.type, input);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function bindOptionsFor(
|
|
39
|
+
slot: ResolvedSlot,
|
|
40
|
+
instanceType: bigint | undefined,
|
|
41
|
+
args: Arg[],
|
|
42
|
+
label: string,
|
|
43
|
+
): BindVfuncOptions {
|
|
44
|
+
const { descriptor, interfaceType } = slot;
|
|
45
|
+
|
|
46
|
+
const options: BindVfuncOptions = {
|
|
47
|
+
byteOffset: descriptor.byteOffset,
|
|
48
|
+
label,
|
|
49
|
+
argDescriptors: buildNativeArgTypes(args, descriptor.canThrow === true),
|
|
50
|
+
returnDescriptor: descriptor.returnDescriptor,
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
if (descriptor.vtableSize !== undefined) {
|
|
54
|
+
options.vtableSize = descriptor.vtableSize;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (instanceType !== undefined) {
|
|
58
|
+
options.instanceType = instanceType;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (interfaceType !== undefined) {
|
|
62
|
+
options.interfaceType = interfaceType;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return options;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function buildInvoker(slot: ResolvedSlot, instanceType: bigint | undefined, caller: string): Invoker {
|
|
69
|
+
const { descriptor } = slot;
|
|
70
|
+
const args = vfuncArgs(descriptor);
|
|
71
|
+
const canThrow = descriptor.canThrow === true;
|
|
72
|
+
const label = `${descriptor.className}.${descriptor.vfuncName}`;
|
|
73
|
+
const [, ...inputArgs] = args.filter(requiresInputArg);
|
|
74
|
+
let pendingSeeds: RefSeeds | undefined;
|
|
75
|
+
|
|
76
|
+
const takeRefSeeds = (): RefSeeds | undefined => {
|
|
77
|
+
const seeds = pendingSeeds;
|
|
78
|
+
pendingSeeds = undefined;
|
|
79
|
+
|
|
80
|
+
return seeds;
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
const shaped = fromNativeCallable(
|
|
84
|
+
bindVfunc(bindOptionsFor(slot, instanceType, args, label)),
|
|
85
|
+
{ args, returns: descriptor.returnDescriptor, canThrow },
|
|
86
|
+
takeRefSeeds,
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
return (instance, inputs) => {
|
|
90
|
+
if (inputs.length !== inputArgs.length) {
|
|
91
|
+
throw new Error(
|
|
92
|
+
`${caller}: ${label} expects ${String(inputArgs.length)} arguments, ` +
|
|
93
|
+
`received ${String(inputs.length)}`,
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const nativeInputs = inputArgs.map((arg, index) => toNativeInput(arg, inputs[index]));
|
|
98
|
+
pendingSeeds = SEEDED_SLOTS[label] ?? seedsFor(descriptor.argDescriptors, instance);
|
|
99
|
+
|
|
100
|
+
return shaped(getHandle(instance), ...nativeInputs);
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function cachedInvoker(cache: InvokerCache, owner: AnyClass, key: string, build: () => Invoker): Invoker {
|
|
105
|
+
let byKey = cache.get(owner);
|
|
106
|
+
|
|
107
|
+
if (byKey === undefined) {
|
|
108
|
+
byKey = new Map();
|
|
109
|
+
cache.set(owner, byKey);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
let invoker = byKey.get(key);
|
|
113
|
+
|
|
114
|
+
if (invoker === undefined) {
|
|
115
|
+
invoker = build();
|
|
116
|
+
byKey.set(key, invoker);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return invoker;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function resolveParentSlot(klass: AnyClass, parentType: bigint, methodName: string): ResolvedSlot | undefined {
|
|
123
|
+
const classDescriptor = findClassVfuncDescriptor(klass, methodName);
|
|
124
|
+
|
|
125
|
+
if (classDescriptor) {
|
|
126
|
+
return { descriptor: classDescriptor };
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
for (const interfaceType of typeInterfaces(parentType)) {
|
|
130
|
+
const descriptor = findInterfaceVfuncDescriptor(interfaceType, methodName);
|
|
131
|
+
|
|
132
|
+
if (descriptor) {
|
|
133
|
+
return { descriptor, interfaceType };
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return undefined;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function resolveParentType(klass: AnyClass, methodName: string): bigint {
|
|
141
|
+
const gtype = getClassType(klass);
|
|
142
|
+
|
|
143
|
+
if (gtype === TYPE_INVALID) {
|
|
144
|
+
throw new Error(
|
|
145
|
+
`callParent: cannot call '${methodName}' because ${klass.name} was never passed to registerClass`,
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
return typeParent(gtype);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function buildParentInvoker(klass: AnyClass, methodName: string): Invoker {
|
|
153
|
+
const parentType = resolveParentType(klass, methodName);
|
|
154
|
+
const slot = resolveParentSlot(klass, parentType, methodName);
|
|
155
|
+
|
|
156
|
+
if (!slot) {
|
|
157
|
+
throw new Error(`callParent: ${klass.name} inherits no '${methodName}' vtable slot`);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
return buildInvoker(slot, parentType, "callParent");
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Calls the implementation of a virtual function that the parent type of `klass` provides, so an
|
|
165
|
+
* override can chain up to the behavior it replaces. `klass` is the class whose override is
|
|
166
|
+
* running, named lexically rather than taken from `instance`, so each level of a hierarchy reaches
|
|
167
|
+
* exactly one level up. Arguments and the return value follow the same convention the override
|
|
168
|
+
* itself uses: pure out parameters are left out and returned instead, and a slot with several
|
|
169
|
+
* outputs returns them as a tuple. Throws if `klass` was never registered, inherits no such slot,
|
|
170
|
+
* or the parent type leaves the slot empty.
|
|
171
|
+
*
|
|
172
|
+
* @param klass The registered class whose override is chaining up.
|
|
173
|
+
* @param methodName Name of the overridden method, such as `vfuncMeasure`.
|
|
174
|
+
* @param instance The instance the override was invoked on.
|
|
175
|
+
* @param inputs The arguments the override received.
|
|
176
|
+
*/
|
|
177
|
+
function callParent(klass: AnyClass, methodName: string, instance: object, ...inputs: unknown[]): unknown {
|
|
178
|
+
const invoker = cachedInvoker(parentInvokers, klass, methodName, () => buildParentInvoker(klass, methodName));
|
|
179
|
+
|
|
180
|
+
return invoker(instance, inputs);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function resolveOwnerSlot(owner: AnyClass, key: string): ResolvedSlot {
|
|
184
|
+
const classDescriptor = getVfuncRegistry(owner)?.[key];
|
|
185
|
+
|
|
186
|
+
if (classDescriptor !== undefined) {
|
|
187
|
+
return { descriptor: classDescriptor };
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const interfaceType = getClassType(owner);
|
|
191
|
+
const descriptor = getInterfaceVfuncRegistry(interfaceType)?.[key];
|
|
192
|
+
|
|
193
|
+
if (descriptor === undefined) {
|
|
194
|
+
throw new Error(`callVfunc: ${owner.name} declares no '${key}' vtable slot`);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
return { descriptor, interfaceType };
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
function requiresDefaultVtable(slot: ResolvedSlot, instanceType: bigint): boolean {
|
|
201
|
+
return slot.interfaceType !== undefined && !typeIsA(instanceType, slot.interfaceType);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function resolveInstanceType(key: string, instance: object, slot: ResolvedSlot): bigint | undefined {
|
|
205
|
+
const instanceType = resolveWrapperType(instance);
|
|
206
|
+
|
|
207
|
+
if (instanceType === TYPE_INVALID) {
|
|
208
|
+
const name = instanceClassName(instance);
|
|
209
|
+
throw new Error(`callVfunc: cannot call '${key}' because ${name} descends from no registered wrapper class`);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
return requiresDefaultVtable(slot, instanceType) ? undefined : instanceType;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Calls the implementation of a virtual function a wrapper class or interface declares, backing the
|
|
217
|
+
* `vfunc`-prefixed members the generated bindings emit. The slot is read from the nearest generated
|
|
218
|
+
* wrapper class in the instance's class chain rather than from the instance's own type, so an
|
|
219
|
+
* override reaching it through `super` runs the implementation it replaced instead of re-entering
|
|
220
|
+
* itself. When that wrapper class does not carry the interface the slot belongs to, because a class
|
|
221
|
+
* registered below it adopted the interface, the slot comes from the interface's own default vtable
|
|
222
|
+
* instead, which holds exactly what the adopting class replaced. Arguments and the return value
|
|
223
|
+
* follow the same convention an override uses: pure out parameters are left out and returned
|
|
224
|
+
* instead, and a slot with several outputs returns them as a tuple. Throws if the owner declares no
|
|
225
|
+
* such slot, the instance descends from no wrapper class, or the resolved vtable leaves the slot
|
|
226
|
+
* empty.
|
|
227
|
+
*
|
|
228
|
+
* @param owner The wrapper class or interface declaring the slot.
|
|
229
|
+
* @param key Name of the generated member, such as `vfuncMeasure`.
|
|
230
|
+
* @param instance The instance to invoke the slot on.
|
|
231
|
+
* @param inputs The arguments the slot receives.
|
|
232
|
+
*/
|
|
233
|
+
function callVfunc(owner: AnyClass, key: string, instance: object, inputs: unknown[]): unknown {
|
|
234
|
+
const slot = resolveOwnerSlot(owner, key);
|
|
235
|
+
const instanceType = resolveInstanceType(key, instance, slot);
|
|
236
|
+
|
|
237
|
+
const invoker = cachedInvoker(vfuncInvokers, owner, `${key}:${String(instanceType)}`, () =>
|
|
238
|
+
buildInvoker(slot, instanceType, "callVfunc"));
|
|
239
|
+
|
|
240
|
+
return invoker(instance, inputs);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
export { callParent, callVfunc };
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { Descriptor } from "@gtkx/native";
|
|
2
|
+
|
|
3
|
+
type RefSeeds = Map<number, unknown>;
|
|
4
|
+
|
|
5
|
+
type SeedFrame = {
|
|
6
|
+
argDescriptors: Descriptor[];
|
|
7
|
+
instance: unknown;
|
|
8
|
+
seeds: RefSeeds;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
const frames: SeedFrame[] = [];
|
|
12
|
+
|
|
13
|
+
const pushSeedFrame = (frame: SeedFrame): void => {
|
|
14
|
+
frames.push(frame);
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const popSeedFrame = (): void => {
|
|
18
|
+
frames.pop();
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const isMatchingFrame = (frame: SeedFrame, argDescriptors: Descriptor[], instance: unknown): boolean =>
|
|
22
|
+
frame.argDescriptors === argDescriptors && frame.instance === instance;
|
|
23
|
+
|
|
24
|
+
const seedsFor = (argDescriptors: Descriptor[], instance: unknown): RefSeeds | undefined => {
|
|
25
|
+
for (let index = frames.length - 1; index >= 0; index--) {
|
|
26
|
+
const frame = frames[index];
|
|
27
|
+
|
|
28
|
+
if (frame !== undefined && isMatchingFrame(frame, argDescriptors, instance)) {
|
|
29
|
+
return frame.seeds;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return undefined;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export { popSeedFrame, pushSeedFrame, type RefSeeds, seedsFor };
|
package/src/vfunc.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { Descriptor } from "@gtkx/native";
|
|
2
|
+
import { type AnyClass, getParentClass, walkClassChain } from "@gtkx/utils";
|
|
3
|
+
import type { Arg } from "./arg.js";
|
|
4
|
+
import { isCallerAllocatedOut } from "./callback.js";
|
|
5
|
+
import { getInterfaceVfuncRegistry, getVfuncRegistry, type VfuncDescriptor } from "./registry.js";
|
|
6
|
+
|
|
7
|
+
function findClassVfuncDescriptor(klass: AnyClass, methodName: string): VfuncDescriptor | null {
|
|
8
|
+
return walkClassChain(getParentClass(klass), (cls) => getVfuncRegistry(cls)?.[methodName]) ?? null;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function findInterfaceVfuncDescriptor(interfaceGtype: bigint, methodName: string): VfuncDescriptor | undefined {
|
|
12
|
+
return getInterfaceVfuncRegistry(interfaceGtype)?.[methodName];
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function vfuncArg(descriptor: Descriptor): Arg {
|
|
16
|
+
if (descriptor.kind === "ref") {
|
|
17
|
+
return { type: descriptor.innerDescriptor, direction: descriptor.inout === true ? "inout" : "out" };
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
if (isCallerAllocatedOut(descriptor)) {
|
|
21
|
+
return { type: descriptor, direction: "out", isCallerAllocated: true };
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return { type: descriptor };
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function vfuncArgs(descriptor: VfuncDescriptor): Arg[] {
|
|
28
|
+
return descriptor.argDescriptors.map((argDescriptor) => vfuncArg(argDescriptor));
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export { findClassVfuncDescriptor, findInterfaceVfuncDescriptor, vfuncArgs };
|