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:
@@ -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();
|
||||
|
||||
@@ -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(®istry);
|
||||
|
||||
// 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:
|
||||
|
||||
@@ -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
@@ -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),)*)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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, &[]))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
@@ -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),
|
||||
}
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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
275
crates/wasmtime/src/store/context.rs
Normal file
275
crates/wasmtime/src/store/context.rs
Normal 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
|
||||
}
|
||||
}
|
||||
195
crates/wasmtime/src/store/data.rs
Normal file
195
crates/wasmtime/src/store/data.rs
Normal 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())
|
||||
}
|
||||
}
|
||||
@@ -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!(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
})?,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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> {
|
||||
|
||||
@@ -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))?;
|
||||
|
||||
@@ -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)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
|
||||
@@ -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)));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user