@gtkx/codegen 1.0.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (316) hide show
  1. package/README.md +1 -1
  2. package/dist/analysis/descriptor-render.d.ts +12 -15
  3. package/dist/analysis/descriptor-render.d.ts.map +1 -1
  4. package/dist/analysis/descriptor-render.js +48 -31
  5. package/dist/analysis/descriptor-render.js.map +1 -1
  6. package/dist/analysis/descriptor.d.ts +16 -7
  7. package/dist/analysis/descriptor.d.ts.map +1 -1
  8. package/dist/analysis/descriptor.js +15 -3
  9. package/dist/analysis/descriptor.js.map +1 -1
  10. package/dist/analysis/inheritance.d.ts +4 -2
  11. package/dist/analysis/inheritance.d.ts.map +1 -1
  12. package/dist/analysis/inheritance.js +80 -38
  13. package/dist/analysis/inheritance.js.map +1 -1
  14. package/dist/analysis/interface-conflicts.d.ts +23 -0
  15. package/dist/analysis/interface-conflicts.d.ts.map +1 -0
  16. package/dist/analysis/interface-conflicts.js +144 -0
  17. package/dist/analysis/interface-conflicts.js.map +1 -0
  18. package/dist/analysis/param-capability.d.ts.map +1 -1
  19. package/dist/analysis/param-capability.js +3 -4
  20. package/dist/analysis/param-capability.js.map +1 -1
  21. package/dist/analysis/param-structure.js +2 -2
  22. package/dist/analysis/param-structure.js.map +1 -1
  23. package/dist/analysis/ts-type.d.ts.map +1 -1
  24. package/dist/analysis/ts-type.js +10 -17
  25. package/dist/analysis/ts-type.js.map +1 -1
  26. package/dist/analysis/type-key.d.ts +5 -0
  27. package/dist/analysis/type-key.d.ts.map +1 -0
  28. package/dist/analysis/type-key.js +9 -0
  29. package/dist/analysis/type-key.js.map +1 -0
  30. package/dist/compile.d.ts +12 -3
  31. package/dist/compile.d.ts.map +1 -1
  32. package/dist/compile.js +73 -24
  33. package/dist/compile.js.map +1 -1
  34. package/dist/docs/api-reference.d.ts.map +1 -1
  35. package/dist/docs/api-reference.js +10 -9
  36. package/dist/docs/api-reference.js.map +1 -1
  37. package/dist/docs/element-page.d.ts.map +1 -1
  38. package/dist/docs/element-page.js +5 -3
  39. package/dist/docs/element-page.js.map +1 -1
  40. package/dist/docs/pipeline.d.ts.map +1 -1
  41. package/dist/docs/pipeline.js +85 -18
  42. package/dist/docs/pipeline.js.map +1 -1
  43. package/dist/docs/render.d.ts +2 -1
  44. package/dist/docs/render.d.ts.map +1 -1
  45. package/dist/docs/render.js +5 -4
  46. package/dist/docs/render.js.map +1 -1
  47. package/dist/docs/symbol-page.d.ts.map +1 -1
  48. package/dist/docs/symbol-page.js +9 -6
  49. package/dist/docs/symbol-page.js.map +1 -1
  50. package/dist/fingerprint.d.ts +12 -1
  51. package/dist/fingerprint.d.ts.map +1 -1
  52. package/dist/fingerprint.js +44 -28
  53. package/dist/fingerprint.js.map +1 -1
  54. package/dist/gi.d.ts.map +1 -1
  55. package/dist/gi.js +1 -0
  56. package/dist/gi.js.map +1 -1
  57. package/dist/gir/ancestry.d.ts +7 -1
  58. package/dist/gir/ancestry.d.ts.map +1 -1
  59. package/dist/gir/ancestry.js +9 -1
  60. package/dist/gir/ancestry.js.map +1 -1
  61. package/dist/gir/annotations.d.ts +1 -2
  62. package/dist/gir/annotations.d.ts.map +1 -1
  63. package/dist/gir/annotations.js +1 -1
  64. package/dist/gir/annotations.js.map +1 -1
  65. package/dist/gir/cursor-overrides.d.ts +8 -0
  66. package/dist/gir/cursor-overrides.d.ts.map +1 -0
  67. package/dist/gir/cursor-overrides.js +6 -0
  68. package/dist/gir/cursor-overrides.js.map +1 -0
  69. package/dist/gir/emittable.d.ts +7 -0
  70. package/dist/gir/emittable.d.ts.map +1 -0
  71. package/dist/gir/emittable.js +3 -0
  72. package/dist/gir/emittable.js.map +1 -0
  73. package/dist/gir/function.d.ts.map +1 -1
  74. package/dist/gir/function.js +28 -2
  75. package/dist/gir/function.js.map +1 -1
  76. package/dist/gir/library.js +1 -1
  77. package/dist/gir/library.js.map +1 -1
  78. package/dist/gir/namespace.d.ts +5 -1
  79. package/dist/gir/namespace.d.ts.map +1 -1
  80. package/dist/gir/namespace.js +4 -2
  81. package/dist/gir/namespace.js.map +1 -1
  82. package/dist/gir/parameter.d.ts +13 -1
  83. package/dist/gir/parameter.d.ts.map +1 -1
  84. package/dist/gir/parameter.js +1 -0
  85. package/dist/gir/parameter.js.map +1 -1
  86. package/dist/gir/parse.d.ts.map +1 -1
  87. package/dist/gir/parse.js +3 -6
  88. package/dist/gir/parse.js.map +1 -1
  89. package/dist/guards.d.ts +16 -0
  90. package/dist/guards.d.ts.map +1 -0
  91. package/dist/guards.js +11 -0
  92. package/dist/guards.js.map +1 -0
  93. package/dist/internal.d.ts +2 -0
  94. package/dist/internal.d.ts.map +1 -1
  95. package/dist/internal.js +2 -0
  96. package/dist/internal.js.map +1 -1
  97. package/dist/json.d.ts +3 -0
  98. package/dist/json.d.ts.map +1 -0
  99. package/dist/json.js +11 -0
  100. package/dist/json.js.map +1 -0
  101. package/dist/khronos/modules.d.ts.map +1 -1
  102. package/dist/khronos/modules.js +22 -7
  103. package/dist/khronos/modules.js.map +1 -1
  104. package/dist/khronos/parse.d.ts.map +1 -1
  105. package/dist/khronos/parse.js +3 -4
  106. package/dist/khronos/parse.js.map +1 -1
  107. package/dist/khronos/pipeline.d.ts +1 -1
  108. package/dist/khronos/pipeline.d.ts.map +1 -1
  109. package/dist/khronos/pipeline.js.map +1 -1
  110. package/dist/khronos/plan.d.ts +1 -7
  111. package/dist/khronos/plan.d.ts.map +1 -1
  112. package/dist/khronos/plan.js +1 -1
  113. package/dist/khronos/plan.js.map +1 -1
  114. package/dist/runner.d.ts +5 -1
  115. package/dist/runner.d.ts.map +1 -1
  116. package/dist/runner.js +16 -0
  117. package/dist/runner.js.map +1 -1
  118. package/dist/staging.d.ts +5 -0
  119. package/dist/staging.d.ts.map +1 -0
  120. package/dist/staging.js +57 -0
  121. package/dist/staging.js.map +1 -0
  122. package/dist/store/compile-store.d.ts.map +1 -1
  123. package/dist/store/compile-store.js +4 -2
  124. package/dist/store/compile-store.js.map +1 -1
  125. package/dist/store/gi/callables.d.ts +7 -2
  126. package/dist/store/gi/callables.d.ts.map +1 -1
  127. package/dist/store/gi/callables.js +25 -30
  128. package/dist/store/gi/callables.js.map +1 -1
  129. package/dist/store/gi/callback.d.ts.map +1 -1
  130. package/dist/store/gi/callback.js +9 -5
  131. package/dist/store/gi/callback.js.map +1 -1
  132. package/dist/store/gi/class.d.ts.map +1 -1
  133. package/dist/store/gi/class.js +59 -38
  134. package/dist/store/gi/class.js.map +1 -1
  135. package/dist/store/gi/companion.d.ts.map +1 -1
  136. package/dist/store/gi/companion.js +6 -6
  137. package/dist/store/gi/companion.js.map +1 -1
  138. package/dist/store/gi/constant.d.ts.map +1 -1
  139. package/dist/store/gi/constant.js +10 -2
  140. package/dist/store/gi/constant.js.map +1 -1
  141. package/dist/store/gi/constructor-props.js +1 -1
  142. package/dist/store/gi/constructor-props.js.map +1 -1
  143. package/dist/store/gi/enum.d.ts.map +1 -1
  144. package/dist/store/gi/enum.js +28 -8
  145. package/dist/store/gi/enum.js.map +1 -1
  146. package/dist/store/gi/function.d.ts +1 -3
  147. package/dist/store/gi/function.d.ts.map +1 -1
  148. package/dist/store/gi/function.js +24 -18
  149. package/dist/store/gi/function.js.map +1 -1
  150. package/dist/store/gi/gtype-binding.d.ts.map +1 -1
  151. package/dist/store/gi/gtype-binding.js +4 -1
  152. package/dist/store/gi/gtype-binding.js.map +1 -1
  153. package/dist/store/gi/interface.d.ts.map +1 -1
  154. package/dist/store/gi/interface.js +32 -17
  155. package/dist/store/gi/interface.js.map +1 -1
  156. package/dist/store/gi/method.d.ts +7 -6
  157. package/dist/store/gi/method.d.ts.map +1 -1
  158. package/dist/store/gi/method.js +13 -21
  159. package/dist/store/gi/method.js.map +1 -1
  160. package/dist/store/gi/param-marshal.d.ts +3 -2
  161. package/dist/store/gi/param-marshal.d.ts.map +1 -1
  162. package/dist/store/gi/param-marshal.js +50 -5
  163. package/dist/store/gi/param-marshal.js.map +1 -1
  164. package/dist/store/gi/pipeline.d.ts.map +1 -1
  165. package/dist/store/gi/pipeline.js +21 -10
  166. package/dist/store/gi/pipeline.js.map +1 -1
  167. package/dist/store/gi/properties.d.ts +2 -1
  168. package/dist/store/gi/properties.d.ts.map +1 -1
  169. package/dist/store/gi/properties.js +6 -3
  170. package/dist/store/gi/properties.js.map +1 -1
  171. package/dist/store/gi/property-accessor.d.ts +10 -5
  172. package/dist/store/gi/property-accessor.d.ts.map +1 -1
  173. package/dist/store/gi/property-accessor.js +95 -44
  174. package/dist/store/gi/property-accessor.js.map +1 -1
  175. package/dist/store/gi/record-constructor.d.ts +8 -1
  176. package/dist/store/gi/record-constructor.d.ts.map +1 -1
  177. package/dist/store/gi/record-constructor.js +31 -10
  178. package/dist/store/gi/record-constructor.js.map +1 -1
  179. package/dist/store/gi/record-field-accessor.d.ts.map +1 -1
  180. package/dist/store/gi/record-field-accessor.js +5 -1
  181. package/dist/store/gi/record-field-accessor.js.map +1 -1
  182. package/dist/store/gi/record-layout.d.ts +6 -1
  183. package/dist/store/gi/record-layout.d.ts.map +1 -1
  184. package/dist/store/gi/record-layout.js +8 -1
  185. package/dist/store/gi/record-layout.js.map +1 -1
  186. package/dist/store/gi/record.d.ts.map +1 -1
  187. package/dist/store/gi/record.js +19 -8
  188. package/dist/store/gi/record.js.map +1 -1
  189. package/dist/store/gi/registration.js +2 -2
  190. package/dist/store/gi/registration.js.map +1 -1
  191. package/dist/store/gi/signal.d.ts +2 -1
  192. package/dist/store/gi/signal.d.ts.map +1 -1
  193. package/dist/store/gi/signal.js +30 -24
  194. package/dist/store/gi/signal.js.map +1 -1
  195. package/dist/store/gi/value-marshalable.d.ts.map +1 -1
  196. package/dist/store/gi/value-marshalable.js +2 -1
  197. package/dist/store/gi/value-marshalable.js.map +1 -1
  198. package/dist/store/gi/vtable.d.ts +4 -1
  199. package/dist/store/gi/vtable.d.ts.map +1 -1
  200. package/dist/store/gi/vtable.js +60 -17
  201. package/dist/store/gi/vtable.js.map +1 -1
  202. package/dist/store/gi-store.d.ts +1 -0
  203. package/dist/store/gi-store.d.ts.map +1 -1
  204. package/dist/store/gi-store.js +15 -20
  205. package/dist/store/gi-store.js.map +1 -1
  206. package/dist/store/jsx/generated-elements.d.ts +2 -1
  207. package/dist/store/jsx/generated-elements.d.ts.map +1 -1
  208. package/dist/store/jsx/generated-elements.js +13 -7
  209. package/dist/store/jsx/generated-elements.js.map +1 -1
  210. package/dist/store/jsx/jsx.js +3 -3
  211. package/dist/store/jsx/jsx.js.map +1 -1
  212. package/dist/store/jsx/metadata.d.ts.map +1 -1
  213. package/dist/store/jsx/metadata.js +3 -1
  214. package/dist/store/jsx/metadata.js.map +1 -1
  215. package/dist/store/jsx-store.d.ts.map +1 -1
  216. package/dist/store/jsx-store.js +3 -3
  217. package/dist/store/jsx-store.js.map +1 -1
  218. package/dist/store/resolve-store.d.ts +14 -3
  219. package/dist/store/resolve-store.d.ts.map +1 -1
  220. package/dist/store/resolve-store.js +98 -15
  221. package/dist/store/resolve-store.js.map +1 -1
  222. package/dist/store/store-fs.d.ts +9 -6
  223. package/dist/store/store-fs.d.ts.map +1 -1
  224. package/dist/store/store-fs.js +59 -25
  225. package/dist/store/store-fs.js.map +1 -1
  226. package/dist/store/store-links.d.ts +3 -0
  227. package/dist/store/store-links.d.ts.map +1 -0
  228. package/dist/store/store-links.js +39 -0
  229. package/dist/store/store-links.js.map +1 -0
  230. package/dist/writer/comparison-context.d.ts +4 -0
  231. package/dist/writer/comparison-context.d.ts.map +1 -0
  232. package/dist/writer/comparison-context.js +13 -0
  233. package/dist/writer/comparison-context.js.map +1 -0
  234. package/dist/writer/context.d.ts +3 -3
  235. package/dist/writer/context.d.ts.map +1 -1
  236. package/dist/writer/context.js +11 -6
  237. package/dist/writer/context.js.map +1 -1
  238. package/dist/writer/module.d.ts +13 -5
  239. package/dist/writer/module.d.ts.map +1 -1
  240. package/dist/writer/module.js +68 -11
  241. package/dist/writer/module.js.map +1 -1
  242. package/dist/xml.d.ts +7 -1
  243. package/dist/xml.d.ts.map +1 -1
  244. package/dist/xml.js +43 -1
  245. package/dist/xml.js.map +1 -1
  246. package/package.json +14 -3
  247. package/src/analysis/descriptor-render.ts +90 -45
  248. package/src/analysis/descriptor.ts +35 -8
  249. package/src/analysis/inheritance.ts +134 -44
  250. package/src/analysis/interface-conflicts.ts +237 -0
  251. package/src/analysis/param-capability.ts +3 -6
  252. package/src/analysis/param-structure.ts +2 -2
  253. package/src/analysis/ts-type.ts +13 -17
  254. package/src/analysis/type-key.ts +14 -0
  255. package/src/compile.ts +110 -26
  256. package/src/docs/api-reference.ts +10 -9
  257. package/src/docs/element-page.ts +5 -2
  258. package/src/docs/pipeline.ts +129 -19
  259. package/src/docs/render.ts +7 -3
  260. package/src/docs/symbol-page.ts +12 -7
  261. package/src/fingerprint.ts +86 -31
  262. package/src/gi.ts +1 -0
  263. package/src/gir/ancestry.ts +17 -1
  264. package/src/gir/annotations.ts +1 -1
  265. package/src/gir/cursor-overrides.ts +12 -0
  266. package/src/gir/emittable.ts +8 -0
  267. package/src/gir/function.ts +45 -9
  268. package/src/gir/library.ts +1 -1
  269. package/src/gir/namespace.ts +8 -2
  270. package/src/gir/parameter.ts +15 -0
  271. package/src/gir/parse.ts +5 -7
  272. package/src/guards.ts +23 -0
  273. package/src/internal.ts +2 -0
  274. package/src/json.ts +11 -0
  275. package/src/khronos/modules.ts +25 -9
  276. package/src/khronos/parse.ts +3 -4
  277. package/src/khronos/pipeline.ts +1 -1
  278. package/src/khronos/plan.ts +0 -1
  279. package/src/runner.ts +19 -1
  280. package/src/staging.ts +69 -0
  281. package/src/store/compile-store.ts +6 -4
  282. package/src/store/gi/callables.ts +41 -33
  283. package/src/store/gi/callback.ts +9 -9
  284. package/src/store/gi/class.ts +79 -48
  285. package/src/store/gi/companion.ts +7 -6
  286. package/src/store/gi/constant.ts +12 -2
  287. package/src/store/gi/constructor-props.ts +1 -1
  288. package/src/store/gi/enum.ts +35 -21
  289. package/src/store/gi/function.ts +30 -24
  290. package/src/store/gi/gtype-binding.ts +7 -2
  291. package/src/store/gi/interface.ts +37 -18
  292. package/src/store/gi/method.ts +28 -29
  293. package/src/store/gi/param-marshal.ts +77 -6
  294. package/src/store/gi/pipeline.ts +25 -15
  295. package/src/store/gi/properties.ts +8 -4
  296. package/src/store/gi/property-accessor.ts +156 -73
  297. package/src/store/gi/record-constructor.ts +50 -18
  298. package/src/store/gi/record-field-accessor.ts +8 -1
  299. package/src/store/gi/record-layout.ts +11 -1
  300. package/src/store/gi/record.ts +20 -12
  301. package/src/store/gi/registration.ts +2 -2
  302. package/src/store/gi/signal.ts +36 -38
  303. package/src/store/gi/value-marshalable.ts +3 -1
  304. package/src/store/gi/vtable.ts +97 -20
  305. package/src/store/gi-store.ts +26 -31
  306. package/src/store/jsx/generated-elements.ts +17 -8
  307. package/src/store/jsx/jsx.ts +3 -3
  308. package/src/store/jsx/metadata.ts +4 -1
  309. package/src/store/jsx-store.ts +3 -3
  310. package/src/store/resolve-store.ts +133 -18
  311. package/src/store/store-fs.ts +87 -34
  312. package/src/store/store-links.ts +49 -0
  313. package/src/writer/comparison-context.ts +18 -0
  314. package/src/writer/context.ts +12 -6
  315. package/src/writer/module.ts +101 -14
  316. package/src/xml.ts +61 -1
