fix(runtime): structuredClone keeps overflow/dynamic properties (#4879)#5052
Merged
Conversation
The GC_TYPE_OBJECT branch of js_structured_clone_inner cloned via
js_object_clone_with_extra, which copies only the inline field_count
slots and truncates the keys array to match. Properties living outside
the inline region — the OVERFLOW_FIELDS side table of a dict-grown
object, or every prop of a {}-born object with no inline capacity —
silently vanished from the clone (50 dynamic props cloned to {}, 3000
lost all but 8).
When the keys array is longer than the inline region, rebuild the clone
key-by-key via js_object_get_field (resolves inline vs overflow per
index) + js_object_set_field_by_name, with RuntimeHandleScope rooting
across the recursive deep-clone (it can run minor GCs). Fully-inline
objects keep the existing fast path.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #4879.
Root cause
Exactly the suspected mechanism: the
GC_TYPE_OBJECTbranch ofjs_structured_clone_inner(builtins/globals.rs) clones viajs_object_clone_with_extra, which copies only the inlinefield_countslots and truncates the keys array tomin(keys_len, field_count). Anything in theOVERFLOW_FIELDSside table — or every property of a{}-born object, which has no inline capacity at all (explains the issue's "even f0 is undefined at 50 props" wrinkle:field_countis 0 there, not 8) — vanished from the clone.Fix
When
keys_array.length > field_count, rebuild the clone key-by-key:js_object_get_fieldresolves inline-vs-overflow per index, values recurse throughjs_structured_clone, andjs_object_set_field_by_namewrites into a fresh object.RuntimeHandleScoperoots src/clone across the recursive deep-clone (it can trigger minor GCs), and key bytes are owned before recursing. Fully-inline objects keep the existingclone_with_extrafast path untouched.Validation
Perry output now matches
node --experimental-strip-typesexactly on:f0/f49/key count all correct){}-born objects, nested dynamic-object-inside-array-inside-literalstructured_clone_keeps_overflow_properties(50-prop{}-born object); the existing structured-clone GC rooting test still passes.Code-only PR — version bump + changelog left for merge time.