From 7cf958fe1f18dfabbc3ce53ab8b6d2abf91b3bd4 Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Mon, 24 Oct 2022 18:38:00 -0400 Subject: [PATCH] fix for-in enumeration containing yield in generator --- src/compiler/transformers/generators.ts | 33 ++-- .../es5-asyncFunctionForInStatements.js | 176 ++++++++++-------- .../yieldInForInInDownlevelGenerator.js | 68 +++++++ .../yieldInForInInDownlevelGenerator.symbols | 21 +++ .../yieldInForInInDownlevelGenerator.types | 28 +++ .../yieldInForInInDownlevelGenerator.ts | 10 + 6 files changed, 244 insertions(+), 92 deletions(-) create mode 100644 tests/baselines/reference/yieldInForInInDownlevelGenerator.js create mode 100644 tests/baselines/reference/yieldInForInInDownlevelGenerator.symbols create mode 100644 tests/baselines/reference/yieldInForInInDownlevelGenerator.types create mode 100644 tests/cases/compiler/yieldInForInInDownlevelGenerator.ts diff --git a/src/compiler/transformers/generators.ts b/src/compiler/transformers/generators.ts index ee19c12c9aec6..abcec891dde18 100644 --- a/src/compiler/transformers/generators.ts +++ b/src/compiler/transformers/generators.ts @@ -1516,7 +1516,6 @@ namespace ts { } function transformAndEmitForInStatement(node: ForInStatement) { - // TODO(rbuckton): Source map locations if (containsYield(node)) { // [source] // for (var p in o) { @@ -1524,32 +1523,37 @@ namespace ts { // } // // [intermediate] - // .local _a, _b, _i - // _a = []; - // for (_b in o) _a.push(_b); + // .local _b, _a, _c, _i + // _b = []; + // _a = o; + // for (_c in _a) _b.push(_c); // _i = 0; // .loop incrementLabel, endLoopLabel // .mark conditionLabel - // .brfalse endLoopLabel, (_i < _a.length) - // p = _a[_i]; + // .brfalse endLoopLabel, (_i < _b.length) + // _c = _b[_i]; + // .brfalse incrementLabel, (_c in _a) + // p = _c; // /*body*/ // .mark incrementLabel - // _b++; + // _c++; // .br conditionLabel // .endloop // .mark endLoopLabel - const keysArray = declareLocal(); // _a - const key = declareLocal(); // _b + const obj = declareLocal(); // _a + const keysArray = declareLocal(); // _b + const key = declareLocal(); // _c const keysIndex = factory.createLoopVariable(); // _i const initializer = node.initializer; hoistVariableDeclaration(keysIndex); + emitAssignment(obj, visitNode(node.expression, visitor, isExpression)); emitAssignment(keysArray, factory.createArrayLiteralExpression()); emitStatement( factory.createForInStatement( key, - visitNode(node.expression, visitor, isExpression), + obj, factory.createExpressionStatement( factory.createCallExpression( factory.createPropertyAccessExpression(keysArray, "push"), @@ -1564,10 +1568,13 @@ namespace ts { const conditionLabel = defineLabel(); const incrementLabel = defineLabel(); - const endLabel = beginLoopBlock(incrementLabel); + const endLoopLabel = beginLoopBlock(incrementLabel); markLabel(conditionLabel); - emitBreakWhenFalse(endLabel, factory.createLessThan(keysIndex, factory.createPropertyAccessExpression(keysArray, "length"))); + emitBreakWhenFalse(endLoopLabel, factory.createLessThan(keysIndex, factory.createPropertyAccessExpression(keysArray, "length"))); + + emitAssignment(key, factory.createElementAccessExpression(keysArray, keysIndex)); + emitBreakWhenFalse(incrementLabel, factory.createBinaryExpression(key, SyntaxKind.InKeyword, obj)); let variable: Expression; if (isVariableDeclarationList(initializer)) { @@ -1582,7 +1589,7 @@ namespace ts { Debug.assert(isLeftHandSideExpression(variable)); } - emitAssignment(variable, factory.createElementAccessExpression(keysArray, keysIndex)); + emitAssignment(variable, key); transformAndEmitEmbeddedStatement(node.statement); markLabel(incrementLabel); diff --git a/tests/baselines/reference/es5-asyncFunctionForInStatements.js b/tests/baselines/reference/es5-asyncFunctionForInStatements.js index ec6bfbb97e1eb..5de03d4d96c37 100644 --- a/tests/baselines/reference/es5-asyncFunctionForInStatements.js +++ b/tests/baselines/reference/es5-asyncFunctionForInStatements.js @@ -50,22 +50,24 @@ function forInStatement0() { } function forInStatement1() { return __awaiter(this, void 0, void 0, function () { - var _a, _b, _i; - return __generator(this, function (_c) { - switch (_c.label) { - case 0: - _a = []; - return [4 /*yield*/, y]; + var _a, _b, _c, _i; + return __generator(this, function (_d) { + switch (_d.label) { + case 0: return [4 /*yield*/, y]; case 1: - for (_b in _c.sent()) - _a.push(_b); + _a = _d.sent(); + _b = []; + for (_c in _a) + _b.push(_c); _i = 0; - _c.label = 2; + _d.label = 2; case 2: - if (!(_i < _a.length)) return [3 /*break*/, 4]; - x = _a[_i]; + if (!(_i < _b.length)) return [3 /*break*/, 4]; + _c = _b[_i]; + if (!(_c in _a)) return [3 /*break*/, 3]; + x = _c; z; - _c.label = 3; + _d.label = 3; case 3: _i++; return [3 /*break*/, 2]; @@ -76,22 +78,25 @@ function forInStatement1() { } function forInStatement2() { return __awaiter(this, void 0, void 0, function () { - var _a, _b, _i; - return __generator(this, function (_c) { - switch (_c.label) { + var _a, _b, _c, _i; + return __generator(this, function (_d) { + switch (_d.label) { case 0: - _a = []; - for (_b in y) - _a.push(_b); + _a = y; + _b = []; + for (_c in _a) + _b.push(_c); _i = 0; - _c.label = 1; + _d.label = 1; case 1: - if (!(_i < _a.length)) return [3 /*break*/, 4]; - x = _a[_i]; + if (!(_i < _b.length)) return [3 /*break*/, 4]; + _c = _b[_i]; + if (!(_c in _a)) return [3 /*break*/, 3]; + x = _c; return [4 /*yield*/, z]; case 2: - _c.sent(); - _c.label = 3; + _d.sent(); + _d.label = 3; case 3: _i++; return [3 /*break*/, 1]; @@ -102,22 +107,25 @@ function forInStatement2() { } function forInStatement3() { return __awaiter(this, void 0, void 0, function () { - var _a, _b, _i; - return __generator(this, function (_c) { - switch (_c.label) { + var _a, _b, _c, _i; + return __generator(this, function (_d) { + switch (_d.label) { case 0: - _a = []; - for (_b in y) - _a.push(_b); + _a = y; + _b = []; + for (_c in _a) + _b.push(_c); _i = 0; - _c.label = 1; + _d.label = 1; case 1: - if (!(_i < _a.length)) return [3 /*break*/, 4]; + if (!(_i < _b.length)) return [3 /*break*/, 4]; + _c = _b[_i]; + if (!(_c in _a)) return [3 /*break*/, 3]; return [4 /*yield*/, x]; case 2: - (_c.sent()).a = _a[_i]; + (_d.sent()).a = _c; z; - _c.label = 3; + _d.label = 3; case 3: _i++; return [3 /*break*/, 1]; @@ -128,22 +136,24 @@ function forInStatement3() { } function forInStatement4() { return __awaiter(this, void 0, void 0, function () { - var _a, _b, _i; - return __generator(this, function (_c) { - switch (_c.label) { - case 0: - _a = []; - return [4 /*yield*/, y]; + var _a, _b, _c, _i; + return __generator(this, function (_d) { + switch (_d.label) { + case 0: return [4 /*yield*/, y]; case 1: - for (_b in _c.sent()) - _a.push(_b); + _a = _d.sent(); + _b = []; + for (_c in _a) + _b.push(_c); _i = 0; - _c.label = 2; + _d.label = 2; case 2: - if (!(_i < _a.length)) return [3 /*break*/, 4]; - x.a = _a[_i]; + if (!(_i < _b.length)) return [3 /*break*/, 4]; + _c = _b[_i]; + if (!(_c in _a)) return [3 /*break*/, 3]; + x.a = _c; z; - _c.label = 3; + _d.label = 3; case 3: _i++; return [3 /*break*/, 2]; @@ -154,22 +164,25 @@ function forInStatement4() { } function forInStatement5() { return __awaiter(this, void 0, void 0, function () { - var _a, _b, _i; - return __generator(this, function (_c) { - switch (_c.label) { + var _a, _b, _c, _i; + return __generator(this, function (_d) { + switch (_d.label) { case 0: - _a = []; - for (_b in y) - _a.push(_b); + _a = y; + _b = []; + for (_c in _a) + _b.push(_c); _i = 0; - _c.label = 1; + _d.label = 1; case 1: - if (!(_i < _a.length)) return [3 /*break*/, 4]; - x.a = _a[_i]; + if (!(_i < _b.length)) return [3 /*break*/, 4]; + _c = _b[_i]; + if (!(_c in _a)) return [3 /*break*/, 3]; + x.a = _c; return [4 /*yield*/, z]; case 2: - _c.sent(); - _c.label = 3; + _d.sent(); + _d.label = 3; case 3: _i++; return [3 /*break*/, 1]; @@ -191,22 +204,24 @@ function forInStatement6() { } function forInStatement7() { return __awaiter(this, void 0, void 0, function () { - var _a, _b, _i, b; - return __generator(this, function (_c) { - switch (_c.label) { - case 0: - _a = []; - return [4 /*yield*/, y]; + var _a, _b, _c, _i, b; + return __generator(this, function (_d) { + switch (_d.label) { + case 0: return [4 /*yield*/, y]; case 1: - for (_b in _c.sent()) - _a.push(_b); + _a = _d.sent(); + _b = []; + for (_c in _a) + _b.push(_c); _i = 0; - _c.label = 2; + _d.label = 2; case 2: - if (!(_i < _a.length)) return [3 /*break*/, 4]; - b = _a[_i]; + if (!(_i < _b.length)) return [3 /*break*/, 4]; + _c = _b[_i]; + if (!(_c in _a)) return [3 /*break*/, 3]; + b = _c; z; - _c.label = 3; + _d.label = 3; case 3: _i++; return [3 /*break*/, 2]; @@ -217,22 +232,25 @@ function forInStatement7() { } function forInStatement8() { return __awaiter(this, void 0, void 0, function () { - var _a, _b, _i, c; - return __generator(this, function (_c) { - switch (_c.label) { + var _a, _b, _c, _i, c; + return __generator(this, function (_d) { + switch (_d.label) { case 0: - _a = []; - for (_b in y) - _a.push(_b); + _a = y; + _b = []; + for (_c in _a) + _b.push(_c); _i = 0; - _c.label = 1; + _d.label = 1; case 1: - if (!(_i < _a.length)) return [3 /*break*/, 4]; - c = _a[_i]; + if (!(_i < _b.length)) return [3 /*break*/, 4]; + _c = _b[_i]; + if (!(_c in _a)) return [3 /*break*/, 3]; + c = _c; return [4 /*yield*/, z]; case 2: - _c.sent(); - _c.label = 3; + _d.sent(); + _d.label = 3; case 3: _i++; return [3 /*break*/, 1]; diff --git a/tests/baselines/reference/yieldInForInInDownlevelGenerator.js b/tests/baselines/reference/yieldInForInInDownlevelGenerator.js new file mode 100644 index 0000000000000..b8d632c78d877 --- /dev/null +++ b/tests/baselines/reference/yieldInForInInDownlevelGenerator.js @@ -0,0 +1,68 @@ +//// [yieldInForInInDownlevelGenerator.ts] +// https://github.com/microsoft/TypeScript/issues/49808 +function* gen() { + var obj: any = { foo: 1, bar: 2 }; + for (var key in obj) { + yield key; + delete obj.bar; + } +} + +//// [yieldInForInInDownlevelGenerator.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 = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [op[0] & 2, 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 }; + } +}; +// https://github.com/microsoft/TypeScript/issues/49808 +function gen() { + var obj, _a, _b, _c, _i, key; + return __generator(this, function (_d) { + switch (_d.label) { + case 0: + obj = { foo: 1, bar: 2 }; + _a = obj; + _b = []; + for (_c in _a) + _b.push(_c); + _i = 0; + _d.label = 1; + case 1: + if (!(_i < _b.length)) return [3 /*break*/, 4]; + _c = _b[_i]; + if (!(_c in _a)) return [3 /*break*/, 3]; + key = _c; + return [4 /*yield*/, key]; + case 2: + _d.sent(); + delete obj.bar; + _d.label = 3; + case 3: + _i++; + return [3 /*break*/, 1]; + case 4: return [2 /*return*/]; + } + }); +} diff --git a/tests/baselines/reference/yieldInForInInDownlevelGenerator.symbols b/tests/baselines/reference/yieldInForInInDownlevelGenerator.symbols new file mode 100644 index 0000000000000..77d309b3e70d7 --- /dev/null +++ b/tests/baselines/reference/yieldInForInInDownlevelGenerator.symbols @@ -0,0 +1,21 @@ +=== tests/cases/compiler/yieldInForInInDownlevelGenerator.ts === +// https://github.com/microsoft/TypeScript/issues/49808 +function* gen() { +>gen : Symbol(gen, Decl(yieldInForInInDownlevelGenerator.ts, 0, 0)) + + var obj: any = { foo: 1, bar: 2 }; +>obj : Symbol(obj, Decl(yieldInForInInDownlevelGenerator.ts, 2, 5)) +>foo : Symbol(foo, Decl(yieldInForInInDownlevelGenerator.ts, 2, 18)) +>bar : Symbol(bar, Decl(yieldInForInInDownlevelGenerator.ts, 2, 26)) + + for (var key in obj) { +>key : Symbol(key, Decl(yieldInForInInDownlevelGenerator.ts, 3, 10)) +>obj : Symbol(obj, Decl(yieldInForInInDownlevelGenerator.ts, 2, 5)) + + yield key; +>key : Symbol(key, Decl(yieldInForInInDownlevelGenerator.ts, 3, 10)) + + delete obj.bar; +>obj : Symbol(obj, Decl(yieldInForInInDownlevelGenerator.ts, 2, 5)) + } +} diff --git a/tests/baselines/reference/yieldInForInInDownlevelGenerator.types b/tests/baselines/reference/yieldInForInInDownlevelGenerator.types new file mode 100644 index 0000000000000..691058eca397e --- /dev/null +++ b/tests/baselines/reference/yieldInForInInDownlevelGenerator.types @@ -0,0 +1,28 @@ +=== tests/cases/compiler/yieldInForInInDownlevelGenerator.ts === +// https://github.com/microsoft/TypeScript/issues/49808 +function* gen() { +>gen : () => Generator + + var obj: any = { foo: 1, bar: 2 }; +>obj : any +>{ foo: 1, bar: 2 } : { foo: number; bar: number; } +>foo : number +>1 : 1 +>bar : number +>2 : 2 + + for (var key in obj) { +>key : string +>obj : any + + yield key; +>yield key : any +>key : string + + delete obj.bar; +>delete obj.bar : boolean +>obj.bar : any +>obj : any +>bar : any + } +} diff --git a/tests/cases/compiler/yieldInForInInDownlevelGenerator.ts b/tests/cases/compiler/yieldInForInInDownlevelGenerator.ts new file mode 100644 index 0000000000000..bcbd3d0fe3fe3 --- /dev/null +++ b/tests/cases/compiler/yieldInForInInDownlevelGenerator.ts @@ -0,0 +1,10 @@ +// @target: es5 +// @lib: esnext +// https://github.com/microsoft/TypeScript/issues/49808 +function* gen() { + var obj: any = { foo: 1, bar: 2 }; + for (var key in obj) { + yield key; + delete obj.bar; + } +} \ No newline at end of file