Repository navigation
ZIPP v0.0.27
ZIPP 0.0.27
A fix release. Seven issues reported by an embedder are fixed, among them a
regular expression that could stop the engine from ever finishing compiling
it (and crashed the sandbox), and the release now publishes a Brotli copy of
every WebAssembly engine, which the embed kit's loader reads directly.
Thank you to @takahashim, who found and reported all seven issues, each with a clear reproduction and most with a pointer to the cause.
- Regular expressions compile in bounded time. Nested quantified groups
such as(?:(?:a|)+|)+grew the compiled program exponentially with their
depth: depth 20 took 1.6 s, depth 31 never finished, and in the
safe-sandbox profile it crashed. They now compile in about a millisecond. - Large bundles compile. A function or script of more than about 33,000
statements no longer runs out of registers. - Any number of regex alternatives in the safe-sandbox profile (it
stopped at 64). - Cooperative cancellation is cheap. An embedder's abort flag no longer
makes every instruction take the slow metering path (about seven times
slower). - Brotli
.wasm.brdownloads beside every.wasm, and loader 1.2.0
in the embed kit, which loads them in any browser or runtime.
Downloads
| File | What it is |
|---|---|
zipp-0.0.27-windows-x86_64-portable.exe |
The Windows command line; run it from anywhere. |
zipp-0.0.27-windows-x86_64-installer.msi |
The same binary, installed for the current user into %LOCALAPPDATA%\Programs\ZIPP and added to PATH; no administrator rights. Uninstall from Settings > Apps. |
zipp-0.0.27-linux-x86_64 |
The Linux command line, x86-64 (glibc 2.35 or later). chmod +x it after downloading. |
zipp-0.0.27-linux-arm64 |
The same for 64-bit Arm. |
zipp-0.0.27-linux-armv7 |
The same for 32-bit Arm (hard float); interpreter only. |
zipp-0.0.27-javascript.wasm |
The JavaScript engine for browsers, Node, Deno and Bun. |
zipp-0.0.27-javascript-lite.wasm |
The JavaScript core without Intl and Temporal, built for size. |
zipp-0.0.27-javascript-python.wasm |
JavaScript and Python, torch built in. |
zipp-0.0.27-javascript-python-base.wasm |
JavaScript and Python without torch. |
zipp-0.0.27-torch-package.wasm |
The torch package for the -python-base engine. |
zipp-0.0.27-<engine>.wasm.br |
Each WebAssembly module compressed with Brotli (quality 11), about a quarter of its size. Serve it with Content-Encoding: br and Content-Type: application/wasm, or let zipp.mjs load it; the plain .wasm is the default. |
zipp-0.0.27-wasm-embed-kit.zip |
zipp.mjs (the loader and sandbox, version 1.2.0) and its types, the host SDK, the engine's TypeScript declarations, the browser GPU adapter, docs and three examples. |
SHA256SUMS |
Checksums of every file. |
Fixes
All seven were reported by @takahashim.
- Nested quantified groups compiled in exponential time
(#58). The regex
optimizer unrolled every small loop with no size bound, so each level of
(?:…|)+copied the levels below it: the program reached about 16 MB at
depth 16. The copies also overflowed a 16-bit loop counter, which panicked
the safe-sandbox engine from depth 17 and, in the default build, could
make two loops share state. Unrolling now has a per-regex budget (a loop
it cannot pay for stays rolled, with identical matches), a loop id that
would overflow is a clean error, and the compiled-size cap is checked
while the program is emitted. Depth 20 compiles in 1 ms (was 1,567 ms) and
depth 31 in about 1 ms (did not finish). - More than 64 regex alternatives refused in safe-sandbox
(#57). The limit is gone
in both profiles. Alternation is built as a shallow tree that keeps the
alternatives' order, which also removes a stack overflow the default
profile had at about 5,000 alternatives: 200,000 compile on a 1 MiB stack.
The safe-sandbox profile's separate 16 KiB limit on a pattern's source
remains. - A function of about 33,000 statements did not compile
(#56). Every statement
kind kept its scratch registers until its scope ended. In a large function
(from 1,024 registers), a statement outside a loop now gives them back, so
a flat body levels off; ordinary functions and loop bodies compile exactly
as before. - An installed abort flag made every instruction slow
(#62). The flag is now
read on its own stride from the countdown rather than forcing the full
metering path; an abort still lands within 4,096 instructions. With a
flag installed, the issue's loop went from 2.4 s to the same time as
without one (about 0.35 s). SharedArrayBufferandAtomicswere missing from safe-sandbox
(#61). Native
safe-sandbox builds such aszipp-sandboxhave both again, backed by
agent-local bytes: the brand,grow,sliceand Atomics behave as on
shared memory, and a synchronousAtomics.waiton an equal value returns
"timed-out"at once. The WebAssembly engines' guest surface is
unchanged and still has no shared-memory globals.- Atomics did not revalidate after converting the value
(#59). The writing
operations now convert their value(s) first and then perform
RevalidateAtomicAccess, so a value whose conversion shrinks a resizable
buffer below the index is aRangeError(as the specification says; Node
24 throws aTypeError), and a detached or out-of-bounds view a
TypeError. - A subclass of
%TypedArray%could be constructed
(#60).super()into
%TypedArray%now throws theTypeError, beforenewTarget.prototypeis
read, asnewandReflect.constructalready did.
Embedding
The embed kit's loader is version 1.2.0 (it replaced 0.0.26's kit after
that release). loadZipp("…/zipp-0.0.27-javascript.wasm.br") works however
the file is served: decoded by the server (Content-Encoding: br),
decompressed by the loader where the runtime can (Node, Firefox), or by
falling back to the plain .wasm beside it. The kit's modules.json lists
each .wasm.br with its size and SHA-256, and the release checks that
every .wasm.br decompresses to exactly its .wasm.
Performance
The engine is 0.0.26's apart from the fixes above, which leave ordinary
code paths as they were. The 0.0.26 engine was measured on 18 October 2026
(native Windows x86-64; full tables at
https://github.com/f2i-com/zipp.org/blob/main/docs/PERFORMANCE.md):
- JIT against JIT (the profile-guided build, 15 counterbalanced
repetitions): 0.634x Node's time over all 30 rows, faster than Node on 24
of them; 0.503x Bun and 0.438x Deno. - Interpreter against interpreter (
ZIPP_NOJIT=1): 0.720x Bellard's
QuickJS and 0.410x QuickJS-NG over the same 30 rows. - Python against CPython 3.13 (the release build): 1.057x by in-process
work time over 36 programs, 0.966x on the 30 micro kernels; 0.537x by
whole-process wall time. - The published Windows binary is a plain release build: on the 13 normal
rows it measures 0.604x Node, and the profile-guided build takes 0.901x
its time.
Publishing
As for earlier releases, the engines the site serves are refreshed AFTER this
release exists, in one commit that:
- sets
zippEngineReleaseinlanding/package.jsonto0.0.27; - refreshes
landing/public/playground-runtimeandlanding/public/wasmfrom
zipp-0.0.27-javascript-python.wasmandzipp-0.0.27-javascript.wasm
(node crates/zipp-wasm/embed-kit/package.mjs extract, then
sync-playground.mjs --refresh-engine), SHA256SUMS-verified; - syncs the sandbox playground (
node landing/scripts/sync-sandbox.mjswith
the release's files) and rewrites the site's Brotli copies
(npm run compress:engines); - updates the version, size and SHA-256 that
landing/src/App.tsxand
landing/README.mdstate about the bundled engine.