#![cfg(not(miri))] use super::engine; use wasmtime::Result; use wasmtime::{ Config, Engine, Store, component::{Component, Linker}, }; mod ownership; mod results; mod no_imports { use super::*; use std::rc::Rc; wasmtime::component::bindgen!({ inline: " package foo:foo; world no-imports { export foo: interface { foo: func(); } export bar: func(); } ", }); #[test] fn run() -> Result<()> { let engine = engine(); let component = Component::new( &engine, r#" (component (core module $m (func (export "")) ) (core instance $i (instantiate $m)) (func $f (export "bar") (canon lift (core func $i ""))) (instance $i (export "foo" (func $f))) (export "foo" (instance $i)) ) "#, )?; let linker = Linker::new(&engine); let mut store = Store::new(&engine, ()); let no_imports = NoImports::instantiate(&mut store, &component, &linker)?; no_imports.call_bar(&mut store)?; no_imports.foo().call_foo(&mut store)?; let linker = Linker::new(&engine); let mut non_send_store = Store::new(&engine, Rc::new(())); let no_imports = NoImports::instantiate(&mut non_send_store, &component, &linker)?; no_imports.call_bar(&mut non_send_store)?; no_imports.foo().call_foo(&mut non_send_store)?; Ok(()) } } mod no_imports_concurrent { use super::*; use futures::{ FutureExt, stream::{FuturesUnordered, TryStreamExt}, }; wasmtime::component::bindgen!({ inline: " package foo:foo; world no-imports { export foo: interface { foo: async func(); } export bar: async func(); } ", }); #[tokio::test] async fn run() -> Result<()> { let mut config = Config::new(); config.wasm_component_model_async(true); let engine = &Engine::new(&config)?; let component = Component::new( &engine, r#" (component (core module $m (import "" "task.return" (func $task-return)) (func (export "bar") (result i32) call $task-return i32.const 0 ) (func (export "callback") (param i32 i32 i32) (result i32) unreachable) ) (core func $task-return (canon task.return)) (core instance $i (instantiate $m (with "" (instance (export "task.return" (func $task-return)))) )) (func $f (export "bar") (canon lift (core func $i "bar") async (callback (func $i "callback"))) ) (instance $i (export "foo" (func $f))) (export "foo" (instance $i)) ) "#, )?; let linker = Linker::new(&engine); let mut store = Store::new(&engine, ()); let no_imports = NoImports::instantiate_async(&mut store, &component, &linker).await?; store .run_concurrent(async move |accessor| { let mut futures = FuturesUnordered::new(); futures.push(no_imports.call_bar(accessor).boxed()); futures.push(no_imports.foo().call_foo(accessor).boxed()); assert!(futures.try_next().await?.is_some()); assert!(futures.try_next().await?.is_some()); Ok(()) }) .await? } } mod one_import { use super::*; use wasmtime::component::HasSelf; wasmtime::component::bindgen!({ inline: " package foo:foo; world one-import { import foo: interface { foo: func(); } export bar: func(); } ", }); #[test] fn run() -> Result<()> { let engine = engine(); let component = Component::new( &engine, r#" (component (import "foo" (instance $i (export "foo" (func)) )) (core module $m (import "" "" (func)) (export "" (func 0)) ) (core func $f (canon lower (func $i "foo"))) (core instance $i (instantiate $m (with "" (instance (export "" (func $f)))) )) (func $f (export "bar") (canon lift (core func $i ""))) ) "#, )?; #[derive(Default)] struct MyImports { hit: bool, } impl foo::Host for MyImports { fn foo(&mut self) { self.hit = true; } } let mut linker = Linker::new(&engine); foo::add_to_linker::<_, HasSelf<_>>(&mut linker, |f| f)?; let mut store = Store::new(&engine, MyImports::default()); let one_import = OneImport::instantiate(&mut store, &component, &linker)?; one_import.call_bar(&mut store)?; assert!(store.data().hit); Ok(()) } } mod one_import_concurrent { use super::*; use wasmtime::component::{Accessor, HasData}; wasmtime::component::bindgen!({ inline: " package foo:foo; world no-imports { import foo: interface { foo: async func(); } export bar: async func(); } " }); #[tokio::test] async fn run() -> Result<()> { let mut config = Config::new(); config.wasm_component_model_async(true); let engine = &Engine::new(&config)?; let component = Component::new( &engine, r#" (component (import "foo" (instance $foo-instance (export "foo" (func async)) )) (core module $libc (memory (export "memory") 1) ) (core instance $libc-instance (instantiate $libc)) (core module $m (import "" "foo" (func $foo (param) (result i32))) (import "" "task.return" (func $task-return)) (func (export "bar") (result i32) call $foo drop call $task-return i32.const 0 ) (func (export "callback") (param i32 i32 i32) (result i32) unreachable) ) (core func $foo (canon lower (func $foo-instance "foo") async (memory $libc-instance "memory"))) (core func $task-return (canon task.return)) (core instance $i (instantiate $m (with "" (instance (export "task.return" (func $task-return)) (export "foo" (func $foo)) )) )) (func $f (export "bar") async (canon lift (core func $i "bar") async (callback (func $i "callback"))) ) (instance $i (export "foo" (func $f))) (export "foo" (instance $i)) ) "#, )?; #[derive(Default)] struct MyImports { hit: bool, } impl HasData for MyImports { type Data<'a> = &'a mut MyImports; } impl foo::HostWithStore for MyImports { async fn foo(accessor: &Accessor) { accessor.with(|mut view| view.get().hit = true); } } impl foo::Host for MyImports {} let mut linker = Linker::new(&engine); foo::add_to_linker::<_, MyImports>(&mut linker, |x| x)?; let mut store = Store::new(&engine, MyImports::default()); let no_imports = NoImports::instantiate_async(&mut store, &component, &linker).await?; store .run_concurrent(async move |accessor| no_imports.call_bar(accessor).await) .await??; assert!(store.data().hit); Ok(()) } } mod resources_at_world_level { use super::*; use wasmtime::component::{HasSelf, Resource}; wasmtime::component::bindgen!({ inline: " package foo:foo; world resources { resource x { constructor(); } export y: func(x: x); } ", }); #[test] fn run() -> Result<()> { let engine = engine(); let component = Component::new( &engine, r#" (component (import "x" (type $x (sub resource))) (import "[constructor]x" (func $ctor (result (own $x)))) (core func $dtor (canon resource.drop $x)) (core func $ctor (canon lower (func $ctor))) (core module $m (import "" "ctor" (func $ctor (result i32))) (import "" "dtor" (func $dtor (param i32))) (func (export "x") (param i32) (call $dtor (local.get 0)) (call $dtor (call $ctor)) ) ) (core instance $i (instantiate $m (with "" (instance (export "ctor" (func $ctor)) (export "dtor" (func $dtor)) )) )) (func (export "y") (param "x" (own $x)) (canon lift (core func $i "x"))) ) "#, )?; #[derive(Default)] struct MyImports { ctor_hit: bool, drops: usize, } impl HostX for MyImports { fn new(&mut self) -> Resource { self.ctor_hit = true; Resource::new_own(80) } fn drop(&mut self, val: Resource) -> Result<()> { match self.drops { 0 => assert_eq!(val.rep(), 40), 1 => assert_eq!(val.rep(), 80), _ => unreachable!(), } self.drops += 1; Ok(()) } } impl ResourcesImports for MyImports {} let mut linker = Linker::new(&engine); Resources::add_to_linker::<_, HasSelf<_>>(&mut linker, |f| f)?; let mut store = Store::new(&engine, MyImports::default()); let one_import = Resources::instantiate(&mut store, &component, &linker)?; one_import.call_y(&mut store, Resource::new_own(40))?; assert!(store.data().ctor_hit); assert_eq!(store.data().drops, 2); Ok(()) } } mod resources_at_interface_level { use super::*; use wasmtime::component::{HasSelf, Resource}; wasmtime::component::bindgen!({ inline: " package foo:foo; interface def { resource x { constructor(); } } interface user { use def.{x}; y: func(x: x); } world resources { export user; } ", }); #[test] fn run() -> Result<()> { let engine = engine(); let component = Component::new( &engine, r#" (component (import (interface "foo:foo/def") (instance $i (export "x" (type $x (sub resource))) (export "[constructor]x" (func (result (own $x)))) )) (alias export $i "x" (type $x)) (core func $dtor (canon resource.drop $x)) (core func $ctor (canon lower (func $i "[constructor]x"))) (core module $m (import "" "ctor" (func $ctor (result i32))) (import "" "dtor" (func $dtor (param i32))) (func (export "x") (param i32) (call $dtor (local.get 0)) (call $dtor (call $ctor)) ) ) (core instance $i (instantiate $m (with "" (instance (export "ctor" (func $ctor)) (export "dtor" (func $dtor)) )) )) (func $y (param "x" (own $x)) (canon lift (core func $i "x"))) (instance (export (interface "foo:foo/user")) (export "y" (func $y)) ) ) "#, )?; #[derive(Default)] struct MyImports { ctor_hit: bool, drops: usize, } use foo::foo::def::X; impl foo::foo::def::HostX for MyImports { fn new(&mut self) -> Resource { self.ctor_hit = true; Resource::new_own(80) } fn drop(&mut self, val: Resource) -> Result<()> { match self.drops { 0 => assert_eq!(val.rep(), 40), 1 => assert_eq!(val.rep(), 80), _ => unreachable!(), } self.drops += 1; Ok(()) } } impl foo::foo::def::Host for MyImports {} let mut linker = Linker::new(&engine); Resources::add_to_linker::<_, HasSelf<_>>(&mut linker, |f| f)?; let mut store = Store::new(&engine, MyImports::default()); let one_import = Resources::instantiate(&mut store, &component, &linker)?; one_import .foo_foo_user() .call_y(&mut store, Resource::new_own(40))?; assert!(store.data().ctor_hit); assert_eq!(store.data().drops, 2); Ok(()) } } mod async_config { use super::*; wasmtime::component::bindgen!({ inline: " package foo:foo; world t1 { import foo: interface { foo: func(); } import x: func(); import y: func(); export z: func(); } ", imports: { default: async }, exports: { default: async }, }); #[expect(dead_code, reason = "just here for bindings")] struct T; impl T1Imports for T { async fn x(&mut self) {} async fn y(&mut self) {} } async fn _test_t1(t1: &T1, store: &mut Store<()>) { let _ = t1.call_z(&mut *store).await; } wasmtime::component::bindgen!({ inline: " package foo:foo; world t2 { import x: func(); import y: func(); export z: func(); } ", imports: { "x": tracing, default: async, }, exports: { default: async }, }); impl T2Imports for T { fn x(&mut self) {} async fn y(&mut self) {} } async fn _test_t2(t2: &T2, store: &mut Store<()>) { let _ = t2.call_z(&mut *store).await; } wasmtime::component::bindgen!({ inline: " package foo:foo; world t3 { import x: func(); import y: func(); export z: func(); } ", imports: { "x": async }, exports: { default: async }, }); impl T3Imports for T { async fn x(&mut self) {} fn y(&mut self) {} } async fn _test_t3(t3: &T3, store: &mut Store<()>) { let _ = t3.call_z(&mut *store).await; } } mod exported_resources { use super::*; use std::mem; use wasmtime::component::{HasSelf, Resource}; wasmtime::component::bindgen!({ inline: " package foo:foo; interface a { resource x { constructor(); } } world resources { export b: interface { use a.{x as y}; resource x { constructor(y: y); foo: func() -> u32; } } resource x; export f: func(x1: x, x2: x) -> x; } ", }); #[derive(Default)] struct MyImports { hostcalls: Vec, next_a_x: u32, } #[derive(PartialEq, Debug)] enum Hostcall { DropRootX(u32), DropAX(u32), NewA, } use foo::foo::a; impl ResourcesImports for MyImports {} impl HostX for MyImports { fn drop(&mut self, val: Resource) -> Result<()> { self.hostcalls.push(Hostcall::DropRootX(val.rep())); Ok(()) } } impl a::HostX for MyImports { fn new(&mut self) -> Resource { let rep = self.next_a_x; self.next_a_x += 1; self.hostcalls.push(Hostcall::NewA); Resource::new_own(rep) } fn drop(&mut self, val: Resource) -> Result<()> { self.hostcalls.push(Hostcall::DropAX(val.rep())); Ok(()) } } impl foo::foo::a::Host for MyImports {} #[test] fn run() -> Result<()> { let engine = engine(); let component = Component::new( &engine, r#" (component ;; setup the `foo:foo/a` import (import (interface "foo:foo/a") (instance $a (export "x" (type $x (sub resource))) (export "[constructor]x" (func (result (own $x)))) )) (alias export $a "x" (type $a-x)) (core func $a-x-drop (canon resource.drop $a-x)) (core func $a-x-ctor (canon lower (func $a "[constructor]x"))) ;; setup the root import of the `x` resource (import "x" (type $x (sub resource))) (core func $root-x-dtor (canon resource.drop $x)) ;; setup and declare the `x` resource for the `b` export. (core module $indirect-dtor (func (export "b-x-dtor") (param i32) local.get 0 i32.const 0 call_indirect (param i32) ) (table (export "$imports") 1 1 funcref) ) (core instance $indirect-dtor (instantiate $indirect-dtor)) (type $b-x (resource (rep i32) (dtor (func $indirect-dtor "b-x-dtor")))) (core func $b-x-drop (canon resource.drop $b-x)) (core func $b-x-rep (canon resource.rep $b-x)) (core func $b-x-new (canon resource.new $b-x)) ;; main module implementation (core module $main (import "foo:foo/a" "[constructor]x" (func $a-x-ctor (result i32))) (import "foo:foo/a" "[resource-drop]x" (func $a-x-dtor (param i32))) (import "$root" "[resource-drop]x" (func $x-dtor (param i32))) (import "[export]b" "[resource-drop]x" (func $b-x-dtor (param i32))) (import "[export]b" "[resource-new]x" (func $b-x-new (param i32) (result i32))) (import "[export]b" "[resource-rep]x" (func $b-x-rep (param i32) (result i32))) (func (export "b#[constructor]x") (param i32) (result i32) (call $a-x-dtor (local.get 0)) (call $b-x-new (call $a-x-ctor)) ) (func (export "b#[method]x.foo") (param i32) (result i32) local.get 0) (func (export "b#[dtor]x") (param i32) (call $a-x-dtor (local.get 0)) ) (func (export "f") (param i32 i32) (result i32) (call $x-dtor (local.get 0)) local.get 1 ) ) (core instance $main (instantiate $main (with "foo:foo/a" (instance (export "[resource-drop]x" (func $a-x-drop)) (export "[constructor]x" (func $a-x-ctor)) )) (with "$root" (instance (export "[resource-drop]x" (func $root-x-dtor)) )) (with "[export]b" (instance (export "[resource-drop]x" (func $b-x-drop)) (export "[resource-rep]x" (func $b-x-rep)) (export "[resource-new]x" (func $b-x-new)) )) )) ;; fill in `$indirect-dtor`'s table with the actual destructor definition ;; now that it's available. (core module $fixup (import "" "b-x-dtor" (func $b-x-dtor (param i32))) (import "" "$imports" (table 1 1 funcref)) (elem (i32.const 0) func $b-x-dtor) ) (core instance (instantiate $fixup (with "" (instance (export "$imports" (table 0 "$imports")) (export "b-x-dtor" (func $main "b#[dtor]x")) )) )) ;; Create the `b` export through a subcomponent instantiation. (func $b-x-ctor (param "y" (own $a-x)) (result (own $b-x)) (canon lift (core func $main "b#[constructor]x"))) (func $b-x-foo (param "self" (borrow $b-x)) (result u32) (canon lift (core func $main "b#[method]x.foo"))) (component $b (import "a-x" (type $y (sub resource))) (import "b-x" (type $x' (sub resource))) (import "ctor" (func $ctor (param "y" (own $y)) (result (own $x')))) (import "foo" (func $foo (param "self" (borrow $x')) (result u32))) (export $x "x" (type $x')) (export "[constructor]x" (func $ctor) (func (param "y" (own $y)) (result (own $x)))) (export "[method]x.foo" (func $foo) (func (param "self" (borrow $x)) (result u32))) ) (instance (export "b") (instantiate $b (with "ctor" (func $b-x-ctor)) (with "foo" (func $b-x-foo)) (with "a-x" (type 0 "x")) (with "b-x" (type $b-x)) )) ;; Create the `f` export which is a bare function (func (export "f") (param "x1" (own $x)) (param "x2" (own $x)) (result (own $x)) (canon lift (core func $main "f"))) ) "#, )?; let mut linker = Linker::new(&engine); Resources::add_to_linker::<_, HasSelf<_>>(&mut linker, |f| f)?; let mut store = Store::new(&engine, MyImports::default()); let i = Resources::instantiate(&mut store, &component, &linker)?; // call the root export `f` twice let ret = i.call_f(&mut store, Resource::new_own(1), Resource::new_own(2))?; assert_eq!(ret.rep(), 2); assert_eq!( mem::take(&mut store.data_mut().hostcalls), [Hostcall::DropRootX(1)] ); let ret = i.call_f(&mut store, Resource::new_own(3), Resource::new_own(4))?; assert_eq!(ret.rep(), 4); assert_eq!( mem::take(&mut store.data_mut().hostcalls), [Hostcall::DropRootX(3)] ); // interact with the `b` export let b = i.b(); let b_x = b.x().call_constructor(&mut store, Resource::new_own(5))?; assert_eq!( mem::take(&mut store.data_mut().hostcalls), [Hostcall::DropAX(5), Hostcall::NewA] ); b.x().call_foo(&mut store, b_x)?; assert_eq!(mem::take(&mut store.data_mut().hostcalls), []); b_x.resource_drop(&mut store)?; assert_eq!( mem::take(&mut store.data_mut().hostcalls), [Hostcall::DropAX(0)], ); Ok(()) } } mod unstable_import { use super::*; use wasmtime::component::HasSelf; wasmtime::component::bindgen!({ inline: " package foo:foo; @unstable(feature = experimental-interface) interface my-interface { @unstable(feature = experimental-function) my-function: func(); } world my-world { @unstable(feature = experimental-import) import my-interface; export bar: func(); } ", }); #[test] fn run() -> Result<()> { // In the example above, all features are required for `my-function` to be imported: assert_success( LinkOptions::default() .experimental_interface(true) .experimental_import(true) .experimental_function(true), ); // And every other incomplete combination should fail: assert_failure(&LinkOptions::default()); assert_failure(LinkOptions::default().experimental_function(true)); assert_failure(LinkOptions::default().experimental_interface(true)); assert_failure( LinkOptions::default() .experimental_interface(true) .experimental_function(true), ); assert_failure( LinkOptions::default() .experimental_interface(true) .experimental_import(true), ); assert_failure(LinkOptions::default().experimental_import(true)); assert_failure( LinkOptions::default() .experimental_import(true) .experimental_function(true), ); Ok(()) } fn assert_success(link_options: &LinkOptions) { run_with_options(link_options).unwrap(); } fn assert_failure(link_options: &LinkOptions) { let err = run_with_options(link_options).unwrap_err().to_string(); assert_eq!( err, "component imports instance `foo:foo/my-interface`, but a matching implementation was not found in the linker" ); } fn run_with_options(link_options: &LinkOptions) -> Result<()> { let engine = engine(); let component = Component::new( &engine, r#" (component (import "foo:foo/my-interface" (instance $i (export "my-function" (func)) )) (core module $m (import "" "" (func)) (export "" (func 0)) ) (core func $f (canon lower (func $i "my-function"))) (core instance $r (instantiate $m (with "" (instance (export "" (func $f)))) )) (func $f (export "bar") (canon lift (core func $r ""))) ) "#, )?; #[derive(Default)] struct MyHost; impl foo::foo::my_interface::Host for MyHost { fn my_function(&mut self) {} } let mut linker = Linker::new(&engine); MyWorld::add_to_linker::<_, HasSelf<_>>(&mut linker, link_options, |h| h)?; let mut store = Store::new(&engine, MyHost::default()); let one_import = MyWorld::instantiate(&mut store, &component, &linker)?; one_import.call_bar(&mut store)?; Ok(()) } } mod anyhow_errors { use super::*; use crate::ErrorExt; use wasmtime::component::HasSelf; use wasmtime::error::Context as _; wasmtime::component::bindgen!({ anyhow: true, imports: { default: trappable }, inline: " package foo:foo; interface my-interface { ok: func() -> u32; trap: func() -> u32; } world my-world { import my-interface; export ok: func() -> u32; export trap: func() -> u32; } ", }); #[test] fn run() -> Result<()> { let engine = engine(); let component = Component::new( &engine, r#" (component (import "foo:foo/my-interface" (instance $i (export "ok" (func (result u32))) (export "trap" (func (result u32))) )) (core module $m (import "" "ok" (func (result i32))) (import "" "trap" (func (result i32))) (export "ok" (func 0)) (export "trap" (func 1)) ) (core func $ok (canon lower (func $i "ok"))) (core func $trap (canon lower (func $i "trap"))) (core instance $r (instantiate $m (with "" (instance (export "ok" (func $ok)) (export "trap" (func $trap)))) )) (func (export "ok") (result u32) (canon lift (core func $r "ok"))) (func (export "trap") (result u32) (canon lift (core func $r "trap"))) ) "#, )?; #[derive(Default)] struct MyHost; impl foo::foo::my_interface::Host for MyHost { // NB: these must return an `anyhow::Result` since we `bindgen!`ed // with `anyhow: true`. fn ok(&mut self) -> anyhow_for_testing::Result { Ok(42) } fn trap(&mut self) -> anyhow_for_testing::Result { anyhow_for_testing::bail!("anyhow error") } } let mut linker = Linker::new(&engine); MyWorld::add_to_linker::<_, HasSelf<_>>(&mut linker, |h| h) .context("failed to add to linker")?; let mut store = Store::new(&engine, MyHost::default()); let instance = MyWorld::instantiate(&mut store, &component, &linker) .context("failed to instantiate")?; let x = instance .call_ok(&mut store) .context("failed to call `ok` function")?; assert_eq!(x, 42); let result = instance.call_trap(&mut store); let error = result.unwrap_err(); error.assert_contains("anyhow error"); Ok(()) } }