Skip to content

Commit c206f8d

Browse files
meixgtargos
authored andcommitted
repl: add isValidParentheses check before wrap input
PR-URL: #59607 Reviewed-By: Ruben Bridgewater <[email protected]> Reviewed-By: Antoine du Hamel <[email protected]>
1 parent 0718a70 commit c206f8d

File tree

4 files changed

+121
-4
lines changed

4 files changed

+121
-4
lines changed

‎lib/internal/repl/utils.js‎

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -301,7 +301,7 @@ function setupPreview(repl, contextSymbol, bufferSymbol, active){
301301
functiongetInputPreview(input,callback){
302302
// For similar reasons as `defaultEval`, wrap expressions starting with a
303303
// curly brace with parenthesis.
304-
if(!wrapped&&input[0]==='{'&&input[input.length-1]!==';'){
304+
if(!wrapped&&input[0]==='{'&&input[input.length-1]!==';'&&isValidSyntax(input)){
305305
input=`(${input})`;
306306
wrapped=true;
307307
}
@@ -754,6 +754,25 @@ function setupReverseSearch(repl){
754754

755755
conststartsWithBraceRegExp=/^\s*{/;
756756
constendsWithSemicolonRegExp=/;\s*$/;
757+
functionisValidSyntax(input){
758+
try{
759+
AcornParser.parse(input,{
760+
ecmaVersion: 'latest',
761+
allowAwaitOutsideFunction: true,
762+
});
763+
returntrue;
764+
}catch{
765+
try{
766+
AcornParser.parse(`_=${input}`,{
767+
ecmaVersion: 'latest',
768+
allowAwaitOutsideFunction: true,
769+
});
770+
returntrue;
771+
}catch{
772+
returnfalse;
773+
}
774+
}
775+
}
757776

758777
/**
759778
* Checks if some provided code represents an object literal.
@@ -776,4 +795,5 @@ module.exports ={
776795
setupPreview,
777796
setupReverseSearch,
778797
isObjectLiteral,
798+
isValidSyntax,
779799
};

‎lib/repl.js‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,7 @@ const{
177177
setupPreview,
178178
setupReverseSearch,
179179
isObjectLiteral,
180+
isValidSyntax,
180181
}=require('internal/repl/utils');
181182
const{
182183
constants: {
@@ -445,7 +446,7 @@ function REPLServer(prompt,
445446
letawaitPromise=false;
446447
constinput=code;
447448

448-
if(isObjectLiteral(code)){
449+
if(isObjectLiteral(code)&&isValidSyntax(code)){
449450
// Add parentheses to make sure `code` is parsed as an expression
450451
code=`(${StringPrototypeTrim(code)})\n`;
451452
wrappedCmd=true;
@@ -2156,6 +2157,7 @@ module.exports ={
21562157
REPL_MODE_SLOPPY,
21572158
REPL_MODE_STRICT,
21582159
Recoverable,
2160+
isValidSyntax,
21592161
};
21602162

21612163
ObjectDefineProperty(module.exports,'builtinModules',{

‎test/parallel/test-repl-preview.js‎

Lines changed: 84 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,83 @@ async function tests(options){
157157
'\x1B[90m1\x1B[39m\x1B[12G\x1B[1A\x1B[1B\x1B[2K\x1B[1A\r',
158158
'\x1B[33m1\x1B[39m',
159159
]
160+
},{
161+
input: 'aaaa',
162+
noPreview: 'Uncaught ReferenceError: aaaa is not defined',
163+
preview: [
164+
'aaaa\r',
165+
'Uncaught ReferenceError: aaaa is not defined',
166+
]
167+
},{
168+
input: '/0',
169+
noPreview: '/0',
170+
preview: [
171+
'/0\r',
172+
'/0',
173+
'^',
174+
'',
175+
'Uncaught SyntaxError: Invalid regular expression: missing /',
176+
]
177+
},{
178+
input: '{})',
179+
noPreview: '{})',
180+
preview: [
181+
'{})\r',
182+
'{})',
183+
' ^',
184+
'',
185+
"Uncaught SyntaxError: Unexpected token ')'",
186+
],
187+
},{
188+
input: "{a: '{'}",
189+
noPreview: "{a: \x1B[32m'{'\x1B[39m }",
190+
preview: [
191+
"{a: '{'}\r",
192+
"{a: \x1B[32m'{'\x1B[39m }",
193+
],
194+
},{
195+
input: "{'{':0}",
196+
noPreview: "{\x1B[32m'{'\x1B[39m: \x1B[33m0\x1B[39m }",
197+
preview: [
198+
"{'{':0}",
199+
"\x1B[90m{'{': 0 }\x1B[39m\x1B[15G\x1B[1A\x1B[1B\x1B[2K\x1B[1A\r",
200+
"{\x1B[32m'{'\x1B[39m: \x1B[33m0\x1B[39m }",
201+
],
202+
},{
203+
input: '{[Symbol.for("{")]: 0 }',
204+
noPreview: '{\x1B[32mSymbol({)\x1B[39m: \x1B[33m0\x1B[39m }',
205+
preview: [
206+
'{[Symbol.for("{")]: 0 }\r',
207+
'{\x1B[32mSymbol({)\x1B[39m: \x1B[33m0\x1B[39m }',
208+
],
209+
},{
210+
input: '{},{}',
211+
noPreview: '{}',
212+
preview: [
213+
'{},{}',
214+
'\x1B[90m{}\x1B[39m\x1B[13G\x1B[1A\x1B[1B\x1B[2K\x1B[1A\r',
215+
'{}',
216+
],
217+
},{
218+
input: '{} //',
219+
noPreview: 'repl > ',
220+
preview: [
221+
'{} //\r',
222+
],
223+
},{
224+
input: '{} //;',
225+
noPreview: 'repl > ',
226+
preview: [
227+
'{} //;\r',
228+
],
229+
},{
230+
input: '{throw 0}',
231+
noPreview: 'Uncaught \x1B[33m0\x1B[39m',
232+
preview: [
233+
'{throw 0}',
234+
'\x1B[90m0\x1B[39m\x1B[17G\x1B[1A\x1B[1B\x1B[2K\x1B[1A\r',
235+
'Uncaught \x1B[33m0\x1B[39m',
236+
],
160237
}];
161238

162239
consthasPreview=repl.terminal&&
@@ -177,8 +254,13 @@ async function tests(options){
177254
assert.deepStrictEqual(lines,preview);
178255
}else{
179256
assert.ok(lines[0].includes(noPreview),lines.map(inspect));
180-
if(preview.length!==1||preview[0]!==`${input}\r`)
181-
assert.strictEqual(lines.length,2);
257+
if(preview.length!==1||preview[0]!==`${input}\r`){
258+
if(preview[preview.length-1].includes('Uncaught SyntaxError')){
259+
assert.strictEqual(lines.length,5);
260+
}else{
261+
assert.strictEqual(lines.length,2);
262+
}
263+
}
182264
}
183265
}
184266
}

‎test/parallel/test-repl.js‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,19 @@ const errorTests = [
328328
expect: '[Function (anonymous)]'
329329
},
330330
// Multiline object
331+
{
332+
send: '{}),({}',
333+
expect: '| ',
334+
},
335+
{
336+
send: '}',
337+
expect: [
338+
'{}),({}',
339+
kArrow,
340+
'',
341+
/^UncaughtSyntaxError:/,
342+
]
343+
},
331344
{
332345
send: '{a: ',
333346
expect: '| '

0 commit comments

Comments
(0)