Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 40 additions & 2 deletions lib/internal/debugger/inspect_repl.js
Original file line number Diff line number Diff line change
Expand Up @@ -380,6 +380,7 @@ function createRepl(inspector) {
let selectedFrame;
let exitDebugRepl;
let contextLineNumber = 2;
let pendingInitialPauseDisplay;

function resetOnStart() {
knownScripts = {};
Expand Down Expand Up @@ -890,11 +891,32 @@ function createRepl(inspector) {
});
}

function shouldWaitForInitialPauseDisplay() {
return inspector.child !== null && inspector.child !== undefined;
}

function waitForInitialPauseDisplay() {
let resolve;
const promise = new Promise((res) => {
resolve = res;
});
pendingInitialPauseDisplay = { promise, resolve };
return pendingInitialPauseDisplay;
}

function resolveInitialPauseDisplay() {
if (pendingInitialPauseDisplay) {
pendingInitialPauseDisplay.resolve();
pendingInitialPauseDisplay = null;
}
}

Debugger.on('paused', ({ callFrames, reason /* , hitBreakpoints */ }) => {
if (process.env.NODE_INSPECT_RESUME_ON_START === '1' &&
reason === 'Break on start') {
debuglog('Paused on start, but NODE_INSPECT_RESUME_ON_START' +
' environment variable is set to 1, resuming');
resolveInitialPauseDisplay();
inspector.client.callMethod('Debugger.resume');
return;
}
Expand All @@ -910,7 +932,7 @@ function createRepl(inspector) {

const header = `${breakType} in ${scriptUrl}:${lineNumber + 1}`;

inspector.suspendReplWhile(() =>
const pauseDisplay = inspector.suspendReplWhile(() =>
PromisePrototypeThen(
SafePromiseAllReturnArrayLike([formatWatchers(true), selectedFrame.list(contextLineNumber)]),
({ 0: watcherList, 1: context }) => {
Expand All @@ -919,6 +941,11 @@ function createRepl(inspector) {
inspect(context);
print(`${header}\n${breakContext}`);
}));
PromisePrototypeThen(
pauseDisplay,
resolveInitialPauseDisplay,
resolveInitialPauseDisplay,
);
});

function handleResumed() {
Expand Down Expand Up @@ -947,7 +974,18 @@ function createRepl(inspector) {

async function runAndInit() {
await inspector.run();
await initAfterStart();
const initialPauseDisplay = shouldWaitForInitialPauseDisplay() ?
waitForInitialPauseDisplay() : null;
try {
await initAfterStart();
if (initialPauseDisplay) {
await initialPauseDisplay.promise;
}
} finally {
if (pendingInitialPauseDisplay === initialPauseDisplay) {
pendingInitialPauseDisplay = null;
}
}
}

function initializeContext(context) {
Expand Down
74 changes: 70 additions & 4 deletions test/parallel/test-debugger-run-restart-init.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ function createAgent(domain, calls, gates) {
agent.setBlackboxPatterns = method('setBlackboxPatterns');
agent.setPauseOnExceptions = method('setPauseOnExceptions');
agent.runIfWaitingForDebugger = method('runIfWaitingForDebugger');
agent.getScriptSource = async () => {
calls.push(`${domain}.getScriptSource`);
return { scriptSource: 'let x = 1;\nx = x + 1;\n' };
};
return agent;
}

Expand All @@ -56,7 +60,30 @@ function evalCommand(repl, command) {
});
}

async function assertCommandWaitsForInit(repl, command, gate, calls) {
function emitInitialPause(inspector) {
inspector.Debugger.emit('paused', {
reason: 'Break on start',
callFrames: [{
callFrameId: 'call-frame-id',
functionName: '',
location: {
scriptId: '1',
lineNumber: 0,
columnNumber: 0,
},
scopeChain: [],
}],
});
}

async function assertCommandWaitsForInitAndPauseDisplay(
repl,
command,
gate,
pauseDisplayGate,
calls,
inspector,
) {
let settled = false;
const promise = evalCommand(repl, command).then(() => {
settled = true;
Expand All @@ -70,6 +97,22 @@ async function assertCommandWaitsForInit(repl, command, gate, calls) {
);

gate.resolve();
await new Promise(setImmediate);
assert.strictEqual(
settled,
false,
`${command} resolved before initial pause was displayed: ${calls}`,
);

emitInitialPause(inspector);
await new Promise(setImmediate);
assert.strictEqual(
settled,
false,
`${command} resolved before pause display completed: ${calls}`,
);

pauseDisplayGate.resolve();
await promise;
assert.strictEqual(settled, true);
}
Expand All @@ -78,17 +121,26 @@ async function assertCommandWaitsForInit(repl, command, gate, calls) {
const calls = [];
const runGate = createGate();
const restartGate = createGate();
const runPauseDisplayGate = createGate();
const restartPauseDisplayGate = createGate();
const gates = [null, runGate, restartGate];
const pauseDisplayGates = [runPauseDisplayGate, restartPauseDisplayGate];
const inspector = {
child: null,
client: new EventEmitter(),
domainNames: ['Debugger', 'HeapProfiler', 'Profiler', 'Runtime'],
stdin: new PassThrough(),
stdout: new PassThrough(),
run: common.mustCall(async () => {
calls.push('inspector.run');
inspector.child = {};
}, 2),
print(text, addNewline = true) {
inspector.stdout.write(addNewline ? `${text}\n` : text);
},
suspendReplWhile(fn) {
return fn();
const pauseDisplayGate = pauseDisplayGates.shift();
return Promise.resolve(fn()).then(() => pauseDisplayGate.promise);
},
};

Expand All @@ -98,8 +150,22 @@ async function assertCommandWaitsForInit(repl, command, gate, calls) {

const repl = await createRepl(inspector)();

await assertCommandWaitsForInit(repl, 'run', runGate, calls);
await assertCommandWaitsForInit(repl, 'restart', restartGate, calls);
await assertCommandWaitsForInitAndPauseDisplay(
repl,
'run',
runGate,
runPauseDisplayGate,
calls,
inspector,
);
await assertCommandWaitsForInitAndPauseDisplay(
repl,
'restart',
restartGate,
restartPauseDisplayGate,
calls,
inspector,
);

assert.deepStrictEqual(
calls.filter((call) => (
Expand Down
Loading