1 #![cfg(not(miri))] 2 3 use super::REALLOC_AND_FREE; 4 use anyhow::Result; 5 use std::ops::Deref; 6 use wasmtime::component::*; 7 use wasmtime::{Store, StoreContextMut, WasmBacktrace}; 8 9 #[test] 10 fn can_compile() -> Result<()> { 11 let engine = super::engine(); 12 let libc = r#" 13 (core module $libc 14 (memory (export "memory") 1) 15 (func (export "realloc") (param i32 i32 i32 i32) (result i32) 16 unreachable) 17 ) 18 (core instance $libc (instantiate $libc)) 19 "#; 20 Component::new( 21 &engine, 22 r#"(component 23 (import "a" (func $f)) 24 (core func (canon lower (func $f))) 25 )"#, 26 )?; 27 Component::new( 28 &engine, 29 format!( 30 r#"(component 31 (import "a" (func $f (param "a" string))) 32 {libc} 33 (core func (canon lower (func $f) (memory $libc "memory") (realloc (func $libc "realloc")))) 34 )"# 35 ), 36 )?; 37 Component::new( 38 &engine, 39 format!( 40 r#"(component 41 (import "f1" (func $f1 (param "a" string) (result string))) 42 {libc} 43 (core func (canon lower (func $f1) (memory $libc "memory") (realloc (func $libc "realloc")))) 44 45 (import "f2" (func $f2 (param "a" u32) (result (list u8)))) 46 (core instance $libc2 (instantiate $libc)) 47 (core func (canon lower (func $f2) (memory $libc2 "memory") (realloc (func $libc2 "realloc")))) 48 49 (core func (canon lower (func $f1) (memory $libc2 "memory") (realloc (func $libc2 "realloc")))) 50 (core func (canon lower (func $f2) (memory $libc "memory") (realloc (func $libc "realloc")))) 51 )"# 52 ), 53 )?; 54 Component::new( 55 &engine, 56 format!( 57 r#"(component 58 (import "log" (func $log (param "a" string))) 59 {libc} 60 (core func $log_lower (canon lower (func $log) (memory $libc "memory") (realloc (func $libc "realloc")))) 61 62 (core module $logger 63 (import "host" "log" (func $log (param i32 i32))) 64 (import "libc" "memory" (memory 1)) 65 66 (func (export "call") 67 i32.const 0 68 i32.const 0 69 call $log) 70 ) 71 (core instance $logger (instantiate $logger 72 (with "host" (instance (export "log" (func $log_lower)))) 73 (with "libc" (instance $libc)) 74 )) 75 76 (func (export "call") 77 (canon lift (core func $logger "call")) 78 ) 79 )"# 80 ), 81 )?; 82 Ok(()) 83 } 84 85 #[test] 86 fn simple() -> Result<()> { 87 let component = r#" 88 (component 89 (import "a" (func $log (param "a" string))) 90 91 (core module $libc 92 (memory (export "memory") 1) 93 94 (func (export "realloc") (param i32 i32 i32 i32) (result i32) 95 unreachable) 96 ) 97 (core instance $libc (instantiate $libc)) 98 (core func $log_lower 99 (canon lower (func $log) (memory $libc "memory") (realloc (func $libc "realloc"))) 100 ) 101 (core module $m 102 (import "libc" "memory" (memory 1)) 103 (import "host" "log" (func $log (param i32 i32))) 104 105 (func (export "call") 106 i32.const 5 107 i32.const 11 108 call $log) 109 110 (data (i32.const 5) "hello world") 111 ) 112 (core instance $i (instantiate $m 113 (with "libc" (instance $libc)) 114 (with "host" (instance (export "log" (func $log_lower)))) 115 )) 116 (func (export "call") 117 (canon lift (core func $i "call")) 118 ) 119 ) 120 "#; 121 122 let engine = super::engine(); 123 let component = Component::new(&engine, component)?; 124 let mut store = Store::new(&engine, None); 125 assert!(store.data().is_none()); 126 127 // First, test the static API 128 129 let mut linker = Linker::new(&engine); 130 linker.root().func_wrap( 131 "a", 132 |mut store: StoreContextMut<'_, Option<String>>, (arg,): (WasmStr,)| -> Result<_> { 133 let s = arg.to_str(&store)?.to_string(); 134 assert!(store.data().is_none()); 135 *store.data_mut() = Some(s); 136 Ok(()) 137 }, 138 )?; 139 let instance = linker.instantiate(&mut store, &component)?; 140 instance 141 .get_typed_func::<(), ()>(&mut store, "call")? 142 .call(&mut store, ())?; 143 assert_eq!(store.data().as_ref().unwrap(), "hello world"); 144 145 // Next, test the dynamic API 146 147 *store.data_mut() = None; 148 let mut linker = Linker::new(&engine); 149 linker.root().func_new( 150 &component, 151 "a", 152 |mut store: StoreContextMut<'_, Option<String>>, args, _results| { 153 if let Val::String(s) = &args[0] { 154 assert!(store.data().is_none()); 155 *store.data_mut() = Some(s.to_string()); 156 Ok(()) 157 } else { 158 panic!() 159 } 160 }, 161 )?; 162 let instance = linker.instantiate(&mut store, &component)?; 163 instance 164 .get_func(&mut store, "call") 165 .unwrap() 166 .call(&mut store, &[], &mut [])?; 167 assert_eq!(store.data().as_ref().unwrap(), "hello world"); 168 169 Ok(()) 170 } 171 172 #[test] 173 fn attempt_to_leave_during_malloc() -> Result<()> { 174 let component = r#" 175 (component 176 (import "thunk" (func $thunk)) 177 (import "ret-string" (func $ret_string (result string))) 178 179 (core module $host_shim 180 (table (export "table") 2 funcref) 181 (func $shim_thunk (export "thunk") 182 i32.const 0 183 call_indirect) 184 (func $shim_ret_string (export "ret-string") (param i32) 185 local.get 0 186 i32.const 1 187 call_indirect (param i32)) 188 ) 189 (core instance $host_shim (instantiate $host_shim)) 190 191 (core module $m 192 (import "host" "thunk" (func $thunk)) 193 (import "host" "ret-string" (func $ret_string (param i32))) 194 195 (memory (export "memory") 1) 196 197 (func $realloc (export "realloc") (param i32 i32 i32 i32) (result i32) 198 call $thunk 199 unreachable) 200 201 (func $run (export "run") 202 i32.const 8 203 call $ret_string) 204 205 (func (export "take-string") (param i32 i32) 206 unreachable) 207 ) 208 (core instance $m (instantiate $m (with "host" (instance $host_shim)))) 209 210 (core module $host_shim_filler_inner 211 (import "shim" "table" (table 2 funcref)) 212 (import "host" "thunk" (func $thunk)) 213 (import "host" "ret-string" (func $ret_string (param i32))) 214 (elem (i32.const 0) $thunk $ret_string) 215 ) 216 217 (core func $thunk_lower 218 (canon lower (func $thunk) (memory $m "memory") (realloc (func $m "realloc"))) 219 ) 220 221 (core func $ret_string_lower 222 (canon lower (func $ret_string) (memory $m "memory") (realloc (func $m "realloc"))) 223 ) 224 225 (core instance (instantiate $host_shim_filler_inner 226 (with "shim" (instance $host_shim)) 227 (with "host" (instance 228 (export "thunk" (func $thunk_lower)) 229 (export "ret-string" (func $ret_string_lower)) 230 )) 231 )) 232 233 (func (export "run") 234 (canon lift (core func $m "run")) 235 ) 236 (func (export "take-string") (param "a" string) 237 (canon lift (core func $m "take-string") (memory $m "memory") (realloc (func $m "realloc"))) 238 ) 239 ) 240 "#; 241 242 let engine = super::engine(); 243 let mut linker = Linker::new(&engine); 244 linker.root().func_wrap("thunk", |_, _: ()| -> Result<()> { 245 panic!("should not get here") 246 })?; 247 linker 248 .root() 249 .func_wrap("ret-string", |_, _: ()| -> Result<_> { 250 Ok(("hello".to_string(),)) 251 })?; 252 let component = Component::new(&engine, component)?; 253 let mut store = Store::new(&engine, ()); 254 255 // Assert that during a host import if we return values to wasm that a trap 256 // happens if we try to leave the instance. 257 let trap = linker 258 .instantiate(&mut store, &component)? 259 .get_typed_func::<(), ()>(&mut store, "run")? 260 .call(&mut store, ()) 261 .unwrap_err(); 262 assert!( 263 format!("{trap:?}").contains("cannot leave component instance"), 264 "bad trap: {trap:?}", 265 ); 266 267 let trace = trap.downcast_ref::<WasmBacktrace>().unwrap().frames(); 268 assert_eq!(trace.len(), 4); 269 270 // This was our entry point... 271 assert_eq!(trace[3].module_name(), Some("m")); 272 assert_eq!(trace[3].func_name(), Some("run")); 273 274 // ... which called an imported function which ends up being originally 275 // defined by the shim instance. The shim instance then does an indirect 276 // call through a table which goes to the `canon.lower`'d host function 277 assert_eq!(trace[2].module_name(), Some("host_shim")); 278 assert_eq!(trace[2].func_name(), Some("shim_ret_string")); 279 280 // ... and the lowered host function will call realloc to allocate space for 281 // the result 282 assert_eq!(trace[1].module_name(), Some("m")); 283 assert_eq!(trace[1].func_name(), Some("realloc")); 284 285 // ... but realloc calls the shim instance and tries to exit the 286 // component, triggering a dynamic trap 287 assert_eq!(trace[0].module_name(), Some("host_shim")); 288 assert_eq!(trace[0].func_name(), Some("shim_thunk")); 289 290 // In addition to the above trap also ensure that when we enter a wasm 291 // component if we try to leave while lowering then that's also a dynamic 292 // trap. 293 let trap = linker 294 .instantiate(&mut store, &component)? 295 .get_typed_func::<(&str,), ()>(&mut store, "take-string")? 296 .call(&mut store, ("x",)) 297 .unwrap_err(); 298 assert!( 299 format!("{trap:?}").contains("cannot leave component instance"), 300 "bad trap: {trap:?}", 301 ); 302 Ok(()) 303 } 304 305 #[test] 306 fn attempt_to_reenter_during_host() -> Result<()> { 307 let component = r#" 308 (component 309 (import "thunk" (func $thunk)) 310 (core func $thunk_lower (canon lower (func $thunk))) 311 312 (core module $m 313 (import "host" "thunk" (func $thunk)) 314 315 (func $run (export "run") 316 call $thunk) 317 ) 318 (core instance $m (instantiate $m 319 (with "host" (instance (export "thunk" (func $thunk_lower)))) 320 )) 321 322 (func (export "run") 323 (canon lift (core func $m "run")) 324 ) 325 ) 326 "#; 327 328 let engine = super::engine(); 329 let component = Component::new(&engine, component)?; 330 331 // First, test the static API 332 333 struct StaticState { 334 func: Option<TypedFunc<(), ()>>, 335 } 336 337 let mut store = Store::new(&engine, StaticState { func: None }); 338 let mut linker = Linker::new(&engine); 339 linker.root().func_wrap( 340 "thunk", 341 |mut store: StoreContextMut<'_, StaticState>, _: ()| -> Result<()> { 342 let func = store.data_mut().func.take().unwrap(); 343 let trap = func.call(&mut store, ()).unwrap_err(); 344 assert!( 345 format!("{trap:?}").contains("cannot reenter component instance"), 346 "bad trap: {trap:?}", 347 ); 348 Ok(()) 349 }, 350 )?; 351 let instance = linker.instantiate(&mut store, &component)?; 352 let func = instance.get_typed_func::<(), ()>(&mut store, "run")?; 353 store.data_mut().func = Some(func); 354 func.call(&mut store, ())?; 355 356 // Next, test the dynamic API 357 358 struct DynamicState { 359 func: Option<Func>, 360 } 361 362 let mut store = Store::new(&engine, DynamicState { func: None }); 363 let mut linker = Linker::new(&engine); 364 linker.root().func_new( 365 &component, 366 "thunk", 367 |mut store: StoreContextMut<'_, DynamicState>, _, _| { 368 let func = store.data_mut().func.take().unwrap(); 369 let trap = func.call(&mut store, &[], &mut []).unwrap_err(); 370 assert!( 371 format!("{trap:?}").contains("cannot reenter component instance"), 372 "bad trap: {trap:?}", 373 ); 374 Ok(()) 375 }, 376 )?; 377 let instance = linker.instantiate(&mut store, &component)?; 378 let func = instance.get_func(&mut store, "run").unwrap(); 379 store.data_mut().func = Some(func); 380 func.call(&mut store, &[], &mut [])?; 381 382 Ok(()) 383 } 384 385 #[test] 386 fn stack_and_heap_args_and_rets() -> Result<()> { 387 let component = format!( 388 r#" 389 (component 390 (type $many_params (tuple 391 string string string string 392 string string string string 393 string)) 394 (import "f1" (func $f1 (param "a" u32) (result u32))) 395 (import "f2" (func $f2 (param "a" $many_params) (result u32))) 396 (import "f3" (func $f3 (param "a" u32) (result string))) 397 (import "f4" (func $f4 (param "a" $many_params) (result string))) 398 399 (core module $libc 400 {REALLOC_AND_FREE} 401 (memory (export "memory") 1) 402 ) 403 (core instance $libc (instantiate (module $libc))) 404 405 (core func $f1_lower (canon lower (func $f1) (memory $libc "memory") (realloc (func $libc "realloc")))) 406 (core func $f2_lower (canon lower (func $f2) (memory $libc "memory") (realloc (func $libc "realloc")))) 407 (core func $f3_lower (canon lower (func $f3) (memory $libc "memory") (realloc (func $libc "realloc")))) 408 (core func $f4_lower (canon lower (func $f4) (memory $libc "memory") (realloc (func $libc "realloc")))) 409 410 (core module $m 411 (import "host" "f1" (func $f1 (param i32) (result i32))) 412 (import "host" "f2" (func $f2 (param i32) (result i32))) 413 (import "host" "f3" (func $f3 (param i32 i32))) 414 (import "host" "f4" (func $f4 (param i32 i32))) 415 (import "libc" "memory" (memory 1)) 416 417 (func $run (export "run") 418 block 419 i32.const 1 420 call $f1 421 i32.const 2 422 i32.eq 423 br_if 0 424 unreachable 425 end 426 427 block 428 call $allocate_empty_strings 429 call $f2 430 i32.const 3 431 i32.eq 432 br_if 0 433 unreachable 434 end 435 436 block 437 i32.const 8 438 i32.const 16000 439 call $f3 440 (call $validate_string_ret (i32.const 16000)) 441 end 442 443 block 444 call $allocate_empty_strings 445 i32.const 20000 446 call $f4 447 (call $validate_string_ret (i32.const 20000)) 448 end 449 ) 450 451 (func $allocate_empty_strings (result i32) 452 (local $ret i32) 453 (local $offset i32) 454 (local $cnt i32) 455 (local.set $ret (i32.const 8000)) 456 (local.set $cnt (i32.const 9)) 457 458 loop 459 (call $setup_str (i32.add (local.get $ret) (local.get $offset))) 460 (local.set $offset (i32.add (local.get $offset) (i32.const 8))) 461 462 (local.tee $cnt (i32.add (local.get $cnt) (i32.const -1))) 463 br_if 0 464 end 465 466 local.get $ret 467 ) 468 (func $setup_str (param $addr i32) 469 (i32.store offset=0 (local.get $addr) (i32.const 1000)) 470 (i32.store offset=4 (local.get $addr) (i32.const 3)) 471 ) 472 473 (func $validate_string_ret (param $addr i32) 474 (local $base i32) 475 (local $len i32) 476 (local.set $base (i32.load (local.get $addr))) 477 (local.set $len (i32.load offset=4 (local.get $addr))) 478 479 block 480 local.get $len 481 i32.const 3 482 i32.eq 483 br_if 0 484 unreachable 485 end 486 487 (i32.load8_u offset=0 (local.get $base)) 488 i32.const 120 ;; 'x' 489 i32.ne 490 if unreachable end 491 492 (i32.load8_u offset=1 (local.get $base)) 493 i32.const 121 ;; 'y' 494 i32.ne 495 if unreachable end 496 497 (i32.load8_u offset=2 (local.get $base)) 498 i32.const 122 ;; 'z' 499 i32.ne 500 if unreachable end 501 ) 502 503 (data (i32.const 1000) "abc") 504 ) 505 (core instance $m (instantiate $m 506 (with "libc" (instance $libc)) 507 (with "host" (instance 508 (export "f1" (func $f1_lower)) 509 (export "f2" (func $f2_lower)) 510 (export "f3" (func $f3_lower)) 511 (export "f4" (func $f4_lower)) 512 )) 513 )) 514 515 (func (export "run") 516 (canon lift (core func $m "run")) 517 ) 518 ) 519 "# 520 ); 521 522 let engine = super::engine(); 523 let component = Component::new(&engine, component)?; 524 let mut store = Store::new(&engine, ()); 525 526 // First, test the static API 527 528 let mut linker = Linker::new(&engine); 529 linker 530 .root() 531 .func_wrap("f1", |_, (x,): (u32,)| -> Result<(u32,)> { 532 assert_eq!(x, 1); 533 Ok((2,)) 534 })?; 535 linker.root().func_wrap( 536 "f2", 537 |cx: StoreContextMut<'_, ()>, 538 (arg,): (( 539 WasmStr, 540 WasmStr, 541 WasmStr, 542 WasmStr, 543 WasmStr, 544 WasmStr, 545 WasmStr, 546 WasmStr, 547 WasmStr, 548 ),)| 549 -> Result<(u32,)> { 550 assert_eq!(arg.0.to_str(&cx).unwrap(), "abc"); 551 Ok((3,)) 552 }, 553 )?; 554 linker 555 .root() 556 .func_wrap("f3", |_, (arg,): (u32,)| -> Result<(String,)> { 557 assert_eq!(arg, 8); 558 Ok(("xyz".to_string(),)) 559 })?; 560 linker.root().func_wrap( 561 "f4", 562 |cx: StoreContextMut<'_, ()>, 563 (arg,): (( 564 WasmStr, 565 WasmStr, 566 WasmStr, 567 WasmStr, 568 WasmStr, 569 WasmStr, 570 WasmStr, 571 WasmStr, 572 WasmStr, 573 ),)| 574 -> Result<(String,)> { 575 assert_eq!(arg.0.to_str(&cx).unwrap(), "abc"); 576 Ok(("xyz".to_string(),)) 577 }, 578 )?; 579 let instance = linker.instantiate(&mut store, &component)?; 580 instance 581 .get_typed_func::<(), ()>(&mut store, "run")? 582 .call(&mut store, ())?; 583 584 // Next, test the dynamic API 585 586 let mut linker = Linker::new(&engine); 587 linker 588 .root() 589 .func_new(&component, "f1", |_, args, results| { 590 if let Val::U32(x) = &args[0] { 591 assert_eq!(*x, 1); 592 results[0] = Val::U32(2); 593 Ok(()) 594 } else { 595 panic!() 596 } 597 })?; 598 linker 599 .root() 600 .func_new(&component, "f2", |_, args, results| { 601 if let Val::Tuple(tuple) = &args[0] { 602 if let Val::String(s) = &tuple.values()[0] { 603 assert_eq!(s.deref(), "abc"); 604 results[0] = Val::U32(3); 605 Ok(()) 606 } else { 607 panic!() 608 } 609 } else { 610 panic!() 611 } 612 })?; 613 linker 614 .root() 615 .func_new(&component, "f3", |_, args, results| { 616 if let Val::U32(x) = &args[0] { 617 assert_eq!(*x, 8); 618 results[0] = Val::String("xyz".into()); 619 Ok(()) 620 } else { 621 panic!(); 622 } 623 })?; 624 linker 625 .root() 626 .func_new(&component, "f4", |_, args, results| { 627 if let Val::Tuple(tuple) = &args[0] { 628 if let Val::String(s) = &tuple.values()[0] { 629 assert_eq!(s.deref(), "abc"); 630 results[0] = Val::String("xyz".into()); 631 Ok(()) 632 } else { 633 panic!() 634 } 635 } else { 636 panic!() 637 } 638 })?; 639 let instance = linker.instantiate(&mut store, &component)?; 640 instance 641 .get_func(&mut store, "run") 642 .unwrap() 643 .call(&mut store, &[], &mut [])?; 644 645 Ok(()) 646 } 647 648 #[test] 649 fn bad_import_alignment() -> Result<()> { 650 let component = format!( 651 r#" 652 (component 653 (import "unaligned-retptr" (func $unaligned_retptr (result string))) 654 (type $many_arg (tuple 655 string string string string 656 string string string string 657 string 658 )) 659 (import "unaligned-argptr" (func $unaligned_argptr (param "a" $many_arg))) 660 (core module $libc_panic 661 (memory (export "memory") 1) 662 (func (export "realloc") (param i32 i32 i32 i32) (result i32) 663 unreachable) 664 ) 665 (core instance $libc_panic (instantiate $libc_panic)) 666 667 (core func $unaligned_retptr_lower 668 (canon lower (func $unaligned_retptr) (memory $libc_panic "memory") (realloc (func $libc_panic "realloc"))) 669 ) 670 (core func $unaligned_argptr_lower 671 (canon lower (func $unaligned_argptr) (memory $libc_panic "memory") (realloc (func $libc_panic "realloc"))) 672 ) 673 674 (core module $m 675 (import "host" "unaligned-retptr" (func $unaligned_retptr (param i32))) 676 (import "host" "unaligned-argptr" (func $unaligned_argptr (param i32))) 677 678 (func (export "unaligned-retptr") 679 (call $unaligned_retptr (i32.const 1))) 680 (func (export "unaligned-argptr") 681 (call $unaligned_argptr (i32.const 1))) 682 ) 683 (core instance $m (instantiate $m 684 (with "host" (instance 685 (export "unaligned-retptr" (func $unaligned_retptr_lower)) 686 (export "unaligned-argptr" (func $unaligned_argptr_lower)) 687 )) 688 )) 689 690 (func (export "unaligned-retptr2") 691 (canon lift (core func $m "unaligned-retptr")) 692 ) 693 (func (export "unaligned-argptr2") 694 (canon lift (core func $m "unaligned-argptr")) 695 ) 696 ) 697 "# 698 ); 699 700 let engine = super::engine(); 701 let mut linker = Linker::new(&engine); 702 linker 703 .root() 704 .func_wrap("unaligned-retptr", |_, _: ()| -> Result<(String,)> { 705 Ok((String::new(),)) 706 })?; 707 linker.root().func_wrap( 708 "unaligned-argptr", 709 |_, 710 _: (( 711 WasmStr, 712 WasmStr, 713 WasmStr, 714 WasmStr, 715 WasmStr, 716 WasmStr, 717 WasmStr, 718 WasmStr, 719 WasmStr, 720 ),)| 721 -> Result<()> { unreachable!() }, 722 )?; 723 let component = Component::new(&engine, component)?; 724 let mut store = Store::new(&engine, ()); 725 726 let trap = linker 727 .instantiate(&mut store, &component)? 728 .get_typed_func::<(), ()>(&mut store, "unaligned-retptr2")? 729 .call(&mut store, ()) 730 .unwrap_err(); 731 assert!( 732 format!("{:?}", trap).contains("pointer not aligned"), 733 "{}", 734 trap 735 ); 736 let trap = linker 737 .instantiate(&mut store, &component)? 738 .get_typed_func::<(), ()>(&mut store, "unaligned-argptr2")? 739 .call(&mut store, ()) 740 .unwrap_err(); 741 assert!( 742 format!("{:?}", trap).contains("pointer not aligned"), 743 "{}", 744 trap 745 ); 746 747 Ok(()) 748 } 749 750 #[test] 751 fn no_actual_wasm_code() -> Result<()> { 752 let component = r#" 753 (component 754 (import "f" (func $f)) 755 756 (core func $f_lower 757 (canon lower (func $f)) 758 ) 759 (core module $m 760 (import "" "" (func $f)) 761 (export "f" (func $f)) 762 ) 763 (core instance $i (instantiate $m 764 (with "" (instance 765 (export "" (func $f_lower)) 766 )) 767 )) 768 (func (export "thunk") 769 (canon lift 770 (core func $i "f") 771 ) 772 ) 773 ) 774 "#; 775 776 let engine = super::engine(); 777 let component = Component::new(&engine, component)?; 778 let mut store = Store::new(&engine, 0); 779 780 // First, test the static API 781 782 let mut linker = Linker::new(&engine); 783 linker.root().func_wrap( 784 "f", 785 |mut store: StoreContextMut<'_, u32>, _: ()| -> Result<()> { 786 *store.data_mut() += 1; 787 Ok(()) 788 }, 789 )?; 790 791 let instance = linker.instantiate(&mut store, &component)?; 792 let thunk = instance.get_typed_func::<(), ()>(&mut store, "thunk")?; 793 794 assert_eq!(*store.data(), 0); 795 thunk.call(&mut store, ())?; 796 assert_eq!(*store.data(), 1); 797 798 // Next, test the dynamic API 799 800 *store.data_mut() = 0; 801 let mut linker = Linker::new(&engine); 802 linker.root().func_new( 803 &component, 804 "f", 805 |mut store: StoreContextMut<'_, u32>, _, _| { 806 *store.data_mut() += 1; 807 Ok(()) 808 }, 809 )?; 810 811 let instance = linker.instantiate(&mut store, &component)?; 812 let thunk = instance.get_func(&mut store, "thunk").unwrap(); 813 814 assert_eq!(*store.data(), 0); 815 thunk.call(&mut store, &[], &mut [])?; 816 assert_eq!(*store.data(), 1); 817 818 Ok(()) 819 } 820