@@ -1,13 +1,14 @@
1
1
  import { camelCase, sourceStringLiteral } from "@gtkx/utils";
2
2
  import type { GirClass } from "../../gir/class.js";
3
- import type { GirCallable, GirParameter } from "../../gir/parameter.js";
3
+ import type { GirCallable, GirParameter, GirReturnValue } from "../../gir/parameter.js";
4
4
  import type { GirProperty } from "../../gir/property.js";
5
5
  import type { ModuleContext } from "../../writer/context.js";
6
+ import type { Declaration } from "../../writer/module.js";
6
7
  import {
7
8
  isCellInout,
9
+ primaryReturnKind,
8
10
  renderDescriptor,
9
11
  renderParamDescriptor,
10
- shouldOmitPrimaryReturn,
11
12
  } from "../../analysis/descriptor-render.js";
12
13
  import { tCallback, tObject, tVoid } from "../../analysis/descriptor.js";
13
14
  import {
@@ -23,7 +24,7 @@ import { renderJsDoc } from "../../writer/doc.js";
23
24
  import { indent, renderBlock, renderBracedOrEmpty } from "../../writer/emit.js";
24
25
  import { parentCompanionRef } from "./companion.js";
25
26
  import { getDoc, handlerSpec } from "./doc-spec.js";
26
- import { isRecordCallerOut, isRecordInout } from "./param-marshal.js";
27
+ import { isAllocatableCallerOut, isRecordInout, renderCallerOutInstance } from "./param-marshal.js";
27
28
 
28
29
  type SignalMapSpec = {
29
30
  context: ModuleContext;
@@ -92,12 +93,18 @@ const renderSignalDeclarations = (
92
93
  klass: GirClass,
93
94
  className: string,
94
95
  isParentlessObjectSubclass: boolean,
95
- ): string[] => {
96
+ ): Declaration[] => {
96
97
  const base = { context, klass, className, isParentlessObjectSubclass };
97
98
 
98
- const declarations = [
99
- renderSignalMap({ ...base, suffix: SIGNALS_SUFFIX, renderEntry: renderSignalHandlerType }),
100
- renderSignalMap({ ...base, suffix: SIGNAL_EMIT_SUFFIX, renderEntry: renderSignalEmitEntry }),
99
+ const declarations: Declaration[] = [
100
+ {
101
+ name: `${className}${SIGNALS_SUFFIX}`,
102
+ code: renderSignalMap({ ...base, suffix: SIGNALS_SUFFIX, renderEntry: renderSignalHandlerType }),
103
+ },
104
+ {
105
+ name: `${className}${SIGNAL_EMIT_SUFFIX}`,
106
+ code: renderSignalMap({ ...base, suffix: SIGNAL_EMIT_SUFFIX, renderEntry: renderSignalEmitEntry }),
107
+ },
101
108
  ];
102
109
 
103
110
  if (
@@ -107,7 +114,7 @@ const renderSignalDeclarations = (
107
114
  klass.implements.length > 0)
108
115
  ) {
109
116
  const isRootObject = context.namespace.name === "GObject" && klass.name === "Object";
110
- declarations.push(renderSignalConnectInterface(className, isRootObject));
117
+ declarations.push({ name: className, code: renderSignalConnectInterface(className, isRootObject) });
111
118
  }
112
119
 
113
120
  return declarations;
@@ -263,7 +270,7 @@ const renderEmitArgLiteral = (options: EmitArgOptions): { literal: string; nextA
263
270
  }
264
271
 
265
272
  if (isCallerAllocatedOut(parameter)) {
266
- const value = renderCallerOutAllocation(context, parameter);
273
+ const value = renderCallerOutInstance(context, parameter);
267
274
 
268
275
  return {
269
276
  literal: `{ type: ${descriptor}, direction: "out", isCallerAllocated: true, value: ${value} }`,
@@ -283,10 +290,18 @@ const renderEmitArgLiteral = (options: EmitArgOptions): { literal: string; nextA
283
290
  return { literal: `{ type: ${descriptor}, value: args[${String(argIndex)}] }`, nextArgIndex: argIndex + 1 };
284
291
  };
285
292
 
293
+ const renderEmitReturnArg = (context: ModuleContext, returnValue: GirReturnValue): string => {
294
+ if (primaryReturnKind(context.library, returnValue) === "void") {
295
+ return "";
296
+ }
297
+
298
+ return `, ${renderDescriptor(context, returnValue.type, returnValue.transferOwnership)}`;
299
+ };
300
+
286
301
  const renderEmitCase = (context: ModuleContext, signal: GirCallable): string => {
287
302
  const params = nonVarargParameters(signal);
288
303
 
289
- if (params.some((parameter) => isCallerAllocatedOut(parameter) && !isRecordCallerOut(context, parameter))) {
304
+ if (params.some((parameter) => isCallerAllocatedOut(parameter) && !isAllocatableCallerOut(context, parameter))) {
290
305
  return renderUnsupportedEmitCase(signal);
291
306
  }
292
307
 
@@ -301,12 +316,7 @@ const renderEmitCase = (context: ModuleContext, signal: GirCallable): string =>
301
316
  return rendered.literal;
302
317
  });
303
318
 
304
- const isVoid = shouldOmitPrimaryReturn(context.library, signal.returnValue);
305
-
306
- const returnArg = isVoid
307
- ? ""
308
- : `, ${renderDescriptor(context, signal.returnValue.type, signal.returnValue.transferOwnership)}`;
309
-
319
+ const returnArg = renderEmitReturnArg(context, signal.returnValue);
310
320
  const body = `return emitSignal(this, sigName, [${argLiterals.join(", ")}]${returnArg});`;
311
321
 
312
322
  return renderBlock(`case ${sourceStringLiteral(signal.name)}:`, body);
@@ -321,16 +331,6 @@ const renderUnsupportedEmitCase = (signal: GirCallable): string => {
321
331
  );
322
332
  };
323
333
 
324
- const renderCallerOutAllocation = (context: ModuleContext, parameter: GirParameter): string => {
325
- const name = parameter.type === undefined ? undefined : context.library.nameFor(parameter.type);
326
-
327
- if (name === undefined) {
328
- throw new Error("renderCallerOutAllocation: expected a named caller-allocated out-parameter");
329
- }
330
-
331
- return `new ${context.qualify(name.namespaceName, name.typeName)}()`;
332
- };
333
-
334
334
  const renderCallback = (context: ModuleContext, signal: GirCallable): string => {
335
335
  const params = nonVarargParameters(signal);
336
336
 
@@ -338,21 +338,19 @@ const renderCallback = (context: ModuleContext, signal: GirCallable): string =>
338
338
  renderParamDescriptor(context, parameter, parameter.type, { argIndexOffset: 1 }),
339
339
  );
340
340
 
341
- const isVoid = shouldOmitPrimaryReturn(context.library, signal.returnValue);
342
-
343
- const returnDescriptor = isVoid
344
- ? tVoid
345
- : renderDescriptor(context, signal.returnValue.type, signal.returnValue.transferOwnership);
346
-
347
341
  const callbackArgs = [tObject("borrowed"), ...callbackParamDescriptors, tVoid];
348
342
  const userDataIndex = String(params.length + 1);
349
- const destroy = "hasDestroy: true, destroyKind: \"closureNotify\"";
350
343
 
351
- return tCallback(
352
- callbackArgs,
353
- returnDescriptor,
354
- `{ ${destroy}, hasUserData: true, userDataIndex: ${userDataIndex} }`,
355
- );
344
+ return tCallback({
345
+ argTypes: callbackArgs,
346
+ returns: renderDescriptor(context, signal.returnValue.type, signal.returnValue.transferOwnership),
347
+ options: [
348
+ "hasDestroy: true",
349
+ "destroyKind: \"closureNotify\"",
350
+ "hasUserData: true",
351
+ `userDataIndex: ${userDataIndex}`,
352
+ ],
353
+ });
356
354
  };
357
355
 
358
356
  const forEachInterfaceSignal = (
@@ -15,6 +15,8 @@ const hasOwnCopySemantics = (record: GirRecord): boolean =>
15
15
  record.glibGetType !== undefined ||
16
16
  ((record.glibRefFunc ?? record.copyFunc) !== undefined && (record.glibUnrefFunc ?? record.freeFunc) !== undefined);
17
17
 
18
+ const isOpaqueRecord = (record: GirRecord): boolean => record.opaque || record.disguised;
19
+
18
20
  const isValueSafeArray = (scope: Scope, type: Extract<GirType, { kind: "carray" }>): boolean =>
19
21
  type.fixedSize !== undefined && isValueSafeRef(scope, type.element, type.elementCType);
20
22
 
@@ -82,6 +84,6 @@ const isValueMarshalable = (context: ModuleContext, namespaceName: string, recor
82
84
  isValueSafeRecord({ context, seen: new Set<string>() }, namespaceName, record);
83
85
 
84
86
  const isConstructibleRecord = (context: ModuleContext, namespaceName: string, record: GirRecord): boolean =>
85
- hasOwnCopySemantics(record) || isValueMarshalable(context, namespaceName, record);
87
+ !isOpaqueRecord(record) && (hasOwnCopySemantics(record) || isValueMarshalable(context, namespaceName, record));
86
88
 
87
89
  export { isConstructibleRecord, isValueMarshalable };
@@ -1,7 +1,8 @@
1
- import { pascalCase, sourceStringLiteral } from "@gtkx/utils";
2
- import type { GirCallback } from "../../gir/callback.js";
1
+ import { pascalCase, sanitizeTypeIdentifier, sourceStringLiteral } from "@gtkx/utils";
3
2
  import type { GirClass, GirVirtualMethod } from "../../gir/class.js";
4
3
  import type { GirField } from "../../gir/field.js";
4
+ import type { GirFunction } from "../../gir/function.js";
5
+ import type { Library } from "../../gir/library.js";
5
6
  import type { GirParameter } from "../../gir/parameter.js";
6
7
  import type { GirRecord } from "../../gir/record.js";
7
8
  import type { TypeId } from "../../gir/type-id.js";
@@ -22,7 +23,9 @@ import {
22
23
  renderHandlerResultType,
23
24
  } from "../../analysis/param-structure.js";
24
25
  import { renderTsType } from "../../analysis/ts-type.js";
26
+ import { typeKey } from "../../analysis/type-key.js";
25
27
  import { ancestorChain, type ResolvedAncestor, resolveInterfaces } from "../../gir/ancestry.js";
28
+ import { callbackAsFunction, type GirCallback } from "../../gir/callback.js";
26
29
  import { renderJsDoc } from "../../writer/doc.js";
27
30
  import { renderBlock, renderBraced } from "../../writer/emit.js";
28
31
  import { handlerSpec, THROWS_TEXT } from "./doc-spec.js";
@@ -80,6 +83,7 @@ const UNCALLABLE_SLOT_KEYS: Set<string> = new Set([
80
83
  "vfuncSetProperty",
81
84
  ]);
82
85
 
86
+ const VTABLE_CACHE: WeakMap<GirClass, Vtable> = new WeakMap();
83
87
  const PROTECTED_SLOT_NOTE = "It is `protected`, so only a subclass chaining up reaches it.";
84
88
  const PUBLIC_SLOT_NOTE = "Calling it from anywhere else re-enters the slot on a live instance.";
85
89
 
@@ -193,7 +197,7 @@ const attachVirtualMethods = (slots: VtableSlot[], klass: GirClass): VtableSlot[
193
197
  return slots.map((slot) => ({ ...slot, vfunc: byKey.get(slot.key) }));
194
198
  };
195
199
 
196
- const collectVtable = (context: ModuleContext, namespaceName: string, klass: GirClass): Vtable | undefined => {
200
+ const buildVtable = (context: ModuleContext, namespaceName: string, klass: GirClass): Vtable | undefined => {
197
201
  const resolved = resolveVtableRecord(context, namespaceName, klass);
198
202
 
199
203
  if (resolved === undefined) {
@@ -210,10 +214,63 @@ const collectVtable = (context: ModuleContext, namespaceName: string, klass: Gir
210
214
  structName: pascalCase(resolved.typeStruct),
211
215
  kind: klass.isInterface ? "interface" : "class",
212
216
  vtableSize,
213
- slots: attachVirtualMethods(slots, klass),
217
+ slots: disambiguateSlots(context, namespaceName, klass, attachVirtualMethods(slots, klass)),
214
218
  };
215
219
  };
216
220
 
221
+ const collectVtable = (context: ModuleContext, namespaceName: string, klass: GirClass): Vtable | undefined => {
222
+ const cached = VTABLE_CACHE.get(klass);
223
+
224
+ if (cached !== undefined) {
225
+ return cached;
226
+ }
227
+
228
+ const vtable = buildVtable(context, namespaceName, klass);
229
+
230
+ if (vtable !== undefined) {
231
+ VTABLE_CACHE.set(klass, vtable);
232
+ }
233
+
234
+ return vtable;
235
+ };
236
+
237
+ const shadowedSlotKey = (klass: GirClass, slot: VtableSlot): string =>
238
+ `vfunc${sanitizeTypeIdentifier(klass.name)}${pascalCase(slot.field.name)}`;
239
+
240
+ const slotSignatureKey = (library: Library, slot: VtableSlot): string => {
241
+ const [, ...parameters] = slot.callback.parameters;
242
+
243
+ const parts = parameters.map(
244
+ (parameter) => `${typeKey(library, parameter.type)}/${parameter.direction}/${String(parameter.nullable)}`);
245
+
246
+ return `${parts.join(",")}->${typeKey(library, slot.callback.returnValue.type)}`;
247
+ };
248
+
249
+ const isShadowingSlot = (library: Library, slot: VtableSlot, inherited: VtableSlot[]): boolean =>
250
+ inherited.some(
251
+ (other) =>
252
+ other.key === slot.key &&
253
+ slotIdentity(other) !== slotIdentity(slot) &&
254
+ slotSignatureKey(library, other) !== slotSignatureKey(library, slot));
255
+
256
+ const disambiguateSlots = (
257
+ context: ModuleContext,
258
+ namespaceName: string,
259
+ klass: GirClass,
260
+ slots: VtableSlot[],
261
+ ): VtableSlot[] => {
262
+ const inherited = ancestorSlots(context, namespaceName, klass);
263
+
264
+ return slots.map((slot) =>
265
+ isShadowingSlot(context.library, slot, inherited) ? { ...slot, key: shadowedSlotKey(klass, slot) } : slot);
266
+ };
267
+
268
+ const ancestorSlots = (context: ModuleContext, namespaceName: string, klass: GirClass): VtableSlot[] => {
269
+ const [, ...ancestors] = [...ancestorChain(context.library, klass, namespaceName)];
270
+
271
+ return ancestors.flatMap((ancestor) => collectVtable(context, ancestor.namespaceName, ancestor.klass)?.slots ?? []);
272
+ };
273
+
217
274
  const vfuncMemberNames = (context: ModuleContext, namespaceName: string, klass: GirClass): string[] =>
218
275
  collectVtable(context, namespaceName, klass)?.slots.map((slot) => slot.key) ?? [];
219
276
 
@@ -231,21 +288,8 @@ const renderVfuncMetadata = (context: ModuleContext, klass: GirClass): string |
231
288
 
232
289
  const slotIdentity = (slot: VtableSlot): string => `${slot.key}:${String(slot.byteOffset)}`;
233
290
 
234
- const inheritedSlotIdentities = (context: ModuleContext, namespaceName: string, klass: GirClass): Set<string> => {
235
- const identities: Set<string> = new Set();
236
- const [, ...ancestors] = [...ancestorChain(context.library, klass, namespaceName)];
237
-
238
- for (const ancestor of ancestors) {
239
- const vtable = collectVtable(context, ancestor.namespaceName, ancestor.klass);
240
- const slots = vtable?.slots ?? [];
241
-
242
- for (const slot of slots) {
243
- identities.add(slotIdentity(slot));
244
- }
245
- }
246
-
247
- return identities;
248
- };
291
+ const inheritedSlotIdentities = (context: ModuleContext, namespaceName: string, klass: GirClass): Set<string> =>
292
+ new Set(ancestorSlots(context, namespaceName, klass).map((slot) => slotIdentity(slot)));
249
293
 
250
294
  const addInterfaceSlotKeys = (
251
295
  context: ModuleContext,
@@ -295,6 +339,37 @@ const protectedSlotKeys = (options: VfuncMembersOptions, slots: VtableSlot[]): S
295
339
  return new Set(slots.map((slot) => slot.key).filter((key) => !shared.has(key)));
296
340
  };
297
341
 
342
+ const vfuncCallables = (context: ModuleContext, namespaceName: string, klass: GirClass): Map<string, GirFunction> => {
343
+ const members: Map<string, GirFunction> = new Map();
344
+
345
+ for (const slot of callableVfuncSlots(context, namespaceName, klass)) {
346
+ const [, ...parameters] = slot.callback.parameters;
347
+ members.set(slot.key, { ...callbackAsFunction(slot.callback), parameters });
348
+ }
349
+
350
+ return members;
351
+ };
352
+
353
+ const addProtectedSlotKeys = (context: ModuleContext, ancestor: ResolvedAncestor, keys: Set<string>): void => {
354
+ const shared = implementedSlotKeys(context, ancestor.namespaceName, ancestor.klass);
355
+
356
+ for (const slot of callableVfuncSlots(context, ancestor.namespaceName, ancestor.klass)) {
357
+ if (!shared.has(slot.key)) {
358
+ keys.add(slot.key);
359
+ }
360
+ }
361
+ };
362
+
363
+ const protectedChainSlotKeys = (context: ModuleContext, klass: GirClass): Set<string> => {
364
+ const keys: Set<string> = new Set();
365
+
366
+ for (const ancestor of ancestorChain(context.library, klass, context.namespace.name)) {
367
+ addProtectedSlotKeys(context, ancestor, keys);
368
+ }
369
+
370
+ return keys;
371
+ };
372
+
298
373
  const callableVfuncSlots = (context: ModuleContext, namespaceName: string, klass: GirClass): VtableSlot[] => {
299
374
  const vtable = collectVtable(context, namespaceName, klass);
300
375
 
@@ -339,7 +414,7 @@ const vfuncEntries = (context: ModuleContext, namespaceName: string, klass: GirC
339
414
 
340
415
  const renderVfuncMembers = (options: VfuncMembersOptions): string[] => {
341
416
  const { context, klass, mode } = options;
342
- const ownerRef = pascalCase(klass.name);
417
+ const ownerRef = sanitizeTypeIdentifier(klass.name);
343
418
  const slots = callableVfuncSlots(context, context.namespace.name, klass);
344
419
 
345
420
  if (slots.length === 0) {
@@ -468,7 +543,9 @@ const renderVtableSlotDescriptor = (context: ModuleContext, vtable: Vtable, slot
468
543
 
469
544
  export {
470
545
  hasCallableVfuncSlots,
546
+ protectedChainSlotKeys,
471
547
  renderVfuncMembers,
548
+ vfuncCallables,
472
549
  renderVfuncMetadata,
473
550
  vfuncEntries,
474
551
  vfuncMemberNames,
@@ -1,64 +1,59 @@
1
1
  import { existsSync, readdirSync, readFileSync } from "node:fs";
2
2
  import { dirname, join, resolve } from "node:path";
3
3
  import { fileURLToPath } from "node:url";
4
+ import type { SourceModule } from "../compile.js";
4
5
  import { FINGERPRINT_FILENAME, type GiFingerprint } from "../fingerprint.js";
5
- import { buildManifest, type StoreOptions, subpathExport, writeStore } from "./store-fs.js";
6
+ import { buildManifest, namespaceBarrel, type StoreOptions, subpathExport, writeStore } from "./store-fs.js";
6
7
 
7
8
  type GiNamespaceInput = {
8
9
  directory: string;
9
10
  rawSource: string;
10
- };
11
-
12
- type CollectedFile = {
13
- fileName: string;
14
- source: string;
11
+ girFile: string;
15
12
  };
16
13
 
17
14
  const OVERRIDES_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), "..", "..", "overrides");
18
15
 
19
- const overrideFiles = (directory: string): string[] => {
16
+ const overrideFiles = (directory: string): SourceModule[] => {
20
17
  const dir = join(OVERRIDES_ROOT, directory);
21
18
 
22
19
  if (!existsSync(dir)) {
23
20
  return [];
24
21
  }
25
22
 
26
- return readdirSync(dir).filter((name) => name.endsWith(".ejs") && name !== "index.ts.ejs");
23
+ return readdirSync(dir)
24
+ .filter((name) => name.endsWith(".ejs") && name !== "index.ts.ejs")
25
+ .map((name) => overrideModule(`${directory}/overrides/${name.replace(/\.ejs$/, "")}`, join(dir, name)));
27
26
  };
28
27
 
29
- const barrelSource = (directory: string): string => {
28
+ const overrideModule = (fileName: string, overridePath: string): SourceModule => ({
29
+ fileName,
30
+ source: readFileSync(overridePath, "utf8"),
31
+ origin: overridePath,
32
+ });
33
+
34
+ const barrelFile = (directory: string, girFile: string): SourceModule => {
35
+ const barrel = namespaceBarrel(directory);
30
36
  const overrideIndex = join(OVERRIDES_ROOT, directory, "index.ts.ejs");
31
37
 
32
38
  if (!existsSync(overrideIndex)) {
33
- return `export * from "./${directory}.js";\n`;
39
+ return { ...barrel, origin: girFile };
34
40
  }
35
41
 
36
- return readFileSync(overrideIndex, "utf8");
42
+ return overrideModule(barrel.fileName, overrideIndex);
37
43
  };
38
44
 
39
45
  const collectStoreSources = (
40
46
  namespaces: GiNamespaceInput[],
41
- ): { collected: CollectedFile[]; exportsMap: Record<string, unknown> } => {
47
+ ): { collected: SourceModule[]; exportsMap: Record<string, unknown> } => {
42
48
  const exportsMap: Record<string, unknown> = {};
43
- const collected: CollectedFile[] = [];
44
-
45
- for (const { directory, rawSource } of namespaces) {
46
- collected.push({
47
- fileName: `${directory}/${directory}.ts`,
48
- source: rawSource,
49
- });
50
-
51
- for (const file of overrideFiles(directory)) {
52
- collected.push({
53
- fileName: `${directory}/overrides/${file.replace(/\.ejs$/, "")}`,
54
- source: readFileSync(join(OVERRIDES_ROOT, directory, file), "utf8"),
55
- });
56
- }
57
-
58
- collected.push({
59
- fileName: `${directory}/index.ts`,
60
- source: barrelSource(directory),
61
- });
49
+ const collected: SourceModule[] = [];
50
+
51
+ for (const { directory, rawSource, girFile } of namespaces) {
52
+ collected.push(
53
+ { fileName: `${directory}/${directory}.ts`, source: rawSource, origin: girFile },
54
+ ...overrideFiles(directory),
55
+ barrelFile(directory, girFile),
56
+ );
62
57
 
63
58
  exportsMap[`./${directory}`] = subpathExport(`${directory}/index`);
64
59
  }
@@ -1,7 +1,8 @@
1
- import { existsSync, readFileSync } from "node:fs";
2
1
  import { join } from "node:path";
3
2
  import type { GlibNamedClass } from "./intrinsic-elements.js";
4
3
  import { namespaceDirectory } from "../../gir/namespace.js";
4
+ import { arrayGuard, hasFields, isBoolean, isString } from "../../guards.js";
5
+ import { readJsonFile } from "../../json.js";
5
6
 
6
7
  /** One element the `@gtkx/jsx` store binds. */
7
8
  type GeneratedElement = {
@@ -33,22 +34,30 @@ const collectGeneratedElements = (intrinsicElements: GlibNamedClass[]): Generate
33
34
  const renderGeneratedElements = (elements: GeneratedElement[]): string =>
34
35
  `${JSON.stringify(elements, null, 2)}\n`;
35
36
 
37
+ const isGeneratedElement = (value: unknown): value is GeneratedElement =>
38
+ hasFields<GeneratedElement>(value, {
39
+ namespace: isString,
40
+ directory: isString,
41
+ glibName: isString,
42
+ isMountable: isBoolean,
43
+ });
44
+
45
+ const isGeneratedInventory = (value: unknown): value is GeneratedElement[] =>
46
+ arrayGuard(isGeneratedElement)(value);
47
+
36
48
  /**
37
49
  * Reads the inventory of elements the `@gtkx/jsx` store binds, written into the store by codegen. Answers
38
50
  * "what does this project's JSX layer cover" without loading the store, which resolves `virtual:` specifiers
39
51
  * that only exist inside a Vite or Vitest build.
40
52
  *
41
53
  * @param jsxStoreDir The jsx store directory, as given by `resolveStore(projectRoot).jsx.storeDir`.
42
- * @returns Every bound element, sorted by GLib type name, or an empty array when the store has no inventory.
54
+ * @returns Every bound element, sorted by GLib type name, or an empty array when the store holds no inventory
55
+ * codegen wrote, which covers an absent, unreadable, unparseable and structurally foreign store file alike.
43
56
  */
44
57
  const readGeneratedElements = (jsxStoreDir: string): GeneratedElement[] => {
45
- const path = join(jsxStoreDir, ELEMENTS_FILENAME);
46
-
47
- if (!existsSync(path)) {
48
- return [];
49
- }
58
+ const parsed = readJsonFile(join(jsxStoreDir, ELEMENTS_FILENAME));
50
59
 
51
- return JSON.parse(readFileSync(path, "utf8")) as GeneratedElement[];
60
+ return isGeneratedInventory(parsed) ? parsed : [];
52
61
  };
53
62
 
54
63
  export {
@@ -1,4 +1,4 @@
1
- import { sourceStringLiteral } from "@gtkx/utils";
1
+ import { sanitizeTypeIdentifier, sourceStringLiteral } from "@gtkx/utils";
2
2
  import type { GirClass } from "../../gir/class.js";
3
3
  import type { Library } from "../../gir/library.js";
4
4
  import type { GirNamespace } from "../../gir/namespace.js";
@@ -285,7 +285,7 @@ const renderInterfacePropsBlock = (
285
285
 
286
286
  const extendsClause = prerequisiteExtends.length === 0 ? "" : ` extends ${prerequisiteExtends.join(", ")}`;
287
287
  addGiNamespace(imports, iface.namespace.name, giNamespaceAlias(iface.namespace.name));
288
- const selfDefault = `${giNamespaceAlias(iface.namespace.name)}.${iface.klass.name}`;
288
+ const selfDefault = `${giNamespaceAlias(iface.namespace.name)}.${sanitizeTypeIdentifier(iface.klass.name)}`;
289
289
  const signature = `export interface ${glib}Props<Self = ${selfDefault}>${extendsClause}`;
290
290
  const block = `${getDoc(iface.klass)}${renderBlock(signature, ownerLines.join("\n"))}`;
291
291
 
@@ -324,7 +324,7 @@ const renderPropBlock = (
324
324
  const ownerLines = dedupePropLines(["ref?: Ref<Self | null> | undefined;", ...propLines]);
325
325
  const extendsList = resolveElementExtends(library, entry, context);
326
326
  const extendsClause = extendsList.length === 0 ? "" : ` extends ${extendsList.join(", ")}`;
327
- const selfDefault = `${giNamespaceAlias(entry.namespace.name)}.${entry.klass.name}`;
327
+ const selfDefault = `${giNamespaceAlias(entry.namespace.name)}.${sanitizeTypeIdentifier(entry.klass.name)}`;
328
328
 
329
329
  const block = `${getDoc(entry.klass)}${renderBlock(
330
330
  `export interface ${entry.glibName}Props<Self = ${selfDefault}>${extendsClause}`,
@@ -66,12 +66,15 @@ const generateMetadata = (library: Library): string => {
66
66
 
67
67
  const collectIntrinsicElements = (library: Library): IntrinsicElementEntry[] => {
68
68
  const entries: IntrinsicElementEntry[] = [];
69
+ const seen: Set<string> = new Set();
69
70
 
70
71
  for (const { glibName, klass, namespace } of iterateClassesWithGlibName(library)) {
71
- if (!isIntrinsicElementClass(klass, namespace, library)) {
72
+ if (!isIntrinsicElementClass(klass, namespace, library) || seen.has(glibName)) {
72
73
  continue;
73
74
  }
74
75
 
76
+ seen.add(glibName);
77
+
75
78
  const sources: GirClass[] = [
76
79
  klass,
77
80
  ...implementedInterfaces(klass, namespace, library).map((entry) => entry.klass),
@@ -1,6 +1,6 @@
1
1
  import type { JsxNamespaceFile } from "./jsx/pipeline.js";
2
2
  import type { RawFile } from "./store-fs.js";
3
- import { buildManifest, type StoreOptions, subpathExport, writeStore } from "./store-fs.js";
3
+ import { buildManifest, namespaceBarrel, type StoreOptions, subpathExport, writeStore } from "./store-fs.js";
4
4
 
5
5
  const writeJsxStore = (
6
6
  options: StoreOptions,
@@ -15,8 +15,8 @@ const writeJsxStore = (
15
15
  const files = [{ fileName: "metadata.ts", source: metadata }];
16
16
 
17
17
  for (const { directory, source } of namespaces) {
18
- files.push({ fileName: `${directory}/${directory}.tsx`, source });
19
- exportsMap[`./${directory}`] = subpathExport(`${directory}/${directory}`);
18
+ files.push({ fileName: `${directory}/${directory}.tsx`, source }, namespaceBarrel(directory));
19
+ exportsMap[`./${directory}`] = subpathExport(`${directory}/index`);
20
20
  }
21
21
 
22
22
  writeStore({