Skip to content

Commit

Permalink
[Fix] Iterator.concat: rewrite implementation to match updated spec…
Browse files Browse the repository at this point in the history
… text

Tests from tc39/test262#4326
  • Loading branch information
ljharb committed Nov 14, 2024
1 parent 2e340da commit 1a70fd3
Show file tree
Hide file tree
Showing 2 changed files with 158 additions and 53 deletions.
88 changes: 35 additions & 53 deletions Iterator.concat/implementation.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,11 @@ var GetMethod = require('es-abstract/2024/GetMethod');
var IsArray = require('es-abstract/2024/IsArray');
var IteratorCloseAll = require('../aos/IteratorCloseAll');
var IteratorStepValue = require('es-abstract/2024/IteratorStepValue');
var ThrowCompletion = require('es-abstract/2024/ThrowCompletion');
var Type = require('es-abstract/2024/Type');

var forEach = require('es-abstract/helpers/forEach');
var getIteratorMethod = require('es-abstract/helpers/getIteratorMethod');

var callBound = require('call-bind/callBound');
var $indexOf = callBound('Array.prototype.indexOf');
var $splice = callBound('Array.prototype.splice');

var iterHelperProto = require('../IteratorHelperPrototype');

var SLOT = require('internal-slot');
Expand Down Expand Up @@ -51,65 +46,52 @@ module.exports = function concat() {
iterables[iterables.length] = { '[[OpenMethod]]': method, '[[Iterable]]': item }; // step 2.4
});

var openIters = []; // step 3
var sentinel = {};
var iterablesIndex = 0;
var iteratorRecord;
var innerAlive = false;
var closure = function () { // step 3
if (iterablesIndex >= iterables.length) {
return sentinel;
}
var iterable = iterables[iterablesIndex]; // step 3.a
if (!innerAlive) {
var iter = Call(iterable['[[OpenMethod]]'], iterable['[[Iterable]]']); // step 3.a.i
if (Type(iter) !== 'Object') {
throw new $TypeError('`Iterator.concat` iterator method did not return an object'); // step 3.a.ii
}
iteratorRecord = GetIteratorDirect(iter); // step 3.a.iii
innerAlive = true; // step 3.a.iv
}

if (innerAlive) { // step 3.a.v
var innerValue = IteratorStepValue(iteratorRecord); // step 3.a.v.1
if (iteratorRecord['[[Done]]']) { // step 3.a.v.2
innerAlive = false; // step 3.a.v.2.a
} else { // step 3.a.v.3
// 1. Let completion be Completion(Yield(innerValue)).
return innerValue; // step 3.a.v.3.a
}
}

iterablesIndex += 1;
return closure();
};

var closeIfAbrupt = function (abruptCompletion) {
if (!(abruptCompletion instanceof CompletionRecord)) {
throw new $TypeError('`abruptCompletion` must be a Completion Record');
}
if (openIters.length > 0) {
iterablesIndex = iterables.length;
innerAlive = false;
if (iteratorRecord) {
IteratorCloseAll(
openIters,
[iteratorRecord],
abruptCompletion
);
}
};

var index = 0;
var closure = function () { // step 4
if (index < iterables.length) {
// forEach(iterables, function (iterable) { // step 4.a
var iteratorRecord;
if (openIters.length === 0) {
var iterable = iterables[index];
var iter = Call(iterable['[[OpenMethod]]'], iterable['[[Iterable]]']); // step 4.a.i
if (Type(iter) !== 'Object') {
closeIfAbrupt(ThrowCompletion(new $TypeError('???'))); // step 4.a.ii
}
iteratorRecord = GetIteratorDirect(iter); // step 4.a.iii
iteratorRecord.obj = iterable['[[Iterable]]'];
openIters[openIters.length] = iteratorRecord; // step 4.a.iv
} else {
iteratorRecord = openIters[0];
}

// var innerAlive = true; // step 4.a.v
// while (innerAlive) { // step 4.a.vi
{ // eslint-disable-line no-lone-blocks
// step 4.a.vi.3.a
var innerValue;
try {
innerValue = IteratorStepValue(iteratorRecord); // step 5.b.ix.4.a
} catch (e) {
// innerAlive = false;
$splice(openIters, $indexOf(openIters, iteratorRecord), 1); // step 4.a.vi.2.a
index += 1;
closeIfAbrupt(ThrowCompletion(e)); // step 4.a.vi.3.b
}
if (iteratorRecord['[[Done]]']) {
// innerAlive = false;
$splice(openIters, $indexOf(openIters, iteratorRecord), 1);
index += 1;
return closure();
}
return innerValue; // // step 4.a.vi.3.a
}
// });
}

// return ReturnCompletion(undefined); // step 4.b
return sentinel;
};
SLOT.set(closure, '[[Sentinel]]', sentinel); // for the userland implementation
SLOT.set(closure, '[[CloseIfAbrupt]]', closeIfAbrupt); // for the userland implementation

Expand Down
123 changes: 123 additions & 0 deletions test/Iterator.concat.js
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,53 @@ module.exports = {
s2t.end();
});

