@typecad/cuttlefish 1.0.0-alpha.7 → 1.0.0-alpha.9
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/dist/api/config.d.ts +32 -0
- package/dist/api/index.d.ts +1 -1
- package/dist/api/schema/types.d.ts +15 -0
- package/dist/api/shared/async-runtime-static.d.ts +12 -1
- package/dist/api/shared/async-runtime-static.js +69 -24
- package/dist/api/shared/async-symbol-detector.d.ts +11 -0
- package/dist/api/shared/async-symbol-detector.js +140 -0
- package/dist/api/shared/async-types.d.ts +24 -0
- package/dist/api/shared/coop-scheduler.d.ts +60 -0
- package/dist/api/shared/coop-scheduler.js +149 -0
- package/dist/api/shared/display-adapter.d.ts +2 -2
- package/dist/api/shared/display-adapter.js +9 -119
- package/dist/api/shared/display-adapters/sdl.js +29 -29
- package/dist/api/shared/display-profile.d.ts +38 -33
- package/dist/api/shared/display-profile.js +22 -170
- package/dist/api/shared/framework-manifest-registry.d.ts +1 -1
- package/dist/api/shared/framework-manifest-registry.js +8 -6
- package/dist/api/shared/framework-manifest.d.ts +147 -28
- package/dist/api/shared/framework-manifest.js +35 -2
- package/dist/api/shared/graphics-strategy.d.ts +15 -6
- package/dist/api/shared/hal-op-ir.d.ts +340 -3
- package/dist/api/shared/hal-op-ir.js +34 -0
- package/dist/api/shared/index.d.ts +9 -1
- package/dist/api/shared/index.js +11 -1
- package/dist/api/shared/ir-core.d.ts +4 -0
- package/dist/api/shared/ir-declarations.d.ts +6 -0
- package/dist/api/shared/platform-strategy.d.ts +104 -2
- package/dist/api/shared/polyfill-helper-registry.js +18 -1
- package/dist/api/shared/promise-runtime.d.ts +1 -1
- package/dist/api/shared/promise-runtime.js +17 -13
- package/dist/api/shared/toolchain-types.d.ts +17 -0
- package/dist/api/shared/validate-framework-manifest.js +77 -0
- package/dist/api/shared/worker-runtime-polyfill.d.ts +22 -0
- package/dist/api/shared/worker-runtime-polyfill.js +34 -0
- package/dist/api/shared/worker-runtime.d.ts +69 -0
- package/dist/api/shared/worker-runtime.js +163 -0
- package/dist/cli.js +125 -121
- package/dist/config-loader.d.ts +7 -2
- package/dist/config-loader.js +20 -19
- package/dist/config-schema.d.ts +107 -65
- package/dist/config-schema.js +14 -0
- package/dist/contract/board-generator.d.ts +17 -0
- package/dist/contract/board-generator.js +57 -0
- package/dist/contract/contract-parser.d.ts +217 -0
- package/dist/contract/contract-parser.js +224 -0
- package/dist/contract/index.d.ts +22 -0
- package/dist/contract/index.js +68 -0
- package/dist/create/board-codegen.js +4 -4
- package/dist/create/board-generators.js +4 -5
- package/dist/create/board-spec.d.ts +74 -77
- package/dist/create/board-spec.js +0 -1
- package/dist/create/init-scaffold.d.ts +7 -3
- package/dist/create/init-scaffold.js +0 -62
- package/dist/create/init-templates.js +62 -16
- package/dist/create/init-wizard.js +32 -27
- package/dist/debug/preprocessor.js +178 -39
- package/dist/debug/types.d.ts +23 -0
- package/dist/diagnostics/mermaid-builder.d.ts +1 -1
- package/dist/diagnostics/mermaid-builder.js +34 -24
- package/dist/emit/compliance/arxml-writer.d.ts +11 -0
- package/dist/emit/compliance/arxml-writer.js +34 -0
- package/dist/emit/compliance/compliance-context.d.ts +57 -0
- package/dist/emit/compliance/compliance-context.js +113 -0
- package/dist/emit/compliance/deviation-ledger.d.ts +27 -0
- package/dist/emit/compliance/deviation-ledger.js +47 -0
- package/dist/emit/compliance/deviation-writer.d.ts +30 -0
- package/dist/emit/compliance/deviation-writer.js +37 -0
- package/dist/emit/compliance/index.d.ts +7 -0
- package/dist/emit/compliance/index.js +6 -0
- package/dist/emit/compliance/rule-engine.d.ts +13 -0
- package/dist/emit/compliance/rule-engine.js +101 -0
- package/dist/emit/compliance/rules.d.ts +16 -0
- package/dist/emit/compliance/rules.js +191 -0
- package/dist/emit/compliance/types.d.ts +59 -0
- package/dist/emit/compliance/types.js +8 -0
- package/dist/emit/emitters/class-emitter.js +6 -1
- package/dist/emit/emitters/emitter-context.d.ts +23 -1
- package/dist/emit/emitters/function-emitter-impl.js +96 -6
- package/dist/emit/emitters/line-appender.js +19 -0
- package/dist/emit/emitters/line-marker.d.ts +38 -0
- package/dist/emit/emitters/line-marker.js +39 -0
- package/dist/emit/emitters/output-finalizer.js +80 -1
- package/dist/emit/emitters/setup.d.ts +17 -0
- package/dist/emit/emitters/setup.js +217 -36
- package/dist/emit/emitters/top-level-prep.js +6 -0
- package/dist/emit/emitters/type-decl-emitter.js +28 -3
- package/dist/emit/emitters/ui-emitter.js +17 -8
- package/dist/emit/expression-renderer.d.ts +1 -1
- package/dist/emit/expression-renderer.js +81 -5
- package/dist/emit/route-hal-op.js +18 -5
- package/dist/emit/snprintf-helpers.js +7 -4
- package/dist/emit/statement-renderer.d.ts +10 -0
- package/dist/emit/statement-renderer.js +21 -5
- package/dist/emit/utils/async-state-machine.js +39 -12
- package/dist/framework-package.js +2 -0
- package/dist/framework-registry.d.ts +17 -0
- package/dist/install/framework-catalog.d.ts +53 -0
- package/dist/install/framework-catalog.js +107 -0
- package/dist/install/handle-install.d.ts +35 -0
- package/dist/install/handle-install.js +177 -0
- package/dist/install/index.d.ts +4 -0
- package/dist/install/index.js +3 -0
- package/dist/ir/adc-range-validation.js +14 -1
- package/dist/ir/build-ir-state.d.ts +1 -0
- package/dist/ir/build-ir-state.js +14 -0
- package/dist/ir/build-ir.js +4 -4
- package/dist/ir/call-graph.js +16 -0
- package/dist/ir/expression-to-ir.js +150 -0
- package/dist/ir/function-builder.js +22 -0
- package/dist/ir/hal/hal-emitter.js +10 -6
- package/dist/ir/hal/hal-parser.js +16 -9
- package/dist/ir/hal/hal-plugins.js +164 -3
- package/dist/ir/identifier-collector.js +21 -4
- package/dist/ir/interrupt-analysis.d.ts +5 -1
- package/dist/ir/interrupt-analysis.js +8 -16
- package/dist/ir/ownership-analysis.js +6 -0
- package/dist/ir/pin-capability-validation.js +37 -0
- package/dist/ir/pin-mode-validation.d.ts +2 -2
- package/dist/ir/pin-mode-validation.js +49 -18
- package/dist/ir/program-analysis.d.ts +42 -0
- package/dist/ir/program-analysis.js +243 -6
- package/dist/ir/render-expr.js +11 -0
- package/dist/ir/timing-validation.js +11 -2
- package/dist/ir/transformers/call-statement.js +105 -0
- package/dist/ir/transformers/expressions.js +4 -0
- package/dist/ir/transformers/hal-call-resolver.js +21 -0
- package/dist/ir/transformers/namespace-methods.js +17 -12
- package/dist/ir/transformers/ui-reactive.js +2 -2
- package/dist/ir/transformers/variables.js +51 -0
- package/dist/ir/type-resolution.js +18 -0
- package/dist/ir/ui-element-auto-wire.js +7 -5
- package/dist/ir/utils/map-statements.d.ts +4 -0
- package/dist/ir/utils/map-statements.js +79 -0
- package/dist/ir/validation-orchestrator.js +6 -1
- package/dist/ir/worker-analysis.d.ts +10 -0
- package/dist/ir/worker-analysis.js +261 -0
- package/dist/orchestrator/graph-builder.js +8 -7
- package/dist/platform/coop-scheduler-runtime.d.ts +19 -0
- package/dist/platform/coop-scheduler-runtime.js +41 -0
- package/dist/platform/generic-strategy.d.ts +15 -3
- package/dist/platform/generic-strategy.js +49 -4
- package/dist/safety/safety-bridge.d.ts +11 -0
- package/dist/safety/safety-bridge.js +48 -0
- package/dist/safety/sidecar-bridge.d.ts +5 -0
- package/dist/safety/sidecar-bridge.js +22 -0
- package/dist/safety-hook.d.ts +79 -0
- package/dist/safety-hook.js +35 -0
- package/dist/stores/display-profile-store.d.ts +0 -1
- package/dist/stores/display-profile-store.js +0 -1
- package/dist/testing.d.ts +4 -0
- package/dist/testing.js +5 -0
- package/dist/transpile.js +124 -35
- package/dist/types.d.ts +22 -2
- package/dist/ui-hook.d.ts +0 -2
- package/dist/utils/cli.d.ts +2 -2
- package/dist/utils/cli.js +68 -3
- package/package.json +20 -5
- package/dist/api/shared/display-adapters/eink-mono.d.ts +0 -2
- package/dist/api/shared/display-adapters/eink-mono.js +0 -53
- package/dist/api/shared/display-adapters/ssd1309.d.ts +0 -2
- package/dist/api/shared/display-adapters/ssd1309.js +0 -136
- package/dist/api/shared/display-adapters/st7796.d.ts +0 -2
- package/dist/api/shared/display-adapters/st7796.js +0 -132
- package/dist/libdef/idf-discovery.d.ts +0 -7
- package/dist/libdef/idf-discovery.js +0 -59
- package/dist/licenses.d.ts +0 -185
- package/dist/licenses.js +0 -963
|
@@ -5,6 +5,7 @@ import { escapeCppKeyword } from "../../utils/strings.js";
|
|
|
5
5
|
import { parsedIsPlainStructType } from "../../api/shared/cpp-type-ir.js";
|
|
6
6
|
import { entryHasUI } from "../../ui-hook.js";
|
|
7
7
|
import { activeNamespaceNames } from "../../ir/build-ir-state.js";
|
|
8
|
+
import { buildCoopSchedInjection } from "../../api/shared/coop-scheduler.js";
|
|
8
9
|
export function emitPostClassDeclarations(ctx) {
|
|
9
10
|
const { strategy, effectiveEmitMode, mappedFunctions, isEntryFile, topLevelScope } = ctx;
|
|
10
11
|
const platformReservedNames = strategy.reservedNames();
|
|
@@ -31,6 +32,12 @@ export function emitPostClassDeclarations(ctx) {
|
|
|
31
32
|
if (ctx.promotedVarDecls.size > 0) {
|
|
32
33
|
for (const [varName, info] of ctx.promotedVarDecls) {
|
|
33
34
|
appendSourceLine(ctx, `${info.cppType} ${escapeCppKeyword(varName, platformReservedNames)} = {};`);
|
|
35
|
+
// Seed the top-level scope's type map so subsequent assign rendering
|
|
36
|
+
// (e.g. the deferred `c = SafeInt(0)` initializer) can resolve the
|
|
37
|
+
// variable's type and inject template args / casts via
|
|
38
|
+
// renderValueForTarget. Without this, type-aware rendering of promoted
|
|
39
|
+
// vars is unreachable (inferLvalueCppType returns undefined).
|
|
40
|
+
ctx.topLevelScope.knownVariableTypes.set(varName, { cppType: info.cppType });
|
|
34
41
|
}
|
|
35
42
|
appendSourceLine(ctx, "");
|
|
36
43
|
}
|
|
@@ -62,7 +69,7 @@ export function emitPostClassDeclarations(ctx) {
|
|
|
62
69
|
// Still register the field types so downstream rendering (e.g. string-concat
|
|
63
70
|
// wrapping via interfaceFieldTypes) can resolve property accesses on the var.
|
|
64
71
|
const fieldTypeEntries = statement.initializer.fields.map((field) => {
|
|
65
|
-
const inferred = inferObjectFieldType(field.value, ctx.globalPointerVarTypes, ctx.knownFunctionReturnTypes, ctx.knownTopLevelObjectTypes, ctx.knownTopLevelObjectFields, ctx.largeEnumNames, statement.name, field.name, strategy.defaultNumericType(), (o, n) => strategy.resolvePinType?.(o, n));
|
|
72
|
+
const inferred = inferObjectFieldType(field.value, ctx.globalPointerVarTypes, ctx.knownFunctionReturnTypes, ctx.knownTopLevelObjectTypes, ctx.knownTopLevelObjectFields, ctx.largeEnumNames, statement.name, field.name, strategy.defaultNumericType(ctx.compliance.isEnabled() ? ctx.compliance : undefined), (o, n) => strategy.resolvePinType?.(o, n));
|
|
66
73
|
return [field.name, inferred];
|
|
67
74
|
});
|
|
68
75
|
if (hasExplicitNamedType) {
|
|
@@ -240,6 +247,29 @@ export function emitFunctions(ctx) {
|
|
|
240
247
|
ctx.sourceMapEntries = ctx.headerMapEntries;
|
|
241
248
|
ctx.headerMapEntries = _swapMaps;
|
|
242
249
|
}
|
|
250
|
+
// Cooperative scheduler trampolines (Phase 0).
|
|
251
|
+
//
|
|
252
|
+
// When the strategy opts into priority/time-budget scheduling, the driver
|
|
253
|
+
// function's per-frame pumps are routed through the no-STL CoopSched. The
|
|
254
|
+
// trampolines (file-scope `static void __tc_coop_*(void*)`) must appear
|
|
255
|
+
// before the driver function definition so CoopSched can call them; they
|
|
256
|
+
// reference only global/static names (task instances, pump functions) so
|
|
257
|
+
// no captures are needed. Emitted once, ahead of the driver only.
|
|
258
|
+
const asyncDriverFnForTrampolines = strategy.asyncDriverFunctionName();
|
|
259
|
+
if (fn.name === asyncDriverFnForTrampolines &&
|
|
260
|
+
(hasPromiseRuntime || asyncTaskClasses.length > 0 || usesTimers)) {
|
|
261
|
+
const coopConfig = strategy.getAsyncRuntimeConfig();
|
|
262
|
+
if (coopConfig.enablePriority || coopConfig.enableTimeBudget) {
|
|
263
|
+
const coopUnits = buildDriverCoopUnits(asyncTaskClasses.map(t => t.taskVarName), hasPromiseRuntime, usesTimers);
|
|
264
|
+
if (coopUnits.length > 0) {
|
|
265
|
+
const { trampolines } = buildCoopSchedInjection(coopUnits);
|
|
266
|
+
appendSourceLine(ctx, "// ── CoopSched trampolines (priority/time-budget scheduling) ──");
|
|
267
|
+
for (const t of trampolines)
|
|
268
|
+
appendSourceLine(ctx, t);
|
|
269
|
+
appendSourceLine(ctx, "");
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
}
|
|
243
273
|
emitCommentLines(fn.leadingComments, "", (line) => appendSourceLine(ctx, line));
|
|
244
274
|
if (fn.typeParameters && fn.typeParameters.length > 0) {
|
|
245
275
|
appendSourceLine(ctx, `template<typename ${fn.typeParameters.join(", typename ")}>`);
|
|
@@ -286,12 +316,12 @@ export function emitFunctions(ctx) {
|
|
|
286
316
|
appendSourceLine(ctx, ` while (${loop.continueCondition}) {`);
|
|
287
317
|
appendSourceLine(ctx, ` ${loop.preIteration}`);
|
|
288
318
|
}
|
|
289
|
-
appendSourceLine(ctx, ` uint32_t __tc_ui_now = (
|
|
319
|
+
appendSourceLine(ctx, ` uint32_t __tc_ui_now = static_cast<uint32_t>(${strategy.currentTimeMillis()});`);
|
|
290
320
|
appendSourceLine(ctx, " static uint32_t __tc_ui_last_tick = __tc_ui_now;");
|
|
291
321
|
appendSourceLine(ctx, " uint32_t __tc_ui_delta = __tc_ui_now - __tc_ui_last_tick;");
|
|
292
322
|
appendSourceLine(ctx, " __tc_ui_last_tick = __tc_ui_now;");
|
|
293
323
|
appendSourceLine(ctx, " if (__tc_ui_delta > 250) __tc_ui_delta = 250;");
|
|
294
|
-
appendSourceLine(ctx, " ui_tick((
|
|
324
|
+
appendSourceLine(ctx, " ui_tick(static_cast<uint16_t>(__tc_ui_delta));");
|
|
295
325
|
if (loop) {
|
|
296
326
|
appendSourceLine(ctx, ` ${loop.postIteration}`);
|
|
297
327
|
appendSourceLine(ctx, ` }`);
|
|
@@ -302,9 +332,30 @@ export function emitFunctions(ctx) {
|
|
|
302
332
|
const asyncConfig = strategy.getAsyncRuntimeConfig();
|
|
303
333
|
asyncConfig.hasPromiseRuntime = hasPromiseRuntime;
|
|
304
334
|
asyncConfig.hasTimers = usesTimers;
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
335
|
+
// Cooperative scheduler path (Phase 0): when opted in, replace the flat
|
|
336
|
+
// per-frame pump sequence with a single CoopSched dispatch. The work
|
|
337
|
+
// units mirror exactly what the strategy's asyncLoopInjection() would
|
|
338
|
+
// have emitted (so Zephyr, which omits the microtask pump, still omits
|
|
339
|
+
// it), but they are dispatched in priority order and budget-bounded.
|
|
340
|
+
if (asyncConfig.enablePriority || asyncConfig.enableTimeBudget) {
|
|
341
|
+
const coopUnits = buildDriverCoopUnits(taskNames, hasPromiseRuntime, usesTimers);
|
|
342
|
+
if (coopUnits.length > 0) {
|
|
343
|
+
const { injection } = buildCoopSchedInjection(coopUnits);
|
|
344
|
+
for (const line of injection)
|
|
345
|
+
appendSourceLine(ctx, line);
|
|
346
|
+
}
|
|
347
|
+
else {
|
|
348
|
+
// No work units: fall back to the strategy's own injection.
|
|
349
|
+
const injectionLines = strategy.asyncLoopInjection(taskNames, asyncConfig);
|
|
350
|
+
for (const line of injectionLines)
|
|
351
|
+
appendSourceLine(ctx, ` ${line}`);
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
else {
|
|
355
|
+
const injectionLines = strategy.asyncLoopInjection(taskNames, asyncConfig);
|
|
356
|
+
for (const line of injectionLines) {
|
|
357
|
+
appendSourceLine(ctx, ` ${line}`);
|
|
358
|
+
}
|
|
308
359
|
}
|
|
309
360
|
}
|
|
310
361
|
if (isLoopDriver && lastIsReturn) {
|
|
@@ -436,3 +487,42 @@ export function emitFunctionForwardDeclarations(ctx) {
|
|
|
436
487
|
appendSourceLine(ctx, "");
|
|
437
488
|
}
|
|
438
489
|
}
|
|
490
|
+
/**
|
|
491
|
+
* Build the CoopSched work units for the driver function's per-frame pumps.
|
|
492
|
+
*
|
|
493
|
+
* The units mirror what a strategy's asyncLoopInjection() emits as a flat
|
|
494
|
+
* sequence, so the cooperative scheduler preserves each strategy's decisions
|
|
495
|
+
* about WHICH pumps run (e.g. Zephyr omits the std::function microtask pump
|
|
496
|
+
* because its async path is state-machine-based). Default priorities:
|
|
497
|
+
* - async task .run() → priority 1 (latency-sensitive: drives awaits)
|
|
498
|
+
* - microtask pump → priority 1 (continuations of resolved promises)
|
|
499
|
+
* - cooperative timer pump → priority 0 (less latency-critical)
|
|
500
|
+
* Both are tunable via AsyncRuntimeConfig.schedulerPriorities; the scheduler
|
|
501
|
+
* bucket-walks from the highest level down to 0.
|
|
502
|
+
*/
|
|
503
|
+
function buildDriverCoopUnits(taskVarNames, hasPromiseRuntime, hasTimers) {
|
|
504
|
+
const units = [];
|
|
505
|
+
let taskIdx = 0;
|
|
506
|
+
for (const name of taskVarNames) {
|
|
507
|
+
units.push({
|
|
508
|
+
name: `task_${taskIdx++}`,
|
|
509
|
+
body: `${name}.run();`,
|
|
510
|
+
priority: 1,
|
|
511
|
+
});
|
|
512
|
+
}
|
|
513
|
+
if (hasPromiseRuntime) {
|
|
514
|
+
units.push({
|
|
515
|
+
name: "microtasks",
|
|
516
|
+
body: "cuttlefish_pump_microtasks();",
|
|
517
|
+
priority: 1,
|
|
518
|
+
});
|
|
519
|
+
}
|
|
520
|
+
if (hasTimers) {
|
|
521
|
+
units.push({
|
|
522
|
+
name: "timers",
|
|
523
|
+
body: "__tc_timer_runtime.run();",
|
|
524
|
+
priority: 0,
|
|
525
|
+
});
|
|
526
|
+
}
|
|
527
|
+
return units;
|
|
528
|
+
}
|
|
@@ -1,7 +1,17 @@
|
|
|
1
1
|
import { cloneEmissionScopeState, recordVariableType } from "../snprintf-helpers.js";
|
|
2
2
|
import { emitCommentLines } from "../utils/index.js";
|
|
3
3
|
import { escapeCppKeyword } from "../../utils/strings.js";
|
|
4
|
+
import { formatLinemarker, shouldEmitMarker, sourceKey } from "./line-marker.js";
|
|
4
5
|
export function appendSourceLine(ctx, line, entry) {
|
|
6
|
+
// GDB debug mode: emit a linemarker before lines whose source span transitions.
|
|
7
|
+
// Runs only when an entry is present (raw braces/whitespace carry no span).
|
|
8
|
+
if (ctx.debugMode === 'gdb' && entry) {
|
|
9
|
+
const next = sourceKey(entry.tsSpan);
|
|
10
|
+
if (shouldEmitMarker(ctx.lastEmittedSource, next)) {
|
|
11
|
+
ctx.sourceLines.push(formatLinemarker(entry.tsSpan.filePath, entry.tsSpan.startLine));
|
|
12
|
+
}
|
|
13
|
+
ctx.lastEmittedSource = next;
|
|
14
|
+
}
|
|
5
15
|
ctx.sourceLines.push(line);
|
|
6
16
|
if (!entry)
|
|
7
17
|
return;
|
|
@@ -17,6 +27,15 @@ export function appendSourceLine(ctx, line, entry) {
|
|
|
17
27
|
});
|
|
18
28
|
}
|
|
19
29
|
export function appendHeaderLine(ctx, line, entry) {
|
|
30
|
+
// GDB debug mode: same transition logic as appendSourceLine, against the
|
|
31
|
+
// shared lastEmittedSource slot. See line-marker.ts for rationale.
|
|
32
|
+
if (ctx.debugMode === 'gdb' && entry) {
|
|
33
|
+
const next = sourceKey(entry.tsSpan);
|
|
34
|
+
if (shouldEmitMarker(ctx.lastEmittedSource, next)) {
|
|
35
|
+
ctx.headerLines.push(formatLinemarker(entry.tsSpan.filePath, entry.tsSpan.startLine));
|
|
36
|
+
}
|
|
37
|
+
ctx.lastEmittedSource = next;
|
|
38
|
+
}
|
|
20
39
|
ctx.headerLines.push(line);
|
|
21
40
|
if (!entry)
|
|
22
41
|
return;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { SourceSpan } from '../../types.js';
|
|
2
|
+
/**
|
|
3
|
+
* A previously-emitted source location, used for transition detection so we
|
|
4
|
+
* emit a linemarker only when the source file or line actually changes.
|
|
5
|
+
*/
|
|
6
|
+
export interface EmittedSource {
|
|
7
|
+
filePath: string;
|
|
8
|
+
/** 0-based source line (matches SourceSpan.startLine). */
|
|
9
|
+
line: number;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Format a GCC linemarker for the given path and 0-based source line.
|
|
13
|
+
* Output form: `# <line+1> "<path>"`.
|
|
14
|
+
*
|
|
15
|
+
* The path is normalized to forward slashes (Windows backslashes break GDB
|
|
16
|
+
* source resolution from the ESP-IDF build directory) and resolved to an
|
|
17
|
+
* absolute path so GDB finds the .ts file regardless of its cwd.
|
|
18
|
+
*
|
|
19
|
+
* GCC accepts both `#line N "F"` and the linemarker form `# N "F"`; we use
|
|
20
|
+
* the latter because it's what GCC emits in preprocessed output and it flows
|
|
21
|
+
* cleanly into DWARF.
|
|
22
|
+
*/
|
|
23
|
+
export declare function formatLinemarker(filePath: string, line: number): string;
|
|
24
|
+
/**
|
|
25
|
+
* Decide whether a linemarker should be emitted before the next source line,
|
|
26
|
+
* given the previously-emitted marker (or null for the first one).
|
|
27
|
+
*
|
|
28
|
+
* Returns true only on file or line transition — this keeps the emitted C++
|
|
29
|
+
* clean (one marker per source-span change, not per emitted C++ line, which
|
|
30
|
+
* would be noisy and slow).
|
|
31
|
+
*/
|
|
32
|
+
export declare function shouldEmitMarker(prev: EmittedSource | null, next: EmittedSource): boolean;
|
|
33
|
+
/**
|
|
34
|
+
* Extract the {filePath, line} for transition tracking from a SourceSpan.
|
|
35
|
+
* SourceSpan.startLine is 0-based; kept 0-based here so shouldEmitMarker
|
|
36
|
+
* compares apples to apples (formatLinemarker does the +1 at format time).
|
|
37
|
+
*/
|
|
38
|
+
export declare function sourceKey(span: SourceSpan): EmittedSource;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { resolve } from 'node:path';
|
|
2
|
+
/**
|
|
3
|
+
* Format a GCC linemarker for the given path and 0-based source line.
|
|
4
|
+
* Output form: `# <line+1> "<path>"`.
|
|
5
|
+
*
|
|
6
|
+
* The path is normalized to forward slashes (Windows backslashes break GDB
|
|
7
|
+
* source resolution from the ESP-IDF build directory) and resolved to an
|
|
8
|
+
* absolute path so GDB finds the .ts file regardless of its cwd.
|
|
9
|
+
*
|
|
10
|
+
* GCC accepts both `#line N "F"` and the linemarker form `# N "F"`; we use
|
|
11
|
+
* the latter because it's what GCC emits in preprocessed output and it flows
|
|
12
|
+
* cleanly into DWARF.
|
|
13
|
+
*/
|
|
14
|
+
export function formatLinemarker(filePath, line) {
|
|
15
|
+
const absolute = resolve(filePath);
|
|
16
|
+
const normalized = absolute.split(/[\\/]/).join('/');
|
|
17
|
+
return `# ${line + 1} "${normalized}"`;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Decide whether a linemarker should be emitted before the next source line,
|
|
21
|
+
* given the previously-emitted marker (or null for the first one).
|
|
22
|
+
*
|
|
23
|
+
* Returns true only on file or line transition — this keeps the emitted C++
|
|
24
|
+
* clean (one marker per source-span change, not per emitted C++ line, which
|
|
25
|
+
* would be noisy and slow).
|
|
26
|
+
*/
|
|
27
|
+
export function shouldEmitMarker(prev, next) {
|
|
28
|
+
if (prev === null)
|
|
29
|
+
return true;
|
|
30
|
+
return prev.filePath !== next.filePath || prev.line !== next.line;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Extract the {filePath, line} for transition tracking from a SourceSpan.
|
|
34
|
+
* SourceSpan.startLine is 0-based; kept 0-based here so shouldEmitMarker
|
|
35
|
+
* compares apples to apples (formatLinemarker does the +1 at format time).
|
|
36
|
+
*/
|
|
37
|
+
export function sourceKey(span) {
|
|
38
|
+
return { filePath: span.filePath, line: span.startLine };
|
|
39
|
+
}
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
|
-
import { writeText } from "../../utils/fs.js";
|
|
2
|
+
import { writeText, readText } from "../../utils/fs.js";
|
|
3
3
|
import { makeGeneratedMap, writeSourceMap } from "../../mapping/source-map.js";
|
|
4
4
|
import { dedupe, hasConsoleCalls, resolveTranspiledModuleInclude } from "../utils/index.js";
|
|
5
5
|
import { appendHeaderLine } from "./line-appender.js";
|
|
6
|
+
import { runSelfCheck } from "../compliance/rule-engine.js";
|
|
7
|
+
import { renderRegistryJson } from "../compliance/deviation-writer.js";
|
|
8
|
+
import { renderArxml } from "../compliance/arxml-writer.js";
|
|
6
9
|
/** Derives a unique C preprocessor guard name from a source file path. */
|
|
7
10
|
function sanitizeGuardName(filePath) {
|
|
8
11
|
// Use the basename minus extension, uppercased, with non-alphanumeric
|
|
@@ -262,6 +265,82 @@ export function finalizeOutput(ctx) {
|
|
|
262
265
|
: undefined;
|
|
263
266
|
const diagnostics = [...program.diagnostics, ...ctx.profileDiagnostics, ...ctx.emitDiagnostics];
|
|
264
267
|
diagnostics.push(...strategy.emitDiagnostics(options.emitMode));
|
|
268
|
+
// ── AUTOSAR compliance self-check + sidecar ────────────────────────────
|
|
269
|
+
// Runs only when --autosar is warn or strict. Emits an AUTOSAR_<ruleId>
|
|
270
|
+
// diagnostic per finding (severity: error for required unrecorded
|
|
271
|
+
// violations in strict mode, otherwise warning) and writes the sidecar
|
|
272
|
+
// deviation registry next to the emitted artifact.
|
|
273
|
+
if (ctx.compliance.isEnabled()) {
|
|
274
|
+
// Scan the ACTUAL file content (post-writeText) rather than ctx.sourceLines,
|
|
275
|
+
// because some ctx.sourceLines entries contain embedded newlines (e.g. ESP32
|
|
276
|
+
// shim blocks pushed as multi-line strings), making array indices != file
|
|
277
|
+
// line numbers. Reading the written file gives correct 1:1 line numbers.
|
|
278
|
+
const actualSourceLines = outputHeaderPath
|
|
279
|
+
? readText(sourcePath).split("\n")
|
|
280
|
+
: readText(sourcePath).split("\n");
|
|
281
|
+
const actualHeaderLines = outputHeaderPath
|
|
282
|
+
? readText(outputHeaderPath).split("\n")
|
|
283
|
+
: [];
|
|
284
|
+
const findings = runSelfCheck(ctx.compliance, actualSourceLines, actualHeaderLines);
|
|
285
|
+
const mode = ctx.compliance.mode();
|
|
286
|
+
// Helper: map a C++ generated line back to its originating TS source
|
|
287
|
+
// line via the source map, so diagnostics point at the user's code.
|
|
288
|
+
const mapCppLineToTs = (cppLine) => {
|
|
289
|
+
// Find the source-map entry whose generated range covers cppLine.
|
|
290
|
+
// Entries are ordered by generatedStartLine; find the last one whose
|
|
291
|
+
// start is ≤ cppLine.
|
|
292
|
+
let best = null;
|
|
293
|
+
for (const entry of ctx.sourceMapEntries) {
|
|
294
|
+
if (entry.generatedStartLine <= cppLine) {
|
|
295
|
+
if (!best || entry.generatedStartLine > best.generatedStartLine) {
|
|
296
|
+
best = entry;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
if (best) {
|
|
301
|
+
return { filePath: best.tsSpan.filePath, line: best.tsSpan.startLine };
|
|
302
|
+
}
|
|
303
|
+
return null;
|
|
304
|
+
};
|
|
305
|
+
for (const f of findings) {
|
|
306
|
+
const isError = mode === "strict" &&
|
|
307
|
+
f.kind === "unrecorded-violation" &&
|
|
308
|
+
f.severity === "required";
|
|
309
|
+
// Map the C++ line back to TS so the diagnostic points at user code.
|
|
310
|
+
const tsLoc = mapCppLineToTs(f.line);
|
|
311
|
+
const emittedFile = f.file === "header" ? `${baseName}.h` : path.basename(sourcePath);
|
|
312
|
+
const cppRef = `${emittedFile}:${f.line}`;
|
|
313
|
+
const tsRef = tsLoc ? `${path.basename(tsLoc.filePath)}:${tsLoc.line}` : null;
|
|
314
|
+
const locationDesc = tsRef
|
|
315
|
+
? `${tsRef} (emitted at ${cppRef})`
|
|
316
|
+
: cppRef;
|
|
317
|
+
const diag = {
|
|
318
|
+
severity: isError ? "error" : "warning",
|
|
319
|
+
code: `AUTOSAR_${f.ruleId}`,
|
|
320
|
+
message: `AUTOSAR ${f.ruleId} ${f.kind} at ${locationDesc}: ${f.snippet}`,
|
|
321
|
+
// Point the diagnostic at the TS source line (if mapped) so the
|
|
322
|
+
// CLI's file:line:col display is meaningful to the user. If the
|
|
323
|
+
// violation is in framework/runtime shim code with no TS origin,
|
|
324
|
+
// point at the emitted C++ artifact instead.
|
|
325
|
+
filePath: tsLoc?.filePath ?? sourcePath,
|
|
326
|
+
line: tsLoc?.line ?? f.line,
|
|
327
|
+
column: 1,
|
|
328
|
+
};
|
|
329
|
+
diagnostics.push(diag);
|
|
330
|
+
}
|
|
331
|
+
// Sidecar deviation registry — written for both warn and strict modes
|
|
332
|
+
// alongside the emitted artifact (mirrors how .thcppmap.json sits next
|
|
333
|
+
// to the .cpp/.ino today).
|
|
334
|
+
const toolVersion = options.toolVersion ?? "unknown";
|
|
335
|
+
const registryPath = path.join(outDir, `${baseName}.autosar-deviations.json`);
|
|
336
|
+
writeText(registryPath, renderRegistryJson(ctx.compliance, path.basename(sourcePath), toolVersion));
|
|
337
|
+
// Optional ARXML projection (Artop/DaVinci tooling). Gated behind
|
|
338
|
+
// --autosar-arxml so projects that don't need it pay no cost.
|
|
339
|
+
if (options.autosarArxml) {
|
|
340
|
+
const arxmlPath = path.join(outDir, `${baseName}.autosar-deviations.arxml`);
|
|
341
|
+
writeText(arxmlPath, renderArxml(ctx.compliance, path.basename(sourcePath)));
|
|
342
|
+
}
|
|
343
|
+
}
|
|
265
344
|
return {
|
|
266
345
|
headerPath: outputHeaderPath,
|
|
267
346
|
sourcePath,
|
|
@@ -1,3 +1,20 @@
|
|
|
1
1
|
import type { ProgramIR } from "../../api/index.js";
|
|
2
2
|
import type { EmitterContext, EmitterOptions } from "./emitter-context.js";
|
|
3
|
+
/**
|
|
4
|
+
* Detect whether a program uses @typecad/safety. The safety transform pass
|
|
5
|
+
* injects safety.* ops when in use; strategies consult this to decide
|
|
6
|
+
* whether to emit the __tc_gpio_read shim that the safety voter calls.
|
|
7
|
+
* Mirrors programUsesWdt() above (same inline walker pattern).
|
|
8
|
+
*
|
|
9
|
+
* `hal-expr` (the expression-position safety.read_safe lowering produced
|
|
10
|
+
* by `const r = safe.read(pin)`) lives in expression trees, not at statement
|
|
11
|
+
* level — but it's carried inside a var_decl statement whose initializer is
|
|
12
|
+
* the hal-expr, and the var_decl gets visited as a top-level statement. We
|
|
13
|
+
* detect both: statement-level `hal-op` operations, and any statement whose
|
|
14
|
+
* rendered initializer text mentions safety.* (a defensive catch for the
|
|
15
|
+
* hal-expr-in-initializer case). The latter is rarely needed because the
|
|
16
|
+
* voter's read_safe op is also captured at the call site, but kept for
|
|
17
|
+
* robustness.
|
|
18
|
+
*/
|
|
19
|
+
export declare function programUsesSafety(program: ProgramIR): boolean;
|
|
3
20
|
export declare function buildEmitterContext(program: ProgramIR, options: EmitterOptions): EmitterContext;
|