mirror of
https://github.com/mii443/wasmer.git
synced 2025-12-12 13:28:49 +00:00
Implement the new Context API for the core API
Co-authored-by: ptitSeb <sebastien.chev@gmail.com>
This commit is contained in:
committed by
Manos Pitsidianakis
parent
f16bd35d3c
commit
738a66f719
27
lib/api/src/js/externals/function.rs
vendored
27
lib/api/src/js/externals/function.rs
vendored
@@ -631,19 +631,6 @@ impl fmt::Debug for Function {
|
||||
}
|
||||
}
|
||||
|
||||
// This is needed for reference types
|
||||
impl wasmer_types::WasmValueType for Function {
|
||||
/// Write the value.
|
||||
unsafe fn write_value_to(&self, _p: *mut i128) {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
/// Read the value.
|
||||
unsafe fn read_value_from(_store: &dyn std::any::Any, _p: *const i128) -> Self {
|
||||
unimplemented!();
|
||||
}
|
||||
}
|
||||
|
||||
/// This private inner module contains the low-level implementation
|
||||
/// for `Function` and its siblings.
|
||||
mod inner {
|
||||
@@ -655,8 +642,6 @@ mod inner {
|
||||
use std::marker::PhantomData;
|
||||
use std::panic::{self, AssertUnwindSafe};
|
||||
|
||||
#[cfg(feature = "experimental-reference-types-extern-ref")]
|
||||
pub use wasmer_types::{ExternRef, VMExternRef};
|
||||
use wasmer_types::{FunctionType, NativeWasmType, Type};
|
||||
// use wasmer::{raise_user_trap, resume_panic};
|
||||
|
||||
@@ -749,18 +734,6 @@ mod inner {
|
||||
f64 => f64
|
||||
);
|
||||
|
||||
#[cfg(feature = "experimental-reference-types-extern-ref")]
|
||||
unsafe impl FromToNativeWasmType for ExternRef {
|
||||
type Native = VMExternRef;
|
||||
|
||||
fn to_native(self) -> Self::Native {
|
||||
self.into()
|
||||
}
|
||||
fn from_native(n: Self::Native) -> Self {
|
||||
n.into()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test_from_to_native_wasm_type {
|
||||
use super::*;
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
//! The import module contains the implementation data structures and helper functions used to
|
||||
//! manipulate and access a wasm module's imports including memories, tables, globals, and
|
||||
//! functions.
|
||||
use crate::js::export::Export;
|
||||
use crate::js::exports::{Exportable, Exports};
|
||||
use crate::js::instance::InstantiationError;
|
||||
use crate::js::module::Module;
|
||||
@@ -302,7 +301,7 @@ macro_rules! import_namespace {
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use crate::js::export::Export;
|
||||
use crate::js::exports::Exportable;
|
||||
use crate::js::Type;
|
||||
use crate::js::{Global, Store, Val};
|
||||
|
||||
@@ -4,9 +4,11 @@ use crate::js::imports::Imports;
|
||||
use crate::js::store::Store;
|
||||
use crate::js::types::{ExportType, ImportType};
|
||||
// use crate::js::InstantiationError;
|
||||
use crate::js::error::CompileError;
|
||||
#[cfg(feature = "wat")]
|
||||
use crate::js::error::WasmError;
|
||||
use crate::js::error::{CompileError, DeserializeError, SerializeError};
|
||||
#[cfg(feature = "js-serializable-module")]
|
||||
use crate::js::error::{DeserializeError, SerializeError};
|
||||
use crate::js::RuntimeError;
|
||||
use js_sys::{Reflect, Uint8Array, WebAssembly};
|
||||
use std::fmt;
|
||||
|
||||
@@ -16,7 +16,6 @@ use crate::js::types::param_from_js;
|
||||
use js_sys::Array;
|
||||
use std::iter::FromIterator;
|
||||
use wasm_bindgen::JsValue;
|
||||
use wasmer_types::NativeWasmType;
|
||||
|
||||
/// A WebAssembly function that can be called natively
|
||||
/// (using the Native ABI).
|
||||
|
||||
@@ -2,13 +2,10 @@ use crate::js::{externals::Memory, FromToNativeWasmType};
|
||||
use crate::{MemoryAccessError, WasmRef, WasmSlice};
|
||||
use std::convert::TryFrom;
|
||||
use std::{fmt, marker::PhantomData, mem};
|
||||
use wasmer_types::{NativeWasmType, ValueType};
|
||||
|
||||
pub use wasmer_types::MemorySize;
|
||||
|
||||
pub use wasmer_types::Memory32;
|
||||
|
||||
pub use wasmer_types::Memory64;
|
||||
pub use wasmer_types::MemorySize;
|
||||
use wasmer_types::ValueType;
|
||||
|
||||
/// Alias for `WasmPtr<T, Memory64>.
|
||||
pub type WasmPtr64<T> = WasmPtr<T, Memory64>;
|
||||
|
||||
195
lib/api/src/sys/context.rs
Normal file
195
lib/api/src/sys/context.rs
Normal file
@@ -0,0 +1,195 @@
|
||||
use wasmer_vm::ContextObjects;
|
||||
|
||||
use crate::Store;
|
||||
|
||||
/// We require the context to have a fixed memory address for its lifetime since
|
||||
/// various bits of the VM have raw pointers that point back to it. Hence we
|
||||
/// wrap the actual context in a box.
|
||||
pub(crate) struct ContextInner<T> {
|
||||
pub(crate) objects: ContextObjects,
|
||||
pub(crate) store: Store,
|
||||
pub(crate) data: T,
|
||||
}
|
||||
|
||||
/// A context containing a set of WebAssembly instances, along with host state.
|
||||
///
|
||||
/// All WebAssembly instances must exist within a context. In the majority of
|
||||
/// cases each instance will have its own context, but it is possible to have
|
||||
/// multiple instances in a context when these instances need to interact with
|
||||
/// each other, for example sharing a memory between instances or calling
|
||||
/// functions in another instance.
|
||||
///
|
||||
/// The lifetimes of run-time WebAssembly objects, notably [`Instance`],
|
||||
/// [`Memory`], [`Global`], [`Table`] and [`Function`] is tied to a context:
|
||||
/// the backing memory for these objects is only freed when the context is
|
||||
/// freed.
|
||||
///
|
||||
/// The `T` generic parameter allows arbitrary data to be attached to a context.
|
||||
/// This data can be accessed using the [`Context::data`] and
|
||||
/// [`Context::data_mut`] methods. Host functions defined using
|
||||
/// [`Function::new_with_env`] and [`Function::new_native_with_env`] receive
|
||||
/// a reference to the context when they are called.
|
||||
pub struct Context<T> {
|
||||
pub(crate) inner: Box<ContextInner<T>>,
|
||||
}
|
||||
|
||||
impl<T> Context<T> {
|
||||
/// Creates a new context with the given host state.
|
||||
// TODO: Eliminate the Store type and move its functionality into Engine.
|
||||
pub fn new(store: &Store, data: T) -> Self {
|
||||
Self {
|
||||
inner: Box::new(ContextInner {
|
||||
objects: Default::default(),
|
||||
store: store.clone(),
|
||||
data,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a reference to the host state in this context.
|
||||
pub fn data(&self) -> &T {
|
||||
&self.inner.data
|
||||
}
|
||||
|
||||
/// Returns a mutable- reference to the host state in this context.
|
||||
pub fn data_mut(&mut self) -> &mut T {
|
||||
&mut self.inner.data
|
||||
}
|
||||
|
||||
/// Drops the context and returns the host state that was stored in it.
|
||||
pub fn into_data(self) -> T {
|
||||
self.inner.data
|
||||
}
|
||||
|
||||
/// Returns a reference to the `Store` of this context.
|
||||
pub fn store(&self) -> &Store {
|
||||
&self.inner.store
|
||||
}
|
||||
}
|
||||
|
||||
/// A temporary handle to a [`Context`].
|
||||
pub struct ContextRef<'a, T: 'a> {
|
||||
inner: &'a ContextInner<T>,
|
||||
}
|
||||
|
||||
impl<'a, T> ContextRef<'a, T> {
|
||||
/// Returns a reference to the host state in this context.
|
||||
pub fn data(&self) -> &'a T {
|
||||
&self.inner.data
|
||||
}
|
||||
|
||||
/// Returns a reference to the `Store` of this context.
|
||||
pub fn store(&self) -> &Store {
|
||||
&self.inner.store
|
||||
}
|
||||
|
||||
pub(crate) fn objects(&self) -> &'a ContextObjects {
|
||||
&self.inner.objects
|
||||
}
|
||||
}
|
||||
|
||||
/// A temporary handle to a [`Context`].
|
||||
pub struct ContextMut<'a, T: 'a> {
|
||||
inner: &'a mut ContextInner<T>,
|
||||
}
|
||||
|
||||
impl<T> ContextMut<'_, T> {
|
||||
/// Returns a reference to the host state in this context.
|
||||
pub fn data(&self) -> &T {
|
||||
&self.inner.data
|
||||
}
|
||||
|
||||
/// Returns a mutable- reference to the host state in this context.
|
||||
pub fn data_mut(&mut self) -> &mut T {
|
||||
&mut self.inner.data
|
||||
}
|
||||
|
||||
pub(crate) fn objects(&self) -> &ContextObjects {
|
||||
&self.inner.objects
|
||||
}
|
||||
|
||||
pub(crate) fn objects_mut(&mut self) -> &mut ContextObjects {
|
||||
&mut self.inner.objects
|
||||
}
|
||||
|
||||
/// Returns a reference to the `Store` of this context.
|
||||
pub fn store(&self) -> &Store {
|
||||
&self.inner.store
|
||||
}
|
||||
|
||||
pub(crate) fn as_raw(&self) -> *mut ContextInner<T> {
|
||||
self.inner as *const ContextInner<T> as *mut ContextInner<T>
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn from_raw(raw: *mut ContextInner<T>) -> Self {
|
||||
Self { inner: &mut *raw }
|
||||
}
|
||||
}
|
||||
|
||||
/// Helper trait for a value that is convertible to a [`ContextRef`].
|
||||
pub trait AsContextRef {
|
||||
/// Host state associated with the [`Context`].
|
||||
type Data;
|
||||
|
||||
/// Returns a `ContextRef` pointing to the underlying context.
|
||||
fn as_context_ref(&self) -> ContextRef<'_, Self::Data>;
|
||||
}
|
||||
|
||||
/// Helper trait for a value that is convertible to a [`ContextMut`].
|
||||
pub trait AsContextMut: AsContextRef {
|
||||
/// Returns a `ContextMut` pointing to the underlying context.
|
||||
fn as_context_mut(&mut self) -> ContextMut<'_, Self::Data>;
|
||||
}
|
||||
|
||||
impl<T> AsContextRef for Context<T> {
|
||||
type Data = T;
|
||||
|
||||
fn as_context_ref(&self) -> ContextRef<'_, Self::Data> {
|
||||
ContextRef { inner: &self.inner }
|
||||
}
|
||||
}
|
||||
impl<T> AsContextMut for Context<T> {
|
||||
fn as_context_mut(&mut self) -> ContextMut<'_, Self::Data> {
|
||||
ContextMut {
|
||||
inner: &mut self.inner,
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<T> AsContextRef for ContextRef<'_, T> {
|
||||
type Data = T;
|
||||
|
||||
fn as_context_ref(&self) -> ContextRef<'_, Self::Data> {
|
||||
ContextRef { inner: self.inner }
|
||||
}
|
||||
}
|
||||
impl<T> AsContextRef for ContextMut<'_, T> {
|
||||
type Data = T;
|
||||
|
||||
fn as_context_ref(&self) -> ContextRef<'_, Self::Data> {
|
||||
ContextRef { inner: self.inner }
|
||||
}
|
||||
}
|
||||
impl<T> AsContextMut for ContextMut<'_, T> {
|
||||
fn as_context_mut(&mut self) -> ContextMut<'_, Self::Data> {
|
||||
ContextMut { inner: self.inner }
|
||||
}
|
||||
}
|
||||
impl<T: AsContextRef> AsContextRef for &'_ T {
|
||||
type Data = T::Data;
|
||||
|
||||
fn as_context_ref(&self) -> ContextRef<'_, Self::Data> {
|
||||
T::as_context_ref(*self)
|
||||
}
|
||||
}
|
||||
impl<T: AsContextRef> AsContextRef for &'_ mut T {
|
||||
type Data = T::Data;
|
||||
|
||||
fn as_context_ref(&self) -> ContextRef<'_, Self::Data> {
|
||||
T::as_context_ref(*self)
|
||||
}
|
||||
}
|
||||
impl<T: AsContextMut> AsContextMut for &'_ mut T {
|
||||
fn as_context_mut(&mut self) -> ContextMut<'_, Self::Data> {
|
||||
T::as_context_mut(*self)
|
||||
}
|
||||
}
|
||||
@@ -1,224 +0,0 @@
|
||||
use crate::sys::{ExportError, Instance};
|
||||
use thiserror::Error;
|
||||
|
||||
/// An error while initializing the user supplied host env with the `WasmerEnv` trait.
|
||||
#[derive(Error, Debug)]
|
||||
#[error("Host env initialization error: {0}")]
|
||||
pub enum HostEnvInitError {
|
||||
/// An error occurred when accessing an export
|
||||
Export(ExportError),
|
||||
}
|
||||
|
||||
impl From<ExportError> for HostEnvInitError {
|
||||
fn from(other: ExportError) -> Self {
|
||||
Self::Export(other)
|
||||
}
|
||||
}
|
||||
|
||||
/// Trait for initializing the environments passed to host functions after
|
||||
/// instantiation but before execution.
|
||||
///
|
||||
/// This is useful for filling an environment with data that can only be accesed
|
||||
/// after instantiation. For example, exported items such as memories and
|
||||
/// functions which don't exist prior to instantiation can be accessed here so
|
||||
/// that host functions can use them.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// This trait can be derived like so:
|
||||
///
|
||||
/// ```
|
||||
/// use wasmer::{WasmerEnv, LazyInit, Memory, TypedFunction};
|
||||
///
|
||||
/// #[derive(WasmerEnv, Clone)]
|
||||
/// pub struct MyEnvWithNoInstanceData {
|
||||
/// non_instance_data: u8,
|
||||
/// }
|
||||
///
|
||||
/// #[derive(WasmerEnv, Clone)]
|
||||
/// pub struct MyEnvWithInstanceData {
|
||||
/// non_instance_data: u8,
|
||||
/// #[wasmer(export)]
|
||||
/// memory: LazyInit<Memory>,
|
||||
/// #[wasmer(export(name = "real_name"))]
|
||||
/// func: LazyInit<TypedFunction<(i32, i32), i32>>,
|
||||
/// #[wasmer(export(optional = true, alias = "memory2", alias = "_memory2"))]
|
||||
/// optional_memory: LazyInit<Memory>,
|
||||
/// }
|
||||
///
|
||||
/// ```
|
||||
///
|
||||
/// When deriving `WasmerEnv`, you must wrap your types to be initialized in
|
||||
/// [`LazyInit`]. The derive macro will also generate helper methods of the form
|
||||
/// `<field_name>_ref` and `<field_name>_ref_unchecked` for easy access to the
|
||||
/// data.
|
||||
///
|
||||
/// The valid arguments to `export` are:
|
||||
/// - `name = "string"`: specify the name of this item in the Wasm module. If this is not specified, it will default to the name of the field.
|
||||
/// - `optional = true`: specify whether this export is optional. Defaults to
|
||||
/// `false`. Being optional means that if the export can't be found, the
|
||||
/// [`LazyInit`] will be left uninitialized.
|
||||
/// - `alias = "string"`: specify additional names to look for in the Wasm module.
|
||||
/// `alias` may be specified multiple times to search for multiple aliases.
|
||||
/// -------
|
||||
///
|
||||
/// This trait may also be implemented manually:
|
||||
/// ```
|
||||
/// # use wasmer::{WasmerEnv, LazyInit, Memory, Instance, HostEnvInitError};
|
||||
/// #[derive(Clone)]
|
||||
/// pub struct MyEnv {
|
||||
/// memory: LazyInit<Memory>,
|
||||
/// }
|
||||
///
|
||||
/// impl WasmerEnv for MyEnv {
|
||||
/// fn init_with_instance(&mut self, instance: &Instance) -> Result<(), HostEnvInitError> {
|
||||
/// let memory: Memory = instance.exports.get_with_generics_weak("memory").unwrap();
|
||||
/// self.memory.initialize(memory.clone());
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// When implementing the trait manually, it's important to get a "weak" export to
|
||||
/// prevent a cyclic reference leaking memory. You can access a "weak" export with
|
||||
/// a method like `get_with_generics_weak`.
|
||||
pub trait WasmerEnv: Clone + Send + Sync {
|
||||
/// The function that Wasmer will call on your type to let it finish
|
||||
/// setting up the environment with data from the `Instance`.
|
||||
///
|
||||
/// This function is called after `Instance` is created but before it is
|
||||
/// returned to the user via `Instance::new`.
|
||||
fn init_with_instance(&mut self, _instance: &Instance) -> Result<(), HostEnvInitError> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl WasmerEnv for u8 {}
|
||||
impl WasmerEnv for i8 {}
|
||||
impl WasmerEnv for u16 {}
|
||||
impl WasmerEnv for i16 {}
|
||||
impl WasmerEnv for u32 {}
|
||||
impl WasmerEnv for i32 {}
|
||||
impl WasmerEnv for u64 {}
|
||||
impl WasmerEnv for i64 {}
|
||||
impl WasmerEnv for u128 {}
|
||||
impl WasmerEnv for i128 {}
|
||||
impl WasmerEnv for f32 {}
|
||||
impl WasmerEnv for f64 {}
|
||||
impl WasmerEnv for usize {}
|
||||
impl WasmerEnv for isize {}
|
||||
impl WasmerEnv for char {}
|
||||
impl WasmerEnv for bool {}
|
||||
impl WasmerEnv for String {}
|
||||
impl<'a> WasmerEnv for &'a ::std::sync::atomic::AtomicBool {}
|
||||
impl<'a> WasmerEnv for &'a ::std::sync::atomic::AtomicI8 {}
|
||||
impl<'a> WasmerEnv for &'a ::std::sync::atomic::AtomicU8 {}
|
||||
impl<'a> WasmerEnv for &'a ::std::sync::atomic::AtomicI16 {}
|
||||
impl<'a> WasmerEnv for &'a ::std::sync::atomic::AtomicU16 {}
|
||||
impl<'a> WasmerEnv for &'a ::std::sync::atomic::AtomicI32 {}
|
||||
impl<'a> WasmerEnv for &'a ::std::sync::atomic::AtomicU32 {}
|
||||
impl<'a> WasmerEnv for &'a ::std::sync::atomic::AtomicI64 {}
|
||||
impl<'a> WasmerEnv for &'a ::std::sync::atomic::AtomicUsize {}
|
||||
impl<'a> WasmerEnv for &'a ::std::sync::atomic::AtomicIsize {}
|
||||
impl<T: WasmerEnv> WasmerEnv for Box<T> {
|
||||
fn init_with_instance(&mut self, instance: &Instance) -> Result<(), HostEnvInitError> {
|
||||
(&mut **self).init_with_instance(instance)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: WasmerEnv> WasmerEnv for ::std::sync::Arc<::std::sync::Mutex<T>> {
|
||||
fn init_with_instance(&mut self, instance: &Instance) -> Result<(), HostEnvInitError> {
|
||||
let mut guard = self.lock().unwrap();
|
||||
guard.init_with_instance(instance)
|
||||
}
|
||||
}
|
||||
|
||||
/// Lazily init an item
|
||||
pub struct LazyInit<T: Sized> {
|
||||
/// The data to be initialized
|
||||
data: std::mem::MaybeUninit<T>,
|
||||
/// Whether or not the data has been initialized
|
||||
initialized: bool,
|
||||
}
|
||||
|
||||
impl<T> LazyInit<T> {
|
||||
/// Creates an unitialized value.
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
data: std::mem::MaybeUninit::uninit(),
|
||||
initialized: false,
|
||||
}
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
/// - The data must be initialized first
|
||||
pub unsafe fn get_unchecked(&self) -> &T {
|
||||
&*self.data.as_ptr()
|
||||
}
|
||||
|
||||
/// Get the inner data.
|
||||
pub fn get_ref(&self) -> Option<&T> {
|
||||
if !self.initialized {
|
||||
None
|
||||
} else {
|
||||
Some(unsafe { self.get_unchecked() })
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets a value and marks the data as initialized.
|
||||
pub fn initialize(&mut self, value: T) -> bool {
|
||||
if self.initialized {
|
||||
return false;
|
||||
}
|
||||
unsafe {
|
||||
self.data.as_mut_ptr().write(value);
|
||||
}
|
||||
self.initialized = true;
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: std::fmt::Debug> std::fmt::Debug for LazyInit<T> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
f.debug_struct("LazyInit")
|
||||
.field("data", &self.get_ref())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone> Clone for LazyInit<T> {
|
||||
fn clone(&self) -> Self {
|
||||
if let Some(inner) = self.get_ref() {
|
||||
Self {
|
||||
data: std::mem::MaybeUninit::new(inner.clone()),
|
||||
initialized: true,
|
||||
}
|
||||
} else {
|
||||
Self {
|
||||
data: std::mem::MaybeUninit::uninit(),
|
||||
initialized: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Drop for LazyInit<T> {
|
||||
fn drop(&mut self) {
|
||||
if self.initialized {
|
||||
unsafe {
|
||||
let ptr = self.data.as_mut_ptr();
|
||||
std::ptr::drop_in_place(ptr);
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Default for LazyInit<T> {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<T: Send> Send for LazyInit<T> {}
|
||||
// I thought we could opt out of sync..., look into this
|
||||
// unsafe impl<T> !Sync for InitWithInstance<T> {}
|
||||
@@ -1,3 +1,4 @@
|
||||
use super::context::AsContextRef;
|
||||
use crate::sys::externals::{Extern, Function, Global, Memory, Table};
|
||||
use crate::sys::native::TypedFunction;
|
||||
use crate::sys::WasmTypeList;
|
||||
@@ -5,7 +6,6 @@ use indexmap::IndexMap;
|
||||
use std::fmt;
|
||||
use std::iter::{ExactSizeIterator, FromIterator};
|
||||
use thiserror::Error;
|
||||
use wasmer_compiler::Export;
|
||||
|
||||
/// The `ExportError` can happen when trying to get a specific
|
||||
/// export [`Extern`] from the [`Instance`] exports.
|
||||
@@ -137,6 +137,7 @@ impl Exports {
|
||||
/// Get an export as a `TypedFunction`.
|
||||
pub fn get_native_function<Args, Rets>(
|
||||
&self,
|
||||
ctx: impl AsContextRef,
|
||||
name: &str,
|
||||
) -> Result<TypedFunction<Args, Rets>, ExportError>
|
||||
where
|
||||
@@ -144,7 +145,7 @@ impl Exports {
|
||||
Rets: WasmTypeList,
|
||||
{
|
||||
self.get_function(name)?
|
||||
.native()
|
||||
.native(ctx)
|
||||
.map_err(|_| ExportError::IncompatibleType)
|
||||
}
|
||||
|
||||
@@ -169,8 +170,7 @@ impl Exports {
|
||||
Rets: WasmTypeList,
|
||||
T: ExportableWithGenerics<'a, Args, Rets>,
|
||||
{
|
||||
let mut out: T = self.get_with_generics(name)?;
|
||||
out.convert_to_weak_instance_ref();
|
||||
let out: T = self.get_with_generics(name)?;
|
||||
Ok(out)
|
||||
}
|
||||
|
||||
@@ -296,22 +296,11 @@ impl<'a> IntoIterator for &'a Exports {
|
||||
///
|
||||
/// [`Instance`]: crate::Instance
|
||||
pub trait Exportable<'a>: Sized {
|
||||
/// This function is used when providedd the [`Extern`] as exportable, so it
|
||||
/// can be used while instantiating the [`Module`].
|
||||
///
|
||||
/// [`Module`]: crate::Module
|
||||
fn to_export(&self) -> Export;
|
||||
|
||||
/// Implementation of how to get the export corresponding to the implementing type
|
||||
/// from an [`Instance`] by name.
|
||||
///
|
||||
/// [`Instance`]: crate::Instance
|
||||
fn get_self_from_extern(_extern: &'a Extern) -> Result<&'a Self, ExportError>;
|
||||
|
||||
/// Convert the extern internally to hold a weak reference to the `InstanceRef`.
|
||||
/// This is useful for preventing cycles, for example for data stored in a
|
||||
/// type implementing `WasmerEnv`.
|
||||
fn convert_to_weak_instance_ref(&mut self);
|
||||
}
|
||||
|
||||
/// A trait for accessing exports (like [`Exportable`]) but it takes generic
|
||||
@@ -320,10 +309,6 @@ pub trait Exportable<'a>: Sized {
|
||||
pub trait ExportableWithGenerics<'a, Args: WasmTypeList, Rets: WasmTypeList>: Sized {
|
||||
/// Get an export with the given generics.
|
||||
fn get_self_from_extern_with_generics(_extern: &'a Extern) -> Result<Self, ExportError>;
|
||||
/// Convert the extern internally to hold a weak reference to the `InstanceRef`.
|
||||
/// This is useful for preventing cycles, for example for data stored in a
|
||||
/// type implementing `WasmerEnv`.
|
||||
fn convert_to_weak_instance_ref(&mut self);
|
||||
}
|
||||
|
||||
/// We implement it for all concrete [`Exportable`] types (that are `Clone`)
|
||||
@@ -332,8 +317,4 @@ impl<'a, T: Exportable<'a> + Clone + 'static> ExportableWithGenerics<'a, (), ()>
|
||||
fn get_self_from_extern_with_generics(_extern: &'a Extern) -> Result<Self, ExportError> {
|
||||
T::get_self_from_extern(_extern).map(|i| i.clone())
|
||||
}
|
||||
|
||||
fn convert_to_weak_instance_ref(&mut self) {
|
||||
<Self as Exportable>::convert_to_weak_instance_ref(self);
|
||||
}
|
||||
}
|
||||
|
||||
61
lib/api/src/sys/extern_ref.rs
Normal file
61
lib/api/src/sys/extern_ref.rs
Normal file
@@ -0,0 +1,61 @@
|
||||
use std::any::Any;
|
||||
|
||||
use wasmer_vm::{ContextHandle, VMExternObj, VMExternRef};
|
||||
|
||||
use crate::ContextRef;
|
||||
|
||||
use super::context::{AsContextMut, AsContextRef};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[repr(transparent)]
|
||||
/// An opaque reference to some data. This reference can be passed through Wasm.
|
||||
pub struct ExternRef {
|
||||
handle: ContextHandle<VMExternObj>,
|
||||
}
|
||||
|
||||
impl ExternRef {
|
||||
/// Make a new extern reference
|
||||
pub fn new<T>(mut ctx: impl AsContextMut, value: T) -> Self
|
||||
where
|
||||
T: Any + Send + Sync + 'static + Sized,
|
||||
{
|
||||
Self {
|
||||
handle: ContextHandle::new(ctx.as_context_mut().objects_mut(), VMExternObj::new(value)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Try to downcast to the given value.
|
||||
pub fn downcast<'a, T, U>(&self, ctx: ContextRef<'a, U>) -> Option<&'a T>
|
||||
where
|
||||
T: Any + Send + Sync + 'static + Sized,
|
||||
{
|
||||
self.handle.get(ctx.objects()).as_ref().downcast_ref::<T>()
|
||||
}
|
||||
|
||||
pub(crate) fn vm_externref(&self) -> VMExternRef {
|
||||
VMExternRef(self.handle.internal_handle())
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn from_vm_externref(
|
||||
mut ctx: impl AsContextMut,
|
||||
vm_externref: VMExternRef,
|
||||
) -> Self {
|
||||
Self {
|
||||
handle: ContextHandle::from_internal(
|
||||
ctx.as_context_mut().objects_mut().id(),
|
||||
vm_externref.0,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks whether this `ExternRef` can be used with the given context.
|
||||
///
|
||||
/// Primitive (`i32`, `i64`, etc) and null funcref/externref values are not
|
||||
/// tied to a context and can be freely shared between contexts.
|
||||
///
|
||||
/// Externref and funcref values are tied to a context and can only be used
|
||||
/// with that context.
|
||||
pub fn is_from_context(&self, ctx: impl AsContextRef) -> bool {
|
||||
self.handle.context_id() == ctx.as_context_ref().objects().id()
|
||||
}
|
||||
}
|
||||
944
lib/api/src/sys/externals/function.rs
vendored
944
lib/api/src/sys/externals/function.rs
vendored
File diff suppressed because it is too large
Load Diff
174
lib/api/src/sys/externals/global.rs
vendored
174
lib/api/src/sys/externals/global.rs
vendored
@@ -1,14 +1,11 @@
|
||||
use crate::sys::context::{AsContextMut, AsContextRef};
|
||||
use crate::sys::exports::{ExportError, Exportable};
|
||||
use crate::sys::externals::Extern;
|
||||
use crate::sys::store::{Store, StoreObject};
|
||||
use crate::sys::types::Val;
|
||||
use crate::sys::value::Value;
|
||||
use crate::sys::GlobalType;
|
||||
use crate::sys::Mutability;
|
||||
use crate::sys::RuntimeError;
|
||||
use std::fmt;
|
||||
use std::sync::Arc;
|
||||
use wasmer_compiler::Export;
|
||||
use wasmer_vm::{Global as RuntimeGlobal, VMGlobal};
|
||||
use wasmer_vm::{ContextHandle, InternalContextHandle, VMExtern, VMGlobal};
|
||||
|
||||
/// A WebAssembly `global` instance.
|
||||
///
|
||||
@@ -16,9 +13,9 @@ use wasmer_vm::{Global as RuntimeGlobal, VMGlobal};
|
||||
/// It consists of an individual value and a flag indicating whether it is mutable.
|
||||
///
|
||||
/// Spec: <https://webassembly.github.io/spec/core/exec/runtime.html#global-instances>
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Global {
|
||||
store: Store,
|
||||
vm_global: VMGlobal,
|
||||
handle: ContextHandle<VMGlobal>,
|
||||
}
|
||||
|
||||
impl Global {
|
||||
@@ -35,8 +32,8 @@ impl Global {
|
||||
/// assert_eq!(g.get(), Value::I32(1));
|
||||
/// assert_eq!(g.ty().mutability, Mutability::Const);
|
||||
/// ```
|
||||
pub fn new(store: &Store, val: Val) -> Self {
|
||||
Self::from_value(store, val, Mutability::Const).unwrap()
|
||||
pub fn new(ctx: impl AsContextMut, val: Value) -> Self {
|
||||
Self::from_value(ctx, val, Mutability::Const).unwrap()
|
||||
}
|
||||
|
||||
/// Create a mutable `Global` with the initial value [`Val`].
|
||||
@@ -52,31 +49,31 @@ impl Global {
|
||||
/// assert_eq!(g.get(), Value::I32(1));
|
||||
/// assert_eq!(g.ty().mutability, Mutability::Var);
|
||||
/// ```
|
||||
pub fn new_mut(store: &Store, val: Val) -> Self {
|
||||
Self::from_value(store, val, Mutability::Var).unwrap()
|
||||
pub fn new_mut(ctx: impl AsContextMut, val: Value) -> Self {
|
||||
Self::from_value(ctx, val, Mutability::Var).unwrap()
|
||||
}
|
||||
|
||||
/// Create a `Global` with the initial value [`Val`] and the provided [`Mutability`].
|
||||
fn from_value(store: &Store, val: Val, mutability: Mutability) -> Result<Self, RuntimeError> {
|
||||
if !val.comes_from_same_store(store) {
|
||||
return Err(RuntimeError::new("cross-`Store` globals are not supported"));
|
||||
fn from_value(
|
||||
mut ctx: impl AsContextMut,
|
||||
val: Value,
|
||||
mutability: Mutability,
|
||||
) -> Result<Self, RuntimeError> {
|
||||
if !val.is_from_context(ctx.as_context_mut()) {
|
||||
return Err(RuntimeError::new(
|
||||
"cross-`Context` values are not supported",
|
||||
));
|
||||
}
|
||||
let global = RuntimeGlobal::new(GlobalType {
|
||||
let global = VMGlobal::new(GlobalType {
|
||||
mutability,
|
||||
ty: val.ty(),
|
||||
});
|
||||
unsafe {
|
||||
global
|
||||
.set_unchecked(val.clone())
|
||||
.map_err(|e| RuntimeError::new(format!("create global for {:?}: {}", val, e)))?;
|
||||
};
|
||||
global.vmglobal().as_mut().val = val.as_raw(ctx.as_context_mut());
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
store: store.clone(),
|
||||
vm_global: VMGlobal {
|
||||
from: Arc::new(global),
|
||||
instance_ref: None,
|
||||
},
|
||||
handle: ContextHandle::new(ctx.as_context_mut().objects_mut(), global),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -94,24 +91,8 @@ impl Global {
|
||||
/// assert_eq!(c.ty(), &GlobalType::new(Type::I32, Mutability::Const));
|
||||
/// assert_eq!(v.ty(), &GlobalType::new(Type::I64, Mutability::Var));
|
||||
/// ```
|
||||
pub fn ty(&self) -> &GlobalType {
|
||||
self.vm_global.from.ty()
|
||||
}
|
||||
|
||||
/// Returns the [`Store`] where the `Global` belongs.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # use wasmer::{Global, Store, Value};
|
||||
/// # let store = Store::default();
|
||||
/// #
|
||||
/// let g = Global::new(&store, Value::I32(1));
|
||||
///
|
||||
/// assert_eq!(g.store(), &store);
|
||||
/// ```
|
||||
pub fn store(&self) -> &Store {
|
||||
&self.store
|
||||
pub fn ty(&self, ctx: impl AsContextRef) -> GlobalType {
|
||||
*self.handle.get(ctx.as_context_ref().objects()).ty()
|
||||
}
|
||||
|
||||
/// Retrieves the current value [`Val`] that the Global has.
|
||||
@@ -126,8 +107,13 @@ impl Global {
|
||||
///
|
||||
/// assert_eq!(g.get(), Value::I32(1));
|
||||
/// ```
|
||||
pub fn get(&self) -> Val {
|
||||
self.vm_global.from.get(&self.store)
|
||||
pub fn get(&self, mut ctx: impl AsContextMut) -> Value {
|
||||
unsafe {
|
||||
let ctx = ctx.as_context_mut();
|
||||
let raw = self.handle.get(ctx.objects()).vmglobal().as_ref().val;
|
||||
let ty = self.handle.get(ctx.objects()).ty().ty;
|
||||
Value::from_raw(ctx, ty, raw)
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets a custom value [`Val`] to the runtime Global.
|
||||
@@ -171,92 +157,58 @@ impl Global {
|
||||
/// // This results in an error: `RuntimeError`.
|
||||
/// g.set(Value::I64(2)).unwrap();
|
||||
/// ```
|
||||
pub fn set(&self, val: Val) -> Result<(), RuntimeError> {
|
||||
if !val.comes_from_same_store(&self.store) {
|
||||
return Err(RuntimeError::new("cross-`Store` values are not supported"));
|
||||
pub fn set(&self, mut ctx: impl AsContextMut, val: Value) -> Result<(), RuntimeError> {
|
||||
if !val.is_from_context(ctx.as_context_mut()) {
|
||||
return Err(RuntimeError::new(
|
||||
"cross-`Context` values are not supported",
|
||||
));
|
||||
}
|
||||
if self.ty(ctx.as_context_mut()).mutability != Mutability::Var {
|
||||
return Err(RuntimeError::new("Attempted to set an immutable global"));
|
||||
}
|
||||
if val.ty() != self.ty(ctx.as_context_mut()).ty {
|
||||
return Err(RuntimeError::new(format!(
|
||||
"Attempted to operate on a global of type {expected} as a global of type {found}",
|
||||
expected = self.ty(ctx.as_context_mut()).ty,
|
||||
found = val.ty(),
|
||||
)));
|
||||
}
|
||||
unsafe {
|
||||
self.vm_global
|
||||
.from
|
||||
.set(val)
|
||||
.map_err(|e| RuntimeError::new(format!("{}", e)))?;
|
||||
self.handle
|
||||
.get_mut(ctx.as_context_mut().objects_mut())
|
||||
.vmglobal()
|
||||
.as_mut()
|
||||
.val = val.as_raw(ctx.as_context_mut());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn from_vm_export(store: &Store, vm_global: VMGlobal) -> Self {
|
||||
pub(crate) fn from_vm_extern(
|
||||
ctx: impl AsContextMut,
|
||||
internal: InternalContextHandle<VMGlobal>,
|
||||
) -> Self {
|
||||
Self {
|
||||
store: store.clone(),
|
||||
vm_global,
|
||||
handle: unsafe {
|
||||
ContextHandle::from_internal(ctx.as_context_ref().objects().id(), internal)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns whether or not these two globals refer to the same data.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # use wasmer::{Global, Store, Value};
|
||||
/// # let store = Store::default();
|
||||
/// #
|
||||
/// let g = Global::new(&store, Value::I32(1));
|
||||
///
|
||||
/// assert!(g.same(&g));
|
||||
/// ```
|
||||
pub fn same(&self, other: &Self) -> bool {
|
||||
Arc::ptr_eq(&self.vm_global.from, &other.vm_global.from)
|
||||
/// Checks whether this `Global` can be used with the given context.
|
||||
pub fn is_from_context(&self, ctx: impl AsContextRef) -> bool {
|
||||
self.handle.context_id() == ctx.as_context_ref().objects().id()
|
||||
}
|
||||
|
||||
/// Get access to the backing VM value for this extern. This function is for
|
||||
/// tests it should not be called by users of the Wasmer API.
|
||||
///
|
||||
/// # Safety
|
||||
/// This function is unsafe to call outside of tests for the wasmer crate
|
||||
/// because there is no stability guarantee for the returned type and we may
|
||||
/// make breaking changes to it at any time or remove this method.
|
||||
#[doc(hidden)]
|
||||
pub unsafe fn get_vm_global(&self) -> &VMGlobal {
|
||||
&self.vm_global
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for Global {
|
||||
fn clone(&self) -> Self {
|
||||
let mut vm_global = self.vm_global.clone();
|
||||
vm_global.upgrade_instance_ref().unwrap();
|
||||
|
||||
Self {
|
||||
store: self.store.clone(),
|
||||
vm_global,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Global {
|
||||
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
formatter
|
||||
.debug_struct("Global")
|
||||
.field("ty", &self.ty())
|
||||
.field("value", &self.get())
|
||||
.finish()
|
||||
pub(crate) fn to_vm_extern(&self) -> VMExtern {
|
||||
VMExtern::Global(self.handle.internal_handle())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Exportable<'a> for Global {
|
||||
fn to_export(&self) -> Export {
|
||||
self.vm_global.clone().into()
|
||||
}
|
||||
|
||||
fn get_self_from_extern(_extern: &'a Extern) -> Result<&'a Self, ExportError> {
|
||||
match _extern {
|
||||
Extern::Global(global) => Ok(global),
|
||||
_ => Err(ExportError::IncompatibleType),
|
||||
}
|
||||
}
|
||||
|
||||
fn convert_to_weak_instance_ref(&mut self) {
|
||||
if let Some(v) = self.vm_global.instance_ref.as_mut() {
|
||||
*v = v.downgrade();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
262
lib/api/src/sys/externals/memory.rs
vendored
262
lib/api/src/sys/externals/memory.rs
vendored
@@ -1,16 +1,17 @@
|
||||
use crate::sys::context::{AsContextMut, AsContextRef};
|
||||
use crate::sys::exports::{ExportError, Exportable};
|
||||
use crate::sys::externals::Extern;
|
||||
use crate::sys::store::Store;
|
||||
use crate::sys::MemoryType;
|
||||
use crate::MemoryAccessError;
|
||||
use crate::{ContextRef, MemoryAccessError};
|
||||
use std::convert::TryInto;
|
||||
use std::marker::PhantomData;
|
||||
use std::mem;
|
||||
use std::mem::MaybeUninit;
|
||||
use std::slice;
|
||||
use std::sync::Arc;
|
||||
use wasmer_compiler::Export;
|
||||
use wasmer_types::Pages;
|
||||
use wasmer_vm::{MemoryError, VMMemory};
|
||||
use wasmer_vm::{
|
||||
ContextHandle, ContextObjects, InternalContextHandle, MemoryError, VMExtern, VMMemory,
|
||||
};
|
||||
|
||||
/// A WebAssembly `memory` instance.
|
||||
///
|
||||
@@ -26,10 +27,9 @@ use wasmer_vm::{MemoryError, VMMemory};
|
||||
/// mutable from both host and WebAssembly.
|
||||
///
|
||||
/// Spec: <https://webassembly.github.io/spec/core/exec/runtime.html#memory-instances>
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Memory {
|
||||
store: Store,
|
||||
vm_memory: VMMemory,
|
||||
handle: ContextHandle<VMMemory>,
|
||||
}
|
||||
|
||||
impl Memory {
|
||||
@@ -46,19 +46,14 @@ impl Memory {
|
||||
/// #
|
||||
/// let m = Memory::new(&store, MemoryType::new(1, None, false)).unwrap();
|
||||
/// ```
|
||||
pub fn new(store: &Store, ty: MemoryType) -> Result<Self, MemoryError> {
|
||||
let tunables = store.tunables();
|
||||
pub fn new(mut ctx: impl AsContextMut, ty: MemoryType) -> Result<Self, MemoryError> {
|
||||
let mut ctx = ctx.as_context_mut();
|
||||
let tunables = ctx.store().tunables();
|
||||
let style = tunables.memory_style(&ty);
|
||||
let memory = tunables.create_host_memory(&ty, &style)?;
|
||||
|
||||
Ok(Self {
|
||||
store: store.clone(),
|
||||
vm_memory: VMMemory {
|
||||
from: memory,
|
||||
// We are creating it from the host, and therefore there is no
|
||||
// associated instance with this memory
|
||||
instance_ref: None,
|
||||
},
|
||||
handle: ContextHandle::new(ctx.objects_mut(), memory),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -75,24 +70,8 @@ impl Memory {
|
||||
///
|
||||
/// assert_eq!(m.ty(), mt);
|
||||
/// ```
|
||||
pub fn ty(&self) -> MemoryType {
|
||||
self.vm_memory.from.ty()
|
||||
}
|
||||
|
||||
/// Returns the [`Store`] where the `Memory` belongs.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # use wasmer::{Memory, MemoryType, Pages, Store, Type, Value};
|
||||
/// # let store = Store::default();
|
||||
/// #
|
||||
/// let m = Memory::new(&store, MemoryType::new(1, None, false)).unwrap();
|
||||
///
|
||||
/// assert_eq!(m.store(), &store);
|
||||
/// ```
|
||||
pub fn store(&self) -> &Store {
|
||||
&self.store
|
||||
pub fn ty(&self, ctx: impl AsContextRef) -> MemoryType {
|
||||
self.handle.get(ctx.as_context_ref().objects()).ty()
|
||||
}
|
||||
|
||||
/// Returns the pointer to the raw bytes of the `Memory`.
|
||||
@@ -100,17 +79,13 @@ impl Memory {
|
||||
// This used by wasmer-emscripten and wasmer-c-api, but should be treated
|
||||
// as deprecated and not used in future code.
|
||||
#[doc(hidden)]
|
||||
pub fn data_ptr(&self) -> *mut u8 {
|
||||
let definition = self.vm_memory.from.vmmemory();
|
||||
let def = unsafe { definition.as_ref() };
|
||||
def.base
|
||||
pub fn data_ptr(&self, ctx: impl AsContextRef) -> *mut u8 {
|
||||
self.buffer(ctx.as_context_ref()).base
|
||||
}
|
||||
|
||||
/// Returns the size (in bytes) of the `Memory`.
|
||||
pub fn data_size(&self) -> u64 {
|
||||
let definition = self.vm_memory.from.vmmemory();
|
||||
let def = unsafe { definition.as_ref() };
|
||||
def.current_length.try_into().unwrap()
|
||||
pub fn data_size(&self, ctx: impl AsContextRef) -> u64 {
|
||||
self.buffer(ctx.as_context_ref()).len.try_into().unwrap()
|
||||
}
|
||||
|
||||
/// Returns the size (in [`Pages`]) of the `Memory`.
|
||||
@@ -125,8 +100,8 @@ impl Memory {
|
||||
///
|
||||
/// assert_eq!(m.size(), Pages(1));
|
||||
/// ```
|
||||
pub fn size(&self) -> Pages {
|
||||
self.vm_memory.from.size()
|
||||
pub fn size(&self, ctx: impl AsContextRef) -> Pages {
|
||||
self.handle.get(ctx.as_context_ref().objects()).size()
|
||||
}
|
||||
|
||||
/// Grow memory by the specified amount of WebAssembly [`Pages`] and return
|
||||
@@ -159,46 +134,17 @@ impl Memory {
|
||||
/// // This results in an error: `MemoryError::CouldNotGrow`.
|
||||
/// let s = m.grow(1).unwrap();
|
||||
/// ```
|
||||
pub fn grow<IntoPages>(&self, delta: IntoPages) -> Result<Pages, MemoryError>
|
||||
pub fn grow<IntoPages>(
|
||||
&self,
|
||||
mut ctx: impl AsContextMut,
|
||||
delta: IntoPages,
|
||||
) -> Result<Pages, MemoryError>
|
||||
where
|
||||
IntoPages: Into<Pages>,
|
||||
{
|
||||
self.vm_memory.from.grow(delta.into())
|
||||
}
|
||||
|
||||
pub(crate) fn from_vm_export(store: &Store, vm_memory: VMMemory) -> Self {
|
||||
Self {
|
||||
store: store.clone(),
|
||||
vm_memory,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns whether or not these two memories refer to the same data.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # use wasmer::{Memory, MemoryType, Store, Value};
|
||||
/// # let store = Store::default();
|
||||
/// #
|
||||
/// let m = Memory::new(&store, MemoryType::new(1, None, false)).unwrap();
|
||||
///
|
||||
/// assert!(m.same(&m));
|
||||
/// ```
|
||||
pub fn same(&self, other: &Self) -> bool {
|
||||
Arc::ptr_eq(&self.vm_memory.from, &other.vm_memory.from)
|
||||
}
|
||||
|
||||
/// Get access to the backing VM value for this extern. This function is for
|
||||
/// tests it should not be called by users of the Wasmer API.
|
||||
///
|
||||
/// # Safety
|
||||
/// This function is unsafe to call outside of tests for the wasmer crate
|
||||
/// because there is no stability guarantee for the returned type and we may
|
||||
/// make breaking changes to it at any time or remove this method.
|
||||
#[doc(hidden)]
|
||||
pub unsafe fn get_vm_memory(&self) -> &VMMemory {
|
||||
&self.vm_memory
|
||||
self.handle
|
||||
.get_mut(ctx.as_context_mut().objects_mut())
|
||||
.grow(delta.into())
|
||||
}
|
||||
|
||||
/// Safely reads bytes from the memory at the given offset.
|
||||
@@ -208,20 +154,13 @@ impl Memory {
|
||||
///
|
||||
/// This method is guaranteed to be safe (from the host side) in the face of
|
||||
/// concurrent writes.
|
||||
pub fn read(&self, offset: u64, buf: &mut [u8]) -> Result<(), MemoryAccessError> {
|
||||
let definition = self.vm_memory.from.vmmemory();
|
||||
let def = unsafe { definition.as_ref() };
|
||||
let end = offset
|
||||
.checked_add(buf.len() as u64)
|
||||
.ok_or(MemoryAccessError::Overflow)?;
|
||||
if end > def.current_length.try_into().unwrap() {
|
||||
return Err(MemoryAccessError::HeapOutOfBounds);
|
||||
}
|
||||
unsafe {
|
||||
volatile_memcpy_read(def.base.add(offset as usize), buf.as_mut_ptr(), buf.len());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
pub fn read(
|
||||
&self,
|
||||
ctx: impl AsContextRef,
|
||||
offset: u64,
|
||||
buf: &mut [u8],
|
||||
) -> Result<(), MemoryAccessError> {
|
||||
self.buffer(ctx.as_context_ref()).read(offset, buf)
|
||||
}
|
||||
|
||||
/// Safely reads bytes from the memory at the given offset.
|
||||
@@ -236,23 +175,11 @@ impl Memory {
|
||||
/// concurrent writes.
|
||||
pub fn read_uninit<'a>(
|
||||
&self,
|
||||
ctx: impl AsContextRef,
|
||||
offset: u64,
|
||||
buf: &'a mut [MaybeUninit<u8>],
|
||||
) -> Result<&'a mut [u8], MemoryAccessError> {
|
||||
let definition = self.vm_memory.from.vmmemory();
|
||||
let def = unsafe { definition.as_ref() };
|
||||
let end = offset
|
||||
.checked_add(buf.len() as u64)
|
||||
.ok_or(MemoryAccessError::Overflow)?;
|
||||
if end > def.current_length.try_into().unwrap() {
|
||||
return Err(MemoryAccessError::HeapOutOfBounds);
|
||||
}
|
||||
let buf_ptr = buf.as_mut_ptr() as *mut u8;
|
||||
unsafe {
|
||||
volatile_memcpy_read(def.base.add(offset as usize), buf_ptr, buf.len());
|
||||
}
|
||||
|
||||
Ok(unsafe { slice::from_raw_parts_mut(buf_ptr, buf.len()) })
|
||||
self.buffer(ctx.as_context_ref()).read_uninit(offset, buf)
|
||||
}
|
||||
|
||||
/// Safely writes bytes to the memory at the given offset.
|
||||
@@ -262,50 +189,107 @@ impl Memory {
|
||||
///
|
||||
/// This method is guaranteed to be safe (from the host side) in the face of
|
||||
/// concurrent reads/writes.
|
||||
pub fn write(&self, offset: u64, data: &[u8]) -> Result<(), MemoryAccessError> {
|
||||
let definition = self.vm_memory.from.vmmemory();
|
||||
let def = unsafe { definition.as_ref() };
|
||||
let end = offset
|
||||
.checked_add(data.len() as u64)
|
||||
.ok_or(MemoryAccessError::Overflow)?;
|
||||
if end > def.current_length.try_into().unwrap() {
|
||||
return Err(MemoryAccessError::HeapOutOfBounds);
|
||||
}
|
||||
unsafe {
|
||||
volatile_memcpy_write(data.as_ptr(), def.base.add(offset as usize), data.len());
|
||||
}
|
||||
Ok(())
|
||||
pub fn write(
|
||||
&self,
|
||||
ctx: impl AsContextRef,
|
||||
offset: u64,
|
||||
data: &[u8],
|
||||
) -> Result<(), MemoryAccessError> {
|
||||
self.buffer(ctx.as_context_ref()).write(offset, data)
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for Memory {
|
||||
fn clone(&self) -> Self {
|
||||
let mut vm_memory = self.vm_memory.clone();
|
||||
vm_memory.upgrade_instance_ref().unwrap();
|
||||
|
||||
Self {
|
||||
store: self.store.clone(),
|
||||
vm_memory,
|
||||
pub(crate) fn buffer<'a, U>(&'a self, ctx: ContextRef<'a, U>) -> MemoryBuffer<'a> {
|
||||
let definition = self.handle.get(ctx.objects()).vmmemory();
|
||||
let def = unsafe { definition.as_ref() };
|
||||
MemoryBuffer {
|
||||
base: def.base,
|
||||
len: def.current_length,
|
||||
marker: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn from_vm_extern(
|
||||
ctx: impl AsContextRef,
|
||||
internal: InternalContextHandle<VMMemory>,
|
||||
) -> Self {
|
||||
Self {
|
||||
handle: unsafe {
|
||||
ContextHandle::from_internal(ctx.as_context_ref().objects().id(), internal)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks whether this `Memory` can be used with the given context.
|
||||
pub fn is_from_context(&self, ctx: impl AsContextRef) -> bool {
|
||||
self.handle.context_id() == ctx.as_context_ref().objects().id()
|
||||
}
|
||||
|
||||
pub(crate) fn to_vm_extern(&self) -> VMExtern {
|
||||
VMExtern::Memory(self.handle.internal_handle())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Exportable<'a> for Memory {
|
||||
fn to_export(&self) -> Export {
|
||||
self.vm_memory.clone().into()
|
||||
}
|
||||
|
||||
fn get_self_from_extern(_extern: &'a Extern) -> Result<&'a Self, ExportError> {
|
||||
match _extern {
|
||||
Extern::Memory(memory) => Ok(memory),
|
||||
_ => Err(ExportError::IncompatibleType),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn convert_to_weak_instance_ref(&mut self) {
|
||||
if let Some(v) = self.vm_memory.instance_ref.as_mut() {
|
||||
*v = v.downgrade();
|
||||
/// Underlying buffer for a memory.
|
||||
#[derive(Copy, Clone)]
|
||||
pub(crate) struct MemoryBuffer<'a> {
|
||||
base: *mut u8,
|
||||
len: usize,
|
||||
marker: PhantomData<(&'a Memory, &'a ContextObjects)>,
|
||||
}
|
||||
|
||||
impl<'a> MemoryBuffer<'a> {
|
||||
pub(crate) fn read(&self, offset: u64, buf: &mut [u8]) -> Result<(), MemoryAccessError> {
|
||||
let end = offset
|
||||
.checked_add(buf.len() as u64)
|
||||
.ok_or(MemoryAccessError::Overflow)?;
|
||||
if end > self.len.try_into().unwrap() {
|
||||
return Err(MemoryAccessError::HeapOutOfBounds);
|
||||
}
|
||||
unsafe {
|
||||
volatile_memcpy_read(self.base.add(offset as usize), buf.as_mut_ptr(), buf.len());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn read_uninit<'b>(
|
||||
&self,
|
||||
offset: u64,
|
||||
buf: &'b mut [MaybeUninit<u8>],
|
||||
) -> Result<&'b mut [u8], MemoryAccessError> {
|
||||
let end = offset
|
||||
.checked_add(buf.len() as u64)
|
||||
.ok_or(MemoryAccessError::Overflow)?;
|
||||
if end > self.len.try_into().unwrap() {
|
||||
return Err(MemoryAccessError::HeapOutOfBounds);
|
||||
}
|
||||
let buf_ptr = buf.as_mut_ptr() as *mut u8;
|
||||
unsafe {
|
||||
volatile_memcpy_read(self.base.add(offset as usize), buf_ptr, buf.len());
|
||||
}
|
||||
|
||||
Ok(unsafe { slice::from_raw_parts_mut(buf_ptr, buf.len()) })
|
||||
}
|
||||
|
||||
pub(crate) fn write(&self, offset: u64, data: &[u8]) -> Result<(), MemoryAccessError> {
|
||||
let end = offset
|
||||
.checked_add(data.len() as u64)
|
||||
.ok_or(MemoryAccessError::Overflow)?;
|
||||
if end > self.len.try_into().unwrap() {
|
||||
return Err(MemoryAccessError::HeapOutOfBounds);
|
||||
}
|
||||
unsafe {
|
||||
volatile_memcpy_write(data.as_ptr(), self.base.add(offset as usize), data.len());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
88
lib/api/src/sys/externals/mod.rs
vendored
88
lib/api/src/sys/externals/mod.rs
vendored
@@ -1,21 +1,20 @@
|
||||
pub(crate) mod function;
|
||||
mod global;
|
||||
mod memory;
|
||||
mod table;
|
||||
pub(crate) mod global;
|
||||
pub(crate) mod memory;
|
||||
pub(crate) mod table;
|
||||
|
||||
pub use self::function::{
|
||||
FromToNativeWasmType, Function, HostFunction, WasmTypeList, WithEnv, WithoutEnv,
|
||||
};
|
||||
pub use self::function::{FromToNativeWasmType, Function, HostFunction, WasmTypeList};
|
||||
|
||||
pub use self::global::Global;
|
||||
pub use self::memory::Memory;
|
||||
pub use self::table::Table;
|
||||
|
||||
use crate::sys::exports::{ExportError, Exportable};
|
||||
use crate::sys::store::{Store, StoreObject};
|
||||
use crate::sys::ExternType;
|
||||
use std::fmt;
|
||||
use wasmer_compiler::Export;
|
||||
use wasmer_vm::VMExtern;
|
||||
|
||||
use super::context::{AsContextMut, AsContextRef};
|
||||
|
||||
/// An `Extern` is the runtime representation of an entity that
|
||||
/// can be imported or exported.
|
||||
@@ -35,61 +34,50 @@ pub enum Extern {
|
||||
|
||||
impl Extern {
|
||||
/// Return the underlying type of the inner `Extern`.
|
||||
pub fn ty(&self) -> ExternType {
|
||||
pub fn ty(&self, ctx: impl AsContextRef) -> ExternType {
|
||||
match self {
|
||||
Self::Function(ft) => ExternType::Function(ft.ty().clone()),
|
||||
Self::Memory(ft) => ExternType::Memory(ft.ty()),
|
||||
Self::Table(tt) => ExternType::Table(*tt.ty()),
|
||||
Self::Global(gt) => ExternType::Global(*gt.ty()),
|
||||
Self::Function(ft) => ExternType::Function(ft.ty(ctx)),
|
||||
Self::Memory(ft) => ExternType::Memory(ft.ty(ctx)),
|
||||
Self::Table(tt) => ExternType::Table(tt.ty(ctx)),
|
||||
Self::Global(gt) => ExternType::Global(gt.ty(ctx)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Create an `Extern` from an `wasmer_compiler::Export`.
|
||||
pub fn from_vm_export(store: &Store, export: Export) -> Self {
|
||||
match export {
|
||||
Export::Function(f) => Self::Function(Function::from_vm_export(store, f)),
|
||||
Export::Memory(m) => Self::Memory(Memory::from_vm_export(store, m)),
|
||||
Export::Global(g) => Self::Global(Global::from_vm_export(store, g)),
|
||||
Export::Table(t) => Self::Table(Table::from_vm_export(store, t)),
|
||||
/// Create an `Extern` from an `wasmer_engine::Export`.
|
||||
pub(crate) fn from_vm_extern(ctx: impl AsContextMut, vm_extern: VMExtern) -> Self {
|
||||
match vm_extern {
|
||||
VMExtern::Function(f) => Self::Function(Function::from_vm_extern(ctx, f)),
|
||||
VMExtern::Memory(m) => Self::Memory(Memory::from_vm_extern(ctx, m)),
|
||||
VMExtern::Global(g) => Self::Global(Global::from_vm_extern(ctx, g)),
|
||||
VMExtern::Table(t) => Self::Table(Table::from_vm_extern(ctx, t)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks whether this `Extern` can be used with the given context.
|
||||
pub fn is_from_context(&self, ctx: impl AsContextRef) -> bool {
|
||||
match self {
|
||||
Self::Function(f) => f.is_from_context(ctx),
|
||||
Self::Global(g) => g.is_from_context(ctx),
|
||||
Self::Memory(m) => m.is_from_context(ctx),
|
||||
Self::Table(t) => t.is_from_context(ctx),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn to_vm_extern(&self) -> VMExtern {
|
||||
match self {
|
||||
Self::Function(f) => f.to_vm_extern(),
|
||||
Self::Global(g) => g.to_vm_extern(),
|
||||
Self::Memory(m) => m.to_vm_extern(),
|
||||
Self::Table(t) => t.to_vm_extern(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Exportable<'a> for Extern {
|
||||
fn to_export(&self) -> Export {
|
||||
match self {
|
||||
Self::Function(f) => f.to_export(),
|
||||
Self::Global(g) => g.to_export(),
|
||||
Self::Memory(m) => m.to_export(),
|
||||
Self::Table(t) => t.to_export(),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_self_from_extern(_extern: &'a Self) -> Result<&'a Self, ExportError> {
|
||||
// Since this is already an extern, we can just return it.
|
||||
Ok(_extern)
|
||||
}
|
||||
|
||||
fn convert_to_weak_instance_ref(&mut self) {
|
||||
match self {
|
||||
Self::Function(f) => f.convert_to_weak_instance_ref(),
|
||||
Self::Global(g) => g.convert_to_weak_instance_ref(),
|
||||
Self::Memory(m) => m.convert_to_weak_instance_ref(),
|
||||
Self::Table(t) => t.convert_to_weak_instance_ref(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl StoreObject for Extern {
|
||||
fn comes_from_same_store(&self, store: &Store) -> bool {
|
||||
let my_store = match self {
|
||||
Self::Function(f) => f.store(),
|
||||
Self::Global(g) => g.store(),
|
||||
Self::Memory(m) => m.store(),
|
||||
Self::Table(t) => t.store(),
|
||||
};
|
||||
Store::same(my_store, store)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Extern {
|
||||
|
||||
184
lib/api/src/sys/externals/table.rs
vendored
184
lib/api/src/sys/externals/table.rs
vendored
@@ -1,12 +1,10 @@
|
||||
use crate::sys::context::{AsContextMut, AsContextRef};
|
||||
use crate::sys::exports::{ExportError, Exportable};
|
||||
use crate::sys::externals::Extern;
|
||||
use crate::sys::store::Store;
|
||||
use crate::sys::types::{Val, ValFuncRef};
|
||||
use crate::sys::RuntimeError;
|
||||
use crate::sys::TableType;
|
||||
use std::sync::Arc;
|
||||
use wasmer_compiler::Export;
|
||||
use wasmer_vm::{Table as RuntimeTable, TableElement, VMTable};
|
||||
use crate::{ExternRef, Function, Value};
|
||||
use wasmer_vm::{ContextHandle, InternalContextHandle, TableElement, VMExtern, VMTable};
|
||||
|
||||
/// A WebAssembly `table` instance.
|
||||
///
|
||||
@@ -17,19 +15,48 @@ use wasmer_vm::{Table as RuntimeTable, TableElement, VMTable};
|
||||
/// mutable from both host and WebAssembly.
|
||||
///
|
||||
/// Spec: <https://webassembly.github.io/spec/core/exec/runtime.html#table-instances>
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Table {
|
||||
store: Store,
|
||||
vm_table: VMTable,
|
||||
handle: ContextHandle<VMTable>,
|
||||
}
|
||||
|
||||
fn set_table_item(
|
||||
table: &dyn RuntimeTable,
|
||||
table: &mut VMTable,
|
||||
item_index: u32,
|
||||
item: TableElement,
|
||||
) -> Result<(), RuntimeError> {
|
||||
table.set(item_index, item).map_err(|e| e.into())
|
||||
}
|
||||
|
||||
fn value_to_table_element(
|
||||
ctx: impl AsContextMut,
|
||||
val: Value,
|
||||
) -> Result<wasmer_vm::TableElement, RuntimeError> {
|
||||
if !val.is_from_context(ctx.as_context_ref()) {
|
||||
return Err(RuntimeError::new("cannot pass Value across contexts"));
|
||||
}
|
||||
Ok(match val {
|
||||
Value::ExternRef(extern_ref) => {
|
||||
wasmer_vm::TableElement::ExternRef(extern_ref.map(|e| e.vm_externref()))
|
||||
}
|
||||
Value::FuncRef(func_ref) => {
|
||||
wasmer_vm::TableElement::FuncRef(func_ref.map(|f| f.vm_funcref(ctx)))
|
||||
}
|
||||
_ => return Err(RuntimeError::new("val is not reference")),
|
||||
})
|
||||
}
|
||||
|
||||
fn value_from_table_element(ctx: impl AsContextMut, item: wasmer_vm::TableElement) -> Value {
|
||||
match item {
|
||||
wasmer_vm::TableElement::FuncRef(funcref) => {
|
||||
Value::FuncRef(funcref.map(|f| unsafe { Function::from_vm_funcref(ctx, f) }))
|
||||
}
|
||||
wasmer_vm::TableElement::ExternRef(extern_ref) => {
|
||||
Value::ExternRef(extern_ref.map(|e| unsafe { ExternRef::from_vm_externref(ctx, e) }))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Table {
|
||||
/// Creates a new `Table` with the provided [`TableType`] definition.
|
||||
///
|
||||
@@ -37,53 +64,58 @@ impl Table {
|
||||
///
|
||||
/// This function will construct the `Table` using the store
|
||||
/// [`BaseTunables`][crate::sys::BaseTunables].
|
||||
pub fn new(store: &Store, ty: TableType, init: Val) -> Result<Self, RuntimeError> {
|
||||
let item = init.into_table_reference(store)?;
|
||||
let tunables = store.tunables();
|
||||
pub fn new(
|
||||
mut ctx: impl AsContextMut,
|
||||
ty: TableType,
|
||||
init: Value,
|
||||
) -> Result<Self, RuntimeError> {
|
||||
let mut ctx = ctx.as_context_mut();
|
||||
let item = value_to_table_element(ctx.as_context_mut(), init)?;
|
||||
let tunables = ctx.store().tunables();
|
||||
let style = tunables.table_style(&ty);
|
||||
let table = tunables
|
||||
let mut table = tunables
|
||||
.create_host_table(&ty, &style)
|
||||
.map_err(RuntimeError::new)?;
|
||||
|
||||
let num_elements = table.size();
|
||||
for i in 0..num_elements {
|
||||
set_table_item(table.as_ref(), i, item.clone())?;
|
||||
set_table_item(&mut table, i, item.clone())?;
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
store: store.clone(),
|
||||
vm_table: VMTable {
|
||||
from: table,
|
||||
instance_ref: None,
|
||||
},
|
||||
handle: ContextHandle::new(ctx.as_context_mut().objects_mut(), table),
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns the [`TableType`] of the `Table`.
|
||||
pub fn ty(&self) -> &TableType {
|
||||
self.vm_table.from.ty()
|
||||
}
|
||||
|
||||
/// Returns the [`Store`] where the `Table` belongs.
|
||||
pub fn store(&self) -> &Store {
|
||||
&self.store
|
||||
pub fn ty(&self, ctx: impl AsContextRef) -> TableType {
|
||||
*self.handle.get(ctx.as_context_ref().objects()).ty()
|
||||
}
|
||||
|
||||
/// Retrieves an element of the table at the provided `index`.
|
||||
pub fn get(&self, index: u32) -> Option<Val> {
|
||||
let item = self.vm_table.from.get(index)?;
|
||||
Some(ValFuncRef::from_table_reference(item, &self.store))
|
||||
pub fn get(&self, ctx: impl AsContextMut, index: u32) -> Option<Value> {
|
||||
let item = self.handle.get(ctx.as_context_ref().objects()).get(index)?;
|
||||
Some(value_from_table_element(ctx, item))
|
||||
}
|
||||
|
||||
/// Sets an element `val` in the Table at the provided `index`.
|
||||
pub fn set(&self, index: u32, val: Val) -> Result<(), RuntimeError> {
|
||||
let item = val.into_table_reference(&self.store)?;
|
||||
set_table_item(self.vm_table.from.as_ref(), index, item)
|
||||
pub fn set(
|
||||
&self,
|
||||
mut ctx: impl AsContextMut,
|
||||
index: u32,
|
||||
val: Value,
|
||||
) -> Result<(), RuntimeError> {
|
||||
let item = value_to_table_element(ctx.as_context_mut(), val)?;
|
||||
set_table_item(
|
||||
self.handle.get_mut(ctx.as_context_mut().objects_mut()),
|
||||
index,
|
||||
item,
|
||||
)
|
||||
}
|
||||
|
||||
/// Retrieves the size of the `Table` (in elements)
|
||||
pub fn size(&self) -> u32 {
|
||||
self.vm_table.from.size()
|
||||
pub fn size(&self, ctx: impl AsContextRef) -> u32 {
|
||||
self.handle.get(ctx.as_context_ref().objects()).size()
|
||||
}
|
||||
|
||||
/// Grows the size of the `Table` by `delta`, initializating
|
||||
@@ -95,10 +127,15 @@ impl Table {
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns an error if the `delta` is out of bounds for the table.
|
||||
pub fn grow(&self, delta: u32, init: Val) -> Result<u32, RuntimeError> {
|
||||
let item = init.into_table_reference(&self.store)?;
|
||||
self.vm_table
|
||||
.from
|
||||
pub fn grow(
|
||||
&self,
|
||||
mut ctx: impl AsContextMut,
|
||||
delta: u32,
|
||||
init: Value,
|
||||
) -> Result<u32, RuntimeError> {
|
||||
let item = value_to_table_element(ctx.as_context_mut(), init)?;
|
||||
self.handle
|
||||
.get_mut(ctx.as_context_mut().objects_mut())
|
||||
.grow(delta, item)
|
||||
.ok_or_else(|| RuntimeError::new(format!("failed to grow table by `{}`", delta)))
|
||||
}
|
||||
@@ -111,80 +148,59 @@ impl Table {
|
||||
/// Returns an error if the range is out of bounds of either the source or
|
||||
/// destination tables.
|
||||
pub fn copy(
|
||||
mut ctx: impl AsContextMut,
|
||||
dst_table: &Self,
|
||||
dst_index: u32,
|
||||
src_table: &Self,
|
||||
src_index: u32,
|
||||
len: u32,
|
||||
) -> Result<(), RuntimeError> {
|
||||
if !Store::same(&dst_table.store, &src_table.store) {
|
||||
if dst_table.handle.context_id() != src_table.handle.context_id() {
|
||||
return Err(RuntimeError::new(
|
||||
"cross-`Store` table copies are not supported",
|
||||
"cross-`Context` table copies are not supported",
|
||||
));
|
||||
}
|
||||
RuntimeTable::copy(
|
||||
dst_table.vm_table.from.as_ref(),
|
||||
src_table.vm_table.from.as_ref(),
|
||||
dst_index,
|
||||
src_index,
|
||||
len,
|
||||
)
|
||||
let mut ctx = ctx.as_context_mut();
|
||||
if dst_table.handle.internal_handle() == src_table.handle.internal_handle() {
|
||||
let table = dst_table.handle.get_mut(ctx.objects_mut());
|
||||
table.copy_within(dst_index, src_index, len)
|
||||
} else {
|
||||
let (src_table, dst_table) = ctx.objects_mut().get_2_mut(
|
||||
src_table.handle.internal_handle(),
|
||||
dst_table.handle.internal_handle(),
|
||||
);
|
||||
VMTable::copy(dst_table, src_table, dst_index, src_index, len)
|
||||
}
|
||||
.map_err(RuntimeError::from_trap)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn from_vm_export(store: &Store, vm_table: VMTable) -> Self {
|
||||
pub(crate) fn from_vm_extern(
|
||||
ctx: impl AsContextMut,
|
||||
internal: InternalContextHandle<VMTable>,
|
||||
) -> Self {
|
||||
Self {
|
||||
store: store.clone(),
|
||||
vm_table,
|
||||
handle: unsafe {
|
||||
ContextHandle::from_internal(ctx.as_context_ref().objects().id(), internal)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns whether or not these two tables refer to the same data.
|
||||
pub fn same(&self, other: &Self) -> bool {
|
||||
Arc::ptr_eq(&self.vm_table.from, &other.vm_table.from)
|
||||
/// Checks whether this `Table` can be used with the given context.
|
||||
pub fn is_from_context(&self, ctx: impl AsContextRef) -> bool {
|
||||
self.handle.context_id() == ctx.as_context_ref().objects().id()
|
||||
}
|
||||
|
||||
/// Get access to the backing VM value for this extern. This function is for
|
||||
/// tests it should not be called by users of the Wasmer API.
|
||||
///
|
||||
/// # Safety
|
||||
/// This function is unsafe to call outside of tests for the wasmer crate
|
||||
/// because there is no stability guarantee for the returned type and we may
|
||||
/// make breaking changes to it at any time or remove this method.
|
||||
#[doc(hidden)]
|
||||
pub unsafe fn get_vm_table(&self) -> &VMTable {
|
||||
&self.vm_table
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for Table {
|
||||
fn clone(&self) -> Self {
|
||||
let mut vm_table = self.vm_table.clone();
|
||||
vm_table.upgrade_instance_ref().unwrap();
|
||||
|
||||
Self {
|
||||
store: self.store.clone(),
|
||||
vm_table,
|
||||
}
|
||||
pub(crate) fn to_vm_extern(&self) -> VMExtern {
|
||||
VMExtern::Table(self.handle.internal_handle())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Exportable<'a> for Table {
|
||||
fn to_export(&self) -> Export {
|
||||
self.vm_table.clone().into()
|
||||
}
|
||||
|
||||
fn get_self_from_extern(_extern: &'a Extern) -> Result<&'a Self, ExportError> {
|
||||
match _extern {
|
||||
Extern::Table(table) => Ok(table),
|
||||
_ => Err(ExportError::IncompatibleType),
|
||||
}
|
||||
}
|
||||
|
||||
fn convert_to_weak_instance_ref(&mut self) {
|
||||
if let Some(v) = self.vm_table.instance_ref.as_mut() {
|
||||
*v = v.downgrade();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -286,7 +286,7 @@ mod test {
|
||||
let happy_dog_entry = imports1.get_export("dog", "happy").unwrap();
|
||||
|
||||
assert!(
|
||||
if let Export::Global(happy_dog_global) = happy_dog_entry.to_export() {
|
||||
if let Export::Global(happy_dog_global) = happy_dog_entry.to_vm_extern() {
|
||||
happy_dog_global.from.ty().ty == Type::I32
|
||||
} else {
|
||||
false
|
||||
@@ -399,7 +399,7 @@ mod test {
|
||||
let happy_dog_entry = imports1.get_export("dog", "happy").unwrap();
|
||||
|
||||
assert!(
|
||||
if let Export::Global(happy_dog_global) = happy_dog_entry.to_export() {
|
||||
if let Export::Global(happy_dog_global) = happy_dog_entry.to_vm_extern() {
|
||||
happy_dog_global.from.ty().ty == Type::I64
|
||||
} else {
|
||||
false
|
||||
@@ -427,7 +427,7 @@ mod test {
|
||||
let happy_dog_entry = imports2.get_export("dog", "happy").unwrap();
|
||||
|
||||
assert!(
|
||||
if let Export::Global(happy_dog_global) = happy_dog_entry.to_export() {
|
||||
if let Export::Global(happy_dog_global) = happy_dog_entry.to_vm_extern() {
|
||||
happy_dog_global.from.ty().ty == Type::I32
|
||||
} else {
|
||||
false
|
||||
|
||||
@@ -2,12 +2,12 @@ use crate::sys::exports::Exports;
|
||||
use crate::sys::externals::Extern;
|
||||
use crate::sys::imports::Imports;
|
||||
use crate::sys::module::Module;
|
||||
use crate::sys::store::Store;
|
||||
use crate::sys::{HostEnvInitError, LinkError, RuntimeError};
|
||||
use crate::sys::{LinkError, RuntimeError};
|
||||
use std::fmt;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use thiserror::Error;
|
||||
use wasmer_vm::{InstanceHandle, VMContext};
|
||||
use wasmer_vm::{ContextHandle, InstanceHandle};
|
||||
|
||||
use super::context::AsContextMut;
|
||||
|
||||
/// A WebAssembly Instance is a stateful, executable
|
||||
/// instance of a WebAssembly [`Module`].
|
||||
@@ -19,10 +19,8 @@ use wasmer_vm::{InstanceHandle, VMContext};
|
||||
/// Spec: <https://webassembly.github.io/spec/core/exec/runtime.html#module-instances>
|
||||
#[derive(Clone)]
|
||||
pub struct Instance {
|
||||
handle: Arc<Mutex<InstanceHandle>>,
|
||||
_handle: ContextHandle<InstanceHandle>,
|
||||
module: Module,
|
||||
#[allow(dead_code)]
|
||||
imports: Vec<Extern>,
|
||||
/// The exports for an instance.
|
||||
pub exports: Exports,
|
||||
}
|
||||
@@ -64,9 +62,10 @@ pub enum InstantiationError {
|
||||
#[error("missing requires CPU features: {0:?}")]
|
||||
CpuFeature(String),
|
||||
|
||||
/// Error occurred when initializing the host environment.
|
||||
#[error(transparent)]
|
||||
HostEnvInitialization(HostEnvInitError),
|
||||
/// Import from a different [`Context`].
|
||||
/// This error occurs when an import from a different context is used.
|
||||
#[error("cannot mix imports from different contexts")]
|
||||
BadContext,
|
||||
}
|
||||
|
||||
impl From<wasmer_compiler::InstantiationError> for InstantiationError {
|
||||
@@ -79,12 +78,6 @@ impl From<wasmer_compiler::InstantiationError> for InstantiationError {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<HostEnvInitError> for InstantiationError {
|
||||
fn from(other: HostEnvInitError) -> Self {
|
||||
Self::HostEnvInitialization(other)
|
||||
}
|
||||
}
|
||||
|
||||
impl Instance {
|
||||
/// Creates a new `Instance` from a WebAssembly [`Module`] and a
|
||||
/// set of imports using [`Imports`] or the [`imports`] macro helper.
|
||||
@@ -114,45 +107,31 @@ impl Instance {
|
||||
/// Those are, as defined by the spec:
|
||||
/// * Link errors that happen when plugging the imports into the instance
|
||||
/// * Runtime errors that happen when running the module `start` function.
|
||||
pub fn new(module: &Module, imports: &Imports) -> Result<Self, InstantiationError> {
|
||||
let store = module.store();
|
||||
pub fn new(
|
||||
mut ctx: impl AsContextMut,
|
||||
module: &Module,
|
||||
imports: &Imports,
|
||||
) -> Result<Self, InstantiationError> {
|
||||
let imports = imports
|
||||
.imports_for_module(module)
|
||||
.map_err(InstantiationError::Link)?;
|
||||
let handle = module.instantiate(&imports)?;
|
||||
let mut handle = module.instantiate(ctx.as_context_mut(), &imports)?;
|
||||
let exports = module
|
||||
.exports()
|
||||
.map(|export| {
|
||||
let name = export.name().to_string();
|
||||
let export = handle.lookup(&name).expect("export");
|
||||
let extern_ = Extern::from_vm_export(store, export.into());
|
||||
let extern_ = Extern::from_vm_extern(ctx.as_context_mut(), export);
|
||||
(name, extern_)
|
||||
})
|
||||
.collect::<Exports>();
|
||||
|
||||
let instance = Self {
|
||||
handle: Arc::new(Mutex::new(handle)),
|
||||
_handle: ContextHandle::new(ctx.as_context_mut().objects_mut(), handle),
|
||||
module: module.clone(),
|
||||
imports,
|
||||
exports,
|
||||
};
|
||||
|
||||
// # Safety
|
||||
// `initialize_host_envs` should be called after instantiation but before
|
||||
// returning an `Instance` to the user. We set up the host environments
|
||||
// via `WasmerEnv::init_with_instance`.
|
||||
//
|
||||
// This usage is correct because we pass a valid pointer to `instance` and the
|
||||
// correct error type returned by `WasmerEnv::init_with_instance` as a generic
|
||||
// parameter.
|
||||
unsafe {
|
||||
instance
|
||||
.handle
|
||||
.lock()
|
||||
.unwrap()
|
||||
.initialize_host_envs::<HostEnvInitError>(&instance as *const _ as *const _)?;
|
||||
}
|
||||
|
||||
Ok(instance)
|
||||
}
|
||||
|
||||
@@ -166,43 +145,29 @@ impl Instance {
|
||||
/// Those are, as defined by the spec:
|
||||
/// * Link errors that happen when plugging the imports into the instance
|
||||
/// * Runtime errors that happen when running the module `start` function.
|
||||
pub fn new_by_index(module: &Module, externs: &[Extern]) -> Result<Self, InstantiationError> {
|
||||
let store = module.store();
|
||||
pub fn new_by_index(
|
||||
mut ctx: impl AsContextMut,
|
||||
module: &Module,
|
||||
externs: &[Extern],
|
||||
) -> Result<Self, InstantiationError> {
|
||||
let imports = externs.to_vec();
|
||||
let handle = module.instantiate(&imports)?;
|
||||
let mut handle = module.instantiate(ctx.as_context_mut(), &imports)?;
|
||||
let exports = module
|
||||
.exports()
|
||||
.map(|export| {
|
||||
let name = export.name().to_string();
|
||||
let export = handle.lookup(&name).expect("export");
|
||||
let extern_ = Extern::from_vm_export(store, export.into());
|
||||
let extern_ = Extern::from_vm_extern(ctx.as_context_mut(), export);
|
||||
(name, extern_)
|
||||
})
|
||||
.collect::<Exports>();
|
||||
|
||||
let instance = Self {
|
||||
handle: Arc::new(Mutex::new(handle)),
|
||||
_handle: ContextHandle::new(ctx.as_context_mut().objects_mut(), handle),
|
||||
module: module.clone(),
|
||||
imports,
|
||||
exports,
|
||||
};
|
||||
|
||||
// # Safety
|
||||
// `initialize_host_envs` should be called after instantiation but before
|
||||
// returning an `Instance` to the user. We set up the host environments
|
||||
// via `WasmerEnv::init_with_instance`.
|
||||
//
|
||||
// This usage is correct because we pass a valid pointer to `instance` and the
|
||||
// correct error type returned by `WasmerEnv::init_with_instance` as a generic
|
||||
// parameter.
|
||||
unsafe {
|
||||
instance
|
||||
.handle
|
||||
.lock()
|
||||
.unwrap()
|
||||
.initialize_host_envs::<HostEnvInitError>(&instance as *const _ as *const _)?;
|
||||
}
|
||||
|
||||
Ok(instance)
|
||||
}
|
||||
|
||||
@@ -210,16 +175,6 @@ impl Instance {
|
||||
pub fn module(&self) -> &Module {
|
||||
&self.module
|
||||
}
|
||||
|
||||
/// Returns the [`Store`] where the `Instance` belongs.
|
||||
pub fn store(&self) -> &Store {
|
||||
self.module.store()
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub fn vmctx_ptr(&self) -> *mut VMContext {
|
||||
self.handle.lock().unwrap().vmctx_ptr()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Instance {
|
||||
|
||||
@@ -12,6 +12,9 @@ use std::{
|
||||
use thiserror::Error;
|
||||
use wasmer_types::ValueType;
|
||||
|
||||
use super::context::AsContextRef;
|
||||
use super::externals::memory::MemoryBuffer;
|
||||
|
||||
/// Error for invalid [`Memory`] access.
|
||||
#[derive(Clone, Copy, Debug, Error)]
|
||||
#[non_exhaustive]
|
||||
@@ -51,7 +54,7 @@ impl From<FromUtf8Error> for MemoryAccessError {
|
||||
/// thread.
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct WasmRef<'a, T: ValueType> {
|
||||
memory: &'a Memory,
|
||||
buffer: MemoryBuffer<'a>,
|
||||
offset: u64,
|
||||
marker: PhantomData<*mut T>,
|
||||
}
|
||||
@@ -59,9 +62,9 @@ pub struct WasmRef<'a, T: ValueType> {
|
||||
impl<'a, T: ValueType> WasmRef<'a, T> {
|
||||
/// Creates a new `WasmRef` at the given offset in a memory.
|
||||
#[inline]
|
||||
pub fn new(memory: &'a Memory, offset: u64) -> Self {
|
||||
pub fn new(ctx: &'a impl AsContextRef, memory: &'a Memory, offset: u64) -> Self {
|
||||
Self {
|
||||
memory,
|
||||
buffer: memory.buffer(ctx.as_context_ref()),
|
||||
offset,
|
||||
marker: PhantomData,
|
||||
}
|
||||
@@ -96,19 +99,13 @@ impl<'a, T: ValueType> WasmRef<'a, T> {
|
||||
WasmPtr::<T, M>::new(offset)
|
||||
}
|
||||
|
||||
/// Get a reference to the Wasm memory backing this reference.
|
||||
#[inline]
|
||||
pub fn memory(self) -> &'a Memory {
|
||||
self.memory
|
||||
}
|
||||
|
||||
/// Reads the location pointed to by this `WasmRef`.
|
||||
#[inline]
|
||||
pub fn read(self) -> Result<T, MemoryAccessError> {
|
||||
let mut out = MaybeUninit::uninit();
|
||||
let buf =
|
||||
unsafe { slice::from_raw_parts_mut(out.as_mut_ptr() as *mut u8, mem::size_of::<T>()) };
|
||||
self.memory.read(self.offset, buf)?;
|
||||
self.buffer.read(self.offset, buf)?;
|
||||
Ok(unsafe { out.assume_init() })
|
||||
}
|
||||
|
||||
@@ -124,7 +121,7 @@ impl<'a, T: ValueType> WasmRef<'a, T> {
|
||||
};
|
||||
val.zero_padding_bytes(data);
|
||||
let data = unsafe { slice::from_raw_parts(data.as_ptr() as *const _, data.len()) };
|
||||
self.memory.write(self.offset, data)
|
||||
self.buffer.write(self.offset, data)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -151,7 +148,7 @@ impl<'a, T: ValueType> fmt::Debug for WasmRef<'a, T> {
|
||||
/// thread.
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct WasmSlice<'a, T: ValueType> {
|
||||
memory: &'a Memory,
|
||||
buffer: MemoryBuffer<'a>,
|
||||
offset: u64,
|
||||
len: u64,
|
||||
marker: PhantomData<*mut T>,
|
||||
@@ -163,7 +160,12 @@ impl<'a, T: ValueType> WasmSlice<'a, T> {
|
||||
///
|
||||
/// Returns a `MemoryAccessError` if the slice length overflows.
|
||||
#[inline]
|
||||
pub fn new(memory: &'a Memory, offset: u64, len: u64) -> Result<Self, MemoryAccessError> {
|
||||
pub fn new(
|
||||
ctx: &'a impl AsContextRef,
|
||||
memory: &'a Memory,
|
||||
offset: u64,
|
||||
len: u64,
|
||||
) -> Result<Self, MemoryAccessError> {
|
||||
let total_len = len
|
||||
.checked_mul(mem::size_of::<T>() as u64)
|
||||
.ok_or(MemoryAccessError::Overflow)?;
|
||||
@@ -171,7 +173,7 @@ impl<'a, T: ValueType> WasmSlice<'a, T> {
|
||||
.checked_add(total_len)
|
||||
.ok_or(MemoryAccessError::Overflow)?;
|
||||
Ok(Self {
|
||||
memory,
|
||||
buffer: memory.buffer(ctx.as_context_ref()),
|
||||
offset,
|
||||
len,
|
||||
marker: PhantomData,
|
||||
@@ -208,12 +210,6 @@ impl<'a, T: ValueType> WasmSlice<'a, T> {
|
||||
self.len == 0
|
||||
}
|
||||
|
||||
/// Get a reference to the Wasm memory backing this reference.
|
||||
#[inline]
|
||||
pub fn memory(self) -> &'a Memory {
|
||||
self.memory
|
||||
}
|
||||
|
||||
/// Get a `WasmRef` to an element in the slice.
|
||||
#[inline]
|
||||
pub fn index(self, idx: u64) -> WasmRef<'a, T> {
|
||||
@@ -222,7 +218,7 @@ impl<'a, T: ValueType> WasmSlice<'a, T> {
|
||||
}
|
||||
let offset = self.offset + idx * mem::size_of::<T>() as u64;
|
||||
WasmRef {
|
||||
memory: self.memory,
|
||||
buffer: self.buffer,
|
||||
offset,
|
||||
marker: PhantomData,
|
||||
}
|
||||
@@ -236,7 +232,7 @@ impl<'a, T: ValueType> WasmSlice<'a, T> {
|
||||
}
|
||||
let offset = self.offset + range.start * mem::size_of::<T>() as u64;
|
||||
Self {
|
||||
memory: self.memory,
|
||||
buffer: self.buffer,
|
||||
offset,
|
||||
len: range.end - range.start,
|
||||
marker: PhantomData,
|
||||
@@ -277,7 +273,7 @@ impl<'a, T: ValueType> WasmSlice<'a, T> {
|
||||
buf.len() * mem::size_of::<T>(),
|
||||
)
|
||||
};
|
||||
self.memory.read_uninit(self.offset, bytes)?;
|
||||
self.buffer.read_uninit(self.offset, bytes)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -302,7 +298,7 @@ impl<'a, T: ValueType> WasmSlice<'a, T> {
|
||||
buf.len() * mem::size_of::<T>(),
|
||||
)
|
||||
};
|
||||
self.memory.read_uninit(self.offset, bytes)?;
|
||||
self.buffer.read_uninit(self.offset, bytes)?;
|
||||
Ok(unsafe { slice::from_raw_parts_mut(buf.as_mut_ptr() as *mut T, buf.len()) })
|
||||
}
|
||||
|
||||
@@ -319,7 +315,7 @@ impl<'a, T: ValueType> WasmSlice<'a, T> {
|
||||
let bytes = unsafe {
|
||||
slice::from_raw_parts(data.as_ptr() as *const u8, data.len() * mem::size_of::<T>())
|
||||
};
|
||||
self.memory.write(self.offset, bytes)
|
||||
self.buffer.write(self.offset, bytes)
|
||||
}
|
||||
|
||||
/// Reads this `WasmSlice` into a `Vec`.
|
||||
@@ -333,7 +329,7 @@ impl<'a, T: ValueType> WasmSlice<'a, T> {
|
||||
len * mem::size_of::<T>(),
|
||||
)
|
||||
};
|
||||
self.memory.read_uninit(self.offset, bytes)?;
|
||||
self.buffer.read_uninit(self.offset, bytes)?;
|
||||
unsafe {
|
||||
vec.set_len(len);
|
||||
}
|
||||
|
||||
@@ -1,33 +1,21 @@
|
||||
mod env;
|
||||
mod context;
|
||||
mod exports;
|
||||
mod extern_ref;
|
||||
mod externals;
|
||||
mod imports;
|
||||
mod instance;
|
||||
mod mem_access;
|
||||
mod module;
|
||||
mod native;
|
||||
mod native_type;
|
||||
mod ptr;
|
||||
mod store;
|
||||
mod tunables;
|
||||
mod types;
|
||||
mod value;
|
||||
|
||||
/// Implement [`WasmerEnv`] for your type with `#[derive(WasmerEnv)]`.
|
||||
///
|
||||
/// See the [`WasmerEnv`] trait for more information.
|
||||
pub use wasmer_derive::WasmerEnv;
|
||||
|
||||
#[doc(hidden)]
|
||||
pub mod internals {
|
||||
//! We use the internals module for exporting types that are only
|
||||
//! intended to use in internal crates such as the compatibility crate
|
||||
//! `wasmer-vm`. Please don't use any of this types directly, as
|
||||
//! they might change frequently or be removed in the future.
|
||||
|
||||
pub use crate::sys::externals::{WithEnv, WithoutEnv};
|
||||
}
|
||||
|
||||
pub use crate::sys::env::{HostEnvInitError, LazyInit, WasmerEnv};
|
||||
pub use crate::sys::context::{AsContextMut, AsContextRef, Context, ContextMut, ContextRef};
|
||||
pub use crate::sys::exports::{ExportError, Exportable, Exports, ExportsIterator};
|
||||
pub use crate::sys::extern_ref::ExternRef;
|
||||
pub use crate::sys::externals::{
|
||||
Extern, FromToNativeWasmType, Function, Global, HostFunction, Memory, Table, WasmTypeList,
|
||||
};
|
||||
@@ -36,27 +24,27 @@ pub use crate::sys::instance::{Instance, InstantiationError};
|
||||
pub use crate::sys::mem_access::{MemoryAccessError, WasmRef, WasmSlice, WasmSliceIter};
|
||||
pub use crate::sys::module::Module;
|
||||
pub use crate::sys::native::TypedFunction;
|
||||
pub use crate::sys::native_type::NativeWasmTypeInto;
|
||||
|
||||
pub use crate::sys::ptr::{Memory32, Memory64, MemorySize, WasmPtr, WasmPtr64};
|
||||
pub use crate::sys::store::{Store, StoreObject};
|
||||
pub use crate::sys::store::Store;
|
||||
pub use crate::sys::tunables::BaseTunables;
|
||||
pub use crate::sys::types::{
|
||||
ExportType, ExternType, FunctionType, GlobalType, ImportType, MemoryType, Mutability,
|
||||
TableType, Val, ValType,
|
||||
};
|
||||
pub use crate::sys::types::{Val as Value, ValType as Type};
|
||||
pub use crate::sys::value::Value;
|
||||
pub use target_lexicon::{Architecture, CallingConvention, OperatingSystem, Triple, HOST};
|
||||
#[cfg(feature = "compiler")]
|
||||
pub use wasmer_compiler::{
|
||||
wasmparser, CompilerConfig, FunctionMiddleware, MiddlewareReaderState, ModuleMiddleware,
|
||||
};
|
||||
pub use wasmer_compiler::{
|
||||
CpuFeature, Engine, Export, Features, FrameInfo, LinkError, RuntimeError, Target, Tunables,
|
||||
CpuFeature, Engine, Features, FrameInfo, LinkError, RuntimeError, Target, Tunables,
|
||||
};
|
||||
pub use wasmer_derive::ValueType;
|
||||
pub use wasmer_types::is_wasm;
|
||||
#[cfg(feature = "experimental-reference-types-extern-ref")]
|
||||
pub use wasmer_types::ExternRef;
|
||||
pub use wasmer_types::{
|
||||
ExportType, ExternType, FunctionType, GlobalType, ImportType, MemoryType, Mutability,
|
||||
TableType, Type,
|
||||
};
|
||||
|
||||
pub use wasmer_types::{
|
||||
Bytes, CompileError, DeserializeError, ExportIndex, GlobalInit, LocalFunctionIndex,
|
||||
MiddlewareError, Pages, ParseCpuFeatureError, SerializeError, ValueType, WasmError, WasmResult,
|
||||
@@ -69,7 +57,7 @@ pub mod vm {
|
||||
//! The `vm` module re-exports wasmer-vm types.
|
||||
|
||||
pub use wasmer_vm::{
|
||||
Memory, MemoryError, MemoryStyle, Table, TableStyle, VMExtern, VMMemoryDefinition,
|
||||
MemoryError, MemoryStyle, TableStyle, VMExtern, VMMemory, VMMemoryDefinition, VMTable,
|
||||
VMTableDefinition,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
use crate::sys::exports::Exportable;
|
||||
use crate::sys::store::Store;
|
||||
use crate::sys::types::{ExportType, ImportType};
|
||||
use crate::sys::InstantiationError;
|
||||
use std::fmt;
|
||||
use std::io;
|
||||
@@ -13,8 +11,11 @@ use wasmer_types::WasmError;
|
||||
use wasmer_types::{
|
||||
CompileError, DeserializeError, ExportsIterator, ImportsIterator, ModuleInfo, SerializeError,
|
||||
};
|
||||
use wasmer_types::{ExportType, ImportType};
|
||||
use wasmer_vm::InstanceHandle;
|
||||
|
||||
use super::context::AsContextMut;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum IoCompileError {
|
||||
/// An IO error
|
||||
@@ -277,16 +278,24 @@ impl Module {
|
||||
|
||||
pub(crate) fn instantiate(
|
||||
&self,
|
||||
mut ctx: impl AsContextMut,
|
||||
imports: &[crate::Extern],
|
||||
) -> Result<InstanceHandle, InstantiationError> {
|
||||
// Ensure all imports come from the same context.
|
||||
for import in imports {
|
||||
if !import.is_from_context(ctx.as_context_ref()) {
|
||||
return Err(InstantiationError::BadContext);
|
||||
}
|
||||
}
|
||||
|
||||
unsafe {
|
||||
let instance_handle = self.artifact.instantiate(
|
||||
let mut instance_handle = self.artifact.instantiate(
|
||||
self.store.tunables(),
|
||||
&imports
|
||||
.iter()
|
||||
.map(crate::Extern::to_export)
|
||||
.map(crate::Extern::to_vm_extern)
|
||||
.collect::<Vec<_>>(),
|
||||
Box::new(self.clone()),
|
||||
ctx.as_context_mut().objects_mut(),
|
||||
)?;
|
||||
|
||||
// After the instance handle is created, we need to initialize
|
||||
@@ -295,7 +304,7 @@ impl Module {
|
||||
// as some of the Instance elements may have placed in other
|
||||
// instance tables.
|
||||
self.artifact
|
||||
.finish_instantiation(&self.store, &instance_handle)?;
|
||||
.finish_instantiation(&self.store, &mut instance_handle)?;
|
||||
|
||||
Ok(instance_handle)
|
||||
}
|
||||
|
||||
@@ -9,19 +9,16 @@
|
||||
//! ```
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use crate::sys::externals::function::{DynamicFunction, VMDynamicFunction};
|
||||
use crate::sys::{FromToNativeWasmType, Function, RuntimeError, Store, WasmTypeList};
|
||||
use std::panic::{catch_unwind, AssertUnwindSafe};
|
||||
use wasmer_compiler::ExportFunction;
|
||||
use wasmer_types::NativeWasmType;
|
||||
use wasmer_vm::{VMDynamicFunctionContext, VMFunctionBody, VMFunctionEnvironment, VMFunctionKind};
|
||||
use crate::sys::{
|
||||
AsContextMut, FromToNativeWasmType, Function, NativeWasmTypeInto, RuntimeError, WasmTypeList,
|
||||
};
|
||||
use wasmer_types::RawValue;
|
||||
|
||||
/// A WebAssembly function that can be called natively
|
||||
/// (using the Native ABI).
|
||||
pub struct TypedFunction<Args = (), Rets = ()> {
|
||||
store: Store,
|
||||
exported: ExportFunction,
|
||||
_phantom: PhantomData<(Args, Rets)>,
|
||||
pub struct TypedFunction<Args, Rets> {
|
||||
func: Function,
|
||||
_phantom: PhantomData<fn(Args) -> Rets>,
|
||||
}
|
||||
|
||||
unsafe impl<Args, Rets> Send for TypedFunction<Args, Rets> {}
|
||||
@@ -31,95 +28,30 @@ where
|
||||
Args: WasmTypeList,
|
||||
Rets: WasmTypeList,
|
||||
{
|
||||
pub(crate) fn new(store: Store, exported: ExportFunction) -> Self {
|
||||
pub(crate) fn new(func: Function) -> Self {
|
||||
Self {
|
||||
store,
|
||||
exported,
|
||||
func,
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn is_host(&self) -> bool {
|
||||
self.exported.vm_function.instance_ref.is_none()
|
||||
}
|
||||
|
||||
pub(crate) fn vmctx(&self) -> VMFunctionEnvironment {
|
||||
self.exported.vm_function.vmctx
|
||||
}
|
||||
|
||||
pub(crate) fn address(&self) -> *const VMFunctionBody {
|
||||
self.exported.vm_function.address
|
||||
}
|
||||
|
||||
pub(crate) fn arg_kind(&self) -> VMFunctionKind {
|
||||
self.exported.vm_function.kind
|
||||
}
|
||||
|
||||
/// Get access to the backing VM value for this extern. This function is for
|
||||
/// tests it should not be called by users of the Wasmer API.
|
||||
///
|
||||
/// # Safety
|
||||
/// This function is unsafe to call outside of tests for the wasmer crate
|
||||
/// because there is no stability guarantee for the returned type and we may
|
||||
/// make breaking changes to it at any time or remove this method.
|
||||
#[doc(hidden)]
|
||||
pub unsafe fn get_vm_function(&self) -> &wasmer_vm::VMFunction {
|
||||
&self.exported.vm_function
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
impl<Args, Rets> From<&TypedFunction<Args, Rets>> for VMFunction
|
||||
where
|
||||
Args: WasmTypeList,
|
||||
Rets: WasmTypeList,
|
||||
{
|
||||
fn from(other: &TypedFunction<Args, Rets>) -> Self {
|
||||
let signature = FunctionType::new(Args::wasm_types(), Rets::wasm_types());
|
||||
Self {
|
||||
address: other.address,
|
||||
vmctx: other.vmctx,
|
||||
signature,
|
||||
kind: other.arg_kind,
|
||||
call_trampoline: None,
|
||||
instance_ref: None,
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
impl<Args: WasmTypeList, Rets: WasmTypeList> Clone for TypedFunction<Args, Rets> {
|
||||
fn clone(&self) -> Self {
|
||||
let mut exported = self.exported.clone();
|
||||
exported.vm_function.upgrade_instance_ref().unwrap();
|
||||
|
||||
Self {
|
||||
store: self.store.clone(),
|
||||
exported,
|
||||
func: self.func.clone(),
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<Args, Rets> From<&TypedFunction<Args, Rets>> for ExportFunction
|
||||
where
|
||||
Args: WasmTypeList,
|
||||
Rets: WasmTypeList,
|
||||
{
|
||||
fn from(other: &TypedFunction<Args, Rets>) -> Self {
|
||||
other.exported.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl<Args, Rets> From<TypedFunction<Args, Rets>> for Function
|
||||
where
|
||||
Args: WasmTypeList,
|
||||
Rets: WasmTypeList,
|
||||
{
|
||||
fn from(other: TypedFunction<Args, Rets>) -> Self {
|
||||
Self {
|
||||
store: other.store,
|
||||
exported: other.exported,
|
||||
}
|
||||
other.func
|
||||
}
|
||||
}
|
||||
|
||||
@@ -132,110 +64,66 @@ macro_rules! impl_native_traits {
|
||||
Rets: WasmTypeList,
|
||||
{
|
||||
/// Call the typed func and return results.
|
||||
#[allow(unused_mut)]
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn call(&self, $( $x: $x, )* ) -> Result<Rets, RuntimeError> {
|
||||
if !self.is_host() {
|
||||
// We assume the trampoline is always going to be present for
|
||||
// Wasm functions
|
||||
let trampoline = self.exported.vm_function.call_trampoline.expect("Call trampoline not found in wasm function");
|
||||
// TODO: when `const fn` related features mature more, we can declare a single array
|
||||
// of the correct size here.
|
||||
let mut params_list = [ $( $x.to_native().to_binary() ),* ];
|
||||
let mut rets_list_array = Rets::empty_array();
|
||||
let rets_list = rets_list_array.as_mut();
|
||||
let using_rets_array;
|
||||
let args_rets: &mut [i128] = if params_list.len() > rets_list.len() {
|
||||
using_rets_array = false;
|
||||
params_list.as_mut()
|
||||
} else {
|
||||
using_rets_array = true;
|
||||
for (i, &arg) in params_list.iter().enumerate() {
|
||||
rets_list[i] = arg;
|
||||
}
|
||||
rets_list.as_mut()
|
||||
};
|
||||
pub fn call(&self, mut ctx: impl AsContextMut, $( $x: $x, )* ) -> Result<Rets, RuntimeError> {
|
||||
let anyfunc = unsafe {
|
||||
*self.func
|
||||
.handle
|
||||
.get(ctx.as_context_ref().objects())
|
||||
.anyfunc
|
||||
.as_ptr()
|
||||
.as_ref()
|
||||
};
|
||||
// TODO: when `const fn` related features mature more, we can declare a single array
|
||||
// of the correct size here.
|
||||
let mut params_list = [ $( $x.to_native().into_raw(ctx.as_context_mut()) ),* ];
|
||||
let mut rets_list_array = Rets::empty_array();
|
||||
let rets_list: &mut [RawValue] = rets_list_array.as_mut();
|
||||
let using_rets_array;
|
||||
let args_rets: &mut [RawValue] = if params_list.len() > rets_list.len() {
|
||||
using_rets_array = false;
|
||||
params_list.as_mut()
|
||||
} else {
|
||||
using_rets_array = true;
|
||||
for (i, &arg) in params_list.iter().enumerate() {
|
||||
rets_list[i] = arg;
|
||||
}
|
||||
rets_list.as_mut()
|
||||
};
|
||||
unsafe {
|
||||
wasmer_vm::wasmer_call_trampoline(
|
||||
ctx.as_context_ref().store(),
|
||||
anyfunc.vmctx,
|
||||
anyfunc.call_trampoline,
|
||||
anyfunc.func_ptr,
|
||||
args_rets.as_mut_ptr() as *mut u8,
|
||||
)
|
||||
}?;
|
||||
let num_rets = rets_list.len();
|
||||
if !using_rets_array && num_rets > 0 {
|
||||
let src_pointer = params_list.as_ptr();
|
||||
let rets_list = &mut rets_list_array.as_mut()[0] as *mut RawValue;
|
||||
unsafe {
|
||||
wasmer_vm::wasmer_call_trampoline(
|
||||
&self.store,
|
||||
self.vmctx(),
|
||||
trampoline,
|
||||
self.address(),
|
||||
args_rets.as_mut_ptr() as *mut u8,
|
||||
)
|
||||
}?;
|
||||
let num_rets = rets_list.len();
|
||||
if !using_rets_array && num_rets > 0 {
|
||||
let src_pointer = params_list.as_ptr();
|
||||
let rets_list = &mut rets_list_array.as_mut()[0] as *mut i128;
|
||||
unsafe {
|
||||
// TODO: we can probably remove this copy by doing some clever `transmute`s.
|
||||
// we know it's not overlapping because `using_rets_array` is false
|
||||
std::ptr::copy_nonoverlapping(src_pointer,
|
||||
rets_list,
|
||||
num_rets);
|
||||
}
|
||||
}
|
||||
Ok(Rets::from_array(rets_list_array))
|
||||
// TODO: When the Host ABI and Wasm ABI are the same, we could do this instead:
|
||||
// but we can't currently detect whether that's safe.
|
||||
//
|
||||
// let results = unsafe {
|
||||
// wasmer_vm::catch_traps_with_result(self.vmctx, || {
|
||||
// let f = std::mem::transmute::<_, unsafe extern "C" fn( *mut VMContext, $( $x, )*) -> Rets::CStruct>(self.address());
|
||||
// // We always pass the vmctx
|
||||
// f( self.vmctx, $( $x, )* )
|
||||
// }).map_err(RuntimeError::from_trap)?
|
||||
// };
|
||||
// Ok(Rets::from_c_struct(results))
|
||||
|
||||
}
|
||||
else {
|
||||
match self.arg_kind() {
|
||||
VMFunctionKind::Static => {
|
||||
let results = catch_unwind(AssertUnwindSafe(|| unsafe {
|
||||
let f = std::mem::transmute::<_, unsafe extern "C" fn( VMFunctionEnvironment, $( $x, )*) -> Rets::CStruct>(self.address());
|
||||
// We always pass the vmctx
|
||||
f( self.vmctx(), $( $x, )* )
|
||||
})).map_err(|e| RuntimeError::new(format!("{:?}", e)))?;
|
||||
Ok(Rets::from_c_struct(results))
|
||||
},
|
||||
VMFunctionKind::Dynamic => {
|
||||
let params_list = [ $( $x.to_native().to_value() ),* ];
|
||||
let results = {
|
||||
type VMContextWithEnv = VMDynamicFunctionContext<DynamicFunction<std::ffi::c_void>>;
|
||||
unsafe {
|
||||
let ctx = self.vmctx().host_env as *mut VMContextWithEnv;
|
||||
(*ctx).ctx.call(¶ms_list)?
|
||||
}
|
||||
};
|
||||
let mut rets_list_array = Rets::empty_array();
|
||||
let mut_rets = rets_list_array.as_mut() as *mut [i128] as *mut i128;
|
||||
for (i, ret) in results.iter().enumerate() {
|
||||
unsafe {
|
||||
ret.write_value_to(mut_rets.add(i));
|
||||
}
|
||||
}
|
||||
Ok(Rets::from_array(rets_list_array))
|
||||
}
|
||||
// TODO: we can probably remove this copy by doing some clever `transmute`s.
|
||||
// we know it's not overlapping because `using_rets_array` is false
|
||||
std::ptr::copy_nonoverlapping(src_pointer,
|
||||
rets_list,
|
||||
num_rets);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#[allow(unused_parens)]
|
||||
impl<'a, $( $x, )* Rets> crate::sys::exports::ExportableWithGenerics<'a, ($( $x ),*), Rets> for TypedFunction<( $( $x ),* ), Rets>
|
||||
where
|
||||
$( $x: FromToNativeWasmType, )*
|
||||
Rets: WasmTypeList,
|
||||
{
|
||||
fn get_self_from_extern_with_generics(_extern: &crate::sys::externals::Extern) -> Result<Self, crate::sys::exports::ExportError> {
|
||||
use crate::sys::exports::Exportable;
|
||||
crate::Function::get_self_from_extern(_extern)?.native().map_err(|_| crate::sys::exports::ExportError::IncompatibleType)
|
||||
}
|
||||
|
||||
fn convert_to_weak_instance_ref(&mut self) {
|
||||
self.exported.vm_function.instance_ref.as_mut().map(|v| *v = v.downgrade());
|
||||
Ok(unsafe { Rets::from_array(ctx, rets_list_array) })
|
||||
// TODO: When the Host ABI and Wasm ABI are the same, we could do this instead:
|
||||
// but we can't currently detect whether that's safe.
|
||||
//
|
||||
// let results = unsafe {
|
||||
// wasmer_vm::catch_traps_with_result(self.vmctx, || {
|
||||
// let f = std::mem::transmute::<_, unsafe extern "C" fn( *mut VMContext, $( $x, )*) -> Rets::CStruct>(self.address());
|
||||
// // We always pass the vmctx
|
||||
// f( self.vmctx, $( $x, )* )
|
||||
// }).map_err(RuntimeError::from_trap)?
|
||||
// };
|
||||
// Ok(Rets::from_c_struct(results))
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
236
lib/api/src/sys/native_type.rs
Normal file
236
lib/api/src/sys/native_type.rs
Normal file
@@ -0,0 +1,236 @@
|
||||
//! This module permits to create native functions
|
||||
//! easily in Rust, thanks to its advanced typing system.
|
||||
|
||||
use wasmer_types::{NativeWasmType, RawValue, Type};
|
||||
use wasmer_vm::{VMExternRef, VMFuncRef};
|
||||
|
||||
use crate::{ExternRef, Function};
|
||||
|
||||
use super::context::AsContextMut;
|
||||
|
||||
/// `NativeWasmTypeInto` performs conversions from and into `NativeWasmType`
|
||||
/// types with a context.
|
||||
pub trait NativeWasmTypeInto: NativeWasmType + Sized {
|
||||
#[doc(hidden)]
|
||||
fn into_abi(self, ctx: impl AsContextMut) -> Self::Abi;
|
||||
|
||||
#[doc(hidden)]
|
||||
unsafe fn from_abi(ctx: impl AsContextMut, abi: Self::Abi) -> Self;
|
||||
|
||||
/// Convert self to raw value representation.
|
||||
fn into_raw(self, ctx: impl AsContextMut) -> RawValue;
|
||||
|
||||
/// Convert to self from raw value representation.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
unsafe fn from_raw(ctx: impl AsContextMut, raw: RawValue) -> Self;
|
||||
}
|
||||
|
||||
impl NativeWasmTypeInto for i32 {
|
||||
#[inline]
|
||||
unsafe fn from_abi(_ctx: impl AsContextMut, abi: Self::Abi) -> Self {
|
||||
abi
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn into_abi(self, _ctx: impl AsContextMut) -> Self::Abi {
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn into_raw(self, _ctx: impl AsContextMut) -> RawValue {
|
||||
RawValue { i32: self }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn from_raw(_ctx: impl AsContextMut, raw: RawValue) -> Self {
|
||||
raw.i32
|
||||
}
|
||||
}
|
||||
|
||||
impl NativeWasmTypeInto for i64 {
|
||||
#[inline]
|
||||
unsafe fn from_abi(_ctx: impl AsContextMut, abi: Self::Abi) -> Self {
|
||||
abi
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn into_abi(self, _ctx: impl AsContextMut) -> Self::Abi {
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn into_raw(self, _ctx: impl AsContextMut) -> RawValue {
|
||||
RawValue { i64: self }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn from_raw(_ctx: impl AsContextMut, raw: RawValue) -> Self {
|
||||
raw.i64
|
||||
}
|
||||
}
|
||||
|
||||
impl NativeWasmTypeInto for f32 {
|
||||
#[inline]
|
||||
unsafe fn from_abi(_ctx: impl AsContextMut, abi: Self::Abi) -> Self {
|
||||
abi
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn into_abi(self, _ctx: impl AsContextMut) -> Self::Abi {
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn into_raw(self, _ctx: impl AsContextMut) -> RawValue {
|
||||
RawValue { f32: self }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn from_raw(_ctx: impl AsContextMut, raw: RawValue) -> Self {
|
||||
raw.f32
|
||||
}
|
||||
}
|
||||
|
||||
impl NativeWasmTypeInto for f64 {
|
||||
#[inline]
|
||||
unsafe fn from_abi(_ctx: impl AsContextMut, abi: Self::Abi) -> Self {
|
||||
abi
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn into_abi(self, _ctx: impl AsContextMut) -> Self::Abi {
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn into_raw(self, _ctx: impl AsContextMut) -> RawValue {
|
||||
RawValue { f64: self }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn from_raw(_ctx: impl AsContextMut, raw: RawValue) -> Self {
|
||||
raw.f64
|
||||
}
|
||||
}
|
||||
|
||||
impl NativeWasmTypeInto for u128 {
|
||||
#[inline]
|
||||
unsafe fn from_abi(_ctx: impl AsContextMut, abi: Self::Abi) -> Self {
|
||||
abi
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn into_abi(self, _ctx: impl AsContextMut) -> Self::Abi {
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn into_raw(self, _ctx: impl AsContextMut) -> RawValue {
|
||||
RawValue { u128: self }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn from_raw(_ctx: impl AsContextMut, raw: RawValue) -> Self {
|
||||
raw.u128
|
||||
}
|
||||
}
|
||||
|
||||
impl NativeWasmType for ExternRef {
|
||||
const WASM_TYPE: Type = Type::ExternRef;
|
||||
type Abi = usize;
|
||||
}
|
||||
|
||||
impl NativeWasmTypeInto for Option<ExternRef> {
|
||||
#[inline]
|
||||
unsafe fn from_abi(ctx: impl AsContextMut, abi: Self::Abi) -> Self {
|
||||
VMExternRef::from_raw(RawValue { externref: abi })
|
||||
.map(|e| ExternRef::from_vm_externref(ctx, e))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn into_abi(self, _ctx: impl AsContextMut) -> Self::Abi {
|
||||
self.map_or(0, |e| unsafe { e.vm_externref().into_raw().externref })
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn into_raw(self, _ctx: impl AsContextMut) -> RawValue {
|
||||
self.map_or(RawValue { externref: 0 }, |e| e.vm_externref().into_raw())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn from_raw(ctx: impl AsContextMut, raw: RawValue) -> Self {
|
||||
VMExternRef::from_raw(raw).map(|e| ExternRef::from_vm_externref(ctx, e))
|
||||
}
|
||||
}
|
||||
|
||||
impl NativeWasmType for Function {
|
||||
const WASM_TYPE: Type = Type::FuncRef;
|
||||
type Abi = usize;
|
||||
}
|
||||
|
||||
impl NativeWasmTypeInto for Option<Function> {
|
||||
#[inline]
|
||||
unsafe fn from_abi(ctx: impl AsContextMut, abi: Self::Abi) -> Self {
|
||||
VMFuncRef::from_raw(RawValue { funcref: abi }).map(|f| Function::from_vm_funcref(ctx, f))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn into_abi(self, ctx: impl AsContextMut) -> Self::Abi {
|
||||
self.map_or(0, |f| unsafe { f.vm_funcref(ctx).into_raw().externref })
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn into_raw(self, ctx: impl AsContextMut) -> RawValue {
|
||||
self.map_or(RawValue { externref: 0 }, |e| e.vm_funcref(ctx).into_raw())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn from_raw(ctx: impl AsContextMut, raw: RawValue) -> Self {
|
||||
VMFuncRef::from_raw(raw).map(|f| Function::from_vm_funcref(ctx, f))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test_native_type {
|
||||
use super::*;
|
||||
use wasmer_types::Type;
|
||||
|
||||
#[test]
|
||||
fn test_wasm_types() {
|
||||
assert_eq!(i32::WASM_TYPE, Type::I32);
|
||||
assert_eq!(i64::WASM_TYPE, Type::I64);
|
||||
assert_eq!(f32::WASM_TYPE, Type::F32);
|
||||
assert_eq!(f64::WASM_TYPE, Type::F64);
|
||||
assert_eq!(u128::WASM_TYPE, Type::V128);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_roundtrip() {
|
||||
unsafe {
|
||||
assert_eq!(i32::from_raw(42i32.into_raw()), 42i32);
|
||||
assert_eq!(i64::from_raw(42i64.into_raw()), 42i64);
|
||||
assert_eq!(f32::from_raw(42f32.into_raw()), 42f32);
|
||||
assert_eq!(f64::from_raw(42f64.into_raw()), 42f64);
|
||||
assert_eq!(u128::from_raw(42u128.into_raw()), 42u128);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// pub trait IntegerAtomic
|
||||
// where
|
||||
// Self: Sized
|
||||
// {
|
||||
// type Primitive;
|
||||
|
||||
// fn add(&self, other: Self::Primitive) -> Self::Primitive;
|
||||
// fn sub(&self, other: Self::Primitive) -> Self::Primitive;
|
||||
// fn and(&self, other: Self::Primitive) -> Self::Primitive;
|
||||
// fn or(&self, other: Self::Primitive) -> Self::Primitive;
|
||||
// fn xor(&self, other: Self::Primitive) -> Self::Primitive;
|
||||
// fn load(&self) -> Self::Primitive;
|
||||
// fn store(&self, other: Self::Primitive) -> Self::Primitive;
|
||||
// fn compare_exchange(&self, expected: Self::Primitive, new: Self::Primitive) -> Self::Primitive;
|
||||
// fn swap(&self, other: Self::Primitive) -> Self::Primitive;
|
||||
// }
|
||||
@@ -1,9 +1,12 @@
|
||||
use crate::sys::{externals::Memory, FromToNativeWasmType};
|
||||
use crate::NativeWasmTypeInto;
|
||||
use crate::{MemoryAccessError, WasmRef, WasmSlice};
|
||||
use std::convert::TryFrom;
|
||||
use std::{fmt, marker::PhantomData, mem};
|
||||
use wasmer_types::ValueType;
|
||||
|
||||
use super::context::AsContextRef;
|
||||
|
||||
pub use wasmer_types::MemorySize;
|
||||
|
||||
pub use wasmer_types::Memory32;
|
||||
@@ -139,20 +142,25 @@ impl<T: ValueType, M: MemorySize> WasmPtr<T, M> {
|
||||
/// Creates a `WasmRef` from this `WasmPtr` which allows reading and
|
||||
/// mutating of the value being pointed to.
|
||||
#[inline]
|
||||
pub fn deref(self, memory: &Memory) -> WasmRef<'_, T> {
|
||||
WasmRef::new(memory, self.offset.into())
|
||||
pub fn deref<'a>(self, ctx: &'a impl AsContextRef, memory: &'a Memory) -> WasmRef<'a, T> {
|
||||
WasmRef::new(ctx, memory, self.offset.into())
|
||||
}
|
||||
|
||||
/// Reads the address pointed to by this `WasmPtr` in a memory.
|
||||
#[inline]
|
||||
pub fn read(self, memory: &Memory) -> Result<T, MemoryAccessError> {
|
||||
self.deref(memory).read()
|
||||
pub fn read(self, ctx: impl AsContextRef, memory: &Memory) -> Result<T, MemoryAccessError> {
|
||||
self.deref(&ctx, memory).read()
|
||||
}
|
||||
|
||||
/// Writes to the address pointed to by this `WasmPtr` in a memory.
|
||||
#[inline]
|
||||
pub fn write(self, memory: &Memory, val: T) -> Result<(), MemoryAccessError> {
|
||||
self.deref(memory).write(val)
|
||||
pub fn write(
|
||||
self,
|
||||
ctx: impl AsContextRef,
|
||||
memory: &Memory,
|
||||
val: T,
|
||||
) -> Result<(), MemoryAccessError> {
|
||||
self.deref(&ctx, memory).write(val)
|
||||
}
|
||||
|
||||
/// Creates a `WasmSlice` starting at this `WasmPtr` which allows reading
|
||||
@@ -161,12 +169,13 @@ impl<T: ValueType, M: MemorySize> WasmPtr<T, M> {
|
||||
/// Returns a `MemoryAccessError` if the slice length overflows a 64-bit
|
||||
/// address.
|
||||
#[inline]
|
||||
pub fn slice(
|
||||
pub fn slice<'a>(
|
||||
self,
|
||||
memory: &Memory,
|
||||
ctx: &'a impl AsContextRef,
|
||||
memory: &'a Memory,
|
||||
len: M::Offset,
|
||||
) -> Result<WasmSlice<'_, T>, MemoryAccessError> {
|
||||
WasmSlice::new(memory, self.offset.into(), len.into())
|
||||
) -> Result<WasmSlice<'a, T>, MemoryAccessError> {
|
||||
WasmSlice::new(ctx, memory, self.offset.into(), len.into())
|
||||
}
|
||||
|
||||
/// Reads a sequence of values from this `WasmPtr` until a value that
|
||||
@@ -176,13 +185,14 @@ impl<T: ValueType, M: MemorySize> WasmPtr<T, M> {
|
||||
#[inline]
|
||||
pub fn read_until(
|
||||
self,
|
||||
ctx: impl AsContextRef,
|
||||
memory: &Memory,
|
||||
mut end: impl FnMut(&T) -> bool,
|
||||
) -> Result<Vec<T>, MemoryAccessError> {
|
||||
let mut vec = Vec::new();
|
||||
for i in 0u64.. {
|
||||
let i = M::Offset::try_from(i).map_err(|_| MemoryAccessError::Overflow)?;
|
||||
let val = self.add_offset(i)?.deref(memory).read()?;
|
||||
let val = self.add_offset(i)?.deref(&ctx, memory).read()?;
|
||||
if end(&val) {
|
||||
break;
|
||||
}
|
||||
@@ -200,10 +210,11 @@ impl<M: MemorySize> WasmPtr<u8, M> {
|
||||
#[inline]
|
||||
pub fn read_utf8_string(
|
||||
self,
|
||||
ctx: impl AsContextRef,
|
||||
memory: &Memory,
|
||||
len: M::Offset,
|
||||
) -> Result<String, MemoryAccessError> {
|
||||
let vec = self.slice(memory, len)?.read_to_vec()?;
|
||||
let vec = self.slice(&ctx, memory, len)?.read_to_vec()?;
|
||||
Ok(String::from_utf8(vec)?)
|
||||
}
|
||||
|
||||
@@ -212,13 +223,20 @@ impl<M: MemorySize> WasmPtr<u8, M> {
|
||||
/// This method is safe to call even if the memory is being concurrently
|
||||
/// modified.
|
||||
#[inline]
|
||||
pub fn read_utf8_string_with_nul(self, memory: &Memory) -> Result<String, MemoryAccessError> {
|
||||
let vec = self.read_until(memory, |&byte| byte == 0)?;
|
||||
pub fn read_utf8_string_with_nul(
|
||||
self,
|
||||
ctx: impl AsContextRef,
|
||||
memory: &Memory,
|
||||
) -> Result<String, MemoryAccessError> {
|
||||
let vec = self.read_until(ctx, memory, |&byte| byte == 0)?;
|
||||
Ok(String::from_utf8(vec)?)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<T: ValueType, M: MemorySize> FromToNativeWasmType for WasmPtr<T, M> {
|
||||
unsafe impl<T: ValueType, M: MemorySize> FromToNativeWasmType for WasmPtr<T, M>
|
||||
where
|
||||
<M as wasmer_types::MemorySize>::Native: NativeWasmTypeInto,
|
||||
{
|
||||
type Native = M::Native;
|
||||
|
||||
fn to_native(self) -> Self::Native {
|
||||
|
||||
@@ -144,9 +144,3 @@ impl fmt::Debug for Store {
|
||||
f.debug_struct("Store").finish()
|
||||
}
|
||||
}
|
||||
|
||||
/// A trait represinting any object that lives in the `Store`.
|
||||
pub trait StoreObject {
|
||||
/// Return true if the object `Store` is the same as the provided `Store`.
|
||||
fn comes_from_same_store(&self, store: &Store) -> bool;
|
||||
}
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
use crate::sys::{MemoryType, Pages, TableType};
|
||||
use std::ptr::NonNull;
|
||||
use std::sync::Arc;
|
||||
use target_lexicon::PointerWidth;
|
||||
use wasmer_compiler::{Target, Tunables};
|
||||
use wasmer_vm::MemoryError;
|
||||
use wasmer_vm::{
|
||||
LinearMemory, LinearTable, Memory, MemoryStyle, Table, TableStyle, VMMemoryDefinition,
|
||||
VMTableDefinition,
|
||||
MemoryStyle, TableStyle, VMMemory, VMMemoryDefinition, VMTable, VMTableDefinition,
|
||||
};
|
||||
|
||||
/// Tunable parameters for WebAssembly compilation.
|
||||
@@ -96,8 +94,8 @@ impl Tunables for BaseTunables {
|
||||
&self,
|
||||
ty: &MemoryType,
|
||||
style: &MemoryStyle,
|
||||
) -> Result<Arc<dyn Memory>, MemoryError> {
|
||||
Ok(Arc::new(LinearMemory::new(ty, style)?))
|
||||
) -> Result<VMMemory, MemoryError> {
|
||||
VMMemory::new(ty, style)
|
||||
}
|
||||
|
||||
/// Create a memory owned by the VM given a [`MemoryType`] and a [`MemoryStyle`].
|
||||
@@ -110,21 +108,13 @@ impl Tunables for BaseTunables {
|
||||
ty: &MemoryType,
|
||||
style: &MemoryStyle,
|
||||
vm_definition_location: NonNull<VMMemoryDefinition>,
|
||||
) -> Result<Arc<dyn Memory>, MemoryError> {
|
||||
Ok(Arc::new(LinearMemory::from_definition(
|
||||
ty,
|
||||
style,
|
||||
vm_definition_location,
|
||||
)?))
|
||||
) -> Result<VMMemory, MemoryError> {
|
||||
VMMemory::from_definition(ty, style, vm_definition_location)
|
||||
}
|
||||
|
||||
/// Create a table owned by the host given a [`TableType`] and a [`TableStyle`].
|
||||
fn create_host_table(
|
||||
&self,
|
||||
ty: &TableType,
|
||||
style: &TableStyle,
|
||||
) -> Result<Arc<dyn Table>, String> {
|
||||
Ok(Arc::new(LinearTable::new(ty, style)?))
|
||||
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`].
|
||||
@@ -137,12 +127,8 @@ impl Tunables for BaseTunables {
|
||||
ty: &TableType,
|
||||
style: &TableStyle,
|
||||
vm_definition_location: NonNull<VMTableDefinition>,
|
||||
) -> Result<Arc<dyn Table>, String> {
|
||||
Ok(Arc::new(LinearTable::from_definition(
|
||||
ty,
|
||||
style,
|
||||
vm_definition_location,
|
||||
)?))
|
||||
) -> Result<VMTable, String> {
|
||||
VMTable::from_definition(ty, style, vm_definition_location)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,112 +0,0 @@
|
||||
use crate::sys::externals::Function;
|
||||
use crate::sys::store::{Store, StoreObject};
|
||||
use crate::sys::RuntimeError;
|
||||
use wasmer_types::Value;
|
||||
pub use wasmer_types::{
|
||||
ExportType, ExternType, FunctionType, GlobalType, ImportType, MemoryType, Mutability,
|
||||
TableType, Type as ValType,
|
||||
};
|
||||
use wasmer_vm::VMFuncRef;
|
||||
|
||||
/// WebAssembly computations manipulate values of basic value types:
|
||||
/// * Integers (32 or 64 bit width)
|
||||
/// * Floating-point (32 or 64 bit width)
|
||||
/// * Vectors (128 bits, with 32 or 64 bit lanes)
|
||||
///
|
||||
/// Spec: <https://webassembly.github.io/spec/core/exec/runtime.html#values>
|
||||
pub type Val = Value<Function>;
|
||||
|
||||
impl StoreObject for Val {
|
||||
fn comes_from_same_store(&self, store: &Store) -> bool {
|
||||
match self {
|
||||
Self::FuncRef(None) => true,
|
||||
Self::FuncRef(Some(f)) => Store::same(store, f.store()),
|
||||
// `ExternRef`s are not tied to specific stores
|
||||
Self::ExternRef(_) => true,
|
||||
Self::I32(_) | Self::I64(_) | Self::F32(_) | Self::F64(_) | Self::V128(_) => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Function> for Val {
|
||||
fn from(val: Function) -> Self {
|
||||
Self::FuncRef(Some(val))
|
||||
}
|
||||
}
|
||||
|
||||
/// It provides useful functions for converting back and forth
|
||||
/// from [`Val`] into `FuncRef`.
|
||||
pub trait ValFuncRef {
|
||||
fn into_vm_funcref(self, store: &Store) -> Result<VMFuncRef, RuntimeError>;
|
||||
|
||||
fn from_vm_funcref(item: VMFuncRef, store: &Store) -> Self;
|
||||
|
||||
fn into_table_reference(self, store: &Store) -> Result<wasmer_vm::TableElement, RuntimeError>;
|
||||
|
||||
fn from_table_reference(item: wasmer_vm::TableElement, store: &Store) -> Self;
|
||||
}
|
||||
|
||||
impl ValFuncRef for Val {
|
||||
fn into_vm_funcref(self, store: &Store) -> Result<VMFuncRef, RuntimeError> {
|
||||
if !self.comes_from_same_store(store) {
|
||||
return Err(RuntimeError::new("cross-`Store` values are not supported"));
|
||||
}
|
||||
Ok(match self {
|
||||
Self::FuncRef(None) => VMFuncRef::null(),
|
||||
Self::FuncRef(Some(f)) => f.vm_funcref(),
|
||||
_ => return Err(RuntimeError::new("val is not func ref")),
|
||||
})
|
||||
}
|
||||
|
||||
fn from_vm_funcref(func_ref: VMFuncRef, store: &Store) -> Self {
|
||||
if func_ref.is_null() {
|
||||
return Self::FuncRef(None);
|
||||
}
|
||||
let item: &wasmer_vm::VMCallerCheckedAnyfunc = unsafe {
|
||||
let anyfunc: *const wasmer_vm::VMCallerCheckedAnyfunc = *func_ref;
|
||||
&*anyfunc
|
||||
};
|
||||
let signature = store
|
||||
.engine()
|
||||
.lookup_signature(item.type_index)
|
||||
.expect("Signature not found in store");
|
||||
let export = wasmer_compiler::ExportFunction {
|
||||
// TODO:
|
||||
// figure out if we ever need a value here: need testing with complicated import patterns
|
||||
metadata: None,
|
||||
vm_function: wasmer_vm::VMFunction {
|
||||
address: item.func_ptr,
|
||||
signature,
|
||||
// TODO: review this comment (unclear if it's still correct):
|
||||
// All functions in tables are already Static (as dynamic functions
|
||||
// are converted to use the trampolines with static signatures).
|
||||
kind: wasmer_vm::VMFunctionKind::Static,
|
||||
vmctx: item.vmctx,
|
||||
call_trampoline: None,
|
||||
instance_ref: None,
|
||||
},
|
||||
};
|
||||
let f = Function::from_vm_export(store, export);
|
||||
Self::FuncRef(Some(f))
|
||||
}
|
||||
|
||||
fn into_table_reference(self, store: &Store) -> Result<wasmer_vm::TableElement, RuntimeError> {
|
||||
if !self.comes_from_same_store(store) {
|
||||
return Err(RuntimeError::new("cross-`Store` values are not supported"));
|
||||
}
|
||||
Ok(match self {
|
||||
// TODO(reftypes): review this clone
|
||||
Self::ExternRef(extern_ref) => wasmer_vm::TableElement::ExternRef(extern_ref),
|
||||
Self::FuncRef(None) => wasmer_vm::TableElement::FuncRef(VMFuncRef::null()),
|
||||
Self::FuncRef(Some(f)) => wasmer_vm::TableElement::FuncRef(f.vm_funcref()),
|
||||
_ => return Err(RuntimeError::new("val is not reference")),
|
||||
})
|
||||
}
|
||||
|
||||
fn from_table_reference(item: wasmer_vm::TableElement, store: &Store) -> Self {
|
||||
match item {
|
||||
wasmer_vm::TableElement::FuncRef(f) => Self::from_vm_funcref(f, store),
|
||||
wasmer_vm::TableElement::ExternRef(extern_ref) => Self::ExternRef(extern_ref),
|
||||
}
|
||||
}
|
||||
}
|
||||
463
lib/api/src/sys/value.rs
Normal file
463
lib/api/src/sys/value.rs
Normal file
@@ -0,0 +1,463 @@
|
||||
use std::convert::TryFrom;
|
||||
use std::fmt;
|
||||
use std::string::{String, ToString};
|
||||
|
||||
use wasmer_types::Type;
|
||||
use wasmer_vm::VMExternRef;
|
||||
use wasmer_vm::VMFuncRef;
|
||||
|
||||
use crate::ExternRef;
|
||||
use crate::Function;
|
||||
|
||||
use super::context::AsContextMut;
|
||||
use super::context::AsContextRef;
|
||||
|
||||
pub use wasmer_types::RawValue;
|
||||
|
||||
/// WebAssembly computations manipulate values of basic value types:
|
||||
/// * Integers (32 or 64 bit width)
|
||||
/// * Floating-point (32 or 64 bit width)
|
||||
/// * Vectors (128 bits, with 32 or 64 bit lanes)
|
||||
///
|
||||
/// Spec: <https://webassembly.github.io/spec/core/exec/runtime.html#values>
|
||||
#[derive(Clone)]
|
||||
pub enum Value {
|
||||
/// A 32-bit integer.
|
||||
///
|
||||
/// In Wasm integers are sign-agnostic, i.e. this can either be signed or unsigned.
|
||||
I32(i32),
|
||||
|
||||
/// A 64-bit integer.
|
||||
///
|
||||
/// In Wasm integers are sign-agnostic, i.e. this can either be signed or unsigned.
|
||||
I64(i64),
|
||||
|
||||
/// A 32-bit float.
|
||||
F32(f32),
|
||||
|
||||
/// A 64-bit float.
|
||||
F64(f64),
|
||||
|
||||
/// An `externref` value which can hold opaque data to the wasm instance itself.
|
||||
ExternRef(Option<ExternRef>),
|
||||
|
||||
/// A first-class reference to a WebAssembly function.
|
||||
FuncRef(Option<Function>),
|
||||
|
||||
/// A 128-bit number
|
||||
V128(u128),
|
||||
}
|
||||
|
||||
macro_rules! accessors {
|
||||
($bind:ident $(($variant:ident($ty:ty) $get:ident $unwrap:ident $cvt:expr))*) => ($(
|
||||
/// Attempt to access the underlying value of this `Value`, returning
|
||||
/// `None` if it is not the correct type.
|
||||
pub fn $get(&self) -> Option<$ty> {
|
||||
if let Self::$variant($bind) = self {
|
||||
Some($cvt)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the underlying value of this `Value`, panicking if it's the
|
||||
/// wrong type.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if `self` is not of the right type.
|
||||
pub fn $unwrap(&self) -> $ty {
|
||||
self.$get().expect(concat!("expected ", stringify!($ty)))
|
||||
}
|
||||
)*)
|
||||
}
|
||||
|
||||
impl Value {
|
||||
/// Returns a null `externref` value.
|
||||
pub fn null() -> Self {
|
||||
Self::ExternRef(None)
|
||||
}
|
||||
|
||||
/// Returns the corresponding [`Type`] for this `Value`.
|
||||
pub fn ty(&self) -> Type {
|
||||
match self {
|
||||
Self::I32(_) => Type::I32,
|
||||
Self::I64(_) => Type::I64,
|
||||
Self::F32(_) => Type::F32,
|
||||
Self::F64(_) => Type::F64,
|
||||
Self::ExternRef(_) => Type::ExternRef,
|
||||
Self::FuncRef(_) => Type::FuncRef,
|
||||
Self::V128(_) => Type::V128,
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts the `Value` into a `RawValue`.
|
||||
pub fn as_raw(&self, ctx: impl AsContextRef) -> RawValue {
|
||||
match *self {
|
||||
Self::I32(i32) => RawValue { i32 },
|
||||
Self::I64(i64) => RawValue { i64 },
|
||||
Self::F32(f32) => RawValue { f32 },
|
||||
Self::F64(f64) => RawValue { f64 },
|
||||
Self::V128(u128) => RawValue { u128 },
|
||||
Self::FuncRef(Some(ref f)) => f.vm_funcref(ctx).into_raw(),
|
||||
|
||||
Self::FuncRef(None) => RawValue { funcref: 0 },
|
||||
Self::ExternRef(Some(ref e)) => e.vm_externref().into_raw(),
|
||||
Self::ExternRef(None) => RawValue { externref: 0 },
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts a `RawValue` to a `Value`.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
pub unsafe fn from_raw(ctx: impl AsContextMut, ty: Type, raw: RawValue) -> Self {
|
||||
match ty {
|
||||
Type::I32 => Self::I32(raw.i32),
|
||||
Type::I64 => Self::I64(raw.i64),
|
||||
Type::F32 => Self::F32(raw.f32),
|
||||
Type::F64 => Self::F64(raw.f64),
|
||||
Type::V128 => Self::V128(raw.u128),
|
||||
Type::FuncRef => {
|
||||
Self::FuncRef(VMFuncRef::from_raw(raw).map(|f| Function::from_vm_funcref(ctx, f)))
|
||||
}
|
||||
Type::ExternRef => Self::ExternRef(
|
||||
VMExternRef::from_raw(raw).map(|e| ExternRef::from_vm_externref(ctx, e)),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks whether a value can be used with the given context.
|
||||
///
|
||||
/// Primitive (`i32`, `i64`, etc) and null funcref/externref values are not
|
||||
/// tied to a context and can be freely shared between contexts.
|
||||
///
|
||||
/// Externref and funcref values are tied to a context and can only be used
|
||||
/// with that context.
|
||||
pub fn is_from_context(&self, ctx: impl AsContextRef) -> bool {
|
||||
match self {
|
||||
Self::I32(_)
|
||||
| Self::I64(_)
|
||||
| Self::F32(_)
|
||||
| Self::F64(_)
|
||||
| Self::V128(_)
|
||||
| Self::ExternRef(None)
|
||||
| Self::FuncRef(None) => true,
|
||||
Self::ExternRef(Some(e)) => e.is_from_context(ctx),
|
||||
Self::FuncRef(Some(f)) => f.is_from_context(ctx),
|
||||
}
|
||||
}
|
||||
|
||||
accessors! {
|
||||
e
|
||||
(I32(i32) i32 unwrap_i32 *e)
|
||||
(I64(i64) i64 unwrap_i64 *e)
|
||||
(F32(f32) f32 unwrap_f32 *e)
|
||||
(F64(f64) f64 unwrap_f64 *e)
|
||||
(ExternRef(&Option<ExternRef>) externref unwrap_externref e)
|
||||
(FuncRef(&Option<Function>) funcref unwrap_funcref e)
|
||||
(V128(u128) v128 unwrap_v128 *e)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Value {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Self::I32(v) => write!(f, "I32({:?})", v),
|
||||
Self::I64(v) => write!(f, "I64({:?})", v),
|
||||
Self::F32(v) => write!(f, "F32({:?})", v),
|
||||
Self::F64(v) => write!(f, "F64({:?})", v),
|
||||
Self::ExternRef(None) => write!(f, "Null ExternRef"),
|
||||
Self::ExternRef(Some(v)) => write!(f, "ExternRef({:?})", v),
|
||||
Self::FuncRef(None) => write!(f, "Null FuncRef"),
|
||||
Self::FuncRef(Some(v)) => write!(f, "FuncRef({:?})", v),
|
||||
Self::V128(v) => write!(f, "V128({:?})", v),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToString for Value {
|
||||
fn to_string(&self) -> String {
|
||||
match self {
|
||||
Self::I32(v) => v.to_string(),
|
||||
Self::I64(v) => v.to_string(),
|
||||
Self::F32(v) => v.to_string(),
|
||||
Self::F64(v) => v.to_string(),
|
||||
Self::ExternRef(_) => "externref".to_string(),
|
||||
Self::FuncRef(_) => "funcref".to_string(),
|
||||
Self::V128(v) => v.to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<i32> for Value {
|
||||
fn from(val: i32) -> Self {
|
||||
Self::I32(val)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u32> for Value {
|
||||
fn from(val: u32) -> Self {
|
||||
// In Wasm integers are sign-agnostic, so i32 is basically a 4 byte storage we can use for signed or unsigned 32-bit integers.
|
||||
Self::I32(val as i32)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<i64> for Value {
|
||||
fn from(val: i64) -> Self {
|
||||
Self::I64(val)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u64> for Value {
|
||||
fn from(val: u64) -> Self {
|
||||
// In Wasm integers are sign-agnostic, so i64 is basically an 8 byte storage we can use for signed or unsigned 64-bit integers.
|
||||
Self::I64(val as i64)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<f32> for Value {
|
||||
fn from(val: f32) -> Self {
|
||||
Self::F32(val)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<f64> for Value {
|
||||
fn from(val: f64) -> Self {
|
||||
Self::F64(val)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Function> for Value {
|
||||
fn from(val: Function) -> Self {
|
||||
Self::FuncRef(Some(val))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Option<Function>> for Value {
|
||||
fn from(val: Option<Function>) -> Self {
|
||||
Self::FuncRef(val)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ExternRef> for Value {
|
||||
fn from(val: ExternRef) -> Self {
|
||||
Self::ExternRef(Some(val))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Option<ExternRef>> for Value {
|
||||
fn from(val: Option<ExternRef>) -> Self {
|
||||
Self::ExternRef(val)
|
||||
}
|
||||
}
|
||||
|
||||
const NOT_I32: &str = "Value is not of Wasm type i32";
|
||||
const NOT_I64: &str = "Value is not of Wasm type i64";
|
||||
const NOT_F32: &str = "Value is not of Wasm type f32";
|
||||
const NOT_F64: &str = "Value is not of Wasm type f64";
|
||||
const NOT_FUNCREF: &str = "Value is not of Wasm type funcref";
|
||||
const NOT_EXTERNREF: &str = "Value is not of Wasm type externref";
|
||||
|
||||
impl TryFrom<Value> for i32 {
|
||||
type Error = &'static str;
|
||||
|
||||
fn try_from(value: Value) -> Result<Self, Self::Error> {
|
||||
value.i32().ok_or(NOT_I32)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Value> for u32 {
|
||||
type Error = &'static str;
|
||||
|
||||
fn try_from(value: Value) -> Result<Self, Self::Error> {
|
||||
value.i32().ok_or(NOT_I32).map(|int| int as Self)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Value> for i64 {
|
||||
type Error = &'static str;
|
||||
|
||||
fn try_from(value: Value) -> Result<Self, Self::Error> {
|
||||
value.i64().ok_or(NOT_I64)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Value> for u64 {
|
||||
type Error = &'static str;
|
||||
|
||||
fn try_from(value: Value) -> Result<Self, Self::Error> {
|
||||
value.i64().ok_or(NOT_I64).map(|int| int as Self)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Value> for f32 {
|
||||
type Error = &'static str;
|
||||
|
||||
fn try_from(value: Value) -> Result<Self, Self::Error> {
|
||||
value.f32().ok_or(NOT_F32)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Value> for f64 {
|
||||
type Error = &'static str;
|
||||
|
||||
fn try_from(value: Value) -> Result<Self, Self::Error> {
|
||||
value.f64().ok_or(NOT_F64)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Value> for Option<Function> {
|
||||
type Error = &'static str;
|
||||
|
||||
fn try_from(value: Value) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
Value::FuncRef(f) => Ok(f),
|
||||
_ => Err(NOT_FUNCREF),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Value> for Option<ExternRef> {
|
||||
type Error = &'static str;
|
||||
|
||||
fn try_from(value: Value) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
Value::ExternRef(e) => Ok(e),
|
||||
_ => Err(NOT_EXTERNREF),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_value_i32_from_u32() {
|
||||
let bytes = [0x00, 0x00, 0x00, 0x00];
|
||||
let v = Value::<()>::from(u32::from_be_bytes(bytes));
|
||||
assert_eq!(v, Value::I32(i32::from_be_bytes(bytes)));
|
||||
|
||||
let bytes = [0x00, 0x00, 0x00, 0x01];
|
||||
let v = Value::<()>::from(u32::from_be_bytes(bytes));
|
||||
assert_eq!(v, Value::I32(i32::from_be_bytes(bytes)));
|
||||
|
||||
let bytes = [0xAA, 0xBB, 0xCC, 0xDD];
|
||||
let v = Value::<()>::from(u32::from_be_bytes(bytes));
|
||||
assert_eq!(v, Value::I32(i32::from_be_bytes(bytes)));
|
||||
|
||||
let bytes = [0xFF, 0xFF, 0xFF, 0xFF];
|
||||
let v = Value::<()>::from(u32::from_be_bytes(bytes));
|
||||
assert_eq!(v, Value::I32(i32::from_be_bytes(bytes)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_value_i64_from_u64() {
|
||||
let bytes = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
|
||||
let v = Value::<()>::from(u64::from_be_bytes(bytes));
|
||||
assert_eq!(v, Value::I64(i64::from_be_bytes(bytes)));
|
||||
|
||||
let bytes = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01];
|
||||
let v = Value::<()>::from(u64::from_be_bytes(bytes));
|
||||
assert_eq!(v, Value::I64(i64::from_be_bytes(bytes)));
|
||||
|
||||
let bytes = [0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x00, 0x11];
|
||||
let v = Value::<()>::from(u64::from_be_bytes(bytes));
|
||||
assert_eq!(v, Value::I64(i64::from_be_bytes(bytes)));
|
||||
|
||||
let bytes = [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF];
|
||||
let v = Value::<()>::from(u64::from_be_bytes(bytes));
|
||||
assert_eq!(v, Value::I64(i64::from_be_bytes(bytes)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn convert_value_to_i32() {
|
||||
let value = Value::<()>::I32(5678);
|
||||
let result = i32::try_from(value);
|
||||
assert_eq!(result.unwrap(), 5678);
|
||||
|
||||
let value = Value::<()>::from(u32::MAX);
|
||||
let result = i32::try_from(value);
|
||||
assert_eq!(result.unwrap(), -1);
|
||||
|
||||
let value = Value::<()>::V128(42);
|
||||
let result = i32::try_from(value);
|
||||
assert_eq!(result.unwrap_err(), "Value is not of Wasm type i32");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn convert_value_to_u32() {
|
||||
let value = Value::<()>::from(u32::MAX);
|
||||
let result = u32::try_from(value);
|
||||
assert_eq!(result.unwrap(), u32::MAX);
|
||||
|
||||
let value = Value::<()>::I32(-1);
|
||||
let result = u32::try_from(value);
|
||||
assert_eq!(result.unwrap(), u32::MAX);
|
||||
|
||||
let value = Value::<()>::V128(42);
|
||||
let result = u32::try_from(value);
|
||||
assert_eq!(result.unwrap_err(), "Value is not of Wasm type i32");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn convert_value_to_i64() {
|
||||
let value = Value::<()>::I64(5678);
|
||||
let result = i64::try_from(value);
|
||||
assert_eq!(result.unwrap(), 5678);
|
||||
|
||||
let value = Value::<()>::from(u64::MAX);
|
||||
let result = i64::try_from(value);
|
||||
assert_eq!(result.unwrap(), -1);
|
||||
|
||||
let value = Value::<()>::V128(42);
|
||||
let result = i64::try_from(value);
|
||||
assert_eq!(result.unwrap_err(), "Value is not of Wasm type i64");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn convert_value_to_u64() {
|
||||
let value = Value::<()>::from(u64::MAX);
|
||||
let result = u64::try_from(value);
|
||||
assert_eq!(result.unwrap(), u64::MAX);
|
||||
|
||||
let value = Value::<()>::I64(-1);
|
||||
let result = u64::try_from(value);
|
||||
assert_eq!(result.unwrap(), u64::MAX);
|
||||
|
||||
let value = Value::<()>::V128(42);
|
||||
let result = u64::try_from(value);
|
||||
assert_eq!(result.unwrap_err(), "Value is not of Wasm type i64");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn convert_value_to_f32() {
|
||||
let value = Value::<()>::F32(1.234);
|
||||
let result = f32::try_from(value);
|
||||
assert_eq!(result.unwrap(), 1.234);
|
||||
|
||||
let value = Value::<()>::V128(42);
|
||||
let result = f32::try_from(value);
|
||||
assert_eq!(result.unwrap_err(), "Value is not of Wasm type f32");
|
||||
|
||||
let value = Value::<()>::F64(1.234);
|
||||
let result = f32::try_from(value);
|
||||
assert_eq!(result.unwrap_err(), "Value is not of Wasm type f32");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn convert_value_to_f64() {
|
||||
let value = Value::<()>::F64(1.234);
|
||||
let result = f64::try_from(value);
|
||||
assert_eq!(result.unwrap(), 1.234);
|
||||
|
||||
let value = Value::<()>::V128(42);
|
||||
let result = f64::try_from(value);
|
||||
assert_eq!(result.unwrap_err(), "Value is not of Wasm type f64");
|
||||
|
||||
let value = Value::<()>::F32(1.234);
|
||||
let result = f64::try_from(value);
|
||||
assert_eq!(result.unwrap_err(), "Value is not of Wasm type f64");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user