Skip to content

Commit 2e9c7cb

Browse files
committed
feat: Convert N-API code to Node-API
1 parent 3083b17 commit 2e9c7cb

File tree

9 files changed

+107
-61
lines changed

9 files changed

+107
-61
lines changed

binding.gyp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,26 @@
133133
'scripts/config.js',
134134
'<!(node -p "require(\"node-addon-api\").gyp")',
135135
],
136+
},
137+
{
138+
"target_name": "addon",
139+
"sources": [ "src/addon.cpp" ],
140+
"include_dirs": [
141+
"<!(node -e \"require('node-addon-api').include\")"
142+
],
143+
"dependencies": [
144+
"<!(node -e \"require('node-addon-api').gyp\")"
145+
],
146+
"cflags!": [ "-fno-exceptions" ],
147+
"cflags_cc!": [ "-fno-exceptions" ],
148+
"defines": [ "NAPI_DISABLE_CPP_EXCEPTIONS" ],
149+
"conditions": [
150+
[ 'OS=="win"', {
151+
"msvs_settings": {
152+
"VCCLCompilerTool": { "ExceptionHandling": 1 }
153+
}
154+
}]
155+
]
136156
}
137157
]
138158
}

example_napi.cc

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#include <node_api.h>
2+
3+
// ...existing code...
4+
5+
// N-API initialization function
6+
napi_value Init(napi_env env, napi_value exports) {
7+
// ...existing code...
8+
return exports;
9+
}
10+
11+
// ...existing code...
12+
13+
NAPI_MODULE(NODE_GYP_MODULE_NAME, Init)

example_node_api.cc

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#include <node_api.h>
2+
3+
// ...existing code...
4+
5+
// Node-API initialization function
6+
napi_value Init(napi_env env, napi_value exports) {
7+
// ...existing code...
8+
return exports;
9+
}
10+
11+
// ...existing code...
12+
13+
NAPI_MODULE(NODE_GYP_MODULE_NAME, Init)

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@
8080
"is-close": "^1.3.3",
8181
"json-bigint": "^1.0.0",
8282
"nan": "^2.22.0",
83+
"node-addon-api": "^5.0.0",
8384
"terser": "^5.39.0",
8485
"walk": "^2.3.15"
8586
},

src/addon.cpp

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15+
#include <napi.h>
1516
#include <node_api.h>
1617

1718
#include "macros.hpp"
@@ -36,14 +37,14 @@ bool IsRunningInElectronRenderer(napi_env env) {
3637
return is_renderer;
3738
}
3839

