MIRROR: javascript for 馃悳's, a tiny runtime with big ambitions
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at master 460 lines 15 kB view raw
1#include <stdio.h> 2#include <stdlib.h> 3#include <string.h> 4 5#include "ant.h" 6#include "errors.h" 7#include "internal.h" 8 9#include "ptr.h" 10#include "gc/roots.h" 11#include "modules/buffer.h" 12#include "modules/wasi.h" 13#include "wasm_c_api.h" 14#include "wasm_export.h" 15 16#define WASM_MAX_PARAMS 32 17#define WASM_MAX_ARGS 256 18 19enum { 20 WASI_INSTANCE_TAG = 0x57415349u, // WASI 21 WASI_FUNC_TAG = 0x57415346u, // WASF 22}; 23 24typedef struct { 25 uint8_t *binary; 26 wasm_module_t module; 27 wasm_module_inst_t inst; 28 wasm_exec_env_t exec_env; 29} wasi_instance_handle_t; 30 31typedef struct { 32 ant_t *js; 33 wasm_module_inst_t inst; 34 wasm_exec_env_t exec_env; 35 wasm_function_inst_t func; 36} wasi_func_env_t; 37 38enum { 39 WASM_SECTION_IMPORT = 2, 40 WASM_SECTION_EXPORT = 7, 41}; 42 43static bool wasm_read_u32_leb(const uint8_t *buf, size_t len, size_t *offset, uint32_t *out) { 44 uint32_t value = 0; 45 uint32_t shift = 0; 46 47 while (*offset < len && shift < 35) { 48 uint8_t byte = buf[(*offset)++]; 49 value |= (uint32_t)(byte & 0x7f) << shift; 50 51 if ((byte & 0x80) == 0) { 52 *out = value; 53 return true; 54 } 55 56 shift += 7; 57 } 58 59 return false; 60} 61 62static bool wasm_read_name(const uint8_t *buf, size_t len, size_t *offset, const uint8_t **data, uint32_t *name_len) { 63 uint32_t size = 0; 64 65 if ( 66 !wasm_read_u32_leb(buf, len, offset, &size) 67 || *offset + size > len 68 ) return false; 69 70 *data = buf + *offset; 71 *name_len = size; 72 *offset += size; 73 74 return true; 75} 76 77static bool wasm_name_equals(const uint8_t *data, uint32_t len, const char *expected) { 78 size_t expected_len = strlen(expected); 79 return len == expected_len && memcmp(data, expected, expected_len) == 0; 80} 81 82static bool wasi_bytes_have_wasi_imports(const uint8_t *wasm_bytes, size_t wasm_len) { 83 size_t offset = 8; 84 85 if (!wasm_bytes || wasm_len < 8 || memcmp(wasm_bytes, "\0asm\x01\0\0\0", 8) != 0) 86 return false; 87 88 while (offset < wasm_len) { 89 uint8_t section_id = wasm_bytes[offset++]; 90 uint32_t section_size = 0; 91 const uint8_t *section; 92 size_t section_offset = 0; 93 size_t section_len; 94 95 if ( 96 !wasm_read_u32_leb(wasm_bytes, wasm_len, &offset, &section_size) 97 || offset + section_size > wasm_len 98 ) return false; 99 100 section = wasm_bytes + offset; 101 section_len = section_size; 102 offset += section_size; 103 104 if (section_id != WASM_SECTION_IMPORT) continue; 105 if (!wasm_read_u32_leb(section, section_size, &section_offset, &section_size)) 106 return false; 107 108 for (uint32_t i = 0; i < section_size; i++) { 109 const uint8_t *module_name = NULL; 110 const uint8_t *field_name = NULL; 111 uint32_t module_name_len = 0; 112 uint32_t field_name_len = 0; 113 uint32_t discard = 0; 114 uint32_t flags = 0; 115 116 if (!wasm_read_name(section, section_len, &section_offset, &module_name, &module_name_len) 117 || !wasm_read_name(section, section_len, &section_offset, &field_name, &field_name_len) 118 || section_offset >= section_len) 119 return false; 120 121 if (wasm_name_equals(module_name, module_name_len, "wasi_snapshot_preview1")) 122 return true; 123 124 switch (section[section_offset++]) { 125 case 0: 126 if (!wasm_read_u32_leb(section, section_len, &section_offset, &discard)) 127 return false; 128 break; 129 case 1: { 130 if (section_offset >= section_len) return false; 131 section_offset++; 132 if (!wasm_read_u32_leb(section, section_len, &section_offset, &flags) 133 || !wasm_read_u32_leb(section, section_len, &section_offset, &discard)) 134 return false; 135 if ((flags & 0x1) != 0 136 && !wasm_read_u32_leb(section, section_len, &section_offset, &discard)) 137 return false; 138 break; 139 } 140 case 2: 141 if (!wasm_read_u32_leb(section, section_len, &section_offset, &flags) 142 || !wasm_read_u32_leb(section, section_len, &section_offset, &discard)) 143 return false; 144 if ((flags & 0x1) != 0 145 && !wasm_read_u32_leb(section, section_len, &section_offset, &discard)) 146 return false; 147 break; 148 case 3: 149 if (section_offset + 2 > section_len) return false; 150 section_offset += 2; 151 break; 152 default: 153 return false; 154 } 155 } 156 } 157 158 return false; 159} 160 161static bool wasi_bytes_have_command_or_reactor_entry(const uint8_t *wasm_bytes, size_t wasm_len) { 162 size_t offset = 8; 163 164 if (!wasm_bytes || wasm_len < 8 || memcmp(wasm_bytes, "\0asm\x01\0\0\0", 8) != 0) 165 return false; 166 167 while (offset < wasm_len) { 168 uint8_t section_id = wasm_bytes[offset++]; 169 uint32_t section_size = 0; 170 const uint8_t *section; 171 size_t section_offset = 0; 172 size_t section_len; 173 174 if (!wasm_read_u32_leb(wasm_bytes, wasm_len, &offset, &section_size) 175 || offset + section_size > wasm_len) 176 return false; 177 178 section = wasm_bytes + offset; 179 section_len = section_size; 180 offset += section_size; 181 182 if (section_id != WASM_SECTION_EXPORT) continue; 183 if (!wasm_read_u32_leb(section, section_len, &section_offset, &section_size)) 184 return false; 185 186 for (uint32_t i = 0; i < section_size; i++) { 187 const uint8_t *name = NULL; 188 uint32_t name_len = 0; 189 uint32_t discard = 0; 190 191 if (!wasm_read_name(section, section_len, &section_offset, &name, &name_len) 192 || section_offset >= section_len) 193 return false; 194 195 if (section[section_offset++] != 0) { 196 if (!wasm_read_u32_leb(section, section_len, &section_offset, &discard)) 197 return false; 198 continue; 199 } 200 201 if (!wasm_read_u32_leb(section, section_len, &section_offset, &discard)) 202 return false; 203 204 if (wasm_name_equals(name, name_len, "_start") 205 || wasm_name_equals(name, name_len, "_initialize")) 206 return true; 207 } 208 } 209 210 return false; 211} 212 213static inline bool wasi_is_proc_exit_exception(const char *exception) { 214 return exception != NULL && strstr(exception, "wasi proc exit") != NULL; 215} 216 217static ant_value_t wasi_handle_proc_exit(wasm_module_inst_t inst) { 218 uint32_t exit_code = wasm_runtime_get_wasi_exit_code(inst); 219 wasm_runtime_clear_exception(inst); 220 if (exit_code != 0) exit((int)exit_code); 221 return js_mkundef(); 222} 223 224static ant_value_t wasi_exported_func_call(ant_t *js, ant_value_t *args, int nargs) { 225 wasi_func_env_t *env = (wasi_func_env_t *)js_get_native(js->current_func, WASI_FUNC_TAG); 226 if (!env) return js_mkerr(js, "Invalid WASI function"); 227 228 uint32_t param_count = wasm_func_get_param_count(env->func, env->inst); 229 uint32_t result_count = wasm_func_get_result_count(env->func, env->inst); 230 if (param_count > WASM_MAX_PARAMS) param_count = WASM_MAX_PARAMS; 231 232 uint32_t wasm_argv[WASM_MAX_PARAMS]; 233 memset(wasm_argv, 0, sizeof(wasm_argv)); 234 235 for (int i = 0; i < nargs && (uint32_t)i < param_count; i++) { 236 wasm_argv[i] = (uint32_t)js_getnum(args[i]); 237 } 238 239 if (!wasm_runtime_call_wasm(env->exec_env, env->func, param_count, wasm_argv)) { 240 const char *exception = wasm_runtime_get_exception(env->inst); 241 if (wasi_is_proc_exit_exception(exception)) return wasi_handle_proc_exit(env->inst); 242 return js_mkerr(js, "%s", exception ? exception : "WASI function call failed"); 243 } 244 245 if (result_count == 0) return js_mkundef(); 246 return js_mknum((double)(int32_t)wasm_argv[0]); 247} 248 249static void wasi_func_finalize(ant_t *js, ant_object_t *obj) { 250 ant_value_t value = js_obj_from_ptr(obj); 251 free(js_get_native(value, WASI_FUNC_TAG)); 252 js_clear_native(value, WASI_FUNC_TAG); 253} 254 255static void wasi_instance_finalize(ant_t *js, ant_object_t *obj) { 256 ant_value_t value = js_obj_from_ptr(obj); 257 wasi_instance_handle_t *handle = (wasi_instance_handle_t *)js_get_native(value, WASI_INSTANCE_TAG); 258 259 if (!handle) return; 260 if (handle->exec_env) wasm_runtime_destroy_exec_env(handle->exec_env); 261 if (handle->inst) wasm_runtime_deinstantiate(handle->inst); 262 js_clear_native(value, WASI_INSTANCE_TAG); 263 if (handle->module) wasm_runtime_unload(handle->module); 264 265 free(handle->binary); 266 free(handle); 267} 268 269bool wasi_module_has_wasi_imports(void *c_api_module) { 270 wasm_importtype_vec_t import_types = {0}; 271 wasm_module_imports((wasm_module_t *)c_api_module, &import_types); 272 273 bool has_wasi = false; 274 for (size_t i = 0; i < import_types.size; i++) { 275 const wasm_name_t *mod = wasm_importtype_module(import_types.data[i]); 276 if (mod && mod->size >= 22 && memcmp(mod->data, "wasi_snapshot_preview1", 22) == 0) { 277 has_wasi = true; 278 break; 279 }} 280 281 wasm_importtype_vec_delete(&import_types); 282 return has_wasi; 283} 284 285bool wasi_module_is_command_or_reactor(void *c_api_module) { 286 wasm_exporttype_vec_t export_types = {0}; 287 bool has_entry = false; 288 289 wasm_module_exports((wasm_module_t *)c_api_module, &export_types); 290 291 for (size_t i = 0; i < export_types.size; i++) { 292 const wasm_name_t *name = wasm_exporttype_name(export_types.data[i]); 293 const wasm_externtype_t *type = wasm_exporttype_type(export_types.data[i]); 294 const wasm_functype_t *func_type; 295 size_t name_len; 296 297 if (!name || wasm_externtype_kind(type) != WASM_EXTERN_FUNC) continue; 298 name_len = name->size; 299 if (name_len > 0 && name->data[name_len - 1] == '\0') name_len--; 300 301 if (!((name_len == 6 && memcmp(name->data, "_start", 6) == 0) 302 || (name_len == 11 && memcmp(name->data, "_initialize", 11) == 0))) 303 continue; 304 305 func_type = wasm_externtype_as_functype_const(type); 306 if (!func_type) continue; 307 if (wasm_functype_params(func_type)->size == 0 && wasm_functype_results(func_type)->size == 0) { 308 has_entry = true; 309 break; 310 } 311 } 312 313 wasm_exporttype_vec_delete(&export_types); 314 return has_entry; 315} 316 317bool wasi_bytes_need_wasi_command_warning_suppression(const uint8_t *wasm_bytes, size_t wasm_len) { 318 return wasi_bytes_have_wasi_imports(wasm_bytes, wasm_len) 319 && !wasi_bytes_have_command_or_reactor_entry(wasm_bytes, wasm_len); 320} 321 322static void wasi_bind_func_export( 323 ant_t *js, ant_value_t exports_obj, ant_value_t instance_obj, 324 wasm_module_inst_t inst, wasm_exec_env_t exec_env, const char *name 325) { 326 wasm_function_inst_t func = wasm_runtime_lookup_function(inst, name); 327 if (!func) return; 328 329 wasi_func_env_t *fenv = calloc(1, sizeof(*fenv)); 330 if (!fenv) return; 331 fenv->js = js; 332 fenv->inst = inst; 333 fenv->exec_env = exec_env; 334 fenv->func = func; 335 336 GC_ROOT_SAVE(root_mark, js); 337 ant_value_t obj = js_mkobj(js); 338 GC_ROOT_PIN(js, obj); 339 340 js_set_slot(obj, SLOT_CFUNC, js_mkfun(wasi_exported_func_call)); 341 js_set_native(obj, fenv, WASI_FUNC_TAG); 342 js_set_slot_wb(js, obj, SLOT_ENTRIES, instance_obj); 343 js_set_finalizer(obj, wasi_func_finalize); 344 js_set(js, exports_obj, name, js_obj_to_func(obj)); 345 GC_ROOT_RESTORE(js, root_mark); 346} 347 348static void wasi_bind_memory_export( 349 ant_t *js, ant_value_t exports_obj, ant_value_t instance_obj, 350 wasm_module_inst_t inst, const char *name 351) { 352 void *mem_data = wasm_runtime_addr_app_to_native(inst, 0); 353 if (!mem_data) return; 354 355 wasm_memory_inst_t mem = wasm_runtime_get_default_memory(inst); 356 uint64_t pages = mem ? wasm_memory_get_cur_page_count(mem) : 0; 357 size_t mem_size = (size_t)(pages * 65536); 358 359 ArrayBufferData *buffer = calloc(1, sizeof(ArrayBufferData)); 360 if (!buffer) return; 361 362 buffer->data = (uint8_t *)mem_data; 363 buffer->length = mem_size; 364 buffer->capacity = mem_size; 365 buffer->ref_count = 1; 366 367 ant_value_t ab = create_arraybuffer_obj(js, buffer); 368 ant_value_t mem_obj = js_mkobj(js); 369 js_set_slot_wb(js, mem_obj, SLOT_DATA, ab); 370 js_set_slot_wb(js, mem_obj, SLOT_CTOR, instance_obj); 371 js_set(js, exports_obj, name, mem_obj); 372} 373 374ant_value_t wasi_instantiate( 375 ant_t *js, const uint8_t *wasm_bytes, size_t wasm_len, 376 ant_value_t module_obj, ant_value_t wasi_opts 377) { 378 char error_buf[128] = {0}; 379 uint8_t *bin_copy = malloc(wasm_len); 380 381 if (!bin_copy) return js_mkerr(js, "out of memory"); 382 memcpy(bin_copy, wasm_bytes, wasm_len); 383 384 wasm_module_t rt_module = wasm_runtime_load(bin_copy, (uint32_t)wasm_len, error_buf, sizeof(error_buf)); 385 if (!rt_module) { 386 free(bin_copy); 387 return js_mkerr(js, "%s", error_buf[0] ? error_buf : "Failed to load WASI module"); 388 } 389 390 const char *dirs[] = { "." }; 391 ant_value_t args_val = is_object_type(wasi_opts) 392 ? js_get(js, wasi_opts, "args") 393 : js_mkundef(); 394 395 int argc = vtype(args_val) == T_ARR ? (int)js_arr_len(js, args_val) : 0; 396 if (argc < 1) argc = 1; 397 if (argc > WASM_MAX_ARGS) argc = WASM_MAX_ARGS; 398 399 char *argv[argc]; 400 if (vtype(args_val) == T_ARR) { 401 for (int i = 0; i < argc; i++) { 402 char *s = js_getstr(js, js_arr_get(js, args_val, (ant_offset_t)i), NULL); 403 argv[i] = s ? s : (char *)""; 404 } 405 } else argv[0] = (char *)"wasi"; 406 407 wasm_runtime_set_wasi_args(rt_module, dirs, 1, NULL, 0, NULL, 0, argv, argc); 408 wasm_module_inst_t inst = wasm_runtime_instantiate(rt_module, 512 * 1024, 256 * 1024, error_buf, sizeof(error_buf)); 409 410 if (!inst) { 411 wasm_runtime_unload(rt_module); 412 free(bin_copy); 413 return js_mkerr(js, "%s", error_buf[0] ? error_buf : "Failed to instantiate WASI module"); 414 } 415 416 wasm_exec_env_t exec_env = wasm_runtime_create_exec_env(inst, 512 * 1024); 417 if (!exec_env) { 418 wasm_runtime_deinstantiate(inst); 419 wasm_runtime_unload(rt_module); 420 free(bin_copy); 421 return js_mkerr(js, "Failed to create WASI exec env"); 422 } 423 424 wasi_instance_handle_t *handle = calloc(1, sizeof(*handle)); 425 if (!handle) { 426 wasm_runtime_destroy_exec_env(exec_env); 427 wasm_runtime_deinstantiate(inst); 428 wasm_runtime_unload(rt_module); 429 free(bin_copy); 430 return js_mkerr(js, "out of memory"); 431 } 432 433 handle->binary = bin_copy; 434 handle->module = rt_module; 435 handle->inst = inst; 436 handle->exec_env = exec_env; 437 438 ant_value_t instance_obj = js_mkobj(js); 439 ant_value_t exports_obj = js_mkobj(js); 440 441 js_set_native(instance_obj, handle, WASI_INSTANCE_TAG); 442 js_set_slot_wb(js, instance_obj, SLOT_CTOR, module_obj); 443 js_set_finalizer(instance_obj, wasi_instance_finalize); 444 445 int32_t export_count = wasm_runtime_get_export_count(rt_module); 446 for (int32_t i = 0; i < export_count; i++) { 447 wasm_export_t export_info; 448 wasm_runtime_get_export_type(rt_module, i, &export_info); 449 450 if (export_info.kind == WASM_IMPORT_EXPORT_KIND_FUNC) 451 wasi_bind_func_export(js, exports_obj, instance_obj, inst, exec_env, export_info.name); 452 else if (export_info.kind == WASM_IMPORT_EXPORT_KIND_MEMORY) 453 wasi_bind_memory_export(js, exports_obj, instance_obj, inst, export_info.name); 454 } 455 456 js_set_slot_wb(js, instance_obj, SLOT_ENTRIES, exports_obj); 457 js_set(js, instance_obj, "exports", exports_obj); 458 459 return instance_obj; 460}