-
-
Notifications
You must be signed in to change notification settings - Fork 2.6k
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
Zig and Node.js N-API example #3000
Comments
It seems like n-api requires that modules register themselves from constructors that run when their dll is opened. If you don't then node's
This issue will require a way to register a dynamic library constructor. The code I was playing with: // https://nodejs.org/api/n-api.html
const std = @import("std");
const napi = @cImport({
@cInclude("node_api.h");
});
fn NAPI_MODULE_X(modname: []const u8, regfunc: napi.napi_addon_register_func, priv: ?*c_void, flags: c_uint) void {
napi_module_register(&napi_module {
.nm_version = napi.NAPI_MODULE_VERSION,
.nm_flags = flags,
.nm_filename = null,
.nm_register_func = regfunc,
.nm_modname = modname,
.nm_priv = priv,
.reserved = c_void(null) ** 4,
});
}
fn NAPI_MODULE(modname: []const u8, regfunc: napi.napi_addon_register_func) void {
return NAPI_MODULE_X(modname, regfunc, null, 0);
}
extern fn myfunc(env: napi.napi_env, exports: napi.napi_value) napi.napi_value {
var answer: napi.napi_value = undefined;
var status: napi.napi_status = undefined;
status = napi.napi_create_int64(env, 42, &answer);
if (status != .napi_ok) return null;
status = napi.napi_set_named_property(env, exports, &"answer", answer);
if (status != .napi_ok) return null;
return exports;
}
comptime {
@export("napi_register_module_v1", myfunc, .Strong);
}
|
I have managed to do exactly that thanks to the help of the nice zig people in freenode by doing that on linux: export const init_array linksection(".init_array") = [_]extern fn () void {
myFunc
}; |
@andrewrk this issue is a good example use case for why we need the ability to support constructors. The workaround found is linux and elf specific. |
Since you need to link C code anyways for this use case, I think calling your constructor from C is a perfectly fine solution and avoids adding any language features to support this use case. The C file can be as simple as #include <node_api.h>
napi_value init(napi_env env, napi_value exports);
NAPI_MODULE(NODE_GYP_MODULE_NAME, init) Example repo here: /~https://github.com/ifreund/zig-napi-example |
It turns out I wasn't actually satisfied with that solution. I'm not sure if this has changed since @daurnimator's initial investigation, but node no longer requires the constructor to be run if the module exports a function with the proper name: For example: const c = @cImport({
@cInclude("node_api.h");
});
export fn napi_register_module_v1(env: c.napi_env, exports: c.napi_value) c.napi_value {
var function: c.napi_value = undefined;
if (c.napi_create_function(env, null, 0, foo, null, &function) != .napi_ok) {
_ = c.napi_throw_error(env, null, "Failed to create function");
return null;
}
if (c.napi_set_named_property(env, exports, "foo", function) != .napi_ok) {
_ = c.napi_throw_error(env, null, "Failed to add function to exports");
return null;
}
return exports;
}
fn foo(env: c.napi_env, info: c.napi_callback_info) callconv(.C) c.napi_value {
var result: c.napi_value = undefined;
if (c.napi_create_int32(env, 42, &result) != .napi_ok) {
_ = c.napi_throw_error(env, null, "Failed to create return value");
return null;
}
return result;
} const example = require('./example.node');
console.log(example.foo()); build with
|
Looks like it might have, e.g. nodejs/node#26175 |
Hi @ifreund - thanks for the example! I ran into some problems trying to get it to run zig 0.9.0-dev.1583+a7d215759:
I then ended up with the following code: const c = @cImport({
@cInclude("node_api.h");
});
export fn napi_register_module_v1(env: c.napi_env, exports: c.napi_value) c.napi_value {
var function: c.napi_value = undefined;
if (c.napi_create_function(env, null, 0, foo, null, &function) != 0) {
_ = c.napi_throw_error(env, null, "Failed to create function");
return null;
}
if (c.napi_set_named_property(env, exports, "foo", function) != 0) {
_ = c.napi_throw_error(env, null, "Failed to add function to exports");
return null;
}
return exports;
}
fn foo(env: c.napi_env, _: c.napi_callback_info) callconv(.C) c.napi_value {
var result: c.napi_value = undefined;
if (c.napi_create_int32(env, 42, &result) != 0) {
_ = c.napi_throw_error(env, null, "Failed to create return value");
return null;
}
return result;
} I tried running this on Debian Linux and it seems to work as expected (nice!):
However, I was not successful in getting this to run on macOS 12.0.1:
/~https://github.com/kristoff-it/zig-cuckoofilter/blob/master/c-abi-examples/node_example.js#L13-L17 seems to imply macOS might need a different approach, but I tried doing a |
This is the same error you'd traditionally get for C if you forgot to specify |
Thanks! I tried the same command with |
We now respect both `-fallow-shlib-undefined` and `-Wl,"-undefined=dynamic_lookup"` flags. This is the first step towards solving issues #8180 and #3000. We currently do not expose any other ld64 equivalent flag for `-undefined` flag - we basically throw an error should the user specify a different flag. Support for those is conditional on closing #8180. As a result of this change, it is now possible to generate a valid native Node.js addon with Zig for macOS.
We now respect both `-fallow-shlib-undefined` and `-Wl,"-undefined=dynamic_lookup"` flags. This is the first step towards solving issues #8180 and #3000. We currently do not expose any other ld64 equivalent flag for `-undefined` flag - we basically throw an error should the user specify a different flag. Support for those is conditional on closing #8180. As a result of this change, it is now possible to generate a valid native Node.js addon with Zig for macOS.
BTW, this repo implements a module with self-registration: /~https://github.com/staltz/zig-nodejs-example/blob/main/src/lib.zig#L6-L9 |
Hey, it seems its not fixed on Windows, trying to compile with
|
Me too, is there something we're missing? |
I think maybe you're missing the import library which is required on windows, see for example /~https://github.com/scheibo/zigpkg/blob/main/build.zig#L57-L61. This can usually be found relative on the headers /~https://github.com/scheibo/zigpkg/blob/main/src/bin/install-zigpkg#L240-L268 |
Oh! That's right! I didn't download Thank you. On a side note: I found that the link can be obtained directly from |
Given Zig's promise of C header includes, it would be cool for the docs or blog to have an example of writing a "Hello Node.js" native addon for Node.js using N-API and Zig.
Node's N-API is fairly recent, and many existing native addons for Node written using Nan etc. are probably being ported. Now might be a great time for some of these native Node modules to consider using Zig instead of C/C++.
The text was updated successfully, but these errors were encountered: