#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>
#include <wasm_c_api.h>
#include <wasm_export.h>

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wundef"
#pragma clang diagnostic ignored "-Wimplicit-int-conversion"
#include <wasm_c_api_internal.h>
#include <wasm_runtime.h>
#pragma clang diagnostic pop

#include "ant.h"
#include "ptr.h"
#include "errors.h"
#include "runtime.h"
#include "internal.h"
#include "descriptors.h"

#include "gc/modules.h"
#include "silver/engine.h"
#include "modules/buffer.h"
#include "modules/wasm.h"
#include "modules/wasi.h"

typedef struct {
  wasm_store_t *store;
  wasm_module_t *module;
} wasm_module_handle_t;

typedef struct {
  wasm_instance_t *instance;
  wasm_extern_vec_t exports;
  wasm_func_t **host_funcs;
  size_t host_func_count;
} wasm_instance_handle_t;

typedef enum {
  WASM_EXTERN_WRAP_GLOBAL = 1,
  WASM_EXTERN_WRAP_MEMORY,
  WASM_EXTERN_WRAP_TABLE,
} wasm_extern_wrap_kind_t;

typedef struct {
  wasm_extern_wrap_kind_t kind;
  wasm_store_t *store;
  bool own_handle;
  bool use_cached_value;
  wasm_val_t cached_value;
  union {
    wasm_global_t *global;
    wasm_memory_t *memory;
    wasm_table_t *table;
  } as;
} wasm_extern_handle_t;

typedef struct {
  ant_t *js;
  wasm_store_t *store;
  ant_value_t owner;
  ant_value_t fn;
} wasm_import_func_env_t;

typedef struct {
  wasm_func_t *func;
  bool own_func;
} wasm_func_handle_t;

enum { WASM_FUNC_STATE_TAG = 0x57465354u }; // WFST

static size_t g_wasm_import_env_count = 0;
static size_t g_wasm_import_env_cap   = 0;

static ant_value_t g_wasm_module_proto    = 0;
static ant_value_t g_wasm_instance_proto  = 0;
static ant_value_t g_wasm_global_proto    = 0;
static ant_value_t g_wasm_memory_proto    = 0;
static ant_value_t g_wasm_table_proto     = 0;
static ant_value_t g_wasm_tag_proto       = 0;
static ant_value_t g_wasm_exception_proto = 0;

static ant_value_t g_wasm_compileerror_proto   = 0;
static ant_value_t g_wasm_linkerror_proto      = 0;
static ant_value_t g_wasm_runtimeerror_proto   = 0;
static ant_value_t g_wasm_pending_import_throw = 0;

static wasm_engine_t *g_wasm_engine                = NULL;
static wasm_import_func_env_t **g_wasm_import_envs = NULL;
static bool g_wasm_pending_import_throw_exists     = false;

static void wasm_clear_pending_import_throw(void) {
  g_wasm_pending_import_throw_exists = false;
  g_wasm_pending_import_throw = js_mkundef();
}

static void wasm_set_pending_import_throw(ant_value_t value) {
  g_wasm_pending_import_throw_exists = true;
  g_wasm_pending_import_throw = value;
}

static ant_value_t wasm_consume_pending_import_throw(void) {
  ant_value_t value = g_wasm_pending_import_throw_exists
    ? g_wasm_pending_import_throw
    : js_mkundef();
  wasm_clear_pending_import_throw();
  return value;
}

static void wasm_register_import_env(wasm_import_func_env_t *env) {
  if (g_wasm_import_env_count == g_wasm_import_env_cap) {
    size_t new_cap = g_wasm_import_env_cap ? g_wasm_import_env_cap * 2 : 16;
    wasm_import_func_env_t **new_arr = realloc(g_wasm_import_envs, new_cap * sizeof(*new_arr));
    if (!new_arr) return;
    g_wasm_import_envs = new_arr;
    g_wasm_import_env_cap = new_cap;
  }
  g_wasm_import_envs[g_wasm_import_env_count++] = env;
}

static void wasm_unregister_import_env(wasm_import_func_env_t *env) {
  for (size_t i = 0; i < g_wasm_import_env_count; i++) {
    if (g_wasm_import_envs[i] != env) continue;
    g_wasm_import_envs[i] = g_wasm_import_envs[--g_wasm_import_env_count];
    return;
  }
}

static void wasm_import_func_env_finalizer(void *env_ptr) {
  wasm_import_func_env_t *env = (wasm_import_func_env_t *)env_ptr;
  wasm_unregister_import_env(env);
  free(env);
}

static ant_value_t wasm_wrap_func(
  ant_t *js, wasm_func_t *func, 
  ant_value_t owner, bool own_func
);

static bool ensure_wasm_engine(void) {
  if (g_wasm_engine) return true;
  g_wasm_engine = wasm_engine_new();
  return g_wasm_engine != NULL;
}

static size_t wasm_name_len(const wasm_name_t *name) {
  if (!name || !name->data) return 0;
  if (name->size > 0 && name->data[name->size - 1] == '\0') return name->size - 1;
  return name->size;
}

static const char *wasm_extern_kind_name(wasm_externkind_t kind) {
switch (kind) {
  case WASM_EXTERN_FUNC: return "function";
  case WASM_EXTERN_GLOBAL: return "global";
  case WASM_EXTERN_TABLE: return "table";
  case WASM_EXTERN_MEMORY: return "memory";
  default: return "unknown";
}}

static wasm_valkind_t wasm_valkind_from_string(const char *name, size_t len, bool *ok) {
  *ok = true;
  if (len == 3 && !memcmp(name, "i32", 3)) return WASM_I32;
  if (len == 3 && !memcmp(name, "i64", 3)) return WASM_I64;
  if (len == 3 && !memcmp(name, "f32", 3)) return WASM_F32;
  if (len == 3 && !memcmp(name, "f64", 3)) return WASM_F64;
  if (len == 9 && !memcmp(name, "externref", 9)) return WASM_EXTERNREF;
  if (len == 7 && !memcmp(name, "funcref", 7)) return WASM_FUNCREF;
  *ok = false;
  return WASM_I32;
}

static wasm_module_handle_t *wasm_module_handle(ant_value_t value) {
  if (!js_check_brand(value, BRAND_WASM_MODULE)) return NULL;
  ant_value_t slot = js_get_slot(value, SLOT_DATA);
  if (vtype(slot) != T_NUM) return NULL;
  return (wasm_module_handle_t *)(uintptr_t)(size_t)js_getnum(slot);
}

static wasm_instance_handle_t *wasm_instance_handle(ant_value_t value) {
  if (!js_check_brand(value, BRAND_WASM_INSTANCE)) return NULL;
  ant_value_t slot = js_get_slot(value, SLOT_DATA);
  if (vtype(slot) != T_NUM) return NULL;
  return (wasm_instance_handle_t *)(uintptr_t)(size_t)js_getnum(slot);
}

static wasm_extern_handle_t *wasm_extern_handle(ant_value_t value, wasm_extern_wrap_kind_t kind) {
  if ((kind == WASM_EXTERN_WRAP_GLOBAL && !js_check_brand(value, BRAND_WASM_GLOBAL))
      || (kind == WASM_EXTERN_WRAP_MEMORY && !js_check_brand(value, BRAND_WASM_MEMORY))
      || (kind == WASM_EXTERN_WRAP_TABLE && !js_check_brand(value, BRAND_WASM_TABLE))) {
    return NULL;
  }

  ant_value_t slot = js_get_slot(value, SLOT_DATA);
  if (vtype(slot) != T_NUM) return NULL;
  wasm_extern_handle_t *handle = (wasm_extern_handle_t *)(uintptr_t)(size_t)js_getnum(slot);
  return handle && handle->kind == kind ? handle : NULL;
}

static ant_value_t wasm_make_error(ant_t *js, ant_value_t proto, const char *name, const char *message) {
  ant_value_t err = js_make_error_silent(js, JS_ERR_TYPE, message ? message : "");
  if (vtype(err) != T_OBJ) return err;
  js_set(js, err, "name", js_mkstr(js, name, strlen(name)));
  if (is_object_type(proto)) js_set_proto_init(err, proto);
  return err;
}

