diff --git a/doc/api/errors.md b/doc/api/errors.md
index 7828d5a688e5c1..7b6b406de0fa54 100644
--- a/doc/api/errors.md
+++ b/doc/api/errors.md
@@ -876,6 +876,22 @@ An unknown cipher was specified.
An unknown Diffie-Hellman group name was given. See
[`crypto.getDiffieHellman()`][] for a list of valid group names.
+
+### `ERR_DEBUGGER_ERROR`
+
+
+An error occurred with the [debugger][].
+
+
+### `ERR_DEBUGGER_STARTUP_ERROR`
+
+
+The [debugger][] timed out waiting for the required host/port to be free.
+
### `ERR_DIR_CLOSED`
@@ -2599,6 +2615,7 @@ closed.
[`util.getSystemErrorName(error.errno)`]: util.md#util_util_getsystemerrorname_err
[`zlib`]: zlib.md
[crypto digest algorithm]: crypto.md#crypto_crypto_gethashes
+[debugger]: debugger.md
[define a custom subpath]: packages.md#packages_subpath_exports
[domains]: domain.md
[event emitter-based]: events.md#events_class_eventemitter
diff --git a/lib/internal/debugger/_inspect.js b/lib/internal/debugger/_inspect.js
index f0bd9331d086c7..c8e38512bb8297 100644
--- a/lib/internal/debugger/_inspect.js
+++ b/lib/internal/debugger/_inspect.js
@@ -1,33 +1,45 @@
-/*
- * Copyright Node.js contributors. All rights reserved.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to
- * deal in the Software without restriction, including without limitation the
- * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
- * sell copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
- * IN THE SOFTWARE.
- */
-
-// TODO(trott): enable ESLint
-/* eslint-disable */
-
'use strict';
+
+const {
+ ArrayPrototypeConcat,
+ ArrayPrototypeForEach,
+ ArrayPrototypeJoin,
+ ArrayPrototypeMap,
+ ArrayPrototypePop,
+ ArrayPrototypeShift,
+ ArrayPrototypeSlice,
+ FunctionPrototypeBind,
+ Number,
+ Promise,
+ PromisePrototypeCatch,
+ PromisePrototypeThen,
+ PromiseResolve,
+ Proxy,
+ RegExpPrototypeSymbolMatch,
+ RegExpPrototypeSymbolSplit,
+ RegExpPrototypeTest,
+ StringPrototypeEndsWith,
+ StringPrototypeSplit,
+} = primordials;
+
const { spawn } = require('child_process');
const { EventEmitter } = require('events');
const net = require('net');
const util = require('util');
+const {
+ AbortController,
+} = require('internal/abort_controller');
+
+const pSetTimeout = util.promisify(require('timers').setTimeout);
+async function* pSetInterval(delay) {
+ while (true) {
+ await pSetTimeout(delay);
+ yield;
+ }
+}
+
+// TODO(aduh95): remove console calls
+const console = require('internal/console/global');
const { 0: InspectClient, 1: createRepl } =
[
@@ -37,95 +49,75 @@ const { 0: InspectClient, 1: createRepl } =
const debuglog = util.debuglog('inspect');
-class StartupError extends Error {
- constructor(message) {
- super(message);
- this.name = 'StartupError';
- }
-}
+const { ERR_DEBUGGER_STARTUP_ERROR } = require('internal/errors').codes;
-function portIsFree(host, port, timeout = 9999) {
- if (port === 0) return Promise.resolve(); // Binding to a random port.
+async function portIsFree(host, port, timeout = 9999) {
+ if (port === 0) return; // Binding to a random port.
const retryDelay = 150;
- let didTimeOut = false;
-
- return new Promise((resolve, reject) => {
- setTimeout(() => {
- didTimeOut = true;
- reject(new StartupError(
- `Timeout (${timeout}) waiting for ${host}:${port} to be free`));
- }, timeout);
+ const ac = new AbortController();
+ const { signal } = ac;
- function pingPort() {
- if (didTimeOut) return;
+ pSetTimeout(timeout).then(() => ac.abort());
+ const asyncIterator = pSetInterval(retryDelay);
+ while (true) {
+ await asyncIterator.next();
+ if (signal.aborted) {
+ throw new ERR_DEBUGGER_STARTUP_ERROR(
+ `Timeout (${timeout}) waiting for ${host}:${port} to be free`);
+ }
+ const error = await new Promise((resolve) => {
const socket = net.connect(port, host);
- let didRetry = false;
- function retry() {
- if (!didRetry && !didTimeOut) {
- didRetry = true;
- setTimeout(pingPort, retryDelay);
- }
- }
-
- socket.on('error', (error) => {
- if (error.code === 'ECONNREFUSED') {
- resolve();
- } else {
- retry();
- }
- });
- socket.on('connect', () => {
- socket.destroy();
- retry();
- });
+ socket.on('error', resolve);
+ socket.on('connect', resolve);
+ });
+ if (error?.code === 'ECONNREFUSED') {
+ return;
}
- pingPort();
- });
+ }
}
-function runScript(script, scriptArgs, inspectHost, inspectPort, childPrint) {
- return portIsFree(inspectHost, inspectPort)
- .then(() => {
- return new Promise((resolve) => {
- const needDebugBrk = process.version.match(/^v(6|7)\./);
- const args = (needDebugBrk ?
- ['--inspect', `--debug-brk=${inspectPort}`] :
- [`--inspect-brk=${inspectPort}`])
- .concat([script], scriptArgs);
- const child = spawn(process.execPath, args);
- child.stdout.setEncoding('utf8');
- child.stderr.setEncoding('utf8');
- child.stdout.on('data', (chunk) => childPrint(chunk, 'stdout'));
- child.stderr.on('data', (chunk) => childPrint(chunk, 'stderr'));
-
- let output = '';
- function waitForListenHint(text) {
- output += text;
- if (/Debugger listening on ws:\/\/\[?(.+?)\]?:(\d+)\//.test(output)) {
- const host = RegExp.$1;
- const port = Number.parseInt(RegExp.$2);
- child.stderr.removeListener('data', waitForListenHint);
- resolve([child, port, host]);
- }
- }
-
- child.stderr.on('data', waitForListenHint);
- });
- });
+const debugRegex = /Debugger listening on ws:\/\/\[?(.+?)\]?:(\d+)\//;
+async function runScript(script, scriptArgs, inspectHost, inspectPort,
+ childPrint) {
+ await portIsFree(inspectHost, inspectPort);
+ const args = ArrayPrototypeConcat(
+ [`--inspect-brk=${inspectPort}`, script],
+ scriptArgs);
+ const child = spawn(process.execPath, args);
+ child.stdout.setEncoding('utf8');
+ child.stderr.setEncoding('utf8');
+ child.stdout.on('data', (chunk) => childPrint(chunk, 'stdout'));
+ child.stderr.on('data', (chunk) => childPrint(chunk, 'stderr'));
+
+ let output = '';
+ return new Promise((resolve) => {
+ function waitForListenHint(text) {
+ output += text;
+ const debug = RegExpPrototypeSymbolMatch(debugRegex, output);
+ if (debug) {
+ const host = debug[1];
+ const port = Number(debug[2]);
+ child.stderr.removeListener('data', waitForListenHint);
+ resolve([child, port, host]);
+ }
+ }
+
+ child.stderr.on('data', waitForListenHint);
+ });
}
function createAgentProxy(domain, client) {
const agent = new EventEmitter();
- agent.then = (...args) => {
+ agent.then = (then, _catch) => {
// TODO: potentially fetch the protocol and pretty-print it here.
const descriptor = {
[util.inspect.custom](depth, { stylize }) {
return stylize(`[Agent ${domain}]`, 'special');
},
};
- return Promise.resolve(descriptor).then(...args);
+ return PromisePrototypeThen(PromiseResolve(descriptor), then, _catch);
};
return new Proxy(agent, {
@@ -148,25 +140,26 @@ class NodeInspector {
this.child = null;
if (options.script) {
- this._runScript = runScript.bind(null,
- options.script,
- options.scriptArgs,
- options.host,
- options.port,
- this.childPrint.bind(this));
+ this._runScript = FunctionPrototypeBind(
+ runScript, null,
+ options.script,
+ options.scriptArgs,
+ options.host,
+ options.port,
+ FunctionPrototypeBind(this.childPrint, this));
} else {
this._runScript =
- () => Promise.resolve([null, options.port, options.host]);
+ () => PromiseResolve([null, options.port, options.host]);
}
this.client = new InspectClient();
this.domainNames = ['Debugger', 'HeapProfiler', 'Profiler', 'Runtime'];
- this.domainNames.forEach((domain) => {
+ ArrayPrototypeForEach(this.domainNames, (domain) => {
this[domain] = createAgentProxy(domain, this.client);
});
this.handleDebugEvent = (fullName, params) => {
- const { 0: domain, 1: name } = fullName.split('.');
+ const { 0: domain, 1: name } = StringPrototypeSplit(fullName, '.');
if (domain in this) {
this[domain].emit(name, params);
}
@@ -176,19 +169,16 @@ class NodeInspector {
// Handle all possible exits
process.on('exit', () => this.killChild());
- process.once('SIGTERM', process.exit.bind(process, 0));
- process.once('SIGHUP', process.exit.bind(process, 0));
-
- this.run()
- .then(() => startRepl())
- .then((repl) => {
- this.repl = repl;
- this.repl.on('exit', () => {
- process.exit(0);
- });
- this.paused = false;
- })
- .then(null, (error) => process.nextTick(() => { throw error; }));
+ const exitCodeZero = () => process.exit(0);
+ process.once('SIGTERM', exitCodeZero);
+ process.once('SIGHUP', exitCodeZero);
+
+ PromisePrototypeCatch(PromisePrototypeThen(this.run(), async () => {
+ const repl = await startRepl();
+ this.repl = repl;
+ this.repl.on('exit', exitCodeZero);
+ this.paused = false;
+ }), (error) => process.nextTick(() => { throw error; }));
}
suspendReplWhile(fn) {
@@ -197,16 +187,16 @@ class NodeInspector {
}
this.stdin.pause();
this.paused = true;
- return new Promise((resolve) => {
+ return PromisePrototypeCatch(PromisePrototypeThen(new Promise((resolve) => {
resolve(fn());
- }).then(() => {
+ }), () => {
this.paused = false;
if (this.repl) {
this.repl.resume();
this.repl.displayPrompt();
}
this.stdin.resume();
- }).then(null, (error) => process.nextTick(() => { throw error; }));
+ }), (error) => process.nextTick(() => { throw error; }));
}
killChild() {
@@ -217,37 +207,28 @@ class NodeInspector {
}
}
- run() {
+ async run() {
this.killChild();
- return this._runScript().then(({ 0: child, 1: port, 2: host }) => {
- this.child = child;
-
- let connectionAttempts = 0;
- const attemptConnect = () => {
- ++connectionAttempts;
- debuglog('connection attempt #%d', connectionAttempts);
- this.stdout.write('.');
- return this.client.connect(port, host)
- .then(() => {
- debuglog('connection established');
- this.stdout.write(' ok\n');
- }, (error) => {
- debuglog('connect failed', error);
- // If it's failed to connect 5 times then print failed message
- if (connectionAttempts >= 5) {
- this.stdout.write(' failed to connect, please retry\n');
- process.exit(1);
- }
-
- return new Promise((resolve) => setTimeout(resolve, 1000))
- .then(attemptConnect);
- });
- };
-
- this.print(`connecting to ${host}:${port} ..`, false);
- return attemptConnect();
- });
+ const { 0: child, 1: port, 2: host } = await this._runScript();
+ this.child = child;
+
+ this.print(`connecting to ${host}:${port} ..`, false);
+ for (let attempt = 0; attempt < 5; attempt++) {
+ debuglog('connection attempt #%d', attempt);
+ this.stdout.write('.');
+ try {
+ await this.client.connect(port, host);
+ debuglog('connection established');
+ this.stdout.write(' ok\n');
+ return;
+ } catch (error) {
+ debuglog('connect failed', error);
+ await pSetTimeout(1000);
+ }
+ }
+ this.stdout.write(' failed to connect, please retry\n');
+ process.exit(1);
}
clearLine() {
@@ -266,16 +247,19 @@ class NodeInspector {
#stdioBuffers = { stdout: '', stderr: '' };
childPrint(text, which) {
- const lines = (this.#stdioBuffers[which] + text)
- .split(/\r\n|\r|\n/g);
+ const lines = RegExpPrototypeSymbolSplit(
+ /\r\n|\r|\n/g,
+ this.#stdioBuffers[which] + text);
this.#stdioBuffers[which] = '';
if (lines[lines.length - 1] !== '') {
- this.#stdioBuffers[which] = lines.pop();
+ this.#stdioBuffers[which] = ArrayPrototypePop(lines);
}
- const textToPrint = lines.map((chunk) => `< ${chunk}`).join('\n');
+ const textToPrint = ArrayPrototypeJoin(
+ ArrayPrototypeMap(lines, (chunk) => `< ${chunk}`),
+ '\n');
if (lines.length) {
this.print(textToPrint, true);
@@ -284,36 +268,41 @@ class NodeInspector {
}
}
- if (textToPrint.endsWith('Waiting for the debugger to disconnect...\n')) {
+ if (StringPrototypeEndsWith(
+ textToPrint,
+ 'Waiting for the debugger to disconnect...\n'
+ )) {
this.killChild();
}
}
}
-function parseArgv([target, ...args]) {
+function parseArgv(args) {
+ const target = ArrayPrototypeShift(args);
let host = '127.0.0.1';
let port = 9229;
let isRemote = false;
let script = target;
let scriptArgs = args;
- const hostMatch = target.match(/^([^:]+):(\d+)$/);
- const portMatch = target.match(/^--port=(\d+)$/);
+ const hostMatch = RegExpPrototypeSymbolMatch(/^([^:]+):(\d+)$/, target);
+ const portMatch = RegExpPrototypeSymbolMatch(/^--port=(\d+)$/, target);
if (hostMatch) {
// Connecting to remote debugger
host = hostMatch[1];
- port = parseInt(hostMatch[2], 10);
+ port = Number(hostMatch[2]);
isRemote = true;
script = null;
} else if (portMatch) {
// Start on custom port
- port = parseInt(portMatch[1], 10);
+ port = Number(portMatch[1]);
script = args[0];
- scriptArgs = args.slice(1);
- } else if (args.length === 1 && /^\d+$/.test(args[0]) && target === '-p') {
+ scriptArgs = ArrayPrototypeSlice(args, 1);
+ } else if (args.length === 1 && RegExpPrototypeTest(/^\d+$/, args[0]) &&
+ target === '-p') {
// Start debugger against a given pid
- const pid = parseInt(args[0], 10);
+ const pid = Number(args[0]);
try {
process._debugProcess(pid);
} catch (e) {
@@ -332,7 +321,7 @@ function parseArgv([target, ...args]) {
};
}
-function startInspect(argv = process.argv.slice(2),
+function startInspect(argv = ArrayPrototypeSlice(process.argv, 2),
stdin = process.stdin,
stdout = process.stdout) {
if (argv.length < 1) {
@@ -340,6 +329,7 @@ function startInspect(argv = process.argv.slice(2),
console.error(`Usage: ${invokedAs} script.js`);
console.error(` ${invokedAs} :`);
+ console.error(` ${invokedAs} --port=`);
console.error(` ${invokedAs} -p `);
process.exit(1);
}
@@ -350,7 +340,7 @@ function startInspect(argv = process.argv.slice(2),
stdin.resume();
function handleUnexpectedError(e) {
- if (!(e instanceof StartupError)) {
+ if (e.code !== 'ERR_DEBUGGER_STARTUP_ERROR') {
console.error('There was an internal error in Node.js. ' +
'Please report this bug.');
console.error(e.message);
diff --git a/lib/internal/debugger/inspect_client.js b/lib/internal/debugger/inspect_client.js
index 831a7fda1cdff1..8fc9011ca888ed 100644
--- a/lib/internal/debugger/inspect_client.js
+++ b/lib/internal/debugger/inspect_client.js
@@ -1,36 +1,22 @@
-/*
- * Copyright Node.js contributors. All rights reserved.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to
- * deal in the Software without restriction, including without limitation the
- * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
- * sell copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
- * IN THE SOFTWARE.
- */
-
-// TODO(trott): enable ESLint
-/* eslint-disable */
-
'use strict';
+
+const {
+ ArrayPrototypePush,
+ ErrorCaptureStackTrace,
+ FunctionPrototypeBind,
+ JSONParse,
+ JSONStringify,
+ ObjectKeys,
+ Promise,
+} = primordials;
+
const Buffer = require('buffer').Buffer;
+const { ERR_DEBUGGER_ERROR } = require('internal/errors').codes;
const { EventEmitter } = require('events');
const http = require('http');
const URL = require('url');
-const util = require('util');
-const debuglog = util.debuglog('inspect');
+const debuglog = require('internal/util/debuglog').debuglog('inspect');
const kOpCodeText = 0x1;
const kOpCodeClose = 0x8;
@@ -49,14 +35,10 @@ const kTwoBytePayloadLengthField = 126;
const kEightBytePayloadLengthField = 127;
const kMaskingKeyWidthInBytes = 4;
-function isEmpty(obj) {
- return Object.keys(obj).length === 0;
-}
-
function unpackError({ code, message, data }) {
- const err = new Error(`${message} - ${data}`);
+ const err = new ERR_DEBUGGER_ERROR(`${message} - ${data}`);
err.code = code;
- Error.captureStackTrace(err, unpackError);
+ ErrorCaptureStackTrace(err, unpackError);
return err;
}
@@ -116,14 +98,14 @@ function decodeFrameHybi17(data) {
const masked = (secondByte & kMaskBit) !== 0;
const compressed = reserved1;
if (compressed) {
- throw new Error('Compressed frames not supported');
+ throw new ERR_DEBUGGER_ERROR('Compressed frames not supported');
}
if (!final || reserved2 || reserved3) {
- throw new Error('Only compression extension is supported');
+ throw new ERR_DEBUGGER_ERROR('Only compression extension is supported');
}
if (masked) {
- throw new Error('Masked server frame - not supported');
+ throw new ERR_DEBUGGER_ERROR('Masked server frame - not supported');
}
let closed = false;
@@ -134,7 +116,7 @@ function decodeFrameHybi17(data) {
case kOpCodeText:
break;
default:
- throw new Error(`Unsupported op code ${opCode}`);
+ throw new ERR_DEBUGGER_ERROR(`Unsupported op code ${opCode}`);
}
let payloadLength = secondByte & kPayloadLengthMask;
@@ -169,7 +151,7 @@ function decodeFrameHybi17(data) {
class Client extends EventEmitter {
constructor() {
super();
- this.handleChunk = this._handleChunk.bind(this);
+ this.handleChunk = FunctionPrototypeBind(this._handleChunk, this);
this._port = undefined;
this._host = undefined;
@@ -198,11 +180,13 @@ class Client extends EventEmitter {
debuglog('< %s', payloadStr);
const lastChar = payloadStr[payloadStr.length - 1];
if (payloadStr[0] !== '{' || lastChar !== '}') {
- throw new Error(`Payload does not look like JSON: ${payloadStr}`);
+ throw new ERR_DEBUGGER_ERROR(
+ `Payload does not look like JSON: ${payloadStr}`
+ );
}
let payload;
try {
- payload = JSON.parse(payloadStr);
+ payload = JSONParse(payloadStr);
} catch (parseError) {
parseError.string = payloadStr;
throw parseError;
@@ -219,7 +203,7 @@ class Client extends EventEmitter {
this.emit('debugEvent', method, params);
this.emit(method, params);
} else {
- throw new Error(`Unsupported response: ${payloadStr}`);
+ throw new ERR_DEBUGGER_ERROR(`Unsupported response: ${payloadStr}`);
}
}
}
@@ -241,15 +225,15 @@ class Client extends EventEmitter {
callMethod(method, params) {
return new Promise((resolve, reject) => {
if (!this._socket) {
- reject(new Error('Use `run` to start the app again.'));
+ reject(new ERR_DEBUGGER_ERROR('Use `run` to start the app again.'));
return;
}
const data = { id: ++this._lastId, method, params };
this._pending[data.id] = (error, result) => {
if (error) reject(unpackError(error));
- else resolve(isEmpty(result) ? undefined : result);
+ else resolve(ObjectKeys(result).length ? result : undefined);
};
- const json = JSON.stringify(data);
+ const json = JSONStringify(data);
debuglog('> %s', json);
this._socket.write(encodeFrameHybi17(Buffer.from(json)));
});
@@ -269,19 +253,22 @@ class Client extends EventEmitter {
function parseChunks() {
const resBody = Buffer.concat(chunks).toString();
if (httpRes.statusCode !== 200) {
- reject(new Error(`Unexpected ${httpRes.statusCode}: ${resBody}`));
+ reject(new ERR_DEBUGGER_ERROR(
+ `Unexpected ${httpRes.statusCode}: ${resBody}`
+ ));
return;
}
try {
- resolve(JSON.parse(resBody));
- } catch (parseError) {
- reject(new Error(`Response didn't contain JSON: ${resBody}`));
-
+ resolve(JSONParse(resBody));
+ } catch {
+ reject(new ERR_DEBUGGER_ERROR(
+ `Response didn't contain JSON: ${resBody}`
+ ));
}
}
httpRes.on('error', reject);
- httpRes.on('data', (chunk) => chunks.push(chunk));
+ httpRes.on('data', (chunk) => ArrayPrototypePush(chunks, chunk));
httpRes.on('end', parseChunks);
}
@@ -290,17 +277,16 @@ class Client extends EventEmitter {
});
}
- connect(port, host) {
+ async connect(port, host) {
this._port = port;
this._host = host;
- return this._discoverWebsocketPath()
- .then((urlPath) => this._connectWebsocket(urlPath));
+ const urlPath = await this._discoverWebsocketPath();
+ return this._connectWebsocket(urlPath);
}
- _discoverWebsocketPath() {
- return this._fetchJSON('/json')
- .then(({ 0: { webSocketDebuggerUrl } }) =>
- URL.parse(webSocketDebuggerUrl).path);
+ async _discoverWebsocketPath() {
+ const { 0: { webSocketDebuggerUrl } } = await this._fetchJSON('/json');
+ return URL.parse(webSocketDebuggerUrl).path;
}
_connectWebsocket(urlPath) {
diff --git a/lib/internal/debugger/inspect_repl.js b/lib/internal/debugger/inspect_repl.js
index 128ae697105a3d..5393cb32718300 100644
--- a/lib/internal/debugger/inspect_repl.js
+++ b/lib/internal/debugger/inspect_repl.js
@@ -1,37 +1,66 @@
-/*
- * Copyright Node.js contributors. All rights reserved.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to
- * deal in the Software without restriction, including without limitation the
- * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
- * sell copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
- * IN THE SOFTWARE.
- */
-
// TODO(trott): enable ESLint
-/* eslint-disable */
+/* eslint-disable getter-return */
'use strict';
+
+const {
+ Array,
+ ArrayFrom,
+ ArrayPrototypeFilter,
+ ArrayPrototypeFind,
+ ArrayPrototypeForEach,
+ ArrayPrototypeIncludes,
+ ArrayPrototypeIndexOf,
+ ArrayPrototypeJoin,
+ ArrayPrototypeMap,
+ ArrayPrototypePush,
+ ArrayPrototypeSlice,
+ ArrayPrototypeSome,
+ ArrayPrototypeSplice,
+ Date,
+ FunctionPrototypeCall,
+ JSONStringify,
+ MathMax,
+ ObjectAssign,
+ ObjectDefineProperty,
+ ObjectKeys,
+ ObjectValues,
+ Promise,
+ PromiseAll,
+ PromisePrototypeCatch,
+ PromisePrototypeThen,
+ PromiseResolve,
+ ReflectGetOwnPropertyDescriptor,
+ ReflectOwnKeys,
+ RegExpPrototypeSymbolMatch,
+ RegExpPrototypeSymbolReplace,
+ SafeArrayIterator,
+ SafeMap,
+ String,
+ StringFromCharCode,
+ StringPrototypeEndsWith,
+ StringPrototypeIncludes,
+ StringPrototypeRepeat,
+ StringPrototypeSlice,
+ StringPrototypeSplit,
+ StringPrototypeStartsWith,
+ StringPrototypeToUpperCase,
+ StringPrototypeTrim,
+} = primordials;
+
+const { ERR_DEBUGGER_ERROR } = require('internal/errors').codes;
+
+const { validateString } = require('internal/validators');
+
const FS = require('fs');
const Path = require('path');
const Repl = require('repl');
-const util = require('util');
const vm = require('vm');
-const fileURLToPath = require('url').fileURLToPath;
+const { fileURLToPath } = require('internal/url');
-const debuglog = util.debuglog('inspect');
+const { customInspectSymbol } = require('internal/util');
+const { inspect: utilInspect } = require('internal/util/inspect');
+const debuglog = require('internal/util/debuglog').debuglog('inspect');
const SHORTCUTS = {
cont: 'c',
@@ -44,7 +73,7 @@ const SHORTCUTS = {
run: 'r',
};
-const HELP = `
+const HELP = StringPrototypeTrim(`
run, restart, r Run the application or reconnect
kill Kill a running application or disconnect
@@ -81,100 +110,95 @@ profiles[n].save(filepath = 'node.cpuprofile')
takeHeapSnapshot(filepath = 'node.heapsnapshot')
Take a heap snapshot and save to disk as JSON.
-`.trim();
+`);
const FUNCTION_NAME_PATTERN = /^(?:function\*? )?([^(\s]+)\(/;
function extractFunctionName(description) {
- const fnNameMatch = description.match(FUNCTION_NAME_PATTERN);
+ const fnNameMatch =
+ RegExpPrototypeSymbolMatch(FUNCTION_NAME_PATTERN, description);
return fnNameMatch ? `: ${fnNameMatch[1]}` : '';
}
-const PUBLIC_BUILTINS = require('module').builtinModules;
-const NATIVES = PUBLIC_BUILTINS ? internalBinding('natives') : {};
+const {
+ moduleIds: PUBLIC_BUILTINS,
+} = internalBinding('native_module');
+const NATIVES = internalBinding('natives');
function isNativeUrl(url) {
- url = url.replace(/\.js$/, '');
- if (PUBLIC_BUILTINS) {
- if (url.startsWith('node:internal/') || PUBLIC_BUILTINS.includes(url))
- return true;
- }
+ url = RegExpPrototypeSymbolReplace(/\.js$/, url, '');
- return url in NATIVES || url === 'bootstrap_node';
+ return StringPrototypeStartsWith(url, 'node:internal/') ||
+ ArrayPrototypeIncludes(PUBLIC_BUILTINS, url) ||
+ url in NATIVES || url === 'bootstrap_node';
}
function getRelativePath(filenameOrURL) {
- const dir = Path.join(Path.resolve(), 'x').slice(0, -1);
+ const dir = StringPrototypeSlice(Path.join(Path.resolve(), 'x'), 0, -1);
- const filename = filenameOrURL.startsWith('file://') ?
+ const filename = StringPrototypeStartsWith(filenameOrURL, 'file://') ?
fileURLToPath(filenameOrURL) : filenameOrURL;
// Change path to relative, if possible
- if (filename.indexOf(dir) === 0) {
- return filename.slice(dir.length);
+ if (StringPrototypeStartsWith(filename, dir)) {
+ return StringPrototypeSlice(filename, dir.length);
}
return filename;
}
-function toCallback(promise, callback) {
- function forward(...args) {
- process.nextTick(() => callback(...args));
- }
- promise.then(forward.bind(null, null), forward);
-}
-
// Adds spaces and prefix to number
// maxN is a maximum number we should have space for
function leftPad(n, prefix, maxN) {
const s = n.toString();
- const nchars = Math.max(2, String(maxN).length) + 1;
- const nspaces = nchars - s.length - 1;
+ const nchars = MathMax(2, String(maxN).length);
+ const nspaces = nchars - s.length;
- return prefix + ' '.repeat(nspaces) + s;
+ return prefix + StringPrototypeRepeat(' ', nspaces) + s;
}
function markSourceColumn(sourceText, position, useColors) {
if (!sourceText) return '';
- const head = sourceText.slice(0, position);
- let tail = sourceText.slice(position);
+ const head = StringPrototypeSlice(sourceText, 0, position);
+ let tail = StringPrototypeSlice(sourceText, position);
// Colourize char if stdout supports colours
if (useColors) {
- tail = tail.replace(/(.+?)([^\w]|$)/, '\u001b[32m$1\u001b[39m$2');
+ tail = RegExpPrototypeSymbolReplace(/(.+?)([^\w]|$)/, tail,
+ '\u001b[32m$1\u001b[39m$2');
}
// Return source line with coloured char at `position`
- return [head, tail].join('');
+ return head + tail;
}
function extractErrorMessage(stack) {
if (!stack) return '';
- const m = stack.match(/^\w+: ([^\n]+)/);
- return m ? m[1] : stack;
+ const m = RegExpPrototypeSymbolMatch(/^\w+: ([^\n]+)/, stack);
+ return m?.[1] ?? stack;
}
function convertResultToError(result) {
const { className, description } = result;
- const err = new Error(extractErrorMessage(description));
+ const err = new ERR_DEBUGGER_ERROR(extractErrorMessage(description));
err.stack = description;
- Object.defineProperty(err, 'name', { value: className });
+ ObjectDefineProperty(err, 'name', { value: className });
return err;
}
class RemoteObject {
constructor(attributes) {
- Object.assign(this, attributes);
+ ObjectAssign(this, attributes);
if (this.type === 'number') {
this.value =
this.unserializableValue ? +this.unserializableValue : +this.value;
}
}
- [util.inspect.custom](depth, opts) {
+ [customInspectSymbol](depth, opts) {
function formatProperty(prop) {
switch (prop.type) {
case 'string':
case 'undefined':
- return util.inspect(prop.value, opts);
+ return utilInspect(prop.value, opts);
case 'number':
case 'boolean':
@@ -183,7 +207,7 @@ class RemoteObject {
case 'object':
case 'symbol':
if (prop.subtype === 'date') {
- return util.inspect(new Date(prop.value), opts);
+ return utilInspect(new Date(prop.value), opts);
}
if (prop.subtype === 'array') {
return opts.stylize(prop.value, 'special');
@@ -199,7 +223,7 @@ class RemoteObject {
case 'number':
case 'string':
case 'undefined':
- return util.inspect(this.value, opts);
+ return utilInspect(this.value, opts);
case 'symbol':
return opts.stylize(this.description, 'special');
@@ -213,10 +237,10 @@ class RemoteObject {
case 'object':
switch (this.subtype) {
case 'date':
- return util.inspect(new Date(this.description), opts);
+ return utilInspect(new Date(this.description), opts);
case 'null':
- return util.inspect(null, opts);
+ return utilInspect(null, opts);
case 'regexp':
return opts.stylize(this.description, 'regexp');
@@ -225,18 +249,21 @@ class RemoteObject {
break;
}
if (this.preview) {
- const props = this.preview.properties
- .map((prop, idx) => {
+ const props = ArrayPrototypeMap(
+ this.preview.properties,
+ (prop, idx) => {
const value = formatProperty(prop);
if (prop.name === `${idx}`) return value;
return `${prop.name}: ${value}`;
});
if (this.preview.overflow) {
- props.push('...');
+ ArrayPrototypePush(props, '...');
}
- const singleLine = props.join(', ');
+ const singleLine = ArrayPrototypeJoin(props, ', ');
const propString =
- singleLine.length > 60 ? props.join(',\n ') : singleLine;
+ singleLine.length > 60 ?
+ ArrayPrototypeJoin(props, ',\n ') :
+ singleLine;
return this.subtype === 'array' ?
`[ ${propString} ]` : `{ ${propString} }`;
@@ -256,34 +283,39 @@ class RemoteObject {
class ScopeSnapshot {
constructor(scope, properties) {
- Object.assign(this, scope);
- this.properties = new Map(properties.map((prop) => {
+ ObjectAssign(this, scope);
+ this.properties = new SafeMap();
+ this.completionGroup = ArrayPrototypeMap(properties, (prop) => {
const value = new RemoteObject(prop.value);
- return [prop.name, value];
- }));
- this.completionGroup = properties.map((prop) => prop.name);
+ this.properties.set(prop.name, value);
+ return prop.name;
+ });
}
- [util.inspect.custom](depth, opts) {
- const type = `${this.type[0].toUpperCase()}${this.type.slice(1)}`;
+ [customInspectSymbol](depth, opts) {
+ const type = StringPrototypeToUpperCase(this.type[0]) +
+ StringPrototypeSlice(this.type, 1);
const name = this.name ? `<${this.name}>` : '';
const prefix = `${type}${name} `;
- return util.inspect(this.properties, opts)
- .replace(/^Map /, prefix);
+ return RegExpPrototypeSymbolReplace(/^Map /,
+ utilInspect(this.properties, opts),
+ prefix);
}
}
function copyOwnProperties(target, source) {
- Object.getOwnPropertyNames(source).forEach((prop) => {
- const descriptor = Object.getOwnPropertyDescriptor(source, prop);
- Object.defineProperty(target, prop, descriptor);
- });
+ ArrayPrototypeForEach(
+ ReflectOwnKeys(source),
+ (prop) => {
+ const desc = ReflectGetOwnPropertyDescriptor(source, prop);
+ ObjectDefineProperty(target, prop, desc);
+ });
}
function aliasProperties(target, mapping) {
- Object.keys(mapping).forEach((key) => {
- const descriptor = Object.getOwnPropertyDescriptor(target, key);
- Object.defineProperty(target, mapping[key], descriptor);
+ ArrayPrototypeForEach(ObjectKeys(mapping), (key) => {
+ const desc = ReflectGetOwnPropertyDescriptor(target, key);
+ ObjectDefineProperty(target, mapping[key], desc);
});
}
@@ -317,7 +349,7 @@ function createRepl(inspector) {
const INSPECT_OPTIONS = { colors: inspector.stdout.isTTY };
function inspect(value) {
- return util.inspect(value, INSPECT_OPTIONS);
+ return utilInspect(value, INSPECT_OPTIONS);
}
function print(value, addNewline = true) {
@@ -327,7 +359,7 @@ function createRepl(inspector) {
function getCurrentLocation() {
if (!selectedFrame) {
- throw new Error('Requires execution to be paused');
+ throw new ERR_DEBUGGER_ERROR('Requires execution to be paused');
}
return selectedFrame.location;
}
@@ -342,22 +374,20 @@ function createRepl(inspector) {
return !script.isNative || isCurrentScript(script);
}
- return Object.keys(knownScripts)
- .map((scriptId) => knownScripts[scriptId])
- .filter(isVisible)
- .map((script) => {
+ return ArrayPrototypeJoin(ArrayPrototypeMap(
+ ArrayPrototypeFilter(ObjectValues(knownScripts), isVisible),
+ (script) => {
const isCurrent = isCurrentScript(script);
const { isNative, url } = script;
const name = `${getRelativePath(url)}${isNative ? ' ' : ''}`;
return `${isCurrent ? '*' : ' '} ${script.scriptId}: ${name}`;
- })
- .join('\n');
+ }), '\n');
}
function listScripts(displayNatives = false) {
print(formatScripts(displayNatives));
}
- listScripts[util.inspect.custom] = function listWithoutInternal() {
+ listScripts[customInspectSymbol] = function listWithoutInternal() {
return formatScripts();
};
@@ -369,18 +399,19 @@ function createRepl(inspector) {
static createAndRegister({ profile }) {
const p = new Profile(profile);
- profiles.push(p);
+ ArrayPrototypePush(profiles, p);
return p;
}
- [util.inspect.custom](depth, { stylize }) {
+ [customInspectSymbol](depth, { stylize }) {
const { startTime, endTime } = this.data;
- return stylize(`[Profile ${endTime - startTime}μs]`, 'special');
+ const MU = StringFromCharCode(956);
+ return stylize(`[Profile ${endTime - startTime}${MU}s]`, 'special');
}
save(filename = 'node.cpuprofile') {
const absoluteFile = Path.resolve(filename);
- const json = JSON.stringify(this.data);
+ const json = JSONStringify(this.data);
FS.writeFileSync(absoluteFile, json);
print('Saved profile to ' + absoluteFile);
}
@@ -388,68 +419,75 @@ function createRepl(inspector) {
class SourceSnippet {
constructor(location, delta, scriptSource) {
- Object.assign(this, location);
+ ObjectAssign(this, location);
this.scriptSource = scriptSource;
this.delta = delta;
}
- [util.inspect.custom](depth, options) {
+ [customInspectSymbol](depth, options) {
const { scriptId, lineNumber, columnNumber, delta, scriptSource } = this;
- const start = Math.max(1, lineNumber - delta + 1);
+ const start = MathMax(1, lineNumber - delta + 1);
const end = lineNumber + delta + 1;
- const lines = scriptSource.split('\n');
- return lines.slice(start - 1, end).map((lineText, offset) => {
- const i = start + offset;
- const isCurrent = i === (lineNumber + 1);
-
- const markedLine = isCurrent ?
- markSourceColumn(lineText, columnNumber, options.colors) :
- lineText;
-
- let isBreakpoint = false;
- knownBreakpoints.forEach(({ location }) => {
- if (!location) return;
- if (scriptId === location.scriptId &&
+ const lines = StringPrototypeSplit(scriptSource, '\n');
+ return ArrayPrototypeJoin(
+ ArrayPrototypeMap(
+ ArrayPrototypeSlice(lines, start - 1, end),
+ (lineText, offset) => {
+ const i = start + offset;
+ const isCurrent = i === (lineNumber + 1);
+
+ const markedLine = isCurrent ?
+ markSourceColumn(lineText, columnNumber, options.colors) :
+ lineText;
+
+ let isBreakpoint = false;
+ ArrayPrototypeForEach(knownBreakpoints, ({ location }) => {
+ if (!location) return;
+ if (scriptId === location.scriptId &&
i === (location.lineNumber + 1)) {
- isBreakpoint = true;
- }
- });
+ isBreakpoint = true;
+ }
+ });
- let prefixChar = ' ';
- if (isCurrent) {
- prefixChar = '>';
- } else if (isBreakpoint) {
- prefixChar = '*';
- }
- return `${leftPad(i, prefixChar, end)} ${markedLine}`;
- }).join('\n');
+ let prefixChar = ' ';
+ if (isCurrent) {
+ prefixChar = '>';
+ } else if (isBreakpoint) {
+ prefixChar = '*';
+ }
+ return `${leftPad(i, prefixChar, end)} ${markedLine}`;
+ }), '\n');
}
}
- function getSourceSnippet(location, delta = 5) {
+ async function getSourceSnippet(location, delta = 5) {
const { scriptId } = location;
- return Debugger.getScriptSource({ scriptId })
- .then(({ scriptSource }) =>
- new SourceSnippet(location, delta, scriptSource));
+ const { scriptSource } = await Debugger.getScriptSource({ scriptId });
+ return new SourceSnippet(location, delta, scriptSource);
}
class CallFrame {
constructor(callFrame) {
- Object.assign(this, callFrame);
+ ObjectAssign(this, callFrame);
}
loadScopes() {
- return Promise.all(
- this.scopeChain
- .filter((scope) => scope.type !== 'global')
- .map((scope) => {
+ return PromiseAll(
+ new SafeArrayIterator(ArrayPrototypeMap(
+ ArrayPrototypeFilter(
+ this.scopeChain,
+ (scope) => scope.type !== 'global'
+ ),
+ async (scope) => {
const { objectId } = scope.object;
- return Runtime.getProperties({
+ const { result } = await Runtime.getProperties({
objectId,
generatePreview: true,
- }).then(({ result }) => new ScopeSnapshot(scope, result));
+ });
+ return new ScopeSnapshot(scope, result);
})
+ )
);
}
@@ -459,70 +497,73 @@ function createRepl(inspector) {
}
class Backtrace extends Array {
- [util.inspect.custom]() {
- return this.map((callFrame, idx) => {
- const {
- location: { scriptId, lineNumber, columnNumber },
- functionName
- } = callFrame;
- const name = functionName || '(anonymous)';
-
- const script = knownScripts[scriptId];
- const relativeUrl =
+ [customInspectSymbol]() {
+ return ArrayPrototypeJoin(
+ ArrayPrototypeMap(this, (callFrame, idx) => {
+ const {
+ location: { scriptId, lineNumber, columnNumber },
+ functionName
+ } = callFrame;
+ const name = functionName || '(anonymous)';
+
+ const script = knownScripts[scriptId];
+ const relativeUrl =
(script && getRelativePath(script.url)) || '';
- const frameLocation =
+ const frameLocation =
`${relativeUrl}:${lineNumber + 1}:${columnNumber}`;
- return `#${idx} ${name} ${frameLocation}`;
- }).join('\n');
+ return `#${idx} ${name} ${frameLocation}`;
+ }), '\n');
}
static from(callFrames) {
- return super.from(Array.from(callFrames).map((callFrame) => {
- if (callFrame instanceof CallFrame) {
- return callFrame;
- }
- return new CallFrame(callFrame);
- }));
+ return FunctionPrototypeCall(
+ ArrayFrom,
+ this,
+ callFrames,
+ (callFrame) =>
+ (callFrame instanceof CallFrame ?
+ callFrame :
+ new CallFrame(callFrame))
+ );
}
}
function prepareControlCode(input) {
if (input === '\n') return lastCommand;
// Add parentheses: exec process.title => exec("process.title");
- const match = input.match(/^\s*exec\s+([^\n]*)/);
+ const match = RegExpPrototypeSymbolMatch(/^\s*exec\s+([^\n]*)/, input);
if (match) {
- lastCommand = `exec(${JSON.stringify(match[1])})`;
+ lastCommand = `exec(${JSONStringify(match[1])})`;
} else {
lastCommand = input;
}
return lastCommand;
}
- function evalInCurrentContext(code) {
+ async function evalInCurrentContext(code) {
// Repl asked for scope variables
if (code === '.scope') {
if (!selectedFrame) {
- return Promise.reject(new Error('Requires execution to be paused'));
+ throw new ERR_DEBUGGER_ERROR('Requires execution to be paused');
}
- return selectedFrame.loadScopes().then((scopes) => {
- return scopes.map((scope) => scope.completionGroup);
- });
+ const scopes = await selectedFrame.loadScopes();
+ return ArrayPrototypeMap(scopes, (scope) => scope.completionGroup);
}
if (selectedFrame) {
- return Debugger.evaluateOnCallFrame({
+ return PromisePrototypeThen(Debugger.evaluateOnCallFrame({
callFrameId: selectedFrame.callFrameId,
expression: code,
objectGroup: 'node-inspect',
generatePreview: true,
- }).then(RemoteObject.fromEvalResult);
+ }), RemoteObject.fromEvalResult);
}
- return Runtime.evaluate({
+ return PromisePrototypeThen(Runtime.evaluate({
expression: code,
objectGroup: 'node-inspect',
generatePreview: true,
- }).then(RemoteObject.fromEvalResult);
+ }), RemoteObject.fromEvalResult);
}
function controlEval(input, context, filename, callback) {
@@ -536,11 +577,16 @@ function createRepl(inspector) {
const code = prepareControlCode(input);
const result = vm.runInContext(code, context, filename);
- if (result && typeof result.then === 'function') {
- toCallback(result, returnToCallback);
- return;
+ const then = result?.then;
+ if (typeof then === 'function') {
+ FunctionPrototypeCall(
+ then, result,
+ (result) => returnToCallback(null, result),
+ returnToCallback
+ );
+ } else {
+ returnToCallback(null, result);
}
- returnToCallback(null, result);
} catch (e) {
returnToCallback(e);
}
@@ -553,76 +599,63 @@ function createRepl(inspector) {
callback(error, result);
}
- try {
- const result = evalInCurrentContext(input);
-
- if (result && typeof result.then === 'function') {
- toCallback(result, returnToCallback);
- return;
- }
- returnToCallback(null, result);
- } catch (e) {
- returnToCallback(e);
- }
+ PromisePrototypeThen(evalInCurrentContext(input),
+ (result) => returnToCallback(null, result),
+ returnToCallback
+ );
}
- function formatWatchers(verbose = false) {
+ async function formatWatchers(verbose = false) {
if (!watchedExpressions.length) {
- return Promise.resolve('');
+ return '';
}
const inspectValue = (expr) =>
- evalInCurrentContext(expr)
- // .then(formatValue)
- .catch((error) => `<${error.message}>`);
+ PromisePrototypeCatch(evalInCurrentContext(expr),
+ (error) => `<${error.message}>`);
const lastIndex = watchedExpressions.length - 1;
- return Promise.all(watchedExpressions.map(inspectValue))
- .then((values) => {
- const lines = watchedExpressions
- .map((expr, idx) => {
- const prefix = `${leftPad(idx, ' ', lastIndex)}: ${expr} =`;
- const value = inspect(values[idx]);
- if (value.indexOf('\n') === -1) {
- return `${prefix} ${value}`;
- }
- return `${prefix}\n ${value.split('\n').join('\n ')}`;
- });
- return lines.join('\n');
- })
- .then((valueList) => {
- return verbose ? `Watchers:\n${valueList}\n` : valueList;
- });
+ const values = await PromiseAll(new SafeArrayIterator(
+ ArrayPrototypeMap(watchedExpressions, inspectValue)));
+ const lines = ArrayPrototypeMap(watchedExpressions, (expr, idx) => {
+ const prefix = `${leftPad(idx, ' ', lastIndex)}: ${expr} =`;
+ const value = inspect(values[idx]);
+ if (!StringPrototypeIncludes(value, '\n')) {
+ return `${prefix} ${value}`;
+ }
+ return `${prefix}\n ${RegExpPrototypeSymbolReplace(/\n/g, value, '\n ')}`;
+ });
+ const valueList = ArrayPrototypeJoin(lines, '\n');
+ return verbose ? `Watchers:\n${valueList}\n` : valueList;
}
function watchers(verbose = false) {
- return formatWatchers(verbose).then(print);
+ return PromisePrototypeThen(formatWatchers(verbose), print);
}
// List source code
function list(delta = 5) {
- return selectedFrame.list(delta)
- .then(null, (error) => {
- print('You can\'t list source code right now');
- throw error;
- });
+ return selectedFrame.list(delta).then(null, (error) => {
+ print("You can't list source code right now");
+ throw error;
+ });
}
function handleBreakpointResolved({ breakpointId, location }) {
const script = knownScripts[location.scriptId];
const scriptUrl = script && script.url;
if (scriptUrl) {
- Object.assign(location, { scriptUrl });
+ ObjectAssign(location, { scriptUrl });
}
- const isExisting = knownBreakpoints.some((bp) => {
+ const isExisting = ArrayPrototypeSome(knownBreakpoints, (bp) => {
if (bp.breakpointId === breakpointId) {
- Object.assign(bp, { location });
+ ObjectAssign(bp, { location });
return true;
}
return false;
});
if (!isExisting) {
- knownBreakpoints.push({ breakpointId, location });
+ ArrayPrototypePush(knownBreakpoints, { breakpointId, location });
}
}
@@ -638,9 +671,11 @@ function createRepl(inspector) {
const scriptUrl = script ? script.url : location.scriptUrl;
return `${getRelativePath(scriptUrl)}:${location.lineNumber + 1}`;
}
- const breaklist = knownBreakpoints
- .map((bp, idx) => `#${idx} ${formatLocation(bp.location)}`)
- .join('\n');
+ const breaklist = ArrayPrototypeJoin(
+ ArrayPrototypeMap(
+ knownBreakpoints,
+ (bp, idx) => `#${idx} ${formatLocation(bp.location)}`),
+ '\n');
print(breaklist);
}
@@ -657,9 +692,9 @@ function createRepl(inspector) {
// setBreakpoint(): set breakpoint at current location
if (script === undefined) {
- return Debugger
- .setBreakpoint({ location: getCurrentLocation(), condition })
- .then(registerBreakpoint);
+ return PromisePrototypeThen(
+ Debugger.setBreakpoint({ location: getCurrentLocation(), condition }),
+ registerBreakpoint);
}
// setBreakpoint(line): set breakpoint in current script at specific line
@@ -668,16 +703,15 @@ function createRepl(inspector) {
scriptId: getCurrentLocation().scriptId,
lineNumber: script - 1,
};
- return Debugger.setBreakpoint({ location, condition })
- .then(registerBreakpoint);
+ return PromisePrototypeThen(
+ Debugger.setBreakpoint({ location, condition }),
+ registerBreakpoint);
}
- if (typeof script !== 'string') {
- throw new TypeError(`setBreakpoint() expects a string, got ${script}`);
- }
+ validateString(script, 'script');
// setBreakpoint('fn()'): Break when a function is called
- if (script.endsWith('()')) {
+ if (StringPrototypeEndsWith(script, '()')) {
const debugExpr = `debug(${script.slice(0, -2)})`;
const debugCall = selectedFrame ?
Debugger.evaluateOnCallFrame({
@@ -689,7 +723,7 @@ function createRepl(inspector) {
expression: debugExpr,
includeCommandLineAPI: true,
});
- return debugCall.then(({ result, wasThrown }) => {
+ return PromisePrototypeThen(debugCall, ({ result, wasThrown }) => {
if (wasThrown) return convertResultToError(result);
return undefined; // This breakpoint can't be removed the same way
});
@@ -701,15 +735,15 @@ function createRepl(inspector) {
if (knownScripts[script]) {
scriptId = script;
} else {
- for (const id of Object.keys(knownScripts)) {
+ ArrayPrototypeForEach(ObjectKeys(knownScripts), (id) => {
const scriptUrl = knownScripts[id].url;
- if (scriptUrl && scriptUrl.indexOf(script) !== -1) {
+ if (scriptUrl && StringPrototypeIncludes(scriptUrl, script)) {
if (scriptId !== null) {
ambiguous = true;
}
scriptId = id;
}
- }
+ });
}
if (ambiguous) {
@@ -723,19 +757,25 @@ function createRepl(inspector) {
if (scriptId !== null) {
const location = { scriptId, lineNumber: line - 1 };
- return Debugger.setBreakpoint({ location, condition })
- .then(registerBreakpoint);
+ return PromisePrototypeThen(
+ Debugger.setBreakpoint({ location, condition }),
+ registerBreakpoint);
}
- const escapedPath = script.replace(/([/\\.?*()^${}|[\]])/g, '\\$1');
+ const escapedPath = RegExpPrototypeSymbolReplace(/([/\\.?*()^${}|[\]])/g,
+ script, '\\$1');
const urlRegex = `^(.*[\\/\\\\])?${escapedPath}$`;
- return Debugger
- .setBreakpointByUrl({ urlRegex, lineNumber: line - 1, condition })
- .then((bp) => {
+ return PromisePrototypeThen(
+ Debugger.setBreakpointByUrl({
+ urlRegex,
+ lineNumber: line - 1,
+ condition,
+ }),
+ (bp) => {
// TODO: handle bp.locations in case the regex matches existing files
if (!bp.location) { // Fake it for now.
- Object.assign(bp, {
+ ObjectAssign(bp, {
actualLocation: {
scriptUrl: `.*/${script}$`,
lineNumber: line - 1,
@@ -747,41 +787,46 @@ function createRepl(inspector) {
}
function clearBreakpoint(url, line) {
- const breakpoint = knownBreakpoints.find(({ location }) => {
+ const breakpoint = ArrayPrototypeFind(knownBreakpoints, ({ location }) => {
if (!location) return false;
const script = knownScripts[location.scriptId];
if (!script) return false;
return (
- script.url.indexOf(url) !== -1 && (location.lineNumber + 1) === line
+ StringPrototypeIncludes(script.url, url) &&
+ (location.lineNumber + 1) === line
);
});
if (!breakpoint) {
print(`Could not find breakpoint at ${url}:${line}`);
- return Promise.resolve();
+ return PromiseResolve();
}
- return Debugger.removeBreakpoint({ breakpointId: breakpoint.breakpointId })
- .then(() => {
- const idx = knownBreakpoints.indexOf(breakpoint);
- knownBreakpoints.splice(idx, 1);
+ return PromisePrototypeThen(
+ Debugger.removeBreakpoint({ breakpointId: breakpoint.breakpointId }),
+ () => {
+ const idx = ArrayPrototypeIndexOf(knownBreakpoints, breakpoint);
+ ArrayPrototypeSplice(knownBreakpoints, idx, 1);
});
}
function restoreBreakpoints() {
- const lastBreakpoints = knownBreakpoints.slice();
- knownBreakpoints.length = 0;
- const newBreakpoints = lastBreakpoints
- .filter(({ location }) => !!location.scriptUrl)
- .map(({ location }) =>
- setBreakpoint(location.scriptUrl, location.lineNumber + 1));
- if (!newBreakpoints.length) return Promise.resolve();
- return Promise.all(newBreakpoints).then((results) => {
- print(`${results.length} breakpoints restored.`);
- });
+ const lastBreakpoints = ArrayPrototypeSplice(knownBreakpoints, 0);
+ const newBreakpoints = ArrayPrototypeMap(
+ ArrayPrototypeFilter(lastBreakpoints,
+ ({ location }) => !!location.scriptUrl),
+ ({ location }) => setBreakpoint(location.scriptUrl,
+ location.lineNumber + 1));
+ if (!newBreakpoints.length) return PromiseResolve();
+ return PromisePrototypeThen(
+ PromiseAll(new SafeArrayIterator(newBreakpoints)),
+ (results) => {
+ print(`${results.length} breakpoints restored.`);
+ });
}
function setPauseOnExceptions(state) {
- return Debugger.setPauseOnExceptions({ state })
- .then(() => {
+ return PromisePrototypeThen(
+ Debugger.setPauseOnExceptions({ state }),
+ () => {
pauseOnExceptionState = state;
});
}
@@ -807,13 +852,13 @@ function createRepl(inspector) {
const header = `${breakType} in ${scriptUrl}:${lineNumber + 1}`;
inspector.suspendReplWhile(() =>
- Promise.all([formatWatchers(true), selectedFrame.list(2)])
- .then(({ 0: watcherList, 1: context }) => {
- if (watcherList) {
- return `${watcherList}\n${inspect(context)}`;
- }
- return inspect(context);
- }).then((breakContext) => {
+ PromisePrototypeThen(
+ PromiseAll(new SafeArrayIterator(
+ [formatWatchers(true), selectedFrame.list(2)])),
+ ({ 0: watcherList, 1: context }) => {
+ const breakContext = watcherList ?
+ `${watcherList}\n${inspect(context)}` :
+ inspect(context);
print(`${header}\n${breakContext}`);
}));
});
@@ -836,15 +881,15 @@ function createRepl(inspector) {
Profiler.on('consoleProfileFinished', ({ profile }) => {
Profile.createAndRegister({ profile });
- print([
- 'Captured new CPU profile.',
- `Access it with profiles[${profiles.length - 1}]`,
- ].join('\n'));
+ print(
+ 'Captured new CPU profile.\n' +
+ `Access it with profiles[${profiles.length - 1}]`
+ );
});
function initializeContext(context) {
- inspector.domainNames.forEach((domain) => {
- Object.defineProperty(context, domain, {
+ ArrayPrototypeForEach(inspector.domainNames, (domain) => {
+ ObjectDefineProperty(context, domain, {
value: inspector[domain],
enumerable: true,
configurable: true,
@@ -910,8 +955,8 @@ function createRepl(inspector) {
},
get profileEnd() {
- return Profiler.stop()
- .then(Profile.createAndRegister);
+ return PromisePrototypeThen(Profiler.stop(),
+ Profile.createAndRegister);
},
get profiles() {
@@ -960,8 +1005,9 @@ function createRepl(inspector) {
HeapProfiler.on('addHeapSnapshotChunk', onChunk);
print('Heap snapshot: 0/0', false);
- HeapProfiler.takeHeapSnapshot({ reportProgress: true })
- .then(onResolve, onReject);
+ PromisePrototypeThen(
+ HeapProfiler.takeHeapSnapshot({ reportProgress: true }),
+ onResolve, onReject);
});
},
@@ -970,21 +1016,22 @@ function createRepl(inspector) {
},
watch(expr) {
- watchedExpressions.push(expr);
+ ArrayPrototypePush(watchedExpressions, expr);
},
unwatch(expr) {
- const index = watchedExpressions.indexOf(expr);
+ const index = ArrayPrototypeIndexOf(watchedExpressions, expr);
// Unwatch by expression
// or
// Unwatch by watcher number
- watchedExpressions.splice(index !== -1 ? index : +expr, 1);
+ ArrayPrototypeSplice(watchedExpressions,
+ index !== -1 ? index : +expr, 1);
},
get repl() {
// Don't display any default messages
- const listeners = repl.listeners('SIGINT').slice(0);
+ const listeners = ArrayPrototypeSlice(repl.listeners('SIGINT'));
repl.removeAllListeners('SIGINT');
const oldContext = repl.context;
@@ -992,7 +1039,7 @@ function createRepl(inspector) {
exitDebugRepl = () => {
// Restore all listeners
process.nextTick(() => {
- listeners.forEach((listener) => {
+ ArrayPrototypeForEach(listeners, (listener) => {
repl.on('SIGINT', listener);
});
});
@@ -1030,16 +1077,16 @@ function createRepl(inspector) {
repl.setPrompt('> ');
- print('Press Ctrl + C to leave debug repl');
+ print('Press Ctrl+C to leave debug repl');
repl.displayPrompt();
},
get version() {
- return Runtime.evaluate({
+ return PromisePrototypeThen(Runtime.evaluate({
expression: 'process.versions.v8',
contextId: 1,
returnByValue: true,
- }).then(({ result }) => {
+ }), ({ result }) => {
print(result.value);
});
},
@@ -1064,20 +1111,20 @@ function createRepl(inspector) {
aliasProperties(context, SHORTCUTS);
}
- function initAfterStart() {
- return Runtime.enable()
- .then(() => Profiler.enable())
- .then(() => Profiler.setSamplingInterval({ interval: 100 }))
- .then(() => Debugger.enable())
- .then(() => Debugger.setPauseOnExceptions({ state: 'none' }))
- .then(() => Debugger.setAsyncCallStackDepth({ maxDepth: 0 }))
- .then(() => Debugger.setBlackboxPatterns({ patterns: [] }))
- .then(() => Debugger.setPauseOnExceptions({ state: pauseOnExceptionState }))
- .then(() => restoreBreakpoints())
- .then(() => Runtime.runIfWaitingForDebugger());
+ async function initAfterStart() {
+ await Runtime.enable();
+ await Profiler.enable();
+ await Profiler.setSamplingInterval({ interval: 100 });
+ await Debugger.enable();
+ await Debugger.setPauseOnExceptions({ state: 'none' });
+ await Debugger.setAsyncCallStackDepth({ maxDepth: 0 });
+ await Debugger.setBlackboxPatterns({ patterns: [] });
+ await Debugger.setPauseOnExceptions({ state: pauseOnExceptionState });
+ await restoreBreakpoints();
+ return Runtime.runIfWaitingForDebugger();
}
- return function startRepl() {
+ return async function startRepl() {
inspector.client.on('close', () => {
resetOnStart();
});
@@ -1085,6 +1132,9 @@ function createRepl(inspector) {
initAfterStart();
});
+ // Init once for the initial connection
+ await initAfterStart();
+
const replOptions = {
prompt: 'debug> ',
input: inspector.stdin,
@@ -1099,13 +1149,10 @@ function createRepl(inspector) {
repl.on('reset', initializeContext);
repl.defineCommand('interrupt', () => {
- // We want this for testing purposes where sending CTRL-C can be tricky.
+ // We want this for testing purposes where sending Ctrl+C can be tricky.
repl.emit('SIGINT');
});
- // Init once for the initial connection
- initAfterStart();
-
return repl;
};
}
diff --git a/lib/internal/errors.js b/lib/internal/errors.js
index 31f569f5e57e6a..441c4f2f317171 100644
--- a/lib/internal/errors.js
+++ b/lib/internal/errors.js
@@ -831,6 +831,8 @@ E('ERR_CRYPTO_SCRYPT_INVALID_PARAMETER', 'Invalid scrypt parameter', Error);
E('ERR_CRYPTO_SCRYPT_NOT_SUPPORTED', 'Scrypt algorithm not supported', Error);
// Switch to TypeError. The current implementation does not seem right.
E('ERR_CRYPTO_SIGN_KEY_REQUIRED', 'No key provided to sign', Error);
+E('ERR_DEBUGGER_ERROR', '%s', Error);
+E('ERR_DEBUGGER_STARTUP_ERROR', '%s', Error);
E('ERR_DIR_CLOSED', 'Directory handle was closed', Error);
E('ERR_DIR_CONCURRENT_OPERATION',
'Cannot do synchronous work on directory handle with concurrent ' +
diff --git a/lib/internal/modules/cjs/loader.js b/lib/internal/modules/cjs/loader.js
index 4ff756a0ab5c3f..7313c7ac847cd6 100644
--- a/lib/internal/modules/cjs/loader.js
+++ b/lib/internal/modules/cjs/loader.js
@@ -438,7 +438,7 @@ function trySelf(parentPath, request) {
const EXPORTS_PATTERN = /^((?:@[^/\\%]+\/)?[^./\\%][^/\\%]*)(\/.*)?$/;
function resolveExports(nmPath, request) {
// The implementation's behavior is meant to mirror resolution in ESM.
- const [, name, expansion = ''] =
+ const { 1: name, 2: expansion = '' } =
StringPrototypeMatch(request, EXPORTS_PATTERN) || [];
if (!name)
return;
diff --git a/lib/internal/modules/esm/module_job.js b/lib/internal/modules/esm/module_job.js
index 9121cb388fccc2..0e1a7eb7b6a779 100644
--- a/lib/internal/modules/esm/module_job.js
+++ b/lib/internal/modules/esm/module_job.js
@@ -12,6 +12,7 @@ const {
PromisePrototypeCatch,
ReflectApply,
RegExpPrototypeTest,
+ SafeArrayIterator,
SafeSet,
StringPrototypeIncludes,
StringPrototypeMatch,
@@ -76,9 +77,9 @@ class ModuleJob {
});
if (promises !== undefined)
- await PromiseAll(promises);
+ await PromiseAll(new SafeArrayIterator(promises));
- return PromiseAll(dependencyJobs);
+ return PromiseAll(new SafeArrayIterator(dependencyJobs));
};
// Promise for the list of all dependencyJobs.
this.linked = link();
@@ -106,8 +107,8 @@ class ModuleJob {
}
jobsInGraph.add(moduleJob);
const dependencyJobs = await moduleJob.linked;
- return PromiseAll(
- ArrayPrototypeMap(dependencyJobs, addJobsToDependencyGraph));
+ return PromiseAll(new SafeArrayIterator(
+ ArrayPrototypeMap(dependencyJobs, addJobsToDependencyGraph)));
};
await addJobsToDependencyGraph(this);
diff --git a/lib/internal/per_context/primordials.js b/lib/internal/per_context/primordials.js
index a4ea444ef7d832..135afd4d60ce56 100644
--- a/lib/internal/per_context/primordials.js
+++ b/lib/internal/per_context/primordials.js
@@ -263,6 +263,9 @@ primordials.SafeWeakSet = makeSafe(
// Refs: https://tc39.es/ecma262/#sec-%typedarray%-intrinsic-object
[
{ name: 'TypedArray', original: Reflect.getPrototypeOf(Uint8Array) },
+ { name: 'ArrayIterator', original: {
+ prototype: Reflect.getPrototypeOf(Array.prototype[Symbol.iterator]()),
+ } },
{ name: 'StringIterator', original: {
prototype: Reflect.getPrototypeOf(String.prototype[Symbol.iterator]()),
} },
@@ -274,6 +277,10 @@ primordials.SafeWeakSet = makeSafe(
copyPrototype(original.prototype, primordials, `${name}Prototype`);
});
+primordials.SafeArrayIterator = createSafeIterator(
+ primordials.ArrayPrototypeSymbolIterator,
+ primordials.ArrayIteratorPrototypeNext
+);
primordials.SafeStringIterator = createSafeIterator(
primordials.StringPrototypeSymbolIterator,
primordials.StringIteratorPrototypeNext
diff --git a/lib/internal/util/debuglog.js b/lib/internal/util/debuglog.js
index a3b8a84772054e..46d3ed5613246e 100644
--- a/lib/internal/util/debuglog.js
+++ b/lib/internal/util/debuglog.js
@@ -6,6 +6,7 @@ const {
ObjectDefineProperty,
RegExp,
RegExpPrototypeTest,
+ SafeArrayIterator,
StringPrototypeToUpperCase
} = primordials;
@@ -78,7 +79,7 @@ function debuglog(set, cb) {
debug = debuglogImpl(enabled, set);
if (typeof cb === 'function')
cb(debug);
- debug(...args);
+ debug(...new SafeArrayIterator(args));
};
let enabled;
let test = () => {
@@ -86,7 +87,7 @@ function debuglog(set, cb) {
test = () => enabled;
return enabled;
};
- const logger = (...args) => debug(...args);
+ const logger = (...args) => debug(...new SafeArrayIterator(args));
ObjectDefineProperty(logger, 'enabled', {
get() {
return test();
diff --git a/test/parallel/test-debug-usage.js b/test/parallel/test-debug-usage.js
index eb9594f236b35c..fcf2d8edb944e0 100644
--- a/test/parallel/test-debug-usage.js
+++ b/test/parallel/test-debug-usage.js
@@ -11,7 +11,7 @@ child.stderr.setEncoding('utf8');
const expectedLines = [
/^\(node:\d+\) \[DEP0068\] DeprecationWarning:/,
- /Usage: .*node.* debug script\.js\r?\n .*node.* debug :\r?\n .*node.* debug -p \r?\n$/,
+ /Usage: .*node.* debug script\.js\r?\n .*node.* debug :\r?\n .*node.* debug --port=\r?\n .*node.* debug -p \r?\n$/,
];
let actualUsageMessage = '';
diff --git a/test/sequential/test-debugger-exec.js b/test/sequential/test-debugger-exec.js
index 15e7e212cf2466..f47eaaa5a6f62d 100644
--- a/test/sequential/test-debugger-exec.js
+++ b/test/sequential/test-debugger-exec.js
@@ -31,7 +31,7 @@ const assert = require('assert');
.then(() => {
assert.match(
cli.output,
- /Press Ctrl \+ C to leave debug repl\n+> /,
+ /Press Ctrl\+C to leave debug repl\n+> /,
'shows hint for how to leave repl');
assert.doesNotMatch(cli.output, /debug>/, 'changes the repl style');
})