Skip to content

Commit 450d97a

Browse files
committed
fixup! src: officially deprecate node::ObjectWrap
1 parent 05d80ba commit 450d97a

2 files changed

Lines changed: 94 additions & 21 deletions

File tree

‎doc/api/addons.md‎

Lines changed: 83 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -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

1717
This rest of this document focuses on the latter, requiring
1818
knowledge 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
830831
namespace 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+
851914
In `myobject.cc`, implement the various methods that are to be exposed.
852915
In the following code, the method `plusOne()` is exposed by adding it to the
853916
constructor'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
10441108
namespace 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

12091275
In addition to wrapping and returning C++ objects, it is possible to pass
12101276
wrapped 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()`
12121278
that 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
12751341
namespace 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.

‎tools/doc/addon-verify.mjs‎

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
//
66
// Each code block to extract is preceded by a marker in the markdown:
77
//
8-
// <!-- addon-verify-file worker_support/addon.cc -->
8+
// <!-- addon-verify-file worker_support/addon.cc [...] -->
99
// ```cpp
1010
// #include <node.h>
1111
// ...
@@ -33,7 +33,7 @@ if (!values.input || !values.output) {
3333

3434
const src = await open(values.input, 'r');
3535

36-
const MARKER_RE = /^<!--\s*addon-verify-file\s+(\S+?)\/(\S+)\s*-->$/;
36+
const MARKER_RE = /^<!--\s*addon-verify-file\s+(?<filenames>((\S+?)\/(\S+)\s?)+)\s*-->$/;
3737

3838
const entries = [];
3939
let nextBlockIsAddonVerifyFile = false;
@@ -47,7 +47,7 @@ for await (const line of src.readLines()) {
4747
continue;
4848
}
4949

50-
entries.at(-1).content += `${line}\n`;
50+
entries.at(-1).contentRef.content += `${line}\n`;
5151
}
5252
if (nextBlockIsAddonVerifyFile) {
5353
if (line) {
@@ -59,8 +59,13 @@ for await (const line of src.readLines()) {
5959
const match = MARKER_RE.exec(line);
6060
if (match) {
6161
nextBlockIsAddonVerifyFile = true;
62-
const [, dir, name] = match;
63-
entries.push({ dir, name, content: '' });
62+
const { filenames } = match.groups;
63+
// Use a single contentRef for files with identical contents
64+
const contentRef = { content: '' };
65+
for (const filename of filenames.split(/\s+/).filter(Boolean)) {
66+
const [dir, name] = filename.split('/');
67+
entries.push({ dir, name, contentRef });
68+
}
6469
}
6570
}
6671

@@ -74,7 +79,7 @@ for (const [name, files] of sections) {
7479
mkdirSync(dir, { recursive: true });
7580

7681
for (const file of files) {
77-
let content = file.content;
82+
let { content } = file.contentRef;
7883
if (file.name === 'test.js') {
7984
content =
8085
"'use strict';\n" +

0 commit comments

Comments
 (0)