@@ -12,7 +12,7 @@ There are three options for implementing addons:
1212
1313* [ Node-API] [ ] (recommended)
1414* ` nan ` ([ Native Abstractions for Node.js] [ ] )
15- * direct use of internal V8, libuv, and Node.js libraries
15+ * direct use of public V8, libuv, and Node.js interfaces
1616
1717This rest of this document focuses on the latter, requiring
1818knowledge of multiple components and APIs:
@@ -35,8 +35,8 @@ knowledge of multiple components and APIs:
3535 offloading work via libuv to non-blocking system operations, worker threads,
3636 or a custom use of libuv threads.
3737
38- * Internal Node.js libraries : Node.js itself exports C++ APIs that addons can
39- use, the most important of which is the ` node::ObjectWrap ` class .
38+ * Public Node.js interfaces : Node.js itself exports C++ APIs that addons
39+ and embedders can make use of .
4040
4141* Other statically linked libraries (including OpenSSL): These
4242 other libraries are located in the ` deps/ ` directory in the Node.js source
@@ -815,7 +815,8 @@ NODE_MODULE(NODE_GYP_MODULE_NAME, InitAll)
815815} // namespace demo
816816```
817817
818- Then, in `myobject.h`, the wrapper class inherits from `node::ObjectWrap`:
818+ Then, in `myobject.h`, the wrapper class inherits from a helper `ObjectWrap`
819+ which handles tying the lifetime of the C++ object to the exposed JS object:
819820
820821<!-- addon-verify-file wrapping_c_objects/myobject.h -->
821822
@@ -825,11 +826,11 @@ Then, in `myobject.h`, the wrapper class inherits from `node::ObjectWrap`:
825826#define MYOBJECT_H
826827
827828#include <node.h>
828- #include <node_object_wrap.h>
829+ #include "object_wrap.h"
829830
830831namespace demo {
831832
832- class MyObject : public node:: ObjectWrap {
833+ class MyObject : public ObjectWrap {
833834 public:
834835 static void Init(v8::Local<v8::Object> exports);
835836
@@ -848,6 +849,68 @@ class MyObject : public node::ObjectWrap {
848849#endif
849850```
850851
852+ where ` ObjectWrap ` is defined as
853+
854+ <!-- addon-verify-file wrapping_c_objects/object_wrap.h factory_of_wrapped_objects/object_wrap.h passing_wrapped_objects_around/object_wrap.h -->
855+
856+ ``` cpp
857+ // object_wrap.h
858+ #ifndef OBJECTWRAP_H
859+ #define OBJECTWRAP_H
860+
861+ #include <node.h>
862+
863+ namespace demo {
864+
865+ class ObjectWrap {
866+ public:
867+ ObjectWrap() : isolate_ (v8::Isolate::GetCurrent()) {
868+ node::AddEnvironmentCleanupHook(isolate_ , CleanupHook, this);
869+ }
870+
871+ virtual ~ ObjectWrap() {
872+ node::RemoveEnvironmentCleanupHook(isolate_ , CleanupHook, this);
873+ }
874+
875+ template <class T >
876+ static T* Unwrap(v8::Local< v8::Object > handle) {
877+ void* ptr = handle->GetAlignedPointerFromInternalField(
878+ 0, v8::kEmbedderDataTypeTagDefault);
879+ ObjectWrap* wrap = static_cast<ObjectWrap* >(ptr);
880+ return static_cast<T* >(wrap);
881+ }
882+
883+ v8::Local< v8::Object > object() {
884+ return handle_ .Get(isolate_ );
885+ }
886+
887+ protected:
888+ inline void Wrap(v8::Local< v8::Object > handle) {
889+ handle->SetAlignedPointerInInternalField(
890+ 0, this, v8::kEmbedderDataTypeTagDefault);
891+ handle_ .Reset(isolate_ , handle);
892+ }
893+
894+ inline void MakeWeak() {
895+ handle_ .SetWeak(this, WeakCallback, v8::WeakCallbackType::kParameter);
896+ }
897+
898+ private:
899+ static void WeakCallback(
900+ const v8::WeakCallbackInfo<ObjectWrap >& data) {
901+ delete data.GetParameter();
902+ }
903+
904+ static void CleanupHook(void* arg) { delete static_cast<ObjectWrap* >(arg); }
905+
906+ v8::Global< v8::Object > handle_ ;
907+ v8::Isolate* isolate_ ;
908+ };
909+
910+ } // namespace demo
911+ #endif
912+ ```
913+
851914In ` myobject.cc ` , implement the various methods that are to be exposed.
852915In the following code, the method ` plusOne() ` is exposed by adding it to the
853916constructor's prototype:
@@ -912,6 +975,7 @@ void MyObject::New(const FunctionCallbackInfo<Value>& args) {
912975 0 : args[ 0] ->NumberValue(context).FromMaybe(0);
913976 MyObject* obj = new MyObject(value);
914977 obj->Wrap(args.This());
978+ obj->MakeWeak();
915979 args.GetReturnValue().Set(args.This());
916980 } else {
917981 // Invoked as plain function ` MyObject(...) ` , turn into construct call.
@@ -1039,11 +1103,11 @@ JavaScript:
10391103#define MYOBJECT_H
10401104
10411105#include <node.h>
1042- #include <node_object_wrap.h>
1106+ #include "object_wrap.h"
10431107
10441108namespace demo {
10451109
1046- class MyObject : public node:: ObjectWrap {
1110+ class MyObject : public ObjectWrap {
10471111 public:
10481112 static void Init();
10491113 static void NewInstance(const v8::FunctionCallbackInfo<v8::Value>& args);
@@ -1063,7 +1127,8 @@ class MyObject : public node::ObjectWrap {
10631127#endif
10641128```
10651129
1066- The implementation in ` myobject.cc ` is similar to the previous example:
1130+ Our ` ObjectWrap ` helper remains the same as in the previous example,
1131+ and implementation in ` myobject.cc ` is similar as well:
10671132
10681133<!-- addon-verify-file factory_of_wrapped_objects/myobject.cc -->
10691134
@@ -1125,6 +1190,7 @@ void MyObject::New(const FunctionCallbackInfo<Value>& args) {
11251190 0 : args[ 0] ->NumberValue(context).FromMaybe(0);
11261191 MyObject* obj = new MyObject(value);
11271192 obj->Wrap(args.This());
1193+ obj->MakeWeak();
11281194 args.GetReturnValue().Set(args.This());
11291195 } else {
11301196 // Invoked as plain function ` MyObject(...) ` , turn into construct call.
@@ -1208,7 +1274,7 @@ console.log(obj2.plusOne());
12081274
12091275In addition to wrapping and returning C++ objects, it is possible to pass
12101276wrapped objects around by unwrapping them with the Node.js helper function
1211- ` node:: ObjectWrap::Unwrap` . The following examples shows a function ` add() `
1277+ ` ObjectWrap::Unwrap ` . The following examples shows a function ` add() `
12121278that can take two ` MyObject ` objects as input arguments:
12131279
12141280<!-- addon-verify-file passing_wrapped_objects_around/addon.cc -->
@@ -1238,9 +1304,9 @@ void Add(const FunctionCallbackInfo<Value>& args) {
12381304 Isolate* isolate = args.GetIsolate();
12391305 Local<Context > context = isolate->GetCurrentContext();
12401306
1241- MyObject* obj1 = node:: ObjectWrap::Unwrap<MyObject >(
1307+ MyObject* obj1 = ObjectWrap::Unwrap<MyObject >(
12421308 args[ 0] ->ToObject(context).ToLocalChecked());
1243- MyObject* obj2 = node:: ObjectWrap::Unwrap<MyObject >(
1309+ MyObject* obj2 = ObjectWrap::Unwrap<MyObject >(
12441310 args[ 1] ->ToObject(context).ToLocalChecked());
12451311
12461312 double sum = obj1->value() + obj2->value();
@@ -1270,11 +1336,11 @@ after unwrapping the object.
12701336#define MYOBJECT_H
12711337
12721338#include <node.h>
1273- #include <node_object_wrap.h>
1339+ #include "object_wrap.h"
12741340
12751341namespace demo {
12761342
1277- class MyObject : public node:: ObjectWrap {
1343+ class MyObject : public ObjectWrap {
12781344 public:
12791345 static void Init();
12801346 static void NewInstance(const v8::FunctionCallbackInfo<v8::Value>& args);
@@ -1294,7 +1360,8 @@ class MyObject : public node::ObjectWrap {
12941360#endif
12951361```
12961362
1297- The implementation of ` myobject.cc ` remains similar to the previous version:
1363+ Our ` ObjectWrap ` helper remains the same as in the previous example,
1364+ and implementation in ` myobject.cc ` is similar as well:
12981365
12991366<!-- addon-verify-file passing_wrapped_objects_around/myobject.cc -->
13001367
@@ -1352,6 +1419,7 @@ void MyObject::New(const FunctionCallbackInfo<Value>& args) {
13521419 0 : args[ 0] ->NumberValue(context).FromMaybe(0);
13531420 MyObject* obj = new MyObject(value);
13541421 obj->Wrap(args.This());
1422+ obj->MakeWeak();
13551423 args.GetReturnValue().Set(args.This());
13561424 } else {
13571425 // Invoked as plain function ` MyObject(...) ` , turn into construct call.
0 commit comments