fix for-in enumeration containing yield in generator (#51295)

This commit is contained in:
Ron Buckton 2022-10-28 15:09:10 -04:00 committed by GitHub
parent 3d2b4017eb
commit 7f8426f4df
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 244 additions and 92 deletions

View File

@ -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);

View File

@ -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];

View File

@ -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*/];
}
});
}

View File

@ -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))
}
}

View File

@ -0,0 +1,28 @@
=== tests/cases/compiler/yieldInForInInDownlevelGenerator.ts ===
// https://github.com/microsoft/TypeScript/issues/49808
function* gen() {
>gen : () => Generator<string, void, unknown>
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
}
}

View File

@ -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;
}
}