Skip to content

Commit 1f08864

Browse files
dario-piotrowiczmeixg
authored andcommittedMar 23, 2025
debugger: fix behavior of plain object exec in debugger repl
Co-authored-by: Xuguang Mei <meixuguang@gmail.com> PR-URL: #57498 Fixes: #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>
1 parent e28c723 commit 1f08864

File tree

4 files changed

+34
-8
lines changed

4 files changed

+34
-8
lines changed
 

‎lib/internal/debugger/inspect_repl.js

+9-1
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ const { fileURLToPath } = require('internal/url');
5656

5757
const { customInspectSymbol, SideEffectFreeRegExpPrototypeSymbolReplace } = require('internal/util');
5858
const { inspect: utilInspect } = require('internal/util/inspect');
59+
const { isObjectLiteral } = require('internal/repl/utils');
5960
const debuglog = require('internal/util/debuglog').debuglog('inspect');
6061

6162
const SHORTCUTS = {
@@ -573,8 +574,15 @@ function createRepl(inspector) {
573574
if (input === '\n') return lastCommand;
574575
// Add parentheses: exec process.title => exec("process.title");
575576
const match = RegExpPrototypeExec(/^\s*(?:exec|p)\s+([^\n]*)/, input);
577+
input = match ? match[1] : input;
578+
579+
if (isObjectLiteral(input)) {
580+
// Add parentheses to make sure `input` is parsed as an expression
581+
input = `(${StringPrototypeTrim(input)})\n`;
582+
}
583+
576584
if (match) {
577-
lastCommand = `exec(${JSONStringify(match[1])})`;
585+
lastCommand = `exec(${JSONStringify(input)})`;
578586
} else {
579587
lastCommand = input;
580588
}

‎lib/internal/repl/utils.js

+17
Original file line numberDiff line numberDiff line change
@@ -739,11 +739,28 @@ function setupReverseSearch(repl) {
739739
return { reverseSearch };
740740
}
741741

742+
const startsWithBraceRegExp = /^\s*{/;
743+
const endsWithSemicolonRegExp = /;\s*$/;
744+
745+
/**
746+
* Checks if some provided code represents an object literal.
747+
* This is helpful to prevent confusing repl code evaluations where
748+
* strings such as `{ a : 1 }` would get interpreted as block statements
749+
* rather than object literals.
750+
* @param {string} code the code to check
751+
* @returns {boolean} true if the code represents an object literal, false otherwise
752+
*/
753+
function isObjectLiteral(code) {
754+
return RegExpPrototypeExec(startsWithBraceRegExp, code) !== null &&
755+
RegExpPrototypeExec(endsWithSemicolonRegExp, code) === null;
756+
}
757+
742758
module.exports = {
743759
REPL_MODE_SLOPPY: Symbol('repl-sloppy'),
744760
REPL_MODE_STRICT,
745761
isRecoverableError,
746762
kStandaloneREPL: Symbol('kStandaloneREPL'),
747763
setupPreview,
748764
setupReverseSearch,
765+
isObjectLiteral,
749766
};

‎lib/repl.js

+3-7
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,7 @@ const {
171171
kStandaloneREPL,
172172
setupPreview,
173173
setupReverseSearch,
174+
isObjectLiteral,
174175
} = require('internal/repl/utils');
175176
const {
176177
constants: {
@@ -440,13 +441,8 @@ function REPLServer(prompt,
440441
let awaitPromise = false;
441442
const input = code;
442443

443-
// It's confusing for `{ a : 1 }` to be interpreted as a block
444-
// statement rather than an object literal. So, we first try
445-
// to wrap it in parentheses, so that it will be interpreted as
446-
// an expression. Note that if the above condition changes,
447-
// lib/internal/repl/utils.js needs to be changed to match.
448-
if (RegExpPrototypeExec(/^\s*{/, code) !== null &&
449-
RegExpPrototypeExec(/;\s*$/, code) === null) {
444+
if (isObjectLiteral(code)) {
445+
// Add parentheses to make sure `code` is parsed as an expression
450446
code = `(${StringPrototypeTrim(code)})\n`;
451447
wrappedCmd = true;
452448
}

‎test/parallel/test-debugger-exec.js

+5
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,11 @@ async function waitInitialBreak() {
6060
/\[ 'undefined', 'function' \]/,
6161
'non-paused exec can see global but not module-scope values'
6262
);
63+
64+
// Ref: https://github.com/nodejs/node/issues/46808
65+
await cli.waitForPrompt();
66+
await cli.command('exec { a: 1 }');
67+
assert.match(cli.output, /\{ a: 1 \}/);
6368
} finally {
6469
await cli.quit();
6570
}

0 commit comments

Comments
 (0)
Please sign in to comment.