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 .0), 82 } 83 } 84 85 async fn resource_drop<T: Send>( 86 self, 87 store: &mut Store<T>, 88 resource: ResourceAny, 89 ) -> Result<()> { 90 match self { 91 ApiStyle::Sync => resource.resource_drop(store), 92 ApiStyle::Async | ApiStyle::AsyncNotConcurrent | ApiStyle::Concurrent => { 93 resource.resource_drop_async(store).await 94 } 95 } 96 } 97 } 98 99 #[test] 100 #[cfg_attr(miri, ignore)] 101 fn components_importing_modules() -> Result<()> { 102 let engine = engine(); 103 104 // FIXME: these components should actually get instantiated in `*.wast` 105 // tests once supplying imports has actually been implemented. 106 107 Component::new( 108 &engine, 109 r#" 110 (component 111 (import "a" (core module)) 112 ) 113 "#, 114 )?; 115 116 Component::new( 117 &engine, 118 r#" 119 (component 120 (import "a" (core module $m1 121 (import "" "" (func)) 122 (import "" "x" (global i32)) 123 124 (export "a" (table 1 funcref)) 125 (export "b" (memory 1)) 126 (export "c" (func (result f32))) 127 (export "d" (global i64)) 128 )) 129 130 (core module $m2 131 (func (export "")) 132 (global (export "x") i32 i32.const 0) 133 ) 134 (core instance $i2 (instantiate (module $m2))) 135 (core instance $i1 (instantiate (module $m1) (with "" (instance $i2)))) 136 137 (core module $m3 138 (import "mod" "1" (memory 1)) 139 (import "mod" "2" (table 1 funcref)) 140 (import "mod" "3" (global i64)) 141 (import "mod" "4" (func (result f32))) 142 ) 143 144 (core instance $i3 (instantiate (module $m3) 145 (with "mod" (instance 146 (export "1" (memory $i1 "b")) 147 (export "2" (table $i1 "a")) 148 (export "3" (global $i1 "d")) 149 (export "4" (func $i1 "c")) 150 )) 151 )) 152 ) 153 "#, 154 )?; 155 156 Ok(()) 157 } 158 159 #[derive(Copy, Clone, PartialEq, Eq)] 160 enum Type { 161 S8, 162 U8, 163 S16, 164 U16, 165 I32, 166 I64, 167 F32, 168 F64, 169 } 170 171 impl Type { 172 fn store(&self) -> &'static str { 173 match self { 174 Self::S8 | Self::U8 => "store8", 175 Self::S16 | Self::U16 => "store16", 176 Self::I32 | Self::F32 | Self::I64 | Self::F64 => "store", 177 } 178 } 179 180 fn primitive(&self) -> &'static str { 181 match self { 182 Self::S8 | Self::U8 | Self::S16 | Self::U16 | Self::I32 => "i32", 183 Self::I64 => "i64", 184 Self::F32 => "f32", 185 Self::F64 => "f64", 186 } 187 } 188 } 189 190 #[derive(Copy, Clone, PartialEq, Eq)] 191 struct Param(Type, Option<usize>); 192 193 fn make_echo_component(type_definition: &str, type_size: u32) -> String { 194 let mut offset = 0; 195 make_echo_component_with_params( 196 type_definition, 197 &iter::repeat(Type::I32) 198 .map(|ty| { 199 let param = Param(ty, Some(offset)); 200 offset += 4; 201 param 202 }) 203 .take(usize::try_from(type_size).unwrap() / 4) 204 .collect::<Vec<_>>(), 205 ) 206 } 207 208 fn make_echo_component_with_params(type_definition: &str, params: &[Param]) -> String { 209 let func = if params.len() == 0 { 210 format!("(func (export \"echo\"))") 211 } else if params.len() == 1 || params.len() > 16 { 212 let primitive = if params.len() == 1 { 213 params[0].0.primitive() 214 } else { 215 "i32" 216 }; 217 218 format!( 219 r#" 220 (func (export "echo") (param {primitive}) (result {primitive}) 221 local.get 0 222 )"#, 223 ) 224 } else { 225 let mut param_string = String::new(); 226 let mut store = String::new(); 227 let mut size = 8; 228 229 for (index, Param(ty, offset)) in params.iter().enumerate() { 230 let primitive = ty.primitive(); 231 232 write!(&mut param_string, " {primitive}").unwrap(); 233 if let Some(offset) = offset { 234 write!( 235 &mut store, 236 "({primitive}.{} offset={offset} (local.get $base) (local.get {index}))", 237 ty.store(), 238 ) 239 .unwrap(); 240 241 size = size.max(offset + 8); 242 } 243 } 244 245 format!( 246 r#" 247 (func (export "echo") (param{param_string}) (result i32) 248 (local $base i32) 249 (local.set $base 250 (call $realloc 251 (i32.const 0) 252 (i32.const 0) 253 (i32.const 4) 254 (i32.const {size}))) 255 {store} 256 local.get $base 257 )"# 258 ) 259 }; 260 261 let type_section = if type_definition.contains("(type ") { 262 type_definition.to_string() 263 } else { 264 format!("(type $Foo' {type_definition})") 265 }; 266 267 format!( 268 r#" 269 (component 270 (core module $m 271 {func} 272 273 (memory (export "memory") 1) 274 {REALLOC_AND_FREE} 275 ) 276 277 (core instance $i (instantiate $m)) 278 279 {type_section} 280 (export $Foo "foo" (type $Foo')) 281 282 (func (export "echo") (param "a" $Foo) (result $Foo) 283 (canon lift 284 (core func $i "echo") 285 (memory $i "memory") 286 (realloc (func $i "realloc")) 287 ) 288 ) 289 )"# 290 ) 291 } 292