pub(crate) mod js;
pub(crate) use js::*;
pub(crate) mod view;
pub(crate) use view::*;
pub(crate) mod buffer;
pub(crate) use buffer::*;
use wasm_bindgen::JsCast;
use wasmer_types::{MemoryError, MemoryType, Pages, WASM_PAGE_SIZE};
use crate::{
AsStoreMut, AsStoreRef, BackendMemory,
js::vm::memory::VMMemory,
vm::{VMExtern, VMExternMemory},
};
#[derive(Debug, Clone, Eq)]
pub struct Memory {
pub(crate) handle: VMMemory,
}
unsafe impl Send for Memory {}
unsafe impl Sync for Memory {}
impl Memory {
pub fn new(store: &mut impl AsStoreMut, ty: MemoryType) -> Result<Self, MemoryError> {
let vm_memory = VMMemory::new(Self::js_memory_from_type(&ty)?, ty);
Ok(Self::from_vm_extern(store, VMExternMemory::Js(vm_memory)))
}
pub(crate) fn js_memory_from_type(
ty: &MemoryType,
) -> Result<js_sys::WebAssembly::Memory, MemoryError> {
let descriptor = js_sys::Object::new();
#[allow(unused_unsafe)]
unsafe {
js_sys::Reflect::set(&descriptor, &"initial".into(), &ty.minimum.0.into()).unwrap();
if let Some(max) = ty.maximum {
js_sys::Reflect::set(&descriptor, &"maximum".into(), &max.0.into()).unwrap();
}
js_sys::Reflect::set(&descriptor, &"shared".into(), &ty.shared.into()).unwrap();
}
let js_memory = js_sys::WebAssembly::Memory::new(&descriptor).map_err(|e| {
let error_message = if let Some(s) = e.as_string() {
s
} else if let Some(obj) = e.dyn_ref::<js_sys::Object>() {
obj.to_string().into()
} else {
"Error while creating the memory".to_string()
};
MemoryError::Generic(error_message)
})?;
Ok(js_memory)
}
pub fn new_from_existing(new_store: &mut impl AsStoreMut, memory: VMMemory) -> Self {
Self::from_vm_extern(new_store, VMExternMemory::Js(memory))
}
pub(crate) fn to_vm_extern(&self) -> VMExtern {
VMExtern::Js(crate::js::vm::VMExtern::Memory(self.handle.clone()))
}
pub fn ty(&self, _store: &impl AsStoreRef) -> MemoryType {
self.handle.ty
}
pub fn view<'a>(&self, store: &'a impl AsStoreRef) -> MemoryView<'a> {
MemoryView::new(self, store)
}
pub fn size(&self, store: &impl AsStoreRef) -> Pages {
let js_memory = &self.handle.memory;
let our_js_memory: &JSMemory = JsCast::unchecked_from_js_ref(js_memory);
let buffer = our_js_memory.buffer();
let byte_length = match buffer.dyn_into::<js_sys::ArrayBuffer>() {
Ok(array_buffer) => array_buffer.byte_length(),
Err(buffer) => match buffer.dyn_into::<js_sys::SharedArrayBuffer>() {
Ok(array_buffer) => array_buffer.byte_length(),
Err(_) => {
unreachable!("Memory.buffer should be an array buffer or a shared array buffer")
}
},
};
Pages(byte_length.div_ceil(WASM_PAGE_SIZE as u32))
}
pub fn grow<IntoPages>(
&self,
store: &mut impl AsStoreMut,
delta: IntoPages,
) -> Result<Pages, MemoryError>
where
IntoPages: Into<Pages>,
{
let pages = delta.into();
let js_memory = &self.handle.memory;
let our_js_memory: &JSMemory = JsCast::unchecked_from_js_ref(js_memory);
let new_pages = our_js_memory.grow(pages.0).map_err(|err| {
if err.is_instance_of::<js_sys::RangeError>() {
MemoryError::CouldNotGrow {
current: self.view(&store.as_store_ref()).size(),
attempted_delta: pages,
}
} else {
MemoryError::Generic(err.as_string().unwrap())
}
})?;
Ok(Pages(new_pages))
}
pub fn grow_at_least(
&self,
store: &mut impl AsStoreMut,
min_size: u64,
) -> Result<(), MemoryError> {
let cur_size = self.view(store).data_size();
if min_size > cur_size {
let delta = min_size - cur_size;
let pages = ((delta - 1) / wasmer_types::WASM_PAGE_SIZE as u64) + 1;
self.grow(store, Pages(pages as u32))?;
}
Ok(())
}
pub fn reset(&self, _store: &mut impl AsStoreMut) -> Result<(), MemoryError> {
Ok(())
}
pub(crate) fn from_vm_extern(_store: &mut impl AsStoreMut, internal: VMExternMemory) -> Self {
Self {
handle: internal.into_js(),
}
}
pub fn try_clone(&self, _store: &impl AsStoreRef) -> Result<VMMemory, MemoryError> {
self.handle.try_clone()
}
pub fn try_copy(&self, store: &impl AsStoreRef) -> Result<VMMemory, MemoryError> {
let mut cloned = self.try_clone(store)?;
cloned.copy()
}
pub fn is_from_store(&self, _store: &impl AsStoreRef) -> bool {
true
}
pub fn as_shared(&self, _store: &impl AsStoreRef) -> Option<crate::shared::SharedMemory> {
None
}
}
impl std::cmp::PartialEq for Memory {
fn eq(&self, other: &Self) -> bool {
self.handle == other.handle
}
}
impl From<Memory> for crate::Memory {
fn from(value: Memory) -> Self {
Self(crate::BackendMemory::Js(value))
}
}
impl From<crate::Memory> for Memory {
fn from(value: crate::Memory) -> Self {
value.into_js()
}
}
impl crate::Memory {
pub fn into_js(self) -> crate::backend::js::memory::Memory {
match self.0 {
BackendMemory::Js(s) => s,
_ => panic!("Not a `js` memory!"),
}
}
pub fn as_js(&self) -> &crate::backend::js::memory::Memory {
match self.0 {
BackendMemory::Js(ref s) => s,
_ => panic!("Not a `js` memory!"),
}
}
pub fn as_js_mut(&mut self) -> &mut crate::backend::js::memory::Memory {
match self.0 {
BackendMemory::Js(ref mut s) => s,
_ => panic!("Not a `js` memory!"),
}
}
}