Skip to content

Commit 24b802e

Browse files
committed
Fix captured block scope variables in downlevel async.
Fixes #10889
1 parent 9812ab5 commit 24b802e

14 files changed

+572
-52
lines changed

src/compiler/emitter.ts

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
7373

7474
const generatorHelper = `
7575
var __generator = (this && this.__generator) || function (thisArg, body) {
76-
var _ = { label: 0, sent: function() { if (sent[0] === 1) throw sent[1]; return sent[1]; }, trys: [], stack: [] }, sent, f;
76+
var _ = { label: 0, sent: function() { if (sent[0] === 1) throw sent[1]; return sent[1]; }, trys: [], stack: [] }, sent, y, f, v, r;
7777
function step(op) {
7878
if (f) throw new TypeError("Generator is already executing.");
7979
while (1) {
@@ -83,12 +83,18 @@ var __generator = (this && this.__generator) || function (thisArg, body) {
8383
case 2: return { value: op[1], done: true };
8484
}
8585
try {
86-
switch (f = 1, op[0]) {
86+
if (f = 1, y) {
87+
v = y[op[0] & 2 ? "return" : op[0] ? "throw" : "next"];
88+
if (v && !(v = v.call(y, op[1])).done) return v;
89+
if (y = void 0, v) op[0] = 0, op[1] = v.value; continue;
90+
}
91+
switch (op[0]) {
8792
case 0: case 1: sent = op; break;
8893
case 4: return _.label++, { value: op[1], done: false };
94+
case 5: _.label++, y = op[1], op = [0]; continue;
8995
case 7: op = _.stack.pop(), _.trys.pop(); continue;
9096
default:
91-
var r = _.trys.length > 0 && _.trys[_.trys.length - 1];
97+
r = _.trys.length > 0 && _.trys[_.trys.length - 1];
9298
if (!r && (op[0] === 6 || op[0] === 2)) { _.done = 1; continue; }
9399
if (op[0] === 3 && (!r || (op[1] > r[0] && op[1] < r[3]))) { _.label = op[1]; break; }
94100
if (op[0] === 6 && _.label < r[1]) { _.label = r[1], sent = op; break; }
@@ -99,8 +105,8 @@ var __generator = (this && this.__generator) || function (thisArg, body) {
99105
}
100106
op = body.call(thisArg, _);
101107
}
102-
catch (e) { op = [6, e]; }
103-
finally { f = 0, sent = void 0; }
108+
catch (e) { op = [6, e], y = void 0; }
109+
finally { f = 0, sent = v = r = void 0; }
104110
}
105111
}
106112
return {

src/compiler/transformers/es6.ts

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2055,26 +2055,39 @@ namespace ts {
20552055
if (!isBlock(loopBody)) {
20562056
loopBody = createBlock([loopBody], /*location*/ undefined, /*multiline*/ true);
20572057
}
2058+
2059+
const isAsyncBlockContainingAwait =
2060+
containingNonArrowFunction
2061+
&& (containingNonArrowFunction.emitFlags & NodeEmitFlags.AsyncFunctionBody) !== 0
2062+
&& (node.statement.transformFlags & TransformFlags.ContainsYield) !== 0;
2063+
2064+
let loopBodyFlags: NodeEmitFlags = 0;
2065+
if (currentState.containsLexicalThis) {
2066+
loopBodyFlags |= NodeEmitFlags.CapturesThis;
2067+
}
2068+
2069+
if (isAsyncBlockContainingAwait) {
2070+
loopBodyFlags |= NodeEmitFlags.AsyncFunctionBody;
2071+
}
2072+
20582073
const convertedLoopVariable =
20592074
createVariableStatement(
2060-
/*modifiers*/ undefined,
2075+
/*modifiers*/ undefined,
20612076
createVariableDeclarationList(
20622077
[
20632078
createVariableDeclaration(
20642079
functionName,
20652080
/*type*/ undefined,
20662081
setNodeEmitFlags(
20672082
createFunctionExpression(
2068-
/*asteriskToken*/ undefined,
2083+
isAsyncBlockContainingAwait ? createToken(SyntaxKind.AsteriskToken) : undefined,
20692084
/*name*/ undefined,
20702085
/*typeParameters*/ undefined,
20712086
loopParameters,
20722087
/*type*/ undefined,
20732088
<Block>loopBody
20742089
),
2075-
currentState.containsLexicalThis
2076-
? NodeEmitFlags.CapturesThis
2077-
: 0
2090+
loopBodyFlags
20782091
)
20792092
)
20802093
]
@@ -2160,7 +2173,7 @@ namespace ts {
21602173
));
21612174
}
21622175

2163-
const convertedLoopBodyStatements = generateCallToConvertedLoop(functionName, loopParameters, currentState);
2176+
const convertedLoopBodyStatements = generateCallToConvertedLoop(functionName, loopParameters, currentState, isAsyncBlockContainingAwait);
21642177
let loop: IterationStatement;
21652178
if (convert) {
21662179
loop = convert(node, convertedLoopBodyStatements);
@@ -2173,12 +2186,17 @@ namespace ts {
21732186
loop = visitEachChild(loop, visitor, context);
21742187
// set loop statement
21752188
loop.statement = createBlock(
2176-
generateCallToConvertedLoop(functionName, loopParameters, currentState),
2189+
convertedLoopBodyStatements,
21772190
/*location*/ undefined,
21782191
/*multiline*/ true
21792192
);
2193+
2194+
// reset and re-aggregate the transform flags
2195+
loop.transformFlags = 0;
2196+
aggregateTransformFlags(loop);
21802197
}
21812198

2199+
21822200
statements.push(
21832201
currentParent.kind === SyntaxKind.LabeledStatement
21842202
? createLabel((<LabeledStatement>currentParent).label, loop)
@@ -2199,7 +2217,7 @@ namespace ts {
21992217
}
22002218
}
22012219

2202-
function generateCallToConvertedLoop(loopFunctionExpressionName: Identifier, parameters: ParameterDeclaration[], state: ConvertedLoopState): Statement[] {
2220+
function generateCallToConvertedLoop(loopFunctionExpressionName: Identifier, parameters: ParameterDeclaration[], state: ConvertedLoopState, isAsyncBlockContainingAwait: boolean): Statement[] {
22032221
const outerConvertedLoopState = convertedLoopState;
22042222

22052223
const statements: Statement[] = [];
@@ -2212,16 +2230,17 @@ namespace ts {
22122230
!state.labeledNonLocalContinues;
22132231

22142232
const call = createCall(loopFunctionExpressionName, /*typeArguments*/ undefined, map(parameters, p => <Identifier>p.name));
2233+
const callResult = isAsyncBlockContainingAwait ? createYield(createToken(SyntaxKind.AsteriskToken), call) : call;
22152234
if (isSimpleLoop) {
2216-
statements.push(createStatement(call));
2235+
statements.push(createStatement(callResult));
22172236
copyOutParameters(state.loopOutParameters, CopyDirection.ToOriginal, statements);
22182237
}
22192238
else {
22202239
const loopResultName = createUniqueName("state");
22212240
const stateVariable = createVariableStatement(
22222241
/*modifiers*/ undefined,
22232242
createVariableDeclarationList(
2224-
[createVariableDeclaration(loopResultName, /*type*/ undefined, call)]
2243+
[createVariableDeclaration(loopResultName, /*type*/ undefined, callResult)]
22252244
)
22262245
);
22272246
statements.push(stateVariable);

src/compiler/transformers/generators.ts

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -906,7 +906,7 @@ namespace ts {
906906
*
907907
* @param node The node to visit.
908908
*/
909-
function visitYieldExpression(node: YieldExpression) {
909+
function visitYieldExpression(node: YieldExpression): LeftHandSideExpression {
910910
// [source]
911911
// x = yield a();
912912
//
@@ -917,7 +917,14 @@ namespace ts {
917917

918918
// NOTE: we are explicitly not handling YieldStar at this time.
919919
const resumeLabel = defineLabel();
920-
emitYield(visitNode(node.expression, visitor, isExpression), /*location*/ node);
920+
const expression = visitNode(node.expression, visitor, isExpression);
921+
if (node.asteriskToken) {
922+
emitYieldStar(expression, /*location*/ node);
923+
}
924+
else {
925+
emitYield(expression, /*location*/ node);
926+
}
927+
921928
markLabel(resumeLabel);
922929
return createGeneratorResume();
923930
}
@@ -2480,6 +2487,16 @@ namespace ts {
24802487
emitWorker(OpCode.BreakWhenFalse, [label, condition], location);
24812488
}
24822489

2490+
/**
2491+
* Emits a YieldStar operation for the provided expression.
2492+
*
2493+
* @param expression An optional value for the yield operation.
2494+
* @param location An optional source map location for the assignment.
2495+
*/
2496+
function emitYieldStar(expression?: Expression, location?: TextRange): void {
2497+
emitWorker(OpCode.YieldStar, [expression], location);
2498+
}
2499+
24832500
/**
24842501
* Emits a Yield operation for the provided expression.
24852502
*

tests/baselines/reference/asyncAwaitIsolatedModules_es5.js

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
5050
});
5151
};
5252
var __generator = (this && this.__generator) || function (thisArg, body) {
53-
var _ = { label: 0, sent: function() { if (sent[0] === 1) throw sent[1]; return sent[1]; }, trys: [], stack: [] }, sent, f;
53+
var _ = { label: 0, sent: function() { if (sent[0] === 1) throw sent[1]; return sent[1]; }, trys: [], stack: [] }, sent, y, f, v, r;
5454
function step(op) {
5555
if (f) throw new TypeError("Generator is already executing.");
5656
while (1) {
@@ -60,12 +60,18 @@ var __generator = (this && this.__generator) || function (thisArg, body) {
6060
case 2: return { value: op[1], done: true };
6161
}
6262
try {
63-
switch (f = 1, op[0]) {
63+
if (f = 1, y) {
64+
v = y[op[0] & 2 ? "return" : op[0] ? "throw" : "next"];
65+
if (v && !(v = v.call(y, op[1])).done) return v;
66+
if (y = void 0, v) op[0] = 0, op[1] = v.value; continue;
67+
}
68+
switch (op[0]) {
6469
case 0: case 1: sent = op; break;
6570
case 4: return _.label++, { value: op[1], done: false };
71+
case 5: _.label++, y = op[1], op = [0]; continue;
6672
case 7: op = _.stack.pop(), _.trys.pop(); continue;
6773
default:
68-
var r = _.trys.length > 0 && _.trys[_.trys.length - 1];
74+
r = _.trys.length > 0 && _.trys[_.trys.length - 1];
6975
if (!r && (op[0] === 6 || op[0] === 2)) { _.done = 1; continue; }
7076
if (op[0] === 3 && (!r || (op[1] > r[0] && op[1] < r[3]))) { _.label = op[1]; break; }
7177
if (op[0] === 6 && _.label < r[1]) { _.label = r[1], sent = op; break; }
@@ -76,8 +82,8 @@ var __generator = (this && this.__generator) || function (thisArg, body) {
7682
}
7783
op = body.call(thisArg, _);
7884
}
79-
catch (e) { op = [6, e]; }
80-
finally { f = 0, sent = void 0; }
85+
catch (e) { op = [6, e], y = void 0; }
86+
finally { f = 0, sent = v = r = void 0; }
8187
}
8288
}
8389
return {

0 commit comments

Comments
 (0)