Implement RFC 11: Redesigning Wasmtime's APIs (#2897)

Implement Wasmtime's new API as designed by RFC 11. This is quite a large commit which has had lots of discussion externally, so for more information it's best to read the RFC thread and the PR thread.
This commit is contained in:
Alex Crichton
2021-06-03 09:10:53 -05:00
committed by GitHub
parent a5a28b1c5b
commit 7a1b7cdf92
233 changed files with 13349 additions and 11997 deletions

View File

@@ -1,16 +1,12 @@
use crate::memory::MemoryCreator;
use crate::trampoline::MemoryCreatorProxy;
use crate::{func::HostFunc, Caller, FuncType, IntoFunc, Trap, Val, WasmRet, WasmTy};
use anyhow::{bail, Result};
use serde::{Deserialize, Serialize};
use std::cmp;
use std::collections::HashMap;
use std::convert::TryFrom;
use std::fmt;
use std::future::Future;
#[cfg(feature = "cache")]
use std::path::Path;
use std::pin::Pin;
use std::sync::Arc;
use wasmparser::WasmFeatures;
#[cfg(feature = "cache")]
@@ -273,97 +269,6 @@ impl Default for InstanceAllocationStrategy {
}
}
/// This type is used for storing host functions in a `Config`.
///
/// The module and function names are interned for more compact storage.
#[derive(Clone)]
struct HostFuncMap {
index_map: HashMap<Arc<str>, usize>,
strings: Vec<Arc<str>>,
funcs: HashMap<(usize, usize), (Arc<HostFunc>, bool)>,
}
impl HostFuncMap {
fn new() -> Self {
Self {
index_map: HashMap::new(),
strings: Vec::new(),
funcs: HashMap::new(),
}
}
fn insert(&mut self, module: &str, name: &str, async_required: bool, func: HostFunc) {
let key = (self.intern_str(module), self.intern_str(name));
self.funcs.insert(key, (Arc::new(func), async_required));
}
fn get(&self, module: &str, name: &str) -> Option<&HostFunc> {
let key = (
self.index_map.get(module).cloned()?,
self.index_map.get(name).cloned()?,
);
self.funcs.get(&key).map(|f| f.0.as_ref())
}
fn intern_str(&mut self, string: &str) -> usize {
if let Some(idx) = self.index_map.get(string) {
return *idx;
}
let string: Arc<str> = string.into();
let idx = self.strings.len();
self.strings.push(string.clone());
self.index_map.insert(string, idx);
idx
}
fn async_required(&self) -> bool {
self.funcs.values().any(|f| f.1)
}
fn iter(&self) -> impl Iterator<Item = &HostFunc> {
self.funcs.values().map(|v| &*v.0)
}
}
macro_rules! generate_wrap_async_host_func {
($num:tt $($args:ident)*) => (paste::paste!{
/// Same as [`Config::wrap_host_func`], except the closure asynchronously produces
/// its result. For more information see the [`Func`](crate::Func) documentation.
///
/// Note: creating an engine will fail if an async host function is defined and
/// [async support](Config::async_support) is not enabled.
#[allow(non_snake_case)]
#[cfg(feature = "async")]
#[cfg_attr(nightlydoc, doc(cfg(feature = "async")))]
pub fn [<wrap $num _host_func_async>]<$($args,)* R>(
&mut self,
module: &str,
name: &str,
func: impl for <'a> Fn(Caller<'a>, $($args),*) -> Box<dyn Future<Output = R> + 'a> + Send + Sync + 'static,
)
where
$($args: WasmTy,)*
R: WasmRet,
{
// Defer the check for async support until engine creation time to not introduce an order dependency
self.host_funcs.insert(
module,
name,
true,
HostFunc::wrap(move |caller: Caller<'_>, $($args: $args),*| {
let store = caller.store().clone();
debug_assert!(store.async_support());
let mut future = Pin::from(func(caller, $($args),*));
match store.block_on(future.as_mut()) {
Ok(ret) => ret.into_fallible(),
Err(e) => R::fallible_from_trap(e),
}
})
);
}
})
}
/// Global configuration options used to create an [`Engine`](crate::Engine)
/// and customize its behavior.
///
@@ -385,7 +290,6 @@ pub struct Config {
pub(crate) wasm_backtrace_details_env_used: bool,
#[cfg(feature = "async")]
pub(crate) async_stack_size: usize,
host_funcs: HostFuncMap,
pub(crate) async_support: bool,
}
@@ -421,7 +325,6 @@ impl Config {
features: WasmFeatures::default(),
#[cfg(feature = "async")]
async_stack_size: 2 << 20,
host_funcs: HostFuncMap::new(),
async_support: false,
};
ret.cranelift_debug_verifier(false);
@@ -1190,107 +1093,6 @@ impl Config {
self
}
/// Defines a host function for the [`Config`] for the given callback.
///
/// Use [`Store::get_host_func`](crate::Store::get_host_func) to get a [`Func`](crate::Func) representing the function.
///
/// Note that the implementation of `func` must adhere to the `ty`
/// signature given, error or traps may occur if it does not respect the
/// `ty` signature.
///
/// Additionally note that this is quite a dynamic function since signatures
/// are not statically known. For performance reasons, it's recommended
/// to use [`Config::wrap_host_func`] if you can because with statically known
/// signatures the engine can optimize the implementation much more.
///
/// The callback must be `Send` and `Sync` as it is shared between all engines created
/// from the `Config`. For more relaxed bounds, use [`Func::new`](crate::Func::new) to define the function.
pub fn define_host_func(
&mut self,
module: &str,
name: &str,
ty: FuncType,
func: impl Fn(Caller<'_>, &[Val], &mut [Val]) -> Result<(), Trap> + Send + Sync + 'static,
) {
self.host_funcs
.insert(module, name, false, HostFunc::new(self, ty, func));
}
/// Defines an async host function for the [`Config`] for the given callback.
///
/// Use [`Store::get_host_func`](crate::Store::get_host_func) to get a [`Func`](crate::Func) representing the function.
///
/// This function is the asynchronous analogue of [`Config::define_host_func`] and much of
/// that documentation applies to this as well.
///
/// Additionally note that this is quite a dynamic function since signatures
/// are not statically known. For performance reasons, it's recommended
/// to use `Config::wrap$N_host_func_async` if you can because with statically known
/// signatures the engine can optimize the implementation much more.
///
/// The callback must be `Send` and `Sync` as it is shared between all engines created
/// from the `Config`. For more relaxed bounds, use [`Func::new_async`](crate::Func::new_async) to define the function.
///
/// Note: creating an engine will fail if an async host function is defined and [async support](Config::async_support)
/// is not enabled.
#[cfg(feature = "async")]
#[cfg_attr(nightlydoc, doc(cfg(feature = "async")))]
pub fn define_host_func_async<F>(&mut self, module: &str, name: &str, ty: FuncType, func: F)
where
F: for<'a> Fn(
Caller<'a>,
&'a [Val],
&'a mut [Val],
) -> Box<dyn Future<Output = Result<(), Trap>> + 'a>
+ Send
+ Sync
+ 'static,
{
// Defer the check for async support until engine creation time to not introduce an order dependency
self.host_funcs.insert(
module,
name,
true,
HostFunc::new(self, ty, move |caller, params, results| {
let store = caller.store().clone();
debug_assert!(store.async_support());
let mut future = Pin::from(func(caller, params, results));
match store.block_on(future.as_mut()) {
Ok(Ok(())) => Ok(()),
Ok(Err(trap)) | Err(trap) => Err(trap),
}
}),
);
}
/// Defines a host function for the [`Config`] from the given Rust closure.
///
/// Use [`Store::get_host_func`](crate::Store::get_host_func) to get a [`Func`](crate::Func) representing the function.
///
/// See [`Func::wrap`](crate::Func::wrap) for information about accepted parameter and result types for the closure.
///
/// The closure must be `Send` and `Sync` as it is shared between all engines created
/// from the `Config`. For more relaxed bounds, use [`Func::wrap`](crate::Func::wrap) to wrap the closure.
pub fn wrap_host_func<Params, Results>(
&mut self,
module: &str,
name: &str,
func: impl IntoFunc<Params, Results> + Send + Sync,
) {
self.host_funcs
.insert(module, name, false, HostFunc::wrap(func));
}
for_each_function_signature!(generate_wrap_async_host_func);
pub(crate) fn host_funcs(&self) -> impl Iterator<Item = &HostFunc> {
self.host_funcs.iter()
}
pub(crate) fn get_host_func(&self, module: &str, name: &str) -> Option<&HostFunc> {
self.host_funcs.get(module, name)
}
pub(crate) fn target_isa(&self) -> Box<dyn TargetIsa> {
self.isa_flags
.clone()
@@ -1303,18 +1105,6 @@ impl Config {
self.isa_flags.clone().finish(settings::Flags::new(flags))
}
pub(crate) fn validate(&self) -> Result<()> {
// This is used to validate that the config is internally consistent prior to
// creating an engine using this config.
// Check that there isn't a host function defined that requires async support enabled
if self.host_funcs.async_required() && !self.async_support {
bail!("an async host function cannot be defined without async support enabled in the config");
}
Ok(())
}
pub(crate) fn build_compiler(&self, allocator: &dyn InstanceAllocator) -> Compiler {
let isa = self.target_isa();
let mut tunables = self.tunables.clone();

View File

@@ -1,47 +1,17 @@
use crate::signatures::{SignatureCollection, SignatureRegistry};
use crate::signatures::SignatureRegistry;
use crate::Config;
use anyhow::Result;
use std::collections::HashMap;
use std::sync::Arc;
#[cfg(feature = "cache")]
use wasmtime_cache::CacheConfig;
use wasmtime_jit::Compiler;
use wasmtime_runtime::{debug_builtins, InstanceAllocator, InstanceHandle, VMCallerCheckedAnyfunc};
/// This is used as a Send+Sync wrapper around two data structures relating to
/// host functions defined on `Config`:
///
/// * `anyfuncs` - this stores a mapping between the host function instance and
/// a `VMCallerCheckedAnyfunc` that can be used as the function's value in Wasmtime's ABI.
/// The address of the anyfunc needs to be stable, thus the boxed value.
///
/// * `signatures` - this stores the collection of shared signatures registered for every
/// usable host functions with this engine.
struct EngineHostFuncs {
anyfuncs: HashMap<InstanceHandle, Box<VMCallerCheckedAnyfunc>>,
signatures: SignatureCollection,
}
impl EngineHostFuncs {
fn new(registry: &SignatureRegistry) -> Self {
Self {
anyfuncs: HashMap::new(),
signatures: SignatureCollection::new(registry),
}
}
}
// This is safe for send and sync as it is read-only once the
// engine is constructed and the host functions live with the config,
// which the engine keeps a strong reference to.
unsafe impl Send for EngineHostFuncs {}
unsafe impl Sync for EngineHostFuncs {}
use wasmtime_runtime::{debug_builtins, InstanceAllocator};
/// An `Engine` which is a global context for compilation and management of wasm
/// modules.
///
/// An engine can be safely shared across threads and is a cheap cloneable
/// handle to the actual engine. The engine itself will be deallocate once all
/// handle to the actual engine. The engine itself will be deallocated once all
/// references to it have gone away.
///
/// Engines store global configuration preferences such as compilation settings,
@@ -69,31 +39,19 @@ struct EngineInner {
compiler: Compiler,
allocator: Box<dyn InstanceAllocator>,
signatures: SignatureRegistry,
host_funcs: EngineHostFuncs,
}
impl Engine {
/// Creates a new [`Engine`] with the specified compilation and
/// configuration settings.
pub fn new(config: &Config) -> Result<Engine> {
// Ensure that wasmtime_runtime's signal handlers are configured. This
// is the per-program initialization required for handling traps, such
// as configuring signals, vectored exception handlers, etc.
wasmtime_runtime::init_traps(crate::module::GlobalModuleRegistry::is_wasm_pc);
debug_builtins::ensure_exported();
config.validate()?;
let allocator = config.build_allocator()?;
let registry = SignatureRegistry::new();
let mut host_funcs = EngineHostFuncs::new(&registry);
// Register all the host function signatures with the collection
for func in config.host_funcs() {
let sig = host_funcs
.signatures
.register(func.ty.as_wasm_func_type(), func.trampoline);
// Cloning the instance handle is safe as host functions outlive the engine
host_funcs.anyfuncs.insert(
unsafe { func.instance.clone() },
Box::new(func.anyfunc(sig)),
);
}
Ok(Engine {
inner: Arc::new(EngineInner {
@@ -101,7 +59,6 @@ impl Engine {
compiler: config.build_compiler(allocator.as_ref()),
allocator,
signatures: registry,
host_funcs,
}),
})
}
@@ -134,21 +91,6 @@ impl Engine {
&self.inner.signatures
}
pub(crate) fn host_func_signatures(&self) -> &SignatureCollection {
&self.inner.host_funcs.signatures
}
pub(crate) fn host_func_anyfunc(
&self,
instance: &InstanceHandle,
) -> Option<&VMCallerCheckedAnyfunc> {
self.inner
.host_funcs
.anyfuncs
.get(instance)
.map(AsRef::as_ref)
}
/// Ahead-of-time (AOT) compiles a WebAssembly module.
///
/// The `bytes` provided must be in one of two formats:

View File

@@ -1,14 +1,13 @@
use crate::memory::Memory;
use crate::trampoline::{generate_global_export, generate_table_export, StoreInstanceHandle};
use crate::values::{from_checked_anyfunc, into_checked_anyfunc, Val};
use crate::store::{StoreData, StoreOpaque, Stored};
use crate::trampoline::{generate_global_export, generate_table_export};
use crate::values::{from_checked_anyfunc, into_checked_anyfunc};
use crate::{
ExternRef, ExternType, Func, GlobalType, Instance, Module, Mutability, Store, TableType, Trap,
ValType,
AsContext, AsContextMut, ExternRef, ExternType, Func, GlobalType, Instance, Memory, Module,
Mutability, TableType, Trap, Val, ValType,
};
use anyhow::{anyhow, bail, Result};
use std::mem;
use std::ptr;
use wasmtime_environ::wasm;
use wasmtime_runtime::{self as runtime, InstanceHandle};
// Externals
@@ -27,7 +26,7 @@ pub enum Extern {
/// A WebAssembly `global` which acts like a `Cell<T>` of sorts, supporting
/// `get` and `set` operations.
Global(Global),
/// A WebAssembly `table` which is an array of `Val` types.
/// A WebAssembly `table` which is an array of `Val` reference types.
Table(Table),
/// A WebAssembly linear memory.
Memory(Memory),
@@ -99,20 +98,28 @@ impl Extern {
}
/// Returns the type associated with this `Extern`.
pub fn ty(&self) -> ExternType {
///
/// The `store` argument provided must own this `Extern` and is used to look
/// up type information.
///
/// # Panics
///
/// Panics if this item does not belong to the `store` provided.
pub fn ty(&self, store: impl AsContext) -> ExternType {
let store = store.as_context();
match self {
Extern::Func(ft) => ExternType::Func(ft.ty()),
Extern::Memory(ft) => ExternType::Memory(ft.ty()),
Extern::Table(tt) => ExternType::Table(tt.ty()),
Extern::Global(gt) => ExternType::Global(gt.ty()),
Extern::Instance(i) => ExternType::Instance(i.ty()),
Extern::Func(ft) => ExternType::Func(ft.ty(store)),
Extern::Memory(ft) => ExternType::Memory(ft.ty(store)),
Extern::Table(tt) => ExternType::Table(tt.ty(store)),
Extern::Global(gt) => ExternType::Global(gt.ty(store)),
Extern::Instance(i) => ExternType::Instance(i.ty(store)),
Extern::Module(m) => ExternType::Module(m.ty()),
}
}
pub(crate) unsafe fn from_wasmtime_export(
wasmtime_export: &wasmtime_runtime::Export,
store: &Store,
wasmtime_export: wasmtime_runtime::Export,
store: &mut StoreOpaque<'_>,
) -> Extern {
match wasmtime_export {
wasmtime_runtime::Export::Function(f) => {
@@ -127,27 +134,20 @@ impl Extern {
wasmtime_runtime::Export::Table(t) => {
Extern::Table(Table::from_wasmtime_table(t, store))
}
wasmtime_runtime::Export::Instance(i) => {
Extern::Instance(Instance::from_wasmtime(i, store))
}
wasmtime_runtime::Export::Module(m) => {
Extern::Module(m.downcast_ref::<Module>().unwrap().clone())
}
}
}
pub(crate) fn comes_from_same_store(&self, store: &Store) -> bool {
let my_store = match self {
Extern::Func(f) => f.store(),
Extern::Global(g) => &g.instance.store,
Extern::Memory(m) => &m.instance.store,
Extern::Table(t) => &t.instance.store,
Extern::Instance(i) => i.store(),
pub(crate) fn comes_from_same_store(&self, store: &StoreOpaque<'_>) -> bool {
match self {
Extern::Func(f) => f.comes_from_same_store(store),
Extern::Global(g) => store.store_data().contains(g.0),
Extern::Memory(m) => m.comes_from_same_store(store),
Extern::Table(t) => store.store_data().contains(t.0),
Extern::Instance(i) => i.comes_from_same_store(store),
// Modules don't live in stores right now, so they're compatible
// with all stores.
Extern::Module(_) => return true,
};
Store::same(my_store, store)
Extern::Module(_) => true,
}
}
pub(crate) fn desc(&self) -> &'static str {
@@ -160,17 +160,6 @@ impl Extern {
Extern::Module(_) => "module",
}
}
pub(crate) fn wasmtime_export(&self) -> wasmtime_runtime::Export {
match self {
Extern::Func(f) => f.wasmtime_export().clone().into(),
Extern::Global(f) => f.wasmtime_export().clone().into(),
Extern::Table(f) => f.wasmtime_export().clone().into(),
Extern::Memory(f) => f.wasmtime_export().clone().into(),
Extern::Instance(f) => wasmtime_runtime::Export::Instance(f.wasmtime_export().clone()),
Extern::Module(f) => wasmtime_runtime::Export::Module(Box::new(f.clone())),
}
}
}
impl From<Func> for Extern {
@@ -216,72 +205,97 @@ impl From<Module> for Extern {
/// instructions will modify and read global values in a wasm module. Globals
/// can either be imported or exported from wasm modules.
///
/// If you're familiar with Rust already you can think of a `Global` as a sort
/// of `Rc<Cell<Val>>`, more or less.
///
/// # `Global` and `Clone`
///
/// Globals are internally reference counted so you can `clone` a `Global`. The
/// cloning process only performs a shallow clone, so two cloned `Global`
/// instances are equivalent in their functionality.
#[derive(Clone)]
pub struct Global {
instance: StoreInstanceHandle,
wasmtime_export: wasmtime_runtime::ExportGlobal,
}
/// A [`Global`] "belongs" to the store that it was originally created within
/// (either via [`Global::new`] or via instantiating a [`Module`]). Operations
/// on a [`Global`] only work with the store it belongs to, and if another store
/// is passed in by accident then methods will panic.
#[derive(Copy, Clone, Debug)]
#[repr(transparent)] // here for the C API
pub struct Global(Stored<wasmtime_runtime::ExportGlobal>);
impl Global {
/// Creates a new WebAssembly `global` value with the provide type `ty` and
/// initial value `val`.
///
/// The `store` argument provided is used as a general global cache for
/// information, and otherwise the `ty` and `val` arguments are used to
/// initialize the global.
/// The `store` argument will be the owner of the [`Global`] returned. Using
/// the returned [`Global`] other items in the store may access this global.
/// For example this could be provided as an argument to
/// [`Instance::new`](crate::Instance::new) or
/// [`Linker::define`](crate::Linker::define).
///
/// # Errors
///
/// Returns an error if the `ty` provided does not match the type of the
/// value `val`.
pub fn new(store: &Store, ty: GlobalType, val: Val) -> Result<Global> {
/// value `val`, or if `val` comes from a different store than `store`.
///
/// # Examples
///
/// ```
/// # use wasmtime::*;
/// # fn main() -> anyhow::Result<()> {
/// let engine = Engine::default();
/// let mut store = Store::new(&engine, ());
///
/// let ty = GlobalType::new(ValType::I32, Mutability::Const);
/// let i32_const = Global::new(&mut store, ty, 1i32.into())?;
/// let ty = GlobalType::new(ValType::F64, Mutability::Var);
/// let f64_mut = Global::new(&mut store, ty, 2.0f64.into())?;
///
/// let module = Module::new(
/// &engine,
/// "(module
/// (global (import \"\" \"i32-const\") i32)
/// (global (import \"\" \"f64-mut\") (mut f64))
/// )"
/// )?;
///
/// let mut linker = Linker::new(&engine);
/// linker.define("", "i32-const", i32_const)?;
/// linker.define("", "f64-mut", f64_mut)?;
///
/// let instance = linker.instantiate(&mut store, &module)?;
/// // ...
/// # Ok(())
/// # }
/// ```
pub fn new(mut store: impl AsContextMut, ty: GlobalType, val: Val) -> Result<Global> {
Global::_new(&mut store.as_context_mut().opaque(), ty, val)
}
fn _new(store: &mut StoreOpaque<'_>, ty: GlobalType, val: Val) -> Result<Global> {
if !val.comes_from_same_store(store) {
bail!("cross-`Store` globals are not supported");
}
if val.ty() != *ty.content() {
bail!("value provided does not match the type of this global");
}
let (instance, wasmtime_export) = generate_global_export(store, &ty, val)?;
Ok(Global {
instance,
wasmtime_export,
})
}
/// Returns the underlying type of this `global`.
pub fn ty(&self) -> GlobalType {
// The original export is coming from wasmtime_runtime itself we should
// support all the types coming out of it, so assert such here.
GlobalType::from_wasmtime_global(&self.wasmtime_export.global)
}
/// Returns the value type of this `global`.
pub fn val_type(&self) -> ValType {
ValType::from_wasm_type(&self.wasmtime_export.global.wasm_ty)
}
/// Returns the underlying mutability of this `global`.
pub fn mutability(&self) -> Mutability {
if self.wasmtime_export.global.mutability {
Mutability::Var
} else {
Mutability::Const
unsafe {
let wasmtime_export = generate_global_export(store, &ty, val)?;
Ok(Global::from_wasmtime_global(wasmtime_export, store))
}
}
/// Returns the underlying type of this `global`.
///
/// # Panics
///
/// Panics if `store` does not own this global.
pub fn ty(&self, store: impl AsContext) -> GlobalType {
let store = store.as_context();
let ty = &store[self.0].global;
GlobalType::from_wasmtime_global(&ty)
}
/// Returns the current [`Val`] of this global.
pub fn get(&self) -> Val {
///
/// # Panics
///
/// Panics if `store` does not own this global.
pub fn get(&self, mut store: impl AsContextMut) -> Val {
unsafe {
let definition = &mut *self.wasmtime_export.definition;
match self.val_type() {
let store = store.as_context_mut();
let definition = &*store[self.0].definition;
match self.ty(&store).content() {
ValType::I32 => Val::from(*definition.as_i32()),
ValType::I64 => Val::from(*definition.as_i64()),
ValType::F32 => Val::F32(*definition.as_u32()),
@@ -293,7 +307,7 @@ impl Global {
.map(|inner| ExternRef { inner }),
),
ValType::FuncRef => {
from_checked_anyfunc(definition.as_anyfunc() as *mut _, &self.instance.store)
from_checked_anyfunc(definition.as_anyfunc() as *mut _, &mut store.opaque())
}
ty => unimplemented!("Global::get for {:?}", ty),
}
@@ -304,21 +318,29 @@ impl Global {
///
/// # Errors
///
/// Returns an error if this global has a different type than `Val`, or if
/// it's not a mutable global.
pub fn set(&self, val: Val) -> Result<()> {
if self.mutability() != Mutability::Var {
/// Returns an error if this global has a different type than `Val`, if
/// it's not a mutable global, or if `val` comes from a different store than
/// the one provided.
///
/// # Panics
///
/// Panics if `store` does not own this global.
pub fn set(&self, mut store: impl AsContextMut, val: Val) -> Result<()> {
let store = store.as_context_mut();
let ty = self.ty(&store);
if ty.mutability() != Mutability::Var {
bail!("immutable global cannot be set");
}
let ty = self.val_type();
if val.ty() != ty {
let ty = ty.content();
if val.ty() != *ty {
bail!("global of type {:?} cannot be set to {:?}", ty, val.ty());
}
if !val.comes_from_same_store(&self.instance.store) {
let mut store = store.opaque();
if !val.comes_from_same_store(&store) {
bail!("cross-`Store` values are not supported");
}
unsafe {
let definition = &mut *self.wasmtime_export.definition;
let definition = &mut *store[self.0].definition;
match val {
Val::I32(i) => *definition.as_i32_mut() = i,
Val::I64(i) => *definition.as_i64_mut() = i,
@@ -326,14 +348,10 @@ impl Global {
Val::F64(f) => *definition.as_u64_mut() = f,
Val::FuncRef(f) => {
*definition.as_anyfunc_mut() = f.map_or(ptr::null(), |f| {
f.caller_checked_anyfunc().as_ptr() as *const _
f.caller_checked_anyfunc(&mut store).as_ptr() as *const _
});
}
Val::ExternRef(x) => {
// In case the old value's `Drop` implementation is
// re-entrant and tries to touch this global again, do a
// replace, and then drop. This way no one can observe a
// halfway-deinitialized value.
let old = mem::replace(definition.as_externref_mut(), x.map(|x| x.inner));
drop(old);
}
@@ -344,66 +362,45 @@ impl Global {
}
pub(crate) unsafe fn from_wasmtime_global(
wasmtime_export: &wasmtime_runtime::ExportGlobal,
store: &Store,
wasmtime_export: wasmtime_runtime::ExportGlobal,
store: &mut StoreOpaque<'_>,
) -> Global {
Global {
instance: store.existing_vmctx(wasmtime_export.vmctx),
wasmtime_export: wasmtime_export.clone(),
}
Global(store.store_data_mut().insert(wasmtime_export))
}
pub(crate) fn wasmtime_ty(&self) -> &wasmtime_environ::wasm::Global {
&self.wasmtime_export.global
pub(crate) fn wasmtime_ty<'a>(
&self,
data: &'a StoreData,
) -> &'a wasmtime_environ::wasm::Global {
&data[self.0].global
}
pub(crate) fn vmimport(&self) -> wasmtime_runtime::VMGlobalImport {
pub(crate) fn vmimport(&self, store: &StoreOpaque<'_>) -> wasmtime_runtime::VMGlobalImport {
wasmtime_runtime::VMGlobalImport {
from: self.wasmtime_export.definition,
from: store[self.0].definition,
}
}
pub(crate) fn wasmtime_export(&self) -> &wasmtime_runtime::ExportGlobal {
&self.wasmtime_export
}
}
/// A WebAssembly `table`, or an array of values.
///
/// Like [`Memory`] a table is an indexed array of values, but unlike [`Memory`]
/// it's an array of WebAssembly values rather than bytes. One of the most
/// common usages of a table is a function table for wasm modules, where each
/// element has the `Func` type.
/// it's an array of WebAssembly reference type values rather than bytes. One of
/// the most common usages of a table is a function table for wasm modules (a
/// `funcref` table), where each element has the `ValType::FuncRef` type.
///
/// Tables, like globals, are not threadsafe and can only be used on one thread.
/// Tables can be grown in size and each element can be read/written.
///
/// # `Table` and `Clone`
///
/// Tables are internally reference counted so you can `clone` a `Table`. The
/// cloning process only performs a shallow clone, so two cloned `Table`
/// instances are equivalent in their functionality.
#[derive(Clone)]
pub struct Table {
instance: StoreInstanceHandle,
wasmtime_export: wasmtime_runtime::ExportTable,
}
fn set_table_item(
instance: &InstanceHandle,
table_index: wasm::DefinedTableIndex,
item_index: u32,
item: runtime::TableElement,
) -> Result<()> {
instance
.table_set(table_index, item_index, item)
.map_err(|()| anyhow!("table element index out of bounds"))
}
/// A [`Table`] "belongs" to the store that it was originally created within
/// (either via [`Table::new`] or via instantiating a [`Module`]). Operations
/// on a [`Table`] only work with the store it belongs to, and if another store
/// is passed in by accident then methods will panic.
#[derive(Copy, Clone, Debug)]
#[repr(transparent)] // here for the C API
pub struct Table(Stored<wasmtime_runtime::ExportTable>);
impl Table {
/// Creates a new `Table` with the given parameters.
/// Creates a new [`Table`] with the given parameters.
///
/// * `store` - a global cache to store information in
/// * `store` - the owner of the resulting [`Table`]
/// * `ty` - the type of this table, containing both the element type as
/// well as the initial size and maximum size, if any.
/// * `init` - the initial value to fill all table entries with, if the
@@ -411,9 +408,48 @@ impl Table {
///
/// # Errors
///
/// Returns an error if `init` does not match the element type of the table.
pub fn new(store: &Store, ty: TableType, init: Val) -> Result<Table> {
let (instance, wasmtime_export) = generate_table_export(store, &ty)?;
/// Returns an error if `init` does not match the element type of the table,
/// or if `init` does not belong to the `store` provided.
///
/// # Examples
///
/// ```
/// # use wasmtime::*;
/// # fn main() -> anyhow::Result<()> {
/// let engine = Engine::default();
/// let mut store = Store::new(&engine, ());
///
/// let ty = TableType::new(ValType::FuncRef, Limits::new(2, None));
/// let table = Table::new(&mut store, ty, Val::FuncRef(None))?;
///
/// let module = Module::new(
/// &engine,
/// "(module
/// (table (import \"\" \"\") 2 funcref)
/// (func $f (result i32)
/// i32.const 10)
/// (elem (i32.const 0) (func $f))
/// )"
/// )?;
///
/// let instance = Instance::new(&mut store, &module, &[table.into()])?;
/// // ...
/// # Ok(())
/// # }
/// ```
pub fn new(mut store: impl AsContextMut, ty: TableType, init: Val) -> Result<Table> {
Table::_new(&mut store.as_context_mut().opaque(), ty, init)
}
fn _new(store: &mut StoreOpaque, ty: TableType, init: Val) -> Result<Table> {
if init.ty() != *ty.element() {
bail!(
"table initialization value type {:?} does not have expected type {:?}",
init.ty(),
ty.element(),
);
}
let wasmtime_export = generate_table_export(store, &ty)?;
let init: runtime::TableElement = match ty.element() {
ValType::FuncRef => into_checked_anyfunc(init, store)?.into(),
@@ -428,41 +464,54 @@ impl Table {
};
// Initialize entries with the init value.
let definition = unsafe { &*wasmtime_export.definition };
let index = instance.table_index(definition);
for i in 0..definition.current_elements {
set_table_item(&instance, index, i, init.clone())?;
}
unsafe {
let table = Table::from_wasmtime_table(wasmtime_export, store);
(*table.wasmtime_table(store))
.fill(0, init, ty.limits().min())
.map_err(Trap::from_runtime)?;
Ok(Table {
instance,
wasmtime_export,
})
Ok(table)
}
}
/// Returns the underlying type of this table, including its element type as
/// well as the maximum/minimum lower bounds.
pub fn ty(&self) -> TableType {
TableType::from_wasmtime_table(&self.wasmtime_export.table.table)
///
/// # Panics
///
/// Panics if `store` does not own this table.
pub fn ty(&self, store: impl AsContext) -> TableType {
let store = store.as_context();
let ty = &store[self.0].table.table;
TableType::from_wasmtime_table(ty)
}
fn wasmtime_table_index(&self) -> wasm::DefinedTableIndex {
unsafe { self.instance.table_index(&*self.wasmtime_export.definition) }
fn wasmtime_table(&self, store: &mut StoreOpaque<'_>) -> *mut runtime::Table {
unsafe {
let export = &store[self.0];
let mut handle = InstanceHandle::from_vmctx(export.vmctx);
let idx = handle.table_index(&*export.definition);
handle.get_defined_table(idx)
}
}
/// Returns the table element value at `index`.
///
/// Returns `None` if `index` is out of bounds.
pub fn get(&self, index: u32) -> Option<Val> {
let table_index = self.wasmtime_table_index();
let item = self.instance.table_get(table_index, index)?;
match item {
runtime::TableElement::FuncRef(f) => {
Some(unsafe { from_checked_anyfunc(f, &self.instance.store) })
}
runtime::TableElement::ExternRef(None) => Some(Val::ExternRef(None)),
runtime::TableElement::ExternRef(Some(x)) => {
Some(Val::ExternRef(Some(ExternRef { inner: x })))
///
/// # Panics
///
/// Panics if `store` does not own this table.
pub fn get(&self, mut store: impl AsContextMut, index: u32) -> Option<Val> {
let mut store = store.as_context_mut().opaque();
let table = self.wasmtime_table(&mut store);
unsafe {
match (*table).get(index)? {
runtime::TableElement::FuncRef(f) => Some(from_checked_anyfunc(f, &mut store)),
runtime::TableElement::ExternRef(None) => Some(Val::ExternRef(None)),
runtime::TableElement::ExternRef(Some(x)) => {
Some(Val::ExternRef(Some(ExternRef { inner: x })))
}
}
}
}
@@ -471,24 +520,33 @@ impl Table {
///
/// # Errors
///
/// Returns an error if `index` is out of bounds or if `val` does not have
/// the right type to be stored in this table.
pub fn set(&self, index: u32, val: Val) -> Result<()> {
if !val.comes_from_same_store(&self.instance.store) {
bail!("cross-`Store` values are not supported in tables");
/// Returns an error if `index` is out of bounds, if `val` does not have
/// the right type to be stored in this table, or if `val` belongs to a
/// different store.
///
/// # Panics
///
/// Panics if `store` does not own this table.
pub fn set(&self, mut store: impl AsContextMut, index: u32, val: Val) -> Result<()> {
let ty = self.ty(&store).element().clone();
let mut store = store.as_context_mut().opaque();
let val = val.into_table_element(&mut store, ty)?;
let table = self.wasmtime_table(&mut store);
unsafe {
(*table)
.set(index, val)
.map_err(|()| anyhow!("table element index out of bounds"))
}
let table_index = self.wasmtime_table_index();
set_table_item(
&self.instance,
table_index,
index,
val.into_table_element()?,
)
}
/// Returns the current size of this table.
pub fn size(&self) -> u32 {
unsafe { (*self.wasmtime_export.definition).current_elements }
///
/// # Panics
///
/// Panics if `store` does not own this table.
pub fn size(&self, store: impl AsContext) -> u32 {
let store = store.as_context();
unsafe { (*store[self.0].definition).current_elements }
}
/// Grows the size of this table by `delta` more elements, initialization
@@ -500,32 +558,26 @@ impl Table {
///
/// Returns an error if the table cannot be grown by `delta`, for example
/// if it would cause the table to exceed its maximum size. Also returns an
/// error if `init` is not of the right type.
pub fn grow(&self, delta: u32, init: Val) -> Result<u32> {
let index = self.wasmtime_table_index();
let orig_size = match self.ty().element() {
ValType::FuncRef => {
let init = into_checked_anyfunc(init, &self.instance.store)?;
self.instance.defined_table_grow(index, delta, init.into())
/// error if `init` is not of the right type or if `init` does not belong to
/// `store`.
///
/// # Panics
///
/// Panics if `store` does not own this table.
pub fn grow(&self, mut store: impl AsContextMut, delta: u32, init: Val) -> Result<u32> {
let ty = self.ty(&store).element().clone();
let mut store = store.as_context_mut().opaque();
let init = init.into_table_element(&mut store, ty)?;
let table = self.wasmtime_table(&mut store);
unsafe {
match (*table).grow(delta, init, store.limiter()) {
Some(size) => {
let vm = (*table).vmtable();
*store[self.0].definition = vm;
Ok(size)
}
None => bail!("failed to grow table by `{}`", delta),
}
ValType::ExternRef => {
let init = match init {
Val::ExternRef(Some(x)) => Some(x.inner),
Val::ExternRef(None) => None,
_ => bail!("incorrect init value for growing table"),
};
self.instance.defined_table_grow(
index,
delta,
runtime::TableElement::ExternRef(init),
)
}
_ => unreachable!("only `funcref` and `externref` tables are supported"),
};
if let Some(size) = orig_size {
Ok(size)
} else {
bail!("failed to grow table by `{}`", delta)
}
}
@@ -536,32 +588,30 @@ impl Table {
///
/// Returns an error if the range is out of bounds of either the source or
/// destination tables.
///
/// # Panics
///
/// Panics if `store` does not own either `dst_table` or `src_table`.
pub fn copy(
mut store: impl AsContextMut,
dst_table: &Table,
dst_index: u32,
src_table: &Table,
src_index: u32,
len: u32,
) -> Result<()> {
if !Store::same(&dst_table.instance.store, &src_table.instance.store) {
bail!("cross-`Store` table copies are not supported");
}
if dst_table.ty() != src_table.ty() {
if dst_table.ty(&store).element() != src_table.ty(&store).element() {
bail!("tables do not have the same element type");
}
// NB: We must use the `dst_table`'s `wasmtime_handle` for the
// `dst_table_index` and vice versa for `src_table` since each table can
// come from different modules.
let dst_table_index = dst_table.wasmtime_table_index();
let dst_table_index = dst_table.instance.get_defined_table(dst_table_index);
let mut store = store.as_context_mut().opaque();
let src_table_index = src_table.wasmtime_table_index();
let src_table_index = src_table.instance.get_defined_table(src_table_index);
runtime::Table::copy(dst_table_index, src_table_index, dst_index, src_index, len)
.map_err(|e| Trap::from_runtime(&dst_table.instance.store, e))?;
let dst = dst_table.wasmtime_table(&mut store);
let src = src_table.wasmtime_table(&mut store);
unsafe {
runtime::Table::copy(dst, src, dst_index, src_index, len)
.map_err(Trap::from_runtime)?;
}
Ok(())
}
@@ -577,49 +627,41 @@ impl Table {
/// * the region to be filled is out of bounds, or
///
/// * `val` comes from a different `Store` from this table.
pub fn fill(&self, dst: u32, val: Val, len: u32) -> Result<()> {
if !val.comes_from_same_store(&self.instance.store) {
bail!("cross-`Store` table fills are not supported");
}
///
/// # Panics
///
/// Panics if `store` does not own either `dst_table` or `src_table`.
pub fn fill(&self, mut store: impl AsContextMut, dst: u32, val: Val, len: u32) -> Result<()> {
let ty = self.ty(&store).element().clone();
let mut store = store.as_context_mut().opaque();
let val = val.into_table_element(&mut store, ty)?;
// Ensure the fill value is the correct type
if self.ty().element() != &val.ty() {
bail!("mismatched element fill type");
let table = self.wasmtime_table(&mut store);
unsafe {
(*table).fill(dst, val, len).map_err(Trap::from_runtime)?;
}
let table_index = self.wasmtime_table_index();
self.instance
.handle
.defined_table_fill(table_index, dst, val.into_table_element()?, len)
.map_err(|e| Trap::from_runtime(&self.instance.store, e))?;
Ok(())
}
pub(crate) unsafe fn from_wasmtime_table(
wasmtime_export: &wasmtime_runtime::ExportTable,
store: &Store,
wasmtime_export: wasmtime_runtime::ExportTable,
store: &mut StoreOpaque<'_>,
) -> Table {
Table {
instance: store.existing_vmctx(wasmtime_export.vmctx),
wasmtime_export: wasmtime_export.clone(),
}
Table(store.store_data_mut().insert(wasmtime_export))
}
pub(crate) fn wasmtime_ty(&self) -> &wasmtime_environ::wasm::Table {
&self.wasmtime_export.table.table
pub(crate) fn wasmtime_ty<'a>(&self, data: &'a StoreData) -> &'a wasmtime_environ::wasm::Table {
&data[self.0].table.table
}
pub(crate) fn vmimport(&self) -> wasmtime_runtime::VMTableImport {
pub(crate) fn vmimport(&self, store: &StoreOpaque<'_>) -> wasmtime_runtime::VMTableImport {
let export = &store[self.0];
wasmtime_runtime::VMTableImport {
from: self.wasmtime_export.definition,
vmctx: self.wasmtime_export.vmctx,
from: export.definition,
vmctx: export.vmctx,
}
}
pub(crate) fn wasmtime_export(&self) -> &wasmtime_runtime::ExportTable {
&self.wasmtime_export
}
}
// Exports
@@ -651,8 +693,12 @@ impl<'instance> Export<'instance> {
}
/// Return the `ExternType` of this export.
pub fn ty(&self) -> ExternType {
self.definition.ty()
///
/// # Panics
///
/// Panics if `store` does not own this `Extern`.
pub fn ty(&self, store: impl AsContext) -> ExternType {
self.definition.ty(store)
}
/// Consume this `Export` and return the contained `Extern`.
@@ -683,4 +729,16 @@ impl<'instance> Export<'instance> {
pub fn into_global(self) -> Option<Global> {
self.definition.into_global()
}
/// Consume this `Export` and return the contained `Instance`, if it's a
/// instance, or `None` otherwise.
pub fn into_instance(self) -> Option<Instance> {
self.definition.into_instance()
}
/// Consume this `Export` and return the contained `Module`, if it's a
/// module, or `None` otherwise.
pub fn into_module(self) -> Option<Module> {
self.definition.into_module()
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,6 @@
use super::{invoke_wasm_and_catch_traps, HostAbi};
use crate::{ExternRef, Func, Store, Trap, ValType};
use crate::store::StoreOpaque;
use crate::{AsContextMut, ExternRef, Func, Trap, ValType};
use anyhow::{bail, Result};
use std::marker;
use std::mem::{self, MaybeUninit};
@@ -12,20 +13,19 @@ use wasmtime_runtime::{VMContext, VMFunctionBody};
/// The function within a [`TypedFunc`] is statically known to have `Params` as its
/// parameters and `Results` as its results.
///
/// This structure is created via [`Func::typed`] or [`Func::typed_unchecked`].
/// This structure is created via [`Func::typed`] or [`TypedFunc::new_unchecked`].
/// For more documentation about this see those methods.
#[repr(transparent)]
#[repr(transparent)] // here for the C API
pub struct TypedFunc<Params, Results> {
_a: marker::PhantomData<fn(Params) -> Results>,
func: Func,
}
impl<Params, Results> Copy for TypedFunc<Params, Results> {}
impl<Params, Results> Clone for TypedFunc<Params, Results> {
fn clone(&self) -> TypedFunc<Params, Results> {
TypedFunc {
_a: marker::PhantomData,
func: self.func.clone(),
}
*self
}
}
@@ -34,6 +34,25 @@ where
Params: WasmParams,
Results: WasmResults,
{
/// An unchecked version of [`Func::typed`] which does not perform a
/// typecheck and simply assumes that the type declared here matches the
/// type of this function.
///
/// The semantics of this function are the same as [`Func::typed`] except
/// that no error is returned because no typechecking is done.
///
/// # Unsafety
///
/// This function only safe to call if `typed` would otherwise return `Ok`
/// for the same `Params` and `Results` specified. If `typed` would return
/// an error then the returned `TypedFunc` is memory unsafe to invoke.
pub unsafe fn new_unchecked(func: Func) -> TypedFunc<Params, Results> {
TypedFunc {
_a: marker::PhantomData,
func,
}
}
/// Returns the underlying [`Func`] that this is wrapping, losing the static
/// type information in the process.
pub fn func(&self) -> &Func {
@@ -51,12 +70,13 @@ where
///
/// This function will panic if it is called when the underlying [`Func`] is
/// connected to an asynchronous store.
pub fn call(&self, params: Params) -> Result<Results, Trap> {
pub fn call(&self, mut store: impl AsContextMut, params: Params) -> Result<Results, Trap> {
let mut store = store.as_context_mut().opaque();
assert!(
!cfg!(feature = "async") || !self.func.store().async_support(),
!cfg!(feature = "async") || !store.async_support(),
"must use `call_async` with async stores"
);
unsafe { self._call(params) }
unsafe { self._call(&mut store, params) }
}
/// Invokes this WebAssembly function with the specified parameters.
@@ -72,55 +92,65 @@ where
/// connected to a synchronous store.
#[cfg(feature = "async")]
#[cfg_attr(nightlydoc, doc(cfg(feature = "async")))]
pub async fn call_async(&self, params: Params) -> Result<Results, Trap> {
pub async fn call_async<T>(
&self,
mut store: impl AsContextMut<Data = T>,
params: Params,
) -> Result<Results, Trap>
where
T: Send,
{
let mut store = store.as_context_mut().opaque_send();
assert!(
self.func.store().async_support(),
store.async_support(),
"must use `call` with non-async stores"
);
self.func
.store()
.on_fiber(|| unsafe { self._call(params) })
store
.on_fiber(|store| unsafe { self._call(store, params) })
.await?
}
unsafe fn _call(&self, params: Params) -> Result<Results, Trap> {
unsafe fn _call(&self, store: &mut StoreOpaque<'_>, params: Params) -> Result<Results, Trap> {
// Validate that all runtime values flowing into this store indeed
// belong within this store, otherwise it would be unsafe for store
// values to cross each other.
if !params.compatible_with_store(&self.func.instance.store) {
return Err(Trap::new(
"attempt to pass cross-`Store` value to Wasm as function argument",
));
}
let params = match params.into_abi(store) {
Some(abi) => abi,
None => {
return Err(Trap::new(
"attempt to pass cross-`Store` value to Wasm as function argument",
))
}
};
let params = MaybeUninit::new(params);
let mut ret = MaybeUninit::uninit();
let mut called = false;
let mut returned = false;
let result = invoke_wasm_and_catch_traps(&self.func.instance.store, || {
called = true;
let params = ptr::read(params.as_ptr());
let anyfunc = self.func.export.anyfunc.as_ref();
let result = params.invoke::<Results>(
&self.func.instance.store,
// Try to capture only a single variable (a tuple) in the closure below.
// This means the size of the closure is one pointer and is much more
// efficient to move in memory. This closure is actually invoked on the
// other side of a C++ shim, so it can never be inlined enough to make
// the memory go away, so the size matters here for performance.
let mut captures = (
self.func.caller_checked_anyfunc(store),
MaybeUninit::uninit(),
params,
false,
);
let result = invoke_wasm_and_catch_traps(store, |callee| {
let (anyfunc, ret, params, returned) = &mut captures;
let anyfunc = anyfunc.as_ref();
let result = Params::invoke::<Results>(
anyfunc.func_ptr.as_ptr(),
anyfunc.vmctx,
ptr::null_mut(),
callee,
*params,
);
ptr::write(ret.as_mut_ptr(), result);
returned = true
*returned = true
});
// This can happen if we early-trap due to interrupts or other
// pre-flight checks, so we need to be sure the parameters are at least
// dropped at some point.
if !called {
drop(params.assume_init());
}
let (_, ret, _, returned) = captures;
debug_assert_eq!(result.is_ok(), returned);
result?;
Ok(ret.assume_init())
Ok(Results::from_abi(store, ret.assume_init()))
}
}
@@ -132,7 +162,7 @@ where
/// stable over time.
///
/// For more information see [`Func::wrap`] and [`Func::typed`]
pub unsafe trait WasmTy {
pub unsafe trait WasmTy: Send {
#[doc(hidden)]
type Abi: Copy;
#[doc(hidden)]
@@ -147,11 +177,11 @@ pub unsafe trait WasmTy {
#[doc(hidden)]
fn valtype() -> ValType;
#[doc(hidden)]
fn compatible_with_store(&self, store: &Store) -> bool;
fn compatible_with_store(&self, store: &StoreOpaque) -> bool;
#[doc(hidden)]
fn into_abi(self, store: &Store) -> Self::Abi;
fn into_abi(self, store: &mut StoreOpaque) -> Self::Abi;
#[doc(hidden)]
unsafe fn from_abi(abi: Self::Abi, store: &Store) -> Self;
unsafe fn from_abi(abi: Self::Abi, store: &mut StoreOpaque) -> Self;
}
macro_rules! primitives {
@@ -163,15 +193,15 @@ macro_rules! primitives {
ValType::$ty
}
#[inline]
fn compatible_with_store(&self, _: &Store) -> bool {
fn compatible_with_store(&self, _: &StoreOpaque) -> bool {
true
}
#[inline]
fn into_abi(self, _store: &Store) -> Self::Abi {
fn into_abi(self, _store: &mut StoreOpaque) -> Self::Abi {
self
}
#[inline]
unsafe fn from_abi(abi: Self::Abi, _store: &Store) -> Self {
unsafe fn from_abi(abi: Self::Abi, _store: &mut StoreOpaque) -> Self {
abi
}
}
@@ -196,18 +226,16 @@ unsafe impl WasmTy for Option<ExternRef> {
}
#[inline]
fn compatible_with_store(&self, _store: &Store) -> bool {
fn compatible_with_store(&self, _store: &StoreOpaque) -> bool {
true
}
#[inline]
fn into_abi(self, store: &Store) -> Self::Abi {
fn into_abi(self, store: &mut StoreOpaque) -> Self::Abi {
if let Some(x) = self {
let abi = x.inner.as_raw();
unsafe {
store
.externref_activations_table()
.insert_with_gc(x.inner, store.module_info_lookup());
store.insert_vmexternref(x.inner);
}
abi
} else {
@@ -216,7 +244,7 @@ unsafe impl WasmTy for Option<ExternRef> {
}
#[inline]
unsafe fn from_abi(abi: Self::Abi, _store: &Store) -> Self {
unsafe fn from_abi(abi: Self::Abi, _store: &mut StoreOpaque) -> Self {
if abi.is_null() {
None
} else {
@@ -236,26 +264,26 @@ unsafe impl WasmTy for Option<Func> {
}
#[inline]
fn compatible_with_store<'a>(&self, store: &Store) -> bool {
fn compatible_with_store<'a>(&self, store: &StoreOpaque) -> bool {
if let Some(f) = self {
Store::same(&store, f.store())
store.store_data().contains(f.0)
} else {
true
}
}
#[inline]
fn into_abi(self, _store: &Store) -> Self::Abi {
fn into_abi(self, store: &mut StoreOpaque) -> Self::Abi {
if let Some(f) = self {
f.caller_checked_anyfunc().as_ptr()
f.caller_checked_anyfunc(store).as_ptr()
} else {
ptr::null_mut()
}
}
#[inline]
unsafe fn from_abi(abi: Self::Abi, store: &Store) -> Self {
Func::from_caller_checked_anyfunc(&store, abi)
unsafe fn from_abi(abi: Self::Abi, store: &mut StoreOpaque) -> Self {
Func::from_caller_checked_anyfunc(store, abi)
}
}
@@ -264,19 +292,20 @@ unsafe impl WasmTy for Option<Func> {
///
/// This is implemented for bare types that can be passed to wasm as well as
/// tuples of those types.
pub unsafe trait WasmParams {
pub unsafe trait WasmParams: Send {
#[doc(hidden)]
type Abi: Copy;
#[doc(hidden)]
fn typecheck(params: impl ExactSizeIterator<Item = crate::ValType>) -> Result<()>;
#[doc(hidden)]
fn compatible_with_store(&self, store: &Store) -> bool;
fn into_abi(self, store: &mut StoreOpaque) -> Option<Self::Abi>;
#[doc(hidden)]
unsafe fn invoke<R: WasmResults>(
self,
store: &Store,
func: *const VMFunctionBody,
vmctx1: *mut VMContext,
vmctx2: *mut VMContext,
) -> R;
abi: Self::Abi,
) -> R::ResultAbi;
}
// Forward an impl from `T` to `(T,)` for convenience if there's only one
@@ -285,20 +314,22 @@ unsafe impl<T> WasmParams for T
where
T: WasmTy,
{
type Abi = <(T,) as WasmParams>::Abi;
fn typecheck(params: impl ExactSizeIterator<Item = crate::ValType>) -> Result<()> {
<(T,)>::typecheck(params)
<(T,) as WasmParams>::typecheck(params)
}
fn compatible_with_store(&self, store: &Store) -> bool {
<T as WasmTy>::compatible_with_store(self, store)
#[inline]
fn into_abi(self, store: &mut StoreOpaque) -> Option<Self::Abi> {
<(T,) as WasmParams>::into_abi((self,), store)
}
unsafe fn invoke<R: WasmResults>(
self,
store: &Store,
func: *const VMFunctionBody,
vmctx1: *mut VMContext,
vmctx2: *mut VMContext,
) -> R {
<(T,)>::invoke((self,), store, func, vmctx1, vmctx2)
abi: Self::Abi,
) -> R::ResultAbi {
<(T,) as WasmParams>::invoke::<R>(func, vmctx1, vmctx2, abi)
}
}
@@ -306,6 +337,8 @@ macro_rules! impl_wasm_params {
($n:tt $($t:ident)*) => {
#[allow(non_snake_case)]
unsafe impl<$($t: WasmTy,)*> WasmParams for ($($t,)*) {
type Abi = ($($t::Abi,)*);
fn typecheck(mut params: impl ExactSizeIterator<Item = crate::ValType>) -> Result<()> {
let mut _n = 0;
$(
@@ -322,28 +355,34 @@ macro_rules! impl_wasm_params {
}
}
fn compatible_with_store(&self, _store: &Store) -> bool {
fn into_abi(self, _store: &mut StoreOpaque) -> Option<Self::Abi> {
let ($($t,)*) = self;
$($t.compatible_with_store(_store)&&)* true
$(
let $t = if $t.compatible_with_store(_store) {
$t.into_abi(_store)
} else {
return None;
};
)*
Some(($($t,)*))
}
unsafe fn invoke<R: WasmResults>(
self,
store: &Store,
func: *const VMFunctionBody,
vmctx1: *mut VMContext,
vmctx2: *mut VMContext,
) -> R {
abi: Self::Abi,
) -> R::ResultAbi {
let fnptr = mem::transmute::<
*const VMFunctionBody,
unsafe extern "C" fn(
*mut VMContext,
*mut VMContext,
$($t::Abi,)*
R::Retptr,
) -> R::Abi,
<R::ResultAbi as HostAbi>::Retptr,
) -> <R::ResultAbi as HostAbi>::Abi,
>(func);
let ($($t,)*) = self;
let ($($t,)*) = abi;
// Use the `call` function to acquire a `retptr` which we'll
// forward to the native function. Once we have it we also
// convert all our arguments to abi arguments to go to the raw
@@ -351,8 +390,8 @@ macro_rules! impl_wasm_params {
//
// Upon returning `R::call` will convert all the returns back
// into `R`.
R::call(store, |retptr| {
fnptr(vmctx1, vmctx2, $($t.into_abi(store),)* retptr)
<R::ResultAbi as HostAbi>::call(|retptr| {
fnptr(vmctx1, vmctx2, $($t,)* retptr)
})
}
}
@@ -369,11 +408,9 @@ for_each_function_signature!(impl_wasm_params);
/// `TypedFunc` is not currently supported.
pub unsafe trait WasmResults: WasmParams {
#[doc(hidden)]
type Abi: Copy;
type ResultAbi: HostAbi;
#[doc(hidden)]
type Retptr: Copy;
#[doc(hidden)]
unsafe fn call(store: &Store, f: impl FnOnce(Self::Retptr) -> Self::Abi) -> Self;
unsafe fn from_abi(store: &mut StoreOpaque, abi: Self::ResultAbi) -> Self;
}
// Forwards from a bare type `T` to the 1-tuple type `(T,)`
@@ -381,11 +418,10 @@ unsafe impl<T: WasmTy> WasmResults for T
where
(T::Abi,): HostAbi,
{
type Abi = <(T,) as WasmResults>::Abi;
type Retptr = <(T,) as WasmResults>::Retptr;
type ResultAbi = <(T,) as WasmResults>::ResultAbi;
unsafe fn call(store: &Store, f: impl FnOnce(Self::Retptr) -> Self::Abi) -> Self {
<(T,) as WasmResults>::call(store, f).0
unsafe fn from_abi(store: &mut StoreOpaque, abi: Self::ResultAbi) -> Self {
<(T,) as WasmResults>::from_abi(store, abi).0
}
}
@@ -395,15 +431,10 @@ macro_rules! impl_wasm_results {
unsafe impl<$($t: WasmTy,)*> WasmResults for ($($t,)*)
where ($($t::Abi,)*): HostAbi
{
type Abi = <($($t::Abi,)*) as HostAbi>::Abi;
type Retptr = <($($t::Abi,)*) as HostAbi>::Retptr;
type ResultAbi = ($($t::Abi,)*);
unsafe fn call(store: &Store, f: impl FnOnce(Self::Retptr) -> Self::Abi) -> Self {
// Delegate via the host abi to figure out what the actual ABI
// for dealing with this tuple type is, and then we can re-tuple
// everything and create actual values via `from_abi` after the
// call is complete.
let ($($t,)*) = <($($t::Abi,)*) as HostAbi>::call(f);
unsafe fn from_abi(store: &mut StoreOpaque, abi: Self::ResultAbi) -> Self {
let ($($t,)*) = abi;
($($t::from_abi($t, store),)*)
}
}

View File

@@ -1,21 +1,20 @@
use crate::trampoline::StoreInstanceHandle;
use crate::store::{InstanceId, StoreData, StoreOpaque, StoreOpaqueSend, Stored};
use crate::types::matching;
use crate::{
Engine, Export, Extern, Func, Global, InstanceType, Memory, Module, Store, Table, Trap,
TypedFunc,
AsContext, AsContextMut, Engine, Export, Extern, Func, Global, InstanceType, Memory, Module,
StoreContextMut, Table, Trap, TypedFunc,
};
use anyhow::{anyhow, bail, Context, Error, Result};
use std::mem;
use std::rc::Rc;
use std::sync::Arc;
use wasmtime_environ::entity::PrimaryMap;
use wasmtime_environ::wasm::{
EntityIndex, FuncIndex, GlobalIndex, InstanceIndex, MemoryIndex, ModuleIndex, TableIndex,
};
use wasmtime_environ::Initializer;
use wasmtime_runtime::{
Imports, InstanceAllocationRequest, InstantiationError, RuntimeInstance, VMContext,
VMExternRefActivationsTable, VMFunctionBody, VMFunctionImport, VMGlobalImport, VMMemoryImport,
VMTableImport,
Imports, InstanceAllocationRequest, InstantiationError, VMContext, VMFunctionBody,
VMFunctionImport, VMGlobalImport, VMMemoryImport, VMTableImport,
};
/// An instantiated WebAssembly module.
@@ -23,22 +22,17 @@ use wasmtime_runtime::{
/// This type represents the instantiation of a [`Module`]. Once instantiated
/// you can access the [`exports`](Instance::exports) which are of type
/// [`Extern`] and provide the ability to call functions, set globals, read
/// memory, etc. This is where all the fun stuff happens!
/// memory, etc. When interacting with any wasm code you'll want to make an
/// [`Instance`] to call any code or execute anything.
///
/// An [`Instance`] is created from two inputs, a [`Module`] and a list of
/// imports, provided as a list of [`Extern`] values. The [`Module`] is the wasm
/// code that was compiled and we're instantiating, and the [`Extern`] imports
/// are how we're satisfying the imports of the module provided. On successful
/// instantiation an [`Instance`] will automatically invoke the wasm `start`
/// function.
///
/// When interacting with any wasm code you'll want to make an [`Instance`] to
/// call any code or execute anything!
#[derive(Clone)]
pub struct Instance {
pub(crate) store: Store,
pub(crate) items: RuntimeInstance,
}
/// Instances are owned by a [`Store`](crate::Store) which is passed in at
/// creation time. It's recommended to create instances with
/// [`Linker::instantiate`](crate::Linker::instantiate) or similar
/// [`Linker`](crate::Linker) methods, but a more low-level constructor is also
/// available as [`Instance::new`].
#[derive(Copy, Clone, Debug)]
#[repr(transparent)]
pub struct Instance(Stored<RuntimeInstance>);
impl Instance {
/// Creates a new [`Instance`] from the previously compiled [`Module`] and
@@ -48,32 +42,30 @@ impl Instance {
/// following the procedure in the [core specification][inst] to
/// instantiate. Instantiation can fail for a number of reasons (many
/// specified below), but if successful the `start` function will be
/// automatically run (if provided) and then the [`Instance`] will be
/// returned.
/// automatically run (if specified in the `module`) and then the
/// [`Instance`] will be returned.
///
/// Per the WebAssembly spec, instantiation includes running the module's
/// start function, if it has one (not to be confused with the `_start`
/// function, which is not run).
///
/// Note that this is a low-level function that just performance an
/// instantiation. See the `Linker` struct for an API which provides a
/// convenient way to link imports and provides automatic Command and Reactor
/// behavior.
/// Note that this is a low-level function that just performs an
/// instantiation. See the [`Linker`](crate::Linker) struct for an API which
/// provides a convenient way to link imports and provides automatic Command
/// and Reactor behavior.
///
/// ## Providing Imports
///
/// The `imports` array here is a bit tricky. The entries in the list of
/// `imports` are intended to correspond 1:1 with the list of imports
/// returned by [`Module::imports`]. Before calling [`Instance::new`] you'll
/// want to inspect the return value of [`Module::imports`] and, for each
/// import type, create an [`Extern`] which corresponds to that type.
/// These [`Extern`] values are all then collected into a list and passed to
/// this function.
/// The entries in the list of `imports` are intended to correspond 1:1
/// with the list of imports returned by [`Module::imports`]. Before
/// calling [`Instance::new`] you'll want to inspect the return value of
/// [`Module::imports`] and, for each import type, create an [`Extern`]
/// which corresponds to that type. These [`Extern`] values are all then
/// collected into a list and passed to this function.
///
/// Note that this function is intentionally relatively low level. It is the
/// intention that we'll soon provide a [higher level API][issue] which will
/// be much more ergonomic for instantiating modules. If you need the full
/// power of customization of imports, though, this is the method for you!
/// Note that this function is intentionally relatively low level. For an
/// easier time passing imports by doing name-based resolution it's
/// recommended to instead use the [`Linker`](crate::Linker) type.
///
/// ## Errors
///
@@ -94,12 +86,24 @@ impl Instance {
/// # Panics
///
/// This function will panic if called with a store associated with a
/// [`asynchronous config`](crate::Config::async_support).
/// [`asynchronous config`](crate::Config::async_support). This function
/// will also panic if any [`Extern`] supplied is not owned by `store`.
///
/// [inst]: https://webassembly.github.io/spec/core/exec/modules.html#exec-instantiation
/// [issue]: https://github.com/bytecodealliance/wasmtime/issues/727
/// [`ExternType`]: crate::ExternType
pub fn new(store: &Store, module: &Module, imports: &[Extern]) -> Result<Instance, Error> {
pub fn new(
mut store: impl AsContextMut,
module: &Module,
imports: &[Extern],
) -> Result<Instance, Error> {
Instance::_new(&mut store.as_context_mut().opaque(), module, imports)
}
fn _new(
store: &mut StoreOpaque<'_>,
module: &Module,
imports: &[Extern],
) -> Result<Instance, Error> {
assert!(
!store.async_support(),
"cannot use `new` when async support is enabled on the config"
@@ -109,10 +113,12 @@ impl Instance {
// small but should be kept in sync (modulo the async bits).
let mut i = Instantiator::new(store, module, imports)?;
loop {
if let Some((instance, items)) = i.step()? {
Instantiator::start_raw(&instance)?;
if let Some(items) = items {
break Ok(Instance::from_wasmtime(&items, store));
if let Some((id, instance)) = i.step(store)? {
if let Some(start) = store.instance(id).module().start_func {
Instantiator::start_raw(store, id, start)?;
}
if let Some(instance) = instance {
break Ok(instance);
}
}
}
@@ -128,13 +134,29 @@ impl Instance {
///
/// # Panics
///
/// This function will panic if called with a store associated with a [`synchronous
/// config`](crate::Config::new). This is only compatible with stores associated with
/// an [`asynchronous config`](crate::Config::async_support).
/// This function will panic if called with a store associated with a
/// [`synchronous config`](crate::Config::new). This is only compatible with
/// stores associated with an [`asynchronous
/// config`](crate::Config::async_support).
///
/// This function will also panic, like [`Instance::new`], if any [`Extern`]
/// specified does not belong to `store`.
#[cfg(feature = "async")]
#[cfg_attr(nightlydoc, doc(cfg(feature = "async")))]
pub async fn new_async(
store: &Store,
pub async fn new_async<T>(
mut store: impl AsContextMut<Data = T>,
module: &Module,
imports: &[Extern],
) -> Result<Instance, Error>
where
T: Send,
{
Instance::_new_async(store.as_context_mut().opaque_send(), module, imports).await
}
#[cfg(feature = "async")]
async fn _new_async<'a>(
mut store: StoreOpaqueSend<'a>,
module: &Module,
imports: &[Extern],
) -> Result<Instance, Error> {
@@ -145,57 +167,63 @@ impl Instance {
// NB: this is the same code as `Instance::new`. It's intentionally
// small but should be kept in sync (modulo the async bits).
let mut i = Instantiator::new(store, module, imports)?;
let mut i = Instantiator::new(&mut store.opaque(), module, imports)?;
loop {
if let Some((instance, items)) = i.step()? {
if instance.handle.module().start_func.is_some() {
let step = i.step(&mut store.opaque())?;
if let Some((id, instance)) = step {
let start = store.instance(id).module().start_func;
if let Some(start) = start {
store
.on_fiber(|| Instantiator::start_raw(&instance))
.on_fiber(|store| Instantiator::start_raw(store, id, start))
.await??;
}
if let Some(items) = items {
break Ok(Instance::from_wasmtime(&items, store));
if let Some(instance) = instance {
break Ok(instance);
}
}
}
}
pub(crate) fn from_wasmtime(handle: &RuntimeInstance, store: &Store) -> Instance {
Instance {
items: handle.clone(),
store: store.clone(),
}
pub(crate) fn from_wasmtime(handle: RuntimeInstance, store: &mut StoreOpaque) -> Instance {
Instance(store.store_data_mut().insert(handle))
}
/// Returns the type signature of this instance.
pub fn ty(&self) -> InstanceType {
///
/// # Panics
///
/// Panics if `store` does not own this instance.
pub fn ty(&self, store: impl AsContext) -> InstanceType {
let store = store.as_context();
let items = &store[self.0];
let mut ty = InstanceType::new();
for export in self.exports() {
ty.add_named_export(export.name(), export.ty());
for (name, item) in items.iter() {
ty.add_named_export(name, item.ty(&store));
}
ty
}
pub(crate) fn wasmtime_export(&self) -> &RuntimeInstance {
&self.items
pub(crate) fn items<'a>(&self, store: &'a StoreData) -> &'a RuntimeInstance {
&store[self.0]
}
/// Returns the associated [`Store`] that this `Instance` is compiled into.
///
/// This is the [`Store`] that generally serves as a sort of global cache
/// for various instance-related things.
pub fn store(&self) -> &Store {
&self.store
pub(crate) fn comes_from_same_store(&self, store: &StoreOpaque) -> bool {
store.store_data().contains(self.0)
}
/// Returns the list of exported items from this [`Instance`].
pub fn exports<'instance>(
&'instance self,
) -> impl ExactSizeIterator<Item = Export<'instance>> + 'instance {
self.items.iter().map(move |(name, item)| {
let extern_ = unsafe { Extern::from_wasmtime_export(item, &self.store) };
Export::new(name, extern_)
})
///
/// # Panics
///
/// Panics if `store` does not own this instance.
pub fn exports<'a, T: 'a>(
&'a self,
store: impl Into<StoreContextMut<'a, T>>,
) -> impl ExactSizeIterator<Item = Export<'a>> + 'a {
let items = &store.into().store_data()[self.0];
items
.iter()
.map(|(name, item)| Export::new(name, item.clone()))
}
/// Looks up an exported [`Extern`] value by name.
@@ -204,17 +232,25 @@ impl Instance {
/// the value, if found.
///
/// Returns `None` if there was no export named `name`.
pub fn get_export(&self, name: &str) -> Option<Extern> {
let export = self.items.get(name)?;
Some(unsafe { Extern::from_wasmtime_export(export, &self.store) })
///
/// # Panics
///
/// Panics if `store` does not own this instance.
pub fn get_export(&self, store: impl AsContextMut, name: &str) -> Option<Extern> {
let store = store.as_context();
store[self.0].get(name).cloned()
}
/// Looks up an exported [`Func`] value by name.
///
/// Returns `None` if there was no export named `name`, or if there was but
/// it wasn't a function.
pub fn get_func(&self, name: &str) -> Option<Func> {
self.get_export(name)?.into_func()
///
/// # Panics
///
/// Panics if `store` does not own this instance.
pub fn get_func(&self, store: impl AsContextMut, name: &str) -> Option<Func> {
self.get_export(store, name)?.into_func()
}
/// Looks up an exported [`Func`] value by name and with its type.
@@ -224,47 +260,67 @@ impl Instance {
///
/// Returns an error if `name` isn't a function export or if the export's
/// type did not match `Params` or `Results`
pub fn get_typed_func<Params, Results>(&self, name: &str) -> Result<TypedFunc<Params, Results>>
///
/// # Panics
///
/// Panics if `store` does not own this instance.
pub fn get_typed_func<Params, Results, S>(
&self,
mut store: S,
name: &str,
) -> Result<TypedFunc<Params, Results>>
where
Params: crate::WasmParams,
Results: crate::WasmResults,
S: AsContextMut,
{
let f = self
.get_export(name)
.get_export(store.as_context_mut(), name)
.and_then(|f| f.into_func())
.ok_or_else(|| anyhow!("failed to find function export `{}`", name))?;
Ok(f.typed::<Params, Results>()?.clone())
Ok(f.typed::<Params, Results, _>(store)?)
}
/// Looks up an exported [`Table`] value by name.
///
/// Returns `None` if there was no export named `name`, or if there was but
/// it wasn't a table.
pub fn get_table(&self, name: &str) -> Option<Table> {
self.get_export(name)?.into_table()
///
/// # Panics
///
/// Panics if `store` does not own this instance.
pub fn get_table(&self, store: impl AsContextMut, name: &str) -> Option<Table> {
self.get_export(store, name)?.into_table()
}
/// Looks up an exported [`Memory`] value by name.
///
/// Returns `None` if there was no export named `name`, or if there was but
/// it wasn't a memory.
pub fn get_memory(&self, name: &str) -> Option<Memory> {
self.get_export(name)?.into_memory()
///
/// # Panics
///
/// Panics if `store` does not own this instance.
pub fn get_memory(&self, store: impl AsContextMut, name: &str) -> Option<Memory> {
self.get_export(store, name)?.into_memory()
}
/// Looks up an exported [`Global`] value by name.
///
/// Returns `None` if there was no export named `name`, or if there was but
/// it wasn't a global.
pub fn get_global(&self, name: &str) -> Option<Global> {
self.get_export(name)?.into_global()
///
/// # Panics
///
/// Panics if `store` does not own this instance.
pub fn get_global(&self, store: impl AsContextMut, name: &str) -> Option<Global> {
self.get_export(store, name)?.into_global()
}
}
struct Instantiator<'a> {
in_progress: Vec<ImportsBuilder<'a>>,
cur: ImportsBuilder<'a>,
store: &'a Store,
}
struct ImportsBuilder<'a> {
@@ -273,7 +329,7 @@ struct ImportsBuilder<'a> {
tables: PrimaryMap<TableIndex, VMTableImport>,
memories: PrimaryMap<MemoryIndex, VMMemoryImport>,
globals: PrimaryMap<GlobalIndex, VMGlobalImport>,
instances: PrimaryMap<InstanceIndex, RuntimeInstance>,
instances: PrimaryMap<InstanceIndex, Instance>,
modules: PrimaryMap<ModuleIndex, Module>,
initializer: usize,
module: Module,
@@ -289,7 +345,11 @@ impl<'a> Instantiator<'a> {
/// directives of a module.
///
/// This doesn't do much work itself beyond setting things up.
fn new(store: &'a Store, module: &Module, imports: &'a [Extern]) -> Result<Instantiator<'a>> {
fn new(
store: &mut StoreOpaque<'_>,
module: &Module,
imports: &'a [Extern],
) -> Result<Instantiator<'a>> {
if !Engine::same(store.engine(), module.engine()) {
bail!("cross-`Engine` instantiation is not currently supported");
}
@@ -301,7 +361,7 @@ impl<'a> Instantiator<'a> {
bail!("expected {} imports, found {}", expected, imports.len());
}
for import in imports {
if !import.comes_from_same_store(store) {
if !import.comes_from_same_store(&store) {
bail!("cross-`Store` instantiation is not currently supported");
}
}
@@ -309,7 +369,6 @@ impl<'a> Instantiator<'a> {
Ok(Instantiator {
in_progress: Vec::new(),
cur: ImportsBuilder::new(module, ImportSource::Runtime(imports)),
store,
})
}
@@ -333,9 +392,12 @@ impl<'a> Instantiator<'a> {
/// created. If this is `None` callers need to keep calling this function
/// since the instance created was simply for a recursive instance
/// defined here.
fn step(&mut self) -> Result<Option<(StoreInstanceHandle, Option<RuntimeInstance>)>> {
fn step(
&mut self,
store: &mut StoreOpaque<'_>,
) -> Result<Option<(InstanceId, Option<Instance>)>> {
if self.cur.initializer == 0 {
self.store.bump_resource_counts(&self.cur.module)?;
store.bump_resource_counts(&self.cur.module)?;
}
// Read the current module's initializer and move forward the
@@ -364,7 +426,8 @@ impl<'a> Instantiator<'a> {
matching::MatchCx {
signatures: self.cur.module.signatures(),
types: self.cur.module.types(),
store: self.store,
store_data: store.store_data(),
engine: store.engine(),
}
.extern_(&expected_ty, head)
.with_context(|| {
@@ -374,7 +437,7 @@ impl<'a> Instantiator<'a> {
};
format!("incompatible import type for `{}{}`", name, extra)
})?;
self.cur.push(head);
self.cur.push(head.clone(), store);
}
// Otherwise if arguments are coming from our outer
@@ -419,9 +482,9 @@ impl<'a> Instantiator<'a> {
// and then push that item into our own index space. We eschew
// type-checking since only valid modules should reach this point.
Some(Initializer::AliasInstanceExport { instance, export }) => {
let export = &self.cur.instances[*instance][export];
let item = unsafe { Extern::from_wasmtime_export(export, self.store) };
self.cur.push(&item);
let instance = self.cur.instances[*instance];
let export = store[instance.0][export].clone();
self.cur.push(export, store);
}
// A recursive instantiation of an instance.
@@ -486,8 +549,8 @@ impl<'a> Instantiator<'a> {
// Note that in all cases we return the raw instance handle to get
// the start function executed by the outer context.
None => {
let instance = self.instantiate_raw()?;
let items = self.runtime_instance(&instance);
let instance = self.instantiate_raw(store)?;
let items = self.runtime_instance(store, instance);
let items = match self.in_progress.pop() {
Some(imports) => {
self.cur = imports;
@@ -503,85 +566,105 @@ impl<'a> Instantiator<'a> {
Ok(None)
}
fn instantiate_raw(&self) -> Result<StoreInstanceHandle> {
fn instantiate_raw(&mut self, store: &mut StoreOpaque<'_>) -> Result<InstanceId> {
let compiled_module = self.cur.module.compiled_module();
// Register the module just before instantiation to ensure we keep the module
// properly referenced while in use by the store.
self.store.modules().borrow_mut().register(&self.cur.module);
store.modules_mut().register(&self.cur.module);
unsafe {
let engine = self.store.engine();
let allocator = engine.allocator();
let instance = allocator.allocate(InstanceAllocationRequest {
module: compiled_module.module().clone(),
finished_functions: compiled_module.finished_functions(),
imports: self.cur.build(),
shared_signatures: self.cur.module.signatures().as_module_map().into(),
host_state: Box::new(()),
interrupts: self.store.interrupts(),
externref_activations_table: self.store.externref_activations_table()
as *const VMExternRefActivationsTable
as *mut _,
module_info_lookup: Some(self.store.module_info_lookup()),
limiter: self.store.limiter().as_ref(),
})?;
let mut instance = store
.engine()
.allocator()
.allocate(InstanceAllocationRequest {
module: compiled_module.module().clone(),
finished_functions: compiled_module.finished_functions(),
imports: self.cur.build(),
shared_signatures: self.cur.module.signatures().as_module_map().into(),
host_state: Box::new(()),
store: Some(store.traitobj),
})?;
// After we've created the `InstanceHandle` we still need to run
// initialization to set up data/elements/etc. We do this after adding
// the `InstanceHandle` to the store though. This is required for safety
// because the start function (for example) may trap, but element
// initializers may have run which placed elements into other instance's
// tables. This means that from this point on, regardless of whether
// initialization is successful, we need to keep the instance alive.
let instance = self.store.add_instance(instance, false);
allocator
.initialize(&instance.handle, engine.config().features.bulk_memory)
// initialization to set up data/elements/etc. We do this after
// adding the `InstanceHandle` to the store though. This is required
// for safety because the start function (for example) may trap, but
// element initializers may have run which placed elements into
// other instance's tables. This means that from this point on,
// regardless of whether initialization is successful, we need to
// keep the instance alive.
//
// Note that we `clone` the instance handle just to make easier
// working the the borrow checker here easier. Technically the `&mut
// instance` has somewhat of a borrow on `store` (which
// conflicts with the borrow on `store.engine`) but this doesn't
// matter in practice since initialization isn't even running any
// code here anyway.
let id = store.add_instance(instance.clone(), false);
store
.engine()
.allocator()
.initialize(
&mut instance,
compiled_module.module(),
store.engine().config().features.bulk_memory,
)
.map_err(|e| -> Error {
match e {
InstantiationError::Trap(trap) => {
Trap::from_runtime(self.store, trap).into()
}
InstantiationError::Trap(trap) => Trap::from_runtime(trap).into(),
other => other.into(),
}
})?;
Ok(instance)
Ok(id)
}
}
fn start_raw(instance: &StoreInstanceHandle) -> Result<()> {
let start_func = instance.handle.module().start_func;
fn start_raw(
store: &mut StoreOpaque<'_>,
instance: InstanceId,
start: FuncIndex,
) -> Result<()> {
// If a start function is present, invoke it. Make sure we use all the
// trap-handling configuration in `store` as well.
if let Some(start) = start_func {
let f = match instance
.handle
.lookup_by_declaration(&EntityIndex::Function(start))
{
wasmtime_runtime::Export::Function(f) => f,
_ => unreachable!(), // valid modules shouldn't hit this
};
let vmctx_ptr = instance.handle.vmctx_ptr();
unsafe {
super::func::invoke_wasm_and_catch_traps(&instance.store, || {
mem::transmute::<
*const VMFunctionBody,
unsafe extern "C" fn(*mut VMContext, *mut VMContext),
>(f.anyfunc.as_ref().func_ptr.as_ptr())(
f.anyfunc.as_ref().vmctx, vmctx_ptr
)
})?;
}
let instance = store.instance(instance);
let f = match instance.lookup_by_declaration(&EntityIndex::Function(start)) {
wasmtime_runtime::Export::Function(f) => f,
_ => unreachable!(), // valid modules shouldn't hit this
};
let vmctx = instance.vmctx_ptr();
unsafe {
super::func::invoke_wasm_and_catch_traps(store, |_default_callee| {
mem::transmute::<
*const VMFunctionBody,
unsafe extern "C" fn(*mut VMContext, *mut VMContext),
>(f.anyfunc.as_ref().func_ptr.as_ptr())(
f.anyfunc.as_ref().vmctx, vmctx
)
})?;
}
Ok(())
}
fn runtime_instance(&self, instance: &StoreInstanceHandle) -> RuntimeInstance {
fn runtime_instance(&mut self, store: &mut StoreOpaque<'_>, instance: InstanceId) -> Instance {
// We use an unsafe `clone()` here to work around the borrow checker.
// Technically our instance is a borrow of `store`, but we need the
// borrow again later when calling `Extern::from_wasmtime_export` (and a
// mutable one at that).
//
// The mutability in `from_wasmtime_export` only mutates `StoreData`
// since we're adding ids, but it definitely doesn't deallocate
// `instance` (nothing does that except `Drop` for `Store`), so this in
// theory should be safe.
let instance = unsafe { store.instance(instance).clone() };
// FIXME(#2916) we should ideally just store the `InstanceId` within the
// store itself. There should be no reason we have to allocate a hash
// map here and allocate a bunch of strings, that's quite wasteful if
// only one or two exports are used. Additionally this can push items
// into the `Store` which never end up getting used.
let exports = instance
.handle
.module()
.exports
.iter()
@@ -591,18 +674,16 @@ impl<'a> Instantiator<'a> {
// means we need to handle that here, otherwise we defer to the
// instance to load the values.
let item = match index {
EntityIndex::Instance(i) => {
wasmtime_runtime::Export::Instance(self.cur.instances[*i].clone())
}
EntityIndex::Module(i) => {
wasmtime_runtime::Export::Module(Box::new(self.cur.modules[*i].clone()))
}
index => instance.handle.lookup_by_declaration(index),
EntityIndex::Instance(i) => Extern::Instance(self.cur.instances[*i].clone()),
EntityIndex::Module(i) => Extern::Module(self.cur.modules[*i].clone()),
index => unsafe {
Extern::from_wasmtime_export(instance.lookup_by_declaration(index), store)
},
};
(name.clone(), item)
})
.collect();
Rc::new(exports)
Instance::from_wasmtime(Arc::new(exports), store)
}
}
@@ -622,25 +703,25 @@ impl<'a> ImportsBuilder<'a> {
}
}
fn push(&mut self, item: &Extern) {
fn push(&mut self, item: Extern, store: &mut StoreOpaque<'_>) {
match item {
Extern::Func(i) => {
self.functions.push(i.vmimport());
self.functions.push(i.vmimport(store));
}
Extern::Global(i) => {
self.globals.push(i.vmimport());
self.globals.push(i.vmimport(store));
}
Extern::Table(i) => {
self.tables.push(i.vmimport());
self.tables.push(i.vmimport(store));
}
Extern::Memory(i) => {
self.memories.push(i.vmimport());
self.memories.push(i.vmimport(store));
}
Extern::Instance(i) => {
self.instances.push(i.items.clone());
self.instances.push(i);
}
Extern::Module(m) => {
self.modules.push(m.clone());
self.modules.push(m);
}
}
}
@@ -655,6 +736,8 @@ impl<'a> ImportsBuilder<'a> {
}
}
pub(crate) type RuntimeInstance = Arc<indexmap::IndexMap<String, Extern>>;
/// An internal structure to this crate to build an `Instance` from a list of
/// items with names. This is intended to stay private for now, it'll need an
/// audit of APIs if publicly exported.
@@ -669,22 +752,11 @@ impl InstanceBuilder {
}
pub(crate) fn insert(&mut self, name: &str, item: impl Into<Extern>) {
let items = Rc::get_mut(&mut self.items).unwrap();
let export = match item.into() {
Extern::Func(i) => wasmtime_runtime::Export::Function(i.wasmtime_export().clone()),
Extern::Memory(i) => wasmtime_runtime::Export::Memory(i.wasmtime_export().clone()),
Extern::Table(i) => wasmtime_runtime::Export::Table(i.wasmtime_export().clone()),
Extern::Global(i) => wasmtime_runtime::Export::Global(i.wasmtime_export().clone()),
Extern::Instance(i) => wasmtime_runtime::Export::Instance(i.items.clone()),
Extern::Module(i) => wasmtime_runtime::Export::Module(Box::new(i.clone())),
};
items.insert(name.to_string(), export);
let items = Arc::get_mut(&mut self.items).unwrap();
items.insert(name.to_string(), item.into());
}
pub(crate) fn finish(self, store: &Store) -> Instance {
Instance {
store: store.clone(),
items: self.items,
}
pub(crate) fn finish(self, store: &mut StoreOpaque) -> Instance {
Instance::from_wasmtime(self.items, store)
}
}

View File

@@ -7,13 +7,14 @@
//! globals, etc, which can do things that WebAssembly cannot (such as print to
//! the screen).
//!
//! The `wasmtime` crate draws inspiration from a number of sources, including
//! The `wasmtime` crate has similar concepts to the
//! the [JS WebAssembly
//! API](https://developer.mozilla.org/en-US/docs/WebAssembly) as well as the
//! [proposed C API](https://github.com/webassembly/wasm-c-api). As with all
//! other Rust code you're guaranteed that programs will be safe (not have
//! undefined behavior or segfault) so long as you don't use `unsafe` in your
//! own program. With `wasmtime` you can easily and conveniently embed a
//! [proposed C API](https://github.com/webassembly/wasm-c-api), but the Rust
//! API is designed for efficiency, ergonomics, and expressivity in Rust. As
//! with all other Rust code you're guaranteed that programs will be safe (not
//! have undefined behavior or segfault) so long as you don't use `unsafe` in
//! your own program. With `wasmtime` you can easily and conveniently embed a
//! WebAssembly runtime with confidence that the WebAssembly is safely
//! sandboxed.
//!
@@ -24,35 +25,36 @@
//! use wasmtime::*;
//!
//! fn main() -> Result<()> {
//! // All wasm objects operate within the context of a "store"
//! let store = Store::default();
//!
//! // Modules can be compiled through either the text or binary format
//! let engine = Engine::default();
//! let wat = r#"
//! (module
//! (import "" "" (func $host_hello (param i32)))
//! (import "host" "hello" (func $host_hello (param i32)))
//!
//! (func (export "hello")
//! i32.const 3
//! call $host_hello)
//! )
//! "#;
//! let module = Module::new(store.engine(), wat)?;
//! let module = Module::new(&engine, wat)?;
//!
//! // Host functions can be defined which take/return wasm values and
//! // execute arbitrary code on the host.
//! let host_hello = Func::wrap(&store, |param: i32| {
//! // All wasm objects operate within the context of a "store". Each
//! // `Store` has a type parameter to store host-specific data, which in
//! // this case we're using `4` for.
//! let mut store = Store::new(&engine, 4);
//! let host_hello = Func::wrap(&mut store, |caller: Caller<'_, u32>, param: i32| {
//! println!("Got {} from WebAssembly", param);
//! println!("my host state is: {}", caller.data());
//! });
//!
//! // Instantiation of a module requires specifying its imports and then
//! // afterwards we can fetch exports by name, as well as asserting the
//! // type signature of the function with `get_typed_func`.
//! let instance = Instance::new(&store, &module, &[host_hello.into()])?;
//! let hello = instance.get_typed_func::<(), ()>("hello")?;
//! let instance = Instance::new(&mut store, &module, &[host_hello.into()])?;
//! let hello = instance.get_typed_func::<(), (), _>(&mut store, "hello")?;
//!
//! // And finally we can call the wasm as if it were a Rust function!
//! hello.call(())?;
//! // And finally we can call the wasm!
//! hello.call(&mut store, ())?;
//!
//! Ok(())
//! }
@@ -63,79 +65,166 @@
//! There are a number of core types and concepts that are important to be aware
//! of when using the `wasmtime` crate:
//!
//! * Reference counting - almost all objects in this API are reference counted.
//! Most of the time when and object is `clone`d you're just bumping a
//! reference count. For example when you clone an [`Instance`] that is a
//! cheap operation, it doesn't create an entirely new instance.
//!
//! * [`Store`] - all WebAssembly object and host values will be "connected" to
//! a store. A [`Store`] is not threadsafe which means that itself and all
//! objects connected to it are pinned to a single thread (this happens
//! automatically through a lack of the `Send` and `Sync` traits). Similarly
//! `wasmtime` does not have a garbage collector so anything created within a
//! [`Store`] will not be deallocated until all references have gone away. See
//! the [`Store`] documentation for more information.
//! * [`Engine`] - a global compilation environment for WebAssembly. An
//! [`Engine`] is an object that can be shared concurrently across threads and
//! is created with a [`Config`] to tweak various settings. Compilation of any
//! WebAssembly requires first configuring and creating an [`Engine`].
//!
//! * [`Module`] - a compiled WebAssembly module. This structure represents
//! in-memory JIT code which is ready to execute after being instantiated.
//! It's often important to cache instances of a [`Module`] because creation
//! (compilation) can be expensive. Note that [`Module`] is safe to share
//! across threads.
//! across threads, and can be created from a WebAssembly binary and an
//! [`Engine`] with [`Module::new`]. Caching can either happen with
//! [`Engine::precompile_module`] or [`Module::serialize`], feeding those
//! bytes back into [`Module::deserialize`].
//!
//! * [`Store`] - container for all information related to WebAssembly objects
//! such as functions, instances, memories, etc. A [`Store<T>`][`Store`]
//! allows customization of the `T` to store arbitrary host data within a
//! [`Store`]. This host data can be accessed through host functions via the
//! [`Caller`] function parameter in host-defined functions. A [`Store`] is
//! required for all WebAssembly operations, such as calling a wasm function.
//! The [`Store`] is passed in as a "context" to methods like [`Func::call`].
//! Dropping a [`Store`] will deallocate all memory associated with
//! WebAssembly objects within the [`Store`].
//!
//! * [`Instance`] - an instantiated WebAssembly module. An instance is where
//! you can actually acquire a [`Func`] from, for example, to call. Each
//! [`Instance`], like all other [`Store`]-connected objects, cannot be sent
//! across threads.
//! you can actually acquire a [`Func`] from, for example, to call.
//!
//! There are other important types within the `wasmtime` crate but it's crucial
//! to be familiar with the above types! Be sure to browse the API documentation
//! to get a feeling for what other functionality is offered by this crate.
//! * [`Func`] - a WebAssembly (or host) function. This can be acquired as the
//! export of an [`Instance`] to call WebAssembly functions, or it can be
//! created via functions like [`Func::wrap`] to wrap host-defined
//! functionality and give it to WebAssembly.
//!
//! * [`Table`], [`Global`], [`Memory`] - other WebAssembly objects which can
//! either be defined on the host or in wasm itself (via instances). These all
//! have various ways of being interacted with like [`Func`].
//!
//! All "store-connected" types such as [`Func`], [`Memory`], etc, require the
//! store to be passed in as a context to each method. Methods in wasmtime
//! frequently have their first parameter as either [`impl
//! AsContext`][`AsContext`] or [`impl AsContextMut`][`AsContextMut`]. These
//! traits are implemented for a variety of types, allowing you to, for example,
//! pass the following types into methods:
//!
//! * `&Store<T>`
//! * `&mut Store<T>`
//! * `&Caller<'_, T>`
//! * `&mut Caller<'_, T>`
//! * `StoreContext<'_, T>`
//! * `StoreContextMut<'_, T>`
//!
//! A [`Store`] is the sole owner of all WebAssembly internals. Types like
//! [`Func`] point within the [`Store`] and require the [`Store`] to be provided
//! to actually access the internals of the WebAssembly function, for instance.
//!
//! ## Linking
//!
//! WebAssembly modules almost always require functionality from the host to
//! perform I/O-like tasks. They might refer to quite a few pieces of host
//! functionality, WASI, or maybe even a number of other wasm modules. To assist
//! with managing this a [`Linker`] type is provided to instantiate modules.
//!
//! A [`Linker`] performs name-based resolution of the imports of a WebAssembly
//! module so the [`Linker::instantiate`] method does not take an `imports`
//! argument like [`Instance::new`] does. Methods like [`Linker::define`] or
//! [`Linker::func_wrap`] can be used to define names within a [`Linker`] to
//! later be used for instantiation.
//!
//! For example we can reimplement the above example with a `Linker`:
//!
//! ```
//! use anyhow::Result;
//! use wasmtime::*;
//!
//! fn main() -> Result<()> {
//! let engine = Engine::default();
//! let wat = r#"
//! (module
//! (import "host" "hello" (func $host_hello (param i32)))
//!
//! (func (export "hello")
//! i32.const 3
//! call $host_hello)
//! )
//! "#;
//! let module = Module::new(&engine, wat)?;
//!
//! // Create a `Linker` and define our host function in it:
//! let mut linker = Linker::new(&engine);
//! linker.func_wrap("host", "hello", |caller: Caller<'_, u32>, param: i32| {
//! println!("Got {} from WebAssembly", param);
//! println!("my host state is: {}", caller.data());
//! })?;
//!
//! // Use the `linker` to instantiate the module, which will automatically
//! // resolve the imports of the module using name-based resolution.
//! let mut store = Store::new(&engine, 0);
//! let instance = linker.instantiate(&mut store, &module)?;
//! let hello = instance.get_typed_func::<(), (), _>(&mut store, "hello")?;
//! hello.call(&mut store, ())?;
//!
//! Ok(())
//! }
//! ```
//!
//! The [`Linker`] type also transparently handles Commands and Reactors
//! as defined by WASI.
//!
//! ## Example Architecture
//!
//! To better understand how Wasmtime types interact with each other let's walk
//! through, at a high-level, an example of how you might use WebAssembly. In
//! our use case let's say we have a web server where we'd like to run some
//! custom WebAssembly on each request. To ensure requests are isolated from
//! each other, though, we'll be creating a new [`Instance`] for each request.
//! custom WebAssembly on each request. To ensure requests are entirely isolated
//! from each other, though, we'll be creating a new [`Store`] for each
//! request.
//!
//! When the server starts, we'll start off by creating an [`Engine`] (and maybe
//! tweaking [`Config`] settings if necessary). This [`Engine`] will be the only
//! engine for the lifetime of the server itself.
//!
//! Next, we can compile our WebAssembly. You'd create a [`Module`] through the
//! [`Module::new`] API. This will generate JIT code and perform expensive
//! compilation tasks up-front.
//! engine for the lifetime of the server itself. Next, we can compile our
//! WebAssembly. You'd create a [`Module`] through the [`Module::new`] API.
//! This will generate JIT code and perform expensive compilation tasks
//! up-front. Finally the last step of initialization would be to create a
//! [`Linker`] which will later be used to instantiate modules, adding
//! functionality like WASI to the linker too.
//!
//! After that setup, the server starts up as usual and is ready to receive
//! requests. Upon receiving a request you'd then create a [`Store`] with
//! [`Store::new`] referring to the original [`Engine`]. Using your [`Module`]
//! from before you'd then call [`Instance::new`] to instantiate our module for
//! the request. Both of these operations are designed to be as cheap as
//! possible.
//! and [`Linker`] from before you'd then call [`Linker::instantiate`] to
//! instantiate our module for the request. Both of these operations are
//! designed to be as cheap as possible.
//!
//! With an [`Instance`] you can then invoke various exports and interact with
//! the WebAssembly module. Once the request is finished the [`Store`],
//! [`Instance`], and all other items loaded are dropped and everything will be
//! deallocated. Note that it's crucial to create a [`Store`]-per-request to
//! ensure that memory usage doesn't balloon accidentally by keeping a [`Store`]
//! alive indefinitely.
//!
//! ## Advanced Linking
//!
//! Often WebAssembly modules are not entirely self-isolated. They might refer
//! to quite a few pieces of host functionality, WASI, or maybe even a number of
//! other wasm modules. To help juggling all this together this crate provides a
//! [`Linker`] type which serves as an abstraction to assist in instantiating a
//! module. The [`Linker`] type also transparently handles Commands and Reactors
//! as defined by WASI.
//! is dropped and everything will be deallocated. Note that if the same
//! [`Store`] were used for every request then that would have all requests
//! sharing resources and nothing would ever get deallocated, causing memory
//! usage to baloon and would achive less isolation between requests.
//!
//! ## WASI
//!
//! The `wasmtime` crate does not natively provide support for WASI, but you can
//! use the `wasmtime-wasi` crate for that purpose. With `wasmtime-wasi` you can
//! create a "wasi instance" and then add all of its items into a [`Linker`],
//! which can then be used to instantiate a [`Module`] that uses WASI.
//! use the [`wasmtime-wasi`] crate for that purpose. With [`wasmtime-wasi`] all
//! WASI functions can be added to a [`Linker`] and then used to instantiate
//! WASI-using modules. For more information see the [WASI example in the
//! documentation](https://docs.wasmtime.dev/examples-rust-wasi.html).
//!
//! [`wasmtime-wasi`]: https://crates.io/crates/wasmtime-wasi
//!
//! ## Cross-store usage of items
//!
//! In `wasmtime` wasm items such as [`Global`] and [`Memory`] "belong" to a
//! [`Store`]. The store they belong to is the one they were created with
//! (passed in as a parameter) or instantiated with. This store is the only
//! store that can be used to interact with wasm items after they're created.
//!
//! The `wasmtime` crate will panic if the [`Store`] argument passed in to these
//! operations is incorrect. In other words it's considered a programmer error
//! rather than a recoverable error for the wrong [`Store`] to be used when
//! calling APIs.
//!
//! ## Crate Features
//!
@@ -192,22 +281,24 @@
//! ```no_run
//! # use anyhow::Result;
//! # use wasmtime::*;
//! use wasmtime_wasi::Wasi;
//! use wasi_cap_std_sync::WasiCtxBuilder;
//! use wasmtime_wasi::sync::WasiCtxBuilder;
//!
//! # fn main() -> Result<()> {
//! let store = Store::default();
//! let mut linker = Linker::new(&store);
//! // Compile our module and create a `Linker` which has WASI functions defined
//! // within it.
//! let engine = Engine::default();
//! let module = Module::from_file(&engine, "foo.wasm")?;
//! let mut linker = Linker::new(&engine);
//! wasmtime_wasi::add_to_linker(&mut linker, |cx| cx)?;
//!
//! // Create an instance of `Wasi` which contains a `WasiCtx`. Note that
//! // `WasiCtx` provides a number of ways to configure what the target program
//! // will have access to.
//! let wasi = Wasi::new(&store, WasiCtxBuilder::new().inherit_stdio().build());
//! wasi.add_to_linker(&mut linker)?;
//! // Configure and create a `WasiCtx`, which WASI functions need access to
//! // through the host state of the store (which in this case is the host state
//! // of the store)
//! let wasi_ctx = WasiCtxBuilder::new().inherit_stdio().build();
//! let mut store = Store::new(&engine, wasi_ctx);
//!
//! // Instantiate our module with the imports we've created, and run it.
//! let module = Module::from_file(store.engine(), "foo.wasm")?;
//! let instance = linker.instantiate(&module)?;
//! let instance = linker.instantiate(&mut store, &module)?;
//! // ...
//!
//! # Ok(())
@@ -221,32 +312,29 @@
//!
//! # use wasmtime::*;
//! # fn main() -> anyhow::Result<()> {
//! let store = Store::default();
//! let log_str = Func::wrap(&store, |caller: Caller<'_>, ptr: i32, len: i32| {
//! let mut store = Store::default();
//! let log_str = Func::wrap(&mut store, |mut caller: Caller<'_, ()>, ptr: i32, len: i32| {
//! // Use our `caller` context to learn about the memory export of the
//! // module which called this host function.
//! let mem = match caller.get_export("memory") {
//! Some(Extern::Memory(mem)) => mem,
//! _ => return Err(Trap::new("failed to find host memory")),
//! };
//!
//! // We're reading raw wasm memory here so we need `unsafe`. Note
//! // though that this should be safe because we don't reenter wasm
//! // while we're reading wasm memory, nor should we clash with
//! // any other memory accessors (assuming they're well-behaved
//! // too).
//! unsafe {
//! let data = mem.data_unchecked()
//! .get(ptr as u32 as usize..)
//! .and_then(|arr| arr.get(..len as u32 as usize));
//! let string = match data {
//! Some(data) => match str::from_utf8(data) {
//! Ok(s) => s,
//! Err(_) => return Err(Trap::new("invalid utf-8")),
//! },
//! None => return Err(Trap::new("pointer/length out of bounds")),
//! };
//! assert_eq!(string, "Hello, world!");
//! println!("{}", string);
//! }
//! // Use the `ptr` and `len` values to get a subslice of the wasm-memory
//! // which we'll attempt to interpret as utf-8.
//! let data = mem.data(&caller)
//! .get(ptr as u32 as usize..)
//! .and_then(|arr| arr.get(..len as u32 as usize));
//! let string = match data {
//! Some(data) => match str::from_utf8(data) {
//! Ok(s) => s,
//! Err(_) => return Err(Trap::new("invalid utf-8")),
//! },
//! None => return Err(Trap::new("pointer/length out of bounds")),
//! };
//! assert_eq!(string, "Hello, world!");
//! println!("{}", string);
//! Ok(())
//! });
//! let module = Module::new(
@@ -262,9 +350,9 @@
//! (data (i32.const 4) "Hello, world!"))
//! "#,
//! )?;
//! let instance = Instance::new(&store, &module, &[log_str.into()])?;
//! let foo = instance.get_typed_func::<(), ()>("foo")?;
//! foo.call(())?;
//! let instance = Instance::new(&mut store, &module, &[log_str.into()])?;
//! let foo = instance.get_typed_func::<(), (), _>(&mut store, "foo")?;
//! foo.call(&mut store, ())?;
//! # Ok(())
//! # }
//! ```
@@ -305,7 +393,9 @@ pub use crate::linker::*;
pub use crate::memory::*;
pub use crate::module::{FrameInfo, FrameSymbol, Module};
pub use crate::r#ref::ExternRef;
pub use crate::store::*;
pub use crate::store::{
AsContext, AsContextMut, InterruptHandle, Store, StoreContext, StoreContextMut,
};
pub use crate::trap::*;
pub use crate::types::*;
pub use crate::values::*;
@@ -324,7 +414,31 @@ cfg_if::cfg_if! {
fn _assert_send_sync() {
fn _assert<T: Send + Sync>() {}
fn _assert_send<T: Send>(_t: T) {}
_assert::<Engine>();
_assert::<Config>();
_assert::<InterruptHandle>();
_assert::<(Func, TypedFunc<(), ()>, Global, Table, Memory)>();
_assert::<Instance>();
_assert::<Module>();
_assert::<Store<()>>();
_assert::<StoreContext<'_, ()>>();
_assert::<StoreContextMut<'_, ()>>();
_assert::<Caller<'_, ()>>();
_assert::<Linker<()>>();
_assert::<Linker<*mut u8>>();
_assert::<ExternRef>();
#[cfg(feature = "async")]
fn _call_async(s: &mut Store<()>, f: Func) {
_assert_send(f.call_async(&mut *s, &[]))
}
#[cfg(feature = "async")]
fn _typed_call_async(s: &mut Store<()>, f: TypedFunc<(), ()>) {
_assert_send(f.call_async(&mut *s, ()))
}
#[cfg(feature = "async")]
fn _instantiate_async(s: &mut Store<()>, m: &Module) {
_assert_send(Instance::new_async(s, m, &[]))
}
}

View File

@@ -4,10 +4,10 @@ pub(crate) const DEFAULT_MEMORY_LIMIT: usize = 10000;
/// Used by hosts to limit resource consumption of instances at runtime.
///
/// [`Store::new_with_limits`](crate::Store::new_with_limits) can be used
/// [`Store::limiter`](crate::Store::limiter) can be used
/// with a resource limiter to take into account non-WebAssembly resource
/// usage to determine if a linear memory or table should be grown.
pub trait ResourceLimiter {
pub trait ResourceLimiter: Send + Sync + 'static {
/// Notifies the resource limiter that an instance's linear memory has been requested to grow.
///
/// * `current` is the current size of the linear memory in WebAssembly page units.
@@ -23,7 +23,7 @@ pub trait ResourceLimiter {
///
/// Returning `true` when a maximum has been exceeded will have no effect as the linear memory
/// will not be grown.
fn memory_growing(&self, current: u32, desired: u32, maximum: Option<u32>) -> bool;
fn memory_growing(&mut self, current: u32, desired: u32, maximum: Option<u32>) -> bool;
/// Notifies the resource limiter that an instance's table has been requested to grow.
///
@@ -39,7 +39,7 @@ pub trait ResourceLimiter {
///
/// Returning `true` when a maximum has been exceeded will have no effect as the table will
/// not be grown.
fn table_growing(&self, current: u32, desired: u32, maximum: Option<u32>) -> bool;
fn table_growing(&mut self, current: u32, desired: u32, maximum: Option<u32>) -> bool;
/// The maximum number of instances that can be created for a [`Store`](crate::Store).
///
@@ -72,11 +72,11 @@ pub trait ResourceLimiter {
pub(crate) struct ResourceLimiterProxy<T>(pub T);
impl<T: ResourceLimiter> wasmtime_runtime::ResourceLimiter for ResourceLimiterProxy<T> {
fn memory_growing(&self, current: u32, desired: u32, maximum: Option<u32>) -> bool {
fn memory_growing(&mut self, current: u32, desired: u32, maximum: Option<u32>) -> bool {
self.0.memory_growing(current, desired, maximum)
}
fn table_growing(&self, current: u32, desired: u32, maximum: Option<u32>) -> bool {
fn table_growing(&mut self, current: u32, desired: u32, maximum: Option<u32>) -> bool {
self.0.table_growing(current, desired, maximum)
}
@@ -180,14 +180,14 @@ impl Default for StoreLimits {
}
impl ResourceLimiter for StoreLimits {
fn memory_growing(&self, _current: u32, desired: u32, _maximum: Option<u32>) -> bool {
fn memory_growing(&mut self, _current: u32, desired: u32, _maximum: Option<u32>) -> bool {
match self.memory_pages {
Some(limit) if desired > limit => false,
_ => true,
}
}
fn table_growing(&self, _current: u32, desired: u32, _maximum: Option<u32>) -> bool {
fn table_growing(&mut self, _current: u32, desired: u32, _maximum: Option<u32>) -> bool {
match self.table_elements {
Some(limit) if desired > limit => false,
_ => true,

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,7 @@
use crate::trampoline::{generate_memory_export, StoreInstanceHandle};
use crate::{MemoryType, Store};
use anyhow::{anyhow, Result};
use crate::store::{StoreData, StoreOpaque, Stored};
use crate::trampoline::generate_memory_export;
use crate::{AsContext, AsContextMut, MemoryType, StoreContext, StoreContextMut};
use anyhow::{bail, Result};
use std::slice;
/// Error for out of bounds [`Memory`] access.
@@ -25,118 +26,72 @@ impl std::error::Error for MemoryAccessError {}
/// that is always a multiple of the WebAssembly page size, currently 64
/// kilobytes.
///
/// WebAssembly memory is used for global data, statics in C/C++/Rust, shadow
/// stack memory, etc. Accessing wasm memory is generally quite fast!
/// WebAssembly memory is used for global data (not to be confused with wasm
/// `global` items), statics in C/C++/Rust, shadow stack memory, etc. Accessing
/// wasm memory is generally quite fast.
///
/// # `Memory` and `Clone`
///
/// Memories are internally reference counted so you can `clone` a `Memory`. The
/// cloning process only performs a shallow clone, so two cloned `Memory`
/// instances are equivalent in their functionality.
///
/// # `Memory` and threads
///
/// It is intended that `Memory` is safe to share between threads. At this time
/// this is not implemented in `wasmtime`, however. This is planned to be
/// implemented though!
/// Memories, like other wasm items, are owned by a [`Store`](crate::Store).
///
/// # `Memory` and Safety
///
/// Linear memory is a lynchpin of safety for WebAssembly, but it turns out
/// there are very few ways to safely inspect the contents of a memory from the
/// host (Rust). This is because memory safety is quite tricky when working with
/// a `Memory` and we're still working out the best idioms to encapsulate
/// everything safely where it's efficient and ergonomic. This section of
/// documentation, however, is intended to help educate a bit what is and isn't
/// safe when working with `Memory`.
/// Linear memory is a lynchpin of safety for WebAssembly. In Wasmtime there are
/// safe methods of interacting with a [`Memory`]:
///
/// For safety purposes you can think of a `Memory` as a glorified
/// `Rc<UnsafeCell<Vec<u8>>>`. There are a few consequences of this
/// interpretation:
/// * [`Memory::read`]
/// * [`Memory::write`]
/// * [`Memory::data`]
/// * [`Memory::data_mut`]
///
/// * At any time someone else may have access to the memory (hence the `Rc`).
/// This could be a wasm instance, other host code, or a set of wasm instances
/// which all reference a `Memory`. When in doubt assume someone else has a
/// handle to your `Memory`.
/// Note that all of these consider the entire store context as borrowed for the
/// duration of the call or the duration of the returned slice. This largely
/// means that while the function is running you'll be unable to borrow anything
/// else from the store. This includes getting access to the `T` on
/// [`Store<T>`](crate::Store), but it also means that you can't recursively
/// call into WebAssembly for instance.
///
/// * At any time, memory can be read from or written to (hence the
/// `UnsafeCell`). Anyone with a handle to a wasm memory can read/write to it.
/// Primarily other instances can execute the `load` and `store` family of
/// instructions, as well as any other which modifies or reads memory.
/// If you'd like to dip your toes into handling [`Memory`] in a more raw
/// fashion (e.g. by using raw pointers or raw slices), then there's a few
/// important points to consider when doing so:
///
/// * At any time memory may grow (hence the `Vec<..>`). Growth may relocate the
/// base memory pointer (similar to how `vec.push(...)` can change the result
/// of `.as_ptr()`)
/// * Any recursive calls into WebAssembly can possibly modify any byte of the
/// entire memory. This means that whenever wasm is called Rust can't have any
/// long-lived borrows live across the wasm function call. Slices like `&mut
/// [u8]` will be violated because they're not actually exclusive at that
/// point, and slices like `&[u8]` are also violated because their contents
/// may be mutated.
///
/// So given that we're working roughly with `Rc<UnsafeCell<Vec<u8>>>` that's a
/// lot to keep in mind! It's hopefully though sort of setting the stage as to
/// what you can safely do with memories.
/// * WebAssembly memories can grow, and growth may change the base pointer.
/// This means that even holding a raw pointer to memory over a wasm function
/// call is also incorrect. Anywhere in the function call the base address of
/// memory may change. Note that growth can also be requested from the
/// embedding API as well.
///
/// Let's run through a few safe examples first of how you can use a `Memory`.
/// As a general rule of thumb it's recommended to stick to the safe methods of
/// [`Memory`] if you can. It's not advised to use raw pointers or `unsafe`
/// operations because of how easy it is to accidentally get things wrong.
///
/// Some examples of safely interacting with memory are:
///
/// ```rust
/// use wasmtime::{Memory, MemoryAccessError};
/// use wasmtime::{Memory, Store, MemoryAccessError};
///
/// // Memory can be read and written safely with the `Memory::read` and
/// // `Memory::write` methods.
/// // An error is returned if the copy did not succeed.
/// fn safe_examples(mem: &Memory) -> Result<(), MemoryAccessError> {
/// fn safe_examples(mem: Memory, store: &mut Store<()>) -> Result<(), MemoryAccessError> {
/// let offset = 5;
/// mem.write(offset, b"hello")?;
/// mem.write(&mut *store, offset, b"hello")?;
/// let mut buffer = [0u8; 5];
/// mem.read(offset, &mut buffer)?;
/// mem.read(&store, offset, &mut buffer)?;
/// assert_eq!(b"hello", &buffer);
///
/// // Note that while this is safe care must be taken because the indexing
/// // here may panic if the memory isn't large enough.
/// assert_eq!(&mem.data(&store)[offset..offset + 5], b"hello");
/// mem.data_mut(&mut *store)[offset..offset + 5].copy_from_slice(b"bye!!");
///
/// Ok(())
/// }
///
/// // You can also get direct, unsafe access to the memory, but must manually
/// // ensure that safety invariants are upheld.
///
/// fn correct_unsafe_examples(mem: &Memory) {
/// // Just like wasm, it's safe to read memory almost at any time. The
/// // gotcha here is that we need to be sure to load from the correct base
/// // pointer and perform the bounds check correctly. So long as this is
/// // all self contained here (e.g. not arbitrary code in the middle) we're
/// // good to go.
/// let byte = unsafe { mem.data_unchecked()[0x123] };
///
/// // Short-lived borrows of memory are safe, but they must be scoped and
/// // not have code which modifies/etc `Memory` while the borrow is active.
/// // For example if you want to read a string from memory it is safe to do
/// // so:
/// let string_base = 0xdead;
/// let string_len = 0xbeef;
/// let string = unsafe {
/// let bytes = &mem.data_unchecked()[string_base..][..string_len];
/// match std::str::from_utf8(bytes) {
/// Ok(s) => s.to_string(), // copy out of wasm memory
/// Err(_) => panic!("not valid utf-8"),
/// }
/// };
///
/// // Additionally like wasm you can write to memory at any point in time,
/// // again making sure that after you get the unchecked slice you don't
/// // execute code which could read/write/modify `Memory`:
/// unsafe {
/// mem.data_unchecked_mut()[0x123] = 3;
/// }
///
/// // When working with *borrows* that point directly into wasm memory you
/// // need to be extremely careful. Any functionality that operates on a
/// // borrow into wasm memory needs to be thoroughly audited to effectively
/// // not touch the `Memory` at all
/// let data_base = 0xfeed;
/// let data_len = 0xface;
/// unsafe {
/// let data = &mem.data_unchecked()[data_base..][..data_len];
/// host_function_that_doesnt_touch_memory(data);
///
/// // effectively the same rules apply to mutable borrows
/// let data_mut = &mut mem.data_unchecked_mut()[data_base..][..data_len];
/// host_function_that_doesnt_touch_memory(data);
/// }
/// }
/// # fn host_function_that_doesnt_touch_memory(_: &[u8]){}
/// ```
///
/// It's worth also, however, covering some examples of **incorrect**,
@@ -144,54 +99,52 @@ impl std::error::Error for MemoryAccessError {}
///
/// ```rust
/// # use anyhow::Result;
/// use wasmtime::Memory;
/// use wasmtime::{Memory, Store};
///
/// // NOTE: All code in this function is not safe to execute and may cause
/// // segfaults/undefined behavior at runtime. Do not copy/paste these examples
/// // into production code!
/// unsafe fn unsafe_examples(mem: &Memory) -> Result<()> {
/// unsafe fn unsafe_examples(mem: Memory, store: &mut Store<()>) -> Result<()> {
/// // First and foremost, any borrow can be invalidated at any time via the
/// // `Memory::grow` function. This can relocate memory which causes any
/// // previous pointer to be possibly invalid now.
/// let pointer: &u8 = &mem.data_unchecked()[0x100];
/// mem.grow(1)?; // invalidates `pointer`!
/// let pointer: &u8 = &*mem.data_ptr(&store);
/// mem.grow(&mut *store, 1)?; // invalidates `pointer`!
/// // println!("{}", *pointer); // FATAL: use-after-free
///
/// // Note that the use-after-free also applies to slices, whether they're
/// // slices of bytes or strings.
/// let slice: &[u8] = &mem.data_unchecked()[0x100..0x102];
/// mem.grow(1)?; // invalidates `slice`!
/// let mem_slice = std::slice::from_raw_parts(
/// mem.data_ptr(&store),
/// mem.data_size(&store),
/// );
/// let slice: &[u8] = &mem_slice[0x100..0x102];
/// mem.grow(&mut *store, 1)?; // invalidates `slice`!
/// // println!("{:?}", slice); // FATAL: use-after-free
///
/// // Due to the reference-counted nature of `Memory` note that literal
/// // calls to `Memory::grow` are not sufficient to audit for. You'll need
/// // to be careful that any mutation of `Memory` doesn't happen while
/// // you're holding an active borrow.
/// let slice: &[u8] = &mem.data_unchecked()[0x100..0x102];
/// some_other_function(); // may invalidate `slice` through another `mem` reference
/// // println!("{:?}", slice); // FATAL: maybe a use-after-free
/// // The `Memory` type may be stored in other locations, so if you hand
/// // off access to the `Store` then those locations may also call
/// // `Memory::grow` or similar, so it's not enough to just audit code for
/// // calls to `Memory::grow`.
/// let pointer: &u8 = &*mem.data_ptr(&store);
/// some_other_function(store); // may invalidate `pointer` through use of `store`
/// // println!("{:?}", pointer); // FATAL: maybe a use-after-free
///
/// // An especially subtle aspect of accessing a wasm instance's memory is
/// // that you need to be extremely careful about aliasing. Anyone at any
/// // time can call `data_unchecked()` or `data_unchecked_mut()`, which
/// // means you can easily have aliasing mutable references:
/// let ref1: &u8 = &mem.data_unchecked()[0x100];
/// let ref2: &mut u8 = &mut mem.data_unchecked_mut()[0x100];
/// let ref1: &u8 = &*mem.data_ptr(&store).add(0x100);
/// let ref2: &mut u8 = &mut *mem.data_ptr(&store).add(0x100);
/// // *ref2 = *ref1; // FATAL: violates Rust's aliasing rules
///
/// // Note that aliasing applies to strings as well, for example this is
/// // not valid because the slices overlap.
/// let slice1: &mut [u8] = &mut mem.data_unchecked_mut()[0x100..][..3];
/// let slice2: &mut [u8] = &mut mem.data_unchecked_mut()[0x102..][..4];
/// // println!("{:?} {:?}", slice1, slice2); // FATAL: aliasing mutable pointers
///
/// Ok(())
/// }
/// # fn some_other_function() {}
/// # fn some_other_function(store: &mut Store<()>) {}
/// ```
///
/// Overall there's some general rules of thumb when working with `Memory` and
/// getting raw pointers inside of it:
/// Overall there's some general rules of thumb when unsafely working with
/// `Memory` and getting raw pointers inside of it:
///
/// * If you never have a "long lived" pointer into memory, you're likely in the
/// clear. Care still needs to be taken in threaded scenarios or when/where
@@ -199,27 +152,24 @@ impl std::error::Error for MemoryAccessError {}
/// * Long-lived pointers must always respect Rust'a aliasing rules. It's ok for
/// shared borrows to overlap with each other, but mutable borrows must
/// overlap with nothing.
/// * Long-lived pointers are only valid if `Memory` isn't used in an unsafe way
/// while the pointer is valid. This includes both aliasing and growth.
/// * Long-lived pointers are only valid if they're not invalidated for their
/// lifetime. This means that [`Store`](crate::Store) isn't used to reenter
/// wasm or the memory itself is never grown or otherwise modified/aliased.
///
/// At this point it's worth reiterating again that working with `Memory` is
/// pretty tricky and that's not great! Proposals such as [interface types] are
/// intended to prevent wasm modules from even needing to import/export memory
/// in the first place, which obviates the need for all of these safety caveats!
/// Additionally over time we're still working out the best idioms to expose in
/// `wasmtime`, so if you've got ideas or questions please feel free to [open an
/// issue]!
/// At this point it's worth reiterating again that unsafely working with
/// `Memory` is pretty tricky and not recommended! It's highly recommended to
/// use the safe methods to interact with [`Memory`] whenever possible.
///
/// ## `Memory` Safety and Threads
///
/// Currently the `wasmtime` crate does not implement the wasm threads proposal,
/// but it is planned to do so. It's additionally worthwhile discussing how this
/// but it is planned to do so. It may be interesting to readers to see how this
/// affects memory safety and what was previously just discussed as well.
///
/// Once threads are added into the mix, all of the above rules still apply.
/// There's an additional, rule, however, that all reads and writes can
/// happen *concurrently*. This effectively means that long-lived borrows into
/// wasm memory are virtually never safe to have.
/// There's an additional consideration that all reads and writes can happen
/// concurrently, though. This effectively means that any borrow into wasm
/// memory are virtually never safe to have.
///
/// Mutable pointers are fundamentally unsafe to have in a concurrent scenario
/// in the face of arbitrary wasm code. Only if you dynamically know for sure
@@ -228,30 +178,28 @@ impl std::error::Error for MemoryAccessError {}
/// underlying contents may change, so unless `UnsafeCell` in one form or
/// another is used everywhere there's no safety.
///
/// One important point about concurrency is that `Memory::grow` can indeed
/// happen concurrently. This, however, will never relocate the base pointer.
/// Shared memories must always have a maximum size and they will be
/// preallocated such that growth will never relocate the base pointer. The
/// maximum length of the memory, however, will change over time.
/// One important point about concurrency is that while [`Memory::grow`] can
/// happen concurrently it will never relocate the base pointer. Shared
/// memories must always have a maximum size and they will be preallocated such
/// that growth will never relocate the base pointer. The current size of the
/// memory may still change over time though.
///
/// Overall the general rule of thumb for shared memories is that you must
/// atomically read and write everything. Nothing can be borrowed and everything
/// must be eagerly copied out.
///
/// [interface types]: https://github.com/webassembly/interface-types
/// [open an issue]: https://github.com/bytecodealliance/wasmtime/issues/new
#[derive(Clone)]
pub struct Memory {
pub(crate) instance: StoreInstanceHandle,
wasmtime_export: wasmtime_runtime::ExportMemory,
}
/// must be eagerly copied out. This means that [`Memory::data`] and
/// [`Memory::data_mut`] won't work in the future (they'll probably return an
/// error) for shared memories when they're implemented. When possible it's
/// recommended to use [`Memory::read`] and [`Memory::write`] which will still
/// be provided.
#[derive(Copy, Clone, Debug)]
#[repr(transparent)] // here for the C API
pub struct Memory(Stored<wasmtime_runtime::ExportMemory>);
impl Memory {
/// Creates a new WebAssembly memory given the configuration of `ty`.
///
/// The `store` argument is a general location for cache information, and
/// otherwise the memory will immediately be allocated according to the
/// type's configuration. All WebAssembly memory is initialized to zero.
/// The `store` argument will be the owner of the returned [`Memory`]. All
/// WebAssembly memory is initialized to zero.
///
/// # Examples
///
@@ -259,142 +207,148 @@ impl Memory {
/// # use wasmtime::*;
/// # fn main() -> anyhow::Result<()> {
/// let engine = Engine::default();
/// let store = Store::new(&engine);
/// let mut store = Store::new(&engine, ());
///
/// let memory_ty = MemoryType::new(Limits::new(1, None));
/// let memory = Memory::new(&store, memory_ty)?;
/// let memory = Memory::new(&mut store, memory_ty)?;
///
/// let module = Module::new(&engine, "(module (memory (import \"\" \"\") 1))")?;
/// let instance = Instance::new(&store, &module, &[memory.into()])?;
/// let instance = Instance::new(&mut store, &module, &[memory.into()])?;
/// // ...
/// # Ok(())
/// # }
/// ```
pub fn new(store: &Store, ty: MemoryType) -> Result<Memory> {
let (instance, wasmtime_export) = generate_memory_export(store, &ty)?;
Ok(Memory {
instance,
wasmtime_export,
})
pub fn new(mut store: impl AsContextMut, ty: MemoryType) -> Result<Memory> {
Memory::_new(&mut store.as_context_mut().opaque(), ty)
}
fn _new(store: &mut StoreOpaque<'_>, ty: MemoryType) -> Result<Memory> {
unsafe {
let export = generate_memory_export(store, &ty)?;
Ok(Memory::from_wasmtime_memory(export, store))
}
}
/// Returns the underlying type of this memory.
///
/// # Panics
///
/// Panics if this memory doesn't belong to `store`.
///
/// # Examples
///
/// ```
/// # use wasmtime::*;
/// # fn main() -> anyhow::Result<()> {
/// let engine = Engine::default();
/// let store = Store::new(&engine);
/// let mut store = Store::new(&engine, ());
/// let module = Module::new(&engine, "(module (memory (export \"mem\") 1))")?;
/// let instance = Instance::new(&store, &module, &[])?;
/// let memory = instance.get_memory("mem").unwrap();
/// let ty = memory.ty();
/// let instance = Instance::new(&mut store, &module, &[])?;
/// let memory = instance.get_memory(&mut store, "mem").unwrap();
/// let ty = memory.ty(&store);
/// assert_eq!(ty.limits().min(), 1);
/// # Ok(())
/// # }
/// ```
pub fn ty(&self) -> MemoryType {
MemoryType::from_wasmtime_memory(&self.wasmtime_export.memory.memory)
pub fn ty(&self, store: impl AsContext) -> MemoryType {
let store = store.as_context();
let ty = &store[self.0].memory.memory;
MemoryType::from_wasmtime_memory(&ty)
}
/// Safely reads memory contents at the given offset into a buffer.
///
/// The entire buffer will be filled.
///
/// If offset + buffer length exceed the current memory capacity, then the
/// If `offset + buffer.len()` exceed the current memory capacity, then the
/// buffer is left untouched and a [`MemoryAccessError`] is returned.
pub fn read(&self, offset: usize, buffer: &mut [u8]) -> Result<(), MemoryAccessError> {
unsafe {
let slice = self
.data_unchecked()
.get(offset..)
.and_then(|s| s.get(..buffer.len()))
.ok_or(MemoryAccessError { _private: () })?;
buffer.copy_from_slice(slice);
Ok(())
}
///
/// # Panics
///
/// Panics if this memory doesn't belong to `store`.
pub fn read(
&self,
store: impl AsContext,
offset: usize,
buffer: &mut [u8],
) -> Result<(), MemoryAccessError> {
let store = store.as_context();
let slice = self
.data(&store)
.get(offset..)
.and_then(|s| s.get(..buffer.len()))
.ok_or(MemoryAccessError { _private: () })?;
buffer.copy_from_slice(slice);
Ok(())
}
/// Safely writes contents of a buffer to this memory at the given offset.
///
/// If the offset + buffer length exceed current memory capacity, then none
/// of the buffer is written to memory and a [`MemoryAccessError`] is
/// If the `offset + buffer.len()` exceeds the current memory capacity, then
/// none of the buffer is written to memory and a [`MemoryAccessError`] is
/// returned.
pub fn write(&self, offset: usize, buffer: &[u8]) -> Result<(), MemoryAccessError> {
///
/// # Panics
///
/// Panics if this memory doesn't belong to `store`.
pub fn write(
&self,
mut store: impl AsContextMut,
offset: usize,
buffer: &[u8],
) -> Result<(), MemoryAccessError> {
let mut context = store.as_context_mut();
self.data_mut(&mut context)
.get_mut(offset..)
.and_then(|s| s.get_mut(..buffer.len()))
.ok_or(MemoryAccessError { _private: () })?
.copy_from_slice(buffer);
Ok(())
}
/// Returns this memory as a native Rust slice.
///
/// Note that this method will consider the entire store context provided as
/// borrowed for the duration of the lifetime of the returned slice.
///
/// # Panics
///
/// Panics if this memory doesn't belong to `store`.
pub fn data<'a, T: 'a>(&self, store: impl Into<StoreContext<'a, T>>) -> &'a [u8] {
unsafe {
self.data_unchecked_mut()
.get_mut(offset..)
.and_then(|s| s.get_mut(..buffer.len()))
.ok_or(MemoryAccessError { _private: () })?
.copy_from_slice(buffer);
Ok(())
let store = store.into();
let definition = *store[self.0].definition;
slice::from_raw_parts(definition.base, definition.current_length)
}
}
/// Returns this memory as a slice view that can be read natively in Rust.
/// Returns this memory as a native Rust mutable slice.
///
/// # Safety
/// Note that this method will consider the entire store context provided as
/// borrowed for the duration of the lifetime of the returned slice.
///
/// This is an unsafe operation because there is no guarantee that the
/// following operations do not happen concurrently while the slice is in
/// use:
/// # Panics
///
/// * Data could be modified by calling into a wasm module.
/// * Memory could be relocated through growth by calling into a wasm
/// module.
/// * When threads are supported, non-atomic reads will race with other
/// writes.
///
/// Extreme care need be taken when the data of a `Memory` is read. The
/// above invariants all need to be upheld at a bare minimum, and in
/// general you'll need to ensure that while you're looking at slice you're
/// the only one who can possibly look at the slice and read/write it.
///
/// Be sure to keep in mind that `Memory` is reference counted, meaning
/// that there may be other users of this `Memory` instance elsewhere in
/// your program. Additionally `Memory` can be shared and used in any number
/// of wasm instances, so calling any wasm code should be considered
/// dangerous while you're holding a slice of memory.
///
/// For more information and examples see the documentation on the
/// [`Memory`] type.
pub unsafe fn data_unchecked(&self) -> &[u8] {
self.data_unchecked_mut()
}
/// Returns this memory as a slice view that can be read and written
/// natively in Rust.
///
/// # Safety
///
/// All of the same safety caveats of [`Memory::data_unchecked`] apply
/// here, doubly so because this is returning a mutable slice! As a
/// double-extra reminder, remember that `Memory` is reference counted, so
/// you can very easily acquire two mutable slices by simply calling this
/// function twice. Extreme caution should be used when using this method,
/// and in general you probably want to result to unsafe accessors and the
/// `data` methods below.
///
/// For more information and examples see the documentation on the
/// [`Memory`] type.
pub unsafe fn data_unchecked_mut(&self) -> &mut [u8] {
let definition = &*self.wasmtime_export.definition;
slice::from_raw_parts_mut(definition.base, definition.current_length)
/// Panics if this memory doesn't belong to `store`.
pub fn data_mut<'a, T: 'a>(&self, store: impl Into<StoreContextMut<'a, T>>) -> &'a mut [u8] {
unsafe {
let store = store.into();
let definition = *store[self.0].definition;
slice::from_raw_parts_mut(definition.base, definition.current_length)
}
}
/// Returns the base pointer, in the host's address space, that the memory
/// is located at.
///
/// When reading and manipulating memory be sure to read up on the caveats
/// of [`Memory::data_unchecked`] to make sure that you can safely
/// read/write the memory.
///
/// For more information and examples see the documentation on the
/// [`Memory`] type.
pub fn data_ptr(&self) -> *mut u8 {
unsafe { (*self.wasmtime_export.definition).base }
///
/// # Panics
///
/// Panics if this memory doesn't belong to `store`.
pub fn data_ptr(&self, store: impl AsContext) -> *mut u8 {
unsafe { (*store.as_context()[self.0].definition).base }
}
/// Returns the byte length of this memory.
@@ -403,21 +357,29 @@ impl Memory {
///
/// For more information and examples see the documentation on the
/// [`Memory`] type.
pub fn data_size(&self) -> usize {
unsafe { (*self.wasmtime_export.definition).current_length }
///
/// # Panics
///
/// Panics if this memory doesn't belong to `store`.
pub fn data_size(&self, store: impl AsContext) -> usize {
unsafe { (*store.as_context()[self.0].definition).current_length }
}
/// Returns the size, in pages, of this wasm memory.
pub fn size(&self) -> u32 {
(self.data_size() / wasmtime_environ::WASM_PAGE_SIZE as usize) as u32
/// Returns the size, in WebAssembly pages, of this wasm memory.
///
/// # Panics
///
/// Panics if this memory doesn't belong to `store`.
pub fn size(&self, store: impl AsContext) -> u32 {
(self.data_size(store) / wasmtime_environ::WASM_PAGE_SIZE as usize) as u32
}
/// Grows this WebAssembly memory by `delta` pages.
///
/// This will attempt to add `delta` more pages of memory on to the end of
/// this `Memory` instance. If successful this may relocate the memory and
/// cause [`Memory::data_ptr`] to return a new value. Additionally previous
/// slices into this memory may no longer be valid.
/// cause [`Memory::data_ptr`] to return a new value. Additionally any
/// unsafetly constructed slices into this memory may no longer be valid.
///
/// On success returns the number of pages this memory previously had
/// before the growth succeeded.
@@ -425,7 +387,13 @@ impl Memory {
/// # Errors
///
/// Returns an error if memory could not be grown, for example if it exceeds
/// the maximum limits of this memory.
/// the maximum limits of this memory. A
/// [`ResourceLimiter`](crate::ResourceLimiter) is another example of
/// preventing a memory to grow.
///
/// # Panics
///
/// Panics if this memory doesn't belong to `store`.
///
/// # Examples
///
@@ -433,52 +401,68 @@ impl Memory {
/// # use wasmtime::*;
/// # fn main() -> anyhow::Result<()> {
/// let engine = Engine::default();
/// let store = Store::new(&engine);
/// let mut store = Store::new(&engine, ());
/// let module = Module::new(&engine, "(module (memory (export \"mem\") 1 2))")?;
/// let instance = Instance::new(&store, &module, &[])?;
/// let memory = instance.get_memory("mem").unwrap();
/// let instance = Instance::new(&mut store, &module, &[])?;
/// let memory = instance.get_memory(&mut store, "mem").unwrap();
///
/// assert_eq!(memory.size(), 1);
/// assert_eq!(memory.grow(1)?, 1);
/// assert_eq!(memory.size(), 2);
/// assert!(memory.grow(1).is_err());
/// assert_eq!(memory.size(), 2);
/// assert_eq!(memory.grow(0)?, 2);
/// assert_eq!(memory.size(&store), 1);
/// assert_eq!(memory.grow(&mut store, 1)?, 1);
/// assert_eq!(memory.size(&store), 2);
/// assert!(memory.grow(&mut store, 1).is_err());
/// assert_eq!(memory.size(&store), 2);
/// assert_eq!(memory.grow(&mut store, 0)?, 2);
/// # Ok(())
/// # }
/// ```
pub fn grow(&self, delta: u32) -> Result<u32> {
let index = self
.instance
.memory_index(unsafe { &*self.wasmtime_export.definition });
self.instance
.memory_grow(index, delta)
.ok_or_else(|| anyhow!("failed to grow memory by `{}`", delta))
pub fn grow(&self, mut store: impl AsContextMut, delta: u32) -> Result<u32> {
let mut store = store.as_context_mut().opaque();
let mem = self.wasmtime_memory(&mut store);
unsafe {
match (*mem).grow(delta, store.limiter()) {
Some(size) => {
let vm = (*mem).vmmemory();
*store[self.0].definition = vm;
Ok(size)
}
None => bail!("failed to grow memory by `{}`", delta),
}
}
}
fn wasmtime_memory(&self, store: &mut StoreOpaque<'_>) -> *mut wasmtime_runtime::Memory {
unsafe {
let export = &store[self.0];
let mut handle = wasmtime_runtime::InstanceHandle::from_vmctx(export.vmctx);
let idx = handle.memory_index(&*export.definition);
handle.get_defined_memory(idx)
}
}
pub(crate) unsafe fn from_wasmtime_memory(
wasmtime_export: &wasmtime_runtime::ExportMemory,
store: &Store,
wasmtime_export: wasmtime_runtime::ExportMemory,
store: &mut StoreOpaque,
) -> Memory {
Memory {
instance: store.existing_vmctx(wasmtime_export.vmctx),
wasmtime_export: wasmtime_export.clone(),
}
Memory(store.store_data_mut().insert(wasmtime_export))
}
pub(crate) fn wasmtime_ty(&self) -> &wasmtime_environ::wasm::Memory {
&self.wasmtime_export.memory.memory
pub(crate) fn wasmtime_ty<'a>(
&self,
store: &'a StoreData,
) -> &'a wasmtime_environ::wasm::Memory {
&store[self.0].memory.memory
}
pub(crate) fn vmimport(&self) -> wasmtime_runtime::VMMemoryImport {
pub(crate) fn vmimport(&self, store: &StoreOpaque<'_>) -> wasmtime_runtime::VMMemoryImport {
let export = &store[self.0];
wasmtime_runtime::VMMemoryImport {
from: self.wasmtime_export.definition,
vmctx: self.wasmtime_export.vmctx,
from: export.definition,
vmctx: export.vmctx,
}
}
pub(crate) fn wasmtime_export(&self) -> &wasmtime_runtime::ExportMemory {
&self.wasmtime_export
pub(crate) fn comes_from_same_store(&self, store: &StoreOpaque) -> bool {
store.store_data().contains(self.0)
}
}
@@ -488,14 +472,15 @@ impl Memory {
/// one can supply wasmtime with custom allocated host managed memory.
///
/// # Safety
/// The memory should be page aligned and a multiple of page size.
/// To prevent possible silent overflows, the memory should be protected by a guard page.
/// Additionally the safety concerns explained in ['Memory'], for accessing the memory
/// apply here as well.
///
/// Note that this is a relatively new and experimental feature and it is recommended
/// to be familiar with wasmtime runtime code to use it.
pub unsafe trait LinearMemory {
/// The memory should be page aligned and a multiple of page size.
/// To prevent possible silent overflows, the memory should be protected by a
/// guard page. Additionally the safety concerns explained in ['Memory'], for
/// accessing the memory apply here as well.
///
/// Note that this is a relatively new and experimental feature and it is
/// recommended to be familiar with wasmtime runtime code to use it.
pub unsafe trait LinearMemory: Send + Sync + 'static {
/// Returns the number of allocated wasm pages.
fn size(&self) -> u32;
@@ -507,7 +492,7 @@ pub unsafe trait LinearMemory {
///
/// Returns `None` if memory can't be grown by the specified amount
/// of wasm pages.
fn grow(&self, delta: u32) -> Option<u32>;
fn grow(&mut self, delta: u32) -> Option<u32>;
/// Return the allocated memory as a mutable pointer to u8.
fn as_ptr(&self) -> *mut u8;
@@ -517,13 +502,14 @@ pub unsafe trait LinearMemory {
/// to wasmtime which supplies host managed memory.
///
/// # Safety
/// This trait is unsafe, as the memory safety depends on proper implementation of
/// memory management. Memories created by the MemoryCreator should always be treated
/// as owned by wasmtime instance, and any modification of them outside of wasmtime
/// invoked routines is unsafe and may lead to corruption.
///
/// Note that this is a relatively new and experimental feature and it is recommended
/// to be familiar with wasmtime runtime code to use it.
/// This trait is unsafe, as the memory safety depends on proper implementation
/// of memory management. Memories created by the MemoryCreator should always be
/// treated as owned by wasmtime instance, and any modification of them outside
/// of wasmtime invoked routines is unsafe and may lead to corruption.
///
/// Note that this is a relatively new and experimental feature and it is
/// recommended to be familiar with wasmtime runtime code to use it.
pub unsafe trait MemoryCreator: Send + Sync {
/// Create a new `LinearMemory` object from the specified parameters.
///
@@ -569,11 +555,12 @@ mod tests {
let mut cfg = Config::new();
cfg.static_memory_maximum_size(0)
.dynamic_memory_guard_size(0);
let store = Store::new(&Engine::new(&cfg).unwrap());
let mut store = Store::new(&Engine::new(&cfg).unwrap(), ());
let ty = MemoryType::new(Limits::new(1, None));
let mem = Memory::new(&store, ty).unwrap();
assert_eq!(mem.wasmtime_export.memory.offset_guard_size, 0);
match mem.wasmtime_export.memory.style {
let mem = Memory::new(&mut store, ty).unwrap();
let store = store.as_context();
assert_eq!(store[mem.0].memory.offset_guard_size, 0);
match &store[mem.0].memory.style {
wasmtime_environ::MemoryStyle::Dynamic => {}
other => panic!("unexpected style {:?}", other),
}

View File

@@ -33,24 +33,6 @@ fn func_by_pc(module: &CompiledModule, pc: usize) -> Option<(DefinedFuncIndex, u
pub struct ModuleRegistry(BTreeMap<usize, Arc<RegisteredModule>>);
impl ModuleRegistry {
/// Fetches frame information about a program counter in a backtrace.
///
/// Returns an object if this `pc` is known to some previously registered
/// module, or returns `None` if no information can be found. The boolean
/// returned indicates whether the original module has unparsed debug
/// information due to the compiler's configuration.
pub fn lookup_frame_info(&self, pc: usize) -> Option<(FrameInfo, bool)> {
let module = self.module(pc)?;
module
.lookup_frame_info(pc)
.map(|info| (info, module.has_unparsed_debuginfo()))
}
/// Fetches trap information about a program counter in a backtrace.
pub fn lookup_trap_info(&self, pc: usize) -> Option<&TrapInformation> {
self.module(pc)?.lookup_trap_info(pc)
}
/// Fetches information about a registered module given a program counter value.
pub fn lookup_module(&self, pc: usize) -> Option<Arc<dyn ModuleInfo>> {
self.module(pc)
@@ -132,87 +114,6 @@ struct RegisteredModule {
}
impl RegisteredModule {
/// Determines if the related module has unparsed debug information.
pub fn has_unparsed_debuginfo(&self) -> bool {
self.module.has_unparsed_debuginfo()
}
/// Fetches frame information about a program counter in a backtrace.
///
/// Returns an object if this `pc` is known to this module, or returns `None`
/// if no information can be found.
pub fn lookup_frame_info(&self, pc: usize) -> Option<FrameInfo> {
let (index, offset) = func_by_pc(&self.module, pc)?;
let info = self.module.func_info(index);
let pos = Self::instr_pos(offset, &info.address_map);
// In debug mode for now assert that we found a mapping for `pc` within
// the function, because otherwise something is buggy along the way and
// not accounting for all the instructions. This isn't super critical
// though so we can omit this check in release mode.
debug_assert!(pos.is_some(), "failed to find instruction for {:x}", pc);
let instr = match pos {
Some(pos) => info.address_map.instructions[pos].srcloc,
None => info.address_map.start_srcloc,
};
// Use our wasm-relative pc to symbolize this frame. If there's a
// symbolication context (dwarf debug info) available then we can try to
// look this up there.
//
// Note that dwarf pcs are code-section-relative, hence the subtraction
// from the location of `instr`. Also note that all errors are ignored
// here for now since technically wasm modules can always have any
// custom section contents.
let mut symbols = Vec::new();
if let Some(s) = &self.module.symbolize_context().ok().and_then(|c| c) {
let to_lookup = (instr.bits() as u64) - s.code_section_offset();
if let Ok(mut frames) = s.addr2line().find_frames(to_lookup) {
while let Ok(Some(frame)) = frames.next() {
symbols.push(FrameSymbol {
name: frame
.function
.as_ref()
.and_then(|l| l.raw_name().ok())
.map(|s| s.to_string()),
file: frame
.location
.as_ref()
.and_then(|l| l.file)
.map(|s| s.to_string()),
line: frame.location.as_ref().and_then(|l| l.line),
column: frame.location.as_ref().and_then(|l| l.column),
});
}
}
}
let module = self.module.module();
let index = module.func_index(index);
Some(FrameInfo {
module_name: module.name.clone(),
func_index: index.index() as u32,
func_name: module.func_names.get(&index).cloned(),
instr,
func_start: info.address_map.start_srcloc,
symbols,
})
}
/// Fetches trap information about a program counter in a backtrace.
pub fn lookup_trap_info(&self, pc: usize) -> Option<&TrapInformation> {
let (index, offset) = func_by_pc(&self.module, pc)?;
let info = self.module.func_info(index);
let idx = info
.traps
.binary_search_by_key(&offset, |info| info.code_offset)
.ok()?;
Some(&info.traps[idx])
}
fn instr_pos(offset: u32, addr_map: &FunctionAddressMap) -> Option<usize> {
// Use our relative position from the start of the function to find the
// machine instruction that corresponds to `pc`, which then allows us to
@@ -306,6 +207,7 @@ impl ModuleInfo for RegisteredModule {
struct GlobalRegisteredModule {
start: usize,
module: Arc<CompiledModule>,
wasm_backtrace_details_env_used: bool,
/// Note that modules can be instantiated in many stores, so the purpose of
/// this field is to keep track of how many stores have registered a
/// module. Information is only removed from the global registry when this
@@ -335,30 +237,69 @@ impl GlobalModuleRegistry {
pub(crate) fn is_wasm_pc(pc: usize) -> bool {
let modules = GLOBAL_MODULES.lock().unwrap();
match modules.0.range(pc..).next() {
Some((end, entry)) => {
if pc < entry.start || *end < pc {
return false;
match modules.module(pc) {
Some(entry) => match func_by_pc(&entry.module, pc) {
Some((index, offset)) => {
let info = entry.module.func_info(index);
RegisteredModule::instr_pos(offset, &info.address_map).is_some()
}
match func_by_pc(&entry.module, pc) {
Some((index, offset)) => {
let info = entry.module.func_info(index);
RegisteredModule::instr_pos(offset, &info.address_map).is_some()
}
None => false,
}
}
None => false,
},
None => false,
}
}
fn module(&self, pc: usize) -> Option<&GlobalRegisteredModule> {
let (end, info) = self.0.range(pc..).next()?;
if pc < info.start || *end < pc {
return None;
}
Some(info)
}
// Work with the global instance of `GlobalModuleRegistry`. Note that only
// shared access is allowed, this isn't intended to mutate the contents.
//
// (right now we use a `Mutex` but if motivated we could one day use a
// `RwLock`)
pub(crate) fn with<R>(f: impl FnOnce(&GlobalModuleRegistry) -> R) -> R {
f(&GLOBAL_MODULES.lock().unwrap())
}
/// Fetches frame information about a program counter in a backtrace.
///
/// Returns an object if this `pc` is known to some previously registered
/// module, or returns `None` if no information can be found. The first
/// boolean returned indicates whether the original module has unparsed
/// debug information due to the compiler's configuration. The second
/// boolean indicates whether the engine used to compile this module is
/// using environment variables to control debuginfo parsing.
pub(crate) fn lookup_frame_info(&self, pc: usize) -> Option<(FrameInfo, bool, bool)> {
let module = self.module(pc)?;
module.lookup_frame_info(pc).map(|info| {
(
info,
module.has_unparsed_debuginfo(),
module.wasm_backtrace_details_env_used,
)
})
}
/// Fetches trap information about a program counter in a backtrace.
pub(crate) fn lookup_trap_info(&self, pc: usize) -> Option<&TrapInformation> {
self.module(pc)?.lookup_trap_info(pc)
}
/// Registers a new region of code, described by `(start, end)` and with
/// the given function information, with the global information.
fn register(&mut self, start: usize, end: usize, module: &Module) {
let info = self.0.entry(end).or_insert_with(|| GlobalRegisteredModule {
start,
module: module.compiled_module().clone(),
wasm_backtrace_details_env_used: module
.engine()
.config()
.wasm_backtrace_details_env_used,
references: 0,
});
@@ -380,6 +321,89 @@ impl GlobalModuleRegistry {
}
}
impl GlobalRegisteredModule {
/// Determines if the related module has unparsed debug information.
pub fn has_unparsed_debuginfo(&self) -> bool {
self.module.has_unparsed_debuginfo()
}
/// Fetches frame information about a program counter in a backtrace.
///
/// Returns an object if this `pc` is known to this module, or returns `None`
/// if no information can be found.
pub fn lookup_frame_info(&self, pc: usize) -> Option<FrameInfo> {
let (index, offset) = func_by_pc(&self.module, pc)?;
let info = self.module.func_info(index);
let pos = RegisteredModule::instr_pos(offset, &info.address_map);
// In debug mode for now assert that we found a mapping for `pc` within
// the function, because otherwise something is buggy along the way and
// not accounting for all the instructions. This isn't super critical
// though so we can omit this check in release mode.
debug_assert!(pos.is_some(), "failed to find instruction for {:x}", pc);
let instr = match pos {
Some(pos) => info.address_map.instructions[pos].srcloc,
None => info.address_map.start_srcloc,
};
// Use our wasm-relative pc to symbolize this frame. If there's a
// symbolication context (dwarf debug info) available then we can try to
// look this up there.
//
// Note that dwarf pcs are code-section-relative, hence the subtraction
// from the location of `instr`. Also note that all errors are ignored
// here for now since technically wasm modules can always have any
// custom section contents.
let mut symbols = Vec::new();
if let Some(s) = &self.module.symbolize_context().ok().and_then(|c| c) {
let to_lookup = (instr.bits() as u64) - s.code_section_offset();
if let Ok(mut frames) = s.addr2line().find_frames(to_lookup) {
while let Ok(Some(frame)) = frames.next() {
symbols.push(FrameSymbol {
name: frame
.function
.as_ref()
.and_then(|l| l.raw_name().ok())
.map(|s| s.to_string()),
file: frame
.location
.as_ref()
.and_then(|l| l.file)
.map(|s| s.to_string()),
line: frame.location.as_ref().and_then(|l| l.line),
column: frame.location.as_ref().and_then(|l| l.column),
});
}
}
}
let module = self.module.module();
let index = module.func_index(index);
Some(FrameInfo {
module_name: module.name.clone(),
func_index: index.index() as u32,
func_name: module.func_names.get(&index).cloned(),
instr,
func_start: info.address_map.start_srcloc,
symbols,
})
}
/// Fetches trap information about a program counter in a backtrace.
pub fn lookup_trap_info(&self, pc: usize) -> Option<&TrapInformation> {
let (index, offset) = func_by_pc(&self.module, pc)?;
let info = self.module.func_info(index);
let idx = info
.traps
.binary_search_by_key(&offset, |info| info.code_offset)
.ok()?;
Some(&info.traps[idx])
}
}
/// Description of a frame in a backtrace for a [`Trap`].
///
/// Whenever a WebAssembly trap occurs an instance of [`Trap`] is created. Each
@@ -522,7 +546,7 @@ impl FrameSymbol {
#[test]
fn test_frame_info() -> Result<(), anyhow::Error> {
use crate::*;
let store = Store::default();
let mut store = Store::<()>::default();
let module = Module::new(
store.engine(),
r#"
@@ -538,18 +562,20 @@ fn test_frame_info() -> Result<(), anyhow::Error> {
"#,
)?;
// Create an instance to ensure the frame information is registered.
Instance::new(&store, &module, &[])?;
let modules = store.modules().borrow();
for (i, alloc) in module.compiled_module().finished_functions() {
let (start, end) = unsafe {
let ptr = (**alloc).as_ptr();
let len = (**alloc).len();
(ptr as usize, ptr as usize + len)
};
for pc in start..end {
let (frame, _) = modules.lookup_frame_info(pc).unwrap();
assert!(frame.func_index() == i.as_u32());
Instance::new(&mut store, &module, &[])?;
GlobalModuleRegistry::with(|modules| {
for (i, alloc) in module.compiled_module().finished_functions() {
let (start, end) = unsafe {
let ptr = (**alloc).as_ptr();
let len = (**alloc).len();
(ptr as usize, ptr as usize + len)
};
for pc in start..end {
let (frame, _, _) = modules.lookup_frame_info(pc).unwrap();
assert!(frame.func_index() == i.as_u32());
}
}
}
});
Ok(())
}

View File

@@ -5,6 +5,7 @@ use wasmtime_runtime::VMExternRef;
/// Represents an opaque reference to any data within WebAssembly.
#[derive(Clone, Debug)]
#[repr(transparent)]
pub struct ExternRef {
pub(crate) inner: VMExternRef,
}
@@ -13,7 +14,7 @@ impl ExternRef {
/// Creates a new instance of `ExternRef` wrapping the given value.
pub fn new<T>(value: T) -> ExternRef
where
T: 'static + Any,
T: 'static + Any + Send + Sync,
{
let inner = VMExternRef::new(value);
ExternRef { inner }
@@ -25,6 +26,9 @@ impl ExternRef {
}
/// Get the strong reference count for this `ExternRef`.
///
/// Note that this loads the reference count with a `SeqCst` ordering to
/// synchronize with other threads.
pub fn strong_count(&self) -> usize {
self.inner.strong_count()
}

View File

@@ -24,15 +24,6 @@ pub struct SignatureCollection {
}
impl SignatureCollection {
/// Creates a new, empty signature collection given a signature registry.
pub fn new(registry: &SignatureRegistry) -> Self {
Self {
registry: registry.0.clone(),
signatures: PrimaryMap::new(),
trampolines: HashMap::new(),
}
}
/// Creates a signature collection for a module given the module's signatures
/// and trampolines.
pub fn new_for_module(
@@ -73,27 +64,6 @@ impl SignatureCollection {
.get(&index)
.map(|(_, trampoline)| *trampoline)
}
/// Registers a single function with the collection.
///
/// Returns the shared signature index for the function.
pub fn register(
&mut self,
ty: &WasmFuncType,
trampoline: VMTrampoline,
) -> VMSharedSignatureIndex {
let index = self.registry.write().unwrap().register(ty);
let entry = match self.trampolines.entry(index) {
Entry::Occupied(e) => e.into_mut(),
Entry::Vacant(e) => e.insert((0, trampoline)),
};
// Increment the ref count
entry.0 += 1;
index
}
}
impl Drop for SignatureCollection {
@@ -259,4 +229,18 @@ impl SignatureRegistry {
.get(index.bits() as usize)
.and_then(|e| e.as_ref().map(|e| &e.ty).cloned())
}
/// Registers a single function with the collection.
///
/// Returns the shared signature index for the function.
pub fn register(&self, ty: &WasmFuncType) -> VMSharedSignatureIndex {
self.0.write().unwrap().register(ty)
}
/// Registers a single function with the collection.
///
/// Returns the shared signature index for the function.
pub unsafe fn unregister(&self, sig: VMSharedSignatureIndex) {
self.0.write().unwrap().unregister_entry(sig, 1)
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,275 @@
use crate::store::{Store, StoreInner};
use std::ops::{Deref, DerefMut};
/// A temporary handle to a [`&Store<T>`][`Store`].
///
/// This type is sutable for [`AsContext`] trait bounds on methods if desired.
/// For more information, see [`Store`].
// NB the repr(transparent) here is for the C API and it's important that the
// representation of this `struct` is a pointer for now. If the representation
// changes then the C API will need to be updated
#[repr(transparent)]
pub struct StoreContext<'a, T>(pub(super) &'a StoreInner<T>);
/// A temporary handle to a [`&mut Store<T>`][`Store`].
///
/// This type is sutable for [`AsContextMut`] or [`AsContext`] trait bounds on
/// methods if desired. For more information, see [`Store`].
// NB the repr(transparent) here is for the same reason as above.
#[repr(transparent)]
pub struct StoreContextMut<'a, T>(pub(super) &'a mut StoreInner<T>);
impl<'a, T> StoreContextMut<'a, T> {
/// One of the unsafe lynchpins of Wasmtime.
///
/// This method is called from one location, `Caller::with`, and is where we
/// load the raw unsafe trait object pointer from a `*mut VMContext` and
/// then cast it back to a `StoreContextMut`. This is naturally unsafe due
/// to the raw pointer usage, but it's also unsafe because `T` here needs to
/// line up with the `T` used to define the trait object itself.
///
/// This should generally be achieved with various trait bounds throughout
/// Wasmtime that might give access to the `Caller<'_, T>` type.
/// Unfortunately there's not a ton of debug asserts we can add here, so we
/// rely on testing to largely help show that this is correctly used.
pub(crate) unsafe fn from_raw(
store: *mut dyn wasmtime_runtime::Store,
) -> StoreContextMut<'a, T> {
StoreContextMut(&mut *(store as *mut StoreInner<T>))
}
/// A helper method to erase the `T` on `Self` so the returned type has no
/// generics. For some more information see [`StoreOpaque`] itself.
///
/// The primary purpose of this is to help improve compile times where
/// non-generic code can be compiled into libwasmtime.rlib.
pub(crate) fn opaque(mut self) -> StoreOpaque<'a> {
StoreOpaque {
traitobj: self.traitobj(),
inner: self.0,
}
}
/// Same as `opaque`, but produces a value which implements the `Send`
/// trait. Useful for futures.
pub(crate) fn opaque_send(mut self) -> StoreOpaqueSend<'a>
where
T: Send,
{
StoreOpaqueSend {
traitobj: self.traitobj(),
inner: self.0,
}
}
fn traitobj(&mut self) -> *mut dyn wasmtime_runtime::Store {
unsafe {
std::mem::transmute::<
*mut (dyn wasmtime_runtime::Store + '_),
*mut (dyn wasmtime_runtime::Store + 'static),
>(self.0)
}
}
}
/// A trait used to get shared access to a [`Store`] in Wasmtime.
///
/// This trait is used as a bound on the first argument of many methods within
/// Wasmtime. This trait is implemented for types like [`Store`],
/// [`Caller`](crate::Caller), and [`StoreContext`] itself. Implementors of this
/// trait provide access to a [`StoreContext`] via some means, allowing the
/// method in question to get access to the store's internal information.
///
/// Note that this is only used in contexts where the store's information is
/// read, but not written. For example methods that return type information will
/// use this trait as a bound. More commonly, though, mutation is required and
/// [`AsContextMut`] is needed.
pub trait AsContext {
/// The host information associated with the [`Store`], aka the `T` in
/// [`Store<T>`].
type Data;
/// Returns the store context that this type provides access to.
fn as_context(&self) -> StoreContext<'_, Self::Data>;
}
/// A trait used to get exclusive mutable access to a [`Store`] in Wasmtime.
///
/// This trait is used as a bound on the first argument of many methods within
/// Wasmtime. This trait is implemented for types like [`Store`],
/// [`Caller`](crate::Caller), and [`StoreContextMut`] itself. Implementors of
/// this trait provide access to a [`StoreContextMut`] via some means, allowing
/// the method in question to get access to the store's internal information.
///
/// This is notably used for methods that may require some mutation of the
/// [`Store`] itself. For example calling a wasm function can mutate linear
/// memory or globals. Creation of a [`Func`](crate::Func) will update internal
/// data structures. This ends up being quite a common bound in Wasmtime, but
/// typically you can simply pass `&mut store` or `&mut caller` to satisfy it.
pub trait AsContextMut: AsContext {
/// Returns the store context that this type provides access to.
fn as_context_mut(&mut self) -> StoreContextMut<'_, Self::Data>;
}
impl<T> AsContext for Store<T> {
type Data = T;
#[inline]
fn as_context(&self) -> StoreContext<'_, T> {
StoreContext(&self.inner)
}
}
impl<T> AsContextMut for Store<T> {
#[inline]
fn as_context_mut(&mut self) -> StoreContextMut<'_, T> {
StoreContextMut(&mut self.inner)
}
}
impl<T> AsContext for StoreContext<'_, T> {
type Data = T;
#[inline]
fn as_context(&self) -> StoreContext<'_, T> {
StoreContext(&*self.0)
}
}
impl<T> AsContext for StoreContextMut<'_, T> {
type Data = T;
#[inline]
fn as_context(&self) -> StoreContext<'_, T> {
StoreContext(&*self.0)
}
}
impl<T> AsContextMut for StoreContextMut<'_, T> {
#[inline]
fn as_context_mut(&mut self) -> StoreContextMut<'_, T> {
StoreContextMut(&mut *self.0)
}
}
// forward AsContext for &T
impl<T: AsContext> AsContext for &'_ T {
type Data = T::Data;
#[inline]
fn as_context(&self) -> StoreContext<'_, T::Data> {
T::as_context(*self)
}
}
// forward AsContext for &mut T
impl<T: AsContext> AsContext for &'_ mut T {
type Data = T::Data;
#[inline]
fn as_context(&self) -> StoreContext<'_, T::Data> {
T::as_context(*self)
}
}
// forward AsContextMut for &mut T
impl<T: AsContextMut> AsContextMut for &'_ mut T {
#[inline]
fn as_context_mut(&mut self) -> StoreContextMut<'_, T::Data> {
T::as_context_mut(*self)
}
}
//
impl<'a, T: AsContext> From<&'a T> for StoreContext<'a, T::Data> {
fn from(t: &'a T) -> StoreContext<'a, T::Data> {
t.as_context()
}
}
impl<'a, T: AsContext> From<&'a mut T> for StoreContext<'a, T::Data> {
fn from(t: &'a mut T) -> StoreContext<'a, T::Data> {
T::as_context(t)
}
}
impl<'a, T: AsContextMut> From<&'a mut T> for StoreContextMut<'a, T::Data> {
fn from(t: &'a mut T) -> StoreContextMut<'a, T::Data> {
t.as_context_mut()
}
}
/// This structure is akin to a `StoreContextMut` except that the `T` is
/// "erased" to an opaque type.
///
/// This structure is used pervasively through wasmtime whenever the `T` isn't
/// needed (quite common!). This allows the compiler to erase generics and
/// compile more code in the wasmtime crate itself instead of monomorphizing
/// everything into consumer crates. The primary purpose of this is to help
/// compile times.
#[doc(hidden)] // this is part of `WasmTy`, but a hidden part, so hide this
pub struct StoreOpaque<'a> {
/// The actual pointer to the `StoreInner` internals.
inner: &'a mut StoreInner<dyn Opaque + 'a>,
/// A raw trait object that can be used to invoke functions with. Note that
/// this is a pointer which aliases with `inner` above, so extreme care
/// needs to be used when using this (the above `inner` cannot be actively
/// borrowed).
pub traitobj: *mut dyn wasmtime_runtime::Store,
}
pub trait Opaque {}
impl<T> Opaque for T {}
// Deref impls to forward all methods on `StoreOpaque` to `StoreInner`.
impl<'a> Deref for StoreOpaque<'a> {
type Target = StoreInner<dyn Opaque + 'a>;
#[inline]
fn deref(&self) -> &Self::Target {
&*self.inner
}
}
impl<'a> DerefMut for StoreOpaque<'a> {
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target {
&mut *self.inner
}
}
pub struct StoreOpaqueSend<'a> {
/// The actual pointer to the `StoreInner` internals.
inner: &'a mut StoreInner<dyn Opaque + Send + 'a>,
pub traitobj: *mut dyn wasmtime_runtime::Store,
}
// This is needed due to the raw pointer `traitobj`, which is simply a pointer
// to `inner` which is already `Send`.
unsafe impl Send for StoreOpaqueSend<'_> {}
impl StoreOpaqueSend<'_> {
pub fn opaque(&mut self) -> StoreOpaque<'_> {
StoreOpaque {
inner: &mut *self.inner,
traitobj: self.traitobj,
}
}
}
impl<'a> Deref for StoreOpaqueSend<'a> {
type Target = StoreInner<dyn Opaque + Send + 'a>;
#[inline]
fn deref(&self) -> &Self::Target {
&*self.inner
}
}
impl<'a> DerefMut for StoreOpaqueSend<'a> {
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target {
&mut *self.inner
}
}

View File

@@ -0,0 +1,195 @@
use crate::store::StoreOpaque;
use crate::{StoreContext, StoreContextMut};
use std::fmt;
use std::marker;
use std::num::NonZeroU64;
use std::ops::Index;
use std::sync::atomic::{AtomicU64, Ordering::SeqCst};
// This is defined here, in a private submodule, so we can explicitly reexport
// it only as `pub(crate)`. This avoids a ton of
// crate-private-type-in-public-interface errors that aren't really too
// interesting to deal with.
#[derive(Copy, Clone)]
pub struct InstanceId(pub(super) usize);
pub struct StoreData {
id: NonZeroU64,
funcs: Vec<crate::func::FuncData>,
tables: Vec<wasmtime_runtime::ExportTable>,
globals: Vec<wasmtime_runtime::ExportGlobal>,
instances: Vec<crate::instance::RuntimeInstance>,
memories: Vec<wasmtime_runtime::ExportMemory>,
}
pub trait StoredData: Sized {
fn get<'a>(id: Stored<Self>, data: &'a StoreData) -> &'a Self;
fn insert(self, data: &mut StoreData) -> Stored<Self>;
fn assert_contained(id: Stored<Self>, data: &StoreData);
}
macro_rules! impl_store_data {
($($field:ident => $t:ty,)*) => ($(
impl StoredData for $t {
#[inline]
fn get<'a>(id: Stored<Self>, data: &'a StoreData) -> &'a Self {
assert!(id.store_id() == data.id,
"object used with the wrong store");
&data.$field[id.index()]
}
fn insert(self, data: &mut StoreData) -> Stored<Self> {
let index = data.$field.len();
data.$field.push(self);
Stored::new(data.id, index)
}
fn assert_contained(id: Stored<Self>, data: &StoreData) {
assert!(id.index() < data.$field.len());
}
}
)*)
}
impl_store_data! {
funcs => crate::func::FuncData,
tables => wasmtime_runtime::ExportTable,
globals => wasmtime_runtime::ExportGlobal,
instances => crate::instance::RuntimeInstance,
memories => wasmtime_runtime::ExportMemory,
}
impl StoreData {
pub fn new() -> StoreData {
static NEXT_ID: AtomicU64 = AtomicU64::new(0);
// Only allow 2^63 stores at which point we start panicking to prevent
// overflow. This should still last us to effectively the end of time.
let id = NEXT_ID.fetch_add(1, SeqCst);
if id & (1 << 63) != 0 {
NEXT_ID.store(1 << 63, SeqCst);
panic!("store id allocator overflow");
}
StoreData {
id: NonZeroU64::new(id + 1).unwrap(),
funcs: Vec::new(),
tables: Vec::new(),
globals: Vec::new(),
instances: Vec::new(),
memories: Vec::new(),
}
}
pub fn insert<T>(&mut self, data: T) -> Stored<T>
where
T: StoredData,
{
data.insert(self)
}
pub fn contains<T>(&self, id: Stored<T>) -> bool
where
T: StoredData,
{
if id.store_id() != self.id {
return false;
}
// this should be true as an invariant of our API, but double-check with
// debug assertions enabled.
if cfg!(debug_assertions) {
T::assert_contained(id, self);
}
true
}
}
impl<T> Index<Stored<T>> for StoreData
where
T: StoredData,
{
type Output = T;
#[inline]
fn index(&self, index: Stored<T>) -> &Self::Output {
T::get(index, self)
}
}
// forward StoreContext => StoreData
impl<I, T> Index<I> for StoreContext<'_, T>
where
StoreData: Index<I>,
{
type Output = <StoreData as Index<I>>::Output;
#[inline]
fn index(&self, index: I) -> &Self::Output {
self.0.store_data.index(index)
}
}
// forward StoreContextMut => StoreData
impl<I, T> Index<I> for StoreContextMut<'_, T>
where
StoreData: Index<I>,
{
type Output = <StoreData as Index<I>>::Output;
#[inline]
fn index(&self, index: I) -> &Self::Output {
self.0.store_data.index(index)
}
}
// forward StoreOpaque => StoreData
impl<I> Index<I> for StoreOpaque<'_>
where
StoreData: Index<I>,
{
type Output = <StoreData as Index<I>>::Output;
#[inline]
fn index(&self, index: I) -> &Self::Output {
self.store_data().index(index)
}
}
#[repr(C)] // used by reference in the C API
pub struct Stored<T> {
store_id: NonZeroU64,
index: usize,
_marker: marker::PhantomData<fn() -> T>,
}
impl<T> Stored<T> {
fn new(store_id: NonZeroU64, index: usize) -> Stored<T> {
Stored {
store_id,
index,
_marker: marker::PhantomData,
}
}
fn store_id(&self) -> NonZeroU64 {
self.store_id
}
fn index(&self) -> usize {
self.index
}
}
impl<T> Copy for Stored<T> {}
impl<T> Clone for Stored<T> {
fn clone(&self) -> Self {
*self
}
}
impl<T> fmt::Debug for Stored<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "store={}, index={}", self.store_id(), self.index())
}
}

View File

@@ -11,53 +11,25 @@ pub use self::func::{create_function, create_raw_function};
use self::global::create_global;
use self::memory::create_memory;
use self::table::create_table;
use crate::{GlobalType, MemoryType, Store, TableType, Val};
use crate::store::{InstanceId, StoreOpaque};
use crate::{GlobalType, MemoryType, TableType, Val};
use anyhow::Result;
use std::any::Any;
use std::ops::Deref;
use std::sync::Arc;
use wasmtime_environ::{entity::PrimaryMap, wasm, Module};
use wasmtime_runtime::{
Imports, InstanceAllocationRequest, InstanceAllocator, InstanceHandle,
OnDemandInstanceAllocator, VMExternRefActivationsTable, VMFunctionBody, VMFunctionImport,
VMSharedSignatureIndex,
Imports, InstanceAllocationRequest, InstanceAllocator, OnDemandInstanceAllocator,
VMFunctionBody, VMFunctionImport, VMSharedSignatureIndex,
};
/// A wrapper around `wasmtime_runtime::InstanceHandle` which pairs it with the
/// `Store` that it's rooted within. The instance is deallocated when `Store` is
/// deallocated, so this is a safe handle in terms of memory management for the
/// `Store`.
pub struct StoreInstanceHandle {
pub store: Store,
pub handle: InstanceHandle,
}
impl Clone for StoreInstanceHandle {
fn clone(&self) -> StoreInstanceHandle {
StoreInstanceHandle {
store: self.store.clone(),
// Note should be safe because the lifetime of the instance handle
// is tied to the `Store` which this is paired with.
handle: unsafe { self.handle.clone() },
}
}
}
impl Deref for StoreInstanceHandle {
type Target = InstanceHandle;
fn deref(&self) -> &InstanceHandle {
&self.handle
}
}
fn create_handle(
module: Module,
store: &Store,
store: &mut StoreOpaque<'_>,
finished_functions: PrimaryMap<wasm::DefinedFuncIndex, *mut [VMFunctionBody]>,
host_state: Box<dyn Any>,
host_state: Box<dyn Any + Send + Sync>,
func_imports: &[VMFunctionImport],
shared_signature_id: Option<VMSharedSignatureIndex>,
) -> Result<StoreInstanceHandle> {
) -> Result<InstanceId> {
let mut imports = Imports::default();
imports.functions = func_imports;
@@ -73,12 +45,7 @@ fn create_handle(
imports,
shared_signatures: shared_signature_id.into(),
host_state,
interrupts: store.interrupts(),
externref_activations_table: store.externref_activations_table()
as *const VMExternRefActivationsTable
as *mut _,
module_info_lookup: Some(store.module_info_lookup()),
limiter: store.limiter().as_ref(),
store: Some(store.traitobj),
},
)?;
@@ -87,38 +54,38 @@ fn create_handle(
}
pub fn generate_global_export(
store: &Store,
store: &mut StoreOpaque<'_>,
gt: &GlobalType,
val: Val,
) -> Result<(StoreInstanceHandle, wasmtime_runtime::ExportGlobal)> {
) -> Result<wasmtime_runtime::ExportGlobal> {
let instance = create_global(store, gt, val)?;
let idx = wasm::EntityIndex::Global(wasm::GlobalIndex::from_u32(0));
match instance.lookup_by_declaration(&idx) {
wasmtime_runtime::Export::Global(g) => Ok((instance, g)),
match store.instance(instance).lookup_by_declaration(&idx) {
wasmtime_runtime::Export::Global(g) => Ok(g),
_ => unreachable!(),
}
}
pub fn generate_memory_export(
store: &Store,
store: &mut StoreOpaque<'_>,
m: &MemoryType,
) -> Result<(StoreInstanceHandle, wasmtime_runtime::ExportMemory)> {
) -> Result<wasmtime_runtime::ExportMemory> {
let instance = create_memory(store, m)?;
let idx = wasm::EntityIndex::Memory(wasm::MemoryIndex::from_u32(0));
match instance.lookup_by_declaration(&idx) {
wasmtime_runtime::Export::Memory(m) => Ok((instance, m)),
match store.instance(instance).lookup_by_declaration(&idx) {
wasmtime_runtime::Export::Memory(m) => Ok(m),
_ => unreachable!(),
}
}
pub fn generate_table_export(
store: &Store,
store: &mut StoreOpaque<'_>,
t: &TableType,
) -> Result<(StoreInstanceHandle, wasmtime_runtime::ExportTable)> {
) -> Result<wasmtime_runtime::ExportTable> {
let instance = create_table(store, t)?;
let idx = wasm::EntityIndex::Table(wasm::TableIndex::from_u32(0));
match instance.lookup_by_declaration(&idx) {
wasmtime_runtime::Export::Table(t) => Ok((instance, t)),
match store.instance(instance).lookup_by_declaration(&idx) {
wasmtime_runtime::Export::Table(t) => Ok(t),
_ => unreachable!(),
}
}

View File

@@ -1,6 +1,6 @@
//! Support for a calling of an imported function.
use crate::{Config, FuncType, Store, Trap};
use crate::{Engine, FuncType, Trap};
use anyhow::Result;
use std::any::Any;
use std::cmp;
@@ -9,7 +9,7 @@ use std::panic::{self, AssertUnwindSafe};
use std::sync::Arc;
use wasmtime_environ::entity::PrimaryMap;
use wasmtime_environ::isa::TargetIsa;
use wasmtime_environ::wasm::{DefinedFuncIndex, SignatureIndex};
use wasmtime_environ::wasm::SignatureIndex;
use wasmtime_environ::{ir, wasm, CompiledFunction, Module, ModuleType};
use wasmtime_jit::trampoline::ir::{
ExternalName, Function, InstBuilder, MemFlags, StackSlotData, StackSlotKind,
@@ -25,7 +25,7 @@ use wasmtime_runtime::{
};
struct TrampolineState {
func: Box<dyn Fn(*mut VMContext, *mut u128) -> Result<(), Trap>>,
func: Box<dyn Fn(*mut VMContext, *mut u128) -> Result<(), Trap> + Send + Sync>,
#[allow(dead_code)]
code_memory: CodeMemory,
}
@@ -197,20 +197,15 @@ fn make_trampoline(
.expect("allocate_for_function")
}
fn create_function_trampoline(
config: &Config,
pub fn create_function(
ft: &FuncType,
func: Box<dyn Fn(*mut VMContext, *mut u128) -> Result<(), Trap>>,
) -> Result<(
Module,
PrimaryMap<DefinedFuncIndex, *mut [VMFunctionBody]>,
VMTrampoline,
TrampolineState,
)> {
func: Box<dyn Fn(*mut VMContext, *mut u128) -> Result<(), Trap> + Send + Sync>,
engine: &Engine,
) -> Result<(InstanceHandle, VMTrampoline)> {
// Note that we specifically enable reference types here in our ISA because
// `Func::new` is intended to be infallible, but our signature may use
// reference types which requires safepoints.
let isa = config.target_isa_with_reference_types();
let isa = engine.config().target_isa_with_reference_types();
let mut sig = blank_sig(&*isa, wasmtime_call_conv(&*isa));
sig.params.extend(
@@ -223,27 +218,15 @@ fn create_function_trampoline(
);
let mut fn_builder_ctx = FunctionBuilderContext::new();
let mut module = Module::new();
let mut finished_functions = PrimaryMap::new();
let mut code_memory = CodeMemory::new();
// First up we manufacture a trampoline which has the ABI specified by `ft`
// and calls into `stub_fn`...
let sig_id = SignatureIndex::from_u32(u32::max_value() - 1);
module.types.push(ModuleType::Function(sig_id));
let func_id = module.functions.push(sig_id);
module
.exports
.insert(String::new(), wasm::EntityIndex::Function(func_id));
let trampoline = make_trampoline(isa.as_ref(), &mut code_memory, &mut fn_builder_ctx, &sig);
finished_functions.push(trampoline);
let wasm_trampoline =
make_trampoline(isa.as_ref(), &mut code_memory, &mut fn_builder_ctx, &sig);
// ... and then we also need a trampoline with the standard "trampoline ABI"
// which enters into the ABI specified by `ft`. Note that this is only used
// if `Func::call` is called on an object created by `Func::new`.
let trampoline = trampoline::make_trampoline(
let host_trampoline = trampoline::make_trampoline(
&*isa,
&mut code_memory,
&mut fn_builder_ctx,
@@ -253,52 +236,22 @@ fn create_function_trampoline(
code_memory.publish(isa.as_ref());
let trampoline_state = TrampolineState { func, code_memory };
Ok((module, finished_functions, trampoline, trampoline_state))
}
pub fn create_function(
ft: &FuncType,
func: Box<dyn Fn(*mut VMContext, *mut u128) -> Result<(), Trap>>,
config: &Config,
store: Option<&Store>,
) -> Result<(InstanceHandle, VMTrampoline)> {
let (module, finished_functions, trampoline, trampoline_state) =
create_function_trampoline(config, ft, func)?;
// If there is no store, use the default signature index which is
// guaranteed to trap if there is ever an indirect call on the function (should not happen)
let shared_signature_id = store
.map(|s| {
s.signatures()
.borrow_mut()
.register(ft.as_wasm_func_type(), trampoline)
})
.unwrap_or(VMSharedSignatureIndex::default());
let sig = engine.signatures().register(ft.as_wasm_func_type());
unsafe {
Ok((
OnDemandInstanceAllocator::default().allocate(InstanceAllocationRequest {
module: Arc::new(module),
finished_functions: &finished_functions,
imports: Imports::default(),
shared_signatures: shared_signature_id.into(),
host_state: Box::new(trampoline_state),
interrupts: std::ptr::null(),
externref_activations_table: std::ptr::null_mut(),
module_info_lookup: None,
limiter: None,
})?,
trampoline,
))
let instance = create_raw_function(
wasm_trampoline,
sig,
Box::new(TrampolineState { func, code_memory }),
)?;
Ok((instance, host_trampoline))
}
}
pub unsafe fn create_raw_function(
func: *mut [VMFunctionBody],
host_state: Box<dyn Any>,
shared_signature_id: VMSharedSignatureIndex,
sig: VMSharedSignatureIndex,
host_state: Box<dyn Any + Send + Sync>,
) -> Result<InstanceHandle> {
let mut module = Module::new();
let mut finished_functions = PrimaryMap::new();
@@ -316,12 +269,9 @@ pub unsafe fn create_raw_function(
module: Arc::new(module),
finished_functions: &finished_functions,
imports: Imports::default(),
shared_signatures: shared_signature_id.into(),
shared_signatures: sig.into(),
host_state,
interrupts: std::ptr::null(),
externref_activations_table: std::ptr::null_mut(),
module_info_lookup: None,
limiter: None,
store: None,
})?,
)
}

View File

@@ -1,5 +1,6 @@
use crate::trampoline::{create_handle, StoreInstanceHandle};
use crate::{GlobalType, Mutability, Store, Val};
use crate::store::{InstanceId, StoreOpaque};
use crate::trampoline::create_handle;
use crate::{GlobalType, Mutability, Val};
use anyhow::Result;
use wasmtime_environ::entity::PrimaryMap;
use wasmtime_environ::{
@@ -8,7 +9,7 @@ use wasmtime_environ::{
};
use wasmtime_runtime::VMFunctionImport;
pub fn create_global(store: &Store, gt: &GlobalType, val: Val) -> Result<StoreInstanceHandle> {
pub fn create_global(store: &mut StoreOpaque<'_>, gt: &GlobalType, val: Val) -> Result<InstanceId> {
let mut module = Module::new();
let mut func_imports = Vec::new();
let mut externref_init = None;
@@ -38,8 +39,9 @@ pub fn create_global(store: &Store, gt: &GlobalType, val: Val) -> Result<StoreIn
Val::FuncRef(Some(f)) => {
// Add a function import to the stub module, and then initialize
// our global with a `ref.func` to grab that imported function.
let shared_sig_index = f.sig_index();
shared_signature_id = Some(shared_sig_index);
let f = f.caller_checked_anyfunc(store);
let f = unsafe { f.as_ref() };
shared_signature_id = Some(f.type_index);
let sig_id = SignatureIndex::from_u32(u32::max_value() - 1);
module.types.push(ModuleType::Function(sig_id));
let func_index = module.functions.push(sig_id);
@@ -52,8 +54,6 @@ pub fn create_global(store: &Store, gt: &GlobalType, val: Val) -> Result<StoreIn
index: wasm::EntityIndex::Function(func_index),
});
let f = f.caller_checked_anyfunc();
let f = unsafe { f.as_ref() };
func_imports.push(VMFunctionImport {
body: f.func_ptr,
vmctx: f.vmctx,
@@ -69,7 +69,7 @@ pub fn create_global(store: &Store, gt: &GlobalType, val: Val) -> Result<StoreIn
module
.exports
.insert(String::new(), wasm::EntityIndex::Global(global_id));
let handle = create_handle(
let id = create_handle(
module,
store,
PrimaryMap::new(),
@@ -79,7 +79,8 @@ pub fn create_global(store: &Store, gt: &GlobalType, val: Val) -> Result<StoreIn
)?;
if let Some(x) = externref_init {
match handle.lookup_by_declaration(&wasm::EntityIndex::Global(global_id)) {
let instance = store.instance(id);
match instance.lookup_by_declaration(&wasm::EntityIndex::Global(global_id)) {
wasmtime_runtime::Export::Global(g) => unsafe {
*(*g.definition).as_externref_mut() = Some(x.inner);
},
@@ -87,5 +88,5 @@ pub fn create_global(store: &Store, gt: &GlobalType, val: Val) -> Result<StoreIn
}
}
Ok(handle)
Ok(id)
}

View File

@@ -1,6 +1,6 @@
use crate::memory::{LinearMemory, MemoryCreator};
use crate::trampoline::{create_handle, StoreInstanceHandle};
use crate::Store;
use crate::store::{InstanceId, StoreOpaque};
use crate::trampoline::create_handle;
use crate::{Limits, MemoryType};
use anyhow::{anyhow, Result};
use wasmtime_environ::entity::PrimaryMap;
@@ -9,7 +9,7 @@ use wasmtime_runtime::{RuntimeLinearMemory, RuntimeMemoryCreator, VMMemoryDefini
use std::sync::Arc;
pub fn create_memory(store: &Store, memory: &MemoryType) -> Result<StoreInstanceHandle> {
pub fn create_memory(store: &mut StoreOpaque<'_>, memory: &MemoryType) -> Result<InstanceId> {
let mut module = Module::new();
let memory = wasm::Memory {
@@ -41,7 +41,7 @@ impl RuntimeLinearMemory for LinearMemoryProxy {
self.mem.maximum()
}
fn grow(&self, delta: u32) -> Option<u32> {
fn grow(&mut self, delta: u32) -> Option<u32> {
self.mem.grow(delta)
}

View File

@@ -1,11 +1,11 @@
use crate::trampoline::{create_handle, StoreInstanceHandle};
use crate::Store;
use crate::store::{InstanceId, StoreOpaque};
use crate::trampoline::create_handle;
use crate::{TableType, ValType};
use anyhow::{bail, Result};
use wasmtime_environ::entity::PrimaryMap;
use wasmtime_environ::{wasm, Module};
pub fn create_table(store: &Store, table: &TableType) -> Result<StoreInstanceHandle> {
pub fn create_table(store: &mut StoreOpaque<'_>, table: &TableType) -> Result<InstanceId> {
let mut module = Module::new();
let table = wasm::Table {

View File

@@ -1,4 +1,5 @@
use crate::{FrameInfo, Store};
use crate::module::GlobalModuleRegistry;
use crate::FrameInfo;
use backtrace::Backtrace;
use std::fmt;
use std::sync::Arc;
@@ -136,23 +137,25 @@ impl Trap {
/// let trap = wasmtime::Trap::new("unexpected error");
/// assert!(trap.to_string().contains("unexpected error"));
/// ```
#[cold] // traps are exceptional, this helps move handling off the main path
pub fn new<I: Into<String>>(message: I) -> Self {
let reason = TrapReason::Message(message.into());
Trap::new_with_trace(None, None, reason, Backtrace::new_unresolved())
Trap::new_with_trace(None, reason, Backtrace::new_unresolved())
}
/// Creates a new `Trap` representing an explicit program exit with a classic `i32`
/// exit status value.
#[cold] // see Trap::new
pub fn i32_exit(status: i32) -> Self {
Trap::new_with_trace(
None,
None,
TrapReason::I32Exit(status),
Backtrace::new_unresolved(),
)
}
pub(crate) fn from_runtime(store: &Store, runtime_trap: wasmtime_runtime::Trap) -> Self {
#[cold] // see Trap::new
pub(crate) fn from_runtime(runtime_trap: wasmtime_runtime::Trap) -> Self {
match runtime_trap {
wasmtime_runtime::Trap::User(error) => Trap::from(error),
wasmtime_runtime::Trap::Jit {
@@ -160,44 +163,40 @@ impl Trap {
backtrace,
maybe_interrupted,
} => {
let mut code = store
.modules()
.borrow()
.lookup_trap_info(pc)
.map(|info| info.trap_code)
.unwrap_or(ir::TrapCode::StackOverflow);
let mut code = GlobalModuleRegistry::with(|modules| {
modules
.lookup_trap_info(pc)
.map(|info| info.trap_code)
.unwrap_or(ir::TrapCode::StackOverflow)
});
if maybe_interrupted && code == ir::TrapCode::StackOverflow {
code = ir::TrapCode::Interrupt;
}
Trap::new_wasm(Some(store), Some(pc), code, backtrace)
Trap::new_wasm(Some(pc), code, backtrace)
}
wasmtime_runtime::Trap::Wasm {
trap_code,
backtrace,
} => Trap::new_wasm(Some(store), None, trap_code, backtrace),
} => Trap::new_wasm(None, trap_code, backtrace),
wasmtime_runtime::Trap::OOM { backtrace } => {
let reason = TrapReason::Message("out of memory".to_string());
Trap::new_with_trace(Some(store), None, reason, backtrace)
Trap::new_with_trace(None, reason, backtrace)
}
}
}
#[cold] // see Trap::new
pub(crate) fn new_wasm(
store: Option<&Store>,
trap_pc: Option<usize>,
code: ir::TrapCode,
backtrace: Backtrace,
) -> Self {
let code = TrapCode::from_non_user(code);
Trap::new_with_trace(store, trap_pc, TrapReason::InstructionTrap(code), backtrace)
Trap::new_with_trace(trap_pc, TrapReason::InstructionTrap(code), backtrace)
}
/// Creates a new `Trap`.
///
/// * `store` - this is optionally provided, if available. If `None` we'll
/// look up the last store, if available, used to call wasm code on the
/// stack.
///
/// * `trap_pc` - this is the precise program counter, if available, that
/// wasm trapped at. This is used when learning about the wasm stack trace
/// to ensure we assign the correct source to every frame.
@@ -208,52 +207,40 @@ impl Trap {
/// * `native_trace` - this is a captured backtrace from when the trap
/// occurred, and this will iterate over the frames to find frames that
/// lie in wasm jit code.
fn new_with_trace(
store: Option<&Store>,
trap_pc: Option<usize>,
reason: TrapReason,
native_trace: Backtrace,
) -> Self {
fn new_with_trace(trap_pc: Option<usize>, reason: TrapReason, native_trace: Backtrace) -> Self {
let mut wasm_trace = Vec::new();
let mut hint_wasm_backtrace_details_env = false;
wasmtime_runtime::with_last_info(|last| {
// If the `store` passed in is `None` then we look at the `last`
// store configured to call wasm, and if that's a `Store` we use
// that. If that all fails then we just don't generate any
// `wasm_trace` information.
if let Some(store) = store.or_else(|| last?.downcast_ref()) {
for frame in native_trace.frames() {
let pc = frame.ip() as usize;
if pc == 0 {
continue;
}
// Note that we need to be careful about the pc we pass in
// here to lookup frame information. This program counter is
// used to translate back to an original source location in
// the origin wasm module. If this pc is the exact pc that
// the trap happened at, then we look up that pc precisely.
// Otherwise backtrace information typically points at the
// pc *after* the call instruction (because otherwise it's
// likely a call instruction on the stack). In that case we
// want to lookup information for the previous instruction
// (the call instruction) so we subtract one as the lookup.
let pc_to_lookup = if Some(pc) == trap_pc { pc } else { pc - 1 };
if let Some((info, has_unparsed_debuginfo)) =
store.modules().borrow().lookup_frame_info(pc_to_lookup)
{
wasm_trace.push(info);
// If this frame has unparsed debug information and the
// store's configuration indicates that we were
// respecting the environment variable of whether to
// do this then we will print out a helpful note in
// `Display` to indicate that more detailed information
// in a trap may be available.
if has_unparsed_debuginfo
&& store.engine().config().wasm_backtrace_details_env_used
{
hint_wasm_backtrace_details_env = true;
}
GlobalModuleRegistry::with(|registry| {
for frame in native_trace.frames() {
let pc = frame.ip() as usize;
if pc == 0 {
continue;
}
// Note that we need to be careful about the pc we pass in
// here to lookup frame information. This program counter is
// used to translate back to an original source location in
// the origin wasm module. If this pc is the exact pc that
// the trap happened at, then we look up that pc precisely.
// Otherwise backtrace information typically points at the
// pc *after* the call instruction (because otherwise it's
// likely a call instruction on the stack). In that case we
// want to lookup information for the previous instruction
// (the call instruction) so we subtract one as the lookup.
let pc_to_lookup = if Some(pc) == trap_pc { pc } else { pc - 1 };
if let Some((info, has_unparsed_debuginfo, wasm_backtrace_details_env_used)) =
registry.lookup_frame_info(pc_to_lookup)
{
wasm_trace.push(info);
// If this frame has unparsed debug information and the
// store's configuration indicates that we were
// respecting the environment variable of whether to
// do this then we will print out a helpful note in
// `Display` to indicate that more detailed information
// in a trap may be available.
if has_unparsed_debuginfo && wasm_backtrace_details_env_used {
hint_wasm_backtrace_details_env = true;
}
}
}
@@ -388,7 +375,7 @@ impl From<Box<dyn std::error::Error + Send + Sync>> for Trap {
trap.clone()
} else {
let reason = TrapReason::Error(e.into());
Trap::new_with_trace(None, None, reason, Backtrace::new_unresolved())
Trap::new_with_trace(None, reason, Backtrace::new_unresolved())
}
}
}

View File

@@ -655,17 +655,6 @@ impl<'module> ExportType<'module> {
EntityOrExtern::Extern(e) => (*e).clone(),
}
}
pub(crate) fn as_import<'a>(&self, module: &'a str) -> ImportType<'a>
where
'module: 'a,
{
ImportType {
module,
name: Some(self.name),
ty: self.ty.clone(),
}
}
}
impl<'module> fmt::Debug for ExportType<'module> {

View File

@@ -1,4 +1,5 @@
use crate::{signatures::SignatureCollection, Extern, Store};
use crate::store::StoreData;
use crate::{signatures::SignatureCollection, Engine, Extern};
use anyhow::{bail, Context, Result};
use wasmtime_environ::wasm::{
EntityType, Global, InstanceTypeIndex, Memory, ModuleTypeIndex, SignatureIndex, Table,
@@ -8,12 +9,13 @@ use wasmtime_jit::TypeTables;
pub struct MatchCx<'a> {
pub signatures: &'a SignatureCollection,
pub types: &'a TypeTables,
pub store: &'a Store,
pub store_data: &'a StoreData,
pub engine: &'a Engine,
}
impl MatchCx<'_> {
pub fn global(&self, expected: &Global, actual: &crate::Global) -> Result<()> {
self.global_ty(expected, actual.wasmtime_ty())
self.global_ty(expected, actual.wasmtime_ty(self.store_data))
}
fn global_ty(&self, expected: &Global, actual: &Global) -> Result<()> {
@@ -28,7 +30,7 @@ impl MatchCx<'_> {
}
pub fn table(&self, expected: &Table, actual: &crate::Table) -> Result<()> {
self.table_ty(expected, actual.wasmtime_ty())
self.table_ty(expected, actual.wasmtime_ty(self.store_data))
}
fn table_ty(&self, expected: &Table, actual: &Table) -> Result<()> {
@@ -50,7 +52,7 @@ impl MatchCx<'_> {
}
pub fn memory(&self, expected: &Memory, actual: &crate::Memory) -> Result<()> {
self.memory_ty(expected, actual.wasmtime_ty())
self.memory_ty(expected, actual.wasmtime_ty(self.store_data))
}
fn memory_ty(&self, expected: &Memory, actual: &Memory) -> Result<()> {
@@ -72,7 +74,7 @@ impl MatchCx<'_> {
pub fn func(&self, expected: SignatureIndex, actual: &crate::Func) -> Result<()> {
let matches = match self.signatures.shared_signature(expected) {
Some(idx) => actual.sig_index() == idx,
Some(idx) => actual.sig_index(self.store_data) == idx,
// If our expected signature isn't registered, then there's no way
// that `actual` can match it.
None => false,
@@ -85,11 +87,11 @@ impl MatchCx<'_> {
}
pub fn instance(&self, expected: InstanceTypeIndex, actual: &crate::Instance) -> Result<()> {
let items = actual.items(self.store_data);
for (name, expected) in self.types.instance_signatures[expected].exports.iter() {
match actual.items.get(name) {
match items.get(name) {
Some(item) => {
let item = unsafe { Extern::from_wasmtime_export(item, self.store) };
self.extern_(expected, &item)
self.extern_(expected, item)
.with_context(|| format!("instance export {:?} incompatible", name))?;
}
None => bail!("instance type missing export {:?}", name),
@@ -104,7 +106,7 @@ impl MatchCx<'_> {
// This should only ever be invoked with module linking, and this is an
// early check that our `field` assertion below should always work as
// well.
assert!(self.store.engine().config().features.module_linking);
assert!(self.engine.config().features.module_linking);
let expected_sig = &self.types.module_signatures[expected];
let module = actual.compiled_module().module();
@@ -149,7 +151,8 @@ impl MatchCx<'_> {
MatchCx {
signatures: actual_signatures,
types: actual_types,
store: self.store,
store_data: self.store_data,
engine: self.engine,
}
.extern_ty_matches(&actual_ty, expected_ty, self.signatures, self.types)
.with_context(|| format!("module import {:?} incompatible", name))?;

View File

@@ -9,23 +9,31 @@
//! throughout the `wasmtime` crate with extra functionality that's only
//! available on Unix.
use crate::Store;
use crate::{AsContextMut, Store};
/// Extensions for the [`Store`] type only available on Unix.
pub trait StoreExt {
// TODO: needs more docs?
/// The signal handler must be
/// [async-signal-safe](http://man7.org/linux/man-pages/man7/signal-safety.7.html).
unsafe fn set_signal_handler<H>(&self, handler: H)
unsafe fn set_signal_handler<H>(&mut self, handler: H)
where
H: 'static + Fn(libc::c_int, *const libc::siginfo_t, *const libc::c_void) -> bool;
H: 'static
+ Fn(libc::c_int, *const libc::siginfo_t, *const libc::c_void) -> bool
+ Send
+ Sync;
}
impl StoreExt for Store {
unsafe fn set_signal_handler<H>(&self, handler: H)
impl<T> StoreExt for Store<T> {
unsafe fn set_signal_handler<H>(&mut self, handler: H)
where
H: 'static + Fn(libc::c_int, *const libc::siginfo_t, *const libc::c_void) -> bool,
H: 'static
+ Fn(libc::c_int, *const libc::siginfo_t, *const libc::c_void) -> bool
+ Send
+ Sync,
{
self.set_signal_handler(Some(Box::new(handler)));
self.as_context_mut()
.opaque()
.set_signal_handler(Some(Box::new(handler)));
}
}

View File

@@ -1,5 +1,6 @@
use crate::r#ref::ExternRef;
use crate::{Func, Store, ValType};
use crate::store::StoreOpaque;
use crate::{Func, ValType};
use anyhow::{bail, Result};
use std::ptr;
use wasmtime_runtime::{self as runtime, VMExternRef};
@@ -86,7 +87,7 @@ impl Val {
}
}
pub(crate) unsafe fn write_value_to(self, store: &Store, p: *mut u128) {
pub(crate) unsafe fn write_value_to(self, store: &mut StoreOpaque, p: *mut u128) {
match self {
Val::I32(i) => ptr::write(p as *mut i32, i),
Val::I64(i) => ptr::write(p as *mut i64, i),
@@ -96,15 +97,13 @@ impl Val {
Val::ExternRef(None) => ptr::write(p, 0),
Val::ExternRef(Some(x)) => {
let externref_ptr = x.inner.as_raw();
store
.externref_activations_table()
.insert_with_gc(x.inner, store.module_info_lookup());
store.insert_vmexternref(x.inner);
ptr::write(p as *mut *mut u8, externref_ptr)
}
Val::FuncRef(f) => ptr::write(
p as *mut *mut runtime::VMCallerCheckedAnyfunc,
if let Some(f) = f {
f.caller_checked_anyfunc().as_ptr()
f.caller_checked_anyfunc(store).as_ptr()
} else {
ptr::null_mut()
},
@@ -112,7 +111,11 @@ impl Val {
}
}
pub(crate) unsafe fn read_value_from(store: &Store, p: *const u128, ty: ValType) -> Val {
pub(crate) unsafe fn read_value_from(
store: &mut StoreOpaque,
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)),
@@ -174,21 +177,36 @@ impl Val {
self.externref().expect("expected externref")
}
pub(crate) fn into_table_element(self) -> Result<runtime::TableElement> {
match self {
Val::FuncRef(Some(f)) => Ok(runtime::TableElement::FuncRef(
f.caller_checked_anyfunc().as_ptr(),
)),
Val::FuncRef(None) => Ok(runtime::TableElement::FuncRef(ptr::null_mut())),
Val::ExternRef(Some(x)) => Ok(runtime::TableElement::ExternRef(Some(x.inner))),
Val::ExternRef(None) => Ok(runtime::TableElement::ExternRef(None)),
pub(crate) fn into_table_element(
self,
store: &mut StoreOpaque<'_>,
ty: ValType,
) -> Result<runtime::TableElement> {
match (self, ty) {
(Val::FuncRef(Some(f)), ValType::FuncRef) => {
if !f.comes_from_same_store(store) {
bail!("cross-`Store` values are not supported in tables");
}
Ok(runtime::TableElement::FuncRef(
f.caller_checked_anyfunc(store).as_ptr(),
))
}
(Val::FuncRef(None), ValType::FuncRef) => {
Ok(runtime::TableElement::FuncRef(ptr::null_mut()))
}
(Val::ExternRef(Some(x)), ValType::ExternRef) => {
Ok(runtime::TableElement::ExternRef(Some(x.inner)))
}
(Val::ExternRef(None), ValType::ExternRef) => {
Ok(runtime::TableElement::ExternRef(None))
}
_ => bail!("value does not match table element type"),
}
}
pub(crate) fn comes_from_same_store(&self, store: &Store) -> bool {
pub(crate) fn comes_from_same_store(&self, store: &StoreOpaque<'_>) -> bool {
match self {
Val::FuncRef(Some(f)) => Store::same(store, f.store()),
Val::FuncRef(Some(f)) => f.comes_from_same_store(store),
Val::FuncRef(None) => true,
// Integers, floats, vectors, and `externref`s have no association
@@ -254,21 +272,21 @@ impl From<Func> for Val {
pub(crate) fn into_checked_anyfunc(
val: Val,
store: &Store,
store: &mut StoreOpaque,
) -> Result<*mut wasmtime_runtime::VMCallerCheckedAnyfunc> {
if !val.comes_from_same_store(store) {
bail!("cross-`Store` values are not supported");
}
Ok(match val {
Val::FuncRef(None) => ptr::null_mut(),
Val::FuncRef(Some(f)) => f.caller_checked_anyfunc().as_ptr(),
Val::FuncRef(Some(f)) => f.caller_checked_anyfunc(store).as_ptr(),
_ => bail!("val is not funcref"),
})
}
pub(crate) unsafe fn from_checked_anyfunc(
anyfunc: *mut wasmtime_runtime::VMCallerCheckedAnyfunc,
store: &Store,
store: &mut StoreOpaque,
) -> Val {
Val::FuncRef(Func::from_caller_checked_anyfunc(store, anyfunc))
}

View File

@@ -9,23 +9,25 @@
//! throughout the `wasmtime` crate with extra functionality that's only
//! available on Windows.
use crate::Store;
use crate::{AsContextMut, Store};
/// Extensions for the [`Store`] type only available on Windows.
pub trait StoreExt {
/// Configures a custom signal handler to execute.
///
/// TODO: needs more documentation.
unsafe fn set_signal_handler<H>(&self, handler: H)
unsafe fn set_signal_handler<H>(&mut self, handler: H)
where
H: 'static + Fn(winapi::um::winnt::PEXCEPTION_POINTERS) -> bool;
H: 'static + Fn(winapi::um::winnt::PEXCEPTION_POINTERS) -> bool + Send + Sync;
}
impl StoreExt for Store {
unsafe fn set_signal_handler<H>(&self, handler: H)
impl<T> StoreExt for Store<T> {
unsafe fn set_signal_handler<H>(&mut self, handler: H)
where
H: 'static + Fn(winapi::um::winnt::PEXCEPTION_POINTERS) -> bool,
H: 'static + Fn(winapi::um::winnt::PEXCEPTION_POINTERS) -> bool + Send + Sync,
{
self.set_signal_handler(Some(Box::new(handler)));
self.as_context_mut()
.opaque()
.set_signal_handler(Some(Box::new(handler)));
}
}