perf(compilers): emit getter keys as string literals, never computed - #3514
Conversation
🦋 Changeset detectedLatest commit: 246eeeb The changes in this PR will be included in the next version bump. This PR includes changesets to release 11 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
Coverage Report for CI Build 35200204109Coverage remained the same at 71.46%Details
Uncovered ChangesNo uncovered changes found. Coverage RegressionsNo coverage regressions found. Coverage Stats
💛 - Coveralls |
Merging this PR will not alter performance
Comparing Footnotes
|
Both compilers emitted non-identifier getter keys in compiled props
literals as computed accessors — `get ["aria-disabled"]() {}` — Babel via
`t.objectMethod("get", id, [], body, !t.isValidIdentifier(key))` and the
Oxc compiler mirroring it deliberately. `get "aria-disabled"() {}` is the
same property, but a computed key in an object literal drops V8 off the
literal boilerplate path into per-property runtime definition for the whole
object. Measured on ButtonRoot's seven-getter `<Polymorphic>` props literal
from yak-bench's polymorphic-chain: 966 ns → 525 ns per object to build and
read once (#3511, finding 1). Every `aria-*`, `data-*`, and `class` prop
paid it.
`id` at every site is already an Identifier or a StringLiteral (from
convertJSXIdentifier or `t.identifier`), so `computed` is simply `false`.
Six Babel sites (shared/component.ts ×3, dom/ssr/universal element.ts) and
the single Oxc funnel (`object_getter_property_with_statements`). Fixtures
regenerated in both test trees plus the three cross-mode parity diffs; every
changed line is a getter key. Babel prints reserved-word string keys bare
(`get class()`), which is valid and the same property.
Co-authored-by: Claude via Cursor <noreply@cursor.com>
|
8a0c039 to
246eeeb
Compare
|
Finding 1 of #3511. Both compilers emit non-identifier getter keys in compiled props literals as computed accessors:
Babel does it via
t.objectMethod("get", id, [], body, !t.isValidIdentifier(key)), andpackages/compiler/src/shared/ast.rsmirrors it on purpose ("Babel: … non-identifier getter keys are computed").get "aria-disabled"()is the same property, but a computed key in an object literal takes V8 off the literal boilerplate path into per-property runtime definition for the whole object.Measured on
ButtonRoot's seven-getter<Polymorphic>props literal from yak-bench'spolymorphic-chain, build + read all 7 keys, min of 7 × 200k:--no-optSo the string-literal form is within 10% of what the same object costs with plain identifier keys — the computed key was the cost, not the hyphen. Every
aria-*,data-*, andclassprop paid it (classis a reserved word, soisValidIdentifierrejects it too).Change
idat every site is already anIdentifieror aStringLiteral(fromconvertJSXIdentifier, ort.identifierfor locked DOM properties), socomputedis justfalse. Six Babel sites —shared/component.ts×3,dom/element.ts,ssr/element.ts,universal/element.ts— and the single Oxc funnelobject_getter_property_with_statements. Nothing else in the emitted code changes.Fixtures regenerated in both trees (
vitest -ufor Babel,UPDATE_OXC_FIXTURES=1 UPDATE_PARITY=1for Oxc, including the three cross-mode parity.difffiles). Audited: every changed line is a getter key. Babel's generator prints a reserved-word string key bare (get class()), which is valid and the same property.Verified
@solidjs/babel-plugin242/242;@solidjs/compilervitest 5785/5785 andcargo testacross all three feature sets.@solidjs/webDOM (825), hydrate (185), server (1001) suites run end-to-end through the rebuilt native compiler; harness artifacts unchanged.Case-level SSR A/B in the yak-bench harness is pending a quiet machine — tonight's runs had a load average of 11 from a remote-desktop session and the raw numbers disagreed by 10× within a tag, so I'm not quoting them. The microbenchmark is deterministic and the emitted code is otherwise identical; I'll add the harness number when I have one worth trusting.