mirror of
https://github.com/mii443/wasmer.git
synced 2025-12-07 13:18:20 +00:00
Port JS API to new Context API
This commit is contained in:
449
lib/api/src/js/context.rs
Normal file
449
lib/api/src/js/context.rs
Normal file
@@ -0,0 +1,449 @@
|
||||
#![allow(dead_code)]
|
||||
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`] and [`Function::new_native`] 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_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
|
||||
}
|
||||
|
||||
/// Returns the raw pointer of the context
|
||||
pub(crate) fn as_raw(&self) -> *mut ContextInner<T> {
|
||||
self.inner as *const ContextInner<T> as *mut ContextInner<T>
|
||||
}
|
||||
|
||||
/// Constructs the context from the raw pointer
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
pub use objects::*;
|
||||
mod objects {
|
||||
use crate::js::export::{VMFunction, VMGlobal, VMMemory, VMTable};
|
||||
use std::{
|
||||
cell::UnsafeCell,
|
||||
fmt,
|
||||
marker::PhantomData,
|
||||
num::{NonZeroU64, NonZeroUsize},
|
||||
ptr::NonNull,
|
||||
sync::atomic::{AtomicU64, Ordering},
|
||||
};
|
||||
|
||||
/// Unique ID to identify a context.
|
||||
///
|
||||
/// Every handle to an object managed by a context also contains the ID of the
|
||||
/// context. This is used to check that a handle is always used with the
|
||||
/// correct context.
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
pub struct ContextId(NonZeroU64);
|
||||
|
||||
impl Default for ContextId {
|
||||
// Allocates a unique ID for a new context.
|
||||
fn default() -> Self {
|
||||
// No overflow checking is needed here: overflowing this would take
|
||||
// thousands of years.
|
||||
static NEXT_ID: AtomicU64 = AtomicU64::new(1);
|
||||
Self(NonZeroU64::new(NEXT_ID.fetch_add(1, Ordering::Relaxed)).unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
/// Trait to represent an object managed by a context. This is implemented on
|
||||
/// the VM types managed by the context.
|
||||
pub trait ContextObject: Sized {
|
||||
fn list(ctx: &ContextObjects) -> &Vec<Self>;
|
||||
fn list_mut(ctx: &mut ContextObjects) -> &mut Vec<Self>;
|
||||
}
|
||||
|
||||
macro_rules! impl_context_object {
|
||||
($($field:ident => $ty:ty,)*) => {
|
||||
$(
|
||||
impl ContextObject for $ty {
|
||||
fn list(ctx: &ContextObjects) -> &Vec<Self> {
|
||||
&ctx.$field
|
||||
}
|
||||
fn list_mut(ctx: &mut ContextObjects) -> &mut Vec<Self> {
|
||||
&mut ctx.$field
|
||||
}
|
||||
}
|
||||
)*
|
||||
};
|
||||
}
|
||||
|
||||
impl_context_object! {
|
||||
functions => VMFunction,
|
||||
tables => VMTable,
|
||||
globals => VMGlobal,
|
||||
memories => VMMemory,
|
||||
instances => js_sys::WebAssembly::Instance,
|
||||
}
|
||||
|
||||
/// Set of objects managed by a context.
|
||||
#[derive(Default)]
|
||||
pub struct ContextObjects {
|
||||
id: ContextId,
|
||||
memories: Vec<VMMemory>,
|
||||
tables: Vec<VMTable>,
|
||||
globals: Vec<VMGlobal>,
|
||||
functions: Vec<VMFunction>,
|
||||
instances: Vec<js_sys::WebAssembly::Instance>,
|
||||
}
|
||||
|
||||
impl ContextObjects {
|
||||
/// Returns the ID of this context.
|
||||
pub fn id(&self) -> ContextId {
|
||||
self.id
|
||||
}
|
||||
|
||||
/// Returns a pair of mutable references from two handles.
|
||||
///
|
||||
/// Panics if both handles point to the same object.
|
||||
pub fn get_2_mut<T: ContextObject>(
|
||||
&mut self,
|
||||
a: InternalContextHandle<T>,
|
||||
b: InternalContextHandle<T>,
|
||||
) -> (&mut T, &mut T) {
|
||||
assert_ne!(a.index(), b.index());
|
||||
let list = T::list_mut(self);
|
||||
if a.index() < b.index() {
|
||||
let (low, high) = list.split_at_mut(b.index());
|
||||
(&mut low[a.index()], &mut high[0])
|
||||
} else {
|
||||
let (low, high) = list.split_at_mut(a.index());
|
||||
(&mut high[0], &mut low[a.index()])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Handle to an object managed by a context.
|
||||
///
|
||||
/// Internally this is just an integer index into a context. A reference to the
|
||||
/// context must be passed in separately to access the actual object.
|
||||
pub struct ContextHandle<T> {
|
||||
id: ContextId,
|
||||
internal: InternalContextHandle<T>,
|
||||
}
|
||||
|
||||
impl<T> core::cmp::PartialEq for ContextHandle<T> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.id == other.id
|
||||
}
|
||||
}
|
||||
impl<T> Clone for ContextHandle<T> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
id: self.id,
|
||||
internal: self.internal,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ContextObject> fmt::Debug for ContextHandle<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("ContextHandle")
|
||||
.field("id", &self.id)
|
||||
.field("internal", &self.internal.index())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ContextObject> ContextHandle<T> {
|
||||
/// Moves the given object into a context and returns a handle to it.
|
||||
pub fn new(ctx: &mut ContextObjects, val: T) -> Self {
|
||||
Self {
|
||||
id: ctx.id,
|
||||
internal: InternalContextHandle::new(ctx, val),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a reference to the object that this handle points to.
|
||||
pub fn get<'a>(&self, ctx: &'a ContextObjects) -> &'a T {
|
||||
assert_eq!(self.id, ctx.id, "object used with the wrong context");
|
||||
self.internal.get(ctx)
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the object that this handle points to.
|
||||
pub fn get_mut<'a>(&self, ctx: &'a mut ContextObjects) -> &'a mut T {
|
||||
assert_eq!(self.id, ctx.id, "object used with the wrong context");
|
||||
self.internal.get_mut(ctx)
|
||||
}
|
||||
|
||||
/// Returns the internal handle contains within this handle.
|
||||
pub fn internal_handle(&self) -> InternalContextHandle<T> {
|
||||
self.internal
|
||||
}
|
||||
|
||||
/// Returns the ID of the context associated with the handle.
|
||||
pub fn context_id(&self) -> ContextId {
|
||||
self.id
|
||||
}
|
||||
|
||||
/// Constructs a `ContextHandle` from a `ContextId` and an `InternalContextHandle`.
|
||||
///
|
||||
/// # Safety
|
||||
/// Handling `InternalContextHandle` values is unsafe because they do not track context ID.
|
||||
pub unsafe fn from_internal(id: ContextId, internal: InternalContextHandle<T>) -> Self {
|
||||
Self { id, internal }
|
||||
}
|
||||
}
|
||||
|
||||
/// Internal handle to an object owned by the current context.
|
||||
///
|
||||
/// Unlike `ContextHandle` this does not track the context ID: it is only
|
||||
/// intended to be used within objects already owned by a context.
|
||||
#[repr(transparent)]
|
||||
pub struct InternalContextHandle<T> {
|
||||
// Use a NonZero here to reduce the size of Option<InternalContextHandle>.
|
||||
idx: NonZeroUsize,
|
||||
marker: PhantomData<fn() -> T>,
|
||||
}
|
||||
|
||||
impl<T> Clone for InternalContextHandle<T> {
|
||||
fn clone(&self) -> Self {
|
||||
*self
|
||||
}
|
||||
}
|
||||
impl<T> Copy for InternalContextHandle<T> {}
|
||||
|
||||
impl<T> fmt::Debug for InternalContextHandle<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("InternalContextHandle")
|
||||
.field("idx", &self.idx)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
impl<T> PartialEq for InternalContextHandle<T> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.idx == other.idx
|
||||
}
|
||||
}
|
||||
impl<T> Eq for InternalContextHandle<T> {}
|
||||
|
||||
impl<T: ContextObject> InternalContextHandle<T> {
|
||||
/// Moves the given object into a context and returns a handle to it.
|
||||
pub fn new(ctx: &mut ContextObjects, val: T) -> Self {
|
||||
let list = T::list_mut(ctx);
|
||||
let idx = NonZeroUsize::new(list.len() + 1).unwrap();
|
||||
list.push(val);
|
||||
Self {
|
||||
idx,
|
||||
marker: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a reference to the object that this handle points to.
|
||||
pub fn get<'a>(&self, ctx: &'a ContextObjects) -> &'a T {
|
||||
&T::list(ctx)[self.idx.get() - 1]
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the object that this handle points to.
|
||||
pub fn get_mut<'a>(&self, ctx: &'a mut ContextObjects) -> &'a mut T {
|
||||
&mut T::list_mut(ctx)[self.idx.get() - 1]
|
||||
}
|
||||
|
||||
pub(crate) fn index(&self) -> usize {
|
||||
self.idx.get()
|
||||
}
|
||||
|
||||
pub(crate) fn from_index(idx: usize) -> Option<Self> {
|
||||
NonZeroUsize::new(idx).map(|idx| Self {
|
||||
idx,
|
||||
marker: PhantomData,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Data used by the generated code is generally located inline within the
|
||||
/// `VMContext` for items defined in an instance. Host-defined objects are
|
||||
/// allocated separately and owned directly by the context.
|
||||
pub enum MaybeInstanceOwned<T> {
|
||||
/// The data is owned here.
|
||||
Host(Box<UnsafeCell<T>>),
|
||||
|
||||
/// The data is stored inline in the `VMContext` of an instance.
|
||||
Instance(NonNull<T>),
|
||||
}
|
||||
|
||||
impl<T> MaybeInstanceOwned<T> {
|
||||
/// Returns underlying pointer to the VM data.
|
||||
pub fn as_ptr(&self) -> NonNull<T> {
|
||||
match self {
|
||||
MaybeInstanceOwned::Host(p) => unsafe { NonNull::new_unchecked(p.get()) },
|
||||
MaybeInstanceOwned::Instance(p) => *p,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,219 +0,0 @@
|
||||
use crate::js::{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(())
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
pub trait WasmerEnv {
|
||||
/// 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,4 +1,7 @@
|
||||
use crate::js::lib::std::string::String;
|
||||
use crate::js::trap::RuntimeError;
|
||||
#[cfg(feature = "std")]
|
||||
use std::borrow::Cow;
|
||||
#[cfg(feature = "std")]
|
||||
use thiserror::Error;
|
||||
|
||||
@@ -83,11 +86,27 @@ pub enum WasmError {
|
||||
#[cfg_attr(feature = "std", error("Unsupported feature: {0}"))]
|
||||
Unsupported(String),
|
||||
|
||||
/// A Javascript value could not be converted to the requested type.
|
||||
#[cfg_attr(feature = "std", error("{0} doesn't match js value type {1}"))]
|
||||
TypeMismatch(Cow<'static, str>, Cow<'static, str>),
|
||||
|
||||
/// A generic error.
|
||||
#[cfg_attr(feature = "std", error("{0}"))]
|
||||
Generic(String),
|
||||
}
|
||||
|
||||
impl From<wasm_bindgen::JsValue> for WasmError {
|
||||
fn from(err: wasm_bindgen::JsValue) -> Self {
|
||||
Self::Generic(
|
||||
if err.is_string() && err.as_string().filter(|s| !s.is_empty()).is_some() {
|
||||
err.as_string().unwrap_or_default()
|
||||
} else {
|
||||
format!("Unexpected Javascript error: {:?}", err)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// The Serialize error can occur when serializing a
|
||||
/// compiled Module into a binary.
|
||||
/// Copied from wasmer_compiler::SerializeError
|
||||
@@ -124,3 +143,45 @@ pub enum DeserializeError {
|
||||
#[cfg_attr(feature = "std", error(transparent))]
|
||||
Compiler(CompileError),
|
||||
}
|
||||
|
||||
/// An error while instantiating a module.
|
||||
///
|
||||
/// This is not a common WebAssembly error, however
|
||||
/// we need to differentiate from a `LinkError` (an error
|
||||
/// that happens while linking, on instantiation), a
|
||||
/// Trap that occurs when calling the WebAssembly module
|
||||
/// start function, and an error when initializing the user's
|
||||
/// host environments.
|
||||
#[derive(Debug)]
|
||||
#[cfg_attr(feature = "std", derive(Error))]
|
||||
pub enum InstantiationError {
|
||||
/// A linking ocurred during instantiation.
|
||||
#[cfg_attr(feature = "std", error("Link error: {0}"))]
|
||||
Link(String),
|
||||
|
||||
/// A runtime error occured while invoking the start function
|
||||
#[cfg_attr(feature = "std", error(transparent))]
|
||||
Start(RuntimeError),
|
||||
|
||||
/// Import from a different [`Context`].
|
||||
/// This error occurs when an import from a different context is used.
|
||||
#[cfg_attr(feature = "std", error("cannot mix imports from different contexts"))]
|
||||
BadContext,
|
||||
|
||||
/// A generic error occured while invoking API functions
|
||||
#[cfg_attr(feature = "std", error(transparent))]
|
||||
Wasm(WasmError),
|
||||
}
|
||||
|
||||
impl From<WasmError> for InstantiationError {
|
||||
fn from(original: WasmError) -> Self {
|
||||
Self::Wasm(original)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "core")]
|
||||
impl std::fmt::Display for InstantiationError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "InstantiationError")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
use crate::js::instance::Instance;
|
||||
use crate::js::context::{AsContextMut, AsContextRef, InternalContextHandle};
|
||||
use crate::js::error::WasmError;
|
||||
use crate::js::wasm_bindgen_polyfill::Global;
|
||||
use crate::js::HostEnvInitError;
|
||||
use crate::js::WasmerEnv;
|
||||
use js_sys::Function;
|
||||
use js_sys::WebAssembly::{Memory, Table};
|
||||
use std::cell::RefCell;
|
||||
use std::fmt;
|
||||
use std::sync::Arc;
|
||||
use wasm_bindgen::{JsCast, JsValue};
|
||||
use wasmer_types::{ExternType, FunctionType, GlobalType, MemoryType, TableType};
|
||||
|
||||
@@ -59,31 +56,14 @@ impl VMTable {
|
||||
pub struct VMFunction {
|
||||
pub(crate) function: Function,
|
||||
pub(crate) ty: FunctionType,
|
||||
pub(crate) environment: Option<Arc<RefCell<Box<dyn WasmerEnv>>>>,
|
||||
}
|
||||
|
||||
unsafe impl Send for VMFunction {}
|
||||
unsafe impl Sync for VMFunction {}
|
||||
|
||||
impl VMFunction {
|
||||
pub(crate) fn new(
|
||||
function: Function,
|
||||
ty: FunctionType,
|
||||
environment: Option<Box<dyn WasmerEnv>>,
|
||||
) -> Self {
|
||||
Self {
|
||||
function,
|
||||
ty,
|
||||
environment: environment.map(|env| Arc::new(RefCell::new(env))),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn init_envs(&self, instance: &Instance) -> Result<(), HostEnvInitError> {
|
||||
if let Some(env) = &self.environment {
|
||||
let mut borrowed_env = env.borrow_mut();
|
||||
borrowed_env.init_with_instance(instance)?;
|
||||
}
|
||||
Ok(())
|
||||
pub(crate) fn new(function: Function, ty: FunctionType) -> Self {
|
||||
Self { function, ty }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,67 +85,90 @@ impl fmt::Debug for VMFunction {
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Export {
|
||||
/// A function export value.
|
||||
Function(VMFunction),
|
||||
Function(InternalContextHandle<VMFunction>),
|
||||
|
||||
/// A table export value.
|
||||
Table(VMTable),
|
||||
Table(InternalContextHandle<VMTable>),
|
||||
|
||||
/// A memory export value.
|
||||
Memory(VMMemory),
|
||||
Memory(InternalContextHandle<VMMemory>),
|
||||
|
||||
/// A global export value.
|
||||
Global(VMGlobal),
|
||||
Global(InternalContextHandle<VMGlobal>),
|
||||
}
|
||||
|
||||
impl Export {
|
||||
/// Return the export as a `JSValue`.
|
||||
pub fn as_jsvalue(&self) -> &JsValue {
|
||||
pub fn as_jsvalue<'context>(&self, ctx: &'context impl AsContextRef) -> &'context JsValue {
|
||||
match self {
|
||||
Export::Memory(js_wasm_memory) => js_wasm_memory.memory.as_ref(),
|
||||
Export::Function(js_func) => js_func.function.as_ref(),
|
||||
Export::Table(js_wasm_table) => js_wasm_table.table.as_ref(),
|
||||
Export::Global(js_wasm_global) => js_wasm_global.global.as_ref(),
|
||||
Self::Memory(js_wasm_memory) => js_wasm_memory
|
||||
.get(ctx.as_context_ref().objects())
|
||||
.memory
|
||||
.as_ref(),
|
||||
Self::Function(js_func) => js_func
|
||||
.get(ctx.as_context_ref().objects())
|
||||
.function
|
||||
.as_ref(),
|
||||
Self::Table(js_wasm_table) => js_wasm_table
|
||||
.get(ctx.as_context_ref().objects())
|
||||
.table
|
||||
.as_ref(),
|
||||
Self::Global(js_wasm_global) => js_wasm_global
|
||||
.get(ctx.as_context_ref().objects())
|
||||
.global
|
||||
.as_ref(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<(JsValue, ExternType)> for Export {
|
||||
fn from((val, extern_type): (JsValue, ExternType)) -> Export {
|
||||
/// Convert a `JsValue` into an `Export` within a given `Context`.
|
||||
pub fn from_js_value(
|
||||
val: JsValue,
|
||||
ctx: &mut impl AsContextMut,
|
||||
extern_type: ExternType,
|
||||
) -> Result<Self, WasmError> {
|
||||
match extern_type {
|
||||
ExternType::Memory(memory_type) => {
|
||||
if val.is_instance_of::<Memory>() {
|
||||
return Export::Memory(VMMemory::new(
|
||||
val.unchecked_into::<Memory>(),
|
||||
memory_type,
|
||||
));
|
||||
Ok(Self::Memory(InternalContextHandle::new(
|
||||
&mut ctx.as_context_mut().objects_mut(),
|
||||
VMMemory::new(val.unchecked_into::<Memory>(), memory_type),
|
||||
)))
|
||||
} else {
|
||||
panic!("Extern type doesn't match js value type");
|
||||
Err(WasmError::TypeMismatch(
|
||||
val.js_typeof()
|
||||
.as_string()
|
||||
.map(Into::into)
|
||||
.unwrap_or("unknown".into()),
|
||||
"Memory".into(),
|
||||
))
|
||||
}
|
||||
}
|
||||
ExternType::Global(global_type) => {
|
||||
if val.is_instance_of::<Global>() {
|
||||
return Export::Global(VMGlobal::new(
|
||||
val.unchecked_into::<Global>(),
|
||||
global_type,
|
||||
));
|
||||
Ok(Self::Global(InternalContextHandle::new(
|
||||
&mut ctx.as_context_mut().objects_mut(),
|
||||
VMGlobal::new(val.unchecked_into::<Global>(), global_type),
|
||||
)))
|
||||
} else {
|
||||
panic!("Extern type doesn't match js value type");
|
||||
}
|
||||
}
|
||||
ExternType::Function(function_type) => {
|
||||
if val.is_instance_of::<Function>() {
|
||||
return Export::Function(VMFunction::new(
|
||||
val.unchecked_into::<Function>(),
|
||||
function_type,
|
||||
None,
|
||||
));
|
||||
Ok(Self::Function(InternalContextHandle::new(
|
||||
&mut ctx.as_context_mut().objects_mut(),
|
||||
VMFunction::new(val.unchecked_into::<Function>(), function_type),
|
||||
)))
|
||||
} else {
|
||||
panic!("Extern type doesn't match js value type");
|
||||
}
|
||||
}
|
||||
ExternType::Table(table_type) => {
|
||||
if val.is_instance_of::<Table>() {
|
||||
return Export::Table(VMTable::new(val.unchecked_into::<Table>(), table_type));
|
||||
Ok(Self::Table(InternalContextHandle::new(
|
||||
&mut ctx.as_context_mut().objects_mut(),
|
||||
VMTable::new(val.unchecked_into::<Table>(), table_type),
|
||||
)))
|
||||
} else {
|
||||
panic!("Extern type doesn't match js value type");
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::js::export::Export;
|
||||
use crate::js::context::AsContextRef;
|
||||
use crate::js::externals::{Extern, Function, Global, Memory, Table};
|
||||
use crate::js::native::TypedFunction;
|
||||
use crate::js::WasmTypeList;
|
||||
@@ -141,18 +141,20 @@ 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
|
||||
Args: WasmTypeList,
|
||||
Rets: WasmTypeList,
|
||||
{
|
||||
self.get_typed_function(name)
|
||||
self.get_typed_function(ctx, name)
|
||||
}
|
||||
|
||||
/// Get an export as a `TypedFunction`.
|
||||
pub fn get_typed_function<Args, Rets>(
|
||||
&self,
|
||||
ctx: &impl AsContextRef,
|
||||
name: &str,
|
||||
) -> Result<TypedFunction<Args, Rets>, ExportError>
|
||||
where
|
||||
@@ -160,12 +162,16 @@ impl Exports {
|
||||
Rets: WasmTypeList,
|
||||
{
|
||||
self.get_function(name)?
|
||||
.native()
|
||||
.native(ctx)
|
||||
.map_err(|_| ExportError::IncompatibleType)
|
||||
}
|
||||
|
||||
/// Hack to get this working with nativefunc too
|
||||
pub fn get_with_generics<'a, T, Args, Rets>(&'a self, name: &str) -> Result<T, ExportError>
|
||||
pub fn get_with_generics<'a, T, Args, Rets>(
|
||||
&'a self,
|
||||
ctx: &impl AsContextRef,
|
||||
name: &str,
|
||||
) -> Result<T, ExportError>
|
||||
where
|
||||
Args: WasmTypeList,
|
||||
Rets: WasmTypeList,
|
||||
@@ -173,19 +179,23 @@ impl Exports {
|
||||
{
|
||||
match self.map.get(name) {
|
||||
None => Err(ExportError::Missing(name.to_string())),
|
||||
Some(extern_) => T::get_self_from_extern_with_generics(extern_),
|
||||
Some(extern_) => T::get_self_from_extern_with_generics(ctx, extern_),
|
||||
}
|
||||
}
|
||||
|
||||
/// Like `get_with_generics` but with a WeakReference to the `InstanceRef` internally.
|
||||
/// This is useful for passing data into `WasmerEnv`, for example.
|
||||
pub fn get_with_generics_weak<'a, T, Args, Rets>(&'a self, name: &str) -> Result<T, ExportError>
|
||||
/// This is useful for passing data into Context data, for example.
|
||||
pub fn get_with_generics_weak<'a, T, Args, Rets>(
|
||||
&'a self,
|
||||
ctx: &impl AsContextRef,
|
||||
name: &str,
|
||||
) -> Result<T, ExportError>
|
||||
where
|
||||
Args: WasmTypeList,
|
||||
Rets: WasmTypeList,
|
||||
T: ExportableWithGenerics<'a, Args, Rets>,
|
||||
{
|
||||
let out: T = self.get_with_generics(name)?;
|
||||
let out: T = self.get_with_generics(ctx, name)?;
|
||||
Ok(out)
|
||||
}
|
||||
|
||||
@@ -311,12 +321,6 @@ impl<'a> IntoIterator for &'a Exports {
|
||||
///
|
||||
/// [`Instance`]: crate::js::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::js::Module
|
||||
fn to_export(&self) -> Export;
|
||||
|
||||
/// Implementation of how to get the export corresponding to the implementing type
|
||||
/// from an [`Instance`] by name.
|
||||
///
|
||||
@@ -329,13 +333,19 @@ pub trait Exportable<'a>: Sized {
|
||||
/// as well.
|
||||
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>;
|
||||
fn get_self_from_extern_with_generics(
|
||||
ctx: &impl AsContextRef,
|
||||
_extern: &'a Extern,
|
||||
) -> Result<Self, ExportError>;
|
||||
}
|
||||
|
||||
/// We implement it for all concrete [`Exportable`] types (that are `Clone`)
|
||||
/// with empty `Args` and `Rets`.
|
||||
impl<'a, T: Exportable<'a> + Clone + 'static> ExportableWithGenerics<'a, (), ()> for T {
|
||||
fn get_self_from_extern_with_generics(_extern: &'a Extern) -> Result<Self, ExportError> {
|
||||
fn get_self_from_extern_with_generics(
|
||||
_ctx: &impl AsContextRef,
|
||||
_extern: &'a Extern,
|
||||
) -> Result<Self, ExportError> {
|
||||
T::get_self_from_extern(_extern).map(|i| i.clone())
|
||||
}
|
||||
}
|
||||
|
||||
787
lib/api/src/js/externals/function.rs
vendored
787
lib/api/src/js/externals/function.rs
vendored
File diff suppressed because it is too large
Load Diff
163
lib/api/src/js/externals/global.rs
vendored
163
lib/api/src/js/externals/global.rs
vendored
@@ -1,9 +1,8 @@
|
||||
use crate::js::export::Export;
|
||||
use crate::js::context::{AsContextMut, AsContextRef, ContextHandle, InternalContextHandle};
|
||||
use crate::js::export::VMGlobal;
|
||||
use crate::js::exports::{ExportError, Exportable};
|
||||
use crate::js::externals::Extern;
|
||||
use crate::js::store::Store;
|
||||
use crate::js::types::{Val, ValType};
|
||||
use crate::js::value::Value;
|
||||
use crate::js::wasm_bindgen_polyfill::Global as JSGlobal;
|
||||
use crate::js::GlobalType;
|
||||
use crate::js::Mutability;
|
||||
@@ -18,12 +17,11 @@ use wasm_bindgen::JsValue;
|
||||
/// Spec: <https://webassembly.github.io/spec/core/exec/runtime.html#global-instances>
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct Global {
|
||||
store: Store,
|
||||
vm_global: VMGlobal,
|
||||
pub(crate) handle: ContextHandle<VMGlobal>,
|
||||
}
|
||||
|
||||
impl Global {
|
||||
/// Create a new `Global` with the initial value [`Val`].
|
||||
/// Create a new `Global` with the initial value [`Value`].
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
@@ -36,11 +34,11 @@ 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: &mut impl AsContextMut, val: Value) -> Self {
|
||||
Self::from_value(ctx, val, Mutability::Const).unwrap()
|
||||
}
|
||||
|
||||
/// Create a mutable `Global` with the initial value [`Val`].
|
||||
/// Create a mutable `Global` with the initial value [`Value`].
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
@@ -53,22 +51,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: &mut 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> {
|
||||
/// Create a `Global` with the initial value [`Value`] and the provided [`Mutability`].
|
||||
fn from_value(
|
||||
ctx: &mut impl AsContextMut,
|
||||
val: Value,
|
||||
mutability: Mutability,
|
||||
) -> Result<Self, RuntimeError> {
|
||||
if !val.is_from_context(ctx) {
|
||||
return Err(RuntimeError::new(
|
||||
"cross-`Context` values are not supported",
|
||||
));
|
||||
}
|
||||
let global_ty = GlobalType {
|
||||
mutability,
|
||||
ty: val.ty(),
|
||||
};
|
||||
let descriptor = js_sys::Object::new();
|
||||
let (type_str, value) = match val {
|
||||
Val::I32(i) => ("i32", JsValue::from_f64(i as _)),
|
||||
Val::I64(i) => ("i64", JsValue::from_f64(i as _)),
|
||||
Val::F32(f) => ("f32", JsValue::from_f64(f as _)),
|
||||
Val::F64(f) => ("f64", JsValue::from_f64(f)),
|
||||
Value::I32(i) => ("i32", JsValue::from_f64(i as _)),
|
||||
Value::I64(i) => ("i64", JsValue::from_f64(i as _)),
|
||||
Value::F32(f) => ("f32", JsValue::from_f64(f as _)),
|
||||
Value::F64(f) => ("f64", JsValue::from_f64(f)),
|
||||
_ => unimplemented!("The type is not yet supported in the JS Global API"),
|
||||
};
|
||||
// This is the value type as string, even though is incorrectly called "value"
|
||||
@@ -81,12 +88,9 @@ impl Global {
|
||||
)?;
|
||||
|
||||
let js_global = JSGlobal::new(&descriptor, &value).unwrap();
|
||||
let global = VMGlobal::new(js_global, global_ty);
|
||||
let vm_global = VMGlobal::new(js_global, global_ty);
|
||||
|
||||
Ok(Self {
|
||||
store: store.clone(),
|
||||
vm_global: global,
|
||||
})
|
||||
Ok(Self::from_vm_export(ctx, vm_global))
|
||||
}
|
||||
|
||||
/// Returns the [`GlobalType`] of the `Global`.
|
||||
@@ -103,27 +107,11 @@ 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.ty
|
||||
pub fn ty(&self, ctx: &impl AsContextRef) -> GlobalType {
|
||||
self.handle.get(ctx.as_context_ref().objects()).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
|
||||
}
|
||||
|
||||
/// Retrieves the current value [`Val`] that the Global has.
|
||||
/// Retrieves the current value [`Value`] that the Global has.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
@@ -135,17 +123,30 @@ impl Global {
|
||||
///
|
||||
/// assert_eq!(g.get(), Value::I32(1));
|
||||
/// ```
|
||||
pub fn get(&self) -> Val {
|
||||
pub fn get(&self, ctx: &impl AsContextRef) -> Value {
|
||||
unsafe {
|
||||
let raw = self
|
||||
.handle
|
||||
.get(ctx.as_context_ref().objects())
|
||||
.global
|
||||
.value()
|
||||
.as_f64()
|
||||
.unwrap();
|
||||
let ty = self.handle.get(ctx.as_context_ref().objects()).ty;
|
||||
Value::from_raw(ctx, ty.ty, raw)
|
||||
}
|
||||
/*
|
||||
match self.vm_global.ty.ty {
|
||||
ValType::I32 => Val::I32(self.vm_global.global.value().as_f64().unwrap() as _),
|
||||
ValType::I64 => Val::I64(self.vm_global.global.value().as_f64().unwrap() as _),
|
||||
ValType::F32 => Val::F32(self.vm_global.global.value().as_f64().unwrap() as _),
|
||||
ValType::F64 => Val::F64(self.vm_global.global.value().as_f64().unwrap()),
|
||||
ValType::I32 => Value::I32(self.vm_global.global.value().as_f64().unwrap() as _),
|
||||
ValType::I64 => Value::I64(self.vm_global.global.value().as_f64().unwrap() as _),
|
||||
ValType::F32 => Value::F32(self.vm_global.global.value().as_f64().unwrap() as _),
|
||||
ValType::F64 => Value::F64(self.vm_global.global.value().as_f64().unwrap()),
|
||||
_ => unimplemented!("The type is not yet supported in the JS Global API"),
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
/// Sets a custom value [`Val`] to the runtime Global.
|
||||
/// Sets a custom value [`Value`] to the runtime Global.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
@@ -186,53 +187,61 @@ impl Global {
|
||||
/// // This results in an error: `RuntimeError`.
|
||||
/// g.set(Value::I64(2)).unwrap();
|
||||
/// ```
|
||||
pub fn set(&self, val: Val) -> Result<(), RuntimeError> {
|
||||
if self.vm_global.ty.mutability == Mutability::Const {
|
||||
pub fn set(&self, ctx: &mut impl AsContextMut, val: Value) -> Result<(), RuntimeError> {
|
||||
if !val.is_from_context(ctx) {
|
||||
return Err(RuntimeError::new(
|
||||
"cross-`Context` values are not supported",
|
||||
));
|
||||
}
|
||||
let global_ty = self.ty(&ctx);
|
||||
if global_ty.mutability == Mutability::Const {
|
||||
return Err(RuntimeError::new("The global is immutable".to_owned()));
|
||||
}
|
||||
if val.ty() != self.vm_global.ty.ty {
|
||||
if val.ty() != global_ty.ty {
|
||||
return Err(RuntimeError::new("The types don't match".to_owned()));
|
||||
}
|
||||
let new_value = match val {
|
||||
Val::I32(i) => JsValue::from_f64(i as _),
|
||||
Val::I64(i) => JsValue::from_f64(i as _),
|
||||
Val::F32(f) => JsValue::from_f64(f as _),
|
||||
Val::F64(f) => JsValue::from_f64(f),
|
||||
_ => unimplemented!("The type is not yet supported in the JS Global API"),
|
||||
Value::I32(i) => JsValue::from_f64(i as _),
|
||||
Value::I64(i) => JsValue::from_f64(i as _),
|
||||
Value::F32(f) => JsValue::from_f64(f as _),
|
||||
Value::F64(f) => JsValue::from_f64(f),
|
||||
_ => {
|
||||
return Err(RuntimeError::new(
|
||||
"The type is not yet supported in the JS Global API".to_owned(),
|
||||
))
|
||||
}
|
||||
};
|
||||
self.vm_global.global.set_value(&new_value);
|
||||
self.handle
|
||||
.get_mut(ctx.as_context_mut().objects_mut())
|
||||
.global
|
||||
.set_value(&new_value);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn from_vm_export(store: &Store, vm_global: VMGlobal) -> Self {
|
||||
pub(crate) fn from_vm_export(ctx: &mut impl AsContextMut, vm_global: VMGlobal) -> Self {
|
||||
Self {
|
||||
store: store.clone(),
|
||||
vm_global,
|
||||
handle: ContextHandle::new(ctx.as_context_mut().objects_mut(), vm_global),
|
||||
}
|
||||
}
|
||||
|
||||
/// 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 {
|
||||
self.vm_global == other.vm_global
|
||||
pub(crate) fn from_vm_extern(
|
||||
ctx: &mut impl AsContextMut,
|
||||
internal: InternalContextHandle<VMGlobal>,
|
||||
) -> Self {
|
||||
Self {
|
||||
handle: unsafe {
|
||||
ContextHandle::from_internal(ctx.as_context_ref().objects().id(), internal)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// 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()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Exportable<'a> for Global {
|
||||
fn to_export(&self) -> Export {
|
||||
Export::Global(self.vm_global.clone())
|
||||
}
|
||||
|
||||
fn get_self_from_extern(_extern: &'a Extern) -> Result<&'a Self, ExportError> {
|
||||
match _extern {
|
||||
Extern::Global(global) => Ok(global),
|
||||
|
||||
229
lib/api/src/js/externals/memory.rs
vendored
229
lib/api/src/js/externals/memory.rs
vendored
@@ -1,9 +1,12 @@
|
||||
use crate::js::export::{Export, VMMemory};
|
||||
use crate::js::context::{
|
||||
AsContextMut, AsContextRef, ContextHandle, ContextObjects, InternalContextHandle,
|
||||
};
|
||||
use crate::js::export::VMMemory;
|
||||
use crate::js::exports::{ExportError, Exportable};
|
||||
use crate::js::externals::Extern;
|
||||
use crate::js::store::Store;
|
||||
use crate::js::{MemoryAccessError, MemoryType};
|
||||
use std::convert::TryInto;
|
||||
use std::marker::PhantomData;
|
||||
use std::mem::MaybeUninit;
|
||||
use std::slice;
|
||||
use thiserror::Error;
|
||||
@@ -77,8 +80,8 @@ extern "C" {
|
||||
/// Spec: <https://webassembly.github.io/spec/core/exec/runtime.html#memory-instances>
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Memory {
|
||||
store: Store,
|
||||
vm_memory: VMMemory,
|
||||
pub(crate) handle: ContextHandle<VMMemory>,
|
||||
#[allow(dead_code)]
|
||||
view: js_sys::Uint8Array,
|
||||
}
|
||||
|
||||
@@ -99,7 +102,7 @@ impl Memory {
|
||||
/// #
|
||||
/// let m = Memory::new(&store, MemoryType::new(1, None, false)).unwrap();
|
||||
/// ```
|
||||
pub fn new(store: &Store, ty: MemoryType) -> Result<Self, MemoryError> {
|
||||
pub fn new(ctx: &mut impl AsContextMut, ty: MemoryType) -> Result<Self, MemoryError> {
|
||||
let descriptor = js_sys::Object::new();
|
||||
js_sys::Reflect::set(&descriptor, &"initial".into(), &ty.minimum.0.into()).unwrap();
|
||||
if let Some(max) = ty.maximum {
|
||||
@@ -110,13 +113,8 @@ impl Memory {
|
||||
let js_memory = js_sys::WebAssembly::Memory::new(&descriptor)
|
||||
.map_err(|_e| MemoryError::Generic("Error while creating the memory".to_owned()))?;
|
||||
|
||||
let memory = VMMemory::new(js_memory, ty);
|
||||
let view = js_sys::Uint8Array::new(&memory.memory.buffer());
|
||||
Ok(Self {
|
||||
store: store.clone(),
|
||||
vm_memory: memory,
|
||||
view,
|
||||
})
|
||||
let vm_memory = VMMemory::new(js_memory, ty);
|
||||
Ok(Self::from_vm_export(ctx, vm_memory))
|
||||
}
|
||||
|
||||
/// Returns the [`MemoryType`] of the `Memory`.
|
||||
@@ -132,26 +130,8 @@ impl Memory {
|
||||
///
|
||||
/// assert_eq!(m.ty(), mt);
|
||||
/// ```
|
||||
pub fn ty(&self) -> MemoryType {
|
||||
let mut ty = self.vm_memory.ty.clone();
|
||||
ty.minimum = self.size();
|
||||
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`.
|
||||
@@ -161,11 +141,18 @@ impl Memory {
|
||||
}
|
||||
|
||||
/// Returns the size (in bytes) of the `Memory`.
|
||||
pub fn data_size(&self) -> u64 {
|
||||
js_sys::Reflect::get(&self.vm_memory.memory.buffer(), &"byteLength".into())
|
||||
.unwrap()
|
||||
.as_f64()
|
||||
.unwrap() as _
|
||||
pub fn data_size(&self, ctx: &impl AsContextRef) -> u64 {
|
||||
js_sys::Reflect::get(
|
||||
&self
|
||||
.handle
|
||||
.get(ctx.as_context_ref().objects())
|
||||
.memory
|
||||
.buffer(),
|
||||
&"byteLength".into(),
|
||||
)
|
||||
.unwrap()
|
||||
.as_f64()
|
||||
.unwrap() as _
|
||||
}
|
||||
|
||||
/// Returns the size (in [`Pages`]) of the `Memory`.
|
||||
@@ -180,11 +167,18 @@ impl Memory {
|
||||
///
|
||||
/// assert_eq!(m.size(), Pages(1));
|
||||
/// ```
|
||||
pub fn size(&self) -> Pages {
|
||||
let bytes = js_sys::Reflect::get(&self.vm_memory.memory.buffer(), &"byteLength".into())
|
||||
.unwrap()
|
||||
.as_f64()
|
||||
.unwrap() as u64;
|
||||
pub fn size(&self, ctx: &impl AsContextRef) -> Pages {
|
||||
let bytes = js_sys::Reflect::get(
|
||||
&self
|
||||
.handle
|
||||
.get(ctx.as_context_ref().objects())
|
||||
.memory
|
||||
.buffer(),
|
||||
&"byteLength".into(),
|
||||
)
|
||||
.unwrap()
|
||||
.as_f64()
|
||||
.unwrap() as u64;
|
||||
Bytes(bytes as usize).try_into().unwrap()
|
||||
}
|
||||
|
||||
@@ -218,16 +212,22 @@ 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,
|
||||
ctx: &mut impl AsContextMut,
|
||||
delta: IntoPages,
|
||||
) -> Result<Pages, MemoryError>
|
||||
where
|
||||
IntoPages: Into<Pages>,
|
||||
{
|
||||
let pages = delta.into();
|
||||
let js_memory = self.vm_memory.memory.clone().unchecked_into::<JSMemory>();
|
||||
let new_pages = js_memory.grow(pages.0).map_err(|err| {
|
||||
let mut ctx_mut = ctx.as_context_mut();
|
||||
let js_memory = &self.handle.get_mut(ctx_mut.objects_mut()).memory;
|
||||
let our_js_memory: &JSMemory = JsCast::unchecked_from_js_ref(js_memory);
|
||||
let new_pages = our_js_memory.grow(pages.0).map_err(|err| {
|
||||
if err.is_instance_of::<js_sys::RangeError>() {
|
||||
MemoryError::CouldNotGrow {
|
||||
current: self.size(),
|
||||
current: self.size(&ctx.as_context_ref()),
|
||||
attempted_delta: pages,
|
||||
}
|
||||
} else {
|
||||
@@ -239,33 +239,43 @@ impl Memory {
|
||||
|
||||
/// Used by tests
|
||||
#[doc(hidden)]
|
||||
pub fn uint8view(&self) -> js_sys::Uint8Array {
|
||||
js_sys::Uint8Array::new(&self.vm_memory.memory.buffer())
|
||||
pub fn uint8view(&self, ctx: &impl AsContextRef) -> js_sys::Uint8Array {
|
||||
js_sys::Uint8Array::new(
|
||||
&self
|
||||
.handle
|
||||
.get(ctx.as_context_ref().objects())
|
||||
.memory
|
||||
.buffer(),
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn from_vm_export(store: &Store, vm_memory: VMMemory) -> Self {
|
||||
pub(crate) fn buffer<'a>(&'a self, _ctx: &'a impl AsContextRef) -> MemoryBuffer<'a> {
|
||||
MemoryBuffer {
|
||||
base: &self.view as *const _ as *mut _,
|
||||
marker: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn from_vm_export(ctx: &mut impl AsContextMut, vm_memory: VMMemory) -> Self {
|
||||
let view = js_sys::Uint8Array::new(&vm_memory.memory.buffer());
|
||||
Self {
|
||||
store: store.clone(),
|
||||
vm_memory,
|
||||
handle: ContextHandle::new(ctx.as_context_mut().objects_mut(), vm_memory),
|
||||
view,
|
||||
}
|
||||
}
|
||||
|
||||
/// 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 {
|
||||
self.vm_memory == other.vm_memory
|
||||
pub(crate) fn from_vm_extern(
|
||||
ctx: &mut impl AsContextMut,
|
||||
internal: InternalContextHandle<VMMemory>,
|
||||
) -> Self {
|
||||
let view =
|
||||
js_sys::Uint8Array::new(&internal.get(ctx.as_context_ref().objects()).memory.buffer());
|
||||
Self {
|
||||
handle: unsafe {
|
||||
ContextHandle::from_internal(ctx.as_context_ref().objects().id(), internal)
|
||||
},
|
||||
view,
|
||||
}
|
||||
}
|
||||
|
||||
/// Safely reads bytes from the memory at the given offset.
|
||||
@@ -275,10 +285,15 @@ 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 view = self.uint8view();
|
||||
pub fn read(
|
||||
&self,
|
||||
_ctx: &impl AsContextRef,
|
||||
offset: u64,
|
||||
data: &mut [u8],
|
||||
) -> Result<(), MemoryAccessError> {
|
||||
let view = &self.view;
|
||||
let offset: u32 = offset.try_into().map_err(|_| MemoryAccessError::Overflow)?;
|
||||
let len: u32 = buf
|
||||
let len: u32 = data
|
||||
.len()
|
||||
.try_into()
|
||||
.map_err(|_| MemoryAccessError::Overflow)?;
|
||||
@@ -286,7 +301,7 @@ impl Memory {
|
||||
if end > view.length() {
|
||||
Err(MemoryAccessError::HeapOutOfBounds)?;
|
||||
}
|
||||
view.subarray(offset, end).copy_to(buf);
|
||||
view.subarray(offset, end).copy_to(data);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -302,10 +317,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 view = self.uint8view();
|
||||
let view = &self.view;
|
||||
let offset: u32 = offset.try_into().map_err(|_| MemoryAccessError::Overflow)?;
|
||||
let len: u32 = buf
|
||||
.len()
|
||||
@@ -334,14 +350,18 @@ 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 view = self.uint8view();
|
||||
pub fn write(
|
||||
&self,
|
||||
_ctx: &mut impl AsContextMut,
|
||||
offset: u64,
|
||||
data: &[u8],
|
||||
) -> Result<(), MemoryAccessError> {
|
||||
let offset: u32 = offset.try_into().map_err(|_| MemoryAccessError::Overflow)?;
|
||||
let len: u32 = data
|
||||
.len()
|
||||
.try_into()
|
||||
.map_err(|_| MemoryAccessError::Overflow)?;
|
||||
let view = self.uint8view();
|
||||
let view = &self.view;
|
||||
let end = offset.checked_add(len).ok_or(MemoryAccessError::Overflow)?;
|
||||
if end > view.length() {
|
||||
Err(MemoryAccessError::HeapOutOfBounds)?;
|
||||
@@ -349,13 +369,14 @@ impl Memory {
|
||||
view.subarray(offset, end).copy_from(data);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 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()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Exportable<'a> for Memory {
|
||||
fn to_export(&self) -> Export {
|
||||
Export::Memory(self.vm_memory.clone())
|
||||
}
|
||||
|
||||
fn get_self_from_extern(_extern: &'a Extern) -> Result<&'a Self, ExportError> {
|
||||
match _extern {
|
||||
Extern::Memory(memory) => Ok(memory),
|
||||
@@ -363,3 +384,57 @@ impl<'a> Exportable<'a> for Memory {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Underlying buffer for a memory.
|
||||
#[derive(Copy, Clone)]
|
||||
pub(crate) struct MemoryBuffer<'a> {
|
||||
base: *mut js_sys::Uint8Array,
|
||||
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)?;
|
||||
let view = unsafe { &*(self.base) };
|
||||
if end > view.length().into() {
|
||||
return Err(MemoryAccessError::HeapOutOfBounds);
|
||||
}
|
||||
view.subarray(offset as _, end as _)
|
||||
.copy_to(unsafe { &mut slice::from_raw_parts_mut(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)?;
|
||||
let view = unsafe { &*(self.base) };
|
||||
if end > view.length().into() {
|
||||
return Err(MemoryAccessError::HeapOutOfBounds);
|
||||
}
|
||||
let buf_ptr = buf.as_mut_ptr() as *mut u8;
|
||||
view.subarray(offset as _, end as _)
|
||||
.copy_to(unsafe { &mut slice::from_raw_parts_mut(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)?;
|
||||
let view = unsafe { &mut *(self.base) };
|
||||
if end > view.length().into() {
|
||||
return Err(MemoryAccessError::HeapOutOfBounds);
|
||||
}
|
||||
view.subarray(offset as _, end as _).copy_from(data);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
65
lib/api/src/js/externals/mod.rs
vendored
65
lib/api/src/js/externals/mod.rs
vendored
@@ -1,19 +1,18 @@
|
||||
pub(crate) mod function;
|
||||
mod global;
|
||||
mod memory;
|
||||
pub(crate) mod memory;
|
||||
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, MemoryError};
|
||||
pub use self::table::Table;
|
||||
|
||||
use crate::js::context::{AsContextMut, AsContextRef};
|
||||
use crate::js::export::Export;
|
||||
use crate::js::exports::{ExportError, Exportable};
|
||||
use crate::js::store::{Store, StoreObject};
|
||||
use crate::js::store::StoreObject;
|
||||
use crate::js::types::AsJs;
|
||||
use crate::js::ExternType;
|
||||
use std::fmt;
|
||||
|
||||
@@ -35,36 +34,58 @@ 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).clone()),
|
||||
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 {
|
||||
pub fn from_vm_export(ctx: &mut impl AsContextMut, 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)),
|
||||
Export::Function(f) => Self::Function(Function::from_vm_extern(ctx, f)),
|
||||
Export::Memory(m) => Self::Memory(Memory::from_vm_extern(ctx, m)),
|
||||
Export::Global(g) => Self::Global(Global::from_vm_extern(ctx, g)),
|
||||
Export::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(val) => val.is_from_context(ctx),
|
||||
Self::Memory(val) => val.is_from_context(ctx),
|
||||
Self::Global(val) => val.is_from_context(ctx),
|
||||
Self::Table(val) => val.is_from_context(ctx),
|
||||
}
|
||||
}
|
||||
|
||||
fn to_export(&self) -> Export {
|
||||
match self {
|
||||
Self::Function(val) => Export::Function(val.handle.internal_handle()),
|
||||
Self::Memory(val) => Export::Memory(val.handle.internal_handle()),
|
||||
Self::Global(val) => Export::Global(val.handle.internal_handle()),
|
||||
Self::Table(val) => Export::Table(val.handle.internal_handle()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Exportable<'a> for Extern {
|
||||
fn to_export(&self) -> Export {
|
||||
impl AsJs for Extern {
|
||||
fn as_jsvalue(&self, ctx: &impl AsContextRef) -> wasm_bindgen::JsValue {
|
||||
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(),
|
||||
Self::Function(_) => self.to_export().as_jsvalue(ctx),
|
||||
Self::Global(_) => self.to_export().as_jsvalue(ctx),
|
||||
Self::Table(_) => self.to_export().as_jsvalue(ctx),
|
||||
Self::Memory(_) => self.to_export().as_jsvalue(ctx),
|
||||
}
|
||||
.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Exportable<'a> for Extern {
|
||||
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)
|
||||
|
||||
122
lib/api/src/js/externals/table.rs
vendored
122
lib/api/src/js/externals/table.rs
vendored
@@ -1,13 +1,11 @@
|
||||
use crate::js::export::VMFunction;
|
||||
use crate::js::export::{Export, VMTable};
|
||||
use crate::js::context::{AsContextMut, AsContextRef, ContextHandle, InternalContextHandle};
|
||||
use crate::js::export::{VMFunction, VMTable};
|
||||
use crate::js::exports::{ExportError, Exportable};
|
||||
use crate::js::externals::{Extern, Function as WasmerFunction};
|
||||
use crate::js::store::Store;
|
||||
use crate::js::types::Val;
|
||||
use crate::js::externals::Extern;
|
||||
use crate::js::value::Value;
|
||||
use crate::js::RuntimeError;
|
||||
use crate::js::TableType;
|
||||
use crate::js::{FunctionType, TableType};
|
||||
use js_sys::Function;
|
||||
use wasmer_types::FunctionType;
|
||||
|
||||
/// A WebAssembly `table` instance.
|
||||
///
|
||||
@@ -20,17 +18,24 @@ use wasmer_types::FunctionType;
|
||||
/// Spec: <https://webassembly.github.io/spec/core/exec/runtime.html#table-instances>
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct Table {
|
||||
store: Store,
|
||||
vm_table: VMTable,
|
||||
pub(crate) handle: ContextHandle<VMTable>,
|
||||
}
|
||||
|
||||
fn set_table_item(table: &VMTable, item_index: u32, item: &Function) -> Result<(), RuntimeError> {
|
||||
table.table.set(item_index, item).map_err(|e| e.into())
|
||||
}
|
||||
|
||||
fn get_function(val: Val) -> Result<Function, RuntimeError> {
|
||||
fn get_function(ctx: &mut impl AsContextMut, val: Value) -> Result<Function, RuntimeError> {
|
||||
if !val.is_from_context(ctx) {
|
||||
return Err(RuntimeError::new("cannot pass Value across contexts"));
|
||||
}
|
||||
match val {
|
||||
Val::FuncRef(func) => Ok(func.as_ref().unwrap().exported.function.clone().into()),
|
||||
Value::FuncRef(Some(ref func)) => Ok(func
|
||||
.handle
|
||||
.get(&ctx.as_context_ref().objects())
|
||||
.function
|
||||
.clone()
|
||||
.into()),
|
||||
// Only funcrefs is supported by the spec atm
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
@@ -43,7 +48,12 @@ impl Table {
|
||||
///
|
||||
/// This function will construct the `Table` using the store
|
||||
/// [`BaseTunables`][crate::js::tunables::BaseTunables].
|
||||
pub fn new(store: &Store, ty: TableType, init: Val) -> Result<Self, RuntimeError> {
|
||||
pub fn new(
|
||||
ctx: &mut impl AsContextMut,
|
||||
ty: TableType,
|
||||
init: Value,
|
||||
) -> Result<Self, RuntimeError> {
|
||||
let mut ctx = ctx.as_context_mut();
|
||||
let descriptor = js_sys::Object::new();
|
||||
js_sys::Reflect::set(&descriptor, &"initial".into(), &ty.minimum.into())?;
|
||||
if let Some(max) = ty.maximum {
|
||||
@@ -55,47 +65,60 @@ impl Table {
|
||||
let table = VMTable::new(js_table, ty);
|
||||
|
||||
let num_elements = table.table.length();
|
||||
let func = get_function(init)?;
|
||||
let func = get_function(&mut ctx, init)?;
|
||||
for i in 0..num_elements {
|
||||
set_table_item(&table, i, &func)?;
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
store: store.clone(),
|
||||
vm_table: table,
|
||||
handle: ContextHandle::new(ctx.objects_mut(), table),
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns the [`TableType`] of the `Table`.
|
||||
pub fn ty(&self) -> &TableType {
|
||||
&self.vm_table.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 func = self.vm_table.table.get(index).ok()?;
|
||||
let ty = FunctionType::new(vec![], vec![]);
|
||||
Some(Val::FuncRef(Some(WasmerFunction::from_vm_export(
|
||||
&self.store,
|
||||
VMFunction::new(func, ty, None),
|
||||
))))
|
||||
pub fn get(&self, ctx: &mut impl AsContextMut, index: u32) -> Option<Value> {
|
||||
if let Some(func) = self
|
||||
.handle
|
||||
.get(ctx.as_context_ref().objects())
|
||||
.table
|
||||
.get(index)
|
||||
.ok()
|
||||
{
|
||||
let ty = FunctionType::new(vec![], vec![]);
|
||||
let vm_function = VMFunction::new(func, ty);
|
||||
let function = crate::js::externals::Function::from_vm_export(ctx, vm_function);
|
||||
Some(Value::FuncRef(Some(function)))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets an element `val` in the Table at the provided `index`.
|
||||
pub fn set(&self, index: u32, val: Val) -> Result<(), RuntimeError> {
|
||||
let func = get_function(val)?;
|
||||
set_table_item(&self.vm_table, index, &func)?;
|
||||
Ok(())
|
||||
pub fn set(
|
||||
&self,
|
||||
ctx: &mut impl AsContextMut,
|
||||
index: u32,
|
||||
val: Value,
|
||||
) -> Result<(), RuntimeError> {
|
||||
let item = get_function(ctx, 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.table.length()
|
||||
pub fn size(&self, ctx: &impl AsContextRef) -> u32 {
|
||||
self.handle
|
||||
.get(ctx.as_context_ref().objects())
|
||||
.table
|
||||
.length()
|
||||
}
|
||||
|
||||
/// Grows the size of the `Table` by `delta`, initializating
|
||||
@@ -107,7 +130,7 @@ 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> {
|
||||
pub fn grow(&self, _delta: u32, _init: Value) -> Result<u32, RuntimeError> {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
@@ -128,16 +151,20 @@ impl Table {
|
||||
unimplemented!("Table.copy is not natively supported in Javascript");
|
||||
}
|
||||
|
||||
pub(crate) fn from_vm_export(store: &Store, vm_table: VMTable) -> Self {
|
||||
pub(crate) fn from_vm_extern(
|
||||
ctx: &mut 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 {
|
||||
self.vm_table == other.vm_table
|
||||
/// 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
|
||||
@@ -148,16 +175,15 @@ impl Table {
|
||||
/// 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
|
||||
pub unsafe fn get_vm_table<'context>(
|
||||
&self,
|
||||
ctx: &'context impl AsContextRef,
|
||||
) -> &'context VMTable {
|
||||
self.handle.get(ctx.as_context_ref().objects())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Exportable<'a> for Table {
|
||||
fn to_export(&self) -> Export {
|
||||
Export::Table(self.vm_table.clone())
|
||||
}
|
||||
|
||||
fn get_self_from_extern(_extern: &'a Extern) -> Result<&'a Self, ExportError> {
|
||||
match _extern {
|
||||
Extern::Table(table) => Ok(table),
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
//! 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::exports::{Exportable, Exports};
|
||||
use crate::js::instance::InstantiationError;
|
||||
use crate::js::context::AsContextRef;
|
||||
use crate::js::error::InstantiationError;
|
||||
use crate::js::exports::Exports;
|
||||
use crate::js::module::Module;
|
||||
use crate::js::types::AsJs;
|
||||
use crate::Extern;
|
||||
use std::collections::HashMap;
|
||||
use std::fmt;
|
||||
@@ -149,7 +151,7 @@ impl Imports {
|
||||
}
|
||||
|
||||
/// Returns the `Imports` as a Javascript `Object`
|
||||
pub fn as_jsobject(&self) -> js_sys::Object {
|
||||
pub fn as_jsobject(&self, ctx: &impl AsContextRef) -> js_sys::Object {
|
||||
let imports = js_sys::Object::new();
|
||||
let namespaces: HashMap<&str, Vec<(&str, &Extern)>> =
|
||||
self.map
|
||||
@@ -164,12 +166,8 @@ impl Imports {
|
||||
for (ns, exports) in namespaces.into_iter() {
|
||||
let import_namespace = js_sys::Object::new();
|
||||
for (name, ext) in exports {
|
||||
js_sys::Reflect::set(
|
||||
&import_namespace,
|
||||
&name.into(),
|
||||
ext.to_export().as_jsvalue(),
|
||||
)
|
||||
.expect("Error while setting into the js namespace object");
|
||||
js_sys::Reflect::set(&import_namespace, &name.into(), &ext.as_jsvalue(ctx))
|
||||
.expect("Error while setting into the js namespace object");
|
||||
}
|
||||
js_sys::Reflect::set(&imports, &ns.into(), &import_namespace.into())
|
||||
.expect("Error while setting into the js imports object");
|
||||
@@ -178,12 +176,6 @@ impl Imports {
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<js_sys::Object> for Imports {
|
||||
fn into(self) -> js_sys::Object {
|
||||
self.as_jsobject()
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoIterator for &Imports {
|
||||
type IntoIter = std::collections::hash_map::IntoIter<(String, String), Extern>;
|
||||
type Item = ((String, String), Extern);
|
||||
@@ -299,15 +291,14 @@ macro_rules! import_namespace {
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
/*
|
||||
mod test {
|
||||
use crate::js::export::Export;
|
||||
use crate::js::exports::Exportable;
|
||||
use crate::js::Type;
|
||||
use crate::js::{Global, Store, Val};
|
||||
use wasm_bindgen_test::*;
|
||||
|
||||
#[wasm_bindgen_test]
|
||||
use crate::js::export::Export;
|
||||
use wasm_bindgen_test::*;
|
||||
fn namespace() {
|
||||
let store = Store::default();
|
||||
let g1 = Global::new(&store, Val::I32(0));
|
||||
@@ -329,7 +320,6 @@ mod test {
|
||||
);
|
||||
}
|
||||
|
||||
#[wasm_bindgen_test]
|
||||
fn imports_macro_allows_trailing_comma_and_none() {
|
||||
use crate::js::Function;
|
||||
|
||||
@@ -381,7 +371,6 @@ mod test {
|
||||
};
|
||||
}
|
||||
|
||||
#[wasm_bindgen_test]
|
||||
fn chaining_works() {
|
||||
let store = Store::default();
|
||||
let g = Global::new(&store, Val::I32(0));
|
||||
@@ -412,7 +401,6 @@ mod test {
|
||||
assert!(small.is_some());
|
||||
}
|
||||
|
||||
#[wasm_bindgen_test]
|
||||
fn extending_conflict_overwrites() {
|
||||
let store = Store::default();
|
||||
let g1 = Global::new(&store, Val::I32(0));
|
||||
@@ -470,3 +458,4 @@ mod test {
|
||||
);
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
@@ -1,15 +1,12 @@
|
||||
use crate::js::env::HostEnvInitError;
|
||||
use crate::js::context::{AsContextMut, AsContextRef, ContextHandle};
|
||||
use crate::js::error::InstantiationError;
|
||||
use crate::js::export::Export;
|
||||
use crate::js::exports::{Exportable, Exports};
|
||||
use crate::js::exports::Exports;
|
||||
use crate::js::externals::Extern;
|
||||
use crate::js::imports::Imports;
|
||||
use crate::js::module::Module;
|
||||
use crate::js::store::Store;
|
||||
use crate::js::trap::RuntimeError;
|
||||
use js_sys::WebAssembly;
|
||||
use std::fmt;
|
||||
#[cfg(feature = "std")]
|
||||
use thiserror::Error;
|
||||
|
||||
/// A WebAssembly Instance is a stateful, executable
|
||||
/// instance of a WebAssembly [`Module`].
|
||||
@@ -21,7 +18,7 @@ use thiserror::Error;
|
||||
/// Spec: <https://webassembly.github.io/spec/core/exec/runtime.html#module-instances>
|
||||
#[derive(Clone)]
|
||||
pub struct Instance {
|
||||
instance: WebAssembly::Instance,
|
||||
_handle: ContextHandle<WebAssembly::Instance>,
|
||||
module: Module,
|
||||
#[allow(dead_code)]
|
||||
imports: Imports,
|
||||
@@ -29,37 +26,6 @@ pub struct Instance {
|
||||
pub exports: Exports,
|
||||
}
|
||||
|
||||
/// An error while instantiating a module.
|
||||
///
|
||||
/// This is not a common WebAssembly error, however
|
||||
/// we need to differentiate from a `LinkError` (an error
|
||||
/// that happens while linking, on instantiation), a
|
||||
/// Trap that occurs when calling the WebAssembly module
|
||||
/// start function, and an error when initializing the user's
|
||||
/// host environments.
|
||||
#[derive(Debug)]
|
||||
#[cfg_attr(feature = "std", derive(Error))]
|
||||
pub enum InstantiationError {
|
||||
/// A linking ocurred during instantiation.
|
||||
#[cfg_attr(feature = "std", error("Link error: {0}"))]
|
||||
Link(String),
|
||||
|
||||
/// A runtime error occured while invoking the start function
|
||||
#[cfg_attr(feature = "std", error(transparent))]
|
||||
Start(RuntimeError),
|
||||
|
||||
/// Error occurred when initializing the host environment.
|
||||
#[cfg_attr(feature = "std", error(transparent))]
|
||||
HostEnvInitialization(HostEnvInitError),
|
||||
}
|
||||
|
||||
#[cfg(feature = "core")]
|
||||
impl std::fmt::Display for InstantiationError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "InstantiationError")
|
||||
}
|
||||
}
|
||||
|
||||
impl Instance {
|
||||
/// Creates a new `Instance` from a WebAssembly [`Module`] and a
|
||||
/// set of imports resolved by the [`Resolver`].
|
||||
@@ -94,14 +60,18 @@ 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> {
|
||||
pub fn new(
|
||||
ctx: &mut impl AsContextMut,
|
||||
module: &Module,
|
||||
imports: &Imports,
|
||||
) -> Result<Self, InstantiationError> {
|
||||
let import_copy = imports.clone();
|
||||
let (instance, imports): (WebAssembly::Instance, Vec<Extern>) = module
|
||||
.instantiate(imports)
|
||||
let (instance, _imports): (ContextHandle<WebAssembly::Instance>, Vec<Extern>) = module
|
||||
.instantiate(&mut ctx.as_context_mut(), imports)
|
||||
.map_err(|e| InstantiationError::Start(e))?;
|
||||
|
||||
let self_instance = Self::from_module_and_instance(module, instance, import_copy)?;
|
||||
self_instance.init_envs(&imports.iter().map(Extern::to_export).collect::<Vec<_>>())?;
|
||||
let self_instance = Self::from_module_and_instance(ctx, module, instance, import_copy)?;
|
||||
//self_instance.init_envs(&imports.iter().map(Extern::to_export).collect::<Vec<_>>())?;
|
||||
Ok(self_instance)
|
||||
}
|
||||
|
||||
@@ -115,12 +85,12 @@ impl Instance {
|
||||
///
|
||||
/// *This method is only available when targeting JS environments*
|
||||
pub fn from_module_and_instance(
|
||||
ctx: &mut impl AsContextMut,
|
||||
module: &Module,
|
||||
instance: WebAssembly::Instance,
|
||||
instance: ContextHandle<WebAssembly::Instance>,
|
||||
imports: Imports,
|
||||
) -> Result<Self, InstantiationError> {
|
||||
let store = module.store();
|
||||
let instance_exports = instance.exports();
|
||||
let instance_exports = instance.get(ctx.as_context_ref().objects()).exports();
|
||||
let exports = module
|
||||
.exports()
|
||||
.map(|export_type| {
|
||||
@@ -133,53 +103,34 @@ impl Instance {
|
||||
&name
|
||||
))
|
||||
})?;
|
||||
let export: Export = (js_export, extern_type).into();
|
||||
let extern_ = Extern::from_vm_export(store, export);
|
||||
let export: Export =
|
||||
Export::from_js_value(js_export, &mut ctx.as_context_mut(), extern_type)?
|
||||
.into();
|
||||
let extern_ = Extern::from_vm_export(&mut ctx.as_context_mut(), export);
|
||||
Ok((name.to_string(), extern_))
|
||||
})
|
||||
.collect::<Result<Exports, InstantiationError>>()?;
|
||||
|
||||
Ok(Self {
|
||||
instance,
|
||||
_handle: instance,
|
||||
module: module.clone(),
|
||||
imports,
|
||||
exports,
|
||||
})
|
||||
}
|
||||
|
||||
/// Initialize the given extern imports with the `Instance`.
|
||||
///
|
||||
/// # Important
|
||||
///
|
||||
/// This method should be called if the Wasmer `Instance` is initialized
|
||||
/// from Javascript with an already existing `WebAssembly.Instance` but with
|
||||
/// a imports from the Rust side.
|
||||
///
|
||||
/// *This method is only available when targeting JS environments*
|
||||
pub fn init_envs(&self, imports: &[Export]) -> Result<(), InstantiationError> {
|
||||
for import in imports {
|
||||
if let Export::Function(func) = import {
|
||||
func.init_envs(&self)
|
||||
.map_err(|e| InstantiationError::HostEnvInitialization(e))?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Gets the [`Module`] associated with this instance.
|
||||
pub fn module(&self) -> &Module {
|
||||
&self.module
|
||||
}
|
||||
|
||||
/// Returns the [`Store`] where the `Instance` belongs.
|
||||
pub fn store(&self) -> &Store {
|
||||
self.module.store()
|
||||
}
|
||||
|
||||
/// Returns the inner WebAssembly Instance
|
||||
#[doc(hidden)]
|
||||
pub fn raw(&self) -> &WebAssembly::Instance {
|
||||
&self.instance
|
||||
pub fn raw<'context>(
|
||||
&self,
|
||||
ctx: &'context impl AsContextRef,
|
||||
) -> &'context WebAssembly::Instance {
|
||||
&self._handle.get(ctx.as_context_ref().objects())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
use crate::js::context::AsContextMut;
|
||||
use crate::js::error::WasmError;
|
||||
use crate::js::{Export, ExternType, Module};
|
||||
use std::collections::HashMap;
|
||||
|
||||
@@ -49,15 +51,23 @@ impl JsImportObject {
|
||||
/// let import_object = JsImportObject::new(&module, js_object);
|
||||
/// import_object.get_export("module", "name");
|
||||
/// ```
|
||||
pub fn get_export(&self, module: &str, name: &str) -> Option<Export> {
|
||||
let namespace = js_sys::Reflect::get(&self.object, &module.into()).ok()?;
|
||||
let js_export = js_sys::Reflect::get(&namespace, &name.into()).ok()?;
|
||||
pub fn get_export(
|
||||
&self,
|
||||
ctx: &mut impl AsContextMut,
|
||||
module: &str,
|
||||
name: &str,
|
||||
) -> Result<Export, WasmError> {
|
||||
let namespace = js_sys::Reflect::get(&self.object, &module.into())?;
|
||||
let js_export = js_sys::Reflect::get(&namespace, &name.into())?;
|
||||
match self
|
||||
.module_imports
|
||||
.get(&(module.to_string(), name.to_string()))
|
||||
{
|
||||
Some(extern_type) => Some((js_export, extern_type.clone()).into()),
|
||||
None => None,
|
||||
Some(extern_type) => Ok(Export::from_js_value(js_export, ctx, extern_type.clone())?),
|
||||
None => Err(WasmError::Generic(format!(
|
||||
"Name {} not found in module {}",
|
||||
name, module
|
||||
))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
use crate::js::context::AsContextRef;
|
||||
use crate::js::externals::memory::MemoryBuffer;
|
||||
use crate::RuntimeError;
|
||||
use crate::{Memory, Memory32, Memory64, WasmPtr};
|
||||
use std::{
|
||||
@@ -51,7 +53,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 +61,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),
|
||||
offset,
|
||||
marker: PhantomData,
|
||||
}
|
||||
@@ -96,19 +98,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 +120,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 +147,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 +159,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 +172,7 @@ impl<'a, T: ValueType> WasmSlice<'a, T> {
|
||||
.checked_add(total_len)
|
||||
.ok_or(MemoryAccessError::Overflow)?;
|
||||
Ok(Self {
|
||||
memory,
|
||||
buffer: memory.buffer(ctx),
|
||||
offset,
|
||||
len,
|
||||
marker: PhantomData,
|
||||
@@ -202,12 +203,6 @@ impl<'a, T: ValueType> WasmSlice<'a, T> {
|
||||
self.len
|
||||
}
|
||||
|
||||
/// 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> {
|
||||
@@ -216,7 +211,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,
|
||||
}
|
||||
@@ -230,7 +225,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,
|
||||
@@ -271,7 +266,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(())
|
||||
}
|
||||
|
||||
@@ -296,7 +291,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()) })
|
||||
}
|
||||
|
||||
@@ -313,7 +308,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`.
|
||||
@@ -327,7 +322,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);
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ mod lib {
|
||||
}
|
||||
}
|
||||
|
||||
mod env;
|
||||
mod context;
|
||||
mod error;
|
||||
mod export;
|
||||
mod exports;
|
||||
@@ -36,19 +36,16 @@ mod module;
|
||||
#[cfg(feature = "wasm-types-polyfill")]
|
||||
mod module_info_polyfill;
|
||||
mod native;
|
||||
mod native_type;
|
||||
mod ptr;
|
||||
mod store;
|
||||
mod trap;
|
||||
mod types;
|
||||
mod value;
|
||||
mod wasm_bindgen_polyfill;
|
||||
|
||||
/// Implement [`WasmerEnv`] for your type with `#[derive(WasmerEnv)]`.
|
||||
///
|
||||
/// See the [`WasmerEnv`] trait for more information.
|
||||
pub use wasmer_derive::WasmerEnv;
|
||||
|
||||
pub use crate::js::env::{HostEnvInitError, LazyInit, WasmerEnv};
|
||||
pub use crate::js::error::{DeserializeError, SerializeError};
|
||||
pub use crate::js::context::{AsContextMut, AsContextRef, Context, ContextMut, ContextRef};
|
||||
pub use crate::js::error::{DeserializeError, InstantiationError, SerializeError};
|
||||
pub use crate::js::export::Export;
|
||||
pub use crate::js::exports::{ExportError, Exportable, Exports, ExportsIterator};
|
||||
pub use crate::js::externals::{
|
||||
@@ -56,20 +53,23 @@ pub use crate::js::externals::{
|
||||
WasmTypeList,
|
||||
};
|
||||
pub use crate::js::imports::Imports;
|
||||
pub use crate::js::instance::{Instance, InstantiationError};
|
||||
pub use crate::js::instance::Instance;
|
||||
pub use crate::js::js_import_object::JsImportObject;
|
||||
pub use crate::js::mem_access::{MemoryAccessError, WasmRef, WasmSlice, WasmSliceIter};
|
||||
pub use crate::js::module::{Module, ModuleTypeHints};
|
||||
pub use crate::js::native::TypedFunction;
|
||||
pub use crate::js::native_type::NativeWasmTypeInto;
|
||||
pub use crate::js::ptr::{Memory32, Memory64, MemorySize, WasmPtr, WasmPtr64};
|
||||
pub use crate::js::trap::RuntimeError;
|
||||
|
||||
pub use crate::js::store::{Store, StoreObject};
|
||||
pub use crate::js::types::ValType as Type;
|
||||
pub use crate::js::types::{
|
||||
ExportType, ExternType, FunctionType, GlobalType, ImportType, MemoryType, Mutability,
|
||||
TableType, Val, ValType,
|
||||
TableType, ValType,
|
||||
};
|
||||
pub use crate::js::types::{Val as Value, ValType as Type};
|
||||
pub use crate::js::value::Value;
|
||||
pub use crate::js::value::Value as Val;
|
||||
|
||||
pub use wasmer_types::is_wasm;
|
||||
pub use wasmer_types::{
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
use crate::js::exports::Exportable;
|
||||
use crate::js::context::{AsContextMut, ContextHandle};
|
||||
#[cfg(feature = "wat")]
|
||||
use crate::js::error::WasmError;
|
||||
use crate::js::error::{CompileError, InstantiationError};
|
||||
#[cfg(feature = "js-serializable-module")]
|
||||
use crate::js::error::{DeserializeError, SerializeError};
|
||||
use crate::js::externals::Extern;
|
||||
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;
|
||||
#[cfg(feature = "js-serializable-module")]
|
||||
use crate::js::error::{DeserializeError, SerializeError};
|
||||
use crate::js::types::{AsJs, ExportType, ImportType};
|
||||
use crate::js::RuntimeError;
|
||||
use js_sys::{Reflect, Uint8Array, WebAssembly};
|
||||
use std::fmt;
|
||||
@@ -220,8 +219,17 @@ impl Module {
|
||||
|
||||
pub(crate) fn instantiate(
|
||||
&self,
|
||||
ctx: &mut impl AsContextMut,
|
||||
imports: &Imports,
|
||||
) -> Result<(WebAssembly::Instance, Vec<Extern>), RuntimeError> {
|
||||
) -> Result<(ContextHandle<WebAssembly::Instance>, Vec<Extern>), RuntimeError> {
|
||||
// Ensure all imports come from the same context.
|
||||
if imports
|
||||
.into_iter()
|
||||
.any(|(_, import)| !import.is_from_context(ctx))
|
||||
{
|
||||
// FIXME is RuntimeError::User appropriate?
|
||||
return Err(RuntimeError::user(Box::new(InstantiationError::BadContext)));
|
||||
}
|
||||
let imports_object = js_sys::Object::new();
|
||||
let mut import_externs: Vec<Extern> = vec![];
|
||||
for import_type in self.imports() {
|
||||
@@ -233,7 +241,7 @@ impl Module {
|
||||
js_sys::Reflect::set(
|
||||
&val,
|
||||
&import_type.name().into(),
|
||||
import.to_export().as_jsvalue(),
|
||||
&import.as_jsvalue(&ctx.as_context_ref()),
|
||||
)?;
|
||||
} else {
|
||||
// If the namespace doesn't exist
|
||||
@@ -241,7 +249,7 @@ impl Module {
|
||||
js_sys::Reflect::set(
|
||||
&import_namespace,
|
||||
&import_type.name().into(),
|
||||
import.to_export().as_jsvalue(),
|
||||
&import.as_jsvalue(&ctx.as_context_ref()),
|
||||
)?;
|
||||
js_sys::Reflect::set(
|
||||
&imports_object,
|
||||
@@ -255,8 +263,11 @@ impl Module {
|
||||
// the error for us, so we don't need to handle it
|
||||
}
|
||||
Ok((
|
||||
WebAssembly::Instance::new(&self.module, &imports_object)
|
||||
.map_err(|e: JsValue| -> RuntimeError { e.into() })?,
|
||||
ContextHandle::new(
|
||||
ctx.as_context_mut().objects_mut(),
|
||||
WebAssembly::Instance::new(&self.module, &imports_object)
|
||||
.map_err(|e: JsValue| -> RuntimeError { e.into() })?,
|
||||
),
|
||||
import_externs,
|
||||
))
|
||||
}
|
||||
|
||||
@@ -9,7 +9,9 @@
|
||||
//! ```
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use crate::js::{FromToNativeWasmType, Function, RuntimeError, Store, WasmTypeList};
|
||||
use crate::js::context::{AsContextMut, AsContextRef, ContextHandle};
|
||||
use crate::js::externals::Function;
|
||||
use crate::js::{FromToNativeWasmType, RuntimeError, WasmTypeList};
|
||||
// use std::panic::{catch_unwind, AssertUnwindSafe};
|
||||
use crate::js::export::VMFunction;
|
||||
use crate::js::types::param_from_js;
|
||||
@@ -21,8 +23,7 @@ use wasm_bindgen::JsValue;
|
||||
/// (using the Native ABI).
|
||||
#[derive(Clone)]
|
||||
pub struct TypedFunction<Args = (), Rets = ()> {
|
||||
store: Store,
|
||||
exported: VMFunction,
|
||||
pub(crate) handle: ContextHandle<VMFunction>,
|
||||
_phantom: PhantomData<(Args, Rets)>,
|
||||
}
|
||||
|
||||
@@ -34,34 +35,18 @@ where
|
||||
Args: WasmTypeList,
|
||||
Rets: WasmTypeList,
|
||||
{
|
||||
pub(crate) fn new(store: Store, exported: VMFunction) -> Self {
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn new<T>(ctx: &mut impl AsContextMut<Data = T>, vm_function: VMFunction) -> Self {
|
||||
Self {
|
||||
store,
|
||||
exported,
|
||||
handle: ContextHandle::new(ctx.as_context_mut().objects_mut(), vm_function),
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<Args, Rets> From<&TypedFunction<Args, Rets>> for VMFunction
|
||||
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 {
|
||||
pub(crate) fn from_handle(f: Function) -> Self {
|
||||
Self {
|
||||
store: other.store,
|
||||
exported: other.exported,
|
||||
handle: f.handle,
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -75,20 +60,23 @@ macro_rules! impl_native_traits {
|
||||
Rets: WasmTypeList,
|
||||
{
|
||||
/// Call the typed func and return results.
|
||||
pub fn call(&self, $( $x: $x, )* ) -> Result<Rets, RuntimeError> {
|
||||
let params_list: Vec<JsValue> = vec![ $( JsValue::from_f64($x.to_native().to_binary() as f64) ),* ];
|
||||
let results = self.exported.function.apply(
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn call(&self, ctx: &mut impl AsContextMut, $( $x: $x, )* ) -> Result<Rets, RuntimeError> where
|
||||
$( $x: FromToNativeWasmType + crate::js::NativeWasmTypeInto, )*
|
||||
{
|
||||
let params_list: Vec<JsValue> = vec![ $( JsValue::from_f64($x.into_raw(ctx))),* ];
|
||||
let results = self.handle.get(ctx.as_context_ref().objects()).function.apply(
|
||||
&JsValue::UNDEFINED,
|
||||
&Array::from_iter(params_list.iter())
|
||||
)?;
|
||||
let mut rets_list_array = Rets::empty_array();
|
||||
let mut_rets = rets_list_array.as_mut() as *mut [i128] as *mut i128;
|
||||
let mut_rets = rets_list_array.as_mut() as *mut [f64] as *mut f64;
|
||||
match Rets::size() {
|
||||
0 => {},
|
||||
1 => unsafe {
|
||||
let ty = Rets::wasm_types()[0];
|
||||
let val = param_from_js(&ty, &results);
|
||||
val.write_value_to(mut_rets);
|
||||
*mut_rets = val.as_raw(&mut ctx.as_context_mut());
|
||||
}
|
||||
_n => {
|
||||
let results: Array = results.into();
|
||||
@@ -96,12 +84,13 @@ macro_rules! impl_native_traits {
|
||||
let ret = results.get(i as u32);
|
||||
unsafe {
|
||||
let val = param_from_js(&ret_type, &ret);
|
||||
val.write_value_to(mut_rets.add(i));
|
||||
let slot = mut_rets.add(i);
|
||||
*slot = val.as_raw(&mut ctx.as_context_mut());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(Rets::from_array(rets_list_array))
|
||||
Ok(unsafe { Rets::from_array(ctx, rets_list_array) })
|
||||
}
|
||||
|
||||
}
|
||||
@@ -112,9 +101,9 @@ macro_rules! impl_native_traits {
|
||||
$( $x: FromToNativeWasmType, )*
|
||||
Rets: WasmTypeList,
|
||||
{
|
||||
fn get_self_from_extern_with_generics(_extern: &crate::js::externals::Extern) -> Result<Self, crate::js::exports::ExportError> {
|
||||
fn get_self_from_extern_with_generics(ctx: &impl AsContextRef, _extern: &crate::js::externals::Extern) -> Result<Self, crate::js::exports::ExportError> {
|
||||
use crate::js::exports::Exportable;
|
||||
crate::js::Function::get_self_from_extern(_extern)?.native().map_err(|_| crate::js::exports::ExportError::IncompatibleType)
|
||||
crate::js::Function::get_self_from_extern(_extern)?.native(ctx).map_err(|_| crate::js::exports::ExportError::IncompatibleType)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
160
lib/api/src/js/native_type.rs
Normal file
160
lib/api/src/js/native_type.rs
Normal file
@@ -0,0 +1,160 @@
|
||||
//! This module permits to create native functions
|
||||
//! easily in Rust, thanks to its advanced typing system.
|
||||
|
||||
use wasmer_types::{NativeWasmType, Type};
|
||||
|
||||
use crate::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: &mut impl AsContextMut) -> Self::Abi;
|
||||
|
||||
#[doc(hidden)]
|
||||
unsafe fn from_abi(ctx: &mut impl AsContextMut, abi: Self::Abi) -> Self;
|
||||
|
||||
/// Convert self to raw value representation.
|
||||
fn into_raw(self, ctx: &mut impl AsContextMut) -> f64;
|
||||
|
||||
/// Convert to self from raw value representation.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
unsafe fn from_raw(ctx: &mut impl AsContextMut, raw: f64) -> Self;
|
||||
}
|
||||
|
||||
impl NativeWasmTypeInto for i32 {
|
||||
#[inline]
|
||||
unsafe fn from_abi(_ctx: &mut impl AsContextMut, abi: Self::Abi) -> Self {
|
||||
abi
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn into_abi(self, _ctx: &mut impl AsContextMut) -> Self::Abi {
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn into_raw(self, _ctx: &mut impl AsContextMut) -> f64 {
|
||||
self.into()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn from_raw(_ctx: &mut impl AsContextMut, raw: f64) -> Self {
|
||||
raw as _
|
||||
}
|
||||
}
|
||||
|
||||
impl NativeWasmTypeInto for i64 {
|
||||
#[inline]
|
||||
unsafe fn from_abi(_ctx: &mut impl AsContextMut, abi: Self::Abi) -> Self {
|
||||
abi
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn into_abi(self, _ctx: &mut impl AsContextMut) -> Self::Abi {
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn into_raw(self, _ctx: &mut impl AsContextMut) -> f64 {
|
||||
self as _
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn from_raw(_ctx: &mut impl AsContextMut, raw: f64) -> Self {
|
||||
raw as _
|
||||
}
|
||||
}
|
||||
|
||||
impl NativeWasmTypeInto for f32 {
|
||||
#[inline]
|
||||
unsafe fn from_abi(_ctx: &mut impl AsContextMut, abi: Self::Abi) -> Self {
|
||||
abi
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn into_abi(self, _ctx: &mut impl AsContextMut) -> Self::Abi {
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn into_raw(self, _ctx: &mut impl AsContextMut) -> f64 {
|
||||
self as _
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn from_raw(_ctx: &mut impl AsContextMut, raw: f64) -> Self {
|
||||
raw as _
|
||||
}
|
||||
}
|
||||
|
||||
impl NativeWasmTypeInto for f64 {
|
||||
#[inline]
|
||||
unsafe fn from_abi(_ctx: &mut impl AsContextMut, abi: Self::Abi) -> Self {
|
||||
abi
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn into_abi(self, _ctx: &mut impl AsContextMut) -> Self::Abi {
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn into_raw(self, _ctx: &mut impl AsContextMut) -> f64 {
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn from_raw(_ctx: &mut impl AsContextMut, raw: f64) -> Self {
|
||||
raw
|
||||
}
|
||||
}
|
||||
|
||||
impl NativeWasmType for Function {
|
||||
const WASM_TYPE: Type = Type::FuncRef;
|
||||
type Abi = f64;
|
||||
}
|
||||
|
||||
/*
|
||||
mod test_native_type {
|
||||
use super::*;
|
||||
use wasmer_types::Type;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
// 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,3 +1,5 @@
|
||||
use crate::js::context::AsContextRef;
|
||||
use crate::js::NativeWasmTypeInto;
|
||||
use crate::js::{externals::Memory, FromToNativeWasmType};
|
||||
use crate::{MemoryAccessError, WasmRef, WasmSlice};
|
||||
use std::convert::TryFrom;
|
||||
@@ -136,20 +138,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<'a>(self, memory: &'a Memory) -> WasmRef<'a, 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
|
||||
@@ -160,10 +167,11 @@ impl<T: ValueType, M: MemorySize> WasmPtr<T, M> {
|
||||
#[inline]
|
||||
pub fn slice<'a>(
|
||||
self,
|
||||
ctx: &'a impl AsContextRef,
|
||||
memory: &'a Memory,
|
||||
len: M::Offset,
|
||||
) -> Result<WasmSlice<'a, T>, MemoryAccessError> {
|
||||
WasmSlice::new(memory, self.offset.into(), len.into())
|
||||
WasmSlice::new(ctx, memory, self.offset.into(), len.into())
|
||||
}
|
||||
|
||||
/// Reads a sequence of values from this `WasmPtr` until a value that
|
||||
@@ -173,13 +181,14 @@ impl<T: ValueType, M: MemorySize> WasmPtr<T, M> {
|
||||
#[inline]
|
||||
pub fn read_until<'a>(
|
||||
self,
|
||||
ctx: &'a impl AsContextRef,
|
||||
memory: &'a 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;
|
||||
}
|
||||
@@ -197,10 +206,11 @@ impl<M: MemorySize> WasmPtr<u8, M> {
|
||||
#[inline]
|
||||
pub fn read_utf8_string<'a>(
|
||||
self,
|
||||
ctx: &'a impl AsContextRef,
|
||||
memory: &'a 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)?)
|
||||
}
|
||||
|
||||
@@ -211,14 +221,18 @@ impl<M: MemorySize> WasmPtr<u8, M> {
|
||||
#[inline]
|
||||
pub fn read_utf8_string_with_nul<'a>(
|
||||
self,
|
||||
ctx: &'a impl AsContextRef,
|
||||
memory: &'a Memory,
|
||||
) -> Result<String, MemoryAccessError> {
|
||||
let vec = self.read_until(memory, |&byte| byte == 0)?;
|
||||
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 {
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
use crate::js::externals::Function;
|
||||
//use crate::js::externals::Function;
|
||||
// use crate::js::store::{Store, StoreObject};
|
||||
// use crate::js::RuntimeError;
|
||||
use crate::js::context::AsContextRef;
|
||||
use crate::js::value::Value;
|
||||
use wasm_bindgen::JsValue;
|
||||
use wasmer_types::Value;
|
||||
pub use wasmer_types::{
|
||||
ExportType, ExternType, FunctionType, GlobalType, ImportType, MemoryType, Mutability,
|
||||
TableType, Type as ValType,
|
||||
@@ -14,20 +15,20 @@ pub use wasmer_types::{
|
||||
/// * Vectors (128 bits, with 32 or 64 bit lanes)
|
||||
///
|
||||
/// Spec: <https://webassembly.github.io/spec/core/exec/runtime.html#values>
|
||||
// pub type Val = ();
|
||||
pub type Val = Value<Function>;
|
||||
// pub type Value = ();
|
||||
//pub type Value = Value<Function>;
|
||||
|
||||
pub trait AsJs {
|
||||
fn as_jsvalue(&self) -> JsValue;
|
||||
fn as_jsvalue(&self, ctx: &impl AsContextRef) -> JsValue;
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn param_from_js(ty: &ValType, js_val: &JsValue) -> Val {
|
||||
pub fn param_from_js(ty: &ValType, js_val: &JsValue) -> Value {
|
||||
match ty {
|
||||
ValType::I32 => Val::I32(js_val.as_f64().unwrap() as _),
|
||||
ValType::I64 => Val::I64(js_val.as_f64().unwrap() as _),
|
||||
ValType::F32 => Val::F32(js_val.as_f64().unwrap() as _),
|
||||
ValType::F64 => Val::F64(js_val.as_f64().unwrap()),
|
||||
ValType::I32 => Value::I32(js_val.as_f64().unwrap() as _),
|
||||
ValType::I64 => Value::I64(js_val.as_f64().unwrap() as _),
|
||||
ValType::F32 => Value::F32(js_val.as_f64().unwrap() as _),
|
||||
ValType::F64 => Value::F64(js_val.as_f64().unwrap()),
|
||||
t => unimplemented!(
|
||||
"The type `{:?}` is not yet supported in the JS Function API",
|
||||
t
|
||||
@@ -35,18 +36,20 @@ pub fn param_from_js(ty: &ValType, js_val: &JsValue) -> Val {
|
||||
}
|
||||
}
|
||||
|
||||
impl AsJs for Val {
|
||||
fn as_jsvalue(&self) -> JsValue {
|
||||
impl AsJs for Value {
|
||||
fn as_jsvalue(&self, ctx: &impl AsContextRef) -> JsValue {
|
||||
match self {
|
||||
Self::I32(i) => JsValue::from_f64(*i as f64),
|
||||
Self::I64(i) => JsValue::from_f64(*i as f64),
|
||||
Self::F32(f) => JsValue::from_f64(*f as f64),
|
||||
Self::F64(f) => JsValue::from_f64(*f),
|
||||
Self::FuncRef(func) => func.as_ref().unwrap().exported.function.clone().into(),
|
||||
v => unimplemented!(
|
||||
"The value `{:?}` is not yet supported in the JS Function API",
|
||||
v
|
||||
),
|
||||
Self::FuncRef(Some(func)) => func
|
||||
.handle
|
||||
.get(ctx.as_context_ref().objects())
|
||||
.function
|
||||
.clone()
|
||||
.into(),
|
||||
Self::FuncRef(None) => JsValue::null(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
423
lib/api/src/js/value.rs
Normal file
423
lib/api/src/js/value.rs
Normal file
@@ -0,0 +1,423 @@
|
||||
use std::convert::TryFrom;
|
||||
use std::fmt;
|
||||
use std::string::{String, ToString};
|
||||
|
||||
use wasmer_types::Type;
|
||||
|
||||
//use crate::ExternRef;
|
||||
use crate::js::externals::function::Function;
|
||||
|
||||
use super::context::AsContextRef;
|
||||
|
||||
/// WebAssembly computations manipulate values of basic value types:
|
||||
/// * Integers (32 or 64 bit width)
|
||||
/// * Floating-point (32 or 64 bit width)
|
||||
///
|
||||
/// Spec: <https://webassembly.github.io/spec/core/exec/runtime.html#values>
|
||||
#[derive(Clone, PartialEq)]
|
||||
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>),
|
||||
}
|
||||
|
||||
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::FuncRef(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,
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts the `Value` into a `f64`.
|
||||
pub fn as_raw(&self, ctx: &impl AsContextRef) -> f64 {
|
||||
match *self {
|
||||
Self::I32(v) => v as f64,
|
||||
Self::I64(v) => v as f64,
|
||||
Self::F32(v) => v as f64,
|
||||
Self::F64(v) => v,
|
||||
Self::FuncRef(Some(ref f)) => f
|
||||
.handle
|
||||
.get(ctx.as_context_ref().objects())
|
||||
.function
|
||||
.as_f64()
|
||||
.unwrap_or(0_f64), //TODO is this correct?
|
||||
|
||||
Self::FuncRef(None) => 0_f64,
|
||||
//Self::ExternRef(Some(ref e)) => unsafe { *e.address().0 } as .into_raw(),
|
||||
//Self::ExternRef(None) => externref: 0 },
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts a `f64` to a `Value`.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
pub unsafe fn from_raw(_ctx: &impl AsContextRef, ty: Type, raw: f64) -> Self {
|
||||
match ty {
|
||||
Type::I32 => Self::I32(raw as i32),
|
||||
Type::I64 => Self::I64(raw as i64),
|
||||
Type::F32 => Self::F32(raw as f32),
|
||||
Type::F64 => Self::F64(raw),
|
||||
Type::FuncRef => todo!(),
|
||||
Type::V128 => todo!(),
|
||||
Type::ExternRef => todo!(),
|
||||
//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::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)
|
||||
}
|
||||
}
|
||||
|
||||
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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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(tests)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
/*
|
||||
|
||||
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)));
|
||||
}
|
||||
|
||||
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)));
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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::<()>::F64(1.234);
|
||||
let result = f32::try_from(value);
|
||||
assert_eq!(result.unwrap_err(), "Value is not of Wasm type f32");
|
||||
}
|
||||
|
||||
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::<()>::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