Built-in shapes via source generator: one-liner opt-in (Math, JSON, Reflect, Atomics)#2555
Merged
Merged
Conversation
lahma
force-pushed
the
builtin-shapes-rollout
branch
from
June 29, 2026 13:04
00b99cf to
708dda8
Compare
…eflect, Atomics) Generalize the Math built-in-shape pilot (sebastienros#2554) into a reusable runtime and a source- generator opt-in, so a namespace built-in adopts shape storage with a one-liner [JsObject(UseShape = true)] instead of a hand-written shape file. - New BuiltinShape (immutable shared layout: names, attributes, name→slot index, + a builder) and BuiltinShapeObject (abstract base holding the per-realm lazily-filled descriptor array and the GetOwnProperty / SetOwnProperty / Delete / GetOwnPropertyKeys / GetOwnProperties / deopt logic). - Source generator: [JsObject(UseShape = true)] makes the generated CreateProperties_Generated install the shared layout + per-realm descriptor array (InitializeBuiltinShape) instead of building a property dictionary, and emits the BuiltinShape + MakeBuiltinFunction hooks from the same key/descriptor/dispatcher metadata. The host just derives from BuiltinShapeObject. Supports [JsFunction]s, static-immutable [JsProperty] constants, and [JsSymbol]s. - Math (replacing the pilot's hand-written MathInstance.Shape.cs), JSON, Reflect, Atomics opt in: they stop allocating a per-realm property dictionary + lazy descriptors in Initialize. No hand-written per-built-in shape code remains. Redefining an existing property's attributes mutates the per-realm descriptor in place (verifyProperty needs no deopt); adding or deleting an own property deopts to the ordinary dictionary. Prototypes / constructors (other bases, accessors, instance refs) are a later step. Benchmarks (BuiltinShapeBenchmark, default job, vs main; EngineOnly control identical at 13.69 KB; generated code allocates byte-identically to the hand-written version): EngineInitMath (Math) 15.51 KB == 15.51 KB (neutral refactor) EngineInitBuiltins (Math+JSON+Reflect+Atomics) 22.87 → 20.20 KB (−11.7%; time −13%) The 4-built-in storage overhead (delta over EngineOnly) drops 9.18 → 6.51 KB (−29%). Conformance: Math 662/662, JSON 502/502, Reflect 306/306, Atomics 780/780; full Test262 0 new failures (the Atomics.waitAsync no-spurious-wakeup tests are pre-existing timing flakes under load — a different variant flakes each run, passes in isolation). Jint.Tests 3138/3076; Jint.Tests.PublicInterface 79/79 (no public API change). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RDS23QeSSNRkaUB2KL74U9
lahma
force-pushed
the
builtin-shapes-rollout
branch
from
June 29, 2026 17:29
708dda8 to
3f773ff
Compare
This was referenced Jul 6, 2026
This was referenced Jul 13, 2026
This was referenced Jul 20, 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.
Summary
Generalize the Math built-in-shape pilot (#2554) into a reusable runtime and a source-generator opt-in, so a namespace built-in adopts shape storage with a one-liner —
[JsObject(UseShape = true)]— instead of a hand-written shape file. Math, JSON, Reflect, and Atomics opt in here.A shape-backed built-in stores its string-keyed own properties as a shared immutable layout + a per-realm lazily-filled descriptor array, instead of building a per-realm dictionary of descriptors in
Initialize.What changed
BuiltinShape— immutable, process-shared layout (names, attributes, name→slot index) + a builder.BuiltinShapeObject— abstract base holding the per-realm lazily-filled descriptor array and theGetOwnProperty/SetOwnProperty/Delete/GetOwnPropertyKeys/GetOwnProperties/ deopt logic.[JsObject(UseShape = true)]makes the generatedCreateProperties_Generatedinstall the shared layout + per-realm descriptor array (InitializeBuiltinShape) instead of building a property dictionary, and emits theBuiltinShape+MakeBuiltinFunctionhooks from the same key/descriptor/dispatcher metadata the dictionary path uses. The host just derives fromBuiltinShapeObject. Supports[JsFunction]s, static-immutable[JsProperty]constants, and[JsSymbol]s.MathInstance.Shape.cs), JSON, Reflect, Atomics opt in — no hand-written per-built-in shape code remains.Redefining an existing property's attributes mutates the per-realm descriptor in place (so
verifyPropertyneeds no deopt); adding or deleting an own property deopts to the ordinary dictionary, after which the unchanged base behavior runs. Prototypes / constructors (other bases, accessors, instance refs) are a later step.Benchmarks (
BuiltinShapeBenchmark, default job, vs main)EngineOnlycontrol is identical (13.69 KB) on both; the generated code allocates byte-identically to the earlier hand-written version.EngineInitMath(Math)EngineInitBuiltins(Math + JSON + Reflect + Atomics)The 4-built-in storage overhead (delta over
EngineOnly) drops from 9.18 KB to 6.51 KB — −29%.Conformance & tests
Atomics.waitAsyncno-spurious-wakeup tests are pre-existing timing flakes under concurrent load — a different variant flakes each run and passes in isolation; already serialized viaNonParallelFeatures).Jint.Tests3138 (net10.0) / 3076 (net472);Jint.Tests.PublicInterface79/79 — no public API change.🤖 Generated with Claude Code
https://claude.ai/code/session_01RDS23QeSSNRkaUB2KL74U9