From 7a8ce5f653d2fa7621ddbe2f8f405a93b469e300 Mon Sep 17 00:00:00 2001 From: Theodor Steiner Date: Wed, 22 Feb 2023 18:57:16 +0900 Subject: [PATCH] fix: use private block scope RegExp instead of shared module scope RegExp --- lib/internal/readline/interface.js | 28 ++++++++++++++++++---------- lib/repl.js | 5 +---- 2 files changed, 19 insertions(+), 14 deletions(-) diff --git a/lib/internal/readline/interface.js b/lib/internal/readline/interface.js index 45524612aea37b..75843fe96251ec 100644 --- a/lib/internal/readline/interface.js +++ b/lib/internal/readline/interface.js @@ -20,6 +20,7 @@ const { MathMaxApply, NumberIsFinite, ObjectSetPrototypeOf, + RegExp, RegExpPrototypeExec, StringPrototypeCodePointAt, StringPrototypeEndsWith, @@ -72,7 +73,7 @@ const kHistorySize = 30; const kMaxUndoRedoStackSize = 2048; const kMincrlfDelay = 100; // \r\n, \n, or \r followed by something other than \n -const lineEnding = /\r?\n|\r(?!\n)/g; +const lineEndingPattern = '\r?\n|\r(?!\n)'; const kLineObjectStream = Symbol('line object stream'); const kQuestionCancel = Symbol('kQuestionCancel'); @@ -585,6 +586,7 @@ class Interface extends InterfaceConstructor { } // Run test() on the new string chunk, not on the entire line buffer. + const lineEnding = new RegExp(lineEndingPattern, 'g'); let newPartContainsEnding = RegExpPrototypeExec(lineEnding, string); if (newPartContainsEnding !== null) { if (this[kLine_buffer]) { @@ -1322,18 +1324,24 @@ class Interface extends InterfaceConstructor { // falls through default: if (typeof s === 'string' && s) { + /** + * Use Regular Expression scoped to this block, as lastIndex and the state for RegExpPrototypeExec + * will be overwritten if the same RegEx instance is reused in recursive function calls. + */ + const lineEnding = new RegExp(lineEndingPattern, 'g'); let nextMatch = RegExpPrototypeExec(lineEnding, s); - if (nextMatch !== null) { - this[kInsertString](StringPrototypeSlice(s, 0, nextMatch.index)); - let { lastIndex } = lineEnding; - while ((nextMatch = RegExpPrototypeExec(lineEnding, s)) !== null) { - this[kLine](); + + // If no line endings are found, just insert the string as is + if (nextMatch === null) { + this[kInsertString](s); + } else { + // Keep track of the end of the last match + let lastIndex = 0; + do { this[kInsertString](StringPrototypeSlice(s, lastIndex, nextMatch.index)); + this[kLine](); ({ lastIndex } = lineEnding); - } - if (lastIndex === s.length) this[kLine](); - } else { - this[kInsertString](s); + } while ((nextMatch = RegExpPrototypeExec(lineEnding, s)) !== null); } } } diff --git a/lib/repl.js b/lib/repl.js index b99aba113cebdd..638fb04689c3d0 100644 --- a/lib/repl.js +++ b/lib/repl.js @@ -862,7 +862,7 @@ function REPLServer(prompt, const matches = self._sawKeyPress ? RegExpPrototypeExec(/^\s+/, cmd) : null; // Preserve indentation in editorMode - if (matches && !self.loadMode) { + if (matches) { const prefix = matches[0]; self.write(prefix); self.line = prefix; @@ -1780,11 +1780,9 @@ function defineDefaultCommands(repl) { const stats = fs.statSync(file); if (stats && stats.isFile()) { _turnOnEditorMode(this); - this.loadMode = true; const data = fs.readFileSync(file, 'utf8'); this.write(data); _turnOffEditorMode(this); - this.loadMode = false; this.write('\n'); } else { this.output.write( @@ -1793,7 +1791,6 @@ function defineDefaultCommands(repl) { } } catch { this.output.write(`Failed to load: ${file}\n`); - this.loadMode = false; } this.displayPrompt(); }