@gtkx/gir 0.18.0 → 0.18.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/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -0
- package/dist/internal/normalizer.d.ts +1 -0
- package/dist/internal/normalizer.d.ts.map +1 -0
- package/dist/internal/normalizer.js +1 -0
- package/dist/internal/normalizer.js.map +1 -0
- package/dist/internal/parser.d.ts +1 -0
- package/dist/internal/parser.d.ts.map +1 -0
- package/dist/internal/parser.js +1 -0
- package/dist/internal/parser.js.map +1 -0
- package/dist/internal/raw-types.d.ts +1 -0
- package/dist/internal/raw-types.d.ts.map +1 -0
- package/dist/internal/raw-types.js +1 -0
- package/dist/internal/raw-types.js.map +1 -0
- package/dist/intrinsics.d.ts +1 -0
- package/dist/intrinsics.d.ts.map +1 -0
- package/dist/intrinsics.js +1 -0
- package/dist/intrinsics.js.map +1 -0
- package/dist/repository.d.ts +1 -0
- package/dist/repository.d.ts.map +1 -0
- package/dist/repository.js +1 -0
- package/dist/repository.js.map +1 -0
- package/dist/types.d.ts +1 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +1 -0
- package/dist/types.js.map +1 -0
- package/dist/utils.d.ts +1 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +1 -0
- package/dist/utils.js.map +1 -0
- package/package.json +4 -2
- package/src/index.ts +64 -0
- package/src/internal/normalizer.ts +551 -0
- package/src/internal/parser.ts +633 -0
- package/src/internal/raw-types.ts +268 -0
- package/src/intrinsics.ts +129 -0
- package/src/repository.ts +406 -0
- package/src/types.ts +1192 -0
- package/src/utils.ts +12 -0
|
@@ -0,0 +1,633 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GObject Introspection XML (GIR) parser.
|
|
3
|
+
*
|
|
4
|
+
* Parses GIR files to extract type information for GTK/GLib libraries.
|
|
5
|
+
* Outputs raw GIR types that are then normalized by the normalizer.
|
|
6
|
+
*
|
|
7
|
+
* @internal
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { XMLParser } from "fast-xml-parser";
|
|
11
|
+
import type {
|
|
12
|
+
ContainerType,
|
|
13
|
+
RawAlias,
|
|
14
|
+
RawCallback,
|
|
15
|
+
RawClass,
|
|
16
|
+
RawConstant,
|
|
17
|
+
RawConstructor,
|
|
18
|
+
RawEnumeration,
|
|
19
|
+
RawEnumerationMember,
|
|
20
|
+
RawField,
|
|
21
|
+
RawFunction,
|
|
22
|
+
RawInterface,
|
|
23
|
+
RawMethod,
|
|
24
|
+
RawNamespace,
|
|
25
|
+
RawParameter,
|
|
26
|
+
RawProperty,
|
|
27
|
+
RawRecord,
|
|
28
|
+
RawSignal,
|
|
29
|
+
RawType,
|
|
30
|
+
} from "./raw-types.js";
|
|
31
|
+
|
|
32
|
+
const ARRAY_ELEMENT_PATHS = new Set<string>([
|
|
33
|
+
"namespace.class",
|
|
34
|
+
"namespace.interface",
|
|
35
|
+
"namespace.function",
|
|
36
|
+
"namespace.enumeration",
|
|
37
|
+
"namespace.bitfield",
|
|
38
|
+
"namespace.record",
|
|
39
|
+
"namespace.callback",
|
|
40
|
+
"namespace.constant",
|
|
41
|
+
"namespace.alias",
|
|
42
|
+
"namespace.class.method",
|
|
43
|
+
"namespace.class.constructor",
|
|
44
|
+
"namespace.class.function",
|
|
45
|
+
"namespace.class.property",
|
|
46
|
+
"namespace.class.signal",
|
|
47
|
+
"namespace.class.glib:signal",
|
|
48
|
+
"namespace.interface.method",
|
|
49
|
+
"namespace.interface.property",
|
|
50
|
+
"namespace.interface.signal",
|
|
51
|
+
"namespace.interface.glib:signal",
|
|
52
|
+
"namespace.record.method",
|
|
53
|
+
"namespace.record.constructor",
|
|
54
|
+
"namespace.record.function",
|
|
55
|
+
"namespace.record.field",
|
|
56
|
+
"namespace.class.method.parameters.parameter",
|
|
57
|
+
"namespace.class.constructor.parameters.parameter",
|
|
58
|
+
"namespace.class.function.parameters.parameter",
|
|
59
|
+
"namespace.function.parameters.parameter",
|
|
60
|
+
"namespace.enumeration.member",
|
|
61
|
+
"namespace.bitfield.member",
|
|
62
|
+
"namespace.interface.method.parameters.parameter",
|
|
63
|
+
"namespace.class.glib:signal.parameters.parameter",
|
|
64
|
+
"namespace.interface.glib:signal.parameters.parameter",
|
|
65
|
+
"namespace.record.method.parameters.parameter",
|
|
66
|
+
"namespace.record.constructor.parameters.parameter",
|
|
67
|
+
"namespace.record.function.parameters.parameter",
|
|
68
|
+
"namespace.callback.parameters.parameter",
|
|
69
|
+
]);
|
|
70
|
+
|
|
71
|
+
const extractDoc = (node: Record<string, unknown>): string | undefined => {
|
|
72
|
+
const doc = node.doc as Record<string, unknown> | undefined;
|
|
73
|
+
if (!doc) return undefined;
|
|
74
|
+
const text = doc["#text"];
|
|
75
|
+
if (typeof text !== "string") return undefined;
|
|
76
|
+
return text.trim();
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
const ensureArray = (value: unknown): Record<string, unknown>[] =>
|
|
80
|
+
Array.isArray(value) ? (value as Record<string, unknown>[]) : [];
|
|
81
|
+
|
|
82
|
+
export type ParserOptions = {
|
|
83
|
+
includeNonIntrospectableNamespaces?: Set<string>;
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Parser for GObject Introspection XML (GIR) files.
|
|
88
|
+
*
|
|
89
|
+
* Converts GIR XML into raw TypeScript objects.
|
|
90
|
+
*/
|
|
91
|
+
export class RawGirParser {
|
|
92
|
+
private parser: XMLParser;
|
|
93
|
+
private includeNonIntrospectableNamespaces: Set<string>;
|
|
94
|
+
private currentNamespace: string = "";
|
|
95
|
+
|
|
96
|
+
constructor(options: ParserOptions = {}) {
|
|
97
|
+
this.includeNonIntrospectableNamespaces = options.includeNonIntrospectableNamespaces ?? new Set();
|
|
98
|
+
this.parser = new XMLParser({
|
|
99
|
+
ignoreAttributes: false,
|
|
100
|
+
attributeNamePrefix: "@_",
|
|
101
|
+
textNodeName: "#text",
|
|
102
|
+
isArray: (_name, jpath, _isLeafNode, _isAttribute) => {
|
|
103
|
+
const path = jpath.split(".").slice(1).join(".");
|
|
104
|
+
return ARRAY_ELEMENT_PATHS.has(path);
|
|
105
|
+
},
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
private shouldIncludeNonIntrospectable(): boolean {
|
|
110
|
+
return this.includeNonIntrospectableNamespaces.has(this.currentNamespace);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
private isIntrospectable(node: Record<string, unknown>): boolean {
|
|
114
|
+
if (node["@_introspectable"] === "0") {
|
|
115
|
+
return this.shouldIncludeNonIntrospectable();
|
|
116
|
+
}
|
|
117
|
+
return true;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Parses a GIR XML string into a raw namespace object.
|
|
122
|
+
*/
|
|
123
|
+
parse(girXml: string): RawNamespace {
|
|
124
|
+
const parsed = this.parser.parse(girXml);
|
|
125
|
+
const repository = parsed.repository;
|
|
126
|
+
|
|
127
|
+
if (!repository?.namespace) {
|
|
128
|
+
throw new Error("Failed to parse GIR file: missing repository or namespace element");
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const namespace = repository.namespace;
|
|
132
|
+
this.currentNamespace = namespace["@_name"];
|
|
133
|
+
|
|
134
|
+
return {
|
|
135
|
+
name: namespace["@_name"],
|
|
136
|
+
version: namespace["@_version"],
|
|
137
|
+
sharedLibrary: namespace["@_shared-library"] ?? "",
|
|
138
|
+
cPrefix: namespace["@_c:identifier-prefixes"] ?? namespace["@_c:prefix"] ?? "",
|
|
139
|
+
classes: this.parseClasses(namespace.class ?? []),
|
|
140
|
+
interfaces: this.parseInterfaces(namespace.interface ?? []),
|
|
141
|
+
functions: this.parseFunctions(namespace.function ?? []),
|
|
142
|
+
enumerations: this.parseEnumerations(namespace.enumeration ?? []),
|
|
143
|
+
bitfields: this.parseEnumerations(namespace.bitfield ?? []),
|
|
144
|
+
records: this.parseRecords(namespace.record ?? []),
|
|
145
|
+
callbacks: this.parseCallbacks(namespace.callback ?? []),
|
|
146
|
+
constants: this.parseConstants(namespace.constant ?? []),
|
|
147
|
+
aliases: this.parseAliases(namespace.alias ?? []),
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
private parseCallbacks(callbacks: Record<string, unknown>[]): RawCallback[] {
|
|
152
|
+
if (!callbacks || !Array.isArray(callbacks)) {
|
|
153
|
+
return [];
|
|
154
|
+
}
|
|
155
|
+
return callbacks
|
|
156
|
+
.filter((cb) => this.isIntrospectable(cb))
|
|
157
|
+
.map((cb) => ({
|
|
158
|
+
name: String(cb["@_name"] ?? ""),
|
|
159
|
+
cType: String(cb["@_c:type"] ?? ""),
|
|
160
|
+
returnType: this.parseReturnType(cb["return-value"] as Record<string, unknown> | undefined),
|
|
161
|
+
parameters: this.parseParameters(
|
|
162
|
+
(cb.parameters && typeof cb.parameters === "object" && cb.parameters !== null
|
|
163
|
+
? cb.parameters
|
|
164
|
+
: {}) as Record<string, unknown>,
|
|
165
|
+
),
|
|
166
|
+
doc: extractDoc(cb),
|
|
167
|
+
}));
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
private parseClasses(classes: Record<string, unknown>[]): RawClass[] {
|
|
171
|
+
return classes.map((cls) => ({
|
|
172
|
+
name: String(cls["@_name"] ?? ""),
|
|
173
|
+
cType: String(cls["@_c:type"] ?? cls["@_glib:type-name"] ?? ""),
|
|
174
|
+
parent: cls["@_parent"] ? String(cls["@_parent"]) : undefined,
|
|
175
|
+
abstract: cls["@_abstract"] === "1",
|
|
176
|
+
glibTypeName: cls["@_glib:type-name"] ? String(cls["@_glib:type-name"]) : undefined,
|
|
177
|
+
glibGetType: cls["@_glib:get-type"] ? String(cls["@_glib:get-type"]) : undefined,
|
|
178
|
+
cSymbolPrefix: cls["@_c:symbol-prefix"] ? String(cls["@_c:symbol-prefix"]) : undefined,
|
|
179
|
+
fundamental: cls["@_glib:fundamental"] === "1",
|
|
180
|
+
refFunc: cls["@_glib:ref-func"] ? String(cls["@_glib:ref-func"]) : undefined,
|
|
181
|
+
unrefFunc: cls["@_glib:unref-func"] ? String(cls["@_glib:unref-func"]) : undefined,
|
|
182
|
+
implements: this.parseImplements(
|
|
183
|
+
cls.implements as Record<string, unknown>[] | Record<string, unknown> | undefined,
|
|
184
|
+
),
|
|
185
|
+
methods: this.parseMethods(ensureArray(cls.method)),
|
|
186
|
+
constructors: this.parseConstructors(ensureArray(cls.constructor)),
|
|
187
|
+
functions: this.parseFunctions(ensureArray(cls.function)),
|
|
188
|
+
properties: this.parseProperties(ensureArray(cls.property)),
|
|
189
|
+
signals: this.parseSignals(ensureArray(cls["glib:signal"])),
|
|
190
|
+
doc: extractDoc(cls),
|
|
191
|
+
}));
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
private parseImplements(implements_: Record<string, unknown>[] | Record<string, unknown> | undefined): string[] {
|
|
195
|
+
if (!implements_) return [];
|
|
196
|
+
const arr = Array.isArray(implements_) ? implements_ : [implements_];
|
|
197
|
+
return arr.map((impl) => String(impl["@_name"] ?? "")).filter(Boolean);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
private parseInterfaces(interfaces: Record<string, unknown>[]): RawInterface[] {
|
|
201
|
+
if (!interfaces || !Array.isArray(interfaces)) {
|
|
202
|
+
return [];
|
|
203
|
+
}
|
|
204
|
+
return interfaces.map((iface) => ({
|
|
205
|
+
name: String(iface["@_name"] ?? ""),
|
|
206
|
+
cType: String(iface["@_c:type"] ?? iface["@_glib:type-name"] ?? ""),
|
|
207
|
+
glibTypeName: iface["@_glib:type-name"] ? String(iface["@_glib:type-name"]) : undefined,
|
|
208
|
+
prerequisites: this.parsePrerequisites(
|
|
209
|
+
iface.prerequisite as Record<string, unknown>[] | Record<string, unknown> | undefined,
|
|
210
|
+
),
|
|
211
|
+
methods: this.parseMethods(ensureArray(iface.method)),
|
|
212
|
+
properties: this.parseProperties(ensureArray(iface.property)),
|
|
213
|
+
signals: this.parseSignals(ensureArray(iface["glib:signal"])),
|
|
214
|
+
doc: extractDoc(iface),
|
|
215
|
+
}));
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
private parsePrerequisites(
|
|
219
|
+
prerequisites: Record<string, unknown>[] | Record<string, unknown> | undefined,
|
|
220
|
+
): string[] {
|
|
221
|
+
if (!prerequisites) return [];
|
|
222
|
+
const arr = Array.isArray(prerequisites) ? prerequisites : [prerequisites];
|
|
223
|
+
return arr.map((prereq) => String(prereq["@_name"] ?? "")).filter(Boolean);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
private parseMethods(methods: Record<string, unknown>[]): RawMethod[] {
|
|
227
|
+
if (!methods || !Array.isArray(methods)) {
|
|
228
|
+
return [];
|
|
229
|
+
}
|
|
230
|
+
return methods
|
|
231
|
+
.filter((method) => this.isIntrospectable(method))
|
|
232
|
+
.map((method) => {
|
|
233
|
+
const returnValue = method["return-value"] as Record<string, unknown> | undefined;
|
|
234
|
+
const finishFunc = method["@_glib:finish-func"] as string | undefined;
|
|
235
|
+
const shadows = method["@_shadows"] as string | undefined;
|
|
236
|
+
const shadowedBy = method["@_shadowed-by"] as string | undefined;
|
|
237
|
+
const parametersNode =
|
|
238
|
+
method.parameters && typeof method.parameters === "object" && method.parameters !== null
|
|
239
|
+
? (method.parameters as Record<string, unknown>)
|
|
240
|
+
: {};
|
|
241
|
+
return {
|
|
242
|
+
name: String(method["@_name"] ?? ""),
|
|
243
|
+
cIdentifier: String(method["@_c:identifier"] ?? ""),
|
|
244
|
+
returnType: this.parseReturnType(returnValue),
|
|
245
|
+
parameters: this.parseParameters(parametersNode),
|
|
246
|
+
instanceParameter: this.parseInstanceParameter(parametersNode),
|
|
247
|
+
throws: method["@_throws"] === "1",
|
|
248
|
+
doc: extractDoc(method),
|
|
249
|
+
returnDoc: returnValue ? extractDoc(returnValue) : undefined,
|
|
250
|
+
finishFunc: finishFunc || undefined,
|
|
251
|
+
shadows: shadows || undefined,
|
|
252
|
+
shadowedBy: shadowedBy || undefined,
|
|
253
|
+
};
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
private parseConstructors(constructors: Record<string, unknown>[]): RawConstructor[] {
|
|
258
|
+
if (!constructors || !Array.isArray(constructors)) {
|
|
259
|
+
return [];
|
|
260
|
+
}
|
|
261
|
+
return constructors
|
|
262
|
+
.filter((ctor) => this.isIntrospectable(ctor))
|
|
263
|
+
.map((ctor) => {
|
|
264
|
+
const returnValue = ctor["return-value"] as Record<string, unknown> | undefined;
|
|
265
|
+
const shadows = ctor["@_shadows"] as string | undefined;
|
|
266
|
+
const shadowedBy = ctor["@_shadowed-by"] as string | undefined;
|
|
267
|
+
return {
|
|
268
|
+
name: String(ctor["@_name"] ?? ""),
|
|
269
|
+
cIdentifier: String(ctor["@_c:identifier"] ?? ""),
|
|
270
|
+
returnType: this.parseReturnType(returnValue),
|
|
271
|
+
parameters: this.parseParameters(
|
|
272
|
+
(ctor.parameters && typeof ctor.parameters === "object" && ctor.parameters !== null
|
|
273
|
+
? ctor.parameters
|
|
274
|
+
: {}) as Record<string, unknown>,
|
|
275
|
+
),
|
|
276
|
+
throws: ctor["@_throws"] === "1",
|
|
277
|
+
doc: extractDoc(ctor),
|
|
278
|
+
returnDoc: returnValue ? extractDoc(returnValue) : undefined,
|
|
279
|
+
shadows: shadows || undefined,
|
|
280
|
+
shadowedBy: shadowedBy || undefined,
|
|
281
|
+
};
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
private parseFunctions(functions: Record<string, unknown>[]): RawFunction[] {
|
|
286
|
+
if (!functions || !Array.isArray(functions)) {
|
|
287
|
+
return [];
|
|
288
|
+
}
|
|
289
|
+
return functions
|
|
290
|
+
.filter((func) => this.isIntrospectable(func))
|
|
291
|
+
.map((func) => {
|
|
292
|
+
const returnValue = func["return-value"] as Record<string, unknown> | undefined;
|
|
293
|
+
const shadows = func["@_shadows"] as string | undefined;
|
|
294
|
+
const shadowedBy = func["@_shadowed-by"] as string | undefined;
|
|
295
|
+
return {
|
|
296
|
+
name: String(func["@_name"] ?? ""),
|
|
297
|
+
cIdentifier: String(func["@_c:identifier"] ?? ""),
|
|
298
|
+
returnType: this.parseReturnType(returnValue),
|
|
299
|
+
parameters: this.parseParameters(
|
|
300
|
+
(func.parameters && typeof func.parameters === "object" && func.parameters !== null
|
|
301
|
+
? func.parameters
|
|
302
|
+
: {}) as Record<string, unknown>,
|
|
303
|
+
),
|
|
304
|
+
throws: func["@_throws"] === "1",
|
|
305
|
+
doc: extractDoc(func),
|
|
306
|
+
returnDoc: returnValue ? extractDoc(returnValue) : undefined,
|
|
307
|
+
shadows: shadows || undefined,
|
|
308
|
+
shadowedBy: shadowedBy || undefined,
|
|
309
|
+
};
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
private parseParameters(parametersNode: Record<string, unknown>): RawParameter[] {
|
|
314
|
+
if (!parametersNode?.parameter) {
|
|
315
|
+
return [];
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
const params = Array.isArray(parametersNode.parameter) ? parametersNode.parameter : [parametersNode.parameter];
|
|
319
|
+
|
|
320
|
+
return params.map((param: Record<string, unknown>) => this.parseSingleParameter(param));
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
private parseInstanceParameter(parametersNode: Record<string, unknown>): RawParameter | undefined {
|
|
324
|
+
const instanceParam = parametersNode?.["instance-parameter"] as Record<string, unknown> | undefined;
|
|
325
|
+
if (!instanceParam) {
|
|
326
|
+
return undefined;
|
|
327
|
+
}
|
|
328
|
+
return this.parseSingleParameter(instanceParam);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
private parseSingleParameter(param: Record<string, unknown>): RawParameter {
|
|
332
|
+
const scope = param["@_scope"] as string | undefined;
|
|
333
|
+
const closure = param["@_closure"] as string | undefined;
|
|
334
|
+
const destroy = param["@_destroy"] as string | undefined;
|
|
335
|
+
const transferOwnership = param["@_transfer-ownership"] as string | undefined;
|
|
336
|
+
const callerAllocates = param["@_caller-allocates"] as string | undefined;
|
|
337
|
+
return {
|
|
338
|
+
name: String(param["@_name"] ?? ""),
|
|
339
|
+
type: this.parseType((param.type ?? param.array) as Record<string, unknown> | undefined),
|
|
340
|
+
direction: (String(param["@_direction"] ?? "in") as "in" | "out" | "inout") || "in",
|
|
341
|
+
callerAllocates: callerAllocates === "1",
|
|
342
|
+
nullable: param["@_nullable"] === "1",
|
|
343
|
+
optional: param["@_allow-none"] === "1" || param["@_optional"] === "1",
|
|
344
|
+
scope: scope as "async" | "call" | "notified" | undefined,
|
|
345
|
+
closure: closure !== undefined ? parseInt(closure, 10) : undefined,
|
|
346
|
+
destroy: destroy !== undefined ? parseInt(destroy, 10) : undefined,
|
|
347
|
+
transferOwnership:
|
|
348
|
+
transferOwnership === "none" || transferOwnership === "full" || transferOwnership === "container"
|
|
349
|
+
? transferOwnership
|
|
350
|
+
: undefined,
|
|
351
|
+
doc: extractDoc(param),
|
|
352
|
+
};
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
private parseReturnType(returnValue: Record<string, unknown> | undefined): RawType {
|
|
356
|
+
if (!returnValue) {
|
|
357
|
+
return { name: "void" };
|
|
358
|
+
}
|
|
359
|
+
const type = this.parseType((returnValue.type ?? returnValue.array) as Record<string, unknown> | undefined);
|
|
360
|
+
const transferOwnership = returnValue["@_transfer-ownership"] as string | undefined;
|
|
361
|
+
if (transferOwnership === "none" || transferOwnership === "full" || transferOwnership === "container") {
|
|
362
|
+
type.transferOwnership = transferOwnership;
|
|
363
|
+
}
|
|
364
|
+
if (returnValue["@_nullable"] === "1") {
|
|
365
|
+
type.nullable = true;
|
|
366
|
+
}
|
|
367
|
+
return type;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
private parseType(typeNode: Record<string, unknown> | undefined): RawType {
|
|
371
|
+
if (!typeNode) {
|
|
372
|
+
return { name: "void" };
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
const typeName = typeNode["@_name"] ? String(typeNode["@_name"]) : undefined;
|
|
376
|
+
const cType = typeNode["@_c:type"] ? String(typeNode["@_c:type"]) : undefined;
|
|
377
|
+
|
|
378
|
+
const containerResult = this.parseGLibContainerType(typeName, typeNode, cType);
|
|
379
|
+
if (containerResult) {
|
|
380
|
+
return containerResult;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
if (typeName) {
|
|
384
|
+
return { name: typeName, cType };
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
const isArrayNode =
|
|
388
|
+
typeNode.type ||
|
|
389
|
+
typeNode["@_zero-terminated"] !== undefined ||
|
|
390
|
+
typeNode["@_fixed-size"] !== undefined ||
|
|
391
|
+
typeNode["@_length"] !== undefined;
|
|
392
|
+
|
|
393
|
+
if (isArrayNode) {
|
|
394
|
+
const lengthAttr = typeNode["@_length"];
|
|
395
|
+
const zeroTerminatedAttr = typeNode["@_zero-terminated"];
|
|
396
|
+
const fixedSizeAttr = typeNode["@_fixed-size"];
|
|
397
|
+
|
|
398
|
+
return {
|
|
399
|
+
name: "array",
|
|
400
|
+
isArray: true,
|
|
401
|
+
elementType: typeNode.type ? this.parseType(typeNode.type as Record<string, unknown>) : undefined,
|
|
402
|
+
sizeParamIndex: lengthAttr !== undefined ? Number(lengthAttr) : undefined,
|
|
403
|
+
zeroTerminated: zeroTerminatedAttr !== undefined ? zeroTerminatedAttr !== "0" : undefined,
|
|
404
|
+
fixedSize: fixedSizeAttr !== undefined ? Number(fixedSizeAttr) : undefined,
|
|
405
|
+
};
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
return { name: "void" };
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
private extractTypeParameters(typeNode: Record<string, unknown>): RawType[] {
|
|
412
|
+
const types: RawType[] = [];
|
|
413
|
+
const typeChildren = typeNode.type;
|
|
414
|
+
const arrayChildren = typeNode.array;
|
|
415
|
+
|
|
416
|
+
if (Array.isArray(typeChildren)) {
|
|
417
|
+
for (const child of typeChildren) {
|
|
418
|
+
types.push(this.parseType(child as Record<string, unknown>));
|
|
419
|
+
}
|
|
420
|
+
} else if (typeChildren) {
|
|
421
|
+
types.push(this.parseType(typeChildren as Record<string, unknown>));
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
if (Array.isArray(arrayChildren)) {
|
|
425
|
+
for (const child of arrayChildren) {
|
|
426
|
+
types.push(this.parseType(child as Record<string, unknown>));
|
|
427
|
+
}
|
|
428
|
+
} else if (arrayChildren) {
|
|
429
|
+
types.push(this.parseType(arrayChildren as Record<string, unknown>));
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
return types;
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
private parseGLibContainerType(
|
|
436
|
+
typeName: string | undefined,
|
|
437
|
+
typeNode: Record<string, unknown>,
|
|
438
|
+
cType: string | undefined,
|
|
439
|
+
): RawType | null {
|
|
440
|
+
if (typeName === "GLib.HashTable") {
|
|
441
|
+
const typeParams = this.extractTypeParameters(typeNode);
|
|
442
|
+
return {
|
|
443
|
+
name: "GLib.HashTable",
|
|
444
|
+
cType,
|
|
445
|
+
isArray: false,
|
|
446
|
+
containerType: "ghashtable" as ContainerType,
|
|
447
|
+
typeParameters: typeParams.length >= 2 ? typeParams : undefined,
|
|
448
|
+
elementType: typeParams[1],
|
|
449
|
+
};
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
if (typeName === "GLib.PtrArray" || typeName === "GLib.Array") {
|
|
453
|
+
const typeParams = this.extractTypeParameters(typeNode);
|
|
454
|
+
return {
|
|
455
|
+
name: typeName,
|
|
456
|
+
cType,
|
|
457
|
+
isArray: true,
|
|
458
|
+
containerType: (typeName === "GLib.PtrArray" ? "gptrarray" : "garray") as ContainerType,
|
|
459
|
+
typeParameters: typeParams.length > 0 ? typeParams : undefined,
|
|
460
|
+
elementType: typeParams[0],
|
|
461
|
+
};
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
if (typeName === "GLib.List" || typeName === "GLib.SList") {
|
|
465
|
+
const innerType = (typeNode.type ?? typeNode.array) as Record<string, unknown> | undefined;
|
|
466
|
+
const elementType = innerType ? this.parseType(innerType) : undefined;
|
|
467
|
+
return {
|
|
468
|
+
name: "array",
|
|
469
|
+
cType,
|
|
470
|
+
isArray: true,
|
|
471
|
+
containerType: (typeName === "GLib.List" ? "glist" : "gslist") as ContainerType,
|
|
472
|
+
typeParameters: elementType ? [elementType] : undefined,
|
|
473
|
+
elementType,
|
|
474
|
+
};
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
return null;
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
private parseProperties(properties: Record<string, unknown>[]): RawProperty[] {
|
|
481
|
+
if (!properties || !Array.isArray(properties)) {
|
|
482
|
+
return [];
|
|
483
|
+
}
|
|
484
|
+
return properties.map((prop) => {
|
|
485
|
+
let getter = prop["@_getter"] ? String(prop["@_getter"]) : undefined;
|
|
486
|
+
let setter = prop["@_setter"] ? String(prop["@_setter"]) : undefined;
|
|
487
|
+
|
|
488
|
+
const attributes = prop.attribute as Record<string, unknown>[] | Record<string, unknown> | undefined;
|
|
489
|
+
if (attributes) {
|
|
490
|
+
const attrList = Array.isArray(attributes) ? attributes : [attributes];
|
|
491
|
+
for (const attr of attrList) {
|
|
492
|
+
const attrName = attr["@_name"];
|
|
493
|
+
const attrValue = attr["@_value"];
|
|
494
|
+
if (attrName === "org.gtk.Property.get" && attrValue) {
|
|
495
|
+
getter = String(attrValue);
|
|
496
|
+
} else if (attrName === "org.gtk.Property.set" && attrValue) {
|
|
497
|
+
setter = String(attrValue);
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
return {
|
|
503
|
+
name: String(prop["@_name"] ?? ""),
|
|
504
|
+
type: this.parseType((prop.type ?? prop.array) as Record<string, unknown> | undefined),
|
|
505
|
+
readable: prop["@_readable"] !== "0",
|
|
506
|
+
writable: prop["@_writable"] === "1",
|
|
507
|
+
constructOnly: prop["@_construct-only"] === "1",
|
|
508
|
+
defaultValueRaw: prop["@_default-value"] !== undefined ? String(prop["@_default-value"]) : undefined,
|
|
509
|
+
getter,
|
|
510
|
+
setter,
|
|
511
|
+
doc: extractDoc(prop),
|
|
512
|
+
};
|
|
513
|
+
});
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
private parseSignals(signals: Record<string, unknown>[]): RawSignal[] {
|
|
517
|
+
if (!signals || !Array.isArray(signals)) {
|
|
518
|
+
return [];
|
|
519
|
+
}
|
|
520
|
+
return signals.map((signal) => {
|
|
521
|
+
const whenValue = String(signal["@_when"] ?? "last");
|
|
522
|
+
const validWhen = whenValue === "first" || whenValue === "last" || whenValue === "cleanup";
|
|
523
|
+
return {
|
|
524
|
+
name: String(signal["@_name"] ?? ""),
|
|
525
|
+
when: validWhen ? (whenValue as "first" | "last" | "cleanup") : "last",
|
|
526
|
+
returnType: signal["return-value"]
|
|
527
|
+
? this.parseReturnType(signal["return-value"] as Record<string, unknown>)
|
|
528
|
+
: undefined,
|
|
529
|
+
parameters:
|
|
530
|
+
signal.parameters && typeof signal.parameters === "object" && signal.parameters !== null
|
|
531
|
+
? this.parseParameters(signal.parameters as Record<string, unknown>)
|
|
532
|
+
: [],
|
|
533
|
+
doc: extractDoc(signal),
|
|
534
|
+
};
|
|
535
|
+
});
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
private parseRecords(records: Record<string, unknown>[]): RawRecord[] {
|
|
539
|
+
if (!records || !Array.isArray(records)) {
|
|
540
|
+
return [];
|
|
541
|
+
}
|
|
542
|
+
return records.map((record) => ({
|
|
543
|
+
name: String(record["@_name"] ?? ""),
|
|
544
|
+
cType: String(record["@_c:type"] ?? record["@_glib:type-name"] ?? ""),
|
|
545
|
+
opaque: record["@_opaque"] === "1",
|
|
546
|
+
disguised: record["@_disguised"] === "1",
|
|
547
|
+
glibTypeName: record["@_glib:type-name"] ? String(record["@_glib:type-name"]) : undefined,
|
|
548
|
+
glibGetType: record["@_glib:get-type"] ? String(record["@_glib:get-type"]) : undefined,
|
|
549
|
+
isGtypeStructFor: record["@_glib:is-gtype-struct-for"]
|
|
550
|
+
? String(record["@_glib:is-gtype-struct-for"])
|
|
551
|
+
: undefined,
|
|
552
|
+
copyFunction: record["@_copy-function"] ? String(record["@_copy-function"]) : undefined,
|
|
553
|
+
freeFunction: record["@_free-function"] ? String(record["@_free-function"]) : undefined,
|
|
554
|
+
fields: this.parseFields(ensureArray(record.field)),
|
|
555
|
+
methods: this.parseMethods(ensureArray(record.method)),
|
|
556
|
+
constructors: this.parseConstructors(ensureArray(record.constructor)),
|
|
557
|
+
functions: this.parseFunctions(ensureArray(record.function)),
|
|
558
|
+
doc: extractDoc(record),
|
|
559
|
+
}));
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
private parseFields(fields: Record<string, unknown>[]): RawField[] {
|
|
563
|
+
if (!fields || !Array.isArray(fields)) {
|
|
564
|
+
return [];
|
|
565
|
+
}
|
|
566
|
+
return fields
|
|
567
|
+
.filter((field) => {
|
|
568
|
+
const hasCallback = field.callback !== undefined;
|
|
569
|
+
return !hasCallback;
|
|
570
|
+
})
|
|
571
|
+
.map((field) => ({
|
|
572
|
+
name: String(field["@_name"] ?? ""),
|
|
573
|
+
type: this.parseType((field.type ?? field.array) as Record<string, unknown> | undefined),
|
|
574
|
+
writable: field["@_writable"] === "1",
|
|
575
|
+
readable: field["@_readable"] !== "0",
|
|
576
|
+
private: field["@_private"] === "1",
|
|
577
|
+
doc: extractDoc(field),
|
|
578
|
+
}));
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
private parseEnumerations(enumerations: Record<string, unknown>[]): RawEnumeration[] {
|
|
582
|
+
if (!enumerations || !Array.isArray(enumerations)) {
|
|
583
|
+
return [];
|
|
584
|
+
}
|
|
585
|
+
return enumerations.map((enumeration) => {
|
|
586
|
+
const glibGetType = enumeration["@_glib:get-type"];
|
|
587
|
+
return {
|
|
588
|
+
name: String(enumeration["@_name"] ?? ""),
|
|
589
|
+
cType: String(enumeration["@_c:type"] ?? ""),
|
|
590
|
+
members: this.parseEnumerationMembers(ensureArray(enumeration.member)),
|
|
591
|
+
glibGetType: typeof glibGetType === "string" ? glibGetType : undefined,
|
|
592
|
+
doc: extractDoc(enumeration),
|
|
593
|
+
};
|
|
594
|
+
});
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
private parseEnumerationMembers(members: Record<string, unknown>[]): RawEnumerationMember[] {
|
|
598
|
+
if (!members || !Array.isArray(members)) {
|
|
599
|
+
return [];
|
|
600
|
+
}
|
|
601
|
+
return members.map((member) => ({
|
|
602
|
+
name: String(member["@_name"] ?? ""),
|
|
603
|
+
value: String(member["@_value"] ?? ""),
|
|
604
|
+
cIdentifier: String(member["@_c:identifier"] ?? ""),
|
|
605
|
+
doc: extractDoc(member),
|
|
606
|
+
}));
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
private parseConstants(constants: Record<string, unknown>[]): RawConstant[] {
|
|
610
|
+
if (!constants || !Array.isArray(constants)) {
|
|
611
|
+
return [];
|
|
612
|
+
}
|
|
613
|
+
return constants.map((constant) => ({
|
|
614
|
+
name: String(constant["@_name"] ?? ""),
|
|
615
|
+
cType: String(constant["@_c:type"] ?? ""),
|
|
616
|
+
value: String(constant["@_value"] ?? ""),
|
|
617
|
+
type: this.parseType((constant.type ?? constant.array) as Record<string, unknown> | undefined),
|
|
618
|
+
doc: extractDoc(constant),
|
|
619
|
+
}));
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
private parseAliases(aliases: Record<string, unknown>[]): RawAlias[] {
|
|
623
|
+
if (!aliases || !Array.isArray(aliases)) {
|
|
624
|
+
return [];
|
|
625
|
+
}
|
|
626
|
+
return aliases.map((alias) => ({
|
|
627
|
+
name: String(alias["@_name"] ?? ""),
|
|
628
|
+
cType: String(alias["@_c:type"] ?? ""),
|
|
629
|
+
targetType: this.parseType(alias.type as Record<string, unknown> | undefined),
|
|
630
|
+
doc: extractDoc(alias),
|
|
631
|
+
}));
|
|
632
|
+
}
|
|
633
|
+
}
|