You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
This repository was archived by the owner on May 7, 2022. It is now read-only.
Repository navigation
This repository was archived by the owner on May 7, 2022. It is now read-only.
JXCore is an "extended Node.js API" that can bind to Spidermonkey, V8 and Chakra.
Because of Node.js' massive ecosystem of packages, educational materials, and mind-share we should expect that in the future this will continue. Part of Node.js going everywhere is that it may need to be on other VMs in environments V8 can't go. There's not much that we can do to prevent this.
So the question becomes: Should Node.js Core move towards being VM neutral and supporting more VMs in the main project?
Some of the advantages would be:
Normalization of the Native API (which currently breaks every major release)
De-facto standard for any JS VM to add Node.js support.
Centralization of test infrastructure would increase stability of non-V8 platforms.
Standardization of API could allow us to adopt new V8 versions outside of breaking major releases.
There's a long discussion about how to do this. Without guarantees from all the target VM vendors that they will support this neutral API it could fall on us to make that work. Historically V8 has made drastic API changes that were not supportable through the API nan had implemented.
There's also an open question about how to structure the tree and build and if we should continue sticking V8 and every supported VM in a vendor directory or pulling it in during build time.
How do we handle the compatibility issues? Speaking from a package maintainer's perspective. Worst case scenario: This will be the cross-browser hell all over again (albeit on a smaller scale because no DOM)
I'm very much in favor of this. My hope is that the WebAssembly story is a big part of this. It would be fantastic to have this on a roadmap in a way that allows Node.js to become part of the WebAssembly discussions, as this is already an area with some potential roadmap to the shared-VM discussion where the major VM vendors are already working on common APIs.
JXcore is an "extended Node.js API" on top of the JVM.
Correct me if I'm wrong, but JXCore is not based on JVM AFAIK. It just provides extended Node.js API on top of one of any chosen JavaScript engine among supported (all of V8, SpiderMonkey and Chakra are supported).
@mikeal Nice. Although I see that you removed JXCore completely, while I believe it does belong to the list and discussion, as it also adds couple of supported non-V8 engines.
@MadaraUchiha So, the native module ecosystem currently binds directly to V8 (often through nan which abstracts the V8 API). We suffer a break in compatibility every time we take a new version of V8 because of that, so moving towards a neutral (stable) API might actually improve compatibility beyond what we currently have.
Sounds like a good idea to make node VM agnostic. My question would be: is there a difference in terms of effort, speed to fix 0-day vulns, maintenance if node is V8-centric or not?
Not on any of the teams, or really important in any way, but I wanted to kind of re-phrase simplistically what I think the majority view of this is going to be:
This is almost incontrovertibly Good for The Community. Good for users. A good thing.
This is a lot of work. Work that could, theoretically, be invested elsewhere.
Unless I'm super-off-base there, then, I'd assume that this conversation needs to directly assault “whether that amount of work being spent on this effort is proportional to the benefits to users.”
(Sorry if this seems obvious! I just didn't see it laid out in so many words above.)
@mikeal Wouldn't this mean that this abstracted API would have to always be the lowest common denominator in terms of newest ES features etc? Otherwise, you're risking syntax and APIs working in one build, but breaking on another.
Another option is to put newest features behind a flag, but that's kind of meh, and we've been trying to (at least I think) avoid that going forward.
My thoughts exactly @ELLIOTTCABLE . I think before node jumps on "let's do it because it's good" let's ask if they're accidentally overcommitting and a using time that could be better used improving aspects of node.
If the aim is to bring node to new platforms (like Windows Phone) and benefit in speed (e.g. on Windows using Chakra) and keep everything under the hood without changing end-user API then it's the way to go. (e.g. node for Windows will use Chakra as default engine, for MacOs - V8, and so on). But if developers will start using language features available only in the particular engine it may cause significant fragmentation which is not a good thing.
@DemiMarie agreed. FFI does have its interesting merits, but I think mostly as a convenience to the end users, and I don't know if the effort is worth the benefit. @ofrobots is experimenting with this idea. I am interested to see what he comes up with.
@kobalicek I think the overhead may can not be avoided NOW. The vms are already mature and different, we have to do some sacrifice in the implementation.
@mhdawson on So you wrap C++ API in C API and then create a C++ layer to provide a sugar on top of the C API. For me this seems like a step back and moving backward, sorry.
@Xuyv Talking about overhead without having the overhead measured is premature. If you wrap every interaction with V8 that was now inlined into a function call then the overhead can be even 5x/10x, which doesn't seem like a good idea, especially if you consider that node.js is a high-performance environment.
BTW: @mhdawson - I think you are doing it the opposite - if you decide to change the API you should first start with node internals and then propagate these changes to node modules, and not to expose the new thing (that will be highly untested and unstable in the early beginning) into all the modules that depend on V8 now and keep the old API internally. Another reason is that the new API should really be able to do everything the old API does.
I just hope I will be able to use V8 in the future and keep my modules high-performance, others are fine to use the new API :)
The net seems to be that the stable ABI would be in C, but C++ wrappers that are all in-lined and only use the stable ABI are possible because the C++ would be built into the module and then module could continue to work provided the exports in C are ABI stable.
Discussion around whether to change the internal use/module use was extensive at the vm summit with people landing on starting with the modules first. The discussion was that the ABI stable API could address 90-95% of native modules, while those with specific requirements could continue to use the v8 APIs directly.
We are at the point of starting to assess the performance overhead in these issues (still early days though so take them with a grain of salt):
If we will be allowed to use V8 directly then I have no problem with that. I personally consider wrapping this in a C API (with a lot of external functions) a significant overhead that I just can't accept. I have already tried to make a C++ wrapper around V8 API in a way to hide everything, my attempt is available here: https://github.com/kobalicek/njs
The thing is, from my own experience, that wrapping VM APIs in a low-level way is much more complicated than creating higher level interface, because their low-level API is the thing that is the most different. For example compare how you wrap classes in V8 and in SpiderMonkey - both engines provide a completely different way of doing that thing.
The way I use NJS looks like this (sorry for a bit long-code, showing only one simple class):
The code itself has zero overhead as it doesn't have to call external functions, everything possible is inlined, everything that would expand is marked NJS_NOINLINE to expand only once in a binary. Maybe I can discuss my way with some nodejs dev somewhere? Not sure if this is the right thread.
I think that the overhead of a stable ABI may just be too large (one good
reason to have a JIT-integrated FFI). Indeed, most languages have
abandoned ABIs, and instead required transitive recompilation of reverse
dependencies
We need to do benchmarks of JS->native and native->JS calls and other API
use.
If one is to have a stable ABI, it needs to still be fast enough that
people don't need to drop down to the VM native API, defeating the purpose.
One option is for the C wrapper to be built with the VM and compiled using
link-time optimization (LTO).
The code itself has zero overhead as it doesn't have to call external functions, everything possible is inlined, everything that would expand is marked NJS_NOINLINE to expand only once in a binary.
That sounds like the approach that nan takes but that is not good enough for what we're discussing here because it only maintains source compatibility, not binary compatibility (API vs. ABI.)
It's not exactly what NaN does - NaN builds on top of V8, NJS defines interface which is then provided by V8-integration layer. But yes, it's not ABI, it's a source-level compatibility layer.
Hmm, no. That wouldn't help with the problem of API/ABI breakage with different versions of node/v8. I believe that would be more or less equivalent to the status quo, albeit deferring compilation to runtime which would impact performance. It would may also have issues on Windows, not sure how good libclang's MSVC compatibility is.
It would solve the ABI problem by doing requiring that source always be
available. The compiled code could (and should) be put in a persistent
cache, so the performance penalty could be minimized.
But this is just working around the problem. I once believed that a
standard FFI integrated into each engine's JIT is the solution. But it is
a LOT of work that has basically no use in browsers for obvious security
reasons, since JS lacks adequate encapsulation to allow writing fully safe
wrappers, nor does it have in-realm access control. An FFI works great for
languages like Rust, OCaml, D, C#, and Haskell that enforce encapsulation,
allowing wrappers to check arguments. Doing the same in JS is nearly
impossible, since JS has too much reflection.
Another issue is that any FFI will allow crashing the VM or – worse –
C-style memory unsafety security vulnerabilities.
If we do use an FFI exclusively, there will need to be ways for library
authors to provide safe wrappers with little performance penalty.
Hmm, no. That wouldn't help with the problem of API/ABI breakage with
different versions of node/v8. I believe that would be more or less
equivalent to the status quo, albeit deferring compilation to runtime which
would impact performance. It would may also have issues on Windows, not
sure how good libclang's MSVC compatibility is.
Activity
Fishrock123 commentedon Jan 22, 2016
Also, some increased competition in this area will likely push for performance improvements in VMs I think, which would be really good for us.
misterdjules commentedon Jan 22, 2016
I didn't know JXcore had anything to do with the JVM. Where can I find more details about this?
MadaraUchiha commentedon Jan 22, 2016
How do we handle the compatibility issues? Speaking from a package maintainer's perspective. Worst case scenario: This will be the cross-browser hell all over again (albeit on a smaller scale because no DOM)
brycebaril commentedon Jan 22, 2016
I'm very much in favor of this. My hope is that the WebAssembly story is a big part of this. It would be fantastic to have this on a roadmap in a way that allows Node.js to become part of the WebAssembly discussions, as this is already an area with some potential roadmap to the shared-VM discussion where the major VM vendors are already working on common APIs.
RReverser commentedon Jan 22, 2016
Correct me if I'm wrong, but JXCore is not based on JVM AFAIK. It just provides extended Node.js API on top of one of any chosen JavaScript engine among supported (all of V8, SpiderMonkey and Chakra are supported).
mikeal commentedon Jan 22, 2016
@misterdjules @RReverser you're right, my bad, I was confusing Nodyn with JXCore. Updated the description.
RReverser commentedon Jan 22, 2016
@mikeal Nice. Although I see that you removed JXCore completely, while I believe it does belong to the list and discussion, as it also adds couple of supported non-V8 engines.
mikeal commentedon Jan 22, 2016
@MadaraUchiha So, the native module ecosystem currently binds directly to V8 (often through
nanwhich abstracts the V8 API). We suffer a break in compatibility every time we take a new version of V8 because of that, so moving towards a neutral (stable) API might actually improve compatibility beyond what we currently have.indutny commentedon Jan 22, 2016
I don't think that it will create more competition than there is for browsers, but it is surely a good thing in my opinion.
There are lots of obstacles on this way though: addon compatibility, ES6 features, etc
mikeal commentedon Jan 22, 2016
@RReverser it's back now :)
rdodev commentedon Jan 22, 2016
Sounds like a good idea to make node VM agnostic. My question would be: is there a difference in terms of effort, speed to fix 0-day vulns, maintenance if node is V8-centric or not?
ELLIOTTCABLE commentedon Jan 22, 2016
Not on any of the teams, or really important in any way, but I wanted to kind of re-phrase simplistically what I think the majority view of this is going to be:
Unless I'm super-off-base there, then, I'd assume that this conversation needs to directly assault “whether that amount of work being spent on this effort is proportional to the benefits to users.”
(Sorry if this seems obvious! I just didn't see it laid out in so many words above.)
MadaraUchiha commentedon Jan 22, 2016
@mikeal Wouldn't this mean that this abstracted API would have to always be the lowest common denominator in terms of newest ES features etc? Otherwise, you're risking syntax and APIs working in one build, but breaking on another.
Another option is to put newest features behind a flag, but that's kind of meh, and we've been trying to (at least I think) avoid that going forward.
rdodev commentedon Jan 22, 2016
My thoughts exactly @ELLIOTTCABLE . I think before node jumps on "let's do it because it's good" let's ask if they're accidentally overcommitting and a using time that could be better used improving aspects of node.
inikulin commentedon Jan 22, 2016
If the aim is to bring node to new platforms (like Windows Phone) and benefit in speed (e.g. on Windows using Chakra) and keep everything under the hood without changing end-user API then it's the way to go. (e.g. node for Windows will use Chakra as default engine, for MacOs - V8, and so on). But if developers will start using language features available only in the particular engine it may cause significant fragmentation which is not a good thing.
120 remaining items
ianwjhalliday commentedon Jul 19, 2016
@DemiMarie agreed. FFI does have its interesting merits, but I think mostly as a convenience to the end users, and I don't know if the effort is worth the benefit. @ofrobots is experimenting with this idea. I am interested to see what he comes up with.
yuej-jin commentedon Jul 29, 2016
@kobalicek I think the overhead may can not be avoided NOW. The vms are already mature and different, we have to do some sacrifice in the implementation.
kobalicek commentedon Jul 29, 2016
@mhdawson on So you wrap C++ API in C API and then create a C++ layer to provide a sugar on top of the C API. For me this seems like a step back and moving backward, sorry.
@Xuyv Talking about overhead without having the overhead measured is premature. If you wrap every interaction with V8 that was now inlined into a function call then the overhead can be even 5x/10x, which doesn't seem like a good idea, especially if you consider that node.js is a high-performance environment.
BTW: @mhdawson - I think you are doing it the opposite - if you decide to change the API you should first start with node internals and then propagate these changes to node modules, and not to expose the new thing (that will be highly untested and unstable in the early beginning) into all the modules that depend on V8 now and keep the old API internally. Another reason is that the new API should really be able to do everything the old API does.
I just hope I will be able to use V8 in the future and keep my modules high-performance, others are fine to use the new API :)
mhdawson commentedon Aug 2, 2016
@kobalicek the challenge is that it may just not be possible to maintain ABI stability while using C++. Ian found this document which covers some of the issues: http://www.oracle.com/technetwork/articles/servers-storage-dev/stablecplusplusabi-333927.html.
The net seems to be that the stable ABI would be in C, but C++ wrappers that are all in-lined and only use the stable ABI are possible because the C++ would be built into the module and then module could continue to work provided the exports in C are ABI stable.
Discussion around whether to change the internal use/module use was extensive at the vm summit with people landing on starting with the modules first. The discussion was that the ABI stable API could address 90-95% of native modules, while those with specific requirements could continue to use the v8 APIs directly.
We are at the point of starting to assess the performance overhead in these issues (still early days though so take them with a grain of salt):
So that is definitely part of the analysis.
kobalicek commentedon Aug 2, 2016
If we will be allowed to use V8 directly then I have no problem with that. I personally consider wrapping this in a C API (with a lot of external functions) a significant overhead that I just can't accept. I have already tried to make a C++ wrapper around V8 API in a way to hide everything, my attempt is available here: https://github.com/kobalicek/njs
The thing is, from my own experience, that wrapping VM APIs in a low-level way is much more complicated than creating higher level interface, because their low-level API is the thing that is the most different. For example compare how you wrap classes in V8 and in SpiderMonkey - both engines provide a completely different way of doing that thing.
The way I use NJS looks like this (sorry for a bit long-code, showing only one simple class):
The code itself has zero overhead as it doesn't have to call external functions, everything possible is inlined, everything that would expand is marked NJS_NOINLINE to expand only once in a binary. Maybe I can discuss my way with some nodejs dev somewhere? Not sure if this is the right thread.
DemiMarie commentedon Aug 3, 2016
bnoordhuis commentedon Aug 3, 2016
That sounds like the approach that nan takes but that is not good enough for what we're discussing here because it only maintains source compatibility, not binary compatibility (API vs. ABI.)
kobalicek commentedon Aug 3, 2016
It's not exactly what NaN does - NaN builds on top of V8, NJS defines interface which is then provided by V8-integration layer. But yes, it's not ABI, it's a source-level compatibility layer.
DemiMarie commentedon Sep 8, 2016
A rather wild option:
What about using libclang and LLVM to JIT compile C++ add-ons at run-time?
ianwjhalliday commentedon Sep 13, 2016
Hmm, no. That wouldn't help with the problem of API/ABI breakage with different versions of node/v8. I believe that would be more or less equivalent to the status quo, albeit deferring compilation to runtime which would impact performance. It would may also have issues on Windows, not sure how good libclang's MSVC compatibility is.
DemiMarie commentedon Sep 14, 2016
It would solve the ABI problem by doing requiring that source always be
available. The compiled code could (and should) be put in a persistent
cache, so the performance penalty could be minimized.
But this is just working around the problem. I once believed that a
standard FFI integrated into each engine's JIT is the solution. But it is
a LOT of work that has basically no use in browsers for obvious security
reasons, since JS lacks adequate encapsulation to allow writing fully safe
wrappers, nor does it have in-realm access control. An FFI works great for
languages like Rust, OCaml, D, C#, and Haskell that enforce encapsulation,
allowing wrappers to check arguments. Doing the same in JS is nearly
impossible, since JS has too much reflection.
Another issue is that any FFI will allow crashing the VM or – worse –
C-style memory unsafety security vulnerabilities.
If we do use an FFI exclusively, there will need to be ways for library
authors to provide safe wrappers with little performance penalty.
On Sep 13, 2016 15:19, "Ian Halliday" notifications@github.com wrote:
valera-rozuvan commentedon Aug 21, 2017
A year has passed without a comment. Has this been discussed elsewhere? Will there be a common ABI for a VM to integrate itself with Node?
williamkapke commentedon Aug 21, 2017
Yup! check out: https://github.com/nodejs/vm
Trott commentedon May 6, 2022
Closing all issues in this archived repository.