@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,61 @@
|
|
|
1
|
+
// Public API surface for cuttlefish safety — importable as
|
|
2
|
+
// '@typecad/cuttlefish/safety' (the legacy '@typecad/safety' specifier is
|
|
3
|
+
// still detected by the transpiler; detection is by name, no module
|
|
4
|
+
// resolution happens).
|
|
5
|
+
//
|
|
6
|
+
// `safe` is a COMPILE-TIME CONSTRUCT ONLY. The cuttlefish transpiler
|
|
7
|
+
// intercepts safe.read calls (via tryResolveSemanticCall → the safety hook's
|
|
8
|
+
// resolveSemanticCall) and lowers them to safety.read_safe HAL ops. The
|
|
9
|
+
// runtime stub below throws so importing `safe` from plain Node never
|
|
10
|
+
// silently works — mirrors how @typecad/ui's ui.mount behaves.
|
|
11
|
+
//
|
|
12
|
+
// Part A v2: mode configuration belongs to @typecad/hal (Pin.asInput() etc.).
|
|
13
|
+
// The safety engine owns only the verified read.
|
|
14
|
+
/** Two-tier fault taxonomy. Stable across safety standards (ISO 26262,
|
|
15
|
+
* IEC 61508, DO-178C). The category is the coarse user-space routing axis;
|
|
16
|
+
* the code is standard-specific detail.
|
|
17
|
+
*
|
|
18
|
+
* Declared as TypeScript `enum` (not `const` object + type alias) so the
|
|
19
|
+
* cuttlefish transpiler lowers these to a single C++ `enum class` definition
|
|
20
|
+
* matching the runtime polyfill's enum, rather than emitting a conflicting
|
|
21
|
+
* struct per usage scope. The transpiler's enum lowering and the polyfill's
|
|
22
|
+
* enum class are structurally identical (same tag name, same enumerators,
|
|
23
|
+
* same values). */
|
|
24
|
+
export var SafetyFaultCategory;
|
|
25
|
+
(function (SafetyFaultCategory) {
|
|
26
|
+
SafetyFaultCategory[SafetyFaultCategory["Ok"] = 1010580540] = "Ok";
|
|
27
|
+
SafetyFaultCategory[SafetyFaultCategory["Signal"] = 1515870810] = "Signal";
|
|
28
|
+
SafetyFaultCategory[SafetyFaultCategory["Integrity"] = 2779096485] = "Integrity";
|
|
29
|
+
SafetyFaultCategory[SafetyFaultCategory["Timing"] = 3284386755] = "Timing";
|
|
30
|
+
SafetyFaultCategory[SafetyFaultCategory["System"] = 1437226410] = "System";
|
|
31
|
+
SafetyFaultCategory[SafetyFaultCategory["Configuration"] = 2857740885] = "Configuration";
|
|
32
|
+
})(SafetyFaultCategory || (SafetyFaultCategory = {}));
|
|
33
|
+
export var SafetyFaultCode;
|
|
34
|
+
(function (SafetyFaultCode) {
|
|
35
|
+
SafetyFaultCode[SafetyFaultCode["Ok"] = 1010580540] = "Ok";
|
|
36
|
+
SafetyFaultCode[SafetyFaultCode["VoteDisagreement"] = 1515870810] = "VoteDisagreement";
|
|
37
|
+
SafetyFaultCode[SafetyFaultCode["StuckHigh"] = 2779096485] = "StuckHigh";
|
|
38
|
+
SafetyFaultCode[SafetyFaultCode["StuckLow"] = 3284386755] = "StuckLow";
|
|
39
|
+
SafetyFaultCode[SafetyFaultCode["PinModeMismatch"] = 1437226410] = "PinModeMismatch";
|
|
40
|
+
SafetyFaultCode[SafetyFaultCode["PinModeUnknown"] = 2857740885] = "PinModeUnknown";
|
|
41
|
+
SafetyFaultCode[SafetyFaultCode["WriteMismatch"] = 1012573635] = "WriteMismatch";
|
|
42
|
+
})(SafetyFaultCode || (SafetyFaultCode = {}));
|
|
43
|
+
export var SafetyStatus;
|
|
44
|
+
(function (SafetyStatus) {
|
|
45
|
+
SafetyStatus[SafetyStatus["Ok"] = 1515870810] = "Ok";
|
|
46
|
+
SafetyStatus[SafetyStatus["Fault"] = 2779096485] = "Fault";
|
|
47
|
+
})(SafetyStatus || (SafetyStatus = {}));
|
|
48
|
+
class CompileTimeOnly extends Error {
|
|
49
|
+
constructor() {
|
|
50
|
+
super("cuttlefish safety: `safe` is a compile-time construct. It is lowered by the cuttlefish transpiler and has no runtime implementation.");
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
/** Safe GPIO API. Compile-time construct only — the cuttlefish transpiler
|
|
54
|
+
* intercepts safe.read and lowers it to a safety.read_safe HAL op. The
|
|
55
|
+
* type annotation (rather than `as const`) gives `safe` a single object
|
|
56
|
+
* type so member access doesn't lower to std::variant access — same reason
|
|
57
|
+
* @typecad/ui's `ui` uses an explicit type annotation. */
|
|
58
|
+
export const safe = {
|
|
59
|
+
read(_pin) { throw new CompileTimeOnly(); },
|
|
60
|
+
write(_pin, _value) { throw new CompileTimeOnly(); },
|
|
61
|
+
};
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { pinModeInterceptPass } from "./passes/pinMode-intercept.js";
|
|
2
|
+
import { buildSafetyPolyfills } from "./runtime/polyfills.js";
|
|
3
|
+
import { halInstances } from "../ir/build-ir-state.js";
|
|
4
|
+
import { analyzeProgram } from "./iso26262/analyze.js";
|
|
5
|
+
import { collectSafetyMetadata } from "./iso26262/collect.js";
|
|
6
|
+
/** Resolve a safety.* HAL op to C++. MCU-agnostic: emits calls only to the
|
|
7
|
+
* safety package's own __tc_safety_* helpers (never to target-specific
|
|
8
|
+
* symbols like digitalRead). The strategy-injected __tc_gpio_read shim is
|
|
9
|
+
* called from inside the voter polyfill, not from here.
|
|
10
|
+
*
|
|
11
|
+
* `op.operation` is a closed union in cuttlefish's HALOpIR that does NOT
|
|
12
|
+
* include safety.* ops (those are owned by this package). The dispatch in
|
|
13
|
+
* routeHALOp() uses string-prefix matching, so by the time we get here the
|
|
14
|
+
* operation is a safety.* string — cast to `string` so the switch is an
|
|
15
|
+
* open comparison. Field access goes through a generic record view because
|
|
16
|
+
* the safety op shapes are not part of the HALOpIR union. */
|
|
17
|
+
function resolveSafetyOp(op) {
|
|
18
|
+
const operation = op.operation;
|
|
19
|
+
const fields = op;
|
|
20
|
+
switch (operation) {
|
|
21
|
+
case "safety.record_pin_mode":
|
|
22
|
+
// mode is a TrackedMode enum value (numeric) — emitted as-is.
|
|
23
|
+
return { code: `__tc_safety_record_pin_mode(${fields.pin}, ${fields.mode});` };
|
|
24
|
+
case "safety.read_safe":
|
|
25
|
+
return { expression: `__tc_safety::read_safe(${fields.pin})` };
|
|
26
|
+
case "safety.write_verify":
|
|
27
|
+
return { expression: `__tc_safety::write_verify(${fields.pin}, ${fields.value})` };
|
|
28
|
+
default:
|
|
29
|
+
return undefined;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
/** Resolve a Pin-instance argument to its pin number at IR time, using the
|
|
33
|
+
* same halInstances registry the HAL layer uses to resolve `this._pin`
|
|
34
|
+
* inside Pin method bodies. Returns undefined for args that aren't tracked
|
|
35
|
+
* Pin instances (the caller emits a diagnostic in that case). Also accepts
|
|
36
|
+
* literal numbers as a fallback (for code that constructs a Pin inline at
|
|
37
|
+
* the call site, e.g. safe.read(Pin.fromPort('PD2')) — though the idiomatic
|
|
38
|
+
* form is to assign the Pin to a local first). */
|
|
39
|
+
function resolvePinArg(arg) {
|
|
40
|
+
if (typeof arg === "number")
|
|
41
|
+
return arg;
|
|
42
|
+
if (typeof arg === "string") {
|
|
43
|
+
// Identifier — look up in halInstances for a Pin instance with _pin.
|
|
44
|
+
const inst = halInstances.get(arg);
|
|
45
|
+
if (inst) {
|
|
46
|
+
const pin = inst.fieldValues.get("_pin") ?? inst.fieldValues.get("pin");
|
|
47
|
+
if (pin !== undefined)
|
|
48
|
+
return Number(pin);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return undefined;
|
|
52
|
+
}
|
|
53
|
+
/** Lower a safe.* TS call to a HAL op. Called from tryResolveSemanticCall
|
|
54
|
+
* (hal-plugins.ts) when the callee resolves to the @typecad/safety package. */
|
|
55
|
+
function resolveSemanticCall(callee, args) {
|
|
56
|
+
// `safe.read(Pin)` → safety.read_safe (pin resolved at IR time)
|
|
57
|
+
if (callee === "safe.read") {
|
|
58
|
+
const pin = resolvePinArg(args[0]);
|
|
59
|
+
if (pin === undefined)
|
|
60
|
+
return undefined;
|
|
61
|
+
return { operation: "safety.read_safe", pin };
|
|
62
|
+
}
|
|
63
|
+
// `safe.write(OutputPin, value)` → safety.write_verify
|
|
64
|
+
// value may be a number (literal) or string (rendered C++ expression text)
|
|
65
|
+
if (callee === "safe.write") {
|
|
66
|
+
const pin = resolvePinArg(args[0]);
|
|
67
|
+
if (pin === undefined)
|
|
68
|
+
return undefined;
|
|
69
|
+
const value = args[1] ?? 0;
|
|
70
|
+
return { operation: "safety.write_verify", pin, value };
|
|
71
|
+
}
|
|
72
|
+
return undefined;
|
|
73
|
+
}
|
|
74
|
+
export function registerSafetyEngine() {
|
|
75
|
+
return {
|
|
76
|
+
transformIR: pinModeInterceptPass,
|
|
77
|
+
resolveSafetyOp,
|
|
78
|
+
resolveSemanticCall,
|
|
79
|
+
buildPolyfills: buildSafetyPolyfills,
|
|
80
|
+
analyzeIR: analyzeProgram,
|
|
81
|
+
collectSafetyMetadata,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/** 5-value pin mode, mapped from gpio.configure's flag tokens at IR time.
|
|
2
|
+
* Numeric so the voter switches on enum values (no strcmp at runtime). The
|
|
3
|
+
* values are 32-bit Hamming-distance constants (0x5A5A5A5A etc.) chosen for
|
|
4
|
+
* SEU resistance — a single-bit flip never lands on another valid mode.
|
|
5
|
+
* This makes the C++ mode table 4 bytes/pin (~1 KB for the 255-entry table
|
|
6
|
+
* on AVR, which has 2 KB SRAM). That is a deliberate trade-off vs 1-byte
|
|
7
|
+
* storage: the SEU resistance is the whole point of the safety package.
|
|
8
|
+
* Matches the C++ enum class TrackedMode in runtime/mode-table.ts. */
|
|
9
|
+
export declare enum TrackedMode {
|
|
10
|
+
Unknown = 0,
|
|
11
|
+
Input = 1515870810,
|
|
12
|
+
Output = 2779096485,
|
|
13
|
+
InputPullup = 1010580540,
|
|
14
|
+
InputPulldown = 3284386755
|
|
15
|
+
}
|
|
16
|
+
/** Map gpio.configure flag-token text ("GPIO.INPUT | GPIO.PULL_UP", the
|
|
17
|
+
* thin GPIO constructor's resolved flags) to the TrackedMode enum. Used by
|
|
18
|
+
* the intercept pass at IR time. Direction wins over pulls; combos with no
|
|
19
|
+
* tracked equivalent (open-drain, runtime expressions) map to Unknown so
|
|
20
|
+
* the table still gets a fresh entry. */
|
|
21
|
+
export declare function mapFlagTokens(flags: string): TrackedMode;
|
|
22
|
+
/** Companion op injected after every gpio.set_mode by the pinMode-intercept
|
|
23
|
+
* pass. Records the mode in the runtime safety mode table. */
|
|
24
|
+
export interface SafetyRecordPinModeOp {
|
|
25
|
+
operation: "safety.record_pin_mode";
|
|
26
|
+
pin: number;
|
|
27
|
+
/** TrackedMode enum value (numeric) — mapped at IR time from the
|
|
28
|
+
* gpio.set_mode op's mode string. */
|
|
29
|
+
mode: TrackedMode;
|
|
30
|
+
}
|
|
31
|
+
/** Op produced by `safe.read(Pin)`. Resolves to a C++ expression returning
|
|
32
|
+
* a SafeReadResult. The pin number is resolved at IR time from the Pin
|
|
33
|
+
* instance via the halInstances registry. */
|
|
34
|
+
export interface SafetyReadSafeOp {
|
|
35
|
+
operation: "safety.read_safe";
|
|
36
|
+
pin: number;
|
|
37
|
+
}
|
|
38
|
+
/** Op produced by `safe.write(OutputPin, value)`. Resolves to a C++ expression
|
|
39
|
+
* returning a SafeWriteResult. The pin number is resolved at IR time from the
|
|
40
|
+
* Pin instance via the halInstances registry. The value may be a numeric
|
|
41
|
+
* literal or a string containing the rendered C++ expression text (e.g.
|
|
42
|
+
* "r.value", "x + 1"). */
|
|
43
|
+
export interface SafetyWriteVerifyOp {
|
|
44
|
+
operation: "safety.write_verify";
|
|
45
|
+
pin: number;
|
|
46
|
+
value: number | string;
|
|
47
|
+
}
|
|
48
|
+
export type SafetyHALOp = SafetyRecordPinModeOp | SafetyReadSafeOp | SafetyWriteVerifyOp;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
// Safety HAL op definitions. These are dispatched by their `operation` string
|
|
2
|
+
// prefix ("safety.*") via routeHALOp() in cuttlefish core. The strategy layer
|
|
3
|
+
// NEVER resolves them — they are resolved by the safety package's
|
|
4
|
+
// resolveSafetyOp, which is MCU-agnostic (it emits calls only to the safety
|
|
5
|
+
// package's own __tc_safety_* helpers and the strategy-injected
|
|
6
|
+
// __tc_gpio_read shim — never to target-specific symbols like digitalRead).
|
|
7
|
+
/** 5-value pin mode, mapped from gpio.configure's flag tokens at IR time.
|
|
8
|
+
* Numeric so the voter switches on enum values (no strcmp at runtime). The
|
|
9
|
+
* values are 32-bit Hamming-distance constants (0x5A5A5A5A etc.) chosen for
|
|
10
|
+
* SEU resistance — a single-bit flip never lands on another valid mode.
|
|
11
|
+
* This makes the C++ mode table 4 bytes/pin (~1 KB for the 255-entry table
|
|
12
|
+
* on AVR, which has 2 KB SRAM). That is a deliberate trade-off vs 1-byte
|
|
13
|
+
* storage: the SEU resistance is the whole point of the safety package.
|
|
14
|
+
* Matches the C++ enum class TrackedMode in runtime/mode-table.ts. */
|
|
15
|
+
export var TrackedMode;
|
|
16
|
+
(function (TrackedMode) {
|
|
17
|
+
TrackedMode[TrackedMode["Unknown"] = 0] = "Unknown";
|
|
18
|
+
TrackedMode[TrackedMode["Input"] = 1515870810] = "Input";
|
|
19
|
+
TrackedMode[TrackedMode["Output"] = 2779096485] = "Output";
|
|
20
|
+
TrackedMode[TrackedMode["InputPullup"] = 1010580540] = "InputPullup";
|
|
21
|
+
TrackedMode[TrackedMode["InputPulldown"] = 3284386755] = "InputPulldown";
|
|
22
|
+
})(TrackedMode || (TrackedMode = {}));
|
|
23
|
+
/** Map gpio.configure flag-token text ("GPIO.INPUT | GPIO.PULL_UP", the
|
|
24
|
+
* thin GPIO constructor's resolved flags) to the TrackedMode enum. Used by
|
|
25
|
+
* the intercept pass at IR time. Direction wins over pulls; combos with no
|
|
26
|
+
* tracked equivalent (open-drain, runtime expressions) map to Unknown so
|
|
27
|
+
* the table still gets a fresh entry. */
|
|
28
|
+
export function mapFlagTokens(flags) {
|
|
29
|
+
// Only a pure token list ("GPIO.INPUT | GPIO.PULL_UP") is statically
|
|
30
|
+
// classifiable — anything else (runtime expression) is Unknown.
|
|
31
|
+
if (!/^\s*GPIO\.[A-Z0-9_]+(\s*\|\s*GPIO\.[A-Z0-9_]+)*\s*$/.test(flags)) {
|
|
32
|
+
return TrackedMode.Unknown;
|
|
33
|
+
}
|
|
34
|
+
const output = flags.includes("GPIO.OUTPUT");
|
|
35
|
+
const input = flags.includes("GPIO.INPUT");
|
|
36
|
+
if (output && !input)
|
|
37
|
+
return TrackedMode.Output;
|
|
38
|
+
if (input || output) {
|
|
39
|
+
if (flags.includes("GPIO.PULL_UP"))
|
|
40
|
+
return TrackedMode.InputPullup;
|
|
41
|
+
if (flags.includes("GPIO.PULL_DOWN"))
|
|
42
|
+
return TrackedMode.InputPulldown;
|
|
43
|
+
return TrackedMode.Input;
|
|
44
|
+
}
|
|
45
|
+
return TrackedMode.Unknown;
|
|
46
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { ProgramIR, Diagnostic } from "../../api/index.js";
|
|
2
|
+
import type { SafetyTransformContext } from "../../safety-hook.js";
|
|
3
|
+
/** Part B: ISO 26262 Part 6 software-rule checker.
|
|
4
|
+
*
|
|
5
|
+
* Runs all checkers against the final transformed IR and returns aggregated
|
|
6
|
+
* diagnostics. Each checker is independent — a failure in one does not
|
|
7
|
+
* prevent the others from running.
|
|
8
|
+
*
|
|
9
|
+
* Diagnostics with severity "error" abort the build (via
|
|
10
|
+
* throwIfFatalDiagnostics in transpile.ts); "warning"/"info" are logged
|
|
11
|
+
* but the build succeeds.
|
|
12
|
+
*/
|
|
13
|
+
export declare function analyzeProgram(program: ProgramIR, ctx: SafetyTransformContext): Diagnostic[];
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { checkRecursion } from "./recursion-checker.js";
|
|
2
|
+
import { checkDynamicAllocation } from "./heap-checker.js";
|
|
3
|
+
import { checkUnboundedLoops } from "./loop-checker.js";
|
|
4
|
+
import { checkInitCompleteness } from "./init-checker.js";
|
|
5
|
+
import { checkGoto } from "./goto-checker.js";
|
|
6
|
+
/** Part B: ISO 26262 Part 6 software-rule checker.
|
|
7
|
+
*
|
|
8
|
+
* Runs all checkers against the final transformed IR and returns aggregated
|
|
9
|
+
* diagnostics. Each checker is independent — a failure in one does not
|
|
10
|
+
* prevent the others from running.
|
|
11
|
+
*
|
|
12
|
+
* Diagnostics with severity "error" abort the build (via
|
|
13
|
+
* throwIfFatalDiagnostics in transpile.ts); "warning"/"info" are logged
|
|
14
|
+
* but the build succeeds.
|
|
15
|
+
*/
|
|
16
|
+
export function analyzeProgram(program, ctx) {
|
|
17
|
+
const diags = [];
|
|
18
|
+
for (const checker of [checkRecursion, checkDynamicAllocation, checkUnboundedLoops, checkInitCompleteness, checkGoto]) {
|
|
19
|
+
try {
|
|
20
|
+
const results = checker(program, ctx);
|
|
21
|
+
diags.push(...results);
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
// A checker crash should not prevent other checkers from running.
|
|
25
|
+
// The diagnostic is lost; the build continues.
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return diags;
|
|
29
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/** Determine the ASIL level of a function from its decorators.
|
|
2
|
+
* Returns a numeric severity: D=4, C=3, B=2, A=1, QM/none=0.
|
|
3
|
+
* Higher = more rules enforced. */
|
|
4
|
+
export declare function asilLevel(decorators?: string[]): number;
|
|
5
|
+
/** Minimum ASIL level at which each rule applies. */
|
|
6
|
+
export declare const ASIL_THRESHOLDS: {
|
|
7
|
+
readonly recursion: 2;
|
|
8
|
+
readonly dynamicAlloc: 4;
|
|
9
|
+
readonly unboundedLoop: 3;
|
|
10
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/** Determine the ASIL level of a function from its decorators.
|
|
2
|
+
* Returns a numeric severity: D=4, C=3, B=2, A=1, QM/none=0.
|
|
3
|
+
* Higher = more rules enforced. */
|
|
4
|
+
export function asilLevel(decorators) {
|
|
5
|
+
if (!decorators)
|
|
6
|
+
return 0;
|
|
7
|
+
if (decorators.includes("asilD"))
|
|
8
|
+
return 4;
|
|
9
|
+
if (decorators.includes("asilC"))
|
|
10
|
+
return 3;
|
|
11
|
+
if (decorators.includes("asilB"))
|
|
12
|
+
return 2;
|
|
13
|
+
if (decorators.includes("asilA"))
|
|
14
|
+
return 1;
|
|
15
|
+
return 0;
|
|
16
|
+
}
|
|
17
|
+
/** Minimum ASIL level at which each rule applies. */
|
|
18
|
+
export const ASIL_THRESHOLDS = {
|
|
19
|
+
recursion: 2, // B+ — recursion forbidden
|
|
20
|
+
dynamicAlloc: 4, // D only — heap forbidden after init
|
|
21
|
+
unboundedLoop: 3, // C+ — unbounded loops warned
|
|
22
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { ProgramIR } from "../../api/index.js";
|
|
2
|
+
import type { SafetyTransformContext, SafetyMetadata } from "../../safety-hook.js";
|
|
3
|
+
/** Part C: collect safety metadata for sidecar artifact generation.
|
|
4
|
+
*
|
|
5
|
+
* Walks the program IR and collects structured metadata about each
|
|
6
|
+
* safety-critical function (those with ASIL annotations):
|
|
7
|
+
* - ASIL level (from // @asilD / @asilC / @asilB comments)
|
|
8
|
+
* - Source location (TS file + line)
|
|
9
|
+
* - Safety mechanisms used (safe.read, safe.write, SafeVariable, SafeInt)
|
|
10
|
+
* - Rule check results (B1–B6 pass/warning/error)
|
|
11
|
+
*
|
|
12
|
+
* The caller writes the returned metadata as a sidecar JSON file
|
|
13
|
+
* alongside the emitted C++ artifact. */
|
|
14
|
+
export declare function collectSafetyMetadata(program: ProgramIR, ctx: SafetyTransformContext): SafetyMetadata[];
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { asilLevel } from "./asil.js";
|
|
2
|
+
import { analyzeProgram } from "./analyze.js";
|
|
3
|
+
/** Part C: collect safety metadata for sidecar artifact generation.
|
|
4
|
+
*
|
|
5
|
+
* Walks the program IR and collects structured metadata about each
|
|
6
|
+
* safety-critical function (those with ASIL annotations):
|
|
7
|
+
* - ASIL level (from // @asilD / @asilC / @asilB comments)
|
|
8
|
+
* - Source location (TS file + line)
|
|
9
|
+
* - Safety mechanisms used (safe.read, safe.write, SafeVariable, SafeInt)
|
|
10
|
+
* - Rule check results (B1–B6 pass/warning/error)
|
|
11
|
+
*
|
|
12
|
+
* The caller writes the returned metadata as a sidecar JSON file
|
|
13
|
+
* alongside the emitted C++ artifact. */
|
|
14
|
+
export function collectSafetyMetadata(program, ctx) {
|
|
15
|
+
const functions = [];
|
|
16
|
+
// Run the analyzers to get current rule results per function
|
|
17
|
+
const diagnostics = analyzeProgram(program, ctx);
|
|
18
|
+
const diagsByFunction = new Map();
|
|
19
|
+
for (const d of diagnostics) {
|
|
20
|
+
// Extract function name from the message (format: "Recursion detected: ..."
|
|
21
|
+
// or "Dynamic allocation in fnName(): ...")
|
|
22
|
+
const fnMatch = d.message.match(/in (\w+)\(\)/);
|
|
23
|
+
const fnName = fnMatch ? fnMatch[1] : "";
|
|
24
|
+
if (fnName) {
|
|
25
|
+
if (!diagsByFunction.has(fnName)) {
|
|
26
|
+
diagsByFunction.set(fnName, []);
|
|
27
|
+
}
|
|
28
|
+
diagsByFunction.get(fnName).push(d);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
const allRuleIds = ["B1_RECURSION", "B2_DYNAMIC_ALLOC", "B3_UNBOUNDED_LOOP", "B5_INIT_COMPLETENESS", "B6_GOTO"];
|
|
32
|
+
for (const fn of program.functions) {
|
|
33
|
+
const level = asilLevel(fn.decorators);
|
|
34
|
+
if (level === 0)
|
|
35
|
+
continue;
|
|
36
|
+
const asilName = level === 4 ? "D" : level === 3 ? "C" : level === 2 ? "B" : level === 1 ? "A" : "QM";
|
|
37
|
+
// Detect safety mechanisms used in this function
|
|
38
|
+
const mechanisms = detectMechanisms(fn);
|
|
39
|
+
// Build rule results: pass by default, override with diagnostics
|
|
40
|
+
const rules = {};
|
|
41
|
+
for (const ruleId of allRuleIds) {
|
|
42
|
+
const fullCode = `ISO26262_${ruleId}`;
|
|
43
|
+
const matchingDiags = (diagsByFunction.get(fn.originalName) ?? [])
|
|
44
|
+
.filter(d => d.code === fullCode);
|
|
45
|
+
if (matchingDiags.length > 0) {
|
|
46
|
+
// Use the first matching diagnostic (they're all the same rule)
|
|
47
|
+
rules[ruleId] = {
|
|
48
|
+
severity: matchingDiags[0].severity,
|
|
49
|
+
message: matchingDiags[0].message,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
rules[ruleId] = "pass";
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
functions.push({
|
|
57
|
+
name: fn.originalName,
|
|
58
|
+
asilLevel: asilName,
|
|
59
|
+
source: fn.sourceSpan ? {
|
|
60
|
+
tsFile: fn.sourceSpan.filePath,
|
|
61
|
+
tsLine: fn.sourceSpan.startLine,
|
|
62
|
+
} : undefined,
|
|
63
|
+
mechanisms,
|
|
64
|
+
rules,
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
// Also scan class methods
|
|
68
|
+
for (const cls of program.classes) {
|
|
69
|
+
for (const method of cls.methods) {
|
|
70
|
+
if (!method.name)
|
|
71
|
+
continue;
|
|
72
|
+
const level = asilLevel(method.decorators);
|
|
73
|
+
if (level === 0)
|
|
74
|
+
continue;
|
|
75
|
+
const asilName = level === 4 ? "D" : level === 3 ? "C" : level === 2 ? "B" : level === 1 ? "A" : "QM";
|
|
76
|
+
const mechanisms = detectMechanisms({ parameters: [], statements: method.statements });
|
|
77
|
+
const rules = {};
|
|
78
|
+
for (const ruleId of allRuleIds) {
|
|
79
|
+
rules[ruleId] = "pass";
|
|
80
|
+
}
|
|
81
|
+
functions.push({
|
|
82
|
+
name: `${cls.name}::${method.name}`,
|
|
83
|
+
asilLevel: asilName,
|
|
84
|
+
mechanisms,
|
|
85
|
+
rules,
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return [{ functions }];
|
|
90
|
+
}
|
|
91
|
+
/** Scan a function's statements for safety-mechanism usage. */
|
|
92
|
+
function detectMechanisms(fn) {
|
|
93
|
+
const mechanisms = new Set();
|
|
94
|
+
const scan = (stmt) => {
|
|
95
|
+
if (!stmt || typeof stmt !== "object")
|
|
96
|
+
return;
|
|
97
|
+
// HAL ops (safe.read → safety.read_safe, safe.write → safety.write_verify)
|
|
98
|
+
if (stmt.kind === "hal-op" && typeof stmt.operation === "string") {
|
|
99
|
+
if (stmt.operation === "safety.read_safe")
|
|
100
|
+
mechanisms.add("safe.read");
|
|
101
|
+
if (stmt.operation === "safety.write_verify")
|
|
102
|
+
mechanisms.add("safe.write");
|
|
103
|
+
}
|
|
104
|
+
// Raw expressions mentioning SafeVariable or SafeInt
|
|
105
|
+
if (stmt.kind === "raw" && typeof stmt.value === "string") {
|
|
106
|
+
if (/\bSafeVariable</.test(stmt.value))
|
|
107
|
+
mechanisms.add("SafeVariable");
|
|
108
|
+
if (/\bSafeInt/.test(stmt.value))
|
|
109
|
+
mechanisms.add("SafeInt");
|
|
110
|
+
}
|
|
111
|
+
// var_decl with cppType containing SafeVariable<SafeInt
|
|
112
|
+
if (stmt.kind === "var_decl" && typeof stmt.cppType === "string") {
|
|
113
|
+
if (/SafeVariable</.test(stmt.cppType))
|
|
114
|
+
mechanisms.add("SafeVariable");
|
|
115
|
+
if (/SafeInt/.test(stmt.cppType))
|
|
116
|
+
mechanisms.add("SafeInt");
|
|
117
|
+
}
|
|
118
|
+
// Recurse
|
|
119
|
+
for (const key of Object.keys(stmt)) {
|
|
120
|
+
const val = stmt[key];
|
|
121
|
+
if (Array.isArray(val)) {
|
|
122
|
+
for (const item of val) {
|
|
123
|
+
if (item && typeof item === "object" && typeof item.kind === "string") {
|
|
124
|
+
scan(item);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
else if (val && typeof val === "object" && typeof val.kind === "string") {
|
|
129
|
+
scan(val);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
for (const stmt of fn.statements) {
|
|
134
|
+
scan(stmt);
|
|
135
|
+
}
|
|
136
|
+
return [...mechanisms].sort();
|
|
137
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ProgramIR, Diagnostic } from "../../api/index.js";
|
|
2
|
+
import type { SafetyTransformContext } from "../../safety-hook.js";
|
|
3
|
+
/** B6: No unconstrained goto (ISO 26262 Part 6, §7.4.8 — ASIL C+).
|
|
4
|
+
*
|
|
5
|
+
* Scans for goto statements in emitted IR. Cuttlefish's only source of
|
|
6
|
+
* goto is labeled break lowering (TS `break labelName;` → C++ `goto
|
|
7
|
+
* __break_labelName;`). While this is a controlled use, ISO 26262 C+
|
|
8
|
+
* restricts goto to specific patterns (forward, within the same block).
|
|
9
|
+
* Reports each goto as an info-level diagnostic for awareness. */
|
|
10
|
+
export declare function checkGoto(program: ProgramIR, _ctx: SafetyTransformContext): Diagnostic[];
|
|
@@ -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[];
|