st.test('test262: test/built-ins/Iterator/concat/next-method-returns-non-object', function (s2t) {
var nonObjectIterator = {
next: function () {
return null;
}
};

var iterable = {};
iterable[Symbol.iterator] = function () {
return nonObjectIterator;
};

var iterator = concat(iterable);

s2t['throws'](function () { iterator.next(); }, TypeError);

s2t.end();
});

st.test('test262: test/built-ins/Iterator/concat/next-method-returns-throwing-done', { skip: !hasPropertyDescriptors }, function (s2t) {
var throwingIterator = {
next: function () {
var result = { done: null, value: 1 };
Object.defineProperty(result, 'done', {
get: function () {
throw new EvalError();
}
});
return result;
},
'return': function () {
throw new Error();
}
};

var iterable = {};
iterable[Symbol.iterator] = function () {
return throwingIterator;
};

var iterator = concat(iterable);

s2t['throws'](function () { iterator.next(); }, EvalError);

s2t.end();
});

st.test('test262: test/built-ins/Iterator/concat/next-method-returns-throwing-value-done', { skip: !hasPropertyDescriptors }, function (s2t) {
function ReturnCalledError() {}
function ValueGetterError() {}
Expand Down Expand Up @@ -312,6 +359,53 @@ module.exports = {
s2t.end();
});

st.test('test262: test/built-ins/Iterator/concat/next-method-returns-throwing-value', { skip: !hasPropertyDescriptors }, function (s2t) {
var throwingIterator = {
next: function () {
var result = { value: null, done: false };
Object.defineProperty(result, 'value', {
get: function () {
throw new EvalError();
}
});
return result;
},
'return': function () {
throw new Error();
}
};

var iterable = {};
iterable[Symbol.iterator] = function () {
return throwingIterator;
};

var iterator = concat(iterable);

s2t['throws'](function () { iterator.next(); }, EvalError);

s2t.end();
});

st.test('test262: test/built-ins/Iterator/concat/next-method-throws', function (s2t) {
var throwingIterator = {
next: function () {
throw new EvalError();
}
};

var iterable = {};
iterable[Symbol.iterator] = function () {
return throwingIterator;
};

var iterator = concat(iterable);

s2t['throws'](function () { iterator.next(); }, EvalError);

s2t.end();
});

st.test('test262: test/built-ins/Iterator/concat/return-is-not-forwarded-after-exhaustion', function (s2t) {
var testIterator1 = {
next: function () {
Expand Down Expand Up @@ -411,6 +505,35 @@ module.exports = {
s2t.end();
});

st.test('test262: test/built-ins/Iterator/concat/throws-typeerror-when-generator-is-running-next', function (s2t) {
var enterCount = 0;

var iterator;

var testIterator1 = {
next: function () {
enterCount += 1;
iterator.next();
return { done: false };
}
};

var iterable = {};
iterable[Symbol.iterator] = function () {
return testIterator1;
};

iterator = concat(iterable);

s2t.equal(enterCount, 0);

s2t['throws'](function () { iterator.next(); }, TypeError);

s2t.equal(enterCount, 1);

s2t.end();
});

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

0 comments on commit 1a70fd3

Please sign in to comment.