Skip to content

Commit

Permalink
src: allow N-API addon in AddLinkedBinding()
Browse files Browse the repository at this point in the history
`AddLinkedBinding()` can be used to load old-style Node.js addons, but
currently not N-API addons. There’s no good reason not to support
N-API addons as well, so add that.

PR-URL: #35301
Reviewed-By: Colin Ihrig <[email protected]>
Reviewed-By: Michael Dawson <[email protected]>
Reviewed-By: Zeyu Yang <[email protected]>
  • Loading branch information
addaleax authored and MylesBorins committed Sep 29, 2020
1 parent 353a567 commit 0d8eaa3
Show file tree
Hide file tree
Showing 6 changed files with 131 additions and 7 deletions.
4 changes: 4 additions & 0 deletions src/api/environment.cc
Original file line number Diff line number Diff line change
Expand Up @@ -677,6 +677,10 @@ void AddLinkedBinding(Environment* env, const node_module& mod) {
prev_head->nm_link = &env->extra_linked_bindings()->back();
}

void AddLinkedBinding(Environment* env, const napi_module& mod) {
AddLinkedBinding(env, napi_module_to_node_module(&mod));
}

void AddLinkedBinding(Environment* env,
const char* name,
addon_context_register_func fn,
Expand Down
4 changes: 4 additions & 0 deletions src/node.h
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,8 @@
// Forward-declare libuv loop
struct uv_loop_s;

struct napi_module;

// Forward-declare these functions now to stop MSVS from becoming
// terminally confused when it's done in node_internals.h
namespace node {
Expand Down Expand Up @@ -824,6 +826,8 @@ extern "C" NODE_EXTERN void node_module_register(void* mod);
// In each variant, the registration function needs to be usable at least for
// the time during which the Environment exists.
NODE_EXTERN void AddLinkedBinding(Environment* env, const node_module& mod);
NODE_EXTERN void AddLinkedBinding(Environment* env,
const struct napi_module& mod);
NODE_EXTERN void AddLinkedBinding(Environment* env,
const char* name,
addon_context_register_func fn,
Expand Down
17 changes: 12 additions & 5 deletions src/node_api.cc
Original file line number Diff line number Diff line change
Expand Up @@ -447,7 +447,7 @@ static void napi_module_register_cb(v8::Local<v8::Object> exports,
v8::Local<v8::Context> context,
void* priv) {
napi_module_register_by_symbol(exports, module, context,
static_cast<napi_module*>(priv)->nm_register_func);
static_cast<const napi_module*>(priv)->nm_register_func);
}

void napi_module_register_by_symbol(v8::Local<v8::Object> exports,
Expand Down Expand Up @@ -480,19 +480,26 @@ void napi_module_register_by_symbol(v8::Local<v8::Object> exports,
}
}

// Registers a NAPI module.
void napi_module_register(napi_module* mod) {
node::node_module* nm = new node::node_module {
namespace node {
node_module napi_module_to_node_module(const napi_module* mod) {
return {
-1,
mod->nm_flags | NM_F_DELETEME,
nullptr,
mod->nm_filename,
nullptr,
napi_module_register_cb,
mod->nm_modname,
mod, // priv
const_cast<napi_module*>(mod), // priv
nullptr,
};
}
} // namespace node

// Registers a NAPI module.
void napi_module_register(napi_module* mod) {
node::node_module* nm = new node::node_module(
node::napi_module_to_node_module(mod));
node::node_module_register(nm);
}

Expand Down
2 changes: 1 addition & 1 deletion src/node_api.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ struct uv_loop_s; // Forward declaration.
typedef napi_value (*napi_addon_register_func)(napi_env env,
napi_value exports);

typedef struct {
typedef struct napi_module {
int nm_version;
unsigned int nm_flags;
const char* nm_filename;
Expand Down
2 changes: 2 additions & 0 deletions src/node_internals.h
Original file line number Diff line number Diff line change
Expand Up @@ -398,6 +398,8 @@ namespace fs {
std::string Basename(const std::string& str, const std::string& extension);
} // namespace fs

node_module napi_module_to_node_module(const napi_module* mod);

} // namespace node

#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
Expand Down
109 changes: 108 additions & 1 deletion test/cctest/test_linked_binding.cc
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#include "node_test_fixture.h"
#include "node_internals.h" // RunBootstrapping()
#include "node_api.h"

void InitializeBinding(v8::Local<v8::Object> exports,
v8::Local<v8::Value> module,
Expand Down Expand Up @@ -83,3 +83,110 @@ TEST_F(LinkedBindingTest, LocallyDefinedLinkedBindingTest) {
CHECK_EQ(strcmp(*utf8val, "value"), 0);
CHECK_EQ(calls, 1);
}

napi_value InitializeLocalNapiBinding(napi_env env, napi_value exports) {
napi_value key, value;
CHECK_EQ(
napi_create_string_utf8(env, "hello", NAPI_AUTO_LENGTH, &key), napi_ok);
CHECK_EQ(
napi_create_string_utf8(env, "world", NAPI_AUTO_LENGTH, &value), napi_ok);
CHECK_EQ(napi_set_property(env, exports, key, value), napi_ok);
return nullptr;
}

static napi_module local_linked_napi = {
NAPI_MODULE_VERSION,
node::ModuleFlags::kLinked,
nullptr,
InitializeLocalNapiBinding,
"local_linked_napi",
nullptr,
{0},
};

TEST_F(LinkedBindingTest, LocallyDefinedLinkedBindingNapiTest) {
const v8::HandleScope handle_scope(isolate_);
const Argv argv;
Env test_env {handle_scope, argv};

AddLinkedBinding(*test_env, local_linked_napi);

v8::Local<v8::Context> context = isolate_->GetCurrentContext();

const char* run_script =
"process._linkedBinding('local_linked_napi').hello";
v8::Local<v8::Script> script = v8::Script::Compile(
context,
v8::String::NewFromOneByte(isolate_,
reinterpret_cast<const uint8_t*>(run_script))
.ToLocalChecked())
.ToLocalChecked();
v8::Local<v8::Value> completion_value = script->Run(context).ToLocalChecked();
v8::String::Utf8Value utf8val(isolate_, completion_value);
CHECK_NOT_NULL(*utf8val);
CHECK_EQ(strcmp(*utf8val, "world"), 0);
}

napi_value NapiLinkedWithInstanceData(napi_env env, napi_value exports) {
int* instance_data = new int(0);
CHECK_EQ(
napi_set_instance_data(
env,
instance_data,
[](napi_env env, void* data, void* hint) {
++*static_cast<int*>(data);
}, nullptr),
napi_ok);

napi_value key, value;
CHECK_EQ(
napi_create_string_utf8(env, "hello", NAPI_AUTO_LENGTH, &key), napi_ok);
CHECK_EQ(
napi_create_external(env, instance_data, nullptr, nullptr, &value),
napi_ok);
CHECK_EQ(napi_set_property(env, exports, key, value), napi_ok);
return nullptr;
}

static napi_module local_linked_napi_id = {
NAPI_MODULE_VERSION,
node::ModuleFlags::kLinked,
nullptr,
NapiLinkedWithInstanceData,
"local_linked_napi_id",
nullptr,
{0},
};

TEST_F(LinkedBindingTest, LocallyDefinedLinkedBindingNapiInstanceDataTest) {
const v8::HandleScope handle_scope(isolate_);
int* instance_data = nullptr;

{
const Argv argv;
Env test_env {handle_scope, argv};

AddLinkedBinding(*test_env, local_linked_napi_id);

v8::Local<v8::Context> context = isolate_->GetCurrentContext();

const char* run_script =
"process._linkedBinding('local_linked_napi_id').hello";
v8::Local<v8::Script> script = v8::Script::Compile(
context,
v8::String::NewFromOneByte(isolate_,
reinterpret_cast<const uint8_t*>(run_script))
.ToLocalChecked())
.ToLocalChecked();
v8::Local<v8::Value> completion_value =
script->Run(context).ToLocalChecked();
CHECK(completion_value->IsExternal());
instance_data = static_cast<int*>(
completion_value.As<v8::External>()->Value());
CHECK_NE(instance_data, nullptr);
CHECK_EQ(*instance_data, 0);
}

CHECK_EQ(*instance_data, 1);
delete instance_data;
}

0 comments on commit 0d8eaa3

Please sign in to comment.