Skip to content

Commit 9421899

Browse files
committed
Async generators
1 parent 4e3b259 commit 9421899

18 files changed

+1418
-6058
lines changed

src/messages.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
// Error messages should be identical to V8.
22
export const Messages = {
3+
AsyncFunctionInSingleStatementContext: 'Async functions can only be declared at the top level or inside a block.',
34
BadImportCallArity: 'Unexpected token',
45
BadGetterArity: 'Getter must not have any formal parameters',
56
BadSetterArity: 'Setter must have exactly one formal parameter',
@@ -12,6 +13,7 @@ export const Messages = {
1213
DefaultRestProperty: 'Unexpected token =',
1314
DuplicateBinding: 'Duplicate binding %0',
1415
DuplicateConstructor: 'A class may only have one constructor',
16+
DuplicateParameter: 'Duplicate parameter name not allowed in this context',
1517
DuplicateProtoProperty: 'Duplicate __proto__ fields are not allowed in object literals',
1618
ForInOfLoopInitializer: '%0 loop variable declaration may not have an initializer',
1719
GeneratorInLegacyContext: 'Generator declarations are not allowed in legacy contexts',

src/nodes.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -115,12 +115,12 @@ export class AsyncFunctionDeclaration {
115115
readonly generator: boolean;
116116
readonly expression: boolean;
117117
readonly async: boolean;
118-
constructor(id: Identifier | null, params: FunctionParameter[], body: BlockStatement) {
118+
constructor(id: Identifier | null, params: FunctionParameter[], body: BlockStatement, generator: boolean) {
119119
this.type = Syntax.FunctionDeclaration;
120120
this.id = id;
121121
this.params = params;
122122
this.body = body;
123-
this.generator = false;
123+
this.generator = generator;
124124
this.expression = false;
125125
this.async = true;
126126
}
@@ -134,12 +134,12 @@ export class AsyncFunctionExpression {
134134
readonly generator: boolean;
135135
readonly expression: boolean;
136136
readonly async: boolean;
137-
constructor(id: Identifier | null, params: FunctionParameter[], body: BlockStatement) {
137+
constructor(id: Identifier | null, params: FunctionParameter[], body: BlockStatement, generator: boolean) {
138138
this.type = Syntax.FunctionExpression;
139139
this.id = id;
140140
this.params = params;
141141
this.body = body;
142-
this.generator = false;
142+
this.generator = generator;
143143
this.expression = false;
144144
this.async = true;
145145
}

src/parser.ts

Lines changed: 49 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -772,8 +772,7 @@ export class Parser {
772772
return body;
773773
}
774774

775-
parsePropertyMethodFunction(): Node.FunctionExpression {
776-
const isGenerator = false;
775+
parsePropertyMethodFunction(isGenerator: boolean): Node.FunctionExpression {
777776
const node = this.createNode();
778777

779778
const previousAllowYield = this.context.allowYield;
@@ -785,19 +784,24 @@ export class Parser {
785784
return this.finalize(node, new Node.FunctionExpression(null, params.params, method, isGenerator));
786785
}
787786

788-
parsePropertyMethodAsyncFunction(): Node.FunctionExpression {
787+
parsePropertyMethodAsyncFunction(isGenerator: boolean): Node.FunctionExpression {
789788
const node = this.createNode();
790789

791790
const previousAllowYield = this.context.allowYield;
792791
const previousAwait = this.context.await;
793792
this.context.allowYield = false;
794793
this.context.await = true;
794+
795795
const params = this.parseFormalParameters();
796+
if (params.message === Messages.StrictParamDupe) {
797+
this.throwError(Messages.DuplicateParameter);
798+
}
799+
796800
const method = this.parsePropertyMethod(params);
797801
this.context.allowYield = previousAllowYield;
798802
this.context.await = previousAwait;
799803

800-
return this.finalize(node, new Node.AsyncFunctionExpression(null, params.params, method));
804+
return this.finalize(node, new Node.AsyncFunctionExpression(null, params.params, method, isGenerator));
801805
}
802806

803807
parseObjectPropertyKey(): Node.PropertyKey {
@@ -855,13 +859,18 @@ export class Parser {
855859
let method = false;
856860
let shorthand = false;
857861
let isAsync = false;
862+
let isGenerator = false;
858863

859864
if (token.type === Token.Identifier) {
860865
const id = token.value;
861866
this.nextToken();
862867
computed = this.match('[');
863868
isAsync = !this.hasLineTerminator && (id === 'async') &&
864-
!this.match(':') && !this.match('(') && !this.match('*') && !this.match(',');
869+
!this.match(':') && !this.match('(') && !this.match(',');
870+
isGenerator = this.match('*');
871+
if (isGenerator) {
872+
this.nextToken();
873+
}
865874
key = isAsync ? this.parseObjectPropertyKey() : this.finalize(node, new Node.Identifier(id));
866875
} else if (this.match('*')) {
867876
this.nextToken();
@@ -908,7 +917,7 @@ export class Parser {
908917
value = this.inheritCoverGrammar(this.parseAssignmentExpression);
909918

910919
} else if (this.match('(')) {
911-
value = isAsync ? this.parsePropertyMethodAsyncFunction() : this.parsePropertyMethodFunction();
920+
value = isAsync ? this.parsePropertyMethodAsyncFunction(isGenerator) : this.parsePropertyMethodFunction(isGenerator);
912921
method = true;
913922

914923
} else if (token.type === Token.Identifier) {
@@ -2316,6 +2325,10 @@ export class Parser {
23162325
const node = this.createNode();
23172326
this.expectKeyword('do');
23182327

2328+
if (this.matchKeyword("class") || this.matchKeyword("function")) {
2329+
this.tolerateError(this.lookahead);
2330+
}
2331+
23192332
const previousInIteration = this.context.inIteration;
23202333
this.context.inIteration = true;
23212334
const body = this.parseStatement();
@@ -2520,6 +2533,10 @@ export class Parser {
25202533
} else {
25212534
this.expect(')');
25222535

2536+
if (this.matchKeyword("class") || this.matchKeyword("function")) {
2537+
this.tolerateError(this.lookahead);
2538+
}
2539+
25232540
const previousInIteration = this.context.inIteration;
25242541
this.context.inIteration = true;
25252542
body = this.isolateCoverGrammar(this.parseStatement);
@@ -3046,12 +3063,15 @@ export class Parser {
30463063

30473064
const isAsync = this.matchContextualKeyword('async');
30483065
if (isAsync) {
3066+
if (this.context.inIteration) {
3067+
this.tolerateError(Messages.AsyncFunctionInSingleStatementContext);
3068+
}
30493069
this.nextToken();
30503070
}
30513071

30523072
this.expectKeyword('function');
30533073

3054-
const isGenerator = isAsync ? false : this.match('*');
3074+
const isGenerator = this.match('*');
30553075
if (isGenerator) {
30563076
this.nextToken();
30573077
}
@@ -3084,6 +3104,10 @@ export class Parser {
30843104
this.context.allowYield = !isGenerator;
30853105

30863106
const formalParameters = this.parseFormalParameters(firstRestricted);
3107+
if (isGenerator && formalParameters.message === Messages.StrictParamDupe) {
3108+
this.throwError(Messages.DuplicateParameter);
3109+
}
3110+
30873111
const params = formalParameters.params;
30883112
const stricted = formalParameters.stricted;
30893113
firstRestricted = formalParameters.firstRestricted;
@@ -3107,8 +3131,9 @@ export class Parser {
31073131
this.context.await = previousAllowAwait;
31083132
this.context.allowYield = previousAllowYield;
31093133

3110-
return isAsync ? this.finalize(node, new Node.AsyncFunctionDeclaration(id, params, body)) :
3111-
this.finalize(node, new Node.FunctionDeclaration(id, params, body, isGenerator));
3134+
return isAsync
3135+
? this.finalize(node, new Node.AsyncFunctionDeclaration(id, params, body, isGenerator))
3136+
: this.finalize(node, new Node.FunctionDeclaration(id, params, body, isGenerator));
31123137
}
31133138

31143139
parseFunctionExpression(): Node.AsyncFunctionExpression | Node.FunctionExpression {
@@ -3121,7 +3146,7 @@ export class Parser {
31213146

31223147
this.expectKeyword('function');
31233148

3124-
const isGenerator = isAsync ? false : this.match('*');
3149+
const isGenerator = this.match('*');
31253150
if (isGenerator) {
31263151
this.nextToken();
31273152
}
@@ -3154,6 +3179,10 @@ export class Parser {
31543179
}
31553180

31563181
const formalParameters = this.parseFormalParameters(firstRestricted);
3182+
if (isGenerator && formalParameters.message === Messages.StrictParamDupe) {
3183+
this.throwError(Messages.DuplicateParameter);
3184+
}
3185+
31573186
const params = formalParameters.params;
31583187
const stricted = formalParameters.stricted;
31593188
firstRestricted = formalParameters.firstRestricted;
@@ -3176,8 +3205,9 @@ export class Parser {
31763205
this.context.await = previousAllowAwait;
31773206
this.context.allowYield = previousAllowYield;
31783207

3179-
return isAsync ? this.finalize(node, new Node.AsyncFunctionExpression(id, params, body)) :
3180-
this.finalize(node, new Node.FunctionExpression(id, params, body, isGenerator));
3208+
return isAsync
3209+
? this.finalize(node, new Node.AsyncFunctionExpression(id, params, body, isGenerator))
3210+
: this.finalize(node, new Node.FunctionExpression(id, params, body, isGenerator));
31813211
}
31823212

31833213
// https://tc39.github.io/ecma262/#sec-directive-prologues-and-the-use-strict-directive
@@ -3360,6 +3390,7 @@ export class Parser {
33603390
let method = false;
33613391
let isStatic = false;
33623392
let isAsync = false;
3393+
let isGenerator = false;
33633394

33643395
if (this.match('*')) {
33653396
this.nextToken();
@@ -3379,8 +3410,12 @@ export class Parser {
33793410
}
33803411
if ((token.type === Token.Identifier) && !this.hasLineTerminator && (token.value === 'async')) {
33813412
const punctuator = this.lookahead.value;
3382-
if (punctuator !== ':' && punctuator !== '(' && punctuator !== '*') {
3413+
if (punctuator !== ':' && punctuator !== '(') {
33833414
isAsync = true;
3415+
isGenerator = this.match("*");
3416+
if (isGenerator) {
3417+
this.nextToken();
3418+
}
33843419
token = this.lookahead;
33853420
computed = this.match('[');
33863421
key = this.parseObjectPropertyKey();
@@ -3415,7 +3450,7 @@ export class Parser {
34153450

34163451
if (!kind && key && this.match('(')) {
34173452
kind = 'init';
3418-
value = isAsync ? this.parsePropertyMethodAsyncFunction() : this.parsePropertyMethodFunction();
3453+
value = isAsync ? this.parsePropertyMethodAsyncFunction(isGenerator) : this.parsePropertyMethodFunction(isGenerator);
34193454
method = true;
34203455
}
34213456

0 commit comments

Comments
 (0)