Skip to content

Commit ffef948

Browse files
committed
repl: add isValidParentheses check before wrap input
1 parent 0230e45 commit ffef948

File tree

4 files changed

+137
-178
lines changed

4 files changed

+137
-178
lines changed

lib/internal/repl/utils.js

Lines changed: 51 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ function isRecoverableError(e, code) {
7272
// here as the point is to test for potentially valid but incomplete
7373
// expressions.
7474
if (RegExpPrototypeExec(/^\s*\{/, code) !== null &&
75-
isRecoverableError(e, `(${code}`))
75+
isRecoverableError(e, `(${code}`))
7676
return true;
7777

7878
let recoverable = false;
@@ -115,10 +115,10 @@ function isRecoverableError(e, code) {
115115

116116
case 'Unterminated string constant': {
117117
const token = StringPrototypeSlice(this.input,
118-
this.lastTokStart, this.pos);
118+
this.lastTokStart, this.pos);
119119
// See https://www.ecma-international.org/ecma-262/#sec-line-terminators
120120
if (RegExpPrototypeExec(/\\(?:\r\n?|\n|\u2028|\u2029)$/,
121-
token) !== null) {
121+
token) !== null) {
122122
recoverable = true;
123123
}
124124
}
@@ -146,7 +146,7 @@ function isRecoverableError(e, code) {
146146
function setupPreview(repl, contextSymbol, bufferSymbol, active) {
147147
// Simple terminals can't handle previews.
148148
if (process.env.TERM === 'dumb' || !active) {
149-
return { showPreview() { }, clearPreview() { } };
149+
return { showPreview() {}, clearPreview() {} };
150150
}
151151

152152
let inputPreview = null;
@@ -171,7 +171,7 @@ function setupPreview(repl, contextSymbol, bufferSymbol, active) {
171171
function isCursorAtInputEnd() {
172172
const { cursorPos, displayPos } = getPreviewPos();
173173
return cursorPos.rows === displayPos.rows &&
174-
cursorPos.cols === displayPos.cols;
174+
cursorPos.cols === displayPos.cols;
175175
}
176176

177177
const clearPreview = (key) => {
@@ -212,9 +212,9 @@ function setupPreview(repl, contextSymbol, bufferSymbol, active) {
212212
escaped = repl.line;
213213
}
214214
} else if ((key.name === 'return' || key.name === 'enter') &&
215-
!key.meta &&
216-
escaped !== repl.line &&
217-
isCursorAtInputEnd()) {
215+
!key.meta &&
216+
escaped !== repl.line &&
217+
isCursorAtInputEnd()) {
218218
repl._insertString(completionPreview);
219219
}
220220
}
@@ -289,22 +289,19 @@ function setupPreview(repl, contextSymbol, bufferSymbol, active) {
289289
function isInStrictMode(repl) {
290290
return repl.replMode === REPL_MODE_STRICT || ArrayPrototypeIncludes(
291291
ArrayPrototypeMap(process.execArgv,
292-
(e) => StringPrototypeReplaceAll(
293-
StringPrototypeToLowerCase(e),
294-
'_',
295-
'-',
296-
)),
292+
(e) => StringPrototypeReplaceAll(
293+
StringPrototypeToLowerCase(e),
294+
'_',
295+
'-',
296+
)),
297297
'--use-strict');
298298
}
299299

300300
// This returns a code preview for arbitrary input code.
301301
function getInputPreview(input, callback) {
302302
// For similar reasons as `defaultEval`, wrap expressions starting with a
303303
// curly brace with parenthesis.
304-
if (StringPrototypeStartsWith(input, '{') &&
305-
!StringPrototypeEndsWith(input, ';') &&
306-
isValidSyntax(input) &&
307-
!wrapped) {
304+
if (!wrapped && input[0] === '{' && input[input.length - 1] !== ';' && isValidSyntax(input)) {
308305
input = `(${input})`;
309306
wrapped = true;
310307
}
@@ -322,16 +319,16 @@ function setupPreview(repl, contextSymbol, bufferSymbol, active) {
322319
const { result } = preview;
323320
if (result.value !== undefined) {
324321
callback(null, inspect(result.value, previewOptions));
325-
// Ignore EvalErrors, SyntaxErrors and ReferenceErrors. It is not clear
326-
// where they came from and if they are recoverable or not. Other errors
327-
// may be inspected.
322+
// Ignore EvalErrors, SyntaxErrors and ReferenceErrors. It is not clear
323+
// where they came from and if they are recoverable or not. Other errors
324+
// may be inspected.
328325
} else if (preview.exceptionDetails &&
329-
(result.className === 'EvalError' ||
330-
result.className === 'SyntaxError' ||
331-
// Report ReferenceError in case the strict mode is active
332-
// for input that has no completions.
333-
(result.className === 'ReferenceError' &&
334-
(hasCompletions || !isInStrictMode(repl))))) {
326+
(result.className === 'EvalError' ||
327+
result.className === 'SyntaxError' ||
328+
// Report ReferenceError in case the strict mode is active
329+
// for input that has no completions.
330+
(result.className === 'ReferenceError' &&
331+
(hasCompletions || !isInStrictMode(repl))))) {
335332
callback(null, null);
336333
} else if (result.objectId) {
337334
// The writer options might change and have influence on the inspect
@@ -371,9 +368,9 @@ function setupPreview(repl, contextSymbol, bufferSymbol, active) {
371368
const showPreview = (showCompletion = true) => {
372369
// Prevent duplicated previews after a refresh or in a multiline command.
373370
if (inputPreview !== null ||
374-
repl[kIsMultiline] ||
375-
!repl.isCompletionEnabled ||
376-
!process.features.inspector) {
371+
repl[kIsMultiline] ||
372+
!repl.isCompletionEnabled ||
373+
!process.features.inspector) {
377374
return;
378375
}
379376

@@ -416,7 +413,7 @@ function setupPreview(repl, contextSymbol, bufferSymbol, active) {
416413
// Do not preview `undefined` if colors are deactivated or explicitly
417414
// requested.
418415
if (inspected === 'undefined' &&
419-
(!repl.useColors || repl.ignoreUndefined)) {
416+
(!repl.useColors || repl.ignoreUndefined)) {
420417
return;
421418
}
422419

@@ -430,7 +427,7 @@ function setupPreview(repl, contextSymbol, bufferSymbol, active) {
430427
// Support unicode characters of width other than one by checking the
431428
// actual width.
432429
if (inspected.length * 2 >= maxColumns &&
433-
getStringWidth(inspected) > maxColumns) {
430+
getStringWidth(inspected) > maxColumns) {
434431
maxColumns -= 4 + (repl.useColors ? 0 : 3);
435432
let res = '';
436433
for (const char of new SafeStringIterator(inspected)) {
@@ -469,8 +466,8 @@ function setupPreview(repl, contextSymbol, bufferSymbol, active) {
469466
let previewLine = line;
470467

471468
if (completionPreview !== null &&
472-
isCursorAtInputEnd() &&
473-
escaped !== repl.line) {
469+
isCursorAtInputEnd() &&
470+
escaped !== repl.line) {
474471
previewLine += completionPreview;
475472
}
476473

@@ -504,8 +501,8 @@ function setupPreview(repl, contextSymbol, bufferSymbol, active) {
504501
const currentCursor = repl.cursor;
505502
originalMoveCursor(dx);
506503
if (currentCursor + dx > repl.line.length &&
507-
typeof repl.completer === 'function' &&
508-
insertCompletionPreview) {
504+
typeof repl.completer === 'function' &&
505+
insertCompletionPreview) {
509506
const insertPreview = true;
510507
showCompletionPreview(repl.line, insertPreview);
511508
}
@@ -600,7 +597,7 @@ function setupReverseSearch(repl) {
600597
// Match not found.
601598
if (cursor === -1) {
602599
goToNextHistoryIndex();
603-
// Match found.
600+
// Match found.
604601
} else {
605602
if (repl.useColors) {
606603
const start = StringPrototypeSlice(entry, 0, cursor);
@@ -613,7 +610,7 @@ function setupReverseSearch(repl) {
613610
// Explicitly go to the next history item in case no further matches are
614611
// possible with the current entry.
615612
if ((dir === 'r' && cursor === 0) ||
616-
(dir === 's' && entry.length === cursor + input.length)) {
613+
(dir === 's' && entry.length === cursor + input.length)) {
617614
goToNextHistoryIndex();
618615
}
619616
return;
@@ -726,7 +723,7 @@ function setupReverseSearch(repl) {
726723
} else if (key.ctrl && checkAndSetDirectionKey(key.name)) {
727724
search();
728725
} else if (key.name === 'backspace' ||
729-
(key.ctrl && (key.name === 'h' || key.name === 'w'))) {
726+
(key.ctrl && (key.name === 'h' || key.name === 'w'))) {
730727
reset(StringPrototypeSlice(input, 0, input.length - 1));
731728
search();
732729
// Special handle <ctrl> + c and escape. Those should only cancel the
@@ -738,11 +735,11 @@ function setupReverseSearch(repl) {
738735
// End search in case either enter is pressed or if any non-reverse-search
739736
// key (combination) is pressed.
740737
} else if (key.ctrl ||
741-
key.meta ||
742-
key.name === 'return' ||
743-
key.name === 'enter' ||
744-
typeof string !== 'string' ||
745-
string === '') {
738+
key.meta ||
739+
key.name === 'return' ||
740+
key.name === 'enter' ||
741+
typeof string !== 'string' ||
742+
string === '') {
746743
reset();
747744
repl[kSubstringSearch] = '';
748745
} else {
@@ -789,52 +786,15 @@ function isValidSyntax(input) {
789786
function isObjectLiteral(code) {
790787
return RegExpPrototypeExec(startsWithBraceRegExp, code) !== null &&
791788
RegExpPrototypeExec(endsWithSemicolonRegExp, code) === null;
792-
function isValidSyntax(input) {
793-
const Parser = require('internal/deps/acorn/acorn/dist/acorn').Parser;
794-
try {
795-
Parser.parse(input, { ecmaVersion: 'latest' });
796-
return true;
797-
} catch {
798-
return false;
799-
}
800-
}
801-
802-
function isValidParentheses(input) {
803-
const stack = [];
804-
const pairs = {
805-
'(': ')',
806-
'[': ']',
807-
'{': '}',
808-
};
809-
810-
for (let i = 0; i < input.length; i++) {
811-
const char = input[i];
812-
813-
if (pairs[char]) {
814-
stack.push(char);
815-
} else if (char === ')' || char === ']' || char === '}') {
816-
if (pairs[stack.pop()] !== char) {
817-
return false;
818-
}
819-
}
820-
function isValidSyntax(input) {
821-
const Parser = require('internal/deps/acorn/acorn/dist/acorn').Parser;
822-
try {
823-
let result = Parser.parse(input, { ecmaVersion: 'latest' });
824-
return true;
825-
} catch (e) {
826-
return false;
827-
}
828-
}
789+
}
829790

830-
module.exports = {
831-
REPL_MODE_SLOPPY: Symbol('repl-sloppy'),
832-
REPL_MODE_STRICT,
833-
isRecoverableError,
834-
kStandaloneREPL: Symbol('kStandaloneREPL'),
835-
setupPreview,
836-
setupReverseSearch,
837-
isObjectLiteral,
838-
isValidParentheses,
839-
isValidSyntax,
840-
};
791+
module.exports = {
792+
REPL_MODE_SLOPPY: Symbol('repl-sloppy'),
793+
REPL_MODE_STRICT,
794+
isRecoverableError,
795+
kStandaloneREPL: Symbol('kStandaloneREPL'),
796+
setupPreview,
797+
setupReverseSearch,
798+
isObjectLiteral,
799+
isValidSyntax,
800+
};

0 commit comments

Comments
 (0)