Skip to content

Commit

Permalink
repl: fix overzealous top-level await
Browse files Browse the repository at this point in the history
Fixes: nodejs#43777

PR-URL: nodejs#43827
Reviewed-By: Guy Bedford <guybedford@gmail.com>
Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de>
  • Loading branch information
tniessen authored and Fyko committed Sep 15, 2022
1 parent b19b871 commit 7bc709d
Show file tree
Hide file tree
Showing 4 changed files with 35 additions and 19 deletions.
12 changes: 10 additions & 2 deletions lib/internal/repl/await.js
Original file line number Diff line number Diff line change
Expand Up @@ -237,8 +237,16 @@ function processTopLevelAwait(src) {
// semicolon. Since there can only be more right parentheses between
// node.expression.end and the semicolon, appending one more to
// node.expression should be fine.
state.prepend(node, 'return (');
state.append(node.expression, ')');
//
// We also create a wrapper object around the result of the expression.
// Consider an expression of the form `(await x).y`. If we just return
// this expression from an async function, the caller will await `y`, too,
// if it evaluates to a Promise. Instead, we return
// `{ value: ((await x).y) }`, which allows the caller to retrieve the
// awaited value correctly.
state.prepend(node.expression, '{ value: (');
state.prepend(node, 'return ');
state.append(node.expression, ') }');
}
break;
}
Expand Down
2 changes: 1 addition & 1 deletion lib/repl.js
Original file line number Diff line number Diff line change
Expand Up @@ -616,7 +616,7 @@ function REPLServer(prompt,

(async () => {
try {
const result = await promise;
const result = (await promise)?.value;
finishExecution(null, result);
} catch (err) {
if (err && process.domain) {
Expand Down
36 changes: 20 additions & 16 deletions test/parallel/test-repl-preprocess-top-level-await.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,25 +17,25 @@ const testCases = [
[ '0',
null ],
[ 'await 0',
'(async () => { return (await 0) })()' ],
'(async () => { return { value: (await 0) } })()' ],
[ `await ${surrogate}`,
`(async () => { return (await ${surrogate}) })()` ],
`(async () => { return { value: (await ${surrogate}) } })()` ],
[ 'await 0;',
'(async () => { return (await 0); })()' ],
'(async () => { return { value: (await 0) }; })()' ],
[ 'await 0;;;',
'(async () => { return (await 0);;; })()' ],
'(async () => { return { value: (await 0) };;; })()' ],
[ `await ${surrogate};`,
`(async () => { return (await ${surrogate}); })()` ],
`(async () => { return { value: (await ${surrogate}) }; })()` ],
[ `await ${surrogate};`,
`(async () => { return (await ${surrogate}); })()` ],
`(async () => { return { value: (await ${surrogate}) }; })()` ],
[ '(await 0)',
'(async () => { return ((await 0)) })()' ],
'(async () => { return ({ value: (await 0) }) })()' ],
[ `(await ${surrogate})`,
`(async () => { return ((await ${surrogate})) })()` ],
`(async () => { return ({ value: (await ${surrogate}) }) })()` ],
[ '(await 0);',
'(async () => { return ((await 0)); })()' ],
'(async () => { return ({ value: (await 0) }); })()' ],
[ `(await ${surrogate});`,
`(async () => { return ((await ${surrogate})); })()` ],
`(async () => { return ({ value: (await ${surrogate}) }); })()` ],
[ 'async function foo() { await 0; }',
null ],
[ 'async () => await 0',
Expand All @@ -45,7 +45,7 @@ const testCases = [
[ 'await 0; return 0;',
null ],
[ `await ${surrogate}; await ${surrogate};`,
`(async () => { await ${surrogate}; return (await ${surrogate}); })()` ],
`(async () => { await ${surrogate}; return { value: (await ${surrogate}) }; })()` ],
[ 'var a = await 1',
'var a; (async () => { void (a = await 1) })()' ],
[ `var a = await ${surrogate}`,
Expand All @@ -71,7 +71,7 @@ const testCases = [
' ([{d}] = [{d: 3}])) })()'],
/* eslint-disable no-template-curly-in-string */
[ 'console.log(`${(await { a: 1 }).a}`)',
'(async () => { return (console.log(`${(await { a: 1 }).a}`)) })()' ],
'(async () => { return { value: (console.log(`${(await { a: 1 }).a}`)) } })()' ],
/* eslint-enable no-template-curly-in-string */
[ 'await 0; function foo() {}',
'var foo; (async () => { await 0; this.foo = foo; function foo() {} })()' ],
Expand All @@ -92,15 +92,15 @@ const testCases = [
[ 'let o = await 1, p',
'let o, p; (async () => { void ( (o = await 1), (p=undefined)) })()' ],
[ 'await (async () => { let p = await 1; return p; })()',
'(async () => { return (await (async () => ' +
'{ let p = await 1; return p; })()) })()' ],
'(async () => { return { value: (await (async () => ' +
'{ let p = await 1; return p; })()) } })()' ],
[ '{ let p = await 1; }',
'(async () => { { let p = await 1; } })()' ],
[ 'var p = await 1',
'var p; (async () => { void (p = await 1) })()' ],
[ 'await (async () => { var p = await 1; return p; })()',
'(async () => { return (await (async () => ' +
'{ var p = await 1; return p; })()) })()' ],
'(async () => { return { value: (await (async () => ' +
'{ var p = await 1; return p; })()) } })()' ],
[ '{ var p = await 1; }',
'var p; (async () => { { void (p = await 1); } })()' ],
[ 'for await (var i of asyncIterable) { i; }',
Expand Down Expand Up @@ -140,6 +140,10 @@ const testCases = [
[ 'var x = await foo(); async function foo() { return Promise.resolve(1);}',
'var x; var foo; (async () => { void (x = await foo()); this.foo = foo; ' +
'async function foo() { return Promise.resolve(1);} })()'],
[ '(await x).y',
'(async () => { return { value: ((await x).y) } })()'],
[ 'await (await x).y',
'(async () => { return { value: (await (await x).y) } })()'],
];

for (const [input, expected] of testCases) {
Expand Down
4 changes: 4 additions & 0 deletions test/parallel/test-repl-top-level-await.js
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,10 @@ async function ordinaryTests() {
'3',
'undefined',
]],
// Regression test for https://github.com/nodejs/node/issues/43777.
['await Promise.resolve(123), Promise.resolve(456)', 'Promise {', { line: 0 }],
['await Promise.resolve(123), await Promise.resolve(456)', '456'],
['await (Promise.resolve(123), Promise.resolve(456))', '456'],
];

for (const [input, expected = [`${input}\r`], options = {}] of testCases) {
Expand Down

0 comments on commit 7bc709d

Please sign in to comment.