use crate::ErrorExt;
use std::sync::{Arc, Mutex};
use wasmtime::component::{Component, Linker as ComponentLinker};
use wasmtime::*;
const SENTINEL: i64 = 0x1122334455667788;
const COLLIDING_MODULE: &str = r#"(module (func (export "f") (param f64)))"#;
const COLLIDING_COMPONENT: &str = r#"
(component
(core module (func (export "f") (param f64)))
)
"#;
fn engine_pair() -> (Engine, Engine) {
(Engine::default(), Engine::default())
}
fn engine_pair_with(f: impl Fn(&mut Config)) -> Result<(Engine, Engine)> {
let mut config = Config::new();
f(&mut config);
Ok((Engine::new(&config)?, Engine::new(&config)?))
}
const OBSERVER_MODULE: &str = r#"
(module
(import "" "" (func $host (param i64)))
(func (export "go")
i64.const 0x1122334455667788
call $host))
"#;
fn observe_i64_in_host(store: &mut Store<()>) -> Result<i64> {
let observed = Arc::new(Mutex::new(0));
let host = Func::wrap(&mut *store, {
let observed = Arc::clone(&observed);
move |x: i64| *observed.lock().unwrap() = x
});
let engine = store.engine().clone();
let module = Module::new(&engine, OBSERVER_MODULE)?;
let instance = Instance::new(&mut *store, &module, &[host.into()])?;
instance
.get_typed_func::<(), ()>(&mut *store, "go")?
.call(&mut *store, ())?;
let observed = *observed.lock().unwrap();
Ok(observed)
}
async fn observe_i64_in_host_async(store: &mut Store<()>) -> Result<i64> {
let observed = Arc::new(Mutex::new(0));
let host = Func::wrap(&mut *store, {
let observed = Arc::clone(&observed);
move |x: i64| *observed.lock().unwrap() = x
});
let engine = store.engine().clone();
let module = Module::new(&engine, OBSERVER_MODULE)?;
let instance = Instance::new_async(&mut *store, &module, &[host.into()]).await?;
instance
.get_typed_func::<(), ()>(&mut *store, "go")?
.call_async(&mut *store, ())
.await?;
let observed = *observed.lock().unwrap();
Ok(observed)
}
fn assert_store_is_uncorrupted(store: &mut Store<()>) -> Result<()> {
assert_eq!(observe_i64_in_host(store)?, SENTINEL);
Ok(())
}
async fn assert_store_is_uncorrupted_async(store: &mut Store<()>) -> Result<()> {
assert_eq!(observe_i64_in_host_async(store).await?, SENTINEL);
Ok(())
}
#[test]
#[cfg_attr(miri, ignore)]
fn instance_new_rejects_foreign_module() -> Result<()> {
let (a, b) = engine_pair();
let mut store = Store::new(&a, ());
let foreign = Module::new(&b, COLLIDING_MODULE)?;
Instance::new(&mut store, &foreign, &[])
.unwrap_err()
.assert_contains("cross-`Engine`");
assert_store_is_uncorrupted(&mut store)
}
#[test]
#[cfg_attr(miri, ignore)]
fn instance_new_rejects_foreign_module_with_imports() -> Result<()> {
let (a, b) = engine_pair();
let mut store = Store::new(&a, ());
let _ = Func::wrap(&mut store, |_: i32| {});
let host = Func::wrap(&mut store, |_: i64| {});
let foreign = Module::new(&b, r#"(module (import "" "" (func (param i64))))"#)?;
Instance::new(&mut store, &foreign, &[host.into()])
.unwrap_err()
.assert_contains("cross-`Engine`");
assert_store_is_uncorrupted(&mut store)
}
#[test]
#[cfg_attr(miri, ignore)]
fn instantiate_pre_rejects_foreign_definition() -> Result<()> {
let (a, b) = engine_pair();
let mut foreign_store = Store::new(&b, ());
let _ = Func::wrap(&mut foreign_store, |_: i32| {});
let foreign = Func::wrap(&mut foreign_store, |_: f64| {});
let mut linker = Linker::<()>::new(&a);
linker.define(&foreign_store, "", "", foreign)?;
let module = Module::new(&a, r#"(module (import "" "" (func (param i64))))"#)?;
linker
.instantiate_pre(&module)
.err()
.expect("should reject an item defined from a different engine's store")
.assert_contains("cross-`Engine`");
let mut store = Store::new(&a, ());
assert_store_is_uncorrupted(&mut store)
}
#[test]
#[cfg_attr(miri, ignore)]
fn instance_pre_instantiate_rejects_foreign_store() -> Result<()> {
let (a, b) = engine_pair();
let mut store = Store::new(&a, ());
let foreign = Module::new(&b, COLLIDING_MODULE)?;
let pre = Linker::<()>::new(&b).instantiate_pre(&foreign)?;
pre.instantiate(&mut store)
.unwrap_err()
.assert_contains("cross-`Engine`");
assert_store_is_uncorrupted(&mut store)
}
#[test]
#[cfg_attr(miri, ignore)]
fn linker_instantiate_pre_rejects_foreign_module() -> Result<()> {
let (a, b) = engine_pair();
let foreign = Module::new(&b, COLLIDING_MODULE)?;
Linker::<()>::new(&a)
.instantiate_pre(&foreign)
.err()
.expect("should reject a module from a different engine")
.assert_contains("cross-`Engine`");
Ok(())
}
#[test]
#[cfg_attr(miri, ignore)]
fn component_linker_instantiate_pre_rejects_foreign_component() -> Result<()> {
let (a, b) = engine_pair();
let foreign = Component::new(&b, COLLIDING_COMPONENT)?;
ComponentLinker::<()>::new(&a)
.instantiate_pre(&foreign)
.err()
.expect("should reject a component from a different engine")
.assert_contains("cross-`Engine`");
Ok(())
}
#[test]
#[cfg_attr(miri, ignore)]
fn component_instance_pre_instantiate_rejects_foreign_store() -> Result<()> {
let (a, b) = engine_pair();
let mut store = Store::new(&a, ());
let foreign = Component::new(&b, COLLIDING_COMPONENT)?;
let pre = ComponentLinker::<()>::new(&b).instantiate_pre(&foreign)?;
pre.instantiate(&mut store)
.unwrap_err()
.assert_contains("cross-`Engine`");
assert_store_is_uncorrupted(&mut store)
}
#[test]
#[cfg_attr(miri, ignore)]
fn tag_new_rejects_foreign_type() -> Result<()> {
let (a, b) = engine_pair_with(|config| {
config.wasm_exceptions(true);
})?;
let mut store = Store::new(&a, ());
let foreign_ty = TagType::new(FuncType::new(&b, [ValType::I32], []));
Tag::new(&mut store, &foreign_ty)
.unwrap_err()
.assert_contains("wrong engine");
assert_store_is_uncorrupted(&mut store)
}
#[test]
#[cfg_attr(miri, ignore)]
fn table_new_rejects_foreign_type() -> Result<()> {
let (a, b) = engine_pair_with(|config| {
config.wasm_gc(true);
})?;
let mut store = Store::new(&a, ());
let foreign_struct = StructType::new(
&b,
[FieldType::new(
Mutability::Const,
StorageType::ValType(ValType::I32),
)],
)?;
let foreign_heap_ty = HeapType::ConcreteStruct(foreign_struct);
let foreign_ty = TableType::new(RefType::new(true, foreign_heap_ty.clone()), 0, None);
Table::new(&mut store, foreign_ty, Ref::null(&foreign_heap_ty))
.unwrap_err()
.assert_contains("wrong engine");
assert_store_is_uncorrupted(&mut store)
}
#[tokio::test]
#[cfg_attr(miri, ignore)]
async fn table_new_async_rejects_foreign_type() -> Result<()> {
let (a, b) = engine_pair_with(|config| {
config.wasm_gc(true);
config.async_support(true);
})?;
let mut store = Store::new(&a, ());
let foreign_struct = StructType::new(
&b,
[FieldType::new(
Mutability::Const,
StorageType::ValType(ValType::I32),
)],
)?;
let foreign_heap_ty = HeapType::ConcreteStruct(foreign_struct);
let foreign_ty = TableType::new(RefType::new(true, foreign_heap_ty.clone()), 0, None);
Table::new_async(&mut store, foreign_ty, Ref::null(&foreign_heap_ty))
.await
.unwrap_err()
.assert_contains("wrong engine");
assert_store_is_uncorrupted_async(&mut store).await
}
#[test]
#[cfg_attr(miri, ignore)]
fn global_new_rejects_foreign_type() -> Result<()> {
let (a, b) = engine_pair_with(|config| {
config.wasm_gc(true);
})?;
let mut store = Store::new(&a, ());
let foreign_struct = StructType::new(
&b,
[FieldType::new(
Mutability::Const,
StorageType::ValType(ValType::I32),
)],
)?;
let foreign_heap_ty = HeapType::ConcreteStruct(foreign_struct);
let foreign_ty = GlobalType::new(
ValType::Ref(RefType::new(true, foreign_heap_ty.clone())),
Mutability::Const,
);
Global::new(&mut store, foreign_ty, Ref::null(&foreign_heap_ty).into())
.unwrap_err()
.assert_contains("wrong engine");
assert_store_is_uncorrupted(&mut store)
}
#[test]
#[should_panic = "wrong engine"]
fn struct_ref_pre_rejects_foreign_type() {
let (a, b) = engine_pair();
let mut store = Store::new(&a, ());
let foreign_ty = StructType::new(
&b,
[FieldType::new(
Mutability::Const,
StorageType::ValType(ValType::I32),
)],
)
.unwrap();
let _ = StructRefPre::new(&mut store, foreign_ty);
}
#[test]
#[should_panic = "wrong engine"]
fn array_ref_pre_rejects_foreign_type() {
let (a, b) = engine_pair();
let mut store = Store::new(&a, ());
let foreign_ty = ArrayType::new(
&b,
FieldType::new(Mutability::Const, StorageType::ValType(ValType::I32)),
);
let _ = ArrayRefPre::new(&mut store, foreign_ty);
}
#[test]
#[should_panic = "wrong engine"]
fn exn_ref_pre_rejects_foreign_type() {
let (a, b) = engine_pair_with(|config| {
config.wasm_exceptions(true);
})
.unwrap();
let mut store = Store::new(&a, ());
let foreign_ty = ExnType::new(&b, [ValType::I32]).unwrap();
let _ = ExnRefPre::new(&mut store, foreign_ty);
}