Skip to content

Commit b45a3f2

Browse files
committed
Introduce HostFunctions newtype for sandbox construction
Signed-off-by: Ludvig Liljenberg <4257730+ludfjig@users.noreply.github.com>
1 parent 2450118 commit b45a3f2

4 files changed

Lines changed: 118 additions & 19 deletions

File tree

src/hyperlight_host/src/func/host_functions.rs

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,8 @@ impl Registerable for UninitializedSandbox {
5252
return_type: Output::TYPE,
5353
};
5454

55-
(*hfs).register_host_function(name.to_string(), entry)
55+
(*hfs).register_host_function(name.to_string(), entry);
56+
Ok(())
5657
}
5758
}
5859

@@ -92,7 +93,26 @@ impl Registerable for crate::MultiUseSandbox {
9293
return_type: Output::TYPE,
9394
};
9495

95-
(*hfs).register_host_function(name.to_string(), entry)
96+
(*hfs).register_host_function(name.to_string(), entry);
97+
Ok(())
98+
}
99+
}
100+
101+
impl Registerable for crate::HostFunctions {
102+
fn register_host_function<Args: ParameterTuple, Output: SupportedReturnType>(
103+
&mut self,
104+
name: &str,
105+
hf: impl Into<HostFunction<Output, Args>>,
106+
) -> Result<()> {
107+
let entry = FunctionEntry {
108+
function: hf.into().into(),
109+
parameter_types: Args::TYPE,
110+
return_type: Output::TYPE,
111+
};
112+
113+
self.inner_mut()
114+
.register_host_function(name.to_string(), entry);
115+
Ok(())
96116
}
97117
}
98118

@@ -236,7 +256,7 @@ pub(crate) fn register_host_function<Args: ParameterTuple, Output: SupportedRetu
236256
.host_funcs
237257
.try_lock()
238258
.map_err(|e| new_error!("Error locking at {}:{}: {}", file!(), line!(), e))?
239-
.register_host_function(name.to_string(), entry)?;
259+
.register_host_function(name.to_string(), entry);
240260

241261
Ok(())
242262
}

src/hyperlight_host/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,9 @@ pub use hypervisor::virtual_machine::is_hypervisor_present;
9191
pub use sandbox::MultiUseSandbox;
9292
/// The re-export for the `UninitializedSandbox` type
9393
pub use sandbox::UninitializedSandbox;
94+
/// A collection of host functions that can be supplied to a sandbox
95+
/// constructor (e.g. [`MultiUseSandbox::from_snapshot`]).
96+
pub use sandbox::host_funcs::HostFunctions;
9497
/// The re-export for the `GuestBinary` type
9598
pub use sandbox::uninitialized::GuestBinary;
9699
/// The re-export for the `GuestCounter` type

src/hyperlight_host/src/sandbox/host_funcs.rs

Lines changed: 89 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,76 @@ pub struct FunctionRegistry {
3535
functions_map: HashMap<String, FunctionEntry>,
3636
}
3737

