From 6f0f77494ab9f21002f76a7748b386fbb2ea1c44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ralph=20K=C3=BCpper?= Date: Sun, 6 Sep 2026 18:35:27 +0200 Subject: [PATCH] test(runtime): cover for-in visited levels across GC --- ...est_issue_9869_for_in_visited_levels_gc.ts | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 test-files/test_issue_9869_for_in_visited_levels_gc.ts diff --git a/test-files/test_issue_9869_for_in_visited_levels_gc.ts b/test-files/test_issue_9869_for_in_visited_levels_gc.ts new file mode 100644 index 0000000000..0fcb5aa57a --- /dev/null +++ b/test-files/test_issue_9869_for_in_visited_levels_gc.ts @@ -0,0 +1,22 @@ +// parity-env: PERRY_GC_SCHEDULE_SEED=9869 PERRY_GC_SCHEDULE_RATE=1 PERRY_GC_SCHEDULE_ALLOC_KB=0 PERRY_GC_FORCE_EVACUATE=1 PERRY_GC_VERIFY_EVACUATION=1 PERRY_GC_PROTECT_FROMSPACE=1 + +// #9869: `for-in` defers building its shadow set until a prototype level has +// an enumerable key to filter. The retained earlier levels must remain rooted +// while key-array construction allocates and triggers a moving collection. +const ancestor = { + own: "shadowed", + ancestor: "visible", +}; +const emptyMiddle = Object.create(ancestor); +const receiver = Object.create(emptyMiddle); +receiver.own = "receiver"; + +// Level zero records `receiver`; the empty middle level allocates a key array +// but keeps the shadow set deferred; the ancestor's keys then trigger a rebuild +// that reads both retained heap objects after evacuation. +const keys: string[] = []; +for (const key in receiver) { + keys.push(key); +} + +console.log(keys.join(","));