Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions Makefile.wasi
Original file line number Diff line number Diff line change
@@ -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
43 changes: 43 additions & 0 deletions README.WASI.md
Original file line number Diff line number Diff line change
@@ -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.
2 changes: 1 addition & 1 deletion dtoa.c
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
#include <ctype.h>
#include <sys/time.h>
#include <math.h>
#include <setjmp.h>
////#include <setjmp.h>

#include "cutils.h"
#include "dtoa.h"
Expand Down
60 changes: 60 additions & 0 deletions glue.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
#include "microquickjs.h"
#include "mquickjs.h"
#include <string.h>
#include <stdio.h>
#include <stdlib.h>

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, "<eval>", 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;
}
45 changes: 45 additions & 0 deletions glue_stdlib.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <inttypes.h>
#include <string.h>
#include <assert.h>
#include <ctype.h>
#include <time.h>
#include <sys/time.h>
#include <math.h>
#include <fcntl.h>
#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"
5 changes: 5 additions & 0 deletions microquickjs.wit
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package local:microquickjs;

world microquickjs {
export eval: func(code: string) -> string;
}
8 changes: 4 additions & 4 deletions mquickjs.c
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
#include <string.h>
#include <assert.h>
#include <math.h>
#include <setjmp.h>
////#include <setjmp.h>

#include "cutils.h"
#include "dtoa.h"
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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;

Expand Down
2 changes: 1 addition & 1 deletion mquickjs_build.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down