Skip to content

Commit

Permalink
Merge #3119
Browse files Browse the repository at this point in the history
3119: Added LinearMemory trait r=ptitSeb a=ptitSeb

Added LinearMemory trait, to describe memory used in runtime.

Co-authored-by: John Sharratt's Shared Account <[email protected]>
Co-authored-by: ptitSeb <[email protected]>
  • Loading branch information
3 people authored Aug 23, 2022
2 parents 50756ba + 2953093 commit ec95964
Show file tree
Hide file tree
Showing 23 changed files with 704 additions and 316 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions lib/api/src/js/export.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use std::fmt;
use wasm_bindgen::{JsCast, JsValue};
use wasmer_types::{ExternType, FunctionType, GlobalType, MemoryType, TableType};

/// Represents linear memory that is managed by the javascript runtime
#[derive(Clone, Debug, PartialEq)]
pub struct VMMemory {
pub(crate) memory: Memory,
Expand All @@ -20,6 +21,11 @@ impl VMMemory {
pub(crate) fn new(memory: Memory, ty: MemoryType) -> Self {
Self { memory, ty }
}

/// Attempts to clone this memory (if its clonable)
pub(crate) fn try_clone(&self) -> Option<VMMemory> {
Some(self.clone())
}
}

#[derive(Clone, Debug, PartialEq)]
Expand Down
50 changes: 26 additions & 24 deletions lib/api/src/js/externals/memory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ use crate::js::{MemoryAccessError, MemoryType};
use std::marker::PhantomData;
use std::mem::MaybeUninit;
use std::slice;
use thiserror::Error;
#[cfg(feature = "tracing")]
use tracing::warn;

Expand All @@ -16,22 +15,7 @@ use wasmer_types::Pages;

use super::MemoryView;

/// Error type describing things that can go wrong when operating on Wasm Memories.
#[derive(Error, Debug, Clone, PartialEq, Hash)]
pub enum MemoryError {
/// The operation would cause the size of the memory to exceed the maximum or would cause
/// an overflow leading to unindexable memory.
#[error("The memory could not grow: current size {} pages, requested increase: {} pages", current.0, attempted_delta.0)]
CouldNotGrow {
/// The current size in pages.
current: Pages,
/// The attempted amount to grow by in pages.
attempted_delta: Pages,
},
/// A user defined error value, used for error cases not listed above.
#[error("A user-defined error occurred: {0}")]
Generic(String),
}
pub use wasmer_types::MemoryError;

#[wasm_bindgen]
extern "C" {
Expand Down Expand Up @@ -113,7 +97,25 @@ impl Memory {
.map_err(|_e| MemoryError::Generic("Error while creating the memory".to_owned()))?;

let vm_memory = VMMemory::new(js_memory, ty);
Ok(Self::from_vm_export(store, vm_memory))
let handle = StoreHandle::new(store.objects_mut(), vm_memory);
Ok(Self::from_vm_extern(store, handle.internal_handle()))
}

/// Creates a new host `Memory` from provided JavaScript memory.
pub fn new_raw(
store: &mut impl AsStoreMut,
js_memory: js_sys::WebAssembly::Memory,
ty: MemoryType,
) -> Result<Self, MemoryError> {
let vm_memory = VMMemory::new(js_memory, ty);
let handle = StoreHandle::new(store.objects_mut(), vm_memory);
Ok(Self::from_vm_extern(store, handle.internal_handle()))
}

/// Create a memory object from an existing memory and attaches it to the store
pub fn new_from_existing(new_store: &mut impl AsStoreMut, memory: VMMemory) -> Self {
let handle = StoreHandle::new(new_store.objects_mut(), memory);
Self::from_vm_extern(new_store, handle.internal_handle())
}

/// Returns the [`MemoryType`] of the `Memory`.
Expand Down Expand Up @@ -193,12 +195,6 @@ impl Memory {
Ok(Pages(new_pages))
}

pub(crate) fn from_vm_export(store: &mut impl AsStoreMut, vm_memory: VMMemory) -> Self {
Self {
handle: StoreHandle::new(store.objects_mut(), vm_memory),
}
}

pub(crate) fn from_vm_extern(
store: &mut impl AsStoreMut,
internal: InternalStoreHandle<VMMemory>,
Expand All @@ -210,6 +206,12 @@ impl Memory {
}
}

/// Attempts to clone this memory (if its clonable)
pub fn try_clone(&self, store: &impl AsStoreRef) -> Option<VMMemory> {
let mem = self.handle.get(store.as_store_ref().objects());
mem.try_clone()
}

/// Checks whether this `Global` can be used with the given context.
pub fn is_from_store(&self, store: &impl AsStoreRef) -> bool {
self.handle.store_id() == store.as_store_ref().objects().id()
Expand Down
2 changes: 1 addition & 1 deletion lib/api/src/js/externals/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ impl Extern {
}

/// Create an `Extern` from an `wasmer_compiler::Export`.
pub fn from_vm_export(store: &mut impl AsStoreMut, export: Export) -> Self {
pub fn from_vm_extern(store: &mut impl AsStoreMut, export: Export) -> Self {
match export {
Export::Function(f) => Self::Function(Function::from_vm_extern(store, f)),
Export::Memory(m) => Self::Memory(Memory::from_vm_extern(store, m)),
Expand Down
7 changes: 6 additions & 1 deletion lib/api/src/js/function_env.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ impl<T> FunctionEnv<T> {
}

/// Get the data as reference
pub fn as_ref<'a>(&self, store: &'a impl AsStoreMut) -> &'a T
pub fn as_ref<'a>(&self, store: &'a impl AsStoreRef) -> &'a T
where
T: Any + Send + 'static + Sized,
{
Expand Down Expand Up @@ -112,6 +112,11 @@ impl<T: Send + 'static> FunctionEnvMut<'_, T> {
self.func_env.as_mut(&mut self.store_mut)
}

/// Borrows a new immmutable reference
pub fn as_ref(&self) -> FunctionEnv<T> {
self.func_env.clone()
}

/// Borrows a new mutable reference
pub fn as_mut<'a>(&'a mut self) -> FunctionEnvMut<'a, T> {
FunctionEnvMut {
Expand Down
26 changes: 26 additions & 0 deletions lib/api/src/js/imports.rs
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,32 @@ impl Imports {
}
imports
}

/// Iterates through all the imports in this structure
pub fn iter<'a>(&'a self) -> ImportsIterator<'a> {
ImportsIterator::new(self)
}
}

pub struct ImportsIterator<'a> {
iter: std::collections::hash_map::Iter<'a, (String, String), Extern>,
}

