GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: inspector_js_api.cc Lines: 193 214 90.2 %
Date: 2022-12-31 04:22:30 Branches: 37 80 46.2 %

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