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