1 #![cfg(not(miri))] 2 3 use super::REALLOC_AND_FREE; 4 use std::ops::Deref; 5 use wasmtime::Result; 6 use wasmtime::component::*; 7 use wasmtime::{Config, Engine, Store, StoreContextMut, Trap, WasmBacktrace}; 8 9 #[test] 10 fn can_compile() -> Result<()> { 11 let engine = super::engine(); 12 let libc = r#" 13 (core module $libc 14 (memory (export "memory") 1) 15 (func (export "realloc") (param i32 i32 i32 i32) (result i32) 16 unreachable) 17 ) 18 (core instance $libc (instantiate $libc)) 19 "#; 20 Component::new( 21 &engine, 22 r#"(component 23 (import "a" (func $f)) 24 (core func (canon lower (func $f))) 25 )"#, 26 )?; 27 Component::new( 28 &engine, 29 format!( 30 r#"(component 31 (import "a" (func $f (param "a" string))) 32 {libc} 33 (core func (canon lower (func $f) (memory $libc "memory") (realloc (func $libc "realloc")))) 34 )"# 35 ), 36 )?; 37 Component::new( 38 &engine, 39 format!( 40 r#"(component 41 (import "f1" (func $f1 (param "a" string) (result string))) 42 {libc} 43 (core func (canon lower (func $f1) (memory $libc "memory") (realloc (func $libc "realloc")))) 44 45 (import "f2" (func $f2 (param "a" u32) (result (list u8)))) 46 (core instance $libc2 (instantiate $libc)) 47 (core func (canon lower (func $f2) (memory $libc2 "memory") (realloc (func $libc2 "realloc")))) 48 49 (core func (canon lower (func $f1) (memory $libc2 "memory") (realloc (func $libc2 "realloc")))) 50 (core func (canon lower (func $f2) (memory $libc "memory") (realloc (func $libc "realloc")))) 51 )"# 52 ), 53 )?; 54 Component::new( 55 &engine, 56 format!( 57 r#"(component 58 (import "log" (func $log (param "a" string))) 59 {libc} 60 (core func $log_lower (canon lower (func $log) (memory $libc "memory") (realloc (func $libc "realloc")))) 61 62 (core module $logger 63 (import "host" "log" (func $log (param i32 i32))) 64 (import "libc" "memory" (memory 1)) 65 66 (func (export "call") 67 i32.const 0 68 i32.const 0 69 call $log) 70 ) 71 (core instance $logger (instantiate $logger 72 (with "host" (instance (export "log" (func $log_lower)))) 73 (with "libc" (instance $libc)) 74 )) 75 76 (func (export "call") 77 (canon lift (core func $logger "call")) 78 ) 79 )"# 80 ), 81 )?; 82 Ok(()) 83 } 84 85 #[test] 86 fn simple() -> Result<()> { 87 let component = r#" 88 (component 89 (import "a" (func $log (param "a" string))) 90 91 (core module $libc 92 (memory (export "memory") 1) 93 94 (func (export "realloc") (param i32 i32 i32 i32) (result i32) 95 unreachable) 96 ) 97 (core instance $libc (instantiate $libc)) 98 (core func $log_lower 99 (canon lower (func $log) (memory $libc "memory") (realloc (func $libc "realloc"))) 100 ) 101 (core module $m 102 (import "libc" "memory" (memory 1)) 103 (import "host" "log" (func $log (param i32 i32))) 104 105 (func (export "call") 106 i32.const 5 107 i32.const 11 108 call $log) 109 110 (data (i32.const 5) "hello world") 111 ) 112 (core instance $i (instantiate $m 113 (with "libc" (instance $libc)) 114 (with "host" (instance (export "log" (func $log_lower)))) 115 )) 116 (func (export "call") 117 (canon lift (core func $i "call")) 118 ) 119 ) 120 "#; 121 122 let engine = super::engine(); 123 let component = Component::new(&engine, component)?; 124 let mut store = Store::new(&engine, None); 125 assert!(store.data().is_none()); 126 127 // First, test the static API 128 129 let mut linker = Linker::new(&engine); 130 linker.root().func_wrap( 131 "a", 132 |mut store: StoreContextMut<'_, Option<String>>, (arg,): (WasmStr,)| -> Result<_> { 133 let s = arg.to_str(&store)?.to_string(); 134 assert!(store.data().is_none()); 135 *store.data_mut() = Some(s); 136 Ok(()) 137 }, 138 )?; 139 let instance = linker.instantiate(&mut store, &component)?; 140 instance 141 .get_typed_func::<(), ()>(&mut store, "call")? 142 .call(&mut store, ())?; 143 assert_eq!(store.data().as_ref().unwrap(), "hello world"); 144 145 // Next, test the dynamic API 146 147 *store.data_mut() = None; 148 let mut linker = Linker::new(&engine); 149 linker.root().func_new( 150 "a", 151 |mut store: StoreContextMut<'_, Option<String>>, _, args, _results| { 152 if let Val::String(s) = &args[0] { 153 assert!(store.data().is_none()); 154 *store.data_mut() = Some(s.to_string()); 155 Ok(()) 156 } else { 157 panic!() 158 } 159 }, 160 )?; 161 let instance = linker.instantiate(&mut store, &component)?; 162 instance 163 .get_func(&mut store, "call") 164 .unwrap() 165 .call(&mut store, &[], &mut [])?; 166 assert_eq!(store.data().as_ref().unwrap(), "hello world"); 167 168 Ok(()) 169 } 170 171 #[test] 172 fn functions_in_instances() -> Result<()> { 173 let component = r#" 174 (component 175 (type $import-type (instance 176 (export "a" (func (param "a" string))) 177 )) 178 (import (interface "test:test/foo") (instance $import (type $import-type))) 179 (alias export $import "a" (func $log)) 180 181 (core module $libc 182 (memory (export "memory") 1) 183 184 (func (export "realloc") (param i32 i32 i32 i32) (result i32) 185 unreachable) 186 ) 187 (core instance $libc (instantiate $libc)) 188 (core func $log_lower 189 (canon lower (func $log) (memory $libc "memory") (realloc (func $libc "realloc"))) 190 ) 191 (core module $m 192 (import "libc" "memory" (memory 1)) 193 (import "host" "log" (func $log (param i32 i32))) 194 195 (func (export "call") 196 i32.const 5 197 i32.const 11 198 call $log) 199 200 (data (i32.const 5) "hello world") 201 ) 202 (core instance $i (instantiate $m 203 (with "libc" (instance $libc)) 204 (with "host" (instance (export "log" (func $log_lower)))) 205 )) 206 (func $call 207 (canon lift (core func $i "call")) 208 ) 209 (component $c 210 (import "import-call" (func $f)) 211 (export "call" (func $f)) 212 ) 213 (instance $export (instantiate $c 214 (with "import-call" (func $call)) 215 )) 216 (export (interface "test:test/foo") (instance $export)) 217 ) 218 "#; 219 220 let engine = super::engine(); 221 let component = Component::new(&engine, component)?; 222 let instance_index = component.get_export_index(None, "test:test/foo").unwrap(); 223 let func_index = component 224 .get_export_index(Some(&instance_index), "call") 225 .unwrap(); 226 let mut store = Store::new(&engine, None); 227 assert!(store.data().is_none()); 228 229 // First, test the static API 230 231 let mut linker = Linker::new(&engine); 232 linker.instance("test:test/foo")?.func_wrap( 233 "a", 234 |mut store: StoreContextMut<'_, Option<String>>, (arg,): (WasmStr,)| -> Result<_> { 235 let s = arg.to_str(&store)?.to_string(); 236 assert!(store.data().is_none()); 237 *store.data_mut() = Some(s); 238 Ok(()) 239 }, 240 )?; 241 let instance = linker.instantiate(&mut store, &component)?; 242 let func = instance.get_typed_func::<(), ()>(&mut store, &func_index)?; 243 func.call(&mut store, ())?; 244 assert_eq!(store.data().as_ref().unwrap(), "hello world"); 245 246 // Next, test the dynamic API 247 248 *store.data_mut() = None; 249 let mut linker = Linker::new(&engine); 250 linker.instance("test:test/foo")?.func_new( 251 "a", 252 |mut store: StoreContextMut<'_, Option<String>>, _, args, _results| { 253 if let Val::String(s) = &args[0] { 254 assert!(store.data().is_none()); 255 *store.data_mut() = Some(s.to_string()); 256 Ok(()) 257 } else { 258 panic!() 259 } 260 }, 261 )?; 262 let instance = linker.instantiate(&mut store, &component)?; 263 let func = instance.get_func(&mut store, func_index).unwrap(); 264 func.call(&mut store, &[], &mut [])?; 265 assert_eq!(store.data().as_ref().unwrap(), "hello world"); 266 267 Ok(()) 268 } 269 270 #[test] 271 fn attempt_to_leave_during_malloc() -> Result<()> { 272 let component = r#" 273 (component 274 (import "thunk" (func $thunk)) 275 (import "ret-string" (func $ret_string (result string))) 276 277 (core module $host_shim 278 (table (export "table") 2 funcref) 279 (func $shim_thunk (export "thunk") 280 i32.const 0 281 call_indirect) 282 (func $shim_ret_string (export "ret-string") (param i32) 283 local.get 0 284 i32.const 1 285 call_indirect (param i32)) 286 ) 287 (core instance $host_shim (instantiate $host_shim)) 288 289 (core module $m 290 (import "host" "thunk" (func $thunk)) 291 (import "host" "ret-string" (func $ret_string (param i32))) 292 293 (memory (export "memory") 1) 294 295 (func $realloc (export "realloc") (param i32 i32 i32 i32) (result i32) 296 call $thunk 297 unreachable) 298 299 (func $run (export "run") 300 i32.const 8 301 call $ret_string) 302 303 (func (export "take-string") (param i32 i32) 304 unreachable) 305 ) 306 (core instance $m (instantiate $m (with "host" (instance $host_shim)))) 307 308 (core module $host_shim_filler_inner 309 (import "shim" "table" (table 2 funcref)) 310 (import "host" "thunk" (func $thunk)) 311 (import "host" "ret-string" (func $ret_string (param i32))) 312 (elem (i32.const 0) $thunk $ret_string) 313 ) 314 315 (core func $thunk_lower 316 (canon lower (func $thunk) (memory $m "memory") (realloc (func $m "realloc"))) 317 ) 318 319 (core func $ret_string_lower 320 (canon lower (func $ret_string) (memory $m "memory") (realloc (func $m "realloc"))) 321 ) 322 323 (core instance (instantiate $host_shim_filler_inner 324 (with "shim" (instance $host_shim)) 325 (with "host" (instance 326 (export "thunk" (func $thunk_lower)) 327 (export "ret-string" (func $ret_string_lower)) 328 )) 329 )) 330 331 (func (export "run") 332 (canon lift (core func $m "run")) 333 ) 334 (func (export "take-string") (param "a" string) 335 (canon lift (core func $m "take-string") (memory $m "memory") (realloc (func $m "realloc"))) 336 ) 337 ) 338 "#; 339 340 let engine = super::engine(); 341 let mut linker = Linker::new(&engine); 342 linker.root().func_wrap("thunk", |_, _: ()| -> Result<()> { 343 panic!("should not get here") 344 })?; 345 linker 346 .root() 347 .func_wrap("ret-string", |_, _: ()| -> Result<_> { 348 Ok(("hello".to_string(),)) 349 })?; 350 let component = Component::new(&engine, component)?; 351 352 { 353 let mut store = Store::new(&engine, ()); 354 355 // Assert that during a host import if we return values to wasm that a trap 356 // happens if we try to leave the instance. 357 let trap = linker 358 .instantiate(&mut store, &component)? 359 .get_typed_func::<(), ()>(&mut store, "run")? 360 .call(&mut store, ()) 361 .unwrap_err(); 362 assert!( 363 format!("{trap:?}").contains("cannot leave component instance"), 364 "bad trap: {trap:?}", 365 ); 366 367 let trace = trap.downcast_ref::<WasmBacktrace>().unwrap().frames(); 368 assert_eq!(trace.len(), 4); 369 370 // This was our entry point... 371 assert_eq!(trace[3].module().name(), Some("m")); 372 assert_eq!(trace[3].func_name(), Some("run")); 373 374 // ... which called an imported function which ends up being originally 375 // defined by the shim instance. The shim instance then does an indirect 376 // call through a table which goes to the `canon.lower`'d host function 377 assert_eq!(trace[2].module().name(), Some("host_shim")); 378 assert_eq!(trace[2].func_name(), Some("shim_ret_string")); 379 380 // ... and the lowered host function will call realloc to allocate space for 381 // the result 382 assert_eq!(trace[1].module().name(), Some("m")); 383 assert_eq!(trace[1].func_name(), Some("realloc")); 384 385 // ... but realloc calls the shim instance and tries to exit the 386 // component, triggering a dynamic trap 387 assert_eq!(trace[0].module().name(), Some("host_shim")); 388 assert_eq!(trace[0].func_name(), Some("shim_thunk")); 389 } 390 391 { 392 let mut store = Store::new(&engine, ()); 393 394 // In addition to the above trap also ensure that when we enter a wasm 395 // component if we try to leave while lowering then that's also a dynamic 396 // trap. 397 let trap = linker 398 .instantiate(&mut store, &component)? 399 .get_typed_func::<(&str,), ()>(&mut store, "take-string")? 400 .call(&mut store, ("x",)) 401 .unwrap_err(); 402 assert!( 403 format!("{trap:?}").contains("cannot leave component instance"), 404 "bad trap: {trap:?}", 405 ); 406 } 407 408 Ok(()) 409 } 410 411 #[test] 412 fn attempt_to_reenter_during_host() -> Result<()> { 413 let component = r#" 414 (component 415 (import "thunk" (func $thunk)) 416 (core func $thunk_lower (canon lower (func $thunk))) 417 418 (core module $m 419 (import "host" "thunk" (func $thunk)) 420 421 (func $run (export "run") 422 call $thunk) 423 ) 424 (core instance $m (instantiate $m 425 (with "host" (instance (export "thunk" (func $thunk_lower)))) 426 )) 427 428 (func (export "run") 429 (canon lift (core func $m "run")) 430 ) 431 ) 432 "#; 433 434 let engine = super::engine(); 435 let component = Component::new(&engine, component)?; 436 437 // First, test the static API 438 439 struct StaticState { 440 func: Option<TypedFunc<(), ()>>, 441 } 442 443 let mut store = Store::new(&engine, StaticState { func: None }); 444 let mut linker = Linker::new(&engine); 445 linker.root().func_wrap( 446 "thunk", 447 |mut store: StoreContextMut<'_, StaticState>, _: ()| -> Result<()> { 448 let func = store.data_mut().func.take().unwrap(); 449 let trap = func.call(&mut store, ()).unwrap_err(); 450 assert_eq!( 451 trap.downcast_ref(), 452 Some(&Trap::CannotEnterComponent), 453 "bad trap: {trap:?}", 454 ); 455 Ok(()) 456 }, 457 )?; 458 let instance = linker.instantiate(&mut store, &component)?; 459 let func = instance.get_typed_func::<(), ()>(&mut store, "run")?; 460 store.data_mut().func = Some(func); 461 func.call(&mut store, ())?; 462 463 // Next, test the dynamic API 464 465 struct DynamicState { 466 func: Option<Func>, 467 } 468 469 let mut store = Store::new(&engine, DynamicState { func: None }); 470 let mut linker = Linker::new(&engine); 471 linker.root().func_new( 472 "thunk", 473 |mut store: StoreContextMut<'_, DynamicState>, _, _, _| { 474 let func = store.data_mut().func.take().unwrap(); 475 let trap = func.call(&mut store, &[], &mut []).unwrap_err(); 476 assert_eq!( 477 trap.downcast_ref(), 478 Some(&Trap::CannotEnterComponent), 479 "bad trap: {trap:?}", 480 ); 481 Ok(()) 482 }, 483 )?; 484 let instance = linker.instantiate(&mut store, &component)?; 485 let func = instance.get_func(&mut store, "run").unwrap(); 486 store.data_mut().func = Some(func); 487 func.call(&mut store, &[], &mut [])?; 488 489 Ok(()) 490 } 491 492 #[tokio::test] 493 async fn stack_and_heap_args_and_rets() -> Result<()> { 494 test_stack_and_heap_args_and_rets(false).await 495 } 496 497 #[tokio::test] 498 async fn stack_and_heap_args_and_rets_concurrent() -> Result<()> { 499 test_stack_and_heap_args_and_rets(true).await 500 } 501 502 async fn test_stack_and_heap_args_and_rets(concurrent: bool) -> Result<()> { 503 let (body, async_lower_opts, async_lift_opts, async_type) = if concurrent { 504 ( 505 r#" 506 (import "host" "f1" (func $f1 (param i32 i32) (result i32))) 507 (import "host" "f2" (func $f2 (param i32 i32) (result i32))) 508 (import "host" "f3" (func $f3 (param i32 i32) (result i32))) 509 (import "host" "f4" (func $f4 (param i32 i32) (result i32))) 510 511 (func $run (export "run") (result i32) 512 (local $params i32) 513 (local $results i32) 514 515 block 516 (local.set $results (call $realloc (i32.const 0) (i32.const 0) (i32.const 4) (i32.const 4))) 517 (call $f1 (i32.const 1) (local.get $results)) 518 drop 519 (i32.load offset=0 (local.get $results)) 520 i32.const 2 521 i32.eq 522 br_if 0 523 unreachable 524 end 525 526 block 527 (local.set $params (call $allocate_empty_strings)) 528 (local.set $results (call $realloc (i32.const 0) (i32.const 0) (i32.const 4) (i32.const 4))) 529 (call $f2 (local.get $params) (local.get $results)) 530 drop 531 (i32.load offset=0 (local.get $results)) 532 i32.const 3 533 i32.eq 534 br_if 0 535 unreachable 536 end 537 538 block 539 (local.set $results (call $realloc (i32.const 0) (i32.const 0) (i32.const 4) (i32.const 8))) 540 (call $f3 (i32.const 8) (local.get $results)) 541 drop 542 (call $validate_string_ret (local.get $results)) 543 end 544 545 block 546 (local.set $params (call $allocate_empty_strings)) 547 (local.set $results (call $realloc (i32.const 0) (i32.const 0) (i32.const 4) (i32.const 8))) 548 (call $f4 (local.get $params) (local.get $results)) 549 drop 550 (call $validate_string_ret (local.get $results)) 551 end 552 553 (call $task-return) 554 555 i32.const 0 556 ) 557 "#, 558 "async", 559 r#"async (callback (func $m "callback"))"#, 560 "async", 561 ) 562 } else { 563 ( 564 r#" 565 (import "host" "f1" (func $f1 (param i32) (result i32))) 566 (import "host" "f2" (func $f2 (param i32) (result i32))) 567 (import "host" "f3" (func $f3 (param i32 i32))) 568 (import "host" "f4" (func $f4 (param i32 i32))) 569 570 (func $run (export "run") 571 block 572 i32.const 1 573 call $f1 574 i32.const 2 575 i32.eq 576 br_if 0 577 unreachable 578 end 579 580 block 581 call $allocate_empty_strings 582 call $f2 583 i32.const 3 584 i32.eq 585 br_if 0 586 unreachable 587 end 588 589 block 590 i32.const 8 591 i32.const 16000 592 call $f3 593 (call $validate_string_ret (i32.const 16000)) 594 end 595 596 block 597 call $allocate_empty_strings 598 i32.const 20000 599 call $f4 600 (call $validate_string_ret (i32.const 20000)) 601 end 602 ) 603 "#, 604 "", 605 "", 606 "", 607 ) 608 }; 609 610 let component = format!( 611 r#" 612 (component 613 (type $many_params (tuple 614 string string string string 615 string string string string 616 string)) 617 (import "f1" (func $f1 {async_type} (param "a" u32) (result u32))) 618 (import "f2" (func $f2 {async_type} (param "a" $many_params) (result u32))) 619 (import "f3" (func $f3 {async_type} (param "a" u32) (result string))) 620 (import "f4" (func $f4 {async_type} (param "a" $many_params) (result string))) 621 622 (core module $libc 623 {REALLOC_AND_FREE} 624 (memory (export "memory") 1) 625 ) 626 (core instance $libc (instantiate (module $libc))) 627 628 (core func $f1_lower (canon lower (func $f1) 629 (memory $libc "memory") 630 (realloc (func $libc "realloc")) 631 {async_lower_opts} 632 )) 633 (core func $f2_lower (canon lower (func $f2) 634 (memory $libc "memory") 635 (realloc (func $libc "realloc")) 636 {async_lower_opts} 637 )) 638 (core func $f3_lower (canon lower (func $f3) 639 (memory $libc "memory") 640 (realloc (func $libc "realloc")) 641 {async_lower_opts} 642 )) 643 (core func $f4_lower (canon lower (func $f4) 644 (memory $libc "memory") 645 (realloc (func $libc "realloc")) 646 {async_lower_opts} 647 )) 648 649 (core module $m 650 (import "libc" "memory" (memory 1)) 651 (import "libc" "realloc" (func $realloc (param i32 i32 i32 i32) (result i32))) 652 (import "host" "task.return" (func $task-return)) 653 {body} 654 655 (func (export "callback") (param i32 i32 i32) (result i32) unreachable) 656 657 (func $allocate_empty_strings (result i32) 658 (local $ret i32) 659 (local $offset i32) 660 (local $cnt i32) 661 (local.set $ret (i32.const 8000)) 662 (local.set $cnt (i32.const 9)) 663 664 loop 665 (call $setup_str (i32.add (local.get $ret) (local.get $offset))) 666 (local.set $offset (i32.add (local.get $offset) (i32.const 8))) 667 668 (local.tee $cnt (i32.add (local.get $cnt) (i32.const -1))) 669 br_if 0 670 end 671 672 local.get $ret 673 ) 674 (func $setup_str (param $addr i32) 675 (i32.store offset=0 (local.get $addr) (i32.const 1000)) 676 (i32.store offset=4 (local.get $addr) (i32.const 3)) 677 ) 678 679 (func $validate_string_ret (param $addr i32) 680 (local $base i32) 681 (local $len i32) 682 (local.set $base (i32.load (local.get $addr))) 683 (local.set $len (i32.load offset=4 (local.get $addr))) 684 685 block 686 local.get $len 687 i32.const 3 688 i32.eq 689 br_if 0 690 unreachable 691 end 692 693 (i32.load8_u offset=0 (local.get $base)) 694 i32.const 120 ;; 'x' 695 i32.ne 696 if unreachable end 697 698 (i32.load8_u offset=1 (local.get $base)) 699 i32.const 121 ;; 'y' 700 i32.ne 701 if unreachable end 702 703 (i32.load8_u offset=2 (local.get $base)) 704 i32.const 122 ;; 'z' 705 i32.ne 706 if unreachable end 707 ) 708 709 (data (i32.const 1000) "abc") 710 ) 711 (core func $task-return (canon task.return)) 712 (core instance $m (instantiate $m 713 (with "libc" (instance $libc)) 714 (with "host" (instance 715 (export "f1" (func $f1_lower)) 716 (export "f2" (func $f2_lower)) 717 (export "f3" (func $f3_lower)) 718 (export "f4" (func $f4_lower)) 719 (export "task.return" (func $task-return)) 720 )) 721 )) 722 723 (func (export "run") {async_type} 724 (canon lift (core func $m "run") {async_lift_opts}) 725 ) 726 ) 727 "# 728 ); 729 730 let mut config = Config::new(); 731 config.wasm_component_model_async(true); 732 let engine = &Engine::new(&config)?; 733 let component = Component::new(&engine, component)?; 734 let mut store = Store::new(&engine, ()); 735 736 // First, test the static API 737 738 let mut linker = Linker::new(&engine); 739 if concurrent { 740 linker 741 .root() 742 .func_wrap_concurrent("f1", |_, (x,): (u32,)| { 743 assert_eq!(x, 1); 744 Box::pin(async { Ok((2u32,)) }) 745 })?; 746 linker.root().func_wrap_concurrent( 747 "f2", 748 |accessor, 749 (arg,): (( 750 WasmStr, 751 WasmStr, 752 WasmStr, 753 WasmStr, 754 WasmStr, 755 WasmStr, 756 WasmStr, 757 WasmStr, 758 WasmStr, 759 ),)| { 760 accessor.with(|v| assert_eq!(arg.0.to_str(&v).unwrap(), "abc")); 761 Box::pin(async { Ok((3u32,)) }) 762 }, 763 )?; 764 linker 765 .root() 766 .func_wrap_concurrent("f3", |_, (arg,): (u32,)| { 767 assert_eq!(arg, 8); 768 Box::pin(async { Ok(("xyz".to_string(),)) }) 769 })?; 770 linker.root().func_wrap_concurrent( 771 "f4", 772 |accessor, 773 (arg,): (( 774 WasmStr, 775 WasmStr, 776 WasmStr, 777 WasmStr, 778 WasmStr, 779 WasmStr, 780 WasmStr, 781 WasmStr, 782 WasmStr, 783 ),)| { 784 accessor.with(|v| assert_eq!(arg.0.to_str(&v).unwrap(), "abc")); 785 Box::pin(async { Ok(("xyz".to_string(),)) }) 786 }, 787 )?; 788 } else { 789 linker 790 .root() 791 .func_wrap("f1", |_, (x,): (u32,)| -> Result<(u32,)> { 792 assert_eq!(x, 1); 793 Ok((2,)) 794 })?; 795 linker.root().func_wrap( 796 "f2", 797 |cx: StoreContextMut<'_, ()>, 798 (arg,): (( 799 WasmStr, 800 WasmStr, 801 WasmStr, 802 WasmStr, 803 WasmStr, 804 WasmStr, 805 WasmStr, 806 WasmStr, 807 WasmStr, 808 ),)| 809 -> Result<(u32,)> { 810 assert_eq!(arg.0.to_str(&cx).unwrap(), "abc"); 811 Ok((3,)) 812 }, 813 )?; 814 linker 815 .root() 816 .func_wrap("f3", |_, (arg,): (u32,)| -> Result<(String,)> { 817 assert_eq!(arg, 8); 818 Ok(("xyz".to_string(),)) 819 })?; 820 linker.root().func_wrap( 821 "f4", 822 |cx: StoreContextMut<'_, ()>, 823 (arg,): (( 824 WasmStr, 825 WasmStr, 826 WasmStr, 827 WasmStr, 828 WasmStr, 829 WasmStr, 830 WasmStr, 831 WasmStr, 832 WasmStr, 833 ),)| 834 -> Result<(String,)> { 835 assert_eq!(arg.0.to_str(&cx).unwrap(), "abc"); 836 Ok(("xyz".to_string(),)) 837 }, 838 )?; 839 } 840 841 let instance = linker.instantiate_async(&mut store, &component).await?; 842 let run = instance.get_typed_func::<(), ()>(&mut store, "run")?; 843 844 if concurrent { 845 store 846 .run_concurrent(async move |accessor| { 847 wasmtime::error::Ok(run.call_concurrent(accessor, ()).await?.0) 848 }) 849 .await??; 850 } else { 851 run.call_async(&mut store, ()).await?; 852 } 853 854 // Next, test the dynamic API 855 856 let mut linker = Linker::new(&engine); 857 if concurrent { 858 linker 859 .root() 860 .func_new_concurrent("f1", |_, _, args, results| { 861 if let Val::U32(x) = &args[0] { 862 assert_eq!(*x, 1); 863 Box::pin(async { 864 results[0] = Val::U32(2); 865 Ok(()) 866 }) 867 } else { 868 panic!() 869 } 870 })?; 871 linker 872 .root() 873 .func_new_concurrent("f2", |_, _, args, results| { 874 if let Val::Tuple(tuple) = &args[0] { 875 if let Val::String(s) = &tuple[0] { 876 assert_eq!(s.deref(), "abc"); 877 Box::pin(async { 878 results[0] = Val::U32(3); 879 Ok(()) 880 }) 881 } else { 882 panic!() 883 } 884 } else { 885 panic!() 886 } 887 })?; 888 linker 889 .root() 890 .func_new_concurrent("f3", |_, _, args, results| { 891 if let Val::U32(x) = &args[0] { 892 assert_eq!(*x, 8); 893 Box::pin(async { 894 results[0] = Val::String("xyz".into()); 895 Ok(()) 896 }) 897 } else { 898 panic!(); 899 } 900 })?; 901 linker 902 .root() 903 .func_new_concurrent("f4", |_, _, args, results| { 904 if let Val::Tuple(tuple) = &args[0] { 905 if let Val::String(s) = &tuple[0] { 906 assert_eq!(s.deref(), "abc"); 907 Box::pin(async { 908 results[0] = Val::String("xyz".into()); 909 Ok(()) 910 }) 911 } else { 912 panic!() 913 } 914 } else { 915 panic!() 916 } 917 })?; 918 } else { 919 linker.root().func_new("f1", |_, _, args, results| { 920 if let Val::U32(x) = &args[0] { 921 assert_eq!(*x, 1); 922 results[0] = Val::U32(2); 923 Ok(()) 924 } else { 925 panic!() 926 } 927 })?; 928 linker.root().func_new("f2", |_, _, args, results| { 929 if let Val::Tuple(tuple) = &args[0] { 930 if let Val::String(s) = &tuple[0] { 931 assert_eq!(s.deref(), "abc"); 932 results[0] = Val::U32(3); 933 Ok(()) 934 } else { 935 panic!() 936 } 937 } else { 938 panic!() 939 } 940 })?; 941 linker.root().func_new("f3", |_, _, args, results| { 942 if let Val::U32(x) = &args[0] { 943 assert_eq!(*x, 8); 944 results[0] = Val::String("xyz".into()); 945 Ok(()) 946 } else { 947 panic!(); 948 } 949 })?; 950 linker.root().func_new("f4", |_, _, args, results| { 951 if let Val::Tuple(tuple) = &args[0] { 952 if let Val::String(s) = &tuple[0] { 953 assert_eq!(s.deref(), "abc"); 954 results[0] = Val::String("xyz".into()); 955 Ok(()) 956 } else { 957 panic!() 958 } 959 } else { 960 panic!() 961 } 962 })?; 963 } 964 965 let instance = linker.instantiate_async(&mut store, &component).await?; 966 let run = instance.get_func(&mut store, "run").unwrap(); 967 968 if concurrent { 969 store 970 .run_concurrent(async |store| { 971 run.call_concurrent(store, &[], &mut []).await?; 972 wasmtime::error::Ok(()) 973 }) 974 .await??; 975 } else { 976 run.call_async(&mut store, &[], &mut []).await?; 977 } 978 979 Ok(()) 980 } 981 982 #[test] 983 fn bad_import_alignment() -> Result<()> { 984 let component = format!( 985 r#" 986 (component 987 (import "unaligned-retptr" (func $unaligned_retptr (result string))) 988 (type $many_arg (tuple 989 string string string string 990 string string string string 991 string 992 )) 993 (import "unaligned-argptr" (func $unaligned_argptr (param "a" $many_arg))) 994 (core module $libc_panic 995 (memory (export "memory") 1) 996 (func (export "realloc") (param i32 i32 i32 i32) (result i32) 997 unreachable) 998 ) 999 (core instance $libc_panic (instantiate $libc_panic)) 1000 1001 (core func $unaligned_retptr_lower 1002 (canon lower (func $unaligned_retptr) (memory $libc_panic "memory") (realloc (func $libc_panic "realloc"))) 1003 ) 1004 (core func $unaligned_argptr_lower 1005 (canon lower (func $unaligned_argptr) (memory $libc_panic "memory") (realloc (func $libc_panic "realloc"))) 1006 ) 1007 1008 (core module $m 1009 (import "host" "unaligned-retptr" (func $unaligned_retptr (param i32))) 1010 (import "host" "unaligned-argptr" (func $unaligned_argptr (param i32))) 1011 1012 (func (export "unaligned-retptr") 1013 (call $unaligned_retptr (i32.const 1))) 1014 (func (export "unaligned-argptr") 1015 (call $unaligned_argptr (i32.const 1))) 1016 ) 1017 (core instance $m (instantiate $m 1018 (with "host" (instance 1019 (export "unaligned-retptr" (func $unaligned_retptr_lower)) 1020 (export "unaligned-argptr" (func $unaligned_argptr_lower)) 1021 )) 1022 )) 1023 1024 (func (export "unaligned-retptr2") 1025 (canon lift (core func $m "unaligned-retptr")) 1026 ) 1027 (func (export "unaligned-argptr2") 1028 (canon lift (core func $m "unaligned-argptr")) 1029 ) 1030 ) 1031 "# 1032 ); 1033 1034 let engine = super::engine(); 1035 let mut linker = Linker::new(&engine); 1036 linker 1037 .root() 1038 .func_wrap("unaligned-retptr", |_, _: ()| -> Result<(String,)> { 1039 Ok((String::new(),)) 1040 })?; 1041 linker.root().func_wrap( 1042 "unaligned-argptr", 1043 |_, 1044 _: (( 1045 WasmStr, 1046 WasmStr, 1047 WasmStr, 1048 WasmStr, 1049 WasmStr, 1050 WasmStr, 1051 WasmStr, 1052 WasmStr, 1053 WasmStr, 1054 ),)| 1055 -> Result<()> { unreachable!() }, 1056 )?; 1057 let component = Component::new(&engine, component)?; 1058 1059 { 1060 let mut store = Store::new(&engine, ()); 1061 let trap = linker 1062 .instantiate(&mut store, &component)? 1063 .get_typed_func::<(), ()>(&mut store, "unaligned-retptr2")? 1064 .call(&mut store, ()) 1065 .unwrap_err(); 1066 assert!( 1067 format!("{trap:?}").contains("pointer not aligned"), 1068 "{}", 1069 trap 1070 ); 1071 } 1072 1073 { 1074 let mut store = Store::new(&engine, ()); 1075 let trap = linker 1076 .instantiate(&mut store, &component)? 1077 .get_typed_func::<(), ()>(&mut store, "unaligned-argptr2")? 1078 .call(&mut store, ()) 1079 .unwrap_err(); 1080 assert!( 1081 format!("{trap:?}").contains("pointer not aligned"), 1082 "{}", 1083 trap 1084 ); 1085 } 1086 1087 Ok(()) 1088 } 1089 1090 #[test] 1091 fn no_actual_wasm_code() -> Result<()> { 1092 let component = r#" 1093 (component 1094 (import "f" (func $f)) 1095 1096 (core func $f_lower 1097 (canon lower (func $f)) 1098 ) 1099 (core module $m 1100 (import "" "" (func $f)) 1101 (export "f" (func $f)) 1102 ) 1103 (core instance $i (instantiate $m 1104 (with "" (instance 1105 (export "" (func $f_lower)) 1106 )) 1107 )) 1108 (func (export "thunk") 1109 (canon lift 1110 (core func $i "f") 1111 ) 1112 ) 1113 ) 1114 "#; 1115 1116 let engine = super::engine(); 1117 let component = Component::new(&engine, component)?; 1118 let mut store = Store::new(&engine, 0); 1119 1120 // First, test the static API 1121 1122 let mut linker = Linker::new(&engine); 1123 linker.root().func_wrap( 1124 "f", 1125 |mut store: StoreContextMut<'_, u32>, _: ()| -> Result<()> { 1126 *store.data_mut() += 1; 1127 Ok(()) 1128 }, 1129 )?; 1130 1131 let instance = linker.instantiate(&mut store, &component)?; 1132 let thunk = instance.get_typed_func::<(), ()>(&mut store, "thunk")?; 1133 1134 assert_eq!(*store.data(), 0); 1135 thunk.call(&mut store, ())?; 1136 assert_eq!(*store.data(), 1); 1137 1138 // Next, test the dynamic API 1139 1140 *store.data_mut() = 0; 1141 let mut linker = Linker::new(&engine); 1142 linker 1143 .root() 1144 .func_new("f", |mut store: StoreContextMut<'_, u32>, _, _, _| { 1145 *store.data_mut() += 1; 1146 Ok(()) 1147 })?; 1148 1149 let instance = linker.instantiate(&mut store, &component)?; 1150 let thunk = instance.get_func(&mut store, "thunk").unwrap(); 1151 1152 assert_eq!(*store.data(), 0); 1153 thunk.call(&mut store, &[], &mut [])?; 1154 assert_eq!(*store.data(), 1); 1155 1156 Ok(()) 1157 } 1158 1159 #[test] 1160 fn use_types_across_component_boundaries() -> Result<()> { 1161 // Create a component that exports a function that returns a record 1162 let engine = super::engine(); 1163 let component = Component::new( 1164 &engine, 1165 r#"(component 1166 (type (;0;) (record (field "a" u8) (field "b" string))) 1167 (import "my-record" (type $my-record (eq 0))) 1168 (core module $m 1169 (memory $memory 17) 1170 (export "memory" (memory $memory)) 1171 (func (export "my-func") (result i32) 1172 i32.const 4 1173 return)) 1174 (core instance $instance (instantiate $m)) 1175 (type $func-type (func (result $my-record))) 1176 (alias core export $instance "my-func" (core func $my-func)) 1177 (alias core export $instance "memory" (core memory $memory)) 1178 (func $my-func (type $func-type) (canon lift (core func $my-func) (memory $memory) string-encoding=utf8)) 1179 (export $export "my-func" (func $my-func)) 1180 )"#, 1181 )?; 1182 let mut store = Store::new(&engine, 0); 1183 let linker = Linker::new(&engine); 1184 let instance = linker.instantiate(&mut store, &component)?; 1185 let my_func = instance.get_func(&mut store, "my-func").unwrap(); 1186 let mut results = vec![Val::Bool(false)]; 1187 my_func.call(&mut store, &[], &mut results)?; 1188 1189 // Create another component that exports a function that takes that record as an argument 1190 let component = Component::new( 1191 &engine, 1192 format!( 1193 r#"(component 1194 (type (;0;) (record (field "a" u8) (field "b" string))) 1195 (import "my-record" (type $my-record (eq 0))) 1196 (core module $m 1197 (memory $memory 17) 1198 (export "memory" (memory $memory)) 1199 {REALLOC_AND_FREE} 1200 (func (export "my-func") (param i32 i32 i32))) 1201 (core instance $instance (instantiate $m)) 1202 (type $func-type (func (param "my-record" $my-record))) 1203 (alias core export $instance "my-func" (core func $my-func)) 1204 (alias core export $instance "memory" (core memory $memory)) 1205 (func $my-func (type $func-type) (canon lift (core func $my-func) (memory $memory) string-encoding=utf8 (realloc (func $instance "realloc")))) 1206 (export $export "my-func" (func $my-func)) 1207 )"# 1208 ), 1209 )?; 1210 let mut store = Store::new(&engine, 0); 1211 let linker = Linker::new(&engine); 1212 let instance = linker.instantiate(&mut store, &component)?; 1213 let my_func = instance.get_func(&mut store, "my-func").unwrap(); 1214 // Call the exported function with the return values of the call to the previous component's exported function 1215 my_func.call(&mut store, &results, &mut [])?; 1216 1217 Ok(()) 1218 } 1219