mirror of
https://github.com/aphrody-labs/oxc.git
synced 2026-10-17 12:56:09 +00:00
perf(packages/codegen): remove branches from loops printing arrays (#27614)
Perf optimization. Optimize printing arrays - `ArrayExpression` and `ArrayPattern`. - Move branches out of the loops, so each loop iteration is as quick as possible. - Convert `write` calls to `writeNoLast` wherever possible.
This commit is contained in:
1 parent
760b60ba14
commit
3c289f3961
3 files changed
+108
-75
No files matched your search
@@ -3,7 +3,7 @@
|
||||
import { typeAssertIs } from "../asserts.ts";
|
||||
import { printPropertyKey } from "./binding_pattern.ts";
|
||||
import { CAT_CLOSE_BRACKET, CAT_OTHER } from "./categories.ts";
|
||||
import { write, writeWithMap, writeWithMapNamed } from "./write.ts";
|
||||
import { write, writeNoLast, writeWithMap, writeWithMapNamed } from "./write.ts";
|
||||
import { printExpression, printMemberExpression } from "./expression.ts";
|
||||
import { printSpaceBeforeIdentifier } from "./space.ts";
|
||||
import { CTX_NONE } from "./operators.ts";
|
||||
@@ -141,36 +141,43 @@ function printAssignmentTargetMaybeDefault(
|
||||
* @param state - Printer state
|
||||
*/
|
||||
function printArrayAssignmentTarget(node: ESTree.ArrayAssignmentTarget, state: State): void {
|
||||
const { elements } = node;
|
||||
let { length } = elements;
|
||||
|
||||
let rest: ESTree.BindingRestElement | ESTree.AssignmentTargetRest | null = null;
|
||||
if (length > 0) {
|
||||
const lastElement = elements[length - 1];
|
||||
if (lastElement != null && lastElement.type === "RestElement") {
|
||||
rest = lastElement;
|
||||
length--;
|
||||
}
|
||||
}
|
||||
|
||||
writeWithMap(state, "[", CAT_OTHER, node.start, node.end, node);
|
||||
|
||||
for (let i = 0; i < length; i++) {
|
||||
if (i !== 0) write(state, ", ", CAT_OTHER);
|
||||
const element = elements[i];
|
||||
if (element != null) {
|
||||
typeAssertIs<ESTree.AssignmentTargetMaybeDefault>(element);
|
||||
printAssignmentTargetMaybeDefault(element, state);
|
||||
}
|
||||
if (i === length - 1 && (element == null || rest !== null)) {
|
||||
write(state, ",", CAT_OTHER);
|
||||
}
|
||||
}
|
||||
const { elements } = node,
|
||||
{ length } = elements;
|
||||
|
||||
if (rest !== null) {
|
||||
if (length > 0) write(state, " ", CAT_OTHER);
|
||||
writeWithMap(state, "...", CAT_OTHER, rest.start, rest.end, rest);
|
||||
printAssignmentTarget(rest.argument, state);
|
||||
if (length !== 0) {
|
||||
const lastElement = elements[length - 1];
|
||||
if (lastElement !== null && lastElement.type === "RestElement") {
|
||||
// Print elements before the `RestElement`
|
||||
const restIndex = length - 1;
|
||||
for (let i = 0; i < restIndex; i++) {
|
||||
// `RestElement` can only be last, and this loop stops before it
|
||||
const element = elements[i] as ESTree.AssignmentTargetMaybeDefault | null;
|
||||
if (element !== null) printAssignmentTargetMaybeDefault(element, state);
|
||||
write(state, ", ", CAT_OTHER);
|
||||
}
|
||||
|
||||
// Print `RestElement`
|
||||
writeWithMap(state, "...", CAT_OTHER, lastElement.start, lastElement.end, lastElement);
|
||||
printAssignmentTarget(lastElement.argument, state);
|
||||
} else {
|
||||
// `RestElement` can only be last, and the last element is not one
|
||||
typeAssertIs<(ESTree.AssignmentTargetMaybeDefault | null)[]>(elements);
|
||||
|
||||
const firstElement = elements[0];
|
||||
if (firstElement !== null) printAssignmentTargetMaybeDefault(firstElement, state);
|
||||
|
||||
for (let i = 1; i < length; i++) {
|
||||
write(state, ", ", CAT_OTHER);
|
||||
|
||||
const element = elements[i];
|
||||
if (element !== null) printAssignmentTargetMaybeDefault(element, state);
|
||||
}
|
||||
|
||||
// A trailing hole needs a comma after it
|
||||
if (lastElement === null) writeNoLast(state, ",");
|
||||
}
|
||||
}
|
||||
|
||||
write(state, "]", CAT_CLOSE_BRACKET);
|
||||
|
||||
@@ -2,7 +2,13 @@
|
||||
|
||||
import { typeAssertIs } from "../asserts.ts";
|
||||
import { CAT_CLOSE_BRACKET, CAT_OTHER, CAT_QUESTION } from "./categories.ts";
|
||||
import { write, writeWithMap, writeWithMapNamed, writeWithMapNamedPrivate } from "./write.ts";
|
||||
import {
|
||||
write,
|
||||
writeNoLast,
|
||||
writeWithMap,
|
||||
writeWithMapNamed,
|
||||
writeWithMapNamedPrivate,
|
||||
} from "./write.ts";
|
||||
import { printExpression } from "./expression.ts";
|
||||
import { printSpaceBeforeIdentifier } from "./space.ts";
|
||||
import { printLiteral } from "./literal.ts";
|
||||
@@ -170,38 +176,43 @@ export function printPropertyKey(key: ESTree.PropertyKey, state: State): void {
|
||||
* @param state - Printer state
|
||||
*/
|
||||
function printArrayBindingPattern(node: ESTree.ArrayPattern, state: State): void {
|
||||
// Oxc stores the rest element separately from the other elements.
|
||||
// A comma and a space separate it from any elements before it.
|
||||
const { elements } = node;
|
||||
let { length } = elements;
|
||||
|
||||
let rest: ESTree.BindingRestElement | null = null;
|
||||
if (length > 0) {
|
||||
const lastElement = elements[length - 1];
|
||||
if (lastElement != null && lastElement.type === "RestElement") {
|
||||
rest = lastElement;
|
||||
length--;
|
||||
}
|
||||
}
|
||||
|
||||
writeWithMap(state, "[", CAT_OTHER, node.start, node.end, node);
|
||||
|
||||
for (let i = 0; i < length; i++) {
|
||||
if (i !== 0) write(state, ", ", CAT_OTHER);
|
||||
const { elements } = node,
|
||||
{ length } = elements;
|
||||
|
||||
const element = elements[i];
|
||||
if (element != null) printBindingPattern(element, state);
|
||||
if (length !== 0) {
|
||||
const lastElement = elements[length - 1];
|
||||
if (lastElement !== null && lastElement.type === "RestElement") {
|
||||
// Print elements before the `RestElement`
|
||||
const restIndex = length - 1;
|
||||
for (let i = 0; i < restIndex; i++) {
|
||||
const element = elements[i];
|
||||
if (element !== null) printBindingPattern(element, state);
|
||||
write(state, ", ", CAT_OTHER);
|
||||
}
|
||||
|
||||
if (i === length - 1 && (element == null || rest !== null)) write(state, ",", CAT_OTHER);
|
||||
}
|
||||
// Print `RestElement`
|
||||
writeWithMap(state, "...", CAT_OTHER, lastElement.start, lastElement.end, lastElement);
|
||||
printBindingPattern(lastElement.argument, state);
|
||||
if (TS) {
|
||||
const { typeAnnotation } = lastElement;
|
||||
if (typeAnnotation != null) printTypeAnnotation(typeAnnotation, state);
|
||||
}
|
||||
} else {
|
||||
// No `RestElement` - print all elements
|
||||
const firstElement = elements[0];
|
||||
if (firstElement !== null) printBindingPattern(firstElement, state);
|
||||
|
||||
if (rest !== null) {
|
||||
if (length > 0) write(state, " ", CAT_OTHER);
|
||||
writeWithMap(state, "...", CAT_OTHER, rest.start, rest.end, rest);
|
||||
printBindingPattern(rest.argument, state);
|
||||
if (TS) {
|
||||
const { typeAnnotation } = rest;
|
||||
if (typeAnnotation != null) printTypeAnnotation(typeAnnotation, state);
|
||||
for (let i = 1; i < length; i++) {
|
||||
write(state, ", ", CAT_OTHER);
|
||||
|
||||
const element = elements[i];
|
||||
if (element !== null) printBindingPattern(element, state);
|
||||
}
|
||||
|
||||
// A trailing hole needs a comma after it
|
||||
if (lastElement === null) writeNoLast(state, ",");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -632,35 +632,50 @@ function printObjectProperty(node: ESTree.ObjectPropertyKind, state: State): voi
|
||||
* @param state - Printer state
|
||||
*/
|
||||
function printArrayExpression(node: ESTree.ArrayExpression, state: State): void {
|
||||
const { elements } = node;
|
||||
const { length } = elements;
|
||||
const isMultiLine = length > 2;
|
||||
|
||||
writeWithMap(state, "[", CAT_OTHER, node.start, node.end, node);
|
||||
|
||||
if (isMultiLine) state.indentLevel++;
|
||||
const { elements } = node,
|
||||
{ length } = elements;
|
||||
|
||||
for (let i = 0; i < length; i++) {
|
||||
if (isMultiLine) {
|
||||
write(state, i !== 0 ? ",\n" : "\n", CAT_OTHER);
|
||||
printIndent(state);
|
||||
} else if (i !== 0) {
|
||||
write(state, ", ", CAT_OTHER);
|
||||
}
|
||||
if (length > 2) {
|
||||
// 3 or more elements, each on its own line
|
||||
state.indentLevel++;
|
||||
writeNoLast(state, "\n");
|
||||
printIndent(state);
|
||||
|
||||
const element = elements[i];
|
||||
// `printExpression` prints a spread too
|
||||
if (element != null) printExpression(element, state, PREC_COMMA, CTX_NONE);
|
||||
const firstElement = elements[0];
|
||||
if (firstElement !== null) printExpression(firstElement, state, PREC_COMMA, CTX_NONE);
|
||||
|
||||
if (i === length - 1 && element == null) {
|
||||
write(state, ",", CAT_OTHER);
|
||||
for (let i = 1; i < length; i++) {
|
||||
// Indent level is at least 1, so `printIndent` always writes, which refreshes `last`
|
||||
writeNoLast(state, ",\n");
|
||||
printIndent(state);
|
||||
|
||||
const element = elements[i];
|
||||
if (element !== null) printExpression(element, state, PREC_COMMA, CTX_NONE);
|
||||
}
|
||||
}
|
||||
|
||||
if (isMultiLine) {
|
||||
write(state, "\n", CAT_OTHER);
|
||||
// A trailing hole needs a comma after it.
|
||||
// The last element is read again here, rather than kept from the loop,
|
||||
// because keeping it would spill it to the stack on every turn of the loop.
|
||||
writeNoLast(state, elements[length - 1] === null ? ",\n" : "\n");
|
||||
state.indentLevel--;
|
||||
printIndent(state);
|
||||
} else if (length !== 0) {
|
||||
// 1 or 2 elements, on one line
|
||||
const firstElement = elements[0];
|
||||
if (firstElement !== null) printExpression(firstElement, state, PREC_COMMA, CTX_NONE);
|
||||
|
||||
if (length === 2) {
|
||||
write(state, ", ", CAT_OTHER);
|
||||
|
||||
const secondElement = elements[1];
|
||||
if (secondElement !== null) printExpression(secondElement, state, PREC_COMMA, CTX_NONE);
|
||||
}
|
||||
|
||||
// A trailing hole needs a comma after it
|
||||
if (elements[length - 1] === null) writeNoLast(state, ",");
|
||||
}
|
||||
|
||||
writeWithMapEnd(state, "]", CAT_CLOSE_BRACKET, node.start, node.end, node);
|
||||
|
||||
Reference in new issue
Block a user