@scriptc/compiler 0.1.7 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ambient/scriptc-node-fallback.d.ts +98 -39
- 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 +108 -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 +88 -30
- 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 +21 -2
- package/dist/backend/llvm/dyn.js +647 -251
- package/dist/backend/llvm/dyn.js.map +1 -1
- package/dist/backend/llvm/emitter.d.ts +596 -1
- package/dist/backend/llvm/emitter.js +769 -425
- 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 +2 -2
- package/dist/backend/llvm/expr-bytes.js +60 -31
- 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 +10 -6
- 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 +78 -81
- 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 +62 -31
- 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 +54 -7
- 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 +220 -131
- 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 +44 -19
- package/dist/backend/llvm/lib-process.js.map +1 -1
- package/dist/backend/llvm/lib-shared.js +162 -3
- 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 +42 -131
- 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 +12 -30
- package/dist/backend/native-toolchain.js +83 -233
- package/dist/backend/native-toolchain.js.map +1 -1
- package/dist/backend/native-toolchain.test.js +77 -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 +73 -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 +103 -34
- package/dist/backend/targets.js.map +1 -1
- package/dist/backend/targets.test.js +72 -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 +37 -7
- 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 +5 -10
- 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 +74 -0
- package/dist/frontend/lowering/class-callbacks.js.map +1 -0
- package/dist/frontend/lowering/class-construction.d.ts +12 -0
- package/dist/frontend/lowering/class-construction.js +156 -0
- package/dist/frontend/lowering/class-construction.js.map +1 -0
- package/dist/frontend/lowering/class-descriptors.d.ts +10 -0
- package/dist/frontend/lowering/class-descriptors.js +114 -0
- package/dist/frontend/lowering/class-descriptors.js.map +1 -0
- package/dist/frontend/lowering/class-dynamic-dispatch.d.ts +51 -0
- package/dist/frontend/lowering/class-dynamic-dispatch.js +1158 -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 +93 -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 +67 -0
- package/dist/frontend/lowering/class-instance-constructor.js.map +1 -0
- package/dist/frontend/lowering/class-method-values.d.ts +13 -0
- package/dist/frontend/lowering/class-method-values.js +235 -0
- package/dist/frontend/lowering/class-method-values.js.map +1 -0
- package/dist/frontend/lowering/class-prototypes.d.ts +19 -0
- package/dist/frontend/lowering/class-prototypes.js +242 -0
- package/dist/frontend/lowering/class-prototypes.js.map +1 -0
- package/dist/frontend/lowering/class-runtime-statics.d.ts +8 -0
- package/dist/frontend/lowering/class-runtime-statics.js +66 -0
- package/dist/frontend/lowering/class-runtime-statics.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 +208 -163
- 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 +58 -63
- 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 +14 -0
- package/dist/frontend/lowering/error-methods.js +177 -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 +51 -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 +132 -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 +509 -183
- package/dist/frontend/lowering/expressions/object-literals.js.map +1 -1
- package/dist/frontend/lowering/expressions/optional-chains.d.ts +3 -4
- package/dist/frontend/lowering/expressions/optional-chains.js +88 -24
- 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 +101 -0
- package/dist/frontend/lowering/function-context.js +109 -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 +11 -0
- package/dist/frontend/lowering/generator-dynamic-dispatch.js +143 -0
- package/dist/frontend/lowering/generator-dynamic-dispatch.js.map +1 -0
- package/dist/frontend/lowering/lib-boundary.d.ts +4 -1
- package/dist/frontend/lowering/lib-boundary.js +112 -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 +18 -0
- package/dist/frontend/lowering/lower-builtin-values.js +249 -0
- package/dist/frontend/lowering/lower-builtin-values.js.map +1 -0
- package/dist/frontend/lowering/lower-builtins.d.ts +33 -46
- package/dist/frontend/lowering/lower-builtins.js +676 -365
- package/dist/frontend/lowering/lower-builtins.js.map +1 -1
- package/dist/frontend/lowering/lower-calls.d.ts +28 -22
- package/dist/frontend/lowering/lower-calls.js +1660 -412
- package/dist/frontend/lowering/lower-calls.js.map +1 -1
- package/dist/frontend/lowering/lower-classes.d.ts +95 -57
- package/dist/frontend/lowering/lower-classes.js +1491 -404
- 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 +36 -32
- package/dist/frontend/lowering/lower-containers.js +925 -181
- 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 +3 -2
- 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 +37 -13
- package/dist/frontend/lowering/lower-exprs.js +2163 -863
- 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 +88 -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 +7 -8
- package/dist/frontend/lowering/lower-island.js +66 -33
- 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 +445 -39
- package/dist/frontend/lowering/lower-modules.js.map +1 -1
- package/dist/frontend/lowering/lower-modules.test.d.ts +1 -0
- package/dist/frontend/lowering/lower-modules.test.js +39 -0
- package/dist/frontend/lowering/lower-modules.test.js.map +1 -0
- 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 +17 -17
- package/dist/frontend/lowering/lower-stmts.js +855 -514
- 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 +20 -11
- 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 +140 -190
- package/dist/frontend/lowering/lowerer.js +1193 -712
- 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 +172 -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 +10 -3
- package/dist/frontend/lowering/surfaces.js +86 -37
- 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 +12 -0
- package/dist/frontend/lowering/text-decoder-encoding.js +82 -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 +121 -38
- package/dist/frontend/npm-static-prune.js.map +1 -1
- package/dist/frontend/npm-static-prune.test.js +9 -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 +39 -20
- package/dist/frontend/program.js +393 -189
- package/dist/frontend/program.js.map +1 -1
- package/dist/frontend/program.test.js +105 -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 +25 -18
- 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 +47 -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 +34 -18
- package/dist/frontend/type-mapper.js +552 -131
- package/dist/frontend/type-mapper.js.map +1 -1
- package/dist/frontend/type-mapper.test.js +146 -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 +23 -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 +334 -176
- package/dist/ir/ir.js +710 -903
- package/dist/ir/ir.js.map +1 -1
- package/dist/ir/ir.test.js +162 -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 +33 -14
- package/dist/ir/serialize.js.map +1 -1
- package/dist/ir/serialize.test.d.ts +1 -0
- package/dist/ir/serialize.test.js +57 -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 +2018 -1498
- package/dist/ir/validate.js.map +1 -1
- package/dist/ir/validate.test.js +536 -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 +137 -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 +94 -0
- package/dist/native/toolchain.js.map +1 -0
- package/dist/native/toolchain.test.d.ts +1 -0
- package/dist/native/toolchain.test.js +85 -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 +107 -98
- 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,20 @@ 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 { captureContextArguments } from "./function-context.js";
|
|
15
|
+
import { OBJECT_CALLABLE_VALUES } from "./surfaces.js";
|
|
11
16
|
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,
|
|
17
|
+
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
18
|
import { cjsClassExprWholeExportOf, cjsExportAssignmentOf, cjsExportDiscardReason, isCjsExportTableLiteral, isCjsJsFile, isJsSourceFile, isModuleExportsAccess, isNodeEsmFile, locOf } from "../program.js";
|
|
14
19
|
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";
|
|
16
|
-
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";
|
|
20
|
+
import { UNSUPPORTED, blockedBindingUseDiag, requiresDynamicPackageDiag, unsupportedDiag } from "../../diagnostics/diagnostic.js";
|
|
21
|
+
import { PoisonError, dynFallbackType, dynUndefinedExpr, jsFuncNameOf, neverTaintedJsType, nodeThrowExpr, own } from "./lowerer.js";
|
|
22
|
+
import { lowerCollectionSpread, lowerNpmStaticSafeIndexRead, lowerSafeIndexRead, strCharsCall, tryLowerNumericIndexRead } from "./lower-containers.js";
|
|
23
|
+
import { arrayValueRead, arrayValueStore, arrayValueType } from "./array-values.js";
|
|
24
|
+
import { lowerOptionalStringIndex } from "./string-index.js";
|
|
19
25
|
import { tryLowerIndexedComparison } from "./indexed-comparison.js";
|
|
20
26
|
import { npmStaticPackageOfPath } from "../npm-static.js";
|
|
21
27
|
import { unsupportedModuleFeatureOf } from "../builtin-modules.js";
|
|
@@ -23,20 +29,37 @@ import { fenceEnumObjectValue, lowerEnumAccess } from "./lower-enums.js";
|
|
|
23
29
|
import { ambientNsRootOf, ambientUndefReadType, ambientUndefVarRootOf, ambientUndefinedFnSymbolOf, contextualUndefReadType, fenceEarlyAliasUse, fenceEarlyNsMemberRef, lowerNsIdentifierValue, nsMemberIdentOf, nsUndefRead, nsWritableTarget } from "./lower-namespaces.js";
|
|
24
30
|
import { expandoMemberRead, expandoWritableTarget } from "./lower-expando.js";
|
|
25
31
|
import { lowerSocketInstanceOf, lowerTlsRootCertificates } from "./lower-server.js";
|
|
26
|
-
import { findGenericMethodOn, lowerStaticFieldRead } from "./lower-classes.js";
|
|
32
|
+
import { findGenericMethodOn, lowerStaticFieldRead, staticFieldWriteTarget, storedClassValueType } from "./lower-classes.js";
|
|
33
|
+
import { hasRuntimeStatics } from "./class-runtime-statics.js";
|
|
27
34
|
import { bindingNeverReassigned, funcTypeFromParamShapes, implicitMonoFile, lowerTaggedTemplate, nullishGenericBindingUnitOf, objLitGenericFnInfoOf, objLitGenericFnNodeOf, requireObjLitGenericReceiver } from "./lower-calls.js";
|
|
28
35
|
import { mixinFnOfCallee } from "./lower-mixins.js";
|
|
29
|
-
import { isConstAssertionTypeNode, isGenericCallableMemberType, isParseArgsDynTypeName, underConstAssertion, unitOnlyUnion } from "../type-mapper.js";
|
|
36
|
+
import { isConstAssertionTypeNode, isGenericCallableMemberType, isParseArgsDynTypeName, underConstAssertion, unitOnlyUnion, withUnitArm } from "../type-mapper.js";
|
|
30
37
|
import { lowerYield } from "./lower-generators.js";
|
|
38
|
+
import { errorPropertyRead, errorPropertyWrite, errorToStringCall } from "./error-methods.js";
|
|
31
39
|
import { lowerStreamProperty, lowerStreamStateProperty, streamSidesOf } from "./lower-stream.js";
|
|
32
|
-
import { countedFor, numLit, varRef } from "../../ir/build.js";
|
|
40
|
+
import { countedFor, numLit, strLit, varRef } from "../../ir/build.js";
|
|
41
|
+
import { unionWideningTags } from "../../ir/analysis.js";
|
|
33
42
|
import { isSafeToDiscard, isSafeToMoveConditionEarlier, isSafeToRepeat } from "./expressions/evaluation-safety.js";
|
|
43
|
+
import { globalSymbolKey } from "./expressions/global-symbols.js";
|
|
44
|
+
import { lowerShortCircuitAssignment } from "./expressions/nullish-assignment.js";
|
|
34
45
|
import { hasOptionalChainGuard, isOptionalChainTail, isRequireMainFilename } from "./expressions/optional-chains.js";
|
|
35
46
|
import { conditionalSpreadOf, foldedStringKeyOf } from "./expressions/object-literals.js";
|
|
36
47
|
import { tryLowerExpression } from "./expressions/try-lower-expression.js";
|
|
37
48
|
import { fenceNodeModuleMutation, isNodeModuleValue, lowerNodeModuleIdentifier, lowerNodeModuleProperty, lowerRequireCacheElement, lowerRequireCacheHas, lowerRequireMainProperty } from "./lower-node-module.js";
|
|
38
49
|
import { lowerAbstractEquality } from "./abstract-equality.js";
|
|
39
50
|
import { coerceStringSearchValue, defaultAfterUndefined, lowerStaticallyUndefinedArgument } from "./optional-arguments.js";
|
|
51
|
+
import { recordTextCodecClass } from "../../ir/ir.js";
|
|
52
|
+
import { classSymbolKeyOf } from "./symbol-fields.js";
|
|
53
|
+
import { lowerClassMethodValue } from "./class-method-values.js";
|
|
54
|
+
import { classInstanceOf } from "./class-dynamic-dispatch.js";
|
|
55
|
+
import { lowerGlobalValue } from "./lower-global-value.js";
|
|
56
|
+
import { lowerClassPrototypeData } from "./class-prototypes.js";
|
|
57
|
+
import { isClassCallback } from "./class-callbacks.js";
|
|
58
|
+
import { builtinPrototypeMethod, lowerArrayIsArrayValue, lowerCheckedPredicateValue, lowerNumberParserValue, lowerObjectAssignValue, lowerStringCodesValue } from "./lower-builtin-values.js";
|
|
59
|
+
import { lowerArrayFromValue } from "./lower-containers.js";
|
|
60
|
+
import { lowerPerfHooksTypeof } from "./lower-builtins.js";
|
|
61
|
+
import { jsBindingHasOpenWrites } from "./lower-stmts.js";
|
|
62
|
+
import { checkedClassInstanceOf } from "./class-construction.js";
|
|
40
63
|
/** A template piece's RAW text (String.raw's contract: escapes stay
|
|
41
64
|
* characters). 7's client AST ships no rawText at runtime (the typing
|
|
42
65
|
* declares it; the serialized node data omits it), so the raw span comes
|
|
@@ -313,6 +336,12 @@ function lowerExprInner(lowerer, expr) {
|
|
|
313
336
|
return !sf.isDeclarationFile && !sf.fileName.includes("/node_modules/");
|
|
314
337
|
});
|
|
315
338
|
if (projectDeclared) {
|
|
339
|
+
// A spread already stored in the island must copy there too.
|
|
340
|
+
// Later overrides can make the literal's own inferred shape
|
|
341
|
+
// appear static without making that source a native record.
|
|
342
|
+
if (ts.isObjectLiteralExpression(expr) && expr.properties.some((p) => ts.isSpreadAssignment(p) && lowerer.mapTypeOf(lowerer.typeOf(p.expression))?.kind === "jsval")) {
|
|
343
|
+
return true;
|
|
344
|
+
}
|
|
316
345
|
const own = lowerer.mapTypeOf(lowerer.typeOf(expr));
|
|
317
346
|
if (own?.kind === "record" || own?.kind === "array")
|
|
318
347
|
return false;
|
|
@@ -350,7 +379,7 @@ function lowerExprInner(lowerer, expr) {
|
|
|
350
379
|
// null keeps the closure shape (function-shaped members, and the
|
|
351
380
|
// conditional-spread arms where getters cannot combine).
|
|
352
381
|
const islandMemberFence = (diagsBefore, err, valueNode, getterName = null) => {
|
|
353
|
-
if (!
|
|
382
|
+
if (!isJsSourceFile(expr.getSourceFile()))
|
|
354
383
|
throw err;
|
|
355
384
|
const fence = lowerer.deferToRuntimeFence(diagsBefore, valueNode, {
|
|
356
385
|
kind: "closure",
|
|
@@ -398,6 +427,8 @@ function lowerExprInner(lowerer, expr) {
|
|
|
398
427
|
// instance): func-typed values keep the call-time closure;
|
|
399
428
|
// data-shaped values fence at the read (the getter), unless a
|
|
400
429
|
// conditional spread owns the literal (getters cannot combine).
|
|
430
|
+
if (!(err instanceof PoisonError))
|
|
431
|
+
throw err;
|
|
401
432
|
const asGetter = value.type.kind !== "func" && spread === null;
|
|
402
433
|
const fence = islandMemberFence(diagsBefore, err, valueNode, asGetter ? name.text : null);
|
|
403
434
|
if (fence === null)
|
|
@@ -486,6 +517,8 @@ function lowerExprInner(lowerer, expr) {
|
|
|
486
517
|
: null;
|
|
487
518
|
}
|
|
488
519
|
catch (err) {
|
|
520
|
+
if (!(err instanceof PoisonError))
|
|
521
|
+
throw err;
|
|
489
522
|
const nameText = name && (ts.isIdentifier(name) || ts.isStringLiteral(name)) ? name.text : null;
|
|
490
523
|
const asGetter = nameText !== null && spread === null && !funcShapedMember(prop);
|
|
491
524
|
value = islandMemberFence(valueDiagsBefore, err, prop, asGetter ? nameText : null);
|
|
@@ -551,6 +584,11 @@ function lowerExprInner(lowerer, expr) {
|
|
|
551
584
|
return { kind: "jsOp", op: "arrLit", args, type: JSVAL, loc };
|
|
552
585
|
}
|
|
553
586
|
if (ts.isTypeOfExpression(expr)) {
|
|
587
|
+
if (lowerer.isStdlibGlobal(expr.expression, "process"))
|
|
588
|
+
return { kind: "strLit", value: "object", type: STRING, loc };
|
|
589
|
+
const performance = lowerPerfHooksTypeof(lowerer, expr.expression);
|
|
590
|
+
if (performance)
|
|
591
|
+
return performance;
|
|
554
592
|
if (isNodeModuleValue(lowerer, expr.expression)) {
|
|
555
593
|
return { kind: "strLit", value: "object", type: STRING, loc };
|
|
556
594
|
}
|
|
@@ -580,8 +618,14 @@ function lowerExprInner(lowerer, expr) {
|
|
|
580
618
|
// every function and constructor global is "function" (the harness's
|
|
581
619
|
// `typeof queueMicrotask === 'function'` probes). Shadowing locals
|
|
582
620
|
// have non-stdlib symbols and keep the ordinary path.
|
|
583
|
-
|
|
584
|
-
|
|
621
|
+
let tested = expr.expression;
|
|
622
|
+
while (ts.isParenthesizedExpression(tested))
|
|
623
|
+
tested = tested.expression;
|
|
624
|
+
if (ts.isIdentifier(tested)) {
|
|
625
|
+
const sym = lowerer.checker.getSymbolAtLocation(tested);
|
|
626
|
+
if (!sym && !lowerer.dynamic) {
|
|
627
|
+
return { kind: "strLit", value: "undefined", type: STRING, loc };
|
|
628
|
+
}
|
|
585
629
|
if (lowerer.isStdlibSymbol(sym)) {
|
|
586
630
|
const t = lowerer.typeOf(expr.expression);
|
|
587
631
|
if (lowerer.checker.getCallSignatures(t).length > 0 ||
|
|
@@ -590,11 +634,9 @@ function lowerExprInner(lowerer, expr) {
|
|
|
590
634
|
}
|
|
591
635
|
}
|
|
592
636
|
}
|
|
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).
|
|
637
|
+
// A known static result type fixes typeof's answer, but its producer
|
|
638
|
+
// still evaluates: calls, getters, and checked reads can have effects
|
|
639
|
+
// or throw. Only trivial operands may disappear.
|
|
598
640
|
let operand = lowerer.lowerExpr(expr.expression);
|
|
599
641
|
if (operand.type.kind === "jsval") {
|
|
600
642
|
return { kind: "jsOp", op: "typeof", args: [operand], type: STRING, loc };
|
|
@@ -612,13 +654,16 @@ function lowerExprInner(lowerer, expr) {
|
|
|
612
654
|
array: "object", object: "object", record: "object",
|
|
613
655
|
symbol: "symbol",
|
|
614
656
|
map: "object", set: "object", promise: "object", bytes: "object",
|
|
615
|
-
|
|
657
|
+
regex: "object", generator: "object", classval: "function",
|
|
616
658
|
moduleNs: "object",
|
|
617
659
|
undefinedT: "undefined", nullT: "object",
|
|
618
660
|
};
|
|
619
661
|
const folded = FOLD[operand.type.kind];
|
|
620
|
-
if (folded
|
|
621
|
-
|
|
662
|
+
if (folded) {
|
|
663
|
+
const result = { kind: "strLit", value: folded, type: STRING, loc };
|
|
664
|
+
return isSafeToDiscard(operand) ? result : {
|
|
665
|
+
kind: "seqExpr", stmts: [{ kind: "exprStmt", expr: operand, loc }], result, type: STRING, loc,
|
|
666
|
+
};
|
|
622
667
|
}
|
|
623
668
|
// A union operand: every arm's typeof answer is static, so the value
|
|
624
669
|
// form is a ternary chain over runtime TAG tests (arms grouped by
|
|
@@ -653,6 +698,9 @@ function lowerExprInner(lowerer, expr) {
|
|
|
653
698
|
lowerer.unsupported("SC1090", expr, "typeof expressions on statically-typed values");
|
|
654
699
|
}
|
|
655
700
|
if (ts.isIdentifier(expr)) {
|
|
701
|
+
if (lowerer.isStdlibGlobal(expr, "console")) {
|
|
702
|
+
return { kind: "libCall", fn: "console.native", args: [], type: DYN, loc };
|
|
703
|
+
}
|
|
656
704
|
const moduleValue = lowerNodeModuleIdentifier(lowerer, expr);
|
|
657
705
|
if (moduleValue)
|
|
658
706
|
return moduleValue;
|
|
@@ -662,8 +710,10 @@ function lowerExprInner(lowerer, expr) {
|
|
|
662
710
|
// `arguments` in a variadic JS function (the rest-marked form): the
|
|
663
711
|
// synthetic trailing dyn-array param — lambdaSignature marked the
|
|
664
712
|
// type and lowerLambda declared the local.
|
|
665
|
-
if (expr.text === "arguments"
|
|
666
|
-
|
|
713
|
+
if (expr.text === "arguments") {
|
|
714
|
+
const binding = captureContextArguments(lowerer.fnStack);
|
|
715
|
+
if (binding)
|
|
716
|
+
return { kind: "varRef", localId: binding.id, type: DYN, loc };
|
|
667
717
|
}
|
|
668
718
|
// A compiler-projected adapter as a VALUE: the adapter has no
|
|
669
719
|
// runtime function object (its calls rewrite directly) — call it
|
|
@@ -676,7 +726,7 @@ function lowerExprInner(lowerer, expr) {
|
|
|
676
726
|
}
|
|
677
727
|
if (projection?.kind === "builtin-function") {
|
|
678
728
|
if (!isJsSourceFile(expr.getSourceFile())) {
|
|
679
|
-
const callable = lowerer.lowerBuiltinCallableValue(projection, loc);
|
|
729
|
+
const callable = lowerer.lowerBuiltinCallableValue({ module: projection.module, member: projection.member }, loc);
|
|
680
730
|
if (callable)
|
|
681
731
|
return callable;
|
|
682
732
|
}
|
|
@@ -688,6 +738,21 @@ function lowerExprInner(lowerer, expr) {
|
|
|
688
738
|
// bridges the tagged representation with a unionNarrow.
|
|
689
739
|
const local = lowerer.resolveLocal(expr);
|
|
690
740
|
if (local) {
|
|
741
|
+
// A switch can dispatch past a lexical declaration. Keep this
|
|
742
|
+
// previously refused codec form out until switch-scope TDZ boxes
|
|
743
|
+
// model that skipped initialization, including value aliases.
|
|
744
|
+
if (local.type.kind === "record" && recordTextCodecClass(lowerer.shapes.get(local.type.shapeId)) !== null) {
|
|
745
|
+
const symbol = lowerer.resolveValueSymbol(expr);
|
|
746
|
+
const decl = symbol ? lowerer.checker.valueDeclarationOf(symbol) : undefined;
|
|
747
|
+
if (decl && ts.isVariableDeclaration(decl) && ts.isVariableDeclarationList(decl.parent)
|
|
748
|
+
&& (decl.parent.flags & ts.NodeFlags.BlockScoped) !== 0 && ts.isVariableStatement(decl.parent.parent)) {
|
|
749
|
+
const clause = decl.parent.parent.parent;
|
|
750
|
+
if ((ts.isCaseClause(clause) || ts.isDefaultClause(clause))
|
|
751
|
+
&& !(expr.getStart() >= clause.getStart() && expr.end <= clause.end)) {
|
|
752
|
+
lowerer.unsupported("SC1090", expr, "codec bindings read from another switch clause before initialization is proven");
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
}
|
|
691
756
|
if (local.type.kind === "caught")
|
|
692
757
|
return lowerer.caughtRead(expr, local, loc);
|
|
693
758
|
const symbol = lowerer.resolveValueSymbol(expr);
|
|
@@ -696,7 +761,7 @@ function lowerExprInner(lowerer, expr) {
|
|
|
696
761
|
const arithmetic = lowerer.runtimeOptionalArithmeticLocals.has(runtimeOptionalRoot);
|
|
697
762
|
const captured = !!symbol &&
|
|
698
763
|
lowerer.runtimeOptionalStorageLocals.has(runtimeOptionalRoot) &&
|
|
699
|
-
(lowerer.ctx.captureBySymbol.get(symbol) === local || runtimeOptionalRoot !== local);
|
|
764
|
+
(local.boxed === true || lowerer.ctx.captureBySymbol.get(symbol) === local || runtimeOptionalRoot !== local);
|
|
700
765
|
if (arithmetic && !runtimeOptionalUseOf(expr)) {
|
|
701
766
|
return { kind: "varRef", localId: local.id, type: local.type, loc };
|
|
702
767
|
}
|
|
@@ -737,7 +802,24 @@ function lowerExprInner(lowerer, expr) {
|
|
|
737
802
|
}
|
|
738
803
|
return { kind: "varRef", localId: local.id, type: local.type, loc };
|
|
739
804
|
}
|
|
740
|
-
|
|
805
|
+
const narrowed = lowerer.maybeNarrow({ kind: "varRef", localId: local.id, type: local.type, loc }, expr);
|
|
806
|
+
if (narrowed.type.kind === "union" && lowerer.runtimeOptionalStorageLocals.has(runtimeOptionalRoot) &&
|
|
807
|
+
lowerer.mapTypeOf(lowerer.typeOf(expr))?.kind === "record") {
|
|
808
|
+
// A presence guard can be followed by a predicate strengthening
|
|
809
|
+
// the record's fields. Its refined shape is not a stored union
|
|
810
|
+
// arm: extract the original layout before reading a member.
|
|
811
|
+
const use = runtimeOptionalUseOf(expr);
|
|
812
|
+
if (use && !use.complex && !use.optional) {
|
|
813
|
+
if (use.kind === "property")
|
|
814
|
+
return runtimeOptionalReceiverRead(lowerer, expr, local, use.access.name.text, loc);
|
|
815
|
+
if (use.kind === "element") {
|
|
816
|
+
const key = runtimeOptionalElementKey(use.access.argumentExpression);
|
|
817
|
+
if (key !== null)
|
|
818
|
+
return runtimeOptionalReceiverRead(lowerer, expr, local, key, loc);
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
return narrowed;
|
|
741
823
|
}
|
|
742
824
|
// `import x = N.y` aliases resolve transparently through globalOf/
|
|
743
825
|
// fnSigOf below; their source-order guards live here (a no-op for
|
|
@@ -774,6 +856,7 @@ function lowerExprInner(lowerer, expr) {
|
|
|
774
856
|
lowerer.unions.get(g.type.unionId).arms.some((a) => a.kind === "string");
|
|
775
857
|
if (!arithmeticUnion && g.type.kind === "union" && narrowed && narrowed.kind !== "union" &&
|
|
776
858
|
!isUnitType(narrowed) &&
|
|
859
|
+
!(narrowed.kind === "record" && recordTextCodecClass(lowerer.shapes.get(narrowed.shapeId)) !== null) &&
|
|
777
860
|
narrowed.kind !== "f64" && narrowed.kind !== "string" && narrowed.kind !== "bool" &&
|
|
778
861
|
lowerer.armTag(g.type.unionId, narrowed) >= 0) {
|
|
779
862
|
const helper = lowerer.narrowedArmHelper(g.type.unionId, narrowed, loc);
|
|
@@ -817,7 +900,7 @@ function lowerExprInner(lowerer, expr) {
|
|
|
817
900
|
if ((expr.text === "__dirname" || expr.text === "__filename") &&
|
|
818
901
|
lowerer.isStdlibGlobal(expr, expr.text)) {
|
|
819
902
|
const sf = expr.getSourceFile();
|
|
820
|
-
if (isNodeEsmFile(sf)) {
|
|
903
|
+
if (isNodeEsmFile(sf, lowerer.program)) {
|
|
821
904
|
lowerer.unsupported("SC1090", expr, `'${expr.text}' in an ES module (Node defines the CJS module globals only in CommonJS modules)`);
|
|
822
905
|
}
|
|
823
906
|
// The WASI runner mounts the host cwd at guest `/` and the host temp
|
|
@@ -899,17 +982,17 @@ function lowerExprInner(lowerer, expr) {
|
|
|
899
982
|
if (roots)
|
|
900
983
|
return roots;
|
|
901
984
|
}
|
|
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
985
|
{
|
|
909
986
|
const callable = lowerer.lowerBuiltinCallableValue(bi, loc);
|
|
910
987
|
if (callable)
|
|
911
988
|
return callable;
|
|
912
989
|
}
|
|
990
|
+
// JavaScript sources: an otherwise unsupported builtin VALUE is
|
|
991
|
+
// the same identity-token story as stdlib globals above (the
|
|
992
|
+
// harness adds worker_threads.Worker to its identity Set).
|
|
993
|
+
if (isJsSourceFile(expr.getSourceFile())) {
|
|
994
|
+
return { kind: "strLit", value: `[builtin ${bi.module}.${bi.member}]`, type: STRING, loc };
|
|
995
|
+
}
|
|
913
996
|
if (builtinModuleFnOf(lowerer, bi.module, bi.member)) {
|
|
914
997
|
lowerer.unsupported("SC1090", expr, `library functions as values (call '${expr.text}' directly)`);
|
|
915
998
|
}
|
|
@@ -977,14 +1060,36 @@ function lowerExprInner(lowerer, expr) {
|
|
|
977
1060
|
// type-mapper.ts pins the one concrete signature `(value: string) =>
|
|
978
1061
|
// primitive`; direct calls `String(x)` never reach here — the call
|
|
979
1062
|
// lowering intercepts them with the wider static coercions).
|
|
980
|
-
const jsPrimitiveCtorSelector = isJsSourceFile(expr.getSourceFile())
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
1063
|
+
const jsPrimitiveCtorSelector = isJsSourceFile(expr.getSourceFile());
|
|
1064
|
+
const primitiveName = stdlibGlobalNameOf(lowerer, expr);
|
|
1065
|
+
if (primitiveName === "String" || primitiveName === "Number" || primitiveName === "Boolean" || primitiveName === "BigInt") {
|
|
1066
|
+
return primitiveCtorClosure(lowerer, primitiveName, loc, jsPrimitiveCtorSelector);
|
|
1067
|
+
}
|
|
1068
|
+
if (lowerer.isStdlibGlobal(expr, "ArrayBuffer"))
|
|
1069
|
+
return { kind: "libCall", fn: "arrayBuffer.constructor", args: [], type: DYN, loc };
|
|
1070
|
+
if (Object.values(BYTES_ELEMENT_NAME).includes(expr.text) && lowerer.isStdlibSymbol(lowerer.checker.getSymbolAtLocation(expr))) {
|
|
1071
|
+
return { kind: "libCall", fn: "bytes.constructor", args: [{ kind: "strLit", value: expr.text, type: STRING, loc }], type: DYN, loc };
|
|
1072
|
+
}
|
|
1073
|
+
if ((expr.text === "encodeURIComponent" || expr.text === "encodeURI" || expr.text === "decodeURIComponent" || expr.text === "decodeURI") && lowerer.isStdlibGlobal(expr, expr.text)) {
|
|
1074
|
+
const name = `%builtin.${expr.text}`;
|
|
1075
|
+
const type = funcOf([DYN], STRING);
|
|
1076
|
+
if (!lowerer.liftedFns.some((fn) => fn.name === name)) {
|
|
1077
|
+
const value = { kind: "libCall", fn: "dyn.toStringCoerce", args: [varRef("value", DYN, loc)], type: STRING, loc };
|
|
1078
|
+
const fn = expr.text === "encodeURIComponent" ? "str.encodeUriComponent" : expr.text === "encodeURI" ? "str.encodeUri" : expr.text === "decodeURI" ? "str.decodeUri" : "str.decodeUriComponent";
|
|
1079
|
+
lowerer.liftedFns.push({ name, params: [{ localId: "value", name: "value", type: DYN }],
|
|
1080
|
+
locals: [{ id: "value", name: "value", type: DYN, mutable: false }], returnType: STRING,
|
|
1081
|
+
body: [{ kind: "return", value: { kind: "libCall", fn, args: [value], type: STRING, loc }, loc }], loc });
|
|
1082
|
+
}
|
|
1083
|
+
return { kind: "closure", fnName: name, captures: [], type, loc };
|
|
1084
|
+
}
|
|
1085
|
+
if (!lowerer.dynamic && lowerer.isStdlibGlobal(expr, "crypto"))
|
|
1086
|
+
return { kind: "libCall", fn: "crypto.native", args: [], type: DYN, loc };
|
|
1087
|
+
const nativeAlias = stdlibGlobalNameOf(lowerer, expr);
|
|
1088
|
+
if (nativeAlias === "Buffer" && expr.text !== "Buffer") {
|
|
1089
|
+
return { kind: "strLit", value: "[builtin Buffer]", type: STRING, loc };
|
|
1090
|
+
}
|
|
1091
|
+
if (!lowerer.dynamic && lowerer.isStdlibGlobal(expr, "fetch")) {
|
|
1092
|
+
return { kind: "libCall", fn: "fetch.function", args: [], type: DYN, loc };
|
|
988
1093
|
}
|
|
989
1094
|
// The lib fence's IDENTIFIER chokepoint: the real standard library
|
|
990
1095
|
// resolves names the old minimal ambient world never declared
|
|
@@ -999,6 +1104,9 @@ function lowerExprInner(lowerer, expr) {
|
|
|
999
1104
|
// global, one token; what a token cannot do (be called, answer
|
|
1000
1105
|
// typeof "function") meets per-site fences/divergences, and
|
|
1001
1106
|
// SEMANTICS.md documents the stance.
|
|
1107
|
+
if (!lowerer.dynamic && stdlibGlobalNameOf(lowerer, expr) === "globalThis") {
|
|
1108
|
+
return lowerGlobalValue(lowerer, expr);
|
|
1109
|
+
}
|
|
1002
1110
|
{
|
|
1003
1111
|
const sym = lowerer.checker.getSymbolAtLocation(expr);
|
|
1004
1112
|
if (lowerer.isStdlibSymbol(sym) || expr.text === "globalThis") {
|
|
@@ -1016,11 +1124,11 @@ function lowerExprInner(lowerer, expr) {
|
|
|
1016
1124
|
// The families with a WHY: each hint states what makes the
|
|
1017
1125
|
// surface genuinely non-static (or what to use instead).
|
|
1018
1126
|
const globalHints = {
|
|
1019
|
-
Proxy: "
|
|
1127
|
+
Proxy: "Proxy constructor values have no native lowering; use a direct new Proxy with checked-native plain objects",
|
|
1020
1128
|
Reflect: "reflective property access has no static lowering — read and call members directly",
|
|
1021
|
-
Intl: "
|
|
1129
|
+
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
1130
|
SharedArrayBuffer: "no shared-memory threads exist in a compiled program — Uint8Array is the byte storage",
|
|
1023
|
-
ArrayBuffer: "
|
|
1131
|
+
ArrayBuffer: "ArrayBuffer constructor values have no native lowering; use new ArrayBuffer(length) for fixed-length shared storage",
|
|
1024
1132
|
WeakRef: "deref()-after-collect exposes GC timing — genuinely dynamic; hold a strong reference instead",
|
|
1025
1133
|
FinalizationRegistry: "finalization callbacks expose GC timing — genuinely dynamic; release resources explicitly instead",
|
|
1026
1134
|
eval: "runtime code evaluation cannot be compiled ahead of time",
|
|
@@ -1182,8 +1290,13 @@ function lowerExprInner(lowerer, expr) {
|
|
|
1182
1290
|
// `this` in plain nested functions is already a tsc error under
|
|
1183
1291
|
// noImplicitThis, so the generic scope walk here is preflight-safe.
|
|
1184
1292
|
const local = lowerer.resolveThis();
|
|
1185
|
-
if (local)
|
|
1186
|
-
|
|
1293
|
+
if (local) {
|
|
1294
|
+
const value = { kind: "varRef", localId: local.id, type: local.type, loc };
|
|
1295
|
+
const initialized = lowerer.ctx.superInitialized;
|
|
1296
|
+
if (initialized)
|
|
1297
|
+
return { kind: "seqExpr", stmts: [{ kind: "if", cond: { kind: "unary", op: "!", operand: { kind: "varRef", localId: initialized.id, type: BOOL, loc }, type: BOOL, loc }, then: [{ kind: "exprStmt", expr: nodeThrowExpr(5, "", "Must call super constructor in derived class before accessing 'this' or returning from derived constructor", DYN, loc), loc }], else_: null, loc }], result: value, type: value.type, loc };
|
|
1298
|
+
return value;
|
|
1299
|
+
}
|
|
1187
1300
|
// `this` in a plain JS FUNCTION (not a method): the AMBIENT
|
|
1188
1301
|
// RECEIVER (libCall dyn.this — scr_dyn_this_get). Firing sites bind
|
|
1189
1302
|
// the emitting handle around each listener call (Node calls
|
|
@@ -1370,6 +1483,20 @@ function lowerExprInner(lowerer, expr) {
|
|
|
1370
1483
|
if (ts.isConditionalExpression(expr))
|
|
1371
1484
|
return lowerTernary(lowerer, expr);
|
|
1372
1485
|
if (ts.isPropertyAccessExpression(expr)) {
|
|
1486
|
+
// Expando statics may still be absent even when JS inference sees
|
|
1487
|
+
// the eventual assignment's scalar type. Preserve the checked value.
|
|
1488
|
+
const staticField = lowerStaticFieldRead(lowerer, expr);
|
|
1489
|
+
if (staticField)
|
|
1490
|
+
return staticField;
|
|
1491
|
+
const prototypeData = lowerClassPrototypeData(lowerer, expr);
|
|
1492
|
+
if (prototypeData)
|
|
1493
|
+
return prototypeData;
|
|
1494
|
+
if (ts.isPropertyAccessExpression(expr.expression) && expr.expression.name.text === "prototype") {
|
|
1495
|
+
const prototype = lowerClassPrototypeData(lowerer, expr.expression);
|
|
1496
|
+
if (prototype)
|
|
1497
|
+
return { kind: "dynKeyGet", value: prototype,
|
|
1498
|
+
key: { kind: "strLit", value: expr.name.text, type: STRING, loc }, type: DYN, loc };
|
|
1499
|
+
}
|
|
1373
1500
|
// `super.x`: the base chain's GETTER, called directly (super
|
|
1374
1501
|
// dispatch is static in JS — never through the dynamic class).
|
|
1375
1502
|
// super.method() calls are routed at the call site; a bare super
|
|
@@ -1411,6 +1538,33 @@ function lowerExprInner(lowerer, expr) {
|
|
|
1411
1538
|
const moduleProperty = lowerNodeModuleProperty(lowerer, expr);
|
|
1412
1539
|
if (moduleProperty)
|
|
1413
1540
|
return moduleProperty;
|
|
1541
|
+
if (stdlibGlobalNameOf(lowerer, expr.expression) === "Symbol" &&
|
|
1542
|
+
["iterator", "asyncIterator", "toStringTag", "toPrimitive", "hasInstance", "isConcatSpreadable", "match", "matchAll", "replace", "search", "split", "species", "unscopables", "dispose", "asyncDispose"].includes(expr.name.text)) {
|
|
1543
|
+
return { kind: "libCall", fn: "sym.wellKnown", args: [{ kind: "strLit", value: expr.name.text, type: STRING, loc }], type: { kind: "symbol" }, loc };
|
|
1544
|
+
}
|
|
1545
|
+
if (!lowerer.dynamic && lowerer.stdlibGlobalMember(expr, "Object") === "freeze") {
|
|
1546
|
+
const name = "%builtin.Object.freeze";
|
|
1547
|
+
if (!lowerer.liftedFns.some((fn) => fn.name === name))
|
|
1548
|
+
lowerer.liftedFns.push({ name,
|
|
1549
|
+
params: [{ localId: "value", name: "value", type: DYN }], locals: [{ id: "value", name: "value", type: DYN, mutable: false }], returnType: DYN,
|
|
1550
|
+
body: [{ kind: "return", value: { kind: "libCall", fn: "dyn.freeze", args: [varRef("value", DYN, loc)], type: DYN, loc }, loc }], loc });
|
|
1551
|
+
return { kind: "closure", fnName: name, captures: [], type: funcOf([DYN], DYN), loc };
|
|
1552
|
+
}
|
|
1553
|
+
if (lowerer.stdlibGlobalMember(expr, "globalThis") === "console") {
|
|
1554
|
+
return { kind: "libCall", fn: "console.native", args: [], type: DYN, loc };
|
|
1555
|
+
}
|
|
1556
|
+
if (lowerer.stdlibGlobalMember(expr, "console") !== null) {
|
|
1557
|
+
return { kind: "dynKeyGet", value: { kind: "libCall", fn: "console.native", args: [], type: DYN, loc },
|
|
1558
|
+
key: { kind: "strLit", value: expr.name.text, type: STRING, loc }, type: DYN, loc };
|
|
1559
|
+
}
|
|
1560
|
+
// Missing host globals (Bun/Deno capability probes) have no binding
|
|
1561
|
+
// in a native program. Declared globals retain their own lowering;
|
|
1562
|
+
// arbitrary global-object mutation remains fenced. Preserve a dyn
|
|
1563
|
+
// undefined value so stored probes compose with optional calls.
|
|
1564
|
+
if (!lowerer.dynamic && stdlibGlobalNameOf(lowerer, expr.expression) === "globalThis" &&
|
|
1565
|
+
!lowerer.checker.getPropertyOfType(lowerer.typeOf(expr.expression), expr.name.text)) {
|
|
1566
|
+
return { kind: "dynFrom", value: { kind: "unitLit", unit: "undefined", type: UNDEFINED_T, loc }, type: DYN, loc };
|
|
1567
|
+
}
|
|
1414
1568
|
if (expr.questionDotToken && expr.name.text === "electron") {
|
|
1415
1569
|
const processProperty = lowerer.lowerProcessProperty(expr);
|
|
1416
1570
|
if (processProperty)
|
|
@@ -1427,6 +1581,12 @@ function lowerExprInner(lowerer, expr) {
|
|
|
1427
1581
|
if (!expr.questionDotToken && isOptionalChainTail(lowerer, expr)) {
|
|
1428
1582
|
return lowerer.lowerOptionalChain(expr);
|
|
1429
1583
|
}
|
|
1584
|
+
// Known Web handles retain their declared surface even when their
|
|
1585
|
+
// storage uses checked values. Apply this before generic reads.
|
|
1586
|
+
lowerer.fenceStaticAbortControllerMemberRead(expr);
|
|
1587
|
+
lowerer.fenceStaticResponseMember(expr, "read");
|
|
1588
|
+
lowerer.fenceStaticHeadersMember(expr, "read");
|
|
1589
|
+
lowerer.fenceStaticReadableStreamMember(expr, "read");
|
|
1430
1590
|
// Island receiver: o.x is an engine property read (getProp may throw
|
|
1431
1591
|
// — reading off null/undefined — bridged catchably like everything
|
|
1432
1592
|
// at the boundary). `o?.x` arrives here too, through the chain
|
|
@@ -1475,15 +1635,6 @@ function lowerExprInner(lowerer, expr) {
|
|
|
1475
1635
|
}
|
|
1476
1636
|
return read;
|
|
1477
1637
|
}
|
|
1478
|
-
// `m.groups` on a match result whose regex is statically known:
|
|
1479
|
-
// the compile-time record projection over the honest slice (or
|
|
1480
|
-
// Node's undefined when the pattern has no named groups) — see
|
|
1481
|
-
// lowerMatchGroupsRead.
|
|
1482
|
-
{
|
|
1483
|
-
const g = lowerMatchGroupsRead(lowerer, expr);
|
|
1484
|
-
if (g !== null)
|
|
1485
|
-
return g;
|
|
1486
|
-
}
|
|
1487
1638
|
// `m.index` on a for-of-over-matchAll binding reads the companion-
|
|
1488
1639
|
// index array at this iteration's cursor — always a number: every
|
|
1489
1640
|
// drained row matched, so the lib's `.index` is never undefined
|
|
@@ -1501,6 +1652,25 @@ function lowerExprInner(lowerer, expr) {
|
|
|
1501
1652
|
};
|
|
1502
1653
|
}
|
|
1503
1654
|
}
|
|
1655
|
+
if ((expr.name.text === "index" || expr.name.text === "input") && !expr.questionDotToken) {
|
|
1656
|
+
const represented = tryLowerExpression(lowerer, expr.expression);
|
|
1657
|
+
if (represented && isMatchSliceType(lowerer, represented.type) &&
|
|
1658
|
+
(lowerer.isStdlibMember(expr) || isJsSourceFile(expr.getSourceFile()))) {
|
|
1659
|
+
const recv = represented;
|
|
1660
|
+
const read = { kind: "dynKeyGet", value: lowerer.coerceToExpected(recv, DYN),
|
|
1661
|
+
key: { kind: "strLit", value: expr.name.text, type: STRING, loc }, type: DYN, loc };
|
|
1662
|
+
return lowerer.maybeNarrow(read, expr);
|
|
1663
|
+
}
|
|
1664
|
+
}
|
|
1665
|
+
// `m.groups` on a match result whose regex is statically known:
|
|
1666
|
+
// the compile-time record projection over the honest slice (or
|
|
1667
|
+
// Node's undefined when the pattern has no named groups) — see
|
|
1668
|
+
// lowerMatchGroupsRead.
|
|
1669
|
+
{
|
|
1670
|
+
const g = lowerMatchGroupsRead(lowerer, expr);
|
|
1671
|
+
if (g !== null)
|
|
1672
|
+
return g;
|
|
1673
|
+
}
|
|
1504
1674
|
// `arguments.length` in a TYPED function whose signature is
|
|
1505
1675
|
// FIXED-ARITY (no optional/default/rest parameters): tsc enforces
|
|
1506
1676
|
// exact arity at every call site and call/apply/bind indirection is
|
|
@@ -1566,11 +1736,11 @@ function lowerExprInner(lowerer, expr) {
|
|
|
1566
1736
|
// resolve unconditionally — they run after the module evaluated,
|
|
1567
1737
|
// the same approximation identifier exports already make. Every
|
|
1568
1738
|
// other module.exports read keeps its existing story.
|
|
1569
|
-
if (!expr.questionDotToken && isModuleExportsAccess(expr) && isCjsJsFile(expr.getSourceFile())) {
|
|
1739
|
+
if (!expr.questionDotToken && isModuleExportsAccess(expr) && isCjsJsFile(expr.getSourceFile(), lowerer.program)) {
|
|
1570
1740
|
const whole = cjsClassExprWholeExportOf(expr.getSourceFile());
|
|
1571
1741
|
if (whole) {
|
|
1572
1742
|
let inBody = false;
|
|
1573
|
-
for (let p = expr.parent; !ts.isSourceFile(p); p = p.parent) {
|
|
1743
|
+
for (let p = expr.parent; p !== undefined && !ts.isSourceFile(p); p = p.parent) {
|
|
1574
1744
|
if (ts.isFunctionLike(p) || ts.isClassDeclaration(p) || ts.isClassExpression(p)) {
|
|
1575
1745
|
inBody = true;
|
|
1576
1746
|
break;
|
|
@@ -1598,7 +1768,7 @@ function lowerExprInner(lowerer, expr) {
|
|
|
1598
1768
|
const writePos = ts.isBinaryExpression(expr.parent) &&
|
|
1599
1769
|
expr.parent.left === expr &&
|
|
1600
1770
|
expr.parent.operatorToken.kind === ts.SyntaxKind.EqualsToken;
|
|
1601
|
-
if (!expr.questionDotToken && cjsMemberRecv && !writePos && isCjsJsFile(sf)) {
|
|
1771
|
+
if (!expr.questionDotToken && cjsMemberRecv && !writePos && isCjsJsFile(sf, lowerer.program)) {
|
|
1602
1772
|
const sym = lowerer.checker.getSymbolAtLocation(expr.name) ??
|
|
1603
1773
|
lowerer.cjsModuleExportSymbol(sf, expr.name.text);
|
|
1604
1774
|
const resolved = sym !== undefined && sym.flags & ts.SymbolFlags.Alias
|
|
@@ -1720,6 +1890,14 @@ function lowerExprInner(lowerer, expr) {
|
|
|
1720
1890
|
isJsSourceFile(expr.getSourceFile())) {
|
|
1721
1891
|
const canonical = stdlibGlobalNameOf(lowerer, expr);
|
|
1722
1892
|
if (canonical !== null) {
|
|
1893
|
+
if (!lowerer.dynamic && canonical === "globalThis")
|
|
1894
|
+
return lowerGlobalValue(lowerer, expr);
|
|
1895
|
+
if (!lowerer.dynamic && canonical === "ArrayBuffer")
|
|
1896
|
+
return { kind: "libCall", fn: "arrayBuffer.constructor", args: [], type: DYN, loc };
|
|
1897
|
+
if (!lowerer.dynamic && canonical === "fetch")
|
|
1898
|
+
return { kind: "libCall", fn: "fetch.function", args: [], type: DYN, loc };
|
|
1899
|
+
if (!lowerer.dynamic && Object.values(BYTES_ELEMENT_NAME).includes(canonical))
|
|
1900
|
+
return { kind: "libCall", fn: "bytes.constructor", args: [{ kind: "strLit", value: canonical, type: STRING, loc }], type: DYN, loc };
|
|
1723
1901
|
return { kind: "strLit", value: `[builtin ${canonical}]`, type: STRING, loc };
|
|
1724
1902
|
}
|
|
1725
1903
|
}
|
|
@@ -1743,6 +1921,7 @@ function lowerExprInner(lowerer, expr) {
|
|
|
1743
1921
|
// instead.
|
|
1744
1922
|
if ((isJsSourceFile(expr.getSourceFile()) &&
|
|
1745
1923
|
(lowerer.mapTypeOf(lowerer.typeOf(expr.expression)) === null ||
|
|
1924
|
+
(lowerer.typeOf(expr.expression).flags & ts.TypeFlags.Never) !== 0 ||
|
|
1746
1925
|
// A never-tainted receiver type maps (never rides as f64) but
|
|
1747
1926
|
// its VALUE lowered checked-dynamic — same dyn read.
|
|
1748
1927
|
neverTaintedJsType(lowerer, expr.expression, lowerer.typeOf(expr.expression)))) ||
|
|
@@ -1756,6 +1935,11 @@ function lowerExprInner(lowerer, expr) {
|
|
|
1756
1935
|
const opt = hasOptionalChainGuard(expr.expression);
|
|
1757
1936
|
return lowerer.maybeNarrow({ kind: "dynKeyGet", key, ...(opt ? { optional: true } : {}), value: recv, type: DYN, loc }, expr);
|
|
1758
1937
|
}
|
|
1938
|
+
if (expr.name.text === "length" && (recv.type.kind === "array" || recv.type.kind === "string")) {
|
|
1939
|
+
return recv.type.kind === "array"
|
|
1940
|
+
? { kind: "arrIntrinsic", method: "length", receiver: recv, args: [], type: F64, loc }
|
|
1941
|
+
: { kind: "strIntrinsic", method: "length", receiver: recv, args: [], type: F64, loc };
|
|
1942
|
+
}
|
|
1759
1943
|
}
|
|
1760
1944
|
// `f.name` / `f.length` / own properties on a FUNCTION-typed JS
|
|
1761
1945
|
// value (the mustCall wrapper's function-instance members): read
|
|
@@ -1764,11 +1948,24 @@ function lowerExprInner(lowerer, expr) {
|
|
|
1764
1948
|
// best-effort static name and the declared arity; anything else is
|
|
1765
1949
|
// the own-property answer, undefined. Function.prototype METHOD
|
|
1766
1950
|
// names stay fenced (a stored-member undefined would mis-answer
|
|
1767
|
-
// `f.call` as a value).
|
|
1768
|
-
|
|
1769
|
-
|
|
1951
|
+
// `f.call` as a value). Static callable values keep the existing TS fence; checked callable
|
|
1952
|
+
// storage can expose its own name and length in both source languages.
|
|
1953
|
+
if (!["apply", "bind", "call", "toString", "caller", "arguments"].includes(expr.name.text) &&
|
|
1954
|
+
(isJsSourceFile(expr.getSourceFile()) || expr.name.text === "name" || expr.name.text === "length" || expr.name.text === "constructor")) {
|
|
1770
1955
|
const probed = tryLowerExpression(lowerer, expr.expression);
|
|
1771
|
-
|
|
1956
|
+
// Inferred callable returns can use checked storage even when the
|
|
1957
|
+
// checker still describes a function. Read its actual dyn value;
|
|
1958
|
+
// the ambient Function member must not hide its own properties.
|
|
1959
|
+
const receiverType = lowerer.typeOf(expr.expression);
|
|
1960
|
+
const receiverSymbol = receiverType.getSymbol();
|
|
1961
|
+
const checkedFunction = lowerer.checker.getCallSignatures(receiverType).length > 0 ||
|
|
1962
|
+
(receiverSymbol?.name === "Function" && lowerer.isStdlibSymbol(receiverSymbol));
|
|
1963
|
+
if (probed?.type.kind === "dyn" && checkedFunction) {
|
|
1964
|
+
const key = { kind: "strLit", value: expr.name.text, type: STRING, loc: locOf(expr.name) };
|
|
1965
|
+
const opt = hasOptionalChainGuard(expr.expression);
|
|
1966
|
+
return lowerer.maybeNarrow({ kind: "dynKeyGet", key, value: probed, ...(opt ? { optional: true } : {}), type: DYN, loc }, expr);
|
|
1967
|
+
}
|
|
1968
|
+
if ((isJsSourceFile(expr.getSourceFile()) || expr.name.text === "constructor") && probed?.type.kind === "func" &&
|
|
1772
1969
|
canBoxFuncIntoDyn(probed.type, (id) => lowerer.shapes.get(id), (id) => lowerer.unions.get(id))) {
|
|
1773
1970
|
const fnName = jsFuncNameOf(expr.expression);
|
|
1774
1971
|
const boxed = {
|
|
@@ -1789,10 +1986,14 @@ function lowerExprInner(lowerer, expr) {
|
|
|
1789
1986
|
// is an ordinary recordGet — maybeNarrow then bridges the union slot
|
|
1790
1987
|
// to the checker's narrowed arm, exactly like a union field read.
|
|
1791
1988
|
if (expr.name.text === "done" || expr.name.text === "value") {
|
|
1792
|
-
const
|
|
1793
|
-
|
|
1794
|
-
|
|
1989
|
+
const receiverType = lowerer.typeOf(expr.expression);
|
|
1990
|
+
const recvSym = receiverType.getAliasSymbol()?.name === "IteratorResult" ? receiverType.getAliasSymbol() : receiverType.getSymbol();
|
|
1991
|
+
if ((recvSym?.name === "IteratorResult" || recvSym?.name === "IteratorYieldResult" || recvSym?.name === "IteratorReturnResult") &&
|
|
1992
|
+
lowerer.checker.declarationsOf(recvSym).some((d) => (ts.isInterfaceDeclaration(d) || ts.isTypeAliasDeclaration(d)) && lowerer.isStdlibFile(d.getSourceFile()))) {
|
|
1795
1993
|
const recv = lowerer.lowerExpr(expr.expression);
|
|
1994
|
+
if (recv.type.kind === "dyn")
|
|
1995
|
+
return lowerer.maybeNarrow({ kind: "dynKeyGet", value: recv,
|
|
1996
|
+
key: { kind: "strLit", value: expr.name.text, type: STRING, loc }, type: DYN, loc }, expr);
|
|
1796
1997
|
if (recv.type.kind === "record") {
|
|
1797
1998
|
const shape = lowerer.shapes.get(recv.type.shapeId);
|
|
1798
1999
|
const field = shape?.fields.find((f) => f.name === expr.name.text);
|
|
@@ -1823,7 +2024,10 @@ function lowerExprInner(lowerer, expr) {
|
|
|
1823
2024
|
// the generic dyn receiver path below. Provenance is limited to the
|
|
1824
2025
|
// node:util parseArgs family; every other stdlib member keeps its
|
|
1825
2026
|
// ordinary surface fence.
|
|
1826
|
-
|
|
2027
|
+
const segmentDataSymbol = lowerer.typeOf(expr.expression).getSymbol();
|
|
2028
|
+
const segmentDataProperty = segmentDataSymbol?.name === "SegmentData" && lowerer.isStdlibSymbol(segmentDataSymbol) &&
|
|
2029
|
+
["segment", "index", "input", "isWordLike"].includes(expr.name.text);
|
|
2030
|
+
if (isParseArgsDynProperty(lowerer, expr) || segmentDataProperty) {
|
|
1827
2031
|
const recv = lowerer.lowerExpr(expr.expression);
|
|
1828
2032
|
if (recv.type.kind === "dyn") {
|
|
1829
2033
|
const key = {
|
|
@@ -1857,10 +2061,6 @@ function lowerExprInner(lowerer, expr) {
|
|
|
1857
2061
|
// The lib fence's PROPERTY chokepoint: a stdlib-declared member that
|
|
1858
2062
|
// no lowering above claimed ([1,2].entries, Math.SQRT2, Promise.all,
|
|
1859
2063
|
// 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
2064
|
lowerer.stdlibMemberFence(expr);
|
|
1865
2065
|
// The npm chokepoint: a member on a package-typed receiver in a
|
|
1866
2066
|
// static build — attributed to the package, like every other site.
|
|
@@ -1890,10 +2090,11 @@ function lowerExprInner(lowerer, expr) {
|
|
|
1890
2090
|
// on undefined/null receivers unless an earlier `?.` guards the
|
|
1891
2091
|
// chain. Scalar-narrowed occurrences bridge via maybeNarrow's
|
|
1892
2092
|
// validated dynCheck as usual.
|
|
1893
|
-
if (recvLowered.type.kind === "dyn")
|
|
2093
|
+
if (recvLowered.type.kind === "dyn" || isJsSourceFile(expr.getSourceFile()) &&
|
|
2094
|
+
["f64", "bool", "bigint", "string", "symbol"].includes(recvLowered.type.kind)) {
|
|
1894
2095
|
const key = { kind: "strLit", value: expr.name.text, type: STRING, loc: locOf(expr.name) };
|
|
1895
2096
|
const opt = hasOptionalChainGuard(expr.expression);
|
|
1896
|
-
return lowerer.maybeNarrow({ kind: "dynKeyGet", key, ...(opt ? { optional: true } : {}), value: recvLowered, type: DYN, loc }, expr);
|
|
2097
|
+
return lowerer.maybeNarrow({ kind: "dynKeyGet", key, ...(opt ? { optional: true } : {}), value: lowerer.coerceToExpected(recvLowered, DYN), type: DYN, loc }, expr);
|
|
1897
2098
|
}
|
|
1898
2099
|
// The lowered receiver is a RECORD the checker spelled wider —
|
|
1899
2100
|
// `s.match(re).groups.key` in a JS file: the checker says
|
|
@@ -1919,6 +2120,17 @@ function lowerExprInner(lowerer, expr) {
|
|
|
1919
2120
|
loc,
|
|
1920
2121
|
}, expr);
|
|
1921
2122
|
}
|
|
2123
|
+
// An inferred package-JS call specializes a parameter to the
|
|
2124
|
+
// caller's own record shape. Optional option names omitted by that
|
|
2125
|
+
// caller read undefined; the missing field is not a failed type
|
|
2126
|
+
// assertion. Known prototype members were handled/fenced above.
|
|
2127
|
+
if (recvShape && !recvShape.tuple && lowerer.implicitParamTypes !== null &&
|
|
2128
|
+
isJsSourceFile(expr.getSourceFile()) && npmStaticPackageOfPath(expr.getSourceFile().fileName) !== null) {
|
|
2129
|
+
return {
|
|
2130
|
+
kind: "seqExpr", stmts: [{ kind: "exprStmt", expr: recvLowered, loc }],
|
|
2131
|
+
result: dynUndefinedExpr(loc), type: DYN, loc,
|
|
2132
|
+
};
|
|
2133
|
+
}
|
|
1922
2134
|
}
|
|
1923
2135
|
// An ABSTRACT property through an abstract-typed receiver: the
|
|
1924
2136
|
// declaration is erased at runtime — Node defines no field for it,
|
|
@@ -1928,6 +2140,29 @@ function lowerExprInner(lowerer, expr) {
|
|
|
1928
2140
|
if (recvLowered.type.kind === "object" && abstractPropertyDeclOf(lowerer, expr)) {
|
|
1929
2141
|
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
2142
|
}
|
|
2143
|
+
// Array reads may add an undefined arm even when the checker still
|
|
2144
|
+
// reports a class. Its property bag must remain reachable, while
|
|
2145
|
+
// an absent element takes the normal checked-value TypeError.
|
|
2146
|
+
const nativeReceiver = lowerer.stripUndefinedArm(recvLowered.type);
|
|
2147
|
+
if (ts.isPrivateIdentifier(expr.name) && recvLowered.type.kind === "object") {
|
|
2148
|
+
const field = lowerer.classes.get(recvLowered.type.className)?.fields.get(expr.name.text);
|
|
2149
|
+
if (field)
|
|
2150
|
+
return { kind: "fieldGet", obj: recvLowered, className: recvLowered.type.className,
|
|
2151
|
+
field: expr.name.text, type: field, loc };
|
|
2152
|
+
}
|
|
2153
|
+
if (nativeReceiver.kind === "object" && nativeReceiver.className === "%Error") {
|
|
2154
|
+
return lowerer.maybeNarrow({ kind: "dynKeyGet", value: lowerer.coerceToExpected(recvLowered, DYN),
|
|
2155
|
+
key: { kind: "strLit", value: expr.name.text, type: STRING, loc }, type: DYN, loc }, expr);
|
|
2156
|
+
}
|
|
2157
|
+
if (isDynTypedRefType(nativeReceiver)) {
|
|
2158
|
+
const info = lowerer.classes.get(nativeReceiver.className);
|
|
2159
|
+
if (info && !info.def.runtime && !info.builtinError && !info.builtinEmitter && !info.builtinStream) {
|
|
2160
|
+
return lowerer.maybeNarrow({
|
|
2161
|
+
kind: "dynKeyGet", value: lowerer.coerceToExpected(recvLowered, DYN),
|
|
2162
|
+
key: { kind: "strLit", value: expr.name.text, type: STRING, loc }, type: DYN, loc,
|
|
2163
|
+
}, expr);
|
|
2164
|
+
}
|
|
2165
|
+
}
|
|
1931
2166
|
lowerer.unsupported("SC1090", expr, `reading '${expr.name.text}' from a value of type '${lowerer.checker.typeToString(lowerer.typeOf(expr.expression))}'`);
|
|
1932
2167
|
}
|
|
1933
2168
|
// Meta-properties, named: `new.target` reflects HOW a function was
|
|
@@ -1944,7 +2179,7 @@ function lowerExprInner(lowerer, expr) {
|
|
|
1944
2179
|
const entry = UNSUPPORTED_EXPR[expr.kind];
|
|
1945
2180
|
if (entry)
|
|
1946
2181
|
lowerer.unsupported(entry.code, expr, entry.feature);
|
|
1947
|
-
lowerer.unsupported("SC1090", expr, `syntax '${ts.
|
|
2182
|
+
lowerer.unsupported("SC1090", expr, `syntax '${ts.syntaxKindName(expr.kind)}'`);
|
|
1948
2183
|
}
|
|
1949
2184
|
function moduleFileName(lowerer, sf) {
|
|
1950
2185
|
return lowerer.targetPlatform === "wasi"
|
|
@@ -1952,7 +2187,7 @@ function moduleFileName(lowerer, sf) {
|
|
|
1952
2187
|
: sf.fileName;
|
|
1953
2188
|
}
|
|
1954
2189
|
function importMetaProperty(lowerer, expr) {
|
|
1955
|
-
if (
|
|
2190
|
+
if (!ts.isMetaProperty(expr.expression))
|
|
1956
2191
|
return null;
|
|
1957
2192
|
const meta = expr.expression;
|
|
1958
2193
|
if (meta.keywordToken !== ts.SyntaxKind.ImportKeyword || meta.name.text !== "meta")
|
|
@@ -1960,6 +2195,8 @@ function importMetaProperty(lowerer, expr) {
|
|
|
1960
2195
|
const sf = expr.getSourceFile();
|
|
1961
2196
|
const fileName = moduleFileName(lowerer, sf);
|
|
1962
2197
|
switch (expr.name.text) {
|
|
2198
|
+
case "env":
|
|
2199
|
+
return dynUndefinedExpr(locOf(expr));
|
|
1963
2200
|
case "url":
|
|
1964
2201
|
return {
|
|
1965
2202
|
kind: "strLit",
|
|
@@ -1983,11 +2220,10 @@ function importMetaProperty(lowerer, expr) {
|
|
|
1983
2220
|
return null;
|
|
1984
2221
|
}
|
|
1985
2222
|
}
|
|
1986
|
-
/** `c ? a : b
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
function lowerTernary(lowerer, expr, expected) {
|
|
2223
|
+
/** `c ? a : b`. An explicit destination constructs each branch at that
|
|
2224
|
+
* layout before joining it. Without one, retain the expression's inferred
|
|
2225
|
+
* type and the contextual/sibling rules below. */
|
|
2226
|
+
export function lowerTernary(lowerer, expr, expected) {
|
|
1991
2227
|
const loc = locOf(expr);
|
|
1992
2228
|
// `Array.isArray(x) ? x : [x]` over a `T | readonly T[]` union: tsc
|
|
1993
2229
|
// narrows the TRUE branch to `any[]` (maybeNarrow's isArray bridge
|
|
@@ -2041,7 +2277,16 @@ function lowerTernary(lowerer, expr, expected) {
|
|
|
2041
2277
|
// literals) sit untaken in compiled JS. A boolLit carries no
|
|
2042
2278
|
// effects, so dropping the condition read loses nothing.
|
|
2043
2279
|
if (cond.kind === "boolLit") {
|
|
2044
|
-
return lowerer.
|
|
2280
|
+
return lowerer.lowerExprExpecting(cond.value ? expr.whenTrue : expr.whenFalse, expected);
|
|
2281
|
+
}
|
|
2282
|
+
if (expected) {
|
|
2283
|
+
// Construct fresh literals in their actual destination. Joining
|
|
2284
|
+
// inferred layouts first can erase the literal discriminator or
|
|
2285
|
+
// choose an empty array's uninhabited element representation.
|
|
2286
|
+
// Runtime-optional proofs apply only within the selected true arm.
|
|
2287
|
+
const then = withRuntimeOptionalNarrowed(lowerer, runtimeOptionalTrueIds(lowerer, expr.condition), () => lowerer.lowerExprExpecting(expr.whenTrue, expected));
|
|
2288
|
+
const else_ = lowerer.lowerExprExpecting(expr.whenFalse, expected);
|
|
2289
|
+
return { kind: "ternary", cond, then, else_, type: expected, loc };
|
|
2045
2290
|
}
|
|
2046
2291
|
const ctxTs = lowerer.checker.getContextualType(expr);
|
|
2047
2292
|
const ctxMapped = ctxTs ? lowerer.mapTypeOf(ctxTs) : null;
|
|
@@ -2058,7 +2303,7 @@ function lowerTernary(lowerer, expr, expected) {
|
|
|
2058
2303
|
// lowerArrayLiteral, and just as unambiguous — tsc already typed the
|
|
2059
2304
|
// whole ternary by the filled arm alone. Both arms empty stays
|
|
2060
2305
|
// fenced (no element type exists anywhere).
|
|
2061
|
-
const ctxArray =
|
|
2306
|
+
const ctxArray = ctxMapped?.kind === "array" ? ctxMapped : null;
|
|
2062
2307
|
const armLiteral = (e) => {
|
|
2063
2308
|
let x = e;
|
|
2064
2309
|
while (ts.isParenthesizedExpression(x))
|
|
@@ -2082,7 +2327,21 @@ function lowerTernary(lowerer, expr, expected) {
|
|
|
2082
2327
|
}
|
|
2083
2328
|
return false;
|
|
2084
2329
|
};
|
|
2330
|
+
// Inferred signature tables often join a populated argument array
|
|
2331
|
+
// with nested empty-array branches. Carry the whole record layout
|
|
2332
|
+
// through those branches before constructing their fresh fields.
|
|
2333
|
+
const ownJoin = lowerer.mapTypeOf(lowerer.typeOf(expr));
|
|
2334
|
+
const recordJoin = ctxMapped?.kind === "record" ? ctxMapped :
|
|
2335
|
+
ownJoin?.kind === "record" ? ownJoin : undefined;
|
|
2085
2336
|
const lowerArm = (e, siblingType) => {
|
|
2337
|
+
let fresh = e;
|
|
2338
|
+
while (ts.isParenthesizedExpression(fresh))
|
|
2339
|
+
fresh = fresh.expression;
|
|
2340
|
+
if (recordJoin && ts.isConditionalExpression(fresh))
|
|
2341
|
+
return lowerTernary(lowerer, fresh, recordJoin);
|
|
2342
|
+
if (recordJoin && ts.isObjectLiteralExpression(fresh)) {
|
|
2343
|
+
return lowerer.lowerObjectLiteral(fresh, recordJoin);
|
|
2344
|
+
}
|
|
2086
2345
|
const lit = armLiteral(e);
|
|
2087
2346
|
if (lit && ctxArray)
|
|
2088
2347
|
return lowerer.lowerArrayLiteral(lit, ctxArray);
|
|
@@ -2135,11 +2394,14 @@ function lowerTernary(lowerer, expr, expected) {
|
|
|
2135
2394
|
const lowerTrueArm = () => withRuntimeOptionalNarrowed(lowerer, runtimeTrueIds, () => lowerArm(expr.whenTrue, ownArrayJoin ?? undefined));
|
|
2136
2395
|
if (!ctxArray && emptyUntypedArrayArm(expr.whenTrue) && !emptyUntypedArrayArm(expr.whenFalse)) {
|
|
2137
2396
|
elseRaw = lowerer.lowerExpr(expr.whenFalse);
|
|
2138
|
-
thenRaw = withRuntimeOptionalNarrowed(lowerer, runtimeTrueIds, () => lowerArm(expr.whenTrue, elseRaw.type));
|
|
2397
|
+
thenRaw = withRuntimeOptionalNarrowed(lowerer, runtimeTrueIds, () => lowerArm(expr.whenTrue, elseRaw.type.kind === "dyn" && ownArrayJoin ? ownArrayJoin : elseRaw.type));
|
|
2139
2398
|
}
|
|
2140
2399
|
else {
|
|
2141
2400
|
thenRaw = lowerTrueArm();
|
|
2142
|
-
|
|
2401
|
+
// A checked-dynamic producer (Object.entries(...).filter(...))
|
|
2402
|
+
// can still have a precise checker array type. Empty literal arms
|
|
2403
|
+
// must adopt that element layout before the checked array boundary.
|
|
2404
|
+
elseRaw = lowerArm(expr.whenFalse, thenRaw.type.kind === "dyn" && ownArrayJoin ? ownArrayJoin : thenRaw.type);
|
|
2143
2405
|
}
|
|
2144
2406
|
// The ternary's IR type is normally the checker's own: it collapses
|
|
2145
2407
|
// same-kind literal unions ("a" | "b" → string) and forms tagged
|
|
@@ -2211,14 +2473,28 @@ function lowerTernary(lowerer, expr, expected) {
|
|
|
2211
2473
|
}
|
|
2212
2474
|
}
|
|
2213
2475
|
}
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2476
|
+
// JavaScript function selection preserves the chosen closure's full
|
|
2477
|
+
// call ABI. A checker join can erase a rest/arguments pack even though
|
|
2478
|
+
// a later call supplies additional observable arguments.
|
|
2479
|
+
const callableJoin = isJsSourceFile(expr.getSourceFile()) &&
|
|
2480
|
+
thenRaw.type.kind === "func" && elseRaw.type.kind === "func" &&
|
|
2481
|
+
!typeEquals(thenRaw.type, elseRaw.type) &&
|
|
2482
|
+
lowerer.dynConvertible(thenRaw.type) && lowerer.dynConvertible(elseRaw.type);
|
|
2483
|
+
// JavaScript inference can discard an initially-undefined field from
|
|
2484
|
+
// a conditional's type even after that field acquired other values.
|
|
2485
|
+
// A checked arm must retain its actual representation at the join.
|
|
2486
|
+
const checkedJsJoin = isJsSourceFile(expr.getSourceFile()) &&
|
|
2487
|
+
(thenRaw.type.kind === "dyn" || elseRaw.type.kind === "dyn") &&
|
|
2488
|
+
lowerer.dynConvertible(thenRaw.type) && lowerer.dynConvertible(elseRaw.type);
|
|
2489
|
+
const type = callableJoin || checkedJsJoin ? DYN :
|
|
2490
|
+
thenRaw.type.kind === "array" && typeEquals(thenRaw.type, elseRaw.type)
|
|
2491
|
+
? thenRaw.type
|
|
2492
|
+
: dynJoin
|
|
2493
|
+
? DYN
|
|
2494
|
+
: (anyJoin ??
|
|
2495
|
+
(useCtx && ctxMapped
|
|
2496
|
+
? ctxMapped
|
|
2497
|
+
: lowerer.irTypeOf(expr)));
|
|
2222
2498
|
// Each arm flows into the ternary's type through the slot-coercion
|
|
2223
2499
|
// path: union arms wrap, dyn slots reject the non-dyn arm with
|
|
2224
2500
|
// SC1101, mismatched record shapes get SC2002; coerceInto is
|
|
@@ -2247,6 +2523,16 @@ function lowerTernary(lowerer, expr, expected) {
|
|
|
2247
2523
|
* unrepresentable without a re-tag), or to nothing (`never` in an
|
|
2248
2524
|
* exhaustive default) leaves the expression union-typed. */
|
|
2249
2525
|
export function maybeNarrow(lowerer, expr, node) {
|
|
2526
|
+
if ((expr.type.kind === "union" || expr.type.kind === "dyn") &&
|
|
2527
|
+
isJsSourceFile(node.getSourceFile()) && ts.isPropertyAccessExpression(node)) {
|
|
2528
|
+
const symbol = lowerer.checker.getSymbolAtLocation(node.name);
|
|
2529
|
+
if (symbol && lowerer.checker.declarationsOf(symbol).some((decl) => ts.isPropertyDeclaration(decl) && !decl.initializer && !decl.type &&
|
|
2530
|
+
isJsSourceFile(decl.getSourceFile()))) {
|
|
2531
|
+
// Inference describes later writes, not the initial undefined.
|
|
2532
|
+
// A use that requires the inferred type must validate the value.
|
|
2533
|
+
return expr.type.kind === "dyn" ? expr : { kind: "dynFrom", value: expr, type: DYN, loc: expr.loc };
|
|
2534
|
+
}
|
|
2535
|
+
}
|
|
2250
2536
|
// A dyn read tsc narrowed to a SCALAR (a typeof test proved the kind):
|
|
2251
2537
|
// bridge with a VALIDATED extraction — dynCheck, the checked-cast
|
|
2252
2538
|
// machinery — rather than a trusted one. After the guard the check
|
|
@@ -2254,15 +2540,45 @@ export function maybeNarrow(lowerer, expr, node) {
|
|
|
2254
2540
|
// instead of misreading the payload (the dyn boundary's usual stance).
|
|
2255
2541
|
// Object/array narrowings stay dyn-typed and keep their fences.
|
|
2256
2542
|
if (expr.type.kind === "dyn") {
|
|
2543
|
+
if (isJsSourceFile(node.getSourceFile()) && ts.isIdentifier(node)) {
|
|
2544
|
+
const symbol = lowerer.resolveValueSymbol(node);
|
|
2545
|
+
if (symbol && lowerer.checker.declarationsOf(symbol).some((decl) => ts.isVariableDeclaration(decl) && jsBindingHasOpenWrites(lowerer, decl)))
|
|
2546
|
+
return expr;
|
|
2547
|
+
if (symbol && lowerer.checker.declarationsOf(symbol).some((decl) => ts.isParameter(decl) && decl.initializer && !decl.type &&
|
|
2548
|
+
!/@(?:param|type)\b/.test(decl.getSourceFile().text.slice(decl.parent?.pos ?? decl.pos, decl.getStart()))))
|
|
2549
|
+
return expr;
|
|
2550
|
+
if (symbol && lowerer.checker.declarationsOf(symbol).some((decl) => {
|
|
2551
|
+
if (!ts.isParameter(decl) || decl.type)
|
|
2552
|
+
return false;
|
|
2553
|
+
const fn = decl.parent;
|
|
2554
|
+
if (!ts.isFunctionExpression(fn) && !ts.isArrowFunction(fn))
|
|
2555
|
+
return false;
|
|
2556
|
+
const assignment = fn.parent;
|
|
2557
|
+
return ts.isBinaryExpression(assignment) && assignment.right === fn &&
|
|
2558
|
+
ts.isPropertyAccessExpression(assignment.left) && assignment.left.name.text === "write" &&
|
|
2559
|
+
lowerer.isStdlibMember(assignment.left);
|
|
2560
|
+
}))
|
|
2561
|
+
return expr;
|
|
2562
|
+
}
|
|
2563
|
+
// Node's TTY declarations promise booleans/numbers even when a pipe
|
|
2564
|
+
// has no such property. Preserve the checked read for JS capability
|
|
2565
|
+
// probes instead of treating a declaration as a runtime type guard.
|
|
2566
|
+
if (isJsSourceFile(node.getSourceFile()) && ts.isPropertyAccessExpression(node) &&
|
|
2567
|
+
["isTTY", "columns", "rows"].includes(node.name.text) && lowerer.isStdlibMember(node))
|
|
2568
|
+
return expr;
|
|
2569
|
+
// A null-initialized JS field can later receive an object even when
|
|
2570
|
+
// checker flow calls its non-null branch never. The live checked
|
|
2571
|
+
// value remains authoritative; never is not a numeric guard.
|
|
2572
|
+
if (isJsSourceFile(node.getSourceFile()) && (lowerer.typeOf(node).flags & ts.TypeFlags.Never) !== 0)
|
|
2573
|
+
return expr;
|
|
2257
2574
|
const narrowed = lowerer.mapTypeOf(lowerer.typeOf(node));
|
|
2258
2575
|
if (narrowed &&
|
|
2259
|
-
(narrowed.kind === "f64" || narrowed.kind === "bool" || narrowed.kind === "string")) {
|
|
2576
|
+
(narrowed.kind === "f64" || narrowed.kind === "bool" || narrowed.kind === "string" || narrowed.kind === "bigint")) {
|
|
2260
2577
|
return { kind: "dynCheck", value: expr, type: narrowed, loc: expr.loc };
|
|
2261
2578
|
}
|
|
2262
|
-
// An
|
|
2263
|
-
//
|
|
2264
|
-
|
|
2265
|
-
if (narrowed?.kind === "bytes" && narrowed.elem === "u8") {
|
|
2579
|
+
// An instanceof narrow retains the native view after validating its
|
|
2580
|
+
// exact element kind. Buffer also matches Uint8Array.
|
|
2581
|
+
if (narrowed?.kind === "bytes" || narrowed?.kind === "url") {
|
|
2266
2582
|
return { kind: "dynCheck", value: expr, type: narrowed, loc: expr.loc };
|
|
2267
2583
|
}
|
|
2268
2584
|
// An `instanceof Error` narrow: the checked-dynamic tree's error encoding rebuilds a
|
|
@@ -2289,6 +2605,11 @@ export function maybeNarrow(lowerer, expr, node) {
|
|
|
2289
2605
|
}
|
|
2290
2606
|
if (expr.type.kind !== "union")
|
|
2291
2607
|
return expr;
|
|
2608
|
+
if (ts.isIdentifier(node)) {
|
|
2609
|
+
const symbol = lowerer.resolveValueSymbol(node);
|
|
2610
|
+
if (symbol && lowerer.unstableSwitchBindings.has(symbol))
|
|
2611
|
+
return expr;
|
|
2612
|
+
}
|
|
2292
2613
|
const narrowedTs = lowerer.typeOf(node);
|
|
2293
2614
|
const narrowed = lowerer.mapTypeOf(narrowedTs);
|
|
2294
2615
|
// `Array.isArray(u)` on a union with one readonly array/tuple arm: the
|
|
@@ -2315,14 +2636,32 @@ export function maybeNarrow(lowerer, expr, node) {
|
|
|
2315
2636
|
}
|
|
2316
2637
|
const tag = lowerer.armTag(expr.type.unionId, narrowed);
|
|
2317
2638
|
if (tag < 0) {
|
|
2318
|
-
//
|
|
2319
|
-
//
|
|
2320
|
-
//
|
|
2639
|
+
// The checker may refine fields inside a discriminated record too.
|
|
2640
|
+
// That creates a different mapped shape, but does not change the
|
|
2641
|
+
// stored payload layout. A required literal discriminator proves
|
|
2642
|
+
// which original arm to read; retain all of that arm's field types.
|
|
2643
|
+
const def = lowerer.unions.get(expr.type.unionId);
|
|
2644
|
+
if (narrowed.kind === "record" && def?.discriminant) {
|
|
2645
|
+
const property = lowerer.checker.getPropertyOfType(narrowedTs, def.discriminant.field);
|
|
2646
|
+
if (property && !(property.flags & (ts.SymbolFlags.Optional | ts.SymbolFlags.GetAccessor | ts.SymbolFlags.SetAccessor))) {
|
|
2647
|
+
const values = literalValues(lowerer.checker.getTypeOfSymbol(property));
|
|
2648
|
+
const arm = values && literalUnionArm(def, values, (id) => lowerer.shapes.get(id));
|
|
2649
|
+
if (arm) {
|
|
2650
|
+
return { kind: "unionNarrow", unionId: expr.type.unionId,
|
|
2651
|
+
tag: lowerer.armTag(expr.type.unionId, arm), value: expr, type: arm, loc: expr.loc };
|
|
2652
|
+
}
|
|
2653
|
+
}
|
|
2654
|
+
}
|
|
2655
|
+
// A base-class arm can narrow to a subclass after instanceof. The
|
|
2656
|
+
// payload retains its base-class tag even when the union also has
|
|
2657
|
+
// records or other classes. Require one unambiguous containing arm.
|
|
2321
2658
|
const arms = lowerer.unions.get(expr.type.unionId)?.arms ?? [];
|
|
2322
|
-
const valueTag =
|
|
2659
|
+
const valueTag = narrowed.kind === "object"
|
|
2660
|
+
? arms.findIndex((arm) => arm.kind === "object" && lowerer.isSubclassOf(narrowed.className, arm.className))
|
|
2661
|
+
: -1;
|
|
2323
2662
|
const valueType = arms[valueTag];
|
|
2324
2663
|
if (narrowed.kind === "object" && valueType?.kind === "object" &&
|
|
2325
|
-
arms.every((arm, i) => i === valueTag ||
|
|
2664
|
+
arms.every((arm, i) => i === valueTag || arm.kind !== "object" || !lowerer.isSubclassOf(narrowed.className, arm.className)) &&
|
|
2326
2665
|
lowerer.isSubclassOf(narrowed.className, valueType.className)) {
|
|
2327
2666
|
return {
|
|
2328
2667
|
kind: "downcast",
|
|
@@ -2701,7 +3040,26 @@ function lowerLooseNullCompare(lowerer, expr, loc) {
|
|
|
2701
3040
|
* results (several non-unit arms) and defaults that change the result
|
|
2702
3041
|
* type are fenced with narrow-first hints. */
|
|
2703
3042
|
export function lowerNullishCoalesce(lowerer, expr, loc) {
|
|
2704
|
-
|
|
3043
|
+
// `a ?? b ?? c` is a left-nested AST. Lower its spine bottom-up so
|
|
3044
|
+
// long dispatch chains do not retain a full expression/binary lowering
|
|
3045
|
+
// frame per operand in a native compiler. The pair lowering still
|
|
3046
|
+
// decides each result layout and preserves the lazy right operand.
|
|
3047
|
+
const parents = [];
|
|
3048
|
+
let first = expr;
|
|
3049
|
+
while (ts.isBinaryExpression(first.left) &&
|
|
3050
|
+
first.left.operatorToken.kind === ts.SyntaxKind.QuestionQuestionToken &&
|
|
3051
|
+
!lowerer.chainRecvByNode.has(first.left)) {
|
|
3052
|
+
parents.push(first);
|
|
3053
|
+
first = first.left;
|
|
3054
|
+
}
|
|
3055
|
+
let result = lowerNullishPair(lowerer, first, first === expr ? loc : locOf(first), lowerAbsenceProbe(lowerer, first.left) ?? lowerer.lowerExpr(first.left));
|
|
3056
|
+
for (let i = parents.length - 1; i >= 0; i--) {
|
|
3057
|
+
const parent = parents[i];
|
|
3058
|
+
result = lowerNullishPair(lowerer, parent, parent === expr ? loc : locOf(parent), result);
|
|
3059
|
+
}
|
|
3060
|
+
return result;
|
|
3061
|
+
}
|
|
3062
|
+
function lowerNullishPair(lowerer, expr, loc, left) {
|
|
2705
3063
|
if (left.type.kind === "dyn") {
|
|
2706
3064
|
// `a ?? b` on a CHECKED-DYNAMIC left: the deciding test is the
|
|
2707
3065
|
// runtime kind (scr_dyn_is_nullish — UNDEF/NULL take the default;
|
|
@@ -2730,8 +3088,18 @@ export function lowerNullishCoalesce(lowerer, expr, loc) {
|
|
|
2730
3088
|
lowerer.badType(expr.left, lowerer.typeOf(expr.left));
|
|
2731
3089
|
if (!def.arms.some(isUnitType))
|
|
2732
3090
|
return left;
|
|
2733
|
-
const type = lowerer.irTypeOf(expr);
|
|
2734
3091
|
const rest = def.arms.filter((a) => !isUnitType(a));
|
|
3092
|
+
// `optionalMap ?? new Map()` carries its element contract on the left;
|
|
3093
|
+
// the empty fallback may use it without constructing a Map<any, any>
|
|
3094
|
+
// arm that has no first-class union representation.
|
|
3095
|
+
const fresh = rest.length === 1 ? lowerer.emptyCollectionFor(expr.right, rest[0]) : null;
|
|
3096
|
+
if (fresh)
|
|
3097
|
+
return { kind: "nullish", left, right: fresh, type: fresh.type, loc };
|
|
3098
|
+
const type = lowerer.irTypeOf(expr);
|
|
3099
|
+
if (type.kind === "dyn" && lowerer.dynConvertible(left.type)) {
|
|
3100
|
+
return { kind: "nullish", left: lowerer.coerceToExpected(left, DYN),
|
|
3101
|
+
right: lowerer.lowerExprExpecting(expr.right, DYN), type: DYN, loc };
|
|
3102
|
+
}
|
|
2735
3103
|
if (typeEquals(type, left.type) || (rest.length === 1 && typeEquals(type, rest[0]))) {
|
|
2736
3104
|
const right = lowerer.lowerExprExpecting(expr.right, type);
|
|
2737
3105
|
return { kind: "nullish", left, right, type, loc };
|
|
@@ -2739,180 +3107,49 @@ export function lowerNullishCoalesce(lowerer, expr, loc) {
|
|
|
2739
3107
|
if (type.kind !== "union" && rest.length > 1 && rest.some((arm) => typeEquals(arm, type))) {
|
|
2740
3108
|
const helper = lowerer.narrowedArmHelper(left.type.unionId, type, loc);
|
|
2741
3109
|
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 };
|
|
3110
|
+
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
3111
|
}
|
|
2774
3112
|
}
|
|
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.
|
|
3113
|
+
// A default may change the union or widen its records. Test the
|
|
3114
|
+
// original tag before coercing: converting the whole left first can
|
|
3115
|
+
// lose nullish arms, and evaluating the default eagerly loses laziness.
|
|
2782
3116
|
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
|
-
}
|
|
3117
|
+
const retag = lowerer.unionRetagHelper(left.type.unionId, type.unionId, loc) ??
|
|
3118
|
+
lowerer.narrowedRetagHelper(expr.left, left.type.unionId, type.unionId, loc);
|
|
3119
|
+
if (retag !== null) {
|
|
3120
|
+
// Narrowing may exclude a record/class arm while storage retains
|
|
3121
|
+
// it. The checked retag traps that impossible arm, but only AFTER
|
|
3122
|
+
// the original nullish test: null/undefined must reach the lazy
|
|
3123
|
+
// default even when neither is representable in the result.
|
|
3124
|
+
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
3125
|
}
|
|
2855
3126
|
}
|
|
2856
3127
|
lowerer.unsupported("SC1090", expr, rest.length !== 1
|
|
2857
3128
|
? `'??' on '${lowerer.fmt(left.type)}' (the non-nullish result is a sub-union; check a discriminant field first)`
|
|
2858
3129
|
: `'??' 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
3130
|
}
|
|
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;
|
|
3131
|
+
/** Test the original storage before projecting its present value. Both
|
|
3132
|
+
* branches are lazy, and an effectful receiver is evaluated exactly once.
|
|
3133
|
+
* Keeping the original tags matters when the checker has removed an arm
|
|
3134
|
+
* or the default changes the union's layout. */
|
|
3135
|
+
function nullishBranches(lowerer, left, leftType, arms, type, loc, absent, present) {
|
|
3136
|
+
const stmts = [];
|
|
3137
|
+
let stable = left;
|
|
3138
|
+
if (!isSafeToRepeat(left)) {
|
|
3139
|
+
const local = lowerer.declareHiddenLocal("%nullish", leftType);
|
|
3140
|
+
stmts.push({ kind: "varDecl", localId: local.id, init: left, loc });
|
|
3141
|
+
stable = varRef(local.id, leftType, loc);
|
|
3142
|
+
}
|
|
3143
|
+
const unitTags = arms.flatMap((arm, tag) => isUnitType(arm) ? [tag] : []);
|
|
3144
|
+
let test = null;
|
|
3145
|
+
for (const tag of unitTags) {
|
|
3146
|
+
const part = { kind: "unionIsTag", unionId: leftType.unionId, tag, negated: false, value: stable, type: BOOL, loc };
|
|
3147
|
+
test = test === null ? part : { kind: "logical", op: "||", left: test, right: part, type: BOOL, loc };
|
|
3148
|
+
}
|
|
3149
|
+
if (test === null)
|
|
3150
|
+
throw new InternalCompilerError("nullish branch requires an absent arm");
|
|
3151
|
+
const result = { kind: "ternary", cond: test, then: absent(), else_: present(stable), type, loc };
|
|
3152
|
+
return stmts.length === 0 ? result : { kind: "seqExpr", stmts, result, type, loc };
|
|
2916
3153
|
}
|
|
2917
3154
|
/** A CONDITION-position expression: the result is consumed as a bool
|
|
2918
3155
|
* only, so `&&`/`||` descend recursively over ToBoolean'd operands —
|
|
@@ -2933,6 +3170,8 @@ export function lowerCondition(lowerer, expr) {
|
|
|
2933
3170
|
if (op === ts.SyntaxKind.AmpersandAmpersandToken || op === ts.SyntaxKind.BarBarToken) {
|
|
2934
3171
|
const isAnd = op === ts.SyntaxKind.AmpersandAmpersandToken;
|
|
2935
3172
|
const left = lowerer.lowerCondition(e.left);
|
|
3173
|
+
if (left.kind === "boolLit" && left.value !== isAnd)
|
|
3174
|
+
return left;
|
|
2936
3175
|
// The right operand evaluates only when the left already answered
|
|
2937
3176
|
// (true for &&, false for ||) — aliased-typeof narrows the left
|
|
2938
3177
|
// PROVES under that polarity hold while it lowers (`type ===
|
|
@@ -2949,6 +3188,8 @@ export function lowerCondition(lowerer, expr) {
|
|
|
2949
3188
|
lowerer.runtimeOptionalLocals.add(lowerer.runtimeOptionalRootOf(runtimeOptionalLocal));
|
|
2950
3189
|
}
|
|
2951
3190
|
});
|
|
3191
|
+
if (left.kind === "boolLit")
|
|
3192
|
+
return right;
|
|
2952
3193
|
return {
|
|
2953
3194
|
kind: "logical",
|
|
2954
3195
|
op: isAnd ? "&&" : "||",
|
|
@@ -2971,14 +3212,23 @@ function lowerPromiseThenPresence(lowerer, access, result) {
|
|
|
2971
3212
|
? (() => {
|
|
2972
3213
|
const arms = lowerer.unions.get(mapped.unionId)?.arms ?? [];
|
|
2973
3214
|
const promises = arms.filter((arm) => arm.kind === "promise");
|
|
2974
|
-
|
|
2975
|
-
|
|
3215
|
+
const promise = promises[0];
|
|
3216
|
+
return promises.length === 1 && promise?.kind === "promise" && arms.every((arm) => arm.kind === "promise" || isUnitType(arm))
|
|
3217
|
+
? promise
|
|
2976
3218
|
: null;
|
|
2977
3219
|
})()
|
|
2978
3220
|
: null;
|
|
2979
3221
|
if (promiseType === null)
|
|
2980
3222
|
return null;
|
|
2981
3223
|
let receiver = lowerer.lowerExpr(access.expression);
|
|
3224
|
+
if (receiver.type.kind === "union" && access.questionDotToken) {
|
|
3225
|
+
const arms = lowerer.unions.get(receiver.type.unionId)?.arms ?? [];
|
|
3226
|
+
if (arms.every((arm) => arm.kind === "promise" || isUnitType(arm))) {
|
|
3227
|
+
return nullishBranches(lowerer, receiver, receiver.type, arms, result.type, locOf(access), () => result.type.kind === "bool"
|
|
3228
|
+
? { kind: "boolLit", value: false, type: BOOL, loc: result.loc }
|
|
3229
|
+
: { kind: "strLit", value: "undefined", type: STRING, loc: result.loc }, () => result);
|
|
3230
|
+
}
|
|
3231
|
+
}
|
|
2982
3232
|
if (receiver.type.kind === "union") {
|
|
2983
3233
|
const optional = lowerer.runtimeOptionalPropertyReceiver(access.expression, receiver, promiseType, access.name.text);
|
|
2984
3234
|
if (optional !== null)
|
|
@@ -2990,6 +3240,29 @@ function lowerPromiseThenPresence(lowerer, access, result) {
|
|
|
2990
3240
|
receiver = { kind: "call", callee: helper, args: [receiver], type: promiseType, loc: receiver.loc };
|
|
2991
3241
|
}
|
|
2992
3242
|
}
|
|
3243
|
+
if (receiver.type.kind === "dyn") {
|
|
3244
|
+
// A specialized JS parameter can retain checked storage while JSDoc
|
|
3245
|
+
// describes a promise. Probe its actual value, preserving nullish
|
|
3246
|
+
// guards and own-property reads on ordinary thenable objects.
|
|
3247
|
+
const loc = locOf(access);
|
|
3248
|
+
const local = lowerer.declareHiddenLocal("%thenReceiver", DYN);
|
|
3249
|
+
const stable = varRef(local.id, DYN, loc);
|
|
3250
|
+
const member = {
|
|
3251
|
+
kind: "dynKeyGet", value: stable,
|
|
3252
|
+
key: { kind: "strLit", value: "then", type: STRING, loc },
|
|
3253
|
+
...(access.questionDotToken ? { optional: true } : {}), type: DYN, loc,
|
|
3254
|
+
};
|
|
3255
|
+
const other = result.type.kind === "bool"
|
|
3256
|
+
? { kind: "dynTest", test: "truthy", value: member, type: BOOL, loc }
|
|
3257
|
+
: { kind: "libCall", fn: "dyn.typeof", args: [member], type: STRING, loc };
|
|
3258
|
+
return {
|
|
3259
|
+
kind: "seqExpr", stmts: [{ kind: "varDecl", localId: local.id, init: receiver, loc }],
|
|
3260
|
+
result: {
|
|
3261
|
+
kind: "ternary", cond: { kind: "dynTest", test: "promise", value: stable, type: BOOL, loc },
|
|
3262
|
+
then: result, else_: other, type: result.type, loc,
|
|
3263
|
+
}, type: result.type, loc,
|
|
3264
|
+
};
|
|
3265
|
+
}
|
|
2993
3266
|
if (receiver.type.kind !== "promise")
|
|
2994
3267
|
return null;
|
|
2995
3268
|
if (isSafeToDiscard(receiver))
|
|
@@ -3012,15 +3285,107 @@ function runtimeOptionalLocalOf(lowerer, node) {
|
|
|
3012
3285
|
return local && lowerer.runtimeOptionalLocals.has(lowerer.runtimeOptionalRootOf(local)) ? local : null;
|
|
3013
3286
|
}
|
|
3014
3287
|
export function runtimeOptionalTrueIds(lowerer, node) {
|
|
3015
|
-
|
|
3016
|
-
|
|
3017
|
-
|
|
3018
|
-
|
|
3019
|
-
|
|
3020
|
-
|
|
3021
|
-
|
|
3022
|
-
const
|
|
3023
|
-
|
|
3288
|
+
return runtimeOptionalGuardIds(lowerer, node, true);
|
|
3289
|
+
}
|
|
3290
|
+
/** Presence proved by one outcome of a boolean guard. Negation flips
|
|
3291
|
+
* the outcome; all operands of a true AND or false OR have that outcome.
|
|
3292
|
+
* Assignments invalidate a syntactic proof even when checker types hide
|
|
3293
|
+
* the undefined produced by an out-of-bounds read. */
|
|
3294
|
+
export function runtimeOptionalGuardIds(lowerer, node, truthy) {
|
|
3295
|
+
const candidates = [];
|
|
3296
|
+
const collect = (expr, outcome) => {
|
|
3297
|
+
if (ts.isParenthesizedExpression(expr))
|
|
3298
|
+
return collect(expr.expression, outcome);
|
|
3299
|
+
if (ts.isPrefixUnaryExpression(expr) && expr.operator === ts.SyntaxKind.ExclamationToken) {
|
|
3300
|
+
return collect(expr.operand, outcome === null ? null : !outcome);
|
|
3301
|
+
}
|
|
3302
|
+
if (ts.isBinaryExpression(expr) &&
|
|
3303
|
+
(outcome === true && expr.operatorToken.kind === ts.SyntaxKind.AmpersandAmpersandToken ||
|
|
3304
|
+
outcome === false && expr.operatorToken.kind === ts.SyntaxKind.BarBarToken)) {
|
|
3305
|
+
collect(expr.left, outcome);
|
|
3306
|
+
collect(expr.right, outcome);
|
|
3307
|
+
}
|
|
3308
|
+
else if (ts.isPropertyAccessExpression(expr) && !expr.questionDotToken) {
|
|
3309
|
+
// A completed ordinary member read checked its receiver even if
|
|
3310
|
+
// the property's value was falsy. Optional chains make no such
|
|
3311
|
+
// promise, and calls/captures retain their separate checked ABI.
|
|
3312
|
+
const local = runtimeOptionalLocalOf(lowerer, expr.expression);
|
|
3313
|
+
if (local !== null)
|
|
3314
|
+
candidates.push(local);
|
|
3315
|
+
else if (ts.isPropertyAccessExpression(expr.expression))
|
|
3316
|
+
collect(expr.expression, outcome);
|
|
3317
|
+
}
|
|
3318
|
+
else if (ts.isBinaryExpression(expr) &&
|
|
3319
|
+
expr.operatorToken.kind !== ts.SyntaxKind.AmpersandAmpersandToken &&
|
|
3320
|
+
expr.operatorToken.kind !== ts.SyntaxKind.BarBarToken &&
|
|
3321
|
+
expr.operatorToken.kind !== ts.SyntaxKind.QuestionQuestionToken) {
|
|
3322
|
+
// Both operands of a comparison/arithmetic operator are evaluated.
|
|
3323
|
+
// Only member reads prove presence; bare identifiers need truth.
|
|
3324
|
+
const member = (operand) => {
|
|
3325
|
+
while (ts.isParenthesizedExpression(operand))
|
|
3326
|
+
operand = operand.expression;
|
|
3327
|
+
if (ts.isPropertyAccessExpression(operand))
|
|
3328
|
+
collect(operand, outcome);
|
|
3329
|
+
};
|
|
3330
|
+
member(expr.left);
|
|
3331
|
+
member(expr.right);
|
|
3332
|
+
const op = expr.operatorToken.kind;
|
|
3333
|
+
const equal = op === ts.SyntaxKind.EqualsEqualsEqualsToken || op === ts.SyntaxKind.EqualsEqualsToken;
|
|
3334
|
+
const unequal = op === ts.SyntaxKind.ExclamationEqualsEqualsToken || op === ts.SyntaxKind.ExclamationEqualsToken;
|
|
3335
|
+
if ((equal && outcome === false) || (unequal && outcome === true)) {
|
|
3336
|
+
const present = (value, unit) => {
|
|
3337
|
+
while (ts.isParenthesizedExpression(unit))
|
|
3338
|
+
unit = unit.expression;
|
|
3339
|
+
const excludesUndefined = ts.isIdentifier(unit) && unit.text === "undefined" &&
|
|
3340
|
+
(lowerer.typeOf(unit).flags & ts.TypeFlags.Undefined) !== 0 ||
|
|
3341
|
+
unit.kind === ts.SyntaxKind.NullKeyword &&
|
|
3342
|
+
(op === ts.SyntaxKind.EqualsEqualsToken || op === ts.SyntaxKind.ExclamationEqualsToken);
|
|
3343
|
+
if (excludesUndefined) {
|
|
3344
|
+
const local = runtimeOptionalLocalOf(lowerer, value);
|
|
3345
|
+
if (local !== null)
|
|
3346
|
+
candidates.push(local);
|
|
3347
|
+
}
|
|
3348
|
+
};
|
|
3349
|
+
present(expr.left, expr.right);
|
|
3350
|
+
present(expr.right, expr.left);
|
|
3351
|
+
}
|
|
3352
|
+
}
|
|
3353
|
+
else if (outcome) {
|
|
3354
|
+
const local = runtimeOptionalLocalOf(lowerer, expr);
|
|
3355
|
+
if (local !== null)
|
|
3356
|
+
candidates.push(local);
|
|
3357
|
+
}
|
|
3358
|
+
};
|
|
3359
|
+
collect(node, truthy);
|
|
3360
|
+
if (candidates.length === 0)
|
|
3361
|
+
return candidates;
|
|
3362
|
+
const assigned = new Set();
|
|
3363
|
+
const visit = (part) => {
|
|
3364
|
+
if (ts.isFunctionLike(part))
|
|
3365
|
+
return;
|
|
3366
|
+
let target = null;
|
|
3367
|
+
if (ts.isBinaryExpression(part) && part.operatorToken.kind >= ts.SyntaxKind.FirstAssignment &&
|
|
3368
|
+
part.operatorToken.kind <= ts.SyntaxKind.LastAssignment)
|
|
3369
|
+
target = part.left;
|
|
3370
|
+
if ((ts.isPrefixUnaryExpression(part) || ts.isPostfixUnaryExpression(part)) &&
|
|
3371
|
+
(part.operator === ts.SyntaxKind.PlusPlusToken || part.operator === ts.SyntaxKind.MinusMinusToken))
|
|
3372
|
+
target = part.operand;
|
|
3373
|
+
if (target !== null) {
|
|
3374
|
+
// Destructuring assignments can contain several identifiers.
|
|
3375
|
+
const mark = (name) => {
|
|
3376
|
+
if (ts.isIdentifier(name)) {
|
|
3377
|
+
const local = lowerer.resolveLocal(name);
|
|
3378
|
+
if (local)
|
|
3379
|
+
assigned.add(lowerer.runtimeOptionalRootOf(local));
|
|
3380
|
+
}
|
|
3381
|
+
ts.forEachChild(name, mark);
|
|
3382
|
+
};
|
|
3383
|
+
mark(target);
|
|
3384
|
+
}
|
|
3385
|
+
ts.forEachChild(part, visit);
|
|
3386
|
+
};
|
|
3387
|
+
visit(node);
|
|
3388
|
+
return candidates.filter((local) => !assigned.has(lowerer.runtimeOptionalRootOf(local)));
|
|
3024
3389
|
}
|
|
3025
3390
|
export function withRuntimeOptionalNarrowed(lowerer, locals, fn) {
|
|
3026
3391
|
if (locals.length === 0)
|
|
@@ -3102,8 +3467,60 @@ export function eqComparableUnion(lowerer, unionId) {
|
|
|
3102
3467
|
* the receiver type AND the ambient-file provenance of the member are
|
|
3103
3468
|
* verified — the name alone proves nothing. */
|
|
3104
3469
|
export function lowerIntrinsicProperty(lowerer, expr) {
|
|
3470
|
+
for (const name of ["String", "Number", "Boolean", "BigInt"]) {
|
|
3471
|
+
if (!lowerer.chainBlocked(expr) && lowerer.isStdlibGlobal(expr, name)) {
|
|
3472
|
+
return primitiveCtorClosure(lowerer, name, locOf(expr), isJsSourceFile(expr.getSourceFile()));
|
|
3473
|
+
}
|
|
3474
|
+
}
|
|
3475
|
+
if (lowerer.stdlibGlobalMember(expr, "Reflect") === "ownKeys") {
|
|
3476
|
+
const value = lowerer.lowerNativeCallableValue({
|
|
3477
|
+
fn: "dyn.ownKeys", params: [DYN], result: DYN, valueParams: [{ mode: "required", type: DYN }],
|
|
3478
|
+
}, "Reflect.ownKeys", locOf(expr));
|
|
3479
|
+
return { kind: "dynFrom", value, fnName: "ownKeys", type: DYN, loc: locOf(expr) };
|
|
3480
|
+
}
|
|
3105
3481
|
if (lowerer.chainBlocked(expr))
|
|
3106
3482
|
return null;
|
|
3483
|
+
const builtinPrototype = builtinPrototypeMethod(lowerer, expr);
|
|
3484
|
+
if (builtinPrototype)
|
|
3485
|
+
return { kind: "libCall", fn: "dyn.builtinMethod", args: [
|
|
3486
|
+
{ kind: "strLit", value: builtinPrototype, type: STRING, loc: locOf(expr) },
|
|
3487
|
+
{ kind: "strLit", value: expr.name.text, type: STRING, loc: locOf(expr.name) },
|
|
3488
|
+
], type: DYN, loc: locOf(expr) };
|
|
3489
|
+
if (ts.isPropertyAccessExpression(expr.expression) &&
|
|
3490
|
+
lowerer.stdlibGlobalMember(expr.expression, "Array") === "prototype") {
|
|
3491
|
+
return { kind: "dynKeyGet", value: { kind: "libCall", fn: "dyn.arrayPrototype", args: [], type: DYN, loc: locOf(expr.expression) },
|
|
3492
|
+
key: { kind: "strLit", value: expr.name.text, type: STRING, loc: locOf(expr.name) }, type: DYN, loc: locOf(expr) };
|
|
3493
|
+
}
|
|
3494
|
+
if (expr.name.text === "apply" && ts.isPropertyAccessExpression(expr.expression) &&
|
|
3495
|
+
lowerer.stdlibGlobalMember(expr.expression, "Function") === "prototype")
|
|
3496
|
+
return { kind: "libCall", fn: "dyn.functionApply", args: [], type: DYN, loc: locOf(expr) };
|
|
3497
|
+
if (lowerer.stdlibGlobalMember(expr, "Array") === "prototype")
|
|
3498
|
+
return { kind: "libCall", fn: "dyn.arrayPrototype", args: [], type: DYN, loc: locOf(expr) };
|
|
3499
|
+
if (lowerer.stdlibGlobalMember(expr, "Object") === "prototype")
|
|
3500
|
+
return { kind: "libCall", fn: "dyn.objectPrototype", args: [], type: DYN, loc: locOf(expr) };
|
|
3501
|
+
if (lowerer.stdlibGlobalMember(expr, "Array") === "isArray")
|
|
3502
|
+
return lowerArrayIsArrayValue(lowerer, locOf(expr));
|
|
3503
|
+
if (lowerer.stdlibGlobalMember(expr, "Array") === "from")
|
|
3504
|
+
return lowerArrayFromValue(lowerer, locOf(expr));
|
|
3505
|
+
const stringMember = lowerer.stdlibGlobalMember(expr, "String");
|
|
3506
|
+
if (stringMember === "fromCharCode" || stringMember === "fromCodePoint")
|
|
3507
|
+
return lowerStringCodesValue(lowerer, stringMember, locOf(expr));
|
|
3508
|
+
const numberMember = lowerer.stdlibGlobalMember(expr, "Number");
|
|
3509
|
+
if (numberMember === "parseInt" || numberMember === "parseFloat")
|
|
3510
|
+
return lowerNumberParserValue(lowerer, numberMember, locOf(expr));
|
|
3511
|
+
if (lowerer.stdlibGlobalMember(expr, "Buffer") === "isBuffer")
|
|
3512
|
+
return lowerCheckedPredicateValue(lowerer, "Buffer.isBuffer", "buffer", locOf(expr));
|
|
3513
|
+
if (lowerer.stdlibGlobalMember(expr, "Date") === "now")
|
|
3514
|
+
return lowerer.lowerNativeCallableValue({
|
|
3515
|
+
fn: "date.now", params: [], result: F64, valueParams: [],
|
|
3516
|
+
}, "Date.now", locOf(expr));
|
|
3517
|
+
if (stdlibGlobalNameOf(lowerer, expr.expression) === "Object") {
|
|
3518
|
+
if (expr.name.text === "assign")
|
|
3519
|
+
return lowerObjectAssignValue(lowerer, locOf(expr));
|
|
3520
|
+
const fn = own(OBJECT_CALLABLE_VALUES, expr.name.text);
|
|
3521
|
+
if (fn)
|
|
3522
|
+
return lowerer.lowerNativeCallableValue(fn, `Object.${expr.name.text}`, locOf(expr));
|
|
3523
|
+
}
|
|
3107
3524
|
// A never-tainted JS receiver type lowered checked-dynamic
|
|
3108
3525
|
// (neverTaintedJsType — `cmd.length` on `const cmd = ['pwd', []]`):
|
|
3109
3526
|
// stand down so the dyn keyed read below the chain answers, instead
|
|
@@ -3137,8 +3554,19 @@ export function lowerIntrinsicProperty(lowerer, expr) {
|
|
|
3137
3554
|
if (kind === "child" ? !isChildSurfaceMember(lowerer, expr) : !tupleLengthOnArray && !lowerer.isStdlibMember(expr))
|
|
3138
3555
|
return null;
|
|
3139
3556
|
const name = expr.name.text;
|
|
3557
|
+
if (kind === "array" && name === "constructor" && isJsSourceFile(expr.getSourceFile())) {
|
|
3558
|
+
const receiver = lowerer.lowerExpr(expr.expression);
|
|
3559
|
+
if (receiver.type.kind === "dyn")
|
|
3560
|
+
return { kind: "dynKeyGet", value: receiver,
|
|
3561
|
+
key: { kind: "strLit", value: name, type: STRING, loc: locOf(expr) }, type: DYN, loc: locOf(expr) };
|
|
3562
|
+
// Ordinary arrays keep the existing Array builtin identity token.
|
|
3563
|
+
const result = { kind: "strLit", value: "[builtin Array]", type: STRING, loc: locOf(expr) };
|
|
3564
|
+
return { kind: "seqExpr", stmts: [{ kind: "exprStmt", expr: receiver, loc: locOf(expr) }], result, type: STRING, loc: locOf(expr) };
|
|
3565
|
+
}
|
|
3140
3566
|
const strictReceiver = (expected) => {
|
|
3141
3567
|
const value = lowerer.lowerExpr(expr.expression);
|
|
3568
|
+
if (value.type.kind === "dyn")
|
|
3569
|
+
return lowerer.coerceInto(expr.expression, value, expected);
|
|
3142
3570
|
return lowerer.runtimeOptionalPropertyReceiver(expr.expression, value, expected, name) ?? value;
|
|
3143
3571
|
};
|
|
3144
3572
|
if (kind === "child") {
|
|
@@ -3310,6 +3738,9 @@ export function lowerIntrinsicProperty(lowerer, expr) {
|
|
|
3310
3738
|
lowerer.noLowering(`URLSearchParams.${name}`, expr, "get, getAll, set, append, delete, has, sort, size, toString(), forEach, and for-of iteration are the supported URLSearchParams members", lowerer.checker.getSymbolAtLocation(expr.name));
|
|
3311
3739
|
}
|
|
3312
3740
|
if (kind === "regex") {
|
|
3741
|
+
if (name === "lastIndex") {
|
|
3742
|
+
return { kind: "regexIntrinsic", method: "lastIndex", receiver: strictReceiver({ kind: "regex" }), args: [], type: F64, loc: locOf(expr) };
|
|
3743
|
+
}
|
|
3313
3744
|
if (name === "source" || name === "flags") {
|
|
3314
3745
|
const receiver = strictReceiver({ kind: "regex" });
|
|
3315
3746
|
return { kind: "regexIntrinsic", method: name, receiver, args: [], type: STRING, loc: locOf(expr) };
|
|
@@ -3329,6 +3760,12 @@ export function lowerIntrinsicProperty(lowerer, expr) {
|
|
|
3329
3760
|
}
|
|
3330
3761
|
if (kind === "bytes") {
|
|
3331
3762
|
const loc = locOf(expr);
|
|
3763
|
+
if (name === "constructor") {
|
|
3764
|
+
const expected = lowerer.mapTypeOf(lowerer.typeOf(expr.expression));
|
|
3765
|
+
const receiver = expected?.kind === "bytes" ? strictReceiver(expected) : lowerer.lowerExpr(expr.expression);
|
|
3766
|
+
return { kind: "dynKeyGet", value: lowerer.coerceInto(expr.expression, receiver, DYN),
|
|
3767
|
+
key: { kind: "strLit", value: name, type: STRING, loc }, type: DYN, loc };
|
|
3768
|
+
}
|
|
3332
3769
|
if (name === "length" || name === "byteLength") {
|
|
3333
3770
|
const expected = lowerer.mapTypeOf(lowerer.typeOf(expr.expression));
|
|
3334
3771
|
let receiver = expected?.kind === "bytes"
|
|
@@ -3359,15 +3796,14 @@ export function lowerIntrinsicProperty(lowerer, expr) {
|
|
|
3359
3796
|
// SEMANTICS.md notes the divergence from Node's Buffer pooling),
|
|
3360
3797
|
// the view's real offset for a DataView. A runtime read, so the
|
|
3361
3798
|
// receiver's evaluation is never discarded.
|
|
3362
|
-
const
|
|
3799
|
+
const expected = lowerer.mapTypeOf(lowerer.typeOf(expr.expression));
|
|
3800
|
+
const receiver = expected?.kind === "bytes" ? strictReceiver(expected) : lowerer.lowerExpr(expr.expression);
|
|
3363
3801
|
return { kind: "bytesIntrinsic", method: "byteOffset", receiver, args: [], type: F64, loc };
|
|
3364
3802
|
}
|
|
3365
3803
|
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)");
|
|
3804
|
+
const expected = lowerer.mapTypeOf(lowerer.typeOf(expr.expression));
|
|
3805
|
+
const receiver = expected?.kind === "bytes" ? strictReceiver(expected) : lowerer.lowerExpr(expr.expression);
|
|
3806
|
+
return { kind: "bytesIntrinsic", method: "buffer", receiver, args: [], type: DYN, loc };
|
|
3371
3807
|
}
|
|
3372
3808
|
if (name === "slice" || name === "subarray" || name === "set" || name === "toString") {
|
|
3373
3809
|
lowerer.unsupported("SC1090", expr, `typed-array methods as values (call '${name}' directly)`);
|
|
@@ -3376,7 +3812,8 @@ export function lowerIntrinsicProperty(lowerer, expr) {
|
|
|
3376
3812
|
}
|
|
3377
3813
|
if (kind === "map") {
|
|
3378
3814
|
if (name === "size") {
|
|
3379
|
-
const
|
|
3815
|
+
const expected = lowerer.mapTypeOf(lowerer.typeOf(expr.expression));
|
|
3816
|
+
const receiver = expected?.kind === "map" ? strictReceiver(expected) : lowerer.lowerExpr(expr.expression);
|
|
3380
3817
|
return { kind: "mapIntrinsic", method: "size", receiver, args: [], type: F64, loc: locOf(expr) };
|
|
3381
3818
|
}
|
|
3382
3819
|
if (MAP_METHODS.has(name) || name === "forEach") {
|
|
@@ -3386,7 +3823,8 @@ export function lowerIntrinsicProperty(lowerer, expr) {
|
|
|
3386
3823
|
}
|
|
3387
3824
|
if (kind === "set") {
|
|
3388
3825
|
if (name === "size") {
|
|
3389
|
-
const
|
|
3826
|
+
const expected = lowerer.mapTypeOf(lowerer.typeOf(expr.expression));
|
|
3827
|
+
const receiver = expected?.kind === "set" ? strictReceiver(expected) : lowerer.lowerExpr(expr.expression);
|
|
3390
3828
|
return { kind: "setIntrinsic", method: "size", receiver, args: [], type: F64, loc: locOf(expr) };
|
|
3391
3829
|
}
|
|
3392
3830
|
if (SET_METHODS.has(name)) {
|
|
@@ -3398,9 +3836,10 @@ export function lowerIntrinsicProperty(lowerer, expr) {
|
|
|
3398
3836
|
const expected = kind === "string"
|
|
3399
3837
|
? STRING
|
|
3400
3838
|
: lowerer.mapTypeOf(lowerer.typeOf(expr.expression));
|
|
3401
|
-
let receiver =
|
|
3402
|
-
|
|
3403
|
-
|
|
3839
|
+
let receiver = lowerer.lowerExpr(expr.expression);
|
|
3840
|
+
if (receiver.type.kind !== "dyn" && expected && (expected.kind === "string" || expected.kind === "array")) {
|
|
3841
|
+
receiver = lowerer.runtimeOptionalPropertyReceiver(expr.expression, receiver, expected, name) ?? receiver;
|
|
3842
|
+
}
|
|
3404
3843
|
if (receiver.type.kind === "union" && lowerer.armTag(receiver.type.unionId, UNDEFINED_T) >= 0 && kind === "array") {
|
|
3405
3844
|
const present = lowerer.stripUndefinedArm(receiver.type);
|
|
3406
3845
|
const helper = present.kind === "array"
|
|
@@ -3466,6 +3905,19 @@ export function lowerArrayLiteral(lowerer, expr, expected) {
|
|
|
3466
3905
|
// destination can use the ordinary tuple-to-array coercion afterward.
|
|
3467
3906
|
const tsType = underConstAssertion(expr) ? lowerer.typeOf(expr) : ctxType ?? lowerer.typeOf(expr);
|
|
3468
3907
|
let mapped = expected ?? lowerer.mapTypeOf(tsType);
|
|
3908
|
+
// Inferred JavaScript arrays can contain unrelated constructors with
|
|
3909
|
+
// structurally equal checker types. Their native class ABIs still differ.
|
|
3910
|
+
if (!expected && isJsSourceFile(expr.getSourceFile()) && mapped?.kind === "array" && mapped.elem.kind === "classval")
|
|
3911
|
+
mapped = DYN;
|
|
3912
|
+
// An inferred destructuring pattern supplies a contextual [any, ...]
|
|
3913
|
+
// tuple. Its positions do not erase the literal's inferred element
|
|
3914
|
+
// types; explicit destinations already arrive through expected.
|
|
3915
|
+
if (expected === undefined && ctxType && lowerer.checker.isTupleType(ctxType) &&
|
|
3916
|
+
lowerer.checker.getTypeArguments(ctxType).some((type) => (type.flags & ts.TypeFlags.Any) !== 0)) {
|
|
3917
|
+
const inferred = lowerer.mapTypeOf(lowerer.typeOf(expr));
|
|
3918
|
+
if (inferred?.kind === "array" || inferred?.kind === "record" && lowerer.shapes.get(inferred.shapeId)?.tuple)
|
|
3919
|
+
mapped = inferred;
|
|
3920
|
+
}
|
|
3469
3921
|
// A JS literal whose OWN inferred type is never-tainted
|
|
3470
3922
|
// (neverTaintedJsType — the evolving `const gb = []`, the mixed
|
|
3471
3923
|
// command tuple `['pwd', []]`) carries no element information: route
|
|
@@ -3562,6 +4014,12 @@ export function lowerArrayLiteral(lowerer, expr, expected) {
|
|
|
3562
4014
|
mapped = arrayOf(first.type);
|
|
3563
4015
|
}
|
|
3564
4016
|
}
|
|
4017
|
+
if (!expected && isJsSourceFile(expr.getSourceFile()) && mapped?.kind === "array") {
|
|
4018
|
+
const elem = mapped.elem;
|
|
4019
|
+
const arms = elem.kind === "union" ? lowerer.unions.get(elem.unionId)?.arms : [elem];
|
|
4020
|
+
if (arms?.length && arms.every((arm) => arm.kind === "classval"))
|
|
4021
|
+
mapped = DYN;
|
|
4022
|
+
}
|
|
3565
4023
|
// An EMPTY literal under a CONST ASSERTION whose type queries panicked
|
|
3566
4024
|
// (tsgo's readonly-[] TupleType conversion — the facade answers `any`,
|
|
3567
4025
|
// which maps to nothing statically and to an island value under
|
|
@@ -3573,6 +4031,11 @@ export function lowerArrayLiteral(lowerer, expr, expected) {
|
|
|
3573
4031
|
underConstAssertion(expr)) {
|
|
3574
4032
|
mapped = arrayOf(unitOnlyUnion(lowerer.unions));
|
|
3575
4033
|
}
|
|
4034
|
+
// Heterogeneous JavaScript object arrays must retain each literal's
|
|
4035
|
+
// real keys. A structural union can otherwise drop descriptor fields.
|
|
4036
|
+
if (!expected && isJsSourceFile(expr.getSourceFile()) && mapped?.kind === "array" && mapped.elem.kind === "union" &&
|
|
4037
|
+
expr.elements.some(ts.isObjectLiteralExpression))
|
|
4038
|
+
mapped = DYN;
|
|
3576
4039
|
// A TUPLE-typed slot (`const t: [string, number] = ["a", 1]`): the
|
|
3577
4040
|
// literal constructs the tuple's record shape — one positional field
|
|
3578
4041
|
// per element, source order (which IS index order, so evaluation order
|
|
@@ -3628,7 +4091,14 @@ export function lowerArrayLiteral(lowerer, expr, expected) {
|
|
|
3628
4091
|
// `unknown[]` annotation or a collapsed `(string | object)[]` maps
|
|
3629
4092
|
// to DYN wholesale now (mapType's dyn-element array rule), so the
|
|
3630
4093
|
// literal IS the dyn array.
|
|
3631
|
-
if (isJsSourceFile(expr.getSourceFile()) || mapped?.kind === "dyn") {
|
|
4094
|
+
if (isJsSourceFile(expr.getSourceFile()) || mapped?.kind === "dyn" || lowerer.checkerAnyArray(expr)) {
|
|
4095
|
+
if (expr.elements.some(ts.isSpreadElement)) {
|
|
4096
|
+
// Native collection drains return vectors of retained dyn
|
|
4097
|
+
// elements. Reuse the ordinary spread builder, then expose the
|
|
4098
|
+
// resulting array through the established unknown[] representation.
|
|
4099
|
+
const vector = lowerArrayLiteral(lowerer, expr, arrayOf(DYN));
|
|
4100
|
+
return lowerer.coerceInto(expr, vector, DYN);
|
|
4101
|
+
}
|
|
3632
4102
|
const elems = expr.elements.map((el) => {
|
|
3633
4103
|
if (ts.isSpreadElement(el)) {
|
|
3634
4104
|
lowerer.unsupported("SC1090", el, "spread elements in a dynamic (unknown[]) array literal");
|
|
@@ -3679,19 +4149,7 @@ export function lowerArrayLiteral(lowerer, expr, expected) {
|
|
|
3679
4149
|
(ts.isConditionalExpression(srcNode)
|
|
3680
4150
|
? lowerTernary(lowerer, srcNode, type)
|
|
3681
4151
|
: 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
|
-
}
|
|
4152
|
+
src = lowerCollectionSpread(lowerer, src, el.expression) ?? src;
|
|
3695
4153
|
// `[...new SymbolIterator]`: a CLASS ITERABLE drains through its
|
|
3696
4154
|
// own protocol into a fresh element array (classIteratorDrainCall
|
|
3697
4155
|
// — an infinite iterator loops forever, exactly Node), and the
|
|
@@ -3723,6 +4181,12 @@ export function lowerArrayLiteral(lowerer, expr, expected) {
|
|
|
3723
4181
|
loc: locOf(el),
|
|
3724
4182
|
};
|
|
3725
4183
|
}
|
|
4184
|
+
// A native checked iterable can supply scalar elements even when
|
|
4185
|
+
// the checker inferred a typed array from its producer. Drain once
|
|
4186
|
+
// and validate the elements before the ordinary spread copy.
|
|
4187
|
+
if (src.type.kind === "dyn" && (type.elem.kind === "dyn" || type.elem.kind === "f64" || type.elem.kind === "string" || type.elem.kind === "bool")) {
|
|
4188
|
+
src = lowerer.coerceInto(el.expression, checkedIteratorPack(lowerer, src, locOf(el)), type);
|
|
4189
|
+
}
|
|
3726
4190
|
// A same-family array whose ELEMENT lifts (string[] into a
|
|
3727
4191
|
// (string | symbol)[] literal — per-element wrap/width copy):
|
|
3728
4192
|
// the interned width helper reshapes before the spread copies.
|
|
@@ -3982,16 +4446,17 @@ export function lowerElementAccess(lowerer, expr) {
|
|
|
3982
4446
|
lowerer.fenceStaticResponseMember(expr, "read");
|
|
3983
4447
|
lowerer.fenceStaticHeadersMember(expr, "read");
|
|
3984
4448
|
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).
|
|
4449
|
+
// Computed global reads share the stored global object's custom
|
|
4450
|
+
// properties. Symbol keys retain their identity-indexed store.
|
|
3993
4451
|
if (!expr.questionDotToken && stdlibGlobalNameOf(lowerer, expr.expression) === "globalThis") {
|
|
3994
|
-
|
|
4452
|
+
const symbol = globalSymbolKey(lowerer, expr.expression, expr.argumentExpression);
|
|
4453
|
+
if (symbol)
|
|
4454
|
+
return { kind: "libCall", fn: "dyn.globalSymbolGet", args: [symbol], type: DYN, loc: locOf(expr) };
|
|
4455
|
+
const key = lowerer.lowerExpr(expr.argumentExpression);
|
|
4456
|
+
const name = key.type.kind === "string" ? key : {
|
|
4457
|
+
kind: "libCall", fn: "dyn.toStringCoerce", args: [lowerer.coerceInto(expr.argumentExpression, key, DYN)], type: STRING, loc: locOf(expr),
|
|
4458
|
+
};
|
|
4459
|
+
return { kind: "dynKeyGet", value: lowerGlobalValue(lowerer, expr.expression), key: name, type: DYN, loc: locOf(expr) };
|
|
3995
4460
|
}
|
|
3996
4461
|
// `req.headers["x-name"]` — the computed twin of `req.headers.host`
|
|
3997
4462
|
// (the server spoke owns both; the envGet precedent).
|
|
@@ -4004,7 +4469,7 @@ export function lowerElementAccess(lowerer, expr) {
|
|
|
4004
4469
|
// lower to the ONE process.envGet intrinsic. The read narrows like any
|
|
4005
4470
|
// union-typed expression when the checker narrowed this occurrence.
|
|
4006
4471
|
if (lowerer.isProcessEnv(expr.expression)) {
|
|
4007
|
-
const key = lowerer
|
|
4472
|
+
const key = lowerEnvironmentKey(lowerer, expr.argumentExpression);
|
|
4008
4473
|
if (key.type.kind !== "string") {
|
|
4009
4474
|
lowerer.unsupported("SC1090", expr.argumentExpression, "indexing process.env with non-string keys");
|
|
4010
4475
|
}
|
|
@@ -4027,17 +4492,31 @@ export function lowerElementAccess(lowerer, expr) {
|
|
|
4027
4492
|
return lowerer.maybeNarrow(ex, expr);
|
|
4028
4493
|
}
|
|
4029
4494
|
// Symbol-keyed property READS (`this[kLimit]`): when the key is a
|
|
4030
|
-
// statically-resolvable
|
|
4031
|
-
//
|
|
4495
|
+
// statically-resolvable symbol declared as a field of the receiver's
|
|
4496
|
+
// class (classSymbolKeyOf — the countdown.js/OpenTUI idioms), the
|
|
4032
4497
|
// read IS an ordinary field read of the hidden slot. Every other
|
|
4033
4498
|
// symbol key stays fenced: record shapes, runtime-identity keys
|
|
4034
|
-
// (symbol parameters,
|
|
4499
|
+
// (symbol parameters, reassigned bindings), and keys no class declares
|
|
4035
4500
|
// — the layouts are compile-time field lists.
|
|
4036
4501
|
if (lowerer.mapTypeOf(lowerer.typeOf(expr.argumentExpression))?.kind === "symbol") {
|
|
4037
4502
|
const target = symbolFieldTarget(lowerer, expr);
|
|
4038
4503
|
if (target)
|
|
4039
4504
|
return lowerer.maybeNarrow(lowerer.fieldGetExpr(target, locOf(expr), expr), expr);
|
|
4040
|
-
|
|
4505
|
+
let receiver = lowerer.lowerExpr(expr.expression);
|
|
4506
|
+
const classInfo = receiver.type.kind === "classval" ? lowerer.classes.get(receiver.type.className) : undefined;
|
|
4507
|
+
if (receiver.type.kind === "func" || receiver.type.kind === "generator" ||
|
|
4508
|
+
((receiver.type.kind === "array" || receiver.type.kind === "string" || receiver.type.kind === "bytes") && lowerer.dynConvertible(receiver.type)) ||
|
|
4509
|
+
(receiver.type.kind === "object" && lowerer.dynConvertible(receiver.type)) || isDynTypedRefType(receiver.type) ||
|
|
4510
|
+
(classInfo && (classInfo.callableBase || hasRuntimeStatics(classInfo))) || ((receiver.type.kind === "map" || receiver.type.kind === "set") && lowerer.dynConvertible(receiver.type))) {
|
|
4511
|
+
const fnName = jsFuncNameOf(expr.expression);
|
|
4512
|
+
receiver = { kind: "dynFrom", value: receiver, type: DYN, loc: locOf(expr),
|
|
4513
|
+
...(fnName !== null ? { fnName } : {}) };
|
|
4514
|
+
}
|
|
4515
|
+
if (receiver.type.kind === "dyn") {
|
|
4516
|
+
const key = lowerer.lowerExprExpecting(expr.argumentExpression, DYN);
|
|
4517
|
+
return lowerer.maybeNarrow({ kind: "dynKeyGet", value: receiver, key, type: DYN, loc: locOf(expr) }, expr);
|
|
4518
|
+
}
|
|
4519
|
+
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
4520
|
}
|
|
4042
4521
|
// A never-tainted JS receiver type (neverTaintedJsType — `cmd[1]` on
|
|
4043
4522
|
// `const cmd = ['pwd', []]`, whose binding lowered checked-dynamic)
|
|
@@ -4046,6 +4525,22 @@ export function lowerElementAccess(lowerer, expr) {
|
|
|
4046
4525
|
let receiverIr = neverTaintedJsType(lowerer, expr.expression, lowerer.typeOf(expr.expression))
|
|
4047
4526
|
? null
|
|
4048
4527
|
: lowerer.mapTypeOf(lowerer.typeOf(expr.expression));
|
|
4528
|
+
if (isJsSourceFile(expr.getSourceFile())) {
|
|
4529
|
+
const receiver = tryLowerExpression(lowerer, expr.expression);
|
|
4530
|
+
if (receiver?.type.kind === "dyn") {
|
|
4531
|
+
const key = lowerer.lowerExprExpecting(expr.argumentExpression, DYN);
|
|
4532
|
+
const optional = hasOptionalChainGuard(expr.expression);
|
|
4533
|
+
return lowerer.maybeNarrow({ kind: "dynKeyGet", value: receiver, key,
|
|
4534
|
+
...(optional ? { optional: true } : {}), type: DYN, loc: locOf(expr) }, expr);
|
|
4535
|
+
}
|
|
4536
|
+
if (receiver?.type.kind === "func" && lowerer.dynConvertible(receiver.type)) {
|
|
4537
|
+
const loc = locOf(expr);
|
|
4538
|
+
const fnName = jsFuncNameOf(expr.expression);
|
|
4539
|
+
const boxed = { kind: "dynFrom", value: receiver, type: DYN, loc, ...(fnName !== null ? { fnName } : {}) };
|
|
4540
|
+
const key = lowerer.lowerExprExpecting(expr.argumentExpression, DYN);
|
|
4541
|
+
return lowerer.maybeNarrow({ kind: "dynKeyGet", key, value: boxed, type: DYN, loc }, expr);
|
|
4542
|
+
}
|
|
4543
|
+
}
|
|
4049
4544
|
// A readonly tuple union under Array.isArray is checker-typed as an
|
|
4050
4545
|
// intersection with any[], whose structural mapping is not the tuple
|
|
4051
4546
|
// shape. maybeNarrow on the receiver uses the runtime tag proof to
|
|
@@ -4054,6 +4549,19 @@ export function lowerElementAccess(lowerer, expr) {
|
|
|
4054
4549
|
const checkerArray = lowerer.checkerArrayValue(expr.expression);
|
|
4055
4550
|
if (checkerArray)
|
|
4056
4551
|
receiverIr = checkerArray.type;
|
|
4552
|
+
if (receiverIr?.kind === "object") {
|
|
4553
|
+
const key = recordKeyLiteralText(expr.argumentExpression) ?? recordKeyTypeLiteralText(lowerer, expr.argumentExpression);
|
|
4554
|
+
if (key !== null) {
|
|
4555
|
+
const target = classFieldTarget(lowerer, expr.expression, receiverIr, key);
|
|
4556
|
+
if (target)
|
|
4557
|
+
return lowerer.maybeNarrow(lowerer.fieldGetExpr(target, locOf(expr), expr), expr);
|
|
4558
|
+
}
|
|
4559
|
+
if (receiverIr.className === "%Error" || isDynTypedRefType(receiverIr)) {
|
|
4560
|
+
const receiver = lowerer.coerceToExpected(lowerer.lowerExpr(expr.expression), DYN);
|
|
4561
|
+
const key = lowerer.lowerExprExpecting(expr.argumentExpression, DYN);
|
|
4562
|
+
return lowerer.maybeNarrow({ kind: "dynKeyGet", value: receiver, key, type: DYN, loc: locOf(expr) }, expr);
|
|
4563
|
+
}
|
|
4564
|
+
}
|
|
4057
4565
|
if (receiverIr?.kind === "jsval") {
|
|
4058
4566
|
// Dispatch follows the RUNTIME world (383(d)): a checker-'any'
|
|
4059
4567
|
// receiver whose value LOWERED checked-dynamic (`bag.list[0]` where
|
|
@@ -4079,6 +4587,8 @@ export function lowerElementAccess(lowerer, expr) {
|
|
|
4079
4587
|
// read undefined — divergence 4's policy).
|
|
4080
4588
|
if (receiverIr?.kind === "bytes") {
|
|
4081
4589
|
let recv = lowerer.lowerExpr(expr.expression);
|
|
4590
|
+
if (recv.type.kind === "dyn")
|
|
4591
|
+
recv = lowerer.coerceInto(expr.expression, recv, receiverIr);
|
|
4082
4592
|
if (recv.type.kind === "union" && lowerer.armTag(recv.type.unionId, UNDEFINED_T) >= 0) {
|
|
4083
4593
|
const present = lowerer.stripUndefinedArm(recv.type);
|
|
4084
4594
|
const helper = present.kind === "bytes"
|
|
@@ -4095,6 +4605,14 @@ export function lowerElementAccess(lowerer, expr) {
|
|
|
4095
4605
|
if (recv.type.kind === "jsval")
|
|
4096
4606
|
return islandElementRead(lowerer, expr, recv);
|
|
4097
4607
|
const index = lowerer.lowerExpr(expr.argumentExpression);
|
|
4608
|
+
if (isJsSourceFile(expr.getSourceFile()) && recv.type.kind === "bytes") {
|
|
4609
|
+
return { kind: "dynKeyGet", value: lowerer.coerceInto(expr.expression, recv, DYN),
|
|
4610
|
+
key: index.type.kind === "f64" ? { kind: "toString", operand: index, type: STRING, loc: locOf(expr) } : index,
|
|
4611
|
+
type: DYN, loc: locOf(expr) };
|
|
4612
|
+
}
|
|
4613
|
+
if (index.type.kind === "dyn" && recv.type.kind === "bytes") {
|
|
4614
|
+
return { kind: "dynKeyGet", value: lowerer.coerceInto(expr.expression, recv, DYN), key: index, type: DYN, loc: locOf(expr) };
|
|
4615
|
+
}
|
|
4098
4616
|
if (index.type.kind !== "f64") {
|
|
4099
4617
|
lowerer.unsupported("SC1090", expr.argumentExpression, "indexing with non-number keys");
|
|
4100
4618
|
}
|
|
@@ -4116,6 +4634,17 @@ export function lowerElementAccess(lowerer, expr) {
|
|
|
4116
4634
|
// like the array path.
|
|
4117
4635
|
if (obj.type.kind === "jsval")
|
|
4118
4636
|
return islandElementRead(lowerer, expr, obj);
|
|
4637
|
+
if (obj.type.kind === "dyn") {
|
|
4638
|
+
// Object.entries over a checked value stores each pair in the
|
|
4639
|
+
// dynamic tree even when the checker names a tuple type.
|
|
4640
|
+
const key = lowerRecordPropertyKey(lowerer, lowerer.lowerExpr(expr.argumentExpression), expr.argumentExpression);
|
|
4641
|
+
const optional = hasOptionalChainGuard(expr.expression);
|
|
4642
|
+
return lowerer.maybeNarrow({
|
|
4643
|
+
kind: "dynKeyGet", value: obj, key,
|
|
4644
|
+
...(optional ? { optional: true } : {}),
|
|
4645
|
+
type: DYN, loc: locOf(expr),
|
|
4646
|
+
}, expr);
|
|
4647
|
+
}
|
|
4119
4648
|
if (obj.type.kind === "union" && lowerer.armTag(obj.type.unionId, UNDEFINED_T) >= 0) {
|
|
4120
4649
|
// The checker sees the outer `arrays[i]` as a tuple when
|
|
4121
4650
|
// noUncheckedIndexedAccess is disabled, but the lowered read is
|
|
@@ -4171,21 +4700,33 @@ export function lowerElementAccess(lowerer, expr) {
|
|
|
4171
4700
|
// (`pkg.workspaces.packages["0"]` — the lowering world types the
|
|
4172
4701
|
// unknown-rooted chain `any`); a non-dyn lowering falls through to
|
|
4173
4702
|
// the fences below (re-lowering is pure IR construction).
|
|
4174
|
-
if (receiverIr?.kind === "dyn" || receiverIr === null) {
|
|
4703
|
+
if (receiverIr?.kind === "dyn" || receiverIr?.kind === "union" || receiverIr === null) {
|
|
4175
4704
|
const obj = lowerer.lowerExpr(expr.expression);
|
|
4176
|
-
|
|
4705
|
+
// A generic mapped type can stay unresolved at this body use even
|
|
4706
|
+
// though its instantiated parameter has a concrete record ABI.
|
|
4707
|
+
// Dispatch from that stored shape, as the array fallback below does.
|
|
4708
|
+
if (receiverIr === null && obj.type.kind === "record") {
|
|
4709
|
+
const shape = lowerer.shapes.get(obj.type.shapeId);
|
|
4710
|
+
const index = tupleLiteralIndex(expr.argumentExpression);
|
|
4711
|
+
const field = shape?.tuple && index !== null ? shape.fields.find((field) => field.name === String(index)) : null;
|
|
4712
|
+
if (field)
|
|
4713
|
+
return lowerer.maybeNarrow({ kind: "recordGet", obj, shapeId: obj.type.shapeId, field: field.name, type: field.type, loc: locOf(expr) }, expr);
|
|
4714
|
+
if (shape && !shape.tuple) {
|
|
4715
|
+
return lowerer.lowerRecordKeyRead(expr, obj.type.shapeId, shape);
|
|
4716
|
+
}
|
|
4717
|
+
}
|
|
4718
|
+
if (obj.type.kind === "dyn" || obj.type.kind === "object" && obj.type.className === "%Error" || isDynTypedRefType(obj.type)) {
|
|
4177
4719
|
const rawKey = lowerer.lowerExpr(expr.argumentExpression);
|
|
4178
4720
|
// Number, bool, and DYN keys stringify (ToPropertyKey) — the
|
|
4179
4721
|
// dyn-keyed read `catchWarning[warning.name]` where the property
|
|
4180
4722
|
// chain itself lowered dyn.
|
|
4181
|
-
const key = rawKey.type.kind === "
|
|
4182
|
-
|
|
4183
|
-
|
|
4184
|
-
|
|
4185
|
-
|
|
4186
|
-
if (key) {
|
|
4723
|
+
const key = rawKey.type.kind === "dyn" || rawKey.type.kind === "symbol" ||
|
|
4724
|
+
rawKey.type.kind === "union" && lowerer.dynConvertible(rawKey.type)
|
|
4725
|
+
? lowerer.coerceToExpected(rawKey, DYN)
|
|
4726
|
+
: lowerRecordPropertyKey(lowerer, rawKey, expr.argumentExpression);
|
|
4727
|
+
if (key.type.kind === "string" || key.type.kind === "dyn") {
|
|
4187
4728
|
const opt = hasOptionalChainGuard(expr.expression);
|
|
4188
|
-
return lowerer.maybeNarrow({ kind: "dynKeyGet", key, ...(opt ? { optional: true } : {}), value: obj, type: DYN, loc: locOf(expr) }, expr);
|
|
4729
|
+
return lowerer.maybeNarrow({ kind: "dynKeyGet", key, ...(opt ? { optional: true } : {}), value: lowerer.coerceToExpected(obj, DYN), type: DYN, loc: locOf(expr) }, expr);
|
|
4189
4730
|
}
|
|
4190
4731
|
}
|
|
4191
4732
|
// A checker-`any` receiver that LOWERS to a static ARRAY
|
|
@@ -4195,6 +4736,9 @@ export function lowerElementAccess(lowerer, expr) {
|
|
|
4195
4736
|
if (obj.type.kind === "array") {
|
|
4196
4737
|
const index = lowerer.lowerExpr(expr.argumentExpression);
|
|
4197
4738
|
if (index.type.kind === "f64") {
|
|
4739
|
+
const safe = lowerSafeIndexRead(lowerer, obj, index, locOf(expr));
|
|
4740
|
+
if (safe)
|
|
4741
|
+
return safe;
|
|
4198
4742
|
return lowerer.maybeNarrow({ kind: "arrayGet", arr: obj, index, type: obj.type.elem, loc: locOf(expr) }, expr);
|
|
4199
4743
|
}
|
|
4200
4744
|
}
|
|
@@ -4217,7 +4761,7 @@ export function lowerElementAccess(lowerer, expr) {
|
|
|
4217
4761
|
}
|
|
4218
4762
|
}
|
|
4219
4763
|
}
|
|
4220
|
-
if (key.type.kind === "string") {
|
|
4764
|
+
if (key.type.kind === "string" || key.type.kind === "f64") {
|
|
4221
4765
|
const lit = ts.isStringLiteral(expr.argumentExpression)
|
|
4222
4766
|
? expr.argumentExpression.text
|
|
4223
4767
|
: null;
|
|
@@ -4225,33 +4769,20 @@ export function lowerElementAccess(lowerer, expr) {
|
|
|
4225
4769
|
if (keyed)
|
|
4226
4770
|
return lowerer.maybeNarrow(keyed, expr);
|
|
4227
4771
|
}
|
|
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
4772
|
}
|
|
4253
4773
|
}
|
|
4254
4774
|
if (receiverIr?.kind !== "array") {
|
|
4775
|
+
{
|
|
4776
|
+
const receiver = tryLowerExpression(lowerer, expr.expression);
|
|
4777
|
+
if (receiver?.type.kind === "func" &&
|
|
4778
|
+
canBoxFuncIntoDyn(receiver.type, (id) => lowerer.shapes.get(id), (id) => lowerer.unions.get(id))) {
|
|
4779
|
+
const key = lowerRecordPropertyKey(lowerer, lowerer.lowerExpr(expr.argumentExpression), expr.argumentExpression);
|
|
4780
|
+
if (key.type.kind === "string") {
|
|
4781
|
+
const boxed = { kind: "dynFrom", value: receiver, type: DYN, loc: locOf(expr.expression) };
|
|
4782
|
+
return lowerer.maybeNarrow({ kind: "dynKeyGet", key, value: boxed, type: DYN, loc: locOf(expr) }, expr);
|
|
4783
|
+
}
|
|
4784
|
+
}
|
|
4785
|
+
}
|
|
4255
4786
|
if (receiverIr?.kind === "string") {
|
|
4256
4787
|
// `s[i]` with a number index reads a UTF-16 code unit — charAt's
|
|
4257
4788
|
// exact job (the UTF-16-exact runtime). Without
|
|
@@ -4259,16 +4790,23 @@ export function lowerElementAccess(lowerer, expr) {
|
|
|
4259
4790
|
// and charAt's "" is the only string-typed answer for an
|
|
4260
4791
|
// out-of-range or fractional index where JS reads `undefined` —
|
|
4261
4792
|
// SEMANTICS.md documents the divergence; in-range integer reads
|
|
4262
|
-
// (the loop pattern) are JS-exact.
|
|
4263
|
-
//
|
|
4264
|
-
|
|
4793
|
+
// (the loop pattern) are JS-exact. Optional results instead retain
|
|
4794
|
+
// undefined for missing string properties.
|
|
4795
|
+
const recv = lowerer.lowerExpr(expr.expression);
|
|
4265
4796
|
const index = lowerer.lowerExpr(expr.argumentExpression);
|
|
4266
|
-
|
|
4267
|
-
|
|
4268
|
-
|
|
4797
|
+
const resultType = lowerer.mapTypeOf(lowerer.typeOf(expr));
|
|
4798
|
+
if (index.type.kind === "f64" && recv.type.kind === "string") {
|
|
4799
|
+
if (resultType?.kind === "string") {
|
|
4800
|
+
return { kind: "strIntrinsic", method: "charAt", receiver: recv, args: [index], type: STRING, loc: locOf(expr) };
|
|
4801
|
+
}
|
|
4802
|
+
if (resultType?.kind === "union") {
|
|
4803
|
+
const arms = lowerer.unions.get(resultType.unionId)?.arms;
|
|
4804
|
+
if (arms?.length === 2 && lowerer.armTag(resultType.unionId, STRING) >= 0 && lowerer.armTag(resultType.unionId, UNDEFINED_T) >= 0) {
|
|
4805
|
+
return lowerOptionalStringIndex(lowerer, recv, index, resultType, locOf(expr));
|
|
4806
|
+
}
|
|
4807
|
+
}
|
|
4269
4808
|
}
|
|
4270
|
-
lowerer.unsupported("SC1090", expr, "string indexing with this index/result shape (a number index
|
|
4271
|
-
"use .charAt(i) under noUncheckedIndexedAccess)");
|
|
4809
|
+
lowerer.unsupported("SC1090", expr, "string indexing with this index/result shape (expected a number index and a string or string | undefined result)");
|
|
4272
4810
|
}
|
|
4273
4811
|
lowerer.unsupported("SC1090", expr, "element access on non-array values");
|
|
4274
4812
|
}
|
|
@@ -4346,14 +4884,22 @@ export function lowerElementAccess(lowerer, expr) {
|
|
|
4346
4884
|
// can validate, and fences by name when it is not — an arrayGet over a
|
|
4347
4885
|
// dyn receiver is never emitted (the validator ICE).
|
|
4348
4886
|
if (arr.type.kind === "dyn") {
|
|
4349
|
-
if (
|
|
4887
|
+
if (canDynCheckTo(receiverIr, (id) => lowerer.shapes.get(id), (id) => lowerer.unions.get(id))) {
|
|
4350
4888
|
arr = { kind: "dynCheck", value: arr, type: receiverIr, loc: locOf(expr) };
|
|
4351
4889
|
}
|
|
4352
4890
|
else {
|
|
4353
4891
|
lowerer.unsupported("SC1090", expr, `element access on checked-dynamic values with '${lowerer.fmt(receiverIr)}' elements`);
|
|
4354
4892
|
}
|
|
4355
4893
|
}
|
|
4356
|
-
|
|
4894
|
+
// Array reads can themselves supply an index. A missing numeric value
|
|
4895
|
+
// is undefined, whose property lookup misses just like NaN; keep that
|
|
4896
|
+
// value instead of rejecting a checker-number index with optional IR.
|
|
4897
|
+
const index = lowerOptionalNumber(lowerer, lowerer.lowerExpr(expr.argumentExpression), locOf(expr.argumentExpression), expr.argumentExpression);
|
|
4898
|
+
if (arr.type.kind === "array" && (index.type.kind === "dyn" ||
|
|
4899
|
+
isJsSourceFile(expr.getSourceFile()) && index.type.kind !== "f64" && lowerer.dynConvertible(index.type))) {
|
|
4900
|
+
return { kind: "dynKeyGet", value: lowerer.coerceToExpected(arr, DYN),
|
|
4901
|
+
key: index, type: DYN, loc: locOf(expr) };
|
|
4902
|
+
}
|
|
4357
4903
|
if (index.type.kind !== "f64") {
|
|
4358
4904
|
lowerer.unsupported("SC1090", expr.argumentExpression, "indexing with non-number keys");
|
|
4359
4905
|
}
|
|
@@ -4464,8 +5010,11 @@ export function lowerRecordKeyRead(lowerer, expr, shapeId, shape, includeUndefin
|
|
|
4464
5010
|
// Number AND checked-dynamic keys ride the JS-exact formatter —
|
|
4465
5011
|
// property keys ARE strings (o[k] is o[String(k)] in JS), and a dyn
|
|
4466
5012
|
// key (agent.sockets[agent.getName(...)]) stringifies the same way.
|
|
4467
|
-
dk
|
|
4468
|
-
|
|
5013
|
+
if (dk.type.kind === "union" && lowerer.dynConvertible(dk.type))
|
|
5014
|
+
dk = lowerer.coerceToExpected(dk, DYN);
|
|
5015
|
+
if (dk.type.kind !== "symbol" && dk.type.kind !== "dyn")
|
|
5016
|
+
dk = lowerRecordPropertyKey(lowerer, dk, keyNode);
|
|
5017
|
+
if (dk.type.kind !== "string" && dk.type.kind !== "symbol" && dk.type.kind !== "dyn") {
|
|
4469
5018
|
lowerer.unsupported("SC1090", keyNode, "indexing records with non-string or non-number keys");
|
|
4470
5019
|
}
|
|
4471
5020
|
const read = { kind: "dynKeyGet", key: dk, value: obj, type: DYN, loc };
|
|
@@ -4475,6 +5024,16 @@ export function lowerRecordKeyRead(lowerer, expr, shapeId, shape, includeUndefin
|
|
|
4475
5024
|
// to observe the missing value instead of throwing during that check.
|
|
4476
5025
|
return includeUndefined ? read : lowerer.maybeNarrow(read, expr);
|
|
4477
5026
|
}
|
|
5027
|
+
if (litKey !== null) {
|
|
5028
|
+
const nominal = representedClassFieldTarget(lowerer, expr.expression, litKey, obj);
|
|
5029
|
+
if (nominal) {
|
|
5030
|
+
const read = lowerer.fieldGetExpr(nominal, loc, expr);
|
|
5031
|
+
return includeUndefined ? read : lowerer.maybeNarrow(read, expr);
|
|
5032
|
+
}
|
|
5033
|
+
}
|
|
5034
|
+
if (hasClassPayload(lowerer, obj.type)) {
|
|
5035
|
+
lowerer.unsupported("SC1090", expr, "computed property reads through structural views of class instances (use a declared literal key)");
|
|
5036
|
+
}
|
|
4478
5037
|
// Predicates can strengthen optional fields without changing the
|
|
4479
5038
|
// receiver's stored layout. Use that layout for bracket reads just as
|
|
4480
5039
|
// fieldTarget does for the corresponding dot reads.
|
|
@@ -4502,6 +5061,19 @@ export function lowerRecordKeyRead(lowerer, expr, shapeId, shape, includeUndefin
|
|
|
4502
5061
|
let key = litKey !== null
|
|
4503
5062
|
? { kind: "strLit", value: litKey, type: STRING, loc: locOf(keyNode) }
|
|
4504
5063
|
: lowerer.lowerExpr(keyNode);
|
|
5064
|
+
// Optional string/number keys need ToPropertyKey, not a boxed copy of
|
|
5065
|
+
// the receiver. Keep record reads on their native storage so nested
|
|
5066
|
+
// references retain identity across index-signature reads.
|
|
5067
|
+
if (key.type.kind === "union" &&
|
|
5068
|
+
!lowerer.unions.get(key.type.unionId)?.arms.some((arm) => arm.kind === "symbol" || arm.kind === "dyn")) {
|
|
5069
|
+
key = lowerRecordPropertyKey(lowerer, key, keyNode);
|
|
5070
|
+
}
|
|
5071
|
+
if ((key.type.kind === "symbol" || key.type.kind === "dyn" || key.type.kind === "union") &&
|
|
5072
|
+
lowerer.dynConvertible(key.type) && lowerer.dynConvertible(obj.type)) {
|
|
5073
|
+
const read = { kind: "dynKeyGet", value: lowerer.coerceToExpected(obj, DYN),
|
|
5074
|
+
key: lowerer.coerceToExpected(key, DYN), type: DYN, loc };
|
|
5075
|
+
return includeUndefined ? read : lowerer.maybeNarrow(read, expr);
|
|
5076
|
+
}
|
|
4505
5077
|
// Runtime NUMBER keys ride the JS-exact formatter (o[n] is o[String(n)]
|
|
4506
5078
|
// in JS — the number-keyed-signature access path).
|
|
4507
5079
|
key = lowerRecordPropertyKey(lowerer, key, keyNode);
|
|
@@ -4530,7 +5102,7 @@ export function lowerRecordKeyRead(lowerer, expr, shapeId, shape, includeUndefin
|
|
|
4530
5102
|
let declared = null;
|
|
4531
5103
|
if (shape.indexValue) {
|
|
4532
5104
|
declared = shape.indexValue;
|
|
4533
|
-
if (includeUndefined || lowerer.program.getCompilerOptions().noUncheckedIndexedAccess) {
|
|
5105
|
+
if (declared.kind !== "dyn" && (includeUndefined || lowerer.program.getCompilerOptions().noUncheckedIndexedAccess)) {
|
|
4534
5106
|
declared = lowerer.withUndefinedArmOf(declared);
|
|
4535
5107
|
if (!declared)
|
|
4536
5108
|
lowerer.badType(expr, lowerer.typeOf(expr));
|
|
@@ -4540,6 +5112,8 @@ export function lowerRecordKeyRead(lowerer, expr, shapeId, shape, includeUndefin
|
|
|
4540
5112
|
shape.fields.every((f) => typeEquals(f.type, shape.fields[0].type))) {
|
|
4541
5113
|
declared = shape.fields[0].type;
|
|
4542
5114
|
}
|
|
5115
|
+
if (!declared && isJsSourceFile(expr.getSourceFile()) && recordKeyResultOk(lowerer, shape, DYN))
|
|
5116
|
+
declared = DYN;
|
|
4543
5117
|
if (!declared) {
|
|
4544
5118
|
lowerer.unsupported("SC1090", expr, `dynamic keyed reads of '${lowerer.fmt({ kind: "record", shapeId })}' (the declared fields have no one common type)`);
|
|
4545
5119
|
}
|
|
@@ -4564,6 +5138,9 @@ export function lowerRecordKeyRead(lowerer, expr, shapeId, shape, includeUndefin
|
|
|
4564
5138
|
* values remain real undefined values, whose JavaScript property spelling
|
|
4565
5139
|
* is the string "undefined". */
|
|
4566
5140
|
function lowerRecordPropertyKey(lowerer, key, node) {
|
|
5141
|
+
if (key.type.kind === "union" && lowerer.dynConvertible(key.type)) {
|
|
5142
|
+
return { kind: "libCall", fn: "dyn.toStringCoerce", args: [lowerer.coerceInto(node, key, DYN)], type: STRING, loc: key.loc };
|
|
5143
|
+
}
|
|
4567
5144
|
if (key.type.kind === "f64" || key.type.kind === "bool" || key.type.kind === "dyn" ||
|
|
4568
5145
|
lowerer.runtimeOptionalWidening(key.type, STRING) !== null) {
|
|
4569
5146
|
return lowerer.ensureString(key, node);
|
|
@@ -4584,6 +5161,10 @@ export function lowerAbsenceProbe(lowerer, node) {
|
|
|
4584
5161
|
}
|
|
4585
5162
|
if (ts.isPropertyAccessExpression(expr)) {
|
|
4586
5163
|
const target = lowerer.fieldTarget(expr);
|
|
5164
|
+
if (target?.container === "class" && target.fieldType.kind === "union" &&
|
|
5165
|
+
lowerer.unions.get(target.fieldType.unionId)?.arms.some(isUnitType)) {
|
|
5166
|
+
return lowerer.fieldGetExpr(target, locOf(expr), expr);
|
|
5167
|
+
}
|
|
4587
5168
|
if (target?.container !== "recordOvf")
|
|
4588
5169
|
return null;
|
|
4589
5170
|
// A JS file-scope object-literal global is record-shaped to the
|
|
@@ -4685,6 +5266,7 @@ function recordKeyTypeLiteralText(lowerer, node) {
|
|
|
4685
5266
|
function recordKeyResultOk(lowerer, shape, type) {
|
|
4686
5267
|
const surfaces = (t) => typeEquals(t, type) ||
|
|
4687
5268
|
(type.kind === "union" && lowerer.armTag(type.unionId, t) >= 0) ||
|
|
5269
|
+
exactUnionWidening(lowerer, t, type) ||
|
|
4688
5270
|
(type.kind === "dyn" && lowerer.dynConvertible(t));
|
|
4689
5271
|
if (!shape.fields.every((f) => surfaces(f.type)))
|
|
4690
5272
|
return false;
|
|
@@ -4696,6 +5278,13 @@ function recordKeyResultOk(lowerer, shape, type) {
|
|
|
4696
5278
|
}
|
|
4697
5279
|
return true;
|
|
4698
5280
|
}
|
|
5281
|
+
function exactUnionWidening(lowerer, from, to) {
|
|
5282
|
+
if (from.kind !== "union" || to.kind !== "union")
|
|
5283
|
+
return false;
|
|
5284
|
+
const source = lowerer.unions.get(from.unionId);
|
|
5285
|
+
const target = lowerer.unions.get(to.unionId);
|
|
5286
|
+
return !!source && !!target && unionWideningTags(source.arms, target.arms) !== null;
|
|
5287
|
+
}
|
|
4699
5288
|
/** The literal index of a tuple access, or null when the expression isn't
|
|
4700
5289
|
* a non-negative integer literal (parentheses tolerated — tsc's own
|
|
4701
5290
|
* literal-index typing accepts them). */
|
|
@@ -4717,11 +5306,29 @@ export function lowerElementCompound(lowerer, expr, op) {
|
|
|
4717
5306
|
const target = expr.left;
|
|
4718
5307
|
fenceNodeModuleMutation(lowerer, target, "assignment");
|
|
4719
5308
|
const loc = locOf(expr);
|
|
4720
|
-
const
|
|
5309
|
+
const checked = lowerer.lowerExpr(target.expression);
|
|
5310
|
+
if (checked?.type.kind === "dyn" && isJsSourceFile(expr.getSourceFile())) {
|
|
5311
|
+
const stmts = [];
|
|
5312
|
+
const save = (value, name) => {
|
|
5313
|
+
const local = lowerer.declareHiddenLocal(name, value.type);
|
|
5314
|
+
stmts.push({ kind: "varDecl", localId: local.id, init: value, loc });
|
|
5315
|
+
return varRef(local.id, value.type, loc);
|
|
5316
|
+
};
|
|
5317
|
+
const receiver = save(checked, "%compoundReceiver");
|
|
5318
|
+
const key = save(lowerer.lowerExprExpecting(target.argumentExpression, DYN), "%compoundKey");
|
|
5319
|
+
const old = save({ kind: "dynKeyGet", value: receiver, key, type: DYN, loc }, "%compoundOld");
|
|
5320
|
+
const rhs = lowerer.lowerExprExpecting(expr.right, DYN);
|
|
5321
|
+
const numeric = ["-", "*", "/", "%", "**"].includes(op);
|
|
5322
|
+
const value = save({ kind: "libCall", fn: op === "+" ? "dyn.add" : numeric ? "dyn.arithmetic" : "dyn.bitwise",
|
|
5323
|
+
args: op === "+" ? [old, rhs] : [old, rhs, strLit(op, loc)], type: DYN, loc }, "%compoundResult");
|
|
5324
|
+
stmts.push({ kind: "exprStmt", expr: { kind: "libCall", fn: "dyn.keySetComputed", args: [receiver, key, value], type: VOID, loc }, loc });
|
|
5325
|
+
return { kind: "seqExpr", stmts, result: value, type: DYN, loc };
|
|
5326
|
+
}
|
|
5327
|
+
const receiverType = checked.type.kind === "array" || checked.type.kind === "bytes" ? checked.type : lowerer.mapTypeOf(lowerer.typeOf(target.expression));
|
|
4721
5328
|
if (receiverType?.kind !== "array" && receiverType?.kind !== "bytes") {
|
|
4722
5329
|
lowerer.unsupported("SC1090", target, "compound assignment to non-array elements");
|
|
4723
5330
|
}
|
|
4724
|
-
let receiver =
|
|
5331
|
+
let receiver = checked;
|
|
4725
5332
|
if (receiver.type.kind === "union" && lowerer.armTag(receiver.type.unionId, UNDEFINED_T) >= 0) {
|
|
4726
5333
|
const present = lowerer.stripUndefinedArm(receiver.type);
|
|
4727
5334
|
const helper = present.kind === receiverType.kind
|
|
@@ -4758,6 +5365,12 @@ export function lowerElementCompound(lowerer, expr, op) {
|
|
|
4758
5365
|
else if (valueType.kind === "f64" && numericRhs.type.kind === "f64") {
|
|
4759
5366
|
computed = { kind: "bin", op, left: lowerOptionalNumber(lowerer, oldRef(), loc), right: numericRhs, type: F64, loc };
|
|
4760
5367
|
}
|
|
5368
|
+
else if (valueType.kind === "dyn" && isJsSourceFile(expr.getSourceFile())) {
|
|
5369
|
+
const left = lowerer.coerceToExpected(oldRef(), DYN), right = lowerer.coerceToExpected(rhs, DYN);
|
|
5370
|
+
const numeric = ["-", "*", "/", "%", "**"].includes(op);
|
|
5371
|
+
computed = { kind: "libCall", fn: op === "+" ? "dyn.add" : numeric ? "dyn.arithmetic" : "dyn.bitwise",
|
|
5372
|
+
args: op === "+" ? [left, right] : [left, right, strLit(op, loc)], type: DYN, loc };
|
|
5373
|
+
}
|
|
4761
5374
|
else {
|
|
4762
5375
|
lowerer.unsupported("SC1043", expr);
|
|
4763
5376
|
}
|
|
@@ -4780,16 +5393,71 @@ export function lowerElementCompound(lowerer, expr, op) {
|
|
|
4780
5393
|
loc,
|
|
4781
5394
|
};
|
|
4782
5395
|
}
|
|
4783
|
-
/**
|
|
4784
|
-
*
|
|
5396
|
+
/** Checked member assignment evaluates the reference before its RHS and
|
|
5397
|
+
* yields the original assigned value, including in a chained assignment. */
|
|
5398
|
+
function lowerDynMemberAssignment(lowerer, expr, recv) {
|
|
5399
|
+
const loc = locOf(expr);
|
|
5400
|
+
const target = expr.left;
|
|
5401
|
+
const receiver = lowerer.declareHiddenLocal("%setReceiver", DYN);
|
|
5402
|
+
const keyValue = ts.isElementAccessExpression(target)
|
|
5403
|
+
? lowerer.lowerExprExpecting(target.argumentExpression, DYN)
|
|
5404
|
+
: lowerer.coerceToExpected({ kind: "strLit", value: target.name.text, type: STRING, loc }, DYN);
|
|
5405
|
+
const key = lowerer.declareHiddenLocal("%setKey", DYN);
|
|
5406
|
+
const raw = lowerer.lowerExpr(expr.right);
|
|
5407
|
+
const value = raw.type.kind === "void"
|
|
5408
|
+
? {
|
|
5409
|
+
kind: "seqExpr", stmts: [{ kind: "exprStmt", expr: raw, loc }],
|
|
5410
|
+
result: dynUndefinedExpr(loc), type: DYN, loc,
|
|
5411
|
+
}
|
|
5412
|
+
: isUnitType(raw.type) ? lowerer.coerceInto(expr.right, raw, DYN) : raw;
|
|
5413
|
+
const assigned = lowerer.declareHiddenLocal("%setValue", value.type);
|
|
5414
|
+
const result = varRef(assigned.id, value.type, loc);
|
|
5415
|
+
const stored = lowerer.coerceInto(expr.right, result, DYN);
|
|
5416
|
+
// Evaluate the reference before the RHS, but defer key coercion until
|
|
5417
|
+
// PutValue. Yield the RHS without rereading a setter-backed property.
|
|
5418
|
+
return {
|
|
5419
|
+
kind: "seqExpr", stmts: [
|
|
5420
|
+
{ kind: "varDecl", localId: receiver.id, init: recv, loc },
|
|
5421
|
+
{ kind: "varDecl", localId: key.id, init: keyValue, loc },
|
|
5422
|
+
{ kind: "varDecl", localId: assigned.id, init: value, loc },
|
|
5423
|
+
{ kind: "exprStmt", expr: { kind: "libCall", fn: "dyn.keySetComputed", args: [varRef(receiver.id, DYN, loc), varRef(key.id, DYN, loc), stored], type: VOID, loc }, loc },
|
|
5424
|
+
], result, type: result.type, loc,
|
|
5425
|
+
};
|
|
5426
|
+
}
|
|
5427
|
+
/** JavaScript callable members live on the closure, so aliases and chained
|
|
5428
|
+
* writes share the same storage as Object.defineProperties. */
|
|
5429
|
+
export function lowerNativeFunctionAssignment(lowerer, expr) {
|
|
5430
|
+
const target = expr.left;
|
|
5431
|
+
if (!isJsSourceFile(expr.getSourceFile()) ||
|
|
5432
|
+
(!ts.isPropertyAccessExpression(target) && !ts.isElementAccessExpression(target)) ||
|
|
5433
|
+
target.questionDotToken)
|
|
5434
|
+
return null;
|
|
5435
|
+
const receiver = tryLowerExpression(lowerer, target.expression);
|
|
5436
|
+
if (receiver?.type.kind !== "func" ||
|
|
5437
|
+
!canBoxFuncIntoDyn(receiver.type, (id) => lowerer.shapes.get(id), (id) => lowerer.unions.get(id)))
|
|
5438
|
+
return null;
|
|
5439
|
+
fenceNodeModuleMutation(lowerer, target, "assignment");
|
|
5440
|
+
const fnName = jsFuncNameOf(target.expression);
|
|
5441
|
+
const boxed = {
|
|
5442
|
+
kind: "dynFrom", value: receiver, type: DYN, loc: locOf(target.expression),
|
|
5443
|
+
...(fnName !== null ? { fnName } : {}),
|
|
5444
|
+
};
|
|
5445
|
+
return lowerDynMemberAssignment(lowerer, expr, boxed);
|
|
5446
|
+
}
|
|
4785
5447
|
export function lowerElementWrite(lowerer, expr) {
|
|
4786
5448
|
const target = expr.left;
|
|
4787
5449
|
fenceNodeModuleMutation(lowerer, target, "assignment");
|
|
5450
|
+
const globalKey = globalSymbolKey(lowerer, target.expression, target.argumentExpression);
|
|
5451
|
+
if (globalKey) {
|
|
5452
|
+
const value = lowerer.lowerExprExpecting(expr.right, DYN);
|
|
5453
|
+
const loc = locOf(expr);
|
|
5454
|
+
return { kind: "exprStmt", expr: { kind: "libCall", fn: "dyn.globalSymbolSet", args: [globalKey, value], type: VOID, loc }, loc };
|
|
5455
|
+
}
|
|
4788
5456
|
// `process.env[key] = v` — the computed twin of the dotted env write:
|
|
4789
5457
|
// setenv(3), string keys and string values only.
|
|
4790
5458
|
if (lowerer.isProcessEnv(target.expression) && !target.questionDotToken) {
|
|
4791
5459
|
const loc = locOf(expr);
|
|
4792
|
-
const key = lowerer
|
|
5460
|
+
const key = lowerEnvironmentKey(lowerer, target.argumentExpression);
|
|
4793
5461
|
if (key.type.kind !== "string") {
|
|
4794
5462
|
lowerer.unsupported("SC1090", target.argumentExpression, "indexing process.env with non-string keys");
|
|
4795
5463
|
}
|
|
@@ -4813,9 +5481,32 @@ export function lowerElementWrite(lowerer, expr) {
|
|
|
4813
5481
|
const value = lowerer.lowerExprExpecting(expr.right, fieldT.fieldType);
|
|
4814
5482
|
return lowerer.fieldSetStmt(fieldT, value, locOf(expr), target);
|
|
4815
5483
|
}
|
|
4816
|
-
|
|
5484
|
+
const receiver = lowerer.lowerExpr(target.expression);
|
|
5485
|
+
if (receiver.type.kind === "dyn")
|
|
5486
|
+
return { kind: "exprStmt", expr: lowerDynMemberAssignment(lowerer, expr, receiver), loc: locOf(expr) };
|
|
5487
|
+
if (isJsSourceFile(expr.getSourceFile()) && receiver.type.kind === "object" && lowerer.dynConvertible(receiver.type)) {
|
|
5488
|
+
return { kind: "exprStmt", expr: lowerDynMemberAssignment(lowerer, expr, lowerer.coerceToExpected(receiver, DYN)), loc: locOf(expr) };
|
|
5489
|
+
}
|
|
5490
|
+
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)");
|
|
5491
|
+
}
|
|
5492
|
+
const checkedReceiver = tryLowerExpression(lowerer, target.expression);
|
|
5493
|
+
if (checkedReceiver?.type.kind === "dyn") {
|
|
5494
|
+
return { kind: "exprStmt", expr: lowerDynMemberAssignment(lowerer, expr, checkedReceiver), loc: locOf(expr) };
|
|
5495
|
+
}
|
|
5496
|
+
if (checkedReceiver?.type.kind === "object" && isJsSourceFile(expr.getSourceFile())) {
|
|
5497
|
+
const info = lowerer.classes.get(checkedReceiver.type.className);
|
|
5498
|
+
const key = recordKeyLiteralText(target.argumentExpression) ?? recordKeyTypeLiteralText(lowerer, target.argumentExpression);
|
|
5499
|
+
if (info && key !== null && isClassCallback(lowerer, info, key)) {
|
|
5500
|
+
const receiver = lowerer.coerceInto(target.expression, checkedReceiver, DYN);
|
|
5501
|
+
return { kind: "exprStmt", expr: lowerDynMemberAssignment(lowerer, expr, receiver), loc: locOf(expr) };
|
|
5502
|
+
}
|
|
5503
|
+
}
|
|
5504
|
+
let receiverIr = lowerer.mapTypeOf(lowerer.typeOf(target.expression));
|
|
5505
|
+
if (receiverIr === null || receiverIr.kind === "dyn") {
|
|
5506
|
+
const receiver = tryLowerExpression(lowerer, target.expression);
|
|
5507
|
+
if (receiver?.type.kind === "array")
|
|
5508
|
+
receiverIr = receiver.type;
|
|
4817
5509
|
}
|
|
4818
|
-
const receiverIr = lowerer.mapTypeOf(lowerer.typeOf(target.expression));
|
|
4819
5510
|
if (receiverIr?.kind === "jsval") {
|
|
4820
5511
|
const obj = lowerer.lowerExpr(target.expression);
|
|
4821
5512
|
// Dispatch follows the RUNTIME world (383(d)): a checker-'any'
|
|
@@ -4857,9 +5548,7 @@ export function lowerElementWrite(lowerer, expr) {
|
|
|
4857
5548
|
const obj = lowerer.lowerExpr(target.expression);
|
|
4858
5549
|
if (obj.type.kind === "dyn") {
|
|
4859
5550
|
const loc = locOf(expr);
|
|
4860
|
-
|
|
4861
|
-
if (key.type.kind === "f64")
|
|
4862
|
-
key = lowerer.ensureString(key, target.argumentExpression);
|
|
5551
|
+
const key = lowerRecordPropertyKey(lowerer, lowerer.lowerExpr(target.argumentExpression), target.argumentExpression);
|
|
4863
5552
|
if (key.type.kind !== "string") {
|
|
4864
5553
|
lowerer.unsupported("SC1090", target.argumentExpression, "indexing with non-string or non-number keys");
|
|
4865
5554
|
}
|
|
@@ -4870,9 +5559,25 @@ export function lowerElementWrite(lowerer, expr) {
|
|
|
4870
5559
|
return { kind: "exprStmt", expr: { kind: "libCall", fn: "dyn.keySet", args: [obj, key, value], type: VOID, loc }, loc };
|
|
4871
5560
|
}
|
|
4872
5561
|
}
|
|
5562
|
+
if (receiverIr?.kind === "object" || receiverIr?.kind === "record") {
|
|
5563
|
+
const key = recordKeyLiteralText(target.argumentExpression) ?? recordKeyTypeLiteralText(lowerer, target.argumentExpression);
|
|
5564
|
+
if (key !== null) {
|
|
5565
|
+
const receiver = lowerer.lowerExpr(target.expression);
|
|
5566
|
+
const field = representedClassFieldTarget(lowerer, target.expression, key, receiver);
|
|
5567
|
+
if (field) {
|
|
5568
|
+
const value = lowerer.lowerExprExpecting(expr.right, field.fieldType);
|
|
5569
|
+
return lowerer.fieldSetStmt(field, value, locOf(expr), target);
|
|
5570
|
+
}
|
|
5571
|
+
if (hasClassPayload(lowerer, receiver.type)) {
|
|
5572
|
+
if (receiver.type.kind === "object" && receiver.type.className === "%Error") {
|
|
5573
|
+
return { kind: "exprStmt", expr: lowerDynMemberAssignment(lowerer, expr, lowerer.coerceInto(target.expression, receiver, DYN)), loc: locOf(expr) };
|
|
5574
|
+
}
|
|
5575
|
+
lowerer.unsupported("SC1090", target, "writing undeclared properties through structural views of class instances");
|
|
5576
|
+
}
|
|
5577
|
+
}
|
|
5578
|
+
}
|
|
4873
5579
|
// Typed-array element write `b[i] = v` — bytesSet: the value is an f64
|
|
4874
|
-
// the runtime coerces JS-exactly; invalid indices
|
|
4875
|
-
// typed arrays are fixed-length; JS would ignore the write).
|
|
5580
|
+
// the runtime coerces JS-exactly; invalid indices are ignored.
|
|
4876
5581
|
if (receiverIr?.kind === "bytes") {
|
|
4877
5582
|
const recv = lowerer.lowerExpr(target.expression);
|
|
4878
5583
|
// The write twin: a typed-array .d.ts surface whose VALUE is a
|
|
@@ -4884,6 +5589,9 @@ export function lowerElementWrite(lowerer, expr) {
|
|
|
4884
5589
|
return { kind: "exprStmt", expr: { kind: "jsOp", op: "setIdx", args: [recv, key, value], type: VOID, loc }, loc };
|
|
4885
5590
|
}
|
|
4886
5591
|
const index = lowerer.lowerExpr(target.argumentExpression);
|
|
5592
|
+
if (index.type.kind === "dyn" && recv.type.kind === "bytes") {
|
|
5593
|
+
return { kind: "exprStmt", expr: lowerDynMemberAssignment(lowerer, expr, lowerer.coerceInto(target.expression, recv, DYN)), loc: locOf(expr) };
|
|
5594
|
+
}
|
|
4887
5595
|
if (index.type.kind !== "f64") {
|
|
4888
5596
|
lowerer.unsupported("SC1090", target.argumentExpression, "indexing with non-number keys");
|
|
4889
5597
|
}
|
|
@@ -4899,6 +5607,9 @@ export function lowerElementWrite(lowerer, expr) {
|
|
|
4899
5607
|
// a recordSet/recordKeySet over a jsval.
|
|
4900
5608
|
if (shape) {
|
|
4901
5609
|
const obj = lowerer.lowerExpr(target.expression);
|
|
5610
|
+
if (hasClassPayload(lowerer, obj.type)) {
|
|
5611
|
+
lowerer.unsupported("SC1090", target, "computed property writes through structural views of class instances (use a declared literal key)");
|
|
5612
|
+
}
|
|
4902
5613
|
if (obj.type.kind === "jsval") {
|
|
4903
5614
|
const key = lowerer.jsvalIn(lowerer.lowerExpr(target.argumentExpression), target.argumentExpression);
|
|
4904
5615
|
const value = lowerer.jsvalIn(lowerer.lowerExpr(expr.right), expr.right);
|
|
@@ -5050,9 +5761,9 @@ export function lowerElementWrite(lowerer, expr) {
|
|
|
5050
5761
|
// type (static `any` — mapTypeOf answers null without --dynamic)
|
|
5051
5762
|
// probes the receiver's own lowered world: a dyn value takes the same
|
|
5052
5763
|
// write, anything else falls through to the fences.
|
|
5053
|
-
if (receiverIr?.kind === "dyn" || receiverIr === null) {
|
|
5764
|
+
if (receiverIr?.kind === "dyn" || receiverIr?.kind === "object" || receiverIr === null) {
|
|
5054
5765
|
const obj = receiverIr !== null ? lowerer.lowerExpr(target.expression) : tryLowerExpression(lowerer, target.expression);
|
|
5055
|
-
if (obj !== null && obj.type.kind === "dyn") {
|
|
5766
|
+
if (obj !== null && (obj.type.kind === "dyn" || obj.type.kind === "object" && obj.type.className === "%Error" || isDynTypedRefType(obj.type))) {
|
|
5056
5767
|
const loc = locOf(expr);
|
|
5057
5768
|
const litKey = recordKeyLiteralText(target.argumentExpression);
|
|
5058
5769
|
let key = litKey !== null
|
|
@@ -5066,7 +5777,7 @@ export function lowerElementWrite(lowerer, expr) {
|
|
|
5066
5777
|
if (value.type.kind !== "dyn") {
|
|
5067
5778
|
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
5779
|
}
|
|
5069
|
-
return { kind: "exprStmt", expr: { kind: "libCall", fn: "dyn.keySet", args: [obj, key, value], type: VOID, loc }, loc };
|
|
5780
|
+
return { kind: "exprStmt", expr: { kind: "libCall", fn: "dyn.keySet", args: [lowerer.coerceToExpected(obj, DYN), key, value], type: VOID, loc }, loc };
|
|
5070
5781
|
}
|
|
5071
5782
|
}
|
|
5072
5783
|
if (receiverIr?.kind !== "array") {
|
|
@@ -5096,19 +5807,45 @@ export function lowerElementWrite(lowerer, expr) {
|
|
|
5096
5807
|
if (arr.type.kind !== "array") {
|
|
5097
5808
|
lowerer.unsupported("SC1090", target.expression, "assignment through a possibly missing nested array receiver");
|
|
5098
5809
|
}
|
|
5099
|
-
const index = lowerer.lowerExpr(target.argumentExpression);
|
|
5810
|
+
const index = lowerOptionalNumber(lowerer, lowerer.lowerExpr(target.argumentExpression), locOf(target.argumentExpression), target.argumentExpression);
|
|
5811
|
+
if (index.type.kind === "dyn" && lowerer.dynConvertible(arr.type)) {
|
|
5812
|
+
return { kind: "exprStmt", expr: lowerDynMemberAssignment(lowerer, expr, lowerer.coerceInto(target.expression, arr, DYN)), loc: locOf(expr) };
|
|
5813
|
+
}
|
|
5100
5814
|
if (index.type.kind !== "f64") {
|
|
5101
5815
|
lowerer.unsupported("SC1090", target.argumentExpression, "indexing with non-number keys");
|
|
5102
5816
|
}
|
|
5103
5817
|
// Optional array reads carry the source array's payload type plus an
|
|
5104
5818
|
// undefined arm. arrayValueStore turns that arm into the runtime's
|
|
5105
5819
|
// UNDEFINED state while keeping number[]/string[] payload storage scalar.
|
|
5106
|
-
|
|
5107
|
-
|
|
5820
|
+
let literal = expr.right;
|
|
5821
|
+
while (ts.isParenthesizedExpression(literal) || ts.isSatisfiesExpression(literal))
|
|
5822
|
+
literal = literal.expression;
|
|
5823
|
+
const value = ts.isObjectLiteralExpression(literal) || ts.isArrayLiteralExpression(literal)
|
|
5824
|
+
? lowerer.lowerExprExpecting(literal, arr.type.elem) : lowerer.lowerExpr(expr.right);
|
|
5825
|
+
// Validate the payload before emitting a native store. A dormant JS
|
|
5826
|
+
// method can contradict its JSDoc element type; retain its call-time
|
|
5827
|
+
// refusal instead of leaving invalid IR in a newly reachable method.
|
|
5828
|
+
const expected = value.type.kind === "undefinedT" ||
|
|
5829
|
+
value.type.kind === "union" && lowerer.armTag(value.type.unionId, UNDEFINED_T) >= 0
|
|
5830
|
+
? arrayValueType(lowerer, arr.type.elem) : arr.type.elem;
|
|
5831
|
+
return arrayValueStore(lowerer, arr, index, lowerer.coerceInto(expr.right, value, expected), arr.type.elem, locOf(expr));
|
|
5832
|
+
}
|
|
5833
|
+
/** Environment property names use ToPrimitive with the string hint, so
|
|
5834
|
+
* checked objects must execute their own conversion hooks. */
|
|
5835
|
+
export function lowerEnvironmentKey(lowerer, node) {
|
|
5836
|
+
const key = lowerer.lowerExpr(node);
|
|
5837
|
+
if (key.type.kind === "dyn")
|
|
5838
|
+
return { kind: "libCall", fn: "dyn.toStringCoerce", args: [key], type: STRING, loc: key.loc };
|
|
5839
|
+
return lowerRecordPropertyKey(lowerer, key, node);
|
|
5108
5840
|
}
|
|
5109
5841
|
export function ensureString(lowerer, e, node) {
|
|
5110
5842
|
if (e.type.kind === "string")
|
|
5111
5843
|
return e;
|
|
5844
|
+
if (e.type.kind === "url")
|
|
5845
|
+
return { kind: "libCall", fn: "url.href", args: [e], type: STRING, loc: e.loc };
|
|
5846
|
+
if (e.type.kind === "regex") {
|
|
5847
|
+
return { kind: "regexIntrinsic", method: "toString", receiver: e, args: [], type: STRING, loc: e.loc };
|
|
5848
|
+
}
|
|
5112
5849
|
if (e.type.kind === "bigint") {
|
|
5113
5850
|
return {
|
|
5114
5851
|
kind: "libCall",
|
|
@@ -5124,18 +5861,21 @@ export function ensureString(lowerer, e, node) {
|
|
|
5124
5861
|
// strings verbatim, arrays via join, objects as "[object Object]").
|
|
5125
5862
|
return { kind: "toString", operand: e, type: STRING, loc: e.loc };
|
|
5126
5863
|
}
|
|
5864
|
+
if (e.type.kind === "bytes") {
|
|
5865
|
+
// Preserve the runtime brand: typed arrays join numeric elements,
|
|
5866
|
+
// while a Buffer stored in a Uint8Array slot decodes its bytes.
|
|
5867
|
+
const boxed = { kind: "dynFrom", value: e, type: DYN, loc: e.loc };
|
|
5868
|
+
return { kind: "toString", operand: boxed, type: STRING, loc: e.loc };
|
|
5869
|
+
}
|
|
5127
5870
|
if (e.type.kind === "jsval") {
|
|
5128
5871
|
// String(v) in the engine — JS-exact (and Node-exact in templates).
|
|
5129
5872
|
return { kind: "jsOp", op: "toStr", args: [e], type: STRING, loc: e.loc };
|
|
5130
5873
|
}
|
|
5131
5874
|
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.
|
|
5875
|
+
// String(err) / `${err}` uses the same dispatch as err.toString().
|
|
5136
5876
|
for (let c = lowerer.classes.get(e.type.className) ?? null; c; c = c.base) {
|
|
5137
5877
|
if (c.builtinError) {
|
|
5138
|
-
return
|
|
5878
|
+
return errorToStringCall(lowerer, e);
|
|
5139
5879
|
}
|
|
5140
5880
|
}
|
|
5141
5881
|
}
|
|
@@ -5326,6 +6066,38 @@ export function lowerAsExpression(lowerer, expr) {
|
|
|
5326
6066
|
return lowerer.jsvalIn(inner, expr.expression);
|
|
5327
6067
|
}
|
|
5328
6068
|
const target = lowerer.mapTypeOf(targetTs0);
|
|
6069
|
+
// Unknown-only record views use the checked-dynamic representation.
|
|
6070
|
+
// A native record asserted to such a view still denotes the original
|
|
6071
|
+
// object. Box a live reference instead of erasing the assertion and
|
|
6072
|
+
// handing native storage to a checked-dynamic property operation.
|
|
6073
|
+
if (target?.kind === "dyn" && (targetTs0.flags & ts.TypeFlags.Object) !== 0) {
|
|
6074
|
+
const sourceShape = inner.type.kind === "record" ? lowerer.shapes.get(inner.type.shapeId) : undefined;
|
|
6075
|
+
if (hasClassPayload(lowerer, inner.type) || sourceShape && shapeHasAccessorSlots(sourceShape)) {
|
|
6076
|
+
lowerer.unsupported("SC1090", expr, "opaque structural views of classes or accessor records (name the property's concrete type to retain native accessor dispatch)");
|
|
6077
|
+
}
|
|
6078
|
+
const boxed = lowerer.coerceInto(expr, inner, DYN);
|
|
6079
|
+
return boxed.kind === "dynFrom" ? { ...boxed, liveRef: true } : boxed;
|
|
6080
|
+
}
|
|
6081
|
+
// Collection assertions are interface views only: their backing
|
|
6082
|
+
// value must already have the same native key/element/value layout.
|
|
6083
|
+
// Erasing a mock or a different instantiation here would let member
|
|
6084
|
+
// lowering interpret a record (or another ABI) as a native handle.
|
|
6085
|
+
if (target?.kind === "map" || target?.kind === "set") {
|
|
6086
|
+
return lowerer.coerceInto(expr, inner, target);
|
|
6087
|
+
}
|
|
6088
|
+
// A class-derived interface still names the class's native layout.
|
|
6089
|
+
// An assertion must not let a structural mock acquire that layout;
|
|
6090
|
+
// unions retain their checked extraction below, and real instances
|
|
6091
|
+
// keep the ordinary class narrowing path.
|
|
6092
|
+
if (target?.kind === "object" && inner.type.kind !== "object" && inner.type.kind !== "union") {
|
|
6093
|
+
return lowerer.coerceInto(expr, inner, target);
|
|
6094
|
+
}
|
|
6095
|
+
if (target?.kind === "object" && inner.type.kind === "object" &&
|
|
6096
|
+
!typeEquals(target, inner.type) &&
|
|
6097
|
+
!lowerer.isSubclassOf(inner.type.className, target.className) &&
|
|
6098
|
+
!lowerer.isSubclassOf(target.className, inner.type.className)) {
|
|
6099
|
+
return lowerer.coerceInto(expr, inner, target);
|
|
6100
|
+
}
|
|
5329
6101
|
// Static assertions normally erase, but record layouts are
|
|
5330
6102
|
// monomorphic: a consumer selected from the asserted shape must see
|
|
5331
6103
|
// that shape physically. Reuse the ordinary slot coercion so plain
|
|
@@ -5373,12 +6145,26 @@ export function lowerAsExpression(lowerer, expr) {
|
|
|
5373
6145
|
return { kind: "jsExit", value: inner, type: target, loc: locOf(expr) };
|
|
5374
6146
|
}
|
|
5375
6147
|
const targetTs = lowerer.checker.getTypeFromTypeNode(expr.type);
|
|
5376
|
-
const target = lowerer.mapTypeOf(targetTs);
|
|
6148
|
+
const target = lowerer.mapTypeOf(targetTs) ?? dynFallbackType(lowerer, expr.type, targetTs);
|
|
5377
6149
|
if (!target)
|
|
5378
6150
|
lowerer.badType(expr.type, targetTs);
|
|
5379
6151
|
if (target.kind === "dyn")
|
|
5380
6152
|
return inner; // `as unknown`: erasure
|
|
5381
|
-
|
|
6153
|
+
// An all-`unknown`-fields record target (`err as { code?: unknown }`
|
|
6154
|
+
// — the errno-probing idiom): there is nothing to validate (every
|
|
6155
|
+
// field is unknown, exactly what the dyn value already answers) and
|
|
6156
|
+
// nothing to build — the cast is pure typing, so it ERASES and the
|
|
6157
|
+
// reads ride the dyn keyed read.
|
|
6158
|
+
if (target.kind === "record") {
|
|
6159
|
+
const shape = lowerer.shapes.get(target.shapeId);
|
|
6160
|
+
if (shape &&
|
|
6161
|
+
!shape.tuple &&
|
|
6162
|
+
shape.fields.every((f) => f.type.kind === "dyn") &&
|
|
6163
|
+
(!shape.indexValue || shape.indexValue.kind === "dyn")) {
|
|
6164
|
+
return inner;
|
|
6165
|
+
}
|
|
6166
|
+
}
|
|
6167
|
+
if (target.kind === "void" || !canDynCheckTo(target, (id) => lowerer.shapes.get(id), (id) => lowerer.unions.get(id))) {
|
|
5382
6168
|
// Bare undefined-armed targets pass when every OTHER arm is
|
|
5383
6169
|
// JSON-safe: the checked-dynamic tree holds a first-class undefined value now
|
|
5384
6170
|
// (index-signature overflow reads produce it for missing keys), and
|
|
@@ -5414,20 +6200,6 @@ export function lowerAsExpression(lowerer, expr) {
|
|
|
5414
6200
|
if (DYN_HANDLE_KINDS.has(target.kind)) {
|
|
5415
6201
|
return { kind: "dynCheck", value: inner, type: target, loc: locOf(expr) };
|
|
5416
6202
|
}
|
|
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
6203
|
lowerer.unsupported("SC1090", expr, `a checked cast of 'unknown' to '${target.kind === "void" ? "void" : lowerer.fmt(target)}' ` +
|
|
5432
6204
|
`(a dynamic value can only be validated against JSON-representable types: ` +
|
|
5433
6205
|
`number, string, boolean, records, arrays, and unions of those)`);
|
|
@@ -5445,7 +6217,10 @@ export function lowerPrefixUnary(lowerer, expr) {
|
|
|
5445
6217
|
if (raw.type.kind === "bigint") {
|
|
5446
6218
|
return { kind: "libCall", fn: "bigint.neg", args: [raw], type: BIGINT_T, loc };
|
|
5447
6219
|
}
|
|
5448
|
-
|
|
6220
|
+
// Match the checked numeric boundary used by JS binary arithmetic.
|
|
6221
|
+
const operand = raw.type.kind === "dyn" && isJsSourceFile(expr.getSourceFile())
|
|
6222
|
+
? { kind: "dynCheck", value: raw, type: F64, loc }
|
|
6223
|
+
: lowerOptionalNumber(lowerer, raw, loc, expr.operand);
|
|
5449
6224
|
if (operand.type.kind !== "f64")
|
|
5450
6225
|
lowerer.unsupported("SC1043", expr);
|
|
5451
6226
|
if (operand.kind === "numLit")
|
|
@@ -5454,6 +6229,9 @@ export function lowerPrefixUnary(lowerer, expr) {
|
|
|
5454
6229
|
}
|
|
5455
6230
|
case ts.SyntaxKind.PlusToken: {
|
|
5456
6231
|
const raw = lowerer.lowerExpr(expr.operand);
|
|
6232
|
+
if (raw.type.kind === "dyn") {
|
|
6233
|
+
return { kind: "libCall", fn: "dyn.toNumberCoerce", args: [raw], type: F64, loc };
|
|
6234
|
+
}
|
|
5457
6235
|
// Unary + is ToNumber; on an already-number operand it's identity,
|
|
5458
6236
|
// and a STRING operand runs the runtime's ECMA-exact StringToNumber
|
|
5459
6237
|
// (num.fromString — Number(aString)'s lowering, scr_string.c).
|
|
@@ -5479,14 +6257,16 @@ export function lowerPrefixUnary(lowerer, expr) {
|
|
|
5479
6257
|
if (raw.type.kind === "bigint") {
|
|
5480
6258
|
return { kind: "libCall", fn: "bigint.not", args: [raw], type: BIGINT_T, loc };
|
|
5481
6259
|
}
|
|
6260
|
+
if (raw.type.kind === "dyn")
|
|
6261
|
+
return {
|
|
6262
|
+
kind: "libCall", fn: "dyn.bitwise", args: [raw, dynUndefinedExpr(loc), { kind: "strLit", value: "~", type: STRING, loc }], type: DYN, loc,
|
|
6263
|
+
};
|
|
5482
6264
|
// A checked-dynamic value retains enough JavaScript structure to
|
|
5483
6265
|
// run exact ToNumber, including the object valueOf/toString
|
|
5484
6266
|
// protocol. This is the static `any`/JS-lane answer for idioms
|
|
5485
6267
|
// such as `while (~index)`, where inference may have widened an
|
|
5486
6268
|
// 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);
|
|
6269
|
+
const operand = lowerOptionalNumber(lowerer, raw, loc, expr.operand);
|
|
5490
6270
|
if (operand.type.kind !== "f64")
|
|
5491
6271
|
lowerer.unsupported("SC1043", expr);
|
|
5492
6272
|
return { kind: "unary", op: "~", operand, type: F64, loc };
|
|
@@ -5496,7 +6276,7 @@ export function lowerPrefixUnary(lowerer, expr) {
|
|
|
5496
6276
|
// `++x` / `--x` in expression position: yields the NEW value.
|
|
5497
6277
|
return lowerIncDec(lowerer, expr, true);
|
|
5498
6278
|
}
|
|
5499
|
-
lowerer.unsupported("SC1090", expr, `syntax '${ts.
|
|
6279
|
+
lowerer.unsupported("SC1090", expr, `syntax '${ts.syntaxKindName(expr.kind)}'`);
|
|
5500
6280
|
}
|
|
5501
6281
|
/** `x++`/`x--`/`++x`/`--x` in EXPRESSION position — the incDec node:
|
|
5502
6282
|
* read, write ±1, yield old (postfix) or new (prefix) value. Receivers
|
|
@@ -5508,6 +6288,14 @@ function lowerIncDec(lowerer, expr, prefix) {
|
|
|
5508
6288
|
const loc = locOf(expr);
|
|
5509
6289
|
const op = expr.operator === ts.SyntaxKind.PlusPlusToken ? "+" : "-";
|
|
5510
6290
|
if (!ts.isIdentifier(expr.operand)) {
|
|
6291
|
+
if (ts.isPropertyAccessExpression(expr.operand)) {
|
|
6292
|
+
const target = staticFieldWriteTarget(lowerer, expr.operand);
|
|
6293
|
+
if (target) {
|
|
6294
|
+
if (target.type.kind !== "f64")
|
|
6295
|
+
lowerer.unsupported("SC1043", expr);
|
|
6296
|
+
return { kind: "incDec", op, prefix, localId: target.id, type: F64, loc };
|
|
6297
|
+
}
|
|
6298
|
+
}
|
|
5511
6299
|
// CLASS-FIELD receivers (`if (--this.limit === 0)` — countdown.js's
|
|
5512
6300
|
// dec(); `--this[kLimit]` — its symbol-keyed spelling): a single-
|
|
5513
6301
|
// evaluation read-modify-write over the instance, yielding old/new
|
|
@@ -5519,6 +6307,25 @@ function lowerIncDec(lowerer, expr, prefix) {
|
|
|
5519
6307
|
// accessor-backed names keep the fence.
|
|
5520
6308
|
if ((ts.isPropertyAccessExpression(expr.operand) || ts.isElementAccessExpression(expr.operand)) &&
|
|
5521
6309
|
!expr.operand.questionDotToken) {
|
|
6310
|
+
if (ts.isPropertyAccessExpression(expr.operand) && isJsSourceFile(expr.getSourceFile())) {
|
|
6311
|
+
const receiver = lowerer.lowerExpr(expr.operand.expression);
|
|
6312
|
+
if (receiver.type.kind === "dyn") {
|
|
6313
|
+
const local = lowerer.declareHiddenLocal("%incrementReceiver", DYN);
|
|
6314
|
+
const old = lowerer.declareHiddenLocal("%incrementOld", DYN);
|
|
6315
|
+
const next = lowerer.declareHiddenLocal("%incrementNext", DYN);
|
|
6316
|
+
const value = varRef(local.id, DYN, loc);
|
|
6317
|
+
const key = { kind: "strLit", value: expr.operand.name.text, type: STRING, loc };
|
|
6318
|
+
return { kind: "seqExpr", stmts: [
|
|
6319
|
+
{ kind: "varDecl", localId: local.id, init: receiver, loc },
|
|
6320
|
+
{ kind: "varDecl", localId: old.id, init: { kind: "libCall", fn: "dyn.toNumeric", args: [
|
|
6321
|
+
{ kind: "dynKeyGet", value, key, type: DYN, loc },
|
|
6322
|
+
], type: DYN, loc }, loc },
|
|
6323
|
+
{ kind: "varDecl", localId: next.id, init: { kind: "libCall", fn: "dyn.increment", args: [varRef(old.id, DYN, loc),
|
|
6324
|
+
{ kind: "boolLit", value: op === "+", type: BOOL, loc }], type: DYN, loc }, loc },
|
|
6325
|
+
{ kind: "exprStmt", expr: { kind: "libCall", fn: "dyn.keySet", args: [value, key, varRef(next.id, DYN, loc)], type: VOID, loc }, loc },
|
|
6326
|
+
], result: varRef(prefix ? next.id : old.id, DYN, loc), type: DYN, loc };
|
|
6327
|
+
}
|
|
6328
|
+
}
|
|
5522
6329
|
const target = ts.isPropertyAccessExpression(expr.operand)
|
|
5523
6330
|
? lowerer.fieldTarget(expr.operand)
|
|
5524
6331
|
: symbolFieldTarget(lowerer, expr.operand);
|
|
@@ -5548,6 +6355,16 @@ function lowerIncDec(lowerer, expr, prefix) {
|
|
|
5548
6355
|
if (!target) {
|
|
5549
6356
|
lowerer.rejectUnresolved(expr.operand, `increment/decrement of '${expr.operand.text}' (not a writable local or module global)`);
|
|
5550
6357
|
}
|
|
6358
|
+
if (target.type.kind === "dyn") {
|
|
6359
|
+
const old = lowerer.declareHiddenLocal("%numericOld", DYN);
|
|
6360
|
+
const next = lowerer.declareHiddenLocal("%numericNext", DYN);
|
|
6361
|
+
const read = lowerer.lowerExpr(expr.operand);
|
|
6362
|
+
return { kind: "seqExpr", stmts: [
|
|
6363
|
+
{ kind: "varDecl", localId: old.id, init: { kind: "libCall", fn: "dyn.toNumeric", args: [read], type: DYN, loc }, loc },
|
|
6364
|
+
{ 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 },
|
|
6365
|
+
{ kind: "assign", localId: target.id, value: varRef(next.id, DYN, loc), loc },
|
|
6366
|
+
], result: varRef(prefix ? next.id : old.id, DYN, loc), type: DYN, loc };
|
|
6367
|
+
}
|
|
5551
6368
|
if (target.type.kind !== "f64")
|
|
5552
6369
|
lowerer.unsupported("SC1043", expr);
|
|
5553
6370
|
return { kind: "incDec", op, prefix, localId: target.id, type: F64, loc };
|
|
@@ -5599,7 +6416,7 @@ function lowerAnyBinaryInIsland(lowerer, expr, left, right, loc) {
|
|
|
5599
6416
|
};
|
|
5600
6417
|
const jop = JS_BIN[op];
|
|
5601
6418
|
if (jop === undefined) {
|
|
5602
|
-
lowerer.unsupported("SC1090", expr, `operator '${ts.tokenToString(op) ?? ts.
|
|
6419
|
+
lowerer.unsupported("SC1090", expr, `operator '${ts.tokenToString(op) ?? ts.syntaxKindName(op)}' on 'any' values`);
|
|
5603
6420
|
}
|
|
5604
6421
|
const type = jsOpResultKind(jop) === "bool" ? BOOL : JSVAL;
|
|
5605
6422
|
return {
|
|
@@ -5608,6 +6425,40 @@ function lowerAnyBinaryInIsland(lowerer, expr, left, right, loc) {
|
|
|
5608
6425
|
type, loc,
|
|
5609
6426
|
};
|
|
5610
6427
|
}
|
|
6428
|
+
export function lowerCompoundValueToTarget(lowerer, expr, compound, target) {
|
|
6429
|
+
const loc = locOf(expr);
|
|
6430
|
+
const read = { kind: "varRef", localId: target.id, type: target.type, loc: locOf(expr.left) };
|
|
6431
|
+
const rhs = lowerer.lowerExpr(expr.right);
|
|
6432
|
+
const numericRhs = lowerOptionalNumber(lowerer, rhs, loc);
|
|
6433
|
+
if (target.type.kind === "jsval" || rhs.type.kind === "jsval") {
|
|
6434
|
+
const JS_COMPOUND = { "+": "add", "-": "sub", "*": "mul", "/": "div", "%": "mod", "**": "pow" };
|
|
6435
|
+
const jop = JS_COMPOUND[compound];
|
|
6436
|
+
if (jop === undefined)
|
|
6437
|
+
lowerer.unsupported("SC1043", expr);
|
|
6438
|
+
const wrapped = {
|
|
6439
|
+
kind: "jsOp", op: jop,
|
|
6440
|
+
args: [lowerer.jsvalIn(read, expr.left), lowerer.jsvalIn(rhs, expr.right)],
|
|
6441
|
+
type: JSVAL, loc,
|
|
6442
|
+
};
|
|
6443
|
+
return lowerer.coerceInto(expr, wrapped, target.type);
|
|
6444
|
+
}
|
|
6445
|
+
if (compound === "+" && target.type.kind === "string") {
|
|
6446
|
+
return { kind: "strConcat", left: read, right: lowerer.ensureString(rhs, expr.right), type: STRING, loc };
|
|
6447
|
+
}
|
|
6448
|
+
if (target.type.kind === "f64" && numericRhs.type.kind === "f64") {
|
|
6449
|
+
return { kind: "bin", op: compound, left: read, right: numericRhs, type: F64, loc };
|
|
6450
|
+
}
|
|
6451
|
+
if ((target.type.kind === "dyn" || rhs.type.kind === "dyn") &&
|
|
6452
|
+
isJsSourceFile(expr.getSourceFile())) {
|
|
6453
|
+
const left = lowerer.coerceInto(expr.left, read, DYN);
|
|
6454
|
+
const right = lowerer.coerceInto(expr.right, rhs, DYN);
|
|
6455
|
+
const numeric = ["-", "*", "/", "%", "**"].includes(compound);
|
|
6456
|
+
const computed = { kind: "libCall", fn: compound === "+" ? "dyn.add" : numeric ? "dyn.arithmetic" : "dyn.bitwise",
|
|
6457
|
+
args: compound === "+" ? [left, right] : [left, right, { kind: "strLit", value: compound, type: STRING, loc }], type: DYN, loc };
|
|
6458
|
+
return lowerer.coerceInto(expr, computed, target.type);
|
|
6459
|
+
}
|
|
6460
|
+
lowerer.unsupported("SC1043", expr);
|
|
6461
|
+
}
|
|
5611
6462
|
export function lowerBinary(lowerer, expr) {
|
|
5612
6463
|
const loc = locOf(expr);
|
|
5613
6464
|
const op = expr.operatorToken.kind;
|
|
@@ -5615,6 +6466,15 @@ export function lowerBinary(lowerer, expr) {
|
|
|
5615
6466
|
if (cacheHas)
|
|
5616
6467
|
return cacheHas;
|
|
5617
6468
|
if (op === ts.SyntaxKind.EqualsToken || (op >= ts.SyntaxKind.FirstCompoundAssignment && op <= ts.SyntaxKind.LastCompoundAssignment)) {
|
|
6469
|
+
if (op === ts.SyntaxKind.EqualsToken) {
|
|
6470
|
+
const callableWrite = lowerNativeFunctionAssignment(lowerer, expr);
|
|
6471
|
+
if (callableWrite)
|
|
6472
|
+
return callableWrite;
|
|
6473
|
+
}
|
|
6474
|
+
if (op === ts.SyntaxKind.QuestionQuestionEqualsToken ||
|
|
6475
|
+
op === ts.SyntaxKind.AmpersandAmpersandEqualsToken || op === ts.SyntaxKind.BarBarEqualsToken) {
|
|
6476
|
+
return lowerShortCircuitAssignment(lowerer, expr);
|
|
6477
|
+
}
|
|
5618
6478
|
if (ts.isPropertyAccessExpression(expr.left) || ts.isElementAccessExpression(expr.left)) {
|
|
5619
6479
|
fenceNodeModuleMutation(lowerer, expr.left, "assignment");
|
|
5620
6480
|
}
|
|
@@ -5640,8 +6500,29 @@ export function lowerBinary(lowerer, expr) {
|
|
|
5640
6500
|
}
|
|
5641
6501
|
const indexedCompound = COMPOUND_ASSIGN_OPS[op];
|
|
5642
6502
|
if (indexedCompound !== undefined && ts.isElementAccessExpression(expr.left)) {
|
|
6503
|
+
if (symbolFieldInfo(lowerer, expr.left))
|
|
6504
|
+
return lowerFieldCompoundValue(lowerer, expr.left, indexedCompound, expr.right, loc);
|
|
5643
6505
|
return lowerElementCompound(lowerer, expr, indexedCompound);
|
|
5644
6506
|
}
|
|
6507
|
+
if (indexedCompound !== undefined && ts.isIdentifier(expr.left)) {
|
|
6508
|
+
const target = lowerer.resolveWritable(expr.left);
|
|
6509
|
+
if (!target) {
|
|
6510
|
+
lowerer.rejectUnresolved(expr.left, "assignment to an unresolved variable");
|
|
6511
|
+
}
|
|
6512
|
+
const value = lowerCompoundValueToTarget(lowerer, expr, indexedCompound, target);
|
|
6513
|
+
return { kind: "assignExpr", localId: target.id, value, type: target.type, loc };
|
|
6514
|
+
}
|
|
6515
|
+
if (indexedCompound !== undefined &&
|
|
6516
|
+
(ts.isPropertyAccessExpression(expr.left) || ts.isElementAccessExpression(expr.left)) &&
|
|
6517
|
+
!(ts.isPropertyAccessExpression(expr.left) && expr.left.questionDotToken)) {
|
|
6518
|
+
const target = expandoWritableTarget(lowerer, expr.left) ??
|
|
6519
|
+
(ts.isPropertyAccessExpression(expr.left) ? nsWritableTarget(lowerer, expr.left) : null);
|
|
6520
|
+
if (target) {
|
|
6521
|
+
const value = lowerCompoundValueToTarget(lowerer, expr, indexedCompound, target);
|
|
6522
|
+
return { kind: "assignExpr", localId: target.id, value, type: target.type, loc };
|
|
6523
|
+
}
|
|
6524
|
+
return lowerFieldCompoundValue(lowerer, expr.left, indexedCompound, expr.right, loc);
|
|
6525
|
+
}
|
|
5645
6526
|
// `events.defaultMaxListeners = v` — the module-property write
|
|
5646
6527
|
// Node validates (validateNumber(n, 'defaultMaxListeners', 0)):
|
|
5647
6528
|
// the value crosses into the checked-dynamic tree and the runtime ladder throws
|
|
@@ -5694,6 +6575,37 @@ export function lowerBinary(lowerer, expr) {
|
|
|
5694
6575
|
const value = lowerer.lowerExprExpecting(expr.right, target.type);
|
|
5695
6576
|
return { kind: "assignExpr", localId: target.id, value, type: target.type, loc };
|
|
5696
6577
|
}
|
|
6578
|
+
// Bundled numeric enums use e[e["name"] = value] = "name".
|
|
6579
|
+
// Capture the receiver and key before the RHS. PutValue coerces the
|
|
6580
|
+
// key afterward, then yields the RHS without rereading the property.
|
|
6581
|
+
if (ts.isElementAccessExpression(expr.left) && !expr.left.questionDotToken) {
|
|
6582
|
+
const recv = tryLowerExpression(lowerer, expr.left.expression);
|
|
6583
|
+
if (recv && (recv.type.kind === "dyn" ||
|
|
6584
|
+
recv.type.kind === "array" && isJsSourceFile(expr.getSourceFile()) && lowerer.dynConvertible(recv.type))) {
|
|
6585
|
+
return lowerDynMemberAssignment(lowerer, expr, lowerer.coerceInto(expr.left.expression, recv, DYN));
|
|
6586
|
+
}
|
|
6587
|
+
if (recv?.type.kind === "array" || recv?.type.kind === "bytes") {
|
|
6588
|
+
const receiver = lowerer.declareHiddenLocal("%setArray", recv.type);
|
|
6589
|
+
const index = lowerer.lowerExpr(expr.left.argumentExpression);
|
|
6590
|
+
if (index.type.kind !== "f64")
|
|
6591
|
+
lowerer.unsupported("SC1090", expr.left.argumentExpression, "indexing with non-number keys");
|
|
6592
|
+
const key = lowerer.declareHiddenLocal("%setIndex", F64);
|
|
6593
|
+
const raw = lowerer.lowerExpr(expr.right);
|
|
6594
|
+
const value = lowerer.declareHiddenLocal("%setValue", raw.type);
|
|
6595
|
+
const result = varRef(value.id, raw.type, loc);
|
|
6596
|
+
const arr = varRef(receiver.id, recv.type, loc);
|
|
6597
|
+
const idx = varRef(key.id, F64, loc);
|
|
6598
|
+
const write = recv.type.kind === "array"
|
|
6599
|
+
? arrayValueStore(lowerer, arr, idx, result, recv.type.elem, loc)
|
|
6600
|
+
: { kind: "bytesSet", arr, index: idx, value: lowerer.coerceInto(expr.right, result, F64), loc };
|
|
6601
|
+
return { kind: "seqExpr", stmts: [
|
|
6602
|
+
{ kind: "varDecl", localId: receiver.id, init: recv, loc },
|
|
6603
|
+
{ kind: "varDecl", localId: key.id, init: index, loc },
|
|
6604
|
+
{ kind: "varDecl", localId: value.id, init: raw, loc },
|
|
6605
|
+
write,
|
|
6606
|
+
], result, type: result.type, loc };
|
|
6607
|
+
}
|
|
6608
|
+
}
|
|
5697
6609
|
// `h.k = v` on an ISLAND receiver in VALUE position: the engine
|
|
5698
6610
|
// property write (setProp throws the engine's TypeErrors on
|
|
5699
6611
|
// nullish receivers, bridged catchably), the RHS value threaded
|
|
@@ -5734,30 +6646,22 @@ export function lowerBinary(lowerer, expr) {
|
|
|
5734
6646
|
// consumer sees exactly what the checker typed.
|
|
5735
6647
|
if (ts.isPropertyAccessExpression(expr.left) && !expr.left.questionDotToken) {
|
|
5736
6648
|
const recv = tryLowerExpression(lowerer, expr.left.expression);
|
|
5737
|
-
if (recv && recv.type.kind === "dyn")
|
|
5738
|
-
|
|
5739
|
-
|
|
5740
|
-
|
|
5741
|
-
const
|
|
5742
|
-
const
|
|
5743
|
-
|
|
5744
|
-
|
|
5745
|
-
|
|
5746
|
-
const
|
|
6649
|
+
if (recv && recv.type.kind === "dyn")
|
|
6650
|
+
return lowerDynMemberAssignment(lowerer, expr, recv);
|
|
6651
|
+
const field = lowerer.fieldTarget(expr.left);
|
|
6652
|
+
if (field) {
|
|
6653
|
+
const recvTmp = lowerer.declareHiddenLocal("%setReceiver", field.obj.type);
|
|
6654
|
+
const receiver = field.obj;
|
|
6655
|
+
field.obj = varRef(recvTmp.id, receiver.type, loc);
|
|
6656
|
+
const value = lowerer.lowerExprExpecting(expr.right, field.fieldType);
|
|
6657
|
+
const valTmp = lowerer.declareHiddenLocal("%setValue", value.type);
|
|
6658
|
+
const result = varRef(valTmp.id, value.type, loc);
|
|
5747
6659
|
return {
|
|
5748
|
-
kind: "seqExpr",
|
|
5749
|
-
|
|
5750
|
-
{ kind: "varDecl", localId:
|
|
5751
|
-
|
|
5752
|
-
|
|
5753
|
-
kind: "exprStmt",
|
|
5754
|
-
expr: { kind: "libCall", fn: "dyn.keySet", args: [{ kind: "varRef", localId: recvTmp.id, type: DYN, loc }, key, stored], type: VOID, loc },
|
|
5755
|
-
loc,
|
|
5756
|
-
},
|
|
5757
|
-
],
|
|
5758
|
-
result: valRef(),
|
|
5759
|
-
type: rhsVal.type,
|
|
5760
|
-
loc,
|
|
6660
|
+
kind: "seqExpr", stmts: [
|
|
6661
|
+
{ kind: "varDecl", localId: recvTmp.id, init: receiver, loc },
|
|
6662
|
+
{ kind: "varDecl", localId: valTmp.id, init: value, loc },
|
|
6663
|
+
lowerer.fieldSetStmt(field, result, loc, expr.left),
|
|
6664
|
+
], result, type: result.type, loc,
|
|
5761
6665
|
};
|
|
5762
6666
|
}
|
|
5763
6667
|
}
|
|
@@ -5786,221 +6690,7 @@ export function lowerBinary(lowerer, expr) {
|
|
|
5786
6690
|
lowerer.unsupported("SC1040", expr, undefined, "object-to-primitive loose equality can call user-defined valueOf/toString methods — compare explicit primitive conversions instead");
|
|
5787
6691
|
}
|
|
5788
6692
|
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
|
-
};
|
|
6693
|
+
return lowerLogicalChain(lowerer, expr);
|
|
6004
6694
|
}
|
|
6005
6695
|
if (op === ts.SyntaxKind.QuestionQuestionToken) {
|
|
6006
6696
|
return lowerer.lowerNullishCoalesce(expr, loc);
|
|
@@ -6011,7 +6701,7 @@ export function lowerBinary(lowerer, expr) {
|
|
|
6011
6701
|
// runs for EFFECT exactly as its statement lowering (JS discards its
|
|
6012
6702
|
// value), the right operand is the expression's value — a seqExpr.
|
|
6013
6703
|
// The validator restricts seqExpr statements to straight-line writes
|
|
6014
|
-
// (the
|
|
6704
|
+
// (the lowering point is mid-expression), so a left operand whose
|
|
6015
6705
|
// statement lowering needs real control flow keeps a pointed fence
|
|
6016
6706
|
// instead of tripping the validator downstream.
|
|
6017
6707
|
const effect = lowerer.lowerExprStatement(expr.left);
|
|
@@ -6052,7 +6742,7 @@ export function lowerBinary(lowerer, expr) {
|
|
|
6052
6742
|
op === ts.SyntaxKind.ExclamationEqualsEqualsToken) {
|
|
6053
6743
|
const negated = op === ts.SyntaxKind.ExclamationEqualsEqualsToken;
|
|
6054
6744
|
const unit = left.kind === "unitLit" ? left : right.kind === "unitLit" ? right : null;
|
|
6055
|
-
const other =
|
|
6745
|
+
const other = left.kind === "unitLit" ? right : left;
|
|
6056
6746
|
if (unit && other.type.kind === "dyn") {
|
|
6057
6747
|
return {
|
|
6058
6748
|
kind: "dynTest",
|
|
@@ -6069,12 +6759,30 @@ export function lowerBinary(lowerer, expr) {
|
|
|
6069
6759
|
// answers false). dyn vs dyn keeps the fence below.
|
|
6070
6760
|
const dynSide = left.type.kind === "dyn" ? left : right;
|
|
6071
6761
|
const scalarSide = dynSide === left ? right : left;
|
|
6762
|
+
const reference = scalarSide.type;
|
|
6763
|
+
if ((isDynTypedRefType(reference) || reference.kind === "record" || reference.kind === "array" ||
|
|
6764
|
+
reference.kind === "bytes" || reference.kind === "func" || reference.kind === "classval" || reference.kind === "regex" || reference.kind === "url" ||
|
|
6765
|
+
reference.kind === "bigint" || reference.kind === "symbol" || reference.kind === "map" || reference.kind === "set" || reference.kind === "union" || DYN_HANDLE_KINDS.has(reference.kind)) &&
|
|
6766
|
+
lowerer.dynConvertible(reference)) {
|
|
6767
|
+
const boxed = {
|
|
6768
|
+
kind: "dynFrom", value: scalarSide, type: DYN, loc: scalarSide.loc,
|
|
6769
|
+
...(reference.kind === "record" || reference.kind === "array" || reference.kind === "bytes" ? { liveRef: true } : {}),
|
|
6770
|
+
};
|
|
6771
|
+
return {
|
|
6772
|
+
kind: "dynScalarEq",
|
|
6773
|
+
left: scalarSide === left ? boxed : left,
|
|
6774
|
+
right: scalarSide === right ? boxed : right,
|
|
6775
|
+
...(negated ? { negated: true } : {}),
|
|
6776
|
+
type: BOOL,
|
|
6777
|
+
loc,
|
|
6778
|
+
};
|
|
6779
|
+
}
|
|
6072
6780
|
if (dynSide.type.kind === "dyn" &&
|
|
6073
6781
|
(scalarSide.type.kind === "f64" || scalarSide.type.kind === "string" || scalarSide.type.kind === "bool" ||
|
|
6074
6782
|
// dyn vs dyn (`context.actual !== context.exact` —
|
|
6075
6783
|
// test/common's exit accounting): the runtime's whole-dyn
|
|
6076
6784
|
// strict equality — scalars by value, units by kind,
|
|
6077
|
-
// reference kinds by
|
|
6785
|
+
// reference kinds by identity (scr_dyn_strict_eq).
|
|
6078
6786
|
scalarSide.type.kind === "dyn")) {
|
|
6079
6787
|
return {
|
|
6080
6788
|
kind: "dynScalarEq",
|
|
@@ -6086,16 +6794,26 @@ export function lowerBinary(lowerer, expr) {
|
|
|
6086
6794
|
};
|
|
6087
6795
|
}
|
|
6088
6796
|
}
|
|
6089
|
-
|
|
6090
|
-
|
|
6091
|
-
|
|
6092
|
-
|
|
6093
|
-
|
|
6094
|
-
|
|
6095
|
-
|
|
6096
|
-
|
|
6097
|
-
|
|
6098
|
-
|
|
6797
|
+
const bitwise = {
|
|
6798
|
+
[ts.SyntaxKind.AmpersandToken]: "&", [ts.SyntaxKind.BarToken]: "|", [ts.SyntaxKind.CaretToken]: "^",
|
|
6799
|
+
[ts.SyntaxKind.LessThanLessThanToken]: "<<", [ts.SyntaxKind.GreaterThanGreaterThanToken]: ">>",
|
|
6800
|
+
[ts.SyntaxKind.GreaterThanGreaterThanGreaterThanToken]: ">>>",
|
|
6801
|
+
};
|
|
6802
|
+
if (bitwise[op]) {
|
|
6803
|
+
const l = lowerer.coerceToExpected(left, DYN);
|
|
6804
|
+
const r = lowerer.coerceToExpected(right, DYN);
|
|
6805
|
+
if (l.type.kind === "dyn" && r.type.kind === "dyn") {
|
|
6806
|
+
const value = { kind: "libCall", fn: "dyn.bitwise", args: [l, r, { kind: "strLit", value: bitwise[op], type: STRING, loc }], type: DYN, loc };
|
|
6807
|
+
return left.type.kind === "f64" || right.type.kind === "f64" ? { kind: "dynCheck", value, type: F64, loc } : value;
|
|
6808
|
+
}
|
|
6809
|
+
}
|
|
6810
|
+
// Numeric operators dispatch on ToNumeric's actual Number/BigInt
|
|
6811
|
+
// result, including values flowing through native callable properties.
|
|
6812
|
+
if (isJsSourceFile(expr.getSourceFile()) || op === ts.SyntaxKind.PlusToken ||
|
|
6813
|
+
[ts.SyntaxKind.LessThanToken, ts.SyntaxKind.LessThanEqualsToken,
|
|
6814
|
+
ts.SyntaxKind.GreaterThanToken, ts.SyntaxKind.GreaterThanEqualsToken].includes(op) ||
|
|
6815
|
+
[ts.SyntaxKind.MinusToken, ts.SyntaxKind.AsteriskToken, ts.SyntaxKind.SlashToken,
|
|
6816
|
+
ts.SyntaxKind.PercentToken, ts.SyntaxKind.AsteriskAsteriskToken].includes(op)) {
|
|
6099
6817
|
const other = left.type.kind === "dyn" ? right : left;
|
|
6100
6818
|
const NUM_BIN = {
|
|
6101
6819
|
[ts.SyntaxKind.MinusToken]: "-",
|
|
@@ -6112,26 +6830,30 @@ export function lowerBinary(lowerer, expr) {
|
|
|
6112
6830
|
};
|
|
6113
6831
|
const arith = NUM_BIN[op];
|
|
6114
6832
|
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 };
|
|
6833
|
+
if (arith) {
|
|
6834
|
+
const l = lowerer.coerceToExpected(left, DYN);
|
|
6835
|
+
const r = lowerer.coerceToExpected(right, DYN);
|
|
6836
|
+
if (l.type.kind === "dyn" && r.type.kind === "dyn") {
|
|
6837
|
+
const value = { kind: "libCall", fn: "dyn.arithmetic",
|
|
6838
|
+
args: [l, r, { kind: "strLit", value: arith, type: STRING, loc }], type: DYN, loc };
|
|
6839
|
+
const numeric = left.type.kind === "bigint" || right.type.kind === "bigint" ? BIGINT_T
|
|
6840
|
+
: left.type.kind === "f64" || right.type.kind === "f64" ? F64 : null;
|
|
6841
|
+
return numeric ? { kind: "dynCheck", value, type: numeric, loc } : value;
|
|
6127
6842
|
}
|
|
6128
|
-
|
|
6129
|
-
|
|
6130
|
-
|
|
6131
|
-
|
|
6132
|
-
|
|
6133
|
-
|
|
6134
|
-
|
|
6843
|
+
}
|
|
6844
|
+
if (cmp && lowerer.dynConvertible(left.type) && lowerer.dynConvertible(right.type)) {
|
|
6845
|
+
return { kind: "libCall", fn: "dyn.compare", args: [lowerer.coerceToExpected(left, DYN),
|
|
6846
|
+
lowerer.coerceToExpected(right, DYN), { kind: "strLit", value: cmp, type: STRING, loc }], type: BOOL, loc };
|
|
6847
|
+
}
|
|
6848
|
+
// Untyped addition chooses concatenation or numeric addition only
|
|
6849
|
+
// after both operands have undergone ToPrimitive at runtime.
|
|
6850
|
+
if (op === ts.SyntaxKind.PlusToken) {
|
|
6851
|
+
const l = lowerer.coerceToExpected(left, DYN);
|
|
6852
|
+
const r = lowerer.coerceToExpected(right, DYN);
|
|
6853
|
+
if (l.type.kind === "dyn" && r.type.kind === "dyn") {
|
|
6854
|
+
const value = { kind: "libCall", fn: "dyn.add", args: [l, r], type: DYN, loc };
|
|
6855
|
+
// A statically known string operand guarantees a string result.
|
|
6856
|
+
return other.type.kind === "string" ? { kind: "dynCheck", value, type: STRING, loc } : value;
|
|
6135
6857
|
}
|
|
6136
6858
|
}
|
|
6137
6859
|
}
|
|
@@ -6150,7 +6872,7 @@ export function lowerBinary(lowerer, expr) {
|
|
|
6150
6872
|
// retain the SC2011 promise that --dynamic lifts this site.
|
|
6151
6873
|
if (lowerer.dynamic)
|
|
6152
6874
|
return lowerAnyBinaryInIsland(lowerer, expr, left, right, loc);
|
|
6153
|
-
lowerer.anyOpFence(`the '${ts.tokenToString(op) ?? ts.
|
|
6875
|
+
lowerer.anyOpFence(`the '${ts.tokenToString(op) ?? ts.syntaxKindName(op)}' operator`, expr);
|
|
6154
6876
|
}
|
|
6155
6877
|
// tsc allows ===/!== on unknown (arithmetic/comparisons it rejects
|
|
6156
6878
|
// itself); a dynamic equality would need a dyn walk — validate first.
|
|
@@ -6353,6 +7075,14 @@ export function lowerBinary(lowerer, expr) {
|
|
|
6353
7075
|
const optionalStringPlus = lowerOptionalStringPlus();
|
|
6354
7076
|
if (optionalStringPlus)
|
|
6355
7077
|
return optionalStringPlus;
|
|
7078
|
+
if (isJsSourceFile(expr.getSourceFile())) {
|
|
7079
|
+
const primitive = (type) => type.kind === "f64" || type.kind === "string" || type.kind === "bool" || isUnitType(type) ||
|
|
7080
|
+
(type.kind === "union" && (lowerer.unions.get(type.unionId)?.arms.every(primitive) ?? false));
|
|
7081
|
+
if ((left.type.kind === "union" || right.type.kind === "union") && primitive(left.type) && primitive(right.type)) {
|
|
7082
|
+
lowerer.runtimeOptionalArithmeticTypes.set(expr, DYN);
|
|
7083
|
+
return { kind: "libCall", fn: "dyn.add", args: [lowerer.coerceToExpected(left, DYN), lowerer.coerceToExpected(right, DYN)], type: DYN, loc };
|
|
7084
|
+
}
|
|
7085
|
+
}
|
|
6356
7086
|
lowerer.unsupported("SC1043", expr);
|
|
6357
7087
|
break;
|
|
6358
7088
|
case ts.SyntaxKind.MinusToken:
|
|
@@ -6395,8 +7125,11 @@ export function lowerBinary(lowerer, expr) {
|
|
|
6395
7125
|
return indexed;
|
|
6396
7126
|
if (plainBothNum)
|
|
6397
7127
|
return { kind: "bin", op: negated ? "!==" : "===", left, right, type: BOOL, loc };
|
|
6398
|
-
if (bothStr)
|
|
7128
|
+
if (bothStr) {
|
|
7129
|
+
if (left.kind === "strLit" && right.kind === "strLit")
|
|
7130
|
+
return { kind: "boolLit", value: (left.value === right.value) !== negated, type: BOOL, loc };
|
|
6399
7131
|
return { kind: "strEq", negated, left, right, type: BOOL, loc };
|
|
7132
|
+
}
|
|
6400
7133
|
// bool === bool: a plain value compare (the config-drift checks'
|
|
6401
7134
|
// `desired.lanMode !== actual.lanMode` shape).
|
|
6402
7135
|
if (left.type.kind === "bool" && right.type.kind === "bool") {
|
|
@@ -6520,6 +7253,8 @@ export function lowerBinary(lowerer, expr) {
|
|
|
6520
7253
|
if ((idLeft.type.kind === "array" ||
|
|
6521
7254
|
idLeft.type.kind === "map" ||
|
|
6522
7255
|
idLeft.type.kind === "set" ||
|
|
7256
|
+
idLeft.type.kind === "regex" ||
|
|
7257
|
+
idLeft.type.kind === "url" ||
|
|
6523
7258
|
idLeft.type.kind === "object" ||
|
|
6524
7259
|
idLeft.type.kind === "record" ||
|
|
6525
7260
|
// Symbols ARE identity: `Symbol('a') === Symbol('a')` is false,
|
|
@@ -6540,6 +7275,10 @@ export function lowerBinary(lowerer, expr) {
|
|
|
6540
7275
|
if (DYN_HANDLE_KINDS.has(idLeft.type.kind) && typeEquals(idLeft.type, idRight.type)) {
|
|
6541
7276
|
return { kind: "bin", op: negated ? "!==" : "===", left: idLeft, right: idRight, type: BOOL, loc };
|
|
6542
7277
|
}
|
|
7278
|
+
if (lowerer.dynConvertible(idLeft.type) && lowerer.dynConvertible(idRight.type)) {
|
|
7279
|
+
return { kind: "dynScalarEq", left: lowerer.coerceToExpected(idLeft, DYN),
|
|
7280
|
+
right: lowerer.coerceToExpected(idRight, DYN), ...(negated ? { negated: true } : {}), type: BOOL, loc };
|
|
7281
|
+
}
|
|
6543
7282
|
lowerer.unsupported("SC1043", expr);
|
|
6544
7283
|
break;
|
|
6545
7284
|
}
|
|
@@ -6554,27 +7293,308 @@ export function lowerBinary(lowerer, expr) {
|
|
|
6554
7293
|
return { kind: "bin", op: cmpOp, left: arithmeticLeft, right: arithmeticRight, type: BOOL, loc };
|
|
6555
7294
|
if (bothStr)
|
|
6556
7295
|
return { kind: "strCmp", op: cmpOp, left, right, type: BOOL, loc };
|
|
7296
|
+
if (lowerer.dynConvertible(left.type) && lowerer.dynConvertible(right.type)) {
|
|
7297
|
+
return { kind: "libCall", fn: "dyn.compare", args: [lowerer.coerceToExpected(left, DYN),
|
|
7298
|
+
lowerer.coerceToExpected(right, DYN), { kind: "strLit", value: cmpOp, type: STRING, loc }], type: BOOL, loc };
|
|
7299
|
+
}
|
|
6557
7300
|
lowerer.unsupported("SC1043", expr);
|
|
6558
7301
|
break;
|
|
6559
7302
|
}
|
|
6560
7303
|
default:
|
|
6561
7304
|
break;
|
|
6562
7305
|
}
|
|
6563
|
-
lowerer.unsupported("SC1090", expr, `operator '${ts.tokenToString(op) ?? ts.
|
|
7306
|
+
lowerer.unsupported("SC1090", expr, `operator '${ts.tokenToString(op) ?? ts.syntaxKindName(op)}'`);
|
|
7307
|
+
}
|
|
7308
|
+
function lowerLogicalChain(lowerer, expr) {
|
|
7309
|
+
// Logical chains have a left-nested AST. Keep the native stack bounded
|
|
7310
|
+
// while retaining each pair's checker type and lazy operand evaluation.
|
|
7311
|
+
const parents = [];
|
|
7312
|
+
let current = expr;
|
|
7313
|
+
let result;
|
|
7314
|
+
while (true) {
|
|
7315
|
+
if (current.operatorToken.kind === ts.SyntaxKind.BarBarToken) {
|
|
7316
|
+
const fallback = lowerAbsenceAwareOrChain(lowerer, current);
|
|
7317
|
+
if (fallback !== null) {
|
|
7318
|
+
result = fallback;
|
|
7319
|
+
break;
|
|
7320
|
+
}
|
|
7321
|
+
}
|
|
7322
|
+
const left = current.left;
|
|
7323
|
+
if (ts.isBinaryExpression(left) &&
|
|
7324
|
+
(left.operatorToken.kind === ts.SyntaxKind.AmpersandAmpersandToken ||
|
|
7325
|
+
left.operatorToken.kind === ts.SyntaxKind.BarBarToken) &&
|
|
7326
|
+
!lowerer.chainRecvByNode.has(left)) {
|
|
7327
|
+
parents.push(current);
|
|
7328
|
+
current = left;
|
|
7329
|
+
continue;
|
|
7330
|
+
}
|
|
7331
|
+
result = lowerLogicalPair(lowerer, current, lowerAbsenceProbe(lowerer, left) ?? lowerer.lowerExpr(left));
|
|
7332
|
+
break;
|
|
7333
|
+
}
|
|
7334
|
+
for (let i = parents.length - 1; i >= 0; i--) {
|
|
7335
|
+
result = lowerLogicalPair(lowerer, parents[i], result);
|
|
7336
|
+
}
|
|
7337
|
+
return result;
|
|
7338
|
+
}
|
|
7339
|
+
function lowerLogicalPair(lowerer, expr, left) {
|
|
7340
|
+
const op = expr.operatorToken.kind;
|
|
7341
|
+
const loc = locOf(expr);
|
|
7342
|
+
// Preserve short-circuiting during lowering too: an unreachable member
|
|
7343
|
+
// on a specialized value must not poison the surrounding JS statement.
|
|
7344
|
+
if (left.kind === "boolLit") {
|
|
7345
|
+
const takeRight = op === ts.SyntaxKind.AmpersandAmpersandToken ? left.value : !left.value;
|
|
7346
|
+
return takeRight ? lowerer.lowerExpr(expr.right) : left;
|
|
7347
|
+
}
|
|
7348
|
+
if (left.kind === "unitLit" && op === ts.SyntaxKind.AmpersandAmpersandToken)
|
|
7349
|
+
return left;
|
|
7350
|
+
if (op === ts.SyntaxKind.BarBarToken && REF_TRUTHY_KINDS.has(left.type.kind))
|
|
7351
|
+
return left;
|
|
7352
|
+
const right = lowerer.lowerExpr(expr.right);
|
|
7353
|
+
// A LITERAL-unit left operand (a compile-time undefined/null — the
|
|
7354
|
+
// capability-probe members: `process.features.inspector ||
|
|
7355
|
+
// !flag.startsWith('--inspect')` reads undefined in a compiled
|
|
7356
|
+
// binary): statically falsy, so the operator folds JS-exactly —
|
|
7357
|
+
// `unit || X` IS X, `unit && X` IS the unit (X's side effects never
|
|
7358
|
+
// run in JS either; the lowered right is simply dropped). Literal
|
|
7359
|
+
// units only — computed unit-typed values keep the fences below.
|
|
7360
|
+
if (left.kind === "unitLit") {
|
|
7361
|
+
return op === ts.SyntaxKind.BarBarToken ? right : left;
|
|
7362
|
+
}
|
|
7363
|
+
// JS objects and symbols are always truthy. In `ref || fallback`
|
|
7364
|
+
// the fallback is therefore unreachable and the result is the
|
|
7365
|
+
// already-evaluated left value — the browser-fallback idiom used by
|
|
7366
|
+
// packages for `process.argv || []` and `process.env || {}`. The
|
|
7367
|
+
// operand itself remains in the IR, preserving reads/calls that
|
|
7368
|
+
// produce the reference; only the unreachable right lowering drops.
|
|
7369
|
+
if (op === ts.SyntaxKind.BarBarToken && REF_TRUTHY_KINDS.has(left.type.kind)) {
|
|
7370
|
+
return left;
|
|
7371
|
+
}
|
|
7372
|
+
if (left.type.kind === "dyn" || right.type.kind === "dyn") {
|
|
7373
|
+
// A checked-dynamic operand (`fn.name || '<anonymous>'` —
|
|
7374
|
+
// test/common's _mustCallInner): both sides live in the checked-dynamic tree and
|
|
7375
|
+
// the deciding test is ToBoolean over the dyn kind
|
|
7376
|
+
// (scr_dyn_truthy) — JS value semantics exactly, result dyn. The
|
|
7377
|
+
// non-dyn side converts through the usual boundary; a value with
|
|
7378
|
+
// no dyn representation keeps the fence.
|
|
7379
|
+
const l = lowerer.coerceToExpected(left, DYN);
|
|
7380
|
+
const r = lowerer.coerceToExpected(right, DYN);
|
|
7381
|
+
if (l.type.kind !== "dyn" || r.type.kind !== "dyn") {
|
|
7382
|
+
lowerer.unsupported("SC1100", expr, "logical operators on 'unknown' values");
|
|
7383
|
+
}
|
|
7384
|
+
return {
|
|
7385
|
+
kind: "logical",
|
|
7386
|
+
op: op === ts.SyntaxKind.AmpersandAmpersandToken ? "&&" : "||",
|
|
7387
|
+
left: l,
|
|
7388
|
+
right: r,
|
|
7389
|
+
type: DYN, loc,
|
|
7390
|
+
};
|
|
7391
|
+
}
|
|
7392
|
+
if (left.type.kind === "jsval" || right.type.kind === "jsval") {
|
|
7393
|
+
return {
|
|
7394
|
+
kind: "logical",
|
|
7395
|
+
op: op === ts.SyntaxKind.AmpersandAmpersandToken ? "&&" : "||",
|
|
7396
|
+
left: lowerer.jsvalIn(left, expr.left),
|
|
7397
|
+
right: lowerer.jsvalIn(right, expr.right),
|
|
7398
|
+
type: JSVAL, loc,
|
|
7399
|
+
};
|
|
7400
|
+
}
|
|
7401
|
+
if (isJsSourceFile(expr.getSourceFile()) && op === ts.SyntaxKind.AmpersandAmpersandToken &&
|
|
7402
|
+
left.type.kind === "union" && (lowerer.unions.get(left.type.unionId)?.arms.some((arm) => arm.kind === "bool")) &&
|
|
7403
|
+
!typeEquals(left.type, right.type) && lowerer.dynConvertible(left.type) && lowerer.dynConvertible(right.type)) {
|
|
7404
|
+
return { kind: "logical", op: "&&", left: lowerer.coerceToExpected(left, DYN), right: lowerer.coerceToExpected(right, DYN), type: DYN, loc };
|
|
7405
|
+
}
|
|
7406
|
+
if (left.type.kind === "union" || right.type.kind === "union") {
|
|
7407
|
+
// Test the deciding operand in its own representation. The result
|
|
7408
|
+
// contains only the values that can survive short-circuit evaluation.
|
|
7409
|
+
let target = lowerer.mapTypeOf(lowerer.typeOf(expr));
|
|
7410
|
+
// Index reads can be absent without noUncheckedIndexedAccess. Their
|
|
7411
|
+
// explicit nullish fallback still runs, even if the checker discarded it.
|
|
7412
|
+
if (target !== null) {
|
|
7413
|
+
const returned = [right.type, ...(op === ts.SyntaxKind.AmpersandAmpersandToken ? [left.type] : [])];
|
|
7414
|
+
for (const type of returned) {
|
|
7415
|
+
const arms = type.kind === "union" ? lowerer.unions.get(type.unionId)?.arms ?? [] : [type];
|
|
7416
|
+
for (const arm of arms) {
|
|
7417
|
+
if (arm.kind === "undefinedT" && !isUnitType(target))
|
|
7418
|
+
target = lowerer.runtimeOptionalType(target);
|
|
7419
|
+
if (arm.kind === "nullT")
|
|
7420
|
+
target = withUnitArm(target, arm.kind, lowerer.unions) ?? target;
|
|
7421
|
+
}
|
|
7422
|
+
}
|
|
7423
|
+
}
|
|
7424
|
+
// A broad-JSDoc npm-static body can leave the CHECKER result `any`
|
|
7425
|
+
// even after declaration-backed specialization has recovered both
|
|
7426
|
+
// operands (`helpOption && args.find(...)` in commander). When the
|
|
7427
|
+
// left consists only of always-truthy references and falsy units,
|
|
7428
|
+
// the static result is exactly the right operand plus those units.
|
|
7429
|
+
if (op === ts.SyntaxKind.AmpersandAmpersandToken &&
|
|
7430
|
+
target === null &&
|
|
7431
|
+
lowerer.implicitParamTypes !== null &&
|
|
7432
|
+
npmStaticPackageOfPath(expr.getSourceFile().fileName) !== null &&
|
|
7433
|
+
(lowerer.typeOf(expr).flags & ts.TypeFlags.Any) !== 0 &&
|
|
7434
|
+
left.type.kind === "union") {
|
|
7435
|
+
const leftArms = lowerer.unions.get(left.type.unionId)?.arms;
|
|
7436
|
+
const rightArms = right.type.kind === "union"
|
|
7437
|
+
? lowerer.unions.get(right.type.unionId)?.arms
|
|
7438
|
+
: [right.type];
|
|
7439
|
+
const rightIsStatic = rightArms?.every((arm) => arm.kind !== "dyn" && arm.kind !== "caught" && arm.kind !== "jsval" && arm.kind !== "void") ?? false;
|
|
7440
|
+
if (leftArms && rightArms && rightIsStatic &&
|
|
7441
|
+
leftArms.some(isUnitType) &&
|
|
7442
|
+
leftArms.every((arm) => isUnitType(arm) || REF_TRUTHY_KINDS.has(arm.kind))) {
|
|
7443
|
+
const byKey = new Map();
|
|
7444
|
+
for (const arm of [...rightArms, ...leftArms.filter(isUnitType)]) {
|
|
7445
|
+
byKey.set(typeKey(arm), arm);
|
|
7446
|
+
}
|
|
7447
|
+
const arms = [...byKey.values()].sort((a, b) => (typeKey(a) < typeKey(b) ? -1 : 1));
|
|
7448
|
+
if (arms.length > 1) {
|
|
7449
|
+
const source = right.type.kind === "union" ? lowerer.unions.get(right.type.unionId) : undefined;
|
|
7450
|
+
target = { kind: "union", unionId: source
|
|
7451
|
+
? lowerer.unions.transform(source, arms)
|
|
7452
|
+
: lowerer.unions.intern(arms) };
|
|
7453
|
+
}
|
|
7454
|
+
}
|
|
7455
|
+
}
|
|
7456
|
+
if (target?.kind === "union") {
|
|
7457
|
+
// `&&` whose left does NOT fit the result union: the checker
|
|
7458
|
+
// built that result by DROPPING left arms which are always
|
|
7459
|
+
// truthy (`Option | undefined && string | undefined` answers
|
|
7460
|
+
// `string | undefined`, not `Option | string | undefined`).
|
|
7461
|
+
// Evaluate the left once, test it in its own union, and retag it
|
|
7462
|
+
// only on the falsy path. Any stranded reference arms are
|
|
7463
|
+
// unreachable there because every JS object is truthy.
|
|
7464
|
+
if (op === ts.SyntaxKind.AmpersandAmpersandToken && left.type.kind === "union" && !typeEquals(left.type, target)) {
|
|
7465
|
+
const def = lowerer.unions.get(left.type.unionId);
|
|
7466
|
+
const trappable = new Set();
|
|
7467
|
+
def?.arms.forEach((arm, tag) => {
|
|
7468
|
+
if (lowerer.armTag(target.unionId, arm) < 0 && REF_TRUTHY_KINDS.has(arm.kind)) {
|
|
7469
|
+
trappable.add(tag);
|
|
7470
|
+
}
|
|
7471
|
+
});
|
|
7472
|
+
const retag = trappable.size === 0
|
|
7473
|
+
? null
|
|
7474
|
+
: lowerer.unionRetagHelper(left.type.unionId, target.unionId, loc, trappable);
|
|
7475
|
+
if (retag) {
|
|
7476
|
+
lowerer.requireTruthyUnion(left.type.unionId, expr);
|
|
7477
|
+
const stmts = [];
|
|
7478
|
+
let stable = left;
|
|
7479
|
+
if (!isSafeToRepeat(left)) {
|
|
7480
|
+
const local = lowerer.declareHiddenLocal("%and", left.type);
|
|
7481
|
+
stmts.push({ kind: "varDecl", localId: local.id, init: left, loc });
|
|
7482
|
+
stable = varRef(local.id, left.type, loc);
|
|
7483
|
+
}
|
|
7484
|
+
const result = {
|
|
7485
|
+
kind: "ternary",
|
|
7486
|
+
cond: lowerer.ensureBool(stable, expr.left),
|
|
7487
|
+
then: lowerer.coerceInto(expr.right, right, target),
|
|
7488
|
+
else_: { kind: "call", callee: retag, args: [stable], type: target, loc },
|
|
7489
|
+
type: target,
|
|
7490
|
+
loc,
|
|
7491
|
+
};
|
|
7492
|
+
return stmts.length === 0 ? result : { kind: "seqExpr", stmts, result, type: target, loc };
|
|
7493
|
+
}
|
|
7494
|
+
}
|
|
7495
|
+
// `||` whose left does NOT fit the result union: the checker built
|
|
7496
|
+
// that result by DROPPING the left's falsy arms (`process.env.X ||
|
|
7497
|
+
// null` is `string | null`, `|| 3000` is `string | number` — the
|
|
7498
|
+
// `undefined` is gone from both), so coercing the left eagerly, as
|
|
7499
|
+
// the shared shape below does, retags an arm the test is about to
|
|
7500
|
+
// rule out and throws where Node yields the default. Single-eval
|
|
7501
|
+
// instead: test the left in its OWN union and retag only on the
|
|
7502
|
+
// truthy side, where the dropped arms are unreachable.
|
|
7503
|
+
if (op === ts.SyntaxKind.BarBarToken && left.type.kind === "union" && !typeEquals(left.type, target)) {
|
|
7504
|
+
const retag = lowerer.unionRetagHelper(left.type.unionId, target.unionId, loc);
|
|
7505
|
+
if (retag) {
|
|
7506
|
+
lowerer.requireTruthyUnion(left.type.unionId, expr);
|
|
7507
|
+
return {
|
|
7508
|
+
kind: "orDefault",
|
|
7509
|
+
left,
|
|
7510
|
+
right: lowerer.coerceInto(expr.right, right, target),
|
|
7511
|
+
retag,
|
|
7512
|
+
type: target,
|
|
7513
|
+
loc,
|
|
7514
|
+
};
|
|
7515
|
+
}
|
|
7516
|
+
}
|
|
7517
|
+
lowerer.requireTruthyUnion(target.unionId, expr);
|
|
7518
|
+
return {
|
|
7519
|
+
kind: "logical",
|
|
7520
|
+
op: op === ts.SyntaxKind.AmpersandAmpersandToken ? "&&" : "||",
|
|
7521
|
+
left: lowerer.coerceInto(expr.left, left, target),
|
|
7522
|
+
right: lowerer.coerceInto(expr.right, right, target),
|
|
7523
|
+
type: target, loc,
|
|
7524
|
+
};
|
|
7525
|
+
}
|
|
7526
|
+
// `u || d` NARROWED by the default: the checker types the result
|
|
7527
|
+
// as u's single non-unit arm (`marker() || "default"`) — evaluate
|
|
7528
|
+
// u once, truthy extracts the arm, falsy takes d lazily (the
|
|
7529
|
+
// orDefault node, nullish's truthiness sibling). An UNMAPPABLE
|
|
7530
|
+
// checker result takes the same rule (`options.runner ||
|
|
7531
|
+
// defaultRunner` — tsc's union of two structurally-compatible
|
|
7532
|
+
// function types has no representation, while the left's one
|
|
7533
|
+
// non-unit arm is the only representable answer): the default
|
|
7534
|
+
// must coerce into the arm or fence on its own.
|
|
7535
|
+
if (op === ts.SyntaxKind.BarBarToken &&
|
|
7536
|
+
left.type.kind === "union") {
|
|
7537
|
+
const def = lowerer.unions.get(left.type.unionId);
|
|
7538
|
+
const rest = def ? def.arms.filter((a) => !isUnitType(a)) : [];
|
|
7539
|
+
const funcArmDefault = rest[0]?.kind === "func" && (target === null || target.kind === "func");
|
|
7540
|
+
if (rest.length === 1 && ((target !== null && typeEquals(target, rest[0])) || funcArmDefault)) {
|
|
7541
|
+
lowerer.requireTruthyUnion(left.type.unionId, expr);
|
|
7542
|
+
const dflt = lowerer.lowerExprExpecting(expr.right, rest[0]);
|
|
7543
|
+
return { kind: "orDefault", left, right: dflt, type: rest[0], loc };
|
|
7544
|
+
}
|
|
7545
|
+
}
|
|
7546
|
+
// JavaScript defaults can join differently inferred object layouts.
|
|
7547
|
+
// When both operands have native checked representations, preserve
|
|
7548
|
+
// the deciding value and short-circuit evaluation in that domain.
|
|
7549
|
+
if (isJsSourceFile(expr.getSourceFile()) &&
|
|
7550
|
+
lowerer.dynConvertible(left.type) && lowerer.dynConvertible(right.type)) {
|
|
7551
|
+
return {
|
|
7552
|
+
kind: "logical", op: op === ts.SyntaxKind.AmpersandAmpersandToken ? "&&" : "||",
|
|
7553
|
+
left: lowerer.coerceInto(expr.left, left, DYN),
|
|
7554
|
+
right: lowerer.coerceInto(expr.right, right, DYN), type: DYN, loc,
|
|
7555
|
+
};
|
|
7556
|
+
}
|
|
7557
|
+
lowerer.unsupported("SC1090", expr, `logical operators on union-typed values outside conditions (${NARROW_FIRST})`);
|
|
7558
|
+
}
|
|
7559
|
+
const kind = left.type.kind;
|
|
7560
|
+
if (kind !== right.type.kind ||
|
|
7561
|
+
(kind !== "f64" && kind !== "string" && kind !== "bool")) {
|
|
7562
|
+
// Mixed PLAIN operands (`value || null`, `flag || undefined`,
|
|
7563
|
+
// `s || 0`): JS value semantics still compile when the checker
|
|
7564
|
+
// types the RESULT as one union both operands coerce into — the
|
|
7565
|
+
// same lift the union-operand path above takes, arriving here
|
|
7566
|
+
// with two plain arm values instead.
|
|
7567
|
+
const target = lowerer.mapTypeOf(lowerer.typeOf(expr));
|
|
7568
|
+
if (target?.kind === "union") {
|
|
7569
|
+
lowerer.requireTruthyUnion(target.unionId, expr);
|
|
7570
|
+
return {
|
|
7571
|
+
kind: "logical",
|
|
7572
|
+
op: op === ts.SyntaxKind.AmpersandAmpersandToken ? "&&" : "||",
|
|
7573
|
+
left: lowerer.coerceInto(expr.left, left, target),
|
|
7574
|
+
right: lowerer.coerceInto(expr.right, right, target),
|
|
7575
|
+
type: target, loc,
|
|
7576
|
+
};
|
|
7577
|
+
}
|
|
7578
|
+
lowerer.unsupported("SC1042", expr);
|
|
7579
|
+
}
|
|
7580
|
+
return {
|
|
7581
|
+
kind: "logical",
|
|
7582
|
+
op: op === ts.SyntaxKind.AmpersandAmpersandToken ? "&&" : "||",
|
|
7583
|
+
left, right, type: left.type, loc,
|
|
7584
|
+
};
|
|
6564
7585
|
}
|
|
6565
7586
|
function lowerAbsenceAwareOrChain(lowerer, expr) {
|
|
6566
7587
|
const nodes = [];
|
|
6567
|
-
const
|
|
6568
|
-
|
|
6569
|
-
|
|
6570
|
-
|
|
6571
|
-
|
|
7588
|
+
const pending = [expr];
|
|
7589
|
+
while (pending.length !== 0) {
|
|
7590
|
+
const node = pending.pop();
|
|
7591
|
+
if (ts.isBinaryExpression(node) && node.operatorToken.kind === ts.SyntaxKind.BarBarToken) {
|
|
7592
|
+
pending.push(node.right, node.left);
|
|
6572
7593
|
}
|
|
6573
7594
|
else {
|
|
6574
7595
|
nodes.push(node);
|
|
6575
7596
|
}
|
|
6576
|
-
}
|
|
6577
|
-
collect(expr);
|
|
7597
|
+
}
|
|
6578
7598
|
if (nodes.length < 3)
|
|
6579
7599
|
return null;
|
|
6580
7600
|
const lowered = nodes.map((node) => lowerAbsenceProbe(lowerer, node) ?? lowerer.lowerExpr(node));
|
|
@@ -6645,10 +7665,7 @@ function errorRootedObjectOf(lowerer, t) {
|
|
|
6645
7665
|
* shape, so the sibling typeof lowerings keep trying. */
|
|
6646
7666
|
function lowerErrorCodeTypeofTest(lowerer, expr, loc) {
|
|
6647
7667
|
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
|
-
]) {
|
|
7668
|
+
for (const [a, b] of operandPairs(expr)) {
|
|
6652
7669
|
if (!ts.isTypeOfExpression(a))
|
|
6653
7670
|
continue;
|
|
6654
7671
|
let prop = a.expression;
|
|
@@ -6708,10 +7725,7 @@ function lowerErrorCodeTypeofTest(lowerer, expr, loc) {
|
|
|
6708
7725
|
}
|
|
6709
7726
|
export function lowerCaughtTypeofTest(lowerer, expr, loc) {
|
|
6710
7727
|
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
|
-
]) {
|
|
7728
|
+
for (const [a, b] of operandPairs(expr)) {
|
|
6715
7729
|
if (!ts.isTypeOfExpression(a))
|
|
6716
7730
|
continue;
|
|
6717
7731
|
const local = lowerer.caughtLocalOf(a.expression);
|
|
@@ -6834,12 +7848,13 @@ export function aliasTypeofNarrows(lowerer, cond, polarity) {
|
|
|
6834
7848
|
walk(cond, polarity);
|
|
6835
7849
|
return out;
|
|
6836
7850
|
}
|
|
7851
|
+
/** Visit both operand orders while keeping the sequence an array of pairs. */
|
|
7852
|
+
function operandPairs(expr) {
|
|
7853
|
+
return [[expr.left, expr.right], [expr.right, expr.left]];
|
|
7854
|
+
}
|
|
6837
7855
|
/** One ===/!== leaf of aliasTypeofNarrows — the qualification battery. */
|
|
6838
7856
|
function aliasTypeofArmOf(lowerer, e, strip) {
|
|
6839
|
-
for (const [a0, b] of
|
|
6840
|
-
[e.left, e.right],
|
|
6841
|
-
[e.right, e.left],
|
|
6842
|
-
]) {
|
|
7857
|
+
for (const [a0, b] of operandPairs(e)) {
|
|
6843
7858
|
if (!ts.isStringLiteral(b))
|
|
6844
7859
|
continue;
|
|
6845
7860
|
const a = strip(a0);
|
|
@@ -6868,7 +7883,7 @@ function aliasTypeofArmOf(lowerer, e, strip) {
|
|
|
6868
7883
|
if (!bindingNeverReassigned(lowerer, valSym, valDecl))
|
|
6869
7884
|
continue;
|
|
6870
7885
|
const fn = valDecl.parent;
|
|
6871
|
-
if (!(e.pos >= fn.pos && e.end <= fn.end))
|
|
7886
|
+
if (fn === undefined || !(e.pos >= fn.pos && e.end <= fn.end))
|
|
6872
7887
|
continue;
|
|
6873
7888
|
const valT = lowerer.typeOf(opnd); // override-aware: implicit bindings compose
|
|
6874
7889
|
const mapped = lowerer.mapTypeOf(valT);
|
|
@@ -6910,10 +7925,7 @@ function aliasTypeofArmOf(lowerer, e, strip) {
|
|
|
6910
7925
|
* bare-typeof value form then composes with strEq for pure operands). */
|
|
6911
7926
|
function lowerUnionTypeofTest(lowerer, expr, loc) {
|
|
6912
7927
|
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
|
-
]) {
|
|
7928
|
+
for (const [a, b] of operandPairs(expr)) {
|
|
6917
7929
|
if (!ts.isTypeOfExpression(a))
|
|
6918
7930
|
continue;
|
|
6919
7931
|
if (lowerer.mapTypeOf(lowerer.typeOf(a.expression))?.kind !== "union")
|
|
@@ -6961,12 +7973,13 @@ function lowerUnionTypeofTest(lowerer, expr, loc) {
|
|
|
6961
7973
|
* side is a typeof over a dyn-typed operand (not this pattern). */
|
|
6962
7974
|
function lowerDynTypeofTest(lowerer, expr, loc) {
|
|
6963
7975
|
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
|
-
]) {
|
|
7976
|
+
for (const [a, b] of operandPairs(expr)) {
|
|
6968
7977
|
if (!ts.isTypeOfExpression(a))
|
|
6969
7978
|
continue;
|
|
7979
|
+
// Non-literal comparisons use the ordinary typeof string value,
|
|
7980
|
+
// preserving both operands' evaluation order without a narrowing test.
|
|
7981
|
+
if (!ts.isStringLiteral(b))
|
|
7982
|
+
continue;
|
|
6970
7983
|
const declared = lowerer.mapTypeOf(lowerer.typeOf(a.expression));
|
|
6971
7984
|
let value;
|
|
6972
7985
|
if (declared?.kind === "dyn") {
|
|
@@ -7024,10 +8037,7 @@ function lowerDynTypeofTest(lowerer, expr, loc) {
|
|
|
7024
8037
|
value = probed;
|
|
7025
8038
|
}
|
|
7026
8039
|
}
|
|
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") {
|
|
8040
|
+
if (b.text === "string" || b.text === "number" || b.text === "boolean" || b.text === "undefined" || b.text === "bigint" || b.text === "symbol") {
|
|
7031
8041
|
return {
|
|
7032
8042
|
kind: "dynTest",
|
|
7033
8043
|
test: b.text,
|
|
@@ -7064,14 +8074,8 @@ function lowerDynTypeofTest(lowerer, expr, loc) {
|
|
|
7064
8074
|
loc,
|
|
7065
8075
|
};
|
|
7066
8076
|
}
|
|
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") {
|
|
8077
|
+
// Native checked values do not yet carry symbols.
|
|
8078
|
+
if (b.text === "symbol") {
|
|
7075
8079
|
return { kind: "boolLit", value: negated, type: BOOL, loc };
|
|
7076
8080
|
}
|
|
7077
8081
|
lowerer.unsupported("SC1090", b, `'typeof' tests against "${b.text}" on 'unknown' values (only "string"/"number"/` +
|
|
@@ -7093,7 +8097,7 @@ function lowerReplacedExportsRead(lowerer, expr) {
|
|
|
7093
8097
|
if (!ts.isIdentifier(recv) || recv.text !== "exports")
|
|
7094
8098
|
return null;
|
|
7095
8099
|
const sf = expr.getSourceFile();
|
|
7096
|
-
if (!isJsSourceFile(sf) || isNodeEsmFile(sf))
|
|
8100
|
+
if (!isJsSourceFile(sf) || isNodeEsmFile(sf, lowerer.program))
|
|
7097
8101
|
return null;
|
|
7098
8102
|
if (lowerer.peekLocal(recv) || lowerer.globalOf(recv))
|
|
7099
8103
|
return null; // a user binding shadows
|
|
@@ -7270,6 +8274,40 @@ export function lowerInstanceOf(lowerer, expr, loc) {
|
|
|
7270
8274
|
const right = lowerer.lowerExpr(expr.right);
|
|
7271
8275
|
return { kind: "jsOp", op: "instanceOf", args: [left, right], type: BOOL, loc };
|
|
7272
8276
|
}
|
|
8277
|
+
if (!lowerer.dynamic && (lowerer.isStdlibGlobal(expr.right, "Set") || lowerer.isStdlibGlobal(expr.right, "Map"))) {
|
|
8278
|
+
const collection = lowerer.isStdlibGlobal(expr.right, "Map") ? "map" : "set";
|
|
8279
|
+
const value = lowerer.lowerExpr(expr.left);
|
|
8280
|
+
if (value.type.kind === "dyn")
|
|
8281
|
+
return { kind: "libCall", fn: collection === "map" ? "dyn.nativeMapIs" : "dyn.nativeSetIs", args: [value], type: BOOL, loc };
|
|
8282
|
+
if (value.type.kind === collection)
|
|
8283
|
+
return { kind: "seqExpr", stmts: [{ kind: "exprStmt", expr: value, loc }],
|
|
8284
|
+
result: { kind: "boolLit", value: true, type: BOOL, loc }, type: BOOL, loc };
|
|
8285
|
+
return { kind: "libCall", fn: collection === "map" ? "dyn.nativeMapIs" : "dyn.nativeSetIs",
|
|
8286
|
+
args: [lowerer.coerceInto(expr.left, value, DYN)], type: BOOL, loc };
|
|
8287
|
+
}
|
|
8288
|
+
if (lowerer.isStdlibGlobal(expr.right, "WeakMap") || lowerer.isStdlibGlobal(expr.right, "WeakSet")) {
|
|
8289
|
+
if (lowerer.dynamic) {
|
|
8290
|
+
const value = lowerer.jsvalIn(lowerer.lowerExpr(expr.left), expr.left);
|
|
8291
|
+
const ctor = { kind: "jsOp", op: "globalGet", name: lowerer.isStdlibGlobal(expr.right, "WeakMap") ? "WeakMap" : "WeakSet", args: [], type: JSVAL, loc };
|
|
8292
|
+
return { kind: "jsOp", op: "instanceOf", args: [value, ctor], type: BOOL, loc };
|
|
8293
|
+
}
|
|
8294
|
+
const value = lowerer.coerceInto(expr.left, lowerer.lowerExpr(expr.left), DYN);
|
|
8295
|
+
return { kind: "libCall", fn: lowerer.isStdlibGlobal(expr.right, "WeakMap") ? "weakMap.is" : "weakSet.is", args: [value], type: BOOL, loc };
|
|
8296
|
+
}
|
|
8297
|
+
const rhsBuiltin = ts.isIdentifier(expr.right) ? lowerer.builtinImportOf(expr.right) :
|
|
8298
|
+
ts.isPropertyAccessExpression(expr.right) ? lowerer.builtinMemberOf(expr.right) : null;
|
|
8299
|
+
if (lowerer.isStdlibGlobal(expr.right, "URL") || rhsBuiltin?.module === "url" && rhsBuiltin.member === "URL") {
|
|
8300
|
+
const value = lowerer.coerceInto(expr.left, lowerer.lowerExpr(expr.left), DYN);
|
|
8301
|
+
return { kind: "libCall", fn: "dyn.nativeUrlIs", args: [value], type: BOOL, loc };
|
|
8302
|
+
}
|
|
8303
|
+
if (lowerer.isStdlibGlobal(expr.right, "DataView")) {
|
|
8304
|
+
const value = lowerer.coerceInto(expr.left, lowerer.lowerExpr(expr.left), DYN);
|
|
8305
|
+
return { kind: "libCall", fn: "dyn.dataViewIs", args: [value], type: BOOL, loc };
|
|
8306
|
+
}
|
|
8307
|
+
if (lowerer.isStdlibGlobal(expr.right, "ArrayBuffer")) {
|
|
8308
|
+
const value = lowerer.coerceInto(expr.left, lowerer.lowerExpr(expr.left), DYN);
|
|
8309
|
+
return { kind: "libCall", fn: "arrayBuffer.is", args: [value], type: BOOL, loc };
|
|
8310
|
+
}
|
|
7273
8311
|
const rhsSymbol = ts.isIdentifier(expr.right) ? lowerer.resolveValueSymbol(expr.right) : null;
|
|
7274
8312
|
// `x instanceof events.EventEmitter` — the namespace-member spelling
|
|
7275
8313
|
// resolves to the same ambient class as the named import.
|
|
@@ -7303,29 +8341,46 @@ export function lowerInstanceOf(lowerer, expr, loc) {
|
|
|
7303
8341
|
// decoration result — fall to the class-VALUE path below, whose
|
|
7304
8342
|
// instanceOfValue reads the interval off the bound class object.
|
|
7305
8343
|
const directRhs = rhsSymbol ? lowerer.classBySymbol.get(rhsSymbol) : undefined;
|
|
7306
|
-
const target = (directRhs?.classDecorators?.valueGlobalId !== undefined ? undefined : directRhs) ??
|
|
8344
|
+
const target = (directRhs?.localClass || directRhs?.classDecorators?.valueGlobalId !== undefined ? undefined : directRhs) ??
|
|
7307
8345
|
rhsNsClass ??
|
|
7308
8346
|
lowerer.builtinErrorInfoOf(rhsSymbol) ??
|
|
8347
|
+
lowerer.builtinErrorInfoOf(rhsMemberSymbol) ??
|
|
7309
8348
|
lowerer.builtinEmitterInfoOf(rhsSymbol) ??
|
|
7310
8349
|
lowerer.builtinEmitterInfoOf(rhsMemberSymbol) ??
|
|
7311
8350
|
lowerer.builtinStreamInfoOf(rhsSymbol) ??
|
|
7312
8351
|
lowerer.builtinStreamInfoOf(rhsMemberSymbol) ??
|
|
7313
8352
|
undefined;
|
|
7314
8353
|
if (!target) {
|
|
7315
|
-
|
|
7316
|
-
|
|
7317
|
-
|
|
7318
|
-
|
|
7319
|
-
|
|
7320
|
-
|
|
7321
|
-
|
|
7322
|
-
|
|
7323
|
-
|
|
7324
|
-
|
|
7325
|
-
|
|
7326
|
-
|
|
7327
|
-
|
|
7328
|
-
|
|
8354
|
+
const date = lowerer.isStdlibGlobal(expr.right, "Date");
|
|
8355
|
+
const url = lowerer.isStdlibGlobal(expr.right, "URL");
|
|
8356
|
+
if ((date || url) && !lowerer.caughtLocalOf(expr.left)) {
|
|
8357
|
+
const value = lowerer.lowerExpr(expr.left);
|
|
8358
|
+
if (value.type.kind === "dyn")
|
|
8359
|
+
return { kind: "libCall", fn: date ? "dyn.nativeDateIs" : "dyn.nativeUrlIs", args: [value], type: BOOL, loc };
|
|
8360
|
+
if (value.type.kind === "union")
|
|
8361
|
+
return { kind: "libCall", fn: date ? "dyn.nativeDateIs" : "dyn.nativeUrlIs",
|
|
8362
|
+
args: [lowerer.coerceInto(expr.left, value, DYN)], type: BOOL, loc };
|
|
8363
|
+
return { kind: "seqExpr", stmts: [{ kind: "exprStmt", expr: value, loc }],
|
|
8364
|
+
result: { kind: "boolLit", value: value.type.kind === (date ? "date" : "url"), type: BOOL, loc }, type: BOOL, loc };
|
|
8365
|
+
}
|
|
8366
|
+
const webBrand = ["Request", "Response", "Headers"].find((name) => lowerer.isStdlibGlobal(expr.right, name));
|
|
8367
|
+
if (webBrand)
|
|
8368
|
+
return { kind: "libCall", fn: "fetch.webIs", args: [lowerer.lowerExprExpecting(expr.left, DYN),
|
|
8369
|
+
{ kind: "strLit", value: webBrand, type: STRING, loc }], type: BOOL, loc };
|
|
8370
|
+
if (lowerer.isStdlibGlobal(expr.right, "ReadableStream")) {
|
|
8371
|
+
const value = lowerer.lowerExprExpecting(expr.left, DYN);
|
|
8372
|
+
return { kind: "libCall", fn: "fetch.streamIs", args: [value], type: BOOL, loc };
|
|
8373
|
+
}
|
|
8374
|
+
// Unknown storage retains each numeric typed array's exact native brand.
|
|
8375
|
+
if (!lowerer.caughtLocalOf(expr.left)) {
|
|
8376
|
+
const elem = Object.keys(BYTES_ELEMENT_NAME).find((kind) => lowerer.isStdlibGlobal(expr.right, BYTES_ELEMENT_NAME[kind]));
|
|
8377
|
+
if (elem !== undefined) {
|
|
8378
|
+
const left = lowerer.lowerExpr(expr.left);
|
|
8379
|
+
if (left.type.kind === "dyn") {
|
|
8380
|
+
return { kind: "dynTest", test: "bytes", bytesElem: elem, value: left, type: BOOL, loc };
|
|
8381
|
+
}
|
|
8382
|
+
return { kind: "dynTest", test: "bytes", bytesElem: elem,
|
|
8383
|
+
value: lowerer.coerceInto(expr.left, left, DYN), type: BOOL, loc };
|
|
7329
8384
|
}
|
|
7330
8385
|
}
|
|
7331
8386
|
// `x instanceof RegExp` over a union with a regex arm (the
|
|
@@ -7338,6 +8393,8 @@ export function lowerInstanceOf(lowerer, expr, loc) {
|
|
|
7338
8393
|
lowerer.isStdlibGlobal(expr.right, "RegExp") &&
|
|
7339
8394
|
!lowerer.caughtLocalOf(expr.left)) {
|
|
7340
8395
|
const left = lowerer.lowerExpr(expr.left);
|
|
8396
|
+
if (left.type.kind === "dyn")
|
|
8397
|
+
return { kind: "libCall", fn: "dyn.nativeRegexIs", args: [left], type: BOOL, loc };
|
|
7341
8398
|
if (left.type.kind === "union") {
|
|
7342
8399
|
const def = lowerer.unions.get(left.type.unionId);
|
|
7343
8400
|
const tag = def ? def.arms.findIndex((a) => a.kind === "regex") : -1;
|
|
@@ -7345,9 +8402,7 @@ export function lowerInstanceOf(lowerer, expr, loc) {
|
|
|
7345
8402
|
return { kind: "unionIsTag", unionId: left.type.unionId, tag, negated: false, value: left, type: BOOL, loc };
|
|
7346
8403
|
}
|
|
7347
8404
|
}
|
|
7348
|
-
|
|
7349
|
-
return { kind: "boolLit", value: true, type: BOOL, loc };
|
|
7350
|
-
}
|
|
8405
|
+
return { kind: "libCall", fn: "dyn.nativeRegexIs", args: [lowerer.coerceInto(expr.left, left, DYN)], type: BOOL, loc };
|
|
7351
8406
|
}
|
|
7352
8407
|
// `x instanceof X` where X is a class VALUE (a classval-typed
|
|
7353
8408
|
// binding): the target is DYNAMIC — a hierarchy target reads its
|
|
@@ -7355,7 +8410,7 @@ export function lowerInstanceOf(lowerer, expr, loc) {
|
|
|
7355
8410
|
// STANDALONE target class has exactly one possible runtime value
|
|
7356
8411
|
// (itself — no descendants can flow into the slot), so the answer
|
|
7357
8412
|
// folds statically exactly like the named-target folds below.
|
|
7358
|
-
const rhsClassval =
|
|
8413
|
+
const rhsClassval = storedClassValueType(lowerer, expr.right);
|
|
7359
8414
|
if (rhsClassval?.kind === "classval" && !lowerer.caughtLocalOf(expr.left)) {
|
|
7360
8415
|
const targetInfo = lowerer.classes.get(rhsClassval.className);
|
|
7361
8416
|
if (!targetInfo) {
|
|
@@ -7366,13 +8421,18 @@ export function lowerInstanceOf(lowerer, expr, loc) {
|
|
|
7366
8421
|
lowerer.unsupported("SC1090", expr, "'instanceof' against a class value whose class has no lowering (the class declaration itself was rejected — see its own diagnostic)");
|
|
7367
8422
|
}
|
|
7368
8423
|
const left = lowerer.lowerExpr(expr.left);
|
|
8424
|
+
if (left.type.kind === "dyn") {
|
|
8425
|
+
const classValue = lowerer.lowerExpr(expr.right);
|
|
8426
|
+
return classInstanceOf(lowerer, left, targetInfo, loc, classValue);
|
|
8427
|
+
}
|
|
7369
8428
|
if (left.type.kind !== "object") {
|
|
7370
8429
|
lowerer.unsupported("SC1090", expr, "'instanceof' on values other than class instances (narrow union-typed values first)");
|
|
7371
8430
|
}
|
|
7372
8431
|
const lhsInfo = lowerer.classes.get(left.type.className);
|
|
7373
8432
|
if (!lhsInfo)
|
|
7374
8433
|
throw new InternalCompilerError(`lowerer bug: unknown class ${left.type.className}`);
|
|
7375
|
-
if (lowerer.inHierarchy(targetInfo) && lowerer.inHierarchy(lhsInfo))
|
|
8434
|
+
if ((lowerer.inHierarchy(targetInfo) && lowerer.inHierarchy(lhsInfo)) ||
|
|
8435
|
+
(targetInfo.localClass !== undefined && lhsInfo.localClass !== undefined)) {
|
|
7376
8436
|
const classValue = lowerer.lowerExpr(expr.right);
|
|
7377
8437
|
if (classValue.type.kind !== "classval")
|
|
7378
8438
|
lowerer.badType(expr.right, lowerer.typeOf(expr.right));
|
|
@@ -7389,6 +8449,11 @@ export function lowerInstanceOf(lowerer, expr, loc) {
|
|
|
7389
8449
|
}
|
|
7390
8450
|
lowerer.unsupported("SC1090", expr, "statically-decided 'instanceof' on computed operands (bind the values to variables first)");
|
|
7391
8451
|
}
|
|
8452
|
+
const constructor = tryLowerExpression(lowerer, expr.right);
|
|
8453
|
+
if (constructor?.type.kind === "dyn" && isJsSourceFile(expr.getSourceFile())) {
|
|
8454
|
+
const value = lowerer.coerceInto(expr.left, lowerer.lowerExpr(expr.left), DYN);
|
|
8455
|
+
return checkedClassInstanceOf(lowerer, value, constructor, loc);
|
|
8456
|
+
}
|
|
7392
8457
|
lowerer.unsupported("SC1090", expr.right, "'instanceof' right-hand sides other than classes declared in the program");
|
|
7393
8458
|
}
|
|
7394
8459
|
// A catch binding on the left: the runtime test against the class's
|
|
@@ -7419,6 +8484,9 @@ export function lowerInstanceOf(lowerer, expr, loc) {
|
|
|
7419
8484
|
// (name strings are user-writable), so `u instanceof TypeError` keeps
|
|
7420
8485
|
// the fence. Reads past the narrow bridge through maybeNarrow's
|
|
7421
8486
|
// validated %Error extraction. SEMANTICS.md 67.
|
|
8487
|
+
if (left.type.kind === "dyn" && !target.def.runtime && !target.builtinError && !target.builtinEmitter && !target.builtinStream && !target.localClass) {
|
|
8488
|
+
return classInstanceOf(lowerer, left, target, loc);
|
|
8489
|
+
}
|
|
7422
8490
|
if (left.type.kind === "dyn" && target.def.name === "%Error") {
|
|
7423
8491
|
return { kind: "dynTest", test: "error", value: left, type: BOOL, loc };
|
|
7424
8492
|
}
|
|
@@ -7441,8 +8509,47 @@ export function lowerInstanceOf(lowerer, expr, loc) {
|
|
|
7441
8509
|
if (left.type.kind === "dyn") {
|
|
7442
8510
|
lowerer.unsupported("SC1090", expr, `'instanceof ${target.def.name.replace(/^%/, "")}' on 'unknown' values (only the Error classes answer — test 'instanceof Error' and read '.name')`);
|
|
7443
8511
|
}
|
|
8512
|
+
if (left.type.kind === "union") {
|
|
8513
|
+
const unionId = left.type.unionId;
|
|
8514
|
+
const arms = lowerer.unions.get(unionId)?.arms;
|
|
8515
|
+
// Ordinary records and scalar values do not carry a class prototype.
|
|
8516
|
+
// Dynamic/opaque runtime objects keep their existing instanceof fences.
|
|
8517
|
+
if (arms?.every((arm) => arm.kind === "object" || arm.kind === "record" ||
|
|
8518
|
+
arm.kind === "f64" || arm.kind === "string" || arm.kind === "bool" ||
|
|
8519
|
+
arm.kind === "bigint" || arm.kind === "symbol" || isUnitType(arm))) {
|
|
8520
|
+
const key = `instanceof.union:${unionId}:${target.def.name}`;
|
|
8521
|
+
let helper = lowerer.widthHelpers.get(key);
|
|
8522
|
+
if (!helper) {
|
|
8523
|
+
helper = `%instanceof.union.${lowerer.widthHelpers.size}`;
|
|
8524
|
+
lowerer.widthHelpers.set(key, helper);
|
|
8525
|
+
const value = { kind: "varRef", localId: "value.0", type: left.type, loc };
|
|
8526
|
+
const body = [];
|
|
8527
|
+
arms.forEach((arm, tag) => {
|
|
8528
|
+
if (arm.kind !== "object")
|
|
8529
|
+
return;
|
|
8530
|
+
const lhsInfo = lowerer.classes.get(arm.className);
|
|
8531
|
+
if (!lhsInfo)
|
|
8532
|
+
throw new InternalCompilerError(`lowerer bug: unknown class ${arm.className}`);
|
|
8533
|
+
const result = lowerer.inHierarchy(lhsInfo) && lowerer.inHierarchy(target)
|
|
8534
|
+
? { kind: "instanceOf", value: { kind: "unionNarrow", unionId, tag, value, type: arm, loc }, className: target.def.name, type: BOOL, loc }
|
|
8535
|
+
: { kind: "boolLit", value: arm.className === target.def.name || lowerer.isSubclassOf(arm.className, target.def.name), type: BOOL, loc };
|
|
8536
|
+
body.push({
|
|
8537
|
+
kind: "if", cond: { kind: "unionIsTag", unionId, tag, negated: false, value, type: BOOL, loc },
|
|
8538
|
+
then: [{ kind: "return", value: result, loc }], else_: null, loc,
|
|
8539
|
+
});
|
|
8540
|
+
});
|
|
8541
|
+
body.push({ kind: "return", value: { kind: "boolLit", value: false, type: BOOL, loc }, loc });
|
|
8542
|
+
lowerer.liftedFns.push({
|
|
8543
|
+
name: helper, params: [{ localId: "value.0", name: "value", type: left.type }], returnType: BOOL,
|
|
8544
|
+
locals: [{ id: "value.0", name: "value", type: left.type, mutable: false }], body, loc,
|
|
8545
|
+
});
|
|
8546
|
+
}
|
|
8547
|
+
return { kind: "call", callee: helper, args: [left], type: BOOL, loc };
|
|
8548
|
+
}
|
|
8549
|
+
}
|
|
7444
8550
|
if (left.type.kind !== "object") {
|
|
7445
|
-
|
|
8551
|
+
return { kind: "seqExpr", stmts: [{ kind: "exprStmt", expr: left, loc }],
|
|
8552
|
+
result: { kind: "boolLit", value: false, type: BOOL, loc }, type: BOOL, loc };
|
|
7446
8553
|
}
|
|
7447
8554
|
const lhsInfo = lowerer.classes.get(left.type.className);
|
|
7448
8555
|
if (!lhsInfo)
|
|
@@ -7613,6 +8720,12 @@ function lowerPrivateIn(lowerer, expr, priv, loc) {
|
|
|
7613
8720
|
* and dyn/unknown stay fenced. Keys are literal strings — a computed key
|
|
7614
8721
|
* over a shape would need the runtime key table. */
|
|
7615
8722
|
function lowerInExpression(lowerer, expr, loc) {
|
|
8723
|
+
if (lowerer.isStdlibGlobal(expr.right, "process") && lowerer.foldedStringKeyOf(expr.left) === "hrtime") {
|
|
8724
|
+
return { kind: "boolLit", value: true, type: BOOL, loc };
|
|
8725
|
+
}
|
|
8726
|
+
const globalKey = globalSymbolKey(lowerer, expr.right, expr.left);
|
|
8727
|
+
if (globalKey)
|
|
8728
|
+
return { kind: "libCall", fn: "dyn.globalSymbolHas", args: [globalKey], type: BOOL, loc };
|
|
7616
8729
|
// `#name in obj` — the ergonomic brand check (ES2022) — resolves
|
|
7617
8730
|
// before any string-key machinery: the left operand is a private
|
|
7618
8731
|
// NAME, not a value.
|
|
@@ -7664,14 +8777,13 @@ function lowerInExpression(lowerer, expr, loc) {
|
|
|
7664
8777
|
// `in` shares the coercion).
|
|
7665
8778
|
{
|
|
7666
8779
|
const probed = tryLowerExpression(lowerer, expr.right);
|
|
7667
|
-
if (probed
|
|
7668
|
-
|
|
7669
|
-
if (
|
|
7670
|
-
|
|
7671
|
-
const
|
|
7672
|
-
|
|
7673
|
-
|
|
7674
|
-
}
|
|
8780
|
+
if (probed && lowerer.dynConvertible(probed.type)) {
|
|
8781
|
+
const k = lowerer.lowerExpr(expr.left); // JS order: the key evaluates first
|
|
8782
|
+
if (lowerer.dynConvertible(k.type)) {
|
|
8783
|
+
const boxed = lowerer.coerceToExpected(k, DYN);
|
|
8784
|
+
const keyLocal = lowerer.declareHiddenLocal("%inKey", DYN);
|
|
8785
|
+
return { kind: "seqExpr", stmts: [{ kind: "varDecl", localId: keyLocal.id, init: boxed, loc }],
|
|
8786
|
+
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
8787
|
}
|
|
7676
8788
|
}
|
|
7677
8789
|
}
|
|
@@ -7697,7 +8809,18 @@ function lowerInExpression(lowerer, expr, loc) {
|
|
|
7697
8809
|
};
|
|
7698
8810
|
return { kind: "unionIsTag", unionId: envType.unionId, tag: undefTag, negated: true, value: read, type: BOOL, loc };
|
|
7699
8811
|
}
|
|
7700
|
-
|
|
8812
|
+
let recv = lowerer.lowerExpr(expr.right);
|
|
8813
|
+
const classInfo = recv.type.kind === "classval" ? lowerer.classes.get(recv.type.className) : undefined;
|
|
8814
|
+
if ((classInfo && (classInfo.callableBase || hasRuntimeStatics(classInfo))) ||
|
|
8815
|
+
(isJsSourceFile(expr.getSourceFile()) && recv.type.kind === "func" && lowerer.dynConvertible(recv.type))) {
|
|
8816
|
+
recv = lowerer.coerceToExpected(recv, DYN);
|
|
8817
|
+
}
|
|
8818
|
+
const siteType = lowerer.mapTypeOf(lowerer.typeOf(expr.right));
|
|
8819
|
+
if (recv.type.kind === "union" && siteType?.kind === "union" && !typeEquals(recv.type, siteType)) {
|
|
8820
|
+
const helper = lowerer.narrowedRetagHelper(expr.right, recv.type.unionId, siteType.unionId, loc);
|
|
8821
|
+
if (helper)
|
|
8822
|
+
recv = { kind: "call", callee: helper, args: [recv], type: siteType, loc };
|
|
8823
|
+
}
|
|
7701
8824
|
// Error-rooted receivers (builtin or user subclass — the isErrnoException
|
|
7702
8825
|
// predicate's `err instanceof Error && "code" in err` shape): `code`
|
|
7703
8826
|
// answers from the runtime error's code slot (stamped by fs/system/
|
|
@@ -7722,6 +8845,9 @@ function lowerInExpression(lowerer, expr, loc) {
|
|
|
7722
8845
|
while (info && info.base)
|
|
7723
8846
|
info = info.base;
|
|
7724
8847
|
if (info?.def.name === "%Error") {
|
|
8848
|
+
if (key === "cause") {
|
|
8849
|
+
return { kind: "libCall", fn: "error.hasCause", args: [recv], type: BOOL, loc };
|
|
8850
|
+
}
|
|
7725
8851
|
if (key === "code") {
|
|
7726
8852
|
const codeType = lowerer.envValueType();
|
|
7727
8853
|
if (codeType.kind !== "union")
|
|
@@ -7935,8 +9061,8 @@ function lowerRuntimeKeyIn(lowerer, expr, loc) {
|
|
|
7935
9061
|
}
|
|
7936
9062
|
return { kind: "call", callee: helper, args: [keyIr, recv], type: BOOL, loc };
|
|
7937
9063
|
}
|
|
7938
|
-
/** A regex literal `/ab+c/gi` → regexLit (
|
|
7939
|
-
*
|
|
9064
|
+
/** A regex literal `/ab+c/gi` → regexLit (fresh state per evaluation,
|
|
9065
|
+
* shared bytecode per (pattern, flags) pair). The TS parser has already syntax-checked the literal;
|
|
7940
9066
|
* what remains here is the flag-alphabet fence (d and v are
|
|
7941
9067
|
* declared-valid TS flags outside this slice). Named capture groups
|
|
7942
9068
|
* `(?<name>...)` and `\k<name>` backreferences compile — libregexp
|
|
@@ -8077,14 +9203,52 @@ function constInitializerOf(lowerer, ident) {
|
|
|
8077
9203
|
* caller keeps its fence), or the groups list (possibly empty — a
|
|
8078
9204
|
* traced regex WITHOUT named groups is the Node-undefined case). */
|
|
8079
9205
|
export function matchResultNamedGroupsOf(lowerer, e) {
|
|
9206
|
+
let producer = e;
|
|
9207
|
+
while (ts.isParenthesizedExpression(producer) || ts.isNonNullExpression(producer))
|
|
9208
|
+
producer = producer.expression;
|
|
9209
|
+
if (ts.isIdentifier(producer)) {
|
|
9210
|
+
const init = constInitializerOf(lowerer, producer);
|
|
9211
|
+
if (init !== null)
|
|
9212
|
+
return matchResultNamedGroupsOf(lowerer, init);
|
|
9213
|
+
}
|
|
8080
9214
|
const reExpr = matchProducerRegexOf(lowerer, e);
|
|
8081
9215
|
if (reExpr === null)
|
|
8082
9216
|
return null;
|
|
9217
|
+
if (ts.isCallExpression(producer) && ts.isPropertyAccessExpression(producer.expression) &&
|
|
9218
|
+
producer.expression.name.text === "match") {
|
|
9219
|
+
const flags = staticRegexFlagsOf(lowerer, reExpr);
|
|
9220
|
+
if (flags === null)
|
|
9221
|
+
return null;
|
|
9222
|
+
// Global String.match returns whole matches without a groups
|
|
9223
|
+
// property, even when the pattern contains named captures.
|
|
9224
|
+
if (flags.includes("g"))
|
|
9225
|
+
return [];
|
|
9226
|
+
}
|
|
8083
9227
|
const pattern = staticRegexPatternOf(lowerer, reExpr);
|
|
8084
9228
|
if (pattern === null)
|
|
8085
9229
|
return null;
|
|
8086
9230
|
return namedCaptureGroupsOfPattern(pattern);
|
|
8087
9231
|
}
|
|
9232
|
+
function staticRegexFlagsOf(lowerer, node) {
|
|
9233
|
+
let expr = node;
|
|
9234
|
+
while (ts.isParenthesizedExpression(expr) || ts.isNonNullExpression(expr))
|
|
9235
|
+
expr = expr.expression;
|
|
9236
|
+
if (ts.isRegularExpressionLiteral(expr))
|
|
9237
|
+
return expr.text.slice(expr.text.lastIndexOf("/") + 1);
|
|
9238
|
+
if (ts.isIdentifier(expr)) {
|
|
9239
|
+
const init = constInitializerOf(lowerer, expr);
|
|
9240
|
+
return init !== null ? staticRegexFlagsOf(lowerer, init) : null;
|
|
9241
|
+
}
|
|
9242
|
+
if (ts.isNewExpression(expr) && ts.isIdentifier(expr.expression) &&
|
|
9243
|
+
lowerer.isStdlibGlobal(expr.expression, "RegExp")) {
|
|
9244
|
+
const flags = expr.arguments?.[1];
|
|
9245
|
+
if (!flags)
|
|
9246
|
+
return "";
|
|
9247
|
+
if (ts.isStringLiteral(flags) || ts.isNoSubstitutionTemplateLiteral(flags))
|
|
9248
|
+
return flags.text;
|
|
9249
|
+
}
|
|
9250
|
+
return null;
|
|
9251
|
+
}
|
|
8088
9252
|
/** The regex EXPRESSION whose match produced `e` (see
|
|
8089
9253
|
* matchResultNamedGroupsOf for the traced shapes). */
|
|
8090
9254
|
function matchProducerRegexOf(lowerer, e) {
|
|
@@ -8366,12 +9530,24 @@ export function lowerGroupsProjection(lowerer, recv, groups, loc) {
|
|
|
8366
9530
|
}
|
|
8367
9531
|
return { kind: "call", callee: helper, args: [recv], type: recordT, loc };
|
|
8368
9532
|
}
|
|
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). */
|
|
9533
|
+
/** Field reads use the shared FieldTarget union. Native method reads select
|
|
9534
|
+
* an interned unbound callable; invocation supplies its receiver separately. */
|
|
8374
9535
|
export function lowerFieldRead(lowerer, expr) {
|
|
9536
|
+
// A checked receiver already owns its property table. An ambient
|
|
9537
|
+
// generic method signature must not divert its value into native
|
|
9538
|
+
// object-literal specialization (Array.prototype.map is one example).
|
|
9539
|
+
if (ts.isIdentifier(expr.expression)) {
|
|
9540
|
+
const stored = lowerer.peekLocal(expr.expression) ?? lowerer.globalOf(expr.expression);
|
|
9541
|
+
if (stored?.type.kind === "dyn") {
|
|
9542
|
+
const value = lowerer.lowerExpr(expr.expression);
|
|
9543
|
+
if (value.type.kind === "dyn")
|
|
9544
|
+
return {
|
|
9545
|
+
kind: "dynKeyGet", value,
|
|
9546
|
+
key: { kind: "strLit", value: expr.name.text, type: STRING, loc: locOf(expr.name) },
|
|
9547
|
+
...(expr.questionDotToken || hasOptionalChainGuard(expr.expression) ? { optional: true } : {}), type: DYN, loc: locOf(expr),
|
|
9548
|
+
};
|
|
9549
|
+
}
|
|
9550
|
+
}
|
|
8375
9551
|
const target = lowerer.fieldTarget(expr);
|
|
8376
9552
|
if (target)
|
|
8377
9553
|
return lowerer.fieldGetExpr(target, locOf(expr), expr);
|
|
@@ -8388,7 +9564,9 @@ export function lowerFieldRead(lowerer, expr) {
|
|
|
8388
9564
|
if (receiverIr?.kind === "object" &&
|
|
8389
9565
|
(lowerer.findMethodOn(lowerer.classes.get(receiverIr.className) ?? null, expr.name.text) ||
|
|
8390
9566
|
findGenericMethodOn(lowerer, lowerer.classes.get(receiverIr.className) ?? null, expr.name.text))) {
|
|
8391
|
-
|
|
9567
|
+
const info = lowerer.classes.get(receiverIr.className);
|
|
9568
|
+
if (info)
|
|
9569
|
+
return lowerClassMethodValue(lowerer, expr, info);
|
|
8392
9570
|
}
|
|
8393
9571
|
// An object-literal GENERIC method as a VALUE (`o.m` — the member is
|
|
8394
9572
|
// excluded from the record shape): the pinned-value rule verbatim when
|
|
@@ -8448,16 +9626,22 @@ export function lowerFieldRead(lowerer, expr) {
|
|
|
8448
9626
|
* and `switch (r.kind)` work without dedicated test nodes. Anything else
|
|
8449
9627
|
* on a union receiver is rejected specifically (narrow first). */
|
|
8450
9628
|
export function lowerUnionProperty(lowerer, expr) {
|
|
8451
|
-
if (expr
|
|
9629
|
+
if (lowerer.chainBlocked(expr))
|
|
8452
9630
|
return null;
|
|
8453
9631
|
const receiverIr = lowerer.mapTypeOf(lowerer.typeOf(expr.expression));
|
|
8454
9632
|
if (receiverIr?.kind !== "union")
|
|
8455
9633
|
return null;
|
|
8456
|
-
// Lower the receiver
|
|
8457
|
-
//
|
|
8458
|
-
|
|
8459
|
-
//
|
|
8460
|
-
|
|
9634
|
+
// Lower the receiver first: its runtime value can still carry the full
|
|
9635
|
+
// binding union even when the checker has removed several variants.
|
|
9636
|
+
let value = lowerer.lowerExpr(expr.expression);
|
|
9637
|
+
// A guard can remove several variants without selecting one record.
|
|
9638
|
+
// Materialize that sub-union before looking for common fields; arms
|
|
9639
|
+
// ruled out by the guard must not veto a field shared by the survivors.
|
|
9640
|
+
if (value.type.kind === "union" && !typeEquals(value.type, receiverIr)) {
|
|
9641
|
+
const helper = lowerer.narrowedRetagHelper(expr.expression, value.type.unionId, receiverIr.unionId, locOf(expr));
|
|
9642
|
+
if (helper)
|
|
9643
|
+
value = { kind: "call", callee: helper, args: [value], type: receiverIr, loc: locOf(expr) };
|
|
9644
|
+
}
|
|
8461
9645
|
// A checker-union receiver whose VALUE lowered to a plain RECORD (the
|
|
8462
9646
|
// merged-signature fiction — `runner(cmd, args)` where runner joined
|
|
8463
9647
|
// a structural runner type with spawnSync's, and the local adopted
|
|
@@ -8553,20 +9737,29 @@ export function lowerUnionProperty(lowerer, expr) {
|
|
|
8553
9737
|
const keyed = lowerUnionKeyedRead(lowerer, expr, value.type.unionId, value, key, field);
|
|
8554
9738
|
if (keyed)
|
|
8555
9739
|
return keyed;
|
|
9740
|
+
// JavaScript overloads commonly inspect a class brand on a scalar-or-
|
|
9741
|
+
// instance argument (e.g. Matrix4.makeTranslation). Preserve primitive
|
|
9742
|
+
// missing-property semantics and dispatch class data through its native
|
|
9743
|
+
// capsule instead of demanding a field shared by every union arm.
|
|
9744
|
+
if (isJsSourceFile(expr.getSourceFile()) && lowerer.dynConvertible(value.type)) {
|
|
9745
|
+
return { kind: "dynKeyGet", value: lowerer.coerceToExpected(value, DYN), key,
|
|
9746
|
+
...(hasOptionalChainGuard(expr.expression) ? { optional: true } : {}), type: DYN, loc: locOf(expr) };
|
|
9747
|
+
}
|
|
8556
9748
|
lowerer.unsupported("SC1090", expr, `reading '${field}' on a union-typed value (every arm must be an object/record ` +
|
|
8557
9749
|
`with a same-typed field '${field}'; ` +
|
|
8558
9750
|
`${NARROW_FIRST})`);
|
|
8559
9751
|
}
|
|
8560
9752
|
/** The unionDisc generalization: a keyed read on a union receiver whose
|
|
8561
9753
|
* arms answer DIFFERENT (but joinable) types. Each arm contributes its
|
|
8562
|
-
* declared answer — a declared field's type (literal keys), an
|
|
9754
|
+
* declared answer — an array element's type (number keys), a declared field's type (literal keys), an
|
|
8563
9755
|
* index-signature arm's value type (plus its declared fields' types for
|
|
8564
9756
|
* runtime keys, which reach them through the keyed-read helper), and
|
|
8565
9757
|
* UNDEFINED for unit arms (reachable only through optional-chain tails,
|
|
8566
9758
|
* where JS short-circuits to undefined; a unit arm the checker narrowed
|
|
8567
9759
|
* 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
|
|
9760
|
+
* answers; every arm's answer must be the join itself, one of its
|
|
9761
|
+
* arms, or a sub-union whose payload representations are unchanged.
|
|
9762
|
+
* Array reads retain the ordinary invalid-index trap policy.
|
|
8570
9763
|
* Returns null when any arm cannot answer — the caller owns the fence
|
|
8571
9764
|
* message. The caller (the property/element dispatch) maybeNarrows. */
|
|
8572
9765
|
function lowerUnionKeyedRead(lowerer, expr, unionId, value, key, literalField) {
|
|
@@ -8602,6 +9795,11 @@ function lowerUnionKeyedRead(lowerer, expr, unionId, value, key, literalField) {
|
|
|
8602
9795
|
push(UNDEFINED_T);
|
|
8603
9796
|
continue;
|
|
8604
9797
|
}
|
|
9798
|
+
if (key.type.kind === "f64") {
|
|
9799
|
+
if (arm.kind !== "array" || !pushAnswer(arm.elem))
|
|
9800
|
+
return null;
|
|
9801
|
+
continue;
|
|
9802
|
+
}
|
|
8605
9803
|
if (arm.kind !== "record")
|
|
8606
9804
|
return null;
|
|
8607
9805
|
const shape = lowerer.shapes.get(arm.shapeId);
|
|
@@ -8631,13 +9829,11 @@ function lowerUnionKeyedRead(lowerer, expr, unionId, value, key, literalField) {
|
|
|
8631
9829
|
if (joinArms.length === 0)
|
|
8632
9830
|
return null;
|
|
8633
9831
|
// 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.
|
|
9832
|
+
// admit (no dyn/jsval/generator arms; containers only beside unit
|
|
9833
|
+
// siblings). The caller owns the diagnostic for a refused join.
|
|
8638
9834
|
if (joinArms.length > 1 &&
|
|
8639
|
-
(!
|
|
8640
|
-
joinArms.some((a) => a.kind === "
|
|
9835
|
+
(!unionContainerArmsOk(joinArms) ||
|
|
9836
|
+
joinArms.some((a) => a.kind === "dyn" || a.kind === "jsval" || a.kind === "generator" || a.kind === "caught"))) {
|
|
8641
9837
|
return null;
|
|
8642
9838
|
}
|
|
8643
9839
|
joinArms.sort((a, b) => (typeKey(a) < typeKey(b) ? -1 : 1));
|
|
@@ -8645,13 +9841,19 @@ function lowerUnionKeyedRead(lowerer, expr, unionId, value, key, literalField) {
|
|
|
8645
9841
|
// Pass 2: every arm's answer must SURFACE as the join, and index arms
|
|
8646
9842
|
// must pass the single-record keyed-read constraints (the helper is
|
|
8647
9843
|
// shared with recordKeyGet, its missing-key policy included).
|
|
8648
|
-
const surfaces = (t) => typeEquals(t, type) || (type.kind === "union" && lowerer.armTag(type.unionId, t) >= 0)
|
|
9844
|
+
const surfaces = (t) => typeEquals(t, type) || (type.kind === "union" && lowerer.armTag(type.unionId, t) >= 0) ||
|
|
9845
|
+
exactUnionWidening(lowerer, t, type);
|
|
8649
9846
|
for (const arm of def.arms) {
|
|
8650
9847
|
if (isUnitType(arm)) {
|
|
8651
9848
|
if (!(type.kind === "union" && lowerer.armTag(type.unionId, UNDEFINED_T) >= 0))
|
|
8652
9849
|
return null;
|
|
8653
9850
|
continue;
|
|
8654
9851
|
}
|
|
9852
|
+
if (key.type.kind === "f64") {
|
|
9853
|
+
if (arm.kind !== "array" || !surfaces(arm.elem))
|
|
9854
|
+
return null;
|
|
9855
|
+
continue;
|
|
9856
|
+
}
|
|
8655
9857
|
if (arm.kind !== "record")
|
|
8656
9858
|
return null;
|
|
8657
9859
|
const shape = lowerer.shapes.get(arm.shapeId);
|
|
@@ -8669,6 +9871,75 @@ function lowerUnionKeyedRead(lowerer, expr, unionId, value, key, literalField) {
|
|
|
8669
9871
|
}
|
|
8670
9872
|
return { kind: "unionKeyGet", unionId, key, value, type, loc: locOf(expr) };
|
|
8671
9873
|
}
|
|
9874
|
+
/** A structural TypeScript view does not change a nominal object's layout
|
|
9875
|
+
* or accessor dispatch. Resolve the property from the represented class;
|
|
9876
|
+
* the optional checker view must not turn its getter into a record read. */
|
|
9877
|
+
function classFieldTarget(lowerer, receiverNode, receiverType, fieldName, saved) {
|
|
9878
|
+
const info = lowerer.classes.get(receiverType.className);
|
|
9879
|
+
if (!info) {
|
|
9880
|
+
// A receiver typed as a class whose collection deferred: the
|
|
9881
|
+
// deferred diagnostics are what explains the miss.
|
|
9882
|
+
lowerer.flushDeferredClass(receiverType.className);
|
|
9883
|
+
return null;
|
|
9884
|
+
}
|
|
9885
|
+
const fieldType = info.fields.get(fieldName);
|
|
9886
|
+
const lowerObjectReceiver = () => {
|
|
9887
|
+
let obj = saved ?? lowerer.lowerExpr(receiverNode);
|
|
9888
|
+
if (obj.type.kind === "union") {
|
|
9889
|
+
const helper = lowerer.narrowedArmHelper(obj.type.unionId, receiverType, locOf(receiverNode));
|
|
9890
|
+
obj = helper
|
|
9891
|
+
? { kind: "call", callee: helper, args: [obj], type: receiverType, loc: locOf(receiverNode) }
|
|
9892
|
+
: lowerer.maybeNarrow(obj, receiverNode);
|
|
9893
|
+
}
|
|
9894
|
+
return obj.type.kind === "dyn" ? lowerer.coerceInto(receiverNode, obj, receiverType) : obj;
|
|
9895
|
+
};
|
|
9896
|
+
if (fieldName === "cause" && receiverType.className !== "%DOMException") {
|
|
9897
|
+
let root = info;
|
|
9898
|
+
while (root.base)
|
|
9899
|
+
root = root.base;
|
|
9900
|
+
if (root.def.name === "%Error") {
|
|
9901
|
+
return { container: "errorCause", obj: lowerObjectReceiver(), field: "cause", fieldType: DYN };
|
|
9902
|
+
}
|
|
9903
|
+
}
|
|
9904
|
+
if (fieldType) {
|
|
9905
|
+
const obj = lowerObjectReceiver();
|
|
9906
|
+
return { container: "class", obj, className: receiverType.className, field: fieldName, fieldType };
|
|
9907
|
+
}
|
|
9908
|
+
// Accessor property: either half declared anywhere on the chain
|
|
9909
|
+
// makes the name an accessor target (fields and accessors share a
|
|
9910
|
+
// namespace — tsc rejects mixing them, so the halves agree on kind).
|
|
9911
|
+
const getF = lowerer.findMethodOn(info, `get:${fieldName}`);
|
|
9912
|
+
const setF = lowerer.findMethodOn(info, `set:${fieldName}`);
|
|
9913
|
+
if (getF || setF) {
|
|
9914
|
+
const obj = lowerObjectReceiver();
|
|
9915
|
+
return {
|
|
9916
|
+
container: "accessor",
|
|
9917
|
+
obj,
|
|
9918
|
+
className: receiverType.className,
|
|
9919
|
+
field: fieldName,
|
|
9920
|
+
fieldType: getF ? getF.sig.ret : setF.sig.params[0].type,
|
|
9921
|
+
};
|
|
9922
|
+
}
|
|
9923
|
+
return null;
|
|
9924
|
+
}
|
|
9925
|
+
/** A structural assertion can leave the value in its original class
|
|
9926
|
+
* representation, including an unchecked array read's undefined arm. Only
|
|
9927
|
+
* a single nominal payload has an unambiguous property layout. Validate
|
|
9928
|
+
* that payload before dispatching a field or accessor; an absent receiver
|
|
9929
|
+
* must never reach the backend as an object pointer. */
|
|
9930
|
+
function hasClassPayload(lowerer, type) {
|
|
9931
|
+
return type.kind === "object" || type.kind === "union" &&
|
|
9932
|
+
(lowerer.unions.get(type.unionId)?.arms.some((arm) => arm.kind === "object") ?? false);
|
|
9933
|
+
}
|
|
9934
|
+
function representedClassFieldTarget(lowerer, receiverNode, fieldName, value) {
|
|
9935
|
+
const arms = value.type.kind === "union"
|
|
9936
|
+
? lowerer.unions.get(value.type.unionId)?.arms.filter((arm) => !isUnitType(arm))
|
|
9937
|
+
: [value.type];
|
|
9938
|
+
const objectType = arms?.length === 1 ? arms[0] : null;
|
|
9939
|
+
return objectType?.kind === "object"
|
|
9940
|
+
? classFieldTarget(lowerer, receiverNode, objectType, fieldName, value)
|
|
9941
|
+
: null;
|
|
9942
|
+
}
|
|
8672
9943
|
/** Recognizes `obj.field` as an assignable field target: receiver is a
|
|
8673
9944
|
* known class instance OR a record, and the member is a field or (class
|
|
8674
9945
|
* receivers) a declared accessor property. Returns the pieces of a
|
|
@@ -8676,55 +9947,26 @@ function lowerUnionKeyedRead(lowerer, expr, unionId, value, key, literalField) {
|
|
|
8676
9947
|
export function fieldTarget(lowerer, access) {
|
|
8677
9948
|
if (lowerer.chainBlocked(access))
|
|
8678
9949
|
return null;
|
|
8679
|
-
|
|
9950
|
+
if (access.name.text === "stackTraceLimit" && lowerer.isStdlibGlobal(access.expression, "Error")) {
|
|
9951
|
+
return { container: "errorStackLimit", obj: { kind: "numLit", value: 0, type: F64, loc: locOf(access) }, field: "stackTraceLimit", fieldType: F64 };
|
|
9952
|
+
}
|
|
9953
|
+
const stored = ts.isIdentifier(access.expression)
|
|
9954
|
+
? lowerer.peekLocal(access.expression)?.type ?? lowerer.globalOf(access.expression)?.type : undefined;
|
|
9955
|
+
const receiverIr = stored?.kind === "record" || stored?.kind === "object" ? stored : lowerer.mapTypeOf(lowerer.typeOf(access.expression));
|
|
8680
9956
|
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;
|
|
9957
|
+
return classFieldTarget(lowerer, access.expression, receiverIr, access.name.text);
|
|
9958
|
+
}
|
|
9959
|
+
if (receiverIr?.kind === "union" && hasClassPayload(lowerer, receiverIr)) {
|
|
9960
|
+
return representedClassFieldTarget(lowerer, access.expression, access.name.text, lowerer.lowerExpr(access.expression));
|
|
8722
9961
|
}
|
|
8723
9962
|
if (receiverIr?.kind === "record") {
|
|
8724
9963
|
const shape = lowerer.shapes.get(receiverIr.shapeId);
|
|
8725
9964
|
const fieldType = shape?.fields.find((f) => f.name === access.name.text)?.type;
|
|
8726
9965
|
if (fieldType) {
|
|
8727
9966
|
let obj = lowerer.lowerExpr(access.expression);
|
|
9967
|
+
const nominal = representedClassFieldTarget(lowerer, access.expression, access.name.text, obj);
|
|
9968
|
+
if (nominal)
|
|
9969
|
+
return nominal;
|
|
8728
9970
|
// A checker-record receiver whose VALUE stayed dyn (the erased
|
|
8729
9971
|
// all-unknown-fields cast — `(err as { code?: unknown }).code`):
|
|
8730
9972
|
// decline, and the dyn keyed-read fallback answers.
|
|
@@ -8858,7 +10100,7 @@ export function uniqueSymbolKeyOf(lowerer, key) {
|
|
|
8858
10100
|
// Module level only: a unique-symbol const inside a FUNCTION is a
|
|
8859
10101
|
// fresh runtime identity per call — one static slot would conflate
|
|
8860
10102
|
// what JS keeps distinct.
|
|
8861
|
-
if (!ts.isVariableStatement(decl.parent
|
|
10103
|
+
if (!ts.isVariableStatement(decl.parent?.parent) || !ts.isSourceFile(decl.parent?.parent?.parent))
|
|
8862
10104
|
return null;
|
|
8863
10105
|
const init = decl.initializer;
|
|
8864
10106
|
if (!init || !ts.isCallExpression(init) || init.questionDotToken)
|
|
@@ -8891,10 +10133,10 @@ export function symbolFieldInfo(lowerer, expr) {
|
|
|
8891
10133
|
lowerer.flushDeferredClass(receiverIr.className);
|
|
8892
10134
|
return null;
|
|
8893
10135
|
}
|
|
8894
|
-
const key =
|
|
10136
|
+
const key = classSymbolKeyOf(lowerer, expr.argumentExpression);
|
|
8895
10137
|
if (!key)
|
|
8896
10138
|
return null;
|
|
8897
|
-
const field = info.symbolFields?.get(key.
|
|
10139
|
+
const field = info.symbolFields?.get(key.identity);
|
|
8898
10140
|
if (field === undefined)
|
|
8899
10141
|
return null;
|
|
8900
10142
|
const fieldType = info.fields.get(field);
|
|
@@ -8916,6 +10158,14 @@ function symbolFieldTarget(lowerer, expr) {
|
|
|
8916
10158
|
* tsc-clean (the property types as the setter's param), but Node yields
|
|
8917
10159
|
* undefined, which these property types cannot represent. */
|
|
8918
10160
|
export function fieldGetExpr(lowerer, target, loc, blame) {
|
|
10161
|
+
if (target.container === "class" && (target.field === "message" || target.field === "name") && (target.className === "%Error" || lowerer.isSubclassOf(target.className, "%Error")))
|
|
10162
|
+
return errorPropertyRead(lowerer, target.obj, target.field);
|
|
10163
|
+
if (target.container === "errorStackLimit") {
|
|
10164
|
+
return { kind: "libCall", fn: "error.stackLimitGet", args: [], type: F64, loc };
|
|
10165
|
+
}
|
|
10166
|
+
if (target.container === "errorCause") {
|
|
10167
|
+
return { kind: "libCall", fn: "error.cause", args: [target.obj], type: DYN, loc };
|
|
10168
|
+
}
|
|
8919
10169
|
// A record-shaped CHECKER target whose receiver VALUE lives in the checked-dynamic tree
|
|
8920
10170
|
// (a JS file-scope object-literal global): the checked-dynamic keyed
|
|
8921
10171
|
// read — dynKeyGet (a missing key answers the dyn undefined, exactly
|
|
@@ -8935,6 +10185,11 @@ export function fieldGetExpr(lowerer, target, loc, blame) {
|
|
|
8935
10185
|
if (!getF) {
|
|
8936
10186
|
lowerer.unsupported("SC1090", blame, `reading a property that has only a setter ('${target.field}' — Node would yield undefined)`);
|
|
8937
10187
|
}
|
|
10188
|
+
if (!target.field.startsWith("#") && isJsSourceFile(blame.getSourceFile()) && lowerer.dynConvertible(getF.sig.ret)) {
|
|
10189
|
+
const value = { kind: "dynKeyGet", value: lowerer.coerceToExpected(target.obj, DYN),
|
|
10190
|
+
key: { kind: "strLit", value: target.field, type: STRING, loc }, type: DYN, loc };
|
|
10191
|
+
return lowerer.coerceToExpected(value, getF.sig.ret);
|
|
10192
|
+
}
|
|
8938
10193
|
return lowerer.accessorCall(target.className, `get:${target.field}`, target.obj, [], getF.sig.ret, loc);
|
|
8939
10194
|
}
|
|
8940
10195
|
// Record accessor properties: the read IS a call of the %get: closure
|
|
@@ -9012,6 +10267,14 @@ export function fieldGetExpr(lowerer, target, loc, blame) {
|
|
|
9012
10267
|
* call). A write to a getter-only property never gets here in a clean
|
|
9013
10268
|
* program (tsc's TS2540 is the fence); the rejection is the backstop. */
|
|
9014
10269
|
export function fieldSetStmt(lowerer, target, value, loc, blame) {
|
|
10270
|
+
if (target.container === "class" && (target.field === "message" || target.field === "name") && (target.className === "%Error" || lowerer.isSubclassOf(target.className, "%Error")))
|
|
10271
|
+
return errorPropertyWrite(lowerer, target.obj, value, target.field);
|
|
10272
|
+
if (target.container === "errorStackLimit") {
|
|
10273
|
+
return { kind: "exprStmt", expr: { kind: "libCall", fn: "error.stackLimitSet", args: [value], type: VOID, loc }, loc };
|
|
10274
|
+
}
|
|
10275
|
+
if (target.container === "errorCause") {
|
|
10276
|
+
return { kind: "exprStmt", expr: { kind: "libCall", fn: "error.setCause", args: [target.obj, value], type: VOID, loc }, loc };
|
|
10277
|
+
}
|
|
9015
10278
|
// A record-shaped CHECKER target whose receiver VALUE lives in the checked-dynamic tree:
|
|
9016
10279
|
// the checked-dynamic keyed write — dyn.keySet (later writes win,
|
|
9017
10280
|
// insertion order; Node's TypeErrors on non-object receivers), the
|
|
@@ -9113,13 +10376,13 @@ export function lowerPromiseRejectCall(lowerer, call, access) {
|
|
|
9113
10376
|
// identity survives to catch/unhandledRejection observers), and the
|
|
9114
10377
|
// result is promise<dyn> when no context names a concrete one.
|
|
9115
10378
|
const reason = lowerer.lowerExpr(reasonNode);
|
|
9116
|
-
if (reason.type.kind === "dyn") {
|
|
10379
|
+
if (reason.type.kind === "dyn" || lowerer.dynConvertible(reason.type) && reason.type.kind !== "object") {
|
|
9117
10380
|
// The result only ever REJECTS, so the inner type is unobservable:
|
|
9118
10381
|
// adopt the context's promise type when one names it, else the
|
|
9119
10382
|
// checker's own Promise<never> reading (promise<void> — exactly the
|
|
9120
10383
|
// Error-reason form below).
|
|
9121
10384
|
const resultT = type?.kind === "promise" ? type : { kind: "promise", inner: VOID };
|
|
9122
|
-
return { kind: "intrinsic", name: "promise.reject", args: [reason], type: resultT, loc };
|
|
10385
|
+
return { kind: "intrinsic", name: "promise.reject", args: [lowerer.coerceInto(reasonNode, reason, DYN)], type: resultT, loc };
|
|
9123
10386
|
}
|
|
9124
10387
|
// An Error reason with NO context-named promise type (a bare
|
|
9125
10388
|
// top-level `Promise.reject(new Error())` — the unhandled-rejection
|
|
@@ -9180,61 +10443,73 @@ export function lowerPromiseAllTupleCall(lowerer, call, access) {
|
|
|
9180
10443
|
}
|
|
9181
10444
|
/** `obj.f op= e` (and `obj.f++` with rhs null ≡ 1) — the element spelling
|
|
9182
10445
|
* `obj[k] op= e` included when k is a declared symbol-keyed field.
|
|
9183
|
-
*
|
|
9184
|
-
*
|
|
10446
|
+
* Save the receiver and old value before the RHS, then write through
|
|
10447
|
+
* that same receiver even if the RHS replaces its original binding. */
|
|
9185
10448
|
export function lowerFieldCompound(lowerer, access, op, rhsNode, loc) {
|
|
10449
|
+
return { kind: "exprStmt", expr: lowerFieldCompoundValue(lowerer, access, op, rhsNode, loc), loc };
|
|
10450
|
+
}
|
|
10451
|
+
function lowerFieldCompoundValue(lowerer, access, op, rhsNode, loc) {
|
|
9186
10452
|
if (access.expression.kind === ts.SyntaxKind.SuperKeyword) {
|
|
9187
10453
|
lowerer.unsupported("SC1090", access, "compound assignment through 'super' (read and write separately)");
|
|
9188
10454
|
}
|
|
9189
|
-
|
|
9190
|
-
|
|
9191
|
-
|
|
10455
|
+
const body = [];
|
|
10456
|
+
const save = (value, name) => {
|
|
10457
|
+
const local = lowerer.declareHiddenLocal(name, value.type);
|
|
10458
|
+
body.push({ kind: "varDecl", localId: local.id, init: value, loc: value.loc });
|
|
10459
|
+
return varRef(local.id, value.type, value.loc);
|
|
10460
|
+
};
|
|
9192
10461
|
// A CHECKED-DYNAMIC receiver (`context.actual++` — test/common's call
|
|
9193
10462
|
// accounting; dot spelling only — symbol-keyed element targets are
|
|
9194
10463
|
// static fields): read the member (dynKeyGet), VALIDATE it as a
|
|
9195
10464
|
// number (dynCheck — a non-number member throws the catchable
|
|
9196
10465
|
// TypeError where JS would ToNumber-coerce; loud, never a silent
|
|
9197
10466
|
// NaN — SEMANTICS.md), combine, write back (dyn.keySet). The
|
|
9198
|
-
// receiver
|
|
9199
|
-
// read and write matches JS's once-evaluation observably.
|
|
10467
|
+
// receiver and member read are saved before evaluating the RHS.
|
|
9200
10468
|
if (ts.isPropertyAccessExpression(access)) {
|
|
9201
10469
|
const probed = tryLowerExpression(lowerer, access.expression);
|
|
9202
10470
|
if (probed?.type.kind === "dyn") {
|
|
10471
|
+
const receiver = save(probed, "%compoundReceiver");
|
|
9203
10472
|
const key = { kind: "strLit", value: access.name.text, type: STRING, loc: locOf(access.name) };
|
|
9204
|
-
const read = { kind: "dynKeyGet", key, value:
|
|
10473
|
+
const read = save({ kind: "dynKeyGet", key, value: receiver, type: DYN, loc }, "%compoundOld");
|
|
9205
10474
|
const cur = { kind: "dynCheck", value: read, type: F64, loc };
|
|
9206
|
-
const rhs = rhsNode
|
|
10475
|
+
const rhs = save(rhsNode
|
|
9207
10476
|
? lowerer.lowerExpr(rhsNode)
|
|
9208
|
-
: { kind: "numLit", value: 1, type: F64, loc };
|
|
9209
|
-
|
|
10477
|
+
: { kind: "numLit", value: 1, type: F64, loc }, "%compoundRhs");
|
|
10478
|
+
const numericRhs = rhs.type.kind === "dyn"
|
|
10479
|
+
? { kind: "dynCheck", value: rhs, type: F64, loc: rhs.loc }
|
|
10480
|
+
: lowerOptionalNumber(lowerer, rhs, loc);
|
|
10481
|
+
if (numericRhs.type.kind !== "f64")
|
|
9210
10482
|
lowerer.unsupported("SC1043", access);
|
|
9211
|
-
const value = { kind: "bin", op, left: cur, right:
|
|
9212
|
-
const recv2 = lowerer.lowerExpr(access.expression);
|
|
10483
|
+
const value = save({ kind: "bin", op, left: cur, right: numericRhs, type: F64, loc }, "%compoundResult");
|
|
9213
10484
|
const boxed = { kind: "dynFrom", value, type: DYN, loc };
|
|
9214
|
-
|
|
10485
|
+
body.push({
|
|
9215
10486
|
kind: "exprStmt",
|
|
9216
|
-
expr: { kind: "libCall", fn: "dyn.keySet", args: [
|
|
10487
|
+
expr: { kind: "libCall", fn: "dyn.keySet", args: [receiver, { ...key }, boxed], type: VOID, loc },
|
|
9217
10488
|
loc,
|
|
9218
|
-
};
|
|
10489
|
+
});
|
|
10490
|
+
return { kind: "seqExpr", stmts: body, result: value, type: value.type, loc };
|
|
9219
10491
|
}
|
|
9220
10492
|
}
|
|
9221
10493
|
const targetOf = () => ts.isPropertyAccessExpression(access) ? lowerer.fieldTarget(access) : symbolFieldTarget(lowerer, access);
|
|
9222
10494
|
const target = targetOf();
|
|
9223
10495
|
if (!target)
|
|
9224
10496
|
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
|
|
10497
|
+
target.obj = save(target.obj, "%compoundReceiver");
|
|
10498
|
+
// Accessors observe getter, RHS side effects, then setter, all through
|
|
10499
|
+
// the saved receiver.
|
|
10500
|
+
const read = save(lowerer.fieldGetExpr(target, locOf(access), access), "%compoundOld");
|
|
10501
|
+
const rhs = save(rhsNode
|
|
9230
10502
|
? lowerer.lowerExpr(rhsNode)
|
|
9231
|
-
: { kind: "numLit", value: 1, type: F64, loc };
|
|
10503
|
+
: { kind: "numLit", value: 1, type: F64, loc }, "%compoundRhs");
|
|
10504
|
+
const numericRhs = rhs.type.kind === "dyn" && isJsSourceFile(access.getSourceFile())
|
|
10505
|
+
? { kind: "dynCheck", value: rhs, type: F64, loc: rhs.loc }
|
|
10506
|
+
: lowerOptionalNumber(lowerer, rhs, loc);
|
|
9232
10507
|
let value;
|
|
9233
10508
|
if (op === "+" && target.fieldType.kind === "string") {
|
|
9234
10509
|
value = { kind: "strConcat", left: read, right: lowerer.ensureString(rhs, rhsNode ?? access), type: STRING, loc };
|
|
9235
10510
|
}
|
|
9236
|
-
else if (target.fieldType.kind === "f64" &&
|
|
9237
|
-
value = { kind: "bin", op, left: read, right:
|
|
10511
|
+
else if (target.fieldType.kind === "f64" && numericRhs.type.kind === "f64") {
|
|
10512
|
+
value = { kind: "bin", op, left: read, right: numericRhs, type: F64, loc };
|
|
9238
10513
|
}
|
|
9239
10514
|
else if (target.fieldType.kind === "dyn" &&
|
|
9240
10515
|
isJsSourceFile(access.getSourceFile()) &&
|
|
@@ -9270,9 +10545,9 @@ export function lowerFieldCompound(lowerer, access, op, rhsNode, loc) {
|
|
|
9270
10545
|
else {
|
|
9271
10546
|
lowerer.unsupported("SC1043", access);
|
|
9272
10547
|
}
|
|
9273
|
-
|
|
9274
|
-
|
|
9275
|
-
return
|
|
10548
|
+
const result = save(value, "%compoundResult");
|
|
10549
|
+
body.push(lowerer.fieldSetStmt(target, result, loc, access));
|
|
10550
|
+
return { kind: "seqExpr", stmts: body, result, type: result.type, loc };
|
|
9276
10551
|
}
|
|
9277
10552
|
/** Stream-rooted receivers' property surface (readableEnded, destroyed,
|
|
9278
10553
|
* ...): the receiver's mapped class dispatches into lower-stream.ts.
|
|
@@ -9298,8 +10573,8 @@ function lowerStreamObjectProperty(lowerer, expr) {
|
|
|
9298
10573
|
* Interning makes every reference under one ABI the SAME immortal closure,
|
|
9299
10574
|
* so `opt.type === String` compares like JS function identity. */
|
|
9300
10575
|
function primitiveCtorClosure(lowerer, name, loc, dynamicInput) {
|
|
9301
|
-
const ret = name === "String" ? STRING : name === "Number" ? F64 : BOOL;
|
|
9302
|
-
const input = dynamicInput ? DYN : STRING;
|
|
10576
|
+
const ret = name === "String" ? STRING : name === "Number" ? F64 : name === "BigInt" ? BIGINT_T : BOOL;
|
|
10577
|
+
const input = dynamicInput || name === "BigInt" ? DYN : STRING;
|
|
9303
10578
|
const fnT = funcOf([input], ret);
|
|
9304
10579
|
const key = `${name}:${dynamicInput ? "dyn" : "string"}`;
|
|
9305
10580
|
let fnName = lowerer.primitiveCtorFns.get(key);
|
|
@@ -9308,16 +10583,20 @@ function primitiveCtorClosure(lowerer, name, loc, dynamicInput) {
|
|
|
9308
10583
|
lowerer.primitiveCtorFns.set(key, fnName);
|
|
9309
10584
|
const v = { kind: "varRef", localId: "v.0", type: input, loc };
|
|
9310
10585
|
const value = dynamicInput
|
|
9311
|
-
? name === "
|
|
9312
|
-
? { kind: "libCall", fn: "dyn.
|
|
9313
|
-
: name === "
|
|
9314
|
-
? { kind: "libCall", fn: "dyn.
|
|
9315
|
-
:
|
|
9316
|
-
|
|
9317
|
-
|
|
9318
|
-
|
|
9319
|
-
|
|
9320
|
-
|
|
10586
|
+
? name === "BigInt"
|
|
10587
|
+
? { kind: "libCall", fn: "dyn.bigintConstructor", args: [v], type: BIGINT_T, loc }
|
|
10588
|
+
: name === "String"
|
|
10589
|
+
? { kind: "libCall", fn: "dyn.toStringCoerce", args: [v], type: STRING, loc }
|
|
10590
|
+
: name === "Number"
|
|
10591
|
+
? { kind: "libCall", fn: "dyn.numberConstructor", args: [v], type: F64, loc }
|
|
10592
|
+
: { kind: "dynTest", test: "truthy", value: v, type: BOOL, loc }
|
|
10593
|
+
: name === "BigInt"
|
|
10594
|
+
? { kind: "libCall", fn: "dyn.bigintConstructor", args: [v], type: BIGINT_T, loc }
|
|
10595
|
+
: name === "String"
|
|
10596
|
+
? v
|
|
10597
|
+
: name === "Number"
|
|
10598
|
+
? { kind: "libCall", fn: "num.fromString", args: [v], type: F64, loc }
|
|
10599
|
+
: { kind: "strEq", negated: true, left: v, right: { kind: "strLit", value: "", type: STRING, loc }, type: BOOL, loc };
|
|
9321
10600
|
const fn = {
|
|
9322
10601
|
name: fnName,
|
|
9323
10602
|
params: [{ localId: "v.0", name: "value", type: input }],
|
|
@@ -9330,4 +10609,25 @@ function primitiveCtorClosure(lowerer, name, loc, dynamicInput) {
|
|
|
9330
10609
|
}
|
|
9331
10610
|
return { kind: "closure", fnName, captures: [], type: fnT, loc };
|
|
9332
10611
|
}
|
|
10612
|
+
/** Drain using checked dispatch so native class and generator iterators keep their methods. */
|
|
10613
|
+
function checkedIteratorPack(lowerer, source, loc) {
|
|
10614
|
+
const name = "%iterator.pack.checked";
|
|
10615
|
+
if (!lowerer.liftedFns.some((fn) => fn.name === name)) {
|
|
10616
|
+
const ref = (id) => ({ kind: "varRef", localId: id, type: DYN, loc });
|
|
10617
|
+
const property = (key) => ({ kind: "dynKeyGet", value: ref("step"), key: { kind: "strLit", value: key, type: STRING, loc }, type: DYN, loc });
|
|
10618
|
+
lowerer.liftedFns.push({ name, params: [{ localId: "source", name: "source", type: DYN }], returnType: DYN,
|
|
10619
|
+
locals: ["source", "iterator", "step", "result"].map((id) => ({ id, name: id, type: DYN, mutable: id !== "source" })),
|
|
10620
|
+
body: [
|
|
10621
|
+
{ kind: "varDecl", localId: "iterator", init: { kind: "libCall", fn: "dyn.iterator", args: [ref("source"), { kind: "strLit", value: "value", type: STRING, loc }], type: DYN, loc }, loc },
|
|
10622
|
+
{ kind: "varDecl", localId: "result", init: { kind: "dynArrLit", elems: [], type: DYN, loc }, loc },
|
|
10623
|
+
{ kind: "while", cond: { kind: "boolLit", value: true, type: BOOL, loc }, body: [
|
|
10624
|
+
{ kind: "varDecl", localId: "step", init: { kind: "dynInvoke", recv: ref("iterator"), method: "next", calleeName: "iterator.next", args: [], type: DYN, loc }, loc },
|
|
10625
|
+
{ kind: "if", cond: { kind: "dynTest", test: "truthy", value: property("done"), type: BOOL, loc }, then: [{ kind: "return", value: ref("result"), loc }], else_: null, loc },
|
|
10626
|
+
{ kind: "exprStmt", expr: { kind: "libCall", fn: "dyn.packPush", args: [ref("result"), property("value")], type: VOID, loc }, loc },
|
|
10627
|
+
], loc },
|
|
10628
|
+
{ kind: "return", value: ref("result"), loc },
|
|
10629
|
+
], loc });
|
|
10630
|
+
}
|
|
10631
|
+
return { kind: "call", callee: name, args: [source], type: DYN, loc };
|
|
10632
|
+
}
|
|
9333
10633
|
//# sourceMappingURL=lower-exprs.js.map
|