perf(transformer): pre-size statement vecs in TS enum & namespace lowering (#23516)

## What

Two zero-capacity arena `Vec`s in the TypeScript transformer were filled
in known-length loops and grew via reallocation. Pre-size them —
mirroring the JSX-props pre-size in #23466:

- **`transform_ts_enum_members`**: emits exactly `members.len() + 1`
statements (one per member + the final `return`) →
`vec_with_capacity(members.len() + 1)` (an **exact** capacity).
- **TS namespace `enter_program`**: rebuilds the program body; pre-size
to `program.body.len()` (an estimate — most statements pass through
1:1).

## Δ — fewer reallocations on TS fixtures

`allocs_transformer.snap`, reallocation count:

| fixture | before | after |
| --- | --- | --- |
| checker.ts | 64 | **5** |
| kitchen-sink.tsx | 304 | **280** |
| App.tsx | 25 | **17** |
| binder.ts | 6 | **0** |

(allocation **count** and **bytes** are unchanged — this removes the
intermediate growth reallocations.)

## Why it is behavior-preserving

Capacity-only: `vec_with_capacity` differs from `vec` solely in initial
capacity; element type, push order, and contents are identical, so the
produced AST is byte-identical. `oxc_transformer` tests pass.

Prepared with AI assistance.
This commit is contained in:
Yunfei He authored and GitHub committed 2026-06-16 17:31:53 +01:00
1 parent 84438bebab
commit dfb52b628a
3 files changed
+11 -7

No files matched your search

@@ -310,7 +310,9 @@ impl<'a> TypeScriptEnum {
) -> ArenaVec<'a, Statement<'a>> {
let ast = ctx.ast;
let mut statements = ast.vec();
// Each member pushes exactly one statement, plus a final `return` statement,
// so the length is known up front — pre-size to avoid growth reallocations.
let mut statements = ast.vec_with_capacity(members.len() + 1);
// If enum number has no initializer, its value will be the previous member value + 1,
// if it's the first member, it will be `0`.
@@ -37,8 +37,10 @@ impl<'a> Traverse<'a, TransformState<'a>> for TypeScriptNamespace {
// Recreate the statements vec for memory efficiency.
// Inserting the `let` declaration multiple times will reallocate the whole statements vec
// every time a namespace declaration is encountered.
let mut new_stmts = ctx.ast.vec();
// every time a namespace declaration is encountered. Pre-size to the current body length
// (a lower bound — namespaces expand) to avoid the growth reallocations for the common
// pass-through statements.
let mut new_stmts = ctx.ast.vec_with_capacity(program.body.len());
for stmt in program.body.take_in(ctx.ast) {
match stmt {
@@ -1,8 +1,8 @@
File | File size || Sys allocs | Sys reallocs || Arena allocs | Arena reallocs
---------------------------------------------------------------------------------------------------------------------------
checker.ts | 2.92 MB || 20 | 0 || 4400 | 64
checker.ts | 2.92 MB || 20 | 0 || 4400 | 5
App.tsx | 415.34 kB || 25 | 2 || 719 | 25
App.tsx | 415.34 kB || 25 | 2 || 719 | 17
RadixUIAdoptionSection.jsx | 2.52 kB || 21 | 2 || 259 | 13
@@ -10,7 +10,7 @@ pdf.mjs | 567.30 kB || 15 | 0 |
antd.js | 6.69 MB || 15 | 0 || 2 | 0
binder.ts | 193.08 kB || 17 | 0 || 379 | 6
binder.ts | 193.08 kB || 17 | 0 || 379 | 0
kitchen-sink.tsx | 732.90 kB || 184 | 3 || 6690 | 304
kitchen-sink.tsx | 732.90 kB || 184 | 3 || 6690 | 280