@@ -44,6 +44,8 @@ using v8::Local;
4444using v8::MaybeLocal;
4545using v8::Number;
4646using v8::Object;
47+ using v8::Promise;
48+ using v8::PromiseHookType;
4749using v8::RetainedObjectInfo;
4850using v8::Symbol;
4951using v8::TryCatch;
@@ -177,6 +179,143 @@ static void PushBackDestroyId(Environment* env, double id) {
177179}
178180
179181
182+ bool DomainEnter (Environment* env, Local<Object> object) {
183+ Local<Value> domain_v = object->Get (env->domain_string ());
184+ if (domain_v->IsObject ()) {
185+ Local<Object> domain = domain_v.As <Object>();
186+ if (domain->Get (env->disposed_string ())->IsTrue ())
187+ return true ;
188+ Local<Value> enter_v = domain->Get (env->enter_string ());
189+ if (enter_v->IsFunction ()) {
190+ if (enter_v.As <Function>()->Call (domain, 0 , nullptr ).IsEmpty ()) {
191+ FatalError (" node::AsyncWrap::MakeCallback" ,
192+ " domain enter callback threw, please report this" );
193+ }
194+ }
195+ }
196+ return false ;
197+ }
198+
199+
200+ bool DomainExit (Environment* env, v8::Local<v8::Object> object) {
201+ Local<Value> domain_v = object->Get (env->domain_string ());
202+ if (domain_v->IsObject ()) {
203+ Local<Object> domain = domain_v.As <Object>();
204+ if (domain->Get (env->disposed_string ())->IsTrue ())
205+ return true ;
206+ Local<Value> exit_v = domain->Get (env->exit_string ());
207+ if (exit_v->IsFunction ()) {
208+ if (exit_v.As <Function>()->Call (domain, 0 , nullptr ).IsEmpty ()) {
209+ FatalError (" node::AsyncWrap::MakeCallback" ,
210+ " domain exit callback threw, please report this" );
211+ }
212+ }
213+ }
214+ return false ;
215+ }
216+
217+
218+ static bool PreCallbackExecution (AsyncWrap* wrap, bool run_domain_cbs) {
219+ AsyncHooks* async_hooks = wrap->env ()->async_hooks ();
220+
221+ if (wrap->env ()->using_domains () && run_domain_cbs) {
222+ bool is_disposed = DomainEnter (wrap->env (), wrap->object ());
223+ if (is_disposed)
224+ return false ;
225+ }
226+
227+ if (async_hooks->fields ()[AsyncHooks::kBefore ] > 0 ) {
228+ Local<Value> uid = Number::New (wrap->env ()->isolate (), wrap->get_id ());
229+ Local<Function> fn = wrap->env ()->async_hooks_before_function ();
230+ TryCatch try_catch (wrap->env ()->isolate ());
231+ MaybeLocal<Value> ar = fn->Call (
232+ wrap->env ()->context (), Undefined (wrap->env ()->isolate ()), 1 , &uid);
233+ if (ar.IsEmpty ()) {
234+ ClearFatalExceptionHandlers (wrap->env ());
235+ FatalException (wrap->env ()->isolate (), try_catch);
236+ return false ;
237+ }
238+ }
239+
240+ return true ;
241+ }
242+
243+
244+ static bool PostCallbackExecution (AsyncWrap* wrap, bool run_domain_cbs) {
245+ AsyncHooks* async_hooks = wrap->env ()->async_hooks ();
246+
247+ // If the callback failed then the after() hooks will be called at the end
248+ // of _fatalException().
249+ if (async_hooks->fields ()[AsyncHooks::kAfter ] > 0 ) {
250+ Local<Value> uid = Number::New (wrap->env ()->isolate (), wrap->get_id ());
251+ Local<Function> fn = wrap->env ()->async_hooks_after_function ();
252+ TryCatch try_catch (wrap->env ()->isolate ());
253+ MaybeLocal<Value> ar = fn->Call (
254+ wrap->env ()->context (), Undefined (wrap->env ()->isolate ()), 1 , &uid);
255+ if (ar.IsEmpty ()) {
256+ ClearFatalExceptionHandlers (wrap->env ());
257+ FatalException (wrap->env ()->isolate (), try_catch);
258+ return false ;
259+ }
260+ }
261+
262+ if (wrap->env ()->using_domains () && run_domain_cbs) {
263+ bool is_disposed = DomainExit (wrap->env (), wrap->object ());
264+ if (is_disposed)
265+ return false ;
266+ }
267+
268+ return true ;
269+ }
270+
271+ class PromiseWrap : public AsyncWrap {
272+ public:
273+ PromiseWrap (Environment* env, Local<Object> object)
274+ : AsyncWrap(env, object, PROVIDER_PROMISE) {}
275+ size_t self_size () const override { return sizeof (*this ); }
276+ };
277+
278+
279+ static void PromiseHook (PromiseHookType type, Local<Promise> promise,
280+ Local<Value> parent, void * arg) {
281+ Local<Context> context = promise->CreationContext ();
282+ Environment* env = Environment::GetCurrent (context);
283+ if (type == PromiseHookType::kInit ) {
284+ // Unfortunately, promises don't have internal fields. Need a surrogate that
285+ // async wrap can wrap.
286+ Local<Object> obj =
287+ env->async_hooks_promise_object ()->NewInstance (context).ToLocalChecked ();
288+ PromiseWrap* wrap = new PromiseWrap (env, obj);
289+ v8::PropertyAttribute hidden =
290+ static_cast <v8::PropertyAttribute>(v8::ReadOnly
291+ | v8::DontDelete
292+ | v8::DontEnum);
293+ promise->DefineOwnProperty (context,
294+ env->promise_wrap (),
295+ v8::External::New (env->isolate (), wrap),
296+ hidden).FromJust ();
297+ // The async tag will be destroyed at the same time as the promise as the
298+ // only reference to it is held by the promise. This allows the promise
299+ // wrap instance to be notified when the promise is destroyed.
300+ promise->DefineOwnProperty (context,
301+ env->promise_async_tag (),
302+ obj, hidden).FromJust ();
303+ } else if (type == PromiseHookType::kResolve ) {
304+ // TODO(matthewloring): need to expose this through the async hooks api.
305+ }
306+ Local<v8::Value> external_wrap =
307+ promise->Get (context, env->promise_wrap ()).ToLocalChecked ();
308+ PromiseWrap* wrap =
309+ static_cast <PromiseWrap*>(external_wrap.As <v8::External>()->Value ());
310+ CHECK_NE (wrap, nullptr );
311+ if (type == PromiseHookType::kBefore ) {
312+ PreCallbackExecution (wrap, false );
313+ } else if (type == PromiseHookType::kAfter ) {
314+ PostCallbackExecution (wrap, false );
315+ }
316+ }
317+
318+
180319static void SetupHooks (const FunctionCallbackInfo<Value>& args) {
181320 Environment* env = Environment::GetCurrent (args);
182321
@@ -201,6 +340,7 @@ static void SetupHooks(const FunctionCallbackInfo<Value>& args) {
201340 SET_HOOK_FN (before);
202341 SET_HOOK_FN (after);
203342 SET_HOOK_FN (destroy);
343+ env->AddPromiseHook (PromiseHook, nullptr );
204344#undef SET_HOOK_FN
205345}
206346
@@ -262,6 +402,11 @@ void AsyncWrap::Initialize(Local<Object> target,
262402 env->SetMethod (target, " clearIdStack" , ClearIdStack);
263403 env->SetMethod (target, " addIdToDestroyList" , QueueDestroyId);
264404
405+ Local<v8::ObjectTemplate> promise_object_template =
406+ v8::ObjectTemplate::New (env->isolate ());
407+ promise_object_template->SetInternalFieldCount (1 );
408+ env->set_async_hooks_promise_object (promise_object_template);
409+
265410 v8::PropertyAttribute ReadOnlyDontDelete =
266411 static_cast <v8::PropertyAttribute>(v8::ReadOnly | v8::DontDelete);
267412
@@ -416,87 +561,30 @@ Local<Value> AsyncWrap::MakeCallback(const Local<Function> cb,
416561 Local<Value>* argv) {
417562 CHECK (env ()->context () == env ()->isolate ()->GetCurrentContext ());
418563
419- AsyncHooks* async_hooks = env ()->async_hooks ();
420- Local<Object> context = object ();
421- Local<Object> domain;
422- Local<Value> uid;
423- bool has_domain = false ;
424-
425564 Environment::AsyncCallbackScope callback_scope (env ());
426565
427- if (env ()->using_domains ()) {
428- Local<Value> domain_v = context->Get (env ()->domain_string ());
429- has_domain = domain_v->IsObject ();
430- if (has_domain) {
431- domain = domain_v.As <Object>();
432- if (domain->Get (env ()->disposed_string ())->IsTrue ())
433- return Local<Value>();
434- }
435- }
566+ Environment::AsyncHooks::ExecScope exec_scope (env (),
567+ get_id (),
568+ get_trigger_id ());
436569
437- if (has_domain) {
438- Local<Value> enter_v = domain->Get (env ()->enter_string ());
439- if (enter_v->IsFunction ()) {
440- if (enter_v.As <Function>()->Call (domain, 0 , nullptr ).IsEmpty ()) {
441- FatalError (" node::AsyncWrap::MakeCallback" ,
442- " domain enter callback threw, please report this" );
443- }
444- }
445- }
446-
447- // Want currentId() to return the correct value from the callbacks.
448- AsyncHooks::ExecScope exec_scope (env (), get_id (), get_trigger_id ());
449-
450- if (async_hooks->fields ()[AsyncHooks::kBefore ] > 0 ) {
451- uid = Number::New (env ()->isolate (), get_id ());
452- Local<Function> fn = env ()->async_hooks_before_function ();
453- TryCatch try_catch (env ()->isolate ());
454- MaybeLocal<Value> ar = fn->Call (
455- env ()->context (), Undefined (env ()->isolate ()), 1 , &uid);
456- if (ar.IsEmpty ()) {
457- ClearFatalExceptionHandlers (env ());
458- FatalException (env ()->isolate (), try_catch);
459- return Local<Value>();
460- }
570+ if (!PreCallbackExecution (this , true )) {
571+ return Local<Value>();
461572 }
462573
463574 // Finally... Get to running the user's callback.
464- MaybeLocal<Value> ret = cb->Call (env ()->context (), context , argc, argv);
575+ MaybeLocal<Value> ret = cb->Call (env ()->context (), object () , argc, argv);
465576
466577 Local<Value> ret_v;
467578 if (!ret.ToLocal (&ret_v)) {
468579 return Local<Value>();
469580 }
470581
471- // If the callback failed then the after() hooks will be called at the end
472- // of _fatalException().
473- if (async_hooks->fields ()[AsyncHooks::kAfter ] > 0 ) {
474- if (uid.IsEmpty ())
475- uid = Number::New (env ()->isolate (), get_id ());
476- Local<Function> fn = env ()->async_hooks_after_function ();
477- TryCatch try_catch (env ()->isolate ());
478- MaybeLocal<Value> ar = fn->Call (
479- env ()->context (), Undefined (env ()->isolate ()), 1 , &uid);
480- if (ar.IsEmpty ()) {
481- ClearFatalExceptionHandlers (env ());
482- FatalException (env ()->isolate (), try_catch);
483- return Local<Value>();
484- }
582+ if (!PostCallbackExecution (this , true )) {
583+ return Local<Value>();
485584 }
486585
487- // The execution scope of the id and trigger_id only go this far.
488586 exec_scope.Dispose ();
489587
490- if (has_domain) {
491- Local<Value> exit_v = domain->Get (env ()->exit_string ());
492- if (exit_v->IsFunction ()) {
493- if (exit_v.As <Function>()->Call (domain, 0 , nullptr ).IsEmpty ()) {
494- FatalError (" node::AsyncWrap::MakeCallback" ,
495- " domain exit callback threw, please report this" );
496- }
497- }
498- }
499-
500588 if (callback_scope.in_makecallback ()) {
501589 return ret_v;
502590 }
0 commit comments