@loopdive/js2 0.57.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +1425 -0
- package/LICENSE +189 -0
- package/README.md +451 -0
- package/dist/checker/index.d.ts +117 -0
- package/dist/checker/language-service.d.ts +39 -0
- package/dist/checker/node-capability-map.d.ts +63 -0
- package/dist/checker/type-mapper.d.ts +84 -0
- package/dist/cjs-rewrite.d.ts +19 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +363 -0
- package/dist/codegen/accessor-driver.d.ts +97 -0
- package/dist/codegen/any-helpers.d.ts +72 -0
- package/dist/codegen/array-element-typing.d.ts +46 -0
- package/dist/codegen/array-holes.d.ts +69 -0
- package/dist/codegen/array-methods.d.ts +68 -0
- package/dist/codegen/array-object-proto.d.ts +64 -0
- package/dist/codegen/array-reduce-fusion.d.ts +31 -0
- package/dist/codegen/array-to-primitive.d.ts +28 -0
- package/dist/codegen/async-cps.d.ts +239 -0
- package/dist/codegen/async-scheduler.d.ts +349 -0
- package/dist/codegen/binary-ops.d.ts +78 -0
- package/dist/codegen/binding-info.d.ts +31 -0
- package/dist/codegen/builtin-scaffold.d.ts +98 -0
- package/dist/codegen/builtin-static-globals.d.ts +8 -0
- package/dist/codegen/builtin-tags.d.ts +189 -0
- package/dist/codegen/case-convert-native.d.ts +12 -0
- package/dist/codegen/case-tables.d.ts +4 -0
- package/dist/codegen/class-bodies.d.ts +41 -0
- package/dist/codegen/class-member-keys.d.ts +33 -0
- package/dist/codegen/class-to-primitive.d.ts +39 -0
- package/dist/codegen/closed-method-dispatch.d.ts +42 -0
- package/dist/codegen/closures.d.ts +285 -0
- package/dist/codegen/coercion-engine.d.ts +154 -0
- package/dist/codegen/coercion-plan.d.ts +29 -0
- package/dist/codegen/context/bodies.d.ts +4 -0
- package/dist/codegen/context/create-context.d.ts +4 -0
- package/dist/codegen/context/errors.d.ts +39 -0
- package/dist/codegen/context/locals.d.ts +69 -0
- package/dist/codegen/context/source-pos.d.ts +5 -0
- package/dist/codegen/context/speculative.d.ts +95 -0
- package/dist/codegen/context/types.d.ts +1936 -0
- package/dist/codegen/custom-iterable.d.ts +34 -0
- package/dist/codegen/dataview-native.d.ts +51 -0
- package/dist/codegen/date-parse-native.d.ts +13 -0
- package/dist/codegen/dead-elimination.d.ts +26 -0
- package/dist/codegen/declarations.d.ts +147 -0
- package/dist/codegen/deno-api.d.ts +11 -0
- package/dist/codegen/destructuring-params.d.ts +102 -0
- package/dist/codegen/dyn-read.d.ts +26 -0
- package/dist/codegen/eval-tiering.d.ts +19 -0
- package/dist/codegen/expressions/assignment.d.ts +61 -0
- package/dist/codegen/expressions/builtins.d.ts +26 -0
- package/dist/codegen/expressions/calls-closures.d.ts +54 -0
- package/dist/codegen/expressions/calls-guards.d.ts +49 -0
- package/dist/codegen/expressions/calls-optional.d.ts +4 -0
- package/dist/codegen/expressions/calls.d.ts +83 -0
- package/dist/codegen/expressions/eval-inline.d.ts +24 -0
- package/dist/codegen/expressions/extern.d.ts +67 -0
- package/dist/codegen/expressions/fnctor-prototype.d.ts +52 -0
- package/dist/codegen/expressions/helpers.d.ts +212 -0
- package/dist/codegen/expressions/identifiers.d.ts +57 -0
- package/dist/codegen/expressions/late-imports.d.ts +81 -0
- package/dist/codegen/expressions/logical-ops.d.ts +18 -0
- package/dist/codegen/expressions/misc.d.ts +27 -0
- package/dist/codegen/expressions/new-super.d.ts +25 -0
- package/dist/codegen/expressions/promise-subclass.d.ts +38 -0
- package/dist/codegen/expressions/proto-override.d.ts +63 -0
- package/dist/codegen/expressions/unary-updates.d.ts +21 -0
- package/dist/codegen/expressions/unary.d.ts +6 -0
- package/dist/codegen/expressions.d.ts +31 -0
- package/dist/codegen/fallback-telemetry.d.ts +53 -0
- package/dist/codegen/fixups.d.ts +80 -0
- package/dist/codegen/fmod.d.ts +10 -0
- package/dist/codegen/fnctor-escape-gate.d.ts +92 -0
- package/dist/codegen/function-body.d.ts +52 -0
- package/dist/codegen/generators-native.d.ts +92 -0
- package/dist/codegen/helpers/body-references-own-this.d.ts +22 -0
- package/dist/codegen/helpers/body-uses-arguments.d.ts +12 -0
- package/dist/codegen/helpers/is-strict-function.d.ts +52 -0
- package/dist/codegen/host-import-allowlist.d.ts +140 -0
- package/dist/codegen/index.d.ts +500 -0
- package/dist/codegen/ir-tail-call.d.ts +8 -0
- package/dist/codegen/iterator-native.d.ts +44 -0
- package/dist/codegen/json-codec-native.d.ts +78 -0
- package/dist/codegen/json-runtime.d.ts +35 -0
- package/dist/codegen/json-standalone.d.ts +25 -0
- package/dist/codegen/linear-uint8-analysis.d.ts +46 -0
- package/dist/codegen/linear-uint8-arena.d.ts +7 -0
- package/dist/codegen/linear-uint8-codegen.d.ts +103 -0
- package/dist/codegen/linear-uint8-signatures.d.ts +26 -0
- package/dist/codegen/literals.d.ts +115 -0
- package/dist/codegen/map-runtime.d.ts +142 -0
- package/dist/codegen/math-helpers.d.ts +7 -0
- package/dist/codegen/member-get-dispatch.d.ts +42 -0
- package/dist/codegen/member-set-dispatch.d.ts +28 -0
- package/dist/codegen/native-proto.d.ts +98 -0
- package/dist/codegen/native-regex.d.ts +158 -0
- package/dist/codegen/native-strings.d.ts +146 -0
- package/dist/codegen/new-target.d.ts +30 -0
- package/dist/codegen/node-fs-api.d.ts +47 -0
- package/dist/codegen/number-format-native.d.ts +9 -0
- package/dist/codegen/number-ryu.d.ts +27 -0
- package/dist/codegen/object-ops.d.ts +94 -0
- package/dist/codegen/object-runtime.d.ts +171 -0
- package/dist/codegen/parse-number-native.d.ts +10 -0
- package/dist/codegen/peephole.d.ts +6 -0
- package/dist/codegen/property-access.d.ts +294 -0
- package/dist/codegen/raw-wasi-api.d.ts +13 -0
- package/dist/codegen/regex/bytecode.d.ts +140 -0
- package/dist/codegen/regex/casefold.d.ts +41 -0
- package/dist/codegen/regex/compile.d.ts +51 -0
- package/dist/codegen/regex/parse.d.ts +76 -0
- package/dist/codegen/regex/unicode.d.ts +42 -0
- package/dist/codegen/regex/vm.d.ts +24 -0
- package/dist/codegen/regexp-standalone.d.ts +350 -0
- package/dist/codegen/registry/error-types.d.ts +38 -0
- package/dist/codegen/registry/imports.d.ts +46 -0
- package/dist/codegen/registry/types.d.ts +59 -0
- package/dist/codegen/set-algebra.d.ts +17 -0
- package/dist/codegen/set-runtime.d.ts +74 -0
- package/dist/codegen/shared.d.ts +111 -0
- package/dist/codegen/stack-balance.d.ts +43 -0
- package/dist/codegen/statements/control-flow.d.ts +25 -0
- package/dist/codegen/statements/destructuring.d.ts +177 -0
- package/dist/codegen/statements/exceptions.d.ts +11 -0
- package/dist/codegen/statements/functions.d.ts +1 -0
- package/dist/codegen/statements/index.d.ts +1 -0
- package/dist/codegen/statements/loops.d.ts +7 -0
- package/dist/codegen/statements/nested-declarations.d.ts +78 -0
- package/dist/codegen/statements/shared.d.ts +38 -0
- package/dist/codegen/statements/tdz.d.ts +43 -0
- package/dist/codegen/statements/variables.d.ts +3 -0
- package/dist/codegen/statements.d.ts +9 -0
- package/dist/codegen/string-builder.d.ts +131 -0
- package/dist/codegen/string-ops.d.ts +87 -0
- package/dist/codegen/struct-accessor-closure.d.ts +36 -0
- package/dist/codegen/symbol-native.d.ts +55 -0
- package/dist/codegen/temporal-native.d.ts +8 -0
- package/dist/codegen/timsort.d.ts +2 -0
- package/dist/codegen/type-coercion.d.ts +123 -0
- package/dist/codegen/typeof-delete.d.ts +38 -0
- package/dist/codegen/uri-encoding-native.d.ts +33 -0
- package/dist/codegen/value-tags.d.ts +74 -0
- package/dist/codegen/walk-instructions.d.ts +20 -0
- package/dist/codegen/weak-collections-runtime.d.ts +16 -0
- package/dist/codegen/with-scope.d.ts +106 -0
- package/dist/codegen-linear/c-abi.d.ts +74 -0
- package/dist/codegen-linear/context.d.ts +86 -0
- package/dist/codegen-linear/index.d.ts +28 -0
- package/dist/codegen-linear/layout.d.ts +39 -0
- package/dist/codegen-linear/runtime.d.ts +161 -0
- package/dist/codegen-linear/simd.d.ts +7 -0
- package/dist/compiler/define-substitution.d.ts +27 -0
- package/dist/compiler/early-errors/assignment.d.ts +26 -0
- package/dist/compiler/early-errors/context.d.ts +17 -0
- package/dist/compiler/early-errors/duplicates.d.ts +20 -0
- package/dist/compiler/early-errors/index.d.ts +11 -0
- package/dist/compiler/early-errors/labels.d.ts +13 -0
- package/dist/compiler/early-errors/module-rules.d.ts +36 -0
- package/dist/compiler/early-errors/node-checks.d.ts +7 -0
- package/dist/compiler/early-errors/predicates.d.ts +140 -0
- package/dist/compiler/early-errors/tdz.d.ts +17 -0
- package/dist/compiler/import-manifest.d.ts +18 -0
- package/dist/compiler/output.d.ts +46 -0
- package/dist/compiler/validation.d.ts +45 -0
- package/dist/compiler.d.ts +48 -0
- package/dist/define-substitution-BcUeKC2A.js +109 -0
- package/dist/emit/binary.d.ts +50 -0
- package/dist/emit/c-header.d.ts +23 -0
- package/dist/emit/canonical-recgroup.d.ts +86 -0
- package/dist/emit/encoder.d.ts +28 -0
- package/dist/emit/object.d.ts +14 -0
- package/dist/emit/opcodes.d.ts +464 -0
- package/dist/emit/sourcemap.d.ts +33 -0
- package/dist/emit/wat.d.ts +6 -0
- package/dist/env.d.ts +46 -0
- package/dist/import-resolver.d.ts +68 -0
- package/dist/index.d.ts +486 -0
- package/dist/index.js +755 -0
- package/dist/ir/alloc-registry.d.ts +75 -0
- package/dist/ir/analysis/encoding.d.ts +38 -0
- package/dist/ir/analysis/escape.d.ts +32 -0
- package/dist/ir/analysis/lattice.d.ts +72 -0
- package/dist/ir/analysis/ownership.d.ts +31 -0
- package/dist/ir/analysis/stack-alloc.d.ts +20 -0
- package/dist/ir/backend/bytecode-emitter.d.ts +237 -0
- package/dist/ir/backend/bytecode-vm.d.ts +74 -0
- package/dist/ir/backend/emitter.d.ts +121 -0
- package/dist/ir/backend/handles.d.ts +133 -0
- package/dist/ir/backend/legality.d.ts +9 -0
- package/dist/ir/backend/linear-emitter.d.ts +41 -0
- package/dist/ir/backend/wasmgc-emitter.d.ts +43 -0
- package/dist/ir/builder.d.ts +401 -0
- package/dist/ir/from-ast.d.ts +192 -0
- package/dist/ir/index.d.ts +16 -0
- package/dist/ir/integration.d.ts +27 -0
- package/dist/ir/lower.d.ts +203 -0
- package/dist/ir/nodes.d.ts +1452 -0
- package/dist/ir/passes/alloc-discipline.d.ts +19 -0
- package/dist/ir/passes/constant-fold.d.ts +7 -0
- package/dist/ir/passes/dead-code.d.ts +18 -0
- package/dist/ir/passes/inline-small.d.ts +7 -0
- package/dist/ir/passes/monomorphize.d.ts +21 -0
- package/dist/ir/passes/simplify-cfg.d.ts +19 -0
- package/dist/ir/passes/tagged-union-types.d.ts +45 -0
- package/dist/ir/passes/tagged-unions.d.ts +22 -0
- package/dist/ir/propagate.d.ts +135 -0
- package/dist/ir/select.d.ts +81 -0
- package/dist/ir/types.d.ts +832 -0
- package/dist/ir/verify-alloc.d.ts +18 -0
- package/dist/ir/verify.d.ts +7 -0
- package/dist/link/index.d.ts +11 -0
- package/dist/link/isolation.d.ts +24 -0
- package/dist/link/linker.d.ts +37 -0
- package/dist/link/reader.d.ts +158 -0
- package/dist/link/resolver.d.ts +19 -0
- package/dist/optimize.d.ts +54 -0
- package/dist/optimize.js +262 -0
- package/dist/position-map.d.ts +64 -0
- package/dist/process-stdin-prelude.d.ts +16 -0
- package/dist/resolve.d.ts +82 -0
- package/dist/runtime/builtins.d.ts +1 -0
- package/dist/runtime-C-4q_KwU.js +164438 -0
- package/dist/runtime-containment.d.ts +6 -0
- package/dist/runtime-eval.d.ts +132 -0
- package/dist/runtime-instantiate.d.ts +16 -0
- package/dist/runtime.d.ts +128 -0
- package/dist/runtime.js +12 -0
- package/dist/shape-inference.d.ts +20 -0
- package/dist/treeshake.d.ts +17 -0
- package/dist/ts-api.d.ts +30 -0
- package/dist/wit-generator.d.ts +18 -0
- package/package.json +187 -0
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { AllocKind, AllocSiteId, IrSiteId, IrType } from './nodes.js';
|
|
2
|
+
/**
|
|
3
|
+
* A live allocation site. `metadata` is stored out-of-band in the registry
|
|
4
|
+
* (keyed by id + namespace), not on this record, so analyses can annotate
|
|
5
|
+
* without mutating the IR.
|
|
6
|
+
*/
|
|
7
|
+
export interface AllocSite {
|
|
8
|
+
readonly id: AllocSiteId;
|
|
9
|
+
readonly kind: AllocKind;
|
|
10
|
+
readonly type: IrType;
|
|
11
|
+
/** Reuses the defining instr's source location, when present. */
|
|
12
|
+
readonly origin?: IrSiteId;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Reserved metadata namespaces. Each analysis owns exactly one and may not
|
|
16
|
+
* write to another's. Enforced by convention in this issue (#1586); the ADR
|
|
17
|
+
* documents the ownership table.
|
|
18
|
+
*/
|
|
19
|
+
export declare const ALLOC_NAMESPACES: {
|
|
20
|
+
/** #1587 — ownership and access-semantics analysis. */
|
|
21
|
+
readonly ownership: "ownership";
|
|
22
|
+
/** #1588 — string encoding tracking. */
|
|
23
|
+
readonly encoding: "encoding";
|
|
24
|
+
/** #1585 — dual-target IR / lifetime analysis. */
|
|
25
|
+
readonly lifetime: "lifetime";
|
|
26
|
+
/** Closure-capture escape analysis (#747). */
|
|
27
|
+
readonly escape: "escape";
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* Module-global allocation-site registry. One per `IrModule` compile, threaded
|
|
31
|
+
* through every pass invocation (see integration.ts). Not a per-function
|
|
32
|
+
* singleton — inlining merges functions, so ids must be module-stable.
|
|
33
|
+
*/
|
|
34
|
+
export declare class AllocSiteRegistry {
|
|
35
|
+
/** index === AllocSiteId. */
|
|
36
|
+
private readonly sites;
|
|
37
|
+
/** metadata[id] = Map<namespace, value>. Sparse — created lazily. */
|
|
38
|
+
private readonly meta;
|
|
39
|
+
/** Mint a fresh, live allocation-site id. */
|
|
40
|
+
fresh(kind: AllocKind, type: IrType, origin?: IrSiteId): AllocSiteId;
|
|
41
|
+
/** True iff `id` indexes a known site (any state). */
|
|
42
|
+
isKnown(id: AllocSiteId): boolean;
|
|
43
|
+
/**
|
|
44
|
+
* Resolve through alias chains to the canonical live site, or `null` if the
|
|
45
|
+
* id is unknown, retired, or its chain terminates in a non-live entry. The
|
|
46
|
+
* `seen` guard makes a malformed cycle resolve to `null` rather than loop.
|
|
47
|
+
*/
|
|
48
|
+
resolve(id: AllocSiteId): AllocSite | null;
|
|
49
|
+
/**
|
|
50
|
+
* Resolve `id` to the index of its canonical entry (following alias chains),
|
|
51
|
+
* or `null` on a broken/cyclic/unknown chain. Used internally so metadata
|
|
52
|
+
* writes after fusion land on the canonical site.
|
|
53
|
+
*/
|
|
54
|
+
private canonicalIndex;
|
|
55
|
+
/**
|
|
56
|
+
* Record that `from` was fused into `to` (rule 2 — alias). Any metadata on
|
|
57
|
+
* `from` is merged onto the canonical site `to` (existing keys on `to` win,
|
|
58
|
+
* so a deliberate annotation is never clobbered by a fused-in default).
|
|
59
|
+
* No-op if either id is unknown.
|
|
60
|
+
*/
|
|
61
|
+
alias(from: AllocSiteId, to: AllocSiteId): void;
|
|
62
|
+
/** Mark an allocation dead and removed (rule 3 — retire). No-op if unknown. */
|
|
63
|
+
retire(id: AllocSiteId): void;
|
|
64
|
+
/**
|
|
65
|
+
* Attach `value` under `ns` to the canonical site behind `id`. No-op if the
|
|
66
|
+
* id resolves to nothing live (retired/unknown/broken chain).
|
|
67
|
+
*/
|
|
68
|
+
annotate<T>(id: AllocSiteId, ns: string, value: T): void;
|
|
69
|
+
/** Read the `ns` annotation on the canonical site behind `id`. */
|
|
70
|
+
read<T>(id: AllocSiteId, ns: string): T | undefined;
|
|
71
|
+
/** Number of sites ever minted (including aliased/retired). For diagnostics. */
|
|
72
|
+
get size(): number;
|
|
73
|
+
/** Snapshot of live sites — for debugging / tooling, not hot paths. */
|
|
74
|
+
liveSites(): AllocSite[];
|
|
75
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { AllocSiteRegistry } from '../alloc-registry.js';
|
|
2
|
+
import { IrFunction } from '../nodes.js';
|
|
3
|
+
/**
|
|
4
|
+
* Encoding lattice. Ordering (most → least restrictive):
|
|
5
|
+
* ascii ⊑ utf8-guaranteed ⊑ wtf16
|
|
6
|
+
* `wtf16` is top (the conservative default); `ascii` is bottom (the
|
|
7
|
+
* strongest claim). The join of two encodings is their least upper bound:
|
|
8
|
+
* any operand being `wtf16` forces `wtf16`; otherwise `utf8-guaranteed`
|
|
9
|
+
* unless both are `ascii`.
|
|
10
|
+
*/
|
|
11
|
+
export type Encoding = "ascii" | "utf8-guaranteed" | "wtf16";
|
|
12
|
+
/** Least upper bound of two encodings (the conservative join). */
|
|
13
|
+
export declare function joinEncoding(a: Encoding, b: Encoding): Encoding;
|
|
14
|
+
/**
|
|
15
|
+
* Classify a statically known string literal by its code units.
|
|
16
|
+
* - all code units ≤ 0x7F → `ascii`
|
|
17
|
+
* - else, no lone surrogates → `utf8-guaranteed`
|
|
18
|
+
* - else (a lone surrogate present) → `wtf16`
|
|
19
|
+
*
|
|
20
|
+
* A surrogate is lone when a high surrogate (0xD800–0xDBFF) is not
|
|
21
|
+
* immediately followed by a low surrogate (0xDC00–0xDFFF), or a low
|
|
22
|
+
* surrogate appears without a preceding high surrogate. Such a string
|
|
23
|
+
* cannot be encoded as UTF-8, so it must stay `wtf16`.
|
|
24
|
+
*/
|
|
25
|
+
export declare function classifyLiteral(value: string): Encoding;
|
|
26
|
+
/**
|
|
27
|
+
* Run the encoding analysis over a single IR function, writing `encoding`
|
|
28
|
+
* annotations onto the string allocation sites in `registry`. Read-only on
|
|
29
|
+
* the IR. Idempotent: re-running overwrites the same annotations with the
|
|
30
|
+
* same values.
|
|
31
|
+
*
|
|
32
|
+
* The analysis is a single forward pass. SSA dominance guarantees each
|
|
33
|
+
* value is defined before use within a block, and string values in Phase 1
|
|
34
|
+
* are produced and consumed locally (literals + concat), so a per-function
|
|
35
|
+
* value→encoding map filled in instruction order is sufficient. Values with
|
|
36
|
+
* no tracked origin are absent from the map and treated as `wtf16`.
|
|
37
|
+
*/
|
|
38
|
+
export declare function analyzeEncoding(fn: IrFunction, registry: AllocSiteRegistry): void;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { AllocSiteRegistry } from '../alloc-registry.js';
|
|
2
|
+
import { IrFunction, IrValueId } from '../nodes.js';
|
|
3
|
+
import { OwnershipResult } from './ownership.js';
|
|
4
|
+
/**
|
|
5
|
+
* How an allocation escapes its defining function. Ordered most-precise →
|
|
6
|
+
* most-conservative; `local` is the only stack-allocatable classification.
|
|
7
|
+
*/
|
|
8
|
+
export type EscapeClass = "local" | "returned" | "stored" | "captured" | "opaque";
|
|
9
|
+
export interface EscapeInfo {
|
|
10
|
+
readonly classification: EscapeClass;
|
|
11
|
+
/** True iff `classification === "local"` — safe for scalar replacement. */
|
|
12
|
+
readonly stackAllocatable: boolean;
|
|
13
|
+
}
|
|
14
|
+
/** Per-function escape-analysis result, keyed by allocation result value. */
|
|
15
|
+
export declare class EscapeResult {
|
|
16
|
+
private readonly infos;
|
|
17
|
+
constructor(infos: ReadonlyMap<IrValueId, EscapeInfo>);
|
|
18
|
+
/** Escape info for an allocation value, or `undefined` if it is not an alloc. */
|
|
19
|
+
of(value: IrValueId): EscapeInfo | undefined;
|
|
20
|
+
classOf(value: IrValueId): EscapeClass | undefined;
|
|
21
|
+
/** Allocation values proven `local` — the stack-allocation candidates. */
|
|
22
|
+
localAllocations(): IrValueId[];
|
|
23
|
+
entries(): Iterable<[IrValueId, EscapeInfo]>;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Run escape analysis on `fn`. Uses the #1587 ownership result (computed fresh
|
|
27
|
+
* when not supplied) as the escape oracle, then walks the IR to attribute the
|
|
28
|
+
* strongest escape edge per allocation. When `registry` is supplied, each
|
|
29
|
+
* allocation's classification is written under the `escape` namespace keyed by
|
|
30
|
+
* the defining instr's alloc id.
|
|
31
|
+
*/
|
|
32
|
+
export declare function analyzeEscape(fn: IrFunction, registry?: AllocSiteRegistry, precomputedOwnership?: OwnershipResult): EscapeResult;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ownership state — a totally ordered lattice from most-precise (`owned`) to
|
|
3
|
+
* most-permissive (`escaped`). Higher = more conservative = safer default.
|
|
4
|
+
*
|
|
5
|
+
* owned ⊑ borrowed ⊑ shared ⊑ escaped
|
|
6
|
+
*
|
|
7
|
+
* - `owned` — exactly one live reference (this one) for its liveness.
|
|
8
|
+
* - `borrowed` — referenced elsewhere; this ref must not deallocate/invalidate.
|
|
9
|
+
* - `shared` — multiple references; mutation must be observable to all.
|
|
10
|
+
* - `escaped` — lifetime extends beyond this function (return, capture,
|
|
11
|
+
* store-to-heap, pass to opaque code).
|
|
12
|
+
*/
|
|
13
|
+
export type Ownership = "owned" | "borrowed" | "shared" | "escaped";
|
|
14
|
+
/** Bottom of the ownership lattice — the most precise classification. */
|
|
15
|
+
export declare const OWNERSHIP_BOTTOM: Ownership;
|
|
16
|
+
/** Top of the ownership lattice — the conservative default. */
|
|
17
|
+
export declare const OWNERSHIP_TOP: Ownership;
|
|
18
|
+
/**
|
|
19
|
+
* Join (least upper bound) of two ownership states. Used at control-flow merge
|
|
20
|
+
* points and whenever an operation widens a value's classification: the result
|
|
21
|
+
* is the *more conservative* of the two. Commutative, associative, idempotent.
|
|
22
|
+
*/
|
|
23
|
+
export declare function joinOwnership(a: Ownership, b: Ownership): Ownership;
|
|
24
|
+
/** True iff `a` is at least as precise as `b` (`a ⊑ b`). */
|
|
25
|
+
export declare function ownershipLeq(a: Ownership, b: Ownership): boolean;
|
|
26
|
+
/**
|
|
27
|
+
* Access operation — how a reference is used. The access lattice is the
|
|
28
|
+
* powerset of these tags ordered by ⊆; join is set union, bottom is `{}`, top
|
|
29
|
+
* is the full set. A value that escapes to opaque code conservatively widens
|
|
30
|
+
* to the full set (the callee may do anything).
|
|
31
|
+
*/
|
|
32
|
+
export type AccessOp = "read" | "write" | "mutate" | "identity" | "escape";
|
|
33
|
+
/**
|
|
34
|
+
* An access set — a small immutable wrapper over `Set<AccessOp>` with union
|
|
35
|
+
* (join) and subset (⊑) operations. Kept as a class so the query API can hand
|
|
36
|
+
* consumers a value they cannot accidentally mutate.
|
|
37
|
+
*/
|
|
38
|
+
export declare class AccessSet {
|
|
39
|
+
private readonly ops;
|
|
40
|
+
private constructor();
|
|
41
|
+
/** Bottom — no observed access. */
|
|
42
|
+
static empty(): AccessSet;
|
|
43
|
+
/** Top — the conservative full access set. */
|
|
44
|
+
static full(): AccessSet;
|
|
45
|
+
static of(...ops: readonly AccessOp[]): AccessSet;
|
|
46
|
+
has(op: AccessOp): boolean;
|
|
47
|
+
/** Join — set union. Returns `this` unchanged when `op` is already present. */
|
|
48
|
+
with(op: AccessOp): AccessSet;
|
|
49
|
+
/** Join — set union of two access sets. */
|
|
50
|
+
union(other: AccessSet): AccessSet;
|
|
51
|
+
/** True iff `this ⊆ other`. */
|
|
52
|
+
subsetOf(other: AccessSet): boolean;
|
|
53
|
+
equals(other: AccessSet): boolean;
|
|
54
|
+
/** Stable, sorted array view — for annotations, tests, and diagnostics. */
|
|
55
|
+
toArray(): AccessOp[];
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* The full classification the analysis attaches to a value. This is the shape
|
|
59
|
+
* stored in the registry `ownership` namespace and the parallel `ValueAnnot`
|
|
60
|
+
* map. Consumers must treat it as the analysis's *tightest provable* result
|
|
61
|
+
* and fall back to their conservative path when it is not tight enough — they
|
|
62
|
+
* may never assume a tighter classification than this carries (ADR-0014).
|
|
63
|
+
*/
|
|
64
|
+
export interface OwnershipAnnotation {
|
|
65
|
+
readonly ownership: Ownership;
|
|
66
|
+
readonly access: AccessSet;
|
|
67
|
+
}
|
|
68
|
+
/** The conservative TOP annotation — used for anything not provably tighter. */
|
|
69
|
+
export declare function topAnnotation(): OwnershipAnnotation;
|
|
70
|
+
/** Join two annotations component-wise (used at CFG merges). */
|
|
71
|
+
export declare function joinAnnotations(a: OwnershipAnnotation, b: OwnershipAnnotation): OwnershipAnnotation;
|
|
72
|
+
export declare function annotationsEqual(a: OwnershipAnnotation, b: OwnershipAnnotation): boolean;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { AllocSiteRegistry } from '../alloc-registry.js';
|
|
2
|
+
import { IrFunction, IrValueId } from '../nodes.js';
|
|
3
|
+
import { AccessSet, OwnershipAnnotation, Ownership } from './lattice.js';
|
|
4
|
+
/**
|
|
5
|
+
* The analysis result for one function. `of` returns the inferred annotation
|
|
6
|
+
* for any value; values the analysis never saw resolve to the conservative
|
|
7
|
+
* TOP, so consumers always get a correctness-preserving answer.
|
|
8
|
+
*/
|
|
9
|
+
export declare class OwnershipResult {
|
|
10
|
+
private readonly annots;
|
|
11
|
+
/** Value -> the alloc id of the instr that defined it, when allocated. */
|
|
12
|
+
private readonly allocOf;
|
|
13
|
+
constructor(annots: ReadonlyMap<IrValueId, OwnershipAnnotation>,
|
|
14
|
+
/** Value -> the alloc id of the instr that defined it, when allocated. */
|
|
15
|
+
allocOf: ReadonlyMap<IrValueId, number>);
|
|
16
|
+
/** Inferred annotation for `value` — TOP if the value was never classified. */
|
|
17
|
+
of(value: IrValueId): OwnershipAnnotation;
|
|
18
|
+
ownershipOf(value: IrValueId): Ownership;
|
|
19
|
+
accessOf(value: IrValueId): AccessSet;
|
|
20
|
+
/** True iff `value` is a heap allocation proven `owned` and never `escaped`. */
|
|
21
|
+
isStackAllocatable(value: IrValueId): boolean;
|
|
22
|
+
/** All classified values — for diagnostics / tests. */
|
|
23
|
+
entries(): Iterable<[IrValueId, OwnershipAnnotation]>;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Run the analysis on `fn`. When `registry` is supplied, the result for each
|
|
27
|
+
* allocated value is also written under the `ownership` namespace keyed by the
|
|
28
|
+
* defining instr's alloc id (the durable cross-pass channel). The returned
|
|
29
|
+
* `OwnershipResult` is the in-function view keyed by `IrValueId`.
|
|
30
|
+
*/
|
|
31
|
+
export declare function analyzeOwnership(fn: IrFunction, registry?: AllocSiteRegistry): OwnershipResult;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { AllocSiteRegistry } from '../alloc-registry.js';
|
|
2
|
+
import { AllocKind, IrFunction } from '../nodes.js';
|
|
3
|
+
import { OwnershipResult } from './ownership.js';
|
|
4
|
+
/** A value-creating instr the analysis proved safe to stack-allocate. */
|
|
5
|
+
export interface StackAllocCandidate {
|
|
6
|
+
readonly allocId: number;
|
|
7
|
+
readonly kind: AllocKind;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Find stack-allocation candidates in `fn` using an ownership result (computed
|
|
11
|
+
* fresh when not supplied). A candidate is an allocation that is:
|
|
12
|
+
* - of a "small" kind (object / refcell / box — not array/closure/string),
|
|
13
|
+
* - proven `owned`, and
|
|
14
|
+
* - never `escaped`.
|
|
15
|
+
*
|
|
16
|
+
* When `registry` is supplied, each candidate's site is marked with a
|
|
17
|
+
* `stackCandidate: true` flag merged into its existing ownership annotation.
|
|
18
|
+
* The marker is inert at lowering — it changes no emitted Wasm.
|
|
19
|
+
*/
|
|
20
|
+
export declare function findStackAllocCandidates(fn: IrFunction, registry?: AllocSiteRegistry, precomputed?: OwnershipResult): StackAllocCandidate[];
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
import { IrBinop, IrInstr, IrUnop } from '../nodes.js';
|
|
2
|
+
import { BlockType, Instr } from '../types.js';
|
|
3
|
+
import { BackendEmitter } from './emitter.js';
|
|
4
|
+
import { IrClassLowering, IrObjectStructLowering } from './handles.js';
|
|
5
|
+
export declare const OP: {
|
|
6
|
+
readonly CONST: 0;
|
|
7
|
+
readonly LOAD: 1;
|
|
8
|
+
readonly STORE: 2;
|
|
9
|
+
readonly ADD: 3;
|
|
10
|
+
readonly SUB: 4;
|
|
11
|
+
readonly MUL: 5;
|
|
12
|
+
readonly CMP_GT: 6;
|
|
13
|
+
readonly CMP_LT: 7;
|
|
14
|
+
readonly CMP_GE: 8;
|
|
15
|
+
readonly CMP_LE: 9;
|
|
16
|
+
readonly CMP_EQ: 10;
|
|
17
|
+
readonly NEG: 11;
|
|
18
|
+
readonly JZ: 12;
|
|
19
|
+
readonly JMP: 13;
|
|
20
|
+
readonly RET: 14;
|
|
21
|
+
readonly DIV: 15;
|
|
22
|
+
readonly CMP_NE: 16;
|
|
23
|
+
readonly TEE: 17;
|
|
24
|
+
readonly GLOBAL_GET: 18;
|
|
25
|
+
readonly GLOBAL_SET: 19;
|
|
26
|
+
readonly SELECT: 20;
|
|
27
|
+
readonly DROP: 21;
|
|
28
|
+
readonly UNREACHABLE: 22;
|
|
29
|
+
readonly CALL: 23;
|
|
30
|
+
readonly CALL_REF: 24;
|
|
31
|
+
readonly STRUCT_NEW: 25;
|
|
32
|
+
readonly STRUCT_GET: 26;
|
|
33
|
+
readonly STRUCT_SET: 27;
|
|
34
|
+
readonly JNZ: 28;
|
|
35
|
+
readonly THROW: 29;
|
|
36
|
+
readonly TRY_START: 30;
|
|
37
|
+
readonly TRY_END: 31;
|
|
38
|
+
};
|
|
39
|
+
export type Opcode = (typeof OP)[keyof typeof OP];
|
|
40
|
+
/**
|
|
41
|
+
* The bytecode equivalent of `BackendEmitter`'s `Instr[]` sink — the ONE seam
|
|
42
|
+
* generalisation #1715 required. A flat `code` opcode stream plus a side
|
|
43
|
+
* constant pool for f64 immediates. A label backpatch list lets `emitIf`
|
|
44
|
+
* forward-reference jump targets it does not yet know.
|
|
45
|
+
*/
|
|
46
|
+
export declare class BytecodeSink {
|
|
47
|
+
readonly code: number[];
|
|
48
|
+
readonly constPool: number[];
|
|
49
|
+
/**
|
|
50
|
+
* (a3 #1584) Structured-branch backpatch list. A `br` / `br_if` (`emitBr` /
|
|
51
|
+
* `emitBrIf`) emits a `JMP` / `JNZ` placeholder whose target is the construct
|
|
52
|
+
* `depth` levels out (De Bruijn) — UNKNOWN until the enclosing `block`/`loop`
|
|
53
|
+
* splices this sink. Each entry pins the operand slot + its outward depth.
|
|
54
|
+
* `emitBlock`/`emitLoop` resolve depth-0 entries (this construct) and carry
|
|
55
|
+
* deeper ones outward (depth-1). A sink with a non-empty list at function exit
|
|
56
|
+
* is an internal error (a `br` with no enclosing construct).
|
|
57
|
+
*/
|
|
58
|
+
readonly pendingBranches: {
|
|
59
|
+
slot: number;
|
|
60
|
+
depth: number;
|
|
61
|
+
}[];
|
|
62
|
+
/**
|
|
63
|
+
* (a4 #1584) Per-function exception table (table-scan model, §1c). One entry
|
|
64
|
+
* per `try` region: the protected code range `[tryStart, tryEnd)` (absolute
|
|
65
|
+
* code indices), the `catchTarget` THROW jumps to on a covered throw, and
|
|
66
|
+
* `spAtEntry` — the value-stack depth to truncate to on catch (always 0: a
|
|
67
|
+
* `try` is a statement-level node lowered at empty operand-stack depth).
|
|
68
|
+
* The VM scans this for the innermost covering entry on THROW; TRY_START/
|
|
69
|
+
* TRY_END are runtime no-ops (the table is authoritative). Travels with the
|
|
70
|
+
* function on its FuncEntry, alongside `code`/`constPool`.
|
|
71
|
+
*/
|
|
72
|
+
readonly exceptionTable: {
|
|
73
|
+
tryStart: number;
|
|
74
|
+
tryEnd: number;
|
|
75
|
+
catchTarget: number;
|
|
76
|
+
spAtEntry: number;
|
|
77
|
+
}[];
|
|
78
|
+
/** Intern an f64 immediate into the constant pool, returning its index. */
|
|
79
|
+
internConst(value: number): number;
|
|
80
|
+
/** Current write position (a jump target / patch site is a code index). */
|
|
81
|
+
here(): number;
|
|
82
|
+
/** Emit an opcode followed by zero or more inline integer operands. */
|
|
83
|
+
emit(op: Opcode, ...operands: number[]): void;
|
|
84
|
+
/**
|
|
85
|
+
* Emit a jump whose target is not yet known; returns the code index of the
|
|
86
|
+
* *operand slot* to backpatch once the target is known.
|
|
87
|
+
*/
|
|
88
|
+
emitJumpPlaceholder(op: typeof OP.JZ | typeof OP.JMP | typeof OP.JNZ | typeof OP.TRY_START): number;
|
|
89
|
+
/** Fill a previously-reserved jump operand slot with the resolved target. */
|
|
90
|
+
patch(slot: number, target: number): void;
|
|
91
|
+
/**
|
|
92
|
+
* (a3 #1584) Record a structured branch (`br`/`br_if`) whose target is the
|
|
93
|
+
* construct `depth` levels out. The placeholder jump was just emitted; `slot`
|
|
94
|
+
* is its operand index. Resolved by the enclosing `emitBlock`/`emitLoop`.
|
|
95
|
+
*/
|
|
96
|
+
recordPendingBranch(slot: number, depth: number): void;
|
|
97
|
+
/**
|
|
98
|
+
* #1584: append another sink's code at the current position, relocating its
|
|
99
|
+
* internal jump targets into this sink's address space and remapping its
|
|
100
|
+
* const-pool indices into this sink's pool. Used by the production
|
|
101
|
+
* `BytecodeEmitter.emitIf` to splice already-lowered `if`-arm buffers (the
|
|
102
|
+
* real `lower.ts` builds each arm into its own sink, exactly as it builds the
|
|
103
|
+
* WasmGC `if`'s `then`/`else` as separate `Instr[]`).
|
|
104
|
+
*
|
|
105
|
+
* (a3) An arm may carry UNPATCHED structured branches (`br`/`br_if` in
|
|
106
|
+
* `pendingBranches`) bound for an enclosing `block`/`loop`. Those slots
|
|
107
|
+
* relocate by `+base` and their depth-tagged entries migrate onto THIS sink's
|
|
108
|
+
* `pendingBranches` so the enclosing construct can resolve them. A leftover
|
|
109
|
+
* unpatched jump that is NOT a recorded structured branch is still an internal
|
|
110
|
+
* error (a forward `if`/`block` exit the owner forgot to patch).
|
|
111
|
+
*/
|
|
112
|
+
spliceArm(arm: BytecodeSink): void;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* #1584 PRODUCTION emitter. Implements the {@link BackendEmitter}<{@link
|
|
116
|
+
* BytecodeSink}> trait surface so the REAL `lower.ts` drives it identically to
|
|
117
|
+
* how it drives {@link WasmGcEmitter}<Instr[]> — same primitive set, same
|
|
118
|
+
* caller-owns-operand-order contract, different execution model. (This
|
|
119
|
+
* supersedes the #1715 proof's hand-driven emitter: the proof's thunked
|
|
120
|
+
* `emitIf` is replaced by the trait's pre-built-arm `emitIf`, since real
|
|
121
|
+
* `lower.ts` builds each arm into its own sink then hands them over.)
|
|
122
|
+
*
|
|
123
|
+
* STACK encoding for the first increment (contract §1a staging note). The opcode
|
|
124
|
+
* set lives above (`OP`, the single source of truth the VM imports read-only);
|
|
125
|
+
* this class only decides which opcode each primitive emits.
|
|
126
|
+
*/
|
|
127
|
+
export declare class BytecodeEmitter implements BackendEmitter<BytecodeSink> {
|
|
128
|
+
readonly backend: "bytecode";
|
|
129
|
+
/** Factory for a child sink — used by `lower.ts` to build `if`-arm buffers. */
|
|
130
|
+
newSink(): BytecodeSink;
|
|
131
|
+
/**
|
|
132
|
+
* The raw-`Instr` escape hatch (the #1584 contract §0a-1). `lower.ts` still
|
|
133
|
+
* has ~119 inline `out.push({op})` sites for op families not yet migrated
|
|
134
|
+
* behind the trait. On the WasmGC path those append to the `Instr[]`; on the
|
|
135
|
+
* bytecode path they reach a node family with no opcode realization yet, so
|
|
136
|
+
* this throws — surfacing the not-yet-migrated boundary loudly rather than
|
|
137
|
+
* silently mis-lowering. As each op family migrates (§2a), its `lower.ts`
|
|
138
|
+
* sites move from `pushRaw` to a typed emitter primitive + opcode.
|
|
139
|
+
*/
|
|
140
|
+
pushRaw(_out: BytecodeSink, instr: Instr): void;
|
|
141
|
+
emitConst(instr: Extract<IrInstr, {
|
|
142
|
+
kind: "const";
|
|
143
|
+
}>, funcName: string, out: BytecodeSink): void;
|
|
144
|
+
emitBinary(op: IrBinop, out: BytecodeSink): void;
|
|
145
|
+
emitUnary(op: IrUnop, out: BytecodeSink): void;
|
|
146
|
+
emitLocalGet(index: number, out: BytecodeSink): void;
|
|
147
|
+
emitLocalSet(index: number, out: BytecodeSink): void;
|
|
148
|
+
emitLocalTee(index: number, out: BytecodeSink): void;
|
|
149
|
+
emitGlobalGet(index: number, out: BytecodeSink): void;
|
|
150
|
+
emitGlobalSet(index: number, out: BytecodeSink): void;
|
|
151
|
+
emitDrop(out: BytecodeSink): void;
|
|
152
|
+
emitSelect(out: BytecodeSink): void;
|
|
153
|
+
emitReturn(out: BytecodeSink): void;
|
|
154
|
+
emitUnreachable(out: BytecodeSink): void;
|
|
155
|
+
/**
|
|
156
|
+
* Structured two-arm conditional. Mirrors `WasmGcEmitter.emitIf(blockType,
|
|
157
|
+
* then: Instr[], els: Instr[], out)`: the caller (real `lower.ts`) pre-lowers
|
|
158
|
+
* each arm into its own {@link BytecodeSink} (via `newSink()`), then hands
|
|
159
|
+
* them here. The cond value is already on the stack. The stack VM has no
|
|
160
|
+
* structured block, so we lower to JZ/JMP + spliced arms with backpatched
|
|
161
|
+
* targets:
|
|
162
|
+
*
|
|
163
|
+
* <cond on stack>
|
|
164
|
+
* JZ elseLabel
|
|
165
|
+
* <then arm>
|
|
166
|
+
* JMP endLabel
|
|
167
|
+
* elseLabel: <else arm>
|
|
168
|
+
* endLabel:
|
|
169
|
+
*
|
|
170
|
+
* `blockType` is ignored (the bytecode VM is untyped over boxed values); it is
|
|
171
|
+
* part of the trait signature for the WasmGC realization.
|
|
172
|
+
*/
|
|
173
|
+
emitIf(_blockType: BlockType, then: BytecodeSink, els: BytecodeSink, out: BytecodeSink): void;
|
|
174
|
+
emitBr(depth: number, out: BytecodeSink): void;
|
|
175
|
+
emitBrIf(depth: number, out: BytecodeSink): void;
|
|
176
|
+
/**
|
|
177
|
+
* Structured `block`. Splices the pre-lowered `body` sink at the current
|
|
178
|
+
* position, then resolves the body's pending structured branches: a branch
|
|
179
|
+
* with `depth === 0` targets THIS block, so it jumps to the block's EXIT (the
|
|
180
|
+
* code position just past the spliced body); deeper branches belong to an
|
|
181
|
+
* outer construct and migrate outward with `depth - 1`. `blockType` is ignored
|
|
182
|
+
* (the VM is untyped over boxed values) — it is part of the trait signature
|
|
183
|
+
* for the WasmGC realization.
|
|
184
|
+
*/
|
|
185
|
+
emitBlock(_blockType: BlockType, body: BytecodeSink, out: BytecodeSink): void;
|
|
186
|
+
/**
|
|
187
|
+
* Structured `loop`. Like `emitBlock`, but a `depth === 0` branch targets the
|
|
188
|
+
* loop HEADER (the code position where the body begins) — `br 0` is "continue"
|
|
189
|
+
* — so the back-edge target is captured BEFORE the splice.
|
|
190
|
+
*/
|
|
191
|
+
emitLoop(_blockType: BlockType, body: BytecodeSink, out: BytecodeSink): void;
|
|
192
|
+
/**
|
|
193
|
+
* Resolve the structured branches `spliceArm` just migrated onto `out`
|
|
194
|
+
* (`out.pendingBranches[firstNew..]`): patch each `depth === 0` branch to
|
|
195
|
+
* `selfTarget` (block exit / loop header) and drop it; decrement the depth of
|
|
196
|
+
* each deeper branch so the next-outer construct resolves it. Entries added
|
|
197
|
+
* before `firstNew` (already-pending outer branches) are untouched.
|
|
198
|
+
*/
|
|
199
|
+
private resolveSplicedBranches;
|
|
200
|
+
emitCall(funcIdx: number, out: BytecodeSink): void;
|
|
201
|
+
emitCallRef(funcTypeIdx: number, out: BytecodeSink): void;
|
|
202
|
+
emitAggregateNew(_layout: IrObjectStructLowering, fieldCount: number, out: BytecodeSink): void;
|
|
203
|
+
emitFieldGet(layout: IrObjectStructLowering | IrClassLowering, name: string, out: BytecodeSink): void;
|
|
204
|
+
emitFieldSet(layout: IrObjectStructLowering | IrClassLowering, name: string, out: BytecodeSink): void;
|
|
205
|
+
emitThrow(_tagIdx: number, out: BytecodeSink): void;
|
|
206
|
+
emitRethrow(_depth: number, out: BytecodeSink): void;
|
|
207
|
+
/**
|
|
208
|
+
* Structured `try`. Table-scan realization:
|
|
209
|
+
*
|
|
210
|
+
* TRY_START <catchTarget> ; no-op marker
|
|
211
|
+
* <tryBody>
|
|
212
|
+
* TRY_END ; no-op marker (end of protected region)
|
|
213
|
+
* JMP endLabel ; normal exit skips the handler
|
|
214
|
+
* catchTarget: <handler> ; THROW lands here (VM pushed exn, truncated sp)
|
|
215
|
+
* endLabel:
|
|
216
|
+
*
|
|
217
|
+
* + an exceptionTable entry {tryStart, tryEnd, catchTarget, spAtEntry:0}.
|
|
218
|
+
* The protected region is [tryStart, tryEnd) = the spliced tryBody (between the
|
|
219
|
+
* markers). spAtEntry is 0: `try` is a statement-level node lowered at empty
|
|
220
|
+
* operand-stack depth, so on catch the VM truncates back to the empty base.
|
|
221
|
+
*
|
|
222
|
+
* Handler selection: our system has ONE exception tag (`__exn`), so a thrown
|
|
223
|
+
* value always matches a source `catch` if present; `catchAll` (emitted for
|
|
224
|
+
* the finally-leak path) is the handler only when there is no `catch`. With a
|
|
225
|
+
* single tag, `catchAll`-alongside-`catch` is the unreachable non-`__exn`
|
|
226
|
+
* leak path — we still splice it after the catch for structural fidelity, but
|
|
227
|
+
* `catchTarget` points at the live handler.
|
|
228
|
+
*/
|
|
229
|
+
emitTry(_blockType: BlockType, body: BytecodeSink, catches: {
|
|
230
|
+
tagIdx: number;
|
|
231
|
+
body: BytecodeSink;
|
|
232
|
+
}[], catchAll: BytecodeSink | undefined, out: BytecodeSink): void;
|
|
233
|
+
emitVecLen(): void;
|
|
234
|
+
emitVecDataPtr(): void;
|
|
235
|
+
emitElemGet(): void;
|
|
236
|
+
emitVecNewFixed(): void;
|
|
237
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { BytecodeSink } from './bytecode-emitter.js';
|
|
2
|
+
/**
|
|
3
|
+
* One entry in a {@link Program}'s function table. A function lowers to its own
|
|
4
|
+
* {@link BytecodeSink} (own `code` + own `constPool` + own jump address-space),
|
|
5
|
+
* so a call switches BOTH `code` and `constPool` and the call frame restores
|
|
6
|
+
* both on return.
|
|
7
|
+
*/
|
|
8
|
+
export interface FuncEntry {
|
|
9
|
+
/** The function's opcode stream (`sink.code`). */
|
|
10
|
+
code: number[];
|
|
11
|
+
/** The function's f64 immediate pool (`sink.constPool`). */
|
|
12
|
+
constPool: number[];
|
|
13
|
+
/** Declared parameter count — `CALL` pops this many args (arity not inline). */
|
|
14
|
+
arity: number;
|
|
15
|
+
/**
|
|
16
|
+
* Local-slot count (params + declared locals). A call installs a fresh
|
|
17
|
+
* `locals[nLocals]` seeded `locals[0..arity-1]` with the popped args; higher
|
|
18
|
+
* slots are lazily 0-init on first `LOAD` (same contract as `runBytecode`).
|
|
19
|
+
*/
|
|
20
|
+
nLocals: number;
|
|
21
|
+
/**
|
|
22
|
+
* #1584 a4 try-throw: the STATIC per-function exception table (table-scan
|
|
23
|
+
* model). `THROW` scans it for the innermost entry whose `[tryStart, tryEnd)`
|
|
24
|
+
* covers the throwing pc, then truncates the value stack to `spAtEntry`, pushes
|
|
25
|
+
* the exn, and jumps to `catchTarget`. `TRY_START`/`TRY_END` are runtime
|
|
26
|
+
* no-ops (the table is authoritative). Array-of-objects with number-only
|
|
27
|
+
* fields (no `readonly`/nested-struct — same js2wasm-codegen accommodation as
|
|
28
|
+
* the FuncEntry array fields). `spAtEntry` is always 0 today (`try` is a
|
|
29
|
+
* statement-level node with an empty operand stack at entry), but it travels
|
|
30
|
+
* in the entry for correctness if a non-statement `try` ever appears.
|
|
31
|
+
*/
|
|
32
|
+
exceptionTable: ExcEntry[];
|
|
33
|
+
}
|
|
34
|
+
/** One protected region in a {@link FuncEntry}'s {@link FuncEntry.exceptionTable}. */
|
|
35
|
+
export interface ExcEntry {
|
|
36
|
+
/** Absolute code index of the region start (inclusive). */
|
|
37
|
+
tryStart: number;
|
|
38
|
+
/** Absolute code index of the region end (exclusive). */
|
|
39
|
+
tryEnd: number;
|
|
40
|
+
/** Absolute code index of the catch arm to jump to on a covered throw. */
|
|
41
|
+
catchTarget: number;
|
|
42
|
+
/** Value-stack depth at try-entry; the stack is truncated to this on catch. */
|
|
43
|
+
spAtEntry: number;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* A whole compiled program: a function table plus the entry function index.
|
|
47
|
+
* `runProgram` runs `functions[entry]` as the bottom frame.
|
|
48
|
+
*/
|
|
49
|
+
export interface Program {
|
|
50
|
+
functions: FuncEntry[];
|
|
51
|
+
entry: number;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Run a whole {@link Program} — the multi-frame call-stack VM (#1584 a1).
|
|
55
|
+
*
|
|
56
|
+
* @param program function table + entry index
|
|
57
|
+
* @param args initial values of the entry function's locals 0..arity-1
|
|
58
|
+
* @returns the number the entry function's `RET` leaves on the stack
|
|
59
|
+
*/
|
|
60
|
+
export declare function runProgram(program: Program, args: readonly number[]): number;
|
|
61
|
+
/**
|
|
62
|
+
* Run a single compiled bytecode function (the #1715 numeric proof entry).
|
|
63
|
+
* Implemented as a one-function {@link Program} so the single- and multi-frame
|
|
64
|
+
* paths share one dispatch loop.
|
|
65
|
+
*
|
|
66
|
+
* @param code flat opcode + inline-operand stream (`sink.code`)
|
|
67
|
+
* @param constPool f64 immediates referenced by `OP.CONST <poolIdx>` (`sink.constPool`)
|
|
68
|
+
* @param args initial values of locals 0..n-1 (the function parameters);
|
|
69
|
+
* any higher local index used by STORE/LOAD is lazily 0-init.
|
|
70
|
+
* @returns the number left on the stack by `OP.RET`
|
|
71
|
+
*/
|
|
72
|
+
export declare function runBytecode(code: readonly number[], constPool: readonly number[], args: readonly number[]): number;
|
|
73
|
+
/** Convenience: run a {@link BytecodeSink}'s program as a single function. */
|
|
74
|
+
export declare function runSink(sink: BytecodeSink, args: readonly number[]): number;
|