tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

iterables-iteration-get-iterator-flattenable-abrupt-completion.js (4643B)


      1 // Copyright (C) 2025 André Bargull. All rights reserved.
      2 // This code is governed by the BSD license found in the LICENSE file.
      3 
      4 /*---
      5 esid: sec-iterator.zip
      6 description: >
      7  Handle abrupt completions during iterables iteration.
      8 info: |
      9  Iterator.zip ( iterables [ , options ] )
     10    ...
     11    12. Repeat, while next is not done,
     12      ...
     13      c. If next is not done, then
     14        i. Let iter be Completion(GetIteratorFlattenable(next, reject-strings)).
     15        ii. IfAbruptCloseIterators(iter, the list-concatenation of « inputIter » and iters).
     16    ...
     17 
     18  GetIteratorFlattenable ( obj, primitiveHandling )
     19    1. If obj is not an Object, then
     20      a. If primitiveHandling is reject-primitives, throw a TypeError exception.
     21      b. Assert: primitiveHandling is iterate-string-primitives.
     22      c. If obj is not a String, throw a TypeError exception.
     23    2. Let method be ? GetMethod(obj, %Symbol.iterator%).
     24    3. If method is undefined, then
     25      a. Let iterator be obj.
     26    4. Else,
     27      a. Let iterator be ? Call(method, obj).
     28    5. If iterator is not an Object, throw a TypeError exception.
     29    6. Return ? GetIteratorDirect(iterator).
     30 
     31  IteratorCloseAll ( iters, completion )
     32    1. For each element iter of iters, in reverse List order, do
     33      a. Set completion to Completion(IteratorClose(iter, completion)).
     34    2. Return ? completion.
     35 
     36  IteratorClose ( iteratorRecord, completion )
     37    1. Assert: iteratorRecord.[[Iterator]] is an Object.
     38    2. Let iterator be iteratorRecord.[[Iterator]].
     39    3. Let innerResult be Completion(GetMethod(iterator, "return")).
     40    4. If innerResult is a normal completion, then
     41      a. Let return be innerResult.[[Value]].
     42      b. If return is undefined, return ? completion.
     43      c. Set innerResult to Completion(Call(return, iterator)).
     44    5. If completion is a throw completion, return ? completion.
     45    ...
     46 includes: [compareArray.js]
     47 features: [joint-iteration]
     48 ---*/
     49 
     50 class ExpectedError extends Error {}
     51 
     52 var badIterators = [
     53  // Throw TypeError in GetIteratorFlattenable because strings are rejected.
     54  {
     55    iterator: "bad iterator",
     56    error: TypeError
     57  },
     58 
     59  // Throw an error when GetIteratorFlattenable performs GetMethod.
     60  {
     61    iterator: {
     62      get [Symbol.iterator]() {
     63        throw new ExpectedError();
     64      }
     65    },
     66    error: ExpectedError,
     67  },
     68 
     69  // Throw an error when GetIteratorFlattenable performs Call.
     70  {
     71    iterator: {
     72      [Symbol.iterator]() {
     73        throw new ExpectedError();
     74      }
     75    },
     76    error: ExpectedError,
     77  },
     78 
     79  // Throw an error when GetIteratorFlattenable performs GetIteratorDirect.
     80  {
     81    iterator: {
     82      get next() {
     83        throw new ExpectedError();
     84      }
     85    },
     86    error: ExpectedError,
     87  },
     88 ];
     89 
     90 function makeIterables(badIterator) {
     91  var log = [];
     92 
     93  var first = {
     94    next() {
     95      log.push("unexpected call to next method");
     96    },
     97    return() {
     98      // Called with the correct receiver and no arguments.
     99      assert.sameValue(this, first);
    100      assert.sameValue(arguments.length, 0);
    101 
    102      // NB: Log after above asserts, because failures aren't propagated.
    103      log.push("close first iterator");
    104 
    105      // IteratorClose ignores new exceptions when called with a Throw completion.
    106      throw new Test262Error();
    107    },
    108  };
    109 
    110  var second = {
    111    next() {
    112      log.push("unexpected call to next method");
    113    },
    114    return() {
    115      // Called with the correct receiver and no arguments.
    116      assert.sameValue(this, second);
    117      assert.sameValue(arguments.length, 0);
    118 
    119      // NB: Log after above asserts, because failures aren't propagated.
    120      log.push("close second iterator");
    121 
    122      // IteratorClose ignores new exceptions when called with a Throw completion.
    123      throw new Test262Error();
    124    },
    125  };
    126 
    127  var elements = [first, second, badIterator];
    128  var elementsIter = elements.values();
    129 
    130  var iterables = {
    131    [Symbol.iterator]() {
    132      return this;
    133    },
    134    next() {
    135      log.push("call next");
    136      return elementsIter.next();
    137    },
    138    return() {
    139      log.push("close iterables iterator");
    140 
    141      // IteratorClose ignores new exceptions when called with a Throw completion.
    142      throw new Test262Error();
    143    },
    144  };
    145 
    146  return {log, iterables};
    147 }
    148 
    149 for (var {iterator, error} of badIterators) {
    150  var {log, iterables} = makeIterables(iterator);
    151 
    152  assert.throws(error, function() {
    153    Iterator.zip(iterables);
    154  });
    155 
    156  // Ensure iterators are closed in the correct order.
    157  assert.compareArray(log, [
    158    "call next",
    159    "call next",
    160    "call next",
    161    "close second iterator",
    162    "close first iterator",
    163    "close iterables iterator",
    164  ]);
    165 }
    166 
    167 reportCompare(0, 0);