use crate::js::store::AsStoreRef;
use crate::js::MemoryAccessError;
use std::convert::TryInto;
use std::marker::PhantomData;
use std::mem::MaybeUninit;
use std::slice;
#[cfg(feature = "tracing")]
use tracing::warn;
use wasmer_types::{Bytes, Pages};
use super::memory::MemoryBuffer;
use super::Memory;
#[derive(Debug)]
pub struct MemoryView<'a> {
view: js_sys::Uint8Array,
size: u64,
marker: PhantomData<&'a Memory>,
}
impl<'a> MemoryView<'a> {
pub(crate) fn new(memory: &Memory, store: &impl AsStoreRef) -> Self {
let memory = memory.handle.get(store.as_store_ref().objects());
Self::new_raw(&memory.memory)
}
pub(crate) fn new_raw(memory: &js_sys::WebAssembly::Memory) -> Self {
let buffer = memory.buffer();
let size = js_sys::Reflect::get(&buffer, &"byteLength".into())
.unwrap()
.as_f64()
.unwrap() as u64;
let view = js_sys::Uint8Array::new(&buffer);
Self {
view,
size,
marker: PhantomData,
}
}
#[doc(hidden)]
pub fn data_ptr(&self) -> *mut u8 {
unimplemented!("direct data pointer access is not possible in JavaScript");
}
pub fn data_size(&self) -> u64 {
self.size
}
#[doc(hidden)]
pub unsafe fn data_unchecked(&self) -> &[u8] {
unimplemented!("direct data pointer access is not possible in JavaScript");
}
#[allow(clippy::mut_from_ref)]
#[doc(hidden)]
pub unsafe fn data_unchecked_mut(&self) -> &mut [u8] {
unimplemented!("direct data pointer access is not possible in JavaScript");
}
pub fn size(&self) -> Pages {
Bytes(self.size as usize).try_into().unwrap()
}
pub(crate) fn buffer(&self) -> MemoryBuffer<'a> {
MemoryBuffer {
base: &self.view as *const _ as *mut _,
marker: PhantomData,
}
}
pub fn read(&self, offset: u64, data: &mut [u8]) -> Result<(), MemoryAccessError> {
let view = &self.view;
let offset: u32 = offset.try_into().map_err(|_| MemoryAccessError::Overflow)?;
let len: u32 = data
.len()
.try_into()
.map_err(|_| MemoryAccessError::Overflow)?;
let end = offset.checked_add(len).ok_or(MemoryAccessError::Overflow)?;
if end > view.length() {
#[cfg(feature = "tracing")]
warn!(
"attempted to read ({} bytes) beyond the bounds of the memory view ({} > {})",
len,
end,
view.length()
);
Err(MemoryAccessError::HeapOutOfBounds)?;
}
view.subarray(offset, end).copy_to(data);
Ok(())
}
pub fn read_u8(&self, offset: u64) -> Result<u8, MemoryAccessError> {
let view = &self.view;
let offset: u32 = offset.try_into().map_err(|_| MemoryAccessError::Overflow)?;
if offset >= view.length() {
#[cfg(feature = "tracing")]
warn!(
"attempted to read beyond the bounds of the memory view ({} >= {})",
offset,
view.length()
);
Err(MemoryAccessError::HeapOutOfBounds)?;
}
Ok(view.get_index(offset))
}
pub fn read_uninit(
&self,
offset: u64,
buf: &'a mut [MaybeUninit<u8>],
) -> Result<&'a mut [u8], MemoryAccessError> {
let view = &self.view;
let offset: u32 = offset.try_into().map_err(|_| MemoryAccessError::Overflow)?;
let len: u32 = buf
.len()
.try_into()
.map_err(|_| MemoryAccessError::Overflow)?;
let end = offset.checked_add(len).ok_or(MemoryAccessError::Overflow)?;
if end > view.length() {
#[cfg(feature = "tracing")]
warn!(
"attempted to read ({} bytes) beyond the bounds of the memory view ({} > {})",
len,
end,
view.length()
);
Err(MemoryAccessError::HeapOutOfBounds)?;
}
for elem in buf.iter_mut() {
*elem = MaybeUninit::new(0);
}
let buf = unsafe { slice::from_raw_parts_mut(buf.as_mut_ptr() as *mut u8, buf.len()) };
view.subarray(offset, end).copy_to(buf);
Ok(buf)
}
pub fn write(&self, offset: u64, data: &[u8]) -> Result<(), MemoryAccessError> {
let offset: u32 = offset.try_into().map_err(|_| MemoryAccessError::Overflow)?;
let len: u32 = data
.len()
.try_into()
.map_err(|_| MemoryAccessError::Overflow)?;
let view = &self.view;
let end = offset.checked_add(len).ok_or(MemoryAccessError::Overflow)?;
if end > view.length() {
#[cfg(feature = "tracing")]
warn!(
"attempted to write ({} bytes) beyond the bounds of the memory view ({} > {})",
len,
end,
view.length()
);
Err(MemoryAccessError::HeapOutOfBounds)?;
}
view.subarray(offset, end).copy_from(data);
Ok(())
}
pub fn write_u8(&self, offset: u64, val: u8) -> Result<(), MemoryAccessError> {
let view = &self.view;
let offset: u32 = offset.try_into().map_err(|_| MemoryAccessError::Overflow)?;
if offset >= view.length() {
#[cfg(feature = "tracing")]
warn!(
"attempted to write beyond the bounds of the memory view ({} >= {})",
offset,
view.length()
);
Err(MemoryAccessError::HeapOutOfBounds)?;
}
view.set_index(offset, val);
Ok(())
}
pub fn copy_to_vec(&self) -> Result<Vec<u8>, MemoryAccessError> {
let mut new_memory = Vec::new();
let mut offset = 0;
let mut chunk = [0u8; 40960];
while offset < self.data_size() {
let remaining = self.data_size() - offset;
let sublen = remaining.min(chunk.len() as u64) as usize;
self.read(offset, &mut chunk[..sublen])?;
new_memory.extend_from_slice(&chunk[..sublen]);
offset += sublen as u64;
}
Ok(new_memory)
}
pub fn copy_to_memory(&self, amount: u64, new_memory: &Self) -> Result<(), MemoryAccessError> {
let mut offset = 0;
let mut chunk = [0u8; 40960];
while offset < amount {
let remaining = amount - offset;
let sublen = remaining.min(chunk.len() as u64) as usize;
self.read(offset, &mut chunk[..sublen])?;
new_memory.write(offset, &chunk[..sublen])?;
offset += sublen as u64;
}
Ok(())
}
}