Improve isIdentifierName performance (#13211)
This commit is contained in:
parent
88da2e80ed
commit
47537688d2
@ -1,3 +1,4 @@
|
|||||||
|
benchmark
|
||||||
src
|
src
|
||||||
test
|
test
|
||||||
*.log
|
*.log
|
||||||
|
|||||||
@ -0,0 +1,3 @@
|
|||||||
|
import "./isIdentifierChar.bench.mjs";
|
||||||
|
import "./isIdentifierStart.bench.mjs";
|
||||||
|
import "./isIdentifierName.bench.mjs";
|
||||||
@ -0,0 +1,31 @@
|
|||||||
|
import Benchmark from "benchmark";
|
||||||
|
import baseline from "@babel/helper-validator-identifier-baseline";
|
||||||
|
import current from "../lib/index.js";
|
||||||
|
import { report } from "./util.mjs";
|
||||||
|
|
||||||
|
const suite = new Benchmark.Suite();
|
||||||
|
|
||||||
|
function benchCases(implementation, name) {
|
||||||
|
suite.add(name + "#isIdentifierChar on 4 ASCII characters", () => {
|
||||||
|
implementation.isIdentifierChar(0x61);
|
||||||
|
implementation.isIdentifierChar(0x7b);
|
||||||
|
implementation.isIdentifierChar(0x5f);
|
||||||
|
implementation.isIdentifierChar(0x24);
|
||||||
|
});
|
||||||
|
|
||||||
|
suite.add(name + "#isIdentifierChar on 4 non-ASCII characters", () => {
|
||||||
|
implementation.isIdentifierChar(0x80);
|
||||||
|
implementation.isIdentifierChar(0x4e00);
|
||||||
|
implementation.isIdentifierChar(0xffff);
|
||||||
|
implementation.isIdentifierChar(0x10000);
|
||||||
|
});
|
||||||
|
|
||||||
|
suite.add(name + "#isIdentifierChar on TIP character", () => {
|
||||||
|
implementation.isIdentifierChar(0x30000);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
benchCases(baseline, "baseline");
|
||||||
|
benchCases(current, "current");
|
||||||
|
|
||||||
|
suite.on("cycle", report).run();
|
||||||
@ -0,0 +1,30 @@
|
|||||||
|
import Benchmark from "benchmark";
|
||||||
|
import baseline from "@babel/helper-validator-identifier-baseline";
|
||||||
|
import current from "../lib/index.js";
|
||||||
|
import { report } from "./util.mjs";
|
||||||
|
|
||||||
|
const suite = new Benchmark.Suite();
|
||||||
|
|
||||||
|
function benchCases(implementation, name) {
|
||||||
|
suite.add(name + "#isIdentifierName on 2 short ASCII words", () => {
|
||||||
|
implementation.isIdentifierName("aforementioned");
|
||||||
|
implementation.isIdentifierName("zap cannon");
|
||||||
|
});
|
||||||
|
|
||||||
|
suite.add(name + "#isIdentifierName on 1 long ASCII words", () => {
|
||||||
|
implementation.isIdentifierName(
|
||||||
|
"Pneumonoultramicroscopicsilicovolcanoconiosis"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
suite.add(name + "#isIdentifierName on 3 non-ASCII words", () => {
|
||||||
|
implementation.isIdentifierName("مذكور أعلاه");
|
||||||
|
implementation.isIdentifierName("cañón de zap");
|
||||||
|
implementation.isIdentifierName("𠡦𠧋𡆠囝〇𠁈𢘑𤯔𠀑埊");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
benchCases(baseline, "baseline");
|
||||||
|
benchCases(current, "current");
|
||||||
|
|
||||||
|
suite.on("cycle", report).run();
|
||||||
@ -0,0 +1,31 @@
|
|||||||
|
import Benchmark from "benchmark";
|
||||||
|
import baseline from "@babel/helper-validator-identifier-baseline";
|
||||||
|
import current from "../lib/index.js";
|
||||||
|
import { report } from "./util.mjs";
|
||||||
|
|
||||||
|
const suite = new Benchmark.Suite();
|
||||||
|
|
||||||
|
function benchCases(implementation, name) {
|
||||||
|
suite.add(name + "#isIdentifierStart on 4 ASCII characters", () => {
|
||||||
|
implementation.isIdentifierStart(0x61);
|
||||||
|
implementation.isIdentifierStart(0x7b);
|
||||||
|
implementation.isIdentifierStart(0x5f);
|
||||||
|
implementation.isIdentifierStart(0x24);
|
||||||
|
});
|
||||||
|
|
||||||
|
suite.add(name + "#isIdentifierStart on 4 non-ASCII characters", () => {
|
||||||
|
implementation.isIdentifierStart(0x80);
|
||||||
|
implementation.isIdentifierStart(0x4e00);
|
||||||
|
implementation.isIdentifierStart(0xffff);
|
||||||
|
implementation.isIdentifierStart(0x10000);
|
||||||
|
});
|
||||||
|
|
||||||
|
suite.add(name + "#isIdentifierStart on TIP character", () => {
|
||||||
|
implementation.isIdentifierStart(0x30000);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
benchCases(baseline, "baseline");
|
||||||
|
benchCases(current, "current");
|
||||||
|
|
||||||
|
suite.on("cycle", report).run();
|
||||||
@ -0,0 +1,27 @@
|
|||||||
|
import Benchmark from "benchmark";
|
||||||
|
import baseline from "@babel/helper-validator-identifier-baseline";
|
||||||
|
import current from "../lib/index.js";
|
||||||
|
import { report } from "./util.mjs";
|
||||||
|
|
||||||
|
const suite = new Benchmark.Suite();
|
||||||
|
|
||||||
|
function benchCases(implementation, name) {
|
||||||
|
suite.add(name + "#isKeyword on 4 keywords", () => {
|
||||||
|
implementation.isKeyword("debugger");
|
||||||
|
implementation.isKeyword("throw");
|
||||||
|
implementation.isKeyword("extends");
|
||||||
|
implementation.isKeyword("instanceof");
|
||||||
|
});
|
||||||
|
|
||||||
|
suite.add(name + "#isKeyword on 4 non-keywords", () => {
|
||||||
|
implementation.isKeyword("debuggerr");
|
||||||
|
implementation.isKeyword("threw");
|
||||||
|
implementation.isKeyword("extend");
|
||||||
|
implementation.isKeyword("instanceOf");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
benchCases(baseline, "baseline");
|
||||||
|
benchCases(current, "current");
|
||||||
|
|
||||||
|
suite.on("cycle", report).run();
|
||||||
@ -0,0 +1,27 @@
|
|||||||
|
import Benchmark from "benchmark";
|
||||||
|
import baseline from "@babel/helper-validator-identifier-baseline";
|
||||||
|
import current from "../lib/index.js";
|
||||||
|
import { report } from "./util.mjs";
|
||||||
|
|
||||||
|
const suite = new Benchmark.Suite();
|
||||||
|
|
||||||
|
function benchCases(implementation, name) {
|
||||||
|
suite.add(name + "#isStrictBindReservedWord on 4 keywords", () => {
|
||||||
|
implementation.isStrictBindReservedWord("arguments");
|
||||||
|
implementation.isStrictBindReservedWord("eval");
|
||||||
|
implementation.isStrictBindReservedWord("implements");
|
||||||
|
implementation.isStrictBindReservedWord("instanceof");
|
||||||
|
});
|
||||||
|
|
||||||
|
suite.add(name + "#isStrictBindReservedWord on 4 non-keywords", () => {
|
||||||
|
implementation.isStrictBindReservedWord("argumentss");
|
||||||
|
implementation.isStrictBindReservedWord("evals");
|
||||||
|
implementation.isStrictBindReservedWord("implement");
|
||||||
|
implementation.isStrictBindReservedWord("instanceOf");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
benchCases(baseline, "baseline");
|
||||||
|
benchCases(current, "current");
|
||||||
|
|
||||||
|
suite.on("cycle", report).run({ async: false });
|
||||||
@ -0,0 +1,13 @@
|
|||||||
|
export function report(event) {
|
||||||
|
const bench = event.target;
|
||||||
|
const factor = bench.hz < 100 ? 100 : 1;
|
||||||
|
const timeMs = bench.stats.mean * 1000;
|
||||||
|
const time =
|
||||||
|
timeMs < 10
|
||||||
|
? `${Math.round(timeMs * 1000) / 1000}ms`
|
||||||
|
: `${Math.round(timeMs)}ms`;
|
||||||
|
const msg = `${bench.name}: ${
|
||||||
|
Math.round(bench.hz * factor) / factor
|
||||||
|
} ops/sec ±${Math.round(bench.stats.rme * 100) / 100}% (${time})`;
|
||||||
|
console.log(msg);
|
||||||
|
}
|
||||||
@ -14,7 +14,9 @@
|
|||||||
"main": "./lib/index.js",
|
"main": "./lib/index.js",
|
||||||
"exports": "./lib/index.js",
|
"exports": "./lib/index.js",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"charcodes": "^0.2.0",
|
"@babel/helper-validator-identifier-baseline": "npm:@babel/helper-validator-identifier@7.10.4",
|
||||||
"unicode-13.0.0": "^0.8.0"
|
"@unicode/unicode-13.0.0": "^1.0.6",
|
||||||
|
"benchmark": "^2.1.4",
|
||||||
|
"charcodes": "^0.2.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,14 +4,14 @@
|
|||||||
// https://tc39.github.io/ecma262/#sec-conformance
|
// https://tc39.github.io/ecma262/#sec-conformance
|
||||||
const version = "13.0.0";
|
const version = "13.0.0";
|
||||||
|
|
||||||
const start = require("unicode-" +
|
const start = require("@unicode/unicode-" +
|
||||||
version +
|
version +
|
||||||
"/Binary_Property/ID_Start/code-points.js").filter(function (ch) {
|
"/Binary_Property/ID_Start/code-points.js").filter(function (ch) {
|
||||||
return ch > 0x7f;
|
return ch > 0x7f;
|
||||||
});
|
});
|
||||||
let last = -1;
|
let last = -1;
|
||||||
const cont = [0x200c, 0x200d].concat(
|
const cont = [0x200c, 0x200d].concat(
|
||||||
require("unicode-" +
|
require("@unicode/unicode-" +
|
||||||
version +
|
version +
|
||||||
"/Binary_Property/ID_Continue/code-points.js").filter(function (ch) {
|
"/Binary_Property/ID_Continue/code-points.js").filter(function (ch) {
|
||||||
return ch > 0x7f && search(start, ch, last + 1) == -1;
|
return ch > 0x7f && search(start, ch, last + 1) == -1;
|
||||||
|
|||||||
@ -85,13 +85,23 @@ export function isIdentifierChar(code: number): boolean {
|
|||||||
|
|
||||||
export function isIdentifierName(name: string): boolean {
|
export function isIdentifierName(name: string): boolean {
|
||||||
let isFirst = true;
|
let isFirst = true;
|
||||||
for (const char of Array.from(name)) {
|
for (let i = 0; i < name.length; i++) {
|
||||||
const cp = char.codePointAt(0);
|
// The implementation is based on
|
||||||
|
// https://source.chromium.org/chromium/chromium/src/+/master:v8/src/builtins/builtins-string-gen.cc;l=1455;drc=221e331b49dfefadbc6fa40b0c68e6f97606d0b3;bpv=0;bpt=1
|
||||||
|
// We reimplement `codePointAt` because `codePointAt` is a V8 builtin which is not inlined by TurboFan (as of M91)
|
||||||
|
// since `name` is mostly ASCII, an inlined `charCodeAt` wins here
|
||||||
|
let cp = name.charCodeAt(i);
|
||||||
|
if ((cp & 0xfc00) === 0xd800 && i + 1 < name.length) {
|
||||||
|
const trail = name.charCodeAt(++i);
|
||||||
|
if ((trail & 0xfc00) === 0xdc00) {
|
||||||
|
cp = 0x10000 + ((cp & 0x3ff) << 10) + (trail & 0x3ff);
|
||||||
|
}
|
||||||
|
}
|
||||||
if (isFirst) {
|
if (isFirst) {
|
||||||
|
isFirst = false;
|
||||||
if (!isIdentifierStart(cp)) {
|
if (!isIdentifierStart(cp)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
isFirst = false;
|
|
||||||
} else if (!isIdentifierChar(cp)) {
|
} else if (!isIdentifierChar(cp)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,13 +4,23 @@ describe("isIdentifierName", function () {
|
|||||||
it("returns false if provided string is empty", function () {
|
it("returns false if provided string is empty", function () {
|
||||||
expect(isIdentifierName("")).toBe(false);
|
expect(isIdentifierName("")).toBe(false);
|
||||||
});
|
});
|
||||||
it.each(["hello", "$", "ゆゆ式", "$20", "hello20", "_", "if"])(
|
it.each([
|
||||||
|
"hello",
|
||||||
|
"$",
|
||||||
|
"ゆゆ式",
|
||||||
|
"$20",
|
||||||
|
"hello20",
|
||||||
|
"_",
|
||||||
|
"if",
|
||||||
|
"_\u200c",
|
||||||
|
"_\u200d",
|
||||||
|
])(
|
||||||
"returns true if provided string %p is an IdentifierName",
|
"returns true if provided string %p is an IdentifierName",
|
||||||
function (word) {
|
function (word) {
|
||||||
expect(isIdentifierName(word)).toBe(true);
|
expect(isIdentifierName(word)).toBe(true);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
it.each(["+hello", "0$", "-ゆゆ式", "#_", "_#"])(
|
it.each(["+hello", "0$", "-ゆゆ式", "#_", "_#", "\ud800\ud800"])(
|
||||||
"returns false if provided string %p is not an IdentifierName",
|
"returns false if provided string %p is not an IdentifierName",
|
||||||
function (word) {
|
function (word) {
|
||||||
expect(isIdentifierName(word)).toBe(false);
|
expect(isIdentifierName(word)).toBe(false);
|
||||||
@ -19,4 +29,13 @@ describe("isIdentifierName", function () {
|
|||||||
it("supports astral symbols", function () {
|
it("supports astral symbols", function () {
|
||||||
expect(isIdentifierName("x\uDB40\uDDD5")).toBe(true);
|
expect(isIdentifierName("x\uDB40\uDDD5")).toBe(true);
|
||||||
});
|
});
|
||||||
|
it("supports Unicode 13", () => {
|
||||||
|
expect(isIdentifierName("\u{30000}")).toBe(true);
|
||||||
|
});
|
||||||
|
it("supports Unicode 12", () => {
|
||||||
|
expect(isIdentifierName("\u{10fe0}")).toBe(true);
|
||||||
|
});
|
||||||
|
it("supports Unicode 11", () => {
|
||||||
|
expect(isIdentifierName("\u{10f00}")).toBe(true);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
56
yarn.lock
56
yarn.lock
@ -805,6 +805,13 @@ __metadata:
|
|||||||
languageName: unknown
|
languageName: unknown
|
||||||
linkType: soft
|
linkType: soft
|
||||||
|
|
||||||
|
"@babel/helper-validator-identifier-baseline@npm:@babel/helper-validator-identifier@7.10.4":
|
||||||
|
version: 7.10.4
|
||||||
|
resolution: "@babel/helper-validator-identifier@npm:7.10.4"
|
||||||
|
checksum: 25098ef842e3ffecdd9a7216f6173da7ad7be1b0b3e454a9f6965055154b9ad7a4acd2f218ba3d2efc0821bdab97837b3cb815844af7d72f66f89d446a54efc6
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"@babel/helper-validator-identifier@npm:^7.12.11":
|
"@babel/helper-validator-identifier@npm:^7.12.11":
|
||||||
version: 7.12.11
|
version: 7.12.11
|
||||||
resolution: "@babel/helper-validator-identifier@npm:7.12.11"
|
resolution: "@babel/helper-validator-identifier@npm:7.12.11"
|
||||||
@ -816,8 +823,10 @@ __metadata:
|
|||||||
version: 0.0.0-use.local
|
version: 0.0.0-use.local
|
||||||
resolution: "@babel/helper-validator-identifier@workspace:packages/babel-helper-validator-identifier"
|
resolution: "@babel/helper-validator-identifier@workspace:packages/babel-helper-validator-identifier"
|
||||||
dependencies:
|
dependencies:
|
||||||
|
"@babel/helper-validator-identifier-baseline": "npm:@babel/helper-validator-identifier@7.10.4"
|
||||||
|
"@unicode/unicode-13.0.0": ^1.0.6
|
||||||
|
benchmark: ^2.1.4
|
||||||
charcodes: ^0.2.0
|
charcodes: ^0.2.0
|
||||||
unicode-13.0.0: ^0.8.0
|
|
||||||
languageName: unknown
|
languageName: unknown
|
||||||
linkType: soft
|
linkType: soft
|
||||||
|
|
||||||
@ -4325,6 +4334,13 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"@unicode/unicode-13.0.0@npm:^1.0.6":
|
||||||
|
version: 1.0.6
|
||||||
|
resolution: "@unicode/unicode-13.0.0@npm:1.0.6"
|
||||||
|
checksum: 8971f6f8d302b4cb7e95db6225f4482301b21981c95062209851f1ebc93c392bbf5861029a8f761a857193fb10453cd4043ae376d15a52aed46fd1ada56825d9
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"@webassemblyjs/ast@npm:1.11.0":
|
"@webassemblyjs/ast@npm:1.11.0":
|
||||||
version: 1.11.0
|
version: 1.11.0
|
||||||
resolution: "@webassemblyjs/ast@npm:1.11.0"
|
resolution: "@webassemblyjs/ast@npm:1.11.0"
|
||||||
@ -5623,6 +5639,16 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"benchmark@npm:^2.1.4":
|
||||||
|
version: 2.1.4
|
||||||
|
resolution: "benchmark@npm:2.1.4"
|
||||||
|
dependencies:
|
||||||
|
lodash: ^4.17.4
|
||||||
|
platform: ^1.3.3
|
||||||
|
checksum: 3d3d8f4771b7f9b17f1a967b8f5e70319930fcec2691b35418062342bfbbd1a3221b3129aaf5938fc6a85703837f8cdf2f6875349c158c5aa574b6eb36ab6baa
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"big.js@npm:^5.2.2":
|
"big.js@npm:^5.2.2":
|
||||||
version: 5.2.2
|
version: 5.2.2
|
||||||
resolution: "big.js@npm:5.2.2"
|
resolution: "big.js@npm:5.2.2"
|
||||||
@ -10838,10 +10864,10 @@ fsevents@^1.2.7:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"lodash@npm:^4.17.14, lodash@npm:^4.17.15, lodash@npm:^4.17.19, lodash@npm:^4.17.20":
|
"lodash@npm:^4.17.14, lodash@npm:^4.17.15, lodash@npm:^4.17.19, lodash@npm:^4.17.20, lodash@npm:^4.17.4":
|
||||||
version: 4.17.20
|
version: 4.17.21
|
||||||
resolution: "lodash@npm:4.17.20"
|
resolution: "lodash@npm:4.17.21"
|
||||||
checksum: c62101d2500c383b5f174a7e9e6fe8098149ddd6e9ccfa85f36d4789446195f5c4afd3cfba433026bcaf3da271256566b04a2bf2618e5a39f6e67f8c12030cb6
|
checksum: 4983720b9abca930a4a46f18db163d7dad8dd00dbed6db0cc7b499b33b717cce69f80928b27bbb1ff2cbd3b19d251ee90669a8b5ea466072ca81c2ebe91e7468
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
@ -12228,6 +12254,13 @@ fsevents@^1.2.7:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"platform@npm:^1.3.3":
|
||||||
|
version: 1.3.6
|
||||||
|
resolution: "platform@npm:1.3.6"
|
||||||
|
checksum: d4d10d5a55476c6d369b03e02b31df50a4e7f1c565efabe707379b8a119709fb2a66dec090ab7fe520a30b767fe3791e3c4a5aba985918e51a17df45e469189f
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"please-upgrade-node@npm:^3.1.1, please-upgrade-node@npm:^3.2.0":
|
"please-upgrade-node@npm:^3.1.1, please-upgrade-node@npm:^3.2.0":
|
||||||
version: 3.2.0
|
version: 3.2.0
|
||||||
resolution: "please-upgrade-node@npm:3.2.0"
|
resolution: "please-upgrade-node@npm:3.2.0"
|
||||||
@ -12673,9 +12706,9 @@ fsevents@^1.2.7:
|
|||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"regenerate@npm:^1.4.0":
|
"regenerate@npm:^1.4.0":
|
||||||
version: 1.4.0
|
version: 1.4.2
|
||||||
resolution: "regenerate@npm:1.4.0"
|
resolution: "regenerate@npm:1.4.2"
|
||||||
checksum: d797b035730c0b5cbb7c230220b6a34610f84c1ea2369f0025292613c1ec88068cd87819fccf9c08f002670f26d59e63bbc309358181a6186f7fda185e93618a
|
checksum: 54275a99effd8a439bcdd88942b61f68a769133df841e90d94df9ae7c250cb6537c0a28dd913116539772b3415edbcb3c8d81c22275595d3755cf0353976dfa4
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
@ -14644,13 +14677,6 @@ typescript@~4.2.3:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"unicode-13.0.0@npm:^0.8.0":
|
|
||||||
version: 0.8.0
|
|
||||||
resolution: "unicode-13.0.0@npm:0.8.0"
|
|
||||||
checksum: 5a1b05faae6d92b408ba7dadc05ce5eaf6e9695257487b513ec5b67117b717e98c120f768efafb9da7e9d4a880e8f06ad4f1f8ccdf553c071875007a4cfca7b5
|
|
||||||
languageName: node
|
|
||||||
linkType: hard
|
|
||||||
|
|
||||||
"unicode-canonical-property-names-ecmascript@npm:^1.0.4":
|
"unicode-canonical-property-names-ecmascript@npm:^1.0.4":
|
||||||
version: 1.0.4
|
version: 1.0.4
|
||||||
resolution: "unicode-canonical-property-names-ecmascript@npm:1.0.4"
|
resolution: "unicode-canonical-property-names-ecmascript@npm:1.0.4"
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user