Skip to content

Commit a1d5b9c

Browse files
committed
repl: hoist declarations when processing top level await
1 parent c2e6822 commit a1d5b9c

2 files changed

Lines changed: 81 additions & 30 deletions

File tree

lib/internal/repl/await.js

Lines changed: 58 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ const {
44
ArrayFrom,
55
ArrayPrototypeForEach,
66
ArrayPrototypeJoin,
7+
ArrayPrototypeMap,
78
ArrayPrototypePop,
89
ArrayPrototypePush,
910
FunctionPrototype,
@@ -25,9 +26,9 @@ const { Recoverable } = require('internal/repl');
2526
const noop = FunctionPrototype;
2627
const visitorsWithoutAncestors = {
2728
ClassDeclaration(node, state, c) {
28-
if (state.ancestors[state.ancestors.length - 2] === state.body) {
29-
state.prepend(node, `${node.id.name}=`);
30-
}
29+
state.prepend(node, `${node.id.name}=`);
30+
state.prepend(state.root.body[0], `let ${node.id.name}; `);
31+
3132
walk.base.ClassDeclaration(node, state, c);
3233
},
3334
ForOfStatement(node, state, c) {
@@ -38,6 +39,7 @@ const visitorsWithoutAncestors = {
3839
},
3940
FunctionDeclaration(node, state, c) {
4041
state.prepend(node, `${node.id.name}=`);
42+
state.prepend(state.root.body[0], `let ${node.id.name}; `);
4143
},
4244
FunctionExpression: noop,
4345
ArrowFunctionExpression: noop,
@@ -51,24 +53,63 @@ const visitorsWithoutAncestors = {
5153
walk.base.ReturnStatement(node, state, c);
5254
},
5355
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 (');
60-
}
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 (');
60+
}
61+
62+
ArrayPrototypeForEach(node.declarations, (decl) => {
63+
state.prepend(decl, '(');
64+
state.append(decl, decl.init ? ')' : '=undefined)');
65+
});
6166

62-
ArrayPrototypeForEach(node.declarations, (decl) => {
63-
state.prepend(decl, '(');
64-
state.append(decl, decl.init ? ')' : '=undefined)');
65-
});
67+
if (node.declarations.length !== 1) {
68+
state.append(node.declarations[node.declarations.length - 1], ')');
69+
}
70+
71+
const declarationSeparator = ', ';
72+
73+
function getVariableDeclarationIdentifier(node) {
74+
switch (node.type) {
75+
case 'Identifier':
76+
return node.name;
77+
case 'ObjectPattern':
78+
return ArrayPrototypeJoin(
79+
ArrayPrototypeMap(
80+
node.properties,
81+
(property) => getVariableDeclarationIdentifier(property.value)
82+
),
83+
declarationSeparator
84+
);
85+
case 'ArrayPattern':
86+
return ArrayPrototypeJoin(
87+
ArrayPrototypeMap(
88+
node.elements,
89+
(element) => getVariableDeclarationIdentifier(element)
90+
),
91+
declarationSeparator
92+
);
93+
}
94+
}
6695

67-
if (node.declarations.length !== 1) {
68-
state.append(node.declarations[node.declarations.length - 1], ')');
96+
const variableIdentifiersToHoist = [];
97+
for (const decl of node.declarations) {
98+
const identifier = getVariableDeclarationIdentifier(decl.id);
99+
if (identifier !== undefined) {
100+
variableIdentifiersToHoist.push(identifier);
69101
}
70102
}
71103

104+
state.prepend(
105+
state.root.body[0],
106+
'let ' +
107+
ArrayPrototypeJoin(
108+
variableIdentifiersToHoist,
109+
declarationSeparator
110+
) + '; '
111+
);
112+
72113
walk.base.VariableDeclaration(node, state, c);
73114
}
74115
};
@@ -126,6 +167,7 @@ function processTopLevelAwait(src) {
126167
}
127168
const body = root.body[0].expression.callee.body;
128169
const state = {
170+
root,
129171
body,
130172
ancestors: [],
131173
replace(from, to, str) {

test/parallel/test-repl-preprocess-top-level-await.js

Lines changed: 23 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -29,38 +29,47 @@ const testCases = [
2929
[ 'await 0; return 0;',
3030
null ],
3131
[ 'var a = await 1',
32-
'(async () => { void (a = await 1) })()' ],
32+
'let 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+
'let i; (async () => { for (void (i = 0); i < 1; ++i) { await i } })()' ],
3939
[ 'for (let i = 0; i < 1; ++i) { await i }',
40-
'(async () => { for (let i = 0; i < 1; ++i) { await i } })()' ],
40+
'let i; (async () => { for (void (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+
'let 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+
'let 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+
'let foo; (async () => { if (await true) { foo=function foo() {} } })()' ],
5462
[ 'if (await true) { class Foo{} }',
55-
'(async () => { if (await true) { class Foo{} } })()' ],
63+
'let Foo; (async () => { if (await true) { Foo=class Foo{} } })()' ],
5664
[ 'if (await true) { var a = 1; }',
57-
'(async () => { if (await true) { void (a = 1); } })()' ],
65+
'let a; (async () => { if (await true) { void (a = 1); } })()' ],
5866
[ 'if (await true) { let a = 1; }',
59-
'(async () => { if (await true) { let a = 1; } })()' ],
67+
'let a; (async () => { if (await true) { void (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+
'let c; let b; let a; (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)) })()' ],
6473
];
6574

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

0 commit comments

Comments
 (0)