1 #![cfg(not(miri))] 2 3 use super::engine; 4 use anyhow::Result; 5 use wasmtime::{ 6 Store, 7 component::{Component, Linker}, 8 }; 9 10 mod ownership; 11 mod results; 12 13 mod no_imports { 14 use super::*; 15 use std::rc::Rc; 16 17 wasmtime::component::bindgen!({ 18 inline: " 19 package foo:foo; 20 21 world no-imports { 22 export foo: interface { 23 foo: func(); 24 } 25 26 export bar: func(); 27 } 28 ", 29 }); 30 31 #[test] 32 fn run() -> Result<()> { 33 let engine = engine(); 34 35 let component = Component::new( 36 &engine, 37 r#" 38 (component 39 (core module $m 40 (func (export "")) 41 ) 42 (core instance $i (instantiate $m)) 43 44 (func $f (export "bar") (canon lift (core func $i ""))) 45 46 (instance $i (export "foo" (func $f))) 47 (export "foo" (instance $i)) 48 ) 49 "#, 50 )?; 51 52 let linker = Linker::new(&engine); 53 let mut store = Store::new(&engine, ()); 54 let no_imports = NoImports::instantiate(&mut store, &component, &linker)?; 55 no_imports.call_bar(&mut store)?; 56 no_imports.foo().call_foo(&mut store)?; 57 58 let linker = Linker::new(&engine); 59 let mut non_send_store = Store::new(&engine, Rc::new(())); 60 let no_imports = NoImports::instantiate(&mut non_send_store, &component, &linker)?; 61 no_imports.call_bar(&mut non_send_store)?; 62 no_imports.foo().call_foo(&mut non_send_store)?; 63 Ok(()) 64 } 65 } 66 67 mod one_import { 68 use super::*; 69 use wasmtime::component::HasSelf; 70 71 wasmtime::component::bindgen!({ 72 inline: " 73 package foo:foo; 74 75 world one-import { 76 import foo: interface { 77 foo: func(); 78 } 79 80 export bar: func(); 81 } 82 ", 83 }); 84 85 #[test] 86 fn run() -> Result<()> { 87 let engine = engine(); 88 89 let component = Component::new( 90 &engine, 91 r#" 92 (component 93 (import "foo" (instance $i 94 (export "foo" (func)) 95 )) 96 (core module $m 97 (import "" "" (func)) 98 (export "" (func 0)) 99 ) 100 (core func $f (canon lower (func $i "foo"))) 101 (core instance $i (instantiate $m 102 (with "" (instance (export "" (func $f)))) 103 )) 104 105 (func $f (export "bar") (canon lift (core func $i ""))) 106 ) 107 "#, 108 )?; 109 110 #[derive(Default)] 111 struct MyImports { 112 hit: bool, 113 } 114 115 impl foo::Host for MyImports { 116 fn foo(&mut self) { 117 self.hit = true; 118 } 119 } 120 121 let mut linker = Linker::new(&engine); 122 foo::add_to_linker::<_, HasSelf<_>>(&mut linker, |f| f)?; 123 let mut store = Store::new(&engine, MyImports::default()); 124 let one_import = OneImport::instantiate(&mut store, &component, &linker)?; 125 one_import.call_bar(&mut store)?; 126 assert!(store.data().hit); 127 Ok(()) 128 } 129 } 130 131 mod resources_at_world_level { 132 use super::*; 133 use wasmtime::component::{HasSelf, Resource}; 134 135 wasmtime::component::bindgen!({ 136 inline: " 137 package foo:foo; 138 139 world resources { 140 resource x { 141 constructor(); 142 } 143 144 export y: func(x: x); 145 } 146 ", 147 }); 148 149 #[test] 150 fn run() -> Result<()> { 151 let engine = engine(); 152 153 let component = Component::new( 154 &engine, 155 r#" 156 (component 157 (import "x" (type $x (sub resource))) 158 (import "[constructor]x" (func $ctor (result (own $x)))) 159 160 (core func $dtor (canon resource.drop $x)) 161 (core func $ctor (canon lower (func $ctor))) 162 163 (core module $m 164 (import "" "ctor" (func $ctor (result i32))) 165 (import "" "dtor" (func $dtor (param i32))) 166 167 (func (export "x") (param i32) 168 (call $dtor (local.get 0)) 169 (call $dtor (call $ctor)) 170 ) 171 ) 172 (core instance $i (instantiate $m 173 (with "" (instance 174 (export "ctor" (func $ctor)) 175 (export "dtor" (func $dtor)) 176 )) 177 )) 178 (func (export "y") (param "x" (own $x)) 179 (canon lift (core func $i "x"))) 180 ) 181 "#, 182 )?; 183 184 #[derive(Default)] 185 struct MyImports { 186 ctor_hit: bool, 187 drops: usize, 188 } 189 190 impl HostX for MyImports { 191 fn new(&mut self) -> Resource<X> { 192 self.ctor_hit = true; 193 Resource::new_own(80) 194 } 195 196 fn drop(&mut self, val: Resource<X>) -> Result<()> { 197 match self.drops { 198 0 => assert_eq!(val.rep(), 40), 199 1 => assert_eq!(val.rep(), 80), 200 _ => unreachable!(), 201 } 202 self.drops += 1; 203 Ok(()) 204 } 205 } 206 207 impl ResourcesImports for MyImports {} 208 209 let mut linker = Linker::new(&engine); 210 Resources::add_to_linker::<_, HasSelf<_>>(&mut linker, |f| f)?; 211 let mut store = Store::new(&engine, MyImports::default()); 212 let one_import = Resources::instantiate(&mut store, &component, &linker)?; 213 one_import.call_y(&mut store, Resource::new_own(40))?; 214 assert!(store.data().ctor_hit); 215 assert_eq!(store.data().drops, 2); 216 Ok(()) 217 } 218 } 219 220 mod resources_at_interface_level { 221 use super::*; 222 use wasmtime::component::{HasSelf, Resource}; 223 224 wasmtime::component::bindgen!({ 225 inline: " 226 package foo:foo; 227 228 interface def { 229 resource x { 230 constructor(); 231 } 232 } 233 234 interface user { 235 use def.{x}; 236 237 y: func(x: x); 238 } 239 240 world resources { 241 export user; 242 } 243 ", 244 }); 245 246 #[test] 247 fn run() -> Result<()> { 248 let engine = engine(); 249 250 let component = Component::new( 251 &engine, 252 r#" 253 (component 254 (import (interface "foo:foo/def") (instance $i 255 (export $x "x" (type (sub resource))) 256 (export "[constructor]x" (func (result (own $x)))) 257 )) 258 (alias export $i "x" (type $x)) 259 (core func $dtor (canon resource.drop $x)) 260 (core func $ctor (canon lower (func $i "[constructor]x"))) 261 262 (core module $m 263 (import "" "ctor" (func $ctor (result i32))) 264 (import "" "dtor" (func $dtor (param i32))) 265 266 (func (export "x") (param i32) 267 (call $dtor (local.get 0)) 268 (call $dtor (call $ctor)) 269 ) 270 ) 271 (core instance $i (instantiate $m 272 (with "" (instance 273 (export "ctor" (func $ctor)) 274 (export "dtor" (func $dtor)) 275 )) 276 )) 277 (func $y (param "x" (own $x)) 278 (canon lift (core func $i "x"))) 279 280 (instance (export (interface "foo:foo/user")) 281 (export "y" (func $y)) 282 ) 283 ) 284 "#, 285 )?; 286 287 #[derive(Default)] 288 struct MyImports { 289 ctor_hit: bool, 290 drops: usize, 291 } 292 293 use foo::foo::def::X; 294 295 impl foo::foo::def::HostX for MyImports { 296 fn new(&mut self) -> Resource<X> { 297 self.ctor_hit = true; 298 Resource::new_own(80) 299 } 300 301 fn drop(&mut self, val: Resource<X>) -> Result<()> { 302 match self.drops { 303 0 => assert_eq!(val.rep(), 40), 304 1 => assert_eq!(val.rep(), 80), 305 _ => unreachable!(), 306 } 307 self.drops += 1; 308 Ok(()) 309 } 310 } 311 312 impl foo::foo::def::Host for MyImports {} 313 314 let mut linker = Linker::new(&engine); 315 Resources::add_to_linker::<_, HasSelf<_>>(&mut linker, |f| f)?; 316 let mut store = Store::new(&engine, MyImports::default()); 317 let one_import = Resources::instantiate(&mut store, &component, &linker)?; 318 one_import 319 .foo_foo_user() 320 .call_y(&mut store, Resource::new_own(40))?; 321 assert!(store.data().ctor_hit); 322 assert_eq!(store.data().drops, 2); 323 Ok(()) 324 } 325 } 326 327 mod async_config { 328 use super::*; 329 330 wasmtime::component::bindgen!({ 331 inline: " 332 package foo:foo; 333 334 world t1 { 335 import foo: interface { 336 foo: func(); 337 } 338 import x: func(); 339 import y: func(); 340 export z: func(); 341 } 342 ", 343 async: true, 344 }); 345 346 #[expect(dead_code, reason = "just here for bindings")] 347 struct T; 348 349 impl T1Imports for T { 350 async fn x(&mut self) {} 351 352 async fn y(&mut self) {} 353 } 354 355 async fn _test_t1(t1: &T1, store: &mut Store<()>) { 356 let _ = t1.call_z(&mut *store).await; 357 } 358 359 wasmtime::component::bindgen!({ 360 inline: " 361 package foo:foo; 362 363 world t2 { 364 import x: func(); 365 import y: func(); 366 export z: func(); 367 } 368 ", 369 async: { 370 except_imports: ["x"], 371 }, 372 }); 373 374 impl T2Imports for T { 375 fn x(&mut self) {} 376 377 async fn y(&mut self) {} 378 } 379 380 async fn _test_t2(t2: &T2, store: &mut Store<()>) { 381 let _ = t2.call_z(&mut *store).await; 382 } 383 384 wasmtime::component::bindgen!({ 385 inline: " 386 package foo:foo; 387 388 world t3 { 389 import x: func(); 390 import y: func(); 391 export z: func(); 392 } 393 ", 394 async: { 395 only_imports: ["x"], 396 }, 397 }); 398 399 impl T3Imports for T { 400 async fn x(&mut self) {} 401 402 fn y(&mut self) {} 403 } 404 405 async fn _test_t3(t3: &T3, store: &mut Store<()>) { 406 let _ = t3.call_z(&mut *store).await; 407 } 408 } 409 410 mod exported_resources { 411 use super::*; 412 use std::mem; 413 use wasmtime::component::{HasSelf, Resource}; 414 415 wasmtime::component::bindgen!({ 416 inline: " 417 package foo:foo; 418 419 interface a { 420 resource x { 421 constructor(); 422 } 423 } 424 425 world resources { 426 export b: interface { 427 use a.{x as y}; 428 429 resource x { 430 constructor(y: y); 431 foo: func() -> u32; 432 } 433 } 434 435 resource x; 436 437 export f: func(x1: x, x2: x) -> x; 438 } 439 ", 440 }); 441 442 #[derive(Default)] 443 struct MyImports { 444 hostcalls: Vec<Hostcall>, 445 next_a_x: u32, 446 } 447 448 #[derive(PartialEq, Debug)] 449 enum Hostcall { 450 DropRootX(u32), 451 DropAX(u32), 452 NewA, 453 } 454 455 use foo::foo::a; 456 457 impl ResourcesImports for MyImports {} 458 459 impl HostX for MyImports { 460 fn drop(&mut self, val: Resource<X>) -> Result<()> { 461 self.hostcalls.push(Hostcall::DropRootX(val.rep())); 462 Ok(()) 463 } 464 } 465 466 impl a::HostX for MyImports { 467 fn new(&mut self) -> Resource<a::X> { 468 let rep = self.next_a_x; 469 self.next_a_x += 1; 470 self.hostcalls.push(Hostcall::NewA); 471 Resource::new_own(rep) 472 } 473 474 fn drop(&mut self, val: Resource<a::X>) -> Result<()> { 475 self.hostcalls.push(Hostcall::DropAX(val.rep())); 476 Ok(()) 477 } 478 } 479 480 impl foo::foo::a::Host for MyImports {} 481 482 #[test] 483 fn run() -> Result<()> { 484 let engine = engine(); 485 486 let component = Component::new( 487 &engine, 488 r#" 489 (component 490 ;; setup the `foo:foo/a` import 491 (import (interface "foo:foo/a") (instance $a 492 (export $x "x" (type (sub resource))) 493 (export "[constructor]x" (func (result (own $x)))) 494 )) 495 (alias export $a "x" (type $a-x)) 496 (core func $a-x-drop (canon resource.drop $a-x)) 497 (core func $a-x-ctor (canon lower (func $a "[constructor]x"))) 498 499 ;; setup the root import of the `x` resource 500 (import "x" (type $x (sub resource))) 501 (core func $root-x-dtor (canon resource.drop $x)) 502 503 ;; setup and declare the `x` resource for the `b` export. 504 (core module $indirect-dtor 505 (func (export "b-x-dtor") (param i32) 506 local.get 0 507 i32.const 0 508 call_indirect (param i32) 509 ) 510 (table (export "$imports") 1 1 funcref) 511 ) 512 (core instance $indirect-dtor (instantiate $indirect-dtor)) 513 (type $b-x (resource (rep i32) (dtor (func $indirect-dtor "b-x-dtor")))) 514 (core func $b-x-drop (canon resource.drop $b-x)) 515 (core func $b-x-rep (canon resource.rep $b-x)) 516 (core func $b-x-new (canon resource.new $b-x)) 517 518 ;; main module implementation 519 (core module $main 520 (import "foo:foo/a" "[constructor]x" (func $a-x-ctor (result i32))) 521 (import "foo:foo/a" "[resource-drop]x" (func $a-x-dtor (param i32))) 522 (import "$root" "[resource-drop]x" (func $x-dtor (param i32))) 523 (import "[export]b" "[resource-drop]x" (func $b-x-dtor (param i32))) 524 (import "[export]b" "[resource-new]x" (func $b-x-new (param i32) (result i32))) 525 (import "[export]b" "[resource-rep]x" (func $b-x-rep (param i32) (result i32))) 526 (func (export "b#[constructor]x") (param i32) (result i32) 527 (call $a-x-dtor (local.get 0)) 528 (call $b-x-new (call $a-x-ctor)) 529 ) 530 (func (export "b#[method]x.foo") (param i32) (result i32) 531 local.get 0) 532 (func (export "b#[dtor]x") (param i32) 533 (call $a-x-dtor (local.get 0)) 534 ) 535 (func (export "f") (param i32 i32) (result i32) 536 (call $x-dtor (local.get 0)) 537 local.get 1 538 ) 539 ) 540 (core instance $main (instantiate $main 541 (with "foo:foo/a" (instance 542 (export "[resource-drop]x" (func $a-x-drop)) 543 (export "[constructor]x" (func $a-x-ctor)) 544 )) 545 (with "$root" (instance 546 (export "[resource-drop]x" (func $root-x-dtor)) 547 )) 548 (with "[export]b" (instance 549 (export "[resource-drop]x" (func $b-x-drop)) 550 (export "[resource-rep]x" (func $b-x-rep)) 551 (export "[resource-new]x" (func $b-x-new)) 552 )) 553 )) 554 555 ;; fill in `$indirect-dtor`'s table with the actual destructor definition 556 ;; now that it's available. 557 (core module $fixup 558 (import "" "b-x-dtor" (func $b-x-dtor (param i32))) 559 (import "" "$imports" (table 1 1 funcref)) 560 (elem (i32.const 0) func $b-x-dtor) 561 ) 562 (core instance (instantiate $fixup 563 (with "" (instance 564 (export "$imports" (table 0 "$imports")) 565 (export "b-x-dtor" (func $main "b#[dtor]x")) 566 )) 567 )) 568 569 ;; Create the `b` export through a subcomponent instantiation. 570 (func $b-x-ctor (param "y" (own $a-x)) (result (own $b-x)) 571 (canon lift (core func $main "b#[constructor]x"))) 572 (func $b-x-foo (param "self" (borrow $b-x)) (result u32) 573 (canon lift (core func $main "b#[method]x.foo"))) 574 (component $b 575 (import "a-x" (type $y (sub resource))) 576 (import "b-x" (type $x' (sub resource))) 577 (import "ctor" (func $ctor (param "y" (own $y)) (result (own $x')))) 578 (import "foo" (func $foo (param "self" (borrow $x')) (result u32))) 579 (export $x "x" (type $x')) 580 (export "[constructor]x" 581 (func $ctor) 582 (func (param "y" (own $y)) (result (own $x)))) 583 (export "[method]x.foo" 584 (func $foo) 585 (func (param "self" (borrow $x)) (result u32))) 586 ) 587 (instance (export "b") (instantiate $b 588 (with "ctor" (func $b-x-ctor)) 589 (with "foo" (func $b-x-foo)) 590 (with "a-x" (type 0 "x")) 591 (with "b-x" (type $b-x)) 592 )) 593 594 ;; Create the `f` export which is a bare function 595 (func (export "f") (param "x1" (own $x)) (param "x2" (own $x)) (result (own $x)) 596 (canon lift (core func $main "f"))) 597 ) 598 "#, 599 )?; 600 601 let mut linker = Linker::new(&engine); 602 Resources::add_to_linker::<_, HasSelf<_>>(&mut linker, |f| f)?; 603 let mut store = Store::new(&engine, MyImports::default()); 604 let i = Resources::instantiate(&mut store, &component, &linker)?; 605 606 // call the root export `f` twice 607 let ret = i.call_f(&mut store, Resource::new_own(1), Resource::new_own(2))?; 608 assert_eq!(ret.rep(), 2); 609 assert_eq!( 610 mem::take(&mut store.data_mut().hostcalls), 611 [Hostcall::DropRootX(1)] 612 ); 613 let ret = i.call_f(&mut store, Resource::new_own(3), Resource::new_own(4))?; 614 assert_eq!(ret.rep(), 4); 615 assert_eq!( 616 mem::take(&mut store.data_mut().hostcalls), 617 [Hostcall::DropRootX(3)] 618 ); 619 620 // interact with the `b` export 621 let b = i.b(); 622 let b_x = b.x().call_constructor(&mut store, Resource::new_own(5))?; 623 assert_eq!( 624 mem::take(&mut store.data_mut().hostcalls), 625 [Hostcall::DropAX(5), Hostcall::NewA] 626 ); 627 b.x().call_foo(&mut store, b_x)?; 628 assert_eq!(mem::take(&mut store.data_mut().hostcalls), []); 629 b_x.resource_drop(&mut store)?; 630 assert_eq!( 631 mem::take(&mut store.data_mut().hostcalls), 632 [Hostcall::DropAX(0)], 633 ); 634 Ok(()) 635 } 636 } 637 638 mod unstable_import { 639 use super::*; 640 use wasmtime::component::HasSelf; 641 642 wasmtime::component::bindgen!({ 643 inline: " 644 package foo:foo; 645 646 @unstable(feature = experimental-interface) 647 interface my-interface { 648 @unstable(feature = experimental-function) 649 my-function: func(); 650 } 651 652 world my-world { 653 @unstable(feature = experimental-import) 654 import my-interface; 655 656 export bar: func(); 657 } 658 ", 659 }); 660 661 #[test] 662 fn run() -> Result<()> { 663 // In the example above, all features are required for `my-function` to be imported: 664 assert_success( 665 LinkOptions::default() 666 .experimental_interface(true) 667 .experimental_import(true) 668 .experimental_function(true), 669 ); 670 671 // And every other incomplete combination should fail: 672 assert_failure(&LinkOptions::default()); 673 assert_failure(LinkOptions::default().experimental_function(true)); 674 assert_failure(LinkOptions::default().experimental_interface(true)); 675 assert_failure( 676 LinkOptions::default() 677 .experimental_interface(true) 678 .experimental_function(true), 679 ); 680 assert_failure( 681 LinkOptions::default() 682 .experimental_interface(true) 683 .experimental_import(true), 684 ); 685 assert_failure(LinkOptions::default().experimental_import(true)); 686 assert_failure( 687 LinkOptions::default() 688 .experimental_import(true) 689 .experimental_function(true), 690 ); 691 692 Ok(()) 693 } 694 695 fn assert_success(link_options: &LinkOptions) { 696 run_with_options(link_options).unwrap(); 697 } 698 fn assert_failure(link_options: &LinkOptions) { 699 let err = run_with_options(link_options).unwrap_err().to_string(); 700 assert_eq!( 701 err, 702 "component imports instance `foo:foo/my-interface`, but a matching implementation was not found in the linker" 703 ); 704 } 705 706 fn run_with_options(link_options: &LinkOptions) -> Result<()> { 707 let engine = engine(); 708 709 let component = Component::new( 710 &engine, 711 r#" 712 (component 713 (import "foo:foo/my-interface" (instance $i 714 (export "my-function" (func)) 715 )) 716 (core module $m 717 (import "" "" (func)) 718 (export "" (func 0)) 719 ) 720 (core func $f (canon lower (func $i "my-function"))) 721 (core instance $r (instantiate $m 722 (with "" (instance (export "" (func $f)))) 723 )) 724 725 (func $f (export "bar") (canon lift (core func $r ""))) 726 ) 727 "#, 728 )?; 729 730 #[derive(Default)] 731 struct MyHost; 732 733 impl foo::foo::my_interface::Host for MyHost { 734 fn my_function(&mut self) {} 735 } 736 737 let mut linker = Linker::new(&engine); 738 MyWorld::add_to_linker::<_, HasSelf<_>>(&mut linker, link_options, |h| h)?; 739 let mut store = Store::new(&engine, MyHost::default()); 740 let one_import = MyWorld::instantiate(&mut store, &component, &linker)?; 741 one_import.call_bar(&mut store)?; 742 Ok(()) 743 } 744 } 745