started planning the pluggable modules
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Sun, 5 Dec 2021 05:23:49 +0000 (23:23 -0600)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Sun, 5 Dec 2021 05:23:49 +0000 (23:23 -0600)
bin/test [deleted file]
bin/test.bat [deleted file]
build.sh
docs/pluggable_modules.md [new file with mode: 0644]
misc/onyx_module.h [new file with mode: 0644]
modules/test_library/module.onyx [new file with mode: 0644]
modules/test_library/test_library.c [new file with mode: 0644]
src/wasm_runtime.c
test_library.so [new file with mode: 0755]

diff --git a/bin/test b/bin/test
deleted file mode 100755 (executable)
index e9ca232..0000000
--- a/bin/test
+++ /dev/null
@@ -1,68 +0,0 @@
-#!/bin/sh
-
-print_check() {
-    if [ ! -z "$TERM" ]; then
-        printf "%-$((($(tput cols) - 8)))s" "⏲ Checking $1.onyx" ;
-    else
-        printf "%s ... " "⏲ Checking $1.onyx" ;
-    fi
-}
-
-if command -v wasmer >/dev/null; then
-    printf "Using $(wasmer --version)\n"
-else
-    printf "Using node $(node -v)\n"
-fi
-
-failed=0
-for test_file in $(find tests/ -name '*.onyx'); do
-    filename=$(basename -- "$test_file")
-    dirname="$(dirname -- "$test_file")"
-    name="${filename%.*}"
-
-    print_check "$name"
-
-    if true; then
-        if ! ./bin/onyx run "$test_file" > ./tmpoutput; then
-            printf "\n❌ Failed to compile $name.onyx.\n"
-            failed=1
-            continue
-        fi
-        
-        # if ! wasmer "./tests/$name.wasm" > ./tmpoutput; then
-        #     printf "\n❌ Failed to run $name.onyx.\n"
-        #     failed=1
-        #     continue
-        # fi
-    else
-        if ! ./bin/onyx -r js --use-post-mvp-features "$test_file" -o "./tests/$name.wasm" >/dev/null; then
-            printf "\n❌ Failed to compile $name.onyx.\n"
-            failed=1
-            continue
-        fi
-        
-        if ! ./bin/onyx-js "./tests/$name.wasm" > ./tmpoutput; then
-            printf "\n❌ Failed to run $name.onyx.\n"
-            failed=1
-            continue
-        fi
-    fi
-
-    if ! diff ./tmpoutput "$dirname/$name" >/dev/null; then
-        printf "\n❌ Test output did not match.\n"
-        diff ./tmpoutput "$dirname/$name"
-        # failed=0
-        continue
-    fi
-
-    echo "✔ Passed"
-
-    rm "./tests/$name.wasm" 2>/dev/null
-done
-rm ./tmpoutput
-
-([ $failed = 0 ] && echo "✔ All tests passed.") \
-                  || echo "❌ Some tests failed."
-
-exit $failed
-
diff --git a/bin/test.bat b/bin/test.bat
deleted file mode 100644 (file)
index 4dbd39f..0000000
+++ /dev/null
@@ -1,34 +0,0 @@
-@echo off
-
-set ONYX_DIR=\dev\onyx
-
-set failed=0
-for /r tests %%v in (*.onyx) do (
-    echo Checking %%~pv%%~nv.onyx
-
-    set ERRORLEVEL=0
-    %ONYX_DIR%\onyx.exe run "%%v" > %ONYX_DIR%\tmpoutput
-    if %ERRORLEVEL% GEQ 1 (
-        echo Failed to compile %%~nv.onyx.
-        set failed=1
-        goto continue
-    )
-
-    set ERRORLEVEL=0
-    fc %ONYX_DIR%\tmpoutput "%%~pv%%~nv" > nul
-    if %ERRORLEVEL% GEQ 1 (
-        echo Output did not match for %%~nv.onyx.
-        set failed=1
-    )
-
-::continue::
-    del %ONYX_DIR%\tmpoutput
-)
-
-del %ONYX_DIR%\tmpoutput
-
-if %failed% NEQ 0 (
-    echo Some of the test cases failed.
-) else (
-    echo All test cases passed.
-)
\ No newline at end of file
index 90e7e8a0fb3218bfc1100368147b28b13742e574..88388a8e533e6b06aa8febf5e1fbf0542bd60b30 100755 (executable)
--- a/build.sh
+++ b/build.sh
@@ -45,7 +45,7 @@ fi
 
 C_FILES="$C_FILES wasm_runtime"
 FLAGS="$FLAGS -DENABLE_RUN_WITH_WASMER"
