1 use std::fmt::Write; 2 use std::iter; 3 use wasmtime::component::{ 4 Component, ComponentNamedList, Instance, Lift, Linker, Lower, ResourceAny, TypedFunc, 5 }; 6 use wasmtime::{Config, Result, Store}; 7 use wasmtime_component_util::REALLOC_AND_FREE; 8 use wasmtime_test_util::component::{async_engine, config, engine}; 9 10 mod aot; 11 mod r#async; 12 mod async_dynamic; 13 mod bindgen; 14 mod call_hook; 15 mod dynamic; 16 mod func; 17 mod import; 18 mod instance; 19 mod linker; 20 mod macros; 21 mod missing_async; 22 mod nested; 23 mod post_return; 24 mod resources; 25 mod strings; 26 27 #[derive(Copy, Clone)] 28 enum ApiStyle { 29 Sync, 30 Async, 31 AsyncNotConcurrent, 32 Concurrent, 33 } 34 35 impl ApiStyle { 36 fn config(self) -> Config { 37 let mut config = config(); 38 match self { 39 ApiStyle::AsyncNotConcurrent => { 40 config.concurrency_support(false); 41 } 42 ApiStyle::Sync | ApiStyle::Async | ApiStyle::Concurrent => { 43 config.wasm_component_model_threading(true); 44 } 45 } 46 config 47 } 48 49 async fn instantiate<T: Send>( 50 self, 51 store: &mut Store<T>, 52 linker: &Linker<T>, 53 component: &Component, 54 ) -> Result<Instance> { 55 match self { 56 ApiStyle::Sync => linker.instantiate(store, &component), 57 ApiStyle::Async | ApiStyle::AsyncNotConcurrent | ApiStyle::Concurrent => { 58 linker.instantiate_async(store, &component).await 59 } 60 } 61 } 62 63 async fn call< 64 P: ComponentNamedList + Lower + 'static, 65 R: ComponentNamedList + Lift + 'static, 66 T: Send, 67 >( 68 self, 69 store: &mut Store<T>, 70 func: TypedFunc<P, R>, 71 params: P, 72 ) -> Result<R> { 73 match self { 74 ApiStyle::Sync => func.call(&mut *store, params), 75 ApiStyle::Async | ApiStyle::AsyncNotConcurrent => { 76 func.call_async(&mut *store, params).await 77 } 78 ApiStyle::Concurrent => Ok(store 79 .run_concurrent(async |access| func.call_concurrent(access, params).await) 80 .await??), 81 } 82 } 83 84 async fn resource_drop<T: Send>( 85 self, 86 store: &mut Store<T>, 87 resource: ResourceAny, 88 ) -> Result<()> { 89 match self { 90 ApiStyle::Sync => resource.resource_drop(store), 91 ApiStyle::Async | ApiStyle::AsyncNotConcurrent | ApiStyle::Concurrent => { 92 resource.resource_drop_async(store).await 93 } 94 } 95 } 96 } 97 98 #[test] 99 #[cfg_attr(miri, ignore)] 100 fn components_importing_modules() -> Result<()> { 101 let engine = engine(); 102 103 // FIXME: these components should actually get instantiated in `*.wast` 104 // tests once supplying imports has actually been implemented. 105 106 Component::new( 107 &engine, 108 r#" 109 (component 110 (import "a" (core module)) 111 ) 112 "#, 113 )?; 114 115 Component::new( 116 &engine, 117 r#" 118 (component 119 (import "a" (core module $m1 120 (import "" "" (func)) 121 (import "" "x" (global i32)) 122 123 (export "a" (table 1 funcref)) 124 (export "b" (memory 1)) 125 (export "c" (func (result f32))) 126 (export "d" (global i64)) 127 )) 128 129 (core module $m2 130 (func (export "")) 131 (global (export "x") i32 i32.const 0) 132 ) 133 (core instance $i2 (instantiate (module $m2))) 134 (core instance $i1 (instantiate (module $m1) (with "" (instance $i2)))) 135 136 (core module $m3 137 (import "mod" "1" (memory 1)) 138 (import "mod" "2" (table 1 funcref)) 139 (import "mod" "3" (global i64)) 140 (import "mod" "4" (func (result f32))) 141 ) 142 143 (core instance $i3 (instantiate (module $m3) 144 (with "mod" (instance 145 (export "1" (memory $i1 "b")) 146 (export "2" (table $i1 "a")) 147 (export "3" (global $i1 "d")) 148 (export "4" (func $i1 "c")) 149 )) 150 )) 151 ) 152 "#, 153 )?; 154 155 Ok(()) 156 } 157 158 #[derive(Copy, Clone, PartialEq, Eq)] 159 enum Type { 160 S8, 161 U8, 162 S16, 163 U16, 164 I32, 165 I64, 166 F32, 167 F64, 168 } 169 170 impl Type { 171 fn store(&self) -> &'static str { 172 match self { 173 Self::S8 | Self::U8 => "store8", 174 Self::S16 | Self::U16 => "store16", 175 Self::I32 | Self::F32 | Self::I64 | Self::F64 => "store", 176 } 177 } 178 179 fn primitive(&self) -> &'static str { 180 match self { 181 Self::S8 | Self::U8 | Self::S16 | Self::U16 | Self::I32 => "i32", 182 Self::I64 => "i64", 183 Self::F32 => "f32", 184 Self::F64 => "f64", 185 } 186 } 187 } 188 189 #[derive(Copy, Clone, PartialEq, Eq)] 190 struct Param(Type, Option<usize>); 191 192 fn make_echo_component(type_definition: &str, type_size: u32) -> String { 193 let mut offset = 0; 194 make_echo_component_with_params( 195 type_definition, 196 &iter::repeat(Type::I32) 197 .map(|ty| { 198 let param = Param(ty, Some(offset)); 199 offset += 4; 200 param 201 }) 202 .take(usize::try_from(type_size).unwrap() / 4) 203 .collect::<Vec<_>>(), 204 ) 205 } 206 207 fn make_echo_component_with_params(type_definition: &str, params: &[Param]) -> String { 208 let func = if params.len() == 0 { 209 format!("(func (export \"echo\"))") 210 } else if params.len() == 1 || params.len() > 16 { 211 let primitive = if params.len() == 1 { 212 params[0].0.primitive() 213 } else { 214 "i32" 215 }; 216 217 format!( 218 r#" 219 (func (export "echo") (param {primitive}) (result {primitive}) 220 local.get 0 221 )"#, 222 ) 223 } else { 224 let mut param_string = String::new(); 225 let mut store = String::new(); 226 let mut size = 8; 227 228 for (index, Param(ty, offset)) in params.iter().enumerate() { 229 let primitive = ty.primitive(); 230 231 write!(&mut param_string, " {primitive}").unwrap(); 232 if let Some(offset) = offset { 233 write!( 234 &mut store, 235 "({primitive}.{} offset={offset} (local.get $base) (local.get {index}))", 236 ty.store(), 237 ) 238 .unwrap(); 239 240 size = size.max(offset + 8); 241 } 242 } 243 244 format!( 245 r#" 246 (func (export "echo") (param{param_string}) (result i32) 247 (local $base i32) 248 (local.set $base 249 (call $realloc 250 (i32.const 0) 251 (i32.const 0) 252 (i32.const 4) 253 (i32.const {size}))) 254 {store} 255 local.get $base 256 )"# 257 ) 258 }; 259 260 let type_section = if type_definition.contains("(type ") { 261 type_definition.to_string() 262 } else { 263 format!("(type $Foo' {type_definition})") 264 }; 265 266 format!( 267 r#" 268 (component 269 (core module $m 270 {func} 271 272 (memory (export "memory") 1) 273 {REALLOC_AND_FREE} 274 ) 275 276 (core instance $i (instantiate $m)) 277 278 {type_section} 279 (export $Foo "foo" (type $Foo')) 280 281 (func (export "echo") (param "a" $Foo) (result $Foo) 282 (canon lift 283 (core func $i "echo") 284 (memory $i "memory") 285 (realloc (func $i "realloc")) 286 ) 287 ) 288 )"# 289 ) 290 } 291