Skip to content

Commit

Permalink
fix(parser): fixed a few non-throwing edge cases
Browse files Browse the repository at this point in the history
  • Loading branch information
KFlash committed May 10, 2019
1 parent b7510cd commit c9e08cd
Show file tree
Hide file tree
Showing 11 changed files with 295 additions and 91 deletions.
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Binary file modified scripts/logo.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion src/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ export const errorMessages: {
[Errors.IllegalArrowFunctionParams]: 'Illegal arrow function parameter list',
[Errors.InvalidArrowDestructLHS]: 'The left-hand side of the arrow can only be destructed through assignment',
[Errors.InvalidBindingDestruct]: 'The binding declaration is not destructible',
[Errors.InvalidAsyncArrow]: 'Invalid async arrow',
[Errors.InvalidAsyncArrow]: 'Async arrow can not be followed by new expression',
[Errors.StaticPrototype]: "Classes may not have a static property named 'prototype'",
[Errors.InvalidConstructor]: 'Class constructor may not be a %0',
[Errors.DuplicateConstructor]: 'Duplicate constructor method in class',
Expand Down
108 changes: 82 additions & 26 deletions src/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1991,7 +1991,7 @@ export function parseAssignmentExpression(
right: parseExpression(parser, context, /* assignable*/ 1)
};

parser.assignable = AssignmentKind.Assignable;
parser.assignable = AssignmentKind.NotAssignable;

return left;
}
Expand Down Expand Up @@ -3264,9 +3264,6 @@ function parseArrayOrObjectAssignmentPattern(
/* assignable */ 1
);

if (parser.flags & Flags.SeenAwait) destructible |= DestructuringKind.HasAWait;
if (parser.flags & Flags.SeenYield) destructible |= DestructuringKind.HasYield;

