// Copyright (C) 2025 André Bargull. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.

/*---
esid: sec-iterator.zipkeyed
description: >
  Handle abrupt completion from IteratorStepValue in IteratorZip.
info: |
  Iterator.zipKeyed ( iterables [ , options ] )
    ...
    16. Return IteratorZip(iters, mode, padding, finishResults).

  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,
        ...
        iii. For each integer i such that 0 ≤ i < iterCount, in ascending order, do
          ...
          3. Else,
            a. Let result be Completion(IteratorStepValue(iter)).
            b. If result is an abrupt completion, then
              i. Remove iter from openIters.
              ii. Return ? IteratorCloseAll(openIters, result).
            ...
            d. If result is done, then
              i. Remove iter from openIters.
              ...

  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 )
    1. Assert: iteratorRecord.[[Iterator]] is an Object.
    2. Let iterator be iteratorRecord.[[Iterator]].
    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)).
    5. If completion is a throw completion, return ? completion.
    ...
includes: [compareArray.js]
features: [joint-iteration]
---*/

var modes = [
  "shortest",
  "longest",
  "strict",
];

function ExpectedError() {}

var log = [];

var first = {
  next() {
    log.push("call first next");
    throw new ExpectedError();
  },
  return() {
    log.push("unexpected call first return");
  }
};

var second = {
  next() {
    log.push("unexpected call second next");
  },
  return() {
    // Called with the correct receiver and no arguments.
    assert.sameValue(this, second);
    assert.sameValue(arguments.length, 0);

    // NB: Log after above asserts, because failures aren't propagated.
    log.push("call second return");

    // IteratorClose ignores new exceptions when called with a Throw completion.
    throw new Test262Error();
  }
};

var third = {
  next() {
    log.push("unexpected call third next");
  },
  return() {
    // Called with the correct receiver and no arguments.
    assert.sameValue(this, third);
    assert.sameValue(arguments.length, 0);

    // NB: Log after above asserts, because failures aren't propagated.
    log.push("call third return");

    // IteratorClose ignores new exceptions when called with a Throw completion.
    throw new Test262Error();
  }
};

// Empty iterator to ensure |return| is not called for closed iterators.
var empty = {
  next() {
    log.push("call empty next");
    return {done: true};
  },
  return() {
    log.push("unexpected call empty return");
  }
};

for (var mode of modes) {
  var it = Iterator.zipKeyed({first, second, third}, {mode});

  assert.throws(ExpectedError, function() {
    it.next();
  });

  assert.compareArray(log, [
    "call first next",
    "call third return",
    "call second return",
  ]);

  // Clear log.
  log.length = 0;
}

// This case applies only when mode is "longest".
var it = Iterator.zipKeyed({empty, first, second, third}, {mode: "longest"});

assert.throws(ExpectedError, function() {
  it.next();
});

assert.compareArray(log, [
  "call empty next",
  "call first next",
  "call third return",
  "call second return",
]);

reportCompare(0, 0);
