/src/wasmer/lib/api/src/ptr.rs
Line | Count | Source (jump to first uncovered line) |
1 | | use crate::access::WasmRefAccess; |
2 | | use crate::mem_access::{MemoryAccessError, WasmRef, WasmSlice}; |
3 | | use crate::{AsStoreRef, FromToNativeWasmType, MemoryView, NativeWasmTypeInto}; |
4 | | use std::convert::TryFrom; |
5 | | use std::{fmt, marker::PhantomData, mem}; |
6 | | pub use wasmer_types::Memory32; |
7 | | pub use wasmer_types::Memory64; |
8 | | pub use wasmer_types::MemorySize; |
9 | | use wasmer_types::ValueType; |
10 | | |
11 | | /// Alias for `WasmPtr<T, Memory64>. |
12 | | pub type WasmPtr64<T> = WasmPtr<T, Memory64>; |
13 | | |
14 | | /// A zero-cost type that represents a pointer to something in Wasm linear |
15 | | /// memory. |
16 | | /// |
17 | | /// This type can be used directly in the host function arguments: |
18 | | /// ``` |
19 | | /// # use wasmer::Memory; |
20 | | /// # use wasmer::WasmPtr; |
21 | | /// # use wasmer::FunctionEnvMut; |
22 | | /// pub fn host_import(mut env: FunctionEnvMut<()>, memory: Memory, ptr: WasmPtr<u32>) { |
23 | | /// let memory = memory.view(&env); |
24 | | /// let derefed_ptr = ptr.deref(&memory); |
25 | | /// let inner_val: u32 = derefed_ptr.read().expect("pointer in bounds"); |
26 | | /// println!("Got {} from Wasm memory address 0x{:X}", inner_val, ptr.offset()); |
27 | | /// // update the value being pointed to |
28 | | /// derefed_ptr.write(inner_val + 1).expect("pointer in bounds"); |
29 | | /// } |
30 | | /// ``` |
31 | | /// |
32 | | /// This type can also be used with primitive-filled structs, but be careful of |
33 | | /// guarantees required by `ValueType`. |
34 | | /// ``` |
35 | | /// # use wasmer::Memory; |
36 | | /// # use wasmer::WasmPtr; |
37 | | /// # use wasmer::ValueType; |
38 | | /// # use wasmer::FunctionEnvMut; |
39 | | /// |
40 | | /// // This is safe as the 12 bytes represented by this struct |
41 | | /// // are valid for all bit combinations. |
42 | | /// #[derive(Copy, Clone, Debug, ValueType)] |
43 | | /// #[repr(C)] |
44 | | /// struct V3 { |
45 | | /// x: f32, |
46 | | /// y: f32, |
47 | | /// z: f32 |
48 | | /// } |
49 | | /// |
50 | | /// fn update_vector_3(mut env: FunctionEnvMut<()>, memory: Memory, ptr: WasmPtr<V3>) { |
51 | | /// let memory = memory.view(&env); |
52 | | /// let derefed_ptr = ptr.deref(&memory); |
53 | | /// let mut inner_val: V3 = derefed_ptr.read().expect("pointer in bounds"); |
54 | | /// println!("Got {:?} from Wasm memory address 0x{:X}", inner_val, ptr.offset()); |
55 | | /// // update the value being pointed to |
56 | | /// inner_val.x = 10.4; |
57 | | /// derefed_ptr.write(inner_val).expect("pointer in bounds"); |
58 | | /// } |
59 | | /// ``` |
60 | | #[repr(transparent)] |
61 | | pub struct WasmPtr<T, M: MemorySize = Memory32> { |
62 | | offset: M::Offset, |
63 | | _phantom: PhantomData<T>, |
64 | | } |
65 | | |
66 | | impl<T, M: MemorySize> WasmPtr<T, M> { |
67 | | /// Create a new `WasmPtr` at the given offset. |
68 | | #[inline] |
69 | 0 | pub fn new(offset: M::Offset) -> Self { |
70 | 0 | Self { |
71 | 0 | offset, |
72 | 0 | _phantom: PhantomData, |
73 | 0 | } |
74 | 0 | } Unexecuted instantiation: <wasmer::ptr::WasmPtr<_, _>>::new Unexecuted instantiation: <wasmer::ptr::WasmPtr<_, _>>::new |
75 | | |
76 | | /// Get the offset into Wasm linear memory for this `WasmPtr`. |
77 | | #[inline] |
78 | 0 | pub fn offset(&self) -> M::Offset { |
79 | 0 | self.offset |
80 | 0 | } Unexecuted instantiation: <wasmer::ptr::WasmPtr<_, _>>::offset Unexecuted instantiation: <wasmer::ptr::WasmPtr<_, _>>::offset |
81 | | |
82 | | /// Casts this `WasmPtr` to a `WasmPtr` of a different type. |
83 | | #[inline] |
84 | 0 | pub fn cast<U>(self) -> WasmPtr<U, M> { |
85 | 0 | WasmPtr { |
86 | 0 | offset: self.offset, |
87 | 0 | _phantom: PhantomData, |
88 | 0 | } |
89 | 0 | } Unexecuted instantiation: <wasmer::ptr::WasmPtr<_, _>>::cast::<_> Unexecuted instantiation: <wasmer::ptr::WasmPtr<_, _>>::cast::<_> |
90 | | |
91 | | /// Returns a null `UserPtr`. |
92 | | #[inline] |
93 | 0 | pub fn null() -> Self { |
94 | 0 | Self::new(M::ZERO) |
95 | 0 | } Unexecuted instantiation: <wasmer::ptr::WasmPtr<_, _>>::null Unexecuted instantiation: <wasmer::ptr::WasmPtr<_, _>>::null |
96 | | |
97 | | /// Checks whether the `WasmPtr` is null. |
98 | | #[inline] |
99 | 0 | pub fn is_null(&self) -> bool { |
100 | 0 | self.offset.into() == 0 |
101 | 0 | } Unexecuted instantiation: <wasmer::ptr::WasmPtr<_, _>>::is_null Unexecuted instantiation: <wasmer::ptr::WasmPtr<_, _>>::is_null |
102 | | |
103 | | /// Calculates an offset from the current pointer address. The argument is |
104 | | /// in units of `T`. |
105 | | /// |
106 | | /// This method returns an error if an address overflow occurs. |
107 | | #[inline] |
108 | 0 | pub fn add_offset(self, offset: M::Offset) -> Result<Self, MemoryAccessError> { |
109 | 0 | let base = self.offset.into(); |
110 | 0 | let index = offset.into(); |
111 | 0 | let offset = index |
112 | 0 | .checked_mul(mem::size_of::<T>() as u64) |
113 | 0 | .ok_or(MemoryAccessError::Overflow)?; |
114 | 0 | let address = base |
115 | 0 | .checked_add(offset) |
116 | 0 | .ok_or(MemoryAccessError::Overflow)?; |
117 | 0 | let address = M::Offset::try_from(address).map_err(|_| MemoryAccessError::Overflow)?; Unexecuted instantiation: <wasmer::ptr::WasmPtr<_, _>>::add_offset::{closure#0}Unexecuted instantiation: <wasmer::ptr::WasmPtr<_, _>>::add_offset::{closure#0} |
118 | 0 | Ok(Self::new(address)) |
119 | 0 | } Unexecuted instantiation: <wasmer::ptr::WasmPtr<_, _>>::add_offset Unexecuted instantiation: <wasmer::ptr::WasmPtr<_, _>>::add_offset |
120 | | |
121 | | /// Calculates an offset from the current pointer address. The argument is |
122 | | /// in units of `T`. |
123 | | /// |
124 | | /// This method returns an error if an address underflow occurs. |
125 | | #[inline] |
126 | 0 | pub fn sub_offset(self, offset: M::Offset) -> Result<Self, MemoryAccessError> { |
127 | 0 | let base = self.offset.into(); |
128 | 0 | let index = offset.into(); |
129 | 0 | let offset = index |
130 | 0 | .checked_mul(mem::size_of::<T>() as u64) |
131 | 0 | .ok_or(MemoryAccessError::Overflow)?; |
132 | 0 | let address = base |
133 | 0 | .checked_sub(offset) |
134 | 0 | .ok_or(MemoryAccessError::Overflow)?; |
135 | 0 | let address = M::Offset::try_from(address).map_err(|_| MemoryAccessError::Overflow)?; Unexecuted instantiation: <wasmer::ptr::WasmPtr<_, _>>::sub_offset::{closure#0}Unexecuted instantiation: <wasmer::ptr::WasmPtr<_, _>>::sub_offset::{closure#0} |
136 | 0 | Ok(Self::new(address)) |
137 | 0 | } Unexecuted instantiation: <wasmer::ptr::WasmPtr<_, _>>::sub_offset Unexecuted instantiation: <wasmer::ptr::WasmPtr<_, _>>::sub_offset |
138 | | } |
139 | | |
140 | | impl<T: ValueType, M: MemorySize> WasmPtr<T, M> { |
141 | | /// Creates a `WasmRef` from this `WasmPtr` which allows reading and |
142 | | /// mutating of the value being pointed to. |
143 | | #[inline] |
144 | 0 | pub fn deref<'a>(&self, view: &'a MemoryView) -> WasmRef<'a, T> { |
145 | 0 | WasmRef::new(view, self.offset.into()) |
146 | 0 | } Unexecuted instantiation: <wasmer::ptr::WasmPtr<_, _>>::deref Unexecuted instantiation: <wasmer::ptr::WasmPtr<_, _>>::deref |
147 | | |
148 | | /// Reads the address pointed to by this `WasmPtr` in a memory. |
149 | | #[inline] |
150 | 0 | pub fn read(&self, view: &MemoryView) -> Result<T, MemoryAccessError> { |
151 | 0 | self.deref(view).read() |
152 | 0 | } Unexecuted instantiation: <wasmer::ptr::WasmPtr<_, _>>::read Unexecuted instantiation: <wasmer::ptr::WasmPtr<_, _>>::read |
153 | | |
154 | | /// Writes to the address pointed to by this `WasmPtr` in a memory. |
155 | | #[inline] |
156 | 0 | pub fn write(&self, view: &MemoryView, val: T) -> Result<(), MemoryAccessError> { |
157 | 0 | self.deref(view).write(val) |
158 | 0 | } Unexecuted instantiation: <wasmer::ptr::WasmPtr<_, _>>::write Unexecuted instantiation: <wasmer::ptr::WasmPtr<_, _>>::write |
159 | | |
160 | | /// Creates a `WasmSlice` starting at this `WasmPtr` which allows reading |
161 | | /// and mutating of an array of value being pointed to. |
162 | | /// |
163 | | /// Returns a `MemoryAccessError` if the slice length overflows a 64-bit |
164 | | /// address. |
165 | | #[inline] |
166 | 0 | pub fn slice<'a>( |
167 | 0 | &self, |
168 | 0 | view: &'a MemoryView, |
169 | 0 | len: M::Offset, |
170 | 0 | ) -> Result<WasmSlice<'a, T>, MemoryAccessError> { |
171 | 0 | WasmSlice::new(view, self.offset.into(), len.into()) |
172 | 0 | } Unexecuted instantiation: <wasmer::ptr::WasmPtr<_, _>>::slice Unexecuted instantiation: <wasmer::ptr::WasmPtr<_, _>>::slice |
173 | | |
174 | | /// Reads a sequence of values from this `WasmPtr` until a value that |
175 | | /// matches the given condition is found. |
176 | | /// |
177 | | /// This last value is not included in the returned vector. |
178 | | #[inline] |
179 | 0 | pub fn read_until( |
180 | 0 | &self, |
181 | 0 | view: &MemoryView, |
182 | 0 | mut end: impl FnMut(&T) -> bool, |
183 | 0 | ) -> Result<Vec<T>, MemoryAccessError> { |
184 | 0 | let mut vec = Vec::new(); |
185 | 0 | for i in 0u64.. { |
186 | 0 | let i = M::Offset::try_from(i).map_err(|_| MemoryAccessError::Overflow)?; Unexecuted instantiation: <wasmer::ptr::WasmPtr<_, _>>::read_until::<_>::{closure#0}Unexecuted instantiation: <wasmer::ptr::WasmPtr<_, _>>::read_until::<_>::{closure#0} |
187 | 0 | let val = self.add_offset(i)?.deref(view).read()?; |
188 | 0 | if end(&val) { |
189 | 0 | break; |
190 | 0 | } |
191 | 0 | vec.push(val); |
192 | | } |
193 | 0 | Ok(vec) |
194 | 0 | } Unexecuted instantiation: <wasmer::ptr::WasmPtr<_, _>>::read_until::<_> Unexecuted instantiation: <wasmer::ptr::WasmPtr<_, _>>::read_until::<_> |
195 | | |
196 | | /// Creates a `WasmAccess` |
197 | | #[inline] |
198 | 0 | pub fn access<'a>( |
199 | 0 | &self, |
200 | 0 | view: &'a MemoryView, |
201 | 0 | ) -> Result<WasmRefAccess<'a, T>, MemoryAccessError> { |
202 | 0 | self.deref(view).access() |
203 | 0 | } Unexecuted instantiation: <wasmer::ptr::WasmPtr<_, _>>::access Unexecuted instantiation: <wasmer::ptr::WasmPtr<_, _>>::access |
204 | | } |
205 | | |
206 | | impl<M: MemorySize> WasmPtr<u8, M> { |
207 | | /// Reads a UTF-8 string from the `WasmPtr` with the given length. |
208 | | /// |
209 | | /// This method is safe to call even if the memory is being concurrently |
210 | | /// modified. |
211 | | #[inline] |
212 | 0 | pub fn read_utf8_string( |
213 | 0 | &self, |
214 | 0 | view: &MemoryView, |
215 | 0 | len: M::Offset, |
216 | 0 | ) -> Result<String, MemoryAccessError> { |
217 | 0 | let vec = self.slice(view, len)?.read_to_vec()?; |
218 | 0 | Ok(String::from_utf8(vec)?) |
219 | 0 | } Unexecuted instantiation: <wasmer::ptr::WasmPtr<u8, _>>::read_utf8_string Unexecuted instantiation: <wasmer::ptr::WasmPtr<u8, _>>::read_utf8_string |
220 | | |
221 | | /// Reads a null-terminated UTF-8 string from the `WasmPtr`. |
222 | | /// |
223 | | /// This method is safe to call even if the memory is being concurrently |
224 | | /// modified. |
225 | | #[inline] |
226 | 0 | pub fn read_utf8_string_with_nul( |
227 | 0 | &self, |
228 | 0 | view: &MemoryView, |
229 | 0 | ) -> Result<String, MemoryAccessError> { |
230 | 0 | let vec = self.read_until(view, |&byte| byte == 0)?; Unexecuted instantiation: <wasmer::ptr::WasmPtr<u8, _>>::read_utf8_string_with_nul::{closure#0}Unexecuted instantiation: <wasmer::ptr::WasmPtr<u8, _>>::read_utf8_string_with_nul::{closure#0} |
231 | 0 | Ok(String::from_utf8(vec)?) |
232 | 0 | } Unexecuted instantiation: <wasmer::ptr::WasmPtr<u8, _>>::read_utf8_string_with_nul Unexecuted instantiation: <wasmer::ptr::WasmPtr<u8, _>>::read_utf8_string_with_nul |
233 | | } |
234 | | |
235 | | unsafe impl<T: ValueType, M: MemorySize> FromToNativeWasmType for WasmPtr<T, M> |
236 | | where |
237 | | <M as wasmer_types::MemorySize>::Native: NativeWasmTypeInto, |
238 | | { |
239 | | type Native = M::Native; |
240 | | |
241 | 0 | fn to_native(self) -> Self::Native { |
242 | 0 | M::offset_to_native(self.offset) |
243 | 0 | } Unexecuted instantiation: <wasmer::ptr::WasmPtr<_, _> as wasmer::native_type::FromToNativeWasmType>::to_native Unexecuted instantiation: <wasmer::ptr::WasmPtr<_, _> as wasmer::native_type::FromToNativeWasmType>::to_native |
244 | 0 | fn from_native(n: Self::Native) -> Self { |
245 | 0 | Self { |
246 | 0 | offset: M::native_to_offset(n), |
247 | 0 | _phantom: PhantomData, |
248 | 0 | } |
249 | 0 | } Unexecuted instantiation: <wasmer::ptr::WasmPtr<_, _> as wasmer::native_type::FromToNativeWasmType>::from_native Unexecuted instantiation: <wasmer::ptr::WasmPtr<_, _> as wasmer::native_type::FromToNativeWasmType>::from_native |
250 | | #[inline] |
251 | 0 | fn is_from_store(&self, _store: &impl AsStoreRef) -> bool { |
252 | 0 | true // in Javascript there are no different stores |
253 | 0 | } Unexecuted instantiation: <wasmer::ptr::WasmPtr<_, _> as wasmer::native_type::FromToNativeWasmType>::is_from_store::<_> Unexecuted instantiation: <wasmer::ptr::WasmPtr<_, _> as wasmer::native_type::FromToNativeWasmType>::is_from_store::<_> |
254 | | } |
255 | | |
256 | | unsafe impl<T: ValueType, M: MemorySize> ValueType for WasmPtr<T, M> { |
257 | 0 | fn zero_padding_bytes(&self, _bytes: &mut [mem::MaybeUninit<u8>]) {}Unexecuted instantiation: <wasmer::ptr::WasmPtr<_, _> as wasmer_types::value::ValueType>::zero_padding_bytes Unexecuted instantiation: <wasmer::ptr::WasmPtr<_, _> as wasmer_types::value::ValueType>::zero_padding_bytes |
258 | | } |
259 | | |
260 | | impl<T: ValueType, M: MemorySize> Clone for WasmPtr<T, M> { |
261 | 0 | fn clone(&self) -> Self { |
262 | 0 | *self |
263 | 0 | } Unexecuted instantiation: <wasmer::ptr::WasmPtr<_, _> as core::clone::Clone>::clone Unexecuted instantiation: <wasmer::ptr::WasmPtr<_, _> as core::clone::Clone>::clone |
264 | | } |
265 | | |
266 | | impl<T: ValueType, M: MemorySize> Copy for WasmPtr<T, M> {} |
267 | | |
268 | | impl<T: ValueType, M: MemorySize> PartialEq for WasmPtr<T, M> { |
269 | 0 | fn eq(&self, other: &Self) -> bool { |
270 | 0 | self.offset.into() == other.offset.into() |
271 | 0 | } Unexecuted instantiation: <wasmer::ptr::WasmPtr<_, _> as core::cmp::PartialEq>::eq Unexecuted instantiation: <wasmer::ptr::WasmPtr<_, _> as core::cmp::PartialEq>::eq |
272 | | } |
273 | | |
274 | | impl<T: ValueType, M: MemorySize> Eq for WasmPtr<T, M> {} |
275 | | |
276 | | impl<T: ValueType, M: MemorySize> fmt::Debug for WasmPtr<T, M> { |
277 | 0 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
278 | 0 | write!(f, "{}(@{})", std::any::type_name::<T>(), self.offset.into()) |
279 | 0 | } Unexecuted instantiation: <wasmer::ptr::WasmPtr<_, _> as core::fmt::Debug>::fmt Unexecuted instantiation: <wasmer::ptr::WasmPtr<_, _> as core::fmt::Debug>::fmt |
280 | | } |