1+ #include " async_wrap-inl.h"
12#include " env-inl.h"
23#define NAPI_EXPERIMENTAL
34#include " js_native_api_v8.h"
5+ #include " memory_tracker-inl.h"
46#include " node_api.h"
57#include " node_binding.h"
68#include " node_buffer.h"
79#include " node_errors.h"
810#include " node_internals.h"
911#include " threadpoolwork-inl.h"
12+ #include " tracing/traced_value.h"
1013#include " util-inl.h"
1114
1215#include < memory>
@@ -104,16 +107,6 @@ static inline napi_env NewEnv(v8::Local<v8::Context> context) {
104107 return result;
105108}
106109
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-
117110static inline void trigger_fatal_exception (
118111 napi_env env, v8::Local<v8::Value> local_err) {
119112 v8::Local<v8::Message> local_msg =
@@ -435,6 +428,111 @@ class ThreadSafeFunction : public node::AsyncResource {
435428 bool handles_closing;
436429};
437430
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+
438536} // end of anonymous namespace
439537
440538} // end of namespace v8impl
@@ -627,28 +725,19 @@ NAPI_NO_RETURN void napi_fatal_error(const char* location,
627725}
628726
629727napi_status napi_open_callback_scope (napi_env env,
630- napi_value resource_object ,
728+ napi_value /* * ignored */ ,
631729 napi_async_context async_context_handle,
632730 napi_callback_scope* result) {
633731 // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw
634732 // JS exceptions.
635733 CHECK_ENV (env);
636734 CHECK_ARG (env, result);
637735
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);
645738
646- *result = v8impl::JsCallbackScopeFromV8CallbackScope (
647- new node::CallbackScope (env->isolate ,
648- resource,
649- *node_async_context));
739+ *result = node_async_context->OpenCallbackScope ();
650740
651- env->open_callback_scopes ++;
652741 return napi_clear_last_error (env);
653742}
654743
@@ -661,8 +750,9 @@ napi_status napi_close_callback_scope(napi_env env, napi_callback_scope scope) {
661750 return napi_callback_scope_mismatch;
662751 }
663752
664- env->open_callback_scopes --;
665- delete v8impl::V8CallbackScopeFromJsCallbackScope (scope);
753+ v8impl::AsyncContext::CloseCallbackScope (reinterpret_cast <node_napi_env>(env),
754+ scope);
755+
666756 return napi_clear_last_error (env);
667757}
668758
@@ -678,20 +768,24 @@ napi_status napi_async_init(napi_env env,
678768 v8::Local<v8::Context> context = env->context ();
679769
680770 v8::Local<v8::Object> v8_resource;
771+ bool externally_managed_resource;
681772 if (async_resource != nullptr ) {
682773 CHECK_TO_OBJECT (env, context, v8_resource, async_resource);
774+ externally_managed_resource = true ;
683775 } else {
684776 v8_resource = v8::Object::New (isolate);
777+ externally_managed_resource = false ;
685778 }
686779
687780 v8::Local<v8::String> v8_resource_name;
688781 CHECK_TO_STRING (env, context, v8_resource_name, async_resource_name);
689782
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);
693788
694- *async_context = node::EmitAsyncInit (isolate, v8_resource, v8_resource_name);
695789 *result = reinterpret_cast <napi_async_context>(async_context);
696790
697791 return napi_clear_last_error (env);
@@ -702,11 +796,8 @@ napi_status napi_async_destroy(napi_env env,
702796 CHECK_ENV (env);
703797 CHECK_ARG (env, async_context);
704798
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);
710801
711802 delete node_async_context;
712803
@@ -734,17 +825,25 @@ napi_status napi_make_callback(napi_env env,
734825 v8::Local<v8::Function> v8func;
735826 CHECK_TO_FUNCTION (env, v8func, func);
736827
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;
743829
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+ }
748847
749848 if (try_catch.HasCaught ()) {
750849 return napi_set_last_error (env, napi_pending_exception);
0 commit comments