All files / lib inspector.js

94.38% Statements 151/160
87.18% Branches 34/39
85.71% Functions 6/7
94.38% Lines 151/160

Press n or j to go to the next uncovered block, b, p or k for the previous block.

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 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 1613x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 76x 76x 76x 76x 76x 3x 3x 895x 895x 894x 894x 895x 3x 3x 2x 2x 2x 2x 2x 2x 2x 2x 3x 3x 5307x 5307x 5307x 1152x 1152x 1152x 794x 2x 2x 2x 792x 792x 792x 5307x 4155x 4155x 4155x 5307x 1x 1x 5307x 3x 3x 1168x 1168x 4x 4x 4x 1162x 807x 807x 1168x 800x 800x 1153x 1168x 1x 1x 1152x 1152x 1152x 797x 797x 1168x 794x 794x 1152x 1168x 3x 3x 883x 883x 882x 882x 882x 883x     882x 882x 883x 3x 3x 4x 4x 4x 4x       4x 3x         3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x  
'use strict';
 
const {
  JSONParse,
  JSONStringify,
  SafeMap,
  Symbol,
} = primordials;
 
const {
  ERR_INSPECTOR_ALREADY_ACTIVATED,
  ERR_INSPECTOR_ALREADY_CONNECTED,
  ERR_INSPECTOR_CLOSED,
  ERR_INSPECTOR_COMMAND,
  ERR_INSPECTOR_NOT_AVAILABLE,
  ERR_INSPECTOR_NOT_CONNECTED,
  ERR_INSPECTOR_NOT_ACTIVE,
  ERR_INSPECTOR_NOT_WORKER,
} = require('internal/errors').codes;
 
const { hasInspector } = internalBinding('config');
if (!hasInspector)
  throw new ERR_INSPECTOR_NOT_AVAILABLE();
 
const EventEmitter = require('events');
const { queueMicrotask } = require('internal/process/task_queues');
const {
  validateCallback,
  validateObject,
  validateString,
} = require('internal/validators');
const { isMainThread } = require('worker_threads');
 
const {
  Connection,
  MainThreadConnection,
  open,
  url,
  isEnabled,
  waitForDebugger
} = internalBinding('inspector');
 
const connectionSymbol = Symbol('connectionProperty');
const messageCallbacksSymbol = Symbol('messageCallbacks');
const nextIdSymbol = Symbol('nextId');
const onMessageSymbol = Symbol('onMessage');
 
class Session extends EventEmitter {
  constructor() {
    super();
    this[connectionSymbol] = null;
    this[nextIdSymbol] = 1;
    this[messageCallbacksSymbol] = new SafeMap();
  }
 
  connect() {
    if (this[connectionSymbol])
      throw new ERR_INSPECTOR_ALREADY_CONNECTED('The inspector session');
    this[connectionSymbol] =
      new Connection((message) => this[onMessageSymbol](message));
  }
 
  connectToMainThread() {
    if (isMainThread)
      throw new ERR_INSPECTOR_NOT_WORKER();
    if (this[connectionSymbol])
      throw new ERR_INSPECTOR_ALREADY_CONNECTED('The inspector session');
    this[connectionSymbol] =
      new MainThreadConnection(
        (message) => queueMicrotask(() => this[onMessageSymbol](message)));
  }
 
  [onMessageSymbol](message) {
    const parsed = JSONParse(message);
    try {
      if (parsed.id) {
        const callback = this[messageCallbacksSymbol].get(parsed.id);
        this[messageCallbacksSymbol].delete(parsed.id);
        if (callback) {
          if (parsed.error) {
            return callback(new ERR_INSPECTOR_COMMAND(parsed.error.code,
                                                      parsed.error.message));
          }
 
          callback(null, parsed.result);
        }
      } else {
        this.emit(parsed.method, parsed);
        this.emit('inspectorNotification', parsed);
      }
    } catch (error) {
      process.emitWarning(error);
    }
  }
 
  post(method, params, callback) {
    validateString(method, 'method');
    if (!callback && typeof params === 'function') {
      callback = params;
      params = null;
    }
    if (params) {
      validateObject(params, 'params');
    }
    if (callback) {
      validateCallback(callback);
    }
 
    if (!this[connectionSymbol]) {
      throw new ERR_INSPECTOR_NOT_CONNECTED();
    }
    const id = this[nextIdSymbol]++;
    const message = { id, method };
    if (params) {
      message.params = params;
    }
    if (callback) {
      this[messageCallbacksSymbol].set(id, callback);
    }
    this[connectionSymbol].dispatch(JSONStringify(message));
  }
 
  disconnect() {
    if (!this[connectionSymbol])
      return;
    this[connectionSymbol].disconnect();
    this[connectionSymbol] = null;
    const remainingCallbacks = this[messageCallbacksSymbol].values();
    for (const callback of remainingCallbacks) {
      process.nextTick(callback, new ERR_INSPECTOR_CLOSED());
    }
    this[messageCallbacksSymbol].clear();
    this[nextIdSymbol] = 1;
  }
}
 
function inspectorOpen(port, host, wait) {
  if (isEnabled()) {
    throw new ERR_INSPECTOR_ALREADY_ACTIVATED();
  }
  open(port, host);
  if (wait)
    waitForDebugger();
}
 
function inspectorWaitForDebugger() {
  if (!waitForDebugger())
    throw new ERR_INSPECTOR_NOT_ACTIVE();
}
 
module.exports = {
  open: inspectorOpen,
  close: process._debugEnd,
  url: url,
  waitForDebugger: inspectorWaitForDebugger,
  // This is dynamically added during bootstrap,
  // where the console from the VM is still available
  console: require('internal/util/inspector').consoleFromVM,
  Session
};