GCC Code Coverage Report
Directory: ../ Exec Total Coverage
File: /home/iojs/build/workspace/node-test-commit-linux-coverage-daily/nodes/benchmark/out/../src/inspector_js_api.cc Lines: 197 217 90.8 %
Date: 2020-11-21 04:10:54 Branches: 41 86 47.7 %

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
1025
std::unique_ptr<StringBuffer> ToProtocolString(Isolate* isolate,
35
                                               Local<Value> value) {
36
2050
  TwoByteValue buffer(isolate, value);
37
2050
  return StringBuffer::create(StringView(*buffer, buffer.length()));
38
}
39
40
struct LocalConnection {
41
767
  static std::unique_ptr<InspectorSession> Connect(
42
      Agent* inspector, std::unique_ptr<InspectorSessionDelegate> delegate) {
43
767
    return inspector->Connect(std::move(delegate), false);
44
  }
45
46
445
  static Local<String> GetClassName(Environment* env) {
47
445
    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
445
  static Local<String> GetClassName(Environment* env) {
58
445
    return FIXED_ONE_BYTE_STRING(env->isolate(), "MainThreadConnection");
59
  }
60
};
61
62
template <typename ConnectionType>
63
2304
class JSBindingsConnection : public AsyncWrap {
64
 public:
65
1536
  class JSBindingsSessionDelegate : public InspectorSessionDelegate {
66
   public:
67
769
    JSBindingsSessionDelegate(Environment* env,
68
                              JSBindingsConnection* connection)
69
                              : env_(env),
70
769
                                connection_(connection) {
71
769
    }
72
73
5565
    void SendMessageToFrontend(const v8_inspector::StringView& message)
74
        override {
75
5565
      Isolate* isolate = env_->isolate();
76
11130
      HandleScope handle_scope(isolate);
77
5565
      Context::Scope context_scope(env_->context());
78
      MaybeLocal<String> v8string =
79
5565
          String::NewFromTwoByte(isolate, message.characters16(),
80
11130
                                 NewStringType::kNormal, message.length());
81
11130
      Local<Value> argument = v8string.ToLocalChecked().As<Value>();
82
5565
      connection_->OnMessage(argument);
83
5565
    }
84
85
   private:
86
    Environment* env_;
87
    BaseObjectPtr<JSBindingsConnection> connection_;
88
  };
89
90
769
  JSBindingsConnection(Environment* env,
91
                       Local<Object> wrap,
92
                       Local<Function> callback)
93
                       : AsyncWrap(env, wrap, PROVIDER_INSPECTORJSBINDING),
94
769
                         callback_(env->isolate(), callback) {
95
769
    Agent* inspector = env->inspector_agent();
96
769
    session_ = ConnectionType::Connect(
97
        inspector, std::make_unique<JSBindingsSessionDelegate>(env, this));
98
769
  }
99
100
5565
  void OnMessage(Local<Value> value) {
101
11130
    MakeCallback(callback_.Get(env()->isolate()), 1, &value);
102
5565
  }
103
104
890
  static void Bind(Environment* env, Local<Object> target) {
105
890
    Local<String> class_name = ConnectionType::GetClassName(env);
106
    Local<FunctionTemplate> tmpl =
107
890
        env->NewFunctionTemplate(JSBindingsConnection::New);
108
1780
    tmpl->InstanceTemplate()->SetInternalFieldCount(
109
        JSBindingsConnection::kInternalFieldCount);
110
890
    tmpl->SetClassName(class_name);
111
1780
    tmpl->Inherit(AsyncWrap::GetConstructorTemplate(env));
112
890
    env->SetProtoMethod(tmpl, "dispatch", JSBindingsConnection::Dispatch);
113
890
    env->SetProtoMethod(tmpl, "disconnect", JSBindingsConnection::Disconnect);
114
2670
    target->Set(env->context(),
115
                class_name,
116
2670
                tmpl->GetFunction(env->context()).ToLocalChecked())
117
        .ToChecked();
118
890
  }
119
120
769
  static void New(const FunctionCallbackInfo<Value>& info) {
121
769
    Environment* env = Environment::GetCurrent(info);
122

1538
    CHECK(info[0]->IsFunction());
123
1538
    Local<Function> callback = info[0].As<Function>();
124
769
    new JSBindingsConnection(env, info.This(), callback);
125
769
  }
126
127
755
  void Disconnect() {
128
755
    session_.reset();
129

755
    delete this;
130
755
  }
131
132
755
  static void Disconnect(const FunctionCallbackInfo<Value>& info) {
133
    JSBindingsConnection* session;
134

755
    ASSIGN_OR_RETURN_UNWRAP(&session, info.Holder());
135
755
    session->Disconnect();
136
  }
137
138
1025
  static void Dispatch(const FunctionCallbackInfo<Value>& info) {
139
1025
    Environment* env = Environment::GetCurrent(info);
140
    JSBindingsConnection* session;
141

1025
    ASSIGN_OR_RETURN_UNWRAP(&session, info.Holder());
142

3075
    CHECK(info[0]->IsString());
143
144

1025
    if (session->session_) {
145
2050
      session->session_->Dispatch(
146
2050
          ToProtocolString(env->isolate(), info[0])->string());
147
    }
148
  }
149
150
2
  void MemoryInfo(MemoryTracker* tracker) const override {
151
2
    tracker->TrackField("callback", callback_);
152
2
    tracker->TrackFieldWithSize(
153
        "session", sizeof(*session_), "InspectorSession");
154
2
  }
155
156
2
  SET_MEMORY_INFO_NAME(JSBindingsConnection)
157
2
  SET_SELF_SIZE(JSBindingsConnection)
158
159
  bool IsNotIndicativeOfMemoryLeakAtExit() const override {
160
    return true;  // Binding connections emit events on their own.
161
  }
162
163
 private:
164
  std::unique_ptr<InspectorSession> session_;
165
  Global<Function> callback_;
166
};
167
168
17477
static bool InspectorEnabled(Environment* env) {
169
17477
  Agent* agent = env->inspector_agent();
170
17477
  return agent->IsActive();
171
}
172
173
445
void SetConsoleExtensionInstaller(const FunctionCallbackInfo<Value>& info) {
174
445
  auto env = Environment::GetCurrent(info);
175
176
445
  CHECK_EQ(info.Length(), 1);
177
890
  CHECK(info[0]->IsFunction());
178
179
890
  env->set_inspector_console_extension_installer(info[0].As<Function>());
180
445
}
181
182
8
void CallAndPauseOnStart(const FunctionCallbackInfo<v8::Value>& args) {
183
8
  Environment* env = Environment::GetCurrent(args);
184
8
  CHECK_GT(args.Length(), 1);
185
16
  CHECK(args[0]->IsFunction());
186
14
  SlicedArguments call_args(args, /* start */ 2);
187
8
  env->inspector_agent()->PauseOnNextJavascriptStatement("Break on start");
188
  v8::MaybeLocal<v8::Value> retval =
189
22
      args[0].As<v8::Function>()->Call(env->context(), args[1],
190
24
                                       call_args.length(), call_args.out());
191
6
  if (!retval.IsEmpty()) {
192
10
    args.GetReturnValue().Set(retval.ToLocalChecked());
193
  }
194
6
}
195
196
17472
void InspectorConsoleCall(const FunctionCallbackInfo<Value>& info) {
197
17472
  Environment* env = Environment::GetCurrent(info);
198
17472
  Isolate* isolate = env->isolate();
199
17472
  Local<Context> context = isolate->GetCurrentContext();
200
17472
  CHECK_GE(info.Length(), 2);
201
34935
  SlicedArguments call_args(info, /* start */ 2);
202
17472
  if (InspectorEnabled(env)) {
203
17438
    Local<Value> inspector_method = info[0];
204
17438
    CHECK(inspector_method->IsFunction());
205
17438
    if (!env->is_in_inspector_console_call()) {
206
17437
      env->set_is_in_inspector_console_call(true);
207
      MaybeLocal<Value> ret =
208
34874
          inspector_method.As<Function>()->Call(context,
209
                                                info.Holder(),
210
17437
                                                call_args.length(),
211
69748
                                                call_args.out());
212
17437
      env->set_is_in_inspector_console_call(false);
213
17437
      if (ret.IsEmpty())
214
9
        return;
215
    }
216
  }
217
218
17463
  Local<Value> node_method = info[1];
219
17463
  CHECK(node_method->IsFunction());
220
52389
  node_method.As<Function>()->Call(context,
221
                                   info.Holder(),
222
17463
                                   call_args.length(),
223
69852
                                   call_args.out()).FromMaybe(Local<Value>());
224
}
225
226
7
static void* GetAsyncTask(int64_t asyncId) {
227
  // The inspector assumes that when other clients use its asyncTask* API,
228
  // they use real pointers, or at least something aligned like real pointer.
229
  // In general it means that our task_id should always be even.
230
  //
231
  // On 32bit platforms, the 64bit asyncId would get truncated when converted
232
  // to a 32bit pointer. However, the javascript part will never enable
233
  // the async_hook on 32bit platforms, therefore the truncation will never
234
  // happen in practice.
235
7
  return reinterpret_cast<void*>(asyncId << 1);
236
}
237
238
template <void (Agent::*asyncTaskFn)(void*)>
239
6
static void InvokeAsyncTaskFnWithId(const FunctionCallbackInfo<Value>& args) {
240
6
  Environment* env = Environment::GetCurrent(args);
241

12
  CHECK(args[0]->IsNumber());
242
24
  int64_t task_id = args[0]->IntegerValue(env->context()).FromJust();
243
6
  (env->inspector_agent()->*asyncTaskFn)(GetAsyncTask(task_id));
244
6
}
245
246
1
static void AsyncTaskScheduledWrapper(const FunctionCallbackInfo<Value>& args) {
247
1
  Environment* env = Environment::GetCurrent(args);
248
249
3
  CHECK(args[0]->IsString());
250
2
  Local<String> task_name = args[0].As<String>();
251
2
  String::Value task_name_value(args.GetIsolate(), task_name);
252
1
  StringView task_name_view(*task_name_value, task_name_value.length());
253
254
2
  CHECK(args[1]->IsNumber());
255
4
  int64_t task_id = args[1]->IntegerValue(env->context()).FromJust();
256
1
  void* task = GetAsyncTask(task_id);
257
258
2
  CHECK(args[2]->IsBoolean());
259
3
  bool recurring = args[2]->BooleanValue(args.GetIsolate());
260
261
1
  env->inspector_agent()->AsyncTaskScheduled(task_name_view, task, recurring);
262
1
}
263
264
5006
static void RegisterAsyncHookWrapper(const FunctionCallbackInfo<Value>& args) {
265
5006
  Environment* env = Environment::GetCurrent(args);
266
267
10012
  CHECK(args[0]->IsFunction());
268
10012
  Local<Function> enable_function = args[0].As<Function>();
269
10012
  CHECK(args[1]->IsFunction());
270
10012
  Local<Function> disable_function = args[1].As<Function>();
271
5006
  env->inspector_agent()->RegisterAsyncHook(env->isolate(),
272
5006
    enable_function, disable_function);
273
5006
}
274
275
5
void IsEnabled(const FunctionCallbackInfo<Value>& args) {
276
5
  Environment* env = Environment::GetCurrent(args);
277
15
  args.GetReturnValue().Set(InspectorEnabled(env));
278
5
}
279
280
void Open(const FunctionCallbackInfo<Value>& args) {
281
  Environment* env = Environment::GetCurrent(args);
282
  Agent* agent = env->inspector_agent();
283
284
  if (args.Length() > 0 && args[0]->IsUint32()) {
285
    uint32_t port = args[0].As<Uint32>()->Value();
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
11
  std::string url = env->inspector_agent()->GetWsUrl();
310
6
  if (url.empty()) {
311
1
    return;
312
  }
313
15
  args.GetReturnValue().Set(OneByteString(env->isolate(), url.c_str()));
314
}
315
316
445
void Initialize(Local<Object> target, Local<Value> unused,
317
                Local<Context> context, void* priv) {
318
445
  Environment* env = Environment::GetCurrent(context);
319
320
  v8::Local<v8::Function> consoleCallFunc =
321
890
      env->NewFunctionTemplate(InspectorConsoleCall, v8::Local<v8::Signature>(),
322
                               v8::ConstructorBehavior::kThrow,
323
445
                               v8::SideEffectType::kHasSideEffect)
324
890
          ->GetFunction(context)
325
445
          .ToLocalChecked();
326
445
  auto name_string = FIXED_ONE_BYTE_STRING(env->isolate(), "consoleCall");
327
890
  target->Set(context, name_string, consoleCallFunc).Check();
328
445
  consoleCallFunc->SetName(name_string);
329
330
  env->SetMethod(
331
445
      target, "setConsoleExtensionInstaller", SetConsoleExtensionInstaller);
332
445
  env->SetMethod(target, "callAndPauseOnStart", CallAndPauseOnStart);
333
445
  env->SetMethod(target, "open", Open);
334
445
  env->SetMethodNoSideEffect(target, "url", Url);
335
445
  env->SetMethod(target, "waitForDebugger", WaitForDebugger);
336
337
445
  env->SetMethod(target, "asyncTaskScheduled", AsyncTaskScheduledWrapper);
338
  env->SetMethod(target, "asyncTaskCanceled",
339
445
      InvokeAsyncTaskFnWithId<&Agent::AsyncTaskCanceled>);
340
  env->SetMethod(target, "asyncTaskStarted",
341
445
      InvokeAsyncTaskFnWithId<&Agent::AsyncTaskStarted>);
342
  env->SetMethod(target, "asyncTaskFinished",
343
445
      InvokeAsyncTaskFnWithId<&Agent::AsyncTaskFinished>);
344
345
445
  env->SetMethod(target, "registerAsyncHook", RegisterAsyncHookWrapper);
346
445
  env->SetMethodNoSideEffect(target, "isEnabled", IsEnabled);
347
348
445
  JSBindingsConnection<LocalConnection>::Bind(env, target);
349
445
  JSBindingsConnection<MainThreadConnection>::Bind(env, target);
350
445
}
351
352
}  // namespace
353
354
4601
void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
355
4601
  registry->Register(InspectorConsoleCall);
356
4601
  registry->Register(SetConsoleExtensionInstaller);
357
4601
  registry->Register(CallAndPauseOnStart);
358
4601
  registry->Register(Open);
359
4601
  registry->Register(Url);
360
4601
  registry->Register(WaitForDebugger);
361
362
4601
  registry->Register(AsyncTaskScheduledWrapper);
363
4601
  registry->Register(InvokeAsyncTaskFnWithId<&Agent::AsyncTaskCanceled>);
364
4601
  registry->Register(InvokeAsyncTaskFnWithId<&Agent::AsyncTaskStarted>);
365
4601
  registry->Register(InvokeAsyncTaskFnWithId<&Agent::AsyncTaskFinished>);
366
367
4601
  registry->Register(RegisterAsyncHookWrapper);
368
4601
  registry->Register(IsEnabled);
369
370
4601
  registry->Register(JSBindingsConnection<LocalConnection>::New);
371
4601
  registry->Register(JSBindingsConnection<LocalConnection>::Dispatch);
372
4601
  registry->Register(JSBindingsConnection<LocalConnection>::Disconnect);
373
4601
  registry->Register(JSBindingsConnection<MainThreadConnection>::New);
374
4601
  registry->Register(JSBindingsConnection<MainThreadConnection>::Dispatch);
375
4601
  registry->Register(JSBindingsConnection<MainThreadConnection>::Disconnect);
376
4601
}
377
378
}  // namespace inspector
379
}  // namespace node
380
381
4670
NODE_MODULE_CONTEXT_AWARE_INTERNAL(inspector,
382
                                  node::inspector::Initialize)
383

18635
NODE_MODULE_EXTERNAL_REFERENCE(inspector,
384
                               node::inspector::RegisterExternalReferences)