ADR-004 — AOT compilation over JIT/interpreter

Status: Accepted Date: 2026-04-27

Context

A JS-to-Wasm system can run code in three ways: an interpreter (a bytecode VM compiled to Wasm); a JIT engine (a compiler compiled to Wasm, generating new code at runtime); or AOT (the source is compiled to static Wasm at build time, and the runtime executes that Wasm directly).

An interpreter inside Wasm typically runs hot code 25–50× slower than natively-compiled equivalents. A JIT compiler shipped inside Wasm adds 2MB+ of binary overhead and depends on speculative optimization to amortize its own dispatch cost — but most serverless and edge Wasm hosts disable runtime code generation for security isolation, which forces JIT engines back onto their interpreter or baseline tier. AOT, by contrast, performs all compilation work at build time: the deployed module is plain Wasm that the host can execute (and, where available, JIT) directly.

Decision

AOT only. The js2wasm pipeline performs all type analysis, lowering, and Wasm emission at build time. There is no runtime code generation inside the compiled module.

Consequences

Positive: maximum performance for static code; minimal binary size (no embedded compiler or interpreter); cold start is proportional to Wasm instantiation; the output is a plain Wasm module the host runtime can JIT itself when permitted.

Negative: features that depend on runtime code generation cannot be served by the compiled module alone. Specifically, eval() and dynamic import() of arbitrary source strings are not supported in the compiled module without an external code-generation path. For statically known strings, eval() can be folded at compile time. For dynamic strings, a host import is used that compiles a fresh Wasm module via the host's compiler (see ADR-010). There is no in-Wasm interpreter fallback.

Alternatives rejected