wasmtime: Initial, partial support for externref
This is enough to get an `externref -> externref` identity function passing. However, `externref`s that are dropped by compiled Wasm code are (safely) leaked. Follow up work will leverage cranelift's stack maps to resolve this issue.
This commit is contained in:
@@ -244,9 +244,10 @@ impl Func {
|
||||
// number of arguments and the right types of arguments. As a result
|
||||
// we should be able to safely run through them all and read them.
|
||||
let mut args = Vec::with_capacity(ty_clone.params().len());
|
||||
let store = Store::upgrade(&store_weak).unwrap();
|
||||
for (i, ty) in ty_clone.params().iter().enumerate() {
|
||||
unsafe {
|
||||
args.push(Val::read_value_from(values_vec.add(i), ty));
|
||||
args.push(Val::read_value_from(&store, values_vec.add(i), ty));
|
||||
}
|
||||
}
|
||||
let mut returns = vec![Val::null(); ty_clone.results().len()];
|
||||
@@ -483,25 +484,25 @@ impl Func {
|
||||
.store
|
||||
.compiler()
|
||||
.signatures()
|
||||
.lookup(self.export.signature)
|
||||
.lookup_wasm(self.export.signature)
|
||||
.expect("failed to lookup signature");
|
||||
|
||||
// This is only called with `Export::Function`, and since it's coming
|
||||
// from wasmtime_runtime itself we should support all the types coming
|
||||
// out of it, so assert such here.
|
||||
FuncType::from_wasmtime_signature(&sig).expect("core wasm signature should be supported")
|
||||
FuncType::from_wasm_func_type(&sig).expect("core wasm signature should be supported")
|
||||
}
|
||||
|
||||
/// Returns the number of parameters that this function takes.
|
||||
pub fn param_arity(&self) -> usize {
|
||||
let sig = self
|
||||
.instance
|
||||
self.instance
|
||||
.store
|
||||
.compiler()
|
||||
.signatures()
|
||||
.lookup(self.export.signature)
|
||||
.expect("failed to lookup signature");
|
||||
sig.params.len() - 2 // skip the two vmctx leading parameters
|
||||
.lookup_wasm(self.export.signature)
|
||||
.expect("failed to lookup signature")
|
||||
.params
|
||||
.len()
|
||||
}
|
||||
|
||||
/// Returns the number of results this function produces.
|
||||
@@ -511,7 +512,7 @@ impl Func {
|
||||
.store
|
||||
.compiler()
|
||||
.signatures()
|
||||
.lookup(self.export.signature)
|
||||
.lookup_wasm(self.export.signature)
|
||||
.expect("failed to lookup signature");
|
||||
sig.returns.len()
|
||||
}
|
||||
@@ -546,7 +547,11 @@ impl Func {
|
||||
let param_tys = my_ty.params().iter();
|
||||
for ((arg, slot), ty) in params.iter().zip(&mut values_vec).zip(param_tys) {
|
||||
if arg.ty() != *ty {
|
||||
bail!("argument type mismatch");
|
||||
bail!(
|
||||
"argument type mismatch: found {} but expected {}",
|
||||
arg.ty(),
|
||||
ty
|
||||
);
|
||||
}
|
||||
if !arg.comes_from_same_store(&self.instance.store) {
|
||||
bail!("cross-`Store` values are not currently supported");
|
||||
@@ -571,7 +576,7 @@ impl Func {
|
||||
for (index, ty) in my_ty.results().iter().enumerate() {
|
||||
unsafe {
|
||||
let ptr = values_vec.as_ptr().add(index);
|
||||
results.push(Val::read_value_from(ptr, ty));
|
||||
results.push(Val::read_value_from(&self.instance.store, ptr, ty));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -722,7 +727,10 @@ impl Func {
|
||||
(get15, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15)
|
||||
}
|
||||
|
||||
pub(crate) fn store(&self) -> &Store {
|
||||
// This should be `pub(crate)` but the C API happens to need it in one
|
||||
// place.
|
||||
#[doc(hidden)]
|
||||
pub fn store(&self) -> &Store {
|
||||
&self.instance.store
|
||||
}
|
||||
}
|
||||
@@ -1038,6 +1046,12 @@ impl Caller<'_> {
|
||||
Some(Extern::Memory(mem))
|
||||
}
|
||||
}
|
||||
|
||||
/// Get a handle to this caller's store.
|
||||
pub fn store(&self) -> Store {
|
||||
// See comment above the `store` member for why this unwrap is OK.
|
||||
Store::upgrade(&self.store).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_into_func {
|
||||
|
||||
278
crates/wasmtime/src/ref.rs
Normal file → Executable file
278
crates/wasmtime/src/ref.rs
Normal file → Executable file
@@ -2,224 +2,182 @@
|
||||
|
||||
use std::any::Any;
|
||||
use std::cell::{self, RefCell};
|
||||
use std::convert::TryFrom;
|
||||
use std::fmt;
|
||||
use std::marker::PhantomData;
|
||||
use std::rc::{Rc, Weak};
|
||||
|
||||
trait InternalRefBase: Any {
|
||||
fn as_any(&self) -> &dyn Any;
|
||||
fn host_info(&self) -> Option<cell::RefMut<Box<dyn Any>>>;
|
||||
fn set_host_info(&self, info: Option<Box<dyn Any>>);
|
||||
fn ptr_eq(&self, other: &dyn InternalRefBase) -> bool;
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct InternalRef(Rc<dyn InternalRefBase>);
|
||||
|
||||
impl InternalRef {
|
||||
pub fn is_ref<T: 'static>(&self) -> bool {
|
||||
let r = self.0.as_any();
|
||||
Any::is::<HostRef<T>>(r)
|
||||
}
|
||||
pub fn get_ref<T: 'static>(&self) -> HostRef<T> {
|
||||
let r = self.0.as_any();
|
||||
r.downcast_ref::<HostRef<T>>()
|
||||
.expect("reference is not T type")
|
||||
.clone()
|
||||
}
|
||||
}
|
||||
|
||||
struct AnyAndHostInfo {
|
||||
any: Box<dyn Any>,
|
||||
host_info: Option<Box<dyn Any>>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct OtherRef(Rc<RefCell<AnyAndHostInfo>>);
|
||||
use wasmtime_runtime::VMExternRef;
|
||||
|
||||
/// Represents an opaque reference to any data within WebAssembly.
|
||||
#[derive(Clone)]
|
||||
pub enum ExternRef {
|
||||
/// A reference to no data.
|
||||
Null,
|
||||
/// A reference to data stored internally in `wasmtime`.
|
||||
Ref(InternalRef),
|
||||
/// A reference to data located outside of `wasmtime`.
|
||||
Other(OtherRef),
|
||||
pub struct ExternRef {
|
||||
pub(crate) inner: VMExternRef,
|
||||
pub(crate) store: Weak<crate::runtime::StoreInner>,
|
||||
}
|
||||
|
||||
impl ExternRef {
|
||||
/// Creates a new instance of `ExternRef` from `Box<dyn Any>`.
|
||||
pub fn new(data: Box<dyn Any>) -> Self {
|
||||
let info = AnyAndHostInfo {
|
||||
any: data,
|
||||
host_info: None,
|
||||
};
|
||||
ExternRef::Other(OtherRef(Rc::new(RefCell::new(info))))
|
||||
/// Creates a new instance of `ExternRef` wrapping the given value.
|
||||
pub fn new<T>(store: &crate::Store, value: T) -> ExternRef
|
||||
where
|
||||
T: 'static + Any,
|
||||
{
|
||||
let inner = VMExternRef::new(value);
|
||||
let store = store.weak();
|
||||
ExternRef { inner, store }
|
||||
}
|
||||
|
||||
/// Creates a `Null` reference.
|
||||
pub fn null() -> Self {
|
||||
ExternRef::Null
|
||||
pub fn null() -> Option<Self> {
|
||||
None
|
||||
}
|
||||
|
||||
/// Returns the data stored in the reference if available.
|
||||
/// # Panics
|
||||
/// Panics if the variant isn't `ExternRef::Other`.
|
||||
pub fn data(&self) -> cell::Ref<Box<dyn Any>> {
|
||||
match self {
|
||||
ExternRef::Other(OtherRef(r)) => cell::Ref::map(r.borrow(), |r| &r.any),
|
||||
_ => panic!("expected ExternRef::Other"),
|
||||
}
|
||||
/// Get this reference's store.
|
||||
///
|
||||
/// Returns `None` if this reference outlived its store.
|
||||
pub fn store(&self) -> Option<crate::runtime::Store> {
|
||||
crate::runtime::Store::upgrade(&self.store)
|
||||
}
|
||||
|
||||
/// Returns true if the two `ExternRef<T>`'s point to the same value (not just
|
||||
/// values that compare as equal).
|
||||
/// Get the underlying data for this `ExternRef`.
|
||||
pub fn data(&self) -> &dyn Any {
|
||||
&*self.inner
|
||||
}
|
||||
|
||||
/// Does this `ExternRef` point to the same inner value as `other`?0
|
||||
///
|
||||
/// This is *only* pointer equality, and does *not* run any inner value's
|
||||
/// `Eq` implementation.
|
||||
pub fn ptr_eq(&self, other: &ExternRef) -> bool {
|
||||
match (self, other) {
|
||||
(ExternRef::Null, ExternRef::Null) => true,
|
||||
(ExternRef::Ref(InternalRef(ref a)), ExternRef::Ref(InternalRef(ref b))) => {
|
||||
a.ptr_eq(b.as_ref())
|
||||
}
|
||||
(ExternRef::Other(OtherRef(ref a)), ExternRef::Other(OtherRef(ref b))) => {
|
||||
Rc::ptr_eq(a, b)
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
self.inner == other.inner
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the host information if available.
|
||||
/// # Panics
|
||||
/// Panics if `ExternRef` is already borrowed or `ExternRef` is `Null`.
|
||||
pub fn host_info(&self) -> Option<cell::RefMut<Box<dyn Any>>> {
|
||||
match self {
|
||||
ExternRef::Null => panic!("null"),
|
||||
ExternRef::Ref(r) => r.0.host_info(),
|
||||
ExternRef::Other(r) => {
|
||||
let info = cell::RefMut::map(r.0.borrow_mut(), |b| &mut b.host_info);
|
||||
if info.is_none() {
|
||||
return None;
|
||||
}
|
||||
Some(cell::RefMut::map(info, |info| info.as_mut().unwrap()))
|
||||
}
|
||||
}
|
||||
/// Returns the host information for this `externref`, if previously created
|
||||
/// with `set_host_info`.
|
||||
pub fn host_info(&self) -> Option<Rc<RefCell<dyn Any>>> {
|
||||
let store = crate::Store::upgrade(&self.store)?;
|
||||
store.host_info(self)
|
||||
}
|
||||
|
||||
/// Sets the host information for an `ExternRef`.
|
||||
/// # Panics
|
||||
/// Panics if `ExternRef` is already borrowed or `ExternRef` is `Null`.
|
||||
pub fn set_host_info(&self, info: Option<Box<dyn Any>>) {
|
||||
match self {
|
||||
ExternRef::Null => panic!("null"),
|
||||
ExternRef::Ref(r) => r.0.set_host_info(info),
|
||||
ExternRef::Other(r) => {
|
||||
r.0.borrow_mut().host_info = info;
|
||||
}
|
||||
}
|
||||
/// Set the host information for this `externref`, returning the old host
|
||||
/// information if it was previously set.
|
||||
pub fn set_host_info<T>(&self, info: T) -> Option<Rc<RefCell<dyn Any>>>
|
||||
where
|
||||
T: 'static + Any,
|
||||
{
|
||||
let store = crate::Store::upgrade(&self.store)?;
|
||||
store.set_host_info(self, Some(Rc::new(RefCell::new(info))))
|
||||
}
|
||||
|
||||
/// Remove the host information for this `externref`, returning the old host
|
||||
/// information if it was previously set.
|
||||
pub fn remove_host_info(&self) -> Option<Rc<RefCell<dyn Any>>> {
|
||||
let store = crate::Store::upgrade(&self.store)?;
|
||||
store.set_host_info(self, None)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for ExternRef {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
ExternRef::Null => write!(f, "null"),
|
||||
ExternRef::Ref(_) => write!(f, "externref"),
|
||||
ExternRef::Other(_) => write!(f, "other ref"),
|
||||
}
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let ExternRef { inner, store: _ } = self;
|
||||
f.debug_struct("ExternRef")
|
||||
.field("inner", &inner)
|
||||
.field("store", &"..")
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
struct ContentBox<T> {
|
||||
content: T,
|
||||
host_info: Option<Box<dyn Any>>,
|
||||
externref_data: Weak<dyn InternalRefBase>,
|
||||
}
|
||||
|
||||
/// Represents a piece of data located in the host environment.
|
||||
pub struct HostRef<T>(Rc<RefCell<ContentBox<T>>>);
|
||||
#[derive(Debug)]
|
||||
pub struct HostRef<T>
|
||||
where
|
||||
T: 'static + Any,
|
||||
{
|
||||
externref: ExternRef,
|
||||
_phantom: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T: 'static> HostRef<T> {
|
||||
impl<T> HostRef<T>
|
||||
where
|
||||
T: 'static + Any,
|
||||
{
|
||||
/// Creates a new `HostRef<T>` from `T`.
|
||||
pub fn new(item: T) -> HostRef<T> {
|
||||
let externref_data: Weak<HostRef<T>> = Weak::new();
|
||||
let content = ContentBox {
|
||||
content: item,
|
||||
host_info: None,
|
||||
externref_data,
|
||||
};
|
||||
HostRef(Rc::new(RefCell::new(content)))
|
||||
pub fn new(store: &crate::Store, item: T) -> HostRef<T> {
|
||||
HostRef {
|
||||
externref: ExternRef::new(store, RefCell::new(item)),
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Immutably borrows the wrapped data.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the value is currently mutably borrowed.
|
||||
pub fn borrow(&self) -> cell::Ref<T> {
|
||||
cell::Ref::map(self.0.borrow(), |b| &b.content)
|
||||
self.inner().borrow()
|
||||
}
|
||||
|
||||
/// Mutably borrows the wrapped data.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the `HostRef<T>` is already borrowed.
|
||||
pub fn borrow_mut(&self) -> cell::RefMut<T> {
|
||||
cell::RefMut::map(self.0.borrow_mut(), |b| &mut b.content)
|
||||
self.inner().borrow_mut()
|
||||
}
|
||||
|
||||
/// Returns true if the two `HostRef<T>`'s point to the same value (not just
|
||||
/// values that compare as equal).
|
||||
pub fn ptr_eq(&self, other: &HostRef<T>) -> bool {
|
||||
Rc::ptr_eq(&self.0, &other.0)
|
||||
self.externref.ptr_eq(&other.externref)
|
||||
}
|
||||
|
||||
/// Returns an opaque reference to the wrapped data in the form of
|
||||
/// an `ExternRef`.
|
||||
/// # Panics
|
||||
/// Panics if `HostRef<T>` is already mutably borrowed.
|
||||
pub fn externref(&self) -> ExternRef {
|
||||
let r = self.0.borrow_mut().externref_data.upgrade();
|
||||
if let Some(r) = r {
|
||||
return ExternRef::Ref(InternalRef(r));
|
||||
}
|
||||
let externref_data: Rc<dyn InternalRefBase> = Rc::new(self.clone());
|
||||
self.0.borrow_mut().externref_data = Rc::downgrade(&externref_data);
|
||||
ExternRef::Ref(InternalRef(externref_data))
|
||||
fn inner(&self) -> &RefCell<T> {
|
||||
self.externref
|
||||
.inner
|
||||
.downcast_ref::<RefCell<T>>()
|
||||
.expect("`HostRef<T>`s always wrap an `ExternRef` of `RefCell<T>`")
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: 'static> InternalRefBase for HostRef<T> {
|
||||
fn ptr_eq(&self, other: &dyn InternalRefBase) -> bool {
|
||||
if let Some(other) = other.as_any().downcast_ref() {
|
||||
self.ptr_eq(other)
|
||||
impl<T> AsRef<ExternRef> for HostRef<T> {
|
||||
fn as_ref(&self) -> &ExternRef {
|
||||
&self.externref
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<HostRef<T>> for ExternRef
|
||||
where
|
||||
T: 'static + Any,
|
||||
{
|
||||
fn from(host: HostRef<T>) -> ExternRef {
|
||||
host.externref
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> TryFrom<ExternRef> for HostRef<T>
|
||||
where
|
||||
T: 'static + Any,
|
||||
{
|
||||
type Error = ExternRef;
|
||||
|
||||
fn try_from(externref: ExternRef) -> Result<Self, ExternRef> {
|
||||
if externref.inner.is::<RefCell<T>>() {
|
||||
Ok(HostRef {
|
||||
externref,
|
||||
_phantom: PhantomData,
|
||||
})
|
||||
} else {
|
||||
false
|
||||
Err(externref)
|
||||
}
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
|
||||
fn host_info(&self) -> Option<cell::RefMut<Box<dyn Any>>> {
|
||||
let info = cell::RefMut::map(self.0.borrow_mut(), |b| &mut b.host_info);
|
||||
if info.is_none() {
|
||||
return None;
|
||||
}
|
||||
Some(cell::RefMut::map(info, |info| info.as_mut().unwrap()))
|
||||
}
|
||||
|
||||
fn set_host_info(&self, info: Option<Box<dyn Any>>) {
|
||||
self.0.borrow_mut().host_info = info;
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Clone for HostRef<T> {
|
||||
fn clone(&self) -> HostRef<T> {
|
||||
HostRef(self.0.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Debug> fmt::Debug for HostRef<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "Ref(")?;
|
||||
self.0.borrow().content.fmt(f)?;
|
||||
write!(f, ")")
|
||||
HostRef {
|
||||
externref: self.externref.clone(),
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
use crate::externals::MemoryCreator;
|
||||
use crate::r#ref::ExternRef;
|
||||
use crate::trampoline::{MemoryCreatorProxy, StoreInstanceHandle};
|
||||
use anyhow::{bail, Result};
|
||||
use std::any::Any;
|
||||
use std::cell::RefCell;
|
||||
use std::cmp;
|
||||
use std::collections::HashMap;
|
||||
use std::convert::TryFrom;
|
||||
use std::fmt;
|
||||
use std::path::Path;
|
||||
@@ -14,7 +17,7 @@ use wasmtime_environ::{CacheConfig, Tunables};
|
||||
use wasmtime_jit::{native, CompilationStrategy, Compiler};
|
||||
use wasmtime_profiling::{JitDumpAgent, NullProfilerAgent, ProfilingAgent, VTuneAgent};
|
||||
use wasmtime_runtime::{
|
||||
debug_builtins, InstanceHandle, RuntimeMemoryCreator, SignalHandler, VMInterrupts,
|
||||
debug_builtins, InstanceHandle, RuntimeMemoryCreator, SignalHandler, VMExternRef, VMInterrupts,
|
||||
};
|
||||
|
||||
// Runtime Environment
|
||||
@@ -733,6 +736,7 @@ pub(crate) struct StoreInner {
|
||||
compiler: RefCell<Compiler>,
|
||||
instances: RefCell<Vec<InstanceHandle>>,
|
||||
signal_handler: RefCell<Option<Box<SignalHandler<'static>>>>,
|
||||
host_info: RefCell<HashMap<VMExternRef, Rc<RefCell<dyn Any>>>>,
|
||||
}
|
||||
|
||||
impl Store {
|
||||
@@ -758,6 +762,7 @@ impl Store {
|
||||
compiler: RefCell::new(compiler),
|
||||
instances: RefCell::new(Vec::new()),
|
||||
signal_handler: RefCell::new(None),
|
||||
host_info: RefCell::new(HashMap::new()),
|
||||
}),
|
||||
}
|
||||
}
|
||||
@@ -809,6 +814,37 @@ impl Store {
|
||||
Rc::downgrade(&self.inner)
|
||||
}
|
||||
|
||||
pub(crate) fn upgrade(weak: &Weak<StoreInner>) -> Option<Self> {
|
||||
let inner = weak.upgrade()?;
|
||||
Some(Self { inner })
|
||||
}
|
||||
|
||||
pub(crate) fn host_info(&self, externref: &ExternRef) -> Option<Rc<RefCell<dyn Any>>> {
|
||||
debug_assert!(
|
||||
std::rc::Weak::ptr_eq(&self.weak(), &externref.store),
|
||||
"externref must be from this store"
|
||||
);
|
||||
let infos = self.inner.host_info.borrow();
|
||||
infos.get(&externref.inner).cloned()
|
||||
}
|
||||
|
||||
pub(crate) fn set_host_info(
|
||||
&self,
|
||||
externref: &ExternRef,
|
||||
info: Option<Rc<RefCell<dyn Any>>>,
|
||||
) -> Option<Rc<RefCell<dyn Any>>> {
|
||||
debug_assert!(
|
||||
std::rc::Weak::ptr_eq(&self.weak(), &externref.store),
|
||||
"externref must be from this store"
|
||||
);
|
||||
let mut infos = self.inner.host_info.borrow_mut();
|
||||
if let Some(info) = info {
|
||||
infos.insert(externref.inner.clone(), info)
|
||||
} else {
|
||||
infos.remove(&externref.inner)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn signal_handler(&self) -> std::cell::Ref<'_, Option<Box<SignalHandler<'static>>>> {
|
||||
self.inner.signal_handler.borrow()
|
||||
}
|
||||
|
||||
@@ -32,7 +32,12 @@ pub(crate) fn create_handle(
|
||||
.local
|
||||
.signatures
|
||||
.values()
|
||||
.map(|sig| store.compiler().signatures().register(sig))
|
||||
.map(|(wasm, native)| {
|
||||
store
|
||||
.compiler()
|
||||
.signatures()
|
||||
.register(wasm.clone(), native.clone())
|
||||
})
|
||||
.collect::<PrimaryMap<_, _>>();
|
||||
|
||||
unsafe {
|
||||
|
||||
@@ -225,7 +225,10 @@ pub fn create_handle_with_function(
|
||||
|
||||
// First up we manufacture a trampoline which has the ABI specified by `ft`
|
||||
// and calls into `stub_fn`...
|
||||
let sig_id = module.local.signatures.push(sig.clone());
|
||||
let sig_id = module
|
||||
.local
|
||||
.signatures
|
||||
.push((ft.to_wasm_func_type(), sig.clone()));
|
||||
let func_id = module.local.functions.push(sig_id);
|
||||
module
|
||||
.exports
|
||||
@@ -244,7 +247,10 @@ pub fn create_handle_with_function(
|
||||
mem::size_of::<u128>(),
|
||||
)?;
|
||||
assert!(relocations.is_empty());
|
||||
let sig_id = store.compiler().signatures().register(&sig);
|
||||
let sig_id = store
|
||||
.compiler()
|
||||
.signatures()
|
||||
.register(ft.to_wasm_func_type(), sig);
|
||||
trampolines.insert(sig_id, trampoline);
|
||||
|
||||
// Next up we wrap everything up into an `InstanceHandle` by publishing our
|
||||
@@ -285,13 +291,19 @@ pub unsafe fn create_handle_with_raw_function(
|
||||
let mut finished_functions = PrimaryMap::new();
|
||||
let mut trampolines = HashMap::new();
|
||||
|
||||
let sig_id = module.local.signatures.push(sig.clone());
|
||||
let sig_id = module
|
||||
.local
|
||||
.signatures
|
||||
.push((ft.to_wasm_func_type(), sig.clone()));
|
||||
let func_id = module.local.functions.push(sig_id);
|
||||
module
|
||||
.exports
|
||||
.insert("trampoline".to_string(), EntityIndex::Function(func_id));
|
||||
finished_functions.push(func);
|
||||
let sig_id = store.compiler().signatures().register(&sig);
|
||||
let sig_id = store
|
||||
.compiler()
|
||||
.signatures()
|
||||
.register(ft.to_wasm_func_type(), sig);
|
||||
trampolines.insert(sig_id, trampoline);
|
||||
|
||||
create_handle(module, store, finished_functions, trampolines, state)
|
||||
|
||||
@@ -67,6 +67,20 @@ pub enum ValType {
|
||||
FuncRef,
|
||||
}
|
||||
|
||||
impl fmt::Display for ValType {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
ValType::I32 => write!(f, "i32"),
|
||||
ValType::I64 => write!(f, "i64"),
|
||||
ValType::F32 => write!(f, "f32"),
|
||||
ValType::F64 => write!(f, "f64"),
|
||||
ValType::V128 => write!(f, "v128"),
|
||||
ValType::ExternRef => write!(f, "externref"),
|
||||
ValType::FuncRef => write!(f, "funcref"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ValType {
|
||||
/// Returns true if `ValType` matches any of the numeric types. (e.g. `I32`,
|
||||
/// `I64`, `F32`, `F64`).
|
||||
@@ -106,6 +120,31 @@ impl ValType {
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn to_wasm_type(&self) -> wasm::WasmType {
|
||||
match self {
|
||||
Self::I32 => wasm::WasmType::I32,
|
||||
Self::I64 => wasm::WasmType::I64,
|
||||
Self::F32 => wasm::WasmType::F32,
|
||||
Self::F64 => wasm::WasmType::F64,
|
||||
Self::V128 => wasm::WasmType::V128,
|
||||
Self::FuncRef => wasm::WasmType::FuncRef,
|
||||
Self::ExternRef => wasm::WasmType::ExternRef,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn from_wasm_type(ty: &wasm::WasmType) -> Option<Self> {
|
||||
match ty {
|
||||
wasm::WasmType::I32 => Some(Self::I32),
|
||||
wasm::WasmType::I64 => Some(Self::I64),
|
||||
wasm::WasmType::F32 => Some(Self::F32),
|
||||
wasm::WasmType::F64 => Some(Self::F64),
|
||||
wasm::WasmType::V128 => Some(Self::V128),
|
||||
wasm::WasmType::FuncRef => Some(Self::FuncRef),
|
||||
wasm::WasmType::ExternRef => Some(Self::ExternRef),
|
||||
wasm::WasmType::Func | wasm::WasmType::EmptyBlockType => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// External Types
|
||||
@@ -184,12 +223,6 @@ impl From<TableType> for ExternType {
|
||||
}
|
||||
}
|
||||
|
||||
// Function Types
|
||||
fn from_wasmtime_abiparam(param: &ir::AbiParam) -> Option<ValType> {
|
||||
assert_eq!(param.purpose, ir::ArgumentPurpose::Normal);
|
||||
ValType::from_wasmtime_type(param.value_type)
|
||||
}
|
||||
|
||||
/// A descriptor for a function in a WebAssembly module.
|
||||
///
|
||||
/// WebAssembly functions can have 0 or more parameters and results.
|
||||
@@ -218,6 +251,13 @@ impl FuncType {
|
||||
&self.results
|
||||
}
|
||||
|
||||
pub(crate) fn to_wasm_func_type(&self) -> wasm::WasmFuncType {
|
||||
wasm::WasmFuncType {
|
||||
params: self.params.iter().map(|p| p.to_wasm_type()).collect(),
|
||||
returns: self.results.iter().map(|r| r.to_wasm_type()).collect(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `Some` if this function signature was compatible with cranelift,
|
||||
/// or `None` if one of the types/results wasn't supported or compatible
|
||||
/// with cranelift.
|
||||
@@ -251,17 +291,16 @@ impl FuncType {
|
||||
/// Returns `None` if any types in the signature can't be converted to the
|
||||
/// types in this crate, but that should very rarely happen and largely only
|
||||
/// indicate a bug in our cranelift integration.
|
||||
pub(crate) fn from_wasmtime_signature(signature: &ir::Signature) -> Option<FuncType> {
|
||||
pub(crate) fn from_wasm_func_type(signature: &wasm::WasmFuncType) -> Option<FuncType> {
|
||||
let params = signature
|
||||
.params
|
||||
.iter()
|
||||
.skip(2) // skip the caller/callee vmctx
|
||||
.map(|p| from_wasmtime_abiparam(p))
|
||||
.map(|p| ValType::from_wasm_type(p))
|
||||
.collect::<Option<Vec<_>>>()?;
|
||||
let results = signature
|
||||
.returns
|
||||
.iter()
|
||||
.map(|p| from_wasmtime_abiparam(p))
|
||||
.map(|r| ValType::from_wasm_type(r))
|
||||
.collect::<Option<Vec<_>>>()?;
|
||||
Some(FuncType {
|
||||
params: params.into_boxed_slice(),
|
||||
@@ -390,7 +429,7 @@ impl MemoryType {
|
||||
|
||||
#[derive(Clone, Hash, Eq, PartialEq)]
|
||||
pub(crate) enum EntityType<'module> {
|
||||
Function(&'module ir::Signature),
|
||||
Function(&'module wasm::WasmFuncType),
|
||||
Table(&'module wasm::Table),
|
||||
Memory(&'module wasm::Memory),
|
||||
Global(&'module wasm::Global),
|
||||
@@ -404,7 +443,7 @@ impl<'module> EntityType<'module> {
|
||||
) -> EntityType<'module> {
|
||||
match entity_index {
|
||||
EntityIndex::Function(func_index) => {
|
||||
let sig = module.local.func_signature(*func_index);
|
||||
let sig = module.local.wasm_func_type(*func_index);
|
||||
EntityType::Function(&sig)
|
||||
}
|
||||
EntityIndex::Table(table_index) => {
|
||||
@@ -422,7 +461,7 @@ impl<'module> EntityType<'module> {
|
||||
/// Convert this `EntityType` to an `ExternType`.
|
||||
pub(crate) fn extern_type(&self) -> ExternType {
|
||||
match self {
|
||||
EntityType::Function(sig) => FuncType::from_wasmtime_signature(sig)
|
||||
EntityType::Function(sig) => FuncType::from_wasm_func_type(sig)
|
||||
.expect("core wasm function type should be supported")
|
||||
.into(),
|
||||
EntityType::Table(table) => TableType::from_wasmtime_table(table).into(),
|
||||
|
||||
@@ -2,6 +2,7 @@ use crate::r#ref::ExternRef;
|
||||
use crate::{Func, Store, ValType};
|
||||
use anyhow::{bail, Result};
|
||||
use std::ptr;
|
||||
use wasmtime_runtime::VMExternRef;
|
||||
|
||||
/// Possible runtime values that a WebAssembly module can either consume or
|
||||
/// produce.
|
||||
@@ -26,9 +27,7 @@ pub enum Val {
|
||||
F64(u64),
|
||||
|
||||
/// An `externref` value which can hold opaque data to the wasm instance itself.
|
||||
///
|
||||
/// Note that this is a nullable value as well.
|
||||
ExternRef(ExternRef),
|
||||
ExternRef(Option<ExternRef>),
|
||||
|
||||
/// A first-class reference to a WebAssembly function.
|
||||
FuncRef(Func),
|
||||
@@ -87,18 +86,31 @@ impl Val {
|
||||
Val::F32(u) => ptr::write(p as *mut u32, *u),
|
||||
Val::F64(u) => ptr::write(p as *mut u64, *u),
|
||||
Val::V128(b) => ptr::write(p as *mut u128, *b),
|
||||
Val::ExternRef(None) => ptr::write(p, 0),
|
||||
Val::ExternRef(Some(x)) => ptr::write(p as *mut *mut u8, x.inner.clone().into_raw()),
|
||||
_ => unimplemented!("Val::write_value_to"),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn read_value_from(p: *const u128, ty: &ValType) -> Val {
|
||||
pub(crate) unsafe fn read_value_from(store: &Store, p: *const u128, ty: &ValType) -> Val {
|
||||
match ty {
|
||||
ValType::I32 => Val::I32(ptr::read(p as *const i32)),
|
||||
ValType::I64 => Val::I64(ptr::read(p as *const i64)),
|
||||
ValType::F32 => Val::F32(ptr::read(p as *const u32)),
|
||||
ValType::F64 => Val::F64(ptr::read(p as *const u64)),
|
||||
ValType::V128 => Val::V128(ptr::read(p as *const u128)),
|
||||
_ => unimplemented!("Val::read_value_from"),
|
||||
ValType::ExternRef => {
|
||||
let raw = ptr::read(p as *const *mut u8);
|
||||
if raw.is_null() {
|
||||
Val::ExternRef(None)
|
||||
} else {
|
||||
Val::ExternRef(Some(ExternRef {
|
||||
inner: VMExternRef::from_raw(raw),
|
||||
store: store.weak(),
|
||||
}))
|
||||
}
|
||||
}
|
||||
_ => unimplemented!("Val::read_value_from: {:?}", ty),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -112,24 +124,31 @@ impl Val {
|
||||
(V128(u128) v128 unwrap_v128 *e)
|
||||
}
|
||||
|
||||
/// Attempt to access the underlying value of this `Val`, returning
|
||||
/// `None` if it is not the correct type.
|
||||
/// Attempt to access the underlying `externref` value of this `Val`.
|
||||
///
|
||||
/// This will return `Some` for both the `ExternRef` and `FuncRef` types.
|
||||
pub fn externref(&self) -> Option<ExternRef> {
|
||||
/// If this is not an `externref`, then `None` is returned.
|
||||
///
|
||||
/// If this is a null `externref`, then `Some(None)` is returned.
|
||||
///
|
||||
/// If this is a non-null `externref`, then `Some(Some(..))` is returned.
|
||||
pub fn externref(&self) -> Option<Option<ExternRef>> {
|
||||
match self {
|
||||
Val::ExternRef(e) => Some(e.clone()),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the underlying value of this `Val`, panicking if it's the
|
||||
/// Returns the underlying `externref` value of this `Val`, panicking if it's the
|
||||
/// wrong type.
|
||||
///
|
||||
/// If this is a null `externref`, then `None` is returned.
|
||||
///
|
||||
/// If this is a non-null `externref`, then `Some(..)` is returned.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if `self` is not of the right type.
|
||||
pub fn unwrap_externref(&self) -> ExternRef {
|
||||
/// Panics if `self` is not a (nullable) `externref`.
|
||||
pub fn unwrap_externref(&self) -> Option<ExternRef> {
|
||||
self.externref().expect("expected externref")
|
||||
}
|
||||
|
||||
@@ -140,8 +159,8 @@ impl Val {
|
||||
// TODO: need to implement this once we actually finalize what
|
||||
// `externref` will look like and it's actually implemented to pass it
|
||||
// to compiled wasm as well.
|
||||
Val::ExternRef(ExternRef::Ref(_)) | Val::ExternRef(ExternRef::Other(_)) => false,
|
||||
Val::ExternRef(ExternRef::Null) => true,
|
||||
Val::ExternRef(Some(e)) => e.store.ptr_eq(&store.weak()),
|
||||
Val::ExternRef(None) => true,
|
||||
|
||||
// Integers have no association with any particular store, so
|
||||
// they're always considered as "yes I came from that store",
|
||||
@@ -176,6 +195,12 @@ impl From<f64> for Val {
|
||||
|
||||
impl From<ExternRef> for Val {
|
||||
fn from(val: ExternRef) -> Val {
|
||||
Val::ExternRef(Some(val))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Option<ExternRef>> for Val {
|
||||
fn from(val: Option<ExternRef>) -> Val {
|
||||
Val::ExternRef(val)
|
||||
}
|
||||
}
|
||||
@@ -194,7 +219,7 @@ pub(crate) fn into_checked_anyfunc(
|
||||
bail!("cross-`Store` values are not supported");
|
||||
}
|
||||
Ok(match val {
|
||||
Val::ExternRef(ExternRef::Null) => wasmtime_runtime::VMCallerCheckedAnyfunc {
|
||||
Val::ExternRef(None) => wasmtime_runtime::VMCallerCheckedAnyfunc {
|
||||
func_ptr: ptr::null(),
|
||||
type_index: wasmtime_runtime::VMSharedSignatureIndex::default(),
|
||||
vmctx: ptr::null_mut(),
|
||||
@@ -216,7 +241,7 @@ pub(crate) fn from_checked_anyfunc(
|
||||
store: &Store,
|
||||
) -> Val {
|
||||
if item.type_index == wasmtime_runtime::VMSharedSignatureIndex::default() {
|
||||
return Val::ExternRef(ExternRef::Null);
|
||||
return Val::ExternRef(None);
|
||||
}
|
||||
let instance_handle = unsafe { wasmtime_runtime::InstanceHandle::from_vmctx(item.vmctx) };
|
||||
let export = wasmtime_runtime::ExportFunction {
|
||||
|
||||
Reference in New Issue
Block a user