lucet_module_data/
types.rs

1use cranelift_codegen::ir;
2use serde::{Deserialize, Serialize};
3use std::convert::TryFrom;
4use std::fmt::{Display, Formatter};
5
6#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
7pub enum ValueType {
8    I32,
9    I64,
10    F32,
11    F64,
12}
13
14impl Display for ValueType {
15    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
16        match self {
17            ValueType::I32 => write!(f, "I32"),
18            ValueType::I64 => write!(f, "I64"),
19            ValueType::F32 => write!(f, "F32"),
20            ValueType::F64 => write!(f, "F64"),
21        }
22    }
23}
24
25#[derive(Debug)]
26pub enum ValueError {
27    Unrepresentable,
28    InvalidVMContext,
29}
30
31impl TryFrom<&ir::AbiParam> for ValueType {
32    type Error = ValueError;
33
34    fn try_from(value: &ir::AbiParam) -> Result<Self, Self::Error> {
35        match value {
36            ir::AbiParam {
37                value_type: cranelift_ty,
38                purpose: ir::ArgumentPurpose::Normal,
39                extension: ir::ArgumentExtension::None,
40                location: ir::ArgumentLoc::Unassigned,
41            } => {
42                let size = cranelift_ty.bits();
43
44                if cranelift_ty.is_int() {
45                    match size {
46                        32 => Ok(ValueType::I32),
47                        64 => Ok(ValueType::I64),
48                        _ => Err(ValueError::Unrepresentable),
49                    }
50                } else if cranelift_ty.is_float() {
51                    match size {
52                        32 => Ok(ValueType::F32),
53                        64 => Ok(ValueType::F64),
54                        _ => Err(ValueError::Unrepresentable),
55                    }
56                } else {
57                    Err(ValueError::Unrepresentable)
58                }
59            }
60            _ => Err(ValueError::Unrepresentable),
61        }
62    }
63}
64
65/// A signature for a function in a wasm module.
66///
67/// Note that this does not explicitly name VMContext as a parameter! It is assumed that all wasm
68/// functions take VMContext as their first parameter.
69#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
70pub struct Signature {
71    pub params: Vec<ValueType>,
72    // In the future, wasm may permit this to be a Vec of ValueType
73    pub ret_ty: Option<ValueType>,
74}
75
76impl Display for Signature {
77    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
78        write!(f, "(")?;
79        for (i, p) in self.params.iter().enumerate() {
80            if i == 0 {
81                write!(f, "{}", p)?;
82            } else {
83                write!(f, ", {}", p)?;
84            }
85        }
86        write!(f, ") -> ")?;
87        match self.ret_ty {
88            Some(ty) => write!(f, "{}", ty),
89            None => write!(f, "()"),
90        }
91    }
92}
93
94#[macro_export]
95macro_rules! lucet_signature {
96    ((() -> ())) => {
97        $crate::Signature {
98            params: vec![],
99            ret_ty: None
100        }
101    };
102    (($($arg_ty:ident),*) -> ()) => {
103        $crate::Signature {
104            params: vec![$($crate::ValueType::$arg_ty),*],
105            ret_ty: None,
106        }
107    };
108    (($($arg_ty:ident),*) -> $ret_ty:ident) => {
109        $crate::Signature {
110            params: vec![$($crate::ValueType::$arg_ty),*],
111            ret_ty: Some($crate::ValueType::$ret_ty),
112        }
113    };
114}
115
116#[derive(Debug)]
117pub enum SignatureError {
118    BadElement(ir::AbiParam, ValueError),
119    BadSignature,
120}
121
122impl TryFrom<&ir::Signature> for Signature {
123    type Error = SignatureError;
124
125    fn try_from(value: &ir::Signature) -> Result<Self, Self::Error> {
126        let mut params: Vec<ValueType> = Vec::new();
127
128        let mut param_iter = value.params.iter();
129
130        // Enforce that the first parameter is VMContext, as Signature assumes.
131        // Even functions declared no-arg take VMContext in reality.
132        if let Some(param) = param_iter.next() {
133            match &param {
134                ir::AbiParam {
135                    value_type: value,
136                    purpose: ir::ArgumentPurpose::VMContext,
137                    extension: ir::ArgumentExtension::None,
138                    location: ir::ArgumentLoc::Unassigned,
139                } => {
140                    if value.is_int() && value.bits() == 64 {
141                        // this is VMContext, so we can move on.
142                    } else {
143                        return Err(SignatureError::BadElement(
144                            param.to_owned(),
145                            ValueError::InvalidVMContext,
146                        ));
147                    }
148                }
149                _ => {
150                    return Err(SignatureError::BadElement(
151                        param.to_owned(),
152                        ValueError::InvalidVMContext,
153                    ));
154                }
155            }
156        } else {
157            return Err(SignatureError::BadSignature);
158        }
159
160        for param in param_iter {
161            let value_ty = ValueType::try_from(param)
162                .map_err(|e| SignatureError::BadElement(param.clone(), e))?;
163
164            params.push(value_ty);
165        }
166
167        let ret_ty: Option<ValueType> = match value.returns.as_slice() {
168            &[] => None,
169            &[ref ret_ty] => {
170                let value_ty = ValueType::try_from(ret_ty)
171                    .map_err(|e| SignatureError::BadElement(ret_ty.clone(), e))?;
172
173                Some(value_ty)
174            }
175            _ => {
176                return Err(SignatureError::BadSignature);
177            }
178        };
179
180        Ok(Signature { params, ret_ty })
181    }
182}