Skip to content

Commit

Permalink
fix(parser): dedupe class field parsing
Browse files Browse the repository at this point in the history
  • Loading branch information
KFlash committed May 12, 2019
1 parent b659e5b commit 4c61090
Show file tree
Hide file tree
Showing 5 changed files with 275 additions and 157 deletions.
25 changes: 23 additions & 2 deletions src/estree.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ export interface T_Node extends T_Statement, T_Expression, T_Pattern, T_ModuleDe
SpreadElement: SpreadElement;
TemplateElement: TemplateElement;
ClassBody: ClassBody;
FieldDefinition: FieldDefinition;
PrivateName: PrivateName;
Decorator: Decorator;
MethodDefinition: MethodDefinition;
VariableDeclarator: VariableDeclarator;
Expand Down Expand Up @@ -52,6 +54,8 @@ export type Node =
| SpreadElement
| TemplateElement
| ClassBody
| FieldDefinition
| PrivateName
| Decorator
| MethodDefinition
| ModuleDeclaration
Expand Down Expand Up @@ -157,6 +161,7 @@ export type Expression =
| AssignmentExpression
| LogicalExpression
| MemberExpression
| PrivateName
| ConditionalExpression
| CallExpression
| NewExpression
Expand Down Expand Up @@ -350,7 +355,20 @@ export interface CatchClause extends _Node<'CatchClause'> {
}

export interface ClassBody extends _Node<'ClassBody'> {
body: MethodDefinition[];
body: (FieldDefinition | MethodDefinition)[];
}

export interface PrivateMemberExpression extends _Node<'MemberExpression'> {
object: Expression;
property: PrivateName;
}

export interface FieldDefinition extends _Node<'FieldDefinition'> {
key: PrivateName | Expression;
value: any;
decorators?: Decorator[] | null;
computed: boolean;
static: boolean;
}

