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:
Huáng Jùnliàng 2021-07-30 16:19:35 -04:00 committed by GitHub
parent 2340b87094
commit d3a7cd5e8d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 72 additions and 20 deletions

View File

@ -5,7 +5,7 @@
"devDependencies": { "devDependencies": {
"@babel-baseline/generator": "npm:@babel/generator@7.14.5", "@babel-baseline/generator": "npm:@babel/generator@7.14.5",
"@babel-baseline/helper-validator-identifier": "npm:@babel/helper-validator-identifier@7.10.4", "@babel-baseline/helper-validator-identifier": "npm:@babel/helper-validator-identifier@7.10.4",
"@babel-baseline/parser": "npm:@babel/parser@7.14.5", "@babel-baseline/parser": "npm:@babel/parser@7.14.8",
"@babel/generator": "workspace:*", "@babel/generator": "workspace:*",
"@babel/helper-validator-identifier": "workspace:*", "@babel/helper-validator-identifier": "workspace:*",
"@babel/parser": "workspace:*", "@babel/parser": "workspace:*",

View File

@ -57,6 +57,7 @@ import {
import { Errors, SourceTypeModuleErrors } from "./error"; import { Errors, SourceTypeModuleErrors } from "./error";
import type { ParsingError } from "./error"; import type { ParsingError } from "./error";
import { setInnerComments } from "./comments"; import { setInnerComments } from "./comments";
import { cloneIdentifier } from "./node";
/*:: /*::
import type { SourceType } from "../options"; import type { SourceType } from "../options";
@ -1938,7 +1939,7 @@ export default class ExpressionParser extends LValParser {
prop.value = this.parseMaybeDefault( prop.value = this.parseMaybeDefault(
startPos, startPos,
startLoc, startLoc,
prop.key.__clone(), cloneIdentifier(prop.key),
); );
} else if (this.match(tt.eq) && refExpressionErrors) { } else if (this.match(tt.eq) && refExpressionErrors) {
if (refExpressionErrors.shorthandAssign === -1) { if (refExpressionErrors.shorthandAssign === -1) {
@ -1947,10 +1948,10 @@ export default class ExpressionParser extends LValParser {
prop.value = this.parseMaybeDefault( prop.value = this.parseMaybeDefault(
startPos, startPos,
startLoc, startLoc,
prop.key.__clone(), cloneIdentifier(prop.key),
); );
} else { } else {
prop.value = prop.key.__clone(); prop.value = cloneIdentifier(prop.key);
} }
prop.shorthand = true; prop.shorthand = true;

View File

@ -26,8 +26,12 @@ class Node implements NodeBase {
trailingComments: Array<Comment>; trailingComments: Array<Comment>;
innerComments: Array<Comment>; innerComments: Array<Comment>;
extra: { [key: string]: any }; extra: { [key: string]: any };
}
const NodePrototype = Node.prototype;
__clone(): this { if (!process.env.BABEL_8_BREAKING) {
// $FlowIgnore
NodePrototype.__clone = function (): Node {
// $FlowIgnore // $FlowIgnore
const newNode: any = new Node(); const newNode: any = new Node();
const keys = Object.keys(this); const keys = Object.keys(this);
@ -39,13 +43,51 @@ class Node implements NodeBase {
key !== "trailingComments" && key !== "trailingComments" &&
key !== "innerComments" key !== "innerComments"
) { ) {
// $FlowIgnore
newNode[key] = this[key]; newNode[key] = this[key];
} }
} }
return newNode; 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 { export class NodeUtils extends UtilParser {

View File

@ -34,6 +34,7 @@ import {
import type { SourceType } from "../options"; import type { SourceType } from "../options";
import { Token } from "../tokenizer"; import { Token } from "../tokenizer";
import { Position } from "../util/location"; import { Position } from "../util/location";
import { cloneStringLiteral, cloneIdentifier } from "./node";
const loopLabel = { kind: "loop" }, const loopLabel = { kind: "loop" },
switchLabel = { kind: "switch" }; switchLabel = { kind: "switch" };
@ -2144,10 +2145,16 @@ export default class StatementParser extends ExpressionParser {
} }
const node = this.startNode(); const node = this.startNode();
node.local = this.parseModuleExportName(); const isString = this.match(tt.string);
node.exported = this.eatContextual("as") const local = this.parseModuleExportName();
? this.parseModuleExportName() node.local = local;
: node.local.__clone(); 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")); nodes.push(this.finishNode(node, "ExportSpecifier"));
} }
@ -2423,7 +2430,7 @@ export default class StatementParser extends ExpressionParser {
); );
} }
this.checkReservedWord(imported.name, specifier.start, true, true); this.checkReservedWord(imported.name, specifier.start, true, true);
specifier.local = imported.__clone(); specifier.local = cloneIdentifier(imported);
} }
this.checkLVal(specifier.local, "import specifier", BIND_LEXICAL); this.checkLVal(specifier.local, "import specifier", BIND_LEXICAL);
node.specifiers.push(this.finishNode(specifier, "ImportSpecifier")); node.specifiers.push(this.finishNode(specifier, "ImportSpecifier"));

View File

@ -25,6 +25,7 @@ import {
} from "../../util/scopeflags"; } from "../../util/scopeflags";
import type { ExpressionErrors } from "../../parser/util"; import type { ExpressionErrors } from "../../parser/util";
import { Errors, makeErrorTemplates, ErrorCodes } from "../../parser/error"; import { Errors, makeErrorTemplates, ErrorCodes } from "../../parser/error";
import { cloneIdentifier } from "../../parser/node";
const reservedTypes = new Set([ const reservedTypes = new Set([
"_", "_",
@ -2655,7 +2656,7 @@ export default (superClass: Class<Parser>): Class<Parser> =>
// `import {type as ,` or `import {type as }` // `import {type as ,` or `import {type as }`
specifier.imported = as_ident; specifier.imported = as_ident;
specifier.importKind = specifierTypeKind; specifier.importKind = specifierTypeKind;
specifier.local = as_ident.__clone(); specifier.local = cloneIdentifier(as_ident);
} else { } else {
// `import {type as foo` // `import {type as foo`
specifier.imported = firstIdent; specifier.imported = firstIdent;
@ -2673,7 +2674,7 @@ export default (superClass: Class<Parser>): Class<Parser> =>
specifier.local = this.parseIdentifier(); specifier.local = this.parseIdentifier();
} else { } else {
isBinding = true; isBinding = true;
specifier.local = specifier.imported.__clone(); specifier.local = cloneIdentifier(specifier.imported);
} }
} else { } else {
if (firstIdentIsString) { if (firstIdentIsString) {
@ -2688,7 +2689,7 @@ export default (superClass: Class<Parser>): Class<Parser> =>
isBinding = true; isBinding = true;
specifier.imported = firstIdent; specifier.imported = firstIdent;
specifier.importKind = null; specifier.importKind = null;
specifier.local = specifier.imported.__clone(); specifier.local = cloneIdentifier(specifier.imported);
} }
const nodeIsTypeImport = hasTypeImportKind(node); const nodeIsTypeImport = hasTypeImportKind(node);

View File

@ -98,6 +98,7 @@ export type Identifier = PatternBase & {
type: "Identifier", type: "Identifier",
name: string, name: string,
// @deprecated
__clone(): Identifier, __clone(): Identifier,
// TypeScript only. Used in case of an optional parameter. // TypeScript only. Used in case of an optional parameter.

View File

@ -122,7 +122,7 @@ describe("path/replacement", function () {
OptionalMemberExpression(path) { OptionalMemberExpression(path) {
path.node.type = "MemberExpression"; path.node.type = "MemberExpression";
// force `replaceWith` to replace `path.node` // force `replaceWith` to replace `path.node`
path.replaceWith(path.node.__clone()); path.replaceWith(t.cloneNode(path.node));
path.parentPath.ensureBlock(); path.parentPath.ensureBlock();
const aQuestionDotBNode = path.node.object.expression; const aQuestionDotBNode = path.node.object.expression;

View File

@ -23,12 +23,12 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@babel-baseline/parser@npm:@babel/parser@7.14.5": "@babel-baseline/parser@npm:@babel/parser@7.14.8":
version: 7.14.5 version: 7.14.8
resolution: "@babel/parser@npm:7.14.5" resolution: "@babel/parser@npm:7.14.8"
bin: bin:
parser: ./bin/babel-parser.js parser: ./bin/babel-parser.js
checksum: 55c14793888cb7d54275811e7f13136875df1ee4fc368f3f10cff46ebdf95b6a072e706a0486be0ac5686a597cbfb82f33b5f66aa6ba80ff50b73bca945035c6 checksum: 1f900e92675bac6120dfb3e9ea86841fdaba11d338a220017dbcb9e95815a3854ea479da8027e80acaa7f0e618b96e59bbbf3a230a05aaa3407c9419eb742cfe
languageName: node languageName: node
linkType: hard linkType: hard
@ -87,7 +87,7 @@ __metadata:
dependencies: dependencies:
"@babel-baseline/generator": "npm:@babel/generator@7.14.5" "@babel-baseline/generator": "npm:@babel/generator@7.14.5"
"@babel-baseline/helper-validator-identifier": "npm:@babel/helper-validator-identifier@7.10.4" "@babel-baseline/helper-validator-identifier": "npm:@babel/helper-validator-identifier@7.10.4"
"@babel-baseline/parser": "npm:@babel/parser@7.14.5" "@babel-baseline/parser": "npm:@babel/parser@7.14.8"
"@babel/generator": "workspace:*" "@babel/generator": "workspace:*"
"@babel/helper-validator-identifier": "workspace:*" "@babel/helper-validator-identifier": "workspace:*"
"@babel/parser": "workspace:*" "@babel/parser": "workspace:*"