diff --git a/src/hyperlight_component_util/src/emit.rs b/src/hyperlight_component_util/src/emit.rs index d6c67cdf7..40a29eb76 100644 --- a/src/hyperlight_component_util/src/emit.rs +++ b/src/hyperlight_component_util/src/emit.rs @@ -124,6 +124,7 @@ pub struct Mod { pub items: TokenStream, pub traits: BTreeMap, pub impls: BTreeMap<(Vec, Ident), TokenStream>, + pub emitted_type_names: BTreeSet, } impl Mod { pub fn empty() -> Self { @@ -132,6 +133,7 @@ impl Mod { items: TokenStream::new(), traits: BTreeMap::new(), impls: BTreeMap::new(), + emitted_type_names: BTreeSet::new(), } } /// Get a reference to a sub-module, creating it if necessary diff --git a/src/hyperlight_component_util/src/host.rs b/src/hyperlight_component_util/src/host.rs index de0b49ec0..efe1411b3 100644 --- a/src/hyperlight_component_util/src/host.rs +++ b/src/hyperlight_component_util/src/host.rs @@ -46,6 +46,7 @@ fn emit_export_extern_decl<'a, 'b, 'c>( .map(|p| rtypes::emit_func_param(s, p)) .collect::>(); let result_decl = rtypes::emit_func_result(s, &ft.result); + let result_decl = quote! { ::std::result::Result<#result_decl, ::hyperlight_host::error::HyperlightError> }; let hln = emit_fn_hl_name(s, ed.kebab_name); let ret = format_ident!("ret"); let marshal = ft @@ -66,11 +67,11 @@ fn emit_export_extern_decl<'a, 'b, 'c>( #hln, marshalled, ); - let ::std::result::Result::Ok(#ret) = #ret else { panic!("bad return from guest {:?}", #ret) }; + let #ret = #ret?; #[allow(clippy::unused_unit)] let mut rts = self.rt.lock().unwrap(); #[allow(clippy::unused_unit)] - #unmarshal + Ok(#unmarshal) } } } @@ -121,7 +122,7 @@ fn emit_export_instance<'a, 'b, 'c>(s: &'c mut State<'a, 'b>, wn: WitName, it: & let ns = wn.namespace_path(); let nsi = wn.namespace_idents(); - let trait_name = kebab_to_type(wn.name); + let trait_name = kebab_to_exports_name(wn.name); let r#trait = s.r#trait(&nsi, trait_name.clone()); let tvs = r#trait .tvs diff --git a/src/hyperlight_component_util/src/resource.rs b/src/hyperlight_component_util/src/resource.rs index a84e46bd8..aa6833b8a 100644 --- a/src/hyperlight_component_util/src/resource.rs +++ b/src/hyperlight_component_util/src/resource.rs @@ -99,7 +99,7 @@ pub fn emit_tables<'a, 'b, 'c>( #sphantom } impl #rtsid { - fn new() -> Self { + pub(crate) fn new() -> Self { #rtsid { #(#inits,)* _phantomI: ::core::marker::PhantomData, diff --git a/src/hyperlight_component_util/src/rtypes.rs b/src/hyperlight_component_util/src/rtypes.rs index d6c4a8b38..a24ba9979 100644 --- a/src/hyperlight_component_util/src/rtypes.rs +++ b/src/hyperlight_component_util/src/rtypes.rs @@ -641,6 +641,11 @@ fn emit_extern_decl<'a, 'b, 'c>( .map(|p| emit_func_param(&mut s, p)) .collect::>(); let result = emit_func_result(&mut s, &ft.result); + let result = if !s.is_guest && s.is_export { + quote! { ::std::result::Result<#result, ::hyperlight_host::error::HyperlightError> } + } else { + result + }; quote! { fn #n(&mut self, #(#params),*) -> #result; } @@ -663,12 +668,22 @@ fn emit_extern_decl<'a, 'b, 'c>( } ResourceItemName::Method(n) => { let result = emit_func_result(&mut sv, &ft.result); + let result = if !sv.is_guest && sv.is_export { + quote! { ::std::result::Result<#result, ::hyperlight_host::error::HyperlightError> } + } else { + result + }; sv.cur_trait().items.extend(quote! { fn #n(&mut self, #(#params),*) -> #result; }); } ResourceItemName::Static(n) => { let result = emit_func_result(&mut sv, &ft.result); + let result = if !sv.is_guest && sv.is_export { + quote! { ::std::result::Result<#result, ::hyperlight_host::error::HyperlightError> } + } else { + result + }; sv.cur_trait().items.extend(quote! { fn #n(&mut self, #(#params),*) -> #result; }); @@ -691,6 +706,9 @@ fn emit_extern_decl<'a, 'b, 'c>( ) -> TokenStream { let id = kebab_to_type(ed.kebab_name); let mut s = s.helper(); + if !s.cur_mod().emitted_type_names.insert(id.clone()) { + return TokenStream::new(); + } let t = emit_defined(&mut s, v, id, t); s.cur_mod().items.extend(t); @@ -712,6 +730,9 @@ fn emit_extern_decl<'a, 'b, 'c>( let rn = kebab_to_type(ed.kebab_name); s.add_helper_supertrait(rn.clone()); let mut s = s.helper(); + if !s.cur_mod().emitted_type_names.insert(rn.clone()) { + return quote! {}; + } s.cur_trait = Some(rn.clone()); s.cur_trait().items.extend(quote! { type T: ::core::marker::Send; @@ -729,7 +750,12 @@ fn emit_extern_decl<'a, 'b, 'c>( emit_instance(&mut s, wn.clone(), it); let nsids = wn.namespace_idents(); - let repr = s.r#trait(&nsids, kebab_to_type(wn.name)); + let trait_name = if !s.is_guest && s.is_export { + kebab_to_exports_name(wn.name) + } else { + kebab_to_type(wn.name) + }; + let repr = s.r#trait(&nsids, trait_name.clone()); let vs = if !repr.tvs.is_empty() { let vs = repr.tvs.clone(); let tvs = vs @@ -745,9 +771,9 @@ fn emit_extern_decl<'a, 'b, 'c>( let tns = wn.namespace_path(); let tn = kebab_to_type(wn.name); let trait_bound = if tns.is_empty() { - quote! { #rp #tn } + quote! { #rp #trait_name } } else { - quote! { #rp #tns::#tn } + quote! { #rp #tns::#trait_name } }; quote! { type #tn: #trait_bound #vs; @@ -766,7 +792,11 @@ fn emit_instance<'a, 'b, 'c>(s: &'c mut State<'a, 'b>, wn: WitName, it: &'c Inst tracing::debug!("emitting instance {:?}", wn); let mut s = s.with_cursor(wn.namespace_idents()); - let name = kebab_to_type(wn.name); + let name = if !s.is_guest && s.is_export { + kebab_to_exports_name(wn.name) + } else { + kebab_to_type(wn.name) + }; s.cur_helper_mod = Some(kebab_to_namespace(wn.name)); s.cur_trait = Some(name.clone()); diff --git a/src/hyperlight_host/tests/wit_test.rs b/src/hyperlight_host/tests/wit_test.rs index e96f24089..01d077887 100644 --- a/src/hyperlight_host/tests/wit_test.rs +++ b/src/hyperlight_host/tests/wit_test.rs @@ -293,9 +293,12 @@ fn sb() -> TestSandbox { mod wit_test { + use hyperlight_host::error::HyperlightError; use proptest::prelude::*; - use crate::bindings::test::wit::{Roundtrip, TestExports, TestHostResource, roundtrip}; + use crate::bindings::test::wit::{ + FailableExports, RoundtripExports, TestExports, TestHostResourceExports, roundtrip, + }; use crate::sb; prop_compose! { @@ -347,7 +350,7 @@ mod wit_test { proptest! { #[test] fn $fn(x $($ty)*) { - assert_eq!(x, sb().roundtrip().$fn(x.clone())) + assert_eq!(x, sb().roundtrip().$fn(x.clone()).unwrap()) } } } @@ -394,7 +397,7 @@ mod wit_test { #[test] fn test_roundtrip_no_result() { - sb().roundtrip().roundtrip_no_result(42); + sb().roundtrip().roundtrip_no_result(42).unwrap(); } use std::sync::atomic::Ordering::Relaxed; @@ -404,7 +407,7 @@ mod wit_test { let guard = crate::SERIALIZE_TEST_RESOURCE_TESTS.lock(); crate::HAS_BEEN_DROPPED.store(false, Relaxed); { - sb().test_host_resource().test_uses_locally(); + sb().test_host_resource().test_uses_locally().unwrap(); } assert!(crate::HAS_BEEN_DROPPED.load(Relaxed)); drop(guard); @@ -416,14 +419,24 @@ mod wit_test { { let mut sb = sb(); let inst = sb.test_host_resource(); - let r = inst.test_makes(); - inst.test_accepts_borrow(&r); - inst.test_accepts_own(r); - inst.test_returns(); + let r = inst.test_makes().unwrap(); + inst.test_accepts_borrow(&r).unwrap(); + inst.test_accepts_own(r).unwrap(); + inst.test_returns().unwrap(); } assert!(crate::HAS_BEEN_DROPPED.load(Relaxed)); drop(guard); } + + #[test] + fn test_guest_call_error_returns_error() { + let mut sb = sb(); + let err = sb.failable().guest_panic().unwrap_err(); + assert!(matches!( + err, + HyperlightError::GuestAborted(_, msg) if msg.contains("deliberate guest panic") + )); + } } mod pick_world_bindings { diff --git a/src/tests/rust_guests/witguest/guest.wit b/src/tests/rust_guests/witguest/guest.wit index 9ed164b80..5fa3982a9 100644 --- a/src/tests/rust_guests/witguest/guest.wit +++ b/src/tests/rust_guests/witguest/guest.wit @@ -5,6 +5,7 @@ world test { import host-resource; export roundtrip; export test-host-resource; + export failable; } interface roundtrip { @@ -89,4 +90,8 @@ interface test-host-resource { test-accepts-borrow: func(x: borrow); test-accepts-own: func(x: own); test-returns: func() -> own; +} + +interface failable { + guest-panic: func() -> string; } \ No newline at end of file diff --git a/src/tests/rust_guests/witguest/src/main.rs b/src/tests/rust_guests/witguest/src/main.rs index 307d9cca6..c2c8e02de 100644 --- a/src/tests/rust_guests/witguest/src/main.rs +++ b/src/tests/rust_guests/witguest/src/main.rs @@ -166,6 +166,12 @@ impl test::wit::Roundtrip for Guest { } } +impl test::wit::Failable for Guest { + fn guest_panic(&mut self) -> alloc::string::String { + panic!("deliberate guest panic") + } +} + use alloc::string::ToString; use test::wit::host_resource::Testresource; @@ -214,6 +220,10 @@ impl test::wit::TestExports for Guest { fn test_host_resource(&mut self) -> &mut Self { self } + type Failable = Self; + fn failable(&mut self) -> &mut Self { + self + } } static GUEST_STATE: Mutex = Mutex::new(Guest {