High level bindings to the quickjs javascript engine
High level bindings to the quickjs javascript engine
This library is a high level bindings of the QuickJS-NG JavaScript engine, a fork of the QuickJS Javascript engine. Its goal is to be an easy to use, and safe wrapper similar to the rlua library.
QuickJS is a small and embeddable JavaScript engine. It supports the ES2020 specification including modules, asynchronous generators, proxies and BigInt. It optionally supports mathematical extensions such as big decimal floating point numbers (BigDecimal), big binary floating point numbers (BigFloat) and operator overloading.
Runtime can be created using custom allocatorembed macroTrace trait to get garbage collector works properly)This crate doesn't aim to provide system and web APIs. The QuickJS library is close to V8 in that regard. If you need APIs from WinterGC or Node, then you can take a look at the follow community projects:
Node APIs in pure RustAWS LLRT Modules in pure RustThe community has also built various utilities which might be relevant to you:
This bindings is feature complete, mostly stable and ready to use.
The error handling is only thing which may change in the future.
Some experimental features like parallel may not works as expected. Use it for your own risk.
Rquickjs needs to compile a C-library which has it's own limitation on supported platforms, furthermore it needs to generate bindings for that platform.
As a result rquickjs might not compile on all platforms which rust supports.
In general you can allways try to compile rquickjs with the bindgen feature, this should work for most platforms.
Rquickjs ships bindings for a limited set of platforms, for these platforms you don't have to enable the bindgen feature.
See below for a list of supported platforms.
| platform | shipped bindings | tested | supported by quickjs |
|---|---|---|---|
| x86_64-unknown-linux-gnu | ✅ | ✅ | ✅ |
| i686-unknown-linux-gnu | ✅ | ✅ | ✅ |
| aarch64-unknown-linux-gnu | ✅ | ✅ | ✅ |
| loongarch64-unknown-linux-gnu | ✅ | ✅ | ✅ |
| x86_64-unknown-linux-musl | ✅ | ✅ | ✅ |
| aarch64-unknown-linux-musl | ✅ | ✅ | ✅ |
| loongarch64-unknown-linux-musl | ✅ | ✅ | ✅ |
| x86_64-pc-windows-gnu | ✅ | ✅ | ✅ |
| i686-pc-windows-gnu | ✅ | ✅ | ✅ |
| x86_64-pc-windows-msvc | ✅ | ✅ | ❌ experimental! |
| aarch64-pc-windows-msvc | ✅ | ❌ | ❌ experimental! |
| x86_64-apple-darwin | ✅ | ✅ | ✅ |
| aarch64-apple-darwin | ✅ | ❌ | ✅ |
| wasm32-wasip1 | ✅ | ✅ | ✅ |
| wasm32-wasip2 | ✅ | ✅ | ✅ |
| wasm32-unknown-unknown | ✅ | ✅ | ✅ |
| other | ❌ | ❌ | Unknown |
wasm32-unknown-unknown is supported out of the box: add rquickjs as a dependency and build for the target. No feature flag, no extra crate, no wasi runtime.
The target ships no libc, so rquickjs-sys supplies the whole OS tail itself:
EMSCRIPTEN / FE_DOWNWARD / FE_UPWARD defines already used for wasi, against a vendored subset of wasi-libc's headers.snprintf, strtod, libm, dlmalloc) are linked statically from a vendored libc.a.sys/wasm-shim/shim.c replaces every member that would otherwise reach for the host: the clock, localtime_r, abort/__assert_fail, and the stdio backends.The result imports exactly one host function and nothing else.
__rquickjs_host_now_us importThe embedder must supply a single import, env.__rquickjs_host_now_us, returning microseconds since the unix epoch as an f64. It backs Date.now(), new Date() and performance-adjacent internals.
const instance = new WebAssembly.Instance(module, {
env: { __rquickjs_host_now_us: () => Date.now() * 1000 },
});Time zone data does not exist here, so scripts observe UTC: getTimezoneOffset() is always 0.
Runtime::set_max_stack_size is mandatoryJS_DEFAULT_STACK_SIZE is 1MB, which is exactly the size of the wasm shadow stack. The default computation of stack_top - 1MB therefore wraps, and stack-overflow detection is silently lost: deep recursion becomes a hard trap instead of a catchable RangeError. Always set an explicit limit:
let runtime = Runtime::new()?;
runtime.set_max_stack_size(256 * 1024);(Defining __wasi__ would also stop the wrap, but quickjs's __wasi__ path pins stack_limit = 0, disabling stack checking outright. That is why the header is patched instead.)
Rather than downloading a wasi-sysroot at build time, sys/vendor/wasi-libc/ commits the subset actually needed: the prebuilt libc.a from the wasi-sdk-24 release, plus the headers required to compile quickjs against it. It's populated (and updated, after bumping the pinned wasi-sdk version in the script) by scripts/vendor-wasm-libc.sh; see sys/vendor/wasi-libc/NOTICE.md for provenance and licensing.
wasi/api.h guards itself with #ifndef __wasi__ / #error, so the vendoring script writes a copy with that one #error commented out. The rest of the vendored headers are unmodified.
wasm-test/ builds the safe API as a cdylib and wasm-test/runner.ts drives a battery through it under deno, asserting the import list along the way:
cargo build --manifest-path wasm-test/Cargo.toml --release --target wasm32-unknown-unknown
deno run --allow-read wasm-test/runner.tsThis library is licensed under the MIT License
No open issues yet, or sync has not completed.