Replace generic __clone call by specific methods (#13611)
* update benchmark babel parser version * perf: replace generic __clone by specific methods baseline 256 length-1 named export: 4_704 ops/sec ±1.59% (0.213ms) baseline 512 length-1 named export: 2_426 ops/sec ±0.52% (0.412ms) baseline 1024 length-1 named export: 1_118 ops/sec ±1.23% (0.895ms) baseline 2048 length-1 named export: 556 ops/sec ±0.77% (1.799ms) current 256 length-1 named export: 7_073 ops/sec ±33.67% (0.141ms) current 512 length-1 named export: 4_441 ops/sec ±0.79% (0.225ms) current 1024 length-1 named export: 2_142 ops/sec ±1.09% (0.467ms) current 2048 length-1 named export: 943 ops/sec ±2.12% (1.06ms) * breaking: remove Node#__clone in Babel 8 * test: use t.cloneNode
This commit is contained in:
@@ -57,6 +57,7 @@ import {
|
||||
import { Errors, SourceTypeModuleErrors } from "./error";
|
||||
import type { ParsingError } from "./error";
|
||||
import { setInnerComments } from "./comments";
|
||||
import { cloneIdentifier } from "./node";
|
||||
|
||||
/*::
|
||||
import type { SourceType } from "../options";
|
||||
@@ -1938,7 +1939,7 @@ export default class ExpressionParser extends LValParser {
|
||||
prop.value = this.parseMaybeDefault(
|
||||
startPos,
|
||||
startLoc,
|
||||
prop.key.__clone(),
|
||||
cloneIdentifier(prop.key),
|
||||
);
|
||||
} else if (this.match(tt.eq) && refExpressionErrors) {
|
||||
if (refExpressionErrors.shorthandAssign === -1) {
|
||||
@@ -1947,10 +1948,10 @@ export default class ExpressionParser extends LValParser {
|
||||
prop.value = this.parseMaybeDefault(
|
||||
startPos,
|
||||
startLoc,
|
||||
prop.key.__clone(),
|
||||
cloneIdentifier(prop.key),
|
||||
);
|
||||
} else {
|
||||
prop.value = prop.key.__clone();
|
||||
prop.value = cloneIdentifier(prop.key);
|
||||
}
|
||||
prop.shorthand = true;
|
||||
|
||||
|
||||
@@ -26,8 +26,12 @@ class Node implements NodeBase {
|
||||
trailingComments: Array<Comment>;
|
||||
innerComments: Array<Comment>;
|
||||
extra: { [key: string]: any };
|
||||
}
|
||||
const NodePrototype = Node.prototype;
|
||||
|
||||
__clone(): this {
|
||||
if (!process.env.BABEL_8_BREAKING) {
|
||||
// $FlowIgnore
|
||||
NodePrototype.__clone = function (): Node {
|
||||
// $FlowIgnore
|
||||
const newNode: any = new Node();
|
||||
const keys = Object.keys(this);
|
||||
@@ -39,13 +43,51 @@ class Node implements NodeBase {
|
||||
key !== "trailingComments" &&
|
||||
key !== "innerComments"
|
||||
) {
|
||||
// $FlowIgnore
|
||||
newNode[key] = this[key];
|
||||
}
|
||||
}
|
||||
|
||||
return newNode;
|
||||
};
|
||||
}
|
||||
|
||||
function clonePlaceholder(node: any): any {
|
||||
return cloneIdentifier(node);
|
||||
}
|
||||
|
||||
export function cloneIdentifier(node: any): any {
|
||||
// We don't need to clone `typeAnnotations` and `optional`: because
|
||||
// cloneIdentifier is only used in object shorthand and named import/export.
|
||||
// Neither of them allow type annotations after the identifier or optional identifier
|
||||
const { type, start, end, loc, range, extra, name } = node;
|
||||
const cloned = Object.create(NodePrototype);
|
||||
cloned.type = type;
|
||||
cloned.start = start;
|
||||
cloned.end = end;
|
||||
cloned.loc = loc;
|
||||
cloned.range = range;
|
||||
cloned.extra = extra;
|
||||
cloned.name = name;
|
||||
if (type === "Placeholder") {
|
||||
cloned.expectedNode = node.expectedNode;
|
||||
}
|
||||
return cloned;
|
||||
}
|
||||
|
||||
export function cloneStringLiteral(node: any): any {
|
||||
const { type, start, end, loc, range, extra } = node;
|
||||
if (type === "Placeholder") {
|
||||
return clonePlaceholder(node);
|
||||
}
|
||||
const cloned = Object.create(NodePrototype);
|
||||
cloned.type = "StringLiteral";
|
||||
cloned.start = start;
|
||||
cloned.end = end;
|
||||
cloned.loc = loc;
|
||||
cloned.range = range;
|
||||
cloned.extra = extra;
|
||||
cloned.value = node.value;
|
||||
return cloned;
|
||||
}
|
||||
|
||||
export class NodeUtils extends UtilParser {
|
||||
|
||||
@@ -34,6 +34,7 @@ import {
|
||||
import type { SourceType } from "../options";
|
||||
import { Token } from "../tokenizer";
|
||||
import { Position } from "../util/location";
|
||||
import { cloneStringLiteral, cloneIdentifier } from "./node";
|
||||
|
||||
const loopLabel = { kind: "loop" },
|
||||
switchLabel = { kind: "switch" };
|
||||
@@ -2144,10 +2145,16 @@ export default class StatementParser extends ExpressionParser {
|
||||
}
|
||||
|
||||
const node = this.startNode();
|
||||
node.local = this.parseModuleExportName();
|
||||
node.exported = this.eatContextual("as")
|
||||
? this.parseModuleExportName()
|
||||
: node.local.__clone();
|
||||
const isString = this.match(tt.string);
|
||||
const local = this.parseModuleExportName();
|
||||
node.local = local;
|
||||
if (this.eatContextual("as")) {
|
||||
node.exported = this.parseModuleExportName();
|
||||
} else if (isString) {
|
||||
node.exported = cloneStringLiteral(local);
|
||||
} else {
|
||||
node.exported = cloneIdentifier(local);
|
||||
}
|
||||
nodes.push(this.finishNode(node, "ExportSpecifier"));
|
||||
}
|
||||
|
||||
@@ -2423,7 +2430,7 @@ export default class StatementParser extends ExpressionParser {
|
||||
);
|
||||
}
|
||||
this.checkReservedWord(imported.name, specifier.start, true, true);
|
||||
specifier.local = imported.__clone();
|
||||
specifier.local = cloneIdentifier(imported);
|
||||
}
|
||||
this.checkLVal(specifier.local, "import specifier", BIND_LEXICAL);
|
||||
node.specifiers.push(this.finishNode(specifier, "ImportSpecifier"));
|
||||
|
||||
@@ -25,6 +25,7 @@ import {
|
||||
} from "../../util/scopeflags";
|
||||
import type { ExpressionErrors } from "../../parser/util";
|
||||
import { Errors, makeErrorTemplates, ErrorCodes } from "../../parser/error";
|
||||
import { cloneIdentifier } from "../../parser/node";
|
||||
|
||||
const reservedTypes = new Set([
|
||||
"_",
|
||||
@@ -2655,7 +2656,7 @@ export default (superClass: Class<Parser>): Class<Parser> =>
|
||||
// `import {type as ,` or `import {type as }`
|
||||
specifier.imported = as_ident;
|
||||
specifier.importKind = specifierTypeKind;
|
||||
specifier.local = as_ident.__clone();
|
||||
specifier.local = cloneIdentifier(as_ident);
|
||||
} else {
|
||||
// `import {type as foo`
|
||||
specifier.imported = firstIdent;
|
||||
@@ -2673,7 +2674,7 @@ export default (superClass: Class<Parser>): Class<Parser> =>
|
||||
specifier.local = this.parseIdentifier();
|
||||
} else {
|
||||
isBinding = true;
|
||||
specifier.local = specifier.imported.__clone();
|
||||
specifier.local = cloneIdentifier(specifier.imported);
|
||||
}
|
||||
} else {
|
||||
if (firstIdentIsString) {
|
||||
@@ -2688,7 +2689,7 @@ export default (superClass: Class<Parser>): Class<Parser> =>
|
||||
isBinding = true;
|
||||
specifier.imported = firstIdent;
|
||||
specifier.importKind = null;
|
||||
specifier.local = specifier.imported.__clone();
|
||||
specifier.local = cloneIdentifier(specifier.imported);
|
||||
}
|
||||
|
||||
const nodeIsTypeImport = hasTypeImportKind(node);
|
||||
|
||||
@@ -98,6 +98,7 @@ export type Identifier = PatternBase & {
|
||||
type: "Identifier",
|
||||
name: string,
|
||||
|
||||
// @deprecated
|
||||
__clone(): Identifier,
|
||||
|
||||
// TypeScript only. Used in case of an optional parameter.
|
||||
|
||||
Reference in New Issue
Block a user