@typecad/cuttlefish 0.1.0-alpha.1
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/LICENSE +21 -0
- package/README.md +79 -0
- package/dist/api/board-types.d.ts +1 -0
- package/dist/api/board-types.js +4 -0
- package/dist/api/capabilities.d.ts +12 -0
- package/dist/api/capabilities.js +1 -0
- package/dist/api/config.d.ts +120 -0
- package/dist/api/config.js +7 -0
- package/dist/api/index.d.ts +5 -0
- package/dist/api/index.js +2 -0
- package/dist/api/schema/builder.d.ts +14 -0
- package/dist/api/schema/builder.js +15 -0
- package/dist/api/schema/index.d.ts +2 -0
- package/dist/api/schema/index.js +1 -0
- package/dist/api/schema/types.d.ts +257 -0
- package/dist/api/schema/types.js +4 -0
- package/dist/api/shared/async-runtime-static.d.ts +8 -0
- package/dist/api/shared/async-runtime-static.js +201 -0
- package/dist/api/shared/async-types.d.ts +42 -0
- package/dist/api/shared/async-types.js +8 -0
- package/dist/api/shared/board-resolver.d.ts +12 -0
- package/dist/api/shared/board-resolver.js +7 -0
- package/dist/api/shared/cpp-type-ir.d.ts +229 -0
- package/dist/api/shared/cpp-type-ir.js +672 -0
- package/dist/api/shared/display-adapter.d.ts +12 -0
- package/dist/api/shared/display-adapter.js +145 -0
- package/dist/api/shared/display-adapters/eink-mono.d.ts +2 -0
- package/dist/api/shared/display-adapters/eink-mono.js +53 -0
- package/dist/api/shared/display-adapters/sdl.d.ts +2 -0
- package/dist/api/shared/display-adapters/sdl.js +452 -0
- package/dist/api/shared/display-adapters/ssd1309.d.ts +2 -0
- package/dist/api/shared/display-adapters/ssd1309.js +136 -0
- package/dist/api/shared/display-adapters/st7796.d.ts +2 -0
- package/dist/api/shared/display-adapters/st7796.js +132 -0
- package/dist/api/shared/display-capabilities.d.ts +45 -0
- package/dist/api/shared/display-capabilities.js +54 -0
- package/dist/api/shared/display-op-ir.d.ts +62 -0
- package/dist/api/shared/display-op-ir.js +19 -0
- package/dist/api/shared/display-profile.d.ts +265 -0
- package/dist/api/shared/display-profile.js +338 -0
- package/dist/api/shared/graphics-strategy.d.ts +29 -0
- package/dist/api/shared/graphics-strategy.js +11 -0
- package/dist/api/shared/hal-op-ir.d.ts +393 -0
- package/dist/api/shared/hal-op-ir.js +18 -0
- package/dist/api/shared/index.d.ts +26 -0
- package/dist/api/shared/index.js +21 -0
- package/dist/api/shared/ir-core.d.ts +363 -0
- package/dist/api/shared/ir-core.js +8 -0
- package/dist/api/shared/ir-declarations.d.ts +201 -0
- package/dist/api/shared/ir-declarations.js +26 -0
- package/dist/api/shared/ir.d.ts +85 -0
- package/dist/api/shared/ir.js +13 -0
- package/dist/api/shared/platform-strategy.d.ts +402 -0
- package/dist/api/shared/platform-strategy.js +14 -0
- package/dist/api/shared/polyfill-helper-registry.d.ts +11 -0
- package/dist/api/shared/polyfill-helper-registry.js +86 -0
- package/dist/api/shared/polyfill-types.d.ts +33 -0
- package/dist/api/shared/polyfill-types.js +76 -0
- package/dist/api/shared/promise-runtime.d.ts +10 -0
- package/dist/api/shared/promise-runtime.js +244 -0
- package/dist/api/shared/snprintf-types.d.ts +29 -0
- package/dist/api/shared/snprintf-types.js +8 -0
- package/dist/api/shared/string-method-registry.d.ts +53 -0
- package/dist/api/shared/string-method-registry.js +168 -0
- package/dist/api/shared/toolchain-types.d.ts +63 -0
- package/dist/api/shared/toolchain-types.js +71 -0
- package/dist/api/shared/types.d.ts +50 -0
- package/dist/api/shared/types.js +6 -0
- package/dist/ast/parse.d.ts +2 -0
- package/dist/ast/parse.js +4 -0
- package/dist/cache.d.ts +26 -0
- package/dist/cache.js +95 -0
- package/dist/cli-utils.d.ts +37 -0
- package/dist/cli-utils.js +250 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +635 -0
- package/dist/config-loader.d.ts +78 -0
- package/dist/config-loader.js +508 -0
- package/dist/config-schema.d.ts +233 -0
- package/dist/config-schema.js +83 -0
- package/dist/create/board-checklist.d.ts +2 -0
- package/dist/create/board-checklist.js +52 -0
- package/dist/create/board-codegen.d.ts +9 -0
- package/dist/create/board-codegen.js +238 -0
- package/dist/create/board-generators.d.ts +12 -0
- package/dist/create/board-generators.js +652 -0
- package/dist/create/board-spec.d.ts +640 -0
- package/dist/create/board-spec.js +215 -0
- package/dist/create/eslint-rules-template.d.ts +2 -0
- package/dist/create/eslint-rules-template.js +672 -0
- package/dist/create/index.d.ts +11 -0
- package/dist/create/index.js +8 -0
- package/dist/create/init-scaffold.d.ts +27 -0
- package/dist/create/init-scaffold.js +150 -0
- package/dist/create/init-templates.d.ts +24 -0
- package/dist/create/init-templates.js +379 -0
- package/dist/create/init-wizard.d.ts +8 -0
- package/dist/create/init-wizard.js +168 -0
- package/dist/debug/breakpoint-loader.d.ts +24 -0
- package/dist/debug/breakpoint-loader.js +137 -0
- package/dist/debug/index.d.ts +3 -0
- package/dist/debug/index.js +7 -0
- package/dist/debug/preprocessor.d.ts +9 -0
- package/dist/debug/preprocessor.js +214 -0
- package/dist/debug/types.d.ts +32 -0
- package/dist/debug/types.js +6 -0
- package/dist/diagnostics/diagnostics-report.d.ts +51 -0
- package/dist/diagnostics/diagnostics-report.js +333 -0
- package/dist/diagnostics/json-schema.d.ts +138 -0
- package/dist/diagnostics/json-schema.js +27 -0
- package/dist/diagnostics/md-writer.d.ts +10 -0
- package/dist/diagnostics/md-writer.js +336 -0
- package/dist/diagnostics/mermaid-builder.d.ts +65 -0
- package/dist/diagnostics/mermaid-builder.js +681 -0
- package/dist/emit/cpp-emitter.d.ts +6 -0
- package/dist/emit/cpp-emitter.js +48 -0
- package/dist/emit/emitters/class-emitter.d.ts +2 -0
- package/dist/emit/emitters/class-emitter.js +523 -0
- package/dist/emit/emitters/emitter-context.d.ts +174 -0
- package/dist/emit/emitters/emitter-context.js +1 -0
- package/dist/emit/emitters/entrypoint-synthesizer.d.ts +2 -0
- package/dist/emit/emitters/entrypoint-synthesizer.js +175 -0
- package/dist/emit/emitters/function-emitter-impl.d.ts +19 -0
- package/dist/emit/emitters/function-emitter-impl.js +428 -0
- package/dist/emit/emitters/line-appender.d.ts +15 -0
- package/dist/emit/emitters/line-appender.js +344 -0
- package/dist/emit/emitters/namespace-emitter.d.ts +2 -0
- package/dist/emit/emitters/namespace-emitter.js +324 -0
- package/dist/emit/emitters/output-finalizer.d.ts +4 -0
- package/dist/emit/emitters/output-finalizer.js +267 -0
- package/dist/emit/emitters/setup.d.ts +3 -0
- package/dist/emit/emitters/setup.js +851 -0
- package/dist/emit/emitters/top-level-prep.d.ts +2 -0
- package/dist/emit/emitters/top-level-prep.js +706 -0
- package/dist/emit/emitters/type-decl-emitter.d.ts +2 -0
- package/dist/emit/emitters/type-decl-emitter.js +251 -0
- package/dist/emit/emitters/ui-emitter.d.ts +21 -0
- package/dist/emit/emitters/ui-emitter.js +564 -0
- package/dist/emit/enum-emitter.d.ts +1 -0
- package/dist/emit/enum-emitter.js +1 -0
- package/dist/emit/expression-renderer.d.ts +283 -0
- package/dist/emit/expression-renderer.js +1477 -0
- package/dist/emit/native-helpers-emitter.d.ts +11 -0
- package/dist/emit/native-helpers-emitter.js +34 -0
- package/dist/emit/route-hal-op.d.ts +5 -0
- package/dist/emit/route-hal-op.js +13 -0
- package/dist/emit/snprintf-helpers.d.ts +17 -0
- package/dist/emit/snprintf-helpers.js +462 -0
- package/dist/emit/statement-renderer.d.ts +189 -0
- package/dist/emit/statement-renderer.js +948 -0
- package/dist/emit/utils/async-state-machine.d.ts +42 -0
- package/dist/emit/utils/async-state-machine.js +343 -0
- package/dist/emit/utils/comment-helpers.d.ts +20 -0
- package/dist/emit/utils/comment-helpers.js +31 -0
- package/dist/emit/utils/cpp-helpers.d.ts +47 -0
- package/dist/emit/utils/cpp-helpers.js +85 -0
- package/dist/emit/utils/include-resolver.d.ts +58 -0
- package/dist/emit/utils/include-resolver.js +155 -0
- package/dist/emit/utils/index.d.ts +8 -0
- package/dist/emit/utils/index.js +12 -0
- package/dist/emit/utils/type-inference.d.ts +159 -0
- package/dist/emit/utils/type-inference.js +559 -0
- package/dist/eslint-check.d.ts +10 -0
- package/dist/eslint-check.js +102 -0
- package/dist/framework-package.d.ts +10 -0
- package/dist/framework-package.js +58 -0
- package/dist/framework-registry.d.ts +39 -0
- package/dist/framework-registry.js +30 -0
- package/dist/incremental-cache.d.ts +116 -0
- package/dist/incremental-cache.js +335 -0
- package/dist/ir/adc-range-validation.d.ts +11 -0
- package/dist/ir/adc-range-validation.js +250 -0
- package/dist/ir/ast-node-utils.d.ts +8 -0
- package/dist/ir/ast-node-utils.js +32 -0
- package/dist/ir/board-pin-utils.d.ts +13 -0
- package/dist/ir/board-pin-utils.js +65 -0
- package/dist/ir/board-resolver.d.ts +36 -0
- package/dist/ir/board-resolver.js +437 -0
- package/dist/ir/build-ir-state.d.ts +167 -0
- package/dist/ir/build-ir-state.js +306 -0
- package/dist/ir/build-ir.d.ts +2 -0
- package/dist/ir/build-ir.js +808 -0
- package/dist/ir/call-graph.d.ts +35 -0
- package/dist/ir/call-graph.js +194 -0
- package/dist/ir/compile-time-only.d.ts +2 -0
- package/dist/ir/compile-time-only.js +16 -0
- package/dist/ir/declaration-builders.d.ts +9 -0
- package/dist/ir/declaration-builders.js +608 -0
- package/dist/ir/entry-points.d.ts +30 -0
- package/dist/ir/entry-points.js +191 -0
- package/dist/ir/expression-to-ir.d.ts +5 -0
- package/dist/ir/expression-to-ir.js +2249 -0
- package/dist/ir/extract-property-chain.d.ts +10 -0
- package/dist/ir/extract-property-chain.js +30 -0
- package/dist/ir/feature-prescan.d.ts +3 -0
- package/dist/ir/feature-prescan.js +43 -0
- package/dist/ir/feature-registry.d.ts +30 -0
- package/dist/ir/feature-registry.js +816 -0
- package/dist/ir/filter.d.ts +7 -0
- package/dist/ir/filter.js +85 -0
- package/dist/ir/function-builder.d.ts +19 -0
- package/dist/ir/function-builder.js +670 -0
- package/dist/ir/hal/hal-emitter.d.ts +62 -0
- package/dist/ir/hal/hal-emitter.js +735 -0
- package/dist/ir/hal/hal-parser.d.ts +61 -0
- package/dist/ir/hal/hal-parser.js +473 -0
- package/dist/ir/hal/hal-plugins.d.ts +34 -0
- package/dist/ir/hal/hal-plugins.js +706 -0
- package/dist/ir/hal-resolver.d.ts +5 -0
- package/dist/ir/hal-resolver.js +5 -0
- package/dist/ir/heap-analysis.d.ts +8 -0
- package/dist/ir/heap-analysis.js +350 -0
- package/dist/ir/heap-array-validation.d.ts +24 -0
- package/dist/ir/heap-array-validation.js +29 -0
- package/dist/ir/identifier-collector.d.ts +18 -0
- package/dist/ir/identifier-collector.js +439 -0
- package/dist/ir/interrupt-analysis.d.ts +39 -0
- package/dist/ir/interrupt-analysis.js +467 -0
- package/dist/ir/memory-budget-validation.d.ts +10 -0
- package/dist/ir/memory-budget-validation.js +86 -0
- package/dist/ir/namespace-builder.d.ts +7 -0
- package/dist/ir/namespace-builder.js +152 -0
- package/dist/ir/ownership-analysis.d.ts +9 -0
- package/dist/ir/ownership-analysis.js +1207 -0
- package/dist/ir/peripheral-ownership.d.ts +3 -0
- package/dist/ir/peripheral-ownership.js +152 -0
- package/dist/ir/peripheral-symbols.d.ts +7 -0
- package/dist/ir/peripheral-symbols.js +57 -0
- package/dist/ir/peripheral-usage.d.ts +54 -0
- package/dist/ir/peripheral-usage.js +691 -0
- package/dist/ir/peripheral-validation.d.ts +8 -0
- package/dist/ir/peripheral-validation.js +77 -0
- package/dist/ir/pin-alias-conflict.d.ts +4 -0
- package/dist/ir/pin-alias-conflict.js +34 -0
- package/dist/ir/pin-capability-validation.d.ts +6 -0
- package/dist/ir/pin-capability-validation.js +413 -0
- package/dist/ir/pin-mode-validation.d.ts +10 -0
- package/dist/ir/pin-mode-validation.js +301 -0
- package/dist/ir/pin-safety.d.ts +11 -0
- package/dist/ir/pin-safety.js +58 -0
- package/dist/ir/program-analysis.d.ts +44 -0
- package/dist/ir/program-analysis.js +589 -0
- package/dist/ir/pulldown-validation.d.ts +7 -0
- package/dist/ir/pulldown-validation.js +49 -0
- package/dist/ir/pwm-timer-sharing.d.ts +4 -0
- package/dist/ir/pwm-timer-sharing.js +93 -0
- package/dist/ir/reachability.d.ts +68 -0
- package/dist/ir/reachability.js +224 -0
- package/dist/ir/register-decorators.d.ts +6 -0
- package/dist/ir/register-decorators.js +49 -0
- package/dist/ir/render-expr.d.ts +4 -0
- package/dist/ir/render-expr.js +127 -0
- package/dist/ir/resource-analysis.d.ts +19 -0
- package/dist/ir/resource-analysis.js +153 -0
- package/dist/ir/statement-to-ir.d.ts +23 -0
- package/dist/ir/statement-to-ir.js +349 -0
- package/dist/ir/symbol-table.d.ts +68 -0
- package/dist/ir/symbol-table.js +179 -0
- package/dist/ir/symbol-types.d.ts +56 -0
- package/dist/ir/symbol-types.js +107 -0
- package/dist/ir/timer0-pwm-timing-conflict.d.ts +4 -0
- package/dist/ir/timer0-pwm-timing-conflict.js +71 -0
- package/dist/ir/timing-validation.d.ts +7 -0
- package/dist/ir/timing-validation.js +95 -0
- package/dist/ir/transformers/array-methods.d.ts +8 -0
- package/dist/ir/transformers/array-methods.js +493 -0
- package/dist/ir/transformers/call-statement.d.ts +5 -0
- package/dist/ir/transformers/call-statement.js +758 -0
- package/dist/ir/transformers/callback-context-registry.d.ts +20 -0
- package/dist/ir/transformers/callback-context-registry.js +55 -0
- package/dist/ir/transformers/canvas-lowering.d.ts +42 -0
- package/dist/ir/transformers/canvas-lowering.js +249 -0
- package/dist/ir/transformers/control-flow.d.ts +8 -0
- package/dist/ir/transformers/control-flow.js +508 -0
- package/dist/ir/transformers/expressions.d.ts +7 -0
- package/dist/ir/transformers/expressions.js +406 -0
- package/dist/ir/transformers/hal-call-resolver.d.ts +29 -0
- package/dist/ir/transformers/hal-call-resolver.js +274 -0
- package/dist/ir/transformers/hal-emit-helpers.d.ts +14 -0
- package/dist/ir/transformers/hal-emit-helpers.js +63 -0
- package/dist/ir/transformers/namespace-methods.d.ts +20 -0
- package/dist/ir/transformers/namespace-methods.js +110 -0
- package/dist/ir/transformers/register-assignment.d.ts +5 -0
- package/dist/ir/transformers/register-assignment.js +56 -0
- package/dist/ir/transformers/ui-call-resolver.d.ts +149 -0
- package/dist/ir/transformers/ui-call-resolver.js +1136 -0
- package/dist/ir/transformers/ui-callback-lowering.d.ts +62 -0
- package/dist/ir/transformers/ui-callback-lowering.js +623 -0
- package/dist/ir/transformers/ui-mount.d.ts +20 -0
- package/dist/ir/transformers/ui-mount.js +35 -0
- package/dist/ir/transformers/ui-reactive.d.ts +68 -0
- package/dist/ir/transformers/ui-reactive.js +129 -0
- package/dist/ir/transformers/variables.d.ts +14 -0
- package/dist/ir/transformers/variables.js +1045 -0
- package/dist/ir/try-catch-validation.d.ts +26 -0
- package/dist/ir/try-catch-validation.js +128 -0
- package/dist/ir/type-resolution.d.ts +31 -0
- package/dist/ir/type-resolution.js +977 -0
- package/dist/ir/ui-element-auto-wire.d.ts +50 -0
- package/dist/ir/ui-element-auto-wire.js +218 -0
- package/dist/ir/unit-suspicion-validation.d.ts +12 -0
- package/dist/ir/unit-suspicion-validation.js +296 -0
- package/dist/ir/utils/walk-ir.d.ts +6 -0
- package/dist/ir/utils/walk-ir.js +162 -0
- package/dist/ir/validation-orchestrator.d.ts +4 -0
- package/dist/ir/validation-orchestrator.js +45 -0
- package/dist/libdef/base-class-resolver.d.ts +23 -0
- package/dist/libdef/base-class-resolver.js +50 -0
- package/dist/libdef/cpp-to-decl.d.ts +34 -0
- package/dist/libdef/cpp-to-decl.js +584 -0
- package/dist/libdef/header-parser.d.ts +53 -0
- package/dist/libdef/header-parser.js +269 -0
- package/dist/libdef/registry.d.ts +10 -0
- package/dist/libdef/registry.js +142 -0
- package/dist/mapping/peripheral-names.d.ts +23 -0
- package/dist/mapping/peripheral-names.js +63 -0
- package/dist/mapping/source-map.d.ts +46 -0
- package/dist/mapping/source-map.js +207 -0
- package/dist/orchestrator/dts-generator.d.ts +17 -0
- package/dist/orchestrator/dts-generator.js +83 -0
- package/dist/orchestrator/graph-builder.d.ts +18 -0
- package/dist/orchestrator/graph-builder.js +218 -0
- package/dist/orchestrator/semantic-facts-verifier.d.ts +38 -0
- package/dist/orchestrator/semantic-facts-verifier.js +121 -0
- package/dist/orchestrator/semantic-facts.d.ts +141 -0
- package/dist/orchestrator/semantic-facts.js +652 -0
- package/dist/orchestrator/type-checker.d.ts +39 -0
- package/dist/orchestrator/type-checker.js +760 -0
- package/dist/platform/async-runtime.d.ts +8 -0
- package/dist/platform/async-runtime.js +26 -0
- package/dist/platform/generic-strategy.d.ts +101 -0
- package/dist/platform/generic-strategy.js +289 -0
- package/dist/platform/index.d.ts +4 -0
- package/dist/platform/index.js +9 -0
- package/dist/platform/registry.d.ts +21 -0
- package/dist/platform/registry.js +42 -0
- package/dist/platform/toolchain.d.ts +4 -0
- package/dist/platform/toolchain.js +43 -0
- package/dist/preview/client.d.ts +1 -0
- package/dist/preview/client.js +137 -0
- package/dist/preview/server.d.ts +5 -0
- package/dist/preview/server.js +199 -0
- package/dist/profiler/index.d.ts +28 -0
- package/dist/profiler/index.js +28 -0
- package/dist/profiler/profiler.d.ts +95 -0
- package/dist/profiler/profiler.js +338 -0
- package/dist/profiler/types.d.ts +87 -0
- package/dist/profiler/types.js +12 -0
- package/dist/stores/display-profile-store.d.ts +16 -0
- package/dist/stores/display-profile-store.js +48 -0
- package/dist/stores/theme-store.d.ts +13 -0
- package/dist/stores/theme-store.js +30 -0
- package/dist/testing.d.ts +45 -0
- package/dist/testing.js +47 -0
- package/dist/transpile/resolution.d.ts +29 -0
- package/dist/transpile/resolution.js +282 -0
- package/dist/transpile.d.ts +3 -0
- package/dist/transpile.js +722 -0
- package/dist/types.d.ts +217 -0
- package/dist/types.js +1 -0
- package/dist/ui/ui-bridge.d.ts +4 -0
- package/dist/ui/ui-bridge.js +34 -0
- package/dist/ui-hook.d.ts +84 -0
- package/dist/ui-hook.js +45 -0
- package/dist/utils/cli.d.ts +3 -0
- package/dist/utils/cli.js +463 -0
- package/dist/utils/fs.d.ts +4 -0
- package/dist/utils/fs.js +23 -0
- package/dist/utils/logger.d.ts +2 -0
- package/dist/utils/logger.js +8 -0
- package/dist/utils/strings.d.ts +35 -0
- package/dist/utils/strings.js +142 -0
- package/dist/utils/ui.d.ts +66 -0
- package/dist/utils/ui.js +149 -0
- package/dist/watch.d.ts +35 -0
- package/dist/watch.js +111 -0
- package/package.json +127 -0
|
@@ -0,0 +1,652 @@
|
|
|
1
|
+
import ts from "typescript";
|
|
2
|
+
// ---------------------------------------------------------------------------
|
|
3
|
+
// FactStore implementation
|
|
4
|
+
// ---------------------------------------------------------------------------
|
|
5
|
+
function createFactStore() {
|
|
6
|
+
const map = new WeakMap();
|
|
7
|
+
return {
|
|
8
|
+
get(node) {
|
|
9
|
+
return map.get(node);
|
|
10
|
+
},
|
|
11
|
+
has(node) {
|
|
12
|
+
return map.has(node);
|
|
13
|
+
},
|
|
14
|
+
set(node, facts) {
|
|
15
|
+
map.set(node, facts);
|
|
16
|
+
},
|
|
17
|
+
merge(node, partial) {
|
|
18
|
+
const existing = map.get(node);
|
|
19
|
+
if (existing) {
|
|
20
|
+
map.set(node, { ...existing, ...partial });
|
|
21
|
+
}
|
|
22
|
+
else {
|
|
23
|
+
map.set(node, { type: "unknown", ...partial });
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
// ---------------------------------------------------------------------------
|
|
29
|
+
// CanonicalType resolver
|
|
30
|
+
//
|
|
31
|
+
// One function replaces six boolean predicates. Each predicate in
|
|
32
|
+
// type-checker.ts becomes a one-line comparison against canonicalize().
|
|
33
|
+
// ---------------------------------------------------------------------------
|
|
34
|
+
/** Names recognized as typed arrays (Int8Array .. Float64Array, etc.). */
|
|
35
|
+
export const TYPED_ARRAY_NAMES = new Set([
|
|
36
|
+
"Uint8Array",
|
|
37
|
+
"Int8Array",
|
|
38
|
+
"Uint16Array",
|
|
39
|
+
"Int16Array",
|
|
40
|
+
"Uint32Array",
|
|
41
|
+
"Int32Array",
|
|
42
|
+
"Float32Array",
|
|
43
|
+
"Float64Array",
|
|
44
|
+
"BigUint64Array",
|
|
45
|
+
"BigInt64Array",
|
|
46
|
+
]);
|
|
47
|
+
/** Names recognized as map-like containers. */
|
|
48
|
+
const MAP_NAMES = new Set(["Map", "ReadonlyMap", "Record"]);
|
|
49
|
+
/** Names recognized as set-like containers. */
|
|
50
|
+
const SET_NAMES = new Set(["Set", "ReadonlySet"]);
|
|
51
|
+
function typeToString(checker, type) {
|
|
52
|
+
return checker.typeToString(type);
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Resolve whether a type's symbol/alias name (or stringified form) contains
|
|
56
|
+
* any of the given names. Walks union constituents. Mirrors the existing
|
|
57
|
+
* typeHasName() logic so behavior is preserved exactly during migration.
|
|
58
|
+
*/
|
|
59
|
+
function typeHasName(checker, type, names) {
|
|
60
|
+
if (type.isUnion()) {
|
|
61
|
+
return type.types.some((t) => typeHasName(checker, t, names));
|
|
62
|
+
}
|
|
63
|
+
const symbolName = type.getSymbol()?.getName();
|
|
64
|
+
const aliasName = type.aliasSymbol?.getName();
|
|
65
|
+
if ((symbolName && names.has(symbolName)) ||
|
|
66
|
+
(aliasName && names.has(aliasName))) {
|
|
67
|
+
return true;
|
|
68
|
+
}
|
|
69
|
+
const text = typeToString(checker, type);
|
|
70
|
+
for (const name of names) {
|
|
71
|
+
if (new RegExp(`\\b${name}\\b`).test(text))
|
|
72
|
+
return true;
|
|
73
|
+
}
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* True iff `type` is an array type (Array<T>, T[], tuple, ReadonlyArray).
|
|
78
|
+
* Preserves the exact behavior of the existing isArrayLikeType() predicate.
|
|
79
|
+
*/
|
|
80
|
+
function isArrayLike(checker, type) {
|
|
81
|
+
if (type.isUnion())
|
|
82
|
+
return type.types.some((t) => isArrayLike(checker, t));
|
|
83
|
+
const checkerWithArray = checker;
|
|
84
|
+
if (checkerWithArray.isArrayType?.(type))
|
|
85
|
+
return true;
|
|
86
|
+
if (checker.isTupleType(type))
|
|
87
|
+
return true;
|
|
88
|
+
const text = typeToString(checker, type);
|
|
89
|
+
return /\bReadonlyArray\b|\bArray\b/.test(text) || /\[\]$/.test(text);
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Classify a resolved TS type into a canonical category. This is the single
|
|
93
|
+
* source of truth for "what kind of C++ concept does this type lower to?"
|
|
94
|
+
*
|
|
95
|
+
* Categories and their verifier meaning:
|
|
96
|
+
* - concrete categories (primitive, struct, array, ...) — fine.
|
|
97
|
+
* - "any" — the transpiler handles `any` via inference; the verifier treats
|
|
98
|
+
* this as a NON-hazard (it is not `unknown`).
|
|
99
|
+
* - "unknown" — genuinely unclassifiable; the verifier flags this.
|
|
100
|
+
*
|
|
101
|
+
* Unions with a null/undefined constituent (e.g. `Account | null`) are
|
|
102
|
+
* classified by their non-null constituent, because the transpiler erases
|
|
103
|
+
* optionality to the value type. `this` types resolve to the enclosing
|
|
104
|
+
* class/interface, so they classify as struct.
|
|
105
|
+
*/
|
|
106
|
+
export function canonicalize(checker, type) {
|
|
107
|
+
const flags = type.flags;
|
|
108
|
+
// Nullable unions (T | null, T | undefined, T | null | undefined): classify
|
|
109
|
+
// by the non-null constituent(s). The transpiler erases optionality. The
|
|
110
|
+
// Null/Undefined flag bits live on the constituents, not the union itself,
|
|
111
|
+
// so we scan the constituents.
|
|
112
|
+
if (type.isUnion()) {
|
|
113
|
+
const hasNullish = type.types.some((t) => (t.flags & (ts.TypeFlags.Null | ts.TypeFlags.Undefined)) !== 0);
|
|
114
|
+
if (hasNullish) {
|
|
115
|
+
const nonNull = type.types.filter((t) => (t.flags & (ts.TypeFlags.Null | ts.TypeFlags.Undefined)) === 0);
|
|
116
|
+
if (nonNull.length === 1) {
|
|
117
|
+
return canonicalize(checker, nonNull[0]);
|
|
118
|
+
}
|
|
119
|
+
if (nonNull.length > 1) {
|
|
120
|
+
// Multiple non-null constituents (a real discriminated union) — leave
|
|
121
|
+
// as unknown; the transpiler lowers these to std::variant with caveats.
|
|
122
|
+
return "unknown";
|
|
123
|
+
}
|
|
124
|
+
// Only null/undefined — treat as primitive so the verifier doesn't fire.
|
|
125
|
+
return "primitive";
|
|
126
|
+
}
|
|
127
|
+
// Non-nullish union. Two common, lowering-safe shapes appear here as a
|
|
128
|
+
// result of TypeScript control-flow narrowing and both lower to a single
|
|
129
|
+
// primitive C++ type, so they should NOT trip the UNCLASSIFIABLE warning:
|
|
130
|
+
//
|
|
131
|
+
// (1) A narrowed enum — `t.kind` after `if (t.kind === K.A) { continue; }`
|
|
132
|
+
// narrows to `K.B | K.C`. Each constituent is an EnumLiteral of the
|
|
133
|
+
// SAME enum, and the transpiler already lowers any enum member to a
|
|
134
|
+
// single integral type (compared via static_cast<int>).
|
|
135
|
+
// (2) A string-literal union — `op` narrowed to `"+" | "-" | "*" | "/"`,
|
|
136
|
+
// which lowers to a single std::string compared with ==.
|
|
137
|
+
//
|
|
138
|
+
// Coalesce when every non-null constituent canonicalizes to the SAME
|
|
139
|
+
// category — then the union lowers to that one category. Heterogeneous
|
|
140
|
+
// unions (e.g. number | string, or a real object discriminated union)
|
|
141
|
+
// fall through to the per-constituent check below and remain "unknown"
|
|
142
|
+
// (the std::variant case). Demo #22 Finding C.
|
|
143
|
+
if (type.types.length > 1) {
|
|
144
|
+
const categories = new Set();
|
|
145
|
+
for (const t of type.types) {
|
|
146
|
+
categories.add(canonicalize(checker, t));
|
|
147
|
+
}
|
|
148
|
+
if (categories.size === 1) {
|
|
149
|
+
const only = categories.values().next().value;
|
|
150
|
+
if (only === "primitive") {
|
|
151
|
+
return "primitive";
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
// `any` — distinct from `unknown`. The transpiler legitimately handles any.
|
|
157
|
+
if (flags & ts.TypeFlags.Any) {
|
|
158
|
+
return "any";
|
|
159
|
+
}
|
|
160
|
+
// `unknown`/`never` — genuinely unclassifiable.
|
|
161
|
+
if (flags & (ts.TypeFlags.Unknown | ts.TypeFlags.Never)) {
|
|
162
|
+
return "unknown";
|
|
163
|
+
}
|
|
164
|
+
// Intersection types (A & B) — common in generated d.ts files (e.g. mixin
|
|
165
|
+
// patterns like `{ kind: "button" } & PressBinding`). They lower to a single
|
|
166
|
+
// struct (the merged field set), so classify as struct. Check each
|
|
167
|
+
// constituent; if any is a container/callable we defer to that, but the
|
|
168
|
+
// overwhelmingly common case is all-object intersections → struct.
|
|
169
|
+
if (flags & ts.TypeFlags.Intersection) {
|
|
170
|
+
const parts = type.types ?? [];
|
|
171
|
+
const partCats = parts.map((t) => canonicalize(checker, t));
|
|
172
|
+
// If every constituent is struct/primitive, the intersection is a struct.
|
|
173
|
+
if (partCats.every((c) => c === "struct" || c === "primitive")) {
|
|
174
|
+
return "struct";
|
|
175
|
+
}
|
|
176
|
+
// If all constituents agree on one non-struct category, inherit it.
|
|
177
|
+
const unique = new Set(partCats);
|
|
178
|
+
if (unique.size === 1)
|
|
179
|
+
return partCats[0];
|
|
180
|
+
return "struct";
|
|
181
|
+
}
|
|
182
|
+
// Primitives.
|
|
183
|
+
if (flags &
|
|
184
|
+
(ts.TypeFlags.NumberLike |
|
|
185
|
+
ts.TypeFlags.StringLike |
|
|
186
|
+
ts.TypeFlags.BooleanLike |
|
|
187
|
+
ts.TypeFlags.BigIntLike |
|
|
188
|
+
ts.TypeFlags.Enum |
|
|
189
|
+
ts.TypeFlags.EnumLiteral |
|
|
190
|
+
ts.TypeFlags.Null |
|
|
191
|
+
ts.TypeFlags.Undefined |
|
|
192
|
+
ts.TypeFlags.Void |
|
|
193
|
+
ts.TypeFlags.Literal)) {
|
|
194
|
+
return "primitive";
|
|
195
|
+
}
|
|
196
|
+
// `this` types and type parameters (<T>) both carry the TypeParameter flag
|
|
197
|
+
// (a `this` type is represented as a constrained type parameter whose
|
|
198
|
+
// constraint is the enclosing class). Resolve to the constraint if bounded;
|
|
199
|
+
// otherwise default to struct, which is the common case for class generics
|
|
200
|
+
// and `this`.
|
|
201
|
+
if (flags & ts.TypeFlags.TypeParameter) {
|
|
202
|
+
const constraint = type.getConstraint?.();
|
|
203
|
+
if (constraint) {
|
|
204
|
+
return canonicalize(checker, constraint);
|
|
205
|
+
}
|
|
206
|
+
return "struct";
|
|
207
|
+
}
|
|
208
|
+
// Containers, checked in specificity order.
|
|
209
|
+
if (typeHasName(checker, type, MAP_NAMES) || /\bstd::map\b/.test(typeToString(checker, type))) {
|
|
210
|
+
return "map";
|
|
211
|
+
}
|
|
212
|
+
if (typeHasName(checker, type, SET_NAMES)) {
|
|
213
|
+
return "set";
|
|
214
|
+
}
|
|
215
|
+
if (typeHasName(checker, type, TYPED_ARRAY_NAMES)) {
|
|
216
|
+
return "typed-array";
|
|
217
|
+
}
|
|
218
|
+
if (isArrayLike(checker, type)) {
|
|
219
|
+
return "array";
|
|
220
|
+
}
|
|
221
|
+
// Functions.
|
|
222
|
+
const symbol = type.getSymbol();
|
|
223
|
+
if (symbol && (symbol.flags & ts.SymbolFlags.Function) !== 0) {
|
|
224
|
+
return "function";
|
|
225
|
+
}
|
|
226
|
+
if (flags & ts.TypeFlags.Object) {
|
|
227
|
+
const sig = type.getCallSignatures();
|
|
228
|
+
if (sig.length > 0 && type.getProperties().length === 0) {
|
|
229
|
+
// Pure callable object type with no other properties.
|
|
230
|
+
return "function";
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
// Everything object-like that isn't a known container or callable is a
|
|
234
|
+
// struct (classes, interfaces, object literals, enums-by-shape).
|
|
235
|
+
if (flags & ts.TypeFlags.Object) {
|
|
236
|
+
return "struct";
|
|
237
|
+
}
|
|
238
|
+
return "unknown";
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* Map a canonicalized type to the concrete C++ type the lowering emits, for
|
|
242
|
+
* the categories where the cppType is deterministic from the TS type alone.
|
|
243
|
+
* Returns undefined when the cppType depends on IR-build context the
|
|
244
|
+
* type-checker does not have (user-defined class/interface names resolve to
|
|
245
|
+
* themselves as struct names but their field layout is a SymbolTable concern;
|
|
246
|
+
* `auto` deduction from complex initializers is an IR-build concern).
|
|
247
|
+
*
|
|
248
|
+
* This populates SemanticFacts.cppType so gate rules can read a concrete type
|
|
249
|
+
* without re-deriving it. Phase 3 of the type-resolution consolidation — see
|
|
250
|
+
* the doc comment on SemanticFacts.cppType for the one-way direction.
|
|
251
|
+
*/
|
|
252
|
+
export function cppTypeFromCanonicalType(checker, type, category) {
|
|
253
|
+
switch (category) {
|
|
254
|
+
case "primitive": {
|
|
255
|
+
// number → double (the lowering's default number type), string →
|
|
256
|
+
// std::string, boolean → bool. BigInt and enums share the primitive
|
|
257
|
+
// category but have no single deterministic cppType here (enums lower to
|
|
258
|
+
// their underlying integral width, which depends on member values known
|
|
259
|
+
// only at IR-build time), so they stay undefined.
|
|
260
|
+
const flags = type.flags;
|
|
261
|
+
if (flags & ts.TypeFlags.StringLike)
|
|
262
|
+
return "std::string";
|
|
263
|
+
if (flags & ts.TypeFlags.NumberLike)
|
|
264
|
+
return "double";
|
|
265
|
+
if (flags & ts.TypeFlags.BooleanLike)
|
|
266
|
+
return "bool";
|
|
267
|
+
return undefined;
|
|
268
|
+
}
|
|
269
|
+
case "typed-array": {
|
|
270
|
+
// Map the typed-array name to its element pointer type. The lowering
|
|
271
|
+
// emits these exact pointer types (TYPED_ARRAY_ELEMENT_MAP in
|
|
272
|
+
// build-ir-state); mirroring them here gives gates the concrete storage
|
|
273
|
+
// type without touching the SymbolTable.
|
|
274
|
+
const name = type.getSymbol()?.getName()
|
|
275
|
+
?? type.aliasSymbol?.getName()
|
|
276
|
+
?? "";
|
|
277
|
+
switch (name) {
|
|
278
|
+
case "Uint8Array": return "uint8_t*";
|
|
279
|
+
case "Int8Array": return "int8_t*";
|
|
280
|
+
case "Uint16Array": return "uint16_t*";
|
|
281
|
+
case "Int16Array": return "int16_t*";
|
|
282
|
+
case "Uint32Array": return "uint32_t*";
|
|
283
|
+
case "Int32Array": return "int32_t*";
|
|
284
|
+
case "Float32Array": return "float*";
|
|
285
|
+
case "Float64Array": return "double*";
|
|
286
|
+
default: return undefined;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
case "array":
|
|
290
|
+
// std::vector<element>, but the element type depends on the TS element
|
|
291
|
+
// type's own cppType (recursively). Defer to the SymbolTable for the
|
|
292
|
+
// concrete element rather than recursing here; gates that need the
|
|
293
|
+
// container-ness already have the "array" category.
|
|
294
|
+
return undefined;
|
|
295
|
+
case "map":
|
|
296
|
+
case "set":
|
|
297
|
+
// std::map<K,V> / std::set<T>, but the key/value element types are a
|
|
298
|
+
// SymbolTable concern. Same rationale as "array".
|
|
299
|
+
return undefined;
|
|
300
|
+
case "struct": {
|
|
301
|
+
// A user-defined class/interface lowers to its own name (classes as
|
|
302
|
+
// pointers, interfaces as values). The bare name is the cppType prefix;
|
|
303
|
+
// pointer-ness is resolved by the IR build. Return the bare name so a
|
|
304
|
+
// gate can at least see "this is a Foo". Stringification matches the
|
|
305
|
+
// lowering's class/interface naming.
|
|
306
|
+
const name = type.getSymbol()?.getName() ?? type.aliasSymbol?.getName();
|
|
307
|
+
return name;
|
|
308
|
+
}
|
|
309
|
+
case "function":
|
|
310
|
+
case "any":
|
|
311
|
+
case "unknown":
|
|
312
|
+
return undefined;
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
// ---------------------------------------------------------------------------
|
|
316
|
+
// buildSemanticFacts — runs the ordered analysis passes.
|
|
317
|
+
//
|
|
318
|
+
// Passes (each produces a slice of the facts; none overwrites another's field):
|
|
319
|
+
// 1. Binding origin pass — tags binding-identifier declaration nodes with
|
|
320
|
+
// their origin (map-value-lookup, array-param, typed-array-param, ...).
|
|
321
|
+
// Covers every binding form, not just identifier VariableDeclarations:
|
|
322
|
+
// destructuring, for-of/in, and catch bindings are included. Reassignment
|
|
323
|
+
// updates the origin to reflect the new initializer.
|
|
324
|
+
// 2. Container-lookup pass — tags container-lookup expression nodes
|
|
325
|
+
// (map.get(k)!, map.at(k), map[k]) with origin "map-value-lookup".
|
|
326
|
+
// The binding-origin pass uses this when classifying initializers.
|
|
327
|
+
//
|
|
328
|
+
// `userFiles` follows the same normalization as runSemanticGates: absolute
|
|
329
|
+
// paths, forward slashes, node_modules/packages excluded.
|
|
330
|
+
// ---------------------------------------------------------------------------
|
|
331
|
+
/**
|
|
332
|
+
* Strip wrapper expressions (parens, non-null, as, type-assertion, satisfies)
|
|
333
|
+
* to reach the underlying expression. Mirrors unwrapExpression() in
|
|
334
|
+
* type-checker.ts so the two layers classify the same nodes.
|
|
335
|
+
*/
|
|
336
|
+
function unwrapExpression(expr) {
|
|
337
|
+
let current = expr;
|
|
338
|
+
while (true) {
|
|
339
|
+
if (ts.isParenthesizedExpression(current) ||
|
|
340
|
+
ts.isNonNullExpression(current) ||
|
|
341
|
+
ts.isAsExpression(current) ||
|
|
342
|
+
ts.isTypeAssertionExpression(current)) {
|
|
343
|
+
current = current.expression;
|
|
344
|
+
continue;
|
|
345
|
+
}
|
|
346
|
+
if (ts.isSatisfiesExpression?.(current)) {
|
|
347
|
+
current = current.expression;
|
|
348
|
+
continue;
|
|
349
|
+
}
|
|
350
|
+
return current;
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
/** Is `node` a container value lookup (map.get/at, or map[k] on a Map/Record)? */
|
|
354
|
+
function isMapValueLookupExpression(node, checker) {
|
|
355
|
+
const expr = unwrapExpression(node);
|
|
356
|
+
if (ts.isCallExpression(expr)) {
|
|
357
|
+
const callee = unwrapExpression(expr.expression);
|
|
358
|
+
if (ts.isPropertyAccessExpression(callee) &&
|
|
359
|
+
(callee.name.text === "get" || callee.name.text === "at")) {
|
|
360
|
+
return canonicalize(checker, checker.getTypeAtLocation(callee.expression)) === "map";
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
if (ts.isElementAccessExpression(expr)) {
|
|
364
|
+
return canonicalize(checker, checker.getTypeAtLocation(expr.expression)) === "map";
|
|
365
|
+
}
|
|
366
|
+
return false;
|
|
367
|
+
}
|
|
368
|
+
/** True iff the resolved type is an object-like value type (the map-value-copy case). */
|
|
369
|
+
function isObjectLikeValueType(type) {
|
|
370
|
+
if (type.isUnion()) {
|
|
371
|
+
return type.types.some((t) => !typeIncludesNullish(t) && isObjectLikeValueType(t));
|
|
372
|
+
}
|
|
373
|
+
// Primitive flag set => not object-like. Mirrors isObjectLikeValueType() in
|
|
374
|
+
// type-checker.ts (NumberLike | StringLike | BooleanLike | BigIntLike |
|
|
375
|
+
// EnumLike | Null | Undefined | Void | Never | Any | Unknown).
|
|
376
|
+
const flags = type.flags;
|
|
377
|
+
const primitiveFlags = ts.TypeFlags.NumberLike |
|
|
378
|
+
ts.TypeFlags.StringLike |
|
|
379
|
+
ts.TypeFlags.BooleanLike |
|
|
380
|
+
ts.TypeFlags.BigIntLike |
|
|
381
|
+
ts.TypeFlags.EnumLike |
|
|
382
|
+
ts.TypeFlags.Null |
|
|
383
|
+
ts.TypeFlags.Undefined |
|
|
384
|
+
ts.TypeFlags.Void |
|
|
385
|
+
ts.TypeFlags.Never |
|
|
386
|
+
ts.TypeFlags.Any |
|
|
387
|
+
ts.TypeFlags.Unknown;
|
|
388
|
+
return (flags & primitiveFlags) === 0;
|
|
389
|
+
}
|
|
390
|
+
/**
|
|
391
|
+
* True iff `type` is the instance type of a `class` declaration (or a union
|
|
392
|
+
* whose non-nullish constituent is). A TypeScript class is a *reference* type,
|
|
393
|
+
* and the transpiler lowers it to a C++ pointer (`C*`, see
|
|
394
|
+
* `ir/type-resolution.ts` `classTypeNames`). That distinction matters for the
|
|
395
|
+
* `TS2CPP_MAP_VALUE_COPY_MUTATION` gate: a `map.get(k)` whose value type is a
|
|
396
|
+
* class returns a *pointer*, so a field write `node.value = v` lowers to
|
|
397
|
+
* `node->value = v` and **persists** through the pointer — it is NOT a value
|
|
398
|
+
* copy the way an `interface`/struct-typed fetch is. Only struct-valued
|
|
399
|
+
* (interface / object-literal) map entries are value copies; class entries are
|
|
400
|
+
* exempt. (Demo #25 Finding A.)
|
|
401
|
+
*/
|
|
402
|
+
function isClassInstanceType(type) {
|
|
403
|
+
if (type.isUnion()) {
|
|
404
|
+
return type.types.some((t) => !typeIncludesNullish(t) && isClassInstanceType(t));
|
|
405
|
+
}
|
|
406
|
+
// A class instance type's symbol carries SymbolFlags.Class. Resolve through
|
|
407
|
+
// aliases (e.g. `type Alias = SomeClass`) so an aliased class is still seen
|
|
408
|
+
// as a class.
|
|
409
|
+
let symbol = type.getSymbol();
|
|
410
|
+
if (symbol && symbol.flags & ts.SymbolFlags.TypeAlias) {
|
|
411
|
+
// For an alias, the aliased type is reachable via the checker; fall back to
|
|
412
|
+
// a flag check on the alias target by walking type.aliasSymbol.
|
|
413
|
+
symbol = type.aliasSymbol ?? symbol;
|
|
414
|
+
}
|
|
415
|
+
if (!symbol)
|
|
416
|
+
return false;
|
|
417
|
+
return (symbol.flags & ts.SymbolFlags.Class) !== 0;
|
|
418
|
+
}
|
|
419
|
+
function typeIncludesNullish(type) {
|
|
420
|
+
if (type.isUnion())
|
|
421
|
+
return type.types.some(typeIncludesNullish);
|
|
422
|
+
return (type.flags & (ts.TypeFlags.Null | ts.TypeFlags.Undefined | ts.TypeFlags.Void)) !== 0;
|
|
423
|
+
}
|
|
424
|
+
function isUserFile(filePath, userFileSet) {
|
|
425
|
+
const p = filePath.replace(/\\/g, "/");
|
|
426
|
+
if (!userFileSet.has(p))
|
|
427
|
+
return false;
|
|
428
|
+
if (p.includes("/node_modules/") || p.includes("/packages/"))
|
|
429
|
+
return false;
|
|
430
|
+
return true;
|
|
431
|
+
}
|
|
432
|
+
/**
|
|
433
|
+
* Build the SemanticFacts store for a program by running the analysis passes.
|
|
434
|
+
*/
|
|
435
|
+
export function buildSemanticFacts(program, userFiles) {
|
|
436
|
+
const facts = createFactStore();
|
|
437
|
+
const diagnostics = [];
|
|
438
|
+
const checker = program.getTypeChecker();
|
|
439
|
+
const userFileSet = new Set(userFiles.map((f) => f.replace(/\\/g, "/")));
|
|
440
|
+
// Map from binding-declaration identifier node -> origin. Populated by the
|
|
441
|
+
// binding-origin pass, read by BindingResolver.resolveOrigin. Uses a Map
|
|
442
|
+
// (not the WeakMap inside FactStore) so resolveOrigin can look up origins
|
|
443
|
+
// directly by declaration node without going through the read-only store.
|
|
444
|
+
const originByDeclaration = new Map();
|
|
445
|
+
// Walk user files and tag binding declarations with their origin.
|
|
446
|
+
for (const sourceFile of program.getSourceFiles()) {
|
|
447
|
+
if (!isUserFile(sourceFile.fileName, userFileSet))
|
|
448
|
+
continue;
|
|
449
|
+
visitForBindings(sourceFile);
|
|
450
|
+
}
|
|
451
|
+
function recordOrigin(declaration, origin) {
|
|
452
|
+
originByDeclaration.set(declaration, origin);
|
|
453
|
+
// Also set a fact on the declaration node itself, for any consumer that
|
|
454
|
+
// walks declarations directly.
|
|
455
|
+
facts.merge(declaration, { origin });
|
|
456
|
+
}
|
|
457
|
+
/**
|
|
458
|
+
* Record an origin on the declaration node that a use-site identifier's
|
|
459
|
+
* symbol will resolve to. The checker reports a symbol's valueDeclaration
|
|
460
|
+
* as the declarator node (VariableDeclaration for `const x = …`,
|
|
461
|
+
* BindingElement for `const { x } = …`, Parameter for function params), not
|
|
462
|
+
* the identifier inside it — so we record on the declarator, not the name.
|
|
463
|
+
*/
|
|
464
|
+
function recordBindingName(bindingName, origin) {
|
|
465
|
+
if (ts.isIdentifier(bindingName)) {
|
|
466
|
+
// For a simple `const x = …`, the declarator is the parent
|
|
467
|
+
// VariableDeclaration / Parameter / etc. (never the identifier itself).
|
|
468
|
+
const declarator = bindingName.parent;
|
|
469
|
+
if (declarator)
|
|
470
|
+
recordOrigin(declarator, origin);
|
|
471
|
+
return;
|
|
472
|
+
}
|
|
473
|
+
// Object/array binding patterns: each BindingElement is its own symbol
|
|
474
|
+
// declaration, so record on each element.
|
|
475
|
+
const walk = (pattern) => {
|
|
476
|
+
pattern.forEachChild((child) => {
|
|
477
|
+
if (ts.isBindingElement(child)) {
|
|
478
|
+
// The BindingElement is the symbol's valueDeclaration.
|
|
479
|
+
recordOrigin(child, origin);
|
|
480
|
+
if (!ts.isIdentifier(child.name))
|
|
481
|
+
walk(child.name);
|
|
482
|
+
}
|
|
483
|
+
else if (ts.isObjectBindingPattern(child) || ts.isArrayBindingPattern(child)) {
|
|
484
|
+
walk(child);
|
|
485
|
+
}
|
|
486
|
+
});
|
|
487
|
+
};
|
|
488
|
+
walk(bindingName);
|
|
489
|
+
}
|
|
490
|
+
function visitForBindings(node) {
|
|
491
|
+
// Function parameters: array / typed-array params get their origin.
|
|
492
|
+
if (isFunctionLikeWithBodyNode(node)) {
|
|
493
|
+
for (const param of node.parameters) {
|
|
494
|
+
const paramType = checker.getTypeAtLocation(param);
|
|
495
|
+
const cat = canonicalize(checker, paramType);
|
|
496
|
+
if (cat === "array") {
|
|
497
|
+
recordBindingName(param.name, "array-param");
|
|
498
|
+
}
|
|
499
|
+
else if (cat === "typed-array") {
|
|
500
|
+
recordBindingName(param.name, "typed-array-param");
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
// Variable declarations: classify initializer.
|
|
505
|
+
if (ts.isVariableDeclaration(node) && node.initializer) {
|
|
506
|
+
recordDeclarationOrigin(node.name, node.initializer);
|
|
507
|
+
}
|
|
508
|
+
// Reassignment (task = other) updates the existing declaration's origin
|
|
509
|
+
// to reflect the new value. The symbol resolves back to the original
|
|
510
|
+
// declarator, so we update that node's entry rather than tagging a new one.
|
|
511
|
+
if (ts.isBinaryExpression(node) &&
|
|
512
|
+
isAssignmentOperatorKind(node.operatorToken.kind) &&
|
|
513
|
+
ts.isIdentifier(node.left)) {
|
|
514
|
+
recordReassignmentOrigin(node.left, node.right);
|
|
515
|
+
}
|
|
516
|
+
// for-of / for-in bindings.
|
|
517
|
+
if ((ts.isForOfStatement(node) || ts.isForInStatement(node)) &&
|
|
518
|
+
ts.isVariableDeclarationList(node.initializer)) {
|
|
519
|
+
for (const decl of node.initializer.declarations) {
|
|
520
|
+
if (decl.initializer) {
|
|
521
|
+
recordDeclarationOrigin(decl.name, decl.initializer);
|
|
522
|
+
}
|
|
523
|
+
else {
|
|
524
|
+
recordBindingName(decl.name, "local-binding");
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
// catch bindings.
|
|
529
|
+
if (ts.isCatchClause(node) && node.variableDeclaration) {
|
|
530
|
+
recordBindingName(node.variableDeclaration.name, "local-binding");
|
|
531
|
+
}
|
|
532
|
+
ts.forEachChild(node, visitForBindings);
|
|
533
|
+
}
|
|
534
|
+
/**
|
|
535
|
+
* Classify a declaration initializer and tag the declarator's origin. Only
|
|
536
|
+
* called for new bindings (VariableDeclaration, for-of/in). Non-special
|
|
537
|
+
* bindings get no entry, so resolveOrigin returns undefined for them.
|
|
538
|
+
*/
|
|
539
|
+
function recordDeclarationOrigin(name, initializer) {
|
|
540
|
+
const init = unwrapExpression(initializer);
|
|
541
|
+
if (isMapValueLookupExpression(init, checker)) {
|
|
542
|
+
// Only struct-typed values count as "map-value copies" — primitive
|
|
543
|
+
// values fetched from a map are not mutated-through, matching the old
|
|
544
|
+
// isObjectLikeValueType gate. A *class*-typed value is also exempt: a TS
|
|
545
|
+
// class is a reference type that lowers to a C++ pointer, so
|
|
546
|
+
// `map.get(k)` returns a pointer and a field write through it persists
|
|
547
|
+
// (it is not a value copy). Only interface / object-literal entries are
|
|
548
|
+
// value copies. (Demo #25 Finding A.)
|
|
549
|
+
const bindingType = checker.getTypeAtLocation(name);
|
|
550
|
+
if (isObjectLikeValueType(bindingType) && !isClassInstanceType(bindingType)) {
|
|
551
|
+
recordBindingName(name, "map-value-lookup");
|
|
552
|
+
return;
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
// Otherwise it's a plain local binding — no entry. resolveOrigin returns
|
|
556
|
+
// undefined (meaning "no special origin"), which is correct for the gates.
|
|
557
|
+
}
|
|
558
|
+
/**
|
|
559
|
+
* A reassignment `task = expr` updates the existing declaration's origin to
|
|
560
|
+
* reflect the new value. Resolve the LHS identifier's symbol to its original
|
|
561
|
+
* declarator and overwrite its entry. If the new value isn't a map-value
|
|
562
|
+
* lookup, the origin is cleared (the binding is no longer a copy).
|
|
563
|
+
*/
|
|
564
|
+
function recordReassignmentOrigin(left, right) {
|
|
565
|
+
const symbol = checker.getSymbolAtLocation(left);
|
|
566
|
+
if (!symbol)
|
|
567
|
+
return;
|
|
568
|
+
const declarator = symbol.valueDeclaration ?? symbol.declarations?.[0];
|
|
569
|
+
if (!declarator)
|
|
570
|
+
return;
|
|
571
|
+
const init = unwrapExpression(right);
|
|
572
|
+
if (isMapValueLookupExpression(init, checker)) {
|
|
573
|
+
const valueType = checker.getTypeAtLocation(left);
|
|
574
|
+
if (isObjectLikeValueType(valueType) && !isClassInstanceType(valueType)) {
|
|
575
|
+
recordOrigin(declarator, "map-value-lookup");
|
|
576
|
+
return;
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
// The new value isn't a map-value copy — clear any prior copy origin so a
|
|
580
|
+
// later mutation isn't misattributed. (Keep param origins intact: an array
|
|
581
|
+
// parameter stays an array parameter regardless of reassignment inside the
|
|
582
|
+
// function body, because reassigning a parameter doesn't change its C++
|
|
583
|
+
// by-value passing.)
|
|
584
|
+
const prior = originByDeclaration.get(declarator);
|
|
585
|
+
if (prior === "map-value-lookup") {
|
|
586
|
+
originByDeclaration.delete(declarator);
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
// Build the BindingResolver that gate rules will use.
|
|
590
|
+
const resolver = {
|
|
591
|
+
resolveOrigin(identifier) {
|
|
592
|
+
const symbol = checker.getSymbolAtLocation(identifier);
|
|
593
|
+
if (!symbol)
|
|
594
|
+
return undefined;
|
|
595
|
+
// Resolve aliases (imports) to the underlying value declaration.
|
|
596
|
+
let valueSymbol = symbol;
|
|
597
|
+
if (symbol.flags & ts.SymbolFlags.Alias) {
|
|
598
|
+
try {
|
|
599
|
+
valueSymbol = checker.getAliasedSymbol(symbol);
|
|
600
|
+
}
|
|
601
|
+
catch {
|
|
602
|
+
/* keep original */
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
const declarations = valueSymbol.valueDeclaration
|
|
606
|
+
? [valueSymbol.valueDeclaration, ...(valueSymbol.declarations ?? [])]
|
|
607
|
+
: valueSymbol.declarations ?? [];
|
|
608
|
+
for (const decl of declarations) {
|
|
609
|
+
// The declaration node itself is the key we recorded (the identifier
|
|
610
|
+
// for simple bindings, or the BindingElement/identifier for patterns).
|
|
611
|
+
if (originByDeclaration.has(decl)) {
|
|
612
|
+
return originByDeclaration.get(decl);
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
return undefined;
|
|
616
|
+
},
|
|
617
|
+
};
|
|
618
|
+
// Attach the resolver to the result via a closure, exposed through a
|
|
619
|
+
// separate field so gate rules in type-checker.ts can resolve identifiers.
|
|
620
|
+
return {
|
|
621
|
+
facts,
|
|
622
|
+
diagnostics,
|
|
623
|
+
resolver,
|
|
624
|
+
};
|
|
625
|
+
}
|
|
626
|
+
function isFunctionLikeWithBodyNode(node) {
|
|
627
|
+
return ((ts.isFunctionDeclaration(node) ||
|
|
628
|
+
ts.isMethodDeclaration(node) ||
|
|
629
|
+
ts.isConstructorDeclaration(node) ||
|
|
630
|
+
ts.isGetAccessorDeclaration(node) ||
|
|
631
|
+
ts.isSetAccessorDeclaration(node) ||
|
|
632
|
+
ts.isFunctionExpression(node) ||
|
|
633
|
+
ts.isArrowFunction(node)) &&
|
|
634
|
+
!!node.body);
|
|
635
|
+
}
|
|
636
|
+
function isAssignmentOperatorKind(kind) {
|
|
637
|
+
return (kind === ts.SyntaxKind.EqualsToken ||
|
|
638
|
+
kind === ts.SyntaxKind.PlusEqualsToken ||
|
|
639
|
+
kind === ts.SyntaxKind.MinusEqualsToken ||
|
|
640
|
+
kind === ts.SyntaxKind.AsteriskEqualsToken ||
|
|
641
|
+
kind === ts.SyntaxKind.SlashEqualsToken ||
|
|
642
|
+
kind === ts.SyntaxKind.PercentEqualsToken ||
|
|
643
|
+
kind === ts.SyntaxKind.AmpersandEqualsToken ||
|
|
644
|
+
kind === ts.SyntaxKind.BarEqualsToken ||
|
|
645
|
+
kind === ts.SyntaxKind.CaretEqualsToken ||
|
|
646
|
+
kind === ts.SyntaxKind.LessThanLessThanEqualsToken ||
|
|
647
|
+
kind === ts.SyntaxKind.GreaterThanGreaterThanEqualsToken ||
|
|
648
|
+
kind === ts.SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken ||
|
|
649
|
+
kind === ts.SyntaxKind.QuestionQuestionEqualsToken ||
|
|
650
|
+
kind === ts.SyntaxKind.AmpersandAmpersandEqualsToken ||
|
|
651
|
+
kind === ts.SyntaxKind.BarBarEqualsToken);
|
|
652
|
+
}
|