1 #![cfg(not(miri))] 2 3 use super::{REALLOC_AND_FREE, TypedFuncExt}; 4 use anyhow::Result; 5 use std::sync::Arc; 6 use wasmtime::component::*; 7 use wasmtime::{Config, Engine, Store, StoreContextMut, Trap}; 8 9 const CANON_32BIT_NAN: u32 = 0b01111111110000000000000000000000; 10 const CANON_64BIT_NAN: u64 = 0b0111111111111000000000000000000000000000000000000000000000000000; 11 12 #[test] 13 fn thunks() -> Result<()> { 14 let component = r#" 15 (component 16 (core module $m 17 (func (export "thunk")) 18 (func (export "thunk-trap") unreachable) 19 ) 20 (core instance $i (instantiate $m)) 21 (func (export "thunk") 22 (canon lift (core func $i "thunk")) 23 ) 24 (func (export "thunk-trap") 25 (canon lift (core func $i "thunk-trap")) 26 ) 27 ) 28 "#; 29 30 let engine = super::engine(); 31 let component = Component::new(&engine, component)?; 32 let mut store = Store::new(&engine, ()); 33 let instance = Linker::new(&engine).instantiate(&mut store, &component)?; 34 instance 35 .get_typed_func::<(), ()>(&mut store, "thunk")? 36 .call_and_post_return(&mut store, ())?; 37 let err = instance 38 .get_typed_func::<(), ()>(&mut store, "thunk-trap")? 39 .call(&mut store, ()) 40 .unwrap_err(); 41 assert_eq!(err.downcast::<Trap>()?, Trap::UnreachableCodeReached); 42 43 Ok(()) 44 } 45 46 #[test] 47 fn typecheck() -> Result<()> { 48 let component = r#" 49 (component 50 (core module $m 51 (func (export "thunk")) 52 (func (export "take-string") (param i32 i32)) 53 (func (export "two-args") (param i32 i32 i32)) 54 (func (export "ret-one") (result i32) unreachable) 55 56 (memory (export "memory") 1) 57 (func (export "realloc") (param i32 i32 i32 i32) (result i32) 58 unreachable) 59 ) 60 (core instance $i (instantiate (module $m))) 61 (func (export "thunk") 62 (canon lift (core func $i "thunk")) 63 ) 64 (func (export "take-string") (param "a" string) 65 (canon lift (core func $i "take-string") (memory $i "memory") (realloc (func $i "realloc"))) 66 ) 67 (func (export "take-two-args") (param "a" s32) (param "b" (list u8)) 68 (canon lift (core func $i "two-args") (memory $i "memory") (realloc (func $i "realloc"))) 69 ) 70 (func (export "ret-tuple") (result (tuple u8 s8)) 71 (canon lift (core func $i "ret-one") (memory $i "memory") (realloc (func $i "realloc"))) 72 ) 73 (func (export "ret-tuple1") (result (tuple u32)) 74 (canon lift (core func $i "ret-one") (memory $i "memory") (realloc (func $i "realloc"))) 75 ) 76 (func (export "ret-string") (result string) 77 (canon lift (core func $i "ret-one") (memory $i "memory") (realloc (func $i "realloc"))) 78 ) 79 (func (export "ret-list-u8") (result (list u8)) 80 (canon lift (core func $i "ret-one") (memory $i "memory") (realloc (func $i "realloc"))) 81 ) 82 ) 83 "#; 84 85 let engine = Engine::default(); 86 let component = Component::new(&engine, component)?; 87 let mut store = Store::new(&engine, ()); 88 let instance = Linker::new(&engine).instantiate(&mut store, &component)?; 89 let thunk = instance.get_func(&mut store, "thunk").unwrap(); 90 let take_string = instance.get_func(&mut store, "take-string").unwrap(); 91 let take_two_args = instance.get_func(&mut store, "take-two-args").unwrap(); 92 let ret_tuple = instance.get_func(&mut store, "ret-tuple").unwrap(); 93 let ret_tuple1 = instance.get_func(&mut store, "ret-tuple1").unwrap(); 94 let ret_string = instance.get_func(&mut store, "ret-string").unwrap(); 95 let ret_list_u8 = instance.get_func(&mut store, "ret-list-u8").unwrap(); 96 assert!(thunk.typed::<(), (u32,)>(&store).is_err()); 97 assert!(thunk.typed::<(u32,), ()>(&store).is_err()); 98 assert!(thunk.typed::<(), ()>(&store).is_ok()); 99 assert!(take_string.typed::<(), ()>(&store).is_err()); 100 assert!(take_string.typed::<(String,), ()>(&store).is_ok()); 101 assert!(take_string.typed::<(&str,), ()>(&store).is_ok()); 102 assert!(take_string.typed::<(&[u8],), ()>(&store).is_err()); 103 assert!(take_two_args.typed::<(), ()>(&store).is_err()); 104 assert!(take_two_args.typed::<(i32, &[u8]), (u32,)>(&store).is_err()); 105 assert!(take_two_args.typed::<(u32, &[u8]), ()>(&store).is_err()); 106 assert!(take_two_args.typed::<(i32, &[u8]), ()>(&store).is_ok()); 107 assert!(ret_tuple.typed::<(), ()>(&store).is_err()); 108 assert!(ret_tuple.typed::<(), (u8,)>(&store).is_err()); 109 assert!(ret_tuple.typed::<(), ((u8, i8),)>(&store).is_ok()); 110 assert!(ret_tuple1.typed::<(), ((u32,),)>(&store).is_ok()); 111 assert!(ret_tuple1.typed::<(), (u32,)>(&store).is_err()); 112 assert!(ret_string.typed::<(), ()>(&store).is_err()); 113 assert!(ret_string.typed::<(), (WasmStr,)>(&store).is_ok()); 114 assert!(ret_list_u8.typed::<(), (WasmList<u16>,)>(&store).is_err()); 115 assert!(ret_list_u8.typed::<(), (WasmList<i8>,)>(&store).is_err()); 116 assert!(ret_list_u8.typed::<(), (WasmList<u8>,)>(&store).is_ok()); 117 118 Ok(()) 119 } 120 121 #[test] 122 fn integers() -> Result<()> { 123 let component = r#" 124 (component 125 (core module $m 126 (func (export "take-i32-100") (param i32) 127 local.get 0 128 i32.const 100 129 i32.eq 130 br_if 0 131 unreachable 132 ) 133 (func (export "take-i64-100") (param i64) 134 local.get 0 135 i64.const 100 136 i64.eq 137 br_if 0 138 unreachable 139 ) 140 (func (export "ret-i32-0") (result i32) i32.const 0) 141 (func (export "ret-i64-0") (result i64) i64.const 0) 142 (func (export "ret-i32-minus-1") (result i32) i32.const -1) 143 (func (export "ret-i64-minus-1") (result i64) i64.const -1) 144 (func (export "ret-i32-100000") (result i32) i32.const 100000) 145 ) 146 (core instance $i (instantiate (module $m))) 147 (func (export "take-u8") (param "a" u8) (canon lift (core func $i "take-i32-100"))) 148 (func (export "take-s8") (param "a" s8) (canon lift (core func $i "take-i32-100"))) 149 (func (export "take-u16") (param "a" u16) (canon lift (core func $i "take-i32-100"))) 150 (func (export "take-s16") (param "a" s16) (canon lift (core func $i "take-i32-100"))) 151 (func (export "take-u32") (param "a" u32) (canon lift (core func $i "take-i32-100"))) 152 (func (export "take-s32") (param "a" s32) (canon lift (core func $i "take-i32-100"))) 153 (func (export "take-u64") (param "a" u64) (canon lift (core func $i "take-i64-100"))) 154 (func (export "take-s64") (param "a" s64) (canon lift (core func $i "take-i64-100"))) 155 156 (func (export "ret-u8") (result u8) (canon lift (core func $i "ret-i32-0"))) 157 (func (export "ret-s8") (result s8) (canon lift (core func $i "ret-i32-0"))) 158 (func (export "ret-u16") (result u16) (canon lift (core func $i "ret-i32-0"))) 159 (func (export "ret-s16") (result s16) (canon lift (core func $i "ret-i32-0"))) 160 (func (export "ret-u32") (result u32) (canon lift (core func $i "ret-i32-0"))) 161 (func (export "ret-s32") (result s32) (canon lift (core func $i "ret-i32-0"))) 162 (func (export "ret-u64") (result u64) (canon lift (core func $i "ret-i64-0"))) 163 (func (export "ret-s64") (result s64) (canon lift (core func $i "ret-i64-0"))) 164 165 (func (export "retm1-u8") (result u8) (canon lift (core func $i "ret-i32-minus-1"))) 166 (func (export "retm1-s8") (result s8) (canon lift (core func $i "ret-i32-minus-1"))) 167 (func (export "retm1-u16") (result u16) (canon lift (core func $i "ret-i32-minus-1"))) 168 (func (export "retm1-s16") (result s16) (canon lift (core func $i "ret-i32-minus-1"))) 169 (func (export "retm1-u32") (result u32) (canon lift (core func $i "ret-i32-minus-1"))) 170 (func (export "retm1-s32") (result s32) (canon lift (core func $i "ret-i32-minus-1"))) 171 (func (export "retm1-u64") (result u64) (canon lift (core func $i "ret-i64-minus-1"))) 172 (func (export "retm1-s64") (result s64) (canon lift (core func $i "ret-i64-minus-1"))) 173 174 (func (export "retbig-u8") (result u8) (canon lift (core func $i "ret-i32-100000"))) 175 (func (export "retbig-s8") (result s8) (canon lift (core func $i "ret-i32-100000"))) 176 (func (export "retbig-u16") (result u16) (canon lift (core func $i "ret-i32-100000"))) 177 (func (export "retbig-s16") (result s16) (canon lift (core func $i "ret-i32-100000"))) 178 (func (export "retbig-u32") (result u32) (canon lift (core func $i "ret-i32-100000"))) 179 (func (export "retbig-s32") (result s32) (canon lift (core func $i "ret-i32-100000"))) 180 ) 181 "#; 182 183 let engine = super::engine(); 184 let component = Component::new(&engine, component)?; 185 let mut store = Store::new(&engine, ()); 186 let new_instance = |store: &mut Store<()>| Linker::new(&engine).instantiate(store, &component); 187 let instance = new_instance(&mut store)?; 188 189 // Passing in 100 is valid for all primitives 190 instance 191 .get_typed_func::<(u8,), ()>(&mut store, "take-u8")? 192 .call_and_post_return(&mut store, (100,))?; 193 instance 194 .get_typed_func::<(i8,), ()>(&mut store, "take-s8")? 195 .call_and_post_return(&mut store, (100,))?; 196 instance 197 .get_typed_func::<(u16,), ()>(&mut store, "take-u16")? 198 .call_and_post_return(&mut store, (100,))?; 199 instance 200 .get_typed_func::<(i16,), ()>(&mut store, "take-s16")? 201 .call_and_post_return(&mut store, (100,))?; 202 instance 203 .get_typed_func::<(u32,), ()>(&mut store, "take-u32")? 204 .call_and_post_return(&mut store, (100,))?; 205 instance 206 .get_typed_func::<(i32,), ()>(&mut store, "take-s32")? 207 .call_and_post_return(&mut store, (100,))?; 208 instance 209 .get_typed_func::<(u64,), ()>(&mut store, "take-u64")? 210 .call_and_post_return(&mut store, (100,))?; 211 instance 212 .get_typed_func::<(i64,), ()>(&mut store, "take-s64")? 213 .call_and_post_return(&mut store, (100,))?; 214 215 // This specific wasm instance traps if any value other than 100 is passed 216 new_instance(&mut store)? 217 .get_typed_func::<(u8,), ()>(&mut store, "take-u8")? 218 .call(&mut store, (101,)) 219 .unwrap_err() 220 .downcast::<Trap>()?; 221 new_instance(&mut store)? 222 .get_typed_func::<(i8,), ()>(&mut store, "take-s8")? 223 .call(&mut store, (101,)) 224 .unwrap_err() 225 .downcast::<Trap>()?; 226 new_instance(&mut store)? 227 .get_typed_func::<(u16,), ()>(&mut store, "take-u16")? 228 .call(&mut store, (101,)) 229 .unwrap_err() 230 .downcast::<Trap>()?; 231 new_instance(&mut store)? 232 .get_typed_func::<(i16,), ()>(&mut store, "take-s16")? 233 .call(&mut store, (101,)) 234 .unwrap_err() 235 .downcast::<Trap>()?; 236 new_instance(&mut store)? 237 .get_typed_func::<(u32,), ()>(&mut store, "take-u32")? 238 .call(&mut store, (101,)) 239 .unwrap_err() 240 .downcast::<Trap>()?; 241 new_instance(&mut store)? 242 .get_typed_func::<(i32,), ()>(&mut store, "take-s32")? 243 .call(&mut store, (101,)) 244 .unwrap_err() 245 .downcast::<Trap>()?; 246 new_instance(&mut store)? 247 .get_typed_func::<(u64,), ()>(&mut store, "take-u64")? 248 .call(&mut store, (101,)) 249 .unwrap_err() 250 .downcast::<Trap>()?; 251 new_instance(&mut store)? 252 .get_typed_func::<(i64,), ()>(&mut store, "take-s64")? 253 .call(&mut store, (101,)) 254 .unwrap_err() 255 .downcast::<Trap>()?; 256 257 // Zero can be returned as any integer 258 assert_eq!( 259 instance 260 .get_typed_func::<(), (u8,)>(&mut store, "ret-u8")? 261 .call_and_post_return(&mut store, ())?, 262 (0,) 263 ); 264 assert_eq!( 265 instance 266 .get_typed_func::<(), (i8,)>(&mut store, "ret-s8")? 267 .call_and_post_return(&mut store, ())?, 268 (0,) 269 ); 270 assert_eq!( 271 instance 272 .get_typed_func::<(), (u16,)>(&mut store, "ret-u16")? 273 .call_and_post_return(&mut store, ())?, 274 (0,) 275 ); 276 assert_eq!( 277 instance 278 .get_typed_func::<(), (i16,)>(&mut store, "ret-s16")? 279 .call_and_post_return(&mut store, ())?, 280 (0,) 281 ); 282 assert_eq!( 283 instance 284 .get_typed_func::<(), (u32,)>(&mut store, "ret-u32")? 285 .call_and_post_return(&mut store, ())?, 286 (0,) 287 ); 288 assert_eq!( 289 instance 290 .get_typed_func::<(), (i32,)>(&mut store, "ret-s32")? 291 .call_and_post_return(&mut store, ())?, 292 (0,) 293 ); 294 assert_eq!( 295 instance 296 .get_typed_func::<(), (u64,)>(&mut store, "ret-u64")? 297 .call_and_post_return(&mut store, ())?, 298 (0,) 299 ); 300 assert_eq!( 301 instance 302 .get_typed_func::<(), (i64,)>(&mut store, "ret-s64")? 303 .call_and_post_return(&mut store, ())?, 304 (0,) 305 ); 306 307 // Returning -1 should reinterpret the bytes as defined by each type. 308 assert_eq!( 309 instance 310 .get_typed_func::<(), (u8,)>(&mut store, "retm1-u8")? 311 .call_and_post_return(&mut store, ())?, 312 (0xff,) 313 ); 314 assert_eq!( 315 instance 316 .get_typed_func::<(), (i8,)>(&mut store, "retm1-s8")? 317 .call_and_post_return(&mut store, ())?, 318 (-1,) 319 ); 320 assert_eq!( 321 instance 322 .get_typed_func::<(), (u16,)>(&mut store, "retm1-u16")? 323 .call_and_post_return(&mut store, ())?, 324 (0xffff,) 325 ); 326 assert_eq!( 327 instance 328 .get_typed_func::<(), (i16,)>(&mut store, "retm1-s16")? 329 .call_and_post_return(&mut store, ())?, 330 (-1,) 331 ); 332 assert_eq!( 333 instance 334 .get_typed_func::<(), (u32,)>(&mut store, "retm1-u32")? 335 .call_and_post_return(&mut store, ())?, 336 (0xffffffff,) 337 ); 338 assert_eq!( 339 instance 340 .get_typed_func::<(), (i32,)>(&mut store, "retm1-s32")? 341 .call_and_post_return(&mut store, ())?, 342 (-1,) 343 ); 344 assert_eq!( 345 instance 346 .get_typed_func::<(), (u64,)>(&mut store, "retm1-u64")? 347 .call_and_post_return(&mut store, ())?, 348 (0xffffffff_ffffffff,) 349 ); 350 assert_eq!( 351 instance 352 .get_typed_func::<(), (i64,)>(&mut store, "retm1-s64")? 353 .call_and_post_return(&mut store, ())?, 354 (-1,) 355 ); 356 357 // Returning 100000 should chop off bytes as necessary 358 let ret: u32 = 100000; 359 assert_eq!( 360 instance 361 .get_typed_func::<(), (u8,)>(&mut store, "retbig-u8")? 362 .call_and_post_return(&mut store, ())?, 363 (ret as u8,), 364 ); 365 assert_eq!( 366 instance 367 .get_typed_func::<(), (i8,)>(&mut store, "retbig-s8")? 368 .call_and_post_return(&mut store, ())?, 369 (ret as i8,), 370 ); 371 assert_eq!( 372 instance 373 .get_typed_func::<(), (u16,)>(&mut store, "retbig-u16")? 374 .call_and_post_return(&mut store, ())?, 375 (ret as u16,), 376 ); 377 assert_eq!( 378 instance 379 .get_typed_func::<(), (i16,)>(&mut store, "retbig-s16")? 380 .call_and_post_return(&mut store, ())?, 381 (ret as i16,), 382 ); 383 assert_eq!( 384 instance 385 .get_typed_func::<(), (u32,)>(&mut store, "retbig-u32")? 386 .call_and_post_return(&mut store, ())?, 387 (ret,), 388 ); 389 assert_eq!( 390 instance 391 .get_typed_func::<(), (i32,)>(&mut store, "retbig-s32")? 392 .call_and_post_return(&mut store, ())?, 393 (ret as i32,), 394 ); 395 396 Ok(()) 397 } 398 399 #[test] 400 fn type_layers() -> Result<()> { 401 let component = r#" 402 (component 403 (core module $m 404 (func (export "take-i32-100") (param i32) 405 local.get 0 406 i32.const 2 407 i32.eq 408 br_if 0 409 unreachable 410 ) 411 ) 412 (core instance $i (instantiate $m)) 413 (func (export "take-u32") (param "a" u32) (canon lift (core func $i "take-i32-100"))) 414 ) 415 "#; 416 417 let engine = super::engine(); 418 let component = Component::new(&engine, component)?; 419 let mut store = Store::new(&engine, ()); 420 let instance = Linker::new(&engine).instantiate(&mut store, &component)?; 421 422 instance 423 .get_typed_func::<(Box<u32>,), ()>(&mut store, "take-u32")? 424 .call_and_post_return(&mut store, (Box::new(2),))?; 425 instance 426 .get_typed_func::<(&u32,), ()>(&mut store, "take-u32")? 427 .call_and_post_return(&mut store, (&2,))?; 428 instance 429 .get_typed_func::<(Arc<u32>,), ()>(&mut store, "take-u32")? 430 .call_and_post_return(&mut store, (Arc::new(2),))?; 431 instance 432 .get_typed_func::<(&Box<Arc<Box<u32>>>,), ()>(&mut store, "take-u32")? 433 .call_and_post_return(&mut store, (&Box::new(Arc::new(Box::new(2))),))?; 434 435 Ok(()) 436 } 437 438 #[test] 439 fn floats() -> Result<()> { 440 let component = r#" 441 (component 442 (core module $m 443 (func (export "i32.reinterpret_f32") (param f32) (result i32) 444 local.get 0 445 i32.reinterpret_f32 446 ) 447 (func (export "i64.reinterpret_f64") (param f64) (result i64) 448 local.get 0 449 i64.reinterpret_f64 450 ) 451 (func (export "f32.reinterpret_i32") (param i32) (result f32) 452 local.get 0 453 f32.reinterpret_i32 454 ) 455 (func (export "f64.reinterpret_i64") (param i64) (result f64) 456 local.get 0 457 f64.reinterpret_i64 458 ) 459 ) 460 (core instance $i (instantiate $m)) 461 462 (func (export "f32-to-u32") (param "a" float32) (result u32) 463 (canon lift (core func $i "i32.reinterpret_f32")) 464 ) 465 (func (export "f64-to-u64") (param "a" float64) (result u64) 466 (canon lift (core func $i "i64.reinterpret_f64")) 467 ) 468 (func (export "u32-to-f32") (param "a" u32) (result float32) 469 (canon lift (core func $i "f32.reinterpret_i32")) 470 ) 471 (func (export "u64-to-f64") (param "a" u64) (result float64) 472 (canon lift (core func $i "f64.reinterpret_i64")) 473 ) 474 ) 475 "#; 476 477 let engine = super::engine(); 478 let component = Component::new(&engine, component)?; 479 let mut store = Store::new(&engine, ()); 480 let instance = Linker::new(&engine).instantiate(&mut store, &component)?; 481 let f32_to_u32 = instance.get_typed_func::<(f32,), (u32,)>(&mut store, "f32-to-u32")?; 482 let f64_to_u64 = instance.get_typed_func::<(f64,), (u64,)>(&mut store, "f64-to-u64")?; 483 let u32_to_f32 = instance.get_typed_func::<(u32,), (f32,)>(&mut store, "u32-to-f32")?; 484 let u64_to_f64 = instance.get_typed_func::<(u64,), (f64,)>(&mut store, "u64-to-f64")?; 485 486 assert_eq!(f32_to_u32.call(&mut store, (1.0,))?, (1.0f32.to_bits(),)); 487 f32_to_u32.post_return(&mut store)?; 488 assert_eq!(f64_to_u64.call(&mut store, (2.0,))?, (2.0f64.to_bits(),)); 489 f64_to_u64.post_return(&mut store)?; 490 assert_eq!(u32_to_f32.call(&mut store, (3.0f32.to_bits(),))?, (3.0,)); 491 u32_to_f32.post_return(&mut store)?; 492 assert_eq!(u64_to_f64.call(&mut store, (4.0f64.to_bits(),))?, (4.0,)); 493 u64_to_f64.post_return(&mut store)?; 494 495 assert_eq!( 496 u32_to_f32 497 .call(&mut store, (CANON_32BIT_NAN | 1,))? 498 .0 499 .to_bits(), 500 CANON_32BIT_NAN | 1 501 ); 502 u32_to_f32.post_return(&mut store)?; 503 assert_eq!( 504 u64_to_f64 505 .call(&mut store, (CANON_64BIT_NAN | 1,))? 506 .0 507 .to_bits(), 508 CANON_64BIT_NAN | 1, 509 ); 510 u64_to_f64.post_return(&mut store)?; 511 512 assert_eq!( 513 f32_to_u32.call(&mut store, (f32::from_bits(CANON_32BIT_NAN | 1),))?, 514 (CANON_32BIT_NAN | 1,) 515 ); 516 f32_to_u32.post_return(&mut store)?; 517 assert_eq!( 518 f64_to_u64.call(&mut store, (f64::from_bits(CANON_64BIT_NAN | 1),))?, 519 (CANON_64BIT_NAN | 1,) 520 ); 521 f64_to_u64.post_return(&mut store)?; 522 523 Ok(()) 524 } 525 526 #[test] 527 fn bools() -> Result<()> { 528 let component = r#" 529 (component 530 (core module $m 531 (func (export "pass") (param i32) (result i32) local.get 0) 532 ) 533 (core instance $i (instantiate $m)) 534 535 (func (export "u32-to-bool") (param "a" u32) (result bool) 536 (canon lift (core func $i "pass")) 537 ) 538 (func (export "bool-to-u32") (param "a" bool) (result u32) 539 (canon lift (core func $i "pass")) 540 ) 541 ) 542 "#; 543 544 let engine = super::engine(); 545 let component = Component::new(&engine, component)?; 546 let mut store = Store::new(&engine, ()); 547 let instance = Linker::new(&engine).instantiate(&mut store, &component)?; 548 let u32_to_bool = instance.get_typed_func::<(u32,), (bool,)>(&mut store, "u32-to-bool")?; 549 let bool_to_u32 = instance.get_typed_func::<(bool,), (u32,)>(&mut store, "bool-to-u32")?; 550 551 assert_eq!(bool_to_u32.call(&mut store, (false,))?, (0,)); 552 bool_to_u32.post_return(&mut store)?; 553 assert_eq!(bool_to_u32.call(&mut store, (true,))?, (1,)); 554 bool_to_u32.post_return(&mut store)?; 555 assert_eq!(u32_to_bool.call(&mut store, (0,))?, (false,)); 556 u32_to_bool.post_return(&mut store)?; 557 assert_eq!(u32_to_bool.call(&mut store, (1,))?, (true,)); 558 u32_to_bool.post_return(&mut store)?; 559 assert_eq!(u32_to_bool.call(&mut store, (2,))?, (true,)); 560 u32_to_bool.post_return(&mut store)?; 561 562 Ok(()) 563 } 564 565 #[test] 566 fn chars() -> Result<()> { 567 let component = r#" 568 (component 569 (core module $m 570 (func (export "pass") (param i32) (result i32) local.get 0) 571 ) 572 (core instance $i (instantiate $m)) 573 574 (func (export "u32-to-char") (param "a" u32) (result char) 575 (canon lift (core func $i "pass")) 576 ) 577 (func (export "char-to-u32") (param "a" char) (result u32) 578 (canon lift (core func $i "pass")) 579 ) 580 ) 581 "#; 582 583 let engine = super::engine(); 584 let component = Component::new(&engine, component)?; 585 let mut store = Store::new(&engine, ()); 586 let instance = Linker::new(&engine).instantiate(&mut store, &component)?; 587 let u32_to_char = instance.get_typed_func::<(u32,), (char,)>(&mut store, "u32-to-char")?; 588 let char_to_u32 = instance.get_typed_func::<(char,), (u32,)>(&mut store, "char-to-u32")?; 589 590 let mut roundtrip = |x: char| -> Result<()> { 591 assert_eq!(char_to_u32.call(&mut store, (x,))?, (x as u32,)); 592 char_to_u32.post_return(&mut store)?; 593 assert_eq!(u32_to_char.call(&mut store, (x as u32,))?, (x,)); 594 u32_to_char.post_return(&mut store)?; 595 Ok(()) 596 }; 597 598 roundtrip('x')?; 599 roundtrip('a')?; 600 roundtrip('\0')?; 601 roundtrip('\n')?; 602 roundtrip('')?; 603 604 let u32_to_char = |store: &mut Store<()>| { 605 Linker::new(&engine) 606 .instantiate(&mut *store, &component)? 607 .get_typed_func::<(u32,), (char,)>(&mut *store, "u32-to-char") 608 }; 609 let err = u32_to_char(&mut store)? 610 .call(&mut store, (0xd800,)) 611 .unwrap_err(); 612 assert!(err.to_string().contains("integer out of range"), "{}", err); 613 let err = u32_to_char(&mut store)? 614 .call(&mut store, (0xdfff,)) 615 .unwrap_err(); 616 assert!(err.to_string().contains("integer out of range"), "{}", err); 617 let err = u32_to_char(&mut store)? 618 .call(&mut store, (0x110000,)) 619 .unwrap_err(); 620 assert!(err.to_string().contains("integer out of range"), "{}", err); 621 let err = u32_to_char(&mut store)? 622 .call(&mut store, (u32::MAX,)) 623 .unwrap_err(); 624 assert!(err.to_string().contains("integer out of range"), "{}", err); 625 626 Ok(()) 627 } 628 629 #[test] 630 fn tuple_result() -> Result<()> { 631 let component = r#" 632 (component 633 (core module $m 634 (memory (export "memory") 1) 635 (func (export "foo") (param i32 i32 f32 f64) (result i32) 636 (local $base i32) 637 (local.set $base (i32.const 8)) 638 (i32.store8 offset=0 (local.get $base) (local.get 0)) 639 (i32.store16 offset=2 (local.get $base) (local.get 1)) 640 (f32.store offset=4 (local.get $base) (local.get 2)) 641 (f64.store offset=8 (local.get $base) (local.get 3)) 642 local.get $base 643 ) 644 645 (func (export "invalid") (result i32) 646 i32.const -8 647 ) 648 ) 649 (core instance $i (instantiate $m)) 650 651 (type $result (tuple s8 u16 float32 float64)) 652 (func (export "tuple") 653 (param "a" s8) (param "b" u16) (param "c" float32) (param "d" float64) (result $result) 654 (canon lift (core func $i "foo") (memory $i "memory")) 655 ) 656 (func (export "invalid") (result $result) 657 (canon lift (core func $i "invalid") (memory $i "memory")) 658 ) 659 ) 660 "#; 661 662 let engine = super::engine(); 663 let component = Component::new(&engine, component)?; 664 let mut store = Store::new(&engine, ()); 665 let instance = Linker::new(&engine).instantiate(&mut store, &component)?; 666 667 let input = (-1, 100, 3.0, 100.0); 668 let output = instance 669 .get_typed_func::<(i8, u16, f32, f64), ((i8, u16, f32, f64),)>(&mut store, "tuple")? 670 .call_and_post_return(&mut store, input)?; 671 assert_eq!((input,), output); 672 673 let invalid_func = 674 instance.get_typed_func::<(), ((i8, u16, f32, f64),)>(&mut store, "invalid")?; 675 let err = invalid_func.call(&mut store, ()).err().unwrap(); 676 assert!( 677 err.to_string().contains("pointer out of bounds of memory"), 678 "{}", 679 err 680 ); 681 682 Ok(()) 683 } 684 685 #[test] 686 fn strings() -> Result<()> { 687 let component = format!( 688 r#"(component 689 (core module $m 690 (memory (export "memory") 1) 691 (func (export "roundtrip") (param i32 i32) (result i32) 692 (local $base i32) 693 (local.set $base 694 (call $realloc 695 (i32.const 0) 696 (i32.const 0) 697 (i32.const 4) 698 (i32.const 8))) 699 (i32.store offset=0 700 (local.get $base) 701 (local.get 0)) 702 (i32.store offset=4 703 (local.get $base) 704 (local.get 1)) 705 (local.get $base) 706 ) 707 708 {REALLOC_AND_FREE} 709 ) 710 (core instance $i (instantiate $m)) 711 712 (func (export "list8-to-str") (param "a" (list u8)) (result string) 713 (canon lift 714 (core func $i "roundtrip") 715 (memory $i "memory") 716 (realloc (func $i "realloc")) 717 ) 718 ) 719 (func (export "str-to-list8") (param "a" string) (result (list u8)) 720 (canon lift 721 (core func $i "roundtrip") 722 (memory $i "memory") 723 (realloc (func $i "realloc")) 724 ) 725 ) 726 (func (export "list16-to-str") (param "a" (list u16)) (result string) 727 (canon lift 728 (core func $i "roundtrip") 729 string-encoding=utf16 730 (memory $i "memory") 731 (realloc (func $i "realloc")) 732 ) 733 ) 734 (func (export "str-to-list16") (param "a" string) (result (list u16)) 735 (canon lift 736 (core func $i "roundtrip") 737 string-encoding=utf16 738 (memory $i "memory") 739 (realloc (func $i "realloc")) 740 ) 741 ) 742 )"# 743 ); 744 745 let engine = super::engine(); 746 let component = Component::new(&engine, component)?; 747 let mut store = Store::new(&engine, ()); 748 let instance = Linker::new(&engine).instantiate(&mut store, &component)?; 749 let list8_to_str = 750 instance.get_typed_func::<(&[u8],), (WasmStr,)>(&mut store, "list8-to-str")?; 751 let str_to_list8 = 752 instance.get_typed_func::<(&str,), (WasmList<u8>,)>(&mut store, "str-to-list8")?; 753 let list16_to_str = 754 instance.get_typed_func::<(&[u16],), (WasmStr,)>(&mut store, "list16-to-str")?; 755 let str_to_list16 = 756 instance.get_typed_func::<(&str,), (WasmList<u16>,)>(&mut store, "str-to-list16")?; 757 758 let mut roundtrip = |x: &str| -> Result<()> { 759 let ret = list8_to_str.call(&mut store, (x.as_bytes(),))?.0; 760 assert_eq!(ret.to_str(&store)?, x); 761 list8_to_str.post_return(&mut store)?; 762 763 let utf16 = x.encode_utf16().collect::<Vec<_>>(); 764 let ret = list16_to_str.call(&mut store, (&utf16[..],))?.0; 765 assert_eq!(ret.to_str(&store)?, x); 766 list16_to_str.post_return(&mut store)?; 767 768 let ret = str_to_list8.call(&mut store, (x,))?.0; 769 assert_eq!( 770 ret.iter(&mut store).collect::<Result<Vec<_>>>()?, 771 x.as_bytes() 772 ); 773 str_to_list8.post_return(&mut store)?; 774 775 let ret = str_to_list16.call(&mut store, (x,))?.0; 776 assert_eq!(ret.iter(&mut store).collect::<Result<Vec<_>>>()?, utf16,); 777 str_to_list16.post_return(&mut store)?; 778 779 Ok(()) 780 }; 781 782 roundtrip("")?; 783 roundtrip("foo")?; 784 roundtrip("hello there")?; 785 roundtrip("")?; 786 roundtrip("Löwe 老虎 Léopard")?; 787 788 let ret = list8_to_str.call(&mut store, (b"\xff",))?.0; 789 let err = ret.to_str(&store).unwrap_err(); 790 assert!(err.to_string().contains("invalid utf-8"), "{}", err); 791 list8_to_str.post_return(&mut store)?; 792 793 let ret = list8_to_str 794 .call(&mut store, (b"hello there \xff invalid",))? 795 .0; 796 let err = ret.to_str(&store).unwrap_err(); 797 assert!(err.to_string().contains("invalid utf-8"), "{}", err); 798 list8_to_str.post_return(&mut store)?; 799 800 let ret = list16_to_str.call(&mut store, (&[0xd800],))?.0; 801 let err = ret.to_str(&store).unwrap_err(); 802 assert!(err.to_string().contains("unpaired surrogate"), "{}", err); 803 list16_to_str.post_return(&mut store)?; 804 805 let ret = list16_to_str.call(&mut store, (&[0xdfff],))?.0; 806 let err = ret.to_str(&store).unwrap_err(); 807 assert!(err.to_string().contains("unpaired surrogate"), "{}", err); 808 list16_to_str.post_return(&mut store)?; 809 810 let ret = list16_to_str.call(&mut store, (&[0xd800, 0xff00],))?.0; 811 let err = ret.to_str(&store).unwrap_err(); 812 assert!(err.to_string().contains("unpaired surrogate"), "{}", err); 813 list16_to_str.post_return(&mut store)?; 814 815 Ok(()) 816 } 817 818 #[tokio::test] 819 async fn async_reentrance() -> Result<()> { 820 _ = env_logger::try_init(); 821 822 let component = r#" 823 (component 824 (core module $shim 825 (import "" "task.return" (func $task-return (param i32))) 826 (table (export "funcs") 1 1 funcref) 827 (func (export "export") (param i32) (result i32) 828 (call_indirect (i32.const 0) (local.get 0)) 829 ) 830 (func (export "callback") (param i32 i32 i32) (result i32) unreachable) 831 ) 832 (core func $task-return (canon task.return (result u32))) 833 (core instance $shim (instantiate $shim 834 (with "" (instance (export "task.return" (func $task-return)))) 835 )) 836 (func $shim-export (param "p1" u32) (result u32) 837 (canon lift (core func $shim "export") async (callback (func $shim "callback"))) 838 ) 839 840 (component $inner 841 (import "import" (func $import (param "p1" u32) (result u32))) 842 (core module $libc (memory (export "memory") 1)) 843 (core instance $libc (instantiate $libc)) 844 (core func $import (canon lower (func $import) async (memory $libc "memory"))) 845 846 (core module $m 847 (import "libc" "memory" (memory 1)) 848 (import "" "import" (func $import (param i32 i32) (result i32))) 849 (import "" "task.return" (func $task-return (param i32))) 850 (func (export "export") (param i32) (result i32) 851 (i32.store offset=0 (i32.const 1200) (local.get 0)) 852 (call $import (i32.const 1200) (i32.const 1204)) 853 drop 854 (call $task-return (i32.load offset=0 (i32.const 1204))) 855 i32.const 0 856 ) 857 (func (export "callback") (param i32 i32 i32) (result i32) unreachable) 858 ) 859 (core type $task-return-type (func (param i32))) 860 (core func $task-return (canon task.return (result u32))) 861 (core instance $i (instantiate $m 862 (with "" (instance 863 (export "task.return" (func $task-return)) 864 (export "import" (func $import)) 865 )) 866 (with "libc" (instance $libc)) 867 )) 868 (func (export "export") (param "p1" u32) (result u32) 869 (canon lift (core func $i "export") async (callback (func $i "callback"))) 870 ) 871 ) 872 (instance $inner (instantiate $inner (with "import" (func $shim-export)))) 873 874 (core module $libc (memory (export "memory") 1)) 875 (core instance $libc (instantiate $libc)) 876 (core func $inner-export (canon lower (func $inner "export") async (memory $libc "memory"))) 877 878 (core module $donut 879 (import "" "funcs" (table 1 1 funcref)) 880 (import "libc" "memory" (memory 1)) 881 (import "" "import" (func $import (param i32 i32) (result i32))) 882 (import "" "task.return" (func $task-return (param i32))) 883 (func $host-export (export "export") (param i32) (result i32) 884 (i32.store offset=0 (i32.const 1200) (local.get 0)) 885 (call $import (i32.const 1200) (i32.const 1204)) 886 drop 887 (call $task-return (i32.load offset=0 (i32.const 1204))) 888 i32.const 0 889 ) 890 (func $guest-export (export "guest-export") (param i32) (result i32) unreachable) 891 (func (export "callback") (param i32 i32 i32) (result i32) unreachable) 892 (func $start 893 (table.set (i32.const 0) (ref.func $guest-export)) 894 ) 895 (start $start) 896 ) 897 898 (core instance $donut (instantiate $donut 899 (with "" (instance 900 (export "task.return" (func $task-return)) 901 (export "import" (func $inner-export)) 902 (export "funcs" (table $shim "funcs")) 903 )) 904 (with "libc" (instance $libc)) 905 )) 906 (func (export "export") (param "p1" u32) (result u32) 907 (canon lift (core func $donut "export") async (callback (func $donut "callback"))) 908 ) 909 )"#; 910 911 let mut config = Config::new(); 912 config.wasm_component_model_async(true); 913 config.async_support(true); 914 let engine = &Engine::new(&config)?; 915 let component = Component::new(&engine, component)?; 916 let mut store = Store::new(&engine, ()); 917 918 let instance = Linker::new(&engine) 919 .instantiate_async(&mut store, &component) 920 .await?; 921 let func = instance.get_typed_func::<(u32,), (u32,)>(&mut store, "export")?; 922 let message = "cannot enter component instance"; 923 match instance 924 .run_concurrent(&mut store, async move |accessor| { 925 anyhow::Ok(func.call_concurrent(accessor, (42,)).await?.0) 926 }) 927 .await 928 { 929 Ok(_) => panic!(), 930 Err(e) => assert!( 931 format!("{e:?}").contains(message), 932 "expected `{message}`; got `{e:?}`" 933 ), 934 } 935 936 Ok(()) 937 } 938 939 #[tokio::test] 940 async fn missing_task_return_call_stackless() -> Result<()> { 941 task_return_trap( 942 r#"(component 943 (core module $m 944 (import "" "task.return" (func $task-return)) 945 (func (export "foo") (result i32) 946 i32.const 0 947 ) 948 (func (export "callback") (param i32 i32 i32) (result i32) unreachable) 949 ) 950 (core func $task-return (canon task.return)) 951 (core instance $i (instantiate $m 952 (with "" (instance (export "task.return" (func $task-return)))) 953 )) 954 (func (export "foo") (canon lift (core func $i "foo") async (callback (func $i "callback")))) 955 )"#, 956 "wasm trap: async-lifted export failed to produce a result", 957 ) 958 .await 959 } 960 961 #[tokio::test] 962 async fn missing_task_return_call_stackful() -> Result<()> { 963 task_return_trap( 964 r#"(component 965 (core module $m 966 (import "" "task.return" (func $task-return)) 967 (func (export "foo")) 968 ) 969 (core func $task-return (canon task.return)) 970 (core instance $i (instantiate $m 971 (with "" (instance (export "task.return" (func $task-return)))) 972 )) 973 (func (export "foo") (canon lift (core func $i "foo") async)) 974 )"#, 975 "wasm trap: async-lifted export failed to produce a result", 976 ) 977 .await 978 } 979 980 #[tokio::test] 981 async fn task_return_type_mismatch() -> Result<()> { 982 task_return_trap( 983 r#"(component 984 (core module $m 985 (import "" "task.return" (func $task-return (param i32))) 986 (func (export "foo") (call $task-return (i32.const 42))) 987 ) 988 (core func $task-return (canon task.return (result u32))) 989 (core instance $i (instantiate $m 990 (with "" (instance (export "task.return" (func $task-return)))) 991 )) 992 (func (export "foo") (canon lift (core func $i "foo") async)) 993 )"#, 994 "invalid `task.return` signature and/or options for current task", 995 ) 996 .await 997 } 998 999 #[tokio::test] 1000 async fn task_return_memory_mismatch() -> Result<()> { 1001 task_return_trap( 1002 r#"(component 1003 (core module $libc (memory (export "memory") 1)) 1004 (core instance $libc (instantiate $libc)) 1005 (core module $m 1006 (import "" "task.return" (func $task-return)) 1007 (func (export "foo") (call $task-return)) 1008 ) 1009 (core func $task-return (canon task.return (memory $libc "memory"))) 1010 (core instance $i (instantiate $m 1011 (with "" (instance (export "task.return" (func $task-return)))) 1012 )) 1013 (func (export "foo") (canon lift (core func $i "foo") async)) 1014 )"#, 1015 "invalid `task.return` signature and/or options for current task", 1016 ) 1017 .await 1018 } 1019 1020 #[tokio::test] 1021 async fn task_return_string_encoding_mismatch() -> Result<()> { 1022 task_return_trap( 1023 r#"(component 1024 (core module $m 1025 (import "" "task.return" (func $task-return)) 1026 (func (export "foo") (call $task-return)) 1027 ) 1028 (core func $task-return (canon task.return string-encoding=utf16)) 1029 (core instance $i (instantiate $m 1030 (with "" (instance (export "task.return" (func $task-return)))) 1031 )) 1032 (func (export "foo") (canon lift (core func $i "foo") async)) 1033 )"#, 1034 "invalid `task.return` signature and/or options for current task", 1035 ) 1036 .await 1037 } 1038 1039 async fn task_return_trap(component: &str, substring: &str) -> Result<()> { 1040 let mut config = Config::new(); 1041 config.wasm_component_model_async(true); 1042 config.wasm_component_model_async_stackful(true); 1043 config.async_support(true); 1044 let engine = &Engine::new(&config)?; 1045 let component = Component::new(&engine, component)?; 1046 let mut store = Store::new(&engine, ()); 1047 1048 let instance = Linker::new(&engine) 1049 .instantiate_async(&mut store, &component) 1050 .await?; 1051 1052 let func = instance.get_typed_func::<(), ()>(&mut store, "foo")?; 1053 match instance 1054 .run_concurrent(&mut store, async move |accessor| { 1055 anyhow::Ok(func.call_concurrent(accessor, ()).await?.0) 1056 }) 1057 .await 1058 { 1059 Ok(_) => panic!(), 1060 Err(e) => { 1061 assert!( 1062 format!("{e:?}").contains(substring), 1063 "could not find `{substring}` in `{e:?}`" 1064 ) 1065 } 1066 } 1067 1068 Ok(()) 1069 } 1070 1071 #[tokio::test] 1072 async fn many_parameters() -> Result<()> { 1073 test_many_parameters(false, false).await 1074 } 1075 1076 #[tokio::test] 1077 async fn many_parameters_concurrent() -> Result<()> { 1078 test_many_parameters(false, true).await 1079 } 1080 1081 #[tokio::test] 1082 async fn many_parameters_dynamic() -> Result<()> { 1083 test_many_parameters(true, false).await 1084 } 1085 1086 #[tokio::test] 1087 async fn many_parameters_dynamic_concurrent() -> Result<()> { 1088 test_many_parameters(true, true).await 1089 } 1090 1091 async fn test_many_parameters(dynamic: bool, concurrent: bool) -> Result<()> { 1092 let (body, async_opts) = if concurrent { 1093 ( 1094 r#" 1095 (call $task-return 1096 (i32.const 0) 1097 (i32.mul 1098 (memory.size) 1099 (i32.const 65536) 1100 ) 1101 (local.get 0) 1102 ) 1103 1104 (i32.const 0) 1105 "#, 1106 r#"async (callback (func $i "callback"))"#, 1107 ) 1108 } else { 1109 ( 1110 r#" 1111 (local $base i32) 1112 1113 ;; Allocate space for the return 1114 (local.set $base 1115 (call $realloc 1116 (i32.const 0) 1117 (i32.const 0) 1118 (i32.const 4) 1119 (i32.const 12))) 1120 1121 ;; Store the pointer/length of the entire linear memory 1122 ;; so we have access to everything. 1123 (i32.store offset=0 1124 (local.get $base) 1125 (i32.const 0)) 1126 (i32.store offset=4 1127 (local.get $base) 1128 (i32.mul 1129 (memory.size) 1130 (i32.const 65536))) 1131 1132 ;; And also store our pointer parameter 1133 (i32.store offset=8 1134 (local.get $base) 1135 (local.get 0)) 1136 1137 (local.get $base) 1138 "#, 1139 "", 1140 ) 1141 }; 1142 1143 let component = format!( 1144 r#"(component 1145 (core module $libc 1146 (memory (export "memory") 1) 1147 1148 {REALLOC_AND_FREE} 1149 ) 1150 (core instance $libc (instantiate $libc)) 1151 (core module $m 1152 (import "libc" "memory" (memory 1)) 1153 (import "libc" "realloc" (func $realloc (param i32 i32 i32 i32) (result i32))) 1154 (import "" "task.return" (func $task-return (param i32 i32 i32))) 1155 (func (export "foo") (param i32) (result i32) 1156 {body} 1157 ) 1158 (func (export "callback") (param i32 i32 i32) (result i32) unreachable) 1159 ) 1160 (type $tuple (tuple (list u8) u32)) 1161 (core func $task-return (canon task.return 1162 (result $tuple) 1163 (memory $libc "memory") 1164 )) 1165 (core instance $i (instantiate $m 1166 (with "" (instance (export "task.return" (func $task-return)))) 1167 (with "libc" (instance $libc)) 1168 )) 1169 1170 (type $t (func 1171 (param "p1" s8) ;; offset 0, size 1 1172 (param "p2" u64) ;; offset 8, size 8 1173 (param "p3" float32) ;; offset 16, size 4 1174 (param "p4" u8) ;; offset 20, size 1 1175 (param "p5" s16) ;; offset 22, size 2 1176 (param "p6" string) ;; offset 24, size 8 1177 (param "p7" (list u32)) ;; offset 32, size 8 1178 (param "p8" bool) ;; offset 40, size 1 1179 (param "p9" bool) ;; offset 41, size 1 1180 (param "p0" char) ;; offset 44, size 4 1181 (param "pa" (list bool)) ;; offset 48, size 8 1182 (param "pb" (list char)) ;; offset 56, size 8 1183 (param "pc" (list string)) ;; offset 64, size 8 1184 1185 (result $tuple) 1186 )) 1187 (func (export "many-param") (type $t) 1188 (canon lift 1189 (core func $i "foo") 1190 (memory $libc "memory") 1191 (realloc (func $libc "realloc")) 1192 {async_opts} 1193 ) 1194 ) 1195 )"# 1196 ); 1197 1198 let mut config = Config::new(); 1199 config.wasm_component_model_async(true); 1200 config.async_support(true); 1201 let engine = &Engine::new(&config)?; 1202 let component = Component::new(&engine, component)?; 1203 let mut store = Store::new(&engine, ()); 1204 1205 let instance = Linker::new(&engine) 1206 .instantiate_async(&mut store, &component) 1207 .await?; 1208 1209 let input = ( 1210 -100, 1211 u64::MAX / 2, 1212 f32::from_bits(CANON_32BIT_NAN | 1), 1213 38, 1214 18831, 1215 "this is the first string", 1216 [1, 2, 3, 4, 5, 6, 7, 8].as_slice(), 1217 true, 1218 false, 1219 '', 1220 [false, true, false, true, true].as_slice(), 1221 ['', '', '', '', ''].as_slice(), 1222 [ 1223 "the quick", 1224 "brown fox", 1225 "was too lazy", 1226 "to jump over the dog", 1227 "what a demanding dog", 1228 ] 1229 .as_slice(), 1230 ); 1231 1232 let (memory, pointer) = if dynamic { 1233 let input = vec![ 1234 Val::S8(input.0), 1235 Val::U64(input.1), 1236 Val::Float32(input.2), 1237 Val::U8(input.3), 1238 Val::S16(input.4), 1239 Val::String(input.5.into()), 1240 Val::List(input.6.iter().copied().map(Val::U32).collect()), 1241 Val::Bool(input.7), 1242 Val::Bool(input.8), 1243 Val::Char(input.9), 1244 Val::List(input.10.iter().copied().map(Val::Bool).collect()), 1245 Val::List(input.11.iter().copied().map(Val::Char).collect()), 1246 Val::List(input.12.iter().map(|&s| Val::String(s.into())).collect()), 1247 ]; 1248 let func = instance.get_func(&mut store, "many-param").unwrap(); 1249 1250 let mut results = vec![Val::Bool(false)]; 1251 if concurrent { 1252 instance 1253 .run_concurrent(&mut store, async |store| { 1254 func.call_concurrent(store, &input, &mut results).await?; 1255 anyhow::Ok(()) 1256 }) 1257 .await??; 1258 } else { 1259 func.call_async(&mut store, &input, &mut results).await?; 1260 }; 1261 let mut results = results.into_iter(); 1262 let Some(Val::Tuple(results)) = results.next() else { 1263 panic!() 1264 }; 1265 let mut results = results.into_iter(); 1266 let Some(Val::List(memory)) = results.next() else { 1267 panic!() 1268 }; 1269 let Some(Val::U32(pointer)) = results.next() else { 1270 panic!() 1271 }; 1272 ( 1273 memory 1274 .into_iter() 1275 .map(|v| if let Val::U8(v) = v { v } else { panic!() }) 1276 .collect(), 1277 pointer, 1278 ) 1279 } else { 1280 let func = instance.get_typed_func::<( 1281 i8, 1282 u64, 1283 f32, 1284 u8, 1285 i16, 1286 &str, 1287 &[u32], 1288 bool, 1289 bool, 1290 char, 1291 &[bool], 1292 &[char], 1293 &[&str], 1294 ), ((Vec<u8>, u32),)>(&mut store, "many-param")?; 1295 1296 if concurrent { 1297 instance 1298 .run_concurrent(&mut store, async move |accessor| { 1299 anyhow::Ok(func.call_concurrent(accessor, input).await?.0) 1300 }) 1301 .await?? 1302 .0 1303 } else { 1304 func.call_async(&mut store, input).await?.0 1305 } 1306 }; 1307 let memory = &memory[..]; 1308 1309 let mut actual = &memory[pointer as usize..][..72]; 1310 assert_eq!(i8::from_le_bytes(*actual.take_n::<1>()), input.0); 1311 actual.skip::<7>(); 1312 assert_eq!(u64::from_le_bytes(*actual.take_n::<8>()), input.1); 1313 assert_eq!( 1314 u32::from_le_bytes(*actual.take_n::<4>()), 1315 CANON_32BIT_NAN | 1 1316 ); 1317 assert_eq!(u8::from_le_bytes(*actual.take_n::<1>()), input.3); 1318 actual.skip::<1>(); 1319 assert_eq!(i16::from_le_bytes(*actual.take_n::<2>()), input.4); 1320 assert_eq!(actual.ptr_len(memory, 1), input.5.as_bytes()); 1321 let mut mem = actual.ptr_len(memory, 4); 1322 for expected in input.6.iter() { 1323 assert_eq!(u32::from_le_bytes(*mem.take_n::<4>()), *expected); 1324 } 1325 assert!(mem.is_empty()); 1326 assert_eq!(actual.take_n::<1>(), &[input.7 as u8]); 1327 assert_eq!(actual.take_n::<1>(), &[input.8 as u8]); 1328 actual.skip::<2>(); 1329 assert_eq!(u32::from_le_bytes(*actual.take_n::<4>()), input.9 as u32); 1330 1331 // (list bool) 1332 mem = actual.ptr_len(memory, 1); 1333 for expected in input.10.iter() { 1334 assert_eq!(mem.take_n::<1>(), &[*expected as u8]); 1335 } 1336 assert!(mem.is_empty()); 1337 1338 // (list char) 1339 mem = actual.ptr_len(memory, 4); 1340 for expected in input.11.iter() { 1341 assert_eq!(u32::from_le_bytes(*mem.take_n::<4>()), *expected as u32); 1342 } 1343 assert!(mem.is_empty()); 1344 1345 // (list string) 1346 mem = actual.ptr_len(memory, 8); 1347 for expected in input.12.iter() { 1348 let actual = mem.ptr_len(memory, 1); 1349 assert_eq!(actual, expected.as_bytes()); 1350 } 1351 assert!(mem.is_empty()); 1352 assert!(actual.is_empty()); 1353 1354 Ok(()) 1355 } 1356 1357 #[tokio::test] 1358 async fn many_results() -> Result<()> { 1359 test_many_results(false, false).await 1360 } 1361 1362 #[tokio::test] 1363 async fn many_results_concurrent() -> Result<()> { 1364 test_many_results(false, true).await 1365 } 1366 1367 #[tokio::test] 1368 async fn many_results_dynamic() -> Result<()> { 1369 test_many_results(true, false).await 1370 } 1371 1372 #[tokio::test] 1373 async fn many_results_dynamic_concurrent() -> Result<()> { 1374 test_many_results(true, true).await 1375 } 1376 1377 async fn test_many_results(dynamic: bool, concurrent: bool) -> Result<()> { 1378 let (ret, async_opts) = if concurrent { 1379 ( 1380 r#" 1381 call $task-return 1382 i32.const 0 1383 "#, 1384 r#"async (callback (func $i "callback"))"#, 1385 ) 1386 } else { 1387 ("", "") 1388 }; 1389 1390 let my_nan = CANON_32BIT_NAN | 1; 1391 1392 let component = format!( 1393 r#"(component 1394 (core module $libc 1395 (memory (export "memory") 1) 1396 1397 {REALLOC_AND_FREE} 1398 ) 1399 (core instance $libc (instantiate $libc)) 1400 (core module $m 1401 (import "libc" "memory" (memory 1)) 1402 (import "libc" "realloc" (func $realloc (param i32 i32 i32 i32) (result i32))) 1403 (import "" "task.return" (func $task-return (param i32))) 1404 (func (export "foo") (result i32) 1405 (local $base i32) 1406 (local $string i32) 1407 (local $list i32) 1408 1409 (local.set $base 1410 (call $realloc 1411 (i32.const 0) 1412 (i32.const 0) 1413 (i32.const 8) 1414 (i32.const 72))) 1415 1416 (i32.store8 offset=0 1417 (local.get $base) 1418 (i32.const -100)) 1419 1420 (i64.store offset=8 1421 (local.get $base) 1422 (i64.const 9223372036854775807)) 1423 1424 (f32.store offset=16 1425 (local.get $base) 1426 (f32.reinterpret_i32 (i32.const {my_nan}))) 1427 1428 (i32.store8 offset=20 1429 (local.get $base) 1430 (i32.const 38)) 1431 1432 (i32.store16 offset=22 1433 (local.get $base) 1434 (i32.const 18831)) 1435 1436 (local.set $string 1437 (call $realloc 1438 (i32.const 0) 1439 (i32.const 0) 1440 (i32.const 1) 1441 (i32.const 6))) 1442 1443 (i32.store8 offset=0 1444 (local.get $string) 1445 (i32.const 97)) ;; 'a' 1446 (i32.store8 offset=1 1447 (local.get $string) 1448 (i32.const 98)) ;; 'b' 1449 (i32.store8 offset=2 1450 (local.get $string) 1451 (i32.const 99)) ;; 'c' 1452 (i32.store8 offset=3 1453 (local.get $string) 1454 (i32.const 100)) ;; 'd' 1455 (i32.store8 offset=4 1456 (local.get $string) 1457 (i32.const 101)) ;; 'e' 1458 (i32.store8 offset=5 1459 (local.get $string) 1460 (i32.const 102)) ;; 'f' 1461 1462 (i32.store offset=24 1463 (local.get $base) 1464 (local.get $string)) 1465 1466 (i32.store offset=28 1467 (local.get $base) 1468 (i32.const 2)) 1469 1470 (local.set $list 1471 (call $realloc 1472 (i32.const 0) 1473 (i32.const 0) 1474 (i32.const 4) 1475 (i32.const 32))) 1476 1477 (i32.store offset=0 1478 (local.get $list) 1479 (i32.const 1)) 1480 (i32.store offset=4 1481 (local.get $list) 1482 (i32.const 2)) 1483 (i32.store offset=8 1484 (local.get $list) 1485 (i32.const 3)) 1486 (i32.store offset=12 1487 (local.get $list) 1488 (i32.const 4)) 1489 (i32.store offset=16 1490 (local.get $list) 1491 (i32.const 5)) 1492 (i32.store offset=20 1493 (local.get $list) 1494 (i32.const 6)) 1495 (i32.store offset=24 1496 (local.get $list) 1497 (i32.const 7)) 1498 (i32.store offset=28 1499 (local.get $list) 1500 (i32.const 8)) 1501 1502 (i32.store offset=32 1503 (local.get $base) 1504 (local.get $list)) 1505 1506 (i32.store offset=36 1507 (local.get $base) 1508 (i32.const 8)) 1509 1510 (i32.store8 offset=40 1511 (local.get $base) 1512 (i32.const 1)) 1513 1514 (i32.store8 offset=41 1515 (local.get $base) 1516 (i32.const 0)) 1517 1518 (i32.store offset=44 1519 (local.get $base) 1520 (i32.const 128681)) ;; '' 1521 1522 (local.set $list 1523 (call $realloc 1524 (i32.const 0) 1525 (i32.const 0) 1526 (i32.const 1) 1527 (i32.const 5))) 1528 1529 (i32.store8 offset=0 1530 (local.get $list) 1531 (i32.const 0)) 1532 (i32.store8 offset=1 1533 (local.get $list) 1534 (i32.const 1)) 1535 (i32.store8 offset=2 1536 (local.get $list) 1537 (i32.const 0)) 1538 (i32.store8 offset=3 1539 (local.get $list) 1540 (i32.const 1)) 1541 (i32.store8 offset=4 1542 (local.get $list) 1543 (i32.const 1)) 1544 1545 (i32.store offset=48 1546 (local.get $base) 1547 (local.get $list)) 1548 1549 (i32.store offset=52 1550 (local.get $base) 1551 (i32.const 5)) 1552 1553 (local.set $list 1554 (call $realloc 1555 (i32.const 0) 1556 (i32.const 0) 1557 (i32.const 4) 1558 (i32.const 20))) 1559 1560 (i32.store offset=0 1561 (local.get $list) 1562 (i32.const 127820)) ;; '' 1563 (i32.store offset=4 1564 (local.get $list) 1565 (i32.const 129360)) ;; '' 1566 (i32.store offset=8 1567 (local.get $list) 1568 (i32.const 127831)) ;; '' 1569 (i32.store offset=12 1570 (local.get $list) 1571 (i32.const 127833)) ;; '' 1572 (i32.store offset=16 1573 (local.get $list) 1574 (i32.const 127841)) ;; '' 1575 1576 (i32.store offset=56 1577 (local.get $base) 1578 (local.get $list)) 1579 1580 (i32.store offset=60 1581 (local.get $base) 1582 (i32.const 5)) 1583 1584 (local.set $list 1585 (call $realloc 1586 (i32.const 0) 1587 (i32.const 0) 1588 (i32.const 4) 1589 (i32.const 16))) 1590 1591 (i32.store offset=0 1592 (local.get $list) 1593 (i32.add (local.get $string) (i32.const 2))) 1594 (i32.store offset=4 1595 (local.get $list) 1596 (i32.const 2)) 1597 (i32.store offset=8 1598 (local.get $list) 1599 (i32.add (local.get $string) (i32.const 4))) 1600 (i32.store offset=12 1601 (local.get $list) 1602 (i32.const 2)) 1603 1604 (i32.store offset=64 1605 (local.get $base) 1606 (local.get $list)) 1607 1608 (i32.store offset=68 1609 (local.get $base) 1610 (i32.const 2)) 1611 1612 local.get $base 1613 1614 {ret} 1615 ) 1616 (func (export "callback") (param i32 i32 i32) (result i32) unreachable) 1617 ) 1618 (type $tuple (tuple 1619 s8 1620 u64 1621 float32 1622 u8 1623 s16 1624 string 1625 (list u32) 1626 bool 1627 bool 1628 char 1629 (list bool) 1630 (list char) 1631 (list string) 1632 )) 1633 (core func $task-return (canon task.return 1634 (result $tuple) 1635 (memory $libc "memory") 1636 )) 1637 (core instance $i (instantiate $m 1638 (with "" (instance (export "task.return" (func $task-return)))) 1639 (with "libc" (instance $libc)) 1640 )) 1641 1642 (type $t (func (result $tuple))) 1643 (func (export "many-results") (type $t) 1644 (canon lift 1645 (core func $i "foo") 1646 (memory $libc "memory") 1647 (realloc (func $libc "realloc")) 1648 {async_opts} 1649 ) 1650 ) 1651 )"# 1652 ); 1653 1654 let mut config = Config::new(); 1655 config.wasm_component_model_async(true); 1656 config.async_support(true); 1657 let engine = &Engine::new(&config)?; 1658 let component = Component::new(&engine, component)?; 1659 let mut store = Store::new(&engine, ()); 1660 1661 let instance = Linker::new(&engine) 1662 .instantiate_async(&mut store, &component) 1663 .await?; 1664 1665 let expected = ( 1666 -100i8, 1667 u64::MAX / 2, 1668 f32::from_bits(CANON_32BIT_NAN | 1), 1669 38u8, 1670 18831i16, 1671 "ab".to_string(), 1672 vec![1u32, 2, 3, 4, 5, 6, 7, 8], 1673 true, 1674 false, 1675 '', 1676 vec![false, true, false, true, true], 1677 vec!['', '', '', '', ''], 1678 vec!["cd".to_string(), "ef".to_string()], 1679 ); 1680 1681 let actual = if dynamic { 1682 let func = instance.get_func(&mut store, "many-results").unwrap(); 1683 1684 let mut results = vec![Val::Bool(false)]; 1685 if concurrent { 1686 instance 1687 .run_concurrent(&mut store, async |store| { 1688 func.call_concurrent(store, &[], &mut results).await?; 1689 anyhow::Ok(()) 1690 }) 1691 .await??; 1692 } else { 1693 func.call_async(&mut store, &[], &mut results).await?; 1694 }; 1695 let mut results = results.into_iter(); 1696 1697 let Some(Val::Tuple(results)) = results.next() else { 1698 panic!() 1699 }; 1700 let mut results = results.into_iter(); 1701 let Some(Val::S8(p1)) = results.next() else { 1702 panic!() 1703 }; 1704 let Some(Val::U64(p2)) = results.next() else { 1705 panic!() 1706 }; 1707 let Some(Val::Float32(p3)) = results.next() else { 1708 panic!() 1709 }; 1710 let Some(Val::U8(p4)) = results.next() else { 1711 panic!() 1712 }; 1713 let Some(Val::S16(p5)) = results.next() else { 1714 panic!() 1715 }; 1716 let Some(Val::String(p6)) = results.next() else { 1717 panic!() 1718 }; 1719 let Some(Val::List(p7)) = results.next() else { 1720 panic!() 1721 }; 1722 let p7 = p7 1723 .into_iter() 1724 .map(|v| if let Val::U32(v) = v { v } else { panic!() }) 1725 .collect(); 1726 let Some(Val::Bool(p8)) = results.next() else { 1727 panic!() 1728 }; 1729 let Some(Val::Bool(p9)) = results.next() else { 1730 panic!() 1731 }; 1732 let Some(Val::Char(p0)) = results.next() else { 1733 panic!() 1734 }; 1735 let Some(Val::List(pa)) = results.next() else { 1736 panic!() 1737 }; 1738 let pa = pa 1739 .into_iter() 1740 .map(|v| if let Val::Bool(v) = v { v } else { panic!() }) 1741 .collect(); 1742 let Some(Val::List(pb)) = results.next() else { 1743 panic!() 1744 }; 1745 let pb = pb 1746 .into_iter() 1747 .map(|v| if let Val::Char(v) = v { v } else { panic!() }) 1748 .collect(); 1749 let Some(Val::List(pc)) = results.next() else { 1750 panic!() 1751 }; 1752 let pc = pc 1753 .into_iter() 1754 .map(|v| if let Val::String(v) = v { v } else { panic!() }) 1755 .collect(); 1756 1757 (p1, p2, p3, p4, p5, p6, p7, p8, p9, p0, pa, pb, pc) 1758 } else { 1759 let func = instance.get_typed_func::<(), (( 1760 i8, 1761 u64, 1762 f32, 1763 u8, 1764 i16, 1765 String, 1766 Vec<u32>, 1767 bool, 1768 bool, 1769 char, 1770 Vec<bool>, 1771 Vec<char>, 1772 Vec<String>, 1773 ),)>(&mut store, "many-results")?; 1774 1775 if concurrent { 1776 instance 1777 .run_concurrent(&mut store, async move |accessor| { 1778 anyhow::Ok(func.call_concurrent(accessor, ()).await?.0) 1779 }) 1780 .await?? 1781 .0 1782 } else { 1783 func.call_async(&mut store, ()).await?.0 1784 } 1785 }; 1786 1787 assert_eq!(expected.0, actual.0); 1788 assert_eq!(expected.1, actual.1); 1789 assert!(expected.2.is_nan()); 1790 assert!(actual.2.is_nan()); 1791 assert_eq!(expected.3, actual.3); 1792 assert_eq!(expected.4, actual.4); 1793 assert_eq!(expected.5, actual.5); 1794 assert_eq!(expected.6, actual.6); 1795 assert_eq!(expected.7, actual.7); 1796 assert_eq!(expected.8, actual.8); 1797 assert_eq!(expected.9, actual.9); 1798 assert_eq!(expected.10, actual.10); 1799 assert_eq!(expected.11, actual.11); 1800 assert_eq!(expected.12, actual.12); 1801 1802 Ok(()) 1803 } 1804 1805 #[test] 1806 fn some_traps() -> Result<()> { 1807 let middle_of_memory = (i32::MAX / 2) & (!0xff); 1808 let component = format!( 1809 r#"(component 1810 (core module $m 1811 (memory (export "memory") 1) 1812 (func (export "take-many") (param i32)) 1813 (func (export "take-list") (param i32 i32)) 1814 1815 (func (export "realloc") (param i32 i32 i32 i32) (result i32) 1816 unreachable) 1817 ) 1818 (core instance $i (instantiate $m)) 1819 1820 (func (export "take-list-unreachable") (param "a" (list u8)) 1821 (canon lift (core func $i "take-list") (memory $i "memory") (realloc (func $i "realloc"))) 1822 ) 1823 (func (export "take-string-unreachable") (param "a" string) 1824 (canon lift (core func $i "take-list") (memory $i "memory") (realloc (func $i "realloc"))) 1825 ) 1826 1827 (type $t (func 1828 (param "s1" string) 1829 (param "s2" string) 1830 (param "s3" string) 1831 (param "s4" string) 1832 (param "s5" string) 1833 (param "s6" string) 1834 (param "s7" string) 1835 (param "s8" string) 1836 (param "s9" string) 1837 (param "s10" string) 1838 )) 1839 (func (export "take-many-unreachable") (type $t) 1840 (canon lift (core func $i "take-many") (memory $i "memory") (realloc (func $i "realloc"))) 1841 ) 1842 1843 (core module $m2 1844 (memory (export "memory") 1) 1845 (func (export "take-many") (param i32)) 1846 (func (export "take-list") (param i32 i32)) 1847 1848 (func (export "realloc") (param i32 i32 i32 i32) (result i32) 1849 i32.const {middle_of_memory}) 1850 ) 1851 (core instance $i2 (instantiate $m2)) 1852 1853 (func (export "take-list-base-oob") (param "a" (list u8)) 1854 (canon lift (core func $i2 "take-list") (memory $i2 "memory") (realloc (func $i2 "realloc"))) 1855 ) 1856 (func (export "take-string-base-oob") (param "a" string) 1857 (canon lift (core func $i2 "take-list") (memory $i2 "memory") (realloc (func $i2 "realloc"))) 1858 ) 1859 (func (export "take-many-base-oob") (type $t) 1860 (canon lift (core func $i2 "take-many") (memory $i2 "memory") (realloc (func $i2 "realloc"))) 1861 ) 1862 1863 (core module $m3 1864 (memory (export "memory") 1) 1865 (func (export "take-many") (param i32)) 1866 (func (export "take-list") (param i32 i32)) 1867 1868 (func (export "realloc") (param i32 i32 i32 i32) (result i32) 1869 i32.const 65532) 1870 ) 1871 (core instance $i3 (instantiate $m3)) 1872 1873 (func (export "take-list-end-oob") (param "a" (list u8)) 1874 (canon lift (core func $i3 "take-list") (memory $i3 "memory") (realloc (func $i3 "realloc"))) 1875 ) 1876 (func (export "take-string-end-oob") (param "a" string) 1877 (canon lift (core func $i3 "take-list") (memory $i3 "memory") (realloc (func $i3 "realloc"))) 1878 ) 1879 (func (export "take-many-end-oob") (type $t) 1880 (canon lift (core func $i3 "take-many") (memory $i3 "memory") (realloc (func $i3 "realloc"))) 1881 ) 1882 1883 (core module $m4 1884 (memory (export "memory") 1) 1885 (func (export "take-many") (param i32)) 1886 1887 (global $cnt (mut i32) (i32.const 0)) 1888 (func (export "realloc") (param i32 i32 i32 i32) (result i32) 1889 global.get $cnt 1890 if (result i32) 1891 i32.const 100000 1892 else 1893 i32.const 1 1894 global.set $cnt 1895 i32.const 0 1896 end 1897 ) 1898 ) 1899 (core instance $i4 (instantiate $m4)) 1900 1901 (func (export "take-many-second-oob") (type $t) 1902 (canon lift (core func $i4 "take-many") (memory $i4 "memory") (realloc (func $i4 "realloc"))) 1903 ) 1904 )"# 1905 ); 1906 1907 let engine = super::engine(); 1908 let component = Component::new(&engine, component)?; 1909 let mut store = Store::new(&engine, ()); 1910 let instance = |store: &mut Store<()>| Linker::new(&engine).instantiate(store, &component); 1911 1912 // This should fail when calling the allocator function for the argument 1913 let err = instance(&mut store)? 1914 .get_typed_func::<(&[u8],), ()>(&mut store, "take-list-unreachable")? 1915 .call(&mut store, (&[],)) 1916 .unwrap_err() 1917 .downcast::<Trap>()?; 1918 assert_eq!(err, Trap::UnreachableCodeReached); 1919 1920 // This should fail when calling the allocator function for the argument 1921 let err = instance(&mut store)? 1922 .get_typed_func::<(&str,), ()>(&mut store, "take-string-unreachable")? 1923 .call(&mut store, ("",)) 1924 .unwrap_err() 1925 .downcast::<Trap>()?; 1926 assert_eq!(err, Trap::UnreachableCodeReached); 1927 1928 // This should fail when calling the allocator function for the space 1929 // to store the arguments (before arguments are even lowered) 1930 let err = instance(&mut store)? 1931 .get_typed_func::<(&str, &str, &str, &str, &str, &str, &str, &str, &str, &str), ()>( 1932 &mut store, 1933 "take-many-unreachable", 1934 )? 1935 .call(&mut store, ("", "", "", "", "", "", "", "", "", "")) 1936 .unwrap_err() 1937 .downcast::<Trap>()?; 1938 assert_eq!(err, Trap::UnreachableCodeReached); 1939 1940 // Assert that when the base pointer returned by malloc is out of bounds 1941 // that errors are reported as such. Both empty and lists with contents 1942 // should all be invalid here. 1943 // 1944 // FIXME(WebAssembly/component-model#32) confirm the semantics here are 1945 // what's desired. 1946 #[track_caller] 1947 fn assert_oob(err: &anyhow::Error) { 1948 assert!( 1949 err.to_string() 1950 .contains("realloc return: beyond end of memory"), 1951 "{err:?}", 1952 ); 1953 } 1954 let err = instance(&mut store)? 1955 .get_typed_func::<(&[u8],), ()>(&mut store, "take-list-base-oob")? 1956 .call(&mut store, (&[],)) 1957 .unwrap_err(); 1958 assert_oob(&err); 1959 let err = instance(&mut store)? 1960 .get_typed_func::<(&[u8],), ()>(&mut store, "take-list-base-oob")? 1961 .call(&mut store, (&[1],)) 1962 .unwrap_err(); 1963 assert_oob(&err); 1964 let err = instance(&mut store)? 1965 .get_typed_func::<(&str,), ()>(&mut store, "take-string-base-oob")? 1966 .call(&mut store, ("",)) 1967 .unwrap_err(); 1968 assert_oob(&err); 1969 let err = instance(&mut store)? 1970 .get_typed_func::<(&str,), ()>(&mut store, "take-string-base-oob")? 1971 .call(&mut store, ("x",)) 1972 .unwrap_err(); 1973 assert_oob(&err); 1974 let err = instance(&mut store)? 1975 .get_typed_func::<(&str, &str, &str, &str, &str, &str, &str, &str, &str, &str), ()>( 1976 &mut store, 1977 "take-many-base-oob", 1978 )? 1979 .call(&mut store, ("", "", "", "", "", "", "", "", "", "")) 1980 .unwrap_err(); 1981 assert_oob(&err); 1982 1983 // Test here that when the returned pointer from malloc is one byte from the 1984 // end of memory that empty things are fine, but larger things are not. 1985 1986 instance(&mut store)? 1987 .get_typed_func::<(&[u8],), ()>(&mut store, "take-list-end-oob")? 1988 .call_and_post_return(&mut store, (&[],))?; 1989 instance(&mut store)? 1990 .get_typed_func::<(&[u8],), ()>(&mut store, "take-list-end-oob")? 1991 .call_and_post_return(&mut store, (&[1, 2, 3, 4],))?; 1992 let err = instance(&mut store)? 1993 .get_typed_func::<(&[u8],), ()>(&mut store, "take-list-end-oob")? 1994 .call(&mut store, (&[1, 2, 3, 4, 5],)) 1995 .unwrap_err(); 1996 assert_oob(&err); 1997 instance(&mut store)? 1998 .get_typed_func::<(&str,), ()>(&mut store, "take-string-end-oob")? 1999 .call_and_post_return(&mut store, ("",))?; 2000 instance(&mut store)? 2001 .get_typed_func::<(&str,), ()>(&mut store, "take-string-end-oob")? 2002 .call_and_post_return(&mut store, ("abcd",))?; 2003 let err = instance(&mut store)? 2004 .get_typed_func::<(&str,), ()>(&mut store, "take-string-end-oob")? 2005 .call(&mut store, ("abcde",)) 2006 .unwrap_err(); 2007 assert_oob(&err); 2008 let err = instance(&mut store)? 2009 .get_typed_func::<(&str, &str, &str, &str, &str, &str, &str, &str, &str, &str), ()>( 2010 &mut store, 2011 "take-many-end-oob", 2012 )? 2013 .call(&mut store, ("", "", "", "", "", "", "", "", "", "")) 2014 .unwrap_err(); 2015 assert_oob(&err); 2016 2017 // For this function the first allocation, the space to store all the 2018 // arguments, is in-bounds but then all further allocations, such as for 2019 // each individual string, are all out of bounds. 2020 let err = instance(&mut store)? 2021 .get_typed_func::<(&str, &str, &str, &str, &str, &str, &str, &str, &str, &str), ()>( 2022 &mut store, 2023 "take-many-second-oob", 2024 )? 2025 .call(&mut store, ("", "", "", "", "", "", "", "", "", "")) 2026 .unwrap_err(); 2027 assert_oob(&err); 2028 let err = instance(&mut store)? 2029 .get_typed_func::<(&str, &str, &str, &str, &str, &str, &str, &str, &str, &str), ()>( 2030 &mut store, 2031 "take-many-second-oob", 2032 )? 2033 .call(&mut store, ("", "", "", "", "", "", "", "", "", "x")) 2034 .unwrap_err(); 2035 assert_oob(&err); 2036 Ok(()) 2037 } 2038 2039 #[test] 2040 fn char_bool_memory() -> Result<()> { 2041 let component = format!( 2042 r#"(component 2043 (core module $m 2044 (memory (export "memory") 1) 2045 (func (export "ret-tuple") (param i32 i32) (result i32) 2046 (local $base i32) 2047 2048 ;; Allocate space for the return 2049 (local.set $base 2050 (call $realloc 2051 (i32.const 0) 2052 (i32.const 0) 2053 (i32.const 4) 2054 (i32.const 8))) 2055 2056 ;; store the boolean 2057 (i32.store offset=0 2058 (local.get $base) 2059 (local.get 0)) 2060 2061 ;; store the char 2062 (i32.store offset=4 2063 (local.get $base) 2064 (local.get 1)) 2065 2066 (local.get $base) 2067 ) 2068 2069 {REALLOC_AND_FREE} 2070 ) 2071 (core instance $i (instantiate $m)) 2072 2073 (func (export "ret-tuple") (param "a" u32) (param "b" u32) (result (tuple bool char)) 2074 (canon lift (core func $i "ret-tuple") 2075 (memory $i "memory") 2076 (realloc (func $i "realloc"))) 2077 ) 2078 )"# 2079 ); 2080 2081 let engine = super::engine(); 2082 let component = Component::new(&engine, component)?; 2083 let mut store = Store::new(&engine, ()); 2084 let instance = Linker::new(&engine).instantiate(&mut store, &component)?; 2085 let func = instance.get_typed_func::<(u32, u32), ((bool, char),)>(&mut store, "ret-tuple")?; 2086 2087 let (ret,) = func.call(&mut store, (0, 'a' as u32))?; 2088 assert_eq!(ret, (false, 'a')); 2089 func.post_return(&mut store)?; 2090 2091 let (ret,) = func.call(&mut store, (1, '' as u32))?; 2092 assert_eq!(ret, (true, '')); 2093 func.post_return(&mut store)?; 2094 2095 let (ret,) = func.call(&mut store, (2, 'a' as u32))?; 2096 assert_eq!(ret, (true, 'a')); 2097 func.post_return(&mut store)?; 2098 2099 assert!(func.call(&mut store, (0, 0xd800)).is_err()); 2100 2101 Ok(()) 2102 } 2103 2104 #[test] 2105 fn string_list_oob() -> Result<()> { 2106 let component = format!( 2107 r#"(component 2108 (core module $m 2109 (memory (export "memory") 1) 2110 (func (export "ret-list") (result i32) 2111 (local $base i32) 2112 2113 ;; Allocate space for the return 2114 (local.set $base 2115 (call $realloc 2116 (i32.const 0) 2117 (i32.const 0) 2118 (i32.const 4) 2119 (i32.const 8))) 2120 2121 (i32.store offset=0 2122 (local.get $base) 2123 (i32.const 100000)) 2124 (i32.store offset=4 2125 (local.get $base) 2126 (i32.const 1)) 2127 2128 (local.get $base) 2129 ) 2130 2131 {REALLOC_AND_FREE} 2132 ) 2133 (core instance $i (instantiate $m)) 2134 2135 (func (export "ret-list-u8") (result (list u8)) 2136 (canon lift (core func $i "ret-list") 2137 (memory $i "memory") 2138 (realloc (func $i "realloc")) 2139 ) 2140 ) 2141 (func (export "ret-string") (result string) 2142 (canon lift (core func $i "ret-list") 2143 (memory $i "memory") 2144 (realloc (func $i "realloc")) 2145 ) 2146 ) 2147 )"# 2148 ); 2149 2150 let engine = super::engine(); 2151 let component = Component::new(&engine, component)?; 2152 let mut store = Store::new(&engine, ()); 2153 let ret_list_u8 = Linker::new(&engine) 2154 .instantiate(&mut store, &component)? 2155 .get_typed_func::<(), (WasmList<u8>,)>(&mut store, "ret-list-u8")?; 2156 let ret_string = Linker::new(&engine) 2157 .instantiate(&mut store, &component)? 2158 .get_typed_func::<(), (WasmStr,)>(&mut store, "ret-string")?; 2159 2160 let err = ret_list_u8.call(&mut store, ()).err().unwrap(); 2161 assert!(err.to_string().contains("out of bounds"), "{}", err); 2162 2163 let err = ret_string.call(&mut store, ()).err().unwrap(); 2164 assert!(err.to_string().contains("out of bounds"), "{}", err); 2165 2166 Ok(()) 2167 } 2168 2169 #[test] 2170 fn tuples() -> Result<()> { 2171 let component = format!( 2172 r#"(component 2173 (core module $m 2174 (memory (export "memory") 1) 2175 (func (export "foo") 2176 (param i32 f64 i32) 2177 (result i32) 2178 2179 local.get 0 2180 i32.const 0 2181 i32.ne 2182 if unreachable end 2183 2184 local.get 1 2185 f64.const 1 2186 f64.ne 2187 if unreachable end 2188 2189 local.get 2 2190 i32.const 2 2191 i32.ne 2192 if unreachable end 2193 2194 i32.const 3 2195 ) 2196 ) 2197 (core instance $i (instantiate $m)) 2198 2199 (func (export "foo") 2200 (param "a" (tuple s32 float64)) 2201 (param "b" (tuple s8)) 2202 (result (tuple u16)) 2203 (canon lift (core func $i "foo")) 2204 ) 2205 )"# 2206 ); 2207 2208 let engine = super::engine(); 2209 let component = Component::new(&engine, component)?; 2210 let mut store = Store::new(&engine, ()); 2211 let instance = Linker::new(&engine).instantiate(&mut store, &component)?; 2212 let foo = instance.get_typed_func::<((i32, f64), (i8,)), ((u16,),)>(&mut store, "foo")?; 2213 assert_eq!(foo.call(&mut store, ((0, 1.0), (2,)))?, ((3,),)); 2214 2215 Ok(()) 2216 } 2217 2218 #[test] 2219 fn option() -> Result<()> { 2220 let component = format!( 2221 r#"(component 2222 (core module $m 2223 (memory (export "memory") 1) 2224 (func (export "pass1") (param i32 i32) (result i32) 2225 (local $base i32) 2226 (local.set $base 2227 (call $realloc 2228 (i32.const 0) 2229 (i32.const 0) 2230 (i32.const 4) 2231 (i32.const 8))) 2232 2233 (i32.store offset=0 2234 (local.get $base) 2235 (local.get 0)) 2236 (i32.store offset=4 2237 (local.get $base) 2238 (local.get 1)) 2239 2240 (local.get $base) 2241 ) 2242 (func (export "pass2") (param i32 i32 i32) (result i32) 2243 (local $base i32) 2244 (local.set $base 2245 (call $realloc 2246 (i32.const 0) 2247 (i32.const 0) 2248 (i32.const 4) 2249 (i32.const 12))) 2250 2251 (i32.store offset=0 2252 (local.get $base) 2253 (local.get 0)) 2254 (i32.store offset=4 2255 (local.get $base) 2256 (local.get 1)) 2257 (i32.store offset=8 2258 (local.get $base) 2259 (local.get 2)) 2260 2261 (local.get $base) 2262 ) 2263 2264 {REALLOC_AND_FREE} 2265 ) 2266 (core instance $i (instantiate $m)) 2267 2268 (func (export "option-u8-to-tuple") (param "a" (option u8)) (result (tuple u32 u32)) 2269 (canon lift (core func $i "pass1") (memory $i "memory")) 2270 ) 2271 (func (export "option-u32-to-tuple") (param "a" (option u32)) (result (tuple u32 u32)) 2272 (canon lift (core func $i "pass1") (memory $i "memory")) 2273 ) 2274 (func (export "option-string-to-tuple") (param "a" (option string)) (result (tuple u32 string)) 2275 (canon lift 2276 (core func $i "pass2") 2277 (memory $i "memory") 2278 (realloc (func $i "realloc")) 2279 ) 2280 ) 2281 (func (export "to-option-u8") (param "a" u32) (param "b" u32) (result (option u8)) 2282 (canon lift (core func $i "pass1") (memory $i "memory")) 2283 ) 2284 (func (export "to-option-u32") (param "a" u32) (param "b" u32) (result (option u32)) 2285 (canon lift 2286 (core func $i "pass1") 2287 (memory $i "memory") 2288 ) 2289 ) 2290 (func (export "to-option-string") (param "a" u32) (param "b" string) (result (option string)) 2291 (canon lift 2292 (core func $i "pass2") 2293 (memory $i "memory") 2294 (realloc (func $i "realloc")) 2295 ) 2296 ) 2297 )"# 2298 ); 2299 2300 let engine = super::engine(); 2301 let component = Component::new(&engine, component)?; 2302 let mut store = Store::new(&engine, ()); 2303 let linker = Linker::new(&engine); 2304 let instance = linker.instantiate(&mut store, &component)?; 2305 2306 let option_u8_to_tuple = instance 2307 .get_typed_func::<(Option<u8>,), ((u32, u32),)>(&mut store, "option-u8-to-tuple")?; 2308 assert_eq!(option_u8_to_tuple.call(&mut store, (None,))?, ((0, 0),)); 2309 option_u8_to_tuple.post_return(&mut store)?; 2310 assert_eq!(option_u8_to_tuple.call(&mut store, (Some(0),))?, ((1, 0),)); 2311 option_u8_to_tuple.post_return(&mut store)?; 2312 assert_eq!( 2313 option_u8_to_tuple.call(&mut store, (Some(100),))?, 2314 ((1, 100),) 2315 ); 2316 option_u8_to_tuple.post_return(&mut store)?; 2317 2318 let option_u32_to_tuple = instance 2319 .get_typed_func::<(Option<u32>,), ((u32, u32),)>(&mut store, "option-u32-to-tuple")?; 2320 assert_eq!(option_u32_to_tuple.call(&mut store, (None,))?, ((0, 0),)); 2321 option_u32_to_tuple.post_return(&mut store)?; 2322 assert_eq!(option_u32_to_tuple.call(&mut store, (Some(0),))?, ((1, 0),)); 2323 option_u32_to_tuple.post_return(&mut store)?; 2324 assert_eq!( 2325 option_u32_to_tuple.call(&mut store, (Some(100),))?, 2326 ((1, 100),) 2327 ); 2328 option_u32_to_tuple.post_return(&mut store)?; 2329 2330 let option_string_to_tuple = instance.get_typed_func::<(Option<&str>,), ((u32, WasmStr),)>( 2331 &mut store, 2332 "option-string-to-tuple", 2333 )?; 2334 let ((a, b),) = option_string_to_tuple.call(&mut store, (None,))?; 2335 assert_eq!(a, 0); 2336 assert_eq!(b.to_str(&store)?, ""); 2337 option_string_to_tuple.post_return(&mut store)?; 2338 let ((a, b),) = option_string_to_tuple.call(&mut store, (Some(""),))?; 2339 assert_eq!(a, 1); 2340 assert_eq!(b.to_str(&store)?, ""); 2341 option_string_to_tuple.post_return(&mut store)?; 2342 let ((a, b),) = option_string_to_tuple.call(&mut store, (Some("hello"),))?; 2343 assert_eq!(a, 1); 2344 assert_eq!(b.to_str(&store)?, "hello"); 2345 option_string_to_tuple.post_return(&mut store)?; 2346 2347 let instance = linker.instantiate(&mut store, &component)?; 2348 let to_option_u8 = 2349 instance.get_typed_func::<(u32, u32), (Option<u8>,)>(&mut store, "to-option-u8")?; 2350 assert_eq!(to_option_u8.call(&mut store, (0x00_00, 0))?, (None,)); 2351 to_option_u8.post_return(&mut store)?; 2352 assert_eq!(to_option_u8.call(&mut store, (0x00_01, 0))?, (Some(0),)); 2353 to_option_u8.post_return(&mut store)?; 2354 assert_eq!(to_option_u8.call(&mut store, (0xfd_01, 0))?, (Some(0xfd),)); 2355 to_option_u8.post_return(&mut store)?; 2356 assert!(to_option_u8.call(&mut store, (0x00_02, 0)).is_err()); 2357 2358 let instance = linker.instantiate(&mut store, &component)?; 2359 let to_option_u32 = 2360 instance.get_typed_func::<(u32, u32), (Option<u32>,)>(&mut store, "to-option-u32")?; 2361 assert_eq!(to_option_u32.call(&mut store, (0, 0))?, (None,)); 2362 to_option_u32.post_return(&mut store)?; 2363 assert_eq!(to_option_u32.call(&mut store, (1, 0))?, (Some(0),)); 2364 to_option_u32.post_return(&mut store)?; 2365 assert_eq!( 2366 to_option_u32.call(&mut store, (1, 0x1234fead))?, 2367 (Some(0x1234fead),) 2368 ); 2369 to_option_u32.post_return(&mut store)?; 2370 assert!(to_option_u32.call(&mut store, (2, 0)).is_err()); 2371 2372 let instance = linker.instantiate(&mut store, &component)?; 2373 let to_option_string = instance 2374 .get_typed_func::<(u32, &str), (Option<WasmStr>,)>(&mut store, "to-option-string")?; 2375 let ret = to_option_string.call(&mut store, (0, ""))?.0; 2376 assert!(ret.is_none()); 2377 to_option_string.post_return(&mut store)?; 2378 let ret = to_option_string.call(&mut store, (1, ""))?.0; 2379 assert_eq!(ret.unwrap().to_str(&store)?, ""); 2380 to_option_string.post_return(&mut store)?; 2381 let ret = to_option_string.call(&mut store, (1, "cheesecake"))?.0; 2382 assert_eq!(ret.unwrap().to_str(&store)?, "cheesecake"); 2383 to_option_string.post_return(&mut store)?; 2384 assert!(to_option_string.call(&mut store, (2, "")).is_err()); 2385 2386 Ok(()) 2387 } 2388 2389 #[test] 2390 fn expected() -> Result<()> { 2391 let component = format!( 2392 r#"(component 2393 (core module $m 2394 (memory (export "memory") 1) 2395 (func (export "pass0") (param i32) (result i32) 2396 local.get 0 2397 ) 2398 (func (export "pass1") (param i32 i32) (result i32) 2399 (local $base i32) 2400 (local.set $base 2401 (call $realloc 2402 (i32.const 0) 2403 (i32.const 0) 2404 (i32.const 4) 2405 (i32.const 8))) 2406 2407 (i32.store offset=0 2408 (local.get $base) 2409 (local.get 0)) 2410 (i32.store offset=4 2411 (local.get $base) 2412 (local.get 1)) 2413 2414 (local.get $base) 2415 ) 2416 (func (export "pass2") (param i32 i32 i32) (result i32) 2417 (local $base i32) 2418 (local.set $base 2419 (call $realloc 2420 (i32.const 0) 2421 (i32.const 0) 2422 (i32.const 4) 2423 (i32.const 12))) 2424 2425 (i32.store offset=0 2426 (local.get $base) 2427 (local.get 0)) 2428 (i32.store offset=4 2429 (local.get $base) 2430 (local.get 1)) 2431 (i32.store offset=8 2432 (local.get $base) 2433 (local.get 2)) 2434 2435 (local.get $base) 2436 ) 2437 2438 {REALLOC_AND_FREE} 2439 ) 2440 (core instance $i (instantiate $m)) 2441 2442 (func (export "take-expected-unit") (param "a" (result)) (result u32) 2443 (canon lift (core func $i "pass0")) 2444 ) 2445 (func (export "take-expected-u8-f32") (param "a" (result u8 (error float32))) (result (tuple u32 u32)) 2446 (canon lift (core func $i "pass1") (memory $i "memory")) 2447 ) 2448 (type $list (list u8)) 2449 (func (export "take-expected-string") (param "a" (result string (error $list))) (result (tuple u32 string)) 2450 (canon lift 2451 (core func $i "pass2") 2452 (memory $i "memory") 2453 (realloc (func $i "realloc")) 2454 ) 2455 ) 2456 (func (export "to-expected-unit") (param "a" u32) (result (result)) 2457 (canon lift (core func $i "pass0")) 2458 ) 2459 (func (export "to-expected-s16-f32") (param "a" u32) (param "b" u32) (result (result s16 (error float32))) 2460 (canon lift 2461 (core func $i "pass1") 2462 (memory $i "memory") 2463 (realloc (func $i "realloc")) 2464 ) 2465 ) 2466 )"# 2467 ); 2468 2469 let engine = super::engine(); 2470 let component = Component::new(&engine, component)?; 2471 let mut store = Store::new(&engine, ()); 2472 let linker = Linker::new(&engine); 2473 let instance = linker.instantiate(&mut store, &component)?; 2474 let take_expected_unit = 2475 instance.get_typed_func::<(Result<(), ()>,), (u32,)>(&mut store, "take-expected-unit")?; 2476 assert_eq!(take_expected_unit.call(&mut store, (Ok(()),))?, (0,)); 2477 take_expected_unit.post_return(&mut store)?; 2478 assert_eq!(take_expected_unit.call(&mut store, (Err(()),))?, (1,)); 2479 take_expected_unit.post_return(&mut store)?; 2480 2481 let take_expected_u8_f32 = instance 2482 .get_typed_func::<(Result<u8, f32>,), ((u32, u32),)>(&mut store, "take-expected-u8-f32")?; 2483 assert_eq!(take_expected_u8_f32.call(&mut store, (Ok(1),))?, ((0, 1),)); 2484 take_expected_u8_f32.post_return(&mut store)?; 2485 assert_eq!( 2486 take_expected_u8_f32.call(&mut store, (Err(2.0),))?, 2487 ((1, 2.0f32.to_bits()),) 2488 ); 2489 take_expected_u8_f32.post_return(&mut store)?; 2490 2491 let take_expected_string = instance 2492 .get_typed_func::<(Result<&str, &[u8]>,), ((u32, WasmStr),)>( 2493 &mut store, 2494 "take-expected-string", 2495 )?; 2496 let ((a, b),) = take_expected_string.call(&mut store, (Ok("hello"),))?; 2497 assert_eq!(a, 0); 2498 assert_eq!(b.to_str(&store)?, "hello"); 2499 take_expected_string.post_return(&mut store)?; 2500 let ((a, b),) = take_expected_string.call(&mut store, (Err(b"goodbye"),))?; 2501 assert_eq!(a, 1); 2502 assert_eq!(b.to_str(&store)?, "goodbye"); 2503 take_expected_string.post_return(&mut store)?; 2504 2505 let instance = linker.instantiate(&mut store, &component)?; 2506 let to_expected_unit = 2507 instance.get_typed_func::<(u32,), (Result<(), ()>,)>(&mut store, "to-expected-unit")?; 2508 assert_eq!(to_expected_unit.call(&mut store, (0,))?, (Ok(()),)); 2509 to_expected_unit.post_return(&mut store)?; 2510 assert_eq!(to_expected_unit.call(&mut store, (1,))?, (Err(()),)); 2511 to_expected_unit.post_return(&mut store)?; 2512 let err = to_expected_unit.call(&mut store, (2,)).unwrap_err(); 2513 assert!(err.to_string().contains("invalid expected"), "{}", err); 2514 2515 let instance = linker.instantiate(&mut store, &component)?; 2516 let to_expected_s16_f32 = instance 2517 .get_typed_func::<(u32, u32), (Result<i16, f32>,)>(&mut store, "to-expected-s16-f32")?; 2518 assert_eq!(to_expected_s16_f32.call(&mut store, (0, 0))?, (Ok(0),)); 2519 to_expected_s16_f32.post_return(&mut store)?; 2520 assert_eq!(to_expected_s16_f32.call(&mut store, (0, 100))?, (Ok(100),)); 2521 to_expected_s16_f32.post_return(&mut store)?; 2522 assert_eq!( 2523 to_expected_s16_f32.call(&mut store, (1, 1.0f32.to_bits()))?, 2524 (Err(1.0),) 2525 ); 2526 to_expected_s16_f32.post_return(&mut store)?; 2527 let ret = to_expected_s16_f32 2528 .call(&mut store, (1, CANON_32BIT_NAN | 1))? 2529 .0; 2530 assert_eq!(ret.unwrap_err().to_bits(), CANON_32BIT_NAN | 1); 2531 to_expected_s16_f32.post_return(&mut store)?; 2532 assert!(to_expected_s16_f32.call(&mut store, (2, 0)).is_err()); 2533 2534 Ok(()) 2535 } 2536 2537 #[test] 2538 fn fancy_list() -> Result<()> { 2539 let component = format!( 2540 r#"(component 2541 (core module $m 2542 (memory (export "memory") 1) 2543 (func (export "take") (param i32 i32) (result i32) 2544 (local $base i32) 2545 (local.set $base 2546 (call $realloc 2547 (i32.const 0) 2548 (i32.const 0) 2549 (i32.const 4) 2550 (i32.const 16))) 2551 2552 (i32.store offset=0 2553 (local.get $base) 2554 (local.get 0)) 2555 (i32.store offset=4 2556 (local.get $base) 2557 (local.get 1)) 2558 (i32.store offset=8 2559 (local.get $base) 2560 (i32.const 0)) 2561 (i32.store offset=12 2562 (local.get $base) 2563 (i32.mul 2564 (memory.size) 2565 (i32.const 65536))) 2566 2567 (local.get $base) 2568 ) 2569 2570 {REALLOC_AND_FREE} 2571 ) 2572 (core instance $i (instantiate $m)) 2573 2574 (type $a (option u8)) 2575 (type $b (result (error string))) 2576 (type $input (list (tuple $a $b))) 2577 (func (export "take") 2578 (param "a" $input) 2579 (result (tuple u32 u32 (list u8))) 2580 (canon lift 2581 (core func $i "take") 2582 (memory $i "memory") 2583 (realloc (func $i "realloc")) 2584 ) 2585 ) 2586 )"# 2587 ); 2588 2589 let engine = super::engine(); 2590 let component = Component::new(&engine, component)?; 2591 let mut store = Store::new(&engine, ()); 2592 let instance = Linker::new(&engine).instantiate(&mut store, &component)?; 2593 2594 let func = instance 2595 .get_typed_func::<(&[(Option<u8>, Result<(), &str>)],), ((u32, u32, WasmList<u8>),)>( 2596 &mut store, "take", 2597 )?; 2598 2599 let input = [ 2600 (None, Ok(())), 2601 (Some(2), Err("hello there")), 2602 (Some(200), Err("general kenobi")), 2603 ]; 2604 let ((ptr, len, list),) = func.call(&mut store, (&input,))?; 2605 let memory = list.as_le_slice(&store); 2606 let ptr = usize::try_from(ptr).unwrap(); 2607 let len = usize::try_from(len).unwrap(); 2608 let mut array = &memory[ptr..][..len * 16]; 2609 2610 for (a, b) in input.iter() { 2611 match a { 2612 Some(val) => { 2613 assert_eq!(*array.take_n::<2>(), [1, *val]); 2614 } 2615 None => { 2616 assert_eq!(*array.take_n::<1>(), [0]); 2617 array.skip::<1>(); 2618 } 2619 } 2620 array.skip::<2>(); 2621 match b { 2622 Ok(()) => { 2623 assert_eq!(*array.take_n::<1>(), [0]); 2624 array.skip::<11>(); 2625 } 2626 Err(s) => { 2627 assert_eq!(*array.take_n::<1>(), [1]); 2628 array.skip::<3>(); 2629 assert_eq!(array.ptr_len(memory, 1), s.as_bytes()); 2630 } 2631 } 2632 } 2633 assert!(array.is_empty()); 2634 2635 Ok(()) 2636 } 2637 2638 trait SliceExt<'a> { 2639 fn take_n<const N: usize>(&mut self) -> &'a [u8; N]; 2640 2641 fn skip<const N: usize>(&mut self) { 2642 self.take_n::<N>(); 2643 } 2644 2645 fn ptr_len<'b>(&mut self, all_memory: &'b [u8], size: usize) -> &'b [u8] { 2646 let ptr = u32::from_le_bytes(*self.take_n::<4>()); 2647 let len = u32::from_le_bytes(*self.take_n::<4>()); 2648 let ptr = usize::try_from(ptr).unwrap(); 2649 let len = usize::try_from(len).unwrap(); 2650 &all_memory[ptr..][..len * size] 2651 } 2652 } 2653 2654 impl<'a> SliceExt<'a> for &'a [u8] { 2655 fn take_n<const N: usize>(&mut self) -> &'a [u8; N] { 2656 let (a, b) = self.split_at(N); 2657 *self = b; 2658 a.try_into().unwrap() 2659 } 2660 } 2661 2662 #[test] 2663 fn invalid_alignment() -> Result<()> { 2664 let component = format!( 2665 r#"(component 2666 (core module $m 2667 (memory (export "memory") 1) 2668 (func (export "realloc") (param i32 i32 i32 i32) (result i32) 2669 i32.const 1) 2670 2671 (func (export "take-i32") (param i32)) 2672 (func (export "ret-1") (result i32) i32.const 1) 2673 (func (export "ret-unaligned-list") (result i32) 2674 (i32.store offset=0 (i32.const 8) (i32.const 1)) 2675 (i32.store offset=4 (i32.const 8) (i32.const 1)) 2676 i32.const 8) 2677 ) 2678 (core instance $i (instantiate $m)) 2679 2680 (func (export "many-params") 2681 (param "s1" string) (param "s2" string) (param "s3" string) (param "s4" string) 2682 (param "s5" string) (param "s6" string) (param "s7" string) (param "s8" string) 2683 (param "s9" string) (param "s10" string) (param "s11" string) (param "s12" string) 2684 (canon lift 2685 (core func $i "take-i32") 2686 (memory $i "memory") 2687 (realloc (func $i "realloc")) 2688 ) 2689 ) 2690 (func (export "string-ret") (result string) 2691 (canon lift 2692 (core func $i "ret-1") 2693 (memory $i "memory") 2694 (realloc (func $i "realloc")) 2695 ) 2696 ) 2697 (func (export "list-u32-ret") (result (list u32)) 2698 (canon lift 2699 (core func $i "ret-unaligned-list") 2700 (memory $i "memory") 2701 (realloc (func $i "realloc")) 2702 ) 2703 ) 2704 )"# 2705 ); 2706 2707 let engine = super::engine(); 2708 let component = Component::new(&engine, component)?; 2709 let mut store = Store::new(&engine, ()); 2710 let instance = |store: &mut Store<()>| Linker::new(&engine).instantiate(store, &component); 2711 2712 let err = instance(&mut store)? 2713 .get_typed_func::<( 2714 &str, 2715 &str, 2716 &str, 2717 &str, 2718 &str, 2719 &str, 2720 &str, 2721 &str, 2722 &str, 2723 &str, 2724 &str, 2725 &str, 2726 ), ()>(&mut store, "many-params")? 2727 .call(&mut store, ("", "", "", "", "", "", "", "", "", "", "", "")) 2728 .unwrap_err(); 2729 assert!( 2730 err.to_string() 2731 .contains("realloc return: result not aligned"), 2732 "{}", 2733 err 2734 ); 2735 2736 let err = instance(&mut store)? 2737 .get_typed_func::<(), (WasmStr,)>(&mut store, "string-ret")? 2738 .call(&mut store, ()) 2739 .err() 2740 .unwrap(); 2741 assert!( 2742 err.to_string().contains("return pointer not aligned"), 2743 "{}", 2744 err 2745 ); 2746 2747 let err = instance(&mut store)? 2748 .get_typed_func::<(), (WasmList<u32>,)>(&mut store, "list-u32-ret")? 2749 .call(&mut store, ()) 2750 .err() 2751 .unwrap(); 2752 assert!( 2753 err.to_string().contains("list pointer is not aligned"), 2754 "{}", 2755 err 2756 ); 2757 2758 Ok(()) 2759 } 2760 2761 #[test] 2762 fn drop_component_still_works() -> Result<()> { 2763 let component = r#" 2764 (component 2765 (import "f" (func $f)) 2766 2767 (core func $f_lower 2768 (canon lower (func $f)) 2769 ) 2770 (core module $m 2771 (import "" "" (func $f)) 2772 2773 (func $f2 2774 call $f 2775 call $f 2776 ) 2777 2778 (export "f" (func $f2)) 2779 ) 2780 (core instance $i (instantiate $m 2781 (with "" (instance 2782 (export "" (func $f_lower)) 2783 )) 2784 )) 2785 (func (export "g") 2786 (canon lift 2787 (core func $i "f") 2788 ) 2789 ) 2790 ) 2791 "#; 2792 2793 let (mut store, instance) = { 2794 let engine = super::engine(); 2795 let component = Component::new(&engine, component)?; 2796 let mut store = Store::new(&engine, 0); 2797 let mut linker = Linker::new(&engine); 2798 linker.root().func_wrap( 2799 "f", 2800 |mut store: StoreContextMut<'_, u32>, _: ()| -> Result<()> { 2801 *store.data_mut() += 1; 2802 Ok(()) 2803 }, 2804 )?; 2805 let instance = linker.instantiate(&mut store, &component)?; 2806 (store, instance) 2807 }; 2808 2809 let f = instance.get_typed_func::<(), ()>(&mut store, "g")?; 2810 assert_eq!(*store.data(), 0); 2811 f.call(&mut store, ())?; 2812 assert_eq!(*store.data(), 2); 2813 2814 Ok(()) 2815 } 2816 2817 #[test] 2818 fn raw_slice_of_various_types() -> Result<()> { 2819 let component = r#" 2820 (component 2821 (core module $m 2822 (memory (export "memory") 1) 2823 2824 (func (export "list8") (result i32) 2825 (call $setup_list (i32.const 16)) 2826 ) 2827 (func (export "list16") (result i32) 2828 (call $setup_list (i32.const 8)) 2829 ) 2830 (func (export "list32") (result i32) 2831 (call $setup_list (i32.const 4)) 2832 ) 2833 (func (export "list64") (result i32) 2834 (call $setup_list (i32.const 2)) 2835 ) 2836 2837 (func $setup_list (param i32) (result i32) 2838 (i32.store offset=0 (i32.const 100) (i32.const 8)) 2839 (i32.store offset=4 (i32.const 100) (local.get 0)) 2840 i32.const 100 2841 ) 2842 2843 (data (i32.const 8) "\00\01\02\03\04\05\06\07\08\09\0a\0b\0c\0d\0e\0f") 2844 ) 2845 (core instance $i (instantiate $m)) 2846 (func (export "list-u8") (result (list u8)) 2847 (canon lift (core func $i "list8") (memory $i "memory")) 2848 ) 2849 (func (export "list-i8") (result (list s8)) 2850 (canon lift (core func $i "list8") (memory $i "memory")) 2851 ) 2852 (func (export "list-u16") (result (list u16)) 2853 (canon lift (core func $i "list16") (memory $i "memory")) 2854 ) 2855 (func (export "list-i16") (result (list s16)) 2856 (canon lift (core func $i "list16") (memory $i "memory")) 2857 ) 2858 (func (export "list-u32") (result (list u32)) 2859 (canon lift (core func $i "list32") (memory $i "memory")) 2860 ) 2861 (func (export "list-i32") (result (list s32)) 2862 (canon lift (core func $i "list32") (memory $i "memory")) 2863 ) 2864 (func (export "list-u64") (result (list u64)) 2865 (canon lift (core func $i "list64") (memory $i "memory")) 2866 ) 2867 (func (export "list-i64") (result (list s64)) 2868 (canon lift (core func $i "list64") (memory $i "memory")) 2869 ) 2870 ) 2871 "#; 2872 2873 let (mut store, instance) = { 2874 let engine = super::engine(); 2875 let component = Component::new(&engine, component)?; 2876 let mut store = Store::new(&engine, ()); 2877 let instance = Linker::new(&engine).instantiate(&mut store, &component)?; 2878 (store, instance) 2879 }; 2880 2881 let list = instance 2882 .get_typed_func::<(), (WasmList<u8>,)>(&mut store, "list-u8")? 2883 .call_and_post_return(&mut store, ())? 2884 .0; 2885 assert_eq!( 2886 list.as_le_slice(&store), 2887 [ 2888 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 2889 0x0e, 0x0f, 2890 ] 2891 ); 2892 let list = instance 2893 .get_typed_func::<(), (WasmList<i8>,)>(&mut store, "list-i8")? 2894 .call_and_post_return(&mut store, ())? 2895 .0; 2896 assert_eq!( 2897 list.as_le_slice(&store), 2898 [ 2899 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 2900 0x0e, 0x0f, 2901 ] 2902 ); 2903 2904 let list = instance 2905 .get_typed_func::<(), (WasmList<u16>,)>(&mut store, "list-u16")? 2906 .call_and_post_return(&mut store, ())? 2907 .0; 2908 assert_eq!( 2909 list.as_le_slice(&store), 2910 [ 2911 u16::to_le(0x01_00), 2912 u16::to_le(0x03_02), 2913 u16::to_le(0x05_04), 2914 u16::to_le(0x07_06), 2915 u16::to_le(0x09_08), 2916 u16::to_le(0x0b_0a), 2917 u16::to_le(0x0d_0c), 2918 u16::to_le(0x0f_0e), 2919 ] 2920 ); 2921 let list = instance 2922 .get_typed_func::<(), (WasmList<i16>,)>(&mut store, "list-i16")? 2923 .call_and_post_return(&mut store, ())? 2924 .0; 2925 assert_eq!( 2926 list.as_le_slice(&store), 2927 [ 2928 i16::to_le(0x01_00), 2929 i16::to_le(0x03_02), 2930 i16::to_le(0x05_04), 2931 i16::to_le(0x07_06), 2932 i16::to_le(0x09_08), 2933 i16::to_le(0x0b_0a), 2934 i16::to_le(0x0d_0c), 2935 i16::to_le(0x0f_0e), 2936 ] 2937 ); 2938 let list = instance 2939 .get_typed_func::<(), (WasmList<u32>,)>(&mut store, "list-u32")? 2940 .call_and_post_return(&mut store, ())? 2941 .0; 2942 assert_eq!( 2943 list.as_le_slice(&store), 2944 [ 2945 u32::to_le(0x03_02_01_00), 2946 u32::to_le(0x07_06_05_04), 2947 u32::to_le(0x0b_0a_09_08), 2948 u32::to_le(0x0f_0e_0d_0c), 2949 ] 2950 ); 2951 let list = instance 2952 .get_typed_func::<(), (WasmList<i32>,)>(&mut store, "list-i32")? 2953 .call_and_post_return(&mut store, ())? 2954 .0; 2955 assert_eq!( 2956 list.as_le_slice(&store), 2957 [ 2958 i32::to_le(0x03_02_01_00), 2959 i32::to_le(0x07_06_05_04), 2960 i32::to_le(0x0b_0a_09_08), 2961 i32::to_le(0x0f_0e_0d_0c), 2962 ] 2963 ); 2964 let list = instance 2965 .get_typed_func::<(), (WasmList<u64>,)>(&mut store, "list-u64")? 2966 .call_and_post_return(&mut store, ())? 2967 .0; 2968 assert_eq!( 2969 list.as_le_slice(&store), 2970 [ 2971 u64::to_le(0x07_06_05_04_03_02_01_00), 2972 u64::to_le(0x0f_0e_0d_0c_0b_0a_09_08), 2973 ] 2974 ); 2975 let list = instance 2976 .get_typed_func::<(), (WasmList<i64>,)>(&mut store, "list-i64")? 2977 .call_and_post_return(&mut store, ())? 2978 .0; 2979 assert_eq!( 2980 list.as_le_slice(&store), 2981 [ 2982 i64::to_le(0x07_06_05_04_03_02_01_00), 2983 i64::to_le(0x0f_0e_0d_0c_0b_0a_09_08), 2984 ] 2985 ); 2986 2987 Ok(()) 2988 } 2989 2990 #[test] 2991 fn lower_then_lift() -> Result<()> { 2992 // First test simple integers when the import/export ABI happen to line up 2993 let component = r#" 2994 (component $c 2995 (import "f" (func $f (result u32))) 2996 2997 (core func $f_lower 2998 (canon lower (func $f)) 2999 ) 3000 (func $f2 (result s32) 3001 (canon lift (core func $f_lower)) 3002 ) 3003 (export "f2" (func $f2)) 3004 ) 3005 "#; 3006 3007 let engine = super::engine(); 3008 let component = Component::new(&engine, component)?; 3009 let mut store = Store::new(&engine, ()); 3010 let mut linker = Linker::new(&engine); 3011 linker.root().func_wrap("f", |_, _: ()| Ok((2u32,)))?; 3012 let instance = linker.instantiate(&mut store, &component)?; 3013 3014 let f = instance.get_typed_func::<(), (i32,)>(&mut store, "f2")?; 3015 assert_eq!(f.call(&mut store, ())?, (2,)); 3016 3017 // First test strings when the import/export ABI happen to line up 3018 let component = format!( 3019 r#" 3020 (component $c 3021 (import "s" (func $f (param "a" string))) 3022 3023 (core module $libc 3024 (memory (export "memory") 1) 3025 {REALLOC_AND_FREE} 3026 ) 3027 (core instance $libc (instantiate $libc)) 3028 3029 (core func $f_lower 3030 (canon lower (func $f) (memory $libc "memory")) 3031 ) 3032 (func $f2 (param "a" string) 3033 (canon lift (core func $f_lower) 3034 (memory $libc "memory") 3035 (realloc (func $libc "realloc")) 3036 ) 3037 ) 3038 (export "f" (func $f2)) 3039 ) 3040 "# 3041 ); 3042 3043 let component = Component::new(&engine, component)?; 3044 let mut store = Store::new(&engine, ()); 3045 linker 3046 .root() 3047 .func_wrap("s", |store: StoreContextMut<'_, ()>, (x,): (WasmStr,)| { 3048 assert_eq!(x.to_str(&store)?, "hello"); 3049 Ok(()) 3050 })?; 3051 let instance = linker.instantiate(&mut store, &component)?; 3052 3053 let f = instance.get_typed_func::<(&str,), ()>(&mut store, "f")?; 3054 f.call(&mut store, ("hello",))?; 3055 3056 // Next test "type punning" where return values are reinterpreted just 3057 // because the return ABI happens to line up. 3058 let component = format!( 3059 r#" 3060 (component $c 3061 (import "s2" (func $f (param "a" string) (result u32))) 3062 3063 (core module $libc 3064 (memory (export "memory") 1) 3065 {REALLOC_AND_FREE} 3066 ) 3067 (core instance $libc (instantiate $libc)) 3068 3069 (core func $f_lower 3070 (canon lower (func $f) (memory $libc "memory")) 3071 ) 3072 (func $f2 (param "a" string) (result string) 3073 (canon lift (core func $f_lower) 3074 (memory $libc "memory") 3075 (realloc (func $libc "realloc")) 3076 ) 3077 ) 3078 (export "f" (func $f2)) 3079 ) 3080 "# 3081 ); 3082 3083 let component = Component::new(&engine, component)?; 3084 let mut store = Store::new(&engine, ()); 3085 linker 3086 .root() 3087 .func_wrap("s2", |store: StoreContextMut<'_, ()>, (x,): (WasmStr,)| { 3088 assert_eq!(x.to_str(&store)?, "hello"); 3089 Ok((u32::MAX,)) 3090 })?; 3091 let instance = linker.instantiate(&mut store, &component)?; 3092 3093 let f = instance.get_typed_func::<(&str,), (WasmStr,)>(&mut store, "f")?; 3094 let err = f.call(&mut store, ("hello",)).err().unwrap(); 3095 assert!( 3096 err.to_string().contains("return pointer not aligned"), 3097 "{}", 3098 err 3099 ); 3100 3101 Ok(()) 3102 } 3103 3104 #[test] 3105 fn errors_that_poison_instance() -> Result<()> { 3106 let component = format!( 3107 r#" 3108 (component $c 3109 (core module $m1 3110 (func (export "f1") unreachable) 3111 (func (export "f2")) 3112 ) 3113 (core instance $m1 (instantiate $m1)) 3114 (func (export "f1") (canon lift (core func $m1 "f1"))) 3115 (func (export "f2") (canon lift (core func $m1 "f2"))) 3116 3117 (core module $m2 3118 (func (export "f") (param i32 i32)) 3119 (func (export "r") (param i32 i32 i32 i32) (result i32) unreachable) 3120 (memory (export "m") 1) 3121 ) 3122 (core instance $m2 (instantiate $m2)) 3123 (func (export "f3") (param "a" string) 3124 (canon lift (core func $m2 "f") (realloc (func $m2 "r")) (memory $m2 "m")) 3125 ) 3126 3127 (core module $m3 3128 (func (export "f") (result i32) i32.const 1) 3129 (memory (export "m") 1) 3130 ) 3131 (core instance $m3 (instantiate $m3)) 3132 (func (export "f4") (result string) 3133 (canon lift (core func $m3 "f") (memory $m3 "m")) 3134 ) 3135 ) 3136 "# 3137 ); 3138 3139 let engine = super::engine(); 3140 let component = Component::new(&engine, component)?; 3141 let mut store = Store::new(&engine, ()); 3142 let linker = Linker::new(&engine); 3143 let instance = linker.instantiate(&mut store, &component)?; 3144 let f1 = instance.get_typed_func::<(), ()>(&mut store, "f1")?; 3145 let f2 = instance.get_typed_func::<(), ()>(&mut store, "f2")?; 3146 assert_unreachable(f1.call(&mut store, ())); 3147 assert_poisoned(f1.call(&mut store, ())); 3148 assert_poisoned(f2.call(&mut store, ())); 3149 3150 let instance = linker.instantiate(&mut store, &component)?; 3151 let f3 = instance.get_typed_func::<(&str,), ()>(&mut store, "f3")?; 3152 assert_unreachable(f3.call(&mut store, ("x",))); 3153 assert_poisoned(f3.call(&mut store, ("x",))); 3154 3155 let instance = linker.instantiate(&mut store, &component)?; 3156 let f4 = instance.get_typed_func::<(), (WasmStr,)>(&mut store, "f4")?; 3157 assert!(f4.call(&mut store, ()).is_err()); 3158 assert_poisoned(f4.call(&mut store, ())); 3159 3160 return Ok(()); 3161 3162 #[track_caller] 3163 fn assert_unreachable<T>(err: Result<T>) { 3164 let err = match err { 3165 Ok(_) => panic!("expected an error"), 3166 Err(e) => e, 3167 }; 3168 assert_eq!( 3169 err.downcast::<Trap>().unwrap(), 3170 Trap::UnreachableCodeReached 3171 ); 3172 } 3173 3174 #[track_caller] 3175 fn assert_poisoned<T>(err: Result<T>) { 3176 let err = match err { 3177 Ok(_) => panic!("expected an error"), 3178 Err(e) => e, 3179 }; 3180 assert_eq!( 3181 err.downcast_ref::<Trap>(), 3182 Some(&Trap::CannotEnterComponent), 3183 "{err}", 3184 ); 3185 } 3186 } 3187 3188 #[test] 3189 fn run_export_with_internal_adapter() -> Result<()> { 3190 let component = r#" 3191 (component 3192 (type $t (func (param "a" u32) (result u32))) 3193 (component $a 3194 (core module $m 3195 (func (export "add-five") (param i32) (result i32) 3196 local.get 0 3197 i32.const 5 3198 i32.add) 3199 ) 3200 (core instance $m (instantiate $m)) 3201 (func (export "add-five") (type $t) (canon lift (core func $m "add-five"))) 3202 ) 3203 (component $b 3204 (import "interface-v1" (instance $i 3205 (export "add-five" (func (type $t))))) 3206 (core module $m 3207 (func $add-five (import "interface-0.1.0" "add-five") (param i32) (result i32)) 3208 (func) ;; causes index out of bounds 3209 (func (export "run") (result i32) i32.const 0 call $add-five) 3210 ) 3211 (core func $add-five (canon lower (func $i "add-five"))) 3212 (core instance $i (instantiate 0 3213 (with "interface-0.1.0" (instance 3214 (export "add-five" (func $add-five)) 3215 )) 3216 )) 3217 (func (result u32) (canon lift (core func $i "run"))) 3218 (export "run" (func 1)) 3219 ) 3220 (instance $a (instantiate $a)) 3221 (instance $b (instantiate $b (with "interface-v1" (instance $a)))) 3222 (export "run" (func $b "run")) 3223 ) 3224 "#; 3225 let engine = super::engine(); 3226 let component = Component::new(&engine, component)?; 3227 let mut store = Store::new(&engine, ()); 3228 let linker = Linker::new(&engine); 3229 let instance = linker.instantiate(&mut store, &component)?; 3230 let run = instance.get_typed_func::<(), (u32,)>(&mut store, "run")?; 3231 assert_eq!(run.call(&mut store, ())?, (5,)); 3232 Ok(()) 3233 } 3234