@gtkx/runtime 1.0.0-rc.4 → 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 +5 -5
- 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 +2 -1
- package/dist/arg.d.ts.map +1 -1
- package/dist/arg.js +2 -1
- package/dist/arg.js.map +1 -1
- package/dist/bind.d.ts +11 -0
- package/dist/bind.d.ts.map +1 -1
- package/dist/bind.js +11 -0
- package/dist/bind.js.map +1 -1
- package/dist/callback.d.ts +2 -1
- package/dist/callback.d.ts.map +1 -1
- package/dist/callback.js +118 -20
- 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 +74 -3
- package/dist/descriptors.d.ts.map +1 -1
- package/dist/descriptors.js +44 -0
- package/dist/descriptors.js.map +1 -1
- package/dist/error.d.ts +1 -0
- package/dist/error.d.ts.map +1 -1
- package/dist/error.js.map +1 -1
- package/dist/fn.d.ts +19 -2
- package/dist/fn.d.ts.map +1 -1
- package/dist/fn.js +64 -27
- 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 +12 -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 +43 -11
- package/dist/lifecycle.d.ts.map +1 -1
- package/dist/lifecycle.js +62 -19
- package/dist/lifecycle.js.map +1 -1
- 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 +3 -2
- package/dist/mixin.d.ts.map +1 -1
- package/dist/mixin.js +13 -1
- package/dist/mixin.js.map +1 -1
- package/dist/native-value.d.ts.map +1 -1
- package/dist/native-value.js +4 -3
- package/dist/native-value.js.map +1 -1
- package/dist/object.d.ts +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.map +1 -1
- package/dist/promisify.js +3 -2
- package/dist/promisify.js.map +1 -1
- package/dist/properties.d.ts +38 -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 +117 -12
- package/dist/register-class.d.ts.map +1 -1
- package/dist/register-class.js +108 -76
- package/dist/register-class.js.map +1 -1
- package/dist/registry.d.ts +69 -19
- package/dist/registry.d.ts.map +1 -1
- package/dist/registry.js +70 -26
- package/dist/registry.js.map +1 -1
- package/dist/signal.d.ts +9 -2
- package/dist/signal.d.ts.map +1 -1
- package/dist/signal.js +63 -7
- package/dist/signal.js.map +1 -1
- package/dist/type.d.ts +2 -1
- package/dist/type.d.ts.map +1 -1
- package/dist/type.js +2 -0
- package/dist/type.js.map +1 -1
- package/dist/value.d.ts +8 -1
- package/dist/value.d.ts.map +1 -1
- package/dist/value.js +42 -13
- 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 +2 -1
- package/src/bind.ts +11 -0
- package/src/callback.ts +162 -22
- package/src/closure.ts +136 -0
- package/src/descriptors.ts +85 -24
- package/src/error.ts +1 -0
- package/src/fn.ts +92 -31
- package/src/index.ts +21 -6
- package/src/internal.ts +1 -0
- package/src/library.ts +2 -1
- package/src/lifecycle.ts +90 -23
- package/src/listeners.ts +3 -1
- package/src/mixin.ts +15 -2
- package/src/native-value.ts +5 -3
- package/src/object.ts +128 -34
- package/src/param-spec.ts +235 -0
- package/src/promisify.ts +4 -2
- package/src/properties.ts +458 -45
- package/src/register-class.ts +326 -109
- package/src/registry.ts +138 -40
- package/src/signal.ts +84 -14
- package/src/type.ts +3 -1
- package/src/value.ts +55 -10
- package/src/vfunc-call.ts +243 -0
- package/src/vfunc-seeds.ts +36 -0
- package/src/vfunc.ts +31 -0
package/src/register-class.ts
CHANGED
|
@@ -1,59 +1,217 @@
|
|
|
1
1
|
import type { Descriptor } from "@gtkx/native";
|
|
2
2
|
import {
|
|
3
3
|
registerClass as nativeRegisterClass,
|
|
4
|
+
type RegisterClassInterface as NativeRegisterClassInterface,
|
|
4
5
|
type RegisterClassOptions as NativeRegisterClassOptions,
|
|
5
6
|
type RegisterClassVfunc as NativeRegisterClassVfunc,
|
|
6
7
|
} from "@gtkx/native";
|
|
7
8
|
import { type AnyClass, getParentClass, walkClassChain } from "@gtkx/utils";
|
|
8
9
|
import { wrapCallback } from "./callback.js";
|
|
10
|
+
import { insertMixinLayer } from "./mixin.js";
|
|
9
11
|
import {
|
|
10
|
-
|
|
12
|
+
buildPropertyDispatch,
|
|
13
|
+
GET_PROPERTY_VFUNC,
|
|
11
14
|
makeGetProperty,
|
|
12
15
|
makeSetProperty,
|
|
13
|
-
type
|
|
16
|
+
type PropertyDispatch,
|
|
14
17
|
type PropertySpec,
|
|
18
|
+
SET_PROPERTY_VFUNC,
|
|
15
19
|
toNativeProperties,
|
|
16
20
|
} from "./properties.js";
|
|
17
21
|
import {
|
|
18
22
|
getClassType,
|
|
19
|
-
|
|
20
|
-
|
|
23
|
+
getInterfaceMixin,
|
|
24
|
+
markDerivedClass,
|
|
21
25
|
registerClassType,
|
|
22
26
|
type VfuncDescriptor,
|
|
23
27
|
} from "./registry.js";
|
|
24
|
-
import { TYPE_INVALID, typeInterfaces } from "./type.js";
|
|
28
|
+
import { TYPE_INTERFACE, TYPE_INVALID, typeFundamental, typeInterfaces, typeIsA } from "./type.js";
|
|
29
|
+
import { findClassVfuncDescriptor, findInterfaceVfuncDescriptor } from "./vfunc.js";
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* A generated interface value, such as `Gio.ListModel`, in the form {@link registerClass} takes it.
|
|
33
|
+
* `__impl__` exists only in the type system: it carries the interface's `Impl` type, of which a class
|
|
34
|
+
* has to match every member it declares itself, which rejects a class value and a member whose
|
|
35
|
+
* signature does not fit the slot it fills. Leaving a slot to the interface is not rejected,
|
|
36
|
+
* because `registerClass` does not reject it either: every member of an `Impl` type is optional,
|
|
37
|
+
* and the `Partial` holds that open whatever the type declares. The `object` beside it keeps
|
|
38
|
+
* TypeScript from also demanding one member in common. An interface that introspection describes
|
|
39
|
+
* no vtable for carries `unknown`, since it has no slot a class could fill.
|
|
40
|
+
*/
|
|
41
|
+
type Interface<TImpl> = AnyClass & {
|
|
42
|
+
/** Type-level slot holding the interface's `Impl` type; no value ever carries it. */
|
|
43
|
+
__impl__: (impl: Partial<TImpl> & object) => void;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* One key of `RegisterClassOptions.properties` with every underscore turned into the dash a canonical
|
|
48
|
+
* GObject property name separates its words with.
|
|
49
|
+
*/
|
|
50
|
+
type Dashed<TName extends string> = TName extends `${infer THead}_${infer TTail}`
|
|
51
|
+
? Dashed<`${THead}-${TTail}`>
|
|
52
|
+
: TName;
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* One dashed key in the camelCase spelling the accessors {@link registerClass} installs carry, which is
|
|
56
|
+
* the spelling the hooks that address a property by name take.
|
|
57
|
+
*/
|
|
58
|
+
type Camelized<TName extends string> = TName extends `${infer THead}-${infer TTail}`
|
|
59
|
+
? `${THead}${Capitalize<Camelized<TTail>>}`
|
|
60
|
+
: TName;
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* The names a registered class carries in its property map: every key of
|
|
64
|
+
* `RegisterClassOptions.properties` in camelCase, whichever of the three spellings it was written in.
|
|
65
|
+
* A `properties` object given a type of its own rather than left to inference has `string` for its key
|
|
66
|
+
* type and names nothing, because a name only known as `string` addresses no member in particular.
|
|
67
|
+
*/
|
|
68
|
+
type InstalledNames<TProperties> = string extends keyof TProperties
|
|
69
|
+
? never
|
|
70
|
+
: Camelized<Dashed<keyof TProperties & string>>;
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* An instance of a registered class: everything the class itself declares, plus the property map the
|
|
74
|
+
* hooks that address a property by name, such as `useProperty` from `@gtkx/react`, read the installed
|
|
75
|
+
* names off. Each name is typed with the value type the class declares for the member of that name,
|
|
76
|
+
* so a property the class does not `declare` contributes nothing.
|
|
77
|
+
*/
|
|
78
|
+
type RegisteredInstance<TInstance, TProperties> = TInstance & {
|
|
79
|
+
/** Type-level map from installed property name to value type; no value ever carries it. */
|
|
80
|
+
__properties__: Pick<TInstance, InstalledNames<TProperties> & keyof TInstance>;
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* The construct signature and prototype a registered class carries, both giving
|
|
85
|
+
* {@link RegisteredInstance}. The signature stays abstract for as long as the class itself is, so
|
|
86
|
+
* registering an abstract base leaves it as impossible to construct as it was.
|
|
87
|
+
*/
|
|
88
|
+
type RegisteredConstructor<TClass, TArgs extends unknown[], TInstance> = {
|
|
89
|
+
/** Object the class's instances inherit from. */
|
|
90
|
+
prototype: TInstance;
|
|
91
|
+
} & (TClass extends new (...args: never) => unknown
|
|
92
|
+
? new (...args: TArgs) => TInstance
|
|
93
|
+
: abstract new (...args: TArgs) => TInstance);
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* The class {@link registerClass} hands back: the same class, with the same statics, whose instances
|
|
97
|
+
* carry the properties `RegisterClassOptions.properties` installed. Binding the call to a name, rather
|
|
98
|
+
* than discarding it, is what carries those names into the type system.
|
|
99
|
+
*/
|
|
100
|
+
type RegisteredClass<TClass extends AnyClass, TProperties> =
|
|
101
|
+
TClass extends abstract new (...args: infer TArgs) => infer TInstance
|
|
102
|
+
? Omit<TClass, "prototype"> & RegisteredConstructor<TClass, TArgs, RegisteredInstance<TInstance, TProperties>>
|
|
103
|
+
: never;
|
|
25
104
|
|
|
26
105
|
/** What {@link registerClass} adds to the new GType beyond the vtable slots it discovers on the class. */
|
|
27
|
-
type RegisterClassOptions = {
|
|
106
|
+
type RegisterClassOptions<TInstance extends object, TProperties extends Record<string, PropertySpec>> = {
|
|
28
107
|
/** Name to register the new GType under, defaulting to the class's own name. */
|
|
29
108
|
typeName?: string;
|
|
30
109
|
/**
|
|
31
|
-
*
|
|
32
|
-
* `
|
|
33
|
-
* prototype
|
|
110
|
+
* Interfaces the new type implements on top of the ones it inherits, given as the interface values
|
|
111
|
+
* themselves, such as `Gio.ListModel`. Their vtable slots are filled from the `vfunc`-prefixed methods on
|
|
112
|
+
* the class's prototype chain, each of which has to match the interface's `Impl` type, such as
|
|
113
|
+
* `Gio.ListModelImpl`.
|
|
34
114
|
*/
|
|
35
|
-
|
|
115
|
+
implements?: Interface<TInstance>[];
|
|
116
|
+
/**
|
|
117
|
+
* Properties to install on the new type, keyed by the name JavaScript addresses each one by and
|
|
118
|
+
* valued with the `GObject.ParamSpec` describing it. A key is read in camelCase however it is
|
|
119
|
+
* written, so `dewPoint`, `dew_point` and `dew-point` all name the same member, and the ParamSpec
|
|
120
|
+
* has to carry the canonical spelling of that name, `dew-point`, or registration throws: the
|
|
121
|
+
* ParamSpec's name is the one GObject emits `notify` with, and a name the key does not spell
|
|
122
|
+
* reaches nothing that listens for it. Every property gains prototype accessors, one for the key
|
|
123
|
+
* as written, one for it with dashes turned into underscores and one for it in camelCase, each
|
|
124
|
+
* unless the class already defines that name. They serve the value from storage of their own on
|
|
125
|
+
* the instance, which is also what the type's `get_property` and `set_property` slots read and
|
|
126
|
+
* write, so a value set from JavaScript, from `g_object_set_property` and at construction all
|
|
127
|
+
* land in the same place.
|
|
128
|
+
*
|
|
129
|
+
* A write the ParamSpec would refuse throws rather than reaching GObject, which reports such a
|
|
130
|
+
* write as a GLib critical and drops it: a `TypeError` for a read-only or construct-only
|
|
131
|
+
* property and for a value of a type the property cannot hold, and a `RangeError` for a value
|
|
132
|
+
* the ParamSpec rejects. The same two checks run over a value handed to the constructor, where
|
|
133
|
+
* a construct-only property is the one that is writable. An accepted write emits one `notify`,
|
|
134
|
+
* which a `freeze_notify` batch collects; a write of the value the property already holds is
|
|
135
|
+
* dropped and emits none.
|
|
136
|
+
*
|
|
137
|
+
* A class that defines the camelCase member itself owns the property: its own accessor decides
|
|
138
|
+
* what a write means, the other two spellings forward to it, and the type's property slots read
|
|
139
|
+
* and write it rather than the generated storage.
|
|
140
|
+
*/
|
|
141
|
+
properties?: TProperties;
|
|
36
142
|
};
|
|
37
143
|
|
|
144
|
+
/** {@link RegisterClassOptions} with the widest instance and property types {@link registerClass} accepts. */
|
|
145
|
+
type AnyRegisterClassOptions = RegisterClassOptions<object, Record<string, PropertySpec>>;
|
|
38
146
|
type VfuncFn = NativeRegisterClassVfunc["fn"];
|
|
39
|
-
type DiscoveredVfunc
|
|
40
|
-
type
|
|
41
|
-
type
|
|
42
|
-
|
|
147
|
+
type DiscoveredVfunc = VfuncDescriptor & { methodName: string; fn: VfuncFn };
|
|
148
|
+
type MethodTable = Map<string, VfuncFn>;
|
|
149
|
+
type InstanceMembers = { methods: MethodTable; names: Set<string>; inheritedNames: Set<string> };
|
|
150
|
+
|
|
151
|
+
type InterfaceVfuncBinding = {
|
|
152
|
+
gtype: bigint;
|
|
153
|
+
vfuncs: DiscoveredVfunc[];
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
type PropertyVfuncSource = {
|
|
157
|
+
klass: AnyClass;
|
|
158
|
+
methods: MethodTable;
|
|
159
|
+
dispatch: PropertyDispatch;
|
|
160
|
+
adoptedTypes: bigint[];
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
type PropertyVfuncSpec = {
|
|
164
|
+
methodName: string;
|
|
165
|
+
isValueOut: boolean;
|
|
166
|
+
makeDispatch: (dispatch: PropertyDispatch) => VfuncFn;
|
|
167
|
+
};
|
|
43
168
|
|
|
44
|
-
const UNSUPPORTED_CONSTRUCT_VFUNCS: Set<string> = new Set(["constructed", "setProperty", "getProperty"]);
|
|
45
169
|
const VALUE_ARG_INDEX = 2;
|
|
46
170
|
|
|
171
|
+
const PROPERTY_VFUNC_SPECS: PropertyVfuncSpec[] = [
|
|
172
|
+
{ methodName: GET_PROPERTY_VFUNC, isValueOut: true, makeDispatch: makeGetProperty },
|
|
173
|
+
{ methodName: SET_PROPERTY_VFUNC, isValueOut: false, makeDispatch: makeSetProperty },
|
|
174
|
+
];
|
|
175
|
+
|
|
176
|
+
const PROPERTY_VFUNC_NAMES: Set<string> = new Set(PROPERTY_VFUNC_SPECS.map((spec) => spec.methodName));
|
|
177
|
+
|
|
47
178
|
/**
|
|
48
|
-
* Registers a subclass of a wrapper class as a new GType, wiring up any class and
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
179
|
+
* Registers a subclass of a wrapper class as a new GType, wiring up any class and interface
|
|
180
|
+
* virtual functions it overrides, both for the interfaces it inherits and for the ones
|
|
181
|
+
* `RegisterClassOptions.implements` names.
|
|
182
|
+
*
|
|
183
|
+
* Throws when the class does not extend a registered wrapper class, when it has no derivable type
|
|
184
|
+
* name, when an entry in `RegisterClassOptions.implements` is not a registered interface, when a
|
|
185
|
+
* listed interface has a prerequisite that neither the parent type nor another listed interface meets,
|
|
186
|
+
* and when an entry in `RegisterClassOptions.properties` names its `GObject.ParamSpec` something other
|
|
187
|
+
* than the canonical spelling of the key it sits under.
|
|
188
|
+
*
|
|
189
|
+
* A slot is filled from the `vfunc`-prefixed methods on the class's prototype chain, up to but not
|
|
190
|
+
* including the registered ancestor the class extends, so a method an intermediate base class
|
|
191
|
+
* declares fills a slot the same way one the class itself declares does. A slot nothing on that
|
|
192
|
+
* chain fills is left untouched.
|
|
193
|
+
*
|
|
194
|
+
* Declare every slot as a method: a class field holding a function, such as `vfuncGetNItems = () => 1`,
|
|
195
|
+
* is assigned to each instance after registration and never reaches the vtable.
|
|
196
|
+
*
|
|
197
|
+
* An override of `vfuncConstructed` runs from inside the base constructor, before JavaScript
|
|
198
|
+
* installs the subclass's field initializers and runs its constructor body, so a field still
|
|
199
|
+
* reads `undefined` there and reading a `#private` field throws. Declare state the override
|
|
200
|
+
* touches without an initializer, and assign private state from the constructor body after
|
|
201
|
+
* `super()`. An instance a native caller creates, through `GObject.newv` or `Gtk.Builder`,
|
|
202
|
+
* never runs the subclass constructor at all, so its declared fields stay uninitialized for
|
|
203
|
+
* the object's whole life.
|
|
52
204
|
*
|
|
53
205
|
* @param klass The subclass to register.
|
|
54
|
-
* @
|
|
206
|
+
* @param options What the new GType gains beyond the vtable slots the class overrides.
|
|
207
|
+
* @returns The same class, now registered, with every name in `options.properties` in its property map.
|
|
55
208
|
*/
|
|
56
|
-
function registerClass<
|
|
209
|
+
function registerClass<
|
|
210
|
+
T extends AnyClass,
|
|
211
|
+
TProperties extends Record<string, PropertySpec> = Record<never, PropertySpec>,
|
|
212
|
+
>(klass: T, options?: RegisterClassOptions<T["prototype"], TProperties>): RegisteredClass<T, TProperties>;
|
|
213
|
+
|
|
214
|
+
function registerClass(klass: AnyClass, options: AnyRegisterClassOptions = {}): AnyClass {
|
|
57
215
|
const parentType = resolveParentType(klass);
|
|
58
216
|
|
|
59
217
|
if (parentType === TYPE_INVALID) {
|
|
@@ -66,18 +224,56 @@ function registerClass<T extends AnyClass>(klass: T, options: RegisterClassOptio
|
|
|
66
224
|
throw new Error("registerClass: cannot derive a GType name (anonymous class with no typeName option)");
|
|
67
225
|
}
|
|
68
226
|
|
|
227
|
+
const declaredTypes = resolveInterfaceTypes(klass, options.implements ?? []);
|
|
228
|
+
const adoptedTypes = declaredTypes.filter((gtype) => !typeIsA(parentType, gtype));
|
|
69
229
|
const properties = options.properties ?? {};
|
|
70
|
-
const
|
|
71
|
-
const
|
|
230
|
+
const { methods, inheritedNames } = collectInstanceMembers(klass);
|
|
231
|
+
const dispatch = buildPropertyDispatch({ klass, properties, adoptedTypes });
|
|
232
|
+
|
|
233
|
+
const classVfuncs = [
|
|
234
|
+
...discoverClassVfuncs(klass, methods),
|
|
235
|
+
...propertyVfuncs({ klass, methods, dispatch, adoptedTypes }),
|
|
236
|
+
];
|
|
237
|
+
|
|
72
238
|
const claimedMethodNames = new Set(classVfuncs.map((vfunc) => vfunc.methodName));
|
|
73
|
-
const interfaceBindings =
|
|
239
|
+
const interfaceBindings = discoverInterfaceBindings(methods, parentType, declaredTypes, claimedMethodNames);
|
|
74
240
|
const nativeOptions = toNativeOptions(classVfuncs, interfaceBindings, properties);
|
|
75
241
|
const newType: bigint = nativeRegisterClass(name, parentType, nativeOptions);
|
|
76
242
|
registerClassType(klass, newType);
|
|
243
|
+
markDerivedClass(klass);
|
|
244
|
+
applyInterfaceMixins(klass, adoptedTypes, inheritedNames);
|
|
77
245
|
|
|
78
246
|
return klass;
|
|
79
247
|
}
|
|
80
248
|
|
|
249
|
+
function resolveInterfaceType(klass: AnyClass, entry: AnyClass): bigint {
|
|
250
|
+
const gtype = getClassType(entry);
|
|
251
|
+
|
|
252
|
+
if (typeFundamental(gtype) !== TYPE_INTERFACE) {
|
|
253
|
+
const label = typeof entry === "function" ? entry.name : String(entry);
|
|
254
|
+
|
|
255
|
+
throw new TypeError(
|
|
256
|
+
`registerClass: ${klass.name} lists '${label}' in implements, which is not a registered interface`,
|
|
257
|
+
);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
return gtype;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
function resolveInterfaceTypes(klass: AnyClass, entries: AnyClass[]): bigint[] {
|
|
264
|
+
return [...new Set(entries.map((entry) => resolveInterfaceType(klass, entry)))];
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
function applyInterfaceMixins(klass: AnyClass, adoptedTypes: bigint[], inheritedNames: Set<string>): void {
|
|
268
|
+
for (const gtype of adoptedTypes) {
|
|
269
|
+
const mixin = getInterfaceMixin(gtype);
|
|
270
|
+
|
|
271
|
+
if (mixin !== undefined) {
|
|
272
|
+
insertMixinLayer(klass, mixin, inheritedNames);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
81
277
|
function resolveParentType(klass: AnyClass): bigint {
|
|
82
278
|
return (
|
|
83
279
|
walkClassChain(getParentClass(klass), (cls) => {
|
|
@@ -88,28 +284,57 @@ function resolveParentType(klass: AnyClass): bigint {
|
|
|
88
284
|
);
|
|
89
285
|
}
|
|
90
286
|
|
|
91
|
-
function
|
|
287
|
+
function addOwnMember(members: InstanceMembers, proto: object, name: string, isInherited: boolean): void {
|
|
288
|
+
if (name === "constructor") {
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
if (isInherited) {
|
|
293
|
+
members.inheritedNames.add(name);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
if (members.names.has(name)) {
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
members.names.add(name);
|
|
301
|
+
const descriptor = Object.getOwnPropertyDescriptor(proto, name);
|
|
302
|
+
|
|
303
|
+
if (typeof descriptor?.value === "function") {
|
|
304
|
+
members.methods.set(name, descriptor.value as VfuncFn);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
function addOwnMembers(members: InstanceMembers, klass: AnyClass, isInherited: boolean): void {
|
|
92
309
|
const proto = (klass as { prototype?: object }).prototype;
|
|
93
310
|
|
|
94
|
-
if (
|
|
95
|
-
return
|
|
311
|
+
if (proto === undefined) {
|
|
312
|
+
return;
|
|
96
313
|
}
|
|
97
314
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
315
|
+
for (const name of Object.getOwnPropertyNames(proto)) {
|
|
316
|
+
addOwnMember(members, proto, name, isInherited);
|
|
317
|
+
}
|
|
318
|
+
}
|
|
102
319
|
|
|
103
|
-
|
|
104
|
-
}
|
|
320
|
+
function collectInstanceMembers(klass: AnyClass): InstanceMembers {
|
|
321
|
+
const members: InstanceMembers = { methods: new Map(), names: new Set(), inheritedNames: new Set() };
|
|
322
|
+
let cls: AnyClass | null = klass;
|
|
323
|
+
|
|
324
|
+
while (cls !== null && getClassType(cls) === TYPE_INVALID) {
|
|
325
|
+
addOwnMembers(members, cls, cls !== klass);
|
|
326
|
+
cls = getParentClass(cls);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
return members;
|
|
105
330
|
}
|
|
106
331
|
|
|
107
|
-
function buildDiscoveredVfunc
|
|
108
|
-
|
|
332
|
+
function buildDiscoveredVfunc(
|
|
333
|
+
fn: VfuncFn,
|
|
109
334
|
methodName: string,
|
|
110
|
-
resolveDescriptor: (methodName: string) => VfuncDescriptor
|
|
335
|
+
resolveDescriptor: (methodName: string) => VfuncDescriptor | undefined,
|
|
111
336
|
skip: Set<string> | undefined,
|
|
112
|
-
): DiscoveredVfunc
|
|
337
|
+
): DiscoveredVfunc | undefined {
|
|
113
338
|
if (skip?.has(methodName)) {
|
|
114
339
|
return undefined;
|
|
115
340
|
}
|
|
@@ -120,12 +345,6 @@ function buildDiscoveredVfunc<K extends "class" | "interface">(
|
|
|
120
345
|
return undefined;
|
|
121
346
|
}
|
|
122
347
|
|
|
123
|
-
const fn = proto[methodName];
|
|
124
|
-
|
|
125
|
-
if (!fn) {
|
|
126
|
-
return undefined;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
348
|
return {
|
|
130
349
|
...descriptor,
|
|
131
350
|
methodName,
|
|
@@ -133,16 +352,15 @@ function buildDiscoveredVfunc<K extends "class" | "interface">(
|
|
|
133
352
|
};
|
|
134
353
|
}
|
|
135
354
|
|
|
136
|
-
function collectDiscoveredVfuncs
|
|
137
|
-
|
|
138
|
-
resolveDescriptor: (methodName: string) => VfuncDescriptor
|
|
355
|
+
function collectDiscoveredVfuncs(
|
|
356
|
+
methods: MethodTable,
|
|
357
|
+
resolveDescriptor: (methodName: string) => VfuncDescriptor | undefined,
|
|
139
358
|
skip?: Set<string>,
|
|
140
|
-
): DiscoveredVfunc
|
|
141
|
-
const
|
|
142
|
-
const result: DiscoveredVfunc<K>[] = [];
|
|
359
|
+
): DiscoveredVfunc[] {
|
|
360
|
+
const result: DiscoveredVfunc[] = [];
|
|
143
361
|
|
|
144
|
-
for (const methodName of
|
|
145
|
-
const discovered = buildDiscoveredVfunc(
|
|
362
|
+
for (const [methodName, fn] of methods) {
|
|
363
|
+
const discovered = buildDiscoveredVfunc(fn, methodName, resolveDescriptor, skip);
|
|
146
364
|
|
|
147
365
|
if (discovered) {
|
|
148
366
|
result.push(discovered);
|
|
@@ -152,20 +370,12 @@ function collectDiscoveredVfuncs<K extends "class" | "interface">(
|
|
|
152
370
|
return result;
|
|
153
371
|
}
|
|
154
372
|
|
|
155
|
-
function discoverClassVfuncs(klass: AnyClass):
|
|
156
|
-
return collectDiscoveredVfuncs(
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
`registerClass: overriding the GObject construct-time vtable slot '${methodName}' is not ` +
|
|
162
|
-
"supported; run construct-time initialization in the subclass constructor, after " +
|
|
163
|
-
"super(...), instead",
|
|
164
|
-
);
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
return descriptor ?? undefined;
|
|
168
|
-
});
|
|
373
|
+
function discoverClassVfuncs(klass: AnyClass, methods: MethodTable): DiscoveredVfunc[] {
|
|
374
|
+
return collectDiscoveredVfuncs(
|
|
375
|
+
methods,
|
|
376
|
+
(methodName) => findClassVfuncDescriptor(klass, methodName) ?? undefined,
|
|
377
|
+
PROPERTY_VFUNC_NAMES,
|
|
378
|
+
);
|
|
169
379
|
}
|
|
170
380
|
|
|
171
381
|
function wrapVfunc(
|
|
@@ -176,18 +386,21 @@ function wrapVfunc(
|
|
|
176
386
|
return wrapCallback(fn as (...args: unknown[]) => unknown, { argDescriptors, returnDescriptor }, "this");
|
|
177
387
|
}
|
|
178
388
|
|
|
179
|
-
function
|
|
180
|
-
|
|
389
|
+
function discoverInterfaceBindings(
|
|
390
|
+
methods: MethodTable,
|
|
181
391
|
parentGtype: bigint,
|
|
392
|
+
declaredTypes: bigint[],
|
|
182
393
|
claimedMethodNames: Set<string>,
|
|
183
394
|
): InterfaceVfuncBinding[] {
|
|
395
|
+
const declared: Set<bigint> = new Set(declaredTypes);
|
|
396
|
+
const gtypes: Set<bigint> = new Set([...typeInterfaces(parentGtype), ...declaredTypes]);
|
|
184
397
|
const bindings: InterfaceVfuncBinding[] = [];
|
|
185
398
|
|
|
186
|
-
for (const
|
|
187
|
-
const
|
|
399
|
+
for (const gtype of gtypes) {
|
|
400
|
+
const vfuncs = discoverInterfaceVfuncs(methods, gtype, claimedMethodNames);
|
|
188
401
|
|
|
189
|
-
if (
|
|
190
|
-
bindings.push({ gtype
|
|
402
|
+
if (vfuncs.length > 0 || declared.has(gtype)) {
|
|
403
|
+
bindings.push({ gtype, vfuncs });
|
|
191
404
|
}
|
|
192
405
|
}
|
|
193
406
|
|
|
@@ -195,46 +408,39 @@ function discoverInheritedInterfaceVfuncs(
|
|
|
195
408
|
}
|
|
196
409
|
|
|
197
410
|
function discoverInterfaceVfuncs(
|
|
198
|
-
|
|
411
|
+
methods: MethodTable,
|
|
199
412
|
interfaceGtype: bigint,
|
|
200
413
|
claimedMethodNames: Set<string>,
|
|
201
|
-
):
|
|
202
|
-
const vfuncRegistry = getInterfaceVfuncRegistry(interfaceGtype);
|
|
203
|
-
|
|
204
|
-
if (!vfuncRegistry) {
|
|
205
|
-
return [];
|
|
206
|
-
}
|
|
207
|
-
|
|
414
|
+
): DiscoveredVfunc[] {
|
|
208
415
|
return collectDiscoveredVfuncs(
|
|
209
|
-
|
|
210
|
-
(methodName) =>
|
|
211
|
-
const entry = vfuncRegistry[methodName];
|
|
212
|
-
|
|
213
|
-
return entry?.kind === "interface" ? entry : undefined;
|
|
214
|
-
},
|
|
416
|
+
methods,
|
|
417
|
+
(methodName) => findInterfaceVfuncDescriptor(interfaceGtype, methodName),
|
|
215
418
|
claimedMethodNames,
|
|
216
419
|
);
|
|
217
420
|
}
|
|
218
421
|
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
walkClassChain(getParentClass(klass), (cls) => {
|
|
222
|
-
const entry = getVfuncRegistry(cls)?.[methodName];
|
|
422
|
+
const hasDispatchedProperties = (dispatch: PropertyDispatch, adoptedTypes: bigint[]): boolean =>
|
|
423
|
+
dispatch.accessors.length > 0 || adoptedTypes.length > 0;
|
|
223
424
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
425
|
+
function propertyVfuncFor(
|
|
426
|
+
source: PropertyVfuncSource,
|
|
427
|
+
spec: PropertyVfuncSpec,
|
|
428
|
+
): DiscoveredVfunc | undefined {
|
|
429
|
+
const { klass, methods, dispatch, adoptedTypes } = source;
|
|
228
430
|
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
}
|
|
431
|
+
const dispatched = hasDispatchedProperties(dispatch, adoptedTypes)
|
|
432
|
+
? spec.makeDispatch(dispatch)
|
|
433
|
+
: undefined;
|
|
233
434
|
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
435
|
+
const fn = methods.get(spec.methodName) ?? dispatched;
|
|
436
|
+
|
|
437
|
+
return fn === undefined ? undefined : buildPropertyVfunc(klass, spec.methodName, fn, spec.isValueOut);
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
function propertyVfuncs(source: PropertyVfuncSource): DiscoveredVfunc[] {
|
|
441
|
+
return PROPERTY_VFUNC_SPECS.map((spec) => propertyVfuncFor(source, spec)).filter(
|
|
442
|
+
(vfunc) => vfunc !== undefined,
|
|
443
|
+
);
|
|
238
444
|
}
|
|
239
445
|
|
|
240
446
|
function markValueCallerAllocated(argDescriptors: Descriptor[]): Descriptor[] {
|
|
@@ -247,7 +453,7 @@ function buildPropertyVfunc(
|
|
|
247
453
|
methodName: string,
|
|
248
454
|
fn: VfuncFn,
|
|
249
455
|
isValueOut: boolean,
|
|
250
|
-
):
|
|
456
|
+
): DiscoveredVfunc {
|
|
251
457
|
const descriptor = findClassVfuncDescriptor(klass, methodName);
|
|
252
458
|
|
|
253
459
|
if (!descriptor) {
|
|
@@ -266,8 +472,23 @@ function buildPropertyVfunc(
|
|
|
266
472
|
};
|
|
267
473
|
}
|
|
268
474
|
|
|
475
|
+
function toNativeInterface(binding: InterfaceVfuncBinding): NativeRegisterClassInterface {
|
|
476
|
+
const nativeInterface: NativeRegisterClassInterface = {
|
|
477
|
+
type: binding.gtype,
|
|
478
|
+
vfuncs: [...binding.vfuncs],
|
|
479
|
+
};
|
|
480
|
+
|
|
481
|
+
const vtableSize = binding.vfuncs[0]?.vtableSize;
|
|
482
|
+
|
|
483
|
+
if (vtableSize !== undefined) {
|
|
484
|
+
nativeInterface.vtableSize = vtableSize;
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
return nativeInterface;
|
|
488
|
+
}
|
|
489
|
+
|
|
269
490
|
function toNativeOptions(
|
|
270
|
-
classVfuncs:
|
|
491
|
+
classVfuncs: DiscoveredVfunc[],
|
|
271
492
|
interfaceBindings: InterfaceVfuncBinding[],
|
|
272
493
|
properties: Record<string, PropertySpec>,
|
|
273
494
|
): NativeRegisterClassOptions | undefined {
|
|
@@ -290,14 +511,10 @@ function toNativeOptions(
|
|
|
290
511
|
}
|
|
291
512
|
|
|
292
513
|
if (hasInterfaces) {
|
|
293
|
-
options.interfaces = interfaceBindings.map((binding) => (
|
|
294
|
-
type: binding.gtype,
|
|
295
|
-
vtableSize: binding.vtableSize,
|
|
296
|
-
vfuncs: [...binding.vfuncs],
|
|
297
|
-
}));
|
|
514
|
+
options.interfaces = interfaceBindings.map((binding) => toNativeInterface(binding));
|
|
298
515
|
}
|
|
299
516
|
|
|
300
517
|
return options;
|
|
301
518
|
}
|
|
302
519
|
|
|
303
|
-
export { registerClass };
|
|
520
|
+
export { type Interface, registerClass };
|