diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
commit | 36d22d82aa202bb199967e9512281e9a53db42c9 (patch) | |
tree | 105e8c98ddea1c1e4784a60a5a6410fa416be2de /js/src/builtin/Iterator.js | |
parent | Initial commit. (diff) | |
download | firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip |
Adding upstream version 115.7.0esr.upstream/115.7.0esr
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'js/src/builtin/Iterator.js')
-rw-r--r-- | js/src/builtin/Iterator.js | 785 |
1 files changed, 785 insertions, 0 deletions
diff --git a/js/src/builtin/Iterator.js b/js/src/builtin/Iterator.js new file mode 100644 index 0000000000..c759691782 --- /dev/null +++ b/js/src/builtin/Iterator.js @@ -0,0 +1,785 @@ +/* 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 = + ArgumentsLength() < 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 (!IsNullOrUndefined(returnMethod)) { + const result = callContentFunction(returnMethod, iterator); + // Step 8. + if (!IsObject(result)) { + ThrowTypeError(JSMSG_OBJECT_REQUIRED, DecompileArg(0, result)); + } + } + // Step 5b & 9. + return value; +} + +/** + * ES2022 draft rev c5f683e61d5dce703650f1c90d2309c46f8c157a + * + * GetIterator ( obj [ , hint [ , method ] ] ) + * https://tc39.es/ecma262/#sec-getiterator + * + * Optimized for single argument + */ +function GetIteratorSync(obj) { + // Steps 1 & 2 skipped as we know we want the sync iterator method + var method = GetMethod(obj, GetBuiltinSymbol("iterator")); + + // Step 3. Let iterator be ? Call(method, obj). + var iterator = callContentFunction(method, obj); + + // Step 4. If Type(iterator) is not Object, throw a TypeError exception. + if (!IsObject(iterator)) { + ThrowTypeError(JSMSG_NOT_ITERABLE, obj === null ? "null" : typeof obj); + } + + // Step 5. Let nextMethod be ? GetV(iterator, "next"). + var nextMethod = iterator.next; + + // Step 6. Let iteratorRecord be the Record { [[Iterator]]: iterator, [[NextMethod]]: nextMethod, [[Done]]: false }. + var iteratorRecord = { + iterator, + nextMethod, + done: false, + }; + + // Step 7. Return iteratorRecord. + return iteratorRecord; +} + +/* 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. + [GetBuiltinSymbol("iterator")]: function IteratorMethod() { + return this; + }, + next(value) { + return callContentFunction(nextMethod, obj, value); + }, + return(value) { + const returnMethod = obj.return; + if (!IsNullOrUndefined(returnMethod)) { + 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 (ArgumentsLength() === 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[GetBuiltinSymbol("iterator")]; + + let iteratorRecord; + // Step 2. + if (!IsNullOrUndefined(usingIterator)) { + // 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 = this; + if (!IsObject(O) || (O = GuardToWrapForValidIterator(O)) === null) { + if (ArgumentsLength() === 0) { + return callFunction( + CallWrapForValidIteratorMethodIfWrapped, + this, + "WrapForValidIteratorNext" + ); + } + return callFunction( + CallWrapForValidIteratorMethodIfWrapped, + this, + value, + "WrapForValidIteratorNext" + ); + } + const iterated = UnsafeGetReservedSlot(O, ITERATED_SLOT); + // Step 3. + let result; + if (ArgumentsLength() === 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 = this; + if (!IsObject(O) || (O = GuardToWrapForValidIterator(O)) === 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 (!IsNullOrUndefined(returnMethod)) { + 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 = this; + if (!IsObject(O) || (O = GuardToWrapForValidIterator(O)) === 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 (IsNullOrUndefined(throwMethod)) { + throw value; + } + // Step 6. + return callContentFunction(throwMethod, iterator, value); +} + +/* Iterator Helper object prototype methods. */ +function IteratorHelperNext(value) { + let O = this; + if (!IsObject(O) || (O = GuardToIteratorHelper(O)) === 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 = this; + if (!IsObject(O) || (O = GuardToIteratorHelper(O)) === 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 = this; + if (!IsObject(O) || (O = GuardToIteratorHelper(O)) === 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 (ArgumentsLength() === 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 = GetArgument(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 = { + [GetBuiltinSymbol("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; + } + } +} |