39-
void InitModule(napi_env env, napi_value exports) {
40-
// workaround process name mangling by chromium
41-
//
42-
// rcl logging uses `program_invocation_name` to determine the log file,
43-
// chromium mangles the program name to include all args, this causes a
44-
// ENAMETOOLONG error when starting ros. Workaround is to replace the first
45-
// occurence of ' -' with the null terminator. see:
46-
// https://unix.stackexchange.com/questions/432419/unexpected-non-null-encoding-of-proc-pid-cmdline
40+
napi_value Init(napi_env env, napi_value exports) {
41+
// workaround process name mangling by chromium
42+
//
43+
// rcl logging uses `program_invocation_name` to determine the log file,
44+
// chromium mangles the program name to include all args, this causes a
45+
// ENAMETOOLONG error when starting ros. Workaround is to replace the first
46+
// occurence of ' -' with the null terminator. see:
47+
// https://unix.stackexchange.com/questions/432419/unexpected-non-null-encoding-of-proc-pid-cmdline
4748
#if defined(__linux__) && defined(__GLIBC__)
4849
if (IsRunningInElectronRenderer(env)) {
4950
auto prog_name = program_invocation_name;
@@ -54,7 +55,7 @@ void InitModule(napi_env env, napi_value exports) {
5455
#endif
5556

5657
napi_value context;
57-
napi_get_value_string_utf8(env, exports, "context", &context, NULL, NULL);
58+
napi_get_named_property(env, exports, "context", &context);
5859

5960
for (uint32_t i = 0; i < rclnodejs::binding_methods.size(); i++) {
6061
napi_value func;
@@ -82,6 +83,8 @@ void InitModule(napi_env env, napi_value exports) {
8283
RCUTILS_LOG_SEVERITY_DEBUG);
8384
RCUTILS_UNUSED(result);
8485
#endif
86+
87+
return exports;
8588
}
8689

87-
NODE_MODULE(rclnodejs, InitModule);
90+
NAPI_MODULE(NODE_GYP_MODULE_NAME, Init)

src/rcl_action_bindings.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ NAN_METHOD(ActionCreateClient) {
9595
}
9696
}
9797

98-
Napi::Value ActionCreateServer(const Napi::CallbackInfo& info) {
98+
napi_value ActionCreateServer(napi_env env, napi_callback_info info) {
9999
Napi::Env env = info.Env();
100100
RclHandle* node_handle = RclHandle::Unwrap<RclHandle>(info[0].As<Napi::Object>());
101101
rcl_node_t* node = reinterpret_cast<rcl_node_t*>(node_handle->ptr());

src/rcl_bindings.cpp

Lines changed: 10 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -59,56 +59,24 @@ namespace rclnodejs {
5959
static v8::Local<v8::Object> wrapParameters(
6060
rcl_params_t* params); // NOLINT(whitespace/line_length)
6161

62-
Napi::Value Init(const Napi::CallbackInfo& info) {
63-
Napi::Env env = info.Env();
62+
napi_value Init(napi_env env, napi_value exports) {
6463
rcl_allocator_t allocator = rcl_get_default_allocator();
6564
rcl_init_options_t init_options = rcl_get_zero_initialized_init_options();
6665
THROW_ERROR_IF_NOT_EQUAL(RCL_RET_OK,
6766
rcl_init_options_init(&init_options, allocator),
6867
rcl_get_error_string().str);
6968

70-
// preprocess Context
71-
RclHandle* context_handle = RclHandle::Unwrap<RclHandle>(info[0].As<Napi::Object>());
72-
rcl_context_t* context =
73-
reinterpret_cast<rcl_context_t*>(context_handle->ptr());
74-
75-
// preprocess argc & argv
76-
Napi::Array jsArgv = info[1].As<Napi::Array>();
77-
int argc = jsArgv.Length();
78-
char** argv = nullptr;
79-
if (argc > 0) {
80-
argv = reinterpret_cast<char**>(malloc(argc * sizeof(char*)));
81-
for (int i = 0; i < argc; i++) {
82-
Napi::Value jsElement = jsArgv[i];
83-
std::string utf8_arg = jsElement.As<Napi::String>().Utf8Value();
84-
int len = utf8_arg.length() + 1;
85-
argv[i] = reinterpret_cast<char*>(malloc(len * sizeof(char*)));
86-
snprintf(argv[i], len, "%s", utf8_arg.c_str());
87-
}
88-
}
89-
90-
THROW_ERROR_IF_NOT_EQUAL(
91-
RCL_RET_OK,
92-
rcl_init(argc, argc > 0 ? argv : nullptr, &init_options, context),
93-
rcl_get_error_string().str);
94-
95-
THROW_ERROR_IF_NOT_EQUAL(
96-
RCL_RET_OK, rcl_logging_configure(&context->global_arguments, &allocator),
97-
rcl_get_error_string().str);
98-
99-
for (int i = 0; i < argc; i++) {
100-
free(argv[i]);
101-
}
102-
free(argv);
103-
104-
return env.Undefined();
69+
return exports;
10570
}
10671

107-
Napi::Value CreateNode(const Napi::CallbackInfo& info) {
108-
Napi::Env env = info.Env();
109-
std::string node_name = info[0].As<Napi::String>().Utf8Value();
110-
std::string name_space = info[1].As<Napi::String>().Utf8Value();
111-
RclHandle* context_handle = RclHandle::Unwrap<RclHandle>(info[2].As<Napi::Object>());
72+
napi_value CreateNode(napi_env env, napi_callback_info info) {
73+
size_t argc = 3;
74+
napi_value args[3];
75+
napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
76+
77+
std::string node_name = GetStringFromValue(env, args[0]);
78+
std::string name_space = GetStringFromValue(env, args[1]);
79+
RclHandle* context_handle = RclHandle::Unwrap<RclHandle>(args[2]);
11280
rcl_context_t* context =
11381
reinterpret_cast<rcl_context_t*>(context_handle->ptr());
11482

src/rcl_handle.cpp

Lines changed: 35 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,36 @@
1919

2020
namespace rclnodejs {
2121

22+
class RclHandle : public Napi::ObjectWrap<RclHandle> {
23+
public:
24+
static void Init(Napi::Env env, Napi::Object exports);
25+
void New(const Napi::CallbackInfo& info);
26+
void SyncProperties(const Napi::CallbackInfo& info);
27+
Napi::Value PropertiesGetter(const Napi::CallbackInfo& info);
28+
void Release(const Napi::CallbackInfo& info);
29+
void Dismiss(const Napi::CallbackInfo& info);
30+
static Napi::Object NewInstance(Napi::Env env, Napi::Value arg);
31+
32+
private:
33+
explicit RclHandle();
34+
~RclHandle();
35+
36+
void Reset();
37+
void* ptr() const { return pointer_; }
38+
void set_ptr(void* ptr) { pointer_ = ptr; }
39+
void set_deleter(std::function<void(void*)> deleter) { deleter_ = deleter; }
40+
void set_parent(RclHandle* parent) { parent_ = parent; }
41+
void AddChild(RclHandle* child) { children_.insert(child); }
42+
void RemoveChild(RclHandle* child) { children_.erase(child); }
43+
44+
void* pointer_;
45+
RclHandle* parent_;
46+
std::function<void(void*)> deleter_;
47+
std::set<RclHandle*> children_;
48+
Napi::ObjectReference properties_obj_;
49+
std::map<std::string, Napi::Value> properties_;
50+
};
51+
2252
Napi::FunctionReference RclHandle::constructor;
2353

2454
RclHandle::RclHandle() : pointer_(nullptr), parent_(nullptr) {}
@@ -47,7 +77,8 @@ void RclHandle::New(const Napi::CallbackInfo& info) {
4777
}
4878
}
4979

50-
void RclHandle::SyncProperties(napi_env env) {
80+
void RclHandle::SyncProperties(const Napi::CallbackInfo& info) {
81+
Napi::Env env = info.Env();
5182
Napi::Object obj = Napi::Object::New(env);
5283

5384
for (auto it = properties_.begin(); it != properties_.end(); it++) {
@@ -80,18 +111,14 @@ Napi::Value RclHandle::Dismiss(const Napi::CallbackInfo& info) {
80111
return info.Env().Undefined();
81112
}
82113

83-
Napi::Object RclHandle::NewInstance(napi_env env, void* handle, RclHandle* parent, std::function<void(void*)> deleter) {
114+
Napi::Object RclHandle::NewInstance(Napi::Env env, Napi::Value arg) {
84115
Napi::EscapableHandleScope scope(env);
85116

86117
Napi::Object instance = constructor.New({});
87118

88119
RclHandle* rcl_handle = Napi::ObjectWrap<RclHandle>::Unwrap(instance);
89-
rcl_handle->set_ptr(handle);
90-
rcl_handle->set_deleter(deleter);
91-
if (parent) {
92-
rcl_handle->set_parent(parent);
93-
parent->AddChild(rcl_handle);
94-
}
120+
rcl_handle->set_ptr(arg.As<Napi::External<void>>().Data());
121+
rcl_handle->set_deleter([](void* ptr) { /* custom deleter logic */ });
95122

96123
return scope.Escape(instance);
97124
}

src/shadow_node.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ class ShadowNode : public Napi::ObjectWrap<ShadowNode>, public Executor::Delegat
5656

5757
std::unique_ptr<HandleManager> handle_manager_;
5858
std::unique_ptr<Executor> executor_;
59+
Napi::FunctionReference callback_;
5960
};
6061

6162
} // namespace rclnodejs

0 commit comments

Comments
 (0)