@gtkx/gir 0.1.18 → 0.1.20

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.
Files changed (3) hide show
  1. package/package.json +1 -1
  2. package/src/parser.ts +28 -1
  3. package/src/types.ts +119 -37
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gtkx/gir",
3
- "version": "0.1.18",
3
+ "version": "0.1.20",
4
4
  "description": "GObject Introspection file parser for GTKX",
5
5
  "keywords": [
6
6
  "gtk",
package/src/parser.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { XMLParser } from "fast-xml-parser";
2
2
  import type {
3
+ GirCallback,
3
4
  GirClass,
4
5
  GirConstructor,
5
6
  GirEnumeration,
@@ -23,6 +24,7 @@ const ARRAY_ELEMENT_PATHS = new Set<string>([
23
24
  "namespace.enumeration",
24
25
  "namespace.bitfield",
25
26
  "namespace.record",
27
+ "namespace.callback",
26
28
  "namespace.class.method",
27
29
  "namespace.class.constructor",
28
30
  "namespace.class.function",
@@ -49,6 +51,7 @@ const ARRAY_ELEMENT_PATHS = new Set<string>([
49
51
  "namespace.record.method.parameters.parameter",
50
52
  "namespace.record.constructor.parameters.parameter",
51
53
  "namespace.record.function.parameters.parameter",
54
+ "namespace.callback.parameters.parameter",
52
55
  ]);
53
56
 
54
57
  const extractDoc = (node: Record<string, unknown>): string | undefined => {
@@ -105,15 +108,38 @@ export class GirParser {
105
108
  enumerations: this.parseEnumerations(namespace.enumeration ?? []),
106
109
  bitfields: this.parseEnumerations(namespace.bitfield ?? []),
107
110
  records: this.parseRecords(namespace.record ?? []),
111
+ callbacks: this.parseCallbacks(namespace.callback ?? []),
108
112
  };
109
113
  }
110
114
 
115
+ private parseCallbacks(callbacks: Record<string, unknown>[]): GirCallback[] {
116
+ if (!callbacks || !Array.isArray(callbacks)) {
117
+ return [];
118
+ }
119
+ return callbacks
120
+ .filter((cb) => cb["@_introspectable"] !== "0")
121
+ .map((cb) => ({
122
+ name: String(cb["@_name"] ?? ""),
123
+ cType: String(cb["@_c:type"] ?? ""),
124
+ returnType: this.parseReturnType(cb["return-value"] as Record<string, unknown> | undefined),
125
+ parameters: this.parseParameters(
126
+ (cb.parameters && typeof cb.parameters === "object" && cb.parameters !== null
127
+ ? cb.parameters
128
+ : {}) as Record<string, unknown>,
129
+ ),
130
+ doc: extractDoc(cb),
131
+ }));
132
+ }
133
+
111
134
  private parseClasses(classes: Record<string, unknown>[]): GirClass[] {
112
135
  return classes.map((cls) => ({
113
136
  name: String(cls["@_name"] ?? ""),
114
137
  cType: String(cls["@_c:type"] ?? cls["@_glib:type-name"] ?? ""),
115
138
  parent: String(cls["@_parent"] ?? ""),
116
139
  abstract: cls["@_abstract"] === "1",
140
+ glibTypeName: cls["@_glib:type-name"] ? String(cls["@_glib:type-name"]) : undefined,
141
+ glibGetType: cls["@_glib:get-type"] ? String(cls["@_glib:get-type"]) : undefined,
142
+ cSymbolPrefix: cls["@_c:symbol-prefix"] ? String(cls["@_c:symbol-prefix"]) : undefined,
117
143
  implements: this.parseImplements(
118
144
  cls.implements as Record<string, unknown>[] | Record<string, unknown> | undefined,
119
145
  ),
@@ -193,6 +219,7 @@ export class GirParser {
193
219
  ? ctor.parameters
194
220
  : {}) as Record<string, unknown>,
195
221
  ),
222
+ throws: ctor["@_throws"] === "1",
196
223
  doc: extractDoc(ctor),
197
224
  }));
198
225
  }
@@ -265,7 +292,7 @@ export class GirParser {
265
292
  const typeName = typeNode["@_name"] ? String(typeNode["@_name"]) : undefined;
266
293
 
267
294
  if (typeName === "GLib.List" || typeName === "GLib.SList") {
268
- const innerType = typeNode.type as Record<string, unknown> | undefined;
295
+ const innerType = (typeNode.type ?? typeNode.array) as Record<string, unknown> | undefined;
269
296
  return {
270
297
  name: "array",
271
298
  cType: typeNode["@_c:type"] ? String(typeNode["@_c:type"]) : undefined,
package/src/types.ts CHANGED
@@ -22,10 +22,28 @@ export interface GirNamespace {
22
22
  bitfields: GirEnumeration[];
23
23
  /** All records (structs) defined in this namespace. */
24
24
  records: GirRecord[];
25
+ /** All callback types defined in this namespace. */
26
+ callbacks: GirCallback[];
25
27
  /** Documentation for the namespace. */
26
28
  doc?: string;
27
29
  }
28
30
 
31
+ /**
32
+ * Represents a GIR callback type definition.
33
+ */
34
+ export interface GirCallback {
35
+ /** The callback name. */
36
+ name: string;
37
+ /** The C type name. */
38
+ cType: string;
39
+ /** The return type. */
40
+ returnType: GirType;
41
+ /** The callback parameters. */
42
+ parameters: GirParameter[];
43
+ /** Documentation for the callback. */
44
+ doc?: string;
45
+ }
46
+
29
47
  /**
30
48
  * Represents a GIR interface definition.
31
49
  */
@@ -56,6 +74,12 @@ export interface GirClass {
56
74
  parent?: string;
57
75
  /** Whether this is an abstract class. */
58
76
  abstract?: boolean;
77
+ /** The GLib type name. */
78
+ glibTypeName?: string;
79
+ /** The GLib get-type function. */
80
+ glibGetType?: string;
81
+ /** The C symbol prefix for this class. */
82
+ cSymbolPrefix?: string;
59
83
  /** List of interface names this class implements. */
60
84
  implements: string[];
61
85
  /** Methods defined on this class. */
@@ -148,6 +172,8 @@ export interface GirConstructor {
148
172
  returnType: GirType;
149
173
  /** The constructor parameters. */
150
174
  parameters: GirParameter[];
175
+ /** Whether this constructor can throw a GError. */
176
+ throws?: boolean;
151
177
  /** Documentation for the constructor. */
152
178
  doc?: string;
153
179
  }
@@ -354,7 +380,7 @@ export const registerEnumsFromNamespace = (typeMapper: TypeMapper, namespace: Gi
354
380
 
355
381
  type TypeMapping = { ts: string; ffi: FfiTypeDescriptor };
356
382
 
357
- export type TypeKind = "class" | "interface" | "enum" | "record";
383
+ export type TypeKind = "class" | "interface" | "enum" | "record" | "callback";
358
384
 
359
385
  export interface RegisteredType {
360
386
  kind: TypeKind;
@@ -418,6 +444,16 @@ export class TypeRegistry {
418
444
  });
419
445
  }
420
446
 
447
+ registerCallback(namespace: string, name: string): void {
448
+ const transformedName = toPascalCase(name);
449
+ this.types.set(`${namespace}.${name}`, {
450
+ kind: "callback",
451
+ name,
452
+ namespace,
453
+ transformedName,
454
+ });
455
+ }
456
+
421
457
  resolve(qualifiedName: string): RegisteredType | undefined {
422
458
  return this.types.get(qualifiedName);
423
459
  }
@@ -445,16 +481,13 @@ export class TypeRegistry {
445
481
  registry.registerEnum(ns.name, bitfield.name);
446
482
  }
447
483
  for (const record of ns.records) {
448
- if (
449
- record.glibTypeName &&
450
- !record.disguised &&
451
- !record.name.endsWith("Class") &&
452
- !record.name.endsWith("Private") &&
453
- !record.name.endsWith("Iface")
454
- ) {
484
+ if (record.glibTypeName && !record.disguised) {
455
485
  registry.registerRecord(ns.name, record.name, record.glibTypeName);
456
486
  }
457
487
  }
488
+ for (const callback of ns.callbacks) {
489
+ registry.registerCallback(ns.name, callback.name);
490
+ }
458
491
  }
459
492
  return registry;
460
493
  }
@@ -462,6 +495,65 @@ export class TypeRegistry {
462
495
 
463
496
  const STRING_TYPES = new Set(["utf8", "filename"]);
464
497
 
498
+ const POINTER_TYPE: TypeMapping = { ts: "number", ffi: { type: "int", size: 64, unsigned: true } };
499
+
500
+ const C_TYPE_MAP = new Map<string, TypeMapping>([
501
+ ["void", { ts: "void", ffi: { type: "undefined" } }],
502
+ ["gboolean", { ts: "boolean", ffi: { type: "boolean" } }],
503
+ ["gchar", { ts: "number", ffi: { type: "int", size: 8, unsigned: false } }],
504
+ ["guchar", { ts: "number", ffi: { type: "int", size: 8, unsigned: true } }],
505
+ ["gint", { ts: "number", ffi: { type: "int", size: 32, unsigned: false } }],
506
+ ["guint", { ts: "number", ffi: { type: "int", size: 32, unsigned: true } }],
507
+ ["gshort", { ts: "number", ffi: { type: "int", size: 16, unsigned: false } }],
508
+ ["gushort", { ts: "number", ffi: { type: "int", size: 16, unsigned: true } }],
509
+ ["glong", { ts: "number", ffi: { type: "int", size: 64, unsigned: false } }],
510
+ ["gulong", { ts: "number", ffi: { type: "int", size: 64, unsigned: true } }],
511
+ ["gint8", { ts: "number", ffi: { type: "int", size: 8, unsigned: false } }],
512
+ ["guint8", { ts: "number", ffi: { type: "int", size: 8, unsigned: true } }],
513
+ ["gint16", { ts: "number", ffi: { type: "int", size: 16, unsigned: false } }],
514
+ ["guint16", { ts: "number", ffi: { type: "int", size: 16, unsigned: true } }],
515
+ ["gint32", { ts: "number", ffi: { type: "int", size: 32, unsigned: false } }],
516
+ ["guint32", { ts: "number", ffi: { type: "int", size: 32, unsigned: true } }],
517
+ ["gint64", { ts: "number", ffi: { type: "int", size: 64, unsigned: false } }],
518
+ ["guint64", { ts: "number", ffi: { type: "int", size: 64, unsigned: true } }],
519
+ ["gfloat", { ts: "number", ffi: { type: "float", size: 32 } }],
520
+ ["gdouble", { ts: "number", ffi: { type: "float", size: 64 } }],
521
+ ["gsize", { ts: "number", ffi: { type: "int", size: 64, unsigned: true } }],
522
+ ["gssize", { ts: "number", ffi: { type: "int", size: 64, unsigned: false } }],
523
+ ["goffset", { ts: "number", ffi: { type: "int", size: 64, unsigned: false } }],
524
+ ["int", { ts: "number", ffi: { type: "int", size: 32, unsigned: false } }],
525
+ ["unsigned int", { ts: "number", ffi: { type: "int", size: 32, unsigned: true } }],
526
+ ["long", { ts: "number", ffi: { type: "int", size: 64, unsigned: false } }],
527
+ ["unsigned long", { ts: "number", ffi: { type: "int", size: 64, unsigned: true } }],
528
+ ["double", { ts: "number", ffi: { type: "float", size: 64 } }],
529
+ ["float", { ts: "number", ffi: { type: "float", size: 32 } }],
530
+ ["size_t", { ts: "number", ffi: { type: "int", size: 64, unsigned: true } }],
531
+ ["ssize_t", { ts: "number", ffi: { type: "int", size: 64, unsigned: false } }],
532
+ ["GType", { ts: "number", ffi: { type: "int", size: 64, unsigned: true } }],
533
+ ["GQuark", { ts: "number", ffi: { type: "int", size: 32, unsigned: true } }],
534
+ ]);
535
+
536
+ const mapCType = (cType: string | undefined): TypeMapping => {
537
+ if (!cType) {
538
+ return POINTER_TYPE;
539
+ }
540
+
541
+ if (cType.endsWith("*")) {
542
+ return POINTER_TYPE;
543
+ }
544
+
545
+ const mapped = C_TYPE_MAP.get(cType);
546
+ if (mapped) {
547
+ return mapped;
548
+ }
549
+
550
+ if (cType.startsWith("const ")) {
551
+ return mapCType(cType.slice(6));
552
+ }
553
+
554
+ return POINTER_TYPE;
555
+ };
556
+
465
557
  const BASIC_TYPE_MAP = new Map<string, TypeMapping>([
466
558
  ["gboolean", { ts: "boolean", ffi: { type: "boolean" } }],
467
559
  ["gchar", { ts: "number", ffi: { type: "int", size: 8, unsigned: false } }],
@@ -522,16 +614,6 @@ const BASIC_TYPE_MAP = new Map<string, TypeMapping>([
522
614
  ["FreeFunc", { ts: "number", ffi: { type: "int", size: 64, unsigned: true } }],
523
615
  ]);
524
616
 
525
- const LIBRARY_MAP: Record<string, string> = {
526
- Gtk: "libgtk-4.so.1",
527
- GObject: "libgobject-2.0.so.0",
528
- GLib: "libglib-2.0.so.0",
529
- Gio: "libgio-2.0.so.0",
530
- GdkPixbuf: "libgdk_pixbuf-2.0.so.0",
531
- Pango: "libpango-1.0.so.0",
532
- Cairo: "libcairo.so.2",
533
- };
534
-
535
617
  export interface ExternalTypeUsage {
536
618
  namespace: string;
537
619
  name: string;
@@ -767,6 +849,9 @@ export class TypeMapper {
767
849
  externalType: isExternal ? externalType : undefined,
768
850
  };
769
851
  }
852
+ if (registered.kind === "callback") {
853
+ return POINTER_TYPE;
854
+ }
770
855
  return {
771
856
  ts: qualifiedName,
772
857
  ffi: { type: "gobject", borrowed: isReturn },
@@ -775,10 +860,7 @@ export class TypeMapper {
775
860
  };
776
861
  }
777
862
  }
778
- return {
779
- ts: "unknown",
780
- ffi: { type: "gobject", borrowed: isReturn },
781
- };
863
+ return mapCType(girType.cType);
782
864
  }
783
865
 
784
866
  if (this.typeRegistry && this.currentNamespace) {
@@ -820,6 +902,9 @@ export class TypeMapper {
820
902
  kind: registered.kind,
821
903
  };
822
904
  }
905
+ if (registered.kind === "callback") {
906
+ return POINTER_TYPE;
907
+ }
823
908
  return {
824
909
  ts: qualifiedName,
825
910
  ffi: { type: "gobject", borrowed: isReturn },
@@ -829,10 +914,17 @@ export class TypeMapper {
829
914
  }
830
915
  }
831
916
 
832
- return {
833
- ts: "unknown",
834
- ffi: { type: "gobject", borrowed: isReturn },
835
- };
917
+ return mapCType(girType.cType);
918
+ }
919
+
920
+ isCallback(typeName: string): boolean {
921
+ if (this.typeRegistry) {
922
+ const resolved = this.currentNamespace
923
+ ? this.typeRegistry.resolveInNamespace(typeName, this.currentNamespace)
924
+ : this.typeRegistry.resolve(typeName);
925
+ return resolved?.kind === "callback";
926
+ }
927
+ return false;
836
928
  }
837
929
 
838
930
  /**
@@ -901,7 +993,7 @@ export class TypeMapper {
901
993
  };
902
994
  }
903
995
 
904
- if (param.type.name === "GLib.Closure" || param.type.name.endsWith("Func")) {
996
+ if (param.type.name === "GLib.Closure" || this.isCallback(param.type.name)) {
905
997
  return {
906
998
  ts: "(...args: unknown[]) => unknown",
907
999
  ffi: { type: "callback" },
@@ -920,7 +1012,6 @@ export class TypeMapper {
920
1012
  */
921
1013
  isClosureTarget(paramIndex: number, allParams: GirParameter[]): boolean {
922
1014
  const trampolineCallbacks = ["Gio.AsyncReadyCallback", "Gtk.DrawingAreaDrawFunc", "DrawingAreaDrawFunc"];
923
- // Check if this param is a closure (user_data) or destroy target for a trampoline callback
924
1015
  return allParams.some(
925
1016
  (p) => trampolineCallbacks.includes(p.type.name) && (p.closure === paramIndex || p.destroy === paramIndex),
926
1017
  );
@@ -934,13 +1025,4 @@ export class TypeMapper {
934
1025
  isNullable(param: GirParameter): boolean {
935
1026
  return param.nullable === true || param.optional === true;
936
1027
  }
937
-
938
- /**
939
- * Gets the shared library name for a namespace.
940
- * @param namespace - The namespace name
941
- * @returns The shared library file name
942
- */
943
- getLibraryName(namespace: string): string {
944
- return LIBRARY_MAP[namespace] ?? `lib${namespace.toLowerCase()}.so`;
945
- }
946
1028
  }