-LIBS="-L$CORE_DIR/lib -lwasmer -Wl,-rpath=$CORE_DIR/lib -lpthread"
+LIBS="-L$CORE_DIR/lib -lwasmer -Wl,-rpath=$CORE_DIR/lib -lpthread -ldl"
 INCLUDES="-I$WASMER_INCLUDE_DIR"
 
 mkdir -p "$BUILD_DIR"
diff --git a/docs/pluggable_modules.md b/docs/pluggable_modules.md
new file mode 100644 (file)
index 0000000..3151fda
--- /dev/null
@@ -0,0 +1,71 @@
+Pluggable Modules
+-----------------
+
+
+After using the 'onyx run' functionality for a while, I've come to the realization
+that maybe pushing that feature farther may be the future of Onyx. Onyx can still
+target WebAssembly and be used for performance based web-applications. But using
+libwasmer and providing a way to do "pluggable modules" gives me the ability to use
+Onyx more natively. I'm basing this functionality on Lua's approach to C interop,
+which does seem a little cumbersome, but also is easy enough to understand and
+program for.
+
+
+The canonical name for this with be a "library", where "module" is reserved for a
+collection of "packages". A library is included in Onyx using the following syntax:
+
+    // Includes "some_useful_code.so / .dll" in this execution.
+    // Currently, this will only be allowed with the "onyx" runtime.
+    #library "some_useful_code"
+
+    // #library_path can be used to add to the places that libraries are
+    // searched for.
+    #if runtime.OS == runtime.OS_Linux {
+        #library_path "./libs/linux"
+    } else {
+        #library_path ".\libs\windows"
+    }
+
+Libraries will be shared objects files or dlls, depending on the OS. Hopefully, that
+is the only change that has to be added to the Onyx language itself. Everything else
+will be handled behind the scenes from the programmer.
+
+The "behinds the scenes" stuff is as follows:
+    - Locate and load the library using dlopen or LoadLibrary.
+    - Lookup the predefined function that will return a pointer to the table describing
+        the provided functions.
+    - During the module linking phase (aka building the import table), these functions
+        will be considered as options.
+
+
+
+
+A typical Onyx file for a library will look like:
+
+    package some_useful_code
+
+    #library "some_useful_code"
+
+    the_code :: (a, b: i32) -> i32 #foreign "some_useful_code" "the_code" ---
+
+
+The corresponding C file that will be compiled to a so/dll looks like:
+
+    #include "onyx_module.h"
+
+    #define ONYX_LIBRARY_NAME some_useful_code
+
+    ONYX_DEF(the_code, (I32, I32), (I32)) {
+        i32 a = params->data[0].of.i32;
+        i32 b = params->data[1].of.i32;
+        results->data[0] = WASM_I32_VAL(a + b);
+        return NULL;
+    }
+
+    ONYX_LIBRARY {
+        ONYX_FUNC(the_code)
+    }
+
+Compiling the C file with:
+
+    gcc -o some_useful_code.so -shared -fPIC some_useful_code.c -I ...
\ No newline at end of file
diff --git a/misc/onyx_module.h b/misc/onyx_module.h
new file mode 100644 (file)
index 0000000..3ad2e35
--- /dev/null
@@ -0,0 +1,47 @@
+
+#include "wasm.h"
+
+#define NUM_VALS(...) (sizeof((wasm_valkind_t []){ 0, __VA_ARGS__ }) / sizeof(wasm_valkind_t))
+#define _VALS(...) { NUM_VALS(__VA_ARGS__) - 1, __VA_ARGS__ }
+
+typedef struct WasmValkindBuffer {
+    unsigned int count;
+    wasm_valkind_t types[20];
+} WasmValkindBuffer;
+
+typedef struct WasmFuncDefinition {
+    char* module_name;
+    char* import_name;
+    wasm_func_callback_t func;
+
+    WasmValkindBuffer *params;
+    WasmValkindBuffer *results;
+} WasmFuncDefinition;
+
+#define STRINGIFY1(a) #a
+#define CONCAT2(a, b) a ## _ ## b
+#define CONCAT3(a, b, c) a ## _ ## b ## _ ## c
+#define ONYX_MODULE_NAME_GEN(m) CONCAT2(__onyx_module, m)
+#define ONYX_FUNC_NAME(m, n) CONCAT3(__onyx_internal, m, n)
+#define ONYX_DEF_NAME(m, n) CONCAT3(__onyx_internal_def, m, n)
+#define ONYX_PARAM_NAME(m, n) CONCAT3(__onyx_internal_param_buffer, m, n)
+#define ONYX_RESULT_NAME(m, n) CONCAT3(__onyx_internal_result_buffer, m, n)
+#define ONYX_IMPORT_NAME(m, n) STRINGIFY1(m) "_" #n
+
+#define ONYX_DEF(name, params_types, result_types) \
+    static wasm_trap_t* ONYX_FUNC_NAME(ONYX_MODULE_NAME, name)(const wasm_val_vec_t* params, wasm_val_vec_t* results); \
+    static struct WasmValkindBuffer  ONYX_PARAM_NAME(ONYX_MODULE_NAME, name) = _VALS params_types; \
+    static struct WasmValkindBuffer  ONYX_RESULT_NAME(ONYX_MODULE_NAME, name) = _VALS result_types; \
+    static struct WasmFuncDefinition ONYX_DEF_NAME(ONYX_MODULE_NAME, name) = { STRINGIFY1(ONYX_MODULE_NAME), #name, ONYX_FUNC_NAME(ONYX_MODULE_NAME, name), & ONYX_PARAM_NAME(ONYX_MODULE_NAME, name), & ONYX_RESULT_NAME(ONYX_MODULE_NAME, name) }; \
+    \
+    static wasm_trap_t* ONYX_FUNC_NAME(ONYX_MODULE_NAME, name)(const wasm_val_vec_t* params, wasm_val_vec_t* results)
+
+#define ONYX_FUNC(name) & ONYX_DEF_NAME(ONYX_MODULE_NAME, name),
+#define ONYX_MODULE struct WasmFuncDefinition *ONYX_MODULE_NAME_GEN(ONYX_MODULE_NAME) [] =
+
+// Shorter names
+#define I32 WASM_I32
+#define I64 WASM_I64
+#define F32 WASM_F32
+#define F64 WASM_F64
+
diff --git a/modules/test_library/module.onyx b/modules/test_library/module.onyx
new file mode 100644 (file)
index 0000000..7b505cb
--- /dev/null
@@ -0,0 +1,5 @@
+package test_library
+
+#library "test_library"
+
+foo :: () -> void #foreign "test_library" "foo" ---
\ No newline at end of file
diff --git a/modules/test_library/test_library.c b/modules/test_library/test_library.c
new file mode 100644 (file)
index 0000000..0a8fd25
--- /dev/null
@@ -0,0 +1,13 @@
+#include "onyx_module.h"
+#include <stdio.h>
+
+#define ONYX_MODULE_NAME test_library
+
+ONYX_DEF(foo, (), ()) {
+    printf("This worked!\n");
+    return NULL;
+}
+
+ONYX_MODULE {
+    ONYX_FUNC(foo)
+};
\ No newline at end of file
index 6a5d3dfb52e82a893f26a9b63d68d11368a894fc..ef74408c063492c074ec1b638c8ed1aac1084af5 100644 (file)
@@ -498,8 +498,22 @@ WASM_INTEROP(onyx_process_destroy_impl) {
     return NULL;
 }
 
+#include <dlfcn.h>
+
 // Returns 1 if successful
 b32 onyx_run_wasm(bh_buffer wasm_bytes) {
+
+    // NOCHECKIN
+    void* handle = dlopen("./test_library.so", RTLD_LAZY);
+    printf("HANDLE: %p\n", handle);
+    if (handle == NULL) {
+        printf("ERROR: %s\n", dlerror());
+    }
+    void *wasm_library = dlsym(handle, "__onyx_module_test_library");
+    printf("LOADED: %p %s\n", wasm_library, wasm_library);
+    dlclose(handle);
+
+
     wasm_instance_t* instance = NULL;
     wasmer_features_t* features = NULL;
     wasm_trap_t* run_trap = NULL;
diff --git a/test_library.so b/test_library.so
new file mode 100755 (executable)
index 0000000..cba999b
Binary files /dev/null and b/test_library.so differ