perf(packages/codegen): print SpreadElement arguments via printExpression (#27599)

Perf optimization. Continuation of [#27458](https://github.com/oxc-project/oxc/issues/27458).

An argument can be any expression, so reading its `type` is a megamorphic load (a builtin call, which V8 does not deduplicate). `printArguments` read it to check for a spread, and then `printExpression` read it again to dispatch. Add a `SpreadElement` arm to `printExpression`, so arguments are passed straight to it, and their `type` is read only once.

This change produces a 1%-1.5% perf improvement on most benchamarks.
This commit is contained in:
overlookmotel committed 2026-10-12 17:10:52 +00:00
1 parent 1f5b9f7b04
commit 4bb972213c
1 file changed
+13 -16
+13 -16
View File
@@ -87,9 +87,13 @@ import type * as ESTree from "../../../../npm/oxc-types/types.d.ts";
* A printer compares its own precedence against it and parenthesizes itself when it would otherwise bind too loosely.
* @param ctx - `CTX_FORBID_IN` / `CTX_FORBID_CALL` flags describing the enclosing position, so that an `in` operator
* inside a `for` head, or a call under a `new`, knows to parenthesize itself.
*
* Also prints a `SpreadElement` in an argument list or array, though it is not an expression.
* An argument or array element can be any expression, so reading its `type` is a megamorphic load.
* Dispatching spreads here too means the caller doesn't read it a second time to check for a spread.
*/
export function printExpression(
node: ESTree.Expression,
node: ESTree.Expression | ESTree.SpreadElement,
state: State,
precedence: number,
ctx: number,
@@ -228,6 +232,10 @@ export function printExpression(
case "JSXFragment":
printJSXFragment(node, state);
break;
case "SpreadElement":
writeWithMap(state, "...", CAT_OTHER, node.start, node.end, node);
printExpression(node.argument, state, precedence, ctx);
break;
/* IF TS */
case "TSAsExpression":
case "TSSatisfiesExpression":
@@ -396,13 +404,8 @@ function printArguments(
for (let i = 0; i < length; i++) {
if (i > 0) write(state, ", ", CAT_OTHER);
const arg = args[i];
if (arg.type === "SpreadElement") {
writeWithMap(state, "...", CAT_OTHER, arg.start, arg.end, arg);
printExpression(arg.argument, state, PREC_COMMA, CTX_NONE);
} else {
printExpression(arg, state, PREC_COMMA, CTX_NONE);
}
// `printExpression` prints a spread too
printExpression(args[i], state, PREC_COMMA, CTX_NONE);
}
writeWithMapEnd(state, ")", CAT_CLOSE_BRACKET, node.start, node.end, node);
@@ -624,14 +627,8 @@ function printArrayExpression(node: ESTree.ArrayExpression, state: State): void
}
const element = elements[i];
if (element != null) {
if (element.type === "SpreadElement") {
writeWithMap(state, "...", CAT_OTHER, element.start, element.end, element);
printExpression(element.argument, state, PREC_COMMA, CTX_NONE);
} else {
printExpression(element, state, PREC_COMMA, CTX_NONE);
}
}
// `printExpression` prints a spread too
if (element != null) printExpression(element, state, PREC_COMMA, CTX_NONE);
if (i === length - 1 && element == null) {
write(state, ",", CAT_OTHER);