Skip to content

Commit

Permalink
feat: hack in support for wasm using Node.js
Browse files Browse the repository at this point in the history
- hack in Node.js to run wasm with Node.js
- only tested on hello world so far

Signed-off-by: Michael Dawson <mdawson@devrus.com>
  • Loading branch information
mhdawson committed Sep 29, 2022
1 parent 5159128 commit 23f346e
Show file tree
Hide file tree
Showing 12 changed files with 1,459 additions and 0 deletions.
1 change: 1 addition & 0 deletions Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ libcrun_SOURCES = src/libcrun/utils.c \
src/libcrun/handlers/wasmedge.c \
src/libcrun/handlers/mono.c \
src/libcrun/handlers/wasmtime.c \
src/libcrun/handlers/wasm_nodejs.c \
src/libcrun/handlers/wasmer.c

if HAVE_EMBEDDED_YAJL
Expand Down
82 changes: 82 additions & 0 deletions NODE-WASM_EXPERIMENT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# NODE-WASM-EXPERIMENT

Experiment of integration Node.js to run wasm containers by
hacking wastime integration

# Node version

The experiment uses the code from the
[Extend Node-API to libnode](https://github.com/nodejs/node/pull/43542)
PR. This was an easy way to get a set of C functions that would be integrated
with crun that is C versus C++. That PR is likely a long way (if ever from
landing) but reduced the work that it would have taken to build C wrapper
using the Node.js C++ embedder API or converting the crun project over
to C++.

Node needs to be built with

```
./configure --shared
``
And the the resulting `../out/Release/libnode.so.108` copied to
libnode.so in the node-lib directory added to this branch.
The following files also need to be copied from `../src` directory
in the Node.js project to the node-include directory added to this branch
* js_native_api.h
* js_native_api_types.h
* node_api.h
* node_api_types.h
**NOTE:** I've included these as part of the PR along with the shared library
so that everybody does not need to build the node shared library
and extract the associated headers. May or may not work on the
machine you run on. I built/ran on Fedora 36.
# Building crun
Before building running you will need to so the following:
* copy the headers node-include/* into /usr/include
* copy node-lib/libnode.so to /lib64
The file `buildit.sh` shows the steps used to build with
node.js integrated.
# Building wasm container
The wasm to be run must be built an pushed to the local repository
and be tagged as a wasm container. See
[wasmtime-tutorial](https://github.com/font/wasmtime-tutorial.git)
for more details.
My specific stesp to build were as follows:
```shell
cargo build --target wasm32-wasi --release
chomd +x ./target/wasm32-wasi/release/hello_wasm.wasm
buildah build --annotation "module.wasm.image/variant=compat" -t wasm-test -f ./Containerfile
```

with the resulting container being:

```shell
REPOSITORY TAG IMAGE ID CREATED SIZE
localhost/wasm-test latest 8c9b454570f0 20 hours ago 2.01 MB
```

# Running

The file `runit.sh` has the steps I used to run. It hard codes
the path to the updated crun to
```
/root/wasmtime-tutorial/crun/crun`
```

which will need to be updated for your path if you use the script





2 changes: 2 additions & 0 deletions buildit.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
./configure --with-wasm_nodejs
make
4 changes: 4 additions & 0 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,10 @@ dnl include support for wasmtime (EXPERIMENTAL)
AC_ARG_WITH([wasmtime], AS_HELP_STRING([--with-wasmtime], [build with wasmtime support]))
AS_IF([test "x$with_wasmtime" = "xyes"], AC_CHECK_HEADERS([wasmtime.h], AC_DEFINE([HAVE_WASMTIME], 1, [Define if wasmtime is available]), [AC_MSG_ERROR([*** Missing wasmtime headers])]))

dnl include support for wasm using nodejs (EXPERIMENTAL)
AC_ARG_WITH([wasm_nodejs], AS_HELP_STRING([--with-wasm_nodejs], [build with wasm support using nodejs support]))
AS_IF([test "x$with_wasm_nodejs" = "xyes"], AC_CHECK_HEADERS([js_native_api.h js_native_api_types.h node_api.h node_api_types.h], AC_DEFINE([HAVE_WASM_NODEJS], 1, [Define if wasm_nodejs is available]), [AC_MSG_ERROR([*** Missing nodejs headers])]))

dnl include support for wasmedge (EXPERIMENTAL)
AC_ARG_WITH([wasmedge], AS_HELP_STRING([--with-wasmedge], [build with WasmEdge support]))
AS_IF([test "x$with_wasmedge" = "xyes"], AC_CHECK_HEADERS([wasmedge/wasmedge.h], AC_DEFINE([HAVE_WASMEDGE], 1, [Define if WasmEdge is available]), [AC_MSG_ERROR([*** Missing wasmedge headers])]))
Expand Down
599 changes: 599 additions & 0 deletions node-include/js_native_api.h

Large diffs are not rendered by default.

167 changes: 167 additions & 0 deletions node-include/js_native_api_types.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
#ifndef SRC_JS_NATIVE_API_TYPES_H_
#define SRC_JS_NATIVE_API_TYPES_H_

// This file needs to be compatible with C compilers.
// This is a public include file, and these includes have essentially
// became part of it's API.
#include <stddef.h> // NOLINT(modernize-deprecated-headers)
#include <stdint.h> // NOLINT(modernize-deprecated-headers)

#if !defined __cplusplus || (defined(_MSC_VER) && _MSC_VER < 1900)
typedef uint16_t char16_t;
#endif

#ifndef NAPI_CDECL
#ifdef _WIN32
#define NAPI_CDECL __cdecl
#else
#define NAPI_CDECL
#endif
#endif

// JSVM API types are all opaque pointers for ABI stability
// typedef undefined structs instead of void* for compile time type safety
typedef struct napi_env__* napi_env;
typedef struct napi_value__* napi_value;
typedef struct napi_ref__* napi_ref;
typedef struct napi_handle_scope__* napi_handle_scope;
typedef struct napi_escapable_handle_scope__* napi_escapable_handle_scope;
typedef struct napi_callback_info__* napi_callback_info;
typedef struct napi_deferred__* napi_deferred;
typedef struct napi_platform__* napi_platform;

typedef enum {
napi_default = 0,
napi_writable = 1 << 0,
napi_enumerable = 1 << 1,
napi_configurable = 1 << 2,

// Used with napi_define_class to distinguish static properties
// from instance properties. Ignored by napi_define_properties.
napi_static = 1 << 10,

#if NAPI_VERSION >= 8
// Default for class methods.
napi_default_method = napi_writable | napi_configurable,

// Default for object properties, like in JS obj[prop].
napi_default_jsproperty = napi_writable | napi_enumerable | napi_configurable,
#endif // NAPI_VERSION >= 8
} napi_property_attributes;

typedef enum {
// ES6 types (corresponds to typeof)
napi_undefined,
napi_null,
napi_boolean,
napi_number,
napi_string,
napi_symbol,
napi_object,
napi_function,
napi_external,
napi_bigint,
} napi_valuetype;

typedef enum {
napi_int8_array,
napi_uint8_array,
napi_uint8_clamped_array,
napi_int16_array,
napi_uint16_array,
napi_int32_array,
napi_uint32_array,
napi_float32_array,
napi_float64_array,
napi_bigint64_array,
napi_biguint64_array,
} napi_typedarray_type;

typedef enum {
napi_ok,
napi_invalid_arg,
napi_object_expected,
napi_string_expected,
napi_name_expected,
napi_function_expected,
napi_number_expected,
napi_boolean_expected,
napi_array_expected,
napi_generic_failure,
napi_pending_exception,
napi_cancelled,
napi_escape_called_twice,
napi_handle_scope_mismatch,
napi_callback_scope_mismatch,
napi_queue_full,
napi_closing,
napi_bigint_expected,
napi_date_expected,
napi_arraybuffer_expected,
napi_detachable_arraybuffer_expected,
napi_would_deadlock // unused
} napi_status;
// Note: when adding a new enum value to `napi_status`, please also update
// * `const int last_status` in the definition of `napi_get_last_error_info()'
// in file js_native_api_v8.cc.
// * `const char* error_messages[]` in file js_native_api_v8.cc with a brief
// message explaining the error.
// * the definition of `napi_status` in doc/api/n-api.md to reflect the newly
// added value(s).

typedef napi_value(NAPI_CDECL* napi_callback)(napi_env env,
napi_callback_info info);
typedef void(NAPI_CDECL* napi_finalize)(napi_env env,
void* finalize_data,
void* finalize_hint);

typedef struct {
// One of utf8name or name should be NULL.
const char* utf8name;
napi_value name;

napi_callback method;
napi_callback getter;
napi_callback setter;
napi_value value;

napi_property_attributes attributes;
void* data;
} napi_property_descriptor;

typedef struct {
const char* error_message;
void* engine_reserved;
uint32_t engine_error_code;
napi_status error_code;
} napi_extended_error_info;

#if NAPI_VERSION >= 6
typedef enum {
napi_key_include_prototypes,
napi_key_own_only
} napi_key_collection_mode;

typedef enum {
napi_key_all_properties = 0,
napi_key_writable = 1,
napi_key_enumerable = 1 << 1,
napi_key_configurable = 1 << 2,
napi_key_skip_strings = 1 << 3,
napi_key_skip_symbols = 1 << 4
} napi_key_filter;

typedef enum {
napi_key_keep_numbers,
napi_key_numbers_to_strings
} napi_key_conversion;
#endif // NAPI_VERSION >= 6

#if NAPI_VERSION >= 8
typedef struct {
uint64_t lower;
uint64_t upper;
} napi_type_tag;
#endif // NAPI_VERSION >= 8

#endif // SRC_JS_NATIVE_API_TYPES_H_

0 comments on commit 23f346e

Please sign in to comment.