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

Making the compiling of native module independent of vm environment #35

Closed
jinderek opened this issue Dec 14, 2016 · 5 comments
Closed

Comments

@jinderek
Copy link

jinderek commented Dec 14, 2016

When we use NAPI to write native module, the API is vm neutral and stable. But the compiling is still binding to vm implementation, like, using the vm shared library.

For example, "napi_set_return_value" needs to link with the its implementation in the node_jsvmapi.cc, which is vm related.

I think if it was better to separate the API implementation, make the compiling of native module totally vm neutral, and to be binary compatible for different vms?

Below are some simple ideas:
We may use the env pointer to store the API functions' pointers, and bind them to implemented functions in vm in runtime.

The API declaration and implementation look like:

node_jsvmapi.h:

// The API functions are stored in env.
struct napi_env__ {

...

void (*napi_set_return_value)(napi_env e, napi_func_cb_info cbinfo,
                                       napi_value v)
...
};

and:

node_jsvmapi.cc:

// API VM implementations.
void napi_set_return_value(napi_env e, napi_func_cb_info cbinfo,
                                       napi_value v) {
...
}

// Corresponding to functions in node_jsvmapi.h
struct napi_env__ api_functions = {
  ...
  napi_set_return_value
  ...
};


when vm loads the module, we bind the API implementation to env pointer.

bind_api_functions_to_env(api_functions, env)

So finally, native method(hello.cc) looks like:

#include <node_jsvmapi.h>

void Method(napi_env env, napi_func_cb_info info) {
// Call using env.
  env->napi_set_return_value(
        info,
        env->napi_create_string("world"));
}

Then we just need the only node_jsvmapi.h to compile and link native module(no need of vm shared library or environment).

@digitalinfinity
Copy link
Contributor

Thanks @Xuyv - this is an interesting idea. One downside of doing this is that every NAPI call becomes an indirect call, which has perf implications. Can you describe a concrete scenario where the current NAPI approach does not work for you? Note that at Node-Interactive, @aruneshchandra demoed a single NAPI-enabled module, compiled once, and then executed with node v0.10, v0.12, v6, and node-chakracore

@jinderek
Copy link
Author

jinderek commented Dec 15, 2016

@digitalinfinity I think it has no problem for NAPI module compiling once and then being executed on different versions of node now. The thing that concerns me is that once the api changes( like, add one more function argument in napi_set_return_value) in new NAPI, the old compiled modules will not work.

If function pointers are stored in env, we may control api changes more elegantly and easily. For example, (just an idea)store different functions in env for new versions of NAPI when native module are loaded. And it's opaque to developers.

@digitalinfinity
Copy link
Contributor

Ah, I understand now- so in this world, the add-on would query node for the environment and ask for a particular version of the API, and then proceed to call it? Interesting. In that case, would node need to preserve all the old versions of the APIs, so that the modules could still call this?

One other way this can be solved is, NAPI APIs can never be removed - it's a purely an additive interface- so new APIs being added would not break modules coded against the old API since those APIs would continue to ship. Thoughts? @mhdawson was the plan for the NAPI interface planned to be additive?

@mhdawson
Copy link
Member

mhdawson commented Jan 3, 2017

I have also thought that using a function table could make migration easier and have raised it with the overall team in the past. Other platforms like Java use one and it makes it possible to support multiple versions of the API, and have modules request the version that they were compiled against.

The downside as mentioned is the performance overhead due to indirect calls. If we had lots of free cycles I'd have us build with/without a function table and do some performance comparisons but I don't think we'll have the cycles in the short term. We should discuss this one of our upcoming weekly meetings to get more input/discussion so that we can document our decision/rational on this point.

In terms of the specific questions, in general I do see it as being additive. We'll still need some
sort of versioning so that a module can figure out what version its being compiled against so that
it can figure out which function are available.

@digitalinfinity
Copy link
Contributor

This was discussed at several VM summits and we decided to stick to our current approach

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants