Skip to content

fix(gc): root Object.defineProperty's receiver, key and descriptor fields across its own allocating calls - #7978

Merged
proggeramlug merged 4 commits into
mainfrom
gc/7963-define-property-rooting
Aug 12, 2026
Merged

fix(gc): root Object.defineProperty's receiver, key and descriptor fields across its own allocating calls#7978
proggeramlug merged 4 commits into
mainfrom
gc/7963-define-property-rooting

Conversation

@proggeramlug

@proggeramlug proggeramlug commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Closes #7963.

The window

js_object_define_property resolved the receiver's ObjectHeader and coerced the key to a StringHeader once, near the top, and then carried both — plus the three NaN-boxed words obj_value / descriptor_value / key_value — as bare Rust locals to the end of the function. Between there and the last use it runs a dozen calls that can allocate and therefore evacuate: define_array_property, enforce_define_property_invariants, obj_value_has_own_key, ensure_key_in_keys_array, clone_closure_rebind_this, define_property_force_store_value, and every desc_has_field / desc_read_field — the last two allocate a field-name string per probe and, when a descriptor field is an accessor, run user JS mid-define.

A raw Rust local is neither a shadow slot nor a temp root nor reachable from any registered scanner, so an evacuating minor could neither keep those objects alive nor rewrite the local. scripts/gc_root_dominance_check.py reads emitted LLVM IR, so it is structurally blind to this class — the runtime instruments are the only detector.

The stale receiver is the worse half: obj as usize is the OWNER KEY of the per-property descriptor side tables, so a define that lands after a collection files its attributes and accessors under a dead address, where the matching read can never find them. That is a silent wrong answer, not a crash.

This is the window #6949's scope note names and defers, and the one #7949/#7962 deliberately left open.

What changed

object_ops/define_property.rs — all five values are rooted in one scope, and an across! macro is now the only way to name any of them across a call: it runs the call first and rebinds all five from their roots afterwards, so a pre-collection address is never nameable. Also rooted: the descriptor's get/set field values (live across ensure_key_in_keys_array and the first of two closure clones), the existing accessor's closure bits (written back into the GC-scanned accessor table when the redefining descriptor omits a field), and the class-prototype mirror's method value. The three per-arm RuntimeHandleScopes collapse into one — an inner scope dropped while an outer one is still taking handles truncates the outer container's newest entries, the hazard documented on gc::RootedValues.

object_ops/descriptor_helpers.rsDescView's six field values were raw JSValues read at decode time and handed back a dozen statements later; the stale word was then stored into the receiver by define_property_force_store_value. Each present field is now a RuntimeHandle, so read returns the post-collection address. validate_nonconfigurable_redefine's per-field arm likewise roots the descriptor, the current value and the current accessor bits, and re-resolves desc_ptr after the allocation that precedes each read.

object/reflect_support.rsobj_value_has_own_key's final keys-array walk held keys and key_str across js_array_get, which materializes a lazy array and therefore allocates. Both are rooted and re-read per iteration.

No new bare get_raw_*_ptr sites: RuntimeHandle::across_{mut,const} is what the scripts/raw_handle_debt.py ratchet asks for. The count falls by 5 (define_property.rs 3 → 2, reflect_support.rs 4 → 3); the recorded baseline is deliberately left unchanged so parallel debt-paying PRs do not collide.

How it is proven

crates/perry-runtime/src/gc/tests/rooted_define_property.rs, three tests, all under CopyingNurseryTestGuard + suppress_automatic_triggers:

  1. define_property_lands_on_the_receiver_a_descriptor_getter_moved — end-to-end through the real #[no_mangle] entry point, with a descriptor whose value field is an accessor whose getter forces a copying minor. Asserts, in order: copied_objects > 0; the receiver's address changed; the key string's address changed; the property reads back the getter's payload bytes; and get_property_attrs finds the entry at the live address (the assertion that catches a stale receiver, since the table is keyed by address).
  2. desc_view_field_values_are_rooted — the DescView half: decode, force a copying minor, assert the field's address changed and it still reads the original bytes.
  3. unrooted_receiver_copy_still_names_from_space — the sabotage arm. The same address in a plain Rust usize — exactly what pre-fix js_object_define_property carried — keeps naming its pre-collection value in the same cycle in which the rooted handle moves. This is what makes (1) and (2) non-vacuous.

