Skip to content

Commit e642691

Browse files
authored
Merge pull request microsoft#15521 from Microsoft/fix15471
Fix over agressive async delegation
2 parents c1b180d + 729c1e1 commit e642691

11 files changed

+768
-814
lines changed

src/compiler/transformers/esnext.ts

Lines changed: 64 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -106,15 +106,11 @@ namespace ts {
106106
}
107107
}
108108

109-
function visitAwaitExpression(node: AwaitExpression) {
109+
function visitAwaitExpression(node: AwaitExpression): Expression {
110110
if (enclosingFunctionFlags & FunctionFlags.Async && enclosingFunctionFlags & FunctionFlags.Generator) {
111-
const expression = visitNode(node.expression, visitor, isExpression);
112111
return setOriginalNode(
113112
setTextRange(
114-
createYield(
115-
/*asteriskToken*/ undefined,
116-
createArrayLiteral([createLiteral("await"), expression])
117-
),
113+
createYield(createAwaitHelper(context, visitNode(node.expression, visitor, isExpression))),
118114
/*location*/ node
119115
),
120116
node
@@ -124,18 +120,26 @@ namespace ts {
124120
}
125121

126122
function visitYieldExpression(node: YieldExpression) {
127-
if (enclosingFunctionFlags & FunctionFlags.Async && enclosingFunctionFlags & FunctionFlags.Generator) {
123+
if (enclosingFunctionFlags & FunctionFlags.Async && enclosingFunctionFlags & FunctionFlags.Generator && node.asteriskToken) {
128124
const expression = visitNode(node.expression, visitor, isExpression);
129-
return updateYield(
130-
node,
131-
node.asteriskToken,
132-
node.asteriskToken
133-
? createAsyncDelegatorHelper(context, expression, expression)
134-
: createArrayLiteral(
135-
expression
136-
? [createLiteral("yield"), expression]
137-
: [createLiteral("yield")]
138-
)
125+
return setOriginalNode(
126+
setTextRange(
127+
createYield(
128+
createAwaitHelper(context,
129+
updateYield(
130+
node,
131+
node.asteriskToken,
132+
createAsyncDelegatorHelper(
133+
context,
134+
createAsyncValuesHelper(context, expression, expression),
135+
expression
136+
)
137+
)
138+
)
139+
),
140+
node
141+
),
142+
node
139143
);
140144
}
141145
return visitEachChild(node, visitor, context);
@@ -347,23 +351,22 @@ namespace ts {
347351
);
348352
}
349353

354+
function awaitAsYield(expression: Expression) {
355+
return createYield(/*asteriskToken*/ undefined, enclosingFunctionFlags & FunctionFlags.Generator ? createAwaitHelper(context, expression) : expression);
356+
}
357+
350358
function transformForAwaitOfStatement(node: ForOfStatement, outermostLabeledStatement: LabeledStatement) {
351359
const expression = visitNode(node.expression, visitor, isExpression);
352360
const iterator = isIdentifier(expression) ? getGeneratedNameForNode(expression) : createTempVariable(/*recordTempVariable*/ undefined);
353361
const result = isIdentifier(expression) ? getGeneratedNameForNode(iterator) : createTempVariable(/*recordTempVariable*/ undefined);
354362
const errorRecord = createUniqueName("e");
355363
const catchVariable = getGeneratedNameForNode(errorRecord);
356364
const returnMethod = createTempVariable(/*recordTempVariable*/ undefined);
357-
const values = createAsyncValuesHelper(context, expression, /*location*/ node.expression);
358-
const next = createYield(
359-
/*asteriskToken*/ undefined,
360-
enclosingFunctionFlags & FunctionFlags.Generator
361-
? createArrayLiteral([
362-
createLiteral("await"),
363-
createCall(createPropertyAccess(iterator, "next" ), /*typeArguments*/ undefined, [])
364-
])
365-
: createCall(createPropertyAccess(iterator, "next" ), /*typeArguments*/ undefined, [])
366-
);
365+
const callValues = createAsyncValuesHelper(context, expression, /*location*/ node.expression);
366+
const callNext = createCall(createPropertyAccess(iterator, "next" ), /*typeArguments*/ undefined, []);
367+
const getDone = createPropertyAccess(result, "done");
368+
const getValue = createPropertyAccess(result, "value");
369+
const callReturn = createFunctionCall(returnMethod, iterator, []);
367370

368371
hoistVariableDeclaration(errorRecord);
369372
hoistVariableDeclaration(returnMethod);
@@ -374,16 +377,19 @@ namespace ts {
374377
/*initializer*/ setEmitFlags(
375378
setTextRange(
376379
createVariableDeclarationList([
377-
setTextRange(createVariableDeclaration(iterator, /*type*/ undefined, values), node.expression),
378-
createVariableDeclaration(result, /*type*/ undefined, next)
380+
setTextRange(createVariableDeclaration(iterator, /*type*/ undefined, callValues), node.expression),
381+
createVariableDeclaration(result)
379382
]),
380383
node.expression
381384
),
382385
EmitFlags.NoHoisting
383386
),
384-
/*condition*/ createLogicalNot(createPropertyAccess(result, "done")),
385-
/*incrementor*/ createAssignment(result, next),
386-
/*statement*/ convertForOfStatementHead(node, createPropertyAccess(result, "value"))
387+
/*condition*/ createComma(
388+
createAssignment(result, awaitAsYield(callNext)),
389+
createLogicalNot(getDone)
390+
),
391+
/*incrementor*/ undefined,
392+
/*statement*/ convertForOfStatementHead(node, awaitAsYield(getValue))
387393
),
388394
/*location*/ node
389395
),
@@ -421,26 +427,14 @@ namespace ts {
421427
createLogicalAnd(
422428
createLogicalAnd(
423429
result,
424-
createLogicalNot(
425-
createPropertyAccess(result, "done")
426-
)
430+
createLogicalNot(getDone)
427431
),
428432
createAssignment(
429433
returnMethod,
430434
createPropertyAccess(iterator, "return")
431435
)
432436
),
433-
createStatement(
434-
createYield(
435-
/*asteriskToken*/ undefined,
436-
enclosingFunctionFlags & FunctionFlags.Generator
437-
? createArrayLiteral([
438-
createLiteral("await"),
439-
createFunctionCall(returnMethod, iterator, [])
440-
])
441-
: createFunctionCall(returnMethod, iterator, [])
442-
)
443-
)
437+
createStatement(awaitAsYield(callReturn))
444438
),
445439
EmitFlags.SingleLine
446440
)
@@ -880,27 +874,39 @@ namespace ts {
880874
);
881875
}
882876

877+
const awaitHelper: EmitHelper = {
878+
name: "typescript:await",
879+
scoped: false,
880+
text: `
881+
var __await = (this && this.__await) || function (v) { return this instanceof __await ? (this.v = v, this) : new __await(v); }
882+
`
883+
};
884+
885+
function createAwaitHelper(context: TransformationContext, expression: Expression) {
886+
context.requestEmitHelper(awaitHelper);
887+
return createCall(createIdentifier("__await"), /*typeArguments*/ undefined, [expression]);
888+
}
889+
883890
const asyncGeneratorHelper: EmitHelper = {
884891
name: "typescript:asyncGenerator",
885892
scoped: false,
886893
text: `
887894
var __asyncGenerator = (this && this.__asyncGenerator) || function (thisArg, _arguments, generator) {
888895
if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
889-
var g = generator.apply(thisArg, _arguments || []), q = [], c, i;
890-
return i = { next: verb("next"), "throw": verb("throw"), "return": verb("return") }, i[Symbol.asyncIterator] = function () { return this; }, i;
891-
function verb(n) { return function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]), next(); }); }; }
892-
function next() { if (!c && q.length) resume((c = q.shift())[0], c[1]); }
893-
function resume(n, v) { try { step(g[n](v)); } catch (e) { settle(c[3], e); } }
894-
function step(r) { r.done ? settle(c[2], r) : Promise.resolve(r.value[1]).then(r.value[0] === "yield" ? send : fulfill, reject); }
895-
function send(value) { settle(c[2], { value: value, done: false }); }
896+
var g = generator.apply(thisArg, _arguments || []), i, q = [];
897+
return i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i;
898+
function verb(n) { if (g[n]) i[n] = function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]) > 1 || resume(n, v); }); }; }
899+
function resume(n, v) { try { step(g[n](v)); } catch (e) { settle(q[0][3], e); } }
900+
function step(r) { r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r); }
896901
function fulfill(value) { resume("next", value); }
897902
function reject(value) { resume("throw", value); }
898-
function settle(f, v) { c = void 0, f(v), next(); }
903+
function settle(f, v) { if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]); }
899904
};
900905
`
901906
};
902907

903908
function createAsyncGeneratorHelper(context: TransformationContext, generatorFunc: FunctionExpression) {
909+
context.requestEmitHelper(awaitHelper);
904910
context.requestEmitHelper(asyncGeneratorHelper);
905911

906912
// Mark this node as originally an async function
@@ -922,16 +928,16 @@ namespace ts {
922928
scoped: false,
923929
text: `
924930
var __asyncDelegator = (this && this.__asyncDelegator) || function (o) {
925-
var i = { next: verb("next"), "throw": verb("throw", function (e) { throw e; }), "return": verb("return", function (v) { return { value: v, done: true }; }) }, p;
926-
return o = __asyncValues(o), i[Symbol.iterator] = function () { return this; }, i;
927-
function verb(n, f) { return function (v) { return v = p && n === "throw" ? f(v) : p && v.done ? v : { value: p ? ["yield", v.value] : ["await", (o[n] || f).call(o, v)], done: false }, p = !p, v; }; }
931+
var i, p;
932+
return i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.iterator] = function () { return this; }, i;
933+
function verb(n) { if (o[n]) i[n] = function (v) { return (p = !p) ? { value: __await(o[n](v)), done: n === "return" } : v; }; }
928934
};
929935
`
930936
};
931937

932938
function createAsyncDelegatorHelper(context: TransformationContext, expression: Expression, location?: TextRange) {
939+
context.requestEmitHelper(awaitHelper);
933940
context.requestEmitHelper(asyncDelegator);
934-
context.requestEmitHelper(asyncValues);
935941
return setTextRange(
936942
createCall(
937943
getHelperName("__asyncDelegator"),

0 commit comments

Comments
 (0)