1 use anyhow::Result;
2 use component_test_util::{async_engine, engine, TypedFuncExt};
3 use std::fmt::Write;
4 use std::iter;
5 use wasmtime::component::Component;
6 use wasmtime_component_util::REALLOC_AND_FREE;
7 
8 mod aot;
9 mod r#async;
10 mod bindgen;
11 mod call_hook;
12 mod dynamic;
13 mod func;
14 mod import;
15 mod instance;
16 mod linker;
17 mod macros;
18 mod nested;
19 mod post_return;
20 mod resources;
21 mod strings;
22 
23 #[test]
24 #[cfg_attr(miri, ignore)]
25 fn components_importing_modules() -> Result<()> {
26     let engine = engine();
27 
28     // FIXME: these components should actually get instantiated in `*.wast`
29     // tests once supplying imports has actually been implemented.
30 
31     Component::new(
32         &engine,
33         r#"
34         (component
35             (import "a" (core module))
36         )
37         "#,
38     )?;
39 
40     Component::new(
41         &engine,
42         r#"
43         (component
44             (import "a" (core module $m1
45                 (import "" "" (func))
46                 (import "" "x" (global i32))
47 
48                 (export "a" (table 1 funcref))
49                 (export "b" (memory 1))
50                 (export "c" (func (result f32)))
51                 (export "d" (global i64))
52             ))
53 
54             (core module $m2
55                 (func (export ""))
56                 (global (export "x") i32 i32.const 0)
57             )
58             (core instance $i2 (instantiate (module $m2)))
59             (core instance $i1 (instantiate (module $m1) (with "" (instance $i2))))
60 
61             (core module $m3
62                 (import "mod" "1" (memory 1))
63                 (import "mod" "2" (table 1 funcref))
64                 (import "mod" "3" (global i64))
65                 (import "mod" "4" (func (result f32)))
66             )
67 
68             (core instance $i3 (instantiate (module $m3)
69                 (with "mod" (instance
70                     (export "1" (memory $i1 "b"))
71                     (export "2" (table $i1 "a"))
72                     (export "3" (global $i1 "d"))
73                     (export "4" (func $i1 "c"))
74                 ))
75             ))
76         )
77         "#,
78     )?;
79 
80     Ok(())
81 }
82 
83 #[derive(Copy, Clone, PartialEq, Eq)]
84 enum Type {
85     S8,
86     U8,
87     S16,
88     U16,
89     I32,
90     I64,
91     F32,
92     F64,
93 }
94 
95 impl Type {
96     fn store(&self) -> &'static str {
97         match self {
98             Self::S8 | Self::U8 => "store8",
99             Self::S16 | Self::U16 => "store16",
100             Self::I32 | Self::F32 | Self::I64 | Self::F64 => "store",
101         }
102     }
103 
104     fn primitive(&self) -> &'static str {
105         match self {
106             Self::S8 | Self::U8 | Self::S16 | Self::U16 | Self::I32 => "i32",
107             Self::I64 => "i64",
108             Self::F32 => "f32",
109             Self::F64 => "f64",
110         }
111     }
112 }
113 
114 #[derive(Copy, Clone, PartialEq, Eq)]
115 struct Param(Type, Option<usize>);
116 
117 fn make_echo_component(type_definition: &str, type_size: u32) -> String {
118     let mut offset = 0;
119     make_echo_component_with_params(
120         type_definition,
121         &iter::repeat(Type::I32)
122             .map(|ty| {
123                 let param = Param(ty, Some(offset));
124                 offset += 4;
125                 param
126             })
127             .take(usize::try_from(type_size).unwrap() / 4)
128             .collect::<Vec<_>>(),
129     )
130 }
131 
132 fn make_echo_component_with_params(type_definition: &str, params: &[Param]) -> String {
133     let func = if params.len() == 0 {
134         format!("(func (export \"echo\"))")
135     } else if params.len() == 1 || params.len() > 16 {
136         let primitive = if params.len() == 1 {
137             params[0].0.primitive()
138         } else {
139             "i32"
140         };
141 
142         format!(
143             r#"
144             (func (export "echo") (param {primitive}) (result {primitive})
145                  local.get 0
146             )"#,
147         )
148     } else {
149         let mut param_string = String::new();
150         let mut store = String::new();
151         let mut size = 8;
152 
153         for (index, Param(ty, offset)) in params.iter().enumerate() {
154             let primitive = ty.primitive();
155 
156             write!(&mut param_string, " {primitive}").unwrap();
157             if let Some(offset) = offset {
158                 write!(
159                     &mut store,
160                     "({primitive}.{} offset={offset} (local.get $base) (local.get {index}))",
161                     ty.store(),
162                 )
163                 .unwrap();
164 
165                 size = size.max(offset + 8);
166             }
167         }
168 
169         format!(
170             r#"
171             (func (export "echo") (param{param_string}) (result i32)
172                 (local $base i32)
173                 (local.set $base
174                     (call $realloc
175                         (i32.const 0)
176                         (i32.const 0)
177                         (i32.const 4)
178                         (i32.const {size})))
179                 {store}
180                 local.get $base
181             )"#
182         )
183     };
184 
185     let type_section = if type_definition.contains("(type ") {
186         type_definition.to_string()
187     } else {
188         format!("(type $Foo' {type_definition})")
189     };
190 
191     format!(
192         r#"
193         (component
194             (core module $m
195                 {func}
196 
197                 (memory (export "memory") 1)
198                 {REALLOC_AND_FREE}
199             )
200 
201             (core instance $i (instantiate $m))
202 
203             {type_section}
204             (export $Foo "foo" (type $Foo'))
205 
206             (func (export "echo") (param "a" $Foo) (result "b" $Foo)
207                 (canon lift
208                     (core func $i "echo")
209                     (memory $i "memory")
210                     (realloc (func $i "realloc"))
211                 )
212             )
213         )"#
214     )
215 }
216