impl<'a> ImportsIterator<'a> {
fn new(imports: &'a Imports) -> Self {
let iter = imports.map.iter();
Self { iter }
}
}

impl<'a> Iterator for ImportsIterator<'a> {
type Item = (&'a str, &'a str, &'a Extern);

fn next(&mut self) -> Option<Self::Item> {
self.iter
.next()
.map(|(k, v)| (k.0.as_str(), k.1.as_str(), v))
}
}

impl IntoIterator for &Imports {
Expand Down
2 changes: 1 addition & 1 deletion lib/api/src/js/instance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ impl Instance {
})?;
let export: Export =
Export::from_js_value(js_export, &mut store, extern_type)?.into();
let extern_ = Extern::from_vm_export(&mut store, export);
let extern_ = Extern::from_vm_extern(&mut store, export);
Ok((name.to_string(), extern_))
})
.collect::<Result<Exports, InstantiationError>>()?;
Expand Down
6 changes: 6 additions & 0 deletions lib/api/src/js/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,12 @@ pub use crate::js::types::{
pub use crate::js::value::Value;
pub use crate::js::value::Value as Val;

pub mod vm {
//! The `vm` module re-exports wasmer-vm types.
pub use crate::js::export::VMMemory;
}

pub use wasmer_types::is_wasm;
pub use wasmer_types::{
Bytes, ExportIndex, GlobalInit, LocalFunctionIndex, Pages, ValueType, WASM_MAX_PAGES,
Expand Down
1 change: 0 additions & 1 deletion lib/api/src/js/native.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ use std::marker::PhantomData;

use crate::js::externals::Function;
use crate::js::store::{AsStoreMut, AsStoreRef, StoreHandle};
use crate::js::FunctionEnv;
use crate::js::{FromToNativeWasmType, RuntimeError, WasmTypeList};
// use std::panic::{catch_unwind, AssertUnwindSafe};
use crate::js::export::VMFunction;
Expand Down
5 changes: 5 additions & 0 deletions lib/api/src/js/store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,11 @@ mod objects {
self.id
}

/// Sets the ID of this store
pub fn set_id(&mut self, id: StoreId) {
self.id = id;
}

/// Returns a pair of mutable references from two handles.
///
/// Panics if both handles point to the same object.
Expand Down
14 changes: 13 additions & 1 deletion lib/api/src/sys/externals/memory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use std::slice;
#[cfg(feature = "tracing")]
use tracing::warn;
use wasmer_types::Pages;
use wasmer_vm::{InternalStoreHandle, MemoryError, StoreHandle, VMExtern, VMMemory};
use wasmer_vm::{InternalStoreHandle, LinearMemory, MemoryError, StoreHandle, VMExtern, VMMemory};

use super::MemoryView;

Expand Down Expand Up @@ -60,6 +60,12 @@ impl Memory {
})
}

/// Create a memory object from an existing memory and attaches it to the store
pub fn new_from_existing(new_store: &mut impl AsStoreMut, memory: VMMemory) -> Self {
let handle = StoreHandle::new(new_store.objects_mut(), memory);
Self::from_vm_extern(new_store, handle.internal_handle())
}

/// Returns the [`MemoryType`] of the `Memory`.
///
/// # Example
Expand Down Expand Up @@ -142,6 +148,12 @@ impl Memory {
self.handle.store_id() == store.as_store_ref().objects().id()
}

/// Attempts to clone this memory (if its clonable)
pub fn try_clone(&self, store: &impl AsStoreRef) -> Option<VMMemory> {
let mem = self.handle.get(store.as_store_ref().objects());
mem.try_clone().map(|mem| mem.into())
}

