File: errors.rs

package info (click to toggle)
rust-wasmtime 26.0.1%2Bdfsg-3
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 48,492 kB
  • sloc: ansic: 4,003; sh: 561; javascript: 542; cpp: 254; asm: 175; ml: 96; makefile: 55
file content (159 lines) | stat: -rw-r--r-- 5,681 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
/// Execute the wiggle guest conversion code to exercise it
mod convert_just_errno {
    use anyhow::Result;
    use wiggle::GuestMemory;
    use wiggle_test::{impl_errno, HostMemory, WasiCtx};

    /// The `errors` argument to the wiggle gives us a hook to map a rich error
    /// type like this one (typical of wiggle use cases in wasi-common and beyond)
    /// down to the flat error enums that witx can specify.
    #[derive(Debug, thiserror::Error)]
    pub enum RichError {
        #[error("Invalid argument: {0}")]
        InvalidArg(String),
        #[error("Won't cross picket line: {0}")]
        PicketLine(String),
    }

    // Define an errno with variants corresponding to RichError. Use it in a
    // trivial function.
    wiggle::from_witx!({
        witx_literal: "
(typename $errno (enum (@witx tag u8) $ok $invalid_arg $picket_line))
(module $one_error_conversion
  (@interface func (export \"foo\")
     (param $strike u32)
     (result $err (expected (error $errno)))))
    ",
        errors: { errno => trappable ErrnoT },
    });

    impl_errno!(types::Errno);

    impl From<RichError> for types::ErrnoT {
        fn from(rich: RichError) -> types::ErrnoT {
            match rich {
                RichError::InvalidArg(s) => {
                    types::ErrnoT::from(types::Errno::InvalidArg).context(s)
                }
                RichError::PicketLine(s) => {
                    types::ErrnoT::from(types::Errno::PicketLine).context(s)
                }
            }
        }
    }

    impl<'a> one_error_conversion::OneErrorConversion for WasiCtx<'a> {
        fn foo(&mut self, _memory: &mut GuestMemory<'_>, strike: u32) -> Result<(), types::ErrnoT> {
            // We use the argument to this function to exercise all of the
            // possible error cases we could hit here
            match strike {
                0 => Ok(()),
                1 => Err(RichError::PicketLine(format!("I'm not a scab")))?,
                _ => Err(RichError::InvalidArg(format!("out-of-bounds: {strike}")))?,
            }
        }
    }

    #[test]
    fn one_error_conversion_test() {
        let mut ctx = WasiCtx::new();
        let mut host_memory = HostMemory::new();
        let mut memory = host_memory.guest_memory();

        // Exercise each of the branches in `foo`.
        // Start with the success case:
        let r0 = one_error_conversion::foo(&mut ctx, &mut memory, 0).unwrap();
        assert_eq!(
            r0,
            types::Errno::Ok as i32,
            "Expected return value for strike=0"
        );
        assert!(ctx.log.borrow().is_empty(), "No error log for strike=0");

        // First error case:
        let r1 = one_error_conversion::foo(&mut ctx, &mut memory, 1).unwrap();
        assert_eq!(
            r1,
            types::Errno::PicketLine as i32,
            "Expected return value for strike=1"
        );

        // Second error case:
        let r2 = one_error_conversion::foo(&mut ctx, &mut memory, 2).unwrap();
        assert_eq!(
            r2,
            types::Errno::InvalidArg as i32,
            "Expected return value for strike=2"
        );
    }
}

/// Type-check the wiggle guest conversion code against a more complex case where
/// we use two distinct error types.
mod convert_multiple_error_types {
    pub use super::convert_just_errno::RichError;
    use anyhow::Result;
    use wiggle::GuestMemory;
    use wiggle_test::{impl_errno, WasiCtx};

    /// Test that we can map multiple types of errors.
    #[derive(Debug, thiserror::Error)]
    #[allow(dead_code)]
    pub enum AnotherRichError {
        #[error("I've had this many cups of coffee and can't even think straight: {0}")]
        TooMuchCoffee(usize),
    }

    // Just like the prior test, except that we have a second errno type. This should mean there
    // are two functions in UserErrorConversion.
    // Additionally, test that the function "baz" marked noreturn always returns a wasmtime::Trap.
    wiggle::from_witx!({
        witx_literal: "
(typename $errno (enum (@witx tag u8) $ok $invalid_arg $picket_line))
(typename $errno2 (enum (@witx tag u8) $ok $too_much_coffee))
(module $two_error_conversions
  (@interface func (export \"foo\")
     (param $strike u32)
     (result $err (expected (error $errno))))
  (@interface func (export \"bar\")
     (param $drink u32)
     (result $err (expected (error $errno2))))
  (@interface func (export \"baz\")
     (param $drink u32)
     (@witx noreturn)))
    ",
        errors: { errno => RichError, errno2 => AnotherRichError },
    });

    impl_errno!(types::Errno);
    impl_errno!(types::Errno2);

    // The UserErrorConversion trait will also have two methods for this test. They correspond to
    // each member of the `errors` mapping.
    // Bodies elided.
    impl<'a> types::UserErrorConversion for WasiCtx<'a> {
        fn errno_from_rich_error(&mut self, _e: RichError) -> Result<types::Errno> {
            unimplemented!()
        }
        fn errno2_from_another_rich_error(
            &mut self,
            _e: AnotherRichError,
        ) -> Result<types::Errno2> {
            unimplemented!()
        }
    }

    // And here's the witx module trait impl, bodies elided
    impl<'a> two_error_conversions::TwoErrorConversions for WasiCtx<'a> {
        fn foo(&mut self, _: &mut GuestMemory<'_>, _: u32) -> Result<(), RichError> {
            unimplemented!()
        }
        fn bar(&mut self, _: &mut GuestMemory<'_>, _: u32) -> Result<(), AnotherRichError> {
            unimplemented!()
        }
        fn baz(&mut self, _: &mut GuestMemory<'_>, _: u32) -> anyhow::Error {
            unimplemented!()
        }
    }
}