@gtkx/runtime 1.0.0 → 1.2.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/closure.js +1 -1
- package/dist/closure.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 +34 -9
- package/dist/descriptors.d.ts.map +1 -1
- package/dist/descriptors.js +42 -17
- 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 +33 -3
- 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 +14 -4
- 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 +3 -1
- 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/closure.ts +1 -1
- package/src/default-application.ts +21 -0
- package/src/descriptors.ts +98 -29
- 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 +66 -11
- package/src/registry.ts +43 -20
- package/src/signal.ts +32 -28
- package/src/t.ts +4 -1
- 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/callback.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { copy, type Descriptor } from "@gtkx/native";
|
|
2
2
|
import type { CallbackDescriptor, RefDescriptor } from "./descriptors.js";
|
|
3
|
+
import { foldedLengthSources, type LengthSource, type LengthSources } from "./folded-lengths.js";
|
|
3
4
|
import { fromNative, toNative } from "./native-value.js";
|
|
4
5
|
import { getHandle } from "./registry.js";
|
|
5
6
|
import { splitTupleResult } from "./tuple.js";
|
|
@@ -7,10 +8,12 @@ import { copyValue } from "./value.js";
|
|
|
7
8
|
import { popSeedFrame, pushSeedFrame, type RefSeeds } from "./vfunc-seeds.js";
|
|
8
9
|
|
|
9
10
|
type Callback = (...args: unknown[]) => unknown;
|
|
10
|
-
type
|
|
11
|
+
type CallbackKind = "vfunc" | "signal" | "callback";
|
|
12
|
+
type CallbackTraits = { isInstanceBound: boolean; hasInstanceArg: boolean; hasFoldedLengths: boolean };
|
|
11
13
|
type OutParam = { value: unknown; descriptor: Descriptor; argIndex: number };
|
|
12
|
-
type LengthLink = { target: OutParam;
|
|
14
|
+
type LengthLink = { target: OutParam; sources: LengthSource[] };
|
|
13
15
|
type OutParamGroups = { lengthLinks: LengthLink[]; valueParams: OutParam[] };
|
|
16
|
+
type OutValues = Map<number, unknown>;
|
|
14
17
|
|
|
15
18
|
type CallbackSpec = {
|
|
16
19
|
argDescriptors: Descriptor[];
|
|
@@ -22,12 +25,20 @@ type CallbackPlan = {
|
|
|
22
25
|
fn: Callback;
|
|
23
26
|
effectiveTypes: Descriptor[];
|
|
24
27
|
returnDescriptor: Descriptor;
|
|
28
|
+
hasPrimary: boolean;
|
|
25
29
|
start: number;
|
|
26
|
-
|
|
30
|
+
isInstanceBound: boolean;
|
|
31
|
+
lengthSources: LengthSources;
|
|
27
32
|
hasOutParams: boolean;
|
|
28
33
|
hasRefOutParams: boolean;
|
|
29
34
|
};
|
|
30
35
|
|
|
36
|
+
const CALLBACK_TRAITS: Record<CallbackKind, CallbackTraits> = {
|
|
37
|
+
callback: { isInstanceBound: false, hasInstanceArg: false, hasFoldedLengths: true },
|
|
38
|
+
signal: { isInstanceBound: false, hasInstanceArg: true, hasFoldedLengths: false },
|
|
39
|
+
vfunc: { isInstanceBound: true, hasInstanceArg: true, hasFoldedLengths: true },
|
|
40
|
+
};
|
|
41
|
+
|
|
31
42
|
const fillCallerAllocatedBuffer = (descriptor: Descriptor, target: object, source: object): void => {
|
|
32
43
|
if (descriptor.kind === "boxed" && descriptor.typeName === "GValue") {
|
|
33
44
|
copyValue(getHandle(target), getHandle(source));
|
|
@@ -108,74 +119,58 @@ const haveOutParamArgs = (effectiveTypes: Descriptor[], start: number): boolean
|
|
|
108
119
|
const haveRefOutParamArgs = (effectiveTypes: Descriptor[], start: number): boolean =>
|
|
109
120
|
effectiveTypes.slice(start).some((descriptor) => hasRefOutParamArg(descriptor));
|
|
110
121
|
|
|
111
|
-
const
|
|
112
|
-
|
|
113
|
-
return undefined;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
const { innerDescriptor } = descriptor;
|
|
117
|
-
|
|
118
|
-
return innerDescriptor.kind === "array" ? innerDescriptor.sizeParamIndex : undefined;
|
|
119
|
-
};
|
|
120
|
-
|
|
121
|
-
const lengthOutParamIndices = (outParams: OutParam[]): Map<number, number> => {
|
|
122
|
-
const indices: Map<number, number> = new Map();
|
|
122
|
+
const groupOutParams = (outParams: OutParam[], lengthSources: LengthSources): OutParamGroups => {
|
|
123
|
+
const groups: OutParamGroups = { lengthLinks: [], valueParams: [] };
|
|
123
124
|
|
|
124
125
|
for (const outParam of outParams) {
|
|
125
|
-
const
|
|
126
|
+
const sources = lengthSources.get(outParam.argIndex);
|
|
126
127
|
|
|
127
|
-
if (
|
|
128
|
-
|
|
128
|
+
if (sources === undefined) {
|
|
129
|
+
groups.valueParams.push(outParam);
|
|
130
|
+
} else {
|
|
131
|
+
groups.lengthLinks.push({ target: outParam, sources });
|
|
129
132
|
}
|
|
130
133
|
}
|
|
131
134
|
|
|
132
|
-
return
|
|
135
|
+
return groups;
|
|
133
136
|
};
|
|
134
137
|
|
|
135
|
-
const
|
|
136
|
-
const
|
|
138
|
+
const getLength = (value: unknown): number => {
|
|
139
|
+
const length = (value as { length?: unknown } | null | undefined)?.length;
|
|
137
140
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
const { descriptor } = outParam;
|
|
141
|
-
written.set(outParam.argIndex, outValue);
|
|
141
|
+
return typeof length === "number" ? length : 0;
|
|
142
|
+
};
|
|
142
143
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
} else if (outValue != null && outParam.value != null) {
|
|
146
|
-
fillCallerAllocatedBuffer(descriptor, outParam.value, outValue);
|
|
147
|
-
}
|
|
148
|
-
}
|
|
144
|
+
const lengthSourceValue = (source: LengthSource, outValues: OutValues, primary: unknown): unknown =>
|
|
145
|
+
source.kind === "return" ? primary : outValues.get(source.argIndex);
|
|
149
146
|
|
|
150
|
-
|
|
151
|
-
|
|
147
|
+
const foldedLength = (sources: LengthSource[], outValues: OutValues, primary: unknown): number =>
|
|
148
|
+
Math.min(...sources.map((source) => getLength(lengthSourceValue(source, outValues, primary))));
|
|
152
149
|
|
|
153
|
-
const
|
|
154
|
-
const
|
|
155
|
-
const groups: OutParamGroups = { lengthLinks: [], valueParams: [] };
|
|
150
|
+
const resolveOutValues = (groups: OutParamGroups, values: unknown[], primary: unknown): OutValues => {
|
|
151
|
+
const outValues: OutValues = new Map();
|
|
156
152
|
|
|
157
|
-
for (const outParam of
|
|
158
|
-
|
|
153
|
+
for (const [position, outParam] of groups.valueParams.entries()) {
|
|
154
|
+
outValues.set(outParam.argIndex, values[position]);
|
|
155
|
+
}
|
|
159
156
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
} else {
|
|
163
|
-
groups.lengthLinks.push({ target: outParam, sourceIndex });
|
|
164
|
-
}
|
|
157
|
+
for (const link of groups.lengthLinks) {
|
|
158
|
+
outValues.set(link.target.argIndex, foldedLength(link.sources, outValues, primary));
|
|
165
159
|
}
|
|
166
160
|
|
|
167
|
-
return
|
|
161
|
+
return outValues;
|
|
168
162
|
};
|
|
169
163
|
|
|
170
|
-
const
|
|
171
|
-
const
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
};
|
|
164
|
+
const writeOutParams = (outParams: OutParam[], outValues: OutValues): void => {
|
|
165
|
+
for (const outParam of outParams) {
|
|
166
|
+
const { descriptor } = outParam;
|
|
167
|
+
const outValue = outValues.get(outParam.argIndex);
|
|
175
168
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
(
|
|
169
|
+
if (descriptor.kind === "ref") {
|
|
170
|
+
(outParam.value as { value: unknown }).value = toNative(descriptor.innerDescriptor, outValue);
|
|
171
|
+
} else if (outValue != null && outParam.value != null) {
|
|
172
|
+
fillCallerAllocatedBuffer(descriptor, outParam.value, outValue);
|
|
173
|
+
}
|
|
179
174
|
}
|
|
180
175
|
};
|
|
181
176
|
|
|
@@ -207,8 +202,8 @@ const applyCallback = (plan: CallbackPlan, thisArg: unknown, inputs: unknown[],
|
|
|
207
202
|
}
|
|
208
203
|
};
|
|
209
204
|
|
|
210
|
-
const getThisArg = (
|
|
211
|
-
|
|
205
|
+
const getThisArg = (isInstanceBound: boolean, wrapped: unknown[]): unknown =>
|
|
206
|
+
isInstanceBound ? (wrapped[0] ?? null) : null;
|
|
212
207
|
|
|
213
208
|
const wrapCallbackArgs = (effectiveTypes: Descriptor[], rawArgs: unknown[]): void => {
|
|
214
209
|
let index = 0;
|
|
@@ -233,29 +228,35 @@ const trimCallbackInputs = (plan: CallbackPlan, wrapped: unknown[]): unknown[] =
|
|
|
233
228
|
return wrapped;
|
|
234
229
|
};
|
|
235
230
|
|
|
231
|
+
const nativeReturn = (plan: CallbackPlan, primary: unknown): unknown =>
|
|
232
|
+
toNative(plan.returnDescriptor, plan.hasPrimary ? primary : undefined);
|
|
233
|
+
|
|
236
234
|
const runCallback = (plan: CallbackPlan, rawArgs: unknown[]): unknown => {
|
|
237
|
-
const { effectiveTypes
|
|
235
|
+
const { effectiveTypes } = plan;
|
|
238
236
|
wrapCallbackArgs(effectiveTypes, rawArgs);
|
|
239
|
-
const thisArg = getThisArg(plan.
|
|
237
|
+
const thisArg = getThisArg(plan.isInstanceBound, rawArgs);
|
|
240
238
|
|
|
241
239
|
if (!plan.hasOutParams) {
|
|
242
|
-
return
|
|
240
|
+
return nativeReturn(plan, plan.fn.apply(thisArg, trimCallbackInputs(plan, rawArgs)));
|
|
243
241
|
}
|
|
244
242
|
|
|
245
243
|
const { inputs, outParams } = partitionCallbackArgs(effectiveTypes, rawArgs, plan.start);
|
|
246
244
|
const result = applyCallback(plan, thisArg, inputs, outParams);
|
|
247
245
|
|
|
248
246
|
if (outParams.length === 0) {
|
|
249
|
-
return
|
|
247
|
+
return nativeReturn(plan, result);
|
|
250
248
|
}
|
|
251
249
|
|
|
252
|
-
const
|
|
253
|
-
const { primary, outValues } = splitTupleResult(result,
|
|
254
|
-
|
|
250
|
+
const groups = groupOutParams(outParams, plan.lengthSources);
|
|
251
|
+
const { primary, outValues } = splitTupleResult(result, plan.hasPrimary, groups.valueParams.length);
|
|
252
|
+
writeOutParams(outParams, resolveOutValues(groups, outValues, primary));
|
|
255
253
|
|
|
256
|
-
return
|
|
254
|
+
return nativeReturn(plan, primary);
|
|
257
255
|
};
|
|
258
256
|
|
|
257
|
+
const planLengthSources = (spec: CallbackSpec, hasFoldedLengths: boolean): LengthSources =>
|
|
258
|
+
hasFoldedLengths ? foldedLengthSources(spec) : new Map<number, LengthSource[]>();
|
|
259
|
+
|
|
259
260
|
const getEffectiveTypes = (spec: CallbackSpec): Descriptor[] => {
|
|
260
261
|
const { userDataIndex } = spec;
|
|
261
262
|
|
|
@@ -267,18 +268,21 @@ const getEffectiveTypes = (spec: CallbackSpec): Descriptor[] => {
|
|
|
267
268
|
};
|
|
268
269
|
|
|
269
270
|
const wrapCallbackValue = (spec: CallbackDescriptor, callback: unknown): unknown =>
|
|
270
|
-
callback == null ? callback : wrapCallback(callback as Callback, spec, "
|
|
271
|
+
callback == null ? callback : wrapCallback(callback as Callback, spec, "callback");
|
|
271
272
|
|
|
272
|
-
function wrapCallback(fn: Callback, spec: CallbackSpec,
|
|
273
|
+
function wrapCallback(fn: Callback, spec: CallbackSpec, kind: CallbackKind): Callback {
|
|
273
274
|
const effectiveTypes = getEffectiveTypes(spec);
|
|
274
|
-
const
|
|
275
|
+
const { isInstanceBound, hasInstanceArg, hasFoldedLengths } = CALLBACK_TRAITS[kind];
|
|
276
|
+
const start = hasInstanceArg ? 1 : 0;
|
|
275
277
|
|
|
276
278
|
const plan: CallbackPlan = {
|
|
277
279
|
fn,
|
|
278
280
|
effectiveTypes,
|
|
279
281
|
returnDescriptor: spec.returnDescriptor,
|
|
282
|
+
hasPrimary: spec.returnDescriptor.kind !== "void",
|
|
280
283
|
start,
|
|
281
|
-
|
|
284
|
+
isInstanceBound,
|
|
285
|
+
lengthSources: planLengthSources(spec, hasFoldedLengths),
|
|
282
286
|
hasOutParams: haveOutParamArgs(effectiveTypes, start),
|
|
283
287
|
hasRefOutParams: haveRefOutParamArgs(effectiveTypes, start),
|
|
284
288
|
};
|
package/src/closure.ts
CHANGED
|
@@ -40,7 +40,7 @@ const MARSHAL_T = callbackT(
|
|
|
40
40
|
CLOSURE_T,
|
|
41
41
|
MARSHAL_VALUE_T,
|
|
42
42
|
uint32T,
|
|
43
|
-
sizedArrayT(MARSHAL_VALUE_T, N_PARAM_VALUES_INDEX, "borrowed", VALUE_SIZE),
|
|
43
|
+
sizedArrayT(MARSHAL_VALUE_T, N_PARAM_VALUES_INDEX, "borrowed", { elementSize: VALUE_SIZE }),
|
|
44
44
|
uint64T,
|
|
45
45
|
uint64T,
|
|
46
46
|
],
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { bind } from "./bind.js";
|
|
2
|
+
import { objectT, voidT } from "./descriptors.js";
|
|
3
|
+
import { GIO_LIB } from "./library.js";
|
|
4
|
+
import { getHandle } from "./registry.js";
|
|
5
|
+
|
|
6
|
+
const readDefaultApplication = bind(GIO_LIB, "g_application_get_default", [], objectT("borrowed"));
|
|
7
|
+
const writeDefaultApplication = bind(GIO_LIB, "g_application_set_default", [objectT("borrowed")], voidT);
|
|
8
|
+
|
|
9
|
+
const claimDefaultApplication = (application: object): void => {
|
|
10
|
+
writeDefaultApplication(getHandle(application));
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
const releaseDefaultApplication = (application: object): void => {
|
|
14
|
+
if (readDefaultApplication() !== application) {
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
writeDefaultApplication(null);
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export { claimDefaultApplication, releaseDefaultApplication };
|
package/src/descriptors.ts
CHANGED
|
@@ -89,7 +89,10 @@ type BoxedOptions = {
|
|
|
89
89
|
size?: number;
|
|
90
90
|
};
|
|
91
91
|
|
|
92
|
-
/**
|
|
92
|
+
/**
|
|
93
|
+
* What the bindings do with a callback's return value, how the callee takes its closure, and how
|
|
94
|
+
* long that closure has to stay alive.
|
|
95
|
+
*/
|
|
93
96
|
type CallbackOptions = {
|
|
94
97
|
/** The callee also takes a destroy notify, which frees the closure once it is done with it. */
|
|
95
98
|
hasDestroy?: boolean;
|
|
@@ -107,10 +110,22 @@ type CallbackOptions = {
|
|
|
107
110
|
type ArrayOptions = {
|
|
108
111
|
/** Stride in bytes between elements stored inline in the array. */
|
|
109
112
|
elementSize?: number | undefined;
|
|
113
|
+
/** Position of the argument whose buffer a cursor array points into. */
|
|
114
|
+
baseParamIndex?: number | undefined;
|
|
110
115
|
/** Position of the argument carrying the element count, for a length-bounded array. */
|
|
111
116
|
sizeParamIndex?: number | undefined;
|
|
112
117
|
/** Element count of a fixed-length array. */
|
|
113
118
|
fixedSize?: number | undefined;
|
|
119
|
+
/** Whether the array carries raw bytes, and so decodes to a `Uint8Array` rather than to numbers. */
|
|
120
|
+
isBytes?: boolean | undefined;
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
/** Where a cursor array's base buffer and total length come from. */
|
|
124
|
+
type CursorBounds = {
|
|
125
|
+
/** Position of the argument holding the buffer the cursor points into. */
|
|
126
|
+
baseParamIndex: number;
|
|
127
|
+
/** Position of the argument carrying that buffer's element count. */
|
|
128
|
+
sizeParamIndex: number;
|
|
114
129
|
};
|
|
115
130
|
|
|
116
131
|
/** How a ref-counted fundamental value is named, wrapped and stored. */
|
|
@@ -125,6 +140,12 @@ type FundamentalOptions = {
|
|
|
125
140
|
isInline?: boolean;
|
|
126
141
|
};
|
|
127
142
|
|
|
143
|
+
type FundamentalLifecycle = {
|
|
144
|
+
sharedLibrary: string;
|
|
145
|
+
refFnName: string;
|
|
146
|
+
unrefFnName: string;
|
|
147
|
+
};
|
|
148
|
+
|
|
128
149
|
/** How a plain C struct is stored and wrapped. */
|
|
129
150
|
type StructOptions = {
|
|
130
151
|
/** The caller owns the storage the callee fills, so a decoded value is borrowed instead of copied. */
|
|
@@ -171,6 +192,7 @@ const voidT: VoidDescriptor = { kind: "void" };
|
|
|
171
192
|
const unicharT: UnicharDescriptor = { kind: "unichar" };
|
|
172
193
|
/** Descriptor for an opaque `gpointer`, taken from a typed array's memory or a numeric address. */
|
|
173
194
|
const bufferT: BufferDescriptor = { kind: "buffer" };
|
|
195
|
+
const fundamentalLifecycles: Map<string, FundamentalLifecycle> = new Map();
|
|
174
196
|
|
|
175
197
|
/**
|
|
176
198
|
* Builds a descriptor for a C string, whose optional length sizes the caller-allocated buffer
|
|
@@ -280,6 +302,15 @@ const structT = (ownership: Ownership = "borrowed", options: StructOptions = {})
|
|
|
280
302
|
return result;
|
|
281
303
|
};
|
|
282
304
|
|
|
305
|
+
const recordFundamentalLifecycle = (typeName: string, lifecycle: FundamentalLifecycle): void => {
|
|
306
|
+
if (!fundamentalLifecycles.has(typeName)) {
|
|
307
|
+
fundamentalLifecycles.set(typeName, lifecycle);
|
|
308
|
+
}
|
|
309
|
+
};
|
|
310
|
+
|
|
311
|
+
const fundamentalLifecycleFor = (typeName: string): FundamentalLifecycle | undefined =>
|
|
312
|
+
fundamentalLifecycles.get(typeName);
|
|
313
|
+
|
|
283
314
|
/** Builds a descriptor for a fundamental type whose lifetime is managed by named ref and unref functions. */
|
|
284
315
|
const fundamentalT = (
|
|
285
316
|
sharedLibrary: string,
|
|
@@ -292,6 +323,7 @@ const fundamentalT = (
|
|
|
292
323
|
|
|
293
324
|
if (options.typeName !== undefined) {
|
|
294
325
|
result.typeName = options.typeName;
|
|
326
|
+
recordFundamentalLifecycle(options.typeName, { sharedLibrary, refFnName, unrefFnName });
|
|
295
327
|
}
|
|
296
328
|
|
|
297
329
|
if (options.wrapperClass !== undefined) {
|
|
@@ -318,6 +350,10 @@ const arrayT = (
|
|
|
318
350
|
result.elementSize = options.elementSize;
|
|
319
351
|
}
|
|
320
352
|
|
|
353
|
+
if (options?.baseParamIndex !== undefined) {
|
|
354
|
+
result.baseParamIndex = options.baseParamIndex;
|
|
355
|
+
}
|
|
356
|
+
|
|
321
357
|
if (options?.sizeParamIndex !== undefined) {
|
|
322
358
|
result.sizeParamIndex = options.sizeParamIndex;
|
|
323
359
|
}
|
|
@@ -326,76 +362,107 @@ const arrayT = (
|
|
|
326
362
|
result.fixedSize = options.fixedSize;
|
|
327
363
|
}
|
|
328
364
|
|
|
365
|
+
if (options?.isBytes === true) {
|
|
366
|
+
result.isBytes = true;
|
|
367
|
+
}
|
|
368
|
+
|
|
329
369
|
return result;
|
|
330
370
|
};
|
|
331
371
|
|
|
332
372
|
/** Builds a descriptor for a `GList` of items. */
|
|
333
|
-
const listT = (
|
|
334
|
-
|
|
373
|
+
const listT = (
|
|
374
|
+
itemDescriptor: Descriptor,
|
|
375
|
+
ownership: Ownership = "borrowed",
|
|
376
|
+
options: ArrayOptions = {},
|
|
377
|
+
): ArrayDescriptor => arrayT(itemDescriptor, "glist", ownership, options);
|
|
335
378
|
|
|
336
379
|
/** Builds a descriptor for a `GSList` of items. */
|
|
337
|
-
const slistT = (
|
|
338
|
-
|
|
380
|
+
const slistT = (
|
|
381
|
+
itemDescriptor: Descriptor,
|
|
382
|
+
ownership: Ownership = "borrowed",
|
|
383
|
+
options: ArrayOptions = {},
|
|
384
|
+
): ArrayDescriptor => arrayT(itemDescriptor, "gslist", ownership, options);
|
|
339
385
|
|
|
340
386
|
/** Builds a descriptor for a `GPtrArray` of items. */
|
|
341
|
-
const ptrArrayT = (
|
|
342
|
-
|
|
387
|
+
const ptrArrayT = (
|
|
388
|
+
itemDescriptor: Descriptor,
|
|
389
|
+
ownership: Ownership = "borrowed",
|
|
390
|
+
options: ArrayOptions = {},
|
|
391
|
+
): ArrayDescriptor => arrayT(itemDescriptor, "gptrarray", ownership, options);
|
|
343
392
|
|
|
344
|
-
/** Builds a descriptor for a `GArray` of items
|
|
393
|
+
/** Builds a descriptor for a `GArray` of items. */
|
|
345
394
|
const gArrayT = (
|
|
346
395
|
itemDescriptor: Descriptor,
|
|
347
396
|
ownership: Ownership = "borrowed",
|
|
348
|
-
|
|
349
|
-
): ArrayDescriptor =>
|
|
350
|
-
arrayT(itemDescriptor, "garray", ownership, elementSize === undefined ? undefined : { elementSize });
|
|
397
|
+
options: ArrayOptions = {},
|
|
398
|
+
): ArrayDescriptor => arrayT(itemDescriptor, "garray", ownership, options);
|
|
351
399
|
|
|
352
400
|
/** Builds a descriptor for a `GByteArray`. */
|
|
353
401
|
const byteArrayT = (ownership: Ownership = "borrowed"): ArrayDescriptor =>
|
|
354
|
-
arrayT(uint8T, "gbytearray", ownership);
|
|
402
|
+
arrayT(uint8T, "gbytearray", ownership, { isBytes: true });
|
|
355
403
|
|
|
356
404
|
/** Builds a descriptor for a C array whose length is carried by another argument. */
|
|
357
405
|
const sizedArrayT = (
|
|
358
406
|
itemDescriptor: Descriptor,
|
|
359
407
|
sizeParamIndex: number,
|
|
360
408
|
ownership: Ownership = "borrowed",
|
|
361
|
-
|
|
362
|
-
): ArrayDescriptor => arrayT(itemDescriptor, "sized", ownership, {
|
|
409
|
+
options: ArrayOptions = {},
|
|
410
|
+
): ArrayDescriptor => arrayT(itemDescriptor, "sized", ownership, { ...options, sizeParamIndex });
|
|
411
|
+
|
|
412
|
+
/**
|
|
413
|
+
* Builds a descriptor for an out pointer into the buffer another argument supplied, decoded as the
|
|
414
|
+
* elements from where it points to the end of that buffer.
|
|
415
|
+
*/
|
|
416
|
+
const cursorArrayT = (
|
|
417
|
+
itemDescriptor: Descriptor,
|
|
418
|
+
bounds: CursorBounds,
|
|
419
|
+
ownership: Ownership = "borrowed",
|
|
420
|
+
options: ArrayOptions = {},
|
|
421
|
+
): ArrayDescriptor => arrayT(itemDescriptor, "cursor", ownership, { ...options, ...bounds });
|
|
363
422
|
|
|
364
423
|
/** Builds a descriptor for a C array of a fixed length. */
|
|
365
424
|
const fixedArrayT = (
|
|
366
425
|
itemDescriptor: Descriptor,
|
|
367
426
|
fixedSize: number,
|
|
368
427
|
ownership: Ownership = "borrowed",
|
|
369
|
-
|
|
370
|
-
): ArrayDescriptor => arrayT(itemDescriptor, "fixed", ownership, {
|
|
428
|
+
options: ArrayOptions = {},
|
|
429
|
+
): ArrayDescriptor => arrayT(itemDescriptor, "fixed", ownership, { ...options, fixedSize });
|
|
371
430
|
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
argDescriptors: Descriptor[],
|
|
375
|
-
returnDescriptor: Descriptor,
|
|
376
|
-
options?: CallbackOptions,
|
|
377
|
-
): CallbackDescriptor => {
|
|
378
|
-
const result: CallbackDescriptor = { kind: "callback", argDescriptors, returnDescriptor };
|
|
379
|
-
|
|
380
|
-
if (options?.hasDestroy !== undefined) {
|
|
431
|
+
const applyClosureOptions = (result: CallbackDescriptor, options: CallbackOptions): void => {
|
|
432
|
+
if (options.hasDestroy !== undefined) {
|
|
381
433
|
result.hasDestroy = options.hasDestroy;
|
|
382
434
|
}
|
|
383
435
|
|
|
384
|
-
if (options
|
|
436
|
+
if (options.destroyKind !== undefined) {
|
|
385
437
|
result.destroyKind = options.destroyKind;
|
|
386
438
|
}
|
|
387
439
|
|
|
388
|
-
if (options
|
|
440
|
+
if (options.hasUserData !== undefined) {
|
|
389
441
|
result.hasUserData = options.hasUserData;
|
|
390
442
|
}
|
|
391
443
|
|
|
392
|
-
if (options
|
|
444
|
+
if (options.userDataIndex !== undefined) {
|
|
393
445
|
result.userDataIndex = options.userDataIndex;
|
|
394
446
|
}
|
|
395
447
|
|
|
396
|
-
if (options
|
|
448
|
+
if (options.scope !== undefined) {
|
|
397
449
|
result.scope = options.scope;
|
|
398
450
|
}
|
|
451
|
+
};
|
|
452
|
+
|
|
453
|
+
/** Builds a descriptor for a function pointer, marshalling a JavaScript function into a native closure. */
|
|
454
|
+
const callbackT = (
|
|
455
|
+
argDescriptors: Descriptor[],
|
|
456
|
+
returnDescriptor: Descriptor,
|
|
457
|
+
options?: CallbackOptions,
|
|
458
|
+
): CallbackDescriptor => {
|
|
459
|
+
const result: CallbackDescriptor = { kind: "callback", argDescriptors, returnDescriptor };
|
|
460
|
+
|
|
461
|
+
if (options === undefined) {
|
|
462
|
+
return result;
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
applyClosureOptions(result, options);
|
|
399
466
|
|
|
400
467
|
return result;
|
|
401
468
|
};
|
|
@@ -426,6 +493,7 @@ export {
|
|
|
426
493
|
flagsT,
|
|
427
494
|
boxedT,
|
|
428
495
|
structT,
|
|
496
|
+
fundamentalLifecycleFor,
|
|
429
497
|
fundamentalT,
|
|
430
498
|
arrayT,
|
|
431
499
|
listT,
|
|
@@ -435,6 +503,7 @@ export {
|
|
|
435
503
|
byteArrayT,
|
|
436
504
|
sizedArrayT,
|
|
437
505
|
fixedArrayT,
|
|
506
|
+
cursorArrayT,
|
|
438
507
|
callbackT,
|
|
439
508
|
type BoxedDescriptor,
|
|
440
509
|
type StructDescriptor,
|
package/src/exit-hook.ts
CHANGED
|
@@ -1,3 +1,21 @@
|
|
|
1
1
|
import { quit } from "./lifecycle.js";
|
|
2
2
|
|
|
3
|
+
const TERMINATION_SIGNALS: NodeJS.Signals[] = ["SIGHUP", "SIGINT", "SIGTERM"];
|
|
4
|
+
|
|
5
|
+
const isSoleHandler = (signal: NodeJS.Signals): boolean => process.listenerCount(signal) === 1;
|
|
6
|
+
|
|
7
|
+
const quitOnSignal = (signal: NodeJS.Signals): void => {
|
|
8
|
+
if (!isSoleHandler(signal)) {
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
quit();
|
|
13
|
+
process.removeAllListeners(signal);
|
|
14
|
+
process.kill(process.pid, signal);
|
|
15
|
+
};
|
|
16
|
+
|
|
3
17
|
process.on("exit", quit);
|
|
18
|
+
|
|
19
|
+
for (const signal of TERMINATION_SIGNALS) {
|
|
20
|
+
process.on(signal, quitOnSignal);
|
|
21
|
+
}
|
package/src/fn.ts
CHANGED
|
@@ -8,7 +8,7 @@ import { checkError } from "./error.js";
|
|
|
8
8
|
import { LIB } from "./library.js";
|
|
9
9
|
import { fromNative } from "./native-value.js";
|
|
10
10
|
import { getHandle } from "./registry.js";
|
|
11
|
-
import { packTupleResult } from "./tuple.js";
|
|
11
|
+
import { hasSurfacedPrimary, packTupleResult } from "./tuple.js";
|
|
12
12
|
|
|
13
13
|
/** The signature a native function is bound against. */
|
|
14
14
|
type FnSpec = {
|
|
@@ -16,6 +16,11 @@ type FnSpec = {
|
|
|
16
16
|
args: Arg[];
|
|
17
17
|
/** Descriptor for the C return value, packed first when output arguments are also returned. */
|
|
18
18
|
returns: Descriptor;
|
|
19
|
+
/**
|
|
20
|
+
* The callee returns a value the bindings do not surface, so the call is made against the
|
|
21
|
+
* declared return type and the value is dropped instead of being packed into the result.
|
|
22
|
+
*/
|
|
23
|
+
isReturnSkipped?: boolean;
|
|
19
24
|
/** The function takes a trailing `GError**`, whose contents are thrown as an error on return. */
|
|
20
25
|
canThrow?: boolean;
|
|
21
26
|
/**
|
|
@@ -135,15 +140,19 @@ const resizeInputs = (inputs: unknown[], argCount: number): unknown[] => {
|
|
|
135
140
|
const directCallable = (
|
|
136
141
|
descriptor: ExternalObject<CallDescriptor>,
|
|
137
142
|
returnDescriptor: Descriptor,
|
|
143
|
+
hasPrimary: boolean,
|
|
138
144
|
argCount: number,
|
|
139
145
|
): ((...inputs: unknown[]) => unknown) => {
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
146
|
+
const marshal = (inputs: unknown[]): unknown =>
|
|
147
|
+
fromNative(returnDescriptor, call(descriptor, resizeInputs(inputs, argCount)));
|
|
148
|
+
|
|
149
|
+
if (hasPrimary) {
|
|
150
|
+
return (...inputs) => marshal(inputs);
|
|
144
151
|
}
|
|
145
152
|
|
|
146
|
-
return (...inputs) =>
|
|
153
|
+
return (...inputs) => {
|
|
154
|
+
marshal(inputs);
|
|
155
|
+
};
|
|
147
156
|
};
|
|
148
157
|
|
|
149
158
|
function fromNativeCallable(
|
|
@@ -152,20 +161,21 @@ function fromNativeCallable(
|
|
|
152
161
|
getRefSeeds?: () => RefSeeds | undefined,
|
|
153
162
|
): (...inputs: unknown[]) => unknown {
|
|
154
163
|
const { args, returns: returnDescriptor, canThrow = false } = spec;
|
|
155
|
-
const hasPrimary = returnDescriptor.
|
|
164
|
+
const hasPrimary = hasSurfacedPrimary(returnDescriptor, spec.isReturnSkipped);
|
|
156
165
|
const plans = buildArgSpecs(args);
|
|
157
166
|
const outPlans = plans.filter((plan) => plan.isOutParam);
|
|
158
167
|
const arePassThrough = plans.every((plan, index) => isPassThroughPlan(plan, index));
|
|
159
168
|
|
|
160
169
|
if (!canThrow && arePassThrough) {
|
|
161
|
-
return directCallable(descriptor, returnDescriptor, plans.length);
|
|
170
|
+
return directCallable(descriptor, returnDescriptor, hasPrimary, plans.length);
|
|
162
171
|
}
|
|
163
172
|
|
|
164
|
-
const shape = (inputs: unknown[], nativeValues: unknown[], nativeResult: unknown): unknown =>
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
173
|
+
const shape = (inputs: unknown[], nativeValues: unknown[], nativeResult: unknown): unknown =>
|
|
174
|
+
packTupleResult(
|
|
175
|
+
readOutParams(outPlans, inputs, nativeValues),
|
|
176
|
+
fromNative(returnDescriptor, nativeResult),
|
|
177
|
+
hasPrimary,
|
|
178
|
+
);
|
|
169
179
|
|
|
170
180
|
if (canThrow) {
|
|
171
181
|
return (...inputs) => {
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import type { Descriptor } from "@gtkx/native";
|
|
2
|
+
|
|
3
|
+
type LengthSource = { kind: "return" } | { kind: "outArg"; argIndex: number };
|
|
4
|
+
type LengthSources = Map<number, LengthSource[]>;
|
|
5
|
+
|
|
6
|
+
type FoldedLengthSpec = {
|
|
7
|
+
argDescriptors: Descriptor[];
|
|
8
|
+
returnDescriptor: Descriptor;
|
|
9
|
+
userDataIndex?: number | undefined;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
const sizedArrayLengthIndex = (descriptor: Descriptor): number | undefined =>
|
|
13
|
+
descriptor.kind === "array" && descriptor.arrayKind === "sized" ? descriptor.sizeParamIndex : undefined;
|
|
14
|
+
|
|
15
|
+
const outArgLengthIndex = (descriptor: Descriptor | undefined): number | undefined =>
|
|
16
|
+
descriptor?.kind === "ref" ? sizedArrayLengthIndex(descriptor.innerDescriptor) : undefined;
|
|
17
|
+
|
|
18
|
+
const effectiveArgIndex = (argIndex: number | undefined, userDataIndex: number | undefined): number | undefined => {
|
|
19
|
+
if (argIndex === undefined || userDataIndex === undefined || argIndex < userDataIndex) {
|
|
20
|
+
return argIndex;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return argIndex === userDataIndex ? undefined : argIndex - 1;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const addLengthSource = (sources: LengthSources, index: number | undefined, source: LengthSource): void => {
|
|
27
|
+
if (index === undefined) {
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const existing = sources.get(index);
|
|
32
|
+
|
|
33
|
+
if (existing === undefined) {
|
|
34
|
+
sources.set(index, [source]);
|
|
35
|
+
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
existing.push(source);
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const addOutArgLengthSources = (sources: LengthSources, spec: FoldedLengthSpec): void => {
|
|
43
|
+
const { userDataIndex } = spec;
|
|
44
|
+
|
|
45
|
+
for (const [declaredIndex, descriptor] of spec.argDescriptors.entries()) {
|
|
46
|
+
const argIndex = effectiveArgIndex(declaredIndex, userDataIndex);
|
|
47
|
+
|
|
48
|
+
if (argIndex !== undefined) {
|
|
49
|
+
const lengthIndex = effectiveArgIndex(outArgLengthIndex(descriptor), userDataIndex);
|
|
50
|
+
addLengthSource(sources, lengthIndex, { kind: "outArg", argIndex });
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const foldedLengthSources = (spec: FoldedLengthSpec): LengthSources => {
|
|
56
|
+
const sources: LengthSources = new Map();
|
|
57
|
+
const returnLengthIndex = effectiveArgIndex(sizedArrayLengthIndex(spec.returnDescriptor), spec.userDataIndex);
|
|
58
|
+
addLengthSource(sources, returnLengthIndex, { kind: "return" });
|
|
59
|
+
addOutArgLengthSources(sources, spec);
|
|
60
|
+
|
|
61
|
+
return sources;
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
export { foldedLengthSources, type LengthSource, type LengthSources };
|