Path: blob/main/tests/all/component_model/func.rs
3054 views
#![cfg(not(miri))]12use super::{ApiStyle, REALLOC_AND_FREE};3use std::sync::Arc;4use wasmtime::Result;5use wasmtime::component::*;6use wasmtime::{Config, Engine, Store, StoreContextMut, Trap};78const CANON_32BIT_NAN: u32 = 0b01111111110000000000000000000000;9const CANON_64BIT_NAN: u64 = 0b0111111111111000000000000000000000000000000000000000000000000000;1011#[test]12fn thunks() -> Result<()> {13let component = r#"14(component15(core module $m16(func (export "thunk"))17(func (export "thunk-trap") unreachable)18)19(core instance $i (instantiate $m))20(func (export "thunk")21(canon lift (core func $i "thunk"))22)23(func (export "thunk-trap")24(canon lift (core func $i "thunk-trap"))25)26)27"#;2829let engine = super::engine();30let component = Component::new(&engine, component)?;31let mut store = Store::new(&engine, ());32let instance = Linker::new(&engine).instantiate(&mut store, &component)?;33instance34.get_typed_func::<(), ()>(&mut store, "thunk")?35.call(&mut store, ())?;36let err = instance37.get_typed_func::<(), ()>(&mut store, "thunk-trap")?38.call(&mut store, ())39.unwrap_err();40assert_eq!(err.downcast::<Trap>()?, Trap::UnreachableCodeReached);4142Ok(())43}4445#[test]46fn typecheck() -> Result<()> {47let component = r#"48(component49(core module $m50(func (export "thunk"))51(func (export "take-string") (param i32 i32))52(func (export "two-args") (param i32 i32 i32))53(func (export "ret-one") (result i32) unreachable)5455(memory (export "memory") 1)56(func (export "realloc") (param i32 i32 i32 i32) (result i32)57unreachable)58)59(core instance $i (instantiate (module $m)))60(func (export "thunk")61(canon lift (core func $i "thunk"))62)63(func (export "take-string") (param "a" string)64(canon lift (core func $i "take-string") (memory $i "memory") (realloc (func $i "realloc")))65)66(func (export "take-two-args") (param "a" s32) (param "b" (list u8))67(canon lift (core func $i "two-args") (memory $i "memory") (realloc (func $i "realloc")))68)69(func (export "ret-tuple") (result (tuple u8 s8))70(canon lift (core func $i "ret-one") (memory $i "memory") (realloc (func $i "realloc")))71)72(func (export "ret-tuple1") (result (tuple u32))73(canon lift (core func $i "ret-one") (memory $i "memory") (realloc (func $i "realloc")))74)75(func (export "ret-string") (result string)76(canon lift (core func $i "ret-one") (memory $i "memory") (realloc (func $i "realloc")))77)78(func (export "ret-list-u8") (result (list u8))79(canon lift (core func $i "ret-one") (memory $i "memory") (realloc (func $i "realloc")))80)81)82"#;8384let engine = Engine::default();85let component = Component::new(&engine, component)?;86let mut store = Store::new(&engine, ());87let instance = Linker::new(&engine).instantiate(&mut store, &component)?;88let thunk = instance.get_func(&mut store, "thunk").unwrap();89let take_string = instance.get_func(&mut store, "take-string").unwrap();90let take_two_args = instance.get_func(&mut store, "take-two-args").unwrap();91let ret_tuple = instance.get_func(&mut store, "ret-tuple").unwrap();92let ret_tuple1 = instance.get_func(&mut store, "ret-tuple1").unwrap();93let ret_string = instance.get_func(&mut store, "ret-string").unwrap();94let ret_list_u8 = instance.get_func(&mut store, "ret-list-u8").unwrap();95assert!(thunk.typed::<(), (u32,)>(&store).is_err());96assert!(thunk.typed::<(u32,), ()>(&store).is_err());97assert!(thunk.typed::<(), ()>(&store).is_ok());98assert!(take_string.typed::<(), ()>(&store).is_err());99assert!(take_string.typed::<(String,), ()>(&store).is_ok());100assert!(take_string.typed::<(&str,), ()>(&store).is_ok());101assert!(take_string.typed::<(&[u8],), ()>(&store).is_err());102assert!(take_two_args.typed::<(), ()>(&store).is_err());103assert!(take_two_args.typed::<(i32, &[u8]), (u32,)>(&store).is_err());104assert!(take_two_args.typed::<(u32, &[u8]), ()>(&store).is_err());105assert!(take_two_args.typed::<(i32, &[u8]), ()>(&store).is_ok());106assert!(ret_tuple.typed::<(), ()>(&store).is_err());107assert!(ret_tuple.typed::<(), (u8,)>(&store).is_err());108assert!(ret_tuple.typed::<(), ((u8, i8),)>(&store).is_ok());109assert!(ret_tuple1.typed::<(), ((u32,),)>(&store).is_ok());110assert!(ret_tuple1.typed::<(), (u32,)>(&store).is_err());111assert!(ret_string.typed::<(), ()>(&store).is_err());112assert!(ret_string.typed::<(), (WasmStr,)>(&store).is_ok());113assert!(ret_list_u8.typed::<(), (WasmList<u16>,)>(&store).is_err());114assert!(ret_list_u8.typed::<(), (WasmList<i8>,)>(&store).is_err());115assert!(ret_list_u8.typed::<(), (WasmList<u8>,)>(&store).is_ok());116117Ok(())118}119120#[test]121fn integers() -> Result<()> {122let component = r#"123(component124(core module $m125(func (export "take-i32-100") (param i32)126local.get 0127i32.const 100128i32.eq129br_if 0130unreachable131)132(func (export "take-i64-100") (param i64)133local.get 0134i64.const 100135i64.eq136br_if 0137unreachable138)139(func (export "ret-i32-0") (result i32) i32.const 0)140(func (export "ret-i64-0") (result i64) i64.const 0)141(func (export "ret-i32-minus-1") (result i32) i32.const -1)142(func (export "ret-i64-minus-1") (result i64) i64.const -1)143(func (export "ret-i32-100000") (result i32) i32.const 100000)144)145(core instance $i (instantiate (module $m)))146(func (export "take-u8") (param "a" u8) (canon lift (core func $i "take-i32-100")))147(func (export "take-s8") (param "a" s8) (canon lift (core func $i "take-i32-100")))148(func (export "take-u16") (param "a" u16) (canon lift (core func $i "take-i32-100")))149(func (export "take-s16") (param "a" s16) (canon lift (core func $i "take-i32-100")))150(func (export "take-u32") (param "a" u32) (canon lift (core func $i "take-i32-100")))151(func (export "take-s32") (param "a" s32) (canon lift (core func $i "take-i32-100")))152(func (export "take-u64") (param "a" u64) (canon lift (core func $i "take-i64-100")))153(func (export "take-s64") (param "a" s64) (canon lift (core func $i "take-i64-100")))154155(func (export "ret-u8") (result u8) (canon lift (core func $i "ret-i32-0")))156(func (export "ret-s8") (result s8) (canon lift (core func $i "ret-i32-0")))157(func (export "ret-u16") (result u16) (canon lift (core func $i "ret-i32-0")))158(func (export "ret-s16") (result s16) (canon lift (core func $i "ret-i32-0")))159(func (export "ret-u32") (result u32) (canon lift (core func $i "ret-i32-0")))160(func (export "ret-s32") (result s32) (canon lift (core func $i "ret-i32-0")))161(func (export "ret-u64") (result u64) (canon lift (core func $i "ret-i64-0")))162(func (export "ret-s64") (result s64) (canon lift (core func $i "ret-i64-0")))163164(func (export "retm1-u8") (result u8) (canon lift (core func $i "ret-i32-minus-1")))165(func (export "retm1-s8") (result s8) (canon lift (core func $i "ret-i32-minus-1")))166(func (export "retm1-u16") (result u16) (canon lift (core func $i "ret-i32-minus-1")))167(func (export "retm1-s16") (result s16) (canon lift (core func $i "ret-i32-minus-1")))168(func (export "retm1-u32") (result u32) (canon lift (core func $i "ret-i32-minus-1")))169(func (export "retm1-s32") (result s32) (canon lift (core func $i "ret-i32-minus-1")))170(func (export "retm1-u64") (result u64) (canon lift (core func $i "ret-i64-minus-1")))171(func (export "retm1-s64") (result s64) (canon lift (core func $i "ret-i64-minus-1")))172173(func (export "retbig-u8") (result u8) (canon lift (core func $i "ret-i32-100000")))174(func (export "retbig-s8") (result s8) (canon lift (core func $i "ret-i32-100000")))175(func (export "retbig-u16") (result u16) (canon lift (core func $i "ret-i32-100000")))176(func (export "retbig-s16") (result s16) (canon lift (core func $i "ret-i32-100000")))177(func (export "retbig-u32") (result u32) (canon lift (core func $i "ret-i32-100000")))178(func (export "retbig-s32") (result s32) (canon lift (core func $i "ret-i32-100000")))179)180"#;181182let engine = super::engine();183let component = Component::new(&engine, component)?;184let mut store = Store::new(&engine, ());185let instance = Linker::new(&engine).instantiate(&mut store, &component)?;186187// Passing in 100 is valid for all primitives188instance189.get_typed_func::<(u8,), ()>(&mut store, "take-u8")?190.call(&mut store, (100,))?;191instance192.get_typed_func::<(i8,), ()>(&mut store, "take-s8")?193.call(&mut store, (100,))?;194instance195.get_typed_func::<(u16,), ()>(&mut store, "take-u16")?196.call(&mut store, (100,))?;197instance198.get_typed_func::<(i16,), ()>(&mut store, "take-s16")?199.call(&mut store, (100,))?;200instance201.get_typed_func::<(u32,), ()>(&mut store, "take-u32")?202.call(&mut store, (100,))?;203instance204.get_typed_func::<(i32,), ()>(&mut store, "take-s32")?205.call(&mut store, (100,))?;206instance207.get_typed_func::<(u64,), ()>(&mut store, "take-u64")?208.call(&mut store, (100,))?;209instance210.get_typed_func::<(i64,), ()>(&mut store, "take-s64")?211.call(&mut store, (100,))?;212213// This specific wasm instance traps if any value other than 100 is passed214with_new_instance(&engine, &component, |store, instance| {215instance216.get_typed_func::<(u8,), ()>(&mut *store, "take-u8")?217.call(store, (101,))218.unwrap_err()219.downcast::<Trap>()220})?;221with_new_instance(&engine, &component, |store, instance| {222instance223.get_typed_func::<(i8,), ()>(&mut *store, "take-s8")?224.call(store, (101,))225.unwrap_err()226.downcast::<Trap>()227})?;228with_new_instance(&engine, &component, |store, instance| {229instance230.get_typed_func::<(u16,), ()>(&mut *store, "take-u16")?231.call(store, (101,))232.unwrap_err()233.downcast::<Trap>()234})?;235with_new_instance(&engine, &component, |store, instance| {236instance237.get_typed_func::<(i16,), ()>(&mut *store, "take-s16")?238.call(store, (101,))239.unwrap_err()240.downcast::<Trap>()241})?;242with_new_instance(&engine, &component, |store, instance| {243instance244.get_typed_func::<(u32,), ()>(&mut *store, "take-u32")?245.call(store, (101,))246.unwrap_err()247.downcast::<Trap>()248})?;249with_new_instance(&engine, &component, |store, instance| {250instance251.get_typed_func::<(i32,), ()>(&mut *store, "take-s32")?252.call(store, (101,))253.unwrap_err()254.downcast::<Trap>()255})?;256with_new_instance(&engine, &component, |store, instance| {257instance258.get_typed_func::<(u64,), ()>(&mut *store, "take-u64")?259.call(store, (101,))260.unwrap_err()261.downcast::<Trap>()262})?;263with_new_instance(&engine, &component, |store, instance| {264instance265.get_typed_func::<(i64,), ()>(&mut *store, "take-s64")?266.call(store, (101,))267.unwrap_err()268.downcast::<Trap>()269})?;270271// Zero can be returned as any integer272assert_eq!(273instance274.get_typed_func::<(), (u8,)>(&mut store, "ret-u8")?275.call(&mut store, ())?,276(0,)277);278assert_eq!(279instance280.get_typed_func::<(), (i8,)>(&mut store, "ret-s8")?281.call(&mut store, ())?,282(0,)283);284assert_eq!(285instance286.get_typed_func::<(), (u16,)>(&mut store, "ret-u16")?287.call(&mut store, ())?,288(0,)289);290assert_eq!(291instance292.get_typed_func::<(), (i16,)>(&mut store, "ret-s16")?293.call(&mut store, ())?,294(0,)295);296assert_eq!(297instance298.get_typed_func::<(), (u32,)>(&mut store, "ret-u32")?299.call(&mut store, ())?,300(0,)301);302assert_eq!(303instance304.get_typed_func::<(), (i32,)>(&mut store, "ret-s32")?305.call(&mut store, ())?,306(0,)307);308assert_eq!(309instance310.get_typed_func::<(), (u64,)>(&mut store, "ret-u64")?311.call(&mut store, ())?,312(0,)313);314assert_eq!(315instance316.get_typed_func::<(), (i64,)>(&mut store, "ret-s64")?317.call(&mut store, ())?,318(0,)319);320321// Returning -1 should reinterpret the bytes as defined by each type.322assert_eq!(323instance324.get_typed_func::<(), (u8,)>(&mut store, "retm1-u8")?325.call(&mut store, ())?,326(0xff,)327);328assert_eq!(329instance330.get_typed_func::<(), (i8,)>(&mut store, "retm1-s8")?331.call(&mut store, ())?,332(-1,)333);334assert_eq!(335instance336.get_typed_func::<(), (u16,)>(&mut store, "retm1-u16")?337.call(&mut store, ())?,338(0xffff,)339);340assert_eq!(341instance342.get_typed_func::<(), (i16,)>(&mut store, "retm1-s16")?343.call(&mut store, ())?,344(-1,)345);346assert_eq!(347instance348.get_typed_func::<(), (u32,)>(&mut store, "retm1-u32")?349.call(&mut store, ())?,350(0xffffffff,)351);352assert_eq!(353instance354.get_typed_func::<(), (i32,)>(&mut store, "retm1-s32")?355.call(&mut store, ())?,356(-1,)357);358assert_eq!(359instance360.get_typed_func::<(), (u64,)>(&mut store, "retm1-u64")?361.call(&mut store, ())?,362(0xffffffff_ffffffff,)363);364assert_eq!(365instance366.get_typed_func::<(), (i64,)>(&mut store, "retm1-s64")?367.call(&mut store, ())?,368(-1,)369);370371// Returning 100000 should chop off bytes as necessary372let ret: u32 = 100000;373assert_eq!(374instance375.get_typed_func::<(), (u8,)>(&mut store, "retbig-u8")?376.call(&mut store, ())?,377(ret as u8,),378);379assert_eq!(380instance381.get_typed_func::<(), (i8,)>(&mut store, "retbig-s8")?382.call(&mut store, ())?,383(ret as i8,),384);385assert_eq!(386instance387.get_typed_func::<(), (u16,)>(&mut store, "retbig-u16")?388.call(&mut store, ())?,389(ret as u16,),390);391assert_eq!(392instance393.get_typed_func::<(), (i16,)>(&mut store, "retbig-s16")?394.call(&mut store, ())?,395(ret as i16,),396);397assert_eq!(398instance399.get_typed_func::<(), (u32,)>(&mut store, "retbig-u32")?400.call(&mut store, ())?,401(ret,),402);403assert_eq!(404instance405.get_typed_func::<(), (i32,)>(&mut store, "retbig-s32")?406.call(&mut store, ())?,407(ret as i32,),408);409410Ok(())411}412413#[test]414fn type_layers() -> Result<()> {415let component = r#"416(component417(core module $m418(func (export "take-i32-100") (param i32)419local.get 0420i32.const 2421i32.eq422br_if 0423unreachable424)425)426(core instance $i (instantiate $m))427(func (export "take-u32") (param "a" u32) (canon lift (core func $i "take-i32-100")))428)429"#;430431let engine = super::engine();432let component = Component::new(&engine, component)?;433let mut store = Store::new(&engine, ());434let instance = Linker::new(&engine).instantiate(&mut store, &component)?;435436instance437.get_typed_func::<(Box<u32>,), ()>(&mut store, "take-u32")?438.call(&mut store, (Box::new(2),))?;439instance440.get_typed_func::<(&u32,), ()>(&mut store, "take-u32")?441.call(&mut store, (&2,))?;442instance443.get_typed_func::<(Arc<u32>,), ()>(&mut store, "take-u32")?444.call(&mut store, (Arc::new(2),))?;445instance446.get_typed_func::<(&Box<Arc<Box<u32>>>,), ()>(&mut store, "take-u32")?447.call(&mut store, (&Box::new(Arc::new(Box::new(2))),))?;448449Ok(())450}451452#[test]453fn floats() -> Result<()> {454let component = r#"455(component456(core module $m457(func (export "i32.reinterpret_f32") (param f32) (result i32)458local.get 0459i32.reinterpret_f32460)461(func (export "i64.reinterpret_f64") (param f64) (result i64)462local.get 0463i64.reinterpret_f64464)465(func (export "f32.reinterpret_i32") (param i32) (result f32)466local.get 0467f32.reinterpret_i32468)469(func (export "f64.reinterpret_i64") (param i64) (result f64)470local.get 0471f64.reinterpret_i64472)473)474(core instance $i (instantiate $m))475476(func (export "f32-to-u32") (param "a" float32) (result u32)477(canon lift (core func $i "i32.reinterpret_f32"))478)479(func (export "f64-to-u64") (param "a" float64) (result u64)480(canon lift (core func $i "i64.reinterpret_f64"))481)482(func (export "u32-to-f32") (param "a" u32) (result float32)483(canon lift (core func $i "f32.reinterpret_i32"))484)485(func (export "u64-to-f64") (param "a" u64) (result float64)486(canon lift (core func $i "f64.reinterpret_i64"))487)488)489"#;490491let engine = super::engine();492let component = Component::new(&engine, component)?;493let mut store = Store::new(&engine, ());494let instance = Linker::new(&engine).instantiate(&mut store, &component)?;495let f32_to_u32 = instance.get_typed_func::<(f32,), (u32,)>(&mut store, "f32-to-u32")?;496let f64_to_u64 = instance.get_typed_func::<(f64,), (u64,)>(&mut store, "f64-to-u64")?;497let u32_to_f32 = instance.get_typed_func::<(u32,), (f32,)>(&mut store, "u32-to-f32")?;498let u64_to_f64 = instance.get_typed_func::<(u64,), (f64,)>(&mut store, "u64-to-f64")?;499500assert_eq!(f32_to_u32.call(&mut store, (1.0,))?, (1.0f32.to_bits(),));501assert_eq!(f64_to_u64.call(&mut store, (2.0,))?, (2.0f64.to_bits(),));502assert_eq!(u32_to_f32.call(&mut store, (3.0f32.to_bits(),))?, (3.0,));503assert_eq!(u64_to_f64.call(&mut store, (4.0f64.to_bits(),))?, (4.0,));504505assert_eq!(506u32_to_f32507.call(&mut store, (CANON_32BIT_NAN | 1,))?508.0509.to_bits(),510CANON_32BIT_NAN | 1511);512assert_eq!(513u64_to_f64514.call(&mut store, (CANON_64BIT_NAN | 1,))?515.0516.to_bits(),517CANON_64BIT_NAN | 1,518);519520assert_eq!(521f32_to_u32.call(&mut store, (f32::from_bits(CANON_32BIT_NAN | 1),))?,522(CANON_32BIT_NAN | 1,)523);524assert_eq!(525f64_to_u64.call(&mut store, (f64::from_bits(CANON_64BIT_NAN | 1),))?,526(CANON_64BIT_NAN | 1,)527);528529Ok(())530}531532#[test]533fn bools() -> Result<()> {534let component = r#"535(component536(core module $m537(func (export "pass") (param i32) (result i32) local.get 0)538)539(core instance $i (instantiate $m))540541(func (export "u32-to-bool") (param "a" u32) (result bool)542(canon lift (core func $i "pass"))543)544(func (export "bool-to-u32") (param "a" bool) (result u32)545(canon lift (core func $i "pass"))546)547)548"#;549550let engine = super::engine();551let component = Component::new(&engine, component)?;552let mut store = Store::new(&engine, ());553let instance = Linker::new(&engine).instantiate(&mut store, &component)?;554let u32_to_bool = instance.get_typed_func::<(u32,), (bool,)>(&mut store, "u32-to-bool")?;555let bool_to_u32 = instance.get_typed_func::<(bool,), (u32,)>(&mut store, "bool-to-u32")?;556557assert_eq!(bool_to_u32.call(&mut store, (false,))?, (0,));558assert_eq!(bool_to_u32.call(&mut store, (true,))?, (1,));559assert_eq!(u32_to_bool.call(&mut store, (0,))?, (false,));560assert_eq!(u32_to_bool.call(&mut store, (1,))?, (true,));561assert_eq!(u32_to_bool.call(&mut store, (2,))?, (true,));562563Ok(())564}565566#[test]567fn chars() -> Result<()> {568let component = r#"569(component570(core module $m571(func (export "pass") (param i32) (result i32) local.get 0)572)573(core instance $i (instantiate $m))574575(func (export "u32-to-char") (param "a" u32) (result char)576(canon lift (core func $i "pass"))577)578(func (export "char-to-u32") (param "a" char) (result u32)579(canon lift (core func $i "pass"))580)581)582"#;583584let engine = super::engine();585let component = Component::new(&engine, component)?;586let mut store = Store::new(&engine, ());587let instance = Linker::new(&engine).instantiate(&mut store, &component)?;588let u32_to_char = instance.get_typed_func::<(u32,), (char,)>(&mut store, "u32-to-char")?;589let char_to_u32 = instance.get_typed_func::<(char,), (u32,)>(&mut store, "char-to-u32")?;590591let mut roundtrip = |x: char| -> Result<()> {592assert_eq!(char_to_u32.call(&mut store, (x,))?, (x as u32,));593assert_eq!(u32_to_char.call(&mut store, (x as u32,))?, (x,));594Ok(())595};596597roundtrip('x')?;598roundtrip('a')?;599roundtrip('\0')?;600roundtrip('\n')?;601roundtrip('��')?;602603let u32_to_char = |store: &mut Store<()>| {604Linker::new(&engine)605.instantiate(&mut *store, &component)?606.get_typed_func::<(u32,), (char,)>(&mut *store, "u32-to-char")607};608let err = u32_to_char(&mut store)?609.call(&mut store, (0xd800,))610.unwrap_err();611assert!(err.to_string().contains("integer out of range"), "{}", err);612let err = u32_to_char(&mut store)?613.call(&mut store, (0xdfff,))614.unwrap_err();615assert!(err.to_string().contains("integer out of range"), "{}", err);616let err = u32_to_char(&mut store)?617.call(&mut store, (0x110000,))618.unwrap_err();619assert!(err.to_string().contains("integer out of range"), "{}", err);620let err = u32_to_char(&mut store)?621.call(&mut store, (u32::MAX,))622.unwrap_err();623assert!(err.to_string().contains("integer out of range"), "{}", err);624625Ok(())626}627628#[test]629fn tuple_result() -> Result<()> {630let component = r#"631(component632(core module $m633(memory (export "memory") 1)634(func (export "foo") (param i32 i32 f32 f64) (result i32)635(local $base i32)636(local.set $base (i32.const 8))637(i32.store8 offset=0 (local.get $base) (local.get 0))638(i32.store16 offset=2 (local.get $base) (local.get 1))639(f32.store offset=4 (local.get $base) (local.get 2))640(f64.store offset=8 (local.get $base) (local.get 3))641local.get $base642)643644(func (export "invalid") (result i32)645i32.const -8646)647)648(core instance $i (instantiate $m))649650(type $result (tuple s8 u16 float32 float64))651(func (export "tuple")652(param "a" s8) (param "b" u16) (param "c" float32) (param "d" float64) (result $result)653(canon lift (core func $i "foo") (memory $i "memory"))654)655(func (export "invalid") (result $result)656(canon lift (core func $i "invalid") (memory $i "memory"))657)658)659"#;660661let engine = super::engine();662let component = Component::new(&engine, component)?;663let mut store = Store::new(&engine, ());664let instance = Linker::new(&engine).instantiate(&mut store, &component)?;665666let input = (-1, 100, 3.0, 100.0);667let output = instance668.get_typed_func::<(i8, u16, f32, f64), ((i8, u16, f32, f64),)>(&mut store, "tuple")?669.call(&mut store, input)?;670assert_eq!((input,), output);671672let invalid_func =673instance.get_typed_func::<(), ((i8, u16, f32, f64),)>(&mut store, "invalid")?;674let err = invalid_func.call(&mut store, ()).err().unwrap();675assert!(676err.to_string().contains("pointer out of bounds of memory"),677"{}",678err679);680681Ok(())682}683684#[test]685fn strings() -> Result<()> {686let component = format!(687r#"(component688(core module $m689(memory (export "memory") 1)690(func (export "roundtrip") (param i32 i32) (result i32)691(local $base i32)692(local.set $base693(call $realloc694(i32.const 0)695(i32.const 0)696(i32.const 4)697(i32.const 8)))698(i32.store offset=0699(local.get $base)700(local.get 0))701(i32.store offset=4702(local.get $base)703(local.get 1))704(local.get $base)705)706707{REALLOC_AND_FREE}708)709(core instance $i (instantiate $m))710711(func (export "list8-to-str") (param "a" (list u8)) (result string)712(canon lift713(core func $i "roundtrip")714(memory $i "memory")715(realloc (func $i "realloc"))716)717)718(func (export "str-to-list8") (param "a" string) (result (list u8))719(canon lift720(core func $i "roundtrip")721(memory $i "memory")722(realloc (func $i "realloc"))723)724)725(func (export "list16-to-str") (param "a" (list u16)) (result string)726(canon lift727(core func $i "roundtrip")728string-encoding=utf16729(memory $i "memory")730(realloc (func $i "realloc"))731)732)733(func (export "str-to-list16") (param "a" string) (result (list u16))734(canon lift735(core func $i "roundtrip")736string-encoding=utf16737(memory $i "memory")738(realloc (func $i "realloc"))739)740)741)"#742);743744let engine = super::engine();745let component = Component::new(&engine, component)?;746let mut store = Store::new(&engine, ());747let instance = Linker::new(&engine).instantiate(&mut store, &component)?;748let list8_to_str =749instance.get_typed_func::<(&[u8],), (WasmStr,)>(&mut store, "list8-to-str")?;750let str_to_list8 =751instance.get_typed_func::<(&str,), (WasmList<u8>,)>(&mut store, "str-to-list8")?;752let list16_to_str =753instance.get_typed_func::<(&[u16],), (WasmStr,)>(&mut store, "list16-to-str")?;754let str_to_list16 =755instance.get_typed_func::<(&str,), (WasmList<u16>,)>(&mut store, "str-to-list16")?;756757let mut roundtrip = |x: &str| -> Result<()> {758let ret = list8_to_str.call(&mut store, (x.as_bytes(),))?.0;759assert_eq!(ret.to_str(&store)?, x);760761let utf16 = x.encode_utf16().collect::<Vec<_>>();762let ret = list16_to_str.call(&mut store, (&utf16[..],))?.0;763assert_eq!(ret.to_str(&store)?, x);764765let ret = str_to_list8.call(&mut store, (x,))?.0;766assert_eq!(767ret.iter(&mut store).collect::<Result<Vec<_>>>()?,768x.as_bytes()769);770771let ret = str_to_list16.call(&mut store, (x,))?.0;772assert_eq!(ret.iter(&mut store).collect::<Result<Vec<_>>>()?, utf16,);773774Ok(())775};776777roundtrip("")?;778roundtrip("foo")?;779roundtrip("hello there")?;780roundtrip("💝")?;781roundtrip("Löwe 老虎 Léopard")?;782783let ret = list8_to_str.call(&mut store, (b"\xff",))?.0;784let err = ret.to_str(&store).unwrap_err();785assert!(err.to_string().contains("invalid utf-8"), "{}", err);786787let ret = list8_to_str788.call(&mut store, (b"hello there \xff invalid",))?789.0;790let err = ret.to_str(&store).unwrap_err();791assert!(err.to_string().contains("invalid utf-8"), "{}", err);792793let ret = list16_to_str.call(&mut store, (&[0xd800],))?.0;794let err = ret.to_str(&store).unwrap_err();795assert!(err.to_string().contains("unpaired surrogate"), "{}", err);796797let ret = list16_to_str.call(&mut store, (&[0xdfff],))?.0;798let err = ret.to_str(&store).unwrap_err();799assert!(err.to_string().contains("unpaired surrogate"), "{}", err);800801let ret = list16_to_str.call(&mut store, (&[0xd800, 0xff00],))?.0;802let err = ret.to_str(&store).unwrap_err();803assert!(err.to_string().contains("unpaired surrogate"), "{}", err);804805Ok(())806}807808#[tokio::test]809async fn async_reentrance() -> Result<()> {810_ = env_logger::try_init();811812let component = r#"813(component814(core module $shim815(import "" "task.return" (func $task-return (param i32)))816(table (export "funcs") 1 1 funcref)817(func (export "export") (param i32) (result i32)818(call_indirect (i32.const 0) (local.get 0))819)820(func (export "callback") (param i32 i32 i32) (result i32) unreachable)821)822(core func $task-return (canon task.return (result u32)))823(core instance $shim (instantiate $shim824(with "" (instance (export "task.return" (func $task-return))))825))826(func $shim-export (param "p1" u32) (result u32)827(canon lift (core func $shim "export") async (callback (func $shim "callback")))828)829830(component $inner831(import "import" (func $import (param "p1" u32) (result u32)))832(core module $libc (memory (export "memory") 1))833(core instance $libc (instantiate $libc))834(core func $import (canon lower (func $import) async (memory $libc "memory")))835836(core module $m837(import "libc" "memory" (memory 1))838(import "" "import" (func $import (param i32 i32) (result i32)))839(import "" "task.return" (func $task-return (param i32)))840(func (export "export") (param i32) (result i32)841(i32.store offset=0 (i32.const 1200) (local.get 0))842(call $import (i32.const 1200) (i32.const 1204))843drop844(call $task-return (i32.load offset=0 (i32.const 1204)))845i32.const 0846)847(func (export "callback") (param i32 i32 i32) (result i32) unreachable)848)849(core type $task-return-type (func (param i32)))850(core func $task-return (canon task.return (result u32)))851(core instance $i (instantiate $m852(with "" (instance853(export "task.return" (func $task-return))854(export "import" (func $import))855))856(with "libc" (instance $libc))857))858(func (export "export") (param "p1" u32) (result u32)859(canon lift (core func $i "export") async (callback (func $i "callback")))860)861)862(instance $inner (instantiate $inner (with "import" (func $shim-export))))863864(core module $libc (memory (export "memory") 1))865(core instance $libc (instantiate $libc))866(core func $inner-export (canon lower (func $inner "export") async (memory $libc "memory")))867868(core module $donut869(import "" "funcs" (table 1 1 funcref))870(import "libc" "memory" (memory 1))871(import "" "import" (func $import (param i32 i32) (result i32)))872(import "" "task.return" (func $task-return (param i32)))873(func $host-export (export "export") (param i32) (result i32)874(i32.store offset=0 (i32.const 1200) (local.get 0))875(call $import (i32.const 1200) (i32.const 1204))876drop877(call $task-return (i32.load offset=0 (i32.const 1204)))878i32.const 0879)880(func $guest-export (export "guest-export") (param i32) (result i32) unreachable)881(func (export "callback") (param i32 i32 i32) (result i32) unreachable)882(func $start883(table.set (i32.const 0) (ref.func $guest-export))884)885(start $start)886)887888(core instance $donut (instantiate $donut889(with "" (instance890(export "task.return" (func $task-return))891(export "import" (func $inner-export))892(export "funcs" (table $shim "funcs"))893))894(with "libc" (instance $libc))895))896(func (export "export") (param "p1" u32) (result u32)897(canon lift (core func $donut "export") async (callback (func $donut "callback")))898)899)"#;900901let mut config = Config::new();902config.wasm_component_model_async(true);903config.wasm_component_model_async_stackful(true);904config.wasm_component_model_threading(true);905let engine = &Engine::new(&config)?;906let component = Component::new(&engine, component)?;907let mut store = Store::new(&engine, ());908909let instance = Linker::new(&engine)910.instantiate_async(&mut store, &component)911.await?;912let func = instance.get_typed_func::<(u32,), (u32,)>(&mut store, "export")?;913let message = "cannot enter component instance";914match store915.run_concurrent(async move |accessor| {916wasmtime::error::Ok(func.call_concurrent(accessor, (42,)).await?.0)917})918.await919{920Ok(_) => panic!(),921Err(e) => assert!(922format!("{e:?}").contains(message),923"expected `{message}`; got `{e:?}`"924),925}926927Ok(())928}929930#[tokio::test]931async fn missing_task_return_call_stackless() -> Result<()> {932task_return_trap(933r#"(component934(core module $m935(import "" "task.return" (func $task-return))936(func (export "foo") (result i32)937i32.const 0938)939(func (export "callback") (param i32 i32 i32) (result i32) unreachable)940)941(core func $task-return (canon task.return))942(core instance $i (instantiate $m943(with "" (instance (export "task.return" (func $task-return))))944))945(func (export "foo") (canon lift (core func $i "foo") async (callback (func $i "callback"))))946)"#,947"wasm trap: async-lifted export failed to produce a result",948)949.await950}951952#[tokio::test]953async fn missing_task_return_call_stackless_explicit_thread() -> Result<()> {954task_return_trap(955r#"(component956(core module $libc957(table (export "__indirect_function_table") 1 funcref))958(core module $m959(import "" "task.return" (func $task-return))960(import "" "thread.new-indirect" (func $thread-new-indirect (param i32 i32) (result i32)))961(import "" "thread.resume-later" (func $thread-resume-later (param i32)))962(import "libc" "__indirect_function_table" (table $indirect-function-table 1 funcref))963(func $thread-start (param i32) (; empty ;))964(elem (table $indirect-function-table) (i32.const 0) func $thread-start)965(func (export "foo") (result i32)966(call $thread-resume-later967(call $thread-new-indirect (i32.const 0) (i32.const 0)))968i32.const 0969)970(func (export "callback") (param i32 i32 i32) (result i32) unreachable)971)972(core instance $libc (instantiate $libc))973(core type $start-func-ty (func (param i32)))974(alias core export $libc "__indirect_function_table" (core table $indirect-function-table))975(core func $thread-new-indirect976(canon thread.new-indirect $start-func-ty (table $indirect-function-table)))977(core func $thread-resume-later (canon thread.resume-later))978(core func $task-return (canon task.return))979(core instance $i (instantiate $m980(with "" (instance981(export "thread.new-indirect" (func $thread-new-indirect))982(export "thread.resume-later" (func $thread-resume-later))983(export "task.return" (func $task-return))984))985(with "libc" (instance $libc))986))987(func (export "foo") (canon lift (core func $i "foo") async (callback (func $i "callback"))))988)"#,989"wasm trap: async-lifted export failed to produce a result",990)991.await992}993994#[tokio::test]995async fn missing_task_return_call_stackful_explicit_thread() -> Result<()> {996task_return_trap(997r#"(component998(core module $libc999(table (export "__indirect_function_table") 1 funcref))1000(core module $m1001(import "" "task.return" (func $task-return))1002(import "" "thread.new-indirect" (func $thread-new-indirect (param i32 i32) (result i32)))1003(import "" "thread.resume-later" (func $thread-resume-later (param i32)))1004(import "libc" "__indirect_function_table" (table $indirect-function-table 1 funcref))1005(func $thread-start (param i32) (; empty ;))1006(elem (table $indirect-function-table) (i32.const 0) func $thread-start)1007(func (export "foo")1008(call $thread-resume-later1009(call $thread-new-indirect (i32.const 0) (i32.const 0)))1010)1011)1012(core instance $libc (instantiate $libc))1013(core type $start-func-ty (func (param i32)))1014(alias core export $libc "__indirect_function_table" (core table $indirect-function-table))1015(core func $thread-new-indirect1016(canon thread.new-indirect $start-func-ty (table $indirect-function-table)))1017(core func $thread-resume-later (canon thread.resume-later))1018(core func $task-return (canon task.return))1019(core instance $i (instantiate $m1020(with "" (instance1021(export "thread.new-indirect" (func $thread-new-indirect))1022(export "thread.resume-later" (func $thread-resume-later))1023(export "task.return" (func $task-return))1024))1025(with "libc" (instance $libc))1026))1027(func (export "foo") (canon lift (core func $i "foo") async))1028)"#,1029"wasm trap: async-lifted export failed to produce a result",1030)1031.await1032}10331034#[tokio::test]1035async fn missing_task_return_call_stackful() -> Result<()> {1036task_return_trap(1037r#"(component1038(core module $m1039(import "" "task.return" (func $task-return))1040(func (export "foo"))1041)1042(core func $task-return (canon task.return))1043(core instance $i (instantiate $m1044(with "" (instance (export "task.return" (func $task-return))))1045))1046(func (export "foo") (canon lift (core func $i "foo") async))1047)"#,1048"wasm trap: async-lifted export failed to produce a result",1049)1050.await1051}10521053#[tokio::test]1054async fn task_return_type_mismatch() -> Result<()> {1055task_return_trap(1056r#"(component1057(core module $m1058(import "" "task.return" (func $task-return (param i32)))1059(func (export "foo") (call $task-return (i32.const 42)))1060)1061(core func $task-return (canon task.return (result u32)))1062(core instance $i (instantiate $m1063(with "" (instance (export "task.return" (func $task-return))))1064))1065(func (export "foo") (canon lift (core func $i "foo") async))1066)"#,1067"invalid `task.return` signature and/or options for current task",1068)1069.await1070}10711072#[tokio::test]1073async fn task_return_memory_mismatch() -> Result<()> {1074task_return_trap(1075r#"(component1076(core module $libc (memory (export "memory") 1))1077(core instance $libc (instantiate $libc))1078(core module $m1079(import "" "task.return" (func $task-return))1080(func (export "foo") (call $task-return))1081)1082(core func $task-return (canon task.return (memory $libc "memory")))1083(core instance $i (instantiate $m1084(with "" (instance (export "task.return" (func $task-return))))1085))1086(func (export "foo") (canon lift (core func $i "foo") async))1087)"#,1088"invalid `task.return` signature and/or options for current task",1089)1090.await1091}10921093#[tokio::test]1094async fn task_return_string_encoding_mismatch() -> Result<()> {1095task_return_trap(1096r#"(component1097(core module $m1098(import "" "task.return" (func $task-return))1099(func (export "foo") (call $task-return))1100)1101(core func $task-return (canon task.return string-encoding=utf16))1102(core instance $i (instantiate $m1103(with "" (instance (export "task.return" (func $task-return))))1104))1105(func (export "foo") (canon lift (core func $i "foo") async))1106)"#,1107"invalid `task.return` signature and/or options for current task",1108)1109.await1110}11111112async fn task_return_trap(component: &str, substring: &str) -> Result<()> {1113let mut config = Config::new();1114config.wasm_component_model_async(true);1115config.wasm_component_model_async_stackful(true);1116config.wasm_component_model_threading(true);1117let engine = &Engine::new(&config)?;1118let component = Component::new(&engine, component)?;1119let mut store = Store::new(&engine, ());11201121let instance = Linker::new(&engine)1122.instantiate_async(&mut store, &component)1123.await?;11241125let func = instance.get_typed_func::<(), ()>(&mut store, "foo")?;1126match store1127.run_concurrent(async move |accessor| {1128wasmtime::error::Ok(func.call_concurrent(accessor, ()).await?.0)1129})1130.await1131{1132Ok(_) => panic!(),1133Err(e) => {1134assert!(1135format!("{e:?}").contains(substring),1136"could not find `{substring}` in `{e:?}`"1137)1138}1139}11401141Ok(())1142}11431144#[tokio::test]1145async fn many_parameters() -> Result<()> {1146test_many_parameters(false, false).await1147}11481149#[tokio::test]1150async fn many_parameters_concurrent() -> Result<()> {1151test_many_parameters(false, true).await1152}11531154#[tokio::test]1155async fn many_parameters_dynamic() -> Result<()> {1156test_many_parameters(true, false).await1157}11581159#[tokio::test]1160async fn many_parameters_dynamic_concurrent() -> Result<()> {1161test_many_parameters(true, true).await1162}11631164async fn test_many_parameters(dynamic: bool, concurrent: bool) -> Result<()> {1165let (body, async_opts) = if concurrent {1166(1167r#"1168(call $task-return1169(i32.const 0)1170(i32.mul1171(memory.size)1172(i32.const 65536)1173)1174(local.get 0)1175)11761177(i32.const 0)1178"#,1179r#"async (callback (func $i "callback"))"#,1180)1181} else {1182(1183r#"1184(local $base i32)11851186;; Allocate space for the return1187(local.set $base1188(call $realloc1189(i32.const 0)1190(i32.const 0)1191(i32.const 4)1192(i32.const 12)))11931194;; Store the pointer/length of the entire linear memory1195;; so we have access to everything.1196(i32.store offset=01197(local.get $base)1198(i32.const 0))1199(i32.store offset=41200(local.get $base)1201(i32.mul1202(memory.size)1203(i32.const 65536)))12041205;; And also store our pointer parameter1206(i32.store offset=81207(local.get $base)1208(local.get 0))12091210(local.get $base)1211"#,1212"",1213)1214};12151216let component = format!(1217r#"(component1218(core module $libc1219(memory (export "memory") 1)12201221{REALLOC_AND_FREE}1222)1223(core instance $libc (instantiate $libc))1224(core module $m1225(import "libc" "memory" (memory 1))1226(import "libc" "realloc" (func $realloc (param i32 i32 i32 i32) (result i32)))1227(import "" "task.return" (func $task-return (param i32 i32 i32)))1228(func (export "foo") (param i32) (result i32)1229{body}1230)1231(func (export "callback") (param i32 i32 i32) (result i32) unreachable)1232)1233(type $tuple (tuple (list u8) u32))1234(core func $task-return (canon task.return1235(result $tuple)1236(memory $libc "memory")1237))1238(core instance $i (instantiate $m1239(with "" (instance (export "task.return" (func $task-return))))1240(with "libc" (instance $libc))1241))12421243(type $t (func1244(param "p1" s8) ;; offset 0, size 11245(param "p2" u64) ;; offset 8, size 81246(param "p3" float32) ;; offset 16, size 41247(param "p4" u8) ;; offset 20, size 11248(param "p5" s16) ;; offset 22, size 21249(param "p6" string) ;; offset 24, size 81250(param "p7" (list u32)) ;; offset 32, size 81251(param "p8" bool) ;; offset 40, size 11252(param "p9" bool) ;; offset 41, size 11253(param "p0" char) ;; offset 44, size 41254(param "pa" (list bool)) ;; offset 48, size 81255(param "pb" (list char)) ;; offset 56, size 81256(param "pc" (list string)) ;; offset 64, size 812571258(result $tuple)1259))1260(func (export "many-param") (type $t)1261(canon lift1262(core func $i "foo")1263(memory $libc "memory")1264(realloc (func $libc "realloc"))1265{async_opts}1266)1267)1268)"#1269);12701271let mut config = Config::new();1272config.wasm_component_model_async(true);1273let engine = &Engine::new(&config)?;1274let component = Component::new(&engine, component)?;1275let mut store = Store::new(&engine, ());12761277let instance = Linker::new(&engine)1278.instantiate_async(&mut store, &component)1279.await?;12801281let input = (1282-100,1283u64::MAX / 2,1284f32::from_bits(CANON_32BIT_NAN | 1),128538,128618831,1287"this is the first string",1288[1, 2, 3, 4, 5, 6, 7, 8].as_slice(),1289true,1290false,1291'��',1292[false, true, false, true, true].as_slice(),1293['��', '��', '��', '��', '��'].as_slice(),1294[1295"the quick",1296"brown fox",1297"was too lazy",1298"to jump over the dog",1299"what a demanding dog",1300]1301.as_slice(),1302);13031304let (memory, pointer) = if dynamic {1305let input = vec![1306Val::S8(input.0),1307Val::U64(input.1),1308Val::Float32(input.2),1309Val::U8(input.3),1310Val::S16(input.4),1311Val::String(input.5.into()),1312Val::List(input.6.iter().copied().map(Val::U32).collect()),1313Val::Bool(input.7),1314Val::Bool(input.8),1315Val::Char(input.9),1316Val::List(input.10.iter().copied().map(Val::Bool).collect()),1317Val::List(input.11.iter().copied().map(Val::Char).collect()),1318Val::List(input.12.iter().map(|&s| Val::String(s.into())).collect()),1319];1320let func = instance.get_func(&mut store, "many-param").unwrap();13211322let mut results = vec![Val::Bool(false)];1323if concurrent {1324store1325.run_concurrent(async |store| {1326func.call_concurrent(store, &input, &mut results).await?;1327wasmtime::error::Ok(())1328})1329.await??;1330} else {1331func.call_async(&mut store, &input, &mut results).await?;1332};1333let mut results = results.into_iter();1334let Some(Val::Tuple(results)) = results.next() else {1335panic!()1336};1337let mut results = results.into_iter();1338let Some(Val::List(memory)) = results.next() else {1339panic!()1340};1341let Some(Val::U32(pointer)) = results.next() else {1342panic!()1343};1344(1345memory1346.into_iter()1347.map(|v| if let Val::U8(v) = v { v } else { panic!() })1348.collect(),1349pointer,1350)1351} else {1352let func = instance.get_typed_func::<(1353i8,1354u64,1355f32,1356u8,1357i16,1358&str,1359&[u32],1360bool,1361bool,1362char,1363&[bool],1364&[char],1365&[&str],1366), ((Vec<u8>, u32),)>(&mut store, "many-param")?;13671368if concurrent {1369store1370.run_concurrent(async move |accessor| {1371wasmtime::error::Ok(func.call_concurrent(accessor, input).await?.0)1372})1373.await??1374.01375} else {1376func.call_async(&mut store, input).await?.01377}1378};1379let memory = &memory[..];13801381let mut actual = &memory[pointer as usize..][..72];1382assert_eq!(i8::from_le_bytes(*actual.take_n::<1>()), input.0);1383actual.skip::<7>();1384assert_eq!(u64::from_le_bytes(*actual.take_n::<8>()), input.1);1385assert_eq!(1386u32::from_le_bytes(*actual.take_n::<4>()),1387CANON_32BIT_NAN | 11388);1389assert_eq!(u8::from_le_bytes(*actual.take_n::<1>()), input.3);1390actual.skip::<1>();1391assert_eq!(i16::from_le_bytes(*actual.take_n::<2>()), input.4);1392assert_eq!(actual.ptr_len(memory, 1), input.5.as_bytes());1393let mut mem = actual.ptr_len(memory, 4);1394for expected in input.6.iter() {1395assert_eq!(u32::from_le_bytes(*mem.take_n::<4>()), *expected);1396}1397assert!(mem.is_empty());1398assert_eq!(actual.take_n::<1>(), &[input.7 as u8]);1399assert_eq!(actual.take_n::<1>(), &[input.8 as u8]);1400actual.skip::<2>();1401assert_eq!(u32::from_le_bytes(*actual.take_n::<4>()), input.9 as u32);14021403// (list bool)1404mem = actual.ptr_len(memory, 1);1405for expected in input.10.iter() {1406assert_eq!(mem.take_n::<1>(), &[*expected as u8]);1407}1408assert!(mem.is_empty());14091410// (list char)1411mem = actual.ptr_len(memory, 4);1412for expected in input.11.iter() {1413assert_eq!(u32::from_le_bytes(*mem.take_n::<4>()), *expected as u32);1414}1415assert!(mem.is_empty());14161417// (list string)1418mem = actual.ptr_len(memory, 8);1419for expected in input.12.iter() {1420let actual = mem.ptr_len(memory, 1);1421assert_eq!(actual, expected.as_bytes());1422}1423assert!(mem.is_empty());1424assert!(actual.is_empty());14251426Ok(())1427}14281429#[tokio::test]1430async fn many_results() -> Result<()> {1431test_many_results(false, false).await1432}14331434#[tokio::test]1435async fn many_results_concurrent() -> Result<()> {1436test_many_results(false, true).await1437}14381439#[tokio::test]1440async fn many_results_dynamic() -> Result<()> {1441test_many_results(true, false).await1442}14431444#[tokio::test]1445async fn many_results_dynamic_concurrent() -> Result<()> {1446test_many_results(true, true).await1447}14481449async fn test_many_results(dynamic: bool, concurrent: bool) -> Result<()> {1450let (ret, async_opts) = if concurrent {1451(1452r#"1453call $task-return1454i32.const 01455"#,1456r#"async (callback (func $i "callback"))"#,1457)1458} else {1459("", "")1460};14611462let my_nan = CANON_32BIT_NAN | 1;14631464let component = format!(1465r#"(component1466(core module $libc1467(memory (export "memory") 1)14681469{REALLOC_AND_FREE}1470)1471(core instance $libc (instantiate $libc))1472(core module $m1473(import "libc" "memory" (memory 1))1474(import "libc" "realloc" (func $realloc (param i32 i32 i32 i32) (result i32)))1475(import "" "task.return" (func $task-return (param i32)))1476(func (export "foo") (result i32)1477(local $base i32)1478(local $string i32)1479(local $list i32)14801481(local.set $base1482(call $realloc1483(i32.const 0)1484(i32.const 0)1485(i32.const 8)1486(i32.const 72)))14871488(i32.store8 offset=01489(local.get $base)1490(i32.const -100))14911492(i64.store offset=81493(local.get $base)1494(i64.const 9223372036854775807))14951496(f32.store offset=161497(local.get $base)1498(f32.reinterpret_i32 (i32.const {my_nan})))14991500(i32.store8 offset=201501(local.get $base)1502(i32.const 38))15031504(i32.store16 offset=221505(local.get $base)1506(i32.const 18831))15071508(local.set $string1509(call $realloc1510(i32.const 0)1511(i32.const 0)1512(i32.const 1)1513(i32.const 6)))15141515(i32.store8 offset=01516(local.get $string)1517(i32.const 97)) ;; 'a'1518(i32.store8 offset=11519(local.get $string)1520(i32.const 98)) ;; 'b'1521(i32.store8 offset=21522(local.get $string)1523(i32.const 99)) ;; 'c'1524(i32.store8 offset=31525(local.get $string)1526(i32.const 100)) ;; 'd'1527(i32.store8 offset=41528(local.get $string)1529(i32.const 101)) ;; 'e'1530(i32.store8 offset=51531(local.get $string)1532(i32.const 102)) ;; 'f'15331534(i32.store offset=241535(local.get $base)1536(local.get $string))15371538(i32.store offset=281539(local.get $base)1540(i32.const 2))15411542(local.set $list1543(call $realloc1544(i32.const 0)1545(i32.const 0)1546(i32.const 4)1547(i32.const 32)))15481549(i32.store offset=01550(local.get $list)1551(i32.const 1))1552(i32.store offset=41553(local.get $list)1554(i32.const 2))1555(i32.store offset=81556(local.get $list)1557(i32.const 3))1558(i32.store offset=121559(local.get $list)1560(i32.const 4))1561(i32.store offset=161562(local.get $list)1563(i32.const 5))1564(i32.store offset=201565(local.get $list)1566(i32.const 6))1567(i32.store offset=241568(local.get $list)1569(i32.const 7))1570(i32.store offset=281571(local.get $list)1572(i32.const 8))15731574(i32.store offset=321575(local.get $base)1576(local.get $list))15771578(i32.store offset=361579(local.get $base)1580(i32.const 8))15811582(i32.store8 offset=401583(local.get $base)1584(i32.const 1))15851586(i32.store8 offset=411587(local.get $base)1588(i32.const 0))15891590(i32.store offset=441591(local.get $base)1592(i32.const 128681)) ;; '🚩'15931594(local.set $list1595(call $realloc1596(i32.const 0)1597(i32.const 0)1598(i32.const 1)1599(i32.const 5)))16001601(i32.store8 offset=01602(local.get $list)1603(i32.const 0))1604(i32.store8 offset=11605(local.get $list)1606(i32.const 1))1607(i32.store8 offset=21608(local.get $list)1609(i32.const 0))1610(i32.store8 offset=31611(local.get $list)1612(i32.const 1))1613(i32.store8 offset=41614(local.get $list)1615(i32.const 1))16161617(i32.store offset=481618(local.get $base)1619(local.get $list))16201621(i32.store offset=521622(local.get $base)1623(i32.const 5))16241625(local.set $list1626(call $realloc1627(i32.const 0)1628(i32.const 0)1629(i32.const 4)1630(i32.const 20)))16311632(i32.store offset=01633(local.get $list)1634(i32.const 127820)) ;; '🍌'1635(i32.store offset=41636(local.get $list)1637(i32.const 129360)) ;; '🥐'1638(i32.store offset=81639(local.get $list)1640(i32.const 127831)) ;; '🍗'1641(i32.store offset=121642(local.get $list)1643(i32.const 127833)) ;; '🍙'1644(i32.store offset=161645(local.get $list)1646(i32.const 127841)) ;; '🍡'16471648(i32.store offset=561649(local.get $base)1650(local.get $list))16511652(i32.store offset=601653(local.get $base)1654(i32.const 5))16551656(local.set $list1657(call $realloc1658(i32.const 0)1659(i32.const 0)1660(i32.const 4)1661(i32.const 16)))16621663(i32.store offset=01664(local.get $list)1665(i32.add (local.get $string) (i32.const 2)))1666(i32.store offset=41667(local.get $list)1668(i32.const 2))1669(i32.store offset=81670(local.get $list)1671(i32.add (local.get $string) (i32.const 4)))1672(i32.store offset=121673(local.get $list)1674(i32.const 2))16751676(i32.store offset=641677(local.get $base)1678(local.get $list))16791680(i32.store offset=681681(local.get $base)1682(i32.const 2))16831684local.get $base16851686{ret}1687)1688(func (export "callback") (param i32 i32 i32) (result i32) unreachable)1689)1690(type $tuple (tuple1691s81692u641693float321694u81695s161696string1697(list u32)1698bool1699bool1700char1701(list bool)1702(list char)1703(list string)1704))1705(core func $task-return (canon task.return1706(result $tuple)1707(memory $libc "memory")1708))1709(core instance $i (instantiate $m1710(with "" (instance (export "task.return" (func $task-return))))1711(with "libc" (instance $libc))1712))17131714(type $t (func (result $tuple)))1715(func (export "many-results") (type $t)1716(canon lift1717(core func $i "foo")1718(memory $libc "memory")1719(realloc (func $libc "realloc"))1720{async_opts}1721)1722)1723)"#1724);17251726let mut config = Config::new();1727config.wasm_component_model_async(true);1728let engine = &Engine::new(&config)?;1729let component = Component::new(&engine, component)?;1730let mut store = Store::new(&engine, ());17311732let instance = Linker::new(&engine)1733.instantiate_async(&mut store, &component)1734.await?;17351736let expected = (1737-100i8,1738u64::MAX / 2,1739f32::from_bits(CANON_32BIT_NAN | 1),174038u8,174118831i16,1742"ab".to_string(),1743vec![1u32, 2, 3, 4, 5, 6, 7, 8],1744true,1745false,1746'��',1747vec![false, true, false, true, true],1748vec!['��', '��', '��', '��', '��'],1749vec!["cd".to_string(), "ef".to_string()],1750);17511752let actual = if dynamic {1753let func = instance.get_func(&mut store, "many-results").unwrap();17541755let mut results = vec![Val::Bool(false)];1756if concurrent {1757store1758.run_concurrent(async |store| {1759func.call_concurrent(store, &[], &mut results).await?;1760wasmtime::error::Ok(())1761})1762.await??;1763} else {1764func.call_async(&mut store, &[], &mut results).await?;1765};1766let mut results = results.into_iter();17671768let Some(Val::Tuple(results)) = results.next() else {1769panic!()1770};1771let mut results = results.into_iter();1772let Some(Val::S8(p1)) = results.next() else {1773panic!()1774};1775let Some(Val::U64(p2)) = results.next() else {1776panic!()1777};1778let Some(Val::Float32(p3)) = results.next() else {1779panic!()1780};1781let Some(Val::U8(p4)) = results.next() else {1782panic!()1783};1784let Some(Val::S16(p5)) = results.next() else {1785panic!()1786};1787let Some(Val::String(p6)) = results.next() else {1788panic!()1789};1790let Some(Val::List(p7)) = results.next() else {1791panic!()1792};1793let p7 = p71794.into_iter()1795.map(|v| if let Val::U32(v) = v { v } else { panic!() })1796.collect();1797let Some(Val::Bool(p8)) = results.next() else {1798panic!()1799};1800let Some(Val::Bool(p9)) = results.next() else {1801panic!()1802};1803let Some(Val::Char(p0)) = results.next() else {1804panic!()1805};1806let Some(Val::List(pa)) = results.next() else {1807panic!()1808};1809let pa = pa1810.into_iter()1811.map(|v| if let Val::Bool(v) = v { v } else { panic!() })1812.collect();1813let Some(Val::List(pb)) = results.next() else {1814panic!()1815};1816let pb = pb1817.into_iter()1818.map(|v| if let Val::Char(v) = v { v } else { panic!() })1819.collect();1820let Some(Val::List(pc)) = results.next() else {1821panic!()1822};1823let pc = pc1824.into_iter()1825.map(|v| if let Val::String(v) = v { v } else { panic!() })1826.collect();18271828(p1, p2, p3, p4, p5, p6, p7, p8, p9, p0, pa, pb, pc)1829} else {1830let func = instance.get_typed_func::<(), ((1831i8,1832u64,1833f32,1834u8,1835i16,1836String,1837Vec<u32>,1838bool,1839bool,1840char,1841Vec<bool>,1842Vec<char>,1843Vec<String>,1844),)>(&mut store, "many-results")?;18451846if concurrent {1847store1848.run_concurrent(async move |accessor| {1849wasmtime::error::Ok(func.call_concurrent(accessor, ()).await?.0)1850})1851.await??1852.01853} else {1854func.call_async(&mut store, ()).await?.01855}1856};18571858assert_eq!(expected.0, actual.0);1859assert_eq!(expected.1, actual.1);1860assert!(expected.2.is_nan());1861assert!(actual.2.is_nan());1862assert_eq!(expected.3, actual.3);1863assert_eq!(expected.4, actual.4);1864assert_eq!(expected.5, actual.5);1865assert_eq!(expected.6, actual.6);1866assert_eq!(expected.7, actual.7);1867assert_eq!(expected.8, actual.8);1868assert_eq!(expected.9, actual.9);1869assert_eq!(expected.10, actual.10);1870assert_eq!(expected.11, actual.11);1871assert_eq!(expected.12, actual.12);18721873Ok(())1874}18751876#[test]1877fn some_traps() -> Result<()> {1878let middle_of_memory = (i32::MAX / 2) & (!0xff);1879let component = format!(1880r#"(component1881(core module $m1882(memory (export "memory") 1)1883(func (export "take-many") (param i32))1884(func (export "take-list") (param i32 i32))18851886(func (export "realloc") (param i32 i32 i32 i32) (result i32)1887unreachable)1888)1889(core instance $i (instantiate $m))18901891(func (export "take-list-unreachable") (param "a" (list u8))1892(canon lift (core func $i "take-list") (memory $i "memory") (realloc (func $i "realloc")))1893)1894(func (export "take-string-unreachable") (param "a" string)1895(canon lift (core func $i "take-list") (memory $i "memory") (realloc (func $i "realloc")))1896)18971898(type $t (func1899(param "s1" string)1900(param "s2" string)1901(param "s3" string)1902(param "s4" string)1903(param "s5" string)1904(param "s6" string)1905(param "s7" string)1906(param "s8" string)1907(param "s9" string)1908(param "s10" string)1909))1910(func (export "take-many-unreachable") (type $t)1911(canon lift (core func $i "take-many") (memory $i "memory") (realloc (func $i "realloc")))1912)19131914(core module $m21915(memory (export "memory") 1)1916(func (export "take-many") (param i32))1917(func (export "take-list") (param i32 i32))19181919(func (export "realloc") (param i32 i32 i32 i32) (result i32)1920i32.const {middle_of_memory})1921)1922(core instance $i2 (instantiate $m2))19231924(func (export "take-list-base-oob") (param "a" (list u8))1925(canon lift (core func $i2 "take-list") (memory $i2 "memory") (realloc (func $i2 "realloc")))1926)1927(func (export "take-string-base-oob") (param "a" string)1928(canon lift (core func $i2 "take-list") (memory $i2 "memory") (realloc (func $i2 "realloc")))1929)1930(func (export "take-many-base-oob") (type $t)1931(canon lift (core func $i2 "take-many") (memory $i2 "memory") (realloc (func $i2 "realloc")))1932)19331934(core module $m31935(memory (export "memory") 1)1936(func (export "take-many") (param i32))1937(func (export "take-list") (param i32 i32))19381939(func (export "realloc") (param i32 i32 i32 i32) (result i32)1940i32.const 65532)1941)1942(core instance $i3 (instantiate $m3))19431944(func (export "take-list-end-oob") (param "a" (list u8))1945(canon lift (core func $i3 "take-list") (memory $i3 "memory") (realloc (func $i3 "realloc")))1946)1947(func (export "take-string-end-oob") (param "a" string)1948(canon lift (core func $i3 "take-list") (memory $i3 "memory") (realloc (func $i3 "realloc")))1949)1950(func (export "take-many-end-oob") (type $t)1951(canon lift (core func $i3 "take-many") (memory $i3 "memory") (realloc (func $i3 "realloc")))1952)19531954(core module $m41955(memory (export "memory") 1)1956(func (export "take-many") (param i32))19571958(global $cnt (mut i32) (i32.const 0))1959(func (export "realloc") (param i32 i32 i32 i32) (result i32)1960global.get $cnt1961if (result i32)1962i32.const 1000001963else1964i32.const 11965global.set $cnt1966i32.const 01967end1968)1969)1970(core instance $i4 (instantiate $m4))19711972(func (export "take-many-second-oob") (type $t)1973(canon lift (core func $i4 "take-many") (memory $i4 "memory") (realloc (func $i4 "realloc")))1974)1975)"#1976);19771978let engine = super::engine();1979let component = Component::new(&engine, component)?;19801981// This should fail when calling the allocator function for the argument1982let err = with_new_instance(&engine, &component, |store, instance| {1983instance1984.get_typed_func::<(&[u8],), ()>(&mut *store, "take-list-unreachable")?1985.call(store, (&[],))1986.unwrap_err()1987.downcast::<Trap>()1988})?;1989assert_eq!(err, Trap::UnreachableCodeReached);19901991// This should fail when calling the allocator function for the argument1992let err = with_new_instance(&engine, &component, |store, instance| {1993instance1994.get_typed_func::<(&str,), ()>(&mut *store, "take-string-unreachable")?1995.call(store, ("",))1996.unwrap_err()1997.downcast::<Trap>()1998})?;1999assert_eq!(err, Trap::UnreachableCodeReached);20002001// This should fail when calling the allocator function for the space2002// to store the arguments (before arguments are even lowered)2003let err = with_new_instance(&engine, &component, |store, instance| {2004instance2005.get_typed_func::<(&str, &str, &str, &str, &str, &str, &str, &str, &str, &str), ()>(2006&mut *store,2007"take-many-unreachable",2008)?2009.call(store, ("", "", "", "", "", "", "", "", "", ""))2010.unwrap_err()2011.downcast::<Trap>()2012})?;2013assert_eq!(err, Trap::UnreachableCodeReached);20142015// Assert that when the base pointer returned by malloc is out of bounds2016// that errors are reported as such. Both empty and lists with contents2017// should all be invalid here.2018//2019// FIXME(WebAssembly/component-model#32) confirm the semantics here are2020// what's desired.2021#[track_caller]2022fn assert_oob(err: &wasmtime::Error) {2023assert!(2024err.to_string()2025.contains("realloc return: beyond end of memory"),2026"{err:?}",2027);2028}2029let err = with_new_instance(&engine, &component, |store, instance| {2030instance2031.get_typed_func::<(&[u8],), ()>(&mut *store, "take-list-base-oob")?2032.call(store, (&[],))2033})2034.unwrap_err();2035assert_oob(&err);2036let err = with_new_instance(&engine, &component, |store, instance| {2037instance2038.get_typed_func::<(&[u8],), ()>(&mut *store, "take-list-base-oob")?2039.call(store, (&[1],))2040})2041.unwrap_err();2042assert_oob(&err);2043let err = with_new_instance(&engine, &component, |store, instance| {2044instance2045.get_typed_func::<(&str,), ()>(&mut *store, "take-string-base-oob")?2046.call(store, ("",))2047})2048.unwrap_err();2049assert_oob(&err);2050let err = with_new_instance(&engine, &component, |store, instance| {2051instance2052.get_typed_func::<(&str,), ()>(&mut *store, "take-string-base-oob")?2053.call(store, ("x",))2054})2055.unwrap_err();2056assert_oob(&err);2057let err = with_new_instance(&engine, &component, |store, instance| {2058instance2059.get_typed_func::<(&str, &str, &str, &str, &str, &str, &str, &str, &str, &str), ()>(2060&mut *store,2061"take-many-base-oob",2062)?2063.call(store, ("", "", "", "", "", "", "", "", "", ""))2064})2065.unwrap_err();2066assert_oob(&err);20672068// Test here that when the returned pointer from malloc is one byte from the2069// end of memory that empty things are fine, but larger things are not.20702071with_new_instance(&engine, &component, |store, instance| {2072instance2073.get_typed_func::<(&[u8],), ()>(&mut *store, "take-list-end-oob")?2074.call(store, (&[],))2075})?;2076with_new_instance(&engine, &component, |store, instance| {2077instance2078.get_typed_func::<(&[u8],), ()>(&mut *store, "take-list-end-oob")?2079.call(store, (&[1, 2, 3, 4],))2080})?;2081let err = with_new_instance(&engine, &component, |store, instance| {2082instance2083.get_typed_func::<(&[u8],), ()>(&mut *store, "take-list-end-oob")?2084.call(store, (&[1, 2, 3, 4, 5],))2085})2086.unwrap_err();2087assert_oob(&err);2088with_new_instance(&engine, &component, |store, instance| {2089instance2090.get_typed_func::<(&str,), ()>(&mut *store, "take-string-end-oob")?2091.call(store, ("",))2092})?;2093with_new_instance(&engine, &component, |store, instance| {2094instance2095.get_typed_func::<(&str,), ()>(&mut *store, "take-string-end-oob")?2096.call(store, ("abcd",))2097})?;2098let err = with_new_instance(&engine, &component, |store, instance| {2099instance2100.get_typed_func::<(&str,), ()>(&mut *store, "take-string-end-oob")?2101.call(store, ("abcde",))2102})2103.unwrap_err();2104assert_oob(&err);2105let err = with_new_instance(&engine, &component, |store, instance| {2106instance2107.get_typed_func::<(&str, &str, &str, &str, &str, &str, &str, &str, &str, &str), ()>(2108&mut *store,2109"take-many-end-oob",2110)?2111.call(store, ("", "", "", "", "", "", "", "", "", ""))2112})2113.unwrap_err();2114assert_oob(&err);21152116// For this function the first allocation, the space to store all the2117// arguments, is in-bounds but then all further allocations, such as for2118// each individual string, are all out of bounds.2119let err = with_new_instance(&engine, &component, |store, instance| {2120instance2121.get_typed_func::<(&str, &str, &str, &str, &str, &str, &str, &str, &str, &str), ()>(2122&mut *store,2123"take-many-second-oob",2124)?2125.call(store, ("", "", "", "", "", "", "", "", "", ""))2126})2127.unwrap_err();2128assert_oob(&err);2129let err = with_new_instance(&engine, &component, |store, instance| {2130instance2131.get_typed_func::<(&str, &str, &str, &str, &str, &str, &str, &str, &str, &str), ()>(2132&mut *store,2133"take-many-second-oob",2134)?2135.call(store, ("", "", "", "", "", "", "", "", "", "x"))2136})2137.unwrap_err();2138assert_oob(&err);2139Ok(())2140}21412142#[test]2143fn char_bool_memory() -> Result<()> {2144let component = format!(2145r#"(component2146(core module $m2147(memory (export "memory") 1)2148(func (export "ret-tuple") (param i32 i32) (result i32)2149(local $base i32)21502151;; Allocate space for the return2152(local.set $base2153(call $realloc2154(i32.const 0)2155(i32.const 0)2156(i32.const 4)2157(i32.const 8)))21582159;; store the boolean2160(i32.store offset=02161(local.get $base)2162(local.get 0))21632164;; store the char2165(i32.store offset=42166(local.get $base)2167(local.get 1))21682169(local.get $base)2170)21712172{REALLOC_AND_FREE}2173)2174(core instance $i (instantiate $m))21752176(func (export "ret-tuple") (param "a" u32) (param "b" u32) (result (tuple bool char))2177(canon lift (core func $i "ret-tuple")2178(memory $i "memory")2179(realloc (func $i "realloc")))2180)2181)"#2182);21832184let engine = super::engine();2185let component = Component::new(&engine, component)?;2186let mut store = Store::new(&engine, ());2187let instance = Linker::new(&engine).instantiate(&mut store, &component)?;2188let func = instance.get_typed_func::<(u32, u32), ((bool, char),)>(&mut store, "ret-tuple")?;21892190let (ret,) = func.call(&mut store, (0, 'a' as u32))?;2191assert_eq!(ret, (false, 'a'));21922193let (ret,) = func.call(&mut store, (1, '��' as u32))?;2194assert_eq!(ret, (true, '��'));21952196let (ret,) = func.call(&mut store, (2, 'a' as u32))?;2197assert_eq!(ret, (true, 'a'));21982199assert!(func.call(&mut store, (0, 0xd800)).is_err());22002201Ok(())2202}22032204#[test]2205fn string_list_oob() -> Result<()> {2206let component = format!(2207r#"(component2208(core module $m2209(memory (export "memory") 1)2210(func (export "ret-list") (result i32)2211(local $base i32)22122213;; Allocate space for the return2214(local.set $base2215(call $realloc2216(i32.const 0)2217(i32.const 0)2218(i32.const 4)2219(i32.const 8)))22202221(i32.store offset=02222(local.get $base)2223(i32.const 100000))2224(i32.store offset=42225(local.get $base)2226(i32.const 1))22272228(local.get $base)2229)22302231{REALLOC_AND_FREE}2232)2233(core instance $i (instantiate $m))22342235(func (export "ret-list-u8") (result (list u8))2236(canon lift (core func $i "ret-list")2237(memory $i "memory")2238(realloc (func $i "realloc"))2239)2240)2241(func (export "ret-string") (result string)2242(canon lift (core func $i "ret-list")2243(memory $i "memory")2244(realloc (func $i "realloc"))2245)2246)2247)"#2248);22492250let engine = super::engine();2251let component = Component::new(&engine, component)?;2252let mut store = Store::new(&engine, ());2253let ret_list_u8 = Linker::new(&engine)2254.instantiate(&mut store, &component)?2255.get_typed_func::<(), (WasmList<u8>,)>(&mut store, "ret-list-u8")?;2256let ret_string = Linker::new(&engine)2257.instantiate(&mut store, &component)?2258.get_typed_func::<(), (WasmStr,)>(&mut store, "ret-string")?;22592260let err = ret_list_u8.call(&mut store, ()).err().unwrap();2261assert!(err.to_string().contains("out of bounds"), "{}", err);22622263let err = ret_string.call(&mut store, ()).err().unwrap();2264assert!(err.to_string().contains("out of bounds"), "{}", err);22652266Ok(())2267}22682269#[test]2270fn tuples() -> Result<()> {2271let component = format!(2272r#"(component2273(core module $m2274(memory (export "memory") 1)2275(func (export "foo")2276(param i32 f64 i32)2277(result i32)22782279local.get 02280i32.const 02281i32.ne2282if unreachable end22832284local.get 12285f64.const 12286f64.ne2287if unreachable end22882289local.get 22290i32.const 22291i32.ne2292if unreachable end22932294i32.const 32295)2296)2297(core instance $i (instantiate $m))22982299(func (export "foo")2300(param "a" (tuple s32 float64))2301(param "b" (tuple s8))2302(result (tuple u16))2303(canon lift (core func $i "foo"))2304)2305)"#2306);23072308let engine = super::engine();2309let component = Component::new(&engine, component)?;2310let mut store = Store::new(&engine, ());2311let instance = Linker::new(&engine).instantiate(&mut store, &component)?;2312let foo = instance.get_typed_func::<((i32, f64), (i8,)), ((u16,),)>(&mut store, "foo")?;2313assert_eq!(foo.call(&mut store, ((0, 1.0), (2,)))?, ((3,),));23142315Ok(())2316}23172318#[test]2319fn option() -> Result<()> {2320let component = format!(2321r#"(component2322(core module $m2323(memory (export "memory") 1)2324(func (export "pass1") (param i32 i32) (result i32)2325(local $base i32)2326(local.set $base2327(call $realloc2328(i32.const 0)2329(i32.const 0)2330(i32.const 4)2331(i32.const 8)))23322333(i32.store offset=02334(local.get $base)2335(local.get 0))2336(i32.store offset=42337(local.get $base)2338(local.get 1))23392340(local.get $base)2341)2342(func (export "pass2") (param i32 i32 i32) (result i32)2343(local $base i32)2344(local.set $base2345(call $realloc2346(i32.const 0)2347(i32.const 0)2348(i32.const 4)2349(i32.const 12)))23502351(i32.store offset=02352(local.get $base)2353(local.get 0))2354(i32.store offset=42355(local.get $base)2356(local.get 1))2357(i32.store offset=82358(local.get $base)2359(local.get 2))23602361(local.get $base)2362)23632364{REALLOC_AND_FREE}2365)2366(core instance $i (instantiate $m))23672368(func (export "option-u8-to-tuple") (param "a" (option u8)) (result (tuple u32 u32))2369(canon lift (core func $i "pass1") (memory $i "memory"))2370)2371(func (export "option-u32-to-tuple") (param "a" (option u32)) (result (tuple u32 u32))2372(canon lift (core func $i "pass1") (memory $i "memory"))2373)2374(func (export "option-string-to-tuple") (param "a" (option string)) (result (tuple u32 string))2375(canon lift2376(core func $i "pass2")2377(memory $i "memory")2378(realloc (func $i "realloc"))2379)2380)2381(func (export "to-option-u8") (param "a" u32) (param "b" u32) (result (option u8))2382(canon lift (core func $i "pass1") (memory $i "memory"))2383)2384(func (export "to-option-u32") (param "a" u32) (param "b" u32) (result (option u32))2385(canon lift2386(core func $i "pass1")2387(memory $i "memory")2388)2389)2390(func (export "to-option-string") (param "a" u32) (param "b" string) (result (option string))2391(canon lift2392(core func $i "pass2")2393(memory $i "memory")2394(realloc (func $i "realloc"))2395)2396)2397)"#2398);23992400let engine = super::engine();2401let component = Component::new(&engine, component)?;2402let mut store = Store::new(&engine, ());2403let linker = Linker::new(&engine);2404let instance = linker.instantiate(&mut store, &component)?;24052406let option_u8_to_tuple = instance2407.get_typed_func::<(Option<u8>,), ((u32, u32),)>(&mut store, "option-u8-to-tuple")?;2408assert_eq!(option_u8_to_tuple.call(&mut store, (None,))?, ((0, 0),));2409assert_eq!(option_u8_to_tuple.call(&mut store, (Some(0),))?, ((1, 0),));2410assert_eq!(2411option_u8_to_tuple.call(&mut store, (Some(100),))?,2412((1, 100),)2413);24142415let option_u32_to_tuple = instance2416.get_typed_func::<(Option<u32>,), ((u32, u32),)>(&mut store, "option-u32-to-tuple")?;2417assert_eq!(option_u32_to_tuple.call(&mut store, (None,))?, ((0, 0),));2418assert_eq!(option_u32_to_tuple.call(&mut store, (Some(0),))?, ((1, 0),));2419assert_eq!(2420option_u32_to_tuple.call(&mut store, (Some(100),))?,2421((1, 100),)2422);24232424let option_string_to_tuple = instance.get_typed_func::<(Option<&str>,), ((u32, WasmStr),)>(2425&mut store,2426"option-string-to-tuple",2427)?;2428let ((a, b),) = option_string_to_tuple.call(&mut store, (None,))?;2429assert_eq!(a, 0);2430assert_eq!(b.to_str(&store)?, "");2431let ((a, b),) = option_string_to_tuple.call(&mut store, (Some(""),))?;2432assert_eq!(a, 1);2433assert_eq!(b.to_str(&store)?, "");2434let ((a, b),) = option_string_to_tuple.call(&mut store, (Some("hello"),))?;2435assert_eq!(a, 1);2436assert_eq!(b.to_str(&store)?, "hello");24372438let instance = linker.instantiate(&mut store, &component)?;2439let to_option_u8 =2440instance.get_typed_func::<(u32, u32), (Option<u8>,)>(&mut store, "to-option-u8")?;2441assert_eq!(to_option_u8.call(&mut store, (0x00_00, 0))?, (None,));2442assert_eq!(to_option_u8.call(&mut store, (0x00_01, 0))?, (Some(0),));2443assert_eq!(to_option_u8.call(&mut store, (0xfd_01, 0))?, (Some(0xfd),));2444assert!(to_option_u8.call(&mut store, (0x00_02, 0)).is_err());24452446let instance = linker.instantiate(&mut store, &component)?;2447let to_option_u32 =2448instance.get_typed_func::<(u32, u32), (Option<u32>,)>(&mut store, "to-option-u32")?;2449assert_eq!(to_option_u32.call(&mut store, (0, 0))?, (None,));2450assert_eq!(to_option_u32.call(&mut store, (1, 0))?, (Some(0),));2451assert_eq!(2452to_option_u32.call(&mut store, (1, 0x1234fead))?,2453(Some(0x1234fead),)2454);2455assert!(to_option_u32.call(&mut store, (2, 0)).is_err());24562457let instance = linker.instantiate(&mut store, &component)?;2458let to_option_string = instance2459.get_typed_func::<(u32, &str), (Option<WasmStr>,)>(&mut store, "to-option-string")?;2460let ret = to_option_string.call(&mut store, (0, ""))?.0;2461assert!(ret.is_none());2462let ret = to_option_string.call(&mut store, (1, ""))?.0;2463assert_eq!(ret.unwrap().to_str(&store)?, "");2464let ret = to_option_string.call(&mut store, (1, "cheesecake"))?.0;2465assert_eq!(ret.unwrap().to_str(&store)?, "cheesecake");2466assert!(to_option_string.call(&mut store, (2, "")).is_err());24672468Ok(())2469}24702471#[test]2472fn expected() -> Result<()> {2473let component = format!(2474r#"(component2475(core module $m2476(memory (export "memory") 1)2477(func (export "pass0") (param i32) (result i32)2478local.get 02479)2480(func (export "pass1") (param i32 i32) (result i32)2481(local $base i32)2482(local.set $base2483(call $realloc2484(i32.const 0)2485(i32.const 0)2486(i32.const 4)2487(i32.const 8)))24882489(i32.store offset=02490(local.get $base)2491(local.get 0))2492(i32.store offset=42493(local.get $base)2494(local.get 1))24952496(local.get $base)2497)2498(func (export "pass2") (param i32 i32 i32) (result i32)2499(local $base i32)2500(local.set $base2501(call $realloc2502(i32.const 0)2503(i32.const 0)2504(i32.const 4)2505(i32.const 12)))25062507(i32.store offset=02508(local.get $base)2509(local.get 0))2510(i32.store offset=42511(local.get $base)2512(local.get 1))2513(i32.store offset=82514(local.get $base)2515(local.get 2))25162517(local.get $base)2518)25192520{REALLOC_AND_FREE}2521)2522(core instance $i (instantiate $m))25232524(func (export "take-expected-unit") (param "a" (result)) (result u32)2525(canon lift (core func $i "pass0"))2526)2527(func (export "take-expected-u8-f32") (param "a" (result u8 (error float32))) (result (tuple u32 u32))2528(canon lift (core func $i "pass1") (memory $i "memory"))2529)2530(type $list (list u8))2531(func (export "take-expected-string") (param "a" (result string (error $list))) (result (tuple u32 string))2532(canon lift2533(core func $i "pass2")2534(memory $i "memory")2535(realloc (func $i "realloc"))2536)2537)2538(func (export "to-expected-unit") (param "a" u32) (result (result))2539(canon lift (core func $i "pass0"))2540)2541(func (export "to-expected-s16-f32") (param "a" u32) (param "b" u32) (result (result s16 (error float32)))2542(canon lift2543(core func $i "pass1")2544(memory $i "memory")2545(realloc (func $i "realloc"))2546)2547)2548)"#2549);25502551let engine = super::engine();2552let component = Component::new(&engine, component)?;2553let mut store = Store::new(&engine, ());2554let linker = Linker::new(&engine);2555let instance = linker.instantiate(&mut store, &component)?;2556let take_expected_unit =2557instance.get_typed_func::<(Result<(), ()>,), (u32,)>(&mut store, "take-expected-unit")?;2558assert_eq!(take_expected_unit.call(&mut store, (Ok(()),))?, (0,));2559assert_eq!(take_expected_unit.call(&mut store, (Err(()),))?, (1,));25602561let take_expected_u8_f32 = instance2562.get_typed_func::<(Result<u8, f32>,), ((u32, u32),)>(&mut store, "take-expected-u8-f32")?;2563assert_eq!(take_expected_u8_f32.call(&mut store, (Ok(1),))?, ((0, 1),));2564assert_eq!(2565take_expected_u8_f32.call(&mut store, (Err(2.0),))?,2566((1, 2.0f32.to_bits()),)2567);25682569let take_expected_string = instance2570.get_typed_func::<(Result<&str, &[u8]>,), ((u32, WasmStr),)>(2571&mut store,2572"take-expected-string",2573)?;2574let ((a, b),) = take_expected_string.call(&mut store, (Ok("hello"),))?;2575assert_eq!(a, 0);2576assert_eq!(b.to_str(&store)?, "hello");2577let ((a, b),) = take_expected_string.call(&mut store, (Err(b"goodbye"),))?;2578assert_eq!(a, 1);2579assert_eq!(b.to_str(&store)?, "goodbye");25802581let instance = linker.instantiate(&mut store, &component)?;2582let to_expected_unit =2583instance.get_typed_func::<(u32,), (Result<(), ()>,)>(&mut store, "to-expected-unit")?;2584assert_eq!(to_expected_unit.call(&mut store, (0,))?, (Ok(()),));2585assert_eq!(to_expected_unit.call(&mut store, (1,))?, (Err(()),));2586let err = to_expected_unit.call(&mut store, (2,)).unwrap_err();2587assert!(err.to_string().contains("invalid expected"), "{}", err);25882589let instance = linker.instantiate(&mut store, &component)?;2590let to_expected_s16_f32 = instance2591.get_typed_func::<(u32, u32), (Result<i16, f32>,)>(&mut store, "to-expected-s16-f32")?;2592assert_eq!(to_expected_s16_f32.call(&mut store, (0, 0))?, (Ok(0),));2593assert_eq!(to_expected_s16_f32.call(&mut store, (0, 100))?, (Ok(100),));2594assert_eq!(2595to_expected_s16_f32.call(&mut store, (1, 1.0f32.to_bits()))?,2596(Err(1.0),)2597);2598let ret = to_expected_s16_f322599.call(&mut store, (1, CANON_32BIT_NAN | 1))?2600.0;2601assert_eq!(ret.unwrap_err().to_bits(), CANON_32BIT_NAN | 1);2602assert!(to_expected_s16_f32.call(&mut store, (2, 0)).is_err());26032604Ok(())2605}26062607#[test]2608fn fancy_list() -> Result<()> {2609let component = format!(2610r#"(component2611(core module $m2612(memory (export "memory") 1)2613(func (export "take") (param i32 i32) (result i32)2614(local $base i32)2615(local.set $base2616(call $realloc2617(i32.const 0)2618(i32.const 0)2619(i32.const 4)2620(i32.const 16)))26212622(i32.store offset=02623(local.get $base)2624(local.get 0))2625(i32.store offset=42626(local.get $base)2627(local.get 1))2628(i32.store offset=82629(local.get $base)2630(i32.const 0))2631(i32.store offset=122632(local.get $base)2633(i32.mul2634(memory.size)2635(i32.const 65536)))26362637(local.get $base)2638)26392640{REALLOC_AND_FREE}2641)2642(core instance $i (instantiate $m))26432644(type $a (option u8))2645(type $b (result (error string)))2646(type $input (list (tuple $a $b)))2647(func (export "take")2648(param "a" $input)2649(result (tuple u32 u32 (list u8)))2650(canon lift2651(core func $i "take")2652(memory $i "memory")2653(realloc (func $i "realloc"))2654)2655)2656)"#2657);26582659let engine = super::engine();2660let component = Component::new(&engine, component)?;2661let mut store = Store::new(&engine, ());2662let instance = Linker::new(&engine).instantiate(&mut store, &component)?;26632664let func = instance2665.get_typed_func::<(&[(Option<u8>, Result<(), &str>)],), ((u32, u32, WasmList<u8>),)>(2666&mut store, "take",2667)?;26682669let input = [2670(None, Ok(())),2671(Some(2), Err("hello there")),2672(Some(200), Err("general kenobi")),2673];2674let ((ptr, len, list),) = func.call(&mut store, (&input,))?;2675let memory = list.as_le_slice(&store);2676let ptr = usize::try_from(ptr).unwrap();2677let len = usize::try_from(len).unwrap();2678let mut array = &memory[ptr..][..len * 16];26792680for (a, b) in input.iter() {2681match a {2682Some(val) => {2683assert_eq!(*array.take_n::<2>(), [1, *val]);2684}2685None => {2686assert_eq!(*array.take_n::<1>(), [0]);2687array.skip::<1>();2688}2689}2690array.skip::<2>();2691match b {2692Ok(()) => {2693assert_eq!(*array.take_n::<1>(), [0]);2694array.skip::<11>();2695}2696Err(s) => {2697assert_eq!(*array.take_n::<1>(), [1]);2698array.skip::<3>();2699assert_eq!(array.ptr_len(memory, 1), s.as_bytes());2700}2701}2702}2703assert!(array.is_empty());27042705Ok(())2706}27072708trait SliceExt<'a> {2709fn take_n<const N: usize>(&mut self) -> &'a [u8; N];27102711fn skip<const N: usize>(&mut self) {2712self.take_n::<N>();2713}27142715fn ptr_len<'b>(&mut self, all_memory: &'b [u8], size: usize) -> &'b [u8] {2716let ptr = u32::from_le_bytes(*self.take_n::<4>());2717let len = u32::from_le_bytes(*self.take_n::<4>());2718let ptr = usize::try_from(ptr).unwrap();2719let len = usize::try_from(len).unwrap();2720&all_memory[ptr..][..len * size]2721}2722}27232724impl<'a> SliceExt<'a> for &'a [u8] {2725fn take_n<const N: usize>(&mut self) -> &'a [u8; N] {2726let (a, b) = self.split_at(N);2727*self = b;2728a.try_into().unwrap()2729}2730}27312732#[test]2733fn invalid_alignment() -> Result<()> {2734let component = format!(2735r#"(component2736(core module $m2737(memory (export "memory") 1)2738(func (export "realloc") (param i32 i32 i32 i32) (result i32)2739i32.const 1)27402741(func (export "take-i32") (param i32))2742(func (export "ret-1") (result i32) i32.const 1)2743(func (export "ret-unaligned-list") (result i32)2744(i32.store offset=0 (i32.const 8) (i32.const 1))2745(i32.store offset=4 (i32.const 8) (i32.const 1))2746i32.const 8)2747)2748(core instance $i (instantiate $m))27492750(func (export "many-params")2751(param "s1" string) (param "s2" string) (param "s3" string) (param "s4" string)2752(param "s5" string) (param "s6" string) (param "s7" string) (param "s8" string)2753(param "s9" string) (param "s10" string) (param "s11" string) (param "s12" string)2754(canon lift2755(core func $i "take-i32")2756(memory $i "memory")2757(realloc (func $i "realloc"))2758)2759)2760(func (export "string-ret") (result string)2761(canon lift2762(core func $i "ret-1")2763(memory $i "memory")2764(realloc (func $i "realloc"))2765)2766)2767(func (export "list-u32-ret") (result (list u32))2768(canon lift2769(core func $i "ret-unaligned-list")2770(memory $i "memory")2771(realloc (func $i "realloc"))2772)2773)2774)"#2775);27762777let engine = super::engine();2778let component = Component::new(&engine, component)?;2779let mut store = Store::new(&engine, ());2780let instance = |store: &mut Store<()>| Linker::new(&engine).instantiate(store, &component);27812782let err = instance(&mut store)?2783.get_typed_func::<(2784&str,2785&str,2786&str,2787&str,2788&str,2789&str,2790&str,2791&str,2792&str,2793&str,2794&str,2795&str,2796), ()>(&mut store, "many-params")?2797.call(&mut store, ("", "", "", "", "", "", "", "", "", "", "", ""))2798.unwrap_err();2799assert!(2800err.to_string()2801.contains("realloc return: result not aligned"),2802"{}",2803err2804);28052806let err = instance(&mut store)?2807.get_typed_func::<(), (WasmStr,)>(&mut store, "string-ret")?2808.call(&mut store, ())2809.err()2810.unwrap();2811assert!(2812err.to_string().contains("return pointer not aligned"),2813"{}",2814err2815);28162817let err = instance(&mut store)?2818.get_typed_func::<(), (WasmList<u32>,)>(&mut store, "list-u32-ret")?2819.call(&mut store, ())2820.err()2821.unwrap();2822assert!(2823err.to_string().contains("list pointer is not aligned"),2824"{}",2825err2826);28272828Ok(())2829}28302831#[test]2832fn drop_component_still_works() -> Result<()> {2833let component = r#"2834(component2835(import "f" (func $f))28362837(core func $f_lower2838(canon lower (func $f))2839)2840(core module $m2841(import "" "" (func $f))28422843(func $f22844call $f2845call $f2846)28472848(export "f" (func $f2))2849)2850(core instance $i (instantiate $m2851(with "" (instance2852(export "" (func $f_lower))2853))2854))2855(func (export "g")2856(canon lift2857(core func $i "f")2858)2859)2860)2861"#;28622863let (mut store, instance) = {2864let engine = super::engine();2865let component = Component::new(&engine, component)?;2866let mut store = Store::new(&engine, 0);2867let mut linker = Linker::new(&engine);2868linker.root().func_wrap(2869"f",2870|mut store: StoreContextMut<'_, u32>, _: ()| -> Result<()> {2871*store.data_mut() += 1;2872Ok(())2873},2874)?;2875let instance = linker.instantiate(&mut store, &component)?;2876(store, instance)2877};28782879let f = instance.get_typed_func::<(), ()>(&mut store, "g")?;2880assert_eq!(*store.data(), 0);2881f.call(&mut store, ())?;2882assert_eq!(*store.data(), 2);28832884Ok(())2885}28862887#[test]2888fn raw_slice_of_various_types() -> Result<()> {2889let component = r#"2890(component2891(core module $m2892(memory (export "memory") 1)28932894(func (export "list8") (result i32)2895(call $setup_list (i32.const 16))2896)2897(func (export "list16") (result i32)2898(call $setup_list (i32.const 8))2899)2900(func (export "list32") (result i32)2901(call $setup_list (i32.const 4))2902)2903(func (export "list64") (result i32)2904(call $setup_list (i32.const 2))2905)29062907(func $setup_list (param i32) (result i32)2908(i32.store offset=0 (i32.const 100) (i32.const 8))2909(i32.store offset=4 (i32.const 100) (local.get 0))2910i32.const 1002911)29122913(data (i32.const 8) "\00\01\02\03\04\05\06\07\08\09\0a\0b\0c\0d\0e\0f")2914)2915(core instance $i (instantiate $m))2916(func (export "list-u8") (result (list u8))2917(canon lift (core func $i "list8") (memory $i "memory"))2918)2919(func (export "list-i8") (result (list s8))2920(canon lift (core func $i "list8") (memory $i "memory"))2921)2922(func (export "list-u16") (result (list u16))2923(canon lift (core func $i "list16") (memory $i "memory"))2924)2925(func (export "list-i16") (result (list s16))2926(canon lift (core func $i "list16") (memory $i "memory"))2927)2928(func (export "list-u32") (result (list u32))2929(canon lift (core func $i "list32") (memory $i "memory"))2930)2931(func (export "list-i32") (result (list s32))2932(canon lift (core func $i "list32") (memory $i "memory"))2933)2934(func (export "list-u64") (result (list u64))2935(canon lift (core func $i "list64") (memory $i "memory"))2936)2937(func (export "list-i64") (result (list s64))2938(canon lift (core func $i "list64") (memory $i "memory"))2939)2940)2941"#;29422943let (mut store, instance) = {2944let engine = super::engine();2945let component = Component::new(&engine, component)?;2946let mut store = Store::new(&engine, ());2947let instance = Linker::new(&engine).instantiate(&mut store, &component)?;2948(store, instance)2949};29502951let list = instance2952.get_typed_func::<(), (WasmList<u8>,)>(&mut store, "list-u8")?2953.call(&mut store, ())?2954.0;2955assert_eq!(2956list.as_le_slice(&store),2957[29580x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d,29590x0e, 0x0f,2960]2961);2962let list = instance2963.get_typed_func::<(), (WasmList<i8>,)>(&mut store, "list-i8")?2964.call(&mut store, ())?2965.0;2966assert_eq!(2967list.as_le_slice(&store),2968[29690x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d,29700x0e, 0x0f,2971]2972);29732974let list = instance2975.get_typed_func::<(), (WasmList<u16>,)>(&mut store, "list-u16")?2976.call(&mut store, ())?2977.0;2978assert_eq!(2979list.as_le_slice(&store),2980[2981u16::to_le(0x01_00),2982u16::to_le(0x03_02),2983u16::to_le(0x05_04),2984u16::to_le(0x07_06),2985u16::to_le(0x09_08),2986u16::to_le(0x0b_0a),2987u16::to_le(0x0d_0c),2988u16::to_le(0x0f_0e),2989]2990);2991let list = instance2992.get_typed_func::<(), (WasmList<i16>,)>(&mut store, "list-i16")?2993.call(&mut store, ())?2994.0;2995assert_eq!(2996list.as_le_slice(&store),2997[2998i16::to_le(0x01_00),2999i16::to_le(0x03_02),3000i16::to_le(0x05_04),3001i16::to_le(0x07_06),3002i16::to_le(0x09_08),3003i16::to_le(0x0b_0a),3004i16::to_le(0x0d_0c),3005i16::to_le(0x0f_0e),3006]3007);3008let list = instance3009.get_typed_func::<(), (WasmList<u32>,)>(&mut store, "list-u32")?3010.call(&mut store, ())?3011.0;3012assert_eq!(3013list.as_le_slice(&store),3014[3015u32::to_le(0x03_02_01_00),3016u32::to_le(0x07_06_05_04),3017u32::to_le(0x0b_0a_09_08),3018u32::to_le(0x0f_0e_0d_0c),3019]3020);3021let list = instance3022.get_typed_func::<(), (WasmList<i32>,)>(&mut store, "list-i32")?3023.call(&mut store, ())?3024.0;3025assert_eq!(3026list.as_le_slice(&store),3027[3028i32::to_le(0x03_02_01_00),3029i32::to_le(0x07_06_05_04),3030i32::to_le(0x0b_0a_09_08),3031i32::to_le(0x0f_0e_0d_0c),3032]3033);3034let list = instance3035.get_typed_func::<(), (WasmList<u64>,)>(&mut store, "list-u64")?3036.call(&mut store, ())?3037.0;3038assert_eq!(3039list.as_le_slice(&store),3040[3041u64::to_le(0x07_06_05_04_03_02_01_00),3042u64::to_le(0x0f_0e_0d_0c_0b_0a_09_08),3043]3044);3045let list = instance3046.get_typed_func::<(), (WasmList<i64>,)>(&mut store, "list-i64")?3047.call(&mut store, ())?3048.0;3049assert_eq!(3050list.as_le_slice(&store),3051[3052i64::to_le(0x07_06_05_04_03_02_01_00),3053i64::to_le(0x0f_0e_0d_0c_0b_0a_09_08),3054]3055);30563057Ok(())3058}30593060#[test]3061fn lower_then_lift() -> Result<()> {3062// First test simple integers when the import/export ABI happen to line up3063let component = r#"3064(component $c3065(import "f" (func $f (result u32)))30663067(core func $f_lower3068(canon lower (func $f))3069)3070(func $f2 (result s32)3071(canon lift (core func $f_lower))3072)3073(export "f2" (func $f2))3074)3075"#;30763077let engine = super::engine();3078let component = Component::new(&engine, component)?;3079let mut store = Store::new(&engine, ());3080let mut linker = Linker::new(&engine);3081linker.root().func_wrap("f", |_, _: ()| Ok((2u32,)))?;3082let instance = linker.instantiate(&mut store, &component)?;30833084let f = instance.get_typed_func::<(), (i32,)>(&mut store, "f2")?;3085assert_eq!(f.call(&mut store, ())?, (2,));30863087// First test strings when the import/export ABI happen to line up3088let component = format!(3089r#"3090(component $c3091(import "s" (func $f (param "a" string)))30923093(core module $libc3094(memory (export "memory") 1)3095{REALLOC_AND_FREE}3096)3097(core instance $libc (instantiate $libc))30983099(core func $f_lower3100(canon lower (func $f) (memory $libc "memory"))3101)3102(func $f2 (param "a" string)3103(canon lift (core func $f_lower)3104(memory $libc "memory")3105(realloc (func $libc "realloc"))3106)3107)3108(export "f" (func $f2))3109)3110"#3111);31123113let component = Component::new(&engine, component)?;3114let mut store = Store::new(&engine, ());3115linker3116.root()3117.func_wrap("s", |store: StoreContextMut<'_, ()>, (x,): (WasmStr,)| {3118assert_eq!(x.to_str(&store)?, "hello");3119Ok(())3120})?;3121let instance = linker.instantiate(&mut store, &component)?;31223123let f = instance.get_typed_func::<(&str,), ()>(&mut store, "f")?;3124f.call(&mut store, ("hello",))?;31253126// Next test "type punning" where return values are reinterpreted just3127// because the return ABI happens to line up.3128let component = format!(3129r#"3130(component $c3131(import "s2" (func $f (param "a" string) (result u32)))31323133(core module $libc3134(memory (export "memory") 1)3135{REALLOC_AND_FREE}3136)3137(core instance $libc (instantiate $libc))31383139(core func $f_lower3140(canon lower (func $f) (memory $libc "memory"))3141)3142(func $f2 (param "a" string) (result string)3143(canon lift (core func $f_lower)3144(memory $libc "memory")3145(realloc (func $libc "realloc"))3146)3147)3148(export "f" (func $f2))3149)3150"#3151);31523153let component = Component::new(&engine, component)?;3154let mut store = Store::new(&engine, ());3155linker3156.root()3157.func_wrap("s2", |store: StoreContextMut<'_, ()>, (x,): (WasmStr,)| {3158assert_eq!(x.to_str(&store)?, "hello");3159Ok((u32::MAX,))3160})?;3161let instance = linker.instantiate(&mut store, &component)?;31623163let f = instance.get_typed_func::<(&str,), (WasmStr,)>(&mut store, "f")?;3164let err = f.call(&mut store, ("hello",)).err().unwrap();3165assert!(3166err.to_string().contains("return pointer not aligned"),3167"{}",3168err3169);31703171Ok(())3172}31733174#[test]3175fn errors_that_poison_instance() -> Result<()> {3176let component = format!(3177r#"3178(component $c3179(core module $m13180(func (export "f1") unreachable)3181(func (export "f2"))3182)3183(core instance $m1 (instantiate $m1))3184(func (export "f1") (canon lift (core func $m1 "f1")))3185(func (export "f2") (canon lift (core func $m1 "f2")))31863187(core module $m23188(func (export "f") (param i32 i32))3189(func (export "r") (param i32 i32 i32 i32) (result i32) unreachable)3190(memory (export "m") 1)3191)3192(core instance $m2 (instantiate $m2))3193(func (export "f3") (param "a" string)3194(canon lift (core func $m2 "f") (realloc (func $m2 "r")) (memory $m2 "m"))3195)31963197(core module $m33198(func (export "f") (result i32) i32.const 1)3199(memory (export "m") 1)3200)3201(core instance $m3 (instantiate $m3))3202(func (export "f4") (result string)3203(canon lift (core func $m3 "f") (memory $m3 "m"))3204)3205)3206"#3207);32083209let engine = super::engine();3210let component = Component::new(&engine, component)?;3211let linker = Linker::new(&engine);32123213{3214let mut store = Store::new(&engine, ());3215let instance = linker.instantiate(&mut store, &component)?;3216let f1 = instance.get_typed_func::<(), ()>(&mut store, "f1")?;3217let f2 = instance.get_typed_func::<(), ()>(&mut store, "f2")?;3218assert_unreachable(f1.call(&mut store, ()));3219assert_poisoned(f1.call(&mut store, ()));3220assert_poisoned(f2.call(&mut store, ()));3221}32223223{3224let mut store = Store::new(&engine, ());3225let instance = linker.instantiate(&mut store, &component)?;3226let f3 = instance.get_typed_func::<(&str,), ()>(&mut store, "f3")?;3227assert_unreachable(f3.call(&mut store, ("x",)));3228assert_poisoned(f3.call(&mut store, ("x",)));32293230// Since we actually poison the store, even an unrelated instance will3231// be considered poisoned:3232let instance = linker.instantiate(&mut store, &component)?;3233let f3 = instance.get_typed_func::<(&str,), ()>(&mut store, "f3")?;3234assert_poisoned(f3.call(&mut store, ("x",)));3235}32363237{3238let mut store = Store::new(&engine, ());3239let instance = linker.instantiate(&mut store, &component)?;3240let f4 = instance.get_typed_func::<(), (WasmStr,)>(&mut store, "f4")?;3241assert!(f4.call(&mut store, ()).is_err());3242assert_poisoned(f4.call(&mut store, ()));3243}32443245return Ok(());32463247#[track_caller]3248fn assert_unreachable<T>(err: Result<T>) {3249let err = match err {3250Ok(_) => panic!("expected an error"),3251Err(e) => e,3252};3253assert_eq!(3254err.downcast::<Trap>().unwrap(),3255Trap::UnreachableCodeReached3256);3257}32583259#[track_caller]3260fn assert_poisoned<T>(err: Result<T>) {3261let err = match err {3262Ok(_) => panic!("expected an error"),3263Err(e) => e,3264};3265assert_eq!(3266err.downcast_ref::<Trap>(),3267Some(&Trap::CannotEnterComponent),3268"{err}",3269);3270}3271}32723273#[test]3274fn run_export_with_internal_adapter() -> Result<()> {3275let component = r#"3276(component3277(type $t (func (param "a" u32) (result u32)))3278(component $a3279(core module $m3280(func (export "add-five") (param i32) (result i32)3281local.get 03282i32.const 53283i32.add)3284)3285(core instance $m (instantiate $m))3286(func (export "add-five") (type $t) (canon lift (core func $m "add-five")))3287)3288(component $b3289(import "interface-v1" (instance $i3290(export "add-five" (func (type $t)))))3291(core module $m3292(func $add-five (import "interface-0.1.0" "add-five") (param i32) (result i32))3293(func) ;; causes index out of bounds3294(func (export "run") (result i32) i32.const 0 call $add-five)3295)3296(core func $add-five (canon lower (func $i "add-five")))3297(core instance $i (instantiate 03298(with "interface-0.1.0" (instance3299(export "add-five" (func $add-five))3300))3301))3302(func (result u32) (canon lift (core func $i "run")))3303(export "run" (func 1))3304)3305(instance $a (instantiate $a))3306(instance $b (instantiate $b (with "interface-v1" (instance $a))))3307(export "run" (func $b "run"))3308)3309"#;3310let engine = super::engine();3311let component = Component::new(&engine, component)?;3312let mut store = Store::new(&engine, ());3313let linker = Linker::new(&engine);3314let instance = linker.instantiate(&mut store, &component)?;3315let run = instance.get_typed_func::<(), (u32,)>(&mut store, "run")?;3316assert_eq!(run.call(&mut store, ())?, (5,));3317Ok(())3318}33193320enum RecurseKind {3321AThenA,3322AThenB,3323AThenBThenA,3324}33253326#[test]3327fn recurse() -> Result<()> {3328test_recurse(RecurseKind::AThenB)3329}33303331#[test]3332fn recurse_trap() -> Result<()> {3333let error = test_recurse(RecurseKind::AThenA).unwrap_err();33343335assert_eq!(error.downcast::<Trap>()?, Trap::CannotEnterComponent);33363337Ok(())3338}33393340#[test]3341fn recurse_more_trap() -> Result<()> {3342let error = test_recurse(RecurseKind::AThenBThenA).unwrap_err();33433344assert_eq!(error.downcast::<Trap>()?, Trap::CannotEnterComponent);33453346Ok(())3347}33483349fn test_recurse(kind: RecurseKind) -> Result<()> {3350#[derive(Default)]3351struct Ctx {3352instances: Vec<Arc<Instance>>,3353}33543355let component = r#"3356(component3357(import "import" (func $import))3358(core func $import (canon lower (func $import)))3359(core module $m3360(func $import (import "" "import"))3361(func (export "export") call $import)3362)3363(core instance $m (instantiate $m (with "" (instance3364(export "import" (func $import))3365))))3366(func (export "export") (canon lift (core func $m "export")))3367)3368"#;3369let engine = super::engine();3370let component = Component::new(&engine, component)?;3371let mut store = Store::new(&engine, Ctx::default());3372let mut linker = Linker::<Ctx>::new(&engine);3373linker.root().func_wrap("import", |mut store, (): ()| {3374if let Some(instance) = store.data_mut().instances.pop() {3375let run = instance.get_typed_func::<(), ()>(&mut store, "export")?;3376run.call(&mut store, ())?;3377store.data_mut().instances.push(instance);3378}3379Ok(())3380})?;3381let instance = Arc::new(linker.instantiate(&mut store, &component)?);3382let instance = match kind {3383RecurseKind::AThenA => {3384store.data_mut().instances.push(instance.clone());3385instance3386}3387RecurseKind::AThenB => {3388let other = Arc::new(linker.instantiate(&mut store, &component)?);3389store.data_mut().instances.push(other);3390instance3391}3392RecurseKind::AThenBThenA => {3393store.data_mut().instances.push(instance.clone());3394let other = Arc::new(linker.instantiate(&mut store, &component)?);3395store.data_mut().instances.push(other);3396instance3397}3398};33993400let export = instance.get_typed_func::<(), ()>(&mut store, "export")?;3401export.call(&mut store, ())?;3402Ok(())3403}34043405#[tokio::test]3406async fn thread_index_via_instantiation_sync() -> Result<()> {3407thread_index_via_instantiation(ApiStyle::Sync).await3408}34093410#[tokio::test]3411async fn thread_index_via_instantiation_async() -> Result<()> {3412thread_index_via_instantiation(ApiStyle::Async).await3413}34143415async fn thread_index_via_instantiation(style: ApiStyle) -> Result<()> {3416let component = r#"3417(component3418(core module $m3419(import "" "thread.index" (func $thread-index (result i32)))3420(func $start3421(if (i32.eqz (call $thread-index)) (then unreachable))3422)3423(start $start)3424)3425(core func $thread-index (canon thread.index))3426(core instance $m (instantiate $m (with "" (instance3427(export "thread.index" (func $thread-index))3428))))3429)3430"#;3431let engine = Engine::new(&style.config())?;3432let component = Component::new(&engine, component)?;3433let mut store = Store::new(&engine, ());3434let linker = Linker::new(&engine);3435style.instantiate(&mut store, &linker, &component).await?;3436Ok(())3437}34383439#[tokio::test]3440async fn thread_index_via_call_sync() -> Result<()> {3441thread_index_via_call(ApiStyle::Sync).await3442}34433444#[tokio::test]3445async fn thread_index_via_call_async() -> Result<()> {3446thread_index_via_call(ApiStyle::Async).await3447}34483449#[tokio::test]3450async fn thread_index_via_call_concurrent() -> Result<()> {3451thread_index_via_call(ApiStyle::Concurrent).await3452}34533454async fn thread_index_via_call(style: ApiStyle) -> Result<()> {3455let component = r#"3456(component3457(core module $m3458(import "" "thread.index" (func $thread-index (result i32)))3459(func (export "run")3460(if (i32.eqz (call $thread-index)) (then unreachable))3461)3462)3463(core func $thread-index (canon thread.index))3464(core instance $m (instantiate $m (with "" (instance3465(export "thread.index" (func $thread-index))3466))))3467(func (export "run") (canon lift (core func $m "run")))3468)3469"#;3470let engine = Engine::new(&style.config())?;3471let component = Component::new(&engine, component)?;3472let mut store = Store::new(&engine, ());3473let linker = Linker::new(&engine);3474let instance = style.instantiate(&mut store, &linker, &component).await?;3475let run = instance.get_typed_func::<(), ()>(&mut store, "run")?;3476style.call(&mut store, run, ()).await?;3477Ok(())3478}34793480#[tokio::test]3481async fn thread_index_via_post_return_sync() -> Result<()> {3482thread_index_via_post_return(ApiStyle::Sync).await3483}34843485#[tokio::test]3486async fn thread_index_via_post_return_async() -> Result<()> {3487thread_index_via_post_return(ApiStyle::Async).await3488}34893490#[tokio::test]3491async fn thread_index_via_post_return_concurrent() -> Result<()> {3492thread_index_via_post_return(ApiStyle::Concurrent).await3493}34943495async fn thread_index_via_post_return(style: ApiStyle) -> Result<()> {3496let component = r#"3497(component3498(core module $m3499(import "" "thread.index" (func $thread-index (result i32)))3500(global $index (mut i32) (i32.const 0))3501(func (export "run")3502(global.set $index (call $thread-index))3503(if (i32.eqz (global.get $index)) (then unreachable))3504)3505(func (export "run-post-return")3506(local $index i32)3507(local.set $index (call $thread-index))3508(if (i32.eqz (local.get $index)) (then unreachable))3509(if (i32.ne (local.get $index) (global.get $index)) (then unreachable))3510)3511)3512(core func $thread-index (canon thread.index))3513(core instance $m (instantiate $m (with "" (instance3514(export "thread.index" (func $thread-index))3515))))3516(func (export "run") (canon lift (core func $m "run") (post-return (func $m "run-post-return"))))3517)3518"#;3519let engine = Engine::new(&style.config())?;3520let component = Component::new(&engine, component)?;3521let mut store = Store::new(&engine, ());3522let linker = Linker::new(&engine);3523let instance = style.instantiate(&mut store, &linker, &component).await?;3524let run = instance.get_typed_func::<(), ()>(&mut store, "run")?;3525style.call(&mut store, run, ()).await?;3526Ok(())3527}35283529#[tokio::test]3530async fn thread_index_via_cabi_realloc_sync() -> Result<()> {3531thread_index_via_cabi_realloc(ApiStyle::Sync).await3532}35333534#[tokio::test]3535async fn thread_index_via_cabi_realloc_async() -> Result<()> {3536thread_index_via_cabi_realloc(ApiStyle::Async).await3537}35383539#[tokio::test]3540async fn thread_index_via_cabi_realloc_concurrent() -> Result<()> {3541thread_index_via_cabi_realloc(ApiStyle::Concurrent).await3542}35433544async fn thread_index_via_cabi_realloc(style: ApiStyle) -> Result<()> {3545let component = r#"3546(component3547(core module $m3548(import "" "thread.index" (func $thread-index (result i32)))3549(global $index (mut i32) (i32.const 0))3550(memory (export "memory") 1)3551(func (export "realloc") (param i32 i32 i32 i32) (result i32)3552(global.set $index (call $thread-index))3553(if (i32.eqz (global.get $index)) (then unreachable))3554(i32.const 100)3555)3556(func (export "run") (param i32 i32)3557(local $index i32)3558(local.set $index (call $thread-index))3559(if (i32.eqz (local.get $index)) (then unreachable))3560(if (i32.ne (local.get $index) (global.get $index)) (then unreachable))3561)3562)3563(core func $thread-index (canon thread.index))3564(core instance $m (instantiate $m (with "" (instance3565(export "thread.index" (func $thread-index))3566))))3567(func (export "run") (param "s" string) (canon lift3568(core func $m "run")3569(memory $m "memory")3570(realloc (func $m "realloc"))3571))3572)3573"#;3574let engine = Engine::new(&style.config())?;3575let component = Component::new(&engine, component)?;3576let mut store = Store::new(&engine, ());3577let linker = Linker::new(&engine);3578let instance = style.instantiate(&mut store, &linker, &component).await?;3579let run = instance.get_typed_func::<(String,), ()>(&mut store, "run")?;3580style.call(&mut store, run, ("hola".to_string(),)).await?;3581Ok(())3582}35833584#[tokio::test]3585async fn thread_index_via_resource_drop_sync() -> Result<()> {3586thread_index_via_resource_drop(ApiStyle::Sync).await3587}35883589#[tokio::test]3590async fn thread_index_via_resource_drop_async() -> Result<()> {3591thread_index_via_resource_drop(ApiStyle::Async).await3592}35933594#[tokio::test]3595async fn thread_index_via_resource_drop_concurrent() -> Result<()> {3596thread_index_via_resource_drop(ApiStyle::Concurrent).await3597}35983599async fn thread_index_via_resource_drop(style: ApiStyle) -> Result<()> {3600let component = r#"3601(component3602(core module $m3603(import "" "thread.index" (func $thread-index (result i32)))3604(func (export "dtor") (param i32)3605(if (i32.eqz (call $thread-index)) (then unreachable))3606)3607)3608(core func $thread-index (canon thread.index))3609(core instance $m (instantiate $m (with "" (instance3610(export "thread.index" (func $thread-index))3611))))3612(type $r (resource (rep i32) (dtor (func $m "dtor"))))3613(core func $new (canon resource.new $r))3614(core module $m23615(import "" "new" (func $new (param i32) (result i32)))3616(func (export "new") (result i32)3617(call $new (i32.const 100))3618)3619)3620(core instance $m2 (instantiate $m2 (with "" (instance3621(export "new" (func $new))3622))))3623(func $new (result (own $r)) (canon lift (core func $m2 "new")))3624(component $c3625(import "r" (type $r (sub resource)))3626(import "new" (func $new (result (own $r))))3627(export $r-export "r" (type $r))3628(export "new" (func $new) (func (result (own $r-export))))3629)3630(instance $c (instantiate $c3631(with "r" (type $r))3632(with "new" (func $new))3633))3634(export "i" (instance $c))3635)3636"#;3637let engine = Engine::new(&style.config())?;3638let component = Component::new(&engine, component)?;3639let mut store = Store::new(&engine, ());3640let linker = Linker::new(&engine);3641let instance = style.instantiate(&mut store, &linker, &component).await?;3642let instance_index = instance.get_export_index(&mut store, None, "i").unwrap();3643let func_index = instance3644.get_export_index(&mut store, Some(&instance_index), "new")3645.unwrap();3646let run = instance.get_typed_func::<(), (ResourceAny,)>(&mut store, &func_index)?;3647let (resource,) = style.call(&mut store, run, ()).await?;3648style.resource_drop(&mut store, resource).await?;3649Ok(())3650}36513652#[tokio::test]3653async fn thread_index_via_guest_call_sync() -> Result<()> {3654thread_index_via_guest_call(ApiStyle::Sync).await3655}36563657#[tokio::test]3658async fn thread_index_via_guest_call_async() -> Result<()> {3659thread_index_via_guest_call(ApiStyle::Async).await3660}36613662#[tokio::test]3663async fn thread_index_via_guest_call_concurrent() -> Result<()> {3664thread_index_via_guest_call(ApiStyle::Concurrent).await3665}36663667async fn thread_index_via_guest_call(style: ApiStyle) -> Result<()> {3668let component = r#"3669(component3670(component $c3671(core module $m3672(import "" "thread.index" (func $thread-index (result i32)))3673(func (export "run") (result i32)3674(call $thread-index)3675)3676)3677(core func $thread-index (canon thread.index))3678(core instance $m (instantiate $m (with "" (instance3679(export "thread.index" (func $thread-index))3680))))3681(func (export "run") (result u32) (canon lift (core func $m "run")))3682)3683(instance $c (instantiate $c))36843685(component $d3686(import "c" (instance $c3687(export "run" (func (result u32)))3688))3689(core func $run (canon lower (func $c "run")))3690(core module $m3691(import "" "thread.index" (func $thread-index (result i32)))3692(import "" "run" (func $run (result i32)))3693(func (export "run")3694(local $mine i32)3695(local $theirs i32)3696(local.set $mine (call $thread-index))3697(if (i32.eqz (local.get $mine)) (then unreachable))3698(local.set $theirs (call $run))3699(if (i32.eqz (local.get $theirs)) (then unreachable))3700)3701)3702(core func $thread-index (canon thread.index))3703(core instance $m (instantiate $m (with "" (instance3704(export "thread.index" (func $thread-index))3705(export "run" (func $run))3706))))3707(func (export "run") (canon lift (core func $m "run")))3708)3709(instance $d (instantiate $d (with "c" (instance $c))))3710(func (export "run") (alias export $d "run"))3711)3712"#;3713let engine = Engine::new(&style.config())?;3714let component = Component::new(&engine, component)?;3715let mut store = Store::new(&engine, ());3716let linker = Linker::new(&engine);3717let instance = style.instantiate(&mut store, &linker, &component).await?;3718let run = instance.get_typed_func::<(), ()>(&mut store, "run")?;3719style.call(&mut store, run, ()).await?;3720Ok(())3721}37223723fn with_new_instance<T>(3724engine: &Engine,3725component: &Component,3726fun: impl Fn(&mut Store<()>, Instance) -> wasmtime::Result<T>,3727) -> wasmtime::Result<T> {3728let mut store = Store::new(engine, ());3729let instance = Linker::new(engine).instantiate(&mut store, component)?;3730fun(&mut store, instance)3731}373237333734