Browse Source

Remove the need to have a `Store` for an `InstancePre` (#5683)

* Remove the need to have a `Store` for an `InstancePre`

This commit relaxes a requirement of the `InstancePre` API, notably its
construction via `Linker::instantiate_pre`. Previously this function
required a `Store<T>` to be present to be able to perform type-checking
on the contents of the linker, and now this requirement has been
removed.

Items stored within a linker are either a `HostFunc`, which has type
information inside of it, or an `Extern`, which doesn't have type
information inside of it. Due to the usage of `Extern` this is why a
`Store` was required during the `InstancePre` construction process, it's
used to extract the type of an `Extern`. This commit implements a
solution where the type information of an `Extern` is stored alongside
the `Extern` itself, meaning that the `InstancePre` construction process
no longer requires a `Store<T>`.

One caveat of this implementation is that some items, such as tables and
memories, technically have a "dynamic type" where during type checking
their current size is consulted to match against the minimum size
required of an import. This no longer works when using
`Linker::instantiate_pre` as the current size used is the one when it
was inserted into the linker rather than the one available at
instantiation time. It's hoped, however, that this is a relatively
esoteric use case that doesn't impact many real-world users.

Additionally note that this is an API-breaking change. Not only is the
`Store` argument removed from `Linker::instantiate_pre`, but some other
methods such as `Linker::define` grew a `Store` argument as the type
needs to be extracted when an item is inserted into a linker.

Closes #5675

* Fix the C API

* Fix benchmark compilation

* Add C API docs

* Update crates/wasmtime/src/linker.rs

Co-authored-by: Andrew Brown <andrew.brown@intel.com>

---------

Co-authored-by: Andrew Brown <andrew.brown@intel.com>
pull/5694/head
Alex Crichton 2 years ago
committed by GitHub
parent
commit
63d80fc509
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 4
      benches/instantiation.rs
  2. 2
      crates/c-api/include/wasmtime/linker.h
  3. 5
      crates/c-api/src/linker.rs
  4. 127
      crates/fuzzing/src/oracles.rs
  5. 7
      crates/fuzzing/src/oracles/dummy.rs
  6. 5
      crates/wasmtime/src/component/instance.rs
  7. 14
      crates/wasmtime/src/externals.rs
  8. 26
      crates/wasmtime/src/instance.rs
  9. 154
      crates/wasmtime/src/linker.rs
  10. 68
      crates/wasmtime/src/types/matching.rs
  11. 14
      crates/wast/src/spectest.rs
  12. 2
      tests/all/call_hook.rs
  13. 44
      tests/all/linker.rs

4
benches/instantiation.rs

@ -46,7 +46,7 @@ fn bench_sequential(c: &mut Criterion, path: &Path) {
let mut linker = Linker::new(&engine);
wasmtime_wasi::add_to_linker(&mut linker, |cx| cx).unwrap();
let pre = linker
.instantiate_pre(&mut store(&engine), &module)
.instantiate_pre(&module)
.expect("failed to pre-instantiate");
(engine, pre)
});
@ -77,7 +77,7 @@ fn bench_parallel(c: &mut Criterion, path: &Path) {
wasmtime_wasi::add_to_linker(&mut linker, |cx| cx).unwrap();
let pre = Arc::new(
linker
.instantiate_pre(&mut store(&engine), &module)
.instantiate_pre(&module)
.expect("failed to pre-instantiate"),
);
(engine, pre)

2
crates/c-api/include/wasmtime/linker.h

@ -59,6 +59,7 @@ WASM_API_EXTERN void wasmtime_linker_allow_shadowing(wasmtime_linker_t* linker,
* \brief Defines a new item in this linker.
*
* \param linker the linker the name is being defined in.
* \param store the store that the `item` is owned by.
* \param module the module name the item is defined under.
* \param module_len the byte length of `module`
* \param name the field name the item is defined under
@ -73,6 +74,7 @@ WASM_API_EXTERN void wasmtime_linker_allow_shadowing(wasmtime_linker_t* linker,
*/
WASM_API_EXTERN wasmtime_error_t* wasmtime_linker_define(
wasmtime_linker_t *linker,
wasmtime_context_t *store,
const char *module,
size_t module_len,
const char *name,

5
crates/c-api/src/linker.rs

@ -1,6 +1,6 @@
use crate::{
bad_utf8, handle_result, wasm_engine_t, wasm_functype_t, wasm_trap_t, wasmtime_error_t,
wasmtime_extern_t, wasmtime_module_t, CStoreContextMut,
wasmtime_extern_t, wasmtime_module_t, CStoreContext, CStoreContextMut,
};
use std::ffi::c_void;
use std::mem::MaybeUninit;
@ -41,6 +41,7 @@ macro_rules! to_str {
#[no_mangle]
pub unsafe extern "C" fn wasmtime_linker_define(
linker: &mut wasmtime_linker_t,
store: CStoreContext<'_>,
module: *const u8,
module_len: usize,
name: *const u8,
@ -51,7 +52,7 @@ pub unsafe extern "C" fn wasmtime_linker_define(
let module = to_str!(module, module_len);
let name = to_str!(name, name_len);
let item = item.to_extern();
handle_result(linker.define(module, name, item), |_linker| ())
handle_result(linker.define(&store, module, name, item), |_linker| ())
}
#[no_mangle]

127
crates/fuzzing/src/oracles.rs

@ -544,45 +544,40 @@ pub fn table_ops(
// test case.
const MAX_GCS: usize = 5;
linker
.define(
"",
"gc",
// NB: use `Func::new` so that this can still compile on the old x86
// backend, where `IntoFunc` isn't implemented for multi-value
// returns.
Func::new(
&mut store,
FuncType::new(
vec![],
vec![ValType::ExternRef, ValType::ExternRef, ValType::ExternRef],
),
{
let num_dropped = num_dropped.clone();
let expected_drops = expected_drops.clone();
let num_gcs = num_gcs.clone();
move |mut caller: Caller<'_, StoreLimits>, _params, results| {
log::info!("table_ops: GC");
if num_gcs.fetch_add(1, SeqCst) < MAX_GCS {
caller.gc();
}
let a = ExternRef::new(CountDrops(num_dropped.clone()));
let b = ExternRef::new(CountDrops(num_dropped.clone()));
let c = ExternRef::new(CountDrops(num_dropped.clone()));
log::info!("table_ops: make_refs() -> ({:p}, {:p}, {:p})", a, b, c);
expected_drops.fetch_add(3, SeqCst);
results[0] = Some(a).into();
results[1] = Some(b).into();
results[2] = Some(c).into();
Ok(())
}
},
),
)
.unwrap();
// NB: use `Func::new` so that this can still compile on the old x86
// backend, where `IntoFunc` isn't implemented for multi-value
// returns.
let func = Func::new(
&mut store,
FuncType::new(
vec![],
vec![ValType::ExternRef, ValType::ExternRef, ValType::ExternRef],
),
{
let num_dropped = num_dropped.clone();
let expected_drops = expected_drops.clone();
let num_gcs = num_gcs.clone();
move |mut caller: Caller<'_, StoreLimits>, _params, results| {
log::info!("table_ops: GC");
if num_gcs.fetch_add(1, SeqCst) < MAX_GCS {
caller.gc();
}
let a = ExternRef::new(CountDrops(num_dropped.clone()));
let b = ExternRef::new(CountDrops(num_dropped.clone()));
let c = ExternRef::new(CountDrops(num_dropped.clone()));
log::info!("table_ops: make_refs() -> ({:p}, {:p}, {:p})", a, b, c);
expected_drops.fetch_add(3, SeqCst);
results[0] = Some(a).into();
results[1] = Some(b).into();
results[2] = Some(c).into();
Ok(())
}
},
);
linker.define(&store, "", "gc", func).unwrap();
linker
.func_wrap("", "take_refs", {
@ -624,37 +619,29 @@ pub fn table_ops(
})
.unwrap();
linker
.define(
"",
"make_refs",
// NB: use `Func::new` so that this can still compile on the old
// x86 backend, where `IntoFunc` isn't implemented for
// multi-value returns.
Func::new(
&mut store,
FuncType::new(
vec![],
vec![ValType::ExternRef, ValType::ExternRef, ValType::ExternRef],
),
{
let num_dropped = num_dropped.clone();
let expected_drops = expected_drops.clone();
move |_caller, _params, results| {
log::info!("table_ops: make_refs");
expected_drops.fetch_add(3, SeqCst);
results[0] =
Some(ExternRef::new(CountDrops(num_dropped.clone()))).into();
results[1] =
Some(ExternRef::new(CountDrops(num_dropped.clone()))).into();
results[2] =
Some(ExternRef::new(CountDrops(num_dropped.clone()))).into();
Ok(())
}
},
),
)
.unwrap();
// NB: use `Func::new` so that this can still compile on the old
// x86 backend, where `IntoFunc` isn't implemented for
// multi-value returns.
let func = Func::new(
&mut store,
FuncType::new(
vec![],
vec![ValType::ExternRef, ValType::ExternRef, ValType::ExternRef],
),
{
let num_dropped = num_dropped.clone();
let expected_drops = expected_drops.clone();
move |_caller, _params, results| {
log::info!("table_ops: make_refs");
expected_drops.fetch_add(3, SeqCst);
results[0] = Some(ExternRef::new(CountDrops(num_dropped.clone()))).into();
results[1] = Some(ExternRef::new(CountDrops(num_dropped.clone()))).into();
results[2] = Some(ExternRef::new(CountDrops(num_dropped.clone()))).into();
Ok(())
}
},
);
linker.define(&store, "", "make_refs", func).unwrap();
let instance = linker.instantiate(&mut store, &module).unwrap();
let run = instance.get_func(&mut store, "run").unwrap();

7
crates/fuzzing/src/oracles/dummy.rs

@ -8,12 +8,9 @@ pub fn dummy_linker<'module, T>(store: &mut Store<T>, module: &Module) -> Result
let mut linker = Linker::new(store.engine());
linker.allow_shadowing(true);
for import in module.imports() {
let extern_ = dummy_extern(store, import.ty())?;
linker
.define(
import.module(),
import.name(),
dummy_extern(store, import.ty())?,
)
.define(&store, import.module(), import.name(), extern_)
.unwrap();
}
Ok(linker)

5
crates/wasmtime/src/component/instance.rs

@ -1,6 +1,7 @@
use crate::component::func::HostFunc;
use crate::component::{Component, ComponentNamedList, Func, Lift, Lower, TypedFunc};
use crate::instance::OwnedImports;
use crate::linker::DefinitionType;
use crate::store::{StoreOpaque, Stored};
use crate::{AsContextMut, Module, StoreContextMut};
use anyhow::{anyhow, Context, Result};
@ -439,13 +440,13 @@ impl<'a> Instantiator<'a> {
}
let val = unsafe { crate::Extern::from_wasmtime_export(export, store) };
let ty = DefinitionType::from(store, &val);
crate::types::matching::MatchCx {
store,
engine: store.engine(),
signatures: module.signatures(),
types: module.types(),
}
.extern_(&expected, &val)
.definition(&expected, &ty)
.expect("unexpected typecheck failure");
}
}

14
crates/wasmtime/src/externals.rs

@ -138,16 +138,6 @@ impl Extern {
Extern::Table(t) => store.store_data().contains(t.0),
}
}
pub(crate) fn desc(&self) -> &'static str {
match self {
Extern::Func(_) => "function",
Extern::Table(_) => "table",
Extern::Memory(_) => "memory",
Extern::SharedMemory(_) => "shared memory",
Extern::Global(_) => "global",
}
}
}
impl From<Func> for Extern {
@ -233,8 +223,8 @@ impl Global {
/// )?;
///
/// let mut linker = Linker::new(&engine);
/// linker.define("", "i32-const", i32_const)?;
/// linker.define("", "f64-mut", f64_mut)?;
/// linker.define(&store, "", "i32-const", i32_const)?;
/// linker.define(&store, "", "f64-mut", f64_mut)?;
///
/// let instance = linker.instantiate(&mut store, &module)?;
/// // ...

26
crates/wasmtime/src/instance.rs

@ -1,4 +1,4 @@
use crate::linker::Definition;
use crate::linker::{Definition, DefinitionType};
use crate::store::{InstanceId, StoreOpaque, Stored};
use crate::types::matching;
use crate::{
@ -157,7 +157,10 @@ impl Instance {
bail!("cross-`Store` instantiation is not currently supported");
}
}
typecheck(store, module, imports, |cx, ty, item| cx.extern_(ty, item))?;
typecheck(module, imports, |cx, ty, item| {
let item = DefinitionType::from(store, item);
cx.definition(ty, &item)
})?;
let mut owned_imports = OwnedImports::new(module);
for import in imports {
owned_imports.push(import, store);
@ -679,19 +682,8 @@ impl<T> InstancePre<T> {
/// This method is unsafe as the `T` of the `InstancePre<T>` is not
/// guaranteed to be the same as the `T` within the `Store`, the caller must
/// verify that.
pub(crate) unsafe fn new(
store: &mut StoreOpaque,
module: &Module,
items: Vec<Definition>,
) -> Result<InstancePre<T>> {
for import in items.iter() {
if !import.comes_from_same_store(store) {
bail!("cross-`Store` instantiation is not currently supported");
}
}
typecheck(store, module, &items, |cx, ty, item| {
cx.definition(ty, item)
})?;
pub(crate) unsafe fn new(module: &Module, items: Vec<Definition>) -> Result<InstancePre<T>> {
typecheck(module, &items, |cx, ty, item| cx.definition(ty, &item.ty()))?;
let host_funcs = items
.iter()
@ -813,7 +805,6 @@ fn pre_instantiate_raw(
}
fn typecheck<I>(
store: &mut StoreOpaque,
module: &Module,
imports: &[I],
check: impl Fn(&matching::MatchCx<'_>, &EntityType, &I) -> Result<()>,
@ -826,8 +817,7 @@ fn typecheck<I>(
let cx = matching::MatchCx {
signatures: module.signatures(),
types: module.types(),
store: store,
engine: store.engine(),
engine: module.engine(),
};
for ((name, field, expected_ty), actual) in env_module.imports().zip(imports) {
check(&cx, &expected_ty, actual)

154
crates/wasmtime/src/linker.rs

@ -2,8 +2,8 @@ use crate::func::HostFunc;
use crate::instance::InstancePre;
use crate::store::StoreOpaque;
use crate::{
AsContextMut, Caller, Engine, Extern, ExternType, Func, FuncType, ImportType, Instance,
IntoFunc, Module, StoreContextMut, Val, ValRaw,
AsContext, AsContextMut, Caller, Engine, Extern, ExternType, Func, FuncType, ImportType,
Instance, IntoFunc, Module, StoreContextMut, Val, ValRaw,
};
use anyhow::{bail, Context, Result};
use log::warn;
@ -113,10 +113,25 @@ struct ImportKey {
#[derive(Clone)]
pub(crate) enum Definition {
Extern(Extern),
Extern(Extern, DefinitionType),
HostFunc(Arc<HostFunc>),
}
/// This is a sort of slimmed down `ExternType` which notably doesn't have a
/// `FuncType`, which is an allocation, and additionally retains the current
/// size of the table/memory.
#[derive(Clone)]
pub(crate) enum DefinitionType {
Func(wasmtime_runtime::VMSharedSignatureIndex),
Global(wasmtime_environ::Global),
// Note that tables and memories store not only the original type
// information but additionally the current size of the table/memory, as
// this is used during linking since the min size specified in the type may
// no longer be the current size of the table/memory.
Table(wasmtime_environ::Table, u32),
Memory(wasmtime_environ::Memory, u64),
}
macro_rules! generate_wrap_async_func {
($num:tt $($args:ident)*) => (paste::paste!{
/// Asynchronous analog of [`Linker::func_wrap`].
@ -296,7 +311,7 @@ impl<T> Linker<T> {
/// let mut linker = Linker::new(&engine);
/// let ty = GlobalType::new(ValType::I32, Mutability::Const);
/// let global = Global::new(&mut store, ty, Val::I32(0x1234))?;
/// linker.define("host", "offset", global)?;
/// linker.define(&store, "host", "offset", global)?;
///
/// let wat = r#"
/// (module
@ -312,12 +327,14 @@ impl<T> Linker<T> {
/// ```
pub fn define(
&mut self,
store: impl AsContext<Data = T>,
module: &str,
name: &str,
item: impl Into<Extern>,
) -> Result<&mut Self> {
let store = store.as_context();
let key = self.import_key(module, Some(name));
self.insert(key, Definition::Extern(item.into()))?;
self.insert(key, Definition::new(store.0, item.into()))?;
Ok(self)
}
@ -327,9 +344,15 @@ impl<T> Linker<T> {
/// This is only relevant when working with the module linking proposal
/// where one-level names are allowed (in addition to two-level names).
/// Otherwise this method need not be used.
pub fn define_name(&mut self, name: &str, item: impl Into<Extern>) -> Result<&mut Self> {
pub fn define_name(
&mut self,
store: impl AsContext<Data = T>,
name: &str,
item: impl Into<Extern>,
) -> Result<&mut Self> {
let store = store.as_context();
let key = self.import_key(name, None);
self.insert(key, Definition::Extern(item.into()))?;
self.insert(key, Definition::new(store.0, item.into()))?;
Ok(self)
}
@ -542,9 +565,18 @@ impl<T> Linker<T> {
module_name: &str,
instance: Instance,
) -> Result<&mut Self> {
for export in instance.exports(store.as_context_mut()) {
let key = self.import_key(module_name, Some(export.name()));
self.insert(key, Definition::Extern(export.into_extern()))?;
let mut store = store.as_context_mut();
let exports = instance
.exports(&mut store)
.map(|e| {
(
self.import_key(module_name, Some(e.name())),
e.into_extern(),
)
})
.collect::<Vec<_>>();
for (key, export) in exports {
self.insert(key, Definition::new(store.0, export))?;
}
Ok(self)
}
@ -814,11 +846,11 @@ impl<T> Linker<T> {
let mut store = store.as_context_mut();
for export in module.exports() {
if let Some(func_ty) = export.ty().func() {
let instance_pre = self.instantiate_pre(&mut store, module)?;
let instance_pre = self.instantiate_pre(module)?;
let export_name = export.name().to_owned();
let func = mk_func(&mut store, func_ty, export_name, instance_pre);
let key = self.import_key(module_name, Some(export.name()));
self.insert(key, Definition::Extern(func.into()))?;
self.insert(key, Definition::new(store.0, func.into()))?;
} else if export.name() == "memory" && export.ty().memory().is_some() {
// Allow an exported "memory" memory for now.
} else if export.name() == "__indirect_function_table" && export.ty().table().is_some()
@ -1003,7 +1035,8 @@ impl<T> Linker<T> {
mut store: impl AsContextMut<Data = T>,
module: &Module,
) -> Result<Instance> {
self.instantiate_pre(&mut store, module)?.instantiate(store)
self._instantiate_pre(module, Some(store.as_context_mut().0))?
.instantiate(store)
}
/// Attempts to instantiate the `module` provided. This is the same as
@ -1018,14 +1051,13 @@ impl<T> Linker<T> {
where
T: Send,
{
self.instantiate_pre(&mut store, module)?
self._instantiate_pre(module, Some(store.as_context_mut().0))?
.instantiate_async(store)
.await
}
/// Performs all checks necessary for instantiating `module` with this
/// linker within `store`, except that instantiation doesn't actually
/// finish.
/// linker, except that instantiation doesn't actually finish.
///
/// This method is used for front-loading type-checking information as well
/// as collecting the imports to use to instantiate a module with. The
@ -1060,7 +1092,7 @@ impl<T> Linker<T> {
/// )
/// "#;
/// let module = Module::new(&engine, wat)?;
/// let instance_pre = linker.instantiate_pre(&mut store, &module)?;
/// let instance_pre = linker.instantiate_pre(&module)?;
///
/// // Finish instantiation after the type-checking has all completed...
/// let instance = instance_pre.instantiate(&mut store)?;
@ -1078,17 +1110,36 @@ impl<T> Linker<T> {
/// # Ok(())
/// # }
/// ```
pub fn instantiate_pre(
pub fn instantiate_pre(&self, module: &Module) -> Result<InstancePre<T>> {
self._instantiate_pre(module, None)
}
/// This is split out to optionally take a `store` so that when the
/// `.instantiate` API is used we can get fresh up-to-date type information
/// for memories and their current size, if necessary.
///
/// Note that providing a `store` here is not required for correctness
/// per-se. If one is not provided, such as the with the `instantiate_pre`
/// API, then the type information used for memories and tables will reflect
/// their size when inserted into the linker rather than their current size.
/// This isn't expected to be much of a problem though since
/// per-store-`Linker` types are likely using `.instantiate(..)` and
/// per-`Engine` linkers don't have memories/tables in them.
fn _instantiate_pre(
&self,
mut store: impl AsContextMut<Data = T>,
module: &Module,
store: Option<&StoreOpaque>,
) -> Result<InstancePre<T>> {
let store = store.as_context_mut().0;
let imports = module
let mut imports = module
.imports()
.map(|import| self._get_by_import(&import))
.collect::<Result<_, _>>()?;
unsafe { InstancePre::new(store, module, imports) }
.collect::<Result<Vec<_>, _>>()?;
if let Some(store) = store {
for import in imports.iter_mut() {
import.update_size(store);
}
}
unsafe { InstancePre::new(module, imports) }
}
/// Returns an iterator over all items defined in this `Linker`, in
@ -1217,12 +1268,24 @@ impl<T> Default for Linker<T> {
}
impl Definition {
fn new(store: &StoreOpaque, item: Extern) -> Definition {
let ty = DefinitionType::from(store, &item);
Definition::Extern(item, ty)
}
pub(crate) fn ty(&self) -> DefinitionType {
match self {
Definition::Extern(_, ty) => ty.clone(),
Definition::HostFunc(func) => DefinitionType::Func(func.sig_index()),
}
}
/// Note the unsafety here is due to calling `HostFunc::to_func`. The
/// requirement here is that the `T` that was originally used to create the
/// `HostFunc` matches the `T` on the store.
pub(crate) unsafe fn to_extern(&self, store: &mut StoreOpaque) -> Extern {
match self {
Definition::Extern(e) => e.clone(),
Definition::Extern(e, _) => e.clone(),
Definition::HostFunc(func) => func.to_func(store).into(),
}
}
@ -1231,17 +1294,56 @@ impl Definition {
/// `HostFunc::to_func_store_rooted`.
pub(crate) unsafe fn to_extern_store_rooted(&self, store: &mut StoreOpaque) -> Extern {
match self {
Definition::Extern(e) => e.clone(),
Definition::Extern(e, _) => e.clone(),
Definition::HostFunc(func) => func.to_func_store_rooted(store).into(),
}
}
pub(crate) fn comes_from_same_store(&self, store: &StoreOpaque) -> bool {
match self {
Definition::Extern(e) => e.comes_from_same_store(store),
Definition::Extern(e, _) => e.comes_from_same_store(store),
Definition::HostFunc(_func) => true,
}
}
fn update_size(&mut self, store: &StoreOpaque) {
match self {
Definition::Extern(Extern::Memory(m), DefinitionType::Memory(_, size)) => {
*size = m.internal_size(store);
}
Definition::Extern(Extern::SharedMemory(m), DefinitionType::Memory(_, size)) => {
*size = m.size();
}
Definition::Extern(Extern::Table(m), DefinitionType::Table(_, size)) => {
*size = m.internal_size(store);
}
_ => {}
}
}
}
impl DefinitionType {
pub(crate) fn from(store: &StoreOpaque, item: &Extern) -> DefinitionType {
let data = store.store_data();
match item {
Extern::Func(f) => DefinitionType::Func(f.sig_index(data)),
Extern::Table(t) => DefinitionType::Table(*t.wasmtime_ty(data), t.internal_size(store)),
Extern::Global(t) => DefinitionType::Global(*t.wasmtime_ty(data)),
Extern::Memory(t) => {
DefinitionType::Memory(*t.wasmtime_ty(data), t.internal_size(store))
}
Extern::SharedMemory(t) => DefinitionType::Memory(*t.ty().wasmtime_memory(), t.size()),
}
}
pub(crate) fn desc(&self) -> &'static str {
match self {
DefinitionType::Func(_) => "function",
DefinitionType::Table(..) => "table",
DefinitionType::Memory(..) => "memory",
DefinitionType::Global(_) => "global",
}
}
}
/// Modules can be interpreted either as Commands or Reactors.

68
crates/wasmtime/src/types/matching.rs

@ -1,6 +1,5 @@
use crate::linker::Definition;
use crate::store::StoreOpaque;
use crate::{signatures::SignatureCollection, Engine, Extern};
use crate::linker::DefinitionType;
use crate::{signatures::SignatureCollection, Engine};
use anyhow::{anyhow, bail, Result};
use wasmtime_environ::{
EntityType, Global, Memory, ModuleTypes, SignatureIndex, Table, WasmFuncType, WasmType,
@ -10,47 +9,10 @@ use wasmtime_runtime::VMSharedSignatureIndex;
pub struct MatchCx<'a> {
pub signatures: &'a SignatureCollection,
pub types: &'a ModuleTypes,
pub store: &'a StoreOpaque,
pub engine: &'a Engine,
}
impl MatchCx<'_> {
pub fn global(&self, expected: &Global, actual: &crate::Global) -> Result<()> {
global_ty(expected, actual.wasmtime_ty(self.store.store_data()))
}
pub fn table(&self, expected: &Table, actual: &crate::Table) -> Result<()> {
table_ty(
expected,
actual.wasmtime_ty(self.store.store_data()),
Some(actual.internal_size(self.store)),
)
}
pub fn memory(&self, expected: &Memory, actual: &crate::Memory) -> Result<()> {
memory_ty(
expected,
actual.wasmtime_ty(self.store.store_data()),
Some(actual.internal_size(self.store)),
)
}
pub fn shared_memory(&self, expected: &Memory, actual: &crate::SharedMemory) -> Result<()> {
memory_ty(expected, actual.ty().wasmtime_memory(), Some(actual.size()))
}
pub fn func(&self, expected: SignatureIndex, actual: &crate::Func) -> Result<()> {
self.vmshared_signature_index(expected, actual.sig_index(self.store.store_data()))
}
pub(crate) fn host_func(
&self,
expected: SignatureIndex,
actual: &crate::func::HostFunc,
) -> Result<()> {
self.vmshared_signature_index(expected, actual.sig_index())
}
pub fn vmshared_signature_index(
&self,
expected: SignatureIndex,
@ -79,39 +41,31 @@ impl MatchCx<'_> {
}
/// Validates that the `expected` type matches the type of `actual`
pub fn extern_(&self, expected: &EntityType, actual: &Extern) -> Result<()> {
pub(crate) fn definition(&self, expected: &EntityType, actual: &DefinitionType) -> Result<()> {
match expected {
EntityType::Global(expected) => match actual {
Extern::Global(actual) => self.global(expected, actual),
DefinitionType::Global(actual) => global_ty(expected, actual),
_ => bail!("expected global, but found {}", actual.desc()),
},
EntityType::Table(expected) => match actual {
Extern::Table(actual) => self.table(expected, actual),
DefinitionType::Table(actual, cur_size) => {
table_ty(expected, actual, Some(*cur_size))
}
_ => bail!("expected table, but found {}", actual.desc()),
},
EntityType::Memory(expected) => match actual {
Extern::Memory(actual) => self.memory(expected, actual),
Extern::SharedMemory(actual) => self.shared_memory(expected, actual),
DefinitionType::Memory(actual, cur_size) => {
memory_ty(expected, actual, Some(*cur_size))
}
_ => bail!("expected memory, but found {}", actual.desc()),
},
EntityType::Function(expected) => match actual {
Extern::Func(actual) => self.func(*expected, actual),
DefinitionType::Func(actual) => self.vmshared_signature_index(*expected, *actual),
_ => bail!("expected func, but found {}", actual.desc()),
},
EntityType::Tag(_) => unimplemented!(),
}
}
/// Validates that the `expected` type matches the type of `actual`
pub(crate) fn definition(&self, expected: &EntityType, actual: &Definition) -> Result<()> {
match actual {
Definition::Extern(e) => self.extern_(expected, e),
Definition::HostFunc(f) => match expected {
EntityType::Function(expected) => self.host_func(*expected, f),
_ => bail!("expected {}, but found func", entity_desc(expected)),
},
}
}
}
#[cfg_attr(not(feature = "component-model"), allow(dead_code))]

14
crates/wast/src/spectest.rs

@ -24,32 +24,32 @@ pub fn link_spectest<T>(
let ty = GlobalType::new(ValType::I32, Mutability::Const);
let g = Global::new(&mut *store, ty, Val::I32(666))?;
linker.define("spectest", "global_i32", g)?;
linker.define(&mut *store, "spectest", "global_i32", g)?;
let ty = GlobalType::new(ValType::I64, Mutability::Const);
let g = Global::new(&mut *store, ty, Val::I64(666))?;
linker.define("spectest", "global_i64", g)?;
linker.define(&mut *store, "spectest", "global_i64", g)?;
let ty = GlobalType::new(ValType::F32, Mutability::Const);
let g = Global::new(&mut *store, ty, Val::F32(0x4426_8000))?;
linker.define("spectest", "global_f32", g)?;
linker.define(&mut *store, "spectest", "global_f32", g)?;
let ty = GlobalType::new(ValType::F64, Mutability::Const);
let g = Global::new(&mut *store, ty, Val::F64(0x4084_d000_0000_0000))?;
linker.define("spectest", "global_f64", g)?;
linker.define(&mut *store, "spectest", "global_f64", g)?;
let ty = TableType::new(ValType::FuncRef, 10, Some(20));
let table = Table::new(&mut *store, ty, Val::FuncRef(None))?;
linker.define("spectest", "table", table)?;
linker.define(&mut *store, "spectest", "table", table)?;
let ty = MemoryType::new(1, Some(2));
let memory = Memory::new(&mut *store, ty)?;
linker.define("spectest", "memory", memory)?;
linker.define(&mut *store, "spectest", "memory", memory)?;
if use_shared_memory {
let ty = MemoryType::shared(1, 1);
let memory = Memory::new(&mut *store, ty)?;
linker.define("spectest", "shared_memory", memory)?;
linker.define(&mut *store, "spectest", "shared_memory", memory)?;
}
Ok(())

2
tests/all/call_hook.rs

@ -262,7 +262,7 @@ async fn call_linked_func_async() -> Result<(), Error> {
let mut linker = Linker::new(&engine);
linker.define("host", "f", f)?;
linker.define(&mut store, "host", "f", f)?;
let wat = r#"
(module

44
tests/all/linker.rs

@ -56,36 +56,36 @@ fn link_twice_bad() -> Result<()> {
// globals
let ty = GlobalType::new(ValType::I32, Mutability::Const);
let global = Global::new(&mut store, ty, Val::I32(0))?;
linker.define("g", "1", global.clone())?;
assert!(linker.define("g", "1", global.clone()).is_err());
linker.define(&mut store, "g", "1", global.clone())?;
assert!(linker.define(&mut store, "g", "1", global.clone()).is_err());
let ty = GlobalType::new(ValType::I32, Mutability::Var);
let global = Global::new(&mut store, ty, Val::I32(0))?;
linker.define("g", "2", global.clone())?;
assert!(linker.define("g", "2", global.clone()).is_err());
linker.define(&mut store, "g", "2", global.clone())?;
assert!(linker.define(&mut store, "g", "2", global.clone()).is_err());
let ty = GlobalType::new(ValType::I64, Mutability::Const);
let global = Global::new(&mut store, ty, Val::I64(0))?;
linker.define("g", "3", global.clone())?;
assert!(linker.define("g", "3", global.clone()).is_err());
linker.define(&mut store, "g", "3", global.clone())?;
assert!(linker.define(&mut store, "g", "3", global.clone()).is_err());
// memories
let ty = MemoryType::new(1, None);
let memory = Memory::new(&mut store, ty)?;
linker.define("m", "", memory.clone())?;
assert!(linker.define("m", "", memory.clone()).is_err());
linker.define(&mut store, "m", "", memory.clone())?;
assert!(linker.define(&mut store, "m", "", memory.clone()).is_err());
let ty = MemoryType::new(2, None);
let memory = Memory::new(&mut store, ty)?;
assert!(linker.define("m", "", memory.clone()).is_err());
assert!(linker.define(&mut store, "m", "", memory.clone()).is_err());
// tables
let ty = TableType::new(ValType::FuncRef, 1, None);
let table = Table::new(&mut store, ty, Val::FuncRef(None))?;
linker.define("t", "", table.clone())?;
assert!(linker.define("t", "", table.clone()).is_err());
linker.define(&mut store, "t", "", table.clone())?;
assert!(linker.define(&mut store, "t", "", table.clone()).is_err());
let ty = TableType::new(ValType::FuncRef, 2, None);
let table = Table::new(&mut store, ty, Val::FuncRef(None))?;
assert!(linker.define("t", "", table.clone()).is_err());
assert!(linker.define(&mut store, "t", "", table.clone()).is_err());
Ok(())
}
@ -100,11 +100,8 @@ fn function_interposition() -> Result<()> {
)?;
for _ in 0..4 {
let instance = linker.instantiate(&mut store, &module)?;
linker.define(
"red",
"green",
instance.get_export(&mut store, "green").unwrap().clone(),
)?;
let green = instance.get_export(&mut store, "green").unwrap().clone();
linker.define(&mut store, "red", "green", green)?;
module = Module::new(
store.engine(),
r#"(module
@ -137,11 +134,8 @@ fn function_interposition_renamed() -> Result<()> {
)?;
for _ in 0..4 {
let instance = linker.instantiate(&mut store, &module)?;
linker.define(
"red",
"green",
instance.get_export(&mut store, "export").unwrap().clone(),
)?;
let export = instance.get_export(&mut store, "export").unwrap().clone();
linker.define(&mut store, "red", "green", export)?;
module = Module::new(
store.engine(),
r#"(module
@ -334,7 +328,7 @@ fn instance_pre() -> Result<()> {
linker.func_wrap("", "", || {})?;
let module = Module::new(&engine, r#"(module (import "" "" (func)))"#)?;
let instance_pre = linker.instantiate_pre(&mut Store::new(&engine, ()), &module)?;
let instance_pre = linker.instantiate_pre(&module)?;
instance_pre.instantiate(&mut Store::new(&engine, ()))?;
instance_pre.instantiate(&mut Store::new(&engine, ()))?;
@ -344,7 +338,7 @@ fn instance_pre() -> Result<()> {
GlobalType::new(ValType::I32, Mutability::Const),
1.into(),
)?;
linker.define("", "g", global)?;
linker.define(&mut store, "", "g", global)?;
let module = Module::new(
&engine,
@ -353,7 +347,7 @@ fn instance_pre() -> Result<()> {
(import "" "g" (global i32))
)"#,
)?;
let instance_pre = linker.instantiate_pre(&mut store, &module)?;
let instance_pre = linker.instantiate_pre(&module)?;
instance_pre.instantiate(&mut store)?;
instance_pre.instantiate(&mut store)?;
Ok(())

Loading…
Cancel
Save