use std::sync::{Arc, Mutex};
use hyperlight_common::flatbuffer_wrappers::function_types::{ParameterValue, ReturnValue};
use hyperlight_common::for_each_tuple;
use hyperlight_common::func::{Error as FuncError, Function, ResultType};
use super::{ParameterTuple, SupportedReturnType};
use crate::sandbox::UninitializedSandbox;
use crate::sandbox::host_funcs::FunctionEntry;
use crate::{HyperlightError, Result, new_error};
pub trait Registerable {
fn register_host_function<Args: ParameterTuple, Output: SupportedReturnType>(
&mut self,
name: &str,
hf: impl Into<HostFunction<Output, Args>>,
) -> Result<()>;
}
impl Registerable for UninitializedSandbox {
fn register_host_function<Args: ParameterTuple, Output: SupportedReturnType>(
&mut self,
name: &str,
hf: impl Into<HostFunction<Output, Args>>,
) -> Result<()> {
let mut hfs = self
.host_funcs
.try_lock()
.map_err(|e| new_error!("Error locking at {}:{}: {}", file!(), line!(), e))?;
let entry = FunctionEntry {
function: hf.into().into(),
parameter_types: Args::TYPE,
return_type: Output::TYPE,
};
(*hfs).register_host_function(name.to_string(), entry)
}
}
#[derive(Clone)]
pub struct HostFunction<Output, Args>
where
Args: ParameterTuple,
Output: SupportedReturnType,
{
func: Arc<dyn Function<Output, Args, HyperlightError> + Send + Sync + 'static>,
}
pub(crate) struct TypeErasedHostFunction {
func: Box<dyn Fn(Vec<ParameterValue>) -> Result<ReturnValue> + Send + Sync + 'static>,
}
impl<Args, Output> HostFunction<Output, Args>
where
Args: ParameterTuple,
Output: SupportedReturnType,
{
pub fn call(&self, args: Args) -> Result<Output> {
self.func.call(args)
}
}
impl TypeErasedHostFunction {
pub(crate) fn call(&self, args: Vec<ParameterValue>) -> Result<ReturnValue> {
(self.func)(args)
}
}
impl From<FuncError> for HyperlightError {
fn from(e: FuncError) -> Self {
match e {
FuncError::ParameterValueConversionFailure(from, to) => {
HyperlightError::ParameterValueConversionFailure(from, to)
}
FuncError::ReturnValueConversionFailure(from, to) => {
HyperlightError::ReturnValueConversionFailure(from, to)
}
FuncError::UnexpectedNoOfArguments(got, expected) => {
HyperlightError::UnexpectedNoOfArguments(got, expected)
}
FuncError::UnexpectedParameterValueType(got, expected) => {
HyperlightError::UnexpectedParameterValueType(got, expected)
}
FuncError::UnexpectedReturnValueType(got, expected) => {
HyperlightError::UnexpectedReturnValueType(got, expected)
}
}
}
}
impl<Args, Output> From<HostFunction<Output, Args>> for TypeErasedHostFunction
where
Args: ParameterTuple,
Output: SupportedReturnType,
{
fn from(func: HostFunction<Output, Args>) -> TypeErasedHostFunction {
TypeErasedHostFunction {
func: Box::new(move |args: Vec<ParameterValue>| {
let args = Args::from_value(args)?;
Ok(func.call(args)?.into_value())
}),
}
}
}
macro_rules! impl_host_function {
([$N:expr] ($($p:ident: $P:ident),*)) => {
impl<F, R, $($P),*> From<F> for HostFunction<R::ReturnType, ($($P,)*)>
where
F: FnMut($($P),*) -> R + Send + 'static,
($($P,)*): ParameterTuple,
R: ResultType<HyperlightError>,
{
fn from(func: F) -> HostFunction<R::ReturnType, ($($P,)*)> {
let func = Mutex::new(func);
let func = move |$($p: $P,)*| {
let mut func = func.lock().map_err(|e| new_error!("Error locking at {}:{}: {}", file!(), line!(), e))?;
(func)($($p),*).into_result()
};
let func = Arc::new(func);
HostFunction { func }
}
}
};
}
for_each_tuple!(impl_host_function);
pub(crate) fn register_host_function<Args: ParameterTuple, Output: SupportedReturnType>(
func: impl Into<HostFunction<Output, Args>>,
sandbox: &mut UninitializedSandbox,
name: &str,
) -> Result<()> {
let func = func.into().into();
let entry = FunctionEntry {
function: func,
parameter_types: Args::TYPE,
return_type: Output::TYPE,
};
sandbox
.host_funcs
.try_lock()
.map_err(|e| new_error!("Error locking at {}:{}: {}", file!(), line!(), e))?
.register_host_function(name.to_string(), entry)?;
Ok(())
}