use crate::js::export::{Export, VMMemory};
use crate::js::exports::{ExportError, Exportable};
use crate::js::externals::Extern;
use crate::js::store::Store;
use crate::js::{MemoryType, MemoryView};
use std::convert::TryInto;
use thiserror::Error;
use wasm_bindgen::prelude::*;
use wasm_bindgen::JsCast;
use wasmer_types::{Bytes, Pages, ValueType};
#[derive(Error, Debug, Clone, PartialEq, Hash)]
pub enum MemoryError {
#[error("The memory could not grow: current size {} pages, requested increase: {} pages", current.0, attempted_delta.0)]
CouldNotGrow {
current: Pages,
attempted_delta: Pages,
},
#[error("A user-defined error occurred: {0}")]
Generic(String),
}
#[wasm_bindgen]
extern "C" {
#[wasm_bindgen(js_namespace = WebAssembly, extends = js_sys::Object, typescript_type = "WebAssembly.Memory")]
#[derive(Clone, Debug, PartialEq, Eq)]
pub type JSMemory;
#[wasm_bindgen(catch, method, js_namespace = WebAssembly)]
pub fn grow(this: &JSMemory, pages: u32) -> Result<u32, JsValue>;
}
#[derive(Debug, Clone, PartialEq)]
pub struct Memory {
store: Store,
vm_memory: VMMemory,
}
impl Memory {
pub fn new(store: &Store, ty: MemoryType) -> Result<Self, MemoryError> {
let descriptor = js_sys::Object::new();
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| MemoryError::Generic("Error while creating the memory".to_owned()))?;
let memory = VMMemory::new(js_memory, ty);
Ok(Self {
store: store.clone(),
vm_memory: memory,
})
}
pub fn ty(&self) -> MemoryType {
let mut ty = self.vm_memory.ty.clone();
ty.minimum = self.size();
ty
}
pub fn store(&self) -> &Store {
&self.store
}
pub unsafe fn data_unchecked(&self) -> &[u8] {
unimplemented!("direct data pointer access is not possible in JavaScript");
}
#[allow(clippy::mut_from_ref)]
pub unsafe fn data_unchecked_mut(&self) -> &mut [u8] {
unimplemented!("direct data pointer access is not possible in JavaScript");
}
pub fn data_ptr(&self) -> *mut u8 {
unimplemented!("direct data pointer access is not possible in JavaScript");
}
pub fn data_size(&self) -> u64 {
js_sys::Reflect::get(&self.vm_memory.memory.buffer(), &"byteLength".into())
.unwrap()
.as_f64()
.unwrap() as _
}
pub fn size(&self) -> Pages {
let bytes = js_sys::Reflect::get(&self.vm_memory.memory.buffer(), &"byteLength".into())
.unwrap()
.as_f64()
.unwrap() as u64;
Bytes(bytes as usize).try_into().unwrap()
}
pub fn grow<IntoPages>(&self, delta: IntoPages) -> Result<Pages, MemoryError>
where
IntoPages: Into<Pages>,
{
let pages = delta.into();
let js_memory = self.vm_memory.memory.clone().unchecked_into::<JSMemory>();
let new_pages = js_memory.grow(pages.0).map_err(|err| {
if err.is_instance_of::<js_sys::RangeError>() {
MemoryError::CouldNotGrow {
current: self.size(),
attempted_delta: pages,
}
} else {
MemoryError::Generic(err.as_string().unwrap())
}
})?;
Ok(Pages(new_pages))
}
pub fn view<T: ValueType>(&self) -> MemoryView<T> {
unimplemented!("The view function is not yet implemented in Wasmer Javascript");
}
#[doc(hidden)]
pub fn uint8view(&self) -> js_sys::Uint8Array {
js_sys::Uint8Array::new(&self.vm_memory.memory.buffer())
}
pub(crate) fn from_vm_export(store: &Store, vm_memory: VMMemory) -> Self {
Self {
store: store.clone(),
vm_memory,
}
}
pub fn same(&self, other: &Self) -> bool {
self.vm_memory == other.vm_memory
}
}
impl<'a> Exportable<'a> for Memory {
fn to_export(&self) -> Export {
Export::Memory(self.vm_memory.clone())
}
fn get_self_from_extern(_extern: &'a Extern) -> Result<&'a Self, ExportError> {
match _extern {
Extern::Memory(memory) => Ok(memory),
_ => Err(ExportError::IncompatibleType),
}
}
}