Shape-backed object literals inside generator/async frames when values cannot suspend#2596
Merged
Merged
Conversation
…alues cannot suspend Any object literal evaluated inside a suspendable frame previously took the dictionary build path (per-property PropertyDescriptor + PropertyDictionary per object) because a property value containing yield/await can suspend the build mid-fill. Most literals in generator bodies have no such values: prove at handler-construction time that the value expressions contain no lexical yield or await (function boundaries excluded, class nodes descended conservatively) and let those literals use the shared hidden-class shape + flat slot array build. Generator-literal census workload: 53 -> 23.1 MB/iter (-56%); the remaining allocations are the user-visible objects and iterator results. Generator and async-exit suites neutral with byte-identical allocations. Full test262 green; mid-build suspension for literals that do contain yield keeps the dictionary path and resumes exactly as before. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01S2U2y9voMQV9rvCkYeQbka
This was referenced Jul 13, 2026
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.
Shape-backed object literals inside generator/async frames when values cannot suspend
Follow-up to #2571/#2593: with the per-yield handler rebuild fixed, the dominant remaining allocation in generator bodies that build object literals is the dictionary build path itself — one
PropertyDescriptor+ aPropertyDictionary(plus itsListDictionaryhead) per constructed object.That path exists because a literal evaluated inside a suspendable frame can suspend mid-build if a property value contains
yield/await, and the shape-backed fast build (slot fill, no descriptors) has no resume support. But the exclusion was frame-based, not literal-based:yield { a: i, b: i + 1 }— a literal that can never suspend its own build — paid the dictionary path merely for being inside a generator.Change
At handler construction, prove per literal that no property value contains a lexical
yield/await:function/arrow) — suspensions there belong to the nested frame;Proven-suspend-free literals then take the existing shape-backed build (
BuildObjectFastShapeBacked) even whenExecutionContext.Suspendableis set. Literals whose values do containyield/awaitkeep the dictionary path and its suspend/resume machinery unchanged.Results
Census workload (generator yielding
{ a: i, b: i + 1, c: i + 2 }100k times, fresh engine per iter):PropertyDescriptorshareHybridDictionary/ListDictionaryshareRemaining allocations are user-visible: the literal objects themselves (45%),
IteratorResultfrom explicit.next()calls (40%), boxed numbers (13%).GeneratorBenchmarkandAsyncFunctionExitBenchmarkare neutral with byte-identical allocations (their bodies build no literals — the census script is the allocation authority for this pattern).Testing
Jint.Tests+Jint.Tests.PublicInterfacegreen (net10.0 + net472){ first: 1, second: yield "mid", third: 3 }) resumes with correct values and key order; nested deepyieldkeeps the outer literal on the resumable path🤖 Generated with Claude Code
https://claude.ai/code/session_01S2U2y9voMQV9rvCkYeQbka