Skip to content

Commit a0074fe

Browse files
committed
Use function type traits to improve registration ergonomics
Signed-off-by: Jorge Prendes <jorge.prendes@gmail.com>
1 parent 3af19d5 commit a0074fe

File tree

5 files changed

+183
-2
lines changed

5 files changed

+183
-2
lines changed

src/hyperlight_guest/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ Provides only the essential building blocks for interacting with the host enviro
1414
[dependencies]
1515
anyhow = { version = "1.0.99", default-features = false }
1616
serde_json = { version = "1.0", default-features = false, features = ["alloc"] }
17-
hyperlight-common = { workspace = true }
17+
hyperlight-common = { workspace = true, default-features = false }
1818
hyperlight-guest-tracing = { workspace = true, default-features = false }
1919
flatbuffers = { version= "25.2.10", default-features = false }
2020

src/hyperlight_guest/src/error.rs

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,10 @@ limitations under the License.
1515
*/
1616

1717
use alloc::format;
18-
use alloc::string::String;
18+
use alloc::string::{String, ToString as _};
1919

2020
use hyperlight_common::flatbuffer_wrappers::guest_error::ErrorCode;
21+
use hyperlight_common::func::Error as FuncError;
2122
use {anyhow, serde_json};
2223

2324
pub type Result<T> = core::result::Result<T, HyperlightGuestError>;
@@ -51,3 +52,30 @@ impl From<serde_json::Error> for HyperlightGuestError {
5152
}
5253
}
5354
}
55+
56+
impl From<FuncError> for HyperlightGuestError {
57+
fn from(e: FuncError) -> Self {
58+
match e {
59+
FuncError::ParameterValueConversionFailure(..) => HyperlightGuestError::new(
60+
ErrorCode::GuestFunctionParameterTypeMismatch,
61+
e.to_string(),
62+
),
63+
FuncError::ReturnValueConversionFailure(..) => HyperlightGuestError::new(
64+
ErrorCode::GuestFunctionParameterTypeMismatch,
65+
e.to_string(),
66+
),
67+
FuncError::UnexpectedNoOfArguments(..) => HyperlightGuestError::new(
68+
ErrorCode::GuestFunctionIncorrecNoOfParameters,
69+
e.to_string(),
70+
),
71+
FuncError::UnexpectedParameterValueType(..) => HyperlightGuestError::new(
72+
ErrorCode::GuestFunctionParameterTypeMismatch,
73+
e.to_string(),
74+
),
75+
FuncError::UnexpectedReturnValueType(..) => HyperlightGuestError::new(
76+
ErrorCode::GuestFunctionParameterTypeMismatch,
77+
e.to_string(),
78+
),
79+
}
80+
}
81+
}

src/hyperlight_guest_bin/src/guest_function/definition.rs

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,14 @@ use alloc::format;
1818
use alloc::string::String;
1919
use alloc::vec::Vec;
2020

21+
use hyperlight_common::flatbuffer_wrappers::function_call::FunctionCall;
2122
use hyperlight_common::flatbuffer_wrappers::function_types::{ParameterType, ReturnType};
2223
use hyperlight_common::flatbuffer_wrappers::guest_error::ErrorCode;
24+
use hyperlight_common::flatbuffer_wrappers::util::get_flatbuffer_result;
25+
use hyperlight_common::for_each_tuple;
26+
use hyperlight_common::func::{
27+
Function, ParameterTuple, ResultType, ReturnValue, SupportedReturnType,
28+
};
2329
use hyperlight_guest::error::{HyperlightGuestError, Result};
2430

2531
/// The definition of a function exposed from the guest to the host
@@ -35,6 +41,101 @@ pub struct GuestFunctionDefinition {
3541
pub function_pointer: usize,
3642
}
3743

