@typecad/cuttlefish 1.0.0-alpha.14 → 1.0.0-alpha.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +82 -79
- package/assets/editor-extensions/typecad-debug/README.md +25 -26
- package/dist/api/config.d.ts +22 -26
- package/dist/api/schema/types.d.ts +20 -3
- package/dist/api/shared/async-runtime-static.d.ts +1 -1
- package/dist/api/shared/async-runtime-static.js +5 -5
- package/dist/api/shared/async-types.d.ts +3 -2
- package/dist/api/shared/board-catalog.d.ts +2 -0
- package/dist/api/shared/board-catalog.js +6 -0
- package/dist/api/shared/board-resolver.d.ts +4 -7
- package/dist/api/shared/board-resolver.js +2 -1
- package/dist/api/shared/coop-scheduler.d.ts +1 -1
- package/dist/api/shared/coop-scheduler.js +1 -1
- package/dist/api/shared/cpp-type-ir.d.ts +1 -1
- package/dist/api/shared/cpp-type-ir.js +1 -1
- package/dist/api/shared/display-profile.d.ts +1 -1
- package/dist/api/shared/display-profile.js +3 -5
- package/dist/api/shared/framework-manifest-registry.d.ts +1 -1
- package/dist/api/shared/framework-manifest-registry.js +0 -1
- package/dist/api/shared/framework-manifest.d.ts +7 -6
- package/dist/api/shared/framework-manifest.js +6 -11
- package/dist/api/shared/hal-op-ir.d.ts +294 -685
- package/dist/api/shared/hal-op-ir.js +31 -65
- package/dist/api/shared/index.d.ts +0 -4
- package/dist/api/shared/index.js +1 -7
- package/dist/api/shared/ir-core.d.ts +6 -0
- package/dist/api/shared/ir-declarations.d.ts +4 -0
- package/dist/api/shared/ir.d.ts +1 -3
- package/dist/api/shared/platform-strategy.d.ts +14 -37
- package/dist/api/shared/polyfill-helper-registry.js +0 -5
- package/dist/api/shared/polyfill-types.js +0 -27
- package/dist/api/shared/promise-runtime.js +3 -3
- package/dist/api/shared/spdx-licenses.d.ts +12 -10
- package/dist/api/shared/spdx-licenses.js +1 -1
- package/dist/api/shared/toolchain-types.d.ts +1 -1
- package/dist/api/shared/toolchain-types.js +3 -3
- package/dist/api/shared/types.d.ts +0 -3
- package/dist/api/shared/validate-framework-manifest.js +13 -33
- package/dist/board-catalog/dts-reader.d.ts +220 -0
- package/dist/board-catalog/dts-reader.js +1052 -0
- package/dist/board-catalog/index.d.ts +11 -0
- package/dist/board-catalog/index.js +12 -0
- package/dist/board-catalog/pinconfig.d.ts +48 -0
- package/dist/board-catalog/pinconfig.js +263 -0
- package/dist/board-catalog/sdk.d.ts +82 -0
- package/dist/board-catalog/sdk.js +180 -0
- package/dist/board-catalog/store.d.ts +196 -0
- package/dist/board-catalog/store.js +363 -0
- package/dist/board-catalog/types.d.ts +233 -0
- package/dist/board-catalog/types.js +7 -0
- package/dist/board-catalog/walker.d.ts +112 -0
- package/dist/board-catalog/walker.js +1101 -0
- package/dist/cli-utils.d.ts +2 -2
- package/dist/cli-utils.js +5 -5
- package/dist/cli.js +219 -242
- package/dist/config-loader.d.ts +14 -19
- package/dist/config-loader.js +240 -78
- package/dist/config-schema.d.ts +3 -5
- package/dist/config-schema.js +8 -8
- package/dist/contract/board-generator.d.ts +4 -4
- package/dist/contract/board-generator.js +16 -5
- package/dist/contract/contract-parser.d.ts +27 -0
- package/dist/contract/contract-parser.js +76 -0
- package/dist/contract/index.d.ts +1 -1
- package/dist/contract/index.js +70 -21
- package/dist/create/active-board-catalog.d.ts +9 -0
- package/dist/create/active-board-catalog.js +94 -0
- package/dist/create/board-catalog.generated.d.ts +96 -0
- package/dist/create/board-catalog.generated.js +128737 -0
- package/dist/create/board-search.d.ts +23 -0
- package/dist/create/board-search.js +88 -0
- package/dist/create/editor-integration.d.ts +4 -2
- package/dist/create/editor-integration.js +41 -2
- package/dist/create/framework-catalog.d.ts +57 -9
- package/dist/create/framework-catalog.js +76 -57
- package/dist/create/index.d.ts +5 -11
- package/dist/create/index.js +3 -8
- package/dist/create/mcu-target.d.ts +39 -0
- package/dist/create/mcu-target.js +80 -0
- package/dist/create/pack-targets.d.ts +19 -0
- package/dist/create/pack-targets.js +56 -0
- package/dist/create/scaffold.d.ts +4 -7
- package/dist/create/scaffold.js +19 -109
- package/dist/create/templates.d.ts +32 -5
- package/dist/create/templates.js +130 -116
- package/dist/create/wizard.d.ts +12 -1
- package/dist/create/wizard.js +168 -38
- package/dist/debug/preprocessor.d.ts +5 -0
- package/dist/debug/preprocessor.js +31 -7
- package/dist/diagnostics/diagnostics-report.d.ts +1 -1
- package/dist/diagnostics/diagnostics-report.js +3 -11
- package/dist/diagnostics/json-schema.d.ts +1 -1
- package/dist/diagnostics/md-writer.js +2 -2
- package/dist/diagnostics/mermaid-builder.d.ts +1 -1
- package/dist/diagnostics/mermaid-builder.js +1 -1
- package/dist/emit/cpp-emitter.js +4 -1
- package/dist/emit/emitters/class-emitter.js +32 -4
- package/dist/emit/emitters/emitter-context.d.ts +11 -0
- package/dist/emit/emitters/entrypoint-synthesizer.js +1 -1
- package/dist/emit/emitters/function-emitter-impl.js +33 -17
- package/dist/emit/emitters/output-finalizer.d.ts +7 -0
- package/dist/emit/emitters/output-finalizer.js +31 -23
- package/dist/emit/emitters/setup.js +125 -63
- package/dist/emit/emitters/top-level-prep.js +1 -1
- package/dist/emit/emitters/type-decl-emitter.js +13 -8
- package/dist/emit/emitters/ui-emitter.js +1 -1
- package/dist/emit/expression-renderer.d.ts +3 -3
- package/dist/emit/expression-renderer.js +17 -7
- package/dist/emit/route-hal-op.js +17 -48
- package/dist/emit/snprintf-helpers.d.ts +1 -3
- package/dist/emit/snprintf-helpers.js +1 -39
- package/dist/emit/statement-renderer.d.ts +0 -4
- package/dist/emit/statement-renderer.js +54 -45
- package/dist/emit/utils/async-state-machine.d.ts +16 -9
- package/dist/emit/utils/async-state-machine.js +56 -54
- package/dist/emit/utils/comment-helpers.d.ts +0 -8
- package/dist/emit/utils/comment-helpers.js +1 -1
- package/dist/emit/utils/cpp-helpers.d.ts +2 -2
- package/dist/emit/utils/cpp-helpers.js +2 -2
- package/dist/emit/utils/hal-op-cpp-type.js +4 -6
- package/dist/emit/utils/index.d.ts +3 -3
- package/dist/emit/utils/index.js +3 -3
- package/dist/emit/utils/type-inference.d.ts +1 -29
- package/dist/emit/utils/type-inference.js +0 -75
- package/dist/framework-package.d.ts +3 -0
- package/dist/framework-package.js +23 -6
- package/dist/framework-registry.d.ts +5 -5
- package/dist/frameworks/native/graphics/terminal-preview.d.ts +5 -0
- package/dist/frameworks/native/graphics/terminal-preview.js +33 -0
- package/dist/frameworks/native/index.d.ts +3 -0
- package/dist/frameworks/native/index.js +15 -0
- package/dist/frameworks/native/native-compile.d.ts +17 -0
- package/dist/frameworks/native/native-compile.js +170 -0
- package/dist/frameworks/native/native-config.d.ts +41 -0
- package/dist/frameworks/native/native-config.js +7 -0
- package/dist/frameworks/native/strategy.d.ts +97 -0
- package/dist/frameworks/native/strategy.js +664 -0
- package/dist/ir/board-resolver.d.ts +18 -23
- package/dist/ir/board-resolver.js +43 -401
- package/dist/ir/build-ir-state.d.ts +3 -0
- package/dist/ir/build-ir-state.js +42 -0
- package/dist/ir/build-ir.d.ts +1 -1
- package/dist/ir/build-ir.js +40 -42
- package/dist/ir/declaration-builders.js +1 -0
- package/dist/ir/expression-to-ir.js +17 -10
- package/dist/ir/hal/hal-emitter.d.ts +0 -2
- package/dist/ir/hal/hal-emitter.js +92 -41
- package/dist/ir/hal/hal-parser.d.ts +5 -5
- package/dist/ir/hal/hal-parser.js +128 -36
- package/dist/ir/hal/hal-plugins.d.ts +0 -1
- package/dist/ir/hal/hal-plugins.js +425 -716
- package/dist/ir/heap-analysis.js +2 -41
- package/dist/ir/identifier-collector.js +32 -20
- package/dist/ir/network-validation.js +7 -11
- package/dist/ir/ownership-analysis.js +75 -13
- package/dist/ir/peripheral-usage.d.ts +8 -2
- package/dist/ir/peripheral-usage.js +78 -149
- package/dist/ir/peripheral-validation.js +23 -3
- package/dist/ir/pin-capability-validation.d.ts +12 -0
- package/dist/ir/pin-capability-validation.js +61 -97
- package/dist/ir/pin-mode-validation.js +5 -5
- package/dist/ir/program-analysis.d.ts +8 -15
- package/dist/ir/program-analysis.js +140 -323
- package/dist/ir/pulldown-validation.js +11 -4
- package/dist/ir/resource-analysis.js +15 -3
- package/dist/ir/statement-to-ir.js +0 -6
- package/dist/ir/timing-validation.js +1 -1
- package/dist/ir/transformers/array-methods.js +11 -5
- package/dist/ir/transformers/call-statement.js +20 -87
- package/dist/ir/transformers/callback-context-registry.d.ts +1 -1
- package/dist/ir/transformers/callback-context-registry.js +8 -16
- package/dist/ir/transformers/control-flow.js +0 -13
- package/dist/ir/transformers/expressions.js +10 -9
- package/dist/ir/transformers/hal-call-resolver.d.ts +2 -2
- package/dist/ir/transformers/hal-call-resolver.js +24 -10
- package/dist/ir/transformers/hal-emit-helpers.d.ts +2 -2
- package/dist/ir/transformers/hal-emit-helpers.js +2 -2
- package/dist/ir/transformers/namespace-methods.js +1 -1
- package/dist/ir/transformers/ui-callback-lowering.d.ts +0 -2
- package/dist/ir/transformers/ui-callback-lowering.js +0 -24
- package/dist/ir/transformers/variables.js +338 -42
- package/dist/ir/try-catch-validation.js +4 -3
- package/dist/ir/type-resolution.js +15 -4
- package/dist/ir/validation-orchestrator.js +0 -9
- package/dist/libdef/cpp-to-decl.d.ts +0 -9
- package/dist/libdef/cpp-to-decl.js +0 -72
- package/dist/libdef/registry.d.ts +0 -1
- package/dist/libdef/registry.js +1 -24
- package/dist/library/init.d.ts +1 -1
- package/dist/library/init.js +137 -137
- package/dist/library-packages.d.ts +1 -1
- package/dist/library-packages.js +1 -1
- package/dist/mapping/peripheral-names.js +9 -4
- package/dist/mapping/source-map.d.ts +3 -4
- package/dist/mapping/source-map.js +12 -18
- package/dist/orchestrator/graph-builder.d.ts +3 -3
- package/dist/orchestrator/graph-builder.js +13 -12
- package/dist/orchestrator/type-checker.d.ts +3 -3
- package/dist/orchestrator/type-checker.js +3 -3
- package/dist/platform/async-runtime.js +1 -1
- package/dist/platform/coop-scheduler-runtime.js +2 -2
- package/dist/platform/generic-debug-codegen.d.ts +36 -0
- package/dist/platform/generic-debug-codegen.js +185 -0
- package/dist/platform/generic-strategy.d.ts +3 -4
- package/dist/platform/generic-strategy.js +5 -80
- package/dist/platform/index.js +1 -1
- package/dist/platform/registry.d.ts +2 -2
- package/dist/platform/registry.js +2 -2
- package/dist/safety/asil-decorators.d.ts +32 -0
- package/dist/safety/asil-decorators.js +32 -0
- package/dist/safety/authoring.d.ts +85 -0
- package/dist/safety/authoring.js +61 -0
- package/dist/safety/engine.d.ts +3 -0
- package/dist/safety/engine.js +83 -0
- package/dist/safety/hal/ops.d.ts +48 -0
- package/dist/safety/hal/ops.js +46 -0
- package/dist/safety/iso26262/analyze.d.ts +13 -0
- package/dist/safety/iso26262/analyze.js +29 -0
- package/dist/safety/iso26262/asil.d.ts +10 -0
- package/dist/safety/iso26262/asil.js +22 -0
- package/dist/safety/iso26262/collect.d.ts +14 -0
- package/dist/safety/iso26262/collect.js +137 -0
- package/dist/safety/iso26262/goto-checker.d.ts +10 -0
- package/dist/safety/iso26262/goto-checker.js +72 -0
- package/dist/safety/iso26262/heap-checker.d.ts +7 -0
- package/dist/safety/iso26262/heap-checker.js +57 -0
- package/dist/safety/iso26262/init-checker.d.ts +13 -0
- package/dist/safety/iso26262/init-checker.js +127 -0
- package/dist/safety/iso26262/loop-checker.d.ts +7 -0
- package/dist/safety/iso26262/loop-checker.js +86 -0
- package/dist/safety/iso26262/recursion-checker.d.ts +9 -0
- package/dist/safety/iso26262/recursion-checker.js +134 -0
- package/dist/safety/iso26262/sidecar-writer.d.ts +25 -0
- package/dist/safety/iso26262/sidecar-writer.js +13 -0
- package/dist/safety/passes/pinMode-intercept.d.ts +3 -0
- package/dist/safety/passes/pinMode-intercept.js +60 -0
- package/dist/safety/runtime/mode-table.d.ts +5 -0
- package/dist/safety/runtime/mode-table.js +55 -0
- package/dist/safety/runtime/polyfills.d.ts +6 -0
- package/dist/safety/runtime/polyfills.js +12 -0
- package/dist/safety/runtime/result-struct.d.ts +11 -0
- package/dist/safety/runtime/result-struct.js +98 -0
- package/dist/safety/runtime/safe-int.d.ts +33 -0
- package/dist/safety/runtime/safe-int.js +170 -0
- package/dist/safety/runtime/safe-traits.d.ts +4 -0
- package/dist/safety/runtime/safe-traits.js +95 -0
- package/dist/safety/runtime/safe-variable.d.ts +39 -0
- package/dist/safety/runtime/safe-variable.js +226 -0
- package/dist/safety/runtime/vote.d.ts +8 -0
- package/dist/safety/runtime/vote.js +92 -0
- package/dist/safety/runtime/write-verify.d.ts +8 -0
- package/dist/safety/runtime/write-verify.js +71 -0
- package/dist/safety/safe-int-types.d.ts +45 -0
- package/dist/safety/safe-int-types.js +25 -0
- package/dist/safety/safe-variable-types.d.ts +29 -0
- package/dist/safety/safe-variable-types.js +19 -0
- package/dist/safety/safety-bridge.d.ts +5 -6
- package/dist/safety/safety-bridge.js +15 -28
- package/dist/safety/sidecar-bridge.d.ts +1 -3
- package/dist/safety/sidecar-bridge.js +3 -20
- package/dist/safety/specifiers.d.ts +6 -0
- package/dist/safety/specifiers.js +14 -0
- package/dist/safety-hook.d.ts +8 -7
- package/dist/safety-hook.js +12 -13
- package/dist/testing.d.ts +4 -5
- package/dist/testing.js +5 -5
- package/dist/transpile/resolution.d.ts +1 -1
- package/dist/transpile/resolution.js +18 -9
- package/dist/transpile/test-pins.d.ts +18 -0
- package/dist/transpile/test-pins.js +142 -0
- package/dist/transpile.d.ts +1 -2
- package/dist/transpile.js +36 -30
- package/dist/types.d.ts +26 -26
- package/dist/ui-hook.d.ts +1 -1
- package/dist/utils/cli.d.ts +2 -2
- package/dist/utils/cli.js +92 -154
- package/dist/utils/fs.d.ts +3 -4
- package/dist/utils/fs.js +4 -5
- package/dist/utils/strings.d.ts +0 -1
- package/dist/utils/strings.js +0 -8
- package/dist/utils/ui.d.ts +5 -2
- package/dist/utils/ui.js +13 -3
- package/package.json +158 -157
- package/dist/add-preset.d.ts +0 -4
- package/dist/add-preset.js +0 -74
- package/dist/api/shared/async-symbol-detector.d.ts +0 -11
- package/dist/api/shared/async-symbol-detector.js +0 -140
- package/dist/api/shared/native-display-op-resolver.d.ts +0 -10
- package/dist/api/shared/native-display-op-resolver.js +0 -64
- package/dist/create/board-checklist.d.ts +0 -2
- package/dist/create/board-checklist.js +0 -52
- package/dist/create/board-codegen.d.ts +0 -9
- package/dist/create/board-codegen.js +0 -238
- package/dist/create/board-generators.d.ts +0 -12
- package/dist/create/board-generators.js +0 -651
- package/dist/create/board-spec.d.ts +0 -181
- package/dist/create/board-spec.js +0 -214
- package/dist/install/framework-catalog.d.ts +0 -53
- package/dist/install/framework-catalog.js +0 -107
- package/dist/install/handle-install.d.ts +0 -35
- package/dist/install/handle-install.js +0 -177
- package/dist/install/index.d.ts +0 -4
- package/dist/install/index.js +0 -3
- package/dist/ir/pin-state-tracking.d.ts +0 -58
- package/dist/ir/pin-state-tracking.js +0 -182
- package/dist/ir/pwm-timer-sharing.d.ts +0 -4
- package/dist/ir/pwm-timer-sharing.js +0 -94
- package/dist/ir/timer0-pwm-timing-conflict.d.ts +0 -4
- package/dist/ir/timer0-pwm-timing-conflict.js +0 -72
- package/dist/libdef/component-decls.d.ts +0 -2
- package/dist/libdef/component-decls.js +0 -6
- package/dist/libdef/component-discovery.d.ts +0 -43
- package/dist/libdef/component-discovery.js +0 -83
- package/dist/theme-tokens.d.ts +0 -22
- 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
|
|
315
|
-
//
|
|
316
|
-
//
|
|
317
|
-
//
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
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
|
|
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,
|
|
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,
|
|
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,
|
|
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
|
|
144
|
-
|
|
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
|
|
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,6 +1,7 @@
|
|
|
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";
|
|
@@ -240,6 +241,11 @@ function extractShimMacro(lines, macroName) {
|
|
|
240
241
|
return lines.slice(ifndefIdx, endIdx + 1);
|
|
241
242
|
}
|
|
242
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);
|
|
243
249
|
const largeEnumNames = new Set();
|
|
244
250
|
const enumNames = new Set();
|
|
245
251
|
const stringEnumNames = new Set();
|
|
@@ -313,23 +319,6 @@ export function buildEmitterContext(program, options) {
|
|
|
313
319
|
})();
|
|
314
320
|
const programAnalysis = analyzeProgram(program, strategy);
|
|
315
321
|
if (options.platformContext) {
|
|
316
|
-
// The UI runtime's per-frame tick calls millis() (injected by the emitter,
|
|
317
|
-
// not present in user source), so a mounted UI needs the native millis ISR
|
|
318
|
-
// even when usesNativeTiming is false. OR entryHasUI() into the flag the
|
|
319
|
-
// AVR strategy reads when gating native_millis, so framework-avr doesn't
|
|
320
|
-
// drop the Timer0 ISR from a UI program.
|
|
321
|
-
if (!programAnalysis.usesNativeTiming && entryHasUI()) {
|
|
322
|
-
programAnalysis.usesNativeTiming = true;
|
|
323
|
-
}
|
|
324
|
-
// The UI runtime header calls constrain() in the progress/range node draw
|
|
325
|
-
// and touch-slider paths (injected by the emitter, not present in user
|
|
326
|
-
// source), so a mounted UI needs the constrain polyfill even when
|
|
327
|
-
// usesConstrain is false. OR entryHasUI() into the flag the native
|
|
328
|
-
// strategy reads when gating the constrain helper, mirroring the
|
|
329
|
-
// usesNativeTiming handling above.
|
|
330
|
-
if (!programAnalysis.usesConstrain && entryHasUI()) {
|
|
331
|
-
programAnalysis.usesConstrain = true;
|
|
332
|
-
}
|
|
333
322
|
options.platformContext.analysis = programAnalysis;
|
|
334
323
|
if (!options.platformContext.architecture && program.boardConstants) {
|
|
335
324
|
options.platformContext.architecture = program.boardConstants.get("architecture");
|
|
@@ -368,16 +357,11 @@ export function buildEmitterContext(program, options) {
|
|
|
368
357
|
if (!programAnalysis.usedPolyfillHelpers.has('__tc_StaticArray')) {
|
|
369
358
|
filteredNativePolyfills = filteredNativePolyfills.filter(p => p.id !== "static_array");
|
|
370
359
|
}
|
|
371
|
-
const usesTimers = programAnalysis.usedPolyfillHelpers.has('__tc_setInterval') ||
|
|
372
|
-
programAnalysis.usedPolyfillHelpers.has('__tc_setTimeout');
|
|
373
|
-
if (!usesTimers) {
|
|
374
|
-
filteredNativePolyfills = filteredNativePolyfills.filter(p => p.id !== "timer_methods");
|
|
375
|
-
}
|
|
376
360
|
// Safety polyfills (mode table + voter + SafeVariable + SafeInt). Gated on
|
|
377
|
-
// programUsesSafety(program) so they ONLY appear when the
|
|
361
|
+
// programUsesSafety(program) so they ONLY appear when the program actually
|
|
378
362
|
// references a safety construct — emitting them unconditionally (just because
|
|
379
363
|
// @typecad/safety is installed and registered its hook) leaks ~200 lines of
|
|
380
|
-
// polyfill into every
|
|
364
|
+
// polyfill into every program, and SafeInt's `return *this` chaining methods
|
|
381
365
|
// tripped byte-identity / lowering assertions that expect no `this` token.
|
|
382
366
|
// programUsesSafety walks the IR for any safety.* hal-op.
|
|
383
367
|
if (isEntryFile && hasSafetyHook() && programUsesSafety(program)) {
|
|
@@ -393,8 +377,7 @@ export function buildEmitterContext(program, options) {
|
|
|
393
377
|
// minimal-libc targets (Zephyr) where the Promise runtime does not.
|
|
394
378
|
const coopConfig = strategy.getAsyncRuntimeConfig?.();
|
|
395
379
|
const hasCoopWork = program.functions.some(fn => fn.isAsync) ||
|
|
396
|
-
|
|
397
|
-
programAnalysis.usedPolyfillHelpers.has('__tc_setTimeout');
|
|
380
|
+
program.classes.some(c => c.methods.some(m => m.isAsync));
|
|
398
381
|
if (isEntryFile && coopConfig && hasCoopWork) {
|
|
399
382
|
const coop = buildCoopSchedulerPolyfill(program, coopConfig, strategy.currentTimeMillis());
|
|
400
383
|
if (coop)
|
|
@@ -404,8 +387,13 @@ export function buildEmitterContext(program, options) {
|
|
|
404
387
|
const emittedPolyfills = allPolyfills.length > 0
|
|
405
388
|
? emitPolyfillBoilerplate(allPolyfills)
|
|
406
389
|
: undefined;
|
|
407
|
-
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));
|
|
408
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;
|
|
409
397
|
if (!isNpmPackage) {
|
|
410
398
|
includes.push(...strategy.forcedIncludes(program, options.platformContext));
|
|
411
399
|
Object.assign(symbolMap, strategy.symbolAliases(program, options.platformContext));
|
|
@@ -424,16 +412,6 @@ export function buildEmitterContext(program, options) {
|
|
|
424
412
|
if (!programAnalysis.usesDateNow) {
|
|
425
413
|
shimLines = shimLines.filter(l => !l.includes('namespace Date'));
|
|
426
414
|
}
|
|
427
|
-
// The UI runtime's per-frame tick uses millis() (injected by the emitter,
|
|
428
|
-
// not authored in user source), so keep the millis() shim when a UI is
|
|
429
|
-
// mounted even if the source-level analysis didn't flag usesMillis. The
|
|
430
|
-
// async runtime and the setInterval/setTimeout scheduler also poll
|
|
431
|
-
// millis() without any user-source millis() call (Async.sleep lowers to a
|
|
432
|
-
// raw hal-op the timing scanners can't see) — mirror the usesNativeTiming
|
|
433
|
-
// derivation and keep the shim for those hidden consumers too.
|
|
434
|
-
if (!programAnalysis.usesMillis && !programAnalysis.hasAsync && programAnalysis.timerCallCount === 0 && !entryHasUI()) {
|
|
435
|
-
shimLines = shimLines.filter(l => !l.includes('millis()'));
|
|
436
|
-
}
|
|
437
415
|
// Strip the nullish helper FUNCTIONS (not the CUTTLEFISH_UNDEFINED macro)
|
|
438
416
|
// when the file doesn't actually emit cuttlefish_nullish(...) calls. A
|
|
439
417
|
// file that only references `null`/`undefined` literals needs just the
|
|
@@ -445,6 +423,14 @@ export function buildEmitterContext(program, options) {
|
|
|
445
423
|
// cuttlefish_nullish( call (a more reliable post-emit check than the
|
|
446
424
|
// per-file pre-emit analysis flag). The logic here governs the .cpp
|
|
447
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
|
+
}
|
|
448
434
|
if (!programAnalysis.usesNullishHelper) {
|
|
449
435
|
const filtered = [];
|
|
450
436
|
for (let i = 0; i < shimLines.length; i++) {
|
|
@@ -458,9 +444,6 @@ export function buildEmitterContext(program, options) {
|
|
|
458
444
|
if (!programAnalysis.usesNum) {
|
|
459
445
|
shimLines = filterShimBlock(shimLines, 'struct __tc_Num {', '} Num;');
|
|
460
446
|
}
|
|
461
|
-
if (!programAnalysis.usesTiming) {
|
|
462
|
-
shimLines = filterShimBlock(shimLines, 'struct __tc_Timing {', '} Timing;');
|
|
463
|
-
}
|
|
464
447
|
if (!programAnalysis.usesWDT) {
|
|
465
448
|
shimLines = filterShimBlock(shimLines, '#include <avr/wdt.h>', '} WDT;');
|
|
466
449
|
}
|
|
@@ -473,9 +456,7 @@ export function buildEmitterContext(program, options) {
|
|
|
473
456
|
// these filters are no-ops there. The strategies also self-gate on the
|
|
474
457
|
// same flags; this is the defensive backstop (mirrors how usesWDT/etc.
|
|
475
458
|
// backstop the strategy-side gating above).
|
|
476
|
-
|
|
477
|
-
// call _uart_* functions) even if no direct uart.* HAL ops are used.
|
|
478
|
-
if (!programAnalysis.usesUart && !programAnalysis.hasConsoleCalls) {
|
|
459
|
+
if (!programAnalysis.usesUart) {
|
|
479
460
|
shimLines = filterShimBlock(shimLines, '// CUTTLEFISH_UART_BEGIN', '// CUTTLEFISH_UART_END');
|
|
480
461
|
shimLines = filterShimBlock(shimLines, '// CUTTLEFISH_UART_EXT_BEGIN', '// CUTTLEFISH_UART_EXT_END');
|
|
481
462
|
}
|
|
@@ -485,14 +466,8 @@ export function buildEmitterContext(program, options) {
|
|
|
485
466
|
if (!programAnalysis.usesI2C) {
|
|
486
467
|
shimLines = filterShimBlock(shimLines, '// CUTTLEFISH_TWI_BEGIN', '// CUTTLEFISH_TWI_END');
|
|
487
468
|
}
|
|
488
|
-
if (!programAnalysis.usesEEPROM && !programAnalysis.usesPreferences) {
|
|
489
|
-
shimLines = filterShimBlock(shimLines, '// CUTTLEFISH_EEPROM_BEGIN', '// CUTTLEFISH_EEPROM_END');
|
|
490
|
-
}
|
|
491
|
-
if (!programAnalysis.usesTone) {
|
|
492
|
-
shimLines = filterShimBlock(shimLines, '// CUTTLEFISH_TONE_BEGIN', '// CUTTLEFISH_TONE_END');
|
|
493
|
-
}
|
|
494
469
|
// framework-esp32 native peripheral driver shims. Same defensive-backstop
|
|
495
|
-
// pattern as the UART/SPI/TWI/EEPROM
|
|
470
|
+
// pattern as the UART/SPI/TWI/EEPROM blocks above. framework-arduino's
|
|
496
471
|
// and framework-avr's shimLines contain none of these markers, so these
|
|
497
472
|
// filters are no-ops there.
|
|
498
473
|
if (!programAnalysis.usesGPIO) {
|
|
@@ -507,9 +482,6 @@ export function buildEmitterContext(program, options) {
|
|
|
507
482
|
if (!programAnalysis.usesDAC) {
|
|
508
483
|
shimLines = filterShimBlock(shimLines, '// CUTTLEFISH_DAC_BEGIN', '// CUTTLEFISH_DAC_END');
|
|
509
484
|
}
|
|
510
|
-
if (!programAnalysis.usesPower) {
|
|
511
|
-
shimLines = filterShimBlock(shimLines, '// CUTTLEFISH_POWER_BEGIN', '// CUTTLEFISH_POWER_END');
|
|
512
|
-
}
|
|
513
485
|
if (!programAnalysis.usesWdt) {
|
|
514
486
|
shimLines = filterShimBlock(shimLines, '// CUTTLEFISH_WDT_BEGIN', '// CUTTLEFISH_WDT_END');
|
|
515
487
|
}
|
|
@@ -608,7 +580,7 @@ export function buildEmitterContext(program, options) {
|
|
|
608
580
|
// time; the package emits NO C++ module/header. Register the imported
|
|
609
581
|
// symbols so references resolve, but skip the #include generation.
|
|
610
582
|
if (imported.moduleSpecifier === "@typecad/ui" ||
|
|
611
|
-
imported.moduleSpecifier
|
|
583
|
+
isSafetyImportSpecifier(imported.moduleSpecifier)) {
|
|
612
584
|
for (const symbol of imported.namedImports) {
|
|
613
585
|
symbolMap[symbol] = symbol;
|
|
614
586
|
}
|
|
@@ -702,11 +674,11 @@ export function buildEmitterContext(program, options) {
|
|
|
702
674
|
// Map a function's original TS name to its emitted C++ name, applying the
|
|
703
675
|
// strategy's user-function renames. The entrypoint sentinel
|
|
704
676
|
// `__cuttlefish_entrypoint__` maps to the strategy's entrypoint name
|
|
705
|
-
// (
|
|
677
|
+
// (per-target entrypoint names); every other name is routed through
|
|
706
678
|
// `strategy.mapFunctionName`, which is where a target renames a user
|
|
707
679
|
// function that would otherwise collide with a C++/framework reserved name
|
|
708
|
-
// — e.g.
|
|
709
|
-
// because
|
|
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
|
|
710
682
|
// `setup()`/`loop()`), and a file-scope `static void main()` collides with
|
|
711
683
|
// C++'s required `int main()` signature. Call-site renames are applied
|
|
712
684
|
// uniformly in StatementRenderer.renderCall (the single call-rendering
|
|
@@ -879,7 +851,7 @@ export function buildEmitterContext(program, options) {
|
|
|
879
851
|
// method/ctor parameter, or a top-level binding. Previously only local
|
|
880
852
|
// var_decls were registered (in class-emitter), which ran too late for the
|
|
881
853
|
// free-function pass — leaving `hero->alive` unrewritten and failing g++.
|
|
882
|
-
//
|
|
854
|
+
// (demo #4 fix.)
|
|
883
855
|
// `classAccessorNames` is built at the outer scope (not inside a nested
|
|
884
856
|
// block) so it can also be passed to the expression renderer as
|
|
885
857
|
// `typeAccessorNames` — the type-keyed fallback used when a getter access's
|
|
@@ -989,6 +961,7 @@ export function buildEmitterContext(program, options) {
|
|
|
989
961
|
const mapFunctionNameLocal = (originalName) => statementRenderer.mapFunctionName(originalName);
|
|
990
962
|
const asyncFunctionMappedNames = new Set(program.functions.filter((fn) => fn.isAsync).map((fn) => mapFunctionNameLocal(fn.originalName)));
|
|
991
963
|
const asyncTaskClasses = [];
|
|
964
|
+
const asyncMethodStarters = [];
|
|
992
965
|
if (hasAsyncRuntime) {
|
|
993
966
|
for (const fn of program.functions) {
|
|
994
967
|
if (fn.isAsync) {
|
|
@@ -1014,21 +987,103 @@ export function buildEmitterContext(program, options) {
|
|
|
1014
987
|
// Use renderWithPrelude so snprintf buffer declarations (e.g.
|
|
1015
988
|
// char __cuttlefish_str_N[...]; snprintf(...)) are emitted before
|
|
1016
989
|
// the statement that references them. Without this, template-literal
|
|
1017
|
-
//
|
|
990
|
+
// string building inside async functions loses the buffer declaration.
|
|
1018
991
|
const { prelude, statement } = contextRenderer.renderWithPrelude(stmt, forHeader, calleeTransformer);
|
|
1019
992
|
if (prelude.length > 0) {
|
|
1020
993
|
return prelude.join("\n") + "\n" + statement;
|
|
1021
994
|
}
|
|
1022
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
|
+
},
|
|
1023
1005
|
});
|
|
1024
1006
|
asyncTaskClasses.push({ ...task, taskVarName: `${fn.originalName}Task` });
|
|
1025
1007
|
}
|
|
1026
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
|
+
}
|
|
1027
1085
|
}
|
|
1028
1086
|
// Additional includes computed from analysis
|
|
1029
|
-
if (strategy.needsIostream() && programAnalysis.hasConsoleCalls) {
|
|
1030
|
-
includes.push("<iostream>");
|
|
1031
|
-
}
|
|
1032
1087
|
const usesVectorTypes = programAnalysis.usesVectorTypes || programAnalysis.hasArrayInObjectLiteral;
|
|
1033
1088
|
if (programAnalysis.usesStdString && strategy.needsStdString()) {
|
|
1034
1089
|
includes.push("<string>");
|
|
@@ -1105,7 +1160,13 @@ export function buildEmitterContext(program, options) {
|
|
|
1105
1160
|
// include in the AVR profile (which leaked the header into every AVR
|
|
1106
1161
|
// program, even ones that never touch the watchdog like `led.toggle()`).
|
|
1107
1162
|
if (programUsesWdt(program)) {
|
|
1108
|
-
|
|
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);
|
|
1109
1170
|
}
|
|
1110
1171
|
// Placeholder defaults for fields that are computed later by other phases
|
|
1111
1172
|
const noopFixPointer = (c) => c;
|
|
@@ -1188,6 +1249,7 @@ export function buildEmitterContext(program, options) {
|
|
|
1188
1249
|
boardConstants: program.boardConstants,
|
|
1189
1250
|
restParamFunctions: program.restParamFunctions,
|
|
1190
1251
|
asyncTaskClasses,
|
|
1252
|
+
asyncMethodStarters,
|
|
1191
1253
|
asyncFunctionOriginalNames,
|
|
1192
1254
|
asyncFunctionMappedNames,
|
|
1193
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().
|
|
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
|
-
//
|
|
254
|
-
//
|
|
255
|
-
//
|
|
256
|
-
//
|
|
257
|
-
//
|
|
258
|
-
//
|
|
259
|
-
|
|
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
|
|
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
|
|
@@ -188,8 +188,8 @@ export declare class ExpressionRenderer {
|
|
|
188
188
|
* String-enum operands are passed through unchanged (they're `const char*`).
|
|
189
189
|
*
|
|
190
190
|
* This is the assignment/argument-site counterpart to the operator-level
|
|
191
|
-
* enum wrapping in renderBinary.
|
|
192
|
-
*
|
|
191
|
+
* enum wrapping in renderBinary. Enum values used as integers need an
|
|
192
|
+
* explicit cast in C++.
|
|
193
193
|
*/
|
|
194
194
|
renderEnumSafeValue(expr: ExpressionIR, knownVariableTypes?: Map<string, KnownVariableInfo>): string;
|
|
195
195
|
/**
|
|
@@ -206,7 +206,7 @@ export declare class ExpressionRenderer {
|
|
|
206
206
|
* operand → `static_cast<EnumType>(value)` (int → enum). C++ `enum
|
|
207
207
|
* class` does not implicitly convert FROM an integral storage type
|
|
208
208
|
* either, so reading an `int`/`uint8_t` cell back into an enum-typed
|
|
209
|
-
* local needs the reverse cast.
|
|
209
|
+
* local needs the reverse cast.
|
|
210
210
|
*
|
|
211
211
|
* This closes the "enum↔integral storage boundary" family — the previous
|
|
212
212
|
* point-specific casts handled enum-as-array-index (demo #28 E) and
|