/// Wasmoon-owned native JIT runtime primitives and managed handles.

///|
/// Read an i64-sized slot from a JIT-owned native value area.
pub fn native_value_slot_read(base : Int64, slot : Int) -> Int64 {
  c_jit_read_i64(base + (slot * 8).to_int64())
}

///|
/// Write an i64-sized slot in a JIT-owned native value area.
pub fn native_value_slot_write(base : Int64, slot : Int, value : Int64) -> Unit {
  c_jit_write_i64(base + (slot * 8).to_int64(), value)
}

///|
pub fn memory_descriptor_length(mem_desc_ptr : Int64) -> Int64 {
  c_mem_desc_get_len(mem_desc_ptr)
}

///|
pub fn memory_descriptor_grow(
  mem_desc_ptr : Int64,
  delta_pages : Int,
  max_pages : Int,
) -> Int {
  c_mem_desc_grow(mem_desc_ptr, delta_pages, max_pages)
}

///|
#borrow(out)
pub fn memory_descriptor_read(
  mem_desc_ptr : Int64,
  offset : Int64,
  out : FixedArray[Byte],
  size : Int,
) -> Int {
  c_mem_desc_read(mem_desc_ptr, offset, out, size)
}

///|
#borrow(data)
pub fn memory_descriptor_write(
  mem_desc_ptr : Int64,
  offset : Int64,
  data : FixedArray[Byte],
  size : Int,
) -> Int {
  c_mem_desc_write(mem_desc_ptr, offset, data, size)
}

///|
#borrow(data)
pub fn memory_descriptor_write_bytes(
  mem_desc_ptr : Int64,
  offset : Int64,
  data : Bytes,
  size : Int,
) -> Int {
  c_mem_desc_write_bytes(mem_desc_ptr, offset, data, size)
}

///|
pub fn memory_descriptor_move(
  mem_desc_ptr : Int64,
  destination : Int64,
  source : Int64,
  size : Int,
) -> Int {
  c_mem_desc_memmove(mem_desc_ptr, destination, source, size)
}

///|
pub fn memory_descriptor_fill(
  mem_desc_ptr : Int64,
  destination : Int64,
  value : Int,
  size : Int,
) -> Int {
  c_mem_desc_memset(mem_desc_ptr, destination, value, size)
}

///|
priv type ExecCodeHandle

///|
#borrow(code)
extern "c" fn alloc_exec_managed(
  code : FixedArray[Byte],
  size : Int,
) -> ExecCodeHandle = "wasmoon_jit_alloc_exec_managed"

///|
extern "c" fn stage_exec_managed(size : Int) -> ExecCodeHandle = "wasmoon_jit_stage_exec_managed"

///|
#borrow(exec_code, code)
extern "c" fn finalize_exec_managed(
  exec_code : ExecCodeHandle,
  code : FixedArray[Byte],
  size : Int,
) -> Int = "wasmoon_jit_finalize_exec_managed"

///|
#borrow(exec_code)
extern "c" fn release_exec_managed(exec_code : ExecCodeHandle) -> Int = "wasmoon_jit_release_exec_managed"

///|
#owned(exec_code)
extern "c" fn exec_code_pointer(exec_code : ExecCodeHandle) -> Int64 = "wasmoon_jit_exec_code_ptr"

///|
/// Low-level trap detail captured by the JIT signal handlers (best-effort).
pub struct TrapDetails {
  signal : Int
  pc : Int64
  lr : Int64
  fp : Int64
  frame_lr : Int64
  fault_addr : Int64
  brk_imm : Int
  func_idx : Int
  x0 : Int64
  x1 : Int64
  x2 : Int64
  x3 : Int64
  x6 : Int64
  x7 : Int64
  x8 : Int64
  x9 : Int64
  x10 : Int64
  x11 : Int64
  x15 : Int64
}

///|
pub fn TrapDetails::TrapDetails() -> TrapDetails {
  {
    signal: 0,
    pc: 0L,
    lr: 0L,
    fp: 0L,
    frame_lr: 0L,
    fault_addr: 0L,
    brk_imm: 0,
    func_idx: -1,
    x0: 0L,
    x1: 0L,
    x2: 0L,
    x3: 0L,
    x6: 0L,
    x7: 0L,
    x8: 0L,
    x9: 0L,
    x10: 0L,
    x11: 0L,
    x15: 0L,
  }
}

