Skip to content

Commit

Permalink
n-api: refactor napi_addon_register_func
Browse files Browse the repository at this point in the history
As per discussion in abi-stable-node:
nodejs/abi-stable-node#256,
take a refactor to napi_addon_register_func such that
the result from the register function is assigned to
the module exports property. By making this change,
native module can be agnostic about which type of
module the environment supports.

PR-URL: #15088
Reviewed-By: Gabriel Schulhof <[email protected]>
Reviewed-By: Michael Dawson <[email protected]>
Reviewed-By: Hitesh Kanwathirtha <[email protected]>
  • Loading branch information
boingoing authored and mhdawson committed Sep 14, 2017
1 parent d195a06 commit 92e5f5c
Show file tree
Hide file tree
Showing 37 changed files with 322 additions and 272 deletions.
55 changes: 32 additions & 23 deletions doc/api/n-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -873,7 +873,7 @@ JavaScript Object associated with the `napi_ref`. Otherise, result
will be NULL.

## Module registration
N-API modules are registered in the same manner as other modules
N-API modules are registered in a manner similar to other modules
except that instead of using the `NODE_MODULE` macro the following
is used:

Expand All @@ -885,32 +885,39 @@ The next difference is the signature for the `Init` method. For a N-API
module it is as follows:

```C
void Init(napi_env env, napi_value exports, napi_value module, void* priv);
napi_value Init(napi_env env, napi_value exports);
```

As with any other module, functions are exported by either adding them to
the `exports` or `module` objects passed to the `Init` method.
The return value from `Init` is treated as the `exports` object for the module.
The `Init` method is passed an empty object via the `exports` parameter as a
convenience. If `Init` returns NULL, the parameter passed as `exports` is
exported by the module. N-API modules cannot modify the `module` object but can
specify anything as the `exports` property of the module.

For example, to add the method `hello` as a function so that it can be called
as a method provided by the addon:

```C
void Init(napi_env env, napi_value exports, napi_value module, void* priv) {
napi_value Init(napi_env env, napi_value exports) {
napi_status status;
napi_property_descriptor desc =
{"hello", Method, 0, 0, 0, napi_default, 0};
if (status != napi_ok) return nullptr;
status = napi_define_properties(env, exports, 1, &desc);
if (status != napi_ok) return nullptr;
return exports;
}
```

For example, to set a function to be returned by the `require()` for the addon:

```C
void Init(napi_env env, napi_value exports, napi_value module, void* priv) {
napi_value Init(napi_env env, napi_value exports) {
napi_value method;
napi_status status;
napi_property_descriptor desc =
{"exports", Method, 0, 0, 0, napi_default, 0};
status = napi_define_properties(env, module, 1, &desc);
status = napi_create_function(env, "exports", Method, NULL, &method));
if (status != napi_ok) return nullptr;
return method;
}
```

Expand All @@ -919,28 +926,30 @@ For example, to define a class so that new instances can be created

```C
// NOTE: partial example, not all referenced code is included

napi_status status;
napi_property_descriptor properties[] = {
napi_value Init(napi_env env, napi_value exports) {
napi_status status;
napi_property_descriptor properties[] = {
{ "value", nullptr, GetValue, SetValue, 0, napi_default, 0 },
DECLARE_NAPI_METHOD("plusOne", PlusOne),
DECLARE_NAPI_METHOD("multiply", Multiply),
};
};

napi_value cons;
status =
napi_define_class(env, "MyObject", New, nullptr, 3, properties, &cons);
if (status != napi_ok) return;
napi_value cons;
status =
napi_define_class(env, "MyObject", New, nullptr, 3, properties, &cons);
if (status != napi_ok) return nullptr;

status = napi_create_reference(env, cons, 1, &constructor);
if (status != napi_ok) return;
status = napi_create_reference(env, cons, 1, &constructor);
if (status != napi_ok) return nullptr;

status = napi_set_named_property(env, exports, "MyObject", cons);
if (status != napi_ok) return;
status = napi_set_named_property(env, exports, "MyObject", cons);
if (status != napi_ok) return nullptr;

return exports;
}
```

