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