-
Notifications
You must be signed in to change notification settings - Fork 1.2k
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
Add NAPI support #2545
Add NAPI support #2545
Conversation
This is not completely finished, it is a starting point for discussions. There are still a couple of unit tests that do not work (I have created separate issues for each one of them) and there is no testing infrastructure set up. This support comes in two flavors: with or without exceptions. I see that testing is always done with exceptions enabled - maybe there should be separate testing run without exceptions - but one will have to identify the tests that do not work in this case - there are quite a few. Also, the multi-threading is still not tested. |
BTW, there's an long-open issue requesting this: #1243 |
Does it make sense to support compiling Node modules in two ways? #1243 implies this was "experimental" before Node 10, but unless people are realistically still using older versions this seems an argument for bumping the minimum we aim to support. |
Alas, JavaScript has become infamous for its competing standards, modes, dialects and interfaces. After all, it is a language that grew from a quick hack to the most-widely used language in the decade after the web exploded. The problem is recognized by the community, so each time there is a new standard, they try very hard for it to be the very last one. N-API has been very successful in this aspect without a single compatibility-breaking change since it was introduced in 2017. |
I think I didn't make my point clearly. With NAPI support is there a good reason to keep supporting compiling node modules without NAPI? We already support javascript variants for V8 without node, node and jsc and people are working on others (#2334). That increase maintenance effort, and the manual testing required before pushing any change. We also really need CI testing for variants or else we can't have any confidence they'll continue to work, and the time CI takes to run is already sometimes a problem. It's possible this would mean dropping support for the oldest version(s) on node we currently claim to support, but we need to review the minimum supported versions periodically anyway. I suspect those versions are simply not used by anyone at this point (I'm not in the node community, but my impression was that in the earlier days of node everyone was upgrading to new versions almost eagerly). https://en.wikipedia.org/wiki/Node.js says they're all years past upstream end-of-life, though that's not always the best criteria since for some target languages distros continue to maintain them well after upstream stops. |
I've just finished off and merged #2455 which looks like it overlaps a bit with the changes here. The key points are that now: (a) C++ output is turned on by default for node (like happened already for v8-without-node) Sorry about the overlap but overall this should make things easier. Let me know if you need help updating for it. |
I plan to use this to create good Node.js bindings for ImageMagick-7 - which is a very good example, since it is a very important library that has been absent from the Node.js ecosystem because of its sheer size and complexity despite the demand for it - evident from the number of ersatz packages on |
Hi @mmontchev. I don't really understand the Javascript space, but seems like this could be very useful once complete. In the meantime, could you please make your next commit to this pull request one where all the napi Javascript tests pass on CI? Meanwhile you can test the GHA CI on your own account. I also suggest you modify ci.yml to only run Javascript while you develop it and fix the GHA CI. We discourage large scale development like this on branches used for pull requests because SWIG uses a lot of compute resource for each CI run. |
@wsfulton I am focusing on making a publishable module out of https://github.com/mmomtchev/node-magickwand - because this is the real NAPI support test - and then I will push through the various PRs / add some documentation |
@wsfulton @ojwb This is ready for review and ready to be used Multithreading/async is still missing and it is coming in a separate chunk |
@ojwb @wsfulton I am struggling with the Normally, this file is to be included in the node addon sources. It is not part of the main Node.js distribution and it is a set of templates. Most actual addons will install it through |
Isn't that |
Yes, but only in the beginning, when setting up the environment, during the build it gets overwritten with the out-of-source directory where the tests are copied one by one. This was creating the confusion, now I am getting the value in the beginning. |
@mmomtchev I think you must have the wrong patch number there as 1957 was merged over two years ago... |
The prerequisites for merging into master as described in Doc/Manual/Extending.html#Extending_prerequisites seem to be met except for the html documentation. Can you please add some napi specific documentation to Doc/Manual/Javascript.html then these will be met. I've run the beautify to make a few minor changes, which I can commit. When ready, I propose to merge the branch without squashing... are you happy with that or do you wish to do a bit of history rewrite via rebase first? If you do want to rebase, please let me know because I can't handle a rebase after starting the code review! I noticed a bug in the node implementation when running the class example (not necessary to fix unless you feel inspired). The output with node is
but is correct with napi:
|
I've reviewed the patch and it seems quite simple and fits in nicely to the current architecture by simply providing a class derived from JSEmitter in javascript.cxx. While there is quite a lot of copy/paste of files in Lib/javascript/napi from Lib/javascript/v8 that could be addressed, it does seem to be the way the v8 vs jsc was first implemented and not an unreasonable approach. Nice to see the code easily expanding to a new variant of Javascript. Well done to all the Javascript authors! Here are my comments that need addressing for the patch:
|
There are two interfaces - Node-API (formerly N-API) which is a plain C API that is part of the core distribution of Node.js. It is defined in
One of the main reasons for this separation is that this way binary addons use only the well-defined plain C linking which works very well across different compilers. This allows a Node.js addon publisher to distribute a binary on NPM which will work with all Node.js binaries that an end-user might have - without regard if one has been built with the When it comes to SWIG, these files are only needed when building the examples and test suite. I did hesitate a lot what to do with these and I ended placing them in |
So to summarise, if I have understood correctly, the generated code is actually targeting As SWIG is actually targeting |
It is declared as broken only for JS now. However, in reality, this test produces invalid code for most target languages. It is part of the larger problem of having a shared namespace for static and non-static member functions.
Indeed, I moved this file out to the Javascript root. The current list includes the union of all words that cannot be used as identifiers in ECMAScript 4 to 6, both in sloppy and in strict mode. The list has had a few new items since it was last updated.
I will wrap up the documentation and this should be ready. The current implementation works out of the box with the yeoman skeleton generator: npm install -g yo
npm install -g generator-napi-module
mkdir hello-world
cd hello-world
yo napi-module |
@wsfulton I think to have addressed all the review suggestions and I updated the documentation section. As the PR is very sizable and contains a very large number of separate commits, it is natural to squash it to a single commit message in order to keep the blame log tidy and readable. Normally, when squashing, GitHub takes automatically care of attributing the changes to their respective authors. If someone manually merges from the git CLI, the author must be specified with |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- There are a couple of minor points to be addressed which I've added as review comments.
- The initial merge request comments about the advantages should be in the docs. I'd encourage you to add any other interesting aspects regarding the JavaScript wrappers.
- Can you add an appropriate detailed README entry for users.
When these minor points are addressed, I'll squash and merge. Author attribution is important, don't worry, I'll get that right. Can you please also add your name into the list of major contributors / authors in the COPYRIGHT file.
Co-authored-by: William S Fulton <wsf@fultondesigns.co.uk>
Co-authored-by: William S Fulton <wsf@fultondesigns.co.uk>
@wsfulton, which README are you referring to? |
Sorry, my bad, I meant the CHANGES.current file in the root directory. |
Thanks @mmomtchev, it's been a pleasure reviewing this. Thanks for your prompt responses and getting this patch into a state that fits very well with what is expected for a major contribution to SWIG. |
@@ -79,7 +79,7 @@ <H3><a name="Javascript_running_swig">28.2.1 Running SWIG</a></H3> | |||
int gcd(int x, int y); | |||
extern double Foo;</pre> | |||
</div> | |||
<p>To build a Javascript module, run SWIG using the <code>-javascript</code> option and a desired target engine <code>-jsc</code>, <code>-v8</code>, or <code>-node</code>. The generator for <code>node</code> is essentially delegating to the <code>v8</code> generator and adds some necessary preprocessor definitions.</p> | |||
<p>To build a Javascript module, run SWIG using the <code>-javascript</code> option and a desired target engine <code>-jsc</code>, <code>-v8</code>, <code>-node</code> or <code>-napi</code>. <code>-v8</code> allows for interfacing with a raw embedded version of V8. In this case, it is up to the user to implement a binary module loading protocol. There are two generators supporting Node.js. The older generator for <code>node</code> is essentially delegating to the <code>v8</code> generator and adds some necessary preprocessor definitions. The more recent <code>-napi</code> generator produces <code>node-addon-api</code> that interfaces to Node.js through Node-API. The V8 generator is more mature, while the Node-API generator offers a number of advantages such as binary stable ABI allowing for publishing of universal binary modules on npm, Electron support and automatic multi-threading.</p> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reference to "automatic multi-threading" is misleading. NAPI effectively forces modules to be "context-aware," but it doesn't make the generated code MT-safe. More correct formulation would probably be "provides Javascript Workers-aware environment for module developer to utilize."
For reference, MT-safety has multiple meanings in the context. One is "having multiple module threads interact with the same context," another is "having multiple Javascript Workers interact with the module." NAPI makes the former usable. And in the context of the latter it facilitates per-Worker state maintenance. But both are on the module developer to implement, not on SWIG. In other words there is nothing "automatic" about it, hence the objection. Just in case, it's exactly as "automatic" as #1973. The tests that pass there simply don't have global mutable states, which makes them MT-safe "naturally."
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you post a patch here or a new github issue and I'll get this committed. Thanks.
NAPI support is the future of JavaScript bindings - it is a coordinated effort across the industry to produce a universal interface for speaking to a JS engine. At the moment, it is still fully implemented only by Node.js/V8, but there is ongoing work to support it in Hermes.
NAPI modules present a number of advantages: