Skip to content

Merge train 180: #10183 - #10195

Merged
proggeramlug merged 3 commits into
mainfrom
train180
Sep 13, 2026
Merged

proggeramlug merged 3 commits into
mainfrom
train180

Conversation

@proggeramlug

@proggeramlug proggeramlug commented Sep 13, 2026

Copy link
Copy Markdown
Contributor

Merge train 180: lands #10183 (bind a RegExp's program and subject in constant work across calls; #10166) at head cf63ad155d, plus the workspace version bump to 0.5.1552.

The two PR commits were cherry-picked onto 820cf76729 (train 179) without conflicts and match by git patch-id --stable. The validated tree was built on train 179's pre-landing head, which has the same tree as 820cf76729. After re-parenting onto the landed commit, this branch's tree is byte-identical to the validated one (1d278c8b65).

This lands perex 0.1.3, lock checksum 060b4682849d20ebcba05d68f9584a1bba20af4b7838c688cfb37af572f562f4. The publish-age override was approved for 0.1.2 and 0.1.3 only. It was not set in the validation environment; the version resolves from the lock.

Local validation (perrymaster, Linux x86_64; --locked, no publish-age override in the environment):

  • cargo metadata --locked, cargo fmt --all -- --check
  • cargo check -p perry-runtime --no-default-features --features full --lib (regex feature off): 0 warnings
  • cargo check -p perry-runtime --lib --tests: no warnings outside the known global_this_webassembly.rs dead code on main
  • cargo test -p perry-runtime --lib -- --test-threads=1: 3709 passed, 1 failed. The failure is native_stack::tests::stack_top_respects_custom_thread_stack_sizes, red on main.
  • cargo clippy -p perry-runtime --lib --tests: the same 12 approx_constant errors as main, and no warnings added relative to train 179
  • scripts/run_lint_gates.sh script tier: 76 of 77 pass. The failure is public benchmark evidence freshness, identical on main at 5cec2fbbc9. The compile tier was not run.
  • check_changeset_fragment.sh PerryTS/perry 10183: pass

Behavioural evidence is on #10183: the four fault injections (length check, witness never recorded, mismatch without fallback, view guard removed) each fail their tests. The measurements are there too. ASCII exec/matchAll/test loops become linear, 40–60× faster at n = 40,000. Non-ASCII loops remain quadratic, which is the documented one-seek-per-call fallback. Short-string per-call instruction counts fall 2–4 %. GitHub runners are down, so this local replay is the gate. This completes the ordered set #10174, #10176, #10181, #10183.

https://claude.ai/code/session_01Da12JXeG5XuVBma5yWp5C9

Summary by CodeRabbit

  • Performance

    • Improved regular expression searches to avoid repeated validation and quadratic behavior.
    • Reused search state across global matching operations, improving performance on non-ASCII and long inputs.
    • Large regular-expression split and replace operations are less likely to hit work limits on linear inputs.
  • Bug Fixes

    • Improved string validation handling after concatenation, appending, and other transformations to ensure accurate subsequent processing.
  • Chores

    • Updated the application version to 0.5.1552.

