@scriptc/compiler 0.2.7 → 0.2.8
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 +2 -0
- package/dist/backend/llvm/alloc.d.ts +3 -0
- package/dist/backend/llvm/alloc.js +54 -13
- package/dist/backend/llvm/alloc.js.map +1 -1
- package/dist/backend/llvm/blocks.d.ts +9 -0
- package/dist/backend/llvm/blocks.js +9 -0
- package/dist/backend/llvm/blocks.js.map +1 -1
- package/dist/backend/llvm/borrowed-receivers.d.ts +7 -0
- package/dist/backend/llvm/borrowed-receivers.js +39 -0
- package/dist/backend/llvm/borrowed-receivers.js.map +1 -1
- package/dist/backend/llvm/call-lifetimes.d.ts +4 -1
- package/dist/backend/llvm/call-lifetimes.js +14 -3
- package/dist/backend/llvm/call-lifetimes.js.map +1 -1
- package/dist/backend/llvm/call-lifetimes.test.js +8 -3
- package/dist/backend/llvm/call-lifetimes.test.js.map +1 -1
- package/dist/backend/llvm/checked-narrows.d.ts +24 -0
- package/dist/backend/llvm/checked-narrows.js +108 -0
- package/dist/backend/llvm/checked-narrows.js.map +1 -0
- package/dist/backend/llvm/checked-narrows.test.d.ts +1 -0
- package/dist/backend/llvm/checked-narrows.test.js +208 -0
- package/dist/backend/llvm/checked-narrows.test.js.map +1 -0
- package/dist/backend/llvm/classes.d.ts +13 -1
- package/dist/backend/llvm/classes.js +26 -18
- package/dist/backend/llvm/classes.js.map +1 -1
- package/dist/backend/llvm/classes.test.js +32 -3
- package/dist/backend/llvm/classes.test.js.map +1 -1
- package/dist/backend/llvm/debug-info.d.ts +6 -2
- package/dist/backend/llvm/debug-info.js +11 -1
- package/dist/backend/llvm/debug-info.js.map +1 -1
- package/dist/backend/llvm/dense-array-access.test.js +89 -0
- package/dist/backend/llvm/dense-array-access.test.js.map +1 -1
- package/dist/backend/llvm/dyn.d.ts +4 -0
- package/dist/backend/llvm/dyn.js +31 -20
- package/dist/backend/llvm/dyn.js.map +1 -1
- package/dist/backend/llvm/emitter.d.ts +184 -11
- package/dist/backend/llvm/emitter.js +1189 -125
- package/dist/backend/llvm/emitter.js.map +1 -1
- package/dist/backend/llvm/emitter.test.js +21 -1
- package/dist/backend/llvm/emitter.test.js.map +1 -1
- package/dist/backend/llvm/expr-async.js +4 -4
- package/dist/backend/llvm/expr-async.js.map +1 -1
- package/dist/backend/llvm/expr-bytes.d.ts +1 -1
- package/dist/backend/llvm/expr-bytes.js +85 -8
- package/dist/backend/llvm/expr-bytes.js.map +1 -1
- package/dist/backend/llvm/expr-callbacks.js +29 -21
- package/dist/backend/llvm/expr-callbacks.js.map +1 -1
- package/dist/backend/llvm/expr-calls.d.ts +5 -0
- package/dist/backend/llvm/expr-calls.js +55 -10
- package/dist/backend/llvm/expr-calls.js.map +1 -1
- package/dist/backend/llvm/expr-containers.d.ts +7 -1
- package/dist/backend/llvm/expr-containers.js +230 -27
- package/dist/backend/llvm/expr-containers.js.map +1 -1
- package/dist/backend/llvm/expr-control.js +25 -10
- package/dist/backend/llvm/expr-control.js.map +1 -1
- package/dist/backend/llvm/expr-dynamic.js +72 -21
- package/dist/backend/llvm/expr-dynamic.js.map +1 -1
- package/dist/backend/llvm/expr-island.js +3 -8
- package/dist/backend/llvm/expr-island.js.map +1 -1
- package/dist/backend/llvm/expr-primitives.js +41 -12
- package/dist/backend/llvm/expr-primitives.js.map +1 -1
- package/dist/backend/llvm/expr-primitives.test.js +50 -2
- package/dist/backend/llvm/expr-primitives.test.js.map +1 -1
- package/dist/backend/llvm/expr-records.js +28 -14
- package/dist/backend/llvm/expr-records.js.map +1 -1
- package/dist/backend/llvm/expr-stream-bridges.js +41 -31
- package/dist/backend/llvm/expr-stream-bridges.js.map +1 -1
- package/dist/backend/llvm/expr-stream-callbacks.js +6 -3
- package/dist/backend/llvm/expr-stream-callbacks.js.map +1 -1
- package/dist/backend/llvm/layouts.d.ts +2 -1
- package/dist/backend/llvm/layouts.js +2 -2
- package/dist/backend/llvm/layouts.js.map +1 -1
- package/dist/backend/llvm/lib-abi.js +10 -0
- package/dist/backend/llvm/lib-abi.js.map +1 -1
- package/dist/backend/llvm/lib-filesystem.js +7 -11
- package/dist/backend/llvm/lib-filesystem.js.map +1 -1
- package/dist/backend/llvm/lib-process.js +8 -8
- package/dist/backend/llvm/lib-process.js.map +1 -1
- package/dist/backend/llvm/lib-shared.js +1 -0
- package/dist/backend/llvm/lib-shared.js.map +1 -1
- package/dist/backend/llvm/local-array-reads.js +73 -13
- package/dist/backend/llvm/local-array-reads.js.map +1 -1
- package/dist/backend/llvm/local-array-reads.test.js +7 -3
- package/dist/backend/llvm/local-array-reads.test.js.map +1 -1
- package/dist/backend/llvm/local-union-storage.js +10 -6
- package/dist/backend/llvm/local-union-storage.js.map +1 -1
- package/dist/backend/llvm/local-union-storage.test.js +3 -2
- package/dist/backend/llvm/local-union-storage.test.js.map +1 -1
- package/dist/backend/llvm/map-number-lookup.d.ts +18 -0
- package/dist/backend/llvm/map-number-lookup.js +62 -0
- package/dist/backend/llvm/map-number-lookup.js.map +1 -0
- package/dist/backend/llvm/map-read-lifetimes.d.ts +6 -0
- package/dist/backend/llvm/map-read-lifetimes.js +24 -3
- package/dist/backend/llvm/map-read-lifetimes.js.map +1 -1
- package/dist/backend/llvm/map-read-lifetimes.test.js +38 -19
- package/dist/backend/llvm/map-read-lifetimes.test.js.map +1 -1
- package/dist/backend/llvm/nullable-unions.d.ts +68 -0
- package/dist/backend/llvm/nullable-unions.js +132 -0
- package/dist/backend/llvm/nullable-unions.js.map +1 -0
- package/dist/backend/llvm/publish.d.ts +58 -0
- package/dist/backend/llvm/publish.js +286 -0
- package/dist/backend/llvm/publish.js.map +1 -0
- package/dist/backend/llvm/rebound-parameters.d.ts +12 -0
- package/dist/backend/llvm/rebound-parameters.js +72 -0
- package/dist/backend/llvm/rebound-parameters.js.map +1 -0
- package/dist/backend/llvm/rebound-parameters.test.d.ts +1 -0
- package/dist/backend/llvm/rebound-parameters.test.js +67 -0
- package/dist/backend/llvm/rebound-parameters.test.js.map +1 -0
- package/dist/backend/llvm/reference-effects.d.ts +18 -2
- package/dist/backend/llvm/reference-effects.js +106 -16
- package/dist/backend/llvm/reference-effects.js.map +1 -1
- package/dist/backend/llvm/reference-effects.test.js +52 -1
- package/dist/backend/llvm/reference-effects.test.js.map +1 -1
- package/dist/backend/llvm/shapes.d.ts +45 -17
- package/dist/backend/llvm/shapes.js +131 -53
- package/dist/backend/llvm/shapes.js.map +1 -1
- package/dist/backend/llvm/shapes.test.js +33 -8
- package/dist/backend/llvm/shapes.test.js.map +1 -1
- package/dist/backend/llvm/stack-unions.test.js +5 -2
- package/dist/backend/llvm/stack-unions.test.js.map +1 -1
- package/dist/backend/llvm/string-construction.d.ts +10 -0
- package/dist/backend/llvm/string-construction.js +42 -0
- package/dist/backend/llvm/string-construction.js.map +1 -1
- package/dist/backend/llvm/string-key-hash.d.ts +9 -2
- package/dist/backend/llvm/string-key-hash.js +20 -3
- package/dist/backend/llvm/string-key-hash.js.map +1 -1
- package/dist/backend/llvm/string-key-hash.test.js +14 -3
- package/dist/backend/llvm/string-key-hash.test.js.map +1 -1
- package/dist/backend/llvm/string-lifetimes.test.js +24 -11
- package/dist/backend/llvm/string-lifetimes.test.js.map +1 -1
- package/dist/backend/llvm/string-reads.d.ts +7 -0
- package/dist/backend/llvm/string-reads.js +76 -0
- package/dist/backend/llvm/string-reads.js.map +1 -0
- package/dist/backend/llvm/switch-dispatch.test.js +1 -1
- package/dist/backend/llvm/switch-dispatch.test.js.map +1 -1
- package/dist/backend/llvm/test-boxed-unions.d.ts +5 -0
- package/dist/backend/llvm/test-boxed-unions.js +13 -0
- package/dist/backend/llvm/test-boxed-unions.js.map +1 -0
- package/dist/backend/llvm/union-repr.d.ts +15 -0
- package/dist/backend/llvm/union-repr.js +89 -0
- package/dist/backend/llvm/union-repr.js.map +1 -0
- package/dist/backend/llvm/virtual-borrows.d.ts +31 -0
- package/dist/backend/llvm/virtual-borrows.js +82 -0
- package/dist/backend/llvm/virtual-borrows.js.map +1 -0
- package/dist/backend/llvm/walk-borrows.d.ts +43 -0
- package/dist/backend/llvm/walk-borrows.js +150 -0
- package/dist/backend/llvm/walk-borrows.js.map +1 -0
- package/dist/backend/llvm/walk-borrows.test.d.ts +1 -0
- package/dist/backend/llvm/walk-borrows.test.js +119 -0
- package/dist/backend/llvm/walk-borrows.test.js.map +1 -0
- package/dist/backend/llvm/walkers.d.ts +5 -1
- package/dist/backend/llvm/walkers.js +24 -21
- package/dist/backend/llvm/walkers.js.map +1 -1
- package/dist/backend/may-throw.d.ts +29 -1
- package/dist/backend/may-throw.js +346 -3
- package/dist/backend/may-throw.js.map +1 -1
- package/dist/backend/may-throw.test.js +113 -1
- package/dist/backend/may-throw.test.js.map +1 -1
- package/dist/backend/native/runtime-inputs.d.ts +1 -1
- package/dist/backend/native/runtime-inputs.js +1 -0
- package/dist/backend/native/runtime-inputs.js.map +1 -1
- package/dist/backend/native-codegen-core.d.ts +7 -5
- package/dist/backend/native-codegen-core.js +14 -9
- package/dist/backend/native-codegen-core.js.map +1 -1
- package/dist/backend/native-codegen.d.ts +4 -0
- package/dist/backend/native-codegen.js +49 -2
- package/dist/backend/native-codegen.js.map +1 -1
- package/dist/backend/native-codegen.test.js +41 -10
- package/dist/backend/native-codegen.test.js.map +1 -1
- package/dist/backend/native-tools.d.ts +2 -0
- package/dist/backend/native-tools.js +3 -0
- package/dist/backend/native-tools.js.map +1 -1
- package/dist/backend/publication.d.ts +28 -0
- package/dist/backend/publication.js +155 -0
- package/dist/backend/publication.js.map +1 -0
- package/dist/backend/publication.test.d.ts +1 -0
- package/dist/backend/publication.test.js +72 -0
- package/dist/backend/publication.test.js.map +1 -0
- package/dist/backend/runtime-abi.d.ts +2 -2
- package/dist/backend/runtime-abi.js +2 -2
- package/dist/backend/runtime-pack-targets.test.js +1 -1
- package/dist/backend/runtime-pack.test.js +1 -1
- package/dist/cli/command.js +70 -7
- package/dist/cli/command.js.map +1 -1
- package/dist/cli/usage.d.ts +4 -1
- package/dist/cli/usage.js +8 -1
- package/dist/cli/usage.js.map +1 -1
- package/dist/compile-types.d.ts +12 -3
- package/dist/coverage/report.d.ts +25 -2
- package/dist/coverage/report.js +161 -33
- package/dist/coverage/report.js.map +1 -1
- package/dist/coverage/verdict-cache.d.ts +63 -0
- package/dist/coverage/verdict-cache.js +193 -0
- package/dist/coverage/verdict-cache.js.map +1 -0
- package/dist/coverage/verdict-cache.test.d.ts +1 -0
- package/dist/coverage/verdict-cache.test.js +52 -0
- package/dist/coverage/verdict-cache.test.js.map +1 -0
- package/dist/coverage/verdict-semantic.d.ts +2 -0
- package/dist/coverage/verdict-semantic.js +24 -0
- package/dist/coverage/verdict-semantic.js.map +1 -0
- package/dist/coverage/verdict-startup.d.ts +15 -0
- package/dist/coverage/verdict-startup.js +66 -0
- package/dist/coverage/verdict-startup.js.map +1 -0
- package/dist/diagnostics/diagnostic.d.ts +29 -1
- package/dist/diagnostics/diagnostic.js +74 -4
- package/dist/diagnostics/diagnostic.js.map +1 -1
- package/dist/diagnostics/envelope.d.ts +130 -0
- package/dist/diagnostics/envelope.js +257 -0
- package/dist/diagnostics/envelope.js.map +1 -0
- package/dist/diagnostics/envelope.test.d.ts +1 -0
- package/dist/diagnostics/envelope.test.js +91 -0
- package/dist/diagnostics/envelope.test.js.map +1 -0
- package/dist/diagnostics/render.d.ts +3 -0
- package/dist/diagnostics/render.js +13 -2
- package/dist/diagnostics/render.js.map +1 -1
- package/dist/executable/prepare.d.ts +4 -0
- package/dist/executable/prepare.js +7 -1
- package/dist/executable/prepare.js.map +1 -1
- package/dist/frontend/analysis.js +1 -0
- package/dist/frontend/analysis.js.map +1 -1
- package/dist/frontend/lowering/array-values.d.ts +7 -0
- package/dist/frontend/lowering/array-values.js +120 -4
- package/dist/frontend/lowering/array-values.js.map +1 -1
- package/dist/frontend/lowering/builtins/numbers.js +29 -1
- package/dist/frontend/lowering/builtins/numbers.js.map +1 -1
- package/dist/frontend/lowering/builtins/process.d.ts +4 -0
- package/dist/frontend/lowering/builtins/process.js +10 -0
- package/dist/frontend/lowering/builtins/process.js.map +1 -1
- package/dist/frontend/lowering/builtins/workers.js +4 -2
- package/dist/frontend/lowering/builtins/workers.js.map +1 -1
- package/dist/frontend/lowering/class-assertions.d.ts +8 -0
- package/dist/frontend/lowering/class-assertions.js +217 -1
- package/dist/frontend/lowering/class-assertions.js.map +1 -1
- package/dist/frontend/lowering/class-dynamic-dispatch.js +8 -5
- package/dist/frontend/lowering/class-dynamic-dispatch.js.map +1 -1
- package/dist/frontend/lowering/coercions/state.d.ts +6 -0
- package/dist/frontend/lowering/coercions/state.js +2 -0
- package/dist/frontend/lowering/coercions/state.js.map +1 -1
- package/dist/frontend/lowering/coercions/unions.d.ts +8 -0
- package/dist/frontend/lowering/coercions/unions.js +67 -2
- package/dist/frontend/lowering/coercions/unions.js.map +1 -1
- package/dist/frontend/lowering/collation-repertoire.d.ts +1 -0
- package/dist/frontend/lowering/collation-repertoire.js +19 -0
- package/dist/frontend/lowering/collation-repertoire.js.map +1 -0
- package/dist/frontend/lowering/construction-escapes.d.ts +38 -0
- package/dist/frontend/lowering/construction-escapes.js +146 -0
- package/dist/frontend/lowering/construction-escapes.js.map +1 -0
- package/dist/frontend/lowering/construction-escapes.test.d.ts +1 -0
- package/dist/frontend/lowering/construction-escapes.test.js +150 -0
- package/dist/frontend/lowering/construction-escapes.test.js.map +1 -0
- package/dist/frontend/lowering/containers/array-callbacks.js +81 -44
- package/dist/frontend/lowering/containers/array-callbacks.js.map +1 -1
- package/dist/frontend/lowering/containers/array-construction.js +15 -2
- package/dist/frontend/lowering/containers/array-construction.js.map +1 -1
- package/dist/frontend/lowering/containers/array-methods.js +231 -12
- package/dist/frontend/lowering/containers/array-methods.js.map +1 -1
- package/dist/frontend/lowering/containers/callback-arguments.js +43 -1
- package/dist/frontend/lowering/containers/callback-arguments.js.map +1 -1
- package/dist/frontend/lowering/containers/string-and-regexp.js +7 -63
- package/dist/frontend/lowering/containers/string-and-regexp.js.map +1 -1
- package/dist/frontend/lowering/cycle-initialization.d.ts +6 -1
- package/dist/frontend/lowering/cycle-initialization.js +59 -33
- package/dist/frontend/lowering/cycle-initialization.js.map +1 -1
- package/dist/frontend/lowering/divergences.d.ts +9 -0
- package/dist/frontend/lowering/divergences.js +630 -0
- package/dist/frontend/lowering/divergences.js.map +1 -0
- package/dist/frontend/lowering/expressions/object-literals.js +40 -2
- package/dist/frontend/lowering/expressions/object-literals.js.map +1 -1
- package/dist/frontend/lowering/lower-calls.d.ts +5 -0
- package/dist/frontend/lowering/lower-calls.js +226 -157
- package/dist/frontend/lowering/lower-calls.js.map +1 -1
- package/dist/frontend/lowering/lower-classes.d.ts +4 -0
- package/dist/frontend/lowering/lower-classes.js +72 -22
- package/dist/frontend/lowering/lower-classes.js.map +1 -1
- package/dist/frontend/lowering/lower-exprs.d.ts +13 -0
- package/dist/frontend/lowering/lower-exprs.js +431 -107
- package/dist/frontend/lowering/lower-exprs.js.map +1 -1
- package/dist/frontend/lowering/lower-generators.d.ts +19 -13
- package/dist/frontend/lowering/lower-generators.js +67 -44
- package/dist/frontend/lowering/lower-generators.js.map +1 -1
- package/dist/frontend/lowering/lower-inspect.js +3 -2
- package/dist/frontend/lowering/lower-inspect.js.map +1 -1
- package/dist/frontend/lowering/lower-island.d.ts +1 -1
- package/dist/frontend/lowering/lower-island.js +24 -7
- package/dist/frontend/lowering/lower-island.js.map +1 -1
- package/dist/frontend/lowering/lower-modules.js +90 -1
- package/dist/frontend/lowering/lower-modules.js.map +1 -1
- package/dist/frontend/lowering/lower-stmts.js +36 -6
- package/dist/frontend/lowering/lower-stmts.js.map +1 -1
- package/dist/frontend/lowering/lowerer.d.ts +78 -2
- package/dist/frontend/lowering/lowerer.js +986 -73
- package/dist/frontend/lowering/lowerer.js.map +1 -1
- package/dist/frontend/lowering/runtime-optional-bounds.d.ts +19 -0
- package/dist/frontend/lowering/runtime-optional-bounds.js +354 -0
- package/dist/frontend/lowering/runtime-optional-bounds.js.map +1 -0
- package/dist/frontend/lowering/runtime-optional-bounds.test.d.ts +1 -0
- package/dist/frontend/lowering/runtime-optional-bounds.test.js +66 -0
- package/dist/frontend/lowering/runtime-optional-bounds.test.js.map +1 -0
- package/dist/frontend/lowering/runtime-optional-elements.d.ts +117 -0
- package/dist/frontend/lowering/runtime-optional-elements.js +738 -0
- package/dist/frontend/lowering/runtime-optional-elements.js.map +1 -0
- package/dist/frontend/lowering/runtime-optional-guards.d.ts +36 -0
- package/dist/frontend/lowering/runtime-optional-guards.js +375 -0
- package/dist/frontend/lowering/runtime-optional-guards.js.map +1 -0
- package/dist/frontend/lowering/runtime-optional-guards.test.d.ts +1 -0
- package/dist/frontend/lowering/runtime-optional-guards.test.js +92 -0
- package/dist/frontend/lowering/runtime-optional-guards.test.js.map +1 -0
- package/dist/frontend/lowering/runtime-optional-ownership.d.ts +63 -0
- package/dist/frontend/lowering/runtime-optional-ownership.js +382 -0
- package/dist/frontend/lowering/runtime-optional-ownership.js.map +1 -0
- package/dist/frontend/lowering/runtime-optional-provenance.d.ts +2 -1
- package/dist/frontend/lowering/runtime-optional-provenance.js +2 -1
- package/dist/frontend/lowering/runtime-optional-provenance.js.map +1 -1
- package/dist/frontend/lowering/string-index-bounds.d.ts +36 -0
- package/dist/frontend/lowering/string-index-bounds.js +537 -0
- package/dist/frontend/lowering/string-index-bounds.js.map +1 -0
- package/dist/frontend/lowering/string-index-bounds.test.d.ts +1 -0
- package/dist/frontend/lowering/string-index-bounds.test.js +223 -0
- package/dist/frontend/lowering/string-index-bounds.test.js.map +1 -0
- package/dist/frontend/lowering/union-narrow.d.ts +2 -0
- package/dist/frontend/lowering/union-narrow.js +10 -6
- package/dist/frontend/lowering/union-narrow.js.map +1 -1
- package/dist/frontend/program-analysis.d.ts +10 -0
- package/dist/frontend/program-analysis.js +7 -0
- package/dist/frontend/program-analysis.js.map +1 -1
- package/dist/frontend/program.d.ts +25 -0
- package/dist/frontend/program.js +613 -17
- package/dist/frontend/program.js.map +1 -1
- package/dist/frontend/program.test.js +64 -1
- package/dist/frontend/program.test.js.map +1 -1
- package/dist/frontend/resolve.d.ts +6 -0
- package/dist/frontend/resolve.js +14 -0
- package/dist/frontend/resolve.js.map +1 -1
- package/dist/frontend/threads-import.d.ts +5 -0
- package/dist/frontend/threads-import.js +21 -0
- package/dist/frontend/threads-import.js.map +1 -0
- package/dist/frontend/type-mapper.d.ts +3 -0
- package/dist/frontend/type-mapper.js +72 -6
- package/dist/frontend/type-mapper.js.map +1 -1
- package/dist/frontend/worker-target.d.ts +4 -0
- package/dist/frontend/worker-target.js +8 -1
- package/dist/frontend/worker-target.js.map +1 -1
- package/dist/index.d.ts +6 -1
- package/dist/index.js +47 -9
- package/dist/index.js.map +1 -1
- package/dist/ir/build.d.ts +5 -0
- package/dist/ir/build.js +7 -0
- package/dist/ir/build.js.map +1 -1
- package/dist/ir/builtin-signatures.d.ts +1 -1
- package/dist/ir/builtin-signatures.js +3 -0
- package/dist/ir/builtin-signatures.js.map +1 -1
- package/dist/ir/int32-slots.d.ts +72 -0
- package/dist/ir/int32-slots.js +837 -0
- package/dist/ir/int32-slots.js.map +1 -0
- package/dist/ir/int32-slots.test.d.ts +1 -0
- package/dist/ir/int32-slots.test.js +222 -0
- package/dist/ir/int32-slots.test.js.map +1 -0
- package/dist/ir/integer-ranges.d.ts +19 -3
- package/dist/ir/integer-ranges.js +168 -23
- package/dist/ir/integer-ranges.js.map +1 -1
- package/dist/ir/integer-ranges.test.js +74 -2
- package/dist/ir/integer-ranges.test.js.map +1 -1
- package/dist/ir/ir.d.ts +24 -6
- package/dist/ir/ir.js.map +1 -1
- package/dist/ir/validate.js +21 -2
- package/dist/ir/validate.js.map +1 -1
- package/dist/ir/validate.test.js +39 -0
- package/dist/ir/validate.test.js.map +1 -1
- package/dist/native/cache.d.ts +3 -0
- package/dist/native/cache.js +49 -1
- package/dist/native/cache.js.map +1 -1
- package/dist/native/cache.test.js +21 -1
- package/dist/native/cache.test.js.map +1 -1
- package/dist/native/cli.js +1 -1
- package/dist/native/cli.js.map +1 -1
- package/dist/native/compiler.d.ts +5 -0
- package/dist/native/compiler.js +51 -4
- package/dist/native/compiler.js.map +1 -1
- package/dist/native/prepare.d.ts +4 -0
- package/dist/native/prepare.js +2 -1
- package/dist/native/prepare.js.map +1 -1
- package/dist/startup-cache.d.ts +2 -0
- package/dist/startup-cache.js +2 -0
- package/dist/startup-cache.js.map +1 -1
- package/package.json +20 -20
- package/surface-manifest.json +1 -1
|
@@ -0,0 +1,837 @@
|
|
|
1
|
+
import { analyzeIntegerRanges, INT32_RANGE, withinInt32, } from "./integer-ranges.js";
|
|
2
|
+
import { findInitializerBindings, withInitializerBindings } from "./initializer-bindings.js";
|
|
3
|
+
import { everyExprChild, everyStmtChild, everyTypeChild } from "./traverse.js";
|
|
4
|
+
/** Whole-program int32 specialization of numeric storage.
|
|
5
|
+
*
|
|
6
|
+
* A class field typed `number` whose every write provably stores an int32
|
|
7
|
+
* (never -0, never NaN, never a fraction or an out-of-range integer) can be
|
|
8
|
+
* stored as an i32: reading it back as a double is exact, so every program
|
|
9
|
+
* observation is unchanged. The same proof seeds parameters of functions
|
|
10
|
+
* that are only ever called directly (every call site passes an int32) and
|
|
11
|
+
* results of functions whose every return is an int32. Parameter and result
|
|
12
|
+
* ABIs stay double; only the facts reach the callee and the call sites.
|
|
13
|
+
*
|
|
14
|
+
* The analysis is optimistic: every candidate slot starts as int32, each
|
|
15
|
+
* function is analyzed by analyzeIntegerRanges under the current facts, and
|
|
16
|
+
* any write the analysis cannot prove disqualifies its slot. Functions that
|
|
17
|
+
* read a disqualified slot are analyzed again, until no write fails. At the
|
|
18
|
+
* fixpoint every assumption was proven against every write.
|
|
19
|
+
*
|
|
20
|
+
* Writes the IR does not show disqualify a slot up front. Instances that
|
|
21
|
+
* reach checked-dynamic code get live views whose runtime stores commit
|
|
22
|
+
* back into the fields; a field of such a class stays a double when any
|
|
23
|
+
* dynamic store in the program can name it (literal-keyed stores name one
|
|
24
|
+
* property, every other store any property, islands everything). Classes
|
|
25
|
+
* with runtime-provided ancestors, generic families, fields under
|
|
26
|
+
* `++`/`--`, and parameters of functions that escape as values, are
|
|
27
|
+
* constructed through class values, or are virtual-dispatch targets are
|
|
28
|
+
* excluded as well. Library builds keep ordinary storage: their instances
|
|
29
|
+
* cross a native boundary. Worker builds do not: instances reach another
|
|
30
|
+
* thread only through postMessage/workerData, whose dyn views read (and
|
|
31
|
+
* commit) fields like every other dynamic reader. Reads are never
|
|
32
|
+
* restricted: every reader (static code, dyn views, JSON, inspect) widens
|
|
33
|
+
* the i32 exactly. */
|
|
34
|
+
export class Int32Slots {
|
|
35
|
+
classes;
|
|
36
|
+
/** Field families (topmost declaring class and field) stored as i32. */
|
|
37
|
+
fields = new Set();
|
|
38
|
+
familyOf = new Map();
|
|
39
|
+
paramFacts = new Map();
|
|
40
|
+
returns = new Set();
|
|
41
|
+
/** Captured-binding groups (one shared box) that hold an int32, and the
|
|
42
|
+
* group of each `function\0local` member. */
|
|
43
|
+
boxes = new Set();
|
|
44
|
+
boxGroupOf = new Map();
|
|
45
|
+
/** Field write values proven int32 by the final analysis. */
|
|
46
|
+
provenWrites = new Set();
|
|
47
|
+
/** Disqualified candidates and the first reason, for tests. */
|
|
48
|
+
reasons = new Map();
|
|
49
|
+
constructor(classes = new Map()) {
|
|
50
|
+
this.classes = classes;
|
|
51
|
+
}
|
|
52
|
+
/** The family key of a class field: the topmost class in the base chain
|
|
53
|
+
* that declares a field of that name (all subclasses share its slot). */
|
|
54
|
+
family(className, field) {
|
|
55
|
+
const key = `${className}\0${field}`;
|
|
56
|
+
const cached = this.familyOf.get(key);
|
|
57
|
+
if (cached !== undefined)
|
|
58
|
+
return cached;
|
|
59
|
+
let owner = this.classes.get(className);
|
|
60
|
+
let result = null;
|
|
61
|
+
if (owner?.fields.some((f) => f.name === field)) {
|
|
62
|
+
for (;;) {
|
|
63
|
+
const base = owner.base ? this.classes.get(owner.base) : undefined;
|
|
64
|
+
if (!base?.fields.some((f) => f.name === field))
|
|
65
|
+
break;
|
|
66
|
+
owner = base;
|
|
67
|
+
}
|
|
68
|
+
result = `${owner.name}\0${field}`;
|
|
69
|
+
}
|
|
70
|
+
this.familyOf.set(key, result);
|
|
71
|
+
return result;
|
|
72
|
+
}
|
|
73
|
+
isField(className, field) {
|
|
74
|
+
if (this.fields.size === 0)
|
|
75
|
+
return false;
|
|
76
|
+
const family = this.family(className, field);
|
|
77
|
+
return family !== null && this.fields.has(family);
|
|
78
|
+
}
|
|
79
|
+
/** The facts analyzeIntegerRanges may assume inside one function. */
|
|
80
|
+
facts(fnName) {
|
|
81
|
+
if (this.fields.size === 0 &&
|
|
82
|
+
this.returns.size === 0 &&
|
|
83
|
+
this.paramFacts.size === 0 &&
|
|
84
|
+
this.boxes.size === 0)
|
|
85
|
+
return {};
|
|
86
|
+
const result = {
|
|
87
|
+
field: (className, field) => (this.isField(className, field) ? INT32_RANGE : null),
|
|
88
|
+
call: (callee) => (this.returns.has(callee) ? INT32_RANGE : null),
|
|
89
|
+
boxed: (localId) => (this.hasBox(this.boxGroup(fnName, localId)) ? INT32_RANGE : null),
|
|
90
|
+
};
|
|
91
|
+
const params = this.paramFacts.get(fnName);
|
|
92
|
+
if (params)
|
|
93
|
+
result.params = params;
|
|
94
|
+
return result;
|
|
95
|
+
}
|
|
96
|
+
boxGroup(fnName, localId) {
|
|
97
|
+
return this.boxGroupOf.get(`${fnName}\0${localId}`) ?? null;
|
|
98
|
+
}
|
|
99
|
+
/* ── construction (analyzeInt32Slots only) ── */
|
|
100
|
+
addBox(group, members) {
|
|
101
|
+
this.boxes.add(group);
|
|
102
|
+
for (const member of members)
|
|
103
|
+
this.boxGroupOf.set(member, group);
|
|
104
|
+
}
|
|
105
|
+
dropBox(group) {
|
|
106
|
+
return this.boxes.delete(group);
|
|
107
|
+
}
|
|
108
|
+
hasBox(group) {
|
|
109
|
+
return group !== null && this.boxes.has(group);
|
|
110
|
+
}
|
|
111
|
+
addField(family) {
|
|
112
|
+
this.fields.add(family);
|
|
113
|
+
}
|
|
114
|
+
dropField(family) {
|
|
115
|
+
return this.fields.delete(family);
|
|
116
|
+
}
|
|
117
|
+
hasFamily(family) {
|
|
118
|
+
return this.fields.has(family);
|
|
119
|
+
}
|
|
120
|
+
addParam(fnName, localId) {
|
|
121
|
+
let params = this.paramFacts.get(fnName);
|
|
122
|
+
if (!params)
|
|
123
|
+
this.paramFacts.set(fnName, (params = new Map()));
|
|
124
|
+
params.set(localId, INT32_RANGE);
|
|
125
|
+
}
|
|
126
|
+
dropParam(fnName, localId) {
|
|
127
|
+
const params = this.paramFacts.get(fnName);
|
|
128
|
+
if (!params?.delete(localId))
|
|
129
|
+
return false;
|
|
130
|
+
if (params.size === 0)
|
|
131
|
+
this.paramFacts.delete(fnName);
|
|
132
|
+
return true;
|
|
133
|
+
}
|
|
134
|
+
hasParam(fnName, localId) {
|
|
135
|
+
return this.paramFacts.get(fnName)?.has(localId) ?? false;
|
|
136
|
+
}
|
|
137
|
+
addReturn(fnName) {
|
|
138
|
+
this.returns.add(fnName);
|
|
139
|
+
}
|
|
140
|
+
dropReturn(fnName) {
|
|
141
|
+
return this.returns.delete(fnName);
|
|
142
|
+
}
|
|
143
|
+
hasReturn(fnName) {
|
|
144
|
+
return this.returns.has(fnName);
|
|
145
|
+
}
|
|
146
|
+
get fieldCount() {
|
|
147
|
+
return this.fields.size;
|
|
148
|
+
}
|
|
149
|
+
get returnCount() {
|
|
150
|
+
return this.returns.size;
|
|
151
|
+
}
|
|
152
|
+
get paramCount() {
|
|
153
|
+
let count = 0;
|
|
154
|
+
for (const params of this.paramFacts.values())
|
|
155
|
+
count += params.size;
|
|
156
|
+
return count;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
const DYN_KINDS = new Set(["dyn", "jsval", "caught"]);
|
|
160
|
+
/** Expressions that hand their operands (or produce results) to runtime
|
|
161
|
+
* helpers, reflection, promises or other code the IR does not show. */
|
|
162
|
+
const BOUNDARY_KINDS = new Set([
|
|
163
|
+
"libCall",
|
|
164
|
+
"intrinsic",
|
|
165
|
+
"jsonStringify",
|
|
166
|
+
"ffiCall",
|
|
167
|
+
"jsOp",
|
|
168
|
+
"jsExit",
|
|
169
|
+
"jsBridgePromise",
|
|
170
|
+
"jsMarshal",
|
|
171
|
+
"dynCall",
|
|
172
|
+
"dynInvoke",
|
|
173
|
+
"dynFrom",
|
|
174
|
+
"dynFromJsval",
|
|
175
|
+
"dynCheck",
|
|
176
|
+
"dynObjLit",
|
|
177
|
+
"dynArrLit",
|
|
178
|
+
"caughtToDyn",
|
|
179
|
+
"caughtNarrow",
|
|
180
|
+
"caughtCheck",
|
|
181
|
+
]);
|
|
182
|
+
/** Runtime helpers that receive dynamic values but never store a property
|
|
183
|
+
* into a live class capsule (verified against the runtime's call graph:
|
|
184
|
+
* none of them reaches scr_dyn_typed_ref_commit with a caller-supplied
|
|
185
|
+
* target). Any other helper that receives a dynamic value may store any
|
|
186
|
+
* property, except literal-keyed `dyn.keySet`/`dyn.keyDelete`. */
|
|
187
|
+
const DYN_READ_ONLY = new Set([
|
|
188
|
+
"arrayBuffer.new",
|
|
189
|
+
"arrayBuffer.viewF32",
|
|
190
|
+
"arrayBuffer.viewF64",
|
|
191
|
+
"arrayBuffer.viewI16",
|
|
192
|
+
"arrayBuffer.viewI32",
|
|
193
|
+
"arrayBuffer.viewI8",
|
|
194
|
+
"arrayBuffer.viewU16",
|
|
195
|
+
"arrayBuffer.viewU32",
|
|
196
|
+
"arrayBuffer.viewU8",
|
|
197
|
+
"arrayBuffer.viewU8C",
|
|
198
|
+
"date.nativeNew",
|
|
199
|
+
"dyn.arrAt",
|
|
200
|
+
"dyn.arrLen",
|
|
201
|
+
"dyn.bagGet",
|
|
202
|
+
"dyn.compare",
|
|
203
|
+
"dyn.hasKey",
|
|
204
|
+
"dyn.iterator",
|
|
205
|
+
"dyn.iteratorCanStep",
|
|
206
|
+
"dyn.iteratorResult",
|
|
207
|
+
"dyn.iteratorStep",
|
|
208
|
+
"dyn.iteratorStepDone",
|
|
209
|
+
"dyn.mapSeedEntries",
|
|
210
|
+
"dyn.mapSeedEntry",
|
|
211
|
+
"dyn.numberConstructor",
|
|
212
|
+
"dyn.packPush",
|
|
213
|
+
"dyn.packPushSpread",
|
|
214
|
+
"dyn.stringConstructor",
|
|
215
|
+
"dyn.this",
|
|
216
|
+
"dyn.toStringCoerce",
|
|
217
|
+
"dyn.typedRefIs",
|
|
218
|
+
"error.new",
|
|
219
|
+
"error.newOptions",
|
|
220
|
+
"fs.utimesSync",
|
|
221
|
+
"json.stringifyReplacer",
|
|
222
|
+
"json.stringifyValue",
|
|
223
|
+
"num.toStringRadix",
|
|
224
|
+
"sharedArrayBuffer.new",
|
|
225
|
+
"text.decodeStream",
|
|
226
|
+
"worker.new",
|
|
227
|
+
]);
|
|
228
|
+
const ISLAND_KINDS = new Set([
|
|
229
|
+
"jsOp",
|
|
230
|
+
"jsMarshal",
|
|
231
|
+
"jsExit",
|
|
232
|
+
"jsBridgePromise",
|
|
233
|
+
"dynFromJsval",
|
|
234
|
+
]);
|
|
235
|
+
/** IR metadata keys whose strings never name a function. */
|
|
236
|
+
const NON_REFERENCE_KEYS = new Set([
|
|
237
|
+
"loc",
|
|
238
|
+
"source",
|
|
239
|
+
"name",
|
|
240
|
+
"sourceName",
|
|
241
|
+
"jsName",
|
|
242
|
+
"localId",
|
|
243
|
+
"type",
|
|
244
|
+
"returnType",
|
|
245
|
+
]);
|
|
246
|
+
function walkFunction(fn, visit) {
|
|
247
|
+
const expr = (e) => {
|
|
248
|
+
visit.expr?.(e);
|
|
249
|
+
return everyExprChild(e, expr, stmt);
|
|
250
|
+
};
|
|
251
|
+
const stmt = (s) => {
|
|
252
|
+
visit.stmt?.(s);
|
|
253
|
+
return everyStmtChild(s, expr, stmt);
|
|
254
|
+
};
|
|
255
|
+
for (const s of fn.body)
|
|
256
|
+
stmt(s);
|
|
257
|
+
}
|
|
258
|
+
export function analyzeInt32Slots(mod) {
|
|
259
|
+
const classes = new Map((mod.classes ?? []).map((c) => [c.name, c]));
|
|
260
|
+
const slots = new Int32Slots(classes);
|
|
261
|
+
if (mod.lib)
|
|
262
|
+
return slots;
|
|
263
|
+
const reason = (key, why) => {
|
|
264
|
+
if (!slots.reasons.has(key))
|
|
265
|
+
slots.reasons.set(key, why);
|
|
266
|
+
};
|
|
267
|
+
/* ── candidate fields ── */
|
|
268
|
+
const children = new Map();
|
|
269
|
+
for (const cls of classes.values())
|
|
270
|
+
if (cls.base) {
|
|
271
|
+
const list = children.get(cls.base);
|
|
272
|
+
if (list)
|
|
273
|
+
list.push(cls.name);
|
|
274
|
+
else
|
|
275
|
+
children.set(cls.base, [cls.name]);
|
|
276
|
+
}
|
|
277
|
+
const subtree = (name) => {
|
|
278
|
+
const out = [];
|
|
279
|
+
const stack = [name];
|
|
280
|
+
while (stack.length) {
|
|
281
|
+
const next = stack.pop();
|
|
282
|
+
out.push(next);
|
|
283
|
+
for (const child of children.get(next) ?? [])
|
|
284
|
+
stack.push(child);
|
|
285
|
+
}
|
|
286
|
+
return out;
|
|
287
|
+
};
|
|
288
|
+
const genericFamilies = new Set();
|
|
289
|
+
for (const cls of classes.values())
|
|
290
|
+
if (cls.genericOf !== undefined) {
|
|
291
|
+
genericFamilies.add(cls.name);
|
|
292
|
+
genericFamilies.add(cls.genericOf);
|
|
293
|
+
}
|
|
294
|
+
const plainClass = (cls) => {
|
|
295
|
+
for (let c = cls; c; c = c.base ? classes.get(c.base) : undefined) {
|
|
296
|
+
if (c.runtime || genericFamilies.has(c.name))
|
|
297
|
+
return false;
|
|
298
|
+
if (c.base && !classes.has(c.base))
|
|
299
|
+
return false;
|
|
300
|
+
}
|
|
301
|
+
return true;
|
|
302
|
+
};
|
|
303
|
+
const dropFamily = (family, why) => {
|
|
304
|
+
reason(`field:${family}`, why);
|
|
305
|
+
slots.dropField(family);
|
|
306
|
+
};
|
|
307
|
+
for (const cls of classes.values())
|
|
308
|
+
for (const field of cls.fields) {
|
|
309
|
+
if (field.type.kind !== "f64" || field.name.startsWith("%"))
|
|
310
|
+
continue;
|
|
311
|
+
const family = slots.family(cls.name, field.name);
|
|
312
|
+
if (plainClass(cls)) {
|
|
313
|
+
if (!slots.reasons.has(`field:${family}`))
|
|
314
|
+
slots.addField(family);
|
|
315
|
+
}
|
|
316
|
+
else
|
|
317
|
+
dropFamily(family, "runtime-provided or generic class");
|
|
318
|
+
}
|
|
319
|
+
/* ── dynamic, reflective and runtime exposure ── */
|
|
320
|
+
const records = new Map((mod.records ?? []).map((r) => [r.id, r]));
|
|
321
|
+
const unions = new Map((mod.unions ?? []).map((u) => [u.id, u]));
|
|
322
|
+
const exposedClasses = new Set();
|
|
323
|
+
const seenNamed = new Set();
|
|
324
|
+
let exposeAll = false;
|
|
325
|
+
const expose = (type) => {
|
|
326
|
+
if (exposeAll)
|
|
327
|
+
return;
|
|
328
|
+
switch (type.kind) {
|
|
329
|
+
case "object":
|
|
330
|
+
case "classval":
|
|
331
|
+
// A live dyn view of an instance materializes its fields as live
|
|
332
|
+
// views in turn: everything reachable from an exposed class is exposed.
|
|
333
|
+
for (const name of subtree(type.className)) {
|
|
334
|
+
if (exposedClasses.has(name))
|
|
335
|
+
continue;
|
|
336
|
+
exposedClasses.add(name);
|
|
337
|
+
for (const field of classes.get(name)?.fields ?? [])
|
|
338
|
+
expose(field.type);
|
|
339
|
+
}
|
|
340
|
+
return;
|
|
341
|
+
case "moduleNs":
|
|
342
|
+
exposeAll = true;
|
|
343
|
+
return;
|
|
344
|
+
case "record": {
|
|
345
|
+
if (seenNamed.has(`r:${type.shapeId}`))
|
|
346
|
+
return;
|
|
347
|
+
seenNamed.add(`r:${type.shapeId}`);
|
|
348
|
+
const shape = records.get(type.shapeId);
|
|
349
|
+
for (const field of shape?.fields ?? [])
|
|
350
|
+
expose(field.type);
|
|
351
|
+
if (shape?.indexValue)
|
|
352
|
+
expose(shape.indexValue);
|
|
353
|
+
return;
|
|
354
|
+
}
|
|
355
|
+
case "union": {
|
|
356
|
+
if (seenNamed.has(`u:${type.unionId}`))
|
|
357
|
+
return;
|
|
358
|
+
seenNamed.add(`u:${type.unionId}`);
|
|
359
|
+
for (const arm of unions.get(type.unionId)?.arms ?? [])
|
|
360
|
+
expose(arm);
|
|
361
|
+
return;
|
|
362
|
+
}
|
|
363
|
+
default:
|
|
364
|
+
everyTypeChild(type, (child) => {
|
|
365
|
+
expose(child);
|
|
366
|
+
return true;
|
|
367
|
+
});
|
|
368
|
+
}
|
|
369
|
+
};
|
|
370
|
+
// A live dyn view (typed-ref capsule) of an instance writes back into
|
|
371
|
+
// its fields only after a runtime store into the capsule; the commit then
|
|
372
|
+
// writes every field from the view, which holds the exact snapshot value
|
|
373
|
+
// of any property no store named. So reaching dynamic code disqualifies
|
|
374
|
+
// exactly the field names some dynamic store in the program can name.
|
|
375
|
+
const dynTypes = new Map();
|
|
376
|
+
const dynNamed = new Map();
|
|
377
|
+
const dynish = (type) => {
|
|
378
|
+
if (DYN_KINDS.has(type.kind))
|
|
379
|
+
return true;
|
|
380
|
+
const cached = dynTypes.get(type);
|
|
381
|
+
if (cached !== undefined)
|
|
382
|
+
return cached;
|
|
383
|
+
let result = false;
|
|
384
|
+
const named = type.kind === "record"
|
|
385
|
+
? `r:${type.shapeId}`
|
|
386
|
+
: type.kind === "union"
|
|
387
|
+
? `u:${type.unionId}`
|
|
388
|
+
: null;
|
|
389
|
+
if (named) {
|
|
390
|
+
const known = dynNamed.get(named);
|
|
391
|
+
if (known !== undefined)
|
|
392
|
+
return known;
|
|
393
|
+
dynNamed.set(named, false);
|
|
394
|
+
const members = type.kind === "record"
|
|
395
|
+
? [
|
|
396
|
+
...(records.get(type.shapeId)?.fields.map((f) => f.type) ?? []),
|
|
397
|
+
...(records.get(type.shapeId)?.indexValue
|
|
398
|
+
? [records.get(type.shapeId).indexValue]
|
|
399
|
+
: []),
|
|
400
|
+
]
|
|
401
|
+
: type.kind === "union"
|
|
402
|
+
? (unions.get(type.unionId)?.arms ?? [])
|
|
403
|
+
: [];
|
|
404
|
+
result = members.some(dynish);
|
|
405
|
+
dynNamed.set(named, result);
|
|
406
|
+
}
|
|
407
|
+
else
|
|
408
|
+
everyTypeChild(type, (child) => !(result = dynish(child)));
|
|
409
|
+
dynTypes.set(type, result);
|
|
410
|
+
return result;
|
|
411
|
+
};
|
|
412
|
+
const dynWritten = new Set();
|
|
413
|
+
let dynWritesAll = false;
|
|
414
|
+
for (const fn of mod.functions)
|
|
415
|
+
walkFunction(fn, {
|
|
416
|
+
expr: (e) => {
|
|
417
|
+
if (dynWritesAll)
|
|
418
|
+
return;
|
|
419
|
+
if (ISLAND_KINDS.has(e.kind) || e.type.kind === "jsval")
|
|
420
|
+
dynWritesAll = true;
|
|
421
|
+
else if (e.kind === "libCall" &&
|
|
422
|
+
!DYN_READ_ONLY.has(e.fn) &&
|
|
423
|
+
e.args.some((a) => dynish(a.type))) {
|
|
424
|
+
const key = e.args[1];
|
|
425
|
+
if ((e.fn === "dyn.keySet" || e.fn === "dyn.keyDelete") && key?.kind === "strLit")
|
|
426
|
+
dynWritten.add(key.value);
|
|
427
|
+
else
|
|
428
|
+
dynWritesAll = true;
|
|
429
|
+
}
|
|
430
|
+
},
|
|
431
|
+
});
|
|
432
|
+
for (const fn of mod.functions)
|
|
433
|
+
walkFunction(fn, {
|
|
434
|
+
expr: (e) => {
|
|
435
|
+
const dynResult = DYN_KINDS.has(e.type.kind);
|
|
436
|
+
const boundary = BOUNDARY_KINDS.has(e.kind);
|
|
437
|
+
let dynChild = false;
|
|
438
|
+
everyExprChild(e, (child) => {
|
|
439
|
+
const dyn = DYN_KINDS.has(child.type.kind);
|
|
440
|
+
dynChild ||= dyn;
|
|
441
|
+
if (boundary || (dynResult && !dyn))
|
|
442
|
+
expose(child.type);
|
|
443
|
+
return true;
|
|
444
|
+
}, () => true);
|
|
445
|
+
// Error helpers construct runtime errors or always throw: their
|
|
446
|
+
// class-typed result is an Error instance or an unreachable dummy.
|
|
447
|
+
const errorHelper = e.kind === "libCall" && e.fn.startsWith("error.");
|
|
448
|
+
if ((boundary && !errorHelper) || (dynChild && !dynResult))
|
|
449
|
+
expose(e.type);
|
|
450
|
+
},
|
|
451
|
+
stmt: (s) => {
|
|
452
|
+
if (s.kind === "throw")
|
|
453
|
+
expose(s.value.type);
|
|
454
|
+
},
|
|
455
|
+
});
|
|
456
|
+
for (const cls of classes.values()) {
|
|
457
|
+
if (!exposeAll && !exposedClasses.has(cls.name))
|
|
458
|
+
continue;
|
|
459
|
+
for (const field of cls.fields)
|
|
460
|
+
if (field.type.kind === "f64" &&
|
|
461
|
+
!field.name.startsWith("%") &&
|
|
462
|
+
(dynWritesAll || dynWritten.has(field.name)))
|
|
463
|
+
dropFamily(slots.family(cls.name, field.name), "dynamic stores can name the field");
|
|
464
|
+
}
|
|
465
|
+
/* ── functions: escapes, parameter and return candidates ── */
|
|
466
|
+
const functions = new Map(mod.functions.map((fn) => [fn.name, fn]));
|
|
467
|
+
const escaped = new Set();
|
|
468
|
+
const seenObjects = new Set();
|
|
469
|
+
const scan = (value, key, parent) => {
|
|
470
|
+
if (typeof value === "string") {
|
|
471
|
+
if (functions.has(value)) {
|
|
472
|
+
const direct = key === "callee" && parent?.kind === "call"
|
|
473
|
+
? true
|
|
474
|
+
: key === "value" && parent?.kind === "strLit";
|
|
475
|
+
if (!direct)
|
|
476
|
+
escaped.add(value);
|
|
477
|
+
}
|
|
478
|
+
return;
|
|
479
|
+
}
|
|
480
|
+
if (value === null || typeof value !== "object" || seenObjects.has(value))
|
|
481
|
+
return;
|
|
482
|
+
seenObjects.add(value);
|
|
483
|
+
if (Array.isArray(value)) {
|
|
484
|
+
for (const item of value)
|
|
485
|
+
scan(item, null, null);
|
|
486
|
+
return;
|
|
487
|
+
}
|
|
488
|
+
const record = value;
|
|
489
|
+
for (const [k, v] of Object.entries(record))
|
|
490
|
+
if (!NON_REFERENCE_KEYS.has(k))
|
|
491
|
+
scan(v, k, record);
|
|
492
|
+
};
|
|
493
|
+
scan(mod, null, null);
|
|
494
|
+
if (typeof mod.entry === "string")
|
|
495
|
+
escaped.add(mod.entry);
|
|
496
|
+
// Virtual dispatch: a `virtualCall` can reach the method's implementation
|
|
497
|
+
// in any class of the receiver's subtree or the one it inherits. Classes
|
|
498
|
+
// under a runtime-provided root keep every method escaped: the runtime
|
|
499
|
+
// calls their vtable entries directly.
|
|
500
|
+
const runtimeRooted = (name) => {
|
|
501
|
+
for (let c = classes.get(name); c; c = c.base ? classes.get(c.base) : undefined)
|
|
502
|
+
if (c.runtime || (c.base && !classes.has(c.base)))
|
|
503
|
+
return true;
|
|
504
|
+
return false;
|
|
505
|
+
};
|
|
506
|
+
const runtimePrefixes = [];
|
|
507
|
+
for (const cls of classes.values())
|
|
508
|
+
if (runtimeRooted(cls.name))
|
|
509
|
+
runtimePrefixes.push(`%${cls.name}.`);
|
|
510
|
+
for (const fn of mod.functions)
|
|
511
|
+
walkFunction(fn, {
|
|
512
|
+
expr: (e) => {
|
|
513
|
+
if (e.kind !== "virtualCall")
|
|
514
|
+
return;
|
|
515
|
+
for (const name of subtree(e.className))
|
|
516
|
+
escaped.add(`%${name}.${e.method}`);
|
|
517
|
+
for (let c = classes.get(e.className); c; c = c.base ? classes.get(c.base) : undefined)
|
|
518
|
+
escaped.add(`%${c.name}.${e.method}`);
|
|
519
|
+
},
|
|
520
|
+
});
|
|
521
|
+
// A class value can reach `new` through its construct thunk; one that
|
|
522
|
+
// only feeds an instanceof interval check cannot.
|
|
523
|
+
const testedOnly = new Set();
|
|
524
|
+
for (const fn of mod.functions) {
|
|
525
|
+
// Direct operands, and unboxed locals bound once to a class reference
|
|
526
|
+
// and read only as instanceof operands (the lowering's temporary).
|
|
527
|
+
const bound = new Map();
|
|
528
|
+
const reads = new Map();
|
|
529
|
+
const tests = new Map();
|
|
530
|
+
const writes = new Map();
|
|
531
|
+
const count = (map, id) => {
|
|
532
|
+
map.set(id, (map.get(id) ?? 0) + 1);
|
|
533
|
+
};
|
|
534
|
+
walkFunction(fn, {
|
|
535
|
+
expr: (e) => {
|
|
536
|
+
if (e.kind === "instanceOfValue") {
|
|
537
|
+
if (e.classValue.kind === "classRef")
|
|
538
|
+
testedOnly.add(e.classValue);
|
|
539
|
+
else if (e.classValue.kind === "varRef")
|
|
540
|
+
count(tests, e.classValue.localId);
|
|
541
|
+
}
|
|
542
|
+
else if (e.kind === "varRef")
|
|
543
|
+
count(reads, e.localId);
|
|
544
|
+
else if (e.kind === "assignExpr" || e.kind === "incDec")
|
|
545
|
+
count(writes, e.localId);
|
|
546
|
+
},
|
|
547
|
+
stmt: (s) => {
|
|
548
|
+
if (s.kind === "varDecl" && s.init?.kind === "classRef")
|
|
549
|
+
bound.set(s.localId, s.init);
|
|
550
|
+
if (s.kind === "assign" || s.kind === "forOf" || s.kind === "varDecl")
|
|
551
|
+
count(writes, s.localId);
|
|
552
|
+
},
|
|
553
|
+
});
|
|
554
|
+
const boxed = new Set(fn.locals.filter((l) => l.boxed).map((l) => l.id));
|
|
555
|
+
for (const [id, ref] of bound)
|
|
556
|
+
if (!boxed.has(id) && writes.get(id) === 1 && reads.get(id) === tests.get(id))
|
|
557
|
+
testedOnly.add(ref);
|
|
558
|
+
}
|
|
559
|
+
for (const fn of mod.functions)
|
|
560
|
+
walkFunction(fn, {
|
|
561
|
+
expr: (e) => {
|
|
562
|
+
if (e.kind === "classRef" && !testedOnly.has(e))
|
|
563
|
+
escaped.add(`%${e.className}.constructor`);
|
|
564
|
+
},
|
|
565
|
+
});
|
|
566
|
+
const virtual = (name) => runtimePrefixes.some((prefix) => name.startsWith(prefix));
|
|
567
|
+
const paramKey = (fnName, localId) => `param:${fnName}\0${localId}`;
|
|
568
|
+
const dropParam = (fnName, localId, why) => {
|
|
569
|
+
reason(paramKey(fnName, localId), why);
|
|
570
|
+
return slots.dropParam(fnName, localId);
|
|
571
|
+
};
|
|
572
|
+
const dropAllParams = (fn, why) => {
|
|
573
|
+
for (const param of fn.params)
|
|
574
|
+
dropParam(fn.name, param.localId, why);
|
|
575
|
+
};
|
|
576
|
+
// Only functions some IR call reaches directly get parameter facts: one
|
|
577
|
+
// the backend invokes by convention has no call site to prove against.
|
|
578
|
+
const called = new Set();
|
|
579
|
+
for (const fn of mod.functions)
|
|
580
|
+
walkFunction(fn, {
|
|
581
|
+
expr: (e) => {
|
|
582
|
+
if (e.kind === "call")
|
|
583
|
+
called.add(e.callee);
|
|
584
|
+
else if (e.kind === "new")
|
|
585
|
+
called.add(`%${e.className}.constructor`);
|
|
586
|
+
},
|
|
587
|
+
});
|
|
588
|
+
for (const fn of mod.functions) {
|
|
589
|
+
if (fn.returnType.kind === "f64" && !fn.async && !fn.generator)
|
|
590
|
+
slots.addReturn(fn.name);
|
|
591
|
+
if (fn.async ||
|
|
592
|
+
fn.generator ||
|
|
593
|
+
!called.has(fn.name) ||
|
|
594
|
+
escaped.has(fn.name) ||
|
|
595
|
+
virtual(fn.name))
|
|
596
|
+
continue;
|
|
597
|
+
for (const param of fn.params)
|
|
598
|
+
if (param.type.kind === "f64")
|
|
599
|
+
slots.addParam(fn.name, param.localId);
|
|
600
|
+
}
|
|
601
|
+
const arityChecked = (fn, args, offset) => {
|
|
602
|
+
if (fn && fn.params.length !== args + offset)
|
|
603
|
+
dropAllParams(fn, "call arity differs");
|
|
604
|
+
};
|
|
605
|
+
for (const fn of mod.functions)
|
|
606
|
+
walkFunction(fn, {
|
|
607
|
+
expr: (e) => {
|
|
608
|
+
if (e.kind === "call")
|
|
609
|
+
arityChecked(functions.get(e.callee), e.args.length, 0);
|
|
610
|
+
else if (e.kind === "new")
|
|
611
|
+
arityChecked(functions.get(`%${e.className}.constructor`), e.args.length, 1);
|
|
612
|
+
},
|
|
613
|
+
});
|
|
614
|
+
/* ── captured bindings ── */
|
|
615
|
+
// A captured local lives in one box shared by its declaring function and
|
|
616
|
+
// every closure that captures it (closure.captures[i] is the lifted
|
|
617
|
+
// function's captures[i]). The box holds an int32 when every write to it
|
|
618
|
+
// anywhere is proven and it starts from a proven parameter or an
|
|
619
|
+
// initialized declaration. Class-captured bindings stay unknown.
|
|
620
|
+
const boxParent = new Map();
|
|
621
|
+
const boxRoot = (key) => {
|
|
622
|
+
let root = key;
|
|
623
|
+
while (boxParent.has(root))
|
|
624
|
+
root = boxParent.get(root);
|
|
625
|
+
if (root !== key)
|
|
626
|
+
boxParent.set(key, root);
|
|
627
|
+
return root;
|
|
628
|
+
};
|
|
629
|
+
const boxKey = (fnName, localId) => `${fnName}\0${localId}`;
|
|
630
|
+
const boxMembers = new Set();
|
|
631
|
+
const unstableBoxes = new Set();
|
|
632
|
+
for (const fn of mod.functions) {
|
|
633
|
+
for (const local of fn.locals)
|
|
634
|
+
if (local.boxed) {
|
|
635
|
+
const key = boxKey(fn.name, local.id);
|
|
636
|
+
boxMembers.add(key);
|
|
637
|
+
if (local.type.kind !== "f64" || local.tdz)
|
|
638
|
+
unstableBoxes.add(key);
|
|
639
|
+
}
|
|
640
|
+
for (const capture of fn.classCaptures ?? [])
|
|
641
|
+
unstableBoxes.add(boxKey(fn.name, capture.localId));
|
|
642
|
+
walkFunction(fn, {
|
|
643
|
+
expr: (e) => {
|
|
644
|
+
if (e.kind === "closure") {
|
|
645
|
+
const lifted = functions.get(e.fnName);
|
|
646
|
+
e.captures.forEach((id, i) => {
|
|
647
|
+
const inner = lifted?.captures?.[i];
|
|
648
|
+
const outer = boxKey(fn.name, id);
|
|
649
|
+
if (!inner)
|
|
650
|
+
unstableBoxes.add(outer);
|
|
651
|
+
else {
|
|
652
|
+
const a = boxRoot(outer), b = boxRoot(boxKey(lifted.name, inner.localId));
|
|
653
|
+
if (a !== b)
|
|
654
|
+
boxParent.set(a, b);
|
|
655
|
+
}
|
|
656
|
+
});
|
|
657
|
+
}
|
|
658
|
+
else if (e.kind === "classRef")
|
|
659
|
+
for (const id of e.captures ?? [])
|
|
660
|
+
unstableBoxes.add(boxKey(fn.name, id));
|
|
661
|
+
else if (e.kind === "incDec")
|
|
662
|
+
unstableBoxes.add(boxKey(fn.name, e.localId));
|
|
663
|
+
},
|
|
664
|
+
stmt: (s) => {
|
|
665
|
+
if (s.kind === "forOf")
|
|
666
|
+
unstableBoxes.add(boxKey(fn.name, s.localId));
|
|
667
|
+
if (s.kind === "tryCatch" && s.catchLocalId)
|
|
668
|
+
unstableBoxes.add(boxKey(fn.name, s.catchLocalId));
|
|
669
|
+
},
|
|
670
|
+
});
|
|
671
|
+
}
|
|
672
|
+
const groups = new Map();
|
|
673
|
+
for (const key of boxMembers) {
|
|
674
|
+
const root = boxRoot(key);
|
|
675
|
+
const list = groups.get(root);
|
|
676
|
+
if (list)
|
|
677
|
+
list.push(key);
|
|
678
|
+
else
|
|
679
|
+
groups.set(root, [key]);
|
|
680
|
+
}
|
|
681
|
+
const boxOrigin = new Map();
|
|
682
|
+
for (const [group, members] of groups) {
|
|
683
|
+
if (members.some((key) => unstableBoxes.has(key)))
|
|
684
|
+
continue;
|
|
685
|
+
// The origin is the one member that is not a capture of its function.
|
|
686
|
+
const origins = members.filter((key) => {
|
|
687
|
+
const [fnName, localId] = key.split("\0");
|
|
688
|
+
return !functions.get(fnName)?.captures?.some((c) => c.localId === localId);
|
|
689
|
+
});
|
|
690
|
+
if (origins.length !== 1)
|
|
691
|
+
continue;
|
|
692
|
+
const [fnName, localId] = origins[0].split("\0");
|
|
693
|
+
const origin = functions.get(fnName);
|
|
694
|
+
if (origin.params.some((p) => p.localId === localId)) {
|
|
695
|
+
if (!slots.hasParam(fnName, localId))
|
|
696
|
+
continue;
|
|
697
|
+
}
|
|
698
|
+
else {
|
|
699
|
+
let initialized = false;
|
|
700
|
+
walkFunction(origin, {
|
|
701
|
+
stmt: (s) => {
|
|
702
|
+
if (s.kind === "varDecl" && s.localId === localId && s.init)
|
|
703
|
+
initialized = true;
|
|
704
|
+
},
|
|
705
|
+
});
|
|
706
|
+
if (!initialized)
|
|
707
|
+
continue;
|
|
708
|
+
}
|
|
709
|
+
boxOrigin.set(`param:${fnName}\0${localId}`, group);
|
|
710
|
+
slots.addBox(group, members);
|
|
711
|
+
}
|
|
712
|
+
const dropBox = (group, why) => {
|
|
713
|
+
if (group === null)
|
|
714
|
+
return false;
|
|
715
|
+
reason(`box:${group}`, why);
|
|
716
|
+
return slots.dropBox(group);
|
|
717
|
+
};
|
|
718
|
+
/* ── readers: which functions depend on which slot facts ── */
|
|
719
|
+
const readers = new Map();
|
|
720
|
+
const reads = (slot, fnName) => {
|
|
721
|
+
const set = readers.get(slot);
|
|
722
|
+
if (set)
|
|
723
|
+
set.add(fnName);
|
|
724
|
+
else
|
|
725
|
+
readers.set(slot, new Set([fnName]));
|
|
726
|
+
};
|
|
727
|
+
for (const fn of mod.functions)
|
|
728
|
+
walkFunction(fn, {
|
|
729
|
+
expr: (e) => {
|
|
730
|
+
if (e.kind === "fieldGet") {
|
|
731
|
+
const family = slots.family(e.className, e.field);
|
|
732
|
+
if (family)
|
|
733
|
+
reads(`field:${family}`, fn.name);
|
|
734
|
+
}
|
|
735
|
+
else if (e.kind === "call")
|
|
736
|
+
reads(`ret:${e.callee}`, fn.name);
|
|
737
|
+
else if (e.kind === "varRef") {
|
|
738
|
+
const group = slots.boxGroup(fn.name, e.localId);
|
|
739
|
+
if (group !== null)
|
|
740
|
+
reads(`box:${group}`, fn.name);
|
|
741
|
+
}
|
|
742
|
+
else if (e.kind === "fieldIncDec") {
|
|
743
|
+
const family = slots.family(e.className, e.field);
|
|
744
|
+
if (family)
|
|
745
|
+
dropFamily(family, "++/-- on the field");
|
|
746
|
+
}
|
|
747
|
+
},
|
|
748
|
+
});
|
|
749
|
+
/* ── the optimistic fixpoint ── */
|
|
750
|
+
const bindings = findInitializerBindings(mod);
|
|
751
|
+
const numeric = (fn) => withInitializerBindings(fn, bindings.get(fn.name) ?? []);
|
|
752
|
+
const queue = mod.functions.map((fn) => fn.name);
|
|
753
|
+
const queued = new Set(queue);
|
|
754
|
+
const invalidate = (slot) => {
|
|
755
|
+
const dependents = readers.get(slot);
|
|
756
|
+
if (dependents)
|
|
757
|
+
for (const fnName of dependents)
|
|
758
|
+
if (!queued.has(fnName)) {
|
|
759
|
+
queued.add(fnName);
|
|
760
|
+
queue.push(fnName);
|
|
761
|
+
}
|
|
762
|
+
};
|
|
763
|
+
const provenByFunction = new Map();
|
|
764
|
+
while (queue.length) {
|
|
765
|
+
const fn = functions.get(queue.pop());
|
|
766
|
+
queued.delete(fn.name);
|
|
767
|
+
const params = slots.facts(fn.name).params;
|
|
768
|
+
if (params)
|
|
769
|
+
for (const id of params.keys())
|
|
770
|
+
reads(`param:${fn.name}\0${id}`, fn.name);
|
|
771
|
+
const ranges = analyzeIntegerRanges(numeric(fn), slots.facts(fn.name));
|
|
772
|
+
const proven = [];
|
|
773
|
+
const int32 = (e) => withinInt32(ranges.get(e));
|
|
774
|
+
const args = (callee, list, offset) => {
|
|
775
|
+
if (!callee)
|
|
776
|
+
return;
|
|
777
|
+
list.forEach((arg, i) => {
|
|
778
|
+
const param = callee.params[i + offset];
|
|
779
|
+
if (!param || !slots.hasParam(callee.name, param.localId) || int32(arg))
|
|
780
|
+
return;
|
|
781
|
+
const slot = `param:${callee.name}\0${param.localId}`;
|
|
782
|
+
if (dropParam(callee.name, param.localId, `argument in ${fn.name} is not int32`))
|
|
783
|
+
invalidate(slot);
|
|
784
|
+
const group = boxOrigin.get(slot) ?? null;
|
|
785
|
+
if (dropBox(group, `parameter ${param.localId} is not int32`))
|
|
786
|
+
invalidate(`box:${group}`);
|
|
787
|
+
});
|
|
788
|
+
};
|
|
789
|
+
const boxWrite = (localId, value) => {
|
|
790
|
+
const group = slots.boxGroup(fn.name, localId);
|
|
791
|
+
if (!slots.hasBox(group) || int32(value))
|
|
792
|
+
return;
|
|
793
|
+
if (dropBox(group, `write in ${fn.name} is not int32`))
|
|
794
|
+
invalidate(`box:${group}`);
|
|
795
|
+
};
|
|
796
|
+
walkFunction(fn, {
|
|
797
|
+
stmt: (s) => {
|
|
798
|
+
if (s.kind === "assign")
|
|
799
|
+
boxWrite(s.localId, s.value);
|
|
800
|
+
else if (s.kind === "varDecl" && s.init)
|
|
801
|
+
boxWrite(s.localId, s.init);
|
|
802
|
+
if (s.kind === "fieldSet") {
|
|
803
|
+
const family = slots.family(s.className, s.field);
|
|
804
|
+
if (!family || !slots.hasFamily(family))
|
|
805
|
+
return;
|
|
806
|
+
if (int32(s.value))
|
|
807
|
+
proven.push(s.value);
|
|
808
|
+
else {
|
|
809
|
+
dropFamily(family, `write in ${fn.name} is not int32`);
|
|
810
|
+
invalidate(`field:${family}`);
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
else if (s.kind === "return" && slots.hasReturn(fn.name)) {
|
|
814
|
+
if (s.value && int32(s.value))
|
|
815
|
+
return;
|
|
816
|
+
reason(`ret:${fn.name}`, "return is not int32");
|
|
817
|
+
slots.dropReturn(fn.name);
|
|
818
|
+
invalidate(`ret:${fn.name}`);
|
|
819
|
+
}
|
|
820
|
+
},
|
|
821
|
+
expr: (e) => {
|
|
822
|
+
if (e.kind === "call")
|
|
823
|
+
args(functions.get(e.callee), e.args, 0);
|
|
824
|
+
else if (e.kind === "new")
|
|
825
|
+
args(functions.get(`%${e.className}.constructor`), e.args, 1);
|
|
826
|
+
else if (e.kind === "assignExpr")
|
|
827
|
+
boxWrite(e.localId, e.value);
|
|
828
|
+
},
|
|
829
|
+
});
|
|
830
|
+
provenByFunction.set(fn.name, proven);
|
|
831
|
+
}
|
|
832
|
+
for (const proven of provenByFunction.values())
|
|
833
|
+
for (const value of proven)
|
|
834
|
+
slots.provenWrites.add(value);
|
|
835
|
+
return slots;
|
|
836
|
+
}
|
|
837
|
+
//# sourceMappingURL=int32-slots.js.map
|