Skip to content

Commit a101fe6

Browse files
ejose19targos
authored andcommitted
repl: correctly hoist top level await declarations
PR-URL: #39265 Reviewed-By: Guy Bedford <[email protected]> Reviewed-By: James M Snell <[email protected]>
1 parent 2d552a3 commit a101fe6

File tree

2 files changed

+147
-24
lines changed

2 files changed

+147
-24
lines changed

Diff for: lib/internal/repl/await.js

+81-13
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
const {
44
ArrayFrom,
55
ArrayPrototypeForEach,
6+
ArrayPrototypeIncludes,
67
ArrayPrototypeJoin,
78
ArrayPrototypePop,
89
ArrayPrototypePush,
@@ -22,12 +23,21 @@ const parser = require('internal/deps/acorn/acorn/dist/acorn').Parser;
2223
const walk = require('internal/deps/acorn/acorn-walk/dist/walk');
2324
const { Recoverable } = require('internal/repl');
2425

26+
function isTopLevelDeclaration(state) {
27+
return state.ancestors[state.ancestors.length - 2] === state.body;
28+
}
29+
2530
const noop = FunctionPrototype;
2631
const visitorsWithoutAncestors = {
2732
ClassDeclaration(node, state, c) {
28-
if (state.ancestors[state.ancestors.length - 2] === state.body) {
33+
if (isTopLevelDeclaration(state)) {
2934
state.prepend(node, `${node.id.name}=`);
35+
ArrayPrototypePush(
36+
state.hoistedDeclarationStatements,
37+
`let ${node.id.name}; `
38+
);
3039
}
40+
3141
walk.base.ClassDeclaration(node, state, c);
3242
},
3343
ForOfStatement(node, state, c) {
@@ -38,6 +48,10 @@ const visitorsWithoutAncestors = {
3848
},
3949
FunctionDeclaration(node, state, c) {
4050
state.prepend(node, `${node.id.name}=`);
51+
ArrayPrototypePush(
52+
state.hoistedDeclarationStatements,
53+
`var ${node.id.name}; `
54+
);
4155
},
4256
FunctionExpression: noop,
4357
ArrowFunctionExpression: noop,
@@ -51,22 +65,72 @@ const visitorsWithoutAncestors = {
5165
walk.base.ReturnStatement(node, state, c);
5266
},
5367
VariableDeclaration(node, state, c) {
54-
if (node.kind === 'var' ||
55-
state.ancestors[state.ancestors.length - 2] === state.body) {
56-
if (node.declarations.length === 1) {
57-
state.replace(node.start, node.start + node.kind.length, 'void');
58-
} else {
59-
state.replace(node.start, node.start + node.kind.length, 'void (');
68+
const variableKind = node.kind;
69+
const isIterableForDeclaration = ArrayPrototypeIncludes(
70+
['ForOfStatement', 'ForInStatement'],
71+
state.ancestors[state.ancestors.length - 2].type
72+
);
73+
74+
if (variableKind === 'var' || isTopLevelDeclaration(state)) {
75+
state.replace(
76+
node.start,
77+
node.start + variableKind.length + (isIterableForDeclaration ? 1 : 0),
78+
variableKind === 'var' && isIterableForDeclaration ?
79+
'' :
80+
'void' + (node.declarations.length === 1 ? '' : ' (')
81+
);
82+
83+
if (!isIterableForDeclaration) {
84+
ArrayPrototypeForEach(node.declarations, (decl) => {
85+
state.prepend(decl, '(');
86+
state.append(decl, decl.init ? ')' : '=undefined)');
87+
});
88+
89+
if (node.declarations.length !== 1) {
90+
state.append(node.declarations[node.declarations.length - 1], ')');
91+
}
92+
}
93+
94+
const variableIdentifiersToHoist = [
95+
['var', []],
96+
['let', []],
97+
];
98+
function registerVariableDeclarationIdentifiers(node) {
99+
switch (node.type) {
100+
case 'Identifier':
101+
ArrayPrototypePush(
102+
variableIdentifiersToHoist[variableKind === 'var' ? 0 : 1][1],
103+
node.name
104+
);
105+
break;
106+
case 'ObjectPattern':
107+
ArrayPrototypeForEach(node.properties, (property) => {
108+
registerVariableDeclarationIdentifiers(property.value);
109+
});
110+
break;
111+
case 'ArrayPattern':
112+
ArrayPrototypeForEach(node.elements, (element) => {
113+
registerVariableDeclarationIdentifiers(element);
114+
});
115+
break;
116+
}
60117
}
61118

62119
ArrayPrototypeForEach(node.declarations, (decl) => {
63-
state.prepend(decl, '(');
64-
state.append(decl, decl.init ? ')' : '=undefined)');
120+
registerVariableDeclarationIdentifiers(decl.id);
65121
});
66122

67-
if (node.declarations.length !== 1) {
68-
state.append(node.declarations[node.declarations.length - 1], ')');
69-
}
123+
ArrayPrototypeForEach(
124+
variableIdentifiersToHoist,
125+
({ 0: kind, 1: identifiers }) => {
126+
if (identifiers.length > 0) {
127+
ArrayPrototypePush(
128+
state.hoistedDeclarationStatements,
129+
`${kind} ${ArrayPrototypeJoin(identifiers, ', ')}; `
130+
);
131+
}
132+
}
133+
);
70134
}
71135

72136
walk.base.VariableDeclaration(node, state, c);
@@ -128,6 +192,7 @@ function processTopLevelAwait(src) {
128192
const state = {
129193
body,
130194
ancestors: [],
195+
hoistedDeclarationStatements: [],
131196
replace(from, to, str) {
132197
for (let i = from; i < to; i++) {
133198
wrappedArray[i] = '';
@@ -172,7 +237,10 @@ function processTopLevelAwait(src) {
172237
state.append(last.expression, ')');
173238
}
174239

175-
return ArrayPrototypeJoin(wrappedArray, '');
240+
return (
241+
ArrayPrototypeJoin(state.hoistedDeclarationStatements, '') +
242+
ArrayPrototypeJoin(wrappedArray, '')
243+
);
176244
}
177245

178246
module.exports = {

Diff for: test/parallel/test-repl-preprocess-top-level-await.js

+66-11
Original file line numberDiff line numberDiff line change
@@ -29,38 +29,93 @@ const testCases = [
2929
[ 'await 0; return 0;',
3030
null ],
3131
[ 'var a = await 1',
32-
'(async () => { void (a = await 1) })()' ],
32+
'var a; (async () => { void (a = await 1) })()' ],
3333
[ 'let a = await 1',
34-
'(async () => { void (a = await 1) })()' ],
34+
'let a; (async () => { void (a = await 1) })()' ],
3535
[ 'const a = await 1',
36-
'(async () => { void (a = await 1) })()' ],
36+
'let a; (async () => { void (a = await 1) })()' ],
3737
[ 'for (var i = 0; i < 1; ++i) { await i }',
38-
'(async () => { for (void (i = 0); i < 1; ++i) { await i } })()' ],
38+
'var i; (async () => { for (void (i = 0); i < 1; ++i) { await i } })()' ],
3939
[ 'for (let i = 0; i < 1; ++i) { await i }',
4040
'(async () => { for (let i = 0; i < 1; ++i) { await i } })()' ],
4141
[ 'var {a} = {a:1}, [b] = [1], {c:{d}} = {c:{d: await 1}}',
42-
'(async () => { void ( ({a} = {a:1}), ([b] = [1]), ' +
42+
'var a, b, d; (async () => { void ( ({a} = {a:1}), ([b] = [1]), ' +
4343
'({c:{d}} = {c:{d: await 1}})) })()' ],
44+
[ 'let [a, b, c] = await ([1, 2, 3])',
45+
'let a, b, c; (async () => { void ([a, b, c] = await ([1, 2, 3])) })()'],
46+
[ 'let {a,b,c} = await ({a: 1, b: 2, c: 3})',
47+
'let a, b, c; (async () => { void ({a,b,c} = ' +
48+
'await ({a: 1, b: 2, c: 3})) })()'],
49+
[ 'let {a: [b]} = {a: [await 1]}, [{d}] = [{d: 3}]',
50+
'let b, d; (async () => { void ( ({a: [b]} = {a: [await 1]}),' +
51+
' ([{d}] = [{d: 3}])) })()'],
4452
/* eslint-disable no-template-curly-in-string */
4553
[ 'console.log(`${(await { a: 1 }).a}`)',
4654
'(async () => { return (console.log(`${(await { a: 1 }).a}`)) })()' ],
4755
/* eslint-enable no-template-curly-in-string */
4856
[ 'await 0; function foo() {}',
49-
'(async () => { await 0; foo=function foo() {} })()' ],
57+
'var foo; (async () => { await 0; foo=function foo() {} })()' ],
5058
[ 'await 0; class Foo {}',
51-
'(async () => { await 0; Foo=class Foo {} })()' ],
59+
'let Foo; (async () => { await 0; Foo=class Foo {} })()' ],
5260
[ 'if (await true) { function foo() {} }',
53-
'(async () => { if (await true) { foo=function foo() {} } })()' ],
61+
'var foo; (async () => { if (await true) { foo=function foo() {} } })()' ],
5462
[ 'if (await true) { class Foo{} }',
5563
'(async () => { if (await true) { class Foo{} } })()' ],
5664
[ 'if (await true) { var a = 1; }',
57-
'(async () => { if (await true) { void (a = 1); } })()' ],
65+
'var a; (async () => { if (await true) { void (a = 1); } })()' ],
5866
[ 'if (await true) { let a = 1; }',
5967
'(async () => { if (await true) { let a = 1; } })()' ],
6068
[ 'var a = await 1; let b = 2; const c = 3;',
61-
'(async () => { void (a = await 1); void (b = 2); void (c = 3); })()' ],
69+
'var a; let b; let c; (async () => { void (a = await 1); void (b = 2);' +
70+
' void (c = 3); })()' ],
6271
[ 'let o = await 1, p',
63-
'(async () => { void ( (o = await 1), (p=undefined)) })()' ],
72+
'let o, p; (async () => { void ( (o = await 1), (p=undefined)) })()' ],
73+
[ 'await (async () => { let p = await 1; return p; })()',
74+
'(async () => { return (await (async () => ' +
75+
'{ let p = await 1; return p; })()) })()' ],
76+
[ '{ let p = await 1; }',
77+
'(async () => { { let p = await 1; } })()' ],
78+
[ 'var p = await 1',
79+
'var p; (async () => { void (p = await 1) })()' ],
80+
[ 'await (async () => { var p = await 1; return p; })()',
81+
'(async () => { return (await (async () => ' +
82+
'{ var p = await 1; return p; })()) })()' ],
83+
[ '{ var p = await 1; }',
84+
'var p; (async () => { { void (p = await 1); } })()' ],
85+
[ 'for await (var i of asyncIterable) { i; }',
86+
'var i; (async () => { for await (i of asyncIterable) { i; } })()'],
87+
[ 'for await (var [i] of asyncIterable) { i; }',
88+
'var i; (async () => { for await ([i] of asyncIterable) { i; } })()'],
89+
[ 'for await (var {i} of asyncIterable) { i; }',
90+
'var i; (async () => { for await ({i} of asyncIterable) { i; } })()'],
91+
[ 'for await (var [{i}, [j]] of asyncIterable) { i; }',
92+
'var i, j; (async () => { for await ([{i}, [j]] of asyncIterable)' +
93+
' { i; } })()'],
94+
[ 'for await (let i of asyncIterable) { i; }',
95+
'(async () => { for await (let i of asyncIterable) { i; } })()'],
96+
[ 'for await (const i of asyncIterable) { i; }',
97+
'(async () => { for await (const i of asyncIterable) { i; } })()'],
98+
[ 'for (var i of [1,2,3]) { await 1; }',
99+
'var i; (async () => { for (i of [1,2,3]) { await 1; } })()'],
100+
[ 'for (var [i] of [[1], [2]]) { await 1; }',
101+
'var i; (async () => { for ([i] of [[1], [2]]) { await 1; } })()'],
102+
[ 'for (var {i} of [{i: 1}, {i: 2}]) { await 1; }',
103+
'var i; (async () => { for ({i} of [{i: 1}, {i: 2}]) { await 1; } })()'],
104+
[ 'for (var [{i}, [j]] of [[{i: 1}, [2]]]) { await 1; }',
105+
'var i, j; (async () => { for ([{i}, [j]] of [[{i: 1}, [2]]])' +
106+
' { await 1; } })()'],
107+
[ 'for (let i of [1,2,3]) { await 1; }',
108+
'(async () => { for (let i of [1,2,3]) { await 1; } })()'],
109+
[ 'for (const i of [1,2,3]) { await 1; }',
110+
'(async () => { for (const i of [1,2,3]) { await 1; } })()'],
111+
[ 'for (var i in {x:1}) { await 1 }',
112+
'var i; (async () => { for (i in {x:1}) { await 1 } })()'],
113+
[ 'for (var [a,b] in {xy:1}) { await 1 }',
114+
'var a, b; (async () => { for ([a,b] in {xy:1}) { await 1 } })()'],
115+
[ 'for (let i in {x:1}) { await 1 }',
116+
'(async () => { for (let i in {x:1}) { await 1 } })()'],
117+
[ 'for (const i in {x:1}) { await 1 }',
118+
'(async () => { for (const i in {x:1}) { await 1 } })()'],
64119
];
65120

66121
for (const [input, expected] of testCases) {

0 commit comments

Comments
 (0)