static ant_value_t wasm_make_compile_error(ant_t *js, const char *message) {
  return wasm_make_error(js, g_wasm_compileerror_proto, "CompileError", message);
}

static ant_value_t wasm_make_link_error(ant_t *js, const char *message) {
  return wasm_make_error(js, g_wasm_linkerror_proto, "LinkError", message);
}

static ant_value_t wasm_make_runtime_error(ant_t *js, const char *message) {
  return wasm_make_error(js, g_wasm_runtimeerror_proto, "RuntimeError", message);
}

static ant_value_t wasm_error_value(ant_t *js, ant_value_t value) {
  if (is_err(value) && js->thrown_exists)
    return js->thrown_value;
  return value;
}

static void wasm_reject_with_error(ant_t *js, ant_value_t promise, ant_value_t error) {
  js_reject_promise(js, promise, error);
}

static 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) {
  const uint8_t *bytes = NULL;
  size_t len = 0;
  memset(out, 0, sizeof(*out));

  if (!buffer_source_get_bytes(js, value, &bytes, &len)) {
    snprintf(error_buf, error_buf_len, "Expected a BufferSource");
    return false;
  }

  wasm_byte_vec_new_uninitialized(out, len);
  if (len > 0 && !out->data) {
    snprintf(error_buf, error_buf_len, "Out of memory");
    return false;
  }

  if (len > 0) memcpy(out->data, bytes, len);
  return true;
}

static ant_value_t wasm_value_from_i64(ant_t *js, int64_t value) {
  char buf[64];
  int n = snprintf(buf, sizeof(buf), "%" PRId64, value);
  if (n < 0) return js_mkerr(js, "Failed to convert i64");
  return js_mkbigint(js, buf, (size_t)n, value < 0);
}

static ant_value_t wasm_value_to_js(ant_t *js, const wasm_val_t *value) {
  switch (value->kind) {
    case WASM_I32: return js_mknum((double)value->of.i32);
    case WASM_I64: return wasm_value_from_i64(js, value->of.i64);
    case WASM_F32: return js_mknum((double)value->of.f32);
    case WASM_F64: return js_mknum(value->of.f64);
    case WASM_EXTERNREF:
      return value->of.ref ? js_mkundef() : js_mknull();
    case WASM_FUNCREF: {
      wasm_func_t *func;
      if (!value->of.ref) return js_mknull();
      func = wasm_ref_as_func(value->of.ref);
      if (!func) return js_mkundef();
      return wasm_wrap_func(js, func, js_mkundef(), true);
    }
    default: return js_mkundef();
  }
}

static bool js_value_to_i64(ant_t *js, ant_value_t value, int64_t *out) {
  if (vtype(value) == T_BIGINT) {
    ant_value_t str_val = js_tostring_val(js, value);
    if (is_err(str_val) || vtype(str_val) != T_STR) return false;
    const char *str = js_str(js, str_val);
    if (!str) return false;
    char *end = NULL;
    long long parsed = strtoll(str, &end, 10);
    if (!end || *end != '\0') return false;
    *out = (int64_t)parsed;
    return true;
  }

  double d = js_to_number(js, value);
  if (!isfinite(d)) return false;
  *out = (int64_t)d;
  return true;
}

static bool js_value_to_wasm(ant_t *js, ant_value_t value, wasm_valkind_t kind, wasm_val_t *out) {
  memset(out, 0, sizeof(*out));
  out->kind = kind;

  switch (kind) {
    case WASM_I32:
      out->of.i32 = (int32_t)js_to_number(js, value);
      return true;
    case WASM_I64:
      return js_value_to_i64(js, value, &out->of.i64);
    case WASM_F32:
      out->of.f32 = (float)js_to_number(js, value);
      return true;
    case WASM_F64:
      out->of.f64 = js_to_number(js, value);
      return true;
    case WASM_EXTERNREF:
      out->of.ref = NULL;
      return vtype(value) == T_NULL || vtype(value) == T_UNDEF;
    case WASM_FUNCREF: {
      ant_value_t state;
      wasm_func_handle_t *handle;
      if (vtype(value) == T_NULL || vtype(value) == T_UNDEF) {
        out->of.ref = NULL;
        return true;
      }
      if (!is_callable(value)) return false;
      state = js_get_slot(value, SLOT_DATA);
      if (!is_object_type(state) || !js_check_native_tag(state, WASM_FUNC_STATE_TAG))
        return false;
      handle = (wasm_func_handle_t *)js_get_native_ptr(state);
      if (!handle || !handle->func) return false;
      out->of.ref = wasm_func_as_ref(handle->func);
      return out->of.ref != NULL;
    }
    default: return false;
  }
}

static ant_value_t wasm_js_from_result_vec(ant_t *js, wasm_val_vec_t *results) {
  if (!results || results->size == 0) return js_mkundef();
  if (results->size == 1) return wasm_value_to_js(js, &results->data[0]);

  ant_value_t arr = js_mkarr(js);
  for (size_t i = 0; i < results->size; i++) {
    js_arr_push(js, arr, wasm_value_to_js(js, &results->data[i]));
  }
  return arr;
}

static ant_value_t wasm_trap_to_error(ant_t *js, wasm_trap_t *trap) {
  wasm_message_t message = WASM_EMPTY_VEC;
  wasm_trap_message(trap, &message);

  ant_value_t err = wasm_make_runtime_error(js, message.data ? message.data : "WebAssembly trap");

  wasm_byte_vec_delete(&message);
  wasm_trap_delete(trap);
  return err;
}

static ant_value_t wasm_wrap_module(ant_t *js, wasm_store_t *store, wasm_module_t *module) {
  wasm_module_handle_t *handle = calloc(1, sizeof(*handle));
  if (!handle) {
    if (module) wasm_module_delete(module);
    if (store) wasm_store_delete(store);
    return js_mkerr(js, "out of memory");
  }

  handle->store = store;
  handle->module = module;

  ant_value_t obj = js_mkobj(js);
  js_set_proto_init(obj, g_wasm_module_proto);
  js_set_slot(obj, SLOT_BRAND, js_mknum(BRAND_WASM_MODULE));
  js_set_slot(obj, SLOT_DATA, ANT_PTR(handle));
  return obj;
}

static void wasm_module_finalize(ant_t *js, ant_object_t *obj) {
  if (!obj->extra_slots) return;

  ant_extra_slot_t *entries = (ant_extra_slot_t *)obj->extra_slots;
  for (uint8_t i = 0; i < obj->extra_count; i++) {
    if (entries[i].slot != SLOT_DATA || vtype(entries[i].value) != T_NUM) continue;
    wasm_module_handle_t *handle = (wasm_module_handle_t *)(uintptr_t)(size_t)js_getnum(entries[i].value);
    if (!handle) return;
    if (handle->module) wasm_module_delete(handle->module);
    if (handle->store) wasm_store_delete(handle->store);
    free(handle);
    return;
  }
}

static ant_value_t wasm_wrap_instance(ant_t *js, wasm_instance_handle_t *handle, ant_value_t module_ref) {
  ant_value_t obj = js_mkobj(js);
  js_set_proto_init(obj, g_wasm_instance_proto);
  js_set_slot(obj, SLOT_BRAND, js_mknum(BRAND_WASM_INSTANCE));
  js_set_slot(obj, SLOT_DATA, ANT_PTR(handle));
  js_set_slot_wb(js, obj, SLOT_CTOR, module_ref);
  return obj;
}

static void wasm_instance_finalize(ant_t *js, ant_object_t *obj) {
  if (!obj->extra_slots) return;

  ant_extra_slot_t *entries = (ant_extra_slot_t *)obj->extra_slots;
  for (uint8_t i = 0; i < obj->extra_count; i++) {
    if (entries[i].slot != SLOT_DATA || vtype(entries[i].value) != T_NUM) continue;
    wasm_instance_handle_t *handle = (wasm_instance_handle_t *)(uintptr_t)(size_t)js_getnum(entries[i].value);
    if (!handle) return;
    for (size_t j = 0; j < handle->host_func_count; j++) {
      if (handle->host_funcs[j]) wasm_func_delete(handle->host_funcs[j]);
    }
    free(handle->host_funcs);
    wasm_extern_vec_delete(&handle->exports);
    free(handle);
    return;
  }
}

