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