export interface ClassDeclaration extends _Declaration<'ClassDeclaration'> {
Expand Down Expand Up @@ -494,6 +512,9 @@ export interface Decorator extends _Node<'Decorator'> {
expression: Expression;
}

export interface PrivateName extends _Node<'PrivateName'> {
name: string;
}
export interface LogicalExpression extends _Expression<'LogicalExpression'> {
operator: LogicalOperator;
left: Expression;
Expand All @@ -506,7 +527,7 @@ export interface MetaProperty extends _Expression<'MetaProperty'> {
}

export interface MethodDefinition extends _Node<'MethodDefinition'> {
key: Expression | null;
key: Expression | PrivateName;
value: FunctionExpression | null;
kind: 'constructor' | 'method' | 'get' | 'set';
computed: boolean;
Expand Down
116 changes: 34 additions & 82 deletions src/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2134,6 +2134,7 @@ export function parseUnaryExpression(parser: ParserState, context: Context): EST
if (context & Context.Strict && unaryOperator === Token.DeleteKeyword) {
if (arg.type === 'Identifier') {
report(parser, Errors.StrictDelete);
// Prohibit delete of private class elements
} else if (isPropertyWithPrivateFieldKey(arg)) {
report(parser, Errors.DeletePrivateField);
}
Expand Down Expand Up @@ -5066,7 +5067,7 @@ export function parseClassBody(

parser.flags = (parser.flags | Flags.HasConstructor) ^ Flags.HasConstructor;

const body: ESTree.MethodDefinition[] = [];
const body: (ESTree.MethodDefinition | ESTree.FieldDefinition)[] = [];

while (parser.token !== Token.RightBrace) {
if (context & Context.OptionsNext) {
Expand All @@ -5080,12 +5081,6 @@ export function parseClassBody(
if (consumeOpt(parser, context, Token.Semicolon)) {
if (decorators.length > 0) report(parser, Errors.InvalidDecoratorSemicolon);
}

if (parser.token === Token.PrivateField) {
body.push(parsePrivateFieldsOrMethod(parser, context, decorators, PropertyKind.None));
consumeOpt(parser, context, Token.Semicolon);
continue;
}
}

if (consumeOpt(parser, context, Token.Semicolon)) continue;
Expand Down Expand Up @@ -5115,7 +5110,7 @@ function parseClassElementList(
type: BindingType,
decorators: ESTree.Decorator[],
isStatic: 0 | 1
): ESTree.MethodDefinition {
): ESTree.MethodDefinition | ESTree.FieldDefinition {
let kind: PropertyKind = isStatic ? PropertyKind.Static : PropertyKind.None;
let key: ESTree.Expression | null = null;

Expand All @@ -5137,7 +5132,6 @@ function parseClassElementList(
if (context & Context.OptionsNext && (parser.token & Token.IsClassField) === Token.IsClassField) {
return parseFieldDefinition(parser, context, key, kind, decorators);
}
if (parser.token === Token.LeftParen) report(parser, Errors.Unexpected);
}
break;

Expand Down Expand Up @@ -5169,20 +5163,16 @@ function parseClassElementList(
kind |= PropertyKind.Generator;
nextToken(parser, context);
} else if (context & Context.OptionsNext && parser.token === Token.PrivateField) {
return parsePrivateFieldsOrMethod(parser, context, decorators, kind);
kind |= PropertyKind.PrivatField;
} else if (context & Context.OptionsNext && parser.token === Token.RightBrace) {
return parseFieldDefinition(parser, context, key, kind, decorators);
} else {
report(parser, Errors.UnexpectedToken, KeywordDescTable[parser.token & Token.Type]);
}

if (kind & PropertyKind.Computed) {
key = parseComputedPropertyName(parser, context);
} else if (kind & (PropertyKind.Generator | PropertyKind.Async | PropertyKind.Getter | PropertyKind.Setter)) {
if (kind & (PropertyKind.Generator | PropertyKind.Async | PropertyKind.GetSet)) {
if (kind & PropertyKind.Generator) {
if (context & Context.OptionsNext && parser.token === Token.PrivateField) {
return parsePrivateFieldsOrMethod(parser, context, decorators, kind);
} else if (parser.token === Token.LeftParen) report(parser, Errors.Unexpected);
if (parser.token === Token.LeftParen) report(parser, Errors.Unexpected);
}
if (parser.token & Token.IsIdentifier) {
key = parseIdentifier(parser, context);
Expand All @@ -5191,15 +5181,18 @@ function parseClassElementList(
} else if (parser.token === Token.LeftBracket) {
kind |= PropertyKind.Computed;
key = parseComputedPropertyName(parser, context);
} else if (parser.token === Token.PrivateField) {
return parsePrivateFieldsOrMethod(parser, context, decorators, kind);
} else if (parser.token === Token.Assign) {
return parseFieldDefinition(parser, context, key, kind, decorators);
} else if (context & Context.OptionsNext && parser.token === Token.PrivateField) {
key = parsePrivateName(parser, context);
} else if ((context & Context.OptionsNext) === 0 && parser.token === Token.RightBrace) {
report(parser, Errors.NoIdentOrDynamicName);
}
} else if (kind & PropertyKind.Computed) {
key = parseComputedPropertyName(parser, context);
} else if (kind & PropertyKind.PrivatField) {
key = parsePrivateName(parser, context);
context = context | Context.InClass;
if (parser.token !== Token.LeftParen) return parseFieldDefinition(parser, context, key, kind, decorators);
}

if (parser.tokenValue === 'constructor') {
if ((kind & PropertyKind.Static) === 0) {
if (
Expand Down Expand Up @@ -5266,14 +5259,15 @@ function parseClassElementList(

/**
* Parses private name
*
* @param parser Parser object
* @param context Context masks
*/
function parsePrivateName(parser: ParserState, context: Context): any {
function parsePrivateName(parser: ParserState, context: Context): ESTree.PrivateName {
// PrivateName::
// #IdentifierName
nextToken(parser, context); // '#'
const name = parser.tokenValue;
nextToken(parser, context); // skip: '#'
const { tokenValue: name } = parser;
if (name === 'constructor') report(parser, Errors.UnexpectedStrictReserved);
nextToken(parser, context);

Expand All @@ -5283,78 +5277,36 @@ function parsePrivateName(parser: ParserState, context: Context): any {
};
}

export function parseFieldDefinition(
parser: ParserState,
context: Context,
key: any,
state: PropertyKind,
decorators: ESTree.Decorator[] | null
): any {
let value: ESTree.Expression | null = null;
if (state & PropertyKind.Generator) report(parser, Errors.Unexpected);
// if (parser.tokenValue === 'prototype') report(parser, Errors.Unexpected); // static ptype
if (parser.token === Token.Assign) {
nextToken(parser, context | Context.AllowRegExp);
if ((parser.token & Token.IsEvalOrArguments) === Token.IsEvalOrArguments)
report(parser, Errors.StrictEvalArguments);
value = parseExpression(parser, context | Context.InClass, /* assignable */ 1);
}
consumeOpt(parser, context, Token.Semicolon);
consumeOpt(parser, context, Token.Comma);

return {
type: 'FieldDefinition',
key,
value,
static: (state & PropertyKind.Static) > 0,
computed: (state & PropertyKind.Computed) > 0,
decorators
};
}

/**
* Parses private fields or method definition
* Parses field definition
*
* @param parser Parser object
* @param context Context masks
* @param decorators
* @param key ESTree AST node
* @param state
* @param decorators
*/
function parsePrivateFieldsOrMethod(

export function parseFieldDefinition(
parser: ParserState,
context: Context,
decorators: ESTree.Decorator[],
state: PropertyKind
): any {
key: ESTree.PrivateName | ESTree.Expression | null,
state: PropertyKind,
decorators: ESTree.Decorator[] | null
): ESTree.FieldDefinition {
// FieldDefinition[?Yield, ?Await];
// static FieldDefinition[?Yield, ?Await];
let value: ESTree.Expression | null = null;

const key = parsePrivateName(parser, context);

if (parser.token === Token.LeftParen) {
return {
type: 'MethodDefinition',
kind:
(state & PropertyKind.Static) === 0 && state & PropertyKind.Constructor
? 'constructor'
: state & PropertyKind.Getter
? 'get'
: state & PropertyKind.Setter
? 'set'
: 'method',
static: (state & PropertyKind.Static) > 0,
computed: (state & PropertyKind.Computed) > 0,
key,
value: parseMethodDefinition(parser, context | Context.InClass, state)
};
}

if (state & PropertyKind.Static && parser.tokenValue === 'constructor') report(parser, Errors.Unexpected);
if (state & PropertyKind.Generator) report(parser, Errors.Unexpected);
if (parser.token === Token.Assign) {
nextToken(parser, context | Context.AllowRegExp);
if ((parser.token & Token.IsEvalOrArguments) === Token.IsEvalOrArguments)
report(parser, Errors.StrictEvalArguments);
value = parseExpression(parser, context | Context.InClass, /* assignable */ 1);
}

consumeOpt(parser, context, Token.Comma);
consumeOpt(parser, context, Token.Semicolon);

return {
type: 'FieldDefinition',
Expand All @@ -5363,7 +5315,7 @@ function parsePrivateFieldsOrMethod(
static: (state & PropertyKind.Static) > 0,
computed: (state & PropertyKind.Computed) > 0,
decorators
};
} as any;
}

/**
Expand Down
7 changes: 6 additions & 1 deletion test/parser/next/decorators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ describe('Next - Decorators', () => {

pass('Next - Decorators (pass)', [
[
`class A { @dec name = 0 }`,
`class A { @dec name = 0; }`,
Context.OptionsNext,
{
body: [
Expand Down Expand Up @@ -1330,6 +1330,7 @@ describe('Next - Decorators', () => {
kind: 'get',
static: false,
computed: false,
decorators: [],
key: {
type: 'PrivateName',
name: 'get'
Expand All @@ -1351,6 +1352,7 @@ describe('Next - Decorators', () => {
kind: 'set',
static: false,
computed: false,
decorators: [],
key: {
type: 'PrivateName',
name: 'set'
Expand All @@ -1377,6 +1379,7 @@ describe('Next - Decorators', () => {
kind: 'get',
static: false,
computed: false,
decorators: [],
key: {
type: 'PrivateName',
name: 'getset'
Expand All @@ -1398,6 +1401,7 @@ describe('Next - Decorators', () => {
kind: 'set',
static: false,
computed: false,
decorators: [],
key: {
type: 'PrivateName',
name: 'getset'
Expand Down Expand Up @@ -1556,6 +1560,7 @@ describe('Next - Decorators', () => {
{
type: 'MethodDefinition',
kind: 'method',
decorators: [],
static: true,
computed: false,
key: {
Expand Down
Loading

0 comments on commit 4c61090

Please sign in to comment.