• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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"  // NOLINT(build/include_inline)
23 #include "async_wrap-inl.h"
24 #include "env-inl.h"
25 #include "node_errors.h"
26 #include "tracing/traced_value.h"
27 #include "util-inl.h"
28 
29 #include "v8.h"
30 
31 using v8::Context;
32 using v8::DontDelete;
33 using v8::EscapableHandleScope;
34 using v8::Function;
35 using v8::FunctionCallbackInfo;
36 using v8::FunctionTemplate;
37 using v8::Global;
38 using v8::HandleScope;
39 using v8::Integer;
40 using v8::Isolate;
41 using v8::Local;
42 using v8::MaybeLocal;
43 using v8::Number;
44 using v8::Object;
45 using v8::ObjectTemplate;
46 using v8::Promise;
47 using v8::PromiseHookType;
48 using v8::PropertyAttribute;
49 using v8::PropertyCallbackInfo;
50 using v8::ReadOnly;
51 using v8::String;
52 using v8::Uint32;
53 using v8::Undefined;
54 using v8::Value;
55 using v8::WeakCallbackInfo;
56 using v8::WeakCallbackType;
57 
58 using TryCatchScope = node::errors::TryCatchScope;
59 
60 namespace node {
61 
62 static const char* const provider_names[] = {
63 #define V(PROVIDER)                                                           \
64   #PROVIDER,
65   NODE_ASYNC_PROVIDER_TYPES(V)
66 #undef V
67 };
68 
69 
70 struct AsyncWrapObject : public AsyncWrap {
Newnode::AsyncWrapObject71   static inline void New(const FunctionCallbackInfo<Value>& args) {
72     Environment* env = Environment::GetCurrent(args);
73     CHECK(args.IsConstructCall());
74     CHECK(env->async_wrap_object_ctor_template()->HasInstance(args.This()));
75     CHECK(args[0]->IsUint32());
76     auto type = static_cast<ProviderType>(args[0].As<Uint32>()->Value());
77     new AsyncWrapObject(env, args.This(), type);
78   }
79 
AsyncWrapObjectnode::AsyncWrapObject80   inline AsyncWrapObject(Environment* env, Local<Object> object,
81                          ProviderType type) : AsyncWrap(env, object, type) {}
82 
GetConstructorTemplatenode::AsyncWrapObject83   static Local<FunctionTemplate> GetConstructorTemplate(Environment* env) {
84     Local<FunctionTemplate> tmpl = env->async_wrap_object_ctor_template();
85     if (tmpl.IsEmpty()) {
86       tmpl = env->NewFunctionTemplate(AsyncWrapObject::New);
87       tmpl->SetClassName(
88           FIXED_ONE_BYTE_STRING(env->isolate(), "AsyncWrap"));
89       tmpl->Inherit(AsyncWrap::GetConstructorTemplate(env));
90       tmpl->InstanceTemplate()->SetInternalFieldCount(
91           AsyncWrapObject::kInternalFieldCount);
92       env->set_async_wrap_object_ctor_template(tmpl);
93     }
94     return tmpl;
95   }
96 
97   SET_NO_MEMORY_INFO()
98   SET_MEMORY_INFO_NAME(AsyncWrapObject)
99   SET_SELF_SIZE(AsyncWrapObject)
100 };
101 
DestroyAsyncIdsCallback(Environment * env)102 void AsyncWrap::DestroyAsyncIdsCallback(Environment* env) {
103   Local<Function> fn = env->async_hooks_destroy_function();
104 
105   TryCatchScope try_catch(env, TryCatchScope::CatchMode::kFatal);
106 
107   do {
108     std::vector<double> destroy_async_id_list;
109     destroy_async_id_list.swap(*env->destroy_async_id_list());
110     if (!env->can_call_into_js()) return;
111     for (auto async_id : destroy_async_id_list) {
112       // Want each callback to be cleaned up after itself, instead of cleaning
113       // them all up after the while() loop completes.
114       HandleScope scope(env->isolate());
115       Local<Value> async_id_value = Number::New(env->isolate(), async_id);
116       MaybeLocal<Value> ret = fn->Call(
117           env->context(), Undefined(env->isolate()), 1, &async_id_value);
118 
119       if (ret.IsEmpty())
120         return;
121     }
122   } while (!env->destroy_async_id_list()->empty());
123 }
124 
Emit(Environment * env,double async_id,AsyncHooks::Fields type,Local<Function> fn)125 void Emit(Environment* env, double async_id, AsyncHooks::Fields type,
126           Local<Function> fn) {
127   AsyncHooks* async_hooks = env->async_hooks();
128 
129   if (async_hooks->fields()[type] == 0 || !env->can_call_into_js())
130     return;
131 
132   HandleScope handle_scope(env->isolate());
133   Local<Value> async_id_value = Number::New(env->isolate(), async_id);
134   TryCatchScope try_catch(env, TryCatchScope::CatchMode::kFatal);
135   USE(fn->Call(env->context(), Undefined(env->isolate()), 1, &async_id_value));
136 }
137 
138 
EmitPromiseResolve(Environment * env,double async_id)139 void AsyncWrap::EmitPromiseResolve(Environment* env, double async_id) {
140   Emit(env, async_id, AsyncHooks::kPromiseResolve,
141        env->async_hooks_promise_resolve_function());
142 }
143 
144 
EmitTraceEventBefore()145 void AsyncWrap::EmitTraceEventBefore() {
146   switch (provider_type()) {
147 #define V(PROVIDER)                                                           \
148     case PROVIDER_ ## PROVIDER:                                               \
149       TRACE_EVENT_NESTABLE_ASYNC_BEGIN0(                                      \
150         TRACING_CATEGORY_NODE1(async_hooks),                                  \
151         #PROVIDER "_CALLBACK", static_cast<int64_t>(get_async_id()));         \
152       break;
153     NODE_ASYNC_PROVIDER_TYPES(V)
154 #undef V
155     default:
156       UNREACHABLE();
157   }
158 }
159 
160 
EmitBefore(Environment * env,double async_id)161 void AsyncWrap::EmitBefore(Environment* env, double async_id) {
162   Emit(env, async_id, AsyncHooks::kBefore,
163        env->async_hooks_before_function());
164 }
165 
166 
EmitTraceEventAfter(ProviderType type,double async_id)167 void AsyncWrap::EmitTraceEventAfter(ProviderType type, double async_id) {
168   switch (type) {
169 #define V(PROVIDER)                                                           \
170     case PROVIDER_ ## PROVIDER:                                               \
171       TRACE_EVENT_NESTABLE_ASYNC_END0(                                        \
172         TRACING_CATEGORY_NODE1(async_hooks),                                  \
173         #PROVIDER "_CALLBACK", static_cast<int64_t>(async_id));               \
174       break;
175     NODE_ASYNC_PROVIDER_TYPES(V)
176 #undef V
177     default:
178       UNREACHABLE();
179   }
180 }
181 
182 
EmitAfter(Environment * env,double async_id)183 void AsyncWrap::EmitAfter(Environment* env, double async_id) {
184   // If the user's callback failed then the after() hooks will be called at the
185   // end of _fatalException().
186   Emit(env, async_id, AsyncHooks::kAfter,
187        env->async_hooks_after_function());
188 }
189 
190 class PromiseWrap : public AsyncWrap {
191  public:
192   enum InternalFields {
193     kIsChainedPromiseField = AsyncWrap::kInternalFieldCount,
194     kInternalFieldCount
195   };
PromiseWrap(Environment * env,Local<Object> object,bool silent)196   PromiseWrap(Environment* env, Local<Object> object, bool silent)
197       : AsyncWrap(env, object, PROVIDER_PROMISE, kInvalidAsyncId, silent) {
198     MakeWeak();
199   }
200 
201   SET_NO_MEMORY_INFO()
202   SET_MEMORY_INFO_NAME(PromiseWrap)
203   SET_SELF_SIZE(PromiseWrap)
204 
205   static PromiseWrap* New(Environment* env,
206                           Local<Promise> promise,
207                           PromiseWrap* parent_wrap,
208                           bool silent);
209   static void getIsChainedPromise(Local<String> property,
210                                   const PropertyCallbackInfo<Value>& info);
211 };
212 
New(Environment * env,Local<Promise> promise,PromiseWrap * parent_wrap,bool silent)213 PromiseWrap* PromiseWrap::New(Environment* env,
214                               Local<Promise> promise,
215                               PromiseWrap* parent_wrap,
216                               bool silent) {
217   Local<Object> obj;
218   if (!env->promise_wrap_template()->NewInstance(env->context()).ToLocal(&obj))
219     return nullptr;
220   obj->SetInternalField(PromiseWrap::kIsChainedPromiseField,
221                         parent_wrap != nullptr ? v8::True(env->isolate())
222                                                : v8::False(env->isolate()));
223   CHECK_NULL(promise->GetAlignedPointerFromInternalField(0));
224   promise->SetInternalField(0, obj);
225   return new PromiseWrap(env, obj, silent);
226 }
227 
getIsChainedPromise(Local<String> property,const PropertyCallbackInfo<Value> & info)228 void PromiseWrap::getIsChainedPromise(Local<String> property,
229                                       const PropertyCallbackInfo<Value>& info) {
230   info.GetReturnValue().Set(
231       info.Holder()->GetInternalField(PromiseWrap::kIsChainedPromiseField));
232 }
233 
extractPromiseWrap(Local<Promise> promise)234 static PromiseWrap* extractPromiseWrap(Local<Promise> promise) {
235   // This check is imperfect. If the internal field is set, it should
236   // be an object. If it's not, we just ignore it. Ideally v8 would
237   // have had GetInternalField returning a MaybeLocal but this works
238   // for now.
239   Local<Value> obj = promise->GetInternalField(0);
240   return obj->IsObject() ? Unwrap<PromiseWrap>(obj.As<Object>()) : nullptr;
241 }
242 
PromiseHook(PromiseHookType type,Local<Promise> promise,Local<Value> parent)243 static void PromiseHook(PromiseHookType type, Local<Promise> promise,
244                         Local<Value> parent) {
245   Local<Context> context = promise->CreationContext();
246 
247   Environment* env = Environment::GetCurrent(context);
248   if (env == nullptr) return;
249   TraceEventScope trace_scope(TRACING_CATEGORY_NODE1(environment),
250                               "EnvPromiseHook", env);
251 
252   PromiseWrap* wrap = extractPromiseWrap(promise);
253   if (type == PromiseHookType::kInit || wrap == nullptr) {
254     bool silent = type != PromiseHookType::kInit;
255 
256     // set parent promise's async Id as this promise's triggerAsyncId
257     if (parent->IsPromise()) {
258       // parent promise exists, current promise
259       // is a chained promise, so we set parent promise's id as
260       // current promise's triggerAsyncId
261       Local<Promise> parent_promise = parent.As<Promise>();
262       PromiseWrap* parent_wrap = extractPromiseWrap(parent_promise);
263       if (parent_wrap == nullptr) {
264         parent_wrap = PromiseWrap::New(env, parent_promise, nullptr, true);
265         if (parent_wrap == nullptr) return;
266       }
267 
268       AsyncHooks::DefaultTriggerAsyncIdScope trigger_scope(parent_wrap);
269       wrap = PromiseWrap::New(env, promise, parent_wrap, silent);
270     } else {
271       wrap = PromiseWrap::New(env, promise, nullptr, silent);
272     }
273   }
274 
275   if (wrap == nullptr) return;
276 
277   if (type == PromiseHookType::kBefore) {
278     env->async_hooks()->push_async_context(wrap->get_async_id(),
279       wrap->get_trigger_async_id(), wrap->object());
280     wrap->EmitTraceEventBefore();
281     AsyncWrap::EmitBefore(wrap->env(), wrap->get_async_id());
282   } else if (type == PromiseHookType::kAfter) {
283     wrap->EmitTraceEventAfter(wrap->provider_type(), wrap->get_async_id());
284     AsyncWrap::EmitAfter(wrap->env(), wrap->get_async_id());
285     if (env->execution_async_id() == wrap->get_async_id()) {
286       // This condition might not be true if async_hooks was enabled during
287       // the promise callback execution.
288       // Popping it off the stack can be skipped in that case, because it is
289       // known that it would correspond to exactly one call with
290       // PromiseHookType::kBefore that was not witnessed by the PromiseHook.
291       env->async_hooks()->pop_async_context(wrap->get_async_id());
292     }
293   } else if (type == PromiseHookType::kResolve) {
294     AsyncWrap::EmitPromiseResolve(wrap->env(), wrap->get_async_id());
295   }
296 }
297 
298 
SetupHooks(const FunctionCallbackInfo<Value> & args)299 static void SetupHooks(const FunctionCallbackInfo<Value>& args) {
300   Environment* env = Environment::GetCurrent(args);
301 
302   CHECK(args[0]->IsObject());
303 
304   // All of init, before, after, destroy, and promise_resolve are supplied by
305   // async_hooks internally, so this should only ever be called once. At which
306   // time all the functions should be set. Detect this by checking if
307   // init !IsEmpty().
308   CHECK(env->async_hooks_init_function().IsEmpty());
309 
310   Local<Object> fn_obj = args[0].As<Object>();
311 
312 #define SET_HOOK_FN(name)                                                      \
313   do {                                                                         \
314     Local<Value> v =                                                           \
315         fn_obj->Get(env->context(),                                            \
316                     FIXED_ONE_BYTE_STRING(env->isolate(), #name))              \
317             .ToLocalChecked();                                                 \
318     CHECK(v->IsFunction());                                                    \
319     env->set_async_hooks_##name##_function(v.As<Function>());                  \
320   } while (0)
321 
322   SET_HOOK_FN(init);
323   SET_HOOK_FN(before);
324   SET_HOOK_FN(after);
325   SET_HOOK_FN(destroy);
326   SET_HOOK_FN(promise_resolve);
327 #undef SET_HOOK_FN
328 
329   {
330     Local<FunctionTemplate> ctor =
331         FunctionTemplate::New(env->isolate());
332     ctor->SetClassName(FIXED_ONE_BYTE_STRING(env->isolate(), "PromiseWrap"));
333     Local<ObjectTemplate> promise_wrap_template = ctor->InstanceTemplate();
334     promise_wrap_template->SetInternalFieldCount(
335         PromiseWrap::kInternalFieldCount);
336     promise_wrap_template->SetAccessor(
337         FIXED_ONE_BYTE_STRING(env->isolate(), "isChainedPromise"),
338         PromiseWrap::getIsChainedPromise);
339     env->set_promise_wrap_template(promise_wrap_template);
340   }
341 }
342 
343 
EnablePromiseHook(const FunctionCallbackInfo<Value> & args)344 static void EnablePromiseHook(const FunctionCallbackInfo<Value>& args) {
345   args.GetIsolate()->SetPromiseHook(PromiseHook);
346 }
347 
348 
DisablePromiseHook(const FunctionCallbackInfo<Value> & args)349 static void DisablePromiseHook(const FunctionCallbackInfo<Value>& args) {
350   Isolate* isolate = args.GetIsolate();
351 
352   // The per-Isolate API provides no way of knowing whether there are multiple
353   // users of the PromiseHook. That hopefully goes away when V8 introduces
354   // a per-context API.
355   isolate->SetPromiseHook(nullptr);
356 }
357 
358 
359 class DestroyParam {
360  public:
361   double asyncId;
362   Environment* env;
363   Global<Object> target;
364   Global<Object> propBag;
365 };
366 
DestroyParamCleanupHook(void * ptr)367 static void DestroyParamCleanupHook(void* ptr) {
368   delete static_cast<DestroyParam*>(ptr);
369 }
370 
WeakCallback(const WeakCallbackInfo<DestroyParam> & info)371 void AsyncWrap::WeakCallback(const WeakCallbackInfo<DestroyParam>& info) {
372   HandleScope scope(info.GetIsolate());
373 
374   std::unique_ptr<DestroyParam> p{info.GetParameter()};
375   Local<Object> prop_bag = PersistentToLocal::Default(info.GetIsolate(),
376                                                       p->propBag);
377   Local<Value> val;
378 
379   p->env->RemoveCleanupHook(DestroyParamCleanupHook, p.get());
380 
381   if (!prop_bag->Get(p->env->context(), p->env->destroyed_string())
382         .ToLocal(&val)) {
383     return;
384   }
385 
386   if (val->IsFalse()) {
387     AsyncWrap::EmitDestroy(p->env, p->asyncId);
388   }
389   // unique_ptr goes out of scope here and pointer is deleted.
390 }
391 
392 
RegisterDestroyHook(const FunctionCallbackInfo<Value> & args)393 static void RegisterDestroyHook(const FunctionCallbackInfo<Value>& args) {
394   CHECK(args[0]->IsObject());
395   CHECK(args[1]->IsNumber());
396   CHECK(args[2]->IsObject());
397 
398   Isolate* isolate = args.GetIsolate();
399   DestroyParam* p = new DestroyParam();
400   p->asyncId = args[1].As<Number>()->Value();
401   p->env = Environment::GetCurrent(args);
402   p->target.Reset(isolate, args[0].As<Object>());
403   p->propBag.Reset(isolate, args[2].As<Object>());
404   p->target.SetWeak(p, AsyncWrap::WeakCallback, WeakCallbackType::kParameter);
405   p->env->AddCleanupHook(DestroyParamCleanupHook, p);
406 }
407 
GetAsyncId(const FunctionCallbackInfo<Value> & args)408 void AsyncWrap::GetAsyncId(const FunctionCallbackInfo<Value>& args) {
409   AsyncWrap* wrap;
410   args.GetReturnValue().Set(kInvalidAsyncId);
411   ASSIGN_OR_RETURN_UNWRAP(&wrap, args.Holder());
412   args.GetReturnValue().Set(wrap->get_async_id());
413 }
414 
415 
PushAsyncContext(const FunctionCallbackInfo<Value> & args)416 void AsyncWrap::PushAsyncContext(const FunctionCallbackInfo<Value>& args) {
417   Environment* env = Environment::GetCurrent(args);
418   // No need for CHECK(IsNumber()) on args because if FromJust() doesn't fail
419   // then the checks in push_async_ids() and pop_async_id() will.
420   double async_id = args[0]->NumberValue(env->context()).FromJust();
421   double trigger_async_id = args[1]->NumberValue(env->context()).FromJust();
422   env->async_hooks()->push_async_context(async_id, trigger_async_id, {});
423 }
424 
425 
PopAsyncContext(const FunctionCallbackInfo<Value> & args)426 void AsyncWrap::PopAsyncContext(const FunctionCallbackInfo<Value>& args) {
427   Environment* env = Environment::GetCurrent(args);
428   double async_id = args[0]->NumberValue(env->context()).FromJust();
429   args.GetReturnValue().Set(env->async_hooks()->pop_async_context(async_id));
430 }
431 
432 
ExecutionAsyncResource(const FunctionCallbackInfo<Value> & args)433 void AsyncWrap::ExecutionAsyncResource(
434     const FunctionCallbackInfo<Value>& args) {
435   Environment* env = Environment::GetCurrent(args);
436   uint32_t index;
437   if (!args[0]->Uint32Value(env->context()).To(&index)) return;
438   args.GetReturnValue().Set(
439       env->async_hooks()->native_execution_async_resource(index));
440 }
441 
442 
ClearAsyncIdStack(const FunctionCallbackInfo<Value> & args)443 void AsyncWrap::ClearAsyncIdStack(const FunctionCallbackInfo<Value>& args) {
444   Environment* env = Environment::GetCurrent(args);
445   env->async_hooks()->clear_async_id_stack();
446 }
447 
448 
AsyncReset(const FunctionCallbackInfo<Value> & args)449 void AsyncWrap::AsyncReset(const FunctionCallbackInfo<Value>& args) {
450   CHECK(args[0]->IsObject());
451 
452   AsyncWrap* wrap;
453   ASSIGN_OR_RETURN_UNWRAP(&wrap, args.Holder());
454 
455   Local<Object> resource = args[0].As<Object>();
456   double execution_async_id =
457       args[1]->IsNumber() ? args[1].As<Number>()->Value() : kInvalidAsyncId;
458   wrap->AsyncReset(resource, execution_async_id);
459 }
460 
461 
GetProviderType(const FunctionCallbackInfo<Value> & args)462 void AsyncWrap::GetProviderType(const FunctionCallbackInfo<Value>& args) {
463   AsyncWrap* wrap;
464   args.GetReturnValue().Set(AsyncWrap::PROVIDER_NONE);
465   ASSIGN_OR_RETURN_UNWRAP(&wrap, args.Holder());
466   args.GetReturnValue().Set(wrap->provider_type());
467 }
468 
469 
EmitDestroy(bool from_gc)470 void AsyncWrap::EmitDestroy(bool from_gc) {
471   AsyncWrap::EmitDestroy(env(), async_id_);
472   // Ensure no double destroy is emitted via AsyncReset().
473   async_id_ = kInvalidAsyncId;
474 
475   if (!persistent().IsEmpty() && !from_gc) {
476     HandleScope handle_scope(env()->isolate());
477     USE(object()->Set(env()->context(), env()->resource_symbol(), object()));
478   }
479 }
480 
QueueDestroyAsyncId(const FunctionCallbackInfo<Value> & args)481 void AsyncWrap::QueueDestroyAsyncId(const FunctionCallbackInfo<Value>& args) {
482   CHECK(args[0]->IsNumber());
483   AsyncWrap::EmitDestroy(
484       Environment::GetCurrent(args),
485       args[0].As<Number>()->Value());
486 }
487 
SetCallbackTrampoline(const FunctionCallbackInfo<Value> & args)488 void AsyncWrap::SetCallbackTrampoline(const FunctionCallbackInfo<Value>& args) {
489   Environment* env = Environment::GetCurrent(args);
490 
491   CHECK(args[0]->IsFunction());
492 
493   env->set_async_hooks_callback_trampoline(args[0].As<Function>());
494 }
495 
GetConstructorTemplate(Environment * env)496 Local<FunctionTemplate> AsyncWrap::GetConstructorTemplate(Environment* env) {
497   Local<FunctionTemplate> tmpl = env->async_wrap_ctor_template();
498   if (tmpl.IsEmpty()) {
499     tmpl = env->NewFunctionTemplate(nullptr);
500     tmpl->SetClassName(FIXED_ONE_BYTE_STRING(env->isolate(), "AsyncWrap"));
501     tmpl->Inherit(BaseObject::GetConstructorTemplate(env));
502     env->SetProtoMethod(tmpl, "getAsyncId", AsyncWrap::GetAsyncId);
503     env->SetProtoMethod(tmpl, "asyncReset", AsyncWrap::AsyncReset);
504     env->SetProtoMethod(tmpl, "getProviderType", AsyncWrap::GetProviderType);
505     env->set_async_wrap_ctor_template(tmpl);
506   }
507   return tmpl;
508 }
509 
Initialize(Local<Object> target,Local<Value> unused,Local<Context> context,void * priv)510 void AsyncWrap::Initialize(Local<Object> target,
511                            Local<Value> unused,
512                            Local<Context> context,
513                            void* priv) {
514   Environment* env = Environment::GetCurrent(context);
515   Isolate* isolate = env->isolate();
516   HandleScope scope(isolate);
517 
518   env->SetMethod(target, "setupHooks", SetupHooks);
519   env->SetMethod(target, "setCallbackTrampoline", SetCallbackTrampoline);
520   env->SetMethod(target, "pushAsyncContext", PushAsyncContext);
521   env->SetMethod(target, "popAsyncContext", PopAsyncContext);
522   env->SetMethod(target, "executionAsyncResource", ExecutionAsyncResource);
523   env->SetMethod(target, "clearAsyncIdStack", ClearAsyncIdStack);
524   env->SetMethod(target, "queueDestroyAsyncId", QueueDestroyAsyncId);
525   env->SetMethod(target, "enablePromiseHook", EnablePromiseHook);
526   env->SetMethod(target, "disablePromiseHook", DisablePromiseHook);
527   env->SetMethod(target, "registerDestroyHook", RegisterDestroyHook);
528 
529   PropertyAttribute ReadOnlyDontDelete =
530       static_cast<PropertyAttribute>(ReadOnly | DontDelete);
531 
532 #define FORCE_SET_TARGET_FIELD(obj, str, field)                               \
533   (obj)->DefineOwnProperty(context,                                           \
534                            FIXED_ONE_BYTE_STRING(isolate, str),               \
535                            field,                                             \
536                            ReadOnlyDontDelete).FromJust()
537 
538   // Attach the uint32_t[] where each slot contains the count of the number of
539   // callbacks waiting to be called on a particular event. It can then be
540   // incremented/decremented from JS quickly to communicate to C++ if there are
541   // any callbacks waiting to be called.
542   FORCE_SET_TARGET_FIELD(target,
543                          "async_hook_fields",
544                          env->async_hooks()->fields().GetJSArray());
545 
546   // The following v8::Float64Array has 5 fields. These fields are shared in
547   // this way to allow JS and C++ to read/write each value as quickly as
548   // possible. The fields are represented as follows:
549   //
550   // kAsyncIdCounter: Maintains the state of the next unique id to be assigned.
551   //
552   // kDefaultTriggerAsyncId: Write the id of the resource responsible for a
553   //   handle's creation just before calling the new handle's constructor.
554   //   After the new handle is constructed kDefaultTriggerAsyncId is set back
555   //   to kInvalidAsyncId.
556   FORCE_SET_TARGET_FIELD(target,
557                          "async_id_fields",
558                          env->async_hooks()->async_id_fields().GetJSArray());
559 
560   FORCE_SET_TARGET_FIELD(target,
561                          "execution_async_resources",
562                          env->async_hooks()->js_execution_async_resources());
563 
564   target->Set(context,
565               env->async_ids_stack_string(),
566               env->async_hooks()->async_ids_stack().GetJSArray()).Check();
567 
568   Local<Object> constants = Object::New(isolate);
569 #define SET_HOOKS_CONSTANT(name)                                              \
570   FORCE_SET_TARGET_FIELD(                                                     \
571       constants, #name, Integer::New(isolate, AsyncHooks::name))
572 
573   SET_HOOKS_CONSTANT(kInit);
574   SET_HOOKS_CONSTANT(kBefore);
575   SET_HOOKS_CONSTANT(kAfter);
576   SET_HOOKS_CONSTANT(kDestroy);
577   SET_HOOKS_CONSTANT(kPromiseResolve);
578   SET_HOOKS_CONSTANT(kTotals);
579   SET_HOOKS_CONSTANT(kCheck);
580   SET_HOOKS_CONSTANT(kExecutionAsyncId);
581   SET_HOOKS_CONSTANT(kTriggerAsyncId);
582   SET_HOOKS_CONSTANT(kAsyncIdCounter);
583   SET_HOOKS_CONSTANT(kDefaultTriggerAsyncId);
584   SET_HOOKS_CONSTANT(kUsesExecutionAsyncResource);
585   SET_HOOKS_CONSTANT(kStackLength);
586 #undef SET_HOOKS_CONSTANT
587   FORCE_SET_TARGET_FIELD(target, "constants", constants);
588 
589   Local<Object> async_providers = Object::New(isolate);
590 #define V(p)                                                                  \
591   FORCE_SET_TARGET_FIELD(                                                     \
592       async_providers, #p, Integer::New(isolate, AsyncWrap::PROVIDER_ ## p));
593   NODE_ASYNC_PROVIDER_TYPES(V)
594 #undef V
595   FORCE_SET_TARGET_FIELD(target, "Providers", async_providers);
596 
597 #undef FORCE_SET_TARGET_FIELD
598 
599   env->set_async_hooks_init_function(Local<Function>());
600   env->set_async_hooks_before_function(Local<Function>());
601   env->set_async_hooks_after_function(Local<Function>());
602   env->set_async_hooks_destroy_function(Local<Function>());
603   env->set_async_hooks_promise_resolve_function(Local<Function>());
604   env->set_async_hooks_binding(target);
605 
606   target->Set(env->context(),
607       FIXED_ONE_BYTE_STRING(env->isolate(), "AsyncWrap"),
608       AsyncWrapObject::GetConstructorTemplate(env)
609           ->GetFunction(env->context()).ToLocalChecked()).Check();
610 }
611 
612 
AsyncWrap(Environment * env,Local<Object> object,ProviderType provider,double execution_async_id)613 AsyncWrap::AsyncWrap(Environment* env,
614                      Local<Object> object,
615                      ProviderType provider,
616                      double execution_async_id)
617     : AsyncWrap(env, object, provider, execution_async_id, false) {}
618 
AsyncWrap(Environment * env,Local<Object> object,ProviderType provider,double execution_async_id,bool silent)619 AsyncWrap::AsyncWrap(Environment* env,
620                      Local<Object> object,
621                      ProviderType provider,
622                      double execution_async_id,
623                      bool silent)
624     : AsyncWrap(env, object) {
625   CHECK_NE(provider, PROVIDER_NONE);
626   provider_type_ = provider;
627 
628   // Use AsyncReset() call to execute the init() callbacks.
629   AsyncReset(object, execution_async_id, silent);
630   init_hook_ran_ = true;
631 }
632 
AsyncWrap(Environment * env,Local<Object> object)633 AsyncWrap::AsyncWrap(Environment* env, Local<Object> object)
634   : BaseObject(env, object) {
635 }
636 
637 // This method is necessary to work around one specific problem:
638 // Before the init() hook runs, if there is one, the BaseObject() constructor
639 // registers this object with the Environment for finalization and debugging
640 // purposes.
641 // If the Environment decides to inspect this object for debugging, it tries to
642 // call virtual methods on this object that are only (meaningfully) implemented
643 // by the subclasses of AsyncWrap.
644 // This could, with bad luck, happen during the AsyncWrap() constructor,
645 // because we run JS code as part of it and that in turn can lead to a heapdump
646 // being taken, either through the inspector or our programmatic API for it.
647 // The object being initialized is not fully constructed at that point, and
648 // in particular its virtual function table points to the AsyncWrap one
649 // (as the subclass constructor has not yet begun execution at that point).
650 // This means that the functions that are used for heap dump memory tracking
651 // are not yet available, and trying to call them would crash the process.
652 // We use this particular `IsDoneInitializing()` method to tell the Environment
653 // that such debugging methods are not yet available.
654 // This may be somewhat unreliable when it comes to future changes, because
655 // at this point it *only* protects AsyncWrap subclasses, and *only* for cases
656 // where heap dumps are being taken while the init() hook is on the call stack.
657 // For now, it seems like the best solution, though.
IsDoneInitializing() const658 bool AsyncWrap::IsDoneInitializing() const {
659   return init_hook_ran_;
660 }
661 
~AsyncWrap()662 AsyncWrap::~AsyncWrap() {
663   EmitTraceEventDestroy();
664   EmitDestroy(true /* from gc */);
665 }
666 
EmitTraceEventDestroy()667 void AsyncWrap::EmitTraceEventDestroy() {
668   switch (provider_type()) {
669   #define V(PROVIDER)                                                         \
670     case PROVIDER_ ## PROVIDER:                                               \
671       TRACE_EVENT_NESTABLE_ASYNC_END0(                                        \
672         TRACING_CATEGORY_NODE1(async_hooks),                                  \
673         #PROVIDER, static_cast<int64_t>(get_async_id()));                     \
674       break;
675     NODE_ASYNC_PROVIDER_TYPES(V)
676   #undef V
677     default:
678       UNREACHABLE();
679   }
680 }
681 
EmitDestroy(Environment * env,double async_id)682 void AsyncWrap::EmitDestroy(Environment* env, double async_id) {
683   if (env->async_hooks()->fields()[AsyncHooks::kDestroy] == 0 ||
684       !env->can_call_into_js()) {
685     return;
686   }
687 
688   if (env->destroy_async_id_list()->empty()) {
689     env->SetImmediate(&DestroyAsyncIdsCallback, CallbackFlags::kUnrefed);
690   }
691 
692   // If the list gets very large empty it faster using a Microtask.
693   // Microtasks can't be added in GC context therefore we use an
694   // interrupt to get this Microtask scheduled as fast as possible.
695   if (env->destroy_async_id_list()->size() == 16384) {
696     env->RequestInterrupt([](Environment* env) {
697       env->isolate()->EnqueueMicrotask(
698         [](void* arg) {
699           DestroyAsyncIdsCallback(static_cast<Environment*>(arg));
700         }, env);
701       });
702   }
703 
704   env->destroy_async_id_list()->push_back(async_id);
705 }
706 
707 // Generalized call for both the constructor and for handles that are pooled
708 // and reused over their lifetime. This way a new uid can be assigned when
709 // the resource is pulled out of the pool and put back into use.
AsyncReset(Local<Object> resource,double execution_async_id,bool silent)710 void AsyncWrap::AsyncReset(Local<Object> resource, double execution_async_id,
711                            bool silent) {
712   CHECK_NE(provider_type(), PROVIDER_NONE);
713 
714   if (async_id_ != kInvalidAsyncId) {
715     // This instance was in use before, we have already emitted an init with
716     // its previous async_id and need to emit a matching destroy for that
717     // before generating a new async_id.
718     EmitDestroy();
719   }
720 
721   // Now we can assign a new async_id_ to this instance.
722   async_id_ = execution_async_id == kInvalidAsyncId ? env()->new_async_id()
723                                                      : execution_async_id;
724   trigger_async_id_ = env()->get_default_trigger_async_id();
725 
726   {
727     HandleScope handle_scope(env()->isolate());
728     Local<Object> obj = object();
729     CHECK(!obj.IsEmpty());
730     if (resource != obj) {
731       USE(obj->Set(env()->context(), env()->resource_symbol(), resource));
732     }
733   }
734 
735   switch (provider_type()) {
736 #define V(PROVIDER)                                                           \
737     case PROVIDER_ ## PROVIDER:                                               \
738       if (*TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED(                        \
739           TRACING_CATEGORY_NODE1(async_hooks))) {                             \
740         auto data = tracing::TracedValue::Create();                           \
741         data->SetInteger("executionAsyncId",                                  \
742                          static_cast<int64_t>(env()->execution_async_id()));  \
743         data->SetInteger("triggerAsyncId",                                    \
744                          static_cast<int64_t>(get_trigger_async_id()));       \
745         TRACE_EVENT_NESTABLE_ASYNC_BEGIN1(                                    \
746           TRACING_CATEGORY_NODE1(async_hooks),                                \
747           #PROVIDER, static_cast<int64_t>(get_async_id()),                    \
748           "data", std::move(data));                                           \
749         }                                                                     \
750       break;
751     NODE_ASYNC_PROVIDER_TYPES(V)
752 #undef V
753     default:
754       UNREACHABLE();
755   }
756 
757   if (silent) return;
758 
759   EmitAsyncInit(env(), resource,
760                 env()->async_hooks()->provider_string(provider_type()),
761                 async_id_, trigger_async_id_);
762 }
763 
764 
EmitAsyncInit(Environment * env,Local<Object> object,Local<String> type,double async_id,double trigger_async_id)765 void AsyncWrap::EmitAsyncInit(Environment* env,
766                               Local<Object> object,
767                               Local<String> type,
768                               double async_id,
769                               double trigger_async_id) {
770   CHECK(!object.IsEmpty());
771   CHECK(!type.IsEmpty());
772   AsyncHooks* async_hooks = env->async_hooks();
773 
774   // Nothing to execute, so can continue normally.
775   if (async_hooks->fields()[AsyncHooks::kInit] == 0) {
776     return;
777   }
778 
779   HandleScope scope(env->isolate());
780   Local<Function> init_fn = env->async_hooks_init_function();
781 
782   Local<Value> argv[] = {
783     Number::New(env->isolate(), async_id),
784     type,
785     Number::New(env->isolate(), trigger_async_id),
786     object,
787   };
788 
789   TryCatchScope try_catch(env, TryCatchScope::CatchMode::kFatal);
790   USE(init_fn->Call(env->context(), object, arraysize(argv), argv));
791 }
792 
793 
MakeCallback(const Local<Function> cb,int argc,Local<Value> * argv)794 MaybeLocal<Value> AsyncWrap::MakeCallback(const Local<Function> cb,
795                                           int argc,
796                                           Local<Value>* argv) {
797   EmitTraceEventBefore();
798 
799   ProviderType provider = provider_type();
800   async_context context { get_async_id(), get_trigger_async_id() };
801   MaybeLocal<Value> ret = InternalMakeCallback(
802       env(), object(), object(), cb, argc, argv, context);
803 
804   // This is a static call with cached values because the `this` object may
805   // no longer be alive at this point.
806   EmitTraceEventAfter(provider, context.async_id);
807 
808   return ret;
809 }
810 
MemoryInfoName() const811 std::string AsyncWrap::MemoryInfoName() const {
812   return provider_names[provider_type()];
813 }
814 
diagnostic_name() const815 std::string AsyncWrap::diagnostic_name() const {
816   return MemoryInfoName() + " (" + std::to_string(env()->thread_id()) + ":" +
817       std::to_string(static_cast<int64_t>(async_id_)) + ")";
818 }
819 
GetOwner()820 Local<Object> AsyncWrap::GetOwner() {
821   return GetOwner(env(), object());
822 }
823 
GetOwner(Environment * env,Local<Object> obj)824 Local<Object> AsyncWrap::GetOwner(Environment* env, Local<Object> obj) {
825   EscapableHandleScope handle_scope(env->isolate());
826   CHECK(!obj.IsEmpty());
827 
828   TryCatchScope ignore_exceptions(env);
829   while (true) {
830     Local<Value> owner;
831     if (!obj->Get(env->context(),
832                   env->owner_symbol()).ToLocal(&owner) ||
833         !owner->IsObject()) {
834       return handle_scope.Escape(obj);
835     }
836 
837     obj = owner.As<Object>();
838   }
839 }
840 
841 }  // namespace node
842 
843 NODE_MODULE_CONTEXT_AWARE_INTERNAL(async_wrap, node::AsyncWrap::Initialize)
844