Skip to content

fix(ext/node): trace v8 serializer delegate#35132

Merged
littledivy merged 2 commits into
mainfrom
orch/divybot-561
Jun 11, 2026
Merged

fix(ext/node): trace v8 serializer delegate#35132
littledivy merged 2 commits into
mainfrom
orch/divybot-561

Conversation

@divybot

@divybot divybot commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

Every `node:v8` Serializer constructed leaks. The Rust SerializerDelegate held a strong v8::Global<Object> back to the JS Serializer instance, while the cppgc trace() impl on Serializer<'_> was empty. V8 saw the JS instance as alive (via the strong global root); cppgc had no edge to walk back to break the cycle. Neither side could collect, so the retained v8::ValueSerializer kept the user's serialized value graph alive forever.

import { serialize } from "node:v8";
for (let i = 0; i < 100_000; i++) serialize({ i });

This is the symmetric counterpart of the deserializer cycle fixed by #34981. The same pattern applies directly: share an Rc<SerializerDelegateState> between the cppgc wrapper and the delegate, store the JS object as a v8::TracedReference, and walk it from trace().

Adds a regression test (Serializer keeps delegate alive across GC) mirroring the deserializer one: it constructs a custom DefaultSerializer subclass, forces GC between writeHeader() and writeValue(), and asserts the _writeHostObject delegate path still runs and round-trips.

Closes #35074

Closes denoland/divybot#561

Every node:v8 Serializer leaked because the Rust SerializerDelegate held
a strong v8::Global back to the JS Serializer instance while the cppgc
trace() impl was empty. V8 kept the JS instance alive via the strong
global root and cppgc had no edge to walk back, so neither side could
collect and the retained ValueSerializer pinned the serialized value
graph forever.

This is the symmetric counterpart of the deserializer cycle fixed in
#34981. Share an Rc<SerializerDelegateState> between the cppgc wrapper
and the delegate, store the JS object as a v8::TracedReference, and walk
it from trace().

Co-Authored-By: Divy Srivastava <[email protected]>
@divybot
divybot marked this pull request as ready for review June 11, 2026 11:24
The previous run's `test specs (1/2) debug macos-aarch64` failed because the
test harness's local npm registry / esbuild server errored on the runner
(`npm registry server error`, `failed to send build request to esbuild`,
`AlertReceived(UnknownCA)`), failing a batch of unrelated npm/bundle specs.
None touch v8 serialization; this empty commit retriggers CI on fresh runners.

Co-Authored-By: Divy Srivastava <[email protected]>
@littledivy
littledivy merged commit d9703a4 into main Jun 11, 2026
136 checks passed
@littledivy
littledivy deleted the orch/divybot-561 branch June 11, 2026 16:49
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.

node:v8 Serializer leaks via cppgc cycle (symmetric counterpart of #34981)

2 participants