@gtkx/runtime 1.0.0-rc.4 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -5
- package/dist/application-class.d.ts +37 -0
- package/dist/application-class.d.ts.map +1 -0
- package/dist/application-class.js +78 -0
- package/dist/application-class.js.map +1 -0
- package/dist/arg.d.ts +2 -1
- package/dist/arg.d.ts.map +1 -1
- package/dist/arg.js +2 -1
- 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 +4 -3
- package/dist/callback.d.ts.map +1 -1
- package/dist/callback.js +124 -29
- 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/default-application.d.ts +4 -0
- package/dist/default-application.d.ts.map +1 -0
- package/dist/default-application.js +17 -0
- package/dist/default-application.js.map +1 -0
- package/dist/descriptors.d.ts +98 -4
- package/dist/descriptors.d.ts.map +1 -1
- package/dist/descriptors.js +72 -6
- package/dist/descriptors.js.map +1 -1
- package/dist/error.d.ts +1 -0
- package/dist/error.d.ts.map +1 -1
- package/dist/error.js.map +1 -1
- package/dist/exit-hook.js +13 -0
- package/dist/exit-hook.js.map +1 -1
- package/dist/fn.d.ts +24 -2
- package/dist/fn.d.ts.map +1 -1
- package/dist/fn.js +67 -32
- package/dist/fn.js.map +1 -1
- package/dist/folded-lengths.d.ts +16 -0
- package/dist/folded-lengths.d.ts.map +1 -0
- package/dist/folded-lengths.js +38 -0
- package/dist/folded-lengths.js.map +1 -0
- package/dist/index.d.ts +14 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +12 -4
- package/dist/index.js.map +1 -1
- package/dist/internal.d.ts +2 -0
- package/dist/internal.d.ts.map +1 -1
- package/dist/internal.js +2 -0
- package/dist/internal.js.map +1 -1
- package/dist/library.d.ts +3 -1
- package/dist/library.d.ts.map +1 -1
- package/dist/library.js +3 -1
- package/dist/library.js.map +1 -1
- package/dist/lifecycle.d.ts +58 -11
- package/dist/lifecycle.d.ts.map +1 -1
- package/dist/lifecycle.js +95 -22
- package/dist/lifecycle.js.map +1 -1
- package/dist/listeners.d.ts.map +1 -1
- package/dist/listeners.js +19 -5
- package/dist/listeners.js.map +1 -1
- package/dist/mixin.d.ts +3 -2
- package/dist/mixin.d.ts.map +1 -1
- package/dist/mixin.js +13 -1
- package/dist/mixin.js.map +1 -1
- package/dist/native-value.d.ts.map +1 -1
- package/dist/native-value.js +4 -3
- package/dist/native-value.js.map +1 -1
- package/dist/object.d.ts +40 -7
- package/dist/object.d.ts.map +1 -1
- package/dist/object.js +95 -26
- 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 +147 -0
- package/dist/param-spec.js.map +1 -0
- package/dist/promisify.d.ts.map +1 -1
- package/dist/promisify.js +3 -2
- 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 +310 -43
- package/dist/properties.js.map +1 -1
- package/dist/register-class.d.ts +147 -12
- package/dist/register-class.d.ts.map +1 -1
- package/dist/register-class.js +129 -83
- package/dist/register-class.js.map +1 -1
- package/dist/registry.d.ts +80 -21
- package/dist/registry.d.ts.map +1 -1
- package/dist/registry.js +86 -33
- package/dist/registry.js.map +1 -1
- package/dist/signal.d.ts +11 -4
- package/dist/signal.d.ts.map +1 -1
- package/dist/signal.js +85 -25
- package/dist/signal.js.map +1 -1
- package/dist/t.d.ts +2 -0
- package/dist/t.d.ts.map +1 -1
- package/dist/t.js +1 -0
- package/dist/t.js.map +1 -1
- package/dist/tuple.d.ts +6 -3
- package/dist/tuple.d.ts.map +1 -1
- package/dist/tuple.js +9 -7
- package/dist/tuple.js.map +1 -1
- package/dist/type.d.ts +2 -1
- 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 -1
- package/dist/value.d.ts.map +1 -1
- package/dist/value.js +99 -24
- 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 +172 -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 +29 -0
- package/dist/vfunc.js.map +1 -0
- package/package.json +14 -4
- package/src/application-class.ts +126 -0
- package/src/arg.ts +2 -1
- package/src/bind.ts +11 -0
- package/src/callback.ts +178 -34
- package/src/closure.ts +136 -0
- package/src/default-application.ts +21 -0
- package/src/descriptors.ts +150 -34
- package/src/error.ts +1 -0
- package/src/exit-hook.ts +18 -0
- package/src/fn.ts +107 -36
- package/src/folded-lengths.ts +64 -0
- package/src/index.ts +21 -6
- package/src/internal.ts +2 -0
- package/src/library.ts +3 -1
- package/src/lifecycle.ts +138 -25
- package/src/listeners.ts +25 -6
- package/src/mixin.ts +15 -2
- package/src/native-value.ts +5 -3
- package/src/object.ts +134 -35
- package/src/param-spec.ts +234 -0
- package/src/promisify.ts +4 -2
- package/src/properties.ts +473 -46
- package/src/register-class.ts +386 -114
- package/src/registry.ts +169 -49
- package/src/signal.ts +116 -42
- package/src/t.ts +3 -0
- package/src/tuple.ts +18 -12
- package/src/type.ts +3 -1
- package/src/value.ts +143 -23
- package/src/vfunc-call.ts +247 -0
- package/src/vfunc-seeds.ts +36 -0
- package/src/vfunc.ts +38 -0
package/src/type.ts
CHANGED
|
@@ -15,6 +15,7 @@ import { LIB } from "./library.js";
|
|
|
15
15
|
|
|
16
16
|
/** Object tagged with its GLib type through a `__type__` GType field. */
|
|
17
17
|
type TypedClass = {
|
|
18
|
+
/** GType the object's class is registered under. */
|
|
18
19
|
__type__: bigint;
|
|
19
20
|
};
|
|
20
21
|
|
|
@@ -119,6 +120,7 @@ const isTypedClass = (value: unknown): value is TypedClass =>
|
|
|
119
120
|
typeof value === "object" && value !== null && "__type__" in value && typeof value.__type__ === "bigint";
|
|
120
121
|
|
|
121
122
|
/** Returns whether `type` is `ancestorType` or descends from it. */
|
|
123
|
+
/* eslint-disable-next-line unicorn/consistent-boolean-name -- mirrors g_type_is_a from the C API */
|
|
122
124
|
function typeIsA(type: bigint, ancestorType: bigint): boolean {
|
|
123
125
|
return gTypeIsA(type, ancestorType) as boolean;
|
|
124
126
|
}
|
|
@@ -150,6 +152,7 @@ function typeName(type: bigint): string | null {
|
|
|
150
152
|
}
|
|
151
153
|
|
|
152
154
|
/** Returns whether `value` is a typed wrapper whose GType is or descends from `gtype`. */
|
|
155
|
+
/* eslint-disable-next-line unicorn/consistent-boolean-name -- mirrors g_type_is_a from the C API */
|
|
153
156
|
function valueIsA(value: unknown, gtype: bigint): boolean {
|
|
154
157
|
return isTypedClass(value) && typeIsA(value.__type__, gtype);
|
|
155
158
|
}
|
|
@@ -284,6 +287,5 @@ export {
|
|
|
284
287
|
resolveBoxedType,
|
|
285
288
|
resolveFundamentalType,
|
|
286
289
|
resolveDescriptorType,
|
|
287
|
-
type ResolvableDescriptor,
|
|
288
290
|
type TypedClass,
|
|
289
291
|
};
|
package/src/value.ts
CHANGED
|
@@ -9,16 +9,21 @@ import {
|
|
|
9
9
|
boxedT,
|
|
10
10
|
float32T,
|
|
11
11
|
float64T,
|
|
12
|
+
type FundamentalDescriptor,
|
|
13
|
+
fundamentalLifecycleFor,
|
|
14
|
+
fundamentalT,
|
|
15
|
+
int8T,
|
|
12
16
|
int32T,
|
|
13
17
|
objectT,
|
|
14
18
|
stringT,
|
|
19
|
+
uint8T,
|
|
15
20
|
uint32T,
|
|
16
21
|
uint64T,
|
|
17
22
|
voidT,
|
|
18
23
|
} from "./descriptors.js";
|
|
19
24
|
import { LIB, PARAM_T, VALUE_SIZE, VALUE_T, VARIANT_T } from "./library.js";
|
|
20
25
|
import { toNative } from "./native-value.js";
|
|
21
|
-
import { getHandle, getWrapperClass, wrapHandle } from "./registry.js";
|
|
26
|
+
import { getHandle, getWrapperClass, wrapHandle, wrapObject } from "./registry.js";
|
|
22
27
|
import {
|
|
23
28
|
getStrvType,
|
|
24
29
|
isResolvableDescriptor,
|
|
@@ -28,6 +33,7 @@ import {
|
|
|
28
33
|
resolveType,
|
|
29
34
|
TYPE_BOOLEAN,
|
|
30
35
|
TYPE_BOXED,
|
|
36
|
+
TYPE_CHAR,
|
|
31
37
|
TYPE_DOUBLE,
|
|
32
38
|
TYPE_ENUM,
|
|
33
39
|
TYPE_FLAGS,
|
|
@@ -35,13 +41,17 @@ import {
|
|
|
35
41
|
TYPE_GTYPE,
|
|
36
42
|
TYPE_INT,
|
|
37
43
|
TYPE_INT64,
|
|
44
|
+
TYPE_INTERFACE,
|
|
38
45
|
TYPE_INVALID,
|
|
46
|
+
TYPE_LONG,
|
|
39
47
|
TYPE_OBJECT,
|
|
40
48
|
TYPE_PARAM,
|
|
41
49
|
TYPE_POINTER,
|
|
42
50
|
TYPE_STRING,
|
|
51
|
+
TYPE_UCHAR,
|
|
43
52
|
TYPE_UINT,
|
|
44
53
|
TYPE_UINT64,
|
|
54
|
+
TYPE_ULONG,
|
|
45
55
|
TYPE_VARIANT,
|
|
46
56
|
typeFundamental,
|
|
47
57
|
typeName,
|
|
@@ -53,16 +63,24 @@ type ValueType = {
|
|
|
53
63
|
};
|
|
54
64
|
|
|
55
65
|
type ValueGetter = (value: ExternalObject<Handle>) => unknown;
|
|
66
|
+
type ValueWriter = ValueType["set"];
|
|
67
|
+
type ValueNarrower = (jsValue: unknown) => unknown;
|
|
56
68
|
|
|
57
69
|
const setBoxedCache = createBindCache();
|
|
58
70
|
const setStaticBoxedCache = createBindCache();
|
|
59
71
|
const dupBoxedCache = createBindCache();
|
|
72
|
+
const setInstanceCache = createBindCache();
|
|
73
|
+
const peekPointerCache = createBindCache();
|
|
60
74
|
const gValueInit = bind(LIB, "g_value_init", [VALUE_T, biguint64T], voidT);
|
|
61
75
|
const gValueCopy = bind(LIB, "g_value_copy", [VALUE_T, VALUE_T], voidT);
|
|
62
76
|
const booleanValueType = bindValueType("boolean", booleanT);
|
|
63
77
|
const typeValueType = bindValueType("gtype", biguint64T);
|
|
78
|
+
const scharValueType = bindValueType("schar", int8T);
|
|
79
|
+
const ucharValueType = bindValueType("uchar", uint8T);
|
|
64
80
|
const intValueType = bindValueType("int", int32T);
|
|
65
81
|
const uintValueType = bindValueType("uint", uint32T);
|
|
82
|
+
const longValueType = bindValueType("long", bigint64T);
|
|
83
|
+
const ulongValueType = bindValueType("ulong", biguint64T);
|
|
66
84
|
const int64ValueType = bindValueType("int64", bigint64T);
|
|
67
85
|
const uint64ValueType = bindValueType("uint64", biguint64T);
|
|
68
86
|
const floatValueType = bindValueType("float", float32T);
|
|
@@ -74,9 +92,10 @@ const objectValueType = bindValueType("object", objectT("borrowed"));
|
|
|
74
92
|
const paramValueType = bindValueType("param", PARAM_T);
|
|
75
93
|
const variantValueType = bindValueType("variant", VARIANT_T);
|
|
76
94
|
const pointerValueType = bindValueType("pointer", uint64T);
|
|
95
|
+
const setStrvBoxed = bind(LIB, "g_value_set_boxed", [VALUE_T, arrayT(stringT("borrowed"))], voidT);
|
|
77
96
|
|
|
78
97
|
const strvValueType: ValueType = {
|
|
79
|
-
set:
|
|
98
|
+
set: setStrvValue,
|
|
80
99
|
get: bind(LIB, "g_value_get_boxed", [VALUE_T], arrayT(stringT("borrowed"))),
|
|
81
100
|
};
|
|
82
101
|
|
|
@@ -100,9 +119,12 @@ const PLAIN_VALUE_TYPES: Partial<Record<Descriptor["kind"], ValueType>> = {
|
|
|
100
119
|
|
|
101
120
|
const PLAIN_VALUE_GETTERS: Map<bigint, ValueGetter> = new Map([
|
|
102
121
|
[TYPE_BOOLEAN, booleanValueType.get],
|
|
103
|
-
[
|
|
122
|
+
[TYPE_CHAR, scharValueType.get],
|
|
123
|
+
[TYPE_UCHAR, ucharValueType.get],
|
|
104
124
|
[TYPE_INT, intValueType.get],
|
|
105
125
|
[TYPE_UINT, uintValueType.get],
|
|
126
|
+
[TYPE_LONG, longValueType.get],
|
|
127
|
+
[TYPE_ULONG, ulongValueType.get],
|
|
106
128
|
[TYPE_INT64, int64ValueType.get],
|
|
107
129
|
[TYPE_UINT64, uint64ValueType.get],
|
|
108
130
|
[TYPE_FLOAT, floatValueType.get],
|
|
@@ -113,9 +135,12 @@ const PLAIN_VALUE_GETTERS: Map<bigint, ValueGetter> = new Map([
|
|
|
113
135
|
|
|
114
136
|
const PLAIN_VALUE_SETTERS: Map<bigint, ValueType["set"]> = new Map([
|
|
115
137
|
[TYPE_BOOLEAN, booleanValueType.set],
|
|
116
|
-
[
|
|
138
|
+
[TYPE_CHAR, scharValueType.set],
|
|
139
|
+
[TYPE_UCHAR, ucharValueType.set],
|
|
117
140
|
[TYPE_INT, intValueType.set],
|
|
118
141
|
[TYPE_UINT, uintValueType.set],
|
|
142
|
+
[TYPE_LONG, longValueType.set],
|
|
143
|
+
[TYPE_ULONG, ulongValueType.set],
|
|
119
144
|
[TYPE_INT64, int64ValueType.set],
|
|
120
145
|
[TYPE_UINT64, uint64ValueType.set],
|
|
121
146
|
[TYPE_FLOAT, floatValueType.set],
|
|
@@ -127,9 +152,11 @@ const PLAIN_VALUE_SETTERS: Map<bigint, ValueType["set"]> = new Map([
|
|
|
127
152
|
const WRAPPED_VALUE_SETTERS: Map<bigint, ValueType["set"]> = new Map([
|
|
128
153
|
[TYPE_STRING, setStringValue],
|
|
129
154
|
[TYPE_OBJECT, handleSetter(objectValueType)],
|
|
155
|
+
[TYPE_INTERFACE, handleSetter(objectValueType)],
|
|
130
156
|
[TYPE_PARAM, handleSetter(paramValueType)],
|
|
131
157
|
[TYPE_VARIANT, handleSetter(variantValueType)],
|
|
132
158
|
[TYPE_BOXED, setBoxedFromValue],
|
|
159
|
+
[TYPE_POINTER, setPointerValue],
|
|
133
160
|
]);
|
|
134
161
|
|
|
135
162
|
const getBoxedTypeName = (type: bigint): string => typeName(type) ?? "GBoxed";
|
|
@@ -160,6 +187,46 @@ const boxedValueType = (type: bigint): ValueType => {
|
|
|
160
187
|
const enumOrFlagsValueType = (type: bigint): ValueType =>
|
|
161
188
|
typeFundamental(type) === TYPE_FLAGS ? flagsValueType : enumValueType;
|
|
162
189
|
|
|
190
|
+
function unsupportedFundamental(type: bigint): never {
|
|
191
|
+
throw new Error(`Unsupported fundamental type '${typeName(type) ?? String(type)}' for value`);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
const setInstanceBind = (fundamental: bigint, descriptor: FundamentalDescriptor) =>
|
|
195
|
+
setInstanceCache(String(fundamental), LIB, "g_value_set_instance", [VALUE_T, descriptor], voidT);
|
|
196
|
+
|
|
197
|
+
const peekPointerBind = (fundamental: bigint, descriptor: FundamentalDescriptor) =>
|
|
198
|
+
peekPointerCache(String(fundamental), LIB, "g_value_peek_pointer", [VALUE_T], descriptor);
|
|
199
|
+
|
|
200
|
+
const customFundamentalDescriptor = (fundamental: bigint): FundamentalDescriptor | undefined => {
|
|
201
|
+
const name = typeName(fundamental);
|
|
202
|
+
|
|
203
|
+
if (name === null) {
|
|
204
|
+
return undefined;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
const lifecycle = fundamentalLifecycleFor(name);
|
|
208
|
+
|
|
209
|
+
if (lifecycle === undefined) {
|
|
210
|
+
return undefined;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
return fundamentalT(lifecycle.sharedLibrary, lifecycle.refFnName, lifecycle.unrefFnName, {
|
|
214
|
+
ownership: "borrowed",
|
|
215
|
+
typeName: name,
|
|
216
|
+
});
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
const customFundamentalValueType = (type: bigint): ValueType | undefined => {
|
|
220
|
+
const fundamental = typeFundamental(type);
|
|
221
|
+
const descriptor = customFundamentalDescriptor(fundamental);
|
|
222
|
+
|
|
223
|
+
if (descriptor === undefined) {
|
|
224
|
+
return undefined;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
return { set: setInstanceBind(fundamental, descriptor), get: peekPointerBind(fundamental, descriptor) };
|
|
228
|
+
};
|
|
229
|
+
|
|
163
230
|
const fundamentalValueType = (type: bigint): ValueType => {
|
|
164
231
|
switch (typeFundamental(type)) {
|
|
165
232
|
case TYPE_PARAM: {
|
|
@@ -172,7 +239,7 @@ const fundamentalValueType = (type: bigint): ValueType => {
|
|
|
172
239
|
return boxedValueType(type);
|
|
173
240
|
}
|
|
174
241
|
default: {
|
|
175
|
-
|
|
242
|
+
return customFundamentalValueType(type) ?? unsupportedFundamental(type);
|
|
176
243
|
}
|
|
177
244
|
}
|
|
178
245
|
};
|
|
@@ -189,7 +256,7 @@ function copyValue(dest: ExternalObject<Handle>, src: ExternalObject<Handle>): v
|
|
|
189
256
|
gValueCopy(src, dest);
|
|
190
257
|
}
|
|
191
258
|
|
|
192
|
-
const
|
|
259
|
+
const newValueForType = (type: bigint): ExternalObject<Handle> => {
|
|
193
260
|
const value = newValue();
|
|
194
261
|
gValueInit(value, type);
|
|
195
262
|
|
|
@@ -202,7 +269,7 @@ const newBoxedValue = (
|
|
|
202
269
|
resolveSetBind: (name: string) => ValueType["set"],
|
|
203
270
|
): ExternalObject<Handle> => {
|
|
204
271
|
const type = resolveDescriptorType(descriptor);
|
|
205
|
-
const value =
|
|
272
|
+
const value = newValueForType(type);
|
|
206
273
|
resolveSetBind(getBoxedTypeName(type))(value, getHandle(boxed));
|
|
207
274
|
|
|
208
275
|
return value;
|
|
@@ -276,8 +343,9 @@ const wrappedValueGetter = (fundamental: bigint): ValueGetter | undefined => {
|
|
|
276
343
|
case TYPE_STRING: {
|
|
277
344
|
return (value) => stringValueType.get(value) ?? null;
|
|
278
345
|
}
|
|
346
|
+
case TYPE_INTERFACE:
|
|
279
347
|
case TYPE_OBJECT: {
|
|
280
|
-
return (value) =>
|
|
348
|
+
return (value) => wrapObject(objectValueType.get(value));
|
|
281
349
|
}
|
|
282
350
|
case TYPE_PARAM: {
|
|
283
351
|
return (value) =>
|
|
@@ -309,6 +377,18 @@ function setStringValue(value: ExternalObject<Handle>, nativeValue: unknown): vo
|
|
|
309
377
|
stringValueType.set(value, nativeValue ?? null);
|
|
310
378
|
}
|
|
311
379
|
|
|
380
|
+
function setStrvValue(value: ExternalObject<Handle>, nativeValue: unknown): void {
|
|
381
|
+
setStrvBoxed(value, nativeValue ?? null);
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
function setPointerValue(value: ExternalObject<Handle>, nativeValue: unknown): void {
|
|
385
|
+
if (nativeValue != null) {
|
|
386
|
+
throw new Error("G_TYPE_POINTER non-null values cannot be marshalled from JS");
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
pointerValueType.set(value, 0);
|
|
390
|
+
}
|
|
391
|
+
|
|
312
392
|
function handleSetter(target: ValueType): ValueType["set"] {
|
|
313
393
|
return (value, nativeValue) => {
|
|
314
394
|
target.set(value, nativeValue == null ? null : getHandle(nativeValue));
|
|
@@ -319,21 +399,61 @@ function setBoxedFromValue(value: ExternalObject<Handle>, nativeValue: unknown):
|
|
|
319
399
|
setBoxedValue(value, nativeValue ?? null);
|
|
320
400
|
}
|
|
321
401
|
|
|
322
|
-
const
|
|
402
|
+
const customFundamentalGetter = (type: bigint): ValueGetter | undefined => {
|
|
403
|
+
const valueType = customFundamentalValueType(type);
|
|
404
|
+
|
|
405
|
+
if (valueType === undefined) {
|
|
406
|
+
return undefined;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
return (value) => wrapHandle(valueType.get(value) as ExternalObject<Handle> | null, getWrapperClass(type));
|
|
410
|
+
};
|
|
411
|
+
|
|
412
|
+
const customFundamentalSetter = (type: bigint): ValueType["set"] | undefined => {
|
|
413
|
+
const valueType = customFundamentalValueType(type);
|
|
414
|
+
|
|
415
|
+
return valueType === undefined ? undefined : handleSetter(valueType);
|
|
416
|
+
};
|
|
417
|
+
|
|
418
|
+
const builtInValueGetter = (fundamental: bigint): ValueGetter | undefined =>
|
|
323
419
|
PLAIN_VALUE_GETTERS.get(fundamental) ?? wrappedValueGetter(fundamental);
|
|
324
420
|
|
|
325
|
-
const
|
|
421
|
+
const builtInValueSetter = (fundamental: bigint): ValueType["set"] | undefined =>
|
|
326
422
|
PLAIN_VALUE_SETTERS.get(fundamental) ?? WRAPPED_VALUE_SETTERS.get(fundamental);
|
|
327
423
|
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
424
|
+
const exactValueType = (type: bigint): ValueType | undefined => {
|
|
425
|
+
if (type === TYPE_GTYPE) {
|
|
426
|
+
return typeValueType;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
return type === getStrvType() ? strvValueType : undefined;
|
|
430
|
+
};
|
|
431
|
+
|
|
432
|
+
const resolveValueGetter = (type: bigint): ValueGetter | undefined =>
|
|
433
|
+
exactValueType(type)?.get ?? builtInValueGetter(typeFundamental(type)) ?? customFundamentalGetter(type);
|
|
434
|
+
|
|
435
|
+
const resolveValueSetter = (type: bigint): ValueType["set"] | undefined =>
|
|
436
|
+
exactValueType(type)?.set ?? builtInValueSetter(typeFundamental(type)) ?? customFundamentalSetter(type);
|
|
437
|
+
|
|
438
|
+
const holdsUnchanged: ValueNarrower = (jsValue) => jsValue;
|
|
439
|
+
const holdsAsFloat: ValueNarrower = (jsValue) => Math.fround(jsValue as number);
|
|
440
|
+
|
|
441
|
+
function valueNarrowerFor(type: bigint): ValueNarrower {
|
|
442
|
+
return typeFundamental(type) === TYPE_FLOAT ? holdsAsFloat : holdsUnchanged;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
function valueWriterFor(type: bigint): ValueWriter {
|
|
446
|
+
const set = resolveValueSetter(type);
|
|
331
447
|
|
|
332
448
|
if (set === undefined) {
|
|
333
449
|
throw new Error(`Unsupported type for intoValue: ${typeName(type) ?? String(type)}`);
|
|
334
450
|
}
|
|
335
451
|
|
|
336
|
-
set
|
|
452
|
+
return set;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
function intoValue(value: ExternalObject<Handle>, jsValue: unknown): void {
|
|
456
|
+
valueWriterFor(getValueType(value))(value, jsValue);
|
|
337
457
|
}
|
|
338
458
|
|
|
339
459
|
const resolveNativeValue = (descriptor: Descriptor, value: unknown): unknown => {
|
|
@@ -357,7 +477,7 @@ const resolveValueGType = (descriptor: Descriptor, nativeValue: unknown): bigint
|
|
|
357
477
|
function toValue(descriptor: Descriptor, value: unknown): ExternalObject<Handle> {
|
|
358
478
|
const nativeValue = resolveNativeValue(descriptor, value);
|
|
359
479
|
const type = resolveValueGType(descriptor, nativeValue);
|
|
360
|
-
const gValue =
|
|
480
|
+
const gValue = newValueForType(type);
|
|
361
481
|
resolveValueType(descriptor).set(gValue, nativeValue);
|
|
362
482
|
|
|
363
483
|
return gValue;
|
|
@@ -365,12 +485,7 @@ function toValue(descriptor: Descriptor, value: unknown): ExternalObject<Handle>
|
|
|
365
485
|
|
|
366
486
|
function fromValue(value: ExternalObject<Handle>): unknown {
|
|
367
487
|
const type = getValueType(value);
|
|
368
|
-
|
|
369
|
-
if (type === getStrvType()) {
|
|
370
|
-
return strvValueType.get(value);
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
const get = resolveValueGetter(typeFundamental(type));
|
|
488
|
+
const get = resolveValueGetter(type);
|
|
374
489
|
|
|
375
490
|
if (get === undefined) {
|
|
376
491
|
throw new Error(`Unsupported type for fromValue: ${typeName(type) ?? String(type)}`);
|
|
@@ -380,7 +495,7 @@ function fromValue(value: ExternalObject<Handle>): unknown {
|
|
|
380
495
|
}
|
|
381
496
|
|
|
382
497
|
function newValueForDescriptor(descriptor: Descriptor): ExternalObject<Handle> {
|
|
383
|
-
return
|
|
498
|
+
return newValueForType(resolveDescriptorType(descriptor));
|
|
384
499
|
}
|
|
385
500
|
|
|
386
501
|
function outValueForDescriptor(
|
|
@@ -394,7 +509,7 @@ function outValueForDescriptor(
|
|
|
394
509
|
write(storage, descriptor, 0, initial);
|
|
395
510
|
}
|
|
396
511
|
|
|
397
|
-
const value =
|
|
512
|
+
const value = newValueForType(TYPE_POINTER);
|
|
398
513
|
pointerValueType.set(value, storage);
|
|
399
514
|
|
|
400
515
|
return { value, read: () => read(storage, descriptor, 0) };
|
|
@@ -417,7 +532,12 @@ export {
|
|
|
417
532
|
toValue,
|
|
418
533
|
fromValue,
|
|
419
534
|
newValueForDescriptor,
|
|
535
|
+
newValueForType,
|
|
420
536
|
outValueForDescriptor,
|
|
421
537
|
outValueForBoxedDescriptor,
|
|
422
538
|
inoutValueForBoxedDescriptor,
|
|
539
|
+
type ValueNarrower,
|
|
540
|
+
valueNarrowerFor,
|
|
541
|
+
type ValueWriter,
|
|
542
|
+
valueWriterFor,
|
|
423
543
|
};
|
|
@@ -0,0 +1,247 @@
|
|
|
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
|
+
{
|
|
86
|
+
args,
|
|
87
|
+
returns: descriptor.returnDescriptor,
|
|
88
|
+
canThrow,
|
|
89
|
+
},
|
|
90
|
+
takeRefSeeds,
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
return (instance, inputs) => {
|
|
94
|
+
if (inputs.length !== inputArgs.length) {
|
|
95
|
+
throw new Error(
|
|
96
|
+
`${caller}: ${label} expects ${String(inputArgs.length)} arguments, ` +
|
|
97
|
+
`received ${String(inputs.length)}`,
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const nativeInputs = inputArgs.map((arg, index) => toNativeInput(arg, inputs[index]));
|
|
102
|
+
pendingSeeds = SEEDED_SLOTS[label] ?? seedsFor(descriptor.argDescriptors, instance);
|
|
103
|
+
|
|
104
|
+
return shaped(getHandle(instance), ...nativeInputs);
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function cachedInvoker(cache: InvokerCache, owner: AnyClass, key: string, build: () => Invoker): Invoker {
|
|
109
|
+
let byKey = cache.get(owner);
|
|
110
|
+
|
|
111
|
+
if (byKey === undefined) {
|
|
112
|
+
byKey = new Map();
|
|
113
|
+
cache.set(owner, byKey);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
let invoker = byKey.get(key);
|
|
117
|
+
|
|
118
|
+
if (invoker === undefined) {
|
|
119
|
+
invoker = build();
|
|
120
|
+
byKey.set(key, invoker);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return invoker;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function resolveParentSlot(klass: AnyClass, parentType: bigint, methodName: string): ResolvedSlot | undefined {
|
|
127
|
+
const classDescriptor = findClassVfuncDescriptor(klass, methodName);
|
|
128
|
+
|
|
129
|
+
if (classDescriptor) {
|
|
130
|
+
return { descriptor: classDescriptor };
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
for (const interfaceType of typeInterfaces(parentType)) {
|
|
134
|
+
const descriptor = findInterfaceVfuncDescriptor(interfaceType, methodName);
|
|
135
|
+
|
|
136
|
+
if (descriptor) {
|
|
137
|
+
return { descriptor, interfaceType };
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return undefined;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function resolveParentType(klass: AnyClass, methodName: string): bigint {
|
|
145
|
+
const gtype = getClassType(klass);
|
|
146
|
+
|
|
147
|
+
if (gtype === TYPE_INVALID) {
|
|
148
|
+
throw new Error(
|
|
149
|
+
`callParent: cannot call '${methodName}' because ${klass.name} was never passed to registerClass`,
|
|
150
|
+
);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
return typeParent(gtype);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function buildParentInvoker(klass: AnyClass, methodName: string): Invoker {
|
|
157
|
+
const parentType = resolveParentType(klass, methodName);
|
|
158
|
+
const slot = resolveParentSlot(klass, parentType, methodName);
|
|
159
|
+
|
|
160
|
+
if (!slot) {
|
|
161
|
+
throw new Error(`callParent: ${klass.name} inherits no '${methodName}' vtable slot`);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
return buildInvoker(slot, parentType, "callParent");
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Calls the implementation of a virtual function that the parent type of `klass` provides, so an
|
|
169
|
+
* override can chain up to the behavior it replaces. `klass` is the class whose override is
|
|
170
|
+
* running, named lexically rather than taken from `instance`, so each level of a hierarchy reaches
|
|
171
|
+
* exactly one level up. Arguments and the return value follow the same convention the override
|
|
172
|
+
* itself uses: pure out parameters are left out and returned instead, and a slot with several
|
|
173
|
+
* outputs returns them as a tuple. Throws if `klass` was never registered, inherits no such slot,
|
|
174
|
+
* or the parent type leaves the slot empty.
|
|
175
|
+
*
|
|
176
|
+
* @param klass The registered class whose override is chaining up.
|
|
177
|
+
* @param methodName Name of the overridden method, such as `vfuncMeasure`.
|
|
178
|
+
* @param instance The instance the override was invoked on.
|
|
179
|
+
* @param inputs The arguments the override received.
|
|
180
|
+
*/
|
|
181
|
+
function callParent(klass: AnyClass, methodName: string, instance: object, ...inputs: unknown[]): unknown {
|
|
182
|
+
const invoker = cachedInvoker(parentInvokers, klass, methodName, () => buildParentInvoker(klass, methodName));
|
|
183
|
+
|
|
184
|
+
return invoker(instance, inputs);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function resolveOwnerSlot(owner: AnyClass, key: string): ResolvedSlot {
|
|
188
|
+
const classDescriptor = getVfuncRegistry(owner)?.[key];
|
|
189
|
+
|
|
190
|
+
if (classDescriptor !== undefined) {
|
|
191
|
+
return { descriptor: classDescriptor };
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
const interfaceType = getClassType(owner);
|
|
195
|
+
const descriptor = getInterfaceVfuncRegistry(interfaceType)?.[key];
|
|
196
|
+
|
|
197
|
+
if (descriptor === undefined) {
|
|
198
|
+
throw new Error(`callVfunc: ${owner.name} declares no '${key}' vtable slot`);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
return { descriptor, interfaceType };
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function requiresDefaultVtable(slot: ResolvedSlot, instanceType: bigint): boolean {
|
|
205
|
+
return slot.interfaceType !== undefined && !typeIsA(instanceType, slot.interfaceType);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
function resolveInstanceType(key: string, instance: object, slot: ResolvedSlot): bigint | undefined {
|
|
209
|
+
const instanceType = resolveWrapperType(instance);
|
|
210
|
+
|
|
211
|
+
if (instanceType === TYPE_INVALID) {
|
|
212
|
+
const name = instanceClassName(instance);
|
|
213
|
+
throw new Error(`callVfunc: cannot call '${key}' because ${name} descends from no registered wrapper class`);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
return requiresDefaultVtable(slot, instanceType) ? undefined : instanceType;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Calls the implementation of a virtual function a wrapper class or interface declares, backing the
|
|
221
|
+
* `vfunc`-prefixed members the generated bindings emit. The slot is read from the nearest generated
|
|
222
|
+
* wrapper class in the instance's class chain rather than from the instance's own type, so an
|
|
223
|
+
* override reaching it through `super` runs the implementation it replaced instead of re-entering
|
|
224
|
+
* itself. When that wrapper class does not carry the interface the slot belongs to, because a class
|
|
225
|
+
* registered below it adopted the interface, the slot comes from the interface's own default vtable
|
|
226
|
+
* instead, which holds exactly what the adopting class replaced. Arguments and the return value
|
|
227
|
+
* follow the same convention an override uses: pure out parameters are left out and returned
|
|
228
|
+
* instead, and a slot with several outputs returns them as a tuple. Throws if the owner declares no
|
|
229
|
+
* such slot, the instance descends from no wrapper class, or the resolved vtable leaves the slot
|
|
230
|
+
* empty.
|
|
231
|
+
*
|
|
232
|
+
* @param owner The wrapper class or interface declaring the slot.
|
|
233
|
+
* @param key Name of the generated member, such as `vfuncMeasure`.
|
|
234
|
+
* @param instance The instance to invoke the slot on.
|
|
235
|
+
* @param inputs The arguments the slot receives.
|
|
236
|
+
*/
|
|
237
|
+
function callVfunc(owner: AnyClass, key: string, instance: object, inputs: unknown[]): unknown {
|
|
238
|
+
const slot = resolveOwnerSlot(owner, key);
|
|
239
|
+
const instanceType = resolveInstanceType(key, instance, slot);
|
|
240
|
+
|
|
241
|
+
const invoker = cachedInvoker(vfuncInvokers, owner, `${key}:${String(instanceType)}`, () =>
|
|
242
|
+
buildInvoker(slot, instanceType, "callVfunc"));
|
|
243
|
+
|
|
244
|
+
return invoker(instance, inputs);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
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 };
|