@@ -23,15 +23,9 @@ struct napi_env__ {
23
23
loop(_loop) {}
24
24
~napi_env__ () {
25
25
last_exception.Reset ();
26
- wrap_template.Reset ();
27
- function_data_template.Reset ();
28
- accessor_data_template.Reset ();
29
26
}
30
27
v8::Isolate* isolate;
31
28
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;
35
29
napi_extended_error_info last_error;
36
30
int open_handle_scopes = 0 ;
37
31
int open_callback_scopes = 0 ;
@@ -41,19 +35,6 @@ struct napi_env__ {
41
35
#define NAPI_PRIVATE_KEY (context, suffix ) \
42
36
(node::Environment::GetCurrent((context))->napi_ ## suffix())
43
37
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
-
57
38
#define RETURN_STATUS_IF_FALSE (env, condition, status ) \
58
39
do { \
59
40
if (!(condition)) { \
@@ -510,15 +491,51 @@ class TryCatch : public v8::TryCatch {
510
491
511
492
// === Function napi_callback wrapper =================================
512
493
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
+ }
515
515
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
+ }
518
522
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
+ };
522
539
523
540
// Base class extended by classes that wrap V8 function and property callback
524
541
// info.
@@ -550,10 +567,10 @@ class CallbackWrapperBase : public CallbackWrapper {
550
567
: CallbackWrapper(JsValueFromV8LocalValue(cbinfo.This()),
551
568
args_length,
552
569
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 ;
557
574
}
558
575
559
576
napi_value GetNewTarget () override { return nullptr ; }
@@ -562,13 +579,10 @@ class CallbackWrapperBase : public CallbackWrapper {
562
579
void InvokeCallback () {
563
580
napi_callback_info cbinfo_wrapper = reinterpret_cast <napi_callback_info>(
564
581
static_cast <CallbackWrapper*>(this ));
565
- napi_callback cb = reinterpret_cast <napi_callback>(
566
- v8::Local<v8::External>::Cast (
567
- _cbdata->GetInternalField (kInternalFieldIndex ))->Value ());
568
582
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 ] ;
572
586
573
587
napi_value result;
574
588
NAPI_CALL_INTO_MODULE_THROW (env, result = cb (env, cbinfo_wrapper));
@@ -579,7 +593,7 @@ class CallbackWrapperBase : public CallbackWrapper {
579
593
}
580
594
581
595
const Info& _cbinfo;
582
- const v8::Local<v8::Object> _cbdata ;
596
+ CallbackBundle* _bundle ;
583
597
};
584
598
585
599
class FunctionCallbackWrapper
@@ -701,62 +715,35 @@ class SetterCallbackWrapper
701
715
// Creates an object to be made available to the static function callback
702
716
// wrapper, used to retrieve the native callback function and data pointer.
703
717
static
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);
709
727
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));
723
728
return cbdata;
724
729
}
725
730
726
731
// Creates an object to be made available to the static getter/setter
727
732
// callback wrapper, used to retrieve the native getter/setter callback
728
733
// function and data pointer.
729
734
static
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);
756
746
757
- cbdata->SetInternalField (
758
- v8impl::kDataIndex ,
759
- v8::External::New (isolate, data));
760
747
return cbdata;
761
748
}
762
749
@@ -1025,7 +1012,7 @@ napi_status napi_create_function(napi_env env,
1025
1012
v8::Isolate* isolate = env->isolate ;
1026
1013
v8::Local<v8::Function> return_value;
1027
1014
v8::EscapableHandleScope scope (isolate);
1028
- v8::Local<v8::Object > cbdata =
1015
+ v8::Local<v8::Value > cbdata =
1029
1016
v8impl::CreateFunctionCallbackData (env, cb, callback_data);
1030
1017
1031
1018
RETURN_STATUS_IF_FALSE (env, !cbdata.IsEmpty (), napi_generic_failure);
@@ -1065,7 +1052,7 @@ napi_status napi_define_class(napi_env env,
1065
1052
v8::Isolate* isolate = env->isolate ;
1066
1053
1067
1054
v8::EscapableHandleScope scope (isolate);
1068
- v8::Local<v8::Object > cbdata =
1055
+ v8::Local<v8::Value > cbdata =
1069
1056
v8impl::CreateFunctionCallbackData (env, constructor, callback_data);
1070
1057
1071
1058
RETURN_STATUS_IF_FALSE (env, !cbdata.IsEmpty (), napi_generic_failure);
@@ -1101,7 +1088,7 @@ napi_status napi_define_class(napi_env env,
1101
1088
// This code is similar to that in napi_define_properties(); the
1102
1089
// difference is it applies to a template instead of an object.
1103
1090
if (p->getter != nullptr || p->setter != nullptr ) {
1104
- v8::Local<v8::Object > cbdata = v8impl::CreateAccessorCallbackData (
1091
+ v8::Local<v8::Value > cbdata = v8impl::CreateAccessorCallbackData (
1105
1092
env, p->getter , p->setter , p->data );
1106
1093
1107
1094
tpl->PrototypeTemplate ()->SetAccessor (
@@ -1112,7 +1099,7 @@ napi_status napi_define_class(napi_env env,
1112
1099
v8::AccessControl::DEFAULT,
1113
1100
attributes);
1114
1101
} else if (p->method != nullptr ) {
1115
- v8::Local<v8::Object > cbdata =
1102
+ v8::Local<v8::Value > cbdata =
1116
1103
v8impl::CreateFunctionCallbackData (env, p->method , p->data );
1117
1104
1118
1105
RETURN_STATUS_IF_FALSE (env, !cbdata.IsEmpty (), napi_generic_failure);
@@ -1474,7 +1461,7 @@ napi_status napi_define_properties(napi_env env,
1474
1461
v8impl::V8PropertyAttributesFromDescriptor (p);
1475
1462
1476
1463
if (p->getter != nullptr || p->setter != nullptr ) {
1477
- v8::Local<v8::Object > cbdata = v8impl::CreateAccessorCallbackData (
1464
+ v8::Local<v8::Value > cbdata = v8impl::CreateAccessorCallbackData (
1478
1465
env,
1479
1466
p->getter ,
1480
1467
p->setter ,
@@ -1493,7 +1480,7 @@ napi_status napi_define_properties(napi_env env,
1493
1480
return napi_set_last_error (env, napi_invalid_arg);
1494
1481
}
1495
1482
} else if (p->method != nullptr ) {
1496
- v8::Local<v8::Object > cbdata =
1483
+ v8::Local<v8::Value > cbdata =
1497
1484
v8impl::CreateFunctionCallbackData (env, p->method , p->data );
1498
1485
1499
1486
RETURN_STATUS_IF_FALSE (env, !cbdata.IsEmpty (), napi_generic_failure);
0 commit comments