44+
/// Trait for functions that can be converted to a `fn(&FunctionCall) -> Result<Vec<u8>>`
45+
#[doc(hidden)]
46+
pub trait IntoGuestFunction<Output, Args>
47+
where
48+
Self: Function<Output, Args, HyperlightGuestError>,
49+
Self: Copy + 'static,
50+
Output: SupportedReturnType,
51+
Args: ParameterTuple,
52+
{
53+
#[doc(hidden)]
54+
const ASSERT_ZERO_SIZED: ();
55+
56+
/// Convert the function into a `fn(&FunctionCall) -> Result<Vec<u8>>`
57+
fn into_guest_function(self) -> fn(&FunctionCall) -> Result<Vec<u8>>;
58+
}
59+
60+
/// Trait for functions that can be converted to a `GuestFunctionDefinition`
61+
pub trait AsGuestFunctionDefinition<Output, Args>
62+
where
63+
Self: Function<Output, Args, HyperlightGuestError>,
64+
Self: IntoGuestFunction<Output, Args>,
65+
Output: SupportedReturnType,
66+
Args: ParameterTuple,
67+
{
68+
/// Get the `GuestFunctionDefinition` for this function
69+
fn as_guest_function_definition(&self, name: impl Into<String>) -> GuestFunctionDefinition;
70+
}
71+
72+
fn into_flatbuffer_result(value: ReturnValue) -> Vec<u8> {
73+
match value {
74+
ReturnValue::Void(()) => get_flatbuffer_result(()),
75+
ReturnValue::Int(i) => get_flatbuffer_result(i),
76+
ReturnValue::UInt(u) => get_flatbuffer_result(u),
77+
ReturnValue::Long(l) => get_flatbuffer_result(l),
78+
ReturnValue::ULong(ul) => get_flatbuffer_result(ul),
79+
ReturnValue::Float(f) => get_flatbuffer_result(f),
80+
ReturnValue::Double(d) => get_flatbuffer_result(d),
81+
ReturnValue::Bool(b) => get_flatbuffer_result(b),
82+
ReturnValue::String(s) => get_flatbuffer_result(s.as_str()),
83+
ReturnValue::VecBytes(v) => get_flatbuffer_result(v.as_slice()),
84+
}
85+
}
86+
87+
macro_rules! impl_host_function {
88+
([$N:expr] ($($p:ident: $P:ident),*)) => {
89+
impl<F, R, $($P),*> IntoGuestFunction<R::ReturnType, ($($P,)*)> for F
90+
where
91+
F: Fn($($P),*) -> R,
92+
F: Function<R::ReturnType, ($($P,)*), HyperlightGuestError>,
93+
F: Copy + 'static, // Copy implies that F has no Drop impl
94+
($($P,)*): ParameterTuple,
95+
R: ResultType<HyperlightGuestError>,
96+
{
97+
#[doc(hidden)]
98+
const ASSERT_ZERO_SIZED: () = const {
99+
assert!(core::mem::size_of::<Self>() == 0)
100+
};
101+
102+
fn into_guest_function(self) -> fn(&FunctionCall) -> Result<Vec<u8>> {
103+
|fc: &FunctionCall| {
104+
// SAFETY: This is safe because of the safety comment on the function.
105+
let this = unsafe { core::mem::zeroed::<F>() };
106+
let params = fc.parameters.clone().unwrap_or_default();
107+
let params = <($($P,)*) as ParameterTuple>::from_value(params)?;
108+
let result = Function::<R::ReturnType, ($($P,)*), HyperlightGuestError>::call(&this, params)?;
109+
Ok(into_flatbuffer_result(result.into_value()))
110+
}
111+
}
112+
}
113+
};
114+
}
115+
116+
impl<F, Args, Output> AsGuestFunctionDefinition<Output, Args> for F
117+
where
118+
F: IntoGuestFunction<Output, Args>,
119+
Args: ParameterTuple,
120+
Output: SupportedReturnType,
121+
{
122+
fn as_guest_function_definition(&self, name: impl Into<String>) -> GuestFunctionDefinition {
123+
let parameter_types = Args::TYPE.to_vec();
124+
let return_type = Output::TYPE;
125+
let function_pointer = self.into_guest_function();
126+
let function_pointer = function_pointer as usize;
127+
128+
GuestFunctionDefinition {
129+
function_name: name.into(),
130+
parameter_types,
131+
return_type,
132+
function_pointer,
133+
}
134+
}
135+
}
136+
137+
for_each_tuple!(impl_host_function);
138+
38139
impl GuestFunctionDefinition {
39140
/// Create a new `GuestFunctionDefinition`.
40141
pub fn new(
@@ -51,6 +152,19 @@ impl GuestFunctionDefinition {
51152
}
52153
}
53154

155+
/// Create a new `GuestFunctionDefinition` from a function that implements
156+
/// `AsGuestFunctionDefinition`.
157+
pub fn from_fn<Output, Args>(
158+
function_name: String,
159+
function: impl AsGuestFunctionDefinition<Output, Args>,
160+
) -> Self
161+
where
162+
Args: ParameterTuple,
163+
Output: SupportedReturnType,
164+
{
165+
function.as_guest_function_definition(function_name)
166+
}
167+
54168
/// Verify that `self` has same signature as the provided `parameter_types`.
55169
pub fn verify_parameters(&self, parameter_types: &[ParameterType]) -> Result<()> {
56170
// Verify that the function does not have more than `MAX_PARAMETERS` parameters.

src/hyperlight_guest_bin/src/guest_function/register.rs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,11 @@ limitations under the License.
1717
use alloc::collections::BTreeMap;
1818
use alloc::string::String;
1919

20+
use hyperlight_common::func::{ParameterTuple, SupportedReturnType};
21+
2022
use super::definition::GuestFunctionDefinition;
2123
use crate::REGISTERED_GUEST_FUNCTIONS;
24+
use crate::guest_function::definition::AsGuestFunctionDefinition;
2225

2326
/// Represents the functions that the guest exposes to the host.
2427
#[derive(Debug, Default, Clone)]
@@ -47,6 +50,18 @@ impl GuestFunctionRegister {
4750
.insert(guest_function.function_name.clone(), guest_function)
4851
}
4952

53+
pub fn register_fn<Output, Args>(
54+
&mut self,
55+
name: impl Into<String>,
56+
f: impl AsGuestFunctionDefinition<Output, Args>,
57+
) where
58+
Args: ParameterTuple,
59+
Output: SupportedReturnType,
60+
{
61+
let gfd = f.as_guest_function_definition(name);
62+
self.register(gfd);
63+
}
64+
5065
/// Gets a `GuestFunctionDefinition` by its `name` field.
5166
pub fn get(&self, function_name: &str) -> Option<&GuestFunctionDefinition> {
5267
self.guest_functions.get(function_name)
@@ -62,3 +77,19 @@ pub fn register_function(function_definition: GuestFunctionDefinition) {
6277
gfd.register(function_definition);
6378
}
6479
}
80+
81+
pub fn register_fn<Output, Args>(
82+
name: impl Into<String>,
83+
f: impl AsGuestFunctionDefinition<Output, Args>,
84+
) where
85+
Args: ParameterTuple,
86+
Output: SupportedReturnType,
87+
{
88+
unsafe {
89+
// This is currently safe, because we are single threaded, but we
90+
// should find a better way to do this, see issue #808
91+
#[allow(static_mut_refs)]
92+
let gfd = &mut REGISTERED_GUEST_FUNCTIONS;
93+
gfd.register_fn(name, f);
94+
}
95+
}

src/hyperlight_guest_bin/src/host_comm.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ use hyperlight_common::flatbuffer_wrappers::function_types::{
2626
use hyperlight_common::flatbuffer_wrappers::guest_error::ErrorCode;
2727
use hyperlight_common::flatbuffer_wrappers::host_function_details::HostFunctionDetails;
2828
use hyperlight_common::flatbuffer_wrappers::util::get_flatbuffer_result;
29+
use hyperlight_common::func::{ParameterTuple, SupportedReturnType};
2930
use hyperlight_guest::error::{HyperlightGuestError, Result};
3031

3132
const BUFFER_SIZE: usize = 1000;
@@ -45,6 +46,13 @@ where
4546
handle.call_host_function::<T>(function_name, parameters, return_type)
4647
}
4748

49+
pub fn call_host<T>(function_name: impl AsRef<str>, args: impl ParameterTuple) -> Result<T>
50+
where
51+
T: SupportedReturnType + TryFrom<ReturnValue>,
52+
{
53+
call_host_function::<T>(function_name.as_ref(), Some(args.into_value()), T::TYPE)
54+
}
55+
4856
pub fn call_host_function_without_returning_result(
4957
function_name: &str,
5058
parameters: Option<Vec<ParameterValue>>,

0 commit comments

Comments
 (0)