static 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) {
  wasm_extern_handle_t *handle = calloc(1, sizeof(*handle));
  if (!handle) return js_mkerr(js, "out of memory");

  handle->kind = kind;
  handle->store = store;
  handle->own_handle = own_handle;
  handle->cached_value = (wasm_val_t)WASM_INIT_VAL;

  switch (kind) {
    case WASM_EXTERN_WRAP_GLOBAL: handle->as.global = (wasm_global_t *)ptr; break;
    case WASM_EXTERN_WRAP_MEMORY: handle->as.memory = (wasm_memory_t *)ptr; break;
    case WASM_EXTERN_WRAP_TABLE: handle->as.table = (wasm_table_t *)ptr; break;
  }

  ant_value_t obj = js_mkobj(js);
  js_set_proto_init(obj, proto);
  js_set_slot(obj, SLOT_BRAND, js_mknum(brand));
  js_set_slot(obj, SLOT_DATA, ANT_PTR(handle));
  if (is_object_type(owner)) js_set_slot_wb(js, obj, SLOT_ENTRIES, owner);
  return obj;
}

static void wasm_extern_finalize(ant_t *js, ant_object_t *obj) {
  if (!obj->extra_slots) return;

  ant_extra_slot_t *entries = (ant_extra_slot_t *)obj->extra_slots;
  for (uint8_t i = 0; i < obj->extra_count; i++) {
    if (entries[i].slot != SLOT_DATA || vtype(entries[i].value) != T_NUM) continue;
    wasm_extern_handle_t *handle = (wasm_extern_handle_t *)(uintptr_t)(size_t)js_getnum(entries[i].value);
    if (!handle) return;

    if (handle->own_handle) {
      switch (handle->kind) {
        case WASM_EXTERN_WRAP_GLOBAL:
          if (handle->as.global) wasm_global_delete(handle->as.global);
          break;
        case WASM_EXTERN_WRAP_MEMORY:
          if (handle->as.memory) wasm_memory_delete(handle->as.memory);
          break;
        case WASM_EXTERN_WRAP_TABLE:
          if (handle->as.table) wasm_table_delete(handle->as.table);
          break;
      }
      if (handle->store) wasm_store_delete(handle->store);
    }

    free(handle);
    return;
  }
}

static ant_value_t js_wasm_exported_func_call(ant_t *js, ant_value_t *args, int nargs) {
  ant_value_t state = js_get_slot(js->current_func, SLOT_DATA);
  wasm_func_handle_t *handle;
  wasm_func_t *func;

  if (!is_object_type(state) || !js_check_native_tag(state, WASM_FUNC_STATE_TAG))
    return js_mkerr(js, "Invalid WebAssembly function");

  handle = (wasm_func_handle_t *)js_get_native_ptr(state);
  func = handle ? handle->func : NULL;
  if (!func) return js_mkerr(js, "Invalid WebAssembly function");

  wasm_functype_t *type = wasm_func_type(func);
  if (!type) return js_mkerr(js, "Failed to inspect WebAssembly function");

  const wasm_valtype_vec_t *params = wasm_functype_params(type);
  const wasm_valtype_vec_t *results_t = wasm_functype_results(type);

  wasm_val_vec_t wasm_args = WASM_EMPTY_VEC;
  wasm_val_vec_t wasm_results = WASM_EMPTY_VEC;
  wasm_trap_t *trap = NULL;
  ant_value_t result = js_mkundef();

  wasm_val_vec_new_uninitialized(&wasm_args, params ? params->size : 0);
  wasm_val_vec_new_uninitialized(&wasm_results, results_t ? results_t->size : 0);

  for (size_t i = 0; params && i < params->size; i++) {
    if ((int)i >= nargs) {
      wasm_val_vec_delete(&wasm_args);
      wasm_val_vec_delete(&wasm_results);
      wasm_functype_delete(type);
      return js_mkerr_typed(js, JS_ERR_TYPE, "Missing WebAssembly argument");
    }

    if (!js_value_to_wasm(js, args[i], wasm_valtype_kind(params->data[i]), &wasm_args.data[i])) {
      wasm_val_vec_delete(&wasm_args);
      wasm_val_vec_delete(&wasm_results);
      wasm_functype_delete(type);
      return js_mkerr_typed(js, JS_ERR_TYPE, "Unsupported WebAssembly argument type");
    }
  }

  wasm_clear_pending_import_throw();
  trap = wasm_func_call(func, &wasm_args, &wasm_results);
  
  if (trap) {
    if (g_wasm_pending_import_throw_exists) {
      result = wasm_consume_pending_import_throw();
      js_mark_errorlike_no_stack(js, result);
      wasm_val_vec_delete(&wasm_args);
      wasm_val_vec_delete(&wasm_results);
      wasm_functype_delete(type);
      wasm_trap_delete(trap);
      return js_throw(js, result);
    }
    
    result = wasm_trap_to_error(js, trap);
    wasm_val_vec_delete(&wasm_args);
    wasm_val_vec_delete(&wasm_results);
    wasm_functype_delete(type);
    return js_throw(js, result);
  }
  
  wasm_clear_pending_import_throw();
  result = wasm_js_from_result_vec(js, &wasm_results);

  wasm_val_vec_delete(&wasm_args);
  wasm_val_vec_delete(&wasm_results);
  wasm_functype_delete(type);
  
  return result;
}

static void wasm_func_state_finalize(ant_t *js, ant_object_t *obj) {
  if (obj->native.tag != WASM_FUNC_STATE_TAG) return;

  wasm_func_handle_t *handle = (wasm_func_handle_t *)obj->native.ptr;
  if (!handle) return;

  if (handle->own_func && handle->func) wasm_func_delete(handle->func);
  free(handle);
  obj->native.ptr = NULL;
  obj->native.tag = 0;
}

static ant_value_t wasm_wrap_func(ant_t *js, wasm_func_t *func, ant_value_t owner, bool own_func) {
  wasm_func_handle_t *handle;
  ant_value_t state;

  if (!func) return js_mkundef();

  handle = calloc(1, sizeof(*handle));
  if (!handle) {
    if (own_func) wasm_func_delete(func);
    return js_mkerr(js, "out of memory");
  }

  handle->func = func;
  handle->own_func = own_func;

  state = js_mkobj(js);
  js_set_native_ptr(state, handle);
  js_set_native_tag(state, WASM_FUNC_STATE_TAG);
  
  if (is_object_type(owner)) js_set_slot_wb(js, state, SLOT_ENTRIES, owner);
  js_set_finalizer(state, wasm_func_state_finalize);

  return js_heavy_mkfun(js, js_wasm_exported_func_call, state);
}

static 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) {
  switch (wasm_extern_kind(external)) {
    case WASM_EXTERN_FUNC: {
      return wasm_wrap_func(js, wasm_extern_as_func(external), instance_obj, false);
    }
    case WASM_EXTERN_GLOBAL: {
      ant_value_t obj = wasm_wrap_extern_object(
        js, WASM_EXTERN_WRAP_GLOBAL, g_wasm_global_proto, BRAND_WASM_GLOBAL,
        NULL, false, wasm_extern_as_global(external), instance_obj
      );
      if (vtype(obj) == T_OBJ) js_set_finalizer(obj, wasm_extern_finalize);
      return obj;
    }
    case WASM_EXTERN_MEMORY: {
      ant_value_t obj = wasm_wrap_extern_object(
        js, WASM_EXTERN_WRAP_MEMORY, g_wasm_memory_proto, BRAND_WASM_MEMORY,
        NULL, false, wasm_extern_as_memory(external), instance_obj
      );
      if (vtype(obj) == T_OBJ) js_set_finalizer(obj, wasm_extern_finalize);
      return obj;
    }
    case WASM_EXTERN_TABLE: {
      ant_value_t obj = wasm_wrap_extern_object(
        js, WASM_EXTERN_WRAP_TABLE, g_wasm_table_proto, BRAND_WASM_TABLE,
        NULL, false, wasm_extern_as_table(external), instance_obj
      );
      if (vtype(obj) == T_OBJ) js_set_finalizer(obj, wasm_extern_finalize);
      return obj;
    }
    default:
      (void)export_type;
      return js_mkundef();
  }
}

