• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1'use strict';
2
3const {
4  JSONParse,
5  JSONStringify,
6  SafeMap,
7  Symbol,
8} = primordials;
9
10const {
11  ERR_INSPECTOR_ALREADY_ACTIVATED,
12  ERR_INSPECTOR_ALREADY_CONNECTED,
13  ERR_INSPECTOR_CLOSED,
14  ERR_INSPECTOR_COMMAND,
15  ERR_INSPECTOR_NOT_AVAILABLE,
16  ERR_INSPECTOR_NOT_CONNECTED,
17  ERR_INSPECTOR_NOT_ACTIVE,
18  ERR_INSPECTOR_NOT_WORKER,
19  ERR_INVALID_ARG_TYPE,
20  ERR_INVALID_CALLBACK
21} = require('internal/errors').codes;
22
23const { hasInspector } = internalBinding('config');
24if (!hasInspector)
25  throw new ERR_INSPECTOR_NOT_AVAILABLE();
26
27const EventEmitter = require('events');
28const { queueMicrotask } = require('internal/process/task_queues');
29const { validateString } = require('internal/validators');
30const { isMainThread } = require('worker_threads');
31
32const {
33  Connection,
34  MainThreadConnection,
35  open,
36  url,
37  isEnabled,
38  waitForDebugger
39} = internalBinding('inspector');
40
41const connectionSymbol = Symbol('connectionProperty');
42const messageCallbacksSymbol = Symbol('messageCallbacks');
43const nextIdSymbol = Symbol('nextId');
44const onMessageSymbol = Symbol('onMessage');
45
46class Session extends EventEmitter {
47  constructor() {
48    super();
49    this[connectionSymbol] = null;
50    this[nextIdSymbol] = 1;
51    this[messageCallbacksSymbol] = new SafeMap();
52  }
53
54  connect() {
55    if (this[connectionSymbol])
56      throw new ERR_INSPECTOR_ALREADY_CONNECTED('The inspector session');
57    this[connectionSymbol] =
58      new Connection((message) => this[onMessageSymbol](message));
59  }
60
61  connectToMainThread() {
62    if (isMainThread)
63      throw new ERR_INSPECTOR_NOT_WORKER();
64    if (this[connectionSymbol])
65      throw new ERR_INSPECTOR_ALREADY_CONNECTED('The inspector session');
66    this[connectionSymbol] =
67      new MainThreadConnection(
68        (message) => queueMicrotask(() => this[onMessageSymbol](message)));
69  }
70
71  [onMessageSymbol](message) {
72    const parsed = JSONParse(message);
73    try {
74      if (parsed.id) {
75        const callback = this[messageCallbacksSymbol].get(parsed.id);
76        this[messageCallbacksSymbol].delete(parsed.id);
77        if (callback) {
78          if (parsed.error) {
79            return callback(new ERR_INSPECTOR_COMMAND(parsed.error.code,
80                                                      parsed.error.message));
81          }
82
83          callback(null, parsed.result);
84        }
85      } else {
86        this.emit(parsed.method, parsed);
87        this.emit('inspectorNotification', parsed);
88      }
89    } catch (error) {
90      process.emitWarning(error);
91    }
92  }
93
94  post(method, params, callback) {
95    validateString(method, 'method');
96    if (!callback && typeof params === 'function') {
97      callback = params;
98      params = null;
99    }
100    if (params && typeof params !== 'object') {
101      throw new ERR_INVALID_ARG_TYPE('params', 'Object', params);
102    }
103    if (callback && typeof callback !== 'function') {
104      throw new ERR_INVALID_CALLBACK(callback);
105    }
106
107    if (!this[connectionSymbol]) {
108      throw new ERR_INSPECTOR_NOT_CONNECTED();
109    }
110    const id = this[nextIdSymbol]++;
111    const message = { id, method };
112    if (params) {
113      message.params = params;
114    }
115    if (callback) {
116      this[messageCallbacksSymbol].set(id, callback);
117    }
118    this[connectionSymbol].dispatch(JSONStringify(message));
119  }
120
121  disconnect() {
122    if (!this[connectionSymbol])
123      return;
124    this[connectionSymbol].disconnect();
125    this[connectionSymbol] = null;
126    const remainingCallbacks = this[messageCallbacksSymbol].values();
127    for (const callback of remainingCallbacks) {
128      process.nextTick(callback, new ERR_INSPECTOR_CLOSED());
129    }
130    this[messageCallbacksSymbol].clear();
131    this[nextIdSymbol] = 1;
132  }
133}
134
135function inspectorOpen(port, host, wait) {
136  if (isEnabled()) {
137    throw new ERR_INSPECTOR_ALREADY_ACTIVATED();
138  }
139  open(port, host);
140  if (wait)
141    waitForDebugger();
142}
143
144function inspectorWaitForDebugger() {
145  if (!waitForDebugger())
146    throw new ERR_INSPECTOR_NOT_ACTIVE();
147}
148
149module.exports = {
150  open: inspectorOpen,
151  close: process._debugEnd,
152  url: url,
153  waitForDebugger: inspectorWaitForDebugger,
154  // This is dynamically added during bootstrap,
155  // where the console from the VM is still available
156  console: require('internal/util/inspector').consoleFromVM,
157  Session
158};
159