@typecad/cuttlefish 1.0.0-alpha.13 → 1.0.0-alpha.15

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 (343) hide show
  1. package/README.md +82 -79
  2. package/assets/editor-extensions/typecad-debug/README.md +141 -0
  3. package/assets/editor-extensions/typecad-debug/out/extension.js +347 -0
  4. package/assets/editor-extensions/typecad-debug/out/extension.js.map +1 -0
  5. package/assets/editor-extensions/typecad-debug/package.json +76 -0
  6. package/assets/editor-extensions/typecad-ui/LICENSE +27 -0
  7. package/assets/editor-extensions/typecad-ui/README.md +52 -0
  8. package/assets/editor-extensions/typecad-ui/file-icon-dark.png +0 -0
  9. package/assets/editor-extensions/typecad-ui/file-icon-light.png +0 -0
  10. package/assets/editor-extensions/typecad-ui/icon.png +0 -0
  11. package/assets/editor-extensions/typecad-ui/language-configuration.json +43 -0
  12. package/assets/editor-extensions/typecad-ui/package.json +54 -0
  13. package/assets/editor-extensions/typecad-ui/snippets/typecad-ui.json +80 -0
  14. package/assets/editor-extensions/typecad-ui/syntaxes/markdown-ui.json +45 -0
  15. package/assets/editor-extensions/typecad-ui/syntaxes/typecad-ui.tmLanguage.json +1269 -0
  16. package/dist/api/config.d.ts +30 -27
  17. package/dist/api/schema/types.d.ts +20 -3
  18. package/dist/api/shared/async-runtime-static.d.ts +1 -1
  19. package/dist/api/shared/async-runtime-static.js +5 -5
  20. package/dist/api/shared/async-types.d.ts +3 -2
  21. package/dist/api/shared/board-catalog.d.ts +2 -0
  22. package/dist/api/shared/board-catalog.js +6 -0
  23. package/dist/api/shared/board-resolver.d.ts +4 -7
  24. package/dist/api/shared/board-resolver.js +2 -1
  25. package/dist/api/shared/coop-scheduler.d.ts +1 -1
  26. package/dist/api/shared/coop-scheduler.js +1 -1
  27. package/dist/api/shared/cpp-type-ir.d.ts +1 -1
  28. package/dist/api/shared/cpp-type-ir.js +1 -1
  29. package/dist/api/shared/display-profile.d.ts +1 -1
  30. package/dist/api/shared/display-profile.js +3 -5
  31. package/dist/api/shared/framework-manifest-registry.d.ts +1 -1
  32. package/dist/api/shared/framework-manifest-registry.js +0 -1
  33. package/dist/api/shared/framework-manifest.d.ts +65 -507
  34. package/dist/api/shared/framework-manifest.js +6 -11
  35. package/dist/api/shared/hal-op-ir.d.ts +294 -685
  36. package/dist/api/shared/hal-op-ir.js +31 -65
  37. package/dist/api/shared/index.d.ts +0 -4
  38. package/dist/api/shared/index.js +1 -7
  39. package/dist/api/shared/ir-core.d.ts +6 -0
  40. package/dist/api/shared/ir-declarations.d.ts +4 -0
  41. package/dist/api/shared/ir.d.ts +1 -3
  42. package/dist/api/shared/platform-strategy.d.ts +14 -37
  43. package/dist/api/shared/polyfill-helper-registry.js +0 -5
  44. package/dist/api/shared/polyfill-types.js +0 -27
  45. package/dist/api/shared/promise-runtime.js +3 -3
  46. package/dist/api/shared/spdx-licenses.d.ts +12 -10
  47. package/dist/api/shared/spdx-licenses.js +1 -1
  48. package/dist/api/shared/toolchain-types.d.ts +1 -1
  49. package/dist/api/shared/toolchain-types.js +3 -3
  50. package/dist/api/shared/types.d.ts +0 -3
  51. package/dist/api/shared/validate-framework-manifest.js +13 -33
  52. package/dist/board-catalog/dts-reader.d.ts +220 -0
  53. package/dist/board-catalog/dts-reader.js +1052 -0
  54. package/dist/board-catalog/index.d.ts +11 -0
  55. package/dist/board-catalog/index.js +12 -0
  56. package/dist/board-catalog/pinconfig.d.ts +48 -0
  57. package/dist/board-catalog/pinconfig.js +263 -0
  58. package/dist/board-catalog/sdk.d.ts +82 -0
  59. package/dist/board-catalog/sdk.js +180 -0
  60. package/dist/board-catalog/store.d.ts +196 -0
  61. package/dist/board-catalog/store.js +363 -0
  62. package/dist/board-catalog/types.d.ts +233 -0
  63. package/dist/board-catalog/types.js +7 -0
  64. package/dist/board-catalog/walker.d.ts +112 -0
  65. package/dist/board-catalog/walker.js +1101 -0
  66. package/dist/cli-utils.d.ts +2 -2
  67. package/dist/cli-utils.js +5 -5
  68. package/dist/cli.js +227 -244
  69. package/dist/config-loader.d.ts +14 -19
  70. package/dist/config-loader.js +242 -78
  71. package/dist/config-schema.d.ts +19 -227
  72. package/dist/config-schema.js +9 -8
  73. package/dist/contract/board-generator.d.ts +4 -4
  74. package/dist/contract/board-generator.js +16 -5
  75. package/dist/contract/contract-parser.d.ts +36 -127
  76. package/dist/contract/contract-parser.js +76 -0
  77. package/dist/contract/index.d.ts +1 -1
  78. package/dist/contract/index.js +70 -21
  79. package/dist/create/active-board-catalog.d.ts +9 -0
  80. package/dist/create/active-board-catalog.js +94 -0
  81. package/dist/create/board-catalog.generated.d.ts +96 -0
  82. package/dist/create/board-catalog.generated.js +128737 -0
  83. package/dist/create/board-search.d.ts +23 -0
  84. package/dist/create/board-search.js +88 -0
  85. package/dist/create/editor-integration.d.ts +32 -0
  86. package/dist/create/editor-integration.js +257 -0
  87. package/dist/create/eslint-rules-template.d.ts +2 -2
  88. package/dist/create/framework-catalog.d.ts +48 -8
  89. package/dist/create/framework-catalog.js +58 -44
  90. package/dist/create/index.d.ts +8 -13
  91. package/dist/create/index.js +5 -9
  92. package/dist/create/mcu-target.d.ts +39 -0
  93. package/dist/create/mcu-target.js +80 -0
  94. package/dist/create/pack-targets.d.ts +19 -0
  95. package/dist/create/pack-targets.js +56 -0
  96. package/dist/create/{init-scaffold.d.ts → scaffold.d.ts} +8 -11
  97. package/dist/create/{init-scaffold.js → scaffold.js} +28 -87
  98. package/dist/create/templates.d.ts +51 -0
  99. package/dist/create/{init-templates.js → templates.js} +135 -116
  100. package/dist/create/wizard.d.ts +19 -0
  101. package/dist/create/wizard.js +314 -0
  102. package/dist/debug/preprocessor.d.ts +5 -0
  103. package/dist/debug/preprocessor.js +31 -7
  104. package/dist/diagnostics/diagnostics-report.d.ts +1 -1
  105. package/dist/diagnostics/diagnostics-report.js +3 -11
  106. package/dist/diagnostics/json-schema.d.ts +1 -1
  107. package/dist/diagnostics/md-writer.js +2 -2
  108. package/dist/diagnostics/mermaid-builder.d.ts +1 -1
  109. package/dist/diagnostics/mermaid-builder.js +1 -1
  110. package/dist/emit/cpp-emitter.js +4 -1
  111. package/dist/emit/emitters/class-emitter.js +32 -4
  112. package/dist/emit/emitters/emitter-context.d.ts +11 -0
  113. package/dist/emit/emitters/entrypoint-synthesizer.js +1 -1
  114. package/dist/emit/emitters/function-emitter-impl.js +33 -17
  115. package/dist/emit/emitters/output-finalizer.d.ts +7 -0
  116. package/dist/emit/emitters/output-finalizer.js +31 -23
  117. package/dist/emit/emitters/setup.js +147 -63
  118. package/dist/emit/emitters/top-level-prep.js +1 -1
  119. package/dist/emit/emitters/type-decl-emitter.js +13 -8
  120. package/dist/emit/emitters/ui-emitter.js +1 -1
  121. package/dist/emit/expression-renderer.d.ts +3 -3
  122. package/dist/emit/expression-renderer.js +17 -7
  123. package/dist/emit/route-hal-op.js +17 -48
  124. package/dist/emit/snprintf-helpers.d.ts +1 -3
  125. package/dist/emit/snprintf-helpers.js +1 -39
  126. package/dist/emit/statement-renderer.d.ts +0 -4
  127. package/dist/emit/statement-renderer.js +54 -45
  128. package/dist/emit/utils/async-state-machine.d.ts +16 -9
  129. package/dist/emit/utils/async-state-machine.js +56 -54
  130. package/dist/emit/utils/comment-helpers.d.ts +0 -8
  131. package/dist/emit/utils/comment-helpers.js +1 -1
  132. package/dist/emit/utils/cpp-helpers.d.ts +2 -2
  133. package/dist/emit/utils/cpp-helpers.js +2 -2
  134. package/dist/emit/utils/hal-op-cpp-type.js +4 -6
  135. package/dist/emit/utils/index.d.ts +3 -3
  136. package/dist/emit/utils/index.js +3 -3
  137. package/dist/emit/utils/type-inference.d.ts +1 -29
  138. package/dist/emit/utils/type-inference.js +0 -75
  139. package/dist/framework-package.d.ts +3 -0
  140. package/dist/framework-package.js +23 -6
  141. package/dist/framework-registry.d.ts +5 -5
  142. package/dist/frameworks/native/graphics/terminal-preview.d.ts +5 -0
  143. package/dist/frameworks/native/graphics/terminal-preview.js +33 -0
  144. package/dist/frameworks/native/index.d.ts +3 -0
  145. package/dist/frameworks/native/index.js +15 -0
  146. package/dist/frameworks/native/native-compile.d.ts +17 -0
  147. package/dist/frameworks/native/native-compile.js +170 -0
  148. package/dist/frameworks/native/native-config.d.ts +41 -0
  149. package/dist/frameworks/native/native-config.js +7 -0
  150. package/dist/frameworks/native/strategy.d.ts +97 -0
  151. package/dist/frameworks/native/strategy.js +664 -0
  152. package/dist/ir/board-resolver.d.ts +18 -23
  153. package/dist/ir/board-resolver.js +43 -401
  154. package/dist/ir/build-ir-state.d.ts +3 -0
  155. package/dist/ir/build-ir-state.js +42 -0
  156. package/dist/ir/build-ir.d.ts +1 -1
  157. package/dist/ir/build-ir.js +64 -45
  158. package/dist/ir/declaration-builders.js +1 -0
  159. package/dist/ir/expression-to-ir.js +17 -10
  160. package/dist/ir/hal/hal-emitter.d.ts +0 -2
  161. package/dist/ir/hal/hal-emitter.js +92 -41
  162. package/dist/ir/hal/hal-parser.d.ts +5 -5
  163. package/dist/ir/hal/hal-parser.js +128 -36
  164. package/dist/ir/hal/hal-plugins.d.ts +0 -1
  165. package/dist/ir/hal/hal-plugins.js +425 -716
  166. package/dist/ir/heap-analysis.js +2 -41
  167. package/dist/ir/identifier-collector.js +32 -20
  168. package/dist/ir/network-validation.js +7 -11
  169. package/dist/ir/ownership-analysis.js +75 -13
  170. package/dist/ir/peripheral-usage.d.ts +8 -2
  171. package/dist/ir/peripheral-usage.js +78 -149
  172. package/dist/ir/peripheral-validation.js +23 -3
  173. package/dist/ir/pin-capability-validation.d.ts +12 -0
  174. package/dist/ir/pin-capability-validation.js +61 -97
  175. package/dist/ir/pin-mode-validation.js +5 -5
  176. package/dist/ir/program-analysis.d.ts +8 -15
  177. package/dist/ir/program-analysis.js +140 -323
  178. package/dist/ir/pulldown-validation.js +11 -4
  179. package/dist/ir/resource-analysis.js +15 -3
  180. package/dist/ir/statement-to-ir.js +0 -6
  181. package/dist/ir/timing-validation.js +1 -1
  182. package/dist/ir/transformers/array-methods.js +11 -5
  183. package/dist/ir/transformers/call-statement.js +20 -87
  184. package/dist/ir/transformers/callback-context-registry.d.ts +1 -1
  185. package/dist/ir/transformers/callback-context-registry.js +8 -16
  186. package/dist/ir/transformers/control-flow.js +0 -13
  187. package/dist/ir/transformers/expressions.js +10 -9
  188. package/dist/ir/transformers/hal-call-resolver.d.ts +2 -2
  189. package/dist/ir/transformers/hal-call-resolver.js +24 -10
  190. package/dist/ir/transformers/hal-emit-helpers.d.ts +2 -2
  191. package/dist/ir/transformers/hal-emit-helpers.js +2 -2
  192. package/dist/ir/transformers/namespace-methods.js +1 -1
  193. package/dist/ir/transformers/ui-callback-lowering.d.ts +0 -2
  194. package/dist/ir/transformers/ui-callback-lowering.js +0 -24
  195. package/dist/ir/transformers/variables.js +338 -42
  196. package/dist/ir/try-catch-validation.js +4 -3
  197. package/dist/ir/type-resolution.js +15 -4
  198. package/dist/ir/validation-orchestrator.js +0 -9
  199. package/dist/libdef/cpp-to-decl.d.ts +0 -9
  200. package/dist/libdef/cpp-to-decl.js +0 -72
  201. package/dist/libdef/registry.d.ts +0 -1
  202. package/dist/libdef/registry.js +1 -24
  203. package/dist/library/catalog.d.ts +35 -0
  204. package/dist/library/catalog.js +67 -0
  205. package/dist/library/cli.d.ts +2 -0
  206. package/dist/library/cli.js +168 -0
  207. package/dist/library/init.d.ts +25 -0
  208. package/dist/library/init.js +397 -0
  209. package/dist/library/install.d.ts +9 -0
  210. package/dist/library/install.js +80 -0
  211. package/dist/library/registry-search.d.ts +34 -0
  212. package/dist/library/registry-search.js +50 -0
  213. package/dist/library/validate.d.ts +17 -0
  214. package/dist/library/validate.js +175 -0
  215. package/dist/library-packages.d.ts +95 -0
  216. package/dist/library-packages.js +275 -0
  217. package/dist/mapping/peripheral-names.js +9 -4
  218. package/dist/mapping/source-map.d.ts +3 -4
  219. package/dist/mapping/source-map.js +12 -18
  220. package/dist/orchestrator/graph-builder.d.ts +3 -3
  221. package/dist/orchestrator/graph-builder.js +29 -12
  222. package/dist/orchestrator/type-checker.d.ts +3 -3
  223. package/dist/orchestrator/type-checker.js +3 -3
  224. package/dist/platform/async-runtime.js +1 -1
  225. package/dist/platform/coop-scheduler-runtime.js +2 -2
  226. package/dist/platform/generic-debug-codegen.d.ts +36 -0
  227. package/dist/platform/generic-debug-codegen.js +185 -0
  228. package/dist/platform/generic-strategy.d.ts +3 -4
  229. package/dist/platform/generic-strategy.js +5 -80
  230. package/dist/platform/index.js +1 -1
  231. package/dist/platform/registry.d.ts +2 -2
  232. package/dist/platform/registry.js +2 -2
  233. package/dist/preview/client.js +17 -3
  234. package/dist/safety/asil-decorators.d.ts +32 -0
  235. package/dist/safety/asil-decorators.js +32 -0
  236. package/dist/safety/authoring.d.ts +85 -0
  237. package/dist/safety/authoring.js +61 -0
  238. package/dist/safety/engine.d.ts +3 -0
  239. package/dist/safety/engine.js +83 -0
  240. package/dist/safety/hal/ops.d.ts +48 -0
  241. package/dist/safety/hal/ops.js +46 -0
  242. package/dist/safety/iso26262/analyze.d.ts +13 -0
  243. package/dist/safety/iso26262/analyze.js +29 -0
  244. package/dist/safety/iso26262/asil.d.ts +10 -0
  245. package/dist/safety/iso26262/asil.js +22 -0
  246. package/dist/safety/iso26262/collect.d.ts +14 -0
  247. package/dist/safety/iso26262/collect.js +137 -0
  248. package/dist/safety/iso26262/goto-checker.d.ts +10 -0
  249. package/dist/safety/iso26262/goto-checker.js +72 -0
  250. package/dist/safety/iso26262/heap-checker.d.ts +7 -0
  251. package/dist/safety/iso26262/heap-checker.js +57 -0
  252. package/dist/safety/iso26262/init-checker.d.ts +13 -0
  253. package/dist/safety/iso26262/init-checker.js +127 -0
  254. package/dist/safety/iso26262/loop-checker.d.ts +7 -0
  255. package/dist/safety/iso26262/loop-checker.js +86 -0
  256. package/dist/safety/iso26262/recursion-checker.d.ts +9 -0
  257. package/dist/safety/iso26262/recursion-checker.js +134 -0
  258. package/dist/safety/iso26262/sidecar-writer.d.ts +25 -0
  259. package/dist/safety/iso26262/sidecar-writer.js +13 -0
  260. package/dist/safety/passes/pinMode-intercept.d.ts +3 -0
  261. package/dist/safety/passes/pinMode-intercept.js +60 -0
  262. package/dist/safety/runtime/mode-table.d.ts +5 -0
  263. package/dist/safety/runtime/mode-table.js +55 -0
  264. package/dist/safety/runtime/polyfills.d.ts +6 -0
  265. package/dist/safety/runtime/polyfills.js +12 -0
  266. package/dist/safety/runtime/result-struct.d.ts +11 -0
  267. package/dist/safety/runtime/result-struct.js +98 -0
  268. package/dist/safety/runtime/safe-int.d.ts +33 -0
  269. package/dist/safety/runtime/safe-int.js +170 -0
  270. package/dist/safety/runtime/safe-traits.d.ts +4 -0
  271. package/dist/safety/runtime/safe-traits.js +95 -0
  272. package/dist/safety/runtime/safe-variable.d.ts +39 -0
  273. package/dist/safety/runtime/safe-variable.js +226 -0
  274. package/dist/safety/runtime/vote.d.ts +8 -0
  275. package/dist/safety/runtime/vote.js +92 -0
  276. package/dist/safety/runtime/write-verify.d.ts +8 -0
  277. package/dist/safety/runtime/write-verify.js +71 -0
  278. package/dist/safety/safe-int-types.d.ts +45 -0
  279. package/dist/safety/safe-int-types.js +25 -0
  280. package/dist/safety/safe-variable-types.d.ts +29 -0
  281. package/dist/safety/safe-variable-types.js +19 -0
  282. package/dist/safety/safety-bridge.d.ts +5 -6
  283. package/dist/safety/safety-bridge.js +15 -28
  284. package/dist/safety/sidecar-bridge.d.ts +1 -3
  285. package/dist/safety/sidecar-bridge.js +3 -20
  286. package/dist/safety/specifiers.d.ts +6 -0
  287. package/dist/safety/specifiers.js +14 -0
  288. package/dist/safety-hook.d.ts +8 -7
  289. package/dist/safety-hook.js +12 -13
  290. package/dist/testing.d.ts +5 -6
  291. package/dist/testing.js +5 -5
  292. package/dist/transpile/resolution.d.ts +1 -1
  293. package/dist/transpile/resolution.js +18 -9
  294. package/dist/transpile/test-pins.d.ts +18 -0
  295. package/dist/transpile/test-pins.js +142 -0
  296. package/dist/transpile.d.ts +1 -2
  297. package/dist/transpile.js +90 -29
  298. package/dist/types.d.ts +45 -26
  299. package/dist/ui-hook.d.ts +1 -1
  300. package/dist/utils/cli.d.ts +2 -2
  301. package/dist/utils/cli.js +160 -157
  302. package/dist/utils/fs.d.ts +3 -4
  303. package/dist/utils/fs.js +4 -5
  304. package/dist/utils/strings.d.ts +0 -1
  305. package/dist/utils/strings.js +0 -8
  306. package/dist/utils/ui.d.ts +5 -2
  307. package/dist/utils/ui.js +13 -3
  308. package/package.json +158 -149
  309. package/dist/add-preset.d.ts +0 -4
  310. package/dist/add-preset.js +0 -74
  311. package/dist/api/shared/async-symbol-detector.d.ts +0 -11
  312. package/dist/api/shared/async-symbol-detector.js +0 -140
  313. package/dist/api/shared/native-display-op-resolver.d.ts +0 -10
  314. package/dist/api/shared/native-display-op-resolver.js +0 -64
  315. package/dist/create/board-checklist.d.ts +0 -2
  316. package/dist/create/board-checklist.js +0 -52
  317. package/dist/create/board-codegen.d.ts +0 -9
  318. package/dist/create/board-codegen.js +0 -238
  319. package/dist/create/board-generators.d.ts +0 -12
  320. package/dist/create/board-generators.js +0 -651
  321. package/dist/create/board-spec.d.ts +0 -637
  322. package/dist/create/board-spec.js +0 -214
  323. package/dist/create/init-templates.d.ts +0 -28
  324. package/dist/create/init-wizard.d.ts +0 -8
  325. package/dist/create/init-wizard.js +0 -182
  326. package/dist/install/framework-catalog.d.ts +0 -53
  327. package/dist/install/framework-catalog.js +0 -107
  328. package/dist/install/handle-install.d.ts +0 -35
  329. package/dist/install/handle-install.js +0 -177
  330. package/dist/install/index.d.ts +0 -4
  331. package/dist/install/index.js +0 -3
  332. package/dist/ir/pin-state-tracking.d.ts +0 -58
  333. package/dist/ir/pin-state-tracking.js +0 -182
  334. package/dist/ir/pwm-timer-sharing.d.ts +0 -4
  335. package/dist/ir/pwm-timer-sharing.js +0 -94
  336. package/dist/ir/timer0-pwm-timing-conflict.d.ts +0 -4
  337. package/dist/ir/timer0-pwm-timing-conflict.js +0 -72
  338. package/dist/libdef/component-decls.d.ts +0 -2
  339. package/dist/libdef/component-decls.js +0 -6
  340. package/dist/libdef/component-discovery.d.ts +0 -43
  341. package/dist/libdef/component-discovery.js +0 -83
  342. package/dist/theme-tokens.d.ts +0 -22
  343. package/dist/theme-tokens.js +0 -172
