GCC Code Coverage Report
Directory: ../ Exec Total Coverage
File: /home/iojs/build/workspace/node-test-commit-linux-coverage/nodes/benchmark/out/../src/inspector_js_api.cc Lines: 192 198 97.0 %
Date: 2017-10-21 Branches: 64 108 59.3 %

Line Branch Exec Source
1
#include "base-object.h"
2
#include "base-object-inl.h"
3
#include "inspector_agent.h"
4
#include "inspector_io.h"
5
#include "node_internals.h"
6
#include "v8.h"
7
#include "v8-inspector.h"
8
9
namespace node {
10
namespace inspector {
11
namespace {
12
13
using v8::Context;
14
using v8::Function;
15
using v8::FunctionCallbackInfo;
16
using v8::FunctionTemplate;
17
using v8::HandleScope;
18
using v8::Isolate;
19
using v8::Local;
20
using v8::MaybeLocal;
21
using v8::NewStringType;
22
using v8::Object;
23
using v8::Persistent;
24
using v8::String;
25
using v8::Value;
26
27
using v8_inspector::StringBuffer;
28
using v8_inspector::StringView;
29
30
19
std::unique_ptr<StringBuffer> ToProtocolString(Isolate* isolate,
31
                                               Local<Value> value) {
32
19
  TwoByteValue buffer(isolate, value);
33
19
  return StringBuffer::create(StringView(*buffer, buffer.length()));
34
}
35
36
class JSBindingsConnection : public AsyncWrap {
37
 public:
38
6
  class JSBindingsSessionDelegate : public InspectorSessionDelegate {
39
   public:
40
9
    JSBindingsSessionDelegate(Environment* env,
41
                              JSBindingsConnection* connection)
42
                              : env_(env),
43
9
                                connection_(connection) {
44
9
    }
45
46
11
    bool WaitForFrontendMessageWhilePaused() override {
47
11
      return false;
48
    }
49
50
237
    void SendMessageToFrontend(const v8_inspector::StringView& message)
51
        override {
52
237
      Isolate* isolate = env_->isolate();
53
237
      HandleScope handle_scope(isolate);
54
237
      Context::Scope context_scope(env_->context());
55
      MaybeLocal<String> v8string =
56
          String::NewFromTwoByte(isolate, message.characters16(),
57
237
                                 NewStringType::kNormal, message.length());
58
474
      Local<Value> argument = v8string.ToLocalChecked().As<Value>();
59
474
      connection_->OnMessage(argument);
60
237
    }
61
62
6
    void Disconnect() {
63
6
      Agent* agent = env_->inspector_agent();
64
6
      if (agent->delegate() == this)
65
6
        agent->Disconnect();
66
6
    }
67
68
   private:
69
    Environment* env_;
70
    JSBindingsConnection* connection_;
71
  };
72
73
9
  JSBindingsConnection(Environment* env,
74
                       Local<Object> wrap,
75
                       Local<Function> callback)
76
                       : AsyncWrap(env, wrap, PROVIDER_INSPECTORJSBINDING),
77
                         delegate_(env, this),
78
9
                         callback_(env->isolate(), callback) {
79
9
    Wrap(wrap, this);
80
81
9
    Agent* inspector = env->inspector_agent();
82
9
    if (inspector->delegate() != nullptr) {
83
2
      env->ThrowTypeError("Session is already attached");
84
11
      return;
85
    }
86
7
    inspector->Connect(&delegate_);
87
  }
88
89
18
  ~JSBindingsConnection() override {
90
6
    callback_.Reset();
91
12
  }
92
93
237
  void OnMessage(Local<Value> value) {
94
474
    MakeCallback(callback_.Get(env()->isolate()), 1, &value);
95
237
  }
96
97
19
  void CheckIsCurrent() {
98
19
    Agent* inspector = env()->inspector_agent();
99
19
    CHECK_EQ(&delegate_, inspector->delegate());
100
19
  }
101
102
9
  static void New(const FunctionCallbackInfo<Value>& info) {
103
9
    Environment* env = Environment::GetCurrent(info);
104
18
    if (!info[0]->IsFunction()) {
105
      env->ThrowTypeError("Message callback is required");
106
9
      return;
107
    }
108
18
    Local<Function> callback = info[0].As<Function>();
109
9
    new JSBindingsConnection(env, info.This(), callback);
110
  }
111
112
6
  void Disconnect() {
113
6
    delegate_.Disconnect();
114
12
    if (!persistent().IsEmpty()) {
115
6
      ClearWrap(object());
116
6
      persistent().Reset();
117
    }
118
6
    delete this;
119
6
  }
120
121
6
  static void Disconnect(const FunctionCallbackInfo<Value>& info) {
122
    JSBindingsConnection* session;
123
12
    ASSIGN_OR_RETURN_UNWRAP(&session, info.Holder());
124
6
    session->Disconnect();
125
  }
126
127
19
  static void Dispatch(const FunctionCallbackInfo<Value>& info) {
128
19
    Environment* env = Environment::GetCurrent(info);
129
    JSBindingsConnection* session;
130
19
    ASSIGN_OR_RETURN_UNWRAP(&session, info.Holder());
131
57
    if (!info[0]->IsString()) {
132
      env->ThrowTypeError("Inspector message must be a string");
133
      return;
134
    }
135
136
19
    session->CheckIsCurrent();
137
19
    Agent* inspector = env->inspector_agent();
138
19
    inspector->Dispatch(ToProtocolString(env->isolate(), info[0])->string());
139
  }
140
141
  size_t self_size() const override { return sizeof(*this); }
142
143
 private:
144
  JSBindingsSessionDelegate delegate_;
145
  Persistent<Function> callback_;
146
};
147
148
149
3200
void AddCommandLineAPI(const FunctionCallbackInfo<Value>& info) {
150
3200
  auto env = Environment::GetCurrent(info);
151
3200
  Local<Context> context = env->context();
152
153


16000
  if (info.Length() != 2 || !info[0]->IsString()) {
154
    return env->ThrowTypeError("inspector.addCommandLineAPI takes "
155
3200
        "exactly 2 arguments: a string and a value.");
156
  }
157
158
3200
  Local<Object> console_api = env->inspector_console_api_object();
159
6400
  console_api->Set(context, info[0], info[1]).FromJust();
160
}
161
162
8
void CallAndPauseOnStart(const FunctionCallbackInfo<v8::Value>& args) {
163
8
  Environment* env = Environment::GetCurrent(args);
164
8
  CHECK_GT(args.Length(), 1);
165
16
  CHECK(args[0]->IsFunction());
166
8
  std::vector<v8::Local<v8::Value>> call_args;
167
46
  for (int i = 2; i < args.Length(); i++) {
168
15
    call_args.push_back(args[i]);
169
  }
170
171
8
  env->inspector_agent()->PauseOnNextJavascriptStatement("Break on start");
172
  v8::MaybeLocal<v8::Value> retval =
173
14
      args[0].As<v8::Function>()->Call(env->context(), args[1],
174
32
                                       call_args.size(), call_args.data());
175
6
  if (!retval.IsEmpty()) {
176
10
    args.GetReturnValue().Set(retval.ToLocalChecked());
177
6
  }
178
6
}
179
180
16825
void InspectorConsoleCall(const FunctionCallbackInfo<Value>& info) {
181
16825
  Isolate* isolate = info.GetIsolate();
182
16825
  HandleScope handle_scope(isolate);
183
16825
  Local<Context> context = isolate->GetCurrentContext();
184
16825
  CHECK_LT(2, info.Length());
185
33650
  std::vector<Local<Value>> call_args;
186
86178
  for (int i = 3; i < info.Length(); ++i) {
187
26264
    call_args.push_back(info[i]);
188
  }
189
16825
  Environment* env = Environment::GetCurrent(isolate);
190
16825
  if (env->inspector_agent()->enabled()) {
191
46
    Local<Value> inspector_method = info[0];
192
46
    CHECK(inspector_method->IsFunction());
193
46
    Local<Value> config_value = info[2];
194
46
    CHECK(config_value->IsObject());
195
46
    Local<Object> config_object = config_value.As<Object>();
196
46
    Local<String> in_call_key = FIXED_ONE_BYTE_STRING(isolate, "in_call");
197
138
    if (!config_object->Has(context, in_call_key).FromMaybe(false)) {
198
90
      CHECK(config_object->Set(context,
199
                               in_call_key,
200
                               v8::True(isolate)).FromJust());
201
180
      CHECK(!inspector_method.As<Function>()->Call(context,
202
                                                   info.Holder(),
203
                                                   call_args.size(),
204
                                                   call_args.data()).IsEmpty());
205
    }
206
92
    CHECK(config_object->Delete(context, in_call_key).FromJust());
207
  }
208
209
16825
  Local<Value> node_method = info[1];
210
16825
  CHECK(node_method->IsFunction());
211
  node_method.As<Function>()->Call(context,
212
                                   info.Holder(),
213
16825
                                   call_args.size(),
214
100950
                                   call_args.data()).FromMaybe(Local<Value>());
215
16825
}
216
217
1822
static void* GetAsyncTask(int64_t asyncId) {
218
  // The inspector assumes that when other clients use its asyncTask* API,
219
  // they use real pointers, or at least something aligned like real pointer.
220
  // In general it means that our task_id should always be even.
221
  //
222
  // On 32bit platforms, the 64bit asyncId would get truncated when converted
223
  // to a 32bit pointer. However, the javascript part will never enable
224
  // the async_hook on 32bit platforms, therefore the truncation will never
225
  // happen in practice.
226
1822
  return reinterpret_cast<void*>(asyncId << 1);
227
}
228
229
template<void (Agent::*asyncTaskFn)(void*)>
230
1401
static void InvokeAsyncTaskFnWithId(const FunctionCallbackInfo<Value>& args) {
231
1401
  Environment* env = Environment::GetCurrent(args);
232

2802
  CHECK(args[0]->IsNumber());
233
5604
  int64_t task_id = args[0]->IntegerValue(env->context()).FromJust();
234
1401
  (env->inspector_agent()->*asyncTaskFn)(GetAsyncTask(task_id));
235
1401
}
236
237
421
static void AsyncTaskScheduledWrapper(const FunctionCallbackInfo<Value>& args) {
238
421
  Environment* env = Environment::GetCurrent(args);
239
240
1263
  CHECK(args[0]->IsString());
241
842
  Local<String> task_name = args[0].As<String>();
242
421
  String::Value task_name_value(task_name);
243
421
  StringView task_name_view(*task_name_value, task_name_value.length());
244
245
842
  CHECK(args[1]->IsNumber());
246
1684
  int64_t task_id = args[1]->IntegerValue(env->context()).FromJust();
247
421
  void* task = GetAsyncTask(task_id);
248
249
842
  CHECK(args[2]->IsBoolean());
250
1684
  bool recurring = args[2]->BooleanValue(env->context()).FromJust();
251
252
421
  env->inspector_agent()->AsyncTaskScheduled(task_name_view, task, recurring);
253
421
}
254
255
3200
static void RegisterAsyncHookWrapper(const FunctionCallbackInfo<Value>& args) {
256
3200
  Environment* env = Environment::GetCurrent(args);
257
258
6400
  CHECK(args[0]->IsFunction());
259
6400
  v8::Local<v8::Function> enable_function = args[0].As<Function>();
260
6400
  CHECK(args[1]->IsFunction());
261
6400
  v8::Local<v8::Function> disable_function = args[1].As<Function>();
262
  env->inspector_agent()->RegisterAsyncHook(env->isolate(),
263
3200
    enable_function, disable_function);
264
3200
}
265
266
3202
void IsEnabled(const FunctionCallbackInfo<Value>& args) {
267
3202
  Environment* env = Environment::GetCurrent(args);
268
9606
  args.GetReturnValue().Set(env->inspector_agent()->enabled());
269
3202
}
270
271
3
void Open(const FunctionCallbackInfo<Value>& args) {
272
3
  Environment* env = Environment::GetCurrent(args);
273
3
  Agent* agent = env->inspector_agent();
274
3
  bool wait_for_connect = false;
275
276


12
  if (args.Length() > 0 && args[0]->IsUint32()) {
277
2
    uint32_t port = args[0]->Uint32Value();
278
1
    agent->options().set_port(static_cast<int>(port));
279
  }
280
281


15
  if (args.Length() > 1 && args[1]->IsString()) {
282
    Utf8Value host(env->isolate(), args[1].As<String>());
283
    agent->options().set_host_name(*host);
284
  }
285
286


12
  if (args.Length() > 2 && args[2]->IsBoolean()) {
287
6
    wait_for_connect =  args[2]->BooleanValue();
288
  }
289
290
3
  agent->StartIoThread(wait_for_connect);
291
3
}
292
293
5
void Url(const FunctionCallbackInfo<Value>& args) {
294
5
  Environment* env = Environment::GetCurrent(args);
295
5
  Agent* agent = env->inspector_agent();
296
5
  InspectorIo* io = agent->io();
297
298
7
  if (!io) return;
299
300
3
  std::vector<std::string> ids = io->GetTargetIds();
301
302
3
  if (ids.empty()) return;
303
304
6
  std::string url = FormatWsAddress(io->host(), io->port(), ids[0], true);
305
12
  args.GetReturnValue().Set(OneByteString(env->isolate(), url.c_str()));
306
}
307
308
3200
void InitInspectorBindings(Local<Object> target, Local<Value> unused,
309
                           Local<Context> context, void* priv) {
310
3200
  Environment* env = Environment::GetCurrent(context);
311
  {
312
3200
    auto obj = Object::New(env->isolate());
313
6400
    auto null = Null(env->isolate());
314
6400
    CHECK(obj->SetPrototype(context, null).FromJust());
315
3200
    env->set_inspector_console_api_object(obj);
316
  }
317
318
3200
  Agent* agent = env->inspector_agent();
319
3200
  env->SetMethod(target, "consoleCall", InspectorConsoleCall);
320
3200
  env->SetMethod(target, "addCommandLineAPI", AddCommandLineAPI);
321
3200
  if (agent->IsWaitingForConnect())
322
9
    env->SetMethod(target, "callAndPauseOnStart", CallAndPauseOnStart);
323
3200
  env->SetMethod(target, "open", Open);
324
3200
  env->SetMethod(target, "url", Url);
325
326
3200
  env->SetMethod(target, "asyncTaskScheduled", AsyncTaskScheduledWrapper);
327
  env->SetMethod(target, "asyncTaskCanceled",
328
3200
      InvokeAsyncTaskFnWithId<&Agent::AsyncTaskCanceled>);
329
  env->SetMethod(target, "asyncTaskStarted",
330
3200
      InvokeAsyncTaskFnWithId<&Agent::AsyncTaskStarted>);
331
  env->SetMethod(target, "asyncTaskFinished",
332
3200
      InvokeAsyncTaskFnWithId<&Agent::AsyncTaskFinished>);
333
334
3200
  env->SetMethod(target, "registerAsyncHook", RegisterAsyncHookWrapper);
335
3200
  env->SetMethod(target, "isEnabled", IsEnabled);
336
337
3200
  auto conn_str = FIXED_ONE_BYTE_STRING(env->isolate(), "Connection");
338
  Local<FunctionTemplate> tmpl =
339
3200
      env->NewFunctionTemplate(JSBindingsConnection::New);
340
6400
  tmpl->InstanceTemplate()->SetInternalFieldCount(1);
341
3200
  tmpl->SetClassName(conn_str);
342
3200
  AsyncWrap::AddWrapMethods(env, tmpl);
343
3200
  env->SetProtoMethod(tmpl, "dispatch", JSBindingsConnection::Dispatch);
344
3200
  env->SetProtoMethod(tmpl, "disconnect", JSBindingsConnection::Disconnect);
345
12800
  target->Set(env->context(), conn_str, tmpl->GetFunction()).ToChecked();
346
3200
}
347
348
}  // namespace
349
}  // namespace inspector
350
}  // namespace node
351
352
3245
NODE_MODULE_CONTEXT_AWARE_BUILTIN(inspector,
353
                                  node::inspector::InitInspectorBindings);