Skip to content

ZIPP v0.0.27

Choose a tag to compare

@github-actions github-actions released this 18 Oct 12:09
· 147 commits to main since this release

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.br downloads 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).
  • SharedArrayBuffer and Atomics were missing from safe-sandbox
    (#61). Native
    safe-sandbox builds such as zipp-sandbox have both again, backed by
    agent-local bytes: the brand, grow, slice and Atomics behave as on
    shared memory, and a synchronous Atomics.wait on 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 a RangeError (as the specification says; Node
    24 throws a TypeError), and a detached or out-of-bounds view a
    TypeError.
  • A subclass of %TypedArray% could be constructed
    (#60). super() into
    %TypedArray% now throws the TypeError, before newTarget.prototype is
    read, as new and Reflect.construct already 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:

  1. sets zippEngineRelease in landing/package.json to 0.0.27;
  2. refreshes landing/public/playground-runtime and landing/public/wasm from
    zipp-0.0.27-javascript-python.wasm and zipp-0.0.27-javascript.wasm
    (node crates/zipp-wasm/embed-kit/package.mjs extract, then
    sync-playground.mjs --refresh-engine), SHA256SUMS-verified;
  3. syncs the sandbox playground (node landing/scripts/sync-sandbox.mjs with
    the release's files) and rewrites the site's Brotli copies
    (npm run compress:engines);
  4. updates the version, size and SHA-256 that landing/src/App.tsx and
    landing/README.md state about the bundled engine.