///|
/// Get the last captured trap details (best-effort).
/// These fields are reset at the start of each JIT call.
pub fn get_last_trap_details() -> TrapDetails {
  {
    signal: c_jit_get_trap_signal(),
    pc: c_jit_get_trap_pc(),
    lr: c_jit_get_trap_lr(),
    fp: c_jit_get_trap_fp(),
    frame_lr: c_jit_get_trap_frame_lr(),
    fault_addr: c_jit_get_trap_fault_addr(),
    brk_imm: c_jit_get_trap_brk_imm(),
    func_idx: c_jit_get_trap_func_idx(),
    x0: c_jit_get_trap_xreg(0),
    x1: c_jit_get_trap_xreg(1),
    x2: c_jit_get_trap_xreg(2),
    x3: c_jit_get_trap_xreg(3),
    x6: c_jit_get_trap_xreg(6),
    x7: c_jit_get_trap_xreg(7),
    x8: c_jit_get_trap_xreg(8),
    x9: c_jit_get_trap_xreg(9),
    x10: c_jit_get_trap_xreg(10),
    x11: c_jit_get_trap_xreg(11),
    x15: c_jit_get_trap_xreg(15),
  }
}

///|
pub fn format_signal_name(sig : Int) -> String {
  match sig {
    5 => "SIGTRAP(5)"
    10 => "SIGBUS(10)"
    11 => "SIGSEGV(11)"
    7 => "SIGBUS(7)"
    _ => "SIG(\{sig})"
  }
}

///|
pub fn jit_trap_message(code : Int) -> String {
  match code {
    1 => "out of bounds memory access"
    2 => "call stack exhausted"
    3 => "unreachable"
    4 => "indirect call type mismatch"
    5 => "invalid conversion to integer"
    6 => "integer divide by zero"
    7 => "integer overflow"
    8 => "jit backend trap"
    9 => "out of memory"
    10 => "gc precise roots unavailable"
    11 => "invocation cancelled"
    _ => "unknown trap"
  }
}

///|
pub fn is_cancelled_trap_code(code : Int) -> Bool {
  code == 11
}

///|
/// Map Wasm trap reasons to the payload consumed by Wasmoon's native trap
/// handler. MachV only carries the integer payload; this module owns the
/// Wasmoon-specific reason-to-payload policy.
pub fn wasm_trap_payload(reason : String) -> Int {
  if reason == "unreachable" {
    0
  } else if reason.contains("out of bounds") {
    1
  } else if reason.contains("type mismatch") {
    2
  } else if reason.contains("invalid conversion") {
    3
  } else if reason.contains("divide by zero") {
    4
  } else if reason.contains("overflow") {
    5
  } else {
    6
  }
}

///|
pub fn take_pending_trap_message() -> String? {
  let code = c_jit_get_trap_code()
  if code == 0 {
    None
  } else {
    c_jit_clear_trap()
    Some(jit_trap_message(code))
  }
}

///|
pub fn is_wasi_exit_trap_code(code : Int) -> Bool {
  code == 100
}

///|
pub fn take_wasi_exit_code(ctx_ptr : Int64) -> Int {
  let raw_code = c_jit_get_wasi_exit_code(ctx_ptr)
  c_jit_clear_wasi_exit(ctx_ptr)
  if raw_code < 0 {
    0
  } else {
    raw_code
  }
}

