diff --git a/test/built-ins/Iterator/zip/suspended-start-iterator-close-calls-next.js b/test/built-ins/Iterator/zip/suspended-start-iterator-close-calls-next.js new file mode 100644 index 0000000000..916ad84098 --- /dev/null +++ b/test/built-ins/Iterator/zip/suspended-start-iterator-close-calls-next.js @@ -0,0 +1,78 @@ +// Copyright (C) 2025 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-iterator.zip +description: > + Generator is closed from suspended-start state and IteratorClose calls next. +info: | + %IteratorHelperPrototype%.return ( ) + ... + 4. If O.[[GeneratorState]] is suspended-start, then + a. Set O.[[GeneratorState]] to completed. + ... + c. Perform ? IteratorCloseAll(O.[[UnderlyingIterators]], ReturnCompletion(undefined)). + d. Return CreateIteratorResultObject(undefined, true). + ... + + IteratorCloseAll ( iters, completion ) + 1. For each element iter of iters, in reverse List order, do + a. Set completion to Completion(IteratorClose(iter, completion)). + 2. Return ? completion. + + IteratorClose ( iteratorRecord, completion ) + ... + 3. Let innerResult be Completion(GetMethod(iterator, "return")). + 4. If innerResult is a normal completion, then + a. Let return be innerResult.[[Value]]. + b. If return is undefined, return ? completion. + c. Set innerResult to Completion(Call(return, iterator)). + ... + + %IteratorHelperPrototype%.next ( ) + 1. Return ? GeneratorResume(this value, undefined, "Iterator Helper"). + + GeneratorResume ( generator, value, generatorBrand ) + 1. Let state be ? GeneratorValidate(generator, generatorBrand). + 2. If state is completed, return CreateIteratorResultObject(undefined, true). + ... + + GeneratorValidate ( generator, generatorBrand ) + ... + 5. Let state be generator.[[GeneratorState]]. + 6. If state is executing, throw a TypeError exception. + 7. Return state. +features: [joint-iteration] +---*/ + +var returnCallCount = 0; + +var underlying = { + next() { + throw new Test262Error("Unexpected call to next"); + }, + return() { + returnCallCount += 1; + + // The generator state is already set to "completed". The generator state is + // not "executing", so `GeneratorValidate` succeeds and `GeneratorResume` + // returns with `CreateIteratorResultObject()`. + var result = it.next(); + assert.sameValue(result.value, undefined); + assert.sameValue(result.done, true); + + return {}; + }, +}; + +var it = Iterator.zip([underlying]); + +assert.sameValue(returnCallCount, 0); + +// This `return()` call sets the generator state to "completed" and then calls +// `IteratorClose()`. +var result = it.return(); +assert.sameValue(result.value, undefined); +assert.sameValue(result.done, true); + +assert.sameValue(returnCallCount, 1); diff --git a/test/built-ins/Iterator/zip/suspended-start-iterator-close-calls-return.js b/test/built-ins/Iterator/zip/suspended-start-iterator-close-calls-return.js new file mode 100644 index 0000000000..20cd09d631 --- /dev/null +++ b/test/built-ins/Iterator/zip/suspended-start-iterator-close-calls-return.js @@ -0,0 +1,82 @@ +// Copyright (C) 2025 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-iterator.zip +description: > + Generator is closed from suspended-start state and IteratorClose calls return. +info: | + %IteratorHelperPrototype%.return ( ) + ... + 4. If O.[[GeneratorState]] is suspended-start, then + a. Set O.[[GeneratorState]] to completed. + ... + c. Perform ? IteratorCloseAll(O.[[UnderlyingIterators]], ReturnCompletion(undefined)). + d. Return CreateIteratorResultObject(undefined, true). + 5. Let C be ReturnCompletion(undefined). + 6. Return ? GeneratorResumeAbrupt(O, C, "Iterator Helper"). + + IteratorCloseAll ( iters, completion ) + 1. For each element iter of iters, in reverse List order, do + a. Set completion to Completion(IteratorClose(iter, completion)). + 2. Return ? completion. + + IteratorClose ( iteratorRecord, completion ) + ... + 3. Let innerResult be Completion(GetMethod(iterator, "return")). + 4. If innerResult is a normal completion, then + a. Let return be innerResult.[[Value]]. + b. If return is undefined, return ? completion. + c. Set innerResult to Completion(Call(return, iterator)). + ... + 8. Return ? completion. + + GeneratorResumeAbrupt ( generator, abruptCompletion, generatorBrand ) + 1. Let state be ? GeneratorValidate(generator, generatorBrand). + 2. If state is suspended-start, then + ... + 3. If state is completed, then + a. If abruptCompletion is a return completion, then + i. Return CreateIteratorResultObject(abruptCompletion.[[Value]], true). + ... + + GeneratorValidate ( generator, generatorBrand ) + ... + 5. Let state be generator.[[GeneratorState]]. + 6. If state is executing, throw a TypeError exception. + 7. Return state. +features: [joint-iteration] +---*/ + +var returnCallCount = 0; + +var underlying = { + next() { + throw new Test262Error("Unexpected call to next"); + }, + return() { + returnCallCount += 1; + + // The generator state is already set to "completed", so this `return()` + // call proceeds to `GeneratorResumeAbrupt`. The generator state is not + // "executing", so `GeneratorValidate` succeeds and `GeneratorResumeAbrupt` + // returns with `CreateIteratorResultObject()`. + var result = it.return(); + assert.sameValue(result.value, undefined); + assert.sameValue(result.done, true); + + return {}; + }, +}; + +var it = Iterator.zip([underlying]); + +assert.sameValue(returnCallCount, 0); + +// This `return()` call sets the generator state to "completed" and then calls +// `IteratorClose()`. +var result = it.return(); +assert.sameValue(result.value, undefined); +assert.sameValue(result.done, true); + +assert.sameValue(returnCallCount, 1); diff --git a/test/built-ins/Iterator/zip/suspended-yield-iterator-close-calls-next.js b/test/built-ins/Iterator/zip/suspended-yield-iterator-close-calls-next.js new file mode 100644 index 0000000000..1a5683bf65 --- /dev/null +++ b/test/built-ins/Iterator/zip/suspended-yield-iterator-close-calls-next.js @@ -0,0 +1,102 @@ +// Copyright (C) 2025 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-iterator.zip +description: > + Generator is closed from suspended-yield state and IteratorClose calls next. +info: | + %IteratorHelperPrototype%.return ( ) + ... + 5. Let C be ReturnCompletion(undefined). + 6. Return ? GeneratorResumeAbrupt(O, C, "Iterator Helper"). + + GeneratorResumeAbrupt ( generator, abruptCompletion, generatorBrand ) + 1. Let state be ? GeneratorValidate(generator, generatorBrand). + ... + 4. Assert: state is suspended-yield. + ... + 8. Set generator.[[GeneratorState]] to executing. + ... + 10. Resume the suspended evaluation of genContext using abruptCompletion as + the result of the operation that suspended it. Let result be the + Completion Record returned by the resumed computation. + ... + + GeneratorValidate ( generator, generatorBrand ) + ... + 5. Let state be generator.[[GeneratorState]]. + 6. If state is executing, throw a TypeError exception. + 7. Return state. + + IteratorZip ( iters, mode, padding, finishResults ) + ... + 3. Let closure be a new Abstract Closure with no parameters that captures + iters, iterCount, openIters, mode, padding, and finishResults, and + performs the following steps when called: + ... + b. Repeat, + ... + v. Let completion be Completion(Yield(results)). + vi. If completion is an abrupt completion, then + 1. Return ? IteratorCloseAll(openIters, completion). + ... + + IteratorCloseAll ( iters, completion ) + 1. For each element iter of iters, in reverse List order, do + a. Set completion to Completion(IteratorClose(iter, completion)). + 2. Return ? completion. + + IteratorClose ( iteratorRecord, completion ) + ... + 3. Let innerResult be Completion(GetMethod(iterator, "return")). + 4. If innerResult is a normal completion, then + a. Let return be innerResult.[[Value]]. + b. If return is undefined, return ? completion. + c. Set innerResult to Completion(Call(return, iterator)). + ... + + %IteratorHelperPrototype%.next ( ) + 1. Return ? GeneratorResume(this value, undefined, "Iterator Helper"). + + GeneratorResume ( generator, value, generatorBrand ) + 1. Let state be ? GeneratorValidate(generator, generatorBrand). + ... +includes: [compareArray.js] +features: [joint-iteration] +---*/ + +var returnCallCount = 0; + +var underlying = { + next() { + return {value: 123, done: false}; + }, + return() { + returnCallCount += 1; + + // The generator state is set to "executing", so this `next()` call throws + // a TypeError when `GeneratorResume` performs `GeneratorValidate`. + assert.throws(TypeError, function() { + it.next(); + }); + + return {}; + }, +}; + +var it = Iterator.zip([underlying]); + +// Move generator into "suspended-yield" state. +var result = it.next(); +assert.compareArray(result.value, [123]); +assert.sameValue(result.done, false); + +assert.sameValue(returnCallCount, 0); + +// This `return()` call continues execution in the suspended generator. +var result = it.return(); +assert.sameValue(result.value, undefined); +assert.sameValue(result.done, true); + +assert.sameValue(returnCallCount, 1); diff --git a/test/built-ins/Iterator/zip/suspended-yield-iterator-close-calls-return.js b/test/built-ins/Iterator/zip/suspended-yield-iterator-close-calls-return.js new file mode 100644 index 0000000000..be55f0128e --- /dev/null +++ b/test/built-ins/Iterator/zip/suspended-yield-iterator-close-calls-return.js @@ -0,0 +1,95 @@ +// Copyright (C) 2025 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-iterator.zip +description: > + Generator is closed from suspended-yield state and IteratorClose calls return. +info: | + %IteratorHelperPrototype%.return ( ) + ... + 5. Let C be ReturnCompletion(undefined). + 6. Return ? GeneratorResumeAbrupt(O, C, "Iterator Helper"). + + GeneratorResumeAbrupt ( generator, abruptCompletion, generatorBrand ) + 1. Let state be ? GeneratorValidate(generator, generatorBrand). + ... + 4. Assert: state is suspended-yield. + ... + 8. Set generator.[[GeneratorState]] to executing. + ... + 10. Resume the suspended evaluation of genContext using abruptCompletion as + the result of the operation that suspended it. Let result be the + Completion Record returned by the resumed computation. + ... + + GeneratorValidate ( generator, generatorBrand ) + ... + 5. Let state be generator.[[GeneratorState]]. + 6. If state is executing, throw a TypeError exception. + 7. Return state. + + IteratorZip ( iters, mode, padding, finishResults ) + ... + 3. Let closure be a new Abstract Closure with no parameters that captures + iters, iterCount, openIters, mode, padding, and finishResults, and + performs the following steps when called: + ... + b. Repeat, + ... + v. Let completion be Completion(Yield(results)). + vi. If completion is an abrupt completion, then + 1. Return ? IteratorCloseAll(openIters, completion). + ... + + IteratorCloseAll ( iters, completion ) + 1. For each element iter of iters, in reverse List order, do + a. Set completion to Completion(IteratorClose(iter, completion)). + 2. Return ? completion. + + IteratorClose ( iteratorRecord, completion ) + ... + 3. Let innerResult be Completion(GetMethod(iterator, "return")). + 4. If innerResult is a normal completion, then + a. Let return be innerResult.[[Value]]. + b. If return is undefined, return ? completion. + c. Set innerResult to Completion(Call(return, iterator)). + ... +includes: [compareArray.js] +features: [joint-iteration] +---*/ + +var returnCallCount = 0; + +var underlying = { + next() { + return {value: 123, done: false}; + }, + return() { + returnCallCount += 1; + + // The generator state is set to "executing", so this `return()` call throws + // a TypeError when `GeneratorResumeAbrupt` performs `GeneratorValidate`. + assert.throws(TypeError, function() { + it.return(); + }); + + return {}; + }, +}; + +var it = Iterator.zip([underlying]); + +// Move generator into "suspended-yield" state. +var result = it.next(); +assert.compareArray(result.value, [123]); +assert.sameValue(result.done, false); + +assert.sameValue(returnCallCount, 0); + +// This `return()` call continues execution in the suspended generator. +var result = it.return(); +assert.sameValue(result.value, undefined); +assert.sameValue(result.done, true); + +assert.sameValue(returnCallCount, 1);