pub(crate) fn to_vm_extern(&self) -> VMExtern {
VMExtern::Memory(self.handle.internal_handle())
}
Expand Down
1 change: 1 addition & 0 deletions lib/api/src/sys/externals/memory_view.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use std::marker::PhantomData;
use std::mem::MaybeUninit;
use std::slice;
use wasmer_types::Pages;
use wasmer_vm::LinearMemory;

use super::memory::MemoryBuffer;
use super::Memory;
Expand Down
134 changes: 134 additions & 0 deletions lib/api/src/sys/tunables.rs
Original file line number Diff line number Diff line change
Expand Up @@ -174,4 +174,138 @@ mod tests {
s => panic!("Unexpected memory style: {:?}", s),
}
}

use std::cell::UnsafeCell;
use std::ptr::NonNull;
use wasmer_types::{MemoryError, MemoryStyle, MemoryType, Pages, WASM_PAGE_SIZE};
use wasmer_vm::{LinearMemory, MaybeInstanceOwned};

#[derive(Debug)]
struct VMTinyMemory {
mem: [u8; WASM_PAGE_SIZE],
}

unsafe impl Send for VMTinyMemory {}
unsafe impl Sync for VMTinyMemory {}

impl VMTinyMemory {
pub fn new() -> Result<Self, MemoryError> {
Ok(VMTinyMemory {
mem: [0; WASM_PAGE_SIZE],
})
}
}

impl LinearMemory for VMTinyMemory {
fn ty(&self) -> MemoryType {
MemoryType {
minimum: Pages::from(1u32),
maximum: Some(Pages::from(1u32)),
shared: false,
}
}
fn size(&self) -> Pages {
Pages::from(1u32)
}
fn style(&self) -> MemoryStyle {
MemoryStyle::Static {
bound: Pages::from(1u32),
offset_guard_size: 0,
}
}
fn grow(&mut self, delta: Pages) -> Result<Pages, MemoryError> {
Err(MemoryError::CouldNotGrow {
current: Pages::from(100u32),
attempted_delta: delta,
})
}
fn vmmemory(&self) -> NonNull<VMMemoryDefinition> {
MaybeInstanceOwned::Host(Box::new(UnsafeCell::new(VMMemoryDefinition {
base: self.mem.as_ptr() as _,
current_length: WASM_PAGE_SIZE,
})))
.as_ptr()
}
fn try_clone(&self) -> Option<Box<dyn LinearMemory + 'static>> {
None
}
}

impl From<VMTinyMemory> for wasmer_vm::VMMemory {
fn from(mem: VMTinyMemory) -> Self {
Self(Box::new(mem))
}
}

struct TinyTunables;
impl Tunables for TinyTunables {
fn memory_style(&self, _memory: &MemoryType) -> MemoryStyle {
MemoryStyle::Static {
bound: Pages::from(1u32),
offset_guard_size: 0,
}
}

/// Construct a `TableStyle` for the provided `TableType`
fn table_style(&self, _table: &TableType) -> TableStyle {
TableStyle::CallerChecksSignature
}
fn create_host_memory(
&self,
_ty: &MemoryType,
_style: &MemoryStyle,
) -> Result<VMMemory, MemoryError> {
let memory = VMTinyMemory::new().unwrap();
Ok(VMMemory::from_custom(memory))
}
unsafe fn create_vm_memory(
&self,
_ty: &MemoryType,
_style: &MemoryStyle,
_vm_definition_location: NonNull<VMMemoryDefinition>,
) -> Result<VMMemory, MemoryError> {
let memory = VMTinyMemory::new().unwrap();
Ok(memory.into())
}

/// Create a table owned by the host given a [`TableType`] and a [`TableStyle`].
fn create_host_table(&self, ty: &TableType, style: &TableStyle) -> Result<VMTable, String> {
VMTable::new(ty, style)
}

/// Create a table owned by the VM given a [`TableType`] and a [`TableStyle`].
///
/// # Safety
/// - `vm_definition_location` must point to a valid location in VM memory.
unsafe fn create_vm_table(
&self,
ty: &TableType,
style: &TableStyle,
vm_definition_location: NonNull<VMTableDefinition>,
) -> Result<VMTable, String> {
VMTable::from_definition(ty, style, vm_definition_location)
}
}

#[test]
fn check_linearmemory() {
let tunables = TinyTunables {};
let vmmemory = tunables.create_host_memory(
&MemoryType::new(1u32, Some(100u32), true),
&MemoryStyle::Static {
bound: Pages::from(1u32),
offset_guard_size: 0u64,
},
);
let mut vmmemory = vmmemory.unwrap();
assert!(vmmemory.grow(Pages::from(2u32)).is_err());
assert_eq!(vmmemory.size(), Pages::from(1u32));
assert_eq!(
vmmemory.grow(Pages::from(0u32)).err().unwrap(),
MemoryError::CouldNotGrow {
current: Pages::from(100u32),
attempted_delta: Pages::from(0u32)
}
);
}
}
Loading

0 comments on commit ec95964

Please sign in to comment.