feat(gc): add WeakRef and FinalizationRegistry#702
Conversation
Implement weak targets, kept objects, finalization cleanup jobs, and error surfacing for queueMicrotask/finalizers. Add JavaScript coverage across interpreter and bytecode behavior plus documentation updates.
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
📝 WalkthroughWalkthroughThis pull request implements ES2021 WeakRef and FinalizationRegistry support and the GC/execution model changes required to support them: queued roots and kept objects in the collector, serialized collection, a dual microtask/finalization queue with queued-root lifecycle, new native WeakRef/FinalizationRegistry types and class-values, engine registration, structuredClone updates, and extensive tests. ChangesWeakRef and FinalizationRegistry (ES2021)
🎯 4 (Complex) | ⏱️ ~60 minutes Possibly Related PRs
Suggested labels
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Comment |
Suite TimingTest Runner (interpreted: 9,965 passed; bytecode: 9,965 passed)
MemoryGC rows aggregate the main thread plus all worker thread-local GCs. Test runner worker shutdown frees thread-local heaps in bulk; that shutdown reclamation is not counted as GC collections or collected objects.
Benchmarks (interpreted: 407; bytecode: 407)
MemoryGC rows aggregate the main thread plus all worker thread-local GCs. Benchmark runner performs explicit between-file collections, so collection and collected-object counts can be much higher than the test runner.
Measured on ubuntu-latest x64. |
Benchmark Results407 benchmarks Interpreted: 🟢 352 improved · 🔴 13 regressed · 42 unchanged · avg +12.5% arraybuffer.js — Interp: 🟢 13, 1 unch. · avg +14.2% · Bytecode: 🟢 8, 6 unch. · avg +6.8%
arrays.js — Interp: 🟢 14, 5 unch. · avg +12.7% · Bytecode: 🟢 14, 5 unch. · avg +8.3%
async-await.js — Interp: 🟢 5, 1 unch. · avg +13.7% · Bytecode: 🟢 2, 4 unch. · avg +0.0%
async-generators.js — Interp: 🟢 2 · avg +14.8% · Bytecode: 🔴 1, 1 unch. · avg -23.7%
base64.js — Interp: 🟢 3, 🔴 3, 4 unch. · avg +0.6% · Bytecode: 🟢 2, 🔴 4, 4 unch. · avg +0.4%
classes.js — Interp: 🟢 30, 1 unch. · avg +12.2% · Bytecode: 🟢 18, 13 unch. · avg +5.6%
closures.js — Interp: 🟢 11 · avg +17.3% · Bytecode: 🟢 11 · avg +13.0%
collections.js — Interp: 🟢 12 · avg +18.2% · Bytecode: 🟢 12 · avg +15.1%
csv.js — Interp: 🟢 13 · avg +13.5% · Bytecode: 🟢 13 · avg +15.7%
destructuring.js — Interp: 🟢 20, 2 unch. · avg +13.3% · Bytecode: 🟢 20, 2 unch. · avg +12.3%
fibonacci.js — Interp: 🟢 8 · avg +17.2% · Bytecode: 🟢 8 · avg +21.2%
float16array.js — Interp: 🟢 28, 4 unch. · avg +10.6% · Bytecode: 🟢 29, 3 unch. · avg +10.6%
for-of.js — Interp: 🟢 6, 1 unch. · avg +12.4% · Bytecode: 🟢 7 · avg +16.7%
generators.js — Interp: 🟢 3, 1 unch. · avg +6.3% · Bytecode: 🟢 2, 2 unch. · avg +8.8%
iterators.js — Interp: 🟢 41, 1 unch. · avg +16.1% · Bytecode: 🟢 40, 2 unch. · avg +9.0%
json.js — Interp: 🟢 19, 1 unch. · avg +15.8% · Bytecode: 🟢 20 · avg +10.8%
jsx.jsx — Interp: 🟢 18, 3 unch. · avg +10.2% · Bytecode: 🟢 20, 1 unch. · avg +10.1%
modules.js — Interp: 🟢 8, 1 unch. · avg +24.9% · Bytecode: 🟢 9 · avg +19.3%
numbers.js — Interp: 🟢 11 · avg +14.2% · Bytecode: 🟢 11 · avg +12.2%
objects.js — Interp: 🟢 5, 2 unch. · avg +8.6% · Bytecode: 🟢 7 · avg +11.3%
promises.js — Interp: 🟢 11, 1 unch. · avg +9.8% · Bytecode: 🟢 4, 8 unch. · avg +0.8%
regexp.js — Interp: 🟢 5, 🔴 3, 3 unch. · avg +4.4% · Bytecode: 🟢 5, 🔴 1, 5 unch. · avg +1.7%
strings.js — Interp: 🟢 17, 2 unch. · avg +9.3% · Bytecode: 🟢 19 · avg +10.1%
tsv.js — Interp: 🟢 9 · avg +15.3% · Bytecode: 🟢 8, 1 unch. · avg +13.3%
typed-arrays.js — Interp: 🟢 15, 🔴 2, 5 unch. · avg +0.6% · Bytecode: 🟢 11, 🔴 8, 3 unch. · avg -4.6%
uint8array-encoding.js — Interp: 🟢 15, 🔴 1, 2 unch. · avg +6.1% · Bytecode: 🟢 11, 🔴 1, 6 unch. · avg +4.3%
weak-collections.js — Interp: 🟢 10, 🔴 4, 1 unch. · avg +28.3% · Bytecode: 🟢 10, 🔴 2, 3 unch. · avg -8.3%
Deterministic profile diffDeterministic profile diff: no significant changes. Measured on ubuntu-latest x64. Benchmark ranges compare cached main-branch min/max ops/sec with the PR run; overlapping ranges are treated as unchanged noise. Percentage deltas are secondary context. |
Avoid retaining stale weak unregister-token pointers in FinalizationRegistry cells after GC. Keep the target cell alive for later cleanup and add regression coverage for unregister after token collection.
test262 Conformance
Areas closest to 100%
Per-test deltas (+79 / -0)Newly passing (79):
Steady-state failures are non-blocking; regressions vs the cached main baseline (lower total pass count, or any PASS → non-PASS transition) fail the conformance gate. Measured on ubuntu-latest x64, bytecode mode. Areas grouped by the first two test262 path components; minimum 25 attempted tests, areas already at 100% excluded. Δ vs main compares against the most recent cached |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 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 `@source/units/Goccia.MicrotaskQueue.pas`:
- Around line 150-208: Remove the transient re-rooting in the task execution
path: delete the calls to TGarbageCollector.Instance.AddTempRoot for
ATask.Handler, ATask.Value and Promise at the start of the shown block and
remove the matching TGarbageCollector.Instance.RemoveTempRoot calls in the
finally; these objects are already rooted by AddQueuedRoots/RemoveQueuedRoots
(used by DrainQueue) so the extra AddTempRoot/RemoveTempRoot pairs (which use
set-membership, not ref-counting) must be eliminated to avoid accidentally
clearing an outer root.
🪄 Autofix (Beta)
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: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: f78980d2-c29a-42a2-8f2f-a7c59404382c
📒 Files selected for processing (37)
docs/built-ins.mddocs/garbage-collector.mddocs/interpreter.mddocs/language-tables.mddocs/language.mddocs/value-system.mdsource/app/GocciaTestRunner.dprsource/units/Goccia.Builtins.GlobalPromise.passource/units/Goccia.Builtins.Globals.passource/units/Goccia.Constants.ConstructorNames.passource/units/Goccia.Engine.passource/units/Goccia.Error.Messages.passource/units/Goccia.Error.Suggestions.passource/units/Goccia.FetchManager.passource/units/Goccia.GarbageCollector.passource/units/Goccia.MicrotaskQueue.passource/units/Goccia.Threading.passource/units/Goccia.Values.ClassValue.passource/units/Goccia.Values.FinalizationRegistryValue.passource/units/Goccia.Values.NativeFunction.passource/units/Goccia.Values.PromiseValue.passource/units/Goccia.Values.WeakRefValue.pastests/built-ins/FinalizationRegistry/constructor.jstests/built-ins/FinalizationRegistry/gc.jstests/built-ins/FinalizationRegistry/prototype/register.jstests/built-ins/FinalizationRegistry/prototype/toStringTag.jstests/built-ins/FinalizationRegistry/prototype/unregister.jstests/built-ins/FinalizationRegistry/subclassing.jstests/built-ins/Object/prototype/toString.jstests/built-ins/WeakRef/constructor.jstests/built-ins/WeakRef/gc.jstests/built-ins/WeakRef/prototype/deref.jstests/built-ins/WeakRef/prototype/toStringTag.jstests/built-ins/WeakRef/subclassing.jstests/built-ins/global-properties/global-this.jstests/built-ins/structuredClone/collections.jstests/language/classes/class-length-property.js
Summary
Testing
Verification run:
./fixtures/ffi/build.sh./build.pas testrunner./build.pas loader./build/GocciaTestRunner tests./build/GocciaTestRunner tests --mode=bytecode./format.pas --checkgit diff --check