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