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