#![cfg(not(miri))] // not testing unsafe code use wasmtime::*; fn async_store() -> Store<()> { let engine = Engine::default(); let mut store = Store::new(&engine, ()); Func::wrap_async(&mut store, |_, ()| Box::new(async {})); return store; } struct MyAsyncLimiter; #[async_trait::async_trait] impl ResourceLimiterAsync for MyAsyncLimiter { async fn memory_growing( &mut self, _current: usize, _desired: usize, _maximum: Option, ) -> Result { Ok(true) } async fn table_growing( &mut self, _current: usize, _desired: usize, _maximum: Option, ) -> Result { Ok(true) } } fn async_limiter_store() -> Store { let engine = Engine::default(); let mut store = Store::new(&engine, MyAsyncLimiter); store.limiter_async(|x| x); return store; } fn assert_requires_async(store: &mut Store) { let module = Module::new(store.engine(), "(module)").unwrap(); assert!(Instance::new(&mut *store, &module, &[]).is_err()); } #[test] fn require_async_after_func_wrap() { let engine = Engine::default(); let mut store = Store::new(&engine, ()); Func::wrap_async(&mut store, |_, ()| Box::new(async {})); assert_requires_async(&mut store); } #[test] fn require_async_after_func_new() { let engine = Engine::default(); let mut store = Store::new(&engine, ()); let ty = FuncType::new(store.engine(), [], []); Func::new_async(&mut store, ty, |_, _, _| Box::new(async { Ok(()) })); assert_requires_async(&mut store); } #[tokio::test] async fn require_async_after_linker_with_func_wrap() -> Result<()> { let engine = Engine::default(); let mut store = Store::new(&engine, ()); let mut linker = Linker::new(store.engine()); linker.func_wrap_async("", "", |_, ()| Box::new(async {}))?; let module = Module::new(store.engine(), r#"(module (import "" "" (func)))"#)?; linker.instantiate_async(&mut store, &module).await?; assert_requires_async(&mut store); Ok(()) } #[tokio::test] async fn require_async_after_linker_with_func_new() -> Result<()> { let engine = Engine::default(); let mut store = Store::new(&engine, ()); let mut linker = Linker::new(store.engine()); let ty = FuncType::new(store.engine(), [], []); linker.func_new_async("", "", ty, |_, _, _| Box::new(async { Ok(()) }))?; let module = Module::new(store.engine(), r#"(module (import "" "" (func)))"#)?; linker.instantiate_async(&mut store, &module).await?; assert_requires_async(&mut store); Ok(()) } #[test] fn require_async_after_epochs() -> Result<()> { let mut config = Config::new(); config.epoch_interruption(true); let engine = Engine::new(&config)?; let mut store = Store::new(&engine, ()); store.epoch_deadline_async_yield_and_update(1); assert_requires_async(&mut store); Ok(()) } #[test] fn require_async_after_fuel() -> Result<()> { let mut config = Config::new(); config.consume_fuel(true); let engine = Engine::new(&config)?; let mut store = Store::new(&engine, ()); store.fuel_async_yield_interval(Some(1))?; assert_requires_async(&mut store); Ok(()) } #[test] fn require_async_after_async_limiter() -> Result<()> { let mut store = async_limiter_store(); assert_requires_async(&mut store); Ok(()) } #[tokio::test] async fn require_async_with_linker_func_wrap() -> Result<()> { let engine = Engine::default(); let mut store = Store::new(&engine, ()); let mut linker = Linker::new(store.engine()); linker.func_wrap_async("", "", |_, ()| Box::new(async {}))?; let module = Module::new(store.engine(), r#"(module (import "" "" (func)))"#)?; assert!(linker.instantiate(&mut store, &module).is_err()); linker.instantiate_async(&mut store, &module).await?; Ok(()) } #[test] fn require_async_with_debug_handler() -> Result<()> { let mut config = Config::new(); config.guest_debug(true); let engine = Engine::new(&config)?; let mut store = Store::new(&engine, ()); store.set_debug_handler(MyDebugHandler); assert_requires_async(&mut store); return Ok(()); #[derive(Clone)] struct MyDebugHandler; impl DebugHandler for MyDebugHandler { type Data = (); async fn handle(&self, _: StoreContextMut<'_, ()>, _: DebugEvent<'_>) {} } } struct MyAsyncCallHook; #[async_trait::async_trait] impl CallHookHandler<()> for MyAsyncCallHook { async fn handle_call_event(&self, _: StoreContextMut<'_, ()>, _: CallHook) -> Result<()> { Ok(()) } } #[test] fn require_async_with_async_call_hook() -> Result<()> { let engine = Engine::default(); let mut store = Store::new(&engine, ()); store.call_hook_async(MyAsyncCallHook); assert_requires_async(&mut store); Ok(()) } #[tokio::test] async fn async_disallows_instance_new() -> Result<()> { let mut store = async_store(); let module = Module::new(store.engine(), "(module)")?; assert!(Instance::new(&mut store, &module, &[]).is_err()); Instance::new_async(&mut store, &module, &[]).await?; Ok(()) } #[tokio::test] async fn async_disallows_linker_instantiate() -> Result<()> { let mut store = async_store(); let module = Module::new(store.engine(), "(module)")?; let linker = Linker::new(store.engine()); assert!(linker.instantiate(&mut store, &module).is_err()); linker.instantiate_async(&mut store, &module).await?; Ok(()) } #[tokio::test] async fn async_disallows_func_call() -> Result<()> { let mut store = async_store(); let func = Func::wrap(&mut store, || {}); assert!(func.call(&mut store, &[], &mut []).is_err()); func.call_async(&mut store, &[], &mut []).await?; Ok(()) } #[tokio::test] async fn async_disallows_typed_func_call() -> Result<()> { let mut store = async_store(); let func = Func::wrap(&mut store, || {}); let func = func.typed::<(), ()>(&mut store)?; assert!(func.call(&mut store, ()).is_err()); func.call_async(&mut store, ()).await?; Ok(()) } #[tokio::test] async fn async_disallows_gc() -> Result<()> { let mut store = async_limiter_store(); assert!(store.gc(None).is_err()); store.gc_async(None).await; Ok(()) } #[tokio::test] async fn async_disallows_array_ref_new() -> Result<()> { let mut store = async_limiter_store(); let ty = ArrayType::new( store.engine(), FieldType::new(Mutability::Var, StorageType::I8), ); let pre = ArrayRefPre::new(&mut store, ty); assert!(ArrayRef::new(&mut store, &pre, &Val::I32(0), 10).is_err()); ArrayRef::new_async(&mut store, &pre, &Val::I32(0), 10).await?; Ok(()) } #[tokio::test] async fn async_disallows_array_ref_new_fixed() -> Result<()> { let mut store = async_limiter_store(); let ty = ArrayType::new( store.engine(), FieldType::new(Mutability::Var, StorageType::I8), ); let pre = ArrayRefPre::new(&mut store, ty); assert!(ArrayRef::new_fixed(&mut store, &pre, &[Val::I32(0)]).is_err()); ArrayRef::new_fixed_async(&mut store, &pre, &[Val::I32(0)]).await?; Ok(()) } #[tokio::test] async fn async_disallows_exnref_new() -> Result<()> { let mut store = async_limiter_store(); let ty = ExnType::new(store.engine(), [])?; let pre = ExnRefPre::new(&mut store, ty); let fty = FuncType::new(store.engine(), [], []); let tag = Tag::new(&mut store, &TagType::new(fty))?; assert!(ExnRef::new(&mut store, &pre, &tag, &[]).is_err()); ExnRef::new_async(&mut store, &pre, &tag, &[]).await?; Ok(()) } #[tokio::test] async fn async_disallows_externref_new() -> Result<()> { let mut store = async_limiter_store(); assert!(ExternRef::new(&mut store, 1).is_err()); ExternRef::new_async(&mut store, 1).await?; Ok(()) } #[tokio::test] async fn async_disallows_structref_new() -> Result<()> { let mut store = async_limiter_store(); let ty = StructType::new( store.engine(), [FieldType::new(Mutability::Var, StorageType::I8)], )?; let pre = StructRefPre::new(&mut store, ty); assert!(StructRef::new(&mut store, &pre, &[Val::I32(0)]).is_err()); StructRef::new_async(&mut store, &pre, &[Val::I32(0)]).await?; Ok(()) } #[test] fn epoch_yield_disallowed_without_async() -> Result<()> { let mut config = Config::new(); config.epoch_interruption(true); let engine = Engine::new(&config)?; let mut store = Store::new(&engine, ()); let module = Module::new(&engine, r#"(module (func (export "") (loop br 0)))"#)?; let instance = Instance::new(&mut store, &module, &[])?; let func = instance.get_typed_func::<(), ()>(&mut store, "")?; store.epoch_deadline_callback(|_store| Ok(UpdateDeadline::Yield(1))); assert!(func.call(&mut store, ()).is_err()); store.epoch_deadline_callback(|_store| Ok(UpdateDeadline::YieldCustom(1, Box::pin(async {})))); assert!(func.call(&mut store, ()).is_err()); store.epoch_deadline_trap(); let err = func.call(&mut store, ()).unwrap_err().downcast::()?; assert_eq!(err, Trap::Interrupt); Ok(()) } #[test] fn start_sync_then_configure_async_then_do_async() -> Result<()> { let mut config = Config::new(); config.consume_fuel(true); let engine = Engine::new(&config)?; let mut store = Store::new(&engine, MyAsyncLimiter); let module = Module::new( &engine, r#" (module (import "" "" (func $host)) (memory 1) (func (export "") call $host (loop br 0) ) ) "#, )?; let mut linker = Linker::new(&engine); linker.func_wrap("", "", |mut store: Caller<'_, MyAsyncLimiter>| { store.as_context_mut().fuel_async_yield_interval(Some(1)) })?; store.set_fuel(100)?; let instance = linker.instantiate(&mut store, &module)?; let func = instance.get_typed_func::<(), ()>(&mut store, "")?; let err = func.call(&mut store, ()).unwrap_err(); assert!( format!("{err:?}").contains("configured to do async things"), "bad error {err:?}", ); Ok(()) } #[tokio::test] async fn async_limiter_disallows_table_new() -> Result<()> { let mut store = async_limiter_store(); let ty = TableType::new(RefType::FUNCREF, 1, None); assert!(Table::new(&mut store, ty.clone(), Ref::Func(None)).is_err()); Table::new_async(&mut store, ty, Ref::Func(None)).await?; Ok(()) } #[tokio::test] async fn async_limiter_disallows_table_grow() -> Result<()> { let mut store = async_limiter_store(); let ty = TableType::new(RefType::FUNCREF, 1, None); let table = Table::new_async(&mut store, ty, Ref::Func(None)).await?; assert!(table.grow(&mut store, 1, Ref::Func(None)).is_err()); table.grow_async(&mut store, 1, Ref::Func(None)).await?; Ok(()) } #[tokio::test] async fn async_limiter_disallows_memory_new() -> Result<()> { let mut store = async_limiter_store(); let ty = MemoryType::new(1, None); assert!(Memory::new(&mut store, ty.clone()).is_err()); Memory::new_async(&mut store, ty).await?; Ok(()) } #[tokio::test] async fn async_limiter_disallows_memory_grow() -> Result<()> { let mut store = async_limiter_store(); let ty = MemoryType::new(1, None); let mem = Memory::new_async(&mut store, ty).await?; assert!(mem.grow(&mut store, 1).is_err()); mem.grow_async(&mut store, 1).await?; Ok(()) }