Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added LinearMemory trait #3119

Merged
merged 15 commits into from
Aug 23, 2022
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 {
ptitSeb marked this conversation as resolved.
Show resolved Hide resolved
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 {
ptitSeb marked this conversation as resolved.
Show resolved Hide resolved
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))
ptitSeb marked this conversation as resolved.
Show resolved Hide resolved
}
unsafe fn create_vm_memory(
&self,
_ty: &MemoryType,
_style: &MemoryStyle,
_vm_definition_location: NonNull<VMMemoryDefinition>,
) -> Result<VMMemory, MemoryError> {
let memory = VMTinyMemory::new().unwrap();
Ok(VMMemory::from_custom(memory))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we do Ok(memory.into()) (as I believe the From is implemented)

}

/// 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