///|
pub fn make_cstring(s : String) -> FixedArray[Byte] {
  let len = s.length()
  let mut size = 0
  let mut i = 0
  while i < len {
    let c = s.code_unit_at(i).to_int()
    if c < 0x80 {
      size += 1
      i += 1
    } else if c < 0x800 {
      size += 2
      i += 1
    } else if c >= 0xD800 && c <= 0xDBFF && i + 1 < len {
      let c2 = s.code_unit_at(i + 1).to_int()
      if c2 >= 0xDC00 && c2 <= 0xDFFF {
        size += 4
        i += 2
      } else {
        size += 3
        i += 1
      }
    } else {
      size += 3
      i += 1
    }
  }
  let result = FixedArray::make(size + 1, b'\x00')
  let mut pos = 0
  i = 0
  while i < len {
    let c = s.code_unit_at(i).to_int()
    if c < 0x80 {
      result[pos] = c.to_byte()
      pos += 1
      i += 1
    } else if c < 0x800 {
      result[pos] = (0xC0 | (c >> 6)).to_byte()
      result[pos + 1] = (0x80 | (c & 0x3F)).to_byte()
      pos += 2
      i += 1
    } else if c >= 0xD800 && c <= 0xDBFF && i + 1 < len {
      let c2 = s.code_unit_at(i + 1).to_int()
      if c2 >= 0xDC00 && c2 <= 0xDFFF {
        let codepoint = 0x10000 + ((c - 0xD800) << 10) + (c2 - 0xDC00)
        result[pos] = (0xF0 | (codepoint >> 18)).to_byte()
        result[pos + 1] = (0x80 | ((codepoint >> 12) & 0x3F)).to_byte()
        result[pos + 2] = (0x80 | ((codepoint >> 6) & 0x3F)).to_byte()
        result[pos + 3] = (0x80 | (codepoint & 0x3F)).to_byte()
        pos += 4
        i += 2
      } else {
        result[pos] = b'\xEF'
        result[pos + 1] = b'\xBF'
        result[pos + 2] = b'\xBD'
        pos += 3
        i += 1
      }
    } else {
      result[pos] = (0xE0 | (c >> 12)).to_byte()
      result[pos + 1] = (0x80 | ((c >> 6) & 0x3F)).to_byte()
      result[pos + 2] = (0x80 | (c & 0x3F)).to_byte()
      pos += 3
      i += 1
    }
  }
  result[size] = b'\x00'
  result
}

///|
pub fn set_wasi_stdout_capture(ctx_ptr : Int64, enabled : Bool) -> Unit {
  c_jit_set_wasi_stdout_capture(ctx_ptr, if enabled { 1 } else { 0 })
}

///|
pub fn set_wasi_stderr_capture(ctx_ptr : Int64, enabled : Bool) -> Unit {
  c_jit_set_wasi_stderr_capture(ctx_ptr, if enabled { 1 } else { 0 })
}

///|
pub fn set_wasi_stdin_buffer(ctx_ptr : Int64, data : Bytes) -> Unit {
  c_jit_set_wasi_stdin_buffer(ctx_ptr, data, data.length())
}

///|
pub fn set_wasi_stdin_callback(ctx_ptr : Int64, callback : () -> Bytes) -> Unit {
  let call_closure : FuncRef[(() -> Bytes) -> Bytes] = fn(f : () -> Bytes) {
    f()
  }
  c_jit_set_wasi_stdin_callback(ctx_ptr, call_closure, callback)
}

///|
pub fn clear_wasi_stdin_callback(ctx_ptr : Int64) -> Unit {
  c_jit_clear_wasi_stdin_callback(ctx_ptr)
}

///|
pub fn clear_wasi_stdin_buffer(ctx_ptr : Int64) -> Unit {
  c_jit_clear_wasi_stdin_buffer(ctx_ptr)
}

///|
pub fn take_wasi_stdout(ctx_ptr : Int64) -> Bytes {
  c_jit_take_wasi_stdout(ctx_ptr)
}

///|
pub fn take_wasi_stderr(ctx_ptr : Int64) -> Bytes {
  c_jit_take_wasi_stderr(ctx_ptr)
}

///|
pub fn init_wasi_context(
  ctx_ptr : Int64,
  args : Array[String],
  env : Array[String],
  preopens : Array[(String, String)],
  quiet? : Bool = false,
) -> Unit {
  if quiet {
    c_jit_init_wasi_fds_quiet(ctx_ptr, preopens.length())
  } else {
    c_jit_init_wasi_fds(ctx_ptr, preopens.length())
  }
  set_wasi_stdout_capture(ctx_ptr, false)
  set_wasi_stderr_capture(ctx_ptr, false)
  clear_wasi_stdin_callback(ctx_ptr)
  clear_wasi_stdin_buffer(ctx_ptr)
  c_jit_set_wasi_args(ctx_ptr, args.length())
  for i, arg in args {
    c_jit_set_wasi_arg(ctx_ptr, i, make_cstring(arg))
  }
  c_jit_set_wasi_envs(ctx_ptr, env.length())
  for i, e in env {
    c_jit_set_wasi_env(ctx_ptr, i, make_cstring(e))
  }
  for i, entry in preopens {
    let (host, guest) = entry
    c_jit_add_preopen(ctx_ptr, i, make_cstring(host), make_cstring(guest))
  }
}

///|
pub fn context_func_table_ptr(ctx_ptr : Int64) -> Int64 {
  c_jit_ctx_get_func_table(ctx_ptr)
}

