Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 2a11d50

Browse filesBrowse files
islandryumarco-ippolito
authored andcommitted
module: correctly detect top-level await in ambiguous contexts
Fixes: #58331 PR-URL: #58646 Reviewed-By: Ethan Arrowood <ethan@arrowood.dev> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com>
1 parent 4f004ef commit 2a11d50
Copy full SHA for 2a11d50

File tree

Expand file treeCollapse file tree

2 files changed

+94
-0
lines changed
Open diff view settings
Filter options
Expand file treeCollapse file tree

2 files changed

+94
-0
lines changed
Open diff view settings
Collapse file

‎src/node_contextify.cc‎

Copy file name to clipboardExpand all lines: src/node_contextify.cc
+16Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1446,6 +1446,12 @@ static std::vector<std::string_view> throws_only_in_cjs_error_messages = {
14461446
"await is only valid in async functions and "
14471447
"the top level bodies of modules"};
14481448

1449+
static const auto maybe_top_level_await_errors =
1450+
std::array<std::string_view, 2>{
1451+
"missing ) after argument list", // example: `func(await 1);`
1452+
"SyntaxError: Unexpected" // example: `if(await 1)`
1453+
};
1454+
14491455
// If cached_data is provided, it would be used for the compilation and
14501456
// the on-disk compilation cache from NODE_COMPILE_CACHE (if configured)
14511457
// would be ignored.
@@ -1654,6 +1660,16 @@ bool ShouldRetryAsESM(Realm* realm,
16541660
break;
16551661
}
16561662
}
1663+
1664+
for (const auto& error_message : maybe_top_level_await_errors) {
1665+
if (message_view.find(error_message) != std::string_view::npos) {
1666+
// If the error message is related to top-level await, we can try to
1667+
// compile it as ESM.
1668+
maybe_valid_in_esm = true;
1669+
break;
1670+
}
1671+
}
1672+
16571673
if (!maybe_valid_in_esm) {
16581674
return false;
16591675
}
Collapse file
+78Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
import { spawnPromisified } from '../common/index.mjs';
2+
import { describe, it } from 'node:test';
3+
import { strictEqual, match } from 'node:assert';
4+
5+
describe('unusual top-level await syntax errors', () => {
6+
const expressions = [
7+
// string
8+
{ expression: '""' },
9+
// number
10+
{ expression: '0' },
11+
// boolean
12+
{ expression: 'true' },
13+
// null
14+
{ expression: 'null' },
15+
// undefined
16+
{ expression: 'undefined' },
17+
// object
18+
{ expression: '{}' },
19+
// array
20+
{ expression: '[]' },
21+
// new
22+
{ expression: 'new Date()' },
23+
// identifier
24+
{ initialize: 'const a = 2;', expression: 'a' },
25+
];
26+
it('should not crash the process', async () => {
27+
for (const { expression, initialize } of expressions) {
28+
const wrapperExpressions = [
29+
`function callAwait() {}; callAwait(await ${expression});`,
30+
`if (await ${expression}) {}`,
31+
`{ key: await ${expression} }`,
32+
`[await ${expression}]`,
33+
`(await ${expression})`,
34+
];
35+
for (const wrapperExpression of wrapperExpressions) {
36+
const { code, signal, stdout, stderr } = await spawnPromisified(process.execPath, [
37+
'--eval',
38+
`
39+
${initialize || ''}
40+
${wrapperExpression}
41+
`,
42+
]);
43+
44+
strictEqual(stderr, '');
45+
strictEqual(stdout, '');
46+
strictEqual(code, 0);
47+
strictEqual(signal, null);
48+
}
49+
}
50+
});
51+
52+
it('should throw the error for unrelated syntax errors', async () => {
53+
const expression = 'foo bar';
54+
const wrapperExpressions = [
55+
[`function callSyntaxError() {}; callSyntaxError(${expression});`, /missing \) after argument list/],
56+
[`if (${expression}) {}`, /Unexpected identifier/],
57+
[`{ key: ${expression} }`, /Unexpected identifier/],
58+
[`[${expression}]`, /Unexpected identifier/],
59+
[`(${expression})`, /Unexpected identifier/],
60+
[`const ${expression} = 1;`, /Missing initializer in const declaration/],
61+
['console.log(\'PI: \' Math.PI);', /missing \) after argument list/],
62+
['callAwait(await "" "");', /missing \) after argument list/],
63+
];
64+
65+
for (const [wrapperExpression, error] of wrapperExpressions) {
66+
const { code, signal, stdout, stderr } = await spawnPromisified(process.execPath, [
67+
'--eval',
68+
`
69+
${wrapperExpression}
70+
`,
71+
]);
72+
match(stderr, error);
73+
strictEqual(stdout, '');
74+
strictEqual(code, 1);
75+
strictEqual(signal, null);
76+
}
77+
});
78+
});

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.