//! A reusable pointer abstraction for getting memory from the guest's memory. //! //! This abstraction is safe: it ensures the memory is in bounds and that the pointer //! is aligned (avoiding undefined behavior). //! //! Therefore, you should use this abstraction whenever possible to avoid memory //! related bugs when implementing an ABI. use crate::{ memory::Memory, types::{ValueType, WasmExternType}, }; use std::{cell::Cell, fmt, marker::PhantomData, mem}; pub struct Array; pub struct Item; #[repr(transparent)] pub struct WasmPtr { offset: u32, _phantom: PhantomData<(T, Ty)>, } impl WasmPtr { #[inline] pub fn new(offset: u32) -> Self { Self { offset, _phantom: PhantomData, } } #[inline] pub fn offset(self) -> u32 { self.offset } } #[inline(always)] fn align_pointer(ptr: usize, align: usize) -> usize { // clears bits below aligment amount (assumes power of 2) to align pointer debug_assert!(align.count_ones() == 1); ptr & !(align - 1) } impl WasmPtr { #[inline] pub fn deref<'a>(self, memory: &'a Memory) -> Option<&'a Cell> { if self.offset == 0 || (self.offset as usize) + mem::size_of::() >= memory.size().bytes().0 { return None; } unsafe { let cell_ptr = align_pointer( memory.view::().as_ptr().add(self.offset as usize) as usize, mem::align_of::(), ) as *const Cell; Some(&*cell_ptr) } } #[inline] pub unsafe fn deref_mut<'a>(self, memory: &'a Memory) -> Option<&'a mut Cell> { if self.offset == 0 || (self.offset as usize) + mem::size_of::() >= memory.size().bytes().0 { return None; } let cell_ptr = align_pointer( memory.view::().as_ptr().add(self.offset as usize) as usize, mem::align_of::(), ) as *mut Cell; Some(&mut *cell_ptr) } } impl WasmPtr { #[inline] pub fn deref<'a>(self, memory: &'a Memory, index: u32, length: u32) -> Option<&'a [Cell]> { // gets the size of the item in the array with padding added such that // for any index, we will always result an aligned memory access let item_size = mem::size_of::() + (mem::size_of::() % mem::align_of::()); let slice_full_len = index as usize + length as usize; if self.offset == 0 || (self.offset as usize) + (item_size * slice_full_len) >= memory.size().bytes().0 { return None; } unsafe { let cell_ptr = align_pointer( memory.view::().as_ptr().add(self.offset as usize) as usize, mem::align_of::(), ) as *const Cell; let cell_ptrs = &std::slice::from_raw_parts(cell_ptr, slice_full_len) [index as usize..slice_full_len]; Some(cell_ptrs) } } #[inline] pub unsafe fn deref_mut<'a>( self, memory: &'a Memory, index: u32, length: u32, ) -> Option<&'a mut [Cell]> { // gets the size of the item in the array with padding added such that // for any index, we will always result an aligned memory access let item_size = mem::size_of::() + (mem::size_of::() % mem::align_of::()); let slice_full_len = index as usize + length as usize; if self.offset == 0 || (self.offset as usize) + (item_size * slice_full_len) >= memory.size().bytes().0 { return None; } let cell_ptr = align_pointer( memory.view::().as_ptr().add(self.offset as usize) as usize, mem::align_of::(), ) as *mut Cell; let cell_ptrs = &mut std::slice::from_raw_parts_mut(cell_ptr, slice_full_len) [index as usize..slice_full_len]; Some(cell_ptrs) } } unsafe impl WasmExternType for WasmPtr { type Native = i32; fn to_native(self) -> Self::Native { self.offset as i32 } fn from_native(n: Self::Native) -> Self { Self { offset: n as u32, _phantom: PhantomData, } } } unsafe impl ValueType for WasmPtr {} impl Clone for WasmPtr { fn clone(&self) -> Self { Self { offset: self.offset, _phantom: PhantomData, } } } impl Copy for WasmPtr {} impl PartialEq for WasmPtr { fn eq(&self, other: &Self) -> bool { self.offset == other.offset } } impl Eq for WasmPtr {} impl fmt::Debug for WasmPtr { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "WasmPtr({:#x})", self.offset) } }