@scriptc/compiler 0.0.35 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ambient/scriptc-node-fallback.d.ts +14 -11
- package/ambient/scriptc-overrides.d.ts +0 -7
- package/ambient/scriptc.d.ts +1 -1
- 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 +7937 -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 +4 -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} +395 -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} +242 -304
- package/dist/backend/c/walkers.js.map +1 -0
- package/dist/backend/external-c.d.ts +33 -0
- package/dist/backend/external-c.js +51 -0
- package/dist/backend/external-c.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/link-plan.d.ts +37 -0
- package/dist/backend/link-plan.js +26 -0
- package/dist/backend/link-plan.js.map +1 -0
- package/dist/backend/linker.d.ts +22 -0
- package/dist/backend/linker.js +236 -0
- package/dist/backend/linker.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 +159 -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 +548 -9988
- 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 +637 -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 +153 -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 +642 -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 +420 -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/unsupported.d.ts +1 -1
- package/dist/backend/llvm/walkers.d.ts +2 -3
- package/dist/backend/llvm/walkers.js +195 -222
- package/dist/backend/llvm/walkers.js.map +1 -1
- package/dist/backend/native-codegen.d.ts +45 -0
- package/dist/backend/native-codegen.js +275 -0
- package/dist/backend/native-codegen.js.map +1 -0
- package/dist/backend/native-codegen.test.js +226 -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 +243 -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} +54 -34
- package/dist/backend/{cc.js → native-toolchain.js} +231 -810
- 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} +219 -7
- package/dist/backend/native-toolchain.test.js.map +1 -0
- package/dist/backend/object-localize.js +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-targets.test.d.ts +1 -0
- package/dist/backend/runtime-pack-targets.test.js +54 -0
- package/dist/backend/runtime-pack-targets.test.js.map +1 -0
- package/dist/backend/runtime-pack.d.ts +112 -0
- package/dist/backend/runtime-pack.js +305 -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 +473 -0
- package/dist/backend/runtime-pack.test.js.map +1 -0
- package/dist/backend/targets.d.ts +76 -0
- package/dist/backend/targets.js +262 -0
- package/dist/backend/targets.js.map +1 -0
- package/dist/backend/targets.test.d.ts +1 -0
- package/dist/backend/targets.test.js +41 -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 +35 -33
- 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/{early-cache.d.ts → executable-cache.d.ts} +2 -2
- package/dist/executable/{early-cache.js → executable-cache.js} +36 -98
- package/dist/executable/executable-cache.js.map +1 -0
- package/dist/executable/executable-cache.test.d.ts +1 -0
- package/dist/executable/{early-cache.test.js → executable-cache.test.js} +3 -3
- package/dist/executable/executable-cache.test.js.map +1 -0
- package/dist/ffi/{profile.js → ffi-manifest.js} +1 -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/array-values.d.ts +8 -0
- package/dist/frontend/lowering/array-values.js +60 -0
- package/dist/frontend/lowering/array-values.js.map +1 -0
- 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-array-sort.d.ts +22 -0
- package/dist/frontend/lowering/lower-array-sort.js +619 -0
- package/dist/frontend/lowering/lower-array-sort.js.map +1 -0
- 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 +1285 -1233
- package/dist/frontend/lowering/lower-builtins.js.map +1 -1
- package/dist/frontend/lowering/lower-calls.d.ts +59 -192
- package/dist/frontend/lowering/lower-calls.js +1644 -1506
- 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 +1984 -2227
- 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 +60 -264
- package/dist/frontend/lowering/lower-exprs.js +2361 -1765
- 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 +273 -261
- package/dist/frontend/lowering/lower-inspect.js.map +1 -1
- package/dist/frontend/lowering/lower-island.d.ts +39 -49
- package/dist/frontend/lowering/lower-island.js +447 -513
- 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 +85 -85
- 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 +909 -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 +1463 -1285
- 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 +134 -22
- package/dist/frontend/lowering/lowerer.js +1277 -126
- 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 +24 -35
- package/dist/frontend/lowering/surfaces.js +78 -66
- 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 +9 -4
- 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.js +2 -36
- 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} +12 -11
- 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 +113 -31
- package/dist/index.js +536 -128
- 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} +119 -37
- package/dist/ir/{nodes.js → ir.js} +170 -166
- 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 +2 -2
- package/dist/ir/serialize.js +3 -2
- package/dist/ir/serialize.js.map +1 -1
- package/dist/ir/validate.d.ts +1 -1
- package/dist/ir/validate.js +93 -41
- 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/{implementation-identity.js → compiler-self-identity.js} +1 -1
- 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 +8 -2
- package/dist/library/fence-eval.js.map +1 -1
- package/dist/library/int-infer.d.ts +1 -36
- package/dist/library/int-infer.js +29 -18
- package/dist/library/int-infer.js.map +1 -1
- package/dist/library/int-infer.test.js +4 -4
- package/dist/library/{early-cache.d.ts → library-cache.d.ts} +1 -1
- package/dist/library/{early-cache.js → library-cache.js} +34 -89
- 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} +7 -7
- 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 +11 -3
- package/dist/startup-cache.js +17 -2
- package/dist/startup-cache.js.map +1 -1
- package/package.json +19 -2
- package/surface-manifest.json +5 -5
- 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/executable/early-cache.js.map +0 -1
- package/dist/executable/early-cache.test.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/implementation-identity.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/{executable/early-cache.test.d.ts → backend/llvm/shapes.test.d.ts} +0 -0
- /package/dist/{library/early-cache.test.d.ts → backend/native-codegen.test.d.ts} +0 -0
- /package/dist/ffi/{profile.d.ts → ffi-manifest.d.ts} +0 -0
- /package/dist/frontend/ts7/{program.d.ts → program-adapter.d.ts} +0 -0
- /package/dist/library/{implementation-identity.d.ts → compiler-self-identity.d.ts} +0 -0
|
@@ -11,17 +11,13 @@
|
|
|
11
11
|
import * as ts from "../ts7/adapter.js";
|
|
12
12
|
import { ladderFenceExpr, nodeThrowExpr } from "./lowerer.js";
|
|
13
13
|
import { isJsSourceFile, locOf } from "../program.js";
|
|
14
|
-
import { arrayOf, BOOL, BYTES_U8, canBoxFuncIntoDyn, canConvertToDyn, DYN, DYN_HANDLE_KINDS, F64, funcOf, HTTP2SESSION_T, HTTP2STREAM_T, HTTPCLIENTREQ_T, HTTPREQ_T, HTTPRES_T, NETSERVER_T, NETSOCKET_T, NULL_T, SECURECTX_T, STRING, UNDEFINED_T, typeKey, VOID } from "../../ir/
|
|
14
|
+
import { arrayOf, BOOL, BYTES_U8, canBoxFuncIntoDyn, canConvertToDyn, DYN, DYN_HANDLE_KINDS, F64, funcOf, HTTP2SESSION_T, HTTP2STREAM_T, HTTPCLIENTREQ_T, HTTPREQ_T, HTTPRES_T, NETSERVER_T, NETSOCKET_T, NULL_T, SECURECTX_T, STRING, UNDEFINED_T, typeKey, VOID } from "../../ir/ir.js";
|
|
15
15
|
import { AGENT_DOCUMENTED_OPTIONS, builtinFenceHintOf, builtinModuleFnOf, fenceOrDropOptionKey, HTTP_CLIENT_DOCUMENTED_OPTIONS, HTTP_SERVER_DOCUMENTED_OPTIONS, HTTP2_SECURE_SERVER_DOCUMENTED_OPTIONS, HTTPS_CLIENT_DOCUMENTED_OPTIONS, sideEffectFreeOptionValue, TLS_SERVER_DOCUMENTED_OPTIONS, } from "./surfaces.js";
|
|
16
16
|
import { conditionalSpreadOf } from "./lower-exprs.js";
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
"createServer",
|
|
22
|
-
"connect",
|
|
23
|
-
"createConnection",
|
|
24
|
-
]);
|
|
17
|
+
import { boolLit, numLit, strLit, varRef } from "../../ir/build.js";
|
|
18
|
+
import { resultIsDiscarded } from "./call-position.js";
|
|
19
|
+
import { lowerCallbackArg as lowerCallbackArgShared } from "./callback-arg.js";
|
|
20
|
+
import { pairsSnapshotHelper } from "./pairs-snapshot.js";
|
|
25
21
|
const NARROW_DATA_HINT = 'write/end take one string or one Uint8Array/Buffer value (narrow unions first)';
|
|
26
22
|
/** The writable numeric http.Server timeout fields. The selector is an
|
|
27
23
|
* internal runtime ABI shared by the get/set libCalls (kept here beside
|
|
@@ -40,10 +36,104 @@ function httpServerTimeoutField(name) {
|
|
|
40
36
|
* arrow bodies (`socket.on("error", () => socket.destroy())` — the shape
|
|
41
37
|
* portless is made of); anything that would CONSUME the result (Node
|
|
42
38
|
* returns `this`/a boolean where this surface returns void) is fenced. */
|
|
43
|
-
function requireStatementPosition(
|
|
44
|
-
if (
|
|
39
|
+
function requireStatementPosition(lowerer, call, what) {
|
|
40
|
+
if (resultIsDiscarded(call))
|
|
45
41
|
return;
|
|
46
|
-
|
|
42
|
+
lowerer.unsupported("SC1090", call, `using the result of ${what} (the result is void here — call it as its own statement)`);
|
|
43
|
+
}
|
|
44
|
+
/** Node enables net.Server's reusePort socket option only for the exact
|
|
45
|
+
* boolean value true. This is intentionally different from the ordinary
|
|
46
|
+
* condition lowering used by ipv6Only: JavaScript values such as 1 and
|
|
47
|
+
* "true" are truthy, but Node leaves reusePort disabled for them.
|
|
48
|
+
*
|
|
49
|
+
* Typed boolean-or-undefined option bindings are represented by the normal
|
|
50
|
+
* tagged union. Initializers narrow that union to its boolean arm; shorthand
|
|
51
|
+
* properties are required to have the concrete BOOL shape below, matching
|
|
52
|
+
* ipv6Only's static option-record fence. Other static values still evaluate
|
|
53
|
+
* once, then produce false; checked-dynamic and island values use strict
|
|
54
|
+
* equality against the boolean true. */
|
|
55
|
+
function lowerExactReusePort(lowerer, value, node) {
|
|
56
|
+
const loc = locOf(node);
|
|
57
|
+
const falseValue = () => boolLit(false, loc);
|
|
58
|
+
if (value.type.kind === "bool")
|
|
59
|
+
return value;
|
|
60
|
+
if (value.type.kind === "dyn") {
|
|
61
|
+
return {
|
|
62
|
+
kind: "dynScalarEq",
|
|
63
|
+
left: value,
|
|
64
|
+
right: boolLit(true, loc),
|
|
65
|
+
type: BOOL,
|
|
66
|
+
loc,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
if (value.type.kind === "jsval") {
|
|
70
|
+
return {
|
|
71
|
+
kind: "jsOp",
|
|
72
|
+
op: "eq",
|
|
73
|
+
args: [value, lowerer.jsvalIn(boolLit(true, loc), node)],
|
|
74
|
+
type: BOOL,
|
|
75
|
+
loc,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
if (value.type.kind === "union") {
|
|
79
|
+
const def = lowerer.unions.get(value.type.unionId);
|
|
80
|
+
const boolTag = def?.arms.findIndex((arm) => arm.kind === "bool") ?? -1;
|
|
81
|
+
if (boolTag >= 0) {
|
|
82
|
+
const local = lowerer.declareHiddenLocal("%listenReusePort", value.type);
|
|
83
|
+
const ref = () => ({ kind: "varRef", localId: local.id, type: value.type, loc });
|
|
84
|
+
return {
|
|
85
|
+
kind: "seqExpr",
|
|
86
|
+
stmts: [{ kind: "varDecl", localId: local.id, init: value, loc }],
|
|
87
|
+
result: {
|
|
88
|
+
kind: "ternary",
|
|
89
|
+
cond: {
|
|
90
|
+
kind: "unionIsTag",
|
|
91
|
+
unionId: value.type.unionId,
|
|
92
|
+
tag: boolTag,
|
|
93
|
+
negated: false,
|
|
94
|
+
value: ref(),
|
|
95
|
+
type: BOOL,
|
|
96
|
+
loc,
|
|
97
|
+
},
|
|
98
|
+
then: {
|
|
99
|
+
kind: "unionNarrow",
|
|
100
|
+
unionId: value.type.unionId,
|
|
101
|
+
tag: boolTag,
|
|
102
|
+
value: ref(),
|
|
103
|
+
type: BOOL,
|
|
104
|
+
loc,
|
|
105
|
+
},
|
|
106
|
+
else_: falseValue(),
|
|
107
|
+
type: BOOL,
|
|
108
|
+
loc,
|
|
109
|
+
},
|
|
110
|
+
type: BOOL,
|
|
111
|
+
loc,
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
// Static non-boolean values cannot equal true, but their initializer may
|
|
116
|
+
// have observable effects (for example, a getter-backed expression).
|
|
117
|
+
// Keep that evaluation in the IR while disabling the kernel option.
|
|
118
|
+
if (value.type.kind === "void") {
|
|
119
|
+
return {
|
|
120
|
+
kind: "seqExpr",
|
|
121
|
+
stmts: [{ kind: "exprStmt", expr: value, loc }],
|
|
122
|
+
result: falseValue(),
|
|
123
|
+
type: BOOL,
|
|
124
|
+
loc,
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
if (value.kind === "unitLit")
|
|
128
|
+
return falseValue();
|
|
129
|
+
const local = lowerer.declareHiddenLocal("%listenReusePort", value.type);
|
|
130
|
+
return {
|
|
131
|
+
kind: "seqExpr",
|
|
132
|
+
stmts: [{ kind: "varDecl", localId: local.id, init: value, loc }],
|
|
133
|
+
result: falseValue(),
|
|
134
|
+
type: BOOL,
|
|
135
|
+
loc,
|
|
136
|
+
};
|
|
47
137
|
}
|
|
48
138
|
/** The %Error param shape the error-listener slots carry. */
|
|
49
139
|
const ERROR_T = { kind: "object", className: "%Error" };
|
|
@@ -51,19 +141,19 @@ const ERROR_T = { kind: "object", className: "%Error" };
|
|
|
51
141
|
* callback rides an interned cast-and-discard adapter (the stream lane's
|
|
52
142
|
* bare-expression-body discard, generalized to closure VALUES): same
|
|
53
143
|
* parameters, void return, the underlying result discarded unread. */
|
|
54
|
-
export function voidizedCallback(
|
|
144
|
+
export function voidizedCallback(lowerer, cb, loc) {
|
|
55
145
|
if (cb.type.kind !== "func" || cb.type.ret.kind === "void")
|
|
56
146
|
return cb;
|
|
57
147
|
const fromT = cb.type;
|
|
58
148
|
const toT = funcOf([...fromT.params], VOID);
|
|
59
149
|
const key = `server.voidcb:${typeKey(fromT)}`;
|
|
60
|
-
const existing =
|
|
61
|
-
const name = existing ?? `%server.voidcb.${
|
|
150
|
+
const existing = lowerer.arrHofHelpers.get(key);
|
|
151
|
+
const name = existing ?? `%server.voidcb.${lowerer.arrHofHelpers.size}`;
|
|
62
152
|
if (!existing) {
|
|
63
|
-
|
|
153
|
+
lowerer.arrHofHelpers.set(key, name);
|
|
64
154
|
const impl = `${name}.impl`;
|
|
65
155
|
const params = fromT.params.map((p, i) => ({ localId: `p${i}.0`, name: `p${i}`, type: p }));
|
|
66
|
-
|
|
156
|
+
lowerer.liftedFns.push({
|
|
67
157
|
name: impl,
|
|
68
158
|
params,
|
|
69
159
|
returnType: VOID,
|
|
@@ -87,7 +177,7 @@ export function voidizedCallback(L, cb, loc) {
|
|
|
87
177
|
],
|
|
88
178
|
loc,
|
|
89
179
|
});
|
|
90
|
-
|
|
180
|
+
lowerer.liftedFns.push({
|
|
91
181
|
name,
|
|
92
182
|
params: [{ localId: "f.0", name: "f", type: fromT }],
|
|
93
183
|
returnType: toT,
|
|
@@ -103,30 +193,41 @@ export function voidizedCallback(L, cb, loc) {
|
|
|
103
193
|
* an interned helper runs the void call and returns its first argument.
|
|
104
194
|
* `prefix` calls (the writeHead statusMessage form's resStatusMsgSet)
|
|
105
195
|
* run first, sharing the same parameter refs. */
|
|
106
|
-
function receiverReturningCall(
|
|
196
|
+
function receiverReturningCall(lowerer, fn, callArgs, recvT, loc, opts) {
|
|
107
197
|
const key = `server.recvret:${fn}:${opts?.prefix?.fn ?? ""}:${(opts?.mainArgIndices ?? []).join(".")}:${callArgs.map((a) => typeKey(a.type)).join(",")}`;
|
|
108
|
-
const existing =
|
|
109
|
-
const name = existing ?? `%server.recvret.${
|
|
198
|
+
const existing = lowerer.arrHofHelpers.get(key);
|
|
199
|
+
const name = existing ?? `%server.recvret.${lowerer.arrHofHelpers.size}`;
|
|
110
200
|
if (!existing) {
|
|
111
|
-
|
|
201
|
+
lowerer.arrHofHelpers.set(key, name);
|
|
112
202
|
const params = callArgs.map((a, i) => ({ localId: `p${i}.0`, name: `p${i}`, type: a.type }));
|
|
113
|
-
const ref = (i) => ({ kind: "varRef", localId: params[i].localId, type: params[i].type, loc });
|
|
114
203
|
const body = [];
|
|
115
204
|
if (opts?.prefix) {
|
|
116
205
|
body.push({
|
|
117
206
|
kind: "exprStmt",
|
|
118
|
-
expr: {
|
|
207
|
+
expr: {
|
|
208
|
+
kind: "libCall",
|
|
209
|
+
fn: opts.prefix.fn,
|
|
210
|
+
args: opts.prefix.argIndices.map((i) => varRef(params[i].localId, params[i].type, loc)),
|
|
211
|
+
type: VOID,
|
|
212
|
+
loc,
|
|
213
|
+
},
|
|
119
214
|
loc,
|
|
120
215
|
});
|
|
121
216
|
}
|
|
122
217
|
const mainIdx = opts?.mainArgIndices ?? params.map((_, i) => i);
|
|
123
218
|
body.push({
|
|
124
219
|
kind: "exprStmt",
|
|
125
|
-
expr: {
|
|
220
|
+
expr: {
|
|
221
|
+
kind: "libCall",
|
|
222
|
+
fn,
|
|
223
|
+
args: mainIdx.map((i) => varRef(params[i].localId, params[i].type, loc)),
|
|
224
|
+
type: VOID,
|
|
225
|
+
loc,
|
|
226
|
+
},
|
|
126
227
|
loc,
|
|
127
228
|
});
|
|
128
|
-
body.push({ kind: "return", value:
|
|
129
|
-
|
|
229
|
+
body.push({ kind: "return", value: varRef(params[0].localId, params[0].type, loc), loc });
|
|
230
|
+
lowerer.liftedFns.push({
|
|
130
231
|
name,
|
|
131
232
|
params,
|
|
132
233
|
returnType: recvT,
|
|
@@ -146,45 +247,12 @@ function receiverReturningCall(L, fn, callArgs, recvT, loc, opts) {
|
|
|
146
247
|
* test/common's mustCall result — adapts through the dynCheck function
|
|
147
248
|
* boundary instead of fencing; handle-carrying events pass no tuple and
|
|
148
249
|
* keep their fences. Returns the lowered closure and its param count. */
|
|
149
|
-
function lowerCallbackArg(
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
cb: { kind: "dynCheck", value: cb, type: toT, loc: locOf(node) },
|
|
155
|
-
nparams: dynTuple.length,
|
|
156
|
-
};
|
|
157
|
-
}
|
|
158
|
-
if (dynTuple !== undefined &&
|
|
159
|
-
cb.type.kind === "func" &&
|
|
160
|
-
(cb.type.rest === true ||
|
|
161
|
-
(cb.type.params.length > 0 && cb.type.params.some((p) => !paramOk(p)))) &&
|
|
162
|
-
cb.type.params.every((p) => p.kind === "dyn") &&
|
|
163
|
-
canBoxFuncIntoDyn(cb.type, (id) => L.shapes.get(id), (id) => L.unions.get(id))) {
|
|
164
|
-
// A plain-JS listener whose params carry no contextual type (a
|
|
165
|
-
// hoisted `function onConn(socket)` — dyn params), or an
|
|
166
|
-
// arguments-reading rest function: box it and adapt through the
|
|
167
|
-
// dynCheck function boundary like the dyn flavor above — the event
|
|
168
|
-
// payloads box into the checked-dynamic tree (handles by reference) and the body's
|
|
169
|
-
// member uses dispatch at runtime.
|
|
170
|
-
const boxed = { kind: "dynFrom", value: cb, type: DYN, loc: locOf(node) };
|
|
171
|
-
const toT = funcOf([...dynTuple], VOID);
|
|
172
|
-
return {
|
|
173
|
-
cb: { kind: "dynCheck", value: boxed, type: toT, loc: locOf(node) },
|
|
174
|
-
nparams: dynTuple.length,
|
|
175
|
-
};
|
|
176
|
-
}
|
|
177
|
-
if (cb.type.kind !== "func" || cb.type.params.length > maxParams) {
|
|
178
|
-
L.unsupported("SC1090", node, `${what} with more than ${maxParams} parameter${maxParams === 1 ? "" : "s"} (${paramHint})`);
|
|
179
|
-
}
|
|
180
|
-
const param = cb.type.params[0];
|
|
181
|
-
if (param !== undefined && !paramOk(param)) {
|
|
182
|
-
L.unsupported("SC1090", node, `${what} whose parameter is not supported (${paramHint})`);
|
|
183
|
-
}
|
|
184
|
-
const adapted = voidizedCallback(L, cb, locOf(node));
|
|
185
|
-
return { cb: adapted, nparams: cb.type.params.length };
|
|
250
|
+
function lowerCallbackArg(lowerer, node, what, maxParams, paramOk, paramHint, dynTuple) {
|
|
251
|
+
return lowerCallbackArgShared(lowerer, node, what, maxParams, paramOk, paramHint, {
|
|
252
|
+
...(dynTuple !== undefined ? { dynTuple } : {}),
|
|
253
|
+
adapt: (cb) => voidizedCallback(lowerer, cb, locOf(node)),
|
|
254
|
+
});
|
|
186
255
|
}
|
|
187
|
-
const boolLit = (value, loc) => ({ kind: "boolLit", value, type: BOOL, loc });
|
|
188
256
|
/** Lowers a handle-kind method receiver. The checker's control flow can
|
|
189
257
|
* narrow an untyped binding to the handle class (`let server; server =
|
|
190
258
|
* createServer(...)` — the keep-alive shape) so the STATIC lowering
|
|
@@ -194,17 +262,17 @@ const boolLit = (value, loc) => ({ kind: "boolLit", value, type: BOOL, loc });
|
|
|
194
262
|
/** True iff `t` is the {address: string, family: string, port: number}
|
|
195
263
|
* record — server.address()'s materialized shape (lower-dgram's
|
|
196
264
|
* AddressInfo check, another receiver). */
|
|
197
|
-
function isAddressInfoRecord(
|
|
265
|
+
function isAddressInfoRecord(lowerer, t) {
|
|
198
266
|
if (t.kind !== "record")
|
|
199
267
|
return false;
|
|
200
|
-
const shape =
|
|
268
|
+
const shape = lowerer.shapes.get(t.shapeId);
|
|
201
269
|
if (!shape || shape.tuple || shape.indexValue || shape.fields.length !== 3)
|
|
202
270
|
return false;
|
|
203
271
|
const want = [["address", "string"], ["family", "string"], ["port", "f64"]];
|
|
204
272
|
return shape.fields.every((f, i) => f.name === want[i][0] && f.type.kind === want[i][1]);
|
|
205
273
|
}
|
|
206
|
-
function
|
|
207
|
-
const recv =
|
|
274
|
+
function coerceToHandle(lowerer, node, want) {
|
|
275
|
+
const recv = lowerer.lowerExpr(node);
|
|
208
276
|
if (recv.type.kind === "dyn" && DYN_HANDLE_KINDS.has(want.kind)) {
|
|
209
277
|
return { kind: "dynCheck", value: recv, type: want, loc: locOf(node) };
|
|
210
278
|
}
|
|
@@ -218,24 +286,22 @@ function handleReceiver(L, node, want) {
|
|
|
218
286
|
* target). Mirrors headersSnapshotHelper's loop, sourced from a pairs
|
|
219
287
|
* array instead of reqHeaderPairs. Null when the shape is not a
|
|
220
288
|
* string-armed pure-index record. */
|
|
221
|
-
function h2HeaderRecordStmts(
|
|
222
|
-
const shape =
|
|
289
|
+
function h2HeaderRecordStmts(lowerer, shapeId, pairsLocal, outLocal, statusLocal, loc) {
|
|
290
|
+
const shape = lowerer.shapes.get(shapeId);
|
|
223
291
|
if (!shape || shape.tuple || shape.fields.length > 0 || !shape.indexValue)
|
|
224
292
|
return null;
|
|
225
293
|
const iv = shape.indexValue;
|
|
226
294
|
if (iv.kind !== "union")
|
|
227
295
|
return null;
|
|
228
|
-
const strTag =
|
|
296
|
+
const strTag = lowerer.armTag(iv.unionId, STRING);
|
|
229
297
|
if (strTag < 0)
|
|
230
298
|
return null;
|
|
231
299
|
const recT = { kind: "record", shapeId };
|
|
232
300
|
const pairsT = arrayOf(STRING);
|
|
233
|
-
const ref = (localId, type) => ({ kind: "varRef", localId, type, loc });
|
|
234
|
-
const num = (value) => ({ kind: "numLit", value, type: F64, loc });
|
|
235
301
|
const pairAt = (offset) => ({
|
|
236
302
|
kind: "arrayGet",
|
|
237
|
-
arr:
|
|
238
|
-
index: offset === 0 ?
|
|
303
|
+
arr: varRef(pairsLocal, pairsT, loc),
|
|
304
|
+
index: offset === 0 ? varRef("i.0", F64, loc) : { kind: "bin", op: "+", left: varRef("i.0", F64, loc), right: numLit(offset, loc), type: F64, loc },
|
|
239
305
|
type: STRING,
|
|
240
306
|
loc,
|
|
241
307
|
});
|
|
@@ -243,28 +309,28 @@ function h2HeaderRecordStmts(L, shapeId, pairsLocal, outLocal, statusLocal, loc)
|
|
|
243
309
|
{ kind: "varDecl", localId: outLocal, init: { kind: "recordLit", fields: [], type: recT, loc }, loc },
|
|
244
310
|
{
|
|
245
311
|
kind: "for",
|
|
246
|
-
init: { kind: "varDecl", localId: "i.0", init:
|
|
312
|
+
init: { kind: "varDecl", localId: "i.0", init: numLit(0, loc), loc },
|
|
247
313
|
cond: {
|
|
248
|
-
kind: "bin", op: "<", left:
|
|
249
|
-
right: { kind: "arrIntrinsic", method: "length", receiver:
|
|
314
|
+
kind: "bin", op: "<", left: varRef("i.0", F64, loc),
|
|
315
|
+
right: { kind: "arrIntrinsic", method: "length", receiver: varRef(pairsLocal, pairsT, loc), args: [], type: F64, loc },
|
|
250
316
|
type: BOOL, loc,
|
|
251
317
|
},
|
|
252
|
-
update: { kind: "assign", localId: "i.0", value: { kind: "bin", op: "+", left:
|
|
318
|
+
update: { kind: "assign", localId: "i.0", value: { kind: "bin", op: "+", left: varRef("i.0", F64, loc), right: numLit(2, loc), type: F64, loc }, loc },
|
|
253
319
|
body: [{
|
|
254
|
-
kind: "recordKeySet", obj:
|
|
320
|
+
kind: "recordKeySet", obj: varRef(outLocal, recT, loc), shapeId, key: pairAt(0),
|
|
255
321
|
value: { kind: "unionWrap", unionId: iv.unionId, tag: strTag, value: pairAt(1), type: iv, loc }, loc,
|
|
256
322
|
}],
|
|
257
323
|
loc,
|
|
258
324
|
},
|
|
259
325
|
];
|
|
260
326
|
if (statusLocal !== null) {
|
|
261
|
-
const f64Tag =
|
|
327
|
+
const f64Tag = lowerer.armTag(iv.unionId, F64);
|
|
262
328
|
if (f64Tag < 0)
|
|
263
329
|
return null;
|
|
264
330
|
stmts.push({
|
|
265
|
-
kind: "recordKeySet", obj:
|
|
331
|
+
kind: "recordKeySet", obj: varRef(outLocal, recT, loc), shapeId,
|
|
266
332
|
key: { kind: "strLit", value: ":status", type: STRING, loc },
|
|
267
|
-
value: { kind: "unionWrap", unionId: iv.unionId, tag: f64Tag, value:
|
|
333
|
+
value: { kind: "unionWrap", unionId: iv.unionId, tag: f64Tag, value: varRef(statusLocal, F64, loc), type: iv, loc }, loc,
|
|
268
334
|
});
|
|
269
335
|
}
|
|
270
336
|
return stmts;
|
|
@@ -287,18 +353,18 @@ function headerParamShapeId(cbT, idx) {
|
|
|
287
353
|
* :status when `withStatus`. Returns the adapter closure (VOID-returning,
|
|
288
354
|
* runtime ABI) to pass into the libCall. */
|
|
289
355
|
/** The canonical header record type — a pure-index record over the
|
|
290
|
-
* header slot `f64 | string | string[] | undefined` (
|
|
356
|
+
* header slot `f64 | string | string[] | undefined` (type-mapper.ts's
|
|
291
357
|
* HEADER-FAMILY canonicalization, rebuilt here for dyn-callback headers
|
|
292
358
|
* with no static param type to read the shape from). */
|
|
293
|
-
function canonicalHeaderRecord(
|
|
359
|
+
function canonicalHeaderRecord(lowerer) {
|
|
294
360
|
const arms = [F64, STRING, arrayOf(STRING), UNDEFINED_T];
|
|
295
361
|
arms.sort((a, b) => (typeKey(a) < typeKey(b) ? -1 : 1));
|
|
296
|
-
const slot = { kind: "union", unionId:
|
|
297
|
-
const shapeId =
|
|
362
|
+
const slot = { kind: "union", unionId: lowerer.unions.intern(arms) };
|
|
363
|
+
const shapeId = lowerer.shapes.intern([], false, slot, []);
|
|
298
364
|
return { type: { kind: "record", shapeId }, shapeId };
|
|
299
365
|
}
|
|
300
|
-
function h2HeadersCallbackAdapter(
|
|
301
|
-
let cb =
|
|
366
|
+
function h2HeadersCallbackAdapter(lowerer, node, what, handleKind, withStatus, loc) {
|
|
367
|
+
let cb = lowerer.lowerExpr(node);
|
|
302
368
|
const maxParams = (handleKind ? 1 : 0) + 1 /* headers */ + 1 /* flags */;
|
|
303
369
|
// A checked-dynamic listener (test/common's mustCall wrapper — a dyn
|
|
304
370
|
// value or an arguments-reading rest function of dyn params): wrap it
|
|
@@ -309,9 +375,9 @@ function h2HeadersCallbackAdapter(L, node, what, handleKind, withStatus, loc) {
|
|
|
309
375
|
const handleT = handleKind === "http2Stream" ? HTTP2STREAM_T : null;
|
|
310
376
|
const isDynCb = cb.type.kind === "dyn" ||
|
|
311
377
|
(cb.type.kind === "func" && (cb.type.rest === true || cb.type.params.every((p) => p.kind === "dyn")) &&
|
|
312
|
-
canBoxFuncIntoDyn(cb.type, (id) =>
|
|
378
|
+
canBoxFuncIntoDyn(cb.type, (id) => lowerer.shapes.get(id), (id) => lowerer.unions.get(id)));
|
|
313
379
|
if (isDynCb) {
|
|
314
|
-
const rec = canonicalHeaderRecord(
|
|
380
|
+
const rec = canonicalHeaderRecord(lowerer);
|
|
315
381
|
const tuple = [];
|
|
316
382
|
if (handleT)
|
|
317
383
|
tuple.push(handleT);
|
|
@@ -322,13 +388,13 @@ function h2HeadersCallbackAdapter(L, node, what, handleKind, withStatus, loc) {
|
|
|
322
388
|
cb = { kind: "dynCheck", value: boxed, type: toT, loc };
|
|
323
389
|
}
|
|
324
390
|
if (cb.type.kind !== "func" || cb.type.ret.kind !== "void" || cb.type.params.length > maxParams) {
|
|
325
|
-
|
|
391
|
+
lowerer.unsupported("SC1090", node, `${what} with more than ${maxParams} parameters or returning a value`);
|
|
326
392
|
}
|
|
327
393
|
const fromT = cb.type;
|
|
328
394
|
const headersIdx = handleT ? 1 : 0;
|
|
329
395
|
const shapeId = fromT.params.length > headersIdx ? headerParamShapeId(fromT, headersIdx) : null;
|
|
330
396
|
if (fromT.params.length > headersIdx && shapeId === null) {
|
|
331
|
-
|
|
397
|
+
lowerer.unsupported("SC1090", node, `${what} whose headers parameter is not a header record (type it IncomingHttpHeaders)`);
|
|
332
398
|
}
|
|
333
399
|
// Runtime ABI params, in order.
|
|
334
400
|
const abiParams = [];
|
|
@@ -340,12 +406,11 @@ function h2HeadersCallbackAdapter(L, node, what, handleKind, withStatus, loc) {
|
|
|
340
406
|
abiParams.push({ localId: "fl.0", name: "fl", type: F64 });
|
|
341
407
|
const toT = funcOf(abiParams.map((p) => p.type), VOID);
|
|
342
408
|
const key = `h2.hdrcb:${handleKind}:${withStatus}:${typeKey(fromT)}:${shapeId ?? ""}`;
|
|
343
|
-
const existing =
|
|
344
|
-
const name = existing ?? `%h2.hdrcb.${
|
|
409
|
+
const existing = lowerer.arrHofHelpers.get(key);
|
|
410
|
+
const name = existing ?? `%h2.hdrcb.${lowerer.arrHofHelpers.size}`;
|
|
345
411
|
if (!existing) {
|
|
346
|
-
|
|
412
|
+
lowerer.arrHofHelpers.set(key, name);
|
|
347
413
|
const impl = `${name}.impl`;
|
|
348
|
-
const ref = (localId, type) => ({ kind: "varRef", localId, type, loc });
|
|
349
414
|
const locals = [
|
|
350
415
|
{ id: "f.0", name: "f", type: fromT, mutable: false, boxed: true },
|
|
351
416
|
...abiParams.map((p) => ({ id: p.localId, name: p.name, type: p.type, mutable: false })),
|
|
@@ -355,30 +420,30 @@ function h2HeadersCallbackAdapter(L, node, what, handleKind, withStatus, loc) {
|
|
|
355
420
|
if (shapeId !== null) {
|
|
356
421
|
locals.push({ id: "out.0", name: "out", type: { kind: "record", shapeId }, mutable: false });
|
|
357
422
|
locals.push({ id: "i.0", name: "i", type: F64, mutable: true });
|
|
358
|
-
const stmts = h2HeaderRecordStmts(
|
|
423
|
+
const stmts = h2HeaderRecordStmts(lowerer, shapeId, "ps.0", "out.0", withStatus ? "st.0" : null, loc);
|
|
359
424
|
if (stmts === null)
|
|
360
|
-
|
|
425
|
+
lowerer.unsupported("SC1090", node, `${what} whose headers parameter is not a supported header record`);
|
|
361
426
|
body.push(...stmts);
|
|
362
|
-
recordRef =
|
|
427
|
+
recordRef = varRef("out.0", { kind: "record", shapeId }, loc);
|
|
363
428
|
}
|
|
364
429
|
// Assemble the user-call arguments in the user's declared order.
|
|
365
430
|
const callArgs = [];
|
|
366
431
|
const push = (e) => { if (callArgs.length < fromT.params.length)
|
|
367
432
|
callArgs.push(e); };
|
|
368
433
|
if (handleT)
|
|
369
|
-
push(
|
|
434
|
+
push(varRef("h.0", handleT, loc));
|
|
370
435
|
if (recordRef)
|
|
371
436
|
push(recordRef);
|
|
372
437
|
else if (fromT.params.length > headersIdx)
|
|
373
|
-
push(
|
|
374
|
-
push(
|
|
438
|
+
push(varRef("ps.0", arrayOf(STRING), loc));
|
|
439
|
+
push(varRef("fl.0", F64, loc));
|
|
375
440
|
body.push({
|
|
376
441
|
kind: "exprStmt",
|
|
377
|
-
expr: { kind: "callValue", callee:
|
|
442
|
+
expr: { kind: "callValue", callee: varRef("f.0", fromT, loc), args: callArgs.slice(0, fromT.params.length), type: fromT.ret, loc },
|
|
378
443
|
loc,
|
|
379
444
|
});
|
|
380
|
-
|
|
381
|
-
|
|
445
|
+
lowerer.liftedFns.push({ name: impl, params: abiParams, returnType: VOID, captures: [{ localId: "f.0", name: "f", type: fromT }], locals, body, loc });
|
|
446
|
+
lowerer.liftedFns.push({
|
|
382
447
|
name, params: [{ localId: "f.0", name: "f", type: fromT }], returnType: toT,
|
|
383
448
|
locals: [{ id: "f.0", name: "f", type: fromT, mutable: false, boxed: true }],
|
|
384
449
|
body: [{ kind: "return", value: { kind: "closure", fnName: impl, captures: ["f.0"], type: toT, loc }, loc }], loc,
|
|
@@ -389,8 +454,8 @@ function h2HeadersCallbackAdapter(L, node, what, handleKind, withStatus, loc) {
|
|
|
389
454
|
/** Builds the flat [name, value, ...] pairs array from an OutgoingHttp
|
|
390
455
|
* Headers argument (an object literal with literal/pseudo keys, or a
|
|
391
456
|
* header Record). Reuses lowerClientHeadersOption's shape. */
|
|
392
|
-
function lowerH2HeadersArg(
|
|
393
|
-
return lowerClientHeadersOption(
|
|
457
|
+
function lowerH2HeadersArg(lowerer, node) {
|
|
458
|
+
return lowerClientHeadersOption(lowerer, node);
|
|
394
459
|
}
|
|
395
460
|
/** The STATICALLY-KNOWN key text of a header-object property: literal
|
|
396
461
|
* identifier/string keys, plus COMPUTED keys that are a bare identifier
|
|
@@ -398,11 +463,11 @@ function lowerH2HeadersArg(L, node) {
|
|
|
398
463
|
* (the portless proxy shape) is as static as spelling the name out, and
|
|
399
464
|
* an identifier read has no side effects to drop. Null for genuinely
|
|
400
465
|
* dynamic keys (the caller fences). */
|
|
401
|
-
function staticHeaderKeyOf(
|
|
466
|
+
function staticHeaderKeyOf(lowerer, prop) {
|
|
402
467
|
if (ts.isIdentifier(prop.name) || ts.isStringLiteral(prop.name))
|
|
403
468
|
return prop.name.text;
|
|
404
469
|
if (ts.isComputedPropertyName(prop.name) && ts.isIdentifier(prop.name.expression)) {
|
|
405
|
-
const t =
|
|
470
|
+
const t = lowerer.typeOf(prop.name.expression);
|
|
406
471
|
if (t.isStringLiteralType())
|
|
407
472
|
return t.value;
|
|
408
473
|
}
|
|
@@ -413,20 +478,20 @@ function staticHeaderKeyOf(L, prop) {
|
|
|
413
478
|
* (the caller falls through to the ordinary builtin tables); every net
|
|
414
479
|
* member lands here — unlowered ones fence with their module-qualified
|
|
415
480
|
* name. */
|
|
416
|
-
export function lowerNetModuleCall(
|
|
481
|
+
export function lowerNetModuleCall(lowerer, expr, bi, loc) {
|
|
417
482
|
if (bi.module === "http")
|
|
418
|
-
return lowerHttpModuleCall(
|
|
483
|
+
return lowerHttpModuleCall(lowerer, expr, bi, loc);
|
|
419
484
|
if (bi.module === "https")
|
|
420
|
-
return lowerHttpsModuleCall(
|
|
485
|
+
return lowerHttpsModuleCall(lowerer, expr, bi, loc);
|
|
421
486
|
if (bi.module === "tls")
|
|
422
|
-
return lowerTlsModuleCall(
|
|
487
|
+
return lowerTlsModuleCall(lowerer, expr, bi, loc);
|
|
423
488
|
if (bi.module === "http2")
|
|
424
|
-
return lowerHttp2ModuleCall(
|
|
489
|
+
return lowerHttp2ModuleCall(lowerer, expr, bi, loc);
|
|
425
490
|
if (bi.module !== "net")
|
|
426
491
|
return null;
|
|
427
492
|
// Members with ordinary surfaces.ts table entries (the autoselect
|
|
428
493
|
// budget pair) ride the generic path — null hands the dispatch back.
|
|
429
|
-
if (builtinModuleFnOf(
|
|
494
|
+
if (builtinModuleFnOf(lowerer, "net", bi.member))
|
|
430
495
|
return null;
|
|
431
496
|
const args = expr.arguments;
|
|
432
497
|
if (bi.member === "createServer") {
|
|
@@ -440,9 +505,9 @@ export function lowerNetModuleCall(L, expr, bi, loc) {
|
|
|
440
505
|
// result — the checked-dynamic lane's concise arrows box it, and
|
|
441
506
|
// the throw means it never materializes.
|
|
442
507
|
if (isJsSourceFile(expr.getSourceFile())) {
|
|
443
|
-
const t =
|
|
508
|
+
const t = lowerer.mapTypeOf(lowerer.typeOf(args[0]));
|
|
444
509
|
if (t !== null && (t.kind === "string" || t.kind === "f64" || t.kind === "bool")) {
|
|
445
|
-
const raw =
|
|
510
|
+
const raw = lowerer.lowerExpr(args[0]);
|
|
446
511
|
return {
|
|
447
512
|
kind: "libCall",
|
|
448
513
|
fn: "error.argTypeThrow",
|
|
@@ -456,10 +521,10 @@ export function lowerNetModuleCall(L, expr, bi, loc) {
|
|
|
456
521
|
};
|
|
457
522
|
}
|
|
458
523
|
}
|
|
459
|
-
const { cb } = lowerCallbackArg(
|
|
524
|
+
const { cb } = lowerCallbackArg(lowerer, args[0], "connection handlers", 1, (p) => p.kind === "netSocket", "use (socket) or ()", [NETSOCKET_T]);
|
|
460
525
|
return { kind: "libCall", fn: "net.createServerCb", args: [cb], type: NETSERVER_T, loc };
|
|
461
526
|
}
|
|
462
|
-
|
|
527
|
+
lowerer.noLowering(`createServer with ${args.length} arguments`, expr, "the supported forms are createServer() and createServer(socket => ...)");
|
|
463
528
|
}
|
|
464
529
|
if (bi.member === "connect" || bi.member === "createConnection") {
|
|
465
530
|
// The options-object form — connect({ port, host?, autoSelectFamily?,
|
|
@@ -472,22 +537,22 @@ export function lowerNetModuleCall(L, expr, bi, loc) {
|
|
|
472
537
|
// spelling — without it Node would dial only the first address). The
|
|
473
538
|
// no-lookup form is connect(port, host) with the option spellings.
|
|
474
539
|
if (args.length >= 1 && ts.isObjectLiteralExpression(args[0])) {
|
|
475
|
-
return lowerNetConnectOptions(
|
|
540
|
+
return lowerNetConnectOptions(lowerer, expr, bi.member, loc);
|
|
476
541
|
}
|
|
477
542
|
// A RUNTIME option bag (a record binding or dyn value — the
|
|
478
543
|
// invalid-input probes build theirs with computed keys): the
|
|
479
544
|
// checked-dynamic walk validates Node-order and the compiler-rendered
|
|
480
545
|
// fence is the post-validation tail.
|
|
481
546
|
if (args.length === 1 && isJsSourceFile(expr.getSourceFile())) {
|
|
482
|
-
const t =
|
|
547
|
+
const t = lowerer.mapTypeOf(lowerer.typeOf(args[0]));
|
|
483
548
|
if (t !== null && (t.kind === "dyn" || t.kind === "record")) {
|
|
484
|
-
const raw =
|
|
485
|
-
if (raw.type.kind === "dyn" ||
|
|
549
|
+
const raw = lowerer.lowerExpr(args[0]);
|
|
550
|
+
if (raw.type.kind === "dyn" || lowerer.dynConvertible(raw.type)) {
|
|
486
551
|
const bag = raw.type.kind === "dyn" ? raw : { kind: "dynFrom", value: raw, type: DYN, loc };
|
|
487
552
|
return {
|
|
488
553
|
kind: "libCall",
|
|
489
554
|
fn: "net.connectOptsChk",
|
|
490
|
-
args: [bag, ladderFenceExpr(
|
|
555
|
+
args: [bag, ladderFenceExpr(lowerer, `${bi.member} with a runtime options record`, expr, "pass the options as an object literal — port, host, autoSelectFamily, autoSelectFamilyAttemptTimeout, and lookup are the supported options")],
|
|
491
556
|
type: NETSOCKET_T,
|
|
492
557
|
loc,
|
|
493
558
|
};
|
|
@@ -495,15 +560,15 @@ export function lowerNetModuleCall(L, expr, bi, loc) {
|
|
|
495
560
|
}
|
|
496
561
|
}
|
|
497
562
|
if (args.length < 1 || args.length > 3) {
|
|
498
|
-
|
|
563
|
+
lowerer.noLowering(`${bi.member} with ${args.length} arguments`, expr, `the supported form is ${bi.member}(port, host?, connectListener?)`);
|
|
499
564
|
}
|
|
500
|
-
const port =
|
|
565
|
+
const port = lowerer.lowerExprExpecting(args[0], F64);
|
|
501
566
|
// The optional middle host: a 2-arg call's second argument is the
|
|
502
567
|
// host when string-typed, the connect listener when func-typed.
|
|
503
568
|
let hostNode;
|
|
504
569
|
let cbNode;
|
|
505
570
|
if (args.length === 2) {
|
|
506
|
-
if (
|
|
571
|
+
if (lowerer.mapTypeOf(lowerer.typeOf(args[1]))?.kind === "string")
|
|
507
572
|
hostNode = args[1];
|
|
508
573
|
else
|
|
509
574
|
cbNode = args[1];
|
|
@@ -513,22 +578,22 @@ export function lowerNetModuleCall(L, expr, bi, loc) {
|
|
|
513
578
|
cbNode = args[2];
|
|
514
579
|
}
|
|
515
580
|
const host = hostNode
|
|
516
|
-
?
|
|
581
|
+
? lowerer.lowerExprExpecting(hostNode, STRING)
|
|
517
582
|
: { kind: "strLit", value: "localhost", type: STRING, loc };
|
|
518
583
|
if (!cbNode) {
|
|
519
584
|
return { kind: "libCall", fn: "net.connect", args: [port, host], type: NETSOCKET_T, loc };
|
|
520
585
|
}
|
|
521
|
-
const { cb } = lowerCallbackArg(
|
|
586
|
+
const { cb } = lowerCallbackArg(lowerer, cbNode, "connect listeners", 0, () => false, "use ()", []);
|
|
522
587
|
return { kind: "libCall", fn: "net.connectCb", args: [port, host, cb], type: NETSOCKET_T, loc };
|
|
523
588
|
}
|
|
524
|
-
|
|
589
|
+
lowerer.noLowering(`net.${bi.member}`, expr, "createServer, connect, and createConnection are the lowered net module functions", lowerer.resolveValueSymbol(expr.expression));
|
|
525
590
|
}
|
|
526
591
|
/** True when `t` is the lookup-function shape the connect lowering
|
|
527
592
|
* accepts: (hostname: string, options: unknown, callback) => void where
|
|
528
593
|
* callback is (err: <union with a null arm>, addresses: { address:
|
|
529
594
|
* string, ... }[]) => void — Node's net.LookupFunction as portless
|
|
530
595
|
* declares it. */
|
|
531
|
-
function lookupFnShapeOk(
|
|
596
|
+
function lookupFnShapeOk(lowerer, t) {
|
|
532
597
|
if (t.kind !== "func" || t.ret.kind !== "void" || t.params.length !== 3)
|
|
533
598
|
return false;
|
|
534
599
|
if (t.params[0].kind !== "string")
|
|
@@ -541,7 +606,7 @@ function lookupFnShapeOk(L, t) {
|
|
|
541
606
|
const errT = cbT.params[0];
|
|
542
607
|
if (errT.kind !== "union")
|
|
543
608
|
return false;
|
|
544
|
-
const errDef =
|
|
609
|
+
const errDef = lowerer.unions.get(errT.unionId);
|
|
545
610
|
if (!errDef || !errDef.arms.some((a) => a.kind === "nullT"))
|
|
546
611
|
return false;
|
|
547
612
|
// Every non-null arm must be the Error root: the emitted answer thunk
|
|
@@ -552,7 +617,7 @@ function lookupFnShapeOk(L, t) {
|
|
|
552
617
|
const addrsT = cbT.params[1];
|
|
553
618
|
if (addrsT.kind !== "array" || addrsT.elem.kind !== "record")
|
|
554
619
|
return false;
|
|
555
|
-
const shape =
|
|
620
|
+
const shape = lowerer.shapes.get(addrsT.elem.shapeId);
|
|
556
621
|
return !!shape && shape.fields.some((f) => f.name === "address" && f.type.kind === "string");
|
|
557
622
|
}
|
|
558
623
|
/** net.connect / net.createConnection with an OPTIONS OBJECT: { port,
|
|
@@ -563,7 +628,7 @@ function lookupFnShapeOk(L, t) {
|
|
|
563
628
|
* alongside, portless's createLoopbackConnection). Other keys fence by
|
|
564
629
|
* name; a connect listener argument is supported on the no-lookup form
|
|
565
630
|
* only. */
|
|
566
|
-
function lowerNetConnectOptions(
|
|
631
|
+
function lowerNetConnectOptions(lowerer, expr, member, loc) {
|
|
567
632
|
const args = expr.arguments;
|
|
568
633
|
const optsNode = args[0];
|
|
569
634
|
const isJs = isJsSourceFile(expr.getSourceFile());
|
|
@@ -579,13 +644,13 @@ function lowerNetConnectOptions(L, expr, member, loc) {
|
|
|
579
644
|
["objectMode", "readableObjectMode", "writableObjectMode"].includes((ts.isPropertyAssignment(p) || ts.isShorthandPropertyAssignment(p)) &&
|
|
580
645
|
(ts.isIdentifier(p.name) || ts.isStringLiteral(p.name)) ? p.name.text : ""));
|
|
581
646
|
if (needsBag) {
|
|
582
|
-
const raw =
|
|
583
|
-
if (raw.type.kind === "dyn" ||
|
|
647
|
+
const raw = lowerer.lowerExpr(optsNode);
|
|
648
|
+
if (raw.type.kind === "dyn" || lowerer.dynConvertible(raw.type)) {
|
|
584
649
|
const bag = raw.type.kind === "dyn" ? raw : { kind: "dynFrom", value: raw, type: DYN, loc };
|
|
585
650
|
return {
|
|
586
651
|
kind: "libCall",
|
|
587
652
|
fn: "net.connectOptsChk",
|
|
588
|
-
args: [bag, ladderFenceExpr(
|
|
653
|
+
args: [bag, ladderFenceExpr(lowerer, `${member} with these options`, optsNode, "port, host, autoSelectFamily, autoSelectFamilyAttemptTimeout, and lookup are the supported options")],
|
|
589
654
|
type: NETSOCKET_T,
|
|
590
655
|
loc,
|
|
591
656
|
};
|
|
@@ -619,16 +684,16 @@ function lowerNetConnectOptions(L, expr, member, loc) {
|
|
|
619
684
|
initializer = null;
|
|
620
685
|
}
|
|
621
686
|
else {
|
|
622
|
-
|
|
687
|
+
lowerer.noLowering(`${member} options with computed keys or spreads`, prop, "each option must be a plain `name: value` (or shorthand) entry with a literal key");
|
|
623
688
|
}
|
|
624
689
|
const key = prop.name.text;
|
|
625
690
|
const lowerVal = () => initializer !== null
|
|
626
|
-
?
|
|
627
|
-
:
|
|
691
|
+
? lowerer.lowerExpr(initializer)
|
|
692
|
+
: lowerer.lowerShorthandValue(prop);
|
|
628
693
|
if (key === "port") {
|
|
629
694
|
port = lowerVal();
|
|
630
695
|
if (port.type.kind !== "f64") {
|
|
631
|
-
|
|
696
|
+
lowerer.noLowering(`a ${member} 'port' option of '${lowerer.fmt(port.type)}' values`, prop, "the port is a number here");
|
|
632
697
|
}
|
|
633
698
|
}
|
|
634
699
|
else if (key === "host") {
|
|
@@ -636,12 +701,12 @@ function lowerNetConnectOptions(L, expr, member, loc) {
|
|
|
636
701
|
if (host.type.kind !== "string") {
|
|
637
702
|
// A provably-non-string host (the invalid-input probes): Node's
|
|
638
703
|
// lookupAndConnect throws ERR_INVALID_ARG_TYPE at connect time.
|
|
639
|
-
if (isJs && (host.type.kind === "dyn" ||
|
|
704
|
+
if (isJs && (host.type.kind === "dyn" || lowerer.dynConvertible(host.type))) {
|
|
640
705
|
optionThrow ??= propThrow("options.host", "of type string", host);
|
|
641
706
|
host = null;
|
|
642
707
|
continue;
|
|
643
708
|
}
|
|
644
|
-
|
|
709
|
+
lowerer.noLowering(`a ${member} 'host' option of '${lowerer.fmt(host.type)}' values`, prop, "the host is a string here");
|
|
645
710
|
}
|
|
646
711
|
}
|
|
647
712
|
else if (key === "autoSelectFamily") {
|
|
@@ -651,13 +716,13 @@ function lowerNetConnectOptions(L, expr, member, loc) {
|
|
|
651
716
|
if (initializer === null || initializer.kind !== ts.SyntaxKind.TrueKeyword) {
|
|
652
717
|
// A provably-non-boolean value throws Node's validateBoolean
|
|
653
718
|
// ladder instead of fencing.
|
|
654
|
-
const raw = initializer !== null && isJs ?
|
|
719
|
+
const raw = initializer !== null && isJs ? lowerer.lowerExpr(initializer) : null;
|
|
655
720
|
if (raw !== null && raw.type.kind !== "bool" &&
|
|
656
|
-
(raw.type.kind === "dyn" ||
|
|
721
|
+
(raw.type.kind === "dyn" || lowerer.dynConvertible(raw.type))) {
|
|
657
722
|
optionThrow ??= propThrow("options.autoSelectFamily", "of type boolean", raw);
|
|
658
723
|
continue;
|
|
659
724
|
}
|
|
660
|
-
|
|
725
|
+
lowerer.noLowering(`${member} with a non-literal autoSelectFamily option`, prop, "the lowered form is autoSelectFamily: true beside a lookup function");
|
|
661
726
|
}
|
|
662
727
|
autoSelect = true;
|
|
663
728
|
}
|
|
@@ -666,22 +731,22 @@ function lowerNetConnectOptions(L, expr, member, loc) {
|
|
|
666
731
|
// from-1 ladder) and is then inert — the single dial has nothing
|
|
667
732
|
// to time, the autoSelectFamily simplification's sibling.
|
|
668
733
|
const raw = lowerVal();
|
|
669
|
-
if (!(raw.type.kind === "dyn" || raw.kind === "unitLit" ||
|
|
670
|
-
|
|
734
|
+
if (!(raw.type.kind === "dyn" || raw.kind === "unitLit" || lowerer.dynConvertible(raw.type))) {
|
|
735
|
+
lowerer.noLowering(`a ${member} 'autoSelectFamilyAttemptTimeout' option of '${lowerer.fmt(raw.type)}' values`, prop, "the budget is a number here");
|
|
671
736
|
}
|
|
672
737
|
attempt = raw.type.kind === "dyn" ? raw : { kind: "dynFrom", value: raw, type: DYN, loc };
|
|
673
738
|
}
|
|
674
739
|
else if (key === "lookup") {
|
|
675
740
|
if (initializer === null) {
|
|
676
|
-
|
|
741
|
+
lowerer.noLowering(`${member} with a shorthand lookup option`, prop, "spell it out: lookup: theResolver");
|
|
677
742
|
}
|
|
678
|
-
lookup =
|
|
679
|
-
if (!lookupFnShapeOk(
|
|
680
|
-
|
|
743
|
+
lookup = lowerer.lowerExpr(initializer);
|
|
744
|
+
if (!lookupFnShapeOk(lowerer, lookup.type)) {
|
|
745
|
+
lowerer.noLowering(`a ${member} 'lookup' option of '${lowerer.fmt(lookup.type)}' values`, prop, "the lowered resolver is (hostname: string, options: unknown, callback: (err: NodeJS.ErrnoException | null, addresses: { address: string; family: number }[]) => void) => void");
|
|
681
746
|
}
|
|
682
747
|
}
|
|
683
748
|
else {
|
|
684
|
-
|
|
749
|
+
lowerer.noLowering(`${member} option '${key}'`, prop, "port, host, autoSelectFamily, and lookup are the supported options");
|
|
685
750
|
}
|
|
686
751
|
}
|
|
687
752
|
// A collected option-contract violation replaces the whole call: Node
|
|
@@ -690,21 +755,21 @@ function lowerNetConnectOptions(L, expr, member, loc) {
|
|
|
690
755
|
if (optionThrow !== null)
|
|
691
756
|
return optionThrow;
|
|
692
757
|
if (port === null) {
|
|
693
|
-
|
|
758
|
+
lowerer.noLowering(`${member} options without a port`, optsNode, "the supported options object is { port, host?, autoSelectFamily?, lookup? }");
|
|
694
759
|
}
|
|
695
760
|
host ??= { kind: "strLit", value: "localhost", type: STRING, loc };
|
|
696
761
|
if (attempt !== null) {
|
|
697
762
|
if (lookup !== null || args.length !== 1) {
|
|
698
|
-
|
|
763
|
+
lowerer.noLowering(`${member} with an autoSelectFamilyAttemptTimeout beside a lookup or connect listener`, expr, "the validated-budget form is the bare options call — register listeners separately");
|
|
699
764
|
}
|
|
700
765
|
return { kind: "libCall", fn: "net.connectAttempt", args: [port, host, attempt], type: NETSOCKET_T, loc };
|
|
701
766
|
}
|
|
702
767
|
if (lookup !== null) {
|
|
703
768
|
if (!autoSelect) {
|
|
704
|
-
|
|
769
|
+
lowerer.noLowering(`${member} with a lookup but no autoSelectFamily: true`, optsNode, "the lookup form dials every answered address in order — Node does that under autoSelectFamily: true, so spell it");
|
|
705
770
|
}
|
|
706
771
|
if (args.length !== 1) {
|
|
707
|
-
|
|
772
|
+
lowerer.noLowering(`${member} with a lookup and a connect listener`, expr, "register the listener separately: socket.once('connect', ...)");
|
|
708
773
|
}
|
|
709
774
|
return { kind: "libCall", fn: "net.connectLookup", args: [port, host, lookup], type: NETSOCKET_T, loc };
|
|
710
775
|
}
|
|
@@ -712,9 +777,9 @@ function lowerNetConnectOptions(L, expr, member, loc) {
|
|
|
712
777
|
return { kind: "libCall", fn: "net.connect", args: [port, host], type: NETSOCKET_T, loc };
|
|
713
778
|
}
|
|
714
779
|
if (args.length !== 2) {
|
|
715
|
-
|
|
780
|
+
lowerer.noLowering(`${member} with ${args.length} arguments`, expr, `the supported form is ${member}(options[, connectListener])`);
|
|
716
781
|
}
|
|
717
|
-
const { cb } = lowerCallbackArg(
|
|
782
|
+
const { cb } = lowerCallbackArg(lowerer, args[1], "connect listeners", 0, () => false, "use ()", []);
|
|
718
783
|
return { kind: "libCall", fn: "net.connectCb", args: [port, host, cb], type: NETSOCKET_T, loc };
|
|
719
784
|
}
|
|
720
785
|
/** `wrapper.close.bind(wrapper)` — the bound REAL close as a VALUE (the
|
|
@@ -726,7 +791,7 @@ function lowerNetConnectOptions(L, expr, member, loc) {
|
|
|
726
791
|
* binding as the method receiver — any other `this` would be a
|
|
727
792
|
* different close. Null when the shape doesn't match (generic fences
|
|
728
793
|
* name `.bind` otherwise). */
|
|
729
|
-
function lowerServerCloseBind(
|
|
794
|
+
function lowerServerCloseBind(lowerer, call, access) {
|
|
730
795
|
if (call.questionDotToken || access.questionDotToken)
|
|
731
796
|
return null;
|
|
732
797
|
if (access.name.text !== "bind")
|
|
@@ -734,9 +799,9 @@ function lowerServerCloseBind(L, call, access) {
|
|
|
734
799
|
const closeAccess = access.expression;
|
|
735
800
|
if (!ts.isPropertyAccessExpression(closeAccess) || closeAccess.name.text !== "close")
|
|
736
801
|
return null;
|
|
737
|
-
if (
|
|
802
|
+
if (lowerer.mapTypeOf(lowerer.typeOf(closeAccess.expression))?.kind !== "netServer")
|
|
738
803
|
return null;
|
|
739
|
-
if (!
|
|
804
|
+
if (!lowerer.isStdlibMember(closeAccess))
|
|
740
805
|
return null;
|
|
741
806
|
const loc = locOf(call);
|
|
742
807
|
const recvNode = closeAccess.expression;
|
|
@@ -745,15 +810,15 @@ function lowerServerCloseBind(L, call, access) {
|
|
|
745
810
|
!thisArg ||
|
|
746
811
|
!ts.isIdentifier(recvNode) ||
|
|
747
812
|
!ts.isIdentifier(thisArg) ||
|
|
748
|
-
|
|
749
|
-
|
|
813
|
+
lowerer.resolveValueSymbol(recvNode) !== lowerer.resolveValueSymbol(thisArg)) {
|
|
814
|
+
lowerer.noLowering("close.bind with this argument shape", call, "the supported form is server.close.bind(server) — the same server binding on both sides");
|
|
750
815
|
}
|
|
751
|
-
const server =
|
|
752
|
-
const t =
|
|
816
|
+
const server = lowerer.lowerExpr(thisArg);
|
|
817
|
+
const t = lowerer.mapTypeOf(lowerer.typeOf(call));
|
|
753
818
|
const cbUnion = t?.kind === "func" && t.params.length === 1 ? t.params[0] : null;
|
|
754
819
|
const cbOk = cbUnion?.kind === "union" &&
|
|
755
820
|
(() => {
|
|
756
|
-
const def =
|
|
821
|
+
const def = lowerer.unions.get(cbUnion.unionId);
|
|
757
822
|
return (!!def &&
|
|
758
823
|
def.arms.some((a) => a.kind === "func" && a.params.length <= 1 && a.ret.kind === "void") &&
|
|
759
824
|
def.arms.some((a) => a.kind === "undefinedT"));
|
|
@@ -762,7 +827,7 @@ function lowerServerCloseBind(L, call, access) {
|
|
|
762
827
|
// => void) => Server; the shipped fallback (callback?: () => void) =>
|
|
763
828
|
// void — the emitted adapter follows the mapped shape either way.
|
|
764
829
|
if (!t || t.kind !== "func" || !cbOk || (t.ret.kind !== "netServer" && t.ret.kind !== "void")) {
|
|
765
|
-
|
|
830
|
+
lowerer.noLowering("close.bind with this signature", call, "the bound close is (callback?: (err?: Error) => void) => Server");
|
|
766
831
|
}
|
|
767
832
|
return { kind: "libCall", fn: "net.serverCloseBind", args: [server], type: t, loc };
|
|
768
833
|
}
|
|
@@ -772,27 +837,27 @@ function lowerServerCloseBind(L, call, access) {
|
|
|
772
837
|
* emitted zero-arg wrapper; server.close() runs it instead of closing,
|
|
773
838
|
* and the override reaches the real close through its bound origClose.
|
|
774
839
|
* Null when the target isn't a net.Server close member. */
|
|
775
|
-
export function lowerServerCloseOverrideAssignment(
|
|
840
|
+
export function lowerServerCloseOverrideAssignment(lowerer, left, right, loc) {
|
|
776
841
|
if (!ts.isPropertyAccessExpression(left) || left.questionDotToken)
|
|
777
842
|
return null;
|
|
778
843
|
if (left.name.text !== "close")
|
|
779
844
|
return null;
|
|
780
|
-
if (
|
|
845
|
+
if (lowerer.mapTypeOf(lowerer.typeOf(left.expression))?.kind !== "netServer")
|
|
781
846
|
return null;
|
|
782
|
-
const server =
|
|
783
|
-
const value =
|
|
847
|
+
const server = lowerer.lowerExpr(left.expression);
|
|
848
|
+
const value = lowerer.lowerExpr(right);
|
|
784
849
|
const t = value.type;
|
|
785
850
|
const cbUnion = t.kind === "func" && t.params.length === 1 ? t.params[0] : null;
|
|
786
851
|
const shapeOk = cbUnion?.kind === "union" &&
|
|
787
852
|
(t.kind === "func" && (t.ret.kind === "netServer" || t.ret.kind === "void")) &&
|
|
788
853
|
(() => {
|
|
789
|
-
const def =
|
|
854
|
+
const def = lowerer.unions.get(cbUnion.unionId);
|
|
790
855
|
return (!!def &&
|
|
791
856
|
def.arms.some((a) => a.kind === "func" && a.params.length <= 1 && a.ret.kind === "void") &&
|
|
792
857
|
def.arms.some((a) => a.kind === "undefinedT"));
|
|
793
858
|
})();
|
|
794
859
|
if (!shapeOk) {
|
|
795
|
-
|
|
860
|
+
lowerer.noLowering("assigning this value to server.close", right, "the override must be a (callback?: (err?: Error) => void) => Server function");
|
|
796
861
|
}
|
|
797
862
|
return {
|
|
798
863
|
kind: "exprStmt",
|
|
@@ -804,20 +869,20 @@ export function lowerServerCloseOverrideAssignment(L, left, right, loc) {
|
|
|
804
869
|
* writable ServerResponse properties (the implicit head reads them):
|
|
805
870
|
* routed from lower-stmts' property-assignment path beside the
|
|
806
871
|
* close-override hook. Null when the target isn't one of the two. */
|
|
807
|
-
export function lowerHttpResPropertyAssignment(
|
|
872
|
+
export function lowerHttpResPropertyAssignment(lowerer, left, right, loc) {
|
|
808
873
|
if (!ts.isPropertyAccessExpression(left) || left.questionDotToken)
|
|
809
874
|
return null;
|
|
810
875
|
const name = left.name.text;
|
|
811
876
|
if (name !== "statusCode" && name !== "statusMessage")
|
|
812
877
|
return null;
|
|
813
|
-
if (
|
|
878
|
+
if (lowerer.mapTypeOf(lowerer.typeOf(left.expression))?.kind !== "httpRes")
|
|
814
879
|
return null;
|
|
815
|
-
if (!
|
|
880
|
+
if (!lowerer.isStdlibMember(left))
|
|
816
881
|
return null;
|
|
817
|
-
const receiver =
|
|
882
|
+
const receiver = lowerer.lowerExpr(left.expression);
|
|
818
883
|
const value = name === "statusCode"
|
|
819
|
-
?
|
|
820
|
-
:
|
|
884
|
+
? lowerer.lowerExprExpecting(right, F64)
|
|
885
|
+
: lowerer.lowerExprExpecting(right, STRING);
|
|
821
886
|
const fn = name === "statusCode" ? "http.resStatusSet" : "http.resStatusMsgSet";
|
|
822
887
|
return {
|
|
823
888
|
kind: "exprStmt",
|
|
@@ -829,19 +894,19 @@ export function lowerHttpResPropertyAssignment(L, left, right, loc) {
|
|
|
829
894
|
* siblings. These are ordinary writable number properties in Node: the
|
|
830
895
|
* runtime stores their exact value per server; active timeout behavior is
|
|
831
896
|
* a separate protocol concern. */
|
|
832
|
-
export function lowerHttpServerTimeoutAssignment(
|
|
897
|
+
export function lowerHttpServerTimeoutAssignment(lowerer, left, right, loc) {
|
|
833
898
|
if (!ts.isPropertyAccessExpression(left) || left.questionDotToken)
|
|
834
899
|
return null;
|
|
835
900
|
const field = httpServerTimeoutField(left.name.text);
|
|
836
901
|
if (field === null)
|
|
837
902
|
return null;
|
|
838
|
-
if (
|
|
903
|
+
if (lowerer.mapTypeOf(lowerer.typeOf(left.expression))?.kind !== "netServer")
|
|
839
904
|
return null;
|
|
840
|
-
if (!
|
|
905
|
+
if (!lowerer.isStdlibMember(left))
|
|
841
906
|
return null;
|
|
842
|
-
const receiver =
|
|
907
|
+
const receiver = coerceToHandle(lowerer, left.expression, NETSERVER_T);
|
|
843
908
|
const selector = { kind: "numLit", value: field, type: F64, loc };
|
|
844
|
-
const value =
|
|
909
|
+
const value = lowerer.lowerExprExpecting(right, F64);
|
|
845
910
|
return {
|
|
846
911
|
kind: "exprStmt",
|
|
847
912
|
expr: { kind: "libCall", fn: "http.serverTimeoutSet", args: [receiver, selector, value], type: VOID, loc },
|
|
@@ -851,17 +916,17 @@ export function lowerHttpServerTimeoutAssignment(L, left, right, loc) {
|
|
|
851
916
|
/** Method calls on net.Server receivers: listen(port[, cb]), close([cb]),
|
|
852
917
|
* on/once("connection" | "error" | "close", cb). address() alone fences
|
|
853
918
|
* toward the composed address().port read. Null for other receivers. */
|
|
854
|
-
function lowerNetServerMethodCall(
|
|
855
|
-
if (
|
|
919
|
+
function lowerNetServerMethodCall(lowerer, call, access) {
|
|
920
|
+
if (lowerer.mapTypeOf(lowerer.typeOf(access.expression))?.kind !== "netServer")
|
|
856
921
|
return null;
|
|
857
|
-
if (!
|
|
922
|
+
if (!lowerer.isStdlibMember(access))
|
|
858
923
|
return null;
|
|
859
924
|
const name = access.name.text;
|
|
860
925
|
const loc = locOf(call);
|
|
861
926
|
const args = call.arguments;
|
|
862
927
|
if (name === "listen") {
|
|
863
928
|
if (args.length < 1 || args.length > 3) {
|
|
864
|
-
|
|
929
|
+
lowerer.noLowering(`listen with ${args.length} arguments`, call, "the supported form is listen(port[, host][, callback]) — port 0 binds an ephemeral port");
|
|
865
930
|
}
|
|
866
931
|
// Node answers the server (`return this` chaining — `const s =
|
|
867
932
|
// http.createServer(...).listen(0)`, `server.listen(0).address()`):
|
|
@@ -871,19 +936,23 @@ function lowerNetServerMethodCall(L, call, access) {
|
|
|
871
936
|
if (ts.isExpressionStatement(call.parent)) {
|
|
872
937
|
return { kind: "libCall", fn, args: callArgs, type: VOID, loc };
|
|
873
938
|
}
|
|
874
|
-
return receiverReturningCall(
|
|
939
|
+
return receiverReturningCall(lowerer, fn, callArgs, NETSERVER_T, loc);
|
|
875
940
|
};
|
|
876
|
-
const receiver =
|
|
877
|
-
// The options-object form — listen({ port, host?, ipv6Only? }[, cb]),
|
|
941
|
+
const receiver = coerceToHandle(lowerer, access.expression, NETSERVER_T);
|
|
942
|
+
// The options-object form — listen({ port, host?, ipv6Only?, reusePort? }[, cb]),
|
|
878
943
|
// the portless listenOnProxyInterface shape: host binds that ONE
|
|
879
944
|
// address (an IP literal — the runtime has no resolver here; absent
|
|
880
945
|
// = Node's host-less dual-stack any), ipv6Only sets IPV6_V6ONLY
|
|
881
946
|
// (truthiness, like Node's option handling — `boolean | undefined`
|
|
882
|
-
// flows).
|
|
947
|
+
// flows). reusePort is different: Node enables it only for exact true,
|
|
948
|
+
// while its presence selects the additive ABI even when the value is
|
|
949
|
+
// false.
|
|
950
|
+
// Every other key fences by name.
|
|
883
951
|
if (ts.isObjectLiteralExpression(args[0])) {
|
|
884
952
|
let port = null;
|
|
885
953
|
let host = null;
|
|
886
954
|
let v6only = null;
|
|
955
|
+
let reusePort = null;
|
|
887
956
|
for (const prop of args[0].properties) {
|
|
888
957
|
let initializer;
|
|
889
958
|
if (ts.isPropertyAssignment(prop) &&
|
|
@@ -894,47 +963,59 @@ function lowerNetServerMethodCall(L, call, access) {
|
|
|
894
963
|
initializer = null;
|
|
895
964
|
}
|
|
896
965
|
else {
|
|
897
|
-
|
|
966
|
+
lowerer.noLowering("listen options with computed keys or spreads", prop, "each option must be a plain `name: value` (or shorthand) entry with a literal key");
|
|
898
967
|
}
|
|
899
968
|
const key = prop.name.text;
|
|
900
969
|
if (key === "port") {
|
|
901
970
|
port = initializer !== null
|
|
902
|
-
?
|
|
903
|
-
:
|
|
971
|
+
? lowerer.lowerExpr(initializer)
|
|
972
|
+
: lowerer.lowerShorthandValue(prop);
|
|
904
973
|
if (port.type.kind !== "f64") {
|
|
905
|
-
|
|
974
|
+
lowerer.noLowering(`a listen 'port' option of '${lowerer.fmt(port.type)}' values`, prop, "the port is a number here");
|
|
906
975
|
}
|
|
907
976
|
}
|
|
908
977
|
else if (key === "host") {
|
|
909
978
|
host = initializer !== null
|
|
910
|
-
?
|
|
911
|
-
:
|
|
979
|
+
? lowerer.lowerExpr(initializer)
|
|
980
|
+
: lowerer.lowerShorthandValue(prop);
|
|
912
981
|
if (host.type.kind !== "string") {
|
|
913
|
-
|
|
982
|
+
lowerer.noLowering(`a listen 'host' option of '${lowerer.fmt(host.type)}' values`, prop, "the host is an IP string here");
|
|
914
983
|
}
|
|
915
984
|
}
|
|
916
985
|
else if (key === "ipv6Only") {
|
|
917
986
|
if (initializer !== null) {
|
|
918
|
-
v6only =
|
|
987
|
+
v6only = lowerer.lowerCondition(initializer); /* truthiness: `boolean | undefined` flows */
|
|
919
988
|
}
|
|
920
989
|
else {
|
|
921
|
-
const v =
|
|
990
|
+
const v = lowerer.lowerShorthandValue(prop);
|
|
922
991
|
if (v.type.kind !== "bool") {
|
|
923
|
-
|
|
992
|
+
lowerer.noLowering(`a listen 'ipv6Only' option of '${lowerer.fmt(v.type)}' values`, prop, "spell the option out (ipv6Only: value) so non-boolean values can narrow");
|
|
924
993
|
}
|
|
925
994
|
v6only = v;
|
|
926
995
|
}
|
|
927
996
|
}
|
|
997
|
+
else if (key === "reusePort") {
|
|
998
|
+
if (initializer !== null) {
|
|
999
|
+
reusePort = lowerExactReusePort(lowerer, lowerer.lowerExpr(initializer), prop);
|
|
1000
|
+
}
|
|
1001
|
+
else {
|
|
1002
|
+
const v = lowerer.lowerShorthandValue(prop);
|
|
1003
|
+
if (v.type.kind !== "bool") {
|
|
1004
|
+
lowerer.noLowering(`a listen 'reusePort' option of '${lowerer.fmt(v.type)}' values`, prop, "spell the option out (reusePort: value) so optional values can narrow");
|
|
1005
|
+
}
|
|
1006
|
+
reusePort = v;
|
|
1007
|
+
}
|
|
1008
|
+
}
|
|
928
1009
|
else if (key === "signal" && ts.isPropertyAssignment(prop) &&
|
|
929
1010
|
isJsSourceFile(call.getSourceFile())) {
|
|
930
1011
|
// A provably-non-AbortSignal signal (the invalid-input probes:
|
|
931
1012
|
// strings, numbers, plain records) throws Node's
|
|
932
1013
|
// validateAbortSignal ladder; plausible signal values keep the
|
|
933
1014
|
// fence — abort-driven close has no lowering yet.
|
|
934
|
-
const raw =
|
|
1015
|
+
const raw = lowerer.lowerExpr(prop.initializer);
|
|
935
1016
|
const provablyNot = raw.type.kind === "string" || raw.type.kind === "f64" ||
|
|
936
1017
|
raw.type.kind === "bool" || raw.type.kind === "record" || raw.type.kind === "array";
|
|
937
|
-
if (provablyNot &&
|
|
1018
|
+
if (provablyNot && lowerer.dynConvertible(raw.type)) {
|
|
938
1019
|
return {
|
|
939
1020
|
kind: "libCall",
|
|
940
1021
|
fn: "error.propTypeThrow",
|
|
@@ -947,19 +1028,23 @@ function lowerNetServerMethodCall(L, call, access) {
|
|
|
947
1028
|
loc,
|
|
948
1029
|
};
|
|
949
1030
|
}
|
|
950
|
-
|
|
1031
|
+
lowerer.noLowering(`listen option 'signal'`, prop, "abort-driven close has no lowering yet — port, host, ipv6Only, and reusePort are the supported listen options");
|
|
951
1032
|
}
|
|
952
1033
|
else {
|
|
953
|
-
|
|
1034
|
+
lowerer.noLowering(`listen option '${key}'`, prop, "port, host, ipv6Only, and reusePort are the supported listen options");
|
|
954
1035
|
}
|
|
955
1036
|
}
|
|
956
1037
|
if (port === null) {
|
|
957
|
-
|
|
1038
|
+
lowerer.noLowering("listen options without a port", args[0], "the supported options object is { port, host?, ipv6Only?, reusePort? } — port 0 binds an ephemeral port");
|
|
958
1039
|
}
|
|
959
1040
|
host ??= { kind: "strLit", value: "", type: STRING, loc }; /* "" = the dual-stack any default */
|
|
960
1041
|
v6only ??= boolLit(false, loc);
|
|
1042
|
+
const listenOptsFn = reusePort === null ? "net.listenOpts" : "net.listenOptsReusePort";
|
|
1043
|
+
const listenOptsCbFn = reusePort === null ? "net.listenOptsCb" : "net.listenOptsReusePortCb";
|
|
961
1044
|
if (args.length === 1) {
|
|
962
|
-
return listenResult(
|
|
1045
|
+
return listenResult(listenOptsFn, reusePort === null
|
|
1046
|
+
? [receiver, port, host, v6only]
|
|
1047
|
+
: [receiver, port, host, v6only, reusePort]);
|
|
963
1048
|
}
|
|
964
1049
|
// The callback may be an OPTIONAL binding — `(() => void) |
|
|
965
1050
|
// undefined`, portless's listenOnProxyInterface pass-through: the
|
|
@@ -967,7 +1052,7 @@ function lowerNetServerMethodCall(L, call, access) {
|
|
|
967
1052
|
// pattern) and the runtime takes NULL for the undefined arm. A
|
|
968
1053
|
// checked-dynamic callback (a JS wrapper value) adapts through the
|
|
969
1054
|
// dynCheck function boundary.
|
|
970
|
-
let cbV =
|
|
1055
|
+
let cbV = lowerer.lowerExpr(args[1]);
|
|
971
1056
|
if (cbV.type.kind === "dyn") {
|
|
972
1057
|
cbV = { kind: "dynCheck", value: cbV, type: funcOf([], VOID), loc };
|
|
973
1058
|
}
|
|
@@ -975,25 +1060,27 @@ function lowerNetServerMethodCall(L, call, access) {
|
|
|
975
1060
|
const cbOk = cbFuncOk(cbV.type) ||
|
|
976
1061
|
(cbV.type.kind === "union" &&
|
|
977
1062
|
(() => {
|
|
978
|
-
const def =
|
|
1063
|
+
const def = lowerer.unions.get(cbV.type.unionId);
|
|
979
1064
|
return (!!def &&
|
|
980
1065
|
def.arms.length === 2 &&
|
|
981
1066
|
def.arms.some(cbFuncOk) &&
|
|
982
1067
|
def.arms.some((a) => a.kind === "undefinedT"));
|
|
983
1068
|
})());
|
|
984
1069
|
if (!cbOk) {
|
|
985
|
-
|
|
1070
|
+
lowerer.unsupported("SC1090", args[1], `listen callbacks of type '${lowerer.fmt(cbV.type)}' (use () — an optional \`(() => void) | undefined\` binding also flows)`);
|
|
986
1071
|
}
|
|
987
|
-
return listenResult(
|
|
1072
|
+
return listenResult(listenOptsCbFn, reusePort === null
|
|
1073
|
+
? [receiver, port, host, v6only, cbV]
|
|
1074
|
+
: [receiver, port, host, v6only, reusePort, cbV]);
|
|
988
1075
|
}
|
|
989
|
-
const port =
|
|
1076
|
+
const port = lowerer.lowerExprExpecting(args[0], F64);
|
|
990
1077
|
// The optional middle host — listen(port, '127.0.0.1'[, cb]): a
|
|
991
1078
|
// string-typed second argument is the bind address (the net.connect
|
|
992
1079
|
// disambiguation), routed through the options-form entry.
|
|
993
1080
|
let hostNode;
|
|
994
1081
|
let cbNode;
|
|
995
1082
|
if (args.length === 2) {
|
|
996
|
-
if (
|
|
1083
|
+
if (lowerer.mapTypeOf(lowerer.typeOf(args[1]))?.kind === "string")
|
|
997
1084
|
hostNode = args[1];
|
|
998
1085
|
else
|
|
999
1086
|
cbNode = args[1];
|
|
@@ -1003,37 +1090,37 @@ function lowerNetServerMethodCall(L, call, access) {
|
|
|
1003
1090
|
cbNode = args[2];
|
|
1004
1091
|
}
|
|
1005
1092
|
if (hostNode !== undefined) {
|
|
1006
|
-
const host =
|
|
1093
|
+
const host = lowerer.lowerExprExpecting(hostNode, STRING);
|
|
1007
1094
|
const v6only = boolLit(false, loc);
|
|
1008
1095
|
if (!cbNode)
|
|
1009
1096
|
return listenResult("net.listenOpts", [receiver, port, host, v6only]);
|
|
1010
|
-
const { cb } = lowerCallbackArg(
|
|
1097
|
+
const { cb } = lowerCallbackArg(lowerer, cbNode, "listen callbacks", 0, () => false, "use ()", []);
|
|
1011
1098
|
return listenResult("net.listenOptsCb", [receiver, port, host, v6only, cb]);
|
|
1012
1099
|
}
|
|
1013
1100
|
if (!cbNode) {
|
|
1014
1101
|
return listenResult("net.listen", [receiver, port]);
|
|
1015
1102
|
}
|
|
1016
|
-
const { cb } = lowerCallbackArg(
|
|
1103
|
+
const { cb } = lowerCallbackArg(lowerer, cbNode, "listen callbacks", 0, () => false, "use ()", []);
|
|
1017
1104
|
return listenResult("net.listenCb", [receiver, port, cb]);
|
|
1018
1105
|
}
|
|
1019
1106
|
if (name === "close") {
|
|
1020
|
-
requireStatementPosition(
|
|
1107
|
+
requireStatementPosition(lowerer, call, "server.close(...)");
|
|
1021
1108
|
if (args.length > 1) {
|
|
1022
|
-
|
|
1109
|
+
lowerer.noLowering(`close with ${args.length} arguments`, call, "the supported form is close([callback])");
|
|
1023
1110
|
}
|
|
1024
|
-
const receiver =
|
|
1111
|
+
const receiver = coerceToHandle(lowerer, access.expression, NETSERVER_T);
|
|
1025
1112
|
if (args.length === 0) {
|
|
1026
1113
|
return { kind: "libCall", fn: "net.serverClose", args: [receiver], type: VOID, loc };
|
|
1027
1114
|
}
|
|
1028
|
-
const { cb } = lowerCallbackArg(
|
|
1115
|
+
const { cb } = lowerCallbackArg(lowerer, args[0], "close callbacks", 0, () => false, "use ()", []);
|
|
1029
1116
|
return { kind: "libCall", fn: "net.serverCloseCb", args: [receiver, cb], type: VOID, loc };
|
|
1030
1117
|
}
|
|
1031
1118
|
if ((name === "on" || name === "once" || name === "addListener") && args.length === 2) {
|
|
1032
|
-
requireStatementPosition(
|
|
1119
|
+
requireStatementPosition(lowerer, call, `server.${name}(...)`);
|
|
1033
1120
|
const once = boolLit(name === "once", loc);
|
|
1034
|
-
const evT =
|
|
1121
|
+
const evT = lowerer.typeOf(args[0]);
|
|
1035
1122
|
const event = evT.isStringLiteralType() ? evT.value : null;
|
|
1036
|
-
const receiver =
|
|
1123
|
+
const receiver = coerceToHandle(lowerer, access.expression, NETSERVER_T);
|
|
1037
1124
|
if (event === "connection" || event === "secureConnection") {
|
|
1038
1125
|
// 'secureConnection' rides the same list: a TLS server's
|
|
1039
1126
|
// 'connection' listeners are DEFERRED to handshake completion
|
|
@@ -1041,23 +1128,23 @@ function lowerNetServerMethodCall(L, call, access) {
|
|
|
1041
1128
|
// secureConnection timing; on a plain net server the name would
|
|
1042
1129
|
// never fire in Node either — the runtime entry gates on the
|
|
1043
1130
|
// deferral flag so the split stays exact.
|
|
1044
|
-
const { cb } = lowerCallbackArg(
|
|
1131
|
+
const { cb } = lowerCallbackArg(lowerer, args[1], `${event} listeners`, 1, (p) => p.kind === "netSocket", "use (socket) or ()", [NETSOCKET_T]);
|
|
1045
1132
|
const fn = event === "connection" ? "net.serverOnConnection" : "net.serverOnSecureConnection";
|
|
1046
1133
|
return { kind: "libCall", fn, args: [receiver, cb, once], type: VOID, loc };
|
|
1047
1134
|
}
|
|
1048
1135
|
if (event === "error") {
|
|
1049
|
-
const { cb } = lowerCallbackArg(
|
|
1136
|
+
const { cb } = lowerCallbackArg(lowerer, args[1], "error listeners", 1, (p) => p.kind === "object" && p.className === "%Error", "use (err) or ()", [ERROR_T]);
|
|
1050
1137
|
return { kind: "libCall", fn: "net.serverOnError", args: [receiver, cb, once], type: VOID, loc };
|
|
1051
1138
|
}
|
|
1052
1139
|
if (event === "close") {
|
|
1053
|
-
const { cb } = lowerCallbackArg(
|
|
1140
|
+
const { cb } = lowerCallbackArg(lowerer, args[1], "close listeners", 0, () => false, "use ()", []);
|
|
1054
1141
|
return { kind: "libCall", fn: "net.serverOnClose", args: [receiver, cb, once], type: VOID, loc };
|
|
1055
1142
|
}
|
|
1056
1143
|
if (event === "listening") {
|
|
1057
1144
|
// The deferred bind emit — listen(port, cb)'s event twin: fires
|
|
1058
1145
|
// once after a successful listen; late registrations on an
|
|
1059
1146
|
// already-listening server never fire (Node's once-per-listen).
|
|
1060
|
-
const { cb } = lowerCallbackArg(
|
|
1147
|
+
const { cb } = lowerCallbackArg(lowerer, args[1], "listening listeners", 0, () => false, "use ()", []);
|
|
1061
1148
|
return { kind: "libCall", fn: "net.serverOnListening", args: [receiver, cb, once], type: VOID, loc };
|
|
1062
1149
|
}
|
|
1063
1150
|
if (event === "request") {
|
|
@@ -1066,20 +1153,20 @@ function lowerNetServerMethodCall(L, call, access) {
|
|
|
1066
1153
|
// handler. Same (req, res) shapes as the createServer callback; on a
|
|
1067
1154
|
// server with no HTTP parser (net.createServer) the registration is
|
|
1068
1155
|
// Node-honest dead weight: the event never fires there either.
|
|
1069
|
-
const cb = lowerRequestHandlerArg(
|
|
1156
|
+
const cb = lowerRequestHandlerArg(lowerer, args[1]);
|
|
1070
1157
|
return { kind: "libCall", fn: "http.serverOnRequest", args: [receiver, cb, once], type: VOID, loc };
|
|
1071
1158
|
}
|
|
1072
1159
|
if (event === "stream") {
|
|
1073
1160
|
// The h2c server's request event: (stream, headers, flags). The
|
|
1074
1161
|
// adapter builds the headers record from the pairs the runtime
|
|
1075
1162
|
// hands over. Dead weight on a non-h2 server (never fires).
|
|
1076
|
-
const cb = h2HeadersCallbackAdapter(
|
|
1163
|
+
const cb = h2HeadersCallbackAdapter(lowerer, args[1], "stream listeners", "http2Stream", false, loc);
|
|
1077
1164
|
return { kind: "libCall", fn: "http2.serverOnStream", args: [receiver, cb, once], type: VOID, loc };
|
|
1078
1165
|
}
|
|
1079
1166
|
if (event === "session") {
|
|
1080
1167
|
// The h2c server's per-connection session event: (session). The
|
|
1081
1168
|
// result is voidized; the handle passes by reference.
|
|
1082
|
-
const { cb } = lowerCallbackArg(
|
|
1169
|
+
const { cb } = lowerCallbackArg(lowerer, args[1], "session listeners", 1, (p) => p.kind === "http2Session", "use (session) or ()", [HTTP2SESSION_T]);
|
|
1083
1170
|
return { kind: "libCall", fn: "http2.serverOnSession", args: [receiver, cb, once], type: VOID, loc };
|
|
1084
1171
|
}
|
|
1085
1172
|
if (event === "connect") {
|
|
@@ -1090,12 +1177,12 @@ function lowerNetServerMethodCall(L, call, access) {
|
|
|
1090
1177
|
// CONNECT and an Http2ServerResponse for valid traditional/extended
|
|
1091
1178
|
// HTTP/2 CONNECT, so portless's union-typed listener selects the
|
|
1092
1179
|
// protocol arm with `instanceof net.Socket`.
|
|
1093
|
-
const cb =
|
|
1180
|
+
const cb = lowerer.lowerExpr(args[1]);
|
|
1094
1181
|
if (cb.type.kind !== "func" || cb.type.params.length > 3) {
|
|
1095
|
-
|
|
1182
|
+
lowerer.unsupported("SC1090", args[1], "connect listeners with more than three parameters (use (req, socket, head))");
|
|
1096
1183
|
}
|
|
1097
1184
|
if (cb.type.ret.kind !== "void") {
|
|
1098
|
-
|
|
1185
|
+
lowerer.unsupported("SC1090", args[1], "connect listeners returning a value (make the callback body a block)");
|
|
1099
1186
|
}
|
|
1100
1187
|
const [p0, p1, p2] = cb.type.params;
|
|
1101
1188
|
const sockParamOk = (t) => {
|
|
@@ -1103,13 +1190,13 @@ function lowerNetServerMethodCall(L, call, access) {
|
|
|
1103
1190
|
return true;
|
|
1104
1191
|
if (t.kind !== "union")
|
|
1105
1192
|
return false;
|
|
1106
|
-
const def =
|
|
1193
|
+
const def = lowerer.unions.get(t.unionId);
|
|
1107
1194
|
return !!def && def.arms.some((a) => a.kind === "netSocket");
|
|
1108
1195
|
};
|
|
1109
1196
|
if ((p0 !== undefined && p0.kind !== "httpReq") ||
|
|
1110
1197
|
(p1 !== undefined && !sockParamOk(p1)) ||
|
|
1111
1198
|
(p2 !== undefined && !(p2.kind === "bytes" && p2.elem === "u8"))) {
|
|
1112
|
-
|
|
1199
|
+
lowerer.unsupported("SC1090", args[1], "connect listeners whose parameters are not (req: IncomingMessage, socket: Socket — or a union carrying the Socket arm, the h2 compat shape, head: Buffer)");
|
|
1113
1200
|
}
|
|
1114
1201
|
return { kind: "libCall", fn: "http.serverOnConnect", args: [receiver, cb, once], type: VOID, loc };
|
|
1115
1202
|
}
|
|
@@ -1119,50 +1206,50 @@ function lowerNetServerMethodCall(L, call, access) {
|
|
|
1119
1206
|
// socket over raw with the bytes read past the head, and destroys
|
|
1120
1207
|
// the socket when no listener exists. Registration works on every
|
|
1121
1208
|
// server kind (a parserless net server never fires it, like Node).
|
|
1122
|
-
const cb =
|
|
1209
|
+
const cb = lowerer.lowerExpr(args[1]);
|
|
1123
1210
|
if (cb.type.kind !== "func" || cb.type.params.length > 3) {
|
|
1124
|
-
|
|
1211
|
+
lowerer.unsupported("SC1090", args[1], "upgrade listeners with more than three parameters (use (req, socket, head))");
|
|
1125
1212
|
}
|
|
1126
1213
|
if (cb.type.ret.kind !== "void") {
|
|
1127
|
-
|
|
1214
|
+
lowerer.unsupported("SC1090", args[1], "upgrade listeners returning a value (make the callback body a block)");
|
|
1128
1215
|
}
|
|
1129
1216
|
const [p0, p1, p2] = cb.type.params;
|
|
1130
1217
|
if ((p0 !== undefined && p0.kind !== "httpReq") ||
|
|
1131
1218
|
(p1 !== undefined && p1.kind !== "netSocket") ||
|
|
1132
1219
|
(p2 !== undefined && !(p2.kind === "bytes" && p2.elem === "u8"))) {
|
|
1133
|
-
|
|
1220
|
+
lowerer.unsupported("SC1090", args[1], "upgrade listeners whose parameters are not (req: IncomingMessage, socket: Socket, head: Buffer)");
|
|
1134
1221
|
}
|
|
1135
1222
|
return { kind: "libCall", fn: "http.serverOnUpgrade", args: [receiver, cb, once], type: VOID, loc };
|
|
1136
1223
|
}
|
|
1137
1224
|
if (event === "sessionError") {
|
|
1138
|
-
const cb =
|
|
1225
|
+
const cb = lowerer.lowerExpr(args[1]);
|
|
1139
1226
|
if (cb.type.kind !== "func" || cb.type.ret.kind !== "void" || cb.type.params.length > 2) {
|
|
1140
|
-
|
|
1227
|
+
lowerer.unsupported("SC1090", args[1], "sessionError listeners with more than two parameters or returning a value");
|
|
1141
1228
|
}
|
|
1142
1229
|
const [err, session] = cb.type.params;
|
|
1143
1230
|
if ((err !== undefined && !(err.kind === "object" && err.className === "%Error")) ||
|
|
1144
1231
|
(session !== undefined && session.kind !== "http2Session")) {
|
|
1145
|
-
|
|
1232
|
+
lowerer.unsupported("SC1090", args[1], "sessionError listeners whose parameters are not (error: Error, session: ServerHttp2Session)");
|
|
1146
1233
|
}
|
|
1147
1234
|
return { kind: "libCall", fn: "http2.serverOnSessionError", args: [receiver, cb, once], type: VOID, loc };
|
|
1148
1235
|
}
|
|
1149
|
-
|
|
1236
|
+
lowerer.noLowering(`server.${name}(${event === null ? "non-literal event" : `"${event}"`}, ...)`, args[0], '"connection", "request", "stream", "session", "upgrade", "connect", "error", "close", "listening", and "sessionError" are the supported server events (as literals)');
|
|
1150
1237
|
}
|
|
1151
1238
|
if (name === "emit") {
|
|
1152
1239
|
// The demux route: emit("connection", socket) hands an accepted
|
|
1153
1240
|
// socket to ANOTHER server (portless's first-byte TLS peek). Only
|
|
1154
1241
|
// the literal "connection" form lowers; Node's boolean result is
|
|
1155
1242
|
// dropped (statement position required).
|
|
1156
|
-
requireStatementPosition(
|
|
1157
|
-
const evT = args.length >= 1 ?
|
|
1243
|
+
requireStatementPosition(lowerer, call, "server.emit(...)");
|
|
1244
|
+
const evT = args.length >= 1 ? lowerer.typeOf(args[0]) : null;
|
|
1158
1245
|
const event = evT?.isStringLiteralType() ? evT.value : null;
|
|
1159
1246
|
if (event !== "connection" || args.length !== 2) {
|
|
1160
|
-
|
|
1247
|
+
lowerer.noLowering(`server.emit(${event === null ? "non-literal event" : `"${event}"`}, ...)`, call, 'emit("connection", socket) is the supported emit form (the demux route)');
|
|
1161
1248
|
}
|
|
1162
|
-
const receiver =
|
|
1163
|
-
const sock =
|
|
1249
|
+
const receiver = coerceToHandle(lowerer, access.expression, NETSERVER_T);
|
|
1250
|
+
const sock = lowerer.lowerExpr(args[1]);
|
|
1164
1251
|
if (sock.type.kind !== "netSocket") {
|
|
1165
|
-
|
|
1252
|
+
lowerer.noLowering(`emit("connection", …) with a '${lowerer.fmt(sock.type)}' argument`, args[1], "the second argument must be a net socket");
|
|
1166
1253
|
}
|
|
1167
1254
|
return { kind: "libCall", fn: "net.serverEmitConnection", args: [receiver, sock], type: VOID, loc };
|
|
1168
1255
|
}
|
|
@@ -1172,30 +1259,30 @@ function lowerNetServerMethodCall(L, call, access) {
|
|
|
1172
1259
|
// the property path; this is the record-valued remainder (`const a =
|
|
1173
1260
|
// server.address()` — the listen-callback shape).
|
|
1174
1261
|
if (args.length !== 0) {
|
|
1175
|
-
|
|
1262
|
+
lowerer.noLowering(`address with ${args.length} arguments`, call, "address() takes no arguments");
|
|
1176
1263
|
}
|
|
1177
|
-
const result =
|
|
1178
|
-
if (!result || !isAddressInfoRecord(
|
|
1179
|
-
|
|
1264
|
+
const result = lowerer.mapTypeOf(lowerer.typeOf(call));
|
|
1265
|
+
if (!result || !isAddressInfoRecord(lowerer, result)) {
|
|
1266
|
+
lowerer.noLowering("address() where the result is not the {address, family, port} record", call, "the AddressInfo shape is the supported result");
|
|
1180
1267
|
}
|
|
1181
|
-
const receiver =
|
|
1268
|
+
const receiver = coerceToHandle(lowerer, access.expression, NETSERVER_T);
|
|
1182
1269
|
return { kind: "libCall", fn: "net.serverAddress", args: [receiver], type: result, loc };
|
|
1183
1270
|
}
|
|
1184
|
-
|
|
1271
|
+
lowerer.noLowering(`Server.${name}`, call, "listen, close, address().port, emit(\"connection\", socket), and on/once of connection/request/upgrade/error/close/listening/sessionError are the supported Server members", lowerer.checker.getSymbolAtLocation(access.name));
|
|
1185
1272
|
}
|
|
1186
1273
|
/** Method calls on net.Socket receivers: write/end/destroy/pipe and
|
|
1187
1274
|
* on/once("data" | "end" | "close" | "error" | "connect"). Null for
|
|
1188
1275
|
* other receivers. */
|
|
1189
|
-
function lowerNetSocketMethodCall(
|
|
1190
|
-
if (
|
|
1276
|
+
function lowerNetSocketMethodCall(lowerer, call, access) {
|
|
1277
|
+
if (lowerer.mapTypeOf(lowerer.typeOf(access.expression))?.kind !== "netSocket")
|
|
1191
1278
|
return null;
|
|
1192
|
-
if (!
|
|
1279
|
+
if (!lowerer.isStdlibMember(access))
|
|
1193
1280
|
return null;
|
|
1194
1281
|
const name = access.name.text;
|
|
1195
1282
|
const loc = locOf(call);
|
|
1196
1283
|
const args = call.arguments;
|
|
1197
1284
|
if (name === "write" || name === "end") {
|
|
1198
|
-
requireStatementPosition(
|
|
1285
|
+
requireStatementPosition(lowerer, call, `socket.${name}(...)`);
|
|
1199
1286
|
// write(chunk, encoding) — the two-argument encoding form: 'buffer'
|
|
1200
1287
|
// beside a string chunk is Node's stream_base typecheck ("Second
|
|
1201
1288
|
// argument must be a buffer", thrown synchronously on an established
|
|
@@ -1203,36 +1290,36 @@ function lowerNetSocketMethodCall(L, call, access) {
|
|
|
1203
1290
|
// plain write (that IS the encoding written); a Buffer chunk ignores
|
|
1204
1291
|
// the encoding like Node does. Other encodings keep the fence.
|
|
1205
1292
|
if (name === "write" && args.length === 2) {
|
|
1206
|
-
const encT =
|
|
1207
|
-
const chunkT =
|
|
1293
|
+
const encT = lowerer.typeOf(args[1]);
|
|
1294
|
+
const chunkT = lowerer.mapTypeOf(lowerer.typeOf(args[0]));
|
|
1208
1295
|
if (encT.isStringLiteralType() && chunkT !== null) {
|
|
1209
1296
|
if (encT.value === "buffer" && chunkT.kind === "string" &&
|
|
1210
1297
|
isJsSourceFile(call.getSourceFile())) {
|
|
1211
|
-
|
|
1298
|
+
lowerer.lowerExpr(args[0]); // evaluation order (effect-free in practice)
|
|
1212
1299
|
return nodeThrowExpr(1, "ERR_INVALID_ARG_TYPE", "Second argument must be a buffer", VOID, loc);
|
|
1213
1300
|
}
|
|
1214
1301
|
const passthrough = (chunkT.kind === "string" && (encT.value === "utf8" || encT.value === "utf-8")) ||
|
|
1215
1302
|
(chunkT.kind === "bytes" && chunkT.elem === "u8");
|
|
1216
1303
|
if (passthrough) {
|
|
1217
|
-
const
|
|
1218
|
-
const
|
|
1219
|
-
const fn =
|
|
1220
|
-
return { kind: "libCall", fn, args: [
|
|
1304
|
+
const socket = coerceToHandle(lowerer, access.expression, NETSOCKET_T);
|
|
1305
|
+
const chunk = lowerer.lowerExpr(args[0]);
|
|
1306
|
+
const fn = chunk.type.kind === "string" ? "net.sockWrite" : "net.sockWriteBytes";
|
|
1307
|
+
return { kind: "libCall", fn, args: [socket, chunk], type: VOID, loc };
|
|
1221
1308
|
}
|
|
1222
1309
|
}
|
|
1223
1310
|
}
|
|
1224
1311
|
const maxArgs = name === "write" ? 1 : 1;
|
|
1225
1312
|
const minArgs = name === "write" ? 1 : 0;
|
|
1226
1313
|
if (args.length < minArgs || args.length > maxArgs) {
|
|
1227
|
-
|
|
1314
|
+
lowerer.noLowering(`${name} with ${args.length} arguments`, call, name === "write"
|
|
1228
1315
|
? "the supported form is write(data) — one string or Buffer"
|
|
1229
1316
|
: "the supported forms are end() and end(data)");
|
|
1230
1317
|
}
|
|
1231
|
-
const receiver =
|
|
1318
|
+
const receiver = coerceToHandle(lowerer, access.expression, NETSOCKET_T);
|
|
1232
1319
|
if (args.length === 0) {
|
|
1233
1320
|
return { kind: "libCall", fn: "net.sockEnd", args: [receiver], type: VOID, loc };
|
|
1234
1321
|
}
|
|
1235
|
-
const data =
|
|
1322
|
+
const data = lowerer.lowerExpr(args[0]);
|
|
1236
1323
|
if (data.type.kind === "string") {
|
|
1237
1324
|
const fn = name === "write" ? "net.sockWrite" : "net.sockEndStr";
|
|
1238
1325
|
return { kind: "libCall", fn, args: [receiver, data], type: VOID, loc };
|
|
@@ -1247,33 +1334,33 @@ function lowerNetSocketMethodCall(L, call, access) {
|
|
|
1247
1334
|
const fn = name === "write" ? "net.sockWriteDyn" : "net.sockEndDyn";
|
|
1248
1335
|
return { kind: "libCall", fn, args: [receiver, data], type: VOID, loc };
|
|
1249
1336
|
}
|
|
1250
|
-
|
|
1337
|
+
lowerer.noLowering(`${name} of '${lowerer.fmt(data.type)}' data`, args[0] ?? call, NARROW_DATA_HINT);
|
|
1251
1338
|
}
|
|
1252
1339
|
if (name === "destroy") {
|
|
1253
|
-
requireStatementPosition(
|
|
1340
|
+
requireStatementPosition(lowerer, call, "socket.destroy()");
|
|
1254
1341
|
if (args.length !== 0) {
|
|
1255
|
-
|
|
1342
|
+
lowerer.noLowering(`destroy with ${args.length} arguments`, call, "destroy() takes no arguments here");
|
|
1256
1343
|
}
|
|
1257
|
-
const receiver =
|
|
1344
|
+
const receiver = coerceToHandle(lowerer, access.expression, NETSOCKET_T);
|
|
1258
1345
|
return { kind: "libCall", fn: "net.sockDestroy", args: [receiver], type: VOID, loc };
|
|
1259
1346
|
}
|
|
1260
1347
|
if (name === "setEncoding") {
|
|
1261
1348
|
// The req twin: utf8 flips 'data' delivery to strings.
|
|
1262
|
-
requireStatementPosition(
|
|
1349
|
+
requireStatementPosition(lowerer, call, "socket.setEncoding(...)");
|
|
1263
1350
|
if (args.length !== 1) {
|
|
1264
|
-
|
|
1351
|
+
lowerer.noLowering(`setEncoding with ${args.length} arguments`, call, "the supported form is setEncoding(encoding)");
|
|
1265
1352
|
}
|
|
1266
|
-
const receiver =
|
|
1267
|
-
const enc =
|
|
1353
|
+
const receiver = coerceToHandle(lowerer, access.expression, NETSOCKET_T);
|
|
1354
|
+
const enc = lowerer.lowerExprExpecting(args[0], STRING);
|
|
1268
1355
|
return { kind: "libCall", fn: "net.sockSetEncoding", args: [receiver, enc], type: VOID, loc };
|
|
1269
1356
|
}
|
|
1270
1357
|
if (name === "setTimeout") {
|
|
1271
|
-
requireStatementPosition(
|
|
1358
|
+
requireStatementPosition(lowerer, call, "socket.setTimeout(...)");
|
|
1272
1359
|
if (args.length !== 1) {
|
|
1273
|
-
|
|
1360
|
+
lowerer.noLowering(`setTimeout with ${args.length} arguments`, call, "the supported form is setTimeout(ms) — register the callback separately: socket.once('timeout', ...)");
|
|
1274
1361
|
}
|
|
1275
|
-
const receiver =
|
|
1276
|
-
const ms =
|
|
1362
|
+
const receiver = coerceToHandle(lowerer, access.expression, NETSOCKET_T);
|
|
1363
|
+
const ms = lowerer.lowerExprExpecting(args[0], F64);
|
|
1277
1364
|
return { kind: "libCall", fn: "net.sockSetTimeout", args: [receiver, ms], type: VOID, loc };
|
|
1278
1365
|
}
|
|
1279
1366
|
if (name === "pause" || name === "resume") {
|
|
@@ -1282,9 +1369,9 @@ function lowerNetSocketMethodCall(L, call, access) {
|
|
|
1282
1369
|
// (and discards sans listeners, so 'end' is reachable). Both answer
|
|
1283
1370
|
// the socket, Node's chaining.
|
|
1284
1371
|
if (args.length !== 0) {
|
|
1285
|
-
|
|
1372
|
+
lowerer.noLowering(`${name} with ${args.length} arguments`, call, `the form is ${name}()`);
|
|
1286
1373
|
}
|
|
1287
|
-
const receiver =
|
|
1374
|
+
const receiver = coerceToHandle(lowerer, access.expression, NETSOCKET_T);
|
|
1288
1375
|
const fn = name === "pause" ? "net.sockPause" : "net.sockResume";
|
|
1289
1376
|
return { kind: "libCall", fn, args: [receiver], type: NETSOCKET_T, loc };
|
|
1290
1377
|
}
|
|
@@ -1292,22 +1379,22 @@ function lowerNetSocketMethodCall(L, call, access) {
|
|
|
1292
1379
|
// TCP_NODELAY on the live fd; missing/undefined means true (Node).
|
|
1293
1380
|
// Answers the socket, Node's chaining.
|
|
1294
1381
|
if (args.length > 1) {
|
|
1295
|
-
|
|
1382
|
+
lowerer.noLowering(`setNoDelay with ${args.length} arguments`, call, "the form is setNoDelay(noDelay?)");
|
|
1296
1383
|
}
|
|
1297
|
-
const receiver =
|
|
1384
|
+
const receiver = coerceToHandle(lowerer, access.expression, NETSOCKET_T);
|
|
1298
1385
|
const enable = args.length === 1
|
|
1299
|
-
?
|
|
1386
|
+
? lowerer.lowerExprExpecting(args[0], BOOL)
|
|
1300
1387
|
: boolLit(true, loc);
|
|
1301
1388
|
return { kind: "libCall", fn: "net.sockSetNoDelay", args: [receiver, enable], type: NETSOCKET_T, loc };
|
|
1302
1389
|
}
|
|
1303
1390
|
if (name === "destroySoon") {
|
|
1304
1391
|
// end() now, destroy once the FIN actually flushed — Node's
|
|
1305
1392
|
// 'finish'-then-destroy.
|
|
1306
|
-
requireStatementPosition(
|
|
1393
|
+
requireStatementPosition(lowerer, call, "socket.destroySoon()");
|
|
1307
1394
|
if (args.length !== 0) {
|
|
1308
|
-
|
|
1395
|
+
lowerer.noLowering(`destroySoon with ${args.length} arguments`, call, "the form is destroySoon()");
|
|
1309
1396
|
}
|
|
1310
|
-
const receiver =
|
|
1397
|
+
const receiver = coerceToHandle(lowerer, access.expression, NETSOCKET_T);
|
|
1311
1398
|
return { kind: "libCall", fn: "net.sockDestroySoon", args: [receiver], type: VOID, loc };
|
|
1312
1399
|
}
|
|
1313
1400
|
if (name === "read") {
|
|
@@ -1315,61 +1402,61 @@ function lowerNetSocketMethodCall(L, call, access) {
|
|
|
1315
1402
|
// `Buffer | null` union: exactly n buffered bytes, or null (Node's
|
|
1316
1403
|
// less-than-n answer); read() drains the whole buffer.
|
|
1317
1404
|
if (args.length > 1) {
|
|
1318
|
-
|
|
1405
|
+
lowerer.noLowering(`read with ${args.length} arguments`, call, "the supported forms are read() and read(n)");
|
|
1319
1406
|
}
|
|
1320
|
-
const receiver =
|
|
1407
|
+
const receiver = coerceToHandle(lowerer, access.expression, NETSOCKET_T);
|
|
1321
1408
|
const n = args.length === 1
|
|
1322
|
-
?
|
|
1409
|
+
? lowerer.lowerExprExpecting(args[0], F64)
|
|
1323
1410
|
: { kind: "numLit", value: 0, type: F64, loc };
|
|
1324
|
-
const type = { kind: "union", unionId:
|
|
1411
|
+
const type = { kind: "union", unionId: lowerer.unions.intern([BYTES_U8, NULL_T]) };
|
|
1325
1412
|
return { kind: "libCall", fn: "net.sockRead", args: [receiver, n], type, loc };
|
|
1326
1413
|
}
|
|
1327
1414
|
if (name === "unshift") {
|
|
1328
|
-
requireStatementPosition(
|
|
1415
|
+
requireStatementPosition(lowerer, call, "socket.unshift(...)");
|
|
1329
1416
|
if (args.length !== 1) {
|
|
1330
|
-
|
|
1417
|
+
lowerer.noLowering(`unshift with ${args.length} arguments`, call, "the supported form is unshift(buffer)");
|
|
1331
1418
|
}
|
|
1332
|
-
const receiver =
|
|
1333
|
-
const data =
|
|
1419
|
+
const receiver = coerceToHandle(lowerer, access.expression, NETSOCKET_T);
|
|
1420
|
+
const data = lowerer.lowerExpr(args[0]);
|
|
1334
1421
|
if (data.type.kind !== "bytes" || data.type.elem !== "u8") {
|
|
1335
|
-
|
|
1422
|
+
lowerer.noLowering(`unshift of '${lowerer.fmt(data.type)}' data`, args[0], "unshift takes one Buffer/Uint8Array (narrow unions first — the read(1) result needs its null arm checked)");
|
|
1336
1423
|
}
|
|
1337
1424
|
return { kind: "libCall", fn: "net.sockUnshift", args: [receiver, data], type: VOID, loc };
|
|
1338
1425
|
}
|
|
1339
1426
|
if (name === "pipe") {
|
|
1340
|
-
requireStatementPosition(
|
|
1427
|
+
requireStatementPosition(lowerer, call, "socket.pipe(...)");
|
|
1341
1428
|
if (args.length !== 1) {
|
|
1342
|
-
|
|
1429
|
+
lowerer.noLowering(`pipe with ${args.length} arguments`, call, "the supported form is pipe(destination)");
|
|
1343
1430
|
}
|
|
1344
|
-
const receiver =
|
|
1345
|
-
const dst =
|
|
1431
|
+
const receiver = coerceToHandle(lowerer, access.expression, NETSOCKET_T);
|
|
1432
|
+
const dst = lowerer.lowerExpr(args[0]);
|
|
1346
1433
|
if (dst.type.kind === "httpRes") {
|
|
1347
1434
|
// socket → ServerResponse (the extended-CONNECT bridge leg): raw
|
|
1348
1435
|
// chunks become response body writes; EOF ends the response.
|
|
1349
1436
|
return { kind: "libCall", fn: "net.sockPipeRes", args: [receiver, dst], type: VOID, loc };
|
|
1350
1437
|
}
|
|
1351
1438
|
if (dst.type.kind !== "netSocket") {
|
|
1352
|
-
|
|
1439
|
+
lowerer.noLowering(`pipe into '${lowerer.fmt(dst.type)}' destinations`, args[0], "a socket pipes into another socket or a ServerResponse");
|
|
1353
1440
|
}
|
|
1354
1441
|
return { kind: "libCall", fn: "net.sockPipe", args: [receiver, dst], type: VOID, loc };
|
|
1355
1442
|
}
|
|
1356
1443
|
if ((name === "on" || name === "once" || name === "addListener") && args.length === 2) {
|
|
1357
|
-
requireStatementPosition(
|
|
1444
|
+
requireStatementPosition(lowerer, call, `socket.${name}(...)`);
|
|
1358
1445
|
const once = boolLit(name === "once", loc);
|
|
1359
|
-
const evT =
|
|
1446
|
+
const evT = lowerer.typeOf(args[0]);
|
|
1360
1447
|
const event = evT.isStringLiteralType() ? evT.value : null;
|
|
1361
|
-
const receiver =
|
|
1448
|
+
const receiver = coerceToHandle(lowerer, access.expression, NETSOCKET_T);
|
|
1362
1449
|
if (event === "data") {
|
|
1363
|
-
const { cb } = lowerCallbackArg(
|
|
1450
|
+
const { cb } = lowerCallbackArg(lowerer, args[1], "data listeners", 1, (p) => p.kind === "bytes" && p.elem === "u8", "use (chunk: Buffer) or ()", [DYN]);
|
|
1364
1451
|
return { kind: "libCall", fn: "net.sockOnData", args: [receiver, cb, once], type: VOID, loc };
|
|
1365
1452
|
}
|
|
1366
1453
|
if (event === "error") {
|
|
1367
|
-
const { cb } = lowerCallbackArg(
|
|
1454
|
+
const { cb } = lowerCallbackArg(lowerer, args[1], "error listeners", 1, (p) => p.kind === "object" && p.className === "%Error", "use (err) or ()", [ERROR_T]);
|
|
1368
1455
|
return { kind: "libCall", fn: "net.sockOnError", args: [receiver, cb, once], type: VOID, loc };
|
|
1369
1456
|
}
|
|
1370
1457
|
if (event === "end" || event === "close" || event === "connect" || event === "timeout" ||
|
|
1371
1458
|
event === "readable") {
|
|
1372
|
-
const { cb } = lowerCallbackArg(
|
|
1459
|
+
const { cb } = lowerCallbackArg(lowerer, args[1], `${event} listeners`, 0, () => false, "use ()", []);
|
|
1373
1460
|
const fn = event === "end" ? "net.sockOnEnd"
|
|
1374
1461
|
: event === "close" ? "net.sockOnClose"
|
|
1375
1462
|
: event === "timeout" ? "net.sockOnTimeout"
|
|
@@ -1380,7 +1467,7 @@ function lowerNetSocketMethodCall(L, call, access) {
|
|
|
1380
1467
|
if (event === "finish") {
|
|
1381
1468
|
// Fires once when the FIN goes out — once either way (the event
|
|
1382
1469
|
// happens at most once per socket).
|
|
1383
|
-
const { cb } = lowerCallbackArg(
|
|
1470
|
+
const { cb } = lowerCallbackArg(lowerer, args[1], "finish listeners", 0, () => false, "use ()", []);
|
|
1384
1471
|
return { kind: "libCall", fn: "net.sockOnFinish", args: [receiver, cb], type: VOID, loc };
|
|
1385
1472
|
}
|
|
1386
1473
|
if (event === "secureConnect") {
|
|
@@ -1388,33 +1475,33 @@ function lowerNetSocketMethodCall(L, call, access) {
|
|
|
1388
1475
|
// 'connect' list already fires at establishment (scr_net.c's
|
|
1389
1476
|
// transport pump); the runtime gates on the transport so a plain
|
|
1390
1477
|
// socket's registration never fires — Node's exact split.
|
|
1391
|
-
const { cb } = lowerCallbackArg(
|
|
1478
|
+
const { cb } = lowerCallbackArg(lowerer, args[1], "secureConnect listeners", 0, () => false, "use ()", []);
|
|
1392
1479
|
return { kind: "libCall", fn: "tls.sockOnSecureConnect", args: [receiver, cb, once], type: VOID, loc };
|
|
1393
1480
|
}
|
|
1394
1481
|
if (event === "session") {
|
|
1395
1482
|
// The received-ticket event: fires once per TLS client socket with
|
|
1396
1483
|
// the serialized session (a Buffer); plain sockets never fire it.
|
|
1397
|
-
const { cb } = lowerCallbackArg(
|
|
1484
|
+
const { cb } = lowerCallbackArg(lowerer, args[1], "session listeners", 1, (p) => p.kind === "bytes" && p.elem === "u8", "use (session: Buffer) or ()", [DYN]);
|
|
1398
1485
|
return { kind: "libCall", fn: "tls.sockOnSession", args: [receiver, cb, once], type: VOID, loc };
|
|
1399
1486
|
}
|
|
1400
|
-
|
|
1487
|
+
lowerer.noLowering(`socket.${name}(${event === null ? "non-literal event" : `"${event}"`}, ...)`, args[0], '"data", "end", "close", "error", "connect", "timeout", "readable", "secureConnect", and "session" are the supported socket events (as literals)');
|
|
1401
1488
|
}
|
|
1402
|
-
|
|
1489
|
+
lowerer.noLowering(`Socket.${name}`, call, "write, end, destroy, pipe, setTimeout, remoteAddress, read, unshift, and on/once of data/end/close/error/connect/timeout/readable are the supported Socket members", lowerer.checker.getSymbolAtLocation(access.name));
|
|
1403
1490
|
}
|
|
1404
1491
|
/** The method-call dispatch for both server-surface receiver kinds — one
|
|
1405
1492
|
* entry in lower-calls.ts's intrinsic chain (the lowerChildMethodCall
|
|
1406
1493
|
* slot). Null when the receiver is neither. */
|
|
1407
|
-
export function lowerServerMethodCall(
|
|
1408
|
-
if (call.questionDotToken || (access.questionDotToken && !
|
|
1494
|
+
export function lowerServerMethodCall(lowerer, call, access) {
|
|
1495
|
+
if (call.questionDotToken || (access.questionDotToken && !lowerer.chainHandled.has(access)))
|
|
1409
1496
|
return null;
|
|
1410
|
-
return (lowerServerCloseBind(
|
|
1411
|
-
lowerNetServerMethodCall(
|
|
1412
|
-
lowerNetSocketMethodCall(
|
|
1413
|
-
lowerH2SessionMethodCall(
|
|
1414
|
-
lowerH2StreamMethodCall(
|
|
1415
|
-
lowerHttpReqMethodCall(
|
|
1416
|
-
lowerHttpResMethodCall(
|
|
1417
|
-
lowerHttpClientMethodCall(
|
|
1497
|
+
return (lowerServerCloseBind(lowerer, call, access) ??
|
|
1498
|
+
lowerNetServerMethodCall(lowerer, call, access) ??
|
|
1499
|
+
lowerNetSocketMethodCall(lowerer, call, access) ??
|
|
1500
|
+
lowerH2SessionMethodCall(lowerer, call, access) ??
|
|
1501
|
+
lowerH2StreamMethodCall(lowerer, call, access) ??
|
|
1502
|
+
lowerHttpReqMethodCall(lowerer, call, access) ??
|
|
1503
|
+
lowerHttpResMethodCall(lowerer, call, access) ??
|
|
1504
|
+
lowerHttpClientMethodCall(lowerer, call, access));
|
|
1418
1505
|
}
|
|
1419
1506
|
/** The `{ ...req.headers }` SNAPSHOT helper, interned per target shape —
|
|
1420
1507
|
* the envSnapshotHelper pattern over http.reqHeaderPairs: a fresh
|
|
@@ -1422,107 +1509,27 @@ export function lowerServerMethodCall(L, call, access) {
|
|
|
1422
1509
|
* arrival order), each value wrapped at the slot union's string arm. Null
|
|
1423
1510
|
* when the shape is not a pure index-signature record with a string arm
|
|
1424
1511
|
* (the caller fences). */
|
|
1425
|
-
function headersSnapshotHelper(
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
const iv = shape.indexValue;
|
|
1430
|
-
if (iv.kind !== "union")
|
|
1431
|
-
return null;
|
|
1432
|
-
const strTag = L.armTag(iv.unionId, STRING);
|
|
1433
|
-
if (strTag < 0)
|
|
1434
|
-
return null;
|
|
1435
|
-
const key = `headers.snapshot:${shapeId}`;
|
|
1436
|
-
const existing = L.widthHelpers.get(key);
|
|
1437
|
-
if (existing)
|
|
1438
|
-
return existing;
|
|
1439
|
-
const name = `%headers.snapshot.${L.widthHelpers.size}`;
|
|
1440
|
-
L.widthHelpers.set(key, name);
|
|
1441
|
-
const recT = { kind: "record", shapeId };
|
|
1442
|
-
const pairsT = arrayOf(STRING);
|
|
1443
|
-
const ref = (localId, type) => ({ kind: "varRef", localId, type, loc });
|
|
1444
|
-
const num = (value) => ({ kind: "numLit", value, type: F64, loc });
|
|
1445
|
-
const pairAt = (offset) => ({
|
|
1446
|
-
kind: "arrayGet",
|
|
1447
|
-
arr: ref("ps.0", pairsT),
|
|
1448
|
-
index: offset === 0
|
|
1449
|
-
? ref("i.0", F64)
|
|
1450
|
-
: { kind: "bin", op: "+", left: ref("i.0", F64), right: num(offset), type: F64, loc },
|
|
1451
|
-
type: STRING,
|
|
1452
|
-
loc,
|
|
1453
|
-
});
|
|
1454
|
-
const body = [
|
|
1455
|
-
{
|
|
1456
|
-
kind: "varDecl",
|
|
1457
|
-
localId: "ps.0",
|
|
1458
|
-
init: { kind: "libCall", fn: "http.reqHeaderPairs", args: [ref("r.0", HTTPREQ_T)], type: pairsT, loc },
|
|
1459
|
-
loc,
|
|
1460
|
-
},
|
|
1461
|
-
{
|
|
1462
|
-
kind: "varDecl",
|
|
1463
|
-
localId: "out.0",
|
|
1464
|
-
init: { kind: "recordLit", fields: [], type: recT, loc },
|
|
1465
|
-
loc,
|
|
1466
|
-
},
|
|
1467
|
-
{
|
|
1468
|
-
kind: "for",
|
|
1469
|
-
init: { kind: "varDecl", localId: "i.0", init: num(0), loc },
|
|
1470
|
-
cond: {
|
|
1471
|
-
kind: "bin",
|
|
1472
|
-
op: "<",
|
|
1473
|
-
left: ref("i.0", F64),
|
|
1474
|
-
right: { kind: "arrIntrinsic", method: "length", receiver: ref("ps.0", pairsT), args: [], type: F64, loc },
|
|
1475
|
-
type: BOOL,
|
|
1476
|
-
loc,
|
|
1477
|
-
},
|
|
1478
|
-
update: {
|
|
1479
|
-
kind: "assign",
|
|
1480
|
-
localId: "i.0",
|
|
1481
|
-
value: { kind: "bin", op: "+", left: ref("i.0", F64), right: num(2), type: F64, loc },
|
|
1482
|
-
loc,
|
|
1483
|
-
},
|
|
1484
|
-
body: [
|
|
1485
|
-
{
|
|
1486
|
-
kind: "recordKeySet",
|
|
1487
|
-
obj: ref("out.0", recT),
|
|
1488
|
-
shapeId,
|
|
1489
|
-
key: pairAt(0),
|
|
1490
|
-
value: { kind: "unionWrap", unionId: iv.unionId, tag: strTag, value: pairAt(1), type: iv, loc },
|
|
1491
|
-
loc,
|
|
1492
|
-
},
|
|
1493
|
-
],
|
|
1494
|
-
loc,
|
|
1495
|
-
},
|
|
1496
|
-
{ kind: "return", value: ref("out.0", recT), loc },
|
|
1497
|
-
];
|
|
1498
|
-
L.liftedFns.push({
|
|
1499
|
-
name,
|
|
1512
|
+
function headersSnapshotHelper(lowerer, shapeId, loc) {
|
|
1513
|
+
return pairsSnapshotHelper(lowerer, shapeId, loc, {
|
|
1514
|
+
keyPrefix: "headers",
|
|
1515
|
+
libCall: "http.reqHeaderPairs",
|
|
1500
1516
|
params: [{ localId: "r.0", name: "r", type: HTTPREQ_T }],
|
|
1501
|
-
|
|
1502
|
-
locals: [
|
|
1503
|
-
{ id: "r.0", name: "r", type: HTTPREQ_T, mutable: false },
|
|
1504
|
-
{ id: "ps.0", name: "ps", type: pairsT, mutable: false },
|
|
1505
|
-
{ id: "out.0", name: "out", type: recT, mutable: false },
|
|
1506
|
-
{ id: "i.0", name: "i", type: F64, mutable: true },
|
|
1507
|
-
],
|
|
1508
|
-
body,
|
|
1509
|
-
loc,
|
|
1517
|
+
callArgs: [varRef("r.0", HTTPREQ_T, loc)],
|
|
1510
1518
|
});
|
|
1511
|
-
return name;
|
|
1512
1519
|
}
|
|
1513
1520
|
/** The `endStream` boolean of an h2 options object literal, as a literal
|
|
1514
1521
|
* bool. Returns undefined when absent (or the arg is absent). Fences on
|
|
1515
1522
|
* a non-literal endStream value or unknown keys that would matter. */
|
|
1516
|
-
function h2EndStreamOption(
|
|
1523
|
+
function h2EndStreamOption(lowerer, node) {
|
|
1517
1524
|
if (node === undefined)
|
|
1518
1525
|
return undefined;
|
|
1519
1526
|
if (!ts.isObjectLiteralExpression(node)) {
|
|
1520
|
-
|
|
1527
|
+
lowerer.noLowering("h2 options argument", node, "pass the options as an object literal ({ endStream: true })");
|
|
1521
1528
|
}
|
|
1522
1529
|
let end;
|
|
1523
1530
|
for (const prop of node.properties) {
|
|
1524
1531
|
if (!ts.isPropertyAssignment(prop) || !ts.isIdentifier(prop.name)) {
|
|
1525
|
-
|
|
1532
|
+
lowerer.noLowering("h2 options with spreads or computed keys", prop, "use plain `name: value` entries");
|
|
1526
1533
|
}
|
|
1527
1534
|
const key = prop.name.text;
|
|
1528
1535
|
if (key === "endStream") {
|
|
@@ -1531,7 +1538,7 @@ function h2EndStreamOption(L, node) {
|
|
|
1531
1538
|
else if (prop.initializer.kind === ts.SyntaxKind.FalseKeyword)
|
|
1532
1539
|
end = false;
|
|
1533
1540
|
else
|
|
1534
|
-
|
|
1541
|
+
lowerer.noLowering("a non-literal endStream option", prop.initializer, "spell it true or false");
|
|
1535
1542
|
}
|
|
1536
1543
|
// Other keys (waitForTrailers, exclusive, parent, weight) are accepted
|
|
1537
1544
|
// and ignored — the honest-defaults stance (they tune framing this
|
|
@@ -1541,35 +1548,35 @@ function h2EndStreamOption(L, node) {
|
|
|
1541
1548
|
}
|
|
1542
1549
|
/** Method calls on ClientHttp2Session / ServerHttp2Session receivers.
|
|
1543
1550
|
* Null for other receivers (the chain keeps trying). */
|
|
1544
|
-
function lowerH2SessionMethodCall(
|
|
1545
|
-
if (
|
|
1551
|
+
function lowerH2SessionMethodCall(lowerer, call, access) {
|
|
1552
|
+
if (lowerer.mapTypeOf(lowerer.typeOf(access.expression))?.kind !== "http2Session")
|
|
1546
1553
|
return null;
|
|
1547
|
-
if (!
|
|
1554
|
+
if (!lowerer.isStdlibMember(access))
|
|
1548
1555
|
return null;
|
|
1549
1556
|
const name = access.name.text;
|
|
1550
1557
|
const loc = locOf(call);
|
|
1551
1558
|
const args = call.arguments;
|
|
1552
|
-
const receiver = () =>
|
|
1559
|
+
const receiver = () => coerceToHandle(lowerer, access.expression, HTTP2SESSION_T);
|
|
1553
1560
|
if (name === "request") {
|
|
1554
1561
|
const pairs = args.length >= 1 && !ts.isObjectLiteralExpression(args[0]) === false && args.length >= 1
|
|
1555
1562
|
? null : null;
|
|
1556
1563
|
void pairs;
|
|
1557
1564
|
// arg0: headers (optional), arg1: options (optional).
|
|
1558
|
-
const headersArg = args.length >= 1 ? lowerH2HeadersArg(
|
|
1559
|
-
const end = h2EndStreamOption(
|
|
1565
|
+
const headersArg = args.length >= 1 ? lowerH2HeadersArg(lowerer, args[0]) : { kind: "arrayLit", elems: [], type: arrayOf(STRING), loc };
|
|
1566
|
+
const end = h2EndStreamOption(lowerer, args[1]);
|
|
1560
1567
|
const endF64 = { kind: "numLit", value: end === undefined ? -1 : end ? 1 : 0, type: F64, loc };
|
|
1561
1568
|
return { kind: "libCall", fn: "http2.sessionRequest", args: [receiver(), headersArg, endF64], type: HTTP2STREAM_T, loc };
|
|
1562
1569
|
}
|
|
1563
1570
|
if (name === "close") {
|
|
1564
|
-
requireStatementPosition(
|
|
1571
|
+
requireStatementPosition(lowerer, call, "session.close(...)");
|
|
1565
1572
|
if (args.length === 0) {
|
|
1566
1573
|
return { kind: "libCall", fn: "http2.sessionClose", args: [receiver()], type: VOID, loc };
|
|
1567
1574
|
}
|
|
1568
|
-
const { cb } = lowerCallbackArg(
|
|
1575
|
+
const { cb } = lowerCallbackArg(lowerer, args[0], "close callbacks", 0, () => false, "use ()", []);
|
|
1569
1576
|
return { kind: "libCall", fn: "http2.sessionCloseCb", args: [receiver(), cb], type: VOID, loc };
|
|
1570
1577
|
}
|
|
1571
1578
|
if (name === "destroy") {
|
|
1572
|
-
requireStatementPosition(
|
|
1579
|
+
requireStatementPosition(lowerer, call, "session.destroy(...)");
|
|
1573
1580
|
return { kind: "libCall", fn: "http2.sessionDestroy", args: [receiver()], type: VOID, loc };
|
|
1574
1581
|
}
|
|
1575
1582
|
if (name === "settings") {
|
|
@@ -1577,110 +1584,110 @@ function lowerH2SessionMethodCall(L, call, access) {
|
|
|
1577
1584
|
// value (the checked-dynamic boundary — object literals box); the
|
|
1578
1585
|
// callback fires on the peer's ACK ((err, settings, duration) in the
|
|
1579
1586
|
// dyn flavor, zero-arg in the typed one).
|
|
1580
|
-
requireStatementPosition(
|
|
1587
|
+
requireStatementPosition(lowerer, call, "session.settings(...)");
|
|
1581
1588
|
if (args.length === 0) {
|
|
1582
1589
|
return { kind: "libCall", fn: "http2.sessionSettings0", args: [receiver()], type: VOID, loc };
|
|
1583
1590
|
}
|
|
1584
|
-
const v =
|
|
1591
|
+
const v = lowerer.lowerExpr(args[0]);
|
|
1585
1592
|
const settingsArg = v.type.kind === "dyn" ? v : { kind: "dynFrom", value: v, type: DYN, loc };
|
|
1586
1593
|
if (args.length >= 2) {
|
|
1587
|
-
const cbV =
|
|
1594
|
+
const cbV = lowerer.lowerExpr(args[1]);
|
|
1588
1595
|
if (cbV.type.kind === "dyn") {
|
|
1589
1596
|
// The dyn callback keeps Node's exact (err, settings, duration)
|
|
1590
1597
|
// shape — the runtime fires it through the checked-dynamic tree.
|
|
1591
1598
|
return { kind: "libCall", fn: "http2.sessionSettingsDynCb", args: [receiver(), settingsArg, cbV], type: VOID, loc };
|
|
1592
1599
|
}
|
|
1593
|
-
const { cb } = lowerCallbackArg(
|
|
1600
|
+
const { cb } = lowerCallbackArg(lowerer, args[1], "settings callbacks", 0, () => false, "use () — or a dynamic (mustCall-wrapped) callback for the (err, settings, duration) shape", []);
|
|
1594
1601
|
return { kind: "libCall", fn: "http2.sessionSettingsCb0", args: [receiver(), settingsArg, cb], type: VOID, loc };
|
|
1595
1602
|
}
|
|
1596
1603
|
return { kind: "libCall", fn: "http2.sessionSettings", args: [receiver(), settingsArg], type: VOID, loc };
|
|
1597
1604
|
}
|
|
1598
1605
|
if ((name === "on" || name === "once" || name === "addListener") && args.length === 2) {
|
|
1599
|
-
requireStatementPosition(
|
|
1606
|
+
requireStatementPosition(lowerer, call, `session.${name}(...)`);
|
|
1600
1607
|
const once = boolLit(name === "once", loc);
|
|
1601
|
-
const evT =
|
|
1608
|
+
const evT = lowerer.typeOf(args[0]);
|
|
1602
1609
|
const event = evT.isStringLiteralType() ? evT.value : null;
|
|
1603
1610
|
if (event === "localSettings" || event === "remoteSettings") {
|
|
1604
1611
|
// The settings payload crosses as a dyn value: dyn (mustCall)
|
|
1605
1612
|
// listeners fire with the settings record; zero-arg typed
|
|
1606
1613
|
// listeners register plainly.
|
|
1607
1614
|
const local = boolLit(event === "localSettings", loc);
|
|
1608
|
-
const cbV =
|
|
1615
|
+
const cbV = lowerer.lowerExpr(args[1]);
|
|
1609
1616
|
if (cbV.type.kind === "dyn") {
|
|
1610
1617
|
return { kind: "libCall", fn: "http2.sessionOnSettingsDyn", args: [receiver(), cbV, once, local], type: VOID, loc };
|
|
1611
1618
|
}
|
|
1612
|
-
const { cb } = lowerCallbackArg(
|
|
1619
|
+
const { cb } = lowerCallbackArg(lowerer, args[1], `${event} listeners`, 0, () => false, "use () — or a dynamic (mustCall-wrapped) listener for the (settings) payload", []);
|
|
1613
1620
|
return { kind: "libCall", fn: "http2.sessionOnSettings0", args: [receiver(), cb, once, local], type: VOID, loc };
|
|
1614
1621
|
}
|
|
1615
1622
|
if (event === "close") {
|
|
1616
|
-
const { cb } = lowerCallbackArg(
|
|
1623
|
+
const { cb } = lowerCallbackArg(lowerer, args[1], "close listeners", 0, () => false, "use ()", []);
|
|
1617
1624
|
return { kind: "libCall", fn: "http2.sessionOnClose", args: [receiver(), cb, once], type: VOID, loc };
|
|
1618
1625
|
}
|
|
1619
1626
|
if (event === "error") {
|
|
1620
|
-
const { cb } = lowerCallbackArg(
|
|
1627
|
+
const { cb } = lowerCallbackArg(lowerer, args[1], "error listeners", 1, (p) => p.kind === "object" && p.className === "%Error", "use (err) or ()", [ERROR_T]);
|
|
1621
1628
|
return { kind: "libCall", fn: "http2.sessionOnError", args: [receiver(), cb, once], type: VOID, loc };
|
|
1622
1629
|
}
|
|
1623
1630
|
if (event === "connect") {
|
|
1624
|
-
const { cb } = lowerCallbackArg(
|
|
1631
|
+
const { cb } = lowerCallbackArg(lowerer, args[1], "connect listeners", 2, (p) => p.kind === "http2Session" || p.kind === "netSocket", "use (session, socket) or ()", [HTTP2SESSION_T, NETSOCKET_T]);
|
|
1625
1632
|
return { kind: "libCall", fn: "http2.sessionOnConnect", args: [receiver(), cb, once], type: VOID, loc };
|
|
1626
1633
|
}
|
|
1627
1634
|
if (event === "stream") {
|
|
1628
|
-
const cb = h2HeadersCallbackAdapter(
|
|
1635
|
+
const cb = h2HeadersCallbackAdapter(lowerer, args[1], "stream listeners", "http2Stream", false, loc);
|
|
1629
1636
|
return { kind: "libCall", fn: "http2.sessionOnStream", args: [receiver(), cb, once], type: VOID, loc };
|
|
1630
1637
|
}
|
|
1631
1638
|
if (event === "goaway") {
|
|
1632
|
-
const cb =
|
|
1639
|
+
const cb = lowerer.lowerExpr(args[1]);
|
|
1633
1640
|
if (cb.type.kind !== "func" || cb.type.ret.kind !== "void" || cb.type.params.length > 3) {
|
|
1634
|
-
|
|
1641
|
+
lowerer.unsupported("SC1090", args[1], "goaway listeners with more than three parameters or returning a value");
|
|
1635
1642
|
}
|
|
1636
1643
|
if (cb.type.params.some((p) => p.kind !== "f64" && p.kind !== "bytes")) {
|
|
1637
|
-
|
|
1644
|
+
lowerer.unsupported("SC1090", args[1], "goaway listeners whose parameters are not (errorCode, lastStreamID, opaqueData?)");
|
|
1638
1645
|
}
|
|
1639
|
-
return { kind: "libCall", fn: "http2.sessionOnGoaway", args: [receiver(), voidizedCallback(
|
|
1646
|
+
return { kind: "libCall", fn: "http2.sessionOnGoaway", args: [receiver(), voidizedCallback(lowerer, cb, loc), once], type: VOID, loc };
|
|
1640
1647
|
}
|
|
1641
|
-
|
|
1648
|
+
lowerer.noLowering(`session.${name}("${event ?? "?"}", ...)`, args[0], '"close", "error", "connect", "stream", and "goaway" are the supported session events');
|
|
1642
1649
|
}
|
|
1643
|
-
|
|
1650
|
+
lowerer.noLowering(`session.${name}`, call, "close/destroy/request and on(\"close\"|\"error\"|\"connect\"|\"stream\"|\"goaway\") are the lowered session members");
|
|
1644
1651
|
}
|
|
1645
1652
|
/** Method calls on ClientHttp2Stream / ServerHttp2Stream receivers. */
|
|
1646
|
-
function lowerH2StreamMethodCall(
|
|
1653
|
+
function lowerH2StreamMethodCall(lowerer, call, access, receiverOverride) {
|
|
1647
1654
|
const compatReqStream = ts.isPropertyAccessExpression(access.expression) &&
|
|
1648
1655
|
access.expression.name.text === "stream" &&
|
|
1649
|
-
|
|
1650
|
-
if (
|
|
1656
|
+
lowerer.mapTypeOf(lowerer.typeOf(access.expression.expression))?.kind === "httpReq";
|
|
1657
|
+
if (lowerer.mapTypeOf(lowerer.typeOf(access.expression))?.kind !== "http2Stream" && !compatReqStream)
|
|
1651
1658
|
return null;
|
|
1652
|
-
if (!
|
|
1659
|
+
if (!lowerer.isStdlibMember(access) && !compatReqStream)
|
|
1653
1660
|
return null;
|
|
1654
1661
|
const name = access.name.text;
|
|
1655
1662
|
const loc = locOf(call);
|
|
1656
1663
|
const args = call.arguments;
|
|
1657
|
-
const receiver = () => receiverOverride ??
|
|
1664
|
+
const receiver = () => receiverOverride ?? coerceToHandle(lowerer, access.expression, HTTP2STREAM_T);
|
|
1658
1665
|
if (name === "respond") {
|
|
1659
|
-
requireStatementPosition(
|
|
1666
|
+
requireStatementPosition(lowerer, call, "stream.respond(...)");
|
|
1660
1667
|
const headersArg = args.length >= 1
|
|
1661
|
-
? lowerH2HeadersArg(
|
|
1668
|
+
? lowerH2HeadersArg(lowerer, args[0])
|
|
1662
1669
|
: { kind: "arrayLit", elems: [], type: arrayOf(STRING), loc };
|
|
1663
|
-
const end = h2EndStreamOption(
|
|
1670
|
+
const end = h2EndStreamOption(lowerer, args[1]);
|
|
1664
1671
|
return { kind: "libCall", fn: "http2.streamRespond", args: [receiver(), headersArg, boolLit(end ?? false, loc)], type: VOID, loc };
|
|
1665
1672
|
}
|
|
1666
1673
|
if (name === "write" || name === "end") {
|
|
1667
|
-
requireStatementPosition(
|
|
1674
|
+
requireStatementPosition(lowerer, call, `stream.${name}(...)`);
|
|
1668
1675
|
const minArgs = name === "write" ? 1 : 0;
|
|
1669
1676
|
if (args.length < minArgs || args.length > 1) {
|
|
1670
1677
|
// end() with a trailing callback is common; accept (data?, cb?) by
|
|
1671
1678
|
// dropping a trailing closure arg (fire-and-forget — the finish
|
|
1672
1679
|
// callback is not modeled here).
|
|
1673
1680
|
if (!(name === "end" && args.length === 2)) {
|
|
1674
|
-
|
|
1681
|
+
lowerer.noLowering(`${name} with ${args.length} arguments`, call, `use ${name}(data${name === "write" ? "" : "?"})`);
|
|
1675
1682
|
}
|
|
1676
1683
|
}
|
|
1677
1684
|
if (args.length === 0) {
|
|
1678
1685
|
return { kind: "libCall", fn: "http2.streamEnd", args: [receiver()], type: VOID, loc };
|
|
1679
1686
|
}
|
|
1680
|
-
const data =
|
|
1687
|
+
const data = lowerer.lowerExpr(args[0]);
|
|
1681
1688
|
const isBytes = data.type.kind === "bytes";
|
|
1682
1689
|
if (data.type.kind !== "string" && !isBytes) {
|
|
1683
|
-
|
|
1690
|
+
lowerer.noLowering(`${name} with '${lowerer.fmt(data.type)}' data`, args[0], "the chunk is a string or a Uint8Array here");
|
|
1684
1691
|
}
|
|
1685
1692
|
const fn = name === "write"
|
|
1686
1693
|
? (isBytes ? "http2.streamWriteBytes" : "http2.streamWrite")
|
|
@@ -1688,25 +1695,25 @@ function lowerH2StreamMethodCall(L, call, access, receiverOverride) {
|
|
|
1688
1695
|
return { kind: "libCall", fn, args: [receiver(), data], type: VOID, loc };
|
|
1689
1696
|
}
|
|
1690
1697
|
if (name === "close") {
|
|
1691
|
-
requireStatementPosition(
|
|
1692
|
-
const code = args.length >= 1 ?
|
|
1698
|
+
requireStatementPosition(lowerer, call, "stream.close(...)");
|
|
1699
|
+
const code = args.length >= 1 ? lowerer.lowerExpr(args[0]) : { kind: "numLit", value: 0, type: F64, loc };
|
|
1693
1700
|
if (code.type.kind !== "f64") {
|
|
1694
|
-
|
|
1701
|
+
lowerer.noLowering("stream.close with a non-numeric code", args[0], "the code is a number (http2.constants.NGHTTP2_*)");
|
|
1695
1702
|
}
|
|
1696
1703
|
if (args.length >= 2) {
|
|
1697
|
-
const { cb } = lowerCallbackArg(
|
|
1704
|
+
const { cb } = lowerCallbackArg(lowerer, args[1], "close callbacks", 0, () => false, "use ()", []);
|
|
1698
1705
|
return { kind: "libCall", fn: "http2.streamCloseCb", args: [receiver(), code, cb], type: VOID, loc };
|
|
1699
1706
|
}
|
|
1700
1707
|
return { kind: "libCall", fn: "http2.streamClose", args: [receiver(), code], type: VOID, loc };
|
|
1701
1708
|
}
|
|
1702
1709
|
if (name === "destroy") {
|
|
1703
|
-
requireStatementPosition(
|
|
1710
|
+
requireStatementPosition(lowerer, call, "stream.destroy(...)");
|
|
1704
1711
|
return { kind: "libCall", fn: "http2.streamDestroy", args: [receiver()], type: VOID, loc };
|
|
1705
1712
|
}
|
|
1706
1713
|
if (name === "setEncoding") {
|
|
1707
|
-
const enc =
|
|
1714
|
+
const enc = lowerer.lowerExpr(args[0]);
|
|
1708
1715
|
if (enc.type.kind !== "string")
|
|
1709
|
-
|
|
1716
|
+
lowerer.noLowering("setEncoding with a non-string encoding", args[0], "pass \"utf8\"");
|
|
1710
1717
|
if (ts.isExpressionStatement(call.parent) || ts.isArrowFunction(call.parent)) {
|
|
1711
1718
|
return { kind: "libCall", fn: "http2.streamSetEncoding", args: [receiver(), enc], type: VOID, loc };
|
|
1712
1719
|
}
|
|
@@ -1715,57 +1722,57 @@ function lowerH2StreamMethodCall(L, call, access, receiverOverride) {
|
|
|
1715
1722
|
return { kind: "libCall", fn: "http2.streamSetEncodingRet", args: [receiver(), enc], type: HTTP2STREAM_T, loc };
|
|
1716
1723
|
}
|
|
1717
1724
|
if (name === "resume" || name === "pause") {
|
|
1718
|
-
requireStatementPosition(
|
|
1725
|
+
requireStatementPosition(lowerer, call, `stream.${name}(...)`);
|
|
1719
1726
|
return { kind: "libCall", fn: name === "resume" ? "http2.streamResume" : "http2.streamPause", args: [receiver()], type: VOID, loc };
|
|
1720
1727
|
}
|
|
1721
1728
|
if ((name === "on" || name === "once" || name === "addListener") && args.length === 2) {
|
|
1722
|
-
requireStatementPosition(
|
|
1729
|
+
requireStatementPosition(lowerer, call, `stream.${name}(...)`);
|
|
1723
1730
|
const once = boolLit(name === "once", loc);
|
|
1724
|
-
const evT =
|
|
1731
|
+
const evT = lowerer.typeOf(args[0]);
|
|
1725
1732
|
const event = evT.isStringLiteralType() ? evT.value : null;
|
|
1726
1733
|
if (event === "data") {
|
|
1727
|
-
const { cb } = lowerCallbackArg(
|
|
1734
|
+
const { cb } = lowerCallbackArg(lowerer, args[1], "data listeners", 1, (p) => (p.kind === "bytes" && p.elem === "u8") || p.kind === "string" || p.kind === "dyn", "use (chunk) or ()", [BYTES_U8]);
|
|
1728
1735
|
return { kind: "libCall", fn: "http2.streamOnData", args: [receiver(), cb, once], type: VOID, loc };
|
|
1729
1736
|
}
|
|
1730
1737
|
if (event === "end" || event === "close" || event === "aborted") {
|
|
1731
|
-
const { cb } = lowerCallbackArg(
|
|
1738
|
+
const { cb } = lowerCallbackArg(lowerer, args[1], `${event} listeners`, 0, () => false, "use ()", []);
|
|
1732
1739
|
const fn = event === "end" ? "http2.streamOnEnd" : event === "close" ? "http2.streamOnClose" : "http2.streamOnAborted";
|
|
1733
1740
|
return { kind: "libCall", fn, args: [receiver(), cb, once], type: VOID, loc };
|
|
1734
1741
|
}
|
|
1735
1742
|
if (event === "error") {
|
|
1736
|
-
const { cb } = lowerCallbackArg(
|
|
1743
|
+
const { cb } = lowerCallbackArg(lowerer, args[1], "error listeners", 1, (p) => p.kind === "object" && p.className === "%Error", "use (err) or ()", [ERROR_T]);
|
|
1737
1744
|
return { kind: "libCall", fn: "http2.streamOnError", args: [receiver(), cb, once], type: VOID, loc };
|
|
1738
1745
|
}
|
|
1739
1746
|
if (event === "response") {
|
|
1740
|
-
const cb = h2HeadersCallbackAdapter(
|
|
1747
|
+
const cb = h2HeadersCallbackAdapter(lowerer, args[1], "response listeners", null, true, loc);
|
|
1741
1748
|
return { kind: "libCall", fn: "http2.streamOnResponse", args: [receiver(), cb, once], type: VOID, loc };
|
|
1742
1749
|
}
|
|
1743
|
-
|
|
1750
|
+
lowerer.noLowering(`stream.${name}("${event ?? "?"}", ...)`, args[0], '"data", "end", "close", "aborted", "error", and "response" are the supported stream events');
|
|
1744
1751
|
}
|
|
1745
|
-
|
|
1752
|
+
lowerer.noLowering(`stream.${name}`, call, "respond/write/end/close/destroy/setEncoding/resume/pause and on(...) are the lowered stream members");
|
|
1746
1753
|
}
|
|
1747
1754
|
/** A compatibility request's `req.stream?.method(...)`: the static Node
|
|
1748
1755
|
* type omits the HTTP/1 undefined arm, so build the runtime guard explicitly.
|
|
1749
1756
|
* The body receives the narrowed backing stream directly, preserving lazy
|
|
1750
1757
|
* argument evaluation on the HTTP/1 arm. */
|
|
1751
|
-
export function lowerCompatReqStreamOptionalCall(
|
|
1758
|
+
export function lowerCompatReqStreamOptionalCall(lowerer, call) {
|
|
1752
1759
|
if (!ts.isPropertyAccessExpression(call.expression) || !call.expression.questionDotToken)
|
|
1753
1760
|
return null;
|
|
1754
1761
|
const stream = call.expression.expression;
|
|
1755
1762
|
if (!ts.isPropertyAccessExpression(stream) || stream.name.text !== "stream" ||
|
|
1756
|
-
|
|
1763
|
+
lowerer.mapTypeOf(lowerer.typeOf(stream.expression))?.kind !== "httpReq")
|
|
1757
1764
|
return null;
|
|
1758
1765
|
const loc = locOf(call);
|
|
1759
|
-
const unionT = { kind: "union", unionId:
|
|
1766
|
+
const unionT = { kind: "union", unionId: lowerer.unions.intern([HTTP2STREAM_T, UNDEFINED_T]) };
|
|
1760
1767
|
const guarded = {
|
|
1761
1768
|
kind: "libCall",
|
|
1762
1769
|
fn: "http.reqH2Stream",
|
|
1763
|
-
args: [
|
|
1770
|
+
args: [coerceToHandle(lowerer, stream.expression, HTTPREQ_T)],
|
|
1764
1771
|
type: unionT,
|
|
1765
1772
|
loc,
|
|
1766
1773
|
};
|
|
1767
|
-
const id = `chain.${
|
|
1768
|
-
const body = lowerH2StreamMethodCall(
|
|
1774
|
+
const id = `chain.${lowerer.chainCounter++}`;
|
|
1775
|
+
const body = lowerH2StreamMethodCall(lowerer, call, call.expression, { kind: "chainRecv", id, type: HTTP2STREAM_T, loc });
|
|
1769
1776
|
if (body === null)
|
|
1770
1777
|
return null;
|
|
1771
1778
|
return { kind: "optChain", id, receiver: guarded, body, type: VOID, loc };
|
|
@@ -1774,76 +1781,76 @@ export function lowerCompatReqStreamOptionalCall(L, call) {
|
|
|
1774
1781
|
* randomBytes(n).toString(enc) precedent: the AddressInfo record between
|
|
1775
1782
|
* the two reads never materializes; the runtime answers the bound port
|
|
1776
1783
|
* directly. Null for every other property shape. */
|
|
1777
|
-
export function lowerServerProperty(
|
|
1778
|
-
if (expr.questionDotToken && !
|
|
1784
|
+
export function lowerServerProperty(lowerer, expr) {
|
|
1785
|
+
if (expr.questionDotToken && !lowerer.chainHandled.has(expr))
|
|
1779
1786
|
return null;
|
|
1780
1787
|
const loc = locOf(expr);
|
|
1781
1788
|
// req.url / req.method — always-present strings on server requests;
|
|
1782
1789
|
// req.headers.NAME — the `string | undefined` union (envGet's type).
|
|
1783
|
-
const recvKind =
|
|
1784
|
-
if (recvKind === "netServer" &&
|
|
1790
|
+
const recvKind = lowerer.mapTypeOf(lowerer.typeOf(expr.expression))?.kind;
|
|
1791
|
+
if (recvKind === "netServer" && lowerer.isStdlibMember(expr)) {
|
|
1785
1792
|
const field = httpServerTimeoutField(expr.name.text);
|
|
1786
1793
|
if (field !== null) {
|
|
1787
|
-
const receiver =
|
|
1794
|
+
const receiver = coerceToHandle(lowerer, expr.expression, NETSERVER_T);
|
|
1788
1795
|
const selector = { kind: "numLit", value: field, type: F64, loc };
|
|
1789
1796
|
return { kind: "libCall", fn: "http.serverTimeoutGet", args: [receiver, selector], type: F64, loc };
|
|
1790
1797
|
}
|
|
1791
1798
|
}
|
|
1792
|
-
if (recvKind === "httpReq" &&
|
|
1799
|
+
if (recvKind === "httpReq" && lowerer.isStdlibMember(expr)) {
|
|
1793
1800
|
const name = expr.name.text;
|
|
1794
1801
|
if (name === "url" || name === "method") {
|
|
1795
|
-
const receiver =
|
|
1802
|
+
const receiver = coerceToHandle(lowerer, expr.expression, HTTPREQ_T);
|
|
1796
1803
|
const fn = name === "url" ? "http.reqUrl" : "http.reqMethod";
|
|
1797
1804
|
return { kind: "libCall", fn, args: [receiver], type: STRING, loc };
|
|
1798
1805
|
}
|
|
1799
1806
|
if (name === "statusCode") {
|
|
1800
1807
|
// `number | undefined` — a real status on client responses, the
|
|
1801
1808
|
// undefined arm on server requests (Node's IncomingMessage split).
|
|
1802
|
-
const receiver =
|
|
1803
|
-
return { kind: "libCall", fn: "http.reqStatusCode", args: [receiver], type:
|
|
1809
|
+
const receiver = coerceToHandle(lowerer, expr.expression, HTTPREQ_T);
|
|
1810
|
+
return { kind: "libCall", fn: "http.reqStatusCode", args: [receiver], type: lowerer.withUndefinedArm(F64), loc };
|
|
1804
1811
|
}
|
|
1805
1812
|
if (name === "socket") {
|
|
1806
|
-
const receiver =
|
|
1813
|
+
const receiver = coerceToHandle(lowerer, expr.expression, HTTPREQ_T);
|
|
1807
1814
|
return { kind: "libCall", fn: "http.reqSocket", args: [receiver], type: NETSOCKET_T, loc };
|
|
1808
1815
|
}
|
|
1809
1816
|
if (name === "httpVersion") {
|
|
1810
|
-
const receiver =
|
|
1817
|
+
const receiver = coerceToHandle(lowerer, expr.expression, HTTPREQ_T);
|
|
1811
1818
|
return { kind: "libCall", fn: "http.reqHttpVersion", args: [receiver], type: STRING, loc };
|
|
1812
1819
|
}
|
|
1813
1820
|
if (name === "httpVersionMajor" || name === "httpVersionMinor") {
|
|
1814
|
-
const receiver =
|
|
1821
|
+
const receiver = coerceToHandle(lowerer, expr.expression, HTTPREQ_T);
|
|
1815
1822
|
const fn = name === "httpVersionMajor" ? "http.reqHttpVersionMajor" : "http.reqHttpVersionMinor";
|
|
1816
1823
|
return { kind: "libCall", fn, args: [receiver], type: F64, loc };
|
|
1817
1824
|
}
|
|
1818
1825
|
if (name === "aborted" || name === "complete") {
|
|
1819
1826
|
// The compat pair's flags (Http2ServerRequest); an http/1 request
|
|
1820
1827
|
// answers aborted: false and complete-once-ended the same way.
|
|
1821
|
-
const receiver =
|
|
1828
|
+
const receiver = coerceToHandle(lowerer, expr.expression, HTTPREQ_T);
|
|
1822
1829
|
const fn = name === "aborted" ? "http.reqAborted" : "http.reqComplete";
|
|
1823
1830
|
return { kind: "libCall", fn, args: [receiver], type: BOOL, loc };
|
|
1824
1831
|
}
|
|
1825
1832
|
if (name === "rawHeaders") {
|
|
1826
1833
|
// [name, value, name, value, ...] in arrival order, names in their
|
|
1827
1834
|
// ORIGINAL case — Node's shape; a fresh string[] per read.
|
|
1828
|
-
const receiver =
|
|
1835
|
+
const receiver = coerceToHandle(lowerer, expr.expression, HTTPREQ_T);
|
|
1829
1836
|
return { kind: "libCall", fn: "http.reqRawHeaders", args: [receiver], type: arrayOf(STRING), loc };
|
|
1830
1837
|
}
|
|
1831
1838
|
if (name === "statusMessage") {
|
|
1832
1839
|
// `string | undefined` — the reason phrase on client responses (""
|
|
1833
1840
|
// when the status line carried none), undefined on server requests
|
|
1834
1841
|
// (the statusCode split).
|
|
1835
|
-
const receiver =
|
|
1836
|
-
return { kind: "libCall", fn: "http.reqStatusMessage", args: [receiver], type:
|
|
1842
|
+
const receiver = coerceToHandle(lowerer, expr.expression, HTTPREQ_T);
|
|
1843
|
+
return { kind: "libCall", fn: "http.reqStatusMessage", args: [receiver], type: lowerer.envValueType(), loc };
|
|
1837
1844
|
}
|
|
1838
1845
|
if (name === "stream") {
|
|
1839
1846
|
const parent = expr.parent;
|
|
1840
1847
|
if (ts.isPropertyAccessExpression(parent) && parent.expression === expr &&
|
|
1841
1848
|
ts.isCallExpression(parent.parent) && parent.parent.expression === parent) {
|
|
1842
|
-
const receiver =
|
|
1849
|
+
const receiver = coerceToHandle(lowerer, expr.expression, HTTPREQ_T);
|
|
1843
1850
|
if (parent.questionDotToken) {
|
|
1844
1851
|
const type = {
|
|
1845
1852
|
kind: "union",
|
|
1846
|
-
unionId:
|
|
1853
|
+
unionId: lowerer.unions.intern([HTTP2STREAM_T, UNDEFINED_T]),
|
|
1847
1854
|
};
|
|
1848
1855
|
return { kind: "libCall", fn: "http.reqH2Stream", args: [receiver], type, loc };
|
|
1849
1856
|
}
|
|
@@ -1855,31 +1862,31 @@ export function lowerServerProperty(L, expr) {
|
|
|
1855
1862
|
loc,
|
|
1856
1863
|
};
|
|
1857
1864
|
}
|
|
1858
|
-
|
|
1865
|
+
lowerer.noLowering("reading 'stream' from a request outside a method call", expr, "call req.stream methods directly; optional calls remain undefined on HTTP/1 and use the live stream on HTTP/2");
|
|
1859
1866
|
}
|
|
1860
1867
|
if (name === "session") {
|
|
1861
1868
|
// Http2ServerRequest's h2-only members are not exposed through the
|
|
1862
1869
|
// compatibility request handle yet. The call forms retain their
|
|
1863
1870
|
// guarded-no-op / unguarded-TypeError stance; other reads fence.
|
|
1864
|
-
|
|
1871
|
+
lowerer.noLowering(`reading '${name}' (an HTTP/2-only member) from a request`, expr, "compatibility requests do not expose their backing h2 stream/session yet; guarded method calls no-op and other reads have no lowering");
|
|
1865
1872
|
}
|
|
1866
1873
|
if (name === "headers") {
|
|
1867
1874
|
// `req.headers` as a VALUE — the `{ ...req.headers }` spread and
|
|
1868
1875
|
// record flows: a fresh snapshot record per read (Node's spread
|
|
1869
1876
|
// copies too; the per-name READS keep their direct lowerings).
|
|
1870
|
-
const mapped =
|
|
1877
|
+
const mapped = lowerer.mapTypeOf(lowerer.typeOf(expr));
|
|
1871
1878
|
if (mapped?.kind === "record") {
|
|
1872
|
-
const helper = headersSnapshotHelper(
|
|
1879
|
+
const helper = headersSnapshotHelper(lowerer, mapped.shapeId, loc);
|
|
1873
1880
|
if (helper !== null) {
|
|
1874
|
-
const receiver =
|
|
1881
|
+
const receiver = coerceToHandle(lowerer, expr.expression, HTTPREQ_T);
|
|
1875
1882
|
return { kind: "call", callee: helper, args: [receiver], type: mapped, loc };
|
|
1876
1883
|
}
|
|
1877
1884
|
}
|
|
1878
|
-
|
|
1885
|
+
lowerer.unsupported("SC1090", expr, "req.headers as a value of this type (read one header: req.headers.name or req.headers[name])");
|
|
1879
1886
|
}
|
|
1880
1887
|
}
|
|
1881
|
-
if (recvKind === "httpRes" &&
|
|
1882
|
-
|
|
1888
|
+
if (recvKind === "httpRes" && lowerer.isStdlibMember(expr) && expr.name.text === "stream") {
|
|
1889
|
+
lowerer.noLowering("reading 'stream' (an HTTP/2-only member) from a response", expr, "compatibility responses do not expose their backing h2 stream yet — guard method calls with '?.' or drop the use");
|
|
1883
1890
|
}
|
|
1884
1891
|
if (recvKind === "netSocket" && expr.name.text === "encrypted") {
|
|
1885
1892
|
// `boolean | undefined` — Node types `encrypted` on TLSSocket only
|
|
@@ -1890,85 +1897,85 @@ export function lowerServerProperty(L, expr) {
|
|
|
1890
1897
|
// stdlib-provenance check deliberately does not apply: the receiver
|
|
1891
1898
|
// KIND is a real socket either way, and `encrypted` on a socket has
|
|
1892
1899
|
// exactly one meaning.
|
|
1893
|
-
const receiver =
|
|
1894
|
-
return { kind: "libCall", fn: "net.sockEncrypted", args: [receiver], type:
|
|
1900
|
+
const receiver = coerceToHandle(lowerer, expr.expression, NETSOCKET_T);
|
|
1901
|
+
return { kind: "libCall", fn: "net.sockEncrypted", args: [receiver], type: lowerer.withUndefinedArm(BOOL), loc };
|
|
1895
1902
|
}
|
|
1896
1903
|
if (recvKind === "netSocket" && expr.name.text === "authorized") {
|
|
1897
1904
|
// TLSSocket.authorized — Node's verify verdict (false on plain
|
|
1898
1905
|
// sockets, servers without requestCert, and unverified clients).
|
|
1899
1906
|
// Like `encrypted`, no stdlib-provenance gate: the member has exactly
|
|
1900
1907
|
// one meaning on a socket-kind receiver.
|
|
1901
|
-
const receiver =
|
|
1908
|
+
const receiver = coerceToHandle(lowerer, expr.expression, NETSOCKET_T);
|
|
1902
1909
|
return { kind: "libCall", fn: "tls.sockAuthorized", args: [receiver], type: BOOL, loc };
|
|
1903
1910
|
}
|
|
1904
1911
|
if (recvKind === "netSocket" && expr.name.text === "authorizationError") {
|
|
1905
1912
|
// TLSSocket.authorizationError — the verify-failure CODE STRING
|
|
1906
1913
|
// (DEPTH_ZERO_SELF_SIGNED_CERT, ...) or null when authorized/never
|
|
1907
1914
|
// verified: Node's exact value shape (a string, not an Error).
|
|
1908
|
-
const receiver =
|
|
1909
|
-
const type = { kind: "union", unionId:
|
|
1915
|
+
const receiver = coerceToHandle(lowerer, expr.expression, NETSOCKET_T);
|
|
1916
|
+
const type = { kind: "union", unionId: lowerer.unions.intern([STRING, NULL_T]) };
|
|
1910
1917
|
return { kind: "libCall", fn: "tls.sockAuthError", args: [receiver], type, loc };
|
|
1911
1918
|
}
|
|
1912
|
-
if (recvKind === "netSocket" &&
|
|
1919
|
+
if (recvKind === "netSocket" && lowerer.isStdlibMember(expr) && expr.name.text === "destroyed") {
|
|
1913
1920
|
// true once the fd is gone (destroy() or full close) — Node's flag,
|
|
1914
1921
|
// the proxy's re-entrant teardown guard.
|
|
1915
|
-
const receiver =
|
|
1922
|
+
const receiver = coerceToHandle(lowerer, expr.expression, NETSOCKET_T);
|
|
1916
1923
|
return { kind: "libCall", fn: "net.sockDestroyed", args: [receiver], type: BOOL, loc };
|
|
1917
1924
|
}
|
|
1918
|
-
if (recvKind === "netSocket" &&
|
|
1925
|
+
if (recvKind === "netSocket" && lowerer.isStdlibMember(expr) && expr.name.text === "writable") {
|
|
1919
1926
|
// Node's stream answer: the write half is open — no end() yet, no FIN
|
|
1920
1927
|
// sent, fd alive (connecting sockets answer true; writes queue). The
|
|
1921
1928
|
// proxy's "may I still answer 502" guard.
|
|
1922
|
-
const receiver =
|
|
1929
|
+
const receiver = coerceToHandle(lowerer, expr.expression, NETSOCKET_T);
|
|
1923
1930
|
return { kind: "libCall", fn: "net.sockWritable", args: [receiver], type: BOOL, loc };
|
|
1924
1931
|
}
|
|
1925
|
-
if (recvKind === "netSocket" &&
|
|
1932
|
+
if (recvKind === "netSocket" && lowerer.isStdlibMember(expr) && expr.name.text === "bytesWritten") {
|
|
1926
1933
|
// Every byte the write paths accepted (buffered included — Node
|
|
1927
1934
|
// counts those too; plaintext on TLS sockets).
|
|
1928
|
-
const receiver =
|
|
1935
|
+
const receiver = coerceToHandle(lowerer, expr.expression, NETSOCKET_T);
|
|
1929
1936
|
return { kind: "libCall", fn: "net.sockBytesWritten", args: [receiver], type: F64, loc };
|
|
1930
1937
|
}
|
|
1931
|
-
if (recvKind === "netSocket" &&
|
|
1938
|
+
if (recvKind === "netSocket" && lowerer.isStdlibMember(expr) && expr.name.text === "readable") {
|
|
1932
1939
|
// true until the read half is done (peer FIN / destroy).
|
|
1933
|
-
const receiver =
|
|
1940
|
+
const receiver = coerceToHandle(lowerer, expr.expression, NETSOCKET_T);
|
|
1934
1941
|
return { kind: "libCall", fn: "net.sockReadable", args: [receiver], type: BOOL, loc };
|
|
1935
1942
|
}
|
|
1936
|
-
if (recvKind === "netSocket" &&
|
|
1943
|
+
if (recvKind === "netSocket" && lowerer.isStdlibMember(expr) && expr.name.text === "remoteAddress") {
|
|
1937
1944
|
// `string | undefined` — Node's read-time caching: a value read while
|
|
1938
1945
|
// connected survives destroy; never-read sockets answer undefined
|
|
1939
1946
|
// after close.
|
|
1940
|
-
const receiver =
|
|
1941
|
-
return { kind: "libCall", fn: "net.sockRemoteAddress", args: [receiver], type:
|
|
1947
|
+
const receiver = coerceToHandle(lowerer, expr.expression, NETSOCKET_T);
|
|
1948
|
+
return { kind: "libCall", fn: "net.sockRemoteAddress", args: [receiver], type: lowerer.envValueType(), loc };
|
|
1942
1949
|
}
|
|
1943
|
-
if (recvKind === "httpClientReq" &&
|
|
1944
|
-
const receiver =
|
|
1950
|
+
if (recvKind === "httpClientReq" && lowerer.isStdlibMember(expr) && expr.name.text === "destroyed") {
|
|
1951
|
+
const receiver = coerceToHandle(lowerer, expr.expression, HTTPCLIENTREQ_T);
|
|
1945
1952
|
return { kind: "libCall", fn: "http.clientDestroyed", args: [receiver], type: BOOL, loc };
|
|
1946
1953
|
}
|
|
1947
|
-
if (isHttpReqHeaders(
|
|
1948
|
-
const receiver =
|
|
1954
|
+
if (isHttpReqHeaders(lowerer, expr.expression)) {
|
|
1955
|
+
const receiver = coerceToHandle(lowerer, expr.expression.expression, HTTPREQ_T);
|
|
1949
1956
|
const key = { kind: "strLit", value: expr.name.text, type: STRING, loc };
|
|
1950
|
-
return { kind: "libCall", fn: "http.reqHeader", args: [receiver, key], type:
|
|
1957
|
+
return { kind: "libCall", fn: "http.reqHeader", args: [receiver, key], type: lowerer.envValueType(), loc };
|
|
1951
1958
|
}
|
|
1952
|
-
if (recvKind === "httpRes" &&
|
|
1953
|
-
const receiver =
|
|
1959
|
+
if (recvKind === "httpRes" && lowerer.isStdlibMember(expr) && expr.name.text === "headersSent") {
|
|
1960
|
+
const receiver = coerceToHandle(lowerer, expr.expression, HTTPRES_T);
|
|
1954
1961
|
return { kind: "libCall", fn: "http.resHeadersSent", args: [receiver], type: BOOL, loc };
|
|
1955
1962
|
}
|
|
1956
|
-
if (recvKind === "httpRes" &&
|
|
1963
|
+
if (recvKind === "httpRes" && lowerer.isStdlibMember(expr) && expr.name.text === "statusCode") {
|
|
1957
1964
|
// 200 until assigned — Node's fresh-response default; the assignment
|
|
1958
1965
|
// twin lives in lowerHttpResPropertyAssignment.
|
|
1959
|
-
const receiver =
|
|
1966
|
+
const receiver = coerceToHandle(lowerer, expr.expression, HTTPRES_T);
|
|
1960
1967
|
return { kind: "libCall", fn: "http.resStatusGet", args: [receiver], type: F64, loc };
|
|
1961
1968
|
}
|
|
1962
|
-
if (recvKind === "httpRes" &&
|
|
1969
|
+
if (recvKind === "httpRes" && lowerer.isStdlibMember(expr) && expr.name.text === "statusMessage") {
|
|
1963
1970
|
// The assigned reason phrase, or the current status code's default
|
|
1964
1971
|
// when none was set (Node answers undefined until the head goes out
|
|
1965
1972
|
// — divergence: this surface is string-typed, the checker's shape).
|
|
1966
|
-
const receiver =
|
|
1973
|
+
const receiver = coerceToHandle(lowerer, expr.expression, HTTPRES_T);
|
|
1967
1974
|
return { kind: "libCall", fn: "http.resStatusMsgGet", args: [receiver], type: STRING, loc };
|
|
1968
1975
|
}
|
|
1969
|
-
if (recvKind === "http2Session" &&
|
|
1976
|
+
if (recvKind === "http2Session" && lowerer.isStdlibMember(expr)) {
|
|
1970
1977
|
const m = expr.name.text;
|
|
1971
|
-
const recv = () =>
|
|
1978
|
+
const recv = () => coerceToHandle(lowerer, expr.expression, HTTP2SESSION_T);
|
|
1972
1979
|
if (m === "closed")
|
|
1973
1980
|
return { kind: "libCall", fn: "http2.sessionClosed", args: [recv()], type: BOOL, loc };
|
|
1974
1981
|
if (m === "destroyed")
|
|
@@ -1992,9 +1999,9 @@ export function lowerServerProperty(L, expr) {
|
|
|
1992
1999
|
};
|
|
1993
2000
|
}
|
|
1994
2001
|
}
|
|
1995
|
-
if (recvKind === "http2Stream" &&
|
|
2002
|
+
if (recvKind === "http2Stream" && lowerer.isStdlibMember(expr)) {
|
|
1996
2003
|
const m = expr.name.text;
|
|
1997
|
-
const recv = () =>
|
|
2004
|
+
const recv = () => coerceToHandle(lowerer, expr.expression, HTTP2STREAM_T);
|
|
1998
2005
|
if (m === "id")
|
|
1999
2006
|
return { kind: "libCall", fn: "http2.streamId", args: [recv()], type: F64, loc };
|
|
2000
2007
|
if (m === "rstCode")
|
|
@@ -2020,11 +2027,11 @@ export function lowerServerProperty(L, expr) {
|
|
|
2020
2027
|
const inner = recv.expression;
|
|
2021
2028
|
if (inner.name.text !== "address" || inner.questionDotToken)
|
|
2022
2029
|
return null;
|
|
2023
|
-
if (
|
|
2030
|
+
if (lowerer.mapTypeOf(lowerer.typeOf(inner.expression))?.kind !== "netServer")
|
|
2024
2031
|
return null;
|
|
2025
|
-
if (!
|
|
2032
|
+
if (!lowerer.isStdlibMember(inner))
|
|
2026
2033
|
return null;
|
|
2027
|
-
const receiver =
|
|
2034
|
+
const receiver = coerceToHandle(lowerer, inner.expression, NETSERVER_T);
|
|
2028
2035
|
return { kind: "libCall", fn: "net.serverPort", args: [receiver], type: F64, loc };
|
|
2029
2036
|
}
|
|
2030
2037
|
/** The (req, res) request-handler argument — http.createServer's, https
|
|
@@ -2033,8 +2040,8 @@ export function lowerServerProperty(L, expr) {
|
|
|
2033
2040
|
* A value-returning handler adapts through the cast-and-discard helper
|
|
2034
2041
|
* (Node ignores handler results — the writeHead-chaining body
|
|
2035
2042
|
* `(req, res) => res.writeHead(200).end()` is the motivating shape). */
|
|
2036
|
-
function lowerRequestHandlerArg(
|
|
2037
|
-
const cb =
|
|
2043
|
+
function lowerRequestHandlerArg(lowerer, node) {
|
|
2044
|
+
const cb = lowerer.lowerExpr(node);
|
|
2038
2045
|
if (cb.type.kind === "dyn") {
|
|
2039
2046
|
// A CHECKED-DYNAMIC handler (test/common's mustCall wrapper around
|
|
2040
2047
|
// the (req, res) listener — the canonical suite shape): adapt
|
|
@@ -2048,7 +2055,7 @@ function lowerRequestHandlerArg(L, node) {
|
|
|
2048
2055
|
if (cb.type.kind === "func" &&
|
|
2049
2056
|
(cb.type.rest === true ||
|
|
2050
2057
|
(cb.type.params.length > 0 && cb.type.params.every((p) => p.kind === "dyn"))) &&
|
|
2051
|
-
canBoxFuncIntoDyn(cb.type, (id) =>
|
|
2058
|
+
canBoxFuncIntoDyn(cb.type, (id) => lowerer.shapes.get(id), (id) => lowerer.unions.get(id))) {
|
|
2052
2059
|
// A hoisted plain-JS handler (`http.createServer(handle)` where
|
|
2053
2060
|
// `function handle(req, res)` has no contextual type — dyn params),
|
|
2054
2061
|
// or an arguments-reading rest function: box it and adapt exactly
|
|
@@ -2058,13 +2065,13 @@ function lowerRequestHandlerArg(L, node) {
|
|
|
2058
2065
|
return { kind: "dynCheck", value: boxed, type: funcOf([HTTPREQ_T, HTTPRES_T], VOID), loc: locOf(node) };
|
|
2059
2066
|
}
|
|
2060
2067
|
if (cb.type.kind !== "func" || cb.type.params.length > 2) {
|
|
2061
|
-
|
|
2068
|
+
lowerer.unsupported("SC1090", node, "request handlers with more than two parameters (use (req, res))");
|
|
2062
2069
|
}
|
|
2063
2070
|
const [p0, p1] = cb.type.params;
|
|
2064
2071
|
if ((p0 !== undefined && p0.kind !== "httpReq") || (p1 !== undefined && p1.kind !== "httpRes")) {
|
|
2065
|
-
|
|
2072
|
+
lowerer.unsupported("SC1090", node, "request handlers whose parameters are not (req: IncomingMessage, res: ServerResponse)");
|
|
2066
2073
|
}
|
|
2067
|
-
return voidizedCallback(
|
|
2074
|
+
return voidizedCallback(lowerer, cb, locOf(node));
|
|
2068
2075
|
}
|
|
2069
2076
|
/** The http.createServer / http.Server options object — { requireHostHeader?,
|
|
2070
2077
|
* joinDuplicateHeaders?, keepAliveTimeoutBuffer?, ... }. The first two
|
|
@@ -2072,9 +2079,9 @@ function lowerRequestHandlerArg(L, node) {
|
|
|
2072
2079
|
* writable server field after Node's non-negative-safe-integer validation.
|
|
2073
2080
|
* Other documented keys fence by name; unknown keys drop like Node drops
|
|
2074
2081
|
* them. */
|
|
2075
|
-
function lowerHttpServerOptions(
|
|
2082
|
+
function lowerHttpServerOptions(lowerer, node, what) {
|
|
2076
2083
|
if (!ts.isObjectLiteralExpression(node)) {
|
|
2077
|
-
|
|
2084
|
+
lowerer.noLowering(`${what} with a non-literal options argument`, node, "pass the options as an object literal: { requireHostHeader?, joinDuplicateHeaders? }");
|
|
2078
2085
|
}
|
|
2079
2086
|
let joinDup = false;
|
|
2080
2087
|
let keepAliveTimeoutBuffer = null;
|
|
@@ -2088,7 +2095,7 @@ function lowerHttpServerOptions(L, node, what) {
|
|
|
2088
2095
|
initializer = null;
|
|
2089
2096
|
}
|
|
2090
2097
|
else {
|
|
2091
|
-
|
|
2098
|
+
lowerer.noLowering(`${what} options with computed keys or spreads`, prop, "each option must be a plain `name: value` (or shorthand) entry with a literal key");
|
|
2092
2099
|
}
|
|
2093
2100
|
const key = prop.name.text;
|
|
2094
2101
|
if (key === "requireHostHeader") {
|
|
@@ -2097,7 +2104,7 @@ function lowerHttpServerOptions(L, node, what) {
|
|
|
2097
2104
|
// (true: answer 400) is the behavior this slice does not have, so
|
|
2098
2105
|
// `true`/dynamic values fence instead of silently not enforcing.
|
|
2099
2106
|
if (initializer === null || initializer.kind !== ts.SyntaxKind.FalseKeyword) {
|
|
2100
|
-
|
|
2107
|
+
lowerer.noLowering(`${what} with a non-\`false\` requireHostHeader option`, prop, "this parser never answers 400 for a missing Host header — requireHostHeader: false is the honest (and only) lowered value");
|
|
2101
2108
|
}
|
|
2102
2109
|
continue;
|
|
2103
2110
|
}
|
|
@@ -2111,18 +2118,18 @@ function lowerHttpServerOptions(L, node, what) {
|
|
|
2111
2118
|
if (initializer !== null && initializer.kind === ts.SyntaxKind.FalseKeyword) {
|
|
2112
2119
|
continue; /* Node's default: repeats keep the first value */
|
|
2113
2120
|
}
|
|
2114
|
-
|
|
2121
|
+
lowerer.noLowering(`${what} with a non-literal joinDuplicateHeaders option`, prop, "the lowered forms are the literals joinDuplicateHeaders: true (repeats join \", \") and false (the keep-first default)");
|
|
2115
2122
|
}
|
|
2116
2123
|
if (key === "keepAliveTimeoutBuffer") {
|
|
2117
2124
|
if (keepAliveTimeoutBuffer !== null) {
|
|
2118
|
-
|
|
2125
|
+
lowerer.noLowering(`${what} with duplicate keepAliveTimeoutBuffer options`, prop, "write keepAliveTimeoutBuffer once in the options literal");
|
|
2119
2126
|
}
|
|
2120
2127
|
const raw = initializer !== null
|
|
2121
|
-
?
|
|
2122
|
-
:
|
|
2123
|
-
const value =
|
|
2128
|
+
? lowerer.lowerExpr(initializer)
|
|
2129
|
+
: lowerer.lowerShorthandValue(prop);
|
|
2130
|
+
const value = lowerer.coerceToExpected(raw, DYN);
|
|
2124
2131
|
if (value.type.kind !== "dyn") {
|
|
2125
|
-
|
|
2132
|
+
lowerer.noLowering(`${what} with a keepAliveTimeoutBuffer value that cannot cross the runtime option boundary`, prop, "keepAliveTimeoutBuffer is a number or undefined");
|
|
2126
2133
|
}
|
|
2127
2134
|
// Preserve the optional field's undefined arm until runtime: Node
|
|
2128
2135
|
// treats it as absent, while numbers take the constructor-only
|
|
@@ -2132,7 +2139,7 @@ function lowerHttpServerOptions(L, node, what) {
|
|
|
2132
2139
|
keepAliveTimeoutBuffer = value;
|
|
2133
2140
|
continue;
|
|
2134
2141
|
}
|
|
2135
|
-
fenceOrDropOptionKey(
|
|
2142
|
+
fenceOrDropOptionKey(lowerer, prop, key, what, HTTP_SERVER_DOCUMENTED_OPTIONS, "requireHostHeader: false, joinDuplicateHeaders, and keepAliveTimeoutBuffer are the supported options");
|
|
2136
2143
|
// An undocumented key, dropped like Node drops it.
|
|
2137
2144
|
}
|
|
2138
2145
|
return { joinDup, keepAliveTimeoutBuffer };
|
|
@@ -2142,10 +2149,10 @@ function lowerHttpServerOptions(L, node, what) {
|
|
|
2142
2149
|
* (the on("request") route), a lone handler, a lone options object, and
|
|
2143
2150
|
* (options, handler). Modeled option values wrap the construction in an
|
|
2144
2151
|
* interned option-setting helper. */
|
|
2145
|
-
function lowerHttpCreateServerForms(
|
|
2152
|
+
function lowerHttpCreateServerForms(lowerer, expr, what, loc) {
|
|
2146
2153
|
const args = expr.arguments ?? [];
|
|
2147
2154
|
if (args.length > 2) {
|
|
2148
|
-
|
|
2155
|
+
lowerer.noLowering(`${what} with ${args.length} arguments`, expr, `the supported forms are ${what}([options][, (req, res) => ...])`);
|
|
2149
2156
|
}
|
|
2150
2157
|
let optsNode = null;
|
|
2151
2158
|
let handlerNode = null;
|
|
@@ -2160,10 +2167,10 @@ function lowerHttpCreateServerForms(L, expr, what, loc) {
|
|
|
2160
2167
|
handlerNode = args[0];
|
|
2161
2168
|
}
|
|
2162
2169
|
const opts = optsNode !== null
|
|
2163
|
-
? lowerHttpServerOptions(
|
|
2170
|
+
? lowerHttpServerOptions(lowerer, optsNode, what)
|
|
2164
2171
|
: { joinDup: false, keepAliveTimeoutBuffer: null };
|
|
2165
2172
|
const server = handlerNode !== null
|
|
2166
|
-
? { kind: "libCall", fn: "http.createServer", args: [lowerRequestHandlerArg(
|
|
2173
|
+
? { kind: "libCall", fn: "http.createServer", args: [lowerRequestHandlerArg(lowerer, handlerNode)], type: NETSERVER_T, loc }
|
|
2167
2174
|
: { kind: "libCall", fn: "http.createServerEmpty", args: [], type: NETSERVER_T, loc };
|
|
2168
2175
|
if (!opts.joinDup && opts.keepAliveTimeoutBuffer === null)
|
|
2169
2176
|
return server;
|
|
@@ -2173,10 +2180,10 @@ function lowerHttpCreateServerForms(L, expr, what, loc) {
|
|
|
2173
2180
|
// the server as the constructor/factory result.
|
|
2174
2181
|
const hasBuffer = opts.keepAliveTimeoutBuffer !== null;
|
|
2175
2182
|
const key = `server.opts:${opts.joinDup ? 1 : 0}:${hasBuffer ? 1 : 0}`;
|
|
2176
|
-
const existing =
|
|
2177
|
-
const name = existing ?? `%server.opts.${
|
|
2183
|
+
const existing = lowerer.arrHofHelpers.get(key);
|
|
2184
|
+
const name = existing ?? `%server.opts.${lowerer.arrHofHelpers.size}`;
|
|
2178
2185
|
if (!existing) {
|
|
2179
|
-
|
|
2186
|
+
lowerer.arrHofHelpers.set(key, name);
|
|
2180
2187
|
const params = [
|
|
2181
2188
|
...(hasBuffer ? [{ localId: "b.0", name: "buffer", type: DYN }] : []),
|
|
2182
2189
|
{ localId: "s.0", name: "s", type: NETSERVER_T },
|
|
@@ -2200,7 +2207,7 @@ function lowerHttpCreateServerForms(L, expr, what, loc) {
|
|
|
2200
2207
|
});
|
|
2201
2208
|
}
|
|
2202
2209
|
body.push({ kind: "return", value: ref, loc });
|
|
2203
|
-
|
|
2210
|
+
lowerer.liftedFns.push({
|
|
2204
2211
|
name,
|
|
2205
2212
|
params,
|
|
2206
2213
|
returnType: NETSERVER_T,
|
|
@@ -2232,37 +2239,35 @@ function lowerHttpCreateServerForms(L, expr, what, loc) {
|
|
|
2232
2239
|
* DROPS (no free pool exists, so selection order cannot observe), other
|
|
2233
2240
|
* documented keys fence by name, and undocumented keys drop like Node
|
|
2234
2241
|
* drops them. Null when the callee isn't the http/https Agent. */
|
|
2235
|
-
export function lowerHttpAgentNew(
|
|
2242
|
+
export function lowerHttpAgentNew(lowerer, expr) {
|
|
2236
2243
|
const callee = expr.expression;
|
|
2237
2244
|
const bi = ts.isPropertyAccessExpression(callee) && callee.name.text === "Agent"
|
|
2238
|
-
?
|
|
2245
|
+
? lowerer.builtinMemberOf(callee)
|
|
2239
2246
|
: ts.isIdentifier(callee)
|
|
2240
|
-
?
|
|
2247
|
+
? lowerer.builtinImportOf(callee)
|
|
2241
2248
|
: null;
|
|
2242
2249
|
if (!bi || bi.member !== "Agent" || (bi.module !== "http" && bi.module !== "https"))
|
|
2243
2250
|
return null;
|
|
2244
2251
|
const loc = locOf(expr);
|
|
2245
2252
|
const api = `new ${bi.module}.Agent`;
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
let
|
|
2249
|
-
let
|
|
2250
|
-
let
|
|
2251
|
-
let
|
|
2252
|
-
let timeout = numLit(-1);
|
|
2253
|
-
let port = numLit(-1);
|
|
2253
|
+
let keepAlive = boolLit(false, loc);
|
|
2254
|
+
let kaMsecs = numLit(-1, loc);
|
|
2255
|
+
let maxSockets = numLit(-1, loc);
|
|
2256
|
+
let maxFree = numLit(-1, loc);
|
|
2257
|
+
let timeout = numLit(-1, loc);
|
|
2258
|
+
let port = numLit(-1, loc);
|
|
2254
2259
|
const args = expr.arguments ?? [];
|
|
2255
2260
|
if (args.length > 1) {
|
|
2256
|
-
|
|
2261
|
+
lowerer.noLowering(`${api} with ${args.length} arguments`, expr, "the supported form is new Agent(options?)");
|
|
2257
2262
|
}
|
|
2258
2263
|
if (args.length === 1) {
|
|
2259
2264
|
const optsNode = stripParensAndCasts(args[0]);
|
|
2260
2265
|
if (!ts.isObjectLiteralExpression(optsNode)) {
|
|
2261
|
-
|
|
2266
|
+
lowerer.noLowering(`${api} with a non-literal options value`, args[0], "spell the options as an object literal at the construction site");
|
|
2262
2267
|
}
|
|
2263
2268
|
for (const prop of optsNode.properties) {
|
|
2264
2269
|
if (ts.isSpreadAssignment(prop)) {
|
|
2265
|
-
|
|
2270
|
+
lowerer.noLowering(`${api} with an options spread`, prop, "write each option inline");
|
|
2266
2271
|
}
|
|
2267
2272
|
let initializer;
|
|
2268
2273
|
if (ts.isPropertyAssignment(prop) &&
|
|
@@ -2273,18 +2278,18 @@ export function lowerHttpAgentNew(L, expr) {
|
|
|
2273
2278
|
initializer = null;
|
|
2274
2279
|
}
|
|
2275
2280
|
else {
|
|
2276
|
-
|
|
2281
|
+
lowerer.noLowering(`${api} options with computed keys`, prop, "each option must be a plain `name: value` (or shorthand) entry with a literal key");
|
|
2277
2282
|
}
|
|
2278
2283
|
const key = prop.name.text;
|
|
2279
2284
|
const lowerVal = (want) => {
|
|
2280
2285
|
const v = initializer !== null
|
|
2281
|
-
?
|
|
2282
|
-
:
|
|
2286
|
+
? lowerer.lowerExpr(initializer)
|
|
2287
|
+
: lowerer.lowerShorthandValue(prop);
|
|
2283
2288
|
if (v.type.kind === "dyn") {
|
|
2284
2289
|
return { kind: "dynCheck", value: v, type: want === "bool" ? BOOL : F64, loc: locOf(prop) };
|
|
2285
2290
|
}
|
|
2286
2291
|
if (v.type.kind !== want) {
|
|
2287
|
-
|
|
2292
|
+
lowerer.noLowering(`a ${api} '${key}' option of '${lowerer.fmt(v.type)}' values`, prop, want === "bool" ? "the option value must be a boolean" : "the option value must be a number");
|
|
2288
2293
|
}
|
|
2289
2294
|
return v;
|
|
2290
2295
|
};
|
|
@@ -2313,46 +2318,46 @@ export function lowerHttpAgentNew(L, expr) {
|
|
|
2313
2318
|
// Free-socket selection order: no free pool exists here (no
|
|
2314
2319
|
// keep-alive reuse), so the choice cannot observe — dropped.
|
|
2315
2320
|
if (initializer !== null)
|
|
2316
|
-
|
|
2321
|
+
lowerer.lowerExpr(initializer); // evaluate, like Node
|
|
2317
2322
|
break;
|
|
2318
2323
|
default:
|
|
2319
|
-
fenceOrDropOptionKey(
|
|
2324
|
+
fenceOrDropOptionKey(lowerer, prop, key, api, AGENT_DOCUMENTED_OPTIONS, "keepAlive, keepAliveMsecs, maxSockets, maxFreeSockets, timeout, port, and scheduling are the supported options");
|
|
2320
2325
|
}
|
|
2321
2326
|
}
|
|
2322
2327
|
}
|
|
2323
2328
|
return {
|
|
2324
2329
|
kind: "libCall",
|
|
2325
2330
|
fn: "http.agentNew",
|
|
2326
|
-
args: [
|
|
2331
|
+
args: [boolLit(bi.module === "https", loc), keepAlive, kaMsecs, maxSockets, maxFree, timeout, port],
|
|
2327
2332
|
type: DYN,
|
|
2328
2333
|
loc,
|
|
2329
2334
|
};
|
|
2330
2335
|
}
|
|
2331
|
-
export function lowerHttpServerNew(
|
|
2336
|
+
export function lowerHttpServerNew(lowerer, expr) {
|
|
2332
2337
|
const callee = expr.expression;
|
|
2333
2338
|
const isHttpServer = (ts.isPropertyAccessExpression(callee) &&
|
|
2334
2339
|
callee.name.text === "Server" &&
|
|
2335
|
-
|
|
2340
|
+
lowerer.builtinMemberOf(callee)?.module === "http") ||
|
|
2336
2341
|
(ts.isIdentifier(callee) &&
|
|
2337
|
-
|
|
2338
|
-
|
|
2342
|
+
lowerer.builtinImportOf(callee)?.module === "http" &&
|
|
2343
|
+
lowerer.builtinImportOf(callee)?.member === "Server");
|
|
2339
2344
|
if (!isHttpServer)
|
|
2340
2345
|
return null;
|
|
2341
|
-
return lowerHttpCreateServerForms(
|
|
2346
|
+
return lowerHttpCreateServerForms(lowerer, expr, "new http.Server", locOf(expr));
|
|
2342
2347
|
}
|
|
2343
2348
|
/** Module-function calls on http import bindings: createServer([options][,
|
|
2344
2349
|
* handler]) and the call-form Server (Node's constructor works without
|
|
2345
2350
|
* `new` — test/parallel's http.Server(fn) spelling). The handler takes
|
|
2346
2351
|
* (req, res), (req), or () — runtime adapters bridge each shape.
|
|
2347
2352
|
* Everything else the module declares fences qualified. */
|
|
2348
|
-
function lowerHttpModuleCall(
|
|
2353
|
+
function lowerHttpModuleCall(lowerer, expr, bi, loc) {
|
|
2349
2354
|
if (bi.member === "createServer" || bi.member === "Server") {
|
|
2350
|
-
return lowerHttpCreateServerForms(
|
|
2355
|
+
return lowerHttpCreateServerForms(lowerer, expr, `http.${bi.member}`, loc);
|
|
2351
2356
|
}
|
|
2352
2357
|
if (bi.member === "request" || bi.member === "get") {
|
|
2353
|
-
return lowerHttpClientCall(
|
|
2358
|
+
return lowerHttpClientCall(lowerer, expr, bi.member, loc);
|
|
2354
2359
|
}
|
|
2355
|
-
|
|
2360
|
+
lowerer.noLowering(`http.${bi.member}`, expr, "createServer, Server, request, and get are the lowered http module functions", lowerer.resolveValueSymbol(expr.expression));
|
|
2356
2361
|
}
|
|
2357
2362
|
/** The cert/key PEM option of tls.createServer / https.createServer: an
|
|
2358
2363
|
* OBJECT LITERAL whose lowered `cert` and `key` entries are strings or
|
|
@@ -2360,9 +2365,9 @@ function lowerHttpModuleCall(L, expr, bi, loc) {
|
|
|
2360
2365
|
* runtime-valued (dyn) entry rides the tls.pemDyn extraction, throwing
|
|
2361
2366
|
* the fence at runtime for non-PEM kinds (the divergence-66 stance).
|
|
2362
2367
|
* Every other key fences by name; SNICallback gets the pointed hint. */
|
|
2363
|
-
function lowerTlsServerOptions(
|
|
2368
|
+
function lowerTlsServerOptions(lowerer, node, what) {
|
|
2364
2369
|
if (!ts.isObjectLiteralExpression(node)) {
|
|
2365
|
-
|
|
2370
|
+
lowerer.noLowering(`${what} with a non-literal options argument`, node, "pass the options as an object literal: { cert, key }");
|
|
2366
2371
|
}
|
|
2367
2372
|
const loc = locOf(node);
|
|
2368
2373
|
let cert = null;
|
|
@@ -2377,7 +2382,7 @@ function lowerTlsServerOptions(L, node, what) {
|
|
|
2377
2382
|
initializer = null;
|
|
2378
2383
|
}
|
|
2379
2384
|
else {
|
|
2380
|
-
|
|
2385
|
+
lowerer.noLowering(`${what} options with computed keys or spreads`, prop, "each option must be a plain `name: value` (or shorthand) entry with a literal key");
|
|
2381
2386
|
}
|
|
2382
2387
|
const k = prop.name.text;
|
|
2383
2388
|
if (k !== "cert" && k !== "key") {
|
|
@@ -2394,15 +2399,15 @@ function lowerTlsServerOptions(L, node, what) {
|
|
|
2394
2399
|
(init === null || init.kind === ts.SyntaxKind.FalseKeyword)) {
|
|
2395
2400
|
continue; // Node's false default, spelled out
|
|
2396
2401
|
}
|
|
2397
|
-
fenceOrDropOptionKey(
|
|
2402
|
+
fenceOrDropOptionKey(lowerer, prop, k, what, TLS_SERVER_DOCUMENTED_OPTIONS, "cert and key (PEM strings or Buffers) are the supported options", {
|
|
2398
2403
|
SNICallback: "SNICallback lowers only on http2.createSecureServer({ allowHTTP1: true, ... }) — serve one cert/key pair here",
|
|
2399
2404
|
requestCert: "client-certificate handshakes are not modeled (SEMANTICS.md divergence 55) — requestCert: false is the lowered value",
|
|
2400
2405
|
});
|
|
2401
2406
|
continue; // an undocumented key, dropped like Node drops it
|
|
2402
2407
|
}
|
|
2403
2408
|
let v = initializer !== null
|
|
2404
|
-
?
|
|
2405
|
-
:
|
|
2409
|
+
? lowerer.lowerExpr(initializer)
|
|
2410
|
+
: lowerer.lowerShorthandValue(prop);
|
|
2406
2411
|
const isArrayOfPem = v.type.kind === "array" &&
|
|
2407
2412
|
(v.type.elem.kind === "string" || (v.type.elem.kind === "bytes" && v.type.elem.elem === "u8"));
|
|
2408
2413
|
if (v.type.kind === "dyn" || isArrayOfPem) {
|
|
@@ -2422,7 +2427,7 @@ function lowerTlsServerOptions(L, node, what) {
|
|
|
2422
2427
|
};
|
|
2423
2428
|
}
|
|
2424
2429
|
else if (v.type.kind !== "string" && !(v.type.kind === "bytes" && v.type.elem === "u8")) {
|
|
2425
|
-
|
|
2430
|
+
lowerer.noLowering(`a ${what} '${k}' option of '${lowerer.fmt(v.type)}' values`, prop, "cert/key are PEM strings or Buffers here");
|
|
2426
2431
|
}
|
|
2427
2432
|
if (k === "cert")
|
|
2428
2433
|
cert = v;
|
|
@@ -2430,7 +2435,7 @@ function lowerTlsServerOptions(L, node, what) {
|
|
|
2430
2435
|
key = v;
|
|
2431
2436
|
}
|
|
2432
2437
|
if (cert === null || key === null) {
|
|
2433
|
-
|
|
2438
|
+
lowerer.noLowering(`${what} without both cert and key`, node, "the supported options object is { cert, key } (PEM strings or Buffers)");
|
|
2434
2439
|
}
|
|
2435
2440
|
return { cert, key };
|
|
2436
2441
|
}
|
|
@@ -2460,12 +2465,12 @@ function tlsLiteralNeedsRuntimeWalk(node, includeHttpTimeoutOptions = false) {
|
|
|
2460
2465
|
((includeHttpTimeoutOptions && p.name.text === "keepAliveTimeoutBuffer") ||
|
|
2461
2466
|
(jsSource && TLS_VALIDATED_OPTIONS.has(p.name.text))));
|
|
2462
2467
|
}
|
|
2463
|
-
function lowerTlsServerOptionsOrDyn(
|
|
2468
|
+
function lowerTlsServerOptionsOrDyn(lowerer, node, what, includeHttpTimeoutOptions = false) {
|
|
2464
2469
|
if (ts.isObjectLiteralExpression(node) &&
|
|
2465
2470
|
!tlsLiteralNeedsRuntimeWalk(node, includeHttpTimeoutOptions)) {
|
|
2466
|
-
return lowerTlsServerOptions(
|
|
2471
|
+
return lowerTlsServerOptions(lowerer, node, what);
|
|
2467
2472
|
}
|
|
2468
|
-
const v =
|
|
2473
|
+
const v = lowerer.lowerExpr(node);
|
|
2469
2474
|
if (v.type.kind === "dyn")
|
|
2470
2475
|
return { dyn: v };
|
|
2471
2476
|
// A typed options RECORD binding (`const options = { key, cert, ... };
|
|
@@ -2474,10 +2479,10 @@ function lowerTlsServerOptionsOrDyn(L, node, what, includeHttpTimeoutOptions = f
|
|
|
2474
2479
|
// stance — members read at runtime, out-of-bounds ones throw the
|
|
2475
2480
|
// catchable fence, undefined/undocumented ones drop). canConvertToDyn
|
|
2476
2481
|
// now folds in the bytes-bearing option records the walker can box.
|
|
2477
|
-
if (canConvertToDyn(v.type, (id) =>
|
|
2482
|
+
if (canConvertToDyn(v.type, (id) => lowerer.shapes.get(id), (id) => lowerer.unions.get(id))) {
|
|
2478
2483
|
return { dyn: { kind: "dynFrom", value: v, type: DYN, loc: locOf(node) } };
|
|
2479
2484
|
}
|
|
2480
|
-
|
|
2485
|
+
lowerer.noLowering(`${what} with a non-literal options argument`, node, "pass the options as an object literal: { cert, key }");
|
|
2481
2486
|
}
|
|
2482
2487
|
/** tls.connect — the TLS client socket. The lowered forms are
|
|
2483
2488
|
* connect(options[, cb]) and connect(port[, host][, options][, cb]) with
|
|
@@ -2487,24 +2492,23 @@ function lowerTlsServerOptionsOrDyn(L, node, what, includeHttpTimeoutOptions = f
|
|
|
2487
2492
|
* fence for other documented members (the divergence-66 stance). The
|
|
2488
2493
|
* callback (and 'secureConnect'/'connect' listeners) fires
|
|
2489
2494
|
* post-handshake — Node's secureConnect timing. */
|
|
2490
|
-
function lowerTlsConnectCall(
|
|
2495
|
+
function lowerTlsConnectCall(lowerer, expr, loc) {
|
|
2491
2496
|
const args = expr.arguments;
|
|
2492
2497
|
const FORMS_HINT = "the supported forms are connect(options[, cb]) and connect(port[, host][, options][, cb]) — options implement port/host/rejectUnauthorized/ca/servername";
|
|
2493
2498
|
if (args.length < 1 || args.length > 4 || args.some(ts.isSpreadElement)) {
|
|
2494
|
-
|
|
2499
|
+
lowerer.noLowering(`tls.connect with ${args.length} arguments`, expr, FORMS_HINT);
|
|
2495
2500
|
}
|
|
2496
2501
|
const isFuncish = (a) => ts.isFunctionExpression(a) || ts.isArrowFunction(a) ||
|
|
2497
|
-
|
|
2502
|
+
lowerer.checker.getCallSignatures(lowerer.typeOf(a)).length > 0;
|
|
2498
2503
|
const isObjectish = (a) => {
|
|
2499
2504
|
if (ts.isObjectLiteralExpression(a))
|
|
2500
2505
|
return true;
|
|
2501
2506
|
if (isFuncish(a) || ts.isStringLiteralLike(a) || ts.isNumericLiteral(a))
|
|
2502
2507
|
return false;
|
|
2503
|
-
const t =
|
|
2508
|
+
const t = lowerer.typeOf(a);
|
|
2504
2509
|
return (t.flags & ts.TypeFlags.Object) !== 0;
|
|
2505
2510
|
};
|
|
2506
|
-
|
|
2507
|
-
let port = num(-1); // -1: the runtime reads options.port
|
|
2511
|
+
let port = numLit(-1, loc); // -1: the runtime reads options.port
|
|
2508
2512
|
let host = { kind: "strLit", value: "", type: STRING, loc }; // "": options.host / localhost
|
|
2509
2513
|
let optsNode = null;
|
|
2510
2514
|
let i = 0;
|
|
@@ -2513,25 +2517,25 @@ function lowerTlsConnectCall(L, expr, loc) {
|
|
|
2513
2517
|
i = 1;
|
|
2514
2518
|
}
|
|
2515
2519
|
else if (isFuncish(args[0])) {
|
|
2516
|
-
|
|
2520
|
+
lowerer.noLowering("tls.connect with a callback-first argument shape", expr, FORMS_HINT);
|
|
2517
2521
|
}
|
|
2518
2522
|
else {
|
|
2519
|
-
const p =
|
|
2523
|
+
const p = lowerer.lowerExpr(args[0]);
|
|
2520
2524
|
if (p.type.kind === "f64")
|
|
2521
2525
|
port = p;
|
|
2522
2526
|
else if (p.type.kind === "dyn")
|
|
2523
2527
|
port = { kind: "dynCheck", value: p, type: F64, loc };
|
|
2524
2528
|
else
|
|
2525
|
-
|
|
2529
|
+
lowerer.noLowering(`tls.connect with a '${lowerer.fmt(p.type)}' port`, args[0], FORMS_HINT);
|
|
2526
2530
|
i = 1;
|
|
2527
2531
|
if (i < args.length && !isFuncish(args[i]) && !isObjectish(args[i])) {
|
|
2528
|
-
const h =
|
|
2532
|
+
const h = lowerer.lowerExpr(args[i]);
|
|
2529
2533
|
if (h.type.kind === "string")
|
|
2530
2534
|
host = h;
|
|
2531
2535
|
else if (h.type.kind === "dyn")
|
|
2532
2536
|
host = { kind: "dynCheck", value: h, type: STRING, loc };
|
|
2533
2537
|
else
|
|
2534
|
-
|
|
2538
|
+
lowerer.noLowering(`tls.connect with a '${lowerer.fmt(h.type)}' host`, args[i], FORMS_HINT);
|
|
2535
2539
|
i++;
|
|
2536
2540
|
}
|
|
2537
2541
|
if (i < args.length && isObjectish(args[i])) {
|
|
@@ -2543,20 +2547,20 @@ function lowerTlsConnectCall(L, expr, loc) {
|
|
|
2543
2547
|
// directly; a typed record that converts boxes into the checked-dynamic tree.
|
|
2544
2548
|
let opts;
|
|
2545
2549
|
if (optsNode !== null) {
|
|
2546
|
-
const o =
|
|
2550
|
+
const o = lowerer.lowerExpr(optsNode);
|
|
2547
2551
|
if (o.type.kind === "dyn") {
|
|
2548
2552
|
opts = o;
|
|
2549
2553
|
}
|
|
2550
|
-
else if (canConvertToDyn(o.type, (id) =>
|
|
2554
|
+
else if (canConvertToDyn(o.type, (id) => lowerer.shapes.get(id), (id) => lowerer.unions.get(id))) {
|
|
2551
2555
|
opts = { kind: "dynFrom", value: o, type: DYN, loc };
|
|
2552
2556
|
}
|
|
2553
2557
|
else {
|
|
2554
|
-
|
|
2558
|
+
lowerer.noLowering(`tls.connect with a '${lowerer.fmt(o.type)}' options record`, optsNode, FORMS_HINT);
|
|
2555
2559
|
}
|
|
2556
2560
|
}
|
|
2557
2561
|
else {
|
|
2558
2562
|
if (port.kind === "numLit" && port.value === -1) {
|
|
2559
|
-
|
|
2563
|
+
lowerer.noLowering("tls.connect without a port or options", expr, FORMS_HINT);
|
|
2560
2564
|
}
|
|
2561
2565
|
// No options: Node's defaults (rejectUnauthorized: true) — the
|
|
2562
2566
|
// runtime walk reads an absent record (the dyn undefined).
|
|
@@ -2570,9 +2574,9 @@ function lowerTlsConnectCall(L, expr, loc) {
|
|
|
2570
2574
|
let cb = null;
|
|
2571
2575
|
if (i < args.length) {
|
|
2572
2576
|
if (i !== args.length - 1 || !isFuncish(args[i])) {
|
|
2573
|
-
|
|
2577
|
+
lowerer.noLowering("tls.connect with this argument shape", expr, FORMS_HINT);
|
|
2574
2578
|
}
|
|
2575
|
-
const r = lowerCallbackArg(
|
|
2579
|
+
const r = lowerCallbackArg(lowerer, args[i], "secureConnect listeners", 0, () => false, "use ()", []);
|
|
2576
2580
|
cb = r.cb;
|
|
2577
2581
|
}
|
|
2578
2582
|
return cb !== null
|
|
@@ -2584,39 +2588,39 @@ function lowerTlsConnectCall(L, expr, loc) {
|
|
|
2584
2588
|
* socket that behaves exactly like a net socket. Everything else the
|
|
2585
2589
|
* module declares fences qualified (createSecureContext and connect
|
|
2586
2590
|
* carry pointed hints from the fence-hint table). */
|
|
2587
|
-
function lowerTlsModuleCall(
|
|
2591
|
+
function lowerTlsModuleCall(lowerer, expr, bi, loc) {
|
|
2588
2592
|
const args = expr.arguments;
|
|
2589
2593
|
if (bi.member === "createServer") {
|
|
2590
2594
|
if (args.length < 1 || args.length > 2) {
|
|
2591
|
-
|
|
2595
|
+
lowerer.noLowering(`createServer with ${args.length} arguments`, expr, "the supported form is createServer({ cert, key }, socket => ...)");
|
|
2592
2596
|
}
|
|
2593
|
-
const opts = lowerTlsServerOptionsOrDyn(
|
|
2597
|
+
const opts = lowerTlsServerOptionsOrDyn(lowerer, args[0], "tls.createServer");
|
|
2594
2598
|
if (args.length === 1) {
|
|
2595
2599
|
return opts.dyn !== undefined
|
|
2596
2600
|
? { kind: "libCall", fn: "tls.createServerDyn", args: [opts.dyn], type: NETSERVER_T, loc }
|
|
2597
2601
|
: { kind: "libCall", fn: "tls.createServer", args: [opts.cert, opts.key], type: NETSERVER_T, loc };
|
|
2598
2602
|
}
|
|
2599
|
-
const { cb } = lowerCallbackArg(
|
|
2603
|
+
const { cb } = lowerCallbackArg(lowerer, args[1], "secureConnection listeners", 1, (p) => p.kind === "netSocket", "use (socket) or ()", [NETSOCKET_T]);
|
|
2600
2604
|
return opts.dyn !== undefined
|
|
2601
2605
|
? { kind: "libCall", fn: "tls.createServerDynCb", args: [opts.dyn, cb], type: NETSERVER_T, loc }
|
|
2602
2606
|
: { kind: "libCall", fn: "tls.createServerCb", args: [opts.cert, opts.key, cb], type: NETSERVER_T, loc };
|
|
2603
2607
|
}
|
|
2604
2608
|
if (bi.member === "connect") {
|
|
2605
|
-
return lowerTlsConnectCall(
|
|
2609
|
+
return lowerTlsConnectCall(lowerer, expr, loc);
|
|
2606
2610
|
}
|
|
2607
2611
|
if (bi.member === "getCACertificates" && args.length === 1 && !args.some(ts.isSpreadElement) &&
|
|
2608
2612
|
isJsSourceFile(expr.getSourceFile())) {
|
|
2609
2613
|
// The type-argument ladder (validateString + the documented name
|
|
2610
2614
|
// set); the real CA list has no lowering, so a valid name meets the
|
|
2611
2615
|
// compiler-rendered fence after the validation.
|
|
2612
|
-
const raw =
|
|
2613
|
-
if (raw.type.kind === "dyn" || raw.kind === "unitLit" ||
|
|
2616
|
+
const raw = lowerer.lowerExpr(args[0]);
|
|
2617
|
+
if (raw.type.kind === "dyn" || raw.kind === "unitLit" || lowerer.dynConvertible(raw.type)) {
|
|
2614
2618
|
const t = raw.type.kind === "dyn" ? raw : { kind: "dynFrom", value: raw, type: DYN, loc };
|
|
2615
2619
|
return {
|
|
2616
2620
|
kind: "libCall",
|
|
2617
2621
|
fn: "tls.caCertsChk",
|
|
2618
|
-
args: [t, ladderFenceExpr(
|
|
2619
|
-
type:
|
|
2622
|
+
args: [t, ladderFenceExpr(lowerer, "tls.getCACertificates", expr)],
|
|
2623
|
+
type: lowerer.mapTypeOf(lowerer.typeOf(expr)) ?? DYN,
|
|
2620
2624
|
loc,
|
|
2621
2625
|
};
|
|
2622
2626
|
}
|
|
@@ -2627,9 +2631,9 @@ function lowerTlsModuleCall(L, expr, bi, loc) {
|
|
|
2627
2631
|
// cert/key pair (PEM strings or Buffers — the createServer options
|
|
2628
2632
|
// walk, reused); ca/ciphers/etc. fence by name there.
|
|
2629
2633
|
if (args.length !== 1) {
|
|
2630
|
-
|
|
2634
|
+
lowerer.noLowering(`createSecureContext with ${args.length} arguments`, expr, "the supported form is createSecureContext({ cert, key })");
|
|
2631
2635
|
}
|
|
2632
|
-
const opts = lowerTlsServerOptionsOrDyn(
|
|
2636
|
+
const opts = lowerTlsServerOptionsOrDyn(lowerer, args[0], "tls.createSecureContext");
|
|
2633
2637
|
if (opts.dyn !== undefined) {
|
|
2634
2638
|
return { kind: "libCall", fn: "tls.createSecureContextDyn", args: [opts.dyn], type: SECURECTX_T, loc };
|
|
2635
2639
|
}
|
|
@@ -2644,49 +2648,49 @@ function lowerTlsModuleCall(L, expr, bi, loc) {
|
|
|
2644
2648
|
// lowerTlsRootCertificates below.
|
|
2645
2649
|
if (bi.member === "getCACertificates") {
|
|
2646
2650
|
if (args.length > 1) {
|
|
2647
|
-
|
|
2651
|
+
lowerer.noLowering(`getCACertificates with ${args.length} arguments`, expr);
|
|
2648
2652
|
}
|
|
2649
2653
|
const typeArg = args.length === 1
|
|
2650
|
-
?
|
|
2654
|
+
? lowerer.lowerExprExpecting(args[0], STRING)
|
|
2651
2655
|
: { kind: "strLit", value: "default", type: STRING, loc };
|
|
2652
2656
|
return { kind: "libCall", fn: "tlsca.get", args: [typeArg], type: arrayOf(STRING), loc };
|
|
2653
2657
|
}
|
|
2654
2658
|
if (bi.member === "setDefaultCACertificates") {
|
|
2655
2659
|
if (args.length !== 1) {
|
|
2656
|
-
|
|
2660
|
+
lowerer.noLowering(`setDefaultCACertificates with ${args.length} arguments`, expr);
|
|
2657
2661
|
}
|
|
2658
|
-
const certs =
|
|
2662
|
+
const certs = lowerer.lowerExpr(args[0]);
|
|
2659
2663
|
if (certs.type.kind !== "array" || certs.type.elem.kind !== "string") {
|
|
2660
|
-
|
|
2664
|
+
lowerer.noLowering(`setDefaultCACertificates of a '${lowerer.fmt(certs.type)}' value`, args[0], "a string[] of PEM certificates is the lowered shape — decode Buffer/typed-array entries to strings first");
|
|
2661
2665
|
}
|
|
2662
2666
|
return { kind: "libCall", fn: "tlsca.set", args: [certs], type: VOID, loc };
|
|
2663
2667
|
}
|
|
2664
|
-
|
|
2665
|
-
"createServer({ cert, key }, handler) and createSecureContext({ cert, key }) are the lowered tls module functions",
|
|
2668
|
+
lowerer.noLowering(`tls.${bi.member}`, expr, builtinFenceHintOf("tls", bi.member) ??
|
|
2669
|
+
"createServer({ cert, key }, handler) and createSecureContext({ cert, key }) are the lowered tls module functions", lowerer.resolveValueSymbol(expr.expression));
|
|
2666
2670
|
}
|
|
2667
2671
|
/** The tls.rootCertificates VALUE read (any import spelling): the same
|
|
2668
2672
|
* cached array getCACertificates("bundled") answers — Node's own
|
|
2669
2673
|
* equality (test-tls-get-ca-certificates-bundled pins certs ===
|
|
2670
2674
|
* rootCertificates). Null for other members (the property chains keep
|
|
2671
2675
|
* trying). */
|
|
2672
|
-
export function lowerTlsRootCertificates(
|
|
2676
|
+
export function lowerTlsRootCertificates(lowerer, bi, loc) {
|
|
2673
2677
|
if (bi.module !== "tls" || bi.member !== "rootCertificates")
|
|
2674
2678
|
return null;
|
|
2675
2679
|
return { kind: "libCall", fn: "tlsca.root", args: [], type: arrayOf(STRING), loc };
|
|
2676
2680
|
}
|
|
2677
2681
|
const httpClientFnBindings = new WeakMap();
|
|
2678
|
-
export function httpClientFnBindingOf(
|
|
2679
|
-
return httpClientFnBindings.get(
|
|
2682
|
+
export function httpClientFnBindingOf(lowerer, sym) {
|
|
2683
|
+
return httpClientFnBindings.get(lowerer)?.get(sym);
|
|
2680
2684
|
}
|
|
2681
2685
|
/** The { module, member } of an http/https client-function REFERENCE
|
|
2682
2686
|
* (`https.request` through a namespace import, or a named `request`
|
|
2683
2687
|
* import binding) — null for anything else. */
|
|
2684
|
-
function clientFnRefOf(
|
|
2688
|
+
function clientFnRefOf(lowerer, node) {
|
|
2685
2689
|
const e = node;
|
|
2686
2690
|
const bi = ts.isPropertyAccessExpression(e)
|
|
2687
|
-
?
|
|
2691
|
+
? lowerer.builtinMemberOf(e)
|
|
2688
2692
|
: ts.isIdentifier(e)
|
|
2689
|
-
?
|
|
2693
|
+
? lowerer.builtinImportOf(e)
|
|
2690
2694
|
: null;
|
|
2691
2695
|
if (!bi)
|
|
2692
2696
|
return null;
|
|
@@ -2698,8 +2702,8 @@ function clientFnRefOf(L, node) {
|
|
|
2698
2702
|
/** True when `sym`'s binding is never written after initialization: a
|
|
2699
2703
|
* `const` declaration, or a parameter/let whose enclosing scope contains
|
|
2700
2704
|
* no assignment or ++/-- targeting it. */
|
|
2701
|
-
function neverReassigned(
|
|
2702
|
-
const decl =
|
|
2705
|
+
function neverReassigned(lowerer, sym) {
|
|
2706
|
+
const decl = lowerer.checker.declarationsOf(sym)[0];
|
|
2703
2707
|
if (!decl)
|
|
2704
2708
|
return false;
|
|
2705
2709
|
if (ts.isVariableDeclaration(decl) && (ts.getCombinedNodeFlags(decl) & ts.NodeFlags.Const) !== 0) {
|
|
@@ -2716,7 +2720,7 @@ function neverReassigned(L, sym) {
|
|
|
2716
2720
|
const root = ts.isFunctionLike(scope.parent) ? scope.parent : scope.parent;
|
|
2717
2721
|
let written = false;
|
|
2718
2722
|
const hitsSym = (n) => {
|
|
2719
|
-
if (ts.isIdentifier(n) &&
|
|
2723
|
+
if (ts.isIdentifier(n) && lowerer.checker.getSymbolAtLocation(n) === sym)
|
|
2720
2724
|
return true;
|
|
2721
2725
|
let hit = false;
|
|
2722
2726
|
ts.forEachChild(n, (c) => {
|
|
@@ -2739,7 +2743,7 @@ function neverReassigned(L, sym) {
|
|
|
2739
2743
|
}
|
|
2740
2744
|
if ((ts.isPrefixUnaryExpression(n) || ts.isPostfixUnaryExpression(n)) &&
|
|
2741
2745
|
(n.operator === ts.SyntaxKind.PlusPlusToken || n.operator === ts.SyntaxKind.MinusMinusToken) &&
|
|
2742
|
-
ts.isIdentifier(n.operand) &&
|
|
2746
|
+
ts.isIdentifier(n.operand) && lowerer.checker.getSymbolAtLocation(n.operand) === sym) {
|
|
2743
2747
|
written = true;
|
|
2744
2748
|
return;
|
|
2745
2749
|
}
|
|
@@ -2754,7 +2758,7 @@ function neverReassigned(L, sym) {
|
|
|
2754
2758
|
* nothing) and answers true. A matched ternary whose condition is not a
|
|
2755
2759
|
* stable identifier fences pointedly; non-client ternaries answer false
|
|
2756
2760
|
* (the ordinary decl path and its per-arm fences apply). */
|
|
2757
|
-
export function registerHttpClientFnBinding(
|
|
2761
|
+
export function registerHttpClientFnBinding(lowerer, nameNode, init) {
|
|
2758
2762
|
if (!init)
|
|
2759
2763
|
return false;
|
|
2760
2764
|
let e = init;
|
|
@@ -2768,24 +2772,24 @@ export function registerHttpClientFnBinding(L, nameNode, init) {
|
|
|
2768
2772
|
x = x.expression;
|
|
2769
2773
|
return x;
|
|
2770
2774
|
};
|
|
2771
|
-
const t = clientFnRefOf(
|
|
2772
|
-
const f = clientFnRefOf(
|
|
2775
|
+
const t = clientFnRefOf(lowerer, unwrap(e.whenTrue));
|
|
2776
|
+
const f = clientFnRefOf(lowerer, unwrap(e.whenFalse));
|
|
2773
2777
|
if (!t || !f)
|
|
2774
2778
|
return false;
|
|
2775
2779
|
if (t.member !== f.member || t.module === f.module) {
|
|
2776
|
-
|
|
2780
|
+
lowerer.noLowering("a conditional between these two client functions", e, "the lowered shape is `cond ? https.request : http.request` (or the .get pair, either order) — the same member of both modules");
|
|
2777
2781
|
}
|
|
2778
2782
|
const cond = unwrap(e.condition);
|
|
2779
|
-
const condSym = ts.isIdentifier(cond) ?
|
|
2780
|
-
if (!condSym || !neverReassigned(
|
|
2781
|
-
|
|
2783
|
+
const condSym = ts.isIdentifier(cond) ? lowerer.resolveValueSymbol(cond) : null;
|
|
2784
|
+
if (!condSym || !neverReassigned(lowerer, condSym)) {
|
|
2785
|
+
lowerer.noLowering("a client-function conditional with this condition shape", e.condition, "the condition must be a bare identifier whose binding is never reassigned (it re-evaluates at each call through the binding)");
|
|
2782
2786
|
}
|
|
2783
|
-
const symbol =
|
|
2787
|
+
const symbol = lowerer.checker.getSymbolAtLocation(nameNode);
|
|
2784
2788
|
if (symbol) {
|
|
2785
|
-
let map = httpClientFnBindings.get(
|
|
2789
|
+
let map = httpClientFnBindings.get(lowerer);
|
|
2786
2790
|
if (!map) {
|
|
2787
2791
|
map = new Map();
|
|
2788
|
-
httpClientFnBindings.set(
|
|
2792
|
+
httpClientFnBindings.set(lowerer, map);
|
|
2789
2793
|
}
|
|
2790
2794
|
map.set(symbol, { cond, trueSecure: t.module === "https", member: t.member });
|
|
2791
2795
|
}
|
|
@@ -2793,19 +2797,19 @@ export function registerHttpClientFnBinding(L, nameNode, init) {
|
|
|
2793
2797
|
}
|
|
2794
2798
|
/** A call THROUGH a registered requestFn binding: the http client
|
|
2795
2799
|
* lowering with the RUNTIME-secure extras (the binding mode). */
|
|
2796
|
-
export function lowerHttpClientFnCall(
|
|
2797
|
-
return lowerHttpClientCall(
|
|
2800
|
+
export function lowerHttpClientFnCall(lowerer, expr, binding, loc) {
|
|
2801
|
+
return lowerHttpClientCall(lowerer, expr, binding.member, loc, binding);
|
|
2798
2802
|
}
|
|
2799
2803
|
/** Module-function calls on https import bindings: createServer(options,
|
|
2800
2804
|
* handler) and request/get — the http client lowering with the secure
|
|
2801
2805
|
* extras (port 443 default, rejectUnauthorized, ca). */
|
|
2802
|
-
function lowerHttpsModuleCall(
|
|
2806
|
+
function lowerHttpsModuleCall(lowerer, expr, bi, loc) {
|
|
2803
2807
|
const args = expr.arguments;
|
|
2804
2808
|
if (bi.member === "createServer") {
|
|
2805
2809
|
if (args.length < 1 || args.length > 2) {
|
|
2806
|
-
|
|
2810
|
+
lowerer.noLowering(`createServer with ${args.length} arguments`, expr, "the supported form is createServer({ cert, key }, (req, res) => ...)");
|
|
2807
2811
|
}
|
|
2808
|
-
const opts = lowerTlsServerOptionsOrDyn(
|
|
2812
|
+
const opts = lowerTlsServerOptionsOrDyn(lowerer, args[0], "https.createServer", true);
|
|
2809
2813
|
if (args.length === 1) {
|
|
2810
2814
|
// The handler-less form: 'request' listeners arrive later via
|
|
2811
2815
|
// server.on("request", ...) — the createSecureServer emission's
|
|
@@ -2814,17 +2818,17 @@ function lowerHttpsModuleCall(L, expr, bi, loc) {
|
|
|
2814
2818
|
if (opts.dyn !== undefined) {
|
|
2815
2819
|
return { kind: "libCall", fn: "https.createServerDyn", args: [opts.dyn], type: NETSERVER_T, loc };
|
|
2816
2820
|
}
|
|
2817
|
-
|
|
2821
|
+
lowerer.noLowering("createServer with 1 arguments", expr, "the supported form is createServer({ cert, key }, (req, res) => ...)");
|
|
2818
2822
|
}
|
|
2819
|
-
const cb = lowerRequestHandlerArg(
|
|
2823
|
+
const cb = lowerRequestHandlerArg(lowerer, args[1]);
|
|
2820
2824
|
return opts.dyn !== undefined
|
|
2821
2825
|
? { kind: "libCall", fn: "https.createServerDynCb", args: [opts.dyn, cb], type: NETSERVER_T, loc }
|
|
2822
2826
|
: { kind: "libCall", fn: "https.createServer", args: [opts.cert, opts.key, cb], type: NETSERVER_T, loc };
|
|
2823
2827
|
}
|
|
2824
2828
|
if (bi.member === "request" || bi.member === "get") {
|
|
2825
|
-
return lowerHttpClientCall(
|
|
2829
|
+
return lowerHttpClientCall(lowerer, expr, bi.member, loc, true);
|
|
2826
2830
|
}
|
|
2827
|
-
|
|
2831
|
+
lowerer.noLowering(`https.${bi.member}`, expr, "createServer, request, and get are the lowered https module functions", lowerer.resolveValueSymbol(expr.expression));
|
|
2828
2832
|
}
|
|
2829
2833
|
/** The h2 SESSION-tuning options of http2.createSecureServer. This slice
|
|
2830
2834
|
* runs most at their defaults, so they are accepted and ignored only with
|
|
@@ -2862,7 +2866,7 @@ function stripParensAndCasts(node) {
|
|
|
2862
2866
|
/** Module-function calls on http2 import bindings. createServer lowers h2c;
|
|
2863
2867
|
* createSecureServer lowers h2 over TLS, with allowHTTP1 selecting one
|
|
2864
2868
|
* dual-ALPN server whose request/connect listeners mirror across parsers. */
|
|
2865
|
-
function lowerHttp2ModuleCall(
|
|
2869
|
+
function lowerHttp2ModuleCall(lowerer, expr, bi, loc) {
|
|
2866
2870
|
const args = expr.arguments;
|
|
2867
2871
|
if (bi.member === "createServer") {
|
|
2868
2872
|
// The REAL h2c server (scr_http2.c). An options object literal is
|
|
@@ -2882,8 +2886,8 @@ function lowerHttp2ModuleCall(L, expr, bi, loc) {
|
|
|
2882
2886
|
// anything else is a tuning-options VALUE — accepted and never
|
|
2883
2887
|
// evaluated (h2 knobs this core runs at defaults; the options
|
|
2884
2888
|
// expression is a pure read in every suite shape).
|
|
2885
|
-
const t =
|
|
2886
|
-
if (
|
|
2889
|
+
const t = lowerer.typeOf(a);
|
|
2890
|
+
if (lowerer.checker.getCallSignatures(t).length > 0 ||
|
|
2887
2891
|
(t.flags & (ts.TypeFlags.Any | ts.TypeFlags.Unknown)) !== 0) {
|
|
2888
2892
|
handlerNode = a;
|
|
2889
2893
|
}
|
|
@@ -2895,18 +2899,18 @@ function lowerHttp2ModuleCall(L, expr, bi, loc) {
|
|
|
2895
2899
|
// becomes the first 'request' listener (Node's exact route), and the
|
|
2896
2900
|
// pair it receives ARE the http req/res handles — Http2ServerRequest/
|
|
2897
2901
|
// Http2ServerResponse over h2 streams (scr_http2.c's compat layer).
|
|
2898
|
-
const cb = lowerRequestHandlerArg(
|
|
2902
|
+
const cb = lowerRequestHandlerArg(lowerer, handlerNode);
|
|
2899
2903
|
return { kind: "libCall", fn: "http2.createServerReq", args: [cb], type: NETSERVER_T, loc };
|
|
2900
2904
|
}
|
|
2901
2905
|
if (bi.member === "connect") {
|
|
2902
2906
|
// http2.connect(authority[, listener]) — the h2c client. The
|
|
2903
2907
|
// authority is a string (URL objects and an options record fence).
|
|
2904
2908
|
if (args.length < 1 || args.length > 3) {
|
|
2905
|
-
|
|
2909
|
+
lowerer.noLowering(`http2.connect with ${args.length} arguments`, expr, "the supported form is connect(authority[, options][, listener]) with a string authority");
|
|
2906
2910
|
}
|
|
2907
|
-
const auth =
|
|
2911
|
+
const auth = lowerer.lowerExpr(args[0]);
|
|
2908
2912
|
if (auth.type.kind !== "string") {
|
|
2909
|
-
|
|
2913
|
+
lowerer.noLowering(`http2.connect with a '${lowerer.fmt(auth.type)}' authority`, args[0], "the authority is a string here (\"http://host:port\")");
|
|
2910
2914
|
}
|
|
2911
2915
|
// connect(authority, options[, listener]): session-tuning options
|
|
2912
2916
|
// are accepted and never evaluated (the core runs at defaults) —
|
|
@@ -2922,9 +2926,9 @@ function lowerHttp2ModuleCall(L, expr, bi, loc) {
|
|
|
2922
2926
|
let reject = { kind: "boolLit", value: true, type: BOOL, loc };
|
|
2923
2927
|
let ca = { kind: "strLit", value: "", type: STRING, loc };
|
|
2924
2928
|
const arg1IsDyn = args.length >= 2 &&
|
|
2925
|
-
(
|
|
2929
|
+
(lowerer.typeOf(args[1]).flags & (ts.TypeFlags.Any | ts.TypeFlags.Unknown)) !== 0;
|
|
2926
2930
|
if (args.length >= 2 && !arg1IsDyn &&
|
|
2927
|
-
|
|
2931
|
+
lowerer.checker.getCallSignatures(lowerer.typeOf(args[1])).length === 0) {
|
|
2928
2932
|
if (ts.isObjectLiteralExpression(args[1])) {
|
|
2929
2933
|
for (const pr of args[1].properties) {
|
|
2930
2934
|
// Plain `name: value` and shorthand `{ ca }` both carry the
|
|
@@ -2943,18 +2947,18 @@ function lowerHttp2ModuleCall(L, expr, bi, loc) {
|
|
|
2943
2947
|
continue;
|
|
2944
2948
|
}
|
|
2945
2949
|
if (name === "createConnection") {
|
|
2946
|
-
|
|
2950
|
+
lowerer.noLowering("http2.connect with a createConnection option", args[1], "custom transports are not modeled — connect dials TCP itself");
|
|
2947
2951
|
}
|
|
2948
2952
|
if (name === "rejectUnauthorized") {
|
|
2949
2953
|
if (valueNode.kind === ts.SyntaxKind.FalseKeyword) {
|
|
2950
2954
|
reject = { kind: "boolLit", value: false, type: BOOL, loc };
|
|
2951
2955
|
}
|
|
2952
2956
|
else if (valueNode.kind !== ts.SyntaxKind.TrueKeyword) {
|
|
2953
|
-
|
|
2957
|
+
lowerer.noLowering("http2.connect with a non-literal rejectUnauthorized option", pr, "spell it true or false — it gates the https authority's certificate verification");
|
|
2954
2958
|
}
|
|
2955
2959
|
}
|
|
2956
2960
|
if (name === "ca") {
|
|
2957
|
-
const v =
|
|
2961
|
+
const v = lowerer.lowerExpr(valueNode);
|
|
2958
2962
|
if (v.type.kind === "string" || (v.type.kind === "bytes" && v.type.elem === "u8")) {
|
|
2959
2963
|
ca = v;
|
|
2960
2964
|
}
|
|
@@ -2968,7 +2972,7 @@ function lowerHttp2ModuleCall(L, expr, bi, loc) {
|
|
|
2968
2972
|
};
|
|
2969
2973
|
}
|
|
2970
2974
|
else {
|
|
2971
|
-
|
|
2975
|
+
lowerer.noLowering(`an http2.connect 'ca' option of '${lowerer.fmt(v.type)}' values`, pr, "ca is a PEM string or Buffer here");
|
|
2972
2976
|
}
|
|
2973
2977
|
}
|
|
2974
2978
|
}
|
|
@@ -2981,7 +2985,7 @@ function lowerHttp2ModuleCall(L, expr, bi, loc) {
|
|
|
2981
2985
|
if (listenerNode !== undefined) {
|
|
2982
2986
|
// The listener is the 'connect' once-listener: (session, socket) or
|
|
2983
2987
|
// fewer. Its result is ignored (voidized); handles pass by ref.
|
|
2984
|
-
const { cb } = lowerCallbackArg(
|
|
2988
|
+
const { cb } = lowerCallbackArg(lowerer, listenerNode, "connect listeners", 2, (p) => p.kind === "http2Session" || p.kind === "netSocket", "use (session, socket), (session), or ()", [HTTP2SESSION_T, NETSOCKET_T]);
|
|
2985
2989
|
return { kind: "libCall", fn: "http2.connectCb", args: [auth, reject, ca, cb], type: HTTP2SESSION_T, loc };
|
|
2986
2990
|
}
|
|
2987
2991
|
return { kind: "libCall", fn: "http2.connect", args: [auth, reject, ca], type: HTTP2SESSION_T, loc };
|
|
@@ -2993,7 +2997,7 @@ function lowerHttp2ModuleCall(L, expr, bi, loc) {
|
|
|
2993
2997
|
}
|
|
2994
2998
|
if (bi.member === "createSecureServer") {
|
|
2995
2999
|
if (args.length < 1 || args.length > 2) {
|
|
2996
|
-
|
|
3000
|
+
lowerer.noLowering(`createSecureServer with ${args.length} arguments`, expr, "the supported forms are createSecureServer(options[, onRequestHandler]) with options { allowHTTP1?, cert, key }");
|
|
2997
3001
|
}
|
|
2998
3002
|
const optsNode = args[0];
|
|
2999
3003
|
// The eager (req, res) handler — Node routes it as the first
|
|
@@ -3007,21 +3011,21 @@ function lowerHttp2ModuleCall(L, expr, bi, loc) {
|
|
|
3007
3011
|
// at runtime, out-of-bounds TLS members throw the catchable
|
|
3008
3012
|
// runtime fence, h2 session-tuning keys drop exactly like the
|
|
3009
3013
|
// literal walk ignores them.
|
|
3010
|
-
const v =
|
|
3014
|
+
const v = lowerer.lowerExpr(optsNode);
|
|
3011
3015
|
let dynOpts;
|
|
3012
3016
|
if (v.type.kind === "dyn") {
|
|
3013
3017
|
dynOpts = v;
|
|
3014
3018
|
}
|
|
3015
|
-
else if (canConvertToDyn(v.type, (id) =>
|
|
3019
|
+
else if (canConvertToDyn(v.type, (id) => lowerer.shapes.get(id), (id) => lowerer.unions.get(id))) {
|
|
3016
3020
|
dynOpts = { kind: "dynFrom", value: v, type: DYN, loc: locOf(optsNode) };
|
|
3017
3021
|
}
|
|
3018
3022
|
else {
|
|
3019
|
-
|
|
3023
|
+
lowerer.noLowering("createSecureServer with a non-literal options argument", optsNode, "pass the options as an object literal ({ allowHTTP1: true, cert, key }) or a runtime record of those members");
|
|
3020
3024
|
}
|
|
3021
3025
|
if (handlerNode === null) {
|
|
3022
3026
|
return { kind: "libCall", fn: "http2.createSecureServerDyn", args: [dynOpts], type: NETSERVER_T, loc };
|
|
3023
3027
|
}
|
|
3024
|
-
const cb = lowerRequestHandlerArg(
|
|
3028
|
+
const cb = lowerRequestHandlerArg(lowerer, handlerNode);
|
|
3025
3029
|
return { kind: "libCall", fn: "http2.createSecureServerDynCb", args: [dynOpts, cb], type: NETSERVER_T, loc };
|
|
3026
3030
|
}
|
|
3027
3031
|
let cert = null;
|
|
@@ -3041,7 +3045,7 @@ function lowerHttp2ModuleCall(L, expr, bi, loc) {
|
|
|
3041
3045
|
const cbT = t.params[1];
|
|
3042
3046
|
if (cbT.kind !== "func" || cbT.ret.kind !== "void" || cbT.params.length > 2)
|
|
3043
3047
|
return false;
|
|
3044
|
-
const armKinds = (u) => u.kind === "union" ? (
|
|
3048
|
+
const armKinds = (u) => u.kind === "union" ? (lowerer.unions.get(u.unionId)?.arms ?? []).map((a) => a.kind) : [];
|
|
3045
3049
|
const p0 = cbT.params[0];
|
|
3046
3050
|
if (p0 !== undefined && !armKinds(p0).includes("nullT"))
|
|
3047
3051
|
return false;
|
|
@@ -3051,17 +3055,17 @@ function lowerHttp2ModuleCall(L, expr, bi, loc) {
|
|
|
3051
3055
|
return true;
|
|
3052
3056
|
};
|
|
3053
3057
|
const lowerSniValue = (node, blame) => {
|
|
3054
|
-
const v =
|
|
3058
|
+
const v = lowerer.lowerExpr(node);
|
|
3055
3059
|
const funcArm = v.type.kind === "func"
|
|
3056
3060
|
? v.type
|
|
3057
3061
|
: v.type.kind === "union"
|
|
3058
|
-
? (
|
|
3062
|
+
? (lowerer.unions.get(v.type.unionId)?.arms ?? []).find((a) => a.kind === "func")
|
|
3059
3063
|
: undefined;
|
|
3060
3064
|
const unionOk = v.type.kind !== "union" ||
|
|
3061
|
-
((
|
|
3062
|
-
(
|
|
3065
|
+
((lowerer.unions.get(v.type.unionId)?.arms ?? []).length === 2 &&
|
|
3066
|
+
(lowerer.unions.get(v.type.unionId)?.arms ?? []).some((a) => a.kind === "undefinedT"));
|
|
3063
3067
|
if (funcArm === undefined || !unionOk || !sniShapeOk(funcArm)) {
|
|
3064
|
-
|
|
3068
|
+
lowerer.noLowering(`a createSecureServer SNICallback of '${lowerer.fmt(v.type)}' values`, blame, SNI_HINT);
|
|
3065
3069
|
}
|
|
3066
3070
|
sni = v;
|
|
3067
3071
|
};
|
|
@@ -3093,13 +3097,13 @@ function lowerHttp2ModuleCall(L, expr, bi, loc) {
|
|
|
3093
3097
|
whenTrue.properties[0].name.text === "SNICallback" &&
|
|
3094
3098
|
whenTrue.properties[0].initializer.getText() === condText;
|
|
3095
3099
|
if (!ok) {
|
|
3096
|
-
|
|
3100
|
+
lowerer.noLowering("createSecureServer options with a conditional spread", prop, "the lowered conditional spread is exactly ...(x ? { SNICallback: x } : {}) — the included value must BE the condition");
|
|
3097
3101
|
}
|
|
3098
3102
|
lowerSniValue(inner.condition, prop);
|
|
3099
3103
|
return;
|
|
3100
3104
|
}
|
|
3101
3105
|
if (!ts.isObjectLiteralExpression(inner)) {
|
|
3102
|
-
|
|
3106
|
+
lowerer.noLowering("createSecureServer options with a computed spread", prop, `spreads must be inline object literals here; ${SNI_HINT}`);
|
|
3103
3107
|
}
|
|
3104
3108
|
for (const p of inner.properties)
|
|
3105
3109
|
lowerOption(p);
|
|
@@ -3114,7 +3118,7 @@ function lowerHttp2ModuleCall(L, expr, bi, loc) {
|
|
|
3114
3118
|
initializer = null;
|
|
3115
3119
|
}
|
|
3116
3120
|
else {
|
|
3117
|
-
|
|
3121
|
+
lowerer.noLowering("createSecureServer options with computed keys", prop, "each option must be a plain `name: value` (or shorthand) entry with a literal key");
|
|
3118
3122
|
}
|
|
3119
3123
|
const k = prop.name.text;
|
|
3120
3124
|
if (k === "allowHTTP1") {
|
|
@@ -3124,15 +3128,15 @@ function lowerHttp2ModuleCall(L, expr, bi, loc) {
|
|
|
3124
3128
|
if (initializer !== null && initializer.kind === ts.SyntaxKind.FalseKeyword)
|
|
3125
3129
|
return;
|
|
3126
3130
|
if (initializer === null || initializer.kind !== ts.SyntaxKind.TrueKeyword) {
|
|
3127
|
-
|
|
3131
|
+
lowerer.noLowering("createSecureServer with a non-literal allowHTTP1 option", prop, "allowHTTP1 picks the protocol stack (true: dual h2/http1; false/absent: ALPN=h2 only) — spell it as a literal");
|
|
3128
3132
|
}
|
|
3129
3133
|
allowHttp1 = true;
|
|
3130
3134
|
return;
|
|
3131
3135
|
}
|
|
3132
3136
|
if (k === "cert" || k === "key") {
|
|
3133
3137
|
let v = initializer !== null
|
|
3134
|
-
?
|
|
3135
|
-
:
|
|
3138
|
+
? lowerer.lowerExpr(initializer)
|
|
3139
|
+
: lowerer.lowerShorthandValue(prop);
|
|
3136
3140
|
if (v.type.kind === "dyn") {
|
|
3137
3141
|
// A runtime PEM value (fixtures.readKey(...)): the tls walk's
|
|
3138
3142
|
// runtime extraction, same fences at runtime.
|
|
@@ -3145,7 +3149,7 @@ function lowerHttp2ModuleCall(L, expr, bi, loc) {
|
|
|
3145
3149
|
};
|
|
3146
3150
|
}
|
|
3147
3151
|
else if (v.type.kind !== "string" && !(v.type.kind === "bytes" && v.type.elem === "u8")) {
|
|
3148
|
-
|
|
3152
|
+
lowerer.noLowering(`a createSecureServer '${k}' option of '${lowerer.fmt(v.type)}' values`, prop, "cert/key are PEM strings or Buffers here");
|
|
3149
3153
|
}
|
|
3150
3154
|
if (k === "cert")
|
|
3151
3155
|
cert = v;
|
|
@@ -3169,7 +3173,7 @@ function lowerHttp2ModuleCall(L, expr, bi, loc) {
|
|
|
3169
3173
|
setting.name.text === "enableConnectProtocol") {
|
|
3170
3174
|
if (setting.initializer.kind !== ts.SyntaxKind.TrueKeyword &&
|
|
3171
3175
|
setting.initializer.kind !== ts.SyntaxKind.FalseKeyword) {
|
|
3172
|
-
|
|
3176
|
+
lowerer.noLowering("settings.enableConnectProtocol with a non-boolean literal", setting, "spell enableConnectProtocol as true or false");
|
|
3173
3177
|
}
|
|
3174
3178
|
enableConnectProtocol = setting.initializer.kind === ts.SyntaxKind.TrueKeyword;
|
|
3175
3179
|
}
|
|
@@ -3179,25 +3183,25 @@ function lowerHttp2ModuleCall(L, expr, bi, loc) {
|
|
|
3179
3183
|
(isScalarLiteral(initializer) || isLiteralObjectOfLiterals(initializer))) {
|
|
3180
3184
|
return;
|
|
3181
3185
|
}
|
|
3182
|
-
|
|
3186
|
+
lowerer.noLowering(`createSecureServer option '${k}' with a non-literal value`, prop, "h2 session-tuning options are accepted with literal values but are not applied yet");
|
|
3183
3187
|
}
|
|
3184
3188
|
if (k === "SNICallback") {
|
|
3185
3189
|
if (initializer === null) {
|
|
3186
|
-
|
|
3190
|
+
lowerer.noLowering("createSecureServer with a shorthand SNICallback option", prop, "spell it out: SNICallback: theCallback");
|
|
3187
3191
|
}
|
|
3188
3192
|
lowerSniValue(initializer, prop);
|
|
3189
3193
|
return;
|
|
3190
3194
|
}
|
|
3191
|
-
fenceOrDropOptionKey(
|
|
3195
|
+
fenceOrDropOptionKey(lowerer, prop, k, "createSecureServer", HTTP2_SECURE_SERVER_DOCUMENTED_OPTIONS, "allowHTTP1: true, cert, key, and SNICallback are the supported options (h2 session-tuning options are accepted as literals and ignored)");
|
|
3192
3196
|
// An undocumented key, dropped like Node drops it.
|
|
3193
3197
|
};
|
|
3194
3198
|
for (const prop of optsNode.properties)
|
|
3195
3199
|
lowerOption(prop);
|
|
3196
3200
|
if (cert === null || key === null) {
|
|
3197
|
-
|
|
3201
|
+
lowerer.noLowering("createSecureServer without both cert and key", optsNode, "the supported options object is { allowHTTP1?, cert, key } (PEM strings or Buffers)");
|
|
3198
3202
|
}
|
|
3199
3203
|
if (sni !== null && handlerNode !== null) {
|
|
3200
|
-
|
|
3204
|
+
lowerer.noLowering("createSecureServer with both an SNICallback and an eager handler", expr, "register the handler separately: server.on(\"request\", (req, res) => ...)");
|
|
3201
3205
|
}
|
|
3202
3206
|
if (!allowHttp1) {
|
|
3203
3207
|
// The REAL h2-over-TLS server (scr_http2.c + scr_tls.c): ALPN
|
|
@@ -3206,10 +3210,10 @@ function lowerHttp2ModuleCall(L, expr, bi, loc) {
|
|
|
3206
3210
|
// SNI callbacks are an https-path feature; the h2 stack serves one
|
|
3207
3211
|
// cert/key pair.
|
|
3208
3212
|
if (sni !== null) {
|
|
3209
|
-
|
|
3213
|
+
lowerer.noLowering("createSecureServer with an SNICallback and no allowHTTP1", optsNode, "SNICallback lowers on the dual allowHTTP1 server — the h2-only server serves one cert/key pair");
|
|
3210
3214
|
}
|
|
3211
3215
|
if (handlerNode !== null) {
|
|
3212
|
-
const cb = lowerRequestHandlerArg(
|
|
3216
|
+
const cb = lowerRequestHandlerArg(lowerer, handlerNode);
|
|
3213
3217
|
return { kind: "libCall", fn: "http2.createSecureServerH2Req", args: [cert, key, cb, boolLit(enableConnectProtocol, loc)], type: NETSERVER_T, loc };
|
|
3214
3218
|
}
|
|
3215
3219
|
return { kind: "libCall", fn: "http2.createSecureServerH2", args: [cert, key, boolLit(enableConnectProtocol, loc)], type: NETSERVER_T, loc };
|
|
@@ -3218,13 +3222,13 @@ function lowerHttp2ModuleCall(L, expr, bi, loc) {
|
|
|
3218
3222
|
return { kind: "libCall", fn: "http2.createSecureServerSni", args: [cert, key, sni, boolLit(enableConnectProtocol, loc)], type: NETSERVER_T, loc };
|
|
3219
3223
|
}
|
|
3220
3224
|
if (handlerNode !== null) {
|
|
3221
|
-
const cb = lowerRequestHandlerArg(
|
|
3225
|
+
const cb = lowerRequestHandlerArg(lowerer, handlerNode);
|
|
3222
3226
|
return { kind: "libCall", fn: "http2.createSecureServerReq", args: [cert, key, cb, boolLit(enableConnectProtocol, loc)], type: NETSERVER_T, loc };
|
|
3223
3227
|
}
|
|
3224
3228
|
return { kind: "libCall", fn: "http2.createSecureServer", args: [cert, key, boolLit(enableConnectProtocol, loc)], type: NETSERVER_T, loc };
|
|
3225
3229
|
}
|
|
3226
|
-
|
|
3227
|
-
"createSecureServer({ allowHTTP1: true, cert, key }) and the h2-only form are lowered",
|
|
3230
|
+
lowerer.noLowering(`http2.${bi.member}`, expr, builtinFenceHintOf("http2", bi.member) ??
|
|
3231
|
+
"createSecureServer({ allowHTTP1: true, cert, key }) and the h2-only form are lowered", lowerer.resolveValueSymbol(expr.expression));
|
|
3228
3232
|
}
|
|
3229
3233
|
/** http.request(options[, cb]) / http.get(options[, cb]) — the client.
|
|
3230
3234
|
* The options argument must be an OBJECT LITERAL; the lowered keys are
|
|
@@ -3244,24 +3248,24 @@ function lowerHttp2ModuleCall(L, expr, bi, loc) {
|
|
|
3244
3248
|
* supported options SPREAD is the conditional `...(c ? {
|
|
3245
3249
|
* rejectUnauthorized: <bool> } : {})` (either orientation) — the
|
|
3246
3250
|
* portless isProxyRunning shape. */
|
|
3247
|
-
function lowerHttpClientCall(
|
|
3251
|
+
function lowerHttpClientCall(lowerer, expr, member, loc, secure = false) {
|
|
3248
3252
|
const binding = typeof secure === "object" ? secure : null;
|
|
3249
3253
|
const secureish = binding !== null || secure === true;
|
|
3250
3254
|
/** A FRESH lowering of the binding's secure condition (pure identifier
|
|
3251
3255
|
* read — each use is its own IR). */
|
|
3252
3256
|
const secureExpr = () => {
|
|
3253
|
-
const c =
|
|
3257
|
+
const c = lowerer.lowerCondition(binding.cond);
|
|
3254
3258
|
return binding.trueSecure
|
|
3255
3259
|
? c
|
|
3256
3260
|
: { kind: "unary", op: "!", operand: c, type: BOOL, loc };
|
|
3257
3261
|
};
|
|
3258
3262
|
const args = expr.arguments;
|
|
3259
3263
|
if (args.length < 1 || args.length > 2) {
|
|
3260
|
-
|
|
3264
|
+
lowerer.noLowering(`${member} with ${args.length} arguments`, expr, `the supported form is ${member}(options[, callback])`);
|
|
3261
3265
|
}
|
|
3262
3266
|
const optsNode = args[0];
|
|
3263
3267
|
if (!ts.isObjectLiteralExpression(optsNode)) {
|
|
3264
|
-
const t =
|
|
3268
|
+
const t = lowerer.mapTypeOf(lowerer.typeOf(optsNode));
|
|
3265
3269
|
if ((t?.kind === "string" || t?.kind === "url") && binding === null) {
|
|
3266
3270
|
// The URL-string form — http.get(`http://127.0.0.1:${port}/x`[, cb])
|
|
3267
3271
|
// and its https spelling: the runtime parses through the WHATWG unit
|
|
@@ -3274,8 +3278,8 @@ function lowerHttpClientCall(L, expr, member, loc, secure = false) {
|
|
|
3274
3278
|
// A URL OBJECT is its href through the same parse — Node's own
|
|
3275
3279
|
// reading of the argument, and the serialization round-trips.
|
|
3276
3280
|
const url = t.kind === "url"
|
|
3277
|
-
? { kind: "libCall", fn: "url.href", args: [
|
|
3278
|
-
:
|
|
3281
|
+
? { kind: "libCall", fn: "url.href", args: [lowerer.lowerExpr(optsNode)], type: STRING, loc }
|
|
3282
|
+
: lowerer.lowerExprExpecting(optsNode, STRING);
|
|
3279
3283
|
const methodLit = { kind: "strLit", value: "GET", type: STRING, loc };
|
|
3280
3284
|
const autoEnd = boolLit(member === "get", loc);
|
|
3281
3285
|
const isTls = secure === true;
|
|
@@ -3283,11 +3287,11 @@ function lowerHttpClientCall(L, expr, member, loc, secure = false) {
|
|
|
3283
3287
|
const fn = isTls ? "https.requestUrl" : "http.requestUrl";
|
|
3284
3288
|
return { kind: "libCall", fn, args: [url, methodLit, autoEnd], type: HTTPCLIENTREQ_T, loc };
|
|
3285
3289
|
}
|
|
3286
|
-
const { cb } = lowerCallbackArg(
|
|
3290
|
+
const { cb } = lowerCallbackArg(lowerer, args[1], "response callbacks", 1, (p) => p.kind === "httpReq", "use (res) or ()", [HTTPREQ_T]);
|
|
3287
3291
|
const fn = isTls ? "https.requestUrlCb" : "http.requestUrlCb";
|
|
3288
3292
|
return { kind: "libCall", fn, args: [url, methodLit, autoEnd, cb], type: HTTPCLIENTREQ_T, loc };
|
|
3289
3293
|
}
|
|
3290
|
-
|
|
3294
|
+
lowerer.noLowering(t?.kind === "string"
|
|
3291
3295
|
? `a URL-string first argument through a request-function binding`
|
|
3292
3296
|
: `${member} with a non-literal options argument`, optsNode, t?.kind === "string"
|
|
3293
3297
|
// Only the binding mode reaches here now: the scheme would have to
|
|
@@ -3324,10 +3328,10 @@ function lowerHttpClientCall(L, expr, member, loc, secure = false) {
|
|
|
3324
3328
|
if (cs !== null && cs !== "unsupported" && cs.props.length === 1 &&
|
|
3325
3329
|
cs.props[0].name.text === "rejectUnauthorized" &&
|
|
3326
3330
|
ts.isPropertyAssignment(cs.props[0]) && secureish) {
|
|
3327
|
-
const cond =
|
|
3328
|
-
const v =
|
|
3331
|
+
const cond = lowerer.lowerCondition(cs.cond);
|
|
3332
|
+
const v = lowerer.lowerExpr(cs.props[0].initializer);
|
|
3329
3333
|
if (v.type.kind !== "bool") {
|
|
3330
|
-
|
|
3334
|
+
lowerer.noLowering(`a ${member} 'rejectUnauthorized' option of '${lowerer.fmt(v.type)}' values`, cs.props[0], "the option value must be a boolean");
|
|
3331
3335
|
}
|
|
3332
3336
|
const dflt = boolLit(true, loc); /* Node's default: verify */
|
|
3333
3337
|
reject = {
|
|
@@ -3340,7 +3344,7 @@ function lowerHttpClientCall(L, expr, member, loc, secure = false) {
|
|
|
3340
3344
|
};
|
|
3341
3345
|
continue;
|
|
3342
3346
|
}
|
|
3343
|
-
|
|
3347
|
+
lowerer.noLowering(`${member} with this options spread`, prop, secureish
|
|
3344
3348
|
? "the one supported spread is the conditional `...(c ? { rejectUnauthorized: <bool> } : {})` (either orientation) — write other members inline"
|
|
3345
3349
|
: "spreads have no lowering here — write each option inline");
|
|
3346
3350
|
}
|
|
@@ -3353,13 +3357,13 @@ function lowerHttpClientCall(L, expr, member, loc, secure = false) {
|
|
|
3353
3357
|
initializer = null;
|
|
3354
3358
|
}
|
|
3355
3359
|
else {
|
|
3356
|
-
|
|
3360
|
+
lowerer.noLowering(`${member} options with computed keys`, prop, "each option must be a plain `name: value` (or shorthand) entry with a literal key");
|
|
3357
3361
|
}
|
|
3358
3362
|
const key = prop.name.text;
|
|
3359
3363
|
const lowerVal = (want) => {
|
|
3360
3364
|
const v = initializer !== null
|
|
3361
|
-
?
|
|
3362
|
-
:
|
|
3365
|
+
? lowerer.lowerExpr(initializer)
|
|
3366
|
+
: lowerer.lowerShorthandValue(prop);
|
|
3363
3367
|
// A checked-dynamic value (an untyped JS binding carrying the
|
|
3364
3368
|
// port/path through a helper): dynCheck validates it into the
|
|
3365
3369
|
// option's type — a mismatch is the catchable path-annotated
|
|
@@ -3368,7 +3372,7 @@ function lowerHttpClientCall(L, expr, member, loc, secure = false) {
|
|
|
3368
3372
|
return { kind: "dynCheck", value: v, type: want === "string" ? STRING : F64, loc: locOf(prop) };
|
|
3369
3373
|
}
|
|
3370
3374
|
if (v.type.kind !== want) {
|
|
3371
|
-
|
|
3375
|
+
lowerer.noLowering(`a ${member} '${key}' option of '${lowerer.fmt(v.type)}' values`, prop, want === "string" ? "the option value must be a string" : "the option value must be a number");
|
|
3372
3376
|
}
|
|
3373
3377
|
return v;
|
|
3374
3378
|
};
|
|
@@ -3391,9 +3395,9 @@ function lowerHttpClientCall(L, expr, member, loc, secure = false) {
|
|
|
3391
3395
|
break;
|
|
3392
3396
|
case "headers":
|
|
3393
3397
|
if (initializer === null) {
|
|
3394
|
-
|
|
3398
|
+
lowerer.noLowering(`${member} with a shorthand headers option`, prop, "spell it out: headers: theHeaders");
|
|
3395
3399
|
}
|
|
3396
|
-
headers = lowerClientHeadersOption(
|
|
3400
|
+
headers = lowerClientHeadersOption(lowerer, initializer);
|
|
3397
3401
|
break;
|
|
3398
3402
|
case "createConnection": {
|
|
3399
3403
|
// The caller's own dialer: a `() => net.Socket` closure the
|
|
@@ -3401,40 +3405,40 @@ function lowerHttpClientCall(L, expr, member, loc, secure = false) {
|
|
|
3401
3405
|
// dial). https keeps the fence — the TLS client owns its socket —
|
|
3402
3406
|
// and the binding mode inherits it (its https arm would).
|
|
3403
3407
|
if (secureish) {
|
|
3404
|
-
|
|
3408
|
+
lowerer.noLowering(`${member} with a createConnection option`, prop, "the https client dials its own TLS socket — pass hostname/port");
|
|
3405
3409
|
}
|
|
3406
3410
|
if (initializer === null) {
|
|
3407
|
-
|
|
3411
|
+
lowerer.noLowering(`${member} with a shorthand createConnection option`, prop, "spell it out: createConnection: theDialer");
|
|
3408
3412
|
}
|
|
3409
|
-
const v =
|
|
3413
|
+
const v = lowerer.lowerExpr(initializer);
|
|
3410
3414
|
if (v.type.kind !== "func" || v.type.params.length !== 0 || v.type.ret.kind !== "netSocket") {
|
|
3411
|
-
|
|
3415
|
+
lowerer.noLowering(`a ${member} 'createConnection' option of '${lowerer.fmt(v.type)}' values`, prop, "the dialer is () => net.Socket — no arguments, returning the socket to use");
|
|
3412
3416
|
}
|
|
3413
3417
|
connCb = v;
|
|
3414
3418
|
break;
|
|
3415
3419
|
}
|
|
3416
3420
|
case "rejectUnauthorized": {
|
|
3417
3421
|
if (!secureish) {
|
|
3418
|
-
|
|
3422
|
+
lowerer.noLowering(`${member} option 'rejectUnauthorized'`, prop, "rejectUnauthorized is an https.request option — the plain http client has no TLS layer");
|
|
3419
3423
|
}
|
|
3420
3424
|
const v = initializer !== null
|
|
3421
|
-
?
|
|
3422
|
-
:
|
|
3425
|
+
? lowerer.lowerExpr(initializer)
|
|
3426
|
+
: lowerer.lowerShorthandValue(prop);
|
|
3423
3427
|
if (v.type.kind !== "bool") {
|
|
3424
|
-
|
|
3428
|
+
lowerer.noLowering(`a ${member} 'rejectUnauthorized' option of '${lowerer.fmt(v.type)}' values`, prop, "the option value must be a boolean");
|
|
3425
3429
|
}
|
|
3426
3430
|
reject = v;
|
|
3427
3431
|
break;
|
|
3428
3432
|
}
|
|
3429
3433
|
case "ca": {
|
|
3430
3434
|
if (!secureish) {
|
|
3431
|
-
|
|
3435
|
+
lowerer.noLowering(`${member} option 'ca'`, prop, "ca is an https.request option — the plain http client has no TLS layer");
|
|
3432
3436
|
}
|
|
3433
3437
|
const v = initializer !== null
|
|
3434
|
-
?
|
|
3435
|
-
:
|
|
3438
|
+
? lowerer.lowerExpr(initializer)
|
|
3439
|
+
: lowerer.lowerShorthandValue(prop);
|
|
3436
3440
|
if (v.type.kind !== "string" && !(v.type.kind === "bytes" && v.type.elem === "u8")) {
|
|
3437
|
-
|
|
3441
|
+
lowerer.noLowering(`a ${member} 'ca' option of '${lowerer.fmt(v.type)}' values`, prop, "the ca is a PEM string or Buffer here");
|
|
3438
3442
|
}
|
|
3439
3443
|
ca = v;
|
|
3440
3444
|
break;
|
|
@@ -3461,17 +3465,17 @@ function lowerHttpClientCall(L, expr, member, loc, secure = false) {
|
|
|
3461
3465
|
break; // the default agent: what the agent-free call compiles
|
|
3462
3466
|
}
|
|
3463
3467
|
const v = initializer !== null
|
|
3464
|
-
?
|
|
3465
|
-
:
|
|
3468
|
+
? lowerer.lowerExpr(initializer)
|
|
3469
|
+
: lowerer.lowerShorthandValue(prop);
|
|
3466
3470
|
if (v.type.kind !== "dyn") {
|
|
3467
|
-
|
|
3471
|
+
lowerer.noLowering(`a ${member} 'agent' option of '${lowerer.fmt(v.type)}' values`, prop, "the agent is the checked-dynamic Agent handle new http.Agent(...) answers " +
|
|
3468
3472
|
"(or false / null / undefined)");
|
|
3469
3473
|
}
|
|
3470
3474
|
agentVal = v;
|
|
3471
3475
|
break;
|
|
3472
3476
|
}
|
|
3473
3477
|
default:
|
|
3474
|
-
fenceOrDropOptionKey(
|
|
3478
|
+
fenceOrDropOptionKey(lowerer, prop, key, member, secureish ? HTTPS_CLIENT_DOCUMENTED_OPTIONS : HTTP_CLIENT_DOCUMENTED_OPTIONS, secureish
|
|
3475
3479
|
? "hostname/host, port, path, method, timeout, headers, agent, rejectUnauthorized, and ca are the supported options"
|
|
3476
3480
|
: "hostname/host, port, path, method, timeout, headers, and agent are the supported options");
|
|
3477
3481
|
}
|
|
@@ -3483,46 +3487,43 @@ function lowerHttpClientCall(L, expr, member, loc, secure = false) {
|
|
|
3483
3487
|
// lowering. A user-set connection header wins, Node's rule; a
|
|
3484
3488
|
// non-literal headers record cannot be checked for one, so the combo
|
|
3485
3489
|
// fences instead of double-sending.
|
|
3486
|
-
const strLitAt = (value) => ({ kind: "strLit", value, type: STRING, loc });
|
|
3487
3490
|
if (headers === null) {
|
|
3488
|
-
headers = { kind: "arrayLit", elems: [
|
|
3491
|
+
headers = { kind: "arrayLit", elems: [strLit("Connection", loc), strLit("close", loc)], type: arrayOf(STRING), loc };
|
|
3489
3492
|
}
|
|
3490
3493
|
else if (headers.kind === "arrayLit") {
|
|
3491
3494
|
const hasConnection = headers.elems.some((el, i) => i % 2 === 0 && el.kind === "strLit" && el.value.toLowerCase() === "connection");
|
|
3492
3495
|
if (!hasConnection) {
|
|
3493
|
-
headers = { ...headers, elems: [...headers.elems,
|
|
3496
|
+
headers = { ...headers, elems: [...headers.elems, strLit("Connection", loc), strLit("close", loc)] };
|
|
3494
3497
|
}
|
|
3495
3498
|
}
|
|
3496
3499
|
else {
|
|
3497
|
-
|
|
3500
|
+
lowerer.noLowering(`${member} with agent: false and a non-literal headers record`, agentClose, "agent: false lowers by injecting Connection: close into a LITERAL headers object — " +
|
|
3498
3501
|
"add connection: \"close\" to the record instead, or write the headers inline");
|
|
3499
3502
|
}
|
|
3500
3503
|
}
|
|
3501
|
-
const strLit = (value) => ({ kind: "strLit", value, type: STRING, loc });
|
|
3502
|
-
const numLit = (value) => ({ kind: "numLit", value, type: F64, loc });
|
|
3503
3504
|
if (connCb !== null && (host !== null || port !== null)) {
|
|
3504
3505
|
// Node would use host/port only for the Host header once a dialer
|
|
3505
3506
|
// exists — a silent half-use; set headers.host instead.
|
|
3506
|
-
|
|
3507
|
+
lowerer.noLowering(`${member} mixing createConnection with hostname/host/port`, optsNode, "the dialer supplies the socket — set the Host header via headers: { host: ... } instead");
|
|
3507
3508
|
}
|
|
3508
3509
|
if (agentVal !== null && connCb !== null) {
|
|
3509
|
-
|
|
3510
|
+
lowerer.noLowering(`${member} mixing an agent value with createConnection`, optsNode, "both own the dial — pass one or the other");
|
|
3510
3511
|
}
|
|
3511
3512
|
if (agentVal !== null && binding !== null) {
|
|
3512
|
-
|
|
3513
|
+
lowerer.noLowering(`${member} through a module-function binding with an agent value`, optsNode, "call http.request/https.request directly when passing an Agent");
|
|
3513
3514
|
}
|
|
3514
|
-
host ??= strLit("localhost");
|
|
3515
|
+
host ??= strLit("localhost", loc);
|
|
3515
3516
|
// The binding mode's default port follows the runtime dial: 443 on the
|
|
3516
3517
|
// TLS arm, 80 on the plain one — exactly each client's own default.
|
|
3517
3518
|
// With an AGENT the sentinel -1 says "no port option": the runtime
|
|
3518
3519
|
// consults the agent's (settable) defaultPort first, Node's merge.
|
|
3519
3520
|
port ??= binding !== null
|
|
3520
|
-
? { kind: "ternary", cond: secureExpr(), then: numLit(443), else_: numLit(80), type: F64, loc }
|
|
3521
|
-
: agentVal !== null ? numLit(-1)
|
|
3522
|
-
: numLit(secure === true ? 443 : 80);
|
|
3523
|
-
path ??= strLit("/");
|
|
3524
|
-
method ??= strLit("GET");
|
|
3525
|
-
timeout ??= numLit(0);
|
|
3521
|
+
? { kind: "ternary", cond: secureExpr(), then: numLit(443, loc), else_: numLit(80, loc), type: F64, loc }
|
|
3522
|
+
: agentVal !== null ? numLit(-1, loc)
|
|
3523
|
+
: numLit(secure === true ? 443 : 80, loc);
|
|
3524
|
+
path ??= strLit("/", loc);
|
|
3525
|
+
method ??= strLit("GET", loc);
|
|
3526
|
+
timeout ??= numLit(0, loc);
|
|
3526
3527
|
headers ??= { kind: "arrayLit", elems: [], type: arrayOf(STRING), loc };
|
|
3527
3528
|
const autoEnd = boolLit(member === "get", loc);
|
|
3528
3529
|
if (connCb !== null) {
|
|
@@ -3530,13 +3531,13 @@ function lowerHttpClientCall(L, expr, member, loc, secure = false) {
|
|
|
3530
3531
|
if (args.length === 1) {
|
|
3531
3532
|
return { kind: "libCall", fn: "http.requestConn", args: base, type: HTTPCLIENTREQ_T, loc };
|
|
3532
3533
|
}
|
|
3533
|
-
const { cb } = lowerCallbackArg(
|
|
3534
|
+
const { cb } = lowerCallbackArg(lowerer, args[1], "response callbacks", 1, (p) => p.kind === "httpReq", "use (res) or ()", [HTTPREQ_T]);
|
|
3534
3535
|
return { kind: "libCall", fn: "http.requestConnCb", args: [...base, cb], type: HTTPCLIENTREQ_T, loc };
|
|
3535
3536
|
}
|
|
3536
3537
|
const base = [host, port, path, method, timeout, headers, autoEnd];
|
|
3537
3538
|
if (secureish) {
|
|
3538
3539
|
reject ??= boolLit(true, loc); /* Node's default: verify */
|
|
3539
|
-
ca ??= strLit(""); /* none: /etc/ssl/cert.pem stands in for Node's roots */
|
|
3540
|
+
ca ??= strLit("", loc); /* none: /etc/ssl/cert.pem stands in for Node's roots */
|
|
3540
3541
|
base.push(reject, ca);
|
|
3541
3542
|
}
|
|
3542
3543
|
if (agentVal !== null)
|
|
@@ -3549,7 +3550,7 @@ function lowerHttpClientCall(L, expr, member, loc, secure = false) {
|
|
|
3549
3550
|
: agentVal !== null ? "http.requestAgent" : "http.request";
|
|
3550
3551
|
return { kind: "libCall", fn, args: base, type: HTTPCLIENTREQ_T, loc };
|
|
3551
3552
|
}
|
|
3552
|
-
const { cb } = lowerCallbackArg(
|
|
3553
|
+
const { cb } = lowerCallbackArg(lowerer, args[1], "response callbacks", 1, (p) => p.kind === "httpReq", "use (res) or ()", [HTTPREQ_T]);
|
|
3553
3554
|
const fn = binding !== null ? "https.requestFnCb"
|
|
3554
3555
|
: secure === true ? (agentVal !== null ? "https.requestAgentCb" : "https.requestCb")
|
|
3555
3556
|
: agentVal !== null ? "http.requestAgentCb" : "http.requestCb";
|
|
@@ -3560,60 +3561,60 @@ function lowerHttpClientCall(L, expr, member, loc, secure = false) {
|
|
|
3560
3561
|
* array directly; any other expression must be a Record whose values are
|
|
3561
3562
|
* strings (or `string | undefined` — absent entries drop, the env-option
|
|
3562
3563
|
* rule), flattened by the interned env.pairs helper. */
|
|
3563
|
-
function lowerClientHeadersOption(
|
|
3564
|
+
function lowerClientHeadersOption(lowerer, node) {
|
|
3564
3565
|
const loc = locOf(node);
|
|
3565
3566
|
if (ts.isObjectLiteralExpression(node)) {
|
|
3566
3567
|
const elems = [];
|
|
3567
3568
|
for (const prop of node.properties) {
|
|
3568
|
-
const key = ts.isPropertyAssignment(prop) ? staticHeaderKeyOf(
|
|
3569
|
+
const key = ts.isPropertyAssignment(prop) ? staticHeaderKeyOf(lowerer, prop) : null;
|
|
3569
3570
|
if (!ts.isPropertyAssignment(prop) || key === null) {
|
|
3570
|
-
|
|
3571
|
+
lowerer.noLowering("request headers with dynamic keys, spreads, or shorthand entries", prop, "each header must be a `name: value` entry whose key is a literal (or a string-literal-typed const: [PORTLESS_HEADER])");
|
|
3571
3572
|
}
|
|
3572
|
-
let value =
|
|
3573
|
+
let value = lowerer.lowerExpr(prop.initializer);
|
|
3573
3574
|
if (value.type.kind === "f64") {
|
|
3574
3575
|
// Node formats number values via String(n) — the same ToString.
|
|
3575
3576
|
value = { kind: "toString", operand: value, type: STRING, loc };
|
|
3576
3577
|
}
|
|
3577
3578
|
if (value.type.kind !== "string") {
|
|
3578
|
-
|
|
3579
|
+
lowerer.noLowering(`request header values of type '${lowerer.fmt(value.type)}'`, prop.initializer, "header values are strings or numbers here");
|
|
3579
3580
|
}
|
|
3580
3581
|
elems.push({ kind: "strLit", value: key, type: STRING, loc });
|
|
3581
3582
|
elems.push(value);
|
|
3582
3583
|
}
|
|
3583
3584
|
return { kind: "arrayLit", elems, type: arrayOf(STRING), loc };
|
|
3584
3585
|
}
|
|
3585
|
-
const v =
|
|
3586
|
+
const v = lowerer.lowerExpr(node);
|
|
3586
3587
|
if (v.type.kind !== "record") {
|
|
3587
|
-
|
|
3588
|
+
lowerer.noLowering(`request headers of '${lowerer.fmt(v.type)}' values`, node, "pass an object literal or a Record<string, string>");
|
|
3588
3589
|
}
|
|
3589
|
-
const helper =
|
|
3590
|
+
const helper = lowerer.envToPairsHelper(v.type.shapeId, loc);
|
|
3590
3591
|
if (helper === null) {
|
|
3591
|
-
|
|
3592
|
+
lowerer.noLowering(`request headers of '${lowerer.fmt(v.type)}' values`, node, "header values must be strings (or string | undefined)");
|
|
3592
3593
|
}
|
|
3593
3594
|
return { kind: "call", callee: helper, args: [v], type: arrayOf(STRING), loc };
|
|
3594
3595
|
}
|
|
3595
3596
|
/** Method calls on ClientRequest receivers: write/end/destroy and
|
|
3596
3597
|
* on/once("response" | "error" | "timeout" | "close"). Null for other
|
|
3597
3598
|
* receivers. */
|
|
3598
|
-
function lowerHttpClientMethodCall(
|
|
3599
|
-
if (
|
|
3599
|
+
function lowerHttpClientMethodCall(lowerer, call, access) {
|
|
3600
|
+
if (lowerer.mapTypeOf(lowerer.typeOf(access.expression))?.kind !== "httpClientReq")
|
|
3600
3601
|
return null;
|
|
3601
|
-
if (!
|
|
3602
|
+
if (!lowerer.isStdlibMember(access))
|
|
3602
3603
|
return null;
|
|
3603
3604
|
const name = access.name.text;
|
|
3604
3605
|
const loc = locOf(call);
|
|
3605
3606
|
const args = call.arguments;
|
|
3606
3607
|
if (name === "write" || name === "end") {
|
|
3607
|
-
requireStatementPosition(
|
|
3608
|
+
requireStatementPosition(lowerer, call, `request.${name}(...)`);
|
|
3608
3609
|
const minArgs = name === "write" ? 1 : 0;
|
|
3609
3610
|
if (args.length < minArgs || args.length > 1) {
|
|
3610
|
-
|
|
3611
|
+
lowerer.noLowering(`${name} with ${args.length} arguments`, call, name === "write" ? "the supported form is write(data)" : "the supported forms are end() and end(data)");
|
|
3611
3612
|
}
|
|
3612
|
-
const receiver =
|
|
3613
|
+
const receiver = lowerer.lowerExpr(access.expression);
|
|
3613
3614
|
if (args.length === 0) {
|
|
3614
3615
|
return { kind: "libCall", fn: "http.clientEnd", args: [receiver], type: VOID, loc };
|
|
3615
3616
|
}
|
|
3616
|
-
const data =
|
|
3617
|
+
const data = lowerer.lowerExpr(args[0]);
|
|
3617
3618
|
if (data.type.kind === "string") {
|
|
3618
3619
|
const fn = name === "write" ? "http.clientWrite" : "http.clientEndStr";
|
|
3619
3620
|
return { kind: "libCall", fn, args: [receiver, data], type: VOID, loc };
|
|
@@ -3627,32 +3628,32 @@ function lowerHttpClientMethodCall(L, call, access) {
|
|
|
3627
3628
|
const fn = name === "write" ? "http.clientWriteBytes" : "http.clientEndBytes";
|
|
3628
3629
|
return { kind: "libCall", fn, args: [receiver, data], type: VOID, loc };
|
|
3629
3630
|
}
|
|
3630
|
-
|
|
3631
|
+
lowerer.noLowering(`${name} of '${lowerer.fmt(data.type)}' data`, args[0] ?? call, NARROW_DATA_HINT);
|
|
3631
3632
|
}
|
|
3632
3633
|
if (name === "destroy") {
|
|
3633
|
-
requireStatementPosition(
|
|
3634
|
+
requireStatementPosition(lowerer, call, "request.destroy()");
|
|
3634
3635
|
if (args.length !== 0) {
|
|
3635
|
-
|
|
3636
|
+
lowerer.noLowering(`destroy with ${args.length} arguments`, call, "destroy() takes no arguments here");
|
|
3636
3637
|
}
|
|
3637
|
-
const receiver =
|
|
3638
|
+
const receiver = lowerer.lowerExpr(access.expression);
|
|
3638
3639
|
return { kind: "libCall", fn: "http.clientDestroy", args: [receiver], type: VOID, loc };
|
|
3639
3640
|
}
|
|
3640
3641
|
if ((name === "on" || name === "once" || name === "addListener") && args.length === 2) {
|
|
3641
|
-
requireStatementPosition(
|
|
3642
|
+
requireStatementPosition(lowerer, call, `request.${name}(...)`);
|
|
3642
3643
|
const once = boolLit(name === "once", loc);
|
|
3643
|
-
const evT =
|
|
3644
|
+
const evT = lowerer.typeOf(args[0]);
|
|
3644
3645
|
const event = evT.isStringLiteralType() ? evT.value : null;
|
|
3645
|
-
const receiver =
|
|
3646
|
+
const receiver = lowerer.lowerExpr(access.expression);
|
|
3646
3647
|
if (event === "response") {
|
|
3647
|
-
const { cb } = lowerCallbackArg(
|
|
3648
|
+
const { cb } = lowerCallbackArg(lowerer, args[1], "response listeners", 1, (p) => p.kind === "httpReq", "use (res) or ()", [HTTPREQ_T]);
|
|
3648
3649
|
return { kind: "libCall", fn: "http.clientOnResponse", args: [receiver, cb, once], type: VOID, loc };
|
|
3649
3650
|
}
|
|
3650
3651
|
if (event === "error") {
|
|
3651
|
-
const { cb } = lowerCallbackArg(
|
|
3652
|
+
const { cb } = lowerCallbackArg(lowerer, args[1], "error listeners", 1, (p) => p.kind === "object" && p.className === "%Error", "use (err) or ()", [ERROR_T]);
|
|
3652
3653
|
return { kind: "libCall", fn: "http.clientOnError", args: [receiver, cb, once], type: VOID, loc };
|
|
3653
3654
|
}
|
|
3654
3655
|
if (event === "timeout" || event === "close") {
|
|
3655
|
-
const { cb } = lowerCallbackArg(
|
|
3656
|
+
const { cb } = lowerCallbackArg(lowerer, args[1], `${event} listeners`, 0, () => false, "use ()", []);
|
|
3656
3657
|
const fn = event === "timeout" ? "http.clientOnTimeout" : "http.clientOnClose";
|
|
3657
3658
|
return { kind: "libCall", fn, args: [receiver, cb, once], type: VOID, loc };
|
|
3658
3659
|
}
|
|
@@ -3660,24 +3661,24 @@ function lowerHttpClientMethodCall(L, call, access) {
|
|
|
3660
3661
|
// The client half of the WebSocket handover: a 101 response fires
|
|
3661
3662
|
// (res, socket, head) INSTEAD of 'response'; no listener destroys
|
|
3662
3663
|
// the connection, Node's default.
|
|
3663
|
-
const cb =
|
|
3664
|
+
const cb = lowerer.lowerExpr(args[1]);
|
|
3664
3665
|
if (cb.type.kind !== "func" || cb.type.params.length > 3) {
|
|
3665
|
-
|
|
3666
|
+
lowerer.unsupported("SC1090", args[1], "upgrade listeners with more than three parameters (use (res, socket, head))");
|
|
3666
3667
|
}
|
|
3667
3668
|
if (cb.type.ret.kind !== "void") {
|
|
3668
|
-
|
|
3669
|
+
lowerer.unsupported("SC1090", args[1], "upgrade listeners returning a value (make the callback body a block)");
|
|
3669
3670
|
}
|
|
3670
3671
|
const [p0, p1, p2] = cb.type.params;
|
|
3671
3672
|
if ((p0 !== undefined && p0.kind !== "httpReq") ||
|
|
3672
3673
|
(p1 !== undefined && p1.kind !== "netSocket") ||
|
|
3673
3674
|
(p2 !== undefined && !(p2.kind === "bytes" && p2.elem === "u8"))) {
|
|
3674
|
-
|
|
3675
|
+
lowerer.unsupported("SC1090", args[1], "upgrade listeners whose parameters are not (res: IncomingMessage, socket: Socket, head: Buffer)");
|
|
3675
3676
|
}
|
|
3676
3677
|
return { kind: "libCall", fn: "http.clientOnUpgrade", args: [receiver, cb, once], type: VOID, loc };
|
|
3677
3678
|
}
|
|
3678
|
-
|
|
3679
|
+
lowerer.noLowering(`request.${name}(${event === null ? "non-literal event" : `"${event}"`}, ...)`, args[0], '"response", "upgrade", "error", "timeout", and "close" are the supported request events (as literals)');
|
|
3679
3680
|
}
|
|
3680
|
-
|
|
3681
|
+
lowerer.noLowering(`ClientRequest.${name}`, call, "write, end, destroy, destroyed, and on/once of response/error/timeout/close are the supported ClientRequest members", lowerer.checker.getSymbolAtLocation(access.name));
|
|
3681
3682
|
}
|
|
3682
3683
|
/** True when `node` is the h2-only stream/session CALL shape —
|
|
3683
3684
|
* `req.stream.destroy()` / `req.session.m(...)` on an identifier
|
|
@@ -3687,7 +3688,7 @@ function lowerHttpClientMethodCall(L, call, access) {
|
|
|
3687
3688
|
* req.stream.destroy()`) is a throw-only body whose declared return type
|
|
3688
3689
|
* (ServerHttp2Stream, unmappable) must not decide the lambda's ABI.
|
|
3689
3690
|
* lambdaSignature consults this and takes void, the `never` stance. */
|
|
3690
|
-
export function isStreamUndefCallExpr(
|
|
3691
|
+
export function isStreamUndefCallExpr(lowerer, node) {
|
|
3691
3692
|
return (ts.isCallExpression(node) &&
|
|
3692
3693
|
ts.isPropertyAccessExpression(node.expression) &&
|
|
3693
3694
|
ts.isPropertyAccessExpression(node.expression.expression) &&
|
|
@@ -3696,81 +3697,81 @@ export function isStreamUndefCallExpr(L, node) {
|
|
|
3696
3697
|
(node.expression.expression.name.text === "stream" ||
|
|
3697
3698
|
node.expression.expression.name.text === "session") &&
|
|
3698
3699
|
ts.isIdentifier(node.expression.expression.expression) &&
|
|
3699
|
-
|
|
3700
|
-
|
|
3700
|
+
lowerer.mapTypeOf(lowerer.typeOf(node.expression.expression.expression))?.kind === "httpReq" &&
|
|
3701
|
+
lowerer.isStdlibMember(node.expression.expression));
|
|
3701
3702
|
}
|
|
3702
3703
|
/** `x instanceof net.Socket` over a UNION-typed x with a netSocket arm —
|
|
3703
3704
|
* the h2 compat 'connect' listener's narrowing (`resOrSocket instanceof
|
|
3704
3705
|
* net.Socket`): one runtime tag test; tsc's control-flow narrowing types
|
|
3705
3706
|
* the branches and reads bridge through maybeNarrow's unionNarrow. Null
|
|
3706
3707
|
* for every other shape (lower-exprs' fences stand). */
|
|
3707
|
-
export function lowerSocketInstanceOf(
|
|
3708
|
+
export function lowerSocketInstanceOf(lowerer, expr, loc) {
|
|
3708
3709
|
const rhs = expr.right;
|
|
3709
3710
|
const isNetSocketRef = (ts.isPropertyAccessExpression(rhs) &&
|
|
3710
3711
|
rhs.name.text === "Socket" &&
|
|
3711
|
-
|
|
3712
|
-
(ts.isIdentifier(rhs) &&
|
|
3713
|
-
|
|
3712
|
+
lowerer.builtinMemberOf(rhs)?.module === "net") ||
|
|
3713
|
+
(ts.isIdentifier(rhs) && lowerer.builtinImportOf(rhs)?.module === "net" &&
|
|
3714
|
+
lowerer.builtinImportOf(rhs)?.member === "Socket");
|
|
3714
3715
|
if (!isNetSocketRef)
|
|
3715
3716
|
return null;
|
|
3716
|
-
const leftT =
|
|
3717
|
+
const leftT = lowerer.mapTypeOf(lowerer.typeOf(expr.left));
|
|
3717
3718
|
if (leftT?.kind !== "union")
|
|
3718
3719
|
return null;
|
|
3719
|
-
const def =
|
|
3720
|
+
const def = lowerer.unions.get(leftT.unionId);
|
|
3720
3721
|
const tag = def ? def.arms.findIndex((a) => a.kind === "netSocket") : -1;
|
|
3721
3722
|
if (tag < 0)
|
|
3722
3723
|
return null;
|
|
3723
|
-
const left =
|
|
3724
|
+
const left = lowerer.lowerExpr(expr.left);
|
|
3724
3725
|
if (left.type.kind !== "union" || left.type.unionId !== leftT.unionId)
|
|
3725
3726
|
return null;
|
|
3726
3727
|
return { kind: "unionIsTag", unionId: leftT.unionId, tag, negated: false, value: left, type: BOOL, loc };
|
|
3727
3728
|
}
|
|
3728
3729
|
/** True when `node` reads `.headers` off an IncomingMessage — the
|
|
3729
3730
|
* receiver shape of both header-read forms. */
|
|
3730
|
-
|
|
3731
|
+
function isHttpReqHeaders(lowerer, node) {
|
|
3731
3732
|
return (ts.isPropertyAccessExpression(node) &&
|
|
3732
3733
|
!node.questionDotToken &&
|
|
3733
3734
|
node.name.text === "headers" &&
|
|
3734
|
-
|
|
3735
|
-
|
|
3735
|
+
lowerer.mapTypeOf(lowerer.typeOf(node.expression))?.kind === "httpReq" &&
|
|
3736
|
+
lowerer.isStdlibMember(node));
|
|
3736
3737
|
}
|
|
3737
3738
|
/** `req.headers["x-name"]` — the element twin of the property read below;
|
|
3738
3739
|
* called from lowerElementAccess (the process.env precedent). Answers the
|
|
3739
3740
|
* interned `string | undefined` union; names match case-insensitively
|
|
3740
3741
|
* (the runtime stores them lowercased, like Node). */
|
|
3741
|
-
export function lowerHttpHeadersElement(
|
|
3742
|
-
if (!isHttpReqHeaders(
|
|
3742
|
+
export function lowerHttpHeadersElement(lowerer, expr) {
|
|
3743
|
+
if (!isHttpReqHeaders(lowerer, expr.expression))
|
|
3743
3744
|
return null;
|
|
3744
3745
|
const recv = expr.expression.expression;
|
|
3745
|
-
const key =
|
|
3746
|
+
const key = lowerer.lowerExpr(expr.argumentExpression);
|
|
3746
3747
|
if (key.type.kind !== "string") {
|
|
3747
|
-
|
|
3748
|
+
lowerer.unsupported("SC1090", expr.argumentExpression, "indexing req.headers with non-string keys");
|
|
3748
3749
|
}
|
|
3749
|
-
const receiver =
|
|
3750
|
+
const receiver = lowerer.lowerExpr(recv);
|
|
3750
3751
|
return {
|
|
3751
3752
|
kind: "libCall",
|
|
3752
3753
|
fn: "http.reqHeader",
|
|
3753
3754
|
args: [receiver, key],
|
|
3754
|
-
type:
|
|
3755
|
+
type: lowerer.envValueType(),
|
|
3755
3756
|
loc: locOf(expr),
|
|
3756
3757
|
};
|
|
3757
3758
|
}
|
|
3758
3759
|
/** Method calls on IncomingMessage receivers: on/once("data" | "end").
|
|
3759
3760
|
* Null for other receivers. */
|
|
3760
|
-
function lowerHttpReqMethodCall(
|
|
3761
|
-
if (
|
|
3761
|
+
function lowerHttpReqMethodCall(lowerer, call, access) {
|
|
3762
|
+
if (lowerer.mapTypeOf(lowerer.typeOf(access.expression))?.kind !== "httpReq")
|
|
3762
3763
|
return null;
|
|
3763
|
-
if (!
|
|
3764
|
+
if (!lowerer.isStdlibMember(access))
|
|
3764
3765
|
return null;
|
|
3765
3766
|
const name = access.name.text;
|
|
3766
3767
|
const loc = locOf(call);
|
|
3767
3768
|
const args = call.arguments;
|
|
3768
3769
|
if (name === "resume" || name === "destroy") {
|
|
3769
|
-
requireStatementPosition(
|
|
3770
|
+
requireStatementPosition(lowerer, call, `req.${name}()`);
|
|
3770
3771
|
if (args.length !== 0) {
|
|
3771
|
-
|
|
3772
|
+
lowerer.noLowering(`${name} with ${args.length} arguments`, call, `${name}() takes no arguments here`);
|
|
3772
3773
|
}
|
|
3773
|
-
const receiver =
|
|
3774
|
+
const receiver = coerceToHandle(lowerer, access.expression, HTTPREQ_T);
|
|
3774
3775
|
const fn = name === "resume" ? "http.reqResume" : "http.reqDestroy";
|
|
3775
3776
|
return { kind: "libCall", fn, args: [receiver], type: VOID, loc };
|
|
3776
3777
|
}
|
|
@@ -3778,111 +3779,121 @@ function lowerHttpReqMethodCall(L, call, access) {
|
|
|
3778
3779
|
// setEncoding('utf8') — 'data' delivers strings (the chunk-encoding
|
|
3779
3780
|
// window); other real encodings fence loudly at runtime, unknown
|
|
3780
3781
|
// names throw Node's ERR_UNKNOWN_ENCODING.
|
|
3781
|
-
requireStatementPosition(
|
|
3782
|
+
requireStatementPosition(lowerer, call, "req.setEncoding(...)");
|
|
3782
3783
|
if (args.length !== 1) {
|
|
3783
|
-
|
|
3784
|
+
lowerer.noLowering(`setEncoding with ${args.length} arguments`, call, "the supported form is setEncoding(encoding)");
|
|
3784
3785
|
}
|
|
3785
|
-
const receiver =
|
|
3786
|
-
const enc =
|
|
3786
|
+
const receiver = coerceToHandle(lowerer, access.expression, HTTPREQ_T);
|
|
3787
|
+
const enc = lowerer.lowerExprExpecting(args[0], STRING);
|
|
3787
3788
|
return { kind: "libCall", fn: "http.reqSetEncoding", args: [receiver, enc], type: VOID, loc };
|
|
3788
3789
|
}
|
|
3789
3790
|
if (name === "pipe") {
|
|
3790
3791
|
// The proxy legs: req→res (the response body forward), req→clientReq
|
|
3791
3792
|
// (the request body forward), req→socket (the upgrade-rejection
|
|
3792
3793
|
// write) — plus socket→socket, which lives on the socket receiver.
|
|
3793
|
-
requireStatementPosition(
|
|
3794
|
+
requireStatementPosition(lowerer, call, "req.pipe(...)");
|
|
3794
3795
|
if (args.length !== 1) {
|
|
3795
|
-
|
|
3796
|
+
lowerer.noLowering(`pipe with ${args.length} arguments`, call, "the supported form is pipe(destination)");
|
|
3796
3797
|
}
|
|
3797
|
-
const receiver =
|
|
3798
|
-
const dst =
|
|
3798
|
+
const receiver = coerceToHandle(lowerer, access.expression, HTTPREQ_T);
|
|
3799
|
+
const dst = lowerer.lowerExpr(args[0]);
|
|
3799
3800
|
const fn = dst.type.kind === "httpRes" ? "http.reqPipeRes"
|
|
3800
3801
|
: dst.type.kind === "httpClientReq" ? "http.reqPipeClient"
|
|
3801
3802
|
: dst.type.kind === "netSocket" ? "http.reqPipeSock"
|
|
3802
3803
|
: null;
|
|
3803
3804
|
if (fn === null) {
|
|
3804
|
-
|
|
3805
|
+
lowerer.noLowering(`pipe into '${lowerer.fmt(dst.type)}' destinations`, args[0], "an IncomingMessage pipes into a ServerResponse, a ClientRequest, or a Socket");
|
|
3805
3806
|
}
|
|
3806
3807
|
return { kind: "libCall", fn, args: [receiver, dst], type: VOID, loc };
|
|
3807
3808
|
}
|
|
3808
3809
|
if ((name === "on" || name === "once" || name === "addListener") && args.length === 2) {
|
|
3809
|
-
requireStatementPosition(
|
|
3810
|
+
requireStatementPosition(lowerer, call, `req.${name}(...)`);
|
|
3810
3811
|
const once = boolLit(name === "once", loc);
|
|
3811
|
-
const evT =
|
|
3812
|
+
const evT = lowerer.typeOf(args[0]);
|
|
3812
3813
|
const event = evT.isStringLiteralType() ? evT.value : null;
|
|
3813
|
-
const receiver =
|
|
3814
|
+
const receiver = coerceToHandle(lowerer, access.expression, HTTPREQ_T);
|
|
3814
3815
|
if (event === "data") {
|
|
3815
|
-
const { cb } = lowerCallbackArg(
|
|
3816
|
+
const { cb } = lowerCallbackArg(lowerer, args[1], "data listeners", 1, (p) => p.kind === "bytes" && p.elem === "u8", "use (chunk: Buffer) or ()", [DYN]);
|
|
3816
3817
|
return { kind: "libCall", fn: "http.reqOnData", args: [receiver, cb, once], type: VOID, loc };
|
|
3817
3818
|
}
|
|
3818
3819
|
if (event === "end") {
|
|
3819
|
-
const { cb } = lowerCallbackArg(
|
|
3820
|
+
const { cb } = lowerCallbackArg(lowerer, args[1], "end listeners", 0, () => false, "use ()", []);
|
|
3820
3821
|
return { kind: "libCall", fn: "http.reqOnEnd", args: [receiver, cb, once], type: VOID, loc };
|
|
3821
3822
|
}
|
|
3822
3823
|
if (event === "error") {
|
|
3823
|
-
const { cb } = lowerCallbackArg(
|
|
3824
|
+
const { cb } = lowerCallbackArg(lowerer, args[1], "error listeners", 1, (p) => p.kind === "object" && p.className === "%Error", "use (err) or ()", [ERROR_T]);
|
|
3824
3825
|
return { kind: "libCall", fn: "http.reqOnError", args: [receiver, cb, once], type: VOID, loc };
|
|
3825
3826
|
}
|
|
3826
3827
|
if (event === "close") {
|
|
3827
|
-
const { cb } = lowerCallbackArg(
|
|
3828
|
+
const { cb } = lowerCallbackArg(lowerer, args[1], "close listeners", 0, () => false, "use ()", []);
|
|
3828
3829
|
return { kind: "libCall", fn: "http.reqOnClose", args: [receiver, cb, once], type: VOID, loc };
|
|
3829
3830
|
}
|
|
3830
3831
|
if (event === "aborted") {
|
|
3831
3832
|
// The h2 compat event (Http2ServerRequest 'aborted'); an http/1
|
|
3832
3833
|
// request registers too and never fires — the parser lane's story.
|
|
3833
|
-
const { cb } = lowerCallbackArg(
|
|
3834
|
+
const { cb } = lowerCallbackArg(lowerer, args[1], "aborted listeners", 0, () => false, "use ()", []);
|
|
3834
3835
|
return { kind: "libCall", fn: "http.reqOnAborted", args: [receiver, cb, once], type: VOID, loc };
|
|
3835
3836
|
}
|
|
3836
|
-
|
|
3837
|
+
lowerer.noLowering(`req.${name}(${event === null ? "non-literal event" : `"${event}"`}, ...)`, args[0], '"data", "end", "error", "close", and "aborted" are the supported request events (as literals)');
|
|
3837
3838
|
}
|
|
3838
|
-
|
|
3839
|
+
lowerer.noLowering(`IncomingMessage.${name}`, call, "url, method, statusCode, statusMessage, socket, headers/rawHeaders reads, resume, destroy, pipe, and on/once of data/end/error/close are the supported IncomingMessage members", lowerer.checker.getSymbolAtLocation(access.name));
|
|
3839
3840
|
}
|
|
3840
3841
|
/** Method calls on ServerResponse receivers: setHeader/writeHead/write/
|
|
3841
3842
|
* end. Null for other receivers. */
|
|
3842
|
-
function lowerHttpResMethodCall(
|
|
3843
|
-
if (
|
|
3843
|
+
function lowerHttpResMethodCall(lowerer, call, access) {
|
|
3844
|
+
if (lowerer.mapTypeOf(lowerer.typeOf(access.expression))?.kind !== "httpRes")
|
|
3844
3845
|
return null;
|
|
3845
|
-
if (!
|
|
3846
|
+
if (!lowerer.isStdlibMember(access))
|
|
3846
3847
|
return null;
|
|
3847
3848
|
const name = access.name.text;
|
|
3848
3849
|
const loc = locOf(call);
|
|
3849
3850
|
const args = call.arguments;
|
|
3850
3851
|
if (name === "setHeader") {
|
|
3851
|
-
requireStatementPosition(
|
|
3852
|
+
requireStatementPosition(lowerer, call, "res.setHeader(...)");
|
|
3852
3853
|
if (args.length !== 2) {
|
|
3853
|
-
|
|
3854
|
+
lowerer.noLowering(`setHeader with ${args.length} arguments`, call, "the supported form is setHeader(name, value)");
|
|
3855
|
+
}
|
|
3856
|
+
const receiver = coerceToHandle(lowerer, access.expression, HTTPRES_T);
|
|
3857
|
+
const header = lowerer.lowerExprExpecting(args[0], STRING);
|
|
3858
|
+
let value = lowerer.lowerExpr(args[1]);
|
|
3859
|
+
if (lowerer.mapTypeOf(lowerer.typeOf(args[1]))?.kind === "string" &&
|
|
3860
|
+
lowerer.runtimeOptionalWidening(value.type, STRING) !== null) {
|
|
3861
|
+
// Missing-value propagation keeps an indexed/header read's storage
|
|
3862
|
+
// as string | undefined after TypeScript narrows this call site to
|
|
3863
|
+
// string. Treat setHeader's string overload as the typed consumer it
|
|
3864
|
+
// is: extract the proven arm through the ordinary checked coercion.
|
|
3865
|
+
// An unguarded optional value remains union-typed to the checker and
|
|
3866
|
+
// retains the setHeader refusal below.
|
|
3867
|
+
value = lowerer.coerceInto(args[1], value, STRING);
|
|
3854
3868
|
}
|
|
3855
|
-
const receiver = handleReceiver(L, access.expression, HTTPRES_T);
|
|
3856
|
-
const header = L.lowerExprExpecting(args[0], STRING);
|
|
3857
|
-
let value = L.lowerExpr(args[1]);
|
|
3858
3869
|
if (value.type.kind === "f64") {
|
|
3859
3870
|
// Node formats number values via String(n) — the same ToString.
|
|
3860
3871
|
value = { kind: "toString", operand: value, type: STRING, loc };
|
|
3861
3872
|
}
|
|
3862
3873
|
if (value.type.kind !== "string") {
|
|
3863
|
-
|
|
3874
|
+
lowerer.noLowering(`setHeader with a '${lowerer.fmt(value.type)}' value`, args[1], "header values are strings or numbers here");
|
|
3864
3875
|
}
|
|
3865
3876
|
return { kind: "libCall", fn: "http.resSetHeader", args: [receiver, header, value], type: VOID, loc };
|
|
3866
3877
|
}
|
|
3867
3878
|
if (name === "writeHead") {
|
|
3868
3879
|
if (args.length < 1 || args.length > 3) {
|
|
3869
|
-
|
|
3880
|
+
lowerer.noLowering(`writeHead with ${args.length} arguments`, call, "the supported forms are writeHead(status[, statusMessage][, headers]) — headers as a literal object, a Record, or a flat [name, value, ...] array");
|
|
3870
3881
|
}
|
|
3871
|
-
const receiver =
|
|
3872
|
-
const status =
|
|
3882
|
+
const receiver = coerceToHandle(lowerer, access.expression, HTTPRES_T);
|
|
3883
|
+
const status = lowerer.lowerExprExpecting(args[0], F64);
|
|
3873
3884
|
// The optional statusMessage: a string-typed second argument is the
|
|
3874
3885
|
// reason phrase (Node's overload split); it rides the composing
|
|
3875
3886
|
// helper as a resStatusMsgSet prefix before the head goes out.
|
|
3876
3887
|
let msg = null;
|
|
3877
3888
|
let headersNode;
|
|
3878
3889
|
if (args.length === 2) {
|
|
3879
|
-
if (
|
|
3880
|
-
msg =
|
|
3890
|
+
if (lowerer.mapTypeOf(lowerer.typeOf(args[1]))?.kind === "string")
|
|
3891
|
+
msg = lowerer.lowerExprExpecting(args[1], STRING);
|
|
3881
3892
|
else
|
|
3882
3893
|
headersNode = args[1];
|
|
3883
3894
|
}
|
|
3884
3895
|
else if (args.length === 3) {
|
|
3885
|
-
msg =
|
|
3896
|
+
msg = lowerer.lowerExprExpecting(args[1], STRING);
|
|
3886
3897
|
headersNode = args[2];
|
|
3887
3898
|
}
|
|
3888
3899
|
// Node answers the response (`return this` — the chaining shape
|
|
@@ -3894,12 +3905,12 @@ function lowerHttpResMethodCall(L, call, access) {
|
|
|
3894
3905
|
return { kind: "libCall", fn, args: callArgs, type: VOID, loc };
|
|
3895
3906
|
}
|
|
3896
3907
|
if (msg === null)
|
|
3897
|
-
return receiverReturningCall(
|
|
3908
|
+
return receiverReturningCall(lowerer, fn, callArgs, HTTPRES_T, loc);
|
|
3898
3909
|
// With a message the helper's parameter list gains the msg at slot
|
|
3899
3910
|
// 1 (feeding the resStatusMsgSet prefix); the head call itself
|
|
3900
3911
|
// re-selects around it.
|
|
3901
3912
|
const withMsg = [callArgs[0], msg, ...callArgs.slice(1)];
|
|
3902
|
-
return receiverReturningCall(
|
|
3913
|
+
return receiverReturningCall(lowerer, fn, withMsg, HTTPRES_T, loc, {
|
|
3903
3914
|
prefix: { fn: "http.resStatusMsgSet", argIndices: [0, 1] },
|
|
3904
3915
|
mainArgIndices: [0, ...callArgs.slice(1).map((_, i) => i + 2)],
|
|
3905
3916
|
});
|
|
@@ -3915,7 +3926,7 @@ function lowerHttpResMethodCall(L, call, access) {
|
|
|
3915
3926
|
// or Node's FLAT ARRAY form ([name, value, name, value, ...]: a
|
|
3916
3927
|
// string[] value, repeated names writing one line each).
|
|
3917
3928
|
if (!ts.isObjectLiteralExpression(headersNode)) {
|
|
3918
|
-
const v =
|
|
3929
|
+
const v = lowerer.lowerExpr(headersNode);
|
|
3919
3930
|
if (v.type.kind === "array" && v.type.elem.kind === "string") {
|
|
3920
3931
|
return headResult("http.resWriteHeadPairs", [receiver, status, v]);
|
|
3921
3932
|
}
|
|
@@ -3926,11 +3937,11 @@ function lowerHttpResMethodCall(L, call, access) {
|
|
|
3926
3937
|
return headResult("http.resWriteHeadDyn", [receiver, status, v]);
|
|
3927
3938
|
}
|
|
3928
3939
|
if (v.type.kind !== "record") {
|
|
3929
|
-
|
|
3940
|
+
lowerer.noLowering(`writeHead with a '${lowerer.fmt(v.type)}' headers argument`, headersNode, "pass an object literal, a Record<string, string>, or a flat [name, value, ...] string array — or call setHeader per entry before writeHead(status)");
|
|
3930
3941
|
}
|
|
3931
|
-
const helper =
|
|
3942
|
+
const helper = lowerer.envToPairsHelper(v.type.shapeId, locOf(headersNode));
|
|
3932
3943
|
if (helper === null) {
|
|
3933
|
-
|
|
3944
|
+
lowerer.noLowering(`writeHead headers of '${lowerer.fmt(v.type)}' values`, headersNode, "header values must be strings (or string | undefined) — convert numbers with String(n)");
|
|
3934
3945
|
}
|
|
3935
3946
|
const pairs = { kind: "call", callee: helper, args: [v], type: arrayOf(STRING), loc };
|
|
3936
3947
|
return headResult("http.resWriteHeadPairs", [receiver, status, pairs]);
|
|
@@ -3938,17 +3949,17 @@ function lowerHttpResMethodCall(L, call, access) {
|
|
|
3938
3949
|
const names = [];
|
|
3939
3950
|
const values = [];
|
|
3940
3951
|
for (const prop of headersNode.properties) {
|
|
3941
|
-
const key = ts.isPropertyAssignment(prop) ? staticHeaderKeyOf(
|
|
3952
|
+
const key = ts.isPropertyAssignment(prop) ? staticHeaderKeyOf(lowerer, prop) : null;
|
|
3942
3953
|
if (!ts.isPropertyAssignment(prop) || key === null) {
|
|
3943
|
-
|
|
3954
|
+
lowerer.noLowering("writeHead headers with dynamic keys, spreads, or shorthand entries", prop, "each header must be a `name: value` entry whose key is a literal (or a string-literal-typed const: [PORTLESS_HEADER])");
|
|
3944
3955
|
}
|
|
3945
|
-
let value =
|
|
3956
|
+
let value = lowerer.lowerExpr(prop.initializer);
|
|
3946
3957
|
if (value.type.kind === "f64") {
|
|
3947
3958
|
// Node formats number values via String(n) — the same ToString.
|
|
3948
3959
|
value = { kind: "toString", operand: value, type: STRING, loc };
|
|
3949
3960
|
}
|
|
3950
3961
|
if (value.type.kind !== "string") {
|
|
3951
|
-
|
|
3962
|
+
lowerer.noLowering(`writeHead header values of type '${lowerer.fmt(value.type)}'`, prop.initializer, "header values are strings or numbers here");
|
|
3952
3963
|
}
|
|
3953
3964
|
names.push({ kind: "strLit", value: key, type: STRING, loc });
|
|
3954
3965
|
values.push(value);
|
|
@@ -3958,15 +3969,15 @@ function lowerHttpResMethodCall(L, call, access) {
|
|
|
3958
3969
|
return headResult("http.resWriteHeadN", [receiver, status, namesArr, valuesArr]);
|
|
3959
3970
|
}
|
|
3960
3971
|
if (name === "write" || name === "end") {
|
|
3961
|
-
requireStatementPosition(
|
|
3972
|
+
requireStatementPosition(lowerer, call, `res.${name}(...)`);
|
|
3962
3973
|
const minArgs = name === "write" ? 1 : 0;
|
|
3963
3974
|
const maxArgs = name === "write" ? 1 : 2;
|
|
3964
3975
|
if (args.length < minArgs || args.length > maxArgs) {
|
|
3965
|
-
|
|
3976
|
+
lowerer.noLowering(`${name} with ${args.length} arguments`, call, name === "write"
|
|
3966
3977
|
? "the supported form is write(data)"
|
|
3967
3978
|
: "the supported forms are end(), end(data), end(callback), and end(data, callback)");
|
|
3968
3979
|
}
|
|
3969
|
-
const receiver =
|
|
3980
|
+
const receiver = coerceToHandle(lowerer, access.expression, HTTPRES_T);
|
|
3970
3981
|
// end's callback forms — end(cb) / end(data, cb): the callback fires
|
|
3971
3982
|
// deferred once the body went out (Node's 'finish' emit), through
|
|
3972
3983
|
// the resOnFinish slot registered just before the end call in an
|
|
@@ -3974,15 +3985,15 @@ function lowerHttpResMethodCall(L, call, access) {
|
|
|
3974
3985
|
let cbArg = null;
|
|
3975
3986
|
let dataNode = args[0];
|
|
3976
3987
|
if (name === "end" && args.length === 2) {
|
|
3977
|
-
const { cb } = lowerCallbackArg(
|
|
3988
|
+
const { cb } = lowerCallbackArg(lowerer, args[1], "end callbacks", 0, () => false, "use ()", []);
|
|
3978
3989
|
cbArg = cb;
|
|
3979
3990
|
}
|
|
3980
3991
|
else if (name === "end" && args.length === 1 &&
|
|
3981
|
-
|
|
3982
|
-
|
|
3983
|
-
const probe =
|
|
3984
|
-
if (
|
|
3985
|
-
const { cb } = lowerCallbackArg(
|
|
3992
|
+
lowerer.mapTypeOf(lowerer.typeOf(args[0]))?.kind !== "string" &&
|
|
3993
|
+
lowerer.mapTypeOf(lowerer.typeOf(args[0]))?.kind !== "bytes") {
|
|
3994
|
+
const probe = lowerer.typeOf(args[0]);
|
|
3995
|
+
if (lowerer.mapTypeOf(probe)?.kind === "func" || lowerer.mapTypeOf(probe) === null || lowerer.mapTypeOf(probe)?.kind === "dyn") {
|
|
3996
|
+
const { cb } = lowerCallbackArg(lowerer, args[0], "end callbacks", 0, () => false, "use ()", []);
|
|
3986
3997
|
cbArg = cb;
|
|
3987
3998
|
dataNode = undefined;
|
|
3988
3999
|
}
|
|
@@ -3990,13 +4001,12 @@ function lowerHttpResMethodCall(L, call, access) {
|
|
|
3990
4001
|
const endWithCb = (fn, callArgs, cb) => {
|
|
3991
4002
|
const all = [...callArgs, cb];
|
|
3992
4003
|
const key = `server.endcb:${fn}:${all.map((a) => typeKey(a.type)).join(",")}`;
|
|
3993
|
-
const existing =
|
|
3994
|
-
const helper = existing ?? `%server.endcb.${
|
|
4004
|
+
const existing = lowerer.arrHofHelpers.get(key);
|
|
4005
|
+
const helper = existing ?? `%server.endcb.${lowerer.arrHofHelpers.size}`;
|
|
3995
4006
|
if (!existing) {
|
|
3996
|
-
|
|
4007
|
+
lowerer.arrHofHelpers.set(key, helper);
|
|
3997
4008
|
const params = all.map((a, i) => ({ localId: `p${i}.0`, name: `p${i}`, type: a.type }));
|
|
3998
|
-
|
|
3999
|
-
L.liftedFns.push({
|
|
4009
|
+
lowerer.liftedFns.push({
|
|
4000
4010
|
name: helper,
|
|
4001
4011
|
params,
|
|
4002
4012
|
returnType: VOID,
|
|
@@ -4004,12 +4014,12 @@ function lowerHttpResMethodCall(L, call, access) {
|
|
|
4004
4014
|
body: [
|
|
4005
4015
|
{
|
|
4006
4016
|
kind: "exprStmt",
|
|
4007
|
-
expr: { kind: "libCall", fn: "http.resOnFinish", args: [
|
|
4017
|
+
expr: { kind: "libCall", fn: "http.resOnFinish", args: [varRef(params[0].localId, params[0].type, loc), varRef(params[all.length - 1].localId, params[all.length - 1].type, loc)], type: VOID, loc },
|
|
4008
4018
|
loc,
|
|
4009
4019
|
},
|
|
4010
4020
|
{
|
|
4011
4021
|
kind: "exprStmt",
|
|
4012
|
-
expr: { kind: "libCall", fn, args: callArgs.map((_, i) =>
|
|
4022
|
+
expr: { kind: "libCall", fn, args: callArgs.map((_, i) => varRef(params[i].localId, params[i].type, loc)), type: VOID, loc },
|
|
4013
4023
|
loc,
|
|
4014
4024
|
},
|
|
4015
4025
|
],
|
|
@@ -4023,7 +4033,7 @@ function lowerHttpResMethodCall(L, call, access) {
|
|
|
4023
4033
|
return endWithCb("http.resEnd", [receiver], cbArg);
|
|
4024
4034
|
return { kind: "libCall", fn: "http.resEnd", args: [receiver], type: VOID, loc };
|
|
4025
4035
|
}
|
|
4026
|
-
const data =
|
|
4036
|
+
const data = lowerer.lowerExpr(dataNode);
|
|
4027
4037
|
if (data.type.kind === "string") {
|
|
4028
4038
|
const fn = name === "write" ? "http.resWrite" : "http.resEndStr";
|
|
4029
4039
|
if (cbArg !== null)
|
|
@@ -4043,7 +4053,7 @@ function lowerHttpResMethodCall(L, call, access) {
|
|
|
4043
4053
|
return endWithCb(fn, [receiver, data], cbArg);
|
|
4044
4054
|
return { kind: "libCall", fn, args: [receiver, data], type: VOID, loc };
|
|
4045
4055
|
}
|
|
4046
|
-
|
|
4056
|
+
lowerer.noLowering(`${name} of '${lowerer.fmt(data.type)}' data`, dataNode ?? call, NARROW_DATA_HINT);
|
|
4047
4057
|
}
|
|
4048
4058
|
if (name === "getHeader" || name === "hasHeader" || name === "removeHeader") {
|
|
4049
4059
|
// The header CRUD trio (setHeader's readers): getHeader answers the
|
|
@@ -4052,14 +4062,14 @@ function lowerHttpResMethodCall(L, call, access) {
|
|
|
4052
4062
|
// removeHeader drops every case-insensitive match before the head
|
|
4053
4063
|
// goes out; hasHeader is the boolean probe.
|
|
4054
4064
|
if (name === "removeHeader")
|
|
4055
|
-
requireStatementPosition(
|
|
4065
|
+
requireStatementPosition(lowerer, call, "res.removeHeader(...)");
|
|
4056
4066
|
if (args.length !== 1) {
|
|
4057
|
-
|
|
4067
|
+
lowerer.noLowering(`${name} with ${args.length} arguments`, call, `the supported form is ${name}(name)`);
|
|
4058
4068
|
}
|
|
4059
|
-
const receiver =
|
|
4060
|
-
const header =
|
|
4069
|
+
const receiver = coerceToHandle(lowerer, access.expression, HTTPRES_T);
|
|
4070
|
+
const header = lowerer.lowerExprExpecting(args[0], STRING);
|
|
4061
4071
|
if (name === "getHeader") {
|
|
4062
|
-
return { kind: "libCall", fn: "http.resGetHeader", args: [receiver, header], type:
|
|
4072
|
+
return { kind: "libCall", fn: "http.resGetHeader", args: [receiver, header], type: lowerer.envValueType(), loc };
|
|
4063
4073
|
}
|
|
4064
4074
|
if (name === "hasHeader") {
|
|
4065
4075
|
return { kind: "libCall", fn: "http.resHasHeader", args: [receiver, header], type: BOOL, loc };
|
|
@@ -4067,25 +4077,25 @@ function lowerHttpResMethodCall(L, call, access) {
|
|
|
4067
4077
|
return { kind: "libCall", fn: "http.resRemoveHeader", args: [receiver, header], type: VOID, loc };
|
|
4068
4078
|
}
|
|
4069
4079
|
if (name === "destroy") {
|
|
4070
|
-
requireStatementPosition(
|
|
4080
|
+
requireStatementPosition(lowerer, call, "res.destroy()");
|
|
4071
4081
|
if (args.length !== 0) {
|
|
4072
|
-
|
|
4082
|
+
lowerer.noLowering(`destroy with ${args.length} arguments`, call, "destroy() takes no arguments here");
|
|
4073
4083
|
}
|
|
4074
|
-
const receiver =
|
|
4084
|
+
const receiver = coerceToHandle(lowerer, access.expression, HTTPRES_T);
|
|
4075
4085
|
return { kind: "libCall", fn: "http.resDestroy", args: [receiver], type: VOID, loc };
|
|
4076
4086
|
}
|
|
4077
4087
|
if ((name === "on" || name === "once" || name === "addListener") && args.length === 2) {
|
|
4078
|
-
requireStatementPosition(
|
|
4088
|
+
requireStatementPosition(lowerer, call, `res.${name}(...)`);
|
|
4079
4089
|
const once = boolLit(name === "once", loc);
|
|
4080
|
-
const evT =
|
|
4090
|
+
const evT = lowerer.typeOf(args[0]);
|
|
4081
4091
|
const event = evT.isStringLiteralType() ? evT.value : null;
|
|
4082
|
-
const receiver =
|
|
4092
|
+
const receiver = coerceToHandle(lowerer, access.expression, HTTPRES_T);
|
|
4083
4093
|
if (event === "close") {
|
|
4084
|
-
const { cb } = lowerCallbackArg(
|
|
4094
|
+
const { cb } = lowerCallbackArg(lowerer, args[1], "close listeners", 0, () => false, "use ()", []);
|
|
4085
4095
|
return { kind: "libCall", fn: "http.resOnClose", args: [receiver, cb, once], type: VOID, loc };
|
|
4086
4096
|
}
|
|
4087
|
-
|
|
4097
|
+
lowerer.noLowering(`res.${name}(${event === null ? "non-literal event" : `"${event}"`}, ...)`, args[0], '"close" is the supported response event (as a literal)');
|
|
4088
4098
|
}
|
|
4089
|
-
|
|
4099
|
+
lowerer.noLowering(`ServerResponse.${name}`, call, "setHeader, getHeader, hasHeader, removeHeader, writeHead, write, end, destroy, headersSent, statusCode, statusMessage, and on/once of close are the supported ServerResponse members", lowerer.checker.getSymbolAtLocation(access.name));
|
|
4090
4100
|
}
|
|
4091
4101
|
//# sourceMappingURL=lower-server.js.map
|