@@ -23,8 +23,6 @@ struct napi_env__ {
2323 loop(_loop) {}
2424 v8::Isolate* isolate;
2525 node::Persistent<v8::Value> last_exception;
26- node::Persistent<v8::ObjectTemplate> function_data_template;
27- node::Persistent<v8::ObjectTemplate> accessor_data_template;
2826 napi_extended_error_info last_error;
2927 int open_handle_scopes = 0 ;
3028 int open_callback_scopes = 0 ;
@@ -34,19 +32,6 @@ struct napi_env__ {
3432#define NAPI_PRIVATE_KEY (context, suffix ) \
3533 (node::Environment::GetCurrent((context))->napi_ ## suffix())
3634
37- #define ENV_OBJECT_TEMPLATE (env, prefix, destination, field_count ) \
38- do { \
39- if ((env)->prefix ## _template.IsEmpty ()) { \
40- (destination) = v8::ObjectTemplate::New (isolate); \
41- (destination)->SetInternalFieldCount ((field_count)); \
42- (env)->prefix ## _template.Reset (isolate, (destination)); \
43- } else { \
44- (destination) = v8::Local<v8::ObjectTemplate>::New ( \
45- isolate, env->prefix ## _template); \
46- } \
47- } while (0 )
48-
49-
5035#define RETURN_STATUS_IF_FALSE (env, condition, status ) \
5136 do { \
5237 if (!(condition)) { \
@@ -491,15 +476,45 @@ class TryCatch : public v8::TryCatch {
491476
492477// === Function napi_callback wrapper =================================
493478
494- static const int kDataIndex = 0 ;
495- static const int kEnvIndex = 1 ;
479+ // TODO(somebody): these constants can be removed with relevant changes
480+ // in CallbackWrapperBase<> and CallbackBundle.
481+ // Leave them for now just to keep the change set and cognitive load minimal.
482+ static const int kFunctionIndex = 0 ; // Used in CallbackBundle::cb[]
483+ static const int kGetterIndex = 0 ; // Used in CallbackBundle::cb[]
484+ static const int kSetterIndex = 1 ; // Used in CallbackBundle::cb[]
485+ static const int kCallbackCount = 2 ; // Used in CallbackBundle::cb[]
486+ // Max is "getter + setter" case
487+
488+ // Use this data structure to associate callback data with each N-API function
489+ // exposed to JavaScript. The structure is stored in a v8::External which gets
490+ // passed into our callback wrapper. This reduces the performance impact of
491+ // calling through N-API.
492+ // Ref: benchmark/misc/function_call
493+ // Discussion (incl. perf. data): https://github.com/nodejs/node/pull/21072
494+ struct CallbackBundle {
495+ // Bind the lifecycle of `this` C++ object to a JavaScript object.
496+ // We never delete a CallbackBundle C++ object directly.
497+ void BindLifecycleTo (v8::Isolate* isolate, v8::Local<v8::Value> target) {
498+ handle.Reset (isolate, target);
499+ handle.SetWeak (this , WeakCallback, v8::WeakCallbackType::kParameter );
500+ }
501+
502+ napi_env env; // Necessary to invoke C++ NAPI callback
503+ void * cb_data; // The user provided callback data
504+ napi_callback cb[kCallbackCount ]; // Max capacity is 2 (getter + setter)
505+ node::Persistent<v8::Value> handle; // Die with this JavaScript object
496506
497- static const int kFunctionIndex = 2 ;
498- static const int kFunctionFieldCount = 3 ;
499-
500- static const int kGetterIndex = 2 ;
501- static const int kSetterIndex = 3 ;
502- static const int kAccessorFieldCount = 4 ;
507+ private:
508+ static void WeakCallback (v8::WeakCallbackInfo<CallbackBundle> const & info) {
509+ // Use the "WeakCallback mechanism" to delete the C++ `bundle` object.
510+ // This will be called when the v8::External containing `this` pointer
511+ // is being GC-ed.
512+ CallbackBundle* bundle = info.GetParameter ();
513+ if (bundle != nullptr ) {
514+ delete bundle;
515+ }
516+ }
517+ };
503518
504519// Base class extended by classes that wrap V8 function and property callback
505520// info.
@@ -531,10 +546,10 @@ class CallbackWrapperBase : public CallbackWrapper {
531546 : CallbackWrapper(JsValueFromV8LocalValue(cbinfo.This()),
532547 args_length,
533548 nullptr ),
534- _cbinfo (cbinfo),
535- _cbdata(v8::Local<v8::Object>::Cast(cbinfo.Data())) {
536- _data = v8::Local<v8::External>::Cast (_cbdata-> GetInternalField ( kDataIndex ))
537- -> Value () ;
549+ _cbinfo (cbinfo) {
550+ _bundle = reinterpret_cast <CallbackBundle*>(
551+ v8::Local<v8::External>::Cast (cbinfo. Data ())-> Value ());
552+ _data = _bundle-> cb_data ;
538553 }
539554
540555 napi_value GetNewTarget () override { return nullptr ; }
@@ -543,13 +558,10 @@ class CallbackWrapperBase : public CallbackWrapper {
543558 void InvokeCallback () {
544559 napi_callback_info cbinfo_wrapper = reinterpret_cast <napi_callback_info>(
545560 static_cast <CallbackWrapper*>(this ));
546- napi_callback cb = reinterpret_cast <napi_callback>(
547- v8::Local<v8::External>::Cast (
548- _cbdata->GetInternalField (kInternalFieldIndex ))->Value ());
549561
550- napi_env env = static_cast <napi_env>(
551- v8::Local<v8::External>:: Cast (
552- _cbdata-> GetInternalField ( kEnvIndex ))-> Value ()) ;
562+ // All other pointers we need are stored in `_bundle`
563+ napi_env env = _bundle-> env ;
564+ napi_callback cb = _bundle-> cb [ kInternalFieldIndex ] ;
553565
554566 napi_value result;
555567 NAPI_CALL_INTO_MODULE_THROW (env, result = cb (env, cbinfo_wrapper));
@@ -560,7 +572,7 @@ class CallbackWrapperBase : public CallbackWrapper {
560572 }
561573
562574 const Info& _cbinfo;
563- const v8::Local<v8::Object> _cbdata ;
575+ CallbackBundle* _bundle ;
564576};
565577
566578class FunctionCallbackWrapper
@@ -682,62 +694,35 @@ class SetterCallbackWrapper
682694// Creates an object to be made available to the static function callback
683695// wrapper, used to retrieve the native callback function and data pointer.
684696static
685- v8::Local<v8::Object> CreateFunctionCallbackData (napi_env env,
686- napi_callback cb,
687- void * data) {
688- v8::Isolate* isolate = env->isolate ;
689- v8::Local<v8::Context> context = isolate->GetCurrentContext ();
697+ v8::Local<v8::Value> CreateFunctionCallbackData (napi_env env,
698+ napi_callback cb,
699+ void * data) {
700+ CallbackBundle* bundle = new CallbackBundle ();
701+ bundle->cb [kFunctionIndex ] = cb;
702+ bundle->cb_data = data;
703+ bundle->env = env;
704+ v8::Local<v8::Value> cbdata = v8::External::New (env->isolate , bundle);
705+ bundle->BindLifecycleTo (env->isolate , cbdata);
690706
691- v8::Local<v8::ObjectTemplate> otpl;
692- ENV_OBJECT_TEMPLATE (env, function_data, otpl, v8impl::kFunctionFieldCount );
693- v8::Local<v8::Object> cbdata = otpl->NewInstance (context).ToLocalChecked ();
694-
695- cbdata->SetInternalField (
696- v8impl::kEnvIndex ,
697- v8::External::New (isolate, static_cast <void *>(env)));
698- cbdata->SetInternalField (
699- v8impl::kFunctionIndex ,
700- v8::External::New (isolate, reinterpret_cast <void *>(cb)));
701- cbdata->SetInternalField (
702- v8impl::kDataIndex ,
703- v8::External::New (isolate, data));
704707 return cbdata;
705708}
706709
707710// Creates an object to be made available to the static getter/setter
708711// callback wrapper, used to retrieve the native getter/setter callback
709712// function and data pointer.
710713static
711- v8::Local<v8::Object> CreateAccessorCallbackData (napi_env env,
712- napi_callback getter,
713- napi_callback setter,
714- void * data) {
715- v8::Isolate* isolate = env->isolate ;
716- v8::Local<v8::Context> context = isolate->GetCurrentContext ();
717-
718- v8::Local<v8::ObjectTemplate> otpl;
719- ENV_OBJECT_TEMPLATE (env, accessor_data, otpl, v8impl::kAccessorFieldCount );
720- v8::Local<v8::Object> cbdata = otpl->NewInstance (context).ToLocalChecked ();
721-
722- cbdata->SetInternalField (
723- v8impl::kEnvIndex ,
724- v8::External::New (isolate, static_cast <void *>(env)));
725-
726- if (getter != nullptr ) {
727- cbdata->SetInternalField (
728- v8impl::kGetterIndex ,
729- v8::External::New (isolate, reinterpret_cast <void *>(getter)));
730- }
731-
732- if (setter != nullptr ) {
733- cbdata->SetInternalField (
734- v8impl::kSetterIndex ,
735- v8::External::New (isolate, reinterpret_cast <void *>(setter)));
736- }
714+ v8::Local<v8::Value> CreateAccessorCallbackData (napi_env env,
715+ napi_callback getter,
716+ napi_callback setter,
717+ void * data) {
718+ CallbackBundle* bundle = new CallbackBundle ();
719+ bundle->cb [kGetterIndex ] = getter;
720+ bundle->cb [kSetterIndex ] = setter;
721+ bundle->cb_data = data;
722+ bundle->env = env;
723+ v8::Local<v8::Value> cbdata = v8::External::New (env->isolate , bundle);
724+ bundle->BindLifecycleTo (env->isolate , cbdata);
737725
738- cbdata->SetInternalField (
739- v8impl::kDataIndex ,
740- v8::External::New (isolate, data));
741726 return cbdata;
742727}
743728
@@ -1038,7 +1023,7 @@ napi_status napi_create_function(napi_env env,
10381023 v8::Isolate* isolate = env->isolate ;
10391024 v8::Local<v8::Function> return_value;
10401025 v8::EscapableHandleScope scope (isolate);
1041- v8::Local<v8::Object > cbdata =
1026+ v8::Local<v8::Value > cbdata =
10421027 v8impl::CreateFunctionCallbackData (env, cb, callback_data);
10431028
10441029 RETURN_STATUS_IF_FALSE (env, !cbdata.IsEmpty (), napi_generic_failure);
@@ -1078,7 +1063,7 @@ napi_status napi_define_class(napi_env env,
10781063 v8::Isolate* isolate = env->isolate ;
10791064
10801065 v8::EscapableHandleScope scope (isolate);
1081- v8::Local<v8::Object > cbdata =
1066+ v8::Local<v8::Value > cbdata =
10821067 v8impl::CreateFunctionCallbackData (env, constructor, callback_data);
10831068
10841069 RETURN_STATUS_IF_FALSE (env, !cbdata.IsEmpty (), napi_generic_failure);
@@ -1114,7 +1099,7 @@ napi_status napi_define_class(napi_env env,
11141099 // This code is similar to that in napi_define_properties(); the
11151100 // difference is it applies to a template instead of an object.
11161101 if (p->getter != nullptr || p->setter != nullptr ) {
1117- v8::Local<v8::Object > cbdata = v8impl::CreateAccessorCallbackData (
1102+ v8::Local<v8::Value > cbdata = v8impl::CreateAccessorCallbackData (
11181103 env, p->getter , p->setter , p->data );
11191104
11201105 tpl->PrototypeTemplate ()->SetAccessor (
@@ -1125,7 +1110,7 @@ napi_status napi_define_class(napi_env env,
11251110 v8::AccessControl::DEFAULT,
11261111 attributes);
11271112 } else if (p->method != nullptr ) {
1128- v8::Local<v8::Object > cbdata =
1113+ v8::Local<v8::Value > cbdata =
11291114 v8impl::CreateFunctionCallbackData (env, p->method , p->data );
11301115
11311116 RETURN_STATUS_IF_FALSE (env, !cbdata.IsEmpty (), napi_generic_failure);
@@ -1487,7 +1472,7 @@ napi_status napi_define_properties(napi_env env,
14871472 v8impl::V8PropertyAttributesFromDescriptor (p);
14881473
14891474 if (p->getter != nullptr || p->setter != nullptr ) {
1490- v8::Local<v8::Object > cbdata = v8impl::CreateAccessorCallbackData (
1475+ v8::Local<v8::Value > cbdata = v8impl::CreateAccessorCallbackData (
14911476 env,
14921477 p->getter ,
14931478 p->setter ,
@@ -1506,7 +1491,7 @@ napi_status napi_define_properties(napi_env env,
15061491 return napi_set_last_error (env, napi_invalid_arg);
15071492 }
15081493 } else if (p->method != nullptr ) {
1509- v8::Local<v8::Object > cbdata =
1494+ v8::Local<v8::Value > cbdata =
15101495 v8impl::CreateFunctionCallbackData (env, p->method , p->data );
15111496
15121497 RETURN_STATUS_IF_FALSE (env, !cbdata.IsEmpty (), napi_generic_failure);
0 commit comments