Add basic GuestString support to wiggle (#13)

* Add basic GuestString support to wiggle

This commit adds basic `GuestString` support to `wiggle`. `GuestString`
is a wrapper around `GuestArray<'_, u8>` array type which itself can
be made into either an owned (cloned) Rust `String` or borrowed as
a reference `&str`. In both cases, `GuestString` ensures that the
underlying bytes are valid Unicode code units, throwing a `InvalidUtf8`
error if not.

This commit adds support *only* for passing in strings as arguments
in WASI. Marshalling of the return arg has not yet been implemented.
I'm not even sure it's possible without multi-value return args
feature of Wasm. It's not a major setback especially since the WASI
spec (and this includes even the `ephemeral` snapshot) doesn't
return strings anywhere. They are only ever passed in as arguments
to interface functions.

It should be noted that error returned in case of invalid UTF-8
requires a lot more love as it doesn't include anything besides
flagging an event that the string contained an invalid Unicode code unit.

* Borrow all of string's memory including nul-byte

Borrow all of string's underlying memory including the nul-byte.
This perhaps might not have a tremendous impact on anything, but
since the nul-byte is technically part of the WASI string, we should
include it in the borrow as well.

* Fill in wiggle-generate blanks for strings

* Print to screen passed string in proptest

* Strings are PointerLengthPairs!

* Fix generation of strings in compound types

* Update test with simple string strategy

* Generate better test strings

* Finalise proptest for strings

* Fix formatting

* Update crates/runtime/src/memory/string.rs

Removes unnecessary comment in code

* Apply Pat's suggestion to wrap Utf8Error as error
This commit is contained in:
Jakub Konka
2020-02-21 22:37:22 +01:00
committed by GitHub
parent 2f223acc55
commit 6ab3ff71d2
9 changed files with 273 additions and 13 deletions

View File

@@ -181,7 +181,36 @@ fn marshal_arg(
let #name = #name as #interface_typename;
}
}
witx::BuiltinType::String => unimplemented!("string types unimplemented"),
witx::BuiltinType::String => {
let lifetime = anon_lifetime();
let ptr_name = names.func_ptr_binding(&param.name);
let len_name = names.func_len_binding(&param.name);
let name = names.func_param(&param.name);
quote! {
let num_elems = match memory.ptr::<u32>(#len_name as u32) {
Ok(p) => match p.as_ref() {
Ok(r) => r,
Err(e) => {
#error_handling
}
}
Err(e) => {
#error_handling
}
};
let #name: wiggle_runtime::GuestString<#lifetime> = match memory.ptr::<u8>(#ptr_name as u32) {
Ok(p) => match p.array(*num_elems) {
Ok(s) => s.into(),
Err(e) => {
#error_handling
}
}
Err(e) => {
#error_handling
}
};
}
}
},
witx::Type::Pointer(pointee) => {
let pointee_type = names.type_ref(pointee, anon_lifetime());

View File

@@ -22,9 +22,9 @@ impl Names {
let ident = format_ident!("{}", id.as_str().to_camel_case());
quote!(#ident)
}
pub fn builtin_type(&self, b: BuiltinType) -> TokenStream {
pub fn builtin_type(&self, b: BuiltinType, lifetime: TokenStream) -> TokenStream {
match b {
BuiltinType::String => quote!(String),
BuiltinType::String => quote!(wiggle_runtime::GuestString<#lifetime>),
BuiltinType::U8 => quote!(u8),
BuiltinType::U16 => quote!(u16),
BuiltinType::U32 => quote!(u32),
@@ -35,7 +35,7 @@ impl Names {
BuiltinType::S64 => quote!(i64),
BuiltinType::F32 => quote!(f32),
BuiltinType::F64 => quote!(f64),
BuiltinType::Char8 => quote!(char),
BuiltinType::Char8 => quote!(u8),
BuiltinType::USize => quote!(usize),
}
}
@@ -59,7 +59,7 @@ impl Names {
}
}
TypeRef::Value(ty) => match &**ty {
witx::Type::Builtin(builtin) => self.builtin_type(*builtin),
witx::Type::Builtin(builtin) => self.builtin_type(*builtin, lifetime.clone()),
witx::Type::Pointer(pointee) => {
let pointee_type = self.type_ref(&pointee, lifetime.clone());
quote!(wiggle_runtime::GuestPtrMut<#lifetime, #pointee_type>)

View File

@@ -285,14 +285,18 @@ fn define_enum(names: &Names, name: &witx::Id, e: &witx::EnumDatatype) -> TokenS
fn define_builtin(names: &Names, name: &witx::Id, builtin: witx::BuiltinType) -> TokenStream {
let ident = names.type_(name);
let built = names.builtin_type(builtin);
quote!(pub type #ident = #built;)
let built = names.builtin_type(builtin, quote!('a));
if let witx::BuiltinType::String = builtin {
quote!(pub type #ident<'a> = #built;)
} else {
quote!(pub type #ident = #built;)
}
}
pub fn type_needs_lifetime(tref: &witx::TypeRef) -> bool {
match &*tref.type_() {
witx::Type::Builtin(b) => match b {
witx::BuiltinType::String => unimplemented!(),
witx::BuiltinType::String => true,
_ => false,
},
witx::Type::Enum { .. }
@@ -392,7 +396,7 @@ fn define_ptr_struct(names: &Names, name: &witx::Id, s: &witx::StructDatatype) -
let type_ = match &m.tref {
witx::TypeRef::Name(nt) => names.type_(&nt.name),
witx::TypeRef::Value(ty) => match &**ty {
witx::Type::Builtin(builtin) => names.builtin_type(*builtin),
witx::Type::Builtin(builtin) => names.builtin_type(*builtin, quote!('a)),
witx::Type::Pointer(pointee) => {
let pointee_type = names.type_ref(&pointee, quote!('a));
quote!(wiggle_runtime::GuestPtrMut<'a, #pointee_type>)
@@ -410,7 +414,7 @@ fn define_ptr_struct(names: &Names, name: &witx::Id, s: &witx::StructDatatype) -
let type_ = match &ml.member.tref {
witx::TypeRef::Name(nt) => names.type_(&nt.name),
witx::TypeRef::Value(ty) => match &**ty {
witx::Type::Builtin(builtin) => names.builtin_type(*builtin),
witx::Type::Builtin(builtin) => names.builtin_type(*builtin, quote!('a)),
witx::Type::Pointer(pointee) => {
let pointee_type = names.type_ref(&pointee, anon_lifetime());
quote!(wiggle_runtime::GuestPtrMut::<#pointee_type>)
@@ -453,7 +457,7 @@ fn define_ptr_struct(names: &Names, name: &witx::Id, s: &witx::StructDatatype) -
}
witx::TypeRef::Value(ty) => match &**ty {
witx::Type::Builtin(builtin) => {
let type_ = names.builtin_type(*builtin);
let type_ = names.builtin_type(*builtin, anon_lifetime());
quote! {
let #name = #type_::read_from_guest(&location.cast(#offset)?)?;
}