diff --git a/eslint/babel-eslint-parser/src/convert/convertTokens.js b/eslint/babel-eslint-parser/src/convert/convertTokens.js index f444d9941d54..cab96602bffc 100644 --- a/eslint/babel-eslint-parser/src/convert/convertTokens.js +++ b/eslint/babel-eslint-parser/src/convert/convertTokens.js @@ -173,6 +173,8 @@ function convertToken(token, source) { } else if (type === tt.bigint) { token.type = "Numeric"; token.value = `${token.value}n`; + } else if (type === tt.privateName) { + token.type = "PrivateIdentifier"; } if (typeof token.type !== "string") { // Acorn does not have rightAssociative diff --git a/eslint/babel-eslint-parser/test/index.js b/eslint/babel-eslint-parser/test/index.js index e8a2d73b8544..fef130a449ec 100644 --- a/eslint/babel-eslint-parser/test/index.js +++ b/eslint/babel-eslint-parser/test/index.js @@ -320,17 +320,30 @@ describe("Babel and Espree", () => { expect(babylonAST.tokens[1].type).toEqual("Punctuator"); }); - // Espree doesn't support private fields yet - it("hash (token)", () => { - const code = "class A { #x }"; - const babylonAST = parseForESLint(code, { - eslintVisitorKeys: true, - eslintScopeManager: true, - babelOptions: BABEL_OPTIONS, - }).ast; - expect(babylonAST.tokens[3].type).toEqual("Punctuator"); - expect(babylonAST.tokens[3].value).toEqual("#"); - }); + if (process.env.BABEL_8_BREAKING) { + it("hash (token)", () => { + const code = "class A { #x }"; + const babylonAST = parseForESLint(code, { + eslintVisitorKeys: true, + eslintScopeManager: true, + babelOptions: BABEL_OPTIONS, + }).ast; + expect(babylonAST.tokens[3].type).toEqual("PrivateIdentifier"); + expect(babylonAST.tokens[3].value).toEqual("x"); + }); + } else { + // Espree doesn't support private fields yet + it("hash (token)", () => { + const code = "class A { #x }"; + const babylonAST = parseForESLint(code, { + eslintVisitorKeys: true, + eslintScopeManager: true, + babelOptions: BABEL_OPTIONS, + }).ast; + expect(babylonAST.tokens[3].type).toEqual("Punctuator"); + expect(babylonAST.tokens[3].value).toEqual("#"); + }); + } it("parse to PropertyDeclaration when `classFeatures: true`", () => { const code = "class A { #x }"; diff --git a/packages/babel-parser/benchmark/many-class-private-properties/1-length.bench.mjs b/packages/babel-parser/benchmark/many-class-private-properties/1-length.bench.mjs new file mode 100644 index 000000000000..b8d7e31d7832 --- /dev/null +++ b/packages/babel-parser/benchmark/many-class-private-properties/1-length.bench.mjs @@ -0,0 +1,34 @@ +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(); +// All codepoints in [0x4e00, 0x9ffc] are valid identifier name per Unicode 13 +function createInput(length) { + if (length > 0x9ffc - 0x4e00) { + throw new Error( + `Length greater than ${ + 0x9ffc - 0x4e00 + } is not supported! Consider modify the \`createInput\`.` + ); + } + let source = "class C { "; + for (let i = 0; i < length; i++) { + source += "#" + String.fromCharCode(0x4e00 + i) + ";"; + } + return source + " }"; +} +function benchCases(name, implementation, options) { + for (const length of [256, 512, 1024, 2048]) { + const input = createInput(length); + suite.add(`${name} ${length} length-1 private properties`, () => { + implementation.parse(input, options); + }); + } +} + +benchCases("baseline", baseline); +benchCases("current", current); + +suite.on("cycle", report).run(); diff --git a/packages/babel-parser/benchmark/many-empty-statements/attachComment-false.bench.mjs b/packages/babel-parser/benchmark/many-empty-statements/attachComment-false.bench.mjs new file mode 100644 index 000000000000..a75b586b49e1 --- /dev/null +++ b/packages/babel-parser/benchmark/many-empty-statements/attachComment-false.bench.mjs @@ -0,0 +1,19 @@ +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 benchCases(name, implementation, options) { + for (const length of [256, 512, 1024, 2048]) { + suite.add(`${name} ${length} empty statement`, () => { + implementation.parse(";".repeat(length), options); + }); + } +} + +benchCases("baseline", baseline); +benchCases("current + attachComment: false", current, { attachComment: false }); + +suite.on("cycle", report).run(); diff --git a/packages/babel-parser/benchmark/util.mjs b/packages/babel-parser/benchmark/util.mjs new file mode 100644 index 000000000000..f88a43b54c84 --- /dev/null +++ b/packages/babel-parser/benchmark/util.mjs @@ -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); +} diff --git a/packages/babel-parser/package.json b/packages/babel-parser/package.json index 6b880fb15fa7..99876ca74781 100644 --- a/packages/babel-parser/package.json +++ b/packages/babel-parser/package.json @@ -33,9 +33,11 @@ "node": ">=6.0.0" }, "devDependencies": { + "@babel-baseline/parser": "npm:@babel/parser@^7.14.0", "@babel/code-frame": "workspace:*", "@babel/helper-fixtures": "workspace:*", "@babel/helper-validator-identifier": "workspace:*", + "benchmark": "^2.1.4", "charcodes": "^0.2.0" }, "bin": "./bin/babel-parser.js" diff --git a/packages/babel-parser/src/parser/expression.js b/packages/babel-parser/src/parser/expression.js index b7cf9391ad1a..8dc5ca010730 100644 --- a/packages/babel-parser/src/parser/expression.js +++ b/packages/babel-parser/src/parser/expression.js @@ -29,7 +29,8 @@ import { isStrictBindReservedWord, isIdentifierStart, } from "../util/identifier"; -import type { Pos, Position } from "../util/location"; +import type { Pos } from "../util/location"; +import { Position } from "../util/location"; import * as charCodes from "charcodes"; import { BIND_OUTSIDE, @@ -705,18 +706,19 @@ export default class ExpressionParser extends LValParser { const computed = this.eat(tt.bracketL); node.object = base; node.computed = computed; + const privateName = + !computed && this.match(tt.privateName) && this.state.value; const property = computed ? this.parseExpression() - : this.parseMaybePrivateName(true); + : privateName + ? this.parsePrivateName() + : this.parseIdentifier(true); - if (this.isPrivateName(property)) { + if (privateName !== false) { if (node.object.type === "Super") { this.raise(startPos, Errors.SuperPrivateField); } - this.classScope.usePrivateName( - this.getPrivateNameSV(property), - property.start, - ); + this.classScope.usePrivateName(privateName, property.start); } node.property = property; @@ -1160,6 +1162,23 @@ export default class ExpressionParser extends LValParser { } } + case tt.privateName: { + // https://tc39.es/proposal-private-fields-in-in + // RelationalExpression [In, Yield, Await] + // [+In] PrivateIdentifier in ShiftExpression[?Yield, ?Await] + const start = this.state.start; + const value = this.state.value; + node = this.parsePrivateName(); + if (this.match(tt._in)) { + this.expectPlugin("privateIn"); + this.classScope.usePrivateName(value, node.start); + } else if (this.hasPlugin("privateIn")) { + this.raise(this.state.start, Errors.PrivateInExpectedIn, value); + } else { + throw this.unexpected(start); + } + return node; + } case tt.hash: { if (this.state.inPipeline) { node = this.startNode(); @@ -1179,32 +1198,6 @@ export default class ExpressionParser extends LValParser { this.registerTopicReference(); return this.finishNode(node, "PipelinePrimaryTopicReference"); } - - // https://tc39.es/proposal-private-fields-in-in - // RelationalExpression [In, Yield, Await] - // [+In] PrivateIdentifier in ShiftExpression[?Yield, ?Await] - const nextCh = this.input.codePointAt(this.state.end); - if (isIdentifierStart(nextCh) || nextCh === charCodes.backslash) { - const start = this.state.start; - // $FlowIgnore It'll either parse a PrivateName or throw. - node = (this.parseMaybePrivateName(true): N.PrivateName); - if (this.match(tt._in)) { - this.expectPlugin("privateIn"); - this.classScope.usePrivateName( - this.getPrivateNameSV(node), - node.start, - ); - } else if (this.hasPlugin("privateIn")) { - this.raise( - this.state.start, - Errors.PrivateInExpectedIn, - this.getPrivateNameSV(node), - ); - } else { - throw this.unexpected(start); - } - return node; - } } // fall through case tt.relational: { @@ -1305,22 +1298,35 @@ export default class ExpressionParser extends LValParser { parseMaybePrivateName( isPrivateNameAllowed: boolean, ): N.PrivateName | N.Identifier { - const isPrivate = this.match(tt.hash); + const isPrivate = this.match(tt.privateName); if (isPrivate) { if (!isPrivateNameAllowed) { - this.raise(this.state.pos, Errors.UnexpectedPrivateField); + this.raise(this.state.start + 1, Errors.UnexpectedPrivateField); } - const node = this.startNode(); - this.next(); - this.assertNoSpace("Unexpected space between # and identifier"); - node.id = this.parseIdentifier(true); - return this.finishNode(node, "PrivateName"); + return this.parsePrivateName(); } else { return this.parseIdentifier(true); } } + parsePrivateName(): N.PrivateName { + const node = this.startNode(); + const id = this.startNodeAt( + this.state.start + 1, + // The position is hardcoded because we merge `#` and name into a single + // tt.privateName token + new Position( + this.state.curLine, + this.state.start + 1 - this.state.lineStart, + ), + ); + const name = this.state.value; + this.next(); // eat #name; + node.id = this.createIdentifier(id, name); + return this.finishNode(node, "PrivateName"); + } + parseFunctionOrFunctionSent(): N.FunctionExpression | N.MetaProperty { const node = this.startNode(); @@ -1976,15 +1982,16 @@ export default class ExpressionParser extends LValParser { const oldInPropertyName = this.state.inPropertyName; this.state.inPropertyName = true; // We check if it's valid for it to be a private name when we push it. + const type = this.state.type; (prop: $FlowFixMe).key = - this.match(tt.num) || - this.match(tt.string) || - this.match(tt.bigint) || - this.match(tt.decimal) + type === tt.num || + type === tt.string || + type === tt.bigint || + type === tt.decimal ? this.parseExprAtom() : this.parseMaybePrivateName(isPrivateNameAllowed); - if (!this.isPrivateName(prop.key)) { + if (type !== tt.privateName) { // ClassPrivateProperty is never computed, so we don't assign in that case. prop.computed = false; } diff --git a/packages/babel-parser/src/parser/statement.js b/packages/babel-parser/src/parser/statement.js index 1986220a1ede..f67e509e9e5d 100644 --- a/packages/babel-parser/src/parser/statement.js +++ b/packages/babel-parser/src/parser/statement.js @@ -36,6 +36,8 @@ import { newParameterDeclarationScope, } from "../util/expression-scope"; import type { SourceType } from "../options"; +import { Token } from "../tokenizer"; +import { Position } from "../util/location"; const loopLabel = { kind: "loop" }, switchLabel = { kind: "switch" }; @@ -47,6 +49,48 @@ const FUNC_NO_FLAGS = 0b000, const loneSurrogate = /[\uD800-\uDFFF]/u; +/** + * Convert tt.privateName to tt.hash + tt.name for backward Babel 7 compat. + * For performance reasons this routine mutates `tokens`, it is okay + * here since we execute `parseTopLevel` once for every file. + * @param {*} tokens + * @returns + */ +function babel7CompatTokens(tokens) { + if (!process.env.BABEL_8_BREAKING) { + for (let i = 0; i < tokens.length; i++) { + const token = tokens[i]; + if (token.type === tt.privateName) { + const { loc, start, value, end } = token; + const hashEndPos = start + 1; + const hashEndLoc = new Position(loc.start.line, loc.start.column + 1); + tokens.splice( + i, + 1, + // $FlowIgnore: hacky way to create token + new Token({ + type: tt.hash, + value: "#", + start: start, + end: hashEndPos, + startLoc: loc.start, + endLoc: hashEndLoc, + }), + // $FlowIgnore: hacky way to create token + new Token({ + type: tt.name, + value: value, + start: hashEndPos, + end: end, + startLoc: hashEndLoc, + endLoc: loc.end, + }), + ); + } + } + } + return tokens; +} export default class StatementParser extends ExpressionParser { // ### Statement parsing @@ -59,7 +103,7 @@ export default class StatementParser extends ExpressionParser { file.program = this.parseProgram(program); file.comments = this.state.comments; - if (this.options.tokens) file.tokens = this.tokens; + if (this.options.tokens) file.tokens = babel7CompatTokens(this.tokens); return this.finishNode(file, "File"); } @@ -1366,9 +1410,10 @@ export default class StatementParser extends ExpressionParser { if (this.eat(tt.star)) { // a generator method.kind = "method"; + const isPrivateName = this.match(tt.privateName); this.parseClassElementName(method); - if (this.isPrivateName(method.key)) { + if (isPrivateName) { // Private generator method this.pushClassPrivateMethod(classBody, privateMethod, true, false); return; @@ -1391,8 +1436,8 @@ export default class StatementParser extends ExpressionParser { } const containsEsc = this.state.containsEsc; + const isPrivate = this.match(tt.privateName); const key = this.parseClassElementName(member); - const isPrivate = this.isPrivateName(key); // Check the key is not a computed expression or string literal. const isSimple = key.type === "Identifier"; const maybeQuestionTokenStart = this.state.start; @@ -1453,10 +1498,11 @@ export default class StatementParser extends ExpressionParser { method.kind = "method"; // The so-called parsed name would have been "async": get the real name. + const isPrivate = this.match(tt.privateName); this.parseClassElementName(method); this.parsePostMemberNameModifiers(publicMember); - if (this.isPrivateName(method.key)) { + if (isPrivate) { // private async method this.pushClassPrivateMethod( classBody, @@ -1488,9 +1534,10 @@ export default class StatementParser extends ExpressionParser { // a getter or setter method.kind = key.name; // The so-called parsed name would have been "get/set": get the real name. + const isPrivate = this.match(tt.privateName); this.parseClassElementName(publicMethod); - if (this.isPrivateName(method.key)) { + if (isPrivate) { // private getter/setter this.pushClassPrivateMethod(classBody, privateMethod, false, false); } else { @@ -1522,25 +1569,20 @@ export default class StatementParser extends ExpressionParser { // https://tc39.es/proposal-class-fields/#prod-ClassElementName parseClassElementName(member: N.ClassMember): N.Expression | N.Identifier { - const key = this.parsePropertyName(member, /* isPrivateNameAllowed */ true); - + const { type, value, start } = this.state; if ( - !member.computed && + (type === tt.name || type === tt.string) && member.static && - ((key: $FlowSubtype).name === "prototype" || - (key: $FlowSubtype).value === "prototype") + value === "prototype" ) { - this.raise(key.start, Errors.StaticPrototype); + this.raise(start, Errors.StaticPrototype); } - if ( - this.isPrivateName(key) && - this.getPrivateNameSV(key) === "constructor" - ) { - this.raise(key.start, Errors.ConstructorClassPrivateField); + if (type === tt.privateName && value === "constructor") { + this.raise(start, Errors.ConstructorClassPrivateField); } - return key; + return this.parsePropertyName(member, /* isPrivateNameAllowed */ true); } parseClassStaticBlock( diff --git a/packages/babel-parser/src/plugins/typescript/index.js b/packages/babel-parser/src/plugins/typescript/index.js index ea067f57079c..f57a4e912421 100644 --- a/packages/babel-parser/src/plugins/typescript/index.js +++ b/packages/babel-parser/src/plugins/typescript/index.js @@ -210,7 +210,7 @@ export default (superClass: Class): Class => this.match(tt.braceL) || this.match(tt.star) || this.match(tt.ellipsis) || - this.match(tt.hash) || + this.match(tt.privateName) || this.isLiteralPropertyName()) && !this.hasPrecedingLineBreak() ); diff --git a/packages/babel-parser/src/tokenizer/index.js b/packages/babel-parser/src/tokenizer/index.js index 26dec238024f..6c721c16d267 100644 --- a/packages/babel-parser/src/tokenizer/index.js +++ b/packages/babel-parser/src/tokenizer/index.js @@ -205,6 +205,21 @@ export default class Tokenizer extends ParserErrors { return this.input.charCodeAt(this.nextTokenStart()); } + codePointAtPos(pos: number): number { + // 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 `input` is mostly ASCII, an inlined `charCodeAt` wins here + let cp = this.input.charCodeAt(pos); + if ((cp & 0xfc00) === 0xd800 && ++pos < this.input.length) { + const trail = this.input.charCodeAt(pos); + if ((trail & 0xfc00) === 0xdc00) { + cp = 0x10000 + ((cp & 0x3ff) << 10) + (trail & 0x3ff); + } + } + return cp; + } + // Toggle strict mode. Re-reads the next number or string to please // pedantic tests (`"use strict"; 010;` should fail). @@ -244,7 +259,7 @@ export default class Tokenizer extends ParserErrors { if (override) { override(this); } else { - this.getTokenFromCode(this.input.codePointAt(this.state.pos)); + this.getTokenFromCode(this.codePointAtPos(this.state.pos)); } } @@ -407,7 +422,7 @@ export default class Tokenizer extends ParserErrors { } const nextPos = this.state.pos + 1; - const next = this.input.charCodeAt(nextPos); + const next = this.codePointAtPos(nextPos); if (next >= charCodes.digit0 && next <= charCodes.digit9) { throw this.raise(this.state.pos, Errors.UnexpectedDigitAfterHash); } @@ -438,6 +453,9 @@ export default class Tokenizer extends ParserErrors { this.finishToken(tt.bracketHashL); } this.state.pos += 2; + } else if (isIdentifierStart(next) || next === charCodes.backslash) { + ++this.state.pos; + this.finishToken(tt.privateName, this.readWord1()); } else { this.finishOp(tt.hash, 1); } @@ -952,7 +970,7 @@ export default class Tokenizer extends ParserErrors { while (this.state.pos < this.length) { const char = this.input[this.state.pos]; - const charCode = this.input.codePointAt(this.state.pos); + const charCode = this.codePointAtPos(this.state.pos); if (VALID_REGEX_FLAGS.has(char)) { if (mods.indexOf(char) > -1) { @@ -1090,7 +1108,7 @@ export default class Tokenizer extends ParserErrors { throw this.raise(start, Errors.InvalidDecimal); } - if (isIdentifierStart(this.input.codePointAt(this.state.pos))) { + if (isIdentifierStart(this.codePointAtPos(this.state.pos))) { throw this.raise(this.state.pos, Errors.NumberIdentifier); } @@ -1176,7 +1194,7 @@ export default class Tokenizer extends ParserErrors { isDecimal = true; } - if (isIdentifierStart(this.input.codePointAt(this.state.pos))) { + if (isIdentifierStart(this.codePointAtPos(this.state.pos))) { throw this.raise(this.state.pos, Errors.NumberIdentifier); } @@ -1447,7 +1465,7 @@ export default class Tokenizer extends ParserErrors { let chunkStart = this.state.pos; while (this.state.pos < this.length) { - const ch = this.input.codePointAt(this.state.pos); + const ch = this.codePointAtPos(this.state.pos); if (isIdentifierChar(ch)) { this.state.pos += ch <= 0xffff ? 1 : 2; } else if (this.state.isIterator && ch === charCodes.atSign) { diff --git a/packages/babel-parser/src/tokenizer/types.js b/packages/babel-parser/src/tokenizer/types.js index ca67cb0732cf..a415e2b98dad 100644 --- a/packages/babel-parser/src/tokenizer/types.js +++ b/packages/babel-parser/src/tokenizer/types.js @@ -90,6 +90,7 @@ export const types: { [name: string]: TokenType } = { regexp: new TokenType("regexp", { startsExpr }), string: new TokenType("string", { startsExpr }), name: new TokenType("name", { startsExpr }), + privateName: new TokenType("#name", { startsExpr }), eof: new TokenType("eof"), // Punctuation token types. diff --git a/packages/babel-parser/test/fixtures/core/opts/private-name-tokens-true-babel-7/input.js b/packages/babel-parser/test/fixtures/core/opts/private-name-tokens-true-babel-7/input.js new file mode 100644 index 000000000000..dfa4ee68c03e --- /dev/null +++ b/packages/babel-parser/test/fixtures/core/opts/private-name-tokens-true-babel-7/input.js @@ -0,0 +1,3 @@ +class C { + #p +} diff --git a/packages/babel-parser/test/fixtures/core/opts/private-name-tokens-true-babel-7/options.json b/packages/babel-parser/test/fixtures/core/opts/private-name-tokens-true-babel-7/options.json new file mode 100644 index 000000000000..9f5aa40a5dd6 --- /dev/null +++ b/packages/babel-parser/test/fixtures/core/opts/private-name-tokens-true-babel-7/options.json @@ -0,0 +1,4 @@ +{ + "tokens": true, + "BABEL_8_BREAKING": false +} diff --git a/packages/babel-parser/test/fixtures/core/opts/private-name-tokens-true-babel-7/output.json b/packages/babel-parser/test/fixtures/core/opts/private-name-tokens-true-babel-7/output.json new file mode 100644 index 000000000000..fc42b809cb5d --- /dev/null +++ b/packages/babel-parser/test/fixtures/core/opts/private-name-tokens-true-babel-7/output.json @@ -0,0 +1,151 @@ +{ + "type": "File", + "start":0,"end":16,"loc":{"start":{"line":1,"column":0},"end":{"line":3,"column":1}}, + "program": { + "type": "Program", + "start":0,"end":16,"loc":{"start":{"line":1,"column":0},"end":{"line":3,"column":1}}, + "sourceType": "script", + "interpreter": null, + "body": [ + { + "type": "ClassDeclaration", + "start":0,"end":16,"loc":{"start":{"line":1,"column":0},"end":{"line":3,"column":1}}, + "id": { + "type": "Identifier", + "start":6,"end":7,"loc":{"start":{"line":1,"column":6},"end":{"line":1,"column":7},"identifierName":"C"}, + "name": "C" + }, + "superClass": null, + "body": { + "type": "ClassBody", + "start":8,"end":16,"loc":{"start":{"line":1,"column":8},"end":{"line":3,"column":1}}, + "body": [ + { + "type": "ClassPrivateProperty", + "start":12,"end":14,"loc":{"start":{"line":2,"column":2},"end":{"line":2,"column":4}}, + "static": false, + "key": { + "type": "PrivateName", + "start":12,"end":14,"loc":{"start":{"line":2,"column":2},"end":{"line":2,"column":4}}, + "id": { + "type": "Identifier", + "start":13,"end":14,"loc":{"start":{"line":2,"column":3},"end":{"line":2,"column":4},"identifierName":"p"}, + "name": "p" + } + }, + "value": null + } + ] + } + } + ], + "directives": [] + }, + "tokens": [ + { + "type": { + "label": "class", + "keyword": "class", + "beforeExpr": false, + "startsExpr": true, + "rightAssociative": false, + "isLoop": false, + "isAssign": false, + "prefix": false, + "postfix": false, + "binop": null + }, + "value": "class", + "start":0,"end":5,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":5}} + }, + { + "type": { + "label": "name", + "beforeExpr": false, + "startsExpr": true, + "rightAssociative": false, + "isLoop": false, + "isAssign": false, + "prefix": false, + "postfix": false, + "binop": null + }, + "value": "C", + "start":6,"end":7,"loc":{"start":{"line":1,"column":6},"end":{"line":1,"column":7}} + }, + { + "type": { + "label": "{", + "beforeExpr": true, + "startsExpr": true, + "rightAssociative": false, + "isLoop": false, + "isAssign": false, + "prefix": false, + "postfix": false, + "binop": null + }, + "start":8,"end":9,"loc":{"start":{"line":1,"column":8},"end":{"line":1,"column":9}} + }, + { + "type": { + "label": "#", + "beforeExpr": false, + "startsExpr": true, + "rightAssociative": false, + "isLoop": false, + "isAssign": false, + "prefix": false, + "postfix": false, + "binop": null, + "updateContext": null + }, + "value": "#", + "start":12,"end":13,"loc":{"start":{"line":2,"column":2},"end":{"line":2,"column":3}} + }, + { + "type": { + "label": "name", + "beforeExpr": false, + "startsExpr": true, + "rightAssociative": false, + "isLoop": false, + "isAssign": false, + "prefix": false, + "postfix": false, + "binop": null + }, + "value": "p", + "start":13,"end":14,"loc":{"start":{"line":2,"column":3},"end":{"line":2,"column":4}} + }, + { + "type": { + "label": "}", + "beforeExpr": false, + "startsExpr": false, + "rightAssociative": false, + "isLoop": false, + "isAssign": false, + "prefix": false, + "postfix": false, + "binop": null + }, + "start":15,"end":16,"loc":{"start":{"line":3,"column":0},"end":{"line":3,"column":1}} + }, + { + "type": { + "label": "eof", + "beforeExpr": false, + "startsExpr": false, + "rightAssociative": false, + "isLoop": false, + "isAssign": false, + "prefix": false, + "postfix": false, + "binop": null, + "updateContext": null + }, + "start":16,"end":16,"loc":{"start":{"line":3,"column":1},"end":{"line":3,"column":1}} + } + ] +} diff --git a/packages/babel-parser/test/fixtures/core/opts/private-name-tokens-true/input.js b/packages/babel-parser/test/fixtures/core/opts/private-name-tokens-true/input.js new file mode 100644 index 000000000000..dfa4ee68c03e --- /dev/null +++ b/packages/babel-parser/test/fixtures/core/opts/private-name-tokens-true/input.js @@ -0,0 +1,3 @@ +class C { + #p +} diff --git a/packages/babel-parser/test/fixtures/core/opts/private-name-tokens-true/options.json b/packages/babel-parser/test/fixtures/core/opts/private-name-tokens-true/options.json new file mode 100644 index 000000000000..5c34e72c636a --- /dev/null +++ b/packages/babel-parser/test/fixtures/core/opts/private-name-tokens-true/options.json @@ -0,0 +1,4 @@ +{ + "tokens": true, + "BABEL_8_BREAKING": true +} diff --git a/packages/babel-parser/test/fixtures/core/opts/private-name-tokens-true/output.json b/packages/babel-parser/test/fixtures/core/opts/private-name-tokens-true/output.json new file mode 100644 index 000000000000..0e4e206e0001 --- /dev/null +++ b/packages/babel-parser/test/fixtures/core/opts/private-name-tokens-true/output.json @@ -0,0 +1,136 @@ +{ + "type": "File", + "start":0,"end":16,"loc":{"start":{"line":1,"column":0},"end":{"line":3,"column":1}}, + "program": { + "type": "Program", + "start":0,"end":16,"loc":{"start":{"line":1,"column":0},"end":{"line":3,"column":1}}, + "sourceType": "script", + "interpreter": null, + "body": [ + { + "type": "ClassDeclaration", + "start":0,"end":16,"loc":{"start":{"line":1,"column":0},"end":{"line":3,"column":1}}, + "id": { + "type": "Identifier", + "start":6,"end":7,"loc":{"start":{"line":1,"column":6},"end":{"line":1,"column":7},"identifierName":"C"}, + "name": "C" + }, + "superClass": null, + "body": { + "type": "ClassBody", + "start":8,"end":16,"loc":{"start":{"line":1,"column":8},"end":{"line":3,"column":1}}, + "body": [ + { + "type": "ClassPrivateProperty", + "start":12,"end":14,"loc":{"start":{"line":2,"column":2},"end":{"line":2,"column":4}}, + "static": false, + "key": { + "type": "PrivateName", + "start":12,"end":14,"loc":{"start":{"line":2,"column":2},"end":{"line":2,"column":4}}, + "id": { + "type": "Identifier", + "start":13,"end":14,"loc":{"start":{"line":2,"column":3},"end":{"line":2,"column":4},"identifierName":"p"}, + "name": "p" + } + }, + "value": null + } + ] + } + } + ], + "directives": [] + }, + "tokens": [ + { + "type": { + "label": "class", + "keyword": "class", + "beforeExpr": false, + "startsExpr": true, + "rightAssociative": false, + "isLoop": false, + "isAssign": false, + "prefix": false, + "postfix": false, + "binop": null + }, + "value": "class", + "start":0,"end":5,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":5}} + }, + { + "type": { + "label": "name", + "beforeExpr": false, + "startsExpr": true, + "rightAssociative": false, + "isLoop": false, + "isAssign": false, + "prefix": false, + "postfix": false, + "binop": null + }, + "value": "C", + "start":6,"end":7,"loc":{"start":{"line":1,"column":6},"end":{"line":1,"column":7}} + }, + { + "type": { + "label": "{", + "beforeExpr": true, + "startsExpr": true, + "rightAssociative": false, + "isLoop": false, + "isAssign": false, + "prefix": false, + "postfix": false, + "binop": null + }, + "start":8,"end":9,"loc":{"start":{"line":1,"column":8},"end":{"line":1,"column":9}} + }, + { + "type": { + "label": "#name", + "beforeExpr": false, + "startsExpr": true, + "rightAssociative": false, + "isLoop": false, + "isAssign": false, + "prefix": false, + "postfix": false, + "binop": null, + "updateContext": null + }, + "value": "p", + "start":12,"end":14,"loc":{"start":{"line":2,"column":2},"end":{"line":2,"column":4}} + }, + { + "type": { + "label": "}", + "beforeExpr": false, + "startsExpr": false, + "rightAssociative": false, + "isLoop": false, + "isAssign": false, + "prefix": false, + "postfix": false, + "binop": null + }, + "start":15,"end":16,"loc":{"start":{"line":3,"column":0},"end":{"line":3,"column":1}} + }, + { + "type": { + "label": "eof", + "beforeExpr": false, + "startsExpr": false, + "rightAssociative": false, + "isLoop": false, + "isAssign": false, + "prefix": false, + "postfix": false, + "binop": null, + "updateContext": null + }, + "start":16,"end":16,"loc":{"start":{"line":3,"column":1},"end":{"line":3,"column":1}} + } + ] +} diff --git a/packages/babel-parser/test/fixtures/es2022/class-private-methods/failure-spaces/options.json b/packages/babel-parser/test/fixtures/es2022/class-private-methods/failure-spaces/options.json new file mode 100644 index 000000000000..a10e365ed5d3 --- /dev/null +++ b/packages/babel-parser/test/fixtures/es2022/class-private-methods/failure-spaces/options.json @@ -0,0 +1,3 @@ +{ + "throws": "Unexpected token (2:2)" +} diff --git a/packages/babel-parser/test/fixtures/es2022/class-private-methods/failure-spaces/output.json b/packages/babel-parser/test/fixtures/es2022/class-private-methods/failure-spaces/output.json deleted file mode 100644 index bda07d6588d0..000000000000 --- a/packages/babel-parser/test/fixtures/es2022/class-private-methods/failure-spaces/output.json +++ /dev/null @@ -1,72 +0,0 @@ -{ - "type": "File", - "start":0,"end":60,"loc":{"start":{"line":1,"column":0},"end":{"line":5,"column":1}}, - "errors": [ - "SyntaxError: Unexpected space between # and identifier (2:3)" - ], - "program": { - "type": "Program", - "start":0,"end":60,"loc":{"start":{"line":1,"column":0},"end":{"line":5,"column":1}}, - "sourceType": "script", - "interpreter": null, - "body": [ - { - "type": "ClassDeclaration", - "start":0,"end":60,"loc":{"start":{"line":1,"column":0},"end":{"line":5,"column":1}}, - "id": { - "type": "Identifier", - "start":6,"end":12,"loc":{"start":{"line":1,"column":6},"end":{"line":1,"column":12},"identifierName":"Spaces"}, - "name": "Spaces" - }, - "superClass": null, - "body": { - "type": "ClassBody", - "start":13,"end":60,"loc":{"start":{"line":1,"column":13},"end":{"line":5,"column":1}}, - "body": [ - { - "type": "ClassPrivateMethod", - "start":17,"end":58,"loc":{"start":{"line":2,"column":2},"end":{"line":4,"column":3}}, - "static": false, - "key": { - "type": "PrivateName", - "start":17,"end":31,"loc":{"start":{"line":2,"column":2},"end":{"line":2,"column":16}}, - "id": { - "type": "Identifier", - "start":20,"end":31,"loc":{"start":{"line":2,"column":5},"end":{"line":2,"column":16},"identifierName":"wrongSpaces"}, - "name": "wrongSpaces" - } - }, - "kind": "method", - "id": null, - "generator": false, - "async": false, - "params": [], - "body": { - "type": "BlockStatement", - "start":34,"end":58,"loc":{"start":{"line":2,"column":19},"end":{"line":4,"column":3}}, - "body": [ - { - "type": "ReturnStatement", - "start":40,"end":54,"loc":{"start":{"line":3,"column":4},"end":{"line":3,"column":18}}, - "argument": { - "type": "CallExpression", - "start":47,"end":53,"loc":{"start":{"line":3,"column":11},"end":{"line":3,"column":17}}, - "callee": { - "type": "Identifier", - "start":47,"end":51,"loc":{"start":{"line":3,"column":11},"end":{"line":3,"column":15},"identifierName":"fail"}, - "name": "fail" - }, - "arguments": [] - } - } - ], - "directives": [] - } - } - ] - } - } - ], - "directives": [] - } -} \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/es2022/class-private-properties/failure-computed/options.json b/packages/babel-parser/test/fixtures/es2022/class-private-properties/failure-computed/options.json index 40615fd0b8fe..e5bad81e363a 100644 --- a/packages/babel-parser/test/fixtures/es2022/class-private-properties/failure-computed/options.json +++ b/packages/babel-parser/test/fixtures/es2022/class-private-properties/failure-computed/options.json @@ -1,4 +1,4 @@ { - "throws": "Unexpected token (3:3)", + "throws": "Unexpected token (3:2)", "plugins": [] } diff --git a/packages/babel-parser/test/fixtures/es2022/class-private-properties/failure-spaces/options.json b/packages/babel-parser/test/fixtures/es2022/class-private-properties/failure-spaces/options.json new file mode 100644 index 000000000000..a10e365ed5d3 --- /dev/null +++ b/packages/babel-parser/test/fixtures/es2022/class-private-properties/failure-spaces/options.json @@ -0,0 +1,3 @@ +{ + "throws": "Unexpected token (2:2)" +} diff --git a/packages/babel-parser/test/fixtures/es2022/class-private-properties/failure-spaces/output.json b/packages/babel-parser/test/fixtures/es2022/class-private-properties/failure-spaces/output.json deleted file mode 100644 index 8e3342fe9f68..000000000000 --- a/packages/babel-parser/test/fixtures/es2022/class-private-properties/failure-spaces/output.json +++ /dev/null @@ -1,47 +0,0 @@ -{ - "type": "File", - "start":0,"end":34,"loc":{"start":{"line":1,"column":0},"end":{"line":3,"column":1}}, - "errors": [ - "SyntaxError: Unexpected space between # and identifier (2:3)" - ], - "program": { - "type": "Program", - "start":0,"end":34,"loc":{"start":{"line":1,"column":0},"end":{"line":3,"column":1}}, - "sourceType": "script", - "interpreter": null, - "body": [ - { - "type": "ClassDeclaration", - "start":0,"end":34,"loc":{"start":{"line":1,"column":0},"end":{"line":3,"column":1}}, - "id": { - "type": "Identifier", - "start":6,"end":12,"loc":{"start":{"line":1,"column":6},"end":{"line":1,"column":12},"identifierName":"Spaces"}, - "name": "Spaces" - }, - "superClass": null, - "body": { - "type": "ClassBody", - "start":13,"end":34,"loc":{"start":{"line":1,"column":13},"end":{"line":3,"column":1}}, - "body": [ - { - "type": "ClassPrivateProperty", - "start":17,"end":32,"loc":{"start":{"line":2,"column":2},"end":{"line":2,"column":17}}, - "static": false, - "key": { - "type": "PrivateName", - "start":17,"end":31,"loc":{"start":{"line":2,"column":2},"end":{"line":2,"column":16}}, - "id": { - "type": "Identifier", - "start":20,"end":31,"loc":{"start":{"line":2,"column":5},"end":{"line":2,"column":16},"identifierName":"wrongSpaces"}, - "name": "wrongSpaces" - } - }, - "value": null - } - ] - } - } - ], - "directives": [] - } -} \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/es2022/class-private-properties/failure-string-literal/options.json b/packages/babel-parser/test/fixtures/es2022/class-private-properties/failure-string-literal/options.json index 99f73539477e..a6d39601787f 100644 --- a/packages/babel-parser/test/fixtures/es2022/class-private-properties/failure-string-literal/options.json +++ b/packages/babel-parser/test/fixtures/es2022/class-private-properties/failure-string-literal/options.json @@ -1,4 +1,4 @@ { - "throws": "Unexpected token (2:3)", + "throws": "Unexpected token (2:2)", "plugins": [] } diff --git a/yarn.lock b/yarn.lock index 07b9bdad35f5..fb3bfe8b251b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5,6 +5,15 @@ __metadata: version: 4 cacheKey: 7 +"@babel-baseline/parser@npm:@babel/parser@^7.14.0, @babel/parser@npm:^7.0.0, @babel/parser@npm:^7.12.13, @babel/parser@npm:^7.12.7, @babel/parser@npm:^7.14.0": + version: 7.14.0 + resolution: "@babel/parser@npm:7.14.0" + bin: + parser: ./bin/babel-parser.js + checksum: ef6165f3038b9f8761a02768ab14034f4935baf2e050a2924aa093f54e3164732bce7fee81b3c8ff3be03048091e4ea208a72b23a3715debf7fd06b79495c9e9 + languageName: node + linkType: hard + "@babel-internal/runtime-integration-rollup@workspace:test/runtime-integration/rollup": version: 0.0.0-use.local resolution: "@babel-internal/runtime-integration-rollup@workspace:test/runtime-integration/rollup" @@ -935,22 +944,15 @@ __metadata: languageName: unknown linkType: soft -"@babel/parser@npm:^7.0.0, @babel/parser@npm:^7.12.13, @babel/parser@npm:^7.12.7, @babel/parser@npm:^7.14.0": - version: 7.14.0 - resolution: "@babel/parser@npm:7.14.0" - bin: - parser: ./bin/babel-parser.js - checksum: ef6165f3038b9f8761a02768ab14034f4935baf2e050a2924aa093f54e3164732bce7fee81b3c8ff3be03048091e4ea208a72b23a3715debf7fd06b79495c9e9 - languageName: node - linkType: hard - "@babel/parser@workspace:*, @babel/parser@workspace:^7.12.13, @babel/parser@workspace:^7.14.0, @babel/parser@workspace:packages/babel-parser": version: 0.0.0-use.local resolution: "@babel/parser@workspace:packages/babel-parser" dependencies: + "@babel-baseline/parser": "npm:@babel/parser@^7.14.0" "@babel/code-frame": "workspace:*" "@babel/helper-fixtures": "workspace:*" "@babel/helper-validator-identifier": "workspace:*" + benchmark: ^2.1.4 charcodes: ^0.2.0 bin: parser: ./bin/babel-parser.js