Sabotage verification, with the fix committed first. Reverting define_property.rs + descriptor_helpers.rs to origin/main makes (1) abort the harness (the property reads back undefined, so the byte check dereferences a masked non-pointer). Restoring the files and instead making DescView::read return its decode-time copy makes (2) fail with "the descriptor's value was not relocated". Both restored: 3/3 pass.

Compiled probe — the A/B

test-files/test_gap_gc_define_property_descriptor_rooting.ts: an allocating Object.groupBy arm (to retire from-space blocks), a hand-written Object.defineProperty loop, and a loop whose descriptor bag carries three allocating accessor getters. Both arms compiled with PERRY_NO_AUTO_OPTIMIZE=1 and PERRY_RUNTIME_DIR pinned to their own .a pair, whose mtimes were confirmed to have moved after the edit.

build SCHEDULE_SEED=1 SCHEDULE_RATE=1 PROTECT_FROMSPACE=1 DEPTH=800 default
pristine origin/main (a769faf) exit 138, [gc-fromspace-protect] FAULT at block+2106, retired_by_minor=#135, obj_type=2 (a receiver ObjectHeader); stdout stops after arm 2, i.e. it dies in the descriptor-getter arm exit 0, byte-identical to node 26.5.1
this branch exit 0, [gc-schedule] done: safepoints=301 scheduled_collections=301 copying_minors=301 moved_objects=110912 loop_polls=8175 exit 0, byte-identical to node 26.5.1

Instrument liveness is reported rather than assumed: the pristine arm retired 135 from-space page-sets before it faulted; the fixed arm ran 301 copying minors moving 110,912 objects.

A second defect this turned up (filed separately)

The probe's first draft compared each arm inline (observed() === expected()). That faults under the same witness configuration on a pristine build and on this branch, in js_jsvalue_equalsjs_eqmain (symbolicated against an unstripped perry-dev runtime with PERRY_DEBUG_SYMBOLS=1): the left operand is an SSA temporary live across a call that collects. That is a codegen root-dominance defect, unrelated to Object.defineProperty; the probe now binds both sides to const first so it witnesses one defect. See the linked issue.

Worth recording as method: the census line (obj_type, size) is a hint, not an attribution — the first draft's fault was obj_type=3 at user_ptr + 4, which really is StringHeader::byte_len, and reading that as "the defineProperty key" would have been wrong. Only the symbolicated backtrace settled it.

Gates

cargo fmt --check, scripts/check_file_size.sh, scripts/gc_runtime_root_holders.py (no new root holder — RuntimeHandle borrows the already-registered handle stack), scripts/raw_handle_debt.py (5 below baseline): all clean.

Working notes: gc-handoff/DEFPROP-NOTES.md.

Summary by CodeRabbit

  • Bug Fixes

    • Improved Object.defineProperty reliability during garbage collection, including cases involving moved objects, property keys, descriptors, and accessor functions.
    • Preserved property values, attributes, and accessor behavior more consistently when redefining properties.
    • Improved reflective property lookups during memory-management activity.
  • Tests

    • Added regression and stress coverage for descriptor rooting, accessor behavior, and relocated objects.
  • Documentation

    • Added release notes and technical audit documentation for these fixes.

Ralph Küpper added 3 commits August 12, 2026 17:57
…elds

`js_object_define_property` resolved the receiver's `ObjectHeader` and coerced
the key to a `StringHeader` once, near the top, then carried both -- plus
`obj_value` / `descriptor_value` / `key_value` -- as bare Rust locals to the end
of the function, past a dozen calls that can allocate and therefore evacuate.
`desc_has_field` / `desc_read_field` additionally run USER JS when a descriptor
field is an accessor. A raw Rust local is neither a shadow slot nor a temp root
nor reachable from any registered scanner, and the static IR checker cannot see
it. `obj as usize` is also the OWNER KEY of the descriptor side tables, so a
stale receiver files attributes and accessors under a dead address.

Root all five in one scope and make `across!` the only way to name them across a
call. Root `DescView`'s six field values as runtime handles (the stale word was
being STORED into the receiver). Root `validate_nonconfigurable_redefine`'s
descriptor / current value / accessor bits. Root `obj_value_has_own_key`'s
keys-array walk across the lazy-array materialization in `js_array_get`.

