Skip to content

Commit

Permalink
Refactor bindingProperty parsing (#13929)
Browse files Browse the repository at this point in the history
* refactor: inline parseMaybePrivateName

* correct test case

* perf: fast exit in checkExpressionErrors

* refactor: add parseBindingProperty

* fix: private property with variance

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

Co-authored-by: Nicol貌 Ribaudo <nicolo.ribaudo@gmail.com>

* chore: update testcase

* refactor: remove refExpressionErrors for record/tuple

They are always non-ambiguous.

Co-authored-by: Nicol貌 Ribaudo <nicolo.ribaudo@gmail.com>
  • Loading branch information
JLHwung and nicolo-ribaudo committed Nov 12, 2021
1 parent 135ab83 commit 54c539e
Show file tree
Hide file tree
Showing 15 changed files with 322 additions and 108 deletions.
111 changes: 53 additions & 58 deletions packages/babel-parser/src/parser/expression.js
Expand Up @@ -124,7 +124,7 @@ export default class ExpressionParser extends LValParser {

checkProto(
prop: N.ObjectMember | N.SpreadElement,
isRecord: boolean,
isRecord: ?boolean,
protoRef: { used: boolean },
refExpressionErrors: ?ExpressionErrors,
): void {
Expand Down Expand Up @@ -402,7 +402,7 @@ export default class ExpressionParser extends LValParser {
minPrec: number,
): N.Expression {
if (this.isPrivateName(left)) {
// https://tc39.es/proposal-private-fields-in-in
// https://tc39.es/ecma262/#prod-RelationalExpression
// RelationalExpression [In, Yield, Await]
// [+In] PrivateIdentifier in ShiftExpression[?Yield, ?Await]

Expand Down Expand Up @@ -1120,7 +1120,6 @@ export default class ExpressionParser extends LValParser {
this.state.type === tt.bracketBarL ? tt.bracketBarR : tt.bracketR,
/* canBePattern */ false,
/* isTuple */ true,
refExpressionErrors,
);
}
case tt.bracketL: {
Expand All @@ -1137,7 +1136,6 @@ export default class ExpressionParser extends LValParser {
this.state.type === tt.braceBarL ? tt.braceBarR : tt.braceR,
/* isPattern */ false,
/* isRecord */ true,
refExpressionErrors,
);
}
case tt.braceL: {
Expand Down Expand Up @@ -1513,21 +1511,6 @@ export default class ExpressionParser extends LValParser {
return this.finishNode(node, "Super");
}

parseMaybePrivateName(
isPrivateNameAllowed: boolean,
): N.PrivateName | N.Identifier {
const isPrivate = this.match(tt.privateName);

if (isPrivate) {
if (!isPrivateNameAllowed) {
this.raise(this.state.start + 1, Errors.UnexpectedPrivateField);
}
return this.parsePrivateName();
} else {
return this.parseIdentifier(true);
}
}

parsePrivateName(): N.PrivateName {
const node = this.startNode();
const id = this.startNodeAt(
Expand Down Expand Up @@ -1925,9 +1908,11 @@ export default class ExpressionParser extends LValParser {
}
}

const prop = this.parsePropertyDefinition(isPattern, refExpressionErrors);
if (!isPattern) {
// $FlowIgnore RestElement will never be returned if !isPattern
let prop;
if (isPattern) {
prop = this.parseBindingProperty();
} else {
prop = this.parsePropertyDefinition(refExpressionErrors);
this.checkProto(prop, isRecord, propHash, refExpressionErrors);
}

Expand Down Expand Up @@ -1974,9 +1959,8 @@ export default class ExpressionParser extends LValParser {

// https://tc39.es/ecma262/#prod-PropertyDefinition
parsePropertyDefinition(
isPattern: boolean,
refExpressionErrors?: ?ExpressionErrors,
): N.ObjectMember | N.SpreadElement | N.RestElement {
): N.ObjectMember | N.SpreadElement {
let decorators = [];
if (this.match(tt.at)) {
if (this.hasPlugin("decorators")) {
Expand All @@ -1991,22 +1975,13 @@ export default class ExpressionParser extends LValParser {
}

const prop = this.startNode();
let isGenerator = false;
let isAsync = false;
let isAccessor = false;
let startPos;
let startLoc;

if (this.match(tt.ellipsis)) {
if (decorators.length) this.unexpected();
if (isPattern) {
this.next();
// Don't use parseRestBinding() as we only allow Identifier here.
prop.argument = this.parseIdentifier();
this.checkCommaAfterRest(charCodes.rightCurlyBrace);
return this.finishNode(prop, "RestElement");
}

return this.parseSpread();
}

Expand All @@ -2017,32 +1992,25 @@ export default class ExpressionParser extends LValParser {

prop.method = false;

if (isPattern || refExpressionErrors) {
if (refExpressionErrors) {
startPos = this.state.start;
startLoc = this.state.startLoc;
}

if (!isPattern) {
isGenerator = this.eat(tt.star);
}

let isGenerator = this.eat(tt.star);
this.parsePropertyNamePrefixOperator(prop);
const containsEsc = this.state.containsEsc;
const key = this.parsePropertyName(prop, /* isPrivateNameAllowed */ false);
const key = this.parsePropertyName(prop);

if (
!isPattern &&
!isGenerator &&
!containsEsc &&
this.maybeAsyncOrAccessorProp(prop)
) {
if (!isGenerator && !containsEsc && this.maybeAsyncOrAccessorProp(prop)) {
const keyName = key.name;
// https://tc39.es/ecma262/#prod-AsyncMethod
// https://tc39.es/ecma262/#prod-AsyncGeneratorMethod
if (keyName === "async" && !this.hasPrecedingLineBreak()) {
isAsync = true;
this.resetPreviousNodeTrailingComments(key);
isGenerator = this.eat(tt.star);
this.parsePropertyName(prop, /* isPrivateNameAllowed */ false);
this.parsePropertyName(prop);
}
// get PropertyName[?Yield, ?Await] () { FunctionBody[~Yield, ~Await] }
// set PropertyName[?Yield, ?Await] ( PropertySetParameterList ) { FunctionBody[~Yield, ~Await] }
Expand All @@ -2055,7 +2023,7 @@ export default class ExpressionParser extends LValParser {
this.raise(this.state.pos, Errors.AccessorIsGenerator, keyName);
this.next();
}
this.parsePropertyName(prop, /* isPrivateNameAllowed */ false);
this.parsePropertyName(prop);
}
}

Expand All @@ -2065,7 +2033,7 @@ export default class ExpressionParser extends LValParser {
startLoc,
isGenerator,
isAsync,
isPattern,
false /* isPattern */,
isAccessor,
refExpressionErrors,
);
Expand Down Expand Up @@ -2231,23 +2199,44 @@ export default class ExpressionParser extends LValParser {

parsePropertyName(
prop: N.ObjectOrClassMember | N.ClassMember | N.TsNamedTypeElementBase,
isPrivateNameAllowed: boolean,
): N.Expression | N.Identifier {
if (this.eat(tt.bracketL)) {
(prop: $FlowSubtype<N.ObjectOrClassMember>).computed = true;
prop.key = this.parseMaybeAssignAllowIn();
this.expect(tt.bracketR);
} else {
// 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 =
type === tt.num ||
type === tt.string ||
type === tt.bigint ||
type === tt.decimal
? this.parseExprAtom()
: this.parseMaybePrivateName(isPrivateNameAllowed);

const { type, value } = this.state;
let key;
// most un-computed property names are identifiers
if (tokenIsKeywordOrIdentifier(type)) {
key = this.parseIdentifier(true);
} else {
switch (type) {
case tt.num:
key = this.parseNumericLiteral(value);
break;
case tt.string:
key = this.parseStringLiteral(value);
break;
case tt.bigint:
key = this.parseBigIntLiteral(value);
break;
case tt.decimal:
key = this.parseDecimalLiteral(value);
break;
case tt.privateName: {
// the class private key has been handled in parseClassElementName
const privateKeyPos = this.state.start + 1;
this.raise(privateKeyPos, Errors.UnexpectedPrivateField);
key = this.parsePrivateName();
break;
}
default:
throw this.unexpected();
}
}
(prop: $FlowFixMe).key = key;
if (type !== tt.privateName) {
// ClassPrivateProperty is never computed, so we don't assign in that case.
prop.computed = false;
Expand Down Expand Up @@ -2974,4 +2963,10 @@ export default class ExpressionParser extends LValParser {
this.eat(tt.braceR);
return this.finishNode<N.ModuleExpression>(node, "ModuleExpression");
}

// Used in Flow plugin
parsePropertyNamePrefixOperator(
// eslint-disable-next-line no-unused-vars
prop: N.ObjectOrClassMember | N.ClassMember,
): void {}
}
49 changes: 49 additions & 0 deletions packages/babel-parser/src/parser/lval.js
Expand Up @@ -11,6 +11,10 @@ import type {
Pattern,
RestElement,
SpreadElement,
/*:: ObjectOrClassMember, */
/*:: ClassMember, */
/*:: ObjectMember, */
/*:: TsNamedTypeElementBase, */
/*:: Identifier, */
/*:: ObjectExpression, */
/*:: ObjectPattern, */
Expand Down Expand Up @@ -46,6 +50,19 @@ export default class LValParser extends NodeUtils {
isRecord?: ?boolean,
refExpressionErrors?: ?ExpressionErrors,
) => T;
+parseObjPropValue: (
prop: any,
startPos: ?number,
startLoc: ?Position,
isGenerator: boolean,
isAsync: boolean,
isPattern: boolean,
isAccessor: boolean,
refExpressionErrors?: ?ExpressionErrors,
) => void;
+parsePropertyName: (
prop: ObjectOrClassMember | ClassMember | TsNamedTypeElementBase,
) => Expression | Identifier;
*/
// Forward-declaration: defined in statement.js
/*::
Expand Down Expand Up @@ -386,6 +403,38 @@ export default class LValParser extends NodeUtils {
return elts;
}

// https://tc39.es/ecma262/#prod-BindingRestProperty
parseBindingRestProperty(prop: RestElement): RestElement {
this.next(); // eat '...'
// Don't use parseRestBinding() as we only allow Identifier here.
prop.argument = this.parseIdentifier();
this.checkCommaAfterRest(charCodes.rightCurlyBrace);
return this.finishNode(prop, "RestElement");
}

// https://tc39.es/ecma262/#prod-BindingProperty
parseBindingProperty(): ObjectMember | RestElement {
const prop = this.startNode();
const { type, start: startPos, startLoc } = this.state;
if (type === tt.ellipsis) {
return this.parseBindingRestProperty(prop);
} else {
this.parsePropertyName(prop);
}
prop.method = false;
this.parseObjPropValue(
prop,
startPos,
startLoc,
false /* isGenerator */,
false /* isAsync */,
true /* isPattern */,
false /* isAccessor */,
);

return prop;
}

parseAssignableListItem(
allowModifiers: ?boolean,
decorators: Decorator[],
Expand Down
20 changes: 13 additions & 7 deletions packages/babel-parser/src/parser/statement.js
Expand Up @@ -1437,6 +1437,7 @@ export default class StatementParser extends ExpressionParser {
const publicMember: typeof publicMethod | typeof publicProp = publicMethod;
member.static = isStatic;
this.parsePropertyNamePrefixOperator(member);
if (this.eat(tt.star)) {
// a generator
Expand Down Expand Up @@ -1597,7 +1598,7 @@ export default class StatementParser extends ExpressionParser {
}
}

// https://tc39.es/proposal-class-fields/#prod-ClassElementName
// https://tc39.es/ecma262/#prod-ClassElementName
parseClassElementName(member: N.ClassMember): N.Expression | N.Identifier {
const { type, value, start } = this.state;
if (
Expand All @@ -1608,11 +1609,16 @@ export default class StatementParser extends ExpressionParser {
this.raise(start, Errors.StaticPrototype);
}

if (type === tt.privateName && value === "constructor") {
this.raise(start, Errors.ConstructorClassPrivateField);
if (type === tt.privateName) {
if (value === "constructor") {
this.raise(start, Errors.ConstructorClassPrivateField);
}
const key = this.parsePrivateName();
member.key = key;
return key;
}

return this.parsePropertyName(member, /* isPrivateNameAllowed */ true);
return this.parsePropertyName(member);
}

parseClassStaticBlock(
Expand Down Expand Up @@ -1733,7 +1739,7 @@ export default class StatementParser extends ExpressionParser {
methodOrProp: N.ClassMethod | N.ClassProperty,
): void {}

// https://tc39.es/proposal-class-fields/#prod-FieldDefinition
// https://tc39.es/ecma262/#prod-FieldDefinition
parseClassPrivateProperty(
node: N.ClassPrivateProperty,
): N.ClassPrivateProperty {
Expand All @@ -1742,14 +1748,14 @@ export default class StatementParser extends ExpressionParser {
return this.finishNode(node, "ClassPrivateProperty");
}

// https://tc39.es/proposal-class-fields/#prod-FieldDefinition
// https://tc39.es/ecma262/#prod-FieldDefinition
parseClassProperty(node: N.ClassProperty): N.ClassProperty {
this.parseInitializer(node);
this.semicolon();
return this.finishNode(node, "ClassProperty");
}

// https://tc39.es/proposal-class-fields/#prod-Initializer
// https://tc39.es/ecma262/#prod-Initializer
parseInitializer(node: N.ClassProperty | N.ClassPrivateProperty): void {
this.scope.enter(SCOPE_CLASS | SCOPE_SUPER);
this.expressionScope.enter(newExpressionScope());
Expand Down

0 comments on commit 54c539e

Please sign in to comment.