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

Conversation

boingoing
Copy link
Contributor

@boingoing boingoing commented Aug 30, 2017

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.

Checklist
  • make -j4 test (UNIX), or vcbuild test (Windows) passes
  • tests and/or benchmarks are included
  • documentation is changed or added
  • commit message follows commit guidelines
Affected core subsystem(s)

n-api

@nodejs-github-bot nodejs-github-bot added c++ Issues and PRs that require attention from people who are familiar with C++. lib / src Issues and PRs related to general changes in the lib or src directory. labels Aug 30, 2017
napi_value Init(napi_env env) {
napi_value exports;
NAPI_CALL(env,
napi_create_function(env, "exports", CreateFunction, NULL, &exports));
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: Four spaces for continuations, IIRC.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks for pointing all these out. Visual Studio strikes again, I guess. I will push an update with these nits fixed. 👍

Copy link
Contributor

@gabrielschulhof gabrielschulhof left a comment

Choose a reason for hiding this comment

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

LGTM

@gabrielschulhof gabrielschulhof added the node-api Issues and PRs related to the Node-API. label Aug 30, 2017
napi_value Init(napi_env env) {
napi_value exports;
NAPI_CALL(env,
napi_create_function(env, "exports", CreateObject, NULL, &exports));
Copy link
Contributor

Choose a reason for hiding this comment

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

Four spaces for continuation, IIRC.

NAPI_CALL_RETURN_VOID(env, napi_define_properties(env, module, 1, &desc));
napi_value exports;
NAPI_CALL(env,
napi_create_function(env, "exports", CreateObject, NULL, &exports));
Copy link
Contributor

Choose a reason for hiding this comment

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

Four spaces.

napi_value exports;
NAPI_CALL(env, napi_create_object(env, &exports));

NAPI_CALL(env,
napi_define_properties(env, exports, sizeof(desc) / sizeof(*desc), desc));
Copy link
Contributor

Choose a reason for hiding this comment

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

Here too.

napi_value exports;
NAPI_CALL(env, napi_create_object(env, &exports));

NAPI_CALL(env, napi_define_properties(
env, exports, sizeof(descriptors) / sizeof(*descriptors), descriptors));
Copy link
Contributor

Choose a reason for hiding this comment

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

Here too.

napi_value exports;
NAPI_CALL(env, napi_create_object(env, &exports));

NAPI_CALL(env, napi_define_properties(
Copy link
Contributor

Choose a reason for hiding this comment

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

Here too.

@@ -137,8 +138,10 @@ void Init(napi_env env, napi_value exports, napi_value module, void* priv) {
DECLARE_NAPI_PROPERTY("staticBuffer", staticBuffer),
};

NAPI_CALL_RETURN_VOID(env, napi_define_properties(
NAPI_CALL(env, napi_define_properties(
Copy link
Contributor

Choose a reason for hiding this comment

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

Here too.

@@ -77,14 +77,12 @@ void Init(napi_env env, napi_value exports, napi_value module, void* priv) {
};

napi_value cons;
NAPI_CALL_RETURN_VOID(env, napi_define_class(env, "MyObject", New,
NAPI_CALL(env, napi_define_class(env, "MyObject", New,
Copy link
Contributor

Choose a reason for hiding this comment

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

Here too.

Copy link
Contributor

@gabrielschulhof gabrielschulhof left a comment

Choose a reason for hiding this comment

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

Boy, I sure painted myself into a corner finding all the two-space continuations that should've been four spaces :) Anyway, this change is awesome, and brings N-API closer to the JerryScript module initializer.

napi_value exports;
NAPI_CALL(env, napi_create_object(env, &exports));

NAPI_CALL(env, napi_define_properties(
Copy link
Contributor

Choose a reason for hiding this comment

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

Here too.

napi_value exports;
NAPI_CALL(env, napi_create_object(env, &exports));

NAPI_CALL(env, napi_define_properties(
Copy link
Contributor

Choose a reason for hiding this comment

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

Here too.

napi_value exports;
NAPI_CALL(env, napi_create_object(env, &exports));

NAPI_CALL(env, napi_define_properties(
Copy link
Contributor

Choose a reason for hiding this comment

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

Here too.

napi_value exports;
NAPI_CALL(env, napi_create_object(env, &exports));

NAPI_CALL(env, napi_define_properties(
Copy link
Contributor

Choose a reason for hiding this comment

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

Here too.

napi_value exports;
NAPI_CALL(env, napi_create_object(env, &exports));

NAPI_CALL(env, napi_define_properties(
env, exports, sizeof(properties) / sizeof(*properties), properties));
Copy link
Contributor

Choose a reason for hiding this comment

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

Here too.

napi_value exports;
NAPI_CALL(env, napi_create_object(env, &exports));

NAPI_CALL(env, napi_define_properties(
Copy link
Contributor

Choose a reason for hiding this comment

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

Here too.

napi_value exports;
NAPI_CALL(env, napi_create_object(env, &exports));

NAPI_CALL(env, napi_define_properties(
env, exports, sizeof(descriptors) / sizeof(*descriptors), descriptors));
Copy link
Contributor

Choose a reason for hiding this comment

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

Here too.

napi_value exports;
NAPI_CALL(env, napi_create_object(env, &exports));

NAPI_CALL(env, napi_define_properties(
env, exports, sizeof(properties) / sizeof(*properties), properties));
Copy link
Contributor

Choose a reason for hiding this comment

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

Here too.

napi_value exports;
NAPI_CALL(env, napi_create_object(env, &exports));

NAPI_CALL(env, napi_define_properties(
env, exports, sizeof(properties) / sizeof(*properties), properties));
Copy link
Contributor

Choose a reason for hiding this comment

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

Here too.

napi_value exports;
NAPI_CALL(env, napi_create_object(env, &exports));

NAPI_CALL(env, napi_define_properties(
env, exports, sizeof(descriptors) / sizeof(*descriptors), descriptors));
Copy link
Contributor

Choose a reason for hiding this comment

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

Here too.

@RReverser
Copy link
Member

Would it make sense to update docs together with actual impl changes or you want to do that in a separate PR?

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.

@mhdawson
Copy link
Member

I think we should update the docs in the same PR.

@boingoing
Copy link
Contributor Author

Thanks for reviewing, folks. I will update the docs along with the code changes.

@boingoing boingoing force-pushed the refactor_napi_addon_register_func branch from ea5c16d to 4fd3578 Compare August 30, 2017 23:36
@boingoing
Copy link
Contributor Author

@mhdawson, @gabrielschulhof Updated to add docs and address the spacing nitpicks (also fixed all the other line continuation indentation mistakes I could find in the addons-napi tests).

doc/api/n-api.md Outdated
napi_status status;
napi_property_descriptor desc =
{"hello", Method, 0, 0, 0, napi_default, 0};
napi_value exports;
status = napi_create_object(env, &exports);
status = napi_define_properties(env, exports, 1, &desc);
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: might want to update the sample to check the status of napi_create_object before calling napi_define_properties- it reads a little better, and helps folks reusing this sample from making a mistake

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That's fair. I noticed this sample and the one below both save the status code and then do nothing with it while I was updating the doc. Makes sense to have both of them do something when the status is not ok, especially now that the Init function return value has meaning.

env,
v8impl::JsValueFromV8LocalValue(exports),
v8impl::JsValueFromV8LocalValue(module),
mod->nm_priv);
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

@gabrielschulhof
Copy link
Contributor

After thinking about it for a while, I realized that, since most often modules consist of an object that has several functions, perhaps we should pass through exports as the second parameter (after the napi_env), while retaining the napi_value return value.

Thus, modules which populate exports need not create a new object, since Node has already done that.

Modules that overwrite exports can simply return the value with which they wish to overwrite.

On the implementation side, if the init callback returns NULL, we assume it has populated exports and we do nothing. If it returns non-NULL, we set the exports property on the module.

If Node ever went into a mode where it no longer supplies exports we can just create it from scratch and pass it in. Then, if the module returns NULL, the object we passed in is the result of having loaded the module, otherwise the return value is the result. That way it would be a convenience we provide which modules need not make use of.

I think the essence of the improvement in this PR is not the removal of the arguments, but rather the use of a return value. Setting module.exports is a really round-about way of saying the same thing that a return value says - i.e. that this is the result of having loaded the module.

@mhdawson
Copy link
Member

mhdawson commented Sep 5, 2017

In the last N-API meeting we agreed that we should add a reserved parameter as suggested by @boingoing above.

@gabrielschulhof I like the idea of avoiding forcing modules to create their own object. Once we figure out ES6 support we might even be able to recognize what type of module is being loaded and then provide the right object regardless of the type of module.

I wonder if it should be that we provide an object to which exports "can" be added (at least for existing CS modules for now) but that its up to the code to either return that object after they have made additions or return a different object. ie. the module must always return an object, but that it can be a modified version of the exports object that was passed in.

@gabrielschulhof
Copy link
Contributor

@mhdawson

I wonder if it should be that we provide an object to which exports "can" be added (at least for existing CS modules for now) but that its up to the code to either return that object after they have made additions or return a different object. ie. the module must always return an object, but that it can be a modified version of the exports object that was passed in.

👍
One possible future addition might be to examine the structure of the object such that if the object has a property named default, remove it from the object and it becomes the default export, and the rest of the object's properties become explicit exports.

@mhdawson
Copy link
Member

mhdawson commented Sep 6, 2017

@nodejs/n-api lets make sure to discuss this in our meeting this week so we can agree/close on the way forward.

@mhdawson
Copy link
Member

mhdawson commented Sep 7, 2017

We have agreed:

  1. to we won't include a reserved field
  2. We will require that an object is returned
  3. We will pass in an exports object to which exports can be added and that object returned in order to avoid modules having to create their own object.

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.
@boingoing boingoing force-pushed the refactor_napi_addon_register_func branch from fb68fc7 to 746af62 Compare September 14, 2017 09:38
@boingoing
Copy link
Contributor Author

Implemented pending feedback and rebased to resolve conflicts.

Copy link
Member

@mhdawson mhdawson left a comment

Choose a reason for hiding this comment

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

LGTM

@mhdawson
Copy link
Member

Landed as 92e5f5c

@mhdawson mhdawson closed this Sep 14, 2017
mhdawson pushed a commit that referenced this pull request Sep 14, 2017
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]>
addaleax pushed a commit to addaleax/ayo that referenced this pull request Sep 17, 2017
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: nodejs/node#15088
Reviewed-By: Gabriel Schulhof <[email protected]>
Reviewed-By: Michael Dawson <[email protected]>
Reviewed-By: Hitesh Kanwathirtha <[email protected]>
jasnell pushed a commit that referenced this pull request Sep 20, 2017
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]>
Qard pushed a commit to Qard/ayo that referenced this pull request Sep 21, 2017
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: nodejs/node#15088
Reviewed-By: Gabriel Schulhof <[email protected]>
Reviewed-By: Michael Dawson <[email protected]>
Reviewed-By: Hitesh Kanwathirtha <[email protected]>
gabrielschulhof pushed a commit to gabrielschulhof/node that referenced this pull request Apr 16, 2018
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: nodejs#15088
Reviewed-By: Gabriel Schulhof <[email protected]>
Reviewed-By: Michael Dawson <[email protected]>
Reviewed-By: Hitesh Kanwathirtha <[email protected]>
MylesBorins pushed a commit that referenced this pull request Apr 16, 2018
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.

Backport-PR-URL: #19447
PR-URL: #15088
Reviewed-By: Gabriel Schulhof <[email protected]>
Reviewed-By: Michael Dawson <[email protected]>
Reviewed-By: Hitesh Kanwathirtha <[email protected]>
@MylesBorins MylesBorins mentioned this pull request Apr 16, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
c++ Issues and PRs that require attention from people who are familiar with C++. lib / src Issues and PRs related to general changes in the lib or src directory. node-api Issues and PRs related to the Node-API.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants