@gtkx/runtime 1.0.0-rc.3 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -9
- package/dist/application-class.d.ts +32 -0
- package/dist/application-class.d.ts.map +1 -0
- package/dist/application-class.js +68 -0
- package/dist/application-class.js.map +1 -0
- package/dist/arg.d.ts +9 -3
- package/dist/arg.d.ts.map +1 -1
- package/dist/arg.js +4 -3
- package/dist/arg.js.map +1 -1
- package/dist/bind.d.ts +11 -0
- package/dist/bind.d.ts.map +1 -1
- package/dist/bind.js +11 -0
- package/dist/bind.js.map +1 -1
- package/dist/callback.d.ts +2 -1
- package/dist/callback.d.ts.map +1 -1
- package/dist/callback.js +119 -21
- package/dist/callback.js.map +1 -1
- package/dist/closure.d.ts +26 -0
- package/dist/closure.d.ts.map +1 -0
- package/dist/closure.js +99 -0
- package/dist/closure.js.map +1 -0
- package/dist/descriptors.d.ts +105 -6
- package/dist/descriptors.d.ts.map +1 -1
- package/dist/descriptors.js +56 -12
- package/dist/descriptors.js.map +1 -1
- package/dist/error.d.ts +7 -0
- package/dist/error.d.ts.map +1 -1
- package/dist/error.js +1 -1
- package/dist/error.js.map +1 -1
- package/dist/fn.d.ts +24 -3
- package/dist/fn.d.ts.map +1 -1
- package/dist/fn.js +70 -33
- package/dist/fn.js.map +1 -1
- package/dist/index.d.ts +29 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +26 -4
- package/dist/index.js.map +1 -1
- package/dist/internal.d.ts +1 -0
- package/dist/internal.d.ts.map +1 -1
- package/dist/internal.js +1 -0
- package/dist/internal.js.map +1 -1
- package/dist/library.d.ts +2 -1
- package/dist/library.d.ts.map +1 -1
- package/dist/library.js +2 -1
- package/dist/library.js.map +1 -1
- package/dist/lifecycle.d.ts +52 -10
- package/dist/lifecycle.d.ts.map +1 -1
- package/dist/lifecycle.js +62 -18
- package/dist/lifecycle.js.map +1 -1
- package/dist/listeners.d.ts +6 -0
- package/dist/listeners.d.ts.map +1 -1
- package/dist/listeners.js +3 -1
- package/dist/listeners.js.map +1 -1
- package/dist/mixin.d.ts +16 -2
- package/dist/mixin.d.ts.map +1 -1
- package/dist/mixin.js +18 -6
- package/dist/mixin.js.map +1 -1
- package/dist/native-value.d.ts +8 -0
- package/dist/native-value.d.ts.map +1 -1
- package/dist/native-value.js +12 -3
- package/dist/native-value.js.map +1 -1
- package/dist/object.d.ts +34 -6
- package/dist/object.d.ts.map +1 -1
- package/dist/object.js +89 -25
- package/dist/object.js.map +1 -1
- package/dist/param-spec.d.ts +11 -0
- package/dist/param-spec.d.ts.map +1 -0
- package/dist/param-spec.js +149 -0
- package/dist/param-spec.js.map +1 -0
- package/dist/promisify.d.ts +3 -5
- package/dist/promisify.d.ts.map +1 -1
- package/dist/promisify.js +4 -3
- package/dist/promisify.js.map +1 -1
- package/dist/properties.d.ts +39 -7
- package/dist/properties.d.ts.map +1 -1
- package/dist/properties.js +305 -42
- package/dist/properties.js.map +1 -1
- package/dist/register-class.d.ts +119 -14
- package/dist/register-class.d.ts.map +1 -1
- package/dist/register-class.js +109 -80
- package/dist/register-class.js.map +1 -1
- package/dist/registry.d.ts +81 -17
- package/dist/registry.d.ts.map +1 -1
- package/dist/registry.js +72 -26
- package/dist/registry.js.map +1 -1
- package/dist/signal.d.ts +22 -3
- package/dist/signal.d.ts.map +1 -1
- package/dist/signal.js +64 -8
- package/dist/signal.js.map +1 -1
- package/dist/t.d.ts +44 -0
- package/dist/t.d.ts.map +1 -1
- package/dist/t.js.map +1 -1
- package/dist/type.d.ts +2 -2
- package/dist/type.d.ts.map +1 -1
- package/dist/type.js +2 -0
- package/dist/type.js.map +1 -1
- package/dist/value.d.ts +10 -10
- package/dist/value.d.ts.map +1 -1
- package/dist/value.js +44 -22
- package/dist/value.js.map +1 -1
- package/dist/vfunc-call.d.ts +37 -0
- package/dist/vfunc-call.d.ts.map +1 -0
- package/dist/vfunc-call.js +168 -0
- package/dist/vfunc-call.js.map +1 -0
- package/dist/vfunc-seeds.d.ts +12 -0
- package/dist/vfunc-seeds.d.ts.map +1 -0
- package/dist/vfunc-seeds.js +19 -0
- package/dist/vfunc-seeds.js.map +1 -0
- package/dist/vfunc.d.ts +8 -0
- package/dist/vfunc.d.ts.map +1 -0
- package/dist/vfunc.js +23 -0
- package/dist/vfunc.js.map +1 -0
- package/package.json +4 -4
- package/src/application-class.ts +116 -0
- package/src/arg.ts +11 -5
- package/src/bind.ts +11 -0
- package/src/callback.ts +163 -23
- package/src/closure.ts +136 -0
- package/src/descriptors.ts +137 -41
- package/src/error.ts +8 -1
- package/src/fn.ts +104 -39
- package/src/index.ts +36 -7
- package/src/internal.ts +1 -0
- package/src/library.ts +2 -1
- package/src/lifecycle.ts +99 -22
- package/src/listeners.ts +9 -1
- package/src/mixin.ts +27 -8
- package/src/native-value.ts +13 -3
- package/src/object.ts +128 -34
- package/src/param-spec.ts +235 -0
- package/src/promisify.ts +8 -8
- package/src/properties.ts +459 -45
- package/src/register-class.ts +329 -114
- package/src/registry.ts +150 -38
- package/src/signal.ts +101 -17
- package/src/t.ts +44 -0
- package/src/type.ts +3 -2
- package/src/value.ts +57 -19
- package/src/vfunc-call.ts +243 -0
- package/src/vfunc-seeds.ts +36 -0
- package/src/vfunc.ts +31 -0
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import { bindVfunc } from "@gtkx/native";
|
|
2
|
+
import { isCallerAllocatedArg, requiresInputArg } from "./arg.js";
|
|
3
|
+
import { buildNativeArgTypes, fromNativeCallable } from "./fn.js";
|
|
4
|
+
import { toNative } from "./native-value.js";
|
|
5
|
+
import { getClassType, getHandle, getInterfaceVfuncRegistry, getVfuncRegistry, instanceClassName, resolveWrapperType, } from "./registry.js";
|
|
6
|
+
import { TYPE_INVALID, typeInterfaces, typeIsA, typeParent } from "./type.js";
|
|
7
|
+
import { seedsFor } from "./vfunc-seeds.js";
|
|
8
|
+
import { findClassVfuncDescriptor, findInterfaceVfuncDescriptor, vfuncArgs } from "./vfunc.js";
|
|
9
|
+
const NO_BASELINE = -1;
|
|
10
|
+
const SEEDED_SLOTS = {
|
|
11
|
+
"LayoutManagerClass.measure": new Map([[6, NO_BASELINE], [7, NO_BASELINE]]),
|
|
12
|
+
"WidgetClass.measure": new Map([[5, NO_BASELINE], [6, NO_BASELINE]]),
|
|
13
|
+
};
|
|
14
|
+
const parentInvokers = new WeakMap();
|
|
15
|
+
const vfuncInvokers = new WeakMap();
|
|
16
|
+
function toNativeInput(arg, input) {
|
|
17
|
+
return isCallerAllocatedArg(arg) ? input : toNative(arg.type, input);
|
|
18
|
+
}
|
|
19
|
+
function bindOptionsFor(slot, instanceType, args, label) {
|
|
20
|
+
const { descriptor, interfaceType } = slot;
|
|
21
|
+
const options = {
|
|
22
|
+
byteOffset: descriptor.byteOffset,
|
|
23
|
+
label,
|
|
24
|
+
argDescriptors: buildNativeArgTypes(args, descriptor.canThrow === true),
|
|
25
|
+
returnDescriptor: descriptor.returnDescriptor,
|
|
26
|
+
};
|
|
27
|
+
if (descriptor.vtableSize !== undefined) {
|
|
28
|
+
options.vtableSize = descriptor.vtableSize;
|
|
29
|
+
}
|
|
30
|
+
if (instanceType !== undefined) {
|
|
31
|
+
options.instanceType = instanceType;
|
|
32
|
+
}
|
|
33
|
+
if (interfaceType !== undefined) {
|
|
34
|
+
options.interfaceType = interfaceType;
|
|
35
|
+
}
|
|
36
|
+
return options;
|
|
37
|
+
}
|
|
38
|
+
function buildInvoker(slot, instanceType, caller) {
|
|
39
|
+
const { descriptor } = slot;
|
|
40
|
+
const args = vfuncArgs(descriptor);
|
|
41
|
+
const canThrow = descriptor.canThrow === true;
|
|
42
|
+
const label = `${descriptor.className}.${descriptor.vfuncName}`;
|
|
43
|
+
const [, ...inputArgs] = args.filter(requiresInputArg);
|
|
44
|
+
let pendingSeeds;
|
|
45
|
+
const takeRefSeeds = () => {
|
|
46
|
+
const seeds = pendingSeeds;
|
|
47
|
+
pendingSeeds = undefined;
|
|
48
|
+
return seeds;
|
|
49
|
+
};
|
|
50
|
+
const shaped = fromNativeCallable(bindVfunc(bindOptionsFor(slot, instanceType, args, label)), { args, returns: descriptor.returnDescriptor, canThrow }, takeRefSeeds);
|
|
51
|
+
return (instance, inputs) => {
|
|
52
|
+
if (inputs.length !== inputArgs.length) {
|
|
53
|
+
throw new Error(`${caller}: ${label} expects ${String(inputArgs.length)} arguments, ` +
|
|
54
|
+
`received ${String(inputs.length)}`);
|
|
55
|
+
}
|
|
56
|
+
const nativeInputs = inputArgs.map((arg, index) => toNativeInput(arg, inputs[index]));
|
|
57
|
+
pendingSeeds = SEEDED_SLOTS[label] ?? seedsFor(descriptor.argDescriptors, instance);
|
|
58
|
+
return shaped(getHandle(instance), ...nativeInputs);
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
function cachedInvoker(cache, owner, key, build) {
|
|
62
|
+
let byKey = cache.get(owner);
|
|
63
|
+
if (byKey === undefined) {
|
|
64
|
+
byKey = new Map();
|
|
65
|
+
cache.set(owner, byKey);
|
|
66
|
+
}
|
|
67
|
+
let invoker = byKey.get(key);
|
|
68
|
+
if (invoker === undefined) {
|
|
69
|
+
invoker = build();
|
|
70
|
+
byKey.set(key, invoker);
|
|
71
|
+
}
|
|
72
|
+
return invoker;
|
|
73
|
+
}
|
|
74
|
+
function resolveParentSlot(klass, parentType, methodName) {
|
|
75
|
+
const classDescriptor = findClassVfuncDescriptor(klass, methodName);
|
|
76
|
+
if (classDescriptor) {
|
|
77
|
+
return { descriptor: classDescriptor };
|
|
78
|
+
}
|
|
79
|
+
for (const interfaceType of typeInterfaces(parentType)) {
|
|
80
|
+
const descriptor = findInterfaceVfuncDescriptor(interfaceType, methodName);
|
|
81
|
+
if (descriptor) {
|
|
82
|
+
return { descriptor, interfaceType };
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return undefined;
|
|
86
|
+
}
|
|
87
|
+
function resolveParentType(klass, methodName) {
|
|
88
|
+
const gtype = getClassType(klass);
|
|
89
|
+
if (gtype === TYPE_INVALID) {
|
|
90
|
+
throw new Error(`callParent: cannot call '${methodName}' because ${klass.name} was never passed to registerClass`);
|
|
91
|
+
}
|
|
92
|
+
return typeParent(gtype);
|
|
93
|
+
}
|
|
94
|
+
function buildParentInvoker(klass, methodName) {
|
|
95
|
+
const parentType = resolveParentType(klass, methodName);
|
|
96
|
+
const slot = resolveParentSlot(klass, parentType, methodName);
|
|
97
|
+
if (!slot) {
|
|
98
|
+
throw new Error(`callParent: ${klass.name} inherits no '${methodName}' vtable slot`);
|
|
99
|
+
}
|
|
100
|
+
return buildInvoker(slot, parentType, "callParent");
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Calls the implementation of a virtual function that the parent type of `klass` provides, so an
|
|
104
|
+
* override can chain up to the behavior it replaces. `klass` is the class whose override is
|
|
105
|
+
* running, named lexically rather than taken from `instance`, so each level of a hierarchy reaches
|
|
106
|
+
* exactly one level up. Arguments and the return value follow the same convention the override
|
|
107
|
+
* itself uses: pure out parameters are left out and returned instead, and a slot with several
|
|
108
|
+
* outputs returns them as a tuple. Throws if `klass` was never registered, inherits no such slot,
|
|
109
|
+
* or the parent type leaves the slot empty.
|
|
110
|
+
*
|
|
111
|
+
* @param klass The registered class whose override is chaining up.
|
|
112
|
+
* @param methodName Name of the overridden method, such as `vfuncMeasure`.
|
|
113
|
+
* @param instance The instance the override was invoked on.
|
|
114
|
+
* @param inputs The arguments the override received.
|
|
115
|
+
*/
|
|
116
|
+
function callParent(klass, methodName, instance, ...inputs) {
|
|
117
|
+
const invoker = cachedInvoker(parentInvokers, klass, methodName, () => buildParentInvoker(klass, methodName));
|
|
118
|
+
return invoker(instance, inputs);
|
|
119
|
+
}
|
|
120
|
+
function resolveOwnerSlot(owner, key) {
|
|
121
|
+
const classDescriptor = getVfuncRegistry(owner)?.[key];
|
|
122
|
+
if (classDescriptor !== undefined) {
|
|
123
|
+
return { descriptor: classDescriptor };
|
|
124
|
+
}
|
|
125
|
+
const interfaceType = getClassType(owner);
|
|
126
|
+
const descriptor = getInterfaceVfuncRegistry(interfaceType)?.[key];
|
|
127
|
+
if (descriptor === undefined) {
|
|
128
|
+
throw new Error(`callVfunc: ${owner.name} declares no '${key}' vtable slot`);
|
|
129
|
+
}
|
|
130
|
+
return { descriptor, interfaceType };
|
|
131
|
+
}
|
|
132
|
+
function requiresDefaultVtable(slot, instanceType) {
|
|
133
|
+
return slot.interfaceType !== undefined && !typeIsA(instanceType, slot.interfaceType);
|
|
134
|
+
}
|
|
135
|
+
function resolveInstanceType(key, instance, slot) {
|
|
136
|
+
const instanceType = resolveWrapperType(instance);
|
|
137
|
+
if (instanceType === TYPE_INVALID) {
|
|
138
|
+
const name = instanceClassName(instance);
|
|
139
|
+
throw new Error(`callVfunc: cannot call '${key}' because ${name} descends from no registered wrapper class`);
|
|
140
|
+
}
|
|
141
|
+
return requiresDefaultVtable(slot, instanceType) ? undefined : instanceType;
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Calls the implementation of a virtual function a wrapper class or interface declares, backing the
|
|
145
|
+
* `vfunc`-prefixed members the generated bindings emit. The slot is read from the nearest generated
|
|
146
|
+
* wrapper class in the instance's class chain rather than from the instance's own type, so an
|
|
147
|
+
* override reaching it through `super` runs the implementation it replaced instead of re-entering
|
|
148
|
+
* itself. When that wrapper class does not carry the interface the slot belongs to, because a class
|
|
149
|
+
* registered below it adopted the interface, the slot comes from the interface's own default vtable
|
|
150
|
+
* instead, which holds exactly what the adopting class replaced. Arguments and the return value
|
|
151
|
+
* follow the same convention an override uses: pure out parameters are left out and returned
|
|
152
|
+
* instead, and a slot with several outputs returns them as a tuple. Throws if the owner declares no
|
|
153
|
+
* such slot, the instance descends from no wrapper class, or the resolved vtable leaves the slot
|
|
154
|
+
* empty.
|
|
155
|
+
*
|
|
156
|
+
* @param owner The wrapper class or interface declaring the slot.
|
|
157
|
+
* @param key Name of the generated member, such as `vfuncMeasure`.
|
|
158
|
+
* @param instance The instance to invoke the slot on.
|
|
159
|
+
* @param inputs The arguments the slot receives.
|
|
160
|
+
*/
|
|
161
|
+
function callVfunc(owner, key, instance, inputs) {
|
|
162
|
+
const slot = resolveOwnerSlot(owner, key);
|
|
163
|
+
const instanceType = resolveInstanceType(key, instance, slot);
|
|
164
|
+
const invoker = cachedInvoker(vfuncInvokers, owner, `${key}:${String(instanceType)}`, () => buildInvoker(slot, instanceType, "callVfunc"));
|
|
165
|
+
return invoker(instance, inputs);
|
|
166
|
+
}
|
|
167
|
+
export { callParent, callVfunc };
|
|
168
|
+
//# sourceMappingURL=vfunc-call.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vfunc-call.js","sourceRoot":"","sources":["../src/vfunc-call.ts"],"names":[],"mappings":"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","sourcesContent":["import type { BindVfuncOptions } from \"@gtkx/native\";\nimport type { AnyClass } from \"@gtkx/utils\";\nimport { bindVfunc } from \"@gtkx/native\";\nimport { type Arg, isCallerAllocatedArg, requiresInputArg } from \"./arg.js\";\nimport { buildNativeArgTypes, fromNativeCallable } from \"./fn.js\";\nimport { toNative } from \"./native-value.js\";\nimport {\n getClassType,\n getHandle,\n getInterfaceVfuncRegistry,\n getVfuncRegistry,\n instanceClassName,\n resolveWrapperType,\n type VfuncDescriptor,\n} from \"./registry.js\";\nimport { TYPE_INVALID, typeInterfaces, typeIsA, typeParent } from \"./type.js\";\nimport { type RefSeeds, seedsFor } from \"./vfunc-seeds.js\";\nimport { findClassVfuncDescriptor, findInterfaceVfuncDescriptor, vfuncArgs } from \"./vfunc.js\";\n\ntype Invoker = (instance: object, inputs: unknown[]) => unknown;\ntype InvokerCache = WeakMap<AnyClass, Map<string, Invoker>>;\ntype ResolvedSlot = { descriptor: VfuncDescriptor; interfaceType?: bigint };\n\nconst NO_BASELINE = -1;\n\nconst SEEDED_SLOTS: Record<string, RefSeeds> = {\n \"LayoutManagerClass.measure\": new Map([[6, NO_BASELINE], [7, NO_BASELINE]]),\n \"WidgetClass.measure\": new Map([[5, NO_BASELINE], [6, NO_BASELINE]]),\n};\n\nconst parentInvokers: InvokerCache = new WeakMap();\nconst vfuncInvokers: InvokerCache = new WeakMap();\n\nfunction toNativeInput(arg: Arg, input: unknown): unknown {\n return isCallerAllocatedArg(arg) ? input : toNative(arg.type, input);\n}\n\nfunction bindOptionsFor(\n slot: ResolvedSlot,\n instanceType: bigint | undefined,\n args: Arg[],\n label: string,\n): BindVfuncOptions {\n const { descriptor, interfaceType } = slot;\n\n const options: BindVfuncOptions = {\n byteOffset: descriptor.byteOffset,\n label,\n argDescriptors: buildNativeArgTypes(args, descriptor.canThrow === true),\n returnDescriptor: descriptor.returnDescriptor,\n };\n\n if (descriptor.vtableSize !== undefined) {\n options.vtableSize = descriptor.vtableSize;\n }\n\n if (instanceType !== undefined) {\n options.instanceType = instanceType;\n }\n\n if (interfaceType !== undefined) {\n options.interfaceType = interfaceType;\n }\n\n return options;\n}\n\nfunction buildInvoker(slot: ResolvedSlot, instanceType: bigint | undefined, caller: string): Invoker {\n const { descriptor } = slot;\n const args = vfuncArgs(descriptor);\n const canThrow = descriptor.canThrow === true;\n const label = `${descriptor.className}.${descriptor.vfuncName}`;\n const [, ...inputArgs] = args.filter(requiresInputArg);\n let pendingSeeds: RefSeeds | undefined;\n\n const takeRefSeeds = (): RefSeeds | undefined => {\n const seeds = pendingSeeds;\n pendingSeeds = undefined;\n\n return seeds;\n };\n\n const shaped = fromNativeCallable(\n bindVfunc(bindOptionsFor(slot, instanceType, args, label)),\n { args, returns: descriptor.returnDescriptor, canThrow },\n takeRefSeeds,\n );\n\n return (instance, inputs) => {\n if (inputs.length !== inputArgs.length) {\n throw new Error(\n `${caller}: ${label} expects ${String(inputArgs.length)} arguments, ` +\n `received ${String(inputs.length)}`,\n );\n }\n\n const nativeInputs = inputArgs.map((arg, index) => toNativeInput(arg, inputs[index]));\n pendingSeeds = SEEDED_SLOTS[label] ?? seedsFor(descriptor.argDescriptors, instance);\n\n return shaped(getHandle(instance), ...nativeInputs);\n };\n}\n\nfunction cachedInvoker(cache: InvokerCache, owner: AnyClass, key: string, build: () => Invoker): Invoker {\n let byKey = cache.get(owner);\n\n if (byKey === undefined) {\n byKey = new Map();\n cache.set(owner, byKey);\n }\n\n let invoker = byKey.get(key);\n\n if (invoker === undefined) {\n invoker = build();\n byKey.set(key, invoker);\n }\n\n return invoker;\n}\n\nfunction resolveParentSlot(klass: AnyClass, parentType: bigint, methodName: string): ResolvedSlot | undefined {\n const classDescriptor = findClassVfuncDescriptor(klass, methodName);\n\n if (classDescriptor) {\n return { descriptor: classDescriptor };\n }\n\n for (const interfaceType of typeInterfaces(parentType)) {\n const descriptor = findInterfaceVfuncDescriptor(interfaceType, methodName);\n\n if (descriptor) {\n return { descriptor, interfaceType };\n }\n }\n\n return undefined;\n}\n\nfunction resolveParentType(klass: AnyClass, methodName: string): bigint {\n const gtype = getClassType(klass);\n\n if (gtype === TYPE_INVALID) {\n throw new Error(\n `callParent: cannot call '${methodName}' because ${klass.name} was never passed to registerClass`,\n );\n }\n\n return typeParent(gtype);\n}\n\nfunction buildParentInvoker(klass: AnyClass, methodName: string): Invoker {\n const parentType = resolveParentType(klass, methodName);\n const slot = resolveParentSlot(klass, parentType, methodName);\n\n if (!slot) {\n throw new Error(`callParent: ${klass.name} inherits no '${methodName}' vtable slot`);\n }\n\n return buildInvoker(slot, parentType, \"callParent\");\n}\n\n/**\n * Calls the implementation of a virtual function that the parent type of `klass` provides, so an\n * override can chain up to the behavior it replaces. `klass` is the class whose override is\n * running, named lexically rather than taken from `instance`, so each level of a hierarchy reaches\n * exactly one level up. Arguments and the return value follow the same convention the override\n * itself uses: pure out parameters are left out and returned instead, and a slot with several\n * outputs returns them as a tuple. Throws if `klass` was never registered, inherits no such slot,\n * or the parent type leaves the slot empty.\n *\n * @param klass The registered class whose override is chaining up.\n * @param methodName Name of the overridden method, such as `vfuncMeasure`.\n * @param instance The instance the override was invoked on.\n * @param inputs The arguments the override received.\n */\nfunction callParent(klass: AnyClass, methodName: string, instance: object, ...inputs: unknown[]): unknown {\n const invoker = cachedInvoker(parentInvokers, klass, methodName, () => buildParentInvoker(klass, methodName));\n\n return invoker(instance, inputs);\n}\n\nfunction resolveOwnerSlot(owner: AnyClass, key: string): ResolvedSlot {\n const classDescriptor = getVfuncRegistry(owner)?.[key];\n\n if (classDescriptor !== undefined) {\n return { descriptor: classDescriptor };\n }\n\n const interfaceType = getClassType(owner);\n const descriptor = getInterfaceVfuncRegistry(interfaceType)?.[key];\n\n if (descriptor === undefined) {\n throw new Error(`callVfunc: ${owner.name} declares no '${key}' vtable slot`);\n }\n\n return { descriptor, interfaceType };\n}\n\nfunction requiresDefaultVtable(slot: ResolvedSlot, instanceType: bigint): boolean {\n return slot.interfaceType !== undefined && !typeIsA(instanceType, slot.interfaceType);\n}\n\nfunction resolveInstanceType(key: string, instance: object, slot: ResolvedSlot): bigint | undefined {\n const instanceType = resolveWrapperType(instance);\n\n if (instanceType === TYPE_INVALID) {\n const name = instanceClassName(instance);\n throw new Error(`callVfunc: cannot call '${key}' because ${name} descends from no registered wrapper class`);\n }\n\n return requiresDefaultVtable(slot, instanceType) ? undefined : instanceType;\n}\n\n/**\n * Calls the implementation of a virtual function a wrapper class or interface declares, backing the\n * `vfunc`-prefixed members the generated bindings emit. The slot is read from the nearest generated\n * wrapper class in the instance's class chain rather than from the instance's own type, so an\n * override reaching it through `super` runs the implementation it replaced instead of re-entering\n * itself. When that wrapper class does not carry the interface the slot belongs to, because a class\n * registered below it adopted the interface, the slot comes from the interface's own default vtable\n * instead, which holds exactly what the adopting class replaced. Arguments and the return value\n * follow the same convention an override uses: pure out parameters are left out and returned\n * instead, and a slot with several outputs returns them as a tuple. Throws if the owner declares no\n * such slot, the instance descends from no wrapper class, or the resolved vtable leaves the slot\n * empty.\n *\n * @param owner The wrapper class or interface declaring the slot.\n * @param key Name of the generated member, such as `vfuncMeasure`.\n * @param instance The instance to invoke the slot on.\n * @param inputs The arguments the slot receives.\n */\nfunction callVfunc(owner: AnyClass, key: string, instance: object, inputs: unknown[]): unknown {\n const slot = resolveOwnerSlot(owner, key);\n const instanceType = resolveInstanceType(key, instance, slot);\n\n const invoker = cachedInvoker(vfuncInvokers, owner, `${key}:${String(instanceType)}`, () =>\n buildInvoker(slot, instanceType, \"callVfunc\"));\n\n return invoker(instance, inputs);\n}\n\nexport { callParent, callVfunc };\n"]}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Descriptor } from "@gtkx/native";
|
|
2
|
+
type RefSeeds = Map<number, unknown>;
|
|
3
|
+
type SeedFrame = {
|
|
4
|
+
argDescriptors: Descriptor[];
|
|
5
|
+
instance: unknown;
|
|
6
|
+
seeds: RefSeeds;
|
|
7
|
+
};
|
|
8
|
+
declare const pushSeedFrame: (frame: SeedFrame) => void;
|
|
9
|
+
declare const popSeedFrame: () => void;
|
|
10
|
+
declare const seedsFor: (argDescriptors: Descriptor[], instance: unknown) => RefSeeds | undefined;
|
|
11
|
+
export { popSeedFrame, pushSeedFrame, type RefSeeds, seedsFor };
|
|
12
|
+
//# sourceMappingURL=vfunc-seeds.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vfunc-seeds.d.ts","sourceRoot":"","sources":["../src/vfunc-seeds.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE/C,KAAK,QAAQ,GAAG,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAErC,KAAK,SAAS,GAAG;IACb,cAAc,EAAE,UAAU,EAAE,CAAC;IAC7B,QAAQ,EAAE,OAAO,CAAC;IAClB,KAAK,EAAE,QAAQ,CAAC;CACnB,CAAC;AAIF,QAAA,MAAM,aAAa,GAAI,OAAO,SAAS,KAAG,IAEzC,CAAC;AAEF,QAAA,MAAM,YAAY,QAAO,IAExB,CAAC;AAKF,QAAA,MAAM,QAAQ,GAAI,gBAAgB,UAAU,EAAE,EAAE,UAAU,OAAO,KAAG,QAAQ,GAAG,SAU9E,CAAC;AAEF,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,KAAK,QAAQ,EAAE,QAAQ,EAAE,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
const frames = [];
|
|
2
|
+
const pushSeedFrame = (frame) => {
|
|
3
|
+
frames.push(frame);
|
|
4
|
+
};
|
|
5
|
+
const popSeedFrame = () => {
|
|
6
|
+
frames.pop();
|
|
7
|
+
};
|
|
8
|
+
const isMatchingFrame = (frame, argDescriptors, instance) => frame.argDescriptors === argDescriptors && frame.instance === instance;
|
|
9
|
+
const seedsFor = (argDescriptors, instance) => {
|
|
10
|
+
for (let index = frames.length - 1; index >= 0; index--) {
|
|
11
|
+
const frame = frames[index];
|
|
12
|
+
if (frame !== undefined && isMatchingFrame(frame, argDescriptors, instance)) {
|
|
13
|
+
return frame.seeds;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
return undefined;
|
|
17
|
+
};
|
|
18
|
+
export { popSeedFrame, pushSeedFrame, seedsFor };
|
|
19
|
+
//# sourceMappingURL=vfunc-seeds.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vfunc-seeds.js","sourceRoot":"","sources":["../src/vfunc-seeds.ts"],"names":[],"mappings":"AAUA,MAAM,MAAM,GAAgB,EAAE,CAAC;AAE/B,MAAM,aAAa,GAAG,CAAC,KAAgB,EAAQ,EAAE;IAC7C,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;AACvB,CAAC,CAAC;AAEF,MAAM,YAAY,GAAG,GAAS,EAAE;IAC5B,MAAM,CAAC,GAAG,EAAE,CAAC;AACjB,CAAC,CAAC;AAEF,MAAM,eAAe,GAAG,CAAC,KAAgB,EAAE,cAA4B,EAAE,QAAiB,EAAW,EAAE,CACnG,KAAK,CAAC,cAAc,KAAK,cAAc,IAAI,KAAK,CAAC,QAAQ,KAAK,QAAQ,CAAC;AAE3E,MAAM,QAAQ,GAAG,CAAC,cAA4B,EAAE,QAAiB,EAAwB,EAAE;IACvF,KAAK,IAAI,KAAK,GAAG,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,KAAK,IAAI,CAAC,EAAE,KAAK,EAAE,EAAE,CAAC;QACtD,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;QAE5B,IAAI,KAAK,KAAK,SAAS,IAAI,eAAe,CAAC,KAAK,EAAE,cAAc,EAAE,QAAQ,CAAC,EAAE,CAAC;YAC1E,OAAO,KAAK,CAAC,KAAK,CAAC;QACvB,CAAC;IACL,CAAC;IAED,OAAO,SAAS,CAAC;AACrB,CAAC,CAAC;AAEF,OAAO,EAAE,YAAY,EAAE,aAAa,EAAiB,QAAQ,EAAE,CAAC","sourcesContent":["import type { Descriptor } from \"@gtkx/native\";\n\ntype RefSeeds = Map<number, unknown>;\n\ntype SeedFrame = {\n argDescriptors: Descriptor[];\n instance: unknown;\n seeds: RefSeeds;\n};\n\nconst frames: SeedFrame[] = [];\n\nconst pushSeedFrame = (frame: SeedFrame): void => {\n frames.push(frame);\n};\n\nconst popSeedFrame = (): void => {\n frames.pop();\n};\n\nconst isMatchingFrame = (frame: SeedFrame, argDescriptors: Descriptor[], instance: unknown): boolean =>\n frame.argDescriptors === argDescriptors && frame.instance === instance;\n\nconst seedsFor = (argDescriptors: Descriptor[], instance: unknown): RefSeeds | undefined => {\n for (let index = frames.length - 1; index >= 0; index--) {\n const frame = frames[index];\n\n if (frame !== undefined && isMatchingFrame(frame, argDescriptors, instance)) {\n return frame.seeds;\n }\n }\n\n return undefined;\n};\n\nexport { popSeedFrame, pushSeedFrame, type RefSeeds, seedsFor };\n"]}
|
package/dist/vfunc.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type AnyClass } from "@gtkx/utils";
|
|
2
|
+
import type { Arg } from "./arg.js";
|
|
3
|
+
import { type VfuncDescriptor } from "./registry.js";
|
|
4
|
+
declare function findClassVfuncDescriptor(klass: AnyClass, methodName: string): VfuncDescriptor | null;
|
|
5
|
+
declare function findInterfaceVfuncDescriptor(interfaceGtype: bigint, methodName: string): VfuncDescriptor | undefined;
|
|
6
|
+
declare function vfuncArgs(descriptor: VfuncDescriptor): Arg[];
|
|
7
|
+
export { findClassVfuncDescriptor, findInterfaceVfuncDescriptor, vfuncArgs };
|
|
8
|
+
//# sourceMappingURL=vfunc.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vfunc.d.ts","sourceRoot":"","sources":["../src/vfunc.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,QAAQ,EAAkC,MAAM,aAAa,CAAC;AAC5E,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAEpC,OAAO,EAA+C,KAAK,eAAe,EAAE,MAAM,eAAe,CAAC;AAElG,iBAAS,wBAAwB,CAAC,KAAK,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,GAAG,eAAe,GAAG,IAAI,CAE7F;AAED,iBAAS,4BAA4B,CAAC,cAAc,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,eAAe,GAAG,SAAS,CAE7G;AAcD,iBAAS,SAAS,CAAC,UAAU,EAAE,eAAe,GAAG,GAAG,EAAE,CAErD;AAED,OAAO,EAAE,wBAAwB,EAAE,4BAA4B,EAAE,SAAS,EAAE,CAAC"}
|
package/dist/vfunc.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { getParentClass, walkClassChain } from "@gtkx/utils";
|
|
2
|
+
import { isCallerAllocatedOut } from "./callback.js";
|
|
3
|
+
import { getInterfaceVfuncRegistry, getVfuncRegistry } from "./registry.js";
|
|
4
|
+
function findClassVfuncDescriptor(klass, methodName) {
|
|
5
|
+
return walkClassChain(getParentClass(klass), (cls) => getVfuncRegistry(cls)?.[methodName]) ?? null;
|
|
6
|
+
}
|
|
7
|
+
function findInterfaceVfuncDescriptor(interfaceGtype, methodName) {
|
|
8
|
+
return getInterfaceVfuncRegistry(interfaceGtype)?.[methodName];
|
|
9
|
+
}
|
|
10
|
+
function vfuncArg(descriptor) {
|
|
11
|
+
if (descriptor.kind === "ref") {
|
|
12
|
+
return { type: descriptor.innerDescriptor, direction: descriptor.inout === true ? "inout" : "out" };
|
|
13
|
+
}
|
|
14
|
+
if (isCallerAllocatedOut(descriptor)) {
|
|
15
|
+
return { type: descriptor, direction: "out", isCallerAllocated: true };
|
|
16
|
+
}
|
|
17
|
+
return { type: descriptor };
|
|
18
|
+
}
|
|
19
|
+
function vfuncArgs(descriptor) {
|
|
20
|
+
return descriptor.argDescriptors.map((argDescriptor) => vfuncArg(argDescriptor));
|
|
21
|
+
}
|
|
22
|
+
export { findClassVfuncDescriptor, findInterfaceVfuncDescriptor, vfuncArgs };
|
|
23
|
+
//# sourceMappingURL=vfunc.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vfunc.js","sourceRoot":"","sources":["../src/vfunc.ts"],"names":[],"mappings":"AACA,OAAO,EAAiB,cAAc,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAE5E,OAAO,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AACrD,OAAO,EAAE,yBAAyB,EAAE,gBAAgB,EAAwB,MAAM,eAAe,CAAC;AAElG,SAAS,wBAAwB,CAAC,KAAe,EAAE,UAAkB;IACjE,OAAO,cAAc,CAAC,cAAc,CAAC,KAAK,CAAC,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,gBAAgB,CAAC,GAAG,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,IAAI,IAAI,CAAC;AACvG,CAAC;AAED,SAAS,4BAA4B,CAAC,cAAsB,EAAE,UAAkB;IAC5E,OAAO,yBAAyB,CAAC,cAAc,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC;AACnE,CAAC;AAED,SAAS,QAAQ,CAAC,UAAsB;IACpC,IAAI,UAAU,CAAC,IAAI,KAAK,KAAK,EAAE,CAAC;QAC5B,OAAO,EAAE,IAAI,EAAE,UAAU,CAAC,eAAe,EAAE,SAAS,EAAE,UAAU,CAAC,KAAK,KAAK,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC;IACxG,CAAC;IAED,IAAI,oBAAoB,CAAC,UAAU,CAAC,EAAE,CAAC;QACnC,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,SAAS,EAAE,KAAK,EAAE,iBAAiB,EAAE,IAAI,EAAE,CAAC;IAC3E,CAAC;IAED,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC;AAChC,CAAC;AAED,SAAS,SAAS,CAAC,UAA2B;IAC1C,OAAO,UAAU,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,aAAa,EAAE,EAAE,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAC,CAAC;AACrF,CAAC;AAED,OAAO,EAAE,wBAAwB,EAAE,4BAA4B,EAAE,SAAS,EAAE,CAAC","sourcesContent":["import type { Descriptor } from \"@gtkx/native\";\nimport { type AnyClass, getParentClass, walkClassChain } from \"@gtkx/utils\";\nimport type { Arg } from \"./arg.js\";\nimport { isCallerAllocatedOut } from \"./callback.js\";\nimport { getInterfaceVfuncRegistry, getVfuncRegistry, type VfuncDescriptor } from \"./registry.js\";\n\nfunction findClassVfuncDescriptor(klass: AnyClass, methodName: string): VfuncDescriptor | null {\n return walkClassChain(getParentClass(klass), (cls) => getVfuncRegistry(cls)?.[methodName]) ?? null;\n}\n\nfunction findInterfaceVfuncDescriptor(interfaceGtype: bigint, methodName: string): VfuncDescriptor | undefined {\n return getInterfaceVfuncRegistry(interfaceGtype)?.[methodName];\n}\n\nfunction vfuncArg(descriptor: Descriptor): Arg {\n if (descriptor.kind === \"ref\") {\n return { type: descriptor.innerDescriptor, direction: descriptor.inout === true ? \"inout\" : \"out\" };\n }\n\n if (isCallerAllocatedOut(descriptor)) {\n return { type: descriptor, direction: \"out\", isCallerAllocated: true };\n }\n\n return { type: descriptor };\n}\n\nfunction vfuncArgs(descriptor: VfuncDescriptor): Arg[] {\n return descriptor.argDescriptors.map((argDescriptor) => vfuncArg(argDescriptor));\n}\n\nexport { findClassVfuncDescriptor, findInterfaceVfuncDescriptor, vfuncArgs };\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gtkx/runtime",
|
|
3
|
-
"version": "1.0.0
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"description": "FFI runtime the generated GTKX bindings call into: type descriptors, GValue marshalling, signals, GObject lifecycle",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"gtkx",
|
|
@@ -45,11 +45,11 @@
|
|
|
45
45
|
"node": ">=24"
|
|
46
46
|
},
|
|
47
47
|
"dependencies": {
|
|
48
|
-
"@gtkx/
|
|
49
|
-
"@gtkx/
|
|
48
|
+
"@gtkx/native": "1.0.0",
|
|
49
|
+
"@gtkx/utils": "1.0.0"
|
|
50
50
|
},
|
|
51
51
|
"devDependencies": {
|
|
52
|
-
"@gtkx/vitest": "1.0.0
|
|
52
|
+
"@gtkx/vitest": "1.0.0"
|
|
53
53
|
},
|
|
54
54
|
"scripts": {
|
|
55
55
|
"release": "tsx ../../scripts/release-package.ts"
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { type AnyClass, getOrInsert } from "@gtkx/utils";
|
|
2
|
+
import { registerClass } from "./register-class.js";
|
|
3
|
+
import { getClassType } from "./registry.js";
|
|
4
|
+
import { typeName } from "./type.js";
|
|
5
|
+
import { callParent } from "./vfunc-call.js";
|
|
6
|
+
|
|
7
|
+
type CommandLineResult = [boolean, string[], number];
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* The GIO application surface {@link createApplication} builds on, which `Gio.Application` and
|
|
11
|
+
* every subclass of it satisfies structurally.
|
|
12
|
+
*/
|
|
13
|
+
type CommandLineApplication = {
|
|
14
|
+
/** Marks the application as quitting, so GLib's own `run` returns without driving a main loop. */
|
|
15
|
+
quit(): void;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
type LocalCommandLineApplication = {
|
|
19
|
+
runLocalCommandLine(argv: string[]): CommandLineResult;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
type ShutdownApplication = {
|
|
23
|
+
run(argv: string[]): number;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
/** An application class together with the construct properties it accepts. */
|
|
27
|
+
type ApplicationClass<T extends CommandLineApplication, P> = AnyClass<T> & (new (props: P) => T);
|
|
28
|
+
|
|
29
|
+
const derivedClasses: WeakMap<AnyClass, AnyClass> = new WeakMap();
|
|
30
|
+
const derivedApplicationClasses: Set<AnyClass> = new Set();
|
|
31
|
+
const quitApplications: WeakSet<object> = new WeakSet();
|
|
32
|
+
const shuttingDownApplications: WeakSet<object> = new WeakSet();
|
|
33
|
+
|
|
34
|
+
const derivedTypeName = (base: AnyClass): string => `Gtkx${typeName(getClassType(base)) ?? base.name}`;
|
|
35
|
+
|
|
36
|
+
const buildApplicationClass = (base: AnyClass<CommandLineApplication>): AnyClass<CommandLineApplication> => {
|
|
37
|
+
class DerivedApplication extends base {
|
|
38
|
+
runLocalCommandLine(argv: string[]): CommandLineResult {
|
|
39
|
+
return this.vfuncLocalCommandLine(argv);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
protected vfuncLocalCommandLine(argv: string[]): CommandLineResult {
|
|
43
|
+
if (shuttingDownApplications.has(this)) {
|
|
44
|
+
this.quit();
|
|
45
|
+
|
|
46
|
+
return [true, argv, 0];
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return callParent(DerivedApplication, "vfuncLocalCommandLine", this, argv) as CommandLineResult;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
protected vfuncNameLost(): boolean {
|
|
53
|
+
quitApplications.add(this);
|
|
54
|
+
|
|
55
|
+
return callParent(DerivedApplication, "vfuncNameLost", this) as boolean;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
override quit(): void {
|
|
59
|
+
quitApplications.add(this);
|
|
60
|
+
super.quit();
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const derived = registerClass(DerivedApplication, { typeName: derivedTypeName(base) });
|
|
65
|
+
derivedApplicationClasses.add(derived);
|
|
66
|
+
|
|
67
|
+
return derived;
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
const isDerivedApplication = (application: object): application is LocalCommandLineApplication => {
|
|
71
|
+
for (const derived of derivedApplicationClasses) {
|
|
72
|
+
if (application instanceof derived) {
|
|
73
|
+
return true;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return false;
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
const shutDownThroughRun = (application: ShutdownApplication): void => {
|
|
81
|
+
if (!isDerivedApplication(application) || quitApplications.has(application)) {
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
shuttingDownApplications.add(application);
|
|
86
|
+
|
|
87
|
+
try {
|
|
88
|
+
application.run([]);
|
|
89
|
+
} finally {
|
|
90
|
+
shuttingDownApplications.delete(application);
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
const deriveApplicationClass = <T extends CommandLineApplication>(base: AnyClass<T>): AnyClass<T> =>
|
|
95
|
+
getOrInsert(derivedClasses, base, () => buildApplicationClass(base)) as AnyClass<T>;
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Constructs an application GTKX can shut down. GLib parses an application's command line at most
|
|
99
|
+
* once per instance and crashes on a second parse, so {@link runApplication} and
|
|
100
|
+
* {@link quitApplication} only accept an application built here.
|
|
101
|
+
*
|
|
102
|
+
* @param base The application class to construct, such as `Gtk.Application`.
|
|
103
|
+
* @param props Construct properties, passed through unchanged.
|
|
104
|
+
* @returns An instance of a class derived from `base`, registered once per base class as its own GType.
|
|
105
|
+
*/
|
|
106
|
+
const createApplication = <T extends CommandLineApplication, P>(base: ApplicationClass<T, P>, props: P): T =>
|
|
107
|
+
new (deriveApplicationClass(base) as new (props: P) => T)(props);
|
|
108
|
+
|
|
109
|
+
export {
|
|
110
|
+
createApplication,
|
|
111
|
+
isDerivedApplication,
|
|
112
|
+
shutDownThroughRun,
|
|
113
|
+
type ApplicationClass,
|
|
114
|
+
type CommandLineApplication,
|
|
115
|
+
type LocalCommandLineApplication,
|
|
116
|
+
};
|
package/src/arg.ts
CHANGED
|
@@ -1,15 +1,21 @@
|
|
|
1
1
|
import type { Descriptor } from "@gtkx/native";
|
|
2
2
|
|
|
3
|
+
/** How one argument of a native call or signal emission is marshalled and which way it flows. */
|
|
3
4
|
type Arg = {
|
|
5
|
+
/** Descriptor the value is marshalled with, wrapped in a pointer to it for an output argument. */
|
|
4
6
|
type: Descriptor;
|
|
7
|
+
/** Direction of an output argument, omitted for a plain input argument. */
|
|
5
8
|
direction?: "out" | "inout";
|
|
6
|
-
|
|
7
|
-
|
|
9
|
+
/** When true, the caller supplies the storage the callee writes into, rather than a pointer to fill. */
|
|
10
|
+
isCallerAllocated?: boolean;
|
|
11
|
+
/** When true, the argument is consumed internally and left out of the packed result. */
|
|
12
|
+
isConsumed?: boolean;
|
|
8
13
|
};
|
|
9
14
|
|
|
10
15
|
const isOutputArg = (arg: Arg): boolean => arg.direction !== undefined;
|
|
11
16
|
const isInoutArg = (arg: Arg): boolean => arg.direction === "inout";
|
|
12
|
-
const isCallerAllocatedArg = (arg: Arg): boolean => arg.
|
|
13
|
-
const isRefArg = (arg: Arg): boolean => arg.direction !== undefined && arg.
|
|
17
|
+
const isCallerAllocatedArg = (arg: Arg): boolean => arg.isCallerAllocated === true;
|
|
18
|
+
const isRefArg = (arg: Arg): boolean => arg.direction !== undefined && arg.isCallerAllocated !== true;
|
|
19
|
+
const requiresInputArg = (arg: Arg): boolean => !isRefArg(arg) || isInoutArg(arg);
|
|
14
20
|
|
|
15
|
-
export { isOutputArg, isInoutArg, isCallerAllocatedArg, isRefArg, type Arg };
|
|
21
|
+
export { isOutputArg, isInoutArg, isCallerAllocatedArg, isRefArg, requiresInputArg, type Arg };
|
package/src/bind.ts
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
import { call, type Descriptor, bind as nativeBind } from "@gtkx/native";
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Precompiles a call to a C function, marshalling the values it is given through the argument
|
|
5
|
+
* descriptors and the result through the return one. The symbol is resolved in the shared library
|
|
6
|
+
* on the first call.
|
|
7
|
+
*
|
|
8
|
+
* @param sharedLibrary The shared library to resolve the symbol in.
|
|
9
|
+
* @param symbol The name of the C function to bind.
|
|
10
|
+
* @param argDescriptors Describe the C arguments, in declaration order.
|
|
11
|
+
* @param returnDescriptor Describes the C return value.
|
|
12
|
+
* @returns A function calling the bound symbol.
|
|
13
|
+
*/
|
|
3
14
|
function bind(
|
|
4
15
|
sharedLibrary: string,
|
|
5
16
|
symbol: string,
|