Node.js  v8.x
Node.js is a JavaScript runtime built on Chrome's V8 JavaScript engine
async-wrap.cc
Go to the documentation of this file.
1 // Copyright Joyent, Inc. and other Node contributors.
2 //
3 // Permission is hereby granted, free of charge, to any person obtaining a
4 // copy of this software and associated documentation files (the
5 // "Software"), to deal in the Software without restriction, including
6 // without limitation the rights to use, copy, modify, merge, publish,
7 // distribute, sublicense, and/or sell copies of the Software, and to permit
8 // persons to whom the Software is furnished to do so, subject to the
9 // following conditions:
10 //
11 // The above copyright notice and this permission notice shall be included
12 // in all copies or substantial portions of the Software.
13 //
14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15 // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
17 // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
18 // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
19 // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
20 // USE OR OTHER DEALINGS IN THE SOFTWARE.
21 
22 #include "async-wrap.h"
23 #include "async-wrap-inl.h"
24 #include "env.h"
25 #include "env-inl.h"
26 #include "util.h"
27 #include "util-inl.h"
28 
29 #include "uv.h"
30 #include "v8.h"
31 #include "v8-profiler.h"
32 
33 using v8::Array;
34 using v8::ArrayBuffer;
35 using v8::Context;
36 using v8::Float64Array;
37 using v8::Function;
38 using v8::FunctionCallbackInfo;
39 using v8::FunctionTemplate;
40 using v8::HandleScope;
41 using v8::HeapProfiler;
42 using v8::Integer;
43 using v8::Isolate;
44 using v8::Local;
45 using v8::MaybeLocal;
46 using v8::Number;
47 using v8::Object;
48 using v8::ObjectTemplate;
49 using v8::Promise;
50 using v8::PromiseHookType;
51 using v8::PropertyCallbackInfo;
52 using v8::RetainedObjectInfo;
53 using v8::String;
54 using v8::Symbol;
55 using v8::TryCatch;
56 using v8::Uint32Array;
57 using v8::Undefined;
58 using v8::Value;
59 
61 
62 namespace node {
63 
64 static const char* const provider_names[] = {
65 #define V(PROVIDER) \
66  #PROVIDER,
67  NODE_ASYNC_PROVIDER_TYPES(V)
68 #undef V
69 };
70 
71 
72 // Report correct information in a heapdump.
73 
74 class RetainedAsyncInfo: public RetainedObjectInfo {
75  public:
76  explicit RetainedAsyncInfo(uint16_t class_id, AsyncWrap* wrap);
77 
78  void Dispose() override;
79  bool IsEquivalent(RetainedObjectInfo* other) override;
80  intptr_t GetHash() override;
81  const char* GetLabel() override;
82  intptr_t GetSizeInBytes() override;
83 
84  private:
85  const char* label_;
86  const AsyncWrap* wrap_;
87  const int length_;
88 };
89 
90 
91 RetainedAsyncInfo::RetainedAsyncInfo(uint16_t class_id, AsyncWrap* wrap)
92  : label_(provider_names[class_id - NODE_ASYNC_ID_OFFSET]),
93  wrap_(wrap),
94  length_(wrap->self_size()) {
95 }
96 
97 
99  delete this;
100 }
101 
102 
103 bool RetainedAsyncInfo::IsEquivalent(RetainedObjectInfo* other) {
104  return label_ == other->GetLabel() &&
105  wrap_ == static_cast<RetainedAsyncInfo*>(other)->wrap_;
106 }
107 
108 
110  return reinterpret_cast<intptr_t>(wrap_);
111 }
112 
113 
115  return label_;
116 }
117 
118 
120  return length_;
121 }
122 
123 
124 RetainedObjectInfo* WrapperInfo(uint16_t class_id, Local<Value> wrapper) {
125  // No class_id should be the provider type of NONE.
126  CHECK_GT(class_id, NODE_ASYNC_ID_OFFSET);
127  // And make sure the class_id doesn't extend past the last provider.
128  CHECK_LE(class_id - NODE_ASYNC_ID_OFFSET, AsyncWrap::PROVIDERS_LENGTH);
129  CHECK(wrapper->IsObject());
130  CHECK(!wrapper.IsEmpty());
131 
132  Local<Object> object = wrapper.As<Object>();
133  CHECK_GT(object->InternalFieldCount(), 0);
134 
135  AsyncWrap* wrap = Unwrap<AsyncWrap>(object);
136  CHECK_NE(nullptr, wrap);
137 
138  return new RetainedAsyncInfo(class_id, wrap);
139 }
140 
141 
142 // end RetainedAsyncInfo
143 
144 
145 static void DestroyIdsCb(uv_timer_t* handle) {
146  Environment* env = Environment::from_destroy_ids_timer_handle(handle);
147 
148  HandleScope handle_scope(env->isolate());
149  Context::Scope context_scope(env->context());
150  Local<Function> fn = env->async_hooks_destroy_function();
151 
152  TryCatch try_catch(env->isolate());
153 
154  do {
155  std::vector<double> destroy_ids_list;
156  destroy_ids_list.swap(*env->destroy_ids_list());
157  for (auto current_id : destroy_ids_list) {
158  // Want each callback to be cleaned up after itself, instead of cleaning
159  // them all up after the while() loop completes.
160  HandleScope scope(env->isolate());
161  Local<Value> argv = Number::New(env->isolate(), current_id);
162  MaybeLocal<Value> ret = fn->Call(
163  env->context(), Undefined(env->isolate()), 1, &argv);
164 
165  if (ret.IsEmpty()) {
167  FatalException(env->isolate(), try_catch);
168  UNREACHABLE();
169  }
170  }
171  } while (!env->destroy_ids_list()->empty());
172 }
173 
174 
175 static void PushBackDestroyId(Environment* env, double id) {
176  if (env->async_hooks()->fields()[AsyncHooks::kDestroy] == 0)
177  return;
178 
179  if (env->destroy_ids_list()->empty())
180  uv_timer_start(env->destroy_ids_timer_handle(), DestroyIdsCb, 0, 0);
181 
182  env->destroy_ids_list()->push_back(id);
183 }
184 
185 
186 bool DomainEnter(Environment* env, Local<Object> object) {
187  Local<Value> domain_v = object->Get(env->domain_string());
188  if (domain_v->IsObject()) {
189  Local<Object> domain = domain_v.As<Object>();
190  if (domain->Get(env->disposed_string())->IsTrue())
191  return true;
192  Local<Value> enter_v = domain->Get(env->enter_string());
193  if (enter_v->IsFunction()) {
194  if (enter_v.As<Function>()->Call(domain, 0, nullptr).IsEmpty()) {
195  FatalError("node::AsyncWrap::MakeCallback",
196  "domain enter callback threw, please report this");
197  }
198  }
199  }
200  return false;
201 }
202 
203 
204 bool DomainExit(Environment* env, v8::Local<v8::Object> object) {
205  Local<Value> domain_v = object->Get(env->domain_string());
206  if (domain_v->IsObject()) {
207  Local<Object> domain = domain_v.As<Object>();
208  if (domain->Get(env->disposed_string())->IsTrue())
209  return true;
210  Local<Value> exit_v = domain->Get(env->exit_string());
211  if (exit_v->IsFunction()) {
212  if (exit_v.As<Function>()->Call(domain, 0, nullptr).IsEmpty()) {
213  FatalError("node::AsyncWrap::MakeCallback",
214  "domain exit callback threw, please report this");
215  }
216  }
217  }
218  return false;
219 }
220 
221 
222 void AsyncWrap::EmitBefore(Environment* env, double async_id) {
223  AsyncHooks* async_hooks = env->async_hooks();
224 
225  if (async_hooks->fields()[AsyncHooks::kBefore] == 0)
226  return;
227 
228  Local<Value> uid = Number::New(env->isolate(), async_id);
229  Local<Function> fn = env->async_hooks_before_function();
230  TryCatch try_catch(env->isolate());
231  MaybeLocal<Value> ar = fn->Call(
232  env->context(), Undefined(env->isolate()), 1, &uid);
233  if (ar.IsEmpty()) {
235  FatalException(env->isolate(), try_catch);
236  UNREACHABLE();
237  }
238 }
239 
240 
241 void AsyncWrap::EmitAfter(Environment* env, double async_id) {
242  AsyncHooks* async_hooks = env->async_hooks();
243 
244  if (async_hooks->fields()[AsyncHooks::kAfter] == 0)
245  return;
246 
247  // If the user's callback failed then the after() hooks will be called at the
248  // end of _fatalException().
249  Local<Value> uid = Number::New(env->isolate(), async_id);
250  Local<Function> fn = env->async_hooks_after_function();
251  TryCatch try_catch(env->isolate());
252  MaybeLocal<Value> ar = fn->Call(
253  env->context(), Undefined(env->isolate()), 1, &uid);
254  if (ar.IsEmpty()) {
256  FatalException(env->isolate(), try_catch);
257  UNREACHABLE();
258  }
259 }
260 
261 class PromiseWrap : public AsyncWrap {
262  public:
263  PromiseWrap(Environment* env, Local<Object> object, bool silent)
264  : AsyncWrap(env, object, PROVIDER_PROMISE, silent) {
265  MakeWeak(this);
266  }
267  size_t self_size() const override { return sizeof(*this); }
268 
269  static constexpr int kPromiseField = 1;
270  static constexpr int kParentIdField = 2;
271  static constexpr int kInternalFieldCount = 3;
272 
273  static PromiseWrap* New(Environment* env,
274  Local<Promise> promise,
275  PromiseWrap* parent_wrap,
276  bool silent);
277  static void GetPromise(Local<String> property,
278  const PropertyCallbackInfo<Value>& info);
279  static void GetParentId(Local<String> property,
280  const PropertyCallbackInfo<Value>& info);
281 };
282 
283 PromiseWrap* PromiseWrap::New(Environment* env,
284  Local<Promise> promise,
285  PromiseWrap* parent_wrap,
286  bool silent) {
287  Local<Object> object = env->promise_wrap_template()
288  ->NewInstance(env->context()).ToLocalChecked();
289  object->SetInternalField(PromiseWrap::kPromiseField, promise);
290  if (parent_wrap != nullptr) {
291  object->SetInternalField(PromiseWrap::kParentIdField,
292  Number::New(env->isolate(),
293  parent_wrap->get_id()));
294  }
295  CHECK_EQ(promise->GetAlignedPointerFromInternalField(0), nullptr);
296  promise->SetInternalField(0, object);
297  return new PromiseWrap(env, object, silent);
298 }
299 
300 void PromiseWrap::GetPromise(Local<String> property,
301  const PropertyCallbackInfo<Value>& info) {
302  info.GetReturnValue().Set(info.Holder()->GetInternalField(kPromiseField));
303 }
304 
305 void PromiseWrap::GetParentId(Local<String> property,
306  const PropertyCallbackInfo<Value>& info) {
307  info.GetReturnValue().Set(info.Holder()->GetInternalField(kParentIdField));
308 }
309 
310 static void PromiseHook(PromiseHookType type, Local<Promise> promise,
311  Local<Value> parent, void* arg) {
312  Environment* env = static_cast<Environment*>(arg);
313  Local<Value> resource_object_value = promise->GetInternalField(0);
314  PromiseWrap* wrap = nullptr;
315  if (resource_object_value->IsObject()) {
316  Local<Object> resource_object = resource_object_value.As<Object>();
317  wrap = Unwrap<PromiseWrap>(resource_object);
318  }
319 
320  if (type == PromiseHookType::kInit || wrap == nullptr) {
321  bool silent = type != PromiseHookType::kInit;
322  PromiseWrap* parent_wrap = nullptr;
323 
324  // set parent promise's async Id as this promise's triggerAsyncId
325  if (parent->IsPromise()) {
326  // parent promise exists, current promise
327  // is a chained promise, so we set parent promise's id as
328  // current promise's triggerAsyncId
329  Local<Promise> parent_promise = parent.As<Promise>();
330  Local<Value> parent_resource = parent_promise->GetInternalField(0);
331  if (parent_resource->IsObject()) {
332  parent_wrap = Unwrap<PromiseWrap>(parent_resource.As<Object>());
333  }
334 
335  if (parent_wrap == nullptr) {
336  parent_wrap = PromiseWrap::New(env, parent_promise, nullptr, true);
337  }
338  // get id from parentWrap
339  double trigger_id = parent_wrap->get_id();
340  env->set_init_trigger_id(trigger_id);
341  }
342 
343  wrap = PromiseWrap::New(env, promise, parent_wrap, silent);
344  } else if (type == PromiseHookType::kResolve) {
345  // TODO(matthewloring): need to expose this through the async hooks api.
346  }
347 
348  CHECK_NE(wrap, nullptr);
349  if (type == PromiseHookType::kBefore) {
350  env->async_hooks()->push_ids(wrap->get_id(), wrap->get_trigger_id());
351  AsyncWrap::EmitBefore(wrap->env(), wrap->get_id());
352  } else if (type == PromiseHookType::kAfter) {
353  AsyncWrap::EmitAfter(wrap->env(), wrap->get_id());
354  if (env->current_async_id() == wrap->get_id()) {
355  // This condition might not be true if async_hooks was enabled during
356  // the promise callback execution.
357  // Popping it off the stack can be skipped in that case, because is is
358  // known that it would correspond to exactly one call with
359  // PromiseHookType::kBefore that was not witnessed by the PromiseHook.
360  env->async_hooks()->pop_ids(wrap->get_id());
361  }
362  }
363 }
364 
365 
366 static void SetupHooks(const FunctionCallbackInfo<Value>& args) {
367  Environment* env = Environment::GetCurrent(args);
368 
369  if (!args[0]->IsObject())
370  return env->ThrowTypeError("first argument must be an object");
371 
372  // All of init, before, after, destroy are supplied by async_hooks
373  // internally, so this should every only be called once. At which time all
374  // the functions should be set. Detect this by checking if init !IsEmpty().
375  CHECK(env->async_hooks_init_function().IsEmpty());
376 
377  Local<Object> fn_obj = args[0].As<Object>();
378 
379 #define SET_HOOK_FN(name) \
380  Local<Value> name##_v = fn_obj->Get( \
381  env->context(), \
382  FIXED_ONE_BYTE_STRING(env->isolate(), #name)).ToLocalChecked(); \
383  CHECK(name##_v->IsFunction()); \
384  env->set_async_hooks_##name##_function(name##_v.As<Function>());
385 
386  SET_HOOK_FN(init);
387  SET_HOOK_FN(before);
388  SET_HOOK_FN(after);
389  SET_HOOK_FN(destroy);
390 #undef SET_HOOK_FN
391 
392  {
393  Local<FunctionTemplate> ctor =
394  FunctionTemplate::New(env->isolate());
395  ctor->SetClassName(FIXED_ONE_BYTE_STRING(env->isolate(), "PromiseWrap"));
396  Local<ObjectTemplate> promise_wrap_template = ctor->InstanceTemplate();
397  promise_wrap_template->SetInternalFieldCount(
399  promise_wrap_template->SetAccessor(
400  FIXED_ONE_BYTE_STRING(env->isolate(), "promise"),
402  promise_wrap_template->SetAccessor(
403  FIXED_ONE_BYTE_STRING(env->isolate(), "parentId"),
405  env->set_promise_wrap_template(promise_wrap_template);
406  }
407 }
408 
409 
410 static void EnablePromiseHook(const FunctionCallbackInfo<Value>& args) {
411  Environment* env = Environment::GetCurrent(args);
412  env->AddPromiseHook(PromiseHook, static_cast<void*>(env));
413 }
414 
415 
416 static void DisablePromiseHook(const FunctionCallbackInfo<Value>& args) {
417  Environment* env = Environment::GetCurrent(args);
418 
419  // Delay the call to `RemovePromiseHook` because we might currently be
420  // between the `before` and `after` calls of a Promise.
421  env->isolate()->EnqueueMicrotask([](void* data) {
422  Environment* env = static_cast<Environment*>(data);
423  env->RemovePromiseHook(PromiseHook, data);
424  }, static_cast<void*>(env));
425 }
426 
427 
428 void AsyncWrap::GetAsyncId(const FunctionCallbackInfo<Value>& args) {
429  AsyncWrap* wrap;
430  args.GetReturnValue().Set(-1);
431  ASSIGN_OR_RETURN_UNWRAP(&wrap, args.Holder());
432  args.GetReturnValue().Set(wrap->get_id());
433 }
434 
435 
436 void AsyncWrap::PushAsyncIds(const FunctionCallbackInfo<Value>& args) {
437  Environment* env = Environment::GetCurrent(args);
438  // No need for CHECK(IsNumber()) on args because if FromJust() doesn't fail
439  // then the checks in push_ids() and pop_ids() will.
440  double async_id = args[0]->NumberValue(env->context()).FromJust();
441  double trigger_id = args[1]->NumberValue(env->context()).FromJust();
442  env->async_hooks()->push_ids(async_id, trigger_id);
443 }
444 
445 
446 void AsyncWrap::PopAsyncIds(const FunctionCallbackInfo<Value>& args) {
447  Environment* env = Environment::GetCurrent(args);
448  double async_id = args[0]->NumberValue(env->context()).FromJust();
449  args.GetReturnValue().Set(env->async_hooks()->pop_ids(async_id));
450 }
451 
452 
453 void AsyncWrap::AsyncIdStackSize(const FunctionCallbackInfo<Value>& args) {
454  Environment* env = Environment::GetCurrent(args);
455  args.GetReturnValue().Set(
456  static_cast<double>(env->async_hooks()->stack_size()));
457 }
458 
459 
460 void AsyncWrap::ClearIdStack(const FunctionCallbackInfo<Value>& args) {
461  Environment* env = Environment::GetCurrent(args);
462  env->async_hooks()->clear_id_stack();
463 }
464 
465 
466 void AsyncWrap::AsyncReset(const FunctionCallbackInfo<Value>& args) {
467  AsyncWrap* wrap;
468  ASSIGN_OR_RETURN_UNWRAP(&wrap, args.Holder());
469  wrap->AsyncReset();
470 }
471 
472 
473 void AsyncWrap::QueueDestroyId(const FunctionCallbackInfo<Value>& args) {
474  CHECK(args[0]->IsNumber());
475  PushBackDestroyId(Environment::GetCurrent(args), args[0]->NumberValue());
476 }
477 
478 void AsyncWrap::AddWrapMethods(Environment* env,
479  Local<FunctionTemplate> constructor,
480  int flag) {
481  env->SetProtoMethod(constructor, "getAsyncId", AsyncWrap::GetAsyncId);
482  if (flag & kFlagHasReset)
483  env->SetProtoMethod(constructor, "asyncReset", AsyncWrap::AsyncReset);
484 }
485 
486 void AsyncWrap::Initialize(Local<Object> target,
487  Local<Value> unused,
488  Local<Context> context) {
489  Environment* env = Environment::GetCurrent(context);
490  Isolate* isolate = env->isolate();
491  HandleScope scope(isolate);
492 
493  env->SetMethod(target, "setupHooks", SetupHooks);
494  env->SetMethod(target, "pushAsyncIds", PushAsyncIds);
495  env->SetMethod(target, "popAsyncIds", PopAsyncIds);
496  env->SetMethod(target, "asyncIdStackSize", AsyncIdStackSize);
497  env->SetMethod(target, "clearIdStack", ClearIdStack);
498  env->SetMethod(target, "addIdToDestroyList", QueueDestroyId);
499  env->SetMethod(target, "enablePromiseHook", EnablePromiseHook);
500  env->SetMethod(target, "disablePromiseHook", DisablePromiseHook);
501 
502  v8::PropertyAttribute ReadOnlyDontDelete =
503  static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontDelete);
504 
505 #define FORCE_SET_TARGET_FIELD(obj, str, field) \
506  (obj)->DefineOwnProperty(context, \
507  FIXED_ONE_BYTE_STRING(isolate, str), \
508  field, \
509  ReadOnlyDontDelete).FromJust()
510 
511  // Attach the uint32_t[] where each slot contains the count of the number of
512  // callbacks waiting to be called on a particular event. It can then be
513  // incremented/decremented from JS quickly to communicate to C++ if there are
514  // any callbacks waiting to be called.
515  uint32_t* fields_ptr = env->async_hooks()->fields();
516  int fields_count = env->async_hooks()->fields_count();
517  Local<ArrayBuffer> fields_ab =
518  ArrayBuffer::New(isolate, fields_ptr, fields_count * sizeof(*fields_ptr));
519  FORCE_SET_TARGET_FIELD(target,
520  "async_hook_fields",
521  Uint32Array::New(fields_ab, 0, fields_count));
522 
523  // The following v8::Float64Array has 5 fields. These fields are shared in
524  // this way to allow JS and C++ to read/write each value as quickly as
525  // possible. The fields are represented as follows:
526  //
527  // kAsyncUid: Maintains the state of the next unique id to be assigned.
528  //
529  // kInitTriggerId: Write the id of the resource responsible for a handle's
530  // creation just before calling the new handle's constructor. After the new
531  // handle is constructed kInitTriggerId is set back to 0.
532  double* uid_fields_ptr = env->async_hooks()->uid_fields();
533  int uid_fields_count = env->async_hooks()->uid_fields_count();
534  Local<ArrayBuffer> uid_fields_ab = ArrayBuffer::New(
535  isolate,
536  uid_fields_ptr,
537  uid_fields_count * sizeof(*uid_fields_ptr));
538  FORCE_SET_TARGET_FIELD(target,
539  "async_uid_fields",
540  Float64Array::New(uid_fields_ab, 0, uid_fields_count));
541 
542  Local<Object> constants = Object::New(isolate);
543 #define SET_HOOKS_CONSTANT(name) \
544  FORCE_SET_TARGET_FIELD( \
545  constants, #name, Integer::New(isolate, AsyncHooks::name));
546 
547  SET_HOOKS_CONSTANT(kInit);
548  SET_HOOKS_CONSTANT(kBefore);
549  SET_HOOKS_CONSTANT(kAfter);
550  SET_HOOKS_CONSTANT(kDestroy);
551  SET_HOOKS_CONSTANT(kTotals);
552  SET_HOOKS_CONSTANT(kCurrentAsyncId);
553  SET_HOOKS_CONSTANT(kCurrentTriggerId);
554  SET_HOOKS_CONSTANT(kAsyncUidCntr);
555  SET_HOOKS_CONSTANT(kInitTriggerId);
556 #undef SET_HOOKS_CONSTANT
557  FORCE_SET_TARGET_FIELD(target, "constants", constants);
558 
559  Local<Object> async_providers = Object::New(isolate);
560 #define V(p) \
561  FORCE_SET_TARGET_FIELD( \
562  async_providers, #p, Integer::New(isolate, AsyncWrap::PROVIDER_ ## p));
563  NODE_ASYNC_PROVIDER_TYPES(V)
564 #undef V
565  FORCE_SET_TARGET_FIELD(target, "Providers", async_providers);
566 
567  // These Symbols are used throughout node so the stored values on each object
568  // can be accessed easily across files.
570  target,
571  "async_id_symbol",
572  Symbol::New(isolate, FIXED_ONE_BYTE_STRING(isolate, "asyncId")));
574  target,
575  "trigger_id_symbol",
576  Symbol::New(isolate, FIXED_ONE_BYTE_STRING(isolate, "triggerId")));
577 
578 #undef FORCE_SET_TARGET_FIELD
579 
580  env->set_async_hooks_init_function(Local<Function>());
581  env->set_async_hooks_before_function(Local<Function>());
582  env->set_async_hooks_after_function(Local<Function>());
583  env->set_async_hooks_destroy_function(Local<Function>());
584 }
585 
586 
587 void LoadAsyncWrapperInfo(Environment* env) {
588  HeapProfiler* heap_profiler = env->isolate()->GetHeapProfiler();
589 #define V(PROVIDER) \
590  heap_profiler->SetWrapperClassInfoProvider( \
591  (NODE_ASYNC_ID_OFFSET + AsyncWrap::PROVIDER_ ## PROVIDER), WrapperInfo);
592  NODE_ASYNC_PROVIDER_TYPES(V)
593 #undef V
594 }
595 
596 
597 AsyncWrap::AsyncWrap(Environment* env,
598  Local<Object> object,
599  ProviderType provider,
600  bool silent)
601  : BaseObject(env, object),
602  provider_type_(provider) {
603  CHECK_NE(provider, PROVIDER_NONE);
604  CHECK_GE(object->InternalFieldCount(), 1);
605 
606  // Shift provider value over to prevent id collision.
607  persistent().SetWrapperClassId(NODE_ASYNC_ID_OFFSET + provider);
608 
609  // Use AsyncReset() call to execute the init() callbacks.
610  AsyncReset(silent);
611 }
612 
613 
614 AsyncWrap::~AsyncWrap() {
615  PushBackDestroyId(env(), get_id());
616 }
617 
618 
619 // Generalized call for both the constructor and for handles that are pooled
620 // and reused over their lifetime. This way a new uid can be assigned when
621 // the resource is pulled out of the pool and put back into use.
622 void AsyncWrap::AsyncReset(bool silent) {
623  async_id_ = env()->new_async_id();
624  trigger_id_ = env()->get_init_trigger_id();
625 
626  if (silent) return;
627 
629  env(), object(),
630  env()->async_hooks()->provider_string(provider_type()),
631  async_id_, trigger_id_);
632 }
633 
634 
635 void AsyncWrap::EmitAsyncInit(Environment* env,
636  Local<Object> object,
637  Local<String> type,
638  double async_id,
639  double trigger_id) {
640  CHECK(!object.IsEmpty());
641  CHECK(!type.IsEmpty());
642  AsyncHooks* async_hooks = env->async_hooks();
643 
644  // Nothing to execute, so can continue normally.
645  if (async_hooks->fields()[AsyncHooks::kInit] == 0) {
646  return;
647  }
648 
649  HandleScope scope(env->isolate());
650  Local<Function> init_fn = env->async_hooks_init_function();
651 
652  Local<Value> argv[] = {
653  Number::New(env->isolate(), async_id),
654  type,
655  Number::New(env->isolate(), trigger_id),
656  object,
657  };
658 
659  TryCatch try_catch(env->isolate());
660  MaybeLocal<Value> ret = init_fn->Call(
661  env->context(), object, arraysize(argv), argv);
662 
663  if (ret.IsEmpty()) {
665  FatalException(env->isolate(), try_catch);
666  }
667 }
668 
669 
670 MaybeLocal<Value> AsyncWrap::MakeCallback(const Local<Function> cb,
671  int argc,
672  Local<Value>* argv) {
673  CHECK(env()->context() == env()->isolate()->GetCurrentContext());
674 
675  Environment::AsyncCallbackScope callback_scope(env());
676 
677  Environment::AsyncHooks::ExecScope exec_scope(env(),
678  get_id(),
679  get_trigger_id());
680 
681  // Return v8::Undefined() because returning an empty handle will cause
682  // ToLocalChecked() to abort.
683  if (env()->using_domains() && DomainEnter(env(), object())) {
684  return Undefined(env()->isolate());
685  }
686 
687  // No need to check a return value because the application will exit if an
688  // exception occurs.
689  AsyncWrap::EmitBefore(env(), get_id());
690 
691  MaybeLocal<Value> ret = cb->Call(env()->context(), object(), argc, argv);
692 
693  if (ret.IsEmpty()) {
694  return ret;
695  }
696 
697  AsyncWrap::EmitAfter(env(), get_id());
698 
699  // Return v8::Undefined() because returning an empty handle will cause
700  // ToLocalChecked() to abort.
701  if (env()->using_domains() && DomainExit(env(), object())) {
702  return Undefined(env()->isolate());
703  }
704 
705  exec_scope.Dispose();
706 
707  if (callback_scope.in_makecallback()) {
708  return ret;
709  }
710 
711  Environment::TickInfo* tick_info = env()->tick_info();
712 
713  if (tick_info->length() == 0) {
714  env()->isolate()->RunMicrotasks();
715  }
716 
717  // Make sure the stack unwound properly. If there are nested MakeCallback's
718  // then it should return early and not reach this code.
719  CHECK_EQ(env()->current_async_id(), 0);
720  CHECK_EQ(env()->trigger_id(), 0);
721 
722  Local<Object> process = env()->process_object();
723 
724  if (tick_info->length() == 0) {
725  tick_info->set_index(0);
726  return ret;
727  }
728 
729  MaybeLocal<Value> rcheck =
730  env()->tick_callback_function()->Call(env()->context(),
731  process,
732  0,
733  nullptr);
734 
735  // Make sure the stack unwound properly.
736  CHECK_EQ(env()->current_async_id(), 0);
737  CHECK_EQ(env()->trigger_id(), 0);
738 
739  return rcheck.IsEmpty() ? MaybeLocal<Value>() : ret;
740 }
741 
742 
743 /* Public C++ embedder API */
744 
745 
746 async_id AsyncHooksGetExecutionAsyncId(Isolate* isolate) {
747  return Environment::GetCurrent(isolate)->current_async_id();
748 }
749 
750 async_id AsyncHooksGetCurrentId(Isolate* isolate) {
751  return AsyncHooksGetExecutionAsyncId(isolate);
752 }
753 
754 
755 async_id AsyncHooksGetTriggerAsyncId(Isolate* isolate) {
756  return Environment::GetCurrent(isolate)->trigger_id();
757 }
758 
759 async_id AsyncHooksGetTriggerId(Isolate* isolate) {
760  return AsyncHooksGetTriggerAsyncId(isolate);
761 }
762 
763 
764 async_context EmitAsyncInit(Isolate* isolate,
765  Local<Object> resource,
766  const char* name,
767  async_id trigger_async_id) {
768  Environment* env = Environment::GetCurrent(isolate);
769 
770  // Initialize async context struct
771  if (trigger_async_id == -1)
772  trigger_async_id = env->get_init_trigger_id();
773 
774  async_context context = {
775  env->new_async_id(), // async_id_
776  trigger_async_id // trigger_async_id_
777  };
778 
779  // Run init hooks
780  Local<String> type =
781  String::NewFromUtf8(isolate, name, v8::NewStringType::kInternalized)
782  .ToLocalChecked();
783  AsyncWrap::EmitAsyncInit(env, resource, type, context.async_id,
784  context.trigger_async_id);
785 
786  return context;
787 }
788 
789 void EmitAsyncDestroy(Isolate* isolate, async_context asyncContext) {
790  PushBackDestroyId(Environment::GetCurrent(isolate), asyncContext.async_id);
791 }
792 
793 } // namespace node
794 
796 
797 
798 // Only legacy public API below this line.
799 
800 namespace node {
801 
802 MaybeLocal<Value> MakeCallback(Isolate* isolate,
803  Local<Object> recv,
804  Local<Function> callback,
805  int argc,
806  Local<Value>* argv,
807  async_id asyncId,
808  async_id triggerAsyncId) {
809  return MakeCallback(isolate, recv, callback, argc, argv,
810  {asyncId, triggerAsyncId});
811 }
812 
813 MaybeLocal<Value> MakeCallback(Isolate* isolate,
814  Local<Object> recv,
815  const char* method,
816  int argc,
817  Local<Value>* argv,
818  async_id asyncId,
819  async_id triggerAsyncId) {
820  return MakeCallback(isolate, recv, method, argc, argv,
821  {asyncId, triggerAsyncId});
822 }
823 
824 MaybeLocal<Value> MakeCallback(Isolate* isolate,
825  Local<Object> recv,
826  Local<String> symbol,
827  int argc,
828  Local<Value>* argv,
829  async_id asyncId,
830  async_id triggerAsyncId) {
831  return MakeCallback(isolate, recv, symbol, argc, argv,
832  {asyncId, triggerAsyncId});
833 }
834 
835 // Undo the Node-8.x-only alias from node.h
836 #undef EmitAsyncInit
837 
838 async_uid EmitAsyncInit(Isolate* isolate,
839  Local<Object> resource,
840  const char* name,
841  async_id trigger_async_id) {
842  return EmitAsyncInit__New(isolate,
843  resource,
844  name,
845  trigger_async_id).async_id;
846 }
847 
848 } // namespace node
RetainedObjectInfo * WrapperInfo(uint16_t class_id, Local< Value > wrapper)
Definition: async-wrap.cc:124
void ClearFatalExceptionHandlers(Environment *env)
Definition: node.cc:2688
#define FORCE_SET_TARGET_FIELD(obj, str, field)
RetainedAsyncInfo(uint16_t class_id, AsyncWrap *wrap)
Definition: async-wrap.cc:91
NODE_MODULE_CONTEXT_AWARE_BUILTIN(inspector, node::inspector::Agent::InitInspector)
async_id AsyncHooksGetTriggerId(Isolate *isolate)
Definition: async-wrap.cc:759
size_t self_size() const override
Definition: async-wrap.cc:267
QueryWrap * wrap
Definition: cares_wrap.cc:478
void FatalException(Isolate *isolate, Local< Value > error, Local< Message > message)
Definition: node.cc:2623
async_context EmitAsyncInit(Isolate *isolate, Local< Object > resource, const char *name, async_id trigger_async_id)
Definition: async-wrap.cc:764
static constexpr int kPromiseField
Definition: async-wrap.cc:269
NO_RETURN void FatalError(const char *location, const char *message)
Definition: node.cc:2616
static void GetPromise(Local< String > property, const PropertyCallbackInfo< Value > &info)
Definition: async-wrap.cc:300
union node::cares_wrap::@8::CaresAsyncData::@0 data
bool DomainEnter(Environment *env, Local< Object > object)
Definition: async-wrap.cc:186
bool IsEquivalent(RetainedObjectInfo *other) override
Definition: async-wrap.cc:103
async_id AsyncHooksGetExecutionAsyncId(Isolate *isolate)
Definition: async-wrap.cc:746
#define EmitAsyncInit
Definition: node.h:584
static PromiseWrap * New(Environment *env, Local< Promise > promise, PromiseWrap *parent_wrap, bool silent)
Definition: async-wrap.cc:283
intptr_t GetSizeInBytes() override
Definition: async-wrap.cc:119
async_id async_uid
Definition: node.h:553
void Initialize(Local< Object > target, Local< Value > unused, Local< Context > context, void *priv)
Definition: node_http2.cc:1172
static constexpr int kParentIdField
Definition: async-wrap.cc:270
PromiseWrap(Environment *env, Local< Object > object, bool silent)
Definition: async-wrap.cc:263
#define V(PROVIDER)
void EmitAsyncDestroy(Isolate *isolate, async_context asyncContext)
Definition: async-wrap.cc:789
::node::async_id trigger_async_id
Definition: node.h:544
double async_id
Definition: node.h:541
::node::async_id async_id
Definition: node.h:543
async_id AsyncHooksGetCurrentId(Isolate *isolate)
Definition: async-wrap.cc:750
node::Environment::AsyncHooks AsyncHooks
Definition: node.cc:164
MaybeLocal< Object > New(Isolate *isolate, Local< String > string, enum encoding enc)
Definition: node_buffer.cc:241
method
Definition: node.d:195
MaybeLocal< Value > MakeCallback(Isolate *isolate, Local< Object > recv, Local< Function > callback, int argc, Local< Value > *argv, async_id asyncId, async_id triggerAsyncId)
Definition: async-wrap.cc:802
void Dispose() override
Definition: async-wrap.cc:98
void LoadAsyncWrapperInfo(Environment *env)
Definition: async-wrap.cc:587
node::Environment::AsyncHooks AsyncHooks
Definition: async-wrap.cc:60
bool DomainExit(Environment *env, v8::Local< v8::Object > object)
Definition: async-wrap.cc:204
static constexpr int kInternalFieldCount
Definition: async-wrap.cc:271
#define SET_HOOK_FN(name)
intptr_t GetHash() override
Definition: async-wrap.cc:109
MaybeLocal< Value > MakeCallback(Isolate *isolate, Local< Object > recv, Local< String > symbol, int argc, Local< Value > *argv, async_id asyncId, async_id triggerAsyncId)
Definition: async-wrap.cc:824
async_id AsyncHooksGetTriggerAsyncId(Isolate *isolate)
Definition: async-wrap.cc:755
#define SET_HOOKS_CONSTANT(name)
static void GetParentId(Local< String > property, const PropertyCallbackInfo< Value > &info)
Definition: async-wrap.cc:305
const char * GetLabel() override
Definition: async-wrap.cc:114