# QuickJS Feasibility Study — Results Report > Self-hosting the Edict compiler: can the full compiler pipeline run inside QuickJS WASM? **Date**: 2026-03-13 (original study) — **Updated**: 2026-03-19 **Issue**: [#134](https://github.com/Sowiedu/Edict/issues/134) **Platform**: darwin-arm64 (Apple Silicon), Node v22.14.0 **QuickJS**: `quickjs-emscripten` (WASM build of QuickJS 2025-09-13) > [!NOTE] > This report was the original feasibility study. For the definitive current status of self-hosting, see [self-hosting-status.md](self-hosting-status.md). For contract verification research, see [self-hosting-contracts-research.md](self-hosting-contracts-research.md). --- ## Executive Summary The Edict compiler's **check + compile pipeline (phases 1–5)** runs successfully inside QuickJS WASM. The check pipeline (phases 1–3) achieves a **3.7x slowdown** vs native Node.js — viable for real-time agent use in sandboxed environments. The original study identified binaryen (WASM codegen) as a critical blocker. **This blocker has been resolved**: binaryen was replaced with a pure-JS WASM binary encoder ([#198](https://github.com/Sowiedu/Edict/issues/198)), enabling WASM codegen inside QuickJS. The **remaining blockers have been resolved**: contract verification (phase 4) now uses a built-in QF-LIA solver (#205–#208), and WASM execution (phase 6) uses a pure-JS WASM interpreter. The full self-hosting loop (check → compile → execute) runs inside QuickJS. --- ## Bundle Sizes | Bundle | Entry Point | Size | Contents | |--------|-------------|------|----------| | Check-only | `dist/edict-quickjs-check.js` | **365.7 KB** | Phases 1–3: validate, resolve, typeCheck, effectCheck, lint, patch, compose | | Full (compile) | `dist/edict-quickjs-full.js` | **932 KB** | Phases 1–5: check + WASM codegen via pure-JS encoder | Both bundles use IIFE format (QuickJS doesn't support ESM `import`). Node.js modules are shimmed to empty stubs via esbuild. Z3 is excluded. For comparison: the browser ESM bundles are 340.5 KB (phases 1–3) and 811.4 KB (full with WASM encoder). --- ## Performance Comparison | Metric | QuickJS (WASM) | Native (Node.js) | Ratio | |--------|---------------|-------------------|-------| | QuickJS init | 9.3ms | — | — | | Bundle load | 30.6ms | — | — | | `checkBrowser(fibonacci)` | **2.3ms** | **0.6ms** | **3.7x** | - **5 runs**, median values - Sample program: `fibonacci.edict.json` (recursive fibonacci with contracts, ~40 AST nodes) - Native Node.js uses JIT compilation; QuickJS is a pure interpreter running in WASM — 3.7x is remarkably good ### Individual Runs (ms) | Run | QuickJS | Native | |-----|---------|--------| | 1 | 3.2 | 3.4 | | 2 | 2.2 | 0.6 | | 3 | 2.4 | 0.7 | | 4 | 2.1 | 0.6 | | 5 | 2.3 | 0.3 | First run includes JIT warmup (Node) and bytecode compilation (QuickJS). --- ## Memory Usage QuickJS runtime memory after loading bundle + running 5 checks: | Category | Count | Size | |----------|-------|------| | Memory allocated | 12,823 blocks | ~100 KB | | Memory used | — | **683 KB** | | Atoms (interned strings) | 2,075 | ~82 KB | | Objects | 3,770 | ~181 KB | | Bytecode functions | 437 | ~219 KB | Total memory footprint: **~684 KB** — very lightweight. --- ## Resolved Blockers ### ~~Binaryen Incompatible with QuickJS~~ → ✅ Resolved **Original blocker**: Binaryen's npm package used top-level `await` (incompatible with IIFE format) and required the `WebAssembly` API to load its own WASM binary. **Resolution**: Binaryen was replaced with a pure-JS WASM binary encoder ([#198](https://github.com/Sowiedu/Edict/issues/198)). The encoder implements the ~75 binaryen API methods Edict actually uses (~730 call sites), producing valid WASM MVP binaries without any native dependencies. See [binaryen-api-audit.md](binaryen-api-audit.md) for the audit that guided this replacement. ### Missing Web APIs → ✅ Mitigated QuickJS lacks `TextEncoder` and `TextDecoder`. These are polyfilled with minimal UTF-8 implementations (~40 lines). Solved. ### No ESM Support in QuickJS → ✅ Mitigated QuickJS doesn't support `import`/`export` in `evalCode`. Solved by using IIFE format bundles. --- ## ~~Remaining Blockers~~ → All Resolved ### ~~1. Contract Verification — Z3~~ ✅ Resolved (#205–#208) Replaced Z3 with a built-in pure-JS QF-LIA solver. Covers 100% of current example program contracts (14/14). Quantified/array contracts degrade gracefully to `undecidable_predicate`. ### ~~2. WASM Execution — No `WebAssembly` API~~ ✅ Resolved A pure-JS WASM interpreter (`src/codegen/wasm-interpreter.ts`) parses and executes WASM binaries without the native `WebAssembly` API. The full self-hosting loop (JSON AST → check → compile → execute) now runs entirely inside QuickJS via `EdictQuickJS.compileAndRun()`. --- ## What Works | Feature | Status | |---------|--------| | Schema validation (phase 1) | ✅ Works | | Name resolution (phase 2a) | ✅ Works | | Type checking (phase 2b) | ✅ Works | | Effect checking (phase 3) | ✅ Works | | **WASM codegen (phase 5)** | ✅ **Works** (pure-JS encoder) | | Lint | ✅ Works | | Patch engine | ✅ Works | | Fragment composition | ✅ Works | | Compact AST expansion | ✅ Works | | Schema migration | ✅ Works | | Contract verification (phase 4) | ⚡ Partial (QF-LIA via built-in solver; quantified/array → undecidable) | | **WASM execution (phase 6)** | ✅ **Works** (pure-JS WASM interpreter) | --- ## Status: Fully Self-Hosted ✅ All original blockers have been resolved: 1. **Check + codegen** (#198): Pure-JS WASM encoder replaced binaryen 2. **Contract verification** (#205–#208): Built-in QF-LIA solver replaced Z3 3. **WASM execution**: Pure-JS WASM interpreter replaced native `WebAssembly` API The 925 KB full bundle runs the complete pipeline (phases 1–6) inside QuickJS. Programs are validated, type-checked, effect-checked, contract-verified, compiled to WASM, and executed — all self-hosted. ### Remaining Improvements - **Interpreter builtin coverage**: Extend the WASM interpreter host import set to support array builtins (`array_map`, `array_get`, etc.) - **Performance**: The interpreted execution path is slower than native `WebAssembly.instantiate()`. For performance-critical use, execute WASM output in a native runtime. --- ## Related Documents | Document | Purpose | |----------|---------| | [self-hosting-status.md](self-hosting-status.md) | Definitive current status of self-hosting | | [self-hosting-contracts-research.md](self-hosting-contracts-research.md) | Z3/contract verification research (#199) | | [binaryen-api-audit.md](binaryen-api-audit.md) | API audit that informed the binaryen replacement | ## Files Created (Original Study) | File | Purpose | |------|---------| | `scripts/build-quickjs-bundle.ts` | esbuild script for IIFE bundles | | `scripts/quickjs-feasibility.ts` | Benchmark harness | | `docs/quickjs-feasibility-report.md` | This report | | `quickjs-feasibility-results.json` | Machine-readable results | --- ## Conclusion **The Edict compiler is fully self-hosted in QuickJS-WASM** — all 6 pipeline phases run inside the sandbox. A 925 KB IIFE bundle with 684 KB memory footprint is lightweight enough for edge deployment. Agents can validate, type-check, effect-check, contract-verify, compile, and execute Edict programs entirely within a QuickJS sandbox.