Uh oh!
There was an error while loading. Please reload this page.
Update code to support N-API on older versions of node - #25
Conversation
| void SetReturnValue(napi_value value) override { | ||
| node::FatalError("napi_set_return_value", | ||
| "Cannot return a value from a setter callback."); | ||
| assert(false); // Cannot return a value from a setter callback |
There was a problem hiding this comment.
Isn't assert() a no-op on a release build of node? Then this will not accomplish what we want when built into node. If it did, you could use it for CHECK_ENV() as well.
There was a problem hiding this comment.
What about simply forward-declaring node::FatalError() if internals are unavailable? Would
#if not (defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS)
namespacenode {
voidFatalError(constchar *location, constchar *message);
}
#endifwork?
There was a problem hiding this comment.
Isn't
assert()a no-op on a release build of node?
What matters is whether this code is compiled in debug mode or not, not what node was compiled as. But yes, in release mode this will be a no-op.
(Also, can you #include <assert.h> if you’re going with this?)
What about simply forward-declaring
node::FatalError()if internals are unavailable?
Please don’t make code rely on things that Node has explicitly marked as internals, you’ll be sad when they go away (also, it wouldn’t work because node doesn’t dllexport them on Windows). If you really want access to node::FatalError(), a better option would be to open a PR that moves it to the public headers.
There was a problem hiding this comment.
I've tested
// Node internals needed even when built outside of Node.js
#if not (defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS)
#ifdef __GNUC__
#defineNO_RETURN__attribute__((noreturn))
#else
#defineNO_RETURN
#endifnamespacenode {
NO_RETURNvoidFatalError(constchar* location, constchar* message);
}
#endifand it works.
There was a problem hiding this comment.
There was a problem hiding this comment.
(Also, can you #include <assert.h> if you’re going with this?)
This is at the top of napi-inl.h:
#include<cassert>There was a problem hiding this comment.
But wait, that isn’t included from node_api.cc – it should probably really have a cassert include of its own?
There was a problem hiding this comment.
Note if you implement a setter function in JavaScript (via Object.defineProperty()), and you return some value from the setter function, the value is simply ignored. That suggests it should be ignored here also. (So maybe the assert should be removed also.)
| void SetReturnValue(napi_value value) override { | ||
| node::FatalError("napi_set_return_value", | ||
| "Cannot return a value from a setter callback."); | ||
| assert(false); // Cannot return a value from a setter callback |
There was a problem hiding this comment.
Isn't
assert()a no-op on a release build of node?
What matters is whether this code is compiled in debug mode or not, not what node was compiled as. But yes, in release mode this will be a no-op.
(Also, can you #include <assert.h> if you’re going with this?)
What about simply forward-declaring
node::FatalError()if internals are unavailable?
Please don’t make code rely on things that Node has explicitly marked as internals, you’ll be sad when they go away (also, it wouldn’t work because node doesn’t dllexport them on Windows). If you really want access to node::FatalError(), a better option would be to open a PR that moves it to the public headers.
| // Currently the environment event loop is the same as the UV default loop. | ||
| // Someday (if node ever supports multiple isolates), it may be better to get | ||
| // the loop from node::Environment::GetCurrent(env->isolate)->event_loop(); | ||
| uv_loop_t* event_loop = uv_default_loop(); |
There was a problem hiding this comment.
I’m not sure, this might not work when there are native modules that use a different loop, or Node embedders that use multiple loops… but nan does it too so it can’t be so terribly bad I guess?
There was a problem hiding this comment.
Right, nan does this now. Other native modules are free to use non-default loops, if they call the libuv APIs directly, and that won't cause any problems with this.
Eventually, we might wrap more of the libuv APIs in N-API, and that would support non-default loops.
There was a problem hiding this comment.
nan supports pick-and-mix. If the default loop is too restrictive, you can fall back to libuv. If the point here is to abstract away libuv, there needs to be a way of specifying a non-default loop without having to call the libuv API directly.
There was a problem hiding this comment.
As I said just above, these APIs are not intended to entirely abstract away libuv, at least not at this time.
gabrielschulhof
commented
Apr 18, 2017
When building against node-api on Windows every N-API symbol remains undefined. Does gyp properly generate the rules to statically link node-api into its dependent? |
gabrielschulhof
commented
Apr 18, 2017
On Windows you need to add |
OK, that can be accomplished with "conditions": [
[ "'<!(node -p \"require('node-api').isNodeApiBuiltin\")'=='false'", {
"libraries+": [
"<(PRODUCT_DIR)/node-api.lib"
]
} ]
]Yet, and assuming that file really does get linked in, the unresolved symbols persist. Perhaps the definition of NAPI_EXTERN is improper. |
@jasongin can you help me out please? https://gist.github.com/gabrielschulhof/2aa53679e517ad9985052d71b7f4c11e works fine on Linux but I can't figure out why all N-API symbols are unresolved on Windows. If it complains about symbols of the form |
gabrielschulhof
commented
Apr 18, 2017
Nope. I'm really grasping at straws here :) |
jasongin
commented
Apr 18, 2017
@gabrielschulhof I haven't had problems building on Windows. The .gyp dependency mentioned in the README should cause the .lib file to be linked automatically: 'dependencies': ["<!(node -p \"require('node-api').gyp\")"],The APIs are static-linked, so there should be no need for |
gabrielschulhof
commented
Apr 18, 2017
I ended up compiling in N-API: "conditions": [
[ "'<!(node -p \"require('node-api').isNodeApiBuiltin\")'=='false'", {
"defines+": [ "EXTERNAL_NAPI" ],
"include_dirs+": ["<!(node -p \"require('node-api').include\")"],
"sources+": [
"<!(node -p \"var path = require('path'); path.join(path.dirname(require.resolve('node-api')), 'src', 'node_api.cc')\")"
]
} ]
] |
jasongin
commented
Apr 18, 2017
I think all that should be replaced with the .gyp dependency. |
gabrielschulhof
commented
Apr 18, 2017
@jasongin ... and the .gyp dependency works - on Linux, but not on Windows. The fact is that by the nature of gyp you have to put so many calls to |
gabrielschulhof
commented
Apr 18, 2017
Having it all in one condition like that is actually nice, because all calls to node-api are localized. |
jasongin
commented
Apr 18, 2017
It works for me on Windows. The tests at #24 build that way. |
gabrielschulhof
commented
Apr 18, 2017
Yeah, I dunno why it doesn't work for me. Must be some combination of VS/node.js/npm/node-gyp. |
gabrielschulhof
commented
Apr 18, 2017
I have the toolchain from npm -g install windows-build-tools, so that's, like, the free version. Maybe that has some limitations. |
jasongin
commented
Apr 18, 2017
I think it's more likely a node-gyp issue. What version of |
gabrielschulhof
commented
Apr 18, 2017
3.5.0 |
gabrielschulhof
commented
Apr 18, 2017
Oh, my approach doesn't work with [ "'<!(node -p \"!!process.version.match(/^v8/)\")'=='false'", {
"defines+": [ "EXTERNAL_NAPI" ],
"include_dirs+": [ "src/n-api" ],
"sources+": [ "src/n-api/node_api.cc" ]
} ] |
jasongin
commented
Apr 18, 2017
Oh, for Windows I had to flip some quotes in the test |
gabrielschulhof
commented
Apr 18, 2017
Hmmm ... lemme try that. |
gabrielschulhof
commented
Apr 18, 2017
Back to the unresolveds in Windows, with: {
"targets": [ {
"target_name": "hello",
"sources": [ "hello.cc" ],
"conditions": [
[ "'<!(node -p \"require('node-api').isNodeApiBuiltin\")'=='false'", {
"include_dirs+": ["<!(node -p \"require('node-api').include\")"],
"dependencies+": ["<!(node -p \"require('node-api').gyp\")"],
} ]
]
} ]
} |
jasongin
commented
Apr 18, 2017
Try removing the condition. You shouldn't need to put those lines inside a conditional block anyway. The include path is needed either way, and the |
Also, I'm unfamiliar with |
gabrielschulhof
commented
Apr 18, 2017
AFAIK a '+' suffix means "add to an existing list if present". |
gabrielschulhof
commented
Apr 18, 2017
It's now down to {
"targets": [ {
"target_name": "hello",
"sources": [ "hello.cc" ],
"include_dirs": ["<!(node -p \"require('node-api').include\")"],
"dependencies": ["<!(node -p \"require('node-api').gyp\")"]
} ]
}Still no joy. |
gabrielschulhof
commented
Apr 18, 2017
... and I can see it building |
gabrielschulhof
commented
Apr 18, 2017
Anyhoo - looks like we're headed for non-fatal errors here, but still, this is what it would look like to copy the (condensed version of) https://github.com/gabrielschulhof/node-api/commit/76f3a759864086021472ddc518f378003552a9b7 |
jasongin
commented
Apr 18, 2017
The .gyp dependency should get translated into a <ProjectReferenceInclude="path\to\node-api.vcxproj">
<Project>{48DE6A98-BCC5-AED1-43A1-3BA1AC79F59F}</Project>
<ReferenceOutputAssembly>false</ReferenceOutputAssembly>
</ProjectReference> |
gabrielschulhof
commented
Apr 18, 2017
It's there: <ProjectReferenceInclude="..\node_modules\node-api\src\node-api.vcxproj">
<Project>{C6B3FC4A-6125-FF58-8F61-7C7914BA71AA}</Project>
<ReferenceOutputAssembly>false</ReferenceOutputAssembly>
</ProjectReference> |
jasongin
commented
Apr 18, 2017
Try this: cd build
"C:\Program Files (x86)\MSBuild\14.0\Bin\MSBuild.exe" /t:Rebuild /p:Configuration=Release /p:Platform=x64 > build.logThen put the |
gabrielschulhof
commented
Apr 18, 2017
It's too bad |
addaleax
commented
Apr 18, 2017
@gabrielschulhof long shot because I didn’t read all the context and am not a Windows person, but does |
mhdawson
left a comment
There was a problem hiding this comment.
LGTM, I'm +1 one on moving the 2nd commit back to node core so that the implementation is as consistent as possible
gabrielschulhof
commented
Apr 18, 2017
@addaleax actually, yes! Unfortunately, it doesn't help :/ |
jasongin
commented
Apr 18, 2017
Oh, I think I know how to fix your build: in The reason is you need the special This might not be the best way to set things up. I'm open to suggestions, but that was the best solution I could come up with to support building against both built-in N-API and external N-API, without modifying the |
Copy the latest node_api* source files from the node master branch, to pick up all the changes from the past few weeks.
Recent changes introduced dependencies on internal node APIs: - Environment::GetCurrent() - FatalError() - arraysize To enable building this code as an external module, compatible back to Node v4, the internal APIs must be avoided. Also replace use of v8::Maybe::ToChecked() (available in Node >= 7) with FromJust(), which does excatly the same thing and works in Node v4.
jasongin
commented
Apr 20, 2017
@gabrielschulhof gave verbal approval regarding the removal of With that and the other approvals, I'm merging this. I will prepare a PR to node master with the changes from the second commit here, so that the sources stay in sync. And since there are other changes in the pipeline there, we will of course need to update the sources here again, but then the changes will be relatively small. |
Background: To enable N-API support for node versions back to v4, the N-API code can also be built as an external addon. To make maintenance easier, a single codebase needs to support both built-in and external scenarios, along with Node versions >= 4 (and corresponding V8 versions). This change includes several minor fixes to avoid using node internal APIs and support older V8 versions: - Expand node::arraysize - In the CHECK_ENV() macro, return an error code instead of calling node::FatalError(). This is more consistent with how other invalid arguments to N-API functions are handled. - In v8impl::SetterCallbackWrapper::SetReturnValue(), do nothing instead of calling node::FatalError(). This is more consistent with JavaScript setter callbacks, where any returned value is silently ignored. - When queueing async work items, get the uv default loop instead of getting the loop from node::Environment::GetCurrent(). Currently that returns the same loop anyway. If/when node supports multiple environments, it should have a public API for getting the environment & event loop, and we can update this implementation then. - Use v8::Maybe::FromJust() instead of the newer alias ToChecked() These changes were copied directly from nodejs/node-addon-api@c7d4df4 where they were reviewed as part of nodejs/node-addon-api#25
Note there are two commits in this PR:
Copy the latest node_api* source files from the node master branch, to pick up all the changes from the past few weeks. All these changes were reviewed when they went into node-master, so you don't need to re-review.
Re-enable building as an external native module and back-compat with Node v4.
Recent changes introduced dependencies on internal node APIs:
Environment::GetCurrent()FatalError()arraysize()To enable building this code as an external module, the internal APIs must be avoided. Some of these changes might be controversial, since there was some debate about them before. But previously we didn't consider that it was better to avoid internal APIs. (While we could use different code for different versions of node, that would make maintenance more difficult.)
Also replace use of
v8::Maybe::ToChecked()(available only in Node >= 7) withFromJust(), which does exactly the same thing and works in Node v4.We should consider applying the second commit here back to the code in the node master branch.