mirror of
https://github.com/mii443/wasmer.git
synced 2025-12-16 17:18:57 +00:00
Address feedback, make InstanceBuilder private
This commit is contained in:
@@ -12,7 +12,7 @@ use wasmer_types::{
|
||||
SignatureIndex, TableIndex,
|
||||
};
|
||||
use wasmer_vm::{
|
||||
FunctionBodyPtr, InstanceHandle, MemoryStyle, ModuleInfo, TableStyle, UnpreparedInstance,
|
||||
FunctionBodyPtr, InstanceBuilder, InstanceHandle, MemoryStyle, ModuleInfo, TableStyle,
|
||||
VMSharedSignatureIndex, VMTrampoline,
|
||||
};
|
||||
|
||||
@@ -116,7 +116,7 @@ pub trait Artifact: Send + Sync + Upcastable {
|
||||
// Get pointers to where metadata about local tables should live in VM memory.
|
||||
|
||||
let (unprepared, memory_definition_locations, table_definition_locations) =
|
||||
UnpreparedInstance::new(&*module);
|
||||
InstanceBuilder::new(&*module);
|
||||
let finished_memories = tunables
|
||||
.create_memories(&module, self.memory_styles(), &memory_definition_locations)
|
||||
.map_err(InstantiationError::Link)?
|
||||
|
||||
197
lib/vm/src/instance/builder.rs
Normal file
197
lib/vm/src/instance/builder.rs
Normal file
@@ -0,0 +1,197 @@
|
||||
use super::{Instance, InstanceAllocator};
|
||||
use crate::vmcontext::{VMMemoryDefinition, VMTableDefinition};
|
||||
use crate::{ModuleInfo, VMOffsets};
|
||||
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::{LocalMemoryIndex, LocalTableIndex};
|
||||
|
||||
/// This is an intermediate type that manages the raw allocation and
|
||||
/// metadata when creating an [`Instance`].
|
||||
///
|
||||
/// This type will free the allocated memory if it's dropped before
|
||||
/// being used.
|
||||
///
|
||||
/// The [`InstanceBuilder::instance_layout`] computes the correct
|
||||
/// layout to represent the wanted [`Instance`].
|
||||
///
|
||||
/// Then we use this layout to allocate an empty `Instance` properly.
|
||||
pub struct InstanceBuilder {
|
||||
/// The buffer that will contain the [`Instance`] and dynamic fields.
|
||||
instance_ptr: NonNull<Instance>,
|
||||
/// The layout of the `instance_ptr` buffer.
|
||||
instance_layout: Layout,
|
||||
/// Information about the offsets into the `instance_ptr` buffer for
|
||||
/// the dynamic fields.
|
||||
offsets: VMOffsets,
|
||||
/// Whether or not this type has transferred ownership of the
|
||||
/// `instance_ptr` buffer. If it has not when being dropped,
|
||||
/// the buffer should be freed.
|
||||
consumed: bool,
|
||||
}
|
||||
|
||||
impl Drop for InstanceBuilder {
|
||||
fn drop(&mut self) {
|
||||
if !self.consumed {
|
||||
// If `consumed` has not been set, then we still have ownership
|
||||
// over the buffer and must free it.
|
||||
let instance_ptr = self.instance_ptr.as_ptr();
|
||||
unsafe {
|
||||
std::alloc::dealloc(instance_ptr as *mut u8, self.instance_layout);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl InstanceBuilder {
|
||||
/// Allocates instance data for use with [`InstanceHandle::new`].
|
||||
///
|
||||
/// Returns a wrapper type around the allocation and 2 vectors of
|
||||
/// pointers into the allocated buffer. These lists of pointers
|
||||
/// correspond to the location in memory for the local memories and
|
||||
/// tables respectively. These pointers should be written to before
|
||||
/// calling [`InstanceHandle::new`].
|
||||
pub fn new(
|
||||
module: &ModuleInfo,
|
||||
) -> (
|
||||
InstanceBuilder,
|
||||
Vec<NonNull<VMMemoryDefinition>>,
|
||||
Vec<NonNull<VMTableDefinition>>,
|
||||
) {
|
||||
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 unprepared = Self {
|
||||
instance_ptr,
|
||||
instance_layout,
|
||||
offsets,
|
||||
consumed: false,
|
||||
};
|
||||
|
||||
// # Safety
|
||||
// Both of these calls are safe because we allocate the pointer
|
||||
// above with the same `offsets` that these functions use.
|
||||
// Thus there will be enough valid memory for both of them.
|
||||
let memories = unsafe { unprepared.memory_definition_locations() };
|
||||
let tables = unsafe { unprepared.table_definition_locations() };
|
||||
|
||||
(unprepared, memories, tables)
|
||||
}
|
||||
|
||||
/// Calculate the appropriate layout for the [`Instance`].
|
||||
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()
|
||||
}
|
||||
|
||||
/// Get the locations of where the local [`VMMemoryDefinition`]s should be stored.
|
||||
///
|
||||
/// This function lets us create `Memory` objects on the host with backing
|
||||
/// memory in the VM.
|
||||
///
|
||||
/// # Safety
|
||||
/// - `instance_ptr` must point to enough memory that all of the
|
||||
/// offsets in `offsets` point to valid locations in memory,
|
||||
/// i.e. `instance_ptr` must have been allocated by
|
||||
/// `Self::new`.
|
||||
unsafe fn memory_definition_locations(&self) -> Vec<NonNull<VMMemoryDefinition>> {
|
||||
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);
|
||||
|
||||
// We need to do some pointer arithmetic now. The unit is `u8`.
|
||||
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
|
||||
}
|
||||
|
||||
/// Get the locations of where the [`VMTableDefinition`]s should be stored.
|
||||
///
|
||||
/// This function lets us create [`Table`] objects on the host with backing
|
||||
/// memory in the VM.
|
||||
///
|
||||
/// # Safety
|
||||
/// - `instance_ptr` must point to enough memory that all of the
|
||||
/// offsets in `offsets` point to valid locations in memory,
|
||||
/// i.e. `instance_ptr` must have been allocated by
|
||||
/// `Self::new`.
|
||||
unsafe fn table_definition_locations(&self) -> Vec<NonNull<VMTableDefinition>> {
|
||||
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);
|
||||
|
||||
// We need to do some pointer arithmetic now. The unit is `u8`.
|
||||
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
|
||||
}
|
||||
|
||||
/// Finish preparing by writing the [`Instance`] into memory.
|
||||
pub(crate) fn write_instance(mut self, instance: Instance) -> InstanceAllocator {
|
||||
// prevent the old state's drop logic from being called as we
|
||||
// transition into the new state.
|
||||
self.consumed = true;
|
||||
|
||||
unsafe {
|
||||
// `instance` is moved at `instance_ptr`. This pointer has
|
||||
// been allocated by `Self::allocate_instance` (so by
|
||||
// `InstanceAllocator::allocate_instance`.
|
||||
ptr::write(self.instance_ptr.as_ptr(), instance);
|
||||
// Now `instance_ptr` is correctly initialized!
|
||||
}
|
||||
let instance = self.instance_ptr;
|
||||
let instance_layout = self.instance_layout;
|
||||
|
||||
// This is correct because of the invariants of `Self` and
|
||||
// because we write `Instance` to the pointer in this function.
|
||||
unsafe { InstanceAllocator::new(instance, instance_layout) }
|
||||
}
|
||||
|
||||
/// Get the [`VMOffsets`] for the allocated buffer.
|
||||
pub(crate) fn offsets(&self) -> &VMOffsets {
|
||||
&self.offsets
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,10 @@
|
||||
//! how it is allocated and deallocated. An `InstanceHandle` is a
|
||||
//! wrapper around an `InstanceAllocator`.
|
||||
|
||||
mod builder;
|
||||
|
||||
pub use builder::InstanceBuilder;
|
||||
|
||||
use crate::export::VMExport;
|
||||
use crate::global::Global;
|
||||
use crate::imports::Imports;
|
||||
@@ -23,7 +27,7 @@ use crate::{FunctionBodyPtr, ModuleInfo, VMOffsets};
|
||||
use crate::{VMExportFunction, VMExportGlobal, VMExportMemory, VMExportTable};
|
||||
use memoffset::offset_of;
|
||||
use more_asserts::assert_lt;
|
||||
use std::alloc::{self, Layout};
|
||||
use std::alloc::Layout;
|
||||
use std::any::Any;
|
||||
use std::cell::{Cell, RefCell};
|
||||
use std::collections::HashMap;
|
||||
@@ -688,189 +692,7 @@ impl Instance {
|
||||
}
|
||||
}
|
||||
|
||||
/// This is an intermediate type that manages the raw allocation and
|
||||
/// metadata when creating an [`Instance`].
|
||||
///
|
||||
/// This type will free the allocated memory if it's dropped before
|
||||
/// being used.
|
||||
///
|
||||
/// The [`UnpreparedInstance::instance_layout`] computes the correct
|
||||
/// layout to represent the wanted [`Instance`].
|
||||
///
|
||||
/// Then we use this layout to allocate an empty `Instance` properly.
|
||||
pub struct UnpreparedInstance {
|
||||
/// The buffer that will contain the [`Instance`] and dynamic fields.
|
||||
instance_ptr: NonNull<Instance>,
|
||||
/// The layout of the `instance_ptr` buffer.
|
||||
instance_layout: Layout,
|
||||
/// Information about the offsets into the `instance_ptr` buffer for
|
||||
/// the dynamic fields.
|
||||
offsets: VMOffsets,
|
||||
/// Whether or not this type has transferred ownership of the
|
||||
/// `instance_ptr` buffer. If it has not when being dropped,
|
||||
/// the buffer should be freed.
|
||||
consumed: bool,
|
||||
}
|
||||
|
||||
impl Drop for UnpreparedInstance {
|
||||
fn drop(&mut self) {
|
||||
if !self.consumed {
|
||||
// If `consumed` has not been set, then we still have ownership
|
||||
// over the buffer and must free it.
|
||||
let instance_ptr = self.instance_ptr.as_ptr();
|
||||
unsafe {
|
||||
std::alloc::dealloc(instance_ptr as *mut u8, self.instance_layout);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl UnpreparedInstance {
|
||||
/// Allocates instance data for use with [`InstanceHandle::new`].
|
||||
///
|
||||
/// Returns a wrapper type around the allocation and 2 vectors of
|
||||
/// pointers into the allocated buffer. These lists of pointers
|
||||
/// correspond to the location in memory for the local memories and
|
||||
/// tables respectively. These pointers should be written to before
|
||||
/// calling [`InstanceHandle::new`].
|
||||
pub fn new(
|
||||
module: &ModuleInfo,
|
||||
) -> (
|
||||
UnpreparedInstance,
|
||||
Vec<NonNull<VMMemoryDefinition>>,
|
||||
Vec<NonNull<VMTableDefinition>>,
|
||||
) {
|
||||
let offsets = VMOffsets::new(mem::size_of::<*const u8>() 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 unprepared = Self {
|
||||
instance_ptr,
|
||||
instance_layout,
|
||||
offsets,
|
||||
consumed: false,
|
||||
};
|
||||
|
||||
let memories = unsafe { unprepared.memory_definition_locations() };
|
||||
let tables = unsafe { unprepared.table_definition_locations() };
|
||||
|
||||
(unprepared, memories, tables)
|
||||
}
|
||||
|
||||
/// Calculate the appropriate layout for the [`Instance`].
|
||||
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()
|
||||
}
|
||||
|
||||
/// Get the locations of where the local [`VMMemoryDefinition`]s should be stored.
|
||||
///
|
||||
/// This function lets us create `Memory` objects on the host with backing
|
||||
/// memory in the VM.
|
||||
///
|
||||
/// # Safety
|
||||
/// - `instance_ptr` must point to enough memory that all of the
|
||||
/// offsets in `offsets` point to valid locations in memory,
|
||||
/// i.e. `instance_ptr` must have been allocated by
|
||||
/// `Self::new`.
|
||||
unsafe fn memory_definition_locations(&self) -> Vec<NonNull<VMMemoryDefinition>> {
|
||||
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);
|
||||
|
||||
// We need to do some pointer arithmetic now. The unit is `u8`.
|
||||
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
|
||||
}
|
||||
|
||||
/// Get the locations of where the [`VMTableDefinition`]s should be stored.
|
||||
///
|
||||
/// This function lets us create [`Table`] objects on the host with backing
|
||||
/// memory in the VM.
|
||||
///
|
||||
/// # Safety
|
||||
/// - `instance_ptr` must point to enough memory that all of the
|
||||
/// offsets in `offsets` point to valid locations in memory,
|
||||
/// i.e. `instance_ptr` must have been allocated by
|
||||
/// `Self::new`.
|
||||
unsafe fn table_definition_locations(&self) -> Vec<NonNull<VMTableDefinition>> {
|
||||
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);
|
||||
|
||||
// We need to do some pointer arithmetic now. The unit is `u8`.
|
||||
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
|
||||
}
|
||||
|
||||
/// Finish preparing by writing the [`Instance`] into memory.
|
||||
pub(crate) fn write_instance(mut self, instance: Instance) -> InstanceAllocator {
|
||||
unsafe {
|
||||
// `instance` is moved at `instance_ptr`. This pointer has
|
||||
// been allocated by `Self::allocate_instance` (so by
|
||||
// `InstanceAllocator::allocate_instance`.
|
||||
ptr::write(self.instance_ptr.as_ptr(), instance);
|
||||
// Now `instance_ptr` is correctly initialized!
|
||||
}
|
||||
let instance = self.instance_ptr;
|
||||
let instance_layout = self.instance_layout;
|
||||
// prevent the old state's drop logic from being called as we
|
||||
// transition into the new state.
|
||||
self.consumed = true;
|
||||
|
||||
// This is correct because of the invariants of `Self` and
|
||||
// because we write `Instance` to the pointer in this function.
|
||||
unsafe { InstanceAllocator::new(instance, instance_layout) }
|
||||
}
|
||||
|
||||
/// Get the [`VMOffsets`] for the allocated buffer.
|
||||
pub(crate) fn offsets(&self) -> VMOffsets {
|
||||
self.offsets.clone()
|
||||
}
|
||||
}
|
||||
|
||||
/// TODO: update these docs
|
||||
/// An `InstanceAllocator` is responsible to allocate, to deallocate,
|
||||
/// and to give access to an `Instance`, in such a way that `Instance`
|
||||
/// is unique, can be shared, safely, across threads, without
|
||||
@@ -895,7 +717,7 @@ impl UnpreparedInstance {
|
||||
/// [`InstanceAllocator`] calls its `deallocate_instance` method without
|
||||
/// checking if this property holds, only when `Self.strong` is equal to 1.
|
||||
///
|
||||
/// Note for the curious reader: [`UnpreparedInstance::new`]
|
||||
/// Note for the curious reader: [`InstanceBuilder::new`]
|
||||
/// and [`InstanceHandle::new`] will respectively allocate a proper
|
||||
/// `Instance` and will fill it correctly.
|
||||
///
|
||||
@@ -940,11 +762,11 @@ impl InstanceAllocator {
|
||||
///
|
||||
/// `instance` must a non-null, non-dangling, properly aligned,
|
||||
/// and correctly initialized pointer to `Instance`. See
|
||||
/// [`UnpreparedInstance::new`] for an example of how to correctly use
|
||||
/// [`InstanceBuilder::new`] for an example of how to correctly use
|
||||
/// this API.
|
||||
/// TODO: update docs
|
||||
pub(crate) unsafe fn new(instance: NonNull<Instance>, instance_layout: Layout) -> Self {
|
||||
InstanceAllocator {
|
||||
Self {
|
||||
strong: Arc::new(atomic::AtomicUsize::new(1)),
|
||||
instance_layout,
|
||||
instance,
|
||||
@@ -1123,7 +945,7 @@ impl InstanceHandle {
|
||||
/// all the local memories.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub unsafe fn new(
|
||||
unprepared: UnpreparedInstance,
|
||||
unprepared: InstanceBuilder,
|
||||
module: Arc<ModuleInfo>,
|
||||
finished_functions: BoxedSlice<LocalFunctionIndex, FunctionBodyPtr>,
|
||||
finished_function_call_trampolines: BoxedSlice<SignatureIndex, VMTrampoline>,
|
||||
@@ -1143,7 +965,7 @@ impl InstanceHandle {
|
||||
let passive_data = RefCell::new(module.passive_data.clone());
|
||||
|
||||
let handle = {
|
||||
let offsets = unprepared.offsets();
|
||||
let offsets = unprepared.offsets().clone();
|
||||
// Create the `Instance`. The unique, the One.
|
||||
let instance = Instance {
|
||||
module,
|
||||
@@ -39,7 +39,7 @@ pub mod libcalls;
|
||||
pub use crate::export::*;
|
||||
pub use crate::global::*;
|
||||
pub use crate::imports::Imports;
|
||||
pub use crate::instance::{ImportInitializerFuncPtr, InstanceHandle, UnpreparedInstance};
|
||||
pub use crate::instance::{ImportInitializerFuncPtr, InstanceBuilder, InstanceHandle};
|
||||
pub use crate::memory::{LinearMemory, Memory, MemoryError, MemoryStyle};
|
||||
pub use crate::mmap::Mmap;
|
||||
pub use crate::module::{ExportsIterator, ImportsIterator, ModuleInfo};
|
||||
|
||||
Reference in New Issue
Block a user