Bug 166698 - [ESNext] Async iteration - Implement async iteration statement: for-await-of
Summary: [ESNext] Async iteration - Implement async iteration statement: for-await-of
Status: RESOLVED FIXED
Alias: None
Product: WebKit
Classification: Unclassified
Component: JavaScriptCore (show other bugs)
Version: WebKit Local Build
Hardware: Unspecified Unspecified
: P2 Normal
Assignee: GSkachkov
URL:
Keywords: InRadar
Depends on:
Blocks: 166693 166695
  Show dependency treegraph
 
Reported: 2017-01-04 14:28 PST by GSkachkov
Modified: 2017-08-30 03:10 PDT (History)
11 users (show)

See Also:


Attachments
Patch (17.30 KB, patch)
2017-08-25 23:50 PDT, GSkachkov
no flags Details | Formatted Diff | Diff
Patch (30.32 KB, patch)
2017-08-28 13:38 PDT, GSkachkov
no flags Details | Formatted Diff | Diff
Patch (33.01 KB, patch)
2017-08-30 01:27 PDT, GSkachkov
no flags Details | Formatted Diff | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description GSkachkov 2017-01-04 14:28:30 PST
async function *foo() {...}

for await (const item of foo()) {
  debug(item);
}
Comment 1 GSkachkov 2017-08-25 23:50:05 PDT
Created attachment 319132 [details]
Patch

WiP. Patch is mostly done, working on adding more tests
Comment 2 Yusuke Suzuki 2017-08-26 18:29:34 PDT
Comment on attachment 319132 [details]
Patch

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

Looks good. But r- due to the bug and the test coverage.

> Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp:4301
> +    bool isForAwait = forLoopNode != nullptr ? forLoopNode->isForAwait() : false;

style: we do not use `xxx != nullptr`.
I think this can be `bool isForAwait = forLoopNode && forLoopNode->isForAwait();`

> Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp:4419
> +            emitIteratorNext(value.get(), iterator.get(), node, isForAwait ? JSC::EmitAwait::Yes : JSC::EmitAwait::No);

JSC:: is not necessary.

> Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp:4432
> +            emitIteratorClose(iterator.get(), node, isForAwait ? JSC::EmitAwait::Yes : JSC::EmitAwait::No);

JSC:: is not necessary.

> Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp:4584
> +RegisterID* BytecodeGenerator::emitIteratorNext(RegisterID* dst, RegisterID* iterator, const ThrowableExpressionData* node, JSC::EmitAwait doEmitAwait)

JSC:: is not necessary.

> Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h:783
> +        RegisterID* emitIteratorNext(RegisterID* dst, RegisterID* iterator, const ThrowableExpressionData* node, JSC::EmitAwait = JSC::EmitAwait::No);

JSC:: is not necessary.

> Source/JavaScriptCore/parser/Parser.cpp:1280
> +    }

I think this accidentally allows `for await (;;)`, `for await (let v = 42;;)` etc.
If `for await` comes, only for-of style is allowed.
And could you add a syntax test to cover cases like this?
Comment 3 GSkachkov 2017-08-28 01:42:12 PDT
Working on fixes. Also found one more issue in my patch that for await did not support the async-from-sync i.e. `for await (let v of [1, 2, 3])`
Will be fixed in new patch
Comment 4 GSkachkov 2017-08-28 13:38:58 PDT
Created attachment 319202 [details]
Patch

Fixed comments and more tests
Comment 5 Yusuke Suzuki 2017-08-28 18:22:26 PDT
Comment on attachment 319202 [details]
Patch

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

Nice! I think we have several bugs.

> Source/JavaScriptCore/parser/Parser.cpp:1490
> +        ASSERT(!isAwaitFor);

In this `enumerationLoop:` labelled case, we do not check isAwaitFor.
This means that `for await (expr of expr);` can be parsed. (And this assertion could fail.).
Let's fix INTOKEN parsing as the same to `for await (var xxx of xxx)` case.
And could you add tests for `for await (expr of expr);` and `for await (expr in expr);` cases?

> Source/JavaScriptCore/parser/Parser.h:734
> +        m_isAsyncFunctionBoundary = false;

Why is this necessary? If it is necessary, is this enough? (We only set false for arrow functions. Why is this unnecessary for the other types of functions?).
Comment 6 GSkachkov 2017-08-30 01:27:25 PDT
Created attachment 319354 [details]
Patch

Fix comments && added tests
Comment 7 GSkachkov 2017-08-30 01:51:13 PDT
Comment on attachment 319202 [details]
Patch

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

>> Source/JavaScriptCore/parser/Parser.cpp:1490
>> +        ASSERT(!isAwaitFor);
> 
> In this `enumerationLoop:` labelled case, we do not check isAwaitFor.
> This means that `for await (expr of expr);` can be parsed. (And this assertion could fail.).
> Let's fix INTOKEN parsing as the same to `for await (var xxx of xxx)` case.
> And could you add tests for `for await (expr of expr);` and `for await (expr in expr);` cases?

Yeah, you are right, I missed `if (!consume(INTOKEN)) {` in this part. Fixed and added test.

>> Source/JavaScriptCore/parser/Parser.h:734
>> +        m_isAsyncFunctionBoundary = false;
> 
> Why is this necessary? If it is necessary, is this enough? (We only set false for arrow functions. Why is this unnecessary for the other types of functions?).

There is a issue that, arrow function inherits  async  property from parent async function scope, I think it is wrong because we can't use `await` within arrow function that is declared async function.
In my patch this lead that it was allow to use for await in arrow function that is declared in async function, that is wrong and should lead to Syntax Error:
```
async function () {
     const arr = () => { for await (...) {} }
}
```
Comment 8 Yusuke Suzuki 2017-08-30 02:02:38 PDT
Comment on attachment 319354 [details]
Patch

r=me
Comment 9 WebKit Commit Bot 2017-08-30 02:51:49 PDT
Comment on attachment 319354 [details]
Patch

Clearing flags on attachment: 319354

Committed r221358: <http://trac.webkit.org/changeset/221358>
Comment 10 WebKit Commit Bot 2017-08-30 02:51:50 PDT
All reviewed patches have been landed.  Closing bug.
Comment 11 Radar WebKit Bug Importer 2017-08-30 03:10:11 PDT
<rdar://problem/34158241>