@gtkx/runtime 1.6.0 → 2.0.0-beta.2
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 +1 -1
- package/dist/application-class.d.ts.map +1 -1
- package/dist/application-class.js +1 -2
- package/dist/application-class.js.map +1 -1
- package/dist/arg.d.ts +4 -1
- package/dist/arg.d.ts.map +1 -1
- package/dist/arg.js.map +1 -1
- package/dist/bind.d.ts.map +1 -1
- package/dist/bind.js +1 -9
- package/dist/bind.js.map +1 -1
- package/dist/callback.d.ts +6 -1
- package/dist/callback.d.ts.map +1 -1
- package/dist/callback.js +33 -21
- package/dist/callback.js.map +1 -1
- package/dist/closure.d.ts +3 -1
- package/dist/closure.d.ts.map +1 -1
- package/dist/closure.js +17 -6
- package/dist/closure.js.map +1 -1
- package/dist/descriptors.d.ts +22 -51
- package/dist/descriptors.d.ts.map +1 -1
- package/dist/descriptors.js +53 -27
- package/dist/descriptors.js.map +1 -1
- package/dist/fn.d.ts.map +1 -1
- package/dist/fn.js +23 -9
- package/dist/fn.js.map +1 -1
- package/dist/folded-lengths.d.ts +2 -1
- package/dist/folded-lengths.d.ts.map +1 -1
- package/dist/folded-lengths.js +17 -7
- package/dist/folded-lengths.js.map +1 -1
- package/dist/index.d.ts +2 -24
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -24
- package/dist/index.js.map +1 -1
- package/dist/internal.d.ts +18 -2
- package/dist/internal.d.ts.map +1 -1
- package/dist/internal.js +14 -2
- package/dist/internal.js.map +1 -1
- package/dist/listeners.d.ts +3 -13
- package/dist/listeners.d.ts.map +1 -1
- package/dist/listeners.js +12 -12
- package/dist/listeners.js.map +1 -1
- package/dist/mixin.d.ts +5 -3
- package/dist/mixin.d.ts.map +1 -1
- package/dist/mixin.js +36 -9
- package/dist/mixin.js.map +1 -1
- package/dist/native-value.d.ts +9 -1
- package/dist/native-value.d.ts.map +1 -1
- package/dist/native-value.js +20 -3
- package/dist/native-value.js.map +1 -1
- package/dist/object.d.ts +11 -0
- package/dist/object.d.ts.map +1 -1
- package/dist/object.js +18 -25
- package/dist/object.js.map +1 -1
- package/dist/properties.d.ts +5 -3
- package/dist/properties.d.ts.map +1 -1
- package/dist/properties.js +67 -23
- package/dist/properties.js.map +1 -1
- package/dist/property-brand.d.ts +5 -0
- package/dist/property-brand.d.ts.map +1 -0
- package/dist/property-brand.js +5 -0
- package/dist/property-brand.js.map +1 -0
- package/dist/register-class.d.ts +142 -264
- package/dist/register-class.d.ts.map +1 -1
- package/dist/register-class.js +19 -37
- package/dist/register-class.js.map +1 -1
- package/dist/registry.d.ts +1 -1
- package/dist/registry.d.ts.map +1 -1
- package/dist/registry.js +4 -4
- package/dist/registry.js.map +1 -1
- package/dist/signal-brand.d.ts +12 -0
- package/dist/signal-brand.d.ts.map +1 -0
- package/dist/signal-brand.js +6 -0
- package/dist/signal-brand.js.map +1 -0
- package/dist/signal.d.ts +39 -2
- package/dist/signal.d.ts.map +1 -1
- package/dist/signal.js +84 -27
- package/dist/signal.js.map +1 -1
- package/dist/t.d.ts +5 -21
- package/dist/t.d.ts.map +1 -1
- package/dist/t.js +1 -5
- package/dist/t.js.map +1 -1
- package/dist/type.d.ts.map +1 -1
- package/dist/type.js +8 -2
- package/dist/type.js.map +1 -1
- package/dist/value.d.ts.map +1 -1
- package/dist/value.js +7 -1
- package/dist/value.js.map +1 -1
- package/dist/variant.d.ts +2 -6
- package/dist/variant.d.ts.map +1 -1
- package/dist/variant.js.map +1 -1
- package/dist/vfunc-call.d.ts.map +1 -1
- package/dist/vfunc-call.js +2 -11
- package/dist/vfunc-call.js.map +1 -1
- package/package.json +9 -5
- package/src/application-class.ts +2 -2
- package/src/arg.ts +4 -1
- package/src/bind.ts +1 -12
- package/src/callback.ts +53 -29
- package/src/closure.ts +31 -6
- package/src/descriptors.ts +95 -59
- package/src/fn.ts +31 -10
- package/src/folded-lengths.ts +23 -10
- package/src/index.ts +2 -24
- package/src/internal.ts +43 -2
- package/src/listeners.ts +21 -26
- package/src/mixin.ts +55 -9
- package/src/native-value.ts +24 -3
- package/src/object.ts +55 -5
- package/src/properties.ts +90 -26
- package/src/property-brand.ts +5 -0
- package/src/register-class.ts +338 -341
- package/src/registry.ts +4 -4
- package/src/signal-brand.ts +29 -0
- package/src/signal.ts +185 -39
- package/src/t.ts +5 -21
- package/src/type.ts +10 -2
- package/src/value.ts +9 -1
- package/src/variant.ts +2 -6
- package/src/vfunc-call.ts +3 -16
package/src/mixin.ts
CHANGED
|
@@ -17,6 +17,12 @@ type MixinReceiver = {
|
|
|
17
17
|
*/
|
|
18
18
|
type Mixin = (base: AnyClass<MixinReceiver>) => AnyClass;
|
|
19
19
|
|
|
20
|
+
const syntheticSignalMembers: WeakMap<object, ReadonlySet<string>> = new WeakMap();
|
|
21
|
+
|
|
22
|
+
function markSyntheticSignalMembers(klass: AnyClass, names: readonly string[]): void {
|
|
23
|
+
syntheticSignalMembers.set(klass.prototype, new Set(names));
|
|
24
|
+
}
|
|
25
|
+
|
|
20
26
|
function isDefinedInClassChain(prototype: object, key: string): boolean {
|
|
21
27
|
let current: object | null = prototype;
|
|
22
28
|
|
|
@@ -31,12 +37,28 @@ function isDefinedInClassChain(prototype: object, key: string): boolean {
|
|
|
31
37
|
return false;
|
|
32
38
|
}
|
|
33
39
|
|
|
34
|
-
function
|
|
40
|
+
function isNaturalInClassChain(prototype: object, key: string): boolean {
|
|
41
|
+
let current: object | null = prototype;
|
|
42
|
+
|
|
43
|
+
while (current !== null && current !== Object.prototype) {
|
|
44
|
+
if (Object.hasOwn(current, key)) {
|
|
45
|
+
return syntheticSignalMembers.get(current)?.has(key) !== true;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
current = Reflect.getPrototypeOf(current);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function copyLayerMember(target: AnyClass, layer: object, key: string, inheritedOverrides: Set<string>): void {
|
|
35
55
|
if (key === "constructor") {
|
|
36
56
|
return;
|
|
37
57
|
}
|
|
38
58
|
|
|
39
|
-
|
|
59
|
+
const isProtectedInherited = !inheritedOverrides.has(key) && isDefinedInClassChain(target.prototype, key);
|
|
60
|
+
|
|
61
|
+
if (isProtectedInherited || Object.hasOwn(target.prototype, key)) {
|
|
40
62
|
return;
|
|
41
63
|
}
|
|
42
64
|
|
|
@@ -47,9 +69,9 @@ function copyLayerMember(target: AnyClass, layer: object, key: string): void {
|
|
|
47
69
|
}
|
|
48
70
|
}
|
|
49
71
|
|
|
50
|
-
function copyLayerMembers(target: AnyClass, layer: object): void {
|
|
72
|
+
function copyLayerMembers(target: AnyClass, layer: object, inheritedOverrides: Set<string> = new Set()): void {
|
|
51
73
|
for (const key of Object.getOwnPropertyNames(layer)) {
|
|
52
|
-
copyLayerMember(target, layer, key);
|
|
74
|
+
copyLayerMember(target, layer, key, inheritedOverrides);
|
|
53
75
|
}
|
|
54
76
|
}
|
|
55
77
|
|
|
@@ -60,7 +82,8 @@ function copyLayerMembers(target: AnyClass, layer: object): void {
|
|
|
60
82
|
* @param target The class whose prototype receives the mixin members.
|
|
61
83
|
* @param mixins The mixins to apply, in order.
|
|
62
84
|
*/
|
|
63
|
-
function installMixins(target: AnyClass, mixins: Mixin[]): void {
|
|
85
|
+
function installMixins(target: AnyClass, mixins: Mixin[], inheritedOverrideNames: Iterable<string> = []): void {
|
|
86
|
+
const inheritedOverrides = new Set(inheritedOverrideNames);
|
|
64
87
|
const empty: AnyClass<MixinReceiver> = class {
|
|
65
88
|
connect(): number {
|
|
66
89
|
return 0;
|
|
@@ -72,7 +95,7 @@ function installMixins(target: AnyClass, mixins: Mixin[]): void {
|
|
|
72
95
|
};
|
|
73
96
|
|
|
74
97
|
for (const mixin of mixins) {
|
|
75
|
-
copyLayerMembers(target, mixin(empty).prototype);
|
|
98
|
+
copyLayerMembers(target, mixin(empty).prototype, inheritedOverrides);
|
|
76
99
|
}
|
|
77
100
|
}
|
|
78
101
|
|
|
@@ -82,11 +105,34 @@ function dropLayerMembers(layer: AnyClass, names: Set<string>): void {
|
|
|
82
105
|
}
|
|
83
106
|
}
|
|
84
107
|
|
|
108
|
+
function createMixinLayer(parent: AnyClass<MixinReceiver>, mixin: Mixin, inheritedNames: Set<string>): AnyClass {
|
|
109
|
+
const layer = mixin(parent);
|
|
110
|
+
const protectedNames = new Set(inheritedNames);
|
|
111
|
+
|
|
112
|
+
for (const key of Object.getOwnPropertyNames(layer.prototype)) {
|
|
113
|
+
if (key !== "constructor" && isNaturalInClassChain(parent.prototype, key)) {
|
|
114
|
+
protectedNames.add(key);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
dropLayerMembers(layer, protectedNames);
|
|
119
|
+
|
|
120
|
+
return layer;
|
|
121
|
+
}
|
|
122
|
+
|
|
85
123
|
function insertMixinLayer(target: AnyClass, mixin: Mixin, inheritedNames: Set<string>): void {
|
|
86
|
-
const
|
|
87
|
-
|
|
124
|
+
const parent = getParentClass(target) as AnyClass<MixinReceiver>;
|
|
125
|
+
const layer = createMixinLayer(parent, mixin, inheritedNames);
|
|
88
126
|
Object.setPrototypeOf(target.prototype, layer.prototype);
|
|
89
127
|
Object.setPrototypeOf(target, layer);
|
|
90
128
|
}
|
|
91
129
|
|
|
92
|
-
export {
|
|
130
|
+
export {
|
|
131
|
+
copyLayerMembers,
|
|
132
|
+
createMixinLayer,
|
|
133
|
+
insertMixinLayer,
|
|
134
|
+
installMixins,
|
|
135
|
+
markSyntheticSignalMembers,
|
|
136
|
+
type MixinReceiver,
|
|
137
|
+
type Mixin,
|
|
138
|
+
};
|
package/src/native-value.ts
CHANGED
|
@@ -255,12 +255,33 @@ function collectionToNative(descriptor: ArrayDescriptor, value: unknown): unknow
|
|
|
255
255
|
return value == null ? null : mapCollection(descriptor, value, toNative);
|
|
256
256
|
}
|
|
257
257
|
|
|
258
|
-
|
|
258
|
+
/**
|
|
259
|
+
* Normalises the JavaScript form of a hash table argument into its entry list. A `Map` is the
|
|
260
|
+
* expected form and any other iterable of pairs is accepted; anything else is rejected, a plain
|
|
261
|
+
* object above all, which `Array.from` would quietly flatten into an empty table.
|
|
262
|
+
*
|
|
263
|
+
* @param value The JavaScript value passed for a hash table argument.
|
|
264
|
+
*/
|
|
265
|
+
function toHashTableEntries(value: unknown): [unknown, unknown][] | null {
|
|
259
266
|
if (value == null) {
|
|
260
267
|
return null;
|
|
261
268
|
}
|
|
262
269
|
|
|
263
|
-
|
|
270
|
+
if (typeof (value as Partial<Iterable<unknown>>)[Symbol.iterator] !== "function") {
|
|
271
|
+
throw new TypeError("A hash table argument must be a Map or an iterable of [key, value] pairs");
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
return [...(value as Iterable<[unknown, unknown]>)];
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
function hashTableToNative(descriptor: HashTableDescriptor, value: unknown): unknown {
|
|
278
|
+
const entries = toHashTableEntries(value);
|
|
279
|
+
|
|
280
|
+
if (entries === null) {
|
|
281
|
+
return null;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
return entries.map(([key, val]): [unknown, unknown] => [
|
|
264
285
|
toNative(descriptor.keyDescriptor, key),
|
|
265
286
|
toNative(descriptor.valueDescriptor, val),
|
|
266
287
|
]);
|
|
@@ -302,4 +323,4 @@ function toNative(descriptor: Descriptor, value: unknown): unknown {
|
|
|
302
323
|
}
|
|
303
324
|
}
|
|
304
325
|
|
|
305
|
-
export { fromNative, toNative };
|
|
326
|
+
export { fromNative, toHashTableEntries, toNative };
|
package/src/object.ts
CHANGED
|
@@ -3,9 +3,16 @@ import { type AnyClass, getParentClass } from "@gtkx/utils";
|
|
|
3
3
|
import { bind } from "./bind.js";
|
|
4
4
|
import { objectT, stringT, voidT } from "./descriptors.js";
|
|
5
5
|
import { LIB, VALUE_T } from "./library.js";
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
coercePropertyValue,
|
|
8
|
+
type ConstructProperty,
|
|
9
|
+
constructPropertyFor,
|
|
10
|
+
readableObjectPropertyFor,
|
|
11
|
+
writableObjectPropertyFor,
|
|
12
|
+
} from "./properties.js";
|
|
13
|
+
import { propertyMapOverride, writablePropertyMapOverride } from "./property-brand.js";
|
|
7
14
|
import { getHandle, registerWrapper } from "./registry.js";
|
|
8
|
-
import { fromValueForDescriptor, newValueForDescriptor, toValue } from "./value.js";
|
|
15
|
+
import { fromValue, fromValueForDescriptor, newValueForDescriptor, toValue } from "./value.js";
|
|
9
16
|
|
|
10
17
|
/**
|
|
11
18
|
* One construct property a wrapper class accepts: the canonical `GObject` name it is set under,
|
|
@@ -172,7 +179,28 @@ function newObjectWithProperties<T extends object>(gtype: bigint, props: object,
|
|
|
172
179
|
* @param propertyName The property name.
|
|
173
180
|
* @param descriptor Describes the property's type.
|
|
174
181
|
*/
|
|
175
|
-
function getObjectProperty
|
|
182
|
+
function getObjectProperty<
|
|
183
|
+
TObject extends { __properties__: object },
|
|
184
|
+
TPropertyMap extends object = TObject extends { [propertyMapOverride]?: infer TResolver }
|
|
185
|
+
? TResolver extends () => infer TMap
|
|
186
|
+
? Extract<NonNullable<TMap>, object>
|
|
187
|
+
: TObject["__properties__"]
|
|
188
|
+
: TObject["__properties__"],
|
|
189
|
+
TName extends Extract<keyof NoInfer<TPropertyMap>, string> = Extract<keyof NoInfer<TPropertyMap>, string>,
|
|
190
|
+
>(obj: TObject, propertyName: TName): NoInfer<TPropertyMap>[TName];
|
|
191
|
+
function getObjectProperty(obj: object, propertyName: string, descriptor: Descriptor): unknown;
|
|
192
|
+
function getObjectProperty(obj: object, propertyName: string, descriptor?: Descriptor): unknown {
|
|
193
|
+
if (arguments.length === 2) {
|
|
194
|
+
const property = readableObjectPropertyFor(obj, propertyName);
|
|
195
|
+
gObjectGetProperty(getHandle(obj), property.name, property.value);
|
|
196
|
+
|
|
197
|
+
return fromValue(property.value);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
if (descriptor === undefined) {
|
|
201
|
+
throw new TypeError("getObjectProperty requires a property descriptor when called with three arguments");
|
|
202
|
+
}
|
|
203
|
+
|
|
176
204
|
const value = newValueForDescriptor(descriptor);
|
|
177
205
|
gObjectGetProperty(getHandle(obj), propertyName, value);
|
|
178
206
|
|
|
@@ -192,8 +220,30 @@ function getObjectProperty(obj: object, propertyName: string, descriptor: Descri
|
|
|
192
220
|
* @param descriptor Describes the property's type.
|
|
193
221
|
* @param jsValue The value to set.
|
|
194
222
|
*/
|
|
195
|
-
function setObjectProperty
|
|
196
|
-
|
|
223
|
+
function setObjectProperty<
|
|
224
|
+
TObject extends { __writableProperties__: object },
|
|
225
|
+
TPropertyMap extends object = TObject extends { [writablePropertyMapOverride]?: infer TResolver }
|
|
226
|
+
? TResolver extends () => infer TMap
|
|
227
|
+
? Extract<NonNullable<TMap>, object>
|
|
228
|
+
: TObject["__writableProperties__"]
|
|
229
|
+
: TObject["__writableProperties__"],
|
|
230
|
+
TName extends Extract<keyof NoInfer<TPropertyMap>, string> = Extract<keyof NoInfer<TPropertyMap>, string>,
|
|
231
|
+
>(obj: TObject, propertyName: TName, jsValue: NoInfer<TPropertyMap>[TName]): void;
|
|
232
|
+
function setObjectProperty(obj: object, propertyName: string, descriptor: Descriptor, jsValue: unknown): void;
|
|
233
|
+
function setObjectProperty(
|
|
234
|
+
obj: object,
|
|
235
|
+
propertyName: string,
|
|
236
|
+
descriptorOrValue: unknown,
|
|
237
|
+
jsValue?: unknown,
|
|
238
|
+
): void {
|
|
239
|
+
if (arguments.length === 3) {
|
|
240
|
+
const property = writableObjectPropertyFor(obj, propertyName, descriptorOrValue);
|
|
241
|
+
gObjectSetProperty(getHandle(obj), property.name, property.value);
|
|
242
|
+
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
gObjectSetProperty(getHandle(obj), propertyName, toValue(descriptorOrValue as Descriptor, jsValue));
|
|
197
247
|
}
|
|
198
248
|
|
|
199
249
|
export {
|
package/src/properties.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import type { ExternalObject, Handle, RegisterClassProperty } from "@gtkx/native";
|
|
2
|
-
import type
|
|
3
|
-
import { camelCase, kebabCase, toCamelIdentifier } from "@gtkx/utils";
|
|
2
|
+
import { type AnyClass, camelCase, kebabCase, toCamelIdentifier } from "@gtkx/utils";
|
|
4
3
|
import { bind } from "./bind.js";
|
|
5
4
|
import { biguint64T, fundamentalT, refT, sizedArrayT, stringT, structT, uint32T, voidT } from "./descriptors.js";
|
|
6
5
|
import { LIB, PARAM_T, VALUE_T } from "./library.js";
|
|
@@ -56,7 +55,7 @@ type AccessorBase = PropertyCheck & {
|
|
|
56
55
|
type DeclaredAccessor = AccessorBase & {
|
|
57
56
|
isInterfaceProperty?: false;
|
|
58
57
|
memberName: string;
|
|
59
|
-
|
|
58
|
+
hasMemberAccessor: boolean;
|
|
60
59
|
};
|
|
61
60
|
|
|
62
61
|
type InterfaceAccessor = AccessorBase & { isInterfaceProperty: true };
|
|
@@ -101,7 +100,7 @@ const paramSpecDefaultValue = bind(LIB, "g_param_spec_get_default_value", [PARAM
|
|
|
101
100
|
const paramSpecName = bind(LIB, "g_param_spec_get_name", [PARAM_T], stringT("borrowed"));
|
|
102
101
|
const typeClassRef = bind(LIB, "g_type_class_ref", [biguint64T], CLASS_T);
|
|
103
102
|
const typeClassUnref = bind(LIB, "g_type_class_unref", [CLASS_T], voidT);
|
|
104
|
-
const
|
|
103
|
+
const propertyChecks: Map<bigint, Map<string, PropertyCheck | null>> = new Map();
|
|
105
104
|
const classFindProperty = bind(LIB, "g_object_class_find_property", [CLASS_T, stringT("borrowed")], PARAM_T);
|
|
106
105
|
|
|
107
106
|
const classListProperties = bind(
|
|
@@ -215,8 +214,11 @@ const findSourceSpec = (source: bigint | AnyClass, name: string): ExternalObject
|
|
|
215
214
|
* @param source The GType, wrapper class or interface declaring the property.
|
|
216
215
|
* @returns Handle of the override spec.
|
|
217
216
|
*/
|
|
218
|
-
const newParamSpecOverride = (name: string, source: bigint | AnyClass): ExternalObject<Handle> =>
|
|
219
|
-
|
|
217
|
+
const newParamSpecOverride = (name: string, source: bigint | AnyClass): ExternalObject<Handle> => {
|
|
218
|
+
const canonicalName = canonicalCase(name);
|
|
219
|
+
|
|
220
|
+
return paramSpecOverrideNew(canonicalName, findSourceSpec(source, canonicalName)) as ExternalObject<Handle>;
|
|
221
|
+
};
|
|
220
222
|
|
|
221
223
|
function describeValue(value: unknown): string {
|
|
222
224
|
if (typeof value === "string") {
|
|
@@ -284,6 +286,24 @@ function assertWritable(instance: object, check: PropertyCheck, value: unknown):
|
|
|
284
286
|
throw new TypeError(propertyMessage(instance, check, refusalTail(check, value, READ_ONLY_REASON)));
|
|
285
287
|
}
|
|
286
288
|
|
|
289
|
+
function assertReadable(instance: object, check: PropertyCheck): void {
|
|
290
|
+
if (isParamReadable(check.flags)) {
|
|
291
|
+
return;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
throw new TypeError(
|
|
295
|
+
propertyMessage(instance, check, `cannot read property '${check.propertyName}'; the property is write-only`),
|
|
296
|
+
);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
function assertMutable(instance: object, check: PropertyCheck, value: unknown): void {
|
|
300
|
+
assertWritable(instance, check, value);
|
|
301
|
+
|
|
302
|
+
if (isParamConstructOnly(check.flags)) {
|
|
303
|
+
throw new TypeError(propertyMessage(instance, check, refusalTail(check, value, CONSTRUCT_ONLY_REASON)));
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
287
307
|
function writeHeld(check: PropertyCheck, gValue: ExternalObject<Handle>, value: unknown): void {
|
|
288
308
|
check.write ??= valueWriterFor(check.valueType);
|
|
289
309
|
check.write(gValue, check.narrowValue(value));
|
|
@@ -334,7 +354,7 @@ function isReadableProperty(gtype: bigint, propertyName: string): boolean {
|
|
|
334
354
|
}
|
|
335
355
|
}
|
|
336
356
|
|
|
337
|
-
function
|
|
357
|
+
function lookupPropertyCheck(gtype: bigint, name: string): PropertyCheck | null {
|
|
338
358
|
const klass = typeClassRef(gtype) as ExternalObject<Handle>;
|
|
339
359
|
|
|
340
360
|
try {
|
|
@@ -346,24 +366,37 @@ function lookupCoercionCheck(gtype: bigint, name: string): PropertyCheck | null
|
|
|
346
366
|
}
|
|
347
367
|
}
|
|
348
368
|
|
|
349
|
-
function
|
|
350
|
-
|
|
369
|
+
function propertyCheckFor(gtype: bigint, name: string): PropertyCheck | null {
|
|
370
|
+
const checks = propertyChecks.getOrInsertComputed(gtype, () => new Map<string, PropertyCheck | null>());
|
|
351
371
|
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
coercionChecks.set(gtype, checks);
|
|
355
|
-
}
|
|
372
|
+
return checks.getOrInsertComputed(name, () => lookupPropertyCheck(gtype, name));
|
|
373
|
+
}
|
|
356
374
|
|
|
357
|
-
|
|
375
|
+
function objectPropertyCheckFor(instance: object, name: string): PropertyCheck {
|
|
376
|
+
const gtype = getInstanceType(instance);
|
|
377
|
+
const check = typeIsA(gtype, TYPE_OBJECT) ? propertyCheckFor(gtype, name) : null;
|
|
358
378
|
|
|
359
|
-
if (check ===
|
|
360
|
-
|
|
361
|
-
checks.set(name, check);
|
|
379
|
+
if (check === null) {
|
|
380
|
+
throw new TypeError(`${instanceClassName(instance)} has no GObject property named '${name}'`);
|
|
362
381
|
}
|
|
363
382
|
|
|
364
383
|
return check;
|
|
365
384
|
}
|
|
366
385
|
|
|
386
|
+
function readableObjectPropertyFor(instance: object, name: string): ConstructProperty {
|
|
387
|
+
const check = objectPropertyCheckFor(instance, name);
|
|
388
|
+
assertReadable(instance, check);
|
|
389
|
+
|
|
390
|
+
return { name: check.propertyName, value: newValueForType(check.valueType) };
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
function writableObjectPropertyFor(instance: object, name: string, value: unknown): ConstructProperty {
|
|
394
|
+
const check = objectPropertyCheckFor(instance, name);
|
|
395
|
+
assertMutable(instance, check, value);
|
|
396
|
+
|
|
397
|
+
return { name: check.propertyName, value: checkedValueFor(instance, check, value) };
|
|
398
|
+
}
|
|
399
|
+
|
|
367
400
|
function truncateToWhole(check: PropertyCheck, value: number): number {
|
|
368
401
|
const range = WHOLE_NUMBER_RANGES.get(typeFundamental(check.valueType));
|
|
369
402
|
|
|
@@ -397,7 +430,7 @@ function coercePropertyValue(gtype: bigint, propertyName: string, value: unknown
|
|
|
397
430
|
return value;
|
|
398
431
|
}
|
|
399
432
|
|
|
400
|
-
const check =
|
|
433
|
+
const check = propertyCheckFor(gtype, propertyName);
|
|
401
434
|
|
|
402
435
|
return check === null ? value : coerceNumber(check, value);
|
|
403
436
|
}
|
|
@@ -512,12 +545,35 @@ function checkedSetter(accessor: DeclaredAccessor): (this: object, value: unknow
|
|
|
512
545
|
};
|
|
513
546
|
}
|
|
514
547
|
|
|
515
|
-
function
|
|
516
|
-
|
|
548
|
+
function memberDescriptorFor(prototype: object, name: string): PropertyDescriptor | undefined {
|
|
549
|
+
let current: object | null = prototype;
|
|
550
|
+
|
|
551
|
+
while (current !== null) {
|
|
552
|
+
const descriptor = Object.getOwnPropertyDescriptor(current, name);
|
|
553
|
+
|
|
554
|
+
if (descriptor !== undefined) {
|
|
555
|
+
return descriptor;
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
current = Reflect.getPrototypeOf(current);
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
return undefined;
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
function isMemberAccessor(descriptor: PropertyDescriptor | undefined): boolean {
|
|
565
|
+
return descriptor !== undefined && ("get" in descriptor || "set" in descriptor);
|
|
517
566
|
}
|
|
518
567
|
|
|
519
568
|
function defineAccessor(prototype: object, descriptor: PropertyDescriptor, alias: string): void {
|
|
520
|
-
if (
|
|
569
|
+
if (Object.getOwnPropertyDescriptor(prototype, alias) !== undefined) {
|
|
570
|
+
return;
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
const parent = Reflect.getPrototypeOf(prototype);
|
|
574
|
+
const inherited = parent === null ? undefined : memberDescriptorFor(parent, alias);
|
|
575
|
+
|
|
576
|
+
if (inherited !== undefined && !isMemberAccessor(inherited)) {
|
|
521
577
|
return;
|
|
522
578
|
}
|
|
523
579
|
|
|
@@ -526,13 +582,13 @@ function defineAccessor(prototype: object, descriptor: PropertyDescriptor, alias
|
|
|
526
582
|
|
|
527
583
|
function installAccessors(klass: AnyClass, accessor: DeclaredAccessor): void {
|
|
528
584
|
const prototype = (klass as { prototype: object }).prototype;
|
|
529
|
-
accessor.
|
|
585
|
+
accessor.hasMemberAccessor = isMemberAccessor(Object.getOwnPropertyDescriptor(prototype, accessor.memberName));
|
|
530
586
|
|
|
531
587
|
const descriptor: PropertyDescriptor = {
|
|
532
588
|
configurable: true,
|
|
533
589
|
enumerable: true,
|
|
534
|
-
get: accessor.
|
|
535
|
-
set: accessor.
|
|
590
|
+
get: accessor.hasMemberAccessor ? memberGetter(accessor) : storedGetter(accessor),
|
|
591
|
+
set: accessor.hasMemberAccessor ? memberSetter(accessor) : checkedSetter(accessor),
|
|
536
592
|
};
|
|
537
593
|
|
|
538
594
|
for (const alias of accessorNames(accessor.name)) {
|
|
@@ -599,7 +655,13 @@ function callMember(instance: object, member: string, args: unknown[]): unknown
|
|
|
599
655
|
}
|
|
600
656
|
|
|
601
657
|
function backingMemberFor(instance: object, accessor: DeclaredAccessor): string | undefined {
|
|
602
|
-
if (accessor.
|
|
658
|
+
if (accessor.hasMemberAccessor) {
|
|
659
|
+
return accessor.memberName;
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
const descriptor = Object.getOwnPropertyDescriptor(instance, accessor.memberName);
|
|
663
|
+
|
|
664
|
+
if (descriptor === undefined || ("value" in descriptor && typeof descriptor.value === "function")) {
|
|
603
665
|
return undefined;
|
|
604
666
|
}
|
|
605
667
|
|
|
@@ -699,7 +761,7 @@ function buildAccessor(klass: AnyClass, name: string, pspec: PropertySpec): Decl
|
|
|
699
761
|
...checkFor(getHandle(pspec), name),
|
|
700
762
|
memberName: camelCase(name),
|
|
701
763
|
storage: Symbol(`gtkx:property:${name}`),
|
|
702
|
-
|
|
764
|
+
hasMemberAccessor: false,
|
|
703
765
|
};
|
|
704
766
|
|
|
705
767
|
installAccessors(klass, accessor);
|
|
@@ -783,8 +845,10 @@ export {
|
|
|
783
845
|
makeGetProperty,
|
|
784
846
|
makeSetProperty,
|
|
785
847
|
newParamSpecOverride,
|
|
848
|
+
readableObjectPropertyFor,
|
|
786
849
|
SET_PROPERTY_VFUNC,
|
|
787
850
|
toNativeProperties,
|
|
851
|
+
writableObjectPropertyFor,
|
|
788
852
|
type ConstructProperty,
|
|
789
853
|
type PropertyDispatch,
|
|
790
854
|
type PropertySpec,
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
const propertyMapOverride: unique symbol = Symbol("gtkx.propertyMapOverride");
|
|
2
|
+
const writablePropertyMapOverride: unique symbol = Symbol("gtkx.writablePropertyMapOverride");
|
|
3
|
+
const descriptorFreePropertySpec: unique symbol = Symbol("gtkx.descriptorFreePropertySpec");
|
|
4
|
+
|
|
5
|
+
export { descriptorFreePropertySpec, propertyMapOverride, writablePropertyMapOverride };
|