///|
#borrow(context, output)
extern "c" fn write_bound_entry_code(
  context : JITContext,
  target : Int64,
  output : FixedArray[Byte],
) -> Int = "wasmoon_jit_write_bound_entry_code"

///|
/// A native entry bound to the instance environment that owns its code.
/// The leaf trampoline preserves all guest arguments and tail-call stack usage.
pub struct NativeCallable {
  context : NativeJITContext
  entry : InstalledTrampoline
}

///|
pub fn NativeJITContext::bind_callable(
  self : NativeJITContext,
  target : Int64,
) -> NativeCallable raise JitInstallError {
  let bytes = FixedArray::make(40, b'\x00')
  let size = write_bound_entry_code(self.handle, target, bytes)
  if size <= 0 {
    raise TrampolineAllocationFailed(code_size=0)
  }
  let code : Array[Int] = []
  for i in 0.. Int64 {
  self.context |> ignore
  self.entry.entry_address()
}

///|
pub fn NativeCallable::release(self : NativeCallable) -> Unit {
  self.entry.release()
}

///|
#borrow(context, local_types, parents, entries, tags)
extern "c" fn set_callable_types(
  context : JITContext,
  local_types : FixedArray[Int],
  local_count : Int,
  parents : FixedArray[Int],
  type_count : Int,
  entries : FixedArray[Int64],
  entry_count : Int,
  tags : FixedArray[Int],
  tag_count : Int,
) -> Unit = "wasmoon_jit_set_callable_types"

///|
pub fn NativeJITContext::set_callable_types(
  self : NativeJITContext,
  local_types : Array[Int],
  parents : Array[Int],
  entries : Array[(Int64, Int)],
  tags : Array[Int],
) -> Unit {
  let locals = FixedArray::make(local_types.length(), 0)
  for i, value in local_types {
    locals[i] = value
  }
  let supers = FixedArray::make(parents.length(), 0)
  for i, value in parents {
    supers[i] = value
  }
  let functions = FixedArray::make(entries.length() * 2, 0L)
  for i, entry in entries {
    functions[i * 2] = entry.0
    functions[i * 2 + 1] = entry.1.to_int64()
  }
  let tag_addresses = FixedArray::make(tags.length(), 0)
  for i, address in tags {
    tag_addresses[i] = address
  }
  set_callable_types(
    self.handle,
    locals,
    locals.length(),
    supers,
    supers.length(),
    functions,
    entries.length(),
    tag_addresses,
    tag_addresses.length(),
  )
}