For more details on setting properties on either the `exports` or `module`
objects, see the section on
For more details on setting properties on objects, see the section on
[Working with JavaScript Properties][].

For more details on building addon modules in general, refer to the existing API
Expand Down
16 changes: 11 additions & 5 deletions src/node_api.cc
Original file line number Diff line number Diff line change
Expand Up @@ -822,11 +822,17 @@ void napi_module_register_cb(v8::Local<v8::Object> exports,
// one is found.
napi_env env = v8impl::GetEnv(context);

mod->nm_register_func(
env,
v8impl::JsValueFromV8LocalValue(exports),
v8impl::JsValueFromV8LocalValue(module),
mod->nm_priv);
napi_value _exports =
mod->nm_register_func(env, v8impl::JsValueFromV8LocalValue(exports));

// If register function returned a non-null exports object different from
// the exports object we passed it, set that as the "exports" property of
// the module.
if (_exports != nullptr &&
_exports != v8impl::JsValueFromV8LocalValue(exports)) {
napi_value _module = v8impl::JsValueFromV8LocalValue(module);
napi_set_named_property(env, _module, "exports", _exports);
}
}

} // end of anonymous namespace
Expand Down
6 changes: 2 additions & 4 deletions src/node_api.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,8 @@
#endif


typedef void (*napi_addon_register_func)(napi_env env,
napi_value exports,
napi_value module,
void* priv);
typedef napi_value (*napi_addon_register_func)(napi_env env,
napi_value exports);

typedef struct {
int nm_version;
Expand Down
5 changes: 3 additions & 2 deletions test/addons-napi/1_hello_world/binding.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,10 @@ napi_value Method(napi_env env, napi_callback_info info) {
return world;
}

void Init(napi_env env, napi_value exports, napi_value module, void* priv) {
napi_value Init(napi_env env, napi_value exports) {
napi_property_descriptor desc = DECLARE_NAPI_PROPERTY("hello", Method);
NAPI_CALL_RETURN_VOID(env, napi_define_properties(env, exports, 1, &desc));
NAPI_CALL(env, napi_define_properties(env, exports, 1, &desc));
return exports;
}

NAPI_MODULE(NODE_GYP_MODULE_NAME, Init)
7 changes: 4 additions & 3 deletions test/addons-napi/2_function_arguments/binding.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ napi_value Add(napi_env env, napi_callback_info info) {
NAPI_CALL(env, napi_typeof(env, args[1], &valuetype1));

NAPI_ASSERT(env, valuetype0 == napi_number && valuetype1 == napi_number,
"Wrong argument type. Numbers expected.");
"Wrong argument type. Numbers expected.");

double value0;
NAPI_CALL(env, napi_get_value_double(env, args[0], &value0));
Expand All @@ -29,9 +29,10 @@ napi_value Add(napi_env env, napi_callback_info info) {
return sum;
}

void Init(napi_env env, napi_value exports, napi_value module, void* priv) {
napi_value Init(napi_env env, napi_value exports) {
napi_property_descriptor desc = DECLARE_NAPI_PROPERTY("add", Add);
NAPI_CALL_RETURN_VOID(env, napi_define_properties(env, exports, 1, &desc));
NAPI_CALL(env, napi_define_properties(env, exports, 1, &desc));
return exports;
}

NAPI_MODULE(NODE_GYP_MODULE_NAME, Init)
11 changes: 6 additions & 5 deletions test/addons-napi/3_callbacks/binding.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,17 @@ napi_value RunCallback(napi_env env, napi_callback_info info) {
NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));

NAPI_ASSERT(env, argc == 1,
"Wrong number of arguments. Expects a single argument.");
"Wrong number of arguments. Expects a single argument.");

napi_valuetype valuetype0;
NAPI_CALL(env, napi_typeof(env, args[0], &valuetype0));
NAPI_ASSERT(env, valuetype0 == napi_function,
"Wrong type of arguments. Expects a function as first argument.");
"Wrong type of arguments. Expects a function as first argument.");

napi_valuetype valuetype1;
NAPI_CALL(env, napi_typeof(env, args[1], &valuetype1));
NAPI_ASSERT(env, valuetype1 == napi_undefined,
"Additional arguments should be undefined.");
"Additional arguments should be undefined.");

