diff --git a/packages/babel-parser/benchmark/many-let-declarations/10-length-binding.mjs b/packages/babel-parser/benchmark/many-let-declarations/10-length-binding.mjs new file mode 100644 index 000000000000..f6c54553592b --- /dev/null +++ b/packages/babel-parser/benchmark/many-let-declarations/10-length-binding.mjs @@ -0,0 +1,22 @@ +import Benchmark from "benchmark"; +import baseline from "@babel-baseline/parser"; +import current from "../../lib/index.js"; +import { report } from "../util.mjs"; + +const suite = new Benchmark.Suite(); +function createInput(length) { + return "{ let foecnatsni };".repeat(length); +} +function benchCases(name, implementation, options) { + for (const length of [64, 128, 256, 512]) { + const input = createInput(length); + suite.add(`${name} ${length} let and length-10 binding identifiers`, () => { + implementation.parse(input, options); + }); + } +} + +benchCases("baseline", baseline); +benchCases("current", current); + +suite.on("cycle", report).run(); diff --git a/packages/babel-parser/src/parser/statement.js b/packages/babel-parser/src/parser/statement.js index 6db4f9d61bbd..7aefb14a4f49 100644 --- a/packages/babel-parser/src/parser/statement.js +++ b/packages/babel-parser/src/parser/statement.js @@ -4,11 +4,7 @@ import * as N from "../types"; import { types as tt, type TokenType } from "../tokenizer/types"; import ExpressionParser from "./expression"; import { Errors, SourceTypeModuleErrors } from "./error"; -import { - isIdentifierChar, - isIdentifierStart, - keywordRelationalOperator, -} from "../util/identifier"; +import { isIdentifierChar, isIdentifierStart } from "../util/identifier"; import { lineBreak } from "../util/whitespace"; import * as charCodes from "charcodes"; import { @@ -49,6 +45,8 @@ const FUNC_NO_FLAGS = 0b000, const loneSurrogate = /[\uD800-\uDFFF]/u; +const keywordRelationalOperator = /in(?:stanceof)?/y; + /** * Convert tt.privateName to tt.hash + tt.name for backward Babel 7 compat. * For performance reasons this routine mutates `tokens`, it is okay @@ -183,7 +181,7 @@ export default class StatementParser extends ExpressionParser { */ isLetKeyword(context: ?string): boolean { const next = this.nextTokenStart(); - const nextCh = this.input.charCodeAt(next); + const nextCh = this.codePointAtPos(next); // For ambiguous cases, determine if a LexicalDeclaration (or only a // Statement) is allowed here. If context is not empty then only a Statement // is allowed. However, `let [` is an explicit negative lookahead for @@ -200,12 +198,17 @@ export default class StatementParser extends ExpressionParser { if (nextCh === charCodes.leftCurlyBrace) return true; if (isIdentifierStart(nextCh)) { - let pos = next + 1; - while (isIdentifierChar(this.input.charCodeAt(pos))) { - ++pos; + keywordRelationalOperator.lastIndex = next; + const matched = keywordRelationalOperator.exec(this.input); + if (matched !== null) { + // We have seen `in` or `instanceof` so far, now check if the identfier + // ends here + const endCh = this.codePointAtPos(next + matched[0].length); + if (!isIdentifierChar(endCh) && endCh !== charCodes.backslash) { + return false; + } } - const ident = this.input.slice(next, pos); - if (!keywordRelationalOperator.test(ident)) return true; + return true; } return false; } diff --git a/packages/babel-parser/test/fixtures/es2015/let/let-declaration-in-escape-id/input.js b/packages/babel-parser/test/fixtures/es2015/let/let-declaration-in-escape-id/input.js new file mode 100644 index 000000000000..4eac9bd1a23b --- /dev/null +++ b/packages/babel-parser/test/fixtures/es2015/let/let-declaration-in-escape-id/input.js @@ -0,0 +1 @@ +let in\u0061; diff --git a/packages/babel-parser/test/fixtures/es2015/let/let-declaration-in-escape-id/output.json b/packages/babel-parser/test/fixtures/es2015/let/let-declaration-in-escape-id/output.json new file mode 100644 index 000000000000..15a1640f4e9a --- /dev/null +++ b/packages/babel-parser/test/fixtures/es2015/let/let-declaration-in-escape-id/output.json @@ -0,0 +1,30 @@ +{ + "type": "File", + "start":0,"end":13,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":13}}, + "program": { + "type": "Program", + "start":0,"end":13,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":13}}, + "sourceType": "script", + "interpreter": null, + "body": [ + { + "type": "VariableDeclaration", + "start":0,"end":13,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":13}}, + "declarations": [ + { + "type": "VariableDeclarator", + "start":4,"end":12,"loc":{"start":{"line":1,"column":4},"end":{"line":1,"column":12}}, + "id": { + "type": "Identifier", + "start":4,"end":12,"loc":{"start":{"line":1,"column":4},"end":{"line":1,"column":12},"identifierName":"ina"}, + "name": "ina" + }, + "init": null + } + ], + "kind": "let" + } + ], + "directives": [] + } +} \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/es2015/let/let-declaration-in-non-BMP/input.js b/packages/babel-parser/test/fixtures/es2015/let/let-declaration-in-non-BMP/input.js new file mode 100644 index 000000000000..93c89c2ec0af --- /dev/null +++ b/packages/babel-parser/test/fixtures/es2015/let/let-declaration-in-non-BMP/input.js @@ -0,0 +1 @@ +let in𝐬𝐭𝐚𝐧𝐜𝐞𝐨𝐟; diff --git a/packages/babel-parser/test/fixtures/es2015/let/let-declaration-in-non-BMP/output.json b/packages/babel-parser/test/fixtures/es2015/let/let-declaration-in-non-BMP/output.json new file mode 100644 index 000000000000..e9cd43853f54 --- /dev/null +++ b/packages/babel-parser/test/fixtures/es2015/let/let-declaration-in-non-BMP/output.json @@ -0,0 +1,30 @@ +{ + "type": "File", + "start":0,"end":23,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":23}}, + "program": { + "type": "Program", + "start":0,"end":23,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":23}}, + "sourceType": "script", + "interpreter": null, + "body": [ + { + "type": "VariableDeclaration", + "start":0,"end":23,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":23}}, + "declarations": [ + { + "type": "VariableDeclarator", + "start":4,"end":22,"loc":{"start":{"line":1,"column":4},"end":{"line":1,"column":22}}, + "id": { + "type": "Identifier", + "start":4,"end":22,"loc":{"start":{"line":1,"column":4},"end":{"line":1,"column":22},"identifierName":"in𝐬𝐭𝐚𝐧𝐜𝐞𝐨𝐟"}, + "name": "in𝐬𝐭𝐚𝐧𝐜𝐞𝐨𝐟" + }, + "init": null + } + ], + "kind": "let" + } + ], + "directives": [] + } +} \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/es2015/let/let-declaration-non-BMP-identifier/input.js b/packages/babel-parser/test/fixtures/es2015/let/let-declaration-non-BMP-identifier/input.js new file mode 100644 index 000000000000..dd461553aeb3 --- /dev/null +++ b/packages/babel-parser/test/fixtures/es2015/let/let-declaration-non-BMP-identifier/input.js @@ -0,0 +1 @@ +let 𝐒𝐧; diff --git a/packages/babel-parser/test/fixtures/es2015/let/let-declaration-non-BMP-identifier/output.json b/packages/babel-parser/test/fixtures/es2015/let/let-declaration-non-BMP-identifier/output.json new file mode 100644 index 000000000000..a182a8ffceab --- /dev/null +++ b/packages/babel-parser/test/fixtures/es2015/let/let-declaration-non-BMP-identifier/output.json @@ -0,0 +1,30 @@ +{ + "type": "File", + "start":0,"end":9,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":9}}, + "program": { + "type": "Program", + "start":0,"end":9,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":9}}, + "sourceType": "script", + "interpreter": null, + "body": [ + { + "type": "VariableDeclaration", + "start":0,"end":9,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":9}}, + "declarations": [ + { + "type": "VariableDeclarator", + "start":4,"end":8,"loc":{"start":{"line":1,"column":4},"end":{"line":1,"column":8}}, + "id": { + "type": "Identifier", + "start":4,"end":8,"loc":{"start":{"line":1,"column":4},"end":{"line":1,"column":8},"identifierName":"𝐒𝐧"}, + "name": "𝐒𝐧" + }, + "init": null + } + ], + "kind": "let" + } + ], + "directives": [] + } +} \ No newline at end of file