use crate::sys::{externals::Memory, FromToNativeWasmType}; use crate::NativeWasmTypeInto; use crate::{MemoryAccessError, WasmRef, WasmSlice}; use std::convert::TryFrom; use std::{fmt, marker::PhantomData, mem}; use wasmer_types::ValueType; use super::store::AsStoreRef; pub use wasmer_types::Memory32; pub use wasmer_types::Memory64; pub use wasmer_types::MemorySize; /// Alias for `WasmPtr. pub type WasmPtr64 = WasmPtr; /// A zero-cost type that represents a pointer to something in Wasm linear /// memory. /// /// This type can be used directly in the host function arguments: /// ``` /// # use wasmer::Memory; /// # use wasmer::WasmPtr; /// # use wasmer::FunctionEnvMut; /// pub fn host_import(mut ctx: FunctionEnvMut<()>, memory: Memory, ptr: WasmPtr) { /// let derefed_ptr = ptr.deref(&mut ctx, &memory); /// let inner_val: u32 = derefed_ptr.read().expect("pointer in bounds"); /// println!("Got {} from Wasm memory address 0x{:X}", inner_val, ptr.offset()); /// // update the value being pointed to /// derefed_ptr.write(inner_val + 1).expect("pointer in bounds"); /// } /// ``` /// /// This type can also be used with primitive-filled structs, but be careful of /// guarantees required by `ValueType`. /// ``` /// # use wasmer::Memory; /// # use wasmer::WasmPtr; /// # use wasmer::ValueType; /// # use wasmer::FunctionEnvMut; /// /// // This is safe as the 12 bytes represented by this struct /// // are valid for all bit combinations. /// #[derive(Copy, Clone, Debug, ValueType)] /// #[repr(C)] /// struct V3 { /// x: f32, /// y: f32, /// z: f32 /// } /// /// fn update_vector_3(mut ctx: FunctionEnvMut<()>, memory: Memory, ptr: WasmPtr) { /// let derefed_ptr = ptr.deref(&mut ctx, &memory); /// let mut inner_val: V3 = derefed_ptr.read().expect("pointer in bounds"); /// println!("Got {:?} from Wasm memory address 0x{:X}", inner_val, ptr.offset()); /// // update the value being pointed to /// inner_val.x = 10.4; /// derefed_ptr.write(inner_val).expect("pointer in bounds"); /// } /// ``` #[repr(transparent)] pub struct WasmPtr { offset: M::Offset, _phantom: PhantomData<*mut T>, } impl WasmPtr { /// Create a new `WasmPtr` at the given offset. #[inline] pub fn new(offset: M::Offset) -> Self { Self { offset, _phantom: PhantomData, } } /// Get the offset into Wasm linear memory for this `WasmPtr`. #[inline] pub fn offset(self) -> M::Offset { self.offset } /// Casts this `WasmPtr` to a `WasmPtr` of a different type. #[inline] pub fn cast(self) -> WasmPtr { WasmPtr { offset: self.offset, _phantom: PhantomData, } } /// Returns a null `UserPtr`. #[inline] pub fn null() -> Self { Self::new(M::ZERO) } /// Checks whether the `WasmPtr` is null. #[inline] pub fn is_null(self) -> bool { self.offset.into() == 0 } /// Calculates an offset from the current pointer address. The argument is /// in units of `T`. /// /// This method returns an error if an address overflow occurs. #[inline] pub fn add_offset(self, offset: M::Offset) -> Result { let base = self.offset.into(); let index = offset.into(); let offset = index .checked_mul(mem::size_of::() as u64) .ok_or(MemoryAccessError::Overflow)?; let address = base .checked_add(offset) .ok_or(MemoryAccessError::Overflow)?; let address = M::Offset::try_from(address).map_err(|_| MemoryAccessError::Overflow)?; Ok(Self::new(address)) } /// Calculates an offset from the current pointer address. The argument is /// in units of `T`. /// /// This method returns an error if an address underflow occurs. #[inline] pub fn sub_offset(self, offset: M::Offset) -> Result { let base = self.offset.into(); let index = offset.into(); let offset = index .checked_mul(mem::size_of::() as u64) .ok_or(MemoryAccessError::Overflow)?; let address = base .checked_sub(offset) .ok_or(MemoryAccessError::Overflow)?; let address = M::Offset::try_from(address).map_err(|_| MemoryAccessError::Overflow)?; Ok(Self::new(address)) } } impl WasmPtr { /// Creates a `WasmRef` from this `WasmPtr` which allows reading and /// mutating of the value being pointed to. #[inline] pub fn deref<'a>(self, ctx: &'a impl AsStoreRef, memory: &'a Memory) -> WasmRef<'a, T> { WasmRef::new(ctx, memory, self.offset.into()) } /// Reads the address pointed to by this `WasmPtr` in a memory. #[inline] pub fn read(self, ctx: &impl AsStoreRef, memory: &Memory) -> Result { self.deref(&ctx, memory).read() } /// Writes to the address pointed to by this `WasmPtr` in a memory. #[inline] pub fn write( self, ctx: &impl AsStoreRef, memory: &Memory, val: T, ) -> Result<(), MemoryAccessError> { self.deref(&ctx, memory).write(val) } /// Creates a `WasmSlice` starting at this `WasmPtr` which allows reading /// and mutating of an array of value being pointed to. /// /// Returns a `MemoryAccessError` if the slice length overflows a 64-bit /// address. #[inline] pub fn slice<'a>( self, ctx: &'a impl AsStoreRef, memory: &'a Memory, len: M::Offset, ) -> Result, MemoryAccessError> { WasmSlice::new(ctx, memory, self.offset.into(), len.into()) } /// Reads a sequence of values from this `WasmPtr` until a value that /// matches the given condition is found. /// /// This last value is not included in the returned vector. #[inline] pub fn read_until( self, ctx: &impl AsStoreRef, memory: &Memory, mut end: impl FnMut(&T) -> bool, ) -> Result, MemoryAccessError> { let mut vec = Vec::new(); for i in 0u64.. { let i = M::Offset::try_from(i).map_err(|_| MemoryAccessError::Overflow)?; let val = self.add_offset(i)?.deref(&ctx, memory).read()?; if end(&val) { break; } vec.push(val); } Ok(vec) } } impl WasmPtr { /// Reads a UTF-8 string from the `WasmPtr` with the given length. /// /// This method is safe to call even if the memory is being concurrently /// modified. #[inline] pub fn read_utf8_string( self, ctx: &impl AsStoreRef, memory: &Memory, len: M::Offset, ) -> Result { let vec = self.slice(&ctx, memory, len)?.read_to_vec()?; Ok(String::from_utf8(vec)?) } /// Reads a null-terminated UTF-8 string from the `WasmPtr`. /// /// This method is safe to call even if the memory is being concurrently /// modified. #[inline] pub fn read_utf8_string_with_nul( self, ctx: &impl AsStoreRef, memory: &Memory, ) -> Result { let vec = self.read_until(ctx, memory, |&byte| byte == 0)?; Ok(String::from_utf8(vec)?) } } unsafe impl FromToNativeWasmType for WasmPtr where ::Native: NativeWasmTypeInto, { type Native = M::Native; fn to_native(self) -> Self::Native { M::offset_to_native(self.offset) } fn from_native(n: Self::Native) -> Self { Self { offset: M::native_to_offset(n), _phantom: PhantomData, } } } unsafe impl ValueType for WasmPtr { fn zero_padding_bytes(&self, _bytes: &mut [mem::MaybeUninit]) {} } 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.into() == other.offset.into() } } impl Eq for WasmPtr {} impl fmt::Debug for WasmPtr { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!( f, "WasmPtr(offset: {}, pointer: {:#x})", self.offset.into(), self.offset.into() ) } }