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}