diff --git a/Cargo.lock b/Cargo.lock index 26019e9d4..c545d3145 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3666,9 +3666,9 @@ dependencies = [ [[package]] name = "wasmparser" -version = "0.224.1" +version = "0.235.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04f17a5917c2ddd3819e84c661fae0d6ba29d7b9c1f0e96c708c65a9c4188e11" +checksum = "161296c618fa2d63f6ed5fffd1112937e803cb9ec71b32b01a76321555660917" dependencies = [ "bitflags 2.9.1", "hashbrown", diff --git a/src/hyperlight_component_macro/Cargo.toml b/src/hyperlight_component_macro/Cargo.toml index b23c0fef9..8c90c88b3 100644 --- a/src/hyperlight_component_macro/Cargo.toml +++ b/src/hyperlight_component_macro/Cargo.toml @@ -16,7 +16,7 @@ name = "hyperlight_component_macro" proc-macro = true [dependencies] -wasmparser = { version = "0.224.0" } +wasmparser = { version = "0.235.0" } quote = { version = "1.0.38" } proc-macro2 = { version = "1.0.95" } syn = { version = "2.0.104" } diff --git a/src/hyperlight_component_util/Cargo.toml b/src/hyperlight_component_util/Cargo.toml index ece9bdb79..57451dbcd 100644 --- a/src/hyperlight_component_util/Cargo.toml +++ b/src/hyperlight_component_util/Cargo.toml @@ -15,7 +15,7 @@ Shared implementation for the procedural macros that generate Hyperlight host an name = "hyperlight_component_util" [dependencies] -wasmparser = { version = "0.224.0" } +wasmparser = { version = "0.235.0" } quote = { version = "1.0.38" } proc-macro2 = { version = "1.0.95" } syn = { version = "2.0.104" } diff --git a/src/hyperlight_component_util/src/elaborate.rs b/src/hyperlight_component_util/src/elaborate.rs index c38051725..28a5f0f0e 100644 --- a/src/hyperlight_component_util/src/elaborate.rs +++ b/src/hyperlight_component_util/src/elaborate.rs @@ -24,14 +24,14 @@ limitations under the License. //! substitute.rs for more details of the approach here). use wasmparser::{ - ComponentAlias, ComponentDefinedType, ComponentFuncResult, ComponentFuncType, - ComponentOuterAliasKind, ComponentType, ComponentTypeDeclaration, ComponentTypeRef, - ComponentValType, CompositeInnerType, CoreType, InstanceTypeDeclaration, ModuleTypeDeclaration, - OuterAliasKind, PrimitiveValType, TypeBounds, TypeRef, + ComponentAlias, ComponentDefinedType, ComponentFuncType, ComponentOuterAliasKind, + ComponentType, ComponentTypeDeclaration, ComponentTypeRef, ComponentValType, + CompositeInnerType, CoreType, InstanceTypeDeclaration, ModuleTypeDeclaration, OuterAliasKind, + PrimitiveValType, TypeBounds, TypeRef, }; use crate::etypes::{ - self, BoundedTyvar, Component, CoreDefined, CoreExportDecl, CoreExternDesc, CoreModule, + BoundedTyvar, Component, CoreDefined, CoreExportDecl, CoreExternDesc, CoreModule, CoreOrComponentExternDesc, Ctx, Defined, ExternDecl, ExternDesc, FloatWidth, Func, Handleable, Instance, IntWidth, Name, Param, QualifiedInstance, RecordField, Resource, ResourceId, TypeBound, Tyvar, Value, VariantCase, @@ -366,6 +366,7 @@ impl<'p, 'a> Ctx<'p, 'a> { PrimitiveValType::F64 => Value::F(FloatWidth::F64), PrimitiveValType::Char => Value::Char, PrimitiveValType::String => Value::String, + PrimitiveValType::ErrorContext => panic!("async not yet supported"), }), } } @@ -428,9 +429,12 @@ impl<'p, 'a> Ctx<'p, 'a> { Defined::Handleable(h) => Ok(Value::Borrow(h.clone())), _ => Err(Error::HandleToNonResource), }, - ComponentDefinedType::Future(_) - | ComponentDefinedType::Stream(_) - | ComponentDefinedType::ErrorContext => panic!("async not yet supported"), + ComponentDefinedType::Future(_) | ComponentDefinedType::Stream(_) => { + panic!("async not yet supported") + } + ComponentDefinedType::FixedSizeList(vt, size) => { + Ok(Value::FixList(Box::new(self.elab_value(vt)?), *size)) + } } } @@ -446,18 +450,9 @@ impl<'p, 'a> Ctx<'p, 'a> { }) }) .collect::, Error<'a>>>()?, - result: match &ft.results { - ComponentFuncResult::Unnamed(vt) => etypes::Result::Unnamed(self.elab_value(vt)?), - ComponentFuncResult::Named(rs) => etypes::Result::Named( - rs.iter() - .map(|(n, vt)| { - Ok(Param { - name: Name { name: n }, - ty: self.elab_value(vt)?, - }) - }) - .collect::, Error<'a>>>()?, - ), + result: match &ft.result { + Some(vt) => Some(self.elab_value(vt)?), + None => None, }, }) } diff --git a/src/hyperlight_component_util/src/etypes.rs b/src/hyperlight_component_util/src/etypes.rs index 0cec42887..5723eaf9a 100644 --- a/src/hyperlight_component_util/src/etypes.rs +++ b/src/hyperlight_component_util/src/etypes.rs @@ -83,6 +83,7 @@ pub enum Value<'a> { Char, String, List(Box>), + FixList(Box>, u32), Record(Vec>), Tuple(Vec>), Flags(Vec>), @@ -136,11 +137,7 @@ pub struct Param<'a> { pub ty: Value<'a>, } -#[derive(Debug, Clone)] -pub enum Result<'a> { - Unnamed(Value<'a>), - Named(Vec>), -} +pub type Result<'a> = Option>; /// functype_e in the specification #[derive(Debug, Clone)] diff --git a/src/hyperlight_component_util/src/hl.rs b/src/hyperlight_component_util/src/hl.rs index 5a8e984f3..781328688 100644 --- a/src/hyperlight_component_util/src/hl.rs +++ b/src/hyperlight_component_util/src/hl.rs @@ -223,6 +223,20 @@ pub fn emit_hl_unmarshal_value(s: &mut State, id: Ident, vt: &Value) -> TokenStr (#retid, cursor) } } + Value::FixList(vt, _size) => { + let inid = format_ident!("{}_elem", id); + let vtun = emit_hl_unmarshal_value(s, inid.clone(), vt); + quote! { + let mut cursor = 0; + let arr = ::core::array::from_fn(|_i| { + let #inid = &#id[cursor..]; + let (x, b) = { #vtun }; + cursor += b; + x + }); + (arr, cursor) + } + } Value::Record(_) => panic!("record not at top level of valtype"), Value::Tuple(vts) => { let inid = format_ident!("{}_elem", id); @@ -515,6 +529,25 @@ pub fn emit_hl_marshal_value(s: &mut State, id: Ident, vt: &Value) -> TokenStrea #retid } } + Value::FixList(vt, _size) => { + // Optimize for byte arrays ie. [u8; N] + if matches!(vt.as_ref(), Value::S(_) | Value::U(_) | Value::F(_)) { + quote! { + alloc::vec::Vec::from(#id.as_slice()) + } + } else { + let retid = format_ident!("{}_fixlist", id); + let inid = format_ident!("{}_elem", id); + let vtun = emit_hl_marshal_value(s, inid.clone(), vt); + quote! { + let mut #retid = alloc::vec::Vec::new(); + for #inid in #id { + #retid.extend({ #vtun }) + } + #retid + } + } + } Value::Record(_) => panic!("record not at top level of valtype"), Value::Tuple(vts) => { let retid = format_ident!("{}_tuple", id); @@ -653,14 +686,13 @@ pub fn emit_hl_unmarshal_param(s: &mut State, id: Ident, pt: &Value) -> TokenStr /// /// Precondition: the result type must only be a named result if there /// are no names in it (i.e. a unit type) -pub fn emit_hl_unmarshal_result(s: &mut State, id: Ident, rt: &etypes::Result) -> TokenStream { +pub fn emit_hl_unmarshal_result(s: &mut State, id: Ident, rt: &etypes::Result<'_>) -> TokenStream { match rt { - etypes::Result::Named(rs) if rs.is_empty() => quote! { () }, - etypes::Result::Unnamed(vt) => { + Some(vt) => { let toks = emit_hl_unmarshal_value(s, id, vt); quote! { { #toks }.0 } } - _ => panic!("named results not supported"), + None => quote! { () }, } } @@ -680,11 +712,10 @@ pub fn emit_hl_marshal_param(s: &mut State, id: Ident, pt: &Value) -> TokenStrea /// are no names in it (a unit type) pub fn emit_hl_marshal_result(s: &mut State, id: Ident, rt: &etypes::Result) -> TokenStream { match rt { - etypes::Result::Named(rs) if rs.is_empty() => quote! { ::alloc::vec::Vec::new() }, - etypes::Result::Unnamed(vt) => { + None => quote! { ::alloc::vec::Vec::new() }, + Some(vt) => { let toks = emit_hl_marshal_value(s, id, vt); quote! { { #toks } } } - _ => panic!("named results not supported"), } } diff --git a/src/hyperlight_component_util/src/rtypes.rs b/src/hyperlight_component_util/src/rtypes.rs index a6b2266a3..e9b8ded61 100644 --- a/src/hyperlight_component_util/src/rtypes.rs +++ b/src/hyperlight_component_util/src/rtypes.rs @@ -29,8 +29,8 @@ use crate::emit::{ split_wit_name, }; use crate::etypes::{ - Component, Defined, ExternDecl, ExternDesc, Func, Handleable, ImportExport, Instance, Param, - Result, TypeBound, Tyvar, Value, + self, Component, Defined, ExternDecl, ExternDesc, Func, Handleable, ImportExport, Instance, + Param, TypeBound, Tyvar, Value, }; /// When referring to an instance or resource trait, emit a token @@ -281,6 +281,11 @@ pub fn emit_value(s: &mut State, vt: &Value) -> TokenStream { let vt = emit_value(s, vt); quote! { alloc::vec::Vec<#vt> } } + Value::FixList(vt, size) => { + let vt = emit_value(s, vt); + let size = *size as usize; + quote! { [#vt; #size] } + } Value::Record(_) => panic!("record not at top level of valtype"), Value::Tuple(vts) => { let vts = vts.iter().map(|vt| emit_value(s, vt)).collect::>(); @@ -521,11 +526,10 @@ pub fn emit_func_param(s: &mut State, p: &Param) -> TokenStream { /// /// Precondition: the result type must only be a named result if there /// are no names in it (i.e. a unit type) -pub fn emit_func_result(s: &mut State, r: &Result) -> TokenStream { +pub fn emit_func_result(s: &mut State, r: &etypes::Result<'_>) -> TokenStream { match r { - Result::Unnamed(vt) => emit_value(s, vt), - Result::Named(rs) if rs.is_empty() => quote! { () }, - _ => panic!("multiple named function results are not currently supported"), + Some(vt) => emit_value(s, vt), + None => quote! { () }, } } diff --git a/src/hyperlight_component_util/src/substitute.rs b/src/hyperlight_component_util/src/substitute.rs index 94f95cf7b..5ea07d609 100644 --- a/src/hyperlight_component_util/src/substitute.rs +++ b/src/hyperlight_component_util/src/substitute.rs @@ -96,6 +96,7 @@ where Value::Char => Value::Char, Value::String => Value::String, Value::List(vt) => Value::List(Box::new(self.value(vt)?)), + Value::FixList(vt, size) => Value::FixList(Box::new(self.value(vt)?), *size), Value::Record(rfs) => Value::Record(self.record_fields(rfs)?), Value::Variant(vcs) => Value::Variant(self.variant_cases(vcs)?), Value::Flags(ns) => Value::Flags(ns.clone()), @@ -139,8 +140,8 @@ where rt: &crate::etypes::Result<'a>, ) -> Result, Self::Error> { Ok(match rt { - crate::etypes::Result::Unnamed(vt) => crate::etypes::Result::Unnamed(self.value(vt)?), - crate::etypes::Result::Named(pts) => crate::etypes::Result::Named(self.params(pts)?), + Some(vt) => Some(self.value(vt)?), + None => None, }) } diff --git a/src/hyperlight_component_util/src/wf.rs b/src/hyperlight_component_util/src/wf.rs index db15a7613..20321d6d7 100644 --- a/src/hyperlight_component_util/src/wf.rs +++ b/src/hyperlight_component_util/src/wf.rs @@ -213,6 +213,7 @@ impl<'p, 'a> Ctx<'p, 'a> { Value::Char => Ok(()), Value::String => Ok(()), Value::List(vt) => self.wf_value(p_, vt), + Value::FixList(vt, _) => self.wf_value(p_, vt), Value::Record(rfs) => anon_err.and(self.wf_record_fields(p_, rfs)), Value::Variant(vcs) => anon_err.and(self.wf_variant_cases(p_, vcs)), Value::Flags(ns) => anon_err.and(error_if_duplicates_by( @@ -268,10 +269,8 @@ impl<'p, 'a> Ctx<'p, 'a> { .iter() .try_for_each(|fp: &'r Param<'a>| self.wf_value(param_pos, &fp.ty))?; match &ft.result { - crate::etypes::Result::Unnamed(vt) => self.wf_value(result_pos, vt), - crate::etypes::Result::Named(ps) => ps - .iter() - .try_for_each(|fp: &'r Param<'a>| self.wf_value(result_pos, &fp.ty)), + Some(vt) => self.wf_value(result_pos, vt), + None => Ok(()), } } fn wf_type_bound<'r>( diff --git a/src/hyperlight_host/tests/wit_test.rs b/src/hyperlight_host/tests/wit_test.rs index e42441dd8..403553379 100644 --- a/src/hyperlight_host/tests/wit_test.rs +++ b/src/hyperlight_host/tests/wit_test.rs @@ -155,6 +155,12 @@ impl test::wit::Roundtrip for Host { ) -> test::wit::roundtrip::Testenum { x } + fn roundtrip_fix_list(&mut self, x: [u8; 4]) -> [u8; 4] { + x + } + fn roundtrip_fix_list_string(&mut self, x: [String; 4]) -> [String; 4] { + x + } fn roundtrip_no_result(&mut self, _x: u32) {} } @@ -331,9 +337,11 @@ mod wit_test { make_test! { roundtrip_flags_large, in arb_largeflags() } make_test! { roundtrip_variant, in arb_testvariant() } make_test! { roundtrip_enum, in arb_testenum() } + make_test! { roundtrip_fix_list, : [u8; 4] } + make_test! { roundtrip_fix_list_string, : [String; 4] } #[test] - fn test_simple_func() { + fn test_roundtrip_no_result() { sb().roundtrip().roundtrip_no_result(42); } diff --git a/src/tests/rust_guests/witguest/Cargo.lock b/src/tests/rust_guests/witguest/Cargo.lock index a1edc4b10..bcb5b4813 100644 --- a/src/tests/rust_guests/witguest/Cargo.lock +++ b/src/tests/rust_guests/witguest/Cargo.lock @@ -500,9 +500,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "wasmparser" -version = "0.224.1" +version = "0.235.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04f17a5917c2ddd3819e84c661fae0d6ba29d7b9c1f0e96c708c65a9c4188e11" +checksum = "161296c618fa2d63f6ed5fffd1112937e803cb9ec71b32b01a76321555660917" dependencies = [ "bitflags", "hashbrown", diff --git a/src/tests/rust_guests/witguest/guest.wit b/src/tests/rust_guests/witguest/guest.wit index 30b4969c5..07afc0d73 100644 --- a/src/tests/rust_guests/witguest/guest.wit +++ b/src/tests/rust_guests/witguest/guest.wit @@ -26,6 +26,8 @@ interface roundtrip { roundtrip-option: func(x: option) -> option; roundtrip-result: func(x: result) -> result; roundtrip-no-result: func(x: u32); + roundtrip-fix-list: func(x: list) -> list; + roundtrip-fix-list-string: func(x: list) -> list; record testrecord { contents: string, diff --git a/src/tests/rust_guests/witguest/src/main.rs b/src/tests/rust_guests/witguest/src/main.rs index 1edf44ec1..d9e610029 100644 --- a/src/tests/rust_guests/witguest/src/main.rs +++ b/src/tests/rust_guests/witguest/src/main.rs @@ -21,6 +21,8 @@ extern crate alloc; extern crate hyperlight_guest; mod bindings; +use alloc::string::String; + use bindings::*; struct Guest {} @@ -113,6 +115,12 @@ impl test::wit::Roundtrip for Guest { ) -> test::wit::roundtrip::Testenum { (Host {}).roundtrip_enum(x) } + fn roundtrip_fix_list(&mut self, x: [u8; 4]) -> [u8; 4] { + (Host {}).roundtrip_fix_list(x) + } + fn roundtrip_fix_list_string(&mut self, x: [String; 4]) -> [String; 4] { + (Host {}).roundtrip_fix_list_string(x) + } fn roundtrip_no_result(&mut self, x: u32) { (Host {}).roundtrip_no_result(x) }