parser.destructible =
(destructible | DestructuringKind.SeenProto | DestructuringKind.Required) ^
(DestructuringKind.Required | DestructuringKind.SeenProto);
Expand Down Expand Up @@ -3698,12 +3695,32 @@ export function parseObjectLiteralOrPattern(
}
}
} else {
value = parseExpression(parser, context, /* aassignable */ 1);
value = parseLeftHandSideExpression(parser, context, /* assignable */ 1);

destructible |=
(parser.assignable & AssignmentKind.Assignable
parser.assignable & AssignmentKind.Assignable
? DestructuringKind.Assignable
: DestructuringKind.NotDestructible) | parser.assignable;
: DestructuringKind.NotDestructible;

if (parser.token === Token.Comma || parser.token === Token.RightBrace) {
if (parser.assignable & AssignmentKind.NotAssignable) destructible |= DestructuringKind.NotDestructible;
} else {
value = parseMemberOrUpdateExpression(parser, context, value, /* isNewExpression */ 0);

destructible =
parser.assignable & AssignmentKind.Assignable ? 0 : destructible | DestructuringKind.NotDestructible;

const { token } = parser;

if (parser.token !== Token.Comma && parser.token !== Token.RightBrace) {
value = parseAssignmentExpression(
parser,
(context | Context.DisallowInContext) ^ Context.DisallowInContext,
value
);
if (token !== Token.Assign) destructible |= DestructuringKind.NotDestructible;
}
}
}
} else if (parser.token === Token.LeftBracket) {
destructible |= DestructuringKind.NotDestructible;
Expand Down Expand Up @@ -3855,12 +3872,37 @@ export function parseObjectLiteralOrPattern(
}
}
} else {
value = parseExpression(parser, context, /* assignable */ 1);
value = parseLeftHandSideExpression(parser, context, /* assignable */ 1);

destructible |=
(parser.assignable & AssignmentKind.Assignable
parser.assignable & AssignmentKind.Assignable
? DestructuringKind.Assignable
: DestructuringKind.NotDestructible) | parser.assignable;
: DestructuringKind.NotDestructible;

if (parser.token === Token.Comma || parser.token === Token.RightBrace) {
if (parser.assignable & AssignmentKind.NotAssignable) {
destructible |= DestructuringKind.NotDestructible;
}
} else {
value = parseMemberOrUpdateExpression(parser, context, value, /* isNewExpression */ 0);
if (parser.assignable & AssignmentKind.Assignable) {
destructible = 0;
} else {
destructible |= DestructuringKind.NotDestructible;
}

let firstOpNotAssign = parser.token !== Token.Assign;
if (parser.token !== Token.Comma && parser.token !== Token.RightBrace) {
value = parseAssignmentExpression(
parser,
(context | Context.DisallowInContext) ^ Context.DisallowInContext,
value
);
if (firstOpNotAssign) {
destructible |= DestructuringKind.NotDestructible;
}
}
}
}
} else if (parser.token === Token.LeftParen) {
state |= Kind.Method;
Expand Down Expand Up @@ -4301,7 +4343,7 @@ export function parseParenthesizedExpression(parser: ParserState, context: Conte
if (parser.token === Token.Arrow) {
if (isComplex) parser.flags |= Flags.SimpleParameterList;
if (!assignable) report(parser, Errors.IllegalArrowFunctionParams);
if (destructible & DestructuringKind.NotDestructible) report(parser, Errors.InvalidLHSInAsyncArrow);
if (destructible & DestructuringKind.NotDestructible) report(parser, Errors.IllegalArrowFunctionParams);
if (destructible & DestructuringKind.Assignable) report(parser, Errors.InvalidArrowDestructLHS);
if (context & (Context.Module | Context.InAwaitContext) && parser.flags & Flags.SeenAwait)
report(parser, Errors.IllegalArrowFunctionParams);
Expand Down Expand Up @@ -4520,31 +4562,42 @@ export function parseNewExpression(
parser: ParserState,
context: Context
): ESTree.NewExpression | ESTree.Expression | ESTree.MetaProperty {
// NewExpression ::
// ('new')+ MemberExpression
//
// NewTarget ::
// 'new' '.' 'target'
//
// Examples of new expression:
// - new foo.bar().baz
// - new foo()()
// - new new foo()()
// - new new foo
// - new new foo()
// - new new foo().bar().baz
// - `new.target[await x]`
// - `new (foo);`
// - `new (foo)();`
// - `new foo()();`
// - `new (await foo);`
// - `new x(await foo);`
const id = parseIdentifier(parser, context | Context.AllowRegExp);
if (parser.token === Token.Period) {
nextToken(parser, context);

if (consumeOpt(parser, context, Token.Period)) {
if (context & Context.AllowNewTarget && parser.token === Token.Target) {
parser.assignable = AssignmentKind.NotAssignable;
return parseMetaProperty(parser, context, id);
}
report(parser, Errors.InvalidNewTarget);
}
parser.assignable = AssignmentKind.NotAssignable;
const callee = parseMemberOrUpdateExpression(
parser,
context,
parsePrimaryExpressionExtended(parser, context, BindingType.None, /* inNewExpression*/ 1, 0),
/* inNewExpression*/ 1
);
let args: any[] = [];
if (parser.token === Token.LeftParen) {
args = parseArguments(parser, context);
}

let callee = parsePrimaryExpressionExtended(parser, context, BindingType.None, /* inNewExpression*/ 1, 0);
callee = parseMemberOrUpdateExpression(parser, context, callee, /* inNewExpression*/ 1);
parser.assignable = AssignmentKind.NotAssignable;
return {
type: 'NewExpression',
callee,
arguments: args
arguments: parser.token === Token.LeftParen ? parseArguments(parser, context) : []
} as ESTree.NewExpression;
}

Expand Down Expand Up @@ -4606,7 +4659,10 @@ export function parseAsyncExpression(
}

// async => ...
if (parser.token === Token.Arrow) return parseArrowFunctionExpression(parser, context, [expr], 0);
if (parser.token === Token.Arrow) {
if (inNewExpression) report(parser, Errors.InvalidAsyncArrow);
return parseArrowFunctionExpression(parser, context, [expr], 0);
}

return expr;
}
Expand Down
128 changes: 125 additions & 3 deletions test/parser/expressions/array.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,16 @@ describe('Expressions - Array', () => {
'[1 <= 0]',
'[a, ...b=c]',
'[a, ...b=c]',
'[a, ...b=c]',
'[a, ...(b=c)]',
'[a, ...b=c.d = 2]',
'[a, ...b=c]',
'([a, ...b=c])',
'[a] = x',
'[[a] = x]',
'[[a] = x]',
'[[a] = [[a] = x]]',
'[[a] = [[a] = [[a] = [[a] = x]]]]',
'[a = b] = x',
'[[a] = b] = x',
'[,,,] = x',
Expand All @@ -32,7 +40,6 @@ describe('Expressions - Array', () => {
'[...a]',
'[a, ...b]',
'[...a,]',
'[...a, ,]',
'[, ...a]',
'[x().foo] = x',
'[(x().foo)] = x',
Expand Down Expand Up @@ -292,17 +299,50 @@ describe('Expressions - Array', () => {
'({...{eval}.x} = {});',
'[{eval}.x] = [];',
'[...{eval}.x] = [];',
'[...{eval}.x] = [[...{arguments}.x] = []];',
'({a: {eval}.x} = {});',
'[...{arguments}.x] = [];',
'[a]',
'[[a] / bcd]',
'([a] / bcd)',
'[({a})]',
'[({a}), ({b})]',
'[(({a}), ({b}))]',
'([(({a}), ({b}))])',
'[a]',
'[a]',
'[a]',
'[a, b] = [10, 20];',
'[a, b.c.d = (a) / 2 ] = [10, 20];',
'({a, b, ...rest} = {a: 10, b: 20, c: 30, d: 40});',
'[a=5, b=7] = [1];',
'[a=5, b=(7)] = ([1]);',
'[a=5, b=(7).c.d] = ([1]);',
'[a, b] = [b, a];',
'[a, b.c] = [d.e, f.g];',
'[a, b.c] = [d.e, (f.g) = h];',
'[a, b] = f(() => { }); ',
'[a, b] = f(() => { [a, b.c] = [d.e, (f.g) = h]; }); ',
'([a, b] = f(() => { [a, b.c] = [d.e, (f.g) = h]; }));',
'[a, b] = f(); ',
'var [a, , b] = f();',
'[a, ...b] = [1, 2, 3];',
'[a, ...b] = [1, 2, ...c];',
'[a, ...b] = [1, 2, ...(c / 2)];',
'[a, ...b] = [1, 2, ...c / 2];',
'[a, ...b] = ([1, 2, ...c / 2]);',
'o = {p: 42, q: true};',
'[a, b, ...rest] = [10, 20, 30, 40, 50];'
'[o = {p: 42, q: true}];',
'([o = {p: 42, q: true}]);',
'[a, b, ...rest] = [10, 20, 30, 40, 50];',
'[[[a.b =[]]]]',
'[[[a.b =[{ x: x.b }]]]]',
'[[[a.b =[{ x: x.b }]]]]',
'[[[a.b =[{ x: x.b }]]] = abc]',
'[(a) = (b)]',
'[(x) = y = (z)]',
'[(x) = y = (z) => (a)]',
'[(x) => y = (z)]'
]) {
it(`${arg}`, () => {
t.doesNotThrow(() => {
Expand Down Expand Up @@ -404,6 +444,14 @@ describe('Expressions - Array', () => {
['[...0,a]=0', Context.None],
['[...0,{a=0}]=0', Context.None],
['[...0,...{a=0}]=0', Context.None],
['function foo() { [a, ...{b=c}]}', Context.None],
['() => {[a, ...{b=c}]}', Context.None],
['([[a](b.c) = [[a] = [[a] = ([[a] = x]]]]))', Context.None],
['([[a](b) = [[a] = [[a] = ([[a] = x]]]]))', Context.None],
['[[a] = [[a] = [[a] = ([[a] = x]]]])', Context.None],
['([[a] = [[a] = [[a] = ([[a] = x]]]]))', Context.None],
['[...a, ,] = [...a, ,]', Context.None],
['([...a, ,] = [...a, ,])', Context.None],
['[...{a=0},]', Context.None],
['[...{a=0},]=0', Context.None],
['[0] = 0', Context.None],
Expand Down Expand Up @@ -431,6 +479,7 @@ describe('Expressions - Array', () => {
['[...{a = b} = c] = d;', Context.None],
['result = [...{ x = yield }] = y;', Context.Strict],
['[true = x] = x', Context.None],
['[(...)]', Context.None],
['(...)', Context.None],
['[...this, y] = foo;', Context.None],
['[{..}, x]', Context.None],
Expand All @@ -450,6 +499,19 @@ describe('Expressions - Array', () => {
[`([...x.y] = z) => z`, Context.None],
['[a, ...]', Context.None],
['[..., ]', Context.None],
['[a=5, b=7] = ([1]) = x;', Context.None],
['[a=5, b=7] = ([1]) => x;', Context.None],
['[a=5, b=7] = ([1]) => x;', Context.None],
['[(a=5, b=(x)) = y] = ([1]);', Context.None],
['[(a=5, b=(7))] = ([1]);', Context.None],
['[a=5, b=(7).c.(d)] = ([1])', Context.None],
['[a=5, b=(7).c.(d)[e]] = ([1]);', Context.None],
['([a] / ...bcd)', Context.None],
['([a], ...[bcd] = (x))', Context.None],
['([a], ...bcd = (x))', Context.None],
['([(({a.b.c[d]}), ({b = c / 2}))])', Context.None],
['([(({a[d]}), ({b = c / 2}))])', Context.None],
['([(({a}), ({b = c / 2}))])', Context.None],
['[..., ...]', Context.None],
['[ (...a)]', Context.None],
['[true = x]', Context.None],
Expand Down Expand Up @@ -544,10 +606,70 @@ describe('Expressions - Array', () => {
['[implements]', Context.Strict | Context.Module],
['"use strict"; [implements]', Context.None],
['x, [foo + y, bar] = doo;', Context.None],
['[...{a: true} = c]', Context.None]
['[...{a: true} = c]', Context.None],
['[[[a.b =[{ x: x.b }]]]] = ([{ a = b / 2}])', Context.None],
['[[[a.b =[{ x: x.b = 123 }]a(b=c)]]]', Context.None],
['[(a.b.c.d = e) = ()]', Context.None],
['function foo() { [(a.b.c.d = e) = ()]}', Context.None],
['[() = ()]', Context.None],
['[(1) = (a = b)]', Context.None],
['[(1) = (a = b.c)]', Context.None],
['[([{ x = y }] = b.call(c)) = ()]', Context.None],
['[(a = b.call(c)) = ()]', Context.None],
['[(a = b.call(c)) = (a = b / 2)]', Context.None]
]);

pass('Expressions - Array (pass)', [
[
'[(x) = y = (z) => (a)]',
Context.None,
{
body: [
{
expression: {
elements: [
{
left: {
name: 'x',
type: 'Identifier'
},
operator: '=',
right: {
left: {
name: 'y',
type: 'Identifier'
},
operator: '=',
right: {
async: false,
body: {
name: 'a',
type: 'Identifier'
},
expression: true,
id: null,
params: [
{
name: 'z',
type: 'Identifier'
}
],
type: 'ArrowFunctionExpression'
},
type: 'AssignmentExpression'
},
type: 'AssignmentExpression'
}
],
type: 'ArrayExpression'
},
type: 'ExpressionStatement'
}
],
sourceType: 'script',
type: 'Program'
}
],
[
'[.../x//yield]',
Context.None,
Expand Down

0 comments on commit c9e08cd

Please sign in to comment.