Skip to content

Commit

Permalink
[Fix] flatMap: close the inner iterator when applicable
Browse files Browse the repository at this point in the history
  • Loading branch information
ljharb committed May 2, 2023
1 parent 660e157 commit 4dc94e0
Show file tree
Hide file tree
Showing 2 changed files with 72 additions and 25 deletions.
62 changes: 37 additions & 25 deletions Iterator.prototype.flatMap/implementation.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,71 +32,83 @@ module.exports = function flatMap(mapper) {

var iterated = GetIteratorDirect(O); // step 4

var sentinel = { sentinel: true };
var innerIterator = sentinel;

var closeIfAbrupt = function (abruptCompletion) {
if (!(abruptCompletion instanceof CompletionRecord)) {
throw new $TypeError('`abruptCompletion` must be a Completion Record');
}
IteratorClose(
iterated,
abruptCompletion
);
try {
if (innerIterator !== sentinel) {
IteratorClose(
innerIterator,
abruptCompletion
);
}
} finally {
innerIterator = sentinel;

IteratorClose(
iterated,
abruptCompletion
);
}
};

var sentinel = { sentinel: true };
var counter = 0; // step 6.a
var innerIterator = sentinel;
var counter = 0; // step 5.a
var innerAlive = false;
var closure = function () {
// while (true) { // step 6.b
// while (true) { // step 5.b
if (innerIterator === sentinel) {
var next = IteratorStep(iterated); // step 6.b.i
var next = IteratorStep(iterated); // step 5.b.i
if (!next) {
innerAlive = false;
innerIterator = sentinel;
// return void undefined; // step 6.b.ii
// return void undefined; // step 5.b.ii
return sentinel;
}
var value = IteratorValue(next); // step 6.b.iii
var value = IteratorValue(next); // step 5.b.iii
}

if (innerIterator === sentinel) {
innerAlive = true; // step 6.b.viii
innerAlive = true; // step 5.b.viii
try {
var mapped = Call(mapper, void undefined, [value, counter]); // step 6.b.iv
// yield mapped // step 6.b.vi
innerIterator = GetIteratorFlattenable(mapped); // step 6.b.vi
var mapped = Call(mapper, void undefined, [value, counter]); // step 5.b.iv
// yield mapped // step 5.b.vi
innerIterator = GetIteratorFlattenable(mapped); // step 5.b.vi
} catch (e) {
innerAlive = false;
innerIterator = sentinel;
closeIfAbrupt(ThrowCompletion(e)); // steps 6.b.v, 6.b.vii
closeIfAbrupt(ThrowCompletion(e)); // steps 5.b.v, 5.b.vii
} finally {
counter += 1; // step 6.b.x
counter += 1; // step 5.b.x
}
}
// while (innerAlive) { // step 6.b.ix
// while (innerAlive) { // step 5.b.ix
if (innerAlive) {
var innerNext;
try {
innerNext = IteratorStep(innerIterator); // step 6.b.ix.1
innerNext = IteratorStep(innerIterator); // step 5.b.ix.1
} catch (e) {
innerIterator = sentinel;
closeIfAbrupt(ThrowCompletion(e)); // step 6.b.ix.2
closeIfAbrupt(ThrowCompletion(e)); // step 5.b.ix.2
}
if (!innerNext) {
innerAlive = false; // step 6.b.ix.3.a
innerAlive = false; // step 5.b.ix.3.a
innerIterator = sentinel;
return closure();
}
// step 6.b.ix.4
// step 5.b.ix.4
var innerValue;
try {
innerValue = IteratorValue(innerNext); // step 6.b.ix.4.a
innerValue = IteratorValue(innerNext); // step 5.b.ix.4.a
} catch (e) {
innerAlive = false;
innerIterator = sentinel;
closeIfAbrupt(ThrowCompletion(e)); // step 6.b.ix.4.b
closeIfAbrupt(ThrowCompletion(e)); // step 5.b.ix.4.b
}
return innerValue; // step 6.b.ix.4.c
return innerValue; // step 5.b.ix.4.c
}
// }
// return void undefined;
Expand Down
35 changes: 35 additions & 0 deletions test/Iterator.prototype.flatMap.js
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,41 @@ module.exports = {
}), [0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9], st, 'test262: test/built-ins/Iterator/prototype/flatMap/mapper-args');
st.deepEqual(counts, [0, 1, 2, 3, 4], 'count values are as expected');

st.test('return protocol', function (s2t) {
var returnCount = 0;

var iter = flatMap([0][Symbol.iterator](), function () {
return {
next: function next() {
return {
done: false,
value: 1
};
},
'return': function () {
returnCount += 1;
return {};
}
};
});
s2t.equal(returnCount, 0, '`return` is not called yet');

s2t.deepEqual(iter.next(), {
done: false,
value: 1
});

s2t.equal(returnCount, 0, '`return` is not called after first yield');

iter['return']();
s2t.equal(returnCount, 1, '`return` is called when iterator return is called');

iter['return']();
s2t.equal(returnCount, 1, '`return` is not called again when iterator return is called again');

s2t.end();
});

st.end();
});
},
Expand Down

0 comments on commit 4dc94e0

Please sign in to comment.