From 6c176138ca7609b0abfa06cc3756339d2c7f8a36 Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Wed, 19 Apr 2017 17:06:12 -0700 Subject: [PATCH] Fix loop block scope binding in generator --- src/compiler/transformers/es2015.ts | 9 +- ...blockScopedBindingsInDownlevelGenerator.js | 93 +++++++++++++++++++ ...ScopedBindingsInDownlevelGenerator.symbols | 14 +++ ...ckScopedBindingsInDownlevelGenerator.types | 22 +++++ ...blockScopedBindingsInDownlevelGenerator.ts | 9 ++ 5 files changed, 142 insertions(+), 5 deletions(-) create mode 100644 tests/baselines/reference/blockScopedBindingsInDownlevelGenerator.js create mode 100644 tests/baselines/reference/blockScopedBindingsInDownlevelGenerator.symbols create mode 100644 tests/baselines/reference/blockScopedBindingsInDownlevelGenerator.types create mode 100644 tests/cases/compiler/blockScopedBindingsInDownlevelGenerator.ts diff --git a/src/compiler/transformers/es2015.ts b/src/compiler/transformers/es2015.ts index f8ecf6bdcff..3ac5dc5d878 100644 --- a/src/compiler/transformers/es2015.ts +++ b/src/compiler/transformers/es2015.ts @@ -2714,9 +2714,8 @@ namespace ts { loopBody = createBlock([loopBody], /*multiline*/ true); } - const isAsyncBlockContainingAwait = - hierarchyFacts & HierarchyFacts.AsyncFunctionBody - && (node.statement.transformFlags & TransformFlags.ContainsYield) !== 0; + const containsYield = (node.statement.transformFlags & TransformFlags.ContainsYield) !== 0; + const isAsyncBlockContainingAwait = containsYield && (hierarchyFacts & HierarchyFacts.AsyncFunctionBody) !== 0; let loopBodyFlags: EmitFlags = 0; if (currentState.containsLexicalThis) { @@ -2739,7 +2738,7 @@ namespace ts { setEmitFlags( createFunctionExpression( /*modifiers*/ undefined, - isAsyncBlockContainingAwait ? createToken(SyntaxKind.AsteriskToken) : undefined, + containsYield ? createToken(SyntaxKind.AsteriskToken) : undefined, /*name*/ undefined, /*typeParameters*/ undefined, loopParameters, @@ -2833,7 +2832,7 @@ namespace ts { )); } - const convertedLoopBodyStatements = generateCallToConvertedLoop(functionName, loopParameters, currentState, isAsyncBlockContainingAwait); + const convertedLoopBodyStatements = generateCallToConvertedLoop(functionName, loopParameters, currentState, containsYield); let loop: Statement; if (convert) { diff --git a/tests/baselines/reference/blockScopedBindingsInDownlevelGenerator.js b/tests/baselines/reference/blockScopedBindingsInDownlevelGenerator.js new file mode 100644 index 00000000000..be2bcd0bb0c --- /dev/null +++ b/tests/baselines/reference/blockScopedBindingsInDownlevelGenerator.js @@ -0,0 +1,93 @@ +//// [blockScopedBindingsInDownlevelGenerator.ts] +function* a() { + for (const i of [1,2,3]) { + (() => i)() + yield i + } +} + +//// [blockScopedBindingsInDownlevelGenerator.js] +var __generator = (this && this.__generator) || function (thisArg, body) { + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; + return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError("Generator is already executing."); + while (_) try { + if (f = 1, y && (t = y[op[0] & 2 ? "return" : op[0] ? "throw" : "next"]) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [0, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; + } +}; +var __values = (this && this.__values) || function (o) { + var m = typeof Symbol === "function" && o[Symbol.iterator], i = 0; + if (m) return m.call(o); + return { + next: function () { + if (o && i >= o.length) o = void 0; + return { value: o && o[i++], done: !o }; + } + }; +}; +function a() { + var _loop_1, _a, _b, i, e_1_1, e_1, _c; + return __generator(this, function (_d) { + switch (_d.label) { + case 0: + _loop_1 = function (i) { + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + (function () { return i; })(); + return [4 /*yield*/, i]; + case 1: + _a.sent(); + return [2 /*return*/]; + } + }); + }; + _d.label = 1; + case 1: + _d.trys.push([1, 6, 7, 8]); + _a = __values([1, 2, 3]), _b = _a.next(); + _d.label = 2; + case 2: + if (!!_b.done) return [3 /*break*/, 5]; + i = _b.value; + return [5 /*yield**/, _loop_1(i)]; + case 3: + _d.sent(); + _d.label = 4; + case 4: + _b = _a.next(); + return [3 /*break*/, 2]; + case 5: return [3 /*break*/, 8]; + case 6: + e_1_1 = _d.sent(); + e_1 = { error: e_1_1 }; + return [3 /*break*/, 8]; + case 7: + try { + if (_b && !_b.done && (_c = _a.return)) _c.call(_a); + } + finally { if (e_1) throw e_1.error; } + return [7 /*endfinally*/]; + case 8: return [2 /*return*/]; + } + }); +} diff --git a/tests/baselines/reference/blockScopedBindingsInDownlevelGenerator.symbols b/tests/baselines/reference/blockScopedBindingsInDownlevelGenerator.symbols new file mode 100644 index 00000000000..8caaed47a64 --- /dev/null +++ b/tests/baselines/reference/blockScopedBindingsInDownlevelGenerator.symbols @@ -0,0 +1,14 @@ +=== tests/cases/compiler/blockScopedBindingsInDownlevelGenerator.ts === +function* a() { +>a : Symbol(a, Decl(blockScopedBindingsInDownlevelGenerator.ts, 0, 0)) + + for (const i of [1,2,3]) { +>i : Symbol(i, Decl(blockScopedBindingsInDownlevelGenerator.ts, 1, 12)) + + (() => i)() +>i : Symbol(i, Decl(blockScopedBindingsInDownlevelGenerator.ts, 1, 12)) + + yield i +>i : Symbol(i, Decl(blockScopedBindingsInDownlevelGenerator.ts, 1, 12)) + } +} diff --git a/tests/baselines/reference/blockScopedBindingsInDownlevelGenerator.types b/tests/baselines/reference/blockScopedBindingsInDownlevelGenerator.types new file mode 100644 index 00000000000..cf93e7c2f4d --- /dev/null +++ b/tests/baselines/reference/blockScopedBindingsInDownlevelGenerator.types @@ -0,0 +1,22 @@ +=== tests/cases/compiler/blockScopedBindingsInDownlevelGenerator.ts === +function* a() { +>a : () => IterableIterator + + for (const i of [1,2,3]) { +>i : number +>[1,2,3] : number[] +>1 : 1 +>2 : 2 +>3 : 3 + + (() => i)() +>(() => i)() : number +>(() => i) : () => number +>() => i : () => number +>i : number + + yield i +>yield i : any +>i : number + } +} diff --git a/tests/cases/compiler/blockScopedBindingsInDownlevelGenerator.ts b/tests/cases/compiler/blockScopedBindingsInDownlevelGenerator.ts new file mode 100644 index 00000000000..ff2bb87149b --- /dev/null +++ b/tests/cases/compiler/blockScopedBindingsInDownlevelGenerator.ts @@ -0,0 +1,9 @@ +// @target: es5 +// @downlevelIteration: true +// @lib: es2015 +function* a() { + for (const i of [1,2,3]) { + (() => i)() + yield i + } +} \ No newline at end of file