38-
impl From<&mut FunctionRegistry> for HostFunctionDetails {
39-
fn from(registry: &mut FunctionRegistry) -> Self {
38+
/// A collection of host functions that can be supplied to a sandbox
39+
/// constructor (e.g. [`crate::MultiUseSandbox::from_snapshot`]) to
40+
/// expose host-side functionality to the guest.
41+
///
42+
/// Use [`HostFunctions::default`] to start with the standard
43+
/// `HostPrint` function pre-registered (matches the registry that the
44+
/// regular `UninitializedSandbox` → `evolve()` path constructs), or
45+
/// [`HostFunctions::new`] to start with an empty registry.
46+
///
47+
/// Add additional host functions via the
48+
/// [`crate::func::Registerable`] trait, just as you would on an
49+
/// `UninitializedSandbox`.
50+
///
51+
/// ```no_run
52+
/// # use hyperlight_host::{HostFunctions, Result};
53+
/// # use hyperlight_host::func::Registerable;
54+
/// # fn example() -> Result<()> {
55+
/// // Default: HostPrint already registered.
56+
/// let mut funcs = HostFunctions::default();
57+
/// funcs.register_host_function("Add", |a: i32, b: i32| Ok(a + b))?;
58+
/// # Ok(())
59+
/// # }
60+
/// ```
61+
pub struct HostFunctions(FunctionRegistry);
62+
63+
impl HostFunctions {
64+
/// Create an empty `HostFunctions` with no host functions
65+
/// registered.
66+
///
67+
/// Most callers want [`HostFunctions::default`] instead, which
68+
/// pre-registers the standard `HostPrint` function.
69+
pub fn new() -> Self {
70+
Self(FunctionRegistry::default())
71+
}
72+
73+
/// Consume this `HostFunctions` and return the inner registry.
74+
pub(crate) fn into_inner(self) -> FunctionRegistry {
75+
self.0
76+
}
77+
78+
/// Borrow the inner registry mutably.
79+
pub(crate) fn inner_mut(&mut self) -> &mut FunctionRegistry {
80+
&mut self.0
81+
}
82+
83+
/// Borrow the inner registry immutably.
84+
pub(crate) fn inner(&self) -> &FunctionRegistry {
85+
&self.0
86+
}
87+
}
88+
89+
impl Default for HostFunctions {
90+
/// Create a `HostFunctions` pre-populated with the standard
91+
/// `HostPrint` function (writes UTF-8 strings to the host's
92+
/// stdout in green).
93+
///
94+
/// This matches the default registry installed by
95+
/// `UninitializedSandbox::new()`, so a snapshot taken from a
96+
/// regular sandbox can be loaded with
97+
/// `MultiUseSandbox::from_snapshot(snap, HostFunctions::default())`
98+
/// without registering anything else.
99+
///
100+
/// Use [`HostFunctions::new`] for an empty registry.
101+
fn default() -> Self {
102+
Self(FunctionRegistry::with_default_host_print())
103+
}
104+
}
105+
106+
impl From<&FunctionRegistry> for HostFunctionDetails {
107+
fn from(registry: &FunctionRegistry) -> Self {
40108
let host_functions = registry
41109
.functions_map
42110
.iter()
@@ -61,15 +129,26 @@ pub struct FunctionEntry {
61129

62130
impl FunctionRegistry {
63131
/// Register a host function with the sandbox.
64-
#[instrument(err(Debug), skip_all, parent = Span::current(), level = "Trace")]
65-
pub(crate) fn register_host_function(
66-
&mut self,
67-
name: String,
68-
func: FunctionEntry,
69-
) -> Result<()> {
132+
#[instrument(skip_all, parent = Span::current(), level = "Trace")]
133+
pub(crate) fn register_host_function(&mut self, name: String, func: FunctionEntry) {
70134
self.functions_map.insert(name, func);
135+
}
71136

72-
Ok(())
137+
/// Create a `FunctionRegistry` pre-populated with the default
138+
/// `HostPrint` function (writes to stdout with green text).
139+
pub(crate) fn with_default_host_print() -> Self {
140+
use crate::func::host_functions::HostFunction;
141+
use crate::func::{ParameterTuple, SupportedReturnType};
142+
143+
let mut registry = Self::default();
144+
let hf: HostFunction<i32, (String,)> = default_writer_func.into();
145+
let entry = FunctionEntry {
146+
function: hf.into(),
147+
parameter_types: <(String,)>::TYPE,
148+
return_type: <i32 as SupportedReturnType>::TYPE,
149+
};
150+
registry.register_host_function("HostPrint".to_string(), entry);
151+
registry
73152
}
74153

75154
/// Assuming a host function called `"HostPrint"` exists, and takes a
@@ -118,7 +197,7 @@ impl FunctionRegistry {
118197

119198
/// The default writer function is to write to stdout with green text.
120199
#[instrument(err(Debug), skip_all, parent = Span::current(), level = "Trace")]
121-
pub(super) fn default_writer_func(s: String) -> Result<i32> {
200+
fn default_writer_func(s: String) -> Result<i32> {
122201
match std::io::stdout().is_terminal() {
123202
false => {
124203
print!("{}", s);

src/hyperlight_host/src/sandbox/uninitialized.rs

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ use std::sync::{Arc, Mutex};
2222
use tracing::{Span, instrument};
2323
use tracing_core::LevelFilter;
2424

25-
use super::host_funcs::{FunctionRegistry, default_writer_func};
25+
use super::host_funcs::FunctionRegistry;
2626
use super::snapshot::Snapshot;
2727
use super::uninitialized_evolve::evolve_impl_multi_use;
2828
use crate::func::host_functions::{HostFunction, register_host_function};
@@ -365,9 +365,9 @@ impl UninitializedSandbox {
365365
let mem_mgr_wrapper =
366366
SandboxMemoryManager::<ExclusiveSharedMemory>::from_snapshot(snapshot.as_ref())?;
367367

368-
let host_funcs = Arc::new(Mutex::new(FunctionRegistry::default()));
368+
let host_funcs = Arc::new(Mutex::new(FunctionRegistry::with_default_host_print()));
369369

370-
let mut sandbox = Self {
370+
let sandbox = Self {
371371
host_funcs,
372372
mgr: mem_mgr_wrapper,
373373
max_guest_log_level: None,
@@ -383,9 +383,6 @@ impl UninitializedSandbox {
383383
pending_file_mappings: Vec::new(),
384384
};
385385

386-
// If we were passed a writer for host print register it otherwise use the default.
387-
sandbox.register_print(default_writer_func)?;
388-
389386
crate::debug!("Sandbox created: {:#?}", sandbox);
390387

391388
Ok(sandbox)

0 commit comments

Comments
 (0)