@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
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { asilLevel, ASIL_THRESHOLDS } from "./asil.js";
|
|
2
|
+
/** B5: Init completeness (ISO 26262 Part 6, §7.4.5 — ASIL D).
|
|
3
|
+
*
|
|
4
|
+
* Checks that every identifier used in a safety-critical function (ASIL D)
|
|
5
|
+
* was initialized in setup() or at file scope. An uninitialized variable
|
|
6
|
+
* in a safety-critical path is a potential source of undefined behavior.
|
|
7
|
+
*
|
|
8
|
+
* Approach: collect identifiers assigned/declared in setup() and top-level
|
|
9
|
+
* (the "init set"), then walk each ASIL D function's statements looking
|
|
10
|
+
* for identifiers used before any local assignment. Reports each as a
|
|
11
|
+
* warning. */
|
|
12
|
+
export function checkInitCompleteness(program, _ctx) {
|
|
13
|
+
const diags = [];
|
|
14
|
+
// Collect the "init set": identifiers declared/assigned in setup() and
|
|
15
|
+
// top-level statements. These are considered initialized.
|
|
16
|
+
const initSet = new Set();
|
|
17
|
+
// Top-level var_decls and assignments
|
|
18
|
+
for (const stmt of program.topLevelStatements) {
|
|
19
|
+
collectAssignedNames(stmt, initSet);
|
|
20
|
+
}
|
|
21
|
+
// setup() statements
|
|
22
|
+
const setupFn = program.functions.find(fn => fn.originalName === "setup");
|
|
23
|
+
if (setupFn) {
|
|
24
|
+
for (const stmt of setupFn.statements) {
|
|
25
|
+
collectAssignedNames(stmt, initSet);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
// Check each ASIL D function for use of identifiers not in the init set
|
|
29
|
+
for (const fn of program.functions) {
|
|
30
|
+
if (fn.originalName === "setup" || fn.originalName === "loop")
|
|
31
|
+
continue;
|
|
32
|
+
if (asilLevel(fn.decorators) < ASIL_THRESHOLDS.dynamicAlloc)
|
|
33
|
+
continue;
|
|
34
|
+
// Collect locally-declared names in this function (params + local vars)
|
|
35
|
+
const localNames = new Set();
|
|
36
|
+
for (const param of fn.parameters) {
|
|
37
|
+
localNames.add(param.name);
|
|
38
|
+
}
|
|
39
|
+
// Walk statements — look for identifiers used before local declaration
|
|
40
|
+
for (const stmt of fn.statements) {
|
|
41
|
+
checkUsedBeforeDeclared(stmt, fn.originalName, initSet, localNames, diags);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return diags;
|
|
45
|
+
}
|
|
46
|
+
/** Collect names that are declared or assigned in a statement tree. */
|
|
47
|
+
function collectAssignedNames(stmt, names) {
|
|
48
|
+
if (!stmt || typeof stmt !== "object")
|
|
49
|
+
return;
|
|
50
|
+
if (stmt.kind === "var_decl" && typeof stmt.name === "string") {
|
|
51
|
+
names.add(stmt.name);
|
|
52
|
+
}
|
|
53
|
+
if (stmt.kind === "assign" && typeof stmt.target === "string") {
|
|
54
|
+
names.add(stmt.target);
|
|
55
|
+
}
|
|
56
|
+
if (stmt.kind === "update" && typeof stmt.target === "string") {
|
|
57
|
+
names.add(stmt.target);
|
|
58
|
+
}
|
|
59
|
+
for (const key of Object.keys(stmt)) {
|
|
60
|
+
const val = stmt[key];
|
|
61
|
+
if (Array.isArray(val)) {
|
|
62
|
+
for (const item of val) {
|
|
63
|
+
if (item && typeof item === "object" && typeof item.kind === "string") {
|
|
64
|
+
collectAssignedNames(item, names);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
else if (val && typeof val === "object" && typeof val.kind === "string") {
|
|
69
|
+
collectAssignedNames(val, names);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
/** Check if a statement uses identifiers that were never initialized. */
|
|
74
|
+
function checkUsedBeforeDeclared(stmt, fnName, initSet, localNames, diags) {
|
|
75
|
+
if (!stmt || typeof stmt !== "object")
|
|
76
|
+
return;
|
|
77
|
+
// Check identifiers used in expressions
|
|
78
|
+
if (stmt.kind === "identifier" && typeof stmt.value === "string") {
|
|
79
|
+
const name = stmt.value;
|
|
80
|
+
// Skip if it's initialized at file scope/setup, or declared locally,
|
|
81
|
+
// or it's a known built-in (skip common names).
|
|
82
|
+
if (!initSet.has(name) && !localNames.has(name) && !isBuiltin(name)) {
|
|
83
|
+
diags.push({
|
|
84
|
+
severity: "warning",
|
|
85
|
+
code: "ISO26262_B5_INIT_COMPLETENESS",
|
|
86
|
+
message: `Variable '${name}' used in ${fnName}() may not be initialized in setup() or at file scope`,
|
|
87
|
+
hint: "ASIL D requires all variables in safety-critical paths to be initialized before use. Initialize in setup() or declare at file scope.",
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
// Recurse
|
|
92
|
+
for (const key of Object.keys(stmt)) {
|
|
93
|
+
const val = stmt[key];
|
|
94
|
+
if (Array.isArray(val)) {
|
|
95
|
+
for (const item of val) {
|
|
96
|
+
if (item && typeof item === "object" && typeof item.kind === "string") {
|
|
97
|
+
checkUsedBeforeDeclared(item, fnName, initSet, localNames, diags);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
else if (val && typeof val === "object" && typeof val.kind === "string") {
|
|
102
|
+
checkUsedBeforeDeclared(val, fnName, initSet, localNames, diags);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
function isBuiltin(name) {
|
|
107
|
+
// Common names that don't need explicit init
|
|
108
|
+
const builtins = new Set([
|
|
109
|
+
"true", "false", "null", "undefined", "undefined_t",
|
|
110
|
+
"console", "Math", "Timing", "Serial", "WDT", "Preferences",
|
|
111
|
+
"delay", "millis", "micros", "delayMicroseconds",
|
|
112
|
+
"HIGH", "LOW", "INPUT", "OUTPUT", "INPUT_PULLUP", "INPUT_PULLDOWN",
|
|
113
|
+
"LED_BUILTIN", "Serial",
|
|
114
|
+
// Cuttlefish internal names
|
|
115
|
+
"__tc_", "SafeReadResult", "SafeWriteResult", "SafeVariable", "SafeInt",
|
|
116
|
+
"safe", "volatile",
|
|
117
|
+
]);
|
|
118
|
+
if (builtins.has(name))
|
|
119
|
+
return true;
|
|
120
|
+
// Skip names starting with __ (cuttlefish internal)
|
|
121
|
+
if (name.startsWith("__"))
|
|
122
|
+
return true;
|
|
123
|
+
// Skip all-caps names (likely macros/constants)
|
|
124
|
+
if (name === name.toUpperCase() && name.length > 1)
|
|
125
|
+
return true;
|
|
126
|
+
return false;
|
|
127
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { ProgramIR, Diagnostic } from "../../api/index.js";
|
|
2
|
+
import type { SafetyTransformContext } from "../../safety-hook.js";
|
|
3
|
+
/** B3: Unbounded loop detection (ISO 26262 Part 6, §7.4.10 — ASIL C+).
|
|
4
|
+
*
|
|
5
|
+
* Only checks functions decorated with @asilC or higher.
|
|
6
|
+
* Lower ASIL levels and QM functions are skipped. */
|
|
7
|
+
export declare function checkUnboundedLoops(program: ProgramIR, _ctx: SafetyTransformContext): Diagnostic[];
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { asilLevel, ASIL_THRESHOLDS } from "./asil.js";
|
|
2
|
+
/** B3: Unbounded loop detection (ISO 26262 Part 6, §7.4.10 — ASIL C+).
|
|
3
|
+
*
|
|
4
|
+
* Only checks functions decorated with @asilC or higher.
|
|
5
|
+
* Lower ASIL levels and QM functions are skipped. */
|
|
6
|
+
export function checkUnboundedLoops(program, _ctx) {
|
|
7
|
+
const diags = [];
|
|
8
|
+
for (const fn of program.functions) {
|
|
9
|
+
if (asilLevel(fn.decorators) < ASIL_THRESHOLDS.unboundedLoop)
|
|
10
|
+
continue;
|
|
11
|
+
for (const stmt of fn.statements) {
|
|
12
|
+
scanForUnboundedLoop(stmt, fn.originalName, diags);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
for (const cls of program.classes) {
|
|
16
|
+
for (const method of cls.methods) {
|
|
17
|
+
if (!method.name)
|
|
18
|
+
continue;
|
|
19
|
+
if (asilLevel(method.decorators) < ASIL_THRESHOLDS.unboundedLoop)
|
|
20
|
+
continue;
|
|
21
|
+
for (const stmt of method.statements) {
|
|
22
|
+
scanForUnboundedLoop(stmt, method.name, diags);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return diags;
|
|
27
|
+
}
|
|
28
|
+
function scanForUnboundedLoop(stmt, fnName, diags) {
|
|
29
|
+
if (!stmt || typeof stmt !== "object")
|
|
30
|
+
return;
|
|
31
|
+
if (stmt.kind === "while") {
|
|
32
|
+
const cond = stmt.condition;
|
|
33
|
+
if (isConstantTrue(cond)) {
|
|
34
|
+
diags.push({
|
|
35
|
+
severity: "warning",
|
|
36
|
+
code: "ISO26262_B3_UNBOUNDED_LOOP",
|
|
37
|
+
message: `Unbounded while(true) in ${fnName}()`,
|
|
38
|
+
hint: "Use a bounded loop counter or a timeout guard to ensure termination.",
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
if (stmt.kind === "for" && !stmt.condition) {
|
|
43
|
+
diags.push({
|
|
44
|
+
severity: "warning",
|
|
45
|
+
code: "ISO26262_B3_UNBOUNDED_LOOP",
|
|
46
|
+
message: `Unbounded for(;;) in ${fnName}()`,
|
|
47
|
+
hint: "Use a bounded loop counter or a timeout guard to ensure termination.",
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
if (stmt.kind === "do_while") {
|
|
51
|
+
const cond = stmt.condition;
|
|
52
|
+
if (isConstantTrue(cond)) {
|
|
53
|
+
diags.push({
|
|
54
|
+
severity: "warning",
|
|
55
|
+
code: "ISO26262_B3_UNBOUNDED_LOOP",
|
|
56
|
+
message: `Unbounded do-while(true) in ${fnName}()`,
|
|
57
|
+
hint: "Use a bounded loop counter or a timeout guard to ensure termination.",
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
for (const key of Object.keys(stmt)) {
|
|
62
|
+
const val = stmt[key];
|
|
63
|
+
if (Array.isArray(val)) {
|
|
64
|
+
for (const item of val) {
|
|
65
|
+
if (item && typeof item === "object" && typeof item.kind === "string") {
|
|
66
|
+
scanForUnboundedLoop(item, fnName, diags);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
else if (val && typeof val === "object" && typeof val.kind === "string") {
|
|
71
|
+
scanForUnboundedLoop(val, fnName, diags);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
function isConstantTrue(expr) {
|
|
76
|
+
if (!expr)
|
|
77
|
+
return false;
|
|
78
|
+
if (expr.kind === "raw" && typeof expr.value === "string") {
|
|
79
|
+
return /^(true|1)$/.test(expr.value.trim());
|
|
80
|
+
}
|
|
81
|
+
if (expr.kind === "boolean" && expr.value === true)
|
|
82
|
+
return true;
|
|
83
|
+
if (expr.kind === "number" && expr.value !== 0)
|
|
84
|
+
return true;
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { ProgramIR, Diagnostic } from "../../api/index.js";
|
|
2
|
+
import type { SafetyTransformContext } from "../../safety-hook.js";
|
|
3
|
+
/** B1: No recursion (ISO 26262 Part 6, Table 8 — ASIL B+).
|
|
4
|
+
*
|
|
5
|
+
* Only checks functions decorated with @asilB or higher.
|
|
6
|
+
* QM/ASIL A functions are skipped.
|
|
7
|
+
*
|
|
8
|
+
* Builds a function call graph and runs DFS cycle detection. */
|
|
9
|
+
export declare function checkRecursion(program: ProgramIR, _ctx: SafetyTransformContext): Diagnostic[];
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { asilLevel, ASIL_THRESHOLDS } from "./asil.js";
|
|
2
|
+
/** B1: No recursion (ISO 26262 Part 6, Table 8 — ASIL B+).
|
|
3
|
+
*
|
|
4
|
+
* Only checks functions decorated with @asilB or higher.
|
|
5
|
+
* QM/ASIL A functions are skipped.
|
|
6
|
+
*
|
|
7
|
+
* Builds a function call graph and runs DFS cycle detection. */
|
|
8
|
+
export function checkRecursion(program, _ctx) {
|
|
9
|
+
const diags = [];
|
|
10
|
+
// Build function set (only ASIL-annotated functions are checked)
|
|
11
|
+
const asilFnNames = new Set();
|
|
12
|
+
for (const fn of program.functions) {
|
|
13
|
+
if (asilLevel(fn.decorators) >= ASIL_THRESHOLDS.recursion) {
|
|
14
|
+
asilFnNames.add(fn.originalName);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
for (const cls of program.classes) {
|
|
18
|
+
for (const method of cls.methods) {
|
|
19
|
+
if (method.name && asilLevel(method.decorators) >= ASIL_THRESHOLDS.recursion) {
|
|
20
|
+
asilFnNames.add(method.name);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
if (asilFnNames.size === 0)
|
|
25
|
+
return diags;
|
|
26
|
+
// Build call graph (all functions, not just ASIL — callees matter)
|
|
27
|
+
const callGraph = buildFunctionCallGraph(program);
|
|
28
|
+
// DFS cycle detection — only report cycles involving ASIL functions
|
|
29
|
+
const WHITE = 0, GRAY = 1, BLACK = 2;
|
|
30
|
+
const color = new Map();
|
|
31
|
+
for (const name of callGraph.keys()) {
|
|
32
|
+
color.set(name, WHITE);
|
|
33
|
+
}
|
|
34
|
+
for (const entry of callGraph.keys()) {
|
|
35
|
+
if (color.get(entry) === WHITE) {
|
|
36
|
+
const path = [];
|
|
37
|
+
dfs(entry, callGraph, color, path, (cycle) => {
|
|
38
|
+
// Only report if any function in the cycle is ASIL-annotated
|
|
39
|
+
if (cycle.some(name => asilFnNames.has(name))) {
|
|
40
|
+
diags.push({
|
|
41
|
+
severity: "error",
|
|
42
|
+
code: "ISO26262_B1_RECURSION",
|
|
43
|
+
message: `Recursion detected: ${cycle.join(" → ")}`,
|
|
44
|
+
hint: "ASIL B+ requires bounded call depth. Refactor to an iterative loop or use a fixed-size memoization table.",
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return diags;
|
|
51
|
+
}
|
|
52
|
+
function buildFunctionCallGraph(program) {
|
|
53
|
+
const graph = new Map();
|
|
54
|
+
const allFnNames = new Set();
|
|
55
|
+
for (const fn of program.functions) {
|
|
56
|
+
allFnNames.add(fn.originalName);
|
|
57
|
+
}
|
|
58
|
+
for (const cls of program.classes) {
|
|
59
|
+
for (const method of cls.methods) {
|
|
60
|
+
if (method.name)
|
|
61
|
+
allFnNames.add(method.name);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
for (const fn of program.functions) {
|
|
65
|
+
const name = fn.originalName;
|
|
66
|
+
const deps = new Set();
|
|
67
|
+
for (const stmt of fn.statements) {
|
|
68
|
+
collectCallTargets(stmt, allFnNames, deps);
|
|
69
|
+
}
|
|
70
|
+
graph.set(name, deps);
|
|
71
|
+
}
|
|
72
|
+
for (const cls of program.classes) {
|
|
73
|
+
for (const method of cls.methods) {
|
|
74
|
+
if (!method.name)
|
|
75
|
+
continue;
|
|
76
|
+
const deps = new Set();
|
|
77
|
+
for (const stmt of method.statements) {
|
|
78
|
+
collectCallTargets(stmt, allFnNames, deps);
|
|
79
|
+
}
|
|
80
|
+
const existing = graph.get(method.name);
|
|
81
|
+
if (existing) {
|
|
82
|
+
for (const d of deps)
|
|
83
|
+
existing.add(d);
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
graph.set(method.name, deps);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return graph;
|
|
91
|
+
}
|
|
92
|
+
function collectCallTargets(stmt, allFnNames, deps) {
|
|
93
|
+
if (!stmt || typeof stmt !== "object")
|
|
94
|
+
return;
|
|
95
|
+
if (stmt.kind === "call" && typeof stmt.callee === "string" && allFnNames.has(stmt.callee)) {
|
|
96
|
+
deps.add(stmt.callee);
|
|
97
|
+
}
|
|
98
|
+
if (stmt.kind === "method-call" && typeof stmt.callee === "string" && allFnNames.has(stmt.callee)) {
|
|
99
|
+
deps.add(stmt.callee);
|
|
100
|
+
}
|
|
101
|
+
for (const key of Object.keys(stmt)) {
|
|
102
|
+
const val = stmt[key];
|
|
103
|
+
if (Array.isArray(val)) {
|
|
104
|
+
for (const item of val) {
|
|
105
|
+
if (item && typeof item === "object" && typeof item.kind === "string") {
|
|
106
|
+
collectCallTargets(item, allFnNames, deps);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
else if (val && typeof val === "object" && typeof val.kind === "string") {
|
|
111
|
+
collectCallTargets(val, allFnNames, deps);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
function dfs(node, graph, color, path, onCycle) {
|
|
116
|
+
color.set(node, 1); // GRAY
|
|
117
|
+
path.push(node);
|
|
118
|
+
const neighbors = graph.get(node);
|
|
119
|
+
if (neighbors) {
|
|
120
|
+
for (const next of neighbors) {
|
|
121
|
+
const nextColor = color.get(next);
|
|
122
|
+
if (nextColor === 1) {
|
|
123
|
+
const cycleStart = path.indexOf(next);
|
|
124
|
+
const cycle = path.slice(cycleStart).concat(next);
|
|
125
|
+
onCycle(cycle);
|
|
126
|
+
}
|
|
127
|
+
else if (nextColor === 0) {
|
|
128
|
+
dfs(next, graph, color, path, onCycle);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
path.pop();
|
|
133
|
+
color.set(node, 2); // BLACK
|
|
134
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { SafetyMetadata } from "../../safety-hook.js";
|
|
2
|
+
/** Sidecar JSON schema for the safety metadata artifact. */
|
|
3
|
+
export interface SafetySidecar {
|
|
4
|
+
schemaVersion: string;
|
|
5
|
+
standard: string;
|
|
6
|
+
tool: string;
|
|
7
|
+
toolVersion: string;
|
|
8
|
+
generatedAt: string;
|
|
9
|
+
safetyFunctions: Array<{
|
|
10
|
+
name: string;
|
|
11
|
+
asilLevel: string;
|
|
12
|
+
source?: {
|
|
13
|
+
tsFile: string;
|
|
14
|
+
tsLine: number;
|
|
15
|
+
};
|
|
16
|
+
mechanisms: string[];
|
|
17
|
+
rules: Record<string, "pass" | {
|
|
18
|
+
severity: string;
|
|
19
|
+
message: string;
|
|
20
|
+
}>;
|
|
21
|
+
}>;
|
|
22
|
+
}
|
|
23
|
+
/** Render safety metadata as a sidecar JSON string.
|
|
24
|
+
* Pure function — does not touch the filesystem. */
|
|
25
|
+
export declare function renderSafetySidecar(metadata: SafetyMetadata, toolVersion: string): string;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/** Render safety metadata as a sidecar JSON string.
|
|
2
|
+
* Pure function — does not touch the filesystem. */
|
|
3
|
+
export function renderSafetySidecar(metadata, toolVersion) {
|
|
4
|
+
const payload = {
|
|
5
|
+
schemaVersion: "1.0.0",
|
|
6
|
+
standard: "ISO 26262 Part 6",
|
|
7
|
+
tool: "cuttlefish (safety)",
|
|
8
|
+
toolVersion,
|
|
9
|
+
generatedAt: new Date().toISOString(),
|
|
10
|
+
safetyFunctions: metadata.functions,
|
|
11
|
+
};
|
|
12
|
+
return JSON.stringify(payload, null, 2);
|
|
13
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
// The post-build IR transform. Walks the built ProgramIR and injects a
|
|
2
|
+
// safety.record_pin_mode companion statement after every pin-configuring
|
|
3
|
+
// gpio op — gpio.configure (the thin GPIO constructor / set re-configure)
|
|
4
|
+
// and gpio.read_cfg (the fused guarded-configure + read). This makes the
|
|
5
|
+
// runtime mode table authoritative regardless of how the user configured
|
|
6
|
+
// the pin.
|
|
7
|
+
//
|
|
8
|
+
// v3: the thin HAL replaced gpio.set_mode's Arduino mode strings with
|
|
9
|
+
// gpio.configure's flag tokens ("GPIO.OUTPUT | GPIO.PULL_UP"). The flags
|
|
10
|
+
// field carries token text at IR time; configure-shaped flag sets map onto
|
|
11
|
+
// the TrackedMode enum, anything else (a runtime expression, an open-drain
|
|
12
|
+
// combo with no tracked equivalent) records Unknown so the voter still sees
|
|
13
|
+
// a fresh table entry rather than a stale mode.
|
|
14
|
+
//
|
|
15
|
+
// Produces a NEW ProgramIR via mapProgramStatements; does not mutate the input.
|
|
16
|
+
// See docs/superpowers/specs/2026-07-27-safety-package-part-a-design.md.
|
|
17
|
+
import { mapProgramStatements } from "../../api/index.js";
|
|
18
|
+
import { mapFlagTokens } from "../hal/ops.js";
|
|
19
|
+
/** The gpio ops that (re)configure a pin's direction/flags. */
|
|
20
|
+
const PIN_MODE_OPS = new Set(["gpio.configure", "gpio.read_cfg"]);
|
|
21
|
+
export function pinModeInterceptPass(program, ctx) {
|
|
22
|
+
if (!ctx.safetyInUse)
|
|
23
|
+
return program;
|
|
24
|
+
return mapProgramStatements(program, (stmt) => {
|
|
25
|
+
const companions = collectPinModeCompanions(stmt);
|
|
26
|
+
return companions.length === 0 ? [stmt] : [stmt, ...companions];
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
/** If stmt is a hal-op statement carrying a pin-configuring gpio op, return a
|
|
30
|
+
* singleton array containing the companion record_pin_mode statement;
|
|
31
|
+
* else []. */
|
|
32
|
+
function collectPinModeCompanions(stmt) {
|
|
33
|
+
if (stmt.kind !== "hal-op")
|
|
34
|
+
return [];
|
|
35
|
+
const op = stmt.operation;
|
|
36
|
+
if (typeof op.operation !== "string")
|
|
37
|
+
return [];
|
|
38
|
+
if (!PIN_MODE_OPS.has(op.operation))
|
|
39
|
+
return [];
|
|
40
|
+
// gpio.configure / gpio.read_cfg carry { pin: number; flags: string }
|
|
41
|
+
// where flags is flag-token text ("GPIO.INPUT | GPIO.PULL_UP") or an
|
|
42
|
+
// unresolved runtime expression.
|
|
43
|
+
const pinModeOp = op;
|
|
44
|
+
if (typeof pinModeOp.pin !== "number" || typeof pinModeOp.flags !== "string")
|
|
45
|
+
return [];
|
|
46
|
+
return [makeCompanion(pinModeOp.pin, mapFlagTokens(pinModeOp.flags))];
|
|
47
|
+
}
|
|
48
|
+
function makeCompanion(pin, mode) {
|
|
49
|
+
const companionOp = {
|
|
50
|
+
operation: "safety.record_pin_mode",
|
|
51
|
+
pin,
|
|
52
|
+
mode,
|
|
53
|
+
};
|
|
54
|
+
return {
|
|
55
|
+
kind: "hal-op",
|
|
56
|
+
operation: companionOp,
|
|
57
|
+
returns_value: false,
|
|
58
|
+
sourceSpan: { startLine: 0, startColumn: 0, filePath: "<safety-companion>" },
|
|
59
|
+
};
|
|
60
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { RuntimePolyfillIR } from "../../api/index.js";
|
|
2
|
+
/** The pin-mode tracking table + accessors. Tree-shaken out unless
|
|
3
|
+
* __tc_safety_record_pin_mode appears in the program (registered in
|
|
4
|
+
* POLYFILL_HELPER_MAP). */
|
|
5
|
+
export declare function modeTablePolyfill(): RuntimePolyfillIR;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { emitResultStructs } from "./result-struct.js";
|
|
2
|
+
/** The pin-mode tracking table + accessors. Tree-shaken out unless
|
|
3
|
+
* __tc_safety_record_pin_mode appears in the program (registered in
|
|
4
|
+
* POLYFILL_HELPER_MAP). */
|
|
5
|
+
export function modeTablePolyfill() {
|
|
6
|
+
return {
|
|
7
|
+
kind: "polyfill",
|
|
8
|
+
id: "safety_mode_table",
|
|
9
|
+
domain: "embedded",
|
|
10
|
+
requiredIncludes: [],
|
|
11
|
+
forwardDeclarations: [],
|
|
12
|
+
helperStructs: [],
|
|
13
|
+
helperFunctions: [
|
|
14
|
+
emitResultStructs(),
|
|
15
|
+
`
|
|
16
|
+
namespace __tc_safety {
|
|
17
|
+
constexpr uint32_t TC_SAFETY_PIN_TABLE_SIZE = 255U;
|
|
18
|
+
|
|
19
|
+
enum class TrackedMode : uint32_t {
|
|
20
|
+
Unknown = 0x00000000U,
|
|
21
|
+
Input = 0x5A5A5A5AU,
|
|
22
|
+
Output = 0xA5A5A5A5U,
|
|
23
|
+
InputPullup = 0x3C3C3C3CU,
|
|
24
|
+
InputPulldown = 0xC3C3C3C3U,
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
// Raw array: unavoidable for O(1) pin-indexed lookup in an embedded shim.
|
|
28
|
+
// Zero-initialized namespace-scope static storage (Unknown = 0U) satisfies
|
|
29
|
+
// M3-2-1 (const-init) and M3-2-4 (trivial static init); no knownPatterns
|
|
30
|
+
// deviation needed (verified in Part A v1).
|
|
31
|
+
TrackedMode g_pin_mode_table[TC_SAFETY_PIN_TABLE_SIZE];
|
|
32
|
+
|
|
33
|
+
inline void record_pin_mode(uint32_t pin, uint32_t mode) {
|
|
34
|
+
if (pin < TC_SAFETY_PIN_TABLE_SIZE) {
|
|
35
|
+
g_pin_mode_table[pin] = static_cast<TrackedMode>(mode);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
inline TrackedMode get_pin_mode(uint32_t pin) {
|
|
40
|
+
if (pin < TC_SAFETY_PIN_TABLE_SIZE) {
|
|
41
|
+
return g_pin_mode_table[pin];
|
|
42
|
+
}
|
|
43
|
+
return TrackedMode::Unknown;
|
|
44
|
+
}
|
|
45
|
+
} // namespace __tc_safety
|
|
46
|
+
|
|
47
|
+
inline void __tc_safety_record_pin_mode(uint32_t pin, uint32_t mode) {
|
|
48
|
+
__tc_safety::record_pin_mode(pin, mode);
|
|
49
|
+
}
|
|
50
|
+
`.trim(),
|
|
51
|
+
],
|
|
52
|
+
shimMacros: [],
|
|
53
|
+
dependencies: [],
|
|
54
|
+
};
|
|
55
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { RuntimePolyfillIR } from "../../api/index.js";
|
|
2
|
+
/** All safety runtime polyfills, in dependency order (mode table before voter
|
|
3
|
+
* and write-verify, safe variable and safe int are standalone). Returned by
|
|
4
|
+
* the safety hook's buildPolyfills(); called from buildEmitterContext so each
|
|
5
|
+
* polyfill participates in usage-based tree-shaking via filterPolyfillHelpers. */
|
|
6
|
+
export declare function buildSafetyPolyfills(): RuntimePolyfillIR[];
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { modeTablePolyfill } from "./mode-table.js";
|
|
2
|
+
import { voterPolyfill } from "./vote.js";
|
|
3
|
+
import { safeVariablePolyfill } from "./safe-variable.js";
|
|
4
|
+
import { safeIntPolyfill } from "./safe-int.js";
|
|
5
|
+
import { writeVerifyPolyfill } from "./write-verify.js";
|
|
6
|
+
/** All safety runtime polyfills, in dependency order (mode table before voter
|
|
7
|
+
* and write-verify, safe variable and safe int are standalone). Returned by
|
|
8
|
+
* the safety hook's buildPolyfills(); called from buildEmitterContext so each
|
|
9
|
+
* polyfill participates in usage-based tree-shaking via filterPolyfillHelpers. */
|
|
10
|
+
export function buildSafetyPolyfills() {
|
|
11
|
+
return [modeTablePolyfill(), voterPolyfill(), writeVerifyPolyfill(), safeVariablePolyfill(), safeIntPolyfill()];
|
|
12
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/** Returns the C++ enum + struct definitions for the safety runtime.
|
|
2
|
+
*
|
|
3
|
+
* These are emitted by the polyfill as the SINGLE canonical source.
|
|
4
|
+
* @typecad/safety is skipped by the graph-builder (like @typecad/ui), so
|
|
5
|
+
* its TypeScript source is never parsed into the user program's IR — the
|
|
6
|
+
* type-decl emitter never sees the enums. The polyfill provides the
|
|
7
|
+
* definitions that both the voter and the user's switch statements
|
|
8
|
+
* reference. Works in both single-file and split-file emit modes because
|
|
9
|
+
* the polyfill is inlined into the entry file's source, visible to all
|
|
10
|
+
* functions in that translation unit. */
|
|
11
|
+
export declare function emitResultStructs(): string;
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
// C++ source strings for the safety result struct + fault enums. Emitted by
|
|
2
|
+
// the mode-table polyfill (Task 9) BEFORE the voter, so the voter can return
|
|
3
|
+
// a SafeReadResult.
|
|
4
|
+
//
|
|
5
|
+
// AUTOSAR C++14 compliance:
|
|
6
|
+
// - enum class (A7-2-1)
|
|
7
|
+
// - fixed-width types (A3-9-1): uint8_t
|
|
8
|
+
// - no C-style casts (M5-0-7), no malloc (A18-5-10)
|
|
9
|
+
// - the raw pin table in mode-table.ts is zero-initialized namespace-scope
|
|
10
|
+
// static storage, satisfying M3-2-1/M3-2-4. No knownPatterns deviation is
|
|
11
|
+
// expected; if a rule fires anyway, add one (see
|
|
12
|
+
// docs/superpowers/plans/2026-07-27-safety-package-part-a.md Task 17).
|
|
13
|
+
/** Returns the C++ enum + struct definitions for the safety runtime.
|
|
14
|
+
*
|
|
15
|
+
* These are emitted by the polyfill as the SINGLE canonical source.
|
|
16
|
+
* @typecad/safety is skipped by the graph-builder (like @typecad/ui), so
|
|
17
|
+
* its TypeScript source is never parsed into the user program's IR — the
|
|
18
|
+
* type-decl emitter never sees the enums. The polyfill provides the
|
|
19
|
+
* definitions that both the voter and the user's switch statements
|
|
20
|
+
* reference. Works in both single-file and split-file emit modes because
|
|
21
|
+
* the polyfill is inlined into the entry file's source, visible to all
|
|
22
|
+
* functions in that translation unit. */
|
|
23
|
+
export function emitResultStructs() {
|
|
24
|
+
return `
|
|
25
|
+
enum class SafetyStatus : uint32_t {
|
|
26
|
+
Ok = 0x5A5A5A5AU,
|
|
27
|
+
Fault = 0xA5A5A5A5U,
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
enum class SafetyFaultCategory : uint32_t {
|
|
31
|
+
Ok = 0x3C3C3C3CU,
|
|
32
|
+
Signal = 0x5A5A5A5AU,
|
|
33
|
+
Integrity = 0xA5A5A5A5U,
|
|
34
|
+
Timing = 0xC3C3C3C3U,
|
|
35
|
+
System = 0x55AA55AAU,
|
|
36
|
+
Configuration = 0xAA55AA55U,
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
enum class SafetyFaultCode : uint32_t {
|
|
40
|
+
Ok = 0x3C3C3C3CU,
|
|
41
|
+
VoteDisagreement = 0x5A5A5A5AU,
|
|
42
|
+
StuckHigh = 0xA5A5A5A5U,
|
|
43
|
+
StuckLow = 0xC3C3C3C3U,
|
|
44
|
+
PinModeMismatch = 0x55AA55AAU,
|
|
45
|
+
PinModeUnknown = 0xAA55AA55U,
|
|
46
|
+
WriteMismatch = 0x3C5AA5C3U,
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
struct SafeReadResult {
|
|
50
|
+
SafetyStatus status;
|
|
51
|
+
uint32_t value;
|
|
52
|
+
SafetyFaultCategory category;
|
|
53
|
+
SafetyFaultCode code;
|
|
54
|
+
|
|
55
|
+
// Chainable result handlers. Each calls the handler at runtime when its
|
|
56
|
+
// condition matches, then returns *this so chains compose and the result
|
|
57
|
+
// stays inspectable afterward. Template F&& accepts the inline C++ lambdas
|
|
58
|
+
// cuttlefish renders — no std::function, no heap allocation.
|
|
59
|
+
template <typename F>
|
|
60
|
+
SafeReadResult& ok(F&& handler) noexcept {
|
|
61
|
+
if (status == SafetyStatus::Ok) { handler(*this); }
|
|
62
|
+
return *this;
|
|
63
|
+
}
|
|
64
|
+
template <typename F>
|
|
65
|
+
SafeReadResult& fail(F&& handler) noexcept {
|
|
66
|
+
if (status != SafetyStatus::Ok) { handler(*this); }
|
|
67
|
+
return *this;
|
|
68
|
+
}
|
|
69
|
+
// fault is an alias of fail (no std::forward — avoids the <utility> header
|
|
70
|
+
// AVR lacks; the handler is called once immediately, so perfect forwarding
|
|
71
|
+
// is not load-bearing).
|
|
72
|
+
template <typename F>
|
|
73
|
+
SafeReadResult& fault(F&& handler) noexcept { return fail(handler); }
|
|
74
|
+
template <typename F>
|
|
75
|
+
SafeReadResult& always(F&& handler) noexcept { handler(*this); return *this; }
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
struct SafeWriteResult {
|
|
79
|
+
SafetyStatus status;
|
|
80
|
+
SafetyFaultCode code;
|
|
81
|
+
|
|
82
|
+
template <typename F>
|
|
83
|
+
SafeWriteResult& ok(F&& handler) noexcept {
|
|
84
|
+
if (status == SafetyStatus::Ok) { handler(*this); }
|
|
85
|
+
return *this;
|
|
86
|
+
}
|
|
87
|
+
template <typename F>
|
|
88
|
+
SafeWriteResult& fail(F&& handler) noexcept {
|
|
89
|
+
if (status != SafetyStatus::Ok) { handler(*this); }
|
|
90
|
+
return *this;
|
|
91
|
+
}
|
|
92
|
+
template <typename F>
|
|
93
|
+
SafeWriteResult& fault(F&& handler) noexcept { return fail(handler); }
|
|
94
|
+
template <typename F>
|
|
95
|
+
SafeWriteResult& always(F&& handler) noexcept { handler(*this); return *this; }
|
|
96
|
+
};
|
|
97
|
+
`.trim();
|
|
98
|
+
}
|