@@ -44,6 +44,8 @@ using v8::Local;
44
44
using v8::MaybeLocal;
45
45
using v8::Number;
46
46
using v8::Object;
47
+ using v8::Promise;
48
+ using v8::PromiseHookType;
47
49
using v8::RetainedObjectInfo;
48
50
using v8::Symbol;
49
51
using v8::TryCatch;
@@ -177,6 +179,143 @@ static void PushBackDestroyId(Environment* env, double id) {
177
179
}
178
180
179
181
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
+
180
319
static void SetupHooks (const FunctionCallbackInfo<Value>& args) {
181
320
Environment* env = Environment::GetCurrent (args);
182
321
@@ -201,6 +340,7 @@ static void SetupHooks(const FunctionCallbackInfo<Value>& args) {
201
340
SET_HOOK_FN (before);
202
341
SET_HOOK_FN (after);
203
342
SET_HOOK_FN (destroy);
343
+ env->AddPromiseHook (PromiseHook, nullptr );
204
344
#undef SET_HOOK_FN
205
345
}
206
346
@@ -262,6 +402,11 @@ void AsyncWrap::Initialize(Local<Object> target,
262
402
env->SetMethod (target, " clearIdStack" , ClearIdStack);
263
403
env->SetMethod (target, " addIdToDestroyList" , QueueDestroyId);
264
404
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
+
265
410
v8::PropertyAttribute ReadOnlyDontDelete =
266
411
static_cast <v8::PropertyAttribute>(v8::ReadOnly | v8::DontDelete);
267
412
@@ -416,87 +561,30 @@ Local<Value> AsyncWrap::MakeCallback(const Local<Function> cb,
416
561
Local<Value>* argv) {
417
562
CHECK (env ()->context () == env ()->isolate ()->GetCurrentContext ());
418
563
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
-
425
564
Environment::AsyncCallbackScope callback_scope (env ());
426
565
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 ());
436
569
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>();
461
572
}
462
573
463
574
// 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);
465
576
466
577
Local<Value> ret_v;
467
578
if (!ret.ToLocal (&ret_v)) {
468
579
return Local<Value>();
469
580
}
470
581
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>();
485
584
}
486
585
487
- // The execution scope of the id and trigger_id only go this far.
488
586
exec_scope.Dispose ();
489
587
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
-
500
588
if (callback_scope.in_makecallback ()) {
501
589
return ret_v;
502
590
}
0 commit comments