static ant_value_t wasm_module_from_bytes(ant_t *js, ant_value_t value, ant_value_t *out_module) {
  wasm_byte_vec_t binary = WASM_EMPTY_VEC;
  wasm_store_t *store = NULL;
  wasm_module_t *module = NULL;
  
  char error_buf[128] = {0};
  bool suppress_wasi_warning = false;

  *out_module = js_mkundef();

  if (!ensure_wasm_engine()) return js_mkerr(js, "Failed to initialize WebAssembly engine");
  if (!(store = wasm_store_new(g_wasm_engine))) return js_mkerr(js, "Failed to create WebAssembly store");

  if (!wasm_buffer_source_to_vec(js, value, &binary, error_buf, sizeof(error_buf))) {
    wasm_store_delete(store);
    return js_mkerr_typed(js, JS_ERR_TYPE, "%s", error_buf);
  }

  suppress_wasi_warning = wasi_bytes_need_wasi_command_warning_suppression(
    (const uint8_t *)binary.data, binary.size
  );
  
  if (suppress_wasi_warning) wasm_runtime_set_log_level(WASM_LOG_LEVEL_ERROR);
  module = wasm_module_new(store, &binary);
  
  if (suppress_wasi_warning) wasm_runtime_set_log_level(WASM_LOG_LEVEL_WARNING);
  wasm_byte_vec_delete(&binary);
  
  if (!module) {
    wasm_store_delete(store);
    return wasm_make_compile_error(js, "Failed to compile WebAssembly module");
  }

  *out_module = wasm_wrap_module(js, store, module);
  if (vtype(*out_module) == T_OBJ) {
    js_set_finalizer(*out_module, wasm_module_finalize);
    js_set_slot_wb(js, *out_module, SLOT_MAP, value);
  }
  return js_mkundef();
}

static ant_value_t js_wasm_module_ctor(ant_t *js, ant_value_t *args, int nargs) {
  ant_value_t module = js_mkundef();
  ant_value_t err;

  if (vtype(js->new_target) == T_UNDEF)
    return js_mkerr_typed(js, JS_ERR_TYPE, "WebAssembly.Module constructor requires 'new'");
  if (nargs < 1)
    return js_mkerr_typed(js, JS_ERR_TYPE, "WebAssembly.Module requires a BufferSource");

  err = wasm_module_from_bytes(js, args[0], &module);
  if (is_err(err)) return err;
  if (vtype(module) != T_OBJ) return js_throw(js, wasm_error_value(js, err));
  return module;
}

static ant_value_t wasm_module_type_descriptors(ant_t *js, ant_value_t module_obj, bool imports) {
  wasm_module_handle_t *handle = wasm_module_handle(module_obj);
  if (!handle) return js_mkerr_typed(js, JS_ERR_TYPE, "Expected a WebAssembly.Module");

  ant_value_t arr = js_mkarr(js);
  if (imports) {
    wasm_importtype_vec_t vec = WASM_EMPTY_VEC;
    wasm_module_imports(handle->module, &vec);
    for (size_t i = 0; i < vec.size; i++) {
      const wasm_importtype_t *entry = vec.data[i];
      const wasm_name_t *module_name = wasm_importtype_module(entry);
      const wasm_name_t *name = wasm_importtype_name(entry);
      const wasm_externtype_t *type = wasm_importtype_type(entry);

      ant_value_t item = js_mkobj(js);
      js_set(js, item, "module", js_mkstr(js, module_name->data, wasm_name_len(module_name)));
      js_set(js, item, "name", js_mkstr(js, name->data, wasm_name_len(name)));
      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)))));
      js_arr_push(js, arr, item);
    }
    wasm_importtype_vec_delete(&vec);
  } else {
    wasm_exporttype_vec_t vec = WASM_EMPTY_VEC;
    wasm_module_exports(handle->module, &vec);
    for (size_t i = 0; i < vec.size; i++) {
      const wasm_exporttype_t *entry = vec.data[i];
      const wasm_name_t *name = wasm_exporttype_name(entry);
      const wasm_externtype_t *type = wasm_exporttype_type(entry);
      
      ant_value_t item = js_mkobj(js);
      js_set(js, item, "name", js_mkstr(js, name->data, wasm_name_len(name)));
      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)))));
      js_arr_push(js, arr, item);
    }
    wasm_exporttype_vec_delete(&vec);
  }

  return arr;
}

static ant_value_t js_wasm_module_imports(ant_t *js, ant_value_t *args, int nargs) {
  if (nargs < 1) return js_mkerr_typed(js, JS_ERR_TYPE, "WebAssembly.Module.imports requires 1 argument");
  return wasm_module_type_descriptors(js, args[0], true);
}

static ant_value_t js_wasm_module_exports(ant_t *js, ant_value_t *args, int nargs) {
  if (nargs < 1) return js_mkerr_typed(js, JS_ERR_TYPE, "WebAssembly.Module.exports requires 1 argument");
  return wasm_module_type_descriptors(js, args[0], false);
}

static ant_value_t js_wasm_instance_exports_getter(ant_t *js, ant_value_t *args, int nargs) {
  if (!js_check_brand(js->this_val, BRAND_WASM_INSTANCE)) return js_mkerr_typed(js, JS_ERR_TYPE, "Expected a WebAssembly.Instance");
  return js_get_slot(js->this_val, SLOT_ENTRIES);
}

static ant_value_t wasm_property_get_nested(ant_t *js, ant_value_t base, const wasm_name_t *name) {
  if (!is_object_type(base)) return js_mkundef();
  return js_getprop_fallback(js, base, name && name->data ? name->data : "");
}

static wasm_trap_t *wasm_import_func_callback(void *env_ptr, const wasm_val_vec_t *args, wasm_val_vec_t *results) {
  wasm_import_func_env_t *env = (wasm_import_func_env_t *)env_ptr;
  ant_t *js = env->js;
  
  ant_value_t *js_args = NULL;
  ant_value_t result = js_mkundef();
  wasm_message_t trap_msg = WASM_EMPTY_VEC;

  if (args && args->size > 0) {
    js_args = calloc(args->size, sizeof(*js_args));
    if (!js_args) {
      wasm_name_new_from_string_nt(&trap_msg, "Out of memory");
      wasm_trap_t *trap = wasm_trap_new(env->store, &trap_msg);
      wasm_byte_vec_delete(&trap_msg);
      return trap;
    }
  }

  for (size_t i = 0; args && i < args->size; i++) {
    js_args[i] = wasm_value_to_js(js, &args->data[i]);
  }

  result = sv_vm_call(
    js->vm, js, env->fn, js_mkundef(), 
    js_args, args ? (int)args->size : 0, NULL, false
  );
  free(js_args);

  if (is_err(result)) {
    ant_value_t thrown = js->thrown_exists ? js->thrown_value : result;
    wasm_set_pending_import_throw(thrown);
    
    const char *msg = "WebAssembly import threw";
    if (vtype(js->thrown_value) == T_OBJ) {
      const char *message = get_str_prop(js, js->thrown_value, "message", 7, NULL);
      if (message && *message) msg = message;
    }
    
    wasm_name_new_from_string_nt(&trap_msg, msg);
    wasm_trap_t *trap = wasm_trap_new(env->store, &trap_msg);
    wasm_byte_vec_delete(&trap_msg);
    
    return trap;
  }

  if (results && results->size > 0) {
  if (results->size == 1) {
    if (!js_value_to_wasm(js, result, results->data[0].kind, &results->data[0])) {
      wasm_name_new_from_string_nt(&trap_msg, "Unsupported import return value");
      wasm_trap_t *trap = wasm_trap_new(env->store, &trap_msg);
      wasm_byte_vec_delete(&trap_msg);
      return trap;
    }
  } else {
    if (vtype(result) != T_ARR) {
      wasm_name_new_from_string_nt(&trap_msg, "Expected an array for multi-value return");
      wasm_trap_t *trap = wasm_trap_new(env->store, &trap_msg);
      wasm_byte_vec_delete(&trap_msg);
      return trap;
    }

    for (size_t i = 0; i < results->size; i++) {
      ant_value_t item = js_arr_get(js, result, (ant_offset_t)i);
      if (!js_value_to_wasm(js, item, results->data[i].kind, &results->data[i])) {
        wasm_name_new_from_string_nt(&trap_msg, "Unsupported import return value");
        wasm_trap_t *trap = wasm_trap_new(env->store, &trap_msg);
        wasm_byte_vec_delete(&trap_msg);
        return trap;
      }
    }
  }}

  return NULL;
}

