1 #![cfg(not(miri))] 2 3 use super::{REALLOC_AND_FREE, TypedFuncExt}; 4 use anyhow::Result; 5 use std::sync::Arc; 6 use wasmtime::component::*; 7 use wasmtime::{Config, Engine, Store, StoreContextMut, Trap}; 8 9 const CANON_32BIT_NAN: u32 = 0b01111111110000000000000000000000; 10 const CANON_64BIT_NAN: u64 = 0b0111111111111000000000000000000000000000000000000000000000000000; 11 12 #[test] 13 fn thunks() -> Result<()> { 14 let component = r#" 15 (component 16 (core module $m 17 (func (export "thunk")) 18 (func (export "thunk-trap") unreachable) 19 ) 20 (core instance $i (instantiate $m)) 21 (func (export "thunk") 22 (canon lift (core func $i "thunk")) 23 ) 24 (func (export "thunk-trap") 25 (canon lift (core func $i "thunk-trap")) 26 ) 27 ) 28 "#; 29 30 let engine = super::engine(); 31 let component = Component::new(&engine, component)?; 32 let mut store = Store::new(&engine, ()); 33 let instance = Linker::new(&engine).instantiate(&mut store, &component)?; 34 instance 35 .get_typed_func::<(), ()>(&mut store, "thunk")? 36 .call_and_post_return(&mut store, ())?; 37 let err = instance 38 .get_typed_func::<(), ()>(&mut store, "thunk-trap")? 39 .call(&mut store, ()) 40 .unwrap_err(); 41 assert_eq!(err.downcast::<Trap>()?, Trap::UnreachableCodeReached); 42 43 Ok(()) 44 } 45 46 #[test] 47 fn typecheck() -> Result<()> { 48 let component = r#" 49 (component 50 (core module $m 51 (func (export "thunk")) 52 (func (export "take-string") (param i32 i32)) 53 (func (export "two-args") (param i32 i32 i32)) 54 (func (export "ret-one") (result i32) unreachable) 55 56 (memory (export "memory") 1) 57 (func (export "realloc") (param i32 i32 i32 i32) (result i32) 58 unreachable) 59 ) 60 (core instance $i (instantiate (module $m))) 61 (func (export "thunk") 62 (canon lift (core func $i "thunk")) 63 ) 64 (func (export "take-string") (param "a" string) 65 (canon lift (core func $i "take-string") (memory $i "memory") (realloc (func $i "realloc"))) 66 ) 67 (func (export "take-two-args") (param "a" s32) (param "b" (list u8)) 68 (canon lift (core func $i "two-args") (memory $i "memory") (realloc (func $i "realloc"))) 69 ) 70 (func (export "ret-tuple") (result (tuple u8 s8)) 71 (canon lift (core func $i "ret-one") (memory $i "memory") (realloc (func $i "realloc"))) 72 ) 73 (func (export "ret-tuple1") (result (tuple u32)) 74 (canon lift (core func $i "ret-one") (memory $i "memory") (realloc (func $i "realloc"))) 75 ) 76 (func (export "ret-string") (result string) 77 (canon lift (core func $i "ret-one") (memory $i "memory") (realloc (func $i "realloc"))) 78 ) 79 (func (export "ret-list-u8") (result (list u8)) 80 (canon lift (core func $i "ret-one") (memory $i "memory") (realloc (func $i "realloc"))) 81 ) 82 ) 83 "#; 84 85 let engine = Engine::default(); 86 let component = Component::new(&engine, component)?; 87 let mut store = Store::new(&engine, ()); 88 let instance = Linker::new(&engine).instantiate(&mut store, &component)?; 89 let thunk = instance.get_func(&mut store, "thunk").unwrap(); 90 let take_string = instance.get_func(&mut store, "take-string").unwrap(); 91 let take_two_args = instance.get_func(&mut store, "take-two-args").unwrap(); 92 let ret_tuple = instance.get_func(&mut store, "ret-tuple").unwrap(); 93 let ret_tuple1 = instance.get_func(&mut store, "ret-tuple1").unwrap(); 94 let ret_string = instance.get_func(&mut store, "ret-string").unwrap(); 95 let ret_list_u8 = instance.get_func(&mut store, "ret-list-u8").unwrap(); 96 assert!(thunk.typed::<(), (u32,)>(&store).is_err()); 97 assert!(thunk.typed::<(u32,), ()>(&store).is_err()); 98 assert!(thunk.typed::<(), ()>(&store).is_ok()); 99 assert!(take_string.typed::<(), ()>(&store).is_err()); 100 assert!(take_string.typed::<(String,), ()>(&store).is_ok()); 101 assert!(take_string.typed::<(&str,), ()>(&store).is_ok()); 102 assert!(take_string.typed::<(&[u8],), ()>(&store).is_err()); 103 assert!(take_two_args.typed::<(), ()>(&store).is_err()); 104 assert!(take_two_args.typed::<(i32, &[u8]), (u32,)>(&store).is_err()); 105 assert!(take_two_args.typed::<(u32, &[u8]), ()>(&store).is_err()); 106 assert!(take_two_args.typed::<(i32, &[u8]), ()>(&store).is_ok()); 107 assert!(ret_tuple.typed::<(), ()>(&store).is_err()); 108 assert!(ret_tuple.typed::<(), (u8,)>(&store).is_err()); 109 assert!(ret_tuple.typed::<(), ((u8, i8),)>(&store).is_ok()); 110 assert!(ret_tuple1.typed::<(), ((u32,),)>(&store).is_ok()); 111 assert!(ret_tuple1.typed::<(), (u32,)>(&store).is_err()); 112 assert!(ret_string.typed::<(), ()>(&store).is_err()); 113 assert!(ret_string.typed::<(), (WasmStr,)>(&store).is_ok()); 114 assert!(ret_list_u8.typed::<(), (WasmList<u16>,)>(&store).is_err()); 115 assert!(ret_list_u8.typed::<(), (WasmList<i8>,)>(&store).is_err()); 116 assert!(ret_list_u8.typed::<(), (WasmList<u8>,)>(&store).is_ok()); 117 118 Ok(()) 119 } 120 121 #[test] 122 fn integers() -> Result<()> { 123 let component = r#" 124 (component 125 (core module $m 126 (func (export "take-i32-100") (param i32) 127 local.get 0 128 i32.const 100 129 i32.eq 130 br_if 0 131 unreachable 132 ) 133 (func (export "take-i64-100") (param i64) 134 local.get 0 135 i64.const 100 136 i64.eq 137 br_if 0 138 unreachable 139 ) 140 (func (export "ret-i32-0") (result i32) i32.const 0) 141 (func (export "ret-i64-0") (result i64) i64.const 0) 142 (func (export "ret-i32-minus-1") (result i32) i32.const -1) 143 (func (export "ret-i64-minus-1") (result i64) i64.const -1) 144 (func (export "ret-i32-100000") (result i32) i32.const 100000) 145 ) 146 (core instance $i (instantiate (module $m))) 147 (func (export "take-u8") (param "a" u8) (canon lift (core func $i "take-i32-100"))) 148 (func (export "take-s8") (param "a" s8) (canon lift (core func $i "take-i32-100"))) 149 (func (export "take-u16") (param "a" u16) (canon lift (core func $i "take-i32-100"))) 150 (func (export "take-s16") (param "a" s16) (canon lift (core func $i "take-i32-100"))) 151 (func (export "take-u32") (param "a" u32) (canon lift (core func $i "take-i32-100"))) 152 (func (export "take-s32") (param "a" s32) (canon lift (core func $i "take-i32-100"))) 153 (func (export "take-u64") (param "a" u64) (canon lift (core func $i "take-i64-100"))) 154 (func (export "take-s64") (param "a" s64) (canon lift (core func $i "take-i64-100"))) 155 156 (func (export "ret-u8") (result u8) (canon lift (core func $i "ret-i32-0"))) 157 (func (export "ret-s8") (result s8) (canon lift (core func $i "ret-i32-0"))) 158 (func (export "ret-u16") (result u16) (canon lift (core func $i "ret-i32-0"))) 159 (func (export "ret-s16") (result s16) (canon lift (core func $i "ret-i32-0"))) 160 (func (export "ret-u32") (result u32) (canon lift (core func $i "ret-i32-0"))) 161 (func (export "ret-s32") (result s32) (canon lift (core func $i "ret-i32-0"))) 162 (func (export "ret-u64") (result u64) (canon lift (core func $i "ret-i64-0"))) 163 (func (export "ret-s64") (result s64) (canon lift (core func $i "ret-i64-0"))) 164 165 (func (export "retm1-u8") (result u8) (canon lift (core func $i "ret-i32-minus-1"))) 166 (func (export "retm1-s8") (result s8) (canon lift (core func $i "ret-i32-minus-1"))) 167 (func (export "retm1-u16") (result u16) (canon lift (core func $i "ret-i32-minus-1"))) 168 (func (export "retm1-s16") (result s16) (canon lift (core func $i "ret-i32-minus-1"))) 169 (func (export "retm1-u32") (result u32) (canon lift (core func $i "ret-i32-minus-1"))) 170 (func (export "retm1-s32") (result s32) (canon lift (core func $i "ret-i32-minus-1"))) 171 (func (export "retm1-u64") (result u64) (canon lift (core func $i "ret-i64-minus-1"))) 172 (func (export "retm1-s64") (result s64) (canon lift (core func $i "ret-i64-minus-1"))) 173 174 (func (export "retbig-u8") (result u8) (canon lift (core func $i "ret-i32-100000"))) 175 (func (export "retbig-s8") (result s8) (canon lift (core func $i "ret-i32-100000"))) 176 (func (export "retbig-u16") (result u16) (canon lift (core func $i "ret-i32-100000"))) 177 (func (export "retbig-s16") (result s16) (canon lift (core func $i "ret-i32-100000"))) 178 (func (export "retbig-u32") (result u32) (canon lift (core func $i "ret-i32-100000"))) 179 (func (export "retbig-s32") (result s32) (canon lift (core func $i "ret-i32-100000"))) 180 ) 181 "#; 182 183 let engine = super::engine(); 184 let component = Component::new(&engine, component)?; 185 let mut store = Store::new(&engine, ()); 186 let new_instance = |store: &mut Store<()>| Linker::new(&engine).instantiate(store, &component); 187 let instance = new_instance(&mut store)?; 188 189 // Passing in 100 is valid for all primitives 190 instance 191 .get_typed_func::<(u8,), ()>(&mut store, "take-u8")? 192 .call_and_post_return(&mut store, (100,))?; 193 instance 194 .get_typed_func::<(i8,), ()>(&mut store, "take-s8")? 195 .call_and_post_return(&mut store, (100,))?; 196 instance 197 .get_typed_func::<(u16,), ()>(&mut store, "take-u16")? 198 .call_and_post_return(&mut store, (100,))?; 199 instance 200 .get_typed_func::<(i16,), ()>(&mut store, "take-s16")? 201 .call_and_post_return(&mut store, (100,))?; 202 instance 203 .get_typed_func::<(u32,), ()>(&mut store, "take-u32")? 204 .call_and_post_return(&mut store, (100,))?; 205 instance 206 .get_typed_func::<(i32,), ()>(&mut store, "take-s32")? 207 .call_and_post_return(&mut store, (100,))?; 208 instance 209 .get_typed_func::<(u64,), ()>(&mut store, "take-u64")? 210 .call_and_post_return(&mut store, (100,))?; 211 instance 212 .get_typed_func::<(i64,), ()>(&mut store, "take-s64")? 213 .call_and_post_return(&mut store, (100,))?; 214 215 // This specific wasm instance traps if any value other than 100 is passed 216 new_instance(&mut store)? 217 .get_typed_func::<(u8,), ()>(&mut store, "take-u8")? 218 .call(&mut store, (101,)) 219 .unwrap_err() 220 .downcast::<Trap>()?; 221 new_instance(&mut store)? 222 .get_typed_func::<(i8,), ()>(&mut store, "take-s8")? 223 .call(&mut store, (101,)) 224 .unwrap_err() 225 .downcast::<Trap>()?; 226 new_instance(&mut store)? 227 .get_typed_func::<(u16,), ()>(&mut store, "take-u16")? 228 .call(&mut store, (101,)) 229 .unwrap_err() 230 .downcast::<Trap>()?; 231 new_instance(&mut store)? 232 .get_typed_func::<(i16,), ()>(&mut store, "take-s16")? 233 .call(&mut store, (101,)) 234 .unwrap_err() 235 .downcast::<Trap>()?; 236 new_instance(&mut store)? 237 .get_typed_func::<(u32,), ()>(&mut store, "take-u32")? 238 .call(&mut store, (101,)) 239 .unwrap_err() 240 .downcast::<Trap>()?; 241 new_instance(&mut store)? 242 .get_typed_func::<(i32,), ()>(&mut store, "take-s32")? 243 .call(&mut store, (101,)) 244 .unwrap_err() 245 .downcast::<Trap>()?; 246 new_instance(&mut store)? 247 .get_typed_func::<(u64,), ()>(&mut store, "take-u64")? 248 .call(&mut store, (101,)) 249 .unwrap_err() 250 .downcast::<Trap>()?; 251 new_instance(&mut store)? 252 .get_typed_func::<(i64,), ()>(&mut store, "take-s64")? 253 .call(&mut store, (101,)) 254 .unwrap_err() 255 .downcast::<Trap>()?; 256 257 // Zero can be returned as any integer 258 assert_eq!( 259 instance 260 .get_typed_func::<(), (u8,)>(&mut store, "ret-u8")? 261 .call_and_post_return(&mut store, ())?, 262 (0,) 263 ); 264 assert_eq!( 265 instance 266 .get_typed_func::<(), (i8,)>(&mut store, "ret-s8")? 267 .call_and_post_return(&mut store, ())?, 268 (0,) 269 ); 270 assert_eq!( 271 instance 272 .get_typed_func::<(), (u16,)>(&mut store, "ret-u16")? 273 .call_and_post_return(&mut store, ())?, 274 (0,) 275 ); 276 assert_eq!( 277 instance 278 .get_typed_func::<(), (i16,)>(&mut store, "ret-s16")? 279 .call_and_post_return(&mut store, ())?, 280 (0,) 281 ); 282 assert_eq!( 283 instance 284 .get_typed_func::<(), (u32,)>(&mut store, "ret-u32")? 285 .call_and_post_return(&mut store, ())?, 286 (0,) 287 ); 288 assert_eq!( 289 instance 290 .get_typed_func::<(), (i32,)>(&mut store, "ret-s32")? 291 .call_and_post_return(&mut store, ())?, 292 (0,) 293 ); 294 assert_eq!( 295 instance 296 .get_typed_func::<(), (u64,)>(&mut store, "ret-u64")? 297 .call_and_post_return(&mut store, ())?, 298 (0,) 299 ); 300 assert_eq!( 301 instance 302 .get_typed_func::<(), (i64,)>(&mut store, "ret-s64")? 303 .call_and_post_return(&mut store, ())?, 304 (0,) 305 ); 306 307 // Returning -1 should reinterpret the bytes as defined by each type. 308 assert_eq!( 309 instance 310 .get_typed_func::<(), (u8,)>(&mut store, "retm1-u8")? 311 .call_and_post_return(&mut store, ())?, 312 (0xff,) 313 ); 314 assert_eq!( 315 instance 316 .get_typed_func::<(), (i8,)>(&mut store, "retm1-s8")? 317 .call_and_post_return(&mut store, ())?, 318 (-1,) 319 ); 320 assert_eq!( 321 instance 322 .get_typed_func::<(), (u16,)>(&mut store, "retm1-u16")? 323 .call_and_post_return(&mut store, ())?, 324 (0xffff,) 325 ); 326 assert_eq!( 327 instance 328 .get_typed_func::<(), (i16,)>(&mut store, "retm1-s16")? 329 .call_and_post_return(&mut store, ())?, 330 (-1,) 331 ); 332 assert_eq!( 333 instance 334 .get_typed_func::<(), (u32,)>(&mut store, "retm1-u32")? 335 .call_and_post_return(&mut store, ())?, 336 (0xffffffff,) 337 ); 338 assert_eq!( 339 instance 340 .get_typed_func::<(), (i32,)>(&mut store, "retm1-s32")? 341 .call_and_post_return(&mut store, ())?, 342 (-1,) 343 ); 344 assert_eq!( 345 instance 346 .get_typed_func::<(), (u64,)>(&mut store, "retm1-u64")? 347 .call_and_post_return(&mut store, ())?, 348 (0xffffffff_ffffffff,) 349 ); 350 assert_eq!( 351 instance 352 .get_typed_func::<(), (i64,)>(&mut store, "retm1-s64")? 353 .call_and_post_return(&mut store, ())?, 354 (-1,) 355 ); 356 357 // Returning 100000 should chop off bytes as necessary 358 let ret: u32 = 100000; 359 assert_eq!( 360 instance 361 .get_typed_func::<(), (u8,)>(&mut store, "retbig-u8")? 362 .call_and_post_return(&mut store, ())?, 363 (ret as u8,), 364 ); 365 assert_eq!( 366 instance 367 .get_typed_func::<(), (i8,)>(&mut store, "retbig-s8")? 368 .call_and_post_return(&mut store, ())?, 369 (ret as i8,), 370 ); 371 assert_eq!( 372 instance 373 .get_typed_func::<(), (u16,)>(&mut store, "retbig-u16")? 374 .call_and_post_return(&mut store, ())?, 375 (ret as u16,), 376 ); 377 assert_eq!( 378 instance 379 .get_typed_func::<(), (i16,)>(&mut store, "retbig-s16")? 380 .call_and_post_return(&mut store, ())?, 381 (ret as i16,), 382 ); 383 assert_eq!( 384 instance 385 .get_typed_func::<(), (u32,)>(&mut store, "retbig-u32")? 386 .call_and_post_return(&mut store, ())?, 387 (ret,), 388 ); 389 assert_eq!( 390 instance 391 .get_typed_func::<(), (i32,)>(&mut store, "retbig-s32")? 392 .call_and_post_return(&mut store, ())?, 393 (ret as i32,), 394 ); 395 396 Ok(()) 397 } 398 399 #[test] 400 fn type_layers() -> Result<()> { 401 let component = r#" 402 (component 403 (core module $m 404 (func (export "take-i32-100") (param i32) 405 local.get 0 406 i32.const 2 407 i32.eq 408 br_if 0 409 unreachable 410 ) 411 ) 412 (core instance $i (instantiate $m)) 413 (func (export "take-u32") (param "a" u32) (canon lift (core func $i "take-i32-100"))) 414 ) 415 "#; 416 417 let engine = super::engine(); 418 let component = Component::new(&engine, component)?; 419 let mut store = Store::new(&engine, ()); 420 let instance = Linker::new(&engine).instantiate(&mut store, &component)?; 421 422 instance 423 .get_typed_func::<(Box<u32>,), ()>(&mut store, "take-u32")? 424 .call_and_post_return(&mut store, (Box::new(2),))?; 425 instance 426 .get_typed_func::<(&u32,), ()>(&mut store, "take-u32")? 427 .call_and_post_return(&mut store, (&2,))?; 428 instance 429 .get_typed_func::<(Arc<u32>,), ()>(&mut store, "take-u32")? 430 .call_and_post_return(&mut store, (Arc::new(2),))?; 431 instance 432 .get_typed_func::<(&Box<Arc<Box<u32>>>,), ()>(&mut store, "take-u32")? 433 .call_and_post_return(&mut store, (&Box::new(Arc::new(Box::new(2))),))?; 434 435 Ok(()) 436 } 437 438 #[test] 439 fn floats() -> Result<()> { 440 let component = r#" 441 (component 442 (core module $m 443 (func (export "i32.reinterpret_f32") (param f32) (result i32) 444 local.get 0 445 i32.reinterpret_f32 446 ) 447 (func (export "i64.reinterpret_f64") (param f64) (result i64) 448 local.get 0 449 i64.reinterpret_f64 450 ) 451 (func (export "f32.reinterpret_i32") (param i32) (result f32) 452 local.get 0 453 f32.reinterpret_i32 454 ) 455 (func (export "f64.reinterpret_i64") (param i64) (result f64) 456 local.get 0 457 f64.reinterpret_i64 458 ) 459 ) 460 (core instance $i (instantiate $m)) 461 462 (func (export "f32-to-u32") (param "a" float32) (result u32) 463 (canon lift (core func $i "i32.reinterpret_f32")) 464 ) 465 (func (export "f64-to-u64") (param "a" float64) (result u64) 466 (canon lift (core func $i "i64.reinterpret_f64")) 467 ) 468 (func (export "u32-to-f32") (param "a" u32) (result float32) 469 (canon lift (core func $i "f32.reinterpret_i32")) 470 ) 471 (func (export "u64-to-f64") (param "a" u64) (result float64) 472 (canon lift (core func $i "f64.reinterpret_i64")) 473 ) 474 ) 475 "#; 476 477 let engine = super::engine(); 478 let component = Component::new(&engine, component)?; 479 let mut store = Store::new(&engine, ()); 480 let instance = Linker::new(&engine).instantiate(&mut store, &component)?; 481 let f32_to_u32 = instance.get_typed_func::<(f32,), (u32,)>(&mut store, "f32-to-u32")?; 482 let f64_to_u64 = instance.get_typed_func::<(f64,), (u64,)>(&mut store, "f64-to-u64")?; 483 let u32_to_f32 = instance.get_typed_func::<(u32,), (f32,)>(&mut store, "u32-to-f32")?; 484 let u64_to_f64 = instance.get_typed_func::<(u64,), (f64,)>(&mut store, "u64-to-f64")?; 485 486 assert_eq!(f32_to_u32.call(&mut store, (1.0,))?, (1.0f32.to_bits(),)); 487 f32_to_u32.post_return(&mut store)?; 488 assert_eq!(f64_to_u64.call(&mut store, (2.0,))?, (2.0f64.to_bits(),)); 489 f64_to_u64.post_return(&mut store)?; 490 assert_eq!(u32_to_f32.call(&mut store, (3.0f32.to_bits(),))?, (3.0,)); 491 u32_to_f32.post_return(&mut store)?; 492 assert_eq!(u64_to_f64.call(&mut store, (4.0f64.to_bits(),))?, (4.0,)); 493 u64_to_f64.post_return(&mut store)?; 494 495 assert_eq!( 496 u32_to_f32 497 .call(&mut store, (CANON_32BIT_NAN | 1,))? 498 .0 499 .to_bits(), 500 CANON_32BIT_NAN | 1 501 ); 502 u32_to_f32.post_return(&mut store)?; 503 assert_eq!( 504 u64_to_f64 505 .call(&mut store, (CANON_64BIT_NAN | 1,))? 506 .0 507 .to_bits(), 508 CANON_64BIT_NAN | 1, 509 ); 510 u64_to_f64.post_return(&mut store)?; 511 512 assert_eq!( 513 f32_to_u32.call(&mut store, (f32::from_bits(CANON_32BIT_NAN | 1),))?, 514 (CANON_32BIT_NAN | 1,) 515 ); 516 f32_to_u32.post_return(&mut store)?; 517 assert_eq!( 518 f64_to_u64.call(&mut store, (f64::from_bits(CANON_64BIT_NAN | 1),))?, 519 (CANON_64BIT_NAN | 1,) 520 ); 521 f64_to_u64.post_return(&mut store)?; 522 523 Ok(()) 524 } 525 526 #[test] 527 fn bools() -> Result<()> { 528 let component = r#" 529 (component 530 (core module $m 531 (func (export "pass") (param i32) (result i32) local.get 0) 532 ) 533 (core instance $i (instantiate $m)) 534 535 (func (export "u32-to-bool") (param "a" u32) (result bool) 536 (canon lift (core func $i "pass")) 537 ) 538 (func (export "bool-to-u32") (param "a" bool) (result u32) 539 (canon lift (core func $i "pass")) 540 ) 541 ) 542 "#; 543 544 let engine = super::engine(); 545 let component = Component::new(&engine, component)?; 546 let mut store = Store::new(&engine, ()); 547 let instance = Linker::new(&engine).instantiate(&mut store, &component)?; 548 let u32_to_bool = instance.get_typed_func::<(u32,), (bool,)>(&mut store, "u32-to-bool")?; 549 let bool_to_u32 = instance.get_typed_func::<(bool,), (u32,)>(&mut store, "bool-to-u32")?; 550 551 assert_eq!(bool_to_u32.call(&mut store, (false,))?, (0,)); 552 bool_to_u32.post_return(&mut store)?; 553 assert_eq!(bool_to_u32.call(&mut store, (true,))?, (1,)); 554 bool_to_u32.post_return(&mut store)?; 555 assert_eq!(u32_to_bool.call(&mut store, (0,))?, (false,)); 556 u32_to_bool.post_return(&mut store)?; 557 assert_eq!(u32_to_bool.call(&mut store, (1,))?, (true,)); 558 u32_to_bool.post_return(&mut store)?; 559 assert_eq!(u32_to_bool.call(&mut store, (2,))?, (true,)); 560 u32_to_bool.post_return(&mut store)?; 561 562 Ok(()) 563 } 564 565 #[test] 566 fn chars() -> Result<()> { 567 let component = r#" 568 (component 569 (core module $m 570 (func (export "pass") (param i32) (result i32) local.get 0) 571 ) 572 (core instance $i (instantiate $m)) 573 574 (func (export "u32-to-char") (param "a" u32) (result char) 575 (canon lift (core func $i "pass")) 576 ) 577 (func (export "char-to-u32") (param "a" char) (result u32) 578 (canon lift (core func $i "pass")) 579 ) 580 ) 581 "#; 582 583 let engine = super::engine(); 584 let component = Component::new(&engine, component)?; 585 let mut store = Store::new(&engine, ()); 586 let instance = Linker::new(&engine).instantiate(&mut store, &component)?; 587 let u32_to_char = instance.get_typed_func::<(u32,), (char,)>(&mut store, "u32-to-char")?; 588 let char_to_u32 = instance.get_typed_func::<(char,), (u32,)>(&mut store, "char-to-u32")?; 589 590 let mut roundtrip = |x: char| -> Result<()> { 591 assert_eq!(char_to_u32.call(&mut store, (x,))?, (x as u32,)); 592 char_to_u32.post_return(&mut store)?; 593 assert_eq!(u32_to_char.call(&mut store, (x as u32,))?, (x,)); 594 u32_to_char.post_return(&mut store)?; 595 Ok(()) 596 }; 597 598 roundtrip('x')?; 599 roundtrip('a')?; 600 roundtrip('\0')?; 601 roundtrip('\n')?; 602 roundtrip('')?; 603 604 let u32_to_char = |store: &mut Store<()>| { 605 Linker::new(&engine) 606 .instantiate(&mut *store, &component)? 607 .get_typed_func::<(u32,), (char,)>(&mut *store, "u32-to-char") 608 }; 609 let err = u32_to_char(&mut store)? 610 .call(&mut store, (0xd800,)) 611 .unwrap_err(); 612 assert!(err.to_string().contains("integer out of range"), "{}", err); 613 let err = u32_to_char(&mut store)? 614 .call(&mut store, (0xdfff,)) 615 .unwrap_err(); 616 assert!(err.to_string().contains("integer out of range"), "{}", err); 617 let err = u32_to_char(&mut store)? 618 .call(&mut store, (0x110000,)) 619 .unwrap_err(); 620 assert!(err.to_string().contains("integer out of range"), "{}", err); 621 let err = u32_to_char(&mut store)? 622 .call(&mut store, (u32::MAX,)) 623 .unwrap_err(); 624 assert!(err.to_string().contains("integer out of range"), "{}", err); 625 626 Ok(()) 627 } 628 629 #[test] 630 fn tuple_result() -> Result<()> { 631 let component = r#" 632 (component 633 (core module $m 634 (memory (export "memory") 1) 635 (func (export "foo") (param i32 i32 f32 f64) (result i32) 636 (local $base i32) 637 (local.set $base (i32.const 8)) 638 (i32.store8 offset=0 (local.get $base) (local.get 0)) 639 (i32.store16 offset=2 (local.get $base) (local.get 1)) 640 (f32.store offset=4 (local.get $base) (local.get 2)) 641 (f64.store offset=8 (local.get $base) (local.get 3)) 642 local.get $base 643 ) 644 645 (func (export "invalid") (result i32) 646 i32.const -8 647 ) 648 ) 649 (core instance $i (instantiate $m)) 650 651 (type $result (tuple s8 u16 float32 float64)) 652 (func (export "tuple") 653 (param "a" s8) (param "b" u16) (param "c" float32) (param "d" float64) (result $result) 654 (canon lift (core func $i "foo") (memory $i "memory")) 655 ) 656 (func (export "invalid") (result $result) 657 (canon lift (core func $i "invalid") (memory $i "memory")) 658 ) 659 ) 660 "#; 661 662 let engine = super::engine(); 663 let component = Component::new(&engine, component)?; 664 let mut store = Store::new(&engine, ()); 665 let instance = Linker::new(&engine).instantiate(&mut store, &component)?; 666 667 let input = (-1, 100, 3.0, 100.0); 668 let output = instance 669 .get_typed_func::<(i8, u16, f32, f64), ((i8, u16, f32, f64),)>(&mut store, "tuple")? 670 .call_and_post_return(&mut store, input)?; 671 assert_eq!((input,), output); 672 673 let invalid_func = 674 instance.get_typed_func::<(), ((i8, u16, f32, f64),)>(&mut store, "invalid")?; 675 let err = invalid_func.call(&mut store, ()).err().unwrap(); 676 assert!( 677 err.to_string().contains("pointer out of bounds of memory"), 678 "{}", 679 err 680 ); 681 682 Ok(()) 683 } 684 685 #[test] 686 fn strings() -> Result<()> { 687 let component = format!( 688 r#"(component 689 (core module $m 690 (memory (export "memory") 1) 691 (func (export "roundtrip") (param i32 i32) (result i32) 692 (local $base i32) 693 (local.set $base 694 (call $realloc 695 (i32.const 0) 696 (i32.const 0) 697 (i32.const 4) 698 (i32.const 8))) 699 (i32.store offset=0 700 (local.get $base) 701 (local.get 0)) 702 (i32.store offset=4 703 (local.get $base) 704 (local.get 1)) 705 (local.get $base) 706 ) 707 708 {REALLOC_AND_FREE} 709 ) 710 (core instance $i (instantiate $m)) 711 712 (func (export "list8-to-str") (param "a" (list u8)) (result string) 713 (canon lift 714 (core func $i "roundtrip") 715 (memory $i "memory") 716 (realloc (func $i "realloc")) 717 ) 718 ) 719 (func (export "str-to-list8") (param "a" string) (result (list u8)) 720 (canon lift 721 (core func $i "roundtrip") 722 (memory $i "memory") 723 (realloc (func $i "realloc")) 724 ) 725 ) 726 (func (export "list16-to-str") (param "a" (list u16)) (result string) 727 (canon lift 728 (core func $i "roundtrip") 729 string-encoding=utf16 730 (memory $i "memory") 731 (realloc (func $i "realloc")) 732 ) 733 ) 734 (func (export "str-to-list16") (param "a" string) (result (list u16)) 735 (canon lift 736 (core func $i "roundtrip") 737 string-encoding=utf16 738 (memory $i "memory") 739 (realloc (func $i "realloc")) 740 ) 741 ) 742 )"# 743 ); 744 745 let engine = super::engine(); 746 let component = Component::new(&engine, component)?; 747 let mut store = Store::new(&engine, ()); 748 let instance = Linker::new(&engine).instantiate(&mut store, &component)?; 749 let list8_to_str = 750 instance.get_typed_func::<(&[u8],), (WasmStr,)>(&mut store, "list8-to-str")?; 751 let str_to_list8 = 752 instance.get_typed_func::<(&str,), (WasmList<u8>,)>(&mut store, "str-to-list8")?; 753 let list16_to_str = 754 instance.get_typed_func::<(&[u16],), (WasmStr,)>(&mut store, "list16-to-str")?; 755 let str_to_list16 = 756 instance.get_typed_func::<(&str,), (WasmList<u16>,)>(&mut store, "str-to-list16")?; 757 758 let mut roundtrip = |x: &str| -> Result<()> { 759 let ret = list8_to_str.call(&mut store, (x.as_bytes(),))?.0; 760 assert_eq!(ret.to_str(&store)?, x); 761 list8_to_str.post_return(&mut store)?; 762 763 let utf16 = x.encode_utf16().collect::<Vec<_>>(); 764 let ret = list16_to_str.call(&mut store, (&utf16[..],))?.0; 765 assert_eq!(ret.to_str(&store)?, x); 766 list16_to_str.post_return(&mut store)?; 767 768 let ret = str_to_list8.call(&mut store, (x,))?.0; 769 assert_eq!( 770 ret.iter(&mut store).collect::<Result<Vec<_>>>()?, 771 x.as_bytes() 772 ); 773 str_to_list8.post_return(&mut store)?; 774 775 let ret = str_to_list16.call(&mut store, (x,))?.0; 776 assert_eq!(ret.iter(&mut store).collect::<Result<Vec<_>>>()?, utf16,); 777 str_to_list16.post_return(&mut store)?; 778 779 Ok(()) 780 }; 781 782 roundtrip("")?; 783 roundtrip("foo")?; 784 roundtrip("hello there")?; 785 roundtrip("")?; 786 roundtrip("Löwe 老虎 Léopard")?; 787 788 let ret = list8_to_str.call(&mut store, (b"\xff",))?.0; 789 let err = ret.to_str(&store).unwrap_err(); 790 assert!(err.to_string().contains("invalid utf-8"), "{}", err); 791 list8_to_str.post_return(&mut store)?; 792 793 let ret = list8_to_str 794 .call(&mut store, (b"hello there \xff invalid",))? 795 .0; 796 let err = ret.to_str(&store).unwrap_err(); 797 assert!(err.to_string().contains("invalid utf-8"), "{}", err); 798 list8_to_str.post_return(&mut store)?; 799 800 let ret = list16_to_str.call(&mut store, (&[0xd800],))?.0; 801 let err = ret.to_str(&store).unwrap_err(); 802 assert!(err.to_string().contains("unpaired surrogate"), "{}", err); 803 list16_to_str.post_return(&mut store)?; 804 805 let ret = list16_to_str.call(&mut store, (&[0xdfff],))?.0; 806 let err = ret.to_str(&store).unwrap_err(); 807 assert!(err.to_string().contains("unpaired surrogate"), "{}", err); 808 list16_to_str.post_return(&mut store)?; 809 810 let ret = list16_to_str.call(&mut store, (&[0xd800, 0xff00],))?.0; 811 let err = ret.to_str(&store).unwrap_err(); 812 assert!(err.to_string().contains("unpaired surrogate"), "{}", err); 813 list16_to_str.post_return(&mut store)?; 814 815 Ok(()) 816 } 817 818 #[tokio::test] 819 async fn async_reentrance() -> Result<()> { 820 _ = env_logger::try_init(); 821 822 let component = r#" 823 (component 824 (core module $shim 825 (import "" "task.return" (func $task-return (param i32))) 826 (table (export "funcs") 1 1 funcref) 827 (func (export "export") (param i32) (result i32) 828 (call_indirect (i32.const 0) (local.get 0)) 829 ) 830 (func (export "callback") (param i32 i32 i32) (result i32) unreachable) 831 ) 832 (core func $task-return (canon task.return (result u32))) 833 (core instance $shim (instantiate $shim 834 (with "" (instance (export "task.return" (func $task-return)))) 835 )) 836 (func $shim-export (param "p1" u32) (result u32) 837 (canon lift (core func $shim "export") async (callback (func $shim "callback"))) 838 ) 839 840 (component $inner 841 (import "import" (func $import (param "p1" u32) (result u32))) 842 (core module $libc (memory (export "memory") 1)) 843 (core instance $libc (instantiate $libc)) 844 (core func $import (canon lower (func $import) async (memory $libc "memory"))) 845 846 (core module $m 847 (import "libc" "memory" (memory 1)) 848 (import "" "import" (func $import (param i32 i32) (result i32))) 849 (import "" "task.return" (func $task-return (param i32))) 850 (func (export "export") (param i32) (result i32) 851 (i32.store offset=0 (i32.const 1200) (local.get 0)) 852 (call $import (i32.const 1200) (i32.const 1204)) 853 drop 854 (call $task-return (i32.load offset=0 (i32.const 1204))) 855 i32.const 0 856 ) 857 (func (export "callback") (param i32 i32 i32) (result i32) unreachable) 858 ) 859 (core type $task-return-type (func (param i32))) 860 (core func $task-return (canon task.return (result u32))) 861 (core instance $i (instantiate $m 862 (with "" (instance 863 (export "task.return" (func $task-return)) 864 (export "import" (func $import)) 865 )) 866 (with "libc" (instance $libc)) 867 )) 868 (func (export "export") (param "p1" u32) (result u32) 869 (canon lift (core func $i "export") async (callback (func $i "callback"))) 870 ) 871 ) 872 (instance $inner (instantiate $inner (with "import" (func $shim-export)))) 873 874 (core module $libc (memory (export "memory") 1)) 875 (core instance $libc (instantiate $libc)) 876 (core func $inner-export (canon lower (func $inner "export") async (memory $libc "memory"))) 877 878 (core module $donut 879 (import "" "funcs" (table 1 1 funcref)) 880 (import "libc" "memory" (memory 1)) 881 (import "" "import" (func $import (param i32 i32) (result i32))) 882 (import "" "task.return" (func $task-return (param i32))) 883 (func $host-export (export "export") (param i32) (result i32) 884 (i32.store offset=0 (i32.const 1200) (local.get 0)) 885 (call $import (i32.const 1200) (i32.const 1204)) 886 drop 887 (call $task-return (i32.load offset=0 (i32.const 1204))) 888 i32.const 0 889 ) 890 (func $guest-export (export "guest-export") (param i32) (result i32) unreachable) 891 (func (export "callback") (param i32 i32 i32) (result i32) unreachable) 892 (func $start 893 (table.set (i32.const 0) (ref.func $guest-export)) 894 ) 895 (start $start) 896 ) 897 898 (core instance $donut (instantiate $donut 899 (with "" (instance 900 (export "task.return" (func $task-return)) 901 (export "import" (func $inner-export)) 902 (export "funcs" (table $shim "funcs")) 903 )) 904 (with "libc" (instance $libc)) 905 )) 906 (func (export "export") (param "p1" u32) (result u32) 907 (canon lift (core func $donut "export") async (callback (func $donut "callback"))) 908 ) 909 )"#; 910 911 let mut config = Config::new(); 912 config.wasm_component_model_async(true); 913 config.wasm_component_model_async_stackful(true); 914 config.wasm_component_model_threading(true); 915 config.async_support(true); 916 let engine = &Engine::new(&config)?; 917 let component = Component::new(&engine, component)?; 918 let mut store = Store::new(&engine, ()); 919 920 let instance = Linker::new(&engine) 921 .instantiate_async(&mut store, &component) 922 .await?; 923 let func = instance.get_typed_func::<(u32,), (u32,)>(&mut store, "export")?; 924 let message = "cannot enter component instance"; 925 match store 926 .run_concurrent(async move |accessor| { 927 anyhow::Ok(func.call_concurrent(accessor, (42,)).await?.0) 928 }) 929 .await 930 { 931 Ok(_) => panic!(), 932 Err(e) => assert!( 933 format!("{e:?}").contains(message), 934 "expected `{message}`; got `{e:?}`" 935 ), 936 } 937 938 Ok(()) 939 } 940 941 #[tokio::test] 942 async fn missing_task_return_call_stackless() -> Result<()> { 943 task_return_trap( 944 r#"(component 945 (core module $m 946 (import "" "task.return" (func $task-return)) 947 (func (export "foo") (result i32) 948 i32.const 0 949 ) 950 (func (export "callback") (param i32 i32 i32) (result i32) unreachable) 951 ) 952 (core func $task-return (canon task.return)) 953 (core instance $i (instantiate $m 954 (with "" (instance (export "task.return" (func $task-return)))) 955 )) 956 (func (export "foo") (canon lift (core func $i "foo") async (callback (func $i "callback")))) 957 )"#, 958 "wasm trap: async-lifted export failed to produce a result", 959 ) 960 .await 961 } 962 963 #[tokio::test] 964 async fn missing_task_return_call_stackless_explicit_thread() -> Result<()> { 965 task_return_trap( 966 r#"(component 967 (core module $libc 968 (table (export "__indirect_function_table") 1 funcref)) 969 (core module $m 970 (import "" "task.return" (func $task-return)) 971 (import "" "thread.new-indirect" (func $thread-new-indirect (param i32 i32) (result i32))) 972 (import "" "thread.resume-later" (func $thread-resume-later (param i32))) 973 (import "libc" "__indirect_function_table" (table $indirect-function-table 1 funcref)) 974 (func $thread-start (param i32) (; empty ;)) 975 (elem (table $indirect-function-table) (i32.const 0) func $thread-start) 976 (func (export "foo") (result i32) 977 (call $thread-resume-later 978 (call $thread-new-indirect (i32.const 0) (i32.const 0))) 979 i32.const 0 980 ) 981 (func (export "callback") (param i32 i32 i32) (result i32) unreachable) 982 ) 983 (core instance $libc (instantiate $libc)) 984 (core type $start-func-ty (func (param i32))) 985 (alias core export $libc "__indirect_function_table" (core table $indirect-function-table)) 986 (core func $thread-new-indirect 987 (canon thread.new-indirect $start-func-ty (table $indirect-function-table))) 988 (core func $thread-resume-later (canon thread.resume-later)) 989 (core func $task-return (canon task.return)) 990 (core instance $i (instantiate $m 991 (with "" (instance 992 (export "thread.new-indirect" (func $thread-new-indirect)) 993 (export "thread.resume-later" (func $thread-resume-later)) 994 (export "task.return" (func $task-return)) 995 )) 996 (with "libc" (instance $libc)) 997 )) 998 (func (export "foo") (canon lift (core func $i "foo") async (callback (func $i "callback")))) 999 )"#, 1000 "wasm trap: async-lifted export failed to produce a result", 1001 ) 1002 .await 1003 } 1004 1005 #[tokio::test] 1006 async fn missing_task_return_call_stackful_explicit_thread() -> Result<()> { 1007 task_return_trap( 1008 r#"(component 1009 (core module $libc 1010 (table (export "__indirect_function_table") 1 funcref)) 1011 (core module $m 1012 (import "" "task.return" (func $task-return)) 1013 (import "" "thread.new-indirect" (func $thread-new-indirect (param i32 i32) (result i32))) 1014 (import "" "thread.resume-later" (func $thread-resume-later (param i32))) 1015 (import "libc" "__indirect_function_table" (table $indirect-function-table 1 funcref)) 1016 (func $thread-start (param i32) (; empty ;)) 1017 (elem (table $indirect-function-table) (i32.const 0) func $thread-start) 1018 (func (export "foo") 1019 (call $thread-resume-later 1020 (call $thread-new-indirect (i32.const 0) (i32.const 0))) 1021 ) 1022 ) 1023 (core instance $libc (instantiate $libc)) 1024 (core type $start-func-ty (func (param i32))) 1025 (alias core export $libc "__indirect_function_table" (core table $indirect-function-table)) 1026 (core func $thread-new-indirect 1027 (canon thread.new-indirect $start-func-ty (table $indirect-function-table))) 1028 (core func $thread-resume-later (canon thread.resume-later)) 1029 (core func $task-return (canon task.return)) 1030 (core instance $i (instantiate $m 1031 (with "" (instance 1032 (export "thread.new-indirect" (func $thread-new-indirect)) 1033 (export "thread.resume-later" (func $thread-resume-later)) 1034 (export "task.return" (func $task-return)) 1035 )) 1036 (with "libc" (instance $libc)) 1037 )) 1038 (func (export "foo") (canon lift (core func $i "foo") async)) 1039 )"#, 1040 "wasm trap: async-lifted export failed to produce a result", 1041 ) 1042 .await 1043 } 1044 1045 #[tokio::test] 1046 async fn missing_task_return_call_stackful() -> Result<()> { 1047 task_return_trap( 1048 r#"(component 1049 (core module $m 1050 (import "" "task.return" (func $task-return)) 1051 (func (export "foo")) 1052 ) 1053 (core func $task-return (canon task.return)) 1054 (core instance $i (instantiate $m 1055 (with "" (instance (export "task.return" (func $task-return)))) 1056 )) 1057 (func (export "foo") (canon lift (core func $i "foo") async)) 1058 )"#, 1059 "wasm trap: async-lifted export failed to produce a result", 1060 ) 1061 .await 1062 } 1063 1064 #[tokio::test] 1065 async fn task_return_type_mismatch() -> Result<()> { 1066 task_return_trap( 1067 r#"(component 1068 (core module $m 1069 (import "" "task.return" (func $task-return (param i32))) 1070 (func (export "foo") (call $task-return (i32.const 42))) 1071 ) 1072 (core func $task-return (canon task.return (result u32))) 1073 (core instance $i (instantiate $m 1074 (with "" (instance (export "task.return" (func $task-return)))) 1075 )) 1076 (func (export "foo") (canon lift (core func $i "foo") async)) 1077 )"#, 1078 "invalid `task.return` signature and/or options for current task", 1079 ) 1080 .await 1081 } 1082 1083 #[tokio::test] 1084 async fn task_return_memory_mismatch() -> Result<()> { 1085 task_return_trap( 1086 r#"(component 1087 (core module $libc (memory (export "memory") 1)) 1088 (core instance $libc (instantiate $libc)) 1089 (core module $m 1090 (import "" "task.return" (func $task-return)) 1091 (func (export "foo") (call $task-return)) 1092 ) 1093 (core func $task-return (canon task.return (memory $libc "memory"))) 1094 (core instance $i (instantiate $m 1095 (with "" (instance (export "task.return" (func $task-return)))) 1096 )) 1097 (func (export "foo") (canon lift (core func $i "foo") async)) 1098 )"#, 1099 "invalid `task.return` signature and/or options for current task", 1100 ) 1101 .await 1102 } 1103 1104 #[tokio::test] 1105 async fn task_return_string_encoding_mismatch() -> Result<()> { 1106 task_return_trap( 1107 r#"(component 1108 (core module $m 1109 (import "" "task.return" (func $task-return)) 1110 (func (export "foo") (call $task-return)) 1111 ) 1112 (core func $task-return (canon task.return string-encoding=utf16)) 1113 (core instance $i (instantiate $m 1114 (with "" (instance (export "task.return" (func $task-return)))) 1115 )) 1116 (func (export "foo") (canon lift (core func $i "foo") async)) 1117 )"#, 1118 "invalid `task.return` signature and/or options for current task", 1119 ) 1120 .await 1121 } 1122 1123 async fn task_return_trap(component: &str, substring: &str) -> Result<()> { 1124 let mut config = Config::new(); 1125 config.wasm_component_model_async(true); 1126 config.wasm_component_model_async_stackful(true); 1127 config.wasm_component_model_threading(true); 1128 config.async_support(true); 1129 let engine = &Engine::new(&config)?; 1130 let component = Component::new(&engine, component)?; 1131 let mut store = Store::new(&engine, ()); 1132 1133 let instance = Linker::new(&engine) 1134 .instantiate_async(&mut store, &component) 1135 .await?; 1136 1137 let func = instance.get_typed_func::<(), ()>(&mut store, "foo")?; 1138 match store 1139 .run_concurrent(async move |accessor| { 1140 anyhow::Ok(func.call_concurrent(accessor, ()).await?.0) 1141 }) 1142 .await 1143 { 1144 Ok(_) => panic!(), 1145 Err(e) => { 1146 assert!( 1147 format!("{e:?}").contains(substring), 1148 "could not find `{substring}` in `{e:?}`" 1149 ) 1150 } 1151 } 1152 1153 Ok(()) 1154 } 1155 1156 #[tokio::test] 1157 async fn many_parameters() -> Result<()> { 1158 test_many_parameters(false, false).await 1159 } 1160 1161 #[tokio::test] 1162 async fn many_parameters_concurrent() -> Result<()> { 1163 test_many_parameters(false, true).await 1164 } 1165 1166 #[tokio::test] 1167 async fn many_parameters_dynamic() -> Result<()> { 1168 test_many_parameters(true, false).await 1169 } 1170 1171 #[tokio::test] 1172 async fn many_parameters_dynamic_concurrent() -> Result<()> { 1173 test_many_parameters(true, true).await 1174 } 1175 1176 async fn test_many_parameters(dynamic: bool, concurrent: bool) -> Result<()> { 1177 let (body, async_opts) = if concurrent { 1178 ( 1179 r#" 1180 (call $task-return 1181 (i32.const 0) 1182 (i32.mul 1183 (memory.size) 1184 (i32.const 65536) 1185 ) 1186 (local.get 0) 1187 ) 1188 1189 (i32.const 0) 1190 "#, 1191 r#"async (callback (func $i "callback"))"#, 1192 ) 1193 } else { 1194 ( 1195 r#" 1196 (local $base i32) 1197 1198 ;; Allocate space for the return 1199 (local.set $base 1200 (call $realloc 1201 (i32.const 0) 1202 (i32.const 0) 1203 (i32.const 4) 1204 (i32.const 12))) 1205 1206 ;; Store the pointer/length of the entire linear memory 1207 ;; so we have access to everything. 1208 (i32.store offset=0 1209 (local.get $base) 1210 (i32.const 0)) 1211 (i32.store offset=4 1212 (local.get $base) 1213 (i32.mul 1214 (memory.size) 1215 (i32.const 65536))) 1216 1217 ;; And also store our pointer parameter 1218 (i32.store offset=8 1219 (local.get $base) 1220 (local.get 0)) 1221 1222 (local.get $base) 1223 "#, 1224 "", 1225 ) 1226 }; 1227 1228 let component = format!( 1229 r#"(component 1230 (core module $libc 1231 (memory (export "memory") 1) 1232 1233 {REALLOC_AND_FREE} 1234 ) 1235 (core instance $libc (instantiate $libc)) 1236 (core module $m 1237 (import "libc" "memory" (memory 1)) 1238 (import "libc" "realloc" (func $realloc (param i32 i32 i32 i32) (result i32))) 1239 (import "" "task.return" (func $task-return (param i32 i32 i32))) 1240 (func (export "foo") (param i32) (result i32) 1241 {body} 1242 ) 1243 (func (export "callback") (param i32 i32 i32) (result i32) unreachable) 1244 ) 1245 (type $tuple (tuple (list u8) u32)) 1246 (core func $task-return (canon task.return 1247 (result $tuple) 1248 (memory $libc "memory") 1249 )) 1250 (core instance $i (instantiate $m 1251 (with "" (instance (export "task.return" (func $task-return)))) 1252 (with "libc" (instance $libc)) 1253 )) 1254 1255 (type $t (func 1256 (param "p1" s8) ;; offset 0, size 1 1257 (param "p2" u64) ;; offset 8, size 8 1258 (param "p3" float32) ;; offset 16, size 4 1259 (param "p4" u8) ;; offset 20, size 1 1260 (param "p5" s16) ;; offset 22, size 2 1261 (param "p6" string) ;; offset 24, size 8 1262 (param "p7" (list u32)) ;; offset 32, size 8 1263 (param "p8" bool) ;; offset 40, size 1 1264 (param "p9" bool) ;; offset 41, size 1 1265 (param "p0" char) ;; offset 44, size 4 1266 (param "pa" (list bool)) ;; offset 48, size 8 1267 (param "pb" (list char)) ;; offset 56, size 8 1268 (param "pc" (list string)) ;; offset 64, size 8 1269 1270 (result $tuple) 1271 )) 1272 (func (export "many-param") (type $t) 1273 (canon lift 1274 (core func $i "foo") 1275 (memory $libc "memory") 1276 (realloc (func $libc "realloc")) 1277 {async_opts} 1278 ) 1279 ) 1280 )"# 1281 ); 1282 1283 let mut config = Config::new(); 1284 config.wasm_component_model_async(true); 1285 config.async_support(true); 1286 let engine = &Engine::new(&config)?; 1287 let component = Component::new(&engine, component)?; 1288 let mut store = Store::new(&engine, ()); 1289 1290 let instance = Linker::new(&engine) 1291 .instantiate_async(&mut store, &component) 1292 .await?; 1293 1294 let input = ( 1295 -100, 1296 u64::MAX / 2, 1297 f32::from_bits(CANON_32BIT_NAN | 1), 1298 38, 1299 18831, 1300 "this is the first string", 1301 [1, 2, 3, 4, 5, 6, 7, 8].as_slice(), 1302 true, 1303 false, 1304 '', 1305 [false, true, false, true, true].as_slice(), 1306 ['', '', '', '', ''].as_slice(), 1307 [ 1308 "the quick", 1309 "brown fox", 1310 "was too lazy", 1311 "to jump over the dog", 1312 "what a demanding dog", 1313 ] 1314 .as_slice(), 1315 ); 1316 1317 let (memory, pointer) = if dynamic { 1318 let input = vec![ 1319 Val::S8(input.0), 1320 Val::U64(input.1), 1321 Val::Float32(input.2), 1322 Val::U8(input.3), 1323 Val::S16(input.4), 1324 Val::String(input.5.into()), 1325 Val::List(input.6.iter().copied().map(Val::U32).collect()), 1326 Val::Bool(input.7), 1327 Val::Bool(input.8), 1328 Val::Char(input.9), 1329 Val::List(input.10.iter().copied().map(Val::Bool).collect()), 1330 Val::List(input.11.iter().copied().map(Val::Char).collect()), 1331 Val::List(input.12.iter().map(|&s| Val::String(s.into())).collect()), 1332 ]; 1333 let func = instance.get_func(&mut store, "many-param").unwrap(); 1334 1335 let mut results = vec![Val::Bool(false)]; 1336 if concurrent { 1337 store 1338 .run_concurrent(async |store| { 1339 func.call_concurrent(store, &input, &mut results).await?; 1340 anyhow::Ok(()) 1341 }) 1342 .await??; 1343 } else { 1344 func.call_async(&mut store, &input, &mut results).await?; 1345 }; 1346 let mut results = results.into_iter(); 1347 let Some(Val::Tuple(results)) = results.next() else { 1348 panic!() 1349 }; 1350 let mut results = results.into_iter(); 1351 let Some(Val::List(memory)) = results.next() else { 1352 panic!() 1353 }; 1354 let Some(Val::U32(pointer)) = results.next() else { 1355 panic!() 1356 }; 1357 ( 1358 memory 1359 .into_iter() 1360 .map(|v| if let Val::U8(v) = v { v } else { panic!() }) 1361 .collect(), 1362 pointer, 1363 ) 1364 } else { 1365 let func = instance.get_typed_func::<( 1366 i8, 1367 u64, 1368 f32, 1369 u8, 1370 i16, 1371 &str, 1372 &[u32], 1373 bool, 1374 bool, 1375 char, 1376 &[bool], 1377 &[char], 1378 &[&str], 1379 ), ((Vec<u8>, u32),)>(&mut store, "many-param")?; 1380 1381 if concurrent { 1382 store 1383 .run_concurrent(async move |accessor| { 1384 anyhow::Ok(func.call_concurrent(accessor, input).await?.0) 1385 }) 1386 .await?? 1387 .0 1388 } else { 1389 func.call_async(&mut store, input).await?.0 1390 } 1391 }; 1392 let memory = &memory[..]; 1393 1394 let mut actual = &memory[pointer as usize..][..72]; 1395 assert_eq!(i8::from_le_bytes(*actual.take_n::<1>()), input.0); 1396 actual.skip::<7>(); 1397 assert_eq!(u64::from_le_bytes(*actual.take_n::<8>()), input.1); 1398 assert_eq!( 1399 u32::from_le_bytes(*actual.take_n::<4>()), 1400 CANON_32BIT_NAN | 1 1401 ); 1402 assert_eq!(u8::from_le_bytes(*actual.take_n::<1>()), input.3); 1403 actual.skip::<1>(); 1404 assert_eq!(i16::from_le_bytes(*actual.take_n::<2>()), input.4); 1405 assert_eq!(actual.ptr_len(memory, 1), input.5.as_bytes()); 1406 let mut mem = actual.ptr_len(memory, 4); 1407 for expected in input.6.iter() { 1408 assert_eq!(u32::from_le_bytes(*mem.take_n::<4>()), *expected); 1409 } 1410 assert!(mem.is_empty()); 1411 assert_eq!(actual.take_n::<1>(), &[input.7 as u8]); 1412 assert_eq!(actual.take_n::<1>(), &[input.8 as u8]); 1413 actual.skip::<2>(); 1414 assert_eq!(u32::from_le_bytes(*actual.take_n::<4>()), input.9 as u32); 1415 1416 // (list bool) 1417 mem = actual.ptr_len(memory, 1); 1418 for expected in input.10.iter() { 1419 assert_eq!(mem.take_n::<1>(), &[*expected as u8]); 1420 } 1421 assert!(mem.is_empty()); 1422 1423 // (list char) 1424 mem = actual.ptr_len(memory, 4); 1425 for expected in input.11.iter() { 1426 assert_eq!(u32::from_le_bytes(*mem.take_n::<4>()), *expected as u32); 1427 } 1428 assert!(mem.is_empty()); 1429 1430 // (list string) 1431 mem = actual.ptr_len(memory, 8); 1432 for expected in input.12.iter() { 1433 let actual = mem.ptr_len(memory, 1); 1434 assert_eq!(actual, expected.as_bytes()); 1435 } 1436 assert!(mem.is_empty()); 1437 assert!(actual.is_empty()); 1438 1439 Ok(()) 1440 } 1441 1442 #[tokio::test] 1443 async fn many_results() -> Result<()> { 1444 test_many_results(false, false).await 1445 } 1446 1447 #[tokio::test] 1448 async fn many_results_concurrent() -> Result<()> { 1449 test_many_results(false, true).await 1450 } 1451 1452 #[tokio::test] 1453 async fn many_results_dynamic() -> Result<()> { 1454 test_many_results(true, false).await 1455 } 1456 1457 #[tokio::test] 1458 async fn many_results_dynamic_concurrent() -> Result<()> { 1459 test_many_results(true, true).await 1460 } 1461 1462 async fn test_many_results(dynamic: bool, concurrent: bool) -> Result<()> { 1463 let (ret, async_opts) = if concurrent { 1464 ( 1465 r#" 1466 call $task-return 1467 i32.const 0 1468 "#, 1469 r#"async (callback (func $i "callback"))"#, 1470 ) 1471 } else { 1472 ("", "") 1473 }; 1474 1475 let my_nan = CANON_32BIT_NAN | 1; 1476 1477 let component = format!( 1478 r#"(component 1479 (core module $libc 1480 (memory (export "memory") 1) 1481 1482 {REALLOC_AND_FREE} 1483 ) 1484 (core instance $libc (instantiate $libc)) 1485 (core module $m 1486 (import "libc" "memory" (memory 1)) 1487 (import "libc" "realloc" (func $realloc (param i32 i32 i32 i32) (result i32))) 1488 (import "" "task.return" (func $task-return (param i32))) 1489 (func (export "foo") (result i32) 1490 (local $base i32) 1491 (local $string i32) 1492 (local $list i32) 1493 1494 (local.set $base 1495 (call $realloc 1496 (i32.const 0) 1497 (i32.const 0) 1498 (i32.const 8) 1499 (i32.const 72))) 1500 1501 (i32.store8 offset=0 1502 (local.get $base) 1503 (i32.const -100)) 1504 1505 (i64.store offset=8 1506 (local.get $base) 1507 (i64.const 9223372036854775807)) 1508 1509 (f32.store offset=16 1510 (local.get $base) 1511 (f32.reinterpret_i32 (i32.const {my_nan}))) 1512 1513 (i32.store8 offset=20 1514 (local.get $base) 1515 (i32.const 38)) 1516 1517 (i32.store16 offset=22 1518 (local.get $base) 1519 (i32.const 18831)) 1520 1521 (local.set $string 1522 (call $realloc 1523 (i32.const 0) 1524 (i32.const 0) 1525 (i32.const 1) 1526 (i32.const 6))) 1527 1528 (i32.store8 offset=0 1529 (local.get $string) 1530 (i32.const 97)) ;; 'a' 1531 (i32.store8 offset=1 1532 (local.get $string) 1533 (i32.const 98)) ;; 'b' 1534 (i32.store8 offset=2 1535 (local.get $string) 1536 (i32.const 99)) ;; 'c' 1537 (i32.store8 offset=3 1538 (local.get $string) 1539 (i32.const 100)) ;; 'd' 1540 (i32.store8 offset=4 1541 (local.get $string) 1542 (i32.const 101)) ;; 'e' 1543 (i32.store8 offset=5 1544 (local.get $string) 1545 (i32.const 102)) ;; 'f' 1546 1547 (i32.store offset=24 1548 (local.get $base) 1549 (local.get $string)) 1550 1551 (i32.store offset=28 1552 (local.get $base) 1553 (i32.const 2)) 1554 1555 (local.set $list 1556 (call $realloc 1557 (i32.const 0) 1558 (i32.const 0) 1559 (i32.const 4) 1560 (i32.const 32))) 1561 1562 (i32.store offset=0 1563 (local.get $list) 1564 (i32.const 1)) 1565 (i32.store offset=4 1566 (local.get $list) 1567 (i32.const 2)) 1568 (i32.store offset=8 1569 (local.get $list) 1570 (i32.const 3)) 1571 (i32.store offset=12 1572 (local.get $list) 1573 (i32.const 4)) 1574 (i32.store offset=16 1575 (local.get $list) 1576 (i32.const 5)) 1577 (i32.store offset=20 1578 (local.get $list) 1579 (i32.const 6)) 1580 (i32.store offset=24 1581 (local.get $list) 1582 (i32.const 7)) 1583 (i32.store offset=28 1584 (local.get $list) 1585 (i32.const 8)) 1586 1587 (i32.store offset=32 1588 (local.get $base) 1589 (local.get $list)) 1590 1591 (i32.store offset=36 1592 (local.get $base) 1593 (i32.const 8)) 1594 1595 (i32.store8 offset=40 1596 (local.get $base) 1597 (i32.const 1)) 1598 1599 (i32.store8 offset=41 1600 (local.get $base) 1601 (i32.const 0)) 1602 1603 (i32.store offset=44 1604 (local.get $base) 1605 (i32.const 128681)) ;; '' 1606 1607 (local.set $list 1608 (call $realloc 1609 (i32.const 0) 1610 (i32.const 0) 1611 (i32.const 1) 1612 (i32.const 5))) 1613 1614 (i32.store8 offset=0 1615 (local.get $list) 1616 (i32.const 0)) 1617 (i32.store8 offset=1 1618 (local.get $list) 1619 (i32.const 1)) 1620 (i32.store8 offset=2 1621 (local.get $list) 1622 (i32.const 0)) 1623 (i32.store8 offset=3 1624 (local.get $list) 1625 (i32.const 1)) 1626 (i32.store8 offset=4 1627 (local.get $list) 1628 (i32.const 1)) 1629 1630 (i32.store offset=48 1631 (local.get $base) 1632 (local.get $list)) 1633 1634 (i32.store offset=52 1635 (local.get $base) 1636 (i32.const 5)) 1637 1638 (local.set $list 1639 (call $realloc 1640 (i32.const 0) 1641 (i32.const 0) 1642 (i32.const 4) 1643 (i32.const 20))) 1644 1645 (i32.store offset=0 1646 (local.get $list) 1647 (i32.const 127820)) ;; '' 1648 (i32.store offset=4 1649 (local.get $list) 1650 (i32.const 129360)) ;; '' 1651 (i32.store offset=8 1652 (local.get $list) 1653 (i32.const 127831)) ;; '' 1654 (i32.store offset=12 1655 (local.get $list) 1656 (i32.const 127833)) ;; '' 1657 (i32.store offset=16 1658 (local.get $list) 1659 (i32.const 127841)) ;; '' 1660 1661 (i32.store offset=56 1662 (local.get $base) 1663 (local.get $list)) 1664 1665 (i32.store offset=60 1666 (local.get $base) 1667 (i32.const 5)) 1668 1669 (local.set $list 1670 (call $realloc 1671 (i32.const 0) 1672 (i32.const 0) 1673 (i32.const 4) 1674 (i32.const 16))) 1675 1676 (i32.store offset=0 1677 (local.get $list) 1678 (i32.add (local.get $string) (i32.const 2))) 1679 (i32.store offset=4 1680 (local.get $list) 1681 (i32.const 2)) 1682 (i32.store offset=8 1683 (local.get $list) 1684 (i32.add (local.get $string) (i32.const 4))) 1685 (i32.store offset=12 1686 (local.get $list) 1687 (i32.const 2)) 1688 1689 (i32.store offset=64 1690 (local.get $base) 1691 (local.get $list)) 1692 1693 (i32.store offset=68 1694 (local.get $base) 1695 (i32.const 2)) 1696 1697 local.get $base 1698 1699 {ret} 1700 ) 1701 (func (export "callback") (param i32 i32 i32) (result i32) unreachable) 1702 ) 1703 (type $tuple (tuple 1704 s8 1705 u64 1706 float32 1707 u8 1708 s16 1709 string 1710 (list u32) 1711 bool 1712 bool 1713 char 1714 (list bool) 1715 (list char) 1716 (list string) 1717 )) 1718 (core func $task-return (canon task.return 1719 (result $tuple) 1720 (memory $libc "memory") 1721 )) 1722 (core instance $i (instantiate $m 1723 (with "" (instance (export "task.return" (func $task-return)))) 1724 (with "libc" (instance $libc)) 1725 )) 1726 1727 (type $t (func (result $tuple))) 1728 (func (export "many-results") (type $t) 1729 (canon lift 1730 (core func $i "foo") 1731 (memory $libc "memory") 1732 (realloc (func $libc "realloc")) 1733 {async_opts} 1734 ) 1735 ) 1736 )"# 1737 ); 1738 1739 let mut config = Config::new(); 1740 config.wasm_component_model_async(true); 1741 config.async_support(true); 1742 let engine = &Engine::new(&config)?; 1743 let component = Component::new(&engine, component)?; 1744 let mut store = Store::new(&engine, ()); 1745 1746 let instance = Linker::new(&engine) 1747 .instantiate_async(&mut store, &component) 1748 .await?; 1749 1750 let expected = ( 1751 -100i8, 1752 u64::MAX / 2, 1753 f32::from_bits(CANON_32BIT_NAN | 1), 1754 38u8, 1755 18831i16, 1756 "ab".to_string(), 1757 vec![1u32, 2, 3, 4, 5, 6, 7, 8], 1758 true, 1759 false, 1760 '', 1761 vec![false, true, false, true, true], 1762 vec!['', '', '', '', ''], 1763 vec!["cd".to_string(), "ef".to_string()], 1764 ); 1765 1766 let actual = if dynamic { 1767 let func = instance.get_func(&mut store, "many-results").unwrap(); 1768 1769 let mut results = vec![Val::Bool(false)]; 1770 if concurrent { 1771 store 1772 .run_concurrent(async |store| { 1773 func.call_concurrent(store, &[], &mut results).await?; 1774 anyhow::Ok(()) 1775 }) 1776 .await??; 1777 } else { 1778 func.call_async(&mut store, &[], &mut results).await?; 1779 }; 1780 let mut results = results.into_iter(); 1781 1782 let Some(Val::Tuple(results)) = results.next() else { 1783 panic!() 1784 }; 1785 let mut results = results.into_iter(); 1786 let Some(Val::S8(p1)) = results.next() else { 1787 panic!() 1788 }; 1789 let Some(Val::U64(p2)) = results.next() else { 1790 panic!() 1791 }; 1792 let Some(Val::Float32(p3)) = results.next() else { 1793 panic!() 1794 }; 1795 let Some(Val::U8(p4)) = results.next() else { 1796 panic!() 1797 }; 1798 let Some(Val::S16(p5)) = results.next() else { 1799 panic!() 1800 }; 1801 let Some(Val::String(p6)) = results.next() else { 1802 panic!() 1803 }; 1804 let Some(Val::List(p7)) = results.next() else { 1805 panic!() 1806 }; 1807 let p7 = p7 1808 .into_iter() 1809 .map(|v| if let Val::U32(v) = v { v } else { panic!() }) 1810 .collect(); 1811 let Some(Val::Bool(p8)) = results.next() else { 1812 panic!() 1813 }; 1814 let Some(Val::Bool(p9)) = results.next() else { 1815 panic!() 1816 }; 1817 let Some(Val::Char(p0)) = results.next() else { 1818 panic!() 1819 }; 1820 let Some(Val::List(pa)) = results.next() else { 1821 panic!() 1822 }; 1823 let pa = pa 1824 .into_iter() 1825 .map(|v| if let Val::Bool(v) = v { v } else { panic!() }) 1826 .collect(); 1827 let Some(Val::List(pb)) = results.next() else { 1828 panic!() 1829 }; 1830 let pb = pb 1831 .into_iter() 1832 .map(|v| if let Val::Char(v) = v { v } else { panic!() }) 1833 .collect(); 1834 let Some(Val::List(pc)) = results.next() else { 1835 panic!() 1836 }; 1837 let pc = pc 1838 .into_iter() 1839 .map(|v| if let Val::String(v) = v { v } else { panic!() }) 1840 .collect(); 1841 1842 (p1, p2, p3, p4, p5, p6, p7, p8, p9, p0, pa, pb, pc) 1843 } else { 1844 let func = instance.get_typed_func::<(), (( 1845 i8, 1846 u64, 1847 f32, 1848 u8, 1849 i16, 1850 String, 1851 Vec<u32>, 1852 bool, 1853 bool, 1854 char, 1855 Vec<bool>, 1856 Vec<char>, 1857 Vec<String>, 1858 ),)>(&mut store, "many-results")?; 1859 1860 if concurrent { 1861 store 1862 .run_concurrent(async move |accessor| { 1863 anyhow::Ok(func.call_concurrent(accessor, ()).await?.0) 1864 }) 1865 .await?? 1866 .0 1867 } else { 1868 func.call_async(&mut store, ()).await?.0 1869 } 1870 }; 1871 1872 assert_eq!(expected.0, actual.0); 1873 assert_eq!(expected.1, actual.1); 1874 assert!(expected.2.is_nan()); 1875 assert!(actual.2.is_nan()); 1876 assert_eq!(expected.3, actual.3); 1877 assert_eq!(expected.4, actual.4); 1878 assert_eq!(expected.5, actual.5); 1879 assert_eq!(expected.6, actual.6); 1880 assert_eq!(expected.7, actual.7); 1881 assert_eq!(expected.8, actual.8); 1882 assert_eq!(expected.9, actual.9); 1883 assert_eq!(expected.10, actual.10); 1884 assert_eq!(expected.11, actual.11); 1885 assert_eq!(expected.12, actual.12); 1886 1887 Ok(()) 1888 } 1889 1890 #[test] 1891 fn some_traps() -> Result<()> { 1892 let middle_of_memory = (i32::MAX / 2) & (!0xff); 1893 let component = format!( 1894 r#"(component 1895 (core module $m 1896 (memory (export "memory") 1) 1897 (func (export "take-many") (param i32)) 1898 (func (export "take-list") (param i32 i32)) 1899 1900 (func (export "realloc") (param i32 i32 i32 i32) (result i32) 1901 unreachable) 1902 ) 1903 (core instance $i (instantiate $m)) 1904 1905 (func (export "take-list-unreachable") (param "a" (list u8)) 1906 (canon lift (core func $i "take-list") (memory $i "memory") (realloc (func $i "realloc"))) 1907 ) 1908 (func (export "take-string-unreachable") (param "a" string) 1909 (canon lift (core func $i "take-list") (memory $i "memory") (realloc (func $i "realloc"))) 1910 ) 1911 1912 (type $t (func 1913 (param "s1" string) 1914 (param "s2" string) 1915 (param "s3" string) 1916 (param "s4" string) 1917 (param "s5" string) 1918 (param "s6" string) 1919 (param "s7" string) 1920 (param "s8" string) 1921 (param "s9" string) 1922 (param "s10" string) 1923 )) 1924 (func (export "take-many-unreachable") (type $t) 1925 (canon lift (core func $i "take-many") (memory $i "memory") (realloc (func $i "realloc"))) 1926 ) 1927 1928 (core module $m2 1929 (memory (export "memory") 1) 1930 (func (export "take-many") (param i32)) 1931 (func (export "take-list") (param i32 i32)) 1932 1933 (func (export "realloc") (param i32 i32 i32 i32) (result i32) 1934 i32.const {middle_of_memory}) 1935 ) 1936 (core instance $i2 (instantiate $m2)) 1937 1938 (func (export "take-list-base-oob") (param "a" (list u8)) 1939 (canon lift (core func $i2 "take-list") (memory $i2 "memory") (realloc (func $i2 "realloc"))) 1940 ) 1941 (func (export "take-string-base-oob") (param "a" string) 1942 (canon lift (core func $i2 "take-list") (memory $i2 "memory") (realloc (func $i2 "realloc"))) 1943 ) 1944 (func (export "take-many-base-oob") (type $t) 1945 (canon lift (core func $i2 "take-many") (memory $i2 "memory") (realloc (func $i2 "realloc"))) 1946 ) 1947 1948 (core module $m3 1949 (memory (export "memory") 1) 1950 (func (export "take-many") (param i32)) 1951 (func (export "take-list") (param i32 i32)) 1952 1953 (func (export "realloc") (param i32 i32 i32 i32) (result i32) 1954 i32.const 65532) 1955 ) 1956 (core instance $i3 (instantiate $m3)) 1957 1958 (func (export "take-list-end-oob") (param "a" (list u8)) 1959 (canon lift (core func $i3 "take-list") (memory $i3 "memory") (realloc (func $i3 "realloc"))) 1960 ) 1961 (func (export "take-string-end-oob") (param "a" string) 1962 (canon lift (core func $i3 "take-list") (memory $i3 "memory") (realloc (func $i3 "realloc"))) 1963 ) 1964 (func (export "take-many-end-oob") (type $t) 1965 (canon lift (core func $i3 "take-many") (memory $i3 "memory") (realloc (func $i3 "realloc"))) 1966 ) 1967 1968 (core module $m4 1969 (memory (export "memory") 1) 1970 (func (export "take-many") (param i32)) 1971 1972 (global $cnt (mut i32) (i32.const 0)) 1973 (func (export "realloc") (param i32 i32 i32 i32) (result i32) 1974 global.get $cnt 1975 if (result i32) 1976 i32.const 100000 1977 else 1978 i32.const 1 1979 global.set $cnt 1980 i32.const 0 1981 end 1982 ) 1983 ) 1984 (core instance $i4 (instantiate $m4)) 1985 1986 (func (export "take-many-second-oob") (type $t) 1987 (canon lift (core func $i4 "take-many") (memory $i4 "memory") (realloc (func $i4 "realloc"))) 1988 ) 1989 )"# 1990 ); 1991 1992 let engine = super::engine(); 1993 let component = Component::new(&engine, component)?; 1994 let mut store = Store::new(&engine, ()); 1995 let instance = |store: &mut Store<()>| Linker::new(&engine).instantiate(store, &component); 1996 1997 // This should fail when calling the allocator function for the argument 1998 let err = instance(&mut store)? 1999 .get_typed_func::<(&[u8],), ()>(&mut store, "take-list-unreachable")? 2000 .call(&mut store, (&[],)) 2001 .unwrap_err() 2002 .downcast::<Trap>()?; 2003 assert_eq!(err, Trap::UnreachableCodeReached); 2004 2005 // This should fail when calling the allocator function for the argument 2006 let err = instance(&mut store)? 2007 .get_typed_func::<(&str,), ()>(&mut store, "take-string-unreachable")? 2008 .call(&mut store, ("",)) 2009 .unwrap_err() 2010 .downcast::<Trap>()?; 2011 assert_eq!(err, Trap::UnreachableCodeReached); 2012 2013 // This should fail when calling the allocator function for the space 2014 // to store the arguments (before arguments are even lowered) 2015 let err = instance(&mut store)? 2016 .get_typed_func::<(&str, &str, &str, &str, &str, &str, &str, &str, &str, &str), ()>( 2017 &mut store, 2018 "take-many-unreachable", 2019 )? 2020 .call(&mut store, ("", "", "", "", "", "", "", "", "", "")) 2021 .unwrap_err() 2022 .downcast::<Trap>()?; 2023 assert_eq!(err, Trap::UnreachableCodeReached); 2024 2025 // Assert that when the base pointer returned by malloc is out of bounds 2026 // that errors are reported as such. Both empty and lists with contents 2027 // should all be invalid here. 2028 // 2029 // FIXME(WebAssembly/component-model#32) confirm the semantics here are 2030 // what's desired. 2031 #[track_caller] 2032 fn assert_oob(err: &anyhow::Error) { 2033 assert!( 2034 err.to_string() 2035 .contains("realloc return: beyond end of memory"), 2036 "{err:?}", 2037 ); 2038 } 2039 let err = instance(&mut store)? 2040 .get_typed_func::<(&[u8],), ()>(&mut store, "take-list-base-oob")? 2041 .call(&mut store, (&[],)) 2042 .unwrap_err(); 2043 assert_oob(&err); 2044 let err = instance(&mut store)? 2045 .get_typed_func::<(&[u8],), ()>(&mut store, "take-list-base-oob")? 2046 .call(&mut store, (&[1],)) 2047 .unwrap_err(); 2048 assert_oob(&err); 2049 let err = instance(&mut store)? 2050 .get_typed_func::<(&str,), ()>(&mut store, "take-string-base-oob")? 2051 .call(&mut store, ("",)) 2052 .unwrap_err(); 2053 assert_oob(&err); 2054 let err = instance(&mut store)? 2055 .get_typed_func::<(&str,), ()>(&mut store, "take-string-base-oob")? 2056 .call(&mut store, ("x",)) 2057 .unwrap_err(); 2058 assert_oob(&err); 2059 let err = instance(&mut store)? 2060 .get_typed_func::<(&str, &str, &str, &str, &str, &str, &str, &str, &str, &str), ()>( 2061 &mut store, 2062 "take-many-base-oob", 2063 )? 2064 .call(&mut store, ("", "", "", "", "", "", "", "", "", "")) 2065 .unwrap_err(); 2066 assert_oob(&err); 2067 2068 // Test here that when the returned pointer from malloc is one byte from the 2069 // end of memory that empty things are fine, but larger things are not. 2070 2071 instance(&mut store)? 2072 .get_typed_func::<(&[u8],), ()>(&mut store, "take-list-end-oob")? 2073 .call_and_post_return(&mut store, (&[],))?; 2074 instance(&mut store)? 2075 .get_typed_func::<(&[u8],), ()>(&mut store, "take-list-end-oob")? 2076 .call_and_post_return(&mut store, (&[1, 2, 3, 4],))?; 2077 let err = instance(&mut store)? 2078 .get_typed_func::<(&[u8],), ()>(&mut store, "take-list-end-oob")? 2079 .call(&mut store, (&[1, 2, 3, 4, 5],)) 2080 .unwrap_err(); 2081 assert_oob(&err); 2082 instance(&mut store)? 2083 .get_typed_func::<(&str,), ()>(&mut store, "take-string-end-oob")? 2084 .call_and_post_return(&mut store, ("",))?; 2085 instance(&mut store)? 2086 .get_typed_func::<(&str,), ()>(&mut store, "take-string-end-oob")? 2087 .call_and_post_return(&mut store, ("abcd",))?; 2088 let err = instance(&mut store)? 2089 .get_typed_func::<(&str,), ()>(&mut store, "take-string-end-oob")? 2090 .call(&mut store, ("abcde",)) 2091 .unwrap_err(); 2092 assert_oob(&err); 2093 let err = instance(&mut store)? 2094 .get_typed_func::<(&str, &str, &str, &str, &str, &str, &str, &str, &str, &str), ()>( 2095 &mut store, 2096 "take-many-end-oob", 2097 )? 2098 .call(&mut store, ("", "", "", "", "", "", "", "", "", "")) 2099 .unwrap_err(); 2100 assert_oob(&err); 2101 2102 // For this function the first allocation, the space to store all the 2103 // arguments, is in-bounds but then all further allocations, such as for 2104 // each individual string, are all out of bounds. 2105 let err = instance(&mut store)? 2106 .get_typed_func::<(&str, &str, &str, &str, &str, &str, &str, &str, &str, &str), ()>( 2107 &mut store, 2108 "take-many-second-oob", 2109 )? 2110 .call(&mut store, ("", "", "", "", "", "", "", "", "", "")) 2111 .unwrap_err(); 2112 assert_oob(&err); 2113 let err = instance(&mut store)? 2114 .get_typed_func::<(&str, &str, &str, &str, &str, &str, &str, &str, &str, &str), ()>( 2115 &mut store, 2116 "take-many-second-oob", 2117 )? 2118 .call(&mut store, ("", "", "", "", "", "", "", "", "", "x")) 2119 .unwrap_err(); 2120 assert_oob(&err); 2121 Ok(()) 2122 } 2123 2124 #[test] 2125 fn char_bool_memory() -> Result<()> { 2126 let component = format!( 2127 r#"(component 2128 (core module $m 2129 (memory (export "memory") 1) 2130 (func (export "ret-tuple") (param i32 i32) (result i32) 2131 (local $base i32) 2132 2133 ;; Allocate space for the return 2134 (local.set $base 2135 (call $realloc 2136 (i32.const 0) 2137 (i32.const 0) 2138 (i32.const 4) 2139 (i32.const 8))) 2140 2141 ;; store the boolean 2142 (i32.store offset=0 2143 (local.get $base) 2144 (local.get 0)) 2145 2146 ;; store the char 2147 (i32.store offset=4 2148 (local.get $base) 2149 (local.get 1)) 2150 2151 (local.get $base) 2152 ) 2153 2154 {REALLOC_AND_FREE} 2155 ) 2156 (core instance $i (instantiate $m)) 2157 2158 (func (export "ret-tuple") (param "a" u32) (param "b" u32) (result (tuple bool char)) 2159 (canon lift (core func $i "ret-tuple") 2160 (memory $i "memory") 2161 (realloc (func $i "realloc"))) 2162 ) 2163 )"# 2164 ); 2165 2166 let engine = super::engine(); 2167 let component = Component::new(&engine, component)?; 2168 let mut store = Store::new(&engine, ()); 2169 let instance = Linker::new(&engine).instantiate(&mut store, &component)?; 2170 let func = instance.get_typed_func::<(u32, u32), ((bool, char),)>(&mut store, "ret-tuple")?; 2171 2172 let (ret,) = func.call(&mut store, (0, 'a' as u32))?; 2173 assert_eq!(ret, (false, 'a')); 2174 func.post_return(&mut store)?; 2175 2176 let (ret,) = func.call(&mut store, (1, '' as u32))?; 2177 assert_eq!(ret, (true, '')); 2178 func.post_return(&mut store)?; 2179 2180 let (ret,) = func.call(&mut store, (2, 'a' as u32))?; 2181 assert_eq!(ret, (true, 'a')); 2182 func.post_return(&mut store)?; 2183 2184 assert!(func.call(&mut store, (0, 0xd800)).is_err()); 2185 2186 Ok(()) 2187 } 2188 2189 #[test] 2190 fn string_list_oob() -> Result<()> { 2191 let component = format!( 2192 r#"(component 2193 (core module $m 2194 (memory (export "memory") 1) 2195 (func (export "ret-list") (result i32) 2196 (local $base i32) 2197 2198 ;; Allocate space for the return 2199 (local.set $base 2200 (call $realloc 2201 (i32.const 0) 2202 (i32.const 0) 2203 (i32.const 4) 2204 (i32.const 8))) 2205 2206 (i32.store offset=0 2207 (local.get $base) 2208 (i32.const 100000)) 2209 (i32.store offset=4 2210 (local.get $base) 2211 (i32.const 1)) 2212 2213 (local.get $base) 2214 ) 2215 2216 {REALLOC_AND_FREE} 2217 ) 2218 (core instance $i (instantiate $m)) 2219 2220 (func (export "ret-list-u8") (result (list u8)) 2221 (canon lift (core func $i "ret-list") 2222 (memory $i "memory") 2223 (realloc (func $i "realloc")) 2224 ) 2225 ) 2226 (func (export "ret-string") (result string) 2227 (canon lift (core func $i "ret-list") 2228 (memory $i "memory") 2229 (realloc (func $i "realloc")) 2230 ) 2231 ) 2232 )"# 2233 ); 2234 2235 let engine = super::engine(); 2236 let component = Component::new(&engine, component)?; 2237 let mut store = Store::new(&engine, ()); 2238 let ret_list_u8 = Linker::new(&engine) 2239 .instantiate(&mut store, &component)? 2240 .get_typed_func::<(), (WasmList<u8>,)>(&mut store, "ret-list-u8")?; 2241 let ret_string = Linker::new(&engine) 2242 .instantiate(&mut store, &component)? 2243 .get_typed_func::<(), (WasmStr,)>(&mut store, "ret-string")?; 2244 2245 let err = ret_list_u8.call(&mut store, ()).err().unwrap(); 2246 assert!(err.to_string().contains("out of bounds"), "{}", err); 2247 2248 let err = ret_string.call(&mut store, ()).err().unwrap(); 2249 assert!(err.to_string().contains("out of bounds"), "{}", err); 2250 2251 Ok(()) 2252 } 2253 2254 #[test] 2255 fn tuples() -> Result<()> { 2256 let component = format!( 2257 r#"(component 2258 (core module $m 2259 (memory (export "memory") 1) 2260 (func (export "foo") 2261 (param i32 f64 i32) 2262 (result i32) 2263 2264 local.get 0 2265 i32.const 0 2266 i32.ne 2267 if unreachable end 2268 2269 local.get 1 2270 f64.const 1 2271 f64.ne 2272 if unreachable end 2273 2274 local.get 2 2275 i32.const 2 2276 i32.ne 2277 if unreachable end 2278 2279 i32.const 3 2280 ) 2281 ) 2282 (core instance $i (instantiate $m)) 2283 2284 (func (export "foo") 2285 (param "a" (tuple s32 float64)) 2286 (param "b" (tuple s8)) 2287 (result (tuple u16)) 2288 (canon lift (core func $i "foo")) 2289 ) 2290 )"# 2291 ); 2292 2293 let engine = super::engine(); 2294 let component = Component::new(&engine, component)?; 2295 let mut store = Store::new(&engine, ()); 2296 let instance = Linker::new(&engine).instantiate(&mut store, &component)?; 2297 let foo = instance.get_typed_func::<((i32, f64), (i8,)), ((u16,),)>(&mut store, "foo")?; 2298 assert_eq!(foo.call(&mut store, ((0, 1.0), (2,)))?, ((3,),)); 2299 2300 Ok(()) 2301 } 2302 2303 #[test] 2304 fn option() -> Result<()> { 2305 let component = format!( 2306 r#"(component 2307 (core module $m 2308 (memory (export "memory") 1) 2309 (func (export "pass1") (param i32 i32) (result i32) 2310 (local $base i32) 2311 (local.set $base 2312 (call $realloc 2313 (i32.const 0) 2314 (i32.const 0) 2315 (i32.const 4) 2316 (i32.const 8))) 2317 2318 (i32.store offset=0 2319 (local.get $base) 2320 (local.get 0)) 2321 (i32.store offset=4 2322 (local.get $base) 2323 (local.get 1)) 2324 2325 (local.get $base) 2326 ) 2327 (func (export "pass2") (param i32 i32 i32) (result i32) 2328 (local $base i32) 2329 (local.set $base 2330 (call $realloc 2331 (i32.const 0) 2332 (i32.const 0) 2333 (i32.const 4) 2334 (i32.const 12))) 2335 2336 (i32.store offset=0 2337 (local.get $base) 2338 (local.get 0)) 2339 (i32.store offset=4 2340 (local.get $base) 2341 (local.get 1)) 2342 (i32.store offset=8 2343 (local.get $base) 2344 (local.get 2)) 2345 2346 (local.get $base) 2347 ) 2348 2349 {REALLOC_AND_FREE} 2350 ) 2351 (core instance $i (instantiate $m)) 2352 2353 (func (export "option-u8-to-tuple") (param "a" (option u8)) (result (tuple u32 u32)) 2354 (canon lift (core func $i "pass1") (memory $i "memory")) 2355 ) 2356 (func (export "option-u32-to-tuple") (param "a" (option u32)) (result (tuple u32 u32)) 2357 (canon lift (core func $i "pass1") (memory $i "memory")) 2358 ) 2359 (func (export "option-string-to-tuple") (param "a" (option string)) (result (tuple u32 string)) 2360 (canon lift 2361 (core func $i "pass2") 2362 (memory $i "memory") 2363 (realloc (func $i "realloc")) 2364 ) 2365 ) 2366 (func (export "to-option-u8") (param "a" u32) (param "b" u32) (result (option u8)) 2367 (canon lift (core func $i "pass1") (memory $i "memory")) 2368 ) 2369 (func (export "to-option-u32") (param "a" u32) (param "b" u32) (result (option u32)) 2370 (canon lift 2371 (core func $i "pass1") 2372 (memory $i "memory") 2373 ) 2374 ) 2375 (func (export "to-option-string") (param "a" u32) (param "b" string) (result (option string)) 2376 (canon lift 2377 (core func $i "pass2") 2378 (memory $i "memory") 2379 (realloc (func $i "realloc")) 2380 ) 2381 ) 2382 )"# 2383 ); 2384 2385 let engine = super::engine(); 2386 let component = Component::new(&engine, component)?; 2387 let mut store = Store::new(&engine, ()); 2388 let linker = Linker::new(&engine); 2389 let instance = linker.instantiate(&mut store, &component)?; 2390 2391 let option_u8_to_tuple = instance 2392 .get_typed_func::<(Option<u8>,), ((u32, u32),)>(&mut store, "option-u8-to-tuple")?; 2393 assert_eq!(option_u8_to_tuple.call(&mut store, (None,))?, ((0, 0),)); 2394 option_u8_to_tuple.post_return(&mut store)?; 2395 assert_eq!(option_u8_to_tuple.call(&mut store, (Some(0),))?, ((1, 0),)); 2396 option_u8_to_tuple.post_return(&mut store)?; 2397 assert_eq!( 2398 option_u8_to_tuple.call(&mut store, (Some(100),))?, 2399 ((1, 100),) 2400 ); 2401 option_u8_to_tuple.post_return(&mut store)?; 2402 2403 let option_u32_to_tuple = instance 2404 .get_typed_func::<(Option<u32>,), ((u32, u32),)>(&mut store, "option-u32-to-tuple")?; 2405 assert_eq!(option_u32_to_tuple.call(&mut store, (None,))?, ((0, 0),)); 2406 option_u32_to_tuple.post_return(&mut store)?; 2407 assert_eq!(option_u32_to_tuple.call(&mut store, (Some(0),))?, ((1, 0),)); 2408 option_u32_to_tuple.post_return(&mut store)?; 2409 assert_eq!( 2410 option_u32_to_tuple.call(&mut store, (Some(100),))?, 2411 ((1, 100),) 2412 ); 2413 option_u32_to_tuple.post_return(&mut store)?; 2414 2415 let option_string_to_tuple = instance.get_typed_func::<(Option<&str>,), ((u32, WasmStr),)>( 2416 &mut store, 2417 "option-string-to-tuple", 2418 )?; 2419 let ((a, b),) = option_string_to_tuple.call(&mut store, (None,))?; 2420 assert_eq!(a, 0); 2421 assert_eq!(b.to_str(&store)?, ""); 2422 option_string_to_tuple.post_return(&mut store)?; 2423 let ((a, b),) = option_string_to_tuple.call(&mut store, (Some(""),))?; 2424 assert_eq!(a, 1); 2425 assert_eq!(b.to_str(&store)?, ""); 2426 option_string_to_tuple.post_return(&mut store)?; 2427 let ((a, b),) = option_string_to_tuple.call(&mut store, (Some("hello"),))?; 2428 assert_eq!(a, 1); 2429 assert_eq!(b.to_str(&store)?, "hello"); 2430 option_string_to_tuple.post_return(&mut store)?; 2431 2432 let instance = linker.instantiate(&mut store, &component)?; 2433 let to_option_u8 = 2434 instance.get_typed_func::<(u32, u32), (Option<u8>,)>(&mut store, "to-option-u8")?; 2435 assert_eq!(to_option_u8.call(&mut store, (0x00_00, 0))?, (None,)); 2436 to_option_u8.post_return(&mut store)?; 2437 assert_eq!(to_option_u8.call(&mut store, (0x00_01, 0))?, (Some(0),)); 2438 to_option_u8.post_return(&mut store)?; 2439 assert_eq!(to_option_u8.call(&mut store, (0xfd_01, 0))?, (Some(0xfd),)); 2440 to_option_u8.post_return(&mut store)?; 2441 assert!(to_option_u8.call(&mut store, (0x00_02, 0)).is_err()); 2442 2443 let instance = linker.instantiate(&mut store, &component)?; 2444 let to_option_u32 = 2445 instance.get_typed_func::<(u32, u32), (Option<u32>,)>(&mut store, "to-option-u32")?; 2446 assert_eq!(to_option_u32.call(&mut store, (0, 0))?, (None,)); 2447 to_option_u32.post_return(&mut store)?; 2448 assert_eq!(to_option_u32.call(&mut store, (1, 0))?, (Some(0),)); 2449 to_option_u32.post_return(&mut store)?; 2450 assert_eq!( 2451 to_option_u32.call(&mut store, (1, 0x1234fead))?, 2452 (Some(0x1234fead),) 2453 ); 2454 to_option_u32.post_return(&mut store)?; 2455 assert!(to_option_u32.call(&mut store, (2, 0)).is_err()); 2456 2457 let instance = linker.instantiate(&mut store, &component)?; 2458 let to_option_string = instance 2459 .get_typed_func::<(u32, &str), (Option<WasmStr>,)>(&mut store, "to-option-string")?; 2460 let ret = to_option_string.call(&mut store, (0, ""))?.0; 2461 assert!(ret.is_none()); 2462 to_option_string.post_return(&mut store)?; 2463 let ret = to_option_string.call(&mut store, (1, ""))?.0; 2464 assert_eq!(ret.unwrap().to_str(&store)?, ""); 2465 to_option_string.post_return(&mut store)?; 2466 let ret = to_option_string.call(&mut store, (1, "cheesecake"))?.0; 2467 assert_eq!(ret.unwrap().to_str(&store)?, "cheesecake"); 2468 to_option_string.post_return(&mut store)?; 2469 assert!(to_option_string.call(&mut store, (2, "")).is_err()); 2470 2471 Ok(()) 2472 } 2473 2474 #[test] 2475 fn expected() -> Result<()> { 2476 let component = format!( 2477 r#"(component 2478 (core module $m 2479 (memory (export "memory") 1) 2480 (func (export "pass0") (param i32) (result i32) 2481 local.get 0 2482 ) 2483 (func (export "pass1") (param i32 i32) (result i32) 2484 (local $base i32) 2485 (local.set $base 2486 (call $realloc 2487 (i32.const 0) 2488 (i32.const 0) 2489 (i32.const 4) 2490 (i32.const 8))) 2491 2492 (i32.store offset=0 2493 (local.get $base) 2494 (local.get 0)) 2495 (i32.store offset=4 2496 (local.get $base) 2497 (local.get 1)) 2498 2499 (local.get $base) 2500 ) 2501 (func (export "pass2") (param i32 i32 i32) (result i32) 2502 (local $base i32) 2503 (local.set $base 2504 (call $realloc 2505 (i32.const 0) 2506 (i32.const 0) 2507 (i32.const 4) 2508 (i32.const 12))) 2509 2510 (i32.store offset=0 2511 (local.get $base) 2512 (local.get 0)) 2513 (i32.store offset=4 2514 (local.get $base) 2515 (local.get 1)) 2516 (i32.store offset=8 2517 (local.get $base) 2518 (local.get 2)) 2519 2520 (local.get $base) 2521 ) 2522 2523 {REALLOC_AND_FREE} 2524 ) 2525 (core instance $i (instantiate $m)) 2526 2527 (func (export "take-expected-unit") (param "a" (result)) (result u32) 2528 (canon lift (core func $i "pass0")) 2529 ) 2530 (func (export "take-expected-u8-f32") (param "a" (result u8 (error float32))) (result (tuple u32 u32)) 2531 (canon lift (core func $i "pass1") (memory $i "memory")) 2532 ) 2533 (type $list (list u8)) 2534 (func (export "take-expected-string") (param "a" (result string (error $list))) (result (tuple u32 string)) 2535 (canon lift 2536 (core func $i "pass2") 2537 (memory $i "memory") 2538 (realloc (func $i "realloc")) 2539 ) 2540 ) 2541 (func (export "to-expected-unit") (param "a" u32) (result (result)) 2542 (canon lift (core func $i "pass0")) 2543 ) 2544 (func (export "to-expected-s16-f32") (param "a" u32) (param "b" u32) (result (result s16 (error float32))) 2545 (canon lift 2546 (core func $i "pass1") 2547 (memory $i "memory") 2548 (realloc (func $i "realloc")) 2549 ) 2550 ) 2551 )"# 2552 ); 2553 2554 let engine = super::engine(); 2555 let component = Component::new(&engine, component)?; 2556 let mut store = Store::new(&engine, ()); 2557 let linker = Linker::new(&engine); 2558 let instance = linker.instantiate(&mut store, &component)?; 2559 let take_expected_unit = 2560 instance.get_typed_func::<(Result<(), ()>,), (u32,)>(&mut store, "take-expected-unit")?; 2561 assert_eq!(take_expected_unit.call(&mut store, (Ok(()),))?, (0,)); 2562 take_expected_unit.post_return(&mut store)?; 2563 assert_eq!(take_expected_unit.call(&mut store, (Err(()),))?, (1,)); 2564 take_expected_unit.post_return(&mut store)?; 2565 2566 let take_expected_u8_f32 = instance 2567 .get_typed_func::<(Result<u8, f32>,), ((u32, u32),)>(&mut store, "take-expected-u8-f32")?; 2568 assert_eq!(take_expected_u8_f32.call(&mut store, (Ok(1),))?, ((0, 1),)); 2569 take_expected_u8_f32.post_return(&mut store)?; 2570 assert_eq!( 2571 take_expected_u8_f32.call(&mut store, (Err(2.0),))?, 2572 ((1, 2.0f32.to_bits()),) 2573 ); 2574 take_expected_u8_f32.post_return(&mut store)?; 2575 2576 let take_expected_string = instance 2577 .get_typed_func::<(Result<&str, &[u8]>,), ((u32, WasmStr),)>( 2578 &mut store, 2579 "take-expected-string", 2580 )?; 2581 let ((a, b),) = take_expected_string.call(&mut store, (Ok("hello"),))?; 2582 assert_eq!(a, 0); 2583 assert_eq!(b.to_str(&store)?, "hello"); 2584 take_expected_string.post_return(&mut store)?; 2585 let ((a, b),) = take_expected_string.call(&mut store, (Err(b"goodbye"),))?; 2586 assert_eq!(a, 1); 2587 assert_eq!(b.to_str(&store)?, "goodbye"); 2588 take_expected_string.post_return(&mut store)?; 2589 2590 let instance = linker.instantiate(&mut store, &component)?; 2591 let to_expected_unit = 2592 instance.get_typed_func::<(u32,), (Result<(), ()>,)>(&mut store, "to-expected-unit")?; 2593 assert_eq!(to_expected_unit.call(&mut store, (0,))?, (Ok(()),)); 2594 to_expected_unit.post_return(&mut store)?; 2595 assert_eq!(to_expected_unit.call(&mut store, (1,))?, (Err(()),)); 2596 to_expected_unit.post_return(&mut store)?; 2597 let err = to_expected_unit.call(&mut store, (2,)).unwrap_err(); 2598 assert!(err.to_string().contains("invalid expected"), "{}", err); 2599 2600 let instance = linker.instantiate(&mut store, &component)?; 2601 let to_expected_s16_f32 = instance 2602 .get_typed_func::<(u32, u32), (Result<i16, f32>,)>(&mut store, "to-expected-s16-f32")?; 2603 assert_eq!(to_expected_s16_f32.call(&mut store, (0, 0))?, (Ok(0),)); 2604 to_expected_s16_f32.post_return(&mut store)?; 2605 assert_eq!(to_expected_s16_f32.call(&mut store, (0, 100))?, (Ok(100),)); 2606 to_expected_s16_f32.post_return(&mut store)?; 2607 assert_eq!( 2608 to_expected_s16_f32.call(&mut store, (1, 1.0f32.to_bits()))?, 2609 (Err(1.0),) 2610 ); 2611 to_expected_s16_f32.post_return(&mut store)?; 2612 let ret = to_expected_s16_f32 2613 .call(&mut store, (1, CANON_32BIT_NAN | 1))? 2614 .0; 2615 assert_eq!(ret.unwrap_err().to_bits(), CANON_32BIT_NAN | 1); 2616 to_expected_s16_f32.post_return(&mut store)?; 2617 assert!(to_expected_s16_f32.call(&mut store, (2, 0)).is_err()); 2618 2619 Ok(()) 2620 } 2621 2622 #[test] 2623 fn fancy_list() -> Result<()> { 2624 let component = format!( 2625 r#"(component 2626 (core module $m 2627 (memory (export "memory") 1) 2628 (func (export "take") (param i32 i32) (result i32) 2629 (local $base i32) 2630 (local.set $base 2631 (call $realloc 2632 (i32.const 0) 2633 (i32.const 0) 2634 (i32.const 4) 2635 (i32.const 16))) 2636 2637 (i32.store offset=0 2638 (local.get $base) 2639 (local.get 0)) 2640 (i32.store offset=4 2641 (local.get $base) 2642 (local.get 1)) 2643 (i32.store offset=8 2644 (local.get $base) 2645 (i32.const 0)) 2646 (i32.store offset=12 2647 (local.get $base) 2648 (i32.mul 2649 (memory.size) 2650 (i32.const 65536))) 2651 2652 (local.get $base) 2653 ) 2654 2655 {REALLOC_AND_FREE} 2656 ) 2657 (core instance $i (instantiate $m)) 2658 2659 (type $a (option u8)) 2660 (type $b (result (error string))) 2661 (type $input (list (tuple $a $b))) 2662 (func (export "take") 2663 (param "a" $input) 2664 (result (tuple u32 u32 (list u8))) 2665 (canon lift 2666 (core func $i "take") 2667 (memory $i "memory") 2668 (realloc (func $i "realloc")) 2669 ) 2670 ) 2671 )"# 2672 ); 2673 2674 let engine = super::engine(); 2675 let component = Component::new(&engine, component)?; 2676 let mut store = Store::new(&engine, ()); 2677 let instance = Linker::new(&engine).instantiate(&mut store, &component)?; 2678 2679 let func = instance 2680 .get_typed_func::<(&[(Option<u8>, Result<(), &str>)],), ((u32, u32, WasmList<u8>),)>( 2681 &mut store, "take", 2682 )?; 2683 2684 let input = [ 2685 (None, Ok(())), 2686 (Some(2), Err("hello there")), 2687 (Some(200), Err("general kenobi")), 2688 ]; 2689 let ((ptr, len, list),) = func.call(&mut store, (&input,))?; 2690 let memory = list.as_le_slice(&store); 2691 let ptr = usize::try_from(ptr).unwrap(); 2692 let len = usize::try_from(len).unwrap(); 2693 let mut array = &memory[ptr..][..len * 16]; 2694 2695 for (a, b) in input.iter() { 2696 match a { 2697 Some(val) => { 2698 assert_eq!(*array.take_n::<2>(), [1, *val]); 2699 } 2700 None => { 2701 assert_eq!(*array.take_n::<1>(), [0]); 2702 array.skip::<1>(); 2703 } 2704 } 2705 array.skip::<2>(); 2706 match b { 2707 Ok(()) => { 2708 assert_eq!(*array.take_n::<1>(), [0]); 2709 array.skip::<11>(); 2710 } 2711 Err(s) => { 2712 assert_eq!(*array.take_n::<1>(), [1]); 2713 array.skip::<3>(); 2714 assert_eq!(array.ptr_len(memory, 1), s.as_bytes()); 2715 } 2716 } 2717 } 2718 assert!(array.is_empty()); 2719 2720 Ok(()) 2721 } 2722 2723 trait SliceExt<'a> { 2724 fn take_n<const N: usize>(&mut self) -> &'a [u8; N]; 2725 2726 fn skip<const N: usize>(&mut self) { 2727 self.take_n::<N>(); 2728 } 2729 2730 fn ptr_len<'b>(&mut self, all_memory: &'b [u8], size: usize) -> &'b [u8] { 2731 let ptr = u32::from_le_bytes(*self.take_n::<4>()); 2732 let len = u32::from_le_bytes(*self.take_n::<4>()); 2733 let ptr = usize::try_from(ptr).unwrap(); 2734 let len = usize::try_from(len).unwrap(); 2735 &all_memory[ptr..][..len * size] 2736 } 2737 } 2738 2739 impl<'a> SliceExt<'a> for &'a [u8] { 2740 fn take_n<const N: usize>(&mut self) -> &'a [u8; N] { 2741 let (a, b) = self.split_at(N); 2742 *self = b; 2743 a.try_into().unwrap() 2744 } 2745 } 2746 2747 #[test] 2748 fn invalid_alignment() -> Result<()> { 2749 let component = format!( 2750 r#"(component 2751 (core module $m 2752 (memory (export "memory") 1) 2753 (func (export "realloc") (param i32 i32 i32 i32) (result i32) 2754 i32.const 1) 2755 2756 (func (export "take-i32") (param i32)) 2757 (func (export "ret-1") (result i32) i32.const 1) 2758 (func (export "ret-unaligned-list") (result i32) 2759 (i32.store offset=0 (i32.const 8) (i32.const 1)) 2760 (i32.store offset=4 (i32.const 8) (i32.const 1)) 2761 i32.const 8) 2762 ) 2763 (core instance $i (instantiate $m)) 2764 2765 (func (export "many-params") 2766 (param "s1" string) (param "s2" string) (param "s3" string) (param "s4" string) 2767 (param "s5" string) (param "s6" string) (param "s7" string) (param "s8" string) 2768 (param "s9" string) (param "s10" string) (param "s11" string) (param "s12" string) 2769 (canon lift 2770 (core func $i "take-i32") 2771 (memory $i "memory") 2772 (realloc (func $i "realloc")) 2773 ) 2774 ) 2775 (func (export "string-ret") (result string) 2776 (canon lift 2777 (core func $i "ret-1") 2778 (memory $i "memory") 2779 (realloc (func $i "realloc")) 2780 ) 2781 ) 2782 (func (export "list-u32-ret") (result (list u32)) 2783 (canon lift 2784 (core func $i "ret-unaligned-list") 2785 (memory $i "memory") 2786 (realloc (func $i "realloc")) 2787 ) 2788 ) 2789 )"# 2790 ); 2791 2792 let engine = super::engine(); 2793 let component = Component::new(&engine, component)?; 2794 let mut store = Store::new(&engine, ()); 2795 let instance = |store: &mut Store<()>| Linker::new(&engine).instantiate(store, &component); 2796 2797 let err = instance(&mut store)? 2798 .get_typed_func::<( 2799 &str, 2800 &str, 2801 &str, 2802 &str, 2803 &str, 2804 &str, 2805 &str, 2806 &str, 2807 &str, 2808 &str, 2809 &str, 2810 &str, 2811 ), ()>(&mut store, "many-params")? 2812 .call(&mut store, ("", "", "", "", "", "", "", "", "", "", "", "")) 2813 .unwrap_err(); 2814 assert!( 2815 err.to_string() 2816 .contains("realloc return: result not aligned"), 2817 "{}", 2818 err 2819 ); 2820 2821 let err = instance(&mut store)? 2822 .get_typed_func::<(), (WasmStr,)>(&mut store, "string-ret")? 2823 .call(&mut store, ()) 2824 .err() 2825 .unwrap(); 2826 assert!( 2827 err.to_string().contains("return pointer not aligned"), 2828 "{}", 2829 err 2830 ); 2831 2832 let err = instance(&mut store)? 2833 .get_typed_func::<(), (WasmList<u32>,)>(&mut store, "list-u32-ret")? 2834 .call(&mut store, ()) 2835 .err() 2836 .unwrap(); 2837 assert!( 2838 err.to_string().contains("list pointer is not aligned"), 2839 "{}", 2840 err 2841 ); 2842 2843 Ok(()) 2844 } 2845 2846 #[test] 2847 fn drop_component_still_works() -> Result<()> { 2848 let component = r#" 2849 (component 2850 (import "f" (func $f)) 2851 2852 (core func $f_lower 2853 (canon lower (func $f)) 2854 ) 2855 (core module $m 2856 (import "" "" (func $f)) 2857 2858 (func $f2 2859 call $f 2860 call $f 2861 ) 2862 2863 (export "f" (func $f2)) 2864 ) 2865 (core instance $i (instantiate $m 2866 (with "" (instance 2867 (export "" (func $f_lower)) 2868 )) 2869 )) 2870 (func (export "g") 2871 (canon lift 2872 (core func $i "f") 2873 ) 2874 ) 2875 ) 2876 "#; 2877 2878 let (mut store, instance) = { 2879 let engine = super::engine(); 2880 let component = Component::new(&engine, component)?; 2881 let mut store = Store::new(&engine, 0); 2882 let mut linker = Linker::new(&engine); 2883 linker.root().func_wrap( 2884 "f", 2885 |mut store: StoreContextMut<'_, u32>, _: ()| -> Result<()> { 2886 *store.data_mut() += 1; 2887 Ok(()) 2888 }, 2889 )?; 2890 let instance = linker.instantiate(&mut store, &component)?; 2891 (store, instance) 2892 }; 2893 2894 let f = instance.get_typed_func::<(), ()>(&mut store, "g")?; 2895 assert_eq!(*store.data(), 0); 2896 f.call(&mut store, ())?; 2897 assert_eq!(*store.data(), 2); 2898 2899 Ok(()) 2900 } 2901 2902 #[test] 2903 fn raw_slice_of_various_types() -> Result<()> { 2904 let component = r#" 2905 (component 2906 (core module $m 2907 (memory (export "memory") 1) 2908 2909 (func (export "list8") (result i32) 2910 (call $setup_list (i32.const 16)) 2911 ) 2912 (func (export "list16") (result i32) 2913 (call $setup_list (i32.const 8)) 2914 ) 2915 (func (export "list32") (result i32) 2916 (call $setup_list (i32.const 4)) 2917 ) 2918 (func (export "list64") (result i32) 2919 (call $setup_list (i32.const 2)) 2920 ) 2921 2922 (func $setup_list (param i32) (result i32) 2923 (i32.store offset=0 (i32.const 100) (i32.const 8)) 2924 (i32.store offset=4 (i32.const 100) (local.get 0)) 2925 i32.const 100 2926 ) 2927 2928 (data (i32.const 8) "\00\01\02\03\04\05\06\07\08\09\0a\0b\0c\0d\0e\0f") 2929 ) 2930 (core instance $i (instantiate $m)) 2931 (func (export "list-u8") (result (list u8)) 2932 (canon lift (core func $i "list8") (memory $i "memory")) 2933 ) 2934 (func (export "list-i8") (result (list s8)) 2935 (canon lift (core func $i "list8") (memory $i "memory")) 2936 ) 2937 (func (export "list-u16") (result (list u16)) 2938 (canon lift (core func $i "list16") (memory $i "memory")) 2939 ) 2940 (func (export "list-i16") (result (list s16)) 2941 (canon lift (core func $i "list16") (memory $i "memory")) 2942 ) 2943 (func (export "list-u32") (result (list u32)) 2944 (canon lift (core func $i "list32") (memory $i "memory")) 2945 ) 2946 (func (export "list-i32") (result (list s32)) 2947 (canon lift (core func $i "list32") (memory $i "memory")) 2948 ) 2949 (func (export "list-u64") (result (list u64)) 2950 (canon lift (core func $i "list64") (memory $i "memory")) 2951 ) 2952 (func (export "list-i64") (result (list s64)) 2953 (canon lift (core func $i "list64") (memory $i "memory")) 2954 ) 2955 ) 2956 "#; 2957 2958 let (mut store, instance) = { 2959 let engine = super::engine(); 2960 let component = Component::new(&engine, component)?; 2961 let mut store = Store::new(&engine, ()); 2962 let instance = Linker::new(&engine).instantiate(&mut store, &component)?; 2963 (store, instance) 2964 }; 2965 2966 let list = instance 2967 .get_typed_func::<(), (WasmList<u8>,)>(&mut store, "list-u8")? 2968 .call_and_post_return(&mut store, ())? 2969 .0; 2970 assert_eq!( 2971 list.as_le_slice(&store), 2972 [ 2973 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 2974 0x0e, 0x0f, 2975 ] 2976 ); 2977 let list = instance 2978 .get_typed_func::<(), (WasmList<i8>,)>(&mut store, "list-i8")? 2979 .call_and_post_return(&mut store, ())? 2980 .0; 2981 assert_eq!( 2982 list.as_le_slice(&store), 2983 [ 2984 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 2985 0x0e, 0x0f, 2986 ] 2987 ); 2988 2989 let list = instance 2990 .get_typed_func::<(), (WasmList<u16>,)>(&mut store, "list-u16")? 2991 .call_and_post_return(&mut store, ())? 2992 .0; 2993 assert_eq!( 2994 list.as_le_slice(&store), 2995 [ 2996 u16::to_le(0x01_00), 2997 u16::to_le(0x03_02), 2998 u16::to_le(0x05_04), 2999 u16::to_le(0x07_06), 3000 u16::to_le(0x09_08), 3001 u16::to_le(0x0b_0a), 3002 u16::to_le(0x0d_0c), 3003 u16::to_le(0x0f_0e), 3004 ] 3005 ); 3006 let list = instance 3007 .get_typed_func::<(), (WasmList<i16>,)>(&mut store, "list-i16")? 3008 .call_and_post_return(&mut store, ())? 3009 .0; 3010 assert_eq!( 3011 list.as_le_slice(&store), 3012 [ 3013 i16::to_le(0x01_00), 3014 i16::to_le(0x03_02), 3015 i16::to_le(0x05_04), 3016 i16::to_le(0x07_06), 3017 i16::to_le(0x09_08), 3018 i16::to_le(0x0b_0a), 3019 i16::to_le(0x0d_0c), 3020 i16::to_le(0x0f_0e), 3021 ] 3022 ); 3023 let list = instance 3024 .get_typed_func::<(), (WasmList<u32>,)>(&mut store, "list-u32")? 3025 .call_and_post_return(&mut store, ())? 3026 .0; 3027 assert_eq!( 3028 list.as_le_slice(&store), 3029 [ 3030 u32::to_le(0x03_02_01_00), 3031 u32::to_le(0x07_06_05_04), 3032 u32::to_le(0x0b_0a_09_08), 3033 u32::to_le(0x0f_0e_0d_0c), 3034 ] 3035 ); 3036 let list = instance 3037 .get_typed_func::<(), (WasmList<i32>,)>(&mut store, "list-i32")? 3038 .call_and_post_return(&mut store, ())? 3039 .0; 3040 assert_eq!( 3041 list.as_le_slice(&store), 3042 [ 3043 i32::to_le(0x03_02_01_00), 3044 i32::to_le(0x07_06_05_04), 3045 i32::to_le(0x0b_0a_09_08), 3046 i32::to_le(0x0f_0e_0d_0c), 3047 ] 3048 ); 3049 let list = instance 3050 .get_typed_func::<(), (WasmList<u64>,)>(&mut store, "list-u64")? 3051 .call_and_post_return(&mut store, ())? 3052 .0; 3053 assert_eq!( 3054 list.as_le_slice(&store), 3055 [ 3056 u64::to_le(0x07_06_05_04_03_02_01_00), 3057 u64::to_le(0x0f_0e_0d_0c_0b_0a_09_08), 3058 ] 3059 ); 3060 let list = instance 3061 .get_typed_func::<(), (WasmList<i64>,)>(&mut store, "list-i64")? 3062 .call_and_post_return(&mut store, ())? 3063 .0; 3064 assert_eq!( 3065 list.as_le_slice(&store), 3066 [ 3067 i64::to_le(0x07_06_05_04_03_02_01_00), 3068 i64::to_le(0x0f_0e_0d_0c_0b_0a_09_08), 3069 ] 3070 ); 3071 3072 Ok(()) 3073 } 3074 3075 #[test] 3076 fn lower_then_lift() -> Result<()> { 3077 // First test simple integers when the import/export ABI happen to line up 3078 let component = r#" 3079 (component $c 3080 (import "f" (func $f (result u32))) 3081 3082 (core func $f_lower 3083 (canon lower (func $f)) 3084 ) 3085 (func $f2 (result s32) 3086 (canon lift (core func $f_lower)) 3087 ) 3088 (export "f2" (func $f2)) 3089 ) 3090 "#; 3091 3092 let engine = super::engine(); 3093 let component = Component::new(&engine, component)?; 3094 let mut store = Store::new(&engine, ()); 3095 let mut linker = Linker::new(&engine); 3096 linker.root().func_wrap("f", |_, _: ()| Ok((2u32,)))?; 3097 let instance = linker.instantiate(&mut store, &component)?; 3098 3099 let f = instance.get_typed_func::<(), (i32,)>(&mut store, "f2")?; 3100 assert_eq!(f.call(&mut store, ())?, (2,)); 3101 3102 // First test strings when the import/export ABI happen to line up 3103 let component = format!( 3104 r#" 3105 (component $c 3106 (import "s" (func $f (param "a" string))) 3107 3108 (core module $libc 3109 (memory (export "memory") 1) 3110 {REALLOC_AND_FREE} 3111 ) 3112 (core instance $libc (instantiate $libc)) 3113 3114 (core func $f_lower 3115 (canon lower (func $f) (memory $libc "memory")) 3116 ) 3117 (func $f2 (param "a" string) 3118 (canon lift (core func $f_lower) 3119 (memory $libc "memory") 3120 (realloc (func $libc "realloc")) 3121 ) 3122 ) 3123 (export "f" (func $f2)) 3124 ) 3125 "# 3126 ); 3127 3128 let component = Component::new(&engine, component)?; 3129 let mut store = Store::new(&engine, ()); 3130 linker 3131 .root() 3132 .func_wrap("s", |store: StoreContextMut<'_, ()>, (x,): (WasmStr,)| { 3133 assert_eq!(x.to_str(&store)?, "hello"); 3134 Ok(()) 3135 })?; 3136 let instance = linker.instantiate(&mut store, &component)?; 3137 3138 let f = instance.get_typed_func::<(&str,), ()>(&mut store, "f")?; 3139 f.call(&mut store, ("hello",))?; 3140 3141 // Next test "type punning" where return values are reinterpreted just 3142 // because the return ABI happens to line up. 3143 let component = format!( 3144 r#" 3145 (component $c 3146 (import "s2" (func $f (param "a" string) (result u32))) 3147 3148 (core module $libc 3149 (memory (export "memory") 1) 3150 {REALLOC_AND_FREE} 3151 ) 3152 (core instance $libc (instantiate $libc)) 3153 3154 (core func $f_lower 3155 (canon lower (func $f) (memory $libc "memory")) 3156 ) 3157 (func $f2 (param "a" string) (result string) 3158 (canon lift (core func $f_lower) 3159 (memory $libc "memory") 3160 (realloc (func $libc "realloc")) 3161 ) 3162 ) 3163 (export "f" (func $f2)) 3164 ) 3165 "# 3166 ); 3167 3168 let component = Component::new(&engine, component)?; 3169 let mut store = Store::new(&engine, ()); 3170 linker 3171 .root() 3172 .func_wrap("s2", |store: StoreContextMut<'_, ()>, (x,): (WasmStr,)| { 3173 assert_eq!(x.to_str(&store)?, "hello"); 3174 Ok((u32::MAX,)) 3175 })?; 3176 let instance = linker.instantiate(&mut store, &component)?; 3177 3178 let f = instance.get_typed_func::<(&str,), (WasmStr,)>(&mut store, "f")?; 3179 let err = f.call(&mut store, ("hello",)).err().unwrap(); 3180 assert!( 3181 err.to_string().contains("return pointer not aligned"), 3182 "{}", 3183 err 3184 ); 3185 3186 Ok(()) 3187 } 3188 3189 #[test] 3190 fn errors_that_poison_instance() -> Result<()> { 3191 let component = format!( 3192 r#" 3193 (component $c 3194 (core module $m1 3195 (func (export "f1") unreachable) 3196 (func (export "f2")) 3197 ) 3198 (core instance $m1 (instantiate $m1)) 3199 (func (export "f1") (canon lift (core func $m1 "f1"))) 3200 (func (export "f2") (canon lift (core func $m1 "f2"))) 3201 3202 (core module $m2 3203 (func (export "f") (param i32 i32)) 3204 (func (export "r") (param i32 i32 i32 i32) (result i32) unreachable) 3205 (memory (export "m") 1) 3206 ) 3207 (core instance $m2 (instantiate $m2)) 3208 (func (export "f3") (param "a" string) 3209 (canon lift (core func $m2 "f") (realloc (func $m2 "r")) (memory $m2 "m")) 3210 ) 3211 3212 (core module $m3 3213 (func (export "f") (result i32) i32.const 1) 3214 (memory (export "m") 1) 3215 ) 3216 (core instance $m3 (instantiate $m3)) 3217 (func (export "f4") (result string) 3218 (canon lift (core func $m3 "f") (memory $m3 "m")) 3219 ) 3220 ) 3221 "# 3222 ); 3223 3224 let engine = super::engine(); 3225 let component = Component::new(&engine, component)?; 3226 let mut store = Store::new(&engine, ()); 3227 let linker = Linker::new(&engine); 3228 let instance = linker.instantiate(&mut store, &component)?; 3229 let f1 = instance.get_typed_func::<(), ()>(&mut store, "f1")?; 3230 let f2 = instance.get_typed_func::<(), ()>(&mut store, "f2")?; 3231 assert_unreachable(f1.call(&mut store, ())); 3232 assert_poisoned(f1.call(&mut store, ())); 3233 assert_poisoned(f2.call(&mut store, ())); 3234 3235 let instance = linker.instantiate(&mut store, &component)?; 3236 let f3 = instance.get_typed_func::<(&str,), ()>(&mut store, "f3")?; 3237 assert_unreachable(f3.call(&mut store, ("x",))); 3238 assert_poisoned(f3.call(&mut store, ("x",))); 3239 3240 let instance = linker.instantiate(&mut store, &component)?; 3241 let f4 = instance.get_typed_func::<(), (WasmStr,)>(&mut store, "f4")?; 3242 assert!(f4.call(&mut store, ()).is_err()); 3243 assert_poisoned(f4.call(&mut store, ())); 3244 3245 return Ok(()); 3246 3247 #[track_caller] 3248 fn assert_unreachable<T>(err: Result<T>) { 3249 let err = match err { 3250 Ok(_) => panic!("expected an error"), 3251 Err(e) => e, 3252 }; 3253 assert_eq!( 3254 err.downcast::<Trap>().unwrap(), 3255 Trap::UnreachableCodeReached 3256 ); 3257 } 3258 3259 #[track_caller] 3260 fn assert_poisoned<T>(err: Result<T>) { 3261 let err = match err { 3262 Ok(_) => panic!("expected an error"), 3263 Err(e) => e, 3264 }; 3265 assert_eq!( 3266 err.downcast_ref::<Trap>(), 3267 Some(&Trap::CannotEnterComponent), 3268 "{err}", 3269 ); 3270 } 3271 } 3272 3273 #[test] 3274 fn run_export_with_internal_adapter() -> Result<()> { 3275 let component = r#" 3276 (component 3277 (type $t (func (param "a" u32) (result u32))) 3278 (component $a 3279 (core module $m 3280 (func (export "add-five") (param i32) (result i32) 3281 local.get 0 3282 i32.const 5 3283 i32.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 $b 3289 (import "interface-v1" (instance $i 3290 (export "add-five" (func (type $t))))) 3291 (core module $m 3292 (func $add-five (import "interface-0.1.0" "add-five") (param i32) (result i32)) 3293 (func) ;; causes index out of bounds 3294 (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 0 3298 (with "interface-0.1.0" (instance 3299 (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 "#; 3310 let engine = super::engine(); 3311 let component = Component::new(&engine, component)?; 3312 let mut store = Store::new(&engine, ()); 3313 let linker = Linker::new(&engine); 3314 let instance = linker.instantiate(&mut store, &component)?; 3315 let run = instance.get_typed_func::<(), (u32,)>(&mut store, "run")?; 3316 assert_eq!(run.call(&mut store, ())?, (5,)); 3317 Ok(()) 3318 } 3319