GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: inspector_js_api.cc Lines: 193 213 90.6 %
Date: 2022-08-05 04:16:08 Branches: 37 78 47.4 %

Line Branch Exec Source
1
#include "base_object-inl.h"
2
#include "inspector_agent.h"
3
#include "inspector_io.h"
4
#include "memory_tracker-inl.h"
5
#include "node_external_reference.h"
6
#include "util-inl.h"
7
#include "v8-inspector.h"
8
#include "v8.h"
9
10
#include <memory>
11
12
namespace node {
13
namespace inspector {
14
namespace {
15
16
using v8::Context;
17
using v8::Function;
18
using v8::FunctionCallbackInfo;
19
using v8::FunctionTemplate;
20
using v8::Global;
21
using v8::HandleScope;
22
using v8::Isolate;
23
using v8::Local;
24
using v8::MaybeLocal;
25
using v8::NewStringType;
26
using v8::Object;
27
using v8::String;
28
using v8::Uint32;
29
using v8::Value;
30
31
using v8_inspector::StringBuffer;
32
using v8_inspector::StringView;
33
34
1235
std::unique_ptr<StringBuffer> ToProtocolString(Isolate* isolate,
35
                                               Local<Value> value) {
36
1235
  TwoByteValue buffer(isolate, value);
37
1235
  return StringBuffer::create(StringView(*buffer, buffer.length()));
38
}
39
40
struct LocalConnection {
41
922
  static std::unique_ptr<InspectorSession> Connect(
42
      Agent* inspector, std::unique_ptr<InspectorSessionDelegate> delegate) {
43
922
    return inspector->Connect(std::move(delegate), false);
44
  }
45
46
1305
  static Local<String> GetClassName(Environment* env) {
47
1305
    return FIXED_ONE_BYTE_STRING(env->isolate(), "Connection");
48
  }
49
};
50
51
struct MainThreadConnection {
52
2
  static std::unique_ptr<InspectorSession> Connect(
53
      Agent* inspector, std::unique_ptr<InspectorSessionDelegate> delegate) {
54
2
    return inspector->ConnectToMainThread(std::move(delegate), true);
55
  }
56
57
1305
  static Local<String> GetClassName(Environment* env) {
58
1305
    return FIXED_ONE_BYTE_STRING(env->isolate(), "MainThreadConnection");
59
  }
60
};
61
62
template <typename ConnectionType>
63
class JSBindingsConnection : public AsyncWrap {
64
 public:
65
  class JSBindingsSessionDelegate : public InspectorSessionDelegate {
66
   public:
67
1848
    JSBindingsSessionDelegate(Environment* env,
68
                              JSBindingsConnection* connection)
69
                              : env_(env),
70
1848
                                connection_(connection) {
71
1848
    }
72
73
14192
    void SendMessageToFrontend(const v8_inspector::StringView& message)
74
        override {
75
14192
      Isolate* isolate = env_->isolate();
76
14192
      HandleScope handle_scope(isolate);
77
28384
      Context::Scope context_scope(env_->context());
78
      Local<Value> argument;
79
14192
      if (!String::NewFromTwoByte(isolate, message.characters16(),
80
                                  NewStringType::kNormal,
81
28384
                                  message.length()).ToLocal(&argument)) return;
82
14192
      connection_->OnMessage(argument);
83
    }
84
85
   private:
86
    Environment* env_;
87
    BaseObjectPtr<JSBindingsConnection> connection_;
88
  };
89
90
1848
  JSBindingsConnection(Environment* env,
91
                       Local<Object> wrap,
92
                       Local<Function> callback)
93
                       : AsyncWrap(env, wrap, PROVIDER_INSPECTORJSBINDING),
94
1848
                         callback_(env->isolate(), callback) {
95
1848
    Agent* inspector = env->inspector_agent();
96
1848
    session_ = ConnectionType::Connect(
97
        inspector, std::make_unique<JSBindingsSessionDelegate>(env, this));
98
1848
  }
99
100
14192
  void OnMessage(Local<Value> value) {
101
28384
    MakeCallback(callback_.Get(env()->isolate()), 1, &value);
102
14192
  }
103
104
5220
  static void Bind(Environment* env, Local<Object> target) {
105
5220
    Isolate* isolate = env->isolate();
106
    Local<FunctionTemplate> tmpl =
107
5220
        NewFunctionTemplate(isolate, JSBindingsConnection::New);
108
10440
    tmpl->InstanceTemplate()->SetInternalFieldCount(
109
        JSBindingsConnection::kInternalFieldCount);
110
5220
    tmpl->Inherit(AsyncWrap::GetConstructorTemplate(env));
111
5220
    SetProtoMethod(isolate, tmpl, "dispatch", JSBindingsConnection::Dispatch);
112
5220
    SetProtoMethod(
113
        isolate, tmpl, "disconnect", JSBindingsConnection::Disconnect);
114
5220
    SetConstructorFunction(
115
        env->context(), target, ConnectionType::GetClassName(env), tmpl);
116
5220
  }
117
118
1848
  static void New(const FunctionCallbackInfo<Value>& info) {
119
1848
    Environment* env = Environment::GetCurrent(info);
120
1848
    CHECK(info[0]->IsFunction());
121
3696
    Local<Function> callback = info[0].As<Function>();
122
1848
    new JSBindingsConnection(env, info.This(), callback);
123
1848
  }
124
125
1818
  void Disconnect() {
126
1818
    session_.reset();
127
1818
    delete this;
128
  }
129
130
1818
  static void Disconnect(const FunctionCallbackInfo<Value>& info) {
131
    JSBindingsConnection* session;
132
1818
    ASSIGN_OR_RETURN_UNWRAP(&session, info.Holder());
133
1818
    session->Disconnect();
134
  }
135
136
2470
  static void Dispatch(const FunctionCallbackInfo<Value>& info) {
137
2470
    Environment* env = Environment::GetCurrent(info);
138
    JSBindingsConnection* session;
139
2470
    ASSIGN_OR_RETURN_UNWRAP(&session, info.Holder());
140
4940
    CHECK(info[0]->IsString());
141
142
2470
    if (session->session_) {
143
4940
      session->session_->Dispatch(
144
4940
          ToProtocolString(env->isolate(), info[0])->string());
145
    }
146
  }
147
148
4
  void MemoryInfo(MemoryTracker* tracker) const override {
149
4
    tracker->TrackField("callback", callback_);
150
4
    tracker->TrackFieldWithSize(
151
        "session", sizeof(*session_), "InspectorSession");
152
  }
153
154
4
  SET_MEMORY_INFO_NAME(JSBindingsConnection)
155
2
  SET_SELF_SIZE(JSBindingsConnection)
156
157
  bool IsNotIndicativeOfMemoryLeakAtExit() const override {
158
    return true;  // Binding connections emit events on their own.
159
  }
160
161
 private:
162
  std::unique_ptr<InspectorSession> session_;
163
  Global<Function> callback_;
164
};
165
166
43749
static bool InspectorEnabled(Environment* env) {
167
43749
  Agent* agent = env->inspector_agent();
168
43749
  return agent->IsActive();
169
}
170
171
1305
void SetConsoleExtensionInstaller(const FunctionCallbackInfo<Value>& info) {
172
1305
  auto env = Environment::GetCurrent(info);
173
174
1305
  CHECK_EQ(info.Length(), 1);
175
1305
  CHECK(info[0]->IsFunction());
176
177
2610
  env->set_inspector_console_extension_installer(info[0].As<Function>());
178
1305
}
179
180
8
void CallAndPauseOnStart(const FunctionCallbackInfo<v8::Value>& args) {
181
8
  Environment* env = Environment::GetCurrent(args);
182
8
  CHECK_GT(args.Length(), 1);
183
8
  CHECK(args[0]->IsFunction());
184
14
  SlicedArguments call_args(args, /* start */ 2);
185
8
  env->inspector_agent()->PauseOnNextJavascriptStatement("Break on start");
186
  v8::MaybeLocal<v8::Value> retval =
187
22
      args[0].As<v8::Function>()->Call(env->context(), args[1],
188
16
                                       call_args.length(), call_args.out());
189
6
  if (!retval.IsEmpty()) {
190
10
    args.GetReturnValue().Set(retval.ToLocalChecked());
191
  }
192
6
}
193
194
43743
void InspectorConsoleCall(const FunctionCallbackInfo<Value>& info) {
195
43743
  Environment* env = Environment::GetCurrent(info);
196
43743
  Isolate* isolate = env->isolate();
197
43743
  Local<Context> context = isolate->GetCurrentContext();
198
43743
  CHECK_GE(info.Length(), 2);
199
43743
  SlicedArguments call_args(info, /* start */ 2);
200
43743
  if (InspectorEnabled(env)) {
201
43742
    Local<Value> inspector_method = info[0];
202
43742
    CHECK(inspector_method->IsFunction());
203
43742
    if (!env->is_in_inspector_console_call()) {
204
43741
      env->set_is_in_inspector_console_call(true);
205
      MaybeLocal<Value> ret =
206
43741
          inspector_method.As<Function>()->Call(context,
207
                                                info.Holder(),
208
43741
                                                call_args.length(),
209
131223
                                                call_args.out());
210
43741
      env->set_is_in_inspector_console_call(false);
211
43741
      if (ret.IsEmpty())
212
9
        return;
213
    }
214
  }
215
216
43734
  Local<Value> node_method = info[1];
217
43734
  CHECK(node_method->IsFunction());
218
87468
  USE(node_method.As<Function>()->Call(context,
219
                                   info.Holder(),
220
43734
                                   call_args.length(),
221
174936
                                   call_args.out()));
222
}
223
224
25
static void* GetAsyncTask(int64_t asyncId) {
225
  // The inspector assumes that when other clients use its asyncTask* API,
226
  // they use real pointers, or at least something aligned like real pointer.
227
  // In general it means that our task_id should always be even.
228
  //
229
  // On 32bit platforms, the 64bit asyncId would get truncated when converted
230
  // to a 32bit pointer. However, the javascript part will never enable
231
  // the async_hook on 32bit platforms, therefore the truncation will never
232
  // happen in practice.
233
25
  return reinterpret_cast<void*>(asyncId << 1);
234
}
235
236
template <void (Agent::*asyncTaskFn)(void*)>
237
44
static void InvokeAsyncTaskFnWithId(const FunctionCallbackInfo<Value>& args) {
238
44
  Environment* env = Environment::GetCurrent(args);
239
44
  CHECK(args[0]->IsNumber());
240
44
  int64_t task_id = args[0]->IntegerValue(env->context()).FromJust();
241
44
  (env->inspector_agent()->*asyncTaskFn)(GetAsyncTask(task_id));
242
44
}
243
244
3
static void AsyncTaskScheduledWrapper(const FunctionCallbackInfo<Value>& args) {
245
3
  Environment* env = Environment::GetCurrent(args);
246
247
6
  CHECK(args[0]->IsString());
248
6
  Local<String> task_name = args[0].As<String>();
249
6
  String::Value task_name_value(args.GetIsolate(), task_name);
250
3
  StringView task_name_view(*task_name_value, task_name_value.length());
251
252
3
  CHECK(args[1]->IsNumber());
253
3
  int64_t task_id = args[1]->IntegerValue(env->context()).FromJust();
254
3
  void* task = GetAsyncTask(task_id);
255
256
3
  CHECK(args[2]->IsBoolean());
257
3
  bool recurring = args[2]->BooleanValue(args.GetIsolate());
258
259
3
  env->inspector_agent()->AsyncTaskScheduled(task_name_view, task, recurring);
260
3
}
261
262
6580
static void RegisterAsyncHookWrapper(const FunctionCallbackInfo<Value>& args) {
263
6580
  Environment* env = Environment::GetCurrent(args);
264
265
6580
  CHECK(args[0]->IsFunction());
266
13160
  Local<Function> enable_function = args[0].As<Function>();
267
6580
  CHECK(args[1]->IsFunction());
268
6580
  Local<Function> disable_function = args[1].As<Function>();
269
6580
  env->inspector_agent()->RegisterAsyncHook(env->isolate(),
270
    enable_function, disable_function);
271
6580
}
272
273
6
void IsEnabled(const FunctionCallbackInfo<Value>& args) {
274
6
  Environment* env = Environment::GetCurrent(args);
275
6
  args.GetReturnValue().Set(InspectorEnabled(env));
276
6
}
277
278
void Open(const FunctionCallbackInfo<Value>& args) {
279
  Environment* env = Environment::GetCurrent(args);
280
  Agent* agent = env->inspector_agent();
281
282
  if (args.Length() > 0 && args[0]->IsUint32()) {
283
    uint32_t port = args[0].As<Uint32>()->Value();
284
    ExclusiveAccess<HostPort>::Scoped host_port(agent->host_port());
285
    host_port->set_port(static_cast<int>(port));
286
  }
287
288
  if (args.Length() > 1 && args[1]->IsString()) {
289
    Utf8Value host(env->isolate(), args[1].As<String>());
290
    ExclusiveAccess<HostPort>::Scoped host_port(agent->host_port());
291
    host_port->set_host(*host);
292
  }
293
294
  agent->StartIoThread();
295
}
296
297
void WaitForDebugger(const FunctionCallbackInfo<Value>& args) {
298
  Environment* env = Environment::GetCurrent(args);
299
  Agent* agent = env->inspector_agent();
300
  if (agent->IsActive())
301
    agent->WaitForConnect();
302
  args.GetReturnValue().Set(agent->IsActive());
303
}
304
305
6
void Url(const FunctionCallbackInfo<Value>& args) {
306
6
  Environment* env = Environment::GetCurrent(args);
307
6
  std::string url = env->inspector_agent()->GetWsUrl();
308
6
  if (url.empty()) {
309
1
    return;
310
  }
311
10
  args.GetReturnValue().Set(OneByteString(env->isolate(), url.c_str()));
312
}
313
314
1305
void Initialize(Local<Object> target, Local<Value> unused,
315
                Local<Context> context, void* priv) {
316
1305
  Environment* env = Environment::GetCurrent(context);
317
1305
  Isolate* isolate = env->isolate();
318
319
  v8::Local<v8::Function> consoleCallFunc =
320
1305
      NewFunctionTemplate(isolate,
321
                          InspectorConsoleCall,
322
                          v8::Local<v8::Signature>(),
323
                          v8::ConstructorBehavior::kThrow,
324
1305
                          v8::SideEffectType::kHasSideEffect)
325
1305
          ->GetFunction(context)
326
1305
          .ToLocalChecked();
327
1305
  auto name_string = FIXED_ONE_BYTE_STRING(isolate, "consoleCall");
328
2610
  target->Set(context, name_string, consoleCallFunc).Check();
329
1305
  consoleCallFunc->SetName(name_string);
330
331
1305
  SetMethod(context,
332
            target,
333
            "setConsoleExtensionInstaller",
334
            SetConsoleExtensionInstaller);
335
1305
  SetMethod(context, target, "callAndPauseOnStart", CallAndPauseOnStart);
336
1305
  SetMethod(context, target, "open", Open);
337
1305
  SetMethodNoSideEffect(context, target, "url", Url);
338
1305
  SetMethod(context, target, "waitForDebugger", WaitForDebugger);
339
340
1305
  SetMethod(context, target, "asyncTaskScheduled", AsyncTaskScheduledWrapper);
341
1305
  SetMethod(context,
342
            target,
343
            "asyncTaskCanceled",
344
            InvokeAsyncTaskFnWithId<&Agent::AsyncTaskCanceled>);
345
1305
  SetMethod(context,
346
            target,
347
            "asyncTaskStarted",
348
            InvokeAsyncTaskFnWithId<&Agent::AsyncTaskStarted>);
349
1305
  SetMethod(context,
350
            target,
351
            "asyncTaskFinished",
352
            InvokeAsyncTaskFnWithId<&Agent::AsyncTaskFinished>);
353
354
1305
  SetMethod(context, target, "registerAsyncHook", RegisterAsyncHookWrapper);
355
1305
  SetMethodNoSideEffect(context, target, "isEnabled", IsEnabled);
356
357
1305
  Local<String> console_string = FIXED_ONE_BYTE_STRING(isolate, "console");
358
359
  // Grab the console from the binding object and expose those to our binding
360
  // layer.
361
1305
  Local<Object> binding = context->GetExtrasBindingObject();
362
  target
363
1305
      ->Set(context,
364
            console_string,
365
2610
            binding->Get(context, console_string).ToLocalChecked())
366
      .Check();
367
368
1305
  JSBindingsConnection<LocalConnection>::Bind(env, target);
369
1305
  JSBindingsConnection<MainThreadConnection>::Bind(env, target);
370
1305
}
371
372
}  // namespace
373
374
5314
void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
375
5314
  registry->Register(InspectorConsoleCall);
376
5314
  registry->Register(SetConsoleExtensionInstaller);
377
5314
  registry->Register(CallAndPauseOnStart);
378
5314
  registry->Register(Open);
379
5314
  registry->Register(Url);
380
5314
  registry->Register(WaitForDebugger);
381
382
5314
  registry->Register(AsyncTaskScheduledWrapper);
383
5314
  registry->Register(InvokeAsyncTaskFnWithId<&Agent::AsyncTaskCanceled>);
384
5314
  registry->Register(InvokeAsyncTaskFnWithId<&Agent::AsyncTaskStarted>);
385
5314
  registry->Register(InvokeAsyncTaskFnWithId<&Agent::AsyncTaskFinished>);
386
387
5314
  registry->Register(RegisterAsyncHookWrapper);
388
5314
  registry->Register(IsEnabled);
389
390
5314
  registry->Register(JSBindingsConnection<LocalConnection>::New);
391
5314
  registry->Register(JSBindingsConnection<LocalConnection>::Dispatch);
392
5314
  registry->Register(JSBindingsConnection<LocalConnection>::Disconnect);
393
5314
  registry->Register(JSBindingsConnection<MainThreadConnection>::New);
394
5314
  registry->Register(JSBindingsConnection<MainThreadConnection>::Dispatch);
395
5314
  registry->Register(JSBindingsConnection<MainThreadConnection>::Disconnect);
396
5314
}
397
398
}  // namespace inspector
399
}  // namespace node
400
401
5386
NODE_MODULE_CONTEXT_AWARE_INTERNAL(inspector,
402
                                  node::inspector::Initialize)
403
5314
NODE_MODULE_EXTERNAL_REFERENCE(inspector,
404
                               node::inspector::RegisterExternalReferences)