casper_wasmi/
host.rs

1use crate::{value::FromValue, RuntimeValue, Trap, TrapCode};
2
3/// Wrapper around slice of [`Value`] for using it
4/// as an argument list conveniently.
5///
6/// [`Value`]: enum.Value.html
7#[derive(Debug)]
8pub struct RuntimeArgs<'a>(&'a [RuntimeValue]);
9
10impl<'a> From<&'a [RuntimeValue]> for RuntimeArgs<'a> {
11    fn from(inner: &'a [RuntimeValue]) -> Self {
12        RuntimeArgs(inner)
13    }
14}
15
16impl AsRef<[RuntimeValue]> for RuntimeArgs<'_> {
17    fn as_ref(&self) -> &[RuntimeValue] {
18        self.0
19    }
20}
21
22impl RuntimeArgs<'_> {
23    /// Extract argument by index `idx`.
24    ///
25    /// # Errors
26    ///
27    /// Returns `Err` if cast is invalid or not enough arguments.
28    pub fn nth_checked<T>(&self, idx: usize) -> Result<T, Trap>
29    where
30        T: FromValue,
31    {
32        self.nth_value_checked(idx)?
33            .try_into()
34            .ok_or(TrapCode::UnexpectedSignature)
35            .map_err(Into::into)
36    }
37
38    /// Extract argument as a [`Value`] by index `idx`.
39    ///
40    /// # Errors
41    ///
42    /// Returns `Err` if this list has not enough arguments.
43    ///
44    /// [`Value`]: enum.Value.html
45    pub fn nth_value_checked(&self, idx: usize) -> Result<RuntimeValue, Trap> {
46        if self.0.len() <= idx {
47            return Err(TrapCode::UnexpectedSignature.into());
48        }
49        Ok(self.0[idx])
50    }
51
52    /// Extract argument by index `idx`.
53    ///
54    /// # Panics
55    ///
56    /// Panics if cast is invalid or not enough arguments.
57    pub fn nth<T>(&self, idx: usize) -> T
58    where
59        T: FromValue,
60    {
61        let value = self.nth_value_checked(idx).expect("Invalid argument index");
62        value.try_into().expect("Unexpected argument type")
63    }
64
65    /// Total number of arguments
66    pub fn len(&self) -> usize {
67        self.0.len()
68    }
69}
70
71/// Trait that allows to implement host functions.
72///
73/// # Examples
74///
75/// ```rust
76/// use casper_wasmi::{
77///     Externals, RuntimeValue, RuntimeArgs, Error, ModuleImportResolver,
78///     FuncRef, ValueType, Signature, FuncInstance, Trap,
79/// };
80///
81/// struct HostExternals {
82///     counter: usize,
83/// }
84///
85/// const ADD_FUNC_INDEX: usize = 0;
86///
87/// impl Externals for HostExternals {
88///     fn invoke_index(
89///         &mut self,
90///         index: usize,
91///         args: RuntimeArgs,
92///     ) -> Result<Option<RuntimeValue>, Trap> {
93///         match index {
94///             ADD_FUNC_INDEX => {
95///                 let a: u32 = args.nth_checked(0)?;
96///                 let b: u32 = args.nth_checked(1)?;
97///                 let result = a + b;
98///
99///                 Ok(Some(RuntimeValue::I32(result as i32)))
100///             }
101///             _ => panic!("Unimplemented function at {}", index),
102///         }
103///     }
104/// }
105///
106/// impl HostExternals {
107///     fn check_signature(
108///         &self,
109///         index: usize,
110///         signature: &Signature
111///     ) -> bool {
112///         let (params, ret_ty): (&[ValueType], Option<ValueType>) = match index {
113///             ADD_FUNC_INDEX => (&[ValueType::I32, ValueType::I32], Some(ValueType::I32)),
114///             _ => return false,
115///         };
116///         signature.params() == params && signature.return_type() == ret_ty
117///     }
118/// }
119///
120/// impl ModuleImportResolver for HostExternals {
121///     fn resolve_func(
122///         &self,
123///         field_name: &str,
124///         signature: &Signature
125///     ) -> Result<FuncRef, Error> {
126///         let index = match field_name {
127///             "add" => ADD_FUNC_INDEX,
128///             _ => {
129///                 return Err(Error::Instantiation(
130///                     format!("Export {} not found", field_name),
131///                 ))
132///             }
133///         };
134///
135///         if !self.check_signature(index, signature) {
136///             return Err(Error::Instantiation(
137///                 format!("Export {} has a bad signature", field_name)
138///             ));
139///         }
140///
141///         Ok(FuncInstance::alloc_host(
142///             Signature::new(&[ValueType::I32, ValueType::I32][..], Some(ValueType::I32)),
143///             index,
144///         ))
145///     }
146/// }
147/// ```
148pub trait Externals {
149    /// Perform invoke of a host function by specified `index`.
150    fn invoke_index(
151        &mut self,
152        index: usize,
153        args: RuntimeArgs,
154    ) -> Result<Option<RuntimeValue>, Trap>;
155}
156
157/// Implementation of [`Externals`] that just traps on [`invoke_index`].
158///
159/// [`Externals`]: trait.Externals.html
160/// [`invoke_index`]: trait.Externals.html#tymethod.invoke_index
161pub struct NopExternals;
162
163impl Externals for NopExternals {
164    fn invoke_index(
165        &mut self,
166        _index: usize,
167        _args: RuntimeArgs,
168    ) -> Result<Option<RuntimeValue>, Trap> {
169        Err(TrapCode::Unreachable.into())
170    }
171}
172
173#[cfg(test)]
174mod tests {
175    use super::RuntimeArgs;
176    use crate::RuntimeValue;
177    use casper_wasmi_core::HostError;
178
179    #[test]
180    fn i32_runtime_args() {
181        let args: RuntimeArgs = (&[RuntimeValue::I32(0)][..]).into();
182        let val: i32 = args.nth_checked(0).unwrap();
183        assert_eq!(val, 0);
184    }
185
186    #[test]
187    fn i64_invalid_arg_cast() {
188        let args: RuntimeArgs = (&[RuntimeValue::I64(90534534545322)][..]).into();
189        assert!(args.nth_checked::<i32>(0).is_err());
190    }
191
192    // Tests that `HostError` trait is object safe.
193    fn _host_error_is_object_safe(_: &dyn HostError) {}
194}