@gtkx/codegen 1.1.0 → 1.2.1

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 (169) hide show
  1. package/dist/analysis/descriptor-render.d.ts.map +1 -1
  2. package/dist/analysis/descriptor-render.js +14 -10
  3. package/dist/analysis/descriptor-render.js.map +1 -1
  4. package/dist/analysis/descriptor.d.ts +11 -5
  5. package/dist/analysis/descriptor.d.ts.map +1 -1
  6. package/dist/analysis/descriptor.js +25 -9
  7. package/dist/analysis/descriptor.js.map +1 -1
  8. package/dist/analysis/interface-conflicts.d.ts +3 -1
  9. package/dist/analysis/interface-conflicts.d.ts.map +1 -1
  10. package/dist/analysis/interface-conflicts.js +3 -8
  11. package/dist/analysis/interface-conflicts.js.map +1 -1
  12. package/dist/analysis/ts-type.d.ts +3 -2
  13. package/dist/analysis/ts-type.d.ts.map +1 -1
  14. package/dist/analysis/ts-type.js +16 -9
  15. package/dist/analysis/ts-type.js.map +1 -1
  16. package/dist/analysis/type-shape.d.ts +3 -2
  17. package/dist/analysis/type-shape.d.ts.map +1 -1
  18. package/dist/analysis/type-shape.js +2 -1
  19. package/dist/analysis/type-shape.js.map +1 -1
  20. package/dist/compile.d.ts +4 -5
  21. package/dist/compile.d.ts.map +1 -1
  22. package/dist/compile.js +56 -4
  23. package/dist/compile.js.map +1 -1
  24. package/dist/docs/api-reference.d.ts +2 -0
  25. package/dist/docs/api-reference.d.ts.map +1 -1
  26. package/dist/docs/api-reference.js +1 -1
  27. package/dist/docs/api-reference.js.map +1 -1
  28. package/dist/docs/pipeline.d.ts +1 -0
  29. package/dist/docs/pipeline.d.ts.map +1 -1
  30. package/dist/docs/pipeline.js +10 -3
  31. package/dist/docs/pipeline.js.map +1 -1
  32. package/dist/docs/symbol-page.d.ts.map +1 -1
  33. package/dist/docs/symbol-page.js +2 -4
  34. package/dist/docs/symbol-page.js.map +1 -1
  35. package/dist/fingerprint.d.ts +11 -5
  36. package/dist/fingerprint.d.ts.map +1 -1
  37. package/dist/fingerprint.js +22 -19
  38. package/dist/fingerprint.js.map +1 -1
  39. package/dist/gi.d.ts +1 -0
  40. package/dist/gi.d.ts.map +1 -1
  41. package/dist/gi.js +7 -2
  42. package/dist/gi.js.map +1 -1
  43. package/dist/gir/class.d.ts +7 -2
  44. package/dist/gir/class.d.ts.map +1 -1
  45. package/dist/gir/class.js +4 -1
  46. package/dist/gir/class.js.map +1 -1
  47. package/dist/gir/gir-path.d.ts +1 -1
  48. package/dist/gir/gir-path.js +1 -1
  49. package/dist/gir/gir-path.js.map +1 -1
  50. package/dist/gir/library.d.ts +5 -1
  51. package/dist/gir/library.d.ts.map +1 -1
  52. package/dist/gir/library.js +8 -1
  53. package/dist/gir/library.js.map +1 -1
  54. package/dist/gir/parse.d.ts.map +1 -1
  55. package/dist/gir/parse.js +4 -1
  56. package/dist/gir/parse.js.map +1 -1
  57. package/dist/gir/primitives.js +1 -1
  58. package/dist/gir/primitives.js.map +1 -1
  59. package/dist/khronos/args.d.ts.map +1 -1
  60. package/dist/khronos/args.js +8 -6
  61. package/dist/khronos/args.js.map +1 -1
  62. package/dist/khronos/render.d.ts.map +1 -1
  63. package/dist/khronos/render.js +8 -4
  64. package/dist/khronos/render.js.map +1 -1
  65. package/dist/runner.d.ts +2 -0
  66. package/dist/runner.d.ts.map +1 -1
  67. package/dist/runner.js +5 -3
  68. package/dist/runner.js.map +1 -1
  69. package/dist/staging.d.ts.map +1 -1
  70. package/dist/staging.js +1 -1
  71. package/dist/staging.js.map +1 -1
  72. package/dist/store/compile-store.d.ts +0 -1
  73. package/dist/store/compile-store.d.ts.map +1 -1
  74. package/dist/store/compile-store.js +7 -34
  75. package/dist/store/compile-store.js.map +1 -1
  76. package/dist/store/gi/callables.d.ts +2 -4
  77. package/dist/store/gi/callables.d.ts.map +1 -1
  78. package/dist/store/gi/callables.js +22 -7
  79. package/dist/store/gi/callables.js.map +1 -1
  80. package/dist/store/gi/class.d.ts.map +1 -1
  81. package/dist/store/gi/class.js +63 -17
  82. package/dist/store/gi/class.js.map +1 -1
  83. package/dist/store/gi/constant.js +1 -1
  84. package/dist/store/gi/constant.js.map +1 -1
  85. package/dist/store/gi/enum.d.ts.map +1 -1
  86. package/dist/store/gi/enum.js +2 -5
  87. package/dist/store/gi/enum.js.map +1 -1
  88. package/dist/store/gi/gtype-binding.d.ts +2 -1
  89. package/dist/store/gi/gtype-binding.d.ts.map +1 -1
  90. package/dist/store/gi/gtype-binding.js +13 -1
  91. package/dist/store/gi/gtype-binding.js.map +1 -1
  92. package/dist/store/gi/interface.js +4 -4
  93. package/dist/store/gi/interface.js.map +1 -1
  94. package/dist/store/gi/method.d.ts.map +1 -1
  95. package/dist/store/gi/method.js +4 -7
  96. package/dist/store/gi/method.js.map +1 -1
  97. package/dist/store/gi/property-accessor.d.ts +0 -3
  98. package/dist/store/gi/property-accessor.d.ts.map +1 -1
  99. package/dist/store/gi/property-accessor.js +15 -108
  100. package/dist/store/gi/property-accessor.js.map +1 -1
  101. package/dist/store/gi/record-constructor.d.ts.map +1 -1
  102. package/dist/store/gi/record-constructor.js +7 -5
  103. package/dist/store/gi/record-constructor.js.map +1 -1
  104. package/dist/store/gi/record-field-accessor.d.ts +3 -2
  105. package/dist/store/gi/record-field-accessor.d.ts.map +1 -1
  106. package/dist/store/gi/record-field-accessor.js +42 -12
  107. package/dist/store/gi/record-field-accessor.js.map +1 -1
  108. package/dist/store/gi/record.d.ts.map +1 -1
  109. package/dist/store/gi/record.js +5 -9
  110. package/dist/store/gi/record.js.map +1 -1
  111. package/dist/store/gi/return-wrap.js +1 -1
  112. package/dist/store/gi/return-wrap.js.map +1 -1
  113. package/dist/store/gi/signal.d.ts.map +1 -1
  114. package/dist/store/gi/signal.js +27 -6
  115. package/dist/store/gi/signal.js.map +1 -1
  116. package/dist/store/jsx-store.d.ts.map +1 -1
  117. package/dist/store/jsx-store.js +0 -1
  118. package/dist/store/jsx-store.js.map +1 -1
  119. package/dist/store/resolve-store.d.ts.map +1 -1
  120. package/dist/store/resolve-store.js +1 -0
  121. package/dist/store/resolve-store.js.map +1 -1
  122. package/dist/store/store-fs.d.ts +0 -1
  123. package/dist/store/store-fs.d.ts.map +1 -1
  124. package/dist/store/store-fs.js +2 -7
  125. package/dist/store/store-fs.js.map +1 -1
  126. package/dist/xml.d.ts +1 -0
  127. package/dist/xml.d.ts.map +1 -1
  128. package/dist/xml.js +28 -1
  129. package/dist/xml.js.map +1 -1
  130. package/overrides/gobject/object.ts.ejs +2 -0
  131. package/package.json +3 -3
  132. package/src/analysis/descriptor-render.ts +19 -10
  133. package/src/analysis/descriptor.ts +43 -9
  134. package/src/analysis/interface-conflicts.ts +6 -8
  135. package/src/analysis/ts-type.ts +30 -11
  136. package/src/analysis/type-shape.ts +5 -2
  137. package/src/compile.ts +87 -6
  138. package/src/docs/api-reference.ts +3 -1
  139. package/src/docs/pipeline.ts +13 -7
  140. package/src/docs/symbol-page.ts +1 -5
  141. package/src/fingerprint.ts +31 -38
  142. package/src/gi.ts +9 -2
  143. package/src/gir/class.ts +12 -3
  144. package/src/gir/gir-path.ts +1 -1
  145. package/src/gir/library.ts +9 -1
  146. package/src/gir/parse.ts +6 -1
  147. package/src/gir/primitives.ts +1 -1
  148. package/src/khronos/args.ts +9 -6
  149. package/src/khronos/render.ts +8 -6
  150. package/src/runner.ts +7 -3
  151. package/src/staging.ts +1 -3
  152. package/src/store/compile-store.ts +7 -40
  153. package/src/store/gi/callables.ts +30 -11
  154. package/src/store/gi/class.ts +96 -21
  155. package/src/store/gi/constant.ts +1 -1
  156. package/src/store/gi/enum.ts +2 -6
  157. package/src/store/gi/gtype-binding.ts +16 -1
  158. package/src/store/gi/interface.ts +4 -4
  159. package/src/store/gi/method.ts +4 -9
  160. package/src/store/gi/property-accessor.ts +22 -193
  161. package/src/store/gi/record-constructor.ts +7 -5
  162. package/src/store/gi/record-field-accessor.ts +65 -13
  163. package/src/store/gi/record.ts +13 -17
  164. package/src/store/gi/return-wrap.ts +1 -1
  165. package/src/store/gi/signal.ts +36 -13
  166. package/src/store/jsx-store.ts +0 -1
  167. package/src/store/resolve-store.ts +1 -0
  168. package/src/store/store-fs.ts +3 -9
  169. package/src/xml.ts +43 -1
