diff --git a/Makefile.wasi b/Makefile.wasi new file mode 100644 index 0000000..10784f8 --- /dev/null +++ b/Makefile.wasi @@ -0,0 +1,46 @@ +WASI_SDK_PATH ?= /home/jules/wasi-sdk +CC = $(WASI_SDK_PATH)/bin/clang +NM = $(WASI_SDK_PATH)/bin/llvm-nm +CFLAGS = -m32 -Oz -D_WASI_EMULATED_SIGNAL -I. +WIT_BINDGEN = wit-bindgen +WASM_TOOLS = wasm-tools +ADAPTER = wasi_snapshot_preview1.reactor.wasm + +OBJS = mquickjs.o cutils.o dtoa.o libm.o microquickjs.o glue.o glue_stdlib.o + +all: microquickjs.component.wasm + +mquickjs_build_native: mqjs_stdlib.c mquickjs_build.c + cc -O2 -I. mqjs_stdlib.c mquickjs_build.c -o mquickjs_build_native + +mqjs_stdlib.h: mquickjs_build_native + ./mquickjs_build_native -m32 > mqjs_stdlib.h + +mquickjs_atom.h: mquickjs_build_native + ./mquickjs_build_native -m32 -a > mquickjs_atom.h + +microquickjs.c microquickjs.h microquickjs_component_type.o: microquickjs.wit + $(WIT_BINDGEN) c ./microquickjs.wit --world microquickjs + +%.o: %.c microquickjs.h mqjs_stdlib.h mquickjs_atom.h + $(CC) $(CFLAGS) -c -o $@ $< + +core.wasm: $(OBJS) microquickjs_component_type.o + $(CC) $(CFLAGS) $(OBJS) microquickjs_component_type.o \ + -Wl,--no-entry \ + -Wl,--export=__wasm_export_microquickjs_eval \ + -Wl,--export=cabi_realloc \ + -Wl,--export=__wasm_call_ctors \ + -lwasi-emulated-signal \ + -o core.wasm + +embedded.wasm: core.wasm microquickjs.wit + $(WASM_TOOLS) component embed ./microquickjs.wit --world microquickjs core.wasm --output embedded.wasm + +microquickjs.component.wasm: embedded.wasm $(ADAPTER) + $(WASM_TOOLS) component new embedded.wasm \ + --adapt wasi_snapshot_preview1=$(ADAPTER) \ + --output microquickjs.component.wasm + +clean: + rm -f $(OBJS) core.wasm embedded.wasm microquickjs.component.wasm mquickjs_build_native mqjs_stdlib.h mquickjs_atom.h microquickjs.c microquickjs.h microquickjs_component_type.o diff --git a/README.WASI.md b/README.WASI.md new file mode 100644 index 0000000..259fd2f --- /dev/null +++ b/README.WASI.md @@ -0,0 +1,43 @@ +# MicroQuickJS WASI 0.2 Component + +This project has been ported to a WebAssembly Component Model component. + +## WIT Interface + +The component exports an `eval` function: + +```wit +package local:microquickjs; + +world microquickjs { + export eval: func(code: string) -> string; +} +``` + +## Building + +Requires: +- `wasi-sdk` (v24.0) +- `wit-bindgen` (v0.35.0) +- `wasm-tools` +- `wasi_snapshot_preview1.reactor.wasm` adapter + +Build steps: +1. Generate stdlib headers using native `mquickjs_build_native`. +2. Generate C bindings using `wit-bindgen`. +3. Compile object files using wasi-sdk clang with `-Oz`. +4. Link to `core.wasm`. +5. Embed WIT and lift to component. + +A `Makefile.wasi` is provided to automate these steps. + +## Verification + +The component can be inspected using: +```sh +wasm-tools component wit microquickjs.component.wasm +``` + +Current limitation: WAMR (iwasm) support for the Component Model CLI flag (`\-\-component`) is still in development/experimental branches. + +Verification can be done against a host implementing the WAMR or Wasmtime embedding API with Component Model support. diff --git a/dtoa.c b/dtoa.c index 604f3f0..e90f0d6 100644 --- a/dtoa.c +++ b/dtoa.c @@ -30,7 +30,7 @@ #include #include #include -#include +////#include #include "cutils.h" #include "dtoa.h" diff --git a/glue.c b/glue.c new file mode 100644 index 0000000..5dfce19 --- /dev/null +++ b/glue.c @@ -0,0 +1,60 @@ +#include "microquickjs.h" +#include "mquickjs.h" +#include +#include +#include + +extern const JSSTDLibraryDef js_stdlib; +/* Forward declaration of canonical ABI allocator */ +void *cabi_realloc(void *ptr, size_t old_size, size_t align, size_t new_size); + +static JSContext *s_ctx = NULL; +static uint8_t *s_mem_buf = NULL; +#define MEM_SIZE (16 << 20) + +static void ensure_runtime(void) { + if (s_ctx) return; + s_mem_buf = malloc(MEM_SIZE); + if (!s_mem_buf) abort(); + s_ctx = JS_NewContext(s_mem_buf, MEM_SIZE, &js_stdlib); + if (!s_ctx) abort(); +} + +void microquickjs_eval(microquickjs_string_t *code, microquickjs_string_t *ret) { + ensure_runtime(); + + size_t src_len = code->len; + char *src = malloc(src_len + 1); + if (!src) abort(); + memcpy(src, code->ptr, src_len); + src[src_len] = '\0'; + + JSValue val = JS_Eval(s_ctx, src, src_len, "", JS_EVAL_RETVAL); + free(src); + + JSCStringBuf sbuf; + if (JS_IsException(val)) { + JSValue exc = JS_GetException(s_ctx); + const char *result_cstr = JS_ToCString(s_ctx, exc, &sbuf); + if (!result_cstr) result_cstr = "Unknown error"; + + size_t elen = strlen(result_cstr) + 8; /* "Error: " + NUL */ + char *ebuf = cabi_realloc(NULL, 0, 1, elen); + if (!ebuf) abort(); + snprintf(ebuf, elen, "Error: %s", result_cstr); + + ret->ptr = (uint8_t *)ebuf; + ret->len = strlen(ebuf); + return; + } + + const char *result_cstr = JS_ToCString(s_ctx, val, &sbuf); + if (!result_cstr) result_cstr = ""; + size_t len = strlen(result_cstr); + char *out = cabi_realloc(NULL, 0, 1, len + 1); + if (!out) abort(); + memcpy(out, result_cstr, len + 1); + + ret->ptr = (uint8_t *)out; + ret->len = len; +} diff --git a/glue_stdlib.c b/glue_stdlib.c new file mode 100644 index 0000000..d0c633f --- /dev/null +++ b/glue_stdlib.c @@ -0,0 +1,45 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "cutils.h" +#include "mquickjs.h" + +static JSValue js_print(JSContext *ctx, JSValue *this_val, int argc, JSValue *argv) +{ + return JS_UNDEFINED; +} +static JSValue js_gc(JSContext *ctx, JSValue *this_val, int argc, JSValue *argv) +{ + return JS_UNDEFINED; +} +static JSValue js_date_now(JSContext *ctx, JSValue *this_val, int argc, JSValue *argv) +{ + return JS_UNDEFINED; +} +static JSValue js_performance_now(JSContext *ctx, JSValue *this_val, int argc, JSValue *argv) +{ + return JS_UNDEFINED; +} +static JSValue js_load(JSContext *ctx, JSValue *this_val, int argc, JSValue *argv) +{ + return JS_UNDEFINED; +} +static JSValue js_setTimeout(JSContext *ctx, JSValue *this_val, int argc, JSValue *argv) +{ + return JS_UNDEFINED; +} +static JSValue js_clearTimeout(JSContext *ctx, JSValue *this_val, int argc, JSValue *argv) +{ + return JS_UNDEFINED; +} + +#include "mqjs_stdlib.h" diff --git a/microquickjs.wit b/microquickjs.wit new file mode 100644 index 0000000..9d19af0 --- /dev/null +++ b/microquickjs.wit @@ -0,0 +1,5 @@ +package local:microquickjs; + +world microquickjs { + export eval: func(code: string) -> string; +} diff --git a/mquickjs.c b/mquickjs.c index a950f3c..7fb5833 100644 --- a/mquickjs.c +++ b/mquickjs.c @@ -29,7 +29,7 @@ #include #include #include -#include +////#include #include "cutils.h" #include "dtoa.h" @@ -7305,7 +7305,7 @@ typedef struct JSParseState { uint8_t is_unicode : 1; /* error handling */ - jmp_buf jmp_env; + int jmp_env[1]; char error_msg[64]; } JSParseState; @@ -7606,7 +7606,7 @@ static void __attribute__((format(printf, 2, 3), noreturn)) js_parse_error(JSPar va_start(ap, fmt); js_vsnprintf(s->error_msg, sizeof(s->error_msg), fmt, ap); va_end(ap); - longjmp(s->jmp_env, 1); + abort(); } static void js_parse_error_mem(JSParseState *s) @@ -11713,7 +11713,7 @@ static JSValue JS_Parse2(JSContext *ctx, JSValue source_str, saved_top_gc_ref = ctx->top_gc_ref; saved_sp = ctx->sp; - if (setjmp(s->jmp_env)) { + if (0) { int line_num, col_num; JSValue val; diff --git a/mquickjs_build.c b/mquickjs_build.c index 6173271..0f19f02 100644 --- a/mquickjs_build.c +++ b/mquickjs_build.c @@ -286,7 +286,7 @@ static int atom_cmp(const void *p1, const void *p2) /* js_atom_table must be properly aligned because the property hash table uses the low bits of the atom pointer value */ -#define ATOM_ALIGN 64 +#define ATOM_ALIGN 256 static void dump_atoms(BuildContext *ctx) {