Skip to main content

hyperlight_common/flatbuffer_wrappers/
function_call.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright 2025 The Hyperlight Authors.
3
4use alloc::string::{String, ToString};
5use alloc::vec::Vec;
6
7use anyhow::{Error, Result, bail};
8use flatbuffers::{FlatBufferBuilder, WIPOffset, size_prefixed_root};
9#[cfg(feature = "tracing")]
10use tracing::{Span, instrument};
11
12use super::function_types::{ParameterValue, ReturnType};
13use crate::flatbuffers::hyperlight::generated::{
14    FunctionCall as FbFunctionCall, FunctionCallArgs as FbFunctionCallArgs,
15    FunctionCallType as FbFunctionCallType, Parameter, ParameterArgs,
16    ParameterValue as FbParameterValue, hlbool, hlboolArgs, hldouble, hldoubleArgs, hlfloat,
17    hlfloatArgs, hlint, hlintArgs, hllong, hllongArgs, hlstring, hlstringArgs, hluint, hluintArgs,
18    hlulong, hlulongArgs, hlvecbytes, hlvecbytesArgs,
19};
20
21/// The type of function call.
22#[derive(Debug, Clone, PartialEq, Eq)]
23pub enum FunctionCallType {
24    /// The function call is to a guest function.
25    Guest,
26    /// The function call is to a host function.
27    Host,
28}
29
30/// `Functioncall` represents a call to a function in the guest or host.
31#[derive(Clone)]
32pub struct FunctionCall {
33    /// The function name
34    pub function_name: String,
35    /// The parameters for the function call.
36    pub parameters: Option<Vec<ParameterValue>>,
37    function_call_type: FunctionCallType,
38    /// The return type of the function call
39    pub expected_return_type: ReturnType,
40}
41
42impl FunctionCall {
43    #[cfg_attr(feature = "tracing", instrument(skip_all, parent = Span::current(), level= "Trace"))]
44    pub fn new(
45        function_name: String,
46        parameters: Option<Vec<ParameterValue>>,
47        function_call_type: FunctionCallType,
48        expected_return_type: ReturnType,
49    ) -> Self {
50        Self {
51            function_name,
52            parameters,
53            function_call_type,
54            expected_return_type,
55        }
56    }
57
58    /// The type of the function call.
59    pub fn function_call_type(&self) -> FunctionCallType {
60        self.function_call_type.clone()
61    }
62
63    /// Encodes self into the given builder and returns the encoded data.
64    ///
65    /// # Notes
66    ///
67    /// The builder should not be reused after a call to encode, since this function
68    /// does not reset the state of the builder. If you want to reuse the builder,
69    /// you'll need to reset it first.
70    pub fn encode<'a>(&self, builder: &'a mut FlatBufferBuilder) -> &'a [u8] {
71        let function_name = builder.create_string(&self.function_name);
72
73        let function_call_type = match self.function_call_type {
74            FunctionCallType::Guest => FbFunctionCallType::guest,
75            FunctionCallType::Host => FbFunctionCallType::host,
76        };
77
78        let expected_return_type = self.expected_return_type.into();
79
80        let parameters = match &self.parameters {
81            Some(p) if !p.is_empty() => {
82                let parameter_offsets: Vec<WIPOffset<Parameter>> = p
83                    .iter()
84                    .map(|param| match param {
85                        ParameterValue::Int(i) => {
86                            let hlint = hlint::create(builder, &hlintArgs { value: *i });
87                            Parameter::create(
88                                builder,
89                                &ParameterArgs {
90                                    value_type: FbParameterValue::hlint,
91                                    value: Some(hlint.as_union_value()),
92                                },
93                            )
94                        }
95                        ParameterValue::UInt(ui) => {
96                            let hluint = hluint::create(builder, &hluintArgs { value: *ui });
97                            Parameter::create(
98                                builder,
99                                &ParameterArgs {
100                                    value_type: FbParameterValue::hluint,
101                                    value: Some(hluint.as_union_value()),
102                                },
103                            )
104                        }
105                        ParameterValue::Long(l) => {
106                            let hllong = hllong::create(builder, &hllongArgs { value: *l });
107                            Parameter::create(
108                                builder,
109                                &ParameterArgs {
110                                    value_type: FbParameterValue::hllong,
111                                    value: Some(hllong.as_union_value()),
112                                },
113                            )
114                        }
115                        ParameterValue::ULong(ul) => {
116                            let hlulong = hlulong::create(builder, &hlulongArgs { value: *ul });
117                            Parameter::create(
118                                builder,
119                                &ParameterArgs {
120                                    value_type: FbParameterValue::hlulong,
121                                    value: Some(hlulong.as_union_value()),
122                                },
123                            )
124                        }
125                        ParameterValue::Float(f) => {
126                            let hlfloat = hlfloat::create(builder, &hlfloatArgs { value: *f });
127                            Parameter::create(
128                                builder,
129                                &ParameterArgs {
130                                    value_type: FbParameterValue::hlfloat,
131                                    value: Some(hlfloat.as_union_value()),
132                                },
133                            )
134                        }
135                        ParameterValue::Double(d) => {
136                            let hldouble = hldouble::create(builder, &hldoubleArgs { value: *d });
137                            Parameter::create(
138                                builder,
139                                &ParameterArgs {
140                                    value_type: FbParameterValue::hldouble,
141                                    value: Some(hldouble.as_union_value()),
142                                },
143                            )
144                        }
145                        ParameterValue::Bool(b) => {
146                            let hlbool = hlbool::create(builder, &hlboolArgs { value: *b });
147                            Parameter::create(
148                                builder,
149                                &ParameterArgs {
150                                    value_type: FbParameterValue::hlbool,
151                                    value: Some(hlbool.as_union_value()),
152                                },
153                            )
154                        }
155                        ParameterValue::String(s) => {
156                            let val = builder.create_string(s.as_str());
157                            let hlstring =
158                                hlstring::create(builder, &hlstringArgs { value: Some(val) });
159                            Parameter::create(
160                                builder,
161                                &ParameterArgs {
162                                    value_type: FbParameterValue::hlstring,
163                                    value: Some(hlstring.as_union_value()),
164                                },
165                            )
166                        }
167                        ParameterValue::VecBytes(v) => {
168                            let vec_bytes = builder.create_vector(v);
169                            let hlvecbytes = hlvecbytes::create(
170                                builder,
171                                &hlvecbytesArgs {
172                                    value: Some(vec_bytes),
173                                },
174                            );
175                            Parameter::create(
176                                builder,
177                                &ParameterArgs {
178                                    value_type: FbParameterValue::hlvecbytes,
179                                    value: Some(hlvecbytes.as_union_value()),
180                                },
181                            )
182                        }
183                    })
184                    .collect();
185                Some(builder.create_vector(&parameter_offsets))
186            }
187            _ => None,
188        };
189
190        let function_call = FbFunctionCall::create(
191            builder,
192            &FbFunctionCallArgs {
193                function_name: Some(function_name),
194                parameters,
195                function_call_type,
196                expected_return_type,
197            },
198        );
199        builder.finish_size_prefixed(function_call, None);
200        builder.finished_data()
201    }
202}
203
204#[cfg_attr(feature = "tracing", instrument(err(Debug), skip_all, parent = Span::current(), level= "Trace"))]
205pub fn validate_guest_function_call_buffer(function_call_buffer: &[u8]) -> Result<()> {
206    let guest_function_call_fb = size_prefixed_root::<FbFunctionCall>(function_call_buffer)
207        .map_err(|e| anyhow::anyhow!("Error reading function call buffer: {:?}", e))?;
208    match guest_function_call_fb.function_call_type() {
209        FbFunctionCallType::guest => Ok(()),
210        other => {
211            bail!("Invalid function call type: {:?}", other);
212        }
213    }
214}
215
216#[cfg_attr(feature = "tracing", instrument(err(Debug), skip_all, parent = Span::current(), level= "Trace"))]
217pub fn validate_host_function_call_buffer(function_call_buffer: &[u8]) -> Result<()> {
218    let host_function_call_fb = size_prefixed_root::<FbFunctionCall>(function_call_buffer)
219        .map_err(|e| anyhow::anyhow!("Error reading function call buffer: {:?}", e))?;
220    match host_function_call_fb.function_call_type() {
221        FbFunctionCallType::host => Ok(()),
222        other => {
223            bail!("Invalid function call type: {:?}", other);
224        }
225    }
226}
227
228impl TryFrom<&[u8]> for FunctionCall {
229    type Error = Error;
230    #[cfg_attr(feature = "tracing", instrument(err(Debug), skip_all, parent = Span::current(), level= "Trace"))]
231    fn try_from(value: &[u8]) -> Result<Self> {
232        let function_call_fb = size_prefixed_root::<FbFunctionCall>(value)
233            .map_err(|e| anyhow::anyhow!("Error reading function call buffer: {:?}", e))?;
234        let function_name = function_call_fb.function_name();
235        let function_call_type = match function_call_fb.function_call_type() {
236            FbFunctionCallType::guest => FunctionCallType::Guest,
237            FbFunctionCallType::host => FunctionCallType::Host,
238            other => {
239                bail!("Invalid function call type: {:?}", other);
240            }
241        };
242        let expected_return_type = function_call_fb.expected_return_type().try_into()?;
243
244        let parameters = function_call_fb
245            .parameters()
246            .map(|v| {
247                v.iter()
248                    .map(|p| p.try_into())
249                    .collect::<Result<Vec<ParameterValue>>>()
250            })
251            .transpose()?;
252
253        Ok(Self {
254            function_name: function_name.to_string(),
255            parameters,
256            function_call_type,
257            expected_return_type,
258        })
259    }
260}
261
262#[cfg(test)]
263mod tests {
264    use alloc::vec;
265
266    use super::*;
267    use crate::flatbuffer_wrappers::function_types::ReturnType;
268
269    #[test]
270    fn read_from_flatbuffer() -> Result<()> {
271        let mut builder = FlatBufferBuilder::new();
272        let test_data = FunctionCall::new(
273            "PrintTwelveArgs".to_string(),
274            Some(vec![
275                ParameterValue::String("1".to_string()),
276                ParameterValue::Int(2),
277                ParameterValue::Long(3),
278                ParameterValue::String("4".to_string()),
279                ParameterValue::String("5".to_string()),
280                ParameterValue::Bool(true),
281                ParameterValue::Bool(false),
282                ParameterValue::UInt(8),
283                ParameterValue::ULong(9),
284                ParameterValue::Int(10),
285                ParameterValue::Float(3.123),
286                ParameterValue::Double(0.01),
287            ]),
288            FunctionCallType::Guest,
289            ReturnType::Int,
290        )
291        .encode(&mut builder);
292
293        let function_call = FunctionCall::try_from(test_data)?;
294        assert_eq!(function_call.function_name, "PrintTwelveArgs");
295        assert!(function_call.parameters.is_some());
296        let parameters = function_call.parameters.unwrap();
297        assert_eq!(parameters.len(), 12);
298        let expected_parameters = vec![
299            ParameterValue::String("1".to_string()),
300            ParameterValue::Int(2),
301            ParameterValue::Long(3),
302            ParameterValue::String("4".to_string()),
303            ParameterValue::String("5".to_string()),
304            ParameterValue::Bool(true),
305            ParameterValue::Bool(false),
306            ParameterValue::UInt(8),
307            ParameterValue::ULong(9),
308            ParameterValue::Int(10),
309            ParameterValue::Float(3.123),
310            ParameterValue::Double(0.01),
311        ];
312        assert!(expected_parameters == parameters);
313        assert_eq!(function_call.function_call_type, FunctionCallType::Guest);
314
315        Ok(())
316    }
317}