Closes #7963.
Comparing each arm inline (observed() === expected()) leaves the left operand an
SSA temporary live across a call that collects, which faults in
js_jsvalue_equals <- js_eq <- main on a pristine build AND on this branch: a
separate codegen root-dominance defect, filed on its own. Bind both sides first
so this program isolates the defineProperty window, where it now exits 138 on
pristine (arm 3, the descriptor-getter arm) and 0 here with 301 copying minors
and 110,912 objects moved.
@coderabbitai

coderabbitai Bot commented Aug 12, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Object.defineProperty now roots and refreshes GC-sensitive values across moving collections. Descriptor validation and reflection key lookup use scoped handles. Runtime and compiled regression tests cover relocated receivers, keys, descriptors, and accessors.

Changes

Object.defineProperty GC rooting

Layer / File(s) Summary
Rooted descriptor views
crates/perry-runtime/src/object/object_ops/descriptor_helpers.rs
DescView stores descriptor fields in scoped runtime handles. Descriptor validation rereads rooted values after allocations.
GC-safe property definition
crates/perry-runtime/src/object/object_ops/define_property.rs, crates/perry-runtime/src/object/reflect_support.rs
Object.defineProperty shares a handle scope and uses across! to refresh receivers, keys, descriptors, accessors, and intermediate values. Own-key lookup roots and refreshes its receiver, key array, and coerced key.
Moving-GC regression coverage
crates/perry-runtime/src/gc/tests/*, test-files/test_gap_gc_define_property_descriptor_rooting.ts
Tests force copying collections and verify relocated receivers, keys, descriptor fields, accessor values, and live descriptor metadata.
Audit and result records
changelog.d/7978-define-property-rooting.md, gc-handoff/DEFPROP-NOTES.md
The changelog and audit notes record the fault, fixes, probe results, coverage, and related compiler findings.

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

Sequence Diagram(s)

sequenceDiagram
  participant Caller
  participant js_object_define_property
  participant RuntimeHandleScope
  participant GC
  participant DescriptorStorage

  Caller->>js_object_define_property: define property
  js_object_define_property->>RuntimeHandleScope: root receiver, key, descriptor, and accessors
  js_object_define_property->>GC: perform allocation-capable operation
  GC-->>RuntimeHandleScope: relocate and refresh rooted values
  js_object_define_property->>DescriptorStorage: install refreshed property data
  DescriptorStorage-->>Caller: return defined property result
Loading

Possibly related issues

Possibly related PRs

  • PerryTS/perry#6757 — Provides the descriptor-decoding and validation paths extended here with rooted DescView values.
  • PerryTS/perry#6941 — Applies the same RuntimeHandle and rebinding approach to related runtime allocation paths.
  • PerryTS/perry#6749 — Modifies the same Object.defineProperty and own-key handling areas, with a separate key-index behavior change.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly and concisely identifies the primary GC-rooting fix for Object.defineProperty.
Description check ✅ Passed The description explains the issue, implementation, related issue, tests, probe results, and verification gates in sufficient detail.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch gc/7963-define-property-rooting

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Nitpick comments (2)
changelog.d/7978-define-property-rooting.md (1)

1-76: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Keep the changeset focused on shipped behavior.

This fragment includes incident details, probe transcripts, raw-handle accounting, and separate #7964/#7803 investigations. Keep the changeset as one concise release-note entry for the Object.defineProperty rooting fix. Move the audit history and compiler investigations to gc-handoff/DEFPROP-NOTES.md.

Based on learnings: “For PerryTS/perry changelog fragments in changelog.d/, describe the final shipped behavior as one coherent release-note entry. Do not include separate development-slice narratives that may contradict one another when the release notes are assembled.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@changelog.d/7978-define-property-rooting.md` around lines 1 - 76, Condense
the changelog fragment into one concise release-note entry describing the
shipped Object.defineProperty GC-rooting fix and its user-visible behavior.
Remove incident details, test/probe transcripts, raw-handle accounting, proof
and investigation narratives from the changelog, and move the audit history and
compiler investigations to gc-handoff/DEFPROP-NOTES.md.

Source: Learnings

crates/perry-runtime/src/object/object_ops/descriptor_helpers.rs (1)

677-689: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Replace the now-unused acc binding with a presence test.

The accessor comparisons read acc_get_handle and acc_set_handle, so the acc binding has no remaining use. Line 689 silences the warning with let _ = acc;. Bind nothing instead, so a future reader cannot reintroduce the stale pre-call copy.

♻️ Proposed refactor
-    if let Some(acc) = cur_accessor {
+    if cur_accessor.is_some() {
-        let _ = acc;
         if desc_has_get {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/perry-runtime/src/object/object_ops/descriptor_helpers.rs` around
lines 677 - 689, Replace the `if let Some(acc) = cur_accessor` binding with a
presence-only check, such as testing whether `cur_accessor` is `Some`, and
remove `let _ = acc;`. Keep the existing accessor comparison logic using
`acc_get_handle` and `acc_set_handle` unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@changelog.d/7978-define-property-rooting.md`:
- Around line 74-76: Correct the raw-handle debt summary in the changelog so it
states that the debt falls to 5, or falls by 2 to 5, matching the listed count
changes; leave the recorded baseline unchanged.

In `@crates/perry-runtime/src/gc/tests/mod.rs`:
- Line 38: Update all repository documentation and scripts that invoke
perry-runtime tests, including targeted test commands, to prefix or otherwise
set RUST_TEST_THREADS=1. Ensure every cargo test -p perry-runtime invocation
consistently runs single-threaded.

In `@gc-handoff/DEFPROP-NOTES.md`:
- Around line 249-255: Remove the machine-specific absolute worktree path from
the collision note in DEFPROP-NOTES.md, replacing it with a repository-relative
description or omitting the collision details while preserving any useful
reproducible information.
- Around line 167-175: Update the ambiguous “Both arms” wording in the probe
description to “all three arms,” since it refers to the three listed test cases;
reserve “both builds” only for comparisons between pristine and fixed builds.

In `@test-files/test_gap_gc_define_property_descriptor_rooting.ts`:
- Around line 35-41: Register test_gap_gc_define_property_descriptor_rooting.ts
in test-parity/gc_repsel_corpus.txt and update its loop_polls witness
configuration in gc-moving-witnesses.yml to clear inherited GC knobs before
applying the specified schedule seed/rate and from-space protection settings,
including depth 800.

---

Nitpick comments:
In `@changelog.d/7978-define-property-rooting.md`:
- Around line 1-76: Condense the changelog fragment into one concise
release-note entry describing the shipped Object.defineProperty GC-rooting fix
and its user-visible behavior. Remove incident details, test/probe transcripts,
raw-handle accounting, proof and investigation narratives from the changelog,
and move the audit history and compiler investigations to
gc-handoff/DEFPROP-NOTES.md.

In `@crates/perry-runtime/src/object/object_ops/descriptor_helpers.rs`:
- Around line 677-689: Replace the `if let Some(acc) = cur_accessor` binding
with a presence-only check, such as testing whether `cur_accessor` is `Some`,
and remove `let _ = acc;`. Keep the existing accessor comparison logic using
`acc_get_handle` and `acc_set_handle` unchanged.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: f3ccdebd-5a30-45b2-826a-a55c0a246613

📥 Commits

Reviewing files that changed from the base of the PR and between 17da9aa and 9ebcc86.

📒 Files selected for processing (8)
  • changelog.d/7978-define-property-rooting.md
  • crates/perry-runtime/src/gc/tests/mod.rs
  • crates/perry-runtime/src/gc/tests/rooted_define_property.rs
  • crates/perry-runtime/src/object/object_ops/define_property.rs
  • crates/perry-runtime/src/object/object_ops/descriptor_helpers.rs
  • crates/perry-runtime/src/object/reflect_support.rs
  • gc-handoff/DEFPROP-NOTES.md
  • test-files/test_gap_gc_define_property_descriptor_rooting.ts

Comment on lines +74 to +76
`scripts/raw_handle_debt.py` falls by 5 (`define_property.rs` 3 → 2,
`reflect_support.rs` 4 → 3); the recorded baseline is deliberately left
unchanged so parallel debt-paying PRs do not collide.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Correct the raw-handle debt wording.

Line 74 says the debt “falls by 5”. The listed counts change from 7 to 5, so the debt falls by 2 and ends at 5. Change the text to “falls to 5” or “falls by 2, to 5”.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@changelog.d/7978-define-property-rooting.md` around lines 74 - 76, Correct
the raw-handle debt summary in the changelog so it states that the debt falls to
5, or falls by 2 to 5, matching the listed count changes; leave the recorded
baseline unchanged.

mod promote_in_place;
mod root_words;
mod rooted_container_values;
mod rooted_define_property;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

for dir in .github scripts; do
  if [[ -d "$dir" ]]; then
    rg -n -C 4 'RUST_TEST_THREADS|cargo test.*perry-runtime|perry-runtime.*test' "$dir"
  fi
done

Repository: PerryTS/perry

Length of output: 39280


Keep perry-runtime test commands single-threaded.

CI runs the crate tests with RUST_TEST_THREADS=1, but repository scripts also invoke cargo test -p perry-runtime without this setting. Apply RUST_TEST_THREADS=1 to every documented or scripted perry-runtime test command, including targeted tests.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/perry-runtime/src/gc/tests/mod.rs` at line 38, Update all repository
documentation and scripts that invoke perry-runtime tests, including targeted
test commands, to prefix or otherwise set RUST_TEST_THREADS=1. Ensure every
cargo test -p perry-runtime invocation consistently runs single-threaded.

Source: Coding guidelines

Comment on lines +167 to +175
`test-files/test_gap_gc_define_property_descriptor_rooting.ts`, three arms: an
allocating `Object.groupBy` first arm (to retire from-space blocks), a
hand-written `Object.defineProperty` loop, and a loop whose descriptor bag
carries three allocating accessor getters (so `desc_read_field` runs user JS
mid-define).

Both arms compiled with `PERRY_NO_AUTO_OPTIMIZE=1` and `PERRY_RUNTIME_DIR`
pinned to their own `.a` pair; the fixed pair's mtimes were confirmed to have
moved after the edit.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Use a consistent probe count.

Lines 167-171 list three probe arms, but Line 173 says “Both arms”. Replace it with “all three arms” if it refers to probe arms, or “both builds” if it refers to the pristine and fixed builds.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@gc-handoff/DEFPROP-NOTES.md` around lines 167 - 175, Update the ambiguous
“Both arms” wording in the probe description to “all three arms,” since it
refers to the three listed test cases; reserve “both builds” only for
comparisons between pristine and fixed builds.

Comment on lines +249 to +255
**Collision:** `/Users/amlug/projects/perry/wt-codex-7964` is another agent's
worktree on branch `fix/7964-zod-star-reexports`, already carrying uncommitted
edits to `perry-hir/src/lower/module_decl.rs`, `perry-hir/src/dynamic_import.rs`
and `perry-codegen/src/codegen/helpers.rs`, plus fixtures
`test-files/test_gap_export_star_variable_reexport.ts` and
`test-files/_helpers/issue_7964_{leaf,barrel,bridge,top}.ts` — the same four-file
shape I arrived at independently. I stopped at the verdict rather than shipping a

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Remove the machine-specific worktree path.

The committed audit note contains /Users/amlug/projects/perry/wt-codex-7964. Replace it with a repository-relative description or omit the collision details. The absolute path is not portable and adds no reproducible evidence.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@gc-handoff/DEFPROP-NOTES.md` around lines 249 - 255, Remove the
machine-specific absolute worktree path from the collision note in
DEFPROP-NOTES.md, replacing it with a repository-relative description or
omitting the collision details while preserving any useful reproducible
information.

Comment on lines +35 to +41
// PERRY_GC_SCHEDULE_SEED=1 PERRY_GC_SCHEDULE_RATE=1 \
// PERRY_GC_PROTECT_FROMSPACE=1 PERRY_GC_PROTECT_FROMSPACE_DEPTH=800
//
// Before the fix this exits 138 with `[gc-fromspace-protect] FAULT` naming a
// retired from-space address during arm 3 (`obj_type=2`, a receiver
// `ObjectHeader`); after it the program exits 0 with 301 copying minors and
// ~110k objects moved, byte-identical to node in every configuration.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

for dir in .github scripts crates; do
  if [[ -d "$dir" ]]; then
    rg -n -C 5 \
      'test_gap_gc_define_property_descriptor_rooting|test_gap_gc_|PERRY_GC_SCHEDULE_SEED|PERRY_GC_SCHEDULE_RATE|PERRY_GC_PROTECT_FROMSPACE|PERRY_GC_PROTECT_FROMSPACE_DEPTH' \
      "$dir"
  fi
done

Repository: PerryTS/perry

Length of output: 50372


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- workflow relevant sections ---'
sed -n '245,325p' .github/workflows/gc-moving-witnesses.yml
sed -n '1,75p' .github/workflows/gc-moving-witnesses.yml

echo '--- matrix runner arm and invocation sections ---'
rg -n -C 8 \
  'loop_polls|SCHEDULE_SEED|SCHEDULE_RATE|PROTECT_FROMSPACE|PROTECT_FROMSPACE_DEPTH|FILTER|filter|env -u|gc_repsel_corpus' \
  scripts/gc_repsel_matrix.sh | head -240

echo '--- exact witness registration ---'
rg -n -C 4 \
  'test_gap_gc_define_property_descriptor_rooting' \
  test-parity .github scripts test-files

Repository: PerryTS/perry

Length of output: 19660


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- file presence ---'
git ls-files --stage -- test-files/test_gap_gc_define_property_descriptor_rooting.ts
if [[ -f test-files/test_gap_gc_define_property_descriptor_rooting.ts ]]; then
  wc -l test-files/test_gap_gc_define_property_descriptor_rooting.ts
  sed -n '1,80p' test-files/test_gap_gc_define_property_descriptor_rooting.ts
else
  echo 'file is not present in the working tree'
fi

echo '--- GC witness files and registry entries ---'
find test-files -maxdepth 1 -type f -name 'test_gap_gc_*.ts' -printf '%f\n' | sort | head -200
rg -n -C 2 \
  'define_property|descriptor_root|test_gap_gc_' \
  test-parity/gc_repsel_corpus.txt test-parity/gc_repsel_triage.txt 2>/dev/null | head -200

echo '--- matrix environment construction and execution ---'
sed -n '430,650p' scripts/gc_repsel_matrix.sh

Repository: PerryTS/perry

Length of output: 35232


Register and run test_gap_gc_define_property_descriptor_rooting.ts with its witness configuration.

The file is not registered in test-parity/gc_repsel_corpus.txt, so gc-moving-witnesses.yml does not execute it. The loop_polls arm also omits PERRY_GC_SCHEDULE_SEED=1, PERRY_GC_SCHEDULE_RATE=1, PERRY_GC_PROTECT_FROMSPACE=1, and PERRY_GC_PROTECT_FROMSPACE_DEPTH=800. Add the registry entry and apply these settings while clearing inherited GC knobs.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test-files/test_gap_gc_define_property_descriptor_rooting.ts` around lines 35
- 41, Register test_gap_gc_define_property_descriptor_rooting.ts in
test-parity/gc_repsel_corpus.txt and update its loop_polls witness configuration
in gc-moving-witnesses.yml to clear inherited GC knobs before applying the
specified schedule seed/rate and from-space protection settings, including depth
800.

proggeramlug added a commit that referenced this pull request Aug 12, 2026
* docs(gc-handoff): triage #7803 on a corpus that links again

The zod dep-corpus links on main again (#7980), so #7803 is runnable for the
first time since it was filed. Record what running it says:

- #7803's own reproducer (seed 1, rate 1, quarantine off) no longer fails, but
  the class it reports does — 3 of 16 seeds fail, one with the same
  "Cannot read properties of undefined" shape.
- The candidate cause on record (#7962/#7978, Object.defineProperti(es)
  rooting) is refuted by a sabotage A/B: reverting both fixes underneath
  current main does not bring the failure back.
- Every failure is intermittent; a fixed seed does not replay. The reportable
  figure is the rate, not the seed.
- Seed 15 is a separate, self-detecting bug: the #7645 pin latch aborts on a
  pinned young Map, and the FATAL's own suggested remediation
  (scripts/gc_pin_sites.py) reports OK.
- Aside: PERRY_GC_DIAG=0 enables diagnostics (var_os(...).is_some()).

* docs(changelog): fragment for #7989

* docs(gc-handoff): mark the protected-arm sweep as in-flight, not concluded

* docs(gc-handoff): correct the seed-1 run count to 4/4 and qualify section 1

* docs(gc-handoff): final sweep counts and the #7990 cross-reference

---------

Co-authored-by: Ralph Küpper <ralph@skelpo.com>
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.

gc: from-space fault in the Object.defineProperty / descriptor-getter family (the window #6949's scope note defers)

1 participant