///|
pub fn context_func_count(ctx_ptr : Int64) -> Int {
  c_jit_ctx_get_func_count(ctx_ptr)
}

///|
pub fn context_table_ptr(ctx_ptr : Int64, table_idx : Int) -> Int64 {
  c_jit_ctx_get_table_ptr(ctx_ptr, table_idx)
}

///|
pub fn context_table_size(ctx_ptr : Int64, table_idx : Int) -> Int {
  c_jit_ctx_get_table_size(ctx_ptr, table_idx)
}

///|
pub fn throw_exception_tag(ctx_ptr : Int64, tag_addr : Int) -> Unit {
  c_jit_exception_throw_tag(ctx_ptr, tag_addr)
}

///|
pub fn throw_exception_values(
  ctx_ptr : Int64,
  tag_addr : Int,
  values : Array[Int64],
) -> Unit {
  let payload = FixedArray::makei(values.length(), fn(i) { values[i] })
  c_jit_exception_throw_values(ctx_ptr, tag_addr, payload, values.length())
}

///|
pub fn setup_segments(
  ctx_ptr : Int64,
  datas : Array[@types.Data],
  elem_segments : Array[Array[Int64]],
  data_dropped? : Array[Bool] = [],
  elem_dropped? : Array[Bool] = [],
) -> Unit {
  c_jit_ctx_init_data_segments(ctx_ptr, datas.length())
  for i, data in datas {
    let bytes = data.init
    let size = bytes.length()
    let dropped = if i < data_dropped.length() && data_dropped[i] {
      1
    } else {
      0
    }
    let buf = FixedArray::make(size.max(1), b'\x00')
    for j in 0.. Unit {
  c_jit_ctx_clear_segments(ctx_ptr)
}

///|
struct NativeJITContext(JITContext)

///|
pub fn NativeJITContext::try_alloc(total_funcs : Int) -> NativeJITContext? {
  let ctx = c_jit_alloc_context_managed(total_funcs)
  if c_jit_context_ptr(ctx) != 0L {
    Some(NativeJITContext(ctx))
  } else {
    None
  }
}

///|
pub fn NativeJITContext::ptr(self : NativeJITContext) -> Int64 {
  c_jit_context_ptr(self.0)
}

///|
pub fn NativeJITContext::set_func(
  self : NativeJITContext,
  idx : Int,
  func_ptr : Int64,
) -> Unit {
  c_jit_ctx_set_func(self.ptr(), idx, func_ptr)
}

///|
pub fn NativeJITContext::alloc_indirect_table(
  self : NativeJITContext,
  count : Int,
) -> Bool {
  c_jit_ctx_alloc_indirect_table(self.ptr(), count) != 0
}

///|
pub fn NativeJITContext::set_indirect(
  self : NativeJITContext,
  table_idx : Int,
  func_idx : Int,
  type_idx : Int,
) -> Unit {
  c_jit_ctx_set_indirect(self.ptr(), table_idx, func_idx, type_idx)
}

///|
pub fn NativeJITContext::set_memory(
  self : NativeJITContext,
  mem0_ptr : Int64,
) -> Unit {
  c_jit_ctx_set_memory(self.ptr(), mem0_ptr)
}

///|
pub fn NativeJITContext::set_globals(
  self : NativeJITContext,
  globals_ptr : Int64,
) -> Unit {
  c_jit_ctx_set_globals(self.ptr(), globals_ptr)
}

///|
pub fn NativeJITContext::alloc_guarded_memory(
  self : NativeJITContext,
  initial_pages : Int,
  max_pages : Int?,
) -> Int64 {
  c_jit_ctx_alloc_guarded_memory(
    self.ptr(),
    initial_pages.to_int64(),
    max_pages.unwrap_or(-1).to_int64(),
  )
}

///|
pub fn NativeJITContext::func_table_ptr(self : NativeJITContext) -> Int64 {
  context_func_table_ptr(self.ptr())
}

///|
pub fn NativeJITContext::set_table_pointers(
  self : NativeJITContext,
  jit_tables : Array[JITTable?],
) -> Unit {
  if jit_tables.is_empty() {
    return
  }
  let table_ptrs = FixedArray::make(jit_tables.length(), 0L)
  let table_sizes = FixedArray::make(jit_tables.length(), 0)
  let table_max_sizes = FixedArray::make(jit_tables.length(), -1)
  for i, jit_table_opt in jit_tables {
    match jit_table_opt {
      Some(jit_table) => {
        table_ptrs[i] = jit_table.raw_ptr()
        table_sizes[i] = jit_table.get_size()
        table_max_sizes[i] = jit_table.get_max().unwrap_or(-1)
      }
      None => ()
    }
  }
  c_jit_ctx_set_table_pointers(
    self.ptr(),
    table_ptrs,
    table_sizes,
    table_max_sizes,
    jit_tables.length(),
  )
}

///|
pub fn NativeJITContext::set_memory_pointers(
  self : NativeJITContext,
  memories : Array[MemoryInfo],
) -> Unit {
  if memories.is_empty() {
    return
  }
  let ptrs = FixedArray::make(memories.length(), 0L)
  for i, mem in memories {
    ptrs[i] = mem.ptr
  }
  c_jit_ctx_set_memory_pointers(self.ptr(), ptrs, memories.length())
}

///|
pub fn NativeJITContext::memory_ptr(
  self : NativeJITContext,
  memidx : Int,
) -> Int64 {
  c_jit_ctx_get_memory_ptr(self.ptr(), memidx)
}

///|
pub fn NativeJITContext::memory_size(
  self : NativeJITContext,
  memidx : Int,
) -> Int64 {
  c_jit_ctx_get_memory_size(self.ptr(), memidx)
}

///|
pub fn NativeJITContext::alloc_wasm_stack(
  self : NativeJITContext,
  stack_size : Int64,
) -> Bool {
  c_jit_alloc_wasm_stack(self.ptr(), stack_size) == 0
}

///|
pub fn NativeJITContext::has_wasm_stack(self : NativeJITContext) -> Bool {
  c_jit_get_wasm_stack_top(self.ptr()) != 0L
}

///|
pub fn NativeJITContext::set_hostcall_callback(
  self : NativeJITContext,
  callback : () -> Int,
) -> Unit {
  let call_closure : FuncRef[(() -> Int) -> Int] = fn(f : () -> Int) { f() }
  c_jit_set_hostcall_callback(self.ptr(), call_closure, callback)
}

///|
pub fn NativeJITContext::clear_hostcall_callback(
  self : NativeJITContext,
) -> Unit {
  c_jit_clear_hostcall_callback(self.ptr())
}

///|
pub fn NativeJITContext::set_cancellation_callback(
  self : NativeJITContext,
  callback : () -> Bool,
) -> Unit {
  let call_closure : FuncRef[(() -> Bool) -> Int] = fn(f : () -> Bool) {
    if f() {
      1
    } else {
      0
    }
  }
  c_jit_set_cancellation_callback(self.ptr(), call_closure, callback)
}

///|
pub fn NativeJITContext::clear_cancellation_callback(
  self : NativeJITContext,
) -> Unit {
  c_jit_clear_cancellation_callback(self.ptr())
}

///|
pub fn NativeJITContext::call_trampoline(
  self : NativeJITContext,
  trampoline_ptr : Int64,
  func_ptr : Int64,
  values_vec : FixedArray[Int64],
  values_len : Int,
  use_stack_switch : Bool,
) -> Int {
  if use_stack_switch {
    c_jit_call_with_stack_switch_managed(
      self.0,
      trampoline_ptr,
      func_ptr,
      values_vec,
      values_len,
    )
  } else {
    c_jit_call_trampoline_managed(
      self.0,
      trampoline_ptr,
      func_ptr,
      values_vec,
      values_len,
    )
  }
}

///|
/// Get the runtime store function address for the current JIT -> hostcall.
///
/// This value is set by `wasmoon_jit_hostcall` right before invoking the
/// MoonBit hostcall callback.
pub fn get_hostcall_func_addr() -> Int {
  c_jit_get_hostcall_func_idx()
}

///|
/// Get the pointer to the hostcall scratch `values_vec` buffer.
fn get_hostcall_values_ptr() -> Int64 {
  c_jit_get_hostcall_values_ptr()
}

///|
pub fn hostcall_value_slot_read(slot : Int) -> Int64 {
  native_value_slot_read(get_hostcall_values_ptr(), slot)
}

///|
pub fn hostcall_value_slot_write(slot : Int, value : Int64) -> Unit {
  native_value_slot_write(get_hostcall_values_ptr(), slot, value)
}

///|
/// Get the number of argument *slots* in `values_vec`.
///
/// Note: slots are 8 bytes each; V128 occupies 2 slots.
pub fn get_hostcall_num_arg_slots() -> Int {
  c_jit_get_hostcall_num_args()
}

///|
/// Get the number of result *slots* in `values_vec`.
///
/// Note: slots are 8 bytes each; V128 occupies 2 slots.
pub fn get_hostcall_num_result_slots() -> Int {
  c_jit_get_hostcall_num_results()
}

///|
/// Memory information for JIT execution.
pub struct MemoryInfo {
  ptr : Int64 // Pointer to `wasmoon_memory_t` (memory descriptor)
  size : Int64 // Initial size in bytes (informational)
  max_pages : Int? // Max pages (None = unlimited)
}

///|
/// Create a new MemoryInfo.
pub fn MemoryInfo::MemoryInfo(
  ptr : Int64,
  size : Int64,
  max_pages : Int?,
) -> MemoryInfo {
  { ptr, size, max_pages }
}

///|
/// Allocate linear memory for WASM (returns 0 on failure).
pub fn alloc_memory(size : Int64) -> Int64 {
  c_jit_alloc_memory(size)
}

///|
/// Allocate a `wasmoon_memory_t` descriptor with owned backing memory.
pub fn alloc_memory_desc(
  size_bytes : Int64,
  max_pages : Int?,
  is_memory64? : Bool = false,
  page_size_log2? : Int = 16,
  is_shared? : Bool = false,
) -> Int64 {
  c_jit_alloc_memory_desc(
    size_bytes,
    max_pages.unwrap_or(-1),
    if is_memory64 {
      1
    } else {
      0
    },
    page_size_log2,
    if is_shared {
      1
    } else {
      0
    },
  )
}

///|
/// Allocate a guarded (reserved) memory descriptor for memory32 memory0.
/// This is required for bounds-check elimination; out-of-bounds accesses trap via guard pages.
pub fn alloc_guarded_memory_desc(
  initial_pages : Int,
  max_pages : Int?,
) -> Int64 {
  c_jit_alloc_guarded_memory_desc(
    initial_pages.to_int64(),
    max_pages.unwrap_or(-1).to_int64(),
  )
}

///|
pub fn free_memory(mem_ptr : Int64) -> Unit {
  c_jit_free_memory(mem_ptr)
}

///|
pub fn free_memory_desc(mem_desc_ptr : Int64) -> Unit {
  c_jit_free_memory_desc(mem_desc_ptr)
}

///|
/// Initialize memory with data at offset.
pub fn memory_init(mem_ptr : Int64, offset : Int64, data : Bytes) -> Bool {
  let size = data.length()
  if size == 0 {
    return true
  }
  c_jit_memory_init_bytes(mem_ptr, offset, data, size) == 0
}

///|
/// Read `size` bytes from linear memory at offset.
pub fn memory_read(mem_ptr : Int64, offset : Int64, size : Int) -> Bytes {
  if mem_ptr == 0L || size <= 0 {
    return b""
  }
  let out = FixedArray::make(size, b'\x00')
  let rc = c_jit_memory_read(mem_ptr, offset, out, size)
  if rc != 0 {
    return b""
  }
  let arr : Array[Byte] = Array::make(size, b'\x00')
  for i in 0.. JITTable? {
  if size < 0 {
    return None
  }
  let alloc_size = if size == 0 { 1 } else { size }
  let table_ptr = c_jit_alloc_shared_indirect_table(alloc_size)
  if table_ptr == 0L {
    return None
  }
  Some({ table_ptr, size, max })
}

///|
/// Set an entry in the shared table.
pub fn JITTable::set(
  self : JITTable,
  table_idx : Int,
  func_ptr : Int64,
  type_hash : Int,
) -> Unit {
  if table_idx < 0 || table_idx >= self.size {
    return
  }
  c_jit_shared_table_set(self.table_ptr, table_idx, func_ptr, type_hash)
}

///|
/// Read raw entry value bits from the shared table.
pub fn JITTable::get_entry_value(self : JITTable, table_idx : Int) -> Int64 {
  if table_idx < 0 || table_idx >= self.size {
    return 0L
  }
  c_jit_read_i64(self.table_ptr + (table_idx * 16).to_int64())
}

///|
/// Read raw entry type index from the shared table.
pub fn JITTable::get_entry_type(self : JITTable, table_idx : Int) -> Int {
  if table_idx < 0 || table_idx >= self.size {
    return -1
  }
  c_jit_read_i64(self.table_ptr + (table_idx * 16 + 8).to_int64()).to_int()
}

///|
/// Get the table size.
pub fn JITTable::get_size(self : JITTable) -> Int {
  self.size
}

///|
/// Get the raw shared table pointer.
pub fn JITTable::raw_ptr(self : JITTable) -> Int64 {
  self.table_ptr
}

///|
/// Refresh table pointer and size from the active JIT context view.
pub fn JITTable::refresh_layout(
  self : JITTable,
  table_ptr : Int64,
  size : Int,
) -> Unit {
  if table_ptr == 0L || size < 0 {
    return
  }
  self.table_ptr = table_ptr
  self.size = size
}

///|
/// Get the table max size.
pub fn JITTable::get_max(self : JITTable) -> Int? {
  self.max
}

///|
fn get_fd_write_ptr() -> Int64 {
  c_jit_get_fd_write_ptr()
}

///|
fn get_proc_exit_ptr() -> Int64 {
  c_jit_get_proc_exit_ptr()
}

///|
fn get_fd_read_ptr() -> Int64 {
  c_jit_get_fd_read_ptr()
}

///|
fn get_args_sizes_get_ptr() -> Int64 {
  c_jit_get_args_sizes_get_ptr()
}

///|
fn get_args_get_ptr() -> Int64 {
  c_jit_get_args_get_ptr()
}

///|
fn get_environ_sizes_get_ptr() -> Int64 {
  c_jit_get_environ_sizes_get_ptr()
}

///|
fn get_environ_get_ptr() -> Int64 {
  c_jit_get_environ_get_ptr()
}

///|
fn get_clock_time_get_ptr() -> Int64 {
  c_jit_get_clock_time_get_ptr()
}

///|
fn get_random_get_ptr() -> Int64 {
  c_jit_get_random_get_ptr()
}

///|
fn get_fd_close_ptr() -> Int64 {
  c_jit_get_fd_close_ptr()
}

///|
fn get_fd_fdstat_get_ptr() -> Int64 {
  c_jit_get_fd_fdstat_get_ptr()
}

///|
fn get_fd_prestat_get_ptr() -> Int64 {
  c_jit_get_fd_prestat_get_ptr()
}

///|
fn get_fd_prestat_dir_name_ptr() -> Int64 {
  c_jit_get_fd_prestat_dir_name_ptr()
}

///|
fn get_fd_seek_ptr() -> Int64 {
  c_jit_get_fd_seek_ptr()
}

///|
fn get_fd_tell_ptr() -> Int64 {
  c_jit_get_fd_tell_ptr()
}

///|
fn get_fd_sync_ptr() -> Int64 {
  c_jit_get_fd_sync_ptr()
}

///|
fn get_fd_datasync_ptr() -> Int64 {
  c_jit_get_fd_datasync_ptr()
}

///|
fn get_fd_filestat_get_ptr() -> Int64 {
  c_jit_get_fd_filestat_get_ptr()
}

///|
fn get_fd_filestat_set_size_ptr() -> Int64 {
  c_jit_get_fd_filestat_set_size_ptr()
}

///|
fn get_path_open_ptr() -> Int64 {
  c_jit_get_path_open_ptr()
}

///|
fn get_path_create_directory_ptr() -> Int64 {
  c_jit_get_path_create_directory_ptr()
}

///|
fn get_path_unlink_file_ptr() -> Int64 {
  c_jit_get_path_unlink_file_ptr()
}

///|
fn get_path_remove_directory_ptr() -> Int64 {
  c_jit_get_path_remove_directory_ptr()
}

///|
fn get_path_rename_ptr() -> Int64 {
  c_jit_get_path_rename_ptr()
}

///|
fn get_clock_res_get_ptr() -> Int64 {
  c_jit_get_clock_res_get_ptr()
}

///|
fn get_sched_yield_ptr() -> Int64 {
  c_jit_get_sched_yield_ptr()
}

///|
fn get_proc_raise_ptr() -> Int64 {
  c_jit_get_proc_raise_ptr()
}

///|
fn get_fd_advise_ptr() -> Int64 {
  c_jit_get_fd_advise_ptr()
}

///|
fn get_fd_allocate_ptr() -> Int64 {
  c_jit_get_fd_allocate_ptr()
}

///|
fn get_fd_pread_ptr() -> Int64 {
  c_jit_get_fd_pread_ptr()
}

///|
fn get_fd_pwrite_ptr() -> Int64 {
  c_jit_get_fd_pwrite_ptr()
}

///|
fn get_fd_readdir_ptr() -> Int64 {
  c_jit_get_fd_readdir_ptr()
}

///|
fn get_fd_renumber_ptr() -> Int64 {
  c_jit_get_fd_renumber_ptr()
}

///|
fn get_fd_fdstat_set_flags_ptr() -> Int64 {
  c_jit_get_fd_fdstat_set_flags_ptr()
}

///|
fn get_fd_fdstat_set_rights_ptr() -> Int64 {
  c_jit_get_fd_fdstat_set_rights_ptr()
}

///|
fn get_fd_filestat_set_times_ptr() -> Int64 {
  c_jit_get_fd_filestat_set_times_ptr()
}

///|
fn get_path_filestat_get_ptr() -> Int64 {
  c_jit_get_path_filestat_get_ptr()
}

///|
fn get_path_filestat_set_times_ptr() -> Int64 {
  c_jit_get_path_filestat_set_times_ptr()
}

///|
fn get_path_link_ptr() -> Int64 {
  c_jit_get_path_link_ptr()
}

///|
fn get_path_readlink_ptr() -> Int64 {
  c_jit_get_path_readlink_ptr()
}

///|
fn get_path_symlink_ptr() -> Int64 {
  c_jit_get_path_symlink_ptr()
}

///|
fn get_sock_accept_ptr() -> Int64 {
  c_jit_get_sock_accept_ptr()
}

///|
fn get_sock_recv_ptr() -> Int64 {
  c_jit_get_sock_recv_ptr()
}

///|
fn get_sock_send_ptr() -> Int64 {
  c_jit_get_sock_send_ptr()
}

///|
fn get_sock_shutdown_ptr() -> Int64 {
  c_jit_get_sock_shutdown_ptr()
}

///|
fn get_spectest_print_ptr() -> Int64 {
  c_jit_get_spectest_print_ptr()
}

///|
fn get_spectest_print_i32_ptr() -> Int64 {
  c_jit_get_spectest_print_i32_ptr()
}

///|
fn get_spectest_print_i64_ptr() -> Int64 {
  c_jit_get_spectest_print_i64_ptr()
}

///|
fn get_spectest_print_f32_ptr() -> Int64 {
  c_jit_get_spectest_print_f32_ptr()
}

///|
fn get_spectest_print_f64_ptr() -> Int64 {
  c_jit_get_spectest_print_f64_ptr()
}

///|
fn get_spectest_print_i32_f32_ptr() -> Int64 {
  c_jit_get_spectest_print_i32_f32_ptr()
}

///|
fn get_spectest_print_f64_f64_ptr() -> Int64 {
  c_jit_get_spectest_print_f64_f64_ptr()
}

///|
fn get_spectest_print_char_ptr() -> Int64 {
  c_jit_get_spectest_print_char_ptr()
}

///|

///|
/// GC-managed executable code block wrapper.
struct ExecCode(ExecCodeHandle)

///|
/// Allocate and copy code to executable memory (GC-managed).
fn ExecCode::try_alloc(code : Array[Int]) -> ExecCode? {
  let size = code.length()
  if size == 0 {
    return None
  }
  let bytes = FixedArray::make(size, b'\x00')
  for i, b in code {
    bytes[i] = b.to_byte()
  }
  let ec = alloc_exec_managed(bytes, size)
  if exec_code_pointer(ec) != 0L {
    Some(ExecCode(ec))
  } else {
    None
  }
}

///|
fn ExecCode::try_stage(size : Int) -> ExecCode? {
  if size <= 0 {
    return None
  }
  let code = stage_exec_managed(size)
  if exec_code_pointer(code) == 0L {
    None
  } else {
    Some(ExecCode(code))
  }
}

///|
fn ExecCode::finalize(self : ExecCode, code : Array[Byte]) -> Bool {
  let size = code.length()
  let bytes = FixedArray::make(size, b'\x00')
  for index, byte in code {
    bytes[index] = byte
  }
  finalize_exec_managed(self.0, bytes, size) == 0
}

///|
fn ExecCode::release(self : ExecCode) -> Bool {
  release_exec_managed(self.0) == 0
}

///|
/// Get the function pointer from this executable code.
fn ExecCode::ptr(self : ExecCode) -> Int64 {
  exec_code_pointer(self.0)
}