use super::{Instance, VMInstance};
use crate::vmcontext::VMTableDefinition;
use crate::{VMGlobalDefinition, VMMemoryDefinition};
use std::alloc::{self, Layout};
use std::convert::TryFrom;
use std::mem;
use std::ptr::{self, NonNull};
use wasmer_types::entity::EntityRef;
use wasmer_types::{LocalGlobalIndex, VMOffsets};
use wasmer_types::{LocalMemoryIndex, LocalTableIndex, ModuleInfo};
pub struct InstanceAllocator {
instance_ptr: NonNull<Instance>,
instance_layout: Layout,
offsets: VMOffsets,
consumed: bool,
}
impl Drop for InstanceAllocator {
fn drop(&mut self) {
if !self.consumed {
let instance_ptr = self.instance_ptr.as_ptr();
unsafe {
std::alloc::dealloc(instance_ptr as *mut u8, self.instance_layout);
}
}
}
}
impl InstanceAllocator {
#[allow(clippy::type_complexity)]
pub fn new(
module: &ModuleInfo,
) -> (
Self,
Vec<NonNull<VMMemoryDefinition>>,
Vec<NonNull<VMTableDefinition>>,
Vec<NonNull<VMGlobalDefinition>>,
) {
let offsets = VMOffsets::new(mem::size_of::<usize>() as u8, module);
let instance_layout = Self::instance_layout(&offsets);
#[allow(clippy::cast_ptr_alignment)]
let instance_ptr = unsafe { alloc::alloc(instance_layout) as *mut Instance };
let instance_ptr = if let Some(ptr) = NonNull::new(instance_ptr) {
ptr
} else {
alloc::handle_alloc_error(instance_layout);
};
let allocator = Self {
instance_ptr,
instance_layout,
offsets,
consumed: false,
};
let memories = unsafe { allocator.memory_definition_locations() };
let tables = unsafe { allocator.table_definition_locations() };
let globals = unsafe { allocator.global_definition_locations() };
(allocator, memories, tables, globals)
}
fn instance_layout(offsets: &VMOffsets) -> Layout {
let vmctx_size = usize::try_from(offsets.size_of_vmctx())
.expect("Failed to convert the size of `vmctx` to a `usize`");
let instance_vmctx_layout =
Layout::array::<u8>(vmctx_size).expect("Failed to create a layout for `VMContext`");
let (instance_layout, _offset) = Layout::new::<Instance>()
.extend(instance_vmctx_layout)
.expect("Failed to extend to `Instance` layout to include `VMContext`");
instance_layout.pad_to_align()
}
unsafe fn memory_definition_locations(&self) -> Vec<NonNull<VMMemoryDefinition>> {
unsafe {
let num_memories = self.offsets.num_local_memories();
let num_memories = usize::try_from(num_memories).unwrap();
let mut out = Vec::with_capacity(num_memories);
let ptr = self.instance_ptr.cast::<u8>().as_ptr();
let base_ptr = ptr.add(mem::size_of::<Instance>());
for i in 0..num_memories {
let mem_offset = self
.offsets
.vmctx_vmmemory_definition(LocalMemoryIndex::new(i));
let mem_offset = usize::try_from(mem_offset).unwrap();
let new_ptr = NonNull::new_unchecked(base_ptr.add(mem_offset));
out.push(new_ptr.cast());
}
out
}
}
unsafe fn table_definition_locations(&self) -> Vec<NonNull<VMTableDefinition>> {
unsafe {
let num_tables = self.offsets.num_local_tables();
let num_tables = usize::try_from(num_tables).unwrap();
let mut out = Vec::with_capacity(num_tables);
let ptr = self.instance_ptr.cast::<u8>().as_ptr();
let base_ptr = ptr.add(std::mem::size_of::<Instance>());
for i in 0..num_tables {
let table_offset = self
.offsets
.vmctx_vmtable_definition(LocalTableIndex::new(i));
let table_offset = usize::try_from(table_offset).unwrap();
let new_ptr = NonNull::new_unchecked(base_ptr.add(table_offset));
out.push(new_ptr.cast());
}
out
}
}
unsafe fn global_definition_locations(&self) -> Vec<NonNull<VMGlobalDefinition>> {
unsafe {
let num_globals = self.offsets.num_local_globals();
let num_globals = usize::try_from(num_globals).unwrap();
let mut out = Vec::with_capacity(num_globals);
let ptr = self.instance_ptr.cast::<u8>().as_ptr();
let base_ptr = ptr.add(std::mem::size_of::<Instance>());
for i in 0..num_globals {
let global_offset = self
.offsets
.vmctx_vmglobal_definition(LocalGlobalIndex::new(i));
let global_offset = usize::try_from(global_offset).unwrap();
let new_ptr = NonNull::new_unchecked(base_ptr.add(global_offset));
out.push(new_ptr.cast());
}
out
}
}
pub(crate) fn into_vminstance(mut self, instance: Instance) -> VMInstance {
self.consumed = true;
unsafe {
ptr::write(self.instance_ptr.as_ptr(), instance);
}
let instance = self.instance_ptr;
let instance_layout = self.instance_layout;
VMInstance {
instance,
instance_layout,
}
}
pub(crate) fn offsets(&self) -> &VMOffsets {
&self.offsets
}
}