Skip to content

Built-in shapes via source generator: one-liner opt-in (Math, JSON, Reflect, Atomics)#2555

Merged
lahma merged 1 commit into
sebastienros:mainfrom
lahma:builtin-shapes-rollout
Jun 29, 2026
Merged

Built-in shapes via source generator: one-liner opt-in (Math, JSON, Reflect, Atomics)#2555
lahma merged 1 commit into
sebastienros:mainfrom
lahma:builtin-shapes-rollout

Conversation

@lahma

@lahma lahma commented Jun 29, 2026

Copy link
Copy Markdown
Collaborator

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 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 dictionary path uses. 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 — no hand-written per-built-in shape code remains.

Redefining an existing property's attributes mutates the per-realm descriptor in place (so verifyProperty needs 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)

EngineOnly control is identical (13.69 KB) on both; the generated code allocates byte-identically to the earlier hand-written version.

Benchmark Allocation Time
EngineInitMath (Math) 15.51 KB == 15.51 KB (neutral refactor) neutral
EngineInitBuiltins (Math + JSON + Reflect + Atomics) 22.87 → 20.20 KB (−11.7%) 3.81 → 3.30 µs (−13%)

The 4-built-in storage overhead (delta over EngineOnly) drops from 9.18 KB to 6.51 KB — −29%.

Conformance & tests

  • Targeted Test262: 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 concurrent load — a different variant flakes each run and passes in isolation; already serialized via NonParallelFeatures).
  • Jint.Tests 3138 (net10.0) / 3076 (net472); Jint.Tests.PublicInterface 79/79 — no public API change.

🤖 Generated with Claude Code

https://claude.ai/code/session_01RDS23QeSSNRkaUB2KL74U9

@lahma
lahma force-pushed the builtin-shapes-rollout branch from 00b99cf to 708dda8 Compare June 29, 2026 13:04
@lahma lahma changed the title Roll out built-in shapes to JSON, Reflect, Atomics (shared base) Built-in shapes via source generator: one-liner opt-in (Math, JSON, Reflect, Atomics) Jun 29, 2026
…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
lahma force-pushed the builtin-shapes-rollout branch from 708dda8 to 3f773ff Compare June 29, 2026 17:29
@lahma
lahma merged commit 6028853 into sebastienros:main Jun 29, 2026
4 checks passed
@lahma
lahma deleted the builtin-shapes-rollout branch June 29, 2026 18:12
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.

1 participant