Repository navigation
fix(string): root spread character arrays across moving GC (#9983) - #11181
Conversation
js_string_to_char_array held the raw result array and a borrowed slice of the source string's payload across js_string_from_bytes allocations, each of which can run a moving minor. Copy the payload out of the heap first, keep the result in a RuntimeHandleScope, and re-read its head before each slot store.
ced76b7 to
2922f8d
Compare
|
Navigate logical layers of code changes, visualize relationships, and explore their blast radius. No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Advanced Run ID: 📒 Files selected for processing (5)
Included review availability: Your plan provides up to 8 included reviews per hour; 1 remains after this review. 📝 WalkthroughWalkthroughThe string-to-character-array builder now copies source bytes before character allocations and keeps the result array rooted while allocations occur. A regression test forces minor collections during construction and checks the array contents and pointer-slot enumeration. ChangesString character-array GC safety
Priority: ➖ Normal Estimated code review effort: 3 (Moderate) | ~20 minutes Change: Bug fix · Severity of issue fixed: Medium Merge Risk: ⚪ Minimal · up to The string-spread GC fix is mergeable after normal checks; no actionable merge-blocking risk was identified. 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
Full details: Docstring CoverageExplanation Docstring coverage is 60.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 5 functions across 4 files. (1 skipped: 1 unsupported.)
✨ Finishing Touches 💡 2📝 Generate docstrings 💡
🧪 Generate unit tests (beta)
🛠️ Fix failing CI checks 💡
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. Comment |
Fixes #9983
Lifted from stale draft #9998 (commit
3fd585fc5) onto currentmain. This is the string-spread half of #9983. TheArray.prototype.slice/Symbol.specieshalf already landed onmainseparately (233514422, "describe species result slots before callbacks").Bug
js_string_to_char_array([..."str"]) kept two heap references across the per-characterjs_string_from_bytesallocations, and each of those allocations can run a moving minor:arr. After the array was evacuated, the remaining stores went into from-space. The live array was left with unwritten elements, and the side pointer mask no longer matched its contents. That is theUNENUMERATEDslot thatPERRY_GC_VERIFY_MARKreported.&[u8]slice of the source string's payload.js_string_from_bytesallocates its result before it copies the input, so a collection that moves the source string leaves the slice pointing at reset from-space.Fix
to_vec()) before the first allocation.RuntimeHandleScope. Each element allocation runs insideRuntimeHandle::across_mut, and the store/barrier (note_array_slot) uses the head thatacross_mutreturns after the allocation. The return value goes throughwith_mut_ptr. This follows the gc(layer 3): from-space quarantine catches 55 stale dereferences across the gap suite — the instrument is in CI but aimed at one synthetic fixture #7341 idiom, so theraw_handle_debtratchet does not change.Test
gc/tests/string_char_array_roots.rsforces a copying minor before every character allocation, using a test-only pre-allocation hook. It then checks:"é";verify_array_pointer_slots_enumerated_for,unenumerated_slots == 0,checked_pointer_slots == 13).Sabotage-tested, one half of the fix reverted at a time:
left: [0, 0],right: [195, 169], i.e. the element bytes were read from reset from-space).With the fix restored, the test passes.
Validation (macOS arm64)
RUST_TEST_THREADS=1 cargo test --release -p perry-runtime --lib: 4434 passed, 0 failed, 5 ignored-p perry -p perry-runtime-static -p perry-stdlib-staticand compiled a.tsspread probe (3000é+ 500ü😀+ suffix, 400 iterations, 20 arrays retained). Output matchesnode, both plain and underPERRY_GC_FORCE_EVACUATE=1 PERRY_GC_PROTECT_FROMSPACE=1 PERRY_GC_SCHEDULE_SEED=7 PERRY_GC_SCHEDULE_RATE=1 PERRY_GC_VERIFY_MARK=1, with 0UNENUMERATEDreports.cargo fmt --all -- --check,check_file_size.sh,gc_runtime_root_holders.py,addr_class_inventory.py,raw_handle_debt.py: pass.scripts/run_lint_gates.sh: 91 of 96 pass. The 5 failures are environmental or also fail on unmodifiedmainon the same host:cargo xwinis not installed; public-baseline freshness;binding_governance.py --check;gc_runtime_root_holders.py --self-test;gc_rekeyed_key_tables.py.Summary by CodeRabbit