GCC Code Coverage Report
Directory: ../src/ Exec Total Coverage
File: /home/node-core-coverage/node-core-coverage/workdir/node/out/../src/async-wrap.cc Lines: 169 178 94.9 %
Date: 2016-12-16 Branches: 86 108 79.6 %

Line Branch Exec Source
1
#include "async-wrap.h"
2
#include "async-wrap-inl.h"
3
#include "env.h"
4
#include "env-inl.h"
5
#include "util.h"
6
#include "util-inl.h"
7
8
#include "uv.h"
9
#include "v8.h"
10
#include "v8-profiler.h"
11
12
using v8::Boolean;
13
using v8::Context;
14
using v8::Function;
15
using v8::FunctionCallbackInfo;
16
using v8::HandleScope;
17
using v8::HeapProfiler;
18
using v8::Int32;
19
using v8::Integer;
20
using v8::Isolate;
21
using v8::Local;
22
using v8::MaybeLocal;
23
using v8::Number;
24
using v8::Object;
25
using v8::RetainedObjectInfo;
26
using v8::TryCatch;
27
using v8::Value;
28
29
namespace node {
30
31
static const char* const provider_names[] = {
32
#define V(PROVIDER)                                                           \
33
  #PROVIDER,
34
  NODE_ASYNC_PROVIDER_TYPES(V)
35
#undef V
36
};
37
38
39
2
class RetainedAsyncInfo: public RetainedObjectInfo {
40
 public:
41
  explicit RetainedAsyncInfo(uint16_t class_id, AsyncWrap* wrap);
42
43
  void Dispose() override;
44
  bool IsEquivalent(RetainedObjectInfo* other) override;
45
  intptr_t GetHash() override;
46
  const char* GetLabel() override;
47
  intptr_t GetSizeInBytes() override;
48
49
 private:
50
  const char* label_;
51
  const AsyncWrap* wrap_;
52
  const int length_;
53
};
54
55
56
RetainedAsyncInfo::RetainedAsyncInfo(uint16_t class_id, AsyncWrap* wrap)
57
1
    : label_(provider_names[class_id - NODE_ASYNC_ID_OFFSET]),
58
      wrap_(wrap),
59
3
      length_(wrap->self_size()) {
60
}
61
62
63
1
void RetainedAsyncInfo::Dispose() {
64
1
  delete this;
65
1
}
66
67
68
bool RetainedAsyncInfo::IsEquivalent(RetainedObjectInfo* other) {
69
  return label_ == other->GetLabel() &&
70
          wrap_ == static_cast<RetainedAsyncInfo*>(other)->wrap_;
71
}
72
73
74
2
intptr_t RetainedAsyncInfo::GetHash() {
75
2
  return reinterpret_cast<intptr_t>(wrap_);
76
}
77
78
79
3
const char* RetainedAsyncInfo::GetLabel() {
80
3
  return label_;
81
}
82
83
84
1
intptr_t RetainedAsyncInfo::GetSizeInBytes() {
85
1
  return length_;
86
}
87
88
89
1
RetainedObjectInfo* WrapperInfo(uint16_t class_id, Local<Value> wrapper) {
90
  // No class_id should be the provider type of NONE.
91
1
  CHECK_NE(NODE_ASYNC_ID_OFFSET, class_id);
92
1
  CHECK(wrapper->IsObject());
93
1
  CHECK(!wrapper.IsEmpty());
94
95
1
  Local<Object> object = wrapper.As<Object>();
96
1
  CHECK_GT(object->InternalFieldCount(), 0);
97
98
1
  AsyncWrap* wrap = Unwrap<AsyncWrap>(object);
99
1
  CHECK_NE(nullptr, wrap);
100
101
2
  return new RetainedAsyncInfo(class_id, wrap);
102
}
103
104
105
// end RetainedAsyncInfo
106
107
108
12
static void EnableHooksJS(const FunctionCallbackInfo<Value>& args) {
109
12
  Environment* env = Environment::GetCurrent(args);
110
12
  Local<Function> init_fn = env->async_hooks_init_function();
111

12
  if (init_fn.IsEmpty() || !init_fn->IsFunction())
112
1
    return env->ThrowTypeError("init callback is not assigned to a function");
113
22
  env->async_hooks()->set_enable_callbacks(1);
114
}
115
116
117
2
static void DisableHooksJS(const FunctionCallbackInfo<Value>& args) {
118
2
  Environment* env = Environment::GetCurrent(args);
119
4
  env->async_hooks()->set_enable_callbacks(0);
120
2
}
121
122
123
14
static void SetupHooks(const FunctionCallbackInfo<Value>& args) {
124
14
  Environment* env = Environment::GetCurrent(args);
125
126
28
  if (env->async_hooks()->callbacks_enabled())
127
    return env->ThrowError("hooks should not be set while also enabled");
128
13
  if (!args[0]->IsObject())
129
    return env->ThrowTypeError("first argument must be an object");
130
131
24
  Local<Object> fn_obj = args[0].As<Object>();
132
133
12
  Local<Value> init_v = fn_obj->Get(
134
      env->context(),
135
48
      FIXED_ONE_BYTE_STRING(env->isolate(), "init")).ToLocalChecked();
136
12
  Local<Value> pre_v = fn_obj->Get(
137
      env->context(),
138
48
      FIXED_ONE_BYTE_STRING(env->isolate(), "pre")).ToLocalChecked();
139
12
  Local<Value> post_v = fn_obj->Get(
140
      env->context(),
141
48
      FIXED_ONE_BYTE_STRING(env->isolate(), "post")).ToLocalChecked();
142
12
  Local<Value> destroy_v = fn_obj->Get(
143
      env->context(),
144
48
      FIXED_ONE_BYTE_STRING(env->isolate(), "destroy")).ToLocalChecked();
145
146
12
  if (!init_v->IsFunction())
147
    return env->ThrowTypeError("init callback must be a function");
148
149
11
  env->set_async_hooks_init_function(init_v.As<Function>());
150
151
11
  if (pre_v->IsFunction())
152
5
    env->set_async_hooks_pre_function(pre_v.As<Function>());
153
11
  if (post_v->IsFunction())
154
6
    env->set_async_hooks_post_function(post_v.As<Function>());
155
11
  if (destroy_v->IsFunction())
156
4
    env->set_async_hooks_destroy_function(destroy_v.As<Function>());
157
}
158
159
160
12
void AsyncWrap::Initialize(Local<Object> target,
161
                           Local<Value> unused,
162
                           Local<Context> context) {
163
12
  Environment* env = Environment::GetCurrent(context);
164
12
  Isolate* isolate = env->isolate();
165
24
  HandleScope scope(isolate);
166
167
12
  env->SetMethod(target, "setupHooks", SetupHooks);
168
12
  env->SetMethod(target, "disable", DisableHooksJS);
169
12
  env->SetMethod(target, "enable", EnableHooksJS);
170
171
12
  Local<Object> async_providers = Object::New(isolate);
172
#define V(PROVIDER)                                                           \
173
  async_providers->Set(FIXED_ONE_BYTE_STRING(isolate, #PROVIDER),             \
174
      Integer::New(isolate, AsyncWrap::PROVIDER_ ## PROVIDER));
175
588
  NODE_ASYNC_PROVIDER_TYPES(V)
176
#undef V
177
36
  target->Set(FIXED_ONE_BYTE_STRING(isolate, "Providers"), async_providers);
178
179
12
  env->set_async_hooks_init_function(Local<Function>());
180
12
  env->set_async_hooks_pre_function(Local<Function>());
181
12
  env->set_async_hooks_post_function(Local<Function>());
182
12
  env->set_async_hooks_destroy_function(Local<Function>());
183
12
}
184
185
186
32
void AsyncWrap::DestroyIdsCb(uv_idle_t* handle) {
187
32
  uv_idle_stop(handle);
188
189
32
  Environment* env = Environment::from_destroy_ids_idle_handle(handle);
190
  // None of the V8 calls done outside the HandleScope leak a handle. If this
191
  // changes in the future then the SealHandleScope wrapping the uv_run()
192
  // will catch this can cause the process to abort.
193
32
  HandleScope handle_scope(env->isolate());
194
64
  Context::Scope context_scope(env->context());
195
32
  Local<Function> fn = env->async_hooks_destroy_function();
196
197
32
  if (fn.IsEmpty())
198
93
    return env->destroy_ids_list()->clear();
199
200
1
  TryCatch try_catch(env->isolate());
201
202
4
  for (auto current_id : *env->destroy_ids_list()) {
203
    // Want each callback to be cleaned up after itself, instead of cleaning
204
    // them all up after the while() loop completes.
205
1
    HandleScope scope(env->isolate());
206
2
    Local<Value> argv = Number::New(env->isolate(), current_id);
207
    MaybeLocal<Value> ret = fn->Call(
208
4
        env->context(), Undefined(env->isolate()), 1, &argv);
209
210
1
    if (ret.IsEmpty()) {
211
1
      ClearFatalExceptionHandlers(env);
212
1
      FatalException(env->isolate(), try_catch);
213
    }
214
  }
215
}
216
217
218
1722
void LoadAsyncWrapperInfo(Environment* env) {
219
1722
  HeapProfiler* heap_profiler = env->isolate()->GetHeapProfiler();
220
#define V(PROVIDER)                                                           \
221
  heap_profiler->SetWrapperClassInfoProvider(                                 \
222
      (NODE_ASYNC_ID_OFFSET + AsyncWrap::PROVIDER_ ## PROVIDER), WrapperInfo);
223
1722
  NODE_ASYNC_PROVIDER_TYPES(V)
224
#undef V
225
1722
}
226
227
228
49332
AsyncWrap::AsyncWrap(Environment* env,
229
                     Local<Object> object,
230
                     ProviderType provider,
231
                     AsyncWrap* parent)
232
49332
    : BaseObject(env, object), bits_(static_cast<uint32_t>(provider) << 1),
233
147996
      uid_(env->get_async_wrap_uid()) {
234
49332
  CHECK_NE(provider, PROVIDER_NONE);
235
49332
  CHECK_GE(object->InternalFieldCount(), 1);
236
237
  // Shift provider value over to prevent id collision.
238
98664
  persistent().SetWrapperClassId(NODE_ASYNC_ID_OFFSET + provider);
239
240
49332
  Local<Function> init_fn = env->async_hooks_init_function();
241
242
  // No init callback exists, no reason to go on.
243
49332
  if (init_fn.IsEmpty())
244
49264
    return;
245
246
  // If async wrap callbacks are disabled and no parent was passed that has
247
  // run the init callback then return.
248

144
  if (!env->async_wrap_callbacks_enabled() &&
249
2
      (parent == nullptr || !parent->ran_init_callback()))
250
    return;
251
252
135
  HandleScope scope(env->isolate());
253
254
  Local<Value> argv[] = {
255
68
    Number::New(env->isolate(), get_uid()),
256
    Int32::New(env->isolate(), provider),
257
    Null(env->isolate()),
258
    Null(env->isolate())
259
476
  };
260
261
68
  if (parent != nullptr) {
262
12
    argv[2] = Number::New(env->isolate(), parent->get_uid());
263
18
    argv[3] = parent->object();
264
  }
265
266
135
  TryCatch try_catch(env->isolate());
267
268
  MaybeLocal<Value> ret =
269
204
      init_fn->Call(env->context(), object, arraysize(argv), argv);
270
271
68
  if (ret.IsEmpty()) {
272
1
    ClearFatalExceptionHandlers(env);
273
1
    FatalException(env->isolate(), try_catch);
274
  }
275
276
67
  bits_ |= 1;  // ran_init_callback() is true now.
277
}
278
279
280
90086
AsyncWrap::~AsyncWrap() {
281
90034
  if (!ran_init_callback())
282
    return;
283
284
104
  if (env()->destroy_ids_list()->empty())
285
64
    uv_idle_start(env()->destroy_ids_idle_handle(), DestroyIdsCb);
286
287
156
  env()->destroy_ids_list()->push_back(get_uid());
288
45017
}
289
290
291
106747
Local<Value> AsyncWrap::MakeCallback(const Local<Function> cb,
292
                                     int argc,
293
                                     Local<Value>* argv) {
294
320241
  CHECK(env()->context() == env()->isolate()->GetCurrentContext());
295
296
213494
  Local<Function> pre_fn = env()->async_hooks_pre_function();
297
213494
  Local<Function> post_fn = env()->async_hooks_post_function();
298
213494
  Local<Value> uid = Number::New(env()->isolate(), get_uid());
299
213494
  Local<Object> context = object();
300
106747
  Local<Object> domain;
301
106747
  bool has_domain = false;
302
303
320144
  Environment::AsyncCallbackScope callback_scope(env());
304
305
106747
  if (env()->using_domains()) {
306
3162
    Local<Value> domain_v = context->Get(env()->domain_string());
307
1054
    has_domain = domain_v->IsObject();
308
1054
    if (has_domain) {
309
44
      domain = domain_v.As<Object>();
310
176
      if (domain->Get(env()->disposed_string())->IsTrue())
311
        return Local<Value>();
312
    }
313
  }
314
315
106747
  if (has_domain) {
316
132
    Local<Value> enter_v = domain->Get(env()->enter_string());
317
44
    if (enter_v->IsFunction()) {
318
132
      if (enter_v.As<Function>()->Call(domain, 0, nullptr).IsEmpty()) {
319
        FatalError("node::AsyncWrap::MakeCallback",
320
                   "domain enter callback threw, please report this");
321
      }
322
    }
323
  }
324
325

213494
  if (ran_init_callback() && !pre_fn.IsEmpty()) {
326
9
    TryCatch try_catch(env()->isolate());
327
10
    MaybeLocal<Value> ar = pre_fn->Call(env()->context(), context, 1, &uid);
328
5
    if (ar.IsEmpty()) {
329
1
      ClearFatalExceptionHandlers(env());
330
1
      FatalException(env()->isolate(), try_catch);
331
      return Local<Value>();
332
    }
333
  }
334
335
213492
  Local<Value> ret = cb->Call(context, argc, argv);
336
337

213406
  if (ran_init_callback() && !post_fn.IsEmpty()) {
338
15
    Local<Value> did_throw = Boolean::New(env()->isolate(), ret.IsEmpty());
339
5
    Local<Value> vals[] = { uid, did_throw };
340
9
    TryCatch try_catch(env()->isolate());
341
    MaybeLocal<Value> ar =
342
15
        post_fn->Call(env()->context(), context, arraysize(vals), vals);
343
5
    if (ar.IsEmpty()) {
344
1
      ClearFatalExceptionHandlers(env());
345
1
      FatalException(env()->isolate(), try_catch);
346
      return Local<Value>();
347
    }
348
  }
349
350
106702
  if (ret.IsEmpty()) {
351
46
    return ret;
352
  }
353
354
106656
  if (has_domain) {
355
81
    Local<Value> exit_v = domain->Get(env()->exit_string());
356
27
    if (exit_v->IsFunction()) {
357
81
      if (exit_v.As<Function>()->Call(domain, 0, nullptr).IsEmpty()) {
358
        FatalError("node::AsyncWrap::MakeCallback",
359
                   "domain exit callback threw, please report this");
360
      }
361
    }
362
  }
363
364
106656
  if (callback_scope.in_makecallback()) {
365
33445
    return ret;
366
  }
367
368
146422
  Environment::TickInfo* tick_info = env()->tick_info();
369
370
73211
  if (tick_info->length() == 0) {
371
43779
    env()->isolate()->RunMicrotasks();
372
  }
373
374
146422
  Local<Object> process = env()->process_object();
375
376
73211
  if (tick_info->length() == 0) {
377
43772
    tick_info->set_index(0);
378
43772
    return ret;
379
  }
380
381
88265
  if (env()->tick_callback_function()->Call(process, 0, nullptr).IsEmpty()) {
382
20
    return Local<Value>();
383
  }
384
385
29367
  return ret;
386
}
387
388
}  // namespace node
389
390
1729
NODE_MODULE_CONTEXT_AWARE_BUILTIN(async_wrap, node::AsyncWrap::Initialize)