@scriptc/compiler 0.0.34 → 0.0.36
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 +7 -6
- package/dist/backend/build-cache.d.ts +44 -0
- package/dist/backend/build-cache.js +241 -0
- package/dist/backend/build-cache.js.map +1 -0
- package/dist/backend/{emission/emit-async.d.ts → c/async.d.ts} +21 -30
- package/dist/backend/{emission/emit-async.js → c/async.js} +270 -286
- package/dist/backend/c/async.js.map +1 -0
- package/dist/backend/{emission/emitter.d.ts → c/c-emitter.d.ts} +6 -23
- package/dist/backend/{emission/emitter.js → c/c-emitter.js} +53 -107
- package/dist/backend/c/c-emitter.js.map +1 -0
- package/dist/backend/c/exprs.d.ts +8 -0
- package/dist/backend/c/exprs.js +7913 -0
- package/dist/backend/c/exprs.js.map +1 -0
- package/dist/backend/{emission/emit-island.d.ts → c/island.d.ts} +5 -5
- package/dist/backend/{emission/emit-island.js → c/island.js} +24 -22
- package/dist/backend/c/island.js.map +1 -0
- package/dist/backend/{emission → c}/may-throw.d.ts +1 -1
- package/dist/backend/{emission → c}/may-throw.js +1 -1
- package/dist/backend/c/may-throw.js.map +1 -0
- package/dist/backend/{emission/emit-shapes.d.ts → c/shapes.d.ts} +19 -19
- package/dist/backend/{emission/emit-shapes.js → c/shapes.js} +78 -87
- package/dist/backend/c/shapes.js.map +1 -0
- package/dist/backend/{emission/emit-stmts.d.ts → c/stmts.d.ts} +12 -12
- package/dist/backend/{emission/emit-stmts.js → c/stmts.js} +377 -343
- package/dist/backend/c/stmts.js.map +1 -0
- package/dist/backend/{emission/emit-types.d.ts → c/types.d.ts} +1 -1
- package/dist/backend/{emission/emit-types.js → c/types.js} +42 -286
- package/dist/backend/c/types.js.map +1 -0
- package/dist/backend/c/types.test.js +25 -0
- package/dist/backend/c/types.test.js.map +1 -0
- package/dist/backend/{emission/emit-walkers.d.ts → c/walkers.d.ts} +19 -41
- package/dist/backend/{emission/emit-walkers.js → c/walkers.js} +238 -298
- package/dist/backend/c/walkers.js.map +1 -0
- package/dist/backend/ffi-callbacks.d.ts +1 -1
- package/dist/backend/ffi-callbacks.js +4 -3
- package/dist/backend/ffi-callbacks.js.map +1 -1
- package/dist/backend/{library-identity.d.ts → library-identity-markers.d.ts} +0 -4
- package/dist/backend/{library-identity.js → library-identity-markers.js} +10 -9
- package/dist/backend/library-identity-markers.js.map +1 -0
- package/dist/backend/llvm/blocks.d.ts +4 -0
- package/dist/backend/llvm/blocks.js +29 -0
- package/dist/backend/llvm/blocks.js.map +1 -1
- package/dist/backend/llvm/classes.d.ts +2 -19
- package/dist/backend/llvm/classes.js +20 -52
- package/dist/backend/llvm/classes.js.map +1 -1
- package/dist/backend/llvm/common.d.ts +4 -0
- package/dist/backend/llvm/common.js +27 -0
- package/dist/backend/llvm/common.js.map +1 -0
- package/dist/backend/llvm/dyn.d.ts +3 -8
- package/dist/backend/llvm/dyn.js +152 -224
- package/dist/backend/llvm/dyn.js.map +1 -1
- package/dist/backend/llvm/emitter.d.ts +4 -1
- package/dist/backend/llvm/emitter.js +514 -9981
- package/dist/backend/llvm/emitter.js.map +1 -1
- package/dist/backend/llvm/expr-async.d.ts +4 -0
- package/dist/backend/llvm/expr-async.js +726 -0
- package/dist/backend/llvm/expr-async.js.map +1 -0
- package/dist/backend/llvm/expr-bytes.d.ts +13 -0
- package/dist/backend/llvm/expr-bytes.js +456 -0
- package/dist/backend/llvm/expr-bytes.js.map +1 -0
- package/dist/backend/llvm/expr-callbacks.d.ts +22 -0
- package/dist/backend/llvm/expr-callbacks.js +520 -0
- package/dist/backend/llvm/expr-callbacks.js.map +1 -0
- package/dist/backend/llvm/expr-calls.d.ts +2 -0
- package/dist/backend/llvm/expr-calls.js +618 -0
- package/dist/backend/llvm/expr-calls.js.map +1 -0
- package/dist/backend/llvm/expr-containers.d.ts +25 -0
- package/dist/backend/llvm/expr-containers.js +623 -0
- package/dist/backend/llvm/expr-containers.js.map +1 -0
- package/dist/backend/llvm/expr-context.d.ts +255 -0
- package/dist/backend/llvm/expr-context.js +2 -0
- package/dist/backend/llvm/expr-context.js.map +1 -0
- package/dist/backend/llvm/expr-control.d.ts +2 -0
- package/dist/backend/llvm/expr-control.js +473 -0
- package/dist/backend/llvm/expr-control.js.map +1 -0
- package/dist/backend/llvm/expr-dispatch.d.ts +4 -0
- package/dist/backend/llvm/expr-dispatch.js +151 -0
- package/dist/backend/llvm/expr-dispatch.js.map +1 -0
- package/dist/backend/llvm/expr-dynamic.d.ts +2 -0
- package/dist/backend/llvm/expr-dynamic.js +810 -0
- package/dist/backend/llvm/expr-dynamic.js.map +1 -0
- package/dist/backend/llvm/expr-island.d.ts +15 -0
- package/dist/backend/llvm/expr-island.js +690 -0
- package/dist/backend/llvm/expr-island.js.map +1 -0
- package/dist/backend/llvm/expr-primitives.d.ts +6 -0
- package/dist/backend/llvm/expr-primitives.js +622 -0
- package/dist/backend/llvm/expr-primitives.js.map +1 -0
- package/dist/backend/llvm/expr-records.d.ts +9 -0
- package/dist/backend/llvm/expr-records.js +299 -0
- package/dist/backend/llvm/expr-records.js.map +1 -0
- package/dist/backend/llvm/expr-stream-bridges.d.ts +13 -0
- package/dist/backend/llvm/expr-stream-bridges.js +396 -0
- package/dist/backend/llvm/expr-stream-bridges.js.map +1 -0
- package/dist/backend/llvm/expr-stream-callbacks.d.ts +12 -0
- package/dist/backend/llvm/expr-stream-callbacks.js +360 -0
- package/dist/backend/llvm/expr-stream-callbacks.js.map +1 -0
- package/dist/backend/llvm/lib-dispatch.d.ts +5 -0
- package/dist/backend/llvm/lib-dispatch.js +267 -0
- package/dist/backend/llvm/lib-dispatch.js.map +1 -0
- package/dist/backend/llvm/lib-filesystem.d.ts +6 -0
- package/dist/backend/llvm/lib-filesystem.js +594 -0
- package/dist/backend/llvm/lib-filesystem.js.map +1 -0
- package/dist/backend/llvm/lib-network.d.ts +2 -0
- package/dist/backend/llvm/lib-network.js +429 -0
- package/dist/backend/llvm/lib-network.js.map +1 -0
- package/dist/backend/llvm/lib-process.d.ts +5 -0
- package/dist/backend/llvm/lib-process.js +585 -0
- package/dist/backend/llvm/lib-process.js.map +1 -0
- package/dist/backend/llvm/lib-shared.d.ts +6 -0
- package/dist/backend/llvm/lib-shared.js +687 -0
- package/dist/backend/llvm/lib-shared.js.map +1 -0
- package/dist/backend/llvm/lib-stream.d.ts +2 -0
- package/dist/backend/llvm/lib-stream.js +470 -0
- package/dist/backend/llvm/lib-stream.js.map +1 -0
- package/dist/backend/llvm/shapes.d.ts +12 -2
- package/dist/backend/llvm/shapes.js +66 -307
- package/dist/backend/llvm/shapes.js.map +1 -1
- package/dist/backend/llvm/shapes.test.js +52 -0
- package/dist/backend/llvm/shapes.test.js.map +1 -0
- package/dist/backend/llvm/split.d.ts +43 -0
- package/dist/backend/llvm/split.js +227 -0
- package/dist/backend/llvm/split.js.map +1 -0
- package/dist/backend/llvm/split.test.d.ts +1 -0
- package/dist/backend/llvm/split.test.js +102 -0
- package/dist/backend/llvm/split.test.js.map +1 -0
- package/dist/backend/llvm/unsupported.d.ts +1 -1
- package/dist/backend/llvm/walkers.d.ts +2 -3
- package/dist/backend/llvm/walkers.js +164 -218
- package/dist/backend/llvm/walkers.js.map +1 -1
- package/dist/backend/native-codegen.d.ts +38 -0
- package/dist/backend/native-codegen.js +261 -0
- package/dist/backend/native-codegen.js.map +1 -0
- package/dist/backend/native-codegen.test.d.ts +1 -0
- package/dist/backend/native-codegen.test.js +188 -0
- package/dist/backend/native-codegen.test.js.map +1 -0
- package/dist/backend/native-link-info.d.ts +96 -0
- package/dist/backend/native-link-info.js +219 -0
- package/dist/backend/native-link-info.js.map +1 -0
- package/dist/backend/native-link-info.test.d.ts +1 -0
- package/dist/backend/native-link-info.test.js +134 -0
- package/dist/backend/native-link-info.test.js.map +1 -0
- package/dist/backend/{cc.d.ts → native-toolchain.d.ts} +139 -47
- package/dist/backend/{cc.js → native-toolchain.js} +1008 -910
- package/dist/backend/native-toolchain.js.map +1 -0
- package/dist/backend/native-toolchain.test.d.ts +1 -0
- package/dist/backend/{cc-cache.test.js → native-toolchain.test.js} +1297 -127
- package/dist/backend/native-toolchain.test.js.map +1 -0
- package/dist/backend/object-localize.js +1 -1
- package/dist/backend/object-localize.test.js +20 -0
- package/dist/backend/object-localize.test.js.map +1 -1
- package/dist/backend/runtime-abi.d.ts +10 -0
- package/dist/backend/runtime-abi.js +11 -0
- package/dist/backend/runtime-abi.js.map +1 -0
- package/dist/backend/runtime-pack.d.ts +137 -0
- package/dist/backend/runtime-pack.js +400 -0
- package/dist/backend/runtime-pack.js.map +1 -0
- package/dist/backend/runtime-pack.test.d.ts +1 -0
- package/dist/backend/runtime-pack.test.js +438 -0
- package/dist/backend/runtime-pack.test.js.map +1 -0
- package/dist/backend/targets.d.ts +30 -0
- package/dist/backend/targets.js +46 -0
- package/dist/backend/targets.js.map +1 -0
- package/dist/backend/targets.test.d.ts +1 -0
- package/dist/backend/targets.test.js +19 -0
- package/dist/backend/targets.test.js.map +1 -0
- package/dist/backend/vendor-archives.d.ts +41 -0
- package/dist/backend/vendor-archives.js +462 -0
- package/dist/backend/vendor-archives.js.map +1 -0
- package/dist/compat/fetch-profile.d.ts +0 -1
- package/dist/compat/fetch-profile.js +0 -1
- package/dist/compat/fetch-profile.js.map +1 -1
- package/dist/coverage/report.js +3 -3
- package/dist/coverage/report.js.map +1 -1
- package/dist/coverage/surface-manifest.js +25 -32
- package/dist/coverage/surface-manifest.js.map +1 -1
- package/dist/diagnostics/diagnostic.d.ts +11 -12
- package/dist/diagnostics/diagnostic.js +19 -17
- package/dist/diagnostics/diagnostic.js.map +1 -1
- package/dist/diagnostics/render.d.ts +1 -1
- package/dist/diagnostics/render.js +1 -1
- package/dist/diagnostics/render.js.map +1 -1
- package/dist/errors.d.ts +5 -0
- package/dist/errors.js +9 -0
- package/dist/errors.js.map +1 -0
- package/dist/executable/executable-cache.d.ts +86 -0
- package/dist/executable/executable-cache.js +398 -0
- package/dist/executable/executable-cache.js.map +1 -0
- package/dist/executable/executable-cache.test.d.ts +1 -0
- package/dist/executable/executable-cache.test.js +300 -0
- package/dist/executable/executable-cache.test.js.map +1 -0
- package/dist/ffi/{profile.d.ts → ffi-manifest.d.ts} +7 -2
- package/dist/ffi/{profile.js → ffi-manifest.js} +2 -1
- package/dist/ffi/ffi-manifest.js.map +1 -0
- package/dist/frontend/builtin-modules.d.ts +21 -0
- package/dist/frontend/builtin-modules.js +82 -0
- package/dist/frontend/builtin-modules.js.map +1 -0
- package/dist/frontend/cjs-lexer.d.ts +7 -0
- package/dist/frontend/cjs-lexer.js +15 -17
- package/dist/frontend/cjs-lexer.js.map +1 -1
- package/dist/frontend/dts-paths.d.ts +26 -0
- package/dist/frontend/dts-paths.js +41 -0
- package/dist/frontend/dts-paths.js.map +1 -0
- package/dist/frontend/input-tracker.d.ts +0 -2
- package/dist/frontend/input-tracker.js +1 -4
- package/dist/frontend/input-tracker.js.map +1 -1
- package/dist/frontend/lib-contract.d.ts +1 -5
- package/dist/frontend/lib-contract.js +1 -1
- package/dist/frontend/lib-contract.js.map +1 -1
- package/dist/frontend/lib-exports.d.ts +1 -1
- package/dist/frontend/lowering/call-position.d.ts +4 -0
- package/dist/frontend/lowering/call-position.js +7 -0
- package/dist/frontend/lowering/call-position.js.map +1 -0
- package/dist/frontend/lowering/callback-arg.d.ts +20 -0
- package/dist/frontend/lowering/callback-arg.js +44 -0
- package/dist/frontend/lowering/callback-arg.js.map +1 -0
- package/dist/frontend/lowering/lib-boundary.d.ts +1 -1
- package/dist/frontend/lowering/lib-boundary.js +34 -34
- package/dist/frontend/lowering/lib-boundary.js.map +1 -1
- package/dist/frontend/lowering/lower-assert.d.ts +3 -3
- package/dist/frontend/lowering/lower-assert.js +214 -241
- package/dist/frontend/lowering/lower-assert.js.map +1 -1
- package/dist/frontend/lowering/lower-builtins.d.ts +74 -146
- package/dist/frontend/lowering/lower-builtins.js +1126 -1234
- package/dist/frontend/lowering/lower-builtins.js.map +1 -1
- package/dist/frontend/lowering/lower-calls.d.ts +56 -188
- package/dist/frontend/lowering/lower-calls.js +1409 -1494
- package/dist/frontend/lowering/lower-calls.js.map +1 -1
- package/dist/frontend/lowering/lower-classes.d.ts +62 -125
- package/dist/frontend/lowering/lower-classes.js +832 -866
- package/dist/frontend/lowering/lower-classes.js.map +1 -1
- package/dist/frontend/lowering/lower-comptime.d.ts +5 -12
- package/dist/frontend/lowering/lower-comptime.js +32 -31
- package/dist/frontend/lowering/lower-comptime.js.map +1 -1
- package/dist/frontend/lowering/lower-containers.d.ts +35 -135
- package/dist/frontend/lowering/lower-containers.js +1394 -1715
- package/dist/frontend/lowering/lower-containers.js.map +1 -1
- package/dist/frontend/lowering/lower-dgram.d.ts +3 -7
- package/dist/frontend/lowering/lower-dgram.js +97 -111
- package/dist/frontend/lowering/lower-dgram.js.map +1 -1
- package/dist/frontend/lowering/lower-enums.d.ts +4 -4
- package/dist/frontend/lowering/lower-enums.js +30 -30
- package/dist/frontend/lowering/lower-enums.js.map +1 -1
- package/dist/frontend/lowering/{lower-emitter.d.ts → lower-event-emitter.d.ts} +7 -18
- package/dist/frontend/lowering/{lower-emitter.js → lower-event-emitter.js} +172 -174
- package/dist/frontend/lowering/lower-event-emitter.js.map +1 -0
- package/dist/frontend/lowering/lower-expando.d.ts +5 -5
- package/dist/frontend/lowering/lower-expando.js +40 -40
- package/dist/frontend/lowering/lower-expando.js.map +1 -1
- package/dist/frontend/lowering/lower-exprs.d.ts +56 -264
- package/dist/frontend/lowering/lower-exprs.js +1644 -1704
- package/dist/frontend/lowering/lower-exprs.js.map +1 -1
- package/dist/frontend/lowering/lower-generators.d.ts +6 -6
- package/dist/frontend/lowering/lower-generators.js +73 -72
- package/dist/frontend/lowering/lower-generators.js.map +1 -1
- package/dist/frontend/lowering/lower-inspect.d.ts +5 -17
- package/dist/frontend/lowering/lower-inspect.js +236 -238
- package/dist/frontend/lowering/lower-inspect.js.map +1 -1
- package/dist/frontend/lowering/lower-island.d.ts +34 -45
- package/dist/frontend/lowering/lower-island.js +440 -511
- package/dist/frontend/lowering/lower-island.js.map +1 -1
- package/dist/frontend/lowering/lower-mixins.d.ts +7 -10
- package/dist/frontend/lowering/lower-mixins.js +86 -86
- package/dist/frontend/lowering/lower-mixins.js.map +1 -1
- package/dist/frontend/lowering/lower-modules.d.ts +12 -18
- package/dist/frontend/lowering/lower-modules.js +284 -283
- package/dist/frontend/lowering/lower-modules.js.map +1 -1
- package/dist/frontend/lowering/lower-namespaces.d.ts +23 -49
- package/dist/frontend/lowering/lower-namespaces.js +123 -115
- package/dist/frontend/lowering/lower-namespaces.js.map +1 -1
- package/dist/frontend/lowering/lower-server.d.ts +18 -25
- package/dist/frontend/lowering/lower-server.js +899 -899
- package/dist/frontend/lowering/lower-server.js.map +1 -1
- package/dist/frontend/lowering/lower-stmts.d.ts +30 -149
- package/dist/frontend/lowering/lower-stmts.js +1176 -1229
- package/dist/frontend/lowering/lower-stmts.js.map +1 -1
- package/dist/frontend/lowering/lower-stream.d.ts +12 -12
- package/dist/frontend/lowering/lower-stream.js +300 -301
- package/dist/frontend/lowering/lower-stream.js.map +1 -1
- package/dist/frontend/lowering/lower-test.d.ts +5 -5
- package/dist/frontend/lowering/lower-test.js +82 -82
- package/dist/frontend/lowering/lower-test.js.map +1 -1
- package/dist/frontend/lowering/lowerer.d.ts +66 -20
- package/dist/frontend/lowering/lowerer.js +421 -198
- package/dist/frontend/lowering/lowerer.js.map +1 -1
- package/dist/frontend/lowering/pairs-snapshot.d.ts +11 -0
- package/dist/frontend/lowering/pairs-snapshot.js +85 -0
- package/dist/frontend/lowering/pairs-snapshot.js.map +1 -0
- package/dist/frontend/lowering/static-this.d.ts +6 -0
- package/dist/frontend/lowering/static-this.js +24 -0
- package/dist/frontend/lowering/static-this.js.map +1 -0
- package/dist/frontend/lowering/surfaces.d.ts +14 -33
- package/dist/frontend/lowering/surfaces.js +64 -64
- package/dist/frontend/lowering/surfaces.js.map +1 -1
- package/dist/frontend/npm-static-rewrite.js +6 -28
- package/dist/frontend/npm-static-rewrite.js.map +1 -1
- package/dist/frontend/npm-static.d.ts +0 -10
- package/dist/frontend/npm-static.js +4 -7
- package/dist/frontend/npm-static.js.map +1 -1
- package/dist/frontend/npm.d.ts +0 -24
- package/dist/frontend/npm.js +11 -85
- package/dist/frontend/npm.js.map +1 -1
- package/dist/frontend/program.d.ts +3 -1
- package/dist/frontend/program.js +51 -17
- package/dist/frontend/program.js.map +1 -1
- package/dist/frontend/provenance-registry.d.ts +0 -2
- package/dist/frontend/provenance-registry.js +0 -10
- package/dist/frontend/provenance-registry.js.map +1 -1
- package/dist/frontend/provenance.js +4 -7
- package/dist/frontend/provenance.js.map +1 -1
- package/dist/frontend/resolve.d.ts +7 -2
- package/dist/frontend/resolve.js +12 -17
- package/dist/frontend/resolve.js.map +1 -1
- package/dist/frontend/resolve.test.d.ts +1 -0
- package/dist/frontend/resolve.test.js +29 -0
- package/dist/frontend/resolve.test.js.map +1 -0
- package/dist/frontend/ts7/adapter.d.ts +2 -2
- package/dist/frontend/ts7/adapter.js +1 -1
- package/dist/frontend/ts7/adapter.js.map +1 -1
- package/dist/frontend/ts7/checker.d.ts +40 -2
- package/dist/frontend/ts7/checker.js +182 -54
- package/dist/frontend/ts7/checker.js.map +1 -1
- package/dist/frontend/ts7/enums.js +2 -1
- package/dist/frontend/ts7/enums.js.map +1 -1
- package/dist/frontend/ts7/{program.js → program-adapter.js} +7 -6
- package/dist/frontend/ts7/program-adapter.js.map +1 -0
- package/dist/frontend/ts7/world-check.d.ts +1 -3
- package/dist/frontend/ts7/world-check.js +3 -1
- package/dist/frontend/ts7/world-check.js.map +1 -1
- package/dist/frontend/tsc-codes.d.ts +8 -0
- package/dist/frontend/tsc-codes.js +109 -0
- package/dist/frontend/tsc-codes.js.map +1 -0
- package/dist/frontend/{types.d.ts → type-mapper.d.ts} +2 -25
- package/dist/frontend/{types.js → type-mapper.js} +31 -52
- package/dist/frontend/type-mapper.js.map +1 -0
- package/dist/frontend/workspace-registry.d.ts +26 -0
- package/dist/frontend/workspace-registry.js +71 -0
- package/dist/frontend/workspace-registry.js.map +1 -0
- package/dist/index.d.ts +115 -30
- package/dist/index.js +650 -146
- package/dist/index.js.map +1 -1
- package/dist/ir/analysis.d.ts +37 -0
- package/dist/ir/analysis.js +118 -0
- package/dist/ir/analysis.js.map +1 -0
- package/dist/ir/analysis.test.d.ts +1 -0
- package/dist/ir/analysis.test.js +21 -0
- package/dist/ir/analysis.test.js.map +1 -0
- package/dist/ir/build.d.ts +9 -0
- package/dist/ir/build.js +33 -0
- package/dist/ir/build.js.map +1 -0
- package/dist/ir/integer-loops.d.ts +1 -1
- package/dist/ir/{nodes.d.ts → ir.d.ts} +60 -26
- package/dist/ir/{nodes.js → ir.js} +173 -141
- package/dist/ir/ir.js.map +1 -0
- package/dist/ir/ir.test.d.ts +1 -0
- package/dist/ir/ir.test.js +17 -0
- package/dist/ir/ir.test.js.map +1 -0
- package/dist/ir/serialize.d.ts +1 -1
- package/dist/ir/serialize.js +2 -1
- package/dist/ir/serialize.js.map +1 -1
- package/dist/ir/validate.d.ts +1 -1
- package/dist/ir/validate.js +16 -5
- package/dist/ir/validate.js.map +1 -1
- package/dist/library/cache-primitives.d.ts +24 -0
- package/dist/library/cache-primitives.js +83 -0
- package/dist/library/cache-primitives.js.map +1 -0
- package/dist/library/cache-reset.test.d.ts +1 -0
- package/dist/library/cache-reset.test.js +47 -0
- package/dist/library/cache-reset.test.js.map +1 -0
- package/dist/library/compiler-self-identity.d.ts +23 -0
- package/dist/library/compiler-self-identity.js +84 -0
- package/dist/library/compiler-self-identity.js.map +1 -0
- package/dist/library/fence-eval.d.ts +3 -1
- package/dist/library/fence-eval.js +5 -1
- package/dist/library/fence-eval.js.map +1 -1
- package/dist/library/int-infer.d.ts +1 -36
- package/dist/library/int-infer.js +17 -17
- package/dist/library/int-infer.js.map +1 -1
- package/dist/library/{early-cache.d.ts → library-cache.d.ts} +1 -1
- package/dist/library/{early-cache.js → library-cache.js} +37 -111
- package/dist/library/library-cache.js.map +1 -0
- package/dist/library/library-cache.test.d.ts +1 -0
- package/dist/library/{early-cache.test.js → library-cache.test.js} +2 -2
- package/dist/library/library-cache.test.js.map +1 -0
- package/dist/library/{profile.d.ts → library-profile.d.ts} +4 -7
- package/dist/library/{profile.js → library-profile.js} +18 -11
- package/dist/library/library-profile.js.map +1 -0
- package/dist/library/semantic-source.js +2 -1
- package/dist/library/semantic-source.js.map +1 -1
- package/dist/library/sidecar.d.ts +5 -2
- package/dist/library/sidecar.js +11 -5
- package/dist/library/sidecar.js.map +1 -1
- package/dist/startup-cache.d.ts +6 -0
- package/dist/startup-cache.js +6 -0
- package/dist/startup-cache.js.map +1 -0
- package/package.json +7 -2
- package/surface-manifest.json +1 -1
- package/dist/backend/cc-cache.test.js.map +0 -1
- package/dist/backend/cc.js.map +0 -1
- package/dist/backend/emission/emit-async.js.map +0 -1
- package/dist/backend/emission/emit-exprs.d.ts +0 -13
- package/dist/backend/emission/emit-exprs.js +0 -7407
- package/dist/backend/emission/emit-exprs.js.map +0 -1
- package/dist/backend/emission/emit-island.js.map +0 -1
- package/dist/backend/emission/emit-shapes.js.map +0 -1
- package/dist/backend/emission/emit-stmts.js.map +0 -1
- package/dist/backend/emission/emit-types.js.map +0 -1
- package/dist/backend/emission/emit-walkers.js.map +0 -1
- package/dist/backend/emission/emitter.js.map +0 -1
- package/dist/backend/emission/may-throw.js.map +0 -1
- package/dist/backend/library-identity.js.map +0 -1
- package/dist/ffi/profile.js.map +0 -1
- package/dist/frontend/lowering/lower-emitter.js.map +0 -1
- package/dist/frontend/shared.d.ts +0 -82
- package/dist/frontend/shared.js +0 -308
- package/dist/frontend/shared.js.map +0 -1
- package/dist/frontend/ts7/program.js.map +0 -1
- package/dist/frontend/types.js.map +0 -1
- package/dist/ir/nodes.js.map +0 -1
- package/dist/library/early-cache.js.map +0 -1
- package/dist/library/early-cache.test.js.map +0 -1
- package/dist/library/profile.js.map +0 -1
- /package/dist/backend/{cc-cache.test.d.ts → c/types.test.d.ts} +0 -0
- /package/dist/{library/early-cache.test.d.ts → backend/llvm/shapes.test.d.ts} +0 -0
- /package/dist/frontend/ts7/{program.d.ts → program-adapter.d.ts} +0 -0
|
@@ -1,12 +1,13 @@
|
|
|
1
|
+
import { InternalCompilerError } from "../../errors.js";
|
|
1
2
|
/* Class lowering: shape collection over the single-inheritance graph
|
|
2
3
|
* (fields, methods, accessors, overrides), constructor/member lowering with
|
|
3
4
|
* synthesized derived ctors and field initializers, super calls and super
|
|
4
5
|
* accessor access, upcasts, `new` expressions, and the builtin Error
|
|
5
6
|
* hierarchy registration. */
|
|
6
7
|
import * as ts from "../ts7/adapter.js";
|
|
7
|
-
import { BOOL, DATE_T, DYN, F64, bytesOf, JSVAL, RUNTIME_EMITTER_CLASS, RUNTIME_ERROR_CLASSES, RUNTIME_STREAM_CLASSES, STRING, UNDEFINED_T, URL_T, VOID, arrayOf, isSupportedMapKey, isUnitType, typeEquals } from "../../ir/
|
|
8
|
+
import { BOOL, DATE_T, DYN, F64, bytesOf, JSVAL, RUNTIME_EMITTER_CLASS, RUNTIME_ERROR_CLASSES, RUNTIME_STREAM_CLASSES, STRING, UNDEFINED_T, URL_T, VOID, arrayOf, isSupportedMapKey, isUnitType, typeEquals } from "../../ir/ir.js";
|
|
8
9
|
import { MAX_GENERIC_INSTANCES, genericCallInstance, implicitAnyParamSymbolsOf, implicitCallInstance, implicitMonoFile, omittedArgFor } from "./lower-calls.js";
|
|
9
|
-
import { isGenericCallableMemberType, typeKey } from "../
|
|
10
|
+
import { isGenericCallableMemberType, typeKey } from "../type-mapper.js";
|
|
10
11
|
import { cjsClassExprWholeExportOf, isCjsJsFile, isJsSourceFile, isModuleExportsAccess, isNodeTypesPath, locOf } from "../program.js";
|
|
11
12
|
import { PoisonError, dynFallbackType, dynUndefinedExpr, newFnCtx, own } from "./lowerer.js";
|
|
12
13
|
import { bufEncoding, lowerMapSeedArrayNew } from "./lower-containers.js";
|
|
@@ -14,19 +15,20 @@ import { pureReemittable } from "./lower-exprs.js";
|
|
|
14
15
|
import { lowerSearchParamsNew } from "./lower-builtins.js";
|
|
15
16
|
import { requiresDynamicPackageDiag, unsupportedDiag } from "../../diagnostics/diagnostic.js";
|
|
16
17
|
import { STREAM_API_MEMBERS, STREAM_PROP_MEMBERS, UNDERSCORE_METHODS, lowerStreamNew, lowerStreamSuperCall, streamCtorShape } from "./lower-stream.js";
|
|
17
|
-
import { emitOverrideShapeReason, emitSpecSuperForward, emitterRooted, lowerEmitterSuperCall } from "./lower-emitter.js";
|
|
18
|
+
import { emitOverrideShapeReason, emitSpecSuperForward, emitterRooted, lowerEmitterSuperCall } from "./lower-event-emitter.js";
|
|
18
19
|
import { declSymbolOf } from "./lower-modules.js";
|
|
19
20
|
import { uniqueSymbolKeyOf } from "./lower-exprs.js";
|
|
20
21
|
import { lowerHttpAgentNew, lowerHttpServerNew } from "./lower-server.js";
|
|
21
22
|
import { ambientNsRootOf, ambientUndefReadType, ambientUndefVarRootOf, ambientUndefinedFnSymbolOf, fenceEarlyAliasUse, fenceEarlyNsMemberRef, nsMemberIdentOf, nsUndefRead } from "./lower-namespaces.js";
|
|
22
23
|
import { mixinResultBindingClassOf } from "./lower-mixins.js";
|
|
24
|
+
import { rejectStaticThis } from "./static-this.js";
|
|
23
25
|
/** The KEY symbol behind a LATE-BOUND (`__@name@id`) property: resolved
|
|
24
26
|
* from the argument of the element-access assignment that declared it
|
|
25
27
|
* (`this[kLimit] = v` — the declaration list holds the BinaryExpression
|
|
26
28
|
* or the ElementAccessExpression itself). Null when no declaration has
|
|
27
29
|
* that shape (well-known-symbol members like `[Symbol.iterator]`). */
|
|
28
|
-
function lateBoundKeySymOf(
|
|
29
|
-
for (const d of
|
|
30
|
+
function lateBoundKeySymOf(lowerer, p) {
|
|
31
|
+
for (const d of lowerer.checker.declarationsOf(p)) {
|
|
30
32
|
const access = ts.isBinaryExpression(d) && ts.isElementAccessExpression(d.left)
|
|
31
33
|
? d.left
|
|
32
34
|
: ts.isElementAccessExpression(d)
|
|
@@ -34,7 +36,7 @@ function lateBoundKeySymOf(L, p) {
|
|
|
34
36
|
: null;
|
|
35
37
|
if (!access || !ts.isIdentifier(access.argumentExpression))
|
|
36
38
|
continue;
|
|
37
|
-
const sym =
|
|
39
|
+
const sym = lowerer.resolveValueSymbol(access.argumentExpression);
|
|
38
40
|
if (sym)
|
|
39
41
|
return sym;
|
|
40
42
|
}
|
|
@@ -55,7 +57,7 @@ function lateBoundKeySymOf(L, p) {
|
|
|
55
57
|
* statically-resolved key declared in symbolFields, and all the slots
|
|
56
58
|
* agree on one IR type. Null when the shape doesn't hold — the value
|
|
57
59
|
* stays checked-dynamic exactly as before. */
|
|
58
|
-
function symbolSlotReturnType(
|
|
60
|
+
function symbolSlotReturnType(lowerer, fnLike, symbolFields, fields) {
|
|
59
61
|
if (symbolFields.size === 0)
|
|
60
62
|
return null;
|
|
61
63
|
if (fnLike.type !== undefined || !fnLike.body)
|
|
@@ -88,7 +90,7 @@ function symbolSlotReturnType(L, fnLike, symbolFields, fields) {
|
|
|
88
90
|
return null;
|
|
89
91
|
if (e.expression.kind !== ts.SyntaxKind.ThisKeyword)
|
|
90
92
|
return null;
|
|
91
|
-
const key = uniqueSymbolKeyOf(
|
|
93
|
+
const key = uniqueSymbolKeyOf(lowerer, e.argumentExpression);
|
|
92
94
|
const fieldName = key ? symbolFields.get(key.sym) : undefined;
|
|
93
95
|
const t = fieldName !== undefined ? fields.get(fieldName) : undefined;
|
|
94
96
|
if (t === undefined || t.kind === "dyn")
|
|
@@ -106,10 +108,10 @@ function symbolSlotReturnType(L, fnLike, symbolFields, fields) {
|
|
|
106
108
|
* toString reach them through dedicated error.* libCall lowerings, and
|
|
107
109
|
* user classes extend them like any base (the emitted subclass struct
|
|
108
110
|
* embeds ScrError's prefix). */
|
|
109
|
-
export function registerBuiltinErrorClasses(
|
|
111
|
+
export function registerBuiltinErrorClasses(lowerer) {
|
|
110
112
|
const loc = { file: "<builtin>", start: 0, end: 0 };
|
|
111
113
|
for (const [irName, rec] of RUNTIME_ERROR_CLASSES) {
|
|
112
|
-
const base = rec.base ? (
|
|
114
|
+
const base = rec.base ? (lowerer.classes.get(rec.base) ?? null) : null;
|
|
113
115
|
const info = {
|
|
114
116
|
def: {
|
|
115
117
|
name: irName,
|
|
@@ -155,7 +157,7 @@ export function registerBuiltinErrorClasses(L) {
|
|
|
155
157
|
};
|
|
156
158
|
if (base)
|
|
157
159
|
base.subclasses.push(info);
|
|
158
|
-
|
|
160
|
+
lowerer.classes.set(irName, info);
|
|
159
161
|
}
|
|
160
162
|
}
|
|
161
163
|
/** The runtime-provided node:events EventEmitter as an eagerly-registered
|
|
@@ -163,11 +165,11 @@ export function registerBuiltinErrorClasses(L) {
|
|
|
163
165
|
* the moment an emitter type appears, so the info must exist before any
|
|
164
166
|
* lowering. No decl, no lowerable bodies — `new`/super() reach it
|
|
165
167
|
* through emitter.* libCalls, the method surface lowers through
|
|
166
|
-
* lower-emitter.ts, and user classes extend it like any base (the
|
|
168
|
+
* lower-event-emitter.ts, and user classes extend it like any base (the
|
|
167
169
|
* emitted subclass struct embeds ScrEmitter's registry/name prefix —
|
|
168
170
|
* carried by the BACKEND, not by IR fields, so the fields list stays
|
|
169
171
|
* empty and subclass field layout starts right after the prefix). */
|
|
170
|
-
export function registerBuiltinEmitterClass(
|
|
172
|
+
export function registerBuiltinEmitterClass(lowerer) {
|
|
171
173
|
const loc = { file: "<builtin>", start: 0, end: 0 };
|
|
172
174
|
const info = {
|
|
173
175
|
def: { name: RUNTIME_EMITTER_CLASS, runtime: true, fields: [], loc },
|
|
@@ -185,7 +187,7 @@ export function registerBuiltinEmitterClass(L) {
|
|
|
185
187
|
throwingSetters: [],
|
|
186
188
|
staticFields: [],
|
|
187
189
|
};
|
|
188
|
-
|
|
190
|
+
lowerer.classes.set(RUNTIME_EMITTER_CLASS, info);
|
|
189
191
|
}
|
|
190
192
|
/** The runtime-provided node:stream classes as eagerly-registered
|
|
191
193
|
* ClassInfos (the emitter story): mapType names `%Readable` et al the
|
|
@@ -195,10 +197,10 @@ export function registerBuiltinEmitterClass(L) {
|
|
|
195
197
|
* unchanged; the stream method/property surface lowers through
|
|
196
198
|
* lower-stream.ts. No decl, no lowerable bodies, empty field lists —
|
|
197
199
|
* every instance is runtime-allocated (user `extends` is fenced). */
|
|
198
|
-
export function registerBuiltinStreamClasses(
|
|
200
|
+
export function registerBuiltinStreamClasses(lowerer) {
|
|
199
201
|
const loc = { file: "<builtin>", start: 0, end: 0 };
|
|
200
202
|
for (const [irName, rec] of RUNTIME_STREAM_CLASSES) {
|
|
201
|
-
const base =
|
|
203
|
+
const base = lowerer.classes.get(rec.base) ?? null;
|
|
202
204
|
const info = {
|
|
203
205
|
def: { name: irName, runtime: true, base: rec.base, fields: [], loc },
|
|
204
206
|
fields: new Map(),
|
|
@@ -217,7 +219,7 @@ export function registerBuiltinStreamClasses(L) {
|
|
|
217
219
|
};
|
|
218
220
|
if (base)
|
|
219
221
|
base.subclasses.push(info);
|
|
220
|
-
|
|
222
|
+
lowerer.classes.set(irName, info);
|
|
221
223
|
}
|
|
222
224
|
}
|
|
223
225
|
/** The stream ClassInfo a VALUE symbol refers to (`new Readable(...)`,
|
|
@@ -227,27 +229,27 @@ export function registerBuiltinStreamClasses(L) {
|
|
|
227
229
|
* also types child stdio — under @types/node the childStream mapping
|
|
228
230
|
* keeps priority and the static stream classes stand down; the shipped
|
|
229
231
|
* fallback declarations are the supported surface). */
|
|
230
|
-
export function builtinStreamInfoOf(
|
|
232
|
+
export function builtinStreamInfoOf(lowerer, symbol) {
|
|
231
233
|
if (!symbol)
|
|
232
234
|
return null;
|
|
233
|
-
if (!
|
|
235
|
+
if (!lowerer.isStdlibSymbol(symbol)) {
|
|
234
236
|
// A const ALIAS of a namespace member (`const Writable =
|
|
235
237
|
// stream.Writable` — the two-step spelling; the one-step
|
|
236
238
|
// require('stream').Writable rides the same walk): follow the
|
|
237
239
|
// member to the stdlib class symbol. The declaration itself is
|
|
238
240
|
// alias plumbing (streamClassAliasDecl — both declaration walks
|
|
239
241
|
// skip it).
|
|
240
|
-
const decl =
|
|
242
|
+
const decl = lowerer.checker.valueDeclarationOf(symbol);
|
|
241
243
|
if (decl && ts.isVariableDeclaration(decl) &&
|
|
242
244
|
(ts.getCombinedNodeFlags(decl) & ts.NodeFlags.Const) !== 0 &&
|
|
243
245
|
decl.initializer !== undefined &&
|
|
244
246
|
ts.isPropertyAccessExpression(decl.initializer) &&
|
|
245
247
|
!decl.initializer.questionDotToken &&
|
|
246
|
-
|
|
247
|
-
const mSym =
|
|
248
|
-
const target = mSym && mSym.flags & ts.SymbolFlags.Alias ?
|
|
248
|
+
lowerer.builtinNamespaceModuleOf(decl.initializer.expression) === "stream") {
|
|
249
|
+
const mSym = lowerer.checker.getSymbolAtLocation(decl.initializer.name);
|
|
250
|
+
const target = mSym && mSym.flags & ts.SymbolFlags.Alias ? lowerer.checker.getAliasedSymbol(mSym) : mSym;
|
|
249
251
|
if (target && target !== symbol)
|
|
250
|
-
return builtinStreamInfoOf(
|
|
252
|
+
return builtinStreamInfoOf(lowerer, target);
|
|
251
253
|
}
|
|
252
254
|
return null;
|
|
253
255
|
}
|
|
@@ -258,7 +260,7 @@ export function builtinStreamInfoOf(L, symbol) {
|
|
|
258
260
|
}
|
|
259
261
|
if (!irName)
|
|
260
262
|
return null;
|
|
261
|
-
const declared =
|
|
263
|
+
const declared = lowerer.checker.declarationsOf(symbol).some((d) => {
|
|
262
264
|
if (!ts.isClassDeclaration(d))
|
|
263
265
|
return false;
|
|
264
266
|
if (isNodeTypesPath(d.getSourceFile().fileName))
|
|
@@ -272,7 +274,7 @@ export function builtinStreamInfoOf(L, symbol) {
|
|
|
272
274
|
}
|
|
273
275
|
return false;
|
|
274
276
|
});
|
|
275
|
-
return declared ? (
|
|
277
|
+
return declared ? (lowerer.classes.get(irName) ?? null) : null;
|
|
276
278
|
}
|
|
277
279
|
/** The undefined-armed union of a JS class property's inferred type — the
|
|
278
280
|
* honest slot for a field first assigned outside the constructor's top
|
|
@@ -280,13 +282,13 @@ export function builtinStreamInfoOf(L, symbol) {
|
|
|
280
282
|
* inference is unmappable, checked-dynamic (dyn stays out of class
|
|
281
283
|
* fields — KEEP NARROW), or an arm-less kind that cannot join a union
|
|
282
284
|
* (genResultRecord's list, including scalar-backed Date values). */
|
|
283
|
-
function undefArmedFieldType(
|
|
284
|
-
const t =
|
|
285
|
-
const mapped =
|
|
285
|
+
function undefArmedFieldType(lowerer, p) {
|
|
286
|
+
const t = lowerer.checker.getTypeOfSymbol(p);
|
|
287
|
+
const mapped = lowerer.mapTypeOf(t);
|
|
286
288
|
if (!mapped || mapped.kind === "void" || mapped.kind === "dyn")
|
|
287
289
|
return null;
|
|
288
290
|
const byKey = new Map();
|
|
289
|
-
const arms = mapped.kind === "union" ? (
|
|
291
|
+
const arms = mapped.kind === "union" ? (lowerer.unions.get(mapped.unionId)?.arms ?? []) : [mapped];
|
|
290
292
|
for (const a of arms) {
|
|
291
293
|
if (a.kind === "map" || a.kind === "regex" || a.kind === "date" ||
|
|
292
294
|
a.kind === "jsval" || a.kind === "generator") {
|
|
@@ -296,38 +298,38 @@ function undefArmedFieldType(L, p) {
|
|
|
296
298
|
}
|
|
297
299
|
byKey.set(typeKey(UNDEFINED_T), UNDEFINED_T);
|
|
298
300
|
const sorted = [...byKey.values()].sort((a, b) => (typeKey(a) < typeKey(b) ? -1 : 1));
|
|
299
|
-
return { kind: "union", unionId:
|
|
301
|
+
return { kind: "union", unionId: lowerer.unions.intern(sorted) };
|
|
300
302
|
}
|
|
301
303
|
/** The emitter ClassInfo a VALUE symbol refers to (`new EventEmitter`,
|
|
302
304
|
* `extends EventEmitter`, `x instanceof EventEmitter`) — any import
|
|
303
305
|
* spelling (named/default/namespace member, CJS require) resolves to
|
|
304
306
|
* the ambient class. Provenance-checked like the error classes: only a
|
|
305
307
|
* stdlib-file declaration inside the "events" ambient module counts. */
|
|
306
|
-
export function builtinEmitterInfoOf(
|
|
308
|
+
export function builtinEmitterInfoOf(lowerer, symbol) {
|
|
307
309
|
if (!symbol)
|
|
308
310
|
return null;
|
|
309
|
-
if (!
|
|
311
|
+
if (!lowerer.isStdlibSymbol(symbol)) {
|
|
310
312
|
// A const ALIAS of the emitter class member (`const EventEmitter =
|
|
311
313
|
// require('node:events').EventEmitter` — commander's spelling; the
|
|
312
314
|
// two-step `const EE = events.EventEmitter` rides the same walk):
|
|
313
315
|
// follow the member off the module namespace. The declaration
|
|
314
316
|
// itself is alias plumbing (builtinMemberRequireDecl — both
|
|
315
317
|
// declaration walks skip it).
|
|
316
|
-
const decl =
|
|
318
|
+
const decl = lowerer.checker.valueDeclarationOf(symbol);
|
|
317
319
|
if (decl !== undefined && ts.isVariableDeclaration(decl) &&
|
|
318
320
|
(ts.getCombinedNodeFlags(decl) & ts.NodeFlags.Const) !== 0 &&
|
|
319
321
|
decl.initializer !== undefined &&
|
|
320
322
|
ts.isPropertyAccessExpression(decl.initializer) &&
|
|
321
323
|
!decl.initializer.questionDotToken &&
|
|
322
324
|
decl.initializer.name.text === "EventEmitter" &&
|
|
323
|
-
|
|
324
|
-
return
|
|
325
|
+
lowerer.builtinNamespaceModuleOf(decl.initializer.expression) === "events") {
|
|
326
|
+
return lowerer.classes.get(RUNTIME_EMITTER_CLASS) ?? null;
|
|
325
327
|
}
|
|
326
328
|
return null;
|
|
327
329
|
}
|
|
328
330
|
if (symbol.name !== "EventEmitter")
|
|
329
331
|
return null;
|
|
330
|
-
const declared =
|
|
332
|
+
const declared = lowerer.checker.declarationsOf(symbol).some((d) => {
|
|
331
333
|
if (!ts.isClassDeclaration(d) && !ts.isInterfaceDeclaration(d))
|
|
332
334
|
return false;
|
|
333
335
|
let node = d.parent;
|
|
@@ -339,24 +341,24 @@ export function builtinEmitterInfoOf(L, symbol) {
|
|
|
339
341
|
}
|
|
340
342
|
return false;
|
|
341
343
|
});
|
|
342
|
-
return declared ? (
|
|
344
|
+
return declared ? (lowerer.classes.get(RUNTIME_EMITTER_CLASS) ?? null) : null;
|
|
343
345
|
}
|
|
344
346
|
/** The builtin error ClassInfo a VALUE symbol refers to (`new Error`,
|
|
345
347
|
* `extends TypeError`, `x instanceof RangeError`), or null. Provenance-
|
|
346
348
|
* checked: only the standard library's declarations count — a user's own
|
|
347
349
|
* `class Error` resolves through classBySymbol instead. */
|
|
348
|
-
export function builtinErrorInfoOf(
|
|
349
|
-
if (!symbol || !
|
|
350
|
+
export function builtinErrorInfoOf(lowerer, symbol) {
|
|
351
|
+
if (!symbol || !lowerer.isStdlibSymbol(symbol))
|
|
350
352
|
return null;
|
|
351
353
|
for (const [irName, rec] of RUNTIME_ERROR_CLASSES) {
|
|
352
354
|
if (rec.lib === symbol.name)
|
|
353
|
-
return
|
|
355
|
+
return lowerer.classes.get(irName) ?? null;
|
|
354
356
|
}
|
|
355
357
|
return null;
|
|
356
358
|
}
|
|
357
359
|
/** The instance-method surface the runtime EventEmitter owns — subclass
|
|
358
360
|
* members with these names are fenced (collectClassShapeInner) and calls
|
|
359
|
-
* to them on emitter-rooted receivers lower through lower-emitter.ts. */
|
|
361
|
+
* to them on emitter-rooted receivers lower through lower-event-emitter.ts. */
|
|
360
362
|
export const EMITTER_API_MEMBERS = new Set([
|
|
361
363
|
"on", "addListener", "once", "prependListener", "prependOnceListener",
|
|
362
364
|
"off", "removeListener", "removeAllListeners", "emit", "listenerCount",
|
|
@@ -374,18 +376,18 @@ export function decoratorNodesOf(n) {
|
|
|
374
376
|
* `@dec(...)` evaluates the CALLEE before any argument — and property
|
|
375
377
|
* chains throw at their ROOT (`@instance.decorate` reads `instance`
|
|
376
378
|
* first). */
|
|
377
|
-
|
|
379
|
+
function ambientDecoratorThrowNameOf(lowerer, dExpr) {
|
|
378
380
|
let e = dExpr;
|
|
379
381
|
while (ts.isParenthesizedExpression(e))
|
|
380
382
|
e = e.expression;
|
|
381
383
|
const target = ts.isCallExpression(e) ? e.expression : e;
|
|
382
|
-
const root = ambientUndefVarRootOf(
|
|
384
|
+
const root = ambientUndefVarRootOf(lowerer, target);
|
|
383
385
|
if (root)
|
|
384
386
|
return root.text;
|
|
385
387
|
let callee = target;
|
|
386
388
|
while (ts.isParenthesizedExpression(callee))
|
|
387
389
|
callee = callee.expression;
|
|
388
|
-
if (ts.isIdentifier(callee) && ambientUndefinedFnSymbolOf(
|
|
390
|
+
if (ts.isIdentifier(callee) && ambientUndefinedFnSymbolOf(lowerer, callee) !== null)
|
|
389
391
|
return callee.text;
|
|
390
392
|
return null;
|
|
391
393
|
}
|
|
@@ -400,7 +402,7 @@ export function ambientDecoratorThrowNameOf(L, dExpr) {
|
|
|
400
402
|
* computed keys. Anything richer (factory calls over defined values,
|
|
401
403
|
* property-access reads, computed-key calls) stops the proof — the
|
|
402
404
|
* named fences answer instead. */
|
|
403
|
-
export function guaranteedDecorationThrow(
|
|
405
|
+
export function guaranteedDecorationThrow(lowerer, decl) {
|
|
404
406
|
const stripParens = (e) => {
|
|
405
407
|
let x = e;
|
|
406
408
|
while (ts.isParenthesizedExpression(x))
|
|
@@ -408,7 +410,7 @@ export function guaranteedDecorationThrow(L, decl) {
|
|
|
408
410
|
return x;
|
|
409
411
|
};
|
|
410
412
|
const decoratorVerdict = (d) => {
|
|
411
|
-
const name = ambientDecoratorThrowNameOf(
|
|
413
|
+
const name = ambientDecoratorThrowNameOf(lowerer, d.expression);
|
|
412
414
|
if (name !== null)
|
|
413
415
|
return { name, node: d };
|
|
414
416
|
const e = stripParens(d.expression);
|
|
@@ -450,12 +452,12 @@ export function guaranteedDecorationThrow(L, decl) {
|
|
|
450
452
|
}
|
|
451
453
|
return null;
|
|
452
454
|
}
|
|
453
|
-
export function collectClassShape(
|
|
454
|
-
const symbol =
|
|
455
|
+
export function collectClassShape(lowerer, decl) {
|
|
456
|
+
const symbol = lowerer.collectDeferring(() => declSymbolOf(lowerer, decl), () => lowerer.collectClassShapeInner(decl));
|
|
455
457
|
// Typed receivers and module retention know the class only by its
|
|
456
458
|
// qualified IR name — index the deferral under it too.
|
|
457
459
|
if (symbol)
|
|
458
|
-
|
|
460
|
+
lowerer.deferredClassByName.set(lowerer.classNamer(decl), symbol);
|
|
459
461
|
// A poisoned class containing a static BLOCK or a DECORATOR must report
|
|
460
462
|
// EAGERLY: deferral's premise ("an unreached broken declaration costs
|
|
461
463
|
// nothing") fails here — Node runs the block (and calls the decorator)
|
|
@@ -465,13 +467,13 @@ export function collectClassShape(L, decl) {
|
|
|
465
467
|
const hasDeclTimeCode = (n) => ts.isClassStaticBlockDeclaration(n) ||
|
|
466
468
|
(n.modifiers ?? []).some((m) => m.kind === ts.SyntaxKind.Decorator);
|
|
467
469
|
if (symbol && (hasDeclTimeCode(decl) || decl.members.some(hasDeclTimeCode))) {
|
|
468
|
-
const diags =
|
|
470
|
+
const diags = lowerer.deferredDiags.get(symbol);
|
|
469
471
|
if (diags) {
|
|
470
|
-
|
|
471
|
-
if (!
|
|
472
|
-
|
|
472
|
+
lowerer.deferredDiags.delete(symbol);
|
|
473
|
+
if (!lowerer.alreadyFlushed.has(symbol)) {
|
|
474
|
+
lowerer.flushedSymbols.add(symbol);
|
|
473
475
|
for (const d of diags)
|
|
474
|
-
|
|
476
|
+
lowerer.pushDiag(d);
|
|
475
477
|
}
|
|
476
478
|
}
|
|
477
479
|
}
|
|
@@ -490,19 +492,19 @@ export function collectClassShape(L, decl) {
|
|
|
490
492
|
?.find((h) => h.token === ts.SyntaxKind.ExtendsKeyword)
|
|
491
493
|
?.types.map((t) => t.expression)
|
|
492
494
|
.filter(ts.isIdentifier)[0];
|
|
493
|
-
const baseSym = baseIdent ?
|
|
494
|
-
const baseDiags = baseSym ?
|
|
495
|
+
const baseSym = baseIdent ? lowerer.resolveValueSymbol(baseIdent) : null;
|
|
496
|
+
const baseDiags = baseSym ? lowerer.deferredDiags.get(baseSym) : undefined;
|
|
495
497
|
if (baseSym && baseDiags) {
|
|
496
|
-
|
|
497
|
-
if (!
|
|
498
|
-
|
|
498
|
+
lowerer.deferredDiags.delete(baseSym);
|
|
499
|
+
if (!lowerer.alreadyFlushed.has(baseSym)) {
|
|
500
|
+
lowerer.flushedSymbols.add(baseSym);
|
|
499
501
|
for (const d of baseDiags)
|
|
500
|
-
|
|
502
|
+
lowerer.pushDiag(d);
|
|
501
503
|
}
|
|
502
504
|
}
|
|
503
505
|
}
|
|
504
506
|
}
|
|
505
|
-
export function collectClassShapeInner(
|
|
507
|
+
export function collectClassShapeInner(lowerer, decl, jsNameOverride, inst,
|
|
506
508
|
/** MIXIN instantiation mode (lower-mixins.ts): the class inside a
|
|
507
509
|
* mixin function, collected per call site — `base` is the ARGUMENT
|
|
508
510
|
* class (the heritage clause names the mixin's parameter and is
|
|
@@ -515,8 +517,8 @@ mixin) {
|
|
|
515
517
|
// nameless class DECLARATION is `export default class {}` — its
|
|
516
518
|
// symbol is the module's default export (declSymbolOf) and it
|
|
517
519
|
// registers under classNamer's "%anon" spelling (unique per file).
|
|
518
|
-
if (!decl.name && ts.isClassDeclaration(decl) && declSymbolOf(
|
|
519
|
-
|
|
520
|
+
if (!decl.name && ts.isClassDeclaration(decl) && declSymbolOf(lowerer, decl) === undefined) {
|
|
521
|
+
lowerer.unsupported("SC1090", decl, "anonymous classes");
|
|
520
522
|
}
|
|
521
523
|
// Decorators are declaration-time CALLS (they run when the class
|
|
522
524
|
// statement evaluates and may replace the declaration outright).
|
|
@@ -542,16 +544,16 @@ mixin) {
|
|
|
542
544
|
// loaders leave `@dec` in place) — there is no runtime behavior
|
|
543
545
|
// to be exact against.
|
|
544
546
|
if (isJsSourceFile(decl.getSourceFile())) {
|
|
545
|
-
|
|
547
|
+
lowerer.unsupported("SC1090", (classDecoratorNodes[0] ?? decoratorNodesOf(decoratedMembers[0])[0]), "decorators in JavaScript sources (V8 has not shipped decorators — Node cannot execute this file)");
|
|
546
548
|
}
|
|
547
549
|
// The guaranteed-throw SHELL: declarations only (expressions
|
|
548
550
|
// lower their throw at the expression — lowerClassExpression),
|
|
549
551
|
// never instantiations/mixins (they share a family declaration).
|
|
550
552
|
if (inst === undefined && mixin === undefined &&
|
|
551
553
|
ts.isClassDeclaration(decl) && decl.typeParameters === undefined) {
|
|
552
|
-
const thrown = guaranteedDecorationThrow(
|
|
554
|
+
const thrown = guaranteedDecorationThrow(lowerer, decl);
|
|
553
555
|
if (thrown) {
|
|
554
|
-
const className =
|
|
556
|
+
const className = lowerer.classNamer(decl);
|
|
555
557
|
const info = {
|
|
556
558
|
def: {
|
|
557
559
|
name: className,
|
|
@@ -578,12 +580,12 @@ mixin) {
|
|
|
578
580
|
shapes: [{ kind: "ambientThrow", name: thrown.name }],
|
|
579
581
|
},
|
|
580
582
|
};
|
|
581
|
-
|
|
583
|
+
lowerer.classes.set(className, info);
|
|
582
584
|
const classSymbol = decl.name
|
|
583
|
-
?
|
|
584
|
-
: declSymbolOf(
|
|
585
|
+
? lowerer.checker.getSymbolAtLocation(decl.name)
|
|
586
|
+
: declSymbolOf(lowerer, decl);
|
|
585
587
|
if (classSymbol)
|
|
586
|
-
|
|
588
|
+
lowerer.classBySymbol.set(classSymbol, info);
|
|
587
589
|
return;
|
|
588
590
|
}
|
|
589
591
|
}
|
|
@@ -598,7 +600,7 @@ mixin) {
|
|
|
598
600
|
? "auto-accessor decorators"
|
|
599
601
|
: "field decorators")
|
|
600
602
|
: "member decorators";
|
|
601
|
-
|
|
603
|
+
lowerer.unsupported("SC1090", dec, `${kind} (the standard context object and member replacement have no static lowering — class decorators and provably-throwing ambient decorations compile)`);
|
|
602
604
|
}
|
|
603
605
|
}
|
|
604
606
|
if (classDecoratorNodes.length > 0) {
|
|
@@ -606,14 +608,14 @@ mixin) {
|
|
|
606
608
|
// minted class; only once-evaluated declarations have a single
|
|
607
609
|
// decoration event to lower.
|
|
608
610
|
if (!ts.isClassDeclaration(decl)) {
|
|
609
|
-
|
|
611
|
+
lowerer.unsupported("SC1090", classDecoratorNodes[0], "decorators on class expressions (each evaluation decorates a distinct class)");
|
|
610
612
|
}
|
|
611
613
|
// A generic class declares ONCE in JS (one decoration event over
|
|
612
614
|
// the one runtime Box) but compiles per instantiation here — the
|
|
613
615
|
// family object is never constructed and the instantiations were
|
|
614
616
|
// never individually decorated.
|
|
615
617
|
if (decl.typeParameters !== undefined) {
|
|
616
|
-
|
|
618
|
+
lowerer.unsupported("SC1090", classDecoratorNodes[0], "decorators on generic classes (JS decorates the one runtime class; the compiled family instantiates per type argument)");
|
|
617
619
|
}
|
|
618
620
|
}
|
|
619
621
|
}
|
|
@@ -629,9 +631,9 @@ mixin) {
|
|
|
629
631
|
// FAMILY of classes has no once-evaluated story.
|
|
630
632
|
const familyMode = decl.typeParameters !== undefined && inst === undefined;
|
|
631
633
|
if (familyMode && !ts.isClassDeclaration(decl)) {
|
|
632
|
-
|
|
634
|
+
lowerer.unsupported("SC1090", decl, "generic class expressions");
|
|
633
635
|
}
|
|
634
|
-
const className = inst ? inst.name : mixin ? mixin.name :
|
|
636
|
+
const className = inst ? inst.name : mixin ? mixin.name : lowerer.classNamer(decl); // program-wide qualified name
|
|
635
637
|
// Single inheritance: `extends` of a class declared in the program.
|
|
636
638
|
// tsc guarantees the base is declared before the derived class (its
|
|
637
639
|
// use-before-declaration error), and collection runs in module order,
|
|
@@ -646,7 +648,7 @@ mixin) {
|
|
|
646
648
|
if (familyMode && decl.heritageClauses !== undefined) {
|
|
647
649
|
const tpSyms = new Set();
|
|
648
650
|
for (const tp of decl.typeParameters) {
|
|
649
|
-
const s =
|
|
651
|
+
const s = lowerer.checker.getSymbolAtLocation(tp.name);
|
|
650
652
|
if (s)
|
|
651
653
|
tpSyms.add(s);
|
|
652
654
|
}
|
|
@@ -656,7 +658,7 @@ mixin) {
|
|
|
656
658
|
for (const t of clause.types) {
|
|
657
659
|
let mentions = false;
|
|
658
660
|
ts.walkPreorder(t, (n) => {
|
|
659
|
-
const s = ts.isIdentifier(n) ?
|
|
661
|
+
const s = ts.isIdentifier(n) ? lowerer.checker.getSymbolAtLocation(n) : undefined;
|
|
660
662
|
if (s && tpSyms.has(s)) {
|
|
661
663
|
mentions = true;
|
|
662
664
|
return "stop";
|
|
@@ -664,7 +666,7 @@ mixin) {
|
|
|
664
666
|
return undefined;
|
|
665
667
|
});
|
|
666
668
|
if (mentions) {
|
|
667
|
-
|
|
669
|
+
lowerer.unsupported("SC1090", t, "generic classes whose 'extends' clause mentions their own type parameters (each instantiation would need a different base)");
|
|
668
670
|
}
|
|
669
671
|
}
|
|
670
672
|
}
|
|
@@ -680,15 +682,15 @@ mixin) {
|
|
|
680
682
|
// `extends events.EventEmitter` — the namespace-member spelling of
|
|
681
683
|
// the ambient emitter base resolves like the named import.
|
|
682
684
|
if (t && ts.isPropertyAccessExpression(t.expression) && ts.isIdentifier(t.expression.name)) {
|
|
683
|
-
const memberSym =
|
|
685
|
+
const memberSym = lowerer.checker.getSymbolAtLocation(t.expression.name);
|
|
684
686
|
const resolved = memberSym && memberSym.flags & ts.SymbolFlags.Alias
|
|
685
|
-
?
|
|
687
|
+
? lowerer.checker.getAliasedSymbol(memberSym)
|
|
686
688
|
: memberSym;
|
|
687
|
-
const emitterBase =
|
|
688
|
-
const streamBaseNs = builtinStreamInfoOf(
|
|
689
|
+
const emitterBase = lowerer.builtinEmitterInfoOf(resolved);
|
|
690
|
+
const streamBaseNs = builtinStreamInfoOf(lowerer, resolved);
|
|
689
691
|
if (emitterBase || streamBaseNs) {
|
|
690
692
|
if (t.typeArguments)
|
|
691
|
-
|
|
693
|
+
lowerer.unsupported("SC1090", t, "extending generic classes");
|
|
692
694
|
base = (emitterBase ?? streamBaseNs);
|
|
693
695
|
continue;
|
|
694
696
|
}
|
|
@@ -697,14 +699,14 @@ mixin) {
|
|
|
697
699
|
// (import= alias chains included), with the source-order guard
|
|
698
700
|
// (the class statement evaluates at its init position; a base
|
|
699
701
|
// block below it would still be uninitialized in Node).
|
|
700
|
-
if (!t.expression.questionDotToken && nsMemberIdentOf(
|
|
702
|
+
if (!t.expression.questionDotToken && nsMemberIdentOf(lowerer, t.expression)) {
|
|
701
703
|
if (t.typeArguments)
|
|
702
|
-
|
|
704
|
+
lowerer.unsupported("SC1090", t, "extending generic classes");
|
|
703
705
|
if (memberSym)
|
|
704
|
-
fenceEarlyNsMemberRef(
|
|
705
|
-
const nsBase = resolved ?
|
|
706
|
+
fenceEarlyNsMemberRef(lowerer, t.expression, memberSym);
|
|
707
|
+
const nsBase = resolved ? lowerer.classBySymbol.get(resolved) : undefined;
|
|
706
708
|
if (!nsBase) {
|
|
707
|
-
|
|
709
|
+
lowerer.unsupported("SC1090", t, `extending the namespace member '${t.expression.name.text}' (no class lowering)`);
|
|
708
710
|
}
|
|
709
711
|
base = nsBase;
|
|
710
712
|
continue;
|
|
@@ -721,15 +723,15 @@ mixin) {
|
|
|
721
723
|
// answers null for them) and keep the computed-expression fence:
|
|
722
724
|
// the runtime base is whichever assignment ran last.
|
|
723
725
|
if (!t.expression.questionDotToken) {
|
|
724
|
-
const propBase = propertyAssignedClassInfoOf(
|
|
726
|
+
const propBase = propertyAssignedClassInfoOf(lowerer, memberSym);
|
|
725
727
|
if (propBase) {
|
|
726
728
|
if (t.typeArguments)
|
|
727
|
-
|
|
729
|
+
lowerer.unsupported("SC1090", t, "extending generic classes");
|
|
728
730
|
const baseDecl = propBase.decl;
|
|
729
731
|
if (baseDecl != null &&
|
|
730
732
|
baseDecl.getSourceFile() === decl.getSourceFile() &&
|
|
731
733
|
baseDecl.getStart() > decl.getStart()) {
|
|
732
|
-
|
|
734
|
+
lowerer.unsupported("SC1090", t, `extending '${t.expression.getText()}' above the statement that assigns it (the property is still undefined when this class evaluates — Node throws here; assign the base first)`);
|
|
733
735
|
}
|
|
734
736
|
base = propBase;
|
|
735
737
|
continue;
|
|
@@ -738,12 +740,12 @@ mixin) {
|
|
|
738
740
|
// fence (the generic computed-expression one below would hide
|
|
739
741
|
// what actually blocks it).
|
|
740
742
|
const rebinds = memberSym !== undefined &&
|
|
741
|
-
|
|
743
|
+
lowerer.checker
|
|
742
744
|
.declarationsOf(memberSym)
|
|
743
745
|
.filter((d) => ts.isBinaryExpression(d) &&
|
|
744
746
|
d.operatorToken.kind === ts.SyntaxKind.EqualsToken).length > 1;
|
|
745
747
|
if (rebinds) {
|
|
746
|
-
|
|
748
|
+
lowerer.unsupported("SC1090", t, `extending the reassigned property '${t.expression.getText()}' (the runtime base is whichever assignment ran last — bind the class exactly once)`);
|
|
747
749
|
}
|
|
748
750
|
}
|
|
749
751
|
}
|
|
@@ -753,8 +755,8 @@ mixin) {
|
|
|
753
755
|
// recursion order delivers exactly that).
|
|
754
756
|
if (t && ts.isClassExpression(t.expression)) {
|
|
755
757
|
if (t.typeArguments)
|
|
756
|
-
|
|
757
|
-
const baseExpr =
|
|
758
|
+
lowerer.unsupported("SC1090", t, "extending generic classes");
|
|
759
|
+
const baseExpr = lowerer.lowerClassExpressionInfo(t.expression);
|
|
758
760
|
base = baseExpr;
|
|
759
761
|
continue;
|
|
760
762
|
}
|
|
@@ -765,16 +767,16 @@ mixin) {
|
|
|
765
767
|
// callee is NOT a mixin function keeps the computed-expression
|
|
766
768
|
// fence below.
|
|
767
769
|
if (t && ts.isCallExpression(t.expression) && !t.typeArguments) {
|
|
768
|
-
const mixinBase =
|
|
770
|
+
const mixinBase = lowerer.mixinCallClassInfoOf(t.expression);
|
|
769
771
|
if (mixinBase) {
|
|
770
772
|
base = mixinBase;
|
|
771
773
|
continue;
|
|
772
774
|
}
|
|
773
775
|
}
|
|
774
776
|
if (!t || !ts.isIdentifier(t.expression)) {
|
|
775
|
-
|
|
777
|
+
lowerer.unsupported("SC1090", clause, "extending computed expressions");
|
|
776
778
|
}
|
|
777
|
-
const symbol =
|
|
779
|
+
const symbol = lowerer.resolveValueSymbol(t.expression);
|
|
778
780
|
// Extending a REBINDABLE decorated class (analysis already ran —
|
|
779
781
|
// this collection is a class expression or a generic
|
|
780
782
|
// instantiation demanded during lowering): the runtime base is
|
|
@@ -782,53 +784,53 @@ mixin) {
|
|
|
782
784
|
// subclasses collected BEFORE analysis meet the same fence from
|
|
783
785
|
// analyzeClassDecoration's subclasses check.
|
|
784
786
|
{
|
|
785
|
-
const directBase = symbol &&
|
|
787
|
+
const directBase = symbol && lowerer.classBySymbol.get(symbol);
|
|
786
788
|
if (directBase && directBase.classDecorators?.valueGlobalId !== undefined) {
|
|
787
|
-
|
|
789
|
+
lowerer.unsupported("SC1090", t, `extending the decorated class '${directBase.def.jsName ?? directBase.def.name}' (its decorators may replace it — the runtime base would be the decoration result)`);
|
|
788
790
|
}
|
|
789
791
|
if (directBase)
|
|
790
|
-
fenceDecorationThrows(
|
|
792
|
+
fenceDecorationThrows(lowerer, directBase, t);
|
|
791
793
|
}
|
|
792
794
|
// `extends DOMException`: the runtime instance carries hidden
|
|
793
795
|
// slots (the legacy code, the cause) BEYOND the ScrError prefix
|
|
794
796
|
// the IR fields describe — a subclass layout would overlap them.
|
|
795
|
-
if (
|
|
796
|
-
|
|
797
|
+
if (lowerer.builtinErrorInfoOf(symbol)?.def.name === "%DOMException") {
|
|
798
|
+
lowerer.unsupported("SC1090", t, "extending DOMException (its runtime layout carries hidden slots a subclass would overlap — extend Error and set name/code yourself)");
|
|
797
799
|
}
|
|
798
|
-
const named = (symbol &&
|
|
799
|
-
|
|
800
|
+
const named = (symbol && lowerer.classBySymbol.get(symbol)) ?? lowerer.builtinErrorInfoOf(symbol) ??
|
|
801
|
+
lowerer.builtinEmitterInfoOf(symbol) ?? builtinStreamInfoOf(lowerer, symbol) ??
|
|
800
802
|
// A const BINDING holding exactly one class (`const B = Animal`,
|
|
801
803
|
// `const B = class {…}`): the base is that class — extends
|
|
802
804
|
// through the alias is the declaration story (a general class
|
|
803
805
|
// VALUE stays fenced: the runtime base would be dynamic).
|
|
804
|
-
exactClassOfReceiver(
|
|
806
|
+
exactClassOfReceiver(lowerer, t.expression) ??
|
|
805
807
|
// A require BINDING of a class-expression whole export
|
|
806
808
|
// (`const C = require('./x')` over `module.exports = class {…}`):
|
|
807
809
|
// the alias resolves to the expression's own symbol — the same
|
|
808
810
|
// declaration story, collected on demand.
|
|
809
|
-
propertyAssignedClassInfoOf(
|
|
811
|
+
propertyAssignedClassInfoOf(lowerer, symbol) ??
|
|
810
812
|
// A const BINDING of a mixin call (`const Tagged = M(Base);
|
|
811
813
|
// class D extends Tagged {}`): the binding pins that call's
|
|
812
814
|
// instantiation — collected on demand (lower-mixins.ts).
|
|
813
|
-
mixinResultBindingClassOf(
|
|
815
|
+
mixinResultBindingClassOf(lowerer, symbol) ?? null;
|
|
814
816
|
// `extends Box<number>` — a GENERIC program class as the base: the
|
|
815
817
|
// base is the concrete INSTANTIATION, resolved through the heritage
|
|
816
818
|
// type (mapType registers/reuses `Box%0`).
|
|
817
819
|
if (named?.generic) {
|
|
818
|
-
const instT =
|
|
819
|
-
const mappedBase =
|
|
820
|
-
const instBase = mappedBase?.kind === "object" ?
|
|
820
|
+
const instT = lowerer.checker.getTypeAtLocation(t);
|
|
821
|
+
const mappedBase = lowerer.mapTypeOf(instT);
|
|
822
|
+
const instBase = mappedBase?.kind === "object" ? lowerer.classes.get(mappedBase.className) : undefined;
|
|
821
823
|
if (!instBase || instBase.generic) {
|
|
822
|
-
|
|
824
|
+
lowerer.unsupported("SC1090", t, `extending the generic class '${t.expression.text}' without a compiled concrete instantiation (the type arguments must map — see the instantiation's own diagnostic)`);
|
|
823
825
|
}
|
|
824
826
|
base = instBase;
|
|
825
827
|
continue;
|
|
826
828
|
}
|
|
827
829
|
if (t.typeArguments)
|
|
828
|
-
|
|
830
|
+
lowerer.unsupported("SC1090", t, "extending generic classes");
|
|
829
831
|
base = named;
|
|
830
832
|
if (!base) {
|
|
831
|
-
|
|
833
|
+
lowerer.unsupported("SC1090", t, `extending classes not declared in the program ('${t.expression.text}')`);
|
|
832
834
|
}
|
|
833
835
|
}
|
|
834
836
|
const fields = new Map(base ? base.fields : []);
|
|
@@ -881,21 +883,21 @@ mixin) {
|
|
|
881
883
|
? genericFieldFnNodeOf(member)
|
|
882
884
|
: member;
|
|
883
885
|
if (!ts.isIdentifier(member.name) && !ts.isPrivateIdentifier(member.name)) {
|
|
884
|
-
|
|
886
|
+
lowerer.unsupported("SC1090", member, "computed generic method names");
|
|
885
887
|
}
|
|
886
888
|
if (fnNode.asteriskToken)
|
|
887
|
-
|
|
889
|
+
lowerer.unsupported("SC1071", member);
|
|
888
890
|
const mName = member.name.text;
|
|
889
891
|
const typeParams = [];
|
|
890
892
|
for (const tp of fnNode.typeParameters) {
|
|
891
|
-
const sym =
|
|
893
|
+
const sym = lowerer.checker.getSymbolAtLocation(tp.name);
|
|
892
894
|
if (!sym)
|
|
893
|
-
|
|
895
|
+
lowerer.unsupported("SC1090", member, "this method form");
|
|
894
896
|
typeParams.push(sym);
|
|
895
897
|
}
|
|
896
898
|
for (const param of fnNode.parameters) {
|
|
897
899
|
if (!ts.isIdentifier(param.name) && !ts.isObjectBindingPattern(param.name) && !ts.isArrayBindingPattern(param.name)) {
|
|
898
|
-
|
|
900
|
+
lowerer.unsupported("SC1031", param);
|
|
899
901
|
}
|
|
900
902
|
}
|
|
901
903
|
(isStatic ? genericStatics : genericMethods).set(mName, {
|
|
@@ -921,19 +923,7 @@ mixin) {
|
|
|
921
923
|
// with arrow functions transparent (they inherit the block's
|
|
922
924
|
// `this`) and this-binding function forms opaque (their `this` is
|
|
923
925
|
// their own).
|
|
924
|
-
|
|
925
|
-
if (ts.isFunctionExpression(n) || ts.isFunctionDeclaration(n) ||
|
|
926
|
-
ts.isMethodDeclaration(n) || ts.isConstructorDeclaration(n) ||
|
|
927
|
-
ts.isGetAccessor(n) || ts.isSetAccessor(n) ||
|
|
928
|
-
ts.isClassDeclaration(n) || ts.isClassExpression(n)) {
|
|
929
|
-
return;
|
|
930
|
-
}
|
|
931
|
-
if (n.kind === ts.SyntaxKind.ThisKeyword || n.kind === ts.SyntaxKind.SuperKeyword) {
|
|
932
|
-
L.unsupported("SC1090", n, "'this' in class static blocks (it names the class — reference the class by name instead)");
|
|
933
|
-
}
|
|
934
|
-
n.forEachChild(checkThis);
|
|
935
|
-
};
|
|
936
|
-
member.body.forEachChild(checkThis);
|
|
926
|
+
rejectStaticThis(lowerer, member.body, () => "'this' in class static blocks (it names the class — reference the class by name instead)");
|
|
937
927
|
staticBlocks.push(member);
|
|
938
928
|
continue;
|
|
939
929
|
}
|
|
@@ -965,17 +955,17 @@ mixin) {
|
|
|
965
955
|
(ts.isIdentifier(member.name) || ts.isPrivateIdentifier(member.name)) &&
|
|
966
956
|
member.initializer &&
|
|
967
957
|
member.postfixToken?.kind !== ts.SyntaxKind.QuestionToken) {
|
|
968
|
-
const type =
|
|
958
|
+
const type = lowerer.irTypeOf(member.name);
|
|
969
959
|
if (type.kind === "void")
|
|
970
|
-
|
|
960
|
+
lowerer.badType(member.name, lowerer.typeOf(member.name));
|
|
971
961
|
if (type.kind === "dyn") {
|
|
972
|
-
|
|
962
|
+
lowerer.unsupported("SC1090", member.name, "'unknown'-typed static fields");
|
|
973
963
|
}
|
|
974
964
|
staticFields.push({
|
|
975
965
|
name: member.name.text,
|
|
976
966
|
type,
|
|
977
967
|
initializer: member.initializer,
|
|
978
|
-
globalId: `%g.s.${
|
|
968
|
+
globalId: `%g.s.${lowerer.classNamer(decl)}.${member.name.text}`,
|
|
979
969
|
readonly: modifiers.some((m) => m.kind === ts.SyntaxKind.ReadonlyKeyword),
|
|
980
970
|
});
|
|
981
971
|
}
|
|
@@ -987,7 +977,7 @@ mixin) {
|
|
|
987
977
|
member.body &&
|
|
988
978
|
member.typeParameters === undefined &&
|
|
989
979
|
member.asteriskToken === undefined) {
|
|
990
|
-
const { shapes, funcType: ft } =
|
|
980
|
+
const { shapes, funcType: ft } = lowerer.lambdaSignature(member);
|
|
991
981
|
staticMethods.set(member.name.text, { params: shapes, ret: ft.ret, member });
|
|
992
982
|
}
|
|
993
983
|
// GENERIC static methods monomorphize like top-level generic
|
|
@@ -1033,11 +1023,11 @@ mixin) {
|
|
|
1033
1023
|
const pname = memberName.text;
|
|
1034
1024
|
if (base !== null &&
|
|
1035
1025
|
(base.fields.has(pname) ||
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
findGenericMethodOn(
|
|
1040
|
-
|
|
1026
|
+
lowerer.findMethodOn(base, pname) !== null ||
|
|
1027
|
+
lowerer.findMethodOn(base, `get:${pname}`) !== null ||
|
|
1028
|
+
lowerer.findMethodOn(base, `set:${pname}`) !== null ||
|
|
1029
|
+
findGenericMethodOn(lowerer, base, pname) !== null)) {
|
|
1030
|
+
lowerer.unsupported("SC1090", memberName, `redeclaring the private name '${pname}' of a base class (JS gives each class its own distinct '${pname}' slot; these layouts have one slot per name — rename one)`);
|
|
1041
1031
|
}
|
|
1042
1032
|
}
|
|
1043
1033
|
// The EventEmitter API surface is runtime-provided: a subclass
|
|
@@ -1046,7 +1036,7 @@ mixin) {
|
|
|
1046
1036
|
// override is fenced rather than silently split-brained — with ONE
|
|
1047
1037
|
// exception: `emit` in the forwarding shape on a plain (non-
|
|
1048
1038
|
// stream) emitter-rooted class monomorphizes per event name
|
|
1049
|
-
// (lower-emitter.ts's emit-overrides block). Stream-rooted classes
|
|
1039
|
+
// (lower-event-emitter.ts's emit-overrides block). Stream-rooted classes
|
|
1050
1040
|
// keep the fence for emit too: the runtime stream machinery emits
|
|
1051
1041
|
// 'data'/'end'/... internally, which could never route through
|
|
1052
1042
|
// the override.
|
|
@@ -1067,18 +1057,18 @@ mixin) {
|
|
|
1067
1057
|
if (memberName.text === "emit" && !streamRooted && !inst && !mixin &&
|
|
1068
1058
|
ts.isMethodDeclaration(member) &&
|
|
1069
1059
|
!member.modifiers?.some((m) => m.kind === ts.SyntaxKind.StaticKeyword)) {
|
|
1070
|
-
const reason = emitOverrideShapeReason(
|
|
1060
|
+
const reason = emitOverrideShapeReason(lowerer, member);
|
|
1071
1061
|
if (reason === null) {
|
|
1072
|
-
const eventSym =
|
|
1073
|
-
const restSym =
|
|
1062
|
+
const eventSym = lowerer.checker.getSymbolAtLocation(member.parameters[0].name);
|
|
1063
|
+
const restSym = lowerer.checker.getSymbolAtLocation(member.parameters[1].name);
|
|
1074
1064
|
if (eventSym && restSym) {
|
|
1075
1065
|
emitOverride = { decl: member, eventSym, restSym };
|
|
1076
1066
|
continue;
|
|
1077
1067
|
}
|
|
1078
1068
|
}
|
|
1079
|
-
|
|
1069
|
+
lowerer.unsupported("SC1090", memberName, `overriding EventEmitter's 'emit' outside the forwarding shape (${reason ?? "its parameters do not resolve statically"}; the compiled form is \`emit(event: string, ...args: unknown[]): boolean\`)`);
|
|
1080
1070
|
}
|
|
1081
|
-
|
|
1071
|
+
lowerer.unsupported("SC1090", memberName, `overriding the EventEmitter member '${memberName.text}' (the runtime owns the emitter surface)`);
|
|
1082
1072
|
}
|
|
1083
1073
|
// The stream surface is likewise runtime-provided on stream-rooted
|
|
1084
1074
|
// subclasses: API members (push/read/write/...) and the property
|
|
@@ -1098,15 +1088,15 @@ mixin) {
|
|
|
1098
1088
|
if (streamBase) {
|
|
1099
1089
|
const name = memberName.text;
|
|
1100
1090
|
if (STREAM_API_MEMBERS.has(name) || STREAM_PROP_MEMBERS.has(name)) {
|
|
1101
|
-
|
|
1091
|
+
lowerer.unsupported("SC1090", memberName, `overriding the stream member '${name}' (the runtime owns the stream surface)`);
|
|
1102
1092
|
}
|
|
1103
1093
|
if (name === "_writev" || name === "_construct") {
|
|
1104
|
-
|
|
1094
|
+
lowerer.unsupported("SC1090", memberName, `declaring '${name}' on a stream subclass (${name === "_writev" ? "batched writes are" : "deferred construction is"} not lowered — writes deliver one chunk at a time)`);
|
|
1105
1095
|
}
|
|
1106
1096
|
const accepted = streamCtorShape(streamBase.def.name).accepted;
|
|
1107
1097
|
for (const [option, methodName] of UNDERSCORE_METHODS) {
|
|
1108
1098
|
if (name === methodName && !accepted.includes(option)) {
|
|
1109
|
-
|
|
1099
|
+
lowerer.unsupported("SC1090", memberName, `declaring '${name}' on a ${streamBase.def.name.slice(1)} subclass (its constructor consumes ${accepted.map((a) => `'${UNDERSCORE_METHODS.get(a)}'`).join("/")})`);
|
|
1110
1100
|
}
|
|
1111
1101
|
}
|
|
1112
1102
|
}
|
|
@@ -1127,13 +1117,13 @@ mixin) {
|
|
|
1127
1117
|
if (modifiers?.some((m) => m.kind === ts.SyntaxKind.AccessorKeyword)) {
|
|
1128
1118
|
// `accessor x = 1` desugars (in JS) to a private slot plus a
|
|
1129
1119
|
// get/set pair — declare the field and accessors explicitly.
|
|
1130
|
-
|
|
1120
|
+
lowerer.unsupported("SC1090", member, "auto-accessor fields ('accessor x')");
|
|
1131
1121
|
}
|
|
1132
1122
|
// #private fields ride the ordinary field machinery — the '#'
|
|
1133
1123
|
// name is unspellable publicly, so the slot never collides, and
|
|
1134
1124
|
// enumeration surfaces (inspect) exclude it like Node.
|
|
1135
1125
|
if (!ts.isIdentifier(member.name) && !ts.isPrivateIdentifier(member.name)) {
|
|
1136
|
-
|
|
1126
|
+
lowerer.unsupported("SC1090", member, "computed field names");
|
|
1137
1127
|
}
|
|
1138
1128
|
// A field initialized with a GENERIC arrow/function expression
|
|
1139
1129
|
// (`time = async <T>(label, fn) => {...}` — the Output.time
|
|
@@ -1148,15 +1138,15 @@ mixin) {
|
|
|
1148
1138
|
// an annotation that pins a concrete signature makes an ordinary
|
|
1149
1139
|
// closure field, which the normal path below owns.
|
|
1150
1140
|
if (genericFieldFnNodeOf(member) !== null &&
|
|
1151
|
-
isGenericCallableMemberType(
|
|
1141
|
+
isGenericCallableMemberType(lowerer.typeOf(member.name), lowerer.checker)) {
|
|
1152
1142
|
if (fields.has(member.name.text)) {
|
|
1153
|
-
|
|
1143
|
+
lowerer.unsupported("SC1090", member.name, "redeclaring inherited fields");
|
|
1154
1144
|
}
|
|
1155
|
-
if (
|
|
1156
|
-
|
|
1145
|
+
if (lowerer.findMethodOn(base, member.name.text)) {
|
|
1146
|
+
lowerer.unsupported("SC1090", member.name, "fields shadowing inherited methods");
|
|
1157
1147
|
}
|
|
1158
|
-
if (findGenericMethodOn(
|
|
1159
|
-
|
|
1148
|
+
if (findGenericMethodOn(lowerer, base, member.name.text)) {
|
|
1149
|
+
lowerer.unsupported("SC1090", member.name, `redeclaring the inherited generic member '${member.name.text}' (generic members dispatch statically, so the base's call sites could never reach this redeclaration)`);
|
|
1160
1150
|
}
|
|
1161
1151
|
collectGenericMember(member, false);
|
|
1162
1152
|
continue;
|
|
@@ -1165,8 +1155,8 @@ mixin) {
|
|
|
1165
1155
|
// the two dispatch worlds (static per-instantiation calls would
|
|
1166
1156
|
// never see the field's value) — the method-vs-generic mixing
|
|
1167
1157
|
// fence, field form.
|
|
1168
|
-
if (findGenericMethodOn(
|
|
1169
|
-
|
|
1158
|
+
if (findGenericMethodOn(lowerer, base, member.name.text)) {
|
|
1159
|
+
lowerer.unsupported("SC1090", member.name, `fields shadowing the inherited generic member '${member.name.text}' (generic members dispatch statically and would never reach this field's value)`);
|
|
1170
1160
|
}
|
|
1171
1161
|
// OPTIONAL fields (`a?: string`) are the record-field precedent
|
|
1172
1162
|
// applied to class shapes: the checker already types the slot
|
|
@@ -1174,13 +1164,13 @@ mixin) {
|
|
|
1174
1164
|
// undefined arm (undefFieldInitLineC — Node defines the property
|
|
1175
1165
|
// as undefined on construction, verified), and reads/writes ride
|
|
1176
1166
|
// the ordinary undefined-armed union machinery.
|
|
1177
|
-
const type =
|
|
1167
|
+
const type = lowerer.irTypeOf(member.name);
|
|
1178
1168
|
if (type.kind === "void")
|
|
1179
|
-
|
|
1169
|
+
lowerer.badType(member.name, lowerer.typeOf(member.name));
|
|
1180
1170
|
// dyn stays out of class fields (KEEP NARROW; record
|
|
1181
1171
|
// fields and array elements are unmappable via mapType already).
|
|
1182
1172
|
if (type.kind === "dyn") {
|
|
1183
|
-
|
|
1173
|
+
lowerer.unsupported("SC1090", member.name, "'unknown'-typed class fields");
|
|
1184
1174
|
}
|
|
1185
1175
|
if (fields.has(member.name.text)) {
|
|
1186
1176
|
// REDECLARING an inherited field: Node [[Define]]s the OWN
|
|
@@ -1200,12 +1190,12 @@ mixin) {
|
|
|
1200
1190
|
fieldOrder.push({ name: member.name.text, type, initializer: member.initializer, redeclared: true });
|
|
1201
1191
|
continue;
|
|
1202
1192
|
}
|
|
1203
|
-
|
|
1193
|
+
lowerer.unsupported("SC1090", member.name, member.initializer
|
|
1204
1194
|
? "redeclaring inherited fields at a different type"
|
|
1205
1195
|
: "redeclaring inherited fields without an initializer (Node resets the field to undefined)");
|
|
1206
1196
|
}
|
|
1207
|
-
if (
|
|
1208
|
-
|
|
1197
|
+
if (lowerer.findMethodOn(base, member.name.text)) {
|
|
1198
|
+
lowerer.unsupported("SC1090", member.name, "fields shadowing inherited methods");
|
|
1209
1199
|
}
|
|
1210
1200
|
// Initializer-less fields whose type ADMITS undefined start as
|
|
1211
1201
|
// JS's undefined (the allocation writes the interned undefined
|
|
@@ -1221,10 +1211,10 @@ mixin) {
|
|
|
1221
1211
|
// list checked against the constructor after the member loop
|
|
1222
1212
|
// (the constructor may be declared later in the class body).
|
|
1223
1213
|
const admitsUndefined = (type.kind === "union" &&
|
|
1224
|
-
(
|
|
1214
|
+
(lowerer.unions.get(type.unionId)?.arms.some((a) => a.kind === "undefinedT") ?? false)) ||
|
|
1225
1215
|
type.kind === "jsval";
|
|
1226
1216
|
if (!member.initializer && !admitsUndefined) {
|
|
1227
|
-
const opts =
|
|
1217
|
+
const opts = lowerer.program.getCompilerOptions();
|
|
1228
1218
|
const spi = opts.strictPropertyInitialization ?? opts.strict ?? false;
|
|
1229
1219
|
if (member.postfixToken?.kind === ts.SyntaxKind.ExclamationToken) {
|
|
1230
1220
|
unguardedFields.push({
|
|
@@ -1253,7 +1243,7 @@ mixin) {
|
|
|
1253
1243
|
if (!member.body)
|
|
1254
1244
|
continue;
|
|
1255
1245
|
if (ctor)
|
|
1256
|
-
|
|
1246
|
+
lowerer.unsupported("SC1090", member, "constructor overloads");
|
|
1257
1247
|
// PARAMETER PROPERTIES (`constructor(public x: number)`): pure
|
|
1258
1248
|
// sugar — the parameter declares a field and assigns it from the
|
|
1259
1249
|
// parameter's value. Visibility (public/private/protected) and
|
|
@@ -1277,39 +1267,39 @@ mixin) {
|
|
|
1277
1267
|
// Non-keyword modifiers (parameter decorators) are rejected
|
|
1278
1268
|
// by tsc under standard decorators; defensive.
|
|
1279
1269
|
if (p.modifiers?.length)
|
|
1280
|
-
|
|
1270
|
+
lowerer.unsupported("SC1090", p, "this parameter form");
|
|
1281
1271
|
continue;
|
|
1282
1272
|
}
|
|
1283
1273
|
// tsc rejects binding patterns (TS1187) and rest params
|
|
1284
1274
|
// (TS1317) as parameter properties; defensive.
|
|
1285
1275
|
if (!ts.isIdentifier(p.name) || p.dotDotDotToken) {
|
|
1286
|
-
|
|
1276
|
+
lowerer.unsupported("SC1090", p, "this parameter property form");
|
|
1287
1277
|
}
|
|
1288
1278
|
const name = p.name.text;
|
|
1289
|
-
const shape =
|
|
1279
|
+
const shape = lowerer.paramShape(p);
|
|
1290
1280
|
const type = shape.bodyType ?? shape.type;
|
|
1291
1281
|
if (type.kind === "void")
|
|
1292
|
-
|
|
1282
|
+
lowerer.badType(p.name, lowerer.typeOf(p.name));
|
|
1293
1283
|
// The class-field dyn rule verbatim (KEEP NARROW).
|
|
1294
1284
|
if (type.kind === "dyn") {
|
|
1295
|
-
|
|
1285
|
+
lowerer.unsupported("SC1090", p.name, "'unknown'-typed class fields");
|
|
1296
1286
|
}
|
|
1297
1287
|
// `override x` (and any same-named inherited member) would
|
|
1298
1288
|
// redeclare a base slot — the declared-field rule verbatim.
|
|
1299
1289
|
if (fields.has(name)) {
|
|
1300
|
-
|
|
1290
|
+
lowerer.unsupported("SC1090", p.name, "redeclaring inherited fields");
|
|
1301
1291
|
}
|
|
1302
|
-
if (
|
|
1303
|
-
|
|
1292
|
+
if (lowerer.findMethodOn(base, name)) {
|
|
1293
|
+
lowerer.unsupported("SC1090", p.name, "fields shadowing inherited methods");
|
|
1304
1294
|
}
|
|
1305
1295
|
paramProps.push({ name, type, param: p });
|
|
1306
1296
|
}
|
|
1307
1297
|
ctor = member;
|
|
1308
1298
|
}
|
|
1309
1299
|
else if (ts.isMethodDeclaration(member)) {
|
|
1310
|
-
const mName = classMemberNameOf(
|
|
1300
|
+
const mName = classMemberNameOf(lowerer, member.name);
|
|
1311
1301
|
if (mName === null)
|
|
1312
|
-
|
|
1302
|
+
lowerer.unsupported("SC1090", member, "computed method names");
|
|
1313
1303
|
// PUBLIC generator METHODS stay fenced (virtualCall dispatch
|
|
1314
1304
|
// over gen-spawn wrappers has no story yet); module-level
|
|
1315
1305
|
// function* and object-literal *methods compile — and #PRIVATE
|
|
@@ -1319,13 +1309,13 @@ mixin) {
|
|
|
1319
1309
|
// emitter routes through the gen-spawn wrapper with `this` as
|
|
1320
1310
|
// param 0 — the async-method precedent, generator form.
|
|
1321
1311
|
if (member.asteriskToken !== undefined && !ts.isPrivateIdentifier(member.name)) {
|
|
1322
|
-
|
|
1312
|
+
lowerer.unsupported("SC1071", member, "generator methods (a #private generator method compiles — privates never dispatch dynamically; or declare a module-level function* and call it from the method)");
|
|
1323
1313
|
}
|
|
1324
1314
|
// An async #private generator (`async *#m()`) is still an async
|
|
1325
1315
|
// generator — the blanket SC1071 fence.
|
|
1326
1316
|
if (member.asteriskToken !== undefined &&
|
|
1327
1317
|
member.modifiers?.some((m) => m.kind === ts.SyntaxKind.AsyncKeyword)) {
|
|
1328
|
-
|
|
1318
|
+
lowerer.unsupported("SC1071", member, "async generators (async function*)");
|
|
1329
1319
|
}
|
|
1330
1320
|
// An ABSTRACT method is a signature with no body — type-world,
|
|
1331
1321
|
// except that it declares the vtable slot: calls through
|
|
@@ -1339,24 +1329,24 @@ mixin) {
|
|
|
1339
1329
|
// per-call-site instantiation needs a nearest declarer WITH a
|
|
1340
1330
|
// body, which an abstract declaration never has.
|
|
1341
1331
|
if (member.typeParameters !== undefined) {
|
|
1342
|
-
|
|
1332
|
+
lowerer.unsupported("SC1090", member, "abstract generic methods (generic methods monomorphize from the nearest declaration's body, and an abstract declaration has none)");
|
|
1343
1333
|
}
|
|
1344
|
-
const { shapes, ret } = abstractMemberSignature(
|
|
1334
|
+
const { shapes, ret } = abstractMemberSignature(lowerer, member);
|
|
1345
1335
|
if (fields.has(mName)) {
|
|
1346
|
-
|
|
1336
|
+
lowerer.unsupported("SC1090", member.name, "methods shadowing inherited fields");
|
|
1347
1337
|
}
|
|
1348
|
-
if (findGenericMethodOn(
|
|
1349
|
-
|
|
1338
|
+
if (findGenericMethodOn(lowerer, base, mName)) {
|
|
1339
|
+
lowerer.unsupported("SC1090", member.name, `overriding the inherited generic method '${mName}' with a non-generic method (generic methods dispatch statically and would never reach this override)`);
|
|
1350
1340
|
}
|
|
1351
1341
|
// Abstract re-declarations keep the overridden ABI exactly,
|
|
1352
1342
|
// like any override (a concrete implementation below must
|
|
1353
1343
|
// agree with BOTH, which exactness makes one constraint).
|
|
1354
|
-
const overridden =
|
|
1344
|
+
const overridden = lowerer.findMethodOn(base, mName);
|
|
1355
1345
|
if (overridden &&
|
|
1356
1346
|
(overridden.sig.params.length !== shapes.length ||
|
|
1357
1347
|
!overridden.sig.params.every((p, i) => typeEquals(p.type, shapes[i].type)) ||
|
|
1358
1348
|
!typeEquals(overridden.sig.ret, ret))) {
|
|
1359
|
-
|
|
1349
|
+
lowerer.unsupported("SC1090", member.name, "overriding a method with a different signature (parameter and return types must match the base declaration exactly)");
|
|
1360
1350
|
}
|
|
1361
1351
|
methods.set(mName, { params: shapes, ret, abstract: true });
|
|
1362
1352
|
continue;
|
|
@@ -1373,10 +1363,10 @@ mixin) {
|
|
|
1373
1363
|
// calls vs vtable slots — cannot see each other's overrides).
|
|
1374
1364
|
if (member.typeParameters !== undefined) {
|
|
1375
1365
|
if (fields.has(mName)) {
|
|
1376
|
-
|
|
1366
|
+
lowerer.unsupported("SC1090", member.name, "methods shadowing inherited fields");
|
|
1377
1367
|
}
|
|
1378
|
-
if (
|
|
1379
|
-
|
|
1368
|
+
if (lowerer.findMethodOn(base, mName)) {
|
|
1369
|
+
lowerer.unsupported("SC1090", member.name, `overriding the inherited method '${mName}' with a generic method (generic methods dispatch statically, so the base's vtable slot could never reach this override)`);
|
|
1380
1370
|
}
|
|
1381
1371
|
collectGenericMember(member, false);
|
|
1382
1372
|
continue;
|
|
@@ -1408,9 +1398,9 @@ mixin) {
|
|
|
1408
1398
|
ts.isIdentifier(member.name) &&
|
|
1409
1399
|
inst === undefined && decl.typeParameters === undefined &&
|
|
1410
1400
|
!fields.has(member.name.text) &&
|
|
1411
|
-
!
|
|
1412
|
-
!findGenericMethodOn(
|
|
1413
|
-
const implicit = implicitAnyParamSymbolsOf(
|
|
1401
|
+
!lowerer.findMethodOn(base, member.name.text) &&
|
|
1402
|
+
!findGenericMethodOn(lowerer, base, member.name.text)) {
|
|
1403
|
+
const implicit = implicitAnyParamSymbolsOf(lowerer, member);
|
|
1414
1404
|
if (implicit) {
|
|
1415
1405
|
genericMethods.set(member.name.text, {
|
|
1416
1406
|
decl: member,
|
|
@@ -1423,12 +1413,12 @@ mixin) {
|
|
|
1423
1413
|
continue;
|
|
1424
1414
|
}
|
|
1425
1415
|
}
|
|
1426
|
-
const { shapes, funcType: ft } =
|
|
1416
|
+
const { shapes, funcType: ft } = lowerer.lambdaSignature(member);
|
|
1427
1417
|
if (fields.has(mName)) {
|
|
1428
|
-
|
|
1418
|
+
lowerer.unsupported("SC1090", member.name, "methods shadowing inherited fields");
|
|
1429
1419
|
}
|
|
1430
|
-
if (findGenericMethodOn(
|
|
1431
|
-
|
|
1420
|
+
if (findGenericMethodOn(lowerer, base, mName)) {
|
|
1421
|
+
lowerer.unsupported("SC1090", member.name, `overriding the inherited generic method '${mName}' with a non-generic method (generic methods dispatch statically and would never reach this override)`);
|
|
1432
1422
|
}
|
|
1433
1423
|
// Symbol-slot return refinement, INHERITED slots (own ctor-declared
|
|
1434
1424
|
// slots refine in the post-scan pass below — the constructor hasn't
|
|
@@ -1436,7 +1426,7 @@ mixin) {
|
|
|
1436
1426
|
// symbolFields/fields). Doing it before the exactness check keeps
|
|
1437
1427
|
// a derived override of a refined base method agreeing.
|
|
1438
1428
|
if (ft.ret.kind === "dyn") {
|
|
1439
|
-
const refined = symbolSlotReturnType(
|
|
1429
|
+
const refined = symbolSlotReturnType(lowerer, member, symbolFields, fields);
|
|
1440
1430
|
if (refined)
|
|
1441
1431
|
ft.ret = refined;
|
|
1442
1432
|
}
|
|
@@ -1448,18 +1438,18 @@ mixin) {
|
|
|
1448
1438
|
// ABI types only (not modes) is deliberate: call sites complete
|
|
1449
1439
|
// against the STATIC receiver's shape, so `m(x?: number)` and
|
|
1450
1440
|
// `m(x: number | undefined)` interchange soundly in overrides.
|
|
1451
|
-
const overridden =
|
|
1441
|
+
const overridden = lowerer.findMethodOn(base, mName);
|
|
1452
1442
|
if (overridden?.declarer.builtinError) {
|
|
1453
1443
|
// Error.prototype.toString is a runtime implementation with no
|
|
1454
1444
|
// vtable slot — calls to it are direct, so an override could
|
|
1455
1445
|
// never be reached through a base-typed receiver.
|
|
1456
|
-
|
|
1446
|
+
lowerer.unsupported("SC1090", member.name, `overriding the builtin Error method '${mName}'`);
|
|
1457
1447
|
}
|
|
1458
1448
|
if (overridden &&
|
|
1459
1449
|
(overridden.sig.params.length !== shapes.length ||
|
|
1460
1450
|
!overridden.sig.params.every((p, i) => typeEquals(p.type, shapes[i].type)) ||
|
|
1461
1451
|
!typeEquals(overridden.sig.ret, ft.ret))) {
|
|
1462
|
-
|
|
1452
|
+
lowerer.unsupported("SC1090", member.name, "overriding a method with a different signature (parameter and return types must match the base declaration exactly)");
|
|
1463
1453
|
}
|
|
1464
1454
|
const asyncMember = member.modifiers?.some((m) => m.kind === ts.SyntaxKind.AsyncKeyword) === true;
|
|
1465
1455
|
// Async methods dispatch STATICALLY (the body enters through its
|
|
@@ -1467,7 +1457,7 @@ mixin) {
|
|
|
1467
1457
|
// an override chain touching an async method on either end would
|
|
1468
1458
|
// put a spawn wrapper behind a virtual slot, so it fences.
|
|
1469
1459
|
if (overridden && (asyncMember || overridden.sig.async === true)) {
|
|
1470
|
-
|
|
1460
|
+
lowerer.unsupported("SC1090", member.name, `overriding ${overridden.sig.async === true ? "the async method" : "a method with an async method"} '${mName}' (async methods dispatch statically — the vtable slot machinery has no fiber-spawn story)`);
|
|
1471
1461
|
}
|
|
1472
1462
|
// A #private GENERATOR method carries its channels on the sig:
|
|
1473
1463
|
// the body lowers as a generator IrFunction (`this` as param 0),
|
|
@@ -1475,7 +1465,7 @@ mixin) {
|
|
|
1475
1465
|
// emitted gen-spawn wrapper, answering the suspended generator.
|
|
1476
1466
|
if (member.asteriskToken !== undefined) {
|
|
1477
1467
|
if (ft.ret.kind !== "generator")
|
|
1478
|
-
|
|
1468
|
+
lowerer.badType(member.name, lowerer.typeOf(member.name));
|
|
1479
1469
|
methods.set(mName, { params: shapes, ret: ft.ret, gen: { yieldT: ft.ret.yieldT, nextT: ft.ret.nextT } });
|
|
1480
1470
|
}
|
|
1481
1471
|
else {
|
|
@@ -1498,7 +1488,7 @@ mixin) {
|
|
|
1498
1488
|
// #private accessors collect as "get:#x"/"set:#x" — the same
|
|
1499
1489
|
// reserved spelling, one more unspellable segment.
|
|
1500
1490
|
if (!ts.isIdentifier(member.name) && !ts.isPrivateIdentifier(member.name)) {
|
|
1501
|
-
|
|
1491
|
+
lowerer.unsupported("SC1090", member, "computed accessor names");
|
|
1502
1492
|
}
|
|
1503
1493
|
// ABSTRACT accessors are the abstract-method story with property
|
|
1504
1494
|
// syntax: body-less by definition, they enter `methods` (marked
|
|
@@ -1506,23 +1496,23 @@ mixin) {
|
|
|
1506
1496
|
// and override exactness verbatim; no module function exists.
|
|
1507
1497
|
const abstractAccessor = ts.getModifiers(member)?.some((m) => m.kind === ts.SyntaxKind.AbstractKeyword) === true;
|
|
1508
1498
|
if (!member.body && !abstractAccessor)
|
|
1509
|
-
|
|
1499
|
+
lowerer.unsupported("SC1090", member, "bodyless accessors");
|
|
1510
1500
|
const prop = member.name.text;
|
|
1511
1501
|
const mName = `${isGet ? "get" : "set"}:${prop}`;
|
|
1512
1502
|
if (fields.has(prop)) {
|
|
1513
1503
|
// tsc rejects field/accessor mixing (TS2610/2611); defensive.
|
|
1514
|
-
|
|
1504
|
+
lowerer.unsupported("SC1090", member.name, "accessors sharing a name with a field");
|
|
1515
1505
|
}
|
|
1516
1506
|
let sig;
|
|
1517
1507
|
if (isGet) {
|
|
1518
|
-
const ret =
|
|
1508
|
+
const ret = lowerer.declaredReturnType(member, member.name);
|
|
1519
1509
|
if (ret.kind === "void")
|
|
1520
|
-
|
|
1510
|
+
lowerer.badType(member.name, lowerer.typeOf(member.name));
|
|
1521
1511
|
sig = { params: [], ret };
|
|
1522
1512
|
}
|
|
1523
1513
|
else {
|
|
1524
1514
|
// tsc rejects optional/default/rest setter params (TS1051-53).
|
|
1525
|
-
sig = { params:
|
|
1515
|
+
sig = { params: lowerer.paramShapes(member.parameters), ret: VOID };
|
|
1526
1516
|
}
|
|
1527
1517
|
// One property, ONE type: tsc (5.1+) admits get/set pairs with
|
|
1528
1518
|
// unrelated annotated types; a property slot here has a single
|
|
@@ -1531,16 +1521,16 @@ mixin) {
|
|
|
1531
1521
|
const twinType = twin ? (isGet ? twin.params[0].type : twin.ret) : null;
|
|
1532
1522
|
const ownType = isGet ? sig.ret : sig.params[0].type;
|
|
1533
1523
|
if (twinType && !typeEquals(twinType, ownType)) {
|
|
1534
|
-
|
|
1524
|
+
lowerer.unsupported("SC1090", member.name, `getter/setter pairs with different types (the property '${prop}' must have one type)`);
|
|
1535
1525
|
}
|
|
1536
1526
|
// Same exactness rule as methods — an accessor override keeps
|
|
1537
1527
|
// the overridden accessor's type (getter return / setter param).
|
|
1538
|
-
const overridden =
|
|
1528
|
+
const overridden = lowerer.findMethodOn(base, mName);
|
|
1539
1529
|
if (overridden &&
|
|
1540
1530
|
(overridden.sig.params.length !== sig.params.length ||
|
|
1541
1531
|
!overridden.sig.params.every((p, i) => typeEquals(p.type, sig.params[i].type)) ||
|
|
1542
1532
|
!typeEquals(overridden.sig.ret, sig.ret))) {
|
|
1543
|
-
|
|
1533
|
+
lowerer.unsupported("SC1090", member.name, "overriding an accessor with a different type (the property type must match the base declaration exactly)");
|
|
1544
1534
|
}
|
|
1545
1535
|
methods.set(mName, abstractAccessor ? { ...sig, abstract: true } : sig);
|
|
1546
1536
|
// Abstract accessors stay OUT of accessorNodes: they are erased
|
|
@@ -1551,10 +1541,10 @@ mixin) {
|
|
|
1551
1541
|
accessorNodes.set(mName, member);
|
|
1552
1542
|
}
|
|
1553
1543
|
else if (ts.isIndexSignatureDeclaration(member)) {
|
|
1554
|
-
|
|
1544
|
+
lowerer.unsupported("SC1090", member, "index signatures");
|
|
1555
1545
|
}
|
|
1556
1546
|
else if (!ts.isSemicolonClassElement(member)) {
|
|
1557
|
-
|
|
1547
|
+
lowerer.unsupported("SC1090", member, `syntax '${ts.SyntaxKind[member.kind]}'`);
|
|
1558
1548
|
}
|
|
1559
1549
|
}
|
|
1560
1550
|
// Parameter properties join the shape FIRST among own fields —
|
|
@@ -1605,7 +1595,7 @@ mixin) {
|
|
|
1605
1595
|
declared.kind !== "map" && declared.kind !== "date" && declared.kind !== "generator" &&
|
|
1606
1596
|
declared.kind !== "void" &&
|
|
1607
1597
|
declared.kind !== "caught";
|
|
1608
|
-
const armed = armable ?
|
|
1598
|
+
const armed = armable ? lowerer.withUndefinedArm(declared) : null;
|
|
1609
1599
|
if (armed !== null && armed.kind === "union") {
|
|
1610
1600
|
fields.set(f.name, armed);
|
|
1611
1601
|
const fo = fieldOrder.find((x) => x.name === f.name);
|
|
@@ -1614,7 +1604,7 @@ mixin) {
|
|
|
1614
1604
|
deferredInitFields.add(f.name);
|
|
1615
1605
|
continue;
|
|
1616
1606
|
}
|
|
1617
|
-
|
|
1607
|
+
lowerer.unsupported("SC1090", f.node, f.why);
|
|
1618
1608
|
}
|
|
1619
1609
|
}
|
|
1620
1610
|
// Partial overrides of an inherited accessor pair. JS gives the
|
|
@@ -1636,13 +1626,13 @@ mixin) {
|
|
|
1636
1626
|
// no accessor pair to shadow, so no throwing setter to
|
|
1637
1627
|
// synthesize (tsc makes an instantiable class implement it, and
|
|
1638
1628
|
// that implementation shadows nothing either).
|
|
1639
|
-
const baseSet =
|
|
1629
|
+
const baseSet = lowerer.findMethodOn(base, `set:${prop}`);
|
|
1640
1630
|
if (baseSet && baseSet.sig.abstract !== true) {
|
|
1641
1631
|
if (!typeEquals(baseSet.sig.params[0].type, methods.get(mName).ret)) {
|
|
1642
1632
|
// Unreachable when the base pair agrees (induction through
|
|
1643
1633
|
// the exactness rule); a base setter-only + new getter of a
|
|
1644
1634
|
// different type would break the slot signature.
|
|
1645
|
-
|
|
1635
|
+
lowerer.unsupported("SC1090", node.name, "accessors whose getter and inherited setter types differ");
|
|
1646
1636
|
}
|
|
1647
1637
|
methods.set(`set:${prop}`, { params: [baseSet.sig.params[0]], ret: VOID });
|
|
1648
1638
|
throwingSetters.push(prop);
|
|
@@ -1650,9 +1640,9 @@ mixin) {
|
|
|
1650
1640
|
}
|
|
1651
1641
|
if (mName.startsWith("set:") && !accessorNodes.has(`get:${prop}`)) {
|
|
1652
1642
|
// The abstract-inherited-getter case is the same erasure story.
|
|
1653
|
-
const baseGet =
|
|
1643
|
+
const baseGet = lowerer.findMethodOn(base, `get:${prop}`);
|
|
1654
1644
|
if (baseGet && baseGet.sig.abstract !== true) {
|
|
1655
|
-
|
|
1645
|
+
lowerer.unsupported("SC1090", node.name, `overriding only the setter of an inherited accessor pair (JS shadows the inherited getter — reads of '${prop}' would yield undefined; declare the getter too)`);
|
|
1656
1646
|
}
|
|
1657
1647
|
}
|
|
1658
1648
|
}
|
|
@@ -1672,8 +1662,8 @@ mixin) {
|
|
|
1672
1662
|
// Named classes (declarations and self-binding expressions) resolve
|
|
1673
1663
|
// by name; the nameless default-export declaration by its module's
|
|
1674
1664
|
// default-export symbol.
|
|
1675
|
-
const classSym = decl.name ?
|
|
1676
|
-
const instType = classSym ?
|
|
1665
|
+
const classSym = decl.name ? lowerer.checker.getSymbolAtLocation(decl.name) : ts.isClassDeclaration(decl) ? declSymbolOf(lowerer, decl) : undefined;
|
|
1666
|
+
const instType = classSym ? lowerer.checker.getDeclaredTypeOfSymbol(classSym) : undefined;
|
|
1677
1667
|
// LATE-BOUND properties (the checker's `__@name@id` spelling —
|
|
1678
1668
|
// `this[kLimit] = v` where kLimit is a unique symbol const) by
|
|
1679
1669
|
// their KEY symbol: the scan below needs the checker's property
|
|
@@ -1682,10 +1672,10 @@ mixin) {
|
|
|
1682
1672
|
// answers null on element-access declaration sites, so the link
|
|
1683
1673
|
// goes through the key.
|
|
1684
1674
|
const lateBoundByKey = new Map();
|
|
1685
|
-
for (const p of instType ?
|
|
1675
|
+
for (const p of instType ? lowerer.checker.getPropertiesOfType(instType) : []) {
|
|
1686
1676
|
if (!p.name.startsWith("__@"))
|
|
1687
1677
|
continue;
|
|
1688
|
-
const keySym = lateBoundKeySymOf(
|
|
1678
|
+
const keySym = lateBoundKeySymOf(lowerer, p);
|
|
1689
1679
|
if (keySym)
|
|
1690
1680
|
lateBoundByKey.set(keySym, p);
|
|
1691
1681
|
}
|
|
@@ -1704,20 +1694,20 @@ mixin) {
|
|
|
1704
1694
|
// inherited) are writes, not declarations.
|
|
1705
1695
|
if (fields.has(name))
|
|
1706
1696
|
continue;
|
|
1707
|
-
if (methods.has(name) ||
|
|
1708
|
-
|
|
1697
|
+
if (methods.has(name) || lowerer.findMethodOn(base, name)) {
|
|
1698
|
+
lowerer.unsupported("SC1090", assign, "constructor-assigned fields shadowing methods");
|
|
1709
1699
|
}
|
|
1710
|
-
const sym =
|
|
1711
|
-
const t = sym ?
|
|
1700
|
+
const sym = lowerer.checker.getSymbolAtLocation(assign);
|
|
1701
|
+
const t = sym ? lowerer.checker.getTypeOfSymbol(sym) : undefined;
|
|
1712
1702
|
// Implicit-any fields (assigned from UNTYPED ctor params —
|
|
1713
1703
|
// countdown.js's `this.limit = limit`) take the JS checked-
|
|
1714
1704
|
// dynamic fallback like every JS binding: the slot holds a dyn
|
|
1715
1705
|
// box, reads validate per use, writes convert in (dynFrom).
|
|
1716
1706
|
// TS-annotated `unknown` fields keep their fence (KEEP NARROW
|
|
1717
1707
|
// applies where an annotation could say better).
|
|
1718
|
-
let type = t ? (
|
|
1708
|
+
let type = t ? (lowerer.mapTypeOf(t) ?? dynFallbackType(lowerer, assign, t)) : null;
|
|
1719
1709
|
if (!type || type.kind === "void")
|
|
1720
|
-
|
|
1710
|
+
lowerer.badType(assign, t ?? lowerer.typeOf(assign));
|
|
1721
1711
|
// A JSDoc claim the BODY contradicts (`@type {Command}`
|
|
1722
1712
|
// assigned `undefined` — the lazy-init idiom): the
|
|
1723
1713
|
// representation follows the body — the field widens to the
|
|
@@ -1726,13 +1716,13 @@ mixin) {
|
|
|
1726
1716
|
// Trust-but-verify: the claim never silently narrows the
|
|
1727
1717
|
// runtime value.
|
|
1728
1718
|
{
|
|
1729
|
-
const rhsT =
|
|
1719
|
+
const rhsT = lowerer.typeOf(stmt.expression.right);
|
|
1730
1720
|
const assignsUndef = (rhsT.flags & ts.TypeFlags.Undefined) !== 0;
|
|
1731
1721
|
const admitsUndef = type.kind === "dyn" ||
|
|
1732
1722
|
isUnitType(type) ||
|
|
1733
|
-
(type.kind === "union" &&
|
|
1723
|
+
(type.kind === "union" && lowerer.armTag(type.unionId, UNDEFINED_T) >= 0);
|
|
1734
1724
|
if (assignsUndef && !admitsUndef) {
|
|
1735
|
-
const widened =
|
|
1725
|
+
const widened = lowerer.withUndefinedArmOf(type);
|
|
1736
1726
|
if (widened !== null)
|
|
1737
1727
|
type = widened;
|
|
1738
1728
|
}
|
|
@@ -1752,7 +1742,7 @@ mixin) {
|
|
|
1752
1742
|
// late-bound fence below.
|
|
1753
1743
|
if (lhs && ts.isElementAccessExpression(lhs) &&
|
|
1754
1744
|
lhs.expression.kind === ts.SyntaxKind.ThisKeyword) {
|
|
1755
|
-
const key = uniqueSymbolKeyOf(
|
|
1745
|
+
const key = uniqueSymbolKeyOf(lowerer, lhs.argumentExpression);
|
|
1756
1746
|
if (!key)
|
|
1757
1747
|
continue;
|
|
1758
1748
|
// A key already declared (own or inherited) makes later
|
|
@@ -1762,7 +1752,7 @@ mixin) {
|
|
|
1762
1752
|
if (fields.has(key.fieldName)) {
|
|
1763
1753
|
// Two DISTINCT Symbol(...) consts with one description in
|
|
1764
1754
|
// one layout would need one printable name for two slots.
|
|
1765
|
-
|
|
1755
|
+
lowerer.unsupported("SC1090", lhs, `distinct symbol keys sharing the printable name '${key.fieldName}' in one class`);
|
|
1766
1756
|
}
|
|
1767
1757
|
const propSym = lateBoundByKey.get(key.sym);
|
|
1768
1758
|
// tsgo does not synthesize the late-bound `__@name@id`
|
|
@@ -1775,13 +1765,13 @@ mixin) {
|
|
|
1775
1765
|
? stmt.expression.right
|
|
1776
1766
|
: undefined;
|
|
1777
1767
|
const t = propSym
|
|
1778
|
-
?
|
|
1768
|
+
? lowerer.checker.getTypeOfSymbol(propSym)
|
|
1779
1769
|
: rhs
|
|
1780
|
-
?
|
|
1770
|
+
? lowerer.checker.getBaseTypeOfLiteralType(lowerer.checker.getTypeAtLocation(rhs))
|
|
1781
1771
|
: undefined;
|
|
1782
|
-
const type = t ? (
|
|
1772
|
+
const type = t ? (lowerer.mapTypeOf(t) ?? dynFallbackType(lowerer, lhs, t)) : null;
|
|
1783
1773
|
if (!type || type.kind === "void")
|
|
1784
|
-
|
|
1774
|
+
lowerer.badType(lhs, t ?? lowerer.typeOf(lhs));
|
|
1785
1775
|
fields.set(key.fieldName, type);
|
|
1786
1776
|
symbolFields.set(key.sym, key.fieldName);
|
|
1787
1777
|
fieldOrder.push({ name: key.fieldName, type, initializer: undefined });
|
|
@@ -1793,14 +1783,14 @@ mixin) {
|
|
|
1793
1783
|
// computed keys — is undefined until its first write, which these
|
|
1794
1784
|
// static layouts cannot represent. Named fence, at the first
|
|
1795
1785
|
// assignment site.
|
|
1796
|
-
for (const p of instType ?
|
|
1786
|
+
for (const p of instType ? lowerer.checker.getPropertiesOfType(instType) : []) {
|
|
1797
1787
|
if (fields.has(p.name) || methods.has(p.name))
|
|
1798
1788
|
continue;
|
|
1799
1789
|
if (methods.has(`get:${p.name}`) || methods.has(`set:${p.name}`))
|
|
1800
1790
|
continue;
|
|
1801
|
-
if (base && (base.fields.has(p.name) ||
|
|
1791
|
+
if (base && (base.fields.has(p.name) || lowerer.findMethodOn(base, p.name)))
|
|
1802
1792
|
continue;
|
|
1803
|
-
const site =
|
|
1793
|
+
const site = lowerer.checker.declarationsOf(p).find((d) => ts.isPropertyAccessExpression(d) || ts.isBinaryExpression(d) ||
|
|
1804
1794
|
ts.isElementAccessExpression(d));
|
|
1805
1795
|
if (!site)
|
|
1806
1796
|
continue;
|
|
@@ -1811,10 +1801,10 @@ mixin) {
|
|
|
1811
1801
|
// descriptions) or assignments outside the constructor's top
|
|
1812
1802
|
// level.
|
|
1813
1803
|
if (p.name.startsWith("__@")) {
|
|
1814
|
-
const keySym = lateBoundKeySymOf(
|
|
1804
|
+
const keySym = lateBoundKeySymOf(lowerer, p);
|
|
1815
1805
|
if (keySym && symbolFields.has(keySym))
|
|
1816
1806
|
continue;
|
|
1817
|
-
|
|
1807
|
+
lowerer.unsupported("SC1090", site, "symbol-keyed class fields outside the supported form (a module-level `const k = Symbol('desc')` key, assigned unconditionally at the top of the constructor)");
|
|
1818
1808
|
}
|
|
1819
1809
|
// JS classes: a property first assigned in a method or a
|
|
1820
1810
|
// conditional constructor position holds `undefined` until the
|
|
@@ -1827,14 +1817,14 @@ mixin) {
|
|
|
1827
1817
|
// TypeScript classes keep the loud fence too: an annotated
|
|
1828
1818
|
// program can spell `T | undefined` itself.
|
|
1829
1819
|
if (isJsSourceFile(decl.getSourceFile())) {
|
|
1830
|
-
const armed = undefArmedFieldType(
|
|
1820
|
+
const armed = undefArmedFieldType(lowerer, p);
|
|
1831
1821
|
if (armed !== null) {
|
|
1832
1822
|
fields.set(p.name, armed);
|
|
1833
1823
|
fieldOrder.push({ name: p.name, type: armed, initializer: undefined });
|
|
1834
1824
|
continue;
|
|
1835
1825
|
}
|
|
1836
1826
|
}
|
|
1837
|
-
|
|
1827
|
+
lowerer.unsupported("SC1090", site, `fields assigned outside the constructor's top level ('this.${p.name}' would be undefined until the first assignment runs — assign it unconditionally at the top of the constructor)`);
|
|
1838
1828
|
}
|
|
1839
1829
|
}
|
|
1840
1830
|
// Second refinement chance, OWN symbol slots: the member loop ran
|
|
@@ -1845,7 +1835,7 @@ mixin) {
|
|
|
1845
1835
|
const sig = methods.get(mName);
|
|
1846
1836
|
if (!sig || sig.ret.kind !== "dyn")
|
|
1847
1837
|
continue;
|
|
1848
|
-
const refined = symbolSlotReturnType(
|
|
1838
|
+
const refined = symbolSlotReturnType(lowerer, node, symbolFields, fields);
|
|
1849
1839
|
if (refined)
|
|
1850
1840
|
sig.ret = refined;
|
|
1851
1841
|
}
|
|
@@ -1857,15 +1847,15 @@ mixin) {
|
|
|
1857
1847
|
// parameter never materializes. A rest constructor in a mixin that
|
|
1858
1848
|
// is NOT the pure forwarding shape has no static story — named
|
|
1859
1849
|
// fence, never a mis-typed array.
|
|
1860
|
-
const mixinForwarding = mixin !== undefined && ctor !== null && mixinForwardingCtor(
|
|
1850
|
+
const mixinForwarding = mixin !== undefined && ctor !== null && mixinForwardingCtor(lowerer, ctor);
|
|
1861
1851
|
if (mixin && ctor && !mixinForwarding && ctor.parameters.some((p) => p.dotDotDotToken)) {
|
|
1862
|
-
|
|
1852
|
+
lowerer.unsupported("SC1090", ctor.parameters.find((p) => p.dotDotDotToken), "mixin constructors whose rest parameter does anything but forward (`super(...args)` as the first statement is the compiled shape)");
|
|
1863
1853
|
}
|
|
1864
1854
|
// Constructor omitted on a derived class: it inherits the base's
|
|
1865
1855
|
// (tsc types `new Derived(...)` against the inherited signature; the
|
|
1866
1856
|
// synthesized constructor forwards the same params to super).
|
|
1867
1857
|
const ctorParams = ctor && !mixinForwarding
|
|
1868
|
-
?
|
|
1858
|
+
? lowerer.paramShapes(ctor.parameters)
|
|
1869
1859
|
: (base?.ctorParams ?? []);
|
|
1870
1860
|
const info = {
|
|
1871
1861
|
def: {
|
|
@@ -1944,9 +1934,9 @@ mixin) {
|
|
|
1944
1934
|
if (familyMode) {
|
|
1945
1935
|
const typeParams = [];
|
|
1946
1936
|
for (const tp of decl.typeParameters) {
|
|
1947
|
-
const sym =
|
|
1937
|
+
const sym = lowerer.checker.getSymbolAtLocation(tp.name);
|
|
1948
1938
|
if (!sym)
|
|
1949
|
-
|
|
1939
|
+
lowerer.unsupported("SC1090", tp, "this type parameter form");
|
|
1950
1940
|
typeParams.push(sym);
|
|
1951
1941
|
}
|
|
1952
1942
|
info.generic = {
|
|
@@ -1956,11 +1946,11 @@ mixin) {
|
|
|
1956
1946
|
family: info,
|
|
1957
1947
|
instances: new Map(),
|
|
1958
1948
|
};
|
|
1959
|
-
|
|
1949
|
+
lowerer.genericClassByDecl.set(decl, info.generic);
|
|
1960
1950
|
}
|
|
1961
1951
|
if (base)
|
|
1962
1952
|
base.subclasses.push(info);
|
|
1963
|
-
|
|
1953
|
+
lowerer.classes.set(className, info);
|
|
1964
1954
|
// A NAMED class binds its name (declarations in their scope, class
|
|
1965
1955
|
// expressions inside their own bodies — tsc resolves both to this
|
|
1966
1956
|
// symbol); a nameless default-export declaration binds its module's
|
|
@@ -1971,14 +1961,14 @@ mixin) {
|
|
|
1971
1961
|
// classes fence at their use sites).
|
|
1972
1962
|
const classSymbol = inst || mixin
|
|
1973
1963
|
? undefined
|
|
1974
|
-
: decl.name ?
|
|
1964
|
+
: decl.name ? lowerer.checker.getSymbolAtLocation(decl.name) : ts.isClassDeclaration(decl) ? declSymbolOf(lowerer, decl) : undefined;
|
|
1975
1965
|
if (classSymbol)
|
|
1976
|
-
|
|
1966
|
+
lowerer.classBySymbol.set(classSymbol, info);
|
|
1977
1967
|
// Static-field storage registers with the module's globals only
|
|
1978
1968
|
// once the whole shape collected (a poisoned class never leaves a
|
|
1979
1969
|
// half-registered global behind).
|
|
1980
1970
|
for (const f of staticFields) {
|
|
1981
|
-
|
|
1971
|
+
lowerer.globalsList.push({ id: f.globalId, name: `${info.def.jsName ?? className}.${f.name}`, type: f.type, mutable: !f.readonly });
|
|
1982
1972
|
}
|
|
1983
1973
|
}
|
|
1984
1974
|
}
|
|
@@ -1992,15 +1982,15 @@ mixin) {
|
|
|
1992
1982
|
* unmapped, the fenced-JS-class story. Null answers (unmappable type
|
|
1993
1983
|
* arguments, the instance cap, an uncollected family) make the whole
|
|
1994
1984
|
* reference unmappable — per-site diagnostics own the fence. */
|
|
1995
|
-
export function genericClassInstanceType(
|
|
1996
|
-
const gci =
|
|
1985
|
+
export function genericClassInstanceType(lowerer, decl, ref) {
|
|
1986
|
+
const gci = lowerer.genericClassByDecl.get(decl);
|
|
1997
1987
|
if (!gci) {
|
|
1998
1988
|
// The family never collected (a deferred/poisoned declaration): the
|
|
1999
1989
|
// pre-generics answer — the class's own name, unregistered, so dead
|
|
2000
1990
|
// storage prunes (typeNamesUnregisteredClass) and live references
|
|
2001
1991
|
// flush the declaration's deferred diagnostics (moduleArtifacts /
|
|
2002
1992
|
// the validator backstop). Exactly the fenced-class story.
|
|
2003
|
-
return { kind: "object", className:
|
|
1993
|
+
return { kind: "object", className: lowerer.classNamer(decl) };
|
|
2004
1994
|
}
|
|
2005
1995
|
// A degenerate reference collapses to the FAMILY's object type instead
|
|
2006
1996
|
// of going unmapped: `Box<any>` under a static build, wilder arguments
|
|
@@ -2015,11 +2005,11 @@ export function genericClassInstanceType(L, decl, ref) {
|
|
|
2015
2005
|
const familyT = { kind: "object", className: gci.family.def.name };
|
|
2016
2006
|
// The checker appends `this` (and outer type parameters) to
|
|
2017
2007
|
// getTypeArguments — only the declaration's own count participates.
|
|
2018
|
-
const args =
|
|
2008
|
+
const args = lowerer.checker.getTypeArguments(ref).slice(0, gci.typeParams.length);
|
|
2019
2009
|
const mapped = [];
|
|
2020
2010
|
if (args.length === gci.typeParams.length) {
|
|
2021
2011
|
for (const a of args) {
|
|
2022
|
-
const m =
|
|
2012
|
+
const m = lowerer.mapTypeOf(a);
|
|
2023
2013
|
if (!m || m.kind === "void") {
|
|
2024
2014
|
// An UNBOUND type parameter argument (`Box<T>` outside any
|
|
2025
2015
|
// instantiation) stays honestly unmapped — nothing concrete is
|
|
@@ -2035,7 +2025,7 @@ export function genericClassInstanceType(L, decl, ref) {
|
|
|
2035
2025
|
// Inside an instantiation the CURRENT bindings are the arguments;
|
|
2036
2026
|
// anywhere else the reference is honestly unmappable.
|
|
2037
2027
|
for (const tp of gci.typeParams) {
|
|
2038
|
-
const b =
|
|
2028
|
+
const b = lowerer.typeParamBindings?.get(tp);
|
|
2039
2029
|
if (!b)
|
|
2040
2030
|
return null;
|
|
2041
2031
|
mapped.push(b);
|
|
@@ -2045,7 +2035,7 @@ export function genericClassInstanceType(L, decl, ref) {
|
|
|
2045
2035
|
const existing = gci.instances.get(key);
|
|
2046
2036
|
if (existing) {
|
|
2047
2037
|
if (existing.info)
|
|
2048
|
-
|
|
2038
|
+
lowerer.noteGenericClassInstanceDemand(existing.info);
|
|
2049
2039
|
return existing.poisoned ? null : { kind: "object", className: existing.name };
|
|
2050
2040
|
}
|
|
2051
2041
|
// The generic-fn cap, same rationale (polymorphic recursion through
|
|
@@ -2060,14 +2050,14 @@ export function genericClassInstanceType(L, decl, ref) {
|
|
|
2060
2050
|
gci.instances.set(key, entry);
|
|
2061
2051
|
const bindings = new Map();
|
|
2062
2052
|
gci.typeParams.forEach((tp, i) => bindings.set(tp, mapped[i]));
|
|
2063
|
-
const rendered = mapped.map((m) =>
|
|
2053
|
+
const rendered = mapped.map((m) => lowerer.fmt(m)).join(", ");
|
|
2064
2054
|
const typeArgsText = `<${rendered.length > 80 ? rendered.slice(0, 77) + "..." : rendered}>`;
|
|
2065
|
-
const prevBindings =
|
|
2066
|
-
const prevContext =
|
|
2067
|
-
|
|
2068
|
-
|
|
2055
|
+
const prevBindings = lowerer.typeParamBindings;
|
|
2056
|
+
const prevContext = lowerer.instantiationContext;
|
|
2057
|
+
lowerer.typeParamBindings = bindings;
|
|
2058
|
+
lowerer.instantiationContext = `instantiating class '${gci.baseName}' with ${typeArgsText}`;
|
|
2069
2059
|
try {
|
|
2070
|
-
|
|
2060
|
+
lowerer.collectClassShapeInner(decl, undefined, { family: gci.family, name, bindings, typeArgsText, ordinal });
|
|
2071
2061
|
}
|
|
2072
2062
|
catch (e) {
|
|
2073
2063
|
// Collection fenced under THESE bindings: the diagnostic (with the
|
|
@@ -2078,18 +2068,18 @@ export function genericClassInstanceType(L, decl, ref) {
|
|
|
2078
2068
|
return null;
|
|
2079
2069
|
}
|
|
2080
2070
|
finally {
|
|
2081
|
-
|
|
2082
|
-
|
|
2071
|
+
lowerer.typeParamBindings = prevBindings;
|
|
2072
|
+
lowerer.instantiationContext = prevContext;
|
|
2083
2073
|
}
|
|
2084
|
-
const info =
|
|
2074
|
+
const info = lowerer.classes.get(name);
|
|
2085
2075
|
if (!info) {
|
|
2086
2076
|
entry.poisoned = true;
|
|
2087
2077
|
return null;
|
|
2088
2078
|
}
|
|
2089
2079
|
entry.info = info;
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2080
|
+
lowerer.genericClassInstances.push(info);
|
|
2081
|
+
lowerer.noteGenericClassInstanceDemand(info);
|
|
2082
|
+
lowerer.onLateClassCollected?.(info);
|
|
2093
2083
|
return { kind: "object", className: name };
|
|
2094
2084
|
}
|
|
2095
2085
|
/** Runs a member-lowering thunk under an INSTANTIATION's type-parameter
|
|
@@ -2098,7 +2088,7 @@ export function genericClassInstanceType(L, decl, ref) {
|
|
|
2098
2088
|
* Coverage counts a generic class's statements once: only the FIRST
|
|
2099
2089
|
* instantiation contributes (the lowerGenericInstance rule). A no-op
|
|
2100
2090
|
* for ordinary classes. */
|
|
2101
|
-
|
|
2091
|
+
function withInstanceBindings(lowerer, info, fn) {
|
|
2102
2092
|
const gi = info.genericInstance;
|
|
2103
2093
|
if (!gi) {
|
|
2104
2094
|
// MIXIN instantiations ride the same mechanism: T (the base
|
|
@@ -2108,37 +2098,37 @@ export function withInstanceBindings(L, info, fn) {
|
|
|
2108
2098
|
const mi = info.mixinInstance;
|
|
2109
2099
|
if (!mi)
|
|
2110
2100
|
return fn();
|
|
2111
|
-
const prevBindings =
|
|
2112
|
-
const prevContext =
|
|
2113
|
-
const prevSuppress =
|
|
2114
|
-
const prevMixinCtx =
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2101
|
+
const prevBindings = lowerer.typeParamBindings;
|
|
2102
|
+
const prevContext = lowerer.instantiationContext;
|
|
2103
|
+
const prevSuppress = lowerer.suppressStats;
|
|
2104
|
+
const prevMixinCtx = lowerer.mixinTypeContext;
|
|
2105
|
+
lowerer.typeParamBindings = mi.bindings;
|
|
2106
|
+
lowerer.instantiationContext = mi.context;
|
|
2107
|
+
lowerer.suppressStats = prevSuppress || mi.ordinal > 0;
|
|
2108
|
+
lowerer.mixinTypeContext = { classNode: info.decl, className: info.def.name };
|
|
2119
2109
|
try {
|
|
2120
2110
|
return fn();
|
|
2121
2111
|
}
|
|
2122
2112
|
finally {
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2113
|
+
lowerer.typeParamBindings = prevBindings;
|
|
2114
|
+
lowerer.instantiationContext = prevContext;
|
|
2115
|
+
lowerer.suppressStats = prevSuppress;
|
|
2116
|
+
lowerer.mixinTypeContext = prevMixinCtx;
|
|
2127
2117
|
}
|
|
2128
2118
|
}
|
|
2129
|
-
const prevBindings =
|
|
2130
|
-
const prevContext =
|
|
2131
|
-
const prevSuppress =
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2119
|
+
const prevBindings = lowerer.typeParamBindings;
|
|
2120
|
+
const prevContext = lowerer.instantiationContext;
|
|
2121
|
+
const prevSuppress = lowerer.suppressStats;
|
|
2122
|
+
lowerer.typeParamBindings = gi.bindings;
|
|
2123
|
+
lowerer.instantiationContext = `instantiating class '${gi.family.generic?.baseName ?? info.def.jsName ?? ""}' with ${gi.typeArgsText}`;
|
|
2124
|
+
lowerer.suppressStats = prevSuppress || gi.ordinal > 0;
|
|
2135
2125
|
try {
|
|
2136
2126
|
return fn();
|
|
2137
2127
|
}
|
|
2138
2128
|
finally {
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2129
|
+
lowerer.typeParamBindings = prevBindings;
|
|
2130
|
+
lowerer.instantiationContext = prevContext;
|
|
2131
|
+
lowerer.suppressStats = prevSuppress;
|
|
2142
2132
|
}
|
|
2143
2133
|
}
|
|
2144
2134
|
/** The `%init` statements for one class's static readonly fields AND
|
|
@@ -2147,18 +2137,18 @@ export function withInstanceBindings(L, info, fn) {
|
|
|
2147
2137
|
* JS evaluates static initializers and blocks. Field failures poison per
|
|
2148
2138
|
* field, like fieldInitStmts; a block lowers as the block statement it
|
|
2149
2139
|
* is, so its statements poison individually inside lowerStmts. */
|
|
2150
|
-
export function lowerStaticFieldInits(
|
|
2140
|
+
export function lowerStaticFieldInits(lowerer, info) {
|
|
2151
2141
|
// Mixin instantiations lower their initializers under the
|
|
2152
2142
|
// instantiation's bindings/context (a no-op for everything else —
|
|
2153
2143
|
// generic FAMILIES own their statics and carry no genericInstance).
|
|
2154
|
-
return withInstanceBindings(
|
|
2144
|
+
return withInstanceBindings(lowerer, info, () => lowerStaticFieldInitsInner(lowerer, info));
|
|
2155
2145
|
}
|
|
2156
|
-
function lowerStaticFieldInitsInner(
|
|
2146
|
+
function lowerStaticFieldInitsInner(lowerer, info) {
|
|
2157
2147
|
// Decoration first: TC39 evaluates decorator expressions, creates the
|
|
2158
2148
|
// class, applies the decorators, and only THEN runs static field
|
|
2159
2149
|
// initializers and static blocks (verified against Node — the
|
|
2160
2150
|
// decorated result is what `this`/the class name mean inside them).
|
|
2161
|
-
const out = [...lowerClassDecoration(
|
|
2151
|
+
const out = [...lowerClassDecoration(lowerer, info)];
|
|
2162
2152
|
const items = [
|
|
2163
2153
|
...info.staticFields.map((f) => ({ pos: f.initializer.getStart(), kind: "field", f })),
|
|
2164
2154
|
...(info.staticBlocks ?? []).map((b) => ({ pos: b.getStart(), kind: "block", b })),
|
|
@@ -2167,38 +2157,26 @@ function lowerStaticFieldInitsInner(L, info) {
|
|
|
2167
2157
|
if (item.kind === "block") {
|
|
2168
2158
|
// The block's body IS a Block statement: lowerStmts scopes its
|
|
2169
2159
|
// let/const like any nested block and poisons per inner statement.
|
|
2170
|
-
out.push(...
|
|
2160
|
+
out.push(...lowerer.lowerStmts([item.b.body]));
|
|
2171
2161
|
continue;
|
|
2172
2162
|
}
|
|
2173
2163
|
const f = item.f;
|
|
2174
|
-
|
|
2175
|
-
|
|
2164
|
+
lowerer.stats.statementsTotal++;
|
|
2165
|
+
lowerer.bumpFileStat(locOf(f.initializer).file, "total");
|
|
2176
2166
|
try {
|
|
2177
2167
|
// `this` in a static field initializer names the CLASS (like a
|
|
2178
2168
|
// static block's), with arrows transparent and this-binding
|
|
2179
2169
|
// function forms opaque — the static-block rule verbatim, named
|
|
2180
2170
|
// here so the generic outside-a-method fence never fires first.
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
ts.isMethodDeclaration(n) || ts.isConstructorDeclaration(n) ||
|
|
2184
|
-
ts.isGetAccessor(n) || ts.isSetAccessor(n) ||
|
|
2185
|
-
ts.isClassDeclaration(n) || ts.isClassExpression(n)) {
|
|
2186
|
-
return;
|
|
2187
|
-
}
|
|
2188
|
-
if (n.kind === ts.SyntaxKind.ThisKeyword || n.kind === ts.SyntaxKind.SuperKeyword) {
|
|
2189
|
-
L.unsupported("SC1090", n, "'this' in static field initializers (it names the class — reference the class by name instead)");
|
|
2190
|
-
}
|
|
2191
|
-
n.forEachChild(checkThis);
|
|
2192
|
-
};
|
|
2193
|
-
checkThis(f.initializer);
|
|
2194
|
-
const value = L.lowerExprExpecting(f.initializer, f.type);
|
|
2171
|
+
rejectStaticThis(lowerer, f.initializer, () => "'this' in static field initializers (it names the class — reference the class by name instead)", true);
|
|
2172
|
+
const value = lowerer.lowerExprExpecting(f.initializer, f.type);
|
|
2195
2173
|
out.push({ kind: "assign", localId: f.globalId, value, loc: locOf(f.initializer) });
|
|
2196
2174
|
}
|
|
2197
2175
|
catch (e) {
|
|
2198
2176
|
if (!(e instanceof PoisonError))
|
|
2199
2177
|
throw e;
|
|
2200
|
-
|
|
2201
|
-
|
|
2178
|
+
lowerer.stats.statementsFailed++;
|
|
2179
|
+
lowerer.bumpFileStat(locOf(f.initializer).file, "failed");
|
|
2202
2180
|
}
|
|
2203
2181
|
}
|
|
2204
2182
|
return out;
|
|
@@ -2220,7 +2198,7 @@ function lowerStaticFieldInitsInner(L, info) {
|
|
|
2220
2198
|
* build time; the runtime base would be the decoration result) and
|
|
2221
2199
|
* namespace-nested declarations (qualified references resolve the class
|
|
2222
2200
|
* directly, not through the rebound value). */
|
|
2223
|
-
export function analyzeClassDecoration(
|
|
2201
|
+
export function analyzeClassDecoration(lowerer, info) {
|
|
2224
2202
|
const cd = info.classDecorators;
|
|
2225
2203
|
if (!cd || cd.shapes !== undefined || cd.poisoned)
|
|
2226
2204
|
return;
|
|
@@ -2243,29 +2221,29 @@ export function analyzeClassDecoration(L, info) {
|
|
|
2243
2221
|
? d.expression
|
|
2244
2222
|
: null;
|
|
2245
2223
|
if (ambientCallee !== null) {
|
|
2246
|
-
const sym =
|
|
2247
|
-
const vdecl = sym && !
|
|
2224
|
+
const sym = lowerer.resolveValueSymbol(ambientCallee);
|
|
2225
|
+
const vdecl = sym && !lowerer.isStdlibSymbol(sym) ? lowerer.checker.declarationsOf(sym)[0] : undefined;
|
|
2248
2226
|
const ambientVar = vdecl !== undefined &&
|
|
2249
2227
|
ts.isVariableDeclaration(vdecl) &&
|
|
2250
2228
|
vdecl.initializer === undefined &&
|
|
2251
2229
|
(ts.getCombinedModifierFlags(vdecl) & ts.ModifierFlags.Ambient) !== 0 &&
|
|
2252
2230
|
!vdecl.getSourceFile().isDeclarationFile;
|
|
2253
|
-
if (ambientVar || ambientUndefinedFnSymbolOf(
|
|
2231
|
+
if (ambientVar || ambientUndefinedFnSymbolOf(lowerer, ambientCallee) !== null) {
|
|
2254
2232
|
shapes.push({ kind: "ambientThrow", name: ambientCallee.text });
|
|
2255
2233
|
continue;
|
|
2256
2234
|
}
|
|
2257
2235
|
}
|
|
2258
|
-
const t =
|
|
2236
|
+
const t = lowerer.typeOf(d.expression);
|
|
2259
2237
|
// Param-count first, off the checker signature: the context-taking
|
|
2260
2238
|
// shape deserves its own name before mapType (whose failure on
|
|
2261
2239
|
// ClassDecoratorContext would blur the story).
|
|
2262
|
-
const sigs =
|
|
2240
|
+
const sigs = lowerer.checker.getCallSignatures(t);
|
|
2263
2241
|
if (sigs.length === 1 && sigs[0].getParameters().length > 1) {
|
|
2264
|
-
|
|
2242
|
+
lowerer.unsupported("SC1090", d, "class decorators that take the standard 'context' parameter (its object — addInitializer, metadata — has no static lowering; single-parameter decorators compile)");
|
|
2265
2243
|
}
|
|
2266
|
-
const mapped =
|
|
2244
|
+
const mapped = lowerer.mapTypeOf(t);
|
|
2267
2245
|
if (!mapped || mapped.kind !== "func" || mapped.rest === true || mapped.params.length > 1) {
|
|
2268
|
-
|
|
2246
|
+
lowerer.unsupported("SC1090", d, "class decorators without one concrete (class) => class-or-void signature ('any'-typed and generic decorators have no compilable call ABI — declare the parameter as the class type)");
|
|
2269
2247
|
}
|
|
2270
2248
|
// The parameter: a classval slot the decorated class's object can
|
|
2271
2249
|
// legally inhabit — the class itself, or a BASE with the same
|
|
@@ -2274,13 +2252,13 @@ export function analyzeClassDecoration(L, info) {
|
|
|
2274
2252
|
const p = mapped.params[0];
|
|
2275
2253
|
const paramOk = p.kind === "classval" &&
|
|
2276
2254
|
(p.className === info.def.name ||
|
|
2277
|
-
(isSubclassOf(
|
|
2255
|
+
(isSubclassOf(lowerer, info.def.name, p.className) &&
|
|
2278
2256
|
(() => {
|
|
2279
|
-
const sup =
|
|
2280
|
-
return sup !== undefined && !sup.generic && ctorAbiEquals(
|
|
2257
|
+
const sup = lowerer.classes.get(p.className);
|
|
2258
|
+
return sup !== undefined && !sup.generic && ctorAbiEquals(lowerer, info, sup);
|
|
2281
2259
|
})()));
|
|
2282
2260
|
if (!paramOk) {
|
|
2283
|
-
|
|
2261
|
+
lowerer.unsupported("SC1090", d, `class decorators whose parameter is not the decorated class ('${display}' cannot flow into a '${lowerer.fmt(p)}' slot — declare the parameter as 'typeof ${display}' or a base class sharing its constructor signature)`);
|
|
2284
2262
|
}
|
|
2285
2263
|
}
|
|
2286
2264
|
// The return: void/undefined keeps the original binding; the class
|
|
@@ -2292,17 +2270,17 @@ export function analyzeClassDecoration(L, info) {
|
|
|
2292
2270
|
const replaces = ret.kind === "classval";
|
|
2293
2271
|
if (replaces) {
|
|
2294
2272
|
const retOk = ret.className === info.def.name ||
|
|
2295
|
-
(isSubclassOf(
|
|
2273
|
+
(isSubclassOf(lowerer, ret.className, info.def.name) &&
|
|
2296
2274
|
(() => {
|
|
2297
|
-
const sub =
|
|
2298
|
-
return sub !== undefined && !sub.generic && ctorAbiEquals(
|
|
2275
|
+
const sub = lowerer.classes.get(ret.className);
|
|
2276
|
+
return sub !== undefined && !sub.generic && ctorAbiEquals(lowerer, sub, info);
|
|
2299
2277
|
})());
|
|
2300
2278
|
if (!retOk) {
|
|
2301
|
-
|
|
2279
|
+
lowerer.unsupported("SC1090", d, `class decorators returning '${lowerer.fmt(ret)}' (a replacement must be '${display}' itself or a subclass sharing its constructor signature — tsc's structural check admits shapes the compiled nominal hierarchy cannot rebind)`);
|
|
2302
2280
|
}
|
|
2303
2281
|
}
|
|
2304
2282
|
else if (ret.kind !== "void") {
|
|
2305
|
-
|
|
2283
|
+
lowerer.unsupported("SC1090", d, `class decorators returning '${lowerer.fmt(ret)}' (supported returns: the decorated class type, a subclass with the same constructor signature, or void)`);
|
|
2306
2284
|
}
|
|
2307
2285
|
shapes.push({ kind: "call", funcType: mapped, replaces });
|
|
2308
2286
|
}
|
|
@@ -2314,13 +2292,13 @@ export function analyzeClassDecoration(L, info) {
|
|
|
2314
2292
|
// and namespace-nested declarations (the qualified-access paths
|
|
2315
2293
|
// resolve the class directly, not through the rebound binding).
|
|
2316
2294
|
if (info.subclasses.length > 0) {
|
|
2317
|
-
|
|
2295
|
+
lowerer.unsupported("SC1090", cd.nodes[0], `class decorators that can replace a class with subclasses ('${info.subclasses[0].def.jsName ?? info.subclasses[0].def.name}' extends '${display}', but the runtime base would be the decoration result — return void, or decorate the leaf classes)`);
|
|
2318
2296
|
}
|
|
2319
2297
|
if (info.decl && !ts.isSourceFile(info.decl.parent)) {
|
|
2320
|
-
|
|
2298
|
+
lowerer.unsupported("SC1090", cd.nodes[0], "class decorators that can replace a namespace-nested class (qualified references resolve the declaration directly — return void, or declare the class at top level)");
|
|
2321
2299
|
}
|
|
2322
2300
|
const globalId = `%g.dec.${info.def.name}`;
|
|
2323
|
-
|
|
2301
|
+
lowerer.globalsList.push({
|
|
2324
2302
|
id: globalId,
|
|
2325
2303
|
name: `${display}.decorated`,
|
|
2326
2304
|
type: { kind: "classval", className: info.def.name },
|
|
@@ -2345,7 +2323,7 @@ export function analyzeClassDecoration(L, info) {
|
|
|
2345
2323
|
* object, each replacing decorator's non-undefined result feeding the
|
|
2346
2324
|
* next application; the final value binds the class name (the mutable
|
|
2347
2325
|
* classval global) when any decorator can replace. */
|
|
2348
|
-
|
|
2326
|
+
function lowerClassDecoration(lowerer, info) {
|
|
2349
2327
|
const cd = info.classDecorators;
|
|
2350
2328
|
if (!cd || cd.poisoned || cd.shapes === undefined || info.decl === null)
|
|
2351
2329
|
return [];
|
|
@@ -2364,16 +2342,16 @@ export function lowerClassDecoration(L, info) {
|
|
|
2364
2342
|
const d = cd.nodes[i];
|
|
2365
2343
|
const shape = cd.shapes[i];
|
|
2366
2344
|
if (shape.kind === "ambientThrow") {
|
|
2367
|
-
stmts.push({ kind: "exprStmt", expr: nsUndefRead(
|
|
2345
|
+
stmts.push({ kind: "exprStmt", expr: nsUndefRead(lowerer, shape.name, d, F64), loc: locOf(d) });
|
|
2368
2346
|
return stmts;
|
|
2369
2347
|
}
|
|
2370
|
-
const value =
|
|
2371
|
-
const local =
|
|
2348
|
+
const value = lowerer.lowerExprExpecting(d.expression, shape.funcType);
|
|
2349
|
+
const local = lowerer.declareHiddenLocal("dec", shape.funcType);
|
|
2372
2350
|
stmts.push({ kind: "varDecl", localId: local.id, init: value, loc: locOf(d) });
|
|
2373
2351
|
temps.push({ localId: local.id, funcType: shape.funcType, replaces: shape.replaces });
|
|
2374
2352
|
}
|
|
2375
2353
|
// 2. Applications, reverse order, over the accumulating class value.
|
|
2376
|
-
let current = classValueRef(
|
|
2354
|
+
let current = classValueRef(lowerer, info, info.decl);
|
|
2377
2355
|
for (let i = temps.length - 1; i >= 0; i--) {
|
|
2378
2356
|
const t = temps[i];
|
|
2379
2357
|
const dLoc = locOf(cd.nodes[i]);
|
|
@@ -2381,16 +2359,16 @@ export function lowerClassDecoration(L, info) {
|
|
|
2381
2359
|
const args = [];
|
|
2382
2360
|
if (t.funcType.params.length === 1) {
|
|
2383
2361
|
const p = t.funcType.params[0];
|
|
2384
|
-
const widened =
|
|
2385
|
-
|
|
2362
|
+
const widened = lowerer.coerceToExpected(current, p);
|
|
2363
|
+
lowerer.requireExactShape(cd.nodes[i], widened.type, p);
|
|
2386
2364
|
args.push(widened);
|
|
2387
2365
|
}
|
|
2388
2366
|
const call = { kind: "callValue", callee, args, type: t.funcType.ret, loc: dLoc };
|
|
2389
2367
|
if (t.replaces) {
|
|
2390
2368
|
const target = { kind: "classval", className: info.def.name };
|
|
2391
|
-
const widened =
|
|
2392
|
-
|
|
2393
|
-
const res =
|
|
2369
|
+
const widened = lowerer.coerceToExpected(call, target);
|
|
2370
|
+
lowerer.requireExactShape(cd.nodes[i], widened.type, target);
|
|
2371
|
+
const res = lowerer.declareHiddenLocal("decres", target);
|
|
2394
2372
|
stmts.push({ kind: "varDecl", localId: res.id, init: widened, loc: dLoc });
|
|
2395
2373
|
current = { kind: "varRef", localId: res.id, type: target, loc: dLoc };
|
|
2396
2374
|
}
|
|
@@ -2407,8 +2385,8 @@ export function lowerClassDecoration(L, info) {
|
|
|
2407
2385
|
catch (e) {
|
|
2408
2386
|
if (!(e instanceof PoisonError))
|
|
2409
2387
|
throw e;
|
|
2410
|
-
|
|
2411
|
-
|
|
2388
|
+
lowerer.stats.statementsFailed++;
|
|
2389
|
+
lowerer.bumpFileStat(loc.file, "failed");
|
|
2412
2390
|
return [];
|
|
2413
2391
|
}
|
|
2414
2392
|
}
|
|
@@ -2416,7 +2394,7 @@ export function lowerClassDecoration(L, info) {
|
|
|
2416
2394
|
* the compile-time prototype-chain walk (`D.x` reads C's global when C
|
|
2417
2395
|
* declared x and nothing between redeclares it; a redeclaration shadows
|
|
2418
2396
|
* with its OWN storage, exactly JS). */
|
|
2419
|
-
export function findStaticOn(
|
|
2397
|
+
export function findStaticOn(lowerer, info, name) {
|
|
2420
2398
|
for (let c = info; c; c = c.base) {
|
|
2421
2399
|
const field = c.staticFields.find((s) => s.name === name);
|
|
2422
2400
|
if (field)
|
|
@@ -2431,15 +2409,15 @@ export function findStaticOn(L, info, name) {
|
|
|
2431
2409
|
* the through-a-VALUE devirtualization test: a classval(info) slot can
|
|
2432
2410
|
* hold any descendant, and a shadowing redeclaration means the runtime
|
|
2433
2411
|
* class decides which storage answers. */
|
|
2434
|
-
export function staticShadowBelow(
|
|
2412
|
+
export function staticShadowBelow(lowerer, info, name) {
|
|
2435
2413
|
return info.subclasses.some((s) => s.staticFields.some((f) => f.name === name) ||
|
|
2436
2414
|
s.staticMethods?.has(name) === true ||
|
|
2437
2415
|
s.genericStatics?.has(name) === true ||
|
|
2438
|
-
staticShadowBelow(
|
|
2416
|
+
staticShadowBelow(lowerer, s, name));
|
|
2439
2417
|
}
|
|
2440
2418
|
/** The nearest GENERIC static declaration of `name` at/above `info` —
|
|
2441
2419
|
* findStaticOn's twin over the genericStatics tables. */
|
|
2442
|
-
export function findGenericStaticOn(
|
|
2420
|
+
export function findGenericStaticOn(lowerer, info, name) {
|
|
2443
2421
|
for (let c = info; c; c = c.base) {
|
|
2444
2422
|
const gs = c.genericStatics?.get(name);
|
|
2445
2423
|
if (gs)
|
|
@@ -2450,16 +2428,16 @@ export function findGenericStaticOn(L, info, name) {
|
|
|
2450
2428
|
/** A static METHOD taken as a value: the zero-capture closure over its
|
|
2451
2429
|
* module function — the declared-function-as-value rule verbatim
|
|
2452
2430
|
* (interned by the backend, so `C.m === C.m` holds). */
|
|
2453
|
-
function staticMethodValue(
|
|
2431
|
+
function staticMethodValue(lowerer, declarer, name, sig, blame, loc) {
|
|
2454
2432
|
const fnName = `%${declarer.def.name}.static:${name}`;
|
|
2455
|
-
|
|
2433
|
+
lowerer.noteEdge(fnName);
|
|
2456
2434
|
const funcType = {
|
|
2457
2435
|
kind: "func",
|
|
2458
2436
|
params: sig.params.filter((p) => p.mode !== "dynRest").map((p) => p.type),
|
|
2459
2437
|
ret: sig.ret,
|
|
2460
2438
|
...(sig.params.some((p) => p.mode === "dynRest") ? { rest: true } : {}),
|
|
2461
2439
|
};
|
|
2462
|
-
|
|
2440
|
+
lowerer.requireExactArityValue(blame, blame, sig.params, funcType);
|
|
2463
2441
|
return { kind: "closure", fnName, captures: [], type: funcType, loc };
|
|
2464
2442
|
}
|
|
2465
2443
|
/** The class itself taken as a VALUE (`const X = C`, an argument, an
|
|
@@ -2470,26 +2448,26 @@ function staticMethodValue(L, declarer, name, sig, blame, loc) {
|
|
|
2470
2448
|
* constructor (Error/EventEmitter/stream chains complete their `new`
|
|
2471
2449
|
* by special rules) — are named fences here. The constructor edge is
|
|
2472
2450
|
* noted at every classRef: a value can always be constructed through. */
|
|
2473
|
-
export function classValueRef(
|
|
2451
|
+
export function classValueRef(lowerer, info, blame) {
|
|
2474
2452
|
const display = info.def.name.replace(/^%|^%m\d+\./, "");
|
|
2475
|
-
fenceDecorationThrows(
|
|
2453
|
+
fenceDecorationThrows(lowerer, info, blame);
|
|
2476
2454
|
if (info.generic) {
|
|
2477
2455
|
// `typeof Box` — the uninstantiated FAMILY as a value: no thunk, no
|
|
2478
2456
|
// single constructor ABI. INSTANTIATIONS have class objects
|
|
2479
2457
|
// (`const B = Box<number>`, `new (v: number) => Box<number>` slots).
|
|
2480
|
-
|
|
2458
|
+
lowerer.unsupported("SC1090", blame, `generic classes as values ('typeof ${display}' keeps the type parameter — instantiation expressions ('${display}<number>') and concrete constructor-typed slots compile)`);
|
|
2481
2459
|
}
|
|
2482
2460
|
if (info.builtinError || info.builtinEmitter || info.builtinStream !== undefined) {
|
|
2483
|
-
|
|
2461
|
+
lowerer.unsupported("SC1090", blame, `builtin classes as values ('${display}' is runtime-provided — reference program-declared classes instead)`);
|
|
2484
2462
|
}
|
|
2485
|
-
if (
|
|
2486
|
-
inheritsBuiltinStreamCtor(
|
|
2463
|
+
if (lowerer.inheritsBuiltinErrorCtor(info) || lowerer.inheritsBuiltinEmitterCtor(info) ||
|
|
2464
|
+
inheritsBuiltinStreamCtor(lowerer, info) ||
|
|
2487
2465
|
(() => { for (let c = info.base; c; c = c.base)
|
|
2488
2466
|
if (c.builtinError || c.builtinEmitter || c.builtinStream !== undefined)
|
|
2489
2467
|
return true; return false; })()) {
|
|
2490
|
-
|
|
2468
|
+
lowerer.unsupported("SC1090", blame, `classes extending builtin bases as values ('${display}' inherits a runtime-provided constructor)`);
|
|
2491
2469
|
}
|
|
2492
|
-
|
|
2470
|
+
lowerer.noteEdge(`%${info.def.name}.constructor`);
|
|
2493
2471
|
return {
|
|
2494
2472
|
kind: "classRef",
|
|
2495
2473
|
className: info.def.name,
|
|
@@ -2502,7 +2480,7 @@ export function classValueRef(L, info, blame) {
|
|
|
2502
2480
|
* constructor signature equals C's (same count, modes, and ABI types),
|
|
2503
2481
|
* which is what keeps newValue completion against C's one signature
|
|
2504
2482
|
* sound for every value legally in the slot. */
|
|
2505
|
-
export function ctorAbiEquals(
|
|
2483
|
+
export function ctorAbiEquals(lowerer, sub, sup) {
|
|
2506
2484
|
const a = sub.ctorParams;
|
|
2507
2485
|
const b = sup.ctorParams;
|
|
2508
2486
|
return a.length === b.length && a.every((p, i) => p.mode === b[i].mode && typeEquals(p.type, b[i].type));
|
|
@@ -2513,18 +2491,18 @@ export function ctorAbiEquals(L, sub, sup) {
|
|
|
2513
2491
|
* compile-time name. Null for everything else — unresolved members
|
|
2514
2492
|
* fall through to the ordinary chain so the static fence or the
|
|
2515
2493
|
* generic member rejection names the site. */
|
|
2516
|
-
export function lowerStaticFieldRead(
|
|
2494
|
+
export function lowerStaticFieldRead(lowerer, expr) {
|
|
2517
2495
|
if (expr.questionDotToken)
|
|
2518
2496
|
return null;
|
|
2519
2497
|
if (!ts.isIdentifier(expr.expression))
|
|
2520
2498
|
return null;
|
|
2521
|
-
const symbol =
|
|
2522
|
-
const info = (symbol ?
|
|
2499
|
+
const symbol = lowerer.resolveValueSymbol(expr.expression);
|
|
2500
|
+
const info = (symbol ? lowerer.classBySymbol.get(symbol) : undefined) ??
|
|
2523
2501
|
// A require binding over `module.exports = class {…}` (the alias
|
|
2524
2502
|
// lands on the expression's own symbol): resolve/collect on demand,
|
|
2525
2503
|
// or `C.name` below would fall through to paths that answer for
|
|
2526
2504
|
// stdlib globals instead of this class.
|
|
2527
|
-
propertyAssignedClassInfoOf(
|
|
2505
|
+
propertyAssignedClassInfoOf(lowerer, symbol) ??
|
|
2528
2506
|
undefined;
|
|
2529
2507
|
if (!info)
|
|
2530
2508
|
return null;
|
|
@@ -2536,27 +2514,27 @@ export function lowerStaticFieldRead(L, expr) {
|
|
|
2536
2514
|
if (info.classDecorators?.valueGlobalId !== undefined)
|
|
2537
2515
|
return null;
|
|
2538
2516
|
const loc = locOf(expr);
|
|
2539
|
-
const found = findStaticOn(
|
|
2517
|
+
const found = findStaticOn(lowerer, info, expr.name.text);
|
|
2540
2518
|
// A #private static resolves only through the DECLARING class's own
|
|
2541
2519
|
// name: in JS the brand lives on that one constructor object, so
|
|
2542
2520
|
// `D.#s` (a subclass receiver) throws Node's TypeError instead of
|
|
2543
2521
|
// reaching up the chain — fenced rather than silently resolved.
|
|
2544
2522
|
if (found && expr.name.text.startsWith("#") && found.declarer !== info) {
|
|
2545
|
-
|
|
2523
|
+
lowerer.unsupported("SC1090", expr, `reading the private static '${expr.name.text}' through the subclass '${info.def.name.replace(/^%|^%m\d+\./, "")}' (JS brands the declaring class object alone — Node throws a TypeError here; spell the declaring class's name)`);
|
|
2546
2524
|
}
|
|
2547
2525
|
if (found?.field !== undefined) {
|
|
2548
|
-
return
|
|
2526
|
+
return lowerer.maybeNarrow({ kind: "varRef", localId: found.field.globalId, type: found.field.type, loc }, expr);
|
|
2549
2527
|
}
|
|
2550
2528
|
if (found) {
|
|
2551
|
-
return staticMethodValue(
|
|
2529
|
+
return staticMethodValue(lowerer, found.declarer, expr.name.text, found.method, expr, loc);
|
|
2552
2530
|
}
|
|
2553
2531
|
// A GENERIC static method as a VALUE: the pinned-value rule verbatim
|
|
2554
2532
|
// (lowerGenericFnValue) — a slot spelling one concrete signature names
|
|
2555
2533
|
// an instance, an unpinned reference fences by name.
|
|
2556
2534
|
{
|
|
2557
|
-
const gfound = findGenericStaticOn(
|
|
2535
|
+
const gfound = findGenericStaticOn(lowerer, info, expr.name.text);
|
|
2558
2536
|
if (gfound)
|
|
2559
|
-
return
|
|
2537
|
+
return lowerer.lowerGenericFnValue(expr, gfound.info);
|
|
2560
2538
|
}
|
|
2561
2539
|
// `C.name` — the JS-observable class name, a compile-time constant on
|
|
2562
2540
|
// the direct spelling (tsc rejects user statics named `name`, so the
|
|
@@ -2599,8 +2577,8 @@ function namedEvaluationName(expr) {
|
|
|
2599
2577
|
* once. Statics-bearing expressions additionally restrict to positions
|
|
2600
2578
|
* where "immediately before the enclosing statement" IS the evaluation
|
|
2601
2579
|
* point (lowerFileInit drains pendingClassExprInits there). */
|
|
2602
|
-
export function lowerClassExpressionInfo(
|
|
2603
|
-
const cached =
|
|
2580
|
+
export function lowerClassExpressionInfo(lowerer, expr) {
|
|
2581
|
+
const cached = lowerer.exprClassInfoByNode.get(expr);
|
|
2604
2582
|
if (cached)
|
|
2605
2583
|
return cached;
|
|
2606
2584
|
// Reentrancy guard: heritage resolution can DEMAND another class
|
|
@@ -2610,30 +2588,30 @@ export function lowerClassExpressionInfo(L, expr) {
|
|
|
2610
2588
|
// can see (TS2506/TS2303 — direct, indirect, and cross-file require
|
|
2611
2589
|
// cycles all probed); this fence is the backstop that turns anything
|
|
2612
2590
|
// it misses into a diagnostic instead of a stack overflow.
|
|
2613
|
-
if (
|
|
2614
|
-
|
|
2591
|
+
if (lowerer.collectingExprClasses.has(expr)) {
|
|
2592
|
+
lowerer.unsupported("SC1090", expr, "class expressions whose extends chain re-enters their own collection (a cyclic base through property assignments)");
|
|
2615
2593
|
}
|
|
2616
|
-
if (
|
|
2617
|
-
|
|
2594
|
+
if (lowerer.instantiationContext) {
|
|
2595
|
+
lowerer.unsupported("SC1090", expr, "class expressions inside generic functions (each instantiation would need its own class)");
|
|
2618
2596
|
}
|
|
2619
2597
|
for (let p = expr.parent; !ts.isSourceFile(p); p = p.parent) {
|
|
2620
2598
|
if (ts.isFunctionLike(p) || ts.isClassStaticBlockDeclaration(p)) {
|
|
2621
|
-
|
|
2599
|
+
lowerer.unsupported("SC1090", expr, "class expressions inside functions (each evaluation creates a DISTINCT class in JS — fresh identity, fresh statics; declare the class at top level)");
|
|
2622
2600
|
}
|
|
2623
2601
|
}
|
|
2624
|
-
|
|
2602
|
+
lowerer.collectingExprClasses.add(expr);
|
|
2625
2603
|
try {
|
|
2626
|
-
|
|
2604
|
+
lowerer.collectClassShapeInner(expr, namedEvaluationName(expr));
|
|
2627
2605
|
}
|
|
2628
2606
|
finally {
|
|
2629
|
-
|
|
2607
|
+
lowerer.collectingExprClasses.delete(expr);
|
|
2630
2608
|
}
|
|
2631
|
-
const info =
|
|
2609
|
+
const info = lowerer.classes.get(lowerer.classNamer(expr));
|
|
2632
2610
|
if (!info)
|
|
2633
2611
|
throw new PoisonError(); // collection poisoned and reported
|
|
2634
|
-
|
|
2635
|
-
|
|
2636
|
-
|
|
2612
|
+
lowerer.exprClassInfoByNode.set(expr, info);
|
|
2613
|
+
lowerer.exprClasses.push(info);
|
|
2614
|
+
lowerer.onExprClassCollected?.(info);
|
|
2637
2615
|
// Static field initializers and static blocks run when the class
|
|
2638
2616
|
// expression EVALUATES. The supported positions evaluate exactly once,
|
|
2639
2617
|
// at the top-level statement containing the expression — the pending
|
|
@@ -2659,9 +2637,9 @@ export function lowerClassExpressionInfo(L, expr) {
|
|
|
2659
2637
|
ts.isSourceFile(holder.parent.parent)) ||
|
|
2660
2638
|
(ts.isExpressionStatement(holder) && ts.isSourceFile(holder.parent));
|
|
2661
2639
|
if (!wholeInit) {
|
|
2662
|
-
|
|
2640
|
+
lowerer.unsupported("SC1090", expr, "class expressions with static initializers or static blocks outside a whole-initializer position (their declaration-time code must run exactly where the expression evaluates — bind the class in its own top-level `const C = class …` statement)");
|
|
2663
2641
|
}
|
|
2664
|
-
|
|
2642
|
+
lowerer.pendingClassExprInits.push(...lowerer.lowerStaticFieldInits(info));
|
|
2665
2643
|
}
|
|
2666
2644
|
return info;
|
|
2667
2645
|
}
|
|
@@ -2673,20 +2651,20 @@ export function lowerClassExpressionInfo(L, expr) {
|
|
|
2673
2651
|
* ReferenceError, so it lowers to exactly that read — every evaluation
|
|
2674
2652
|
* throws identically, which is why the once-evaluated restriction and
|
|
2675
2653
|
* the member fences don't apply. */
|
|
2676
|
-
export function lowerClassExpression(
|
|
2654
|
+
export function lowerClassExpression(lowerer, expr) {
|
|
2677
2655
|
if (decoratorNodesOf(expr).length > 0 ||
|
|
2678
2656
|
expr.members.some((m) => decoratorNodesOf(m).length > 0)) {
|
|
2679
2657
|
if (!isJsSourceFile(expr.getSourceFile()) && expr.typeParameters === undefined) {
|
|
2680
|
-
const thrown = guaranteedDecorationThrow(
|
|
2658
|
+
const thrown = guaranteedDecorationThrow(lowerer, expr);
|
|
2681
2659
|
if (thrown) {
|
|
2682
2660
|
// The expression's static type never materializes — the read
|
|
2683
2661
|
// throws — so the nominal IR type only has to satisfy the
|
|
2684
2662
|
// consumer. F64 is the ambient-undefRead convention.
|
|
2685
|
-
return nsUndefRead(
|
|
2663
|
+
return nsUndefRead(lowerer, thrown.name, expr, F64);
|
|
2686
2664
|
}
|
|
2687
2665
|
}
|
|
2688
2666
|
}
|
|
2689
|
-
return classValueRef(
|
|
2667
|
+
return classValueRef(lowerer, lowerClassExpressionInfo(lowerer, expr), expr);
|
|
2690
2668
|
}
|
|
2691
2669
|
/** The EXACT class a receiver expression is statically known to BE (not
|
|
2692
2670
|
* merely be typed by): the class name itself, or a `const` binding
|
|
@@ -2694,20 +2672,20 @@ export function lowerClassExpression(L, expr) {
|
|
|
2694
2672
|
* can never hold a subclass at runtime, so static WRITES through them
|
|
2695
2673
|
* hit the declaring class's storage exactly (the shadowing hazards of
|
|
2696
2674
|
* general class values don't arise). Null for everything else. */
|
|
2697
|
-
export function exactClassOfReceiver(
|
|
2675
|
+
export function exactClassOfReceiver(lowerer, expr) {
|
|
2698
2676
|
if (!ts.isIdentifier(expr))
|
|
2699
2677
|
return null;
|
|
2700
|
-
const symbol =
|
|
2678
|
+
const symbol = lowerer.resolveValueSymbol(expr);
|
|
2701
2679
|
if (!symbol)
|
|
2702
2680
|
return null;
|
|
2703
|
-
const direct =
|
|
2681
|
+
const direct = lowerer.classBySymbol.get(symbol);
|
|
2704
2682
|
// A rebindable decorated name is NOT exactly its class — the binding
|
|
2705
2683
|
// may hold a replacing decorator's result (a subclass value), where a
|
|
2706
2684
|
// static write would create an own property in JS. The general
|
|
2707
2685
|
// class-value write fence answers instead.
|
|
2708
2686
|
if (direct)
|
|
2709
2687
|
return direct.classDecorators?.valueGlobalId !== undefined ? null : direct;
|
|
2710
|
-
const decl =
|
|
2688
|
+
const decl = lowerer.checker.valueDeclarationOf(symbol);
|
|
2711
2689
|
if (!decl || !ts.isVariableDeclaration(decl) || decl.initializer === undefined ||
|
|
2712
2690
|
!ts.isVariableDeclarationList(decl.parent) ||
|
|
2713
2691
|
(decl.parent.flags & ts.NodeFlags.Const) === 0) {
|
|
@@ -2717,10 +2695,10 @@ export function exactClassOfReceiver(L, expr) {
|
|
|
2717
2695
|
while (ts.isParenthesizedExpression(init))
|
|
2718
2696
|
init = init.expression;
|
|
2719
2697
|
if (ts.isClassExpression(init))
|
|
2720
|
-
return
|
|
2698
|
+
return lowerer.exprClassInfoByNode.get(init) ?? null;
|
|
2721
2699
|
if (ts.isIdentifier(init)) {
|
|
2722
|
-
const initSym =
|
|
2723
|
-
const aliased = initSym ? (
|
|
2700
|
+
const initSym = lowerer.resolveValueSymbol(init);
|
|
2701
|
+
const aliased = initSym ? (lowerer.classBySymbol.get(initSym) ?? null) : null;
|
|
2724
2702
|
// `const X = C` over a rebindable decorated name: X holds the
|
|
2725
2703
|
// decoration result — not exactly C (see the direct case above).
|
|
2726
2704
|
return aliased?.classDecorators?.valueGlobalId !== undefined ? null : aliased;
|
|
@@ -2742,14 +2720,14 @@ export function exactClassOfReceiver(L, expr) {
|
|
|
2742
2720
|
* fence names it. Collection is on demand and idempotent
|
|
2743
2721
|
* (lowerClassExpressionInfo), so resolution order between files and
|
|
2744
2722
|
* passes never matters. */
|
|
2745
|
-
export function propertyAssignedClassInfoOf(
|
|
2723
|
+
export function propertyAssignedClassInfoOf(lowerer, symbol) {
|
|
2746
2724
|
if (!symbol)
|
|
2747
2725
|
return null;
|
|
2748
|
-
const resolved = symbol.flags & ts.SymbolFlags.Alias ?
|
|
2749
|
-
const registered =
|
|
2726
|
+
const resolved = symbol.flags & ts.SymbolFlags.Alias ? lowerer.checker.getAliasedSymbol(symbol) : symbol;
|
|
2727
|
+
const registered = lowerer.classBySymbol.get(resolved);
|
|
2750
2728
|
if (registered)
|
|
2751
2729
|
return registered;
|
|
2752
|
-
const decls =
|
|
2730
|
+
const decls = lowerer.checker.declarationsOf(resolved);
|
|
2753
2731
|
// The class expression's OWN symbol (tsgo's answer through CJS export
|
|
2754
2732
|
// aliases): the declaration is the expression itself. Its top-level
|
|
2755
2733
|
// assignment statement must be the binding's ONLY producer. The
|
|
@@ -2760,8 +2738,8 @@ export function propertyAssignedClassInfoOf(L, symbol) {
|
|
|
2760
2738
|
const assign = enclosingTopLevelClassAssignment(decls[0]);
|
|
2761
2739
|
if (!assign || countAssignmentsTo(assign) !== 1)
|
|
2762
2740
|
return null;
|
|
2763
|
-
const info =
|
|
2764
|
-
|
|
2741
|
+
const info = lowerer.lowerClassExpressionInfo(decls[0]);
|
|
2742
|
+
lowerer.classBySymbol.set(resolved, info);
|
|
2765
2743
|
return info;
|
|
2766
2744
|
}
|
|
2767
2745
|
// The expando property symbol: every top-level `X.N = …` assignment is
|
|
@@ -2778,8 +2756,8 @@ export function propertyAssignedClassInfoOf(L, symbol) {
|
|
|
2778
2756
|
rhs = rhs.expression;
|
|
2779
2757
|
if (!ts.isClassExpression(rhs))
|
|
2780
2758
|
return null;
|
|
2781
|
-
const info =
|
|
2782
|
-
|
|
2759
|
+
const info = lowerer.lowerClassExpressionInfo(rhs);
|
|
2760
|
+
lowerer.classBySymbol.set(resolved, info);
|
|
2783
2761
|
return info;
|
|
2784
2762
|
}
|
|
2785
2763
|
/** The top-level `… = <this class expression>` assignment a class
|
|
@@ -2830,7 +2808,7 @@ function countAssignmentsTo(assign) {
|
|
|
2830
2808
|
* global and calls through the value. Null when the receiver isn't a
|
|
2831
2809
|
* class name/value or the member doesn't resolve (the fences name the
|
|
2832
2810
|
* site downstream). */
|
|
2833
|
-
export function lowerStaticMethodCall(
|
|
2811
|
+
export function lowerStaticMethodCall(lowerer, call, access) {
|
|
2834
2812
|
if (access.questionDotToken)
|
|
2835
2813
|
return null;
|
|
2836
2814
|
// `module.exports.describe()` in a module whose whole export IS a
|
|
@@ -2843,28 +2821,28 @@ export function lowerStaticMethodCall(L, call, access) {
|
|
|
2843
2821
|
const whole = cjsClassExprWholeExportOf(access.getSourceFile());
|
|
2844
2822
|
if (!whole)
|
|
2845
2823
|
return null;
|
|
2846
|
-
return staticCallOn(
|
|
2824
|
+
return staticCallOn(lowerer, call, access, lowerer.lowerClassExpressionInfo(whole.classExpr), false);
|
|
2847
2825
|
}
|
|
2848
|
-
const symbol =
|
|
2849
|
-
const direct = (symbol ?
|
|
2826
|
+
const symbol = lowerer.resolveValueSymbol(access.expression);
|
|
2827
|
+
const direct = (symbol ? lowerer.classBySymbol.get(symbol) : undefined) ??
|
|
2850
2828
|
// A require binding over `module.exports = class {…}`: the alias
|
|
2851
2829
|
// lands on the expression's own symbol — exact, like the name.
|
|
2852
|
-
propertyAssignedClassInfoOf(
|
|
2830
|
+
propertyAssignedClassInfoOf(lowerer, symbol) ??
|
|
2853
2831
|
undefined;
|
|
2854
2832
|
let info = direct ?? null;
|
|
2855
2833
|
// A rebindable decorated name is a class VALUE receiver: the call
|
|
2856
2834
|
// devirtualizes under the value rules (shadow fences below).
|
|
2857
2835
|
let throughValue = direct?.classDecorators?.valueGlobalId !== undefined;
|
|
2858
2836
|
if (!info) {
|
|
2859
|
-
const recvT =
|
|
2837
|
+
const recvT = lowerer.mapTypeOf(lowerer.typeOf(access.expression));
|
|
2860
2838
|
if (recvT?.kind !== "classval")
|
|
2861
2839
|
return null;
|
|
2862
|
-
info =
|
|
2840
|
+
info = lowerer.classes.get(recvT.className) ?? null;
|
|
2863
2841
|
throughValue = true;
|
|
2864
2842
|
}
|
|
2865
2843
|
if (!info)
|
|
2866
2844
|
return null;
|
|
2867
|
-
return staticCallOn(
|
|
2845
|
+
return staticCallOn(lowerer, call, access, info, throughValue);
|
|
2868
2846
|
}
|
|
2869
2847
|
/** True when a class-VALUE receiver can only hold `info`'s own class
|
|
2870
2848
|
* object at runtime: the receiver provably IS the class
|
|
@@ -2876,10 +2854,10 @@ export function lowerStaticMethodCall(L, call, access) {
|
|
|
2876
2854
|
* rebindable global). Exactly then a #private static access through
|
|
2877
2855
|
* the value always carries the declaring class's brand — Node's
|
|
2878
2856
|
* TypeError cannot arise. */
|
|
2879
|
-
function classValueIsExactlyOwn(
|
|
2880
|
-
return exactClassOfReceiver(
|
|
2857
|
+
function classValueIsExactlyOwn(lowerer, recv, info) {
|
|
2858
|
+
return exactClassOfReceiver(lowerer, recv) === info || info.subclasses.length === 0;
|
|
2881
2859
|
}
|
|
2882
|
-
function staticCallOn(
|
|
2860
|
+
function staticCallOn(lowerer, call, access, info, throughValue) {
|
|
2883
2861
|
const loc = locOf(call);
|
|
2884
2862
|
// #private statics: through-a-VALUE receiver the call devirtualizes
|
|
2885
2863
|
// exactly when the value can only BE the declaring class
|
|
@@ -2887,29 +2865,29 @@ function staticCallOn(L, call, access, info, throughValue) {
|
|
|
2887
2865
|
// runtime fences, since only the declaring class object carries the
|
|
2888
2866
|
// brand in JS), and the direct spelling resolves only on the
|
|
2889
2867
|
// declaring class itself — `D.#s` is Node's TypeError.
|
|
2890
|
-
if (access.name.text.startsWith("#") && throughValue && !classValueIsExactlyOwn(
|
|
2891
|
-
|
|
2868
|
+
if (access.name.text.startsWith("#") && throughValue && !classValueIsExactlyOwn(lowerer, access.expression, info)) {
|
|
2869
|
+
lowerer.unsupported("SC1090", call, `calling the private static '${access.name.text}' through a class value (JS brands the declaring class object alone — call it through the class's own name)`);
|
|
2892
2870
|
}
|
|
2893
|
-
const found = findStaticOn(
|
|
2871
|
+
const found = findStaticOn(lowerer, info, access.name.text);
|
|
2894
2872
|
if (found && access.name.text.startsWith("#") && found.declarer !== info) {
|
|
2895
|
-
|
|
2873
|
+
lowerer.unsupported("SC1090", call, `calling the private static '${access.name.text}' through the subclass '${info.def.name.replace(/^%|^%m\d+\./, "")}' (JS brands the declaring class object alone — Node throws a TypeError here; spell the declaring class's name)`);
|
|
2896
2874
|
}
|
|
2897
2875
|
if (!found) {
|
|
2898
2876
|
// GENERIC static methods: monomorphized like top-level generic
|
|
2899
2877
|
// functions, called directly as `%C.static:m%n` — with the same
|
|
2900
2878
|
// through-a-VALUE shadowing fence as plain statics.
|
|
2901
|
-
const gfound = findGenericStaticOn(
|
|
2879
|
+
const gfound = findGenericStaticOn(lowerer, info, access.name.text);
|
|
2902
2880
|
if (!gfound)
|
|
2903
2881
|
return null;
|
|
2904
|
-
if (throughValue && staticShadowBelow(
|
|
2905
|
-
|
|
2882
|
+
if (throughValue && staticShadowBelow(lowerer, info, access.name.text)) {
|
|
2883
|
+
lowerer.unsupported("SC1090", call, `calling the static member '${access.name.text}' through a class value (a subclass of '${info.def.name.replace(/^%|^%m\d+\./, "")}' redeclares it, so the runtime class decides which declaration answers)`);
|
|
2906
2884
|
}
|
|
2907
|
-
const instance = genericCallInstance(
|
|
2908
|
-
const args =
|
|
2885
|
+
const instance = genericCallInstance(lowerer, call, gfound.info);
|
|
2886
|
+
const args = lowerer.completeArgs(call.arguments, instance.params, loc, call);
|
|
2909
2887
|
return { kind: "call", callee: instance.name, args, type: instance.returnType, loc };
|
|
2910
2888
|
}
|
|
2911
|
-
if (throughValue && staticShadowBelow(
|
|
2912
|
-
|
|
2889
|
+
if (throughValue && staticShadowBelow(lowerer, info, access.name.text)) {
|
|
2890
|
+
lowerer.unsupported("SC1090", call, `calling the static member '${access.name.text}' through a class value (a subclass of '${info.def.name.replace(/^%|^%m\d+\./, "")}' redeclares it, so the runtime class decides which declaration answers)`);
|
|
2913
2891
|
}
|
|
2914
2892
|
if (found.field !== undefined) {
|
|
2915
2893
|
// A func-typed static field in call position: read the global,
|
|
@@ -2918,19 +2896,19 @@ function staticCallOn(L, call, access, info, throughValue) {
|
|
|
2918
2896
|
return null;
|
|
2919
2897
|
const callee = { kind: "varRef", localId: found.field.globalId, type: found.field.type, loc };
|
|
2920
2898
|
const params = found.field.type.params;
|
|
2921
|
-
const args = call.arguments.map((a, i) =>
|
|
2899
|
+
const args = call.arguments.map((a, i) => lowerer.lowerExprExpecting(a, params[i]));
|
|
2922
2900
|
for (let i = args.length; i < params.length; i++) {
|
|
2923
|
-
const absent = omittedArgFor(
|
|
2901
|
+
const absent = omittedArgFor(lowerer, params[i], loc);
|
|
2924
2902
|
if (!absent) {
|
|
2925
|
-
|
|
2903
|
+
lowerer.unsupported("SC1090", call, "calls omitting a non-optional parameter of the callee's type");
|
|
2926
2904
|
}
|
|
2927
2905
|
args.push(absent);
|
|
2928
2906
|
}
|
|
2929
2907
|
return { kind: "callValue", callee, args, type: found.field.type.ret, loc };
|
|
2930
2908
|
}
|
|
2931
2909
|
const fnName = `%${found.declarer.def.name}.static:${access.name.text}`;
|
|
2932
|
-
|
|
2933
|
-
const args =
|
|
2910
|
+
lowerer.noteEdge(fnName);
|
|
2911
|
+
const args = lowerer.completeArgs(call.arguments, found.method.params, loc, call);
|
|
2934
2912
|
return { kind: "call", callee: fnName, args, type: found.method.ret, loc };
|
|
2935
2913
|
}
|
|
2936
2914
|
/** Static member access through a class VALUE (`X.m` where X is
|
|
@@ -2940,10 +2918,10 @@ function staticCallOn(L, call, access, info, throughValue) {
|
|
|
2940
2918
|
* genuinely dynamic member: the class.name libCall reads the runtime
|
|
2941
2919
|
* class object's stored name. Null when the receiver isn't a class
|
|
2942
2920
|
* value or the member doesn't resolve. */
|
|
2943
|
-
export function lowerClassValueProperty(
|
|
2921
|
+
export function lowerClassValueProperty(lowerer, expr) {
|
|
2944
2922
|
if (expr.questionDotToken)
|
|
2945
2923
|
return null;
|
|
2946
|
-
const recvT =
|
|
2924
|
+
const recvT = lowerer.mapTypeOf(lowerer.typeOf(expr.expression));
|
|
2947
2925
|
if (recvT?.kind !== "classval")
|
|
2948
2926
|
return null;
|
|
2949
2927
|
const loc = locOf(expr);
|
|
@@ -2952,7 +2930,7 @@ export function lowerClassValueProperty(L, expr) {
|
|
|
2952
2930
|
// genuinely dynamic member. It CONSUMES the receiver (no evaluation
|
|
2953
2931
|
// is discarded), so any receiver expression is fine here.
|
|
2954
2932
|
if (member === "name") {
|
|
2955
|
-
const recv =
|
|
2933
|
+
const recv = lowerer.lowerExpr(expr.expression);
|
|
2956
2934
|
if (recv.type.kind !== "classval")
|
|
2957
2935
|
return null;
|
|
2958
2936
|
return { kind: "libCall", fn: "class.name", args: [recv], type: STRING, loc };
|
|
@@ -2965,16 +2943,16 @@ export function lowerClassValueProperty(L, expr) {
|
|
|
2965
2943
|
// Devirtualized reads DISCARD the receiver value, so only
|
|
2966
2944
|
// side-effect-free receivers are claimed (the instanceOf fold
|
|
2967
2945
|
// rule); computed ones meet the pointed fence with a bindable fix.
|
|
2968
|
-
|
|
2946
|
+
lowerer.unsupported("SC1090", expr, "static member access through a computed class-value expression (bind the class value to a variable first)");
|
|
2969
2947
|
}
|
|
2970
2948
|
// The direct class-name spelling resolved in lowerStaticFieldRead;
|
|
2971
2949
|
// reaching here means the receiver is a classval-typed BINDING.
|
|
2972
|
-
const info =
|
|
2950
|
+
const info = lowerer.classes.get(recvT.className);
|
|
2973
2951
|
if (!info)
|
|
2974
2952
|
return null;
|
|
2975
|
-
const found = findStaticOn(
|
|
2953
|
+
const found = findStaticOn(lowerer, info, member);
|
|
2976
2954
|
if (!found) {
|
|
2977
|
-
|
|
2955
|
+
lowerer.unsupported("SC1090", expr, `the static member '${member}' of class '${info.def.name.replace(/^%|^%m\d+\./, "")}' (static accessors and initializer-less static fields have no lowering, and Function members like .call/.bind/.prototype have no value form)`);
|
|
2978
2956
|
}
|
|
2979
2957
|
// #private statics read through a class VALUE exactly when the value
|
|
2980
2958
|
// can only BE the declaring class (classValueIsExactlyOwn — a const
|
|
@@ -2985,35 +2963,35 @@ export function lowerClassValueProperty(L, expr) {
|
|
|
2985
2963
|
// lowerStaticFieldRead, so a private reaching here is a
|
|
2986
2964
|
// classval-typed binding.
|
|
2987
2965
|
if (member.startsWith("#")) {
|
|
2988
|
-
if (!classValueIsExactlyOwn(
|
|
2989
|
-
|
|
2966
|
+
if (!classValueIsExactlyOwn(lowerer, expr.expression, info)) {
|
|
2967
|
+
lowerer.unsupported("SC1090", expr, `reading the private static '${member}' through a class value (JS brands the declaring class object alone — spell the declaring class's name)`);
|
|
2990
2968
|
}
|
|
2991
2969
|
if (found.declarer !== info) {
|
|
2992
|
-
|
|
2970
|
+
lowerer.unsupported("SC1090", expr, `reading the private static '${member}' through the subclass '${info.def.name.replace(/^%|^%m\d+\./, "")}' (JS brands the declaring class object alone — Node throws a TypeError here; spell the declaring class's name)`);
|
|
2993
2971
|
}
|
|
2994
2972
|
}
|
|
2995
|
-
if (staticShadowBelow(
|
|
2996
|
-
|
|
2973
|
+
if (staticShadowBelow(lowerer, info, member)) {
|
|
2974
|
+
lowerer.unsupported("SC1090", expr, `reading the static member '${member}' through a class value (a subclass of '${info.def.name.replace(/^%|^%m\d+\./, "")}' redeclares it, so the runtime class decides which declaration answers)`);
|
|
2997
2975
|
}
|
|
2998
2976
|
if (found.field !== undefined) {
|
|
2999
|
-
return
|
|
2977
|
+
return lowerer.maybeNarrow({ kind: "varRef", localId: found.field.globalId, type: found.field.type, loc }, expr);
|
|
3000
2978
|
}
|
|
3001
|
-
return staticMethodValue(
|
|
2979
|
+
return staticMethodValue(lowerer, found.declarer, member, found.method, expr, loc);
|
|
3002
2980
|
}
|
|
3003
2981
|
/** An abstract method's signature — lambdaSignature minus the body check
|
|
3004
2982
|
* (an abstract declaration IS exactly a signature; tsc rejects the
|
|
3005
2983
|
* async/generator/generic-with-body combinations before this runs, and
|
|
3006
2984
|
* the generic case fences at the caller). */
|
|
3007
|
-
function abstractMemberSignature(
|
|
2985
|
+
function abstractMemberSignature(lowerer, member) {
|
|
3008
2986
|
for (const param of member.parameters) {
|
|
3009
2987
|
if (!ts.isIdentifier(param.name))
|
|
3010
|
-
|
|
2988
|
+
lowerer.unsupported("SC1031", param);
|
|
3011
2989
|
}
|
|
3012
|
-
return { shapes:
|
|
2990
|
+
return { shapes: lowerer.paramShapes(member.parameters), ret: lowerer.declaredReturnType(member, member.name) };
|
|
3013
2991
|
}
|
|
3014
2992
|
/** The nearest declaration of `name` at or above `info` — the method a
|
|
3015
2993
|
* receiver of that static class runs when nothing below overrides it. */
|
|
3016
|
-
export function findMethodOn(
|
|
2994
|
+
export function findMethodOn(lowerer, info, name) {
|
|
3017
2995
|
for (let c = info; c; c = c.base) {
|
|
3018
2996
|
const sig = c.methods.get(name);
|
|
3019
2997
|
if (sig)
|
|
@@ -3022,8 +3000,8 @@ export function findMethodOn(L, info, name) {
|
|
|
3022
3000
|
return null;
|
|
3023
3001
|
}
|
|
3024
3002
|
/** True when `sub` is a STRICT descendant of `sup` in the class graph. */
|
|
3025
|
-
export function isSubclassOf(
|
|
3026
|
-
for (let c =
|
|
3003
|
+
export function isSubclassOf(lowerer, sub, sup) {
|
|
3004
|
+
for (let c = lowerer.classes.get(sub)?.base ?? null; c; c = c.base) {
|
|
3027
3005
|
if (c.def.name === sup)
|
|
3028
3006
|
return true;
|
|
3029
3007
|
}
|
|
@@ -3032,7 +3010,7 @@ export function isSubclassOf(L, sub, sup) {
|
|
|
3032
3010
|
/** In an extends-hierarchy (as base or derived): the class carries a
|
|
3033
3011
|
* vtable and participates in dynamic instanceof; standalone classes keep
|
|
3034
3012
|
* their exact pre-inheritance layout and behavior. */
|
|
3035
|
-
export function inHierarchy(
|
|
3013
|
+
export function inHierarchy(lowerer, info) {
|
|
3036
3014
|
// The runtime emitter class is ALWAYS a hierarchy member: ScrEmitter
|
|
3037
3015
|
// carries its vtable word whether or not the program subclasses it
|
|
3038
3016
|
// (the runtime allocates bare instances with scr_emitter_vt).
|
|
@@ -3043,15 +3021,15 @@ export function inHierarchy(L, info) {
|
|
|
3043
3021
|
* class can reach a distinct implementation, so it must dispatch
|
|
3044
3022
|
* dynamically. Abstract re-declarations don't count (they carry no
|
|
3045
3023
|
* implementation; the concrete ones below them do, via the recursion). */
|
|
3046
|
-
export function overrideBelow(
|
|
3024
|
+
export function overrideBelow(lowerer, info, name) {
|
|
3047
3025
|
return info.subclasses.some((s) => {
|
|
3048
3026
|
const m = s.methods.get(name);
|
|
3049
|
-
return (m !== undefined && m.abstract !== true) ||
|
|
3027
|
+
return (m !== undefined && m.abstract !== true) || lowerer.overrideBelow(s, name);
|
|
3050
3028
|
});
|
|
3051
3029
|
}
|
|
3052
3030
|
/** The nearest GENERIC-method declaration of `name` at/above `info` —
|
|
3053
3031
|
* findMethodOn's twin over the genericMethods tables. */
|
|
3054
|
-
export function findGenericMethodOn(
|
|
3032
|
+
export function findGenericMethodOn(lowerer, info, name) {
|
|
3055
3033
|
for (let c = info; c; c = c.base) {
|
|
3056
3034
|
const gm = c.genericMethods?.get(name);
|
|
3057
3035
|
if (gm)
|
|
@@ -3063,8 +3041,8 @@ export function findGenericMethodOn(L, info, name) {
|
|
|
3063
3041
|
* method `name` — overrideBelow's twin: generic methods have no vtable
|
|
3064
3042
|
* slot, so a call that could reach an override compiles only when the
|
|
3065
3043
|
* receiver's runtime class is statically exact. */
|
|
3066
|
-
|
|
3067
|
-
return info.subclasses.some((s) => s.genericMethods?.has(name) === true || genericOverrideBelow(
|
|
3044
|
+
function genericOverrideBelow(lowerer, info, name) {
|
|
3045
|
+
return info.subclasses.some((s) => s.genericMethods?.has(name) === true || genericOverrideBelow(lowerer, s, name));
|
|
3068
3046
|
}
|
|
3069
3047
|
/** The receiver's EXACT runtime class, when the expression proves it: a
|
|
3070
3048
|
* `new C(...)` expression directly, or a const binding initialized with
|
|
@@ -3072,23 +3050,23 @@ export function genericOverrideBelow(L, info, name) {
|
|
|
3072
3050
|
* The class is read off the mapped INITIALIZER type — a `const b: Base =
|
|
3073
3051
|
* new D()` receiver is exactly D, not its annotation. Distinct from
|
|
3074
3052
|
* exactClassOfReceiver, which answers for CLASS-VALUE receivers. */
|
|
3075
|
-
export function exactInstanceClassOf(
|
|
3053
|
+
export function exactInstanceClassOf(lowerer, expr) {
|
|
3076
3054
|
let e = expr;
|
|
3077
3055
|
while (ts.isParenthesizedExpression(e))
|
|
3078
3056
|
e = e.expression;
|
|
3079
3057
|
const classOfNew = (n) => {
|
|
3080
3058
|
if (!ts.isNewExpression(n))
|
|
3081
3059
|
return null;
|
|
3082
|
-
const t =
|
|
3083
|
-
return t?.kind === "object" ? (
|
|
3060
|
+
const t = lowerer.mapTypeOf(lowerer.typeOf(n));
|
|
3061
|
+
return t?.kind === "object" ? (lowerer.classes.get(t.className) ?? null) : null;
|
|
3084
3062
|
};
|
|
3085
3063
|
const direct = classOfNew(e);
|
|
3086
3064
|
if (direct)
|
|
3087
3065
|
return direct;
|
|
3088
3066
|
if (!ts.isIdentifier(e))
|
|
3089
3067
|
return null;
|
|
3090
|
-
const symbol =
|
|
3091
|
-
const decl = symbol ?
|
|
3068
|
+
const symbol = lowerer.resolveValueSymbol(e);
|
|
3069
|
+
const decl = symbol ? lowerer.checker.valueDeclarationOf(symbol) : undefined;
|
|
3092
3070
|
if (!decl || !ts.isVariableDeclaration(decl) || decl.initializer === undefined ||
|
|
3093
3071
|
!ts.isVariableDeclarationList(decl.parent) ||
|
|
3094
3072
|
(decl.parent.flags & ts.NodeFlags.Const) === 0) {
|
|
@@ -3111,7 +3089,7 @@ export function exactInstanceClassOf(L, expr) {
|
|
|
3111
3089
|
* and uses that want the empty record width-coerce at the use site.
|
|
3112
3090
|
* Const + direct `new` only — a reassignable binding or a produced
|
|
3113
3091
|
* value keeps today's record story and its fences. */
|
|
3114
|
-
export function genericIfaceBindingKeepsClass(
|
|
3092
|
+
export function genericIfaceBindingKeepsClass(lowerer, decl, declaredType) {
|
|
3115
3093
|
if (declaredType.kind !== "record")
|
|
3116
3094
|
return false;
|
|
3117
3095
|
if (!ts.isIdentifier(decl.name) || decl.initializer === undefined || decl.type === undefined)
|
|
@@ -3123,14 +3101,14 @@ export function genericIfaceBindingKeepsClass(L, decl, declaredType) {
|
|
|
3123
3101
|
init = init.expression;
|
|
3124
3102
|
if (!ts.isNewExpression(init))
|
|
3125
3103
|
return false;
|
|
3126
|
-
const shape =
|
|
3104
|
+
const shape = lowerer.shapes.get(declaredType.shapeId);
|
|
3127
3105
|
if (!shape || shape.fields.length > 0 || shape.indexValue !== undefined || shape.tuple === true)
|
|
3128
3106
|
return false;
|
|
3129
|
-
const annT =
|
|
3130
|
-
const props =
|
|
3107
|
+
const annT = lowerer.typeOf(decl.name);
|
|
3108
|
+
const props = lowerer.checker.getPropertiesOfType(annT);
|
|
3131
3109
|
if (props.length === 0)
|
|
3132
3110
|
return false;
|
|
3133
|
-
return props.every((p) => isGenericCallableMemberType(
|
|
3111
|
+
return props.every((p) => isGenericCallableMemberType(lowerer.checker.getTypeOfSymbol(p), lowerer.checker));
|
|
3134
3112
|
}
|
|
3135
3113
|
/** `recv.m<T>(args)` — a GENERIC method call, dispatched STATICALLY: the
|
|
3136
3114
|
* checker's resolved signature (type arguments substituted, inferred or
|
|
@@ -3140,14 +3118,14 @@ export function genericIfaceBindingKeepsClass(L, decl, declaredType) {
|
|
|
3140
3118
|
* runtime class could OVERRIDE the method (genericOverrideBelow) must be
|
|
3141
3119
|
* statically exact (exactInstanceClassOf) — the override set then
|
|
3142
3120
|
* resolves at compile time — or fences by name. */
|
|
3143
|
-
export function lowerClassGenericMethodCall(
|
|
3121
|
+
export function lowerClassGenericMethodCall(lowerer, call, access, recvInfo, found, recvIr) {
|
|
3144
3122
|
const name = access.name.text;
|
|
3145
3123
|
let { declarer, info } = found;
|
|
3146
|
-
if (genericOverrideBelow(
|
|
3147
|
-
const exact = exactInstanceClassOf(
|
|
3148
|
-
const refound = exact ? findGenericMethodOn(
|
|
3124
|
+
if (genericOverrideBelow(lowerer, recvInfo, name)) {
|
|
3125
|
+
const exact = exactInstanceClassOf(lowerer, access.expression);
|
|
3126
|
+
const refound = exact ? findGenericMethodOn(lowerer, exact, name) : null;
|
|
3149
3127
|
if (!refound) {
|
|
3150
|
-
|
|
3128
|
+
lowerer.unsupported("SC1090", call, `calling the generic method '${name}' through a receiver whose runtime class may override it (a subclass of '${recvInfo.def.name.replace(/^%|^%m\d+\./, "")}' redeclares it and generic methods dispatch statically — bind the receiver to a const initialized with its 'new' expression)`);
|
|
3151
3129
|
}
|
|
3152
3130
|
({ declarer, info } = refound);
|
|
3153
3131
|
}
|
|
@@ -3156,23 +3134,23 @@ export function lowerClassGenericMethodCall(L, call, access, recvInfo, found, re
|
|
|
3156
3134
|
// type parameters); everything else about the dispatch — static
|
|
3157
3135
|
// resolution, the exactness rule above — is the generic story.
|
|
3158
3136
|
const instance = info.implicitParams
|
|
3159
|
-
? implicitCallInstance(
|
|
3160
|
-
: genericCallInstance(
|
|
3161
|
-
const receiver = recvIr ??
|
|
3137
|
+
? implicitCallInstance(lowerer, call, info)
|
|
3138
|
+
: genericCallInstance(lowerer, call, info);
|
|
3139
|
+
const receiver = recvIr ?? lowerer.lowerExpr(access.expression);
|
|
3162
3140
|
const loc = locOf(call);
|
|
3163
3141
|
// The declarer sits at/above the receiver's static class on the plain
|
|
3164
3142
|
// path; the EXACT path can land below it (a base-typed const provably
|
|
3165
3143
|
// holding the subclass) — that direction is the checker-grade downcast
|
|
3166
3144
|
// (the exactness proof is static, stronger than an instanceof guard).
|
|
3167
|
-
const thisArg = receiver.type.kind === "object" && isSubclassOf(
|
|
3145
|
+
const thisArg = receiver.type.kind === "object" && isSubclassOf(lowerer, declarer.def.name, receiver.type.className)
|
|
3168
3146
|
? { kind: "downcast", value: receiver, type: { kind: "object", className: declarer.def.name }, loc }
|
|
3169
|
-
: upcastTo(
|
|
3170
|
-
const args =
|
|
3147
|
+
: upcastTo(lowerer, receiver, declarer.def.name);
|
|
3148
|
+
const args = lowerer.completeArgs(call.arguments, instance.params, loc, call);
|
|
3171
3149
|
return { kind: "call", callee: instance.name, args: [thisArg, ...args], type: instance.returnType, loc };
|
|
3172
3150
|
}
|
|
3173
3151
|
/** Wraps a derived-class expression in an upcast when the target base
|
|
3174
3152
|
* class differs (a no-op reinterpret at runtime; keeps IR types exact). */
|
|
3175
|
-
export function upcastTo(
|
|
3153
|
+
export function upcastTo(lowerer, expr, className) {
|
|
3176
3154
|
if (expr.type.kind === "object" && expr.type.className !== className) {
|
|
3177
3155
|
return { kind: "upcast", value: expr, type: { kind: "object", className }, loc: expr.loc };
|
|
3178
3156
|
}
|
|
@@ -3190,14 +3168,14 @@ export function upcastTo(L, expr, className) {
|
|
|
3190
3168
|
* and no other reference to the parameter — the one rest-constructor
|
|
3191
3169
|
* form with an exact static story under monomorphization (the
|
|
3192
3170
|
* instantiation adopts the base's ABI; see collectClassShapeInner). */
|
|
3193
|
-
function mixinForwardingCtor(
|
|
3171
|
+
function mixinForwardingCtor(lowerer, ctor) {
|
|
3194
3172
|
if (ctor.parameters.length !== 1 || !ctor.body)
|
|
3195
3173
|
return false;
|
|
3196
3174
|
const p = ctor.parameters[0];
|
|
3197
3175
|
if (!p.dotDotDotToken || !ts.isIdentifier(p.name))
|
|
3198
3176
|
return false;
|
|
3199
3177
|
const paramName = p.name;
|
|
3200
|
-
const paramSym =
|
|
3178
|
+
const paramSym = lowerer.checker.getSymbolAtLocation(paramName);
|
|
3201
3179
|
if (!paramSym)
|
|
3202
3180
|
return false;
|
|
3203
3181
|
const first = ctor.body.statements[0];
|
|
@@ -3212,13 +3190,13 @@ function mixinForwardingCtor(L, ctor) {
|
|
|
3212
3190
|
if (!ts.isSpreadElement(a) || !ts.isIdentifier(a.expression))
|
|
3213
3191
|
return false;
|
|
3214
3192
|
const spreadIdent = a.expression;
|
|
3215
|
-
if (
|
|
3193
|
+
if (lowerer.checker.getSymbolAtLocation(spreadIdent) !== paramSym)
|
|
3216
3194
|
return false;
|
|
3217
3195
|
let extraRef = false;
|
|
3218
3196
|
ts.walkPreorder(ctor.body, (n) => {
|
|
3219
3197
|
if (n === spreadIdent)
|
|
3220
3198
|
return undefined;
|
|
3221
|
-
if (ts.isIdentifier(n) && n.text === paramName.text &&
|
|
3199
|
+
if (ts.isIdentifier(n) && n.text === paramName.text && lowerer.checker.getSymbolAtLocation(n) === paramSym) {
|
|
3222
3200
|
extraRef = true;
|
|
3223
3201
|
return "stop";
|
|
3224
3202
|
}
|
|
@@ -3226,7 +3204,7 @@ function mixinForwardingCtor(L, ctor) {
|
|
|
3226
3204
|
});
|
|
3227
3205
|
return !extraRef;
|
|
3228
3206
|
}
|
|
3229
|
-
export function lowerClassMembers(
|
|
3207
|
+
export function lowerClassMembers(lowerer, info) {
|
|
3230
3208
|
const out = [];
|
|
3231
3209
|
const className = info.def.name;
|
|
3232
3210
|
// Generic-class INSTANTIATIONS (and mixin instantiations) are
|
|
@@ -3242,20 +3220,20 @@ export function lowerClassMembers(L, info) {
|
|
|
3242
3220
|
// exists): the diagnostic is recorded — the member skips like a
|
|
3243
3221
|
// signature-blocked function (lowerStaticMethod's rule) instead of
|
|
3244
3222
|
// crashing the whole lowering.
|
|
3245
|
-
if (!info.generic && (always ||
|
|
3223
|
+
if (!info.generic && (always || lowerer.wantBody(`%${className}.constructor`))) {
|
|
3246
3224
|
try {
|
|
3247
|
-
out.push(
|
|
3225
|
+
out.push(lowerer.lowerClassCtor(info));
|
|
3248
3226
|
}
|
|
3249
3227
|
catch (e) {
|
|
3250
3228
|
if (!(e instanceof PoisonError))
|
|
3251
3229
|
throw e;
|
|
3252
3230
|
}
|
|
3253
3231
|
}
|
|
3254
|
-
for (const { mName, member } of
|
|
3255
|
-
if (!always && !
|
|
3232
|
+
for (const { mName, member } of lowerer.classMethodMembers(info)) {
|
|
3233
|
+
if (!always && !lowerer.wantBody(`%${className}.${mName}`))
|
|
3256
3234
|
continue;
|
|
3257
3235
|
try {
|
|
3258
|
-
const fn =
|
|
3236
|
+
const fn = lowerer.lowerClassMethodMember(info, member);
|
|
3259
3237
|
if (fn)
|
|
3260
3238
|
out.push(fn);
|
|
3261
3239
|
}
|
|
@@ -3265,15 +3243,15 @@ export function lowerClassMembers(L, info) {
|
|
|
3265
3243
|
}
|
|
3266
3244
|
}
|
|
3267
3245
|
for (const name of info.staticMethods?.keys() ?? []) {
|
|
3268
|
-
if (!
|
|
3246
|
+
if (!lowerer.wantBody(`%${className}.static:${name}`))
|
|
3269
3247
|
continue;
|
|
3270
|
-
const fn = lowerStaticMethod(
|
|
3248
|
+
const fn = lowerStaticMethod(lowerer, info, name);
|
|
3271
3249
|
if (fn)
|
|
3272
3250
|
out.push(fn);
|
|
3273
3251
|
}
|
|
3274
3252
|
for (const prop of info.throwingSetters) {
|
|
3275
|
-
if (always ||
|
|
3276
|
-
out.push(
|
|
3253
|
+
if (always || lowerer.wantBody(`%${className}.set:${prop}`))
|
|
3254
|
+
out.push(lowerer.throwingSetterFn(info, prop));
|
|
3277
3255
|
}
|
|
3278
3256
|
return out;
|
|
3279
3257
|
}
|
|
@@ -3281,17 +3259,17 @@ export function lowerClassMembers(L, info) {
|
|
|
3281
3259
|
* base class runs just its field initializers; a derived class inherits
|
|
3282
3260
|
* the base's signature — forward every param to super(), then run own
|
|
3283
3261
|
* field initializers. */
|
|
3284
|
-
export function lowerClassCtor(
|
|
3285
|
-
return withInstanceBindings(
|
|
3262
|
+
export function lowerClassCtor(lowerer, info) {
|
|
3263
|
+
return withInstanceBindings(lowerer, info, () => lowerClassCtorInner(lowerer, info));
|
|
3286
3264
|
}
|
|
3287
|
-
function lowerClassCtorInner(
|
|
3265
|
+
function lowerClassCtorInner(lowerer, info) {
|
|
3288
3266
|
const className = info.def.name;
|
|
3289
3267
|
const thisType = { kind: "object", className };
|
|
3290
|
-
const prevClass =
|
|
3291
|
-
|
|
3292
|
-
|
|
3268
|
+
const prevClass = lowerer.currentClass;
|
|
3269
|
+
lowerer.currentClass = info;
|
|
3270
|
+
lowerer.fnStack.push(newFnCtx(false, null, null, VOID));
|
|
3293
3271
|
try {
|
|
3294
|
-
const thisLocal =
|
|
3272
|
+
const thisLocal = lowerer.declareThis(thisType);
|
|
3295
3273
|
const params = [{ localId: thisLocal.id, name: "this", type: thisType }];
|
|
3296
3274
|
const body = [];
|
|
3297
3275
|
// The construction-relevant base: generic families are transparent
|
|
@@ -3306,17 +3284,17 @@ function lowerClassCtorInner(L, info) {
|
|
|
3306
3284
|
const loc = locOf(info.ctor);
|
|
3307
3285
|
const forward = info.ctorParams.map((shape, i) => {
|
|
3308
3286
|
const local = { id: `arg${i}.0`, name: `arg${i}`, type: shape.type, mutable: false };
|
|
3309
|
-
|
|
3287
|
+
lowerer.ctx.locals.push(local);
|
|
3310
3288
|
params.push({ localId: local.id, name: local.name, type: shape.type });
|
|
3311
3289
|
return { kind: "varRef", localId: local.id, type: shape.type, loc };
|
|
3312
3290
|
});
|
|
3313
|
-
body.push(...
|
|
3291
|
+
body.push(...lowerer.lowerDerivedCtorBody(info, thisLocal, forward));
|
|
3314
3292
|
}
|
|
3315
3293
|
else if (info.ctor) {
|
|
3316
3294
|
// The default-param prologue runs FIRST — before field initializers
|
|
3317
3295
|
// and (in a derived class) before super(): JS evaluates parameter
|
|
3318
3296
|
// defaults on entry, ahead of everything the body does.
|
|
3319
|
-
const declared =
|
|
3297
|
+
const declared = lowerer.declareParams(info.ctor.parameters, info.ctorParams);
|
|
3320
3298
|
params.push(...declared.params);
|
|
3321
3299
|
body.push(...declared.prologue);
|
|
3322
3300
|
if (!ctorBase) {
|
|
@@ -3324,13 +3302,13 @@ function lowerClassCtorInner(L, info) {
|
|
|
3324
3302
|
// of construction, the parameter-property assignments open the
|
|
3325
3303
|
// constructor body (probed — a field initializer reading a
|
|
3326
3304
|
// parameter property sees undefined).
|
|
3327
|
-
body.push(...
|
|
3328
|
-
body.push(...paramPropInitStmts(
|
|
3305
|
+
body.push(...lowerer.fieldInitStmts(info, thisLocal));
|
|
3306
|
+
body.push(...paramPropInitStmts(lowerer, info, thisLocal));
|
|
3329
3307
|
if (info.ctor.body)
|
|
3330
|
-
body.push(...
|
|
3308
|
+
body.push(...lowerer.lowerStmts(info.ctor.body.statements));
|
|
3331
3309
|
}
|
|
3332
3310
|
else if (info.ctor.body) {
|
|
3333
|
-
body.push(...
|
|
3311
|
+
body.push(...lowerer.lowerDerivedCtorBody(info, thisLocal));
|
|
3334
3312
|
}
|
|
3335
3313
|
}
|
|
3336
3314
|
else {
|
|
@@ -3342,12 +3320,12 @@ function lowerClassCtorInner(L, info) {
|
|
|
3342
3320
|
const loc = locOf(info.decl);
|
|
3343
3321
|
const superArgs = info.ctorParams.map((shape, i) => {
|
|
3344
3322
|
const local = { id: `arg${i}.0`, name: `arg${i}`, type: shape.type, mutable: false };
|
|
3345
|
-
|
|
3323
|
+
lowerer.ctx.locals.push(local);
|
|
3346
3324
|
params.push({ localId: local.id, name: local.name, type: shape.type });
|
|
3347
3325
|
return { kind: "varRef", localId: local.id, type: shape.type, loc };
|
|
3348
3326
|
});
|
|
3349
3327
|
try {
|
|
3350
|
-
body.push(
|
|
3328
|
+
body.push(lowerer.superCallStmt(info, thisLocal, superArgs, loc));
|
|
3351
3329
|
}
|
|
3352
3330
|
catch (e) {
|
|
3353
3331
|
// A synthesized super() can fence (a stream base whose
|
|
@@ -3357,20 +3335,20 @@ function lowerClassCtorInner(L, info) {
|
|
|
3357
3335
|
throw e;
|
|
3358
3336
|
}
|
|
3359
3337
|
}
|
|
3360
|
-
body.push(...
|
|
3338
|
+
body.push(...lowerer.fieldInitStmts(info, thisLocal));
|
|
3361
3339
|
}
|
|
3362
3340
|
return {
|
|
3363
3341
|
name: `%${className}.constructor`,
|
|
3364
3342
|
params,
|
|
3365
3343
|
returnType: VOID,
|
|
3366
|
-
locals:
|
|
3344
|
+
locals: lowerer.ctx.locals,
|
|
3367
3345
|
body,
|
|
3368
3346
|
loc: locOf(info.ctor ?? info.decl),
|
|
3369
3347
|
};
|
|
3370
3348
|
}
|
|
3371
3349
|
finally {
|
|
3372
|
-
|
|
3373
|
-
|
|
3350
|
+
lowerer.fnStack.pop();
|
|
3351
|
+
lowerer.currentClass = prevClass;
|
|
3374
3352
|
}
|
|
3375
3353
|
}
|
|
3376
3354
|
/** The lowered method-map name of a class member: identifier text, a
|
|
@@ -3382,7 +3360,7 @@ function lowerClassCtorInner(L, info) {
|
|
|
3382
3360
|
* accessor "get:x" convention; for-of, spreads, and array destructuring
|
|
3383
3361
|
* dispatch to it through the iterator protocol). Null for genuinely
|
|
3384
3362
|
* runtime-keyed names — the computed-member fences stay. */
|
|
3385
|
-
export function classMemberNameOf(
|
|
3363
|
+
export function classMemberNameOf(lowerer, name) {
|
|
3386
3364
|
if (ts.isIdentifier(name))
|
|
3387
3365
|
return name.text;
|
|
3388
3366
|
// #private methods key by their spelled name ('#m') — '#' is
|
|
@@ -3394,37 +3372,37 @@ export function classMemberNameOf(L, name) {
|
|
|
3394
3372
|
let e = name.expression;
|
|
3395
3373
|
while (ts.isParenthesizedExpression(e))
|
|
3396
3374
|
e = e.expression;
|
|
3397
|
-
if (ts.isPropertyAccessExpression(e) &&
|
|
3375
|
+
if (ts.isPropertyAccessExpression(e) && lowerer.stdlibGlobalMember(e, "Symbol") === "iterator") {
|
|
3398
3376
|
return "sym:iterator";
|
|
3399
3377
|
}
|
|
3400
|
-
return
|
|
3378
|
+
return lowerer.foldedStringKeyOf(name.expression);
|
|
3401
3379
|
}
|
|
3402
|
-
export function classIteratorOf(
|
|
3380
|
+
export function classIteratorOf(lowerer, t) {
|
|
3403
3381
|
if (t.kind !== "object")
|
|
3404
3382
|
return null;
|
|
3405
|
-
const info =
|
|
3383
|
+
const info = lowerer.classes.get(t.className);
|
|
3406
3384
|
if (!info)
|
|
3407
3385
|
return null;
|
|
3408
|
-
const iter = findMethodOn(
|
|
3386
|
+
const iter = findMethodOn(lowerer, info, "sym:iterator");
|
|
3409
3387
|
if (!iter || iter.sig.params.length !== 0 || iter.sig.abstract === true)
|
|
3410
3388
|
return null;
|
|
3411
3389
|
const iterT = iter.sig.ret;
|
|
3412
3390
|
if (iterT.kind !== "object")
|
|
3413
3391
|
return null;
|
|
3414
|
-
const itInfo =
|
|
3392
|
+
const itInfo = lowerer.classes.get(iterT.className);
|
|
3415
3393
|
if (!itInfo)
|
|
3416
3394
|
return null;
|
|
3417
3395
|
// IteratorClose honesty: a declared return()/throw() would be called
|
|
3418
3396
|
// by JS on abrupt completion; these desugars never close.
|
|
3419
|
-
if (findMethodOn(
|
|
3397
|
+
if (findMethodOn(lowerer, itInfo, "return") || findMethodOn(lowerer, itInfo, "throw"))
|
|
3420
3398
|
return null;
|
|
3421
|
-
const next = findMethodOn(
|
|
3399
|
+
const next = findMethodOn(lowerer, itInfo, "next");
|
|
3422
3400
|
if (!next || next.sig.params.length !== 0 || next.sig.abstract === true)
|
|
3423
3401
|
return null;
|
|
3424
3402
|
const resultT = next.sig.ret;
|
|
3425
3403
|
if (resultT.kind !== "record")
|
|
3426
3404
|
return null;
|
|
3427
|
-
const shape =
|
|
3405
|
+
const shape = lowerer.shapes.get(resultT.shapeId);
|
|
3428
3406
|
const value = shape?.fields.find((f) => f.name === "value");
|
|
3429
3407
|
if (!shape || !value)
|
|
3430
3408
|
return null;
|
|
@@ -3435,12 +3413,12 @@ export function classIteratorOf(L, t) {
|
|
|
3435
3413
|
}
|
|
3436
3414
|
/** The `it.next()` step of a class iterator as an ordinary (possibly
|
|
3437
3415
|
* virtual) method call. */
|
|
3438
|
-
export function classIteratorNextCall(
|
|
3439
|
-
return accessorCall(
|
|
3416
|
+
export function classIteratorNextCall(lowerer, cit, itRef, loc) {
|
|
3417
|
+
return accessorCall(lowerer, cit.iterT.className, "next", itRef, [], cit.resultT, loc);
|
|
3440
3418
|
}
|
|
3441
3419
|
/** `recv[Symbol.iterator]()` as an ordinary method call. */
|
|
3442
|
-
export function classIteratorOpenCall(
|
|
3443
|
-
return accessorCall(
|
|
3420
|
+
export function classIteratorOpenCall(lowerer, cit, recv, loc) {
|
|
3421
|
+
return accessorCall(lowerer, cit.className, "sym:iterator", recv, [], cit.iterT, loc);
|
|
3444
3422
|
}
|
|
3445
3423
|
/** `[...new C]` / `f(...new C)` over a CLASS ITERABLE: the eager drain —
|
|
3446
3424
|
* an interned `%iter.drain.<n>(recv)` lifted function running the
|
|
@@ -3451,23 +3429,23 @@ export function classIteratorOpenCall(L, cit, recv, loc) {
|
|
|
3451
3429
|
* `(number | symbol)[]`) pushes each value wrapped into its arm. Null
|
|
3452
3430
|
* when the value isn't a recognized class iterable or the element
|
|
3453
3431
|
* doesn't coerce — spread fences stay. */
|
|
3454
|
-
export function classIteratorDrainCall(
|
|
3455
|
-
const cit = classIteratorOf(
|
|
3432
|
+
export function classIteratorDrainCall(lowerer, src, loc, elemT) {
|
|
3433
|
+
const cit = classIteratorOf(lowerer, src.type);
|
|
3456
3434
|
if (!cit)
|
|
3457
3435
|
return null;
|
|
3458
3436
|
const outElem = elemT ?? cit.valueT;
|
|
3459
3437
|
// Probe the element coercion purely: identical types, or an arm of a
|
|
3460
3438
|
// union destination (the wrap coerceToExpected applies below).
|
|
3461
3439
|
if (!typeEquals(outElem, cit.valueT)) {
|
|
3462
|
-
if (outElem.kind !== "union" ||
|
|
3440
|
+
if (outElem.kind !== "union" || lowerer.armTag(outElem.unionId, cit.valueT) < 0)
|
|
3463
3441
|
return null;
|
|
3464
3442
|
}
|
|
3465
3443
|
const outT = arrayOf(outElem);
|
|
3466
3444
|
const key = `${cit.className}:${typeKey(outElem)}`;
|
|
3467
|
-
let name =
|
|
3445
|
+
let name = lowerer.iterDrainHelpers.get(key);
|
|
3468
3446
|
if (!name) {
|
|
3469
|
-
name = `%iter.drain.${
|
|
3470
|
-
|
|
3447
|
+
name = `%iter.drain.${lowerer.iterDrainHelpers.size}`;
|
|
3448
|
+
lowerer.iterDrainHelpers.set(key, name);
|
|
3471
3449
|
const recvT = { kind: "object", className: cit.className };
|
|
3472
3450
|
const recvRef = { kind: "varRef", localId: "r.0", type: recvT, loc };
|
|
3473
3451
|
const itRef = { kind: "varRef", localId: "it.0", type: cit.iterT, loc };
|
|
@@ -3475,7 +3453,7 @@ export function classIteratorDrainCall(L, src, loc, elemT) {
|
|
|
3475
3453
|
const resRef = { kind: "varRef", localId: "res.0", type: cit.resultT, loc };
|
|
3476
3454
|
const valueRead = { kind: "recordGet", obj: resRef, shapeId: cit.resultT.shapeId, field: "value", type: cit.valueT, loc };
|
|
3477
3455
|
const loop = [
|
|
3478
|
-
{ kind: "varDecl", localId: "res.0", init: classIteratorNextCall(
|
|
3456
|
+
{ kind: "varDecl", localId: "res.0", init: classIteratorNextCall(lowerer, cit, itRef, loc), loc },
|
|
3479
3457
|
...(cit.hasDone
|
|
3480
3458
|
? [
|
|
3481
3459
|
{
|
|
@@ -3493,14 +3471,14 @@ export function classIteratorDrainCall(L, src, loc, elemT) {
|
|
|
3493
3471
|
kind: "arrIntrinsic",
|
|
3494
3472
|
method: "push",
|
|
3495
3473
|
receiver: outRef,
|
|
3496
|
-
args: [
|
|
3474
|
+
args: [lowerer.coerceToExpected(valueRead, outElem)],
|
|
3497
3475
|
type: F64,
|
|
3498
3476
|
loc,
|
|
3499
3477
|
},
|
|
3500
3478
|
loc,
|
|
3501
3479
|
},
|
|
3502
3480
|
];
|
|
3503
|
-
|
|
3481
|
+
lowerer.liftedFns.push({
|
|
3504
3482
|
name,
|
|
3505
3483
|
params: [{ localId: "r.0", name: "r", type: recvT }],
|
|
3506
3484
|
returnType: outT,
|
|
@@ -3512,7 +3490,7 @@ export function classIteratorDrainCall(L, src, loc, elemT) {
|
|
|
3512
3490
|
],
|
|
3513
3491
|
body: [
|
|
3514
3492
|
{ kind: "varDecl", localId: "out.0", init: { kind: "arrayLit", elems: [], type: outT, loc }, loc },
|
|
3515
|
-
{ kind: "varDecl", localId: "it.0", init: classIteratorOpenCall(
|
|
3493
|
+
{ kind: "varDecl", localId: "it.0", init: classIteratorOpenCall(lowerer, cit, recvRef, loc), loc },
|
|
3516
3494
|
{
|
|
3517
3495
|
kind: "while",
|
|
3518
3496
|
cond: { kind: "boolLit", value: true, type: BOOL, loc },
|
|
@@ -3535,18 +3513,18 @@ export function classIteratorDrainCall(L, src, loc, elemT) {
|
|
|
3535
3513
|
/** The tail of a class iterable's protocol from an already-open ITERATOR
|
|
3536
3514
|
* object (`var [a, ...rest] = new C` — the rest element drains whatever
|
|
3537
3515
|
* next() still yields): the drain loop keyed by the iterator class. */
|
|
3538
|
-
export function classIteratorRestDrainCall(
|
|
3516
|
+
export function classIteratorRestDrainCall(lowerer, cit, itVal, loc) {
|
|
3539
3517
|
const outT = arrayOf(cit.valueT);
|
|
3540
3518
|
const key = `it:${cit.iterT.className}`;
|
|
3541
|
-
let name =
|
|
3519
|
+
let name = lowerer.iterDrainHelpers.get(key);
|
|
3542
3520
|
if (!name) {
|
|
3543
|
-
name = `%iter.drain.${
|
|
3544
|
-
|
|
3521
|
+
name = `%iter.drain.${lowerer.iterDrainHelpers.size}`;
|
|
3522
|
+
lowerer.iterDrainHelpers.set(key, name);
|
|
3545
3523
|
const itRef = { kind: "varRef", localId: "it.0", type: cit.iterT, loc };
|
|
3546
3524
|
const outRef = { kind: "varRef", localId: "out.0", type: outT, loc };
|
|
3547
3525
|
const resRef = { kind: "varRef", localId: "res.0", type: cit.resultT, loc };
|
|
3548
3526
|
const loop = [
|
|
3549
|
-
{ kind: "varDecl", localId: "res.0", init: classIteratorNextCall(
|
|
3527
|
+
{ kind: "varDecl", localId: "res.0", init: classIteratorNextCall(lowerer, cit, itRef, loc), loc },
|
|
3550
3528
|
...(cit.hasDone
|
|
3551
3529
|
? [
|
|
3552
3530
|
{
|
|
@@ -3571,7 +3549,7 @@ export function classIteratorRestDrainCall(L, cit, itVal, loc) {
|
|
|
3571
3549
|
loc,
|
|
3572
3550
|
},
|
|
3573
3551
|
];
|
|
3574
|
-
|
|
3552
|
+
lowerer.liftedFns.push({
|
|
3575
3553
|
name,
|
|
3576
3554
|
params: [{ localId: "it.0", name: "it", type: cit.iterT }],
|
|
3577
3555
|
returnType: outT,
|
|
@@ -3597,13 +3575,13 @@ export function classIteratorRestDrainCall(L, cit, itVal, loc) {
|
|
|
3597
3575
|
/** One method or accessor body as its module function `%C.name`
|
|
3598
3576
|
* (accessors are methods with property syntax: "get:x"/"set:x" entries —
|
|
3599
3577
|
* see collectClassShape). */
|
|
3600
|
-
export function lowerClassMethodMember(
|
|
3601
|
-
return withInstanceBindings(
|
|
3578
|
+
export function lowerClassMethodMember(lowerer, info, fnLike) {
|
|
3579
|
+
return withInstanceBindings(lowerer, info, () => lowerClassMethodMemberInner(lowerer, info, fnLike));
|
|
3602
3580
|
}
|
|
3603
|
-
function lowerClassMethodMemberInner(
|
|
3581
|
+
function lowerClassMethodMemberInner(lowerer, info, fnLike) {
|
|
3604
3582
|
const className = info.def.name;
|
|
3605
3583
|
const thisType = { kind: "object", className };
|
|
3606
|
-
const memberName = ts.isMethodDeclaration(fnLike) ? classMemberNameOf(
|
|
3584
|
+
const memberName = ts.isMethodDeclaration(fnLike) ? classMemberNameOf(lowerer, fnLike.name) : ts.isIdentifier(fnLike.name) || ts.isPrivateIdentifier(fnLike.name) ? fnLike.name.text : null;
|
|
3607
3585
|
if (memberName === null)
|
|
3608
3586
|
return null;
|
|
3609
3587
|
const mName = ts.isMethodDeclaration(fnLike)
|
|
@@ -3612,8 +3590,8 @@ function lowerClassMethodMemberInner(L, info, fnLike) {
|
|
|
3612
3590
|
const sig = info.methods.get(mName);
|
|
3613
3591
|
if (!sig || !fnLike.body)
|
|
3614
3592
|
return null;
|
|
3615
|
-
const prevClass =
|
|
3616
|
-
|
|
3593
|
+
const prevClass = lowerer.currentClass;
|
|
3594
|
+
lowerer.currentClass = info;
|
|
3617
3595
|
// ASYNC methods: the module function is an async IrFunction — its
|
|
3618
3596
|
// body returns the promise's INNER type (a `return v` fulfills with
|
|
3619
3597
|
// v) and every call enters through the emitted fiber spawn wrapper
|
|
@@ -3630,26 +3608,26 @@ function lowerClassMethodMemberInner(L, info, fnLike) {
|
|
|
3630
3608
|
const bodyReturn = isAsync && sig.ret.kind === "promise"
|
|
3631
3609
|
? sig.ret.inner
|
|
3632
3610
|
: genCh !== null
|
|
3633
|
-
?
|
|
3611
|
+
? lowerer.genBodyReturnType(sig.ret)
|
|
3634
3612
|
: sig.ret;
|
|
3635
3613
|
const fnCtx = newFnCtx(false, null, null, bodyReturn);
|
|
3636
3614
|
fnCtx.isAsync = isAsync;
|
|
3637
3615
|
if (genCh !== null)
|
|
3638
3616
|
fnCtx.generator = genCh;
|
|
3639
|
-
|
|
3617
|
+
lowerer.fnStack.push(fnCtx);
|
|
3640
3618
|
try {
|
|
3641
|
-
const thisLocal =
|
|
3619
|
+
const thisLocal = lowerer.declareThis(thisType);
|
|
3642
3620
|
const params = [{ localId: thisLocal.id, name: "this", type: thisType }];
|
|
3643
3621
|
// `this` is declared first, so method parameter DEFAULTS may use it
|
|
3644
3622
|
// (JS allows this in method defaults; it is param 0 here).
|
|
3645
|
-
const declared =
|
|
3623
|
+
const declared = lowerer.declareParams(fnLike.parameters, sig.params);
|
|
3646
3624
|
params.push(...declared.params);
|
|
3647
|
-
const body = [...declared.prologue, ...
|
|
3625
|
+
const body = [...declared.prologue, ...lowerer.lowerStmts(fnLike.body.statements)];
|
|
3648
3626
|
const fn = {
|
|
3649
3627
|
name: `%${className}.${mName}`,
|
|
3650
3628
|
params,
|
|
3651
3629
|
returnType: bodyReturn,
|
|
3652
|
-
locals:
|
|
3630
|
+
locals: lowerer.ctx.locals,
|
|
3653
3631
|
body,
|
|
3654
3632
|
loc: locOf(fnLike),
|
|
3655
3633
|
};
|
|
@@ -3660,8 +3638,8 @@ function lowerClassMethodMemberInner(L, info, fnLike) {
|
|
|
3660
3638
|
return fn;
|
|
3661
3639
|
}
|
|
3662
3640
|
finally {
|
|
3663
|
-
|
|
3664
|
-
|
|
3641
|
+
lowerer.fnStack.pop();
|
|
3642
|
+
lowerer.currentClass = prevClass;
|
|
3665
3643
|
}
|
|
3666
3644
|
}
|
|
3667
3645
|
/** One static method body as its module function `%C.static:m` — an
|
|
@@ -3671,22 +3649,10 @@ function lowerClassMethodMemberInner(L, info, fnLike) {
|
|
|
3671
3649
|
* named fences, with arrow functions transparent (they inherit the
|
|
3672
3650
|
* method's `this`) and this-binding function forms opaque — the static-
|
|
3673
3651
|
* block rule verbatim. */
|
|
3674
|
-
export function lowerStaticMethod(
|
|
3652
|
+
export function lowerStaticMethod(lowerer, info, name) {
|
|
3675
3653
|
const entry = info.staticMethods?.get(name);
|
|
3676
3654
|
if (!entry?.member.body)
|
|
3677
3655
|
return null;
|
|
3678
|
-
const checkThis = (n) => {
|
|
3679
|
-
if (ts.isFunctionExpression(n) || ts.isFunctionDeclaration(n) ||
|
|
3680
|
-
ts.isMethodDeclaration(n) || ts.isConstructorDeclaration(n) ||
|
|
3681
|
-
ts.isGetAccessor(n) || ts.isSetAccessor(n) ||
|
|
3682
|
-
ts.isClassDeclaration(n) || ts.isClassExpression(n)) {
|
|
3683
|
-
return;
|
|
3684
|
-
}
|
|
3685
|
-
if (n.kind === ts.SyntaxKind.ThisKeyword || n.kind === ts.SyntaxKind.SuperKeyword) {
|
|
3686
|
-
L.unsupported("SC1090", n, `'${n.kind === ts.SyntaxKind.ThisKeyword ? "this" : "super"}' in static methods (it names the RECEIVER class — a dynamic value; reference the class by name instead)`);
|
|
3687
|
-
}
|
|
3688
|
-
n.forEachChild(checkThis);
|
|
3689
|
-
};
|
|
3690
3656
|
// Async statics: an async IrFunction like any module function — the
|
|
3691
3657
|
// body returns the promise's INNER type, calls enter through the
|
|
3692
3658
|
// fiber spawn wrapper (callTargetC routes by fn.async).
|
|
@@ -3695,16 +3661,16 @@ export function lowerStaticMethod(L, info, name) {
|
|
|
3695
3661
|
const bodyReturn = isAsync && entry.ret.kind === "promise" ? entry.ret.inner : entry.ret;
|
|
3696
3662
|
const fnCtx = newFnCtx(false, null, null, bodyReturn);
|
|
3697
3663
|
fnCtx.isAsync = isAsync;
|
|
3698
|
-
|
|
3664
|
+
lowerer.fnStack.push(fnCtx);
|
|
3699
3665
|
try {
|
|
3700
|
-
entry.member.body
|
|
3701
|
-
const declared =
|
|
3702
|
-
const body = [...declared.prologue, ...
|
|
3666
|
+
rejectStaticThis(lowerer, entry.member.body, (keyword) => `'${keyword}' in static methods (it names the RECEIVER class — a dynamic value; reference the class by name instead)`);
|
|
3667
|
+
const declared = lowerer.declareParams(entry.member.parameters, entry.params);
|
|
3668
|
+
const body = [...declared.prologue, ...lowerer.lowerStmts(entry.member.body.statements)];
|
|
3703
3669
|
const fn = {
|
|
3704
3670
|
name: `%${info.def.name}.static:${name}`,
|
|
3705
3671
|
params: declared.params,
|
|
3706
3672
|
returnType: bodyReturn,
|
|
3707
|
-
locals:
|
|
3673
|
+
locals: lowerer.ctx.locals,
|
|
3708
3674
|
body,
|
|
3709
3675
|
loc: locOf(entry.member),
|
|
3710
3676
|
};
|
|
@@ -3722,7 +3688,7 @@ export function lowerStaticMethod(L, info, name) {
|
|
|
3722
3688
|
return null;
|
|
3723
3689
|
}
|
|
3724
3690
|
finally {
|
|
3725
|
-
|
|
3691
|
+
lowerer.fnStack.pop();
|
|
3726
3692
|
}
|
|
3727
3693
|
}
|
|
3728
3694
|
/** A synthesized throwing setter: a getter-only override shadows the
|
|
@@ -3730,7 +3696,7 @@ export function lowerStaticMethod(L, info, name) {
|
|
|
3730
3696
|
* throw exactly like Node's TypeError — a real instance (a typed catch's
|
|
3731
3697
|
* `e instanceof TypeError` matches), catchable, exit 1 uncaught (message
|
|
3732
3698
|
* text is compiler-worded; stdout and exit code are the contract). */
|
|
3733
|
-
export function throwingSetterFn(
|
|
3699
|
+
export function throwingSetterFn(lowerer, info, prop) {
|
|
3734
3700
|
const className = info.def.name;
|
|
3735
3701
|
const thisType = { kind: "object", className };
|
|
3736
3702
|
const sig = info.methods.get(`set:${prop}`);
|
|
@@ -3773,23 +3739,23 @@ export function throwingSetterFn(L, info, prop) {
|
|
|
3773
3739
|
* null when the generic class extends nothing, exactly the source's
|
|
3774
3740
|
* story (tsc forbids super() there). Ordinary classes answer their base
|
|
3775
3741
|
* unchanged. */
|
|
3776
|
-
|
|
3742
|
+
function superBaseOf(info) {
|
|
3777
3743
|
const b = info.base;
|
|
3778
3744
|
return b?.generic ? b.base : b;
|
|
3779
3745
|
}
|
|
3780
3746
|
/** The class's OWN field initializers as fieldSet statements (declaration
|
|
3781
3747
|
* order) — a base constructor's prologue, a derived constructor's
|
|
3782
3748
|
* super()-return continuation. */
|
|
3783
|
-
export function fieldInitStmts(
|
|
3749
|
+
export function fieldInitStmts(lowerer, info, thisLocal) {
|
|
3784
3750
|
const out = [];
|
|
3785
3751
|
const thisType = { kind: "object", className: info.def.name };
|
|
3786
3752
|
for (const f of info.fieldOrder) {
|
|
3787
3753
|
if (!f.initializer)
|
|
3788
3754
|
continue;
|
|
3789
|
-
|
|
3790
|
-
|
|
3755
|
+
lowerer.stats.statementsTotal++;
|
|
3756
|
+
lowerer.bumpFileStat(locOf(f.initializer).file, "total");
|
|
3791
3757
|
try {
|
|
3792
|
-
const value =
|
|
3758
|
+
const value = lowerer.lowerExprExpecting(f.initializer, f.type);
|
|
3793
3759
|
out.push({
|
|
3794
3760
|
kind: "fieldSet",
|
|
3795
3761
|
obj: { kind: "varRef", localId: thisLocal.id, type: thisType, loc: locOf(f.initializer) },
|
|
@@ -3802,8 +3768,8 @@ export function fieldInitStmts(L, info, thisLocal) {
|
|
|
3802
3768
|
catch (e) {
|
|
3803
3769
|
if (!(e instanceof PoisonError))
|
|
3804
3770
|
throw e;
|
|
3805
|
-
|
|
3806
|
-
|
|
3771
|
+
lowerer.stats.statementsFailed++;
|
|
3772
|
+
lowerer.bumpFileStat(locOf(f.initializer).file, "failed");
|
|
3807
3773
|
}
|
|
3808
3774
|
}
|
|
3809
3775
|
return out;
|
|
@@ -3817,16 +3783,16 @@ export function fieldInitStmts(L, info, thisLocal) {
|
|
|
3817
3783
|
* Each assignment reads the parameter's BODY local (defaults already
|
|
3818
3784
|
* applied by the declareParams prologue), whose type the collection made
|
|
3819
3785
|
* the field's type — slot-exact by construction. */
|
|
3820
|
-
|
|
3786
|
+
function paramPropInitStmts(lowerer, info, thisLocal) {
|
|
3821
3787
|
const out = [];
|
|
3822
3788
|
const thisType = { kind: "object", className: info.def.name };
|
|
3823
3789
|
for (const pp of info.paramProps ?? []) {
|
|
3824
3790
|
const loc = locOf(pp.param);
|
|
3825
|
-
const local = ts.isIdentifier(pp.param.name) ?
|
|
3791
|
+
const local = ts.isIdentifier(pp.param.name) ? lowerer.resolveLocal(pp.param.name) : null;
|
|
3826
3792
|
if (!local || !typeEquals(local.type, pp.type)) {
|
|
3827
3793
|
// Defensive: collection derived the field type from the same
|
|
3828
3794
|
// paramShape the ctor's declareParams bound — they cannot diverge.
|
|
3829
|
-
|
|
3795
|
+
lowerer.unsupported("SC1090", pp.param, "this parameter property form");
|
|
3830
3796
|
}
|
|
3831
3797
|
out.push({
|
|
3832
3798
|
kind: "fieldSet",
|
|
@@ -3846,7 +3812,7 @@ export function paramPropInitStmts(L, info, thisLocal) {
|
|
|
3846
3812
|
* guarantees a super call exists and runs before any this-use; the
|
|
3847
3813
|
* supported form is a top-level expression statement — anywhere else
|
|
3848
3814
|
* (conditionals, expression positions) is rejected, not misordered. */
|
|
3849
|
-
export function lowerDerivedCtorBody(
|
|
3815
|
+
export function lowerDerivedCtorBody(lowerer, info, thisLocal,
|
|
3850
3816
|
/** Mixin forwarding-constructor mode: `super(...args)` forwards these
|
|
3851
3817
|
* pre-declared synthetic params directly (the spread never lowers —
|
|
3852
3818
|
* the base's ABI is this constructor's ABI). */
|
|
@@ -3860,49 +3826,49 @@ forward) {
|
|
|
3860
3826
|
? stmt.expression
|
|
3861
3827
|
: null;
|
|
3862
3828
|
if (!superCall) {
|
|
3863
|
-
out.push(...
|
|
3829
|
+
out.push(...lowerer.lowerStmts([stmt]));
|
|
3864
3830
|
continue;
|
|
3865
3831
|
}
|
|
3866
|
-
if (!
|
|
3867
|
-
|
|
3868
|
-
|
|
3832
|
+
if (!lowerer.suppressStats) {
|
|
3833
|
+
lowerer.stats.statementsTotal++;
|
|
3834
|
+
lowerer.bumpFileStat(locOf(stmt).file, "total");
|
|
3869
3835
|
}
|
|
3870
3836
|
try {
|
|
3871
3837
|
if (superSeen)
|
|
3872
|
-
|
|
3838
|
+
lowerer.unsupported("SC1090", stmt, "multiple super() calls");
|
|
3873
3839
|
superSeen = true;
|
|
3874
3840
|
const base = superBaseOf(info);
|
|
3875
3841
|
if (base.builtinEmitter && superCall.arguments.length > 0) {
|
|
3876
3842
|
// @types/node admits super({ captureRejections }) — no lowering.
|
|
3877
|
-
|
|
3843
|
+
lowerer.unsupported("SC1090", superCall, "EventEmitter constructor options ('captureRejections')");
|
|
3878
3844
|
}
|
|
3879
3845
|
if (base.builtinStream) {
|
|
3880
3846
|
// super(options?) into a runtime stream base: the stream spoke
|
|
3881
3847
|
// parses the options and binds overridden underscore methods.
|
|
3882
|
-
out.push(...lowerStreamSuperCall(
|
|
3883
|
-
out.push(...
|
|
3884
|
-
out.push(...paramPropInitStmts(
|
|
3848
|
+
out.push(...lowerStreamSuperCall(lowerer, info, base, superCall.arguments, thisLocal, locOf(stmt), stmt));
|
|
3849
|
+
out.push(...lowerer.fieldInitStmts(info, thisLocal));
|
|
3850
|
+
out.push(...paramPropInitStmts(lowerer, info, thisLocal));
|
|
3885
3851
|
continue;
|
|
3886
3852
|
}
|
|
3887
3853
|
const args = forward !== undefined
|
|
3888
3854
|
? forward
|
|
3889
3855
|
: base.builtinError
|
|
3890
|
-
? [
|
|
3856
|
+
? [lowerer.errorMessageArg(superCall.arguments, locOf(stmt), stmt)]
|
|
3891
3857
|
: base.builtinEmitter
|
|
3892
3858
|
? []
|
|
3893
|
-
:
|
|
3894
|
-
out.push(
|
|
3859
|
+
: lowerer.completeArgs(superCall.arguments, base.ctorParams, locOf(stmt), stmt);
|
|
3860
|
+
out.push(lowerer.superCallStmt(info, thisLocal, args, locOf(stmt)));
|
|
3895
3861
|
// super() returns → field initializers → parameter-property
|
|
3896
3862
|
// assignments (Node's order, probed) → the rest of the body.
|
|
3897
|
-
out.push(...
|
|
3898
|
-
out.push(...paramPropInitStmts(
|
|
3863
|
+
out.push(...lowerer.fieldInitStmts(info, thisLocal));
|
|
3864
|
+
out.push(...paramPropInitStmts(lowerer, info, thisLocal));
|
|
3899
3865
|
}
|
|
3900
3866
|
catch (e) {
|
|
3901
3867
|
if (!(e instanceof PoisonError))
|
|
3902
3868
|
throw e;
|
|
3903
|
-
if (!
|
|
3904
|
-
|
|
3905
|
-
|
|
3869
|
+
if (!lowerer.suppressStats) {
|
|
3870
|
+
lowerer.stats.statementsFailed++;
|
|
3871
|
+
lowerer.bumpFileStat(locOf(stmt).file, "failed");
|
|
3906
3872
|
}
|
|
3907
3873
|
}
|
|
3908
3874
|
}
|
|
@@ -3910,14 +3876,14 @@ forward) {
|
|
|
3910
3876
|
// tsc guarantees the call exists somewhere; if it wasn't a top-level
|
|
3911
3877
|
// statement the per-site rejection above already fired — this is the
|
|
3912
3878
|
// constructor-level backstop so a half-initialized ctor never emits.
|
|
3913
|
-
|
|
3879
|
+
lowerer.pushDiag(unsupportedDiag("SC1090", locOf(info.ctor), "super() calls anywhere but as a top-level constructor statement"));
|
|
3914
3880
|
}
|
|
3915
3881
|
return out;
|
|
3916
3882
|
}
|
|
3917
3883
|
/** `super(args)` → direct call of the base constructor with the SAME
|
|
3918
3884
|
* `this` (upcast; retained by the varRef read — the callee owns and
|
|
3919
3885
|
* releases its param per the universal convention). */
|
|
3920
|
-
export function superCallStmt(
|
|
3886
|
+
export function superCallStmt(lowerer, info, thisLocal, args, loc) {
|
|
3921
3887
|
const base = superBaseOf(info);
|
|
3922
3888
|
const thisRef = {
|
|
3923
3889
|
kind: "varRef",
|
|
@@ -3935,7 +3901,7 @@ export function superCallStmt(L, info, thisLocal, args, loc) {
|
|
|
3935
3901
|
expr: {
|
|
3936
3902
|
kind: "libCall",
|
|
3937
3903
|
fn: "error.ctor",
|
|
3938
|
-
args: [
|
|
3904
|
+
args: [lowerer.upcastTo(thisRef, base.def.name), ...args],
|
|
3939
3905
|
type: VOID,
|
|
3940
3906
|
loc,
|
|
3941
3907
|
},
|
|
@@ -3952,7 +3918,7 @@ export function superCallStmt(L, info, thisLocal, args, loc) {
|
|
|
3952
3918
|
expr: {
|
|
3953
3919
|
kind: "libCall",
|
|
3954
3920
|
fn: "emitter.ctor",
|
|
3955
|
-
args: [
|
|
3921
|
+
args: [lowerer.upcastTo(thisRef, base.def.name)],
|
|
3956
3922
|
type: VOID,
|
|
3957
3923
|
loc,
|
|
3958
3924
|
},
|
|
@@ -3964,15 +3930,15 @@ export function superCallStmt(L, info, thisLocal, args, loc) {
|
|
|
3964
3930
|
// super() with default options (underscore methods still bind; a
|
|
3965
3931
|
// construction passing options requires a declared constructor —
|
|
3966
3932
|
// lowerNew fences that). Zero options ⇒ exactly one init stmt.
|
|
3967
|
-
return lowerStreamSuperCall(
|
|
3933
|
+
return lowerStreamSuperCall(lowerer, info, base, [], thisLocal, loc, info.decl ?? info.ctor)[0];
|
|
3968
3934
|
}
|
|
3969
|
-
|
|
3935
|
+
lowerer.noteEdge(`%${base.def.name}.constructor`);
|
|
3970
3936
|
return {
|
|
3971
3937
|
kind: "exprStmt",
|
|
3972
3938
|
expr: {
|
|
3973
3939
|
kind: "call",
|
|
3974
3940
|
callee: `%${base.def.name}.constructor`,
|
|
3975
|
-
args: [
|
|
3941
|
+
args: [lowerer.upcastTo(thisRef, base.def.name), ...args],
|
|
3976
3942
|
type: VOID,
|
|
3977
3943
|
loc,
|
|
3978
3944
|
},
|
|
@@ -3982,37 +3948,37 @@ export function superCallStmt(L, info, thisLocal, args, loc) {
|
|
|
3982
3948
|
/** `super.method(args)`: the base chain's implementation, called
|
|
3983
3949
|
* DIRECTLY over this method's own `this` (upcast to the declarer) —
|
|
3984
3950
|
* super dispatch is static in JS too, never through the dynamic class. */
|
|
3985
|
-
export function lowerSuperMethodCall(
|
|
3986
|
-
const cls =
|
|
3951
|
+
export function lowerSuperMethodCall(lowerer, call, access) {
|
|
3952
|
+
const cls = lowerer.currentClass;
|
|
3987
3953
|
if (!cls?.base) {
|
|
3988
3954
|
// tsc rejects super outside derived-class bodies first; defensive.
|
|
3989
|
-
|
|
3955
|
+
lowerer.unsupported("SC1090", access, "'super' outside a derived class");
|
|
3990
3956
|
}
|
|
3991
3957
|
// An emit-override SPECIALIZATION body's forward — `super.emit(event,
|
|
3992
3958
|
// ...args)` — carries no literal event name; the specialization's own
|
|
3993
3959
|
// context answers it (matched before any lookup: neither identifier
|
|
3994
3960
|
// resolves through the ordinary lowering).
|
|
3995
|
-
const forward = emitSpecSuperForward(
|
|
3961
|
+
const forward = emitSpecSuperForward(lowerer, call, access);
|
|
3996
3962
|
if (forward)
|
|
3997
3963
|
return forward;
|
|
3998
|
-
const found =
|
|
3964
|
+
const found = lowerer.findMethodOn(cls.base, access.name.text);
|
|
3999
3965
|
if (!found) {
|
|
4000
3966
|
// `super.m(...)` of a GENERIC method: super dispatch is static in JS
|
|
4001
3967
|
// too, so the base chain's declaration answers unconditionally — the
|
|
4002
3968
|
// ordinary instantiation route over this method's own `this`.
|
|
4003
|
-
const gfound = findGenericMethodOn(
|
|
3969
|
+
const gfound = findGenericMethodOn(lowerer, cls.base, access.name.text);
|
|
4004
3970
|
if (gfound) {
|
|
4005
|
-
const thisL =
|
|
3971
|
+
const thisL = lowerer.resolveThis();
|
|
4006
3972
|
if (!thisL)
|
|
4007
|
-
|
|
4008
|
-
const instance = genericCallInstance(
|
|
3973
|
+
lowerer.unsupported("SC1080", access);
|
|
3974
|
+
const instance = genericCallInstance(lowerer, call, gfound.info);
|
|
4009
3975
|
const loc = locOf(call);
|
|
4010
3976
|
const thisRef = { kind: "varRef", localId: thisL.id, type: thisL.type, loc };
|
|
4011
|
-
const args =
|
|
3977
|
+
const args = lowerer.completeArgs(call.arguments, instance.params, loc, call);
|
|
4012
3978
|
return {
|
|
4013
3979
|
kind: "call",
|
|
4014
3980
|
callee: instance.name,
|
|
4015
|
-
args: [
|
|
3981
|
+
args: [lowerer.upcastTo(thisRef, gfound.declarer.def.name), ...args],
|
|
4016
3982
|
type: instance.returnType,
|
|
4017
3983
|
loc,
|
|
4018
3984
|
};
|
|
@@ -4023,43 +3989,43 @@ export function lowerSuperMethodCall(L, call, access) {
|
|
|
4023
3989
|
// spoke lowers with this method's own `this` as the receiver (an
|
|
4024
3990
|
// emit override at-or-below `cls` never answers; the nearest one
|
|
4025
3991
|
// strictly above does).
|
|
4026
|
-
if (EMITTER_API_MEMBERS.has(access.name.text) && emitterRooted(
|
|
4027
|
-
const viaEmitter = lowerEmitterSuperCall(
|
|
3992
|
+
if (EMITTER_API_MEMBERS.has(access.name.text) && emitterRooted(lowerer, cls.base)) {
|
|
3993
|
+
const viaEmitter = lowerEmitterSuperCall(lowerer, call, access, cls);
|
|
4028
3994
|
if (viaEmitter)
|
|
4029
3995
|
return viaEmitter;
|
|
4030
3996
|
}
|
|
4031
|
-
|
|
3997
|
+
lowerer.unsupported("SC1090", access, `'super.${access.name.text}' (no base class declares it)`);
|
|
4032
3998
|
}
|
|
4033
3999
|
// tsc rejects super-access of abstract members (TS2513); defensive —
|
|
4034
4000
|
// no function exists behind an abstract declaration.
|
|
4035
4001
|
if (found.sig.abstract === true) {
|
|
4036
|
-
|
|
4002
|
+
lowerer.unsupported("SC1090", access, `'super.${access.name.text}' of an abstract method`);
|
|
4037
4003
|
}
|
|
4038
|
-
const thisLocal =
|
|
4004
|
+
const thisLocal = lowerer.resolveThis();
|
|
4039
4005
|
if (!thisLocal)
|
|
4040
|
-
|
|
4041
|
-
|
|
4006
|
+
lowerer.unsupported("SC1080", access);
|
|
4007
|
+
lowerer.noteEdge(`%${found.declarer.def.name}.${access.name.text}`);
|
|
4042
4008
|
const loc = locOf(call);
|
|
4043
4009
|
const thisRef = { kind: "varRef", localId: thisLocal.id, type: thisLocal.type, loc };
|
|
4044
|
-
const args =
|
|
4010
|
+
const args = lowerer.completeArgs(call.arguments, found.sig.params, loc, call);
|
|
4045
4011
|
return {
|
|
4046
4012
|
kind: "call",
|
|
4047
4013
|
callee: `%${found.declarer.def.name}.${access.name.text}`,
|
|
4048
|
-
args: [
|
|
4014
|
+
args: [lowerer.upcastTo(thisRef, found.declarer.def.name), ...args],
|
|
4049
4015
|
type: found.sig.ret,
|
|
4050
4016
|
loc,
|
|
4051
4017
|
};
|
|
4052
4018
|
}
|
|
4053
4019
|
/** The `this` reference for super accessor reads/writes, with the shared
|
|
4054
4020
|
* validity checks (derived-class body, resolvable this). */
|
|
4055
|
-
export function superThisRef(
|
|
4056
|
-
const cls =
|
|
4021
|
+
export function superThisRef(lowerer, access) {
|
|
4022
|
+
const cls = lowerer.currentClass;
|
|
4057
4023
|
if (!cls?.base) {
|
|
4058
|
-
|
|
4024
|
+
lowerer.unsupported("SC1090", access, "'super' outside a derived class");
|
|
4059
4025
|
}
|
|
4060
|
-
const thisLocal =
|
|
4026
|
+
const thisLocal = lowerer.resolveThis();
|
|
4061
4027
|
if (!thisLocal)
|
|
4062
|
-
|
|
4028
|
+
lowerer.unsupported("SC1080", access);
|
|
4063
4029
|
const loc = locOf(access);
|
|
4064
4030
|
return {
|
|
4065
4031
|
thisRef: { kind: "varRef", localId: thisLocal.id, type: thisLocal.type, loc },
|
|
@@ -4069,49 +4035,49 @@ export function superThisRef(L, access) {
|
|
|
4069
4035
|
/** `super.x` read: a DIRECT call of the base chain's getter over this
|
|
4070
4036
|
* method's own `this` (upcast to the declarer) — like super.method(),
|
|
4071
4037
|
* never through the vtable. */
|
|
4072
|
-
export function lowerSuperAccessorRead(
|
|
4073
|
-
const { thisRef, base } =
|
|
4038
|
+
export function lowerSuperAccessorRead(lowerer, access) {
|
|
4039
|
+
const { thisRef, base } = lowerer.superThisRef(access);
|
|
4074
4040
|
const name = access.name.text;
|
|
4075
|
-
const found =
|
|
4041
|
+
const found = lowerer.findMethodOn(base, `get:${name}`);
|
|
4076
4042
|
if (!found) {
|
|
4077
|
-
|
|
4043
|
+
lowerer.unsupported("SC1090", access, lowerer.findMethodOn(base, name)
|
|
4078
4044
|
? `bound method references through 'super' (call 'super.${name}(...)' directly)`
|
|
4079
4045
|
: `'super.${name}' (only base-class methods and getter properties are readable through 'super')`);
|
|
4080
4046
|
}
|
|
4081
4047
|
// tsc rejects super-access of abstract members (TS2513); defensive.
|
|
4082
4048
|
if (found.sig.abstract === true) {
|
|
4083
|
-
|
|
4049
|
+
lowerer.unsupported("SC1090", access, `'super.${name}' of an abstract accessor`);
|
|
4084
4050
|
}
|
|
4085
|
-
|
|
4051
|
+
lowerer.noteEdge(`%${found.declarer.def.name}.get:${name}`);
|
|
4086
4052
|
return {
|
|
4087
4053
|
kind: "call",
|
|
4088
4054
|
callee: `%${found.declarer.def.name}.get:${name}`,
|
|
4089
|
-
args: [
|
|
4055
|
+
args: [lowerer.upcastTo(thisRef, found.declarer.def.name)],
|
|
4090
4056
|
type: found.sig.ret,
|
|
4091
4057
|
loc: locOf(access),
|
|
4092
4058
|
};
|
|
4093
4059
|
}
|
|
4094
4060
|
/** `super.x = v`: a DIRECT call of the base chain's setter (same
|
|
4095
4061
|
* static-dispatch rule as every super member access). */
|
|
4096
|
-
export function lowerSuperAccessorWrite(
|
|
4097
|
-
const { thisRef, base } =
|
|
4062
|
+
export function lowerSuperAccessorWrite(lowerer, access, rhs, loc) {
|
|
4063
|
+
const { thisRef, base } = lowerer.superThisRef(access);
|
|
4098
4064
|
const name = access.name.text;
|
|
4099
|
-
const found =
|
|
4065
|
+
const found = lowerer.findMethodOn(base, `set:${name}`);
|
|
4100
4066
|
if (!found) {
|
|
4101
|
-
|
|
4067
|
+
lowerer.unsupported("SC1090", access, `assignment to 'super.${name}' (no base class declares a setter for it)`);
|
|
4102
4068
|
}
|
|
4103
4069
|
// tsc rejects super-access of abstract members (TS2513); defensive.
|
|
4104
4070
|
if (found.sig.abstract === true) {
|
|
4105
|
-
|
|
4071
|
+
lowerer.unsupported("SC1090", access, `assignment to 'super.${name}' of an abstract accessor`);
|
|
4106
4072
|
}
|
|
4107
|
-
|
|
4108
|
-
const value =
|
|
4073
|
+
lowerer.noteEdge(`%${found.declarer.def.name}.set:${name}`);
|
|
4074
|
+
const value = lowerer.lowerExprExpecting(rhs, found.sig.params[0].type);
|
|
4109
4075
|
return {
|
|
4110
4076
|
kind: "exprStmt",
|
|
4111
4077
|
expr: {
|
|
4112
4078
|
kind: "call",
|
|
4113
4079
|
callee: `%${found.declarer.def.name}.set:${name}`,
|
|
4114
|
-
args: [
|
|
4080
|
+
args: [lowerer.upcastTo(thisRef, found.declarer.def.name), value],
|
|
4115
4081
|
type: VOID,
|
|
4116
4082
|
loc,
|
|
4117
4083
|
},
|
|
@@ -4122,7 +4088,7 @@ export function lowerSuperAccessorWrite(L, access, rhs, loc) {
|
|
|
4122
4088
|
* inherited through ctor-less bases — is a builtin error class's. Such
|
|
4123
4089
|
* classes construct with the error message rule, and their synthesized
|
|
4124
4090
|
* constructors forward one plain string to error.ctor. */
|
|
4125
|
-
export function inheritsBuiltinErrorCtor(
|
|
4091
|
+
export function inheritsBuiltinErrorCtor(lowerer, info) {
|
|
4126
4092
|
for (let c = info; c; c = c.base) {
|
|
4127
4093
|
if (c.builtinError)
|
|
4128
4094
|
return true;
|
|
@@ -4133,7 +4099,7 @@ export function inheritsBuiltinErrorCtor(L, info) {
|
|
|
4133
4099
|
}
|
|
4134
4100
|
/** The EventEmitter twin: a ctor-less chain into the emitter base
|
|
4135
4101
|
* inherits `new C()` — zero arguments (the options bag fences). */
|
|
4136
|
-
export function inheritsBuiltinEmitterCtor(
|
|
4102
|
+
export function inheritsBuiltinEmitterCtor(lowerer, info) {
|
|
4137
4103
|
for (let c = info; c; c = c.base) {
|
|
4138
4104
|
if (c.builtinStream)
|
|
4139
4105
|
return false; // the stream story owns the chain
|
|
@@ -4149,7 +4115,7 @@ export function inheritsBuiltinEmitterCtor(L, info) {
|
|
|
4149
4115
|
* super() with default options; passing options through an inherited
|
|
4150
4116
|
* constructor would need the literal at the new-site to plumb, so it
|
|
4151
4117
|
* asks for a declared constructor instead). */
|
|
4152
|
-
|
|
4118
|
+
function inheritsBuiltinStreamCtor(lowerer, info) {
|
|
4153
4119
|
for (let c = info; c; c = c.base) {
|
|
4154
4120
|
if (c.builtinStream)
|
|
4155
4121
|
return true;
|
|
@@ -4164,19 +4130,19 @@ export function inheritsBuiltinStreamCtor(L, info) {
|
|
|
4164
4130
|
* super() call: "" when omitted or explicitly undefined (Node's message
|
|
4165
4131
|
* property default), the string otherwise. The lib signature's second
|
|
4166
4132
|
* parameter (options/cause) has no lowering. */
|
|
4167
|
-
export function errorMessageArg(
|
|
4133
|
+
export function errorMessageArg(lowerer, args, loc, blame) {
|
|
4168
4134
|
if (args.length > 1) {
|
|
4169
|
-
|
|
4135
|
+
lowerer.unsupported("SC1090", args[1] ?? blame, "Error constructor options ('cause')");
|
|
4170
4136
|
}
|
|
4171
4137
|
if (args.length === 0)
|
|
4172
4138
|
return { kind: "strLit", value: "", type: STRING, loc };
|
|
4173
|
-
const value =
|
|
4139
|
+
const value = lowerer.lowerExpr(args[0]);
|
|
4174
4140
|
if (value.type.kind === "string")
|
|
4175
4141
|
return value;
|
|
4176
4142
|
if (value.kind === "unitLit" && value.unit === "undefined") {
|
|
4177
4143
|
return { kind: "strLit", value: "", type: STRING, loc };
|
|
4178
4144
|
}
|
|
4179
|
-
|
|
4145
|
+
lowerer.unsupported("SC1090", args[0], `Error messages of type '${lowerer.fmt(value.type)}' (the message must be a string)`);
|
|
4180
4146
|
}
|
|
4181
4147
|
/** `new C(...)` of a registered PROGRAM class — the shared tail of the
|
|
4182
4148
|
* identifier and namespace-qualified construction forms. */
|
|
@@ -4184,48 +4150,48 @@ export function errorMessageArg(L, args, loc, blame) {
|
|
|
4184
4150
|
* class resolves to the INSTANTIATION the expression's checker type names
|
|
4185
4151
|
* (inference and explicit type arguments both land there; defaults apply).
|
|
4186
4152
|
* The identity function for ordinary classes. */
|
|
4187
|
-
function genericNewTarget(
|
|
4153
|
+
function genericNewTarget(lowerer, expr, info) {
|
|
4188
4154
|
if (!info.generic)
|
|
4189
4155
|
return info;
|
|
4190
|
-
const t =
|
|
4191
|
-
const mapped =
|
|
4192
|
-
const instInfo = mapped?.kind === "object" ?
|
|
4156
|
+
const t = lowerer.typeOf(expr);
|
|
4157
|
+
const mapped = lowerer.mapTypeOf(t);
|
|
4158
|
+
const instInfo = mapped?.kind === "object" ? lowerer.classes.get(mapped.className) : undefined;
|
|
4193
4159
|
// Unmappable type arguments (or a poisoned instantiation): the site
|
|
4194
4160
|
// reports the type it cannot compile — the instantiation's own
|
|
4195
4161
|
// diagnostic (context-tagged) already told the deeper story.
|
|
4196
4162
|
if (!instInfo || instInfo.generic)
|
|
4197
|
-
|
|
4163
|
+
lowerer.badType(expr, t);
|
|
4198
4164
|
return instInfo;
|
|
4199
4165
|
}
|
|
4200
4166
|
/** A class whose decoration provably throws has no reachable VALUE form:
|
|
4201
4167
|
* the binding never initializes (the %init ReferenceError unwinds first),
|
|
4202
4168
|
* so `new`, the class as a value, and `extends` all fence — reaching one
|
|
4203
4169
|
* in compiled code would require executing past the throw. */
|
|
4204
|
-
|
|
4170
|
+
function fenceDecorationThrows(lowerer, info, blame) {
|
|
4205
4171
|
if (info.decorationThrows === undefined)
|
|
4206
4172
|
return;
|
|
4207
|
-
|
|
4173
|
+
lowerer.unsupported("SC1090", blame, `using the class '${info.def.jsName || info.def.name}' whose decoration provably throws ('${info.decorationThrows.name}' is an ambient name nothing defines — the class statement crashes before the binding exists)`);
|
|
4208
4174
|
}
|
|
4209
|
-
function lowerProgramClassNew(
|
|
4210
|
-
const info = genericNewTarget(
|
|
4211
|
-
fenceDecorationThrows(
|
|
4212
|
-
|
|
4175
|
+
function lowerProgramClassNew(lowerer, expr, declaredInfo, loc) {
|
|
4176
|
+
const info = genericNewTarget(lowerer, expr, declaredInfo);
|
|
4177
|
+
fenceDecorationThrows(lowerer, info, expr);
|
|
4178
|
+
lowerer.noteEdge(`%${info.def.name}.constructor`);
|
|
4213
4179
|
// A ctor-less chain into an EventEmitter base inherits `new C()` —
|
|
4214
4180
|
// zero arguments (the options bag fences, like the super() form).
|
|
4215
4181
|
// Stream subclasses come first: their chain roots at the emitter
|
|
4216
4182
|
// too, but the message should name the stream story.
|
|
4217
|
-
if (inheritsBuiltinStreamCtor(
|
|
4218
|
-
|
|
4183
|
+
if (inheritsBuiltinStreamCtor(lowerer, info) && (expr.arguments ?? []).length > 0) {
|
|
4184
|
+
lowerer.noLowering(`new ${info.def.name.replace(/^%/, "")} with arguments through an inherited stream constructor`, expr.arguments[0], "declare a constructor that passes an inline options object to super(...)");
|
|
4219
4185
|
}
|
|
4220
|
-
if (
|
|
4221
|
-
|
|
4186
|
+
if (lowerer.inheritsBuiltinEmitterCtor(info) && (expr.arguments ?? []).length > 0) {
|
|
4187
|
+
lowerer.unsupported("SC1090", expr.arguments[0], "EventEmitter constructor options ('captureRejections')");
|
|
4222
4188
|
}
|
|
4223
4189
|
// A ctor-less chain into a builtin error base inherits `new
|
|
4224
4190
|
// C(message?)` — completed by the error rule (one plain string),
|
|
4225
4191
|
// not the general ABI completion.
|
|
4226
|
-
const args =
|
|
4227
|
-
? [
|
|
4228
|
-
:
|
|
4192
|
+
const args = lowerer.inheritsBuiltinErrorCtor(info)
|
|
4193
|
+
? [lowerer.errorMessageArg(expr.arguments ?? [], loc, expr)]
|
|
4194
|
+
: lowerer.completeArgs(expr.arguments ?? [], info.ctorParams, loc, expr);
|
|
4229
4195
|
return {
|
|
4230
4196
|
kind: "new",
|
|
4231
4197
|
className: info.def.name,
|
|
@@ -4234,18 +4200,18 @@ function lowerProgramClassNew(L, expr, info0, loc) {
|
|
|
4234
4200
|
loc,
|
|
4235
4201
|
};
|
|
4236
4202
|
}
|
|
4237
|
-
export function lowerNew(
|
|
4203
|
+
export function lowerNew(lowerer, expr) {
|
|
4238
4204
|
const loc = locOf(expr);
|
|
4239
4205
|
// `new X(...)` where X is a package-declared class, in a static build:
|
|
4240
4206
|
// the per-package requires-dynamic diagnostic (the constructor runs in
|
|
4241
4207
|
// the embedded engine). Under --dynamic, X is jsval-typed and lowers
|
|
4242
4208
|
// to the construct op below.
|
|
4243
|
-
if (!
|
|
4209
|
+
if (!lowerer.dynamic) {
|
|
4244
4210
|
const pkg = ts.isIdentifier(expr.expression)
|
|
4245
|
-
?
|
|
4211
|
+
? lowerer.npmPackageOfSymbol(lowerer.resolveValueSymbol(expr.expression) ?? undefined)
|
|
4246
4212
|
: null;
|
|
4247
4213
|
if (pkg) {
|
|
4248
|
-
|
|
4214
|
+
lowerer.pushDiag(requiresDynamicPackageDiag(pkg, loc));
|
|
4249
4215
|
throw new PoisonError();
|
|
4250
4216
|
}
|
|
4251
4217
|
}
|
|
@@ -4253,23 +4219,23 @@ export function lowerNew(L, expr) {
|
|
|
4253
4219
|
// or any 'any'-typed constructor value) runs JS_CallConstructor —
|
|
4254
4220
|
// `new Command()` is the npm entry point. Arguments marshal in; the
|
|
4255
4221
|
// instance stays an island handle.
|
|
4256
|
-
if (
|
|
4257
|
-
const callee =
|
|
4258
|
-
const args = (expr.arguments ?? []).map((a) =>
|
|
4222
|
+
if (lowerer.isIslandExpr(expr.expression)) {
|
|
4223
|
+
const callee = lowerer.lowerExpr(expr.expression);
|
|
4224
|
+
const args = (expr.arguments ?? []).map((a) => lowerer.jsvalIn(lowerer.lowerExpr(a), a));
|
|
4259
4225
|
return { kind: "jsOp", op: "construct", args: [callee, ...args], type: JSVAL, loc };
|
|
4260
4226
|
}
|
|
4261
4227
|
// `new events.EventEmitter()` — the namespace-member (and CJS
|
|
4262
4228
|
// `require('events').EventEmitter`) construction form: the property's
|
|
4263
4229
|
// symbol resolves to the same ambient class as the named import.
|
|
4264
4230
|
if (ts.isPropertyAccessExpression(expr.expression) && ts.isIdentifier(expr.expression.name)) {
|
|
4265
|
-
const memberSym =
|
|
4231
|
+
const memberSym = lowerer.checker.getSymbolAtLocation(expr.expression.name);
|
|
4266
4232
|
const resolved = memberSym && memberSym.flags & ts.SymbolFlags.Alias
|
|
4267
|
-
?
|
|
4233
|
+
? lowerer.checker.getAliasedSymbol(memberSym)
|
|
4268
4234
|
: memberSym;
|
|
4269
|
-
const emitterInfo =
|
|
4235
|
+
const emitterInfo = lowerer.builtinEmitterInfoOf(resolved);
|
|
4270
4236
|
if (emitterInfo) {
|
|
4271
4237
|
if ((expr.arguments ?? []).length > 0) {
|
|
4272
|
-
|
|
4238
|
+
lowerer.unsupported("SC1090", expr.arguments[0], "EventEmitter constructor options ('captureRejections')");
|
|
4273
4239
|
}
|
|
4274
4240
|
return {
|
|
4275
4241
|
kind: "libCall",
|
|
@@ -4281,41 +4247,41 @@ export function lowerNew(L, expr) {
|
|
|
4281
4247
|
}
|
|
4282
4248
|
// `new stream.Readable({...})` — the namespace-member (and CJS
|
|
4283
4249
|
// `require('stream').Readable`) construction form.
|
|
4284
|
-
const streamInfoNs = builtinStreamInfoOf(
|
|
4250
|
+
const streamInfoNs = builtinStreamInfoOf(lowerer, resolved);
|
|
4285
4251
|
if (streamInfoNs)
|
|
4286
|
-
return lowerStreamNew(
|
|
4252
|
+
return lowerStreamNew(lowerer, expr, streamInfoNs);
|
|
4287
4253
|
// `new N.C(...)` / `new a.Point(...)` — construction through a
|
|
4288
4254
|
// USER namespace qualifier (import= alias chains included): the
|
|
4289
4255
|
// member resolves to the registered program class, guarded by the
|
|
4290
4256
|
// namespace source-order fences (lower-namespaces.ts).
|
|
4291
|
-
if (!expr.expression.questionDotToken && nsMemberIdentOf(
|
|
4257
|
+
if (!expr.expression.questionDotToken && nsMemberIdentOf(lowerer, expr.expression)) {
|
|
4292
4258
|
if (memberSym)
|
|
4293
|
-
fenceEarlyNsMemberRef(
|
|
4259
|
+
fenceEarlyNsMemberRef(lowerer, expr.expression, memberSym);
|
|
4294
4260
|
// resolveValueSymbol (not the bare alias chase): the reference
|
|
4295
4261
|
// must flush deferred collection diagnostics like any other.
|
|
4296
|
-
const classSym =
|
|
4297
|
-
const info = classSym ?
|
|
4262
|
+
const classSym = lowerer.resolveValueSymbol(expr.expression.name);
|
|
4263
|
+
const info = classSym ? lowerer.classBySymbol.get(classSym) : undefined;
|
|
4298
4264
|
// Qualified spellings of a rebindable decorated class (an import=
|
|
4299
4265
|
// alias chain landing on it) cannot construct the declaration
|
|
4300
4266
|
// directly — the decoration result decides. The bare-name path
|
|
4301
4267
|
// routes through the class VALUE; the qualified one fences.
|
|
4302
4268
|
if (info?.classDecorators?.valueGlobalId !== undefined) {
|
|
4303
|
-
|
|
4269
|
+
lowerer.unsupported("SC1090", expr, "constructing a decorated class through a qualified name (a replacing decorator rebinds the class name — construct through the bare name)");
|
|
4304
4270
|
}
|
|
4305
4271
|
if (info)
|
|
4306
|
-
return lowerProgramClassNew(
|
|
4307
|
-
|
|
4272
|
+
return lowerProgramClassNew(lowerer, expr, info, loc);
|
|
4273
|
+
lowerer.unsupported("SC1090", expr, `constructing '${expr.expression.name.text}' (a namespace member with no class lowering)`);
|
|
4308
4274
|
}
|
|
4309
4275
|
// `new B.C()` where B is an AMBIENT namespace (fundule merges
|
|
4310
4276
|
// included): Node evaluates the callee first and throws
|
|
4311
4277
|
// ReferenceError before any argument runs — undefRead reproduces it
|
|
4312
4278
|
// exactly.
|
|
4313
4279
|
if (!expr.expression.questionDotToken) {
|
|
4314
|
-
const ambientRoot = ambientNsRootOf(
|
|
4280
|
+
const ambientRoot = ambientNsRootOf(lowerer, expr.expression.expression);
|
|
4315
4281
|
if (ambientRoot !== null) {
|
|
4316
|
-
const t = ambientUndefReadType(
|
|
4282
|
+
const t = ambientUndefReadType(lowerer, expr);
|
|
4317
4283
|
if (t)
|
|
4318
|
-
return nsUndefRead(
|
|
4284
|
+
return nsUndefRead(lowerer, ambientRoot.text, expr, t);
|
|
4319
4285
|
}
|
|
4320
4286
|
}
|
|
4321
4287
|
// Construction through a CJS export member tsgo types `any`
|
|
@@ -4331,9 +4297,9 @@ export function lowerNew(L, expr) {
|
|
|
4331
4297
|
(isModuleExportsAccess(expr.expression.expression) ||
|
|
4332
4298
|
(ts.isIdentifier(expr.expression.expression) &&
|
|
4333
4299
|
expr.expression.expression.text === "exports" &&
|
|
4334
|
-
!
|
|
4335
|
-
!
|
|
4336
|
-
|
|
4300
|
+
!lowerer.peekLocal(expr.expression.expression) &&
|
|
4301
|
+
!lowerer.globalOf(expr.expression.expression)))) ||
|
|
4302
|
+
lowerer.cjsLocalModuleBindingOf(expr.expression.expression))) {
|
|
4337
4303
|
// Candidate symbols for the export global: the member symbol as
|
|
4338
4304
|
// spelled, its alias-chased resolution (resolveValueSymbol carries
|
|
4339
4305
|
// the dep-module fallback tsgo needs at member-use sites), and the
|
|
@@ -4341,28 +4307,28 @@ export function lowerNew(L, expr) {
|
|
|
4341
4307
|
const candidates = [
|
|
4342
4308
|
memberSym,
|
|
4343
4309
|
resolved,
|
|
4344
|
-
|
|
4345
|
-
|
|
4310
|
+
lowerer.resolveValueSymbol(expr.expression.name) ?? undefined,
|
|
4311
|
+
lowerer.cjsModuleExportSymbol(expr.getSourceFile(), expr.expression.name.text),
|
|
4346
4312
|
];
|
|
4347
4313
|
const exportSym = candidates.find((s) => s !== undefined);
|
|
4348
4314
|
const g = candidates
|
|
4349
|
-
.map((s) => (s ?
|
|
4315
|
+
.map((s) => (s ? lowerer.globalsBySymbol.get(s) : undefined))
|
|
4350
4316
|
.find((x) => x !== undefined);
|
|
4351
4317
|
if (g && g.type.kind === "classval") {
|
|
4352
|
-
const info =
|
|
4353
|
-
propertyAssignedClassInfoOf(
|
|
4354
|
-
|
|
4318
|
+
const info = lowerer.classes.get(g.type.className) ??
|
|
4319
|
+
propertyAssignedClassInfoOf(lowerer, exportSym) ??
|
|
4320
|
+
lowerer.classes.get(g.type.className);
|
|
4355
4321
|
if (info && !info.generic) {
|
|
4356
|
-
|
|
4322
|
+
lowerer.noteEdge(`%${info.def.name}.constructor`);
|
|
4357
4323
|
const below = (c) => {
|
|
4358
4324
|
for (const s of c.subclasses) {
|
|
4359
|
-
|
|
4325
|
+
lowerer.noteEdge(`%${s.def.name}.constructor`);
|
|
4360
4326
|
below(s);
|
|
4361
4327
|
}
|
|
4362
4328
|
};
|
|
4363
4329
|
below(info);
|
|
4364
4330
|
const callee = { kind: "varRef", localId: g.id, type: g.type, loc };
|
|
4365
|
-
const args =
|
|
4331
|
+
const args = lowerer.completeArgs(expr.arguments ?? [], info.ctorParams, loc, expr);
|
|
4366
4332
|
return {
|
|
4367
4333
|
kind: "newValue",
|
|
4368
4334
|
callee,
|
|
@@ -4378,7 +4344,7 @@ export function lowerNew(L, expr) {
|
|
|
4378
4344
|
// of http.createServer (Node's Server class IS the factory's
|
|
4379
4345
|
// product); routed to lower-server ahead of the stdlib-ctor fences.
|
|
4380
4346
|
{
|
|
4381
|
-
const httpServer = lowerHttpServerNew(
|
|
4347
|
+
const httpServer = lowerHttpServerNew(lowerer, expr);
|
|
4382
4348
|
if (httpServer)
|
|
4383
4349
|
return httpServer;
|
|
4384
4350
|
}
|
|
@@ -4386,20 +4352,20 @@ export function lowerNew(L, expr) {
|
|
|
4386
4352
|
// handle (lower-server): getName/destroy/counters through the dyn
|
|
4387
4353
|
// handle ops, requests thread it via the agent option.
|
|
4388
4354
|
{
|
|
4389
|
-
const agent = lowerHttpAgentNew(
|
|
4355
|
+
const agent = lowerHttpAgentNew(lowerer, expr);
|
|
4390
4356
|
if (agent)
|
|
4391
4357
|
return agent;
|
|
4392
4358
|
}
|
|
4393
4359
|
if (ts.isIdentifier(expr.expression)) {
|
|
4394
4360
|
// `import C = N.C; new C()` — the alias's own source-order guards
|
|
4395
4361
|
// (a no-op for every non-import= binding).
|
|
4396
|
-
fenceEarlyAliasUse(
|
|
4397
|
-
const symbol =
|
|
4362
|
+
fenceEarlyAliasUse(lowerer, expr.expression, expr);
|
|
4363
|
+
const symbol = lowerer.resolveValueSymbol(expr.expression);
|
|
4398
4364
|
// `new Error(msg?)` (and TypeError/RangeError/SyntaxError): the
|
|
4399
4365
|
// runtime-provided classes construct through one libCall — the result
|
|
4400
4366
|
// TYPE names which builtin, and the message completes to "" exactly
|
|
4401
4367
|
// like Node's message property default.
|
|
4402
|
-
const errInfo =
|
|
4368
|
+
const errInfo = lowerer.builtinErrorInfoOf(symbol);
|
|
4403
4369
|
// `new DOMException(message?, nameOrOptions?)`: both arguments cross
|
|
4404
4370
|
// as dyn values (absent → the dyn undefined), and the runtime owns
|
|
4405
4371
|
// WebIDL's resolution — ToString of the message ("" for undefined),
|
|
@@ -4409,18 +4375,18 @@ export function lowerNew(L, expr) {
|
|
|
4409
4375
|
if (errInfo && errInfo.def.name === "%DOMException") {
|
|
4410
4376
|
const args = expr.arguments ?? [];
|
|
4411
4377
|
if (args.length > 2) {
|
|
4412
|
-
|
|
4378
|
+
lowerer.noLowering(`new DOMException with ${args.length} arguments`, expr);
|
|
4413
4379
|
}
|
|
4414
4380
|
const toDynArg = (a) => {
|
|
4415
4381
|
if (!a)
|
|
4416
4382
|
return dynUndefinedExpr(loc);
|
|
4417
|
-
const v =
|
|
4383
|
+
const v = lowerer.lowerExpr(a);
|
|
4418
4384
|
if (v.type.kind === "dyn")
|
|
4419
4385
|
return v;
|
|
4420
|
-
if (v.kind === "unitLit" || (v.type.kind !== "jsval" &&
|
|
4386
|
+
if (v.kind === "unitLit" || (v.type.kind !== "jsval" && lowerer.dynConvertible(v.type))) {
|
|
4421
4387
|
return { kind: "dynFrom", value: v, type: DYN, loc };
|
|
4422
4388
|
}
|
|
4423
|
-
|
|
4389
|
+
lowerer.noLowering(`new DOMException with a '${lowerer.fmt(v.type)}' argument`, a, "message strings and string/options-object names lower (Node ToStrings other values — convert explicitly)");
|
|
4424
4390
|
};
|
|
4425
4391
|
const msgArg = toDynArg(args[0]);
|
|
4426
4392
|
const nameArg = toDynArg(args[1]);
|
|
@@ -4433,7 +4399,7 @@ export function lowerNew(L, expr) {
|
|
|
4433
4399
|
};
|
|
4434
4400
|
}
|
|
4435
4401
|
if (errInfo) {
|
|
4436
|
-
const msg =
|
|
4402
|
+
const msg = lowerer.errorMessageArg(expr.arguments ?? [], loc, expr);
|
|
4437
4403
|
return {
|
|
4438
4404
|
kind: "libCall",
|
|
4439
4405
|
fn: "error.new",
|
|
@@ -4445,10 +4411,10 @@ export function lowerNew(L, expr) {
|
|
|
4445
4411
|
// `new EventEmitter()`: the runtime-provided emitter constructs
|
|
4446
4412
|
// through one libCall. Zero arguments — the options bag
|
|
4447
4413
|
// (@types/node's captureRejections) has no lowering.
|
|
4448
|
-
const emitterInfo =
|
|
4414
|
+
const emitterInfo = lowerer.builtinEmitterInfoOf(symbol);
|
|
4449
4415
|
if (emitterInfo) {
|
|
4450
4416
|
if ((expr.arguments ?? []).length > 0) {
|
|
4451
|
-
|
|
4417
|
+
lowerer.unsupported("SC1090", expr.arguments[0], "EventEmitter constructor options ('captureRejections')");
|
|
4452
4418
|
}
|
|
4453
4419
|
return {
|
|
4454
4420
|
kind: "libCall",
|
|
@@ -4460,9 +4426,9 @@ export function lowerNew(L, expr) {
|
|
|
4460
4426
|
}
|
|
4461
4427
|
// `new Readable({...})` and the other stream classes: the options
|
|
4462
4428
|
// object parses structurally in the stream spoke.
|
|
4463
|
-
const streamInfo = builtinStreamInfoOf(
|
|
4429
|
+
const streamInfo = builtinStreamInfoOf(lowerer, symbol);
|
|
4464
4430
|
if (streamInfo)
|
|
4465
|
-
return lowerStreamNew(
|
|
4431
|
+
return lowerStreamNew(lowerer, expr, streamInfo);
|
|
4466
4432
|
// `new URL(input)`: the WHATWG URL class (stdlib/@types provenance —
|
|
4467
4433
|
// a user's own `class URL` resolves through classBySymbol below).
|
|
4468
4434
|
// One string argument; invalid input throws a catchable TypeError
|
|
@@ -4473,17 +4439,17 @@ export function lowerNew(L, expr) {
|
|
|
4473
4439
|
// so bad input throws Node's catchable SyntaxError at construction.
|
|
4474
4440
|
// String arguments only (Node also accepts a RegExp to copy — that
|
|
4475
4441
|
// form keeps the fence).
|
|
4476
|
-
if (symbol && symbol.name === "RegExp" &&
|
|
4442
|
+
if (symbol && symbol.name === "RegExp" && lowerer.isStdlibSymbol(symbol)) {
|
|
4477
4443
|
const args = expr.arguments ?? [];
|
|
4478
4444
|
if (args.length > 2) {
|
|
4479
|
-
|
|
4445
|
+
lowerer.noLowering(`new RegExp with ${args.length} arguments`, expr);
|
|
4480
4446
|
}
|
|
4481
4447
|
const strArg = (a, what) => {
|
|
4482
4448
|
if (!a)
|
|
4483
4449
|
return { kind: "strLit", value: "", type: STRING, loc };
|
|
4484
|
-
const v =
|
|
4450
|
+
const v = lowerer.lowerExpr(a);
|
|
4485
4451
|
if (v.type.kind !== "string") {
|
|
4486
|
-
|
|
4452
|
+
lowerer.noLowering(`new RegExp with a '${lowerer.fmt(v.type)}' ${what}`, a, "string arguments are the lowered form (a RegExp copy or ToString coercion has no lowering)");
|
|
4487
4453
|
}
|
|
4488
4454
|
return v;
|
|
4489
4455
|
};
|
|
@@ -4491,32 +4457,32 @@ export function lowerNew(L, expr) {
|
|
|
4491
4457
|
const flags = strArg(args[1], "flags argument");
|
|
4492
4458
|
return { kind: "libCall", fn: "regex.new", args: [pattern, flags], type: { kind: "regex" }, loc };
|
|
4493
4459
|
}
|
|
4494
|
-
if (symbol && symbol.name === "URL" &&
|
|
4460
|
+
if (symbol && symbol.name === "URL" && lowerer.isStdlibSymbol(symbol)) {
|
|
4495
4461
|
const args = expr.arguments ?? [];
|
|
4496
4462
|
if (args.length !== 1) {
|
|
4497
|
-
|
|
4463
|
+
lowerer.noLowering(`new URL with ${args.length} argument${args.length === 1 ? "" : "s"}`, expr, "one absolute-URL string is the supported form (resolve relative inputs against a base yourself)", symbol);
|
|
4498
4464
|
}
|
|
4499
|
-
const input =
|
|
4465
|
+
const input = lowerer.lowerExprExpecting(args[0], STRING);
|
|
4500
4466
|
return { kind: "libCall", fn: "url.new", args: [input], type: URL_T, loc };
|
|
4501
4467
|
}
|
|
4502
4468
|
// `new URLSearchParams(init?)`: the WHATWG list (stdlib provenance —
|
|
4503
4469
|
// see lowerSearchParamsNew for the lowered init shapes).
|
|
4504
|
-
if (symbol && symbol.name === "URLSearchParams" &&
|
|
4505
|
-
return lowerSearchParamsNew(
|
|
4470
|
+
if (symbol && symbol.name === "URLSearchParams" && lowerer.isStdlibSymbol(symbol)) {
|
|
4471
|
+
return lowerSearchParamsNew(lowerer, expr, loc);
|
|
4506
4472
|
}
|
|
4507
4473
|
// Date's read-only value slice: store the constructor's TimeClip'd
|
|
4508
4474
|
// epoch milliseconds as the scalar date kind. That is sufficient
|
|
4509
4475
|
// for every getter and toISOString; identity and setters stay
|
|
4510
4476
|
// fenced, so copying the scalar cannot create an observable lie.
|
|
4511
|
-
if (symbol && symbol.name === "Date" &&
|
|
4477
|
+
if (symbol && symbol.name === "Date" && lowerer.isStdlibSymbol(symbol)) {
|
|
4512
4478
|
const args = expr.arguments ?? [];
|
|
4513
4479
|
if (args.some(ts.isSpreadElement) || args.length > 1) {
|
|
4514
|
-
|
|
4480
|
+
lowerer.noLowering(`new Date with ${args.length} arguments`, expr, "new Date(), new Date(milliseconds), and new Date(dateString) are supported; the local-time year/month field constructor has no lowering", symbol);
|
|
4515
4481
|
}
|
|
4516
4482
|
if (args.length === 0) {
|
|
4517
4483
|
return { kind: "libCall", fn: "date.newNow", args: [], type: DATE_T, loc };
|
|
4518
4484
|
}
|
|
4519
|
-
const arg =
|
|
4485
|
+
const arg = lowerer.lowerExpr(args[0]);
|
|
4520
4486
|
if (arg.type.kind === "f64") {
|
|
4521
4487
|
return { kind: "libCall", fn: "date.newMs", args: [arg], type: DATE_T, loc };
|
|
4522
4488
|
}
|
|
@@ -4526,7 +4492,7 @@ export function lowerNew(L, expr) {
|
|
|
4526
4492
|
if (arg.type.kind === "date") {
|
|
4527
4493
|
return arg;
|
|
4528
4494
|
}
|
|
4529
|
-
|
|
4495
|
+
lowerer.noLowering(`new Date of '${lowerer.fmt(arg.type)}' values`, args[0], "pass milliseconds, a date string, or another Date value", symbol);
|
|
4530
4496
|
}
|
|
4531
4497
|
// `new StringDecoder(encoding?)` (node:string_decoder): the decoder
|
|
4532
4498
|
// is a two-field record — the CANONICAL encoding name (aliases fold
|
|
@@ -4534,15 +4500,15 @@ export function lowerNew(L, expr) {
|
|
|
4534
4500
|
// the packed-f64 pending state starting at 0 (nothing buffered).
|
|
4535
4501
|
// The encoding must be a literal (Node's alias set); omitted means
|
|
4536
4502
|
// utf8, Node's default.
|
|
4537
|
-
if (symbol && symbol.name === "StringDecoder" &&
|
|
4503
|
+
if (symbol && symbol.name === "StringDecoder" && lowerer.isStdlibSymbol(symbol)) {
|
|
4538
4504
|
const args = expr.arguments ?? [];
|
|
4539
4505
|
if (args.length > 1) {
|
|
4540
|
-
|
|
4506
|
+
lowerer.noLowering("new StringDecoder with 2 arguments", expr, undefined, symbol);
|
|
4541
4507
|
}
|
|
4542
|
-
const encName = args.length === 1 ? bufEncoding(
|
|
4543
|
-
const decT =
|
|
4508
|
+
const encName = args.length === 1 ? bufEncoding(lowerer, "new StringDecoder", args[0]) : "utf8";
|
|
4509
|
+
const decT = lowerer.mapTypeOf(lowerer.typeOf(expr));
|
|
4544
4510
|
if (decT?.kind !== "record")
|
|
4545
|
-
|
|
4511
|
+
lowerer.badType(expr, lowerer.typeOf(expr));
|
|
4546
4512
|
return {
|
|
4547
4513
|
kind: "recordLit",
|
|
4548
4514
|
fields: [
|
|
@@ -4556,8 +4522,8 @@ export function lowerNew(L, expr) {
|
|
|
4556
4522
|
// Encoder objects likewise never exist: lowerTextCodecCall claims
|
|
4557
4523
|
// composed calls before the receiver lowers, while the same-scope
|
|
4558
4524
|
// const store-then-call declaration is erased before reaching here.
|
|
4559
|
-
if (symbol && (symbol.name === "TextDecoder" || symbol.name === "TextEncoder") &&
|
|
4560
|
-
|
|
4525
|
+
if (symbol && (symbol.name === "TextDecoder" || symbol.name === "TextEncoder") && lowerer.isStdlibSymbol(symbol)) {
|
|
4526
|
+
lowerer.noLowering(`new ${symbol.name}`, expr, `${symbol.name} values have no representation — a same-scope const store-then-call or the composed form compiles: ` +
|
|
4561
4527
|
(symbol.name === "TextDecoder"
|
|
4562
4528
|
? "new TextDecoder().decode(bytes)"
|
|
4563
4529
|
: "new TextEncoder().encode(s)"), symbol);
|
|
@@ -4567,20 +4533,20 @@ export function lowerNew(L, expr) {
|
|
|
4567
4533
|
// representation (stdlib provenance — see lowerBytesNew for the
|
|
4568
4534
|
// lowered argument shapes; a user's own class with one of the names
|
|
4569
4535
|
// resolves through classBySymbol below).
|
|
4570
|
-
const bytesNew =
|
|
4536
|
+
const bytesNew = lowerer.lowerBytesNew(expr, symbol);
|
|
4571
4537
|
if (bytesNew)
|
|
4572
4538
|
return bytesNew;
|
|
4573
|
-
const info = (symbol ?
|
|
4539
|
+
const info = (symbol ? lowerer.classBySymbol.get(symbol) : undefined) ??
|
|
4574
4540
|
// `const C = require('./x'); new C()` over `module.exports =
|
|
4575
4541
|
// class {…}`: the binding aliases the expression's own symbol —
|
|
4576
4542
|
// the declaration story, collected on demand.
|
|
4577
|
-
propertyAssignedClassInfoOf(
|
|
4543
|
+
propertyAssignedClassInfoOf(lowerer, symbol) ??
|
|
4578
4544
|
undefined;
|
|
4579
4545
|
// A rebindable decorated name constructs through its VALUE (the
|
|
4580
4546
|
// classval-typed path below — newValue through the decoration
|
|
4581
4547
|
// result's construct thunk), never the declaration directly.
|
|
4582
4548
|
if (info && info.classDecorators?.valueGlobalId === undefined) {
|
|
4583
|
-
return lowerProgramClassNew(
|
|
4549
|
+
return lowerProgramClassNew(lowerer, expr, info, loc);
|
|
4584
4550
|
}
|
|
4585
4551
|
// `new Map<K, V>()`: the lib Map constructor. The SEEDED forms: an
|
|
4586
4552
|
// entries ARRAY LITERAL of PAIR LITERALS at the construction site
|
|
@@ -4605,46 +4571,46 @@ export function lowerNew(L, expr) {
|
|
|
4605
4571
|
// returns its argument for objects and BOXES primitives — the wrapper
|
|
4606
4572
|
// story with no lowering — so it keeps the constructor fence.
|
|
4607
4573
|
if (symbol?.name === "Object" &&
|
|
4608
|
-
|
|
4574
|
+
lowerer.isStdlibSymbol(symbol) &&
|
|
4609
4575
|
(expr.arguments ?? []).length === 0) {
|
|
4610
4576
|
return {
|
|
4611
4577
|
kind: "recordLit",
|
|
4612
4578
|
fields: [],
|
|
4613
|
-
type: { kind: "record", shapeId:
|
|
4579
|
+
type: { kind: "record", shapeId: lowerer.shapes.intern([]) },
|
|
4614
4580
|
loc,
|
|
4615
4581
|
};
|
|
4616
4582
|
}
|
|
4617
|
-
if (symbol?.name === "Array" &&
|
|
4583
|
+
if (symbol?.name === "Array" && lowerer.isStdlibSymbol(symbol)) {
|
|
4618
4584
|
const args = expr.arguments ?? [];
|
|
4619
4585
|
if (args.some(ts.isSpreadElement)) {
|
|
4620
|
-
|
|
4586
|
+
lowerer.noLowering("new Array with spread arguments", expr, "write the array literal: [...xs]");
|
|
4621
4587
|
}
|
|
4622
|
-
if (args.length === 1 &&
|
|
4623
|
-
|
|
4588
|
+
if (args.length === 1 && lowerer.mapTypeOf(lowerer.typeOf(args[0]))?.kind === "f64") {
|
|
4589
|
+
lowerer.noLowering("new Array(count)", expr, "the one-number form allocates HOLES (reads answer undefined, which the element type cannot carry) — build and push, or use the elements form: new Array(a, b)");
|
|
4624
4590
|
}
|
|
4625
|
-
let t =
|
|
4591
|
+
let t = lowerer.mapTypeOf(lowerer.typeOf(expr));
|
|
4626
4592
|
// JS's `new Array()` types any[]; the contextual type carries the
|
|
4627
4593
|
// annotation when one exists (the new Map() stance).
|
|
4628
4594
|
if (t?.kind !== "array") {
|
|
4629
|
-
const ctx =
|
|
4630
|
-
const ctxMapped = ctx ?
|
|
4595
|
+
const ctx = lowerer.checker.getContextualType(expr);
|
|
4596
|
+
const ctxMapped = ctx ? lowerer.mapTypeOf(ctx) : null;
|
|
4631
4597
|
if (ctxMapped?.kind === "array")
|
|
4632
4598
|
t = ctxMapped;
|
|
4633
4599
|
}
|
|
4634
4600
|
if (t?.kind !== "array")
|
|
4635
|
-
|
|
4636
|
-
const elems = args.map((a) =>
|
|
4601
|
+
lowerer.badType(expr, lowerer.typeOf(expr));
|
|
4602
|
+
const elems = args.map((a) => lowerer.lowerExprExpecting(a, t.elem));
|
|
4637
4603
|
return { kind: "arrayLit", elems, type: t, loc };
|
|
4638
4604
|
}
|
|
4639
|
-
if (symbol?.name === "Map" &&
|
|
4605
|
+
if (symbol?.name === "Map" && lowerer.isStdlibSymbol(symbol)) {
|
|
4640
4606
|
const seedArg = (expr.arguments?.length ?? 0) === 1 ? expr.arguments[0] : null;
|
|
4641
4607
|
const isPairLit = (el) => ts.isArrayLiteralExpression(el) && el.elements.length === 2 &&
|
|
4642
4608
|
!el.elements.some(ts.isSpreadElement);
|
|
4643
4609
|
const entriesLit = seedArg && ts.isArrayLiteralExpression(seedArg) && seedArg.elements.every(isPairLit)
|
|
4644
4610
|
? seedArg.elements.filter(isPairLit)
|
|
4645
4611
|
: null;
|
|
4646
|
-
let tsType =
|
|
4647
|
-
let mapped =
|
|
4612
|
+
let tsType = lowerer.typeOf(expr);
|
|
4613
|
+
let mapped = lowerer.mapTypeOf(tsType);
|
|
4648
4614
|
// JavaScript's `new Map()` has no type-argument syntax: the no-arg
|
|
4649
4615
|
// constructor overload pins Map<any, any> whatever the JSDoc says
|
|
4650
4616
|
// (`@type` on the declaration types the VARIABLE, not this
|
|
@@ -4652,20 +4618,20 @@ export function lowerNew(L, expr) {
|
|
|
4652
4618
|
// it when it is a supported map. TS type arguments keep winning:
|
|
4653
4619
|
// their expression type already maps.
|
|
4654
4620
|
if (mapped?.kind !== "map") {
|
|
4655
|
-
const ctx =
|
|
4656
|
-
const ctxMapped = ctx ?
|
|
4621
|
+
const ctx = lowerer.checker.getContextualType(expr);
|
|
4622
|
+
const ctxMapped = ctx ? lowerer.mapTypeOf(ctx) : null;
|
|
4657
4623
|
if (ctx && ctxMapped?.kind === "map") {
|
|
4658
4624
|
tsType = ctx;
|
|
4659
4625
|
mapped = ctxMapped;
|
|
4660
4626
|
}
|
|
4661
4627
|
}
|
|
4662
4628
|
if (seedArg && !entriesLit && mapped?.kind === "map") {
|
|
4663
|
-
const seeded = lowerMapSeedArrayNew(
|
|
4629
|
+
const seeded = lowerMapSeedArrayNew(lowerer, seedArg, mapped);
|
|
4664
4630
|
if (seeded)
|
|
4665
4631
|
return seeded;
|
|
4666
4632
|
}
|
|
4667
4633
|
if ((expr.arguments?.length ?? 0) > 0 && !entriesLit) {
|
|
4668
|
-
|
|
4634
|
+
lowerer.noLowering("new Map(entries)", expr, "supported seeds: an array literal of [key, value] pair literals, or a " +
|
|
4669
4635
|
"[K, V][]-typed tuple-array value — construct the Map empty and set() " +
|
|
4670
4636
|
"each entry otherwise");
|
|
4671
4637
|
}
|
|
@@ -4673,12 +4639,12 @@ export function lowerNew(L, expr) {
|
|
|
4673
4639
|
if (!entriesLit)
|
|
4674
4640
|
return { kind: "mapNew", type: mapped, loc };
|
|
4675
4641
|
const seed = entriesLit.map((pair) => ({
|
|
4676
|
-
key:
|
|
4677
|
-
value:
|
|
4642
|
+
key: lowerer.lowerExprExpecting(pair.elements[0], mapped.key),
|
|
4643
|
+
value: lowerer.lowerExprExpecting(pair.elements[1], mapped.value),
|
|
4678
4644
|
}));
|
|
4679
4645
|
return { kind: "mapNew", seed, type: mapped, loc };
|
|
4680
4646
|
}
|
|
4681
|
-
const targs =
|
|
4647
|
+
const targs = lowerer.checker.getTypeArguments(tsType);
|
|
4682
4648
|
// JAVASCRIPT `new Map()` whose arguments never resolved past
|
|
4683
4649
|
// Map<any, any> (no annotation, no contextual type, no seed): the
|
|
4684
4650
|
// WeakMap stance below — the VALUE lowers as an opaque dyn object
|
|
@@ -4693,17 +4659,17 @@ export function lowerNew(L, expr) {
|
|
|
4693
4659
|
targs.every((t) => (t.flags & ts.TypeFlags.Any) !== 0)) {
|
|
4694
4660
|
return { kind: "dynObjLit", type: DYN, loc };
|
|
4695
4661
|
}
|
|
4696
|
-
const keyIr = targs[0] ?
|
|
4662
|
+
const keyIr = targs[0] ? lowerer.mapTypeOf(targs[0]) : null;
|
|
4697
4663
|
if (targs[0] && (!keyIr || !isSupportedMapKey(keyIr))) {
|
|
4698
|
-
|
|
4664
|
+
lowerer.unsupported("SC1090", expr, `Map keys of type '${lowerer.checker.typeToString(targs[0])}' ` +
|
|
4699
4665
|
`(Map keys must be string or number)`);
|
|
4700
4666
|
}
|
|
4701
4667
|
if (targs[1]) {
|
|
4702
|
-
|
|
4668
|
+
lowerer.unsupported("SC1090", expr, `Map values of type '${lowerer.checker.typeToString(targs[1])}' ` +
|
|
4703
4669
|
`(Map values must be number, string, boolean, records, class instances, ` +
|
|
4704
4670
|
`arrays, promises, or unions of those — not functions, Maps, 'unknown', or 'any')`);
|
|
4705
4671
|
}
|
|
4706
|
-
|
|
4672
|
+
lowerer.badType(expr, tsType);
|
|
4707
4673
|
}
|
|
4708
4674
|
// `new Set<T>()`: Map's sibling. The SEEDED form lowers for any
|
|
4709
4675
|
// T[]-typed argument — literal or variable, T already a legal
|
|
@@ -4718,14 +4684,14 @@ export function lowerNew(L, expr) {
|
|
|
4718
4684
|
// every reached METHOD use meets its own per-site fence → runtime
|
|
4719
4685
|
// fence). TypeScript keeps the compile fence.
|
|
4720
4686
|
if ((symbol?.name === "WeakMap" || symbol?.name === "WeakSet") &&
|
|
4721
|
-
|
|
4687
|
+
lowerer.isStdlibSymbol(symbol) &&
|
|
4722
4688
|
isJsSourceFile(expr.getSourceFile()) &&
|
|
4723
4689
|
(expr.arguments?.length ?? 0) === 0) {
|
|
4724
4690
|
return { kind: "dynObjLit", type: DYN, loc };
|
|
4725
4691
|
}
|
|
4726
|
-
if (symbol?.name === "Set" &&
|
|
4727
|
-
const tsType =
|
|
4728
|
-
const mapped =
|
|
4692
|
+
if (symbol?.name === "Set" && lowerer.isStdlibSymbol(symbol)) {
|
|
4693
|
+
const tsType = lowerer.typeOf(expr);
|
|
4694
|
+
const mapped = lowerer.mapTypeOf(tsType);
|
|
4729
4695
|
if (mapped?.kind === "set" && (expr.arguments?.length ?? 0) === 1) {
|
|
4730
4696
|
const argNode = expr.arguments[0];
|
|
4731
4697
|
// An array LITERAL seed builds element-wise (its contextual type
|
|
@@ -4733,14 +4699,14 @@ export function lowerNew(L, expr) {
|
|
|
4733
4699
|
// union — unmappable, so the generic literal path can't type it);
|
|
4734
4700
|
// an array-typed VALUE seed lowers as itself.
|
|
4735
4701
|
if (ts.isArrayLiteralExpression(argNode) && !argNode.elements.some(ts.isSpreadElement)) {
|
|
4736
|
-
const elems = argNode.elements.map((el) =>
|
|
4702
|
+
const elems = argNode.elements.map((el) => lowerer.lowerExprExpecting(el, mapped.elem));
|
|
4737
4703
|
const seed = { kind: "arrayLit", elems, type: arrayOf(mapped.elem), loc };
|
|
4738
4704
|
return { kind: "setNew", seed, type: mapped, loc };
|
|
4739
4705
|
}
|
|
4740
4706
|
if (!ts.isSpreadElement(argNode)) {
|
|
4741
|
-
const argIr =
|
|
4707
|
+
const argIr = lowerer.mapTypeOf(lowerer.typeOf(argNode));
|
|
4742
4708
|
if (argIr?.kind === "array" && typeEquals(argIr.elem, mapped.elem)) {
|
|
4743
|
-
let seed =
|
|
4709
|
+
let seed = lowerer.lowerExpr(argNode);
|
|
4744
4710
|
// A T[]-DECLARED seed whose value is an island handle (a
|
|
4745
4711
|
// package's exported array — the binding never held a
|
|
4746
4712
|
// static array): the VALIDATED exit copies the engine
|
|
@@ -4748,7 +4714,7 @@ export function lowerNew(L, expr) {
|
|
|
4748
4714
|
// lying handle), and the bulk add proceeds on the copy —
|
|
4749
4715
|
// construction reads the seed once, so the aliasing
|
|
4750
4716
|
// divergence has nothing to observe.
|
|
4751
|
-
if (seed.type.kind === "jsval" &&
|
|
4717
|
+
if (seed.type.kind === "jsval" && lowerer.boundaryExitSafe(arrayOf(mapped.elem))) {
|
|
4752
4718
|
seed = { kind: "jsExit", value: seed, type: arrayOf(mapped.elem), loc: seed.loc };
|
|
4753
4719
|
}
|
|
4754
4720
|
if (typeEquals(seed.type, arrayOf(mapped.elem))) {
|
|
@@ -4770,7 +4736,7 @@ export function lowerNew(L, expr) {
|
|
|
4770
4736
|
ts.isArrayLiteralExpression(expr.arguments[0]) &&
|
|
4771
4737
|
!expr.arguments[0].elements.some(ts.isSpreadElement)) {
|
|
4772
4738
|
const lit = expr.arguments[0];
|
|
4773
|
-
const elems = lit.elements.map((el) =>
|
|
4739
|
+
const elems = lit.elements.map((el) => lowerer.lowerExpr(el));
|
|
4774
4740
|
const first = elems[0];
|
|
4775
4741
|
if (first !== undefined &&
|
|
4776
4742
|
(first.type.kind === "string" || first.type.kind === "f64") &&
|
|
@@ -4781,35 +4747,35 @@ export function lowerNew(L, expr) {
|
|
|
4781
4747
|
}
|
|
4782
4748
|
}
|
|
4783
4749
|
if ((expr.arguments?.length ?? 0) > 0) {
|
|
4784
|
-
|
|
4750
|
+
lowerer.noLowering("new Set(values)", expr, "construct the Set empty and add() each value — only an array of " +
|
|
4785
4751
|
"already-legal elements (string or number) seeds a Set");
|
|
4786
4752
|
}
|
|
4787
4753
|
if (mapped?.kind === "set")
|
|
4788
4754
|
return { kind: "setNew", type: mapped, loc };
|
|
4789
|
-
const targs =
|
|
4755
|
+
const targs = lowerer.checker.getTypeArguments(tsType);
|
|
4790
4756
|
if (targs[0]) {
|
|
4791
|
-
|
|
4757
|
+
lowerer.unsupported("SC1090", expr, `Set elements of type '${lowerer.checker.typeToString(targs[0])}' ` +
|
|
4792
4758
|
`(Set elements must be string or number — Map's key kinds — or a server handle, which stores under reference identity)`);
|
|
4793
4759
|
}
|
|
4794
|
-
|
|
4760
|
+
lowerer.badType(expr, tsType);
|
|
4795
4761
|
}
|
|
4796
4762
|
// `new AsyncLocalStorage()` (node:async_hooks): a fresh store id —
|
|
4797
|
-
// an f64 handle (
|
|
4763
|
+
// an f64 handle (type-mapper.ts), the Channel story. Construction options
|
|
4798
4764
|
// ({ defaultValue, name }) have no lowering yet.
|
|
4799
|
-
if (symbol?.name === "AsyncLocalStorage" &&
|
|
4765
|
+
if (symbol?.name === "AsyncLocalStorage" && lowerer.isStdlibSymbol(symbol)) {
|
|
4800
4766
|
if ((expr.arguments?.length ?? 0) > 0) {
|
|
4801
|
-
|
|
4767
|
+
lowerer.noLowering("new AsyncLocalStorage(options)", expr, "the zero-argument constructor is the supported form (defaultValue/name options have no lowering yet)");
|
|
4802
4768
|
}
|
|
4803
4769
|
return { kind: "libCall", fn: "als.new", args: [], type: F64, loc };
|
|
4804
4770
|
}
|
|
4805
4771
|
// `new Promise<T>((resolve) => ...)`: the ambient Promise constructor.
|
|
4806
|
-
if (symbol?.name === "Promise" &&
|
|
4807
|
-
const type =
|
|
4772
|
+
if (symbol?.name === "Promise" && lowerer.isStdlibSymbol(symbol)) {
|
|
4773
|
+
const type = lowerer.irTypeOf(expr);
|
|
4808
4774
|
if (type.kind !== "promise")
|
|
4809
|
-
|
|
4775
|
+
lowerer.badType(expr, lowerer.typeOf(expr));
|
|
4810
4776
|
const args = expr.arguments ?? [];
|
|
4811
4777
|
if (args.length !== 1) {
|
|
4812
|
-
|
|
4778
|
+
lowerer.unsupported("SC1090", expr, "Promise construction without an executor");
|
|
4813
4779
|
}
|
|
4814
4780
|
// `new Promise(setImmediate)` (the Node-suite early-exit shape):
|
|
4815
4781
|
// the executor IS the stdlib setImmediate, so resolve rides the
|
|
@@ -4818,9 +4784,9 @@ export function lowerNew(L, expr) {
|
|
|
4818
4784
|
{
|
|
4819
4785
|
const a0 = args[0];
|
|
4820
4786
|
if (ts.isIdentifier(a0) && a0.text === "setImmediate") {
|
|
4821
|
-
const sym =
|
|
4822
|
-
const decls = sym ?
|
|
4823
|
-
if (decls.length > 0 && decls.every((d) =>
|
|
4787
|
+
const sym = lowerer.checker.getSymbolAtLocation(a0);
|
|
4788
|
+
const decls = sym ? lowerer.checker.declarationsOf(sym) : [];
|
|
4789
|
+
if (decls.length > 0 && decls.every((d) => lowerer.isStdlibFile(d.getSourceFile()))) {
|
|
4824
4790
|
// The settled value is the undefined dyn value — the result
|
|
4825
4791
|
// is promise<dyn> whatever T the checker inferred for the
|
|
4826
4792
|
// unusual executor (Promise<unknown> in the suite's shape).
|
|
@@ -4841,9 +4807,9 @@ export function lowerNew(L, expr) {
|
|
|
4841
4807
|
// catch-side instanceof and the uncaught printer working). First
|
|
4842
4808
|
// settle wins, exactly JS: reject-after-resolve and double-reject
|
|
4843
4809
|
// are no-ops, and an executor throw after any settle is swallowed.
|
|
4844
|
-
const executor =
|
|
4810
|
+
const executor = lowerer.lowerExpr(args[0]);
|
|
4845
4811
|
if (executor.type.kind !== "func")
|
|
4846
|
-
|
|
4812
|
+
lowerer.badType(args[0], lowerer.typeOf(args[0]));
|
|
4847
4813
|
if (executor.type.params.length > 1) {
|
|
4848
4814
|
const rj = executor.type.params[1];
|
|
4849
4815
|
if (executor.type.params.length > 2 ||
|
|
@@ -4854,7 +4820,7 @@ export function lowerNew(L, expr) {
|
|
|
4854
4820
|
rj.params[0].className !== "%Error") {
|
|
4855
4821
|
// A non-contextually-typed executor VALUE whose second param
|
|
4856
4822
|
// isn't the pinned (reason: Error) => void shape.
|
|
4857
|
-
|
|
4823
|
+
lowerer.unsupported("SC1090", args[0], "Promise executors whose reject parameter is not '(reason: Error) => void'");
|
|
4858
4824
|
}
|
|
4859
4825
|
}
|
|
4860
4826
|
return { kind: "newPromise", executor, type, loc };
|
|
@@ -4865,7 +4831,7 @@ export function lowerNew(L, expr) {
|
|
|
4865
4831
|
// TextEncoder, ...) typechecks and reports SC2020 here. The named
|
|
4866
4832
|
// families carry pointed hints: each states WHY no honest static
|
|
4867
4833
|
// lowering exists (or what to use instead).
|
|
4868
|
-
if (
|
|
4834
|
+
if (lowerer.isStdlibSymbol(symbol ?? undefined)) {
|
|
4869
4835
|
// The deprecated `new Buffer(string, encoding?)` ctor's string arm
|
|
4870
4836
|
// with a NON-STRING first argument and a string second: Node
|
|
4871
4837
|
// throws ERR_INVALID_ARG_TYPE synchronously (and DEP0005 never
|
|
@@ -4875,16 +4841,16 @@ export function lowerNew(L, expr) {
|
|
|
4875
4841
|
if (expr.expression.text === "Buffer" &&
|
|
4876
4842
|
expr.arguments?.length === 2 &&
|
|
4877
4843
|
!expr.arguments.some(ts.isSpreadElement) &&
|
|
4878
|
-
|
|
4879
|
-
|
|
4880
|
-
const first =
|
|
4881
|
-
if (first.kind === "unitLit" || first.type.kind === "dyn" ||
|
|
4844
|
+
lowerer.mapTypeOf(lowerer.typeOf(expr.arguments[0]))?.kind !== "string" &&
|
|
4845
|
+
lowerer.mapTypeOf(lowerer.typeOf(expr.arguments[1]))?.kind === "string") {
|
|
4846
|
+
const first = lowerer.lowerExpr(expr.arguments[0]);
|
|
4847
|
+
if (first.kind === "unitLit" || first.type.kind === "dyn" || lowerer.dynConvertible(first.type)) {
|
|
4882
4848
|
const got = first.type.kind === "dyn" ? first : { kind: "dynFrom", value: first, type: DYN, loc };
|
|
4883
4849
|
// The encoding argument still evaluates in Node before the
|
|
4884
4850
|
// throw only via the ctor body's later reads — it does NOT
|
|
4885
4851
|
// observe it before throwing, so dropping it is exact for
|
|
4886
4852
|
// effect-free operands; effectful ones keep the fence.
|
|
4887
|
-
const enc =
|
|
4853
|
+
const enc = lowerer.lowerExpr(expr.arguments[1]);
|
|
4888
4854
|
if (enc.kind === "strLit" || pureReemittable(enc)) {
|
|
4889
4855
|
return { kind: "libCall", fn: "buffer.newStringFail", args: [got], type: bytesOf("u8"), loc };
|
|
4890
4856
|
}
|
|
@@ -4904,7 +4870,7 @@ export function lowerNew(L, expr) {
|
|
|
4904
4870
|
Proxy: "property-access metaprogramming has no static lowering (every property read must resolve at compile time)",
|
|
4905
4871
|
Function: "runtime code generation cannot be compiled ahead of time (the eval stance) — write the function",
|
|
4906
4872
|
};
|
|
4907
|
-
|
|
4873
|
+
lowerer.noLowering(`new ${expr.expression.text}`, expr, ctorHints[expr.expression.text], symbol);
|
|
4908
4874
|
}
|
|
4909
4875
|
}
|
|
4910
4876
|
// `new crypto.X509Certificate(data)` — the Dirent-style data record:
|
|
@@ -4923,37 +4889,37 @@ export function lowerNew(L, expr) {
|
|
|
4923
4889
|
const callee = expr.expression;
|
|
4924
4890
|
const isX509 = (ts.isPropertyAccessExpression(callee) &&
|
|
4925
4891
|
callee.name.text === "X509Certificate" &&
|
|
4926
|
-
|
|
4892
|
+
lowerer.builtinNamespaceModuleOf(callee.expression) === "crypto") ||
|
|
4927
4893
|
(ts.isIdentifier(callee) &&
|
|
4928
4894
|
(() => {
|
|
4929
|
-
const bi =
|
|
4895
|
+
const bi = lowerer.builtinImportOf(callee);
|
|
4930
4896
|
return bi?.module === "crypto" && bi.member === "X509Certificate";
|
|
4931
4897
|
})());
|
|
4932
4898
|
if (isX509) {
|
|
4933
4899
|
const args = expr.arguments ?? [];
|
|
4934
4900
|
if (args.length !== 1) {
|
|
4935
|
-
|
|
4901
|
+
lowerer.noLowering("X509Certificate with this argument shape", expr, "the supported form is new X509Certificate(readFileSync(path))");
|
|
4936
4902
|
}
|
|
4937
|
-
const data =
|
|
4903
|
+
const data = lowerer.lowerExpr(args[0]);
|
|
4938
4904
|
const isBytes = data.type.kind === "bytes" && data.type.elem === "u8";
|
|
4939
4905
|
if (!isBytes && data.type.kind !== "string") {
|
|
4940
|
-
|
|
4906
|
+
lowerer.noLowering(`X509Certificate over '${lowerer.fmt(data.type)}' data`, args[0], "pass the certificate Buffer or PEM string (an fs.readFileSync result)");
|
|
4941
4907
|
}
|
|
4942
|
-
const t =
|
|
4908
|
+
const t = lowerer.mapTypeOf(lowerer.typeOf(expr));
|
|
4943
4909
|
if (t?.kind !== "record")
|
|
4944
|
-
|
|
4910
|
+
lowerer.badType(expr, lowerer.typeOf(expr));
|
|
4945
4911
|
const key = `x509.record:${isBytes ? "bytes" : "str"}`;
|
|
4946
|
-
let helper =
|
|
4912
|
+
let helper = lowerer.widthHelpers.get(key);
|
|
4947
4913
|
if (!helper) {
|
|
4948
|
-
helper = `%x509.record.${
|
|
4949
|
-
|
|
4914
|
+
helper = `%x509.record.${lowerer.widthHelpers.size}`;
|
|
4915
|
+
lowerer.widthHelpers.set(key, helper);
|
|
4950
4916
|
const dataT = data.type;
|
|
4951
4917
|
const dRef = { kind: "varRef", localId: "d.0", type: dataT, loc };
|
|
4952
4918
|
const field = (name, fn) => ({
|
|
4953
4919
|
name,
|
|
4954
4920
|
value: { kind: "libCall", fn, args: [dRef], type: STRING, loc },
|
|
4955
4921
|
});
|
|
4956
|
-
|
|
4922
|
+
lowerer.liftedFns.push({
|
|
4957
4923
|
name: helper,
|
|
4958
4924
|
params: [{ localId: "d.0", name: "d", type: dataT }],
|
|
4959
4925
|
returnType: t,
|
|
@@ -4989,47 +4955,47 @@ export function lowerNew(L, expr) {
|
|
|
4989
4955
|
// callee (unannotated heterogeneous registries) keeps a pointed
|
|
4990
4956
|
// fence — annotate the slot with the common constructor type.
|
|
4991
4957
|
{
|
|
4992
|
-
const calleeT =
|
|
4958
|
+
const calleeT = lowerer.mapTypeOf(lowerer.typeOf(expr.expression));
|
|
4993
4959
|
if (calleeT?.kind === "classval") {
|
|
4994
|
-
let info =
|
|
4960
|
+
let info = lowerer.classes.get(calleeT.className);
|
|
4995
4961
|
if (!info && ts.isPropertyAccessExpression(expr.expression) && ts.isIdentifier(expr.expression.name)) {
|
|
4996
4962
|
// A property-assigned class expression not collected yet (this
|
|
4997
4963
|
// body lowers ahead of the assignment statement — hoisted
|
|
4998
4964
|
// functions): collect it on demand and retry, keeping the
|
|
4999
4965
|
// dynamic newValue dispatch below (the runtime field value
|
|
5000
4966
|
// decides, exactly Node under reassignment-through-aliases).
|
|
5001
|
-
propertyAssignedClassInfoOf(
|
|
5002
|
-
info =
|
|
4967
|
+
propertyAssignedClassInfoOf(lowerer, lowerer.checker.getSymbolAtLocation(expr.expression.name));
|
|
4968
|
+
info = lowerer.classes.get(calleeT.className);
|
|
5003
4969
|
}
|
|
5004
4970
|
if (!info) {
|
|
5005
4971
|
// The TYPE world names a class the lowering never registered —
|
|
5006
4972
|
// a fenced class expression, an abstract/deferred declaration:
|
|
5007
4973
|
// flush its own diagnostics (they tell the real story) and
|
|
5008
4974
|
// poison this construction site, never an ICE.
|
|
5009
|
-
|
|
5010
|
-
|
|
4975
|
+
lowerer.flushDeferredClass(calleeT.className);
|
|
4976
|
+
lowerer.unsupported("SC1090", expr, "constructing through a class value whose class has no lowering (the class declaration itself was rejected — see its own diagnostic)");
|
|
5011
4977
|
}
|
|
5012
4978
|
// A classval of a generic FAMILY (`new () => Box<any>` slots): no
|
|
5013
4979
|
// single constructor ABI exists to complete against. No producer
|
|
5014
4980
|
// can fill such a slot (family values and widenings both fence),
|
|
5015
4981
|
// so the construction site is the honest place to name it.
|
|
5016
4982
|
if (info.generic) {
|
|
5017
|
-
|
|
4983
|
+
lowerer.unsupported("SC1090", expr, "constructing through a class value of an uninstantiated generic type (annotate the slot with a concrete instantiation — e.g. 'new (v: number) => Box<number>')");
|
|
5018
4984
|
}
|
|
5019
|
-
const callee =
|
|
4985
|
+
const callee = lowerer.lowerExpr(expr.expression);
|
|
5020
4986
|
if (callee.type.kind !== "classval")
|
|
5021
|
-
|
|
5022
|
-
|
|
4987
|
+
lowerer.badType(expr.expression, lowerer.typeOf(expr.expression));
|
|
4988
|
+
lowerer.noteEdge(`%${info.def.name}.constructor`);
|
|
5023
4989
|
// Every constructor a value in this slot can dispatch to is a
|
|
5024
4990
|
// descendant's — mark them reachable like a virtual edge.
|
|
5025
4991
|
const below = (c) => {
|
|
5026
4992
|
for (const s of c.subclasses) {
|
|
5027
|
-
|
|
4993
|
+
lowerer.noteEdge(`%${s.def.name}.constructor`);
|
|
5028
4994
|
below(s);
|
|
5029
4995
|
}
|
|
5030
4996
|
};
|
|
5031
4997
|
below(info);
|
|
5032
|
-
const args =
|
|
4998
|
+
const args = lowerer.completeArgs(expr.arguments ?? [], info.ctorParams, loc, expr);
|
|
5033
4999
|
return {
|
|
5034
5000
|
kind: "newValue",
|
|
5035
5001
|
callee,
|
|
@@ -5039,9 +5005,9 @@ export function lowerNew(L, expr) {
|
|
|
5039
5005
|
};
|
|
5040
5006
|
}
|
|
5041
5007
|
if (calleeT?.kind === "union") {
|
|
5042
|
-
const def =
|
|
5008
|
+
const def = lowerer.unions.get(calleeT.unionId);
|
|
5043
5009
|
if (def?.arms.some((a) => a.kind === "classval")) {
|
|
5044
|
-
|
|
5010
|
+
lowerer.unsupported("SC1090", expr, "constructing through a union of class values (annotate the slot with the common constructor type — e.g. `new () => Base` — or narrow first)");
|
|
5045
5011
|
}
|
|
5046
5012
|
}
|
|
5047
5013
|
}
|
|
@@ -5058,45 +5024,45 @@ export function lowerNew(L, expr) {
|
|
|
5058
5024
|
? expr.expression.name
|
|
5059
5025
|
: null;
|
|
5060
5026
|
if (ctorName !== null) {
|
|
5061
|
-
const raw =
|
|
5062
|
-
const sym = raw && raw.flags & ts.SymbolFlags.Alias ?
|
|
5063
|
-
const hint = sym &&
|
|
5027
|
+
const raw = lowerer.checker.getSymbolAtLocation(ctorName);
|
|
5028
|
+
const sym = raw && raw.flags & ts.SymbolFlags.Alias ? lowerer.checker.getAliasedSymbol(raw) : raw;
|
|
5029
|
+
const hint = sym && lowerer.isStdlibSymbol(sym) ? own(STDLIB_CTOR_HINTS, sym.name) : undefined;
|
|
5064
5030
|
if (hint !== undefined) {
|
|
5065
|
-
|
|
5031
|
+
lowerer.unsupported("SC1090", expr, hint);
|
|
5066
5032
|
}
|
|
5067
5033
|
}
|
|
5068
5034
|
}
|
|
5069
|
-
|
|
5035
|
+
lowerer.unsupported("SC1090", expr, "constructing values other than classes declared in the program");
|
|
5070
5036
|
}
|
|
5071
5037
|
/** A getter/setter invocation over an accessor target's receiver — the
|
|
5072
5038
|
* same whole-program devirtualization as method calls: a virtualCall
|
|
5073
5039
|
* when some strict subclass of the receiver's static class overrides
|
|
5074
5040
|
* this HALF of the accessor (get and set devirtualize independently),
|
|
5075
5041
|
* a direct call of the nearest declaration otherwise. */
|
|
5076
|
-
export function accessorCall(
|
|
5077
|
-
const info =
|
|
5042
|
+
export function accessorCall(lowerer, className, member, obj, extraArgs, ret, loc) {
|
|
5043
|
+
const info = lowerer.classes.get(className);
|
|
5078
5044
|
if (!info)
|
|
5079
|
-
throw new
|
|
5080
|
-
const found =
|
|
5045
|
+
throw new InternalCompilerError(`lowerer bug: accessor call on unknown class ${className}`);
|
|
5046
|
+
const found = lowerer.findMethodOn(info, member);
|
|
5081
5047
|
if (!found)
|
|
5082
|
-
throw new
|
|
5048
|
+
throw new InternalCompilerError(`lowerer bug: no ${member} on ${className}`);
|
|
5083
5049
|
// The abstract direct-call fence, accessor form (see
|
|
5084
5050
|
// lowerObjectMethodCall): an abstract accessor with no concrete
|
|
5085
5051
|
// override below has no implementation for a direct call to target.
|
|
5086
|
-
if (found.sig.abstract === true && !
|
|
5087
|
-
|
|
5052
|
+
if (found.sig.abstract === true && !lowerer.overrideBelow(info, member)) {
|
|
5053
|
+
lowerer.pushDiag(unsupportedDiag("SC1090", loc, `${member.startsWith("get:") ? "reads" : "writes"} of the abstract accessor '${member.slice(4)}' with no concrete implementation below the receiver's static class`));
|
|
5088
5054
|
throw new PoisonError();
|
|
5089
5055
|
}
|
|
5090
|
-
if (
|
|
5091
|
-
|
|
5056
|
+
if (lowerer.overrideBelow(info, member))
|
|
5057
|
+
lowerer.noteVirtualEdge(info, member);
|
|
5092
5058
|
else
|
|
5093
|
-
|
|
5094
|
-
if (
|
|
5059
|
+
lowerer.noteEdge(`%${found.declarer.def.name}.${member}`);
|
|
5060
|
+
if (lowerer.overrideBelow(info, member)) {
|
|
5095
5061
|
return {
|
|
5096
5062
|
kind: "virtualCall",
|
|
5097
5063
|
className: info.def.name,
|
|
5098
5064
|
method: member,
|
|
5099
|
-
args: [
|
|
5065
|
+
args: [lowerer.upcastTo(obj, info.def.name), ...extraArgs],
|
|
5100
5066
|
type: ret,
|
|
5101
5067
|
loc,
|
|
5102
5068
|
};
|
|
@@ -5104,7 +5070,7 @@ export function accessorCall(L, className, member, obj, extraArgs, ret, loc) {
|
|
|
5104
5070
|
return {
|
|
5105
5071
|
kind: "call",
|
|
5106
5072
|
callee: `%${found.declarer.def.name}.${member}`,
|
|
5107
|
-
args: [
|
|
5073
|
+
args: [lowerer.upcastTo(obj, found.declarer.def.name), ...extraArgs],
|
|
5108
5074
|
type: ret,
|
|
5109
5075
|
loc,
|
|
5110
5076
|
};
|
|
@@ -5113,7 +5079,7 @@ export function accessorCall(L, className, member, obj, extraArgs, ret, loc) {
|
|
|
5113
5079
|
* `time = async <T>(...) => {...}` or `= function g<T>(...) {...}`
|
|
5114
5080
|
* (parens stripped): bindingGenericFnNodeOf's shape rule, member form.
|
|
5115
5081
|
* Null when the field isn't that shape. */
|
|
5116
|
-
|
|
5082
|
+
function genericFieldFnNodeOf(member) {
|
|
5117
5083
|
if (member.initializer === undefined)
|
|
5118
5084
|
return null;
|
|
5119
5085
|
let init = member.initializer;
|