Something went wrong. Try again.
MIRROR: javascript for 🐜's, a tiny runtime with big ambitions ant.themackabu.dev
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293#include <stdio.h>#include <stdlib.h>#include <string.h>
#include "ant.h"#include "cfunc.h"#include "errors.h"#include "internal.h"
#include "ptr.h"#include "gc/roots.h"#include "modules/buffer.h"#include "modules/wasi.h"#include "wasm_c_api.h"#include "wasm_export.h"
#define WASM_MAX_PARAMS 32#define WASM_MAX_ARGS 256
static constexpr uint32_t WASI_INSTANCE_TAG = Ant_NativeTag("WASI");static constexpr uint32_t WASI_FUNC_TAG = Ant_NativeTag("WASF");
typedef struct { uint8_t *binary; wasm_module_t module; wasm_module_inst_t inst; wasm_exec_env_t exec_env;} wasi_instance_handle_t;
typedef struct { ant_t *js; wasm_module_inst_t inst; wasm_exec_env_t exec_env; wasm_function_inst_t func;} wasi_func_env_t;
static inline bool wasi_is_proc_exit_exception(const char *exception) { return exception != NULL && strstr(exception, "wasi proc exit") != NULL;}
static ant_value_t wasi_handle_proc_exit(wasm_module_inst_t inst) { uint32_t exit_code = wasm_runtime_get_wasi_exit_code(inst); wasm_runtime_clear_exception(inst); if (exit_code != 0) exit((int)exit_code); return js_mkundef();}
static ant_value_t wasi_exported_func_call(ant_params_t) { wasi_func_env_t *env = (wasi_func_env_t *)js_get_native(js->current_func, WASI_FUNC_TAG); if (!env) return js_mkerr(js, "Invalid WASI function");
uint32_t param_count = wasm_func_get_param_count(env->func, env->inst); uint32_t result_count = wasm_func_get_result_count(env->func, env->inst); if (param_count > WASM_MAX_PARAMS) param_count = WASM_MAX_PARAMS;
uint32_t wasm_argv[WASM_MAX_PARAMS]; memset(wasm_argv, 0, sizeof(wasm_argv));
for (int i = 0; i < nargs && (uint32_t)i < param_count; i++) { wasm_argv[i] = (uint32_t)js_getnum(args[i]); }
if (!wasm_runtime_call_wasm(env->exec_env, env->func, param_count, wasm_argv)) { const char *exception = wasm_runtime_get_exception(env->inst); if (wasi_is_proc_exit_exception(exception)) return wasi_handle_proc_exit(env->inst); return js_mkerr(js, "%s", exception ? exception : "WASI function call failed"); }
if (result_count == 0) return js_mkundef(); return js_mknum((double)(int32_t)wasm_argv[0]);}
static void wasi_func_finalize(ant_t *js, ant_object_t *obj) { ant_value_t value = js_obj_from_ptr(obj); free(js_get_native(value, WASI_FUNC_TAG)); js_clear_native(value, WASI_FUNC_TAG);}
static void wasi_instance_finalize(ant_t *js, ant_object_t *obj) { ant_value_t value = js_obj_from_ptr(obj); wasi_instance_handle_t *handle = (wasi_instance_handle_t *)js_get_native(value, WASI_INSTANCE_TAG);
if (!handle) return; if (handle->exec_env) wasm_runtime_destroy_exec_env(handle->exec_env); if (handle->inst) wasm_runtime_deinstantiate(handle->inst); js_clear_native(value, WASI_INSTANCE_TAG); if (handle->module) wasm_runtime_unload(handle->module);
free(handle->binary); free(handle);}
bool wasi_module_has_wasi_imports(void *c_api_module) { wasm_importtype_vec_t import_types = {0}; wasm_module_imports((wasm_module_t *)c_api_module, &import_types);
bool has_wasi = false; for (size_t i = 0; i < import_types.size; i++) { const wasm_name_t *mod = wasm_importtype_module(import_types.data[i]); if (mod && mod->size >= 22 && memcmp(mod->data, "wasi_snapshot_preview1", 22) == 0) { has_wasi = true; break; }}
wasm_importtype_vec_delete(&import_types); return has_wasi;}
bool wasi_module_is_command_or_reactor(void *c_api_module) { wasm_exporttype_vec_t export_types = {0}; bool has_entry = false;
wasm_module_exports((wasm_module_t *)c_api_module, &export_types);
for (size_t i = 0; i < export_types.size; i++) { const wasm_name_t *name = wasm_exporttype_name(export_types.data[i]); const wasm_externtype_t *type = wasm_exporttype_type(export_types.data[i]); const wasm_functype_t *func_type; size_t name_len;
if (!name || wasm_externtype_kind(type) != WASM_EXTERN_FUNC) continue; name_len = name->size; if (name_len > 0 && name->data[name_len - 1] == '\0') name_len--;
if (!((name_len == 6 && memcmp(name->data, "_start", 6) == 0) || (name_len == 11 && memcmp(name->data, "_initialize", 11) == 0))) continue;
func_type = wasm_externtype_as_functype_const(type); if (!func_type) continue; if (wasm_functype_params(func_type)->size == 0 && wasm_functype_results(func_type)->size == 0) { has_entry = true; break; } }
wasm_exporttype_vec_delete(&export_types); return has_entry;}
static void wasi_bind_func_export( ant_t *js, ant_value_t exports_obj, ant_value_t instance_obj, wasm_module_inst_t inst, wasm_exec_env_t exec_env, const char *name) { wasm_function_inst_t func = wasm_runtime_lookup_function(inst, name); if (!func) return;
wasi_func_env_t *fenv = calloc(1, sizeof(*fenv)); if (!fenv) return; fenv->js = js; fenv->inst = inst; fenv->exec_env = exec_env; fenv->func = func;
GC_ROOT_SAVE(root_mark, js); ant_value_t obj = Ant_Object_CreateBare(js); GC_ROOT_PIN(js, obj); js_set_slot(obj, SLOT_CFUNC, Ant_Function_Create(wasi_exported_func_call)); js_set_native(obj, fenv, WASI_FUNC_TAG); js_set_slot_wb(js, obj, SLOT_ENTRIES, instance_obj); js_set_finalizer(obj, wasi_func_finalize); js_set(js, exports_obj, name, js_obj_to_func(js, obj)); GC_ROOT_RESTORE(js, root_mark);}
static void wasi_bind_memory_export( ant_t *js, ant_value_t exports_obj, ant_value_t instance_obj, wasm_module_inst_t inst, const char *name) { void *mem_data = wasm_runtime_addr_app_to_native(inst, 0); if (!mem_data) return;
wasm_memory_inst_t mem = wasm_runtime_get_default_memory(inst); uint64_t pages = mem ? wasm_memory_get_cur_page_count(mem) : 0; size_t mem_size = (size_t)(pages * 65536);
ArrayBufferData *buffer = create_external_array_buffer_data((uint8_t *)mem_data, mem_size); if (!buffer) return;
ant_value_t ab = create_arraybuffer_obj(js, buffer); ant_value_t mem_obj = Ant_Object_CreateBare(js); js_set_slot_wb(js, mem_obj, SLOT_DATA, ab); js_set_slot_wb(js, mem_obj, SLOT_CTOR, instance_obj); js_set(js, exports_obj, name, mem_obj);}
ant_value_t wasi_instantiate( ant_t *js, const uint8_t *wasm_bytes, size_t wasm_len, ant_value_t module_obj, ant_value_t wasi_opts) { char error_buf[128] = {0}; uint8_t *bin_copy = malloc(wasm_len);
if (!bin_copy) return js_mkerr(js, "out of memory"); memcpy(bin_copy, wasm_bytes, wasm_len);
wasm_module_t rt_module = wasm_runtime_load(bin_copy, (uint32_t)wasm_len, error_buf, sizeof(error_buf)); if (!rt_module) { free(bin_copy); return js_mkerr(js, "%s", error_buf[0] ? error_buf : "Failed to load WASI module"); }
const char *default_dirs[] = { "." }; const char **dirs = default_dirs; const char *dir_buf[WASM_MAX_ARGS]; int dir_count = 1;
ant_value_t args_val = is_object_type(wasi_opts) ? js_get(js, wasi_opts, "args") : js_mkundef(); ant_value_t preopens_val = is_object_type(wasi_opts) ? js_get(js, wasi_opts, "preopens") : js_mkundef();
if (vtype(preopens_val) == kTypeArray && js_arr_len(js, preopens_val) > 0) { dir_count = (int)js_arr_len(js, preopens_val); if (dir_count > WASM_MAX_ARGS) dir_count = WASM_MAX_ARGS; for (int i = 0; i < dir_count; i++) { char *s = js_getstr(js, js_arr_get(js, preopens_val, (ant_offset_t)i), NULL); dir_buf[i] = s ? s : "."; } dirs = dir_buf; }
int argc = vtype(args_val) == kTypeArray ? (int)js_arr_len(js, args_val) : 0; if (argc < 1) argc = 1; if (argc > WASM_MAX_ARGS) argc = WASM_MAX_ARGS;
char *argv[argc]; if (vtype(args_val) == kTypeArray) { for (int i = 0; i < argc; i++) { char *s = js_getstr(js, js_arr_get(js, args_val, (ant_offset_t)i), NULL); argv[i] = s ? s : (char *)""; } } else argv[0] = (char *)"wasi";
wasm_runtime_set_wasi_args(rt_module, dirs, dir_count, NULL, 0, NULL, 0, argv, argc); wasm_module_inst_t inst = wasm_runtime_instantiate(rt_module, 512 * 1024, 256 * 1024, error_buf, sizeof(error_buf));
if (!inst) { wasm_runtime_unload(rt_module); free(bin_copy); return js_mkerr(js, "%s", error_buf[0] ? error_buf : "Failed to instantiate WASI module"); }
wasm_exec_env_t exec_env = wasm_runtime_create_exec_env(inst, 512 * 1024); if (!exec_env) { wasm_runtime_deinstantiate(inst); wasm_runtime_unload(rt_module); free(bin_copy); return js_mkerr(js, "Failed to create WASI exec env"); }
wasi_instance_handle_t *handle = calloc(1, sizeof(*handle)); if (!handle) { wasm_runtime_destroy_exec_env(exec_env); wasm_runtime_deinstantiate(inst); wasm_runtime_unload(rt_module); free(bin_copy); return js_mkerr(js, "out of memory"); }
handle->binary = bin_copy; handle->module = rt_module; handle->inst = inst; handle->exec_env = exec_env;
ant_value_t instance_obj = Ant_Object_CreateBare(js); ant_value_t exports_obj = Ant_Object_CreateBare(js);
js_set_native(instance_obj, handle, WASI_INSTANCE_TAG); js_set_slot_wb(js, instance_obj, SLOT_CTOR, module_obj); js_set_finalizer(instance_obj, wasi_instance_finalize);
int32_t export_count = wasm_runtime_get_export_count(rt_module); for (int32_t i = 0; i < export_count; i++) { wasm_export_t export_info; wasm_runtime_get_export_type(rt_module, i, &export_info); if (export_info.kind == WASM_IMPORT_EXPORT_KIND_FUNC) wasi_bind_func_export(js, exports_obj, instance_obj, inst, exec_env, export_info.name); else if (export_info.kind == WASM_IMPORT_EXPORT_KIND_MEMORY) wasi_bind_memory_export(js, exports_obj, instance_obj, inst, export_info.name); }
js_set_slot_wb(js, instance_obj, SLOT_ENTRIES, exports_obj); js_set(js, instance_obj, "exports", exports_obj);
return instance_obj;}