Console does not allow await in a top level block
Categories
(DevTools :: Console, defect, P3)
Tracking
(Not tracked)
People
(Reporter: bytesized, Assigned: artemmanusenkov, Mentored)
References
(Blocks 1 open bug)
Details
Attachments
(2 files)
STR:
- Open the Browser Console (Control+Shift+J)
- Enter this code:
await OS.File.read("C:\\mozilla-build\\VERSION");
- Observe that this runs, as expected.
- Enter this code:
try { await OS.File.read("C:\\mozilla-build\\VERSION"); } catch (ex) {}
Expected Result:
The second line of code entered should have the same effect as the first line, except that any error should be caught.
Actual Result:
The second line of code entered gives this error:
Uncaught SyntaxError: await is only valid in async functions and async generators
I'm using Nightly on Windows 10.
Comment 1•3 years ago
|
||
Nicolas, can you add some pointers to fix this bug?
Comment 2•3 years ago
|
||
This isn't Browser Console only. You can reproduce it with
{ await 1 }
Comment 3•3 years ago
|
||
Did some investigation on this.
This fails here: devtools/client/debugger/src/workers/parser/mapAwaitExpression.js#187-188 , where the expression is considered to not have a top-level await expression
We are doing this check to avoid rewriting any function declared in the expression for example, but here it's failing.
The check we do is pretty basic in devtools/client/debugger/src/workers/parser/mapAwaitExpression.js#14 : from the computed AST, we go through the different nodes, and check if we have a top-level await one.
And here is the logic for the "isTopLevel" part devtools/client/debugger/src/workers/parser/utils/helpers.js#180-184
So here the code is inacurate. We might want to check if we don't have body with a type
of FunctionDeclaration
This doesn't seem to be enough, and wrapExpressionFromAst
should probably be modified as well.
Comment 4•2 years ago
|
||
Hi Nicolas, this bug looks interesting, and I'd love to work on it. I don't have any experience in debugger, but I've worked on a few good first bugs in other panels. I might need some guidance to get started. Let me know if this task requires someone with better understanding.
Comment 5•2 years ago
|
||
Hello Sai, this might require some knowledge about AST, but you can probably learn along the way :)
This used the parser worker, so if you work in the files I pointed to in Comment 3, you'd need to update the worker bundle
This is done by doing cd devtools/client/debugger && yarn && node bin/bundle.js
.
I'm assigning the byg to you for now, let me know if you need any help :)
Comment hidden (typo) |
Comment 7•2 years ago
|
||
Thanks for assigning, Nicolas. Adding upon comment #3, following are my observations,
isTopLevel
returns true if exactly on of the AwaitExpression's ancestor is of key "body". I assume this was implemented because Program node is assigned the key "body". However, even BlockStatement nodes are assigned the same key. So, in the case of {await 1}
, the await expression has two ancestors with key "body", Program and BlockStatement.
I think isTopLevel
can be updated from
return ancestors.filter(ancestor => ancestor.key == "body").length == 1;
to
return !ancestors.some(ancestor => t.isFunction(ancestor.node));
It seems to be working for most variations of top level await I can think of.
I'll start looking into wrapExpressionFromAst
once you confirm if the proposed change looks good, and if I'm on the right track.
Updated•2 years ago
|
Comment 8•2 years ago
|
||
(In reply to Sai Hemanth from comment #7)
Thanks for assigning, Nicolas. Adding upon comment #3, following are my observations,
isTopLevel
returns true if exactly on of the AwaitExpression's ancestor is of key "body". I assume this was implemented because Program node is assigned the key "body". However, even BlockStatement nodes are assigned the same key. So, in the case of{await 1}
, the await expression has two ancestors with key "body", Program and BlockStatement.I think
isTopLevel
can be updated fromreturn ancestors.filter(ancestor => ancestor.key == "body").length == 1;
to
return !ancestors.some(ancestor => t.isFunction(ancestor.node));
It seems to be working for most variations of top level await I can think of.
I'll start looking intowrapExpressionFromAst
once you confirm if the proposed change looks good, and if I'm on the right track.
Sorry for the delay Sai.
Yes please go ahead. If it seems good from your manual testing, it's worth putting a patch to review (see contribution_quickref.html#to-submit-a-patch )
Comment 9•2 years ago
|
||
Comment 10•2 years ago
|
||
I noticed I also get this error with the following:
let a = await 0;
for (const v of []) { }
let b = 1;
Does that fit in to the understanding here?
Comment 11•2 years ago
|
||
(In reply to microrffr from comment #10)
I noticed I also get this error with the following:
let a = await 0; for (const v of []) { } let b = 1;
Does that fit in to the understanding here?
This looks like a different issue, I filed Bug 1773627 to investigate
Comment 12•2 years ago
|
||
The bug assignee is inactive on Bugzilla, so the assignee is being reset.
Assignee | ||
Comment 15•3 months ago
|
||
Hi! I'd like to take this @nchevobbe
I've been working on this today and there's more to it that meets the eye initially. Seems like in some cases we'll need to recursively/iteratively add returnStatement
to all block statements, for example in nested if statement like if (cond1) { if (cond2) { 3 } else { await 4 } } else { await 2 }
(it can go very deep, and we don't know which BlockStatement
will yield us the result, hence we need to add it to every BlockStatement
).
if we have this kind of deep if nesting without await
it works fine currently, but i couldn't figure how it does it haha. if you could point out how/where maybe a better way to handle the nested return await
will become apparent
Also something weird happened with one test i did
async function asyncFunc() {return 'hi'}
try {1; asyncFunc()} catch(e) { }
returns promisetry {await 1; asyncFunc()} catch(e) { }
returns 'hi'try {1; await asyncFunc()} catch(e) { }
returns 'hi'try {await 1; await asyncFunc()} catch(e) { }
'returns hi'
1,3,4 work fine, 2 acts weirdly
after all the mappings at the end of mapExpression
function it turns into
(async () => { try { await 1; return asyncFunc(); } catch (e) { }})()
But when it runs it doesn't return a promise, as it should, like the first test does, or if you just take it and run it in node/console. i think I'm missing some further manipulation done to it
And a couple of partially relevant things to the bugticket:
-
how do i run the tests in the worker folder?
client/debugger/src/workers/parser/tests
i can't get them to run with ./mach test -
when i do node bin/watch.js i get Error: Cannot find module 'shelljs'.
-
I don't see the worker build process documented anywhere, what you described
cd devtools/client/debugger && yarn && node bin/bundle.js
, i'd like to write it somewhere after we figure out the bug
Assignee | ||
Comment 16•3 months ago
|
||
Updated•3 months ago
|
Assignee | ||
Comment 17•3 months ago
|
||
I submitted what i've done so far, so that try {await 1; asyncFunc()} catch(e) { }
could be reproduced. i just copied the tests from the last revision so far so don't mind them
Comment 18•3 months ago
|
||
Hey Artem, thanks for looking into this
I can't look at the parser questions at the moment, but I can answer questions from the last section
(In reply to Artem Manushenkov from comment #15)
- how do i run the tests in the worker folder?
client/debugger/src/workers/parser/tests
i can't get them to run with ./mach test
from devtools/client/debugger
, run yarn
once to install npm packages, and then you can run tests with yarn test parser/tests/
I don't see the worker build process documented anywhere, what you described
cd devtools/client/debugger && yarn && node bin/bundle.js
, i'd like to write it somewhere after we figure out the bug
It's mentioned in https://searchfox.org/mozilla-central/rev/109bb25545f0d2df31954dc0a9afbf30d900b6bb/devtools/docs/contributor/tests/node-tests.md#67-76 :)
Description
•