From b014266c33ec776bf42817a89c5488368094808c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Nie=C3=9Fen?= Date: Fri, 15 Jul 2022 21:30:50 +0200 Subject: [PATCH] repl: fix overzealous top-level await Fixes: https://github.com/nodejs/node/issues/43777 PR-URL: https://github.com/nodejs/node/pull/43827 Reviewed-By: Guy Bedford Reviewed-By: Antoine du Hamel Reviewed-By: Benjamin Gruenbaum Reviewed-By: Ruben Bridgewater --- lib/internal/repl/await.js | 12 +++++-- lib/repl.js | 2 +- .../test-repl-preprocess-top-level-await.js | 36 ++++++++++--------- test/parallel/test-repl-top-level-await.js | 4 +++ 4 files changed, 35 insertions(+), 19 deletions(-) diff --git a/lib/internal/repl/await.js b/lib/internal/repl/await.js index ec1d0c8823e107..c53ffce581e37b 100644 --- a/lib/internal/repl/await.js +++ b/lib/internal/repl/await.js @@ -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; } diff --git a/lib/repl.js b/lib/repl.js index a02d4cfc3fb795..c4e606c77e56e6 100644 --- a/lib/repl.js +++ b/lib/repl.js @@ -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) { diff --git a/test/parallel/test-repl-preprocess-top-level-await.js b/test/parallel/test-repl-preprocess-top-level-await.js index 51b15e107f53bc..bdd1c6fe8a423a 100644 --- a/test/parallel/test-repl-preprocess-top-level-await.js +++ b/test/parallel/test-repl-preprocess-top-level-await.js @@ -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', @@ -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}`, @@ -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() {} })()' ], @@ -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; }', @@ -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) { diff --git a/test/parallel/test-repl-top-level-await.js b/test/parallel/test-repl-top-level-await.js index a7c3811d5e1b07..4ab2a32ee09bc0 100644 --- a/test/parallel/test-repl-top-level-await.js +++ b/test/parallel/test-repl-top-level-await.js @@ -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) {