I'm trying to build a Node.js module in Swift, using their C-compatible N-API. I've gotten the compiling and linking working with a C file and a Swift file, and the next step is now to move everything from C into Swift, one piece at a time.
Full conversion process
This is the C file I'm starting out with:
#include <node_api.h>
napi_value Method(napi_env env, napi_callback_info info) {
napi_status status;
napi_value world;
status = napi_create_string_utf8(env, "world", 5, &world);
return world;
}
napi_value Init(napi_env env, napi_value exports) {
napi_status status;
napi_property_descriptor desc = { "hello", 0, Method, 0, 0, 0, napi_default, 0 };
status = napi_define_properties(env, exports, 1, &desc);
return exports;
}
static napi_module _module = { 1, 0, __FILE__, Init, "hello", NULL, NULL, NULL, NULL };
static void _register_hello(void) __attribute__((constructor));
static void _register_hello(void) {
napi_module_register(&_module);
}
Trying this out will return the string "world"
:
$ node -p "require('./build/Release/hello.node').hello()"
world
I started by creating a bridging header in order to bring in node_api.h
into Swift:
#define NODE_GYP_MODULE_NAME hello
#define USING_UV_SHARED 1
#define USING_V8_SHARED 1
#define V8_DEPRECATION_WARNINGS 1
#define _DARWIN_USE_64_BIT_INODE 1
#define _LARGEFILE_SOURCE
#define _FILE_OFFSET_BITS 64
#define BUILDING_NODE_EXTENSIO
#include "node_api.h"
The next step was to port the first function, Method
, from C to Swift:
+napi_value Method(napi_env env, napi_callback_info info);
-napi_value Method(napi_env env, napi_callback_info info) {
- napi_status status;
- napi_value world;
- status = napi_create_string_utf8(env, "world", 5, &world);
- return world;
-}
+@_cdecl("Method")
+func method(_ env: napi_env?, _ info: napi_callback_info?) -> napi_value? {
+ var status: napi_status?
+ var world: napi_value?
+ status = napi_create_string_utf8(env, "Swift", 5, &world)
+ return world
+}
I also replaced the string "world"
with "Swift"
so that we can see that it worked:
$ node -p "require('./build/Release/hello.node').hello()"
Swift
Next, we'll move over the Init
function:
+napi_value Init(napi_env env, napi_value exports);
-napi_value Init(napi_env env, napi_value exports) {
- napi_status status;
- napi_property_descriptor desc = { "hello", 0, Method, 0, 0, 0, napi_default, 0 };
- status = napi_define_properties(env, exports, 1, &desc);
- return exports;
-}
+@_cdecl("Init")
+func initModule(_ env: napi_env?, _ exports: napi_value?) -> napi_value? {
+ var status: napi_status?
+ var desc = napi_property_descriptor(utf8name: "hello", name: nil, method: method, getter: nil, +setter: nil, value: nil, attributes: napi_default, data: nil)
+ status = napi_define_properties(env, exports, 1, &desc)
+ return exports
+}
So far so good, now we just need to port the small shared library constructor, that registers the Node.js module to the process that loaded us.
Everything is working, except for the small part that registers the Node.js module with the process that loaded us. It looks like this in C:
static napi_module _module = { 1, 0, __FILE__, Init, "hello", NULL, { NULL, NULL, NULL, NULL } };
static void _register_hello(void) __attribute__((constructor));
static void _register_hello(void) {
napi_module_register(&_module);
}
and this is as far as I've been able to take it in Swift:
fileprivate var _module = napi_module(nm_version: NAPI_MODULE_VERSION, nm_flags: 0, nm_filename: #file, nm_register_func: initModule, nm_modname: "hello", nm_priv: nil, reserved: (nil, nil, nil, nil))
@_cdecl("_register_hello")
func _register_hello() -> Void {
napi_module_register(&_module)
}
While this compiles, it doesn't call napi_module_register
when it's loaded, since there is no code being run when I load it. I cannot figure out how to mark the _register_hello
function as being the shared library constructor
Any help would be much appreciated!
(for anyone interested in writing Node.js addons in Swift, I'll post a full writeup and repo once I get everything working properly!)
tl;dr is there an equivalent to __attribute__ ((constructor))
in Swift?