ReviewOS

zig-utils/zig-js

RegExp: integrate bounded-repeat capture rollback from zig-regex

#988
Closed glennmichael123 opened this 8 days ago ·
8 days ago

Reproduced dependency defect

The pinned zig-regex 1d3e5c9e3f2fb1c421de6433cab503220988617f loses accepted captures when an additional greedy bounded-repeat iteration fails. This is one family within zig-regex#30, not a resolution of that entire issue.

For /(?:((a+){2,3}|(.){3})){0,3}/ on aABBb, Node v24.4.1 returns aAB with captures aAB, undefined, B; the pinned dependency returns the same span with all captures absent. The candidate restores the previous capture state after the failed trial, matching the existing unbounded/reverse-repeat handling.

Measured on the preserved 21,575-case differential input: 20,775 compiled, 800 refused on both revisions; mismatches 308 -> 141, 167 corrected cases and zero newly mismatching case IDs. All saved oracle answers were independently rechecked against Node. The bounded regex core suite passes 369/369. This is correctness evidence, not a throughput claim.

Integration definition of done

  • Land the dependency fix with its targeted capture regression; leave zig-regex#30 open for the remaining 141 mismatches.
  • Advance all CI/performance checkout pins and the corresponding dependency-inventory entries to that exact revision.
  • Pin a production-runtime regression covering the accepted captures, empty/lazy/reverse controls, sticky lastIndex, and replacement callback arguments.
  • Verify before/after affected test262 subtrees and both evaluator modes, then rerun the dependency audit and documentation gates.

Run one heavy job at a time under the shared 3072 MB guard. No change to refusal policy or step budgets. Related verification queue: #933.

8 days ago

Integrated on main in 9da999a, pinning zig-regex 601edb4 at all 11 workflow/inventory occurrences. The commit and dependency fix both use the configured glennmichael123 identity, with no trailers.

Measured integration evidence:

  • New production-runtime case: ten checks for captured values, required empty iterations, lazy/reverse/max controls, successful clearing, sticky lastIndex, and replacement callbacks. Node v24.4.1: 10/10. Exact-parent tree and VM: 6/10 -> 10/10 each. zig build test-runtime -Dtest-filter='bounded backtracking repeats retain accepted captures': one case passed.
  • Eight test262 subtrees, identical before/after: RegExp exec 79, test 45; String match 51, matchAll 25, replace 55, replaceAll 45, search 43, split 120. 463 pass, zero fail on each revision; zero flips.
  • Dependency audit: 16 classified edges, zero migration targets, 65 classified repository tools. Link check: 339 files, all resolve. Checked docs build: 51 pages, manifest accepted.
  • Debug binaries use engine bcc32b71, the real collector 0285ef80, Zig 0.17.0-dev.1441+d5181a9c9, and corpus 4249661388e5d3f92a85186213da140a6481490f; only the regex dependency differs. Integration build/test gate peaked at 2058 MB under the shared 3072 MB guard.

Leaving this issue open for the additional regex-literal qualification. The exhaustive code-unit tests take several minutes in Debug. Completed before/after cases are checkpointed; interrupted or timed-out runs are not counted as passes. No full-corpus or performance claim. zig-regex#30 remains open for 141 differential mismatches outside the fixed capture-rollback family.

8 days ago

Qualification complete for the integration already on main in 9da999a.

Additional exact-parent checks now completed:

  • test/language/literals/regexp: 238 pass, zero fail on each revision, no skips. All 238 source files are covered exactly once by 234 disjoint filters. The four exhaustive 65,536-code-unit sweeps ran unmodified; a timed-out batch resumed from completed per-case records, never from an inferred pass. Largest completed literal batch: 1424 MB under the unchanged 3072 MB shared guard.
  • Nine additional RegExp subtrees: CharacterClassEscapes 12, Symbol.species 4, dotall 4, escape 20, lookBehind 17, match-indices 14, named-groups 36, regexp-modifiers 70, unicodeSets 114: 291 pass, zero fail before and after.
  • Combined with the earlier eight subtrees: 992 pass, zero fail on each revision across 18 non-overlapping subtrees; zero flips and identical diagnostics. This is targeted qualification, not a full test262 run.

The Node-confirmed ten-check runtime regression, both evaluator modes, focused production-runtime gate, dependency audit, documentation links, and checked 51-page docs build also passed as recorded above. All checkout pins and inventory entries point to the pushed dependency fix. No engine behavior, refusal policy, or resource limit was bypassed for these checks.

Closing this integration issue. zig-regex#30 remains open for the measured 141 remaining mismatches. A separate, narrowly scoped before/after cost control is being prepared for the capture snapshot; there is no performance claim in this qualification.

8 days ago

Published the measured dependency-cost controls in zig-js commit 0d0bb75cb89f3ee1c96963df82288c1fdc4fa741, after freezing the fixture in c47efccf7aa601acc169cf1fdd724f48cc45bbf7.

Four identical-output library controls, seven alternating exact-parent pairs per row, ReleaseFast, Apple M2 Pro. Every sample exceeded 50 ms and matched its expected full result and checksum. Median candidate/parent elapsed-time ratios were 0.998, 1.002, 0.987 and 0.999; sample RSDs were 0.68–2.61%. No samples were discarded. Source, collector and executable hashes, sample uniqueness/order and direct-parent ancestry were independently validated.

This is a desktop diagnostic, not an end-to-end engine/JSC score, a speedup claim, or proof of negligible overhead generally. Thermal, instruction and energy data were unavailable. README has a linked, scoped table; generated scorecards were not changed.

Publication gates: dependency audit 16 classified edges / 0 migration targets / 65 tools; 340 documentation files with resolving links; 51-page docs build with accepted manifest. Guard peak 179 MB. The correctness result remains 167 corrected differential inputs and zero newly mismatching IDs, with 141 mismatches still tracked in zig-utils/zig-regex#30; consumer qualification is in zig-utils/zig-js#988. Hosted CI is still queued, not claimed green.

8 days ago

Additional qualification of zig-regex 601edb49 / zig-js 9da999ae, after the original Debug checks:

  • Direct library root built with zig test -OReleaseSafe -Mroot=src/root.zig -lc: 189/189 passed, guard peak 465 MB.
  • Existing tests/regression.zig fixture, with ReleaseSafe explicitly applied to both the test root and imported regex module: 82/82 before and after, maximum build/run guard footprint 464 MB.
  • The same preserved 21,575-input differential corpus in explicit ReleaseSafe reproduced exactly the same mismatch IDs as Debug, not merely the same counts: 308 before, 141 after; 800 refusals on each side; 167 corrected IDs and zero new mismatches. All 44 bounded range runs completed.
  • Full focused zig-js runtime gate with real pinned zig-gc: 28/28 before, 29/29 after, where the extra case is the new capture regression. Highest guarded process-tree footprint was 2117 MB.

Build-mode caveat: zig build test-core -Doptimize=ReleaseSafe passes 369/369, but its build output shows the library's own root test remaining in Debug. The direct-root and regression commands above explicitly qualify ReleaseSafe rather than treating the build flag as proof.

The earlier 992-case targeted test262 qualification remains valid. A broader whole-RegExp run was terminated by SIGTERM with no final summary (640 MB peak, no guard memory/timeout breach; sender unknown); it is not counted as passing. An all-case retry now uses the runner's existing checkpointable worker mode, with unchanged tests, both binaries, and the shared 3072 MB guard. Its final result is still pending.

No additional engine fix or policy change was made. This does not resolve the remaining 141 mismatches in zig-utils/zig-regex#30 or claim that the full unit/fuzz/CI suites are green. Related consumer issue: zig-utils/zig-js#988.

8 days ago

Extended exact-parent qualification — September 24

All 1,879 pinned test/built-ins/RegExp cases have now been attempted on both preserved Debug binaries. The audited result is:

OutcomeParentCandidate
Positive pass1,6861,686
Expected-negative pass192192
Resource-incomplete11
Observed assertion/parse/negative failures among completed cases00

