+++ /dev/null
-#!/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
-
+++ /dev/null
-@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
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"
--- /dev/null
+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
--- /dev/null
+
+#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
+
--- /dev/null
+package test_library
+
+#library "test_library"
+
+foo :: () -> void #foreign "test_library" "foo" ---
\ No newline at end of file
--- /dev/null
+#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
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;