Skip to content

Commit

Permalink
fix(parser): fixed class field edge cases
Browse files Browse the repository at this point in the history
  • Loading branch information
KFlash committed Jun 1, 2019
1 parent 281ad30 commit de0d0b5
Show file tree
Hide file tree
Showing 5 changed files with 872 additions and 104 deletions.
56 changes: 43 additions & 13 deletions src/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2003,7 +2003,7 @@ function parseImportCallDeclaration(parser: ParserState, context: Context, start
function parseExportDeclaration(
parser: ParserState,
context: Context,
_start: number
start: number
): ESTree.ExportAllDeclaration | ESTree.ExportNamedDeclaration | ESTree.ExportDefaultDeclaration {
// ExportDeclaration:
// 'export' '*' 'from' ModuleSpecifier ';'
Expand All @@ -2013,8 +2013,6 @@ function parseExportDeclaration(
// 'export' Declaration
// 'export' 'default'

const { tokenIndex } = parser;

// https://tc39.github.io/ecma262/#sec-exports
nextToken(parser, context | Context.AllowRegExp);

Expand Down Expand Up @@ -2075,37 +2073,49 @@ function parseExportDeclaration(
consumeSemicolon(parser, context | Context.AllowRegExp);
}

return finishNode(parser, context, tokenIndex, {
return finishNode(parser, context, start, {
type: 'ExportDefaultDeclaration',
declaration
});
}

switch (parser.token) {
case Token.Multiply: {
//
// 'export' '*' 'as' IdentifierName 'from' ModuleSpecifier ';'
//
// See: https://github.com/tc39/ecma262/pull/1174

let ecma262PR: 0 | 1 = 0;

nextToken(parser, context); // Skips: '*'

if (context & Context.OptionsNext && consumeOpt(parser, context, Token.AsKeyword)) {
ecma262PR = 1;

specifiers.push(
finishNode(parser, context, parser.index, {
type: 'ExportNamespaceSpecifier',
specifier: parseIdentifier(parser, context, tokenIndex)
specifier: parseIdentifier(parser, context, start)
} as any)
);
}

consume(parser, context, Token.FromKeyword);

if (parser.token !== Token.StringLiteral) report(parser, Errors.InvalidExportImportSource, 'Export');

source = parseLiteral(parser, context, parser.tokenIndex);

consumeSemicolon(parser, context | Context.AllowRegExp);

return ecma262PR
? finishNode(parser, context, tokenIndex, {
? finishNode(parser, context, start, {
type: 'ExportNamedDeclaration',
source,
specifiers
} as any)
: finishNode(parser, context, tokenIndex, {
: finishNode(parser, context, start, {
type: 'ExportAllDeclaration',
source
} as any);
Expand All @@ -2123,11 +2133,15 @@ function parseExportDeclaration(
// ExportSpecifier :
// IdentifierName
// IdentifierName 'as' IdentifierName

nextToken(parser, context); // Skips: '{'

while (parser.token & Token.IsIdentifier) {
const { tokenIndex } = parser;
const local = parseIdentifier(parser, context, tokenIndex);

let exported: ESTree.Identifier | null;

if (parser.token === Token.AsKeyword) {
nextToken(parser, context);
exported = parseIdentifier(parser, context, parser.tokenIndex);
Expand All @@ -2152,6 +2166,7 @@ function parseExportDeclaration(
// The left hand side can't be a keyword where there is no
// 'from' keyword since it references a local binding.
if (parser.token !== Token.StringLiteral) report(parser, Errors.InvalidExportImportSource, 'Export');

source = parseLiteral(parser, context, parser.tokenIndex);
}

Expand Down Expand Up @@ -2183,7 +2198,9 @@ function parseExportDeclaration(
break;
case Token.AsyncKeyword:
const idxAfterAsync = parser.tokenIndex;

nextToken(parser, context);

if ((parser.flags & Flags.NewLine) === 0 && parser.token === Token.FunctionKeyword) {
declaration = parseFunctionDeclaration(parser, context, 1, 0, 1, idxAfterAsync);
break;
Expand All @@ -2193,7 +2210,7 @@ function parseExportDeclaration(
report(parser, Errors.UnexpectedToken, KeywordDescTable[parser.token & Token.Type]);
}

return finishNode(parser, context, tokenIndex, {
return finishNode(parser, context, start, {
type: 'ExportNamedDeclaration',
source,
specifiers,
Expand Down Expand Up @@ -5854,6 +5871,7 @@ function parseClassElementList(
kind |= PropertyKind.Computed;
key = parseComputedPropertyName(parser, context);
} else if (context & Context.OptionsNext && parser.token === Token.PrivateField) {
kind |= PropertyKind.PrivateField;
key = parsePrivateName(parser, context, tokenIndex);
} else if (parser.token === Token.RightBrace) {
report(parser, Errors.NoIdentOrDynamicName);
Expand Down Expand Up @@ -5889,14 +5907,13 @@ function parseClassElementList(
}

if (
(kind & PropertyKind.Computed) === 0 &&
(kind & (PropertyKind.PrivateField | PropertyKind.Computed)) === 0 &&
kind & (PropertyKind.Static | PropertyKind.Generator | PropertyKind.Async | PropertyKind.GetSet) &&
parser.tokenValue === 'prototype'
) {
report(parser, Errors.StaticPrototype);
}

// Note: This is temporary until this reach Stage 4
if (context & Context.OptionsNext && parser.token !== Token.LeftParen) {
if (parser.tokenValue === 'constructor') report(parser, Errors.InvalidClassFieldConstructor);
return parseFieldDefinition(parser, context, key, kind, decorators, tokenIndex);
Expand Down Expand Up @@ -5982,13 +5999,26 @@ export function parseFieldDefinition(
// FieldDefinition ;
// ;
let value: ESTree.Expression | null = null;

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, parser.tokenIndex);

const idxAfterAssign = parser.tokenIndex;

if (parser.token === Token.Arguments) report(parser, Errors.StrictEvalArguments);

value = parsePrimaryExpressionExtended(parser, context | Context.InClass, BindingType.None, 0, 1, idxAfterAssign);

if ((parser.token & Token.IsClassField) !== Token.IsClassField) {
value = parseMemberOrUpdateExpression(parser, context, value as any, 0, 0, idxAfterAssign);
if ((parser.token & Token.IsClassField) !== Token.IsClassField) {
value = parseAssignmentExpression(parser, context, idxAfterAssign, value as any);
}
}
}

return finishNode(parser, context, start, {
type: 'FieldDefinition',
key,
Expand Down
1 change: 1 addition & 0 deletions test/parser/miscellaneous/pass.ts
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ describe('Miscellaneous - Pass', () => {
`([{x = y}] = z)`,
`[{x = y}] = z`,
`new await()()`,
'var s = 0; for (let key in a) { s += a[key] };',
'`a${b=c}d`',
`(x) = (y) += z`,
`(x) = (y) = z`,
Expand Down
36 changes: 36 additions & 0 deletions test/parser/module/export.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,31 @@ describe('Module - Export', () => {
});
}

// Namespace export parsing
for (const arg of [
"export * as arguments from 'bar'",
"export * as await from 'bar'",
"export * as default from 'bar'",
"export * as enum from 'bar'",
"export * as foo from 'bar'",
"export * as for from 'bar'",
"export * as let from 'bar'",
"export * as static from 'bar'",
"export * as yield from 'bar'"
]) {
it(`${arg}`, () => {
t.doesNotThrow(() => {
parseSource(`${arg}`, undefined, Context.Strict | Context.Module | Context.OptionsNext);
});
});

it(`${arg}`, () => {
t.throws(() => {
parseSource(`${arg}`, undefined, Context.Strict | Context.Module);
});
});
}

// Async await module
for (const arg of [
'export default async function() { await 1; }',
Expand Down Expand Up @@ -90,6 +115,8 @@ describe('Module - Export', () => {
'export *;',
'export * from;',
'export { Q } from;',
'export { 123 } from;',
'export { # } from;',
"export default from 'module.js';",
'export * as z from "c";',
"export * as arguments from 'bar'",
Expand Down Expand Up @@ -220,6 +247,15 @@ describe('Module - Export', () => {
["export * as static from 'bar'", Context.Strict | Context.Module],
["export * as yield from 'bar'", Context.Strict | Context.Module],
['export {', Context.Strict | Context.Module],
['export *;', Context.Strict | Context.Module],
['export * as;', Context.Strict | Context.Module],
['export * as foo;', Context.Strict | Context.Module],
['export * as foo from;', Context.Strict | Context.Module],
["export * as foo from ';", Context.Strict | Context.Module],
["export * as ,foo from 'bar'", Context.Strict | Context.Module],
['export * as foo from;', Context.Strict | Context.Module | Context.OptionsNext],
["export * as foo from ';", Context.Strict | Context.Module | Context.OptionsNext],
["export * as ,foo from 'bar'", Context.Strict | Context.Module | Context.OptionsNext],
['var a; export { a', Context.Strict | Context.Module],
['var a; export { a,', Context.Strict | Context.Module],
['var a; export { a, ;', Context.Strict | Context.Module],
Expand Down
Loading

0 comments on commit de0d0b5

Please sign in to comment.