diff options
Diffstat (limited to '')
-rw-r--r-- | js/src/builtin/Iterator.js | 681 |
1 files changed, 681 insertions, 0 deletions
diff --git a/js/src/builtin/Iterator.js b/js/src/builtin/Iterator.js new file mode 100644 index 0000000000..2fa9bef38e --- /dev/null +++ b/js/src/builtin/Iterator.js @@ -0,0 +1,681 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +function IteratorIdentity() { + return this; +} + +/* ECMA262 7.2.7 */ +function IteratorNext(iteratorRecord, value) { + // Steps 1-2. + const result = (arguments.length < 2 + ? callContentFunction(iteratorRecord.nextMethod, iteratorRecord.iterator) + : callContentFunction(iteratorRecord.nextMethod, iteratorRecord.iterator, value)); + // Step 3. + if (!IsObject(result)) { + ThrowTypeError(JSMSG_OBJECT_REQUIRED, result); + } + // Step 4. + return result; +} + +/* ECMA262 7.4.6 */ +function IteratorClose(iteratorRecord, value) { + // Step 3. + const iterator = iteratorRecord.iterator; + // Step 4. + const returnMethod = iterator.return; + // Step 5. + if (returnMethod !== undefined && returnMethod !== null) { + const result = callContentFunction(returnMethod, iterator); + // Step 8. + if (!IsObject(result)) { + ThrowTypeError(JSMSG_OBJECT_REQUIRED, DecompileArg(0, result)); + } + } + // Step 5b & 9. + return value; +} + +/* Iterator Helpers proposal 1.1.1 */ +function GetIteratorDirect(obj) { + // Step 1. + if (!IsObject(obj)) { + ThrowTypeError(JSMSG_OBJECT_REQUIRED, DecompileArg(0, obj)); + } + + // Step 2. + const nextMethod = obj.next; + // Step 3. + if (!IsCallable(nextMethod)) { + ThrowTypeError(JSMSG_NOT_FUNCTION, DecompileArg(0, nextMethod)); + } + + // Steps 4-5. + return { + iterator: obj, + nextMethod, + done: false, + }; +} + +function GetIteratorDirectWrapper(obj) { + // Step 1. + if (!IsObject(obj)) { + ThrowTypeError(JSMSG_OBJECT_REQUIRED, obj); + } + + // Step 2. + const nextMethod = obj.next; + // Step 3. + if (!IsCallable(nextMethod)) { + ThrowTypeError(JSMSG_NOT_FUNCTION, nextMethod); + } + + // Steps 4-5. + return { + // Use a named function expression instead of a method definition, so + // we don't create an inferred name for this function at runtime. + [std_iterator]: function IteratorMethod() { + return this; + }, + next(value) { + return callContentFunction(nextMethod, obj, value); + }, + return(value) { + const returnMethod = obj.return; + if (returnMethod !== undefined && returnMethod !== null) { + return callContentFunction(returnMethod, obj, value); + } + return {done: true, value}; + }, + }; +} + +/* Iterator Helpers proposal 1.1.2 */ +function IteratorStep(iteratorRecord, value) { + // Steps 2-3. + let result; + if (arguments.length === 2) { + result = callContentFunction( + iteratorRecord.nextMethod, + iteratorRecord.iterator, + value + ); + } else { + result = callContentFunction( + iteratorRecord.nextMethod, + iteratorRecord.iterator + ); + } + + // IteratorNext Step 3. + if (!IsObject(result)) { + ThrowTypeError(JSMSG_OBJECT_REQUIRED, DecompileArg(0, result)); + } + + // Steps 4-6. + return result.done ? false : result; +} + +/* Iterator Helpers proposal 2.1.3.3.1 */ +function IteratorFrom(O) { + // Step 1. + const usingIterator = O[std_iterator]; + + let iteratorRecord; + // Step 2. + if (usingIterator !== undefined && usingIterator !== null) { + // Step a. + // Inline call to GetIterator. + const iterator = callContentFunction(usingIterator, O); + iteratorRecord = GetIteratorDirect(iterator); + // Step b-c. + if (iteratorRecord.iterator instanceof GetBuiltinConstructor("Iterator")) { + return iteratorRecord.iterator; + } + } else { + // Step 3. + iteratorRecord = GetIteratorDirect(O); + } + + // Step 4. + const wrapper = NewWrapForValidIterator(); + // Step 5. + UnsafeSetReservedSlot(wrapper, ITERATED_SLOT, iteratorRecord); + // Step 6. + return wrapper; +} + +/* Iterator Helpers proposal 2.1.3.3.1.1.1 */ +function WrapForValidIteratorNext(value) { + // Step 1-2. + let O; + if (!IsObject(this) || (O = GuardToWrapForValidIterator(this)) === null) { + if (arguments.length === 0) { + return callFunction(CallWrapForValidIteratorMethodIfWrapped, this, + "WrapForValidIteratorNext"); + } + return callFunction(CallWrapForValidIteratorMethodIfWrapped, this, + value, "WrapForValidIteratorNext"); + } + const iterated = UnsafeGetReservedSlot(O, ITERATED_SLOT); + // Step 3. + let result; + if (arguments.length === 0) { + result = callContentFunction(iterated.nextMethod, iterated.iterator); + } else { // Step 4. + result = callContentFunction(iterated.nextMethod, iterated.iterator, value); + } + // Inlined from IteratorNext. + if (!IsObject(result)) + ThrowTypeError(JSMSG_OBJECT_REQUIRED, DecompileArg(0, result)); + return result; +} + +/* Iterator Helpers proposal 2.1.3.3.1.1.2 */ +function WrapForValidIteratorReturn(value) { + // Step 1-2. + let O; + if (!IsObject(this) || (O = GuardToWrapForValidIterator(this)) === null) { + return callFunction(CallWrapForValidIteratorMethodIfWrapped, this, + value, "WrapForValidIteratorReturn"); + } + const iterated = UnsafeGetReservedSlot(O, ITERATED_SLOT); + + // Step 3. + // Inline call to IteratorClose. + const iterator = iterated.iterator; + const returnMethod = iterator.return; + if (returnMethod !== undefined && returnMethod !== null) { + let innerResult = callContentFunction(returnMethod, iterator); + if (!IsObject(innerResult)) { + ThrowTypeError(JSMSG_OBJECT_REQUIRED, DecompileArg(0, innerResult)); + } + } + // Step 4. + return { + done: true, + value, + }; +} + +/* Iterator Helpers proposal 2.1.3.3.1.1.3 */ +function WrapForValidIteratorThrow(value) { + // Step 1-2. + let O; + if (!IsObject(this) || (O = GuardToWrapForValidIterator(this)) === null) { + return callFunction(CallWrapForValidIteratorMethodIfWrapped, this, + value, "WrapForValidIteratorThrow"); + } + const iterated = UnsafeGetReservedSlot(O, ITERATED_SLOT); + // Step 3. + const iterator = iterated.iterator; + // Step 4. + const throwMethod = iterator.throw; + // Step 5. + if (throwMethod === undefined || throwMethod === null) { + throw value; + } + // Step 6. + return callContentFunction(throwMethod, iterator, value); +} + +/* Iterator Helper object prototype methods. */ +function IteratorHelperNext(value) { + let O; + if (!IsObject(this) || (O = GuardToIteratorHelper(this)) === null) { + return callFunction(CallIteratorHelperMethodIfWrapped, this, + value, "IteratorHelperNext"); + } + const generator = UnsafeGetReservedSlot(O, ITERATOR_HELPER_GENERATOR_SLOT); + return callContentFunction(GeneratorNext, generator, value); +} + +function IteratorHelperReturn(value) { + let O; + if (!IsObject(this) || (O = GuardToIteratorHelper(this)) === null) { + return callFunction(CallIteratorHelperMethodIfWrapped, this, + value, "IteratorHelperReturn"); + } + const generator = UnsafeGetReservedSlot(O, ITERATOR_HELPER_GENERATOR_SLOT); + return callContentFunction(GeneratorReturn, generator, value); +} + +function IteratorHelperThrow(value) { + let O; + if (!IsObject(this) || (O = GuardToIteratorHelper(this)) === null) { + return callFunction(CallIteratorHelperMethodIfWrapped, this, + value, "IteratorHelperThrow"); + } + const generator = UnsafeGetReservedSlot(O, ITERATOR_HELPER_GENERATOR_SLOT); + return callContentFunction(GeneratorThrow, generator, value); +} + +// Lazy %Iterator.prototype% methods +// Iterator Helpers proposal 2.1.5.2-2.1.5.7 +// +// In order to match the semantics of the built-in generator objects used in +// the proposal, we use a reserved slot on the IteratorHelper objects to store +// a regular generator that is called from the %IteratorHelper.prototype% +// methods. +// +// Each of the lazy methods is divided into a prelude and a body, with the +// eager prelude steps being contained in the corresponding IteratorX method +// and the lazy body steps inside the IteratorXGenerator generator functions. +// +// Each prelude method initializes and returns a new IteratorHelper object. +// As part of this initialization process, the appropriate generator function +// is called, followed by GeneratorNext being called on returned generator +// instance in order to move it to it's first yield point. This is done so that +// if the return or throw methods are called on the IteratorHelper before next +// has been called, we can catch them in the try and use the finally block to +// close the source iterator. +// +// The needClose flag is used to track when the source iterator should be closed +// following an exception being thrown within the generator, corresponding to +// whether or not the abrupt completions in the spec are being passed back to +// the caller (when needClose is false) or handled with IfAbruptCloseIterator +// (when needClose is true). + +/* Iterator Helpers proposal 2.1.5.2 Prelude */ +function IteratorMap(mapper) { + // Step 1. + const iterated = GetIteratorDirect(this); + + // Step 2. + if (!IsCallable(mapper)) { + ThrowTypeError(JSMSG_NOT_FUNCTION, DecompileArg(0, mapper)); + } + + const iteratorHelper = NewIteratorHelper(); + const generator = IteratorMapGenerator(iterated, mapper); + callContentFunction(GeneratorNext, generator); + UnsafeSetReservedSlot(iteratorHelper, ITERATOR_HELPER_GENERATOR_SLOT, generator); + return iteratorHelper; +} + +/* Iterator Helpers proposal 2.1.5.2 Body */ +function* IteratorMapGenerator(iterated, mapper) { + // Step 1. + let lastValue; + // Step 2. + let needClose = true; + try { + yield; + needClose = false; + + for (let next = IteratorStep(iterated, lastValue); + next; + next = IteratorStep(iterated, lastValue)) { + // Step c. + const value = next.value; + + // Steps d-g. + needClose = true; + lastValue = yield callContentFunction(mapper, undefined, value); + needClose = false; + } + } finally { + if (needClose) { + IteratorClose(iterated); + } + } +} + +/* Iterator Helpers proposal 2.1.5.3 Prelude */ +function IteratorFilter(filterer) { + // Step 1. + const iterated = GetIteratorDirect(this); + + // Step 2. + if (!IsCallable(filterer)) { + ThrowTypeError(JSMSG_NOT_FUNCTION, DecompileArg(0, filterer)); + } + + const iteratorHelper = NewIteratorHelper(); + const generator = IteratorFilterGenerator(iterated, filterer); + callContentFunction(GeneratorNext, generator); + UnsafeSetReservedSlot(iteratorHelper, ITERATOR_HELPER_GENERATOR_SLOT, generator); + return iteratorHelper; +} + +/* Iterator Helpers proposal 2.1.5.3 Body */ +function* IteratorFilterGenerator(iterated, filterer) { + // Step 1. + let lastValue; + // Step 2. + let needClose = true; + try { + yield; + needClose = false; + + for (let next = IteratorStep(iterated, lastValue); + next; + next = IteratorStep(iterated, lastValue)) { + // Step c. + const value = next.value; + + // Steps d-g. + needClose = true; + if (callContentFunction(filterer, undefined, value)) { + lastValue = yield value; + } + needClose = false; + } + } finally { + if (needClose) { + IteratorClose(iterated); + } + } +} + +/* Iterator Helpers proposal 2.1.5.4 Prelude */ +function IteratorTake(limit) { + // Step 1. + const iterated = GetIteratorDirect(this); + + // Step 2. + const remaining = ToInteger(limit); + // Step 3. + if (remaining < 0) { + ThrowRangeError(JSMSG_NEGATIVE_LIMIT); + } + + const iteratorHelper = NewIteratorHelper(); + const generator = IteratorTakeGenerator(iterated, remaining); + callContentFunction(GeneratorNext, generator); + UnsafeSetReservedSlot(iteratorHelper, ITERATOR_HELPER_GENERATOR_SLOT, generator); + return iteratorHelper; +} + +/* Iterator Helpers proposal 2.1.5.4 Body */ +function* IteratorTakeGenerator(iterated, remaining) { + // Step 1. + let lastValue; + // Step 2. + let needClose = true; + try { + yield; + needClose = false; + + for (; remaining > 0; remaining--) { + const next = IteratorStep(iterated, lastValue); + if (!next) { + return; + } + + const value = next.value; + needClose = true; + lastValue = yield value; + needClose = false; + } + } finally { + if (needClose) { + IteratorClose(iterated); + } + } + + IteratorClose(iterated); +} + +/* Iterator Helpers proposal 2.1.5.5 Prelude */ +function IteratorDrop(limit) { + // Step 1. + const iterated = GetIteratorDirect(this); + + // Step 2. + const remaining = ToInteger(limit); + // Step 3. + if (remaining < 0) { + ThrowRangeError(JSMSG_NEGATIVE_LIMIT); + } + + const iteratorHelper = NewIteratorHelper(); + const generator = IteratorDropGenerator(iterated, remaining); + callContentFunction(GeneratorNext, generator); + UnsafeSetReservedSlot(iteratorHelper, ITERATOR_HELPER_GENERATOR_SLOT, generator); + return iteratorHelper; +} + +/* Iterator Helpers proposal 2.1.5.5 Body */ +function* IteratorDropGenerator(iterated, remaining) { + let needClose = true; + try { + yield; + needClose = false; + + // Step 1. + for (; remaining > 0; remaining--) { + if (!IteratorStep(iterated)) { + return; + } + } + + // Step 2. + let lastValue; + // Step 3. + for (let next = IteratorStep(iterated, lastValue); + next; + next = IteratorStep(iterated, lastValue)) { + // Steps c-d. + const value = next.value; + + needClose = true; + lastValue = yield value; + needClose = false; + } + } finally { + if (needClose) { + IteratorClose(iterated); + } + } +} + +/* Iterator Helpers proposal 2.1.5.6 Prelude */ +function IteratorAsIndexedPairs() { + // Step 1. + const iterated = GetIteratorDirect(this); + + const iteratorHelper = NewIteratorHelper(); + const generator = IteratorAsIndexedPairsGenerator(iterated); + callContentFunction(GeneratorNext, generator); + UnsafeSetReservedSlot(iteratorHelper, ITERATOR_HELPER_GENERATOR_SLOT, generator); + return iteratorHelper; +} + +/* Iterator Helpers proposal 2.1.5.6 Body */ +function* IteratorAsIndexedPairsGenerator(iterated) { + // Step 2. + let lastValue; + // Step 3. + let needClose = true; + try { + yield; + needClose = false; + + for (let next = IteratorStep(iterated, lastValue), index = 0; + next; + next = IteratorStep(iterated, lastValue), index++) { + // Steps c-d. + const value = next.value; + + needClose = true; + lastValue = yield [index, value]; + needClose = false; + } + } finally { + if (needClose) { + IteratorClose(iterated); + } + } +} + +/* Iterator Helpers proposal 2.1.5.7 Prelude */ +function IteratorFlatMap(mapper) { + // Step 1. + const iterated = GetIteratorDirect(this); + + // Step 2. + if (!IsCallable(mapper)) { + ThrowTypeError(JSMSG_NOT_FUNCTION, DecompileArg(0, mapper)); + } + + const iteratorHelper = NewIteratorHelper(); + const generator = IteratorFlatMapGenerator(iterated, mapper); + callContentFunction(GeneratorNext, generator); + UnsafeSetReservedSlot(iteratorHelper, ITERATOR_HELPER_GENERATOR_SLOT, generator); + return iteratorHelper; +} + +/* Iterator Helpers proposal 2.1.5.7 Body */ +function* IteratorFlatMapGenerator(iterated, mapper) { + // Step 1. + let needClose = true; + try { + yield; + needClose = false; + + for (let next = IteratorStep(iterated); + next; + next = IteratorStep(iterated)) { + // Step c. + const value = next.value; + + needClose = true; + // Step d. + const mapped = callContentFunction(mapper, undefined, value); + // Steps f-i. + for (const innerValue of allowContentIter(mapped)) { + yield innerValue; + } + needClose = false; + } + } finally { + if (needClose) { + IteratorClose(iterated); + } + } +} + +/* Iterator Helpers proposal 2.1.5.8 */ +function IteratorReduce(reducer/*, initialValue*/) { + // Step 1. + const iterated = GetIteratorDirectWrapper(this); + + // Step 2. + if (!IsCallable(reducer)) { + ThrowTypeError(JSMSG_NOT_FUNCTION, DecompileArg(0, reducer)); + } + + // Step 3. + let accumulator; + if (arguments.length === 1) { + // Step a. + const next = callContentFunction(iterated.next, iterated); + if (!IsObject(next)) { + ThrowTypeError(JSMSG_OBJECT_REQUIRED, DecompileArg(0, next)); + } + // Step b. + if (next.done) { + ThrowTypeError(JSMSG_EMPTY_ITERATOR_REDUCE); + } + // Step c. + accumulator = next.value; + } else { + // Step 4. + accumulator = arguments[1]; + } + + // Step 5. + for (const value of allowContentIter(iterated)) { + accumulator = callContentFunction(reducer, undefined, accumulator, value); + } + return accumulator; +} + +/* Iterator Helpers proposal 2.1.5.9 */ +function IteratorToArray() { + // Step 1. + const iterated = {[std_iterator]: () => this}; + // Steps 2-3. + return [...allowContentIter(iterated)]; +} + +/* Iterator Helpers proposal 2.1.5.10 */ +function IteratorForEach(fn) { + // Step 1. + const iterated = GetIteratorDirectWrapper(this); + + // Step 2. + if (!IsCallable(fn)) { + ThrowTypeError(JSMSG_NOT_FUNCTION, DecompileArg(0, fn)); + } + + // Step 3. + for (const value of allowContentIter(iterated)) { + callContentFunction(fn, undefined, value); + } +} + +/* Iterator Helpers proposal 2.1.5.11 */ +function IteratorSome(fn) { + // Step 1. + const iterated = GetIteratorDirectWrapper(this); + + // Step 2. + if (!IsCallable(fn)) { + ThrowTypeError(JSMSG_NOT_FUNCTION, DecompileArg(0, fn)); + } + + // Step 3. + for (const value of allowContentIter(iterated)) { + // Steps d-f. + if (callContentFunction(fn, undefined, value)) { + return true; + } + } + // Step 3b. + return false; +} + +/* Iterator Helpers proposal 2.1.5.12 */ +function IteratorEvery(fn) { + // Step 1. + const iterated = GetIteratorDirectWrapper(this); + + // Step 2. + if (!IsCallable(fn)) { + ThrowTypeError(JSMSG_NOT_FUNCTION, DecompileArg(0, fn)); + } + + // Step 3. + for (const value of allowContentIter(iterated)) { + // Steps d-f. + if (!callContentFunction(fn, undefined, value)) { + return false; + } + } + // Step 3b. + return true; +} + +/* Iterator Helpers proposal 2.1.5.13 */ +function IteratorFind(fn) { + // Step 1. + const iterated = GetIteratorDirectWrapper(this); + + // Step 2. + if (!IsCallable(fn)) { + ThrowTypeError(JSMSG_NOT_FUNCTION, DecompileArg(0, fn)); + } + + // Step 3. + for (const value of allowContentIter(iterated)) { + // Steps d-f. + if (callContentFunction(fn, undefined, value)) { + return value; + } + } +} |