@typecad/cuttlefish 1.0.0-alpha.13 → 1.0.0-alpha.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +82 -79
- package/assets/editor-extensions/typecad-debug/README.md +141 -0
- package/assets/editor-extensions/typecad-debug/out/extension.js +347 -0
- package/assets/editor-extensions/typecad-debug/out/extension.js.map +1 -0
- package/assets/editor-extensions/typecad-debug/package.json +76 -0
- package/assets/editor-extensions/typecad-ui/LICENSE +27 -0
- package/assets/editor-extensions/typecad-ui/README.md +52 -0
- package/assets/editor-extensions/typecad-ui/file-icon-dark.png +0 -0
- package/assets/editor-extensions/typecad-ui/file-icon-light.png +0 -0
- package/assets/editor-extensions/typecad-ui/icon.png +0 -0
- package/assets/editor-extensions/typecad-ui/language-configuration.json +43 -0
- package/assets/editor-extensions/typecad-ui/package.json +54 -0
- package/assets/editor-extensions/typecad-ui/snippets/typecad-ui.json +80 -0
- package/assets/editor-extensions/typecad-ui/syntaxes/markdown-ui.json +45 -0
- package/assets/editor-extensions/typecad-ui/syntaxes/typecad-ui.tmLanguage.json +1269 -0
- package/dist/api/config.d.ts +30 -27
- 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 +65 -507
- 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 +227 -244
- package/dist/config-loader.d.ts +14 -19
- package/dist/config-loader.js +242 -78
- package/dist/config-schema.d.ts +19 -227
- package/dist/config-schema.js +9 -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 +36 -127
- 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 +32 -0
- package/dist/create/editor-integration.js +257 -0
- package/dist/create/eslint-rules-template.d.ts +2 -2
- package/dist/create/framework-catalog.d.ts +48 -8
- package/dist/create/framework-catalog.js +58 -44
- package/dist/create/index.d.ts +8 -13
- package/dist/create/index.js +5 -9
- 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/{init-scaffold.d.ts → scaffold.d.ts} +8 -11
- package/dist/create/{init-scaffold.js → scaffold.js} +28 -87
- package/dist/create/templates.d.ts +51 -0
- package/dist/create/{init-templates.js → templates.js} +135 -116
- package/dist/create/wizard.d.ts +19 -0
- package/dist/create/wizard.js +314 -0
- 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 +147 -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 +64 -45
- 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/catalog.d.ts +35 -0
- package/dist/library/catalog.js +67 -0
- package/dist/library/cli.d.ts +2 -0
- package/dist/library/cli.js +168 -0
- package/dist/library/init.d.ts +25 -0
- package/dist/library/init.js +397 -0
- package/dist/library/install.d.ts +9 -0
- package/dist/library/install.js +80 -0
- package/dist/library/registry-search.d.ts +34 -0
- package/dist/library/registry-search.js +50 -0
- package/dist/library/validate.d.ts +17 -0
- package/dist/library/validate.js +175 -0
- package/dist/library-packages.d.ts +95 -0
- package/dist/library-packages.js +275 -0
- 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 +29 -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/preview/client.js +17 -3
- 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 +5 -6
- 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 +90 -29
- package/dist/types.d.ts +45 -26
- package/dist/ui-hook.d.ts +1 -1
- package/dist/utils/cli.d.ts +2 -2
- package/dist/utils/cli.js +160 -157
- 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 -149
- 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 -637
- package/dist/create/board-spec.js +0 -214
- package/dist/create/init-templates.d.ts +0 -28
- package/dist/create/init-wizard.d.ts +0 -8
- package/dist/create/init-wizard.js +0 -182
- 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,72 @@
|
|
|
1
|
+
import { asilLevel, ASIL_THRESHOLDS } from "./asil.js";
|
|
2
|
+
/** B6: No unconstrained goto (ISO 26262 Part 6, §7.4.8 — ASIL C+).
|
|
3
|
+
*
|
|
4
|
+
* Scans for goto statements in emitted IR. Cuttlefish's only source of
|
|
5
|
+
* goto is labeled break lowering (TS `break labelName;` → C++ `goto
|
|
6
|
+
* __break_labelName;`). While this is a controlled use, ISO 26262 C+
|
|
7
|
+
* restricts goto to specific patterns (forward, within the same block).
|
|
8
|
+
* Reports each goto as an info-level diagnostic for awareness. */
|
|
9
|
+
export function checkGoto(program, _ctx) {
|
|
10
|
+
const diags = [];
|
|
11
|
+
for (const fn of program.functions) {
|
|
12
|
+
if (asilLevel(fn.decorators) < ASIL_THRESHOLDS.unboundedLoop)
|
|
13
|
+
continue;
|
|
14
|
+
for (const stmt of fn.statements) {
|
|
15
|
+
scanForGoto(stmt, fn.originalName, diags);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
for (const cls of program.classes) {
|
|
19
|
+
for (const method of cls.methods) {
|
|
20
|
+
if (!method.name)
|
|
21
|
+
continue;
|
|
22
|
+
if (asilLevel(method.decorators) < ASIL_THRESHOLDS.unboundedLoop)
|
|
23
|
+
continue;
|
|
24
|
+
for (const stmt of method.statements) {
|
|
25
|
+
scanForGoto(stmt, method.name, diags);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return diags;
|
|
30
|
+
}
|
|
31
|
+
function scanForGoto(stmt, fnName, diags) {
|
|
32
|
+
if (!stmt || typeof stmt !== "object")
|
|
33
|
+
return;
|
|
34
|
+
// Check for goto in raw expressions (lowered from labeled break)
|
|
35
|
+
if (stmt.kind === "raw" && typeof stmt.value === "string") {
|
|
36
|
+
if (/\bgoto\s+\w+/.test(stmt.value)) {
|
|
37
|
+
const match = stmt.value.match(/\bgoto\s+(\w+)/);
|
|
38
|
+
const label = match ? match[1] : "unknown";
|
|
39
|
+
diags.push({
|
|
40
|
+
severity: "info",
|
|
41
|
+
code: "ISO26262_B6_GOTO",
|
|
42
|
+
message: `goto ${label} in ${fnName}() — lowered from labeled break`,
|
|
43
|
+
hint: "ISO 26262 C+ restricts goto. Consider restructuring with a flag variable or early return.",
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
// Check for the "labeled" statement kind (cuttlefish's IR representation
|
|
48
|
+
// of labeled break — the actual goto is emitted as a raw statement
|
|
49
|
+
// during rendering, but the labeled IR node is present at analysis time).
|
|
50
|
+
if (stmt.kind === "labeled" && typeof stmt.label === "string") {
|
|
51
|
+
diags.push({
|
|
52
|
+
severity: "info",
|
|
53
|
+
code: "ISO26262_B6_GOTO",
|
|
54
|
+
message: `Labeled break '${stmt.label}' in ${fnName}() will lower to goto`,
|
|
55
|
+
hint: "ISO 26262 C+ restricts goto. Consider restructuring with a flag variable or early return.",
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
// Recurse
|
|
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
|
+
scanForGoto(item, fnName, diags);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
else if (val && typeof val === "object" && typeof val.kind === "string") {
|
|
69
|
+
scanForGoto(val, fnName, diags);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { ProgramIR, Diagnostic } from "../../api/index.js";
|
|
2
|
+
import type { SafetyTransformContext } from "../../safety-hook.js";
|
|
3
|
+
/** B2: No dynamic allocation after init (ISO 26262 Part 6, §7.4.11 — ASIL D).
|
|
4
|
+
*
|
|
5
|
+
* Only checks functions decorated with @asilD.
|
|
6
|
+
* Lower ASIL levels and QM functions are skipped. */
|
|
7
|
+
export declare function checkDynamicAllocation(program: ProgramIR, _ctx: SafetyTransformContext): Diagnostic[];
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { asilLevel, ASIL_THRESHOLDS } from "./asil.js";
|
|
2
|
+
/** B2: No dynamic allocation after init (ISO 26262 Part 6, §7.4.11 — ASIL D).
|
|
3
|
+
*
|
|
4
|
+
* Only checks functions decorated with @asilD.
|
|
5
|
+
* Lower ASIL levels and QM functions are skipped. */
|
|
6
|
+
export function checkDynamicAllocation(program, _ctx) {
|
|
7
|
+
const diags = [];
|
|
8
|
+
const initFnNames = new Set(["setup", "main", "__top_level__"]);
|
|
9
|
+
for (const fn of program.functions) {
|
|
10
|
+
if (initFnNames.has(fn.originalName))
|
|
11
|
+
continue;
|
|
12
|
+
if (asilLevel(fn.decorators) < ASIL_THRESHOLDS.dynamicAlloc)
|
|
13
|
+
continue;
|
|
14
|
+
for (const stmt of fn.statements) {
|
|
15
|
+
scanForAllocation(stmt, fn.originalName, diags);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
for (const cls of program.classes) {
|
|
19
|
+
for (const method of cls.methods) {
|
|
20
|
+
if (!method.name || initFnNames.has(method.name))
|
|
21
|
+
continue;
|
|
22
|
+
if (asilLevel(method.decorators) < ASIL_THRESHOLDS.dynamicAlloc)
|
|
23
|
+
continue;
|
|
24
|
+
for (const stmt of method.statements) {
|
|
25
|
+
scanForAllocation(stmt, method.name, diags);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return diags;
|
|
30
|
+
}
|
|
31
|
+
function scanForAllocation(stmt, fnName, diags) {
|
|
32
|
+
if (!stmt || typeof stmt !== "object")
|
|
33
|
+
return;
|
|
34
|
+
if (stmt.kind === "raw" && typeof stmt.value === "string") {
|
|
35
|
+
if (/\bnew\s+[A-Z_]/.test(stmt.value) || /\bmalloc\s*\(/.test(stmt.value) || /\bcalloc\s*\(/.test(stmt.value)) {
|
|
36
|
+
diags.push({
|
|
37
|
+
severity: "error",
|
|
38
|
+
code: "ISO26262_B2_DYNAMIC_ALLOC",
|
|
39
|
+
message: `Dynamic allocation in ${fnName}(): ${stmt.value.trim().slice(0, 80)}`,
|
|
40
|
+
hint: "ASIL D forbids dynamic allocation after initialization. Pre-allocate in setup() or use a fixed-size buffer.",
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
for (const key of Object.keys(stmt)) {
|
|
45
|
+
const val = stmt[key];
|
|
46
|
+
if (Array.isArray(val)) {
|
|
47
|
+
for (const item of val) {
|
|
48
|
+
if (item && typeof item === "object" && typeof item.kind === "string") {
|
|
49
|
+
scanForAllocation(item, fnName, diags);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
else if (val && typeof val === "object" && typeof val.kind === "string") {
|
|
54
|
+
scanForAllocation(val, fnName, diags);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { ProgramIR, Diagnostic } from "../../api/index.js";
|
|
2
|
+
import type { SafetyTransformContext } from "../../safety-hook.js";
|
|
3
|
+
/** B5: Init completeness (ISO 26262 Part 6, §7.4.5 — ASIL D).
|
|
4
|
+
*
|
|
5
|
+
* Checks that every identifier used in a safety-critical function (ASIL D)
|
|
6
|
+
* was initialized in setup() or at file scope. An uninitialized variable
|
|
7
|
+
* in a safety-critical path is a potential source of undefined behavior.
|
|
8
|
+
*
|
|
9
|
+
* Approach: collect identifiers assigned/declared in setup() and top-level
|
|
10
|
+
* (the "init set"), then walk each ASIL D function's statements looking
|
|
11
|
+
* for identifiers used before any local assignment. Reports each as a
|
|
12
|
+
* warning. */
|
|
13
|
+
export declare function checkInitCompleteness(program: ProgramIR, _ctx: SafetyTransformContext): Diagnostic[];
|
|
@@ -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;
|