All 1,878 completed case IDs agree. The resource-incomplete case is character-class-escape-non-whitespace.js: an isolated process exceeded the unchanged 3072 MB guard on both builds (termination observations 3461/3421 MB). It is not counted as a pass or skip. The exact test, raw receipts, and follow-up acceptance criteria are tracked in zig-utils/zig-js#989, linked to the owned-pipeline memory work in zig-utils/zig-js#473. No claim about the leak mechanism or a memory improvement is made.

The separate 339 String-method and 238 regex-literal cases also have identical passing before/after results. Removing overlap with the earlier 992-case qualification gives 2,455 unique passing cases plus 1 resource-incomplete case, out of 2,456 attempted on each build. This is not a full-subtree or full-test262 pass claim. No source inputs, assertions, skip rules, optimization mode, or memory cap were weakened.

Provenance: engine bcc32b717ee2765d2d1143ff0f697390eda642e7, real GC 0285ef801dd221a4ae8e88b31a09ae31627657a3, zig-regex exact parent 1d3e5c9e3f2fb1c421de6433cab503220988617f versus 601edb49ba0579f1a30e4318290a7d7fb919aeed, and clean test262 4249661388e5d3f92a85186213da140a6481490f. Before binary SHA-256: 61f4c677eb2c5d0b3b46529ec952449d77600008bab31fbfa30c5019e35ac87e; after: bd1232b0ae43b6ac27433ad48edc8ffc55ef592d93ec1a655563f3026c0e9817.

Every heavy run used the shared machine lock and explicit 3072 MB ceiling. Interrupted earlier attempts are preserved, excluded from completed-batch counts, and not silently reclassified. Five earlier SIGTERM attempts had no identified sender; one later lost supervisor had no recorded exit/signal. Those are distinct from the reproduced guard exits. The final driver exited 0 after recording all attempts; that driver exit is not a full-pass test result. A fresh process check found no surviving qualification workers.

Auditors: audit-regexp-resource-qualification.js and audit-disjoint-corpus.js; result regexp-resource-qualification.json and raw per-case/batch receipts retained under /private/tmp/zig-js-sep23-verification.xkalXk on the measurement host. The first auditor validates every index, exit, terminal marker, before/after outcome, corpus revision, binary identity, and raw native log; it reports allCasesAttempted: true, complete: false rather than hiding the blocked case.

The original correctness result remains 308 -> 141 differential mismatches, 167 corrected IDs, zero new mismatch IDs over 20,775 compiled inputs (800 unchanged refusals), identical in Debug and explicit ReleaseSafe. Broader zig-utils/zig-regex#30 stays open. Integration and measured cost evidence are already pushed on main via zig-utils/zig-js#988; this follow-up adds evidence, not another engine change.

8 days ago

Resolved on main by 517a5385 and evidence commit 56419e1f.

The root cause was repeated eager compilation of identical immutable RegExp programs into the Context arena. The fix keeps fresh JS RegExp identity and independent lastIndex while reusing only immutable Thompson/one-pass programs through a bounded 64-entry interpreter-local source+flags cache; mutable backtracking programs remain excluded.

Verification under the unchanged 3072 MiB process-tree guard:

  • exact parent: unchanged upstream witness terminated after a sampled 3249 MiB tree reading; no completed outcome/peak claimed
  • candidate: the unchanged witness passed 3/3 times, each at 145,818,056 bytes peak process footprint
  • 56 alternating fresh-process samples: fresh replace 186.58 → 12.78 MiB median; construction-only 155.94 → 10.30 MiB; hoisted and 2,048-unique-pattern controls unchanged at displayed precision
  • 2,456 inventoried affected corpus cases completed, plus 70 baseline/candidate Annex B cases
  • zero regressions among 1,878 previously completed baseline RegExp outcomes; the one prior resource-incomplete case now passes
  • 4 cache unit tests, 30 focused runtime cases, 18 Node/tree/VM semantic checks, direct-root interpreter test, 341-file docs link check, and 51-page BunPress manifest gate passed

Report: regexp-retention-2026-09-25.md

Raw bundle: regexp-retention-2026-09-25.json

The report explicitly does not compare the killed baseline guard reading to the candidate per-process peak, does not claim a throughput/JSC result, and does not claim a general Context-arena bound.

Sign in to comment on this issue.