-
Notifications
You must be signed in to change notification settings - Fork 4
Injecting Node-API functions into weak-node-api
#49
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
Conversation
|
a2539b0
to
63d865c
Compare
"#include <node_api.h>", // Node-API | ||
"#include <stdio.h>", // fprintf() | ||
"#include <stdlib.h>", // abort() | ||
"namespace node_api::internal {", |
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.
I wonder if this is too intrusive? Should keep this under callstack::
instead or is this okay? I mean, ideally this would be usable outside of our setup.
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.
Removed this as it served little purpose now that the inject function is top-level.
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.
Now that I'm no longer using a namespace, nor any C++ features, I'm thinking about making this an actual .c
file to make sure I'm not using any C++ features. IDYT @mani3xis?
63d865c
to
9e0f637
Compare
9e0f637
to
9f18d8c
Compare
9f18d8c
to
86cff81
Compare
a438cb8
to
55e73d0
Compare
55e73d0
to
2a04f9e
Compare
6b024e9
to
d04ce9e
Compare
629a063
to
74b48e1
Compare
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.
I found a major issue (relying on mangled name) and a few minors and nits. Overall looks good!
"aarch64-linux-android": "arm64-v8a", | ||
"armv7-linux-androideabi": "armeabi-v7a", | ||
"i686-linux-android": "x86", | ||
"x86_64-linux-android": "x86_64", |
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.
nit: What about making a type for the values to avoid typos? I'm sure that you listed all values already, so "arm64-v8a" | "armeabi-v7a" | "x86" | "x86_64"
should do the trick, wdyt?
nit 2: Maybe we should pick a more general/unified way of describing targets / cpu archs? I'm afraid that adding new platforms will case an explosion of enums.
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.
And use that type instead of string
in the Record
above? I don't see that I will be referencing that type elsewhere 🤔 Where would you use that type? Perhaps a suggestive edit would help me understand your intent.
packages/ferric/src/cargo.ts
Outdated
const weakNodeApiPath = getWeakNodeApiAndroidLibraryPath(target); | ||
|
||
return { | ||
RUSTFLAGS: `-L ${weakNodeApiPath} -l weak-node-api`, |
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.
Nit: Hopefully there are no spaces in the path (or at least the string gets quoted in such case)
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.
You're right - great catch. This should be quoted 👍
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.
Hmm - it failed when I quoted it (either with " or ') 🤔 It seems CARGO_ENCODED_RUSTFLAGS is what I'd have to use 😆
} | ||
}); | ||
assert.equal(result.length, 1, "Expected exactly library file"); | ||
const result = readdirSync(tripletOutputPath, { |
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.
Minor: Perhaps we can use asynchronous functions instead and use await Promise.all()
. This should help performance a bit.
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.
I turned all the sync calls (except existsSync
) into async I/O 👍
/weak-node-api/*.android.node | ||
/weak-node-api/weak-node-api.cpp | ||
/weak-node-api/weak_node_api.cpp | ||
/weak-node-api/weak_node_api.hpp |
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.
Nit: /weak-node-api/weak_node_api.[ch]pp
if you like one-liners
namespace callstack::nodeapihost { | ||
using node_api::internal::inject_host_t; |
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.
nit: The _t
suffix is reserved by POSIX but this is not a big deal breaker. I would recommend to follow the convention and use CamelCase for types (as used in React and rest of our code), like NodeApiHost
type below.
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.
Okay, thanks 👍 How about InjectHostFunction
?
// Generate function calling into the host | ||
...functions.flatMap(({ returnType, name, argumentTypes }) => { | ||
return [ | ||
`extern "C" ${returnType} ${name}(${argumentTypes |
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.
Missing calling convention specifier and there's one function that does not return (napi_fatal_error
IIRC) which also had noreturn
attribute.
Moreover, I would highly advise to keep the NAPI_VERSION
-specific #if
...#endif
macros in the generated source. We would be able to control which functions are "visible" and therefore generate errors when somebody tries to use function that we do not support.
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.
keep the
NAPI_VERSION
-specific #if...#endif macros in the generated source
We talked about this in person, right?
As I expect this code to be compiled into a single prebuilt and linked to from multiple Node-API modules (each with their own NAPI_VERSION
s to restrict their available API), I don't see a scenario where we'd want to provide a prebuilt with symbols less than NAPI_VERSION
10. To me the constant is entirely a "consumer side" concept. (And I might be wrong about that?)
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.
Missing calling convention specifier
What more than extern "C"
do we need in your opinion?
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.
I'll make sure to add the noreturn
👍
.join(", ")}) {`, | ||
`if (g_host.${name} == nullptr) {`, | ||
` fprintf(stderr, "Node-API function '${name}' called before it was injected!\\n");`, | ||
" abort();", |
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.
Maybe we should introduce a "backdoor" which would allow users to configure how to behave in such a case? For example, in my first draft which I've generated earlier this week I'm using the ASSERT()
macro for such checks (yet I'm looking for a better name). This can be a fallback to assert()
from assert.h
or whatever the user would like to have. Might be a great addition for testing/mocking.
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.
would allow users to configure how to behave in such a case
Again, I expect us to ship the prebuild for this and I therefore expect that to be outside of the control of users.
That being said, I'd be happy to extend this, once I see a clear need 👍
I mean, we would want something relying on that behavior if we add it in.
fallbackReturnStatement: string; | ||
}; | ||
|
||
export function getNodeApiFunctions(version: NodeApiVersion = "v8") { |
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.
Nit: I understand where this is coming from (the default version when not specified), although wouldn't it be better to start with v1
which we are not supporting yet?
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.
I am contemplating moving this to a top-level constant instead. As mentioned above, I don't understand the need for weak-node-api to be built with less than the highest number available - I might actually want to bump it to v10 🙈
Would you rather that prebuilds to fail compiling instead of us aborting at runtime? (Less and less as we increase our support).
${CMAKE_CURRENT_SOURCE_DIR}/include | ||
) | ||
target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_17) | ||
target_compile_definitions(${PROJECT_NAME} PRIVATE NAPI_VERSION=8) |
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.
Nit: As mentioned earlier, shouldn't we start with NAPI_VERSION=1
? Moreover hardcoding the version here might not be a good idea, as it would be hard to generate prebuilds for various versions (like with node-pre-gyp and friends).
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.
Same as above
`extern "C" ${returnType} ${name}(${argumentTypes | ||
.map((type, index) => `${type} arg${index}`) | ||
.join(", ")}) {`, | ||
`if (g_host.${name} == nullptr) {`, |
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.
Comment (just to leave the trace from our yesterday's call): This will work for (the deprecated) napi_register_module()
only when weak-node-api
is built as shared-library and only if we guarantee that host injected the functions before the add-on is loaded! In earlier modules, there was a macro which generated a function that would call this napi_register_module()
, and it was decorated with __attribute__((constructor))
, meaning that a call to napi_register_module()
happened BEFORE dlopen()
returned, leaving no time to inject functions (when statically linked).
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.
Didn't we talk about this and realized this would only be an issue on a platform where all dynamic libraries are preloaded on app startup (iOS)? I mean, we'd expect to be in control of when the dlopen
of the Node-API module is happening and we can make sure the functions are injected beforehand.
…nd move it out of namespace
74b48e1
to
9578c63
Compare
Merging this PR will:
weak-node-api
to have the Node-API functions injected from the host, instead of resolving them itself.weak-node-api
into the process without polluting the global symbol registry, when the host injects the Node-API functions fromlibhermes
and the host (which is future work).weak-node-api
as a pre-built, like the Node-API modules are already doing.log_debug
function (which we'll likely want to mute in the future).Here's at generated files: https://gist.github.com/kraenhansen/049fac2b03daacab1ebc41ca425920ca