>_ENGINEERING REPORT
node-js is a compiled JavaScript runtime in Rust, hosted on the fusevm bytecode VM and its Cranelift JIT over a JsHost object heap. This report describes the architecture, the value model, the builtin objects, the parity tooling, the current state of the tree, and the dependency posture. The statements below are facts about the design and the manifest, not aspirational metrics.
Summary
V8 compiles JavaScript to its own bytecode and JITs it with its own engine. node-js takes a different path: it lexes and parses JavaScript to an AST, lowers that to fusevm bytecode, and runs it on a compiled VM with a Cranelift JIT. Arithmetic and comparison operators lower to native VM ops so the JIT can trace hot loops; JS-specific behaviour is served by a JsHost object heap through a numbered builtin-call protocol and a strict numeric hook. It joins fusevm alongside zshrs, stryke, awkrs, pythonrs, and rubylang, and reuses that shared bytecode VM and JIT rather than shipping its own.
The binary is node. The current tree implements the lexer/parser (including arrow-function => lookahead and template-literal re-lexing), AST-to-bytecode lowering with function/arrow/try sub-chunks, var/let/const bindings, the full operator surface, all the control-flow constructs (including try/catch/finally and for…of/for…in), functions/arrows/closures/new, array and object literals with spread and template literals, and a broad builtin surface across eight namespaces. Behaviour is checked against the reference node by both a frozen-corpus parity harness and a generative fuzzer. Classes, async, generators, ES modules, and the Node.js standard library are planned; AOT and the persistent bytecode cache are scaffolded but not yet CLI-exposed.
Hosting on fusevm
node-js contains no virtual machine or JIT of its own. The execution path is:
JS source → lexer → parser → AST → lower to fusevm bytecode → fusevm VM + Cranelift JIT
│
JsHost heap (objects/arrays/strings, CallBuiltin, numeric hook)
Shared engine
fusevm is pulled from crates.io with the jit, jit-disk-cache, and aot features. JIT and VM improvements land once and benefit zshrs, stryke, awkrs, pythonrs, rubylang, and node-js together.
Native arithmetic
Operators lower to native fusevm ops; a strict numeric hook supplies JS semantics (+ string concat, the == coercion matrix, ToInt32 for bitwise ops, truthiness) only for the operand cases the VM can't compute natively.
Sub-chunk lowering
The compiler emits the main chunk plus a table of function/arrow sub-chunks and try-block chunks; load_merged rebases a freshly compiled program's func/try ids above those already on the host before running (src/lib.rs, src/compiler.rs).
Scaffolded AOT
The crate is built crate-type = ["rlib", "staticlib"] with fusevm's aot feature so an AOT object can later be linked against the runtime staticlib into a standalone executable — the same shape as pythonrs. Not yet wired to a CLI flag.
Value model
Primitive values — number, boolean, null, and undefined — ride through the VM directly as fusevm::Value. The reference types (objects, arrays, strings, and functions) live on the JsHost object heap behind Value::Obj(u32) handles, so mutation is observed through every alias — a.push(x) changes the array the caller still holds, exactly as in JavaScript. Object property insertion order is preserved via an IndexMap, matching what iteration and JSON.stringify observe.
Truthiness, equality, and coercion follow JavaScript, not the VM's default numeric rules: 0, NaN, "", null, undefined, and false are falsy; loose == runs the abstract-equality coercion matrix; + is string concatenation when either operand is a string; bitwise operators apply ToInt32 before operating. These are routed through host operations rather than relying on the VM's native numeric branch.
Builtin objects
Eight builtin namespaces and a broad per-type method surface are registered as fusevm builtins (src/builtins.rs), reachable through the compiler's CallBuiltin lowering and member-access dispatch:
console / JSON
console.log; JSON.stringify / JSON.parse with insertion-ordered round-trips.
Math / Number
Math — floor/ceil/round/trunc/abs/sign/max/min/pow/sqrt/cbrt/random/hypot/log/log2/log10/exp/trig, PI/E; Number constants (MAX_SAFE_INTEGER, EPSILON, …).
Object / Array
Object.keys/values/entries/hasOwnProperty; array methods map/filter/reduce/forEach/find/every/some/push/pop/slice/join/concat/includes/indexOf/flat/flatMap/reverse/fill/at.
String / global
String methods charAt/charCodeAt/padStart/padEnd/repeat/replace/replaceAll/startsWith/endsWith/…; globals parseInt/parseFloat/isNaN/isFinite, Boolean, globalThis.
Differential parity
Correctness is measured, not asserted, by two tools that diff node-js against the reference node. Both are development tools that need node on PATH, so CI never runs them.
Frozen-corpus harness
parity (src/bin/parity.rs) runs a fixed corpus through node-js and the reference node, diffing stdout. The expected outputs are frozen into tests/data/parity_expected.txt, which tests/parity.rs replays with no node installed — so the regression check runs in CI without a Node.js toolchain.
Generative fuzzer
parity-fuzz (src/bin/parity_fuzz.rs) generates thousands of deterministic-output JS snippets and diffs node -e against the reference, delta-debugging each divergence to a minimal repro. Subprocess-only (never links the lib) and std-only (no rand).
Fuzz generators are biased toward where a JS frontend is likely to disagree with the reference: float representation and the exponential-notation threshold, ToInt32 bitwise wrap, the == coercion matrix, + coercion, string/array methods, toFixed/toPrecision rounding, and JSON round-trips.
Component status
| Component | State | Notes |
|---|---|---|
| lexer / parser → AST | Implemented | Newline-aware tokenizer; arrow => lookahead; template ${...} re-lex (src/lexer.rs, src/parser.rs). |
| AST → fusevm bytecode | Implemented | Native arithmetic + CallBuiltin; func/arrow/try sub-chunks; id rebasing (src/compiler.rs, src/lib.rs). |
| bindings & operators | Implemented | var/let/const; arithmetic/comparison/logical/bitwise/nullish, typeof/void/delete/instanceof/in, ?:, updates, compound assign. |
| control flow | Implemented | if/else, while, do…while, for, for…in, for…of, switch, break/continue/return, throw, try/catch/finally. |
| functions / arrows / closures | Implemented | Declarations and expressions, arrow functions, closures, recursion, new (src/host.rs). |
| literals / spread / templates | Implemented | Array/object literals, member/index access, spread ..., template literals. |
| builtin objects & methods | Implemented | console/Math/JSON/Object/Array/Number/String/Boolean + broad array/string methods (src/builtins.rs). |
| parity harness + fuzzer | Implemented | Frozen corpus replayed no-node (tests/parity.rs); generative fuzzer (src/bin/parity_fuzz.rs). Dev tools, never in CI. |
| AOT native-exe & bytecode cache | Scaffolded | staticlib + fusevm aot/jit-disk-cache features enabled (mirrors pythonrs); not yet exposed on the CLI. |
| classes / async / modules / stdlib | Planned | class, async/await, generators, ES modules, destructuring, default/rest params, RegExp, Map/Set/Promise, Node.js stdlib. |
Dependency posture
Dependencies are kept foundational and durable. Direct dependencies from Cargo.toml:
| Crate | Role |
|---|---|
| fusevm | Language-agnostic bytecode VM + Cranelift JIT (jit, jit-disk-cache, aot) |
| clap | CLI argument parsing (derive, cargo) |
| thiserror | Error type derivation |
| rustc-hash | Fast hashing in the frontend |
| indexmap | Insertion-ordered object property storage (observable in iteration / repr) |
The parity and parity-fuzz binaries are subprocess-only and add no runtime dependencies; the fuzzer is std-only (no rand), deriving determinism from a seeded generator.
Compatibility & longevity
Reference semantics
Behaviour tracks the reference node as the compatibility target, checked by a frozen-corpus harness and a generative fuzzer over the coercion and number-formatting edge cases.
Ordered objects
Property insertion order is preserved, matching what JS programs observe in iteration and JSON.stringify.
Cross-architecture
macOS aarch64 and Linux x86_64 / aarch64 via the Cranelift JIT; portable bytecode underneath.
Standalone crate
An explicit empty [workspace] keeps node-js buildable on its own, independent of the meta repo.
Links
- Docs — index.html
- Source — github.com/MenkeTechnologies/node-js
- Issues — github.com/MenkeTechnologies/node-js/issues
- fusevm — github.com/MenkeTechnologies/fusevm (the shared VM)
- License — MIT (LICENSE)