@scriptc/compiler 0.1.7 → 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 -24
- package/ambient/scriptc-overrides.d.ts +10 -6
- package/ambient/scriptc.d.ts +15 -0
- 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/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 +1 -1
- package/dist/backend/link-plan.js +16 -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 +1 -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 +2 -0
- package/dist/backend/llvm/blocks.js +9 -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.js +2 -7
- package/dist/backend/llvm/debug-info.js.map +1 -1
- 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 +494 -165
- package/dist/backend/llvm/dyn.js.map +1 -1
- package/dist/backend/llvm/emitter.d.ts +596 -1
- package/dist/backend/llvm/emitter.js +695 -410
- 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 +62 -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 +30 -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 +117 -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/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 +31 -211
- package/dist/backend/native-toolchain.js.map +1 -1
- package/dist/backend/native-toolchain.test.js +5 -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/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.js +3 -3
- package/dist/diagnostics/diagnostic.js.map +1 -1
- package/dist/executable/executable-cache.d.ts +3 -4
- package/dist/executable/executable-cache.js +5 -6
- package/dist/executable/executable-cache.js.map +1 -1
- package/dist/executable/executable-cache.test.js +16 -17
- 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 +1 -3
- package/dist/frontend/arguments-usage.js.map +1 -1
- 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 +473 -178
- package/dist/frontend/lowering/expressions/object-literals.js.map +1 -1
- package/dist/frontend/lowering/expressions/optional-chains.js +20 -8
- 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 +539 -343
- package/dist/frontend/lowering/lower-builtins.js.map +1 -1
- package/dist/frontend/lowering/lower-calls.d.ts +20 -17
- package/dist/frontend/lowering/lower-calls.js +1236 -353
- 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 +1674 -779
- 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 +233 -30
- 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 +728 -496
- 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 +1031 -672
- 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 +8 -3
- package/dist/frontend/lowering/surfaces.js +61 -29
- 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 +38 -19
- package/dist/frontend/program.js +344 -169
- package/dist/frontend/program.js.map +1 -1
- package/dist/frontend/program.test.js +87 -2
- package/dist/frontend/program.test.js.map +1 -1
- 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 +25 -17
- package/dist/frontend/type-mapper.js +465 -121
- package/dist/frontend/type-mapper.js.map +1 -1
- package/dist/frontend/type-mapper.test.js +123 -2
- package/dist/frontend/type-mapper.test.js.map +1 -1
- 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 -198
- package/dist/index.js +369 -1572
- package/dist/index.js.map +1 -1
- package/dist/ir/analysis.d.ts +16 -4
- package/dist/ir/analysis.js +21 -4
- 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 +291 -152
- package/dist/ir/ir.js +588 -881
- 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 +1942 -1491
- 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/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 +52 -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 -536
- package/dist/backend/c/c-emitter.js +0 -1932
- 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 -8847
- 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 -1108
- 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 -1772
- 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") {
|
|
@@ -1016,11 +1094,11 @@ function lowerExprInner(lowerer, expr) {
|
|
|
1016
1094
|
// The families with a WHY: each hint states what makes the
|
|
1017
1095
|
// surface genuinely non-static (or what to use instead).
|
|
1018
1096
|
const globalHints = {
|
|
1019
|
-
Proxy: "
|
|
1097
|
+
Proxy: "Proxy constructor values have no native lowering; use a direct new Proxy with checked-native plain objects",
|
|
1020
1098
|
Reflect: "reflective property access has no static lowering — read and call members directly",
|
|
1021
|
-
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",
|
|
1022
1100
|
SharedArrayBuffer: "no shared-memory threads exist in a compiled program — Uint8Array is the byte storage",
|
|
1023
|
-
ArrayBuffer: "
|
|
1101
|
+
ArrayBuffer: "ArrayBuffer constructor values have no native lowering; use new ArrayBuffer(length) for fixed-length shared storage",
|
|
1024
1102
|
WeakRef: "deref()-after-collect exposes GC timing — genuinely dynamic; hold a strong reference instead",
|
|
1025
1103
|
FinalizationRegistry: "finalization callbacks expose GC timing — genuinely dynamic; release resources explicitly instead",
|
|
1026
1104
|
eval: "runtime code evaluation cannot be compiled ahead of time",
|
|
@@ -1370,6 +1448,14 @@ function lowerExprInner(lowerer, expr) {
|
|
|
1370
1448
|
if (ts.isConditionalExpression(expr))
|
|
1371
1449
|
return lowerTernary(lowerer, expr);
|
|
1372
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;
|
|
1373
1459
|
// `super.x`: the base chain's GETTER, called directly (super
|
|
1374
1460
|
// dispatch is static in JS — never through the dynamic class).
|
|
1375
1461
|
// super.method() calls are routed at the call site; a bare super
|
|
@@ -1411,6 +1497,25 @@ function lowerExprInner(lowerer, expr) {
|
|
|
1411
1497
|
const moduleProperty = lowerNodeModuleProperty(lowerer, expr);
|
|
1412
1498
|
if (moduleProperty)
|
|
1413
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
|
+
}
|
|
1414
1519
|
if (expr.questionDotToken && expr.name.text === "electron") {
|
|
1415
1520
|
const processProperty = lowerer.lowerProcessProperty(expr);
|
|
1416
1521
|
if (processProperty)
|
|
@@ -1427,6 +1532,12 @@ function lowerExprInner(lowerer, expr) {
|
|
|
1427
1532
|
if (!expr.questionDotToken && isOptionalChainTail(lowerer, expr)) {
|
|
1428
1533
|
return lowerer.lowerOptionalChain(expr);
|
|
1429
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");
|
|
1430
1541
|
// Island receiver: o.x is an engine property read (getProp may throw
|
|
1431
1542
|
// — reading off null/undefined — bridged catchably like everything
|
|
1432
1543
|
// at the boundary). `o?.x` arrives here too, through the chain
|
|
@@ -1566,11 +1677,11 @@ function lowerExprInner(lowerer, expr) {
|
|
|
1566
1677
|
// resolve unconditionally — they run after the module evaluated,
|
|
1567
1678
|
// the same approximation identifier exports already make. Every
|
|
1568
1679
|
// other module.exports read keeps its existing story.
|
|
1569
|
-
if (!expr.questionDotToken && isModuleExportsAccess(expr) && isCjsJsFile(expr.getSourceFile())) {
|
|
1680
|
+
if (!expr.questionDotToken && isModuleExportsAccess(expr) && isCjsJsFile(expr.getSourceFile(), lowerer.program)) {
|
|
1570
1681
|
const whole = cjsClassExprWholeExportOf(expr.getSourceFile());
|
|
1571
1682
|
if (whole) {
|
|
1572
1683
|
let inBody = false;
|
|
1573
|
-
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) {
|
|
1574
1685
|
if (ts.isFunctionLike(p) || ts.isClassDeclaration(p) || ts.isClassExpression(p)) {
|
|
1575
1686
|
inBody = true;
|
|
1576
1687
|
break;
|
|
@@ -1598,7 +1709,7 @@ function lowerExprInner(lowerer, expr) {
|
|
|
1598
1709
|
const writePos = ts.isBinaryExpression(expr.parent) &&
|
|
1599
1710
|
expr.parent.left === expr &&
|
|
1600
1711
|
expr.parent.operatorToken.kind === ts.SyntaxKind.EqualsToken;
|
|
1601
|
-
if (!expr.questionDotToken && cjsMemberRecv && !writePos && isCjsJsFile(sf)) {
|
|
1712
|
+
if (!expr.questionDotToken && cjsMemberRecv && !writePos && isCjsJsFile(sf, lowerer.program)) {
|
|
1602
1713
|
const sym = lowerer.checker.getSymbolAtLocation(expr.name) ??
|
|
1603
1714
|
lowerer.cjsModuleExportSymbol(sf, expr.name.text);
|
|
1604
1715
|
const resolved = sym !== undefined && sym.flags & ts.SymbolFlags.Alias
|
|
@@ -1720,6 +1831,8 @@ function lowerExprInner(lowerer, expr) {
|
|
|
1720
1831
|
isJsSourceFile(expr.getSourceFile())) {
|
|
1721
1832
|
const canonical = stdlibGlobalNameOf(lowerer, expr);
|
|
1722
1833
|
if (canonical !== null) {
|
|
1834
|
+
if (!lowerer.dynamic && canonical === "globalThis")
|
|
1835
|
+
return lowerGlobalValue(lowerer, expr);
|
|
1723
1836
|
return { kind: "strLit", value: `[builtin ${canonical}]`, type: STRING, loc };
|
|
1724
1837
|
}
|
|
1725
1838
|
}
|
|
@@ -1743,6 +1856,7 @@ function lowerExprInner(lowerer, expr) {
|
|
|
1743
1856
|
// instead.
|
|
1744
1857
|
if ((isJsSourceFile(expr.getSourceFile()) &&
|
|
1745
1858
|
(lowerer.mapTypeOf(lowerer.typeOf(expr.expression)) === null ||
|
|
1859
|
+
(lowerer.typeOf(expr.expression).flags & ts.TypeFlags.Never) !== 0 ||
|
|
1746
1860
|
// A never-tainted receiver type maps (never rides as f64) but
|
|
1747
1861
|
// its VALUE lowered checked-dynamic — same dyn read.
|
|
1748
1862
|
neverTaintedJsType(lowerer, expr.expression, lowerer.typeOf(expr.expression)))) ||
|
|
@@ -1756,6 +1870,11 @@ function lowerExprInner(lowerer, expr) {
|
|
|
1756
1870
|
const opt = hasOptionalChainGuard(expr.expression);
|
|
1757
1871
|
return lowerer.maybeNarrow({ kind: "dynKeyGet", key, ...(opt ? { optional: true } : {}), value: recv, type: DYN, loc }, expr);
|
|
1758
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
|
+
}
|
|
1759
1878
|
}
|
|
1760
1879
|
// `f.name` / `f.length` / own properties on a FUNCTION-typed JS
|
|
1761
1880
|
// value (the mustCall wrapper's function-instance members): read
|
|
@@ -1764,11 +1883,24 @@ function lowerExprInner(lowerer, expr) {
|
|
|
1764
1883
|
// best-effort static name and the declared arity; anything else is
|
|
1765
1884
|
// the own-property answer, undefined. Function.prototype METHOD
|
|
1766
1885
|
// names stay fenced (a stored-member undefined would mis-answer
|
|
1767
|
-
// `f.call` as a value).
|
|
1768
|
-
|
|
1769
|
-
|
|
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")) {
|
|
1770
1890
|
const probed = tryLowerExpression(lowerer, expr.expression);
|
|
1771
|
-
|
|
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" &&
|
|
1772
1904
|
canBoxFuncIntoDyn(probed.type, (id) => lowerer.shapes.get(id), (id) => lowerer.unions.get(id))) {
|
|
1773
1905
|
const fnName = jsFuncNameOf(expr.expression);
|
|
1774
1906
|
const boxed = {
|
|
@@ -1823,7 +1955,10 @@ function lowerExprInner(lowerer, expr) {
|
|
|
1823
1955
|
// the generic dyn receiver path below. Provenance is limited to the
|
|
1824
1956
|
// node:util parseArgs family; every other stdlib member keeps its
|
|
1825
1957
|
// ordinary surface fence.
|
|
1826
|
-
|
|
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) {
|
|
1827
1962
|
const recv = lowerer.lowerExpr(expr.expression);
|
|
1828
1963
|
if (recv.type.kind === "dyn") {
|
|
1829
1964
|
const key = {
|
|
@@ -1857,10 +1992,6 @@ function lowerExprInner(lowerer, expr) {
|
|
|
1857
1992
|
// The lib fence's PROPERTY chokepoint: a stdlib-declared member that
|
|
1858
1993
|
// no lowering above claimed ([1,2].entries, Math.SQRT2, Promise.all,
|
|
1859
1994
|
// re.exec as a value, ...) reports SC2020 here.
|
|
1860
|
-
lowerer.fenceStaticAbortControllerMemberRead(expr);
|
|
1861
|
-
lowerer.fenceStaticResponseMember(expr, "read");
|
|
1862
|
-
lowerer.fenceStaticHeadersMember(expr, "read");
|
|
1863
|
-
lowerer.fenceStaticReadableStreamMember(expr, "read");
|
|
1864
1995
|
lowerer.stdlibMemberFence(expr);
|
|
1865
1996
|
// The npm chokepoint: a member on a package-typed receiver in a
|
|
1866
1997
|
// static build — attributed to the package, like every other site.
|
|
@@ -1919,6 +2050,17 @@ function lowerExprInner(lowerer, expr) {
|
|
|
1919
2050
|
loc,
|
|
1920
2051
|
}, expr);
|
|
1921
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
|
+
}
|
|
1922
2064
|
}
|
|
1923
2065
|
// An ABSTRACT property through an abstract-typed receiver: the
|
|
1924
2066
|
// declaration is erased at runtime — Node defines no field for it,
|
|
@@ -1928,6 +2070,19 @@ function lowerExprInner(lowerer, expr) {
|
|
|
1928
2070
|
if (recvLowered.type.kind === "object" && abstractPropertyDeclOf(lowerer, expr)) {
|
|
1929
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)`);
|
|
1930
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
|
+
}
|
|
1931
2086
|
lowerer.unsupported("SC1090", expr, `reading '${expr.name.text}' from a value of type '${lowerer.checker.typeToString(lowerer.typeOf(expr.expression))}'`);
|
|
1932
2087
|
}
|
|
1933
2088
|
// Meta-properties, named: `new.target` reflects HOW a function was
|
|
@@ -1944,7 +2099,7 @@ function lowerExprInner(lowerer, expr) {
|
|
|
1944
2099
|
const entry = UNSUPPORTED_EXPR[expr.kind];
|
|
1945
2100
|
if (entry)
|
|
1946
2101
|
lowerer.unsupported(entry.code, expr, entry.feature);
|
|
1947
|
-
lowerer.unsupported("SC1090", expr, `syntax '${ts.
|
|
2102
|
+
lowerer.unsupported("SC1090", expr, `syntax '${ts.syntaxKindName(expr.kind)}'`);
|
|
1948
2103
|
}
|
|
1949
2104
|
function moduleFileName(lowerer, sf) {
|
|
1950
2105
|
return lowerer.targetPlatform === "wasi"
|
|
@@ -1983,11 +2138,10 @@ function importMetaProperty(lowerer, expr) {
|
|
|
1983
2138
|
return null;
|
|
1984
2139
|
}
|
|
1985
2140
|
}
|
|
1986
|
-
/** `c ? a : b
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
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) {
|
|
1991
2145
|
const loc = locOf(expr);
|
|
1992
2146
|
// `Array.isArray(x) ? x : [x]` over a `T | readonly T[]` union: tsc
|
|
1993
2147
|
// narrows the TRUE branch to `any[]` (maybeNarrow's isArray bridge
|
|
@@ -2041,7 +2195,16 @@ function lowerTernary(lowerer, expr, expected) {
|
|
|
2041
2195
|
// literals) sit untaken in compiled JS. A boolLit carries no
|
|
2042
2196
|
// effects, so dropping the condition read loses nothing.
|
|
2043
2197
|
if (cond.kind === "boolLit") {
|
|
2044
|
-
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 };
|
|
2045
2208
|
}
|
|
2046
2209
|
const ctxTs = lowerer.checker.getContextualType(expr);
|
|
2047
2210
|
const ctxMapped = ctxTs ? lowerer.mapTypeOf(ctxTs) : null;
|
|
@@ -2058,7 +2221,7 @@ function lowerTernary(lowerer, expr, expected) {
|
|
|
2058
2221
|
// lowerArrayLiteral, and just as unambiguous — tsc already typed the
|
|
2059
2222
|
// whole ternary by the filled arm alone. Both arms empty stays
|
|
2060
2223
|
// fenced (no element type exists anywhere).
|
|
2061
|
-
const ctxArray =
|
|
2224
|
+
const ctxArray = ctxMapped?.kind === "array" ? ctxMapped : null;
|
|
2062
2225
|
const armLiteral = (e) => {
|
|
2063
2226
|
let x = e;
|
|
2064
2227
|
while (ts.isParenthesizedExpression(x))
|
|
@@ -2082,7 +2245,21 @@ function lowerTernary(lowerer, expr, expected) {
|
|
|
2082
2245
|
}
|
|
2083
2246
|
return false;
|
|
2084
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;
|
|
2085
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
|
+
}
|
|
2086
2263
|
const lit = armLiteral(e);
|
|
2087
2264
|
if (lit && ctxArray)
|
|
2088
2265
|
return lowerer.lowerArrayLiteral(lit, ctxArray);
|
|
@@ -2135,11 +2312,14 @@ function lowerTernary(lowerer, expr, expected) {
|
|
|
2135
2312
|
const lowerTrueArm = () => withRuntimeOptionalNarrowed(lowerer, runtimeTrueIds, () => lowerArm(expr.whenTrue, ownArrayJoin ?? undefined));
|
|
2136
2313
|
if (!ctxArray && emptyUntypedArrayArm(expr.whenTrue) && !emptyUntypedArrayArm(expr.whenFalse)) {
|
|
2137
2314
|
elseRaw = lowerer.lowerExpr(expr.whenFalse);
|
|
2138
|
-
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));
|
|
2139
2316
|
}
|
|
2140
2317
|
else {
|
|
2141
2318
|
thenRaw = lowerTrueArm();
|
|
2142
|
-
|
|
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);
|
|
2143
2323
|
}
|
|
2144
2324
|
// The ternary's IR type is normally the checker's own: it collapses
|
|
2145
2325
|
// same-kind literal unions ("a" | "b" → string) and forms tagged
|
|
@@ -2211,14 +2391,22 @@ function lowerTernary(lowerer, expr, expected) {
|
|
|
2211
2391
|
}
|
|
2212
2392
|
}
|
|
2213
2393
|
}
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
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)));
|
|
2222
2410
|
// Each arm flows into the ternary's type through the slot-coercion
|
|
2223
2411
|
// path: union arms wrap, dyn slots reject the non-dyn arm with
|
|
2224
2412
|
// SC1101, mismatched record shapes get SC2002; coerceInto is
|
|
@@ -2247,6 +2435,16 @@ function lowerTernary(lowerer, expr, expected) {
|
|
|
2247
2435
|
* unrepresentable without a re-tag), or to nothing (`never` in an
|
|
2248
2436
|
* exhaustive default) leaves the expression union-typed. */
|
|
2249
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
|
+
}
|
|
2250
2448
|
// A dyn read tsc narrowed to a SCALAR (a typeof test proved the kind):
|
|
2251
2449
|
// bridge with a VALIDATED extraction — dynCheck, the checked-cast
|
|
2252
2450
|
// machinery — rather than a trusted one. After the guard the check
|
|
@@ -2254,15 +2452,40 @@ export function maybeNarrow(lowerer, expr, node) {
|
|
|
2254
2452
|
// instead of misreading the payload (the dyn boundary's usual stance).
|
|
2255
2453
|
// Object/array narrowings stay dyn-typed and keep their fences.
|
|
2256
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;
|
|
2257
2481
|
const narrowed = lowerer.mapTypeOf(lowerer.typeOf(node));
|
|
2258
2482
|
if (narrowed &&
|
|
2259
|
-
(narrowed.kind === "f64" || narrowed.kind === "bool" || narrowed.kind === "string")) {
|
|
2483
|
+
(narrowed.kind === "f64" || narrowed.kind === "bool" || narrowed.kind === "string" || narrowed.kind === "bigint")) {
|
|
2260
2484
|
return { kind: "dynCheck", value: expr, type: narrowed, loc: expr.loc };
|
|
2261
2485
|
}
|
|
2262
|
-
// An
|
|
2263
|
-
//
|
|
2264
|
-
|
|
2265
|
-
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") {
|
|
2266
2489
|
return { kind: "dynCheck", value: expr, type: narrowed, loc: expr.loc };
|
|
2267
2490
|
}
|
|
2268
2491
|
// An `instanceof Error` narrow: the checked-dynamic tree's error encoding rebuilds a
|
|
@@ -2289,6 +2512,11 @@ export function maybeNarrow(lowerer, expr, node) {
|
|
|
2289
2512
|
}
|
|
2290
2513
|
if (expr.type.kind !== "union")
|
|
2291
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
|
+
}
|
|
2292
2520
|
const narrowedTs = lowerer.typeOf(node);
|
|
2293
2521
|
const narrowed = lowerer.mapTypeOf(narrowedTs);
|
|
2294
2522
|
// `Array.isArray(u)` on a union with one readonly array/tuple arm: the
|
|
@@ -2315,14 +2543,32 @@ export function maybeNarrow(lowerer, expr, node) {
|
|
|
2315
2543
|
}
|
|
2316
2544
|
const tag = lowerer.armTag(expr.type.unionId, narrowed);
|
|
2317
2545
|
if (tag < 0) {
|
|
2318
|
-
//
|
|
2319
|
-
//
|
|
2320
|
-
//
|
|
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.
|
|
2321
2565
|
const arms = lowerer.unions.get(expr.type.unionId)?.arms ?? [];
|
|
2322
|
-
const valueTag =
|
|
2566
|
+
const valueTag = narrowed.kind === "object"
|
|
2567
|
+
? arms.findIndex((arm) => arm.kind === "object" && lowerer.isSubclassOf(narrowed.className, arm.className))
|
|
2568
|
+
: -1;
|
|
2323
2569
|
const valueType = arms[valueTag];
|
|
2324
2570
|
if (narrowed.kind === "object" && valueType?.kind === "object" &&
|
|
2325
|
-
arms.every((arm, i) => i === valueTag ||
|
|
2571
|
+
arms.every((arm, i) => i === valueTag || arm.kind !== "object" || !lowerer.isSubclassOf(narrowed.className, arm.className)) &&
|
|
2326
2572
|
lowerer.isSubclassOf(narrowed.className, valueType.className)) {
|
|
2327
2573
|
return {
|
|
2328
2574
|
kind: "downcast",
|
|
@@ -2701,7 +2947,26 @@ function lowerLooseNullCompare(lowerer, expr, loc) {
|
|
|
2701
2947
|
* results (several non-unit arms) and defaults that change the result
|
|
2702
2948
|
* type are fenced with narrow-first hints. */
|
|
2703
2949
|
export function lowerNullishCoalesce(lowerer, expr, loc) {
|
|
2704
|
-
|
|
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) {
|
|
2705
2970
|
if (left.type.kind === "dyn") {
|
|
2706
2971
|
// `a ?? b` on a CHECKED-DYNAMIC left: the deciding test is the
|
|
2707
2972
|
// runtime kind (scr_dyn_is_nullish — UNDEF/NULL take the default;
|
|
@@ -2730,8 +2995,14 @@ export function lowerNullishCoalesce(lowerer, expr, loc) {
|
|
|
2730
2995
|
lowerer.badType(expr.left, lowerer.typeOf(expr.left));
|
|
2731
2996
|
if (!def.arms.some(isUnitType))
|
|
2732
2997
|
return left;
|
|
2733
|
-
const type = lowerer.irTypeOf(expr);
|
|
2734
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);
|
|
2735
3006
|
if (typeEquals(type, left.type) || (rest.length === 1 && typeEquals(type, rest[0]))) {
|
|
2736
3007
|
const right = lowerer.lowerExprExpecting(expr.right, type);
|
|
2737
3008
|
return { kind: "nullish", left, right, type, loc };
|
|
@@ -2739,180 +3010,49 @@ export function lowerNullishCoalesce(lowerer, expr, loc) {
|
|
|
2739
3010
|
if (type.kind !== "union" && rest.length > 1 && rest.some((arm) => typeEquals(arm, type))) {
|
|
2740
3011
|
const helper = lowerer.narrowedArmHelper(left.type.unionId, type, loc);
|
|
2741
3012
|
if (helper !== null) {
|
|
2742
|
-
|
|
2743
|
-
let stable = left;
|
|
2744
|
-
if (!isSafeToRepeat(left)) {
|
|
2745
|
-
const local = lowerer.declareHiddenLocal("%nullish", left.type);
|
|
2746
|
-
stmts.push({ kind: "varDecl", localId: local.id, init: left, loc });
|
|
2747
|
-
stable = varRef(local.id, left.type, loc);
|
|
2748
|
-
}
|
|
2749
|
-
const unitTags = def.arms
|
|
2750
|
-
.map((arm, tag) => ({ arm, tag }))
|
|
2751
|
-
.filter(({ arm }) => isUnitType(arm))
|
|
2752
|
-
.map(({ tag }) => tag);
|
|
2753
|
-
let isNullish = { kind: "unionIsTag", unionId: left.type.unionId, tag: unitTags[0], negated: false, value: stable, type: BOOL, loc };
|
|
2754
|
-
for (const tag of unitTags.slice(1)) {
|
|
2755
|
-
isNullish = {
|
|
2756
|
-
kind: "logical",
|
|
2757
|
-
op: "||",
|
|
2758
|
-
left: isNullish,
|
|
2759
|
-
right: { kind: "unionIsTag", unionId: left.type.unionId, tag, negated: false, value: stable, type: BOOL, loc },
|
|
2760
|
-
type: BOOL,
|
|
2761
|
-
loc,
|
|
2762
|
-
};
|
|
2763
|
-
}
|
|
2764
|
-
const result = {
|
|
2765
|
-
kind: "ternary",
|
|
2766
|
-
cond: isNullish,
|
|
2767
|
-
then: lowerer.lowerExprExpecting(expr.right, type),
|
|
2768
|
-
else_: { kind: "call", callee: helper, args: [stable], type, loc },
|
|
2769
|
-
type,
|
|
2770
|
-
loc,
|
|
2771
|
-
};
|
|
2772
|
-
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 }));
|
|
2773
3014
|
}
|
|
2774
3015
|
}
|
|
2775
|
-
//
|
|
2776
|
-
//
|
|
2777
|
-
//
|
|
2778
|
-
// helper tests the unit tags and re-wraps the payload per arm. The
|
|
2779
|
-
// helper call evaluates the default EAGERLY where JS is lazy, so only
|
|
2780
|
-
// effect-free defaults (the literal null/0/"" spellings) qualify —
|
|
2781
|
-
// 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.
|
|
2782
3019
|
if (type.kind === "union") {
|
|
2783
|
-
const
|
|
2784
|
-
|
|
2785
|
-
if (
|
|
2786
|
-
|
|
2787
|
-
|
|
2788
|
-
|
|
2789
|
-
|
|
2790
|
-
|
|
2791
|
-
stable = varRef(local.id, leftT, loc);
|
|
2792
|
-
}
|
|
2793
|
-
const unitTags = def.arms
|
|
2794
|
-
.map((arm, tag) => ({ arm, tag }))
|
|
2795
|
-
.filter(({ arm }) => isUnitType(arm))
|
|
2796
|
-
.map(({ tag }) => tag);
|
|
2797
|
-
let isNullish = {
|
|
2798
|
-
kind: "unionIsTag",
|
|
2799
|
-
unionId: leftT.unionId,
|
|
2800
|
-
tag: unitTags[0],
|
|
2801
|
-
negated: false,
|
|
2802
|
-
value: stable,
|
|
2803
|
-
type: BOOL,
|
|
2804
|
-
loc,
|
|
2805
|
-
};
|
|
2806
|
-
for (const tag of unitTags.slice(1)) {
|
|
2807
|
-
isNullish = {
|
|
2808
|
-
kind: "logical",
|
|
2809
|
-
op: "||",
|
|
2810
|
-
left: isNullish,
|
|
2811
|
-
right: { kind: "unionIsTag", unionId: leftT.unionId, tag, negated: false, value: stable, type: BOOL, loc },
|
|
2812
|
-
type: BOOL,
|
|
2813
|
-
loc,
|
|
2814
|
-
};
|
|
2815
|
-
}
|
|
2816
|
-
let present = null;
|
|
2817
|
-
for (let i = armPairs.length - 1; i >= 0; i--) {
|
|
2818
|
-
const pair = armPairs[i];
|
|
2819
|
-
const wrapped = {
|
|
2820
|
-
kind: "unionWrap",
|
|
2821
|
-
unionId: type.unionId,
|
|
2822
|
-
tag: pair.dst,
|
|
2823
|
-
value: { kind: "unionNarrow", unionId: leftT.unionId, tag: pair.src, value: stable, type: pair.arm, loc },
|
|
2824
|
-
type,
|
|
2825
|
-
loc,
|
|
2826
|
-
};
|
|
2827
|
-
present = present === null
|
|
2828
|
-
? wrapped
|
|
2829
|
-
: {
|
|
2830
|
-
kind: "ternary",
|
|
2831
|
-
cond: { kind: "unionIsTag", unionId: leftT.unionId, tag: pair.src, negated: false, value: stable, type: BOOL, loc },
|
|
2832
|
-
then: wrapped,
|
|
2833
|
-
else_: present,
|
|
2834
|
-
type,
|
|
2835
|
-
loc,
|
|
2836
|
-
};
|
|
2837
|
-
}
|
|
2838
|
-
const result = {
|
|
2839
|
-
kind: "ternary",
|
|
2840
|
-
cond: isNullish,
|
|
2841
|
-
then: lowerer.lowerExprExpecting(expr.right, type),
|
|
2842
|
-
else_: present,
|
|
2843
|
-
type,
|
|
2844
|
-
loc,
|
|
2845
|
-
};
|
|
2846
|
-
return stmts.length === 0 ? result : { kind: "seqExpr", stmts, result, type, loc };
|
|
2847
|
-
}
|
|
2848
|
-
if (armPairs.every((p) => p.src >= 0 && p.dst >= 0)) {
|
|
2849
|
-
const right = lowerer.lowerExprExpecting(expr.right, type);
|
|
2850
|
-
if (isSafeToDiscard(right)) {
|
|
2851
|
-
const helper = nullishRetagHelper(lowerer, leftT, type, loc);
|
|
2852
|
-
return { kind: "call", callee: helper, args: [left, right], type, loc };
|
|
2853
|
-
}
|
|
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 }));
|
|
2854
3028
|
}
|
|
2855
3029
|
}
|
|
2856
3030
|
lowerer.unsupported("SC1090", expr, rest.length !== 1
|
|
2857
3031
|
? `'??' on '${lowerer.fmt(left.type)}' (the non-nullish result is a sub-union; check a discriminant field first)`
|
|
2858
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)`);
|
|
2859
3033
|
}
|
|
2860
|
-
/**
|
|
2861
|
-
|
|
2862
|
-
|
|
2863
|
-
|
|
2864
|
-
function
|
|
2865
|
-
const
|
|
2866
|
-
|
|
2867
|
-
if (
|
|
2868
|
-
|
|
2869
|
-
|
|
2870
|
-
|
|
2871
|
-
|
|
2872
|
-
const
|
|
2873
|
-
|
|
2874
|
-
const
|
|
2875
|
-
|
|
2876
|
-
|
|
2877
|
-
|
|
2878
|
-
|
|
2879
|
-
|
|
2880
|
-
|
|
2881
|
-
|
|
2882
|
-
then: [
|
|
2883
|
-
{
|
|
2884
|
-
kind: "return",
|
|
2885
|
-
value: {
|
|
2886
|
-
kind: "unionWrap",
|
|
2887
|
-
unionId: resT.unionId,
|
|
2888
|
-
tag: dst,
|
|
2889
|
-
value: { kind: "unionNarrow", unionId: leftT.unionId, tag, value: l, type: arm, loc },
|
|
2890
|
-
type: resT,
|
|
2891
|
-
loc,
|
|
2892
|
-
},
|
|
2893
|
-
loc,
|
|
2894
|
-
},
|
|
2895
|
-
],
|
|
2896
|
-
else_: null,
|
|
2897
|
-
loc,
|
|
2898
|
-
});
|
|
2899
|
-
});
|
|
2900
|
-
body.push({ kind: "return", value: r, loc });
|
|
2901
|
-
lowerer.liftedFns.push({
|
|
2902
|
-
name,
|
|
2903
|
-
params: [
|
|
2904
|
-
{ localId: "l.0", name: "l", type: leftT },
|
|
2905
|
-
{ localId: "r.0", name: "r", type: resT },
|
|
2906
|
-
],
|
|
2907
|
-
returnType: resT,
|
|
2908
|
-
locals: [
|
|
2909
|
-
{ id: "l.0", name: "l", type: leftT, mutable: true },
|
|
2910
|
-
{ id: "r.0", name: "r", type: resT, mutable: true },
|
|
2911
|
-
],
|
|
2912
|
-
body,
|
|
2913
|
-
loc,
|
|
2914
|
-
});
|
|
2915
|
-
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 };
|
|
2916
3056
|
}
|
|
2917
3057
|
/** A CONDITION-position expression: the result is consumed as a bool
|
|
2918
3058
|
* only, so `&&`/`||` descend recursively over ToBoolean'd operands —
|
|
@@ -2971,14 +3111,23 @@ function lowerPromiseThenPresence(lowerer, access, result) {
|
|
|
2971
3111
|
? (() => {
|
|
2972
3112
|
const arms = lowerer.unions.get(mapped.unionId)?.arms ?? [];
|
|
2973
3113
|
const promises = arms.filter((arm) => arm.kind === "promise");
|
|
2974
|
-
|
|
2975
|
-
|
|
3114
|
+
const promise = promises[0];
|
|
3115
|
+
return promises.length === 1 && promise?.kind === "promise" && arms.every((arm) => arm.kind === "promise" || isUnitType(arm))
|
|
3116
|
+
? promise
|
|
2976
3117
|
: null;
|
|
2977
3118
|
})()
|
|
2978
3119
|
: null;
|
|
2979
3120
|
if (promiseType === null)
|
|
2980
3121
|
return null;
|
|
2981
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
|
+
}
|
|
2982
3131
|
if (receiver.type.kind === "union") {
|
|
2983
3132
|
const optional = lowerer.runtimeOptionalPropertyReceiver(access.expression, receiver, promiseType, access.name.text);
|
|
2984
3133
|
if (optional !== null)
|
|
@@ -2990,6 +3139,29 @@ function lowerPromiseThenPresence(lowerer, access, result) {
|
|
|
2990
3139
|
receiver = { kind: "call", callee: helper, args: [receiver], type: promiseType, loc: receiver.loc };
|
|
2991
3140
|
}
|
|
2992
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
|
+
}
|
|
2993
3165
|
if (receiver.type.kind !== "promise")
|
|
2994
3166
|
return null;
|
|
2995
3167
|
if (isSafeToDiscard(receiver))
|
|
@@ -3012,15 +3184,107 @@ function runtimeOptionalLocalOf(lowerer, node) {
|
|
|
3012
3184
|
return local && lowerer.runtimeOptionalLocals.has(lowerer.runtimeOptionalRootOf(local)) ? local : null;
|
|
3013
3185
|
}
|
|
3014
3186
|
export function runtimeOptionalTrueIds(lowerer, node) {
|
|
3015
|
-
|
|
3016
|
-
|
|
3017
|
-
|
|
3018
|
-
|
|
3019
|
-
|
|
3020
|
-
|
|
3021
|
-
|
|
3022
|
-
const
|
|
3023
|
-
|
|
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)));
|
|
3024
3288
|
}
|
|
3025
3289
|
export function withRuntimeOptionalNarrowed(lowerer, locals, fn) {
|
|
3026
3290
|
if (locals.length === 0)
|
|
@@ -3104,6 +3368,15 @@ export function eqComparableUnion(lowerer, unionId) {
|
|
|
3104
3368
|
export function lowerIntrinsicProperty(lowerer, expr) {
|
|
3105
3369
|
if (lowerer.chainBlocked(expr))
|
|
3106
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
|
+
}
|
|
3107
3380
|
// A never-tainted JS receiver type lowered checked-dynamic
|
|
3108
3381
|
// (neverTaintedJsType — `cmd.length` on `const cmd = ['pwd', []]`):
|
|
3109
3382
|
// stand down so the dyn keyed read below the chain answers, instead
|
|
@@ -3139,6 +3412,8 @@ export function lowerIntrinsicProperty(lowerer, expr) {
|
|
|
3139
3412
|
const name = expr.name.text;
|
|
3140
3413
|
const strictReceiver = (expected) => {
|
|
3141
3414
|
const value = lowerer.lowerExpr(expr.expression);
|
|
3415
|
+
if (value.type.kind === "dyn")
|
|
3416
|
+
return lowerer.coerceInto(expr.expression, value, expected);
|
|
3142
3417
|
return lowerer.runtimeOptionalPropertyReceiver(expr.expression, value, expected, name) ?? value;
|
|
3143
3418
|
};
|
|
3144
3419
|
if (kind === "child") {
|
|
@@ -3359,15 +3634,14 @@ export function lowerIntrinsicProperty(lowerer, expr) {
|
|
|
3359
3634
|
// SEMANTICS.md notes the divergence from Node's Buffer pooling),
|
|
3360
3635
|
// the view's real offset for a DataView. A runtime read, so the
|
|
3361
3636
|
// receiver's evaluation is never discarded.
|
|
3362
|
-
const
|
|
3637
|
+
const expected = lowerer.mapTypeOf(lowerer.typeOf(expr.expression));
|
|
3638
|
+
const receiver = expected?.kind === "bytes" ? strictReceiver(expected) : lowerer.lowerExpr(expr.expression);
|
|
3363
3639
|
return { kind: "bytesIntrinsic", method: "byteOffset", receiver, args: [], type: F64, loc };
|
|
3364
3640
|
}
|
|
3365
3641
|
if (name === "buffer") {
|
|
3366
|
-
|
|
3367
|
-
|
|
3368
|
-
|
|
3369
|
-
lowerer.unsupported("SC1090", expr, "'.buffer' outside new DataView(x.buffer, ...) / Buffer.from(x.buffer, ...) " +
|
|
3370
|
-
"(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 };
|
|
3371
3645
|
}
|
|
3372
3646
|
if (name === "slice" || name === "subarray" || name === "set" || name === "toString") {
|
|
3373
3647
|
lowerer.unsupported("SC1090", expr, `typed-array methods as values (call '${name}' directly)`);
|
|
@@ -3376,7 +3650,8 @@ export function lowerIntrinsicProperty(lowerer, expr) {
|
|
|
3376
3650
|
}
|
|
3377
3651
|
if (kind === "map") {
|
|
3378
3652
|
if (name === "size") {
|
|
3379
|
-
const
|
|
3653
|
+
const expected = lowerer.mapTypeOf(lowerer.typeOf(expr.expression));
|
|
3654
|
+
const receiver = expected?.kind === "map" ? strictReceiver(expected) : lowerer.lowerExpr(expr.expression);
|
|
3380
3655
|
return { kind: "mapIntrinsic", method: "size", receiver, args: [], type: F64, loc: locOf(expr) };
|
|
3381
3656
|
}
|
|
3382
3657
|
if (MAP_METHODS.has(name) || name === "forEach") {
|
|
@@ -3386,7 +3661,8 @@ export function lowerIntrinsicProperty(lowerer, expr) {
|
|
|
3386
3661
|
}
|
|
3387
3662
|
if (kind === "set") {
|
|
3388
3663
|
if (name === "size") {
|
|
3389
|
-
const
|
|
3664
|
+
const expected = lowerer.mapTypeOf(lowerer.typeOf(expr.expression));
|
|
3665
|
+
const receiver = expected?.kind === "set" ? strictReceiver(expected) : lowerer.lowerExpr(expr.expression);
|
|
3390
3666
|
return { kind: "setIntrinsic", method: "size", receiver, args: [], type: F64, loc: locOf(expr) };
|
|
3391
3667
|
}
|
|
3392
3668
|
if (SET_METHODS.has(name)) {
|
|
@@ -3629,6 +3905,13 @@ export function lowerArrayLiteral(lowerer, expr, expected) {
|
|
|
3629
3905
|
// to DYN wholesale now (mapType's dyn-element array rule), so the
|
|
3630
3906
|
// literal IS the dyn array.
|
|
3631
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
|
+
}
|
|
3632
3915
|
const elems = expr.elements.map((el) => {
|
|
3633
3916
|
if (ts.isSpreadElement(el)) {
|
|
3634
3917
|
lowerer.unsupported("SC1090", el, "spread elements in a dynamic (unknown[]) array literal");
|
|
@@ -3679,19 +3962,7 @@ export function lowerArrayLiteral(lowerer, expr, expected) {
|
|
|
3679
3962
|
(ts.isConditionalExpression(srcNode)
|
|
3680
3963
|
? lowerTernary(lowerer, srcNode, type)
|
|
3681
3964
|
: lowerer.lowerExpr(el.expression));
|
|
3682
|
-
|
|
3683
|
-
// insertion order (setIntrinsic toArray); the spread machinery
|
|
3684
|
-
// then copies like any array source.
|
|
3685
|
-
if (src.type.kind === "set" && typeEquals(src.type.elem, type.elem)) {
|
|
3686
|
-
src = {
|
|
3687
|
-
kind: "setIntrinsic",
|
|
3688
|
-
method: "toArray",
|
|
3689
|
-
receiver: src,
|
|
3690
|
-
args: [],
|
|
3691
|
-
type: arrayOf(src.type.elem),
|
|
3692
|
-
loc: locOf(el),
|
|
3693
|
-
};
|
|
3694
|
-
}
|
|
3965
|
+
src = lowerCollectionSpread(lowerer, src, el.expression) ?? src;
|
|
3695
3966
|
// `[...new SymbolIterator]`: a CLASS ITERABLE drains through its
|
|
3696
3967
|
// own protocol into a fresh element array (classIteratorDrainCall
|
|
3697
3968
|
// — an infinite iterator loops forever, exactly Node), and the
|
|
@@ -3723,6 +3994,16 @@ export function lowerArrayLiteral(lowerer, expr, expected) {
|
|
|
3723
3994
|
loc: locOf(el),
|
|
3724
3995
|
};
|
|
3725
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
|
+
}
|
|
3726
4007
|
// A same-family array whose ELEMENT lifts (string[] into a
|
|
3727
4008
|
// (string | symbol)[] literal — per-element wrap/width copy):
|
|
3728
4009
|
// the interned width helper reshapes before the spread copies.
|
|
@@ -3982,16 +4263,17 @@ export function lowerElementAccess(lowerer, expr) {
|
|
|
3982
4263
|
lowerer.fenceStaticResponseMember(expr, "read");
|
|
3983
4264
|
lowerer.fenceStaticHeadersMember(expr, "read");
|
|
3984
4265
|
lowerer.fenceStaticReadableStreamMember(expr, "read");
|
|
3985
|
-
//
|
|
3986
|
-
//
|
|
3987
|
-
// compile-time bindings, not runtime properties, and none of the
|
|
3988
|
-
// probed web-platform objects exist here — the honest answer is
|
|
3989
|
-
// undefined for every dynamic name (SEMANTICS.md documents the
|
|
3990
|
-
// divergence: statically-known globals are not reachable this way;
|
|
3991
|
-
// the PROPERTY spelling answers the identity token instead — see
|
|
3992
|
-
// lowerPropertyAccess's globalThis rule).
|
|
4266
|
+
// Computed global reads share the stored global object's custom
|
|
4267
|
+
// properties. Symbol keys retain their identity-indexed store.
|
|
3993
4268
|
if (!expr.questionDotToken && stdlibGlobalNameOf(lowerer, expr.expression) === "globalThis") {
|
|
3994
|
-
|
|
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) };
|
|
3995
4277
|
}
|
|
3996
4278
|
// `req.headers["x-name"]` — the computed twin of `req.headers.host`
|
|
3997
4279
|
// (the server spoke owns both; the envGet precedent).
|
|
@@ -4004,7 +4286,7 @@ export function lowerElementAccess(lowerer, expr) {
|
|
|
4004
4286
|
// lower to the ONE process.envGet intrinsic. The read narrows like any
|
|
4005
4287
|
// union-typed expression when the checker narrowed this occurrence.
|
|
4006
4288
|
if (lowerer.isProcessEnv(expr.expression)) {
|
|
4007
|
-
const key = lowerer
|
|
4289
|
+
const key = lowerEnvironmentKey(lowerer, expr.argumentExpression);
|
|
4008
4290
|
if (key.type.kind !== "string") {
|
|
4009
4291
|
lowerer.unsupported("SC1090", expr.argumentExpression, "indexing process.env with non-string keys");
|
|
4010
4292
|
}
|
|
@@ -4027,17 +4309,27 @@ export function lowerElementAccess(lowerer, expr) {
|
|
|
4027
4309
|
return lowerer.maybeNarrow(ex, expr);
|
|
4028
4310
|
}
|
|
4029
4311
|
// Symbol-keyed property READS (`this[kLimit]`): when the key is a
|
|
4030
|
-
// statically-resolvable
|
|
4031
|
-
//
|
|
4312
|
+
// statically-resolvable symbol declared as a field of the receiver's
|
|
4313
|
+
// class (classSymbolKeyOf — the countdown.js/OpenTUI idioms), the
|
|
4032
4314
|
// read IS an ordinary field read of the hidden slot. Every other
|
|
4033
4315
|
// symbol key stays fenced: record shapes, runtime-identity keys
|
|
4034
|
-
// (symbol parameters,
|
|
4316
|
+
// (symbol parameters, reassigned bindings), and keys no class declares
|
|
4035
4317
|
// — the layouts are compile-time field lists.
|
|
4036
4318
|
if (lowerer.mapTypeOf(lowerer.typeOf(expr.argumentExpression))?.kind === "symbol") {
|
|
4037
4319
|
const target = symbolFieldTarget(lowerer, expr);
|
|
4038
4320
|
if (target)
|
|
4039
4321
|
return lowerer.maybeNarrow(lowerer.fieldGetExpr(target, locOf(expr), expr), expr);
|
|
4040
|
-
|
|
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)");
|
|
4041
4333
|
}
|
|
4042
4334
|
// A never-tainted JS receiver type (neverTaintedJsType — `cmd[1]` on
|
|
4043
4335
|
// `const cmd = ['pwd', []]`, whose binding lowered checked-dynamic)
|
|
@@ -4046,6 +4338,16 @@ export function lowerElementAccess(lowerer, expr) {
|
|
|
4046
4338
|
let receiverIr = neverTaintedJsType(lowerer, expr.expression, lowerer.typeOf(expr.expression))
|
|
4047
4339
|
? null
|
|
4048
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
|
+
}
|
|
4049
4351
|
// A readonly tuple union under Array.isArray is checker-typed as an
|
|
4050
4352
|
// intersection with any[], whose structural mapping is not the tuple
|
|
4051
4353
|
// shape. maybeNarrow on the receiver uses the runtime tag proof to
|
|
@@ -4054,6 +4356,19 @@ export function lowerElementAccess(lowerer, expr) {
|
|
|
4054
4356
|
const checkerArray = lowerer.checkerArrayValue(expr.expression);
|
|
4055
4357
|
if (checkerArray)
|
|
4056
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
|
+
}
|
|
4057
4372
|
if (receiverIr?.kind === "jsval") {
|
|
4058
4373
|
// Dispatch follows the RUNTIME world (383(d)): a checker-'any'
|
|
4059
4374
|
// receiver whose value LOWERED checked-dynamic (`bag.list[0]` where
|
|
@@ -4079,6 +4394,8 @@ export function lowerElementAccess(lowerer, expr) {
|
|
|
4079
4394
|
// read undefined — divergence 4's policy).
|
|
4080
4395
|
if (receiverIr?.kind === "bytes") {
|
|
4081
4396
|
let recv = lowerer.lowerExpr(expr.expression);
|
|
4397
|
+
if (recv.type.kind === "dyn")
|
|
4398
|
+
recv = lowerer.coerceInto(expr.expression, recv, receiverIr);
|
|
4082
4399
|
if (recv.type.kind === "union" && lowerer.armTag(recv.type.unionId, UNDEFINED_T) >= 0) {
|
|
4083
4400
|
const present = lowerer.stripUndefinedArm(recv.type);
|
|
4084
4401
|
const helper = present.kind === "bytes"
|
|
@@ -4095,6 +4412,9 @@ export function lowerElementAccess(lowerer, expr) {
|
|
|
4095
4412
|
if (recv.type.kind === "jsval")
|
|
4096
4413
|
return islandElementRead(lowerer, expr, recv);
|
|
4097
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
|
+
}
|
|
4098
4418
|
if (index.type.kind !== "f64") {
|
|
4099
4419
|
lowerer.unsupported("SC1090", expr.argumentExpression, "indexing with non-number keys");
|
|
4100
4420
|
}
|
|
@@ -4116,6 +4436,17 @@ export function lowerElementAccess(lowerer, expr) {
|
|
|
4116
4436
|
// like the array path.
|
|
4117
4437
|
if (obj.type.kind === "jsval")
|
|
4118
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
|
+
}
|
|
4119
4450
|
if (obj.type.kind === "union" && lowerer.armTag(obj.type.unionId, UNDEFINED_T) >= 0) {
|
|
4120
4451
|
// The checker sees the outer `arrays[i]` as a tuple when
|
|
4121
4452
|
// noUncheckedIndexedAccess is disabled, but the lowered read is
|
|
@@ -4171,21 +4502,28 @@ export function lowerElementAccess(lowerer, expr) {
|
|
|
4171
4502
|
// (`pkg.workspaces.packages["0"]` — the lowering world types the
|
|
4172
4503
|
// unknown-rooted chain `any`); a non-dyn lowering falls through to
|
|
4173
4504
|
// the fences below (re-lowering is pure IR construction).
|
|
4174
|
-
if (receiverIr?.kind === "dyn" || receiverIr === null) {
|
|
4505
|
+
if (receiverIr?.kind === "dyn" || receiverIr?.kind === "union" || receiverIr === null) {
|
|
4175
4506
|
const obj = lowerer.lowerExpr(expr.expression);
|
|
4176
|
-
|
|
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)) {
|
|
4177
4517
|
const rawKey = lowerer.lowerExpr(expr.argumentExpression);
|
|
4178
4518
|
// Number, bool, and DYN keys stringify (ToPropertyKey) — the
|
|
4179
4519
|
// dyn-keyed read `catchWarning[warning.name]` where the property
|
|
4180
4520
|
// chain itself lowered dyn.
|
|
4181
|
-
const key = rawKey.type.kind === "
|
|
4182
|
-
? rawKey
|
|
4183
|
-
:
|
|
4184
|
-
|
|
4185
|
-
: null;
|
|
4186
|
-
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") {
|
|
4187
4525
|
const opt = hasOptionalChainGuard(expr.expression);
|
|
4188
|
-
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);
|
|
4189
4527
|
}
|
|
4190
4528
|
}
|
|
4191
4529
|
// A checker-`any` receiver that LOWERS to a static ARRAY
|
|
@@ -4195,6 +4533,9 @@ export function lowerElementAccess(lowerer, expr) {
|
|
|
4195
4533
|
if (obj.type.kind === "array") {
|
|
4196
4534
|
const index = lowerer.lowerExpr(expr.argumentExpression);
|
|
4197
4535
|
if (index.type.kind === "f64") {
|
|
4536
|
+
const safe = lowerSafeIndexRead(lowerer, obj, index, locOf(expr));
|
|
4537
|
+
if (safe)
|
|
4538
|
+
return safe;
|
|
4198
4539
|
return lowerer.maybeNarrow({ kind: "arrayGet", arr: obj, index, type: obj.type.elem, loc: locOf(expr) }, expr);
|
|
4199
4540
|
}
|
|
4200
4541
|
}
|
|
@@ -4217,7 +4558,7 @@ export function lowerElementAccess(lowerer, expr) {
|
|
|
4217
4558
|
}
|
|
4218
4559
|
}
|
|
4219
4560
|
}
|
|
4220
|
-
if (key.type.kind === "string") {
|
|
4561
|
+
if (key.type.kind === "string" || key.type.kind === "f64") {
|
|
4221
4562
|
const lit = ts.isStringLiteral(expr.argumentExpression)
|
|
4222
4563
|
? expr.argumentExpression.text
|
|
4223
4564
|
: null;
|
|
@@ -4225,33 +4566,20 @@ export function lowerElementAccess(lowerer, expr) {
|
|
|
4225
4566
|
if (keyed)
|
|
4226
4567
|
return lowerer.maybeNarrow(keyed, expr);
|
|
4227
4568
|
}
|
|
4228
|
-
// `u?.split(":")[0]` — a NUMBER index over an undefined-armed
|
|
4229
|
-
// array union: the array arm answers its element (invalid indices
|
|
4230
|
-
// trap — divergence 4's policy), unit arms answer undefined (the
|
|
4231
|
-
// chain's short-circuit value). Every non-unit arm must be the
|
|
4232
|
-
// same array type.
|
|
4233
|
-
if (key.type.kind === "f64") {
|
|
4234
|
-
const def = lowerer.unions.get(value.type.unionId);
|
|
4235
|
-
const arrArms = def?.arms.filter((a) => a.kind === "array") ?? [];
|
|
4236
|
-
if (def &&
|
|
4237
|
-
arrArms.length > 0 &&
|
|
4238
|
-
def.arms.every((a) => a.kind === "array" || isUnitType(a)) &&
|
|
4239
|
-
arrArms.every((a) => typeEquals(a, arrArms[0]))) {
|
|
4240
|
-
const elem = arrArms[0].elem;
|
|
4241
|
-
const hasUnit = def.arms.some(isUnitType);
|
|
4242
|
-
const t = !hasUnit
|
|
4243
|
-
? elem
|
|
4244
|
-
: elem.kind === "union"
|
|
4245
|
-
? lowerer.withUndefinedArmOf(elem)
|
|
4246
|
-
: lowerer.withUndefinedArm(elem);
|
|
4247
|
-
if (t) {
|
|
4248
|
-
return lowerer.maybeNarrow({ kind: "unionKeyGet", unionId: value.type.unionId, key, value, type: t, loc: locOf(expr) }, expr);
|
|
4249
|
-
}
|
|
4250
|
-
}
|
|
4251
|
-
}
|
|
4252
4569
|
}
|
|
4253
4570
|
}
|
|
4254
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
|
+
}
|
|
4255
4583
|
if (receiverIr?.kind === "string") {
|
|
4256
4584
|
// `s[i]` with a number index reads a UTF-16 code unit — charAt's
|
|
4257
4585
|
// exact job (the UTF-16-exact runtime). Without
|
|
@@ -4259,16 +4587,23 @@ export function lowerElementAccess(lowerer, expr) {
|
|
|
4259
4587
|
// and charAt's "" is the only string-typed answer for an
|
|
4260
4588
|
// out-of-range or fractional index where JS reads `undefined` —
|
|
4261
4589
|
// SEMANTICS.md documents the divergence; in-range integer reads
|
|
4262
|
-
// (the loop pattern) are JS-exact.
|
|
4263
|
-
//
|
|
4264
|
-
|
|
4590
|
+
// (the loop pattern) are JS-exact. Optional results instead retain
|
|
4591
|
+
// undefined for missing string properties.
|
|
4592
|
+
const recv = lowerer.lowerExpr(expr.expression);
|
|
4265
4593
|
const index = lowerer.lowerExpr(expr.argumentExpression);
|
|
4266
|
-
|
|
4267
|
-
|
|
4268
|
-
|
|
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
|
+
}
|
|
4269
4605
|
}
|
|
4270
|
-
lowerer.unsupported("SC1090", expr, "string indexing with this index/result shape (a number index
|
|
4271
|
-
"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)");
|
|
4272
4607
|
}
|
|
4273
4608
|
lowerer.unsupported("SC1090", expr, "element access on non-array values");
|
|
4274
4609
|
}
|
|
@@ -4346,14 +4681,17 @@ export function lowerElementAccess(lowerer, expr) {
|
|
|
4346
4681
|
// can validate, and fences by name when it is not — an arrayGet over a
|
|
4347
4682
|
// dyn receiver is never emitted (the validator ICE).
|
|
4348
4683
|
if (arr.type.kind === "dyn") {
|
|
4349
|
-
if (
|
|
4684
|
+
if (canDynCheckTo(receiverIr, (id) => lowerer.shapes.get(id), (id) => lowerer.unions.get(id))) {
|
|
4350
4685
|
arr = { kind: "dynCheck", value: arr, type: receiverIr, loc: locOf(expr) };
|
|
4351
4686
|
}
|
|
4352
4687
|
else {
|
|
4353
4688
|
lowerer.unsupported("SC1090", expr, `element access on checked-dynamic values with '${lowerer.fmt(receiverIr)}' elements`);
|
|
4354
4689
|
}
|
|
4355
4690
|
}
|
|
4356
|
-
|
|
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);
|
|
4357
4695
|
if (index.type.kind !== "f64") {
|
|
4358
4696
|
lowerer.unsupported("SC1090", expr.argumentExpression, "indexing with non-number keys");
|
|
4359
4697
|
}
|
|
@@ -4475,6 +4813,16 @@ export function lowerRecordKeyRead(lowerer, expr, shapeId, shape, includeUndefin
|
|
|
4475
4813
|
// to observe the missing value instead of throwing during that check.
|
|
4476
4814
|
return includeUndefined ? read : lowerer.maybeNarrow(read, expr);
|
|
4477
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
|
+
}
|
|
4478
4826
|
// Predicates can strengthen optional fields without changing the
|
|
4479
4827
|
// receiver's stored layout. Use that layout for bracket reads just as
|
|
4480
4828
|
// fieldTarget does for the corresponding dot reads.
|
|
@@ -4530,7 +4878,7 @@ export function lowerRecordKeyRead(lowerer, expr, shapeId, shape, includeUndefin
|
|
|
4530
4878
|
let declared = null;
|
|
4531
4879
|
if (shape.indexValue) {
|
|
4532
4880
|
declared = shape.indexValue;
|
|
4533
|
-
if (includeUndefined || lowerer.program.getCompilerOptions().noUncheckedIndexedAccess) {
|
|
4881
|
+
if (declared.kind !== "dyn" && (includeUndefined || lowerer.program.getCompilerOptions().noUncheckedIndexedAccess)) {
|
|
4534
4882
|
declared = lowerer.withUndefinedArmOf(declared);
|
|
4535
4883
|
if (!declared)
|
|
4536
4884
|
lowerer.badType(expr, lowerer.typeOf(expr));
|
|
@@ -4540,6 +4888,8 @@ export function lowerRecordKeyRead(lowerer, expr, shapeId, shape, includeUndefin
|
|
|
4540
4888
|
shape.fields.every((f) => typeEquals(f.type, shape.fields[0].type))) {
|
|
4541
4889
|
declared = shape.fields[0].type;
|
|
4542
4890
|
}
|
|
4891
|
+
if (!declared && isJsSourceFile(expr.getSourceFile()) && recordKeyResultOk(lowerer, shape, DYN))
|
|
4892
|
+
declared = DYN;
|
|
4543
4893
|
if (!declared) {
|
|
4544
4894
|
lowerer.unsupported("SC1090", expr, `dynamic keyed reads of '${lowerer.fmt({ kind: "record", shapeId })}' (the declared fields have no one common type)`);
|
|
4545
4895
|
}
|
|
@@ -4584,6 +4934,10 @@ export function lowerAbsenceProbe(lowerer, node) {
|
|
|
4584
4934
|
}
|
|
4585
4935
|
if (ts.isPropertyAccessExpression(expr)) {
|
|
4586
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
|
+
}
|
|
4587
4941
|
if (target?.container !== "recordOvf")
|
|
4588
4942
|
return null;
|
|
4589
4943
|
// A JS file-scope object-literal global is record-shaped to the
|
|
@@ -4685,6 +5039,7 @@ function recordKeyTypeLiteralText(lowerer, node) {
|
|
|
4685
5039
|
function recordKeyResultOk(lowerer, shape, type) {
|
|
4686
5040
|
const surfaces = (t) => typeEquals(t, type) ||
|
|
4687
5041
|
(type.kind === "union" && lowerer.armTag(type.unionId, t) >= 0) ||
|
|
5042
|
+
exactUnionWidening(lowerer, t, type) ||
|
|
4688
5043
|
(type.kind === "dyn" && lowerer.dynConvertible(t));
|
|
4689
5044
|
if (!shape.fields.every((f) => surfaces(f.type)))
|
|
4690
5045
|
return false;
|
|
@@ -4696,6 +5051,13 @@ function recordKeyResultOk(lowerer, shape, type) {
|
|
|
4696
5051
|
}
|
|
4697
5052
|
return true;
|
|
4698
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
|
+
}
|
|
4699
5061
|
/** The literal index of a tuple access, or null when the expression isn't
|
|
4700
5062
|
* a non-negative integer literal (parentheses tolerated — tsc's own
|
|
4701
5063
|
* literal-index typing accepts them). */
|
|
@@ -4780,16 +5142,71 @@ export function lowerElementCompound(lowerer, expr, op) {
|
|
|
4780
5142
|
loc,
|
|
4781
5143
|
};
|
|
4782
5144
|
}
|
|
4783
|
-
/**
|
|
4784
|
-
*
|
|
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
|
+
}
|
|
4785
5196
|
export function lowerElementWrite(lowerer, expr) {
|
|
4786
5197
|
const target = expr.left;
|
|
4787
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
|
+
}
|
|
4788
5205
|
// `process.env[key] = v` — the computed twin of the dotted env write:
|
|
4789
5206
|
// setenv(3), string keys and string values only.
|
|
4790
5207
|
if (lowerer.isProcessEnv(target.expression) && !target.questionDotToken) {
|
|
4791
5208
|
const loc = locOf(expr);
|
|
4792
|
-
const key = lowerer
|
|
5209
|
+
const key = lowerEnvironmentKey(lowerer, target.argumentExpression);
|
|
4793
5210
|
if (key.type.kind !== "string") {
|
|
4794
5211
|
lowerer.unsupported("SC1090", target.argumentExpression, "indexing process.env with non-string keys");
|
|
4795
5212
|
}
|
|
@@ -4813,9 +5230,21 @@ export function lowerElementWrite(lowerer, expr) {
|
|
|
4813
5230
|
const value = lowerer.lowerExprExpecting(expr.right, fieldT.fieldType);
|
|
4814
5231
|
return lowerer.fieldSetStmt(fieldT, value, locOf(expr), target);
|
|
4815
5232
|
}
|
|
4816
|
-
|
|
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;
|
|
4817
5247
|
}
|
|
4818
|
-
const receiverIr = lowerer.mapTypeOf(lowerer.typeOf(target.expression));
|
|
4819
5248
|
if (receiverIr?.kind === "jsval") {
|
|
4820
5249
|
const obj = lowerer.lowerExpr(target.expression);
|
|
4821
5250
|
// Dispatch follows the RUNTIME world (383(d)): a checker-'any'
|
|
@@ -4857,9 +5286,7 @@ export function lowerElementWrite(lowerer, expr) {
|
|
|
4857
5286
|
const obj = lowerer.lowerExpr(target.expression);
|
|
4858
5287
|
if (obj.type.kind === "dyn") {
|
|
4859
5288
|
const loc = locOf(expr);
|
|
4860
|
-
|
|
4861
|
-
if (key.type.kind === "f64")
|
|
4862
|
-
key = lowerer.ensureString(key, target.argumentExpression);
|
|
5289
|
+
const key = lowerRecordPropertyKey(lowerer, lowerer.lowerExpr(target.argumentExpression), target.argumentExpression);
|
|
4863
5290
|
if (key.type.kind !== "string") {
|
|
4864
5291
|
lowerer.unsupported("SC1090", target.argumentExpression, "indexing with non-string or non-number keys");
|
|
4865
5292
|
}
|
|
@@ -4870,6 +5297,20 @@ export function lowerElementWrite(lowerer, expr) {
|
|
|
4870
5297
|
return { kind: "exprStmt", expr: { kind: "libCall", fn: "dyn.keySet", args: [obj, key, value], type: VOID, loc }, loc };
|
|
4871
5298
|
}
|
|
4872
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
|
+
}
|
|
4873
5314
|
// Typed-array element write `b[i] = v` — bytesSet: the value is an f64
|
|
4874
5315
|
// the runtime coerces JS-exactly; invalid indices trap (no appends —
|
|
4875
5316
|
// typed arrays are fixed-length; JS would ignore the write).
|
|
@@ -4884,6 +5325,9 @@ export function lowerElementWrite(lowerer, expr) {
|
|
|
4884
5325
|
return { kind: "exprStmt", expr: { kind: "jsOp", op: "setIdx", args: [recv, key, value], type: VOID, loc }, loc };
|
|
4885
5326
|
}
|
|
4886
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
|
+
}
|
|
4887
5331
|
if (index.type.kind !== "f64") {
|
|
4888
5332
|
lowerer.unsupported("SC1090", target.argumentExpression, "indexing with non-number keys");
|
|
4889
5333
|
}
|
|
@@ -4899,6 +5343,9 @@ export function lowerElementWrite(lowerer, expr) {
|
|
|
4899
5343
|
// a recordSet/recordKeySet over a jsval.
|
|
4900
5344
|
if (shape) {
|
|
4901
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
|
+
}
|
|
4902
5349
|
if (obj.type.kind === "jsval") {
|
|
4903
5350
|
const key = lowerer.jsvalIn(lowerer.lowerExpr(target.argumentExpression), target.argumentExpression);
|
|
4904
5351
|
const value = lowerer.jsvalIn(lowerer.lowerExpr(expr.right), expr.right);
|
|
@@ -5050,9 +5497,9 @@ export function lowerElementWrite(lowerer, expr) {
|
|
|
5050
5497
|
// type (static `any` — mapTypeOf answers null without --dynamic)
|
|
5051
5498
|
// probes the receiver's own lowered world: a dyn value takes the same
|
|
5052
5499
|
// write, anything else falls through to the fences.
|
|
5053
|
-
if (receiverIr?.kind === "dyn" || receiverIr === null) {
|
|
5500
|
+
if (receiverIr?.kind === "dyn" || receiverIr?.kind === "object" || receiverIr === null) {
|
|
5054
5501
|
const obj = receiverIr !== null ? lowerer.lowerExpr(target.expression) : tryLowerExpression(lowerer, target.expression);
|
|
5055
|
-
if (obj !== null && obj.type.kind === "dyn") {
|
|
5502
|
+
if (obj !== null && (obj.type.kind === "dyn" || isDynTypedRefType(obj.type))) {
|
|
5056
5503
|
const loc = locOf(expr);
|
|
5057
5504
|
const litKey = recordKeyLiteralText(target.argumentExpression);
|
|
5058
5505
|
let key = litKey !== null
|
|
@@ -5066,7 +5513,7 @@ export function lowerElementWrite(lowerer, expr) {
|
|
|
5066
5513
|
if (value.type.kind !== "dyn") {
|
|
5067
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)`);
|
|
5068
5515
|
}
|
|
5069
|
-
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 };
|
|
5070
5517
|
}
|
|
5071
5518
|
}
|
|
5072
5519
|
if (receiverIr?.kind !== "array") {
|
|
@@ -5096,19 +5543,42 @@ export function lowerElementWrite(lowerer, expr) {
|
|
|
5096
5543
|
if (arr.type.kind !== "array") {
|
|
5097
5544
|
lowerer.unsupported("SC1090", target.expression, "assignment through a possibly missing nested array receiver");
|
|
5098
5545
|
}
|
|
5099
|
-
const index = lowerer.lowerExpr(target.argumentExpression);
|
|
5546
|
+
const index = lowerOptionalNumber(lowerer, lowerer.lowerExpr(target.argumentExpression), locOf(target.argumentExpression), target.argumentExpression);
|
|
5100
5547
|
if (index.type.kind !== "f64") {
|
|
5101
5548
|
lowerer.unsupported("SC1090", target.argumentExpression, "indexing with non-number keys");
|
|
5102
5549
|
}
|
|
5103
5550
|
// Optional array reads carry the source array's payload type plus an
|
|
5104
5551
|
// undefined arm. arrayValueStore turns that arm into the runtime's
|
|
5105
5552
|
// UNDEFINED state while keeping number[]/string[] payload storage scalar.
|
|
5106
|
-
|
|
5107
|
-
|
|
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);
|
|
5108
5573
|
}
|
|
5109
5574
|
export function ensureString(lowerer, e, node) {
|
|
5110
5575
|
if (e.type.kind === "string")
|
|
5111
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
|
+
}
|
|
5112
5582
|
if (e.type.kind === "bigint") {
|
|
5113
5583
|
return {
|
|
5114
5584
|
kind: "libCall",
|
|
@@ -5124,18 +5594,21 @@ export function ensureString(lowerer, e, node) {
|
|
|
5124
5594
|
// strings verbatim, arrays via join, objects as "[object Object]").
|
|
5125
5595
|
return { kind: "toString", operand: e, type: STRING, loc: e.loc };
|
|
5126
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
|
+
}
|
|
5127
5603
|
if (e.type.kind === "jsval") {
|
|
5128
5604
|
// String(v) in the engine — JS-exact (and Node-exact in templates).
|
|
5129
5605
|
return { kind: "jsOp", op: "toStr", args: [e], type: STRING, loc: e.loc };
|
|
5130
5606
|
}
|
|
5131
5607
|
if (e.type.kind === "object") {
|
|
5132
|
-
// String(err) / `${err}`
|
|
5133
|
-
// .toString — the ONE runtime implementation (overriding it is
|
|
5134
|
-
// fenced), "name: message" with the empty-side elisions, exactly
|
|
5135
|
-
// Node's String(err), which carries no stack either.
|
|
5608
|
+
// String(err) / `${err}` uses the same dispatch as err.toString().
|
|
5136
5609
|
for (let c = lowerer.classes.get(e.type.className) ?? null; c; c = c.base) {
|
|
5137
5610
|
if (c.builtinError) {
|
|
5138
|
-
return
|
|
5611
|
+
return errorToStringCall(lowerer, e);
|
|
5139
5612
|
}
|
|
5140
5613
|
}
|
|
5141
5614
|
}
|
|
@@ -5326,6 +5799,38 @@ export function lowerAsExpression(lowerer, expr) {
|
|
|
5326
5799
|
return lowerer.jsvalIn(inner, expr.expression);
|
|
5327
5800
|
}
|
|
5328
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
|
+
}
|
|
5329
5834
|
// Static assertions normally erase, but record layouts are
|
|
5330
5835
|
// monomorphic: a consumer selected from the asserted shape must see
|
|
5331
5836
|
// that shape physically. Reuse the ordinary slot coercion so plain
|
|
@@ -5378,7 +5883,21 @@ export function lowerAsExpression(lowerer, expr) {
|
|
|
5378
5883
|
lowerer.badType(expr.type, targetTs);
|
|
5379
5884
|
if (target.kind === "dyn")
|
|
5380
5885
|
return inner; // `as unknown`: erasure
|
|
5381
|
-
|
|
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))) {
|
|
5382
5901
|
// Bare undefined-armed targets pass when every OTHER arm is
|
|
5383
5902
|
// JSON-safe: the checked-dynamic tree holds a first-class undefined value now
|
|
5384
5903
|
// (index-signature overflow reads produce it for missing keys), and
|
|
@@ -5414,20 +5933,6 @@ export function lowerAsExpression(lowerer, expr) {
|
|
|
5414
5933
|
if (DYN_HANDLE_KINDS.has(target.kind)) {
|
|
5415
5934
|
return { kind: "dynCheck", value: inner, type: target, loc: locOf(expr) };
|
|
5416
5935
|
}
|
|
5417
|
-
// An all-`unknown`-fields record target (`err as { code?: unknown }`
|
|
5418
|
-
// — the errno-probing idiom): there is nothing to validate (every
|
|
5419
|
-
// field is unknown, exactly what the dyn value already answers) and
|
|
5420
|
-
// nothing to build — the cast is pure typing, so it ERASES and the
|
|
5421
|
-
// reads ride the dyn keyed read.
|
|
5422
|
-
if (target.kind === "record") {
|
|
5423
|
-
const shape = lowerer.shapes.get(target.shapeId);
|
|
5424
|
-
if (shape &&
|
|
5425
|
-
!shape.tuple &&
|
|
5426
|
-
shape.fields.every((f) => f.type.kind === "dyn") &&
|
|
5427
|
-
(!shape.indexValue || shape.indexValue.kind === "dyn")) {
|
|
5428
|
-
return inner;
|
|
5429
|
-
}
|
|
5430
|
-
}
|
|
5431
5936
|
lowerer.unsupported("SC1090", expr, `a checked cast of 'unknown' to '${target.kind === "void" ? "void" : lowerer.fmt(target)}' ` +
|
|
5432
5937
|
`(a dynamic value can only be validated against JSON-representable types: ` +
|
|
5433
5938
|
`number, string, boolean, records, arrays, and unions of those)`);
|
|
@@ -5445,7 +5950,10 @@ export function lowerPrefixUnary(lowerer, expr) {
|
|
|
5445
5950
|
if (raw.type.kind === "bigint") {
|
|
5446
5951
|
return { kind: "libCall", fn: "bigint.neg", args: [raw], type: BIGINT_T, loc };
|
|
5447
5952
|
}
|
|
5448
|
-
|
|
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);
|
|
5449
5957
|
if (operand.type.kind !== "f64")
|
|
5450
5958
|
lowerer.unsupported("SC1043", expr);
|
|
5451
5959
|
if (operand.kind === "numLit")
|
|
@@ -5454,6 +5962,9 @@ export function lowerPrefixUnary(lowerer, expr) {
|
|
|
5454
5962
|
}
|
|
5455
5963
|
case ts.SyntaxKind.PlusToken: {
|
|
5456
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
|
+
}
|
|
5457
5968
|
// Unary + is ToNumber; on an already-number operand it's identity,
|
|
5458
5969
|
// and a STRING operand runs the runtime's ECMA-exact StringToNumber
|
|
5459
5970
|
// (num.fromString — Number(aString)'s lowering, scr_string.c).
|
|
@@ -5479,14 +5990,16 @@ export function lowerPrefixUnary(lowerer, expr) {
|
|
|
5479
5990
|
if (raw.type.kind === "bigint") {
|
|
5480
5991
|
return { kind: "libCall", fn: "bigint.not", args: [raw], type: BIGINT_T, loc };
|
|
5481
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
|
+
};
|
|
5482
5997
|
// A checked-dynamic value retains enough JavaScript structure to
|
|
5483
5998
|
// run exact ToNumber, including the object valueOf/toString
|
|
5484
5999
|
// protocol. This is the static `any`/JS-lane answer for idioms
|
|
5485
6000
|
// such as `while (~index)`, where inference may have widened an
|
|
5486
6001
|
// otherwise numeric slot to the checked-dynamic tree.
|
|
5487
|
-
const operand = raw
|
|
5488
|
-
? { kind: "libCall", fn: "dyn.toNumberCoerce", args: [raw], type: F64, loc }
|
|
5489
|
-
: lowerOptionalNumber(lowerer, raw, loc, expr.operand);
|
|
6002
|
+
const operand = lowerOptionalNumber(lowerer, raw, loc, expr.operand);
|
|
5490
6003
|
if (operand.type.kind !== "f64")
|
|
5491
6004
|
lowerer.unsupported("SC1043", expr);
|
|
5492
6005
|
return { kind: "unary", op: "~", operand, type: F64, loc };
|
|
@@ -5496,7 +6009,7 @@ export function lowerPrefixUnary(lowerer, expr) {
|
|
|
5496
6009
|
// `++x` / `--x` in expression position: yields the NEW value.
|
|
5497
6010
|
return lowerIncDec(lowerer, expr, true);
|
|
5498
6011
|
}
|
|
5499
|
-
lowerer.unsupported("SC1090", expr, `syntax '${ts.
|
|
6012
|
+
lowerer.unsupported("SC1090", expr, `syntax '${ts.syntaxKindName(expr.kind)}'`);
|
|
5500
6013
|
}
|
|
5501
6014
|
/** `x++`/`x--`/`++x`/`--x` in EXPRESSION position — the incDec node:
|
|
5502
6015
|
* read, write ±1, yield old (postfix) or new (prefix) value. Receivers
|
|
@@ -5508,6 +6021,14 @@ function lowerIncDec(lowerer, expr, prefix) {
|
|
|
5508
6021
|
const loc = locOf(expr);
|
|
5509
6022
|
const op = expr.operator === ts.SyntaxKind.PlusPlusToken ? "+" : "-";
|
|
5510
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
|
+
}
|
|
5511
6032
|
// CLASS-FIELD receivers (`if (--this.limit === 0)` — countdown.js's
|
|
5512
6033
|
// dec(); `--this[kLimit]` — its symbol-keyed spelling): a single-
|
|
5513
6034
|
// evaluation read-modify-write over the instance, yielding old/new
|
|
@@ -5548,6 +6069,16 @@ function lowerIncDec(lowerer, expr, prefix) {
|
|
|
5548
6069
|
if (!target) {
|
|
5549
6070
|
lowerer.rejectUnresolved(expr.operand, `increment/decrement of '${expr.operand.text}' (not a writable local or module global)`);
|
|
5550
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
|
+
}
|
|
5551
6082
|
if (target.type.kind !== "f64")
|
|
5552
6083
|
lowerer.unsupported("SC1043", expr);
|
|
5553
6084
|
return { kind: "incDec", op, prefix, localId: target.id, type: F64, loc };
|
|
@@ -5599,7 +6130,7 @@ function lowerAnyBinaryInIsland(lowerer, expr, left, right, loc) {
|
|
|
5599
6130
|
};
|
|
5600
6131
|
const jop = JS_BIN[op];
|
|
5601
6132
|
if (jop === undefined) {
|
|
5602
|
-
lowerer.unsupported("SC1090", expr, `operator '${ts.tokenToString(op) ?? ts.
|
|
6133
|
+
lowerer.unsupported("SC1090", expr, `operator '${ts.tokenToString(op) ?? ts.syntaxKindName(op)}' on 'any' values`);
|
|
5603
6134
|
}
|
|
5604
6135
|
const type = jsOpResultKind(jop) === "bool" ? BOOL : JSVAL;
|
|
5605
6136
|
return {
|
|
@@ -5608,6 +6139,39 @@ function lowerAnyBinaryInIsland(lowerer, expr, left, right, loc) {
|
|
|
5608
6139
|
type, loc,
|
|
5609
6140
|
};
|
|
5610
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
|
+
}
|
|
5611
6175
|
export function lowerBinary(lowerer, expr) {
|
|
5612
6176
|
const loc = locOf(expr);
|
|
5613
6177
|
const op = expr.operatorToken.kind;
|
|
@@ -5615,6 +6179,15 @@ export function lowerBinary(lowerer, expr) {
|
|
|
5615
6179
|
if (cacheHas)
|
|
5616
6180
|
return cacheHas;
|
|
5617
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
|
+
}
|
|
5618
6191
|
if (ts.isPropertyAccessExpression(expr.left) || ts.isElementAccessExpression(expr.left)) {
|
|
5619
6192
|
fenceNodeModuleMutation(lowerer, expr.left, "assignment");
|
|
5620
6193
|
}
|
|
@@ -5640,8 +6213,29 @@ export function lowerBinary(lowerer, expr) {
|
|
|
5640
6213
|
}
|
|
5641
6214
|
const indexedCompound = COMPOUND_ASSIGN_OPS[op];
|
|
5642
6215
|
if (indexedCompound !== undefined && ts.isElementAccessExpression(expr.left)) {
|
|
6216
|
+
if (symbolFieldInfo(lowerer, expr.left))
|
|
6217
|
+
return lowerFieldCompoundValue(lowerer, expr.left, indexedCompound, expr.right, loc);
|
|
5643
6218
|
return lowerElementCompound(lowerer, expr, indexedCompound);
|
|
5644
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
|
+
}
|
|
5645
6239
|
// `events.defaultMaxListeners = v` — the module-property write
|
|
5646
6240
|
// Node validates (validateNumber(n, 'defaultMaxListeners', 0)):
|
|
5647
6241
|
// the value crosses into the checked-dynamic tree and the runtime ladder throws
|
|
@@ -5694,6 +6288,15 @@ export function lowerBinary(lowerer, expr) {
|
|
|
5694
6288
|
const value = lowerer.lowerExprExpecting(expr.right, target.type);
|
|
5695
6289
|
return { kind: "assignExpr", localId: target.id, value, type: target.type, loc };
|
|
5696
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
|
+
}
|
|
5697
6300
|
// `h.k = v` on an ISLAND receiver in VALUE position: the engine
|
|
5698
6301
|
// property write (setProp throws the engine's TypeErrors on
|
|
5699
6302
|
// nullish receivers, bridged catchably), the RHS value threaded
|
|
@@ -5760,6 +6363,22 @@ export function lowerBinary(lowerer, expr) {
|
|
|
5760
6363
|
loc,
|
|
5761
6364
|
};
|
|
5762
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
|
+
}
|
|
5763
6382
|
}
|
|
5764
6383
|
}
|
|
5765
6384
|
// Destructuring assignment in VALUE position (`(() => [i] = [i+1])()`,
|
|
@@ -5786,221 +6405,7 @@ export function lowerBinary(lowerer, expr) {
|
|
|
5786
6405
|
lowerer.unsupported("SC1040", expr, undefined, "object-to-primitive loose equality can call user-defined valueOf/toString methods — compare explicit primitive conversions instead");
|
|
5787
6406
|
}
|
|
5788
6407
|
if (op === ts.SyntaxKind.AmpersandAmpersandToken || op === ts.SyntaxKind.BarBarToken) {
|
|
5789
|
-
|
|
5790
|
-
const fallback = lowerAbsenceAwareOrChain(lowerer, expr);
|
|
5791
|
-
if (fallback)
|
|
5792
|
-
return fallback;
|
|
5793
|
-
}
|
|
5794
|
-
// JS value semantics: `a && b` is `toBool(a) ? b : a` — the result is
|
|
5795
|
-
// an operand value, so both operands must share one IR kind (which is
|
|
5796
|
-
// also what tsc's type says, modulo literal-union collapsing that
|
|
5797
|
-
// mapType already handles). Mixed kinds (`n && s`) stay rejected.
|
|
5798
|
-
const left = lowerAbsenceProbe(lowerer, expr.left) ?? lowerer.lowerExpr(expr.left);
|
|
5799
|
-
const right = lowerer.lowerExpr(expr.right);
|
|
5800
|
-
// A LITERAL-unit left operand (a compile-time undefined/null — the
|
|
5801
|
-
// capability-probe members: `process.features.inspector ||
|
|
5802
|
-
// !flag.startsWith('--inspect')` reads undefined in a compiled
|
|
5803
|
-
// binary): statically falsy, so the operator folds JS-exactly —
|
|
5804
|
-
// `unit || X` IS X, `unit && X` IS the unit (X's side effects never
|
|
5805
|
-
// run in JS either; the lowered right is simply dropped). Literal
|
|
5806
|
-
// units only — computed unit-typed values keep the fences below.
|
|
5807
|
-
if (left.kind === "unitLit") {
|
|
5808
|
-
return op === ts.SyntaxKind.BarBarToken ? right : left;
|
|
5809
|
-
}
|
|
5810
|
-
// JS objects and symbols are always truthy. In `ref || fallback`
|
|
5811
|
-
// the fallback is therefore unreachable and the result is the
|
|
5812
|
-
// already-evaluated left value — the browser-fallback idiom used by
|
|
5813
|
-
// packages for `process.argv || []` and `process.env || {}`. The
|
|
5814
|
-
// operand itself remains in the IR, preserving reads/calls that
|
|
5815
|
-
// produce the reference; only the unreachable right lowering drops.
|
|
5816
|
-
if (op === ts.SyntaxKind.BarBarToken && REF_TRUTHY_KINDS.has(left.type.kind)) {
|
|
5817
|
-
return left;
|
|
5818
|
-
}
|
|
5819
|
-
if (left.type.kind === "dyn" || right.type.kind === "dyn") {
|
|
5820
|
-
// A checked-dynamic operand (`fn.name || '<anonymous>'` —
|
|
5821
|
-
// test/common's _mustCallInner): both sides live in the checked-dynamic tree and
|
|
5822
|
-
// the deciding test is ToBoolean over the dyn kind
|
|
5823
|
-
// (scr_dyn_truthy) — JS value semantics exactly, result dyn. The
|
|
5824
|
-
// non-dyn side converts through the usual boundary; a value with
|
|
5825
|
-
// no dyn representation keeps the fence.
|
|
5826
|
-
const l = lowerer.coerceToExpected(left, DYN);
|
|
5827
|
-
const r = lowerer.coerceToExpected(right, DYN);
|
|
5828
|
-
if (l.type.kind !== "dyn" || r.type.kind !== "dyn") {
|
|
5829
|
-
lowerer.unsupported("SC1100", expr, "logical operators on 'unknown' values");
|
|
5830
|
-
}
|
|
5831
|
-
return {
|
|
5832
|
-
kind: "logical",
|
|
5833
|
-
op: op === ts.SyntaxKind.AmpersandAmpersandToken ? "&&" : "||",
|
|
5834
|
-
left: l,
|
|
5835
|
-
right: r,
|
|
5836
|
-
type: DYN, loc,
|
|
5837
|
-
};
|
|
5838
|
-
}
|
|
5839
|
-
if (left.type.kind === "jsval" || right.type.kind === "jsval") {
|
|
5840
|
-
return {
|
|
5841
|
-
kind: "logical",
|
|
5842
|
-
op: op === ts.SyntaxKind.AmpersandAmpersandToken ? "&&" : "||",
|
|
5843
|
-
left: lowerer.jsvalIn(left, expr.left),
|
|
5844
|
-
right: lowerer.jsvalIn(right, expr.right),
|
|
5845
|
-
type: JSVAL, loc,
|
|
5846
|
-
};
|
|
5847
|
-
}
|
|
5848
|
-
if (left.type.kind === "union" || right.type.kind === "union") {
|
|
5849
|
-
// JS value semantics over a union: the deciding test is the ARM
|
|
5850
|
-
// value's ToBoolean (a per-union helper), and the result is the
|
|
5851
|
-
// deciding operand. Supported when the checker's type of the whole
|
|
5852
|
-
// expression maps to ONE union both operands coerce into (same
|
|
5853
|
-
// union passes through; a plain arm wraps — `u || undefined`).
|
|
5854
|
-
// Everything else (`u && flag`, whose value would need a wider
|
|
5855
|
-
// re-tagged union) stays fenced — in CONDITION position those
|
|
5856
|
-
// shapes lower through lowerCondition's bool descent instead.
|
|
5857
|
-
let target = lowerer.mapTypeOf(lowerer.typeOf(expr));
|
|
5858
|
-
// A broad-JSDoc npm-static body can leave the CHECKER result `any`
|
|
5859
|
-
// even after declaration-backed specialization has recovered both
|
|
5860
|
-
// operands (`helpOption && args.find(...)` in commander). When the
|
|
5861
|
-
// left consists only of always-truthy references and falsy units,
|
|
5862
|
-
// the static result is exactly the right operand plus those units.
|
|
5863
|
-
if (target === null &&
|
|
5864
|
-
lowerer.implicitParamTypes !== null &&
|
|
5865
|
-
npmStaticPackageOfPath(expr.getSourceFile().fileName) !== null &&
|
|
5866
|
-
(lowerer.typeOf(expr).flags & ts.TypeFlags.Any) !== 0 &&
|
|
5867
|
-
left.type.kind === "union") {
|
|
5868
|
-
const leftArms = lowerer.unions.get(left.type.unionId)?.arms;
|
|
5869
|
-
const rightArms = right.type.kind === "union"
|
|
5870
|
-
? lowerer.unions.get(right.type.unionId)?.arms
|
|
5871
|
-
: [right.type];
|
|
5872
|
-
const rightIsStatic = rightArms?.every((arm) => arm.kind !== "dyn" && arm.kind !== "caught" && arm.kind !== "jsval" && arm.kind !== "void") ?? false;
|
|
5873
|
-
if (leftArms && rightArms && rightIsStatic &&
|
|
5874
|
-
leftArms.some(isUnitType) &&
|
|
5875
|
-
leftArms.every((arm) => isUnitType(arm) || REF_TRUTHY_KINDS.has(arm.kind))) {
|
|
5876
|
-
const byKey = new Map();
|
|
5877
|
-
for (const arm of [...rightArms, ...leftArms.filter(isUnitType)]) {
|
|
5878
|
-
byKey.set(typeKey(arm), arm);
|
|
5879
|
-
}
|
|
5880
|
-
const arms = [...byKey.values()].sort((a, b) => (typeKey(a) < typeKey(b) ? -1 : 1));
|
|
5881
|
-
if (arms.length > 1) {
|
|
5882
|
-
target = { kind: "union", unionId: lowerer.unions.intern(arms) };
|
|
5883
|
-
}
|
|
5884
|
-
}
|
|
5885
|
-
}
|
|
5886
|
-
if (target?.kind === "union") {
|
|
5887
|
-
// `&&` whose left does NOT fit the result union: the checker
|
|
5888
|
-
// built that result by DROPPING left arms which are always
|
|
5889
|
-
// truthy (`Option | undefined && string | undefined` answers
|
|
5890
|
-
// `string | undefined`, not `Option | string | undefined`).
|
|
5891
|
-
// Evaluate the left once, test it in its own union, and retag it
|
|
5892
|
-
// only on the falsy path. Any stranded reference arms are
|
|
5893
|
-
// unreachable there because every JS object is truthy.
|
|
5894
|
-
if (op === ts.SyntaxKind.AmpersandAmpersandToken && left.type.kind === "union" && !typeEquals(left.type, target)) {
|
|
5895
|
-
const def = lowerer.unions.get(left.type.unionId);
|
|
5896
|
-
const trappable = new Set();
|
|
5897
|
-
def?.arms.forEach((arm, tag) => {
|
|
5898
|
-
if (lowerer.armTag(target.unionId, arm) < 0 && REF_TRUTHY_KINDS.has(arm.kind)) {
|
|
5899
|
-
trappable.add(tag);
|
|
5900
|
-
}
|
|
5901
|
-
});
|
|
5902
|
-
const retag = trappable.size === 0
|
|
5903
|
-
? null
|
|
5904
|
-
: lowerer.unionRetagHelper(left.type.unionId, target.unionId, loc, trappable);
|
|
5905
|
-
if (retag) {
|
|
5906
|
-
lowerer.requireTruthyUnion(left.type.unionId, expr);
|
|
5907
|
-
const stmts = [];
|
|
5908
|
-
let stable = left;
|
|
5909
|
-
if (!isSafeToRepeat(left)) {
|
|
5910
|
-
const local = lowerer.declareHiddenLocal("%and", left.type);
|
|
5911
|
-
stmts.push({ kind: "varDecl", localId: local.id, init: left, loc });
|
|
5912
|
-
stable = varRef(local.id, left.type, loc);
|
|
5913
|
-
}
|
|
5914
|
-
const result = {
|
|
5915
|
-
kind: "ternary",
|
|
5916
|
-
cond: lowerer.ensureBool(stable, expr.left),
|
|
5917
|
-
then: lowerer.coerceInto(expr.right, right, target),
|
|
5918
|
-
else_: { kind: "call", callee: retag, args: [stable], type: target, loc },
|
|
5919
|
-
type: target,
|
|
5920
|
-
loc,
|
|
5921
|
-
};
|
|
5922
|
-
return stmts.length === 0 ? result : { kind: "seqExpr", stmts, result, type: target, loc };
|
|
5923
|
-
}
|
|
5924
|
-
}
|
|
5925
|
-
// `||` whose left does NOT fit the result union: the checker built
|
|
5926
|
-
// that result by DROPPING the left's falsy arms (`process.env.X ||
|
|
5927
|
-
// null` is `string | null`, `|| 3000` is `string | number` — the
|
|
5928
|
-
// `undefined` is gone from both), so coercing the left eagerly, as
|
|
5929
|
-
// the shared shape below does, retags an arm the test is about to
|
|
5930
|
-
// rule out and throws where Node yields the default. Single-eval
|
|
5931
|
-
// instead: test the left in its OWN union and retag only on the
|
|
5932
|
-
// truthy side, where the dropped arms are unreachable.
|
|
5933
|
-
if (op === ts.SyntaxKind.BarBarToken && left.type.kind === "union" && !typeEquals(left.type, target)) {
|
|
5934
|
-
const retag = lowerer.unionRetagHelper(left.type.unionId, target.unionId, loc);
|
|
5935
|
-
if (retag) {
|
|
5936
|
-
lowerer.requireTruthyUnion(left.type.unionId, expr);
|
|
5937
|
-
return {
|
|
5938
|
-
kind: "orDefault",
|
|
5939
|
-
left,
|
|
5940
|
-
right: lowerer.coerceInto(expr.right, right, target),
|
|
5941
|
-
retag,
|
|
5942
|
-
type: target,
|
|
5943
|
-
loc,
|
|
5944
|
-
};
|
|
5945
|
-
}
|
|
5946
|
-
}
|
|
5947
|
-
lowerer.requireTruthyUnion(target.unionId, expr);
|
|
5948
|
-
return {
|
|
5949
|
-
kind: "logical",
|
|
5950
|
-
op: op === ts.SyntaxKind.AmpersandAmpersandToken ? "&&" : "||",
|
|
5951
|
-
left: lowerer.coerceInto(expr.left, left, target),
|
|
5952
|
-
right: lowerer.coerceInto(expr.right, right, target),
|
|
5953
|
-
type: target, loc,
|
|
5954
|
-
};
|
|
5955
|
-
}
|
|
5956
|
-
// `u || d` NARROWED by the default: the checker types the result
|
|
5957
|
-
// as u's single non-unit arm (`marker() || "default"`) — evaluate
|
|
5958
|
-
// u once, truthy extracts the arm, falsy takes d lazily (the
|
|
5959
|
-
// orDefault node, nullish's truthiness sibling). An UNMAPPABLE
|
|
5960
|
-
// checker result takes the same rule (`options.runner ||
|
|
5961
|
-
// defaultRunner` — tsc's union of two structurally-compatible
|
|
5962
|
-
// function types has no representation, while the left's one
|
|
5963
|
-
// non-unit arm is the only representable answer): the default
|
|
5964
|
-
// must coerce into the arm or fence on its own.
|
|
5965
|
-
if (op === ts.SyntaxKind.BarBarToken &&
|
|
5966
|
-
left.type.kind === "union") {
|
|
5967
|
-
const def = lowerer.unions.get(left.type.unionId);
|
|
5968
|
-
const rest = def ? def.arms.filter((a) => !isUnitType(a)) : [];
|
|
5969
|
-
const funcArmDefault = rest[0]?.kind === "func" && (target === null || target.kind === "func");
|
|
5970
|
-
if (rest.length === 1 && ((target !== null && typeEquals(target, rest[0])) || funcArmDefault)) {
|
|
5971
|
-
lowerer.requireTruthyUnion(left.type.unionId, expr);
|
|
5972
|
-
const dflt = lowerer.lowerExprExpecting(expr.right, rest[0]);
|
|
5973
|
-
return { kind: "orDefault", left, right: dflt, type: rest[0], loc };
|
|
5974
|
-
}
|
|
5975
|
-
}
|
|
5976
|
-
lowerer.unsupported("SC1090", expr, `logical operators on union-typed values outside conditions (${NARROW_FIRST})`);
|
|
5977
|
-
}
|
|
5978
|
-
const kind = left.type.kind;
|
|
5979
|
-
if (kind !== right.type.kind ||
|
|
5980
|
-
(kind !== "f64" && kind !== "string" && kind !== "bool")) {
|
|
5981
|
-
// Mixed PLAIN operands (`value || null`, `flag || undefined`,
|
|
5982
|
-
// `s || 0`): JS value semantics still compile when the checker
|
|
5983
|
-
// types the RESULT as one union both operands coerce into — the
|
|
5984
|
-
// same lift the union-operand path above takes, arriving here
|
|
5985
|
-
// with two plain arm values instead.
|
|
5986
|
-
const target = lowerer.mapTypeOf(lowerer.typeOf(expr));
|
|
5987
|
-
if (target?.kind === "union") {
|
|
5988
|
-
lowerer.requireTruthyUnion(target.unionId, expr);
|
|
5989
|
-
return {
|
|
5990
|
-
kind: "logical",
|
|
5991
|
-
op: op === ts.SyntaxKind.AmpersandAmpersandToken ? "&&" : "||",
|
|
5992
|
-
left: lowerer.coerceInto(expr.left, left, target),
|
|
5993
|
-
right: lowerer.coerceInto(expr.right, right, target),
|
|
5994
|
-
type: target, loc,
|
|
5995
|
-
};
|
|
5996
|
-
}
|
|
5997
|
-
lowerer.unsupported("SC1042", expr);
|
|
5998
|
-
}
|
|
5999
|
-
return {
|
|
6000
|
-
kind: "logical",
|
|
6001
|
-
op: op === ts.SyntaxKind.AmpersandAmpersandToken ? "&&" : "||",
|
|
6002
|
-
left, right, type: left.type, loc,
|
|
6003
|
-
};
|
|
6408
|
+
return lowerLogicalChain(lowerer, expr);
|
|
6004
6409
|
}
|
|
6005
6410
|
if (op === ts.SyntaxKind.QuestionQuestionToken) {
|
|
6006
6411
|
return lowerer.lowerNullishCoalesce(expr, loc);
|
|
@@ -6011,7 +6416,7 @@ export function lowerBinary(lowerer, expr) {
|
|
|
6011
6416
|
// runs for EFFECT exactly as its statement lowering (JS discards its
|
|
6012
6417
|
// value), the right operand is the expression's value — a seqExpr.
|
|
6013
6418
|
// The validator restricts seqExpr statements to straight-line writes
|
|
6014
|
-
// (the
|
|
6419
|
+
// (the lowering point is mid-expression), so a left operand whose
|
|
6015
6420
|
// statement lowering needs real control flow keeps a pointed fence
|
|
6016
6421
|
// instead of tripping the validator downstream.
|
|
6017
6422
|
const effect = lowerer.lowerExprStatement(expr.left);
|
|
@@ -6052,7 +6457,7 @@ export function lowerBinary(lowerer, expr) {
|
|
|
6052
6457
|
op === ts.SyntaxKind.ExclamationEqualsEqualsToken) {
|
|
6053
6458
|
const negated = op === ts.SyntaxKind.ExclamationEqualsEqualsToken;
|
|
6054
6459
|
const unit = left.kind === "unitLit" ? left : right.kind === "unitLit" ? right : null;
|
|
6055
|
-
const other =
|
|
6460
|
+
const other = left.kind === "unitLit" ? right : left;
|
|
6056
6461
|
if (unit && other.type.kind === "dyn") {
|
|
6057
6462
|
return {
|
|
6058
6463
|
kind: "dynTest",
|
|
@@ -6069,12 +6474,30 @@ export function lowerBinary(lowerer, expr) {
|
|
|
6069
6474
|
// answers false). dyn vs dyn keeps the fence below.
|
|
6070
6475
|
const dynSide = left.type.kind === "dyn" ? left : right;
|
|
6071
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
|
+
}
|
|
6072
6495
|
if (dynSide.type.kind === "dyn" &&
|
|
6073
6496
|
(scalarSide.type.kind === "f64" || scalarSide.type.kind === "string" || scalarSide.type.kind === "bool" ||
|
|
6074
6497
|
// dyn vs dyn (`context.actual !== context.exact` —
|
|
6075
6498
|
// test/common's exit accounting): the runtime's whole-dyn
|
|
6076
6499
|
// strict equality — scalars by value, units by kind,
|
|
6077
|
-
// reference kinds by
|
|
6500
|
+
// reference kinds by identity (scr_dyn_strict_eq).
|
|
6078
6501
|
scalarSide.type.kind === "dyn")) {
|
|
6079
6502
|
return {
|
|
6080
6503
|
kind: "dynScalarEq",
|
|
@@ -6086,15 +6509,22 @@ export function lowerBinary(lowerer, expr) {
|
|
|
6086
6509
|
};
|
|
6087
6510
|
}
|
|
6088
6511
|
}
|
|
6089
|
-
|
|
6090
|
-
|
|
6091
|
-
|
|
6092
|
-
|
|
6093
|
-
|
|
6094
|
-
|
|
6095
|
-
|
|
6096
|
-
|
|
6097
|
-
|
|
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) {
|
|
6098
6528
|
const checkNum = (e) => e.type.kind === "dyn" ? { kind: "dynCheck", value: e, type: F64, loc: e.loc } : e;
|
|
6099
6529
|
const other = left.type.kind === "dyn" ? right : left;
|
|
6100
6530
|
const NUM_BIN = {
|
|
@@ -6112,26 +6542,29 @@ export function lowerBinary(lowerer, expr) {
|
|
|
6112
6542
|
};
|
|
6113
6543
|
const arith = NUM_BIN[op];
|
|
6114
6544
|
const cmp = NUM_CMP[op];
|
|
6115
|
-
if (
|
|
6116
|
-
const l =
|
|
6117
|
-
const r =
|
|
6118
|
-
if (
|
|
6119
|
-
|
|
6120
|
-
|
|
6121
|
-
|
|
6122
|
-
|
|
6123
|
-
|
|
6124
|
-
if (op === ts.SyntaxKind.PlusToken) {
|
|
6125
|
-
if (other.type.kind === "f64" || other.type.kind === "dyn") {
|
|
6126
|
-
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;
|
|
6127
6554
|
}
|
|
6128
|
-
|
|
6129
|
-
|
|
6130
|
-
|
|
6131
|
-
|
|
6132
|
-
|
|
6133
|
-
|
|
6134
|
-
|
|
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;
|
|
6135
6568
|
}
|
|
6136
6569
|
}
|
|
6137
6570
|
}
|
|
@@ -6150,7 +6583,7 @@ export function lowerBinary(lowerer, expr) {
|
|
|
6150
6583
|
// retain the SC2011 promise that --dynamic lifts this site.
|
|
6151
6584
|
if (lowerer.dynamic)
|
|
6152
6585
|
return lowerAnyBinaryInIsland(lowerer, expr, left, right, loc);
|
|
6153
|
-
lowerer.anyOpFence(`the '${ts.tokenToString(op) ?? ts.
|
|
6586
|
+
lowerer.anyOpFence(`the '${ts.tokenToString(op) ?? ts.syntaxKindName(op)}' operator`, expr);
|
|
6154
6587
|
}
|
|
6155
6588
|
// tsc allows ===/!== on unknown (arithmetic/comparisons it rejects
|
|
6156
6589
|
// itself); a dynamic equality would need a dyn walk — validate first.
|
|
@@ -6353,6 +6786,14 @@ export function lowerBinary(lowerer, expr) {
|
|
|
6353
6786
|
const optionalStringPlus = lowerOptionalStringPlus();
|
|
6354
6787
|
if (optionalStringPlus)
|
|
6355
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
|
+
}
|
|
6356
6797
|
lowerer.unsupported("SC1043", expr);
|
|
6357
6798
|
break;
|
|
6358
6799
|
case ts.SyntaxKind.MinusToken:
|
|
@@ -6395,8 +6836,11 @@ export function lowerBinary(lowerer, expr) {
|
|
|
6395
6836
|
return indexed;
|
|
6396
6837
|
if (plainBothNum)
|
|
6397
6838
|
return { kind: "bin", op: negated ? "!==" : "===", left, right, type: BOOL, loc };
|
|
6398
|
-
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 };
|
|
6399
6842
|
return { kind: "strEq", negated, left, right, type: BOOL, loc };
|
|
6843
|
+
}
|
|
6400
6844
|
// bool === bool: a plain value compare (the config-drift checks'
|
|
6401
6845
|
// `desired.lanMode !== actual.lanMode` shape).
|
|
6402
6846
|
if (left.type.kind === "bool" && right.type.kind === "bool") {
|
|
@@ -6520,6 +6964,8 @@ export function lowerBinary(lowerer, expr) {
|
|
|
6520
6964
|
if ((idLeft.type.kind === "array" ||
|
|
6521
6965
|
idLeft.type.kind === "map" ||
|
|
6522
6966
|
idLeft.type.kind === "set" ||
|
|
6967
|
+
idLeft.type.kind === "regex" ||
|
|
6968
|
+
idLeft.type.kind === "url" ||
|
|
6523
6969
|
idLeft.type.kind === "object" ||
|
|
6524
6970
|
idLeft.type.kind === "record" ||
|
|
6525
6971
|
// Symbols ARE identity: `Symbol('a') === Symbol('a')` is false,
|
|
@@ -6560,21 +7006,283 @@ export function lowerBinary(lowerer, expr) {
|
|
|
6560
7006
|
default:
|
|
6561
7007
|
break;
|
|
6562
7008
|
}
|
|
6563
|
-
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
|
+
};
|
|
6564
7273
|
}
|
|
6565
7274
|
function lowerAbsenceAwareOrChain(lowerer, expr) {
|
|
6566
7275
|
const nodes = [];
|
|
6567
|
-
const
|
|
6568
|
-
|
|
6569
|
-
|
|
6570
|
-
|
|
6571
|
-
|
|
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);
|
|
6572
7281
|
}
|
|
6573
7282
|
else {
|
|
6574
7283
|
nodes.push(node);
|
|
6575
7284
|
}
|
|
6576
|
-
}
|
|
6577
|
-
collect(expr);
|
|
7285
|
+
}
|
|
6578
7286
|
if (nodes.length < 3)
|
|
6579
7287
|
return null;
|
|
6580
7288
|
const lowered = nodes.map((node) => lowerAbsenceProbe(lowerer, node) ?? lowerer.lowerExpr(node));
|
|
@@ -6645,10 +7353,7 @@ function errorRootedObjectOf(lowerer, t) {
|
|
|
6645
7353
|
* shape, so the sibling typeof lowerings keep trying. */
|
|
6646
7354
|
function lowerErrorCodeTypeofTest(lowerer, expr, loc) {
|
|
6647
7355
|
const negated = expr.operatorToken.kind === ts.SyntaxKind.ExclamationEqualsEqualsToken;
|
|
6648
|
-
for (const [a, b] of
|
|
6649
|
-
[expr.left, expr.right],
|
|
6650
|
-
[expr.right, expr.left],
|
|
6651
|
-
]) {
|
|
7356
|
+
for (const [a, b] of operandPairs(expr)) {
|
|
6652
7357
|
if (!ts.isTypeOfExpression(a))
|
|
6653
7358
|
continue;
|
|
6654
7359
|
let prop = a.expression;
|
|
@@ -6708,10 +7413,7 @@ function lowerErrorCodeTypeofTest(lowerer, expr, loc) {
|
|
|
6708
7413
|
}
|
|
6709
7414
|
export function lowerCaughtTypeofTest(lowerer, expr, loc) {
|
|
6710
7415
|
const negated = expr.operatorToken.kind === ts.SyntaxKind.ExclamationEqualsEqualsToken;
|
|
6711
|
-
for (const [a, b] of
|
|
6712
|
-
[expr.left, expr.right],
|
|
6713
|
-
[expr.right, expr.left],
|
|
6714
|
-
]) {
|
|
7416
|
+
for (const [a, b] of operandPairs(expr)) {
|
|
6715
7417
|
if (!ts.isTypeOfExpression(a))
|
|
6716
7418
|
continue;
|
|
6717
7419
|
const local = lowerer.caughtLocalOf(a.expression);
|
|
@@ -6834,12 +7536,13 @@ export function aliasTypeofNarrows(lowerer, cond, polarity) {
|
|
|
6834
7536
|
walk(cond, polarity);
|
|
6835
7537
|
return out;
|
|
6836
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
|
+
}
|
|
6837
7543
|
/** One ===/!== leaf of aliasTypeofNarrows — the qualification battery. */
|
|
6838
7544
|
function aliasTypeofArmOf(lowerer, e, strip) {
|
|
6839
|
-
for (const [a0, b] of
|
|
6840
|
-
[e.left, e.right],
|
|
6841
|
-
[e.right, e.left],
|
|
6842
|
-
]) {
|
|
7545
|
+
for (const [a0, b] of operandPairs(e)) {
|
|
6843
7546
|
if (!ts.isStringLiteral(b))
|
|
6844
7547
|
continue;
|
|
6845
7548
|
const a = strip(a0);
|
|
@@ -6868,7 +7571,7 @@ function aliasTypeofArmOf(lowerer, e, strip) {
|
|
|
6868
7571
|
if (!bindingNeverReassigned(lowerer, valSym, valDecl))
|
|
6869
7572
|
continue;
|
|
6870
7573
|
const fn = valDecl.parent;
|
|
6871
|
-
if (!(e.pos >= fn.pos && e.end <= fn.end))
|
|
7574
|
+
if (fn === undefined || !(e.pos >= fn.pos && e.end <= fn.end))
|
|
6872
7575
|
continue;
|
|
6873
7576
|
const valT = lowerer.typeOf(opnd); // override-aware: implicit bindings compose
|
|
6874
7577
|
const mapped = lowerer.mapTypeOf(valT);
|
|
@@ -6910,10 +7613,7 @@ function aliasTypeofArmOf(lowerer, e, strip) {
|
|
|
6910
7613
|
* bare-typeof value form then composes with strEq for pure operands). */
|
|
6911
7614
|
function lowerUnionTypeofTest(lowerer, expr, loc) {
|
|
6912
7615
|
const negated = expr.operatorToken.kind === ts.SyntaxKind.ExclamationEqualsEqualsToken;
|
|
6913
|
-
for (const [a, b] of
|
|
6914
|
-
[expr.left, expr.right],
|
|
6915
|
-
[expr.right, expr.left],
|
|
6916
|
-
]) {
|
|
7616
|
+
for (const [a, b] of operandPairs(expr)) {
|
|
6917
7617
|
if (!ts.isTypeOfExpression(a))
|
|
6918
7618
|
continue;
|
|
6919
7619
|
if (lowerer.mapTypeOf(lowerer.typeOf(a.expression))?.kind !== "union")
|
|
@@ -6961,12 +7661,13 @@ function lowerUnionTypeofTest(lowerer, expr, loc) {
|
|
|
6961
7661
|
* side is a typeof over a dyn-typed operand (not this pattern). */
|
|
6962
7662
|
function lowerDynTypeofTest(lowerer, expr, loc) {
|
|
6963
7663
|
const negated = expr.operatorToken.kind === ts.SyntaxKind.ExclamationEqualsEqualsToken;
|
|
6964
|
-
for (const [a, b] of
|
|
6965
|
-
[expr.left, expr.right],
|
|
6966
|
-
[expr.right, expr.left],
|
|
6967
|
-
]) {
|
|
7664
|
+
for (const [a, b] of operandPairs(expr)) {
|
|
6968
7665
|
if (!ts.isTypeOfExpression(a))
|
|
6969
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;
|
|
6970
7671
|
const declared = lowerer.mapTypeOf(lowerer.typeOf(a.expression));
|
|
6971
7672
|
let value;
|
|
6972
7673
|
if (declared?.kind === "dyn") {
|
|
@@ -7024,10 +7725,7 @@ function lowerDynTypeofTest(lowerer, expr, loc) {
|
|
|
7024
7725
|
value = probed;
|
|
7025
7726
|
}
|
|
7026
7727
|
}
|
|
7027
|
-
if (
|
|
7028
|
-
lowerer.unsupported("SC1090", expr, "'typeof' tests on 'unknown' values against non-literal strings");
|
|
7029
|
-
}
|
|
7030
|
-
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") {
|
|
7031
7729
|
return {
|
|
7032
7730
|
kind: "dynTest",
|
|
7033
7731
|
test: b.text,
|
|
@@ -7064,14 +7762,8 @@ function lowerDynTypeofTest(lowerer, expr, loc) {
|
|
|
7064
7762
|
loc,
|
|
7065
7763
|
};
|
|
7066
7764
|
}
|
|
7067
|
-
//
|
|
7068
|
-
|
|
7069
|
-
// + bytes + Error + functions), so the test's answer is a
|
|
7070
|
-
// compile-time constant: false for ===, true for !==. The capability
|
|
7071
|
-
// probes this settles (`typeof ms === 'bigint'` in dual-mode number
|
|
7072
|
-
// helpers) then FOLD their impossible arm (lowerTernary), which is
|
|
7073
|
-
// what lets the reachable arm compile statically.
|
|
7074
|
-
if (b.text === "bigint" || b.text === "symbol") {
|
|
7765
|
+
// Native checked values do not yet carry symbols.
|
|
7766
|
+
if (b.text === "symbol") {
|
|
7075
7767
|
return { kind: "boolLit", value: negated, type: BOOL, loc };
|
|
7076
7768
|
}
|
|
7077
7769
|
lowerer.unsupported("SC1090", b, `'typeof' tests against "${b.text}" on 'unknown' values (only "string"/"number"/` +
|
|
@@ -7093,7 +7785,7 @@ function lowerReplacedExportsRead(lowerer, expr) {
|
|
|
7093
7785
|
if (!ts.isIdentifier(recv) || recv.text !== "exports")
|
|
7094
7786
|
return null;
|
|
7095
7787
|
const sf = expr.getSourceFile();
|
|
7096
|
-
if (!isJsSourceFile(sf) || isNodeEsmFile(sf))
|
|
7788
|
+
if (!isJsSourceFile(sf) || isNodeEsmFile(sf, lowerer.program))
|
|
7097
7789
|
return null;
|
|
7098
7790
|
if (lowerer.peekLocal(recv) || lowerer.globalOf(recv))
|
|
7099
7791
|
return null; // a user binding shadows
|
|
@@ -7270,6 +7962,34 @@ export function lowerInstanceOf(lowerer, expr, loc) {
|
|
|
7270
7962
|
const right = lowerer.lowerExpr(expr.right);
|
|
7271
7963
|
return { kind: "jsOp", op: "instanceOf", args: [left, right], type: BOOL, loc };
|
|
7272
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
|
+
}
|
|
7273
7993
|
const rhsSymbol = ts.isIdentifier(expr.right) ? lowerer.resolveValueSymbol(expr.right) : null;
|
|
7274
7994
|
// `x instanceof events.EventEmitter` — the namespace-member spelling
|
|
7275
7995
|
// resolves to the same ambient class as the named import.
|
|
@@ -7303,29 +8023,41 @@ export function lowerInstanceOf(lowerer, expr, loc) {
|
|
|
7303
8023
|
// decoration result — fall to the class-VALUE path below, whose
|
|
7304
8024
|
// instanceOfValue reads the interval off the bound class object.
|
|
7305
8025
|
const directRhs = rhsSymbol ? lowerer.classBySymbol.get(rhsSymbol) : undefined;
|
|
7306
|
-
const target = (directRhs?.classDecorators?.valueGlobalId !== undefined ? undefined : directRhs) ??
|
|
8026
|
+
const target = (directRhs?.localClass || directRhs?.classDecorators?.valueGlobalId !== undefined ? undefined : directRhs) ??
|
|
7307
8027
|
rhsNsClass ??
|
|
7308
8028
|
lowerer.builtinErrorInfoOf(rhsSymbol) ??
|
|
8029
|
+
lowerer.builtinErrorInfoOf(rhsMemberSymbol) ??
|
|
7309
8030
|
lowerer.builtinEmitterInfoOf(rhsSymbol) ??
|
|
7310
8031
|
lowerer.builtinEmitterInfoOf(rhsMemberSymbol) ??
|
|
7311
8032
|
lowerer.builtinStreamInfoOf(rhsSymbol) ??
|
|
7312
8033
|
lowerer.builtinStreamInfoOf(rhsMemberSymbol) ??
|
|
7313
8034
|
undefined;
|
|
7314
8035
|
if (!target) {
|
|
7315
|
-
|
|
7316
|
-
|
|
7317
|
-
|
|
7318
|
-
|
|
7319
|
-
|
|
7320
|
-
|
|
7321
|
-
|
|
7322
|
-
|
|
7323
|
-
|
|
7324
|
-
|
|
7325
|
-
|
|
7326
|
-
|
|
7327
|
-
|
|
7328
|
-
|
|
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
|
+
}
|
|
7329
8061
|
}
|
|
7330
8062
|
}
|
|
7331
8063
|
// `x instanceof RegExp` over a union with a regex arm (the
|
|
@@ -7338,6 +8070,8 @@ export function lowerInstanceOf(lowerer, expr, loc) {
|
|
|
7338
8070
|
lowerer.isStdlibGlobal(expr.right, "RegExp") &&
|
|
7339
8071
|
!lowerer.caughtLocalOf(expr.left)) {
|
|
7340
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 };
|
|
7341
8075
|
if (left.type.kind === "union") {
|
|
7342
8076
|
const def = lowerer.unions.get(left.type.unionId);
|
|
7343
8077
|
const tag = def ? def.arms.findIndex((a) => a.kind === "regex") : -1;
|
|
@@ -7355,7 +8089,7 @@ export function lowerInstanceOf(lowerer, expr, loc) {
|
|
|
7355
8089
|
// STANDALONE target class has exactly one possible runtime value
|
|
7356
8090
|
// (itself — no descendants can flow into the slot), so the answer
|
|
7357
8091
|
// folds statically exactly like the named-target folds below.
|
|
7358
|
-
const rhsClassval =
|
|
8092
|
+
const rhsClassval = storedClassValueType(lowerer, expr.right);
|
|
7359
8093
|
if (rhsClassval?.kind === "classval" && !lowerer.caughtLocalOf(expr.left)) {
|
|
7360
8094
|
const targetInfo = lowerer.classes.get(rhsClassval.className);
|
|
7361
8095
|
if (!targetInfo) {
|
|
@@ -7372,7 +8106,8 @@ export function lowerInstanceOf(lowerer, expr, loc) {
|
|
|
7372
8106
|
const lhsInfo = lowerer.classes.get(left.type.className);
|
|
7373
8107
|
if (!lhsInfo)
|
|
7374
8108
|
throw new InternalCompilerError(`lowerer bug: unknown class ${left.type.className}`);
|
|
7375
|
-
if (lowerer.inHierarchy(targetInfo) && lowerer.inHierarchy(lhsInfo))
|
|
8109
|
+
if ((lowerer.inHierarchy(targetInfo) && lowerer.inHierarchy(lhsInfo)) ||
|
|
8110
|
+
(targetInfo.localClass !== undefined && lhsInfo.localClass !== undefined)) {
|
|
7376
8111
|
const classValue = lowerer.lowerExpr(expr.right);
|
|
7377
8112
|
if (classValue.type.kind !== "classval")
|
|
7378
8113
|
lowerer.badType(expr.right, lowerer.typeOf(expr.right));
|
|
@@ -7419,6 +8154,9 @@ export function lowerInstanceOf(lowerer, expr, loc) {
|
|
|
7419
8154
|
// (name strings are user-writable), so `u instanceof TypeError` keeps
|
|
7420
8155
|
// the fence. Reads past the narrow bridge through maybeNarrow's
|
|
7421
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
|
+
}
|
|
7422
8160
|
if (left.type.kind === "dyn" && target.def.name === "%Error") {
|
|
7423
8161
|
return { kind: "dynTest", test: "error", value: left, type: BOOL, loc };
|
|
7424
8162
|
}
|
|
@@ -7441,6 +8179,44 @@ export function lowerInstanceOf(lowerer, expr, loc) {
|
|
|
7441
8179
|
if (left.type.kind === "dyn") {
|
|
7442
8180
|
lowerer.unsupported("SC1090", expr, `'instanceof ${target.def.name.replace(/^%/, "")}' on 'unknown' values (only the Error classes answer — test 'instanceof Error' and read '.name')`);
|
|
7443
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
|
+
}
|
|
7444
8220
|
if (left.type.kind !== "object") {
|
|
7445
8221
|
lowerer.unsupported("SC1090", expr, "'instanceof' on values other than class instances (narrow union-typed values first)");
|
|
7446
8222
|
}
|
|
@@ -7613,6 +8389,12 @@ function lowerPrivateIn(lowerer, expr, priv, loc) {
|
|
|
7613
8389
|
* and dyn/unknown stay fenced. Keys are literal strings — a computed key
|
|
7614
8390
|
* over a shape would need the runtime key table. */
|
|
7615
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 };
|
|
7616
8398
|
// `#name in obj` — the ergonomic brand check (ES2022) — resolves
|
|
7617
8399
|
// before any string-key machinery: the left operand is a private
|
|
7618
8400
|
// NAME, not a value.
|
|
@@ -7664,14 +8446,13 @@ function lowerInExpression(lowerer, expr, loc) {
|
|
|
7664
8446
|
// `in` shares the coercion).
|
|
7665
8447
|
{
|
|
7666
8448
|
const probed = tryLowerExpression(lowerer, expr.right);
|
|
7667
|
-
if (probed
|
|
7668
|
-
|
|
7669
|
-
if (k.type.kind === "
|
|
7670
|
-
|
|
7671
|
-
const
|
|
7672
|
-
|
|
7673
|
-
|
|
7674
|
-
}
|
|
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 };
|
|
7675
8456
|
}
|
|
7676
8457
|
}
|
|
7677
8458
|
}
|
|
@@ -7697,7 +8478,16 @@ function lowerInExpression(lowerer, expr, loc) {
|
|
|
7697
8478
|
};
|
|
7698
8479
|
return { kind: "unionIsTag", unionId: envType.unionId, tag: undefTag, negated: true, value: read, type: BOOL, loc };
|
|
7699
8480
|
}
|
|
7700
|
-
|
|
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
|
+
}
|
|
7701
8491
|
// Error-rooted receivers (builtin or user subclass — the isErrnoException
|
|
7702
8492
|
// predicate's `err instanceof Error && "code" in err` shape): `code`
|
|
7703
8493
|
// answers from the runtime error's code slot (stamped by fs/system/
|
|
@@ -7722,6 +8512,9 @@ function lowerInExpression(lowerer, expr, loc) {
|
|
|
7722
8512
|
while (info && info.base)
|
|
7723
8513
|
info = info.base;
|
|
7724
8514
|
if (info?.def.name === "%Error") {
|
|
8515
|
+
if (key === "cause") {
|
|
8516
|
+
return { kind: "libCall", fn: "error.hasCause", args: [recv], type: BOOL, loc };
|
|
8517
|
+
}
|
|
7725
8518
|
if (key === "code") {
|
|
7726
8519
|
const codeType = lowerer.envValueType();
|
|
7727
8520
|
if (codeType.kind !== "union")
|
|
@@ -8366,12 +9159,24 @@ export function lowerGroupsProjection(lowerer, recv, groups, loc) {
|
|
|
8366
9159
|
}
|
|
8367
9160
|
return { kind: "call", callee: helper, args: [recv], type: recordT, loc };
|
|
8368
9161
|
}
|
|
8369
|
-
/** Field
|
|
8370
|
-
|
|
8371
|
-
* on classes are rejected specifically; func-typed record fields are
|
|
8372
|
-
* ordinary closure values, so bare references to them work (unlike class
|
|
8373
|
-
* 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. */
|
|
8374
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
|
+
}
|
|
8375
9180
|
const target = lowerer.fieldTarget(expr);
|
|
8376
9181
|
if (target)
|
|
8377
9182
|
return lowerer.fieldGetExpr(target, locOf(expr), expr);
|
|
@@ -8388,7 +9193,9 @@ export function lowerFieldRead(lowerer, expr) {
|
|
|
8388
9193
|
if (receiverIr?.kind === "object" &&
|
|
8389
9194
|
(lowerer.findMethodOn(lowerer.classes.get(receiverIr.className) ?? null, expr.name.text) ||
|
|
8390
9195
|
findGenericMethodOn(lowerer, lowerer.classes.get(receiverIr.className) ?? null, expr.name.text))) {
|
|
8391
|
-
|
|
9196
|
+
const info = lowerer.classes.get(receiverIr.className);
|
|
9197
|
+
if (info)
|
|
9198
|
+
return lowerClassMethodValue(lowerer, expr, info);
|
|
8392
9199
|
}
|
|
8393
9200
|
// An object-literal GENERIC method as a VALUE (`o.m` — the member is
|
|
8394
9201
|
// excluded from the record shape): the pinned-value rule verbatim when
|
|
@@ -8448,16 +9255,22 @@ export function lowerFieldRead(lowerer, expr) {
|
|
|
8448
9255
|
* and `switch (r.kind)` work without dedicated test nodes. Anything else
|
|
8449
9256
|
* on a union receiver is rejected specifically (narrow first). */
|
|
8450
9257
|
export function lowerUnionProperty(lowerer, expr) {
|
|
8451
|
-
if (expr
|
|
9258
|
+
if (lowerer.chainBlocked(expr))
|
|
8452
9259
|
return null;
|
|
8453
9260
|
const receiverIr = lowerer.mapTypeOf(lowerer.typeOf(expr.expression));
|
|
8454
9261
|
if (receiverIr?.kind !== "union")
|
|
8455
9262
|
return null;
|
|
8456
|
-
// Lower the receiver
|
|
8457
|
-
//
|
|
8458
|
-
|
|
8459
|
-
//
|
|
8460
|
-
|
|
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
|
+
}
|
|
8461
9274
|
// A checker-union receiver whose VALUE lowered to a plain RECORD (the
|
|
8462
9275
|
// merged-signature fiction — `runner(cmd, args)` where runner joined
|
|
8463
9276
|
// a structural runner type with spawnSync's, and the local adopted
|
|
@@ -8553,20 +9366,29 @@ export function lowerUnionProperty(lowerer, expr) {
|
|
|
8553
9366
|
const keyed = lowerUnionKeyedRead(lowerer, expr, value.type.unionId, value, key, field);
|
|
8554
9367
|
if (keyed)
|
|
8555
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
|
+
}
|
|
8556
9377
|
lowerer.unsupported("SC1090", expr, `reading '${field}' on a union-typed value (every arm must be an object/record ` +
|
|
8557
9378
|
`with a same-typed field '${field}'; ` +
|
|
8558
9379
|
`${NARROW_FIRST})`);
|
|
8559
9380
|
}
|
|
8560
9381
|
/** The unionDisc generalization: a keyed read on a union receiver whose
|
|
8561
9382
|
* arms answer DIFFERENT (but joinable) types. Each arm contributes its
|
|
8562
|
-
* 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
|
|
8563
9384
|
* index-signature arm's value type (plus its declared fields' types for
|
|
8564
9385
|
* runtime keys, which reach them through the keyed-read helper), and
|
|
8565
9386
|
* UNDEFINED for unit arms (reachable only through optional-chain tails,
|
|
8566
9387
|
* where JS short-circuits to undefined; a unit arm the checker narrowed
|
|
8567
9388
|
* away is simply unreachable). The result type is the JOIN of those
|
|
8568
|
-
* answers; every arm's answer must be the join itself
|
|
8569
|
-
* 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.
|
|
8570
9392
|
* Returns null when any arm cannot answer — the caller owns the fence
|
|
8571
9393
|
* message. The caller (the property/element dispatch) maybeNarrows. */
|
|
8572
9394
|
function lowerUnionKeyedRead(lowerer, expr, unionId, value, key, literalField) {
|
|
@@ -8602,6 +9424,11 @@ function lowerUnionKeyedRead(lowerer, expr, unionId, value, key, literalField) {
|
|
|
8602
9424
|
push(UNDEFINED_T);
|
|
8603
9425
|
continue;
|
|
8604
9426
|
}
|
|
9427
|
+
if (key.type.kind === "f64") {
|
|
9428
|
+
if (arm.kind !== "array" || !pushAnswer(arm.elem))
|
|
9429
|
+
return null;
|
|
9430
|
+
continue;
|
|
9431
|
+
}
|
|
8605
9432
|
if (arm.kind !== "record")
|
|
8606
9433
|
return null;
|
|
8607
9434
|
const shape = lowerer.shapes.get(arm.shapeId);
|
|
@@ -8631,13 +9458,11 @@ function lowerUnionKeyedRead(lowerer, expr, unionId, value, key, literalField) {
|
|
|
8631
9458
|
if (joinArms.length === 0)
|
|
8632
9459
|
return null;
|
|
8633
9460
|
// The join must be a BUILDABLE union: arm kinds the union invariants
|
|
8634
|
-
// admit (no
|
|
8635
|
-
//
|
|
8636
|
-
// join mixing, say, a func answer with data answers has no union to
|
|
8637
|
-
// 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.
|
|
8638
9463
|
if (joinArms.length > 1 &&
|
|
8639
|
-
(!
|
|
8640
|
-
joinArms.some((a) => a.kind === "
|
|
9464
|
+
(!unionContainerArmsOk(joinArms) ||
|
|
9465
|
+
joinArms.some((a) => a.kind === "dyn" || a.kind === "jsval" || a.kind === "generator" || a.kind === "caught"))) {
|
|
8641
9466
|
return null;
|
|
8642
9467
|
}
|
|
8643
9468
|
joinArms.sort((a, b) => (typeKey(a) < typeKey(b) ? -1 : 1));
|
|
@@ -8645,13 +9470,19 @@ function lowerUnionKeyedRead(lowerer, expr, unionId, value, key, literalField) {
|
|
|
8645
9470
|
// Pass 2: every arm's answer must SURFACE as the join, and index arms
|
|
8646
9471
|
// must pass the single-record keyed-read constraints (the helper is
|
|
8647
9472
|
// shared with recordKeyGet, its missing-key policy included).
|
|
8648
|
-
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);
|
|
8649
9475
|
for (const arm of def.arms) {
|
|
8650
9476
|
if (isUnitType(arm)) {
|
|
8651
9477
|
if (!(type.kind === "union" && lowerer.armTag(type.unionId, UNDEFINED_T) >= 0))
|
|
8652
9478
|
return null;
|
|
8653
9479
|
continue;
|
|
8654
9480
|
}
|
|
9481
|
+
if (key.type.kind === "f64") {
|
|
9482
|
+
if (arm.kind !== "array" || !surfaces(arm.elem))
|
|
9483
|
+
return null;
|
|
9484
|
+
continue;
|
|
9485
|
+
}
|
|
8655
9486
|
if (arm.kind !== "record")
|
|
8656
9487
|
return null;
|
|
8657
9488
|
const shape = lowerer.shapes.get(arm.shapeId);
|
|
@@ -8669,6 +9500,75 @@ function lowerUnionKeyedRead(lowerer, expr, unionId, value, key, literalField) {
|
|
|
8669
9500
|
}
|
|
8670
9501
|
return { kind: "unionKeyGet", unionId, key, value, type, loc: locOf(expr) };
|
|
8671
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
|
+
}
|
|
8672
9572
|
/** Recognizes `obj.field` as an assignable field target: receiver is a
|
|
8673
9573
|
* known class instance OR a record, and the member is a field or (class
|
|
8674
9574
|
* receivers) a declared accessor property. Returns the pieces of a
|
|
@@ -8676,55 +9576,26 @@ function lowerUnionKeyedRead(lowerer, expr, unionId, value, key, literalField) {
|
|
|
8676
9576
|
export function fieldTarget(lowerer, access) {
|
|
8677
9577
|
if (lowerer.chainBlocked(access))
|
|
8678
9578
|
return null;
|
|
8679
|
-
|
|
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));
|
|
8680
9585
|
if (receiverIr?.kind === "object") {
|
|
8681
|
-
|
|
8682
|
-
|
|
8683
|
-
|
|
8684
|
-
|
|
8685
|
-
lowerer.flushDeferredClass(receiverIr.className);
|
|
8686
|
-
return null;
|
|
8687
|
-
}
|
|
8688
|
-
const fieldType = info.fields.get(access.name.text);
|
|
8689
|
-
const lowerObjectReceiver = () => {
|
|
8690
|
-
let obj = lowerer.lowerExpr(access.expression);
|
|
8691
|
-
if (obj.type.kind === "union" && lowerer.armTag(obj.type.unionId, UNDEFINED_T) >= 0) {
|
|
8692
|
-
const present = lowerer.stripUndefinedArm(obj.type);
|
|
8693
|
-
const helper = present.kind === "object"
|
|
8694
|
-
? lowerer.narrowedArmHelper(obj.type.unionId, present, locOf(access.expression))
|
|
8695
|
-
: null;
|
|
8696
|
-
obj = helper
|
|
8697
|
-
? { kind: "call", callee: helper, args: [obj], type: present, loc: locOf(access.expression) }
|
|
8698
|
-
: lowerer.maybeNarrow(obj, access.expression);
|
|
8699
|
-
}
|
|
8700
|
-
return obj;
|
|
8701
|
-
};
|
|
8702
|
-
if (fieldType) {
|
|
8703
|
-
const obj = lowerObjectReceiver();
|
|
8704
|
-
return { container: "class", obj, className: receiverIr.className, field: access.name.text, fieldType };
|
|
8705
|
-
}
|
|
8706
|
-
// Accessor property: either half declared anywhere on the chain
|
|
8707
|
-
// makes the name an accessor target (fields and accessors share a
|
|
8708
|
-
// namespace — tsc rejects mixing them, so the halves agree on kind).
|
|
8709
|
-
const getF = lowerer.findMethodOn(info, `get:${access.name.text}`);
|
|
8710
|
-
const setF = lowerer.findMethodOn(info, `set:${access.name.text}`);
|
|
8711
|
-
if (getF || setF) {
|
|
8712
|
-
const obj = lowerObjectReceiver();
|
|
8713
|
-
return {
|
|
8714
|
-
container: "accessor",
|
|
8715
|
-
obj,
|
|
8716
|
-
className: receiverIr.className,
|
|
8717
|
-
field: access.name.text,
|
|
8718
|
-
fieldType: getF ? getF.sig.ret : setF.sig.params[0].type,
|
|
8719
|
-
};
|
|
8720
|
-
}
|
|
8721
|
-
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));
|
|
8722
9590
|
}
|
|
8723
9591
|
if (receiverIr?.kind === "record") {
|
|
8724
9592
|
const shape = lowerer.shapes.get(receiverIr.shapeId);
|
|
8725
9593
|
const fieldType = shape?.fields.find((f) => f.name === access.name.text)?.type;
|
|
8726
9594
|
if (fieldType) {
|
|
8727
9595
|
let obj = lowerer.lowerExpr(access.expression);
|
|
9596
|
+
const nominal = representedClassFieldTarget(lowerer, access.expression, access.name.text, obj);
|
|
9597
|
+
if (nominal)
|
|
9598
|
+
return nominal;
|
|
8728
9599
|
// A checker-record receiver whose VALUE stayed dyn (the erased
|
|
8729
9600
|
// all-unknown-fields cast — `(err as { code?: unknown }).code`):
|
|
8730
9601
|
// decline, and the dyn keyed-read fallback answers.
|
|
@@ -8858,7 +9729,7 @@ export function uniqueSymbolKeyOf(lowerer, key) {
|
|
|
8858
9729
|
// Module level only: a unique-symbol const inside a FUNCTION is a
|
|
8859
9730
|
// fresh runtime identity per call — one static slot would conflate
|
|
8860
9731
|
// what JS keeps distinct.
|
|
8861
|
-
if (!ts.isVariableStatement(decl.parent
|
|
9732
|
+
if (!ts.isVariableStatement(decl.parent?.parent) || !ts.isSourceFile(decl.parent?.parent?.parent))
|
|
8862
9733
|
return null;
|
|
8863
9734
|
const init = decl.initializer;
|
|
8864
9735
|
if (!init || !ts.isCallExpression(init) || init.questionDotToken)
|
|
@@ -8891,10 +9762,10 @@ export function symbolFieldInfo(lowerer, expr) {
|
|
|
8891
9762
|
lowerer.flushDeferredClass(receiverIr.className);
|
|
8892
9763
|
return null;
|
|
8893
9764
|
}
|
|
8894
|
-
const key =
|
|
9765
|
+
const key = classSymbolKeyOf(lowerer, expr.argumentExpression);
|
|
8895
9766
|
if (!key)
|
|
8896
9767
|
return null;
|
|
8897
|
-
const field = info.symbolFields?.get(key.
|
|
9768
|
+
const field = info.symbolFields?.get(key.identity);
|
|
8898
9769
|
if (field === undefined)
|
|
8899
9770
|
return null;
|
|
8900
9771
|
const fieldType = info.fields.get(field);
|
|
@@ -8916,6 +9787,12 @@ function symbolFieldTarget(lowerer, expr) {
|
|
|
8916
9787
|
* tsc-clean (the property types as the setter's param), but Node yields
|
|
8917
9788
|
* undefined, which these property types cannot represent. */
|
|
8918
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
|
+
}
|
|
8919
9796
|
// A record-shaped CHECKER target whose receiver VALUE lives in the checked-dynamic tree
|
|
8920
9797
|
// (a JS file-scope object-literal global): the checked-dynamic keyed
|
|
8921
9798
|
// read — dynKeyGet (a missing key answers the dyn undefined, exactly
|
|
@@ -9012,6 +9889,12 @@ export function fieldGetExpr(lowerer, target, loc, blame) {
|
|
|
9012
9889
|
* call). A write to a getter-only property never gets here in a clean
|
|
9013
9890
|
* program (tsc's TS2540 is the fence); the rejection is the backstop. */
|
|
9014
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
|
+
}
|
|
9015
9898
|
// A record-shaped CHECKER target whose receiver VALUE lives in the checked-dynamic tree:
|
|
9016
9899
|
// the checked-dynamic keyed write — dyn.keySet (later writes win,
|
|
9017
9900
|
// insertion order; Node's TypeErrors on non-object receivers), the
|
|
@@ -9180,61 +10063,73 @@ export function lowerPromiseAllTupleCall(lowerer, call, access) {
|
|
|
9180
10063
|
}
|
|
9181
10064
|
/** `obj.f op= e` (and `obj.f++` with rhs null ≡ 1) — the element spelling
|
|
9182
10065
|
* `obj[k] op= e` included when k is a declared symbol-keyed field.
|
|
9183
|
-
*
|
|
9184
|
-
*
|
|
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. */
|
|
9185
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) {
|
|
9186
10072
|
if (access.expression.kind === ts.SyntaxKind.SuperKeyword) {
|
|
9187
10073
|
lowerer.unsupported("SC1090", access, "compound assignment through 'super' (read and write separately)");
|
|
9188
10074
|
}
|
|
9189
|
-
|
|
9190
|
-
|
|
9191
|
-
|
|
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
|
+
};
|
|
9192
10081
|
// A CHECKED-DYNAMIC receiver (`context.actual++` — test/common's call
|
|
9193
10082
|
// accounting; dot spelling only — symbol-keyed element targets are
|
|
9194
10083
|
// static fields): read the member (dynKeyGet), VALIDATE it as a
|
|
9195
10084
|
// number (dynCheck — a non-number member throws the catchable
|
|
9196
10085
|
// TypeError where JS would ToNumber-coerce; loud, never a silent
|
|
9197
10086
|
// NaN — SEMANTICS.md), combine, write back (dyn.keySet). The
|
|
9198
|
-
// receiver
|
|
9199
|
-
// read and write matches JS's once-evaluation observably.
|
|
10087
|
+
// receiver and member read are saved before evaluating the RHS.
|
|
9200
10088
|
if (ts.isPropertyAccessExpression(access)) {
|
|
9201
10089
|
const probed = tryLowerExpression(lowerer, access.expression);
|
|
9202
10090
|
if (probed?.type.kind === "dyn") {
|
|
10091
|
+
const receiver = save(probed, "%compoundReceiver");
|
|
9203
10092
|
const key = { kind: "strLit", value: access.name.text, type: STRING, loc: locOf(access.name) };
|
|
9204
|
-
const read = { kind: "dynKeyGet", key, value:
|
|
10093
|
+
const read = save({ kind: "dynKeyGet", key, value: receiver, type: DYN, loc }, "%compoundOld");
|
|
9205
10094
|
const cur = { kind: "dynCheck", value: read, type: F64, loc };
|
|
9206
|
-
const rhs = rhsNode
|
|
10095
|
+
const rhs = save(rhsNode
|
|
9207
10096
|
? lowerer.lowerExpr(rhsNode)
|
|
9208
|
-
: { kind: "numLit", value: 1, type: F64, loc };
|
|
9209
|
-
|
|
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")
|
|
9210
10102
|
lowerer.unsupported("SC1043", access);
|
|
9211
|
-
const value = { kind: "bin", op, left: cur, right:
|
|
9212
|
-
const recv2 = lowerer.lowerExpr(access.expression);
|
|
10103
|
+
const value = save({ kind: "bin", op, left: cur, right: numericRhs, type: F64, loc }, "%compoundResult");
|
|
9213
10104
|
const boxed = { kind: "dynFrom", value, type: DYN, loc };
|
|
9214
|
-
|
|
10105
|
+
body.push({
|
|
9215
10106
|
kind: "exprStmt",
|
|
9216
|
-
expr: { kind: "libCall", fn: "dyn.keySet", args: [
|
|
10107
|
+
expr: { kind: "libCall", fn: "dyn.keySet", args: [receiver, { ...key }, boxed], type: VOID, loc },
|
|
9217
10108
|
loc,
|
|
9218
|
-
};
|
|
10109
|
+
});
|
|
10110
|
+
return { kind: "seqExpr", stmts: body, result: value, type: value.type, loc };
|
|
9219
10111
|
}
|
|
9220
10112
|
}
|
|
9221
10113
|
const targetOf = () => ts.isPropertyAccessExpression(access) ? lowerer.fieldTarget(access) : symbolFieldTarget(lowerer, access);
|
|
9222
10114
|
const target = targetOf();
|
|
9223
10115
|
if (!target)
|
|
9224
10116
|
lowerer.unsupported("SC1090", access, "compound assignment to unsupported field targets");
|
|
9225
|
-
|
|
9226
|
-
//
|
|
9227
|
-
//
|
|
9228
|
-
const read = lowerer.fieldGetExpr(target, locOf(access), access);
|
|
9229
|
-
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
|
|
9230
10122
|
? lowerer.lowerExpr(rhsNode)
|
|
9231
|
-
: { 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);
|
|
9232
10127
|
let value;
|
|
9233
10128
|
if (op === "+" && target.fieldType.kind === "string") {
|
|
9234
10129
|
value = { kind: "strConcat", left: read, right: lowerer.ensureString(rhs, rhsNode ?? access), type: STRING, loc };
|
|
9235
10130
|
}
|
|
9236
|
-
else if (target.fieldType.kind === "f64" &&
|
|
9237
|
-
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 };
|
|
9238
10133
|
}
|
|
9239
10134
|
else if (target.fieldType.kind === "dyn" &&
|
|
9240
10135
|
isJsSourceFile(access.getSourceFile()) &&
|
|
@@ -9270,9 +10165,9 @@ export function lowerFieldCompound(lowerer, access, op, rhsNode, loc) {
|
|
|
9270
10165
|
else {
|
|
9271
10166
|
lowerer.unsupported("SC1043", access);
|
|
9272
10167
|
}
|
|
9273
|
-
|
|
9274
|
-
|
|
9275
|
-
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 };
|
|
9276
10171
|
}
|
|
9277
10172
|
/** Stream-rooted receivers' property surface (readableEnded, destroyed,
|
|
9278
10173
|
* ...): the receiver's mapped class dispatches into lower-stream.ts.
|