Skip to content
AI SI Daily
中文

Search the site

↑↓ Select · Enter Open · Esc Close

● Hands-on

quickjs-oxide: Can an AI-written, safe-Rust QuickJS keep pace with C?

AI Rewrites tests quickjs-oxide, a zero-unsafe Rust rewrite of QuickJS: how AI built it, what we reproduced on Test262 and benchmarks, and where it still falls short.

AI SI Daily EditorialTested: Debian 13 VM, 8 vCPU Xeon, 15 GB RAM, Rust 1.99.0, GCC 14.2 (2026-10-22)

QuickJS is Fabrice Bellard’s small, embeddable JavaScript engine, written in C. quickjs-oxide rewrites it in Rust with zero unsafe code. The project team says AI did most of the writing and claims the engine runs about as fast as the latest QuickJS release. It tests whether an AI-driven rewrite, followed by human-led performance work, can bring memory-safe Rust level with a carefully optimized C interpreter.

The numbers here come from two places: what the project says about itself, and what we got when we built and ran it ourselves on October 22, 2026 (our test environment is listed above the article). They are labeled project claims and our measurements respectively.

What quickjs-oxide is

The project describes itself as “an independent Rust rewrite of QuickJS targeting semantic feature parity” with QuickJS 2026-06-04. It targets Bellard’s release, not the community fork quickjs-ng. Distributed under the MIT license, it ships a qjs command-line binary, a Rust embedding API on crates.io and a WebAssembly build that powers an in-browser demo.

According to the README, every crate sets unsafe_code = "forbid". That covers the garbage collector, the regular-expression engine and the Unicode tables. The only third-party crates in the engine are num-bigint and num-traits; the native host adds tz-rs for time zones. In its current form, the core compiles every function to register bytecode, using inline caches, NaN-boxed values and a mark-sweep collector.

How AI built it

The account comes from the team’s announcement on X by PocketJS (@pocket_js, October 20, 23:16 UTC+8). Describing the engine as “implemented from scratch by AI in Rust”, it lays out three phases:

  1. One month to reach a high Test262 pass rate.
  2. Another month “cleaning up the code, getting rid of the AI slop,” reaching competitive speed with a stack VM and reference counting.
  3. Two days to rewrite the core into a register VM, over the RustChinaConf weekend.

The commit history fits that timeline, though it cannot show how much of the code AI wrote. The repository has 921 commits from July 11 to October 22, 2026 (UTC+8):

  • Yifeng “Evan” Wang (GitHub doodlewind) authored about 660, nearly all in July and August.
  • A second contributor, GitHub user Eric-Song-Nop, authored most of the remaining 258 commits between September and October.
  • The pull requests that introduced the register-based “new core” were merged on October 19, the same day as v0.3.0.

Evan (@ewind_dev) described the first phase on X: for the first few weeks he used /goal to push Test262 completion “mindlessly”. That left performance “extremely poor”, until @ericoolen_eric rescued it from “a gap of over a hundred times” (our translation from Chinese).

Which models did the work? The announcement doesn’t say, and the repository has no write-up. Commit trailers give a partial answer: 48 commits name an AI co-author. Of those, 44 credit “Claude Opus 5.5”, two credit “Claude Fable 5” and two credit “Claude Code”, all from September onward. The July–August commits carry no AI trailers, so the agent behind the first phase remains unstated. Neither the time spent per phase in hours nor the token cost is disclosed. The repository’s AGENTS.md sets a strict benchmarking protocol for agents: serial measurements, retired-instruction gates and “no parallel agents” during a run.

Conformance: we reproduced the Test262 number

Project claim (docs/test262.md): 81,441 of the 83,562 Test262 variants run under QuickJS 2026-06-04’s own test configuration pass, or 97.462%. Across the full 102,037-variant corpus, the rate is 79.815%.

Our measurement: we ran the project’s full Test262 script with 6 workers. It downloaded the pinned Test262 commit and reported pass=81441 fail=2121 unsupported=208 skipped=18475; the 2,121 non-passes include the 208 variants the runner does not yet execute. The script confirmed that our results match the project’s frozen result vector.

That headline figure needs context: its denominator follows QuickJS’s own skip list. About 1,870 variants fail on purpose because the team decided to drop five features (deviation ledger). with is a SyntaxError, direct eval runs as indirect eval, a runtime has one realm, and the engine never consults Symbol.species or Symbol.unscopables. We confirmed the with behavior directly: a one-line with script runs on QuickJS but fails on quickjs-oxide.

