Skip to content

Commit

Permalink
refactor(ES2018): Convert Iterator operations to use IteratorRecord
Browse files Browse the repository at this point in the history
BREAKING CHANGE: This is backwards incompatible, as ES2017 and older operated
directly on the `Iterator` instance.
  • Loading branch information
ExE-Boss committed Nov 6, 2019
1 parent e42f371 commit 4523a17
Show file tree
Hide file tree
Showing 2 changed files with 71 additions and 7 deletions.
58 changes: 53 additions & 5 deletions es2018.js
Original file line number Diff line number Diff line change
Expand Up @@ -187,17 +187,65 @@ var ES2018 = assign(assign({}, ES2017), {
throw new $TypeError('iterator must return an object');
}

return iterator;

// TODO: This should return an IteratorRecord
/*
var nextMethod = this.GetV(iterator, 'next');
return {
'[[Iterator]]': iterator,
'[[NextMethod]]': nextMethod,
'[[Done]]': false
};
*/
},

// https://ecma-international.org/ecma-262/9.0/#sec-iteratornext
IteratorNext: function IteratorNext(iteratorRecord, value) {
var result = this.Call(
iteratorRecord['[[NextMethod]]'],
iteratorRecord['[[Iterator]]'],
arguments.length < 2 ? [] : [value]
);
if (this.Type(result) !== 'Object') {
throw new $TypeError('iterator next must return an object');
}
return result;
},

// https://ecma-international.org/ecma-262/9.0/#sec-iteratorclose
IteratorClose: function IteratorClose(iteratorRecord, completion) {
if (this.Type(iteratorRecord['[[Iterator]]']) !== 'Object') {
throw new $TypeError('Assertion failed: Type(iteratorRecord.[[Iterator]]) is not Object');
}
if (!this.IsCallable(completion)) {
throw new $TypeError('Assertion failed: completion is not a thunk for a Completion Record');
}
var completionThunk = completion;

var iterator = iteratorRecord['[[Iterator]]'];
var iteratorReturn = this.GetMethod(iterator, 'return');

if (typeof iteratorReturn === 'undefined') {
return completionThunk();
}

var completionRecord;
try {
var innerResult = this.Call(iteratorReturn, iterator, []);
} catch (e) {
// if we hit here, then "e" is the innerResult completion that needs re-throwing

// if the completion is of type "throw", this will throw.
completionRecord = completionThunk();
completionThunk = null; // ensure it's not called twice.

// if not, then return the innerResult completion
throw e;
}
completionRecord = completionThunk(); // if innerResult worked, then throw if the completion does
completionThunk = null; // ensure it's not called twice.

if (this.Type(innerResult) !== 'Object') {
throw new $TypeError('iterator .return must return an object');
}

return completionRecord;
},

// https://www.ecma-international.org/ecma-262/9.0/#sec-iterabletolist
Expand Down
20 changes: 18 additions & 2 deletions test/tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,21 @@ var getArraySubclassWithSpeciesConstructor = function getArraySubclass(speciesCo
return Bar;
};

var testIterator = function (t, iterator, expected) {
/**
* @template T
* @param {tape.Test} t
* @param {Iterator<T> | {'[[Iterator]]': Iterator<T>}} iteratorOrIteratorRecord
* @param {T[]} expected
*/
var testIterator = function (t, iteratorOrIteratorRecord, expected) {
/** @type {Iterator<T>} */
var iterator;
if ('[[Iterator]]' in iteratorOrIteratorRecord) {
iterator = iteratorOrIteratorRecord['[[Iterator]]'];
} else {
iterator = iteratorOrIteratorRecord;
}

var resultCount = 0;
var result;
while (result = iterator.next(), !result.done) { // eslint-disable-line no-sequences
Expand Down Expand Up @@ -3774,7 +3788,9 @@ var es2018 = function ES2018(ES, ops, expectedMissing, skips) {
return it;
};

st.equal(ES.GetIterator(obj, 'async'), it);
var iteratorRecord = ES.GetIterator(obj, 'async');
st.equal(iteratorRecord['[[Iterator]]'], it);
st.equal(iteratorRecord['[[NextMethod]]'], it.next);

st.end();
});
Expand Down

0 comments on commit 4523a17

Please sign in to comment.