nodejs / nodejs/node

REPL: dot-commands produce SyntaxError inside multi-line input

オープン
#63,864 コメント 1 件 リアクション 0 件 担当者 0 名 GitHub で見る

まだ誰も着手していません。

repl
主要言語
JavaScript
スター
122k
フォーク
37.3k
平均マージ
4日 2時間
マージ済み PR(30日)
283

説明

Version

any v24.1.0+

Platform
Tested on:
Microsoft Windows NT 10.0.19045.0 x64;
WSL2 Debian;
Subsystem

REPL

What steps will reproduce the bug?

printf "function a() {\n.break\n" | node -i
printf "function a() {\n.exit\n" | node -i
printf "function a() {\n.help\n" | node -i

How often does it reproduce? Is there a required condition?

Always. Required condition: any registered dot-command typed as the first line after a Recoverable error if self.terminal === true. This includes both real interactive TTYs and node -i with piped stdin.

What is the expected behavior? Why is that the expected behavior?
Welcome to Node.js v24.0.2.
Type ".help" for more information.
> function a() {
| .exit
Welcome to Node.js v24.0.2.
Type ".help" for more information.
> function a() {
| .break
>
Welcome to Node.js v24.0.2.
Type ".help" for more information.
> function a() {
| .help
.break    Sometimes you get stuck, this gets you out
.clear    Alias for .break
.editor   Enter editor mode
.exit     Exit the REPL
.help     Print this help message
.load     Load JS from a file into the REPL session
.save     Save all evaluated commands in this REPL session to a file
Press Ctrl+C to abort current expression, Ctrl+D to exit the REPL
|
What do you see instead?
Welcome to Node.js v24.1.0.
Type ".help" for more information.
> function a() {
| .exit
.exit
^
Uncaught SyntaxError: Unexpected token '.'
Welcome to Node.js v24.1.0.
Type ".help" for more information.
> function a() {
| .break
.break
^
Uncaught SyntaxError: Unexpected token '.'
Welcome to Node.js v24.1.0.
Type ".help" for more information.
> function a() {
| .help
.help
^
Uncaught SyntaxError: Unexpected token '.'
Additional information

The expected behavior is dot-commands working in multi-line mode. Since 24.1.0 they do not. Only changes to TTY multi-line input behavior in 24.1.0 were introduced by #58003.
Bisect:

git checkout v24.1.0
./configure
make -j2 node > /dev/null 2>/dev/null 
printf "function a(){\n.break\n" | ./node -i

git checkout 629a954477^
make -j2 node > /dev/null 2>/dev/null
printf "function a(){\n.break\n" | ./node -i

git checkout 629a954477
make -j2 node > /dev/null 2>/dev/null
printf "function a(){\n.break\n" | ./node -i

Hunk level bisect:

git checkout 629a954477
make -j2 node >/dev/null 2>/dev/null

printf "n\nn\nn\nn\ny\nn\n" | git checkout -p 629a954477^ -- lib/repl.js
make -j2 node >/dev/null 2>/dev/null
printf "function a() {\n.break\n" | ./node -i

particular hunk introducing the bug:

@@ -943,33 +941,15 @@ function REPLServer(prompt,
       }

       // If error was SyntaxError and not JSON.parse error
-      if (e) {
-        if (e instanceof Recoverable && !sawCtrlD) {
-          // Start buffering data like that:
-          // {
-          // ...  x: 1
-          // ... }
+      // We can start a multiline command
+      if (e instanceof Recoverable && !sawCtrlD) {
+        if (self.terminal) {
+          self[kAddNewLineOnTTY]();
+        } else {
           self[kBufferedCommandSymbol] += cmd + '\n';
           self.displayPrompt();
-          return;
-        }
-      }
-
-      // In the next two if blocks, we do not use os.EOL instead of '\n'
-      // because on Windows it is '\r\n'
-      if (StringPrototypeIncludes(cmd, '\n')) { // If you are editing a multiline command
-        self.history[0] = self[kNormalizeHistoryLineEndings](cmd, '\n', '\r');
-      } else if (self[kBufferedCommandSymbol]) { // If a new multiline command was entered
-        // Remove the first N lines from the self.history array
-        // where N is the number of lines in the buffered command
-
-        const lines = StringPrototypeSplit(self[kBufferedCommandSymbol], '\n');
-        self.history = ArrayPrototypeSlice(self.history, lines.length);
-        lines[lines.length - 1] = cmd;
-        const newHistoryLine = ArrayPrototypeJoin(ArrayPrototypeReverse(lines), '\r');
-        if (self.history[0] !== newHistoryLine) {
-          ArrayPrototypeUnshift(self.history, newHistoryLine);
         }
+        return;
       }

       if (e) {

Reason:

  1. The change in handling of Recoverable error shifts the TTY multi-line handling into self[kAddNewLineOnTTY]();.
  2. It causes onLine(cmd) handler to be called with the whole multi-line buffer in cmd.
  3. That results in the REPL keyword check failing - instead of checking the first character in ".break" it checks the first character in "function a() {\r.break". (\n becomes \r in kAddHistory, not relevant to the bug)
  4. Which leads to REPL keywords being passed to eval where they produce SyntaxError: Unexpected token '.'.

コントリビューションガイド

コントリビューションガイドを開く

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

調査の方向性

lib/repl.js から始め、Recoverable-error パス、kAddNewLineOnTTY、onLine(cmd) ハンドラーに注目してください。提供されている printf コマンドを node -i で使って再現し、その後、バッファリングされた複数行入力がドットコマンドのチェックに到達するまでを追跡してください。複数行入力内で .break、.clear、.exit、.help が SyntaxError を発生させずに動作すれば完了です。

索引モデルが issue の本文から書いたものです。

評価

技術スタック
javascript, nodejs
領域
cli
issue の種類
バグ
難易度
3/5
見積もり時間
1〜2日
活発さ
静か
明瞭さ
明確に書かれている
初心者へのやさしさ
72/100

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。