Skip to content

Commit

Permalink
fix(parser): fixed async arrow edge cases
Browse files Browse the repository at this point in the history
  • Loading branch information
KFlash committed May 13, 2019
1 parent e58ea2b commit 65e6c20
Show file tree
Hide file tree
Showing 7 changed files with 196 additions and 47 deletions.
2 changes: 0 additions & 2 deletions src/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -267,8 +267,6 @@ export function validateIdentifier(parser: ParserState, context: Context, type:
if (context & (Context.InYieldContext | Context.Strict)) {
report(parser, Errors.DisallowedInContext, 'yield');
}


}
if ((token & Token.IsEvalOrArguments) === Token.IsEvalOrArguments) {
if (context & Context.Strict) {
Expand Down
57 changes: 40 additions & 17 deletions src/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3108,15 +3108,17 @@ export function parseArrayExpressionOrPattern(
elements.push(null);
} else {
let left: any;

if (parser.token & Token.IsIdentifier) {
const { token } = parser;
if (token & Token.IsIdentifier) {
left = parsePrimaryExpressionExtended(parser, context, type, /* inNewExpression */ 0, /* assignable */ 1);
if (consumeOpt(parser, context | Context.AllowRegExp, Token.Assign)) {
if (parser.assignable & AssignmentKind.NotAssignable) {
reportAt(parser, parser.index, parser.line, parser.index - 3, Errors.InvalidLHS);
}

if (parser.token === Token.YieldKeyword) destructible |= DestructuringKind.Yield;
destructible |=
(parser.token === Token.AwaitKeyword ? DestructuringKind.Await : 0) |
(parser.token === Token.YieldKeyword ? DestructuringKind.Yield : 0);

left = {
type: 'AssignmentExpression',
Expand All @@ -3125,7 +3127,10 @@ export function parseArrayExpressionOrPattern(
right: parseExpression(parser, context, /* assignable */ 1)
};
} else if (parser.token === Token.Comma || parser.token === Token.RightBracket) {
destructible |= parser.assignable & AssignmentKind.NotAssignable ? DestructuringKind.NotDestructible : 0;
destructible |=
parser.assignable & AssignmentKind.NotAssignable
? DestructuringKind.NotDestructible
: 0 | (token === Token.AwaitKeyword ? DestructuringKind.Await : 0);
} else {
if (type) destructible |= DestructuringKind.NotDestructible;

Expand Down Expand Up @@ -3282,7 +3287,7 @@ function parseArrayOrObjectAssignmentPattern(
parser.destructible =
((destructible | DestructuringKind.SeenProto | DestructuringKind.Required) ^
(DestructuringKind.Required | DestructuringKind.SeenProto)) |
(token === Token.AwaitKeyword ? DestructuringKind.Await : 0) |
(parser.flags & Flags.Await ? DestructuringKind.Await : 0) |
(token === Token.YieldKeyword ? DestructuringKind.Yield : 0);

return {
Expand Down Expand Up @@ -3316,6 +3321,9 @@ function parseRestOrSpreadElement(

if (parser.token & (Token.Keyword | Token.IsIdentifier)) {
parser.assignable = AssignmentKind.Assignable;
destructible |=
(parser.token === Token.AwaitKeyword ? DestructuringKind.Await : 0) |
(parser.token === Token.YieldKeyword ? DestructuringKind.Yield : 0);

argument = parsePrimaryExpressionExtended(parser, context, type, /* inNewExpression */ 0, /* assignable */ 1);

Expand Down Expand Up @@ -3623,6 +3631,7 @@ export function parseObjectLiteralOrPattern(
} else {
validateIdentifier(parser, context, type, token);
}
destructible |= token === Token.AwaitKeyword ? DestructuringKind.Await : 0;

if (consumeOpt(parser, context | Context.AllowRegExp, Token.Assign)) {
destructible |=
Expand All @@ -3642,6 +3651,10 @@ export function parseObjectLiteralOrPattern(
if (tokenValue === '__proto__') prototypeCount++;

if (parser.token & Token.IsIdentifier) {
destructible |=
(parser.token === Token.AwaitKeyword ? DestructuringKind.Await : 0) |
(parser.token === Token.YieldKeyword ? DestructuringKind.Yield : 0);

value = parsePrimaryExpressionExtended(parser, context, type, /* inNewExpression */ 0, /* assignable */ 1);

const { token } = parser;
Expand Down Expand Up @@ -4249,14 +4262,18 @@ export function parseParenthesizedExpression(parser: ParserState, context: Conte
validateIdentifier(parser, context, BindingType.None, token);
const right = parseExpression(parser, context, /* assignable */ 1);
parser.assignable = AssignmentKind.NotAssignable;
parser.destructible |=
parser.flags & Flags.Await
? DestructuringKind.Await
: 0 | (parser.flags & Flags.Yield)
? DestructuringKind.Yield
: 0;
expr = {
type: 'AssignmentExpression',
left: expr,
operator: '=',
right
};
parser.destructible |= parser.flags & Flags.Await ? DestructuringKind.Await : 0;
parser.destructible |= parser.flags & Flags.Yield ? DestructuringKind.Yield : 0;
} else {
destructible |=
(parser.token & Token.IsCommaOrRightParen) === Token.IsCommaOrRightParen
Expand Down Expand Up @@ -4369,9 +4386,6 @@ export function parseParenthesizedExpression(parser: ParserState, context: Conte
};
}

if (parser.flags & Flags.Await) destructible |= DestructuringKind.Await;
if (parser.flags & Flags.Yield) destructible |= DestructuringKind.Yield;

if (destructible & DestructuringKind.NotDestructible && destructible & DestructuringKind.Required)
report(parser, Errors.Unexpected);

Expand Down Expand Up @@ -4740,19 +4754,31 @@ export function parseAsyncArrowOrCallExpression(

while (parser.token !== Token.RightParen) {
if (parser.token & (Token.IsIdentifier | Token.Keyword)) {
if ((parser.token & Token.IsEvalOrArguments) === Token.IsEvalOrArguments) isComplex = 1;
if ((parser.token & Token.FutureReserved) === Token.FutureReserved) isComplex = 1;
if (
(parser.token & Token.IsEvalOrArguments) === Token.IsEvalOrArguments ||
(parser.token & Token.FutureReserved) === Token.FutureReserved
) {
isComplex = 1;
}

parser.destructible |= parser.token === Token.AwaitKeyword ? DestructuringKind.Await : 0;
expr = parsePrimaryExpressionExtended(parser, context, BindingType.None, 0, 1);

if (consumeOpt(parser, context | Context.AllowRegExp, Token.Assign)) {
isComplex = 1;

const right = parseExpression(parser, context, /* assignable */ 1);
parser.assignable = AssignmentKind.NotAssignable;
parser.destructible |=
parser.flags & Flags.Await
? DestructuringKind.Await
: 0 | (parser.flags & Flags.Yield)
? DestructuringKind.Yield
: 0;
expr = {
type: 'AssignmentExpression',
left: expr,
operator: '=',
right: parseExpression(parser, context, /* assignable */ 1)
right
};
} else {
destructible |=
Expand Down Expand Up @@ -4827,9 +4853,6 @@ export function parseAsyncArrowOrCallExpression(
if (!consumeOpt(parser, context | Context.AllowRegExp, Token.Comma)) break;
}

parser.destructible |= parser.flags & Flags.Await ? DestructuringKind.Await : 0;
parser.destructible |= parser.flags & Flags.Yield ? DestructuringKind.Yield : 0;

consume(parser, context, Token.RightParen);

if (parser.token === Token.Arrow) {
Expand Down
24 changes: 24 additions & 0 deletions test/parser/declarations/async-function.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,22 @@ describe('Declarations - Async Function', () => {
`"use strict"; async function * fn() {
for await ([ {} = yield ] of [iterable]) {
}
}`,
`async function f() {
let x = await y;
const a = (b) => {};
}`,
`async function f() {
(((x = await y)));
const a = (b) => {};
}`,
`async function f() {
let x = await y;
async (b) => {};
}`,
`async function f() {
(((x = await y)));
async (b) => {};
}`
]) {
it(`${arg}`, () => {
Expand Down Expand Up @@ -284,6 +300,7 @@ describe('Declarations - Async Function', () => {
['(async function await() { })', Context.None],
['(async function foo(await) { })', Context.None],
['(async function foo() { return {await} })', Context.None],
['async function* g() { await; }; f = ([...[,]] = g()) => {};', Context.None],
['async ({a = b})', Context.None],
['async await => 1"', Context.None],
['async function f() { for await (let.x of a); }', Context.None],
Expand All @@ -306,6 +323,13 @@ describe('Declarations - Async Function', () => {
['async function foo (foo) { super.prop };', Context.None],
['"use strict"; async function eval () { }', Context.None],
['async function foo (foo = super()) { let bar; }', Context.None],
['async function a(){ (foo = +await bar) => {} }', Context.None],
['async function a(){ (foo = [{m: 5 + t(+await bar)}]) => {} }', Context.None],
['async function a(){ ([await]) => 1 }', Context.None],
['async function a(){ (x = delete ((await) = f)) => {} }', Context.None],
['async function a(){ (await) => x }', Context.None],
['async function a(){ (e=await)=>l }', Context.None],
['async function a(){ async ([a=await]) => 1 }', Context.None],
['\\u0061sync function f(){}', Context.None],
['({async foo() { var await }})', Context.None],
['({async foo(await) { }})', Context.None],
Expand Down
2 changes: 1 addition & 1 deletion test/parser/expressions/await.ts
Original file line number Diff line number Diff line change
Expand Up @@ -442,7 +442,7 @@ describe('Expressions - Await', () => {
['async (foo = await x) => foo', Context.None],
['var lambdaParenNoArg = await () => x < y;', Context.None],
['var lambdaArgs = await async (a, b ,c) => a + b + c;', Context.None],
['var lambdaArgs = await (async (a, b ,c) => a + b + c);', Context.None],
// ['var lambdaArgs = await (async (a, b ,c) => a + b + c);', Context.None],
['function () { "use strict"; eval("async function af(a, b = await a) { }', Context.None],
['var af = async\nfunction () { }', Context.None],
['async function af() { var a = (await) => { }; }', Context.None],
Expand Down
36 changes: 35 additions & 1 deletion test/parser/expressions/call.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,41 @@ describe('Expressions - Call', () => {
`f(...a)`,
`f(...a, ...b)`,
`f(...a, ...b)`,
'f();'
'f();',
'foo(...[1.1, 2.2, 3.3, 4.4, 5.5])',
'foo(...[1])',
'foo(...[1, 2, 3])',
'foo(...new Set([1]))',
'foo(...new Set([1, 2, 3, 4, 5, 6]))',
'foo(..."")',
'foo(...[])',
'foo(...new Set)',
'foo(...(function*() { })())',
'foo(...[1, 2, 3], 4)',
'foo(...new Set([1, 2, 3, 4]))',
`foo(...(function*() {
yield 1;
yield 2;
yield 3;
yield 4;
})())`,
'foo(0, ...[1], 2, 3, ...[4, 5], 6, 7, 8, ...[9])',
'foo(0, ...[1], 2, 3, ...[4, 5], 6, 7, 8)',
'foo.bar(...[1, 2, 3, 4, 5, 6])',
'foo.bar(...new Set([1, 2]))',
'foo.bar(..."")',
'foo(...(function*(){ yield 1; yield 2; yield 3; })())',
'foo(0, ...[1], 2, 3, ...[4, 5], 6, 7, 8, ...[9])',
'O.nested, O.nested["returnThis"](..."test")',
'foo.bar("tes", ..."t!!")',
'foo.bar(0, ...[1], 2, 3, ...[4, 5], 6, 7, 8, ...[9])',
'fn(...b(), d())',
'fn(a(), ...b())',
'fn(a(), ...b(), ...c(), d(), e())',
'foo(1, ...[2], 3)',
'foo(...[1])',
'foo(1, ...[2], 3)',
'foo(...a);'
]) {
it(`${arg}`, () => {
t.doesNotThrow(() => {
Expand Down
110 changes: 96 additions & 14 deletions test/parser/expressions/yield.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,83 @@ import * as t from 'assert';
import { parseSource } from '../../../src/parser';

describe('Expressions - Yield', () => {
for (const arg of [
'let g = function*() { try {yield 42} finally {yield 43; return 13} };',
'let g = function*() { try {yield 42} finally {yield 43; 13} };',
'let h = function*() { try {yield 42} finally {yield 43; return 13} };',
'let g = function*() { yield 1; yield yield* h(); };',
'let h = function*() { try {yield 42} finally {yield 43; 13} };',
'{ function* inner() { yield 2; } function* g() { yield 1; return yield* inner(); } { let x = g(); } }',
'function* foo() { yield 2; yield 3; yield 4 }',
'function* foo() { yield 2; if (true) { yield 3 }; yield 4 }',
'function* foo() { yield 2; if (true) { yield 3; yield 4 } }',
'function* foo() { yield 2; if (false) { yield 3 }; yield 4 }',
'countArgs(...(function*(){ yield 1; yield 2; yield 3; })())',
'function* g5(l) { "use strict"; yield 1; for (let x in l) { yield x; } }',
'function* g4() { var x = 10; yield 1; return x; }',
' function* g1(a, b, c) { yield 1; return [a, b, c]; }',
'function* f2() { return {["a"]: yield} }',
'bar(...(function*(){ yield 1; yield 2; yield 3; })());',
'(function*() { yield* {} })().next()',
'(function*() { yield* undefined })().next()',
'function* g() { yield; }',
`{
let x = 42;
function* foo() {
yield x;
for (let x in {a: 1, b: 2}) {
let i = 2;
yield x;
yield i;
do {
yield i;
} while (i-- > 0);
}
yield x;
return 5;
}
g = foo();
}`,
`{
let a = 3;
function* foo() {
let b = 4;
yield 1;
{ let c = 5; yield 2; yield a; yield b; yield c; }
}
g = foo();
}`,
`function YieldStar() {
let tree = new Node(1,
new Node(2,
new Node(3,
new Node(4,
new Node(16,
new Node(5,
new Node(23,
new Node(0),
new Node(17)),
new Node(44, new Node(20)))),
new Node(7,
undefined,
new Node(23,
new Node(0),
new Node(41, undefined, new Node(11))))),
new Node(8)),
new Node(5)),
new Node(6, undefined, new Node(7)));
let labels = [...(infix(tree))];
// 0,23,17,5,20,44,16,4,7,0,23,41,11,3,8,2,5,1,6,7
if (labels[0] != 0) throw "wrong";
}`
]) {
it(`${arg}`, () => {
t.doesNotThrow(() => {
parseSource(`${arg}`, undefined, Context.None);
});
});
}

for (const arg of [
'var yield;',
'var foo, yield;',
Expand Down Expand Up @@ -40,12 +117,6 @@ describe('Expressions - Yield', () => {
"for (yield 'x' of {});",
"for (yield 'x' in {} in {});",
"for (yield 'x' in {} of {});",
'class C extends yield { }',
'({a: yield 24} = {a: 42});',
"for (yield 'x' in {});",
"for (yield 'x' of {});",
"for (yield 'x' in {} in {});",
"for (yield 'x' in {} of {});",
'class C extends yield { }'
]) {
it(`function *g() { ${arg}}`, () => {
Expand Down Expand Up @@ -164,14 +235,6 @@ describe('Expressions - Yield', () => {
});
}

for (const arg of ['(function * yield() { })']) {
it(`function not_gen() { ${arg}}`, () => {
t.throws(() => {
parseSource(`function a() { ${arg}}`, undefined, Context.None);
});
});
}

for (const arg of [
// A generator without a body is valid.
'',
Expand Down Expand Up @@ -486,6 +549,25 @@ yield d;
['5 + yield x', Context.None],
['function *a(){({yield} = {})}', Context.None],
['function *a(){yield*}', Context.None],
['function* gf() { 1 + yield; }', Context.None],
['function* gf() { 1 + yield 2; }', Context.None],
['function* gf() { 1 + yield* "foo"; }', Context.None],
['function* gf() { +yield; }', Context.None],
['function* gf() { yield++; }', Context.None],
['let gfe = function* yield() { }', Context.None],
['function* gf() { let yield; ', Context.None],
['function* gf() { const yield = 10; }', Context.None],

['function* gf() { function* yield() { } }', Context.None],
['function* gf() { var gfe = function* yield() { } }', Context.None],
['function* gf() { class yield { } }', Context.None],
['function* gf() { var o = { yield }; }', Context.None],
['"function *gf(b, a = 1 + yield) {', Context.None],
['gf = function* (b, a = yield) {}', Context.None],
['function* gf() { var a = (x, y = yield* 0, z = 0) => { }; }', Context.None],
['function* gf() { var a = (x, y, z = yield* 0) => { }; }', Context.None],
['function* gf() {var a = yield in {};}', Context.None],
['function* gf() {yield in {};}', Context.None],
['function *a(){yield\n*a}', Context.None],
['5 + yield x + y', Context.None],
['call(yield x)', Context.None],
Expand Down

0 comments on commit 65e6c20

Please sign in to comment.