debugger: fix behavior of plain object exec in debugger repl

Co-authored-by: Xuguang Mei <meixuguang@gmail.com>
PR-URL: https://github.com/nodejs/node/pull/57498
Fixes: https://github.com/nodejs/node/issues/46808
Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Xuguang Mei <meixuguang@gmail.com>
Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de>
This commit is contained in:
Dario Piotrowicz 2025-03-18 19:30:31 +00:00 committed by GitHub
parent cad76cc1d5
commit 922ce9d236
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 34 additions and 8 deletions

View File

@ -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 = {
@ -573,8 +574,15 @@ 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;
if (isObjectLiteral(input)) {
// Add parentheses to make sure `input` is parsed as an expression
input = `(${StringPrototypeTrim(input)})\n`;
}
if (match) {
lastCommand = `exec(${JSONStringify(match[1])})`;
lastCommand = `exec(${JSONStringify(input)})`;
} else {
lastCommand = input;
}

View File

@ -739,6 +739,22 @@ 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
* 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(startsWithBraceRegExp, code) !== null &&
RegExpPrototypeExec(endsWithSemicolonRegExp, code) === null;
}
module.exports = {
REPL_MODE_SLOPPY: Symbol('repl-sloppy'),
REPL_MODE_STRICT,
@ -746,4 +762,5 @@ module.exports = {
kStandaloneREPL: Symbol('kStandaloneREPL'),
setupPreview,
setupReverseSearch,
isObjectLiteral,
};

View File

@ -172,6 +172,7 @@ const {
kStandaloneREPL,
setupPreview,
setupReverseSearch,
isObjectLiteral,
} = require('internal/repl/utils');
const {
constants: {
@ -436,13 +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 needs 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;
}

View File

@ -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();
}