use std::{any::Any, fmt};
use js_sys::{
Function as JsFunction,
WebAssembly::{self, Memory as JsMemory, Table as JsTable},
};
use serde::{Deserialize, Serialize};
use tracing::trace;
use wasm_bindgen::{JsCast, JsValue};
use wasmer_types::{
FunctionType, GlobalType, MemoryError, MemoryType, Pages, RawValue, TableType, WASM_PAGE_SIZE,
};
use crate::js::{js_handle::JsHandle, wasm_bindgen_polyfill::Global as JsGlobal};
#[derive(Clone, Debug, PartialEq)]
pub struct VMMemory {
pub(crate) memory: JsHandle<JsMemory>,
pub(crate) ty: MemoryType,
}
unsafe impl Send for VMMemory {}
unsafe impl Sync for VMMemory {}
#[derive(Serialize, Deserialize)]
struct DummyBuffer {
#[serde(rename = "byteLength")]
byte_length: u32,
}
impl VMMemory {
pub fn new(memory: JsMemory, ty: MemoryType) -> Self {
Self {
memory: JsHandle::new(memory),
ty,
}
}
pub fn get_runtime_size(&self) -> u32 {
let dummy: DummyBuffer = match serde_wasm_bindgen::from_value(self.memory.buffer()) {
Ok(o) => o,
Err(_) => return 0,
};
if dummy.byte_length == 0 {
return 0;
}
dummy.byte_length / WASM_PAGE_SIZE as u32
}
pub(crate) fn try_clone(&self) -> Result<VMMemory, MemoryError> {
Ok(self.clone())
}
#[deprecated = "use `copy` instead"]
pub fn duplicate(&mut self) -> Result<VMMemory, wasmer_types::MemoryError> {
self.copy()
}
pub fn copy(&mut self) -> Result<VMMemory, wasmer_types::MemoryError> {
let new_memory = crate::js::externals::memory::Memory::js_memory_from_type(&self.ty)?;
let src = crate::js::externals::memory_view::MemoryView::new_raw(&self.memory);
let amount = src.data_size() as usize;
trace!(%amount, "memory copy started");
let mut dst = crate::js::externals::memory_view::MemoryView::new_raw(&new_memory);
let dst_size = dst.data_size() as usize;
if amount > dst_size {
let delta = amount - dst_size;
let pages = ((delta - 1) / WASM_PAGE_SIZE) + 1;
let our_js_memory: &crate::js::externals::memory::JSMemory =
JsCast::unchecked_from_js_ref(&new_memory);
our_js_memory.grow(pages as u32).map_err(|err| {
if err.is_instance_of::<js_sys::RangeError>() {
let cur_pages = dst_size;
MemoryError::CouldNotGrow {
current: Pages(cur_pages as u32),
attempted_delta: Pages(pages as u32),
}
} else {
MemoryError::Generic(err.as_string().unwrap())
}
})?;
dst = crate::js::externals::memory_view::MemoryView::new_raw(&new_memory);
}
src.copy_to_memory(amount as u64, &dst).map_err(|err| {
wasmer_types::MemoryError::Generic(format!("failed to copy the memory - {}", err))
})?;
trace!("memory copy finished (size={})", dst.size().bytes().0);
Ok(Self {
memory: JsHandle::new(new_memory),
ty: self.ty.clone(),
})
}
}
impl From<VMMemory> for JsValue {
fn from(value: VMMemory) -> Self {
JsValue::from(value.memory)
}
}
impl From<VMMemory> for (JsValue, MemoryType) {
fn from(value: VMMemory) -> Self {
(JsValue::from(value.memory), value.ty)
}
}
pub type VMSharedMemory = VMMemory;
#[derive(Clone, Debug, PartialEq)]
pub struct VMGlobal {
pub(crate) global: JsGlobal,
pub(crate) ty: GlobalType,
}
impl VMGlobal {
pub(crate) fn new(global: JsGlobal, ty: GlobalType) -> Self {
Self { global, ty }
}
}
unsafe impl Send for VMGlobal {}
unsafe impl Sync for VMGlobal {}
#[derive(Clone, Debug, PartialEq)]
pub struct VMTable {
pub(crate) table: JsTable,
pub(crate) ty: TableType,
}
unsafe impl Send for VMTable {}
unsafe impl Sync for VMTable {}
impl VMTable {
pub(crate) fn new(table: JsTable, ty: TableType) -> Self {
Self { table, ty }
}
pub fn get_runtime_size(&self) -> u32 {
self.table.length()
}
}
#[derive(Clone)]
pub struct VMFunction {
pub(crate) function: JsHandle<JsFunction>,
pub(crate) ty: FunctionType,
}
unsafe impl Send for VMFunction {}
unsafe impl Sync for VMFunction {}
impl VMFunction {
pub(crate) fn new(function: JsFunction, ty: FunctionType) -> Self {
Self {
function: JsHandle::new(function),
ty,
}
}
}
impl PartialEq for VMFunction {
fn eq(&self, other: &Self) -> bool {
self.function == other.function
}
}
impl fmt::Debug for VMFunction {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("VMFunction")
.field("function", &self.function)
.finish()
}
}
pub enum VMExtern {
Function(VMFunction),
Table(VMTable),
Memory(VMMemory),
Global(VMGlobal),
}
pub type VMInstance = WebAssembly::Instance;
#[derive(Debug)]
pub struct VMFunctionEnvironment {
contents: Box<dyn Any + Send + 'static>,
}
impl VMFunctionEnvironment {
pub fn new(val: impl Any + Send + 'static) -> Self {
Self {
contents: Box::new(val),
}
}
#[allow(clippy::should_implement_trait)]
pub fn as_ref(&self) -> &(dyn Any + Send + 'static) {
&*self.contents
}
#[allow(clippy::should_implement_trait)]
pub fn as_mut(&mut self) -> &mut (dyn Any + Send + 'static) {
&mut *self.contents
}
}
pub(crate) struct VMExternRef;
impl VMExternRef {
pub fn into_raw(self) -> RawValue {
unimplemented!();
}
pub unsafe fn from_raw(_raw: RawValue) -> Option<Self> {
unimplemented!();
}
}
#[repr(C)]
pub struct VMFunctionBody(u8);
#[repr(transparent)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub(crate) struct VMFuncRef;
impl VMFuncRef {
pub fn into_raw(self) -> RawValue {
unimplemented!();
}
pub unsafe fn from_raw(_raw: RawValue) -> Option<Self> {
unimplemented!();
}
}
pub struct VMTrampoline;
pub(crate) type VMExternTable = VMTable;
pub(crate) type VMExternMemory = VMMemory;
pub(crate) type VMExternGlobal = VMGlobal;
pub(crate) type VMExternFunction = VMFunction;
pub type VMFunctionCallback = *const VMFunctionBody;