Refactor private name tokenizing (#13256)

* add benchmark

* refactor: create tt.privateName token for private names

* add backward compat privateName = hash + name to Babel 7

* perf: get private name SV from token value

* chore: tweak benchmark file

* chore: update test fixtures

* convert tt.privateName to PrivateIdentifier

* perf: avoid most isPrivateName call

* Update packages/babel-parser/src/parser/expression.js

Co-authored-by: Justin Ridgewell <justin@ridgewell.name>

* perf: use inlinable codePointAtPos

* make prettier happy

Co-authored-by: Justin Ridgewell <justin@ridgewell.name>
This commit is contained in:
Huáng Jùnliàng 2021-05-06 09:46:09 -04:00 committed by GitHub
parent 278193b6f7
commit a387973821
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 551 additions and 210 deletions

View File

@ -173,6 +173,8 @@ function convertToken(token, source) {
} else if (type === tt.bigint) { } else if (type === tt.bigint) {
token.type = "Numeric"; token.type = "Numeric";
token.value = `${token.value}n`; token.value = `${token.value}n`;
} else if (type === tt.privateName) {
token.type = "PrivateIdentifier";
} }
if (typeof token.type !== "string") { if (typeof token.type !== "string") {
// Acorn does not have rightAssociative // Acorn does not have rightAssociative

View File

@ -320,17 +320,30 @@ describe("Babel and Espree", () => {
expect(babylonAST.tokens[1].type).toEqual("Punctuator"); expect(babylonAST.tokens[1].type).toEqual("Punctuator");
}); });
// Espree doesn't support private fields yet if (process.env.BABEL_8_BREAKING) {
it("hash (token)", () => { it("hash (token)", () => {
const code = "class A { #x }"; const code = "class A { #x }";
const babylonAST = parseForESLint(code, { const babylonAST = parseForESLint(code, {
eslintVisitorKeys: true, eslintVisitorKeys: true,
eslintScopeManager: true, eslintScopeManager: true,
babelOptions: BABEL_OPTIONS, babelOptions: BABEL_OPTIONS,
}).ast; }).ast;
expect(babylonAST.tokens[3].type).toEqual("Punctuator"); expect(babylonAST.tokens[3].type).toEqual("PrivateIdentifier");
expect(babylonAST.tokens[3].value).toEqual("#"); 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`", () => { it("parse to PropertyDeclaration when `classFeatures: true`", () => {
const code = "class A { #x }"; const code = "class A { #x }";

View File

@ -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();

View File

@ -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();

View File

@ -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);
}

View File

@ -33,9 +33,11 @@
"node": ">=6.0.0" "node": ">=6.0.0"
}, },
"devDependencies": { "devDependencies": {
"@babel-baseline/parser": "npm:@babel/parser@^7.14.0",
"@babel/code-frame": "workspace:*", "@babel/code-frame": "workspace:*",
"@babel/helper-fixtures": "workspace:*", "@babel/helper-fixtures": "workspace:*",
"@babel/helper-validator-identifier": "workspace:*", "@babel/helper-validator-identifier": "workspace:*",
"benchmark": "^2.1.4",
"charcodes": "^0.2.0" "charcodes": "^0.2.0"
}, },
"bin": "./bin/babel-parser.js" "bin": "./bin/babel-parser.js"

View File

@ -29,7 +29,8 @@ import {
isStrictBindReservedWord, isStrictBindReservedWord,
isIdentifierStart, isIdentifierStart,
} from "../util/identifier"; } 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 * as charCodes from "charcodes";
import { import {
BIND_OUTSIDE, BIND_OUTSIDE,
@ -705,18 +706,19 @@ export default class ExpressionParser extends LValParser {
const computed = this.eat(tt.bracketL); const computed = this.eat(tt.bracketL);
node.object = base; node.object = base;
node.computed = computed; node.computed = computed;
const privateName =
!computed && this.match(tt.privateName) && this.state.value;
const property = computed const property = computed
? this.parseExpression() ? this.parseExpression()
: this.parseMaybePrivateName(true); : privateName
? this.parsePrivateName()
: this.parseIdentifier(true);
if (this.isPrivateName(property)) { if (privateName !== false) {
if (node.object.type === "Super") { if (node.object.type === "Super") {
this.raise(startPos, Errors.SuperPrivateField); this.raise(startPos, Errors.SuperPrivateField);
} }
this.classScope.usePrivateName( this.classScope.usePrivateName(privateName, property.start);
this.getPrivateNameSV(property),
property.start,
);
} }
node.property = property; 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: { case tt.hash: {
if (this.state.inPipeline) { if (this.state.inPipeline) {
node = this.startNode(); node = this.startNode();
@ -1179,32 +1198,6 @@ export default class ExpressionParser extends LValParser {
this.registerTopicReference(); this.registerTopicReference();
return this.finishNode(node, "PipelinePrimaryTopicReference"); 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 // fall through
case tt.relational: { case tt.relational: {
@ -1305,22 +1298,35 @@ export default class ExpressionParser extends LValParser {
parseMaybePrivateName( parseMaybePrivateName(
isPrivateNameAllowed: boolean, isPrivateNameAllowed: boolean,
): N.PrivateName | N.Identifier { ): N.PrivateName | N.Identifier {
const isPrivate = this.match(tt.hash); const isPrivate = this.match(tt.privateName);
if (isPrivate) { if (isPrivate) {
if (!isPrivateNameAllowed) { if (!isPrivateNameAllowed) {
this.raise(this.state.pos, Errors.UnexpectedPrivateField); this.raise(this.state.start + 1, Errors.UnexpectedPrivateField);
} }
const node = this.startNode(); return this.parsePrivateName();
this.next();
this.assertNoSpace("Unexpected space between # and identifier");
node.id = this.parseIdentifier(true);
return this.finishNode(node, "PrivateName");
} else { } else {
return this.parseIdentifier(true); 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 { parseFunctionOrFunctionSent(): N.FunctionExpression | N.MetaProperty {
const node = this.startNode(); const node = this.startNode();
@ -1976,15 +1982,16 @@ export default class ExpressionParser extends LValParser {
const oldInPropertyName = this.state.inPropertyName; const oldInPropertyName = this.state.inPropertyName;
this.state.inPropertyName = true; this.state.inPropertyName = true;
// We check if it's valid for it to be a private name when we push it. // 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 = (prop: $FlowFixMe).key =
this.match(tt.num) || type === tt.num ||
this.match(tt.string) || type === tt.string ||
this.match(tt.bigint) || type === tt.bigint ||
this.match(tt.decimal) type === tt.decimal
? this.parseExprAtom() ? this.parseExprAtom()
: this.parseMaybePrivateName(isPrivateNameAllowed); : this.parseMaybePrivateName(isPrivateNameAllowed);
if (!this.isPrivateName(prop.key)) { if (type !== tt.privateName) {
// ClassPrivateProperty is never computed, so we don't assign in that case. // ClassPrivateProperty is never computed, so we don't assign in that case.
prop.computed = false; prop.computed = false;
} }

View File

@ -36,6 +36,8 @@ import {
newParameterDeclarationScope, newParameterDeclarationScope,
} from "../util/expression-scope"; } from "../util/expression-scope";
import type { SourceType } from "../options"; import type { SourceType } from "../options";
import { Token } from "../tokenizer";
import { Position } from "../util/location";
const loopLabel = { kind: "loop" }, const loopLabel = { kind: "loop" },
switchLabel = { kind: "switch" }; switchLabel = { kind: "switch" };
@ -47,6 +49,48 @@ const FUNC_NO_FLAGS = 0b000,
const loneSurrogate = /[\uD800-\uDFFF]/u; 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 { export default class StatementParser extends ExpressionParser {
// ### Statement parsing // ### Statement parsing
@ -59,7 +103,7 @@ export default class StatementParser extends ExpressionParser {
file.program = this.parseProgram(program); file.program = this.parseProgram(program);
file.comments = this.state.comments; 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"); return this.finishNode(file, "File");
} }
@ -1366,9 +1410,10 @@ export default class StatementParser extends ExpressionParser {
if (this.eat(tt.star)) { if (this.eat(tt.star)) {
// a generator // a generator
method.kind = "method"; method.kind = "method";
const isPrivateName = this.match(tt.privateName);
this.parseClassElementName(method); this.parseClassElementName(method);
if (this.isPrivateName(method.key)) { if (isPrivateName) {
// Private generator method // Private generator method
this.pushClassPrivateMethod(classBody, privateMethod, true, false); this.pushClassPrivateMethod(classBody, privateMethod, true, false);
return; return;
@ -1391,8 +1436,8 @@ export default class StatementParser extends ExpressionParser {
} }
const containsEsc = this.state.containsEsc; const containsEsc = this.state.containsEsc;
const isPrivate = this.match(tt.privateName);
const key = this.parseClassElementName(member); const key = this.parseClassElementName(member);
const isPrivate = this.isPrivateName(key);
// Check the key is not a computed expression or string literal. // Check the key is not a computed expression or string literal.
const isSimple = key.type === "Identifier"; const isSimple = key.type === "Identifier";
const maybeQuestionTokenStart = this.state.start; const maybeQuestionTokenStart = this.state.start;
@ -1453,10 +1498,11 @@ export default class StatementParser extends ExpressionParser {
method.kind = "method"; method.kind = "method";
// The so-called parsed name would have been "async": get the real name. // The so-called parsed name would have been "async": get the real name.
const isPrivate = this.match(tt.privateName);
this.parseClassElementName(method); this.parseClassElementName(method);
this.parsePostMemberNameModifiers(publicMember); this.parsePostMemberNameModifiers(publicMember);
if (this.isPrivateName(method.key)) { if (isPrivate) {
// private async method // private async method
this.pushClassPrivateMethod( this.pushClassPrivateMethod(
classBody, classBody,
@ -1488,9 +1534,10 @@ export default class StatementParser extends ExpressionParser {
// a getter or setter // a getter or setter
method.kind = key.name; method.kind = key.name;
// The so-called parsed name would have been "get/set": get the real name. // The so-called parsed name would have been "get/set": get the real name.
const isPrivate = this.match(tt.privateName);
this.parseClassElementName(publicMethod); this.parseClassElementName(publicMethod);
if (this.isPrivateName(method.key)) { if (isPrivate) {
// private getter/setter // private getter/setter
this.pushClassPrivateMethod(classBody, privateMethod, false, false); this.pushClassPrivateMethod(classBody, privateMethod, false, false);
} else { } else {
@ -1522,25 +1569,20 @@ export default class StatementParser extends ExpressionParser {
// https://tc39.es/proposal-class-fields/#prod-ClassElementName // https://tc39.es/proposal-class-fields/#prod-ClassElementName
parseClassElementName(member: N.ClassMember): N.Expression | N.Identifier { parseClassElementName(member: N.ClassMember): N.Expression | N.Identifier {
const key = this.parsePropertyName(member, /* isPrivateNameAllowed */ true); const { type, value, start } = this.state;
if ( if (
!member.computed && (type === tt.name || type === tt.string) &&
member.static && member.static &&
((key: $FlowSubtype<N.Identifier>).name === "prototype" || value === "prototype"
(key: $FlowSubtype<N.StringLiteral>).value === "prototype")
) { ) {
this.raise(key.start, Errors.StaticPrototype); this.raise(start, Errors.StaticPrototype);
} }
if ( if (type === tt.privateName && value === "constructor") {
this.isPrivateName(key) && this.raise(start, Errors.ConstructorClassPrivateField);
this.getPrivateNameSV(key) === "constructor"
) {
this.raise(key.start, Errors.ConstructorClassPrivateField);
} }
return key; return this.parsePropertyName(member, /* isPrivateNameAllowed */ true);
} }
parseClassStaticBlock( parseClassStaticBlock(

View File

@ -210,7 +210,7 @@ export default (superClass: Class<Parser>): Class<Parser> =>
this.match(tt.braceL) || this.match(tt.braceL) ||
this.match(tt.star) || this.match(tt.star) ||
this.match(tt.ellipsis) || this.match(tt.ellipsis) ||
this.match(tt.hash) || this.match(tt.privateName) ||
this.isLiteralPropertyName()) && this.isLiteralPropertyName()) &&
!this.hasPrecedingLineBreak() !this.hasPrecedingLineBreak()
); );

View File

@ -205,6 +205,21 @@ export default class Tokenizer extends ParserErrors {
return this.input.charCodeAt(this.nextTokenStart()); 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 // Toggle strict mode. Re-reads the next number or string to please
// pedantic tests (`"use strict"; 010;` should fail). // pedantic tests (`"use strict"; 010;` should fail).
@ -244,7 +259,7 @@ export default class Tokenizer extends ParserErrors {
if (override) { if (override) {
override(this); override(this);
} else { } 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 nextPos = this.state.pos + 1;
const next = this.input.charCodeAt(nextPos); const next = this.codePointAtPos(nextPos);
if (next >= charCodes.digit0 && next <= charCodes.digit9) { if (next >= charCodes.digit0 && next <= charCodes.digit9) {
throw this.raise(this.state.pos, Errors.UnexpectedDigitAfterHash); throw this.raise(this.state.pos, Errors.UnexpectedDigitAfterHash);
} }
@ -438,6 +453,9 @@ export default class Tokenizer extends ParserErrors {
this.finishToken(tt.bracketHashL); this.finishToken(tt.bracketHashL);
} }
this.state.pos += 2; this.state.pos += 2;
} else if (isIdentifierStart(next) || next === charCodes.backslash) {
++this.state.pos;
this.finishToken(tt.privateName, this.readWord1());
} else { } else {
this.finishOp(tt.hash, 1); this.finishOp(tt.hash, 1);
} }
@ -952,7 +970,7 @@ export default class Tokenizer extends ParserErrors {
while (this.state.pos < this.length) { while (this.state.pos < this.length) {
const char = this.input[this.state.pos]; 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 (VALID_REGEX_FLAGS.has(char)) {
if (mods.indexOf(char) > -1) { if (mods.indexOf(char) > -1) {
@ -1090,7 +1108,7 @@ export default class Tokenizer extends ParserErrors {
throw this.raise(start, Errors.InvalidDecimal); 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); throw this.raise(this.state.pos, Errors.NumberIdentifier);
} }
@ -1176,7 +1194,7 @@ export default class Tokenizer extends ParserErrors {
isDecimal = true; 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); throw this.raise(this.state.pos, Errors.NumberIdentifier);
} }
@ -1447,7 +1465,7 @@ export default class Tokenizer extends ParserErrors {
let chunkStart = this.state.pos; let chunkStart = this.state.pos;
while (this.state.pos < this.length) { while (this.state.pos < this.length) {
const ch = this.input.codePointAt(this.state.pos); const ch = this.codePointAtPos(this.state.pos);
if (isIdentifierChar(ch)) { if (isIdentifierChar(ch)) {
this.state.pos += ch <= 0xffff ? 1 : 2; this.state.pos += ch <= 0xffff ? 1 : 2;
} else if (this.state.isIterator && ch === charCodes.atSign) { } else if (this.state.isIterator && ch === charCodes.atSign) {

View File

@ -90,6 +90,7 @@ export const types: { [name: string]: TokenType } = {
regexp: new TokenType("regexp", { startsExpr }), regexp: new TokenType("regexp", { startsExpr }),
string: new TokenType("string", { startsExpr }), string: new TokenType("string", { startsExpr }),
name: new TokenType("name", { startsExpr }), name: new TokenType("name", { startsExpr }),
privateName: new TokenType("#name", { startsExpr }),
eof: new TokenType("eof"), eof: new TokenType("eof"),
// Punctuation token types. // Punctuation token types.

View File

@ -0,0 +1,3 @@
class C {
#p
}

View File

@ -0,0 +1,4 @@
{
"tokens": true,
"BABEL_8_BREAKING": false
}

View File

@ -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}}
}
]
}

View File

@ -0,0 +1,3 @@
class C {
#p
}

View File

@ -0,0 +1,4 @@
{
"tokens": true,
"BABEL_8_BREAKING": true
}

View File

@ -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}}
}
]
}

View File

@ -0,0 +1,3 @@
{
"throws": "Unexpected token (2:2)"
}

View File

@ -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": []
}
}

View File

@ -1,4 +1,4 @@
{ {
"throws": "Unexpected token (3:3)", "throws": "Unexpected token (3:2)",
"plugins": [] "plugins": []
} }

View File

@ -0,0 +1,3 @@
{
"throws": "Unexpected token (2:2)"
}

View File

@ -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": []
}
}

View File

@ -1,4 +1,4 @@
{ {
"throws": "Unexpected token (2:3)", "throws": "Unexpected token (2:2)",
"plugins": [] "plugins": []
} }

View File

@ -5,6 +5,15 @@ __metadata:
version: 4 version: 4
cacheKey: 7 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": "@babel-internal/runtime-integration-rollup@workspace:test/runtime-integration/rollup":
version: 0.0.0-use.local version: 0.0.0-use.local
resolution: "@babel-internal/runtime-integration-rollup@workspace:test/runtime-integration/rollup" resolution: "@babel-internal/runtime-integration-rollup@workspace:test/runtime-integration/rollup"
@ -935,22 +944,15 @@ __metadata:
languageName: unknown languageName: unknown
linkType: soft 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": "@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 version: 0.0.0-use.local
resolution: "@babel/parser@workspace:packages/babel-parser" resolution: "@babel/parser@workspace:packages/babel-parser"
dependencies: dependencies:
"@babel-baseline/parser": "npm:@babel/parser@^7.14.0"
"@babel/code-frame": "workspace:*" "@babel/code-frame": "workspace:*"
"@babel/helper-fixtures": "workspace:*" "@babel/helper-fixtures": "workspace:*"
"@babel/helper-validator-identifier": "workspace:*" "@babel/helper-validator-identifier": "workspace:*"
benchmark: ^2.1.4
charcodes: ^0.2.0 charcodes: ^0.2.0
bin: bin:
parser: ./bin/babel-parser.js parser: ./bin/babel-parser.js