From 7377a78f351114d3f54c36bc4b024cdc6014c6f0 Mon Sep 17 00:00:00 2001 From: Dario Piotrowicz Date: Sun, 16 Mar 2025 00:12:46 +0000 Subject: [PATCH 1/3] debugger: fix behavior of plain object exec in debugger repl Co-authored-by: Xuguang Mei --- lib/internal/debugger/inspect_repl.js | 10 +++++++++- lib/repl.js | 3 ++- test/parallel/test-debugger-exec.js | 5 +++++ 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/lib/internal/debugger/inspect_repl.js b/lib/internal/debugger/inspect_repl.js index 8dc5e9823cae6e..f2d5eabfe70a26 100644 --- a/lib/internal/debugger/inspect_repl.js +++ b/lib/internal/debugger/inspect_repl.js @@ -573,8 +573,16 @@ function createRepl(inspector) { if (input === '\n') return lastCommand; // Add parentheses: exec process.title => exec("process.title"); const match = RegExpPrototypeExec(/^\s*(?:exec|p)\s+([^\n]*)/, input); + input = match ? match[1] : input; + + // Add parentheses to make sure input is parsed as expression + if (RegExpPrototypeExec(/^\s*{/, input) !== null && + RegExpPrototypeExec(/;\s*$/, input) === null) { + input = `(${StringPrototypeTrim(input)})\n`; + } + if (match) { - lastCommand = `exec(${JSONStringify(match[1])})`; + lastCommand = `exec(${JSONStringify(input)})`; } else { lastCommand = input; } diff --git a/lib/repl.js b/lib/repl.js index 6cfceacac620f0..7e8cf13da2ce17 100644 --- a/lib/repl.js +++ b/lib/repl.js @@ -440,7 +440,8 @@ function REPLServer(prompt, // statement rather than an object literal. So, we first try // to wrap it in parentheses, so that it will be interpreted as // an expression. Note that if the above condition changes, - // lib/internal/repl/utils.js needs to be changed to match. + // lib/internal/repl/utils.js and lib/internal/debugger/inspect_repl.js + // need to be changed to match. if (RegExpPrototypeExec(/^\s*{/, code) !== null && RegExpPrototypeExec(/;\s*$/, code) === null) { code = `(${StringPrototypeTrim(code)})\n`; diff --git a/test/parallel/test-debugger-exec.js b/test/parallel/test-debugger-exec.js index 51bc7497345ba2..536e0128ea2a84 100644 --- a/test/parallel/test-debugger-exec.js +++ b/test/parallel/test-debugger-exec.js @@ -60,6 +60,11 @@ async function waitInitialBreak() { /\[ 'undefined', 'function' \]/, 'non-paused exec can see global but not module-scope values' ); + + // Ref: https://github.com/nodejs/node/issues/46808 + await cli.waitForPrompt(); + await cli.command('exec { a: 1 }'); + assert.match(cli.output, /\{ a: 1 \}/); } finally { await cli.quit(); } From 146f2682dabd382d9d3663786789f472151d639c Mon Sep 17 00:00:00 2001 From: Dario Piotrowicz Date: Sun, 16 Mar 2025 11:58:13 +0000 Subject: [PATCH 2/3] fixup! debugger: fix behavior of plain object exec in debugger repl add `isObjectLiteral` function to share logic --- lib/internal/debugger/inspect_repl.js | 6 +++--- lib/internal/repl/utils.js | 14 ++++++++++++++ lib/repl.js | 11 +++-------- 3 files changed, 20 insertions(+), 11 deletions(-) diff --git a/lib/internal/debugger/inspect_repl.js b/lib/internal/debugger/inspect_repl.js index f2d5eabfe70a26..2a0bd4e5149fd7 100644 --- a/lib/internal/debugger/inspect_repl.js +++ b/lib/internal/debugger/inspect_repl.js @@ -56,6 +56,7 @@ const { fileURLToPath } = require('internal/url'); const { customInspectSymbol, SideEffectFreeRegExpPrototypeSymbolReplace } = require('internal/util'); const { inspect: utilInspect } = require('internal/util/inspect'); +const { isObjectLiteral } = require('internal/repl/utils'); const debuglog = require('internal/util/debuglog').debuglog('inspect'); const SHORTCUTS = { @@ -575,9 +576,8 @@ function createRepl(inspector) { const match = RegExpPrototypeExec(/^\s*(?:exec|p)\s+([^\n]*)/, input); input = match ? match[1] : input; - // Add parentheses to make sure input is parsed as expression - if (RegExpPrototypeExec(/^\s*{/, input) !== null && - RegExpPrototypeExec(/;\s*$/, input) === null) { + if (isObjectLiteral(input)) { + // Add parentheses to make sure `input` is parsed as an expression input = `(${StringPrototypeTrim(input)})\n`; } diff --git a/lib/internal/repl/utils.js b/lib/internal/repl/utils.js index 126f8ae85d0977..9301c9eb700896 100644 --- a/lib/internal/repl/utils.js +++ b/lib/internal/repl/utils.js @@ -739,6 +739,19 @@ function setupReverseSearch(repl) { return { reverseSearch }; } +/** + * Checks if some provided code represents an object literal. + * This is helpful to prevent confusing repl code evaluations where + * strings such as `{ a : 1 }` would get interpreted as block statements + * rather than object literals. + * @param {string} code the code to check + * @returns {boolean} true if the code represents an object literal, false otherwise + */ +function isObjectLiteral(code) { + return RegExpPrototypeExec(/^\s*{/, code) !== null && + RegExpPrototypeExec(/;\s*$/, code) === null; +} + module.exports = { REPL_MODE_SLOPPY: Symbol('repl-sloppy'), REPL_MODE_STRICT, @@ -746,4 +759,5 @@ module.exports = { kStandaloneREPL: Symbol('kStandaloneREPL'), setupPreview, setupReverseSearch, + isObjectLiteral, }; diff --git a/lib/repl.js b/lib/repl.js index 7e8cf13da2ce17..e61a24bb041c7e 100644 --- a/lib/repl.js +++ b/lib/repl.js @@ -172,6 +172,7 @@ const { kStandaloneREPL, setupPreview, setupReverseSearch, + isObjectLiteral, } = require('internal/repl/utils'); const { constants: { @@ -436,14 +437,8 @@ function REPLServer(prompt, let awaitPromise = false; const input = code; - // It's confusing for `{ a : 1 }` to be interpreted as a block - // statement rather than an object literal. So, we first try - // to wrap it in parentheses, so that it will be interpreted as - // an expression. Note that if the above condition changes, - // lib/internal/repl/utils.js and lib/internal/debugger/inspect_repl.js - // need to be changed to match. - if (RegExpPrototypeExec(/^\s*{/, code) !== null && - RegExpPrototypeExec(/;\s*$/, code) === null) { + if (isObjectLiteral(code)) { + // Add parentheses to make sure `code` is parsed as an expression code = `(${StringPrototypeTrim(code)})\n`; wrappedCmd = true; } From 0804e770a361b8be235c6b407a2a6d7ba2b5566f Mon Sep 17 00:00:00 2001 From: Dario Piotrowicz Date: Sun, 16 Mar 2025 15:11:53 +0000 Subject: [PATCH 3/3] fixup! fixup! debugger: fix behavior of plain object exec in debugger repl move regexes outside of `isObjectLiteral` --- lib/internal/repl/utils.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/internal/repl/utils.js b/lib/internal/repl/utils.js index 9301c9eb700896..79a2c55dff16d7 100644 --- a/lib/internal/repl/utils.js +++ b/lib/internal/repl/utils.js @@ -739,6 +739,9 @@ function setupReverseSearch(repl) { return { reverseSearch }; } +const startsWithBraceRegExp = /^\s*{/; +const endsWithSemicolonRegExp = /;\s*$/; + /** * Checks if some provided code represents an object literal. * This is helpful to prevent confusing repl code evaluations where @@ -748,8 +751,8 @@ function setupReverseSearch(repl) { * @returns {boolean} true if the code represents an object literal, false otherwise */ function isObjectLiteral(code) { - return RegExpPrototypeExec(/^\s*{/, code) !== null && - RegExpPrototypeExec(/;\s*$/, code) === null; + return RegExpPrototypeExec(startsWithBraceRegExp, code) !== null && + RegExpPrototypeExec(endsWithSemicolonRegExp, code) === null; } module.exports = {