Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Suppress GetMethod(iterator.return) errors in IteratorClose #1398

Closed
LeszekSwirski opened this issue Jan 9, 2019 · 1 comment · Fixed by #1408
Closed

Suppress GetMethod(iterator.return) errors in IteratorClose #1398

LeszekSwirski opened this issue Jan 9, 2019 · 1 comment · Fixed by #1408

Comments

@LeszekSwirski
Copy link

IteratorClose is currently specified as

IteratorClose ( iteratorRecord, completion )

  1. Assert: Type(iteratorRecord.[[Iterator]]) is Object.
  2. Assert: completion is a Completion Record.
  3. Let iterator be iteratorRecord.[[Iterator]].
  4. Let return be ? GetMethod(iterator, "return").
  5. If return is undefined, return Completion(completion).
  6. Let innerResult be Call(return, iterator, « »).
  7. If completion.[[Type]] is throw, return Completion(completion).
  8. If innerResult.[[Type]] is throw, return Completion(innerResult).
  9. If Type(innerResult.[[Value]]) is not Object, throw a TypeError exception.
  10. Return Completion(completion).

If the outer completion is throw, then it is commonly understood that exceptions throw by iterator.return() should be suppressed. However, there is one exception that is not suppressed, which is iterator.return not being callable -- on 4., ? GetMethod(iterator, "return") will return the abrupt completion of GetMethod (caused by IsCallable failing), rather than the outer completion.

Therefore, errors in GetMethod(iterator, "return") and in Call(return, iterator, « ») are inconsistent, and the access and call have to be split across an exception handling boundary (try-catch or similar). I suggest that we amend this to:

  1. ...
  2. Let return be GetMethod(iterator, "return").
  3. If return.[[Value]] is undefined, return Completion(completion).
  4. If return.[[Type]] is throw and completion.[[Type]] is throw, return Completion(completion).
  5. Let innerResult be Call(return.[[Value]], iterator, « »).
  6. ...

This is now consistent, and can be desugared more simply to wrap iterator.return() in a try-catch (or the equivalent of such an operation in an engine's IR).

This should not be a web-compat issue, as it is already currently inconsistent across implementations: current versions of V8 and SpiderMonkey are spec compatible, but JSC, Chakra and XS are not. This can be tested with:

(function() {
  try {
    var it = {
      [Symbol.iterator]: function() {
        return {
          i: 0,
          next: function() {
            if (this.i > 0) return { done: true }
            return { value: this.i++, done: false };
          },
          return: 2
        }
      }
    }

    for (var x of it) {
      throw 3;
    }
  } catch (e) {
    if (e == 3) {
      print("Not spec compatible")
    } else {
      print("Spec compatible")
    }
  }
})();

Additionally, babel desugaring currently does not take this into account, desugaring the loop to something that does not check IsCallable:

var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;

try {
  for (var _iterator = it[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
    var x = _step.value;

    throw 3;
  }
} catch (err) {
  _didIteratorError = true;
  _iteratorError = err;
} finally {
  try {
    if (!_iteratorNormalCompletion && _iterator.return) {
      _iterator.return();
    }
  } finally {
    if (_didIteratorError) {
      throw _iteratorError;
    }
  }
}

A similar spec incompatibility can be constructed for array destructuring.

@mathiasbynens
Copy link
Member

This seems like another nice simplification to the iterator protocol (previously: #976, #988, #1021), unless there is a good reason for the currently specified behavior. @kmiller68 @bterlson

zenparsing pushed a commit to zenparsing/ecma262 that referenced this issue Jan 14, 2019
llebout pushed a commit to llebout/chromium_v8 that referenced this issue Jun 16, 2019
As per the new specs, when the exception is thrown by iterator's return method
while doing iterator close because it is not callable, the exception is
suppressed in the same way as if the return method is called and threw an exception.

tc39/ecma262#1398

Bug: v8:9056
Change-Id: I21abd5fdd01d3a957c3c16d9d3aaab9091e43142
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1648256
Reviewed-by: Leszek Swirski <leszeks@chromium.org>
Commit-Queue: Swapnil Gaikwad <swapnilgaikwad@google.com>
Cr-Commit-Position: refs/heads/master@{#62035}
ljharb pushed a commit to zenparsing/ecma262 that referenced this issue Apr 6, 2020
@ljharb ljharb closed this as completed in 0b89915 Apr 30, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants