@gtkx/runtime 1.2.2 → 1.4.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 +6 -4
- package/dist/arg.d.ts +6 -1
- package/dist/arg.d.ts.map +1 -1
- package/dist/arg.js +2 -1
- package/dist/arg.js.map +1 -1
- package/dist/closure.d.ts.map +1 -1
- package/dist/closure.js +2 -11
- package/dist/closure.js.map +1 -1
- package/dist/descriptors.d.ts +21 -3
- package/dist/descriptors.d.ts.map +1 -1
- package/dist/descriptors.js +50 -21
- package/dist/descriptors.js.map +1 -1
- package/dist/field.d.ts +42 -0
- package/dist/field.d.ts.map +1 -0
- package/dist/field.js +41 -0
- package/dist/field.js.map +1 -0
- package/dist/fn.d.ts +2 -0
- package/dist/fn.d.ts.map +1 -1
- package/dist/fn.js +50 -10
- package/dist/fn.js.map +1 -1
- package/dist/index.d.ts +14 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +13 -5
- package/dist/index.js.map +1 -1
- package/dist/mixin.d.ts +2 -1
- package/dist/mixin.d.ts.map +1 -1
- package/dist/mixin.js +1 -1
- package/dist/mixin.js.map +1 -1
- package/dist/native-value.d.ts +9 -3
- package/dist/native-value.d.ts.map +1 -1
- package/dist/native-value.js +86 -9
- package/dist/native-value.js.map +1 -1
- package/dist/object.d.ts +14 -6
- package/dist/object.d.ts.map +1 -1
- package/dist/object.js +21 -10
- package/dist/object.js.map +1 -1
- package/dist/param-spec.d.ts +32 -1
- package/dist/param-spec.d.ts.map +1 -1
- package/dist/param-spec.js +67 -5
- package/dist/param-spec.js.map +1 -1
- package/dist/promisify.d.ts +13 -1
- package/dist/promisify.d.ts.map +1 -1
- package/dist/promisify.js +14 -1
- package/dist/promisify.js.map +1 -1
- package/dist/properties.d.ts +48 -5
- package/dist/properties.d.ts.map +1 -1
- package/dist/properties.js +191 -20
- package/dist/properties.js.map +1 -1
- package/dist/regex.d.ts +25 -0
- package/dist/regex.d.ts.map +1 -0
- package/dist/regex.js +59 -0
- package/dist/regex.js.map +1 -0
- package/dist/register-class.d.ts +165 -15
- package/dist/register-class.d.ts.map +1 -1
- package/dist/register-class.js +175 -19
- package/dist/register-class.js.map +1 -1
- package/dist/registry.d.ts +48 -4
- package/dist/registry.d.ts.map +1 -1
- package/dist/registry.js +147 -3
- package/dist/registry.js.map +1 -1
- package/dist/signal.d.ts +11 -1
- package/dist/signal.d.ts.map +1 -1
- package/dist/signal.js +66 -6
- package/dist/signal.js.map +1 -1
- package/dist/t.d.ts +11 -0
- package/dist/t.d.ts.map +1 -1
- package/dist/t.js +3 -0
- package/dist/t.js.map +1 -1
- package/dist/type.d.ts +2 -1
- package/dist/type.d.ts.map +1 -1
- package/dist/type.js +5 -1
- package/dist/type.js.map +1 -1
- package/dist/value.d.ts +48 -4
- package/dist/value.d.ts.map +1 -1
- package/dist/value.js +172 -16
- package/dist/value.js.map +1 -1
- package/dist/variant.d.ts +75 -26
- package/dist/variant.d.ts.map +1 -1
- package/dist/variant.js +44 -29
- package/dist/variant.js.map +1 -1
- package/package.json +5 -5
- package/src/arg.ts +6 -1
- package/src/closure.ts +2 -14
- package/src/descriptors.ts +74 -21
- package/src/field.ts +76 -0
- package/src/fn.ts +73 -17
- package/src/index.ts +46 -5
- package/src/mixin.ts +1 -1
- package/src/native-value.ts +146 -14
- package/src/object.ts +23 -11
- package/src/param-spec.ts +96 -8
- package/src/promisify.ts +27 -1
- package/src/properties.ts +280 -32
- package/src/regex.ts +92 -0
- package/src/register-class.ts +485 -42
- package/src/registry.ts +219 -4
- package/src/signal.ts +117 -4
- package/src/t.ts +13 -0
- package/src/type.ts +7 -0
- package/src/value.ts +268 -14
- package/src/variant.ts +184 -53
package/src/fn.ts
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
|
-
import type { CallDescriptor, Descriptor, ExternalObject, Ref } from "@gtkx/native";
|
|
1
|
+
import type { CallDescriptor, Descriptor, ExternalObject, Handle, Ref } from "@gtkx/native";
|
|
2
2
|
import { call, bind as nativeBind } from "@gtkx/native";
|
|
3
3
|
import type { RefSeeds } from "./vfunc-seeds.js";
|
|
4
|
-
import { type Arg, isCallerAllocatedArg, isOutputArg, isRefArg, requiresInputArg } from "./arg.js";
|
|
4
|
+
import { type Arg, isCallerAllocatedArg, isOutputArg, isRefArg, isUnpackedArg, requiresInputArg } from "./arg.js";
|
|
5
5
|
import { wrapCallbackValue } from "./callback.js";
|
|
6
|
-
import { boxedT, refT } from "./descriptors.js";
|
|
6
|
+
import { boxedT, isGtypeDescriptor, refT } from "./descriptors.js";
|
|
7
7
|
import { checkError } from "./error.js";
|
|
8
8
|
import { LIB } from "./library.js";
|
|
9
|
-
import { fromNative } from "./native-value.js";
|
|
9
|
+
import { fromNative, toNative } from "./native-value.js";
|
|
10
10
|
import { getHandle } from "./registry.js";
|
|
11
11
|
import { hasSurfacedPrimary, packTupleResult } from "./tuple.js";
|
|
12
|
+
import { TYPE_INVALID } from "./type.js";
|
|
13
|
+
import { fromValue, getValueType } from "./value.js";
|
|
12
14
|
|
|
13
15
|
/** The signature a native function is bound against. */
|
|
14
16
|
type FnSpec = {
|
|
@@ -29,6 +31,8 @@ type FnSpec = {
|
|
|
29
31
|
* architectures.
|
|
30
32
|
*/
|
|
31
33
|
fixedArgCount?: number;
|
|
34
|
+
/** The callee returns a `GValue`, surfaced as what it holds rather than as the value itself. */
|
|
35
|
+
isReturnUnpacked?: boolean;
|
|
32
36
|
};
|
|
33
37
|
|
|
34
38
|
type ArgSpec = {
|
|
@@ -36,13 +40,18 @@ type ArgSpec = {
|
|
|
36
40
|
index: number;
|
|
37
41
|
isRef: boolean;
|
|
38
42
|
isCallerAllocated: boolean;
|
|
43
|
+
isUnpacked: boolean;
|
|
39
44
|
requiresInput: boolean;
|
|
40
45
|
inputIndex: number;
|
|
41
46
|
isOutParam: boolean;
|
|
47
|
+
carriesGtype: boolean;
|
|
42
48
|
};
|
|
43
49
|
|
|
44
50
|
const NO_OUT_PARAMS: unknown[] = [];
|
|
45
51
|
|
|
52
|
+
const hasGtypeInput = (descriptor: Descriptor): boolean =>
|
|
53
|
+
isGtypeDescriptor(descriptor) || (descriptor.kind === "array" && isGtypeDescriptor(descriptor.itemDescriptor));
|
|
54
|
+
|
|
46
55
|
const buildNativeArgTypes = (args: Arg[], canThrow: boolean): Descriptor[] => {
|
|
47
56
|
const nativeArgTypes = args.map((argSpec) =>
|
|
48
57
|
argSpec.direction !== undefined && argSpec.isCallerAllocated !== true ? refT(argSpec.type) : argSpec.type,
|
|
@@ -70,9 +79,11 @@ const buildArgSpecs = (args: Arg[]): ArgSpec[] => {
|
|
|
70
79
|
index,
|
|
71
80
|
isRef,
|
|
72
81
|
isCallerAllocated: isCallerAllocatedArg(arg),
|
|
82
|
+
isUnpacked: isUnpackedArg(arg),
|
|
73
83
|
requiresInput,
|
|
74
84
|
inputIndex: requiresInput ? inputCursor++ : -1,
|
|
75
85
|
isOutParam,
|
|
86
|
+
carriesGtype: hasGtypeInput(arg.type),
|
|
76
87
|
};
|
|
77
88
|
});
|
|
78
89
|
};
|
|
@@ -83,8 +94,11 @@ const resolveCallerAllocated = (inputs: unknown[], inputIndex: number): unknown
|
|
|
83
94
|
return wrapper == null ? wrapper : getHandle(wrapper);
|
|
84
95
|
};
|
|
85
96
|
|
|
97
|
+
const inputValueFor = (spec: ArgSpec, inputs: unknown[]): unknown =>
|
|
98
|
+
spec.carriesGtype ? toNative(spec.arg.type, inputs[spec.inputIndex]) : inputs[spec.inputIndex];
|
|
99
|
+
|
|
86
100
|
const buildRefValue = (spec: ArgSpec, inputs: unknown[], seeds: RefSeeds | undefined): Ref => ({
|
|
87
|
-
value: spec.requiresInput ?
|
|
101
|
+
value: spec.requiresInput ? inputValueFor(spec, inputs) : (seeds?.get(spec.index) ?? null),
|
|
88
102
|
});
|
|
89
103
|
|
|
90
104
|
const buildNativeValue = (spec: ArgSpec, inputs: unknown[], seeds: RefSeeds | undefined): unknown => {
|
|
@@ -102,12 +116,30 @@ const buildNativeValue = (spec: ArgSpec, inputs: unknown[], seeds: RefSeeds | un
|
|
|
102
116
|
return wrapCallbackValue(arg.type, inputs[inputIndex]);
|
|
103
117
|
}
|
|
104
118
|
|
|
105
|
-
return inputs
|
|
119
|
+
return inputValueFor(spec, inputs);
|
|
106
120
|
};
|
|
107
121
|
|
|
108
122
|
const buildNativeValues = (plans: ArgSpec[], inputs: unknown[], seeds: RefSeeds | undefined): unknown[] =>
|
|
109
123
|
plans.map((plan) => buildNativeValue(plan, inputs, seeds));
|
|
110
124
|
|
|
125
|
+
const unpackValueHandle = (handle: ExternalObject<Handle> | null): unknown => {
|
|
126
|
+
if (handle === null || getValueType(handle) === TYPE_INVALID) {
|
|
127
|
+
return null;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return fromValue(handle);
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
const readCallerAllocated = (plan: ArgSpec, inputs: unknown[]): unknown => {
|
|
134
|
+
const wrapper = inputs[plan.inputIndex];
|
|
135
|
+
|
|
136
|
+
if (!plan.isUnpacked) {
|
|
137
|
+
return wrapper;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return unpackValueHandle(wrapper == null ? null : getHandle(wrapper));
|
|
141
|
+
};
|
|
142
|
+
|
|
111
143
|
const readOutParams = (outPlans: ArgSpec[], inputs: unknown[], nativeValues: unknown[]): unknown[] => {
|
|
112
144
|
if (outPlans.length === 0) {
|
|
113
145
|
return NO_OUT_PARAMS;
|
|
@@ -115,7 +147,7 @@ const readOutParams = (outPlans: ArgSpec[], inputs: unknown[], nativeValues: unk
|
|
|
115
147
|
|
|
116
148
|
return Array.from(outPlans, (plan) =>
|
|
117
149
|
plan.isCallerAllocated
|
|
118
|
-
? inputs
|
|
150
|
+
? readCallerAllocated(plan, inputs)
|
|
119
151
|
: fromNative(plan.arg.type, (nativeValues[plan.index] as Ref).value));
|
|
120
152
|
};
|
|
121
153
|
|
|
@@ -123,8 +155,20 @@ const isPassThroughPlan = (plan: ArgSpec, index: number): boolean =>
|
|
|
123
155
|
plan.inputIndex === index &&
|
|
124
156
|
!plan.isRef &&
|
|
125
157
|
!plan.isCallerAllocated &&
|
|
158
|
+
!plan.carriesGtype &&
|
|
126
159
|
plan.arg.type.kind !== "callback";
|
|
127
160
|
|
|
161
|
+
const requiredInputIndices = (plans: ArgSpec[]): number[] =>
|
|
162
|
+
plans.filter((plan) => plan.requiresInput && plan.arg.isRequired === true).map((plan) => plan.inputIndex);
|
|
163
|
+
|
|
164
|
+
const assertRequiredInputs = (requiredIndices: number[], inputs: unknown[]): void => {
|
|
165
|
+
for (const index of requiredIndices) {
|
|
166
|
+
if (inputs[index] === undefined) {
|
|
167
|
+
throw new TypeError(`Missing required argument at position ${String(index + 1)}`);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
};
|
|
171
|
+
|
|
128
172
|
const resizeInputs = (inputs: unknown[], argCount: number): unknown[] => {
|
|
129
173
|
while (inputs.length < argCount) {
|
|
130
174
|
inputs.push(undefined);
|
|
@@ -137,14 +181,25 @@ const resizeInputs = (inputs: unknown[], argCount: number): unknown[] => {
|
|
|
137
181
|
return inputs;
|
|
138
182
|
};
|
|
139
183
|
|
|
184
|
+
const returnReader = (spec: FnSpec): ((nativeResult: unknown) => unknown) =>
|
|
185
|
+
spec.isReturnUnpacked === true
|
|
186
|
+
? (nativeResult) => unpackValueHandle((nativeResult ?? null) as ExternalObject<Handle> | null)
|
|
187
|
+
: (nativeResult) => fromNative(spec.returns, nativeResult);
|
|
188
|
+
|
|
140
189
|
const directCallable = (
|
|
141
190
|
descriptor: ExternalObject<CallDescriptor>,
|
|
142
|
-
|
|
191
|
+
readReturn: (nativeResult: unknown) => unknown,
|
|
143
192
|
hasPrimary: boolean,
|
|
144
|
-
|
|
193
|
+
plans: ArgSpec[],
|
|
145
194
|
): ((...inputs: unknown[]) => unknown) => {
|
|
146
|
-
const
|
|
147
|
-
|
|
195
|
+
const argCount = plans.length;
|
|
196
|
+
const requiredIndices = requiredInputIndices(plans);
|
|
197
|
+
|
|
198
|
+
const marshal = (inputs: unknown[]): unknown => {
|
|
199
|
+
assertRequiredInputs(requiredIndices, inputs);
|
|
200
|
+
|
|
201
|
+
return readReturn(call(descriptor, resizeInputs(inputs, argCount)));
|
|
202
|
+
};
|
|
148
203
|
|
|
149
204
|
if (hasPrimary) {
|
|
150
205
|
return (...inputs) => marshal(inputs);
|
|
@@ -165,20 +220,20 @@ function fromNativeCallable(
|
|
|
165
220
|
const plans = buildArgSpecs(args);
|
|
166
221
|
const outPlans = plans.filter((plan) => plan.isOutParam);
|
|
167
222
|
const arePassThrough = plans.every((plan, index) => isPassThroughPlan(plan, index));
|
|
223
|
+
const readReturn = returnReader(spec);
|
|
168
224
|
|
|
169
225
|
if (!canThrow && arePassThrough) {
|
|
170
|
-
return directCallable(descriptor,
|
|
226
|
+
return directCallable(descriptor, readReturn, hasPrimary, plans);
|
|
171
227
|
}
|
|
172
228
|
|
|
229
|
+
const requiredIndices = requiredInputIndices(plans);
|
|
230
|
+
|
|
173
231
|
const shape = (inputs: unknown[], nativeValues: unknown[], nativeResult: unknown): unknown =>
|
|
174
|
-
packTupleResult(
|
|
175
|
-
readOutParams(outPlans, inputs, nativeValues),
|
|
176
|
-
fromNative(returnDescriptor, nativeResult),
|
|
177
|
-
hasPrimary,
|
|
178
|
-
);
|
|
232
|
+
packTupleResult(readOutParams(outPlans, inputs, nativeValues), readReturn(nativeResult), hasPrimary);
|
|
179
233
|
|
|
180
234
|
if (canThrow) {
|
|
181
235
|
return (...inputs) => {
|
|
236
|
+
assertRequiredInputs(requiredIndices, inputs);
|
|
182
237
|
const nativeValues = buildNativeValues(plans, inputs, getRefSeeds?.());
|
|
183
238
|
const errorRef: Ref = { value: null };
|
|
184
239
|
nativeValues.push(errorRef);
|
|
@@ -190,6 +245,7 @@ function fromNativeCallable(
|
|
|
190
245
|
}
|
|
191
246
|
|
|
192
247
|
return (...inputs) => {
|
|
248
|
+
assertRequiredInputs(requiredIndices, inputs);
|
|
193
249
|
const nativeValues = buildNativeValues(plans, inputs, getRefSeeds?.());
|
|
194
250
|
|
|
195
251
|
return shape(inputs, nativeValues, call(descriptor, nativeValues));
|
package/src/index.ts
CHANGED
|
@@ -7,6 +7,8 @@ export { type ClosureCallback, ClosureMarshalError, toClosure, tryToClosure } fr
|
|
|
7
7
|
/** @public */
|
|
8
8
|
export { createErrorDomain, type ErrorDomain } from "./error.js";
|
|
9
9
|
/** @public */
|
|
10
|
+
export { type Field, type StridedField } from "./field.js";
|
|
11
|
+
/** @public */
|
|
10
12
|
export { onExit, quit, quitApplication, runApplication, type RunApplicationResult } from "./lifecycle.js";
|
|
11
13
|
/** @public */
|
|
12
14
|
export { offSignal, onceSignal, onSignal } from "./listeners.js";
|
|
@@ -24,23 +26,45 @@ export {
|
|
|
24
26
|
setObjectProperty,
|
|
25
27
|
} from "./object.js";
|
|
26
28
|
/** @public */
|
|
27
|
-
export {
|
|
29
|
+
export { getParamSpecFlags, getParamSpecOwnerType, getParamSpecValueType } from "./param-spec.js";
|
|
30
|
+
/** @public */
|
|
31
|
+
export { promisify, trimFinish } from "./promisify.js";
|
|
32
|
+
/** @public */
|
|
33
|
+
export {
|
|
34
|
+
coerceObjectProperty,
|
|
35
|
+
getDeclaredPropertyName,
|
|
36
|
+
isReadableProperty,
|
|
37
|
+
newParamSpecOverride,
|
|
38
|
+
} from "./properties.js";
|
|
39
|
+
/** @public */
|
|
40
|
+
export { matchAllRegex, matchRegex } from "./regex.js";
|
|
28
41
|
/** @public */
|
|
29
|
-
export { type Interface, registerClass } from "./register-class.js";
|
|
42
|
+
export { type Interface, registerClass, type SignalGType, type SignalSpec } from "./register-class.js";
|
|
30
43
|
/** @public */
|
|
31
44
|
export {
|
|
32
45
|
getClassType,
|
|
33
46
|
getHandle,
|
|
34
47
|
getInstanceType,
|
|
35
48
|
getWrapperClass,
|
|
49
|
+
peekTypeClass,
|
|
50
|
+
registerClassStruct,
|
|
36
51
|
registerInterface,
|
|
37
52
|
registerWrapperClass,
|
|
53
|
+
registerWrapperClassResolver,
|
|
38
54
|
type StaticBase,
|
|
39
55
|
setHandle,
|
|
40
56
|
wrapHandle,
|
|
57
|
+
type WrapperClassResolver,
|
|
41
58
|
} from "./registry.js";
|
|
42
59
|
/** @public */
|
|
43
|
-
export {
|
|
60
|
+
export {
|
|
61
|
+
connectSignal,
|
|
62
|
+
disconnectSignal,
|
|
63
|
+
emitSignal,
|
|
64
|
+
getSignalBaseName,
|
|
65
|
+
type SignalHandler,
|
|
66
|
+
signalForHandlerName,
|
|
67
|
+
} from "./signal.js";
|
|
44
68
|
/** @public */
|
|
45
69
|
export { t } from "./t.js";
|
|
46
70
|
/** @public */
|
|
@@ -79,9 +103,26 @@ export {
|
|
|
79
103
|
valueIsA,
|
|
80
104
|
} from "./type.js";
|
|
81
105
|
/** @public */
|
|
82
|
-
export {
|
|
106
|
+
export {
|
|
107
|
+
fromValue,
|
|
108
|
+
getBoxedValue,
|
|
109
|
+
type JsValue,
|
|
110
|
+
setBoxedValue,
|
|
111
|
+
toValueHandle,
|
|
112
|
+
tryToValueHandle,
|
|
113
|
+
ValueMarshalError,
|
|
114
|
+
} from "./value.js";
|
|
83
115
|
/** @public */
|
|
84
|
-
export {
|
|
116
|
+
export {
|
|
117
|
+
type ByteArray,
|
|
118
|
+
type FromVariantOptions,
|
|
119
|
+
fromVariant,
|
|
120
|
+
type RecursiveFromVariantOptions,
|
|
121
|
+
type RecursiveVariantValue,
|
|
122
|
+
toVariant,
|
|
123
|
+
type VariantInput,
|
|
124
|
+
type VariantValue,
|
|
125
|
+
} from "./variant.js";
|
|
85
126
|
/** @public */
|
|
86
127
|
export { callParent, callVfunc } from "./vfunc-call.js";
|
|
87
128
|
/** @public */
|
package/src/mixin.ts
CHANGED
|
@@ -89,4 +89,4 @@ function insertMixinLayer(target: AnyClass, mixin: Mixin, inheritedNames: Set<st
|
|
|
89
89
|
Object.setPrototypeOf(target, layer);
|
|
90
90
|
}
|
|
91
91
|
|
|
92
|
-
export { insertMixinLayer, installMixins, type MixinReceiver, type Mixin };
|
|
92
|
+
export { copyLayerMembers, insertMixinLayer, installMixins, type MixinReceiver, type Mixin };
|
package/src/native-value.ts
CHANGED
|
@@ -1,10 +1,41 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import
|
|
3
|
-
|
|
1
|
+
import type { AnyClass } from "@gtkx/utils";
|
|
2
|
+
import {
|
|
3
|
+
bindFunctionPointer,
|
|
4
|
+
call,
|
|
5
|
+
type CallDescriptor,
|
|
6
|
+
type Descriptor,
|
|
7
|
+
type ExternalObject,
|
|
8
|
+
getType,
|
|
9
|
+
type Handle,
|
|
10
|
+
type Ref,
|
|
11
|
+
} from "@gtkx/native";
|
|
12
|
+
import {
|
|
13
|
+
type ArrayDescriptor,
|
|
14
|
+
boxedT,
|
|
15
|
+
type CallbackDescriptor,
|
|
16
|
+
type FundamentalDescriptor,
|
|
17
|
+
type HashTableDescriptor,
|
|
18
|
+
isGtypeDescriptor,
|
|
19
|
+
refT,
|
|
20
|
+
type StructDescriptor,
|
|
21
|
+
} from "./descriptors.js";
|
|
22
|
+
import { checkError } from "./error.js";
|
|
23
|
+
import { LIB } from "./library.js";
|
|
24
|
+
import {
|
|
25
|
+
coerceGType,
|
|
26
|
+
getHandle,
|
|
27
|
+
getWrapperClass,
|
|
28
|
+
resolveWrapperClass,
|
|
29
|
+
wrapCallScopedObject,
|
|
30
|
+
wrapFundamentalHandle,
|
|
31
|
+
wrapHandle,
|
|
32
|
+
wrapObject,
|
|
33
|
+
} from "./registry.js";
|
|
4
34
|
import { resolveDescriptorType } from "./type.js";
|
|
5
35
|
|
|
6
36
|
type MarshalledKind = "object" | "struct" | "boxed" | "fundamental" | "array" | "hashtable";
|
|
7
37
|
type MarshalledDescriptor = Extract<Descriptor, { kind: MarshalledKind }>;
|
|
38
|
+
type DecodedCallbackTarget = { fnPtr: bigint; userData?: bigint | undefined };
|
|
8
39
|
|
|
9
40
|
const MARSHALLED_KINDS: Set<Descriptor["kind"]> = new Set<MarshalledKind>([
|
|
10
41
|
"object",
|
|
@@ -15,6 +46,8 @@ const MARSHALLED_KINDS: Set<Descriptor["kind"]> = new Set<MarshalledKind>([
|
|
|
15
46
|
"hashtable",
|
|
16
47
|
]);
|
|
17
48
|
|
|
49
|
+
const NULL_POINTER = 0n;
|
|
50
|
+
|
|
18
51
|
function isMarshalledDescriptor(descriptor: Descriptor): descriptor is MarshalledDescriptor {
|
|
19
52
|
return MARSHALLED_KINDS.has(descriptor.kind);
|
|
20
53
|
}
|
|
@@ -32,18 +65,103 @@ function collectionFromNative(descriptor: ArrayDescriptor, value: unknown): unkn
|
|
|
32
65
|
}
|
|
33
66
|
|
|
34
67
|
function boxedFromNative(descriptor: Descriptor, value: unknown): unknown {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
68
|
+
if (value == null) {
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const handle = value as ExternalObject<Handle>;
|
|
73
|
+
const type = resolveDescriptorType(descriptor);
|
|
74
|
+
|
|
75
|
+
return wrapHandle(handle, getWrapperClass(type));
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function fundamentalWrapperClass(descriptor: FundamentalDescriptor, handle: ExternalObject<Handle>): AnyClass {
|
|
79
|
+
if (descriptor.wrapperClass !== undefined) {
|
|
80
|
+
return descriptor.wrapperClass;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const declaredType = resolveDescriptorType(descriptor);
|
|
84
|
+
|
|
85
|
+
return resolveWrapperClass(getType(handle, declaredType)) ?? getWrapperClass(declaredType);
|
|
38
86
|
}
|
|
39
87
|
|
|
40
88
|
function fundamentalFromNative(descriptor: FundamentalDescriptor, value: unknown): unknown {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
89
|
+
if (value == null) {
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const handle = value as ExternalObject<Handle>;
|
|
94
|
+
|
|
95
|
+
return wrapFundamentalHandle(handle, fundamentalWrapperClass(descriptor, handle));
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const errorRefDescriptor = (): Descriptor =>
|
|
99
|
+
refT(boxedT("GError", { ownership: "full", sharedLibrary: LIB, getTypeFnName: "g_error_get_type" }));
|
|
100
|
+
|
|
101
|
+
function decodedCallbackValues(
|
|
102
|
+
descriptor: CallbackDescriptor,
|
|
103
|
+
target: DecodedCallbackTarget,
|
|
104
|
+
inputs: unknown[],
|
|
105
|
+
): unknown[] {
|
|
106
|
+
const values: unknown[] = [];
|
|
107
|
+
let cursor = 0;
|
|
108
|
+
|
|
109
|
+
for (const [index, argDescriptor] of descriptor.argDescriptors.entries()) {
|
|
110
|
+
if (index === descriptor.userDataIndex) {
|
|
111
|
+
values.push(target.userData ?? NULL_POINTER);
|
|
112
|
+
} else {
|
|
113
|
+
values.push(toNative(argDescriptor, inputs[cursor]));
|
|
114
|
+
cursor += 1;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return values;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function decodedCallbackCallable(
|
|
122
|
+
descriptor: CallbackDescriptor,
|
|
123
|
+
target: DecodedCallbackTarget,
|
|
124
|
+
): (...inputs: unknown[]) => unknown {
|
|
125
|
+
const canThrow = descriptor.canThrow === true;
|
|
126
|
+
const isOneShot = descriptor.scope === "async";
|
|
127
|
+
const argDescriptors = canThrow ? [...descriptor.argDescriptors, errorRefDescriptor()] : descriptor.argDescriptors;
|
|
128
|
+
let bound: ExternalObject<CallDescriptor> | undefined;
|
|
129
|
+
let isSpent = false;
|
|
130
|
+
|
|
131
|
+
return (...inputs) => {
|
|
132
|
+
if (isSpent) {
|
|
133
|
+
throw new Error("An async-scoped callback was already invoked; its native caller has released it");
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
bound ??= bindFunctionPointer(target.fnPtr, argDescriptors, descriptor.returnDescriptor, "decoded callback");
|
|
137
|
+
const values = decodedCallbackValues(descriptor, target, inputs);
|
|
138
|
+
isSpent = isOneShot;
|
|
139
|
+
|
|
140
|
+
if (!canThrow) {
|
|
141
|
+
return fromNative(descriptor.returnDescriptor, call(bound, values));
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const errorRef: Ref = { value: null };
|
|
145
|
+
values.push(errorRef);
|
|
146
|
+
const result = call(bound, values);
|
|
147
|
+
checkError(errorRef);
|
|
148
|
+
|
|
149
|
+
return fromNative(descriptor.returnDescriptor, result);
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function callbackFromNative(descriptor: CallbackDescriptor, value: unknown): unknown {
|
|
154
|
+
if (value == null) {
|
|
155
|
+
return value;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const target = value as Partial<DecodedCallbackTarget>;
|
|
159
|
+
|
|
160
|
+
if (typeof target.fnPtr !== "bigint") {
|
|
161
|
+
return value;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
return decodedCallbackCallable(descriptor, { fnPtr: target.fnPtr, userData: target.userData });
|
|
47
165
|
}
|
|
48
166
|
|
|
49
167
|
function hashTableFromNative(descriptor: HashTableDescriptor, value: unknown): unknown {
|
|
@@ -64,12 +182,21 @@ function hashTableFromNative(descriptor: HashTableDescriptor, value: unknown): u
|
|
|
64
182
|
/**
|
|
65
183
|
* Converts a raw value returned from native code into its JavaScript form,
|
|
66
184
|
* wrapping object, struct, boxed, and fundamental handles and recursively
|
|
67
|
-
* converting arrays and hash tables according to the descriptor.
|
|
185
|
+
* converting arrays and hash tables according to the descriptor. A callback
|
|
186
|
+
* decoded from a native function pointer and its bound user data becomes a
|
|
187
|
+
* callable function that invokes the native callback; it stays valid only as
|
|
188
|
+
* long as the native caller keeps the pointer pair alive, which for an
|
|
189
|
+
* async-scoped callback means until the first invocation — calling one a
|
|
190
|
+
* second time throws instead of reaching the released native closure.
|
|
68
191
|
*
|
|
69
192
|
* @param descriptor Describes the native type of the value.
|
|
70
193
|
* @param value The raw native value to convert.
|
|
71
194
|
*/
|
|
72
195
|
function fromNative(descriptor: Descriptor, value: unknown): unknown {
|
|
196
|
+
if (descriptor.kind === "callback") {
|
|
197
|
+
return callbackFromNative(descriptor, value);
|
|
198
|
+
}
|
|
199
|
+
|
|
73
200
|
if (!isMarshalledDescriptor(descriptor)) {
|
|
74
201
|
return value;
|
|
75
202
|
}
|
|
@@ -113,13 +240,18 @@ function hashTableToNative(descriptor: HashTableDescriptor, value: unknown): unk
|
|
|
113
240
|
|
|
114
241
|
/**
|
|
115
242
|
* Converts a JavaScript value into the raw form native code expects, unwrapping
|
|
116
|
-
* object, struct, boxed, and fundamental wrappers back to their handles
|
|
243
|
+
* object, struct, boxed, and fundamental wrappers back to their handles,
|
|
244
|
+
* resolving a class passed for a GType to the GType it was registered under, and
|
|
117
245
|
* recursively converting arrays and maps according to the descriptor.
|
|
118
246
|
*
|
|
119
247
|
* @param descriptor Describes the native type to convert to.
|
|
120
248
|
* @param value The JavaScript value to convert.
|
|
121
249
|
*/
|
|
122
250
|
function toNative(descriptor: Descriptor, value: unknown): unknown {
|
|
251
|
+
if (isGtypeDescriptor(descriptor)) {
|
|
252
|
+
return coerceGType(value);
|
|
253
|
+
}
|
|
254
|
+
|
|
123
255
|
if (!isMarshalledDescriptor(descriptor)) {
|
|
124
256
|
return value;
|
|
125
257
|
}
|
package/src/object.ts
CHANGED
|
@@ -3,9 +3,9 @@ import { type AnyClass, getParentClass } from "@gtkx/utils";
|
|
|
3
3
|
import { bind } from "./bind.js";
|
|
4
4
|
import { objectT, stringT, voidT } from "./descriptors.js";
|
|
5
5
|
import { LIB, VALUE_T } from "./library.js";
|
|
6
|
-
import { type ConstructProperty, constructPropertyFor } from "./properties.js";
|
|
6
|
+
import { coercePropertyValue, type ConstructProperty, constructPropertyFor } from "./properties.js";
|
|
7
7
|
import { getHandle, registerWrapper } from "./registry.js";
|
|
8
|
-
import {
|
|
8
|
+
import { fromValueForDescriptor, newValueForDescriptor, toValue } from "./value.js";
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* One construct property a wrapper class accepts: the canonical `GObject` name it is set under,
|
|
@@ -110,7 +110,7 @@ function constructPropertyForEntry(
|
|
|
110
110
|
return constructPropertyFor(source.gtype, name, value, source.wrapper);
|
|
111
111
|
}
|
|
112
112
|
|
|
113
|
-
return { name: binding[0], value: toValue(binding[1], value) };
|
|
113
|
+
return { name: binding[0], value: toValue(binding[1], coercePropertyValue(source.gtype, binding[0], value)) };
|
|
114
114
|
}
|
|
115
115
|
|
|
116
116
|
/**
|
|
@@ -123,17 +123,24 @@ function constructPropertyForEntry(
|
|
|
123
123
|
* for a read-only property and for a value of a type the property cannot hold, and
|
|
124
124
|
* a `RangeError` for a value the ParamSpec rejects. A value marshalled through a
|
|
125
125
|
* declared descriptor is converted rather than checked, so a `null` handed to a
|
|
126
|
-
* numeric one of those lands 0 rather than being refused
|
|
126
|
+
* numeric one of those lands 0 rather than being refused, and a number is fitted to
|
|
127
|
+
* the property first, as `coerceObjectProperty` does: truncated toward zero for a
|
|
128
|
+
* whole-number property and clamped to the range its `GObject.ParamSpec` allows.
|
|
127
129
|
* Properties whose value is `undefined` are skipped. A type registered with
|
|
128
130
|
* `registerClass` binds the wrapper before its `constructed` slot runs, so an
|
|
129
131
|
* override of that slot already sees a usable instance.
|
|
132
|
+
* When construction returns an object that already has a wrapper — it reached
|
|
133
|
+
* JavaScript and was wrapped before `g_object_new` returned, for example a
|
|
134
|
+
* `Gtk.Window` observed through the toplevels list — that existing wrapper is
|
|
135
|
+
* returned instead of binding `wrapper`, so both references stay one object.
|
|
130
136
|
*
|
|
131
137
|
* @param gtype The GType of the object to construct.
|
|
132
138
|
* @param props Property names mapped to the values to set them to.
|
|
133
139
|
* @param wrapper The wrapper instance to bind to the new object.
|
|
134
|
-
* @returns The
|
|
140
|
+
* @returns The wrapper bound to the constructed object: `wrapper` itself, or
|
|
141
|
+
* the wrapper the object already had.
|
|
135
142
|
*/
|
|
136
|
-
function newObjectWithProperties(gtype: bigint, props: object, wrapper:
|
|
143
|
+
function newObjectWithProperties<T extends object>(gtype: bigint, props: object, wrapper: T): T {
|
|
137
144
|
const names: string[] = [];
|
|
138
145
|
const values: ExternalObject<Handle>[] = [];
|
|
139
146
|
const bindings = constructBindingsFor(wrapper.constructor as AnyClass | undefined);
|
|
@@ -148,9 +155,13 @@ function newObjectWithProperties(gtype: bigint, props: object, wrapper: object):
|
|
|
148
155
|
}
|
|
149
156
|
}
|
|
150
157
|
|
|
151
|
-
newObject(gtype, names, values, wrapper, registerWrapper);
|
|
158
|
+
const existing = newObject(gtype, names, values, wrapper, registerWrapper);
|
|
152
159
|
|
|
153
|
-
|
|
160
|
+
if (existing !== null) {
|
|
161
|
+
return existing as T;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
return wrapper;
|
|
154
165
|
}
|
|
155
166
|
|
|
156
167
|
/**
|
|
@@ -165,15 +176,16 @@ function getObjectProperty(obj: object, propertyName: string, descriptor: Descri
|
|
|
165
176
|
const value = newValueForDescriptor(descriptor);
|
|
166
177
|
gObjectGetProperty(getHandle(obj), propertyName, value);
|
|
167
178
|
|
|
168
|
-
return
|
|
179
|
+
return fromValueForDescriptor(descriptor, value);
|
|
169
180
|
}
|
|
170
181
|
|
|
171
182
|
/**
|
|
172
183
|
* Writes a JavaScript value to a GObject property, converting it to native form
|
|
173
184
|
* using the descriptor. The descriptor converts what it is given rather than checking it against
|
|
174
185
|
* the property's `GObject.ParamSpec`, so `null` and `undefined` written to a numeric or enum
|
|
175
|
-
* property land 0,
|
|
176
|
-
* refused with a
|
|
186
|
+
* property land 0, and a fractional number written to a whole-number property is truncated toward
|
|
187
|
+
* zero, where the same writes to a property installed through `registerClass` are refused with a
|
|
188
|
+
* `TypeError`.
|
|
177
189
|
*
|
|
178
190
|
* @param obj The object to write to.
|
|
179
191
|
* @param propertyName The property name.
|