napi_value argv[1];
const char* str = "hello world";
Expand All @@ -45,12 +45,13 @@ napi_value RunCallbackWithRecv(napi_env env, napi_callback_info info) {
return NULL;
}

void Init(napi_env env, napi_value exports, napi_value module, void* priv) {
napi_value Init(napi_env env, napi_value exports) {
napi_property_descriptor desc[2] = {
DECLARE_NAPI_PROPERTY("RunCallback", RunCallback),
DECLARE_NAPI_PROPERTY("RunCallbackWithRecv", RunCallbackWithRecv),
};
NAPI_CALL_RETURN_VOID(env, napi_define_properties(env, exports, 2, desc));
NAPI_CALL(env, napi_define_properties(env, exports, 2, desc));
return exports;
}

NAPI_MODULE(NODE_GYP_MODULE_NAME, Init)
8 changes: 4 additions & 4 deletions test/addons-napi/4_object_factory/binding.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ napi_value CreateObject(napi_env env, napi_callback_info info) {
return obj;
}

void Init(napi_env env, napi_value exports, napi_value module, void* priv) {
napi_property_descriptor desc =
DECLARE_NAPI_PROPERTY("exports", CreateObject);
NAPI_CALL_RETURN_VOID(env, napi_define_properties(env, module, 1, &desc));
napi_value Init(napi_env env, napi_value exports) {
NAPI_CALL(env,
napi_create_function(env, "exports", CreateObject, NULL, &exports));
return exports;
}

NAPI_MODULE(NODE_GYP_MODULE_NAME, Init)
10 changes: 5 additions & 5 deletions test/addons-napi/5_function_factory/binding.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,15 @@ napi_value MyFunction(napi_env env, napi_callback_info info) {
napi_value CreateFunction(napi_env env, napi_callback_info info) {
napi_value fn;
NAPI_CALL(env,
napi_create_function(env, "theFunction", MyFunction, NULL, &fn));
napi_create_function(env, "theFunction", MyFunction, NULL, &fn));

return fn;
}

void Init(napi_env env, napi_value exports, napi_value module, void* priv) {
napi_property_descriptor desc =
DECLARE_NAPI_PROPERTY("exports", CreateFunction);
NAPI_CALL_RETURN_VOID(env, napi_define_properties(env, module, 1, &desc));
napi_value Init(napi_env env, napi_value exports) {
NAPI_CALL(env,
napi_create_function(env, "exports", CreateFunction, NULL, &exports));
return exports;
}

NAPI_MODULE(NODE_GYP_MODULE_NAME, Init)
4 changes: 3 additions & 1 deletion test/addons-napi/6_object_wrap/binding.cc
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
#include "myobject.h"
#include "../common.h"

void Init(napi_env env, napi_value exports, napi_value module, void* priv) {
napi_value Init(napi_env env, napi_value exports) {
MyObject::Init(env, exports);
return exports;
}

NAPI_MODULE(NODE_GYP_MODULE_NAME, Init)
18 changes: 9 additions & 9 deletions test/addons-napi/6_object_wrap/myobject.cc
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,12 @@ void MyObject::Init(napi_env env, napi_value exports) {

napi_value cons;
NAPI_CALL_RETURN_VOID(env,
napi_define_class(env, "MyObject", New, nullptr, 3, properties, &cons));
napi_define_class(env, "MyObject", New, nullptr, 3, properties, &cons));

NAPI_CALL_RETURN_VOID(env, napi_create_reference(env, cons, 1, &constructor));

NAPI_CALL_RETURN_VOID(env,
napi_set_named_property(env, exports, "MyObject", cons));
napi_set_named_property(env, exports, "MyObject", cons));
}

napi_value MyObject::New(napi_env env, napi_callback_info info) {
Expand All @@ -55,11 +55,11 @@ napi_value MyObject::New(napi_env env, napi_callback_info info) {

obj->env_ = env;
NAPI_CALL(env, napi_wrap(env,
_this,
obj,
MyObject::Destructor,
nullptr, // finalize_hint
&obj->wrapper_));
_this,
obj,
MyObject::Destructor,
nullptr, // finalize_hint
&obj->wrapper_));

return _this;
}
Expand All @@ -80,7 +80,7 @@ napi_value MyObject::New(napi_env env, napi_callback_info info) {
napi_value MyObject::GetValue(napi_env env, napi_callback_info info) {
napi_value _this;
NAPI_CALL(env,
napi_get_cb_info(env, info, nullptr, nullptr, &_this, nullptr));
napi_get_cb_info(env, info, nullptr, nullptr, &_this, nullptr));

MyObject* obj;
NAPI_CALL(env, napi_unwrap(env, _this, reinterpret_cast<void**>(&obj)));
Expand Down Expand Up @@ -108,7 +108,7 @@ napi_value MyObject::SetValue(napi_env env, napi_callback_info info) {
napi_value MyObject::PlusOne(napi_env env, napi_callback_info info) {
napi_value _this;
NAPI_CALL(env,
napi_get_cb_info(env, info, nullptr, nullptr, &_this, nullptr));
napi_get_cb_info(env, info, nullptr, nullptr, &_this, nullptr));

MyObject* obj;
NAPI_CALL(env, napi_unwrap(env, _this, reinterpret_cast<void**>(&obj)));
Expand Down
10 changes: 5 additions & 5 deletions test/addons-napi/7_factory_wrap/binding.cc
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,12 @@ napi_value CreateObject(napi_env env, napi_callback_info info) {
return instance;
}

void Init(napi_env env, napi_value exports, napi_value module, void* priv) {
NAPI_CALL_RETURN_VOID(env, MyObject::Init(env));
napi_value Init(napi_env env, napi_value exports) {
NAPI_CALL(env, MyObject::Init(env));

napi_property_descriptor desc =
DECLARE_NAPI_PROPERTY("exports", CreateObject);
NAPI_CALL_RETURN_VOID(env, napi_define_properties(env, module, 1, &desc));
NAPI_CALL(env,
napi_create_function(env, "exports", CreateObject, NULL, &exports));
return exports;
}

NAPI_MODULE(NODE_GYP_MODULE_NAME, Init)
12 changes: 6 additions & 6 deletions test/addons-napi/7_factory_wrap/myobject.cc
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,11 @@ napi_value MyObject::New(napi_env env, napi_callback_info info) {

obj->env_ = env;
NAPI_CALL(env, napi_wrap(env,
_this,
obj,
MyObject::Destructor,
nullptr, /* finalize_hint */
&obj->wrapper_));
_this,
obj,
MyObject::Destructor,
nullptr, /* finalize_hint */
&obj->wrapper_));

return _this;
}
Expand All @@ -80,7 +80,7 @@ napi_status MyObject::NewInstance(napi_env env,
napi_value MyObject::PlusOne(napi_env env, napi_callback_info info) {
napi_value _this;
NAPI_CALL(env,
napi_get_cb_info(env, info, nullptr, nullptr, &_this, nullptr));
napi_get_cb_info(env, info, nullptr, nullptr, &_this, nullptr));

MyObject* obj;
NAPI_CALL(env, napi_unwrap(env, _this, reinterpret_cast<void**>(&obj)));
Expand Down
8 changes: 5 additions & 3 deletions test/addons-napi/8_passing_wrapped/binding.cc
Original file line number Diff line number Diff line change
Expand Up @@ -29,16 +29,18 @@ napi_value Add(napi_env env, napi_callback_info info) {
return sum;
}

void Init(napi_env env, napi_value exports, napi_value module, void* priv) {
napi_value Init(napi_env env, napi_value exports) {
MyObject::Init(env);

napi_property_descriptor desc[] = {
DECLARE_NAPI_PROPERTY("createObject", CreateObject),
DECLARE_NAPI_PROPERTY("add", Add),
};

NAPI_CALL_RETURN_VOID(env,
napi_define_properties(env, exports, sizeof(desc) / sizeof(*desc), desc));
NAPI_CALL(env,
napi_define_properties(env, exports, sizeof(desc) / sizeof(*desc), desc));

return exports;
}

NAPI_MODULE(NODE_GYP_MODULE_NAME, Init)
Loading

0 comments on commit 92e5f5c

Please sign in to comment.