1
+ #include " async_wrap-inl.h"
1
2
#include " env-inl.h"
2
3
#define NAPI_EXPERIMENTAL
3
4
#include " js_native_api_v8.h"
5
+ #include " memory_tracker-inl.h"
4
6
#include " node_api.h"
5
7
#include " node_binding.h"
6
8
#include " node_buffer.h"
7
9
#include " node_errors.h"
8
10
#include " node_internals.h"
9
11
#include " threadpoolwork-inl.h"
12
+ #include " tracing/traced_value.h"
10
13
#include " util-inl.h"
11
14
12
15
#include < memory>
@@ -104,16 +107,6 @@ static inline napi_env NewEnv(v8::Local<v8::Context> context) {
104
107
return result;
105
108
}
106
109
107
- static inline napi_callback_scope
108
- JsCallbackScopeFromV8CallbackScope (node::CallbackScope* s) {
109
- return reinterpret_cast <napi_callback_scope>(s);
110
- }
111
-
112
- static inline node::CallbackScope*
113
- V8CallbackScopeFromJsCallbackScope (napi_callback_scope s) {
114
- return reinterpret_cast <node::CallbackScope*>(s);
115
- }
116
-
117
110
static inline void trigger_fatal_exception (
118
111
napi_env env, v8::Local<v8::Value> local_err) {
119
112
v8::Local<v8::Message> local_msg =
@@ -435,6 +428,111 @@ class ThreadSafeFunction : public node::AsyncResource {
435
428
bool handles_closing;
436
429
};
437
430
431
+ /* *
432
+ * Compared to node::AsyncResource, the resource object in AsyncContext is
433
+ * gc-able. AsyncContext holds a weak reference to the resource object.
434
+ * AsyncContext::MakeCallback doesn't implicitly set the receiver of the
435
+ * callback to the resource object.
436
+ */
437
+ class AsyncContext {
438
+ public:
439
+ AsyncContext (node_napi_env env,
440
+ v8::Local<v8::Object> resource_object,
441
+ const v8::Local<v8::String> resource_name,
442
+ bool externally_managed_resource)
443
+ : env_(env) {
444
+ async_id_ = node_env ()->new_async_id ();
445
+ trigger_async_id_ = node_env ()->get_default_trigger_async_id ();
446
+ resource_.Reset (node_env ()->isolate (), resource_object);
447
+ lost_reference_ = false ;
448
+ if (externally_managed_resource) {
449
+ resource_.SetWeak (
450
+ this , AsyncContext::WeakCallback, v8::WeakCallbackType::kParameter );
451
+ }
452
+
453
+ node::AsyncWrap::EmitAsyncInit (node_env (),
454
+ resource_object,
455
+ resource_name,
456
+ async_id_,
457
+ trigger_async_id_);
458
+ }
459
+
460
+ ~AsyncContext () {
461
+ resource_.Reset ();
462
+ lost_reference_ = true ;
463
+ node::AsyncWrap::EmitDestroy (node_env (), async_id_);
464
+ }
465
+
466
+ inline v8::MaybeLocal<v8::Value> MakeCallback (
467
+ v8::Local<v8::Object> recv,
468
+ const v8::Local<v8::Function> callback,
469
+ int argc,
470
+ v8::Local<v8::Value> argv[]) {
471
+ EnsureReference ();
472
+ return node::InternalMakeCallback (node_env (),
473
+ resource (),
474
+ recv,
475
+ callback,
476
+ argc,
477
+ argv,
478
+ {async_id_, trigger_async_id_});
479
+ }
480
+
481
+ inline napi_callback_scope OpenCallbackScope () {
482
+ EnsureReference ();
483
+ napi_callback_scope it =
484
+ reinterpret_cast <napi_callback_scope>(new CallbackScope (this ));
485
+ env_->open_callback_scopes ++;
486
+ return it;
487
+ }
488
+
489
+ inline void EnsureReference () {
490
+ if (lost_reference_) {
491
+ const v8::HandleScope handle_scope (node_env ()->isolate ());
492
+ resource_.Reset (node_env ()->isolate (),
493
+ v8::Object::New (node_env ()->isolate ()));
494
+ lost_reference_ = false ;
495
+ }
496
+ }
497
+
498
+ inline node::Environment* node_env () { return env_->node_env (); }
499
+ inline v8::Local<v8::Object> resource () {
500
+ return resource_.Get (node_env ()->isolate ());
501
+ }
502
+ inline node::async_context async_context () {
503
+ return {async_id_, trigger_async_id_};
504
+ }
505
+
506
+ static inline void CloseCallbackScope (node_napi_env env,
507
+ napi_callback_scope s) {
508
+ CallbackScope* callback_scope = reinterpret_cast <CallbackScope*>(s);
509
+ delete callback_scope;
510
+ env->open_callback_scopes --;
511
+ }
512
+
513
+ static void WeakCallback (const v8::WeakCallbackInfo<AsyncContext>& data) {
514
+ AsyncContext* async_context = data.GetParameter ();
515
+ async_context->resource_ .Reset ();
516
+ async_context->lost_reference_ = true ;
517
+ }
518
+
519
+ private:
520
+ class CallbackScope : public node ::CallbackScope {
521
+ public:
522
+ explicit CallbackScope (AsyncContext* async_context)
523
+ : node::CallbackScope(async_context->node_env ()->isolate(),
524
+ async_context->resource_.Get(
525
+ async_context->node_env ()->isolate()),
526
+ async_context->async_context()) {}
527
+ };
528
+
529
+ node_napi_env env_;
530
+ double async_id_;
531
+ double trigger_async_id_;
532
+ v8::Global<v8::Object> resource_;
533
+ bool lost_reference_;
534
+ };
535
+
438
536
} // end of anonymous namespace
439
537
440
538
} // end of namespace v8impl
@@ -627,28 +725,19 @@ NAPI_NO_RETURN void napi_fatal_error(const char* location,
627
725
}
628
726
629
727
napi_status napi_open_callback_scope (napi_env env,
630
- napi_value resource_object ,
728
+ napi_value /* * ignored */ ,
631
729
napi_async_context async_context_handle,
632
730
napi_callback_scope* result) {
633
731
// Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw
634
732
// JS exceptions.
635
733
CHECK_ENV (env);
636
734
CHECK_ARG (env, result);
637
735
638
- v8::Local<v8::Context> context = env->context ();
639
-
640
- node::async_context* node_async_context =
641
- reinterpret_cast <node::async_context*>(async_context_handle);
642
-
643
- v8::Local<v8::Object> resource;
644
- CHECK_TO_OBJECT (env, context, resource, resource_object);
736
+ v8impl::AsyncContext* node_async_context =
737
+ reinterpret_cast <v8impl::AsyncContext*>(async_context_handle);
645
738
646
- *result = v8impl::JsCallbackScopeFromV8CallbackScope (
647
- new node::CallbackScope (env->isolate ,
648
- resource,
649
- *node_async_context));
739
+ *result = node_async_context->OpenCallbackScope ();
650
740
651
- env->open_callback_scopes ++;
652
741
return napi_clear_last_error (env);
653
742
}
654
743
@@ -661,8 +750,9 @@ napi_status napi_close_callback_scope(napi_env env, napi_callback_scope scope) {
661
750
return napi_callback_scope_mismatch;
662
751
}
663
752
664
- env->open_callback_scopes --;
665
- delete v8impl::V8CallbackScopeFromJsCallbackScope (scope);
753
+ v8impl::AsyncContext::CloseCallbackScope (reinterpret_cast <node_napi_env>(env),
754
+ scope);
755
+
666
756
return napi_clear_last_error (env);
667
757
}
668
758
@@ -678,20 +768,24 @@ napi_status napi_async_init(napi_env env,
678
768
v8::Local<v8::Context> context = env->context ();
679
769
680
770
v8::Local<v8::Object> v8_resource;
771
+ bool externally_managed_resource;
681
772
if (async_resource != nullptr ) {
682
773
CHECK_TO_OBJECT (env, context, v8_resource, async_resource);
774
+ externally_managed_resource = true ;
683
775
} else {
684
776
v8_resource = v8::Object::New (isolate);
777
+ externally_managed_resource = false ;
685
778
}
686
779
687
780
v8::Local<v8::String> v8_resource_name;
688
781
CHECK_TO_STRING (env, context, v8_resource_name, async_resource_name);
689
782
690
- // TODO(jasongin): Consider avoiding allocation here by using
691
- // a tagged pointer with 2×31 bit fields instead.
692
- node::async_context* async_context = new node::async_context ();
783
+ auto async_context =
784
+ new v8impl::AsyncContext (reinterpret_cast <node_napi_env>(env),
785
+ v8_resource,
786
+ v8_resource_name,
787
+ externally_managed_resource);
693
788
694
- *async_context = node::EmitAsyncInit (isolate, v8_resource, v8_resource_name);
695
789
*result = reinterpret_cast <napi_async_context>(async_context);
696
790
697
791
return napi_clear_last_error (env);
@@ -702,11 +796,8 @@ napi_status napi_async_destroy(napi_env env,
702
796
CHECK_ENV (env);
703
797
CHECK_ARG (env, async_context);
704
798
705
- node::async_context* node_async_context =
706
- reinterpret_cast <node::async_context*>(async_context);
707
- node::EmitAsyncDestroy (
708
- reinterpret_cast <node_napi_env>(env)->node_env (),
709
- *node_async_context);
799
+ v8impl::AsyncContext* node_async_context =
800
+ reinterpret_cast <v8impl::AsyncContext*>(async_context);
710
801
711
802
delete node_async_context;
712
803
@@ -734,17 +825,25 @@ napi_status napi_make_callback(napi_env env,
734
825
v8::Local<v8::Function> v8func;
735
826
CHECK_TO_FUNCTION (env, v8func, func);
736
827
737
- node::async_context* node_async_context =
738
- reinterpret_cast <node::async_context*>(async_context);
739
- if (node_async_context == nullptr ) {
740
- static node::async_context empty_context = { 0 , 0 };
741
- node_async_context = &empty_context;
742
- }
828
+ v8::MaybeLocal<v8::Value> callback_result;
743
829
744
- v8::MaybeLocal<v8::Value> callback_result = node::MakeCallback (
745
- env->isolate , v8recv, v8func, argc,
746
- reinterpret_cast <v8::Local<v8::Value>*>(const_cast <napi_value*>(argv)),
747
- *node_async_context);
830
+ if (async_context == nullptr ) {
831
+ callback_result = node::MakeCallback (
832
+ env->isolate ,
833
+ v8recv,
834
+ v8func,
835
+ argc,
836
+ reinterpret_cast <v8::Local<v8::Value>*>(const_cast <napi_value*>(argv)),
837
+ {0 , 0 });
838
+ } else {
839
+ auto node_async_context =
840
+ reinterpret_cast <v8impl::AsyncContext*>(async_context);
841
+ callback_result = node_async_context->MakeCallback (
842
+ v8recv,
843
+ v8func,
844
+ argc,
845
+ reinterpret_cast <v8::Local<v8::Value>*>(const_cast <napi_value*>(argv)));
846
+ }
748
847
749
848
if (try_catch.HasCaught ()) {
750
849
return napi_set_last_error (env, napi_pending_exception);
0 commit comments