static ant_value_t wasm_instantiate_module(ant_t *js, ant_value_t module_obj, ant_value_t import_obj, ant_value_t *out_instance) {
  wasm_module_handle_t *module_handle = wasm_module_handle(module_obj);
  wasm_importtype_vec_t import_types = WASM_EMPTY_VEC;
  wasm_exporttype_vec_t export_types = WASM_EMPTY_VEC;
  
  wasm_extern_t **imports = NULL;
  wasm_func_t **owned_host_funcs = NULL;
  
  size_t owned_host_func_count = 0;
  wasm_extern_vec_t exports = WASM_EMPTY_VEC;
  
  wasm_trap_t *trap = NULL;
  wasm_instance_t *instance = NULL;
  ant_value_t instance_obj = js_mkundef();
  ant_value_t exports_obj = js_mkobj(js);
  *out_instance = js_mkundef();

  if (!module_handle) 
    return js_mkerr_typed(js, JS_ERR_TYPE, "Expected a WebAssembly.Module");

  if (wasi_module_has_wasi_imports(module_handle->module)
      && wasi_module_is_command_or_reactor(module_handle->module)) {
    ant_value_t wasi_opts = is_object_type(import_obj) ? js_get(js, import_obj, "wasi") : js_mkundef();
    if (!is_object_type(import_obj) || is_object_type(wasi_opts)) {
      ant_value_t bytes_src = js_get_slot(module_obj, SLOT_MAP);
      wasm_byte_vec_t binary = WASM_EMPTY_VEC;
      char wasi_err[128] = {0};
      if (!wasm_buffer_source_to_vec(js, bytes_src, &binary, wasi_err, sizeof(wasi_err))) {
        return js_mkerr(js, "WASI: cannot extract module bytes");
      }
      *out_instance = wasi_instantiate(js, (const uint8_t *)binary.data, binary.size, module_obj, wasi_opts);
      wasm_byte_vec_delete(&binary);
      return is_err(*out_instance) ? *out_instance : js_mkundef();
    }
  }

  wasm_module_imports(module_handle->module, &import_types);
  if (import_types.size > 0) {
    imports = calloc(import_types.size, sizeof(*imports));
    owned_host_funcs = calloc(import_types.size, sizeof(*owned_host_funcs));
    if (!imports || !owned_host_funcs) {
      free(imports);
      free(owned_host_funcs);
      wasm_importtype_vec_delete(&import_types);
      return js_mkerr(js, "out of memory");
    }
  }

  for (size_t i = 0; i < import_types.size; i++) {
    const wasm_importtype_t *import_type = import_types.data[i];
    const wasm_name_t *module_name = wasm_importtype_module(import_type);
    const wasm_name_t *field_name = wasm_importtype_name(import_type);
    const wasm_externtype_t *extern_type = wasm_importtype_type(import_type);
    
    ant_value_t namespace_obj = wasm_property_get_nested(js, import_obj, module_name);
    ant_value_t value = wasm_property_get_nested(js, namespace_obj, field_name);
    wasm_externkind_t kind = wasm_externtype_kind(extern_type);

    if (kind == WASM_EXTERN_MEMORY || kind == WASM_EXTERN_TABLE) {
      free(imports);
      free(owned_host_funcs);
      wasm_importtype_vec_delete(&import_types);
      return wasm_make_link_error(js, "The current WAMR backend does not support memory/table imports");
    }

    if (kind == WASM_EXTERN_FUNC) {
      const wasm_functype_t *func_type = wasm_externtype_as_functype_const(extern_type);
      wasm_import_func_env_t *env = NULL;

      if (!is_callable(value)) {
        free(imports);
        free(owned_host_funcs);
        wasm_importtype_vec_delete(&import_types);
        return wasm_make_link_error(js, "Missing function import");
      }

      env = calloc(1, sizeof(*env));
      if (!env) {
        free(imports);
        free(owned_host_funcs);
        wasm_importtype_vec_delete(&import_types);
        return js_mkerr(js, "out of memory");
      }

      env->js = js;
      env->store = module_handle->store;
      env->owner = module_obj;
      env->fn = value;
      wasm_register_import_env(env);


      owned_host_funcs[owned_host_func_count] = wasm_func_new_with_env(
        module_handle->store,
        func_type,
        wasm_import_func_callback,
        env,
        wasm_import_func_env_finalizer
      );

      if (!owned_host_funcs[owned_host_func_count]) {
        free(env);
        free(imports);
        free(owned_host_funcs);
        wasm_importtype_vec_delete(&import_types);
        return wasm_make_link_error(js, "Failed to create function import");
      }

      imports[i] = wasm_func_as_extern(owned_host_funcs[owned_host_func_count]);
      owned_host_func_count++;
      continue;
    }

    if (kind == WASM_EXTERN_GLOBAL) {
      wasm_extern_handle_t *handle = wasm_extern_handle(value, WASM_EXTERN_WRAP_GLOBAL);
      if (!handle || !handle->as.global) {
        free(imports);
        free(owned_host_funcs);
        wasm_importtype_vec_delete(&import_types);
        return wasm_make_link_error(js, "Missing global import");
      }
      imports[i] = wasm_global_as_extern(handle->as.global);
      continue;
    }
  }

  {
    wasm_extern_vec_t import_vec = WASM_EMPTY_VEC;
    if (imports && import_types.size > 0)
      import_vec = (wasm_extern_vec_t){ import_types.size, imports, import_types.size, sizeof(*imports), NULL };

    wasm_clear_pending_import_throw();
    instance = wasm_instance_new_with_args(module_handle->store, module_handle->module, &import_vec, &trap, KILOBYTE(32), 0);
  }

  free(imports);
  wasm_importtype_vec_delete(&import_types);

  if (!instance) {
    for (size_t i = 0; i < owned_host_func_count; i++) {
      if (owned_host_funcs[i]) wasm_func_delete(owned_host_funcs[i]);
    }
    free(owned_host_funcs);
    
    if (trap) {
      if (g_wasm_pending_import_throw_exists) {
        ant_value_t thrown = wasm_consume_pending_import_throw();
        js_mark_errorlike_no_stack(js, thrown);
        wasm_trap_delete(trap);
        return js_throw(js, thrown);
      }
      return wasm_trap_to_error(js, trap);
    }
    
    return wasm_make_link_error(js, "Failed to instantiate WebAssembly module");
  }
  
  wasm_clear_pending_import_throw();
  wasm_instance_exports(instance, &exports);
  wasm_module_exports(module_handle->module, &export_types);

  {
    wasm_instance_handle_t *inst_handle = calloc(1, sizeof(*inst_handle));
    if (!inst_handle) {
      wasm_extern_vec_delete(&exports);
      wasm_exporttype_vec_delete(&export_types);
      return js_mkerr(js, "out of memory");
    }
    inst_handle->instance = instance;
    inst_handle->exports = exports;
    inst_handle->host_funcs = owned_host_funcs;
    inst_handle->host_func_count = owned_host_func_count;

    instance_obj = wasm_wrap_instance(js, inst_handle, module_obj);
  }
  if (vtype(instance_obj) != T_OBJ) {
    wasm_exporttype_vec_delete(&export_types);
    return instance_obj;
  }
  js_set_finalizer(instance_obj, wasm_instance_finalize);

  for (size_t i = 0; i < export_types.size && i < exports.size; i++) {
    const wasm_exporttype_t *export_type = export_types.data[i];
    const wasm_name_t *name = wasm_exporttype_name(export_type);
    ant_value_t export_value = wasm_wrap_export_value(js, instance_obj, export_type, exports.data[i]);
    js_setprop(js, exports_obj, js_mkstr(js, name->data, wasm_name_len(name)), export_value);
  }
  
  wasm_exporttype_vec_delete(&export_types);
  js_set_slot_wb(js, instance_obj, SLOT_ENTRIES, exports_obj);
  if (is_object_type(import_obj)) js_set_slot_wb(js, instance_obj, SLOT_MAP, import_obj);

  *out_instance = instance_obj;
  return js_mkundef();
}

