@@ -23,15 +23,9 @@ struct napi_env__ {
2323 loop(_loop) {}
2424 ~napi_env__ () {
2525 last_exception.Reset ();
26- wrap_template.Reset ();
27- function_data_template.Reset ();
28- accessor_data_template.Reset ();
2926 }
3027 v8::Isolate* isolate;
3128 v8::Persistent<v8::Value> last_exception;
32- v8::Persistent<v8::ObjectTemplate> wrap_template;
33- v8::Persistent<v8::ObjectTemplate> function_data_template;
34- v8::Persistent<v8::ObjectTemplate> accessor_data_template;
3529 napi_extended_error_info last_error;
3630 int open_handle_scopes = 0 ;
3731 int open_callback_scopes = 0 ;
@@ -41,19 +35,6 @@ struct napi_env__ {
4135#define NAPI_PRIVATE_KEY (context, suffix ) \
4236 (node::Environment::GetCurrent((context))->napi_ ## suffix())
4337
44- #define ENV_OBJECT_TEMPLATE (env, prefix, destination, field_count ) \
45- do { \
46- if ((env)->prefix ## _template.IsEmpty ()) { \
47- (destination) = v8::ObjectTemplate::New (isolate); \
48- (destination)->SetInternalFieldCount ((field_count)); \
49- (env)->prefix ## _template.Reset (isolate, (destination)); \
50- } else { \
51- (destination) = v8::Local<v8::ObjectTemplate>::New ( \
52- isolate, env->prefix ## _template); \
53- } \
54- } while (0 )
55-
56-
5738#define RETURN_STATUS_IF_FALSE (env, condition, status ) \
5839 do { \
5940 if (!(condition)) { \
@@ -510,15 +491,51 @@ class TryCatch : public v8::TryCatch {
510491
511492// === Function napi_callback wrapper =================================
512493
513- static const int kDataIndex = 0 ;
514- static const int kEnvIndex = 1 ;
494+ // TODO(somebody): these constants can be removed with relevant changes
495+ // in CallbackWrapperBase<> and CallbackBundle.
496+ // Leave them for now just to keep the change set and cognitive load minimal.
497+ static const int kFunctionIndex = 0 ; // Used in CallbackBundle::cb[]
498+ static const int kGetterIndex = 0 ; // Used in CallbackBundle::cb[]
499+ static const int kSetterIndex = 1 ; // Used in CallbackBundle::cb[]
500+ static const int kCallbackCount = 2 ; // Used in CallbackBundle::cb[]
501+ // Max is "getter + setter" case
502+
503+ // Use this data structure to associate callback data with each N-API function
504+ // exposed to JavaScript. The structure is stored in a v8::External which gets
505+ // passed into our callback wrapper. This reduces the performance impact of
506+ // calling through N-API.
507+ // Ref: benchmark/misc/function_call
508+ // Discussion (incl. perf. data): https://github.com/nodejs/node/pull/21072
509+ class CallbackBundle {
510+ public:
511+ ~CallbackBundle () {
512+ handle.ClearWeak ();
513+ handle.Reset ();
514+ }
515515
516- static const int kFunctionIndex = 2 ;
517- static const int kFunctionFieldCount = 3 ;
516+ // Bind the lifecycle of `this` C++ object to a JavaScript object.
517+ // We never delete a CallbackBundle C++ object directly.
518+ void BindLifecycleTo (v8::Isolate* isolate, v8::Local<v8::Value> target) {
519+ handle.Reset (isolate, target);
520+ handle.SetWeak (this , WeakCallback, v8::WeakCallbackType::kParameter );
521+ }
518522
519- static const int kGetterIndex = 2 ;
520- static const int kSetterIndex = 3 ;
521- static const int kAccessorFieldCount = 4 ;
523+ napi_env env; // Necessary to invoke C++ NAPI callback
524+ void * cb_data; // The user provided callback data
525+ napi_callback cb[kCallbackCount ]; // Max capacity is 2 (getter + setter)
526+ v8::Persistent<v8::Value> handle; // Die with this JavaScript object
527+
528+ private:
529+ static void WeakCallback (v8::WeakCallbackInfo<CallbackBundle> const & info) {
530+ // Use the "WeakCallback mechanism" to delete the C++ `bundle` object.
531+ // This will be called when the v8::External containing `this` pointer
532+ // is being GC-ed.
533+ CallbackBundle* bundle = info.GetParameter ();
534+ if (bundle != nullptr ) {
535+ delete bundle;
536+ }
537+ }
538+ };
522539
523540// Base class extended by classes that wrap V8 function and property callback
524541// info.
@@ -550,10 +567,10 @@ class CallbackWrapperBase : public CallbackWrapper {
550567 : CallbackWrapper(JsValueFromV8LocalValue(cbinfo.This()),
551568 args_length,
552569 nullptr ),
553- _cbinfo (cbinfo),
554- _cbdata(v8::Local<v8::Object>::Cast(cbinfo.Data())) {
555- _data = v8::Local<v8::External>::Cast (_cbdata-> GetInternalField ( kDataIndex ))
556- -> Value () ;
570+ _cbinfo (cbinfo) {
571+ _bundle = reinterpret_cast <CallbackBundle*>(
572+ v8::Local<v8::External>::Cast (cbinfo. Data ())-> Value ());
573+ _data = _bundle-> cb_data ;
557574 }
558575
559576 napi_value GetNewTarget () override { return nullptr ; }
@@ -562,13 +579,10 @@ class CallbackWrapperBase : public CallbackWrapper {
562579 void InvokeCallback () {
563580 napi_callback_info cbinfo_wrapper = reinterpret_cast <napi_callback_info>(
564581 static_cast <CallbackWrapper*>(this ));
565- napi_callback cb = reinterpret_cast <napi_callback>(
566- v8::Local<v8::External>::Cast (
567- _cbdata->GetInternalField (kInternalFieldIndex ))->Value ());
568582
569- napi_env env = static_cast <napi_env>(
570- v8::Local<v8::External>:: Cast (
571- _cbdata-> GetInternalField ( kEnvIndex ))-> Value ()) ;
583+ // All other pointers we need are stored in `_bundle`
584+ napi_env env = _bundle-> env ;
585+ napi_callback cb = _bundle-> cb [ kInternalFieldIndex ] ;
572586
573587 napi_value result;
574588 NAPI_CALL_INTO_MODULE_THROW (env, result = cb (env, cbinfo_wrapper));
@@ -579,7 +593,7 @@ class CallbackWrapperBase : public CallbackWrapper {
579593 }
580594
581595 const Info& _cbinfo;
582- const v8::Local<v8::Object> _cbdata ;
596+ CallbackBundle* _bundle ;
583597};
584598
585599class FunctionCallbackWrapper
@@ -701,62 +715,35 @@ class SetterCallbackWrapper
701715// Creates an object to be made available to the static function callback
702716// wrapper, used to retrieve the native callback function and data pointer.
703717static
704- v8::Local<v8::Object> CreateFunctionCallbackData (napi_env env,
705- napi_callback cb,
706- void * data) {
707- v8::Isolate* isolate = env->isolate ;
708- v8::Local<v8::Context> context = isolate->GetCurrentContext ();
718+ v8::Local<v8::Value> CreateFunctionCallbackData (napi_env env,
719+ napi_callback cb,
720+ void * data) {
721+ CallbackBundle* bundle = new CallbackBundle ();
722+ bundle->cb [kFunctionIndex ] = cb;
723+ bundle->cb_data = data;
724+ bundle->env = env;
725+ v8::Local<v8::Value> cbdata = v8::External::New (env->isolate , bundle);
726+ bundle->BindLifecycleTo (env->isolate , cbdata);
709727
710- v8::Local<v8::ObjectTemplate> otpl;
711- ENV_OBJECT_TEMPLATE (env, function_data, otpl, v8impl::kFunctionFieldCount );
712- v8::Local<v8::Object> cbdata = otpl->NewInstance (context).ToLocalChecked ();
713-
714- cbdata->SetInternalField (
715- v8impl::kEnvIndex ,
716- v8::External::New (isolate, static_cast <void *>(env)));
717- cbdata->SetInternalField (
718- v8impl::kFunctionIndex ,
719- v8::External::New (isolate, reinterpret_cast <void *>(cb)));
720- cbdata->SetInternalField (
721- v8impl::kDataIndex ,
722- v8::External::New (isolate, data));
723728 return cbdata;
724729}
725730
726731// Creates an object to be made available to the static getter/setter
727732// callback wrapper, used to retrieve the native getter/setter callback
728733// function and data pointer.
729734static
730- v8::Local<v8::Object> CreateAccessorCallbackData (napi_env env,
731- napi_callback getter,
732- napi_callback setter,
733- void * data) {
734- v8::Isolate* isolate = env->isolate ;
735- v8::Local<v8::Context> context = isolate->GetCurrentContext ();
736-
737- v8::Local<v8::ObjectTemplate> otpl;
738- ENV_OBJECT_TEMPLATE (env, accessor_data, otpl, v8impl::kAccessorFieldCount );
739- v8::Local<v8::Object> cbdata = otpl->NewInstance (context).ToLocalChecked ();
740-
741- cbdata->SetInternalField (
742- v8impl::kEnvIndex ,
743- v8::External::New (isolate, static_cast <void *>(env)));
744-
745- if (getter != nullptr ) {
746- cbdata->SetInternalField (
747- v8impl::kGetterIndex ,
748- v8::External::New (isolate, reinterpret_cast <void *>(getter)));
749- }
750-
751- if (setter != nullptr ) {
752- cbdata->SetInternalField (
753- v8impl::kSetterIndex ,
754- v8::External::New (isolate, reinterpret_cast <void *>(setter)));
755- }
735+ v8::Local<v8::Value> CreateAccessorCallbackData (napi_env env,
736+ napi_callback getter,
737+ napi_callback setter,
738+ void * data) {
739+ CallbackBundle* bundle = new CallbackBundle ();
740+ bundle->cb [kGetterIndex ] = getter;
741+ bundle->cb [kSetterIndex ] = setter;
742+ bundle->cb_data = data;
743+ bundle->env = env;
744+ v8::Local<v8::Value> cbdata = v8::External::New (env->isolate , bundle);
745+ bundle->BindLifecycleTo (env->isolate , cbdata);
756746
757- cbdata->SetInternalField (
758- v8impl::kDataIndex ,
759- v8::External::New (isolate, data));
760747 return cbdata;
761748}
762749
@@ -1025,7 +1012,7 @@ napi_status napi_create_function(napi_env env,
10251012 v8::Isolate* isolate = env->isolate ;
10261013 v8::Local<v8::Function> return_value;
10271014 v8::EscapableHandleScope scope (isolate);
1028- v8::Local<v8::Object > cbdata =
1015+ v8::Local<v8::Value > cbdata =
10291016 v8impl::CreateFunctionCallbackData (env, cb, callback_data);
10301017
10311018 RETURN_STATUS_IF_FALSE (env, !cbdata.IsEmpty (), napi_generic_failure);
@@ -1065,7 +1052,7 @@ napi_status napi_define_class(napi_env env,
10651052 v8::Isolate* isolate = env->isolate ;
10661053
10671054 v8::EscapableHandleScope scope (isolate);
1068- v8::Local<v8::Object > cbdata =
1055+ v8::Local<v8::Value > cbdata =
10691056 v8impl::CreateFunctionCallbackData (env, constructor, callback_data);
10701057
10711058 RETURN_STATUS_IF_FALSE (env, !cbdata.IsEmpty (), napi_generic_failure);
@@ -1101,7 +1088,7 @@ napi_status napi_define_class(napi_env env,
11011088 // This code is similar to that in napi_define_properties(); the
11021089 // difference is it applies to a template instead of an object.
11031090 if (p->getter != nullptr || p->setter != nullptr ) {
1104- v8::Local<v8::Object > cbdata = v8impl::CreateAccessorCallbackData (
1091+ v8::Local<v8::Value > cbdata = v8impl::CreateAccessorCallbackData (
11051092 env, p->getter , p->setter , p->data );
11061093
11071094 tpl->PrototypeTemplate ()->SetAccessor (
@@ -1112,7 +1099,7 @@ napi_status napi_define_class(napi_env env,
11121099 v8::AccessControl::DEFAULT,
11131100 attributes);
11141101 } else if (p->method != nullptr ) {
1115- v8::Local<v8::Object > cbdata =
1102+ v8::Local<v8::Value > cbdata =
11161103 v8impl::CreateFunctionCallbackData (env, p->method , p->data );
11171104
11181105 RETURN_STATUS_IF_FALSE (env, !cbdata.IsEmpty (), napi_generic_failure);
@@ -1474,7 +1461,7 @@ napi_status napi_define_properties(napi_env env,
14741461 v8impl::V8PropertyAttributesFromDescriptor (p);
14751462
14761463 if (p->getter != nullptr || p->setter != nullptr ) {
1477- v8::Local<v8::Object > cbdata = v8impl::CreateAccessorCallbackData (
1464+ v8::Local<v8::Value > cbdata = v8impl::CreateAccessorCallbackData (
14781465 env,
14791466 p->getter ,
14801467 p->setter ,
@@ -1493,7 +1480,7 @@ napi_status napi_define_properties(napi_env env,
14931480 return napi_set_last_error (env, napi_invalid_arg);
14941481 }
14951482 } else if (p->method != nullptr ) {
1496- v8::Local<v8::Object > cbdata =
1483+ v8::Local<v8::Value > cbdata =
14971484 v8impl::CreateFunctionCallbackData (env, p->method , p->data );
14981485
14991486 RETURN_STATUS_IF_FALSE (env, !cbdata.IsEmpty (), napi_generic_failure);
0 commit comments