Performance: the speed claim held up in our test, the memory claim did not

Project claim (README): relative to QuickJS 2026-06-04’s run time, quickjs-oxide takes 0.94× on V8 v7, 1.15× on Octane 2.0 and 0.99× on Are We Fast Yet (AWFY). These are geometric means over 37 fixed-work programs on one Linux machine. That baseline is demanding, as Bellard reported this QuickJS release as 42% faster than its predecessor on the bench-v8 (aka V8 v7) score.

Our measurement: we built QuickJS 2026-06-04 from Bellard’s tarball (its SHA-256 matched the project’s pin) using the default Makefile. We built quickjs-oxide in release mode, which took 37 seconds. We then used the project’s own bench.py to build the same 37 benchmark programs from the pinned V8 v7, Octane and AWFY sources and ran each 4 times in ABBA order, pinned to one CPU. Geometric-mean time relative to QuickJS:

Suite Project claim Our run
V8 v7 (8) 0.94× 0.91×
Octane 2.0 (15) 1.15× 1.13×
AWFY (14) 0.99× 0.96×
All 37 1.04× 1.01×

Those averages mask a wide spread. quickjs-oxide ran markedly faster on Earley-Boyer, RayTrace and AWFY Storage (0.56–0.75×), but clearly slower on Octane GameBoy (2.26×), CodeLoad (1.77×), zlib (1.59×) and Mandreel (1.45×). On the project’s BigInt micro-probes, it took 4–15× longer than QuickJS. Because we ran on a shared cloud VM, individual ratios should be treated as approximate.

Memory: the X announcement says “around 30% more memory than QuickJS.” By contrast, the v0.3.1 release notes give a peak-RSS geometric mean of 1.59× QuickJS. Our /usr/bin/time measurement on the 37 programs came to 1.57×, and an empty script peaked at about 3.4 MiB versus 2.6 MiB for QuickJS. We could not find which workload the 30% figure refers to.

Limits and risks

  • Not a drop-in qjs yet. The std and os modules, the REPL, Worker, qjsc and the C API are not supported (compatibility). Apps relying on QuickJS’s C embedding must switch to the Rust API.
  • No unsafe does not mean no bugs. Safe Rust rules out classes of memory-corruption bugs in the engine’s own code, but logic errors, panics, denial of service and bugs in the Rust toolchain or standard library are still possible. We found no independent security audit.
  • No 32-bit targets yet. PocketJS replied on X that 32-bit targets such as armeabi-v7a and x86 are “on our roadmap” but not supported yet.
  • Interpreter only. It is a pure interpreter without a JIT, which the team calls a “best fit for embedded devices.”
  • Young code. Releases started on October 11, 2026, and v0.x versions may change the embedding API.

How people reacted on X

Reactions ranged from impressed to skeptical:

  • Malte Ubl (@cramforce) asked whether this points to a world where “the interpreter itself can be trusted” rather than wrapped in WebAssembly sandboxes. PocketJS answered that the aim is an interpreter that is “memory-safe by construction.”
  • @k3rn_net questioned the achievement: “Anybody can do this who has enough tokens to burn.”
  • @EgeMustafaCelik noted that “a whole month of cleanup after hitting test262 is the honest bit most ai written projects skip.”
  • @anoymous_elangm cautioned that Test262 measures compatibility, while timings only measure specific workloads.
  • Ericoolen (@ericoolen_eric) recommended matklad’s post on millisecond-scale benchmarks. He wrote that keeping perf tests around 300 ms, so they don’t slow agent iteration, would have saved the project early detours.

We found no Hacker News submission as of October 22, 2026.

What this says about AI rewrites

quickjs-oxide follows the same pattern as tsrs: pin an upstream release, use its test suite as the specification, and let agents iterate against it. Its history adds two things to that picture:

  • Passing tests is only half the work. The first phase produced a conformant but very slow engine, and humans with profilers had to redesign it.
  • The project’s heavy investment in measurement discipline, from frozen Test262 hashes and instruction-count gates to written rules for agents, is what made it easy for us to reproduce its claims.

Our current take: in our tests, the Test262 and speed claims held up, while the memory claim on X did not. The engine is worth testing for Rust embedders who want a QuickJS-class engine without C, but it is not yet a QuickJS replacement for apps that depend on its std/os modules or C API.

Primary sources