@scriptc/compiler 0.1.6 → 0.2.0
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/ambient/scriptc-node-fallback.d.ts +73 -25
- package/ambient/scriptc-overrides.d.ts +10 -6
- package/ambient/scriptc.d.ts +28 -5
- package/dist/backend/build-cache.d.ts +1 -3
- package/dist/backend/build-cache.js +3 -22
- package/dist/backend/build-cache.js.map +1 -1
- package/dist/backend/cache-root.d.ts +3 -0
- package/dist/backend/cache-root.js +23 -0
- package/dist/backend/cache-root.js.map +1 -0
- package/dist/backend/cycle-analysis.d.ts +15 -0
- package/dist/backend/cycle-analysis.js +107 -0
- package/dist/backend/cycle-analysis.js.map +1 -0
- package/dist/backend/cycle-analysis.test.js +124 -0
- package/dist/backend/cycle-analysis.test.js.map +1 -0
- package/dist/backend/debug-symbols.d.ts +8 -0
- package/dist/backend/debug-symbols.js +52 -0
- package/dist/backend/debug-symbols.js.map +1 -0
- package/dist/backend/executable-features.d.ts +4 -0
- package/dist/backend/executable-features.js +42 -0
- package/dist/backend/executable-features.js.map +1 -0
- package/dist/backend/external-c.d.ts +5 -27
- package/dist/backend/external-c.js +5 -40
- package/dist/backend/external-c.js.map +1 -1
- package/dist/backend/ffi-callbacks.d.ts +2 -3
- package/dist/backend/ffi-callbacks.js +2 -3
- package/dist/backend/ffi-callbacks.js.map +1 -1
- package/dist/backend/library-identity-markers.d.ts +4 -9
- package/dist/backend/library-identity-markers.js +11 -49
- package/dist/backend/library-identity-markers.js.map +1 -1
- package/dist/backend/library-pack.d.ts +5 -0
- package/dist/backend/library-pack.js +123 -0
- package/dist/backend/library-pack.js.map +1 -0
- package/dist/backend/link-plan-core.d.ts +20 -0
- package/dist/backend/link-plan-core.js +18 -0
- package/dist/backend/link-plan-core.js.map +1 -0
- package/dist/backend/link-plan.d.ts +2 -1
- package/dist/backend/link-plan.js +18 -17
- package/dist/backend/link-plan.js.map +1 -1
- package/dist/backend/link-trace.d.ts +2 -0
- package/dist/backend/link-trace.js +24 -0
- package/dist/backend/link-trace.js.map +1 -0
- package/dist/backend/linker.js +9 -1
- package/dist/backend/linker.js.map +1 -1
- package/dist/backend/literals.d.ts +9 -0
- package/dist/backend/literals.js +35 -0
- package/dist/backend/literals.js.map +1 -0
- package/dist/backend/literals.test.d.ts +1 -0
- package/dist/backend/literals.test.js +53 -0
- package/dist/backend/literals.test.js.map +1 -0
- package/dist/backend/llvm/blocks.d.ts +4 -0
- package/dist/backend/llvm/blocks.js +25 -4
- package/dist/backend/llvm/blocks.js.map +1 -1
- package/dist/backend/llvm/blocks.test.d.ts +1 -0
- package/dist/backend/llvm/blocks.test.js +91 -0
- package/dist/backend/llvm/blocks.test.js.map +1 -0
- package/dist/backend/llvm/classes.d.ts +14 -8
- package/dist/backend/llvm/classes.js +70 -28
- package/dist/backend/llvm/classes.js.map +1 -1
- package/dist/backend/llvm/common.d.ts +5 -1
- package/dist/backend/llvm/common.js +45 -4
- package/dist/backend/llvm/common.js.map +1 -1
- package/dist/backend/llvm/debug-info.d.ts +32 -0
- package/dist/backend/llvm/debug-info.js +198 -0
- package/dist/backend/llvm/debug-info.js.map +1 -0
- package/dist/backend/llvm/debug-info.test.d.ts +1 -0
- package/dist/backend/llvm/debug-info.test.js +110 -0
- package/dist/backend/llvm/debug-info.test.js.map +1 -0
- package/dist/backend/llvm/dyn.d.ts +16 -2
- package/dist/backend/llvm/dyn.js +498 -175
- package/dist/backend/llvm/dyn.js.map +1 -1
- package/dist/backend/llvm/emitter.d.ts +598 -1
- package/dist/backend/llvm/emitter.js +739 -409
- package/dist/backend/llvm/emitter.js.map +1 -1
- package/dist/backend/llvm/emitter.test.d.ts +1 -0
- package/dist/backend/llvm/emitter.test.js +179 -0
- package/dist/backend/llvm/emitter.test.js.map +1 -0
- package/dist/backend/llvm/expr-async.js +23 -14
- package/dist/backend/llvm/expr-async.js.map +1 -1
- package/dist/backend/llvm/expr-bytes.d.ts +1 -1
- package/dist/backend/llvm/expr-bytes.js +32 -14
- package/dist/backend/llvm/expr-bytes.js.map +1 -1
- package/dist/backend/llvm/expr-callbacks.d.ts +1 -1
- package/dist/backend/llvm/expr-callbacks.js +25 -22
- package/dist/backend/llvm/expr-callbacks.js.map +1 -1
- package/dist/backend/llvm/expr-calls.js +136 -35
- package/dist/backend/llvm/expr-calls.js.map +1 -1
- package/dist/backend/llvm/expr-containers.d.ts +1 -1
- package/dist/backend/llvm/expr-containers.js +43 -14
- package/dist/backend/llvm/expr-containers.js.map +1 -1
- package/dist/backend/llvm/expr-context.d.ts +5 -260
- package/dist/backend/llvm/expr-control.js +9 -5
- package/dist/backend/llvm/expr-control.js.map +1 -1
- package/dist/backend/llvm/expr-dispatch.d.ts +1 -1
- package/dist/backend/llvm/expr-dispatch.js +2 -0
- package/dist/backend/llvm/expr-dispatch.js.map +1 -1
- package/dist/backend/llvm/expr-dynamic.js +72 -74
- package/dist/backend/llvm/expr-dynamic.js.map +1 -1
- package/dist/backend/llvm/expr-island.d.ts +1 -1
- package/dist/backend/llvm/expr-island.js +11 -7
- package/dist/backend/llvm/expr-island.js.map +1 -1
- package/dist/backend/llvm/expr-primitives.d.ts +1 -1
- package/dist/backend/llvm/expr-primitives.js +51 -30
- package/dist/backend/llvm/expr-primitives.js.map +1 -1
- package/dist/backend/llvm/expr-primitives.test.js +1 -1
- package/dist/backend/llvm/expr-records.d.ts +5 -1
- package/dist/backend/llvm/expr-records.js +43 -4
- package/dist/backend/llvm/expr-records.js.map +1 -1
- package/dist/backend/llvm/expr-stream-bridges.d.ts +4 -4
- package/dist/backend/llvm/expr-stream-bridges.js +102 -121
- package/dist/backend/llvm/expr-stream-bridges.js.map +1 -1
- package/dist/backend/llvm/expr-stream-bridges.test.d.ts +1 -0
- package/dist/backend/llvm/expr-stream-bridges.test.js +55 -0
- package/dist/backend/llvm/expr-stream-bridges.test.js.map +1 -0
- package/dist/backend/llvm/expr-stream-callbacks.d.ts +1 -1
- package/dist/backend/llvm/expr-stream-callbacks.js +12 -12
- package/dist/backend/llvm/expr-stream-callbacks.js.map +1 -1
- package/dist/backend/llvm/layouts.d.ts +19 -0
- package/dist/backend/llvm/layouts.js +12 -0
- package/dist/backend/llvm/layouts.js.map +1 -0
- package/dist/backend/llvm/lib-abi.d.ts +19 -0
- package/dist/backend/llvm/lib-abi.js +82 -0
- package/dist/backend/llvm/lib-abi.js.map +1 -0
- package/dist/backend/llvm/lib-connect.d.ts +2 -0
- package/dist/backend/llvm/lib-connect.js +51 -0
- package/dist/backend/llvm/lib-connect.js.map +1 -0
- package/dist/backend/llvm/lib-datagram.d.ts +2 -0
- package/dist/backend/llvm/lib-datagram.js +135 -0
- package/dist/backend/llvm/lib-datagram.js.map +1 -0
- package/dist/backend/llvm/lib-dispatch.js +50 -6
- package/dist/backend/llvm/lib-dispatch.js.map +1 -1
- package/dist/backend/llvm/lib-filesystem.js +59 -18
- package/dist/backend/llvm/lib-filesystem.js.map +1 -1
- package/dist/backend/llvm/lib-http2.d.ts +2 -0
- package/dist/backend/llvm/lib-http2.js +215 -0
- package/dist/backend/llvm/lib-http2.js.map +1 -0
- package/dist/backend/llvm/lib-lookup.d.ts +2 -0
- package/dist/backend/llvm/lib-lookup.js +39 -0
- package/dist/backend/llvm/lib-lookup.js.map +1 -0
- package/dist/backend/llvm/lib-network.js +25 -2
- package/dist/backend/llvm/lib-network.js.map +1 -1
- package/dist/backend/llvm/lib-process.js +21 -16
- package/dist/backend/llvm/lib-process.js.map +1 -1
- package/dist/backend/llvm/lib-shared.js +125 -1
- package/dist/backend/llvm/lib-shared.js.map +1 -1
- package/dist/backend/llvm/lib-stream.js +3 -2
- package/dist/backend/llvm/lib-stream.js.map +1 -1
- package/dist/backend/llvm/lib-test.d.ts +2 -0
- package/dist/backend/llvm/lib-test.js +34 -0
- package/dist/backend/llvm/lib-test.js.map +1 -0
- package/dist/backend/llvm/lib-tls.d.ts +2 -0
- package/dist/backend/llvm/lib-tls.js +82 -0
- package/dist/backend/llvm/lib-tls.js.map +1 -0
- package/dist/backend/llvm/shapes.d.ts +10 -9
- package/dist/backend/llvm/shapes.js +35 -130
- package/dist/backend/llvm/shapes.js.map +1 -1
- package/dist/backend/llvm/split.js +4 -1
- package/dist/backend/llvm/split.js.map +1 -1
- package/dist/backend/llvm/split.test.js +26 -1
- package/dist/backend/llvm/split.test.js.map +1 -1
- package/dist/backend/llvm/unsupported.d.ts +1 -0
- package/dist/backend/llvm/unsupported.js +1 -2
- package/dist/backend/llvm/unsupported.js.map +1 -1
- package/dist/backend/llvm/walkers.js +1 -7
- package/dist/backend/llvm/walkers.js.map +1 -1
- package/dist/backend/mangle.d.ts +2 -2
- package/dist/backend/mangle.js +16 -5
- package/dist/backend/mangle.js.map +1 -1
- package/dist/backend/{c/may-throw.d.ts → may-throw.d.ts} +1 -1
- package/dist/backend/{c/may-throw.js → may-throw.js} +51 -46
- package/dist/backend/may-throw.js.map +1 -0
- package/dist/backend/may-throw.test.d.ts +1 -0
- package/dist/backend/may-throw.test.js +58 -0
- package/dist/backend/may-throw.test.js.map +1 -0
- package/dist/backend/native-codegen-core.d.ts +22 -0
- package/dist/backend/native-codegen-core.js +44 -0
- package/dist/backend/native-codegen-core.js.map +1 -0
- package/dist/backend/native-codegen.d.ts +3 -19
- package/dist/backend/native-codegen.js +5 -44
- package/dist/backend/native-codegen.js.map +1 -1
- package/dist/backend/native-codegen.test.js +2 -2
- package/dist/backend/native-codegen.test.js.map +1 -1
- package/dist/backend/native-link-info-core.d.ts +94 -0
- package/dist/backend/native-link-info-core.js +62 -0
- package/dist/backend/native-link-info-core.js.map +1 -0
- package/dist/backend/native-link-info.d.ts +3 -88
- package/dist/backend/native-link-info.js +12 -243
- package/dist/backend/native-link-info.js.map +1 -1
- package/dist/backend/native-link-info.test.js +41 -146
- package/dist/backend/native-link-info.test.js.map +1 -1
- package/dist/backend/native-toolchain.d.ts +10 -25
- package/dist/backend/native-toolchain.js +89 -219
- package/dist/backend/native-toolchain.js.map +1 -1
- package/dist/backend/native-toolchain.test.js +15 -5
- package/dist/backend/native-toolchain.test.js.map +1 -1
- package/dist/backend/native-tools.d.ts +24 -0
- package/dist/backend/native-tools.js +33 -0
- package/dist/backend/native-tools.js.map +1 -0
- package/dist/backend/object-localize.js +19 -8
- package/dist/backend/object-localize.js.map +1 -1
- package/dist/backend/object-localize.test.js +22 -0
- package/dist/backend/object-localize.test.js.map +1 -1
- package/dist/backend/runtime-abi.d.ts +2 -2
- package/dist/backend/runtime-abi.js +2 -2
- package/dist/backend/runtime-pack-core.d.ts +101 -0
- package/dist/backend/runtime-pack-core.js +172 -0
- package/dist/backend/runtime-pack-core.js.map +1 -0
- package/dist/backend/runtime-pack-native.d.ts +21 -0
- package/dist/backend/runtime-pack-native.js +70 -0
- package/dist/backend/runtime-pack-native.js.map +1 -0
- package/dist/backend/runtime-pack-targets.test.js +1 -1
- package/dist/backend/runtime-pack.d.ts +3 -83
- package/dist/backend/runtime-pack.js +8 -151
- package/dist/backend/runtime-pack.js.map +1 -1
- package/dist/backend/runtime-pack.test.js +24 -1
- package/dist/backend/runtime-pack.test.js.map +1 -1
- package/dist/backend/source-locations.d.ts +12 -0
- package/dist/backend/source-locations.js +32 -0
- package/dist/backend/source-locations.js.map +1 -0
- package/dist/backend/source-locations.test.d.ts +1 -0
- package/dist/backend/source-locations.test.js +16 -0
- package/dist/backend/source-locations.test.js.map +1 -0
- package/dist/backend/target-diagnostics.d.ts +22 -0
- package/dist/backend/target-diagnostics.js +108 -0
- package/dist/backend/target-diagnostics.js.map +1 -0
- package/dist/backend/target-platform.d.ts +15 -0
- package/dist/backend/target-platform.js +72 -0
- package/dist/backend/target-platform.js.map +1 -0
- package/dist/backend/targets.d.ts +13 -4
- package/dist/backend/targets.js +95 -30
- package/dist/backend/targets.js.map +1 -1
- package/dist/backend/targets.test.js +65 -4
- package/dist/backend/targets.test.js.map +1 -1
- package/dist/backend/toolchain-environment.d.ts +6 -0
- package/dist/backend/toolchain-environment.js +99 -0
- package/dist/backend/toolchain-environment.js.map +1 -0
- package/dist/backend/vendor-archives.d.ts +1 -9
- package/dist/backend/vendor-archives.js +2 -9
- package/dist/backend/vendor-archives.js.map +1 -1
- package/dist/backend/vendor-inputs.d.ts +9 -0
- package/dist/backend/vendor-inputs.js +10 -0
- package/dist/backend/vendor-inputs.js.map +1 -0
- package/dist/backend/warm-cache.d.ts +3 -0
- package/dist/backend/warm-cache.js +41 -0
- package/dist/backend/warm-cache.js.map +1 -0
- package/dist/cli/command.d.ts +3 -0
- package/dist/cli/command.js +328 -0
- package/dist/cli/command.js.map +1 -0
- package/dist/cli/host.d.ts +24 -0
- package/dist/cli/host.js +2 -0
- package/dist/cli/host.js.map +1 -0
- package/dist/cli/output-options.d.ts +26 -0
- package/dist/cli/output-options.js +60 -0
- package/dist/cli/output-options.js.map +1 -0
- package/dist/cli/paths.d.ts +12 -0
- package/dist/cli/paths.js +42 -0
- package/dist/cli/paths.js.map +1 -0
- package/dist/cli/usage.d.ts +74 -0
- package/dist/cli/usage.js +86 -0
- package/dist/cli/usage.js.map +1 -0
- package/dist/cli/wasi-paths.d.ts +8 -0
- package/dist/cli/wasi-paths.js +41 -0
- package/dist/cli/wasi-paths.js.map +1 -0
- package/dist/cli/wasi-runner.d.ts +2 -0
- package/dist/cli/wasi-runner.js +28 -0
- package/dist/cli/wasi-runner.js.map +1 -0
- package/dist/compat/fetch-profile.js +3 -3
- package/dist/compat/fetch-profile.js.map +1 -1
- package/dist/compile-types.d.ts +162 -0
- package/dist/compile-types.js +2 -0
- package/dist/compile-types.js.map +1 -0
- package/dist/coverage/report.js +11 -5
- package/dist/coverage/report.js.map +1 -1
- package/dist/coverage/surface-manifest.js +5 -2
- package/dist/coverage/surface-manifest.js.map +1 -1
- package/dist/diagnostics/diagnostic.d.ts +4 -0
- package/dist/diagnostics/diagnostic.js +15 -3
- package/dist/diagnostics/diagnostic.js.map +1 -1
- package/dist/executable/executable-cache.d.ts +3 -4
- package/dist/executable/executable-cache.js +33 -10
- package/dist/executable/executable-cache.js.map +1 -1
- package/dist/executable/executable-cache.test.js +38 -16
- package/dist/executable/executable-cache.test.js.map +1 -1
- package/dist/executable/prepare.d.ts +14 -0
- package/dist/executable/prepare.js +66 -0
- package/dist/executable/prepare.js.map +1 -0
- package/dist/ffi/ffi-manifest.d.ts +11 -4
- package/dist/ffi/ffi-manifest.js +170 -58
- package/dist/ffi/ffi-manifest.js.map +1 -1
- package/dist/ffi/ffi-manifest.test.d.ts +1 -0
- package/dist/ffi/ffi-manifest.test.js +111 -0
- package/dist/ffi/ffi-manifest.test.js.map +1 -0
- package/dist/format-integer.d.ts +3 -0
- package/dist/format-integer.js +14 -0
- package/dist/format-integer.js.map +1 -0
- package/dist/frontend/analysis.d.ts +5 -0
- package/dist/frontend/analysis.js +88 -0
- package/dist/frontend/analysis.js.map +1 -0
- package/dist/frontend/arguments-usage.d.ts +4 -0
- package/dist/frontend/arguments-usage.js +40 -0
- package/dist/frontend/arguments-usage.js.map +1 -0
- package/dist/frontend/binding-source.d.ts +4 -0
- package/dist/frontend/binding-source.js +18 -0
- package/dist/frontend/binding-source.js.map +1 -0
- package/dist/frontend/builtin-modules.d.ts +1 -1
- package/dist/frontend/builtin-modules.js +1 -1
- package/dist/frontend/builtin-modules.js.map +1 -1
- package/dist/frontend/cjs-lexer.d.ts +4 -25
- package/dist/frontend/cjs-lexer.js +6 -806
- package/dist/frontend/cjs-lexer.js.map +1 -1
- package/dist/frontend/cjs-resolve.d.ts +5 -0
- package/dist/frontend/cjs-resolve.js +429 -0
- package/dist/frontend/cjs-resolve.js.map +1 -0
- package/dist/frontend/cjs-syntax.d.ts +26 -0
- package/dist/frontend/cjs-syntax.js +806 -0
- package/dist/frontend/cjs-syntax.js.map +1 -0
- package/dist/frontend/comptime-node.d.ts +4 -0
- package/dist/frontend/comptime-node.js +16 -0
- package/dist/frontend/comptime-node.js.map +1 -0
- package/dist/frontend/comptime-node.test.d.ts +1 -0
- package/dist/frontend/comptime-node.test.js +37 -0
- package/dist/frontend/comptime-node.test.js.map +1 -0
- package/dist/frontend/dts-paths.d.ts +3 -4
- package/dist/frontend/dts-paths.js +15 -8
- package/dist/frontend/dts-paths.js.map +1 -1
- package/dist/frontend/fork-target.js +2 -2
- package/dist/frontend/fork-target.js.map +1 -1
- package/dist/frontend/helper-tokens.d.ts +1 -0
- package/dist/frontend/helper-tokens.js +147 -0
- package/dist/frontend/helper-tokens.js.map +1 -0
- package/dist/frontend/helper-tokens.test.d.ts +1 -0
- package/dist/frontend/helper-tokens.test.js +35 -0
- package/dist/frontend/helper-tokens.test.js.map +1 -0
- package/dist/frontend/inferred-js-diagnostics.d.ts +5 -0
- package/dist/frontend/inferred-js-diagnostics.js +69 -0
- package/dist/frontend/inferred-js-diagnostics.js.map +1 -0
- package/dist/frontend/inferred-js-diagnostics.test.d.ts +1 -0
- package/dist/frontend/inferred-js-diagnostics.test.js +58 -0
- package/dist/frontend/inferred-js-diagnostics.test.js.map +1 -0
- package/dist/frontend/input-tracker.d.ts +5 -2
- package/dist/frontend/input-tracker.js +24 -3
- package/dist/frontend/input-tracker.js.map +1 -1
- package/dist/frontend/input-tracker.test.js +23 -0
- package/dist/frontend/input-tracker.test.js.map +1 -1
- package/dist/frontend/lib-contract.js +32 -24
- package/dist/frontend/lib-contract.js.map +1 -1
- package/dist/frontend/literal-values.d.ts +4 -0
- package/dist/frontend/literal-values.js +21 -0
- package/dist/frontend/literal-values.js.map +1 -0
- package/dist/frontend/literal-values.test.d.ts +1 -0
- package/dist/frontend/literal-values.test.js +91 -0
- package/dist/frontend/literal-values.test.js.map +1 -0
- package/dist/frontend/lowering/abstract-equality.d.ts +1 -1
- package/dist/frontend/lowering/abstract-equality.js +3 -0
- package/dist/frontend/lowering/abstract-equality.js.map +1 -1
- package/dist/frontend/lowering/array-indexed-mutation.js +11 -6
- package/dist/frontend/lowering/array-indexed-mutation.js.map +1 -1
- package/dist/frontend/lowering/array-values.d.ts +4 -1
- package/dist/frontend/lowering/array-values.js +43 -1
- package/dist/frontend/lowering/array-values.js.map +1 -1
- package/dist/frontend/lowering/callback-arg.js +1 -0
- package/dist/frontend/lowering/callback-arg.js.map +1 -1
- package/dist/frontend/lowering/class-callbacks.d.ts +12 -0
- package/dist/frontend/lowering/class-callbacks.js +63 -0
- package/dist/frontend/lowering/class-callbacks.js.map +1 -0
- package/dist/frontend/lowering/class-descriptors.d.ts +9 -0
- package/dist/frontend/lowering/class-descriptors.js +73 -0
- package/dist/frontend/lowering/class-descriptors.js.map +1 -0
- package/dist/frontend/lowering/class-dynamic-dispatch.d.ts +41 -0
- package/dist/frontend/lowering/class-dynamic-dispatch.js +803 -0
- package/dist/frontend/lowering/class-dynamic-dispatch.js.map +1 -0
- package/dist/frontend/lowering/class-dynamic-dispatch.test.d.ts +1 -0
- package/dist/frontend/lowering/class-dynamic-dispatch.test.js +92 -0
- package/dist/frontend/lowering/class-dynamic-dispatch.test.js.map +1 -0
- package/dist/frontend/lowering/class-instance-constructor.d.ts +7 -0
- package/dist/frontend/lowering/class-instance-constructor.js +64 -0
- package/dist/frontend/lowering/class-instance-constructor.js.map +1 -0
- package/dist/frontend/lowering/class-method-values.d.ts +10 -0
- package/dist/frontend/lowering/class-method-values.js +170 -0
- package/dist/frontend/lowering/class-method-values.js.map +1 -0
- package/dist/frontend/lowering/class-prototypes.d.ts +16 -0
- package/dist/frontend/lowering/class-prototypes.js +134 -0
- package/dist/frontend/lowering/class-prototypes.js.map +1 -0
- package/dist/frontend/lowering/class-static-data.d.ts +6 -0
- package/dist/frontend/lowering/class-static-data.js +56 -0
- package/dist/frontend/lowering/class-static-data.js.map +1 -0
- package/dist/frontend/lowering/containers/buffer-numeric-methods.d.ts +4 -0
- package/dist/frontend/lowering/containers/buffer-numeric-methods.js +26 -0
- package/dist/frontend/lowering/containers/buffer-numeric-methods.js.map +1 -0
- package/dist/frontend/lowering/containers/bytes.d.ts +11 -7
- package/dist/frontend/lowering/containers/bytes.js +164 -155
- package/dist/frontend/lowering/containers/bytes.js.map +1 -1
- package/dist/frontend/lowering/containers/string-and-regexp.d.ts +2 -2
- package/dist/frontend/lowering/containers/string-and-regexp.js +39 -16
- package/dist/frontend/lowering/containers/string-and-regexp.js.map +1 -1
- package/dist/frontend/lowering/containers/string-replacement.d.ts +7 -0
- package/dist/frontend/lowering/containers/string-replacement.js +217 -0
- package/dist/frontend/lowering/containers/string-replacement.js.map +1 -0
- package/dist/frontend/lowering/error-methods.d.ts +11 -0
- package/dist/frontend/lowering/error-methods.js +78 -0
- package/dist/frontend/lowering/error-methods.js.map +1 -0
- package/dist/frontend/lowering/expressions/global-symbols.d.ts +6 -0
- package/dist/frontend/lowering/expressions/global-symbols.js +11 -0
- package/dist/frontend/lowering/expressions/global-symbols.js.map +1 -0
- package/dist/frontend/lowering/expressions/native-proxy.d.ts +7 -0
- package/dist/frontend/lowering/expressions/native-proxy.js +49 -0
- package/dist/frontend/lowering/expressions/native-proxy.js.map +1 -0
- package/dist/frontend/lowering/expressions/nullish-assignment.d.ts +6 -0
- package/dist/frontend/lowering/expressions/nullish-assignment.js +124 -0
- package/dist/frontend/lowering/expressions/nullish-assignment.js.map +1 -0
- package/dist/frontend/lowering/expressions/object-literals.d.ts +8 -8
- package/dist/frontend/lowering/expressions/object-literals.js +485 -185
- package/dist/frontend/lowering/expressions/object-literals.js.map +1 -1
- package/dist/frontend/lowering/expressions/optional-chains.d.ts +5 -6
- package/dist/frontend/lowering/expressions/optional-chains.js +27 -14
- package/dist/frontend/lowering/expressions/optional-chains.js.map +1 -1
- package/dist/frontend/lowering/expressions/try-lower-expression.js +1 -0
- package/dist/frontend/lowering/expressions/try-lower-expression.js.map +1 -1
- package/dist/frontend/lowering/expressions/union-field-write.d.ts +8 -0
- package/dist/frontend/lowering/expressions/union-field-write.js +71 -0
- package/dist/frontend/lowering/expressions/union-field-write.js.map +1 -0
- package/dist/frontend/lowering/function-context.d.ts +93 -0
- package/dist/frontend/lowering/function-context.js +103 -0
- package/dist/frontend/lowering/function-context.js.map +1 -0
- package/dist/frontend/lowering/generator-delegation.d.ts +7 -0
- package/dist/frontend/lowering/generator-delegation.js +85 -0
- package/dist/frontend/lowering/generator-delegation.js.map +1 -0
- package/dist/frontend/lowering/generator-dynamic-dispatch.d.ts +10 -0
- package/dist/frontend/lowering/generator-dynamic-dispatch.js +105 -0
- package/dist/frontend/lowering/generator-dynamic-dispatch.js.map +1 -0
- package/dist/frontend/lowering/lib-boundary.d.ts +2 -1
- package/dist/frontend/lowering/lib-boundary.js +59 -20
- package/dist/frontend/lowering/lib-boundary.js.map +1 -1
- package/dist/frontend/lowering/lib-boundary.test.d.ts +1 -0
- package/dist/frontend/lowering/lib-boundary.test.js +45 -0
- package/dist/frontend/lowering/lib-boundary.test.js.map +1 -0
- package/dist/frontend/lowering/lower-array-sort.d.ts +1 -1
- package/dist/frontend/lowering/lower-assert.d.ts +1 -1
- package/dist/frontend/lowering/lower-assert.js.map +1 -1
- package/dist/frontend/lowering/lower-async-iteration.d.ts +1 -1
- package/dist/frontend/lowering/lower-async-iteration.js.map +1 -1
- package/dist/frontend/lowering/lower-builtin-values.d.ts +12 -0
- package/dist/frontend/lowering/lower-builtin-values.js +139 -0
- package/dist/frontend/lowering/lower-builtin-values.js.map +1 -0
- package/dist/frontend/lowering/lower-builtins.d.ts +27 -41
- package/dist/frontend/lowering/lower-builtins.js +553 -346
- package/dist/frontend/lowering/lower-builtins.js.map +1 -1
- package/dist/frontend/lowering/lower-calls.d.ts +22 -26
- package/dist/frontend/lowering/lower-calls.js +1373 -380
- package/dist/frontend/lowering/lower-calls.js.map +1 -1
- package/dist/frontend/lowering/lower-classes.d.ts +83 -45
- package/dist/frontend/lowering/lower-classes.js +952 -339
- package/dist/frontend/lowering/lower-classes.js.map +1 -1
- package/dist/frontend/lowering/lower-comptime.d.ts +1 -1
- package/dist/frontend/lowering/lower-comptime.js +23 -32
- package/dist/frontend/lowering/lower-comptime.js.map +1 -1
- package/dist/frontend/lowering/lower-containers.d.ts +27 -32
- package/dist/frontend/lowering/lower-containers.js +796 -151
- package/dist/frontend/lowering/lower-containers.js.map +1 -1
- package/dist/frontend/lowering/lower-dgram.d.ts +1 -1
- package/dist/frontend/lowering/lower-dgram.js.map +1 -1
- package/dist/frontend/lowering/lower-enums.d.ts +1 -1
- package/dist/frontend/lowering/lower-enums.js.map +1 -1
- package/dist/frontend/lowering/lower-event-emitter.d.ts +30 -1
- package/dist/frontend/lowering/lower-event-emitter.js +58 -26
- package/dist/frontend/lowering/lower-event-emitter.js.map +1 -1
- package/dist/frontend/lowering/lower-expando.d.ts +7 -1
- package/dist/frontend/lowering/lower-expando.js +14 -6
- package/dist/frontend/lowering/lower-expando.js.map +1 -1
- package/dist/frontend/lowering/lower-exprs.d.ts +35 -11
- package/dist/frontend/lowering/lower-exprs.js +1785 -790
- package/dist/frontend/lowering/lower-exprs.js.map +1 -1
- package/dist/frontend/lowering/lower-generators.d.ts +2 -5
- package/dist/frontend/lowering/lower-generators.js +77 -46
- package/dist/frontend/lowering/lower-generators.js.map +1 -1
- package/dist/frontend/lowering/lower-global-value.d.ts +7 -0
- package/dist/frontend/lowering/lower-global-value.js +17 -0
- package/dist/frontend/lowering/lower-global-value.js.map +1 -0
- package/dist/frontend/lowering/lower-inspect.d.ts +1 -1
- package/dist/frontend/lowering/lower-inspect.js +17 -8
- package/dist/frontend/lowering/lower-inspect.js.map +1 -1
- package/dist/frontend/lowering/lower-island.d.ts +5 -8
- package/dist/frontend/lowering/lower-island.js +34 -23
- package/dist/frontend/lowering/lower-island.js.map +1 -1
- package/dist/frontend/lowering/lower-mixins.d.ts +2 -2
- package/dist/frontend/lowering/lower-mixins.js +5 -3
- package/dist/frontend/lowering/lower-mixins.js.map +1 -1
- package/dist/frontend/lowering/lower-modules.d.ts +1 -1
- package/dist/frontend/lowering/lower-modules.js +252 -38
- package/dist/frontend/lowering/lower-modules.js.map +1 -1
- package/dist/frontend/lowering/lower-namespaces.d.ts +1 -1
- package/dist/frontend/lowering/lower-namespaces.js.map +1 -1
- package/dist/frontend/lowering/lower-node-module.d.ts +1 -1
- package/dist/frontend/lowering/lower-node-module.js +13 -10
- package/dist/frontend/lowering/lower-node-module.js.map +1 -1
- package/dist/frontend/lowering/lower-server.d.ts +1 -1
- package/dist/frontend/lowering/lower-server.js +6 -10
- package/dist/frontend/lowering/lower-server.js.map +1 -1
- package/dist/frontend/lowering/lower-stmts.d.ts +10 -14
- package/dist/frontend/lowering/lower-stmts.js +731 -532
- package/dist/frontend/lowering/lower-stmts.js.map +1 -1
- package/dist/frontend/lowering/lower-stream.d.ts +1 -1
- package/dist/frontend/lowering/lower-stream.js +19 -10
- package/dist/frontend/lowering/lower-stream.js.map +1 -1
- package/dist/frontend/lowering/lower-test.d.ts +1 -1
- package/dist/frontend/lowering/lower-test.js.map +1 -1
- package/dist/frontend/lowering/lower-url.d.ts +6 -0
- package/dist/frontend/lowering/lower-url.js +77 -0
- package/dist/frontend/lowering/lower-url.js.map +1 -0
- package/dist/frontend/lowering/lowerer.d.ts +113 -189
- package/dist/frontend/lowering/lowerer.js +1033 -663
- package/dist/frontend/lowering/lowerer.js.map +1 -1
- package/dist/frontend/lowering/lowerer.test.d.ts +1 -0
- package/dist/frontend/lowering/lowerer.test.js +26 -0
- package/dist/frontend/lowering/lowerer.test.js.map +1 -0
- package/dist/frontend/lowering/native-ffi.d.ts +5 -0
- package/dist/frontend/lowering/native-ffi.js +40 -0
- package/dist/frontend/lowering/native-ffi.js.map +1 -0
- package/dist/frontend/lowering/object-enumeration-receiver.d.ts +7 -0
- package/dist/frontend/lowering/object-enumeration-receiver.js +33 -0
- package/dist/frontend/lowering/object-enumeration-receiver.js.map +1 -0
- package/dist/frontend/lowering/object-factory-new.d.ts +8 -0
- package/dist/frontend/lowering/object-factory-new.js +68 -0
- package/dist/frontend/lowering/object-factory-new.js.map +1 -0
- package/dist/frontend/lowering/optional-arguments.js +6 -1
- package/dist/frontend/lowering/optional-arguments.js.map +1 -1
- package/dist/frontend/lowering/pairs-snapshot.js +2 -1
- package/dist/frontend/lowering/pairs-snapshot.js.map +1 -1
- package/dist/frontend/lowering/runtime-optional-locals.d.ts +14 -0
- package/dist/frontend/lowering/runtime-optional-locals.js +41 -0
- package/dist/frontend/lowering/runtime-optional-locals.js.map +1 -0
- package/dist/frontend/lowering/runtime-optional-locals.test.d.ts +1 -0
- package/dist/frontend/lowering/runtime-optional-locals.test.js +85 -0
- package/dist/frontend/lowering/runtime-optional-locals.test.js.map +1 -0
- package/dist/frontend/lowering/sanitize-class-types.d.ts +8 -0
- package/dist/frontend/lowering/sanitize-class-types.js +79 -0
- package/dist/frontend/lowering/sanitize-class-types.js.map +1 -0
- package/dist/frontend/lowering/sanitize-class-types.test.d.ts +1 -0
- package/dist/frontend/lowering/sanitize-class-types.test.js +60 -0
- package/dist/frontend/lowering/sanitize-class-types.test.js.map +1 -0
- package/dist/frontend/lowering/static-this.d.ts +5 -1
- package/dist/frontend/lowering/static-this.js +28 -2
- package/dist/frontend/lowering/static-this.js.map +1 -1
- package/dist/frontend/lowering/string-index.d.ts +7 -0
- package/dist/frontend/lowering/string-index.js +39 -0
- package/dist/frontend/lowering/string-index.js.map +1 -0
- package/dist/frontend/lowering/surfaces.d.ts +9 -4
- package/dist/frontend/lowering/surfaces.js +98 -35
- package/dist/frontend/lowering/surfaces.js.map +1 -1
- package/dist/frontend/lowering/symbol-fields.d.ts +22 -0
- package/dist/frontend/lowering/symbol-fields.js +183 -0
- package/dist/frontend/lowering/symbol-fields.js.map +1 -0
- package/dist/frontend/lowering/symbol-methods.d.ts +7 -0
- package/dist/frontend/lowering/symbol-methods.js +30 -0
- package/dist/frontend/lowering/symbol-methods.js.map +1 -0
- package/dist/frontend/lowering/text-decoder-encoding.d.ts +10 -0
- package/dist/frontend/lowering/text-decoder-encoding.js +77 -0
- package/dist/frontend/lowering/text-decoder-encoding.js.map +1 -0
- package/dist/frontend/lowering/text-decoder-encoding.test.d.ts +1 -0
- package/dist/frontend/lowering/text-decoder-encoding.test.js +38 -0
- package/dist/frontend/lowering/text-decoder-encoding.test.js.map +1 -0
- package/dist/frontend/lowering/union-narrow.d.ts +9 -0
- package/dist/frontend/lowering/union-narrow.js +59 -0
- package/dist/frontend/lowering/union-narrow.js.map +1 -0
- package/dist/frontend/lowering/union-narrow.test.d.ts +1 -0
- package/dist/frontend/lowering/union-narrow.test.js +72 -0
- package/dist/frontend/lowering/union-narrow.test.js.map +1 -0
- package/dist/frontend/lowering/union-retag.d.ts +43 -0
- package/dist/frontend/lowering/union-retag.js +259 -0
- package/dist/frontend/lowering/union-retag.js.map +1 -0
- package/dist/frontend/lowering/union-retag.test.d.ts +1 -0
- package/dist/frontend/lowering/union-retag.test.js +405 -0
- package/dist/frontend/lowering/union-retag.test.js.map +1 -0
- package/dist/frontend/lowering/unregistered-class-types.d.ts +14 -0
- package/dist/frontend/lowering/unregistered-class-types.js +94 -0
- package/dist/frontend/lowering/unregistered-class-types.js.map +1 -0
- package/dist/frontend/lowering/unregistered-class-types.test.d.ts +1 -0
- package/dist/frontend/lowering/unregistered-class-types.test.js +65 -0
- package/dist/frontend/lowering/unregistered-class-types.test.js.map +1 -0
- package/dist/frontend/lowering/width-lift.d.ts +40 -0
- package/dist/frontend/lowering/width-lift.js +2 -0
- package/dist/frontend/lowering/width-lift.js.map +1 -0
- package/dist/frontend/module-syntax.d.ts +58 -0
- package/dist/frontend/module-syntax.js +123 -0
- package/dist/frontend/module-syntax.js.map +1 -0
- package/dist/frontend/module-syntax.test.d.ts +1 -0
- package/dist/frontend/module-syntax.test.js +78 -0
- package/dist/frontend/module-syntax.test.js.map +1 -0
- package/dist/frontend/npm-fetch-analysis.d.ts +17 -0
- package/dist/frontend/npm-fetch-analysis.js +100 -0
- package/dist/frontend/npm-fetch-analysis.js.map +1 -0
- package/dist/frontend/npm-fetch-analysis.test.d.ts +1 -0
- package/dist/frontend/npm-fetch-analysis.test.js +107 -0
- package/dist/frontend/npm-fetch-analysis.test.js.map +1 -0
- package/dist/frontend/npm-fetch-node.d.ts +2 -0
- package/dist/frontend/npm-fetch-node.js +12 -0
- package/dist/frontend/npm-fetch-node.js.map +1 -0
- package/dist/frontend/npm-fetch-syntax.d.ts +6 -0
- package/dist/frontend/npm-fetch-syntax.js +207 -0
- package/dist/frontend/npm-fetch-syntax.js.map +1 -0
- package/dist/frontend/npm-node.d.ts +10 -0
- package/dist/frontend/npm-node.js +14 -0
- package/dist/frontend/npm-node.js.map +1 -0
- package/dist/frontend/npm-static-bundled-cjs.d.ts +8 -0
- package/dist/frontend/npm-static-bundled-cjs.js +236 -0
- package/dist/frontend/npm-static-bundled-cjs.js.map +1 -0
- package/dist/frontend/npm-static-bundled-cjs.test.d.ts +1 -0
- package/dist/frontend/npm-static-bundled-cjs.test.js +69 -0
- package/dist/frontend/npm-static-bundled-cjs.test.js.map +1 -0
- package/dist/frontend/npm-static-declaration-syntax.d.ts +57 -0
- package/dist/frontend/npm-static-declaration-syntax.js +944 -0
- package/dist/frontend/npm-static-declaration-syntax.js.map +1 -0
- package/dist/frontend/npm-static-declarations.d.ts +11 -39
- package/dist/frontend/npm-static-declarations.js +22 -561
- package/dist/frontend/npm-static-declarations.js.map +1 -1
- package/dist/frontend/npm-static-declarations.test.js +245 -6
- package/dist/frontend/npm-static-declarations.test.js.map +1 -1
- package/dist/frontend/npm-static-prune.d.ts +1 -1
- package/dist/frontend/npm-static-prune.js +5 -4
- package/dist/frontend/npm-static-prune.js.map +1 -1
- package/dist/frontend/npm-static-prune.test.js +1 -1
- package/dist/frontend/npm-static-prune.test.js.map +1 -1
- package/dist/frontend/npm-static-rewrite-host.d.ts +4 -0
- package/dist/frontend/npm-static-rewrite-host.js +179 -0
- package/dist/frontend/npm-static-rewrite-host.js.map +1 -0
- package/dist/frontend/npm-static-rewrite-syntax.d.ts +16 -0
- package/dist/frontend/npm-static-rewrite-syntax.js +905 -0
- package/dist/frontend/npm-static-rewrite-syntax.js.map +1 -0
- package/dist/frontend/npm-static-rewrite.d.ts +0 -5
- package/dist/frontend/npm-static-rewrite.js +2 -1079
- package/dist/frontend/npm-static-rewrite.js.map +1 -1
- package/dist/frontend/npm-static-subtyping.d.ts +5 -0
- package/dist/frontend/npm-static-subtyping.js +55 -0
- package/dist/frontend/npm-static-subtyping.js.map +1 -0
- package/dist/frontend/npm-static-subtyping.test.d.ts +1 -0
- package/dist/frontend/npm-static-subtyping.test.js +58 -0
- package/dist/frontend/npm-static-subtyping.test.js.map +1 -0
- package/dist/frontend/npm-static.d.ts +7 -15
- package/dist/frontend/npm-static.js +19 -28
- package/dist/frontend/npm-static.js.map +1 -1
- package/dist/frontend/npm.d.ts +8 -69
- package/dist/frontend/npm.js +21 -361
- package/dist/frontend/npm.js.map +1 -1
- package/dist/frontend/npm.test.js +2 -2
- package/dist/frontend/npm.test.js.map +1 -1
- package/dist/frontend/pipeline-native.d.ts +6 -0
- package/dist/frontend/pipeline-native.js +32 -0
- package/dist/frontend/pipeline-native.js.map +1 -0
- package/dist/frontend/pipeline.d.ts +54 -0
- package/dist/frontend/pipeline.js +382 -0
- package/dist/frontend/pipeline.js.map +1 -0
- package/dist/frontend/pipeline.test.d.ts +1 -0
- package/dist/frontend/pipeline.test.js +77 -0
- package/dist/frontend/pipeline.test.js.map +1 -0
- package/dist/frontend/program-analysis.d.ts +10 -0
- package/dist/frontend/program-analysis.js +14 -0
- package/dist/frontend/program-analysis.js.map +1 -0
- package/dist/frontend/program-node.d.ts +9 -0
- package/dist/frontend/program-node.js +48 -0
- package/dist/frontend/program-node.js.map +1 -0
- package/dist/frontend/program.d.ts +43 -19
- package/dist/frontend/program.js +556 -197
- package/dist/frontend/program.js.map +1 -1
- package/dist/frontend/program.test.d.ts +1 -0
- package/dist/frontend/program.test.js +202 -0
- package/dist/frontend/program.test.js.map +1 -0
- package/dist/frontend/provenance-core.d.ts +9 -0
- package/dist/frontend/provenance-core.js +435 -0
- package/dist/frontend/provenance-core.js.map +1 -0
- package/dist/frontend/provenance.d.ts +0 -4
- package/dist/frontend/provenance.js +4 -459
- package/dist/frontend/provenance.js.map +1 -1
- package/dist/frontend/resolve.d.ts +3 -1
- package/dist/frontend/resolve.js +6 -3
- package/dist/frontend/resolve.js.map +1 -1
- package/dist/frontend/runtime-resolve.d.ts +4 -3
- package/dist/frontend/runtime-resolve.js +22 -18
- package/dist/frontend/runtime-resolve.js.map +1 -1
- package/dist/frontend/runtime-resolve.test.js +53 -0
- package/dist/frontend/runtime-resolve.test.js.map +1 -1
- package/dist/frontend/services-node.d.ts +5 -0
- package/dist/frontend/services-node.js +19 -0
- package/dist/frontend/services-node.js.map +1 -0
- package/dist/frontend/services-node.test.d.ts +1 -0
- package/dist/frontend/services-node.test.js +70 -0
- package/dist/frontend/services-node.test.js.map +1 -0
- package/dist/frontend/services.d.ts +44 -0
- package/dist/frontend/services.js +90 -0
- package/dist/frontend/services.js.map +1 -0
- package/dist/frontend/services.test.d.ts +1 -0
- package/dist/frontend/services.test.js +174 -0
- package/dist/frontend/services.test.js.map +1 -0
- package/dist/frontend/ts7/adapter.d.ts +3 -3
- package/dist/frontend/ts7/adapter.js +14 -31
- package/dist/frontend/ts7/adapter.js.map +1 -1
- package/dist/frontend/ts7/api.d.ts +16 -0
- package/dist/frontend/ts7/api.js +24 -0
- package/dist/frontend/ts7/api.js.map +1 -0
- package/dist/frontend/ts7/ast-bytes.d.ts +31 -0
- package/dist/frontend/ts7/ast-bytes.js +137 -0
- package/dist/frontend/ts7/ast-bytes.js.map +1 -0
- package/dist/frontend/ts7/ast-bytes.test.d.ts +1 -0
- package/dist/frontend/ts7/ast-bytes.test.js +73 -0
- package/dist/frontend/ts7/ast-bytes.test.js.map +1 -0
- package/dist/frontend/ts7/ast-guards.generated.d.ts +353 -0
- package/dist/frontend/ts7/ast-guards.generated.js +1951 -0
- package/dist/frontend/ts7/ast-guards.generated.js.map +1 -0
- package/dist/frontend/ts7/ast-guards.test.d.ts +1 -0
- package/dist/frontend/ts7/ast-guards.test.js +43 -0
- package/dist/frontend/ts7/ast-guards.test.js.map +1 -0
- package/dist/frontend/ts7/ast-node.d.ts +194 -0
- package/dist/frontend/ts7/ast-node.js +448 -0
- package/dist/frontend/ts7/ast-node.js.map +1 -0
- package/dist/frontend/ts7/ast-node.test.d.ts +1 -0
- package/dist/frontend/ts7/ast-node.test.js +266 -0
- package/dist/frontend/ts7/ast-node.test.js.map +1 -0
- package/dist/frontend/ts7/ast-schema.generated.d.ts +503 -0
- package/dist/frontend/ts7/ast-schema.generated.js +1446 -0
- package/dist/frontend/ts7/ast-schema.generated.js.map +1 -0
- package/dist/frontend/ts7/ast-text.d.ts +6 -0
- package/dist/frontend/ts7/ast-text.js +124 -0
- package/dist/frontend/ts7/ast-text.js.map +1 -0
- package/dist/frontend/ts7/ast-text.test.d.ts +1 -0
- package/dist/frontend/ts7/ast-text.test.js +38 -0
- package/dist/frontend/ts7/ast-text.test.js.map +1 -0
- package/dist/frontend/ts7/ast-tokens.generated.d.ts +2 -0
- package/dist/frontend/ts7/ast-tokens.generated.js +155 -0
- package/dist/frontend/ts7/ast-tokens.generated.js.map +1 -0
- package/dist/frontend/ts7/ast-types.d.ts +42 -0
- package/dist/frontend/ts7/ast-types.generated.d.ts +1615 -0
- package/dist/frontend/ts7/ast-types.generated.js +5 -0
- package/dist/frontend/ts7/ast-types.generated.js.map +1 -0
- package/dist/frontend/ts7/ast-types.js +3 -0
- package/dist/frontend/ts7/ast-types.js.map +1 -0
- package/dist/frontend/ts7/ast-wire.d.ts +58 -0
- package/dist/frontend/ts7/ast-wire.js +267 -0
- package/dist/frontend/ts7/ast-wire.js.map +1 -0
- package/dist/frontend/ts7/ast-wire.test.d.ts +1 -0
- package/dist/frontend/ts7/ast-wire.test.js +151 -0
- package/dist/frontend/ts7/ast-wire.test.js.map +1 -0
- package/dist/frontend/ts7/ast.d.ts +14 -14
- package/dist/frontend/ts7/ast.js +21 -15
- package/dist/frontend/ts7/ast.js.map +1 -1
- package/dist/frontend/ts7/census-check.js +1 -0
- package/dist/frontend/ts7/census-check.js.map +1 -1
- package/dist/frontend/ts7/checker-cache.d.ts +48 -0
- package/dist/frontend/ts7/checker-cache.js +92 -0
- package/dist/frontend/ts7/checker-cache.js.map +1 -0
- package/dist/frontend/ts7/checker.d.ts +19 -46
- package/dist/frontend/ts7/checker.js +231 -161
- package/dist/frontend/ts7/checker.js.map +1 -1
- package/dist/frontend/ts7/checker.test.d.ts +1 -0
- package/dist/frontend/ts7/checker.test.js +335 -0
- package/dist/frontend/ts7/checker.test.js.map +1 -0
- package/dist/frontend/ts7/enums.d.ts +4 -27
- package/dist/frontend/ts7/enums.generated.d.ts +878 -0
- package/dist/frontend/ts7/enums.generated.js +1313 -0
- package/dist/frontend/ts7/enums.generated.js.map +1 -0
- package/dist/frontend/ts7/enums.js +4 -45
- package/dist/frontend/ts7/enums.js.map +1 -1
- package/dist/frontend/ts7/enums.test.d.ts +1 -0
- package/dist/frontend/ts7/enums.test.js +48 -0
- package/dist/frontend/ts7/enums.test.js.map +1 -0
- package/dist/frontend/ts7/native-api.d.ts +10 -0
- package/dist/frontend/ts7/native-api.js +10 -0
- package/dist/frontend/ts7/native-api.js.map +1 -0
- package/dist/frontend/ts7/native-host.d.ts +2 -0
- package/dist/frontend/ts7/native-host.js +11 -0
- package/dist/frontend/ts7/native-host.js.map +1 -0
- package/dist/frontend/ts7/native-process.d.ts +14 -0
- package/dist/frontend/ts7/native-process.js +62 -0
- package/dist/frontend/ts7/native-process.js.map +1 -0
- package/dist/frontend/ts7/program-adapter.d.ts +10 -98
- package/dist/frontend/ts7/program-adapter.js +16 -310
- package/dist/frontend/ts7/program-adapter.js.map +1 -1
- package/dist/frontend/ts7/program-host.d.ts +115 -0
- package/dist/frontend/ts7/program-host.js +347 -0
- package/dist/frontend/ts7/program-host.js.map +1 -0
- package/dist/frontend/ts7/program-host.test.d.ts +1 -0
- package/dist/frontend/ts7/program-host.test.js +207 -0
- package/dist/frontend/ts7/program-host.test.js.map +1 -0
- package/dist/frontend/ts7/rpc-api.d.ts +12 -0
- package/dist/frontend/ts7/rpc-api.js +23 -0
- package/dist/frontend/ts7/rpc-api.js.map +1 -0
- package/dist/frontend/ts7/rpc-client.d.ts +27 -0
- package/dist/frontend/ts7/rpc-client.js +102 -0
- package/dist/frontend/ts7/rpc-client.js.map +1 -0
- package/dist/frontend/ts7/rpc-client.test.d.ts +1 -0
- package/dist/frontend/ts7/rpc-client.test.js +133 -0
- package/dist/frontend/ts7/rpc-client.test.js.map +1 -0
- package/dist/frontend/ts7/rpc-filesystem.d.ts +16 -0
- package/dist/frontend/ts7/rpc-filesystem.js +42 -0
- package/dist/frontend/ts7/rpc-filesystem.js.map +1 -0
- package/dist/frontend/ts7/rpc-filesystem.test.d.ts +1 -0
- package/dist/frontend/ts7/rpc-filesystem.test.js +89 -0
- package/dist/frontend/ts7/rpc-filesystem.test.js.map +1 -0
- package/dist/frontend/ts7/rpc-process.d.ts +2 -0
- package/dist/frontend/ts7/rpc-process.js +148 -0
- package/dist/frontend/ts7/rpc-process.js.map +1 -0
- package/dist/frontend/ts7/rpc-process.test.d.ts +1 -0
- package/dist/frontend/ts7/rpc-process.test.js +29 -0
- package/dist/frontend/ts7/rpc-process.test.js.map +1 -0
- package/dist/frontend/ts7/rpc-wire.d.ts +46 -0
- package/dist/frontend/ts7/rpc-wire.js +195 -0
- package/dist/frontend/ts7/rpc-wire.js.map +1 -0
- package/dist/frontend/ts7/rpc-wire.test.d.ts +1 -0
- package/dist/frontend/ts7/rpc-wire.test.js +168 -0
- package/dist/frontend/ts7/rpc-wire.test.js.map +1 -0
- package/dist/frontend/ts7/semantic-checker.d.ts +137 -0
- package/dist/frontend/ts7/semantic-checker.js +221 -0
- package/dist/frontend/ts7/semantic-checker.js.map +1 -0
- package/dist/frontend/ts7/semantic-checker.test.d.ts +1 -0
- package/dist/frontend/ts7/semantic-checker.test.js +119 -0
- package/dist/frontend/ts7/semantic-checker.test.js.map +1 -0
- package/dist/frontend/ts7/semantic-json.d.ts +5 -0
- package/dist/frontend/ts7/semantic-json.js +52 -0
- package/dist/frontend/ts7/semantic-json.js.map +1 -0
- package/dist/frontend/ts7/semantic-json.test.d.ts +1 -0
- package/dist/frontend/ts7/semantic-json.test.js +42 -0
- package/dist/frontend/ts7/semantic-json.test.js.map +1 -0
- package/dist/frontend/ts7/semantic-model.d.ts +222 -0
- package/dist/frontend/ts7/semantic-model.js +423 -0
- package/dist/frontend/ts7/semantic-model.js.map +1 -0
- package/dist/frontend/ts7/semantic-model.test.d.ts +1 -0
- package/dist/frontend/ts7/semantic-model.test.js +322 -0
- package/dist/frontend/ts7/semantic-model.test.js.map +1 -0
- package/dist/frontend/ts7/semantic-schema.generated.d.ts +283 -0
- package/dist/frontend/ts7/semantic-schema.generated.js +222 -0
- package/dist/frontend/ts7/semantic-schema.generated.js.map +1 -0
- package/dist/frontend/ts7/semantic-types.d.ts +5 -0
- package/dist/frontend/ts7/semantic-types.js +2 -0
- package/dist/frontend/ts7/semantic-types.js.map +1 -0
- package/dist/frontend/ts7/session-cache.d.ts +17 -0
- package/dist/frontend/ts7/session-cache.js +97 -0
- package/dist/frontend/ts7/session-cache.js.map +1 -0
- package/dist/frontend/ts7/session-cache.test.d.ts +1 -0
- package/dist/frontend/ts7/session-cache.test.js +87 -0
- package/dist/frontend/ts7/session-cache.test.js.map +1 -0
- package/dist/frontend/ts7/session-path.d.ts +18 -0
- package/dist/frontend/ts7/session-path.js +203 -0
- package/dist/frontend/ts7/session-path.js.map +1 -0
- package/dist/frontend/ts7/session-path.test.d.ts +1 -0
- package/dist/frontend/ts7/session-path.test.js +34 -0
- package/dist/frontend/ts7/session-path.test.js.map +1 -0
- package/dist/frontend/ts7/session-schema.generated.d.ts +167 -0
- package/dist/frontend/ts7/session-schema.generated.js +5 -0
- package/dist/frontend/ts7/session-schema.generated.js.map +1 -0
- package/dist/frontend/ts7/session-timing.d.ts +50 -0
- package/dist/frontend/ts7/session-timing.js +61 -0
- package/dist/frontend/ts7/session-timing.js.map +1 -0
- package/dist/frontend/ts7/session-timing.test.d.ts +1 -0
- package/dist/frontend/ts7/session-timing.test.js +52 -0
- package/dist/frontend/ts7/session-timing.test.js.map +1 -0
- package/dist/frontend/ts7/session.d.ts +105 -0
- package/dist/frontend/ts7/session.js +233 -0
- package/dist/frontend/ts7/session.js.map +1 -0
- package/dist/frontend/ts7/session.test.d.ts +1 -0
- package/dist/frontend/ts7/session.test.js +215 -0
- package/dist/frontend/ts7/session.test.js.map +1 -0
- package/dist/frontend/ts7/source-parser-node.d.ts +6 -0
- package/dist/frontend/ts7/source-parser-node.js +15 -0
- package/dist/frontend/ts7/source-parser-node.js.map +1 -0
- package/dist/frontend/ts7/source-parser.d.ts +25 -0
- package/dist/frontend/ts7/source-parser.js +96 -0
- package/dist/frontend/ts7/source-parser.js.map +1 -0
- package/dist/frontend/ts7/source-parser.test.d.ts +1 -0
- package/dist/frontend/ts7/source-parser.test.js +151 -0
- package/dist/frontend/ts7/source-parser.test.js.map +1 -0
- package/dist/frontend/ts7/syntax.d.ts +3 -0
- package/dist/frontend/ts7/syntax.js +4 -0
- package/dist/frontend/ts7/syntax.js.map +1 -0
- package/dist/frontend/tsc-codes.js +3 -0
- package/dist/frontend/tsc-codes.js.map +1 -1
- package/dist/frontend/type-mapper.d.ts +29 -19
- package/dist/frontend/type-mapper.js +713 -306
- package/dist/frontend/type-mapper.js.map +1 -1
- package/dist/frontend/type-mapper.test.d.ts +1 -0
- package/dist/frontend/type-mapper.test.js +207 -0
- package/dist/frontend/type-mapper.test.js.map +1 -0
- package/dist/frontend/union-discriminants.d.ts +16 -0
- package/dist/frontend/union-discriminants.js +98 -0
- package/dist/frontend/union-discriminants.js.map +1 -0
- package/dist/frontend/union-discriminants.test.d.ts +1 -0
- package/dist/frontend/union-discriminants.test.js +182 -0
- package/dist/frontend/union-discriminants.test.js.map +1 -0
- package/dist/index.d.ts +7 -197
- package/dist/index.js +369 -1567
- package/dist/index.js.map +1 -1
- package/dist/ir/analysis.d.ts +16 -4
- package/dist/ir/analysis.js +25 -6
- package/dist/ir/analysis.js.map +1 -1
- package/dist/ir/analysis.test.js +15 -2
- package/dist/ir/analysis.test.js.map +1 -1
- package/dist/ir/constant-tables.js +67 -49
- package/dist/ir/constant-tables.js.map +1 -1
- package/dist/ir/constant-tables.test.js +1 -1
- package/dist/ir/control-flow.d.ts +6 -0
- package/dist/ir/control-flow.js +142 -0
- package/dist/ir/control-flow.js.map +1 -0
- package/dist/ir/control-flow.test.d.ts +1 -0
- package/dist/ir/control-flow.test.js +113 -0
- package/dist/ir/control-flow.test.js.map +1 -0
- package/dist/ir/integer-loops.js +7 -18
- package/dist/ir/integer-loops.js.map +1 -1
- package/dist/ir/integer-ranges.js +53 -27
- package/dist/ir/integer-ranges.js.map +1 -1
- package/dist/ir/ir.d.ts +309 -164
- package/dist/ir/ir.js +588 -879
- package/dist/ir/ir.js.map +1 -1
- package/dist/ir/ir.test.js +127 -1
- package/dist/ir/ir.test.js.map +1 -1
- package/dist/ir/scalar-records.js +165 -134
- package/dist/ir/scalar-records.js.map +1 -1
- package/dist/ir/scalar-records.test.js +1 -1
- package/dist/ir/serialize.d.ts +4 -2
- package/dist/ir/serialize.js +19 -12
- package/dist/ir/serialize.js.map +1 -1
- package/dist/ir/serialize.test.d.ts +1 -0
- package/dist/ir/serialize.test.js +53 -0
- package/dist/ir/serialize.test.js.map +1 -0
- package/dist/ir/traverse.d.ts +41 -0
- package/dist/ir/traverse.js +660 -0
- package/dist/ir/traverse.js.map +1 -0
- package/dist/ir/traverse.test.d.ts +1 -0
- package/dist/ir/traverse.test.js +242 -0
- package/dist/ir/traverse.test.js.map +1 -0
- package/dist/ir/validate.js +1956 -1496
- package/dist/ir/validate.js.map +1 -1
- package/dist/ir/validate.test.js +476 -2
- package/dist/ir/validate.test.js.map +1 -1
- package/dist/library/cache-primitives.d.ts +2 -3
- package/dist/library/cache-primitives.js +4 -6
- package/dist/library/cache-primitives.js.map +1 -1
- package/dist/library/compiler-self-identity.d.ts +1 -1
- package/dist/library/compiler-self-identity.js +31 -15
- package/dist/library/compiler-self-identity.js.map +1 -1
- package/dist/library/compiler-self-identity.test.d.ts +1 -0
- package/dist/library/compiler-self-identity.test.js +93 -0
- package/dist/library/compiler-self-identity.test.js.map +1 -0
- package/dist/library/fence-eval.js +2 -2
- package/dist/library/fence-eval.js.map +1 -1
- package/dist/library/int-infer.js +7 -6
- package/dist/library/int-infer.js.map +1 -1
- package/dist/library/int-infer.test.js +4 -4
- package/dist/library/library-cache.d.ts +3 -2
- package/dist/library/library-cache.js +5 -17
- package/dist/library/library-cache.js.map +1 -1
- package/dist/library/library-cache.test.js +54 -41
- package/dist/library/library-cache.test.js.map +1 -1
- package/dist/library/library-profile.d.ts +3 -1
- package/dist/library/library-profile.js +15 -6
- package/dist/library/library-profile.js.map +1 -1
- package/dist/library/prepare.d.ts +15 -0
- package/dist/library/prepare.js +584 -0
- package/dist/library/prepare.js.map +1 -0
- package/dist/library/semantic-source.d.ts +1 -1
- package/dist/library/semantic-source.js +8 -6
- package/dist/library/semantic-source.js.map +1 -1
- package/dist/library/sidecar-validate.js +4 -4
- package/dist/library/sidecar-validate.js.map +1 -1
- package/dist/library/sidecar.d.ts +2 -0
- package/dist/library/sidecar.js +10 -3
- package/dist/library/sidecar.js.map +1 -1
- package/dist/native/cache.d.ts +11 -0
- package/dist/native/cache.js +113 -0
- package/dist/native/cache.js.map +1 -0
- package/dist/native/cache.test.d.ts +1 -0
- package/dist/native/cache.test.js +66 -0
- package/dist/native/cache.test.js.map +1 -0
- package/dist/native/cli.d.ts +1 -0
- package/dist/native/cli.js +112 -0
- package/dist/native/cli.js.map +1 -0
- package/dist/native/compiler.d.ts +15 -0
- package/dist/native/compiler.js +336 -0
- package/dist/native/compiler.js.map +1 -0
- package/dist/native/compiler.test.d.ts +1 -0
- package/dist/native/compiler.test.js +174 -0
- package/dist/native/compiler.test.js.map +1 -0
- package/dist/native/comptime.d.ts +5 -0
- package/dist/native/comptime.js +73 -0
- package/dist/native/comptime.js.map +1 -0
- package/dist/native/comptime.test.d.ts +1 -0
- package/dist/native/comptime.test.js +65 -0
- package/dist/native/comptime.test.js.map +1 -0
- package/dist/native/executable-cache.d.ts +26 -0
- package/dist/native/executable-cache.js +228 -0
- package/dist/native/executable-cache.js.map +1 -0
- package/dist/native/executable-cache.test.d.ts +1 -0
- package/dist/native/executable-cache.test.js +151 -0
- package/dist/native/executable-cache.test.js.map +1 -0
- package/dist/native/file-identity.d.ts +4 -0
- package/dist/native/file-identity.js +14 -0
- package/dist/native/file-identity.js.map +1 -0
- package/dist/native/file-identity.test.d.ts +1 -0
- package/dist/native/file-identity.test.js +43 -0
- package/dist/native/file-identity.test.js.map +1 -0
- package/dist/native/library.d.ts +22 -0
- package/dist/native/library.js +83 -0
- package/dist/native/library.js.map +1 -0
- package/dist/native/prepare.d.ts +21 -0
- package/dist/native/prepare.js +174 -0
- package/dist/native/prepare.js.map +1 -0
- package/dist/native/prepare.test.d.ts +1 -0
- package/dist/native/prepare.test.js +70 -0
- package/dist/native/prepare.test.js.map +1 -0
- package/dist/native/sanitizer.d.ts +11 -0
- package/dist/native/sanitizer.js +135 -0
- package/dist/native/sanitizer.js.map +1 -0
- package/dist/native/sanitizer.test.d.ts +1 -0
- package/dist/native/sanitizer.test.js +58 -0
- package/dist/native/sanitizer.test.js.map +1 -0
- package/dist/native/task.d.ts +4 -0
- package/dist/native/task.js +16 -0
- package/dist/native/task.js.map +1 -0
- package/dist/native/task.test.d.ts +1 -0
- package/dist/native/task.test.js +17 -0
- package/dist/native/task.test.js.map +1 -0
- package/dist/native/toolchain.d.ts +45 -0
- package/dist/native/toolchain.js +74 -0
- package/dist/native/toolchain.js.map +1 -0
- package/dist/native/toolchain.test.d.ts +1 -0
- package/dist/native/toolchain.test.js +33 -0
- package/dist/native/toolchain.test.js.map +1 -0
- package/dist/startup-cache.d.ts +1 -1
- package/dist/startup-cache.js +2 -5
- package/dist/startup-cache.js.map +1 -1
- package/dist/timing.d.ts +4 -0
- package/dist/timing.js +19 -0
- package/dist/timing.js.map +1 -0
- package/dist/wasi-paths.js +10 -7
- package/dist/wasi-paths.js.map +1 -1
- package/package.json +28 -19
- package/surface-manifest.json +171 -18
- package/dist/backend/c/async.d.ts +0 -171
- package/dist/backend/c/async.js +0 -1317
- package/dist/backend/c/async.js.map +0 -1
- package/dist/backend/c/c-emitter.d.ts +0 -531
- package/dist/backend/c/c-emitter.js +0 -1917
- package/dist/backend/c/c-emitter.js.map +0 -1
- package/dist/backend/c/exprs.d.ts +0 -15
- package/dist/backend/c/exprs.js +0 -8803
- package/dist/backend/c/exprs.js.map +0 -1
- package/dist/backend/c/island.d.ts +0 -45
- package/dist/backend/c/island.js +0 -281
- package/dist/backend/c/island.js.map +0 -1
- package/dist/backend/c/may-throw.js.map +0 -1
- package/dist/backend/c/shapes.d.ts +0 -142
- package/dist/backend/c/shapes.js +0 -603
- package/dist/backend/c/shapes.js.map +0 -1
- package/dist/backend/c/stmts.d.ts +0 -76
- package/dist/backend/c/stmts.js +0 -1084
- package/dist/backend/c/stmts.js.map +0 -1
- package/dist/backend/c/types.d.ts +0 -71
- package/dist/backend/c/types.js +0 -459
- package/dist/backend/c/types.js.map +0 -1
- package/dist/backend/c/types.test.js +0 -32
- package/dist/backend/c/types.test.js.map +0 -1
- package/dist/backend/c/walkers.d.ts +0 -134
- package/dist/backend/c/walkers.js +0 -1773
- package/dist/backend/c/walkers.js.map +0 -1
- /package/dist/backend/{c/types.test.d.ts → cycle-analysis.test.d.ts} +0 -0
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { InternalCompilerError } from "../../errors.js";
|
|
2
|
+
import { literalValues } from "../literal-values.js";
|
|
3
|
+
import { literalUnionArm } from "../union-discriminants.js";
|
|
2
4
|
/* Expression lowering: the expression dispatch (lowerExpr), literals
|
|
3
5
|
* (array/object/regex/template), operators (binary incl. compound targets,
|
|
4
6
|
* unary, instanceof, caught-typeof tests), union narrowing and unit
|
|
@@ -6,16 +8,19 @@ import { InternalCompilerError } from "../../errors.js";
|
|
|
6
8
|
* ToBoolean/ToString coercion helpers, and field/element reads and writes
|
|
7
9
|
* (FieldTarget). */
|
|
8
10
|
import * as ts from "../ts7/adapter.js";
|
|
9
|
-
import { dirname
|
|
11
|
+
import { dirname } from "node:path";
|
|
12
|
+
import * as posix from "node:path/posix";
|
|
10
13
|
import { pathToFileURL } from "node:url";
|
|
14
|
+
import { OBJECT_CALLABLE_VALUES } from "./surfaces.js";
|
|
11
15
|
import { wasiGuestPath } from "../../wasi-paths.js";
|
|
12
|
-
import { BIGINT_T, BOOL, CAUGHT, DYN, DYN_HANDLE_KINDS, F64, JSVAL, NULL_T, REF_TRUTHY_KINDS, REGEX, RUNTIME_ERROR_CLASSES, SEARCH_PARAMS_T, STRING, UNDEFINED_T, VOID, arrayOf, canAdaptDynFuncTo, canBoxFuncIntoDyn, funcOf, isDynTypedRefType,
|
|
16
|
+
import { BIGINT_T, BYTES_ELEMENT_NAME, BOOL, CAUGHT, DYN, DYN_HANDLE_KINDS, F64, JSVAL, NULL_T, REF_TRUTHY_KINDS, REGEX, RUNTIME_ERROR_CLASSES, SEARCH_PARAMS_T, STRING, UNDEFINED_T, VOID, arrayOf, canAdaptDynFuncTo, canDynCheckTo, canBoxFuncIntoDyn, funcOf, isDynTypedRefType, isSupportedArrayElem, isUnitType, jsOpResultKind, shapeHasAccessorSlots, typeEquals, typeKey, unionContainerArmsOk } from "../../ir/ir.js";
|
|
13
17
|
import { cjsClassExprWholeExportOf, cjsExportAssignmentOf, cjsExportDiscardReason, isCjsExportTableLiteral, isCjsJsFile, isJsSourceFile, isModuleExportsAccess, isNodeEsmFile, locOf } from "../program.js";
|
|
14
18
|
import { ARRAY_METHODS, builtinConstLit, builtinFenceHintOf, builtinModuleConstOf, builtinModulesArrayLit, builtinModuleFnOf, COMPOUND_ASSIGN_OPS, ISLAND_SURFACE, isChildSurfaceMember, MAP_METHODS, NARROW_FIRST, SET_METHODS, STRING_INDEX_METHODS, STR_METHODS, UNSUPPORTED_EXPR, sideEffectFreeOptionValue, stdlibGlobalNameOf } from "./surfaces.js";
|
|
15
|
-
import { blockedBindingUseDiag, requiresDynamicPackageDiag, unsupportedDiag } from "../../diagnostics/diagnostic.js";
|
|
19
|
+
import { UNSUPPORTED, blockedBindingUseDiag, requiresDynamicPackageDiag, unsupportedDiag } from "../../diagnostics/diagnostic.js";
|
|
16
20
|
import { PoisonError, dynUndefinedExpr, jsFuncNameOf, neverTaintedJsType, nodeThrowExpr, own } from "./lowerer.js";
|
|
17
|
-
import { lowerNpmStaticSafeIndexRead, lowerSafeIndexRead, strCharsCall, tryLowerNumericIndexRead } from "./lower-containers.js";
|
|
18
|
-
import { arrayValueRead, arrayValueStore } from "./array-values.js";
|
|
21
|
+
import { lowerCollectionSpread, lowerNpmStaticSafeIndexRead, lowerSafeIndexRead, strCharsCall, tryLowerNumericIndexRead } from "./lower-containers.js";
|
|
22
|
+
import { arrayValueRead, arrayValueStore, arrayValueType } from "./array-values.js";
|
|
23
|
+
import { lowerOptionalStringIndex } from "./string-index.js";
|
|
19
24
|
import { tryLowerIndexedComparison } from "./indexed-comparison.js";
|
|
20
25
|
import { npmStaticPackageOfPath } from "../npm-static.js";
|
|
21
26
|
import { unsupportedModuleFeatureOf } from "../builtin-modules.js";
|
|
@@ -23,20 +28,32 @@ import { fenceEnumObjectValue, lowerEnumAccess } from "./lower-enums.js";
|
|
|
23
28
|
import { ambientNsRootOf, ambientUndefReadType, ambientUndefVarRootOf, ambientUndefinedFnSymbolOf, contextualUndefReadType, fenceEarlyAliasUse, fenceEarlyNsMemberRef, lowerNsIdentifierValue, nsMemberIdentOf, nsUndefRead, nsWritableTarget } from "./lower-namespaces.js";
|
|
24
29
|
import { expandoMemberRead, expandoWritableTarget } from "./lower-expando.js";
|
|
25
30
|
import { lowerSocketInstanceOf, lowerTlsRootCertificates } from "./lower-server.js";
|
|
26
|
-
import { findGenericMethodOn, lowerStaticFieldRead } from "./lower-classes.js";
|
|
31
|
+
import { findGenericMethodOn, lowerStaticFieldRead, staticFieldWriteTarget, storedClassValueType } from "./lower-classes.js";
|
|
27
32
|
import { bindingNeverReassigned, funcTypeFromParamShapes, implicitMonoFile, lowerTaggedTemplate, nullishGenericBindingUnitOf, objLitGenericFnInfoOf, objLitGenericFnNodeOf, requireObjLitGenericReceiver } from "./lower-calls.js";
|
|
28
33
|
import { mixinFnOfCallee } from "./lower-mixins.js";
|
|
29
|
-
import { isConstAssertionTypeNode, isGenericCallableMemberType, isParseArgsDynTypeName, underConstAssertion, unitOnlyUnion } from "../type-mapper.js";
|
|
34
|
+
import { isConstAssertionTypeNode, isGenericCallableMemberType, isParseArgsDynTypeName, underConstAssertion, unitOnlyUnion, withUnitArm } from "../type-mapper.js";
|
|
30
35
|
import { lowerYield } from "./lower-generators.js";
|
|
36
|
+
import { errorToStringCall } from "./error-methods.js";
|
|
31
37
|
import { lowerStreamProperty, lowerStreamStateProperty, streamSidesOf } from "./lower-stream.js";
|
|
32
38
|
import { countedFor, numLit, varRef } from "../../ir/build.js";
|
|
39
|
+
import { unionWideningTags } from "../../ir/analysis.js";
|
|
33
40
|
import { isSafeToDiscard, isSafeToMoveConditionEarlier, isSafeToRepeat } from "./expressions/evaluation-safety.js";
|
|
41
|
+
import { globalSymbolKey } from "./expressions/global-symbols.js";
|
|
42
|
+
import { lowerShortCircuitAssignment } from "./expressions/nullish-assignment.js";
|
|
34
43
|
import { hasOptionalChainGuard, isOptionalChainTail, isRequireMainFilename } from "./expressions/optional-chains.js";
|
|
35
44
|
import { conditionalSpreadOf, foldedStringKeyOf } from "./expressions/object-literals.js";
|
|
36
45
|
import { tryLowerExpression } from "./expressions/try-lower-expression.js";
|
|
37
46
|
import { fenceNodeModuleMutation, isNodeModuleValue, lowerNodeModuleIdentifier, lowerNodeModuleProperty, lowerRequireCacheElement, lowerRequireCacheHas, lowerRequireMainProperty } from "./lower-node-module.js";
|
|
38
47
|
import { lowerAbstractEquality } from "./abstract-equality.js";
|
|
39
48
|
import { coerceStringSearchValue, defaultAfterUndefined, lowerStaticallyUndefinedArgument } from "./optional-arguments.js";
|
|
49
|
+
import { recordTextCodecClass } from "../../ir/ir.js";
|
|
50
|
+
import { classSymbolKeyOf } from "./symbol-fields.js";
|
|
51
|
+
import { lowerClassMethodValue } from "./class-method-values.js";
|
|
52
|
+
import { classInstanceOf } from "./class-dynamic-dispatch.js";
|
|
53
|
+
import { lowerGlobalValue } from "./lower-global-value.js";
|
|
54
|
+
import { lowerClassPrototypeData } from "./class-prototypes.js";
|
|
55
|
+
import { lowerArrayIsArrayValue } from "./lower-builtin-values.js";
|
|
56
|
+
import { lowerPerfHooksTypeof } from "./lower-builtins.js";
|
|
40
57
|
/** A template piece's RAW text (String.raw's contract: escapes stay
|
|
41
58
|
* characters). 7's client AST ships no rawText at runtime (the typing
|
|
42
59
|
* declares it; the serialized node data omits it), so the raw span comes
|
|
@@ -313,6 +330,12 @@ function lowerExprInner(lowerer, expr) {
|
|
|
313
330
|
return !sf.isDeclarationFile && !sf.fileName.includes("/node_modules/");
|
|
314
331
|
});
|
|
315
332
|
if (projectDeclared) {
|
|
333
|
+
// A spread already stored in the island must copy there too.
|
|
334
|
+
// Later overrides can make the literal's own inferred shape
|
|
335
|
+
// appear static without making that source a native record.
|
|
336
|
+
if (ts.isObjectLiteralExpression(expr) && expr.properties.some((p) => ts.isSpreadAssignment(p) && lowerer.mapTypeOf(lowerer.typeOf(p.expression))?.kind === "jsval")) {
|
|
337
|
+
return true;
|
|
338
|
+
}
|
|
316
339
|
const own = lowerer.mapTypeOf(lowerer.typeOf(expr));
|
|
317
340
|
if (own?.kind === "record" || own?.kind === "array")
|
|
318
341
|
return false;
|
|
@@ -350,7 +373,7 @@ function lowerExprInner(lowerer, expr) {
|
|
|
350
373
|
// null keeps the closure shape (function-shaped members, and the
|
|
351
374
|
// conditional-spread arms where getters cannot combine).
|
|
352
375
|
const islandMemberFence = (diagsBefore, err, valueNode, getterName = null) => {
|
|
353
|
-
if (!
|
|
376
|
+
if (!isJsSourceFile(expr.getSourceFile()))
|
|
354
377
|
throw err;
|
|
355
378
|
const fence = lowerer.deferToRuntimeFence(diagsBefore, valueNode, {
|
|
356
379
|
kind: "closure",
|
|
@@ -398,6 +421,8 @@ function lowerExprInner(lowerer, expr) {
|
|
|
398
421
|
// instance): func-typed values keep the call-time closure;
|
|
399
422
|
// data-shaped values fence at the read (the getter), unless a
|
|
400
423
|
// conditional spread owns the literal (getters cannot combine).
|
|
424
|
+
if (!(err instanceof PoisonError))
|
|
425
|
+
throw err;
|
|
401
426
|
const asGetter = value.type.kind !== "func" && spread === null;
|
|
402
427
|
const fence = islandMemberFence(diagsBefore, err, valueNode, asGetter ? name.text : null);
|
|
403
428
|
if (fence === null)
|
|
@@ -486,6 +511,8 @@ function lowerExprInner(lowerer, expr) {
|
|
|
486
511
|
: null;
|
|
487
512
|
}
|
|
488
513
|
catch (err) {
|
|
514
|
+
if (!(err instanceof PoisonError))
|
|
515
|
+
throw err;
|
|
489
516
|
const nameText = name && (ts.isIdentifier(name) || ts.isStringLiteral(name)) ? name.text : null;
|
|
490
517
|
const asGetter = nameText !== null && spread === null && !funcShapedMember(prop);
|
|
491
518
|
value = islandMemberFence(valueDiagsBefore, err, prop, asGetter ? nameText : null);
|
|
@@ -551,6 +578,11 @@ function lowerExprInner(lowerer, expr) {
|
|
|
551
578
|
return { kind: "jsOp", op: "arrLit", args, type: JSVAL, loc };
|
|
552
579
|
}
|
|
553
580
|
if (ts.isTypeOfExpression(expr)) {
|
|
581
|
+
if (lowerer.isStdlibGlobal(expr.expression, "process"))
|
|
582
|
+
return { kind: "strLit", value: "object", type: STRING, loc };
|
|
583
|
+
const performance = lowerPerfHooksTypeof(lowerer, expr.expression);
|
|
584
|
+
if (performance)
|
|
585
|
+
return performance;
|
|
554
586
|
if (isNodeModuleValue(lowerer, expr.expression)) {
|
|
555
587
|
return { kind: "strLit", value: "object", type: STRING, loc };
|
|
556
588
|
}
|
|
@@ -580,8 +612,14 @@ function lowerExprInner(lowerer, expr) {
|
|
|
580
612
|
// every function and constructor global is "function" (the harness's
|
|
581
613
|
// `typeof queueMicrotask === 'function'` probes). Shadowing locals
|
|
582
614
|
// have non-stdlib symbols and keep the ordinary path.
|
|
583
|
-
|
|
584
|
-
|
|
615
|
+
let tested = expr.expression;
|
|
616
|
+
while (ts.isParenthesizedExpression(tested))
|
|
617
|
+
tested = tested.expression;
|
|
618
|
+
if (ts.isIdentifier(tested)) {
|
|
619
|
+
const sym = lowerer.checker.getSymbolAtLocation(tested);
|
|
620
|
+
if (!sym && !lowerer.dynamic) {
|
|
621
|
+
return { kind: "strLit", value: "undefined", type: STRING, loc };
|
|
622
|
+
}
|
|
585
623
|
if (lowerer.isStdlibSymbol(sym)) {
|
|
586
624
|
const t = lowerer.typeOf(expr.expression);
|
|
587
625
|
if (lowerer.checker.getCallSignatures(t).length > 0 ||
|
|
@@ -590,11 +628,9 @@ function lowerExprInner(lowerer, expr) {
|
|
|
590
628
|
}
|
|
591
629
|
}
|
|
592
630
|
}
|
|
593
|
-
//
|
|
594
|
-
//
|
|
595
|
-
//
|
|
596
|
-
// literals after lowering, so folding away the value is safe only
|
|
597
|
-
// when the operand is trivial; otherwise keep it simple and reject).
|
|
631
|
+
// A known static result type fixes typeof's answer, but its producer
|
|
632
|
+
// still evaluates: calls, getters, and checked reads can have effects
|
|
633
|
+
// or throw. Only trivial operands may disappear.
|
|
598
634
|
let operand = lowerer.lowerExpr(expr.expression);
|
|
599
635
|
if (operand.type.kind === "jsval") {
|
|
600
636
|
return { kind: "jsOp", op: "typeof", args: [operand], type: STRING, loc };
|
|
@@ -612,13 +648,16 @@ function lowerExprInner(lowerer, expr) {
|
|
|
612
648
|
array: "object", object: "object", record: "object",
|
|
613
649
|
symbol: "symbol",
|
|
614
650
|
map: "object", set: "object", promise: "object", bytes: "object",
|
|
615
|
-
|
|
651
|
+
regex: "object", generator: "object", classval: "function",
|
|
616
652
|
moduleNs: "object",
|
|
617
653
|
undefinedT: "undefined", nullT: "object",
|
|
618
654
|
};
|
|
619
655
|
const folded = FOLD[operand.type.kind];
|
|
620
|
-
if (folded
|
|
621
|
-
|
|
656
|
+
if (folded) {
|
|
657
|
+
const result = { kind: "strLit", value: folded, type: STRING, loc };
|
|
658
|
+
return isSafeToDiscard(operand) ? result : {
|
|
659
|
+
kind: "seqExpr", stmts: [{ kind: "exprStmt", expr: operand, loc }], result, type: STRING, loc,
|
|
660
|
+
};
|
|
622
661
|
}
|
|
623
662
|
// A union operand: every arm's typeof answer is static, so the value
|
|
624
663
|
// form is a ternary chain over runtime TAG tests (arms grouped by
|
|
@@ -653,6 +692,9 @@ function lowerExprInner(lowerer, expr) {
|
|
|
653
692
|
lowerer.unsupported("SC1090", expr, "typeof expressions on statically-typed values");
|
|
654
693
|
}
|
|
655
694
|
if (ts.isIdentifier(expr)) {
|
|
695
|
+
if (lowerer.isStdlibGlobal(expr, "console")) {
|
|
696
|
+
return { kind: "libCall", fn: "console.native", args: [], type: DYN, loc };
|
|
697
|
+
}
|
|
656
698
|
const moduleValue = lowerNodeModuleIdentifier(lowerer, expr);
|
|
657
699
|
if (moduleValue)
|
|
658
700
|
return moduleValue;
|
|
@@ -676,7 +718,7 @@ function lowerExprInner(lowerer, expr) {
|
|
|
676
718
|
}
|
|
677
719
|
if (projection?.kind === "builtin-function") {
|
|
678
720
|
if (!isJsSourceFile(expr.getSourceFile())) {
|
|
679
|
-
const callable = lowerer.lowerBuiltinCallableValue(projection, loc);
|
|
721
|
+
const callable = lowerer.lowerBuiltinCallableValue({ module: projection.module, member: projection.member }, loc);
|
|
680
722
|
if (callable)
|
|
681
723
|
return callable;
|
|
682
724
|
}
|
|
@@ -688,6 +730,21 @@ function lowerExprInner(lowerer, expr) {
|
|
|
688
730
|
// bridges the tagged representation with a unionNarrow.
|
|
689
731
|
const local = lowerer.resolveLocal(expr);
|
|
690
732
|
if (local) {
|
|
733
|
+
// A switch can dispatch past a lexical declaration. Keep this
|
|
734
|
+
// previously refused codec form out until switch-scope TDZ boxes
|
|
735
|
+
// model that skipped initialization, including value aliases.
|
|
736
|
+
if (local.type.kind === "record" && recordTextCodecClass(lowerer.shapes.get(local.type.shapeId)) !== null) {
|
|
737
|
+
const symbol = lowerer.resolveValueSymbol(expr);
|
|
738
|
+
const decl = symbol ? lowerer.checker.valueDeclarationOf(symbol) : undefined;
|
|
739
|
+
if (decl && ts.isVariableDeclaration(decl) && ts.isVariableDeclarationList(decl.parent)
|
|
740
|
+
&& (decl.parent.flags & ts.NodeFlags.BlockScoped) !== 0 && ts.isVariableStatement(decl.parent.parent)) {
|
|
741
|
+
const clause = decl.parent.parent.parent;
|
|
742
|
+
if ((ts.isCaseClause(clause) || ts.isDefaultClause(clause))
|
|
743
|
+
&& !(expr.getStart() >= clause.getStart() && expr.end <= clause.end)) {
|
|
744
|
+
lowerer.unsupported("SC1090", expr, "codec bindings read from another switch clause before initialization is proven");
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
}
|
|
691
748
|
if (local.type.kind === "caught")
|
|
692
749
|
return lowerer.caughtRead(expr, local, loc);
|
|
693
750
|
const symbol = lowerer.resolveValueSymbol(expr);
|
|
@@ -696,7 +753,7 @@ function lowerExprInner(lowerer, expr) {
|
|
|
696
753
|
const arithmetic = lowerer.runtimeOptionalArithmeticLocals.has(runtimeOptionalRoot);
|
|
697
754
|
const captured = !!symbol &&
|
|
698
755
|
lowerer.runtimeOptionalStorageLocals.has(runtimeOptionalRoot) &&
|
|
699
|
-
(lowerer.ctx.captureBySymbol.get(symbol) === local || runtimeOptionalRoot !== local);
|
|
756
|
+
(local.boxed === true || lowerer.ctx.captureBySymbol.get(symbol) === local || runtimeOptionalRoot !== local);
|
|
700
757
|
if (arithmetic && !runtimeOptionalUseOf(expr)) {
|
|
701
758
|
return { kind: "varRef", localId: local.id, type: local.type, loc };
|
|
702
759
|
}
|
|
@@ -737,7 +794,24 @@ function lowerExprInner(lowerer, expr) {
|
|
|
737
794
|
}
|
|
738
795
|
return { kind: "varRef", localId: local.id, type: local.type, loc };
|
|
739
796
|
}
|
|
740
|
-
|
|
797
|
+
const narrowed = lowerer.maybeNarrow({ kind: "varRef", localId: local.id, type: local.type, loc }, expr);
|
|
798
|
+
if (narrowed.type.kind === "union" && lowerer.runtimeOptionalStorageLocals.has(runtimeOptionalRoot) &&
|
|
799
|
+
lowerer.mapTypeOf(lowerer.typeOf(expr))?.kind === "record") {
|
|
800
|
+
// A presence guard can be followed by a predicate strengthening
|
|
801
|
+
// the record's fields. Its refined shape is not a stored union
|
|
802
|
+
// arm: extract the original layout before reading a member.
|
|
803
|
+
const use = runtimeOptionalUseOf(expr);
|
|
804
|
+
if (use && !use.complex && !use.optional) {
|
|
805
|
+
if (use.kind === "property")
|
|
806
|
+
return runtimeOptionalReceiverRead(lowerer, expr, local, use.access.name.text, loc);
|
|
807
|
+
if (use.kind === "element") {
|
|
808
|
+
const key = runtimeOptionalElementKey(use.access.argumentExpression);
|
|
809
|
+
if (key !== null)
|
|
810
|
+
return runtimeOptionalReceiverRead(lowerer, expr, local, key, loc);
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
return narrowed;
|
|
741
815
|
}
|
|
742
816
|
// `import x = N.y` aliases resolve transparently through globalOf/
|
|
743
817
|
// fnSigOf below; their source-order guards live here (a no-op for
|
|
@@ -774,6 +848,7 @@ function lowerExprInner(lowerer, expr) {
|
|
|
774
848
|
lowerer.unions.get(g.type.unionId).arms.some((a) => a.kind === "string");
|
|
775
849
|
if (!arithmeticUnion && g.type.kind === "union" && narrowed && narrowed.kind !== "union" &&
|
|
776
850
|
!isUnitType(narrowed) &&
|
|
851
|
+
!(narrowed.kind === "record" && recordTextCodecClass(lowerer.shapes.get(narrowed.shapeId)) !== null) &&
|
|
777
852
|
narrowed.kind !== "f64" && narrowed.kind !== "string" && narrowed.kind !== "bool" &&
|
|
778
853
|
lowerer.armTag(g.type.unionId, narrowed) >= 0) {
|
|
779
854
|
const helper = lowerer.narrowedArmHelper(g.type.unionId, narrowed, loc);
|
|
@@ -817,7 +892,7 @@ function lowerExprInner(lowerer, expr) {
|
|
|
817
892
|
if ((expr.text === "__dirname" || expr.text === "__filename") &&
|
|
818
893
|
lowerer.isStdlibGlobal(expr, expr.text)) {
|
|
819
894
|
const sf = expr.getSourceFile();
|
|
820
|
-
if (isNodeEsmFile(sf)) {
|
|
895
|
+
if (isNodeEsmFile(sf, lowerer.program)) {
|
|
821
896
|
lowerer.unsupported("SC1090", expr, `'${expr.text}' in an ES module (Node defines the CJS module globals only in CommonJS modules)`);
|
|
822
897
|
}
|
|
823
898
|
// The WASI runner mounts the host cwd at guest `/` and the host temp
|
|
@@ -899,17 +974,17 @@ function lowerExprInner(lowerer, expr) {
|
|
|
899
974
|
if (roots)
|
|
900
975
|
return roots;
|
|
901
976
|
}
|
|
902
|
-
// JavaScript sources: a builtin member taken as a bare VALUE is
|
|
903
|
-
// the same identity-token story as stdlib globals above (the
|
|
904
|
-
// harness adds worker_threads.Worker to its identity Set).
|
|
905
|
-
if (isJsSourceFile(expr.getSourceFile())) {
|
|
906
|
-
return { kind: "strLit", value: `[builtin ${bi.module}.${bi.member}]`, type: STRING, loc };
|
|
907
|
-
}
|
|
908
977
|
{
|
|
909
978
|
const callable = lowerer.lowerBuiltinCallableValue(bi, loc);
|
|
910
979
|
if (callable)
|
|
911
980
|
return callable;
|
|
912
981
|
}
|
|
982
|
+
// JavaScript sources: an otherwise unsupported builtin VALUE is
|
|
983
|
+
// the same identity-token story as stdlib globals above (the
|
|
984
|
+
// harness adds worker_threads.Worker to its identity Set).
|
|
985
|
+
if (isJsSourceFile(expr.getSourceFile())) {
|
|
986
|
+
return { kind: "strLit", value: `[builtin ${bi.module}.${bi.member}]`, type: STRING, loc };
|
|
987
|
+
}
|
|
913
988
|
if (builtinModuleFnOf(lowerer, bi.module, bi.member)) {
|
|
914
989
|
lowerer.unsupported("SC1090", expr, `library functions as values (call '${expr.text}' directly)`);
|
|
915
990
|
}
|
|
@@ -999,6 +1074,9 @@ function lowerExprInner(lowerer, expr) {
|
|
|
999
1074
|
// global, one token; what a token cannot do (be called, answer
|
|
1000
1075
|
// typeof "function") meets per-site fences/divergences, and
|
|
1001
1076
|
// SEMANTICS.md documents the stance.
|
|
1077
|
+
if (!lowerer.dynamic && stdlibGlobalNameOf(lowerer, expr) === "globalThis") {
|
|
1078
|
+
return lowerGlobalValue(lowerer, expr);
|
|
1079
|
+
}
|
|
1002
1080
|
{
|
|
1003
1081
|
const sym = lowerer.checker.getSymbolAtLocation(expr);
|
|
1004
1082
|
if (lowerer.isStdlibSymbol(sym) || expr.text === "globalThis") {
|
|
@@ -1006,14 +1084,21 @@ function lowerExprInner(lowerer, expr) {
|
|
|
1006
1084
|
const canonical = stdlibGlobalNameOf(lowerer, expr) ?? expr.text;
|
|
1007
1085
|
return { kind: "strLit", value: `[builtin ${canonical}]`, type: STRING, loc };
|
|
1008
1086
|
}
|
|
1087
|
+
// In a dynamic TypeScript build, the real global object is the
|
|
1088
|
+
// escape hatch for host capabilities without static lowering.
|
|
1089
|
+
// Direct globalThis.member uses still take their static surface
|
|
1090
|
+
// paths; an explicit `as any` or `: any` can reach engine ops.
|
|
1091
|
+
if (lowerer.dynamic && expr.text === "globalThis") {
|
|
1092
|
+
return { kind: "jsOp", op: "globalGet", name: "globalThis", args: [], type: JSVAL, loc };
|
|
1093
|
+
}
|
|
1009
1094
|
// The families with a WHY: each hint states what makes the
|
|
1010
1095
|
// surface genuinely non-static (or what to use instead).
|
|
1011
1096
|
const globalHints = {
|
|
1012
|
-
Proxy: "
|
|
1097
|
+
Proxy: "Proxy constructor values have no native lowering; use a direct new Proxy with checked-native plain objects",
|
|
1013
1098
|
Reflect: "reflective property access has no static lowering — read and call members directly",
|
|
1014
|
-
Intl: "
|
|
1099
|
+
Intl: "native Intl supports default Unicode grapheme segmentation and the composed new Intl.NumberFormat(\"en-US\").format(x); locale negotiation and other ICU-backed operations remain unsupported",
|
|
1015
1100
|
SharedArrayBuffer: "no shared-memory threads exist in a compiled program — Uint8Array is the byte storage",
|
|
1016
|
-
ArrayBuffer: "
|
|
1101
|
+
ArrayBuffer: "ArrayBuffer constructor values have no native lowering; use new ArrayBuffer(length) for fixed-length shared storage",
|
|
1017
1102
|
WeakRef: "deref()-after-collect exposes GC timing — genuinely dynamic; hold a strong reference instead",
|
|
1018
1103
|
FinalizationRegistry: "finalization callbacks expose GC timing — genuinely dynamic; release resources explicitly instead",
|
|
1019
1104
|
eval: "runtime code evaluation cannot be compiled ahead of time",
|
|
@@ -1363,6 +1448,14 @@ function lowerExprInner(lowerer, expr) {
|
|
|
1363
1448
|
if (ts.isConditionalExpression(expr))
|
|
1364
1449
|
return lowerTernary(lowerer, expr);
|
|
1365
1450
|
if (ts.isPropertyAccessExpression(expr)) {
|
|
1451
|
+
// Expando statics may still be absent even when JS inference sees
|
|
1452
|
+
// the eventual assignment's scalar type. Preserve the checked value.
|
|
1453
|
+
const staticField = lowerStaticFieldRead(lowerer, expr);
|
|
1454
|
+
if (staticField)
|
|
1455
|
+
return staticField;
|
|
1456
|
+
const prototypeData = lowerClassPrototypeData(lowerer, expr);
|
|
1457
|
+
if (prototypeData)
|
|
1458
|
+
return prototypeData;
|
|
1366
1459
|
// `super.x`: the base chain's GETTER, called directly (super
|
|
1367
1460
|
// dispatch is static in JS — never through the dynamic class).
|
|
1368
1461
|
// super.method() calls are routed at the call site; a bare super
|
|
@@ -1404,6 +1497,25 @@ function lowerExprInner(lowerer, expr) {
|
|
|
1404
1497
|
const moduleProperty = lowerNodeModuleProperty(lowerer, expr);
|
|
1405
1498
|
if (moduleProperty)
|
|
1406
1499
|
return moduleProperty;
|
|
1500
|
+
if (stdlibGlobalNameOf(lowerer, expr.expression) === "Symbol" &&
|
|
1501
|
+
["iterator", "asyncIterator", "toStringTag", "toPrimitive", "hasInstance", "isConcatSpreadable", "match", "matchAll", "replace", "search", "split", "species", "unscopables", "dispose", "asyncDispose"].includes(expr.name.text)) {
|
|
1502
|
+
return { kind: "libCall", fn: "sym.wellKnown", args: [{ kind: "strLit", value: expr.name.text, type: STRING, loc }], type: { kind: "symbol" }, loc };
|
|
1503
|
+
}
|
|
1504
|
+
if (lowerer.stdlibGlobalMember(expr, "globalThis") === "console") {
|
|
1505
|
+
return { kind: "libCall", fn: "console.native", args: [], type: DYN, loc };
|
|
1506
|
+
}
|
|
1507
|
+
if (lowerer.stdlibGlobalMember(expr, "console") !== null) {
|
|
1508
|
+
return { kind: "dynKeyGet", value: { kind: "libCall", fn: "console.native", args: [], type: DYN, loc },
|
|
1509
|
+
key: { kind: "strLit", value: expr.name.text, type: STRING, loc }, type: DYN, loc };
|
|
1510
|
+
}
|
|
1511
|
+
// Missing host globals (Bun/Deno capability probes) have no binding
|
|
1512
|
+
// in a native program. Declared globals retain their own lowering;
|
|
1513
|
+
// arbitrary global-object mutation remains fenced. Preserve a dyn
|
|
1514
|
+
// undefined value so stored probes compose with optional calls.
|
|
1515
|
+
if (!lowerer.dynamic && stdlibGlobalNameOf(lowerer, expr.expression) === "globalThis" &&
|
|
1516
|
+
!lowerer.checker.getPropertyOfType(lowerer.typeOf(expr.expression), expr.name.text)) {
|
|
1517
|
+
return { kind: "dynFrom", value: { kind: "unitLit", unit: "undefined", type: UNDEFINED_T, loc }, type: DYN, loc };
|
|
1518
|
+
}
|
|
1407
1519
|
if (expr.questionDotToken && expr.name.text === "electron") {
|
|
1408
1520
|
const processProperty = lowerer.lowerProcessProperty(expr);
|
|
1409
1521
|
if (processProperty)
|
|
@@ -1420,6 +1532,12 @@ function lowerExprInner(lowerer, expr) {
|
|
|
1420
1532
|
if (!expr.questionDotToken && isOptionalChainTail(lowerer, expr)) {
|
|
1421
1533
|
return lowerer.lowerOptionalChain(expr);
|
|
1422
1534
|
}
|
|
1535
|
+
// Known Web handles retain their declared surface even when their
|
|
1536
|
+
// storage uses checked values. Apply this before generic reads.
|
|
1537
|
+
lowerer.fenceStaticAbortControllerMemberRead(expr);
|
|
1538
|
+
lowerer.fenceStaticResponseMember(expr, "read");
|
|
1539
|
+
lowerer.fenceStaticHeadersMember(expr, "read");
|
|
1540
|
+
lowerer.fenceStaticReadableStreamMember(expr, "read");
|
|
1423
1541
|
// Island receiver: o.x is an engine property read (getProp may throw
|
|
1424
1542
|
// — reading off null/undefined — bridged catchably like everything
|
|
1425
1543
|
// at the boundary). `o?.x` arrives here too, through the chain
|
|
@@ -1559,11 +1677,11 @@ function lowerExprInner(lowerer, expr) {
|
|
|
1559
1677
|
// resolve unconditionally — they run after the module evaluated,
|
|
1560
1678
|
// the same approximation identifier exports already make. Every
|
|
1561
1679
|
// other module.exports read keeps its existing story.
|
|
1562
|
-
if (!expr.questionDotToken && isModuleExportsAccess(expr) && isCjsJsFile(expr.getSourceFile())) {
|
|
1680
|
+
if (!expr.questionDotToken && isModuleExportsAccess(expr) && isCjsJsFile(expr.getSourceFile(), lowerer.program)) {
|
|
1563
1681
|
const whole = cjsClassExprWholeExportOf(expr.getSourceFile());
|
|
1564
1682
|
if (whole) {
|
|
1565
1683
|
let inBody = false;
|
|
1566
|
-
for (let p = expr.parent; !ts.isSourceFile(p); p = p.parent) {
|
|
1684
|
+
for (let p = expr.parent; p !== undefined && !ts.isSourceFile(p); p = p.parent) {
|
|
1567
1685
|
if (ts.isFunctionLike(p) || ts.isClassDeclaration(p) || ts.isClassExpression(p)) {
|
|
1568
1686
|
inBody = true;
|
|
1569
1687
|
break;
|
|
@@ -1591,7 +1709,7 @@ function lowerExprInner(lowerer, expr) {
|
|
|
1591
1709
|
const writePos = ts.isBinaryExpression(expr.parent) &&
|
|
1592
1710
|
expr.parent.left === expr &&
|
|
1593
1711
|
expr.parent.operatorToken.kind === ts.SyntaxKind.EqualsToken;
|
|
1594
|
-
if (!expr.questionDotToken && cjsMemberRecv && !writePos && isCjsJsFile(sf)) {
|
|
1712
|
+
if (!expr.questionDotToken && cjsMemberRecv && !writePos && isCjsJsFile(sf, lowerer.program)) {
|
|
1595
1713
|
const sym = lowerer.checker.getSymbolAtLocation(expr.name) ??
|
|
1596
1714
|
lowerer.cjsModuleExportSymbol(sf, expr.name.text);
|
|
1597
1715
|
const resolved = sym !== undefined && sym.flags & ts.SymbolFlags.Alias
|
|
@@ -1713,6 +1831,8 @@ function lowerExprInner(lowerer, expr) {
|
|
|
1713
1831
|
isJsSourceFile(expr.getSourceFile())) {
|
|
1714
1832
|
const canonical = stdlibGlobalNameOf(lowerer, expr);
|
|
1715
1833
|
if (canonical !== null) {
|
|
1834
|
+
if (!lowerer.dynamic && canonical === "globalThis")
|
|
1835
|
+
return lowerGlobalValue(lowerer, expr);
|
|
1716
1836
|
return { kind: "strLit", value: `[builtin ${canonical}]`, type: STRING, loc };
|
|
1717
1837
|
}
|
|
1718
1838
|
}
|
|
@@ -1736,6 +1856,7 @@ function lowerExprInner(lowerer, expr) {
|
|
|
1736
1856
|
// instead.
|
|
1737
1857
|
if ((isJsSourceFile(expr.getSourceFile()) &&
|
|
1738
1858
|
(lowerer.mapTypeOf(lowerer.typeOf(expr.expression)) === null ||
|
|
1859
|
+
(lowerer.typeOf(expr.expression).flags & ts.TypeFlags.Never) !== 0 ||
|
|
1739
1860
|
// A never-tainted receiver type maps (never rides as f64) but
|
|
1740
1861
|
// its VALUE lowered checked-dynamic — same dyn read.
|
|
1741
1862
|
neverTaintedJsType(lowerer, expr.expression, lowerer.typeOf(expr.expression)))) ||
|
|
@@ -1749,6 +1870,11 @@ function lowerExprInner(lowerer, expr) {
|
|
|
1749
1870
|
const opt = hasOptionalChainGuard(expr.expression);
|
|
1750
1871
|
return lowerer.maybeNarrow({ kind: "dynKeyGet", key, ...(opt ? { optional: true } : {}), value: recv, type: DYN, loc }, expr);
|
|
1751
1872
|
}
|
|
1873
|
+
if (expr.name.text === "length" && (recv.type.kind === "array" || recv.type.kind === "string")) {
|
|
1874
|
+
return recv.type.kind === "array"
|
|
1875
|
+
? { kind: "arrIntrinsic", method: "length", receiver: recv, args: [], type: F64, loc }
|
|
1876
|
+
: { kind: "strIntrinsic", method: "length", receiver: recv, args: [], type: F64, loc };
|
|
1877
|
+
}
|
|
1752
1878
|
}
|
|
1753
1879
|
// `f.name` / `f.length` / own properties on a FUNCTION-typed JS
|
|
1754
1880
|
// value (the mustCall wrapper's function-instance members): read
|
|
@@ -1757,11 +1883,24 @@ function lowerExprInner(lowerer, expr) {
|
|
|
1757
1883
|
// best-effort static name and the declared arity; anything else is
|
|
1758
1884
|
// the own-property answer, undefined. Function.prototype METHOD
|
|
1759
1885
|
// names stay fenced (a stored-member undefined would mis-answer
|
|
1760
|
-
// `f.call` as a value).
|
|
1761
|
-
|
|
1762
|
-
|
|
1886
|
+
// `f.call` as a value). Static callable values keep the existing TS fence; checked callable
|
|
1887
|
+
// storage can expose its own name and length in both source languages.
|
|
1888
|
+
if (!["apply", "bind", "call", "toString", "caller", "arguments"].includes(expr.name.text) &&
|
|
1889
|
+
(isJsSourceFile(expr.getSourceFile()) || expr.name.text === "name" || expr.name.text === "length" || expr.name.text === "constructor")) {
|
|
1763
1890
|
const probed = tryLowerExpression(lowerer, expr.expression);
|
|
1764
|
-
|
|
1891
|
+
// Inferred callable returns can use checked storage even when the
|
|
1892
|
+
// checker still describes a function. Read its actual dyn value;
|
|
1893
|
+
// the ambient Function member must not hide its own properties.
|
|
1894
|
+
const receiverType = lowerer.typeOf(expr.expression);
|
|
1895
|
+
const receiverSymbol = receiverType.getSymbol();
|
|
1896
|
+
const checkedFunction = lowerer.checker.getCallSignatures(receiverType).length > 0 ||
|
|
1897
|
+
(receiverSymbol?.name === "Function" && lowerer.isStdlibSymbol(receiverSymbol));
|
|
1898
|
+
if (probed?.type.kind === "dyn" && checkedFunction) {
|
|
1899
|
+
const key = { kind: "strLit", value: expr.name.text, type: STRING, loc: locOf(expr.name) };
|
|
1900
|
+
const opt = hasOptionalChainGuard(expr.expression);
|
|
1901
|
+
return lowerer.maybeNarrow({ kind: "dynKeyGet", key, value: probed, ...(opt ? { optional: true } : {}), type: DYN, loc }, expr);
|
|
1902
|
+
}
|
|
1903
|
+
if ((isJsSourceFile(expr.getSourceFile()) || expr.name.text === "constructor") && probed?.type.kind === "func" &&
|
|
1765
1904
|
canBoxFuncIntoDyn(probed.type, (id) => lowerer.shapes.get(id), (id) => lowerer.unions.get(id))) {
|
|
1766
1905
|
const fnName = jsFuncNameOf(expr.expression);
|
|
1767
1906
|
const boxed = {
|
|
@@ -1816,7 +1955,10 @@ function lowerExprInner(lowerer, expr) {
|
|
|
1816
1955
|
// the generic dyn receiver path below. Provenance is limited to the
|
|
1817
1956
|
// node:util parseArgs family; every other stdlib member keeps its
|
|
1818
1957
|
// ordinary surface fence.
|
|
1819
|
-
|
|
1958
|
+
const segmentDataSymbol = lowerer.typeOf(expr.expression).getSymbol();
|
|
1959
|
+
const segmentDataProperty = segmentDataSymbol?.name === "SegmentData" && lowerer.isStdlibSymbol(segmentDataSymbol) &&
|
|
1960
|
+
["segment", "index", "input", "isWordLike"].includes(expr.name.text);
|
|
1961
|
+
if (isParseArgsDynProperty(lowerer, expr) || segmentDataProperty) {
|
|
1820
1962
|
const recv = lowerer.lowerExpr(expr.expression);
|
|
1821
1963
|
if (recv.type.kind === "dyn") {
|
|
1822
1964
|
const key = {
|
|
@@ -1850,10 +1992,6 @@ function lowerExprInner(lowerer, expr) {
|
|
|
1850
1992
|
// The lib fence's PROPERTY chokepoint: a stdlib-declared member that
|
|
1851
1993
|
// no lowering above claimed ([1,2].entries, Math.SQRT2, Promise.all,
|
|
1852
1994
|
// re.exec as a value, ...) reports SC2020 here.
|
|
1853
|
-
lowerer.fenceStaticAbortControllerMemberRead(expr);
|
|
1854
|
-
lowerer.fenceStaticResponseMember(expr, "read");
|
|
1855
|
-
lowerer.fenceStaticHeadersMember(expr, "read");
|
|
1856
|
-
lowerer.fenceStaticReadableStreamMember(expr, "read");
|
|
1857
1995
|
lowerer.stdlibMemberFence(expr);
|
|
1858
1996
|
// The npm chokepoint: a member on a package-typed receiver in a
|
|
1859
1997
|
// static build — attributed to the package, like every other site.
|
|
@@ -1912,6 +2050,17 @@ function lowerExprInner(lowerer, expr) {
|
|
|
1912
2050
|
loc,
|
|
1913
2051
|
}, expr);
|
|
1914
2052
|
}
|
|
2053
|
+
// An inferred package-JS call specializes a parameter to the
|
|
2054
|
+
// caller's own record shape. Optional option names omitted by that
|
|
2055
|
+
// caller read undefined; the missing field is not a failed type
|
|
2056
|
+
// assertion. Known prototype members were handled/fenced above.
|
|
2057
|
+
if (recvShape && !recvShape.tuple && lowerer.implicitParamTypes !== null &&
|
|
2058
|
+
isJsSourceFile(expr.getSourceFile()) && npmStaticPackageOfPath(expr.getSourceFile().fileName) !== null) {
|
|
2059
|
+
return {
|
|
2060
|
+
kind: "seqExpr", stmts: [{ kind: "exprStmt", expr: recvLowered, loc }],
|
|
2061
|
+
result: dynUndefinedExpr(loc), type: DYN, loc,
|
|
2062
|
+
};
|
|
2063
|
+
}
|
|
1915
2064
|
}
|
|
1916
2065
|
// An ABSTRACT property through an abstract-typed receiver: the
|
|
1917
2066
|
// declaration is erased at runtime — Node defines no field for it,
|
|
@@ -1921,6 +2070,19 @@ function lowerExprInner(lowerer, expr) {
|
|
|
1921
2070
|
if (recvLowered.type.kind === "object" && abstractPropertyDeclOf(lowerer, expr)) {
|
|
1922
2071
|
lowerer.unsupported("SC1090", expr, `reading the abstract property '${expr.name.text}' through a '${lowerer.checker.typeToString(lowerer.typeOf(expr.expression))}'-typed receiver (abstract property declarations are erased at runtime, so no shared slot exists — type the receiver as the concrete class, or declare an abstract getter instead)`);
|
|
1923
2072
|
}
|
|
2073
|
+
// Array reads may add an undefined arm even when the checker still
|
|
2074
|
+
// reports a class. Its property bag must remain reachable, while
|
|
2075
|
+
// an absent element takes the normal checked-value TypeError.
|
|
2076
|
+
const nativeReceiver = lowerer.stripUndefinedArm(recvLowered.type);
|
|
2077
|
+
if (isDynTypedRefType(nativeReceiver)) {
|
|
2078
|
+
const info = lowerer.classes.get(nativeReceiver.className);
|
|
2079
|
+
if (info && !info.def.runtime && !info.builtinError && !info.builtinEmitter && !info.builtinStream) {
|
|
2080
|
+
return lowerer.maybeNarrow({
|
|
2081
|
+
kind: "dynKeyGet", value: lowerer.coerceToExpected(recvLowered, DYN),
|
|
2082
|
+
key: { kind: "strLit", value: expr.name.text, type: STRING, loc }, type: DYN, loc,
|
|
2083
|
+
}, expr);
|
|
2084
|
+
}
|
|
2085
|
+
}
|
|
1924
2086
|
lowerer.unsupported("SC1090", expr, `reading '${expr.name.text}' from a value of type '${lowerer.checker.typeToString(lowerer.typeOf(expr.expression))}'`);
|
|
1925
2087
|
}
|
|
1926
2088
|
// Meta-properties, named: `new.target` reflects HOW a function was
|
|
@@ -1937,7 +2099,7 @@ function lowerExprInner(lowerer, expr) {
|
|
|
1937
2099
|
const entry = UNSUPPORTED_EXPR[expr.kind];
|
|
1938
2100
|
if (entry)
|
|
1939
2101
|
lowerer.unsupported(entry.code, expr, entry.feature);
|
|
1940
|
-
lowerer.unsupported("SC1090", expr, `syntax '${ts.
|
|
2102
|
+
lowerer.unsupported("SC1090", expr, `syntax '${ts.syntaxKindName(expr.kind)}'`);
|
|
1941
2103
|
}
|
|
1942
2104
|
function moduleFileName(lowerer, sf) {
|
|
1943
2105
|
return lowerer.targetPlatform === "wasi"
|
|
@@ -1976,11 +2138,10 @@ function importMetaProperty(lowerer, expr) {
|
|
|
1976
2138
|
return null;
|
|
1977
2139
|
}
|
|
1978
2140
|
}
|
|
1979
|
-
/** `c ? a : b
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
function lowerTernary(lowerer, expr, expected) {
|
|
2141
|
+
/** `c ? a : b`. An explicit destination constructs each branch at that
|
|
2142
|
+
* layout before joining it. Without one, retain the expression's inferred
|
|
2143
|
+
* type and the contextual/sibling rules below. */
|
|
2144
|
+
export function lowerTernary(lowerer, expr, expected) {
|
|
1984
2145
|
const loc = locOf(expr);
|
|
1985
2146
|
// `Array.isArray(x) ? x : [x]` over a `T | readonly T[]` union: tsc
|
|
1986
2147
|
// narrows the TRUE branch to `any[]` (maybeNarrow's isArray bridge
|
|
@@ -2034,7 +2195,16 @@ function lowerTernary(lowerer, expr, expected) {
|
|
|
2034
2195
|
// literals) sit untaken in compiled JS. A boolLit carries no
|
|
2035
2196
|
// effects, so dropping the condition read loses nothing.
|
|
2036
2197
|
if (cond.kind === "boolLit") {
|
|
2037
|
-
return lowerer.
|
|
2198
|
+
return lowerer.lowerExprExpecting(cond.value ? expr.whenTrue : expr.whenFalse, expected);
|
|
2199
|
+
}
|
|
2200
|
+
if (expected) {
|
|
2201
|
+
// Construct fresh literals in their actual destination. Joining
|
|
2202
|
+
// inferred layouts first can erase the literal discriminator or
|
|
2203
|
+
// choose an empty array's uninhabited element representation.
|
|
2204
|
+
// Runtime-optional proofs apply only within the selected true arm.
|
|
2205
|
+
const then = withRuntimeOptionalNarrowed(lowerer, runtimeOptionalTrueIds(lowerer, expr.condition), () => lowerer.lowerExprExpecting(expr.whenTrue, expected));
|
|
2206
|
+
const else_ = lowerer.lowerExprExpecting(expr.whenFalse, expected);
|
|
2207
|
+
return { kind: "ternary", cond, then, else_, type: expected, loc };
|
|
2038
2208
|
}
|
|
2039
2209
|
const ctxTs = lowerer.checker.getContextualType(expr);
|
|
2040
2210
|
const ctxMapped = ctxTs ? lowerer.mapTypeOf(ctxTs) : null;
|
|
@@ -2051,7 +2221,7 @@ function lowerTernary(lowerer, expr, expected) {
|
|
|
2051
2221
|
// lowerArrayLiteral, and just as unambiguous — tsc already typed the
|
|
2052
2222
|
// whole ternary by the filled arm alone. Both arms empty stays
|
|
2053
2223
|
// fenced (no element type exists anywhere).
|
|
2054
|
-
const ctxArray =
|
|
2224
|
+
const ctxArray = ctxMapped?.kind === "array" ? ctxMapped : null;
|
|
2055
2225
|
const armLiteral = (e) => {
|
|
2056
2226
|
let x = e;
|
|
2057
2227
|
while (ts.isParenthesizedExpression(x))
|
|
@@ -2075,7 +2245,21 @@ function lowerTernary(lowerer, expr, expected) {
|
|
|
2075
2245
|
}
|
|
2076
2246
|
return false;
|
|
2077
2247
|
};
|
|
2248
|
+
// Inferred signature tables often join a populated argument array
|
|
2249
|
+
// with nested empty-array branches. Carry the whole record layout
|
|
2250
|
+
// through those branches before constructing their fresh fields.
|
|
2251
|
+
const ownJoin = lowerer.mapTypeOf(lowerer.typeOf(expr));
|
|
2252
|
+
const recordJoin = ctxMapped?.kind === "record" ? ctxMapped :
|
|
2253
|
+
ownJoin?.kind === "record" ? ownJoin : undefined;
|
|
2078
2254
|
const lowerArm = (e, siblingType) => {
|
|
2255
|
+
let fresh = e;
|
|
2256
|
+
while (ts.isParenthesizedExpression(fresh))
|
|
2257
|
+
fresh = fresh.expression;
|
|
2258
|
+
if (recordJoin && ts.isConditionalExpression(fresh))
|
|
2259
|
+
return lowerTernary(lowerer, fresh, recordJoin);
|
|
2260
|
+
if (recordJoin && ts.isObjectLiteralExpression(fresh)) {
|
|
2261
|
+
return lowerer.lowerObjectLiteral(fresh, recordJoin);
|
|
2262
|
+
}
|
|
2079
2263
|
const lit = armLiteral(e);
|
|
2080
2264
|
if (lit && ctxArray)
|
|
2081
2265
|
return lowerer.lowerArrayLiteral(lit, ctxArray);
|
|
@@ -2128,11 +2312,14 @@ function lowerTernary(lowerer, expr, expected) {
|
|
|
2128
2312
|
const lowerTrueArm = () => withRuntimeOptionalNarrowed(lowerer, runtimeTrueIds, () => lowerArm(expr.whenTrue, ownArrayJoin ?? undefined));
|
|
2129
2313
|
if (!ctxArray && emptyUntypedArrayArm(expr.whenTrue) && !emptyUntypedArrayArm(expr.whenFalse)) {
|
|
2130
2314
|
elseRaw = lowerer.lowerExpr(expr.whenFalse);
|
|
2131
|
-
thenRaw = withRuntimeOptionalNarrowed(lowerer, runtimeTrueIds, () => lowerArm(expr.whenTrue, elseRaw.type));
|
|
2315
|
+
thenRaw = withRuntimeOptionalNarrowed(lowerer, runtimeTrueIds, () => lowerArm(expr.whenTrue, elseRaw.type.kind === "dyn" && ownArrayJoin ? ownArrayJoin : elseRaw.type));
|
|
2132
2316
|
}
|
|
2133
2317
|
else {
|
|
2134
2318
|
thenRaw = lowerTrueArm();
|
|
2135
|
-
|
|
2319
|
+
// A checked-dynamic producer (Object.entries(...).filter(...))
|
|
2320
|
+
// can still have a precise checker array type. Empty literal arms
|
|
2321
|
+
// must adopt that element layout before the checked array boundary.
|
|
2322
|
+
elseRaw = lowerArm(expr.whenFalse, thenRaw.type.kind === "dyn" && ownArrayJoin ? ownArrayJoin : thenRaw.type);
|
|
2136
2323
|
}
|
|
2137
2324
|
// The ternary's IR type is normally the checker's own: it collapses
|
|
2138
2325
|
// same-kind literal unions ("a" | "b" → string) and forms tagged
|
|
@@ -2204,14 +2391,22 @@ function lowerTernary(lowerer, expr, expected) {
|
|
|
2204
2391
|
}
|
|
2205
2392
|
}
|
|
2206
2393
|
}
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
2394
|
+
// JavaScript function selection preserves the chosen closure's full
|
|
2395
|
+
// call ABI. A checker join can erase a rest/arguments pack even though
|
|
2396
|
+
// a later call supplies additional observable arguments.
|
|
2397
|
+
const callableJoin = isJsSourceFile(expr.getSourceFile()) &&
|
|
2398
|
+
thenRaw.type.kind === "func" && elseRaw.type.kind === "func" &&
|
|
2399
|
+
!typeEquals(thenRaw.type, elseRaw.type) &&
|
|
2400
|
+
lowerer.dynConvertible(thenRaw.type) && lowerer.dynConvertible(elseRaw.type);
|
|
2401
|
+
const type = callableJoin ? DYN :
|
|
2402
|
+
thenRaw.type.kind === "array" && typeEquals(thenRaw.type, elseRaw.type)
|
|
2403
|
+
? thenRaw.type
|
|
2404
|
+
: dynJoin
|
|
2405
|
+
? DYN
|
|
2406
|
+
: (anyJoin ??
|
|
2407
|
+
(useCtx && ctxMapped
|
|
2408
|
+
? ctxMapped
|
|
2409
|
+
: lowerer.irTypeOf(expr)));
|
|
2215
2410
|
// Each arm flows into the ternary's type through the slot-coercion
|
|
2216
2411
|
// path: union arms wrap, dyn slots reject the non-dyn arm with
|
|
2217
2412
|
// SC1101, mismatched record shapes get SC2002; coerceInto is
|
|
@@ -2240,6 +2435,16 @@ function lowerTernary(lowerer, expr, expected) {
|
|
|
2240
2435
|
* unrepresentable without a re-tag), or to nothing (`never` in an
|
|
2241
2436
|
* exhaustive default) leaves the expression union-typed. */
|
|
2242
2437
|
export function maybeNarrow(lowerer, expr, node) {
|
|
2438
|
+
if ((expr.type.kind === "union" || expr.type.kind === "dyn") &&
|
|
2439
|
+
isJsSourceFile(node.getSourceFile()) && ts.isPropertyAccessExpression(node)) {
|
|
2440
|
+
const symbol = lowerer.checker.getSymbolAtLocation(node.name);
|
|
2441
|
+
if (symbol && lowerer.checker.declarationsOf(symbol).some((decl) => ts.isPropertyDeclaration(decl) && !decl.initializer && !decl.type &&
|
|
2442
|
+
isJsSourceFile(decl.getSourceFile()))) {
|
|
2443
|
+
// Inference describes later writes, not the initial undefined.
|
|
2444
|
+
// A use that requires the inferred type must validate the value.
|
|
2445
|
+
return expr.type.kind === "dyn" ? expr : { kind: "dynFrom", value: expr, type: DYN, loc: expr.loc };
|
|
2446
|
+
}
|
|
2447
|
+
}
|
|
2243
2448
|
// A dyn read tsc narrowed to a SCALAR (a typeof test proved the kind):
|
|
2244
2449
|
// bridge with a VALIDATED extraction — dynCheck, the checked-cast
|
|
2245
2450
|
// machinery — rather than a trusted one. After the guard the check
|
|
@@ -2247,15 +2452,40 @@ export function maybeNarrow(lowerer, expr, node) {
|
|
|
2247
2452
|
// instead of misreading the payload (the dyn boundary's usual stance).
|
|
2248
2453
|
// Object/array narrowings stay dyn-typed and keep their fences.
|
|
2249
2454
|
if (expr.type.kind === "dyn") {
|
|
2455
|
+
if (isJsSourceFile(node.getSourceFile()) && ts.isIdentifier(node)) {
|
|
2456
|
+
const symbol = lowerer.resolveValueSymbol(node);
|
|
2457
|
+
if (symbol && lowerer.checker.declarationsOf(symbol).some((decl) => {
|
|
2458
|
+
if (!ts.isParameter(decl) || decl.type)
|
|
2459
|
+
return false;
|
|
2460
|
+
const fn = decl.parent;
|
|
2461
|
+
if (!ts.isFunctionExpression(fn) && !ts.isArrowFunction(fn))
|
|
2462
|
+
return false;
|
|
2463
|
+
const assignment = fn.parent;
|
|
2464
|
+
return ts.isBinaryExpression(assignment) && assignment.right === fn &&
|
|
2465
|
+
ts.isPropertyAccessExpression(assignment.left) && assignment.left.name.text === "write" &&
|
|
2466
|
+
lowerer.isStdlibMember(assignment.left);
|
|
2467
|
+
}))
|
|
2468
|
+
return expr;
|
|
2469
|
+
}
|
|
2470
|
+
// Node's TTY declarations promise booleans/numbers even when a pipe
|
|
2471
|
+
// has no such property. Preserve the checked read for JS capability
|
|
2472
|
+
// probes instead of treating a declaration as a runtime type guard.
|
|
2473
|
+
if (isJsSourceFile(node.getSourceFile()) && ts.isPropertyAccessExpression(node) &&
|
|
2474
|
+
["isTTY", "columns", "rows"].includes(node.name.text) && lowerer.isStdlibMember(node))
|
|
2475
|
+
return expr;
|
|
2476
|
+
// A null-initialized JS field can later receive an object even when
|
|
2477
|
+
// checker flow calls its non-null branch never. The live checked
|
|
2478
|
+
// value remains authoritative; never is not a numeric guard.
|
|
2479
|
+
if (isJsSourceFile(node.getSourceFile()) && (lowerer.typeOf(node).flags & ts.TypeFlags.Never) !== 0)
|
|
2480
|
+
return expr;
|
|
2250
2481
|
const narrowed = lowerer.mapTypeOf(lowerer.typeOf(node));
|
|
2251
2482
|
if (narrowed &&
|
|
2252
|
-
(narrowed.kind === "f64" || narrowed.kind === "bool" || narrowed.kind === "string")) {
|
|
2483
|
+
(narrowed.kind === "f64" || narrowed.kind === "bool" || narrowed.kind === "string" || narrowed.kind === "bigint")) {
|
|
2253
2484
|
return { kind: "dynCheck", value: expr, type: narrowed, loc: expr.loc };
|
|
2254
2485
|
}
|
|
2255
|
-
// An
|
|
2256
|
-
//
|
|
2257
|
-
|
|
2258
|
-
if (narrowed?.kind === "bytes" && narrowed.elem === "u8") {
|
|
2486
|
+
// An instanceof narrow retains the native view after validating its
|
|
2487
|
+
// exact element kind. Buffer also matches Uint8Array.
|
|
2488
|
+
if (narrowed?.kind === "bytes" || narrowed?.kind === "url") {
|
|
2259
2489
|
return { kind: "dynCheck", value: expr, type: narrowed, loc: expr.loc };
|
|
2260
2490
|
}
|
|
2261
2491
|
// An `instanceof Error` narrow: the checked-dynamic tree's error encoding rebuilds a
|
|
@@ -2282,6 +2512,11 @@ export function maybeNarrow(lowerer, expr, node) {
|
|
|
2282
2512
|
}
|
|
2283
2513
|
if (expr.type.kind !== "union")
|
|
2284
2514
|
return expr;
|
|
2515
|
+
if (ts.isIdentifier(node)) {
|
|
2516
|
+
const symbol = lowerer.resolveValueSymbol(node);
|
|
2517
|
+
if (symbol && lowerer.unstableSwitchBindings.has(symbol))
|
|
2518
|
+
return expr;
|
|
2519
|
+
}
|
|
2285
2520
|
const narrowedTs = lowerer.typeOf(node);
|
|
2286
2521
|
const narrowed = lowerer.mapTypeOf(narrowedTs);
|
|
2287
2522
|
// `Array.isArray(u)` on a union with one readonly array/tuple arm: the
|
|
@@ -2307,8 +2542,43 @@ export function maybeNarrow(lowerer, expr, node) {
|
|
|
2307
2542
|
return expr;
|
|
2308
2543
|
}
|
|
2309
2544
|
const tag = lowerer.armTag(expr.type.unionId, narrowed);
|
|
2310
|
-
if (tag < 0)
|
|
2545
|
+
if (tag < 0) {
|
|
2546
|
+
// The checker may refine fields inside a discriminated record too.
|
|
2547
|
+
// That creates a different mapped shape, but does not change the
|
|
2548
|
+
// stored payload layout. A required literal discriminator proves
|
|
2549
|
+
// which original arm to read; retain all of that arm's field types.
|
|
2550
|
+
const def = lowerer.unions.get(expr.type.unionId);
|
|
2551
|
+
if (narrowed.kind === "record" && def?.discriminant) {
|
|
2552
|
+
const property = lowerer.checker.getPropertyOfType(narrowedTs, def.discriminant.field);
|
|
2553
|
+
if (property && !(property.flags & (ts.SymbolFlags.Optional | ts.SymbolFlags.GetAccessor | ts.SymbolFlags.SetAccessor))) {
|
|
2554
|
+
const values = literalValues(lowerer.checker.getTypeOfSymbol(property));
|
|
2555
|
+
const arm = values && literalUnionArm(def, values, (id) => lowerer.shapes.get(id));
|
|
2556
|
+
if (arm) {
|
|
2557
|
+
return { kind: "unionNarrow", unionId: expr.type.unionId,
|
|
2558
|
+
tag: lowerer.armTag(expr.type.unionId, arm), value: expr, type: arm, loc: expr.loc };
|
|
2559
|
+
}
|
|
2560
|
+
}
|
|
2561
|
+
}
|
|
2562
|
+
// A base-class arm can narrow to a subclass after instanceof. The
|
|
2563
|
+
// payload retains its base-class tag even when the union also has
|
|
2564
|
+
// records or other classes. Require one unambiguous containing arm.
|
|
2565
|
+
const arms = lowerer.unions.get(expr.type.unionId)?.arms ?? [];
|
|
2566
|
+
const valueTag = narrowed.kind === "object"
|
|
2567
|
+
? arms.findIndex((arm) => arm.kind === "object" && lowerer.isSubclassOf(narrowed.className, arm.className))
|
|
2568
|
+
: -1;
|
|
2569
|
+
const valueType = arms[valueTag];
|
|
2570
|
+
if (narrowed.kind === "object" && valueType?.kind === "object" &&
|
|
2571
|
+
arms.every((arm, i) => i === valueTag || arm.kind !== "object" || !lowerer.isSubclassOf(narrowed.className, arm.className)) &&
|
|
2572
|
+
lowerer.isSubclassOf(narrowed.className, valueType.className)) {
|
|
2573
|
+
return {
|
|
2574
|
+
kind: "downcast",
|
|
2575
|
+
value: { kind: "unionNarrow", unionId: expr.type.unionId, tag: valueTag, value: expr, type: valueType, loc: expr.loc },
|
|
2576
|
+
type: narrowed,
|
|
2577
|
+
loc: expr.loc,
|
|
2578
|
+
};
|
|
2579
|
+
}
|
|
2311
2580
|
return expr;
|
|
2581
|
+
}
|
|
2312
2582
|
return {
|
|
2313
2583
|
kind: "unionNarrow",
|
|
2314
2584
|
unionId: expr.type.unionId,
|
|
@@ -2365,12 +2635,11 @@ function runtimeOptionalReceiverRead(lowerer, expr, local, property, loc) {
|
|
|
2365
2635
|
}
|
|
2366
2636
|
return { kind: "call", callee: helper, args: [varRef(local.id, local.type, loc)], type: narrowed, loc };
|
|
2367
2637
|
}
|
|
2368
|
-
const valueTag = lowerer.armTag(local.type.unionId, narrowed);
|
|
2369
2638
|
const undefTag = lowerer.armTag(local.type.unionId, UNDEFINED_T);
|
|
2370
|
-
if (valueTag < 0 || undefTag < 0)
|
|
2371
|
-
throw new InternalCompilerError("runtime-optional local is missing its value or undefined arm");
|
|
2372
2639
|
const def = lowerer.unions.get(local.type.unionId);
|
|
2373
|
-
if (!def ||
|
|
2640
|
+
if (!def || undefTag < 0)
|
|
2641
|
+
throw new InternalCompilerError("runtime-optional local is missing its undefined arm");
|
|
2642
|
+
if (def.arms.length !== 2) {
|
|
2374
2643
|
// The checker may narrow a runtime-optional capture to one arm of a
|
|
2375
2644
|
// value union (`Circle | Square | undefined` -> `Circle`). The
|
|
2376
2645
|
// two-arm fast path below cannot extract that arm safely: a later
|
|
@@ -2383,10 +2652,19 @@ function runtimeOptionalReceiverRead(lowerer, expr, local, property, loc) {
|
|
|
2383
2652
|
}
|
|
2384
2653
|
return { kind: "call", callee: helper, args: [varRef(local.id, local.type, loc)], type: narrowed, loc };
|
|
2385
2654
|
}
|
|
2655
|
+
// A predicate can strengthen a record's fields or narrow a base class
|
|
2656
|
+
// to a subclass without changing the slot's stored value arm. Extract
|
|
2657
|
+
// that actual arm first: record reads need its original field layout,
|
|
2658
|
+
// and class reads apply the ordinary downcast bridge afterward.
|
|
2659
|
+
const valueTag = undefTag === 0 ? 1 : 0;
|
|
2660
|
+
const valueType = def.arms[valueTag];
|
|
2661
|
+
if (!valueType || isUnitType(valueType)) {
|
|
2662
|
+
lowerer.unsupported("SC1090", expr, "a runtime-optional receiver without a representable value arm");
|
|
2663
|
+
}
|
|
2386
2664
|
const message = property === null
|
|
2387
2665
|
? `${expr.text} is not a function`
|
|
2388
2666
|
: `Cannot read properties of undefined (reading '${property}')`;
|
|
2389
|
-
return {
|
|
2667
|
+
return lowerer.maybeNarrow({
|
|
2390
2668
|
kind: "ternary",
|
|
2391
2669
|
cond: {
|
|
2392
2670
|
kind: "unionIsTag",
|
|
@@ -2397,18 +2675,18 @@ function runtimeOptionalReceiverRead(lowerer, expr, local, property, loc) {
|
|
|
2397
2675
|
type: BOOL,
|
|
2398
2676
|
loc,
|
|
2399
2677
|
},
|
|
2400
|
-
then: nodeThrowExpr(1, "", message,
|
|
2678
|
+
then: nodeThrowExpr(1, "", message, valueType, loc),
|
|
2401
2679
|
else_: {
|
|
2402
2680
|
kind: "unionNarrow",
|
|
2403
2681
|
unionId: local.type.unionId,
|
|
2404
2682
|
tag: valueTag,
|
|
2405
2683
|
value: varRef(local.id, local.type, loc),
|
|
2406
|
-
type:
|
|
2684
|
+
type: valueType,
|
|
2407
2685
|
loc,
|
|
2408
2686
|
},
|
|
2409
|
-
type:
|
|
2687
|
+
type: valueType,
|
|
2410
2688
|
loc,
|
|
2411
|
-
};
|
|
2689
|
+
}, narrowedNode);
|
|
2412
2690
|
}
|
|
2413
2691
|
function runtimeOptionalUseOf(expr) {
|
|
2414
2692
|
let receiver = expr;
|
|
@@ -2669,7 +2947,26 @@ function lowerLooseNullCompare(lowerer, expr, loc) {
|
|
|
2669
2947
|
* results (several non-unit arms) and defaults that change the result
|
|
2670
2948
|
* type are fenced with narrow-first hints. */
|
|
2671
2949
|
export function lowerNullishCoalesce(lowerer, expr, loc) {
|
|
2672
|
-
|
|
2950
|
+
// `a ?? b ?? c` is a left-nested AST. Lower its spine bottom-up so
|
|
2951
|
+
// long dispatch chains do not retain a full expression/binary lowering
|
|
2952
|
+
// frame per operand in a native compiler. The pair lowering still
|
|
2953
|
+
// decides each result layout and preserves the lazy right operand.
|
|
2954
|
+
const parents = [];
|
|
2955
|
+
let first = expr;
|
|
2956
|
+
while (ts.isBinaryExpression(first.left) &&
|
|
2957
|
+
first.left.operatorToken.kind === ts.SyntaxKind.QuestionQuestionToken &&
|
|
2958
|
+
!lowerer.chainRecvByNode.has(first.left)) {
|
|
2959
|
+
parents.push(first);
|
|
2960
|
+
first = first.left;
|
|
2961
|
+
}
|
|
2962
|
+
let result = lowerNullishPair(lowerer, first, first === expr ? loc : locOf(first), lowerAbsenceProbe(lowerer, first.left) ?? lowerer.lowerExpr(first.left));
|
|
2963
|
+
for (let i = parents.length - 1; i >= 0; i--) {
|
|
2964
|
+
const parent = parents[i];
|
|
2965
|
+
result = lowerNullishPair(lowerer, parent, parent === expr ? loc : locOf(parent), result);
|
|
2966
|
+
}
|
|
2967
|
+
return result;
|
|
2968
|
+
}
|
|
2969
|
+
function lowerNullishPair(lowerer, expr, loc, left) {
|
|
2673
2970
|
if (left.type.kind === "dyn") {
|
|
2674
2971
|
// `a ?? b` on a CHECKED-DYNAMIC left: the deciding test is the
|
|
2675
2972
|
// runtime kind (scr_dyn_is_nullish — UNDEF/NULL take the default;
|
|
@@ -2698,8 +2995,14 @@ export function lowerNullishCoalesce(lowerer, expr, loc) {
|
|
|
2698
2995
|
lowerer.badType(expr.left, lowerer.typeOf(expr.left));
|
|
2699
2996
|
if (!def.arms.some(isUnitType))
|
|
2700
2997
|
return left;
|
|
2701
|
-
const type = lowerer.irTypeOf(expr);
|
|
2702
2998
|
const rest = def.arms.filter((a) => !isUnitType(a));
|
|
2999
|
+
// `optionalMap ?? new Map()` carries its element contract on the left;
|
|
3000
|
+
// the empty fallback may use it without constructing a Map<any, any>
|
|
3001
|
+
// arm that has no first-class union representation.
|
|
3002
|
+
const fresh = rest.length === 1 ? lowerer.emptyCollectionFor(expr.right, rest[0]) : null;
|
|
3003
|
+
if (fresh)
|
|
3004
|
+
return { kind: "nullish", left, right: fresh, type: fresh.type, loc };
|
|
3005
|
+
const type = lowerer.irTypeOf(expr);
|
|
2703
3006
|
if (typeEquals(type, left.type) || (rest.length === 1 && typeEquals(type, rest[0]))) {
|
|
2704
3007
|
const right = lowerer.lowerExprExpecting(expr.right, type);
|
|
2705
3008
|
return { kind: "nullish", left, right, type, loc };
|
|
@@ -2707,180 +3010,49 @@ export function lowerNullishCoalesce(lowerer, expr, loc) {
|
|
|
2707
3010
|
if (type.kind !== "union" && rest.length > 1 && rest.some((arm) => typeEquals(arm, type))) {
|
|
2708
3011
|
const helper = lowerer.narrowedArmHelper(left.type.unionId, type, loc);
|
|
2709
3012
|
if (helper !== null) {
|
|
2710
|
-
|
|
2711
|
-
let stable = left;
|
|
2712
|
-
if (!isSafeToRepeat(left)) {
|
|
2713
|
-
const local = lowerer.declareHiddenLocal("%nullish", left.type);
|
|
2714
|
-
stmts.push({ kind: "varDecl", localId: local.id, init: left, loc });
|
|
2715
|
-
stable = varRef(local.id, left.type, loc);
|
|
2716
|
-
}
|
|
2717
|
-
const unitTags = def.arms
|
|
2718
|
-
.map((arm, tag) => ({ arm, tag }))
|
|
2719
|
-
.filter(({ arm }) => isUnitType(arm))
|
|
2720
|
-
.map(({ tag }) => tag);
|
|
2721
|
-
let isNullish = { kind: "unionIsTag", unionId: left.type.unionId, tag: unitTags[0], negated: false, value: stable, type: BOOL, loc };
|
|
2722
|
-
for (const tag of unitTags.slice(1)) {
|
|
2723
|
-
isNullish = {
|
|
2724
|
-
kind: "logical",
|
|
2725
|
-
op: "||",
|
|
2726
|
-
left: isNullish,
|
|
2727
|
-
right: { kind: "unionIsTag", unionId: left.type.unionId, tag, negated: false, value: stable, type: BOOL, loc },
|
|
2728
|
-
type: BOOL,
|
|
2729
|
-
loc,
|
|
2730
|
-
};
|
|
2731
|
-
}
|
|
2732
|
-
const result = {
|
|
2733
|
-
kind: "ternary",
|
|
2734
|
-
cond: isNullish,
|
|
2735
|
-
then: lowerer.lowerExprExpecting(expr.right, type),
|
|
2736
|
-
else_: { kind: "call", callee: helper, args: [stable], type, loc },
|
|
2737
|
-
type,
|
|
2738
|
-
loc,
|
|
2739
|
-
};
|
|
2740
|
-
return stmts.length === 0 ? result : { kind: "seqExpr", stmts, result, type, loc };
|
|
3013
|
+
return nullishBranches(lowerer, left, left.type, def.arms, type, loc, () => lowerer.lowerExprExpecting(expr.right, type), (stable) => ({ kind: "call", callee: helper, args: [stable], type, loc }));
|
|
2741
3014
|
}
|
|
2742
3015
|
}
|
|
2743
|
-
//
|
|
2744
|
-
//
|
|
2745
|
-
//
|
|
2746
|
-
// helper tests the unit tags and re-wraps the payload per arm. The
|
|
2747
|
-
// helper call evaluates the default EAGERLY where JS is lazy, so only
|
|
2748
|
-
// effect-free defaults (the literal null/0/"" spellings) qualify —
|
|
2749
|
-
// anything effectful keeps the fence.
|
|
3016
|
+
// A default may change the union or widen its records. Test the
|
|
3017
|
+
// original tag before coercing: converting the whole left first can
|
|
3018
|
+
// lose nullish arms, and evaluating the default eagerly loses laziness.
|
|
2750
3019
|
if (type.kind === "union") {
|
|
2751
|
-
const
|
|
2752
|
-
|
|
2753
|
-
if (
|
|
2754
|
-
|
|
2755
|
-
|
|
2756
|
-
|
|
2757
|
-
|
|
2758
|
-
|
|
2759
|
-
stable = varRef(local.id, leftT, loc);
|
|
2760
|
-
}
|
|
2761
|
-
const unitTags = def.arms
|
|
2762
|
-
.map((arm, tag) => ({ arm, tag }))
|
|
2763
|
-
.filter(({ arm }) => isUnitType(arm))
|
|
2764
|
-
.map(({ tag }) => tag);
|
|
2765
|
-
let isNullish = {
|
|
2766
|
-
kind: "unionIsTag",
|
|
2767
|
-
unionId: leftT.unionId,
|
|
2768
|
-
tag: unitTags[0],
|
|
2769
|
-
negated: false,
|
|
2770
|
-
value: stable,
|
|
2771
|
-
type: BOOL,
|
|
2772
|
-
loc,
|
|
2773
|
-
};
|
|
2774
|
-
for (const tag of unitTags.slice(1)) {
|
|
2775
|
-
isNullish = {
|
|
2776
|
-
kind: "logical",
|
|
2777
|
-
op: "||",
|
|
2778
|
-
left: isNullish,
|
|
2779
|
-
right: { kind: "unionIsTag", unionId: leftT.unionId, tag, negated: false, value: stable, type: BOOL, loc },
|
|
2780
|
-
type: BOOL,
|
|
2781
|
-
loc,
|
|
2782
|
-
};
|
|
2783
|
-
}
|
|
2784
|
-
let present = null;
|
|
2785
|
-
for (let i = armPairs.length - 1; i >= 0; i--) {
|
|
2786
|
-
const pair = armPairs[i];
|
|
2787
|
-
const wrapped = {
|
|
2788
|
-
kind: "unionWrap",
|
|
2789
|
-
unionId: type.unionId,
|
|
2790
|
-
tag: pair.dst,
|
|
2791
|
-
value: { kind: "unionNarrow", unionId: leftT.unionId, tag: pair.src, value: stable, type: pair.arm, loc },
|
|
2792
|
-
type,
|
|
2793
|
-
loc,
|
|
2794
|
-
};
|
|
2795
|
-
present = present === null
|
|
2796
|
-
? wrapped
|
|
2797
|
-
: {
|
|
2798
|
-
kind: "ternary",
|
|
2799
|
-
cond: { kind: "unionIsTag", unionId: leftT.unionId, tag: pair.src, negated: false, value: stable, type: BOOL, loc },
|
|
2800
|
-
then: wrapped,
|
|
2801
|
-
else_: present,
|
|
2802
|
-
type,
|
|
2803
|
-
loc,
|
|
2804
|
-
};
|
|
2805
|
-
}
|
|
2806
|
-
const result = {
|
|
2807
|
-
kind: "ternary",
|
|
2808
|
-
cond: isNullish,
|
|
2809
|
-
then: lowerer.lowerExprExpecting(expr.right, type),
|
|
2810
|
-
else_: present,
|
|
2811
|
-
type,
|
|
2812
|
-
loc,
|
|
2813
|
-
};
|
|
2814
|
-
return stmts.length === 0 ? result : { kind: "seqExpr", stmts, result, type, loc };
|
|
2815
|
-
}
|
|
2816
|
-
if (armPairs.every((p) => p.src >= 0 && p.dst >= 0)) {
|
|
2817
|
-
const right = lowerer.lowerExprExpecting(expr.right, type);
|
|
2818
|
-
if (isSafeToDiscard(right)) {
|
|
2819
|
-
const helper = nullishRetagHelper(lowerer, leftT, type, loc);
|
|
2820
|
-
return { kind: "call", callee: helper, args: [left, right], type, loc };
|
|
2821
|
-
}
|
|
3020
|
+
const retag = lowerer.unionRetagHelper(left.type.unionId, type.unionId, loc) ??
|
|
3021
|
+
lowerer.narrowedRetagHelper(expr.left, left.type.unionId, type.unionId, loc);
|
|
3022
|
+
if (retag !== null) {
|
|
3023
|
+
// Narrowing may exclude a record/class arm while storage retains
|
|
3024
|
+
// it. The checked retag traps that impossible arm, but only AFTER
|
|
3025
|
+
// the original nullish test: null/undefined must reach the lazy
|
|
3026
|
+
// default even when neither is representable in the result.
|
|
3027
|
+
return nullishBranches(lowerer, left, left.type, def.arms, type, loc, () => lowerer.lowerExprExpecting(expr.right, type), (stable) => ({ kind: "call", callee: retag, args: [stable], type, loc }));
|
|
2822
3028
|
}
|
|
2823
3029
|
}
|
|
2824
3030
|
lowerer.unsupported("SC1090", expr, rest.length !== 1
|
|
2825
3031
|
? `'??' on '${lowerer.fmt(left.type)}' (the non-nullish result is a sub-union; check a discriminant field first)`
|
|
2826
3032
|
: `'??' where the default changes the result type (left is '${lowerer.fmt(left.type)}' but the whole expression is '${lowerer.fmt(type)}' — give both sides one type)`);
|
|
2827
3033
|
}
|
|
2828
|
-
/**
|
|
2829
|
-
|
|
2830
|
-
|
|
2831
|
-
|
|
2832
|
-
function
|
|
2833
|
-
const
|
|
2834
|
-
|
|
2835
|
-
if (
|
|
2836
|
-
|
|
2837
|
-
|
|
2838
|
-
|
|
2839
|
-
|
|
2840
|
-
const
|
|
2841
|
-
|
|
2842
|
-
const
|
|
2843
|
-
|
|
2844
|
-
|
|
2845
|
-
|
|
2846
|
-
|
|
2847
|
-
|
|
2848
|
-
|
|
2849
|
-
|
|
2850
|
-
then: [
|
|
2851
|
-
{
|
|
2852
|
-
kind: "return",
|
|
2853
|
-
value: {
|
|
2854
|
-
kind: "unionWrap",
|
|
2855
|
-
unionId: resT.unionId,
|
|
2856
|
-
tag: dst,
|
|
2857
|
-
value: { kind: "unionNarrow", unionId: leftT.unionId, tag, value: l, type: arm, loc },
|
|
2858
|
-
type: resT,
|
|
2859
|
-
loc,
|
|
2860
|
-
},
|
|
2861
|
-
loc,
|
|
2862
|
-
},
|
|
2863
|
-
],
|
|
2864
|
-
else_: null,
|
|
2865
|
-
loc,
|
|
2866
|
-
});
|
|
2867
|
-
});
|
|
2868
|
-
body.push({ kind: "return", value: r, loc });
|
|
2869
|
-
lowerer.liftedFns.push({
|
|
2870
|
-
name,
|
|
2871
|
-
params: [
|
|
2872
|
-
{ localId: "l.0", name: "l", type: leftT },
|
|
2873
|
-
{ localId: "r.0", name: "r", type: resT },
|
|
2874
|
-
],
|
|
2875
|
-
returnType: resT,
|
|
2876
|
-
locals: [
|
|
2877
|
-
{ id: "l.0", name: "l", type: leftT, mutable: true },
|
|
2878
|
-
{ id: "r.0", name: "r", type: resT, mutable: true },
|
|
2879
|
-
],
|
|
2880
|
-
body,
|
|
2881
|
-
loc,
|
|
2882
|
-
});
|
|
2883
|
-
return name;
|
|
3034
|
+
/** Test the original storage before projecting its present value. Both
|
|
3035
|
+
* branches are lazy, and an effectful receiver is evaluated exactly once.
|
|
3036
|
+
* Keeping the original tags matters when the checker has removed an arm
|
|
3037
|
+
* or the default changes the union's layout. */
|
|
3038
|
+
function nullishBranches(lowerer, left, leftType, arms, type, loc, absent, present) {
|
|
3039
|
+
const stmts = [];
|
|
3040
|
+
let stable = left;
|
|
3041
|
+
if (!isSafeToRepeat(left)) {
|
|
3042
|
+
const local = lowerer.declareHiddenLocal("%nullish", leftType);
|
|
3043
|
+
stmts.push({ kind: "varDecl", localId: local.id, init: left, loc });
|
|
3044
|
+
stable = varRef(local.id, leftType, loc);
|
|
3045
|
+
}
|
|
3046
|
+
const unitTags = arms.flatMap((arm, tag) => isUnitType(arm) ? [tag] : []);
|
|
3047
|
+
let test = null;
|
|
3048
|
+
for (const tag of unitTags) {
|
|
3049
|
+
const part = { kind: "unionIsTag", unionId: leftType.unionId, tag, negated: false, value: stable, type: BOOL, loc };
|
|
3050
|
+
test = test === null ? part : { kind: "logical", op: "||", left: test, right: part, type: BOOL, loc };
|
|
3051
|
+
}
|
|
3052
|
+
if (test === null)
|
|
3053
|
+
throw new InternalCompilerError("nullish branch requires an absent arm");
|
|
3054
|
+
const result = { kind: "ternary", cond: test, then: absent(), else_: present(stable), type, loc };
|
|
3055
|
+
return stmts.length === 0 ? result : { kind: "seqExpr", stmts, result, type, loc };
|
|
2884
3056
|
}
|
|
2885
3057
|
/** A CONDITION-position expression: the result is consumed as a bool
|
|
2886
3058
|
* only, so `&&`/`||` descend recursively over ToBoolean'd operands —
|
|
@@ -2939,14 +3111,23 @@ function lowerPromiseThenPresence(lowerer, access, result) {
|
|
|
2939
3111
|
? (() => {
|
|
2940
3112
|
const arms = lowerer.unions.get(mapped.unionId)?.arms ?? [];
|
|
2941
3113
|
const promises = arms.filter((arm) => arm.kind === "promise");
|
|
2942
|
-
|
|
2943
|
-
|
|
3114
|
+
const promise = promises[0];
|
|
3115
|
+
return promises.length === 1 && promise?.kind === "promise" && arms.every((arm) => arm.kind === "promise" || isUnitType(arm))
|
|
3116
|
+
? promise
|
|
2944
3117
|
: null;
|
|
2945
3118
|
})()
|
|
2946
3119
|
: null;
|
|
2947
3120
|
if (promiseType === null)
|
|
2948
3121
|
return null;
|
|
2949
3122
|
let receiver = lowerer.lowerExpr(access.expression);
|
|
3123
|
+
if (receiver.type.kind === "union" && access.questionDotToken) {
|
|
3124
|
+
const arms = lowerer.unions.get(receiver.type.unionId)?.arms ?? [];
|
|
3125
|
+
if (arms.every((arm) => arm.kind === "promise" || isUnitType(arm))) {
|
|
3126
|
+
return nullishBranches(lowerer, receiver, receiver.type, arms, result.type, locOf(access), () => result.type.kind === "bool"
|
|
3127
|
+
? { kind: "boolLit", value: false, type: BOOL, loc: result.loc }
|
|
3128
|
+
: { kind: "strLit", value: "undefined", type: STRING, loc: result.loc }, () => result);
|
|
3129
|
+
}
|
|
3130
|
+
}
|
|
2950
3131
|
if (receiver.type.kind === "union") {
|
|
2951
3132
|
const optional = lowerer.runtimeOptionalPropertyReceiver(access.expression, receiver, promiseType, access.name.text);
|
|
2952
3133
|
if (optional !== null)
|
|
@@ -2958,6 +3139,29 @@ function lowerPromiseThenPresence(lowerer, access, result) {
|
|
|
2958
3139
|
receiver = { kind: "call", callee: helper, args: [receiver], type: promiseType, loc: receiver.loc };
|
|
2959
3140
|
}
|
|
2960
3141
|
}
|
|
3142
|
+
if (receiver.type.kind === "dyn") {
|
|
3143
|
+
// A specialized JS parameter can retain checked storage while JSDoc
|
|
3144
|
+
// describes a promise. Probe its actual value, preserving nullish
|
|
3145
|
+
// guards and own-property reads on ordinary thenable objects.
|
|
3146
|
+
const loc = locOf(access);
|
|
3147
|
+
const local = lowerer.declareHiddenLocal("%thenReceiver", DYN);
|
|
3148
|
+
const stable = varRef(local.id, DYN, loc);
|
|
3149
|
+
const member = {
|
|
3150
|
+
kind: "dynKeyGet", value: stable,
|
|
3151
|
+
key: { kind: "strLit", value: "then", type: STRING, loc },
|
|
3152
|
+
...(access.questionDotToken ? { optional: true } : {}), type: DYN, loc,
|
|
3153
|
+
};
|
|
3154
|
+
const other = result.type.kind === "bool"
|
|
3155
|
+
? { kind: "dynTest", test: "truthy", value: member, type: BOOL, loc }
|
|
3156
|
+
: { kind: "libCall", fn: "dyn.typeof", args: [member], type: STRING, loc };
|
|
3157
|
+
return {
|
|
3158
|
+
kind: "seqExpr", stmts: [{ kind: "varDecl", localId: local.id, init: receiver, loc }],
|
|
3159
|
+
result: {
|
|
3160
|
+
kind: "ternary", cond: { kind: "dynTest", test: "promise", value: stable, type: BOOL, loc },
|
|
3161
|
+
then: result, else_: other, type: result.type, loc,
|
|
3162
|
+
}, type: result.type, loc,
|
|
3163
|
+
};
|
|
3164
|
+
}
|
|
2961
3165
|
if (receiver.type.kind !== "promise")
|
|
2962
3166
|
return null;
|
|
2963
3167
|
if (isSafeToDiscard(receiver))
|
|
@@ -2980,15 +3184,107 @@ function runtimeOptionalLocalOf(lowerer, node) {
|
|
|
2980
3184
|
return local && lowerer.runtimeOptionalLocals.has(lowerer.runtimeOptionalRootOf(local)) ? local : null;
|
|
2981
3185
|
}
|
|
2982
3186
|
export function runtimeOptionalTrueIds(lowerer, node) {
|
|
2983
|
-
|
|
2984
|
-
|
|
2985
|
-
|
|
2986
|
-
|
|
2987
|
-
|
|
2988
|
-
|
|
2989
|
-
|
|
2990
|
-
const
|
|
2991
|
-
|
|
3187
|
+
return runtimeOptionalGuardIds(lowerer, node, true);
|
|
3188
|
+
}
|
|
3189
|
+
/** Presence proved by one outcome of a boolean guard. Negation flips
|
|
3190
|
+
* the outcome; all operands of a true AND or false OR have that outcome.
|
|
3191
|
+
* Assignments invalidate a syntactic proof even when checker types hide
|
|
3192
|
+
* the undefined produced by an out-of-bounds read. */
|
|
3193
|
+
export function runtimeOptionalGuardIds(lowerer, node, truthy) {
|
|
3194
|
+
const candidates = [];
|
|
3195
|
+
const collect = (expr, outcome) => {
|
|
3196
|
+
if (ts.isParenthesizedExpression(expr))
|
|
3197
|
+
return collect(expr.expression, outcome);
|
|
3198
|
+
if (ts.isPrefixUnaryExpression(expr) && expr.operator === ts.SyntaxKind.ExclamationToken) {
|
|
3199
|
+
return collect(expr.operand, outcome === null ? null : !outcome);
|
|
3200
|
+
}
|
|
3201
|
+
if (ts.isBinaryExpression(expr) &&
|
|
3202
|
+
(outcome === true && expr.operatorToken.kind === ts.SyntaxKind.AmpersandAmpersandToken ||
|
|
3203
|
+
outcome === false && expr.operatorToken.kind === ts.SyntaxKind.BarBarToken)) {
|
|
3204
|
+
collect(expr.left, outcome);
|
|
3205
|
+
collect(expr.right, outcome);
|
|
3206
|
+
}
|
|
3207
|
+
else if (ts.isPropertyAccessExpression(expr) && !expr.questionDotToken) {
|
|
3208
|
+
// A completed ordinary member read checked its receiver even if
|
|
3209
|
+
// the property's value was falsy. Optional chains make no such
|
|
3210
|
+
// promise, and calls/captures retain their separate checked ABI.
|
|
3211
|
+
const local = runtimeOptionalLocalOf(lowerer, expr.expression);
|
|
3212
|
+
if (local !== null)
|
|
3213
|
+
candidates.push(local);
|
|
3214
|
+
else if (ts.isPropertyAccessExpression(expr.expression))
|
|
3215
|
+
collect(expr.expression, outcome);
|
|
3216
|
+
}
|
|
3217
|
+
else if (ts.isBinaryExpression(expr) &&
|
|
3218
|
+
expr.operatorToken.kind !== ts.SyntaxKind.AmpersandAmpersandToken &&
|
|
3219
|
+
expr.operatorToken.kind !== ts.SyntaxKind.BarBarToken &&
|
|
3220
|
+
expr.operatorToken.kind !== ts.SyntaxKind.QuestionQuestionToken) {
|
|
3221
|
+
// Both operands of a comparison/arithmetic operator are evaluated.
|
|
3222
|
+
// Only member reads prove presence; bare identifiers need truth.
|
|
3223
|
+
const member = (operand) => {
|
|
3224
|
+
while (ts.isParenthesizedExpression(operand))
|
|
3225
|
+
operand = operand.expression;
|
|
3226
|
+
if (ts.isPropertyAccessExpression(operand))
|
|
3227
|
+
collect(operand, outcome);
|
|
3228
|
+
};
|
|
3229
|
+
member(expr.left);
|
|
3230
|
+
member(expr.right);
|
|
3231
|
+
const op = expr.operatorToken.kind;
|
|
3232
|
+
const equal = op === ts.SyntaxKind.EqualsEqualsEqualsToken || op === ts.SyntaxKind.EqualsEqualsToken;
|
|
3233
|
+
const unequal = op === ts.SyntaxKind.ExclamationEqualsEqualsToken || op === ts.SyntaxKind.ExclamationEqualsToken;
|
|
3234
|
+
if ((equal && outcome === false) || (unequal && outcome === true)) {
|
|
3235
|
+
const present = (value, unit) => {
|
|
3236
|
+
while (ts.isParenthesizedExpression(unit))
|
|
3237
|
+
unit = unit.expression;
|
|
3238
|
+
const excludesUndefined = ts.isIdentifier(unit) && unit.text === "undefined" &&
|
|
3239
|
+
(lowerer.typeOf(unit).flags & ts.TypeFlags.Undefined) !== 0 ||
|
|
3240
|
+
unit.kind === ts.SyntaxKind.NullKeyword &&
|
|
3241
|
+
(op === ts.SyntaxKind.EqualsEqualsToken || op === ts.SyntaxKind.ExclamationEqualsToken);
|
|
3242
|
+
if (excludesUndefined) {
|
|
3243
|
+
const local = runtimeOptionalLocalOf(lowerer, value);
|
|
3244
|
+
if (local !== null)
|
|
3245
|
+
candidates.push(local);
|
|
3246
|
+
}
|
|
3247
|
+
};
|
|
3248
|
+
present(expr.left, expr.right);
|
|
3249
|
+
present(expr.right, expr.left);
|
|
3250
|
+
}
|
|
3251
|
+
}
|
|
3252
|
+
else if (outcome) {
|
|
3253
|
+
const local = runtimeOptionalLocalOf(lowerer, expr);
|
|
3254
|
+
if (local !== null)
|
|
3255
|
+
candidates.push(local);
|
|
3256
|
+
}
|
|
3257
|
+
};
|
|
3258
|
+
collect(node, truthy);
|
|
3259
|
+
if (candidates.length === 0)
|
|
3260
|
+
return candidates;
|
|
3261
|
+
const assigned = new Set();
|
|
3262
|
+
const visit = (part) => {
|
|
3263
|
+
if (ts.isFunctionLike(part))
|
|
3264
|
+
return;
|
|
3265
|
+
let target = null;
|
|
3266
|
+
if (ts.isBinaryExpression(part) && part.operatorToken.kind >= ts.SyntaxKind.FirstAssignment &&
|
|
3267
|
+
part.operatorToken.kind <= ts.SyntaxKind.LastAssignment)
|
|
3268
|
+
target = part.left;
|
|
3269
|
+
if ((ts.isPrefixUnaryExpression(part) || ts.isPostfixUnaryExpression(part)) &&
|
|
3270
|
+
(part.operator === ts.SyntaxKind.PlusPlusToken || part.operator === ts.SyntaxKind.MinusMinusToken))
|
|
3271
|
+
target = part.operand;
|
|
3272
|
+
if (target !== null) {
|
|
3273
|
+
// Destructuring assignments can contain several identifiers.
|
|
3274
|
+
const mark = (name) => {
|
|
3275
|
+
if (ts.isIdentifier(name)) {
|
|
3276
|
+
const local = lowerer.resolveLocal(name);
|
|
3277
|
+
if (local)
|
|
3278
|
+
assigned.add(lowerer.runtimeOptionalRootOf(local));
|
|
3279
|
+
}
|
|
3280
|
+
ts.forEachChild(name, mark);
|
|
3281
|
+
};
|
|
3282
|
+
mark(target);
|
|
3283
|
+
}
|
|
3284
|
+
ts.forEachChild(part, visit);
|
|
3285
|
+
};
|
|
3286
|
+
visit(node);
|
|
3287
|
+
return candidates.filter((local) => !assigned.has(lowerer.runtimeOptionalRootOf(local)));
|
|
2992
3288
|
}
|
|
2993
3289
|
export function withRuntimeOptionalNarrowed(lowerer, locals, fn) {
|
|
2994
3290
|
if (locals.length === 0)
|
|
@@ -3072,6 +3368,15 @@ export function eqComparableUnion(lowerer, unionId) {
|
|
|
3072
3368
|
export function lowerIntrinsicProperty(lowerer, expr) {
|
|
3073
3369
|
if (lowerer.chainBlocked(expr))
|
|
3074
3370
|
return null;
|
|
3371
|
+
if (lowerer.stdlibGlobalMember(expr, "Array") === "prototype")
|
|
3372
|
+
return { kind: "libCall", fn: "dyn.arrayPrototype", args: [], type: DYN, loc: locOf(expr) };
|
|
3373
|
+
if (lowerer.stdlibGlobalMember(expr, "Array") === "isArray")
|
|
3374
|
+
return lowerArrayIsArrayValue(lowerer, locOf(expr));
|
|
3375
|
+
if (stdlibGlobalNameOf(lowerer, expr.expression) === "Object") {
|
|
3376
|
+
const fn = own(OBJECT_CALLABLE_VALUES, expr.name.text);
|
|
3377
|
+
if (fn)
|
|
3378
|
+
return lowerer.lowerNativeCallableValue(fn, `Object.${expr.name.text}`, locOf(expr));
|
|
3379
|
+
}
|
|
3075
3380
|
// A never-tainted JS receiver type lowered checked-dynamic
|
|
3076
3381
|
// (neverTaintedJsType — `cmd.length` on `const cmd = ['pwd', []]`):
|
|
3077
3382
|
// stand down so the dyn keyed read below the chain answers, instead
|
|
@@ -3107,6 +3412,8 @@ export function lowerIntrinsicProperty(lowerer, expr) {
|
|
|
3107
3412
|
const name = expr.name.text;
|
|
3108
3413
|
const strictReceiver = (expected) => {
|
|
3109
3414
|
const value = lowerer.lowerExpr(expr.expression);
|
|
3415
|
+
if (value.type.kind === "dyn")
|
|
3416
|
+
return lowerer.coerceInto(expr.expression, value, expected);
|
|
3110
3417
|
return lowerer.runtimeOptionalPropertyReceiver(expr.expression, value, expected, name) ?? value;
|
|
3111
3418
|
};
|
|
3112
3419
|
if (kind === "child") {
|
|
@@ -3327,15 +3634,14 @@ export function lowerIntrinsicProperty(lowerer, expr) {
|
|
|
3327
3634
|
// SEMANTICS.md notes the divergence from Node's Buffer pooling),
|
|
3328
3635
|
// the view's real offset for a DataView. A runtime read, so the
|
|
3329
3636
|
// receiver's evaluation is never discarded.
|
|
3330
|
-
const
|
|
3637
|
+
const expected = lowerer.mapTypeOf(lowerer.typeOf(expr.expression));
|
|
3638
|
+
const receiver = expected?.kind === "bytes" ? strictReceiver(expected) : lowerer.lowerExpr(expr.expression);
|
|
3331
3639
|
return { kind: "bytesIntrinsic", method: "byteOffset", receiver, args: [], type: F64, loc };
|
|
3332
3640
|
}
|
|
3333
3641
|
if (name === "buffer") {
|
|
3334
|
-
|
|
3335
|
-
|
|
3336
|
-
|
|
3337
|
-
lowerer.unsupported("SC1090", expr, "'.buffer' outside new DataView(x.buffer, ...) / Buffer.from(x.buffer, ...) " +
|
|
3338
|
-
"(no free-standing ArrayBuffer value exists; subarray() answers an aliasing view directly)");
|
|
3642
|
+
const expected = lowerer.mapTypeOf(lowerer.typeOf(expr.expression));
|
|
3643
|
+
const receiver = expected?.kind === "bytes" ? strictReceiver(expected) : lowerer.lowerExpr(expr.expression);
|
|
3644
|
+
return { kind: "bytesIntrinsic", method: "buffer", receiver, args: [], type: DYN, loc };
|
|
3339
3645
|
}
|
|
3340
3646
|
if (name === "slice" || name === "subarray" || name === "set" || name === "toString") {
|
|
3341
3647
|
lowerer.unsupported("SC1090", expr, `typed-array methods as values (call '${name}' directly)`);
|
|
@@ -3344,7 +3650,8 @@ export function lowerIntrinsicProperty(lowerer, expr) {
|
|
|
3344
3650
|
}
|
|
3345
3651
|
if (kind === "map") {
|
|
3346
3652
|
if (name === "size") {
|
|
3347
|
-
const
|
|
3653
|
+
const expected = lowerer.mapTypeOf(lowerer.typeOf(expr.expression));
|
|
3654
|
+
const receiver = expected?.kind === "map" ? strictReceiver(expected) : lowerer.lowerExpr(expr.expression);
|
|
3348
3655
|
return { kind: "mapIntrinsic", method: "size", receiver, args: [], type: F64, loc: locOf(expr) };
|
|
3349
3656
|
}
|
|
3350
3657
|
if (MAP_METHODS.has(name) || name === "forEach") {
|
|
@@ -3354,7 +3661,8 @@ export function lowerIntrinsicProperty(lowerer, expr) {
|
|
|
3354
3661
|
}
|
|
3355
3662
|
if (kind === "set") {
|
|
3356
3663
|
if (name === "size") {
|
|
3357
|
-
const
|
|
3664
|
+
const expected = lowerer.mapTypeOf(lowerer.typeOf(expr.expression));
|
|
3665
|
+
const receiver = expected?.kind === "set" ? strictReceiver(expected) : lowerer.lowerExpr(expr.expression);
|
|
3358
3666
|
return { kind: "setIntrinsic", method: "size", receiver, args: [], type: F64, loc: locOf(expr) };
|
|
3359
3667
|
}
|
|
3360
3668
|
if (SET_METHODS.has(name)) {
|
|
@@ -3429,7 +3737,10 @@ export function lowerIntrinsicProperty(lowerer, expr) {
|
|
|
3429
3737
|
export function lowerArrayLiteral(lowerer, expr, expected) {
|
|
3430
3738
|
const loc = locOf(expr);
|
|
3431
3739
|
const ctxType = lowerer.checker.getContextualType(expr);
|
|
3432
|
-
const
|
|
3740
|
+
// `as const satisfies readonly T[]` checks against an array but keeps
|
|
3741
|
+
// the literal's inferred tuple type. Build that shape; an actual array
|
|
3742
|
+
// destination can use the ordinary tuple-to-array coercion afterward.
|
|
3743
|
+
const tsType = underConstAssertion(expr) ? lowerer.typeOf(expr) : ctxType ?? lowerer.typeOf(expr);
|
|
3433
3744
|
let mapped = expected ?? lowerer.mapTypeOf(tsType);
|
|
3434
3745
|
// A JS literal whose OWN inferred type is never-tainted
|
|
3435
3746
|
// (neverTaintedJsType — the evolving `const gb = []`, the mixed
|
|
@@ -3542,13 +3853,13 @@ export function lowerArrayLiteral(lowerer, expr, expected) {
|
|
|
3542
3853
|
// literal constructs the tuple's record shape — one positional field
|
|
3543
3854
|
// per element, source order (which IS index order, so evaluation order
|
|
3544
3855
|
// is JS-exact). tsc has already checked the arity; the recount below
|
|
3545
|
-
// backstops `as` smuggling.
|
|
3856
|
+
// backstops `as` smuggling. Fixed tuple spreads have known positions.
|
|
3546
3857
|
if (mapped?.kind === "record") {
|
|
3547
3858
|
const shape = lowerer.shapes.get(mapped.shapeId);
|
|
3548
3859
|
if (shape?.tuple) {
|
|
3549
3860
|
const spread = expr.elements.find(ts.isSpreadElement);
|
|
3550
3861
|
if (spread) {
|
|
3551
|
-
lowerer
|
|
3862
|
+
return lowerTupleSpreadLiteral(lowerer, expr, mapped, shape);
|
|
3552
3863
|
}
|
|
3553
3864
|
if (expr.elements.length !== shape.fields.length) {
|
|
3554
3865
|
// tsc padded an UNDER-LENGTH literal against an optional-element
|
|
@@ -3594,6 +3905,13 @@ export function lowerArrayLiteral(lowerer, expr, expected) {
|
|
|
3594
3905
|
// to DYN wholesale now (mapType's dyn-element array rule), so the
|
|
3595
3906
|
// literal IS the dyn array.
|
|
3596
3907
|
if (isJsSourceFile(expr.getSourceFile()) || mapped?.kind === "dyn") {
|
|
3908
|
+
if (expr.elements.some(ts.isSpreadElement)) {
|
|
3909
|
+
// Native collection drains return vectors of retained dyn
|
|
3910
|
+
// elements. Reuse the ordinary spread builder, then expose the
|
|
3911
|
+
// resulting array through the established unknown[] representation.
|
|
3912
|
+
const vector = lowerArrayLiteral(lowerer, expr, arrayOf(DYN));
|
|
3913
|
+
return lowerer.coerceInto(expr, vector, DYN);
|
|
3914
|
+
}
|
|
3597
3915
|
const elems = expr.elements.map((el) => {
|
|
3598
3916
|
if (ts.isSpreadElement(el)) {
|
|
3599
3917
|
lowerer.unsupported("SC1090", el, "spread elements in a dynamic (unknown[]) array literal");
|
|
@@ -3644,19 +3962,7 @@ export function lowerArrayLiteral(lowerer, expr, expected) {
|
|
|
3644
3962
|
(ts.isConditionalExpression(srcNode)
|
|
3645
3963
|
? lowerTernary(lowerer, srcNode, type)
|
|
3646
3964
|
: lowerer.lowerExpr(el.expression));
|
|
3647
|
-
|
|
3648
|
-
// insertion order (setIntrinsic toArray); the spread machinery
|
|
3649
|
-
// then copies like any array source.
|
|
3650
|
-
if (src.type.kind === "set" && typeEquals(src.type.elem, type.elem)) {
|
|
3651
|
-
src = {
|
|
3652
|
-
kind: "setIntrinsic",
|
|
3653
|
-
method: "toArray",
|
|
3654
|
-
receiver: src,
|
|
3655
|
-
args: [],
|
|
3656
|
-
type: arrayOf(src.type.elem),
|
|
3657
|
-
loc: locOf(el),
|
|
3658
|
-
};
|
|
3659
|
-
}
|
|
3965
|
+
src = lowerCollectionSpread(lowerer, src, el.expression) ?? src;
|
|
3660
3966
|
// `[...new SymbolIterator]`: a CLASS ITERABLE drains through its
|
|
3661
3967
|
// own protocol into a fresh element array (classIteratorDrainCall
|
|
3662
3968
|
// — an infinite iterator loops forever, exactly Node), and the
|
|
@@ -3688,6 +3994,16 @@ export function lowerArrayLiteral(lowerer, expr, expected) {
|
|
|
3688
3994
|
loc: locOf(el),
|
|
3689
3995
|
};
|
|
3690
3996
|
}
|
|
3997
|
+
// A native checked iterable can supply scalar elements even when
|
|
3998
|
+
// the checker inferred a typed array from its producer. Drain once
|
|
3999
|
+
// and validate the elements before the ordinary spread copy.
|
|
4000
|
+
if (src.type.kind === "dyn" && (type.elem.kind === "dyn" || type.elem.kind === "f64" || type.elem.kind === "string" || type.elem.kind === "bool")) {
|
|
4001
|
+
src = lowerer.coerceInto(el.expression, {
|
|
4002
|
+
kind: "libCall", fn: "dyn.iterPack", args: [src,
|
|
4003
|
+
{ kind: "strLit", value: el.expression.getText(), type: STRING, loc: locOf(el) }],
|
|
4004
|
+
type: DYN, loc: locOf(el),
|
|
4005
|
+
}, type);
|
|
4006
|
+
}
|
|
3691
4007
|
// A same-family array whose ELEMENT lifts (string[] into a
|
|
3692
4008
|
// (string | symbol)[] literal — per-element wrap/width copy):
|
|
3693
4009
|
// the interned width helper reshapes before the spread copies.
|
|
@@ -3817,6 +4133,62 @@ export function lowerArrayLiteral(lowerer, expr, expected) {
|
|
|
3817
4133
|
loc,
|
|
3818
4134
|
};
|
|
3819
4135
|
}
|
|
4136
|
+
/** Build a fixed tuple from positional elements and fixed tuple spreads.
|
|
4137
|
+
* Capture each value in source order: deferring the reads until recordLit
|
|
4138
|
+
* would observe a later element's mutations of the spread source. */
|
|
4139
|
+
function lowerTupleSpreadLiteral(lowerer, expr, type, shape) {
|
|
4140
|
+
const loc = locOf(expr);
|
|
4141
|
+
const byName = new Map(shape.fields.map((field) => [field.name, field.type]));
|
|
4142
|
+
const stmts = [];
|
|
4143
|
+
const fields = [];
|
|
4144
|
+
const append = (node, value) => {
|
|
4145
|
+
const name = String(fields.length);
|
|
4146
|
+
const expected = byName.get(name);
|
|
4147
|
+
if (!expected)
|
|
4148
|
+
lowerer.badType(expr, lowerer.typeOf(expr));
|
|
4149
|
+
const coerced = lowerer.coerceInto(node, value, expected);
|
|
4150
|
+
const temp = lowerer.declareHiddenLocal("%tupleElement", expected);
|
|
4151
|
+
const at = locOf(node);
|
|
4152
|
+
stmts.push({ kind: "varDecl", localId: temp.id, init: coerced, loc: at });
|
|
4153
|
+
fields.push({ name, value: varRef(temp.id, expected, at) });
|
|
4154
|
+
};
|
|
4155
|
+
for (const element of expr.elements) {
|
|
4156
|
+
if (!ts.isSpreadElement(element)) {
|
|
4157
|
+
const expected = byName.get(String(fields.length));
|
|
4158
|
+
if (!expected)
|
|
4159
|
+
lowerer.badType(element, lowerer.typeOf(element));
|
|
4160
|
+
append(element, lowerer.lowerExprExpecting(element, expected));
|
|
4161
|
+
continue;
|
|
4162
|
+
}
|
|
4163
|
+
const source = lowerer.lowerExpr(element.expression);
|
|
4164
|
+
const sourceShape = source.type.kind === "record" ? lowerer.shapes.get(source.type.shapeId) : undefined;
|
|
4165
|
+
if (!sourceShape?.tuple) {
|
|
4166
|
+
// Empty tuples use a zero-length array representation. Preserve a
|
|
4167
|
+
// producing call's effects even though there are no positions to copy.
|
|
4168
|
+
const sourceTs = lowerer.typeOf(element.expression);
|
|
4169
|
+
if (source.type.kind === "array" && lowerer.checker.isTupleType(sourceTs) &&
|
|
4170
|
+
lowerer.checker.getTypeArguments(sourceTs).length === 0) {
|
|
4171
|
+
stmts.push({ kind: "exprStmt", expr: source, loc: locOf(element) });
|
|
4172
|
+
continue;
|
|
4173
|
+
}
|
|
4174
|
+
lowerer.unsupported("SC1090", element, "spreading a variable-length value into a fixed tuple literal");
|
|
4175
|
+
}
|
|
4176
|
+
const temp = lowerer.declareHiddenLocal("%tupleSpread", source.type);
|
|
4177
|
+
const at = locOf(element);
|
|
4178
|
+
stmts.push({ kind: "varDecl", localId: temp.id, init: source, loc: at });
|
|
4179
|
+
const receiver = varRef(temp.id, source.type, at);
|
|
4180
|
+
const positions = [...sourceShape.fields].sort((a, b) => Number(a.name) - Number(b.name));
|
|
4181
|
+
for (const field of positions) {
|
|
4182
|
+
append(element.expression, {
|
|
4183
|
+
kind: "recordGet", obj: receiver, shapeId: sourceShape.id,
|
|
4184
|
+
field: field.name, type: field.type, loc: at,
|
|
4185
|
+
});
|
|
4186
|
+
}
|
|
4187
|
+
}
|
|
4188
|
+
if (fields.length !== shape.fields.length)
|
|
4189
|
+
lowerer.badType(expr, lowerer.typeOf(expr));
|
|
4190
|
+
return { kind: "seqExpr", stmts, result: { kind: "recordLit", fields, type, loc }, type, loc };
|
|
4191
|
+
}
|
|
3820
4192
|
/** Convert an undefined-armed numeric value at an arithmetic use. Ordinary
|
|
3821
4193
|
* reads preserve their tagged value; JavaScript's ToNumber(undefined) is NaN,
|
|
3822
4194
|
* so compounds and binary arithmetic use this checked one-time extraction. */
|
|
@@ -3891,16 +4263,17 @@ export function lowerElementAccess(lowerer, expr) {
|
|
|
3891
4263
|
lowerer.fenceStaticResponseMember(expr, "read");
|
|
3892
4264
|
lowerer.fenceStaticHeadersMember(expr, "read");
|
|
3893
4265
|
lowerer.fenceStaticReadableStreamMember(expr, "read");
|
|
3894
|
-
//
|
|
3895
|
-
//
|
|
3896
|
-
// compile-time bindings, not runtime properties, and none of the
|
|
3897
|
-
// probed web-platform objects exist here — the honest answer is
|
|
3898
|
-
// undefined for every dynamic name (SEMANTICS.md documents the
|
|
3899
|
-
// divergence: statically-known globals are not reachable this way;
|
|
3900
|
-
// the PROPERTY spelling answers the identity token instead — see
|
|
3901
|
-
// lowerPropertyAccess's globalThis rule).
|
|
4266
|
+
// Computed global reads share the stored global object's custom
|
|
4267
|
+
// properties. Symbol keys retain their identity-indexed store.
|
|
3902
4268
|
if (!expr.questionDotToken && stdlibGlobalNameOf(lowerer, expr.expression) === "globalThis") {
|
|
3903
|
-
|
|
4269
|
+
const symbol = globalSymbolKey(lowerer, expr.expression, expr.argumentExpression);
|
|
4270
|
+
if (symbol)
|
|
4271
|
+
return { kind: "libCall", fn: "dyn.globalSymbolGet", args: [symbol], type: DYN, loc: locOf(expr) };
|
|
4272
|
+
const key = lowerer.lowerExpr(expr.argumentExpression);
|
|
4273
|
+
const name = key.type.kind === "string" ? key : {
|
|
4274
|
+
kind: "libCall", fn: "dyn.toStringCoerce", args: [lowerer.coerceInto(expr.argumentExpression, key, DYN)], type: STRING, loc: locOf(expr),
|
|
4275
|
+
};
|
|
4276
|
+
return { kind: "dynKeyGet", value: lowerGlobalValue(lowerer, expr.expression), key: name, type: DYN, loc: locOf(expr) };
|
|
3904
4277
|
}
|
|
3905
4278
|
// `req.headers["x-name"]` — the computed twin of `req.headers.host`
|
|
3906
4279
|
// (the server spoke owns both; the envGet precedent).
|
|
@@ -3913,7 +4286,7 @@ export function lowerElementAccess(lowerer, expr) {
|
|
|
3913
4286
|
// lower to the ONE process.envGet intrinsic. The read narrows like any
|
|
3914
4287
|
// union-typed expression when the checker narrowed this occurrence.
|
|
3915
4288
|
if (lowerer.isProcessEnv(expr.expression)) {
|
|
3916
|
-
const key = lowerer
|
|
4289
|
+
const key = lowerEnvironmentKey(lowerer, expr.argumentExpression);
|
|
3917
4290
|
if (key.type.kind !== "string") {
|
|
3918
4291
|
lowerer.unsupported("SC1090", expr.argumentExpression, "indexing process.env with non-string keys");
|
|
3919
4292
|
}
|
|
@@ -3936,17 +4309,27 @@ export function lowerElementAccess(lowerer, expr) {
|
|
|
3936
4309
|
return lowerer.maybeNarrow(ex, expr);
|
|
3937
4310
|
}
|
|
3938
4311
|
// Symbol-keyed property READS (`this[kLimit]`): when the key is a
|
|
3939
|
-
// statically-resolvable
|
|
3940
|
-
//
|
|
4312
|
+
// statically-resolvable symbol declared as a field of the receiver's
|
|
4313
|
+
// class (classSymbolKeyOf — the countdown.js/OpenTUI idioms), the
|
|
3941
4314
|
// read IS an ordinary field read of the hidden slot. Every other
|
|
3942
4315
|
// symbol key stays fenced: record shapes, runtime-identity keys
|
|
3943
|
-
// (symbol parameters,
|
|
4316
|
+
// (symbol parameters, reassigned bindings), and keys no class declares
|
|
3944
4317
|
// — the layouts are compile-time field lists.
|
|
3945
4318
|
if (lowerer.mapTypeOf(lowerer.typeOf(expr.argumentExpression))?.kind === "symbol") {
|
|
3946
4319
|
const target = symbolFieldTarget(lowerer, expr);
|
|
3947
4320
|
if (target)
|
|
3948
4321
|
return lowerer.maybeNarrow(lowerer.fieldGetExpr(target, locOf(expr), expr), expr);
|
|
3949
|
-
|
|
4322
|
+
let receiver = lowerer.lowerExpr(expr.expression);
|
|
4323
|
+
if (receiver.type.kind === "func") {
|
|
4324
|
+
const fnName = jsFuncNameOf(expr.expression);
|
|
4325
|
+
receiver = { kind: "dynFrom", value: receiver, type: DYN, loc: locOf(expr),
|
|
4326
|
+
...(fnName !== null ? { fnName } : {}) };
|
|
4327
|
+
}
|
|
4328
|
+
if (receiver.type.kind === "dyn") {
|
|
4329
|
+
const key = lowerer.lowerExprExpecting(expr.argumentExpression, DYN);
|
|
4330
|
+
return lowerer.maybeNarrow({ kind: "dynKeyGet", value: receiver, key, type: DYN, loc: locOf(expr) }, expr);
|
|
4331
|
+
}
|
|
4332
|
+
lowerer.unsupported("SC1090", expr, "symbol-keyed property access outside class fields keyed by a stable module-level literal Symbol()/Symbol.for() (other static shapes have no symbol-keyed storage)");
|
|
3950
4333
|
}
|
|
3951
4334
|
// A never-tainted JS receiver type (neverTaintedJsType — `cmd[1]` on
|
|
3952
4335
|
// `const cmd = ['pwd', []]`, whose binding lowered checked-dynamic)
|
|
@@ -3955,6 +4338,16 @@ export function lowerElementAccess(lowerer, expr) {
|
|
|
3955
4338
|
let receiverIr = neverTaintedJsType(lowerer, expr.expression, lowerer.typeOf(expr.expression))
|
|
3956
4339
|
? null
|
|
3957
4340
|
: lowerer.mapTypeOf(lowerer.typeOf(expr.expression));
|
|
4341
|
+
if (isJsSourceFile(expr.getSourceFile())) {
|
|
4342
|
+
const receiver = tryLowerExpression(lowerer, expr.expression);
|
|
4343
|
+
if (receiver?.type.kind === "func" && lowerer.dynConvertible(receiver.type)) {
|
|
4344
|
+
const loc = locOf(expr);
|
|
4345
|
+
const fnName = jsFuncNameOf(expr.expression);
|
|
4346
|
+
const boxed = { kind: "dynFrom", value: receiver, type: DYN, loc, ...(fnName !== null ? { fnName } : {}) };
|
|
4347
|
+
const key = lowerRecordPropertyKey(lowerer, lowerer.lowerExpr(expr.argumentExpression), expr.argumentExpression);
|
|
4348
|
+
return lowerer.maybeNarrow({ kind: "dynKeyGet", key, value: boxed, type: DYN, loc }, expr);
|
|
4349
|
+
}
|
|
4350
|
+
}
|
|
3958
4351
|
// A readonly tuple union under Array.isArray is checker-typed as an
|
|
3959
4352
|
// intersection with any[], whose structural mapping is not the tuple
|
|
3960
4353
|
// shape. maybeNarrow on the receiver uses the runtime tag proof to
|
|
@@ -3963,6 +4356,19 @@ export function lowerElementAccess(lowerer, expr) {
|
|
|
3963
4356
|
const checkerArray = lowerer.checkerArrayValue(expr.expression);
|
|
3964
4357
|
if (checkerArray)
|
|
3965
4358
|
receiverIr = checkerArray.type;
|
|
4359
|
+
if (receiverIr?.kind === "object") {
|
|
4360
|
+
const key = recordKeyLiteralText(expr.argumentExpression) ?? recordKeyTypeLiteralText(lowerer, expr.argumentExpression);
|
|
4361
|
+
if (key !== null) {
|
|
4362
|
+
const target = classFieldTarget(lowerer, expr.expression, receiverIr, key);
|
|
4363
|
+
if (target)
|
|
4364
|
+
return lowerer.maybeNarrow(lowerer.fieldGetExpr(target, locOf(expr), expr), expr);
|
|
4365
|
+
}
|
|
4366
|
+
if (isDynTypedRefType(receiverIr)) {
|
|
4367
|
+
const receiver = lowerer.coerceToExpected(lowerer.lowerExpr(expr.expression), DYN);
|
|
4368
|
+
const key = lowerer.lowerExprExpecting(expr.argumentExpression, DYN);
|
|
4369
|
+
return lowerer.maybeNarrow({ kind: "dynKeyGet", value: receiver, key, type: DYN, loc: locOf(expr) }, expr);
|
|
4370
|
+
}
|
|
4371
|
+
}
|
|
3966
4372
|
if (receiverIr?.kind === "jsval") {
|
|
3967
4373
|
// Dispatch follows the RUNTIME world (383(d)): a checker-'any'
|
|
3968
4374
|
// receiver whose value LOWERED checked-dynamic (`bag.list[0]` where
|
|
@@ -3988,6 +4394,8 @@ export function lowerElementAccess(lowerer, expr) {
|
|
|
3988
4394
|
// read undefined — divergence 4's policy).
|
|
3989
4395
|
if (receiverIr?.kind === "bytes") {
|
|
3990
4396
|
let recv = lowerer.lowerExpr(expr.expression);
|
|
4397
|
+
if (recv.type.kind === "dyn")
|
|
4398
|
+
recv = lowerer.coerceInto(expr.expression, recv, receiverIr);
|
|
3991
4399
|
if (recv.type.kind === "union" && lowerer.armTag(recv.type.unionId, UNDEFINED_T) >= 0) {
|
|
3992
4400
|
const present = lowerer.stripUndefinedArm(recv.type);
|
|
3993
4401
|
const helper = present.kind === "bytes"
|
|
@@ -4004,6 +4412,9 @@ export function lowerElementAccess(lowerer, expr) {
|
|
|
4004
4412
|
if (recv.type.kind === "jsval")
|
|
4005
4413
|
return islandElementRead(lowerer, expr, recv);
|
|
4006
4414
|
const index = lowerer.lowerExpr(expr.argumentExpression);
|
|
4415
|
+
if (index.type.kind === "dyn" && recv.type.kind === "bytes") {
|
|
4416
|
+
return { kind: "dynKeyGet", value: lowerer.coerceInto(expr.expression, recv, DYN), key: index, type: DYN, loc: locOf(expr) };
|
|
4417
|
+
}
|
|
4007
4418
|
if (index.type.kind !== "f64") {
|
|
4008
4419
|
lowerer.unsupported("SC1090", expr.argumentExpression, "indexing with non-number keys");
|
|
4009
4420
|
}
|
|
@@ -4025,6 +4436,17 @@ export function lowerElementAccess(lowerer, expr) {
|
|
|
4025
4436
|
// like the array path.
|
|
4026
4437
|
if (obj.type.kind === "jsval")
|
|
4027
4438
|
return islandElementRead(lowerer, expr, obj);
|
|
4439
|
+
if (obj.type.kind === "dyn") {
|
|
4440
|
+
// Object.entries over a checked value stores each pair in the
|
|
4441
|
+
// dynamic tree even when the checker names a tuple type.
|
|
4442
|
+
const key = lowerRecordPropertyKey(lowerer, lowerer.lowerExpr(expr.argumentExpression), expr.argumentExpression);
|
|
4443
|
+
const optional = hasOptionalChainGuard(expr.expression);
|
|
4444
|
+
return lowerer.maybeNarrow({
|
|
4445
|
+
kind: "dynKeyGet", value: obj, key,
|
|
4446
|
+
...(optional ? { optional: true } : {}),
|
|
4447
|
+
type: DYN, loc: locOf(expr),
|
|
4448
|
+
}, expr);
|
|
4449
|
+
}
|
|
4028
4450
|
if (obj.type.kind === "union" && lowerer.armTag(obj.type.unionId, UNDEFINED_T) >= 0) {
|
|
4029
4451
|
// The checker sees the outer `arrays[i]` as a tuple when
|
|
4030
4452
|
// noUncheckedIndexedAccess is disabled, but the lowered read is
|
|
@@ -4080,21 +4502,28 @@ export function lowerElementAccess(lowerer, expr) {
|
|
|
4080
4502
|
// (`pkg.workspaces.packages["0"]` — the lowering world types the
|
|
4081
4503
|
// unknown-rooted chain `any`); a non-dyn lowering falls through to
|
|
4082
4504
|
// the fences below (re-lowering is pure IR construction).
|
|
4083
|
-
if (receiverIr?.kind === "dyn" || receiverIr === null) {
|
|
4505
|
+
if (receiverIr?.kind === "dyn" || receiverIr?.kind === "union" || receiverIr === null) {
|
|
4084
4506
|
const obj = lowerer.lowerExpr(expr.expression);
|
|
4085
|
-
|
|
4507
|
+
// A generic mapped type can stay unresolved at this body use even
|
|
4508
|
+
// though its instantiated parameter has a concrete record ABI.
|
|
4509
|
+
// Dispatch from that stored shape, as the array fallback below does.
|
|
4510
|
+
if (receiverIr === null && obj.type.kind === "record") {
|
|
4511
|
+
const shape = lowerer.shapes.get(obj.type.shapeId);
|
|
4512
|
+
if (shape && !shape.tuple) {
|
|
4513
|
+
return lowerer.lowerRecordKeyRead(expr, obj.type.shapeId, shape);
|
|
4514
|
+
}
|
|
4515
|
+
}
|
|
4516
|
+
if (obj.type.kind === "dyn" || isDynTypedRefType(obj.type)) {
|
|
4086
4517
|
const rawKey = lowerer.lowerExpr(expr.argumentExpression);
|
|
4087
4518
|
// Number, bool, and DYN keys stringify (ToPropertyKey) — the
|
|
4088
4519
|
// dyn-keyed read `catchWarning[warning.name]` where the property
|
|
4089
4520
|
// chain itself lowered dyn.
|
|
4090
|
-
const key = rawKey.type.kind === "
|
|
4091
|
-
? rawKey
|
|
4092
|
-
:
|
|
4093
|
-
|
|
4094
|
-
: null;
|
|
4095
|
-
if (key) {
|
|
4521
|
+
const key = rawKey.type.kind === "dyn" || rawKey.type.kind === "symbol"
|
|
4522
|
+
? lowerer.coerceToExpected(rawKey, DYN)
|
|
4523
|
+
: lowerRecordPropertyKey(lowerer, rawKey, expr.argumentExpression);
|
|
4524
|
+
if (key.type.kind === "string" || key.type.kind === "dyn") {
|
|
4096
4525
|
const opt = hasOptionalChainGuard(expr.expression);
|
|
4097
|
-
return lowerer.maybeNarrow({ kind: "dynKeyGet", key, ...(opt ? { optional: true } : {}), value: obj, type: DYN, loc: locOf(expr) }, expr);
|
|
4526
|
+
return lowerer.maybeNarrow({ kind: "dynKeyGet", key, ...(opt ? { optional: true } : {}), value: lowerer.coerceToExpected(obj, DYN), type: DYN, loc: locOf(expr) }, expr);
|
|
4098
4527
|
}
|
|
4099
4528
|
}
|
|
4100
4529
|
// A checker-`any` receiver that LOWERS to a static ARRAY
|
|
@@ -4104,6 +4533,9 @@ export function lowerElementAccess(lowerer, expr) {
|
|
|
4104
4533
|
if (obj.type.kind === "array") {
|
|
4105
4534
|
const index = lowerer.lowerExpr(expr.argumentExpression);
|
|
4106
4535
|
if (index.type.kind === "f64") {
|
|
4536
|
+
const safe = lowerSafeIndexRead(lowerer, obj, index, locOf(expr));
|
|
4537
|
+
if (safe)
|
|
4538
|
+
return safe;
|
|
4107
4539
|
return lowerer.maybeNarrow({ kind: "arrayGet", arr: obj, index, type: obj.type.elem, loc: locOf(expr) }, expr);
|
|
4108
4540
|
}
|
|
4109
4541
|
}
|
|
@@ -4126,7 +4558,7 @@ export function lowerElementAccess(lowerer, expr) {
|
|
|
4126
4558
|
}
|
|
4127
4559
|
}
|
|
4128
4560
|
}
|
|
4129
|
-
if (key.type.kind === "string") {
|
|
4561
|
+
if (key.type.kind === "string" || key.type.kind === "f64") {
|
|
4130
4562
|
const lit = ts.isStringLiteral(expr.argumentExpression)
|
|
4131
4563
|
? expr.argumentExpression.text
|
|
4132
4564
|
: null;
|
|
@@ -4134,33 +4566,20 @@ export function lowerElementAccess(lowerer, expr) {
|
|
|
4134
4566
|
if (keyed)
|
|
4135
4567
|
return lowerer.maybeNarrow(keyed, expr);
|
|
4136
4568
|
}
|
|
4137
|
-
// `u?.split(":")[0]` — a NUMBER index over an undefined-armed
|
|
4138
|
-
// array union: the array arm answers its element (invalid indices
|
|
4139
|
-
// trap — divergence 4's policy), unit arms answer undefined (the
|
|
4140
|
-
// chain's short-circuit value). Every non-unit arm must be the
|
|
4141
|
-
// same array type.
|
|
4142
|
-
if (key.type.kind === "f64") {
|
|
4143
|
-
const def = lowerer.unions.get(value.type.unionId);
|
|
4144
|
-
const arrArms = def?.arms.filter((a) => a.kind === "array") ?? [];
|
|
4145
|
-
if (def &&
|
|
4146
|
-
arrArms.length > 0 &&
|
|
4147
|
-
def.arms.every((a) => a.kind === "array" || isUnitType(a)) &&
|
|
4148
|
-
arrArms.every((a) => typeEquals(a, arrArms[0]))) {
|
|
4149
|
-
const elem = arrArms[0].elem;
|
|
4150
|
-
const hasUnit = def.arms.some(isUnitType);
|
|
4151
|
-
const t = !hasUnit
|
|
4152
|
-
? elem
|
|
4153
|
-
: elem.kind === "union"
|
|
4154
|
-
? lowerer.withUndefinedArmOf(elem)
|
|
4155
|
-
: lowerer.withUndefinedArm(elem);
|
|
4156
|
-
if (t) {
|
|
4157
|
-
return lowerer.maybeNarrow({ kind: "unionKeyGet", unionId: value.type.unionId, key, value, type: t, loc: locOf(expr) }, expr);
|
|
4158
|
-
}
|
|
4159
|
-
}
|
|
4160
|
-
}
|
|
4161
4569
|
}
|
|
4162
4570
|
}
|
|
4163
4571
|
if (receiverIr?.kind !== "array") {
|
|
4572
|
+
{
|
|
4573
|
+
const receiver = tryLowerExpression(lowerer, expr.expression);
|
|
4574
|
+
if (receiver?.type.kind === "func" &&
|
|
4575
|
+
canBoxFuncIntoDyn(receiver.type, (id) => lowerer.shapes.get(id), (id) => lowerer.unions.get(id))) {
|
|
4576
|
+
const key = lowerRecordPropertyKey(lowerer, lowerer.lowerExpr(expr.argumentExpression), expr.argumentExpression);
|
|
4577
|
+
if (key.type.kind === "string") {
|
|
4578
|
+
const boxed = { kind: "dynFrom", value: receiver, type: DYN, loc: locOf(expr.expression) };
|
|
4579
|
+
return lowerer.maybeNarrow({ kind: "dynKeyGet", key, value: boxed, type: DYN, loc: locOf(expr) }, expr);
|
|
4580
|
+
}
|
|
4581
|
+
}
|
|
4582
|
+
}
|
|
4164
4583
|
if (receiverIr?.kind === "string") {
|
|
4165
4584
|
// `s[i]` with a number index reads a UTF-16 code unit — charAt's
|
|
4166
4585
|
// exact job (the UTF-16-exact runtime). Without
|
|
@@ -4168,16 +4587,23 @@ export function lowerElementAccess(lowerer, expr) {
|
|
|
4168
4587
|
// and charAt's "" is the only string-typed answer for an
|
|
4169
4588
|
// out-of-range or fractional index where JS reads `undefined` —
|
|
4170
4589
|
// SEMANTICS.md documents the divergence; in-range integer reads
|
|
4171
|
-
// (the loop pattern) are JS-exact.
|
|
4172
|
-
//
|
|
4173
|
-
|
|
4590
|
+
// (the loop pattern) are JS-exact. Optional results instead retain
|
|
4591
|
+
// undefined for missing string properties.
|
|
4592
|
+
const recv = lowerer.lowerExpr(expr.expression);
|
|
4174
4593
|
const index = lowerer.lowerExpr(expr.argumentExpression);
|
|
4175
|
-
|
|
4176
|
-
|
|
4177
|
-
|
|
4594
|
+
const resultType = lowerer.mapTypeOf(lowerer.typeOf(expr));
|
|
4595
|
+
if (index.type.kind === "f64" && recv.type.kind === "string") {
|
|
4596
|
+
if (resultType?.kind === "string") {
|
|
4597
|
+
return { kind: "strIntrinsic", method: "charAt", receiver: recv, args: [index], type: STRING, loc: locOf(expr) };
|
|
4598
|
+
}
|
|
4599
|
+
if (resultType?.kind === "union") {
|
|
4600
|
+
const arms = lowerer.unions.get(resultType.unionId)?.arms;
|
|
4601
|
+
if (arms?.length === 2 && lowerer.armTag(resultType.unionId, STRING) >= 0 && lowerer.armTag(resultType.unionId, UNDEFINED_T) >= 0) {
|
|
4602
|
+
return lowerOptionalStringIndex(lowerer, recv, index, resultType, locOf(expr));
|
|
4603
|
+
}
|
|
4604
|
+
}
|
|
4178
4605
|
}
|
|
4179
|
-
lowerer.unsupported("SC1090", expr, "string indexing with this index/result shape (a number index
|
|
4180
|
-
"use .charAt(i) under noUncheckedIndexedAccess)");
|
|
4606
|
+
lowerer.unsupported("SC1090", expr, "string indexing with this index/result shape (expected a number index and a string or string | undefined result)");
|
|
4181
4607
|
}
|
|
4182
4608
|
lowerer.unsupported("SC1090", expr, "element access on non-array values");
|
|
4183
4609
|
}
|
|
@@ -4255,14 +4681,17 @@ export function lowerElementAccess(lowerer, expr) {
|
|
|
4255
4681
|
// can validate, and fences by name when it is not — an arrayGet over a
|
|
4256
4682
|
// dyn receiver is never emitted (the validator ICE).
|
|
4257
4683
|
if (arr.type.kind === "dyn") {
|
|
4258
|
-
if (
|
|
4684
|
+
if (canDynCheckTo(receiverIr, (id) => lowerer.shapes.get(id), (id) => lowerer.unions.get(id))) {
|
|
4259
4685
|
arr = { kind: "dynCheck", value: arr, type: receiverIr, loc: locOf(expr) };
|
|
4260
4686
|
}
|
|
4261
4687
|
else {
|
|
4262
4688
|
lowerer.unsupported("SC1090", expr, `element access on checked-dynamic values with '${lowerer.fmt(receiverIr)}' elements`);
|
|
4263
4689
|
}
|
|
4264
4690
|
}
|
|
4265
|
-
|
|
4691
|
+
// Array reads can themselves supply an index. A missing numeric value
|
|
4692
|
+
// is undefined, whose property lookup misses just like NaN; keep that
|
|
4693
|
+
// value instead of rejecting a checker-number index with optional IR.
|
|
4694
|
+
const index = lowerOptionalNumber(lowerer, lowerer.lowerExpr(expr.argumentExpression), locOf(expr.argumentExpression), expr.argumentExpression);
|
|
4266
4695
|
if (index.type.kind !== "f64") {
|
|
4267
4696
|
lowerer.unsupported("SC1090", expr.argumentExpression, "indexing with non-number keys");
|
|
4268
4697
|
}
|
|
@@ -4384,6 +4813,26 @@ export function lowerRecordKeyRead(lowerer, expr, shapeId, shape, includeUndefin
|
|
|
4384
4813
|
// to observe the missing value instead of throwing during that check.
|
|
4385
4814
|
return includeUndefined ? read : lowerer.maybeNarrow(read, expr);
|
|
4386
4815
|
}
|
|
4816
|
+
if (litKey !== null) {
|
|
4817
|
+
const nominal = representedClassFieldTarget(lowerer, expr.expression, litKey, obj);
|
|
4818
|
+
if (nominal) {
|
|
4819
|
+
const read = lowerer.fieldGetExpr(nominal, loc, expr);
|
|
4820
|
+
return includeUndefined ? read : lowerer.maybeNarrow(read, expr);
|
|
4821
|
+
}
|
|
4822
|
+
}
|
|
4823
|
+
if (hasClassPayload(lowerer, obj.type)) {
|
|
4824
|
+
lowerer.unsupported("SC1090", expr, "computed property reads through structural views of class instances (use a declared literal key)");
|
|
4825
|
+
}
|
|
4826
|
+
// Predicates can strengthen optional fields without changing the
|
|
4827
|
+
// receiver's stored layout. Use that layout for bracket reads just as
|
|
4828
|
+
// fieldTarget does for the corresponding dot reads.
|
|
4829
|
+
if (obj.type.kind === "record") {
|
|
4830
|
+
const actualShape = lowerer.shapes.get(obj.type.shapeId);
|
|
4831
|
+
if (!actualShape)
|
|
4832
|
+
throw new InternalCompilerError("record receiver is missing its stored shape");
|
|
4833
|
+
shapeId = obj.type.shapeId;
|
|
4834
|
+
shape = actualShape;
|
|
4835
|
+
}
|
|
4387
4836
|
if (litKey !== null) {
|
|
4388
4837
|
const field = shape.fields.find((f) => f.name === litKey);
|
|
4389
4838
|
if (field) {
|
|
@@ -4429,7 +4878,7 @@ export function lowerRecordKeyRead(lowerer, expr, shapeId, shape, includeUndefin
|
|
|
4429
4878
|
let declared = null;
|
|
4430
4879
|
if (shape.indexValue) {
|
|
4431
4880
|
declared = shape.indexValue;
|
|
4432
|
-
if (includeUndefined || lowerer.program.getCompilerOptions().noUncheckedIndexedAccess) {
|
|
4881
|
+
if (declared.kind !== "dyn" && (includeUndefined || lowerer.program.getCompilerOptions().noUncheckedIndexedAccess)) {
|
|
4433
4882
|
declared = lowerer.withUndefinedArmOf(declared);
|
|
4434
4883
|
if (!declared)
|
|
4435
4884
|
lowerer.badType(expr, lowerer.typeOf(expr));
|
|
@@ -4439,6 +4888,8 @@ export function lowerRecordKeyRead(lowerer, expr, shapeId, shape, includeUndefin
|
|
|
4439
4888
|
shape.fields.every((f) => typeEquals(f.type, shape.fields[0].type))) {
|
|
4440
4889
|
declared = shape.fields[0].type;
|
|
4441
4890
|
}
|
|
4891
|
+
if (!declared && isJsSourceFile(expr.getSourceFile()) && recordKeyResultOk(lowerer, shape, DYN))
|
|
4892
|
+
declared = DYN;
|
|
4442
4893
|
if (!declared) {
|
|
4443
4894
|
lowerer.unsupported("SC1090", expr, `dynamic keyed reads of '${lowerer.fmt({ kind: "record", shapeId })}' (the declared fields have no one common type)`);
|
|
4444
4895
|
}
|
|
@@ -4483,6 +4934,10 @@ export function lowerAbsenceProbe(lowerer, node) {
|
|
|
4483
4934
|
}
|
|
4484
4935
|
if (ts.isPropertyAccessExpression(expr)) {
|
|
4485
4936
|
const target = lowerer.fieldTarget(expr);
|
|
4937
|
+
if (target?.container === "class" && target.fieldType.kind === "union" &&
|
|
4938
|
+
lowerer.unions.get(target.fieldType.unionId)?.arms.some(isUnitType)) {
|
|
4939
|
+
return lowerer.fieldGetExpr(target, locOf(expr), expr);
|
|
4940
|
+
}
|
|
4486
4941
|
if (target?.container !== "recordOvf")
|
|
4487
4942
|
return null;
|
|
4488
4943
|
// A JS file-scope object-literal global is record-shaped to the
|
|
@@ -4584,6 +5039,7 @@ function recordKeyTypeLiteralText(lowerer, node) {
|
|
|
4584
5039
|
function recordKeyResultOk(lowerer, shape, type) {
|
|
4585
5040
|
const surfaces = (t) => typeEquals(t, type) ||
|
|
4586
5041
|
(type.kind === "union" && lowerer.armTag(type.unionId, t) >= 0) ||
|
|
5042
|
+
exactUnionWidening(lowerer, t, type) ||
|
|
4587
5043
|
(type.kind === "dyn" && lowerer.dynConvertible(t));
|
|
4588
5044
|
if (!shape.fields.every((f) => surfaces(f.type)))
|
|
4589
5045
|
return false;
|
|
@@ -4595,6 +5051,13 @@ function recordKeyResultOk(lowerer, shape, type) {
|
|
|
4595
5051
|
}
|
|
4596
5052
|
return true;
|
|
4597
5053
|
}
|
|
5054
|
+
function exactUnionWidening(lowerer, from, to) {
|
|
5055
|
+
if (from.kind !== "union" || to.kind !== "union")
|
|
5056
|
+
return false;
|
|
5057
|
+
const source = lowerer.unions.get(from.unionId);
|
|
5058
|
+
const target = lowerer.unions.get(to.unionId);
|
|
5059
|
+
return !!source && !!target && unionWideningTags(source.arms, target.arms) !== null;
|
|
5060
|
+
}
|
|
4598
5061
|
/** The literal index of a tuple access, or null when the expression isn't
|
|
4599
5062
|
* a non-negative integer literal (parentheses tolerated — tsc's own
|
|
4600
5063
|
* literal-index typing accepts them). */
|
|
@@ -4679,16 +5142,71 @@ export function lowerElementCompound(lowerer, expr, op) {
|
|
|
4679
5142
|
loc,
|
|
4680
5143
|
};
|
|
4681
5144
|
}
|
|
4682
|
-
/**
|
|
4683
|
-
*
|
|
5145
|
+
/** Checked member assignment evaluates the reference before its RHS and
|
|
5146
|
+
* yields the original assigned value, including in a chained assignment. */
|
|
5147
|
+
function lowerDynMemberAssignment(lowerer, expr, recv) {
|
|
5148
|
+
const loc = locOf(expr);
|
|
5149
|
+
const target = expr.left;
|
|
5150
|
+
const receiver = lowerer.declareHiddenLocal("%setReceiver", DYN);
|
|
5151
|
+
const keyValue = ts.isElementAccessExpression(target)
|
|
5152
|
+
? lowerer.lowerExprExpecting(target.argumentExpression, DYN)
|
|
5153
|
+
: lowerer.coerceToExpected({ kind: "strLit", value: target.name.text, type: STRING, loc }, DYN);
|
|
5154
|
+
const key = lowerer.declareHiddenLocal("%setKey", DYN);
|
|
5155
|
+
const raw = lowerer.lowerExpr(expr.right);
|
|
5156
|
+
const value = raw.type.kind === "void"
|
|
5157
|
+
? {
|
|
5158
|
+
kind: "seqExpr", stmts: [{ kind: "exprStmt", expr: raw, loc }],
|
|
5159
|
+
result: dynUndefinedExpr(loc), type: DYN, loc,
|
|
5160
|
+
}
|
|
5161
|
+
: isUnitType(raw.type) ? lowerer.coerceInto(expr.right, raw, DYN) : raw;
|
|
5162
|
+
const assigned = lowerer.declareHiddenLocal("%setValue", value.type);
|
|
5163
|
+
const result = varRef(assigned.id, value.type, loc);
|
|
5164
|
+
const stored = lowerer.coerceInto(expr.right, result, DYN);
|
|
5165
|
+
// Evaluate the reference before the RHS, but defer key coercion until
|
|
5166
|
+
// PutValue. Yield the RHS without rereading a setter-backed property.
|
|
5167
|
+
return {
|
|
5168
|
+
kind: "seqExpr", stmts: [
|
|
5169
|
+
{ kind: "varDecl", localId: receiver.id, init: recv, loc },
|
|
5170
|
+
{ kind: "varDecl", localId: key.id, init: keyValue, loc },
|
|
5171
|
+
{ kind: "varDecl", localId: assigned.id, init: value, loc },
|
|
5172
|
+
{ kind: "exprStmt", expr: { kind: "libCall", fn: "dyn.keySetComputed", args: [varRef(receiver.id, DYN, loc), varRef(key.id, DYN, loc), stored], type: VOID, loc }, loc },
|
|
5173
|
+
], result, type: result.type, loc,
|
|
5174
|
+
};
|
|
5175
|
+
}
|
|
5176
|
+
/** JavaScript callable members live on the closure, so aliases and chained
|
|
5177
|
+
* writes share the same storage as Object.defineProperties. */
|
|
5178
|
+
export function lowerNativeFunctionAssignment(lowerer, expr) {
|
|
5179
|
+
const target = expr.left;
|
|
5180
|
+
if (!isJsSourceFile(expr.getSourceFile()) ||
|
|
5181
|
+
(!ts.isPropertyAccessExpression(target) && !ts.isElementAccessExpression(target)) ||
|
|
5182
|
+
target.questionDotToken)
|
|
5183
|
+
return null;
|
|
5184
|
+
const receiver = tryLowerExpression(lowerer, target.expression);
|
|
5185
|
+
if (receiver?.type.kind !== "func" ||
|
|
5186
|
+
!canBoxFuncIntoDyn(receiver.type, (id) => lowerer.shapes.get(id), (id) => lowerer.unions.get(id)))
|
|
5187
|
+
return null;
|
|
5188
|
+
fenceNodeModuleMutation(lowerer, target, "assignment");
|
|
5189
|
+
const fnName = jsFuncNameOf(target.expression);
|
|
5190
|
+
const boxed = {
|
|
5191
|
+
kind: "dynFrom", value: receiver, type: DYN, loc: locOf(target.expression),
|
|
5192
|
+
...(fnName !== null ? { fnName } : {}),
|
|
5193
|
+
};
|
|
5194
|
+
return lowerDynMemberAssignment(lowerer, expr, boxed);
|
|
5195
|
+
}
|
|
4684
5196
|
export function lowerElementWrite(lowerer, expr) {
|
|
4685
5197
|
const target = expr.left;
|
|
4686
5198
|
fenceNodeModuleMutation(lowerer, target, "assignment");
|
|
5199
|
+
const globalKey = globalSymbolKey(lowerer, target.expression, target.argumentExpression);
|
|
5200
|
+
if (globalKey) {
|
|
5201
|
+
const value = lowerer.lowerExprExpecting(expr.right, DYN);
|
|
5202
|
+
const loc = locOf(expr);
|
|
5203
|
+
return { kind: "exprStmt", expr: { kind: "libCall", fn: "dyn.globalSymbolSet", args: [globalKey, value], type: VOID, loc }, loc };
|
|
5204
|
+
}
|
|
4687
5205
|
// `process.env[key] = v` — the computed twin of the dotted env write:
|
|
4688
5206
|
// setenv(3), string keys and string values only.
|
|
4689
5207
|
if (lowerer.isProcessEnv(target.expression) && !target.questionDotToken) {
|
|
4690
5208
|
const loc = locOf(expr);
|
|
4691
|
-
const key = lowerer
|
|
5209
|
+
const key = lowerEnvironmentKey(lowerer, target.argumentExpression);
|
|
4692
5210
|
if (key.type.kind !== "string") {
|
|
4693
5211
|
lowerer.unsupported("SC1090", target.argumentExpression, "indexing process.env with non-string keys");
|
|
4694
5212
|
}
|
|
@@ -4712,9 +5230,21 @@ export function lowerElementWrite(lowerer, expr) {
|
|
|
4712
5230
|
const value = lowerer.lowerExprExpecting(expr.right, fieldT.fieldType);
|
|
4713
5231
|
return lowerer.fieldSetStmt(fieldT, value, locOf(expr), target);
|
|
4714
5232
|
}
|
|
4715
|
-
|
|
5233
|
+
const receiver = lowerer.lowerExpr(target.expression);
|
|
5234
|
+
if (receiver.type.kind === "dyn")
|
|
5235
|
+
return { kind: "exprStmt", expr: lowerDynMemberAssignment(lowerer, expr, receiver), loc: locOf(expr) };
|
|
5236
|
+
lowerer.unsupported("SC1090", target, "symbol-keyed property writes outside class fields keyed by a stable module-level literal Symbol()/Symbol.for() (other static shapes have no symbol-keyed storage)");
|
|
5237
|
+
}
|
|
5238
|
+
const checkedReceiver = tryLowerExpression(lowerer, target.expression);
|
|
5239
|
+
if (checkedReceiver?.type.kind === "dyn") {
|
|
5240
|
+
return { kind: "exprStmt", expr: lowerDynMemberAssignment(lowerer, expr, checkedReceiver), loc: locOf(expr) };
|
|
5241
|
+
}
|
|
5242
|
+
let receiverIr = lowerer.mapTypeOf(lowerer.typeOf(target.expression));
|
|
5243
|
+
if (receiverIr === null || receiverIr.kind === "dyn") {
|
|
5244
|
+
const receiver = tryLowerExpression(lowerer, target.expression);
|
|
5245
|
+
if (receiver?.type.kind === "array")
|
|
5246
|
+
receiverIr = receiver.type;
|
|
4716
5247
|
}
|
|
4717
|
-
const receiverIr = lowerer.mapTypeOf(lowerer.typeOf(target.expression));
|
|
4718
5248
|
if (receiverIr?.kind === "jsval") {
|
|
4719
5249
|
const obj = lowerer.lowerExpr(target.expression);
|
|
4720
5250
|
// Dispatch follows the RUNTIME world (383(d)): a checker-'any'
|
|
@@ -4756,9 +5286,7 @@ export function lowerElementWrite(lowerer, expr) {
|
|
|
4756
5286
|
const obj = lowerer.lowerExpr(target.expression);
|
|
4757
5287
|
if (obj.type.kind === "dyn") {
|
|
4758
5288
|
const loc = locOf(expr);
|
|
4759
|
-
|
|
4760
|
-
if (key.type.kind === "f64")
|
|
4761
|
-
key = lowerer.ensureString(key, target.argumentExpression);
|
|
5289
|
+
const key = lowerRecordPropertyKey(lowerer, lowerer.lowerExpr(target.argumentExpression), target.argumentExpression);
|
|
4762
5290
|
if (key.type.kind !== "string") {
|
|
4763
5291
|
lowerer.unsupported("SC1090", target.argumentExpression, "indexing with non-string or non-number keys");
|
|
4764
5292
|
}
|
|
@@ -4769,6 +5297,20 @@ export function lowerElementWrite(lowerer, expr) {
|
|
|
4769
5297
|
return { kind: "exprStmt", expr: { kind: "libCall", fn: "dyn.keySet", args: [obj, key, value], type: VOID, loc }, loc };
|
|
4770
5298
|
}
|
|
4771
5299
|
}
|
|
5300
|
+
if (receiverIr?.kind === "object" || receiverIr?.kind === "record") {
|
|
5301
|
+
const key = recordKeyLiteralText(target.argumentExpression) ?? recordKeyTypeLiteralText(lowerer, target.argumentExpression);
|
|
5302
|
+
if (key !== null) {
|
|
5303
|
+
const receiver = lowerer.lowerExpr(target.expression);
|
|
5304
|
+
const field = representedClassFieldTarget(lowerer, target.expression, key, receiver);
|
|
5305
|
+
if (field) {
|
|
5306
|
+
const value = lowerer.lowerExprExpecting(expr.right, field.fieldType);
|
|
5307
|
+
return lowerer.fieldSetStmt(field, value, locOf(expr), target);
|
|
5308
|
+
}
|
|
5309
|
+
if (hasClassPayload(lowerer, receiver.type)) {
|
|
5310
|
+
lowerer.unsupported("SC1090", target, "writing undeclared properties through structural views of class instances");
|
|
5311
|
+
}
|
|
5312
|
+
}
|
|
5313
|
+
}
|
|
4772
5314
|
// Typed-array element write `b[i] = v` — bytesSet: the value is an f64
|
|
4773
5315
|
// the runtime coerces JS-exactly; invalid indices trap (no appends —
|
|
4774
5316
|
// typed arrays are fixed-length; JS would ignore the write).
|
|
@@ -4783,6 +5325,9 @@ export function lowerElementWrite(lowerer, expr) {
|
|
|
4783
5325
|
return { kind: "exprStmt", expr: { kind: "jsOp", op: "setIdx", args: [recv, key, value], type: VOID, loc }, loc };
|
|
4784
5326
|
}
|
|
4785
5327
|
const index = lowerer.lowerExpr(target.argumentExpression);
|
|
5328
|
+
if (index.type.kind === "dyn" && recv.type.kind === "bytes") {
|
|
5329
|
+
return { kind: "exprStmt", expr: lowerDynMemberAssignment(lowerer, expr, lowerer.coerceInto(target.expression, recv, DYN)), loc: locOf(expr) };
|
|
5330
|
+
}
|
|
4786
5331
|
if (index.type.kind !== "f64") {
|
|
4787
5332
|
lowerer.unsupported("SC1090", target.argumentExpression, "indexing with non-number keys");
|
|
4788
5333
|
}
|
|
@@ -4798,6 +5343,9 @@ export function lowerElementWrite(lowerer, expr) {
|
|
|
4798
5343
|
// a recordSet/recordKeySet over a jsval.
|
|
4799
5344
|
if (shape) {
|
|
4800
5345
|
const obj = lowerer.lowerExpr(target.expression);
|
|
5346
|
+
if (hasClassPayload(lowerer, obj.type)) {
|
|
5347
|
+
lowerer.unsupported("SC1090", target, "computed property writes through structural views of class instances (use a declared literal key)");
|
|
5348
|
+
}
|
|
4801
5349
|
if (obj.type.kind === "jsval") {
|
|
4802
5350
|
const key = lowerer.jsvalIn(lowerer.lowerExpr(target.argumentExpression), target.argumentExpression);
|
|
4803
5351
|
const value = lowerer.jsvalIn(lowerer.lowerExpr(expr.right), expr.right);
|
|
@@ -4949,9 +5497,9 @@ export function lowerElementWrite(lowerer, expr) {
|
|
|
4949
5497
|
// type (static `any` — mapTypeOf answers null without --dynamic)
|
|
4950
5498
|
// probes the receiver's own lowered world: a dyn value takes the same
|
|
4951
5499
|
// write, anything else falls through to the fences.
|
|
4952
|
-
if (receiverIr?.kind === "dyn" || receiverIr === null) {
|
|
5500
|
+
if (receiverIr?.kind === "dyn" || receiverIr?.kind === "object" || receiverIr === null) {
|
|
4953
5501
|
const obj = receiverIr !== null ? lowerer.lowerExpr(target.expression) : tryLowerExpression(lowerer, target.expression);
|
|
4954
|
-
if (obj !== null && obj.type.kind === "dyn") {
|
|
5502
|
+
if (obj !== null && (obj.type.kind === "dyn" || isDynTypedRefType(obj.type))) {
|
|
4955
5503
|
const loc = locOf(expr);
|
|
4956
5504
|
const litKey = recordKeyLiteralText(target.argumentExpression);
|
|
4957
5505
|
let key = litKey !== null
|
|
@@ -4965,7 +5513,7 @@ export function lowerElementWrite(lowerer, expr) {
|
|
|
4965
5513
|
if (value.type.kind !== "dyn") {
|
|
4966
5514
|
lowerer.unsupported("SC1101", expr.right, `storing '${lowerer.fmt(value.type)}' values in a checked-dynamic object (the value cannot convert into the checked-dynamic tree)`);
|
|
4967
5515
|
}
|
|
4968
|
-
return { kind: "exprStmt", expr: { kind: "libCall", fn: "dyn.keySet", args: [obj, key, value], type: VOID, loc }, loc };
|
|
5516
|
+
return { kind: "exprStmt", expr: { kind: "libCall", fn: "dyn.keySet", args: [lowerer.coerceToExpected(obj, DYN), key, value], type: VOID, loc }, loc };
|
|
4969
5517
|
}
|
|
4970
5518
|
}
|
|
4971
5519
|
if (receiverIr?.kind !== "array") {
|
|
@@ -4995,19 +5543,42 @@ export function lowerElementWrite(lowerer, expr) {
|
|
|
4995
5543
|
if (arr.type.kind !== "array") {
|
|
4996
5544
|
lowerer.unsupported("SC1090", target.expression, "assignment through a possibly missing nested array receiver");
|
|
4997
5545
|
}
|
|
4998
|
-
const index = lowerer.lowerExpr(target.argumentExpression);
|
|
5546
|
+
const index = lowerOptionalNumber(lowerer, lowerer.lowerExpr(target.argumentExpression), locOf(target.argumentExpression), target.argumentExpression);
|
|
4999
5547
|
if (index.type.kind !== "f64") {
|
|
5000
5548
|
lowerer.unsupported("SC1090", target.argumentExpression, "indexing with non-number keys");
|
|
5001
5549
|
}
|
|
5002
5550
|
// Optional array reads carry the source array's payload type plus an
|
|
5003
5551
|
// undefined arm. arrayValueStore turns that arm into the runtime's
|
|
5004
5552
|
// UNDEFINED state while keeping number[]/string[] payload storage scalar.
|
|
5005
|
-
|
|
5006
|
-
|
|
5553
|
+
let literal = expr.right;
|
|
5554
|
+
while (ts.isParenthesizedExpression(literal) || ts.isSatisfiesExpression(literal))
|
|
5555
|
+
literal = literal.expression;
|
|
5556
|
+
const value = ts.isObjectLiteralExpression(literal) || ts.isArrayLiteralExpression(literal)
|
|
5557
|
+
? lowerer.lowerExprExpecting(literal, arr.type.elem) : lowerer.lowerExpr(expr.right);
|
|
5558
|
+
// Validate the payload before emitting a native store. A dormant JS
|
|
5559
|
+
// method can contradict its JSDoc element type; retain its call-time
|
|
5560
|
+
// refusal instead of leaving invalid IR in a newly reachable method.
|
|
5561
|
+
const expected = value.type.kind === "undefinedT" ||
|
|
5562
|
+
value.type.kind === "union" && lowerer.armTag(value.type.unionId, UNDEFINED_T) >= 0
|
|
5563
|
+
? arrayValueType(lowerer, arr.type.elem) : arr.type.elem;
|
|
5564
|
+
return arrayValueStore(lowerer, arr, index, lowerer.coerceInto(expr.right, value, expected), arr.type.elem, locOf(expr));
|
|
5565
|
+
}
|
|
5566
|
+
/** Environment property names use ToPrimitive with the string hint, so
|
|
5567
|
+
* checked objects must execute their own conversion hooks. */
|
|
5568
|
+
export function lowerEnvironmentKey(lowerer, node) {
|
|
5569
|
+
const key = lowerer.lowerExpr(node);
|
|
5570
|
+
if (key.type.kind === "dyn")
|
|
5571
|
+
return { kind: "libCall", fn: "dyn.toStringCoerce", args: [key], type: STRING, loc: key.loc };
|
|
5572
|
+
return lowerRecordPropertyKey(lowerer, key, node);
|
|
5007
5573
|
}
|
|
5008
5574
|
export function ensureString(lowerer, e, node) {
|
|
5009
5575
|
if (e.type.kind === "string")
|
|
5010
5576
|
return e;
|
|
5577
|
+
if (e.type.kind === "url")
|
|
5578
|
+
return { kind: "libCall", fn: "url.href", args: [e], type: STRING, loc: e.loc };
|
|
5579
|
+
if (e.type.kind === "regex") {
|
|
5580
|
+
return { kind: "regexIntrinsic", method: "toString", receiver: e, args: [], type: STRING, loc: e.loc };
|
|
5581
|
+
}
|
|
5011
5582
|
if (e.type.kind === "bigint") {
|
|
5012
5583
|
return {
|
|
5013
5584
|
kind: "libCall",
|
|
@@ -5023,18 +5594,21 @@ export function ensureString(lowerer, e, node) {
|
|
|
5023
5594
|
// strings verbatim, arrays via join, objects as "[object Object]").
|
|
5024
5595
|
return { kind: "toString", operand: e, type: STRING, loc: e.loc };
|
|
5025
5596
|
}
|
|
5597
|
+
if (e.type.kind === "bytes") {
|
|
5598
|
+
// Preserve the runtime brand: typed arrays join numeric elements,
|
|
5599
|
+
// while a Buffer stored in a Uint8Array slot decodes its bytes.
|
|
5600
|
+
const boxed = { kind: "dynFrom", value: e, type: DYN, loc: e.loc };
|
|
5601
|
+
return { kind: "toString", operand: boxed, type: STRING, loc: e.loc };
|
|
5602
|
+
}
|
|
5026
5603
|
if (e.type.kind === "jsval") {
|
|
5027
5604
|
// String(v) in the engine — JS-exact (and Node-exact in templates).
|
|
5028
5605
|
return { kind: "jsOp", op: "toStr", args: [e], type: STRING, loc: e.loc };
|
|
5029
5606
|
}
|
|
5030
5607
|
if (e.type.kind === "object") {
|
|
5031
|
-
// String(err) / `${err}`
|
|
5032
|
-
// .toString — the ONE runtime implementation (overriding it is
|
|
5033
|
-
// fenced), "name: message" with the empty-side elisions, exactly
|
|
5034
|
-
// Node's String(err), which carries no stack either.
|
|
5608
|
+
// String(err) / `${err}` uses the same dispatch as err.toString().
|
|
5035
5609
|
for (let c = lowerer.classes.get(e.type.className) ?? null; c; c = c.base) {
|
|
5036
5610
|
if (c.builtinError) {
|
|
5037
|
-
return
|
|
5611
|
+
return errorToStringCall(lowerer, e);
|
|
5038
5612
|
}
|
|
5039
5613
|
}
|
|
5040
5614
|
}
|
|
@@ -5225,6 +5799,38 @@ export function lowerAsExpression(lowerer, expr) {
|
|
|
5225
5799
|
return lowerer.jsvalIn(inner, expr.expression);
|
|
5226
5800
|
}
|
|
5227
5801
|
const target = lowerer.mapTypeOf(targetTs0);
|
|
5802
|
+
// Unknown-only record views use the checked-dynamic representation.
|
|
5803
|
+
// A native record asserted to such a view still denotes the original
|
|
5804
|
+
// object. Box a live reference instead of erasing the assertion and
|
|
5805
|
+
// handing native storage to a checked-dynamic property operation.
|
|
5806
|
+
if (target?.kind === "dyn" && (targetTs0.flags & ts.TypeFlags.Object) !== 0) {
|
|
5807
|
+
const sourceShape = inner.type.kind === "record" ? lowerer.shapes.get(inner.type.shapeId) : undefined;
|
|
5808
|
+
if (hasClassPayload(lowerer, inner.type) || sourceShape && shapeHasAccessorSlots(sourceShape)) {
|
|
5809
|
+
lowerer.unsupported("SC1090", expr, "opaque structural views of classes or accessor records (name the property's concrete type to retain native accessor dispatch)");
|
|
5810
|
+
}
|
|
5811
|
+
const boxed = lowerer.coerceInto(expr, inner, DYN);
|
|
5812
|
+
return boxed.kind === "dynFrom" ? { ...boxed, liveRef: true } : boxed;
|
|
5813
|
+
}
|
|
5814
|
+
// Collection assertions are interface views only: their backing
|
|
5815
|
+
// value must already have the same native key/element/value layout.
|
|
5816
|
+
// Erasing a mock or a different instantiation here would let member
|
|
5817
|
+
// lowering interpret a record (or another ABI) as a native handle.
|
|
5818
|
+
if (target?.kind === "map" || target?.kind === "set") {
|
|
5819
|
+
return lowerer.coerceInto(expr, inner, target);
|
|
5820
|
+
}
|
|
5821
|
+
// A class-derived interface still names the class's native layout.
|
|
5822
|
+
// An assertion must not let a structural mock acquire that layout;
|
|
5823
|
+
// unions retain their checked extraction below, and real instances
|
|
5824
|
+
// keep the ordinary class narrowing path.
|
|
5825
|
+
if (target?.kind === "object" && inner.type.kind !== "object" && inner.type.kind !== "union") {
|
|
5826
|
+
return lowerer.coerceInto(expr, inner, target);
|
|
5827
|
+
}
|
|
5828
|
+
if (target?.kind === "object" && inner.type.kind === "object" &&
|
|
5829
|
+
!typeEquals(target, inner.type) &&
|
|
5830
|
+
!lowerer.isSubclassOf(inner.type.className, target.className) &&
|
|
5831
|
+
!lowerer.isSubclassOf(target.className, inner.type.className)) {
|
|
5832
|
+
return lowerer.coerceInto(expr, inner, target);
|
|
5833
|
+
}
|
|
5228
5834
|
// Static assertions normally erase, but record layouts are
|
|
5229
5835
|
// monomorphic: a consumer selected from the asserted shape must see
|
|
5230
5836
|
// that shape physically. Reuse the ordinary slot coercion so plain
|
|
@@ -5277,7 +5883,21 @@ export function lowerAsExpression(lowerer, expr) {
|
|
|
5277
5883
|
lowerer.badType(expr.type, targetTs);
|
|
5278
5884
|
if (target.kind === "dyn")
|
|
5279
5885
|
return inner; // `as unknown`: erasure
|
|
5280
|
-
|
|
5886
|
+
// An all-`unknown`-fields record target (`err as { code?: unknown }`
|
|
5887
|
+
// — the errno-probing idiom): there is nothing to validate (every
|
|
5888
|
+
// field is unknown, exactly what the dyn value already answers) and
|
|
5889
|
+
// nothing to build — the cast is pure typing, so it ERASES and the
|
|
5890
|
+
// reads ride the dyn keyed read.
|
|
5891
|
+
if (target.kind === "record") {
|
|
5892
|
+
const shape = lowerer.shapes.get(target.shapeId);
|
|
5893
|
+
if (shape &&
|
|
5894
|
+
!shape.tuple &&
|
|
5895
|
+
shape.fields.every((f) => f.type.kind === "dyn") &&
|
|
5896
|
+
(!shape.indexValue || shape.indexValue.kind === "dyn")) {
|
|
5897
|
+
return inner;
|
|
5898
|
+
}
|
|
5899
|
+
}
|
|
5900
|
+
if (target.kind === "void" || !canDynCheckTo(target, (id) => lowerer.shapes.get(id), (id) => lowerer.unions.get(id))) {
|
|
5281
5901
|
// Bare undefined-armed targets pass when every OTHER arm is
|
|
5282
5902
|
// JSON-safe: the checked-dynamic tree holds a first-class undefined value now
|
|
5283
5903
|
// (index-signature overflow reads produce it for missing keys), and
|
|
@@ -5313,20 +5933,6 @@ export function lowerAsExpression(lowerer, expr) {
|
|
|
5313
5933
|
if (DYN_HANDLE_KINDS.has(target.kind)) {
|
|
5314
5934
|
return { kind: "dynCheck", value: inner, type: target, loc: locOf(expr) };
|
|
5315
5935
|
}
|
|
5316
|
-
// An all-`unknown`-fields record target (`err as { code?: unknown }`
|
|
5317
|
-
// — the errno-probing idiom): there is nothing to validate (every
|
|
5318
|
-
// field is unknown, exactly what the dyn value already answers) and
|
|
5319
|
-
// nothing to build — the cast is pure typing, so it ERASES and the
|
|
5320
|
-
// reads ride the dyn keyed read.
|
|
5321
|
-
if (target.kind === "record") {
|
|
5322
|
-
const shape = lowerer.shapes.get(target.shapeId);
|
|
5323
|
-
if (shape &&
|
|
5324
|
-
!shape.tuple &&
|
|
5325
|
-
shape.fields.every((f) => f.type.kind === "dyn") &&
|
|
5326
|
-
(!shape.indexValue || shape.indexValue.kind === "dyn")) {
|
|
5327
|
-
return inner;
|
|
5328
|
-
}
|
|
5329
|
-
}
|
|
5330
5936
|
lowerer.unsupported("SC1090", expr, `a checked cast of 'unknown' to '${target.kind === "void" ? "void" : lowerer.fmt(target)}' ` +
|
|
5331
5937
|
`(a dynamic value can only be validated against JSON-representable types: ` +
|
|
5332
5938
|
`number, string, boolean, records, arrays, and unions of those)`);
|
|
@@ -5344,7 +5950,10 @@ export function lowerPrefixUnary(lowerer, expr) {
|
|
|
5344
5950
|
if (raw.type.kind === "bigint") {
|
|
5345
5951
|
return { kind: "libCall", fn: "bigint.neg", args: [raw], type: BIGINT_T, loc };
|
|
5346
5952
|
}
|
|
5347
|
-
|
|
5953
|
+
// Match the checked numeric boundary used by JS binary arithmetic.
|
|
5954
|
+
const operand = raw.type.kind === "dyn" && isJsSourceFile(expr.getSourceFile())
|
|
5955
|
+
? { kind: "dynCheck", value: raw, type: F64, loc }
|
|
5956
|
+
: lowerOptionalNumber(lowerer, raw, loc, expr.operand);
|
|
5348
5957
|
if (operand.type.kind !== "f64")
|
|
5349
5958
|
lowerer.unsupported("SC1043", expr);
|
|
5350
5959
|
if (operand.kind === "numLit")
|
|
@@ -5353,6 +5962,9 @@ export function lowerPrefixUnary(lowerer, expr) {
|
|
|
5353
5962
|
}
|
|
5354
5963
|
case ts.SyntaxKind.PlusToken: {
|
|
5355
5964
|
const raw = lowerer.lowerExpr(expr.operand);
|
|
5965
|
+
if (raw.type.kind === "dyn") {
|
|
5966
|
+
return { kind: "libCall", fn: "dyn.toNumberCoerce", args: [raw], type: F64, loc };
|
|
5967
|
+
}
|
|
5356
5968
|
// Unary + is ToNumber; on an already-number operand it's identity,
|
|
5357
5969
|
// and a STRING operand runs the runtime's ECMA-exact StringToNumber
|
|
5358
5970
|
// (num.fromString — Number(aString)'s lowering, scr_string.c).
|
|
@@ -5378,14 +5990,16 @@ export function lowerPrefixUnary(lowerer, expr) {
|
|
|
5378
5990
|
if (raw.type.kind === "bigint") {
|
|
5379
5991
|
return { kind: "libCall", fn: "bigint.not", args: [raw], type: BIGINT_T, loc };
|
|
5380
5992
|
}
|
|
5993
|
+
if (raw.type.kind === "dyn")
|
|
5994
|
+
return {
|
|
5995
|
+
kind: "libCall", fn: "dyn.bitwise", args: [raw, dynUndefinedExpr(loc), { kind: "strLit", value: "~", type: STRING, loc }], type: DYN, loc,
|
|
5996
|
+
};
|
|
5381
5997
|
// A checked-dynamic value retains enough JavaScript structure to
|
|
5382
5998
|
// run exact ToNumber, including the object valueOf/toString
|
|
5383
5999
|
// protocol. This is the static `any`/JS-lane answer for idioms
|
|
5384
6000
|
// such as `while (~index)`, where inference may have widened an
|
|
5385
6001
|
// otherwise numeric slot to the checked-dynamic tree.
|
|
5386
|
-
const operand = raw
|
|
5387
|
-
? { kind: "libCall", fn: "dyn.toNumberCoerce", args: [raw], type: F64, loc }
|
|
5388
|
-
: lowerOptionalNumber(lowerer, raw, loc, expr.operand);
|
|
6002
|
+
const operand = lowerOptionalNumber(lowerer, raw, loc, expr.operand);
|
|
5389
6003
|
if (operand.type.kind !== "f64")
|
|
5390
6004
|
lowerer.unsupported("SC1043", expr);
|
|
5391
6005
|
return { kind: "unary", op: "~", operand, type: F64, loc };
|
|
@@ -5395,7 +6009,7 @@ export function lowerPrefixUnary(lowerer, expr) {
|
|
|
5395
6009
|
// `++x` / `--x` in expression position: yields the NEW value.
|
|
5396
6010
|
return lowerIncDec(lowerer, expr, true);
|
|
5397
6011
|
}
|
|
5398
|
-
lowerer.unsupported("SC1090", expr, `syntax '${ts.
|
|
6012
|
+
lowerer.unsupported("SC1090", expr, `syntax '${ts.syntaxKindName(expr.kind)}'`);
|
|
5399
6013
|
}
|
|
5400
6014
|
/** `x++`/`x--`/`++x`/`--x` in EXPRESSION position — the incDec node:
|
|
5401
6015
|
* read, write ±1, yield old (postfix) or new (prefix) value. Receivers
|
|
@@ -5407,6 +6021,14 @@ function lowerIncDec(lowerer, expr, prefix) {
|
|
|
5407
6021
|
const loc = locOf(expr);
|
|
5408
6022
|
const op = expr.operator === ts.SyntaxKind.PlusPlusToken ? "+" : "-";
|
|
5409
6023
|
if (!ts.isIdentifier(expr.operand)) {
|
|
6024
|
+
if (ts.isPropertyAccessExpression(expr.operand)) {
|
|
6025
|
+
const target = staticFieldWriteTarget(lowerer, expr.operand);
|
|
6026
|
+
if (target) {
|
|
6027
|
+
if (target.type.kind !== "f64")
|
|
6028
|
+
lowerer.unsupported("SC1043", expr);
|
|
6029
|
+
return { kind: "incDec", op, prefix, localId: target.id, type: F64, loc };
|
|
6030
|
+
}
|
|
6031
|
+
}
|
|
5410
6032
|
// CLASS-FIELD receivers (`if (--this.limit === 0)` — countdown.js's
|
|
5411
6033
|
// dec(); `--this[kLimit]` — its symbol-keyed spelling): a single-
|
|
5412
6034
|
// evaluation read-modify-write over the instance, yielding old/new
|
|
@@ -5447,6 +6069,16 @@ function lowerIncDec(lowerer, expr, prefix) {
|
|
|
5447
6069
|
if (!target) {
|
|
5448
6070
|
lowerer.rejectUnresolved(expr.operand, `increment/decrement of '${expr.operand.text}' (not a writable local or module global)`);
|
|
5449
6071
|
}
|
|
6072
|
+
if (target.type.kind === "dyn") {
|
|
6073
|
+
const old = lowerer.declareHiddenLocal("%numericOld", DYN);
|
|
6074
|
+
const next = lowerer.declareHiddenLocal("%numericNext", DYN);
|
|
6075
|
+
const read = lowerer.lowerExpr(expr.operand);
|
|
6076
|
+
return { kind: "seqExpr", stmts: [
|
|
6077
|
+
{ kind: "varDecl", localId: old.id, init: { kind: "libCall", fn: "dyn.toNumeric", args: [read], type: DYN, loc }, loc },
|
|
6078
|
+
{ kind: "varDecl", localId: next.id, init: { kind: "libCall", fn: "dyn.increment", args: [varRef(old.id, DYN, loc), { kind: "boolLit", value: op === "+", type: BOOL, loc }], type: DYN, loc }, loc },
|
|
6079
|
+
{ kind: "assign", localId: target.id, value: varRef(next.id, DYN, loc), loc },
|
|
6080
|
+
], result: varRef(prefix ? next.id : old.id, DYN, loc), type: DYN, loc };
|
|
6081
|
+
}
|
|
5450
6082
|
if (target.type.kind !== "f64")
|
|
5451
6083
|
lowerer.unsupported("SC1043", expr);
|
|
5452
6084
|
return { kind: "incDec", op, prefix, localId: target.id, type: F64, loc };
|
|
@@ -5498,7 +6130,7 @@ function lowerAnyBinaryInIsland(lowerer, expr, left, right, loc) {
|
|
|
5498
6130
|
};
|
|
5499
6131
|
const jop = JS_BIN[op];
|
|
5500
6132
|
if (jop === undefined) {
|
|
5501
|
-
lowerer.unsupported("SC1090", expr, `operator '${ts.tokenToString(op) ?? ts.
|
|
6133
|
+
lowerer.unsupported("SC1090", expr, `operator '${ts.tokenToString(op) ?? ts.syntaxKindName(op)}' on 'any' values`);
|
|
5502
6134
|
}
|
|
5503
6135
|
const type = jsOpResultKind(jop) === "bool" ? BOOL : JSVAL;
|
|
5504
6136
|
return {
|
|
@@ -5507,6 +6139,39 @@ function lowerAnyBinaryInIsland(lowerer, expr, left, right, loc) {
|
|
|
5507
6139
|
type, loc,
|
|
5508
6140
|
};
|
|
5509
6141
|
}
|
|
6142
|
+
export function lowerCompoundValueToTarget(lowerer, expr, compound, target) {
|
|
6143
|
+
const loc = locOf(expr);
|
|
6144
|
+
const read = { kind: "varRef", localId: target.id, type: target.type, loc: locOf(expr.left) };
|
|
6145
|
+
const rhs = lowerer.lowerExpr(expr.right);
|
|
6146
|
+
const numericRhs = lowerOptionalNumber(lowerer, rhs, loc);
|
|
6147
|
+
if (target.type.kind === "jsval" || rhs.type.kind === "jsval") {
|
|
6148
|
+
const JS_COMPOUND = { "+": "add", "-": "sub", "*": "mul", "/": "div", "%": "mod", "**": "pow" };
|
|
6149
|
+
const jop = JS_COMPOUND[compound];
|
|
6150
|
+
if (jop === undefined)
|
|
6151
|
+
lowerer.unsupported("SC1043", expr);
|
|
6152
|
+
const wrapped = {
|
|
6153
|
+
kind: "jsOp", op: jop,
|
|
6154
|
+
args: [lowerer.jsvalIn(read, expr.left), lowerer.jsvalIn(rhs, expr.right)],
|
|
6155
|
+
type: JSVAL, loc,
|
|
6156
|
+
};
|
|
6157
|
+
return lowerer.coerceInto(expr, wrapped, target.type);
|
|
6158
|
+
}
|
|
6159
|
+
if (compound === "+" && target.type.kind === "string") {
|
|
6160
|
+
return { kind: "strConcat", left: read, right: lowerer.ensureString(rhs, expr.right), type: STRING, loc };
|
|
6161
|
+
}
|
|
6162
|
+
if (target.type.kind === "f64" && numericRhs.type.kind === "f64") {
|
|
6163
|
+
return { kind: "bin", op: compound, left: read, right: numericRhs, type: F64, loc };
|
|
6164
|
+
}
|
|
6165
|
+
if ((target.type.kind === "dyn" || rhs.type.kind === "dyn") &&
|
|
6166
|
+
(target.type.kind === "dyn" || target.type.kind === "f64") &&
|
|
6167
|
+
(rhs.type.kind === "dyn" || rhs.type.kind === "f64") &&
|
|
6168
|
+
isJsSourceFile(expr.getSourceFile())) {
|
|
6169
|
+
const checkNum = (value) => value.type.kind === "dyn" ? { kind: "dynCheck", value, type: F64, loc: value.loc } : value;
|
|
6170
|
+
const computed = { kind: "bin", op: compound, left: checkNum(read), right: checkNum(rhs), type: F64, loc };
|
|
6171
|
+
return target.type.kind === "dyn" ? { kind: "dynFrom", value: computed, type: DYN, loc } : computed;
|
|
6172
|
+
}
|
|
6173
|
+
lowerer.unsupported("SC1043", expr);
|
|
6174
|
+
}
|
|
5510
6175
|
export function lowerBinary(lowerer, expr) {
|
|
5511
6176
|
const loc = locOf(expr);
|
|
5512
6177
|
const op = expr.operatorToken.kind;
|
|
@@ -5514,6 +6179,15 @@ export function lowerBinary(lowerer, expr) {
|
|
|
5514
6179
|
if (cacheHas)
|
|
5515
6180
|
return cacheHas;
|
|
5516
6181
|
if (op === ts.SyntaxKind.EqualsToken || (op >= ts.SyntaxKind.FirstCompoundAssignment && op <= ts.SyntaxKind.LastCompoundAssignment)) {
|
|
6182
|
+
if (op === ts.SyntaxKind.EqualsToken) {
|
|
6183
|
+
const callableWrite = lowerNativeFunctionAssignment(lowerer, expr);
|
|
6184
|
+
if (callableWrite)
|
|
6185
|
+
return callableWrite;
|
|
6186
|
+
}
|
|
6187
|
+
if (op === ts.SyntaxKind.QuestionQuestionEqualsToken ||
|
|
6188
|
+
op === ts.SyntaxKind.AmpersandAmpersandEqualsToken || op === ts.SyntaxKind.BarBarEqualsToken) {
|
|
6189
|
+
return lowerShortCircuitAssignment(lowerer, expr);
|
|
6190
|
+
}
|
|
5517
6191
|
if (ts.isPropertyAccessExpression(expr.left) || ts.isElementAccessExpression(expr.left)) {
|
|
5518
6192
|
fenceNodeModuleMutation(lowerer, expr.left, "assignment");
|
|
5519
6193
|
}
|
|
@@ -5539,8 +6213,29 @@ export function lowerBinary(lowerer, expr) {
|
|
|
5539
6213
|
}
|
|
5540
6214
|
const indexedCompound = COMPOUND_ASSIGN_OPS[op];
|
|
5541
6215
|
if (indexedCompound !== undefined && ts.isElementAccessExpression(expr.left)) {
|
|
6216
|
+
if (symbolFieldInfo(lowerer, expr.left))
|
|
6217
|
+
return lowerFieldCompoundValue(lowerer, expr.left, indexedCompound, expr.right, loc);
|
|
5542
6218
|
return lowerElementCompound(lowerer, expr, indexedCompound);
|
|
5543
6219
|
}
|
|
6220
|
+
if (indexedCompound !== undefined && ts.isIdentifier(expr.left)) {
|
|
6221
|
+
const target = lowerer.resolveWritable(expr.left);
|
|
6222
|
+
if (!target) {
|
|
6223
|
+
lowerer.rejectUnresolved(expr.left, "assignment to an unresolved variable");
|
|
6224
|
+
}
|
|
6225
|
+
const value = lowerCompoundValueToTarget(lowerer, expr, indexedCompound, target);
|
|
6226
|
+
return { kind: "assignExpr", localId: target.id, value, type: target.type, loc };
|
|
6227
|
+
}
|
|
6228
|
+
if (indexedCompound !== undefined &&
|
|
6229
|
+
(ts.isPropertyAccessExpression(expr.left) || ts.isElementAccessExpression(expr.left)) &&
|
|
6230
|
+
!(ts.isPropertyAccessExpression(expr.left) && expr.left.questionDotToken)) {
|
|
6231
|
+
const target = expandoWritableTarget(lowerer, expr.left) ??
|
|
6232
|
+
(ts.isPropertyAccessExpression(expr.left) ? nsWritableTarget(lowerer, expr.left) : null);
|
|
6233
|
+
if (target) {
|
|
6234
|
+
const value = lowerCompoundValueToTarget(lowerer, expr, indexedCompound, target);
|
|
6235
|
+
return { kind: "assignExpr", localId: target.id, value, type: target.type, loc };
|
|
6236
|
+
}
|
|
6237
|
+
return lowerFieldCompoundValue(lowerer, expr.left, indexedCompound, expr.right, loc);
|
|
6238
|
+
}
|
|
5544
6239
|
// `events.defaultMaxListeners = v` — the module-property write
|
|
5545
6240
|
// Node validates (validateNumber(n, 'defaultMaxListeners', 0)):
|
|
5546
6241
|
// the value crosses into the checked-dynamic tree and the runtime ladder throws
|
|
@@ -5593,6 +6288,15 @@ export function lowerBinary(lowerer, expr) {
|
|
|
5593
6288
|
const value = lowerer.lowerExprExpecting(expr.right, target.type);
|
|
5594
6289
|
return { kind: "assignExpr", localId: target.id, value, type: target.type, loc };
|
|
5595
6290
|
}
|
|
6291
|
+
// Bundled numeric enums use e[e["name"] = value] = "name".
|
|
6292
|
+
// Capture the receiver and key before the RHS. PutValue coerces the
|
|
6293
|
+
// key afterward, then yields the RHS without rereading the property.
|
|
6294
|
+
if (ts.isElementAccessExpression(expr.left) && !expr.left.questionDotToken) {
|
|
6295
|
+
const recv = tryLowerExpression(lowerer, expr.left.expression);
|
|
6296
|
+
if (recv?.type.kind === "dyn") {
|
|
6297
|
+
return lowerDynMemberAssignment(lowerer, expr, recv);
|
|
6298
|
+
}
|
|
6299
|
+
}
|
|
5596
6300
|
// `h.k = v` on an ISLAND receiver in VALUE position: the engine
|
|
5597
6301
|
// property write (setProp throws the engine's TypeErrors on
|
|
5598
6302
|
// nullish receivers, bridged catchably), the RHS value threaded
|
|
@@ -5659,6 +6363,22 @@ export function lowerBinary(lowerer, expr) {
|
|
|
5659
6363
|
loc,
|
|
5660
6364
|
};
|
|
5661
6365
|
}
|
|
6366
|
+
const field = lowerer.fieldTarget(expr.left);
|
|
6367
|
+
if (field) {
|
|
6368
|
+
const recvTmp = lowerer.declareHiddenLocal("%setReceiver", field.obj.type);
|
|
6369
|
+
const receiver = field.obj;
|
|
6370
|
+
field.obj = varRef(recvTmp.id, receiver.type, loc);
|
|
6371
|
+
const value = lowerer.lowerExprExpecting(expr.right, field.fieldType);
|
|
6372
|
+
const valTmp = lowerer.declareHiddenLocal("%setValue", value.type);
|
|
6373
|
+
const result = varRef(valTmp.id, value.type, loc);
|
|
6374
|
+
return {
|
|
6375
|
+
kind: "seqExpr", stmts: [
|
|
6376
|
+
{ kind: "varDecl", localId: recvTmp.id, init: receiver, loc },
|
|
6377
|
+
{ kind: "varDecl", localId: valTmp.id, init: value, loc },
|
|
6378
|
+
lowerer.fieldSetStmt(field, result, loc, expr.left),
|
|
6379
|
+
], result, type: result.type, loc,
|
|
6380
|
+
};
|
|
6381
|
+
}
|
|
5662
6382
|
}
|
|
5663
6383
|
}
|
|
5664
6384
|
// Destructuring assignment in VALUE position (`(() => [i] = [i+1])()`,
|
|
@@ -5685,221 +6405,7 @@ export function lowerBinary(lowerer, expr) {
|
|
|
5685
6405
|
lowerer.unsupported("SC1040", expr, undefined, "object-to-primitive loose equality can call user-defined valueOf/toString methods — compare explicit primitive conversions instead");
|
|
5686
6406
|
}
|
|
5687
6407
|
if (op === ts.SyntaxKind.AmpersandAmpersandToken || op === ts.SyntaxKind.BarBarToken) {
|
|
5688
|
-
|
|
5689
|
-
const fallback = lowerAbsenceAwareOrChain(lowerer, expr);
|
|
5690
|
-
if (fallback)
|
|
5691
|
-
return fallback;
|
|
5692
|
-
}
|
|
5693
|
-
// JS value semantics: `a && b` is `toBool(a) ? b : a` — the result is
|
|
5694
|
-
// an operand value, so both operands must share one IR kind (which is
|
|
5695
|
-
// also what tsc's type says, modulo literal-union collapsing that
|
|
5696
|
-
// mapType already handles). Mixed kinds (`n && s`) stay rejected.
|
|
5697
|
-
const left = lowerAbsenceProbe(lowerer, expr.left) ?? lowerer.lowerExpr(expr.left);
|
|
5698
|
-
const right = lowerer.lowerExpr(expr.right);
|
|
5699
|
-
// A LITERAL-unit left operand (a compile-time undefined/null — the
|
|
5700
|
-
// capability-probe members: `process.features.inspector ||
|
|
5701
|
-
// !flag.startsWith('--inspect')` reads undefined in a compiled
|
|
5702
|
-
// binary): statically falsy, so the operator folds JS-exactly —
|
|
5703
|
-
// `unit || X` IS X, `unit && X` IS the unit (X's side effects never
|
|
5704
|
-
// run in JS either; the lowered right is simply dropped). Literal
|
|
5705
|
-
// units only — computed unit-typed values keep the fences below.
|
|
5706
|
-
if (left.kind === "unitLit") {
|
|
5707
|
-
return op === ts.SyntaxKind.BarBarToken ? right : left;
|
|
5708
|
-
}
|
|
5709
|
-
// JS objects and symbols are always truthy. In `ref || fallback`
|
|
5710
|
-
// the fallback is therefore unreachable and the result is the
|
|
5711
|
-
// already-evaluated left value — the browser-fallback idiom used by
|
|
5712
|
-
// packages for `process.argv || []` and `process.env || {}`. The
|
|
5713
|
-
// operand itself remains in the IR, preserving reads/calls that
|
|
5714
|
-
// produce the reference; only the unreachable right lowering drops.
|
|
5715
|
-
if (op === ts.SyntaxKind.BarBarToken && REF_TRUTHY_KINDS.has(left.type.kind)) {
|
|
5716
|
-
return left;
|
|
5717
|
-
}
|
|
5718
|
-
if (left.type.kind === "dyn" || right.type.kind === "dyn") {
|
|
5719
|
-
// A checked-dynamic operand (`fn.name || '<anonymous>'` —
|
|
5720
|
-
// test/common's _mustCallInner): both sides live in the checked-dynamic tree and
|
|
5721
|
-
// the deciding test is ToBoolean over the dyn kind
|
|
5722
|
-
// (scr_dyn_truthy) — JS value semantics exactly, result dyn. The
|
|
5723
|
-
// non-dyn side converts through the usual boundary; a value with
|
|
5724
|
-
// no dyn representation keeps the fence.
|
|
5725
|
-
const l = lowerer.coerceToExpected(left, DYN);
|
|
5726
|
-
const r = lowerer.coerceToExpected(right, DYN);
|
|
5727
|
-
if (l.type.kind !== "dyn" || r.type.kind !== "dyn") {
|
|
5728
|
-
lowerer.unsupported("SC1100", expr, "logical operators on 'unknown' values");
|
|
5729
|
-
}
|
|
5730
|
-
return {
|
|
5731
|
-
kind: "logical",
|
|
5732
|
-
op: op === ts.SyntaxKind.AmpersandAmpersandToken ? "&&" : "||",
|
|
5733
|
-
left: l,
|
|
5734
|
-
right: r,
|
|
5735
|
-
type: DYN, loc,
|
|
5736
|
-
};
|
|
5737
|
-
}
|
|
5738
|
-
if (left.type.kind === "jsval" || right.type.kind === "jsval") {
|
|
5739
|
-
return {
|
|
5740
|
-
kind: "logical",
|
|
5741
|
-
op: op === ts.SyntaxKind.AmpersandAmpersandToken ? "&&" : "||",
|
|
5742
|
-
left: lowerer.jsvalIn(left, expr.left),
|
|
5743
|
-
right: lowerer.jsvalIn(right, expr.right),
|
|
5744
|
-
type: JSVAL, loc,
|
|
5745
|
-
};
|
|
5746
|
-
}
|
|
5747
|
-
if (left.type.kind === "union" || right.type.kind === "union") {
|
|
5748
|
-
// JS value semantics over a union: the deciding test is the ARM
|
|
5749
|
-
// value's ToBoolean (a per-union helper), and the result is the
|
|
5750
|
-
// deciding operand. Supported when the checker's type of the whole
|
|
5751
|
-
// expression maps to ONE union both operands coerce into (same
|
|
5752
|
-
// union passes through; a plain arm wraps — `u || undefined`).
|
|
5753
|
-
// Everything else (`u && flag`, whose value would need a wider
|
|
5754
|
-
// re-tagged union) stays fenced — in CONDITION position those
|
|
5755
|
-
// shapes lower through lowerCondition's bool descent instead.
|
|
5756
|
-
let target = lowerer.mapTypeOf(lowerer.typeOf(expr));
|
|
5757
|
-
// A broad-JSDoc npm-static body can leave the CHECKER result `any`
|
|
5758
|
-
// even after declaration-backed specialization has recovered both
|
|
5759
|
-
// operands (`helpOption && args.find(...)` in commander). When the
|
|
5760
|
-
// left consists only of always-truthy references and falsy units,
|
|
5761
|
-
// the static result is exactly the right operand plus those units.
|
|
5762
|
-
if (target === null &&
|
|
5763
|
-
lowerer.implicitParamTypes !== null &&
|
|
5764
|
-
npmStaticPackageOfPath(expr.getSourceFile().fileName) !== null &&
|
|
5765
|
-
(lowerer.typeOf(expr).flags & ts.TypeFlags.Any) !== 0 &&
|
|
5766
|
-
left.type.kind === "union") {
|
|
5767
|
-
const leftArms = lowerer.unions.get(left.type.unionId)?.arms;
|
|
5768
|
-
const rightArms = right.type.kind === "union"
|
|
5769
|
-
? lowerer.unions.get(right.type.unionId)?.arms
|
|
5770
|
-
: [right.type];
|
|
5771
|
-
const rightIsStatic = rightArms?.every((arm) => arm.kind !== "dyn" && arm.kind !== "caught" && arm.kind !== "jsval" && arm.kind !== "void") ?? false;
|
|
5772
|
-
if (leftArms && rightArms && rightIsStatic &&
|
|
5773
|
-
leftArms.some(isUnitType) &&
|
|
5774
|
-
leftArms.every((arm) => isUnitType(arm) || REF_TRUTHY_KINDS.has(arm.kind))) {
|
|
5775
|
-
const byKey = new Map();
|
|
5776
|
-
for (const arm of [...rightArms, ...leftArms.filter(isUnitType)]) {
|
|
5777
|
-
byKey.set(typeKey(arm), arm);
|
|
5778
|
-
}
|
|
5779
|
-
const arms = [...byKey.values()].sort((a, b) => (typeKey(a) < typeKey(b) ? -1 : 1));
|
|
5780
|
-
if (arms.length > 1) {
|
|
5781
|
-
target = { kind: "union", unionId: lowerer.unions.intern(arms) };
|
|
5782
|
-
}
|
|
5783
|
-
}
|
|
5784
|
-
}
|
|
5785
|
-
if (target?.kind === "union") {
|
|
5786
|
-
// `&&` whose left does NOT fit the result union: the checker
|
|
5787
|
-
// built that result by DROPPING left arms which are always
|
|
5788
|
-
// truthy (`Option | undefined && string | undefined` answers
|
|
5789
|
-
// `string | undefined`, not `Option | string | undefined`).
|
|
5790
|
-
// Evaluate the left once, test it in its own union, and retag it
|
|
5791
|
-
// only on the falsy path. Any stranded reference arms are
|
|
5792
|
-
// unreachable there because every JS object is truthy.
|
|
5793
|
-
if (op === ts.SyntaxKind.AmpersandAmpersandToken && left.type.kind === "union" && !typeEquals(left.type, target)) {
|
|
5794
|
-
const def = lowerer.unions.get(left.type.unionId);
|
|
5795
|
-
const trappable = new Set();
|
|
5796
|
-
def?.arms.forEach((arm, tag) => {
|
|
5797
|
-
if (lowerer.armTag(target.unionId, arm) < 0 && REF_TRUTHY_KINDS.has(arm.kind)) {
|
|
5798
|
-
trappable.add(tag);
|
|
5799
|
-
}
|
|
5800
|
-
});
|
|
5801
|
-
const retag = trappable.size === 0
|
|
5802
|
-
? null
|
|
5803
|
-
: lowerer.unionRetagHelper(left.type.unionId, target.unionId, loc, trappable);
|
|
5804
|
-
if (retag) {
|
|
5805
|
-
lowerer.requireTruthyUnion(left.type.unionId, expr);
|
|
5806
|
-
const stmts = [];
|
|
5807
|
-
let stable = left;
|
|
5808
|
-
if (!isSafeToRepeat(left)) {
|
|
5809
|
-
const local = lowerer.declareHiddenLocal("%and", left.type);
|
|
5810
|
-
stmts.push({ kind: "varDecl", localId: local.id, init: left, loc });
|
|
5811
|
-
stable = varRef(local.id, left.type, loc);
|
|
5812
|
-
}
|
|
5813
|
-
const result = {
|
|
5814
|
-
kind: "ternary",
|
|
5815
|
-
cond: lowerer.ensureBool(stable, expr.left),
|
|
5816
|
-
then: lowerer.coerceInto(expr.right, right, target),
|
|
5817
|
-
else_: { kind: "call", callee: retag, args: [stable], type: target, loc },
|
|
5818
|
-
type: target,
|
|
5819
|
-
loc,
|
|
5820
|
-
};
|
|
5821
|
-
return stmts.length === 0 ? result : { kind: "seqExpr", stmts, result, type: target, loc };
|
|
5822
|
-
}
|
|
5823
|
-
}
|
|
5824
|
-
// `||` whose left does NOT fit the result union: the checker built
|
|
5825
|
-
// that result by DROPPING the left's falsy arms (`process.env.X ||
|
|
5826
|
-
// null` is `string | null`, `|| 3000` is `string | number` — the
|
|
5827
|
-
// `undefined` is gone from both), so coercing the left eagerly, as
|
|
5828
|
-
// the shared shape below does, retags an arm the test is about to
|
|
5829
|
-
// rule out and throws where Node yields the default. Single-eval
|
|
5830
|
-
// instead: test the left in its OWN union and retag only on the
|
|
5831
|
-
// truthy side, where the dropped arms are unreachable.
|
|
5832
|
-
if (op === ts.SyntaxKind.BarBarToken && left.type.kind === "union" && !typeEquals(left.type, target)) {
|
|
5833
|
-
const retag = lowerer.unionRetagHelper(left.type.unionId, target.unionId, loc);
|
|
5834
|
-
if (retag) {
|
|
5835
|
-
lowerer.requireTruthyUnion(left.type.unionId, expr);
|
|
5836
|
-
return {
|
|
5837
|
-
kind: "orDefault",
|
|
5838
|
-
left,
|
|
5839
|
-
right: lowerer.coerceInto(expr.right, right, target),
|
|
5840
|
-
retag,
|
|
5841
|
-
type: target,
|
|
5842
|
-
loc,
|
|
5843
|
-
};
|
|
5844
|
-
}
|
|
5845
|
-
}
|
|
5846
|
-
lowerer.requireTruthyUnion(target.unionId, expr);
|
|
5847
|
-
return {
|
|
5848
|
-
kind: "logical",
|
|
5849
|
-
op: op === ts.SyntaxKind.AmpersandAmpersandToken ? "&&" : "||",
|
|
5850
|
-
left: lowerer.coerceInto(expr.left, left, target),
|
|
5851
|
-
right: lowerer.coerceInto(expr.right, right, target),
|
|
5852
|
-
type: target, loc,
|
|
5853
|
-
};
|
|
5854
|
-
}
|
|
5855
|
-
// `u || d` NARROWED by the default: the checker types the result
|
|
5856
|
-
// as u's single non-unit arm (`marker() || "default"`) — evaluate
|
|
5857
|
-
// u once, truthy extracts the arm, falsy takes d lazily (the
|
|
5858
|
-
// orDefault node, nullish's truthiness sibling). An UNMAPPABLE
|
|
5859
|
-
// checker result takes the same rule (`options.runner ||
|
|
5860
|
-
// defaultRunner` — tsc's union of two structurally-compatible
|
|
5861
|
-
// function types has no representation, while the left's one
|
|
5862
|
-
// non-unit arm is the only representable answer): the default
|
|
5863
|
-
// must coerce into the arm or fence on its own.
|
|
5864
|
-
if (op === ts.SyntaxKind.BarBarToken &&
|
|
5865
|
-
left.type.kind === "union") {
|
|
5866
|
-
const def = lowerer.unions.get(left.type.unionId);
|
|
5867
|
-
const rest = def ? def.arms.filter((a) => !isUnitType(a)) : [];
|
|
5868
|
-
const funcArmDefault = rest[0]?.kind === "func" && (target === null || target.kind === "func");
|
|
5869
|
-
if (rest.length === 1 && ((target !== null && typeEquals(target, rest[0])) || funcArmDefault)) {
|
|
5870
|
-
lowerer.requireTruthyUnion(left.type.unionId, expr);
|
|
5871
|
-
const dflt = lowerer.lowerExprExpecting(expr.right, rest[0]);
|
|
5872
|
-
return { kind: "orDefault", left, right: dflt, type: rest[0], loc };
|
|
5873
|
-
}
|
|
5874
|
-
}
|
|
5875
|
-
lowerer.unsupported("SC1090", expr, `logical operators on union-typed values outside conditions (${NARROW_FIRST})`);
|
|
5876
|
-
}
|
|
5877
|
-
const kind = left.type.kind;
|
|
5878
|
-
if (kind !== right.type.kind ||
|
|
5879
|
-
(kind !== "f64" && kind !== "string" && kind !== "bool")) {
|
|
5880
|
-
// Mixed PLAIN operands (`value || null`, `flag || undefined`,
|
|
5881
|
-
// `s || 0`): JS value semantics still compile when the checker
|
|
5882
|
-
// types the RESULT as one union both operands coerce into — the
|
|
5883
|
-
// same lift the union-operand path above takes, arriving here
|
|
5884
|
-
// with two plain arm values instead.
|
|
5885
|
-
const target = lowerer.mapTypeOf(lowerer.typeOf(expr));
|
|
5886
|
-
if (target?.kind === "union") {
|
|
5887
|
-
lowerer.requireTruthyUnion(target.unionId, expr);
|
|
5888
|
-
return {
|
|
5889
|
-
kind: "logical",
|
|
5890
|
-
op: op === ts.SyntaxKind.AmpersandAmpersandToken ? "&&" : "||",
|
|
5891
|
-
left: lowerer.coerceInto(expr.left, left, target),
|
|
5892
|
-
right: lowerer.coerceInto(expr.right, right, target),
|
|
5893
|
-
type: target, loc,
|
|
5894
|
-
};
|
|
5895
|
-
}
|
|
5896
|
-
lowerer.unsupported("SC1042", expr);
|
|
5897
|
-
}
|
|
5898
|
-
return {
|
|
5899
|
-
kind: "logical",
|
|
5900
|
-
op: op === ts.SyntaxKind.AmpersandAmpersandToken ? "&&" : "||",
|
|
5901
|
-
left, right, type: left.type, loc,
|
|
5902
|
-
};
|
|
6408
|
+
return lowerLogicalChain(lowerer, expr);
|
|
5903
6409
|
}
|
|
5904
6410
|
if (op === ts.SyntaxKind.QuestionQuestionToken) {
|
|
5905
6411
|
return lowerer.lowerNullishCoalesce(expr, loc);
|
|
@@ -5910,7 +6416,7 @@ export function lowerBinary(lowerer, expr) {
|
|
|
5910
6416
|
// runs for EFFECT exactly as its statement lowering (JS discards its
|
|
5911
6417
|
// value), the right operand is the expression's value — a seqExpr.
|
|
5912
6418
|
// The validator restricts seqExpr statements to straight-line writes
|
|
5913
|
-
// (the
|
|
6419
|
+
// (the lowering point is mid-expression), so a left operand whose
|
|
5914
6420
|
// statement lowering needs real control flow keeps a pointed fence
|
|
5915
6421
|
// instead of tripping the validator downstream.
|
|
5916
6422
|
const effect = lowerer.lowerExprStatement(expr.left);
|
|
@@ -5951,7 +6457,7 @@ export function lowerBinary(lowerer, expr) {
|
|
|
5951
6457
|
op === ts.SyntaxKind.ExclamationEqualsEqualsToken) {
|
|
5952
6458
|
const negated = op === ts.SyntaxKind.ExclamationEqualsEqualsToken;
|
|
5953
6459
|
const unit = left.kind === "unitLit" ? left : right.kind === "unitLit" ? right : null;
|
|
5954
|
-
const other =
|
|
6460
|
+
const other = left.kind === "unitLit" ? right : left;
|
|
5955
6461
|
if (unit && other.type.kind === "dyn") {
|
|
5956
6462
|
return {
|
|
5957
6463
|
kind: "dynTest",
|
|
@@ -5968,12 +6474,30 @@ export function lowerBinary(lowerer, expr) {
|
|
|
5968
6474
|
// answers false). dyn vs dyn keeps the fence below.
|
|
5969
6475
|
const dynSide = left.type.kind === "dyn" ? left : right;
|
|
5970
6476
|
const scalarSide = dynSide === left ? right : left;
|
|
6477
|
+
const reference = scalarSide.type;
|
|
6478
|
+
if ((isDynTypedRefType(reference) || reference.kind === "record" || reference.kind === "array" ||
|
|
6479
|
+
reference.kind === "bytes" || reference.kind === "func" || reference.kind === "regex" || reference.kind === "url" ||
|
|
6480
|
+
reference.kind === "bigint" || reference.kind === "symbol" || reference.kind === "set" || reference.kind === "union" || DYN_HANDLE_KINDS.has(reference.kind)) &&
|
|
6481
|
+
lowerer.dynConvertible(reference)) {
|
|
6482
|
+
const boxed = {
|
|
6483
|
+
kind: "dynFrom", value: scalarSide, type: DYN, loc: scalarSide.loc,
|
|
6484
|
+
...(reference.kind === "record" || reference.kind === "array" || reference.kind === "bytes" ? { liveRef: true } : {}),
|
|
6485
|
+
};
|
|
6486
|
+
return {
|
|
6487
|
+
kind: "dynScalarEq",
|
|
6488
|
+
left: scalarSide === left ? boxed : left,
|
|
6489
|
+
right: scalarSide === right ? boxed : right,
|
|
6490
|
+
...(negated ? { negated: true } : {}),
|
|
6491
|
+
type: BOOL,
|
|
6492
|
+
loc,
|
|
6493
|
+
};
|
|
6494
|
+
}
|
|
5971
6495
|
if (dynSide.type.kind === "dyn" &&
|
|
5972
6496
|
(scalarSide.type.kind === "f64" || scalarSide.type.kind === "string" || scalarSide.type.kind === "bool" ||
|
|
5973
6497
|
// dyn vs dyn (`context.actual !== context.exact` —
|
|
5974
6498
|
// test/common's exit accounting): the runtime's whole-dyn
|
|
5975
6499
|
// strict equality — scalars by value, units by kind,
|
|
5976
|
-
// reference kinds by
|
|
6500
|
+
// reference kinds by identity (scr_dyn_strict_eq).
|
|
5977
6501
|
scalarSide.type.kind === "dyn")) {
|
|
5978
6502
|
return {
|
|
5979
6503
|
kind: "dynScalarEq",
|
|
@@ -5985,15 +6509,22 @@ export function lowerBinary(lowerer, expr) {
|
|
|
5985
6509
|
};
|
|
5986
6510
|
}
|
|
5987
6511
|
}
|
|
5988
|
-
|
|
5989
|
-
|
|
5990
|
-
|
|
5991
|
-
|
|
5992
|
-
|
|
5993
|
-
|
|
5994
|
-
|
|
5995
|
-
|
|
5996
|
-
|
|
6512
|
+
const bitwise = {
|
|
6513
|
+
[ts.SyntaxKind.AmpersandToken]: "&", [ts.SyntaxKind.BarToken]: "|", [ts.SyntaxKind.CaretToken]: "^",
|
|
6514
|
+
[ts.SyntaxKind.LessThanLessThanToken]: "<<", [ts.SyntaxKind.GreaterThanGreaterThanToken]: ">>",
|
|
6515
|
+
[ts.SyntaxKind.GreaterThanGreaterThanGreaterThanToken]: ">>>",
|
|
6516
|
+
};
|
|
6517
|
+
if (bitwise[op]) {
|
|
6518
|
+
const l = lowerer.coerceToExpected(left, DYN);
|
|
6519
|
+
const r = lowerer.coerceToExpected(right, DYN);
|
|
6520
|
+
if (l.type.kind === "dyn" && r.type.kind === "dyn") {
|
|
6521
|
+
const value = { kind: "libCall", fn: "dyn.bitwise", args: [l, r, { kind: "strLit", value: bitwise[op], type: STRING, loc }], type: DYN, loc };
|
|
6522
|
+
return left.type.kind === "f64" || right.type.kind === "f64" ? { kind: "dynCheck", value, type: F64, loc } : value;
|
|
6523
|
+
}
|
|
6524
|
+
}
|
|
6525
|
+
// Numeric operators dispatch on ToNumeric's actual Number/BigInt
|
|
6526
|
+
// result, including values flowing through native callable properties.
|
|
6527
|
+
if (isJsSourceFile(expr.getSourceFile()) || op === ts.SyntaxKind.PlusToken) {
|
|
5997
6528
|
const checkNum = (e) => e.type.kind === "dyn" ? { kind: "dynCheck", value: e, type: F64, loc: e.loc } : e;
|
|
5998
6529
|
const other = left.type.kind === "dyn" ? right : left;
|
|
5999
6530
|
const NUM_BIN = {
|
|
@@ -6011,26 +6542,29 @@ export function lowerBinary(lowerer, expr) {
|
|
|
6011
6542
|
};
|
|
6012
6543
|
const arith = NUM_BIN[op];
|
|
6013
6544
|
const cmp = NUM_CMP[op];
|
|
6014
|
-
if (
|
|
6015
|
-
const l =
|
|
6016
|
-
const r =
|
|
6017
|
-
if (
|
|
6018
|
-
|
|
6019
|
-
|
|
6020
|
-
|
|
6021
|
-
|
|
6022
|
-
|
|
6023
|
-
if (op === ts.SyntaxKind.PlusToken) {
|
|
6024
|
-
if (other.type.kind === "f64" || other.type.kind === "dyn") {
|
|
6025
|
-
return { kind: "bin", op: "+", left: checkNum(left), right: checkNum(right), type: F64, loc };
|
|
6545
|
+
if (arith) {
|
|
6546
|
+
const l = lowerer.coerceToExpected(left, DYN);
|
|
6547
|
+
const r = lowerer.coerceToExpected(right, DYN);
|
|
6548
|
+
if (l.type.kind === "dyn" && r.type.kind === "dyn") {
|
|
6549
|
+
const value = { kind: "libCall", fn: "dyn.arithmetic",
|
|
6550
|
+
args: [l, r, { kind: "strLit", value: arith, type: STRING, loc }], type: DYN, loc };
|
|
6551
|
+
const numeric = left.type.kind === "bigint" || right.type.kind === "bigint" ? BIGINT_T
|
|
6552
|
+
: left.type.kind === "f64" || right.type.kind === "f64" ? F64 : null;
|
|
6553
|
+
return numeric ? { kind: "dynCheck", value, type: numeric, loc } : value;
|
|
6026
6554
|
}
|
|
6027
|
-
|
|
6028
|
-
|
|
6029
|
-
|
|
6030
|
-
|
|
6031
|
-
|
|
6032
|
-
|
|
6033
|
-
|
|
6555
|
+
}
|
|
6556
|
+
if (cmp && (other.type.kind === "f64" || other.type.kind === "dyn")) {
|
|
6557
|
+
return { kind: "bin", op: cmp, left: checkNum(left), right: checkNum(right), type: BOOL, loc };
|
|
6558
|
+
}
|
|
6559
|
+
// Untyped addition chooses concatenation or numeric addition only
|
|
6560
|
+
// after both operands have undergone ToPrimitive at runtime.
|
|
6561
|
+
if (op === ts.SyntaxKind.PlusToken) {
|
|
6562
|
+
const l = lowerer.coerceToExpected(left, DYN);
|
|
6563
|
+
const r = lowerer.coerceToExpected(right, DYN);
|
|
6564
|
+
if (l.type.kind === "dyn" && r.type.kind === "dyn") {
|
|
6565
|
+
const value = { kind: "libCall", fn: "dyn.add", args: [l, r], type: DYN, loc };
|
|
6566
|
+
// A statically known string operand guarantees a string result.
|
|
6567
|
+
return other.type.kind === "string" ? { kind: "dynCheck", value, type: STRING, loc } : value;
|
|
6034
6568
|
}
|
|
6035
6569
|
}
|
|
6036
6570
|
}
|
|
@@ -6049,7 +6583,7 @@ export function lowerBinary(lowerer, expr) {
|
|
|
6049
6583
|
// retain the SC2011 promise that --dynamic lifts this site.
|
|
6050
6584
|
if (lowerer.dynamic)
|
|
6051
6585
|
return lowerAnyBinaryInIsland(lowerer, expr, left, right, loc);
|
|
6052
|
-
lowerer.anyOpFence(`the '${ts.tokenToString(op) ?? ts.
|
|
6586
|
+
lowerer.anyOpFence(`the '${ts.tokenToString(op) ?? ts.syntaxKindName(op)}' operator`, expr);
|
|
6053
6587
|
}
|
|
6054
6588
|
// tsc allows ===/!== on unknown (arithmetic/comparisons it rejects
|
|
6055
6589
|
// itself); a dynamic equality would need a dyn walk — validate first.
|
|
@@ -6252,6 +6786,14 @@ export function lowerBinary(lowerer, expr) {
|
|
|
6252
6786
|
const optionalStringPlus = lowerOptionalStringPlus();
|
|
6253
6787
|
if (optionalStringPlus)
|
|
6254
6788
|
return optionalStringPlus;
|
|
6789
|
+
if (isJsSourceFile(expr.getSourceFile())) {
|
|
6790
|
+
const primitive = (type) => type.kind === "f64" || type.kind === "string" || type.kind === "bool" || isUnitType(type) ||
|
|
6791
|
+
(type.kind === "union" && (lowerer.unions.get(type.unionId)?.arms.every(primitive) ?? false));
|
|
6792
|
+
if ((left.type.kind === "union" || right.type.kind === "union") && primitive(left.type) && primitive(right.type)) {
|
|
6793
|
+
lowerer.runtimeOptionalArithmeticTypes.set(expr, DYN);
|
|
6794
|
+
return { kind: "libCall", fn: "dyn.add", args: [lowerer.coerceToExpected(left, DYN), lowerer.coerceToExpected(right, DYN)], type: DYN, loc };
|
|
6795
|
+
}
|
|
6796
|
+
}
|
|
6255
6797
|
lowerer.unsupported("SC1043", expr);
|
|
6256
6798
|
break;
|
|
6257
6799
|
case ts.SyntaxKind.MinusToken:
|
|
@@ -6294,8 +6836,11 @@ export function lowerBinary(lowerer, expr) {
|
|
|
6294
6836
|
return indexed;
|
|
6295
6837
|
if (plainBothNum)
|
|
6296
6838
|
return { kind: "bin", op: negated ? "!==" : "===", left, right, type: BOOL, loc };
|
|
6297
|
-
if (bothStr)
|
|
6839
|
+
if (bothStr) {
|
|
6840
|
+
if (left.kind === "strLit" && right.kind === "strLit")
|
|
6841
|
+
return { kind: "boolLit", value: (left.value === right.value) !== negated, type: BOOL, loc };
|
|
6298
6842
|
return { kind: "strEq", negated, left, right, type: BOOL, loc };
|
|
6843
|
+
}
|
|
6299
6844
|
// bool === bool: a plain value compare (the config-drift checks'
|
|
6300
6845
|
// `desired.lanMode !== actual.lanMode` shape).
|
|
6301
6846
|
if (left.type.kind === "bool" && right.type.kind === "bool") {
|
|
@@ -6419,6 +6964,8 @@ export function lowerBinary(lowerer, expr) {
|
|
|
6419
6964
|
if ((idLeft.type.kind === "array" ||
|
|
6420
6965
|
idLeft.type.kind === "map" ||
|
|
6421
6966
|
idLeft.type.kind === "set" ||
|
|
6967
|
+
idLeft.type.kind === "regex" ||
|
|
6968
|
+
idLeft.type.kind === "url" ||
|
|
6422
6969
|
idLeft.type.kind === "object" ||
|
|
6423
6970
|
idLeft.type.kind === "record" ||
|
|
6424
6971
|
// Symbols ARE identity: `Symbol('a') === Symbol('a')` is false,
|
|
@@ -6459,21 +7006,283 @@ export function lowerBinary(lowerer, expr) {
|
|
|
6459
7006
|
default:
|
|
6460
7007
|
break;
|
|
6461
7008
|
}
|
|
6462
|
-
lowerer.unsupported("SC1090", expr, `operator '${ts.tokenToString(op) ?? ts.
|
|
7009
|
+
lowerer.unsupported("SC1090", expr, `operator '${ts.tokenToString(op) ?? ts.syntaxKindName(op)}'`);
|
|
7010
|
+
}
|
|
7011
|
+
function lowerLogicalChain(lowerer, expr) {
|
|
7012
|
+
// Logical chains have a left-nested AST. Keep the native stack bounded
|
|
7013
|
+
// while retaining each pair's checker type and lazy operand evaluation.
|
|
7014
|
+
const parents = [];
|
|
7015
|
+
let current = expr;
|
|
7016
|
+
let result;
|
|
7017
|
+
while (true) {
|
|
7018
|
+
if (current.operatorToken.kind === ts.SyntaxKind.BarBarToken) {
|
|
7019
|
+
const fallback = lowerAbsenceAwareOrChain(lowerer, current);
|
|
7020
|
+
if (fallback !== null) {
|
|
7021
|
+
result = fallback;
|
|
7022
|
+
break;
|
|
7023
|
+
}
|
|
7024
|
+
}
|
|
7025
|
+
const left = current.left;
|
|
7026
|
+
if (ts.isBinaryExpression(left) &&
|
|
7027
|
+
(left.operatorToken.kind === ts.SyntaxKind.AmpersandAmpersandToken ||
|
|
7028
|
+
left.operatorToken.kind === ts.SyntaxKind.BarBarToken) &&
|
|
7029
|
+
!lowerer.chainRecvByNode.has(left)) {
|
|
7030
|
+
parents.push(current);
|
|
7031
|
+
current = left;
|
|
7032
|
+
continue;
|
|
7033
|
+
}
|
|
7034
|
+
result = lowerLogicalPair(lowerer, current, lowerAbsenceProbe(lowerer, left) ?? lowerer.lowerExpr(left));
|
|
7035
|
+
break;
|
|
7036
|
+
}
|
|
7037
|
+
for (let i = parents.length - 1; i >= 0; i--) {
|
|
7038
|
+
result = lowerLogicalPair(lowerer, parents[i], result);
|
|
7039
|
+
}
|
|
7040
|
+
return result;
|
|
7041
|
+
}
|
|
7042
|
+
function lowerLogicalPair(lowerer, expr, left) {
|
|
7043
|
+
const op = expr.operatorToken.kind;
|
|
7044
|
+
const loc = locOf(expr);
|
|
7045
|
+
const right = lowerer.lowerExpr(expr.right);
|
|
7046
|
+
// A LITERAL-unit left operand (a compile-time undefined/null — the
|
|
7047
|
+
// capability-probe members: `process.features.inspector ||
|
|
7048
|
+
// !flag.startsWith('--inspect')` reads undefined in a compiled
|
|
7049
|
+
// binary): statically falsy, so the operator folds JS-exactly —
|
|
7050
|
+
// `unit || X` IS X, `unit && X` IS the unit (X's side effects never
|
|
7051
|
+
// run in JS either; the lowered right is simply dropped). Literal
|
|
7052
|
+
// units only — computed unit-typed values keep the fences below.
|
|
7053
|
+
if (left.kind === "unitLit") {
|
|
7054
|
+
return op === ts.SyntaxKind.BarBarToken ? right : left;
|
|
7055
|
+
}
|
|
7056
|
+
// JS objects and symbols are always truthy. In `ref || fallback`
|
|
7057
|
+
// the fallback is therefore unreachable and the result is the
|
|
7058
|
+
// already-evaluated left value — the browser-fallback idiom used by
|
|
7059
|
+
// packages for `process.argv || []` and `process.env || {}`. The
|
|
7060
|
+
// operand itself remains in the IR, preserving reads/calls that
|
|
7061
|
+
// produce the reference; only the unreachable right lowering drops.
|
|
7062
|
+
if (op === ts.SyntaxKind.BarBarToken && REF_TRUTHY_KINDS.has(left.type.kind)) {
|
|
7063
|
+
return left;
|
|
7064
|
+
}
|
|
7065
|
+
if (left.type.kind === "dyn" || right.type.kind === "dyn") {
|
|
7066
|
+
// A checked-dynamic operand (`fn.name || '<anonymous>'` —
|
|
7067
|
+
// test/common's _mustCallInner): both sides live in the checked-dynamic tree and
|
|
7068
|
+
// the deciding test is ToBoolean over the dyn kind
|
|
7069
|
+
// (scr_dyn_truthy) — JS value semantics exactly, result dyn. The
|
|
7070
|
+
// non-dyn side converts through the usual boundary; a value with
|
|
7071
|
+
// no dyn representation keeps the fence.
|
|
7072
|
+
const l = lowerer.coerceToExpected(left, DYN);
|
|
7073
|
+
const r = lowerer.coerceToExpected(right, DYN);
|
|
7074
|
+
if (l.type.kind !== "dyn" || r.type.kind !== "dyn") {
|
|
7075
|
+
lowerer.unsupported("SC1100", expr, "logical operators on 'unknown' values");
|
|
7076
|
+
}
|
|
7077
|
+
return {
|
|
7078
|
+
kind: "logical",
|
|
7079
|
+
op: op === ts.SyntaxKind.AmpersandAmpersandToken ? "&&" : "||",
|
|
7080
|
+
left: l,
|
|
7081
|
+
right: r,
|
|
7082
|
+
type: DYN, loc,
|
|
7083
|
+
};
|
|
7084
|
+
}
|
|
7085
|
+
if (left.type.kind === "jsval" || right.type.kind === "jsval") {
|
|
7086
|
+
return {
|
|
7087
|
+
kind: "logical",
|
|
7088
|
+
op: op === ts.SyntaxKind.AmpersandAmpersandToken ? "&&" : "||",
|
|
7089
|
+
left: lowerer.jsvalIn(left, expr.left),
|
|
7090
|
+
right: lowerer.jsvalIn(right, expr.right),
|
|
7091
|
+
type: JSVAL, loc,
|
|
7092
|
+
};
|
|
7093
|
+
}
|
|
7094
|
+
if (left.type.kind === "union" || right.type.kind === "union") {
|
|
7095
|
+
// Test the deciding operand in its own representation. The result
|
|
7096
|
+
// contains only the values that can survive short-circuit evaluation.
|
|
7097
|
+
let target = lowerer.mapTypeOf(lowerer.typeOf(expr));
|
|
7098
|
+
// Index reads can be absent without noUncheckedIndexedAccess. Their
|
|
7099
|
+
// explicit nullish fallback still runs, even if the checker discarded it.
|
|
7100
|
+
if (target !== null) {
|
|
7101
|
+
const returned = [right.type, ...(op === ts.SyntaxKind.AmpersandAmpersandToken ? [left.type] : [])];
|
|
7102
|
+
for (const type of returned) {
|
|
7103
|
+
const arms = type.kind === "union" ? lowerer.unions.get(type.unionId)?.arms ?? [] : [type];
|
|
7104
|
+
for (const arm of arms) {
|
|
7105
|
+
if (arm.kind === "undefinedT" && !isUnitType(target))
|
|
7106
|
+
target = lowerer.runtimeOptionalType(target);
|
|
7107
|
+
if (arm.kind === "nullT")
|
|
7108
|
+
target = withUnitArm(target, arm.kind, lowerer.unions) ?? target;
|
|
7109
|
+
}
|
|
7110
|
+
}
|
|
7111
|
+
}
|
|
7112
|
+
// A broad-JSDoc npm-static body can leave the CHECKER result `any`
|
|
7113
|
+
// even after declaration-backed specialization has recovered both
|
|
7114
|
+
// operands (`helpOption && args.find(...)` in commander). When the
|
|
7115
|
+
// left consists only of always-truthy references and falsy units,
|
|
7116
|
+
// the static result is exactly the right operand plus those units.
|
|
7117
|
+
if (op === ts.SyntaxKind.AmpersandAmpersandToken &&
|
|
7118
|
+
target === null &&
|
|
7119
|
+
lowerer.implicitParamTypes !== null &&
|
|
7120
|
+
npmStaticPackageOfPath(expr.getSourceFile().fileName) !== null &&
|
|
7121
|
+
(lowerer.typeOf(expr).flags & ts.TypeFlags.Any) !== 0 &&
|
|
7122
|
+
left.type.kind === "union") {
|
|
7123
|
+
const leftArms = lowerer.unions.get(left.type.unionId)?.arms;
|
|
7124
|
+
const rightArms = right.type.kind === "union"
|
|
7125
|
+
? lowerer.unions.get(right.type.unionId)?.arms
|
|
7126
|
+
: [right.type];
|
|
7127
|
+
const rightIsStatic = rightArms?.every((arm) => arm.kind !== "dyn" && arm.kind !== "caught" && arm.kind !== "jsval" && arm.kind !== "void") ?? false;
|
|
7128
|
+
if (leftArms && rightArms && rightIsStatic &&
|
|
7129
|
+
leftArms.some(isUnitType) &&
|
|
7130
|
+
leftArms.every((arm) => isUnitType(arm) || REF_TRUTHY_KINDS.has(arm.kind))) {
|
|
7131
|
+
const byKey = new Map();
|
|
7132
|
+
for (const arm of [...rightArms, ...leftArms.filter(isUnitType)]) {
|
|
7133
|
+
byKey.set(typeKey(arm), arm);
|
|
7134
|
+
}
|
|
7135
|
+
const arms = [...byKey.values()].sort((a, b) => (typeKey(a) < typeKey(b) ? -1 : 1));
|
|
7136
|
+
if (arms.length > 1) {
|
|
7137
|
+
const source = right.type.kind === "union" ? lowerer.unions.get(right.type.unionId) : undefined;
|
|
7138
|
+
target = { kind: "union", unionId: source
|
|
7139
|
+
? lowerer.unions.transform(source, arms)
|
|
7140
|
+
: lowerer.unions.intern(arms) };
|
|
7141
|
+
}
|
|
7142
|
+
}
|
|
7143
|
+
}
|
|
7144
|
+
if (target?.kind === "union") {
|
|
7145
|
+
// `&&` whose left does NOT fit the result union: the checker
|
|
7146
|
+
// built that result by DROPPING left arms which are always
|
|
7147
|
+
// truthy (`Option | undefined && string | undefined` answers
|
|
7148
|
+
// `string | undefined`, not `Option | string | undefined`).
|
|
7149
|
+
// Evaluate the left once, test it in its own union, and retag it
|
|
7150
|
+
// only on the falsy path. Any stranded reference arms are
|
|
7151
|
+
// unreachable there because every JS object is truthy.
|
|
7152
|
+
if (op === ts.SyntaxKind.AmpersandAmpersandToken && left.type.kind === "union" && !typeEquals(left.type, target)) {
|
|
7153
|
+
const def = lowerer.unions.get(left.type.unionId);
|
|
7154
|
+
const trappable = new Set();
|
|
7155
|
+
def?.arms.forEach((arm, tag) => {
|
|
7156
|
+
if (lowerer.armTag(target.unionId, arm) < 0 && REF_TRUTHY_KINDS.has(arm.kind)) {
|
|
7157
|
+
trappable.add(tag);
|
|
7158
|
+
}
|
|
7159
|
+
});
|
|
7160
|
+
const retag = trappable.size === 0
|
|
7161
|
+
? null
|
|
7162
|
+
: lowerer.unionRetagHelper(left.type.unionId, target.unionId, loc, trappable);
|
|
7163
|
+
if (retag) {
|
|
7164
|
+
lowerer.requireTruthyUnion(left.type.unionId, expr);
|
|
7165
|
+
const stmts = [];
|
|
7166
|
+
let stable = left;
|
|
7167
|
+
if (!isSafeToRepeat(left)) {
|
|
7168
|
+
const local = lowerer.declareHiddenLocal("%and", left.type);
|
|
7169
|
+
stmts.push({ kind: "varDecl", localId: local.id, init: left, loc });
|
|
7170
|
+
stable = varRef(local.id, left.type, loc);
|
|
7171
|
+
}
|
|
7172
|
+
const result = {
|
|
7173
|
+
kind: "ternary",
|
|
7174
|
+
cond: lowerer.ensureBool(stable, expr.left),
|
|
7175
|
+
then: lowerer.coerceInto(expr.right, right, target),
|
|
7176
|
+
else_: { kind: "call", callee: retag, args: [stable], type: target, loc },
|
|
7177
|
+
type: target,
|
|
7178
|
+
loc,
|
|
7179
|
+
};
|
|
7180
|
+
return stmts.length === 0 ? result : { kind: "seqExpr", stmts, result, type: target, loc };
|
|
7181
|
+
}
|
|
7182
|
+
}
|
|
7183
|
+
// `||` whose left does NOT fit the result union: the checker built
|
|
7184
|
+
// that result by DROPPING the left's falsy arms (`process.env.X ||
|
|
7185
|
+
// null` is `string | null`, `|| 3000` is `string | number` — the
|
|
7186
|
+
// `undefined` is gone from both), so coercing the left eagerly, as
|
|
7187
|
+
// the shared shape below does, retags an arm the test is about to
|
|
7188
|
+
// rule out and throws where Node yields the default. Single-eval
|
|
7189
|
+
// instead: test the left in its OWN union and retag only on the
|
|
7190
|
+
// truthy side, where the dropped arms are unreachable.
|
|
7191
|
+
if (op === ts.SyntaxKind.BarBarToken && left.type.kind === "union" && !typeEquals(left.type, target)) {
|
|
7192
|
+
const retag = lowerer.unionRetagHelper(left.type.unionId, target.unionId, loc);
|
|
7193
|
+
if (retag) {
|
|
7194
|
+
lowerer.requireTruthyUnion(left.type.unionId, expr);
|
|
7195
|
+
return {
|
|
7196
|
+
kind: "orDefault",
|
|
7197
|
+
left,
|
|
7198
|
+
right: lowerer.coerceInto(expr.right, right, target),
|
|
7199
|
+
retag,
|
|
7200
|
+
type: target,
|
|
7201
|
+
loc,
|
|
7202
|
+
};
|
|
7203
|
+
}
|
|
7204
|
+
}
|
|
7205
|
+
lowerer.requireTruthyUnion(target.unionId, expr);
|
|
7206
|
+
return {
|
|
7207
|
+
kind: "logical",
|
|
7208
|
+
op: op === ts.SyntaxKind.AmpersandAmpersandToken ? "&&" : "||",
|
|
7209
|
+
left: lowerer.coerceInto(expr.left, left, target),
|
|
7210
|
+
right: lowerer.coerceInto(expr.right, right, target),
|
|
7211
|
+
type: target, loc,
|
|
7212
|
+
};
|
|
7213
|
+
}
|
|
7214
|
+
// `u || d` NARROWED by the default: the checker types the result
|
|
7215
|
+
// as u's single non-unit arm (`marker() || "default"`) — evaluate
|
|
7216
|
+
// u once, truthy extracts the arm, falsy takes d lazily (the
|
|
7217
|
+
// orDefault node, nullish's truthiness sibling). An UNMAPPABLE
|
|
7218
|
+
// checker result takes the same rule (`options.runner ||
|
|
7219
|
+
// defaultRunner` — tsc's union of two structurally-compatible
|
|
7220
|
+
// function types has no representation, while the left's one
|
|
7221
|
+
// non-unit arm is the only representable answer): the default
|
|
7222
|
+
// must coerce into the arm or fence on its own.
|
|
7223
|
+
if (op === ts.SyntaxKind.BarBarToken &&
|
|
7224
|
+
left.type.kind === "union") {
|
|
7225
|
+
const def = lowerer.unions.get(left.type.unionId);
|
|
7226
|
+
const rest = def ? def.arms.filter((a) => !isUnitType(a)) : [];
|
|
7227
|
+
const funcArmDefault = rest[0]?.kind === "func" && (target === null || target.kind === "func");
|
|
7228
|
+
if (rest.length === 1 && ((target !== null && typeEquals(target, rest[0])) || funcArmDefault)) {
|
|
7229
|
+
lowerer.requireTruthyUnion(left.type.unionId, expr);
|
|
7230
|
+
const dflt = lowerer.lowerExprExpecting(expr.right, rest[0]);
|
|
7231
|
+
return { kind: "orDefault", left, right: dflt, type: rest[0], loc };
|
|
7232
|
+
}
|
|
7233
|
+
}
|
|
7234
|
+
// JavaScript defaults can join differently inferred object layouts.
|
|
7235
|
+
// When both operands have native checked representations, preserve
|
|
7236
|
+
// the deciding value and short-circuit evaluation in that domain.
|
|
7237
|
+
if (isJsSourceFile(expr.getSourceFile()) &&
|
|
7238
|
+
lowerer.dynConvertible(left.type) && lowerer.dynConvertible(right.type)) {
|
|
7239
|
+
return {
|
|
7240
|
+
kind: "logical", op: op === ts.SyntaxKind.AmpersandAmpersandToken ? "&&" : "||",
|
|
7241
|
+
left: lowerer.coerceInto(expr.left, left, DYN),
|
|
7242
|
+
right: lowerer.coerceInto(expr.right, right, DYN), type: DYN, loc,
|
|
7243
|
+
};
|
|
7244
|
+
}
|
|
7245
|
+
lowerer.unsupported("SC1090", expr, `logical operators on union-typed values outside conditions (${NARROW_FIRST})`);
|
|
7246
|
+
}
|
|
7247
|
+
const kind = left.type.kind;
|
|
7248
|
+
if (kind !== right.type.kind ||
|
|
7249
|
+
(kind !== "f64" && kind !== "string" && kind !== "bool")) {
|
|
7250
|
+
// Mixed PLAIN operands (`value || null`, `flag || undefined`,
|
|
7251
|
+
// `s || 0`): JS value semantics still compile when the checker
|
|
7252
|
+
// types the RESULT as one union both operands coerce into — the
|
|
7253
|
+
// same lift the union-operand path above takes, arriving here
|
|
7254
|
+
// with two plain arm values instead.
|
|
7255
|
+
const target = lowerer.mapTypeOf(lowerer.typeOf(expr));
|
|
7256
|
+
if (target?.kind === "union") {
|
|
7257
|
+
lowerer.requireTruthyUnion(target.unionId, expr);
|
|
7258
|
+
return {
|
|
7259
|
+
kind: "logical",
|
|
7260
|
+
op: op === ts.SyntaxKind.AmpersandAmpersandToken ? "&&" : "||",
|
|
7261
|
+
left: lowerer.coerceInto(expr.left, left, target),
|
|
7262
|
+
right: lowerer.coerceInto(expr.right, right, target),
|
|
7263
|
+
type: target, loc,
|
|
7264
|
+
};
|
|
7265
|
+
}
|
|
7266
|
+
lowerer.unsupported("SC1042", expr);
|
|
7267
|
+
}
|
|
7268
|
+
return {
|
|
7269
|
+
kind: "logical",
|
|
7270
|
+
op: op === ts.SyntaxKind.AmpersandAmpersandToken ? "&&" : "||",
|
|
7271
|
+
left, right, type: left.type, loc,
|
|
7272
|
+
};
|
|
6463
7273
|
}
|
|
6464
7274
|
function lowerAbsenceAwareOrChain(lowerer, expr) {
|
|
6465
7275
|
const nodes = [];
|
|
6466
|
-
const
|
|
6467
|
-
|
|
6468
|
-
|
|
6469
|
-
|
|
6470
|
-
|
|
7276
|
+
const pending = [expr];
|
|
7277
|
+
while (pending.length !== 0) {
|
|
7278
|
+
const node = pending.pop();
|
|
7279
|
+
if (ts.isBinaryExpression(node) && node.operatorToken.kind === ts.SyntaxKind.BarBarToken) {
|
|
7280
|
+
pending.push(node.right, node.left);
|
|
6471
7281
|
}
|
|
6472
7282
|
else {
|
|
6473
7283
|
nodes.push(node);
|
|
6474
7284
|
}
|
|
6475
|
-
}
|
|
6476
|
-
collect(expr);
|
|
7285
|
+
}
|
|
6477
7286
|
if (nodes.length < 3)
|
|
6478
7287
|
return null;
|
|
6479
7288
|
const lowered = nodes.map((node) => lowerAbsenceProbe(lowerer, node) ?? lowerer.lowerExpr(node));
|
|
@@ -6544,10 +7353,7 @@ function errorRootedObjectOf(lowerer, t) {
|
|
|
6544
7353
|
* shape, so the sibling typeof lowerings keep trying. */
|
|
6545
7354
|
function lowerErrorCodeTypeofTest(lowerer, expr, loc) {
|
|
6546
7355
|
const negated = expr.operatorToken.kind === ts.SyntaxKind.ExclamationEqualsEqualsToken;
|
|
6547
|
-
for (const [a, b] of
|
|
6548
|
-
[expr.left, expr.right],
|
|
6549
|
-
[expr.right, expr.left],
|
|
6550
|
-
]) {
|
|
7356
|
+
for (const [a, b] of operandPairs(expr)) {
|
|
6551
7357
|
if (!ts.isTypeOfExpression(a))
|
|
6552
7358
|
continue;
|
|
6553
7359
|
let prop = a.expression;
|
|
@@ -6607,10 +7413,7 @@ function lowerErrorCodeTypeofTest(lowerer, expr, loc) {
|
|
|
6607
7413
|
}
|
|
6608
7414
|
export function lowerCaughtTypeofTest(lowerer, expr, loc) {
|
|
6609
7415
|
const negated = expr.operatorToken.kind === ts.SyntaxKind.ExclamationEqualsEqualsToken;
|
|
6610
|
-
for (const [a, b] of
|
|
6611
|
-
[expr.left, expr.right],
|
|
6612
|
-
[expr.right, expr.left],
|
|
6613
|
-
]) {
|
|
7416
|
+
for (const [a, b] of operandPairs(expr)) {
|
|
6614
7417
|
if (!ts.isTypeOfExpression(a))
|
|
6615
7418
|
continue;
|
|
6616
7419
|
const local = lowerer.caughtLocalOf(a.expression);
|
|
@@ -6619,7 +7422,7 @@ export function lowerCaughtTypeofTest(lowerer, expr, loc) {
|
|
|
6619
7422
|
if (!ts.isStringLiteral(b)) {
|
|
6620
7423
|
lowerer.unsupported("SC1090", expr, "'typeof' catch-binding tests against non-literal strings");
|
|
6621
7424
|
}
|
|
6622
|
-
if (b.text === "string" || b.text === "number" || b.text === "boolean") {
|
|
7425
|
+
if (b.text === "string" || b.text === "number" || b.text === "boolean" || b.text === "object") {
|
|
6623
7426
|
return {
|
|
6624
7427
|
kind: "caughtTest",
|
|
6625
7428
|
value: { kind: "varRef", localId: local.id, type: CAUGHT, loc },
|
|
@@ -6629,8 +7432,7 @@ export function lowerCaughtTypeofTest(lowerer, expr, loc) {
|
|
|
6629
7432
|
loc,
|
|
6630
7433
|
};
|
|
6631
7434
|
}
|
|
6632
|
-
lowerer.unsupported("SC1090", b, `'typeof' catch-binding tests against "${b.text}" (only "string"/"number"/"boolean" `
|
|
6633
|
-
`narrow a catch binding; use 'instanceof' for objects)`);
|
|
7435
|
+
lowerer.unsupported("SC1090", b, `'typeof' catch-binding tests against "${b.text}" (only "string"/"number"/"boolean"/"object" are supported)`);
|
|
6634
7436
|
}
|
|
6635
7437
|
return null;
|
|
6636
7438
|
}
|
|
@@ -6734,12 +7536,13 @@ export function aliasTypeofNarrows(lowerer, cond, polarity) {
|
|
|
6734
7536
|
walk(cond, polarity);
|
|
6735
7537
|
return out;
|
|
6736
7538
|
}
|
|
7539
|
+
/** Visit both operand orders while keeping the sequence an array of pairs. */
|
|
7540
|
+
function operandPairs(expr) {
|
|
7541
|
+
return [[expr.left, expr.right], [expr.right, expr.left]];
|
|
7542
|
+
}
|
|
6737
7543
|
/** One ===/!== leaf of aliasTypeofNarrows — the qualification battery. */
|
|
6738
7544
|
function aliasTypeofArmOf(lowerer, e, strip) {
|
|
6739
|
-
for (const [a0, b] of
|
|
6740
|
-
[e.left, e.right],
|
|
6741
|
-
[e.right, e.left],
|
|
6742
|
-
]) {
|
|
7545
|
+
for (const [a0, b] of operandPairs(e)) {
|
|
6743
7546
|
if (!ts.isStringLiteral(b))
|
|
6744
7547
|
continue;
|
|
6745
7548
|
const a = strip(a0);
|
|
@@ -6768,7 +7571,7 @@ function aliasTypeofArmOf(lowerer, e, strip) {
|
|
|
6768
7571
|
if (!bindingNeverReassigned(lowerer, valSym, valDecl))
|
|
6769
7572
|
continue;
|
|
6770
7573
|
const fn = valDecl.parent;
|
|
6771
|
-
if (!(e.pos >= fn.pos && e.end <= fn.end))
|
|
7574
|
+
if (fn === undefined || !(e.pos >= fn.pos && e.end <= fn.end))
|
|
6772
7575
|
continue;
|
|
6773
7576
|
const valT = lowerer.typeOf(opnd); // override-aware: implicit bindings compose
|
|
6774
7577
|
const mapped = lowerer.mapTypeOf(valT);
|
|
@@ -6810,10 +7613,7 @@ function aliasTypeofArmOf(lowerer, e, strip) {
|
|
|
6810
7613
|
* bare-typeof value form then composes with strEq for pure operands). */
|
|
6811
7614
|
function lowerUnionTypeofTest(lowerer, expr, loc) {
|
|
6812
7615
|
const negated = expr.operatorToken.kind === ts.SyntaxKind.ExclamationEqualsEqualsToken;
|
|
6813
|
-
for (const [a, b] of
|
|
6814
|
-
[expr.left, expr.right],
|
|
6815
|
-
[expr.right, expr.left],
|
|
6816
|
-
]) {
|
|
7616
|
+
for (const [a, b] of operandPairs(expr)) {
|
|
6817
7617
|
if (!ts.isTypeOfExpression(a))
|
|
6818
7618
|
continue;
|
|
6819
7619
|
if (lowerer.mapTypeOf(lowerer.typeOf(a.expression))?.kind !== "union")
|
|
@@ -6861,12 +7661,13 @@ function lowerUnionTypeofTest(lowerer, expr, loc) {
|
|
|
6861
7661
|
* side is a typeof over a dyn-typed operand (not this pattern). */
|
|
6862
7662
|
function lowerDynTypeofTest(lowerer, expr, loc) {
|
|
6863
7663
|
const negated = expr.operatorToken.kind === ts.SyntaxKind.ExclamationEqualsEqualsToken;
|
|
6864
|
-
for (const [a, b] of
|
|
6865
|
-
[expr.left, expr.right],
|
|
6866
|
-
[expr.right, expr.left],
|
|
6867
|
-
]) {
|
|
7664
|
+
for (const [a, b] of operandPairs(expr)) {
|
|
6868
7665
|
if (!ts.isTypeOfExpression(a))
|
|
6869
7666
|
continue;
|
|
7667
|
+
// Non-literal comparisons use the ordinary typeof string value,
|
|
7668
|
+
// preserving both operands' evaluation order without a narrowing test.
|
|
7669
|
+
if (!ts.isStringLiteral(b))
|
|
7670
|
+
continue;
|
|
6870
7671
|
const declared = lowerer.mapTypeOf(lowerer.typeOf(a.expression));
|
|
6871
7672
|
let value;
|
|
6872
7673
|
if (declared?.kind === "dyn") {
|
|
@@ -6924,10 +7725,7 @@ function lowerDynTypeofTest(lowerer, expr, loc) {
|
|
|
6924
7725
|
value = probed;
|
|
6925
7726
|
}
|
|
6926
7727
|
}
|
|
6927
|
-
if (
|
|
6928
|
-
lowerer.unsupported("SC1090", expr, "'typeof' tests on 'unknown' values against non-literal strings");
|
|
6929
|
-
}
|
|
6930
|
-
if (b.text === "string" || b.text === "number" || b.text === "boolean" || b.text === "undefined") {
|
|
7728
|
+
if (b.text === "string" || b.text === "number" || b.text === "boolean" || b.text === "undefined" || b.text === "bigint" || b.text === "symbol") {
|
|
6931
7729
|
return {
|
|
6932
7730
|
kind: "dynTest",
|
|
6933
7731
|
test: b.text,
|
|
@@ -6964,14 +7762,8 @@ function lowerDynTypeofTest(lowerer, expr, loc) {
|
|
|
6964
7762
|
loc,
|
|
6965
7763
|
};
|
|
6966
7764
|
}
|
|
6967
|
-
//
|
|
6968
|
-
|
|
6969
|
-
// + bytes + Error + functions), so the test's answer is a
|
|
6970
|
-
// compile-time constant: false for ===, true for !==. The capability
|
|
6971
|
-
// probes this settles (`typeof ms === 'bigint'` in dual-mode number
|
|
6972
|
-
// helpers) then FOLD their impossible arm (lowerTernary), which is
|
|
6973
|
-
// what lets the reachable arm compile statically.
|
|
6974
|
-
if (b.text === "bigint" || b.text === "symbol") {
|
|
7765
|
+
// Native checked values do not yet carry symbols.
|
|
7766
|
+
if (b.text === "symbol") {
|
|
6975
7767
|
return { kind: "boolLit", value: negated, type: BOOL, loc };
|
|
6976
7768
|
}
|
|
6977
7769
|
lowerer.unsupported("SC1090", b, `'typeof' tests against "${b.text}" on 'unknown' values (only "string"/"number"/` +
|
|
@@ -6993,7 +7785,7 @@ function lowerReplacedExportsRead(lowerer, expr) {
|
|
|
6993
7785
|
if (!ts.isIdentifier(recv) || recv.text !== "exports")
|
|
6994
7786
|
return null;
|
|
6995
7787
|
const sf = expr.getSourceFile();
|
|
6996
|
-
if (!isJsSourceFile(sf) || isNodeEsmFile(sf))
|
|
7788
|
+
if (!isJsSourceFile(sf) || isNodeEsmFile(sf, lowerer.program))
|
|
6997
7789
|
return null;
|
|
6998
7790
|
if (lowerer.peekLocal(recv) || lowerer.globalOf(recv))
|
|
6999
7791
|
return null; // a user binding shadows
|
|
@@ -7170,6 +7962,34 @@ export function lowerInstanceOf(lowerer, expr, loc) {
|
|
|
7170
7962
|
const right = lowerer.lowerExpr(expr.right);
|
|
7171
7963
|
return { kind: "jsOp", op: "instanceOf", args: [left, right], type: BOOL, loc };
|
|
7172
7964
|
}
|
|
7965
|
+
if (!lowerer.dynamic && (lowerer.isStdlibGlobal(expr.right, "Set") || lowerer.isStdlibGlobal(expr.right, "Map"))) {
|
|
7966
|
+
const collection = lowerer.isStdlibGlobal(expr.right, "Map") ? "map" : "set";
|
|
7967
|
+
const value = lowerer.lowerExpr(expr.left);
|
|
7968
|
+
if (value.type.kind === "dyn")
|
|
7969
|
+
return { kind: "libCall", fn: collection === "map" ? "dyn.nativeMapIs" : "dyn.nativeSetIs", args: [value], type: BOOL, loc };
|
|
7970
|
+
if (value.type.kind === collection)
|
|
7971
|
+
return { kind: "seqExpr", stmts: [{ kind: "exprStmt", expr: value, loc }],
|
|
7972
|
+
result: { kind: "boolLit", value: true, type: BOOL, loc }, type: BOOL, loc };
|
|
7973
|
+
}
|
|
7974
|
+
if (lowerer.isStdlibGlobal(expr.right, "WeakMap") || lowerer.isStdlibGlobal(expr.right, "WeakSet")) {
|
|
7975
|
+
if (lowerer.dynamic) {
|
|
7976
|
+
const value = lowerer.jsvalIn(lowerer.lowerExpr(expr.left), expr.left);
|
|
7977
|
+
const ctor = { kind: "jsOp", op: "globalGet", name: lowerer.isStdlibGlobal(expr.right, "WeakMap") ? "WeakMap" : "WeakSet", args: [], type: JSVAL, loc };
|
|
7978
|
+
return { kind: "jsOp", op: "instanceOf", args: [value, ctor], type: BOOL, loc };
|
|
7979
|
+
}
|
|
7980
|
+
const value = lowerer.coerceInto(expr.left, lowerer.lowerExpr(expr.left), DYN);
|
|
7981
|
+
return { kind: "libCall", fn: lowerer.isStdlibGlobal(expr.right, "WeakMap") ? "weakMap.is" : "weakSet.is", args: [value], type: BOOL, loc };
|
|
7982
|
+
}
|
|
7983
|
+
const rhsBuiltin = ts.isIdentifier(expr.right) ? lowerer.builtinImportOf(expr.right) :
|
|
7984
|
+
ts.isPropertyAccessExpression(expr.right) ? lowerer.builtinMemberOf(expr.right) : null;
|
|
7985
|
+
if (lowerer.isStdlibGlobal(expr.right, "URL") || rhsBuiltin?.module === "url" && rhsBuiltin.member === "URL") {
|
|
7986
|
+
const value = lowerer.coerceInto(expr.left, lowerer.lowerExpr(expr.left), DYN);
|
|
7987
|
+
return { kind: "libCall", fn: "dyn.nativeUrlIs", args: [value], type: BOOL, loc };
|
|
7988
|
+
}
|
|
7989
|
+
if (lowerer.isStdlibGlobal(expr.right, "ArrayBuffer")) {
|
|
7990
|
+
const value = lowerer.coerceInto(expr.left, lowerer.lowerExpr(expr.left), DYN);
|
|
7991
|
+
return { kind: "libCall", fn: "arrayBuffer.is", args: [value], type: BOOL, loc };
|
|
7992
|
+
}
|
|
7173
7993
|
const rhsSymbol = ts.isIdentifier(expr.right) ? lowerer.resolveValueSymbol(expr.right) : null;
|
|
7174
7994
|
// `x instanceof events.EventEmitter` — the namespace-member spelling
|
|
7175
7995
|
// resolves to the same ambient class as the named import.
|
|
@@ -7203,29 +8023,41 @@ export function lowerInstanceOf(lowerer, expr, loc) {
|
|
|
7203
8023
|
// decoration result — fall to the class-VALUE path below, whose
|
|
7204
8024
|
// instanceOfValue reads the interval off the bound class object.
|
|
7205
8025
|
const directRhs = rhsSymbol ? lowerer.classBySymbol.get(rhsSymbol) : undefined;
|
|
7206
|
-
const target = (directRhs?.classDecorators?.valueGlobalId !== undefined ? undefined : directRhs) ??
|
|
8026
|
+
const target = (directRhs?.localClass || directRhs?.classDecorators?.valueGlobalId !== undefined ? undefined : directRhs) ??
|
|
7207
8027
|
rhsNsClass ??
|
|
7208
8028
|
lowerer.builtinErrorInfoOf(rhsSymbol) ??
|
|
8029
|
+
lowerer.builtinErrorInfoOf(rhsMemberSymbol) ??
|
|
7209
8030
|
lowerer.builtinEmitterInfoOf(rhsSymbol) ??
|
|
7210
8031
|
lowerer.builtinEmitterInfoOf(rhsMemberSymbol) ??
|
|
7211
8032
|
lowerer.builtinStreamInfoOf(rhsSymbol) ??
|
|
7212
8033
|
lowerer.builtinStreamInfoOf(rhsMemberSymbol) ??
|
|
7213
8034
|
undefined;
|
|
7214
8035
|
if (!target) {
|
|
7215
|
-
|
|
7216
|
-
|
|
7217
|
-
|
|
7218
|
-
|
|
7219
|
-
|
|
7220
|
-
|
|
7221
|
-
|
|
7222
|
-
|
|
7223
|
-
|
|
7224
|
-
|
|
7225
|
-
|
|
7226
|
-
|
|
7227
|
-
|
|
7228
|
-
|
|
8036
|
+
const date = lowerer.isStdlibGlobal(expr.right, "Date");
|
|
8037
|
+
const url = lowerer.isStdlibGlobal(expr.right, "URL");
|
|
8038
|
+
if ((date || url) && !lowerer.caughtLocalOf(expr.left)) {
|
|
8039
|
+
const value = lowerer.lowerExpr(expr.left);
|
|
8040
|
+
if (value.type.kind === "dyn")
|
|
8041
|
+
return { kind: "libCall", fn: date ? "dyn.nativeDateIs" : "dyn.nativeUrlIs", args: [value], type: BOOL, loc };
|
|
8042
|
+
if (value.type.kind === "union")
|
|
8043
|
+
lowerer.unsupported("SC1090", expr, "Date or URL instanceof over a union");
|
|
8044
|
+
return { kind: "seqExpr", stmts: [{ kind: "exprStmt", expr: value, loc }],
|
|
8045
|
+
result: { kind: "boolLit", value: value.type.kind === (date ? "date" : "url"), type: BOOL, loc }, type: BOOL, loc };
|
|
8046
|
+
}
|
|
8047
|
+
// Unknown storage retains each numeric typed array's exact native brand.
|
|
8048
|
+
if (ts.isIdentifier(expr.right) && !lowerer.caughtLocalOf(expr.left)) {
|
|
8049
|
+
const elem = Object.keys(BYTES_ELEMENT_NAME).find((kind) => lowerer.isStdlibGlobal(expr.right, BYTES_ELEMENT_NAME[kind]));
|
|
8050
|
+
if (elem !== undefined) {
|
|
8051
|
+
const left = lowerer.lowerExpr(expr.left);
|
|
8052
|
+
if (left.type.kind === "dyn") {
|
|
8053
|
+
return { kind: "dynTest", test: "bytes", bytesElem: elem, value: left, type: BOOL, loc };
|
|
8054
|
+
}
|
|
8055
|
+
if (left.type.kind === "bytes") {
|
|
8056
|
+
return {
|
|
8057
|
+
kind: "seqExpr", stmts: [{ kind: "exprStmt", expr: left, loc }],
|
|
8058
|
+
result: { kind: "boolLit", value: left.type.elem === elem, type: BOOL, loc }, type: BOOL, loc,
|
|
8059
|
+
};
|
|
8060
|
+
}
|
|
7229
8061
|
}
|
|
7230
8062
|
}
|
|
7231
8063
|
// `x instanceof RegExp` over a union with a regex arm (the
|
|
@@ -7238,6 +8070,8 @@ export function lowerInstanceOf(lowerer, expr, loc) {
|
|
|
7238
8070
|
lowerer.isStdlibGlobal(expr.right, "RegExp") &&
|
|
7239
8071
|
!lowerer.caughtLocalOf(expr.left)) {
|
|
7240
8072
|
const left = lowerer.lowerExpr(expr.left);
|
|
8073
|
+
if (left.type.kind === "dyn")
|
|
8074
|
+
return { kind: "libCall", fn: "dyn.nativeRegexIs", args: [left], type: BOOL, loc };
|
|
7241
8075
|
if (left.type.kind === "union") {
|
|
7242
8076
|
const def = lowerer.unions.get(left.type.unionId);
|
|
7243
8077
|
const tag = def ? def.arms.findIndex((a) => a.kind === "regex") : -1;
|
|
@@ -7255,7 +8089,7 @@ export function lowerInstanceOf(lowerer, expr, loc) {
|
|
|
7255
8089
|
// STANDALONE target class has exactly one possible runtime value
|
|
7256
8090
|
// (itself — no descendants can flow into the slot), so the answer
|
|
7257
8091
|
// folds statically exactly like the named-target folds below.
|
|
7258
|
-
const rhsClassval =
|
|
8092
|
+
const rhsClassval = storedClassValueType(lowerer, expr.right);
|
|
7259
8093
|
if (rhsClassval?.kind === "classval" && !lowerer.caughtLocalOf(expr.left)) {
|
|
7260
8094
|
const targetInfo = lowerer.classes.get(rhsClassval.className);
|
|
7261
8095
|
if (!targetInfo) {
|
|
@@ -7272,7 +8106,8 @@ export function lowerInstanceOf(lowerer, expr, loc) {
|
|
|
7272
8106
|
const lhsInfo = lowerer.classes.get(left.type.className);
|
|
7273
8107
|
if (!lhsInfo)
|
|
7274
8108
|
throw new InternalCompilerError(`lowerer bug: unknown class ${left.type.className}`);
|
|
7275
|
-
if (lowerer.inHierarchy(targetInfo) && lowerer.inHierarchy(lhsInfo))
|
|
8109
|
+
if ((lowerer.inHierarchy(targetInfo) && lowerer.inHierarchy(lhsInfo)) ||
|
|
8110
|
+
(targetInfo.localClass !== undefined && lhsInfo.localClass !== undefined)) {
|
|
7276
8111
|
const classValue = lowerer.lowerExpr(expr.right);
|
|
7277
8112
|
if (classValue.type.kind !== "classval")
|
|
7278
8113
|
lowerer.badType(expr.right, lowerer.typeOf(expr.right));
|
|
@@ -7319,6 +8154,9 @@ export function lowerInstanceOf(lowerer, expr, loc) {
|
|
|
7319
8154
|
// (name strings are user-writable), so `u instanceof TypeError` keeps
|
|
7320
8155
|
// the fence. Reads past the narrow bridge through maybeNarrow's
|
|
7321
8156
|
// validated %Error extraction. SEMANTICS.md 67.
|
|
8157
|
+
if (left.type.kind === "dyn" && !target.def.runtime && !target.builtinError && !target.builtinEmitter && !target.builtinStream && !target.localClass) {
|
|
8158
|
+
return classInstanceOf(lowerer, left, target, loc);
|
|
8159
|
+
}
|
|
7322
8160
|
if (left.type.kind === "dyn" && target.def.name === "%Error") {
|
|
7323
8161
|
return { kind: "dynTest", test: "error", value: left, type: BOOL, loc };
|
|
7324
8162
|
}
|
|
@@ -7341,6 +8179,44 @@ export function lowerInstanceOf(lowerer, expr, loc) {
|
|
|
7341
8179
|
if (left.type.kind === "dyn") {
|
|
7342
8180
|
lowerer.unsupported("SC1090", expr, `'instanceof ${target.def.name.replace(/^%/, "")}' on 'unknown' values (only the Error classes answer — test 'instanceof Error' and read '.name')`);
|
|
7343
8181
|
}
|
|
8182
|
+
if (left.type.kind === "union") {
|
|
8183
|
+
const unionId = left.type.unionId;
|
|
8184
|
+
const arms = lowerer.unions.get(unionId)?.arms;
|
|
8185
|
+
// Ordinary records and scalar values do not carry a class prototype.
|
|
8186
|
+
// Dynamic/opaque runtime objects keep their existing instanceof fences.
|
|
8187
|
+
if (arms?.every((arm) => arm.kind === "object" || arm.kind === "record" ||
|
|
8188
|
+
arm.kind === "f64" || arm.kind === "string" || arm.kind === "bool" ||
|
|
8189
|
+
arm.kind === "bigint" || arm.kind === "symbol" || isUnitType(arm))) {
|
|
8190
|
+
const key = `instanceof.union:${unionId}:${target.def.name}`;
|
|
8191
|
+
let helper = lowerer.widthHelpers.get(key);
|
|
8192
|
+
if (!helper) {
|
|
8193
|
+
helper = `%instanceof.union.${lowerer.widthHelpers.size}`;
|
|
8194
|
+
lowerer.widthHelpers.set(key, helper);
|
|
8195
|
+
const value = { kind: "varRef", localId: "value.0", type: left.type, loc };
|
|
8196
|
+
const body = [];
|
|
8197
|
+
arms.forEach((arm, tag) => {
|
|
8198
|
+
if (arm.kind !== "object")
|
|
8199
|
+
return;
|
|
8200
|
+
const lhsInfo = lowerer.classes.get(arm.className);
|
|
8201
|
+
if (!lhsInfo)
|
|
8202
|
+
throw new InternalCompilerError(`lowerer bug: unknown class ${arm.className}`);
|
|
8203
|
+
const result = lowerer.inHierarchy(lhsInfo) && lowerer.inHierarchy(target)
|
|
8204
|
+
? { kind: "instanceOf", value: { kind: "unionNarrow", unionId, tag, value, type: arm, loc }, className: target.def.name, type: BOOL, loc }
|
|
8205
|
+
: { kind: "boolLit", value: arm.className === target.def.name || lowerer.isSubclassOf(arm.className, target.def.name), type: BOOL, loc };
|
|
8206
|
+
body.push({
|
|
8207
|
+
kind: "if", cond: { kind: "unionIsTag", unionId, tag, negated: false, value, type: BOOL, loc },
|
|
8208
|
+
then: [{ kind: "return", value: result, loc }], else_: null, loc,
|
|
8209
|
+
});
|
|
8210
|
+
});
|
|
8211
|
+
body.push({ kind: "return", value: { kind: "boolLit", value: false, type: BOOL, loc }, loc });
|
|
8212
|
+
lowerer.liftedFns.push({
|
|
8213
|
+
name: helper, params: [{ localId: "value.0", name: "value", type: left.type }], returnType: BOOL,
|
|
8214
|
+
locals: [{ id: "value.0", name: "value", type: left.type, mutable: false }], body, loc,
|
|
8215
|
+
});
|
|
8216
|
+
}
|
|
8217
|
+
return { kind: "call", callee: helper, args: [left], type: BOOL, loc };
|
|
8218
|
+
}
|
|
8219
|
+
}
|
|
7344
8220
|
if (left.type.kind !== "object") {
|
|
7345
8221
|
lowerer.unsupported("SC1090", expr, "'instanceof' on values other than class instances (narrow union-typed values first)");
|
|
7346
8222
|
}
|
|
@@ -7513,6 +8389,12 @@ function lowerPrivateIn(lowerer, expr, priv, loc) {
|
|
|
7513
8389
|
* and dyn/unknown stay fenced. Keys are literal strings — a computed key
|
|
7514
8390
|
* over a shape would need the runtime key table. */
|
|
7515
8391
|
function lowerInExpression(lowerer, expr, loc) {
|
|
8392
|
+
if (lowerer.isStdlibGlobal(expr.right, "process") && lowerer.foldedStringKeyOf(expr.left) === "hrtime") {
|
|
8393
|
+
return { kind: "boolLit", value: true, type: BOOL, loc };
|
|
8394
|
+
}
|
|
8395
|
+
const globalKey = globalSymbolKey(lowerer, expr.right, expr.left);
|
|
8396
|
+
if (globalKey)
|
|
8397
|
+
return { kind: "libCall", fn: "dyn.globalSymbolHas", args: [globalKey], type: BOOL, loc };
|
|
7516
8398
|
// `#name in obj` — the ergonomic brand check (ES2022) — resolves
|
|
7517
8399
|
// before any string-key machinery: the left operand is a private
|
|
7518
8400
|
// NAME, not a value.
|
|
@@ -7564,14 +8446,13 @@ function lowerInExpression(lowerer, expr, loc) {
|
|
|
7564
8446
|
// `in` shares the coercion).
|
|
7565
8447
|
{
|
|
7566
8448
|
const probed = tryLowerExpression(lowerer, expr.right);
|
|
7567
|
-
if (probed
|
|
7568
|
-
|
|
7569
|
-
if (k.type.kind === "
|
|
7570
|
-
|
|
7571
|
-
const
|
|
7572
|
-
|
|
7573
|
-
|
|
7574
|
-
}
|
|
8449
|
+
if (probed && (probed.type.kind === "dyn" || isDynTypedRefType(probed.type))) {
|
|
8450
|
+
const k = lowerer.lowerExpr(expr.left); // JS order: the key evaluates first
|
|
8451
|
+
if (k.type.kind === "symbol" || k.type.kind === "dyn" || k.type.kind === "string" || k.type.kind === "f64") {
|
|
8452
|
+
const boxed = lowerer.coerceToExpected(k, DYN);
|
|
8453
|
+
const keyLocal = lowerer.declareHiddenLocal("%inKey", DYN);
|
|
8454
|
+
return { kind: "seqExpr", stmts: [{ kind: "varDecl", localId: keyLocal.id, init: boxed, loc }],
|
|
8455
|
+
result: { kind: "libCall", fn: "dyn.hasKeyComputed", args: [lowerer.coerceToExpected(lowerer.lowerExpr(expr.right), DYN), varRef(keyLocal.id, DYN, loc)], type: BOOL, loc }, type: BOOL, loc };
|
|
7575
8456
|
}
|
|
7576
8457
|
}
|
|
7577
8458
|
}
|
|
@@ -7597,7 +8478,16 @@ function lowerInExpression(lowerer, expr, loc) {
|
|
|
7597
8478
|
};
|
|
7598
8479
|
return { kind: "unionIsTag", unionId: envType.unionId, tag: undefTag, negated: true, value: read, type: BOOL, loc };
|
|
7599
8480
|
}
|
|
7600
|
-
|
|
8481
|
+
let recv = lowerer.lowerExpr(expr.right);
|
|
8482
|
+
if (isJsSourceFile(expr.getSourceFile()) && recv.type.kind === "func" && lowerer.dynConvertible(recv.type)) {
|
|
8483
|
+
recv = lowerer.coerceToExpected(recv, DYN);
|
|
8484
|
+
}
|
|
8485
|
+
const siteType = lowerer.mapTypeOf(lowerer.typeOf(expr.right));
|
|
8486
|
+
if (recv.type.kind === "union" && siteType?.kind === "union" && !typeEquals(recv.type, siteType)) {
|
|
8487
|
+
const helper = lowerer.narrowedRetagHelper(expr.right, recv.type.unionId, siteType.unionId, loc);
|
|
8488
|
+
if (helper)
|
|
8489
|
+
recv = { kind: "call", callee: helper, args: [recv], type: siteType, loc };
|
|
8490
|
+
}
|
|
7601
8491
|
// Error-rooted receivers (builtin or user subclass — the isErrnoException
|
|
7602
8492
|
// predicate's `err instanceof Error && "code" in err` shape): `code`
|
|
7603
8493
|
// answers from the runtime error's code slot (stamped by fs/system/
|
|
@@ -7622,6 +8512,9 @@ function lowerInExpression(lowerer, expr, loc) {
|
|
|
7622
8512
|
while (info && info.base)
|
|
7623
8513
|
info = info.base;
|
|
7624
8514
|
if (info?.def.name === "%Error") {
|
|
8515
|
+
if (key === "cause") {
|
|
8516
|
+
return { kind: "libCall", fn: "error.hasCause", args: [recv], type: BOOL, loc };
|
|
8517
|
+
}
|
|
7625
8518
|
if (key === "code") {
|
|
7626
8519
|
const codeType = lowerer.envValueType();
|
|
7627
8520
|
if (codeType.kind !== "union")
|
|
@@ -8266,12 +9159,24 @@ export function lowerGroupsProjection(lowerer, recv, groups, loc) {
|
|
|
8266
9159
|
}
|
|
8267
9160
|
return { kind: "call", callee: helper, args: [recv], type: recordT, loc };
|
|
8268
9161
|
}
|
|
8269
|
-
/** Field
|
|
8270
|
-
|
|
8271
|
-
* on classes are rejected specifically; func-typed record fields are
|
|
8272
|
-
* ordinary closure values, so bare references to them work (unlike class
|
|
8273
|
-
* methods, which have no bound-value form). */
|
|
9162
|
+
/** Field reads use the shared FieldTarget union. Native method reads select
|
|
9163
|
+
* an interned unbound callable; invocation supplies its receiver separately. */
|
|
8274
9164
|
export function lowerFieldRead(lowerer, expr) {
|
|
9165
|
+
// A checked receiver already owns its property table. An ambient
|
|
9166
|
+
// generic method signature must not divert its value into native
|
|
9167
|
+
// object-literal specialization (Array.prototype.map is one example).
|
|
9168
|
+
if (ts.isIdentifier(expr.expression)) {
|
|
9169
|
+
const stored = lowerer.peekLocal(expr.expression) ?? lowerer.globalOf(expr.expression);
|
|
9170
|
+
if (stored?.type.kind === "dyn") {
|
|
9171
|
+
const value = lowerer.lowerExpr(expr.expression);
|
|
9172
|
+
if (value.type.kind === "dyn")
|
|
9173
|
+
return {
|
|
9174
|
+
kind: "dynKeyGet", value,
|
|
9175
|
+
key: { kind: "strLit", value: expr.name.text, type: STRING, loc: locOf(expr.name) },
|
|
9176
|
+
...(expr.questionDotToken || hasOptionalChainGuard(expr.expression) ? { optional: true } : {}), type: DYN, loc: locOf(expr),
|
|
9177
|
+
};
|
|
9178
|
+
}
|
|
9179
|
+
}
|
|
8275
9180
|
const target = lowerer.fieldTarget(expr);
|
|
8276
9181
|
if (target)
|
|
8277
9182
|
return lowerer.fieldGetExpr(target, locOf(expr), expr);
|
|
@@ -8288,7 +9193,9 @@ export function lowerFieldRead(lowerer, expr) {
|
|
|
8288
9193
|
if (receiverIr?.kind === "object" &&
|
|
8289
9194
|
(lowerer.findMethodOn(lowerer.classes.get(receiverIr.className) ?? null, expr.name.text) ||
|
|
8290
9195
|
findGenericMethodOn(lowerer, lowerer.classes.get(receiverIr.className) ?? null, expr.name.text))) {
|
|
8291
|
-
|
|
9196
|
+
const info = lowerer.classes.get(receiverIr.className);
|
|
9197
|
+
if (info)
|
|
9198
|
+
return lowerClassMethodValue(lowerer, expr, info);
|
|
8292
9199
|
}
|
|
8293
9200
|
// An object-literal GENERIC method as a VALUE (`o.m` — the member is
|
|
8294
9201
|
// excluded from the record shape): the pinned-value rule verbatim when
|
|
@@ -8348,16 +9255,22 @@ export function lowerFieldRead(lowerer, expr) {
|
|
|
8348
9255
|
* and `switch (r.kind)` work without dedicated test nodes. Anything else
|
|
8349
9256
|
* on a union receiver is rejected specifically (narrow first). */
|
|
8350
9257
|
export function lowerUnionProperty(lowerer, expr) {
|
|
8351
|
-
if (expr
|
|
9258
|
+
if (lowerer.chainBlocked(expr))
|
|
8352
9259
|
return null;
|
|
8353
9260
|
const receiverIr = lowerer.mapTypeOf(lowerer.typeOf(expr.expression));
|
|
8354
9261
|
if (receiverIr?.kind !== "union")
|
|
8355
9262
|
return null;
|
|
8356
|
-
// Lower the receiver
|
|
8357
|
-
//
|
|
8358
|
-
|
|
8359
|
-
//
|
|
8360
|
-
|
|
9263
|
+
// Lower the receiver first: its runtime value can still carry the full
|
|
9264
|
+
// binding union even when the checker has removed several variants.
|
|
9265
|
+
let value = lowerer.lowerExpr(expr.expression);
|
|
9266
|
+
// A guard can remove several variants without selecting one record.
|
|
9267
|
+
// Materialize that sub-union before looking for common fields; arms
|
|
9268
|
+
// ruled out by the guard must not veto a field shared by the survivors.
|
|
9269
|
+
if (value.type.kind === "union" && !typeEquals(value.type, receiverIr)) {
|
|
9270
|
+
const helper = lowerer.narrowedRetagHelper(expr.expression, value.type.unionId, receiverIr.unionId, locOf(expr));
|
|
9271
|
+
if (helper)
|
|
9272
|
+
value = { kind: "call", callee: helper, args: [value], type: receiverIr, loc: locOf(expr) };
|
|
9273
|
+
}
|
|
8361
9274
|
// A checker-union receiver whose VALUE lowered to a plain RECORD (the
|
|
8362
9275
|
// merged-signature fiction — `runner(cmd, args)` where runner joined
|
|
8363
9276
|
// a structural runner type with spawnSync's, and the local adopted
|
|
@@ -8453,20 +9366,29 @@ export function lowerUnionProperty(lowerer, expr) {
|
|
|
8453
9366
|
const keyed = lowerUnionKeyedRead(lowerer, expr, value.type.unionId, value, key, field);
|
|
8454
9367
|
if (keyed)
|
|
8455
9368
|
return keyed;
|
|
9369
|
+
// JavaScript overloads commonly inspect a class brand on a scalar-or-
|
|
9370
|
+
// instance argument (e.g. Matrix4.makeTranslation). Preserve primitive
|
|
9371
|
+
// missing-property semantics and dispatch class data through its native
|
|
9372
|
+
// capsule instead of demanding a field shared by every union arm.
|
|
9373
|
+
if (isJsSourceFile(expr.getSourceFile()) && def.arms.some(isDynTypedRefType) && lowerer.dynConvertible(value.type)) {
|
|
9374
|
+
return { kind: "dynKeyGet", value: lowerer.coerceToExpected(value, DYN), key,
|
|
9375
|
+
...(hasOptionalChainGuard(expr.expression) ? { optional: true } : {}), type: DYN, loc: locOf(expr) };
|
|
9376
|
+
}
|
|
8456
9377
|
lowerer.unsupported("SC1090", expr, `reading '${field}' on a union-typed value (every arm must be an object/record ` +
|
|
8457
9378
|
`with a same-typed field '${field}'; ` +
|
|
8458
9379
|
`${NARROW_FIRST})`);
|
|
8459
9380
|
}
|
|
8460
9381
|
/** The unionDisc generalization: a keyed read on a union receiver whose
|
|
8461
9382
|
* arms answer DIFFERENT (but joinable) types. Each arm contributes its
|
|
8462
|
-
* declared answer — a declared field's type (literal keys), an
|
|
9383
|
+
* declared answer — an array element's type (number keys), a declared field's type (literal keys), an
|
|
8463
9384
|
* index-signature arm's value type (plus its declared fields' types for
|
|
8464
9385
|
* runtime keys, which reach them through the keyed-read helper), and
|
|
8465
9386
|
* UNDEFINED for unit arms (reachable only through optional-chain tails,
|
|
8466
9387
|
* where JS short-circuits to undefined; a unit arm the checker narrowed
|
|
8467
9388
|
* away is simply unreachable). The result type is the JOIN of those
|
|
8468
|
-
* answers; every arm's answer must be the join itself
|
|
8469
|
-
* arms
|
|
9389
|
+
* answers; every arm's answer must be the join itself, one of its
|
|
9390
|
+
* arms, or a sub-union whose payload representations are unchanged.
|
|
9391
|
+
* Array reads retain the ordinary invalid-index trap policy.
|
|
8470
9392
|
* Returns null when any arm cannot answer — the caller owns the fence
|
|
8471
9393
|
* message. The caller (the property/element dispatch) maybeNarrows. */
|
|
8472
9394
|
function lowerUnionKeyedRead(lowerer, expr, unionId, value, key, literalField) {
|
|
@@ -8502,6 +9424,11 @@ function lowerUnionKeyedRead(lowerer, expr, unionId, value, key, literalField) {
|
|
|
8502
9424
|
push(UNDEFINED_T);
|
|
8503
9425
|
continue;
|
|
8504
9426
|
}
|
|
9427
|
+
if (key.type.kind === "f64") {
|
|
9428
|
+
if (arm.kind !== "array" || !pushAnswer(arm.elem))
|
|
9429
|
+
return null;
|
|
9430
|
+
continue;
|
|
9431
|
+
}
|
|
8505
9432
|
if (arm.kind !== "record")
|
|
8506
9433
|
return null;
|
|
8507
9434
|
const shape = lowerer.shapes.get(arm.shapeId);
|
|
@@ -8531,13 +9458,11 @@ function lowerUnionKeyedRead(lowerer, expr, unionId, value, key, literalField) {
|
|
|
8531
9458
|
if (joinArms.length === 0)
|
|
8532
9459
|
return null;
|
|
8533
9460
|
// The join must be a BUILDABLE union: arm kinds the union invariants
|
|
8534
|
-
// admit (no
|
|
8535
|
-
//
|
|
8536
|
-
// join mixing, say, a func answer with data answers has no union to
|
|
8537
|
-
// surface as; the caller owns the fence message.
|
|
9461
|
+
// admit (no dyn/jsval/generator arms; containers only beside unit
|
|
9462
|
+
// siblings). The caller owns the diagnostic for a refused join.
|
|
8538
9463
|
if (joinArms.length > 1 &&
|
|
8539
|
-
(!
|
|
8540
|
-
joinArms.some((a) => a.kind === "
|
|
9464
|
+
(!unionContainerArmsOk(joinArms) ||
|
|
9465
|
+
joinArms.some((a) => a.kind === "dyn" || a.kind === "jsval" || a.kind === "generator" || a.kind === "caught"))) {
|
|
8541
9466
|
return null;
|
|
8542
9467
|
}
|
|
8543
9468
|
joinArms.sort((a, b) => (typeKey(a) < typeKey(b) ? -1 : 1));
|
|
@@ -8545,13 +9470,19 @@ function lowerUnionKeyedRead(lowerer, expr, unionId, value, key, literalField) {
|
|
|
8545
9470
|
// Pass 2: every arm's answer must SURFACE as the join, and index arms
|
|
8546
9471
|
// must pass the single-record keyed-read constraints (the helper is
|
|
8547
9472
|
// shared with recordKeyGet, its missing-key policy included).
|
|
8548
|
-
const surfaces = (t) => typeEquals(t, type) || (type.kind === "union" && lowerer.armTag(type.unionId, t) >= 0)
|
|
9473
|
+
const surfaces = (t) => typeEquals(t, type) || (type.kind === "union" && lowerer.armTag(type.unionId, t) >= 0) ||
|
|
9474
|
+
exactUnionWidening(lowerer, t, type);
|
|
8549
9475
|
for (const arm of def.arms) {
|
|
8550
9476
|
if (isUnitType(arm)) {
|
|
8551
9477
|
if (!(type.kind === "union" && lowerer.armTag(type.unionId, UNDEFINED_T) >= 0))
|
|
8552
9478
|
return null;
|
|
8553
9479
|
continue;
|
|
8554
9480
|
}
|
|
9481
|
+
if (key.type.kind === "f64") {
|
|
9482
|
+
if (arm.kind !== "array" || !surfaces(arm.elem))
|
|
9483
|
+
return null;
|
|
9484
|
+
continue;
|
|
9485
|
+
}
|
|
8555
9486
|
if (arm.kind !== "record")
|
|
8556
9487
|
return null;
|
|
8557
9488
|
const shape = lowerer.shapes.get(arm.shapeId);
|
|
@@ -8569,6 +9500,75 @@ function lowerUnionKeyedRead(lowerer, expr, unionId, value, key, literalField) {
|
|
|
8569
9500
|
}
|
|
8570
9501
|
return { kind: "unionKeyGet", unionId, key, value, type, loc: locOf(expr) };
|
|
8571
9502
|
}
|
|
9503
|
+
/** A structural TypeScript view does not change a nominal object's layout
|
|
9504
|
+
* or accessor dispatch. Resolve the property from the represented class;
|
|
9505
|
+
* the optional checker view must not turn its getter into a record read. */
|
|
9506
|
+
function classFieldTarget(lowerer, receiverNode, receiverType, fieldName, saved) {
|
|
9507
|
+
const info = lowerer.classes.get(receiverType.className);
|
|
9508
|
+
if (!info) {
|
|
9509
|
+
// A receiver typed as a class whose collection deferred: the
|
|
9510
|
+
// deferred diagnostics are what explains the miss.
|
|
9511
|
+
lowerer.flushDeferredClass(receiverType.className);
|
|
9512
|
+
return null;
|
|
9513
|
+
}
|
|
9514
|
+
const fieldType = info.fields.get(fieldName);
|
|
9515
|
+
const lowerObjectReceiver = () => {
|
|
9516
|
+
let obj = saved ?? lowerer.lowerExpr(receiverNode);
|
|
9517
|
+
if (obj.type.kind === "union") {
|
|
9518
|
+
const helper = lowerer.narrowedArmHelper(obj.type.unionId, receiverType, locOf(receiverNode));
|
|
9519
|
+
obj = helper
|
|
9520
|
+
? { kind: "call", callee: helper, args: [obj], type: receiverType, loc: locOf(receiverNode) }
|
|
9521
|
+
: lowerer.maybeNarrow(obj, receiverNode);
|
|
9522
|
+
}
|
|
9523
|
+
return obj.type.kind === "dyn" ? lowerer.coerceInto(receiverNode, obj, receiverType) : obj;
|
|
9524
|
+
};
|
|
9525
|
+
if (fieldName === "cause" && receiverType.className !== "%DOMException") {
|
|
9526
|
+
let root = info;
|
|
9527
|
+
while (root.base)
|
|
9528
|
+
root = root.base;
|
|
9529
|
+
if (root.def.name === "%Error") {
|
|
9530
|
+
return { container: "errorCause", obj: lowerObjectReceiver(), field: "cause", fieldType: DYN };
|
|
9531
|
+
}
|
|
9532
|
+
}
|
|
9533
|
+
if (fieldType) {
|
|
9534
|
+
const obj = lowerObjectReceiver();
|
|
9535
|
+
return { container: "class", obj, className: receiverType.className, field: fieldName, fieldType };
|
|
9536
|
+
}
|
|
9537
|
+
// Accessor property: either half declared anywhere on the chain
|
|
9538
|
+
// makes the name an accessor target (fields and accessors share a
|
|
9539
|
+
// namespace — tsc rejects mixing them, so the halves agree on kind).
|
|
9540
|
+
const getF = lowerer.findMethodOn(info, `get:${fieldName}`);
|
|
9541
|
+
const setF = lowerer.findMethodOn(info, `set:${fieldName}`);
|
|
9542
|
+
if (getF || setF) {
|
|
9543
|
+
const obj = lowerObjectReceiver();
|
|
9544
|
+
return {
|
|
9545
|
+
container: "accessor",
|
|
9546
|
+
obj,
|
|
9547
|
+
className: receiverType.className,
|
|
9548
|
+
field: fieldName,
|
|
9549
|
+
fieldType: getF ? getF.sig.ret : setF.sig.params[0].type,
|
|
9550
|
+
};
|
|
9551
|
+
}
|
|
9552
|
+
return null;
|
|
9553
|
+
}
|
|
9554
|
+
/** A structural assertion can leave the value in its original class
|
|
9555
|
+
* representation, including an unchecked array read's undefined arm. Only
|
|
9556
|
+
* a single nominal payload has an unambiguous property layout. Validate
|
|
9557
|
+
* that payload before dispatching a field or accessor; an absent receiver
|
|
9558
|
+
* must never reach the backend as an object pointer. */
|
|
9559
|
+
function hasClassPayload(lowerer, type) {
|
|
9560
|
+
return type.kind === "object" || type.kind === "union" &&
|
|
9561
|
+
(lowerer.unions.get(type.unionId)?.arms.some((arm) => arm.kind === "object") ?? false);
|
|
9562
|
+
}
|
|
9563
|
+
function representedClassFieldTarget(lowerer, receiverNode, fieldName, value) {
|
|
9564
|
+
const arms = value.type.kind === "union"
|
|
9565
|
+
? lowerer.unions.get(value.type.unionId)?.arms.filter((arm) => !isUnitType(arm))
|
|
9566
|
+
: [value.type];
|
|
9567
|
+
const objectType = arms?.length === 1 ? arms[0] : null;
|
|
9568
|
+
return objectType?.kind === "object"
|
|
9569
|
+
? classFieldTarget(lowerer, receiverNode, objectType, fieldName, value)
|
|
9570
|
+
: null;
|
|
9571
|
+
}
|
|
8572
9572
|
/** Recognizes `obj.field` as an assignable field target: receiver is a
|
|
8573
9573
|
* known class instance OR a record, and the member is a field or (class
|
|
8574
9574
|
* receivers) a declared accessor property. Returns the pieces of a
|
|
@@ -8576,55 +9576,26 @@ function lowerUnionKeyedRead(lowerer, expr, unionId, value, key, literalField) {
|
|
|
8576
9576
|
export function fieldTarget(lowerer, access) {
|
|
8577
9577
|
if (lowerer.chainBlocked(access))
|
|
8578
9578
|
return null;
|
|
8579
|
-
|
|
9579
|
+
if (access.name.text === "stackTraceLimit" && lowerer.isStdlibGlobal(access.expression, "Error")) {
|
|
9580
|
+
return { container: "errorStackLimit", obj: { kind: "numLit", value: 0, type: F64, loc: locOf(access) }, field: "stackTraceLimit", fieldType: F64 };
|
|
9581
|
+
}
|
|
9582
|
+
const stored = ts.isIdentifier(access.expression)
|
|
9583
|
+
? lowerer.peekLocal(access.expression)?.type ?? lowerer.globalOf(access.expression)?.type : undefined;
|
|
9584
|
+
const receiverIr = stored?.kind === "record" ? stored : lowerer.mapTypeOf(lowerer.typeOf(access.expression));
|
|
8580
9585
|
if (receiverIr?.kind === "object") {
|
|
8581
|
-
|
|
8582
|
-
|
|
8583
|
-
|
|
8584
|
-
|
|
8585
|
-
lowerer.flushDeferredClass(receiverIr.className);
|
|
8586
|
-
return null;
|
|
8587
|
-
}
|
|
8588
|
-
const fieldType = info.fields.get(access.name.text);
|
|
8589
|
-
const lowerObjectReceiver = () => {
|
|
8590
|
-
let obj = lowerer.lowerExpr(access.expression);
|
|
8591
|
-
if (obj.type.kind === "union" && lowerer.armTag(obj.type.unionId, UNDEFINED_T) >= 0) {
|
|
8592
|
-
const present = lowerer.stripUndefinedArm(obj.type);
|
|
8593
|
-
const helper = present.kind === "object"
|
|
8594
|
-
? lowerer.narrowedArmHelper(obj.type.unionId, present, locOf(access.expression))
|
|
8595
|
-
: null;
|
|
8596
|
-
obj = helper
|
|
8597
|
-
? { kind: "call", callee: helper, args: [obj], type: present, loc: locOf(access.expression) }
|
|
8598
|
-
: lowerer.maybeNarrow(obj, access.expression);
|
|
8599
|
-
}
|
|
8600
|
-
return obj;
|
|
8601
|
-
};
|
|
8602
|
-
if (fieldType) {
|
|
8603
|
-
const obj = lowerObjectReceiver();
|
|
8604
|
-
return { container: "class", obj, className: receiverIr.className, field: access.name.text, fieldType };
|
|
8605
|
-
}
|
|
8606
|
-
// Accessor property: either half declared anywhere on the chain
|
|
8607
|
-
// makes the name an accessor target (fields and accessors share a
|
|
8608
|
-
// namespace — tsc rejects mixing them, so the halves agree on kind).
|
|
8609
|
-
const getF = lowerer.findMethodOn(info, `get:${access.name.text}`);
|
|
8610
|
-
const setF = lowerer.findMethodOn(info, `set:${access.name.text}`);
|
|
8611
|
-
if (getF || setF) {
|
|
8612
|
-
const obj = lowerObjectReceiver();
|
|
8613
|
-
return {
|
|
8614
|
-
container: "accessor",
|
|
8615
|
-
obj,
|
|
8616
|
-
className: receiverIr.className,
|
|
8617
|
-
field: access.name.text,
|
|
8618
|
-
fieldType: getF ? getF.sig.ret : setF.sig.params[0].type,
|
|
8619
|
-
};
|
|
8620
|
-
}
|
|
8621
|
-
return null;
|
|
9586
|
+
return classFieldTarget(lowerer, access.expression, receiverIr, access.name.text);
|
|
9587
|
+
}
|
|
9588
|
+
if (receiverIr?.kind === "union" && hasClassPayload(lowerer, receiverIr)) {
|
|
9589
|
+
return representedClassFieldTarget(lowerer, access.expression, access.name.text, lowerer.lowerExpr(access.expression));
|
|
8622
9590
|
}
|
|
8623
9591
|
if (receiverIr?.kind === "record") {
|
|
8624
9592
|
const shape = lowerer.shapes.get(receiverIr.shapeId);
|
|
8625
9593
|
const fieldType = shape?.fields.find((f) => f.name === access.name.text)?.type;
|
|
8626
9594
|
if (fieldType) {
|
|
8627
9595
|
let obj = lowerer.lowerExpr(access.expression);
|
|
9596
|
+
const nominal = representedClassFieldTarget(lowerer, access.expression, access.name.text, obj);
|
|
9597
|
+
if (nominal)
|
|
9598
|
+
return nominal;
|
|
8628
9599
|
// A checker-record receiver whose VALUE stayed dyn (the erased
|
|
8629
9600
|
// all-unknown-fields cast — `(err as { code?: unknown }).code`):
|
|
8630
9601
|
// decline, and the dyn keyed-read fallback answers.
|
|
@@ -8758,7 +9729,7 @@ export function uniqueSymbolKeyOf(lowerer, key) {
|
|
|
8758
9729
|
// Module level only: a unique-symbol const inside a FUNCTION is a
|
|
8759
9730
|
// fresh runtime identity per call — one static slot would conflate
|
|
8760
9731
|
// what JS keeps distinct.
|
|
8761
|
-
if (!ts.isVariableStatement(decl.parent
|
|
9732
|
+
if (!ts.isVariableStatement(decl.parent?.parent) || !ts.isSourceFile(decl.parent?.parent?.parent))
|
|
8762
9733
|
return null;
|
|
8763
9734
|
const init = decl.initializer;
|
|
8764
9735
|
if (!init || !ts.isCallExpression(init) || init.questionDotToken)
|
|
@@ -8791,10 +9762,10 @@ export function symbolFieldInfo(lowerer, expr) {
|
|
|
8791
9762
|
lowerer.flushDeferredClass(receiverIr.className);
|
|
8792
9763
|
return null;
|
|
8793
9764
|
}
|
|
8794
|
-
const key =
|
|
9765
|
+
const key = classSymbolKeyOf(lowerer, expr.argumentExpression);
|
|
8795
9766
|
if (!key)
|
|
8796
9767
|
return null;
|
|
8797
|
-
const field = info.symbolFields?.get(key.
|
|
9768
|
+
const field = info.symbolFields?.get(key.identity);
|
|
8798
9769
|
if (field === undefined)
|
|
8799
9770
|
return null;
|
|
8800
9771
|
const fieldType = info.fields.get(field);
|
|
@@ -8816,6 +9787,12 @@ function symbolFieldTarget(lowerer, expr) {
|
|
|
8816
9787
|
* tsc-clean (the property types as the setter's param), but Node yields
|
|
8817
9788
|
* undefined, which these property types cannot represent. */
|
|
8818
9789
|
export function fieldGetExpr(lowerer, target, loc, blame) {
|
|
9790
|
+
if (target.container === "errorStackLimit") {
|
|
9791
|
+
return { kind: "libCall", fn: "error.stackLimitGet", args: [], type: F64, loc };
|
|
9792
|
+
}
|
|
9793
|
+
if (target.container === "errorCause") {
|
|
9794
|
+
return { kind: "libCall", fn: "error.cause", args: [target.obj], type: DYN, loc };
|
|
9795
|
+
}
|
|
8819
9796
|
// A record-shaped CHECKER target whose receiver VALUE lives in the checked-dynamic tree
|
|
8820
9797
|
// (a JS file-scope object-literal global): the checked-dynamic keyed
|
|
8821
9798
|
// read — dynKeyGet (a missing key answers the dyn undefined, exactly
|
|
@@ -8912,6 +9889,12 @@ export function fieldGetExpr(lowerer, target, loc, blame) {
|
|
|
8912
9889
|
* call). A write to a getter-only property never gets here in a clean
|
|
8913
9890
|
* program (tsc's TS2540 is the fence); the rejection is the backstop. */
|
|
8914
9891
|
export function fieldSetStmt(lowerer, target, value, loc, blame) {
|
|
9892
|
+
if (target.container === "errorStackLimit") {
|
|
9893
|
+
return { kind: "exprStmt", expr: { kind: "libCall", fn: "error.stackLimitSet", args: [value], type: VOID, loc }, loc };
|
|
9894
|
+
}
|
|
9895
|
+
if (target.container === "errorCause") {
|
|
9896
|
+
return { kind: "exprStmt", expr: { kind: "libCall", fn: "error.setCause", args: [target.obj, value], type: VOID, loc }, loc };
|
|
9897
|
+
}
|
|
8915
9898
|
// A record-shaped CHECKER target whose receiver VALUE lives in the checked-dynamic tree:
|
|
8916
9899
|
// the checked-dynamic keyed write — dyn.keySet (later writes win,
|
|
8917
9900
|
// insertion order; Node's TypeErrors on non-object receivers), the
|
|
@@ -9080,61 +10063,73 @@ export function lowerPromiseAllTupleCall(lowerer, call, access) {
|
|
|
9080
10063
|
}
|
|
9081
10064
|
/** `obj.f op= e` (and `obj.f++` with rhs null ≡ 1) — the element spelling
|
|
9082
10065
|
* `obj[k] op= e` included when k is a declared symbol-keyed field.
|
|
9083
|
-
*
|
|
9084
|
-
*
|
|
10066
|
+
* Save the receiver and old value before the RHS, then write through
|
|
10067
|
+
* that same receiver even if the RHS replaces its original binding. */
|
|
9085
10068
|
export function lowerFieldCompound(lowerer, access, op, rhsNode, loc) {
|
|
10069
|
+
return { kind: "exprStmt", expr: lowerFieldCompoundValue(lowerer, access, op, rhsNode, loc), loc };
|
|
10070
|
+
}
|
|
10071
|
+
function lowerFieldCompoundValue(lowerer, access, op, rhsNode, loc) {
|
|
9086
10072
|
if (access.expression.kind === ts.SyntaxKind.SuperKeyword) {
|
|
9087
10073
|
lowerer.unsupported("SC1090", access, "compound assignment through 'super' (read and write separately)");
|
|
9088
10074
|
}
|
|
9089
|
-
|
|
9090
|
-
|
|
9091
|
-
|
|
10075
|
+
const body = [];
|
|
10076
|
+
const save = (value, name) => {
|
|
10077
|
+
const local = lowerer.declareHiddenLocal(name, value.type);
|
|
10078
|
+
body.push({ kind: "varDecl", localId: local.id, init: value, loc: value.loc });
|
|
10079
|
+
return varRef(local.id, value.type, value.loc);
|
|
10080
|
+
};
|
|
9092
10081
|
// A CHECKED-DYNAMIC receiver (`context.actual++` — test/common's call
|
|
9093
10082
|
// accounting; dot spelling only — symbol-keyed element targets are
|
|
9094
10083
|
// static fields): read the member (dynKeyGet), VALIDATE it as a
|
|
9095
10084
|
// number (dynCheck — a non-number member throws the catchable
|
|
9096
10085
|
// TypeError where JS would ToNumber-coerce; loud, never a silent
|
|
9097
10086
|
// NaN — SEMANTICS.md), combine, write back (dyn.keySet). The
|
|
9098
|
-
// receiver
|
|
9099
|
-
// read and write matches JS's once-evaluation observably.
|
|
10087
|
+
// receiver and member read are saved before evaluating the RHS.
|
|
9100
10088
|
if (ts.isPropertyAccessExpression(access)) {
|
|
9101
10089
|
const probed = tryLowerExpression(lowerer, access.expression);
|
|
9102
10090
|
if (probed?.type.kind === "dyn") {
|
|
10091
|
+
const receiver = save(probed, "%compoundReceiver");
|
|
9103
10092
|
const key = { kind: "strLit", value: access.name.text, type: STRING, loc: locOf(access.name) };
|
|
9104
|
-
const read = { kind: "dynKeyGet", key, value:
|
|
10093
|
+
const read = save({ kind: "dynKeyGet", key, value: receiver, type: DYN, loc }, "%compoundOld");
|
|
9105
10094
|
const cur = { kind: "dynCheck", value: read, type: F64, loc };
|
|
9106
|
-
const rhs = rhsNode
|
|
10095
|
+
const rhs = save(rhsNode
|
|
9107
10096
|
? lowerer.lowerExpr(rhsNode)
|
|
9108
|
-
: { kind: "numLit", value: 1, type: F64, loc };
|
|
9109
|
-
|
|
10097
|
+
: { kind: "numLit", value: 1, type: F64, loc }, "%compoundRhs");
|
|
10098
|
+
const numericRhs = rhs.type.kind === "dyn"
|
|
10099
|
+
? { kind: "dynCheck", value: rhs, type: F64, loc: rhs.loc }
|
|
10100
|
+
: lowerOptionalNumber(lowerer, rhs, loc);
|
|
10101
|
+
if (numericRhs.type.kind !== "f64")
|
|
9110
10102
|
lowerer.unsupported("SC1043", access);
|
|
9111
|
-
const value = { kind: "bin", op, left: cur, right:
|
|
9112
|
-
const recv2 = lowerer.lowerExpr(access.expression);
|
|
10103
|
+
const value = save({ kind: "bin", op, left: cur, right: numericRhs, type: F64, loc }, "%compoundResult");
|
|
9113
10104
|
const boxed = { kind: "dynFrom", value, type: DYN, loc };
|
|
9114
|
-
|
|
10105
|
+
body.push({
|
|
9115
10106
|
kind: "exprStmt",
|
|
9116
|
-
expr: { kind: "libCall", fn: "dyn.keySet", args: [
|
|
10107
|
+
expr: { kind: "libCall", fn: "dyn.keySet", args: [receiver, { ...key }, boxed], type: VOID, loc },
|
|
9117
10108
|
loc,
|
|
9118
|
-
};
|
|
10109
|
+
});
|
|
10110
|
+
return { kind: "seqExpr", stmts: body, result: value, type: value.type, loc };
|
|
9119
10111
|
}
|
|
9120
10112
|
}
|
|
9121
10113
|
const targetOf = () => ts.isPropertyAccessExpression(access) ? lowerer.fieldTarget(access) : symbolFieldTarget(lowerer, access);
|
|
9122
10114
|
const target = targetOf();
|
|
9123
10115
|
if (!target)
|
|
9124
10116
|
lowerer.unsupported("SC1090", access, "compound assignment to unsupported field targets");
|
|
9125
|
-
|
|
9126
|
-
//
|
|
9127
|
-
//
|
|
9128
|
-
const read = lowerer.fieldGetExpr(target, locOf(access), access);
|
|
9129
|
-
const rhs = rhsNode
|
|
10117
|
+
target.obj = save(target.obj, "%compoundReceiver");
|
|
10118
|
+
// Accessors observe getter, RHS side effects, then setter, all through
|
|
10119
|
+
// the saved receiver.
|
|
10120
|
+
const read = save(lowerer.fieldGetExpr(target, locOf(access), access), "%compoundOld");
|
|
10121
|
+
const rhs = save(rhsNode
|
|
9130
10122
|
? lowerer.lowerExpr(rhsNode)
|
|
9131
|
-
: { kind: "numLit", value: 1, type: F64, loc };
|
|
10123
|
+
: { kind: "numLit", value: 1, type: F64, loc }, "%compoundRhs");
|
|
10124
|
+
const numericRhs = rhs.type.kind === "dyn" && isJsSourceFile(access.getSourceFile())
|
|
10125
|
+
? { kind: "dynCheck", value: rhs, type: F64, loc: rhs.loc }
|
|
10126
|
+
: lowerOptionalNumber(lowerer, rhs, loc);
|
|
9132
10127
|
let value;
|
|
9133
10128
|
if (op === "+" && target.fieldType.kind === "string") {
|
|
9134
10129
|
value = { kind: "strConcat", left: read, right: lowerer.ensureString(rhs, rhsNode ?? access), type: STRING, loc };
|
|
9135
10130
|
}
|
|
9136
|
-
else if (target.fieldType.kind === "f64" &&
|
|
9137
|
-
value = { kind: "bin", op, left: read, right:
|
|
10131
|
+
else if (target.fieldType.kind === "f64" && numericRhs.type.kind === "f64") {
|
|
10132
|
+
value = { kind: "bin", op, left: read, right: numericRhs, type: F64, loc };
|
|
9138
10133
|
}
|
|
9139
10134
|
else if (target.fieldType.kind === "dyn" &&
|
|
9140
10135
|
isJsSourceFile(access.getSourceFile()) &&
|
|
@@ -9170,9 +10165,9 @@ export function lowerFieldCompound(lowerer, access, op, rhsNode, loc) {
|
|
|
9170
10165
|
else {
|
|
9171
10166
|
lowerer.unsupported("SC1043", access);
|
|
9172
10167
|
}
|
|
9173
|
-
|
|
9174
|
-
|
|
9175
|
-
return
|
|
10168
|
+
const result = save(value, "%compoundResult");
|
|
10169
|
+
body.push(lowerer.fieldSetStmt(target, result, loc, access));
|
|
10170
|
+
return { kind: "seqExpr", stmts: body, result, type: result.type, loc };
|
|
9176
10171
|
}
|
|
9177
10172
|
/** Stream-rooted receivers' property surface (readableEnded, destroyed,
|
|
9178
10173
|
* ...): the receiver's mapped class dispatches into lower-stream.ts.
|