@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,32 @@
|
|
|
1
|
+
export {};
|
|
2
|
+
/** ASIL level annotations for functions — comment-based.
|
|
3
|
+
*
|
|
4
|
+
* ASIL levels are declared via leading comment annotations rather than
|
|
5
|
+
* TypeScript decorators because:
|
|
6
|
+
* - TS decorators on function declarations produce TS1206 errors in the
|
|
7
|
+
* VS Code language server and @typescript-eslint/parser
|
|
8
|
+
* - Comment annotations work in any editor, any toolchain, without
|
|
9
|
+
* `experimentalDecorators` or special config
|
|
10
|
+
*
|
|
11
|
+
* The cuttlefish IR builder extracts these from leading comments via
|
|
12
|
+
* extractAsilFromComments() in function-builder.ts.
|
|
13
|
+
*
|
|
14
|
+
* Part B's ISO 26262 checkers (analyzeIR) read the resulting `decorators`
|
|
15
|
+
* field on FunctionIR to decide which rules to enforce:
|
|
16
|
+
*
|
|
17
|
+
* // @asilD → all rules enforced (recursion, heap, loops)
|
|
18
|
+
* // @asilC → recursion + loops enforced (heap is ASIL D only)
|
|
19
|
+
* // @asilB → recursion enforced
|
|
20
|
+
* // @asilA → no rules enforced (quality-managed)
|
|
21
|
+
* (no annotation) → no rules enforced (implicit QM)
|
|
22
|
+
*
|
|
23
|
+
* Usage:
|
|
24
|
+
* // @asilD
|
|
25
|
+
* function engageBrake(): void {
|
|
26
|
+
* safe.write(brakePin, 1);
|
|
27
|
+
* }
|
|
28
|
+
*
|
|
29
|
+
* These are documentation-only exports — they have no runtime behavior
|
|
30
|
+
* and are never imported by user code. They exist so the ASIL level
|
|
31
|
+
* names appear in type hints and IDE autocomplete.
|
|
32
|
+
*/
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
export type { SafeVariable } from "./safe-variable-types.js";
|
|
2
|
+
export type { SafeInt } from "./safe-int-types.js";
|
|
3
|
+
import type { Pin } from "@typecad/hal";
|
|
4
|
+
/** The pin argument safe.read accepts — a thin-HAL `GPIO`. A named alias so
|
|
5
|
+
* the safety API's stringified type stays free of union syntax at call sites.
|
|
6
|
+
* At runtime the safety package resolves the same `_pin: number` field via
|
|
7
|
+
* the halInstances registry at IR time. */
|
|
8
|
+
export type AnyPin = Pin;
|
|
9
|
+
/** Two-tier fault taxonomy. Stable across safety standards (ISO 26262,
|
|
10
|
+
* IEC 61508, DO-178C). The category is the coarse user-space routing axis;
|
|
11
|
+
* the code is standard-specific detail.
|
|
12
|
+
*
|
|
13
|
+
* Declared as TypeScript `enum` (not `const` object + type alias) so the
|
|
14
|
+
* cuttlefish transpiler lowers these to a single C++ `enum class` definition
|
|
15
|
+
* matching the runtime polyfill's enum, rather than emitting a conflicting
|
|
16
|
+
* struct per usage scope. The transpiler's enum lowering and the polyfill's
|
|
17
|
+
* enum class are structurally identical (same tag name, same enumerators,
|
|
18
|
+
* same values). */
|
|
19
|
+
export declare enum SafetyFaultCategory {
|
|
20
|
+
Ok = 1010580540,
|
|
21
|
+
Signal = 1515870810,
|
|
22
|
+
Integrity = 2779096485,
|
|
23
|
+
Timing = 3284386755,
|
|
24
|
+
System = 1437226410,
|
|
25
|
+
Configuration = 2857740885
|
|
26
|
+
}
|
|
27
|
+
export declare enum SafetyFaultCode {
|
|
28
|
+
Ok = 1010580540,
|
|
29
|
+
VoteDisagreement = 1515870810,
|
|
30
|
+
StuckHigh = 2779096485,
|
|
31
|
+
StuckLow = 3284386755,
|
|
32
|
+
PinModeMismatch = 1437226410,
|
|
33
|
+
PinModeUnknown = 2857740885,
|
|
34
|
+
WriteMismatch = 1012573635
|
|
35
|
+
}
|
|
36
|
+
export declare enum SafetyStatus {
|
|
37
|
+
Ok = 1515870810,
|
|
38
|
+
Fault = 2779096485
|
|
39
|
+
}
|
|
40
|
+
export interface SafeReadResult {
|
|
41
|
+
readonly status: SafetyStatus;
|
|
42
|
+
readonly value: number;
|
|
43
|
+
readonly category: SafetyFaultCategory;
|
|
44
|
+
readonly code: SafetyFaultCode;
|
|
45
|
+
/** Run handler iff status === Ok. Handler receives the full result. Returns the result (chainable). */
|
|
46
|
+
ok(handler: (r: SafeReadResult) => void): SafeReadResult;
|
|
47
|
+
/** Run handler iff status !== Ok. Handler receives the full result. Returns the result (chainable). */
|
|
48
|
+
fail(handler: (r: SafeReadResult) => void): SafeReadResult;
|
|
49
|
+
/** Alias of fail(). */
|
|
50
|
+
fault(handler: (r: SafeReadResult) => void): SafeReadResult;
|
|
51
|
+
/** Run handler unconditionally. Returns the result (chainable). */
|
|
52
|
+
always(handler: (r: SafeReadResult) => void): SafeReadResult;
|
|
53
|
+
}
|
|
54
|
+
export interface SafeWriteResult {
|
|
55
|
+
readonly status: SafetyStatus;
|
|
56
|
+
readonly code: SafetyFaultCode;
|
|
57
|
+
ok(handler: (r: SafeWriteResult) => void): SafeWriteResult;
|
|
58
|
+
fail(handler: (r: SafeWriteResult) => void): SafeWriteResult;
|
|
59
|
+
fault(handler: (r: SafeWriteResult) => void): SafeWriteResult;
|
|
60
|
+
always(handler: (r: SafeWriteResult) => void): SafeWriteResult;
|
|
61
|
+
}
|
|
62
|
+
/** Safe GPIO API. Compile-time construct only — the cuttlefish transpiler
|
|
63
|
+
* intercepts safe.read and lowers it to a safety.read_safe HAL op. The
|
|
64
|
+
* type annotation (rather than `as const`) gives `safe` a single object
|
|
65
|
+
* type so member access doesn't lower to std::variant access — same reason
|
|
66
|
+
* @typecad/ui's `ui` uses an explicit type annotation. */
|
|
67
|
+
export declare const safe: {
|
|
68
|
+
/** Safe digital read: confirms the pin's recorded mode is INPUT or
|
|
69
|
+
* INPUT_PULLUP (via the auto-populated mode table), performs a 2-of-3
|
|
70
|
+
* vote via the strategy-injected __tc_gpio_read shim, returns a
|
|
71
|
+
* SafeReadResult carrying any detected fault.
|
|
72
|
+
*
|
|
73
|
+
* Accepts any thin-HAL GPIO regardless of its constructed flag set. The
|
|
74
|
+
* runtime mode-table check (driven by the GPIO's construction flags)
|
|
75
|
+
* rejects pins configured as OUTPUT (returns PinModeMismatch). */
|
|
76
|
+
read(pin: AnyPin): SafeReadResult;
|
|
77
|
+
/** Safe digital write with readback verification. Writes the value,
|
|
78
|
+
* immediately reads the physical pin state back, and returns a
|
|
79
|
+
* SafeWriteResult confirming the write succeeded or flagging a fault.
|
|
80
|
+
*
|
|
81
|
+
* Verifies the pin's recorded mode is OUTPUT (returns PinModeMismatch
|
|
82
|
+
* for non-output pins). On write/read mismatch, returns
|
|
83
|
+
* WriteMismatch. */
|
|
84
|
+
write(pin: Pin, value: number): SafeWriteResult;
|
|
85
|
+
};
|
|
@@ -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[];
|