1 use super::{super::REALLOC_AND_FREE, engine}; 2 use wasmtime::{ 3 Error, Store, 4 component::{Component, Linker}, 5 format_err, 6 }; 7 8 mod empty_error { 9 use super::*; 10 use wasmtime::component::HasSelf; 11 12 wasmtime::component::bindgen!({ 13 inline: " 14 package inline:inline; 15 world result-playground { 16 import imports: interface { 17 empty-error: func(a: f64) -> result<f64>; 18 } 19 20 export empty-error: func(a: f64) -> result<f64>; 21 }", 22 imports: {default: trappable}, 23 }); 24 25 #[test] run() -> Result<(), Error>26 fn run() -> Result<(), Error> { 27 let engine = engine(); 28 let component = Component::new( 29 &engine, 30 r#" 31 (component 32 (import "imports" (instance $i 33 (export "empty-error" (func (param "a" f64) (result (result f64)))) 34 )) 35 (core module $libc 36 (memory (export "memory") 1) 37 ) 38 (core instance $libc (instantiate $libc)) 39 (core module $m 40 (import "" "core_empty_error" (func $f (param f64 i32))) 41 (import "libc" "memory" (memory 0)) 42 (func (export "core_empty_error_export") (param f64) (result i32) 43 (call $f (local.get 0) (i32.const 8)) 44 (i32.const 8) 45 ) 46 ) 47 (core func $core_empty_error 48 (canon lower (func $i "empty-error") (memory $libc "memory")) 49 ) 50 (core instance $i (instantiate $m 51 (with "" (instance (export "core_empty_error" (func $core_empty_error)))) 52 (with "libc" (instance $libc)) 53 )) 54 (func $f_empty_error 55 (export "empty-error") 56 (param "a" f64) 57 (result (result f64)) 58 (canon lift (core func $i "core_empty_error_export") (memory $libc "memory")) 59 ) 60 ) 61 "#, 62 )?; 63 64 #[derive(Default)] 65 struct MyImports {} 66 67 impl imports::Host for MyImports { 68 fn empty_error(&mut self, a: f64) -> Result<Result<f64, ()>, Error> { 69 if a == 0.0 { 70 Ok(Ok(a)) 71 } else if a == 1.0 { 72 Ok(Err(())) 73 } else { 74 Err(format_err!("empty_error: trap")) 75 } 76 } 77 } 78 79 let mut linker = Linker::new(&engine); 80 imports::add_to_linker::<_, HasSelf<_>>(&mut linker, |f| f)?; 81 82 let mut store = Store::new(&engine, MyImports::default()); 83 let results = ResultPlayground::instantiate(&mut store, &component, &linker)?; 84 85 assert_eq!( 86 results 87 .call_empty_error(&mut store, 0.0) 88 .expect("no trap") 89 .expect("no error returned"), 90 0.0 91 ); 92 93 results 94 .call_empty_error(&mut store, 1.0) 95 .expect("no trap") 96 .err() 97 .expect("() error returned"); 98 99 let e = results 100 .call_empty_error(&mut store, 2.0) 101 .err() 102 .expect("trap"); 103 assert_eq!( 104 format!("{}", e.source().expect("trap message is stored in source")), 105 "empty_error: trap" 106 ); 107 108 Ok(()) 109 } 110 } 111 112 mod string_error { 113 use super::*; 114 use wasmtime::component::HasSelf; 115 116 wasmtime::component::bindgen!({ 117 inline: " 118 package inline:inline; 119 world result-playground { 120 import imports: interface { 121 string-error: func(a: f64) -> result<f64, string>; 122 } 123 124 export string-error: func(a: f64) -> result<f64, string>; 125 }", 126 imports: { default: trappable }, 127 }); 128 129 #[test] run() -> Result<(), Error>130 fn run() -> Result<(), Error> { 131 let engine = engine(); 132 let component = Component::new( 133 &engine, 134 format!( 135 r#" 136 (component 137 (import "imports" (instance $i 138 (export "string-error" (func (param "a" f64) (result (result f64 (error string))))) 139 )) 140 (core module $libc 141 (memory (export "memory") 1) 142 {REALLOC_AND_FREE} 143 ) 144 (core instance $libc (instantiate $libc)) 145 (core module $m 146 (import "" "core_string_error" (func $f (param f64 i32))) 147 (import "libc" "memory" (memory 0)) 148 (import "libc" "realloc" (func $realloc (param i32 i32 i32 i32) (result i32))) 149 (func (export "core_string_error_export") (param f64) (result i32) 150 (local $retptr i32) 151 (local.set $retptr 152 (call $realloc 153 (i32.const 0) 154 (i32.const 0) 155 (i32.const 4) 156 (i32.const 16))) 157 (call $f (local.get 0) (local.get $retptr)) 158 (local.get $retptr) 159 ) 160 ) 161 (core func $core_string_error 162 (canon lower (func $i "string-error") (memory $libc "memory") (realloc (func $libc "realloc"))) 163 ) 164 (core instance $i (instantiate $m 165 (with "" (instance (export "core_string_error" (func $core_string_error)))) 166 (with "libc" (instance $libc)) 167 )) 168 (func $f_string_error 169 (export "string-error") 170 (param "a" f64) 171 (result (result f64 (error string))) 172 (canon lift (core func $i "core_string_error_export") (memory $libc "memory")) 173 ) 174 ) 175 "# 176 ), 177 )?; 178 179 #[derive(Default)] 180 struct MyImports {} 181 182 impl imports::Host for MyImports { 183 fn string_error(&mut self, a: f64) -> Result<Result<f64, String>, Error> { 184 if a == 0.0 { 185 Ok(Ok(a)) 186 } else if a == 1.0 { 187 Ok(Err("string_error: error".to_owned())) 188 } else { 189 Err(format_err!("string_error: trap")) 190 } 191 } 192 } 193 194 let mut linker = Linker::new(&engine); 195 imports::add_to_linker::<_, HasSelf<_>>(&mut linker, |f| f)?; 196 197 let mut store = Store::new(&engine, MyImports::default()); 198 let results = ResultPlayground::instantiate(&mut store, &component, &linker)?; 199 200 assert_eq!( 201 results 202 .call_string_error(&mut store, 0.0) 203 .expect("no trap") 204 .expect("no error returned"), 205 0.0 206 ); 207 208 let e = results 209 .call_string_error(&mut store, 1.0) 210 .expect("no trap") 211 .err() 212 .expect("error returned"); 213 assert_eq!(e, "string_error: error"); 214 215 let e = results 216 .call_string_error(&mut store, 2.0) 217 .err() 218 .expect("trap"); 219 assert_eq!( 220 format!("{}", e.source().expect("trap message is stored in source")), 221 "string_error: trap" 222 ); 223 224 Ok(()) 225 } 226 } 227 228 mod enum_error { 229 use super::*; 230 use exports::foo; 231 use inline::inline::imports; 232 use wasmtime::component::HasSelf; 233 234 wasmtime::component::bindgen!({ 235 inline: " 236 package inline:inline; 237 interface imports { 238 enum e1 { a, b, c } 239 enum-error: func(a: f64) -> result<f64, e1>; 240 } 241 world result-playground { 242 import imports; 243 export foo: interface { 244 enum e1 { a, b, c } 245 enum-error: func(a: f64) -> result<f64, e1>; 246 } 247 }", 248 trappable_error_type: { "inline:inline/imports.e1" => TrappableE1 }, 249 imports: { default: trappable }, 250 }); 251 252 // You can create concrete trap types which make it all the way out to the 253 // host caller, via downcast_ref below. 254 #[derive(Debug)] 255 pub struct MyTrap; 256 257 impl std::fmt::Display for MyTrap { fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result258 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 259 write!(f, "{self:?}") 260 } 261 } 262 impl std::error::Error for MyTrap {} 263 264 pub enum TrappableE1 { 265 Normal(imports::E1), 266 MyTrap(MyTrap), 267 } 268 269 // It is possible to define From impls that target these generated trappable 270 // types. This allows you to integrate libraries with other error types, or 271 // use your own more descriptive error types, and use ? to convert them at 272 // their throw site. 273 impl From<MyTrap> for TrappableE1 { from(t: MyTrap) -> TrappableE1274 fn from(t: MyTrap) -> TrappableE1 { 275 TrappableE1::MyTrap(t) 276 } 277 } 278 279 impl From<imports::E1> for TrappableE1 { from(t: imports::E1) -> TrappableE1280 fn from(t: imports::E1) -> TrappableE1 { 281 TrappableE1::Normal(t) 282 } 283 } 284 285 #[test] run() -> Result<(), Error>286 fn run() -> Result<(), Error> { 287 let engine = engine(); 288 let component = Component::new( 289 &engine, 290 format!( 291 r#" 292 (component 293 (type $err' (enum "a" "b" "c")) 294 (import (interface "inline:inline/imports") (instance $i 295 (export "err" (type $err (eq $err'))) 296 (export "enum-error" (func (param "a" f64) (result (result f64 (error $err))))) 297 )) 298 (core module $libc 299 (memory (export "memory") 1) 300 {REALLOC_AND_FREE} 301 ) 302 (core instance $libc (instantiate $libc)) 303 (core module $m 304 (import "" "core_enum_error" (func $f (param f64 i32))) 305 (import "libc" "memory" (memory 0)) 306 (import "libc" "realloc" (func $realloc (param i32 i32 i32 i32) (result i32))) 307 (func (export "core_enum_error_export") (param f64) (result i32) 308 (local $retptr i32) 309 (local.set $retptr 310 (call $realloc 311 (i32.const 0) 312 (i32.const 0) 313 (i32.const 4) 314 (i32.const 16))) 315 (call $f (local.get 0) (local.get $retptr)) 316 (local.get $retptr) 317 ) 318 ) 319 (core func $core_enum_error 320 (canon lower (func $i "enum-error") (memory $libc "memory") (realloc (func $libc "realloc"))) 321 ) 322 (core instance $i (instantiate $m 323 (with "" (instance (export "core_enum_error" (func $core_enum_error)))) 324 (with "libc" (instance $libc)) 325 )) 326 (func $f_enum_error 327 (param "a" f64) 328 (result (result f64 (error $err'))) 329 (canon lift (core func $i "core_enum_error_export") (memory $libc "memory")) 330 ) 331 332 (component $nested 333 (import "f-err" (type $err (eq $err'))) 334 (import "f" (func $f (param "a" f64) (result (result f64 (error $err))))) 335 (export $err2 "err" (type $err')) 336 (export "enum-error" (func $f) (func (param "a" f64) (result (result f64 (error $err2))))) 337 ) 338 339 (instance $n (instantiate $nested 340 (with "f-err" (type $err')) 341 (with "f" (func $f_enum_error)) 342 )) 343 (export "foo" (instance $n)) 344 ) 345 "# 346 ), 347 )?; 348 349 #[derive(Default)] 350 struct MyImports {} 351 352 impl imports::Host for MyImports { 353 fn convert_e1(&mut self, err: TrappableE1) -> wasmtime::Result<imports::E1> { 354 match err { 355 TrappableE1::Normal(e) => Ok(e), 356 TrappableE1::MyTrap(e) => Err(e.into()), 357 } 358 } 359 360 fn enum_error(&mut self, a: f64) -> Result<f64, TrappableE1> { 361 if a == 0.0 { 362 Ok(a) 363 } else if a == 1.0 { 364 Err(imports::E1::A)? 365 } else { 366 Err(MyTrap)? 367 } 368 } 369 } 370 371 let mut linker = Linker::new(&engine); 372 imports::add_to_linker::<_, HasSelf<_>>(&mut linker, |f| f)?; 373 374 let mut store = Store::new(&engine, MyImports::default()); 375 let results = ResultPlayground::instantiate(&mut store, &component, &linker)?; 376 377 assert_eq!( 378 results 379 .foo() 380 .call_enum_error(&mut store, 0.0) 381 .expect("no trap") 382 .expect("no error returned"), 383 0.0 384 ); 385 386 let e = results 387 .foo() 388 .call_enum_error(&mut store, 1.0) 389 .expect("no trap") 390 .err() 391 .expect("error returned"); 392 assert_eq!(e, foo::E1::A); 393 394 let e = results 395 .foo() 396 .call_enum_error(&mut store, 2.0) 397 .err() 398 .expect("trap"); 399 assert_eq!( 400 format!("{}", e.source().expect("trap message is stored in source")), 401 "MyTrap" 402 ); 403 e.downcast_ref::<MyTrap>() 404 .expect("downcast trap to concrete MyTrap type"); 405 406 Ok(()) 407 } 408 } 409 410 mod record_error { 411 use super::*; 412 use exports::foo; 413 use inline::inline::imports; 414 use wasmtime::component::HasSelf; 415 416 wasmtime::component::bindgen!({ 417 inline: " 418 package inline:inline; 419 interface imports { 420 record e2 { line: u32, col: u32 } 421 record-error: func(a: f64) -> result<f64, e2>; 422 } 423 world result-playground { 424 import imports; 425 export foo: interface { 426 record e2 { line: u32, col: u32 } 427 record-error: func(a: f64) -> result<f64, e2>; 428 } 429 }", 430 trappable_error_type: { "inline:inline/imports.e2" => TrappableE2 }, 431 imports: { default: trappable }, 432 }); 433 434 pub enum TrappableE2 { 435 Normal(imports::E2), 436 Trap(wasmtime::Error), 437 } 438 439 impl From<imports::E2> for TrappableE2 { from(t: imports::E2) -> TrappableE2440 fn from(t: imports::E2) -> TrappableE2 { 441 TrappableE2::Normal(t) 442 } 443 } 444 445 #[test] run() -> Result<(), Error>446 fn run() -> Result<(), Error> { 447 let engine = engine(); 448 let component = Component::new( 449 &engine, 450 format!( 451 r#" 452 (component 453 (type $e2' (record 454 (field "line" u32) 455 (field "col" u32) 456 )) 457 (import (interface "inline:inline/imports") (instance $i 458 (export "e2" (type $e2 (eq $e2'))) 459 (type $result (result f64 (error $e2))) 460 (export "record-error" (func (param "a" f64) (result $result))) 461 )) 462 (core module $libc 463 (memory (export "memory") 1) 464 {REALLOC_AND_FREE} 465 ) 466 (core instance $libc (instantiate $libc)) 467 (core module $m 468 (import "" "core_record_error" (func $f (param f64 i32))) 469 (import "libc" "memory" (memory 0)) 470 (import "libc" "realloc" (func $realloc (param i32 i32 i32 i32) (result i32))) 471 (func (export "core_record_error_export") (param f64) (result i32) 472 (local $retptr i32) 473 (local.set $retptr 474 (call $realloc 475 (i32.const 0) 476 (i32.const 0) 477 (i32.const 4) 478 (i32.const 16))) 479 (call $f (local.get 0) (local.get $retptr)) 480 (local.get $retptr) 481 ) 482 ) 483 (core func $core_record_error 484 (canon lower (func $i "record-error") (memory $libc "memory") (realloc (func $libc "realloc"))) 485 ) 486 (core instance $i (instantiate $m 487 (with "" (instance (export "core_record_error" (func $core_record_error)))) 488 (with "libc" (instance $libc)) 489 )) 490 (func $f_record_error 491 (param "a" f64) 492 (result (result f64 (error (record (field "line" u32) (field "col" u32))))) 493 (canon lift (core func $i "core_record_error_export") (memory $libc "memory")) 494 ) 495 496 (component $nested 497 (import "f-e2" (type $f-e2 (eq $e2'))) 498 (import "f" (func $f (param "a" f64) (result (result f64 (error $f-e2))))) 499 (export $e2 "e2" (type $e2')) 500 (export "record-error" (func $f) (func (param "a" f64) (result (result f64 (error $e2))))) 501 ) 502 503 (instance (export "foo") (instantiate $nested 504 (with "f-e2" (type $e2')) 505 (with "f" (func $f_record_error)) 506 )) 507 ) 508 "# 509 ), 510 )?; 511 512 #[derive(Default)] 513 struct MyImports {} 514 515 impl imports::Host for MyImports { 516 fn convert_e2(&mut self, err: TrappableE2) -> wasmtime::Result<imports::E2> { 517 match err { 518 TrappableE2::Normal(e) => Ok(e), 519 TrappableE2::Trap(e) => Err(e), 520 } 521 } 522 fn record_error(&mut self, a: f64) -> Result<f64, TrappableE2> { 523 if a == 0.0 { 524 Ok(a) 525 } else if a == 1.0 { 526 Err(imports::E2 { 527 line: 420, 528 col: 1312, 529 })? 530 } else { 531 Err(TrappableE2::Trap(format_err!("record_error: trap"))) 532 } 533 } 534 } 535 536 let mut linker = Linker::new(&engine); 537 imports::add_to_linker::<_, HasSelf<_>>(&mut linker, |f| f)?; 538 539 let mut store = Store::new(&engine, MyImports::default()); 540 let results = ResultPlayground::instantiate(&mut store, &component, &linker)?; 541 542 assert_eq!( 543 results 544 .foo() 545 .call_record_error(&mut store, 0.0) 546 .expect("no trap") 547 .expect("no error returned"), 548 0.0 549 ); 550 551 let e = results 552 .foo() 553 .call_record_error(&mut store, 1.0) 554 .expect("no trap") 555 .err() 556 .expect("error returned"); 557 assert!(matches!( 558 e, 559 record_error::foo::E2 { 560 line: 420, 561 col: 1312 562 } 563 )); 564 565 let e = results 566 .foo() 567 .call_record_error(&mut store, 2.0) 568 .err() 569 .expect("trap"); 570 assert_eq!( 571 format!("{}", e.source().expect("trap message is stored in source")), 572 "record_error: trap" 573 ); 574 575 Ok(()) 576 } 577 } 578 579 mod variant_error { 580 use super::*; 581 use exports::foo; 582 use inline::inline::imports; 583 use wasmtime::component::HasSelf; 584 585 wasmtime::component::bindgen!({ 586 inline: " 587 package inline:inline; 588 interface imports { 589 enum e1 { a, b, c } 590 record e2 { line: u32, col: u32 } 591 variant e3 { E1(e1), E2(e2) } 592 variant-error: func(a: f64) -> result<f64, e3>; 593 } 594 world result-playground { 595 import imports; 596 export foo: interface { 597 enum e1 { a, b, c } 598 record e2 { line: u32, col: u32 } 599 variant e3 { E1(e1), E2(e2) } 600 variant-error: func(a: f64) -> result<f64, e3>; 601 } 602 }", 603 trappable_error_type: { "inline:inline/imports.e3" => TrappableE3 }, 604 imports: { default: trappable }, 605 }); 606 607 pub enum TrappableE3 { 608 Normal(imports::E3), 609 Trap(wasmtime::Error), 610 } 611 612 impl From<imports::E3> for TrappableE3 { from(t: imports::E3) -> TrappableE3613 fn from(t: imports::E3) -> TrappableE3 { 614 TrappableE3::Normal(t) 615 } 616 } 617 618 #[test] run() -> Result<(), Error>619 fn run() -> Result<(), Error> { 620 let engine = engine(); 621 let component = Component::new( 622 &engine, 623 format!( 624 r#" 625 (component 626 (type $e1' (enum "a" "b" "c")) 627 (type $e2' (record (field "line" u32) (field "col" u32))) 628 (type $e3' (variant 629 (case "E1" $e1') 630 (case "E2" $e2') 631 )) 632 (import (interface "inline:inline/imports") (instance $i 633 (export "e1" (type $e1 (eq $e1'))) 634 (export "e2" (type $e2 (eq $e2'))) 635 (type $e3' (variant 636 (case "E1" $e1) 637 (case "E2" $e2) 638 )) 639 (export "e3" (type $e3 (eq $e3'))) 640 (type $result (result f64 (error $e3))) 641 (export "variant-error" (func (param "a" f64) (result $result))) 642 )) 643 (core module $libc 644 (memory (export "memory") 1) 645 {REALLOC_AND_FREE} 646 ) 647 (core instance $libc (instantiate $libc)) 648 (core module $m 649 (import "" "core_variant_error" (func $f (param f64 i32))) 650 (import "libc" "memory" (memory 0)) 651 (import "libc" "realloc" (func $realloc (param i32 i32 i32 i32) (result i32))) 652 (func (export "core_variant_error_export") (param f64) (result i32) 653 (local $retptr i32) 654 (local.set $retptr 655 (call $realloc 656 (i32.const 0) 657 (i32.const 0) 658 (i32.const 4) 659 (i32.const 16))) 660 (call $f (local.get 0) (local.get $retptr)) 661 (local.get $retptr) 662 ) 663 ) 664 (core func $core_variant_error 665 (canon lower (func $i "variant-error") (memory $libc "memory") (realloc (func $libc "realloc"))) 666 ) 667 (core instance $i (instantiate $m 668 (with "" (instance (export "core_variant_error" (func $core_variant_error)))) 669 (with "libc" (instance $libc)) 670 )) 671 (func $f_variant_error 672 (param "a" f64) 673 (result (result f64 (error $e3'))) 674 (canon lift (core func $i "core_variant_error_export") (memory $libc "memory")) 675 ) 676 677 (component $nested 678 (import "f-e1" (type $e1i (eq $e1'))) 679 (import "f-e2" (type $e2i (eq $e2'))) 680 (type $e3i' (variant 681 (case "E1" $e1i) 682 (case "E2" $e2i) 683 )) 684 (import "f-e3" (type $e3i (eq $e3i'))) 685 (import "f" (func $f (param "a" f64) (result (result f64 (error $e3i))))) 686 (export $e1 "e1" (type $e1')) 687 (export $e2 "e2" (type $e2')) 688 (type $e3' (variant 689 (case "E1" $e1) 690 (case "E2" $e2) 691 )) 692 (export $e3 "e3" (type $e3')) 693 (export "variant-error" (func $f) 694 (func (param "a" f64) (result (result f64 (error $e3))))) 695 ) 696 697 (instance (export "foo") (instantiate $nested 698 (with "f-e1" (type $e1')) 699 (with "f-e2" (type $e2')) 700 (with "f-e3" (type $e3')) 701 (with "f" (func $f_variant_error)) 702 )) 703 ) 704 "# 705 ), 706 )?; 707 708 #[derive(Default)] 709 struct MyImports {} 710 711 impl imports::Host for MyImports { 712 fn convert_e3(&mut self, err: TrappableE3) -> wasmtime::Result<imports::E3> { 713 match err { 714 TrappableE3::Normal(e) => Ok(e), 715 TrappableE3::Trap(e) => Err(e), 716 } 717 } 718 fn variant_error(&mut self, a: f64) -> Result<f64, TrappableE3> { 719 if a == 0.0 { 720 Ok(a) 721 } else if a == 1.0 { 722 Err(imports::E3::E2(imports::E2 { 723 line: 420, 724 col: 1312, 725 }))? 726 } else { 727 Err(TrappableE3::Trap(format_err!("variant_error: trap"))) 728 } 729 } 730 } 731 732 let mut linker = Linker::new(&engine); 733 imports::add_to_linker::<_, HasSelf<_>>(&mut linker, |f| f)?; 734 735 let mut store = Store::new(&engine, MyImports::default()); 736 let results = ResultPlayground::instantiate(&mut store, &component, &linker)?; 737 738 assert_eq!( 739 results 740 .foo() 741 .call_variant_error(&mut store, 0.0) 742 .expect("no trap") 743 .expect("no error returned"), 744 0.0 745 ); 746 747 let e = results 748 .foo() 749 .call_variant_error(&mut store, 1.0) 750 .expect("no trap") 751 .err() 752 .expect("error returned"); 753 assert!(matches!( 754 e, 755 variant_error::foo::E3::E2(variant_error::foo::E2 { 756 line: 420, 757 col: 1312 758 }) 759 )); 760 761 let e = results 762 .foo() 763 .call_variant_error(&mut store, 2.0) 764 .err() 765 .expect("trap"); 766 assert_eq!( 767 format!("{}", e.source().expect("trap message is stored in source")), 768 "variant_error: trap" 769 ); 770 771 Ok(()) 772 } 773 } 774 775 mod multiple_interfaces_error { 776 use super::*; 777 use exports::foo; 778 use inline::inline::imports; 779 use inline::inline::types; 780 use wasmtime::component::HasSelf; 781 782 wasmtime::component::bindgen!({ 783 inline: " 784 package inline:inline; 785 interface types { 786 enum e1 { a, b, c } 787 enum-error: func(a: f64) -> result<f64, e1>; 788 } 789 interface imports { 790 use types.{e1}; 791 enum-error: func(a: f64) -> result<f64, e1>; 792 } 793 world result-playground { 794 import imports; 795 export foo: interface { 796 enum e1 { a, b, c } 797 enum-error: func(a: f64) -> result<f64, e1>; 798 } 799 }", 800 trappable_error_type: { "inline:inline/types.e1" => TrappableE1 }, 801 imports: { default: trappable }, 802 }); 803 804 pub enum TrappableE1 { 805 Normal(types::E1), 806 Trap(wasmtime::Error), 807 } 808 809 impl From<types::E1> for TrappableE1 { from(t: imports::E1) -> TrappableE1810 fn from(t: imports::E1) -> TrappableE1 { 811 TrappableE1::Normal(t) 812 } 813 } 814 815 #[test] run() -> Result<(), Error>816 fn run() -> Result<(), Error> { 817 let engine = engine(); 818 // NOTE: this component doesn't make use of a types import, and relies instead on 819 // subtyping. 820 let component = Component::new( 821 &engine, 822 format!( 823 r#" 824 (component 825 (type $err' (enum "a" "b" "c")) 826 (import (interface "inline:inline/imports") (instance $i 827 (export "e1" (type $e1 (eq $err'))) 828 (export "enum-error" (func (param "a" f64) (result (result f64 (error $e1))))) 829 )) 830 (core module $libc 831 (memory (export "memory") 1) 832 {REALLOC_AND_FREE} 833 ) 834 (core instance $libc (instantiate $libc)) 835 (core module $m 836 (import "" "core_enum_error" (func $f (param f64 i32))) 837 (import "libc" "memory" (memory 0)) 838 (import "libc" "realloc" (func $realloc (param i32 i32 i32 i32) (result i32))) 839 (func (export "core_enum_error_export") (param f64) (result i32) 840 (local $retptr i32) 841 (local.set $retptr 842 (call $realloc 843 (i32.const 0) 844 (i32.const 0) 845 (i32.const 4) 846 (i32.const 16))) 847 (call $f (local.get 0) (local.get $retptr)) 848 (local.get $retptr) 849 ) 850 ) 851 (core func $core_enum_error 852 (canon lower (func $i "enum-error") (memory $libc "memory") (realloc (func $libc "realloc"))) 853 ) 854 (core instance $i (instantiate $m 855 (with "" (instance (export "core_enum_error" (func $core_enum_error)))) 856 (with "libc" (instance $libc)) 857 )) 858 (func $f_enum_error 859 (param "a" f64) 860 (result (result f64 (error $err'))) 861 (canon lift (core func $i "core_enum_error_export") (memory $libc "memory")) 862 ) 863 864 (component $nested 865 (import "f-err" (type $err (eq $err'))) 866 (import "f" (func $f (param "a" f64) (result (result f64 (error $err))))) 867 (export $err2 "err" (type $err')) 868 (export "enum-error" (func $f) (func (param "a" f64) (result (result f64 (error $err2))))) 869 ) 870 871 (instance $n (instantiate $nested 872 (with "f-err" (type $err')) 873 (with "f" (func $f_enum_error)) 874 )) 875 (export "foo" (instance $n)) 876 ) 877 "# 878 ), 879 )?; 880 881 // You can create concrete trap types which make it all the way out to the 882 // host caller, via downcast_ref below. 883 #[derive(Debug)] 884 struct MyTrap; 885 886 impl std::fmt::Display for MyTrap { 887 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 888 write!(f, "{self:?}") 889 } 890 } 891 impl std::error::Error for MyTrap {} 892 893 // It is possible to define From impls that target these generated trappable 894 // types. This allows you to integrate libraries with other error types, or 895 // use your own more descriptive error types, and use ? to convert them at 896 // their throw site. 897 impl From<MyTrap> for TrappableE1 { 898 fn from(t: MyTrap) -> TrappableE1 { 899 TrappableE1::Trap(format_err!(t)) 900 } 901 } 902 903 #[derive(Default)] 904 struct MyImports {} 905 906 impl types::Host for MyImports { 907 fn convert_e1(&mut self, err: TrappableE1) -> wasmtime::Result<imports::E1> { 908 match err { 909 TrappableE1::Normal(e) => Ok(e), 910 TrappableE1::Trap(e) => Err(e), 911 } 912 } 913 fn enum_error(&mut self, a: f64) -> Result<f64, TrappableE1> { 914 if a == 0.0 { 915 Ok(a) 916 } else if a == 1.0 { 917 Err(imports::E1::A)? 918 } else { 919 Err(MyTrap)? 920 } 921 } 922 } 923 924 impl imports::Host for MyImports { 925 fn enum_error(&mut self, a: f64) -> Result<f64, TrappableE1> { 926 if a == 0.0 { 927 Ok(a) 928 } else if a == 1.0 { 929 Err(imports::E1::A)? 930 } else { 931 Err(MyTrap)? 932 } 933 } 934 } 935 936 let mut linker = Linker::new(&engine); 937 imports::add_to_linker::<_, HasSelf<_>>(&mut linker, |f| f)?; 938 939 let mut store = Store::new(&engine, MyImports::default()); 940 let results = ResultPlayground::instantiate(&mut store, &component, &linker)?; 941 942 assert_eq!( 943 results 944 .foo() 945 .call_enum_error(&mut store, 0.0) 946 .expect("no trap") 947 .expect("no error returned"), 948 0.0 949 ); 950 951 let e = results 952 .foo() 953 .call_enum_error(&mut store, 1.0) 954 .expect("no trap") 955 .err() 956 .expect("error returned"); 957 assert_eq!(e, foo::E1::A); 958 959 let e = results 960 .foo() 961 .call_enum_error(&mut store, 2.0) 962 .err() 963 .expect("trap"); 964 assert_eq!( 965 format!("{}", e.source().expect("trap message is stored in source")), 966 "MyTrap" 967 ); 968 e.downcast_ref::<MyTrap>() 969 .expect("downcast trap to concrete MyTrap type"); 970 971 Ok(()) 972 } 973 } 974 975 mod with_remapping { 976 use super::*; 977 use wasmtime::component::HasSelf; 978 979 mod interfaces { 980 wasmtime::component::bindgen!({ 981 interfaces: " 982 import imports: interface { 983 empty-error: func(a: f64) -> result<f64>; 984 }", 985 imports: { default: trappable }, 986 }); 987 } 988 989 wasmtime::component::bindgen!({ 990 inline: " 991 package inline:inline; 992 world result-playground { 993 import imports: interface { 994 empty-error: func(a: f64) -> result<f64>; 995 } 996 997 export empty-error: func(a: f64) -> result<f64>; 998 }", 999 with: { 1000 "imports": interfaces::imports, 1001 }, 1002 imports: { default: trappable }, 1003 }); 1004 1005 #[test] run() -> Result<(), Error>1006 fn run() -> Result<(), Error> { 1007 let engine = engine(); 1008 let component = Component::new( 1009 &engine, 1010 r#" 1011 (component 1012 (import "imports" (instance $i 1013 (export "empty-error" (func (param "a" f64) (result (result f64)))) 1014 )) 1015 (core module $libc 1016 (memory (export "memory") 1) 1017 ) 1018 (core instance $libc (instantiate $libc)) 1019 (core module $m 1020 (import "" "core_empty_error" (func $f (param f64 i32))) 1021 (import "libc" "memory" (memory 0)) 1022 (func (export "core_empty_error_export") (param f64) (result i32) 1023 (call $f (local.get 0) (i32.const 8)) 1024 (i32.const 8) 1025 ) 1026 ) 1027 (core func $core_empty_error 1028 (canon lower (func $i "empty-error") (memory $libc "memory")) 1029 ) 1030 (core instance $i (instantiate $m 1031 (with "" (instance (export "core_empty_error" (func $core_empty_error)))) 1032 (with "libc" (instance $libc)) 1033 )) 1034 (func $f_empty_error 1035 (export "empty-error") 1036 (param "a" f64) 1037 (result (result f64)) 1038 (canon lift (core func $i "core_empty_error_export") (memory $libc "memory")) 1039 ) 1040 ) 1041 "#, 1042 )?; 1043 1044 #[derive(Default)] 1045 struct MyImports {} 1046 1047 impl interfaces::imports::Host for MyImports { 1048 fn empty_error(&mut self, a: f64) -> Result<Result<f64, ()>, Error> { 1049 if a == 0.0 { 1050 Ok(Ok(a)) 1051 } else if a == 1.0 { 1052 Ok(Err(())) 1053 } else { 1054 Err(format_err!("empty_error: trap")) 1055 } 1056 } 1057 } 1058 1059 let mut linker = Linker::new(&engine); 1060 interfaces::imports::add_to_linker::<_, HasSelf<_>>(&mut linker, |f| f)?; 1061 1062 let mut store = Store::new(&engine, MyImports::default()); 1063 let results = ResultPlayground::instantiate(&mut store, &component, &linker)?; 1064 1065 assert_eq!( 1066 results 1067 .call_empty_error(&mut store, 0.0) 1068 .expect("no trap") 1069 .expect("no error returned"), 1070 0.0 1071 ); 1072 1073 results 1074 .call_empty_error(&mut store, 1.0) 1075 .expect("no trap") 1076 .err() 1077 .expect("() error returned"); 1078 1079 let e = results 1080 .call_empty_error(&mut store, 2.0) 1081 .err() 1082 .expect("trap"); 1083 assert_eq!( 1084 format!("{}", e.source().expect("trap message is stored in source")), 1085 "empty_error: trap" 1086 ); 1087 1088 Ok(()) 1089 } 1090 } 1091