@@ -311,36 +311,40 @@ export function emitFunctions(ctx) {
311
311
  // Drive the UI runtime each frame. Fires only in the driver function when
312
312
  // a UI is mounted (entryHasUI). Uses a separate gate from the async pump
313
313
  // so a pure-UI program (no async/timers) still animates. When the strategy
314
- // provides hostEventLoop, the per-frame work runs inside a while loop so a
315
- // host target (SDL) can pump events and present between ticks. Emitted
316
- // AFTER the function's init statements (ui_init/display_init/touch_init)
317
- // so initialization precedes the loop.
318
- if (entryHasUI() && fn.name === asyncDriverFn) {
319
- const loop = strategy.hostEventLoop?.();
320
- if (loop) {
321
- appendSourceLine(ctx, ` bool ${loop.flagName} = true;`);
322
- appendSourceLine(ctx, ` while (${loop.continueCondition}) {`);
323
- appendSourceLine(ctx, ` ${loop.preIteration}`);
314
+ // provides hostEventLoop, the per-frame work ui_tick AND the async-task
315
+ // injections below runs inside a while loop so a driver that is called
316
+ // once (a main() entrypoint: SDL native, Zephyr's scheduler loop) can pump
317
+ // events, tick the UI, and advance async tasks every frame. (A repeatedly-
318
+ // called driver like Arduino's loop() provides no hostEventLoop and the
319
+ // work stays flat.) Emitted AFTER the function's init statements
320
+ // (ui_init/display_init/touch_init) so initialization precedes the loop.
321
+ const hostLoop = entryHasUI() && fn.name === asyncDriverFn
322
+ ? strategy.hostEventLoop?.()
323
+ : undefined;
324
+ if (hostLoop) {
325
+ appendSourceLine(ctx, ` bool ${hostLoop.flagName} = true;`);
326
+ appendSourceLine(ctx, ` while (${hostLoop.continueCondition}) {`);
327
+ if (hostLoop.preIteration) {
328
+ appendSourceLine(ctx, ` ${hostLoop.preIteration}`);
324
329
  }
330
+ }
331
+ if (entryHasUI() && fn.name === asyncDriverFn) {
325
332
  appendSourceLine(ctx, ` uint32_t __tc_ui_now = static_cast<uint32_t>(${strategy.currentTimeMillis()});`);
326
333
  appendSourceLine(ctx, " static uint32_t __tc_ui_last_tick = __tc_ui_now;");
327
334
  appendSourceLine(ctx, " uint32_t __tc_ui_delta = __tc_ui_now - __tc_ui_last_tick;");
328
335
  appendSourceLine(ctx, " __tc_ui_last_tick = __tc_ui_now;");
329
336
  appendSourceLine(ctx, " if (__tc_ui_delta > 250) __tc_ui_delta = 250;");
330
337
  appendSourceLine(ctx, " ui_tick(static_cast<uint16_t>(__tc_ui_delta));");
331
- if (loop) {
332
- appendSourceLine(ctx, ` ${loop.postIteration}`);
333
- appendSourceLine(ctx, ` }`);
334
- }
335
338
  }
336
339
  if (isLoopDriver && (hasPromiseRuntime || asyncTaskClasses.length > 0 || usesTimers)) {
337
340
  const taskNames = asyncTaskClasses.map(t => t.taskVarName);
338
341
  const asyncConfig = strategy.getAsyncRuntimeConfig();
339
342
  asyncConfig.hasPromiseRuntime = hasPromiseRuntime;
340
343
  asyncConfig.hasTimers = usesTimers;
344
+ const injectionIndent = hostLoop ? " " : " ";
341
345
  // Cooperative scheduler path (Phase 0): when opted in, replace the flat
342
346
  // per-frame pump sequence with a single CoopSched dispatch. The work
343
- // units mirror exactly what the strategy's asyncLoopInjection() would
347
+ // units mirror exactly what a strategy's asyncLoopInjection() would
344
348
  // have emitted (so Zephyr, which omits the microtask pump, still omits
345
349
  // it), but they are dispatched in priority order and budget-bounded.
346
350
  if (asyncConfig.enablePriority || asyncConfig.enableTimeBudget) {
@@ -354,16 +358,22 @@ export function emitFunctions(ctx) {
354
358
  // No work units: fall back to the strategy's own injection.
355
359
  const injectionLines = strategy.asyncLoopInjection(taskNames, asyncConfig);
356
360
  for (const line of injectionLines)
357
- appendSourceLine(ctx, ` ${line}`);
361
+ appendSourceLine(ctx, `${injectionIndent}${line}`);
358
362
  }
359
363
  }
360
364
  else {
361
365
  const injectionLines = strategy.asyncLoopInjection(taskNames, asyncConfig);
362
366
  for (const line of injectionLines) {
363
- appendSourceLine(ctx, ` ${line}`);
367
+ appendSourceLine(ctx, `${injectionIndent}${line}`);
364
368
  }
365
369
  }
366
370
  }
371
+ if (hostLoop) {
372
+ if (hostLoop.postIteration) {
373
+ appendSourceLine(ctx, ` ${hostLoop.postIteration}`);
374
+ }
375
+ appendSourceLine(ctx, " }");
376
+ }
367
377
  if (isLoopDriver && lastIsReturn) {
368
378
  appendRenderedStatement(ctx, lastStmt, " ", functionScope);
369
379
  }
@@ -400,6 +410,12 @@ export function emitFunctions(ctx) {
400
410
  export function emitFunctionForwardDeclarations(ctx) {
401
411
  const { strategy, effectiveEmitMode } = ctx;
402
412
  const excludedNames = new Set(strategy.forwardDeclarationExclusions?.() ?? []);
413
+ // Async-method starters: the owning class's method body calls these before
414
+ // the task class (and this definition) exists later in the TU. The class
415
+ // forward declarations emitted nearby make the <Class>* parameter valid.
416
+ for (const starter of ctx.asyncMethodStarters ?? []) {
417
+ appendSourceLine(ctx, starter.proto);
418
+ }
403
419
  // Callback forward declarations (no IRAM_ATTR — ESP-IDF's attribute uses
404
420
  // __COUNTER__, so decl+def would get conflicting .iram1.N sections).
405
421
  if (effectiveEmitMode !== "split") {
@@ -7,4 +7,11 @@ export declare function emitPreamble(ctx: EmitterContext): void;
7
7
  * from task bodies are already declared.
8
8
  */
9
9
  export declare function emitAsyncTaskClasses(ctx: EmitterContext): void;
10
+ /**
11
+ * Emit async-METHOD task classes + instances + starter definitions. Must run
12
+ * AFTER emitClasses (segments dereference _owner->field on the now-complete
13
+ * owning class) and BEFORE emitFunctions (the loop pump references the
14
+ * instances; the starter definitions call .start on them).
15
+ */
16
+ export declare function emitAsyncMethodTasks(ctx: EmitterContext): void;
10
17
  export declare function finalizeOutput(ctx: EmitterContext): GeneratedOutputs;
@@ -1,7 +1,7 @@
1
1
  import path from "node:path";
2
2
  import { writeText, readText } from "../../utils/fs.js";
3
3
  import { makeGeneratedMap, writeSourceMap } from "../../mapping/source-map.js";
4
- import { dedupe, hasConsoleCalls, resolveTranspiledModuleInclude } from "../utils/index.js";
4
+ import { dedupe, resolveTranspiledModuleInclude } from "../utils/index.js";
5
5
  import { appendHeaderLine } from "./line-appender.js";
6
6
  import { runSelfCheck } from "../compliance/rule-engine.js";
7
7
  import { renderRegistryJson } from "../compliance/deviation-writer.js";
@@ -78,24 +78,6 @@ export function emitPreamble(ctx) {
78
78
  }
79
79
  // Async task classes are emitted later (emitAsyncTaskClasses) AFTER top-level
80
80
  // globals so references like WIFI_SSID inside the state machine compile.
81
- if (strategy.needsVectorOverload() && hasConsoleCalls(program, strategy) && (programAnalysis.usesVectorTypes || programAnalysis.hasArrayInObjectLiteral)) {
82
- appendSourceLineLocal(ctx, "template <typename T>");
83
- appendSourceLineLocal(ctx, "std::ostream& operator<<(std::ostream& os, const std::vector<T>& values)");
84
- appendSourceLineLocal(ctx, "{");
85
- appendSourceLineLocal(ctx, ' os << "[";');
86
- appendSourceLineLocal(ctx, " for (size_t i = 0; i < values.size(); ++i)");
87
- appendSourceLineLocal(ctx, " {");
88
- appendSourceLineLocal(ctx, " if (i > 0)");
89
- appendSourceLineLocal(ctx, " {");
90
- appendSourceLineLocal(ctx, ' os << ", ";');
91
- appendSourceLineLocal(ctx, " }");
92
- appendSourceLineLocal(ctx, " os << values[i];");
93
- appendSourceLineLocal(ctx, " }");
94
- appendSourceLineLocal(ctx, ' os << "]";');
95
- appendSourceLineLocal(ctx, " return os;");
96
- appendSourceLineLocal(ctx, "}");
97
- appendSourceLineLocal(ctx, "");
98
- }
99
81
  if (programAnalysis.hasGenerators) {
100
82
  appendSourceLineLocal(ctx, "#include <coroutine>");
101
83
  appendSourceLineLocal(ctx, "#include <optional>");
@@ -140,12 +122,38 @@ export function emitAsyncTaskClasses(ctx) {
140
122
  const { asyncTaskClasses } = ctx;
141
123
  if (asyncTaskClasses.length === 0)
142
124
  return;
143
- for (const { classDef, instanceDecl } of asyncTaskClasses) {
144
- for (const line of classDef.split("\n")) {
125
+ for (const t of asyncTaskClasses) {
126
+ // Async-METHOD tasks move to emitAsyncMethodTasks (after the user
127
+ // classes): their segment bodies dereference `_owner->field`, which
128
+ // needs the owning class COMPLETE — at this point only the forward
129
+ // declaration exists.
130
+ if (t.starterDef !== undefined)
131
+ continue;
132
+ for (const line of t.classDef.split("\n")) {
133
+ appendSourceLineLocal(ctx, line);
134
+ }
135
+ appendSourceLineLocal(ctx, "");
136
+ appendSourceLineLocal(ctx, t.instanceDecl);
137
+ appendSourceLineLocal(ctx, "");
138
+ }
139
+ }
140
+ /**
141
+ * Emit async-METHOD task classes + instances + starter definitions. Must run
142
+ * AFTER emitClasses (segments dereference _owner->field on the now-complete
143
+ * owning class) and BEFORE emitFunctions (the loop pump references the
144
+ * instances; the starter definitions call .start on them).
145
+ */
146
+ export function emitAsyncMethodTasks(ctx) {
147
+ const { asyncTaskClasses } = ctx;
148
+ for (const t of asyncTaskClasses) {
149
+ if (t.starterDef === undefined)
150
+ continue;
151
+ for (const line of t.classDef.split("\n")) {
145
152
  appendSourceLineLocal(ctx, line);
146
153
  }
147
154
  appendSourceLineLocal(ctx, "");
148
- appendSourceLineLocal(ctx, instanceDecl);
155
+ appendSourceLineLocal(ctx, t.instanceDecl);
156
+ appendSourceLineLocal(ctx, t.starterDef);
149
157
  appendSourceLineLocal(ctx, "");
150
158
  }
151
159
  }
@@ -330,7 +338,7 @@ export function finalizeOutput(ctx) {
330
338
  }
331
339
  // Sidecar deviation registry — written for both warn and strict modes
332
340
  // alongside the emitted artifact (mirrors how .thcppmap.json sits next
333
- // to the .cpp/.ino today).
341
+ // to the .cpp today).
334
342
  const toolVersion = options.toolVersion ?? "unknown";
335
343
  const registryPath = path.join(outDir, `${baseName}.autosar-deviations.json`);
336
344
  writeText(registryPath, renderRegistryJson(ctx.compliance, path.basename(sourcePath), toolVersion));
@@ -1,10 +1,12 @@
1
1
  import path from "node:path";
2
2
  import { filterPolyfillHelpers, isStringEnum } from "../../api/shared/index.js";
3
3
  import { hasSafetyHook, requireSafetyHook } from "../../safety-hook.js";
4
+ import { isSafetyImportSpecifier } from "../../safety/specifiers.js";
4
5
  import { analyzeProgram } from "../../ir/program-analysis.js";
5
6
  import { collectStatementIdentifiers } from "../../ir/identifier-collector.js";
6
7
  import { ensureDir } from "../../utils/fs.js";
7
8
  import { resolveImport } from "../../libdef/registry.js";
9
+ import { isRegisteredCuttlefishLibrary } from "../../library-packages.js";
8
10
  import { emitPolyfillBoilerplate } from "../native-helpers-emitter.js";
9
11
  import { resolveStrategy } from "../../platform/registry.js";
10
12
  import { buildCoopSchedulerPolyfill } from "../../platform/coop-scheduler-runtime.js";
@@ -239,6 +241,11 @@ function extractShimMacro(lines, macroName) {
239
241
  return lines.slice(ifndefIdx, endIdx + 1);
240
242
  }
241
243
  export function buildEmitterContext(program, options) {
244
+ // Framework strategies that resolve per-program chip data (Zephyr's chip
245
+ // view reconstructs from the generated manifest) must do it before any
246
+ // body rendering — hal-op lowering reads their cached descriptor.
247
+ options.strategy
248
+ ?.prepareChip?.(program, options.platformContext);
242
249
  const largeEnumNames = new Set();
243
250
  const enumNames = new Set();
244
251
  const stringEnumNames = new Set();
@@ -312,23 +319,6 @@ export function buildEmitterContext(program, options) {
312
319
  })();
313
320
  const programAnalysis = analyzeProgram(program, strategy);
314
321
  if (options.platformContext) {
315
- // The UI runtime's per-frame tick calls millis() (injected by the emitter,
316
- // not present in user source), so a mounted UI needs the native millis ISR
317
- // even when usesNativeTiming is false. OR entryHasUI() into the flag the
318
- // AVR strategy reads when gating native_millis, so framework-avr doesn't
319
- // drop the Timer0 ISR from a UI program.
320
- if (!programAnalysis.usesNativeTiming && entryHasUI()) {
321
- programAnalysis.usesNativeTiming = true;
322
- }
323
- // The UI runtime header calls constrain() in the progress/range node draw
324
- // and touch-slider paths (injected by the emitter, not present in user
325
- // source), so a mounted UI needs the constrain polyfill even when
326
- // usesConstrain is false. OR entryHasUI() into the flag the native
327
- // strategy reads when gating the constrain helper, mirroring the
328
- // usesNativeTiming handling above.
329
- if (!programAnalysis.usesConstrain && entryHasUI()) {
330
- programAnalysis.usesConstrain = true;
331
- }
332
322
  options.platformContext.analysis = programAnalysis;
333
323
  if (!options.platformContext.architecture && program.boardConstants) {
334
324
  options.platformContext.architecture = program.boardConstants.get("architecture");
@@ -367,16 +357,11 @@ export function buildEmitterContext(program, options) {
367
357
  if (!programAnalysis.usedPolyfillHelpers.has('__tc_StaticArray')) {
368
358
  filteredNativePolyfills = filteredNativePolyfills.filter(p => p.id !== "static_array");
369
359
  }
370
- const usesTimers = programAnalysis.usedPolyfillHelpers.has('__tc_setInterval') ||
371
- programAnalysis.usedPolyfillHelpers.has('__tc_setTimeout');
372
- if (!usesTimers) {
373
- filteredNativePolyfills = filteredNativePolyfills.filter(p => p.id !== "timer_methods");
374
- }
375
360
  // Safety polyfills (mode table + voter + SafeVariable + SafeInt). Gated on
376
- // programUsesSafety(program) so they ONLY appear when the sketch actually
361
+ // programUsesSafety(program) so they ONLY appear when the program actually
377
362
  // references a safety construct — emitting them unconditionally (just because
378
363
  // @typecad/safety is installed and registered its hook) leaks ~200 lines of
379
- // polyfill into every sketch, and SafeInt's `return *this` chaining methods
364
+ // polyfill into every program, and SafeInt's `return *this` chaining methods
380
365
  // tripped byte-identity / lowering assertions that expect no `this` token.
381
366
  // programUsesSafety walks the IR for any safety.* hal-op.
382
367
  if (isEntryFile && hasSafetyHook() && programUsesSafety(program)) {
@@ -392,8 +377,7 @@ export function buildEmitterContext(program, options) {
392
377
  // minimal-libc targets (Zephyr) where the Promise runtime does not.
393
378
  const coopConfig = strategy.getAsyncRuntimeConfig?.();
394
379
  const hasCoopWork = program.functions.some(fn => fn.isAsync) ||
395
- programAnalysis.usedPolyfillHelpers.has('__tc_setInterval') ||
396
- programAnalysis.usedPolyfillHelpers.has('__tc_setTimeout');
380
+ program.classes.some(c => c.methods.some(m => m.isAsync));
397
381
  if (isEntryFile && coopConfig && hasCoopWork) {
398
382
  const coop = buildCoopSchedulerPolyfill(program, coopConfig, strategy.currentTimeMillis());
399
383
  if (coop)
@@ -403,8 +387,13 @@ export function buildEmitterContext(program, options) {
403
387
  const emittedPolyfills = allPolyfills.length > 0
404
388
  ? emitPolyfillBoilerplate(allPolyfills)
405
389
  : undefined;
406
- const hasAsyncRuntime = program.functions.some(fn => fn.isAsync);
390
+ const hasAsyncRuntime = program.functions.some(fn => fn.isAsync) || program.classes.some(c => c.methods.some(m => m.isAsync));
407
391
  const hasPromiseRuntime = nativePolyfills.some((p) => p.id === "async_runtime" && p.hasPromiseRuntime === true);
392
+ // Native timer capability comes from the strategy's async config, not from
393
+ // scanning the program: platforms either provide native timer callbacks for
394
+ // the async runtime (host/native) or they do not (Zephyr — periodic work is
395
+ // a Thread or a Counter there).
396
+ const usesTimers = strategy.getAsyncRuntimeConfig?.()?.hasTimers === true;
408
397
  if (!isNpmPackage) {
409
398
  includes.push(...strategy.forcedIncludes(program, options.platformContext));
410
399
  Object.assign(symbolMap, strategy.symbolAliases(program, options.platformContext));
@@ -423,16 +412,6 @@ export function buildEmitterContext(program, options) {
423
412
  if (!programAnalysis.usesDateNow) {
424
413
  shimLines = shimLines.filter(l => !l.includes('namespace Date'));
425
414
  }
426
- // The UI runtime's per-frame tick uses millis() (injected by the emitter,
427
- // not authored in user source), so keep the millis() shim when a UI is
428
- // mounted even if the source-level analysis didn't flag usesMillis. The
429
- // async runtime and the setInterval/setTimeout scheduler also poll
430
- // millis() without any user-source millis() call (Async.sleep lowers to a
431
- // raw hal-op the timing scanners can't see) — mirror the usesNativeTiming
432
- // derivation and keep the shim for those hidden consumers too.
433
- if (!programAnalysis.usesMillis && !programAnalysis.hasAsync && programAnalysis.timerCallCount === 0 && !entryHasUI()) {
434
- shimLines = shimLines.filter(l => !l.includes('millis()'));
435
- }
436
415
  // Strip the nullish helper FUNCTIONS (not the CUTTLEFISH_UNDEFINED macro)
437
416
  // when the file doesn't actually emit cuttlefish_nullish(...) calls. A
438
417
  // file that only references `null`/`undefined` literals needs just the
@@ -444,6 +423,14 @@ export function buildEmitterContext(program, options) {
444
423
  // cuttlefish_nullish( call (a more reliable post-emit check than the
445
424
  // per-file pre-emit analysis flag). The logic here governs the .cpp
446
425
  // source shim only.
426
+ // The runtime clock (__tc_now_ms) is gated by the Zephyr strategy at
427
+ // emission; native emits its definition unconditionally. Strip it from
428
+ // programs that can never read the clock (no Time calls, async, timers,
429
+ // scheduler, or mounted UI) so the definition doesn't leak into the
430
+ // emitted header.
431
+ if (!programAnalysis.usesWallClock && !programAnalysis.hasAsync && !entryHasUI() && !hasPromiseRuntime) {
432
+ shimLines = shimLines.filter(l => !l.includes('__tc_now_ms'));
433
+ }
447
434
  if (!programAnalysis.usesNullishHelper) {
448
435
  const filtered = [];
449
436
  for (let i = 0; i < shimLines.length; i++) {
@@ -457,9 +444,6 @@ export function buildEmitterContext(program, options) {
457
444
  if (!programAnalysis.usesNum) {
458
445
  shimLines = filterShimBlock(shimLines, 'struct __tc_Num {', '} Num;');
459
446
  }
460
- if (!programAnalysis.usesTiming) {
461
- shimLines = filterShimBlock(shimLines, 'struct __tc_Timing {', '} Timing;');
462
- }
463
447
  if (!programAnalysis.usesWDT) {
464
448
  shimLines = filterShimBlock(shimLines, '#include <avr/wdt.h>', '} WDT;');
465
449
  }
@@ -472,9 +456,7 @@ export function buildEmitterContext(program, options) {
472
456
  // these filters are no-ops there. The strategies also self-gate on the
473
457
  // same flags; this is the defensive backstop (mirrors how usesWDT/etc.
474
458
  // backstop the strategy-side gating above).
475
- // Keep the UART block when console calls are present (console_log etc.
476
- // call _uart_* functions) even if no direct uart.* HAL ops are used.
477
- if (!programAnalysis.usesUart && !programAnalysis.hasConsoleCalls) {
459
+ if (!programAnalysis.usesUart) {
478
460
  shimLines = filterShimBlock(shimLines, '// CUTTLEFISH_UART_BEGIN', '// CUTTLEFISH_UART_END');
479
461
  shimLines = filterShimBlock(shimLines, '// CUTTLEFISH_UART_EXT_BEGIN', '// CUTTLEFISH_UART_EXT_END');
480
462
  }
@@ -484,14 +466,8 @@ export function buildEmitterContext(program, options) {
484
466
  if (!programAnalysis.usesI2C) {
485
467
  shimLines = filterShimBlock(shimLines, '// CUTTLEFISH_TWI_BEGIN', '// CUTTLEFISH_TWI_END');
486
468
  }
487
- if (!programAnalysis.usesEEPROM && !programAnalysis.usesPreferences) {
488
- shimLines = filterShimBlock(shimLines, '// CUTTLEFISH_EEPROM_BEGIN', '// CUTTLEFISH_EEPROM_END');
489
- }
490
- if (!programAnalysis.usesTone) {
491
- shimLines = filterShimBlock(shimLines, '// CUTTLEFISH_TONE_BEGIN', '// CUTTLEFISH_TONE_END');
492
- }
493
469
  // framework-esp32 native peripheral driver shims. Same defensive-backstop
494
- // pattern as the UART/SPI/TWI/EEPROM/tone blocks above. framework-arduino's
470
+ // pattern as the UART/SPI/TWI/EEPROM blocks above. framework-arduino's
495
471
  // and framework-avr's shimLines contain none of these markers, so these
496
472
  // filters are no-ops there.
497
473
  if (!programAnalysis.usesGPIO) {
@@ -506,9 +482,6 @@ export function buildEmitterContext(program, options) {
506
482
  if (!programAnalysis.usesDAC) {
507
483
  shimLines = filterShimBlock(shimLines, '// CUTTLEFISH_DAC_BEGIN', '// CUTTLEFISH_DAC_END');
508
484
  }
509
- if (!programAnalysis.usesPower) {
510
- shimLines = filterShimBlock(shimLines, '// CUTTLEFISH_POWER_BEGIN', '// CUTTLEFISH_POWER_END');
511
- }
512
485
  if (!programAnalysis.usesWdt) {
513
486
  shimLines = filterShimBlock(shimLines, '// CUTTLEFISH_WDT_BEGIN', '// CUTTLEFISH_WDT_END');
514
487
  }
@@ -547,6 +520,14 @@ export function buildEmitterContext(program, options) {
547
520
  if (!programAnalysis.usesPreferences) {
548
521
  shimLines = filterShimBlock(shimLines, '// CUTTLEFISH_PREFERENCES_BEGIN', '// CUTTLEFISH_PREFERENCES_END');
549
522
  }
523
+ // Entry-TU marker for framework shims: the UI runtime header (ui_tick) is
524
+ // emitted ONLY into the entry file (emitUIRuntime gates on isEntryFile), so
525
+ // shim helpers that keep the UI alive during bounded blocking waits (e.g.
526
+ // Zephyr's wifi connect) must compile those calls down everywhere else.
527
+ // Defined before the shim lines land in the preamble, after all filters.
528
+ if (isEntryFile && entryHasUI() && shimLines.length > 0) {
529
+ shimLines.unshift("#define CUTTLEFISH_ENTRY_UI_TU 1");
530
+ }
550
531
  profileDiagnostics = [...strategy.profileDiagnostics(program, options.platformContext)];
551
532
  }
552
533
  for (const imported of program.imports) {
@@ -554,6 +535,19 @@ export function buildEmitterContext(program, options) {
554
535
  // named imports — add it to the symbolMap so cross-module references
555
536
  // resolve. Demo #12 Finding C — was skipped, so `encode` wasn't declared.
556
537
  const defaultName = imported.defaultImportName;
538
+ // Cuttlefish library packages: the import emits the shim-header include
539
+ // (resolved through the libdef registry from the library manifest) and
540
+ // contributes native shims + build fragments. This must run BEFORE the
541
+ // SDK skip below — @typecad-scoped libraries would otherwise be treated
542
+ // as type-level-only like the rest of the SDK.
543
+ if (isRegisteredCuttlefishLibrary(imported.moduleSpecifier)) {
544
+ const resolved = resolveImport(imported, options.libdefs, options.target, options.platformContext, program.fileName);
545
+ includes.push(normalizeInclude(resolved.include));
546
+ Object.assign(symbolMap, resolved.symbolMap);
547
+ if (defaultName)
548
+ symbolMap[defaultName] = defaultName;
549
+ continue;
550
+ }
557
551
  if (isCuttlefishSDKImport(imported.moduleSpecifier, program.fileName)) {
558
552
  for (const symbol of imported.namedImports) {
559
553
  symbolMap[symbol] = symbol;
@@ -586,7 +580,7 @@ export function buildEmitterContext(program, options) {
586
580
  // time; the package emits NO C++ module/header. Register the imported
587
581
  // symbols so references resolve, but skip the #include generation.
588
582
  if (imported.moduleSpecifier === "@typecad/ui" ||
589
- imported.moduleSpecifier === "@typecad/safety") {
583
+ isSafetyImportSpecifier(imported.moduleSpecifier)) {
590
584
  for (const symbol of imported.namedImports) {
591
585
  symbolMap[symbol] = symbol;
592
586
  }
@@ -680,11 +674,11 @@ export function buildEmitterContext(program, options) {
680
674
  // Map a function's original TS name to its emitted C++ name, applying the
681
675
  // strategy's user-function renames. The entrypoint sentinel
682
676
  // `__cuttlefish_entrypoint__` maps to the strategy's entrypoint name
683
- // (`main` on native, `setup` on Arduino); every other name is routed through
677
+ // (per-target entrypoint names); every other name is routed through
684
678
  // `strategy.mapFunctionName`, which is where a target renames a user
685
679
  // function that would otherwise collide with a C++/framework reserved name
686
- // — e.g. Arduino renames a user `function main()` to `cuttlefish_main`,
687
- // because Arduino has no `main()` (the entrypoints are the auto-generated
680
+ // — e.g. Targets without a user `main()` rename one to `cuttlefish_main`;
681
+ // because this target has no user `main()` (the entrypoints are the auto-generated
688
682
  // `setup()`/`loop()`), and a file-scope `static void main()` collides with
689
683
  // C++'s required `int main()` signature. Call-site renames are applied
690
684
  // uniformly in StatementRenderer.renderCall (the single call-rendering
@@ -857,7 +851,7 @@ export function buildEmitterContext(program, options) {
857
851
  // method/ctor parameter, or a top-level binding. Previously only local
858
852
  // var_decls were registered (in class-emitter), which ran too late for the
859
853
  // free-function pass — leaving `hero->alive` unrewritten and failing g++.
860
- // See SUPPORT_MATRIX §4.3 (demo #4 fix).
854
+ // (demo #4 fix.)
861
855
  // `classAccessorNames` is built at the outer scope (not inside a nested
862
856
  // block) so it can also be passed to the expression renderer as
863
857
  // `typeAccessorNames` — the type-keyed fallback used when a getter access's
@@ -967,6 +961,7 @@ export function buildEmitterContext(program, options) {
967
961
  const mapFunctionNameLocal = (originalName) => statementRenderer.mapFunctionName(originalName);
968
962
  const asyncFunctionMappedNames = new Set(program.functions.filter((fn) => fn.isAsync).map((fn) => mapFunctionNameLocal(fn.originalName)));
969
963
  const asyncTaskClasses = [];
964
+ const asyncMethodStarters = [];
970
965
  if (hasAsyncRuntime) {
971
966
  for (const fn of program.functions) {
972
967
  if (fn.isAsync) {
@@ -992,21 +987,103 @@ export function buildEmitterContext(program, options) {
992
987
  // Use renderWithPrelude so snprintf buffer declarations (e.g.
993
988
  // char __cuttlefish_str_N[...]; snprintf(...)) are emitted before
994
989
  // the statement that references them. Without this, template-literal
995
- // console.log inside async functions loses the buffer declaration.
990
+ // string building inside async functions loses the buffer declaration.
996
991
  const { prelude, statement } = contextRenderer.renderWithPrelude(stmt, forHeader, calleeTransformer);
997
992
  if (prelude.length > 0) {
998
993
  return prelude.join("\n") + "\n" + statement;
999
994
  }
1000
995
  return statement;
996
+ }, {
997
+ onUnsupportedAwait: (callee, span) => {
998
+ emitDiagnostics.push({
999
+ severity: "error",
1000
+ code: "await-unsupported-call",
1001
+ message: `await ${callee}(...) has no cooperative lowering here — the call would be dropped. Await is supported on Time.sleep, HAL ops (wifi.join / wifi.scan / http.send), ui.onTap(), and pin-edge waits.`,
1002
+ ...(span ? { source: `${callee}(...)` } : {}),
1003
+ });
1004
+ },
1001
1005
  });
1002
1006
  asyncTaskClasses.push({ ...task, taskVarName: `${fn.originalName}Task` });
1003
1007
  }
1004
1008
  }
1009
+ // Async METHODS on user classes: the body becomes an owner-bound task
1010
+ // (this->x renders as _owner->x inside segments); the in-class method
1011
+ // body is replaced by a call to a forward-declared starter that binds
1012
+ // the receiver and arms the machine (see class-emitter.ts).
1013
+ for (const cls of program.classes) {
1014
+ for (const method of cls.methods) {
1015
+ if (!method.isAsync)
1016
+ continue;
1017
+ const fnName = `${cls.name}_${method.name}`;
1018
+ if (method.isStatic) {
1019
+ emitDiagnostics.push({
1020
+ severity: "error",
1021
+ code: "async-method-static",
1022
+ message: `static async method ${cls.name}.${method.name}() is not supported — hoist it to a free async function instead.`,
1023
+ });
1024
+ continue;
1025
+ }
1026
+ if (method.parameters.length > 0) {
1027
+ emitDiagnostics.push({
1028
+ severity: "error",
1029
+ code: "async-method-params",
1030
+ message: `async method ${cls.name}.${method.name}() takes parameters, which the cooperative state machine cannot capture — make it parameterless.`,
1031
+ });
1032
+ continue;
1033
+ }
1034
+ if (method.returnType !== "void" && !String(method.returnType).startsWith("Promise")) {
1035
+ emitDiagnostics.push({
1036
+ severity: "error",
1037
+ code: "async-method-return",
1038
+ message: `async method ${cls.name}.${method.name}() returns ${method.returnType}; async methods lower to fire-and-forget tasks and must return void (Promise<void>).`,
1039
+ });
1040
+ continue;
1041
+ }
1042
+ const task = generateAsyncTaskClass(fnName, method.statements, strategy, knownFunctionReturnTypes, (stmt, forHeader, strategy, pointerVarTypes, calleeTransformer, knownReturnTypes) => {
1043
+ const contextRenderer = new StatementRenderer({
1044
+ strategy,
1045
+ boardConstants: program.boardConstants,
1046
+ classNameMap,
1047
+ enumNames,
1048
+ stringEnumNames,
1049
+ largeEnumNames,
1050
+ knownFunctionReturnTypes: knownReturnTypes ?? knownFunctionReturnTypes,
1051
+ knownVariableTypes: topLevelScope.knownVariableTypes,
1052
+ globalPointerVarTypes,
1053
+ namespaceNames,
1054
+ snprintfCounter,
1055
+ stringVarNames,
1056
+ varAccessorNames,
1057
+ typeAccessorNames: classAccessorNames,
1058
+ pointerVarTypes,
1059
+ diagnostics: emitDiagnostics,
1060
+ });
1061
+ const { prelude, statement } = contextRenderer.renderWithPrelude(stmt, forHeader, calleeTransformer);
1062
+ const body = prelude.length > 0 ? prelude.join("\n") + "\n" + statement : statement;
1063
+ // The task class is not the receiver: rebind member accesses from
1064
+ // the owning instance to the task's _owner pointer. The renderer
1065
+ // emits every this-form as `this->`; a rendered string literal
1066
+ // containing that token would be rewritten too (accepted edge).
1067
+ return body.replace(/this->/g, "_owner->");
1068
+ }, {
1069
+ ownerClassName: cls.name,
1070
+ onUnsupportedAwait: (callee, span) => {
1071
+ emitDiagnostics.push({
1072
+ severity: "error",
1073
+ code: "await-unsupported-call",
1074
+ message: `await ${callee}(...) has no cooperative lowering here — the call would be dropped. Await is supported on Time.sleep, HAL ops (wifi.join / wifi.scan / http.send), ui.onTap(), and pin-edge waits.`,
1075
+ ...(span ? { source: `${callee}(...)` } : {}),
1076
+ });
1077
+ },
1078
+ });
1079
+ asyncTaskClasses.push({ ...task, taskVarName: `${fnName}Task` });
1080
+ asyncMethodStarters.push({
1081
+ proto: `void __tc_async_start_${fnName}(${cls.name}* owner);`,
1082
+ });
1083
+ }
1084
+ }
1005
1085
  }
1006
1086
  // Additional includes computed from analysis
1007
- if (strategy.needsIostream() && programAnalysis.hasConsoleCalls) {
1008
- includes.push("<iostream>");
1009
- }
1010
1087
  const usesVectorTypes = programAnalysis.usesVectorTypes || programAnalysis.hasArrayInObjectLiteral;
1011
1088
  if (programAnalysis.usesStdString && strategy.needsStdString()) {
1012
1089
  includes.push("<string>");
@@ -1083,7 +1160,13 @@ export function buildEmitterContext(program, options) {
1083
1160
  // include in the AVR profile (which leaked the header into every AVR
1084
1161
  // program, even ones that never touch the watchdog like `led.toggle()`).
1085
1162
  if (programUsesWdt(program)) {
1086
- includes.push("<avr/wdt.h>");
1163
+ // Strategy-vetoed: framework-zephyr lowers wdt.* to the Zephyr watchdog
1164
+ // driver (<zephyr/drivers/watchdog.h>, forced under usesWDT) and must not
1165
+ // carry the AVR-only header into a Zephyr build.
1166
+ const wdtInc = strategy.filterRequiredIncludes
1167
+ ? strategy.filterRequiredIncludes(["<avr/wdt.h>"])
1168
+ : ["<avr/wdt.h>"];
1169
+ includes.push(...wdtInc);
1087
1170
  }
1088
1171
  // Placeholder defaults for fields that are computed later by other phases
1089
1172
  const noopFixPointer = (c) => c;
@@ -1166,6 +1249,7 @@ export function buildEmitterContext(program, options) {
1166
1249
  boardConstants: program.boardConstants,
1167
1250
  restParamFunctions: program.restParamFunctions,
1168
1251
  asyncTaskClasses,
1252
+ asyncMethodStarters,
1169
1253
  asyncFunctionOriginalNames,
1170
1254
  asyncFunctionMappedNames,
1171
1255
  hasAsyncRuntime,
@@ -411,7 +411,7 @@ export function runTopLevelPreprocessing(ctx) {
411
411
  // sees runtime mutations — e.g. `const reached = descended` freezes `reached`
412
412
  // at `descended`'s initial value because both are emitted as file-scope globals
413
413
  // initialized before main(), while the `descended = depth` assignments run
414
- // inside main(). See SUPPORT_MATRIX §6.1.
414
+ // inside main().
415
415
  {
416
416
  // Collect every variable name that is the target of an assignment anywhere
417
417
  // in the program (top-level statements + function/method bodies). Such
@@ -2,6 +2,7 @@ import { emitCommentLines, isRuntimeExpression } from "../utils/index.js";
2
2
  import { appendSourceLine, appendHeaderLine, appendRenderedStatement } from "./line-appender.js";
3
3
  import { escapeCppKeyword } from "../../utils/strings.js";
4
4
  import { isStringEnum } from "../../api/shared/index.js";
5
+ import { parsedIsVector } from "../../api/shared/cpp-type-ir.js";
5
6
  import { resolveEnumValues, narrowestEnumUnderlying } from "../utils/cpp-helpers.js";
6
7
  export function emitTypeDeclarations(ctx) {
7
8
  const { program, strategy, effectiveEmitMode, reservedNames, emittedTopLevelStatements, topLevelScope } = ctx;
@@ -249,14 +250,18 @@ export function emitTypeDeclarations(ctx) {
249
250
  const typeAlreadyConst = cppType.trimStart().startsWith("const");
250
251
  const constPrefix = isConst && !typeAlreadyConst ? "const " : "";
251
252
  const varName = escapeCppKeyword(statement.name, platformReservedNames);
252
- // Array-typed top-level consts (e.g. `export const ARR: T[] = [...]`)
253
- // lower with cppType "auto" and a `{ kind: "array", elementType }`
254
- // initializer. They emit as C-style arrays (`T name[] = {...}`), so the
255
- // matching extern is `extern const T name[];` not a scalar extern and
256
- // not a std::vector extern. Without this, cross-file consumers fail with
257
- // "was not declared in this scope" (the definition lives in the .cpp
258
- // only). Mirror renderVarDecl's element-type fallback for "auto".
259
- if (cppType === "auto" && statement.initializer && statement.initializer.kind === "array") {
253
+ // Array-typed top-level consts (e.g. `export const ARR: T[] = [...]`
254
+ // and `new Uint8Array(N)` buffers) lower with an `{ kind: "array",
255
+ // elementType }` initializer typed arrays carry a pointer cppType
256
+ // (`uint8_t*`) even though renderVarDecl emits the DEFINITION as a
257
+ // C-style array (`T name[] = {...}`). The matching extern must use the
258
+ // same array form regardless of cppType a pointer extern conflicts
259
+ // with the array definition. Vector-typed arrays are the exception: the
260
+ // definition emits `std::vector<T> name = {...}`, so the extern stays a
261
+ // vector extern. Without this, cross-file consumers fail with "was not
262
+ // declared in this scope" (the definition lives in the .cpp only).
263
+ // Mirror renderVarDecl's element-type fallback for "auto".
264
+ if (statement.initializer && statement.initializer.kind === "array" && !parsedIsVector(cppType)) {
260
265
  const elemType = statement.initializer.elementType && statement.initializer.elementType !== "auto"
261
266
  ? strategy.normalizeCppType(statement.initializer.elementType)
262
267
  : strategy.defaultNumericType(ctx.compliance.isEnabled() ? ctx.compliance : undefined);
@@ -163,7 +163,7 @@ export function emitUIRuntime(ctx) {
163
163
  if (profile) {
164
164
  const adapter = generateDisplayAdapter(profile, ctx.strategy);
165
165
  // Prepend includes to the very front — Arduino's auto-prototyper scans
166
- // the whole .ino and generates prototypes that reference GFXcanvas16
166
+ // the whole translation unit and generates prototypes that reference GFXcanvas16
167
167
  // etc. before any #include, so the GFX header must come first.
168
168
  ctx.sourceLines.unshift(adapter.includes);
169
169
  // Color-depth preamble: UI_COLOR_T is referenced by adapter draw