static ant_value_t js_wasm_instance_ctor(ant_t *js, ant_value_t *args, int nargs) {
  ant_value_t instance = js_mkundef();
  ant_value_t import_obj = (nargs >= 2 && is_object_type(args[1])) ? args[1] : js_mkundef();
  ant_value_t err;

  if (vtype(js->new_target) == T_UNDEF) return js_mkerr_typed(js, JS_ERR_TYPE, "WebAssembly.Instance constructor requires 'new'");
  if (nargs < 1) return js_mkerr_typed(js, JS_ERR_TYPE, "WebAssembly.Instance requires a module");

  err = wasm_instantiate_module(js, args[0], import_obj, &instance);
  if (is_err(err)) return err;
  if (vtype(instance) != T_OBJ) return js_throw(js, wasm_error_value(js, err));
  
  return instance;
}

static ant_value_t js_wasm_global_value_getter(ant_t *js, ant_value_t *args, int nargs) {
  wasm_extern_handle_t *handle = wasm_extern_handle(js->this_val, WASM_EXTERN_WRAP_GLOBAL);
  wasm_val_t value = WASM_INIT_VAL;

  if (!handle || !handle->as.global) return js_mkerr_typed(js, JS_ERR_TYPE, "Expected a WebAssembly.Global");
  if (handle->use_cached_value) return wasm_value_to_js(js, &handle->cached_value);

  wasm_global_get(handle->as.global, &value);
  return wasm_value_to_js(js, &value);
}

static ant_value_t js_wasm_global_value_setter(ant_t *js, ant_value_t *args, int nargs) {
  wasm_extern_handle_t *handle = wasm_extern_handle(js->this_val, WASM_EXTERN_WRAP_GLOBAL);
  wasm_globaltype_t *type = NULL;
  
  const wasm_valtype_t *content = NULL;
  wasm_val_t value = WASM_INIT_VAL;

  if (!handle || !handle->as.global)
    return js_mkerr_typed(js, JS_ERR_TYPE, "Expected a WebAssembly.Global");
  if (nargs < 1) return js_mkundef();

  type = wasm_global_type(handle->as.global);
  if (!type) return js_mkerr(js, "Failed to inspect WebAssembly.Global");
  if (wasm_globaltype_mutability(type) != WASM_VAR) {
    wasm_globaltype_delete(type);
    return js_mkerr_typed(js, JS_ERR_TYPE, "WebAssembly.Global is immutable");
  }

  content = wasm_globaltype_content(type);
  if (!js_value_to_wasm(js, args[0], wasm_valtype_kind(content), &value)) {
    wasm_globaltype_delete(type);
    return js_mkerr_typed(js, JS_ERR_TYPE, "Unsupported global value");
  }

  if (handle->use_cached_value) handle->cached_value = value;
  else wasm_global_set(handle->as.global, &value);
  wasm_globaltype_delete(type);
  
  return js_mkundef();
}

static ant_value_t js_wasm_global_value_of(ant_t *js, ant_value_t *args, int nargs) {
  return js_wasm_global_value_getter(js, NULL, 0);
}

static ant_value_t js_wasm_global_ctor(ant_t *js, ant_value_t *args, int nargs) {
  ant_value_t descriptor;
  ant_value_t mutable_val;
  
  const char *value_type;
  ant_offset_t value_type_len = 0;
  bool ok = false;
  
  wasm_valkind_t kind;
  wasm_store_t *store = NULL;
  wasm_valtype_t *valtype = NULL;
  wasm_globaltype_t *globaltype = NULL;
  wasm_global_t *global = NULL;
  wasm_val_t initial = WASM_INIT_VAL;
  ant_value_t result;

  if (vtype(js->new_target) == T_UNDEF)
    return js_mkerr_typed(js, JS_ERR_TYPE, "WebAssembly.Global constructor requires 'new'");
  if (nargs < 1 || !is_object_type(args[0]))
    return js_mkerr_typed(js, JS_ERR_TYPE, "WebAssembly.Global requires a descriptor object");
  if (!ensure_wasm_engine())
    return js_mkerr(js, "Failed to initialize WebAssembly engine");

  descriptor = args[0];
  value_type = get_str_prop(js, descriptor, "value", 5, &value_type_len);
  if (!value_type)
    return js_mkerr_typed(js, JS_ERR_TYPE, "WebAssembly.Global descriptor requires a value type");

  kind = wasm_valkind_from_string(value_type, value_type_len, &ok);
  if (!ok)
    return js_mkerr_typed(js, JS_ERR_TYPE, "Unsupported WebAssembly.Global value type");

  if (!js_value_to_wasm(js, nargs >= 2 ? args[1] : js_mknum(0), kind, &initial))
    return js_mkerr_typed(js, JS_ERR_TYPE, "Unsupported WebAssembly.Global initial value");

  mutable_val = js_get(js, descriptor, "mutable");
  store = wasm_store_new(g_wasm_engine);
  if (!store) return js_mkerr(js, "Failed to create WebAssembly store");

  valtype = wasm_valtype_new(kind);
  globaltype = wasm_globaltype_new(valtype, js_truthy(js, mutable_val) ? WASM_VAR : WASM_CONST);
  global = globaltype ? wasm_global_new(store, globaltype, &initial) : NULL;

  wasm_globaltype_delete(globaltype);

  if (!global) {
    wasm_store_delete(store);
    return js_throw(js, wasm_make_runtime_error(js, "Failed to create WebAssembly.Global"));
  }

  result = wasm_wrap_extern_object(
    js, WASM_EXTERN_WRAP_GLOBAL, g_wasm_global_proto, BRAND_WASM_GLOBAL,
    store, true, global, js_mkundef()
  );
  if (vtype(result) == T_OBJ) {
    wasm_extern_handle_t *handle = wasm_extern_handle(result, WASM_EXTERN_WRAP_GLOBAL);
    if (handle) {
      handle->use_cached_value = true;
      handle->cached_value = initial;
    }
    js_set_finalizer(result, wasm_extern_finalize);
  }
  return result;
}

static ant_value_t js_wasm_memory_buffer_getter(ant_t *js, ant_value_t *args, int nargs) {
  wasm_extern_handle_t *handle = wasm_extern_handle(js->this_val, WASM_EXTERN_WRAP_MEMORY);
  byte_t *data; size_t len; ArrayBufferData *buffer;

  if (!handle || !handle->as.memory)
    return js_mkerr_typed(js, JS_ERR_TYPE, "Expected a WebAssembly.Memory");

  data = wasm_memory_data(handle->as.memory);
  len = wasm_memory_data_size(handle->as.memory);
  buffer = calloc(1, sizeof(ArrayBufferData));
  
  if (!buffer) return js_mkerr(js, "out of memory");
  buffer->data = (uint8_t *)data;
  buffer->length = len;
  buffer->capacity = len;
  buffer->ref_count = 1;
  
  return create_arraybuffer_obj(js, buffer);
}

static ant_value_t js_wasm_memory_grow(ant_t *js, ant_value_t *args, int nargs) {
  wasm_extern_handle_t *handle = wasm_extern_handle(js->this_val, WASM_EXTERN_WRAP_MEMORY);
  wasm_memory_pages_t old_size;
  uint32_t delta;

  if (!handle || !handle->as.memory)
    return js_mkerr_typed(js, JS_ERR_TYPE, "Expected a WebAssembly.Memory");

  delta = (uint32_t)(nargs > 0 ? js_to_number(js, args[0]) : 0);
  old_size = wasm_memory_size(handle->as.memory);

  if (delta == 0) return js_mknum((double)old_size);

  wasm_module_inst_t inst = (wasm_module_inst_t)handle->as.memory->inst_comm_rt;
  if (!inst)
    return js_mkerr_typed(js, JS_ERR_RANGE, "Memory instance not available");

  if (inst->module_type == Wasm_Module_Bytecode) {
    WASMModuleInstance *wasm_inst = (WASMModuleInstance *)inst;
    WASMMemoryInstance *mem_inst = wasm_inst->memories[handle->as.memory->memory_idx_rt];
    uint32_t needed = mem_inst->cur_page_count + delta;
    if (needed > mem_inst->max_page_count) mem_inst->max_page_count = needed;
  }

  if (!wasm_runtime_enlarge_memory(inst, (uint64_t)delta))
    return js_mkerr_typed(js, JS_ERR_RANGE, "Failed to grow memory by %u pages", delta);

  return js_mknum((double)old_size);
}

