Path: blob/main/tests/all/component_model/bindgen.rs
3067 views
#![cfg(not(miri))]12use super::engine;3use wasmtime::Result;4use wasmtime::{5Config, Engine, Store,6component::{Component, Linker},7};89mod ownership;10mod results;1112mod no_imports {13use super::*;14use std::rc::Rc;1516wasmtime::component::bindgen!({17inline: "18package foo:foo;1920world no-imports {21export foo: interface {22foo: func();23}2425export bar: func();26}27",28});2930#[test]31fn run() -> Result<()> {32let engine = engine();3334let component = Component::new(35&engine,36r#"37(component38(core module $m39(func (export ""))40)41(core instance $i (instantiate $m))4243(func $f (export "bar") (canon lift (core func $i "")))4445(instance $i (export "foo" (func $f)))46(export "foo" (instance $i))47)48"#,49)?;5051let linker = Linker::new(&engine);52let mut store = Store::new(&engine, ());53let no_imports = NoImports::instantiate(&mut store, &component, &linker)?;54no_imports.call_bar(&mut store)?;55no_imports.foo().call_foo(&mut store)?;5657let linker = Linker::new(&engine);58let mut non_send_store = Store::new(&engine, Rc::new(()));59let no_imports = NoImports::instantiate(&mut non_send_store, &component, &linker)?;60no_imports.call_bar(&mut non_send_store)?;61no_imports.foo().call_foo(&mut non_send_store)?;62Ok(())63}64}6566mod no_imports_concurrent {67use super::*;68use futures::{69FutureExt,70stream::{FuturesUnordered, TryStreamExt},71};7273wasmtime::component::bindgen!({74inline: "75package foo:foo;7677world no-imports {78export foo: interface {79foo: async func();80}8182export bar: async func();83}84",85});8687#[tokio::test]88async fn run() -> Result<()> {89let mut config = Config::new();90config.wasm_component_model_async(true);91let engine = &Engine::new(&config)?;9293let component = Component::new(94&engine,95r#"96(component97(core module $m98(import "" "task.return" (func $task-return))99(func (export "bar") (result i32)100call $task-return101i32.const 0102)103(func (export "callback") (param i32 i32 i32) (result i32) unreachable)104)105(core func $task-return (canon task.return))106(core instance $i (instantiate $m107(with "" (instance (export "task.return" (func $task-return))))108))109110(func $f (export "bar")111(canon lift (core func $i "bar") async (callback (func $i "callback")))112)113114(instance $i (export "foo" (func $f)))115(export "foo" (instance $i))116)117"#,118)?;119120let linker = Linker::new(&engine);121let mut store = Store::new(&engine, ());122let no_imports = NoImports::instantiate_async(&mut store, &component, &linker).await?;123store124.run_concurrent(async move |accessor| {125let mut futures = FuturesUnordered::new();126futures.push(no_imports.call_bar(accessor).boxed());127futures.push(no_imports.foo().call_foo(accessor).boxed());128assert!(futures.try_next().await?.is_some());129assert!(futures.try_next().await?.is_some());130Ok(())131})132.await?133}134}135136mod one_import {137use super::*;138use wasmtime::component::HasSelf;139140wasmtime::component::bindgen!({141inline: "142package foo:foo;143144world one-import {145import foo: interface {146foo: func();147}148149export bar: func();150}151",152});153154#[test]155fn run() -> Result<()> {156let engine = engine();157158let component = Component::new(159&engine,160r#"161(component162(import "foo" (instance $i163(export "foo" (func))164))165(core module $m166(import "" "" (func))167(export "" (func 0))168)169(core func $f (canon lower (func $i "foo")))170(core instance $i (instantiate $m171(with "" (instance (export "" (func $f))))172))173174(func $f (export "bar") (canon lift (core func $i "")))175)176"#,177)?;178179#[derive(Default)]180struct MyImports {181hit: bool,182}183184impl foo::Host for MyImports {185fn foo(&mut self) {186self.hit = true;187}188}189190let mut linker = Linker::new(&engine);191foo::add_to_linker::<_, HasSelf<_>>(&mut linker, |f| f)?;192let mut store = Store::new(&engine, MyImports::default());193let one_import = OneImport::instantiate(&mut store, &component, &linker)?;194one_import.call_bar(&mut store)?;195assert!(store.data().hit);196Ok(())197}198}199200mod one_import_concurrent {201use super::*;202use wasmtime::component::{Accessor, HasData};203204wasmtime::component::bindgen!({205inline: "206package foo:foo;207208world no-imports {209import foo: interface {210foo: async func();211}212213export bar: async func();214}215"216});217218#[tokio::test]219async fn run() -> Result<()> {220let mut config = Config::new();221config.wasm_component_model_async(true);222let engine = &Engine::new(&config)?;223224let component = Component::new(225&engine,226r#"227(component228(import "foo" (instance $foo-instance229(export "foo" (func async))230))231(core module $libc232(memory (export "memory") 1)233)234(core instance $libc-instance (instantiate $libc))235(core module $m236(import "" "foo" (func $foo (param) (result i32)))237(import "" "task.return" (func $task-return))238(func (export "bar") (result i32)239call $foo240drop241call $task-return242i32.const 0243)244(func (export "callback") (param i32 i32 i32) (result i32) unreachable)245)246(core func $foo (canon lower (func $foo-instance "foo") async (memory $libc-instance "memory")))247(core func $task-return (canon task.return))248(core instance $i (instantiate $m249(with "" (instance250(export "task.return" (func $task-return))251(export "foo" (func $foo))252))253))254255(func $f (export "bar") async256(canon lift (core func $i "bar") async (callback (func $i "callback")))257)258259(instance $i (export "foo" (func $f)))260(export "foo" (instance $i))261)262"#,263)?;264265#[derive(Default)]266struct MyImports {267hit: bool,268}269270impl HasData for MyImports {271type Data<'a> = &'a mut MyImports;272}273274impl foo::HostWithStore for MyImports {275async fn foo<T>(accessor: &Accessor<T, Self>) {276accessor.with(|mut view| view.get().hit = true);277}278}279280impl foo::Host for MyImports {}281282let mut linker = Linker::new(&engine);283foo::add_to_linker::<_, MyImports>(&mut linker, |x| x)?;284let mut store = Store::new(&engine, MyImports::default());285let no_imports = NoImports::instantiate_async(&mut store, &component, &linker).await?;286store287.run_concurrent(async move |accessor| no_imports.call_bar(accessor).await)288.await??;289assert!(store.data().hit);290Ok(())291}292}293294mod resources_at_world_level {295use super::*;296use wasmtime::component::{HasSelf, Resource};297298wasmtime::component::bindgen!({299inline: "300package foo:foo;301302world resources {303resource x {304constructor();305}306307export y: func(x: x);308}309",310});311312#[test]313fn run() -> Result<()> {314let engine = engine();315316let component = Component::new(317&engine,318r#"319(component320(import "x" (type $x (sub resource)))321(import "[constructor]x" (func $ctor (result (own $x))))322323(core func $dtor (canon resource.drop $x))324(core func $ctor (canon lower (func $ctor)))325326(core module $m327(import "" "ctor" (func $ctor (result i32)))328(import "" "dtor" (func $dtor (param i32)))329330(func (export "x") (param i32)331(call $dtor (local.get 0))332(call $dtor (call $ctor))333)334)335(core instance $i (instantiate $m336(with "" (instance337(export "ctor" (func $ctor))338(export "dtor" (func $dtor))339))340))341(func (export "y") (param "x" (own $x))342(canon lift (core func $i "x")))343)344"#,345)?;346347#[derive(Default)]348struct MyImports {349ctor_hit: bool,350drops: usize,351}352353impl HostX for MyImports {354fn new(&mut self) -> Resource<X> {355self.ctor_hit = true;356Resource::new_own(80)357}358359fn drop(&mut self, val: Resource<X>) -> Result<()> {360match self.drops {3610 => assert_eq!(val.rep(), 40),3621 => assert_eq!(val.rep(), 80),363_ => unreachable!(),364}365self.drops += 1;366Ok(())367}368}369370impl ResourcesImports for MyImports {}371372let mut linker = Linker::new(&engine);373Resources::add_to_linker::<_, HasSelf<_>>(&mut linker, |f| f)?;374let mut store = Store::new(&engine, MyImports::default());375let one_import = Resources::instantiate(&mut store, &component, &linker)?;376one_import.call_y(&mut store, Resource::new_own(40))?;377assert!(store.data().ctor_hit);378assert_eq!(store.data().drops, 2);379Ok(())380}381}382383mod resources_at_interface_level {384use super::*;385use wasmtime::component::{HasSelf, Resource};386387wasmtime::component::bindgen!({388inline: "389package foo:foo;390391interface def {392resource x {393constructor();394}395}396397interface user {398use def.{x};399400y: func(x: x);401}402403world resources {404export user;405}406",407});408409#[test]410fn run() -> Result<()> {411let engine = engine();412413let component = Component::new(414&engine,415r#"416(component417(import (interface "foo:foo/def") (instance $i418(export "x" (type $x (sub resource)))419(export "[constructor]x" (func (result (own $x))))420))421(alias export $i "x" (type $x))422(core func $dtor (canon resource.drop $x))423(core func $ctor (canon lower (func $i "[constructor]x")))424425(core module $m426(import "" "ctor" (func $ctor (result i32)))427(import "" "dtor" (func $dtor (param i32)))428429(func (export "x") (param i32)430(call $dtor (local.get 0))431(call $dtor (call $ctor))432)433)434(core instance $i (instantiate $m435(with "" (instance436(export "ctor" (func $ctor))437(export "dtor" (func $dtor))438))439))440(func $y (param "x" (own $x))441(canon lift (core func $i "x")))442443(instance (export (interface "foo:foo/user"))444(export "y" (func $y))445)446)447"#,448)?;449450#[derive(Default)]451struct MyImports {452ctor_hit: bool,453drops: usize,454}455456use foo::foo::def::X;457458impl foo::foo::def::HostX for MyImports {459fn new(&mut self) -> Resource<X> {460self.ctor_hit = true;461Resource::new_own(80)462}463464fn drop(&mut self, val: Resource<X>) -> Result<()> {465match self.drops {4660 => assert_eq!(val.rep(), 40),4671 => assert_eq!(val.rep(), 80),468_ => unreachable!(),469}470self.drops += 1;471Ok(())472}473}474475impl foo::foo::def::Host for MyImports {}476477let mut linker = Linker::new(&engine);478Resources::add_to_linker::<_, HasSelf<_>>(&mut linker, |f| f)?;479let mut store = Store::new(&engine, MyImports::default());480let one_import = Resources::instantiate(&mut store, &component, &linker)?;481one_import482.foo_foo_user()483.call_y(&mut store, Resource::new_own(40))?;484assert!(store.data().ctor_hit);485assert_eq!(store.data().drops, 2);486Ok(())487}488}489490mod async_config {491use super::*;492493wasmtime::component::bindgen!({494inline: "495package foo:foo;496497world t1 {498import foo: interface {499foo: func();500}501import x: func();502import y: func();503export z: func();504}505",506imports: { default: async },507exports: { default: async },508});509510#[expect(dead_code, reason = "just here for bindings")]511struct T;512513impl T1Imports for T {514async fn x(&mut self) {}515516async fn y(&mut self) {}517}518519async fn _test_t1(t1: &T1, store: &mut Store<()>) {520let _ = t1.call_z(&mut *store).await;521}522523wasmtime::component::bindgen!({524inline: "525package foo:foo;526527world t2 {528import x: func();529import y: func();530export z: func();531}532",533imports: {534"x": tracing,535default: async,536},537exports: { default: async },538});539540impl T2Imports for T {541fn x(&mut self) {}542543async fn y(&mut self) {}544}545546async fn _test_t2(t2: &T2, store: &mut Store<()>) {547let _ = t2.call_z(&mut *store).await;548}549550wasmtime::component::bindgen!({551inline: "552package foo:foo;553554world t3 {555import x: func();556import y: func();557export z: func();558}559",560imports: { "x": async },561exports: { default: async },562});563564impl T3Imports for T {565async fn x(&mut self) {}566567fn y(&mut self) {}568}569570async fn _test_t3(t3: &T3, store: &mut Store<()>) {571let _ = t3.call_z(&mut *store).await;572}573}574575mod exported_resources {576use super::*;577use std::mem;578use wasmtime::component::{HasSelf, Resource};579580wasmtime::component::bindgen!({581inline: "582package foo:foo;583584interface a {585resource x {586constructor();587}588}589590world resources {591export b: interface {592use a.{x as y};593594resource x {595constructor(y: y);596foo: func() -> u32;597}598}599600resource x;601602export f: func(x1: x, x2: x) -> x;603}604",605});606607#[derive(Default)]608struct MyImports {609hostcalls: Vec<Hostcall>,610next_a_x: u32,611}612613#[derive(PartialEq, Debug)]614enum Hostcall {615DropRootX(u32),616DropAX(u32),617NewA,618}619620use foo::foo::a;621622impl ResourcesImports for MyImports {}623624impl HostX for MyImports {625fn drop(&mut self, val: Resource<X>) -> Result<()> {626self.hostcalls.push(Hostcall::DropRootX(val.rep()));627Ok(())628}629}630631impl a::HostX for MyImports {632fn new(&mut self) -> Resource<a::X> {633let rep = self.next_a_x;634self.next_a_x += 1;635self.hostcalls.push(Hostcall::NewA);636Resource::new_own(rep)637}638639fn drop(&mut self, val: Resource<a::X>) -> Result<()> {640self.hostcalls.push(Hostcall::DropAX(val.rep()));641Ok(())642}643}644645impl foo::foo::a::Host for MyImports {}646647#[test]648fn run() -> Result<()> {649let engine = engine();650651let component = Component::new(652&engine,653r#"654(component655;; setup the `foo:foo/a` import656(import (interface "foo:foo/a") (instance $a657(export "x" (type $x (sub resource)))658(export "[constructor]x" (func (result (own $x))))659))660(alias export $a "x" (type $a-x))661(core func $a-x-drop (canon resource.drop $a-x))662(core func $a-x-ctor (canon lower (func $a "[constructor]x")))663664;; setup the root import of the `x` resource665(import "x" (type $x (sub resource)))666(core func $root-x-dtor (canon resource.drop $x))667668;; setup and declare the `x` resource for the `b` export.669(core module $indirect-dtor670(func (export "b-x-dtor") (param i32)671local.get 0672i32.const 0673call_indirect (param i32)674)675(table (export "$imports") 1 1 funcref)676)677(core instance $indirect-dtor (instantiate $indirect-dtor))678(type $b-x (resource (rep i32) (dtor (func $indirect-dtor "b-x-dtor"))))679(core func $b-x-drop (canon resource.drop $b-x))680(core func $b-x-rep (canon resource.rep $b-x))681(core func $b-x-new (canon resource.new $b-x))682683;; main module implementation684(core module $main685(import "foo:foo/a" "[constructor]x" (func $a-x-ctor (result i32)))686(import "foo:foo/a" "[resource-drop]x" (func $a-x-dtor (param i32)))687(import "$root" "[resource-drop]x" (func $x-dtor (param i32)))688(import "[export]b" "[resource-drop]x" (func $b-x-dtor (param i32)))689(import "[export]b" "[resource-new]x" (func $b-x-new (param i32) (result i32)))690(import "[export]b" "[resource-rep]x" (func $b-x-rep (param i32) (result i32)))691(func (export "b#[constructor]x") (param i32) (result i32)692(call $a-x-dtor (local.get 0))693(call $b-x-new (call $a-x-ctor))694)695(func (export "b#[method]x.foo") (param i32) (result i32)696local.get 0)697(func (export "b#[dtor]x") (param i32)698(call $a-x-dtor (local.get 0))699)700(func (export "f") (param i32 i32) (result i32)701(call $x-dtor (local.get 0))702local.get 1703)704)705(core instance $main (instantiate $main706(with "foo:foo/a" (instance707(export "[resource-drop]x" (func $a-x-drop))708(export "[constructor]x" (func $a-x-ctor))709))710(with "$root" (instance711(export "[resource-drop]x" (func $root-x-dtor))712))713(with "[export]b" (instance714(export "[resource-drop]x" (func $b-x-drop))715(export "[resource-rep]x" (func $b-x-rep))716(export "[resource-new]x" (func $b-x-new))717))718))719720;; fill in `$indirect-dtor`'s table with the actual destructor definition721;; now that it's available.722(core module $fixup723(import "" "b-x-dtor" (func $b-x-dtor (param i32)))724(import "" "$imports" (table 1 1 funcref))725(elem (i32.const 0) func $b-x-dtor)726)727(core instance (instantiate $fixup728(with "" (instance729(export "$imports" (table 0 "$imports"))730(export "b-x-dtor" (func $main "b#[dtor]x"))731))732))733734;; Create the `b` export through a subcomponent instantiation.735(func $b-x-ctor (param "y" (own $a-x)) (result (own $b-x))736(canon lift (core func $main "b#[constructor]x")))737(func $b-x-foo (param "self" (borrow $b-x)) (result u32)738(canon lift (core func $main "b#[method]x.foo")))739(component $b740(import "a-x" (type $y (sub resource)))741(import "b-x" (type $x' (sub resource)))742(import "ctor" (func $ctor (param "y" (own $y)) (result (own $x'))))743(import "foo" (func $foo (param "self" (borrow $x')) (result u32)))744(export $x "x" (type $x'))745(export "[constructor]x"746(func $ctor)747(func (param "y" (own $y)) (result (own $x))))748(export "[method]x.foo"749(func $foo)750(func (param "self" (borrow $x)) (result u32)))751)752(instance (export "b") (instantiate $b753(with "ctor" (func $b-x-ctor))754(with "foo" (func $b-x-foo))755(with "a-x" (type 0 "x"))756(with "b-x" (type $b-x))757))758759;; Create the `f` export which is a bare function760(func (export "f") (param "x1" (own $x)) (param "x2" (own $x)) (result (own $x))761(canon lift (core func $main "f")))762)763"#,764)?;765766let mut linker = Linker::new(&engine);767Resources::add_to_linker::<_, HasSelf<_>>(&mut linker, |f| f)?;768let mut store = Store::new(&engine, MyImports::default());769let i = Resources::instantiate(&mut store, &component, &linker)?;770771// call the root export `f` twice772let ret = i.call_f(&mut store, Resource::new_own(1), Resource::new_own(2))?;773assert_eq!(ret.rep(), 2);774assert_eq!(775mem::take(&mut store.data_mut().hostcalls),776[Hostcall::DropRootX(1)]777);778let ret = i.call_f(&mut store, Resource::new_own(3), Resource::new_own(4))?;779assert_eq!(ret.rep(), 4);780assert_eq!(781mem::take(&mut store.data_mut().hostcalls),782[Hostcall::DropRootX(3)]783);784785// interact with the `b` export786let b = i.b();787let b_x = b.x().call_constructor(&mut store, Resource::new_own(5))?;788assert_eq!(789mem::take(&mut store.data_mut().hostcalls),790[Hostcall::DropAX(5), Hostcall::NewA]791);792b.x().call_foo(&mut store, b_x)?;793assert_eq!(mem::take(&mut store.data_mut().hostcalls), []);794b_x.resource_drop(&mut store)?;795assert_eq!(796mem::take(&mut store.data_mut().hostcalls),797[Hostcall::DropAX(0)],798);799Ok(())800}801}802803mod unstable_import {804use super::*;805use wasmtime::component::HasSelf;806807wasmtime::component::bindgen!({808inline: "809package foo:foo;810811@unstable(feature = experimental-interface)812interface my-interface {813@unstable(feature = experimental-function)814my-function: func();815}816817world my-world {818@unstable(feature = experimental-import)819import my-interface;820821export bar: func();822}823",824});825826#[test]827fn run() -> Result<()> {828// In the example above, all features are required for `my-function` to be imported:829assert_success(830LinkOptions::default()831.experimental_interface(true)832.experimental_import(true)833.experimental_function(true),834);835836// And every other incomplete combination should fail:837assert_failure(&LinkOptions::default());838assert_failure(LinkOptions::default().experimental_function(true));839assert_failure(LinkOptions::default().experimental_interface(true));840assert_failure(841LinkOptions::default()842.experimental_interface(true)843.experimental_function(true),844);845assert_failure(846LinkOptions::default()847.experimental_interface(true)848.experimental_import(true),849);850assert_failure(LinkOptions::default().experimental_import(true));851assert_failure(852LinkOptions::default()853.experimental_import(true)854.experimental_function(true),855);856857Ok(())858}859860fn assert_success(link_options: &LinkOptions) {861run_with_options(link_options).unwrap();862}863fn assert_failure(link_options: &LinkOptions) {864let err = run_with_options(link_options).unwrap_err().to_string();865assert_eq!(866err,867"component imports instance `foo:foo/my-interface`, but a matching implementation was not found in the linker"868);869}870871fn run_with_options(link_options: &LinkOptions) -> Result<()> {872let engine = engine();873874let component = Component::new(875&engine,876r#"877(component878(import "foo:foo/my-interface" (instance $i879(export "my-function" (func))880))881(core module $m882(import "" "" (func))883(export "" (func 0))884)885(core func $f (canon lower (func $i "my-function")))886(core instance $r (instantiate $m887(with "" (instance (export "" (func $f))))888))889890(func $f (export "bar") (canon lift (core func $r "")))891)892"#,893)?;894895#[derive(Default)]896struct MyHost;897898impl foo::foo::my_interface::Host for MyHost {899fn my_function(&mut self) {}900}901902let mut linker = Linker::new(&engine);903MyWorld::add_to_linker::<_, HasSelf<_>>(&mut linker, link_options, |h| h)?;904let mut store = Store::new(&engine, MyHost::default());905let one_import = MyWorld::instantiate(&mut store, &component, &linker)?;906one_import.call_bar(&mut store)?;907Ok(())908}909}910911mod anyhow_errors {912use super::*;913use crate::ErrorExt;914use wasmtime::component::HasSelf;915use wasmtime::error::Context as _;916917wasmtime::component::bindgen!({918anyhow: true,919imports: { default: trappable },920inline: "921package foo:foo;922923interface my-interface {924ok: func() -> u32;925trap: func() -> u32;926}927928world my-world {929import my-interface;930export ok: func() -> u32;931export trap: func() -> u32;932}933",934});935936#[test]937fn run() -> Result<()> {938let engine = engine();939940let component = Component::new(941&engine,942r#"943(component944(import "foo:foo/my-interface" (instance $i945(export "ok" (func (result u32)))946(export "trap" (func (result u32)))947))948949(core module $m950(import "" "ok" (func (result i32)))951(import "" "trap" (func (result i32)))952(export "ok" (func 0))953(export "trap" (func 1))954)955956(core func $ok (canon lower (func $i "ok")))957(core func $trap (canon lower (func $i "trap")))958959(core instance $r (instantiate $m960(with "" (instance (export "ok" (func $ok))961(export "trap" (func $trap))))962))963964(func (export "ok") (result u32) (canon lift (core func $r "ok")))965(func (export "trap") (result u32) (canon lift (core func $r "trap")))966)967"#,968)?;969970#[derive(Default)]971struct MyHost;972973impl foo::foo::my_interface::Host for MyHost {974// NB: these must return an `anyhow::Result` since we `bindgen!`ed975// with `anyhow: true`.976fn ok(&mut self) -> anyhow_for_testing::Result<u32> {977Ok(42)978}979fn trap(&mut self) -> anyhow_for_testing::Result<u32> {980anyhow_for_testing::bail!("anyhow error")981}982}983984let mut linker = Linker::new(&engine);985MyWorld::add_to_linker::<_, HasSelf<_>>(&mut linker, |h| h)986.context("failed to add to linker")?;987let mut store = Store::new(&engine, MyHost::default());988let instance = MyWorld::instantiate(&mut store, &component, &linker)989.context("failed to instantiate")?;990991let x = instance992.call_ok(&mut store)993.context("failed to call `ok` function")?;994assert_eq!(x, 42);995996let result = instance.call_trap(&mut store);997let error = result.unwrap_err();998error.assert_contains("anyhow error");9991000Ok(())1001}1002}100310041005