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