perf(transformer): skip expression exit pipeline for unaffected nodes (#27492)

The shared expression-exit hook enters the common, JSX, class, async-generator, and async plugins for every expression. Most identifiers, literals, and member expressions cannot be changed by any of those exit hooks.

Inline an expression-kind guard and outline the unchanged ordered pipeline. Only arrow functions, await/yield expressions, calls, classes, functions, and JSX elements/fragments enter it. Callback ordering stays the same. In transformed benchmark ASTs, 87–93% of expressions bypass the pipeline; generated x86 code confirms a direct return for those nodes.

Step 3 of 3 replacing #27446. This is the largest measured optimization in the stack. The [historical direct comparison with its parent source state, 24f05550ad → 3d14714939](https://app.codspeed.io/oxc-project/oxc/runs/compare/6ac775c75d230c8573718652..6ac77b325d230c857371872b?q=transformer) showed rounded additional gains:

| Transformer benchmark | Improvement over parent |
| --- | ---: |
| react.development.js | 10% |
| binder.ts | 10% |
| App.tsx | 8% |
| kitchen-sink.tsx | 7% |
| RadixUIAdoptionSection.jsx | 1% |

Isolated declarations was unchanged. cal.com.tsx was skipped, so reused values do not establish a gain there. Runtime environments differed; these are CodSpeed simulation measurements, not precise native wall-clock speedups.

The combined stack previously reported 10.32% aggregate improvement against actual main baseline f14ead5. The [comparison with pre-regression 676cc60](https://app.codspeed.io/oxc-project/oxc/runs/compare/6ac740d268443cfb6c1e1069..6ac77b325d230c857371872b?q=transformer) showed rounded gains of 6% React/binder, 5% App, and 4% kitchen-sink, with no material new regressions. These are historical results from the original PR; fresh reports must identify their actual comparison base.

The inlining-only PR #27488 and class-state layout PR #27490 were closed and removed after their isolated CodSpeed reports showed no material overall benefit. The two ES2022 inlining attributes and ClassTransform enum are removed; both guards and the exit-pipeline optimization remain. The measurements above predate those removals. Further local tests were skipped at the user's request; fresh CI and CodSpeed runs will validate the restacked heads.

AI assistance was used for this change.

Parent: #27491. [Graphite stack](https://app.graphite.com/github/pr/oxc-project/oxc/27489).
This commit is contained in:
camc314 committed 2026-10-10 03:37:24 +00:00
1 parent cd9a62f6fa
commit 14a92bab32
1 file changed
+26 -5
+26 -5
View File
@@ -382,12 +382,22 @@ impl<'a> Traverse<'a, TransformState<'a>> for TransformerImpl<'a> {
self.x4_regexp.enter_expression(expr, ctx);
}
#[inline]
fn exit_expression(&mut self, expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) {
self.common.exit_expression(expr, ctx);
self.x1_jsx.exit_expression(expr, ctx);
self.x2_es2022.exit_expression(expr, ctx);
self.x2_es2018.exit_expression(expr, ctx);
self.x2_es2017.exit_expression(expr, ctx);
// The exit plugins only act on these expression kinds. Keep the no-op path in the walker.
if matches!(
expr,
Expression::ArrowFunctionExpression(_)
| Expression::AwaitExpression(_)
| Expression::CallExpression(_)
| Expression::ClassExpression(_)
| Expression::FunctionExpression(_)
| Expression::JSXElement(_)
| Expression::JSXFragment(_)
| Expression::YieldExpression(_)
) {
self.exit_expression_impl(expr, ctx);
}
}
fn enter_simple_assignment_target(
@@ -779,3 +789,14 @@ impl<'a> Traverse<'a, TransformState<'a>> for TransformerImpl<'a> {
}
}
}
impl<'a> TransformerImpl<'a> {
#[inline(never)]
fn exit_expression_impl(&mut self, expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) {
self.common.exit_expression(expr, ctx);
self.x1_jsx.exit_expression(expr, ctx);
self.x2_es2022.exit_expression(expr, ctx);
self.x2_es2018.exit_expression(expr, ctx);
self.x2_es2017.exit_expression(expr, ctx);
}
}