Ralph Küpper added 3 commits September 13, 2026 11:56
…oss calls (#10166)

A search per JavaScript call (an exec or test loop, matchAll's next(),
search) bound its subject and program from scratch every time: binding
a subject decodes the entire string and binding a program revalidates
every word. A loop over one string therefore did O(n) binding work per
call and O(n²) overall, and `.test()` paid a program validation per call.

Perex 0.1.3 adds constant-work rebinding for what the host already
validated: BoundSubject::new_counted(storage, utf16_len) and a
ProgramWitness from BoundProgram::witness() that
BoundProgram::new_witnessed checks against the program's length and
header. Perry now keeps both, as plain data, with no allocation and
nothing new traced:

- Program: ProgramCell gains `witness: Option<ProgramWitness>` in its
  pointer-free prefix, beside the words it describes. The first
  validating bind records it; later binds use new_witnessed, falling
  back to validation (recording a fresh witness) if it does not match.
  A recompile emits a new cell that starts with none, so a witness can
  never describe other words. RegExpHeader stays 56 bytes. Debug builds
  assert no witness is written while a view of the cell's words is live.
- Subject: StringHeader gains STRING_FLAG_WTF8_VALIDATED. Perry strings
  are not all valid WTF-8 (raw Buffer/FFI payloads reach regex
  operations), so nothing is assumed: the first bind decodes, and only
  if that succeeds and the decoded UTF-16 length equals the header's is
  the header marked; later binds use new_counted. A validated header is
  already shared, so its payload is never mutated in place.
  init_string_header strips the bit from every constructed string, and
  the in-place writers (js_string_append, js_string_append_chain) clear
  it, so it never reaches another string; concat's memo check ignores it.

Deliberately excluded: a cross-call lastIndex position hint for
non-ASCII exec loops. Recognising the same string across calls without
a traced reference would need a heap generation counter bumped on every
free and move path, and one missed path gives silent wrong answers. The
remaining cost is one seek from the nearer end of the subject per call
on non-ASCII subjects; it is charged but uncapped (#10176), so those
loops finish, but are not linear.

Tests:
- string::tests_validated_flag: slice, substring, trim, concat, repeat,
  padStart, toUpperCase, join, string_copy_range and
  js_string_from_bytes_known_utf16 (passed the source's whole flags
  word) never inherit the bit; both in-place append paths clear it.
- gc::tests::runtime_roots::perex_cross_call: a program cell records its
  witness on first bind and a recompiled program's new cell has none; a
  foreign equal-header witness (x(b) on x(a)) still answers with the
  cell's own words; a mismatched witness falls back and is replaced; a
  subject is marked only with an exact length, never with a corrupted
  utf16_len or malformed bytes; marks survive moving collections; a
  witness write under a live view is caught.
- perex_reuse's accounting now expects one validation per program.
Fault injection, each confirmed to fail its test: marking without the
length check; never recording a witness; a mismatch without fallback;
removing the view guard; keeping the whole flags word in
init_string_header; keeping the bit in the in-place writers.

Requires perex 0.1.3 (crates.io checksum
060b4682849d20ebcba05d68f9584a1bba20af4b7838c688cfb37af572f562f4),
resolved once with CARGO_RESOLVER_INCOMPATIBLE_PUBLISH_AGE=allow with
the maintainer's approval; ordinary --locked builds use it without the
override.

Claude-Session: https://claude.ai/code/session_01Da12JXeG5XuVBma5yWp5C9
@proggeramlug
proggeramlug merged commit fc736cb into main Sep 13, 2026
20 of 22 checks passed
@proggeramlug
proggeramlug deleted the train180 branch September 13, 2026 11:57
@coderabbitai

coderabbitai Bot commented Sep 13, 2026

Copy link
Copy Markdown

Review Change StackReview Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Advanced

Run ID: 7a87b679-30b2-463a-ace2-eb9b663c7a99

📥 Commits

Reviewing files that changed from the base of the PR and between 820cf76 and acfd56d.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (13)
  • CLAUDE.md
  • Cargo.toml
  • changelog.d/10183-regex-cross-call-rebinding.md
  • crates/perry-runtime/src/gc/tests/runtime_roots.rs
  • crates/perry-runtime/src/gc/tests/runtime_roots/perex_cross_call.rs
  • crates/perry-runtime/src/gc/tests/runtime_roots/perex_reuse.rs
  • crates/perry-runtime/src/regex/perex_api.rs
  • crates/perry-runtime/src/regex/perex_match_search.rs
  • crates/perry-runtime/src/regex/perex_owner.rs
  • crates/perry-runtime/src/string/append.rs
  • crates/perry-runtime/src/string/concat.rs
  • crates/perry-runtime/src/string/mod.rs
  • crates/perry-runtime/src/string/tests_validated_flag.rs

📝 Walkthrough

Walkthrough

The runtime now reuses validated RegExp programs and string subjects across calls. Program witnesses and string validation flags remain valid across moving GC. String mutations clear stale validation state. Tests cover rebinding, malformed input, reuse, and GC behavior.

Changes

Regex cross-call binding

Layer / File(s) Summary
Validated string metadata
crates/perry-runtime/src/string/*
Adds STRING_FLAG_WTF8_VALIDATED. String construction and mutation clear stale validation state. Tests cover derived strings and in-place appends.
Program witness ownership
crates/perry-runtime/src/regex/perex_owner.rs
ProgramCell stores validation witnesses. GcProgram reads and records witnesses. Debug tracking rejects witness writes while program words are live.
Binding and execution integration
crates/perry-runtime/src/regex/perex_api.rs, crates/perry-runtime/src/regex/perex_match_search.rs
Shared binders reuse or record program witnesses and validated string metadata. Fresh execution, reuse initialization, and match search use these binders.
Behavior validation and release updates
crates/perry-runtime/src/gc/tests/runtime_roots/*, crates/perry-runtime/src/string/tests_validated_flag.rs, Cargo.toml, CLAUDE.md, changelog.d/10183-regex-cross-call-rebinding.md
Tests cover witness behavior, validation failures, moving GC, reuse work, and live-view protection. Version metadata and the changelog are updated.

Estimated code review effort: 4 (Complex) | ~45 minutes

Change: Bug fix

Sequence Diagram(s)

sequenceDiagram
  participant RegExpSearch
  participant perex_api
  participant GcProgram
  participant RuntimeString
  RegExpSearch->>perex_api: Bind program and subject
  perex_api->>GcProgram: Reuse or record ProgramWitness
  perex_api->>RuntimeString: Reuse or set validation flag
  perex_api-->>RegExpSearch: Return bound program and subject
Loading
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch train180

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant