///|
// A native Store adopts managed construction state once. Thereafter the native
// descriptor is authoritative for interpreter, host and compiled table access.
fn Store::bind_table_storage(
  self : Store,
  table : Table,
  native : @native_bridge.JITTable,
) -> Unit raise RuntimeError {
  let elem_type = table.elem_type
  let references = self.references
  let storage : NativeTableStorage = { table: native, references, }
  match elem_type {
    FuncRef | RefFunc | RefFuncTyped(_) | RefNullFuncTyped(_) | NullFuncRef =>
      native.raw_ptr().use_store_references()
    _ => ()
  }
  // Admission is one unpublished batch, including all nested snapshots.
  let created : Array[Int32] = []
  errdefer (while created.pop() is Some(index) {
    references.release_native_exception(index)
  })
  match table.storage {
    Managed(elements) =>
      for index, value in elements {
        let (type_index, word) = references.encode_table_value(
          value,
          created_exceptions=created,
        )
        native.set(index, word, type_index)
      }
    Native(_) => raise InvalidHandle
  }
  table.storage = Native(storage)
}

///|
fn StoreReferences::encode_table_value(
  self : StoreReferences,
  value : @types.Value,
  created_exceptions? : Array[Int32],
) -> (Int32, Int64) raise RuntimeError {
  match value {
    FuncRef(address) => {
      guard address >= 0 else { raise UndefinedElement }
      (-1, @native_bridge.encode_store_funcref(address))
    }
    I31(value) => (-1, ((value & @types.I32_MAX).to_int64() << 1) | 1L)
    ExnRef(index) =>
      (
        -1,
        match created_exceptions {
          Some(created) => self.encode_native_exception(index, created~)
          None => self.encode_native_exception(index)
        },
      )
    ExternRef(index) => {
      let value = self.unwrap_extern(ExternRef(index))
      (
        if index < 0 {
          -1 - index
        } else {
          -1
        },
        match value {
          ExternRef(index) => @native_bridge.encode_externref(index)
          I31(value) => ((value & @types.I32_MAX).to_int64() << 1) | 1L
          StructRef(value) | ArrayRef(value) =>
            @native_bridge.value_to_i64(StructRef(value))
          Null => 0L
          _ => raise TypeMismatch
        },
      )
    }
    _ => (-1, @native_bridge.value_to_i64(value))
  }
}

///|
fn StoreReferences::decode_table_value(
  self : StoreReferences,
  type_ : @types.ValueType,
  metadata : Int32,
  word : Int64,
  root : Bool,
) -> @types.Value raise RuntimeError {
  // A wrapped null has a zero payload but a nonnull external identity.
  if (match type_ {
      ExternRef | RefExtern | NullExternRef => true
      _ => false
    }) &&
    metadata >= 0 {
    guard self.wrapped_externs.contains(metadata) else { raise InvalidHandle }
    return ExternRef(-1 - metadata)
  }
  if word == 0L {
    return Null
  }
  match type_ {
    FuncRef | RefFunc | RefFuncTyped(_) | RefNullFuncTyped(_) | NullFuncRef => {
      guard word > 0L && (word & 0x3000000000000000L) == 0x3000000000000000L else {
        raise InvalidHandle
      }
      FuncRef(((word & 0x0FFFFFFFFFFFFFFFL) - 1L).to_int())
    }
    ExnRef | NullExnRef => self.decode_native_exception(word)
    ContRef
    | RefCont
    | RefContTyped(_)
    | RefNullContTyped(_)
    | NullContRef
    | RefNoCont => @native_bridge.i64_to_value(word, type_)
    ExternRef | RefExtern | NullExternRef =>
      if @native_bridge.decode_externref(word) is Some(index) {
        ExternRef(index)
      } else {
        let value = self.decode_table_gc_word(word)
        if root {
          value
        } else {
          self.wrap_as_extern(value)
        }
      }
    _ => self.decode_table_gc_word(word)
  }
}

///|
fn StoreReferences::decode_table_gc_word(
  self : StoreReferences,
  word : Int64,
) -> @types.Value raise RuntimeError {
  if @native_bridge.decode_externref(word) is Some(index) {
    return ExternRef(index)
  }
  if (word & 1L) != 0L {
    return I31((word >> 1).to_int())
  }
  let index = @native_bridge.decode_heap_ref(word)
  match self.native.c_heap {
    Some(heap) =>
      if heap.is_array(index) {
        ArrayRef(index)
      } else {
        StructRef(index)
      }
    None =>
      match self.heap.get(index) {
        Array(_) => ArrayRef(index)
        Struct(_) => StructRef(index)
      }
  }
}