static ant_value_t js_wasm_memory_ctor(ant_t *js, ant_value_t *args, int nargs) {
  if (vtype(js->new_target) == T_UNDEF) return js_mkerr_typed(js, JS_ERR_TYPE, "WebAssembly.Memory constructor requires 'new'");
  return js_mkerr_typed(js, JS_ERR_TYPE, "The current WAMR backend does not support standalone WebAssembly.Memory");
}

static ant_value_t js_wasm_table_length_getter(ant_t *js, ant_value_t *args, int nargs) {
  wasm_extern_handle_t *handle = wasm_extern_handle(js->this_val, WASM_EXTERN_WRAP_TABLE);
  if (!handle || !handle->as.table)  return js_mkerr_typed(js, JS_ERR_TYPE, "Expected a WebAssembly.Table");
  return js_mknum((double)wasm_table_size(handle->as.table));
}

static ant_value_t js_wasm_table_get(ant_t *js, ant_value_t *args, int nargs) {
  wasm_extern_handle_t *handle = wasm_extern_handle(js->this_val, WASM_EXTERN_WRAP_TABLE);
  wasm_ref_t *ref;
  wasm_func_t *func;
  uint32_t index;

  if (!handle || !handle->as.table)
    return js_mkerr_typed(js, JS_ERR_TYPE, "Expected a WebAssembly.Table");

  index = (uint32_t)(nargs > 0 ? js_to_number(js, args[0]) : 0);
  ref = wasm_table_get(handle->as.table, index);

  if (!ref) return js_mknull();
  func = wasm_ref_as_func(ref);
  
  if (func) {
    ant_value_t owner = js_get_slot(js->this_val, SLOT_ENTRIES);
    ant_value_t wrapped = wasm_wrap_func(js, func, owner, true);
    wasm_ref_delete(ref);
    return wrapped;
  }
  
  wasm_ref_delete(ref);
  return js_mknull();
}

static ant_value_t js_wasm_table_set(ant_t *js, ant_value_t *args, int nargs) {
  wasm_extern_handle_t *handle = wasm_extern_handle(js->this_val, WASM_EXTERN_WRAP_TABLE);
  wasm_ref_t *ref = NULL;
  uint32_t index;

  if (!handle || !handle->as.table) return js_mkerr_typed(js, JS_ERR_TYPE, "Expected a WebAssembly.Table");
  if (nargs < 2) return js_mkerr_typed(js, JS_ERR_TYPE, "WebAssembly.Table.set requires 2 arguments");

  index = (uint32_t)js_to_number(js, args[0]);
  if (!(vtype(args[1]) == T_NULL || vtype(args[1]) == T_UNDEF)) {
    ant_value_t state;
    wasm_func_handle_t *func_handle;
    
    if (!is_callable(args[1]))
      return js_mkerr_typed(js, JS_ERR_TYPE, "WebAssembly.Table.set expects a WebAssembly function or null");
    
    state = js_get_slot(args[1], SLOT_DATA);
    if (!is_object_type(state) || !js_check_native_tag(state, WASM_FUNC_STATE_TAG))
      return js_mkerr_typed(js, JS_ERR_TYPE, "WebAssembly.Table.set expects a WebAssembly function or null");
    
    func_handle = (wasm_func_handle_t *)js_get_native_ptr(state);
    if (!func_handle || !func_handle->func)
      return js_mkerr_typed(js, JS_ERR_TYPE, "WebAssembly.Table.set expects a WebAssembly function or null");
    
    ref = wasm_func_as_ref(func_handle->func);
    if (!ref) return js_mkerr_typed(js, JS_ERR_TYPE, "Failed to materialize WebAssembly function reference");
  }

  if (!wasm_table_set(handle->as.table, index, ref)) {
    if (ref) wasm_ref_delete(ref);
    return js_mkerr_typed(js, JS_ERR_TYPE, "Failed to update WebAssembly.Table");
  }
  
  return js_mkundef();
}

static ant_value_t js_wasm_table_grow(ant_t *js, ant_value_t *args, int nargs) {
  return js_mkerr_typed(js, JS_ERR_TYPE, "The current WAMR backend does not support host-side table.grow");
}

static ant_value_t js_wasm_table_ctor(ant_t *js, ant_value_t *args, int nargs) {
  if (vtype(js->new_target) == T_UNDEF) return js_mkerr_typed(js, JS_ERR_TYPE, "WebAssembly.Table constructor requires 'new'");
  return js_mkerr_typed(js, JS_ERR_TYPE, "The current WAMR backend does not support standalone WebAssembly.Table");
}

static ant_value_t js_wasm_tag_ctor(ant_t *js, ant_value_t *args, int nargs) {
  return js_mkerr_typed(js, JS_ERR_TYPE, "The current WAMR backend does not expose WebAssembly.Tag");
}

static ant_value_t js_wasm_exception_ctor(ant_t *js, ant_value_t *args, int nargs) {
  return js_mkerr_typed(js, JS_ERR_TYPE, "The current WAMR backend does not expose WebAssembly.Exception");
}

static ant_value_t js_wasm_validate(ant_t *js, ant_value_t *args, int nargs) {
  wasm_byte_vec_t binary = WASM_EMPTY_VEC;
  wasm_store_t *store;
  
  bool ok;
  char error_buf[128] = {0};
  bool suppress_wasi_warning = false;

  if (nargs < 1) return js_false;
  if (!ensure_wasm_engine()) return js_false;
  if (!(store = wasm_store_new(g_wasm_engine))) return js_false;
  
  if (!wasm_buffer_source_to_vec(js, args[0], &binary, error_buf, sizeof(error_buf))) {
    wasm_store_delete(store);
    return js_false;
  }

  suppress_wasi_warning = wasi_bytes_need_wasi_command_warning_suppression(
    (const uint8_t *)binary.data, binary.size
  );
  
  if (suppress_wasi_warning) wasm_runtime_set_log_level(WASM_LOG_LEVEL_ERROR);
  ok = wasm_module_validate(store, &binary);
  
  if (suppress_wasi_warning) wasm_runtime_set_log_level(WASM_LOG_LEVEL_WARNING);
  wasm_byte_vec_delete(&binary);
  wasm_store_delete(store);
  
  return js_bool(ok);
}

static ant_value_t js_wasm_compile(ant_t *js, ant_value_t *args, int nargs) {
  ant_value_t promise = js_mkpromise(js);
  ant_value_t module = js_mkundef();
  ant_value_t err;

  if (nargs < 1) {
    wasm_reject_with_error(js, promise, js_mkerr_typed(js, JS_ERR_TYPE, "WebAssembly.compile requires 1 argument"));
    return promise;
  }

  err = wasm_module_from_bytes(js, args[0], &module);
  if (is_err(err) || vtype(module) != T_OBJ) {
    wasm_reject_with_error(js, promise, wasm_error_value(js, err));
    return promise;
  }

  js_resolve_promise(js, promise, module);
  return promise;
}

