Path: blob/main/tests/all/component_model/import.rs
1692 views
#![cfg(not(miri))]12use super::REALLOC_AND_FREE;3use anyhow::Result;4use std::ops::Deref;5use wasmtime::component::*;6use wasmtime::{Config, Engine, Store, StoreContextMut, Trap, WasmBacktrace};78#[test]9fn can_compile() -> Result<()> {10let engine = super::engine();11let libc = r#"12(core module $libc13(memory (export "memory") 1)14(func (export "realloc") (param i32 i32 i32 i32) (result i32)15unreachable)16)17(core instance $libc (instantiate $libc))18"#;19Component::new(20&engine,21r#"(component22(import "a" (func $f))23(core func (canon lower (func $f)))24)"#,25)?;26Component::new(27&engine,28format!(29r#"(component30(import "a" (func $f (param "a" string)))31{libc}32(core func (canon lower (func $f) (memory $libc "memory") (realloc (func $libc "realloc"))))33)"#34),35)?;36Component::new(37&engine,38format!(39r#"(component40(import "f1" (func $f1 (param "a" string) (result string)))41{libc}42(core func (canon lower (func $f1) (memory $libc "memory") (realloc (func $libc "realloc"))))4344(import "f2" (func $f2 (param "a" u32) (result (list u8))))45(core instance $libc2 (instantiate $libc))46(core func (canon lower (func $f2) (memory $libc2 "memory") (realloc (func $libc2 "realloc"))))4748(core func (canon lower (func $f1) (memory $libc2 "memory") (realloc (func $libc2 "realloc"))))49(core func (canon lower (func $f2) (memory $libc "memory") (realloc (func $libc "realloc"))))50)"#51),52)?;53Component::new(54&engine,55format!(56r#"(component57(import "log" (func $log (param "a" string)))58{libc}59(core func $log_lower (canon lower (func $log) (memory $libc "memory") (realloc (func $libc "realloc"))))6061(core module $logger62(import "host" "log" (func $log (param i32 i32)))63(import "libc" "memory" (memory 1))6465(func (export "call")66i32.const 067i32.const 068call $log)69)70(core instance $logger (instantiate $logger71(with "host" (instance (export "log" (func $log_lower))))72(with "libc" (instance $libc))73))7475(func (export "call")76(canon lift (core func $logger "call"))77)78)"#79),80)?;81Ok(())82}8384#[test]85fn simple() -> Result<()> {86let component = r#"87(component88(import "a" (func $log (param "a" string)))8990(core module $libc91(memory (export "memory") 1)9293(func (export "realloc") (param i32 i32 i32 i32) (result i32)94unreachable)95)96(core instance $libc (instantiate $libc))97(core func $log_lower98(canon lower (func $log) (memory $libc "memory") (realloc (func $libc "realloc")))99)100(core module $m101(import "libc" "memory" (memory 1))102(import "host" "log" (func $log (param i32 i32)))103104(func (export "call")105i32.const 5106i32.const 11107call $log)108109(data (i32.const 5) "hello world")110)111(core instance $i (instantiate $m112(with "libc" (instance $libc))113(with "host" (instance (export "log" (func $log_lower))))114))115(func (export "call")116(canon lift (core func $i "call"))117)118)119"#;120121let engine = super::engine();122let component = Component::new(&engine, component)?;123let mut store = Store::new(&engine, None);124assert!(store.data().is_none());125126// First, test the static API127128let mut linker = Linker::new(&engine);129linker.root().func_wrap(130"a",131|mut store: StoreContextMut<'_, Option<String>>, (arg,): (WasmStr,)| -> Result<_> {132let s = arg.to_str(&store)?.to_string();133assert!(store.data().is_none());134*store.data_mut() = Some(s);135Ok(())136},137)?;138let instance = linker.instantiate(&mut store, &component)?;139instance140.get_typed_func::<(), ()>(&mut store, "call")?141.call(&mut store, ())?;142assert_eq!(store.data().as_ref().unwrap(), "hello world");143144// Next, test the dynamic API145146*store.data_mut() = None;147let mut linker = Linker::new(&engine);148linker.root().func_new(149"a",150|mut store: StoreContextMut<'_, Option<String>>, args, _results| {151if let Val::String(s) = &args[0] {152assert!(store.data().is_none());153*store.data_mut() = Some(s.to_string());154Ok(())155} else {156panic!()157}158},159)?;160let instance = linker.instantiate(&mut store, &component)?;161instance162.get_func(&mut store, "call")163.unwrap()164.call(&mut store, &[], &mut [])?;165assert_eq!(store.data().as_ref().unwrap(), "hello world");166167Ok(())168}169170#[test]171fn functions_in_instances() -> Result<()> {172let component = r#"173(component174(type $import-type (instance175(export "a" (func (param "a" string)))176))177(import (interface "test:test/foo") (instance $import (type $import-type)))178(alias export $import "a" (func $log))179180(core module $libc181(memory (export "memory") 1)182183(func (export "realloc") (param i32 i32 i32 i32) (result i32)184unreachable)185)186(core instance $libc (instantiate $libc))187(core func $log_lower188(canon lower (func $log) (memory $libc "memory") (realloc (func $libc "realloc")))189)190(core module $m191(import "libc" "memory" (memory 1))192(import "host" "log" (func $log (param i32 i32)))193194(func (export "call")195i32.const 5196i32.const 11197call $log)198199(data (i32.const 5) "hello world")200)201(core instance $i (instantiate $m202(with "libc" (instance $libc))203(with "host" (instance (export "log" (func $log_lower))))204))205(func $call206(canon lift (core func $i "call"))207)208(component $c209(import "import-call" (func $f))210(export "call" (func $f))211)212(instance $export (instantiate $c213(with "import-call" (func $call))214))215(export (interface "test:test/foo") (instance $export))216)217"#;218219let engine = super::engine();220let component = Component::new(&engine, component)?;221let instance_index = component.get_export_index(None, "test:test/foo").unwrap();222let func_index = component223.get_export_index(Some(&instance_index), "call")224.unwrap();225let mut store = Store::new(&engine, None);226assert!(store.data().is_none());227228// First, test the static API229230let mut linker = Linker::new(&engine);231linker.instance("test:test/foo")?.func_wrap(232"a",233|mut store: StoreContextMut<'_, Option<String>>, (arg,): (WasmStr,)| -> Result<_> {234let s = arg.to_str(&store)?.to_string();235assert!(store.data().is_none());236*store.data_mut() = Some(s);237Ok(())238},239)?;240let instance = linker.instantiate(&mut store, &component)?;241let func = instance.get_typed_func::<(), ()>(&mut store, &func_index)?;242func.call(&mut store, ())?;243assert_eq!(store.data().as_ref().unwrap(), "hello world");244245// Next, test the dynamic API246247*store.data_mut() = None;248let mut linker = Linker::new(&engine);249linker.instance("test:test/foo")?.func_new(250"a",251|mut store: StoreContextMut<'_, Option<String>>, args, _results| {252if let Val::String(s) = &args[0] {253assert!(store.data().is_none());254*store.data_mut() = Some(s.to_string());255Ok(())256} else {257panic!()258}259},260)?;261let instance = linker.instantiate(&mut store, &component)?;262let func = instance.get_func(&mut store, func_index).unwrap();263func.call(&mut store, &[], &mut [])?;264assert_eq!(store.data().as_ref().unwrap(), "hello world");265266Ok(())267}268269#[test]270fn attempt_to_leave_during_malloc() -> Result<()> {271let component = r#"272(component273(import "thunk" (func $thunk))274(import "ret-string" (func $ret_string (result string)))275276(core module $host_shim277(table (export "table") 2 funcref)278(func $shim_thunk (export "thunk")279i32.const 0280call_indirect)281(func $shim_ret_string (export "ret-string") (param i32)282local.get 0283i32.const 1284call_indirect (param i32))285)286(core instance $host_shim (instantiate $host_shim))287288(core module $m289(import "host" "thunk" (func $thunk))290(import "host" "ret-string" (func $ret_string (param i32)))291292(memory (export "memory") 1)293294(func $realloc (export "realloc") (param i32 i32 i32 i32) (result i32)295call $thunk296unreachable)297298(func $run (export "run")299i32.const 8300call $ret_string)301302(func (export "take-string") (param i32 i32)303unreachable)304)305(core instance $m (instantiate $m (with "host" (instance $host_shim))))306307(core module $host_shim_filler_inner308(import "shim" "table" (table 2 funcref))309(import "host" "thunk" (func $thunk))310(import "host" "ret-string" (func $ret_string (param i32)))311(elem (i32.const 0) $thunk $ret_string)312)313314(core func $thunk_lower315(canon lower (func $thunk) (memory $m "memory") (realloc (func $m "realloc")))316)317318(core func $ret_string_lower319(canon lower (func $ret_string) (memory $m "memory") (realloc (func $m "realloc")))320)321322(core instance (instantiate $host_shim_filler_inner323(with "shim" (instance $host_shim))324(with "host" (instance325(export "thunk" (func $thunk_lower))326(export "ret-string" (func $ret_string_lower))327))328))329330(func (export "run")331(canon lift (core func $m "run"))332)333(func (export "take-string") (param "a" string)334(canon lift (core func $m "take-string") (memory $m "memory") (realloc (func $m "realloc")))335)336)337"#;338339let engine = super::engine();340let mut linker = Linker::new(&engine);341linker.root().func_wrap("thunk", |_, _: ()| -> Result<()> {342panic!("should not get here")343})?;344linker345.root()346.func_wrap("ret-string", |_, _: ()| -> Result<_> {347Ok(("hello".to_string(),))348})?;349let component = Component::new(&engine, component)?;350let mut store = Store::new(&engine, ());351352// Assert that during a host import if we return values to wasm that a trap353// happens if we try to leave the instance.354let trap = linker355.instantiate(&mut store, &component)?356.get_typed_func::<(), ()>(&mut store, "run")?357.call(&mut store, ())358.unwrap_err();359assert!(360format!("{trap:?}").contains("cannot leave component instance"),361"bad trap: {trap:?}",362);363364let trace = trap.downcast_ref::<WasmBacktrace>().unwrap().frames();365assert_eq!(trace.len(), 4);366367// This was our entry point...368assert_eq!(trace[3].module().name(), Some("m"));369assert_eq!(trace[3].func_name(), Some("run"));370371// ... which called an imported function which ends up being originally372// defined by the shim instance. The shim instance then does an indirect373// call through a table which goes to the `canon.lower`'d host function374assert_eq!(trace[2].module().name(), Some("host_shim"));375assert_eq!(trace[2].func_name(), Some("shim_ret_string"));376377// ... and the lowered host function will call realloc to allocate space for378// the result379assert_eq!(trace[1].module().name(), Some("m"));380assert_eq!(trace[1].func_name(), Some("realloc"));381382// ... but realloc calls the shim instance and tries to exit the383// component, triggering a dynamic trap384assert_eq!(trace[0].module().name(), Some("host_shim"));385assert_eq!(trace[0].func_name(), Some("shim_thunk"));386387// In addition to the above trap also ensure that when we enter a wasm388// component if we try to leave while lowering then that's also a dynamic389// trap.390let trap = linker391.instantiate(&mut store, &component)?392.get_typed_func::<(&str,), ()>(&mut store, "take-string")?393.call(&mut store, ("x",))394.unwrap_err();395assert!(396format!("{trap:?}").contains("cannot leave component instance"),397"bad trap: {trap:?}",398);399Ok(())400}401402#[test]403fn attempt_to_reenter_during_host() -> Result<()> {404let component = r#"405(component406(import "thunk" (func $thunk))407(core func $thunk_lower (canon lower (func $thunk)))408409(core module $m410(import "host" "thunk" (func $thunk))411412(func $run (export "run")413call $thunk)414)415(core instance $m (instantiate $m416(with "host" (instance (export "thunk" (func $thunk_lower))))417))418419(func (export "run")420(canon lift (core func $m "run"))421)422)423"#;424425let engine = super::engine();426let component = Component::new(&engine, component)?;427428// First, test the static API429430struct StaticState {431func: Option<TypedFunc<(), ()>>,432}433434let mut store = Store::new(&engine, StaticState { func: None });435let mut linker = Linker::new(&engine);436linker.root().func_wrap(437"thunk",438|mut store: StoreContextMut<'_, StaticState>, _: ()| -> Result<()> {439let func = store.data_mut().func.take().unwrap();440let trap = func.call(&mut store, ()).unwrap_err();441assert_eq!(442trap.downcast_ref(),443Some(&Trap::CannotEnterComponent),444"bad trap: {trap:?}",445);446Ok(())447},448)?;449let instance = linker.instantiate(&mut store, &component)?;450let func = instance.get_typed_func::<(), ()>(&mut store, "run")?;451store.data_mut().func = Some(func);452func.call(&mut store, ())?;453454// Next, test the dynamic API455456struct DynamicState {457func: Option<Func>,458}459460let mut store = Store::new(&engine, DynamicState { func: None });461let mut linker = Linker::new(&engine);462linker.root().func_new(463"thunk",464|mut store: StoreContextMut<'_, DynamicState>, _, _| {465let func = store.data_mut().func.take().unwrap();466let trap = func.call(&mut store, &[], &mut []).unwrap_err();467assert_eq!(468trap.downcast_ref(),469Some(&Trap::CannotEnterComponent),470"bad trap: {trap:?}",471);472Ok(())473},474)?;475let instance = linker.instantiate(&mut store, &component)?;476let func = instance.get_func(&mut store, "run").unwrap();477store.data_mut().func = Some(func);478func.call(&mut store, &[], &mut [])?;479480Ok(())481}482483#[tokio::test]484async fn stack_and_heap_args_and_rets() -> Result<()> {485test_stack_and_heap_args_and_rets(false).await486}487488#[tokio::test]489async fn stack_and_heap_args_and_rets_concurrent() -> Result<()> {490test_stack_and_heap_args_and_rets(true).await491}492493async fn test_stack_and_heap_args_and_rets(concurrent: bool) -> Result<()> {494let (body, async_lower_opts, async_lift_opts) = if concurrent {495(496r#"497(import "host" "f1" (func $f1 (param i32 i32) (result i32)))498(import "host" "f2" (func $f2 (param i32 i32) (result i32)))499(import "host" "f3" (func $f3 (param i32 i32) (result i32)))500(import "host" "f4" (func $f4 (param i32 i32) (result i32)))501502(func $run (export "run") (result i32)503(local $params i32)504(local $results i32)505506block507(local.set $results (call $realloc (i32.const 0) (i32.const 0) (i32.const 4) (i32.const 4)))508(call $f1 (i32.const 1) (local.get $results))509drop510(i32.load offset=0 (local.get $results))511i32.const 2512i32.eq513br_if 0514unreachable515end516517block518(local.set $params (call $allocate_empty_strings))519(local.set $results (call $realloc (i32.const 0) (i32.const 0) (i32.const 4) (i32.const 4)))520(call $f2 (local.get $params) (local.get $results))521drop522(i32.load offset=0 (local.get $results))523i32.const 3524i32.eq525br_if 0526unreachable527end528529block530(local.set $results (call $realloc (i32.const 0) (i32.const 0) (i32.const 4) (i32.const 8)))531(call $f3 (i32.const 8) (local.get $results))532drop533(call $validate_string_ret (local.get $results))534end535536block537(local.set $params (call $allocate_empty_strings))538(local.set $results (call $realloc (i32.const 0) (i32.const 0) (i32.const 4) (i32.const 8)))539(call $f4 (local.get $params) (local.get $results))540drop541(call $validate_string_ret (local.get $results))542end543544(call $task-return)545546i32.const 0547)548"#,549"async",550r#"async (callback (func $m "callback"))"#,551)552} else {553(554r#"555(import "host" "f1" (func $f1 (param i32) (result i32)))556(import "host" "f2" (func $f2 (param i32) (result i32)))557(import "host" "f3" (func $f3 (param i32 i32)))558(import "host" "f4" (func $f4 (param i32 i32)))559560(func $run (export "run")561block562i32.const 1563call $f1564i32.const 2565i32.eq566br_if 0567unreachable568end569570block571call $allocate_empty_strings572call $f2573i32.const 3574i32.eq575br_if 0576unreachable577end578579block580i32.const 8581i32.const 16000582call $f3583(call $validate_string_ret (i32.const 16000))584end585586block587call $allocate_empty_strings588i32.const 20000589call $f4590(call $validate_string_ret (i32.const 20000))591end592)593"#,594"",595"",596)597};598599let component = format!(600r#"601(component602(type $many_params (tuple603string string string string604string string string string605string))606(import "f1" (func $f1 (param "a" u32) (result u32)))607(import "f2" (func $f2 (param "a" $many_params) (result u32)))608(import "f3" (func $f3 (param "a" u32) (result string)))609(import "f4" (func $f4 (param "a" $many_params) (result string)))610611(core module $libc612{REALLOC_AND_FREE}613(memory (export "memory") 1)614)615(core instance $libc (instantiate (module $libc)))616617(core func $f1_lower (canon lower (func $f1)618(memory $libc "memory")619(realloc (func $libc "realloc"))620{async_lower_opts}621))622(core func $f2_lower (canon lower (func $f2)623(memory $libc "memory")624(realloc (func $libc "realloc"))625{async_lower_opts}626))627(core func $f3_lower (canon lower (func $f3)628(memory $libc "memory")629(realloc (func $libc "realloc"))630{async_lower_opts}631))632(core func $f4_lower (canon lower (func $f4)633(memory $libc "memory")634(realloc (func $libc "realloc"))635{async_lower_opts}636))637638(core module $m639(import "libc" "memory" (memory 1))640(import "libc" "realloc" (func $realloc (param i32 i32 i32 i32) (result i32)))641(import "host" "task.return" (func $task-return))642{body}643644(func (export "callback") (param i32 i32 i32) (result i32) unreachable)645646(func $allocate_empty_strings (result i32)647(local $ret i32)648(local $offset i32)649(local $cnt i32)650(local.set $ret (i32.const 8000))651(local.set $cnt (i32.const 9))652653loop654(call $setup_str (i32.add (local.get $ret) (local.get $offset)))655(local.set $offset (i32.add (local.get $offset) (i32.const 8)))656657(local.tee $cnt (i32.add (local.get $cnt) (i32.const -1)))658br_if 0659end660661local.get $ret662)663(func $setup_str (param $addr i32)664(i32.store offset=0 (local.get $addr) (i32.const 1000))665(i32.store offset=4 (local.get $addr) (i32.const 3))666)667668(func $validate_string_ret (param $addr i32)669(local $base i32)670(local $len i32)671(local.set $base (i32.load (local.get $addr)))672(local.set $len (i32.load offset=4 (local.get $addr)))673674block675local.get $len676i32.const 3677i32.eq678br_if 0679unreachable680end681682(i32.load8_u offset=0 (local.get $base))683i32.const 120 ;; 'x'684i32.ne685if unreachable end686687(i32.load8_u offset=1 (local.get $base))688i32.const 121 ;; 'y'689i32.ne690if unreachable end691692(i32.load8_u offset=2 (local.get $base))693i32.const 122 ;; 'z'694i32.ne695if unreachable end696)697698(data (i32.const 1000) "abc")699)700(core func $task-return (canon task.return))701(core instance $m (instantiate $m702(with "libc" (instance $libc))703(with "host" (instance704(export "f1" (func $f1_lower))705(export "f2" (func $f2_lower))706(export "f3" (func $f3_lower))707(export "f4" (func $f4_lower))708(export "task.return" (func $task-return))709))710))711712(func (export "run")713(canon lift (core func $m "run") {async_lift_opts})714)715)716"#717);718719let mut config = Config::new();720config.wasm_component_model_async(true);721config.async_support(true);722let engine = &Engine::new(&config)?;723let component = Component::new(&engine, component)?;724let mut store = Store::new(&engine, ());725726// First, test the static API727728let mut linker = Linker::new(&engine);729if concurrent {730linker731.root()732.func_wrap_concurrent("f1", |_, (x,): (u32,)| {733assert_eq!(x, 1);734Box::pin(async { Ok((2u32,)) })735})?;736linker.root().func_wrap_concurrent(737"f2",738|accessor,739(arg,): ((740WasmStr,741WasmStr,742WasmStr,743WasmStr,744WasmStr,745WasmStr,746WasmStr,747WasmStr,748WasmStr,749),)| {750accessor.with(|v| assert_eq!(arg.0.to_str(&v).unwrap(), "abc"));751Box::pin(async { Ok((3u32,)) })752},753)?;754linker755.root()756.func_wrap_concurrent("f3", |_, (arg,): (u32,)| {757assert_eq!(arg, 8);758Box::pin(async { Ok(("xyz".to_string(),)) })759})?;760linker.root().func_wrap_concurrent(761"f4",762|accessor,763(arg,): ((764WasmStr,765WasmStr,766WasmStr,767WasmStr,768WasmStr,769WasmStr,770WasmStr,771WasmStr,772WasmStr,773),)| {774accessor.with(|v| assert_eq!(arg.0.to_str(&v).unwrap(), "abc"));775Box::pin(async { Ok(("xyz".to_string(),)) })776},777)?;778} else {779linker780.root()781.func_wrap("f1", |_, (x,): (u32,)| -> Result<(u32,)> {782assert_eq!(x, 1);783Ok((2,))784})?;785linker.root().func_wrap(786"f2",787|cx: StoreContextMut<'_, ()>,788(arg,): ((789WasmStr,790WasmStr,791WasmStr,792WasmStr,793WasmStr,794WasmStr,795WasmStr,796WasmStr,797WasmStr,798),)|799-> Result<(u32,)> {800assert_eq!(arg.0.to_str(&cx).unwrap(), "abc");801Ok((3,))802},803)?;804linker805.root()806.func_wrap("f3", |_, (arg,): (u32,)| -> Result<(String,)> {807assert_eq!(arg, 8);808Ok(("xyz".to_string(),))809})?;810linker.root().func_wrap(811"f4",812|cx: StoreContextMut<'_, ()>,813(arg,): ((814WasmStr,815WasmStr,816WasmStr,817WasmStr,818WasmStr,819WasmStr,820WasmStr,821WasmStr,822WasmStr,823),)|824-> Result<(String,)> {825assert_eq!(arg.0.to_str(&cx).unwrap(), "abc");826Ok(("xyz".to_string(),))827},828)?;829}830831let instance = linker.instantiate_async(&mut store, &component).await?;832let run = instance.get_typed_func::<(), ()>(&mut store, "run")?;833834if concurrent {835instance836.run_concurrent(&mut store, async move |accessor| {837run.call_concurrent(accessor, ()).await838})839.await??;840} else {841run.call_async(&mut store, ()).await?;842}843844// Next, test the dynamic API845846let mut linker = Linker::new(&engine);847if concurrent {848linker849.root()850.func_new_concurrent("f1", |_, args, results| {851if let Val::U32(x) = &args[0] {852assert_eq!(*x, 1);853Box::pin(async {854results[0] = Val::U32(2);855Ok(())856})857} else {858panic!()859}860})?;861linker862.root()863.func_new_concurrent("f2", |_, args, results| {864if let Val::Tuple(tuple) = &args[0] {865if let Val::String(s) = &tuple[0] {866assert_eq!(s.deref(), "abc");867Box::pin(async {868results[0] = Val::U32(3);869Ok(())870})871} else {872panic!()873}874} else {875panic!()876}877})?;878linker879.root()880.func_new_concurrent("f3", |_, args, results| {881if let Val::U32(x) = &args[0] {882assert_eq!(*x, 8);883Box::pin(async {884results[0] = Val::String("xyz".into());885Ok(())886})887} else {888panic!();889}890})?;891linker892.root()893.func_new_concurrent("f4", |_, args, results| {894if let Val::Tuple(tuple) = &args[0] {895if let Val::String(s) = &tuple[0] {896assert_eq!(s.deref(), "abc");897Box::pin(async {898results[0] = Val::String("xyz".into());899Ok(())900})901} else {902panic!()903}904} else {905panic!()906}907})?;908} else {909linker.root().func_new("f1", |_, args, results| {910if let Val::U32(x) = &args[0] {911assert_eq!(*x, 1);912results[0] = Val::U32(2);913Ok(())914} else {915panic!()916}917})?;918linker.root().func_new("f2", |_, args, results| {919if let Val::Tuple(tuple) = &args[0] {920if let Val::String(s) = &tuple[0] {921assert_eq!(s.deref(), "abc");922results[0] = Val::U32(3);923Ok(())924} else {925panic!()926}927} else {928panic!()929}930})?;931linker.root().func_new("f3", |_, args, results| {932if let Val::U32(x) = &args[0] {933assert_eq!(*x, 8);934results[0] = Val::String("xyz".into());935Ok(())936} else {937panic!();938}939})?;940linker.root().func_new("f4", |_, args, results| {941if let Val::Tuple(tuple) = &args[0] {942if let Val::String(s) = &tuple[0] {943assert_eq!(s.deref(), "abc");944results[0] = Val::String("xyz".into());945Ok(())946} else {947panic!()948}949} else {950panic!()951}952})?;953}954955let instance = linker.instantiate_async(&mut store, &component).await?;956let run = instance.get_func(&mut store, "run").unwrap();957958if concurrent {959instance960.run_concurrent(&mut store, async |store| {961run.call_concurrent(store, &[], &mut []).await962})963.await??;964} else {965run.call_async(&mut store, &[], &mut []).await?;966}967968Ok(())969}970971#[test]972fn bad_import_alignment() -> Result<()> {973let component = format!(974r#"975(component976(import "unaligned-retptr" (func $unaligned_retptr (result string)))977(type $many_arg (tuple978string string string string979string string string string980string981))982(import "unaligned-argptr" (func $unaligned_argptr (param "a" $many_arg)))983(core module $libc_panic984(memory (export "memory") 1)985(func (export "realloc") (param i32 i32 i32 i32) (result i32)986unreachable)987)988(core instance $libc_panic (instantiate $libc_panic))989990(core func $unaligned_retptr_lower991(canon lower (func $unaligned_retptr) (memory $libc_panic "memory") (realloc (func $libc_panic "realloc")))992)993(core func $unaligned_argptr_lower994(canon lower (func $unaligned_argptr) (memory $libc_panic "memory") (realloc (func $libc_panic "realloc")))995)996997(core module $m998(import "host" "unaligned-retptr" (func $unaligned_retptr (param i32)))999(import "host" "unaligned-argptr" (func $unaligned_argptr (param i32)))10001001(func (export "unaligned-retptr")1002(call $unaligned_retptr (i32.const 1)))1003(func (export "unaligned-argptr")1004(call $unaligned_argptr (i32.const 1)))1005)1006(core instance $m (instantiate $m1007(with "host" (instance1008(export "unaligned-retptr" (func $unaligned_retptr_lower))1009(export "unaligned-argptr" (func $unaligned_argptr_lower))1010))1011))10121013(func (export "unaligned-retptr2")1014(canon lift (core func $m "unaligned-retptr"))1015)1016(func (export "unaligned-argptr2")1017(canon lift (core func $m "unaligned-argptr"))1018)1019)1020"#1021);10221023let engine = super::engine();1024let mut linker = Linker::new(&engine);1025linker1026.root()1027.func_wrap("unaligned-retptr", |_, _: ()| -> Result<(String,)> {1028Ok((String::new(),))1029})?;1030linker.root().func_wrap(1031"unaligned-argptr",1032|_,1033_: ((1034WasmStr,1035WasmStr,1036WasmStr,1037WasmStr,1038WasmStr,1039WasmStr,1040WasmStr,1041WasmStr,1042WasmStr,1043),)|1044-> Result<()> { unreachable!() },1045)?;1046let component = Component::new(&engine, component)?;1047let mut store = Store::new(&engine, ());10481049let trap = linker1050.instantiate(&mut store, &component)?1051.get_typed_func::<(), ()>(&mut store, "unaligned-retptr2")?1052.call(&mut store, ())1053.unwrap_err();1054assert!(1055format!("{trap:?}").contains("pointer not aligned"),1056"{}",1057trap1058);1059let trap = linker1060.instantiate(&mut store, &component)?1061.get_typed_func::<(), ()>(&mut store, "unaligned-argptr2")?1062.call(&mut store, ())1063.unwrap_err();1064assert!(1065format!("{trap:?}").contains("pointer not aligned"),1066"{}",1067trap1068);10691070Ok(())1071}10721073#[test]1074fn no_actual_wasm_code() -> Result<()> {1075let component = r#"1076(component1077(import "f" (func $f))10781079(core func $f_lower1080(canon lower (func $f))1081)1082(core module $m1083(import "" "" (func $f))1084(export "f" (func $f))1085)1086(core instance $i (instantiate $m1087(with "" (instance1088(export "" (func $f_lower))1089))1090))1091(func (export "thunk")1092(canon lift1093(core func $i "f")1094)1095)1096)1097"#;10981099let engine = super::engine();1100let component = Component::new(&engine, component)?;1101let mut store = Store::new(&engine, 0);11021103// First, test the static API11041105let mut linker = Linker::new(&engine);1106linker.root().func_wrap(1107"f",1108|mut store: StoreContextMut<'_, u32>, _: ()| -> Result<()> {1109*store.data_mut() += 1;1110Ok(())1111},1112)?;11131114let instance = linker.instantiate(&mut store, &component)?;1115let thunk = instance.get_typed_func::<(), ()>(&mut store, "thunk")?;11161117assert_eq!(*store.data(), 0);1118thunk.call(&mut store, ())?;1119assert_eq!(*store.data(), 1);11201121// Next, test the dynamic API11221123*store.data_mut() = 0;1124let mut linker = Linker::new(&engine);1125linker1126.root()1127.func_new("f", |mut store: StoreContextMut<'_, u32>, _, _| {1128*store.data_mut() += 1;1129Ok(())1130})?;11311132let instance = linker.instantiate(&mut store, &component)?;1133let thunk = instance.get_func(&mut store, "thunk").unwrap();11341135assert_eq!(*store.data(), 0);1136thunk.call(&mut store, &[], &mut [])?;1137assert_eq!(*store.data(), 1);11381139Ok(())1140}11411142#[test]1143fn use_types_across_component_boundaries() -> Result<()> {1144// Create a component that exports a function that returns a record1145let engine = super::engine();1146let component = Component::new(1147&engine,1148r#"(component1149(type (;0;) (record (field "a" u8) (field "b" string)))1150(import "my-record" (type $my-record (eq 0)))1151(core module $m1152(memory $memory 17)1153(export "memory" (memory $memory))1154(func (export "my-func") (result i32)1155i32.const 41156return))1157(core instance $instance (instantiate $m))1158(type $func-type (func (result $my-record)))1159(alias core export $instance "my-func" (core func $my-func))1160(alias core export $instance "memory" (core memory $memory))1161(func $my-func (type $func-type) (canon lift (core func $my-func) (memory $memory) string-encoding=utf8))1162(export $export "my-func" (func $my-func))1163)"#,1164)?;1165let mut store = Store::new(&engine, 0);1166let linker = Linker::new(&engine);1167let instance = linker.instantiate(&mut store, &component)?;1168let my_func = instance.get_func(&mut store, "my-func").unwrap();1169let mut results = vec![Val::Bool(false)];1170my_func.call(&mut store, &[], &mut results)?;11711172// Create another component that exports a function that takes that record as an argument1173let component = Component::new(1174&engine,1175format!(1176r#"(component1177(type (;0;) (record (field "a" u8) (field "b" string)))1178(import "my-record" (type $my-record (eq 0)))1179(core module $m1180(memory $memory 17)1181(export "memory" (memory $memory))1182{REALLOC_AND_FREE}1183(func (export "my-func") (param i32 i32 i32)))1184(core instance $instance (instantiate $m))1185(type $func-type (func (param "my-record" $my-record)))1186(alias core export $instance "my-func" (core func $my-func))1187(alias core export $instance "memory" (core memory $memory))1188(func $my-func (type $func-type) (canon lift (core func $my-func) (memory $memory) string-encoding=utf8 (realloc (func $instance "realloc"))))1189(export $export "my-func" (func $my-func))1190)"#1191),1192)?;1193let mut store = Store::new(&engine, 0);1194let linker = Linker::new(&engine);1195let instance = linker.instantiate(&mut store, &component)?;1196let my_func = instance.get_func(&mut store, "my-func").unwrap();1197// Call the exported function with the return values of the call to the previous component's exported function1198my_func.call(&mut store, &results, &mut [])?;11991200Ok(())1201}120212031204