Bug 209124 - Promise.{all,allSettled,race} does not always close iterator
Summary: Promise.{all,allSettled,race} does not always close iterator
Status: RESOLVED WONTFIX
Alias: None
Product: WebKit
Classification: Unclassified
Component: JavaScriptCore (show other bugs)
Version: WebKit Nightly Build
Hardware: All All
: P2 Trivial
Assignee: Alexey Shvayka
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2020-03-15 10:06 PDT by Alexey Shvayka
Modified: 2020-06-04 14:29 PDT (History)
8 users (show)

See Also:


Attachments
Patch (8.08 KB, patch)
2020-03-15 11:46 PDT, Alexey Shvayka
no flags Details | Formatted Diff | Diff
Patch (8.73 KB, patch)
2020-03-16 05:18 PDT, Alexey Shvayka
ashvayka: review-
ashvayka: commit-queue-
Details | Formatted Diff | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Alexey Shvayka 2020-03-15 10:06:33 PDT
Test case:
  delete Promise.resolve;

  Promise.all({
    [Symbol.iterator]() { return this; },
    return() { console.log("iterator closed"); },
  });

Expected:
  Promise rejected with TypeError, "iterator closed" logged

Actual:
  Promise rejected with TypeError, no logs

ECMA262: https://tc39.es/ecma262/#sec-promise.all (step 6.a)
Test262: https://test262.report/browse/built-ins/Promise/all/invoke-resolve-get-error-close.js
Comment 1 Alexey Shvayka 2020-03-15 11:46:23 PDT
Created attachment 393622 [details]
Patch
Comment 2 Alexey Shvayka 2020-03-16 05:18:03 PDT
Created attachment 393648 [details]
Patch

Inline @getIteratorAndPromiseResolve and drop @isObject check.
Comment 3 Ross Kirsling 2020-03-17 18:58:16 PDT
Comment on attachment 393648 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=393648&action=review

> Source/JavaScriptCore/builtins/PromiseConstructor.js:64
> +            var promiseResolve = this.resolve;
> +            if (typeof promiseResolve !== "function")
> +                @throwTypeError("Promise.resolve is not a function");

I wish we could stick the try-catch for iterator.return under this `if` to avoid the rethrow, but I guess a this.resolve getter might throw in an arbitrary way...

> Source/JavaScriptCore/builtins/PromiseConstructor.js:67
> +                iterator.return();

Shouldn't we check whether iterator.return is undefined (per step 5)?

> Source/JavaScriptCore/builtins/PromiseConstructor.js:81
> +        var wrapper = {};
> +        wrapper.@@iterator = function() { return iterator; };
> +
> +        for (var value of wrapper) {

I wonder if this is really better than operating the iterator manually in a while loop?
Comment 4 Keith Miller 2020-03-17 19:25:51 PDT
Imo, the spec is doing the wrong thing here... If I were a web-dev asked to polyfill this I would do what JSC does today. I think it makes more sense for the spec to do the intuitive thing unless we have a compelling reason otherwise. Also, FWIW, looking at test262 report at one point every engine failed this test... https://test262.report/browse/built-ins/Promise/all/invoke-resolve-get-error-close.js?date=2019-07-06.
Comment 5 Keith Miller 2020-03-17 19:28:11 PDT
Comment on attachment 393648 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=393648&action=review

>> Source/JavaScriptCore/builtins/PromiseConstructor.js:81
>> +        for (var value of wrapper) {
> 
> I wonder if this is really better than operating the iterator manually in a while loop?

It's definitely better once we start optimizing the builtin iterators as the front end knows what for-of does in a way it can't for manual iteration. Also getting the details of iterating right is a PITA with a while loop. Trust me... 🤮
Comment 6 Alexey Shvayka 2020-04-15 11:57:15 PDT
(In reply to Ross Kirsling from comment #3)
> > Source/JavaScriptCore/builtins/PromiseConstructor.js:67
> > +                iterator.return();
> 
> Shouldn't we check whether iterator.return is undefined (per step 5)?

If `iterator.return` is null or undefined, calling it will throw a TypeError that will be ignored due to empty `catch` clause.

This patch would not bring 100% spec compliance though: `this.resolve` should be looked up before `iterator.next` (step 6 of https://tc39.es/ecma262/#sec-getiterator), not after.

I hope that spec change (https://github.com/tc39/ecma262/pull/1912) will be approved during next TC-39 meeting. Great job on the fix, Keith!
Comment 7 Alexey Shvayka 2020-06-04 13:22:57 PDT
(In reply to Alexey Shvayka from comment #6)
> I hope that spec change (https://github.com/tc39/ecma262/pull/1912) will be
> approved during next TC-39 meeting. Great job on the fix, Keith!

The change has gained consensus and is already implemented in SpiderMonkey.
r262544 synced updated test262 coverage.
Comment 8 Keith Miller 2020-06-04 14:29:05 PDT
(In reply to Alexey Shvayka from comment #7)
> (In reply to Alexey Shvayka from comment #6)
> > I hope that spec change (https://github.com/tc39/ecma262/pull/1912) will be
> > approved during next TC-39 meeting. Great job on the fix, Keith!
> 
> The change has gained consensus and is already implemented in SpiderMonkey.
> r262544 synced updated test262 coverage.

Oh you changed test262 for me, thanks a bunch! I was about to look into it now that TC-39 is done.