pipa-js 0.1.5

A fast, minimal ES2023 JavaScript runtime built in Rust.
Documentation

pipa (枇杷) - A fast, minimal ES2023 JavaScript runtime built in Rust.

Features

  • ES2023 compliant — implements the ECMAScript 2023 specification
  • Async/await built-in — first-class async/await support without transpilation
  • Bytecode support — compile JavaScript to .jsc bytecode files for fast loading and execution, with configurable optimization levels (-O0 through -O3)
  • Fast — outperforms QuickJS in benchmarks
  • Small — ~5.2 MB binary (with repl feature)
  • Zero-dependency built-in implementations for:
    • Regex/JSON/Base64/BigInt
    • Unicode
    • fetch (HTTP client), rusttls required
    • WebSocket
    • Server-Sent Events (SSE)

No external C libraries or system dependencies for the above — everything is implemented from scratch in Rust.

Benchmarks (2026-05-23)

V8 benchmark suite comparison (higher is better):

Benchmark qjs node boa pipa vs qjs
Richards 978 46846 133 1005 +2.8%
DeltaBlue 946 94979 140 1034 +9.3%
Crypto 1067 60072 125 1120 +5.0%
RayTrace 1507 79697 315 1052 -30.2%
EarleyBoyer 2153 95129 281 1441 -33.1%
RegExp 334 12703 41.6 1046 +213.2%
Splay 2432 48609 536 1999 -17.8%
NavierStokes 1882 56392 288 1941 +3.1%
SCORE (total) 1220 53836 184 1279 +4.8%

Ranking: #1 node (53836) · #2 pipa (1279) · #3 qjs (1220) · #4 boa (184)

test262 Compatibility (2026-05-23)

Tested against tc39/test262 (excluding intl402 and annexB).

Category Pass Rate Notes
Core Operators
Addition, Coalesce, Comma, Grouping, Logical-And/Or, Strict-Equals/Not-Equals, Void, Bitwise-Not, Relational 100% Fully compliant
Subtraction, Division, Multiplication, Modulus, Exponentiation 64–82% Mostly compliant
Control Flow
if 94%
for 81%
while 74%
switch 78%
try/catch/finally 74%
const/let 73–74%
Functions
Function declarations/expressions 66–73%
Arrow functions 80%
Generators 65–67%
Builtins
Math 49%
JSON 23%
Boolean 59%
Promise 7% Limited async support
Proxy/Reflect 0–19% Not yet implemented
Set/Map/WeakMap/WeakSet 6–23% Partial implementation

Overall sampled pass rate: ~52% (5327 tests sampled across all categories above)

Usage

cargo install pipa-js
# Run a script
pipa script.js

# Run precompiled bytecode
pipa script.jsc

# Compile JavaScript to bytecode
pipa -compile input.js output.jsc

# Disassemble bytecode (debugging)
pipa -diss script.jsc

# Specify optimization level (default: -O2)
pipa -O3 script.js

# Start REPL (requires the repl feature)
pipa

Embedding in Rust

Use pipa-js as a library to embed JavaScript in your Rust project:

[dependencies]
pipa-js = "0.1.2"

Evaluate JavaScript

use pipa::{JSRuntime, eval};

let mut rt = JSRuntime::new();
let mut ctx = rt.new_context();

let val = eval(&mut ctx, "1 + 2").unwrap();
assert_eq!(val.get_int(), 3);

Read strings & values from JavaScript

use pipa::{JSRuntime, eval};

let mut rt = JSRuntime::new();
let mut ctx = rt.new_context();

eval(&mut ctx, r#"
    function greet(name) {
        return "Hello, " + name + "!";
    }
"#).unwrap();

let val = eval(&mut ctx, r#"greet("world")"#).unwrap();
assert!(val.is_string());
let s = ctx.get_atom_str(val.get_atom());
assert_eq!(s, "Hello, world!");

Call custom Rust functions from JavaScript

use pipa::{JSRuntime, eval, JSValue};

fn js_print(ctx: &mut pipa::JSContext, args: &[JSValue]) -> JSValue {
    for arg in args {
        if arg.is_string() {
            print!("{}", ctx.get_atom_str(arg.get_atom()));
        } else if arg.is_int() {
            print!("{}", arg.get_int());
        }
    }
    println!();
    JSValue::undefined()
}

let mut rt = JSRuntime::new();
let mut ctx = rt.new_context();

ctx.register_global_builtin("print", 1, js_print);
eval(&mut ctx, r#"print("hello from Rust!")"#).unwrap();

Async/await with event loop

use pipa::{JSRuntime, eval, eval_async};

let mut rt = JSRuntime::new();
let mut ctx = rt.new_context();

eval_async(&mut ctx, r#"
    var result = null;
    (async () => {
        result = await fetch("https://httpbin.org/json");
    })();
"#).unwrap();

let val = eval(&mut ctx, "JSON.stringify(result)").unwrap();
println!("{}", ctx.get_atom_str(val.get_atom()));

Requires the fetch feature (enabled by default). eval_async is eval + run_event_loop in one call.

Bytecode compilation

use pipa::{JSRuntime, eval, compile_to_register_bytecode};

let mut rt = JSRuntime::new();
let mut ctx = rt.new_context();

// Compile JavaScript to register-based bytecode
let (code, constants) = compile_to_register_bytecode(
    &mut ctx,
    "function fib(n) { return n < 2 ? n : fib(n-1) + fib(n-2); } fib(20)",
).unwrap();

// code: Vec<u8>, constants: Vec<JSValue>
assert!(!code.is_empty());

Build

# Default build (includes REPL, fetch, and process support)
cargo build --release

# Minimal build (no REPL, no fetch, no process)
cargo build --release --no-default-features

If using pipa as a library dependency and you don't need REPL/fetch/process features, add it with default-features = false:

[dependencies]
pipa-js = { version = "0.1.1", default-features = false }

License

MIT