@@ -23,8 +23,6 @@ struct napi_env__ {
23
23
loop(_loop) {}
24
24
v8::Isolate* isolate;
25
25
node::Persistent<v8::Value> last_exception;
26
- node::Persistent<v8::ObjectTemplate> function_data_template;
27
- node::Persistent<v8::ObjectTemplate> accessor_data_template;
28
26
napi_extended_error_info last_error;
29
27
int open_handle_scopes = 0 ;
30
28
int open_callback_scopes = 0 ;
@@ -34,19 +32,6 @@ struct napi_env__ {
34
32
#define NAPI_PRIVATE_KEY (context, suffix ) \
35
33
(node::Environment::GetCurrent((context))->napi_ ## suffix())
36
34
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
-
50
35
#define RETURN_STATUS_IF_FALSE (env, condition, status ) \
51
36
do { \
52
37
if (!(condition)) { \
@@ -491,15 +476,45 @@ class TryCatch : public v8::TryCatch {
491
476
492
477
// === Function napi_callback wrapper =================================
493
478
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
496
506
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
+ };
503
518
504
519
// Base class extended by classes that wrap V8 function and property callback
505
520
// info.
@@ -531,10 +546,10 @@ class CallbackWrapperBase : public CallbackWrapper {
531
546
: CallbackWrapper(JsValueFromV8LocalValue(cbinfo.This()),
532
547
args_length,
533
548
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 ;
538
553
}
539
554
540
555
napi_value GetNewTarget () override { return nullptr ; }
@@ -543,13 +558,10 @@ class CallbackWrapperBase : public CallbackWrapper {
543
558
void InvokeCallback () {
544
559
napi_callback_info cbinfo_wrapper = reinterpret_cast <napi_callback_info>(
545
560
static_cast <CallbackWrapper*>(this ));
546
- napi_callback cb = reinterpret_cast <napi_callback>(
547
- v8::Local<v8::External>::Cast (
548
- _cbdata->GetInternalField (kInternalFieldIndex ))->Value ());
549
561
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 ] ;
553
565
554
566
napi_value result;
555
567
NAPI_CALL_INTO_MODULE_THROW (env, result = cb (env, cbinfo_wrapper));
@@ -560,7 +572,7 @@ class CallbackWrapperBase : public CallbackWrapper {
560
572
}
561
573
562
574
const Info& _cbinfo;
563
- const v8::Local<v8::Object> _cbdata ;
575
+ CallbackBundle* _bundle ;
564
576
};
565
577
566
578
class FunctionCallbackWrapper
@@ -682,62 +694,35 @@ class SetterCallbackWrapper
682
694
// Creates an object to be made available to the static function callback
683
695
// wrapper, used to retrieve the native callback function and data pointer.
684
696
static
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);
690
706
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));
704
707
return cbdata;
705
708
}
706
709
707
710
// Creates an object to be made available to the static getter/setter
708
711
// callback wrapper, used to retrieve the native getter/setter callback
709
712
// function and data pointer.
710
713
static
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);
737
725
738
- cbdata->SetInternalField (
739
- v8impl::kDataIndex ,
740
- v8::External::New (isolate, data));
741
726
return cbdata;
742
727
}
743
728
@@ -1038,7 +1023,7 @@ napi_status napi_create_function(napi_env env,
1038
1023
v8::Isolate* isolate = env->isolate ;
1039
1024
v8::Local<v8::Function> return_value;
1040
1025
v8::EscapableHandleScope scope (isolate);
1041
- v8::Local<v8::Object > cbdata =
1026
+ v8::Local<v8::Value > cbdata =
1042
1027
v8impl::CreateFunctionCallbackData (env, cb, callback_data);
1043
1028
1044
1029
RETURN_STATUS_IF_FALSE (env, !cbdata.IsEmpty (), napi_generic_failure);
@@ -1078,7 +1063,7 @@ napi_status napi_define_class(napi_env env,
1078
1063
v8::Isolate* isolate = env->isolate ;
1079
1064
1080
1065
v8::EscapableHandleScope scope (isolate);
1081
- v8::Local<v8::Object > cbdata =
1066
+ v8::Local<v8::Value > cbdata =
1082
1067
v8impl::CreateFunctionCallbackData (env, constructor, callback_data);
1083
1068
1084
1069
RETURN_STATUS_IF_FALSE (env, !cbdata.IsEmpty (), napi_generic_failure);
@@ -1114,7 +1099,7 @@ napi_status napi_define_class(napi_env env,
1114
1099
// This code is similar to that in napi_define_properties(); the
1115
1100
// difference is it applies to a template instead of an object.
1116
1101
if (p->getter != nullptr || p->setter != nullptr ) {
1117
- v8::Local<v8::Object > cbdata = v8impl::CreateAccessorCallbackData (
1102
+ v8::Local<v8::Value > cbdata = v8impl::CreateAccessorCallbackData (
1118
1103
env, p->getter , p->setter , p->data );
1119
1104
1120
1105
tpl->PrototypeTemplate ()->SetAccessor (
@@ -1125,7 +1110,7 @@ napi_status napi_define_class(napi_env env,
1125
1110
v8::AccessControl::DEFAULT,
1126
1111
attributes);
1127
1112
} else if (p->method != nullptr ) {
1128
- v8::Local<v8::Object > cbdata =
1113
+ v8::Local<v8::Value > cbdata =
1129
1114
v8impl::CreateFunctionCallbackData (env, p->method , p->data );
1130
1115
1131
1116
RETURN_STATUS_IF_FALSE (env, !cbdata.IsEmpty (), napi_generic_failure);
@@ -1487,7 +1472,7 @@ napi_status napi_define_properties(napi_env env,
1487
1472
v8impl::V8PropertyAttributesFromDescriptor (p);
1488
1473
1489
1474
if (p->getter != nullptr || p->setter != nullptr ) {
1490
- v8::Local<v8::Object > cbdata = v8impl::CreateAccessorCallbackData (
1475
+ v8::Local<v8::Value > cbdata = v8impl::CreateAccessorCallbackData (
1491
1476
env,
1492
1477
p->getter ,
1493
1478
p->setter ,
@@ -1506,7 +1491,7 @@ napi_status napi_define_properties(napi_env env,
1506
1491
return napi_set_last_error (env, napi_invalid_arg);
1507
1492
}
1508
1493
} else if (p->method != nullptr ) {
1509
- v8::Local<v8::Object > cbdata =
1494
+ v8::Local<v8::Value > cbdata =
1510
1495
v8impl::CreateFunctionCallbackData (env, p->method , p->data );
1511
1496
1512
1497
RETURN_STATUS_IF_FALSE (env, !cbdata.IsEmpty (), napi_generic_failure);
0 commit comments