@gtkx/runtime 1.0.0 → 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 +1 -1
- package/dist/application-class.d.ts +5 -0
- package/dist/application-class.d.ts.map +1 -1
- package/dist/application-class.js +11 -1
- package/dist/application-class.js.map +1 -1
- package/dist/callback.d.ts +2 -2
- package/dist/callback.d.ts.map +1 -1
- package/dist/callback.js +51 -54
- package/dist/callback.js.map +1 -1
- 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 +25 -2
- package/dist/descriptors.d.ts.map +1 -1
- package/dist/descriptors.js +31 -9
- package/dist/descriptors.js.map +1 -1
- package/dist/exit-hook.js +13 -0
- package/dist/exit-hook.js.map +1 -1
- package/dist/fn.d.ts +5 -0
- package/dist/fn.d.ts.map +1 -1
- package/dist/fn.js +11 -13
- 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/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 +16 -1
- package/dist/lifecycle.d.ts.map +1 -1
- package/dist/lifecycle.js +44 -14
- package/dist/lifecycle.js.map +1 -1
- package/dist/listeners.d.ts.map +1 -1
- package/dist/listeners.js +16 -4
- package/dist/listeners.js.map +1 -1
- package/dist/native-value.js +2 -2
- package/dist/native-value.js.map +1 -1
- package/dist/object.d.ts +7 -2
- package/dist/object.d.ts.map +1 -1
- package/dist/object.js +7 -2
- package/dist/object.js.map +1 -1
- package/dist/param-spec.d.ts.map +1 -1
- package/dist/param-spec.js +18 -20
- package/dist/param-spec.js.map +1 -1
- package/dist/properties.d.ts +2 -1
- package/dist/properties.d.ts.map +1 -1
- package/dist/properties.js +13 -9
- package/dist/properties.js.map +1 -1
- package/dist/register-class.d.ts +30 -0
- package/dist/register-class.d.ts.map +1 -1
- package/dist/register-class.js +21 -7
- package/dist/register-class.js.map +1 -1
- package/dist/registry.d.ts +12 -3
- package/dist/registry.d.ts.map +1 -1
- package/dist/registry.js +24 -15
- package/dist/registry.js.map +1 -1
- package/dist/signal.d.ts +2 -2
- package/dist/signal.d.ts.map +1 -1
- package/dist/signal.js +22 -18
- 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/value.d.ts +3 -1
- package/dist/value.d.ts.map +1 -1
- package/dist/value.js +64 -18
- package/dist/value.js.map +1 -1
- package/dist/vfunc-call.d.ts.map +1 -1
- package/dist/vfunc-call.js +5 -1
- package/dist/vfunc-call.js.map +1 -1
- package/dist/vfunc.d.ts.map +1 -1
- package/dist/vfunc.js +9 -3
- package/dist/vfunc.js.map +1 -1
- package/package.json +14 -4
- package/src/application-class.ts +12 -2
- package/src/callback.ts +70 -66
- package/src/default-application.ts +21 -0
- package/src/descriptors.ts +69 -14
- package/src/exit-hook.ts +18 -0
- package/src/fn.ts +23 -13
- package/src/folded-lengths.ts +64 -0
- package/src/internal.ts +1 -0
- package/src/library.ts +2 -1
- package/src/lifecycle.ts +60 -14
- package/src/listeners.ts +22 -5
- package/src/native-value.ts +2 -2
- package/src/object.ts +7 -2
- package/src/param-spec.ts +21 -22
- package/src/properties.ts +23 -9
- package/src/register-class.ts +63 -8
- package/src/registry.ts +40 -18
- package/src/signal.ts +32 -28
- package/src/t.ts +3 -0
- package/src/tuple.ts +18 -12
- package/src/value.ts +94 -19
- package/src/vfunc-call.ts +5 -1
- package/src/vfunc.ts +10 -3
package/src/param-spec.ts
CHANGED
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
TYPE_ENUM,
|
|
14
14
|
TYPE_FLAGS,
|
|
15
15
|
TYPE_FLOAT,
|
|
16
|
+
TYPE_GTYPE,
|
|
16
17
|
TYPE_INT,
|
|
17
18
|
TYPE_INT64,
|
|
18
19
|
TYPE_INTERFACE,
|
|
@@ -51,7 +52,6 @@ const UINT32_MAXIMUM = 4_294_967_295;
|
|
|
51
52
|
const INT64_MINIMUM = -(2n ** 63n);
|
|
52
53
|
const INT64_MAXIMUM = 2n ** 63n - 1n;
|
|
53
54
|
const UINT64_MAXIMUM = 2n ** 64n - 1n;
|
|
54
|
-
const FLOAT_MAXIMUM = 3.4028234663852886e38;
|
|
55
55
|
|
|
56
56
|
const WRAPPED_FUNDAMENTALS: Set<bigint> = new Set([
|
|
57
57
|
TYPE_BOXED,
|
|
@@ -61,6 +61,8 @@ const WRAPPED_FUNDAMENTALS: Set<bigint> = new Set([
|
|
|
61
61
|
TYPE_VARIANT,
|
|
62
62
|
]);
|
|
63
63
|
|
|
64
|
+
const isWideUnsignedValue: ValueGuard = wideIntegerGuardFor(0n, UINT64_MAXIMUM);
|
|
65
|
+
|
|
64
66
|
const SCALAR_GUARDS: Map<bigint, ValueGuard> = new Map([
|
|
65
67
|
[TYPE_BOOLEAN, isBooleanValue],
|
|
66
68
|
[TYPE_STRING, isStringValue],
|
|
@@ -71,11 +73,11 @@ const SCALAR_GUARDS: Map<bigint, ValueGuard> = new Map([
|
|
|
71
73
|
[TYPE_ENUM, integerGuardFor(INT32_MINIMUM, INT32_MAXIMUM)],
|
|
72
74
|
[TYPE_FLAGS, integerGuardFor(0, UINT32_MAXIMUM)],
|
|
73
75
|
[TYPE_LONG, wideIntegerGuardFor(INT64_MINIMUM, INT64_MAXIMUM)],
|
|
74
|
-
[TYPE_ULONG,
|
|
76
|
+
[TYPE_ULONG, isWideUnsignedValue],
|
|
75
77
|
[TYPE_INT64, wideIntegerGuardFor(INT64_MINIMUM, INT64_MAXIMUM)],
|
|
76
|
-
[TYPE_UINT64,
|
|
77
|
-
[TYPE_FLOAT,
|
|
78
|
-
[TYPE_DOUBLE,
|
|
78
|
+
[TYPE_UINT64, isWideUnsignedValue],
|
|
79
|
+
[TYPE_FLOAT, isNumberValue],
|
|
80
|
+
[TYPE_DOUBLE, isNumberValue],
|
|
79
81
|
[TYPE_POINTER, isNullValue],
|
|
80
82
|
]);
|
|
81
83
|
|
|
@@ -115,12 +117,8 @@ function isStrvValue(value: unknown): boolean {
|
|
|
115
117
|
return value == null || (Array.isArray(value) && value.every((item) => typeof item === "string"));
|
|
116
118
|
}
|
|
117
119
|
|
|
118
|
-
function
|
|
119
|
-
return
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
function isFloatValue(value: unknown): boolean {
|
|
123
|
-
return isDoubleValue(value) && (value == null || Math.abs(value as number) <= FLOAT_MAXIMUM);
|
|
120
|
+
function isNumberValue(value: unknown): boolean {
|
|
121
|
+
return typeof value === "number";
|
|
124
122
|
}
|
|
125
123
|
|
|
126
124
|
function isAnyValue(): boolean {
|
|
@@ -131,12 +129,8 @@ function isNullValue(value: unknown): boolean {
|
|
|
131
129
|
return value == null;
|
|
132
130
|
}
|
|
133
131
|
|
|
134
|
-
function isIntegerWithin(value: unknown, minimum: number, maximum: number): boolean {
|
|
135
|
-
return Number.isSafeInteger(value) && (value as number) >= minimum && (value as number) <= maximum;
|
|
136
|
-
}
|
|
137
|
-
|
|
138
132
|
function integerGuardFor(minimum: number, maximum: number): ValueGuard {
|
|
139
|
-
return (value) => value
|
|
133
|
+
return (value) => Number.isSafeInteger(value) && (value as number) >= minimum && (value as number) <= maximum;
|
|
140
134
|
}
|
|
141
135
|
|
|
142
136
|
function toWideInteger(value: unknown): bigint | undefined {
|
|
@@ -149,10 +143,6 @@ function toWideInteger(value: unknown): bigint | undefined {
|
|
|
149
143
|
|
|
150
144
|
function wideIntegerGuardFor(minimum: bigint, maximum: bigint): ValueGuard {
|
|
151
145
|
return (value) => {
|
|
152
|
-
if (value == null) {
|
|
153
|
-
return true;
|
|
154
|
-
}
|
|
155
|
-
|
|
156
146
|
const wide = toWideInteger(value);
|
|
157
147
|
|
|
158
148
|
return wide !== undefined && wide >= minimum && wide <= maximum;
|
|
@@ -173,11 +163,20 @@ function wrappedGuardFor(valueType: bigint): ValueGuard {
|
|
|
173
163
|
return (value) => value == null || typeIsA(resolveGtype(value), valueType);
|
|
174
164
|
}
|
|
175
165
|
|
|
166
|
+
function exactGuardFor(valueType: bigint): ValueGuard | undefined {
|
|
167
|
+
if (valueType === TYPE_GTYPE) {
|
|
168
|
+
return isWideUnsignedValue;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
return valueType === getStrvType() ? isStrvValue : undefined;
|
|
172
|
+
}
|
|
173
|
+
|
|
176
174
|
function valueGuardFor(valueType: bigint): ValueGuard {
|
|
177
175
|
assertParamLayout();
|
|
176
|
+
const exact = exactGuardFor(valueType);
|
|
178
177
|
|
|
179
|
-
if (
|
|
180
|
-
return
|
|
178
|
+
if (exact !== undefined) {
|
|
179
|
+
return exact;
|
|
181
180
|
}
|
|
182
181
|
|
|
183
182
|
const fundamental = typeFundamental(valueType);
|
package/src/properties.ts
CHANGED
|
@@ -16,7 +16,14 @@ import {
|
|
|
16
16
|
} from "./param-spec.js";
|
|
17
17
|
import { getHandle, getInterfaceProperties, instanceClassName, type InterfaceProperty } from "./registry.js";
|
|
18
18
|
import { typeName } from "./type.js";
|
|
19
|
-
import {
|
|
19
|
+
import {
|
|
20
|
+
fromValue,
|
|
21
|
+
newValueForType,
|
|
22
|
+
type ValueNarrower,
|
|
23
|
+
valueNarrowerFor,
|
|
24
|
+
type ValueWriter,
|
|
25
|
+
valueWriterFor,
|
|
26
|
+
} from "./value.js";
|
|
20
27
|
|
|
21
28
|
type PropertyCheck = {
|
|
22
29
|
name: string;
|
|
@@ -25,6 +32,7 @@ type PropertyCheck = {
|
|
|
25
32
|
flags: number;
|
|
26
33
|
valueType: bigint;
|
|
27
34
|
canHoldValue: ValueGuard;
|
|
35
|
+
narrowValue: ValueNarrower;
|
|
28
36
|
write?: ValueWriter;
|
|
29
37
|
scratch?: ExternalObject<Handle>;
|
|
30
38
|
};
|
|
@@ -80,6 +88,8 @@ const typeLabel = (type: bigint): string => typeName(type) ?? String(type);
|
|
|
80
88
|
const defaultValueFor = (handle: ExternalObject<Handle>): ExternalObject<Handle> =>
|
|
81
89
|
paramSpecDefaultValue(handle) as ExternalObject<Handle>;
|
|
82
90
|
|
|
91
|
+
const heldValue = (value: unknown): unknown => value ?? null;
|
|
92
|
+
|
|
83
93
|
const holdsReason = (check: PropertyCheck): string =>
|
|
84
94
|
`the property holds values of type '${typeLabel(check.valueType)}'`;
|
|
85
95
|
|
|
@@ -93,6 +103,7 @@ function checkFor(handle: ExternalObject<Handle>, name: string): PropertyCheck {
|
|
|
93
103
|
flags: getParamFlags(handle),
|
|
94
104
|
valueType,
|
|
95
105
|
canHoldValue: valueGuardFor(valueType),
|
|
106
|
+
narrowValue: valueNarrowerFor(valueType),
|
|
96
107
|
};
|
|
97
108
|
}
|
|
98
109
|
|
|
@@ -168,6 +179,11 @@ function assertWritable(instance: object, check: PropertyCheck, value: unknown):
|
|
|
168
179
|
throw new TypeError(propertyMessage(instance, check, refusalTail(check, value, READ_ONLY_REASON)));
|
|
169
180
|
}
|
|
170
181
|
|
|
182
|
+
function writeHeld(check: PropertyCheck, gValue: ExternalObject<Handle>, value: unknown): void {
|
|
183
|
+
check.write ??= valueWriterFor(check.valueType);
|
|
184
|
+
check.write(gValue, check.narrowValue(value));
|
|
185
|
+
}
|
|
186
|
+
|
|
171
187
|
function fillCheckedValue(
|
|
172
188
|
instance: object,
|
|
173
189
|
check: PropertyCheck,
|
|
@@ -175,8 +191,7 @@ function fillCheckedValue(
|
|
|
175
191
|
value: unknown,
|
|
176
192
|
): void {
|
|
177
193
|
assertValueFits(instance, check, value);
|
|
178
|
-
check
|
|
179
|
-
check.write(gValue, value);
|
|
194
|
+
writeHeld(check, gValue, value);
|
|
180
195
|
assertValueValidates(instance, check, value, gValue);
|
|
181
196
|
}
|
|
182
197
|
|
|
@@ -224,11 +239,11 @@ function readStored(instance: Record<symbol, unknown>, accessor: PropertyAccesso
|
|
|
224
239
|
}
|
|
225
240
|
|
|
226
241
|
function storeValue(instance: Record<symbol, unknown>, accessor: PropertyAccessor, value: unknown): void {
|
|
227
|
-
instance[accessor.storage] = value;
|
|
242
|
+
instance[accessor.storage] = heldValue(value);
|
|
228
243
|
}
|
|
229
244
|
|
|
230
245
|
function writeStored(instance: Record<symbol, unknown>, accessor: PropertyAccessor, value: unknown): void {
|
|
231
|
-
if (readStored(instance, accessor) === value) {
|
|
246
|
+
if (readStored(instance, accessor) === heldValue(value)) {
|
|
232
247
|
return;
|
|
233
248
|
}
|
|
234
249
|
|
|
@@ -239,13 +254,12 @@ function writeStored(instance: Record<symbol, unknown>, accessor: PropertyAccess
|
|
|
239
254
|
function writeProperty(instance: object, accessor: PropertyAccessor, value: unknown): void {
|
|
240
255
|
const stored = instance as Record<symbol, unknown>;
|
|
241
256
|
|
|
242
|
-
if (readStored(stored, accessor) === value) {
|
|
257
|
+
if (readStored(stored, accessor) === heldValue(value)) {
|
|
243
258
|
return;
|
|
244
259
|
}
|
|
245
260
|
|
|
246
261
|
assertValueAccepted(instance, accessor, value);
|
|
247
|
-
|
|
248
|
-
(instance as NotifyingObject).notify?.(accessor.propertyName);
|
|
262
|
+
writeStored(stored, accessor, accessor.narrowValue(value));
|
|
249
263
|
}
|
|
250
264
|
|
|
251
265
|
function storedGetter(accessor: PropertyAccessor): (this: object) => unknown {
|
|
@@ -431,7 +445,7 @@ function makeGetProperty(dispatch: PropertyDispatch) {
|
|
|
431
445
|
const accessor = resolveAccessor(dispatch, propertyId, pspec);
|
|
432
446
|
const current = readCurrent(this, accessor);
|
|
433
447
|
assertValueServes(this, accessor, current);
|
|
434
|
-
|
|
448
|
+
writeHeld(accessor, getHandle(value), current);
|
|
435
449
|
};
|
|
436
450
|
}
|
|
437
451
|
|
package/src/register-class.ts
CHANGED
|
@@ -134,6 +134,36 @@ type RegisterClassOptions<TInstance extends object, TProperties extends Record<s
|
|
|
134
134
|
* which a `freeze_notify` batch collects; a write of the value the property already holds is
|
|
135
135
|
* dropped and emits none.
|
|
136
136
|
*
|
|
137
|
+
* `null` and `undefined` both mean NULL, and mean it only where the ParamSpec's own type holds
|
|
138
|
+
* NULL, which is a string, string-array, boxed, object, interface, param, variant or pointer
|
|
139
|
+
* property. Such a property holds the `null` either spelling wrote, so the member, the type's
|
|
140
|
+
* `get_property` slot and `g_object_get_property` serve the same thing, and writing the other
|
|
141
|
+
* spelling over it emits no `notify`. Every other property, so every integer, floating-point,
|
|
142
|
+
* boolean, enum, flags and GType one, refuses both with the same `TypeError` it refuses a
|
|
143
|
+
* string with, and keeps the value it already holds. It refuses them for what it holds rather
|
|
144
|
+
* than for its range, whatever that range is: the type is checked before the range, so a
|
|
145
|
+
* `gint` whose range excludes 0 answers a nullish with that same `TypeError` and never with
|
|
146
|
+
* the `RangeError` that names the value GObject would put in its place. The one place
|
|
147
|
+
* `undefined` means something else is the constructor, which reads it as the property not
|
|
148
|
+
* being given at all and leaves it at the ParamSpec's default, so a property is never handed a
|
|
149
|
+
* value it cannot serve back.
|
|
150
|
+
*
|
|
151
|
+
* A floating-point property takes every JavaScript number, `NaN` and both infinities
|
|
152
|
+
* included, and its ParamSpec alone rules on which of them the range admits: a `gdouble`
|
|
153
|
+
* bounded by `-Infinity` and `Infinity` holds either infinity, and a magnitude a bounded
|
|
154
|
+
* one excludes, like any `NaN`, comes back as the `RangeError` that names what GObject
|
|
155
|
+
* would put in its place rather than as the `TypeError` a type the property cannot hold
|
|
156
|
+
* earns. A `gfloat` property holds what GObject narrows the double to, so it serves `0.1`
|
|
157
|
+
* back as `0.10000000149011612` and a finite magnitude no `gfloat` reaches as an infinity,
|
|
158
|
+
* which the range then rules on in turn. That narrowing belongs to the property alone: the
|
|
159
|
+
* same magnitude written to a `gfloat` through a generated binding, a signal argument or a
|
|
160
|
+
* closure return is refused outright rather than narrowed.
|
|
161
|
+
*
|
|
162
|
+
* A generated property of a wrapped type answers a nullish differently, and the two halves of
|
|
163
|
+
* the API disagree here: that property marshals what it is written through its descriptor
|
|
164
|
+
* rather than through the checks above, so `new Gtk.Label({ widthRequest: null })` and a later
|
|
165
|
+
* write of `null` to that member both land 0, where a `gint` installed here refuses both.
|
|
166
|
+
*
|
|
137
167
|
* A class that defines the camelCase member itself owns the property: its own accessor decides
|
|
138
168
|
* what a write means, the other two spellings forward to it, and the type's property slots read
|
|
139
169
|
* and write it rather than the generated storage.
|
|
@@ -166,7 +196,11 @@ type PropertyVfuncSpec = {
|
|
|
166
196
|
makeDispatch: (dispatch: PropertyDispatch) => VfuncFn;
|
|
167
197
|
};
|
|
168
198
|
|
|
199
|
+
type ArgPatch = { isCallerAllocated: true } | { isCallScoped: true };
|
|
200
|
+
|
|
201
|
+
const INSTANCE_ARG_INDEX = 0;
|
|
169
202
|
const VALUE_ARG_INDEX = 2;
|
|
203
|
+
const TEARDOWN_VFUNC_NAMES: Set<string> = new Set(["dispose", "finalize"]);
|
|
170
204
|
|
|
171
205
|
const PROPERTY_VFUNC_SPECS: PropertyVfuncSpec[] = [
|
|
172
206
|
{ methodName: GET_PROPERTY_VFUNC, isValueOut: true, makeDispatch: makeGetProperty },
|
|
@@ -345,10 +379,13 @@ function buildDiscoveredVfunc(
|
|
|
345
379
|
return undefined;
|
|
346
380
|
}
|
|
347
381
|
|
|
382
|
+
const argDescriptors = slotArgDescriptors(descriptor);
|
|
383
|
+
|
|
348
384
|
return {
|
|
349
385
|
...descriptor,
|
|
350
386
|
methodName,
|
|
351
|
-
|
|
387
|
+
argDescriptors,
|
|
388
|
+
fn: wrapVfunc(fn, argDescriptors, descriptor),
|
|
352
389
|
};
|
|
353
390
|
}
|
|
354
391
|
|
|
@@ -381,9 +418,13 @@ function discoverClassVfuncs(klass: AnyClass, methods: MethodTable): DiscoveredV
|
|
|
381
418
|
function wrapVfunc(
|
|
382
419
|
fn: VfuncFn,
|
|
383
420
|
argDescriptors: NativeRegisterClassVfunc["argDescriptors"],
|
|
384
|
-
|
|
421
|
+
descriptor: VfuncDescriptor,
|
|
385
422
|
): VfuncFn {
|
|
386
|
-
return wrapCallback(
|
|
423
|
+
return wrapCallback(
|
|
424
|
+
fn as (...args: unknown[]) => unknown,
|
|
425
|
+
{ argDescriptors, returnDescriptor: descriptor.returnDescriptor },
|
|
426
|
+
"vfunc",
|
|
427
|
+
);
|
|
387
428
|
}
|
|
388
429
|
|
|
389
430
|
function discoverInterfaceBindings(
|
|
@@ -443,9 +484,23 @@ function propertyVfuncs(source: PropertyVfuncSource): DiscoveredVfunc[] {
|
|
|
443
484
|
);
|
|
444
485
|
}
|
|
445
486
|
|
|
446
|
-
function
|
|
447
|
-
return argDescriptors.map((arg,
|
|
448
|
-
|
|
487
|
+
function markArg(argDescriptors: Descriptor[], index: number, patch: ArgPatch): Descriptor[] {
|
|
488
|
+
return argDescriptors.map((arg, at) => (at === index ? { ...arg, ...patch } : arg));
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
function isTeardownSlot(descriptor: VfuncDescriptor): boolean {
|
|
492
|
+
return (
|
|
493
|
+
TEARDOWN_VFUNC_NAMES.has(descriptor.vfuncName) &&
|
|
494
|
+
descriptor.argDescriptors[INSTANCE_ARG_INDEX]?.kind === "object"
|
|
495
|
+
);
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
function slotArgDescriptors(descriptor: VfuncDescriptor): Descriptor[] {
|
|
499
|
+
if (!isTeardownSlot(descriptor)) {
|
|
500
|
+
return descriptor.argDescriptors;
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
return markArg(descriptor.argDescriptors, INSTANCE_ARG_INDEX, { isCallScoped: true });
|
|
449
504
|
}
|
|
450
505
|
|
|
451
506
|
function buildPropertyVfunc(
|
|
@@ -461,14 +516,14 @@ function buildPropertyVfunc(
|
|
|
461
516
|
}
|
|
462
517
|
|
|
463
518
|
const argDescriptors = isValueOut
|
|
464
|
-
?
|
|
519
|
+
? markArg(descriptor.argDescriptors, VALUE_ARG_INDEX, { isCallerAllocated: true })
|
|
465
520
|
: descriptor.argDescriptors;
|
|
466
521
|
|
|
467
522
|
return {
|
|
468
523
|
...descriptor,
|
|
469
524
|
methodName,
|
|
470
525
|
argDescriptors,
|
|
471
|
-
fn: wrapVfunc(fn, argDescriptors, descriptor
|
|
526
|
+
fn: wrapVfunc(fn, argDescriptors, descriptor),
|
|
472
527
|
};
|
|
473
528
|
}
|
|
474
529
|
|
package/src/registry.ts
CHANGED
|
@@ -12,10 +12,12 @@ import { TYPE_INVALID, type TypedClass, typeInterfaces, typeIsA, typeName, typeP
|
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
14
|
* Static side of class `C` with its construct signature preserved but the member
|
|
15
|
-
* named `K` (default `"new"`) removed.
|
|
15
|
+
* named `K` (default `"new"`) removed. The signature is kept abstract so an abstract
|
|
16
|
+
* `C` survives it: a subclass extends the result, and only the subclass's own
|
|
17
|
+
* declaration decides whether `new` reaches it.
|
|
16
18
|
*/
|
|
17
19
|
type StaticBase<C, K extends PropertyKey = "new"> = Omit<C, K> &
|
|
18
|
-
(C extends new (...args: infer A) => infer R ? new (...args: A) => R : never);
|
|
20
|
+
(C extends abstract new (...args: infer A) => infer R ? abstract new (...args: A) => R : never);
|
|
19
21
|
|
|
20
22
|
/** One overridable vtable slot: where it sits in the vtable struct and how it is marshalled. */
|
|
21
23
|
type VfuncDescriptor = {
|
|
@@ -35,6 +37,12 @@ type VfuncDescriptor = {
|
|
|
35
37
|
argDescriptors: NativeRegisterClassVfunc["argDescriptors"];
|
|
36
38
|
/** Descriptor for the value the slot returns. */
|
|
37
39
|
returnDescriptor: NativeRegisterClassVfunc["returnDescriptor"];
|
|
40
|
+
/**
|
|
41
|
+
* GIR marks the slot's return value as one the bindings do not surface, so a call through the
|
|
42
|
+
* slot drops it and an implementation of the slot reports success in its place, while
|
|
43
|
+
* `VfuncDescriptor.returnDescriptor` keeps the C type the slot is called against either way.
|
|
44
|
+
*/
|
|
45
|
+
isReturnSkipped?: boolean;
|
|
38
46
|
/**
|
|
39
47
|
* The slot takes a trailing `GError**` that `VfuncDescriptor.argDescriptors` leaves out, the
|
|
40
48
|
* way GIR leaves it out of a callable's parameters. A call through the slot has to append it
|
|
@@ -48,6 +56,7 @@ type VfuncDescriptor = {
|
|
|
48
56
|
* overrides each one.
|
|
49
57
|
*/
|
|
50
58
|
type VfuncRegistry = Record<string, VfuncDescriptor>;
|
|
59
|
+
type WrapperBinding = (handle: ExternalObject<Handle>, instance: object) => void;
|
|
51
60
|
|
|
52
61
|
/**
|
|
53
62
|
* How one property an interface declares reaches the vtable, given as the interface's own accessor
|
|
@@ -320,24 +329,18 @@ function resolveComposedClass(runtimeType: bigint): AnyClass | null {
|
|
|
320
329
|
}
|
|
321
330
|
|
|
322
331
|
function wrapObject(value: unknown): object | null {
|
|
323
|
-
|
|
324
|
-
return null;
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
return getOrCreateWrapper(value as ExternalObject<Handle>);
|
|
332
|
+
return value == null ? null : getOrCreateWrapper(value as ExternalObject<Handle>);
|
|
328
333
|
}
|
|
329
334
|
|
|
330
|
-
function
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
const existing = getWrapper(handle);
|
|
335
|
+
function wrapCallScopedObject(value: unknown): object | null {
|
|
336
|
+
return value == null ? null : wrapperFor(value as ExternalObject<Handle>, bindCallScopedWrapper);
|
|
337
|
+
}
|
|
336
338
|
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
339
|
+
function existingWrapperFor(handle: ExternalObject<Handle>): object | null {
|
|
340
|
+
return handleMap.has(handle) ? handle : getWrapper(handle);
|
|
341
|
+
}
|
|
340
342
|
|
|
343
|
+
function createWrapper(handle: ExternalObject<Handle>): object {
|
|
341
344
|
const runtimeType: bigint = getType(handle);
|
|
342
345
|
|
|
343
346
|
if (runtimeType === TYPE_INVALID) {
|
|
@@ -350,12 +353,26 @@ function getOrCreateWrapper(handle: ExternalObject<Handle>): object {
|
|
|
350
353
|
throw new Error(`Expected registered GLib type, got type ${String(runtimeType)}`);
|
|
351
354
|
}
|
|
352
355
|
|
|
353
|
-
|
|
354
|
-
|
|
356
|
+
return Object.create(cls.prototype) as object;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
function wrapperFor(handle: ExternalObject<Handle>, bind: WrapperBinding): object {
|
|
360
|
+
const existing = existingWrapperFor(handle);
|
|
361
|
+
|
|
362
|
+
if (existing) {
|
|
363
|
+
return existing;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
const instance = createWrapper(handle);
|
|
367
|
+
bind(handle, instance);
|
|
355
368
|
|
|
356
369
|
return instance;
|
|
357
370
|
}
|
|
358
371
|
|
|
372
|
+
function getOrCreateWrapper(handle: ExternalObject<Handle>): object {
|
|
373
|
+
return wrapperFor(handle, registerWrapper);
|
|
374
|
+
}
|
|
375
|
+
|
|
359
376
|
function instanceClassName(instance: object): string {
|
|
360
377
|
return (instance as { constructor?: { name?: string } }).constructor?.name ?? "object";
|
|
361
378
|
}
|
|
@@ -376,6 +393,10 @@ function setHandle(instance: object, handle: ExternalObject<Handle>): void {
|
|
|
376
393
|
handleMap.set(instance, handle);
|
|
377
394
|
}
|
|
378
395
|
|
|
396
|
+
function bindCallScopedWrapper(handle: ExternalObject<Handle>, instance: object): void {
|
|
397
|
+
setHandle(instance, handle);
|
|
398
|
+
}
|
|
399
|
+
|
|
379
400
|
function registerWrapper(handle: ExternalObject<Handle>, instance: object): void {
|
|
380
401
|
setHandle(instance, handle);
|
|
381
402
|
setWrapper(handle, instance);
|
|
@@ -416,6 +437,7 @@ export {
|
|
|
416
437
|
instanceClassName,
|
|
417
438
|
registerWrapper,
|
|
418
439
|
resolveWrapperType,
|
|
440
|
+
wrapCallScopedObject,
|
|
419
441
|
wrapObject,
|
|
420
442
|
type InterfaceProperty,
|
|
421
443
|
type StaticBase,
|
package/src/signal.ts
CHANGED
|
@@ -43,6 +43,11 @@ type SignalConnectSpec = {
|
|
|
43
43
|
isAfter: boolean;
|
|
44
44
|
};
|
|
45
45
|
|
|
46
|
+
type EmitValues = {
|
|
47
|
+
values: ExternalObject<Handle>[];
|
|
48
|
+
reads: (() => unknown)[];
|
|
49
|
+
};
|
|
50
|
+
|
|
46
51
|
/** One argument of a signal emission: how to marshal it, plus the value to marshal. */
|
|
47
52
|
type EmitArg = Arg & {
|
|
48
53
|
/**
|
|
@@ -194,7 +199,7 @@ function connectBind(type: bigint, signal: string, callback: CallbackDescriptor)
|
|
|
194
199
|
*/
|
|
195
200
|
function connectSignal(instance: object, signal: string, spec: SignalConnectSpec): number {
|
|
196
201
|
const { callback, handler, isAfter } = spec;
|
|
197
|
-
const wrapped = wrapCallback(handler, callback, "
|
|
202
|
+
const wrapped = wrapCallback(handler, callback, "signal");
|
|
198
203
|
const type: bigint = (instance as TypedClass).__type__;
|
|
199
204
|
const connect = connectBind(type, signal, callback);
|
|
200
205
|
const handlerId = connect(getHandle(instance), signal, wrapped, isAfter ? 1 : 0) as number;
|
|
@@ -221,47 +226,46 @@ const createEmitValue = (arg: EmitArg): { value: ExternalObject<Handle>; read?:
|
|
|
221
226
|
return isInoutArg(arg) ? outValueForDescriptor(arg.type, arg.value) : outValueForDescriptor(arg.type);
|
|
222
227
|
};
|
|
223
228
|
|
|
229
|
+
const collectEmitValues = (instance: object, args: EmitArg[]): EmitValues => {
|
|
230
|
+
const collected: EmitValues = { values: [toValue(objectT("full"), instance)], reads: [] };
|
|
231
|
+
|
|
232
|
+
for (const arg of args) {
|
|
233
|
+
const { value, read } = createEmitValue(arg);
|
|
234
|
+
collected.values.push(value);
|
|
235
|
+
|
|
236
|
+
if (read) {
|
|
237
|
+
collected.reads.push(read);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
return collected;
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
const readEmitOutputs = (reads: (() => unknown)[]): unknown[] => reads.map((read) => read());
|
|
245
|
+
|
|
224
246
|
/**
|
|
225
247
|
* Emits a signal on an instance with the given arguments and returns its result
|
|
226
248
|
* combined with any output-argument values.
|
|
227
249
|
* @param instance Emitter to emit the signal on.
|
|
228
250
|
* @param signal Signal name, optionally including a `::detail` suffix.
|
|
229
251
|
* @param args Arguments to pass, including output and inout arguments.
|
|
230
|
-
* @param
|
|
252
|
+
* @param returns Descriptor for the signal's return value, omitted when it returns void.
|
|
231
253
|
*/
|
|
232
|
-
function emitSignal(instance: object, signal: string, args: EmitArg[],
|
|
254
|
+
function emitSignal(instance: object, signal: string, args: EmitArg[], returns?: Descriptor): unknown {
|
|
233
255
|
const signalId = getSignalId(instance, signal);
|
|
234
256
|
const detail = getSignalDetailQuark(signal);
|
|
235
|
-
const values
|
|
236
|
-
const reads: (() => unknown)[] = [];
|
|
237
|
-
|
|
238
|
-
for (const arg of args) {
|
|
239
|
-
const { value, read } = createEmitValue(arg);
|
|
240
|
-
values.push(value);
|
|
257
|
+
const { values, reads } = collectEmitValues(instance, args);
|
|
241
258
|
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
}
|
|
245
|
-
}
|
|
259
|
+
if (returns === undefined) {
|
|
260
|
+
gSignalEmitv(values, signalId, detail, undefined);
|
|
246
261
|
|
|
247
|
-
|
|
248
|
-
const returnValue = newValueForDescriptor(returnDescriptor);
|
|
249
|
-
gSignalEmitv(values, signalId, detail, returnValue);
|
|
250
|
-
|
|
251
|
-
return packTupleResult(
|
|
252
|
-
reads.map((read) => read()),
|
|
253
|
-
fromValue(returnValue),
|
|
254
|
-
true,
|
|
255
|
-
);
|
|
262
|
+
return packTupleResult(readEmitOutputs(reads), undefined, false);
|
|
256
263
|
}
|
|
257
264
|
|
|
258
|
-
|
|
265
|
+
const returnValue = newValueForDescriptor(returns);
|
|
266
|
+
gSignalEmitv(values, signalId, detail, returnValue);
|
|
259
267
|
|
|
260
|
-
return packTupleResult(
|
|
261
|
-
reads.map((read) => read()),
|
|
262
|
-
undefined,
|
|
263
|
-
false,
|
|
264
|
-
);
|
|
268
|
+
return packTupleResult(readEmitOutputs(reads), fromValue(returnValue), true);
|
|
265
269
|
}
|
|
266
270
|
|
|
267
271
|
export {
|
package/src/t.ts
CHANGED
|
@@ -77,6 +77,8 @@ type T = {
|
|
|
77
77
|
sizedArray: typeof helpers.sizedArrayT;
|
|
78
78
|
/** Builds a descriptor for a C array of a fixed length. */
|
|
79
79
|
fixedArray: typeof helpers.fixedArrayT;
|
|
80
|
+
/** Builds a descriptor for an out pointer into the buffer another argument supplied. */
|
|
81
|
+
cursorArray: typeof helpers.cursorArrayT;
|
|
80
82
|
/** Builds a descriptor for a function pointer, marshalling a JavaScript function into a native closure. */
|
|
81
83
|
callback: typeof helpers.callbackT;
|
|
82
84
|
/**
|
|
@@ -127,6 +129,7 @@ const t: T = {
|
|
|
127
129
|
byteArray: helpers.byteArrayT,
|
|
128
130
|
sizedArray: helpers.sizedArrayT,
|
|
129
131
|
fixedArray: helpers.fixedArrayT,
|
|
132
|
+
cursorArray: helpers.cursorArrayT,
|
|
130
133
|
callback: helpers.callbackT,
|
|
131
134
|
fn,
|
|
132
135
|
};
|
package/src/tuple.ts
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
import type { Descriptor } from "@gtkx/native";
|
|
2
|
+
|
|
3
|
+
type SplitResult = { primary: unknown; outValues: unknown[] };
|
|
4
|
+
|
|
5
|
+
const hasSurfacedPrimary = (returnDescriptor: Descriptor, isReturnSkipped: boolean | undefined): boolean =>
|
|
6
|
+
returnDescriptor.kind !== "void" && isReturnSkipped !== true;
|
|
7
|
+
|
|
1
8
|
const packTupleResult = (outs: unknown[], primary: unknown, hasPrimary: boolean): unknown => {
|
|
2
9
|
if (hasPrimary) {
|
|
3
10
|
return outs.length === 0 ? primary : [primary, ...outs];
|
|
@@ -14,19 +21,15 @@ const packTupleResult = (outs: unknown[], primary: unknown, hasPrimary: boolean)
|
|
|
14
21
|
return outs;
|
|
15
22
|
};
|
|
16
23
|
|
|
17
|
-
const
|
|
18
|
-
result
|
|
19
|
-
|
|
20
|
-
outCount: number,
|
|
21
|
-
): { primary: unknown; outValues: unknown[] } => {
|
|
22
|
-
if (hasPrimary) {
|
|
23
|
-
if (Array.isArray(result)) {
|
|
24
|
-
return { primary: result[0], outValues: result.slice(1) };
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
return { primary: result, outValues: [] };
|
|
24
|
+
const splitPrimaryResult = (result: unknown, outCount: number): SplitResult => {
|
|
25
|
+
if (outCount > 0 && Array.isArray(result)) {
|
|
26
|
+
return { primary: result[0], outValues: result.slice(1) };
|
|
28
27
|
}
|
|
29
28
|
|
|
29
|
+
return { primary: result, outValues: [] };
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const splitOutResult = (result: unknown, outCount: number): SplitResult => {
|
|
30
33
|
if (outCount === 1) {
|
|
31
34
|
return { primary: undefined, outValues: [result] };
|
|
32
35
|
}
|
|
@@ -34,4 +37,7 @@ const splitTupleResult = (
|
|
|
34
37
|
return { primary: undefined, outValues: Array.isArray(result) ? result : [] };
|
|
35
38
|
};
|
|
36
39
|
|
|
37
|
-
|
|
40
|
+
const splitTupleResult = (result: unknown, hasPrimary: boolean, outCount: number): SplitResult =>
|
|
41
|
+
hasPrimary ? splitPrimaryResult(result, outCount) : splitOutResult(result, outCount);
|
|
42
|
+
|
|
43
|
+
export { hasSurfacedPrimary, packTupleResult, splitTupleResult };
|