Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

n-api: refactor napi_addon_register_func #15088

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
55 changes: 32 additions & 23 deletions doc/api/n-api.md
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
Expand Up @@ -824,11 +824,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);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kkoopa, @rvagg I'm wondering if you have any insight as to why the module and exports were made available to modules in the past. This change means that the addon won't have access to the module object and will only be able to replace the exports object as opposed to being able to add to it.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No idea why. As far as I know, it is just the way it always has been (at least from Node v0.6 onwards).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it was just for compatibility with JS-based CommonJS modules where you have both pre-created exports and module itself available.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like it was done so native modules could also do module.exports = function() {};

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok from comments/backgroup it does not sound like we think the limitations will be a problem. I guess worst case is we have to add a second register with additional parameters later on and best case we don't need them and stick with it as proposed here. So once updates are added I'll take another look.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One alternative that was discussed was to pre-emptively reserve a parameter in the register func signature in case the lack of module object availability during registration became a problem- that would allow us to deal with that situation in the future if it becomes a problem without causing a breaking change

Copy link
Member

@RReverser RReverser Aug 31, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@digitalinfinity Even if it's needed in the future, it should be easy to add it as a separate function similar to napi_get_global. Although I don't see any good reason to expose such CommonJS implementation details to native modules for now.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@RReverser That makes sense- for some reason I was imagining the fix would involve changing the signature of napi_addon_register_func but your approach makes sense- so I'm fine with this signature as is.

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
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
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
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
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
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
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
@@ -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
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
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
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
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)