static ant_value_t js_wasm_instantiate(ant_t *js, ant_value_t *args, int nargs) {
  ant_value_t promise = js_mkpromise(js);
  ant_value_t module = js_mkundef();
  ant_value_t instance = js_mkundef();
  ant_value_t import_obj = (nargs >= 2 && is_object_type(args[1])) ? args[1] : js_mkundef();
  ant_value_t err;

  if (nargs < 1) {
    wasm_reject_with_error(js, promise, js_mkerr_typed(js, JS_ERR_TYPE, "WebAssembly.instantiate requires 1 argument"));
    return promise;
  }

  if (wasm_module_handle(args[0])) {
    err = wasm_instantiate_module(js, args[0], import_obj, &instance);
    if (is_err(err) || vtype(instance) != T_OBJ) {
      wasm_reject_with_error(js, promise, wasm_error_value(js, err));
      return promise;
    }
    js_resolve_promise(js, promise, instance);
    return promise;
  }

  err = wasm_module_from_bytes(js, args[0], &module);
  if (is_err(err) || vtype(module) != T_OBJ) {
    wasm_reject_with_error(js, promise, wasm_error_value(js, err));
    return promise;
  }

  err = wasm_instantiate_module(js, module, import_obj, &instance);
  if (is_err(err) || vtype(instance) != T_OBJ) {
    wasm_reject_with_error(js, promise, wasm_error_value(js, err));
    return promise;
  }

  ant_value_t result = js_mkobj(js);
  js_set(js, result, "module", module);
  js_set(js, result, "instance", instance);
  js_resolve_promise(js, promise, result);
  
  return promise;
}

static ant_value_t js_wasm_compile_error_ctor(ant_t *js, ant_value_t *args, int nargs) {
  ant_value_t msg = (nargs > 0) ? js_tostring_val(js, args[0]) : js_mkstr(js, "", 0);
  if (is_err(msg)) return msg;
  return wasm_make_error(js, g_wasm_compileerror_proto, "CompileError", js_str(js, msg));
}

static ant_value_t js_wasm_link_error_ctor(ant_t *js, ant_value_t *args, int nargs) {
  ant_value_t msg = (nargs > 0) ? js_tostring_val(js, args[0]) : js_mkstr(js, "", 0);
  if (is_err(msg)) return msg;
  return wasm_make_error(js, g_wasm_linkerror_proto, "LinkError", js_str(js, msg));
}

static ant_value_t js_wasm_runtime_error_ctor(ant_t *js, ant_value_t *args, int nargs) {
  ant_value_t msg = (nargs > 0) ? js_tostring_val(js, args[0]) : js_mkstr(js, "", 0);
  if (is_err(msg)) return msg;
  return wasm_make_error(js, g_wasm_runtimeerror_proto, "RuntimeError", js_str(js, msg));
}

void gc_mark_wasm(ant_t *js, gc_mark_fn mark) {
  for (size_t i = 0; i < g_wasm_import_env_count; i++) {
    wasm_import_func_env_t *env = g_wasm_import_envs[i];
    mark(js, env->fn);
    mark(js, env->owner);
  }
  
  if (g_wasm_pending_import_throw_exists)
    mark(js, g_wasm_pending_import_throw);
}

void init_wasm_module(void) {
  ant_t *js = rt->js;
  ant_value_t global = js_glob(js);
  
  ant_value_t error_proto = js_get_ctor_proto(js, "Error", 5);
  ant_value_t ns = js_mkobj(js);

  if (!ensure_wasm_engine()) return;

  g_wasm_module_proto = js_mkobj(js);
  g_wasm_instance_proto = js_mkobj(js);
  g_wasm_global_proto = js_mkobj(js);
  g_wasm_memory_proto = js_mkobj(js);
  g_wasm_table_proto = js_mkobj(js);
  g_wasm_tag_proto = js_mkobj(js);
  g_wasm_exception_proto = js_mkobj(js);

  g_wasm_compileerror_proto = js_mkobj(js);
  g_wasm_linkerror_proto = js_mkobj(js);
  g_wasm_runtimeerror_proto = js_mkobj(js);

  js_set_proto_init(g_wasm_module_proto, js->sym.object_proto);
  js_set_proto_init(g_wasm_instance_proto, js->sym.object_proto);
  js_set_proto_init(g_wasm_global_proto, js->sym.object_proto);
  js_set_proto_init(g_wasm_memory_proto, js->sym.object_proto);
  js_set_proto_init(g_wasm_table_proto, js->sym.object_proto);
  js_set_proto_init(g_wasm_tag_proto, js->sym.object_proto);
  js_set_proto_init(g_wasm_exception_proto, js->sym.object_proto);

  js_set_proto_init(g_wasm_compileerror_proto, error_proto);
  js_set_proto_init(g_wasm_linkerror_proto, error_proto);
  js_set_proto_init(g_wasm_runtimeerror_proto, error_proto);

  js_set(js, g_wasm_global_proto, "valueOf", js_mkfun(js_wasm_global_value_of));
  js_set_getter_desc(js, g_wasm_global_proto, "value", 5, js_mkfun(js_wasm_global_value_getter), JS_DESC_C);
  js_set_setter_desc(js, g_wasm_global_proto, "value", 5, js_mkfun(js_wasm_global_value_setter), JS_DESC_C);

  js_set_getter_desc(js, g_wasm_instance_proto, "exports", 7, js_mkfun(js_wasm_instance_exports_getter), JS_DESC_C);
  js_set_getter_desc(js, g_wasm_memory_proto, "buffer", 6, js_mkfun(js_wasm_memory_buffer_getter), JS_DESC_C);
  js_set(js, g_wasm_memory_proto, "grow", js_mkfun(js_wasm_memory_grow));

  js_set_getter_desc(js, g_wasm_table_proto, "length", 6, js_mkfun(js_wasm_table_length_getter), JS_DESC_C);
  js_set(js, g_wasm_table_proto, "get", js_mkfun(js_wasm_table_get));
  js_set(js, g_wasm_table_proto, "set", js_mkfun(js_wasm_table_set));
  js_set(js, g_wasm_table_proto, "grow", js_mkfun(js_wasm_table_grow));

  ant_value_t module_ctor = js_make_ctor(js, js_wasm_module_ctor, g_wasm_module_proto, "Module", 6);
  ant_value_t instance_ctor = js_make_ctor(js, js_wasm_instance_ctor, g_wasm_instance_proto, "Instance", 8);
  ant_value_t global_ctor = js_make_ctor(js, js_wasm_global_ctor, g_wasm_global_proto, "Global", 6);
  ant_value_t memory_ctor = js_make_ctor(js, js_wasm_memory_ctor, g_wasm_memory_proto, "Memory", 6);
  ant_value_t table_ctor = js_make_ctor(js, js_wasm_table_ctor, g_wasm_table_proto, "Table", 5);
  ant_value_t tag_ctor = js_make_ctor(js, js_wasm_tag_ctor, g_wasm_tag_proto, "Tag", 3);
  ant_value_t exception_ctor = js_make_ctor(js, js_wasm_exception_ctor, g_wasm_exception_proto, "Exception", 9);

  ant_value_t compile_error_ctor = js_make_ctor(js, js_wasm_compile_error_ctor, g_wasm_compileerror_proto, "CompileError", 12);
  ant_value_t link_error_ctor = js_make_ctor(js, js_wasm_link_error_ctor, g_wasm_linkerror_proto, "LinkError", 9);
  ant_value_t runtime_error_ctor = js_make_ctor(js, js_wasm_runtime_error_ctor, g_wasm_runtimeerror_proto, "RuntimeError", 12);

  js_set(js, module_ctor, "imports", js_mkfun(js_wasm_module_imports));
  js_set(js, module_ctor, "exports", js_mkfun(js_wasm_module_exports));

  js_set(js, ns, "validate", js_mkfun(js_wasm_validate));
  js_set(js, ns, "compile", js_mkfun(js_wasm_compile));
  js_set(js, ns, "instantiate", js_mkfun(js_wasm_instantiate));

  js_set(js, ns, "Module", module_ctor);
  js_set(js, ns, "Instance", instance_ctor);
  js_set(js, ns, "Global", global_ctor);
  js_set(js, ns, "Memory", memory_ctor);
  js_set(js, ns, "Table", table_ctor);
  js_set(js, ns, "Tag", tag_ctor);
  js_set(js, ns, "Exception", exception_ctor);
  js_set(js, ns, "CompileError", compile_error_ctor);
  js_set(js, ns, "LinkError", link_error_ctor);
  js_set(js, ns, "RuntimeError", runtime_error_ctor);

  js_set(js, global, "WebAssembly", ns);
  js_set_descriptor(js, global, "WebAssembly", 11, JS_DESC_W | JS_DESC_C);
}
