pub(crate) mod view;
pub(crate) use view::*;
pub(crate) mod buffer;
pub(crate) use buffer::*;
use crate::{AsStoreMut, AsStoreRef, BackendMemory, jsc::vm::VMMemory, vm::VMExtern};
use rusty_jsc::{JSObject, JSValue};
use wasmer_types::{MemoryError, MemoryType, Pages};
#[derive(Debug, Clone)]
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 handle = VMMemory::new(Self::js_memory_from_type(store, &ty)?, ty);
Ok(Self { handle })
}
pub(crate) fn js_memory_from_type(
store: &impl AsStoreRef,
ty: &MemoryType,
) -> Result<JSObject, MemoryError> {
let store_ref = store.as_store_ref();
let engine = store_ref.engine();
let context = engine.as_jsc().context();
let mut descriptor = JSObject::new(context);
descriptor.set_property(
context,
"initial".to_string(),
JSValue::number(context, ty.minimum.0.into()),
);
if let Some(max) = ty.maximum {
descriptor.set_property(
context,
"maximum".to_string(),
JSValue::number(context, max.0.into()),
);
}
descriptor.set_property(
context,
"shared".to_string(),
JSValue::boolean(context, ty.shared),
);
engine
.as_jsc()
.wasm_memory_type()
.construct(context, &[descriptor.to_jsvalue()])
.map_err(|e| MemoryError::Generic(format!("{e:?}")))
}
pub fn new_from_existing(new_store: &mut impl AsStoreMut, memory: VMMemory) -> Self {
Self {
handle: memory.clone(),
}
}
pub(crate) fn to_vm_extern(&self) -> VMExtern {
VMExtern::Jsc(crate::backend::jsc::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 store_ref = store.as_store_ref();
let engine = store_ref.engine();
let context = engine.as_jsc().context();
let js_memory = &self.handle.memory;
let buffer = js_memory
.get_property(context, "buffer")
.to_object(context)
.unwrap();
let byte_length = buffer
.get_property(context, "byteLength")
.to_number(context)
.unwrap() as u32;
Pages(byte_length.div_ceil(wasmer_types::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 store_mut = store.as_store_mut();
let engine = store_mut.engine();
let context = engine.as_jsc().context();
let func = self
.handle
.memory
.get_property(context, "grow".to_string())
.to_object(context)
.unwrap();
match func.call(
context,
Some(&self.handle.memory),
&[JSValue::number(context, pages.0 as _)],
) {
Ok(val) => Ok(Pages(val.to_number(context).unwrap() as _)),
Err(e) => {
let old_pages = pages;
Err(MemoryError::CouldNotGrow {
current: old_pages,
attempted_delta: 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 fn copy_to_store(
&self,
store: &impl AsStoreRef,
new_store: &mut impl AsStoreMut,
) -> Result<Self, MemoryError> {
let view = self.view(store);
let ty = self.ty(store);
let amount = view.data_size() as usize;
let new_memory = Self::new(new_store, ty)?;
let mut new_view = new_memory.view(&new_store);
let new_view_size = new_view.data_size() as usize;
if amount > new_view_size {
let delta = amount - new_view_size;
let pages = ((delta - 1) / wasmer_types::WASM_PAGE_SIZE) + 1;
new_memory.grow(new_store, Pages(pages as u32))?;
new_view = new_memory.view(&new_store);
}
view.copy_to_memory(amount as u64, &new_view)
.map_err(|err| MemoryError::Generic(err.to_string()))?;
Ok(new_memory)
}
pub(crate) fn from_vm_extern(
_store: &mut impl AsStoreMut,
internal: crate::vm::VMExternMemory,
) -> Self {
Self {
handle: internal.into_jsc(),
}
}
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(store)
}
pub fn is_from_store(&self, _store: &impl AsStoreRef) -> bool {
true
}
#[allow(unused)]
pub fn duplicate(&mut self, store: &impl AsStoreRef) -> Result<VMMemory, MemoryError> {
self.handle.copy(store)
}
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 std::cmp::Eq for Memory {}
impl crate::Memory {
pub fn into_jsc(self) -> crate::backend::jsc::memory::Memory {
match self.0 {
BackendMemory::Jsc(s) => s,
_ => panic!("Not a `jsc` memory!"),
}
}
pub fn as_jsc(&self) -> &crate::backend::jsc::memory::Memory {
match self.0 {
BackendMemory::Jsc(ref s) => s,
_ => panic!("Not a `jsc` memory!"),
}
}
pub fn as_jsc_mut(&mut self) -> &mut crate::backend::jsc::memory::Memory {
match self.0 {
BackendMemory::Jsc(ref mut s) => s,
_ => panic!("Not a `jsc` memory!"),
}
}
}