@@ -1,4 +1,4 @@
1
- import { sanitizeTypeIdentifier, sourceStringLiteral, uniqBy } from "@gtkx/utils";
1
+ import { escapeIdentifierStart, sanitizeTypeIdentifier, sourceStringLiteral, uniqBy } from "@gtkx/utils";
2
2
  import type { EnumMember, GirEnum } from "../../gir/enum.js";
3
3
  import type { ModuleContext } from "../../writer/context.js";
4
4
  import { hasAnnotations } from "../../gir/annotations.js";
@@ -94,11 +94,7 @@ const appendEnumDeclaration = (context: ModuleContext, enumeration: GirEnum, mem
94
94
  });
95
95
  };
96
96
 
97
- const enumMemberKey = (name: string): string => {
98
- const upper = name.toUpperCase().replaceAll("-", "_");
99
-
100
- return /^\d/.test(upper) ? `_${upper}` : upper;
101
- };
97
+ const enumMemberKey = (name: string): string => escapeIdentifierStart(name.toUpperCase().replaceAll("-", "_"));
102
98
 
103
99
  const renderQuarkExpression = (context: ModuleContext, errorDomain: string): string => {
104
100
  if (context.namespace.name === "GLib") {
@@ -1,17 +1,32 @@
1
1
  import { sourceStringLiteral } from "@gtkx/utils";
2
2
  import type { ModuleContext } from "../../writer/context.js";
3
3
  import { PRIMITIVE_TS_TYPE } from "../../gir/primitives.js";
4
+ import { renderJsDoc } from "../../writer/doc.js";
5
+ import { renderBlock } from "../../writer/emit.js";
4
6
 
5
7
  type TypeSource = {
6
8
  glibGetType: string | undefined;
7
9
  glibTypeName: string | undefined;
8
10
  };
9
11
 
12
+ const GTYPE_NOTE = [
13
+ "The GType this class is registered under, read off the class it is accessed on rather than",
14
+ "inherited, so a subclass `registerClass` created reports its own type and one that never went",
15
+ "through `registerClass` reports the invalid type.",
16
+ ].join("\n");
17
+
10
18
  const gtypeTsType = (context: ModuleContext): string =>
11
19
  context.namespace.name === "GObject" ? "Type" : PRIMITIVE_TS_TYPE.gtype;
12
20
 
13
21
  const gtypeMemberDeclaration = (context: ModuleContext): string => `declare __type__: ${gtypeTsType(context)};`;
14
22
 
23
+ const gtypeStaticMember = (context: ModuleContext): string => {
24
+ context.addRuntimeImport("getClassType");
25
+ const block = renderBlock(`static get __gtype__(): ${gtypeTsType(context)}`, "return getClassType(this);");
26
+
27
+ return `${renderJsDoc(undefined, GTYPE_NOTE)}${block}`;
28
+ };
29
+
15
30
  const renderInternGtype = (context: ModuleContext, typeName: string | undefined): string | undefined => {
16
31
  if (typeName === undefined) {
17
32
  return undefined;
@@ -48,4 +63,4 @@ const renderSourceGtype = (context: ModuleContext, source: TypeSource): string |
48
63
  ? undefined
49
64
  : renderGtypeExpression(context, source.glibGetType, source.glibTypeName);
50
65
 
51
- export { gtypeTsType, gtypeMemberDeclaration, renderSourceGtype };
66
+ export { gtypeTsType, gtypeMemberDeclaration, gtypeStaticMember, renderSourceGtype };
@@ -21,7 +21,7 @@ import {
21
21
  renderStaticHead,
22
22
  } from "./callables.js";
23
23
  import { annotationSpec, getDoc } from "./doc-spec.js";
24
- import { renderSourceGtype } from "./gtype-binding.js";
24
+ import { gtypeStaticMember, renderSourceGtype } from "./gtype-binding.js";
25
25
  import { methodExportName } from "./method.js";
26
26
  import {
27
27
  type PropertyAccessorArgs,
@@ -336,11 +336,11 @@ const collectMethodMembers = (options: MethodMemberOptions): string[] => {
336
336
  };
337
337
 
338
338
  const collectPropertyMembers = (options: PropertyMemberOptions): string[] => {
339
- const { context, iface, scope, renderers, claimedNames } = options;
339
+ const { context, iface, renderers, claimedNames } = options;
340
340
  const members: string[] = [];
341
341
 
342
342
  for (const property of iface.properties) {
343
- const block = renderers.renderProperty({ context, property, claimedNames, methodByName: scope.methodByName });
343
+ const block = renderers.renderProperty({ context, property, claimedNames });
344
344
 
345
345
  if (block !== undefined) {
346
346
  members.push(block);
@@ -378,7 +378,7 @@ const renderInterfaceClass = (context: ModuleContext, options: InterfaceClassOpt
378
378
  const members: string[] = [];
379
379
 
380
380
  if (gtypeExpr !== undefined) {
381
- members.push(renderInterfaceHasInstance(context, className, gtypeExpr));
381
+ members.push(renderInterfaceHasInstance(context, className, gtypeExpr), gtypeStaticMember(context));
382
382
  }
383
383
 
384
384
  members.push(...renderStaticHead(context, callables, className), renderInterfaceBrand(context, implRef));
@@ -1,4 +1,4 @@
1
- import { camelCase, sourceStringLiteral } from "@gtkx/utils";
1
+ import { camelCase, escapeIdentifierStart, sourceStringLiteral } from "@gtkx/utils";
2
2
  import type { GirFunction } from "../../gir/function.js";
3
3
  import type { GirType } from "../../gir/type.js";
4
4
  import type { ModuleContext } from "../../writer/context.js";
@@ -19,7 +19,7 @@ import {
19
19
  inputParameters,
20
20
  parameterIdentifier,
21
21
  } from "../../analysis/param-structure.js";
22
- import { renderTsType } from "../../analysis/ts-type.js";
22
+ import { renderParameterTsType, renderTsType } from "../../analysis/ts-type.js";
23
23
  import { type GirParameter, isCallerAllocatedOut, isInoutParameter, isOutParameter } from "../../gir/parameter.js";
24
24
  import { hasUnknownArrayLength, type TypeId } from "../../gir/type-id.js";
25
25
  import { areClosuresInvoked } from "./closure-invocation.js";
@@ -86,12 +86,7 @@ type PlanArgsContext = {
86
86
  folded: Set<number>;
87
87
  };
88
88
 
89
- const memberName = (girName: string): string => {
90
- const camel = camelCase(girName);
91
-
92
- return /^\d/.test(camel) ? `_${camel}` : camel;
93
- };
94
-
89
+ const memberName = (girName: string): string => escapeIdentifierStart(camelCase(girName));
95
90
  const methodExportName = (fn: GirFunction): string => memberName(fn.name);
96
91
 
97
92
  const arrayLengthArgument = (source: GirParameter, sourceIndex: number): string => {
@@ -125,7 +120,7 @@ const parameterAnnotation = (context: ModuleContext, fn: GirFunction, parameter:
125
120
  return comparator;
126
121
  }
127
122
 
128
- const base = renderTsType(context, parameter.type, parameter.nullable);
123
+ const base = renderParameterTsType(context, parameter.type, parameter.nullable);
129
124
 
130
125
  return requiresClosureAnnotation(context, fn, parameter) ? closureAnnotation(context, base) : base;
131
126
  };
@@ -1,21 +1,14 @@
1
- import { camelCase, sourceStringLiteral, toCamelIdentifier } from "@gtkx/utils";
1
+ import { sourceStringLiteral, toCamelIdentifier } from "@gtkx/utils";
2
2
  import type { GirFunction } from "../../gir/function.js";
3
3
  import type { TypeId } from "../../gir/type-id.js";
4
4
  import type { ModuleContext } from "../../writer/context.js";
5
5
  import { renderDescriptor } from "../../analysis/descriptor-render.js";
6
- import { inputParameters } from "../../analysis/param-structure.js";
7
6
  import { renderTsType } from "../../analysis/ts-type.js";
8
7
  import { type GirProperty, isConstructableProperty } from "../../gir/property.js";
9
- import { comparisonContextFor } from "../../writer/comparison-context.js";
10
8
  import { renderBlock } from "../../writer/emit.js";
11
9
  import { isEmittableCallable } from "./callables.js";
12
10
  import { getDoc } from "./doc-spec.js";
13
- import { methodExportName, renderMethodReturnType } from "./method.js";
14
-
15
- type AccessorTypes = {
16
- readType: string;
17
- writeType: string;
18
- };
11
+ import { methodExportName } from "./method.js";
19
12
 
20
13
  type InheritedAccessorTypes = {
21
14
  readType: string | undefined;
@@ -29,24 +22,17 @@ type ResolvedAccessor = {
29
22
  writeType: string;
30
23
  hasGetter: boolean;
31
24
  isWritable: boolean;
32
- getterMember: string | undefined;
33
- setterMember: string | undefined;
34
25
  };
35
26
 
36
27
  type PropertyAccessorArgs = {
37
28
  context: ModuleContext;
38
29
  property: GirProperty;
39
30
  claimedNames: Set<string>;
40
- methodByName: Map<string, GirFunction>;
41
31
  inheritedTypes?: InheritedAccessorTypes | undefined;
42
32
  inheritedNames?: Set<string> | undefined;
43
33
  };
44
34
 
45
- type AccessorDelegate = { member: string; method: GirFunction };
46
- type AccessorDelegates = { getter: AccessorDelegate | undefined; setter: AccessorDelegate | undefined };
47
- type TypedDelegates = { types: AccessorTypes; delegates: AccessorDelegates };
48
-
49
- const NULLABLE_SUFFIX = " | null";
35
+ const NON_NULLABLE_KINDS: Set<string> = new Set(["primitive", "enum"]);
50
36
 
51
37
  const isNullablePropertyType = (context: ModuleContext, type: TypeId | undefined): boolean => {
52
38
  if (type === undefined) {
@@ -59,141 +45,19 @@ const isNullablePropertyType = (context: ModuleContext, type: TypeId | undefined
59
45
  return true;
60
46
  }
61
47
 
62
- if (resolved.kind === "primitive") {
63
- return false;
64
- }
65
-
66
- if (resolved.kind === "enum") {
67
- return false;
68
- }
69
-
70
48
  if (resolved.kind === "alias") {
71
49
  return isNullablePropertyType(context, resolved.value.target);
72
50
  }
73
51
 
74
- return true;
75
- };
76
-
77
- const resolveDelegate = (
78
- args: PropertyAccessorArgs,
79
- attribute: string | undefined,
80
- member: string | undefined,
81
- isDelegatable: (method: GirFunction) => boolean,
82
- ): AccessorDelegate | undefined => {
83
- const method = member !== undefined && attribute !== undefined ? args.methodByName.get(attribute) : undefined;
84
-
85
- if (member === undefined || method === undefined || !isDelegatable(method)) {
86
- return undefined;
87
- }
88
-
89
- return { member, method };
90
- };
91
-
92
- const resolveGetterDelegate = (args: PropertyAccessorArgs, jsName: string): AccessorDelegate | undefined => {
93
- const { context, property, claimedNames } = args;
94
- const member = delegateMember(property.getter, jsName, claimedNames);
95
-
96
- return resolveDelegate(
97
- args,
98
- property.getter,
99
- member,
100
- (method) =>
101
- inputParameters(context.library, method).length === 0 &&
102
- !renderMethodReturnType(context, method).startsWith("["),
103
- );
104
- };
105
-
106
- const resolveSetterDelegate = (
107
- args: PropertyAccessorArgs,
108
- jsName: string,
109
- isWritable: boolean,
110
- ): AccessorDelegate | undefined => {
111
- const { context, property, claimedNames } = args;
112
- const member = isWritable ? delegateMember(property.setter, jsName, claimedNames) : undefined;
113
-
114
- return resolveDelegate(
115
- args,
116
- property.setter,
117
- member,
118
- (method) => inputParameters(context.library, method).length === 1,
119
- );
120
- };
121
-
122
- const baseTypeName = (type: string): string =>
123
- type.endsWith(NULLABLE_SUFFIX) ? type.slice(0, -NULLABLE_SUFFIX.length) : type;
124
-
125
- const areDelegateBaseTypesAgreeing = (
126
- context: ModuleContext,
127
- getter: AccessorDelegate | undefined,
128
- setter: AccessorDelegate | undefined,
129
- ): boolean => {
130
- const setterParam = setter?.method.parameters[0];
131
-
132
- if (setterParam === undefined || getter === undefined) {
133
- return true;
134
- }
135
-
136
- const scratch = comparisonContextFor(context);
137
- const read = baseTypeName(renderMethodReturnType(scratch, getter.method));
138
- const written = baseTypeName(renderTsType(scratch, setterParam.type, false));
139
-
140
- return read === written;
141
- };
142
-
143
- const resolveDelegates = (args: PropertyAccessorArgs, jsName: string, isWritable: boolean): AccessorDelegates => {
144
- const getter = resolveGetterDelegate(args, jsName);
145
- const setter = resolveSetterDelegate(args, jsName, isWritable);
146
-
147
- if (areDelegateBaseTypesAgreeing(args.context, getter, setter)) {
148
- return { getter, setter };
149
- }
150
-
151
- return { getter, setter: undefined };
52
+ return !NON_NULLABLE_KINDS.has(resolved.kind);
152
53
  };
153
54
 
154
55
  const declaredPropertyType = (context: ModuleContext, property: GirProperty): string =>
155
- renderTsType(context, property.type, isNullablePropertyType(context, property.type));
156
-
157
- const resolveOwnTypes = (
158
- context: ModuleContext,
159
- property: GirProperty,
160
- delegates: AccessorDelegates,
161
- ): AccessorTypes => {
162
- const getterMethod = delegates.getter?.method;
163
- const setterParam = delegates.setter?.method.parameters[0];
164
-
165
- return {
166
- readType: getterMethod === undefined
167
- ? declaredPropertyType(context, property)
168
- : renderMethodReturnType(context, getterMethod),
169
- writeType: setterParam === undefined
170
- ? declaredPropertyType(context, property)
171
- : renderTsType(context, setterParam.type, setterParam.nullable || setterParam.optional),
172
- };
173
- };
174
-
175
- const isAssignableType = (source: string, target: string): boolean =>
176
- source === target || baseTypeName(target) === source;
177
-
178
- const agreeingDelegates = (
179
- delegates: AccessorDelegates,
180
- ownTypes: AccessorTypes,
181
- declaredTypes: AccessorTypes,
182
- ): AccessorDelegates => ({
183
- getter: isAssignableType(ownTypes.readType, declaredTypes.readType) ? delegates.getter : undefined,
184
- setter: isAssignableType(declaredTypes.writeType, ownTypes.writeType) ? delegates.setter : undefined,
185
- });
186
-
187
- const resolveTypedDelegates = (args: PropertyAccessorArgs, resolved: AccessorDelegates): TypedDelegates => {
188
- const ownTypes = resolveOwnTypes(args.context, args.property, resolved);
189
-
190
- const types = {
191
- readType: args.inheritedTypes?.readType ?? ownTypes.readType,
192
- writeType: args.inheritedTypes?.writeType ?? ownTypes.writeType,
193
- };
194
-
195
- return { types, delegates: agreeingDelegates(resolved, ownTypes, types) };
196
- };
56
+ renderTsType(
57
+ context,
58
+ property.type,
59
+ property.defaultValue === "NULL" || isNullablePropertyType(context, property.type),
60
+ );
197
61
 
198
62
  const isSkippedAccessor = (args: PropertyAccessorArgs, jsName: string): boolean =>
199
63
  !args.property.introspectable ||
@@ -201,31 +65,28 @@ const isSkippedAccessor = (args: PropertyAccessorArgs, jsName: string): boolean
201
65
  args.inheritedNames?.has(jsName) === true ||
202
66
  jsName === "constructor";
203
67
 
68
+ const isAccessorEmittable = (args: PropertyAccessorArgs, jsName: string, isWritable: boolean): boolean =>
69
+ !isSkippedAccessor(args, jsName) && (args.property.readable || isWritable);
70
+
204
71
  const resolveAccessor = (args: PropertyAccessorArgs): ResolvedAccessor | undefined => {
205
72
  const { property } = args;
206
73
  const jsName = toCamelIdentifier(property.name);
207
-
208
- if (isSkippedAccessor(args, jsName)) {
209
- return undefined;
210
- }
211
-
212
74
  const isWritable = isConstructableProperty(property) && !property.constructOnly;
213
- const { types, delegates } = resolveTypedDelegates(args, resolveDelegates(args, jsName, isWritable));
214
- const hasGetter = property.readable || delegates.getter !== undefined;
215
75
 
216
- if (!hasGetter && !isWritable) {
76
+ if (!isAccessorEmittable(args, jsName, isWritable)) {
217
77
  return undefined;
218
78
  }
219
79
 
80
+ const declared = declaredPropertyType(args.context, property);
81
+ const hasGetter = property.readable;
82
+
220
83
  return {
221
84
  property,
222
85
  jsName,
223
- readType: types.readType,
224
- writeType: types.writeType,
86
+ readType: args.inheritedTypes?.readType ?? declared,
87
+ writeType: args.inheritedTypes?.writeType ?? declared,
225
88
  hasGetter,
226
89
  isWritable,
227
- getterMember: delegates.getter?.member,
228
- setterMember: delegates.setter?.member,
229
90
  };
230
91
  };
231
92
 
@@ -234,7 +95,6 @@ const resolveOwnerAccessor = (
234
95
  property: GirProperty,
235
96
  methods: GirFunction[],
236
97
  ): ResolvedAccessor | undefined => {
237
- const methodByName: Map<string, GirFunction> = new Map();
238
98
  const claimedNames: Set<string> = new Set();
239
99
 
240
100
  for (const method of methods) {
@@ -242,11 +102,10 @@ const resolveOwnerAccessor = (
242
102
  continue;
243
103
  }
244
104
 
245
- methodByName.set(method.name, method);
246
105
  claimedNames.add(methodExportName(method));
247
106
  }
248
107
 
249
- return resolveAccessor({ context, property, claimedNames, methodByName });
108
+ return resolveAccessor({ context, property, claimedNames });
250
109
  };
251
110
 
252
111
  const resolveAccessorTypes = (
@@ -284,18 +143,15 @@ const renderResolvedPropertyAccessor = (
284
143
  property: GirProperty,
285
144
  accessor: ResolvedAccessor,
286
145
  ): string => {
287
- const { jsName, readType, writeType, hasGetter, isWritable, setterMember } = accessor;
146
+ const { jsName, readType, writeType, hasGetter, isWritable } = accessor;
288
147
  const blocks: string[] = [];
289
148
 
290
149
  if (hasGetter) {
291
- blocks.push(renderBlock(`get ${jsName}(): ${readType}`, renderGetterBody(context, property, accessor)));
150
+ blocks.push(renderBlock(`get ${jsName}(): ${readType}`, renderGenericGetBody(context, property, readType)));
292
151
  }
293
152
 
294
153
  if (isWritable) {
295
- const setterBody =
296
- setterMember === undefined ? renderGenericSetBody(context, property) : `this.${setterMember}(value);`;
297
-
298
- blocks.push(renderBlock(`set ${jsName}(value: ${writeType})`, setterBody));
154
+ blocks.push(renderBlock(`set ${jsName}(value: ${writeType})`, renderGenericSetBody(context, property)));
299
155
  }
300
156
 
301
157
  return `${propertyDoc(property)}${blocks.join("\n\n")}`;
@@ -347,33 +203,6 @@ const renderGenericSetBody = (context: ModuleContext, property: GirProperty): st
347
203
  return `setObjectProperty(this, ${sourceStringLiteral(property.name)}, ${descriptor}, value);`;
348
204
  };
349
205
 
350
- const renderGetterBody = (context: ModuleContext, property: GirProperty, accessor: ResolvedAccessor): string =>
351
- accessor.getterMember === undefined
352
- ? renderGenericGetBody(context, property, accessor.readType)
353
- : `return this.${accessor.getterMember}();`;
354
-
355
- const delegateMember = (
356
- attribute: string | undefined,
357
- accessorName: string,
358
- claimedNames: Set<string>,
359
- ): string | undefined => {
360
- if (attribute === undefined) {
361
- return undefined;
362
- }
363
-
364
- const member = camelCase(attribute);
365
-
366
- if (member === accessorName) {
367
- return undefined;
368
- }
369
-
370
- if (!claimedNames.has(member)) {
371
- return undefined;
372
- }
373
-
374
- return member;
375
- };
376
-
377
206
  export {
378
207
  propertyDoc,
379
208
  resolveAccessor,
@@ -11,7 +11,7 @@ import { renderBlock, renderBracedOrEmpty } from "../../writer/emit.js";
11
11
  import { type Callables, staticMembers } from "./callables.js";
12
12
  import { getDoc } from "./doc-spec.js";
13
13
  import { renderSourceGtype } from "./gtype-binding.js";
14
- import { emitFieldWrite, isEmittableField, isInlineField } from "./record-field-accessor.js";
14
+ import { emitFieldWrite, isEmittableField, isInlineField, isStorableFieldType } from "./record-field-accessor.js";
15
15
  import { computeRecordFieldSlots, type RecordFieldSlot } from "./record-layout.js";
16
16
  import { isConstructibleRecord } from "./value-marshalable.js";
17
17
 
@@ -25,7 +25,9 @@ type RecordConstructorSpec = {
25
25
  };
26
26
 
27
27
  const isWritableFieldSlot = (context: ModuleContext, entry: RecordFieldSlot): entry is WritableFieldSlot =>
28
- entry.field.writable && isEmittableField(context, entry.field);
28
+ entry.field.writable &&
29
+ isEmittableField(context, entry.field) &&
30
+ isStorableFieldType(context, entry.field.type);
29
31
 
30
32
  const renderRecordConstructorPropsInterface = (
31
33
  context: ModuleContext,
@@ -121,9 +123,9 @@ const renderRecordConstructor = (context: ModuleContext, spec: RecordConstructor
121
123
 
122
124
  const statements = [
123
125
  ...superCall,
124
- `const handle = alloc(${allocArgs(context, record, size).join(", ")});`,
126
+ `const __handle = alloc(${allocArgs(context, record, size).join(", ")});`,
125
127
  ...renderFieldWrites(context, slots),
126
- "setHandle(this, handle);",
128
+ "setHandle(this, __handle);",
127
129
  ];
128
130
 
129
131
  return renderBlock(`constructor(props: ${className}ConstructorProps = {})`, statements.join("\n"));
@@ -151,7 +153,7 @@ const renderFieldWrite = (context: ModuleContext, entry: WritableFieldSlot): str
151
153
  const write = emitFieldWrite(context, {
152
154
  descriptor,
153
155
  slot: entry.slot,
154
- targetExpr: "handle",
156
+ targetExpr: "__handle",
155
157
  valueExpr: `props.${name}`,
156
158
  });
157
159
 
@@ -9,6 +9,7 @@ import type { ModuleContext } from "../../writer/context.js";
9
9
  import { isInlineCallbackRef, renderDescriptor } from "../../analysis/descriptor-render.js";
10
10
  import { tStruct } from "../../analysis/descriptor.js";
11
11
  import { renderTsType } from "../../analysis/ts-type.js";
12
+ import { isUnboundedArray } from "../../analysis/type-shape.js";
12
13
  import { indent, renderBlock } from "../../writer/emit.js";
13
14
  import { getDoc } from "./doc-spec.js";
14
15
  import { bitMask, computeRecordFieldSlots, mergeBitfield, type RecordFieldSlot } from "./record-layout.js";
@@ -27,6 +28,10 @@ type AdmittedField = {
27
28
  jsName: string;
28
29
  };
29
30
 
31
+ type AccessibleField = AdmittedField & {
32
+ isSettable: boolean;
33
+ };
34
+
30
35
  type RecordFieldEntry = {
31
36
  jsName: string;
32
37
  tsType: string;
@@ -167,12 +172,49 @@ const admitField = (
167
172
  return { field, jsName };
168
173
  };
169
174
 
175
+ const isNullTerminatedArrayField = (context: ModuleContext, ref: TypeId): boolean => {
176
+ const type = context.library.typeFor(ref);
177
+
178
+ return type?.kind === "carray" && isUnboundedArray(type) && type.elementCType?.endsWith("*") === true;
179
+ };
180
+
181
+ const isAccessibleFieldType = (context: ModuleContext, ref: TypeId): boolean =>
182
+ isAccessorEligibleType(context, ref) || isNullTerminatedArrayField(context, ref);
183
+
184
+ const isStorableFieldType = (context: ModuleContext, ref: TypeId): boolean => {
185
+ const kind = context.library.typeFor(ref)?.kind;
186
+
187
+ return kind !== "carray" && kind !== "list" && kind !== "hashtable";
188
+ };
189
+
190
+ const admitAccessibleField = (
191
+ context: ModuleContext,
192
+ slot: RecordFieldSlot,
193
+ claimedNames: Set<string>,
194
+ siblingFields: GirField[],
195
+ ): AccessibleField | undefined => {
196
+ const admitted = admitField(context, slot, claimedNames);
197
+
198
+ if (admitted === undefined) {
199
+ return undefined;
200
+ }
201
+
202
+ const { field, jsName } = admitted;
203
+ const target: StructArrayTarget = { field, jsName, slot: slot.slot, siblingFields };
204
+ const hasStructArray = resolveStructArray(context, target) !== undefined;
205
+ const hasAccessor = hasStructArray || isAccessibleFieldType(context, field.type);
206
+ const isSettable = field.writable && (hasStructArray || isStorableFieldType(context, field.type));
207
+
208
+ return hasAccessor ? { ...admitted, isSettable } : undefined;
209
+ };
210
+
170
211
  const resolveRecordFieldEntry = (
171
212
  context: ModuleContext,
172
213
  slot: RecordFieldSlot,
173
214
  claimedNames: Set<string>,
215
+ siblingFields: GirField[],
174
216
  ): RecordFieldEntry | undefined => {
175
- const admitted = admitField(context, slot, claimedNames);
217
+ const admitted = admitAccessibleField(context, slot, claimedNames, siblingFields);
176
218
 
177
219
  if (admitted === undefined) {
178
220
  return undefined;
@@ -181,7 +223,7 @@ const resolveRecordFieldEntry = (
181
223
  return {
182
224
  jsName: admitted.jsName,
183
225
  tsType: renderTsType(context, admitted.field.type, false),
184
- isWritable: admitted.field.writable,
226
+ isWritable: admitted.isSettable,
185
227
  doc: admitted.field.doc,
186
228
  annotations: admitted.field.annotations,
187
229
  };
@@ -193,7 +235,7 @@ const renderRecordFieldAccessor = (
193
235
  claimedNames: Set<string>,
194
236
  siblingFields: GirField[],
195
237
  ): string | undefined => {
196
- const admitted = admitField(context, slot, claimedNames);
238
+ const admitted = admitAccessibleField(context, slot, claimedNames, siblingFields);
197
239
 
198
240
  if (admitted === undefined) {
199
241
  return undefined;
@@ -207,12 +249,6 @@ const renderRecordFieldAccessor = (
207
249
  return `${doc}${structArray}`;
208
250
  }
209
251
 
210
- if (!isAccessorEligibleType(context, field.type)) {
211
- const tsType = renderTsType(context, field.type, false);
212
-
213
- return `${doc}declare ${jsName}: ${tsType};`;
214
- }
215
-
216
252
  const descriptor = context.hoistDescriptor(
217
253
  renderDescriptor(context, field.type, "none", { isInline: isInlineField(context, field) }),
218
254
  );
@@ -230,7 +266,7 @@ const renderRecordFieldAccessor = (
230
266
 
231
267
  const blocks: string[] = [getterBlock(accessorOptions)];
232
268
 
233
- if (field.writable) {
269
+ if (admitted.isSettable) {
234
270
  blocks.push(setterBlock(accessorOptions));
235
271
  }
236
272
 
@@ -366,6 +402,21 @@ const visitInlineStructFields = (
366
402
  }
367
403
  };
368
404
 
405
+ const inlineLeafCount = (context: ModuleContext, fields: GirField[]): number => {
406
+ let count = 0;
407
+
408
+ visitInlineStructFields(context, fields, 0, {
409
+ leaf: () => {
410
+ count += 1;
411
+ },
412
+ nested: (_jsName, nested) => {
413
+ count += inlineLeafCount(context, nested);
414
+ },
415
+ });
416
+
417
+ return count;
418
+ };
419
+
369
420
  const elementAccessExpr = (descriptor: string, offset: number): string =>
370
421
  `read(__array, ${descriptor}, __base + ${String(offset)})`;
371
422
 
@@ -452,7 +503,7 @@ const structArrayGetterBlock = (options: StructArrayAccessorOptions): string =>
452
503
  };
453
504
 
454
505
  const structArraySetterStatements = (context: ModuleContext, options: StructArrayAccessorOptions): string => {
455
- const { elementDescriptor, offset, elementSize, elementFields } = options;
506
+ const { elementDescriptor, offset, elementSize, elementFields, lengthExpr } = options;
456
507
  const writes: string[] = [];
457
508
 
458
509
  appendElementWriteStatements(context, {
@@ -468,7 +519,7 @@ const structArraySetterStatements = (context: ModuleContext, options: StructArra
468
519
  `const __descriptor = ${elementDescriptor};`,
469
520
  `const __array = read(getHandle(this), __descriptor, ${String(offset)}) as ReturnType<typeof getHandle>;`,
470
521
  "for (const [__index, __element] of __value.entries()) {",
471
- indent(loop, 1),
522
+ indent(`if (__index >= ${lengthExpr}) {\n break;\n}\n\n${loop}`, 1),
472
523
  "}",
473
524
  `write(getHandle(this), __descriptor, ${String(offset)}, __array);`,
474
525
  ].join("\n");
@@ -543,7 +594,7 @@ const resolveStructArray = (context: ModuleContext, target: StructArrayTarget):
543
594
 
544
595
  const shape = resolveStructArrayShape(context, elements, siblingFields);
545
596
 
546
- if (shape === undefined) {
597
+ if (shape === undefined || inlineLeafCount(context, elements.elementFields) === 0) {
547
598
  return undefined;
548
599
  }
549
600
 
@@ -639,6 +690,7 @@ const setterBlock = (options: AccessorOptions): string =>
639
690
  export {
640
691
  isEmittableField,
641
692
  isInlineField,
693
+ isStorableFieldType,
642
694
  emitFieldWrite,
643
695
  resolveRecordFieldEntry,
644
696
  renderRecordFieldAccessor,