summaryrefslogtreecommitdiff
path: root/lib/internal/util/inspector.js
blob: 0d9580c83224e4e24242191bd5335d74e16303f9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
'use strict';

const {
  ArrayPrototypeSome,
  ArrayPrototypePushApply,
  FunctionPrototypeBind,
  ObjectDefineProperty,
  ObjectKeys,
  ObjectPrototypeHasOwnProperty,
  RegExpPrototypeExec,
  SafeWeakMap,
} = primordials;

const { validatePort } = require('internal/validators');

const kMinPort = 1024;
const kMaxPort = 65535;
const kInspectArgRegex = /--inspect(?:-brk|-port)?|--debug-port/;
const kInspectMsgRegex = /Debugger listening on ws:\/\/\[?(.+?)\]?:(\d+)\/|For help, see: https:\/\/nodejs\.org\/en\/docs\/inspector|Debugger attached|Waiting for the debugger to disconnect\.\.\./;

const _isUsingInspector = new SafeWeakMap();
function isUsingInspector(execArgv = process.execArgv) {
  if (!_isUsingInspector.has(execArgv)) {
    _isUsingInspector.set(execArgv,
                          ArrayPrototypeSome(execArgv, (arg) => RegExpPrototypeExec(kInspectArgRegex, arg) !== null) ||
      RegExpPrototypeExec(kInspectArgRegex, process.env.NODE_OPTIONS) !== null);
  }
  return _isUsingInspector.get(execArgv);
}

let debugPortOffset = 1;
function getInspectPort(inspectPort) {
  if (typeof inspectPort === 'function') {
    inspectPort = inspectPort();
  } else if (inspectPort == null) {
    inspectPort = process.debugPort + debugPortOffset;
    if (inspectPort > kMaxPort)
      inspectPort = inspectPort - kMaxPort + kMinPort - 1;
    debugPortOffset++;
  }
  validatePort(inspectPort);

  return inspectPort;
}

let session;
function sendInspectorCommand(cb, onError) {
  const { hasInspector } = internalBinding('config');
  if (!hasInspector) return onError();
  const inspector = require('inspector');
  if (session === undefined) session = new inspector.Session();
  session.connect();
  try {
    return cb(session);
  } finally {
    session.disconnect();
  }
}

function isInspectorMessage(string) {
  return isUsingInspector() && RegExpPrototypeExec(kInspectMsgRegex, string) !== null;
}

// Create a special require function for the inspector command line API
function installConsoleExtensions(commandLineApi) {
  if (commandLineApi.require) { return; }
  const { tryGetCwd } = require('internal/process/execution');
  const CJSModule = require('internal/modules/cjs/loader').Module;
  const { makeRequireFunction } = require('internal/modules/helpers');
  const consoleAPIModule = new CJSModule('<inspector console>');
  const cwd = tryGetCwd();
  consoleAPIModule.paths = [];
  ArrayPrototypePushApply(consoleAPIModule.paths, CJSModule._nodeModulePaths(cwd));
  ArrayPrototypePushApply(consoleAPIModule.paths, CJSModule.globalPaths);
  commandLineApi.require = makeRequireFunction(consoleAPIModule);
}

// Wrap a console implemented by Node.js with features from the VM inspector
function wrapConsole(consoleFromNode) {
  const { consoleCall, console: consoleFromVM } = internalBinding('inspector');
  for (const key of ObjectKeys(consoleFromVM)) {
    // If global console has the same method as inspector console,
    // then wrap these two methods into one. Native wrapper will preserve
    // the original stack.
    if (ObjectPrototypeHasOwnProperty(consoleFromNode, key)) {
      consoleFromNode[key] = FunctionPrototypeBind(
        consoleCall,
        consoleFromNode,
        consoleFromVM[key],
        consoleFromNode[key],
      );
      ObjectDefineProperty(consoleFromNode[key], 'name', {
        __proto__: null,
        value: key,
      });
    } else {
      // Add additional console APIs from the inspector
      consoleFromNode[key] = consoleFromVM[key];
    }
  }
}

module.exports = {
  getInspectPort,
  installConsoleExtensions,
  isInspectorMessage,
  isUsingInspector,
  sendInspectorCommand,
  wrapConsole,
};