MIRROR: javascript for 馃悳's, a tiny runtime with big ambitions
1#include <stdio.h>
2#include <stdlib.h>
3#include <string.h>
4#include <inttypes.h>
5#include <wasm_c_api.h>
6#include <wasm_export.h>
7
8#pragma clang diagnostic push
9#pragma clang diagnostic ignored "-Wundef"
10#pragma clang diagnostic ignored "-Wimplicit-int-conversion"
11#include <wasm_c_api_internal.h>
12#include <wasm_runtime.h>
13#pragma clang diagnostic pop
14
15#include "ant.h"
16#include "ptr.h"
17#include "errors.h"
18#include "runtime.h"
19#include "internal.h"
20#include "descriptors.h"
21
22#include "gc/modules.h"
23#include "silver/engine.h"
24#include "modules/buffer.h"
25#include "modules/wasm.h"
26#include "modules/wasi.h"
27
28typedef struct {
29 wasm_store_t *store;
30 wasm_module_t *module;
31} wasm_module_handle_t;
32
33typedef struct {
34 wasm_instance_t *instance;
35 wasm_extern_vec_t exports;
36 wasm_func_t **host_funcs;
37 size_t host_func_count;
38} wasm_instance_handle_t;
39
40typedef enum {
41 WASM_EXTERN_WRAP_GLOBAL = 1,
42 WASM_EXTERN_WRAP_MEMORY,
43 WASM_EXTERN_WRAP_TABLE,
44} wasm_extern_wrap_kind_t;
45
46typedef struct {
47 wasm_extern_wrap_kind_t kind;
48 wasm_store_t *store;
49 bool own_handle;
50 bool use_cached_value;
51 wasm_val_t cached_value;
52 union {
53 wasm_global_t *global;
54 wasm_memory_t *memory;
55 wasm_table_t *table;
56 } as;
57} wasm_extern_handle_t;
58
59typedef struct {
60 ant_t *js;
61 wasm_store_t *store;
62 ant_value_t owner;
63 ant_value_t fn;
64} wasm_import_func_env_t;
65
66typedef struct {
67 wasm_func_t *func;
68 bool own_func;
69} wasm_func_handle_t;
70
71enum { WASM_FUNC_STATE_TAG = 0x57465354u }; // WFST
72
73static size_t g_wasm_import_env_count = 0;
74static size_t g_wasm_import_env_cap = 0;
75
76static ant_value_t g_wasm_module_proto = 0;
77static ant_value_t g_wasm_instance_proto = 0;
78static ant_value_t g_wasm_global_proto = 0;
79static ant_value_t g_wasm_memory_proto = 0;
80static ant_value_t g_wasm_table_proto = 0;
81static ant_value_t g_wasm_tag_proto = 0;
82static ant_value_t g_wasm_exception_proto = 0;
83
84static ant_value_t g_wasm_compileerror_proto = 0;
85static ant_value_t g_wasm_linkerror_proto = 0;
86static ant_value_t g_wasm_runtimeerror_proto = 0;
87static ant_value_t g_wasm_pending_import_throw = 0;
88
89static wasm_engine_t *g_wasm_engine = NULL;
90static wasm_import_func_env_t **g_wasm_import_envs = NULL;
91static bool g_wasm_pending_import_throw_exists = false;
92
93static void wasm_clear_pending_import_throw(void) {
94 g_wasm_pending_import_throw_exists = false;
95 g_wasm_pending_import_throw = js_mkundef();
96}
97
98static void wasm_set_pending_import_throw(ant_value_t value) {
99 g_wasm_pending_import_throw_exists = true;
100 g_wasm_pending_import_throw = value;
101}
102
103static ant_value_t wasm_consume_pending_import_throw(void) {
104 ant_value_t value = g_wasm_pending_import_throw_exists
105 ? g_wasm_pending_import_throw
106 : js_mkundef();
107 wasm_clear_pending_import_throw();
108 return value;
109}
110
111static void wasm_register_import_env(wasm_import_func_env_t *env) {
112 if (g_wasm_import_env_count == g_wasm_import_env_cap) {
113 size_t new_cap = g_wasm_import_env_cap ? g_wasm_import_env_cap * 2 : 16;
114 wasm_import_func_env_t **new_arr = realloc(g_wasm_import_envs, new_cap * sizeof(*new_arr));
115 if (!new_arr) return;
116 g_wasm_import_envs = new_arr;
117 g_wasm_import_env_cap = new_cap;
118 }
119 g_wasm_import_envs[g_wasm_import_env_count++] = env;
120}
121
122static void wasm_unregister_import_env(wasm_import_func_env_t *env) {
123 for (size_t i = 0; i < g_wasm_import_env_count; i++) {
124 if (g_wasm_import_envs[i] != env) continue;
125 g_wasm_import_envs[i] = g_wasm_import_envs[--g_wasm_import_env_count];
126 return;
127 }
128}
129
130static void wasm_import_func_env_finalizer(void *env_ptr) {
131 wasm_import_func_env_t *env = (wasm_import_func_env_t *)env_ptr;
132 wasm_unregister_import_env(env);
133 free(env);
134}
135
136static ant_value_t wasm_wrap_func(
137 ant_t *js, wasm_func_t *func,
138 ant_value_t owner, bool own_func
139);
140
141static bool ensure_wasm_engine(void) {
142 if (g_wasm_engine) return true;
143 g_wasm_engine = wasm_engine_new();
144 return g_wasm_engine != NULL;
145}
146
147static size_t wasm_name_len(const wasm_name_t *name) {
148 if (!name || !name->data) return 0;
149 if (name->size > 0 && name->data[name->size - 1] == '\0') return name->size - 1;
150 return name->size;
151}
152
153static const char *wasm_extern_kind_name(wasm_externkind_t kind) {
154switch (kind) {
155 case WASM_EXTERN_FUNC: return "function";
156 case WASM_EXTERN_GLOBAL: return "global";
157 case WASM_EXTERN_TABLE: return "table";
158 case WASM_EXTERN_MEMORY: return "memory";
159 default: return "unknown";
160}}
161
162static wasm_valkind_t wasm_valkind_from_string(const char *name, size_t len, bool *ok) {
163 *ok = true;
164 if (len == 3 && !memcmp(name, "i32", 3)) return WASM_I32;
165 if (len == 3 && !memcmp(name, "i64", 3)) return WASM_I64;
166 if (len == 3 && !memcmp(name, "f32", 3)) return WASM_F32;
167 if (len == 3 && !memcmp(name, "f64", 3)) return WASM_F64;
168 if (len == 9 && !memcmp(name, "externref", 9)) return WASM_EXTERNREF;
169 if (len == 7 && !memcmp(name, "funcref", 7)) return WASM_FUNCREF;
170 *ok = false;
171 return WASM_I32;
172}
173
174static wasm_module_handle_t *wasm_module_handle(ant_value_t value) {
175 if (!js_check_brand(value, BRAND_WASM_MODULE)) return NULL;
176 ant_value_t slot = js_get_slot(value, SLOT_DATA);
177 if (vtype(slot) != T_NUM) return NULL;
178 return (wasm_module_handle_t *)(uintptr_t)(size_t)js_getnum(slot);
179}
180
181static wasm_instance_handle_t *wasm_instance_handle(ant_value_t value) {
182 if (!js_check_brand(value, BRAND_WASM_INSTANCE)) return NULL;
183 ant_value_t slot = js_get_slot(value, SLOT_DATA);
184 if (vtype(slot) != T_NUM) return NULL;
185 return (wasm_instance_handle_t *)(uintptr_t)(size_t)js_getnum(slot);
186}
187
188static wasm_extern_handle_t *wasm_extern_handle(ant_value_t value, wasm_extern_wrap_kind_t kind) {
189 if ((kind == WASM_EXTERN_WRAP_GLOBAL && !js_check_brand(value, BRAND_WASM_GLOBAL))
190 || (kind == WASM_EXTERN_WRAP_MEMORY && !js_check_brand(value, BRAND_WASM_MEMORY))
191 || (kind == WASM_EXTERN_WRAP_TABLE && !js_check_brand(value, BRAND_WASM_TABLE))) {
192 return NULL;
193 }
194
195 ant_value_t slot = js_get_slot(value, SLOT_DATA);
196 if (vtype(slot) != T_NUM) return NULL;
197 wasm_extern_handle_t *handle = (wasm_extern_handle_t *)(uintptr_t)(size_t)js_getnum(slot);
198 return handle && handle->kind == kind ? handle : NULL;
199}
200
201static ant_value_t wasm_make_error(ant_t *js, ant_value_t proto, const char *name, const char *message) {
202 ant_value_t err = js_make_error_silent(js, JS_ERR_TYPE, message ? message : "");
203 if (vtype(err) != T_OBJ) return err;
204 js_set(js, err, "name", js_mkstr(js, name, strlen(name)));
205 if (is_object_type(proto)) js_set_proto_init(err, proto);
206 return err;
207}
208
209static ant_value_t wasm_make_compile_error(ant_t *js, const char *message) {
210 return wasm_make_error(js, g_wasm_compileerror_proto, "CompileError", message);
211}
212
213static ant_value_t wasm_make_link_error(ant_t *js, const char *message) {
214 return wasm_make_error(js, g_wasm_linkerror_proto, "LinkError", message);
215}
216
217static ant_value_t wasm_make_runtime_error(ant_t *js, const char *message) {
218 return wasm_make_error(js, g_wasm_runtimeerror_proto, "RuntimeError", message);
219}
220
221static ant_value_t wasm_error_value(ant_t *js, ant_value_t value) {
222 if (is_err(value) && js->thrown_exists)
223 return js->thrown_value;
224 return value;
225}
226
227static void wasm_reject_with_error(ant_t *js, ant_value_t promise, ant_value_t error) {
228 js_reject_promise(js, promise, error);
229}
230
231static bool wasm_buffer_source_to_vec(ant_t *js, ant_value_t value, wasm_byte_vec_t *out, char *error_buf, size_t error_buf_len) {
232 const uint8_t *bytes = NULL;
233 size_t len = 0;
234 memset(out, 0, sizeof(*out));
235
236 if (!buffer_source_get_bytes(js, value, &bytes, &len)) {
237 snprintf(error_buf, error_buf_len, "Expected a BufferSource");
238 return false;
239 }
240
241 wasm_byte_vec_new_uninitialized(out, len);
242 if (len > 0 && !out->data) {
243 snprintf(error_buf, error_buf_len, "Out of memory");
244 return false;
245 }
246
247 if (len > 0) memcpy(out->data, bytes, len);
248 return true;
249}
250
251static ant_value_t wasm_value_from_i64(ant_t *js, int64_t value) {
252 char buf[64];
253 int n = snprintf(buf, sizeof(buf), "%" PRId64, value);
254 if (n < 0) return js_mkerr(js, "Failed to convert i64");
255 return js_mkbigint(js, buf, (size_t)n, value < 0);
256}
257
258static ant_value_t wasm_value_to_js(ant_t *js, const wasm_val_t *value) {
259 switch (value->kind) {
260 case WASM_I32: return js_mknum((double)value->of.i32);
261 case WASM_I64: return wasm_value_from_i64(js, value->of.i64);
262 case WASM_F32: return js_mknum((double)value->of.f32);
263 case WASM_F64: return js_mknum(value->of.f64);
264 case WASM_EXTERNREF:
265 return value->of.ref ? js_mkundef() : js_mknull();
266 case WASM_FUNCREF: {
267 wasm_func_t *func;
268 if (!value->of.ref) return js_mknull();
269 func = wasm_ref_as_func(value->of.ref);
270 if (!func) return js_mkundef();
271 return wasm_wrap_func(js, func, js_mkundef(), true);
272 }
273 default: return js_mkundef();
274 }
275}
276
277static bool js_value_to_i64(ant_t *js, ant_value_t value, int64_t *out) {
278 if (vtype(value) == T_BIGINT) {
279 ant_value_t str_val = js_tostring_val(js, value);
280 if (is_err(str_val) || vtype(str_val) != T_STR) return false;
281 const char *str = js_str(js, str_val);
282 if (!str) return false;
283 char *end = NULL;
284 long long parsed = strtoll(str, &end, 10);
285 if (!end || *end != '\0') return false;
286 *out = (int64_t)parsed;
287 return true;
288 }
289
290 double d = js_to_number(js, value);
291 if (!isfinite(d)) return false;
292 *out = (int64_t)d;
293 return true;
294}
295
296static bool js_value_to_wasm(ant_t *js, ant_value_t value, wasm_valkind_t kind, wasm_val_t *out) {
297 memset(out, 0, sizeof(*out));
298 out->kind = kind;
299
300 switch (kind) {
301 case WASM_I32:
302 out->of.i32 = (int32_t)js_to_number(js, value);
303 return true;
304 case WASM_I64:
305 return js_value_to_i64(js, value, &out->of.i64);
306 case WASM_F32:
307 out->of.f32 = (float)js_to_number(js, value);
308 return true;
309 case WASM_F64:
310 out->of.f64 = js_to_number(js, value);
311 return true;
312 case WASM_EXTERNREF:
313 out->of.ref = NULL;
314 return vtype(value) == T_NULL || vtype(value) == T_UNDEF;
315 case WASM_FUNCREF: {
316 ant_value_t state;
317 wasm_func_handle_t *handle;
318 if (vtype(value) == T_NULL || vtype(value) == T_UNDEF) {
319 out->of.ref = NULL;
320 return true;
321 }
322 if (!is_callable(value)) return false;
323 state = js_get_slot(value, SLOT_DATA);
324 if (!is_object_type(state) || !js_check_native_tag(state, WASM_FUNC_STATE_TAG))
325 return false;
326 handle = (wasm_func_handle_t *)js_get_native_ptr(state);
327 if (!handle || !handle->func) return false;
328 out->of.ref = wasm_func_as_ref(handle->func);
329 return out->of.ref != NULL;
330 }
331 default: return false;
332 }
333}
334
335static ant_value_t wasm_js_from_result_vec(ant_t *js, wasm_val_vec_t *results) {
336 if (!results || results->size == 0) return js_mkundef();
337 if (results->size == 1) return wasm_value_to_js(js, &results->data[0]);
338
339 ant_value_t arr = js_mkarr(js);
340 for (size_t i = 0; i < results->size; i++) {
341 js_arr_push(js, arr, wasm_value_to_js(js, &results->data[i]));
342 }
343 return arr;
344}
345
346static ant_value_t wasm_trap_to_error(ant_t *js, wasm_trap_t *trap) {
347 wasm_message_t message = WASM_EMPTY_VEC;
348 wasm_trap_message(trap, &message);
349
350 ant_value_t err = wasm_make_runtime_error(js, message.data ? message.data : "WebAssembly trap");
351
352 wasm_byte_vec_delete(&message);
353 wasm_trap_delete(trap);
354 return err;
355}
356
357static ant_value_t wasm_wrap_module(ant_t *js, wasm_store_t *store, wasm_module_t *module) {
358 wasm_module_handle_t *handle = calloc(1, sizeof(*handle));
359 if (!handle) {
360 if (module) wasm_module_delete(module);
361 if (store) wasm_store_delete(store);
362 return js_mkerr(js, "out of memory");
363 }
364
365 handle->store = store;
366 handle->module = module;
367
368 ant_value_t obj = js_mkobj(js);
369 js_set_proto_init(obj, g_wasm_module_proto);
370 js_set_slot(obj, SLOT_BRAND, js_mknum(BRAND_WASM_MODULE));
371 js_set_slot(obj, SLOT_DATA, ANT_PTR(handle));
372 return obj;
373}
374
375static void wasm_module_finalize(ant_t *js, ant_object_t *obj) {
376 if (!obj->extra_slots) return;
377
378 ant_extra_slot_t *entries = (ant_extra_slot_t *)obj->extra_slots;
379 for (uint8_t i = 0; i < obj->extra_count; i++) {
380 if (entries[i].slot != SLOT_DATA || vtype(entries[i].value) != T_NUM) continue;
381 wasm_module_handle_t *handle = (wasm_module_handle_t *)(uintptr_t)(size_t)js_getnum(entries[i].value);
382 if (!handle) return;
383 if (handle->module) wasm_module_delete(handle->module);
384 if (handle->store) wasm_store_delete(handle->store);
385 free(handle);
386 return;
387 }
388}
389
390static ant_value_t wasm_wrap_instance(ant_t *js, wasm_instance_handle_t *handle, ant_value_t module_ref) {
391 ant_value_t obj = js_mkobj(js);
392 js_set_proto_init(obj, g_wasm_instance_proto);
393 js_set_slot(obj, SLOT_BRAND, js_mknum(BRAND_WASM_INSTANCE));
394 js_set_slot(obj, SLOT_DATA, ANT_PTR(handle));
395 js_set_slot_wb(js, obj, SLOT_CTOR, module_ref);
396 return obj;
397}
398
399static void wasm_instance_finalize(ant_t *js, ant_object_t *obj) {
400 if (!obj->extra_slots) return;
401
402 ant_extra_slot_t *entries = (ant_extra_slot_t *)obj->extra_slots;
403 for (uint8_t i = 0; i < obj->extra_count; i++) {
404 if (entries[i].slot != SLOT_DATA || vtype(entries[i].value) != T_NUM) continue;
405 wasm_instance_handle_t *handle = (wasm_instance_handle_t *)(uintptr_t)(size_t)js_getnum(entries[i].value);
406 if (!handle) return;
407 for (size_t j = 0; j < handle->host_func_count; j++) {
408 if (handle->host_funcs[j]) wasm_func_delete(handle->host_funcs[j]);
409 }
410 free(handle->host_funcs);
411 wasm_extern_vec_delete(&handle->exports);
412 free(handle);
413 return;
414 }
415}
416
417static ant_value_t wasm_wrap_extern_object(ant_t *js, wasm_extern_wrap_kind_t kind, ant_value_t proto, int brand, wasm_store_t *store, bool own_handle, void *ptr, ant_value_t owner) {
418 wasm_extern_handle_t *handle = calloc(1, sizeof(*handle));
419 if (!handle) return js_mkerr(js, "out of memory");
420
421 handle->kind = kind;
422 handle->store = store;
423 handle->own_handle = own_handle;
424 handle->cached_value = (wasm_val_t)WASM_INIT_VAL;
425
426 switch (kind) {
427 case WASM_EXTERN_WRAP_GLOBAL: handle->as.global = (wasm_global_t *)ptr; break;
428 case WASM_EXTERN_WRAP_MEMORY: handle->as.memory = (wasm_memory_t *)ptr; break;
429 case WASM_EXTERN_WRAP_TABLE: handle->as.table = (wasm_table_t *)ptr; break;
430 }
431
432 ant_value_t obj = js_mkobj(js);
433 js_set_proto_init(obj, proto);
434 js_set_slot(obj, SLOT_BRAND, js_mknum(brand));
435 js_set_slot(obj, SLOT_DATA, ANT_PTR(handle));
436 if (is_object_type(owner)) js_set_slot_wb(js, obj, SLOT_ENTRIES, owner);
437 return obj;
438}
439
440static void wasm_extern_finalize(ant_t *js, ant_object_t *obj) {
441 if (!obj->extra_slots) return;
442
443 ant_extra_slot_t *entries = (ant_extra_slot_t *)obj->extra_slots;
444 for (uint8_t i = 0; i < obj->extra_count; i++) {
445 if (entries[i].slot != SLOT_DATA || vtype(entries[i].value) != T_NUM) continue;
446 wasm_extern_handle_t *handle = (wasm_extern_handle_t *)(uintptr_t)(size_t)js_getnum(entries[i].value);
447 if (!handle) return;
448
449 if (handle->own_handle) {
450 switch (handle->kind) {
451 case WASM_EXTERN_WRAP_GLOBAL:
452 if (handle->as.global) wasm_global_delete(handle->as.global);
453 break;
454 case WASM_EXTERN_WRAP_MEMORY:
455 if (handle->as.memory) wasm_memory_delete(handle->as.memory);
456 break;
457 case WASM_EXTERN_WRAP_TABLE:
458 if (handle->as.table) wasm_table_delete(handle->as.table);
459 break;
460 }
461 if (handle->store) wasm_store_delete(handle->store);
462 }
463
464 free(handle);
465 return;
466 }
467}
468
469static ant_value_t js_wasm_exported_func_call(ant_t *js, ant_value_t *args, int nargs) {
470 ant_value_t state = js_get_slot(js->current_func, SLOT_DATA);
471 wasm_func_handle_t *handle;
472 wasm_func_t *func;
473
474 if (!is_object_type(state) || !js_check_native_tag(state, WASM_FUNC_STATE_TAG))
475 return js_mkerr(js, "Invalid WebAssembly function");
476
477 handle = (wasm_func_handle_t *)js_get_native_ptr(state);
478 func = handle ? handle->func : NULL;
479 if (!func) return js_mkerr(js, "Invalid WebAssembly function");
480
481 wasm_functype_t *type = wasm_func_type(func);
482 if (!type) return js_mkerr(js, "Failed to inspect WebAssembly function");
483
484 const wasm_valtype_vec_t *params = wasm_functype_params(type);
485 const wasm_valtype_vec_t *results_t = wasm_functype_results(type);
486
487 wasm_val_vec_t wasm_args = WASM_EMPTY_VEC;
488 wasm_val_vec_t wasm_results = WASM_EMPTY_VEC;
489 wasm_trap_t *trap = NULL;
490 ant_value_t result = js_mkundef();
491
492 wasm_val_vec_new_uninitialized(&wasm_args, params ? params->size : 0);
493 wasm_val_vec_new_uninitialized(&wasm_results, results_t ? results_t->size : 0);
494
495 for (size_t i = 0; params && i < params->size; i++) {
496 if ((int)i >= nargs) {
497 wasm_val_vec_delete(&wasm_args);
498 wasm_val_vec_delete(&wasm_results);
499 wasm_functype_delete(type);
500 return js_mkerr_typed(js, JS_ERR_TYPE, "Missing WebAssembly argument");
501 }
502
503 if (!js_value_to_wasm(js, args[i], wasm_valtype_kind(params->data[i]), &wasm_args.data[i])) {
504 wasm_val_vec_delete(&wasm_args);
505 wasm_val_vec_delete(&wasm_results);
506 wasm_functype_delete(type);
507 return js_mkerr_typed(js, JS_ERR_TYPE, "Unsupported WebAssembly argument type");
508 }
509 }
510
511 wasm_clear_pending_import_throw();
512 trap = wasm_func_call(func, &wasm_args, &wasm_results);
513
514 if (trap) {
515 if (g_wasm_pending_import_throw_exists) {
516 result = wasm_consume_pending_import_throw();
517 js_mark_errorlike_no_stack(js, result);
518 wasm_val_vec_delete(&wasm_args);
519 wasm_val_vec_delete(&wasm_results);
520 wasm_functype_delete(type);
521 wasm_trap_delete(trap);
522 return js_throw(js, result);
523 }
524
525 result = wasm_trap_to_error(js, trap);
526 wasm_val_vec_delete(&wasm_args);
527 wasm_val_vec_delete(&wasm_results);
528 wasm_functype_delete(type);
529 return js_throw(js, result);
530 }
531
532 wasm_clear_pending_import_throw();
533 result = wasm_js_from_result_vec(js, &wasm_results);
534
535 wasm_val_vec_delete(&wasm_args);
536 wasm_val_vec_delete(&wasm_results);
537 wasm_functype_delete(type);
538
539 return result;
540}
541
542static void wasm_func_state_finalize(ant_t *js, ant_object_t *obj) {
543 if (obj->native.tag != WASM_FUNC_STATE_TAG) return;
544
545 wasm_func_handle_t *handle = (wasm_func_handle_t *)obj->native.ptr;
546 if (!handle) return;
547
548 if (handle->own_func && handle->func) wasm_func_delete(handle->func);
549 free(handle);
550 obj->native.ptr = NULL;
551 obj->native.tag = 0;
552}
553
554static ant_value_t wasm_wrap_func(ant_t *js, wasm_func_t *func, ant_value_t owner, bool own_func) {
555 wasm_func_handle_t *handle;
556 ant_value_t state;
557
558 if (!func) return js_mkundef();
559
560 handle = calloc(1, sizeof(*handle));
561 if (!handle) {
562 if (own_func) wasm_func_delete(func);
563 return js_mkerr(js, "out of memory");
564 }
565
566 handle->func = func;
567 handle->own_func = own_func;
568
569 state = js_mkobj(js);
570 js_set_native_ptr(state, handle);
571 js_set_native_tag(state, WASM_FUNC_STATE_TAG);
572
573 if (is_object_type(owner)) js_set_slot_wb(js, state, SLOT_ENTRIES, owner);
574 js_set_finalizer(state, wasm_func_state_finalize);
575
576 return js_heavy_mkfun(js, js_wasm_exported_func_call, state);
577}
578
579static ant_value_t wasm_wrap_export_value(ant_t *js, ant_value_t instance_obj, const wasm_exporttype_t *export_type, wasm_extern_t *external) {
580 switch (wasm_extern_kind(external)) {
581 case WASM_EXTERN_FUNC: {
582 return wasm_wrap_func(js, wasm_extern_as_func(external), instance_obj, false);
583 }
584 case WASM_EXTERN_GLOBAL: {
585 ant_value_t obj = wasm_wrap_extern_object(
586 js, WASM_EXTERN_WRAP_GLOBAL, g_wasm_global_proto, BRAND_WASM_GLOBAL,
587 NULL, false, wasm_extern_as_global(external), instance_obj
588 );
589 if (vtype(obj) == T_OBJ) js_set_finalizer(obj, wasm_extern_finalize);
590 return obj;
591 }
592 case WASM_EXTERN_MEMORY: {
593 ant_value_t obj = wasm_wrap_extern_object(
594 js, WASM_EXTERN_WRAP_MEMORY, g_wasm_memory_proto, BRAND_WASM_MEMORY,
595 NULL, false, wasm_extern_as_memory(external), instance_obj
596 );
597 if (vtype(obj) == T_OBJ) js_set_finalizer(obj, wasm_extern_finalize);
598 return obj;
599 }
600 case WASM_EXTERN_TABLE: {
601 ant_value_t obj = wasm_wrap_extern_object(
602 js, WASM_EXTERN_WRAP_TABLE, g_wasm_table_proto, BRAND_WASM_TABLE,
603 NULL, false, wasm_extern_as_table(external), instance_obj
604 );
605 if (vtype(obj) == T_OBJ) js_set_finalizer(obj, wasm_extern_finalize);
606 return obj;
607 }
608 default:
609 (void)export_type;
610 return js_mkundef();
611 }
612}
613
614static ant_value_t wasm_module_from_bytes(ant_t *js, ant_value_t value, ant_value_t *out_module) {
615 wasm_byte_vec_t binary = WASM_EMPTY_VEC;
616 wasm_store_t *store = NULL;
617 wasm_module_t *module = NULL;
618
619 char error_buf[128] = {0};
620 bool suppress_wasi_warning = false;
621
622 *out_module = js_mkundef();
623
624 if (!ensure_wasm_engine()) return js_mkerr(js, "Failed to initialize WebAssembly engine");
625 if (!(store = wasm_store_new(g_wasm_engine))) return js_mkerr(js, "Failed to create WebAssembly store");
626
627 if (!wasm_buffer_source_to_vec(js, value, &binary, error_buf, sizeof(error_buf))) {
628 wasm_store_delete(store);
629 return js_mkerr_typed(js, JS_ERR_TYPE, "%s", error_buf);
630 }
631
632 suppress_wasi_warning = wasi_bytes_need_wasi_command_warning_suppression(
633 (const uint8_t *)binary.data, binary.size
634 );
635
636 if (suppress_wasi_warning) wasm_runtime_set_log_level(WASM_LOG_LEVEL_ERROR);
637 module = wasm_module_new(store, &binary);
638
639 if (suppress_wasi_warning) wasm_runtime_set_log_level(WASM_LOG_LEVEL_WARNING);
640 wasm_byte_vec_delete(&binary);
641
642 if (!module) {
643 wasm_store_delete(store);
644 return wasm_make_compile_error(js, "Failed to compile WebAssembly module");
645 }
646
647 *out_module = wasm_wrap_module(js, store, module);
648 if (vtype(*out_module) == T_OBJ) {
649 js_set_finalizer(*out_module, wasm_module_finalize);
650 js_set_slot_wb(js, *out_module, SLOT_MAP, value);
651 }
652 return js_mkundef();
653}
654
655static ant_value_t js_wasm_module_ctor(ant_t *js, ant_value_t *args, int nargs) {
656 ant_value_t module = js_mkundef();
657 ant_value_t err;
658
659 if (vtype(js->new_target) == T_UNDEF)
660 return js_mkerr_typed(js, JS_ERR_TYPE, "WebAssembly.Module constructor requires 'new'");
661 if (nargs < 1)
662 return js_mkerr_typed(js, JS_ERR_TYPE, "WebAssembly.Module requires a BufferSource");
663
664 err = wasm_module_from_bytes(js, args[0], &module);
665 if (is_err(err)) return err;
666 if (vtype(module) != T_OBJ) return js_throw(js, wasm_error_value(js, err));
667 return module;
668}
669
670static ant_value_t wasm_module_type_descriptors(ant_t *js, ant_value_t module_obj, bool imports) {
671 wasm_module_handle_t *handle = wasm_module_handle(module_obj);
672 if (!handle) return js_mkerr_typed(js, JS_ERR_TYPE, "Expected a WebAssembly.Module");
673
674 ant_value_t arr = js_mkarr(js);
675 if (imports) {
676 wasm_importtype_vec_t vec = WASM_EMPTY_VEC;
677 wasm_module_imports(handle->module, &vec);
678 for (size_t i = 0; i < vec.size; i++) {
679 const wasm_importtype_t *entry = vec.data[i];
680 const wasm_name_t *module_name = wasm_importtype_module(entry);
681 const wasm_name_t *name = wasm_importtype_name(entry);
682 const wasm_externtype_t *type = wasm_importtype_type(entry);
683
684 ant_value_t item = js_mkobj(js);
685 js_set(js, item, "module", js_mkstr(js, module_name->data, wasm_name_len(module_name)));
686 js_set(js, item, "name", js_mkstr(js, name->data, wasm_name_len(name)));
687 js_set(js, item, "kind", js_mkstr(js, wasm_extern_kind_name(wasm_externtype_kind(type)), strlen(wasm_extern_kind_name(wasm_externtype_kind(type)))));
688 js_arr_push(js, arr, item);
689 }
690 wasm_importtype_vec_delete(&vec);
691 } else {
692 wasm_exporttype_vec_t vec = WASM_EMPTY_VEC;
693 wasm_module_exports(handle->module, &vec);
694 for (size_t i = 0; i < vec.size; i++) {
695 const wasm_exporttype_t *entry = vec.data[i];
696 const wasm_name_t *name = wasm_exporttype_name(entry);
697 const wasm_externtype_t *type = wasm_exporttype_type(entry);
698
699 ant_value_t item = js_mkobj(js);
700 js_set(js, item, "name", js_mkstr(js, name->data, wasm_name_len(name)));
701 js_set(js, item, "kind", js_mkstr(js, wasm_extern_kind_name(wasm_externtype_kind(type)), strlen(wasm_extern_kind_name(wasm_externtype_kind(type)))));
702 js_arr_push(js, arr, item);
703 }
704 wasm_exporttype_vec_delete(&vec);
705 }
706
707 return arr;
708}
709
710static ant_value_t js_wasm_module_imports(ant_t *js, ant_value_t *args, int nargs) {
711 if (nargs < 1) return js_mkerr_typed(js, JS_ERR_TYPE, "WebAssembly.Module.imports requires 1 argument");
712 return wasm_module_type_descriptors(js, args[0], true);
713}
714
715static ant_value_t js_wasm_module_exports(ant_t *js, ant_value_t *args, int nargs) {
716 if (nargs < 1) return js_mkerr_typed(js, JS_ERR_TYPE, "WebAssembly.Module.exports requires 1 argument");
717 return wasm_module_type_descriptors(js, args[0], false);
718}
719
720static ant_value_t js_wasm_instance_exports_getter(ant_t *js, ant_value_t *args, int nargs) {
721 if (!js_check_brand(js->this_val, BRAND_WASM_INSTANCE)) return js_mkerr_typed(js, JS_ERR_TYPE, "Expected a WebAssembly.Instance");
722 return js_get_slot(js->this_val, SLOT_ENTRIES);
723}
724
725static ant_value_t wasm_property_get_nested(ant_t *js, ant_value_t base, const wasm_name_t *name) {
726 if (!is_object_type(base)) return js_mkundef();
727 return js_getprop_fallback(js, base, name && name->data ? name->data : "");
728}
729
730static wasm_trap_t *wasm_import_func_callback(void *env_ptr, const wasm_val_vec_t *args, wasm_val_vec_t *results) {
731 wasm_import_func_env_t *env = (wasm_import_func_env_t *)env_ptr;
732 ant_t *js = env->js;
733
734 ant_value_t *js_args = NULL;
735 ant_value_t result = js_mkundef();
736 wasm_message_t trap_msg = WASM_EMPTY_VEC;
737
738 if (args && args->size > 0) {
739 js_args = calloc(args->size, sizeof(*js_args));
740 if (!js_args) {
741 wasm_name_new_from_string_nt(&trap_msg, "Out of memory");
742 wasm_trap_t *trap = wasm_trap_new(env->store, &trap_msg);
743 wasm_byte_vec_delete(&trap_msg);
744 return trap;
745 }
746 }
747
748 for (size_t i = 0; args && i < args->size; i++) {
749 js_args[i] = wasm_value_to_js(js, &args->data[i]);
750 }
751
752 result = sv_vm_call(
753 js->vm, js, env->fn, js_mkundef(),
754 js_args, args ? (int)args->size : 0, NULL, false
755 );
756 free(js_args);
757
758 if (is_err(result)) {
759 ant_value_t thrown = js->thrown_exists ? js->thrown_value : result;
760 wasm_set_pending_import_throw(thrown);
761
762 const char *msg = "WebAssembly import threw";
763 if (vtype(js->thrown_value) == T_OBJ) {
764 const char *message = get_str_prop(js, js->thrown_value, "message", 7, NULL);
765 if (message && *message) msg = message;
766 }
767
768 wasm_name_new_from_string_nt(&trap_msg, msg);
769 wasm_trap_t *trap = wasm_trap_new(env->store, &trap_msg);
770 wasm_byte_vec_delete(&trap_msg);
771
772 return trap;
773 }
774
775 if (results && results->size > 0) {
776 if (results->size == 1) {
777 if (!js_value_to_wasm(js, result, results->data[0].kind, &results->data[0])) {
778 wasm_name_new_from_string_nt(&trap_msg, "Unsupported import return value");
779 wasm_trap_t *trap = wasm_trap_new(env->store, &trap_msg);
780 wasm_byte_vec_delete(&trap_msg);
781 return trap;
782 }
783 } else {
784 if (vtype(result) != T_ARR) {
785 wasm_name_new_from_string_nt(&trap_msg, "Expected an array for multi-value return");
786 wasm_trap_t *trap = wasm_trap_new(env->store, &trap_msg);
787 wasm_byte_vec_delete(&trap_msg);
788 return trap;
789 }
790
791 for (size_t i = 0; i < results->size; i++) {
792 ant_value_t item = js_arr_get(js, result, (ant_offset_t)i);
793 if (!js_value_to_wasm(js, item, results->data[i].kind, &results->data[i])) {
794 wasm_name_new_from_string_nt(&trap_msg, "Unsupported import return value");
795 wasm_trap_t *trap = wasm_trap_new(env->store, &trap_msg);
796 wasm_byte_vec_delete(&trap_msg);
797 return trap;
798 }
799 }
800 }}
801
802 return NULL;
803}
804
805static ant_value_t wasm_instantiate_module(ant_t *js, ant_value_t module_obj, ant_value_t import_obj, ant_value_t *out_instance) {
806 wasm_module_handle_t *module_handle = wasm_module_handle(module_obj);
807 wasm_importtype_vec_t import_types = WASM_EMPTY_VEC;
808 wasm_exporttype_vec_t export_types = WASM_EMPTY_VEC;
809
810 wasm_extern_t **imports = NULL;
811 wasm_func_t **owned_host_funcs = NULL;
812
813 size_t owned_host_func_count = 0;
814 wasm_extern_vec_t exports = WASM_EMPTY_VEC;
815
816 wasm_trap_t *trap = NULL;
817 wasm_instance_t *instance = NULL;
818 ant_value_t instance_obj = js_mkundef();
819 ant_value_t exports_obj = js_mkobj(js);
820 *out_instance = js_mkundef();
821
822 if (!module_handle)
823 return js_mkerr_typed(js, JS_ERR_TYPE, "Expected a WebAssembly.Module");
824
825 if (wasi_module_has_wasi_imports(module_handle->module)
826 && wasi_module_is_command_or_reactor(module_handle->module)) {
827 ant_value_t wasi_opts = is_object_type(import_obj) ? js_get(js, import_obj, "wasi") : js_mkundef();
828 if (!is_object_type(import_obj) || is_object_type(wasi_opts)) {
829 ant_value_t bytes_src = js_get_slot(module_obj, SLOT_MAP);
830 wasm_byte_vec_t binary = WASM_EMPTY_VEC;
831 char wasi_err[128] = {0};
832 if (!wasm_buffer_source_to_vec(js, bytes_src, &binary, wasi_err, sizeof(wasi_err))) {
833 return js_mkerr(js, "WASI: cannot extract module bytes");
834 }
835 *out_instance = wasi_instantiate(js, (const uint8_t *)binary.data, binary.size, module_obj, wasi_opts);
836 wasm_byte_vec_delete(&binary);
837 return is_err(*out_instance) ? *out_instance : js_mkundef();
838 }
839 }
840
841 wasm_module_imports(module_handle->module, &import_types);
842 if (import_types.size > 0) {
843 imports = calloc(import_types.size, sizeof(*imports));
844 owned_host_funcs = calloc(import_types.size, sizeof(*owned_host_funcs));
845 if (!imports || !owned_host_funcs) {
846 free(imports);
847 free(owned_host_funcs);
848 wasm_importtype_vec_delete(&import_types);
849 return js_mkerr(js, "out of memory");
850 }
851 }
852
853 for (size_t i = 0; i < import_types.size; i++) {
854 const wasm_importtype_t *import_type = import_types.data[i];
855 const wasm_name_t *module_name = wasm_importtype_module(import_type);
856 const wasm_name_t *field_name = wasm_importtype_name(import_type);
857 const wasm_externtype_t *extern_type = wasm_importtype_type(import_type);
858
859 ant_value_t namespace_obj = wasm_property_get_nested(js, import_obj, module_name);
860 ant_value_t value = wasm_property_get_nested(js, namespace_obj, field_name);
861 wasm_externkind_t kind = wasm_externtype_kind(extern_type);
862
863 if (kind == WASM_EXTERN_MEMORY || kind == WASM_EXTERN_TABLE) {
864 free(imports);
865 free(owned_host_funcs);
866 wasm_importtype_vec_delete(&import_types);
867 return wasm_make_link_error(js, "The current WAMR backend does not support memory/table imports");
868 }
869
870 if (kind == WASM_EXTERN_FUNC) {
871 const wasm_functype_t *func_type = wasm_externtype_as_functype_const(extern_type);
872 wasm_import_func_env_t *env = NULL;
873
874 if (!is_callable(value)) {
875 free(imports);
876 free(owned_host_funcs);
877 wasm_importtype_vec_delete(&import_types);
878 return wasm_make_link_error(js, "Missing function import");
879 }
880
881 env = calloc(1, sizeof(*env));
882 if (!env) {
883 free(imports);
884 free(owned_host_funcs);
885 wasm_importtype_vec_delete(&import_types);
886 return js_mkerr(js, "out of memory");
887 }
888
889 env->js = js;
890 env->store = module_handle->store;
891 env->owner = module_obj;
892 env->fn = value;
893 wasm_register_import_env(env);
894
895
896 owned_host_funcs[owned_host_func_count] = wasm_func_new_with_env(
897 module_handle->store,
898 func_type,
899 wasm_import_func_callback,
900 env,
901 wasm_import_func_env_finalizer
902 );
903
904 if (!owned_host_funcs[owned_host_func_count]) {
905 free(env);
906 free(imports);
907 free(owned_host_funcs);
908 wasm_importtype_vec_delete(&import_types);
909 return wasm_make_link_error(js, "Failed to create function import");
910 }
911
912 imports[i] = wasm_func_as_extern(owned_host_funcs[owned_host_func_count]);
913 owned_host_func_count++;
914 continue;
915 }
916
917 if (kind == WASM_EXTERN_GLOBAL) {
918 wasm_extern_handle_t *handle = wasm_extern_handle(value, WASM_EXTERN_WRAP_GLOBAL);
919 if (!handle || !handle->as.global) {
920 free(imports);
921 free(owned_host_funcs);
922 wasm_importtype_vec_delete(&import_types);
923 return wasm_make_link_error(js, "Missing global import");
924 }
925 imports[i] = wasm_global_as_extern(handle->as.global);
926 continue;
927 }
928 }
929
930 {
931 wasm_extern_vec_t import_vec = WASM_EMPTY_VEC;
932 if (imports && import_types.size > 0)
933 import_vec = (wasm_extern_vec_t){ import_types.size, imports, import_types.size, sizeof(*imports), NULL };
934
935 wasm_clear_pending_import_throw();
936 instance = wasm_instance_new_with_args(module_handle->store, module_handle->module, &import_vec, &trap, KILOBYTE(32), 0);
937 }
938
939 free(imports);
940 wasm_importtype_vec_delete(&import_types);
941
942 if (!instance) {
943 for (size_t i = 0; i < owned_host_func_count; i++) {
944 if (owned_host_funcs[i]) wasm_func_delete(owned_host_funcs[i]);
945 }
946 free(owned_host_funcs);
947
948 if (trap) {
949 if (g_wasm_pending_import_throw_exists) {
950 ant_value_t thrown = wasm_consume_pending_import_throw();
951 js_mark_errorlike_no_stack(js, thrown);
952 wasm_trap_delete(trap);
953 return js_throw(js, thrown);
954 }
955 return wasm_trap_to_error(js, trap);
956 }
957
958 return wasm_make_link_error(js, "Failed to instantiate WebAssembly module");
959 }
960
961 wasm_clear_pending_import_throw();
962 wasm_instance_exports(instance, &exports);
963 wasm_module_exports(module_handle->module, &export_types);
964
965 {
966 wasm_instance_handle_t *inst_handle = calloc(1, sizeof(*inst_handle));
967 if (!inst_handle) {
968 wasm_extern_vec_delete(&exports);
969 wasm_exporttype_vec_delete(&export_types);
970 return js_mkerr(js, "out of memory");
971 }
972 inst_handle->instance = instance;
973 inst_handle->exports = exports;
974 inst_handle->host_funcs = owned_host_funcs;
975 inst_handle->host_func_count = owned_host_func_count;
976
977 instance_obj = wasm_wrap_instance(js, inst_handle, module_obj);
978 }
979 if (vtype(instance_obj) != T_OBJ) {
980 wasm_exporttype_vec_delete(&export_types);
981 return instance_obj;
982 }
983 js_set_finalizer(instance_obj, wasm_instance_finalize);
984
985 for (size_t i = 0; i < export_types.size && i < exports.size; i++) {
986 const wasm_exporttype_t *export_type = export_types.data[i];
987 const wasm_name_t *name = wasm_exporttype_name(export_type);
988 ant_value_t export_value = wasm_wrap_export_value(js, instance_obj, export_type, exports.data[i]);
989 js_setprop(js, exports_obj, js_mkstr(js, name->data, wasm_name_len(name)), export_value);
990 }
991
992 wasm_exporttype_vec_delete(&export_types);
993 js_set_slot_wb(js, instance_obj, SLOT_ENTRIES, exports_obj);
994 if (is_object_type(import_obj)) js_set_slot_wb(js, instance_obj, SLOT_MAP, import_obj);
995
996 *out_instance = instance_obj;
997 return js_mkundef();
998}
999
1000static ant_value_t js_wasm_instance_ctor(ant_t *js, ant_value_t *args, int nargs) {
1001 ant_value_t instance = js_mkundef();
1002 ant_value_t import_obj = (nargs >= 2 && is_object_type(args[1])) ? args[1] : js_mkundef();
1003 ant_value_t err;
1004
1005 if (vtype(js->new_target) == T_UNDEF) return js_mkerr_typed(js, JS_ERR_TYPE, "WebAssembly.Instance constructor requires 'new'");
1006 if (nargs < 1) return js_mkerr_typed(js, JS_ERR_TYPE, "WebAssembly.Instance requires a module");
1007
1008 err = wasm_instantiate_module(js, args[0], import_obj, &instance);
1009 if (is_err(err)) return err;
1010 if (vtype(instance) != T_OBJ) return js_throw(js, wasm_error_value(js, err));
1011
1012 return instance;
1013}
1014
1015static ant_value_t js_wasm_global_value_getter(ant_t *js, ant_value_t *args, int nargs) {
1016 wasm_extern_handle_t *handle = wasm_extern_handle(js->this_val, WASM_EXTERN_WRAP_GLOBAL);
1017 wasm_val_t value = WASM_INIT_VAL;
1018
1019 if (!handle || !handle->as.global) return js_mkerr_typed(js, JS_ERR_TYPE, "Expected a WebAssembly.Global");
1020 if (handle->use_cached_value) return wasm_value_to_js(js, &handle->cached_value);
1021
1022 wasm_global_get(handle->as.global, &value);
1023 return wasm_value_to_js(js, &value);
1024}
1025
1026static ant_value_t js_wasm_global_value_setter(ant_t *js, ant_value_t *args, int nargs) {
1027 wasm_extern_handle_t *handle = wasm_extern_handle(js->this_val, WASM_EXTERN_WRAP_GLOBAL);
1028 wasm_globaltype_t *type = NULL;
1029
1030 const wasm_valtype_t *content = NULL;
1031 wasm_val_t value = WASM_INIT_VAL;
1032
1033 if (!handle || !handle->as.global)
1034 return js_mkerr_typed(js, JS_ERR_TYPE, "Expected a WebAssembly.Global");
1035 if (nargs < 1) return js_mkundef();
1036
1037 type = wasm_global_type(handle->as.global);
1038 if (!type) return js_mkerr(js, "Failed to inspect WebAssembly.Global");
1039 if (wasm_globaltype_mutability(type) != WASM_VAR) {
1040 wasm_globaltype_delete(type);
1041 return js_mkerr_typed(js, JS_ERR_TYPE, "WebAssembly.Global is immutable");
1042 }
1043
1044 content = wasm_globaltype_content(type);
1045 if (!js_value_to_wasm(js, args[0], wasm_valtype_kind(content), &value)) {
1046 wasm_globaltype_delete(type);
1047 return js_mkerr_typed(js, JS_ERR_TYPE, "Unsupported global value");
1048 }
1049
1050 if (handle->use_cached_value) handle->cached_value = value;
1051 else wasm_global_set(handle->as.global, &value);
1052 wasm_globaltype_delete(type);
1053
1054 return js_mkundef();
1055}
1056
1057static ant_value_t js_wasm_global_value_of(ant_t *js, ant_value_t *args, int nargs) {
1058 return js_wasm_global_value_getter(js, NULL, 0);
1059}
1060
1061static ant_value_t js_wasm_global_ctor(ant_t *js, ant_value_t *args, int nargs) {
1062 ant_value_t descriptor;
1063 ant_value_t mutable_val;
1064
1065 const char *value_type;
1066 ant_offset_t value_type_len = 0;
1067 bool ok = false;
1068
1069 wasm_valkind_t kind;
1070 wasm_store_t *store = NULL;
1071 wasm_valtype_t *valtype = NULL;
1072 wasm_globaltype_t *globaltype = NULL;
1073 wasm_global_t *global = NULL;
1074 wasm_val_t initial = WASM_INIT_VAL;
1075 ant_value_t result;
1076
1077 if (vtype(js->new_target) == T_UNDEF)
1078 return js_mkerr_typed(js, JS_ERR_TYPE, "WebAssembly.Global constructor requires 'new'");
1079 if (nargs < 1 || !is_object_type(args[0]))
1080 return js_mkerr_typed(js, JS_ERR_TYPE, "WebAssembly.Global requires a descriptor object");
1081 if (!ensure_wasm_engine())
1082 return js_mkerr(js, "Failed to initialize WebAssembly engine");
1083
1084 descriptor = args[0];
1085 value_type = get_str_prop(js, descriptor, "value", 5, &value_type_len);
1086 if (!value_type)
1087 return js_mkerr_typed(js, JS_ERR_TYPE, "WebAssembly.Global descriptor requires a value type");
1088
1089 kind = wasm_valkind_from_string(value_type, value_type_len, &ok);
1090 if (!ok)
1091 return js_mkerr_typed(js, JS_ERR_TYPE, "Unsupported WebAssembly.Global value type");
1092
1093 if (!js_value_to_wasm(js, nargs >= 2 ? args[1] : js_mknum(0), kind, &initial))
1094 return js_mkerr_typed(js, JS_ERR_TYPE, "Unsupported WebAssembly.Global initial value");
1095
1096 mutable_val = js_get(js, descriptor, "mutable");
1097 store = wasm_store_new(g_wasm_engine);
1098 if (!store) return js_mkerr(js, "Failed to create WebAssembly store");
1099
1100 valtype = wasm_valtype_new(kind);
1101 globaltype = wasm_globaltype_new(valtype, js_truthy(js, mutable_val) ? WASM_VAR : WASM_CONST);
1102 global = globaltype ? wasm_global_new(store, globaltype, &initial) : NULL;
1103
1104 wasm_globaltype_delete(globaltype);
1105
1106 if (!global) {
1107 wasm_store_delete(store);
1108 return js_throw(js, wasm_make_runtime_error(js, "Failed to create WebAssembly.Global"));
1109 }
1110
1111 result = wasm_wrap_extern_object(
1112 js, WASM_EXTERN_WRAP_GLOBAL, g_wasm_global_proto, BRAND_WASM_GLOBAL,
1113 store, true, global, js_mkundef()
1114 );
1115 if (vtype(result) == T_OBJ) {
1116 wasm_extern_handle_t *handle = wasm_extern_handle(result, WASM_EXTERN_WRAP_GLOBAL);
1117 if (handle) {
1118 handle->use_cached_value = true;
1119 handle->cached_value = initial;
1120 }
1121 js_set_finalizer(result, wasm_extern_finalize);
1122 }
1123 return result;
1124}
1125
1126static ant_value_t js_wasm_memory_buffer_getter(ant_t *js, ant_value_t *args, int nargs) {
1127 wasm_extern_handle_t *handle = wasm_extern_handle(js->this_val, WASM_EXTERN_WRAP_MEMORY);
1128 byte_t *data; size_t len; ArrayBufferData *buffer;
1129
1130 if (!handle || !handle->as.memory)
1131 return js_mkerr_typed(js, JS_ERR_TYPE, "Expected a WebAssembly.Memory");
1132
1133 data = wasm_memory_data(handle->as.memory);
1134 len = wasm_memory_data_size(handle->as.memory);
1135 buffer = calloc(1, sizeof(ArrayBufferData));
1136
1137 if (!buffer) return js_mkerr(js, "out of memory");
1138 buffer->data = (uint8_t *)data;
1139 buffer->length = len;
1140 buffer->capacity = len;
1141 buffer->ref_count = 1;
1142
1143 return create_arraybuffer_obj(js, buffer);
1144}
1145
1146static ant_value_t js_wasm_memory_grow(ant_t *js, ant_value_t *args, int nargs) {
1147 wasm_extern_handle_t *handle = wasm_extern_handle(js->this_val, WASM_EXTERN_WRAP_MEMORY);
1148 wasm_memory_pages_t old_size;
1149 uint32_t delta;
1150
1151 if (!handle || !handle->as.memory)
1152 return js_mkerr_typed(js, JS_ERR_TYPE, "Expected a WebAssembly.Memory");
1153
1154 delta = (uint32_t)(nargs > 0 ? js_to_number(js, args[0]) : 0);
1155 old_size = wasm_memory_size(handle->as.memory);
1156
1157 if (delta == 0) return js_mknum((double)old_size);
1158
1159 wasm_module_inst_t inst = (wasm_module_inst_t)handle->as.memory->inst_comm_rt;
1160 if (!inst)
1161 return js_mkerr_typed(js, JS_ERR_RANGE, "Memory instance not available");
1162
1163 if (inst->module_type == Wasm_Module_Bytecode) {
1164 WASMModuleInstance *wasm_inst = (WASMModuleInstance *)inst;
1165 WASMMemoryInstance *mem_inst = wasm_inst->memories[handle->as.memory->memory_idx_rt];
1166 uint32_t needed = mem_inst->cur_page_count + delta;
1167 if (needed > mem_inst->max_page_count) mem_inst->max_page_count = needed;
1168 }
1169
1170 if (!wasm_runtime_enlarge_memory(inst, (uint64_t)delta))
1171 return js_mkerr_typed(js, JS_ERR_RANGE, "Failed to grow memory by %u pages", delta);
1172
1173 return js_mknum((double)old_size);
1174}
1175
1176static ant_value_t js_wasm_memory_ctor(ant_t *js, ant_value_t *args, int nargs) {
1177 if (vtype(js->new_target) == T_UNDEF) return js_mkerr_typed(js, JS_ERR_TYPE, "WebAssembly.Memory constructor requires 'new'");
1178 return js_mkerr_typed(js, JS_ERR_TYPE, "The current WAMR backend does not support standalone WebAssembly.Memory");
1179}
1180
1181static ant_value_t js_wasm_table_length_getter(ant_t *js, ant_value_t *args, int nargs) {
1182 wasm_extern_handle_t *handle = wasm_extern_handle(js->this_val, WASM_EXTERN_WRAP_TABLE);
1183 if (!handle || !handle->as.table) return js_mkerr_typed(js, JS_ERR_TYPE, "Expected a WebAssembly.Table");
1184 return js_mknum((double)wasm_table_size(handle->as.table));
1185}
1186
1187static ant_value_t js_wasm_table_get(ant_t *js, ant_value_t *args, int nargs) {
1188 wasm_extern_handle_t *handle = wasm_extern_handle(js->this_val, WASM_EXTERN_WRAP_TABLE);
1189 wasm_ref_t *ref;
1190 wasm_func_t *func;
1191 uint32_t index;
1192
1193 if (!handle || !handle->as.table)
1194 return js_mkerr_typed(js, JS_ERR_TYPE, "Expected a WebAssembly.Table");
1195
1196 index = (uint32_t)(nargs > 0 ? js_to_number(js, args[0]) : 0);
1197 ref = wasm_table_get(handle->as.table, index);
1198
1199 if (!ref) return js_mknull();
1200 func = wasm_ref_as_func(ref);
1201
1202 if (func) {
1203 ant_value_t owner = js_get_slot(js->this_val, SLOT_ENTRIES);
1204 ant_value_t wrapped = wasm_wrap_func(js, func, owner, true);
1205 wasm_ref_delete(ref);
1206 return wrapped;
1207 }
1208
1209 wasm_ref_delete(ref);
1210 return js_mknull();
1211}
1212
1213static ant_value_t js_wasm_table_set(ant_t *js, ant_value_t *args, int nargs) {
1214 wasm_extern_handle_t *handle = wasm_extern_handle(js->this_val, WASM_EXTERN_WRAP_TABLE);
1215 wasm_ref_t *ref = NULL;
1216 uint32_t index;
1217
1218 if (!handle || !handle->as.table) return js_mkerr_typed(js, JS_ERR_TYPE, "Expected a WebAssembly.Table");
1219 if (nargs < 2) return js_mkerr_typed(js, JS_ERR_TYPE, "WebAssembly.Table.set requires 2 arguments");
1220
1221 index = (uint32_t)js_to_number(js, args[0]);
1222 if (!(vtype(args[1]) == T_NULL || vtype(args[1]) == T_UNDEF)) {
1223 ant_value_t state;
1224 wasm_func_handle_t *func_handle;
1225
1226 if (!is_callable(args[1]))
1227 return js_mkerr_typed(js, JS_ERR_TYPE, "WebAssembly.Table.set expects a WebAssembly function or null");
1228
1229 state = js_get_slot(args[1], SLOT_DATA);
1230 if (!is_object_type(state) || !js_check_native_tag(state, WASM_FUNC_STATE_TAG))
1231 return js_mkerr_typed(js, JS_ERR_TYPE, "WebAssembly.Table.set expects a WebAssembly function or null");
1232
1233 func_handle = (wasm_func_handle_t *)js_get_native_ptr(state);
1234 if (!func_handle || !func_handle->func)
1235 return js_mkerr_typed(js, JS_ERR_TYPE, "WebAssembly.Table.set expects a WebAssembly function or null");
1236
1237 ref = wasm_func_as_ref(func_handle->func);
1238 if (!ref) return js_mkerr_typed(js, JS_ERR_TYPE, "Failed to materialize WebAssembly function reference");
1239 }
1240
1241 if (!wasm_table_set(handle->as.table, index, ref)) {
1242 if (ref) wasm_ref_delete(ref);
1243 return js_mkerr_typed(js, JS_ERR_TYPE, "Failed to update WebAssembly.Table");
1244 }
1245
1246 return js_mkundef();
1247}
1248
1249static ant_value_t js_wasm_table_grow(ant_t *js, ant_value_t *args, int nargs) {
1250 return js_mkerr_typed(js, JS_ERR_TYPE, "The current WAMR backend does not support host-side table.grow");
1251}
1252
1253static ant_value_t js_wasm_table_ctor(ant_t *js, ant_value_t *args, int nargs) {
1254 if (vtype(js->new_target) == T_UNDEF) return js_mkerr_typed(js, JS_ERR_TYPE, "WebAssembly.Table constructor requires 'new'");
1255 return js_mkerr_typed(js, JS_ERR_TYPE, "The current WAMR backend does not support standalone WebAssembly.Table");
1256}
1257
1258static ant_value_t js_wasm_tag_ctor(ant_t *js, ant_value_t *args, int nargs) {
1259 return js_mkerr_typed(js, JS_ERR_TYPE, "The current WAMR backend does not expose WebAssembly.Tag");
1260}
1261
1262static ant_value_t js_wasm_exception_ctor(ant_t *js, ant_value_t *args, int nargs) {
1263 return js_mkerr_typed(js, JS_ERR_TYPE, "The current WAMR backend does not expose WebAssembly.Exception");
1264}
1265
1266static ant_value_t js_wasm_validate(ant_t *js, ant_value_t *args, int nargs) {
1267 wasm_byte_vec_t binary = WASM_EMPTY_VEC;
1268 wasm_store_t *store;
1269
1270 bool ok;
1271 char error_buf[128] = {0};
1272 bool suppress_wasi_warning = false;
1273
1274 if (nargs < 1) return js_false;
1275 if (!ensure_wasm_engine()) return js_false;
1276 if (!(store = wasm_store_new(g_wasm_engine))) return js_false;
1277
1278 if (!wasm_buffer_source_to_vec(js, args[0], &binary, error_buf, sizeof(error_buf))) {
1279 wasm_store_delete(store);
1280 return js_false;
1281 }
1282
1283 suppress_wasi_warning = wasi_bytes_need_wasi_command_warning_suppression(
1284 (const uint8_t *)binary.data, binary.size
1285 );
1286
1287 if (suppress_wasi_warning) wasm_runtime_set_log_level(WASM_LOG_LEVEL_ERROR);
1288 ok = wasm_module_validate(store, &binary);
1289
1290 if (suppress_wasi_warning) wasm_runtime_set_log_level(WASM_LOG_LEVEL_WARNING);
1291 wasm_byte_vec_delete(&binary);
1292 wasm_store_delete(store);
1293
1294 return js_bool(ok);
1295}
1296
1297static ant_value_t js_wasm_compile(ant_t *js, ant_value_t *args, int nargs) {
1298 ant_value_t promise = js_mkpromise(js);
1299 ant_value_t module = js_mkundef();
1300 ant_value_t err;
1301
1302 if (nargs < 1) {
1303 wasm_reject_with_error(js, promise, js_mkerr_typed(js, JS_ERR_TYPE, "WebAssembly.compile requires 1 argument"));
1304 return promise;
1305 }
1306
1307 err = wasm_module_from_bytes(js, args[0], &module);
1308 if (is_err(err) || vtype(module) != T_OBJ) {
1309 wasm_reject_with_error(js, promise, wasm_error_value(js, err));
1310 return promise;
1311 }
1312
1313 js_resolve_promise(js, promise, module);
1314 return promise;
1315}
1316
1317static ant_value_t js_wasm_instantiate(ant_t *js, ant_value_t *args, int nargs) {
1318 ant_value_t promise = js_mkpromise(js);
1319 ant_value_t module = js_mkundef();
1320 ant_value_t instance = js_mkundef();
1321 ant_value_t import_obj = (nargs >= 2 && is_object_type(args[1])) ? args[1] : js_mkundef();
1322 ant_value_t err;
1323
1324 if (nargs < 1) {
1325 wasm_reject_with_error(js, promise, js_mkerr_typed(js, JS_ERR_TYPE, "WebAssembly.instantiate requires 1 argument"));
1326 return promise;
1327 }
1328
1329 if (wasm_module_handle(args[0])) {
1330 err = wasm_instantiate_module(js, args[0], import_obj, &instance);
1331 if (is_err(err) || vtype(instance) != T_OBJ) {
1332 wasm_reject_with_error(js, promise, wasm_error_value(js, err));
1333 return promise;
1334 }
1335 js_resolve_promise(js, promise, instance);
1336 return promise;
1337 }
1338
1339 err = wasm_module_from_bytes(js, args[0], &module);
1340 if (is_err(err) || vtype(module) != T_OBJ) {
1341 wasm_reject_with_error(js, promise, wasm_error_value(js, err));
1342 return promise;
1343 }
1344
1345 err = wasm_instantiate_module(js, module, import_obj, &instance);
1346 if (is_err(err) || vtype(instance) != T_OBJ) {
1347 wasm_reject_with_error(js, promise, wasm_error_value(js, err));
1348 return promise;
1349 }
1350
1351 ant_value_t result = js_mkobj(js);
1352 js_set(js, result, "module", module);
1353 js_set(js, result, "instance", instance);
1354 js_resolve_promise(js, promise, result);
1355
1356 return promise;
1357}
1358
1359static ant_value_t js_wasm_compile_error_ctor(ant_t *js, ant_value_t *args, int nargs) {
1360 ant_value_t msg = (nargs > 0) ? js_tostring_val(js, args[0]) : js_mkstr(js, "", 0);
1361 if (is_err(msg)) return msg;
1362 return wasm_make_error(js, g_wasm_compileerror_proto, "CompileError", js_str(js, msg));
1363}
1364
1365static ant_value_t js_wasm_link_error_ctor(ant_t *js, ant_value_t *args, int nargs) {
1366 ant_value_t msg = (nargs > 0) ? js_tostring_val(js, args[0]) : js_mkstr(js, "", 0);
1367 if (is_err(msg)) return msg;
1368 return wasm_make_error(js, g_wasm_linkerror_proto, "LinkError", js_str(js, msg));
1369}
1370
1371static ant_value_t js_wasm_runtime_error_ctor(ant_t *js, ant_value_t *args, int nargs) {
1372 ant_value_t msg = (nargs > 0) ? js_tostring_val(js, args[0]) : js_mkstr(js, "", 0);
1373 if (is_err(msg)) return msg;
1374 return wasm_make_error(js, g_wasm_runtimeerror_proto, "RuntimeError", js_str(js, msg));
1375}
1376
1377void gc_mark_wasm(ant_t *js, gc_mark_fn mark) {
1378 for (size_t i = 0; i < g_wasm_import_env_count; i++) {
1379 wasm_import_func_env_t *env = g_wasm_import_envs[i];
1380 mark(js, env->fn);
1381 mark(js, env->owner);
1382 }
1383
1384 if (g_wasm_pending_import_throw_exists)
1385 mark(js, g_wasm_pending_import_throw);
1386}
1387
1388void init_wasm_module(void) {
1389 ant_t *js = rt->js;
1390 ant_value_t global = js_glob(js);
1391
1392 ant_value_t error_proto = js_get_ctor_proto(js, "Error", 5);
1393 ant_value_t ns = js_mkobj(js);
1394
1395 if (!ensure_wasm_engine()) return;
1396
1397 g_wasm_module_proto = js_mkobj(js);
1398 g_wasm_instance_proto = js_mkobj(js);
1399 g_wasm_global_proto = js_mkobj(js);
1400 g_wasm_memory_proto = js_mkobj(js);
1401 g_wasm_table_proto = js_mkobj(js);
1402 g_wasm_tag_proto = js_mkobj(js);
1403 g_wasm_exception_proto = js_mkobj(js);
1404
1405 g_wasm_compileerror_proto = js_mkobj(js);
1406 g_wasm_linkerror_proto = js_mkobj(js);
1407 g_wasm_runtimeerror_proto = js_mkobj(js);
1408
1409 js_set_proto_init(g_wasm_module_proto, js->sym.object_proto);
1410 js_set_proto_init(g_wasm_instance_proto, js->sym.object_proto);
1411 js_set_proto_init(g_wasm_global_proto, js->sym.object_proto);
1412 js_set_proto_init(g_wasm_memory_proto, js->sym.object_proto);
1413 js_set_proto_init(g_wasm_table_proto, js->sym.object_proto);
1414 js_set_proto_init(g_wasm_tag_proto, js->sym.object_proto);
1415 js_set_proto_init(g_wasm_exception_proto, js->sym.object_proto);
1416
1417 js_set_proto_init(g_wasm_compileerror_proto, error_proto);
1418 js_set_proto_init(g_wasm_linkerror_proto, error_proto);
1419 js_set_proto_init(g_wasm_runtimeerror_proto, error_proto);
1420
1421 js_set(js, g_wasm_global_proto, "valueOf", js_mkfun(js_wasm_global_value_of));
1422 js_set_getter_desc(js, g_wasm_global_proto, "value", 5, js_mkfun(js_wasm_global_value_getter), JS_DESC_C);
1423 js_set_setter_desc(js, g_wasm_global_proto, "value", 5, js_mkfun(js_wasm_global_value_setter), JS_DESC_C);
1424
1425 js_set_getter_desc(js, g_wasm_instance_proto, "exports", 7, js_mkfun(js_wasm_instance_exports_getter), JS_DESC_C);
1426 js_set_getter_desc(js, g_wasm_memory_proto, "buffer", 6, js_mkfun(js_wasm_memory_buffer_getter), JS_DESC_C);
1427 js_set(js, g_wasm_memory_proto, "grow", js_mkfun(js_wasm_memory_grow));
1428
1429 js_set_getter_desc(js, g_wasm_table_proto, "length", 6, js_mkfun(js_wasm_table_length_getter), JS_DESC_C);
1430 js_set(js, g_wasm_table_proto, "get", js_mkfun(js_wasm_table_get));
1431 js_set(js, g_wasm_table_proto, "set", js_mkfun(js_wasm_table_set));
1432 js_set(js, g_wasm_table_proto, "grow", js_mkfun(js_wasm_table_grow));
1433
1434 ant_value_t module_ctor = js_make_ctor(js, js_wasm_module_ctor, g_wasm_module_proto, "Module", 6);
1435 ant_value_t instance_ctor = js_make_ctor(js, js_wasm_instance_ctor, g_wasm_instance_proto, "Instance", 8);
1436 ant_value_t global_ctor = js_make_ctor(js, js_wasm_global_ctor, g_wasm_global_proto, "Global", 6);
1437 ant_value_t memory_ctor = js_make_ctor(js, js_wasm_memory_ctor, g_wasm_memory_proto, "Memory", 6);
1438 ant_value_t table_ctor = js_make_ctor(js, js_wasm_table_ctor, g_wasm_table_proto, "Table", 5);
1439 ant_value_t tag_ctor = js_make_ctor(js, js_wasm_tag_ctor, g_wasm_tag_proto, "Tag", 3);
1440 ant_value_t exception_ctor = js_make_ctor(js, js_wasm_exception_ctor, g_wasm_exception_proto, "Exception", 9);
1441
1442 ant_value_t compile_error_ctor = js_make_ctor(js, js_wasm_compile_error_ctor, g_wasm_compileerror_proto, "CompileError", 12);
1443 ant_value_t link_error_ctor = js_make_ctor(js, js_wasm_link_error_ctor, g_wasm_linkerror_proto, "LinkError", 9);
1444 ant_value_t runtime_error_ctor = js_make_ctor(js, js_wasm_runtime_error_ctor, g_wasm_runtimeerror_proto, "RuntimeError", 12);
1445
1446 js_set(js, module_ctor, "imports", js_mkfun(js_wasm_module_imports));
1447 js_set(js, module_ctor, "exports", js_mkfun(js_wasm_module_exports));
1448
1449 js_set(js, ns, "validate", js_mkfun(js_wasm_validate));
1450 js_set(js, ns, "compile", js_mkfun(js_wasm_compile));
1451 js_set(js, ns, "instantiate", js_mkfun(js_wasm_instantiate));
1452
1453 js_set(js, ns, "Module", module_ctor);
1454 js_set(js, ns, "Instance", instance_ctor);
1455 js_set(js, ns, "Global", global_ctor);
1456 js_set(js, ns, "Memory", memory_ctor);
1457 js_set(js, ns, "Table", table_ctor);
1458 js_set(js, ns, "Tag", tag_ctor);
1459 js_set(js, ns, "Exception", exception_ctor);
1460 js_set(js, ns, "CompileError", compile_error_ctor);
1461 js_set(js, ns, "LinkError", link_error_ctor);
1462 js_set(js, ns, "RuntimeError", runtime_error_ctor);
1463
1464 js_set(js, global, "WebAssembly", ns);
1465 js_set_descriptor(js, global, "WebAssembly", 11, JS_DESC_W | JS_DESC_C);
1466}