From 6f6c6499c610537462830df96c03d0c05a867e30 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Fri, 17 Jan 2020 14:54:05 +0100 Subject: [PATCH 01/86] Set up machinery --- .gitignore | 2 ++ .gitmodules | 3 +++ Cargo.toml | 11 +++++++++++ crates/WASI | 1 + crates/generate/Cargo.toml | 14 ++++++++++++++ crates/generate/src/lib.rs | 13 +++++++++++++ src/lib.rs | 2 ++ 7 files changed, 46 insertions(+) create mode 100644 .gitignore create mode 100644 .gitmodules create mode 100644 Cargo.toml create mode 160000 crates/WASI create mode 100644 crates/generate/Cargo.toml create mode 100644 crates/generate/src/lib.rs create mode 100644 src/lib.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000..53eaa21960 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/target +**/*.rs.bk diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000000..dc4e4a07bb --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "crates/WASI"] + path = crates/WASI + url = https://github.com/webassembly/wasi.git diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000000..f1e5049299 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "wig-new" +version = "0.1.0" +authors = ["Jakub Konka "] +edition = "2018" + +[dependencies] +generate = { path = "crates/generate" } + +[workspace] +members = ["crates/generate"] diff --git a/crates/WASI b/crates/WASI new file mode 160000 index 0000000000..77629f3442 --- /dev/null +++ b/crates/WASI @@ -0,0 +1 @@ +Subproject commit 77629f34429c1bc65af797dac687fd47fc73df4b diff --git a/crates/generate/Cargo.toml b/crates/generate/Cargo.toml new file mode 100644 index 0000000000..935d466c70 --- /dev/null +++ b/crates/generate/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "generate" +version = "0.1.0" +authors = ["Jakub Konka "] +edition = "2018" + +[lib] +proc-macro = true + +[dependencies] +witx = { path = "../WASI/tools/witx" } +quote = "1.0" +proc-macro2 = "1.0" +heck = "0.3" diff --git a/crates/generate/src/lib.rs b/crates/generate/src/lib.rs new file mode 100644 index 0000000000..4731366562 --- /dev/null +++ b/crates/generate/src/lib.rs @@ -0,0 +1,13 @@ +extern crate proc_macro; + +use proc_macro::TokenStream; +use proc_macro2::TokenStream as TokenStream2; + +#[proc_macro] +pub fn from_witx(args: TokenStream) -> TokenStream { + TokenStream::new() + // TokenStream::from(raw_types::gen( + // TokenStream2::from(args), + // raw_types::Mode::Host, + // )) +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000000..0136867e5c --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,2 @@ +generate::from_witx!(); + From 64f07933881e11a88721dba49c3a2045715ce4fe Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Fri, 17 Jan 2020 14:59:51 +0100 Subject: [PATCH 02/86] Fix incorrect hardcoded path to witx spec --- crates/generate/src/lib.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/crates/generate/src/lib.rs b/crates/generate/src/lib.rs index 4731366562..17757a987d 100644 --- a/crates/generate/src/lib.rs +++ b/crates/generate/src/lib.rs @@ -3,8 +3,11 @@ extern crate proc_macro; use proc_macro::TokenStream; use proc_macro2::TokenStream as TokenStream2; +const WITX_PATH: &'static str = "crates/WASI/phases/snapshot/witx/wasi_snapshot_preview1.witx"; + #[proc_macro] pub fn from_witx(args: TokenStream) -> TokenStream { + let doc = witx::load(&[WITX_PATH]).unwrap(); TokenStream::new() // TokenStream::from(raw_types::gen( // TokenStream2::from(args), From f24b0240ae3285313e16f9c262d1d5bdd3352d56 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Fri, 17 Jan 2020 16:22:52 +0100 Subject: [PATCH 03/86] Start tweaking original wig with Pat's suggestion --- Cargo.toml | 4 +- crates/generate/Cargo.toml | 2 +- crates/generate/src/imp.rs | 275 +++++++++++++++++++++++++++++++++++++ crates/generate/src/lib.rs | 14 +- 4 files changed, 282 insertions(+), 13 deletions(-) create mode 100644 crates/generate/src/imp.rs diff --git a/Cargo.toml b/Cargo.toml index f1e5049299..22ce267e3e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] -name = "wig-new" +name = "wiggle" version = "0.1.0" -authors = ["Jakub Konka "] +authors = ["Jakub Konka "] edition = "2018" [dependencies] diff --git a/crates/generate/Cargo.toml b/crates/generate/Cargo.toml index 935d466c70..2403f089a8 100644 --- a/crates/generate/Cargo.toml +++ b/crates/generate/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "generate" version = "0.1.0" -authors = ["Jakub Konka "] +authors = ["Jakub Konka "] edition = "2018" [lib] diff --git a/crates/generate/src/imp.rs b/crates/generate/src/imp.rs new file mode 100644 index 0000000000..24f9ee6c2f --- /dev/null +++ b/crates/generate/src/imp.rs @@ -0,0 +1,275 @@ +use heck::{CamelCase, MixedCase, ShoutySnakeCase}; +use proc_macro2::{Delimiter, Group, Literal, TokenStream, TokenTree}; +use quote::{format_ident, quote}; +use std::convert::TryFrom; + +const WITX_PATH: &'static str = "crates/WASI/phases/snapshot/witx/wasi_snapshot_preview1.witx"; + +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub enum Mode { + Host, + Wasi32, + Wasi, +} + +impl Mode { + pub fn include_target_types(&self) -> bool { + match self { + Mode::Host | Mode::Wasi32 => true, + Mode::Wasi => false, + } + } +} + +pub fn gen() -> TokenStream { + let mut output = TokenStream::new(); + let doc = match witx::load(&[&WITX_PATH]) { + Ok(doc) => doc, + Err(e) => { + panic!("error opening file {}: {}", WITX_PATH, e); + } + }; + + gen_datatypes(&mut output, &doc, Mode::Wasi); + // gen_datatypes(&mut output, &doc, Mode::Wasi32); + // gen_datatypes(&mut output, &doc, Mode::Host); + + output +} + +fn gen_datatypes(output: &mut TokenStream, doc: &witx::Document, mode: Mode) { + for namedtype in doc.typenames() { + if mode.include_target_types() != namedtype_has_target_size(&namedtype) { + continue; + } + + gen_datatype(output, mode, &namedtype); + } +} + +fn gen_datatype(output: &mut TokenStream, mode: Mode, namedtype: &witx::NamedType) { + let wasi_name = format_ident!("{}", namedtype.name.as_str().to_camel_case()); + match &namedtype.tref { + witx::TypeRef::Name(alias_to) => { + let to = tref_tokens(mode, &alias_to.tref); + output.extend(quote!(pub type #wasi_name = #to;)); + } + witx::TypeRef::Value(v) => match &**v { + witx::Type::Enum(e) => { + let repr = int_repr_tokens(e.repr); + output.extend(quote!(#[repr(#repr)])); + output + .extend(quote!(#[derive(Copy, Clone, Debug, std::hash::Hash, Eq, PartialEq)])); + + let mut inner = TokenStream::new(); + for variant in &e.variants { + let value_name = if namedtype.name.as_str() == "errno" { + // FIXME discussion point! + format_ident!("E{}", variant.name.as_str().to_mixed_case()) + } else { + format_ident!("{}", variant.name.as_str().to_camel_case()) + }; + inner.extend(quote!(#value_name,)); + } + + output.extend(quote!(pub enum #wasi_name { + #inner + })); + } + witx::Type::Int(_) => {} // TODO + witx::Type::Flags(f) => { + let repr = int_repr_tokens(f.repr); + output.extend(quote!(#[repr(transparent)])); + output + .extend(quote!(#[derive(Copy, Clone, Debug, std::hash::Hash, Eq, PartialEq)])); + output.extend(quote!(pub struct #wasi_name(#repr);)); + // TODO + // Since `Flags` are represented by a "transparent" struct, we should probably + // auto-generate `from_raw(raw: #repr)` method or similar + + let mut inner = TokenStream::new(); + for (index, flag) in f.flags.iter().enumerate() { + let value_name = format_ident!("{}", flag.name.as_str().to_shouty_snake_case()); + let flag_value = Literal::u128_unsuffixed( + 1u128 + .checked_shl(u32::try_from(index).expect("flag value overflow")) + .expect("flag value overflow"), + ); + inner.extend( + quote!(pub const #value_name: #wasi_name = #wasi_name(#flag_value);), + ); + } + + output.extend(quote!(impl #wasi_name { + #inner + })); + } + witx::Type::Struct(s) => { + output.extend(quote!(#[repr(C)])); + // Types which contain unions can't trivially implement Debug, + // Hash, or Eq, because the type itself doesn't record which + // union member is active. + if struct_has_union(&s) { + output.extend(quote!(#[derive(Copy, Clone)])); + output.extend(quote!(#[allow(missing_debug_implementations)])); + } else { + output.extend(quote!(#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)])); + } + + output.extend(quote!(pub struct #wasi_name)); + + let mut inner = TokenStream::new(); + for member in &s.members { + let member_name = format_ident!("r#{}", member.name.as_str()); + let member_type = tref_tokens(mode, &member.tref); + inner.extend(quote!(pub #member_name: #member_type,)); + } + let braced = Group::new(Delimiter::Brace, inner); + output.extend(TokenStream::from(TokenTree::Group(braced))); + } + witx::Type::Union(u) => { + output.extend(quote!(#[repr(C)])); + output.extend(quote!(#[derive(Copy, Clone)])); + output.extend(quote!(#[allow(missing_debug_implementations)])); + + output.extend(quote!(pub union #wasi_name)); + + let mut inner = TokenStream::new(); + for variant in &u.variants { + let variant_name = format_ident!("r#{}", variant.name.as_str()); + let variant_type = tref_tokens(mode, &variant.tref); + inner.extend(quote!(pub #variant_name: #variant_type,)); + } + let braced = Group::new(Delimiter::Brace, inner); + output.extend(TokenStream::from(TokenTree::Group(braced))); + } + witx::Type::Handle(_h) => { + output.extend(quote!(pub type #wasi_name = u32;)); + } + witx::Type::Builtin(b) => { + if namedtype.name.as_str() == "size" { + match mode { + Mode::Host => output.extend(quote!(pub type #wasi_name = usize;)), + Mode::Wasi => panic!("size has target-specific size"), + Mode::Wasi32 => output.extend(quote!(pub type #wasi_name = u32;)), + } + } else { + let b_type = builtin_tokens(mode, *b); + output.extend(quote!(pub type #wasi_name = #b_type;)); + } + } + witx::Type::Pointer { .. } + | witx::Type::ConstPointer { .. } + | witx::Type::Array { .. } => { + let tref_tokens = tref_tokens(mode, &namedtype.tref); + output.extend(quote!(pub type #wasi_name = #tref_tokens;)); + } + }, + } +} + +fn int_repr_tokens(int_repr: witx::IntRepr) -> TokenStream { + match int_repr { + witx::IntRepr::U8 => quote!(u8), + witx::IntRepr::U16 => quote!(u16), + witx::IntRepr::U32 => quote!(u32), + witx::IntRepr::U64 => quote!(u64), + } +} + +fn builtin_tokens(mode: Mode, builtin: witx::BuiltinType) -> TokenStream { + match builtin { + witx::BuiltinType::String => match mode { + Mode::Host => quote!((*const u8, usize)), + Mode::Wasi => panic!("strings have target-specific size"), + Mode::Wasi32 => quote!((u32, u32)), + }, + witx::BuiltinType::U8 => quote!(u8), + witx::BuiltinType::U16 => quote!(u16), + witx::BuiltinType::U32 => quote!(u32), + witx::BuiltinType::U64 => quote!(u64), + witx::BuiltinType::S8 => quote!(i8), + witx::BuiltinType::S16 => quote!(i16), + witx::BuiltinType::S32 => quote!(i32), + witx::BuiltinType::S64 => quote!(i64), + witx::BuiltinType::F32 => quote!(f32), + witx::BuiltinType::F64 => quote!(f64), + witx::BuiltinType::Char8 => quote!(char), + witx::BuiltinType::USize => quote!(usize), + } +} + +fn tref_tokens(mode: Mode, tref: &witx::TypeRef) -> TokenStream { + match tref { + witx::TypeRef::Name(n) => TokenStream::from(TokenTree::Ident(format_ident!( + "{}", + n.name.as_str().to_camel_case() + ))), + witx::TypeRef::Value(v) => match &**v { + witx::Type::Builtin(b) => builtin_tokens(mode, *b), + witx::Type::Pointer(pointee) => { + let pointee = tref_tokens(mode, pointee); + match mode { + Mode::Host => quote!(*mut #pointee), + Mode::Wasi => panic!("pointers have target-specific size"), + Mode::Wasi32 => quote!(u32), + } + } + witx::Type::ConstPointer(pointee) => { + let pointee = tref_tokens(mode, pointee); + match mode { + Mode::Host => quote!(*const #pointee), + Mode::Wasi => panic!("pointers have target-specific size"), + Mode::Wasi32 => quote!(u32), + } + } + witx::Type::Array(element) => { + let element_name = tref_tokens(mode, element); + match mode { + Mode::Host => quote!((*const #element_name, usize)), + Mode::Wasi => panic!("arrays have target-specific size"), + Mode::Wasi32 => quote!((u32, u32)), + } + } + t => panic!("cannot give name to anonymous type {:?}", t), + }, + } +} + +/// Test whether the given struct contains any union members. +fn struct_has_union(s: &witx::StructDatatype) -> bool { + s.members.iter().any(|member| match &*member.tref.type_() { + witx::Type::Union { .. } => true, + witx::Type::Struct(s) => struct_has_union(&s), + _ => false, + }) +} + +/// Test whether the type referred to has a target-specific size. +fn tref_has_target_size(tref: &witx::TypeRef) -> bool { + match tref { + witx::TypeRef::Name(nt) => namedtype_has_target_size(&nt), + witx::TypeRef::Value(t) => type_has_target_size(&t), + } +} + +/// Test whether the given named type has a target-specific size. +fn namedtype_has_target_size(nt: &witx::NamedType) -> bool { + if nt.name.as_str() == "size" { + true + } else { + tref_has_target_size(&nt.tref) + } +} + +/// Test whether the given type has a target-specific size. +fn type_has_target_size(ty: &witx::Type) -> bool { + match ty { + witx::Type::Builtin(witx::BuiltinType::String) => true, + witx::Type::Pointer { .. } | witx::Type::ConstPointer { .. } => true, + witx::Type::Array(elem) => tref_has_target_size(elem), + witx::Type::Struct(s) => s.members.iter().any(|m| tref_has_target_size(&m.tref)), + witx::Type::Union(u) => u.variants.iter().any(|v| tref_has_target_size(&v.tref)), + _ => false, + } +} diff --git a/crates/generate/src/lib.rs b/crates/generate/src/lib.rs index 17757a987d..8e214f3345 100644 --- a/crates/generate/src/lib.rs +++ b/crates/generate/src/lib.rs @@ -1,16 +1,10 @@ extern crate proc_macro; -use proc_macro::TokenStream; -use proc_macro2::TokenStream as TokenStream2; +mod imp; -const WITX_PATH: &'static str = "crates/WASI/phases/snapshot/witx/wasi_snapshot_preview1.witx"; +use proc_macro::TokenStream; #[proc_macro] -pub fn from_witx(args: TokenStream) -> TokenStream { - let doc = witx::load(&[WITX_PATH]).unwrap(); - TokenStream::new() - // TokenStream::from(raw_types::gen( - // TokenStream2::from(args), - // raw_types::Mode::Host, - // )) +pub fn from_witx(_args: TokenStream) -> TokenStream { + TokenStream::from(imp::gen()) } From 8882cb8ea691dce28058a8ea1ffcdde1e7356abe Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Fri, 17 Jan 2020 19:39:44 +0100 Subject: [PATCH 04/86] Add auto-generated file --- lib_generated.rs | 2821 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 2821 insertions(+) create mode 100644 lib_generated.rs diff --git a/lib_generated.rs b/lib_generated.rs new file mode 100644 index 0000000000..ea181be91d --- /dev/null +++ b/lib_generated.rs @@ -0,0 +1,2821 @@ +#![feature(prelude_import)] +#[prelude_import] +use std::prelude::v1::*; +#[macro_use] +extern crate std; +pub type Filesize = u64; +pub type Timestamp = u64; +#[repr(u32)] +pub enum Clockid { + Realtime, + Monotonic, + ProcessCputimeId, + ThreadCputimeId, +} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::marker::Copy for Clockid {} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::clone::Clone for Clockid { + #[inline] + fn clone(&self) -> Clockid { + { + *self + } + } +} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::fmt::Debug for Clockid { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + match (&*self,) { + (&Clockid::Realtime,) => { + let mut debug_trait_builder = f.debug_tuple("Realtime"); + debug_trait_builder.finish() + } + (&Clockid::Monotonic,) => { + let mut debug_trait_builder = f.debug_tuple("Monotonic"); + debug_trait_builder.finish() + } + (&Clockid::ProcessCputimeId,) => { + let mut debug_trait_builder = f.debug_tuple("ProcessCputimeId"); + debug_trait_builder.finish() + } + (&Clockid::ThreadCputimeId,) => { + let mut debug_trait_builder = f.debug_tuple("ThreadCputimeId"); + debug_trait_builder.finish() + } + } + } +} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::hash::Hash for Clockid { + fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) -> () { + match (&*self,) { + _ => ::core::hash::Hash::hash( + &unsafe { ::core::intrinsics::discriminant_value(self) }, + state, + ), + } + } +} +impl ::core::marker::StructuralEq for Clockid {} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::cmp::Eq for Clockid { + #[inline] + #[doc(hidden)] + fn assert_receiver_is_total_eq(&self) -> () { + {} + } +} +impl ::core::marker::StructuralPartialEq for Clockid {} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::cmp::PartialEq for Clockid { + #[inline] + fn eq(&self, other: &Clockid) -> bool { + { + let __self_vi = unsafe { ::core::intrinsics::discriminant_value(&*self) } as u32; + let __arg_1_vi = unsafe { ::core::intrinsics::discriminant_value(&*other) } as u32; + if true && __self_vi == __arg_1_vi { + match (&*self, &*other) { + _ => true, + } + } else { + false + } + } + } +} +#[repr(u16)] +pub enum Errno { + Esuccess, + E2big, + Eacces, + Eaddrinuse, + Eaddrnotavail, + Eafnosupport, + Eagain, + Ealready, + Ebadf, + Ebadmsg, + Ebusy, + Ecanceled, + Echild, + Econnaborted, + Econnrefused, + Econnreset, + Edeadlk, + Edestaddrreq, + Edom, + Edquot, + Eexist, + Efault, + Efbig, + Ehostunreach, + Eidrm, + Eilseq, + Einprogress, + Eintr, + Einval, + Eio, + Eisconn, + Eisdir, + Eloop, + Emfile, + Emlink, + Emsgsize, + Emultihop, + Enametoolong, + Enetdown, + Enetreset, + Enetunreach, + Enfile, + Enobufs, + Enodev, + Enoent, + Enoexec, + Enolck, + Enolink, + Enomem, + Enomsg, + Enoprotoopt, + Enospc, + Enosys, + Enotconn, + Enotdir, + Enotempty, + Enotrecoverable, + Enotsock, + Enotsup, + Enotty, + Enxio, + Eoverflow, + Eownerdead, + Eperm, + Epipe, + Eproto, + Eprotonosupport, + Eprototype, + Erange, + Erofs, + Espipe, + Esrch, + Estale, + Etimedout, + Etxtbsy, + Exdev, + Enotcapable, +} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::marker::Copy for Errno {} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::clone::Clone for Errno { + #[inline] + fn clone(&self) -> Errno { + { + *self + } + } +} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::fmt::Debug for Errno { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + match (&*self,) { + (&Errno::Esuccess,) => { + let mut debug_trait_builder = f.debug_tuple("Esuccess"); + debug_trait_builder.finish() + } + (&Errno::E2big,) => { + let mut debug_trait_builder = f.debug_tuple("E2big"); + debug_trait_builder.finish() + } + (&Errno::Eacces,) => { + let mut debug_trait_builder = f.debug_tuple("Eacces"); + debug_trait_builder.finish() + } + (&Errno::Eaddrinuse,) => { + let mut debug_trait_builder = f.debug_tuple("Eaddrinuse"); + debug_trait_builder.finish() + } + (&Errno::Eaddrnotavail,) => { + let mut debug_trait_builder = f.debug_tuple("Eaddrnotavail"); + debug_trait_builder.finish() + } + (&Errno::Eafnosupport,) => { + let mut debug_trait_builder = f.debug_tuple("Eafnosupport"); + debug_trait_builder.finish() + } + (&Errno::Eagain,) => { + let mut debug_trait_builder = f.debug_tuple("Eagain"); + debug_trait_builder.finish() + } + (&Errno::Ealready,) => { + let mut debug_trait_builder = f.debug_tuple("Ealready"); + debug_trait_builder.finish() + } + (&Errno::Ebadf,) => { + let mut debug_trait_builder = f.debug_tuple("Ebadf"); + debug_trait_builder.finish() + } + (&Errno::Ebadmsg,) => { + let mut debug_trait_builder = f.debug_tuple("Ebadmsg"); + debug_trait_builder.finish() + } + (&Errno::Ebusy,) => { + let mut debug_trait_builder = f.debug_tuple("Ebusy"); + debug_trait_builder.finish() + } + (&Errno::Ecanceled,) => { + let mut debug_trait_builder = f.debug_tuple("Ecanceled"); + debug_trait_builder.finish() + } + (&Errno::Echild,) => { + let mut debug_trait_builder = f.debug_tuple("Echild"); + debug_trait_builder.finish() + } + (&Errno::Econnaborted,) => { + let mut debug_trait_builder = f.debug_tuple("Econnaborted"); + debug_trait_builder.finish() + } + (&Errno::Econnrefused,) => { + let mut debug_trait_builder = f.debug_tuple("Econnrefused"); + debug_trait_builder.finish() + } + (&Errno::Econnreset,) => { + let mut debug_trait_builder = f.debug_tuple("Econnreset"); + debug_trait_builder.finish() + } + (&Errno::Edeadlk,) => { + let mut debug_trait_builder = f.debug_tuple("Edeadlk"); + debug_trait_builder.finish() + } + (&Errno::Edestaddrreq,) => { + let mut debug_trait_builder = f.debug_tuple("Edestaddrreq"); + debug_trait_builder.finish() + } + (&Errno::Edom,) => { + let mut debug_trait_builder = f.debug_tuple("Edom"); + debug_trait_builder.finish() + } + (&Errno::Edquot,) => { + let mut debug_trait_builder = f.debug_tuple("Edquot"); + debug_trait_builder.finish() + } + (&Errno::Eexist,) => { + let mut debug_trait_builder = f.debug_tuple("Eexist"); + debug_trait_builder.finish() + } + (&Errno::Efault,) => { + let mut debug_trait_builder = f.debug_tuple("Efault"); + debug_trait_builder.finish() + } + (&Errno::Efbig,) => { + let mut debug_trait_builder = f.debug_tuple("Efbig"); + debug_trait_builder.finish() + } + (&Errno::Ehostunreach,) => { + let mut debug_trait_builder = f.debug_tuple("Ehostunreach"); + debug_trait_builder.finish() + } + (&Errno::Eidrm,) => { + let mut debug_trait_builder = f.debug_tuple("Eidrm"); + debug_trait_builder.finish() + } + (&Errno::Eilseq,) => { + let mut debug_trait_builder = f.debug_tuple("Eilseq"); + debug_trait_builder.finish() + } + (&Errno::Einprogress,) => { + let mut debug_trait_builder = f.debug_tuple("Einprogress"); + debug_trait_builder.finish() + } + (&Errno::Eintr,) => { + let mut debug_trait_builder = f.debug_tuple("Eintr"); + debug_trait_builder.finish() + } + (&Errno::Einval,) => { + let mut debug_trait_builder = f.debug_tuple("Einval"); + debug_trait_builder.finish() + } + (&Errno::Eio,) => { + let mut debug_trait_builder = f.debug_tuple("Eio"); + debug_trait_builder.finish() + } + (&Errno::Eisconn,) => { + let mut debug_trait_builder = f.debug_tuple("Eisconn"); + debug_trait_builder.finish() + } + (&Errno::Eisdir,) => { + let mut debug_trait_builder = f.debug_tuple("Eisdir"); + debug_trait_builder.finish() + } + (&Errno::Eloop,) => { + let mut debug_trait_builder = f.debug_tuple("Eloop"); + debug_trait_builder.finish() + } + (&Errno::Emfile,) => { + let mut debug_trait_builder = f.debug_tuple("Emfile"); + debug_trait_builder.finish() + } + (&Errno::Emlink,) => { + let mut debug_trait_builder = f.debug_tuple("Emlink"); + debug_trait_builder.finish() + } + (&Errno::Emsgsize,) => { + let mut debug_trait_builder = f.debug_tuple("Emsgsize"); + debug_trait_builder.finish() + } + (&Errno::Emultihop,) => { + let mut debug_trait_builder = f.debug_tuple("Emultihop"); + debug_trait_builder.finish() + } + (&Errno::Enametoolong,) => { + let mut debug_trait_builder = f.debug_tuple("Enametoolong"); + debug_trait_builder.finish() + } + (&Errno::Enetdown,) => { + let mut debug_trait_builder = f.debug_tuple("Enetdown"); + debug_trait_builder.finish() + } + (&Errno::Enetreset,) => { + let mut debug_trait_builder = f.debug_tuple("Enetreset"); + debug_trait_builder.finish() + } + (&Errno::Enetunreach,) => { + let mut debug_trait_builder = f.debug_tuple("Enetunreach"); + debug_trait_builder.finish() + } + (&Errno::Enfile,) => { + let mut debug_trait_builder = f.debug_tuple("Enfile"); + debug_trait_builder.finish() + } + (&Errno::Enobufs,) => { + let mut debug_trait_builder = f.debug_tuple("Enobufs"); + debug_trait_builder.finish() + } + (&Errno::Enodev,) => { + let mut debug_trait_builder = f.debug_tuple("Enodev"); + debug_trait_builder.finish() + } + (&Errno::Enoent,) => { + let mut debug_trait_builder = f.debug_tuple("Enoent"); + debug_trait_builder.finish() + } + (&Errno::Enoexec,) => { + let mut debug_trait_builder = f.debug_tuple("Enoexec"); + debug_trait_builder.finish() + } + (&Errno::Enolck,) => { + let mut debug_trait_builder = f.debug_tuple("Enolck"); + debug_trait_builder.finish() + } + (&Errno::Enolink,) => { + let mut debug_trait_builder = f.debug_tuple("Enolink"); + debug_trait_builder.finish() + } + (&Errno::Enomem,) => { + let mut debug_trait_builder = f.debug_tuple("Enomem"); + debug_trait_builder.finish() + } + (&Errno::Enomsg,) => { + let mut debug_trait_builder = f.debug_tuple("Enomsg"); + debug_trait_builder.finish() + } + (&Errno::Enoprotoopt,) => { + let mut debug_trait_builder = f.debug_tuple("Enoprotoopt"); + debug_trait_builder.finish() + } + (&Errno::Enospc,) => { + let mut debug_trait_builder = f.debug_tuple("Enospc"); + debug_trait_builder.finish() + } + (&Errno::Enosys,) => { + let mut debug_trait_builder = f.debug_tuple("Enosys"); + debug_trait_builder.finish() + } + (&Errno::Enotconn,) => { + let mut debug_trait_builder = f.debug_tuple("Enotconn"); + debug_trait_builder.finish() + } + (&Errno::Enotdir,) => { + let mut debug_trait_builder = f.debug_tuple("Enotdir"); + debug_trait_builder.finish() + } + (&Errno::Enotempty,) => { + let mut debug_trait_builder = f.debug_tuple("Enotempty"); + debug_trait_builder.finish() + } + (&Errno::Enotrecoverable,) => { + let mut debug_trait_builder = f.debug_tuple("Enotrecoverable"); + debug_trait_builder.finish() + } + (&Errno::Enotsock,) => { + let mut debug_trait_builder = f.debug_tuple("Enotsock"); + debug_trait_builder.finish() + } + (&Errno::Enotsup,) => { + let mut debug_trait_builder = f.debug_tuple("Enotsup"); + debug_trait_builder.finish() + } + (&Errno::Enotty,) => { + let mut debug_trait_builder = f.debug_tuple("Enotty"); + debug_trait_builder.finish() + } + (&Errno::Enxio,) => { + let mut debug_trait_builder = f.debug_tuple("Enxio"); + debug_trait_builder.finish() + } + (&Errno::Eoverflow,) => { + let mut debug_trait_builder = f.debug_tuple("Eoverflow"); + debug_trait_builder.finish() + } + (&Errno::Eownerdead,) => { + let mut debug_trait_builder = f.debug_tuple("Eownerdead"); + debug_trait_builder.finish() + } + (&Errno::Eperm,) => { + let mut debug_trait_builder = f.debug_tuple("Eperm"); + debug_trait_builder.finish() + } + (&Errno::Epipe,) => { + let mut debug_trait_builder = f.debug_tuple("Epipe"); + debug_trait_builder.finish() + } + (&Errno::Eproto,) => { + let mut debug_trait_builder = f.debug_tuple("Eproto"); + debug_trait_builder.finish() + } + (&Errno::Eprotonosupport,) => { + let mut debug_trait_builder = f.debug_tuple("Eprotonosupport"); + debug_trait_builder.finish() + } + (&Errno::Eprototype,) => { + let mut debug_trait_builder = f.debug_tuple("Eprototype"); + debug_trait_builder.finish() + } + (&Errno::Erange,) => { + let mut debug_trait_builder = f.debug_tuple("Erange"); + debug_trait_builder.finish() + } + (&Errno::Erofs,) => { + let mut debug_trait_builder = f.debug_tuple("Erofs"); + debug_trait_builder.finish() + } + (&Errno::Espipe,) => { + let mut debug_trait_builder = f.debug_tuple("Espipe"); + debug_trait_builder.finish() + } + (&Errno::Esrch,) => { + let mut debug_trait_builder = f.debug_tuple("Esrch"); + debug_trait_builder.finish() + } + (&Errno::Estale,) => { + let mut debug_trait_builder = f.debug_tuple("Estale"); + debug_trait_builder.finish() + } + (&Errno::Etimedout,) => { + let mut debug_trait_builder = f.debug_tuple("Etimedout"); + debug_trait_builder.finish() + } + (&Errno::Etxtbsy,) => { + let mut debug_trait_builder = f.debug_tuple("Etxtbsy"); + debug_trait_builder.finish() + } + (&Errno::Exdev,) => { + let mut debug_trait_builder = f.debug_tuple("Exdev"); + debug_trait_builder.finish() + } + (&Errno::Enotcapable,) => { + let mut debug_trait_builder = f.debug_tuple("Enotcapable"); + debug_trait_builder.finish() + } + } + } +} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::hash::Hash for Errno { + fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) -> () { + match (&*self,) { + _ => ::core::hash::Hash::hash( + &unsafe { ::core::intrinsics::discriminant_value(self) }, + state, + ), + } + } +} +impl ::core::marker::StructuralEq for Errno {} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::cmp::Eq for Errno { + #[inline] + #[doc(hidden)] + fn assert_receiver_is_total_eq(&self) -> () { + {} + } +} +impl ::core::marker::StructuralPartialEq for Errno {} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::cmp::PartialEq for Errno { + #[inline] + fn eq(&self, other: &Errno) -> bool { + { + let __self_vi = unsafe { ::core::intrinsics::discriminant_value(&*self) } as u16; + let __arg_1_vi = unsafe { ::core::intrinsics::discriminant_value(&*other) } as u16; + if true && __self_vi == __arg_1_vi { + match (&*self, &*other) { + _ => true, + } + } else { + false + } + } + } +} +#[repr(transparent)] +pub struct Rights(u64); +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::marker::Copy for Rights {} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::clone::Clone for Rights { + #[inline] + fn clone(&self) -> Rights { + { + let _: ::core::clone::AssertParamIsClone; + *self + } + } +} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::fmt::Debug for Rights { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + match *self { + Rights(ref __self_0_0) => { + let mut debug_trait_builder = f.debug_tuple("Rights"); + let _ = debug_trait_builder.field(&&(*__self_0_0)); + debug_trait_builder.finish() + } + } + } +} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::hash::Hash for Rights { + fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) -> () { + match *self { + Rights(ref __self_0_0) => ::core::hash::Hash::hash(&(*__self_0_0), state), + } + } +} +impl ::core::marker::StructuralEq for Rights {} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::cmp::Eq for Rights { + #[inline] + #[doc(hidden)] + fn assert_receiver_is_total_eq(&self) -> () { + { + let _: ::core::cmp::AssertParamIsEq; + } + } +} +impl ::core::marker::StructuralPartialEq for Rights {} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::cmp::PartialEq for Rights { + #[inline] + fn eq(&self, other: &Rights) -> bool { + match *other { + Rights(ref __self_1_0) => match *self { + Rights(ref __self_0_0) => (*__self_0_0) == (*__self_1_0), + }, + } + } + #[inline] + fn ne(&self, other: &Rights) -> bool { + match *other { + Rights(ref __self_1_0) => match *self { + Rights(ref __self_0_0) => (*__self_0_0) != (*__self_1_0), + }, + } + } +} +impl Rights { + pub const FD_DATASYNC: Rights = Rights(1); + pub const FD_READ: Rights = Rights(2); + pub const FD_SEEK: Rights = Rights(4); + pub const FD_FDSTAT_SET_FLAGS: Rights = Rights(8); + pub const FD_SYNC: Rights = Rights(16); + pub const FD_TELL: Rights = Rights(32); + pub const FD_WRITE: Rights = Rights(64); + pub const FD_ADVISE: Rights = Rights(128); + pub const FD_ALLOCATE: Rights = Rights(256); + pub const PATH_CREATE_DIRECTORY: Rights = Rights(512); + pub const PATH_CREATE_FILE: Rights = Rights(1024); + pub const PATH_LINK_SOURCE: Rights = Rights(2048); + pub const PATH_LINK_TARGET: Rights = Rights(4096); + pub const PATH_OPEN: Rights = Rights(8192); + pub const FD_READDIR: Rights = Rights(16384); + pub const PATH_READLINK: Rights = Rights(32768); + pub const PATH_RENAME_SOURCE: Rights = Rights(65536); + pub const PATH_RENAME_TARGET: Rights = Rights(131072); + pub const PATH_FILESTAT_GET: Rights = Rights(262144); + pub const PATH_FILESTAT_SET_SIZE: Rights = Rights(524288); + pub const PATH_FILESTAT_SET_TIMES: Rights = Rights(1048576); + pub const FD_FILESTAT_GET: Rights = Rights(2097152); + pub const FD_FILESTAT_SET_SIZE: Rights = Rights(4194304); + pub const FD_FILESTAT_SET_TIMES: Rights = Rights(8388608); + pub const PATH_SYMLINK: Rights = Rights(16777216); + pub const PATH_REMOVE_DIRECTORY: Rights = Rights(33554432); + pub const PATH_UNLINK_FILE: Rights = Rights(67108864); + pub const POLL_FD_READWRITE: Rights = Rights(134217728); + pub const SOCK_SHUTDOWN: Rights = Rights(268435456); +} +pub type Fd = u32; +pub type Filedelta = i64; +#[repr(u8)] +pub enum Whence { + Set, + Cur, + End, +} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::marker::Copy for Whence {} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::clone::Clone for Whence { + #[inline] + fn clone(&self) -> Whence { + { + *self + } + } +} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::fmt::Debug for Whence { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + match (&*self,) { + (&Whence::Set,) => { + let mut debug_trait_builder = f.debug_tuple("Set"); + debug_trait_builder.finish() + } + (&Whence::Cur,) => { + let mut debug_trait_builder = f.debug_tuple("Cur"); + debug_trait_builder.finish() + } + (&Whence::End,) => { + let mut debug_trait_builder = f.debug_tuple("End"); + debug_trait_builder.finish() + } + } + } +} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::hash::Hash for Whence { + fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) -> () { + match (&*self,) { + _ => ::core::hash::Hash::hash( + &unsafe { ::core::intrinsics::discriminant_value(self) }, + state, + ), + } + } +} +impl ::core::marker::StructuralEq for Whence {} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::cmp::Eq for Whence { + #[inline] + #[doc(hidden)] + fn assert_receiver_is_total_eq(&self) -> () { + {} + } +} +impl ::core::marker::StructuralPartialEq for Whence {} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::cmp::PartialEq for Whence { + #[inline] + fn eq(&self, other: &Whence) -> bool { + { + let __self_vi = unsafe { ::core::intrinsics::discriminant_value(&*self) } as u8; + let __arg_1_vi = unsafe { ::core::intrinsics::discriminant_value(&*other) } as u8; + if true && __self_vi == __arg_1_vi { + match (&*self, &*other) { + _ => true, + } + } else { + false + } + } + } +} +pub type Dircookie = u64; +pub type Dirnamlen = u32; +pub type Inode = u64; +#[repr(u8)] +pub enum Filetype { + Unknown, + BlockDevice, + CharacterDevice, + Directory, + RegularFile, + SocketDgram, + SocketStream, + SymbolicLink, +} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::marker::Copy for Filetype {} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::clone::Clone for Filetype { + #[inline] + fn clone(&self) -> Filetype { + { + *self + } + } +} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::fmt::Debug for Filetype { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + match (&*self,) { + (&Filetype::Unknown,) => { + let mut debug_trait_builder = f.debug_tuple("Unknown"); + debug_trait_builder.finish() + } + (&Filetype::BlockDevice,) => { + let mut debug_trait_builder = f.debug_tuple("BlockDevice"); + debug_trait_builder.finish() + } + (&Filetype::CharacterDevice,) => { + let mut debug_trait_builder = f.debug_tuple("CharacterDevice"); + debug_trait_builder.finish() + } + (&Filetype::Directory,) => { + let mut debug_trait_builder = f.debug_tuple("Directory"); + debug_trait_builder.finish() + } + (&Filetype::RegularFile,) => { + let mut debug_trait_builder = f.debug_tuple("RegularFile"); + debug_trait_builder.finish() + } + (&Filetype::SocketDgram,) => { + let mut debug_trait_builder = f.debug_tuple("SocketDgram"); + debug_trait_builder.finish() + } + (&Filetype::SocketStream,) => { + let mut debug_trait_builder = f.debug_tuple("SocketStream"); + debug_trait_builder.finish() + } + (&Filetype::SymbolicLink,) => { + let mut debug_trait_builder = f.debug_tuple("SymbolicLink"); + debug_trait_builder.finish() + } + } + } +} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::hash::Hash for Filetype { + fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) -> () { + match (&*self,) { + _ => ::core::hash::Hash::hash( + &unsafe { ::core::intrinsics::discriminant_value(self) }, + state, + ), + } + } +} +impl ::core::marker::StructuralEq for Filetype {} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::cmp::Eq for Filetype { + #[inline] + #[doc(hidden)] + fn assert_receiver_is_total_eq(&self) -> () { + {} + } +} +impl ::core::marker::StructuralPartialEq for Filetype {} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::cmp::PartialEq for Filetype { + #[inline] + fn eq(&self, other: &Filetype) -> bool { + { + let __self_vi = unsafe { ::core::intrinsics::discriminant_value(&*self) } as u8; + let __arg_1_vi = unsafe { ::core::intrinsics::discriminant_value(&*other) } as u8; + if true && __self_vi == __arg_1_vi { + match (&*self, &*other) { + _ => true, + } + } else { + false + } + } + } +} +#[repr(C)] +pub struct Dirent { + pub d_next: Dircookie, + pub d_ino: Inode, + pub d_namlen: Dirnamlen, + pub d_type: Filetype, +} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::marker::Copy for Dirent {} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::clone::Clone for Dirent { + #[inline] + fn clone(&self) -> Dirent { + { + let _: ::core::clone::AssertParamIsClone; + let _: ::core::clone::AssertParamIsClone; + let _: ::core::clone::AssertParamIsClone; + let _: ::core::clone::AssertParamIsClone; + *self + } + } +} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::fmt::Debug for Dirent { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + match *self { + Dirent { + d_next: ref __self_0_0, + d_ino: ref __self_0_1, + d_namlen: ref __self_0_2, + d_type: ref __self_0_3, + } => { + let mut debug_trait_builder = f.debug_struct("Dirent"); + let _ = debug_trait_builder.field("d_next", &&(*__self_0_0)); + let _ = debug_trait_builder.field("d_ino", &&(*__self_0_1)); + let _ = debug_trait_builder.field("d_namlen", &&(*__self_0_2)); + let _ = debug_trait_builder.field("d_type", &&(*__self_0_3)); + debug_trait_builder.finish() + } + } + } +} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::hash::Hash for Dirent { + fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) -> () { + match *self { + Dirent { + d_next: ref __self_0_0, + d_ino: ref __self_0_1, + d_namlen: ref __self_0_2, + d_type: ref __self_0_3, + } => { + ::core::hash::Hash::hash(&(*__self_0_0), state); + ::core::hash::Hash::hash(&(*__self_0_1), state); + ::core::hash::Hash::hash(&(*__self_0_2), state); + ::core::hash::Hash::hash(&(*__self_0_3), state) + } + } + } +} +impl ::core::marker::StructuralEq for Dirent {} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::cmp::Eq for Dirent { + #[inline] + #[doc(hidden)] + fn assert_receiver_is_total_eq(&self) -> () { + { + let _: ::core::cmp::AssertParamIsEq; + let _: ::core::cmp::AssertParamIsEq; + let _: ::core::cmp::AssertParamIsEq; + let _: ::core::cmp::AssertParamIsEq; + } + } +} +impl ::core::marker::StructuralPartialEq for Dirent {} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::cmp::PartialEq for Dirent { + #[inline] + fn eq(&self, other: &Dirent) -> bool { + match *other { + Dirent { + d_next: ref __self_1_0, + d_ino: ref __self_1_1, + d_namlen: ref __self_1_2, + d_type: ref __self_1_3, + } => match *self { + Dirent { + d_next: ref __self_0_0, + d_ino: ref __self_0_1, + d_namlen: ref __self_0_2, + d_type: ref __self_0_3, + } => { + (*__self_0_0) == (*__self_1_0) + && (*__self_0_1) == (*__self_1_1) + && (*__self_0_2) == (*__self_1_2) + && (*__self_0_3) == (*__self_1_3) + } + }, + } + } + #[inline] + fn ne(&self, other: &Dirent) -> bool { + match *other { + Dirent { + d_next: ref __self_1_0, + d_ino: ref __self_1_1, + d_namlen: ref __self_1_2, + d_type: ref __self_1_3, + } => match *self { + Dirent { + d_next: ref __self_0_0, + d_ino: ref __self_0_1, + d_namlen: ref __self_0_2, + d_type: ref __self_0_3, + } => { + (*__self_0_0) != (*__self_1_0) + || (*__self_0_1) != (*__self_1_1) + || (*__self_0_2) != (*__self_1_2) + || (*__self_0_3) != (*__self_1_3) + } + }, + } + } +} +#[repr(u8)] +pub enum Advice { + Normal, + Sequential, + Random, + Willneed, + Dontneed, + Noreuse, +} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::marker::Copy for Advice {} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::clone::Clone for Advice { + #[inline] + fn clone(&self) -> Advice { + { + *self + } + } +} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::fmt::Debug for Advice { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + match (&*self,) { + (&Advice::Normal,) => { + let mut debug_trait_builder = f.debug_tuple("Normal"); + debug_trait_builder.finish() + } + (&Advice::Sequential,) => { + let mut debug_trait_builder = f.debug_tuple("Sequential"); + debug_trait_builder.finish() + } + (&Advice::Random,) => { + let mut debug_trait_builder = f.debug_tuple("Random"); + debug_trait_builder.finish() + } + (&Advice::Willneed,) => { + let mut debug_trait_builder = f.debug_tuple("Willneed"); + debug_trait_builder.finish() + } + (&Advice::Dontneed,) => { + let mut debug_trait_builder = f.debug_tuple("Dontneed"); + debug_trait_builder.finish() + } + (&Advice::Noreuse,) => { + let mut debug_trait_builder = f.debug_tuple("Noreuse"); + debug_trait_builder.finish() + } + } + } +} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::hash::Hash for Advice { + fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) -> () { + match (&*self,) { + _ => ::core::hash::Hash::hash( + &unsafe { ::core::intrinsics::discriminant_value(self) }, + state, + ), + } + } +} +impl ::core::marker::StructuralEq for Advice {} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::cmp::Eq for Advice { + #[inline] + #[doc(hidden)] + fn assert_receiver_is_total_eq(&self) -> () { + {} + } +} +impl ::core::marker::StructuralPartialEq for Advice {} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::cmp::PartialEq for Advice { + #[inline] + fn eq(&self, other: &Advice) -> bool { + { + let __self_vi = unsafe { ::core::intrinsics::discriminant_value(&*self) } as u8; + let __arg_1_vi = unsafe { ::core::intrinsics::discriminant_value(&*other) } as u8; + if true && __self_vi == __arg_1_vi { + match (&*self, &*other) { + _ => true, + } + } else { + false + } + } + } +} +#[repr(transparent)] +pub struct Fdflags(u16); +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::marker::Copy for Fdflags {} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::clone::Clone for Fdflags { + #[inline] + fn clone(&self) -> Fdflags { + { + let _: ::core::clone::AssertParamIsClone; + *self + } + } +} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::fmt::Debug for Fdflags { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + match *self { + Fdflags(ref __self_0_0) => { + let mut debug_trait_builder = f.debug_tuple("Fdflags"); + let _ = debug_trait_builder.field(&&(*__self_0_0)); + debug_trait_builder.finish() + } + } + } +} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::hash::Hash for Fdflags { + fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) -> () { + match *self { + Fdflags(ref __self_0_0) => ::core::hash::Hash::hash(&(*__self_0_0), state), + } + } +} +impl ::core::marker::StructuralEq for Fdflags {} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::cmp::Eq for Fdflags { + #[inline] + #[doc(hidden)] + fn assert_receiver_is_total_eq(&self) -> () { + { + let _: ::core::cmp::AssertParamIsEq; + } + } +} +impl ::core::marker::StructuralPartialEq for Fdflags {} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::cmp::PartialEq for Fdflags { + #[inline] + fn eq(&self, other: &Fdflags) -> bool { + match *other { + Fdflags(ref __self_1_0) => match *self { + Fdflags(ref __self_0_0) => (*__self_0_0) == (*__self_1_0), + }, + } + } + #[inline] + fn ne(&self, other: &Fdflags) -> bool { + match *other { + Fdflags(ref __self_1_0) => match *self { + Fdflags(ref __self_0_0) => (*__self_0_0) != (*__self_1_0), + }, + } + } +} +impl Fdflags { + pub const APPEND: Fdflags = Fdflags(1); + pub const DSYNC: Fdflags = Fdflags(2); + pub const NONBLOCK: Fdflags = Fdflags(4); + pub const RSYNC: Fdflags = Fdflags(8); + pub const SYNC: Fdflags = Fdflags(16); +} +#[repr(C)] +pub struct Fdstat { + pub fs_filetype: Filetype, + pub fs_flags: Fdflags, + pub fs_rights_base: Rights, + pub fs_rights_inheriting: Rights, +} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::marker::Copy for Fdstat {} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::clone::Clone for Fdstat { + #[inline] + fn clone(&self) -> Fdstat { + { + let _: ::core::clone::AssertParamIsClone; + let _: ::core::clone::AssertParamIsClone; + let _: ::core::clone::AssertParamIsClone; + let _: ::core::clone::AssertParamIsClone; + *self + } + } +} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::fmt::Debug for Fdstat { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + match *self { + Fdstat { + fs_filetype: ref __self_0_0, + fs_flags: ref __self_0_1, + fs_rights_base: ref __self_0_2, + fs_rights_inheriting: ref __self_0_3, + } => { + let mut debug_trait_builder = f.debug_struct("Fdstat"); + let _ = debug_trait_builder.field("fs_filetype", &&(*__self_0_0)); + let _ = debug_trait_builder.field("fs_flags", &&(*__self_0_1)); + let _ = debug_trait_builder.field("fs_rights_base", &&(*__self_0_2)); + let _ = debug_trait_builder.field("fs_rights_inheriting", &&(*__self_0_3)); + debug_trait_builder.finish() + } + } + } +} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::hash::Hash for Fdstat { + fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) -> () { + match *self { + Fdstat { + fs_filetype: ref __self_0_0, + fs_flags: ref __self_0_1, + fs_rights_base: ref __self_0_2, + fs_rights_inheriting: ref __self_0_3, + } => { + ::core::hash::Hash::hash(&(*__self_0_0), state); + ::core::hash::Hash::hash(&(*__self_0_1), state); + ::core::hash::Hash::hash(&(*__self_0_2), state); + ::core::hash::Hash::hash(&(*__self_0_3), state) + } + } + } +} +impl ::core::marker::StructuralEq for Fdstat {} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::cmp::Eq for Fdstat { + #[inline] + #[doc(hidden)] + fn assert_receiver_is_total_eq(&self) -> () { + { + let _: ::core::cmp::AssertParamIsEq; + let _: ::core::cmp::AssertParamIsEq; + let _: ::core::cmp::AssertParamIsEq; + let _: ::core::cmp::AssertParamIsEq; + } + } +} +impl ::core::marker::StructuralPartialEq for Fdstat {} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::cmp::PartialEq for Fdstat { + #[inline] + fn eq(&self, other: &Fdstat) -> bool { + match *other { + Fdstat { + fs_filetype: ref __self_1_0, + fs_flags: ref __self_1_1, + fs_rights_base: ref __self_1_2, + fs_rights_inheriting: ref __self_1_3, + } => match *self { + Fdstat { + fs_filetype: ref __self_0_0, + fs_flags: ref __self_0_1, + fs_rights_base: ref __self_0_2, + fs_rights_inheriting: ref __self_0_3, + } => { + (*__self_0_0) == (*__self_1_0) + && (*__self_0_1) == (*__self_1_1) + && (*__self_0_2) == (*__self_1_2) + && (*__self_0_3) == (*__self_1_3) + } + }, + } + } + #[inline] + fn ne(&self, other: &Fdstat) -> bool { + match *other { + Fdstat { + fs_filetype: ref __self_1_0, + fs_flags: ref __self_1_1, + fs_rights_base: ref __self_1_2, + fs_rights_inheriting: ref __self_1_3, + } => match *self { + Fdstat { + fs_filetype: ref __self_0_0, + fs_flags: ref __self_0_1, + fs_rights_base: ref __self_0_2, + fs_rights_inheriting: ref __self_0_3, + } => { + (*__self_0_0) != (*__self_1_0) + || (*__self_0_1) != (*__self_1_1) + || (*__self_0_2) != (*__self_1_2) + || (*__self_0_3) != (*__self_1_3) + } + }, + } + } +} +pub type Device = u64; +#[repr(transparent)] +pub struct Fstflags(u16); +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::marker::Copy for Fstflags {} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::clone::Clone for Fstflags { + #[inline] + fn clone(&self) -> Fstflags { + { + let _: ::core::clone::AssertParamIsClone; + *self + } + } +} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::fmt::Debug for Fstflags { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + match *self { + Fstflags(ref __self_0_0) => { + let mut debug_trait_builder = f.debug_tuple("Fstflags"); + let _ = debug_trait_builder.field(&&(*__self_0_0)); + debug_trait_builder.finish() + } + } + } +} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::hash::Hash for Fstflags { + fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) -> () { + match *self { + Fstflags(ref __self_0_0) => ::core::hash::Hash::hash(&(*__self_0_0), state), + } + } +} +impl ::core::marker::StructuralEq for Fstflags {} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::cmp::Eq for Fstflags { + #[inline] + #[doc(hidden)] + fn assert_receiver_is_total_eq(&self) -> () { + { + let _: ::core::cmp::AssertParamIsEq; + } + } +} +impl ::core::marker::StructuralPartialEq for Fstflags {} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::cmp::PartialEq for Fstflags { + #[inline] + fn eq(&self, other: &Fstflags) -> bool { + match *other { + Fstflags(ref __self_1_0) => match *self { + Fstflags(ref __self_0_0) => (*__self_0_0) == (*__self_1_0), + }, + } + } + #[inline] + fn ne(&self, other: &Fstflags) -> bool { + match *other { + Fstflags(ref __self_1_0) => match *self { + Fstflags(ref __self_0_0) => (*__self_0_0) != (*__self_1_0), + }, + } + } +} +impl Fstflags { + pub const ATIM: Fstflags = Fstflags(1); + pub const ATIM_NOW: Fstflags = Fstflags(2); + pub const MTIM: Fstflags = Fstflags(4); + pub const MTIM_NOW: Fstflags = Fstflags(8); +} +#[repr(transparent)] +pub struct Lookupflags(u32); +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::marker::Copy for Lookupflags {} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::clone::Clone for Lookupflags { + #[inline] + fn clone(&self) -> Lookupflags { + { + let _: ::core::clone::AssertParamIsClone; + *self + } + } +} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::fmt::Debug for Lookupflags { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + match *self { + Lookupflags(ref __self_0_0) => { + let mut debug_trait_builder = f.debug_tuple("Lookupflags"); + let _ = debug_trait_builder.field(&&(*__self_0_0)); + debug_trait_builder.finish() + } + } + } +} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::hash::Hash for Lookupflags { + fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) -> () { + match *self { + Lookupflags(ref __self_0_0) => ::core::hash::Hash::hash(&(*__self_0_0), state), + } + } +} +impl ::core::marker::StructuralEq for Lookupflags {} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::cmp::Eq for Lookupflags { + #[inline] + #[doc(hidden)] + fn assert_receiver_is_total_eq(&self) -> () { + { + let _: ::core::cmp::AssertParamIsEq; + } + } +} +impl ::core::marker::StructuralPartialEq for Lookupflags {} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::cmp::PartialEq for Lookupflags { + #[inline] + fn eq(&self, other: &Lookupflags) -> bool { + match *other { + Lookupflags(ref __self_1_0) => match *self { + Lookupflags(ref __self_0_0) => (*__self_0_0) == (*__self_1_0), + }, + } + } + #[inline] + fn ne(&self, other: &Lookupflags) -> bool { + match *other { + Lookupflags(ref __self_1_0) => match *self { + Lookupflags(ref __self_0_0) => (*__self_0_0) != (*__self_1_0), + }, + } + } +} +impl Lookupflags { + pub const SYMLINK_FOLLOW: Lookupflags = Lookupflags(1); +} +#[repr(transparent)] +pub struct Oflags(u16); +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::marker::Copy for Oflags {} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::clone::Clone for Oflags { + #[inline] + fn clone(&self) -> Oflags { + { + let _: ::core::clone::AssertParamIsClone; + *self + } + } +} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::fmt::Debug for Oflags { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + match *self { + Oflags(ref __self_0_0) => { + let mut debug_trait_builder = f.debug_tuple("Oflags"); + let _ = debug_trait_builder.field(&&(*__self_0_0)); + debug_trait_builder.finish() + } + } + } +} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::hash::Hash for Oflags { + fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) -> () { + match *self { + Oflags(ref __self_0_0) => ::core::hash::Hash::hash(&(*__self_0_0), state), + } + } +} +impl ::core::marker::StructuralEq for Oflags {} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::cmp::Eq for Oflags { + #[inline] + #[doc(hidden)] + fn assert_receiver_is_total_eq(&self) -> () { + { + let _: ::core::cmp::AssertParamIsEq; + } + } +} +impl ::core::marker::StructuralPartialEq for Oflags {} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::cmp::PartialEq for Oflags { + #[inline] + fn eq(&self, other: &Oflags) -> bool { + match *other { + Oflags(ref __self_1_0) => match *self { + Oflags(ref __self_0_0) => (*__self_0_0) == (*__self_1_0), + }, + } + } + #[inline] + fn ne(&self, other: &Oflags) -> bool { + match *other { + Oflags(ref __self_1_0) => match *self { + Oflags(ref __self_0_0) => (*__self_0_0) != (*__self_1_0), + }, + } + } +} +impl Oflags { + pub const CREAT: Oflags = Oflags(1); + pub const DIRECTORY: Oflags = Oflags(2); + pub const EXCL: Oflags = Oflags(4); + pub const TRUNC: Oflags = Oflags(8); +} +pub type Linkcount = u64; +#[repr(C)] +pub struct Filestat { + pub dev: Device, + pub ino: Inode, + pub filetype: Filetype, + pub nlink: Linkcount, + pub size: Filesize, + pub atim: Timestamp, + pub mtim: Timestamp, + pub ctim: Timestamp, +} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::marker::Copy for Filestat {} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::clone::Clone for Filestat { + #[inline] + fn clone(&self) -> Filestat { + { + let _: ::core::clone::AssertParamIsClone; + let _: ::core::clone::AssertParamIsClone; + let _: ::core::clone::AssertParamIsClone; + let _: ::core::clone::AssertParamIsClone; + let _: ::core::clone::AssertParamIsClone; + let _: ::core::clone::AssertParamIsClone; + let _: ::core::clone::AssertParamIsClone; + let _: ::core::clone::AssertParamIsClone; + *self + } + } +} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::fmt::Debug for Filestat { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + match *self { + Filestat { + dev: ref __self_0_0, + ino: ref __self_0_1, + filetype: ref __self_0_2, + nlink: ref __self_0_3, + size: ref __self_0_4, + atim: ref __self_0_5, + mtim: ref __self_0_6, + ctim: ref __self_0_7, + } => { + let mut debug_trait_builder = f.debug_struct("Filestat"); + let _ = debug_trait_builder.field("dev", &&(*__self_0_0)); + let _ = debug_trait_builder.field("ino", &&(*__self_0_1)); + let _ = debug_trait_builder.field("filetype", &&(*__self_0_2)); + let _ = debug_trait_builder.field("nlink", &&(*__self_0_3)); + let _ = debug_trait_builder.field("size", &&(*__self_0_4)); + let _ = debug_trait_builder.field("atim", &&(*__self_0_5)); + let _ = debug_trait_builder.field("mtim", &&(*__self_0_6)); + let _ = debug_trait_builder.field("ctim", &&(*__self_0_7)); + debug_trait_builder.finish() + } + } + } +} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::hash::Hash for Filestat { + fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) -> () { + match *self { + Filestat { + dev: ref __self_0_0, + ino: ref __self_0_1, + filetype: ref __self_0_2, + nlink: ref __self_0_3, + size: ref __self_0_4, + atim: ref __self_0_5, + mtim: ref __self_0_6, + ctim: ref __self_0_7, + } => { + ::core::hash::Hash::hash(&(*__self_0_0), state); + ::core::hash::Hash::hash(&(*__self_0_1), state); + ::core::hash::Hash::hash(&(*__self_0_2), state); + ::core::hash::Hash::hash(&(*__self_0_3), state); + ::core::hash::Hash::hash(&(*__self_0_4), state); + ::core::hash::Hash::hash(&(*__self_0_5), state); + ::core::hash::Hash::hash(&(*__self_0_6), state); + ::core::hash::Hash::hash(&(*__self_0_7), state) + } + } + } +} +impl ::core::marker::StructuralEq for Filestat {} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::cmp::Eq for Filestat { + #[inline] + #[doc(hidden)] + fn assert_receiver_is_total_eq(&self) -> () { + { + let _: ::core::cmp::AssertParamIsEq; + let _: ::core::cmp::AssertParamIsEq; + let _: ::core::cmp::AssertParamIsEq; + let _: ::core::cmp::AssertParamIsEq; + let _: ::core::cmp::AssertParamIsEq; + let _: ::core::cmp::AssertParamIsEq; + let _: ::core::cmp::AssertParamIsEq; + let _: ::core::cmp::AssertParamIsEq; + } + } +} +impl ::core::marker::StructuralPartialEq for Filestat {} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::cmp::PartialEq for Filestat { + #[inline] + fn eq(&self, other: &Filestat) -> bool { + match *other { + Filestat { + dev: ref __self_1_0, + ino: ref __self_1_1, + filetype: ref __self_1_2, + nlink: ref __self_1_3, + size: ref __self_1_4, + atim: ref __self_1_5, + mtim: ref __self_1_6, + ctim: ref __self_1_7, + } => match *self { + Filestat { + dev: ref __self_0_0, + ino: ref __self_0_1, + filetype: ref __self_0_2, + nlink: ref __self_0_3, + size: ref __self_0_4, + atim: ref __self_0_5, + mtim: ref __self_0_6, + ctim: ref __self_0_7, + } => { + (*__self_0_0) == (*__self_1_0) + && (*__self_0_1) == (*__self_1_1) + && (*__self_0_2) == (*__self_1_2) + && (*__self_0_3) == (*__self_1_3) + && (*__self_0_4) == (*__self_1_4) + && (*__self_0_5) == (*__self_1_5) + && (*__self_0_6) == (*__self_1_6) + && (*__self_0_7) == (*__self_1_7) + } + }, + } + } + #[inline] + fn ne(&self, other: &Filestat) -> bool { + match *other { + Filestat { + dev: ref __self_1_0, + ino: ref __self_1_1, + filetype: ref __self_1_2, + nlink: ref __self_1_3, + size: ref __self_1_4, + atim: ref __self_1_5, + mtim: ref __self_1_6, + ctim: ref __self_1_7, + } => match *self { + Filestat { + dev: ref __self_0_0, + ino: ref __self_0_1, + filetype: ref __self_0_2, + nlink: ref __self_0_3, + size: ref __self_0_4, + atim: ref __self_0_5, + mtim: ref __self_0_6, + ctim: ref __self_0_7, + } => { + (*__self_0_0) != (*__self_1_0) + || (*__self_0_1) != (*__self_1_1) + || (*__self_0_2) != (*__self_1_2) + || (*__self_0_3) != (*__self_1_3) + || (*__self_0_4) != (*__self_1_4) + || (*__self_0_5) != (*__self_1_5) + || (*__self_0_6) != (*__self_1_6) + || (*__self_0_7) != (*__self_1_7) + } + }, + } + } +} +pub type Userdata = u64; +#[repr(u8)] +pub enum Eventtype { + Clock, + FdRead, + FdWrite, +} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::marker::Copy for Eventtype {} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::clone::Clone for Eventtype { + #[inline] + fn clone(&self) -> Eventtype { + { + *self + } + } +} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::fmt::Debug for Eventtype { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + match (&*self,) { + (&Eventtype::Clock,) => { + let mut debug_trait_builder = f.debug_tuple("Clock"); + debug_trait_builder.finish() + } + (&Eventtype::FdRead,) => { + let mut debug_trait_builder = f.debug_tuple("FdRead"); + debug_trait_builder.finish() + } + (&Eventtype::FdWrite,) => { + let mut debug_trait_builder = f.debug_tuple("FdWrite"); + debug_trait_builder.finish() + } + } + } +} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::hash::Hash for Eventtype { + fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) -> () { + match (&*self,) { + _ => ::core::hash::Hash::hash( + &unsafe { ::core::intrinsics::discriminant_value(self) }, + state, + ), + } + } +} +impl ::core::marker::StructuralEq for Eventtype {} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::cmp::Eq for Eventtype { + #[inline] + #[doc(hidden)] + fn assert_receiver_is_total_eq(&self) -> () { + {} + } +} +impl ::core::marker::StructuralPartialEq for Eventtype {} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::cmp::PartialEq for Eventtype { + #[inline] + fn eq(&self, other: &Eventtype) -> bool { + { + let __self_vi = unsafe { ::core::intrinsics::discriminant_value(&*self) } as u8; + let __arg_1_vi = unsafe { ::core::intrinsics::discriminant_value(&*other) } as u8; + if true && __self_vi == __arg_1_vi { + match (&*self, &*other) { + _ => true, + } + } else { + false + } + } + } +} +#[repr(transparent)] +pub struct Eventrwflags(u16); +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::marker::Copy for Eventrwflags {} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::clone::Clone for Eventrwflags { + #[inline] + fn clone(&self) -> Eventrwflags { + { + let _: ::core::clone::AssertParamIsClone; + *self + } + } +} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::fmt::Debug for Eventrwflags { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + match *self { + Eventrwflags(ref __self_0_0) => { + let mut debug_trait_builder = f.debug_tuple("Eventrwflags"); + let _ = debug_trait_builder.field(&&(*__self_0_0)); + debug_trait_builder.finish() + } + } + } +} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::hash::Hash for Eventrwflags { + fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) -> () { + match *self { + Eventrwflags(ref __self_0_0) => ::core::hash::Hash::hash(&(*__self_0_0), state), + } + } +} +impl ::core::marker::StructuralEq for Eventrwflags {} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::cmp::Eq for Eventrwflags { + #[inline] + #[doc(hidden)] + fn assert_receiver_is_total_eq(&self) -> () { + { + let _: ::core::cmp::AssertParamIsEq; + } + } +} +impl ::core::marker::StructuralPartialEq for Eventrwflags {} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::cmp::PartialEq for Eventrwflags { + #[inline] + fn eq(&self, other: &Eventrwflags) -> bool { + match *other { + Eventrwflags(ref __self_1_0) => match *self { + Eventrwflags(ref __self_0_0) => (*__self_0_0) == (*__self_1_0), + }, + } + } + #[inline] + fn ne(&self, other: &Eventrwflags) -> bool { + match *other { + Eventrwflags(ref __self_1_0) => match *self { + Eventrwflags(ref __self_0_0) => (*__self_0_0) != (*__self_1_0), + }, + } + } +} +impl Eventrwflags { + pub const FD_READWRITE_HANGUP: Eventrwflags = Eventrwflags(1); +} +#[repr(C)] +pub struct EventFdReadwrite { + pub nbytes: Filesize, + pub flags: Eventrwflags, +} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::marker::Copy for EventFdReadwrite {} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::clone::Clone for EventFdReadwrite { + #[inline] + fn clone(&self) -> EventFdReadwrite { + { + let _: ::core::clone::AssertParamIsClone; + let _: ::core::clone::AssertParamIsClone; + *self + } + } +} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::fmt::Debug for EventFdReadwrite { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + match *self { + EventFdReadwrite { + nbytes: ref __self_0_0, + flags: ref __self_0_1, + } => { + let mut debug_trait_builder = f.debug_struct("EventFdReadwrite"); + let _ = debug_trait_builder.field("nbytes", &&(*__self_0_0)); + let _ = debug_trait_builder.field("flags", &&(*__self_0_1)); + debug_trait_builder.finish() + } + } + } +} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::hash::Hash for EventFdReadwrite { + fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) -> () { + match *self { + EventFdReadwrite { + nbytes: ref __self_0_0, + flags: ref __self_0_1, + } => { + ::core::hash::Hash::hash(&(*__self_0_0), state); + ::core::hash::Hash::hash(&(*__self_0_1), state) + } + } + } +} +impl ::core::marker::StructuralEq for EventFdReadwrite {} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::cmp::Eq for EventFdReadwrite { + #[inline] + #[doc(hidden)] + fn assert_receiver_is_total_eq(&self) -> () { + { + let _: ::core::cmp::AssertParamIsEq; + let _: ::core::cmp::AssertParamIsEq; + } + } +} +impl ::core::marker::StructuralPartialEq for EventFdReadwrite {} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::cmp::PartialEq for EventFdReadwrite { + #[inline] + fn eq(&self, other: &EventFdReadwrite) -> bool { + match *other { + EventFdReadwrite { + nbytes: ref __self_1_0, + flags: ref __self_1_1, + } => match *self { + EventFdReadwrite { + nbytes: ref __self_0_0, + flags: ref __self_0_1, + } => (*__self_0_0) == (*__self_1_0) && (*__self_0_1) == (*__self_1_1), + }, + } + } + #[inline] + fn ne(&self, other: &EventFdReadwrite) -> bool { + match *other { + EventFdReadwrite { + nbytes: ref __self_1_0, + flags: ref __self_1_1, + } => match *self { + EventFdReadwrite { + nbytes: ref __self_0_0, + flags: ref __self_0_1, + } => (*__self_0_0) != (*__self_1_0) || (*__self_0_1) != (*__self_1_1), + }, + } + } +} +#[repr(C)] +#[allow(missing_debug_implementations)] +pub union EventU { + pub fd_readwrite: EventFdReadwrite, +} +#[automatically_derived] +#[allow(unused_qualifications)] +#[allow(missing_debug_implementations)] +impl ::core::marker::Copy for EventU {} +#[automatically_derived] +#[allow(unused_qualifications)] +#[allow(missing_debug_implementations)] +impl ::core::clone::Clone for EventU { + #[inline] + fn clone(&self) -> EventU { + { + let _: ::core::clone::AssertParamIsCopy; + *self + } + } +} +#[repr(C)] +#[allow(missing_debug_implementations)] +pub struct Event { + pub userdata: Userdata, + pub error: Errno, + pub r#type: Eventtype, + pub u: EventU, +} +#[automatically_derived] +#[allow(unused_qualifications)] +#[allow(missing_debug_implementations)] +impl ::core::marker::Copy for Event {} +#[automatically_derived] +#[allow(unused_qualifications)] +#[allow(missing_debug_implementations)] +impl ::core::clone::Clone for Event { + #[inline] + fn clone(&self) -> Event { + { + let _: ::core::clone::AssertParamIsClone; + let _: ::core::clone::AssertParamIsClone; + let _: ::core::clone::AssertParamIsClone; + let _: ::core::clone::AssertParamIsClone; + *self + } + } +} +#[repr(transparent)] +pub struct Subclockflags(u16); +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::marker::Copy for Subclockflags {} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::clone::Clone for Subclockflags { + #[inline] + fn clone(&self) -> Subclockflags { + { + let _: ::core::clone::AssertParamIsClone; + *self + } + } +} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::fmt::Debug for Subclockflags { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + match *self { + Subclockflags(ref __self_0_0) => { + let mut debug_trait_builder = f.debug_tuple("Subclockflags"); + let _ = debug_trait_builder.field(&&(*__self_0_0)); + debug_trait_builder.finish() + } + } + } +} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::hash::Hash for Subclockflags { + fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) -> () { + match *self { + Subclockflags(ref __self_0_0) => ::core::hash::Hash::hash(&(*__self_0_0), state), + } + } +} +impl ::core::marker::StructuralEq for Subclockflags {} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::cmp::Eq for Subclockflags { + #[inline] + #[doc(hidden)] + fn assert_receiver_is_total_eq(&self) -> () { + { + let _: ::core::cmp::AssertParamIsEq; + } + } +} +impl ::core::marker::StructuralPartialEq for Subclockflags {} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::cmp::PartialEq for Subclockflags { + #[inline] + fn eq(&self, other: &Subclockflags) -> bool { + match *other { + Subclockflags(ref __self_1_0) => match *self { + Subclockflags(ref __self_0_0) => (*__self_0_0) == (*__self_1_0), + }, + } + } + #[inline] + fn ne(&self, other: &Subclockflags) -> bool { + match *other { + Subclockflags(ref __self_1_0) => match *self { + Subclockflags(ref __self_0_0) => (*__self_0_0) != (*__self_1_0), + }, + } + } +} +impl Subclockflags { + pub const SUBSCRIPTION_CLOCK_ABSTIME: Subclockflags = Subclockflags(1); +} +#[repr(C)] +pub struct SubscriptionClock { + pub id: Clockid, + pub timeout: Timestamp, + pub precision: Timestamp, + pub flags: Subclockflags, +} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::marker::Copy for SubscriptionClock {} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::clone::Clone for SubscriptionClock { + #[inline] + fn clone(&self) -> SubscriptionClock { + { + let _: ::core::clone::AssertParamIsClone; + let _: ::core::clone::AssertParamIsClone; + let _: ::core::clone::AssertParamIsClone; + let _: ::core::clone::AssertParamIsClone; + *self + } + } +} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::fmt::Debug for SubscriptionClock { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + match *self { + SubscriptionClock { + id: ref __self_0_0, + timeout: ref __self_0_1, + precision: ref __self_0_2, + flags: ref __self_0_3, + } => { + let mut debug_trait_builder = f.debug_struct("SubscriptionClock"); + let _ = debug_trait_builder.field("id", &&(*__self_0_0)); + let _ = debug_trait_builder.field("timeout", &&(*__self_0_1)); + let _ = debug_trait_builder.field("precision", &&(*__self_0_2)); + let _ = debug_trait_builder.field("flags", &&(*__self_0_3)); + debug_trait_builder.finish() + } + } + } +} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::hash::Hash for SubscriptionClock { + fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) -> () { + match *self { + SubscriptionClock { + id: ref __self_0_0, + timeout: ref __self_0_1, + precision: ref __self_0_2, + flags: ref __self_0_3, + } => { + ::core::hash::Hash::hash(&(*__self_0_0), state); + ::core::hash::Hash::hash(&(*__self_0_1), state); + ::core::hash::Hash::hash(&(*__self_0_2), state); + ::core::hash::Hash::hash(&(*__self_0_3), state) + } + } + } +} +impl ::core::marker::StructuralEq for SubscriptionClock {} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::cmp::Eq for SubscriptionClock { + #[inline] + #[doc(hidden)] + fn assert_receiver_is_total_eq(&self) -> () { + { + let _: ::core::cmp::AssertParamIsEq; + let _: ::core::cmp::AssertParamIsEq; + let _: ::core::cmp::AssertParamIsEq; + let _: ::core::cmp::AssertParamIsEq; + } + } +} +impl ::core::marker::StructuralPartialEq for SubscriptionClock {} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::cmp::PartialEq for SubscriptionClock { + #[inline] + fn eq(&self, other: &SubscriptionClock) -> bool { + match *other { + SubscriptionClock { + id: ref __self_1_0, + timeout: ref __self_1_1, + precision: ref __self_1_2, + flags: ref __self_1_3, + } => match *self { + SubscriptionClock { + id: ref __self_0_0, + timeout: ref __self_0_1, + precision: ref __self_0_2, + flags: ref __self_0_3, + } => { + (*__self_0_0) == (*__self_1_0) + && (*__self_0_1) == (*__self_1_1) + && (*__self_0_2) == (*__self_1_2) + && (*__self_0_3) == (*__self_1_3) + } + }, + } + } + #[inline] + fn ne(&self, other: &SubscriptionClock) -> bool { + match *other { + SubscriptionClock { + id: ref __self_1_0, + timeout: ref __self_1_1, + precision: ref __self_1_2, + flags: ref __self_1_3, + } => match *self { + SubscriptionClock { + id: ref __self_0_0, + timeout: ref __self_0_1, + precision: ref __self_0_2, + flags: ref __self_0_3, + } => { + (*__self_0_0) != (*__self_1_0) + || (*__self_0_1) != (*__self_1_1) + || (*__self_0_2) != (*__self_1_2) + || (*__self_0_3) != (*__self_1_3) + } + }, + } + } +} +#[repr(C)] +pub struct SubscriptionFdReadwrite { + pub file_descriptor: Fd, +} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::marker::Copy for SubscriptionFdReadwrite {} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::clone::Clone for SubscriptionFdReadwrite { + #[inline] + fn clone(&self) -> SubscriptionFdReadwrite { + { + let _: ::core::clone::AssertParamIsClone; + *self + } + } +} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::fmt::Debug for SubscriptionFdReadwrite { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + match *self { + SubscriptionFdReadwrite { + file_descriptor: ref __self_0_0, + } => { + let mut debug_trait_builder = f.debug_struct("SubscriptionFdReadwrite"); + let _ = debug_trait_builder.field("file_descriptor", &&(*__self_0_0)); + debug_trait_builder.finish() + } + } + } +} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::hash::Hash for SubscriptionFdReadwrite { + fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) -> () { + match *self { + SubscriptionFdReadwrite { + file_descriptor: ref __self_0_0, + } => ::core::hash::Hash::hash(&(*__self_0_0), state), + } + } +} +impl ::core::marker::StructuralEq for SubscriptionFdReadwrite {} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::cmp::Eq for SubscriptionFdReadwrite { + #[inline] + #[doc(hidden)] + fn assert_receiver_is_total_eq(&self) -> () { + { + let _: ::core::cmp::AssertParamIsEq; + } + } +} +impl ::core::marker::StructuralPartialEq for SubscriptionFdReadwrite {} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::cmp::PartialEq for SubscriptionFdReadwrite { + #[inline] + fn eq(&self, other: &SubscriptionFdReadwrite) -> bool { + match *other { + SubscriptionFdReadwrite { + file_descriptor: ref __self_1_0, + } => match *self { + SubscriptionFdReadwrite { + file_descriptor: ref __self_0_0, + } => (*__self_0_0) == (*__self_1_0), + }, + } + } + #[inline] + fn ne(&self, other: &SubscriptionFdReadwrite) -> bool { + match *other { + SubscriptionFdReadwrite { + file_descriptor: ref __self_1_0, + } => match *self { + SubscriptionFdReadwrite { + file_descriptor: ref __self_0_0, + } => (*__self_0_0) != (*__self_1_0), + }, + } + } +} +#[repr(C)] +#[allow(missing_debug_implementations)] +pub union SubscriptionU { + pub clock: SubscriptionClock, + pub fd_readwrite: SubscriptionFdReadwrite, +} +#[automatically_derived] +#[allow(unused_qualifications)] +#[allow(missing_debug_implementations)] +impl ::core::marker::Copy for SubscriptionU {} +#[automatically_derived] +#[allow(unused_qualifications)] +#[allow(missing_debug_implementations)] +impl ::core::clone::Clone for SubscriptionU { + #[inline] + fn clone(&self) -> SubscriptionU { + { + let _: ::core::clone::AssertParamIsCopy; + *self + } + } +} +#[repr(C)] +#[allow(missing_debug_implementations)] +pub struct Subscription { + pub userdata: Userdata, + pub r#type: Eventtype, + pub u: SubscriptionU, +} +#[automatically_derived] +#[allow(unused_qualifications)] +#[allow(missing_debug_implementations)] +impl ::core::marker::Copy for Subscription {} +#[automatically_derived] +#[allow(unused_qualifications)] +#[allow(missing_debug_implementations)] +impl ::core::clone::Clone for Subscription { + #[inline] + fn clone(&self) -> Subscription { + { + let _: ::core::clone::AssertParamIsClone; + let _: ::core::clone::AssertParamIsClone; + let _: ::core::clone::AssertParamIsClone; + *self + } + } +} +pub type Exitcode = u32; +#[repr(u8)] +pub enum Signal { + None, + Hup, + Int, + Quit, + Ill, + Trap, + Abrt, + Bus, + Fpe, + Kill, + Usr1, + Segv, + Usr2, + Pipe, + Alrm, + Term, + Chld, + Cont, + Stop, + Tstp, + Ttin, + Ttou, + Urg, + Xcpu, + Xfsz, + Vtalrm, + Prof, + Winch, + Poll, + Pwr, + Sys, +} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::marker::Copy for Signal {} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::clone::Clone for Signal { + #[inline] + fn clone(&self) -> Signal { + { + *self + } + } +} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::fmt::Debug for Signal { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + match (&*self,) { + (&Signal::None,) => { + let mut debug_trait_builder = f.debug_tuple("None"); + debug_trait_builder.finish() + } + (&Signal::Hup,) => { + let mut debug_trait_builder = f.debug_tuple("Hup"); + debug_trait_builder.finish() + } + (&Signal::Int,) => { + let mut debug_trait_builder = f.debug_tuple("Int"); + debug_trait_builder.finish() + } + (&Signal::Quit,) => { + let mut debug_trait_builder = f.debug_tuple("Quit"); + debug_trait_builder.finish() + } + (&Signal::Ill,) => { + let mut debug_trait_builder = f.debug_tuple("Ill"); + debug_trait_builder.finish() + } + (&Signal::Trap,) => { + let mut debug_trait_builder = f.debug_tuple("Trap"); + debug_trait_builder.finish() + } + (&Signal::Abrt,) => { + let mut debug_trait_builder = f.debug_tuple("Abrt"); + debug_trait_builder.finish() + } + (&Signal::Bus,) => { + let mut debug_trait_builder = f.debug_tuple("Bus"); + debug_trait_builder.finish() + } + (&Signal::Fpe,) => { + let mut debug_trait_builder = f.debug_tuple("Fpe"); + debug_trait_builder.finish() + } + (&Signal::Kill,) => { + let mut debug_trait_builder = f.debug_tuple("Kill"); + debug_trait_builder.finish() + } + (&Signal::Usr1,) => { + let mut debug_trait_builder = f.debug_tuple("Usr1"); + debug_trait_builder.finish() + } + (&Signal::Segv,) => { + let mut debug_trait_builder = f.debug_tuple("Segv"); + debug_trait_builder.finish() + } + (&Signal::Usr2,) => { + let mut debug_trait_builder = f.debug_tuple("Usr2"); + debug_trait_builder.finish() + } + (&Signal::Pipe,) => { + let mut debug_trait_builder = f.debug_tuple("Pipe"); + debug_trait_builder.finish() + } + (&Signal::Alrm,) => { + let mut debug_trait_builder = f.debug_tuple("Alrm"); + debug_trait_builder.finish() + } + (&Signal::Term,) => { + let mut debug_trait_builder = f.debug_tuple("Term"); + debug_trait_builder.finish() + } + (&Signal::Chld,) => { + let mut debug_trait_builder = f.debug_tuple("Chld"); + debug_trait_builder.finish() + } + (&Signal::Cont,) => { + let mut debug_trait_builder = f.debug_tuple("Cont"); + debug_trait_builder.finish() + } + (&Signal::Stop,) => { + let mut debug_trait_builder = f.debug_tuple("Stop"); + debug_trait_builder.finish() + } + (&Signal::Tstp,) => { + let mut debug_trait_builder = f.debug_tuple("Tstp"); + debug_trait_builder.finish() + } + (&Signal::Ttin,) => { + let mut debug_trait_builder = f.debug_tuple("Ttin"); + debug_trait_builder.finish() + } + (&Signal::Ttou,) => { + let mut debug_trait_builder = f.debug_tuple("Ttou"); + debug_trait_builder.finish() + } + (&Signal::Urg,) => { + let mut debug_trait_builder = f.debug_tuple("Urg"); + debug_trait_builder.finish() + } + (&Signal::Xcpu,) => { + let mut debug_trait_builder = f.debug_tuple("Xcpu"); + debug_trait_builder.finish() + } + (&Signal::Xfsz,) => { + let mut debug_trait_builder = f.debug_tuple("Xfsz"); + debug_trait_builder.finish() + } + (&Signal::Vtalrm,) => { + let mut debug_trait_builder = f.debug_tuple("Vtalrm"); + debug_trait_builder.finish() + } + (&Signal::Prof,) => { + let mut debug_trait_builder = f.debug_tuple("Prof"); + debug_trait_builder.finish() + } + (&Signal::Winch,) => { + let mut debug_trait_builder = f.debug_tuple("Winch"); + debug_trait_builder.finish() + } + (&Signal::Poll,) => { + let mut debug_trait_builder = f.debug_tuple("Poll"); + debug_trait_builder.finish() + } + (&Signal::Pwr,) => { + let mut debug_trait_builder = f.debug_tuple("Pwr"); + debug_trait_builder.finish() + } + (&Signal::Sys,) => { + let mut debug_trait_builder = f.debug_tuple("Sys"); + debug_trait_builder.finish() + } + } + } +} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::hash::Hash for Signal { + fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) -> () { + match (&*self,) { + _ => ::core::hash::Hash::hash( + &unsafe { ::core::intrinsics::discriminant_value(self) }, + state, + ), + } + } +} +impl ::core::marker::StructuralEq for Signal {} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::cmp::Eq for Signal { + #[inline] + #[doc(hidden)] + fn assert_receiver_is_total_eq(&self) -> () { + {} + } +} +impl ::core::marker::StructuralPartialEq for Signal {} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::cmp::PartialEq for Signal { + #[inline] + fn eq(&self, other: &Signal) -> bool { + { + let __self_vi = unsafe { ::core::intrinsics::discriminant_value(&*self) } as u8; + let __arg_1_vi = unsafe { ::core::intrinsics::discriminant_value(&*other) } as u8; + if true && __self_vi == __arg_1_vi { + match (&*self, &*other) { + _ => true, + } + } else { + false + } + } + } +} +#[repr(transparent)] +pub struct Riflags(u16); +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::marker::Copy for Riflags {} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::clone::Clone for Riflags { + #[inline] + fn clone(&self) -> Riflags { + { + let _: ::core::clone::AssertParamIsClone; + *self + } + } +} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::fmt::Debug for Riflags { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + match *self { + Riflags(ref __self_0_0) => { + let mut debug_trait_builder = f.debug_tuple("Riflags"); + let _ = debug_trait_builder.field(&&(*__self_0_0)); + debug_trait_builder.finish() + } + } + } +} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::hash::Hash for Riflags { + fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) -> () { + match *self { + Riflags(ref __self_0_0) => ::core::hash::Hash::hash(&(*__self_0_0), state), + } + } +} +impl ::core::marker::StructuralEq for Riflags {} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::cmp::Eq for Riflags { + #[inline] + #[doc(hidden)] + fn assert_receiver_is_total_eq(&self) -> () { + { + let _: ::core::cmp::AssertParamIsEq; + } + } +} +impl ::core::marker::StructuralPartialEq for Riflags {} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::cmp::PartialEq for Riflags { + #[inline] + fn eq(&self, other: &Riflags) -> bool { + match *other { + Riflags(ref __self_1_0) => match *self { + Riflags(ref __self_0_0) => (*__self_0_0) == (*__self_1_0), + }, + } + } + #[inline] + fn ne(&self, other: &Riflags) -> bool { + match *other { + Riflags(ref __self_1_0) => match *self { + Riflags(ref __self_0_0) => (*__self_0_0) != (*__self_1_0), + }, + } + } +} +impl Riflags { + pub const RECV_PEEK: Riflags = Riflags(1); + pub const RECV_WAITALL: Riflags = Riflags(2); +} +#[repr(transparent)] +pub struct Roflags(u16); +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::marker::Copy for Roflags {} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::clone::Clone for Roflags { + #[inline] + fn clone(&self) -> Roflags { + { + let _: ::core::clone::AssertParamIsClone; + *self + } + } +} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::fmt::Debug for Roflags { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + match *self { + Roflags(ref __self_0_0) => { + let mut debug_trait_builder = f.debug_tuple("Roflags"); + let _ = debug_trait_builder.field(&&(*__self_0_0)); + debug_trait_builder.finish() + } + } + } +} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::hash::Hash for Roflags { + fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) -> () { + match *self { + Roflags(ref __self_0_0) => ::core::hash::Hash::hash(&(*__self_0_0), state), + } + } +} +impl ::core::marker::StructuralEq for Roflags {} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::cmp::Eq for Roflags { + #[inline] + #[doc(hidden)] + fn assert_receiver_is_total_eq(&self) -> () { + { + let _: ::core::cmp::AssertParamIsEq; + } + } +} +impl ::core::marker::StructuralPartialEq for Roflags {} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::cmp::PartialEq for Roflags { + #[inline] + fn eq(&self, other: &Roflags) -> bool { + match *other { + Roflags(ref __self_1_0) => match *self { + Roflags(ref __self_0_0) => (*__self_0_0) == (*__self_1_0), + }, + } + } + #[inline] + fn ne(&self, other: &Roflags) -> bool { + match *other { + Roflags(ref __self_1_0) => match *self { + Roflags(ref __self_0_0) => (*__self_0_0) != (*__self_1_0), + }, + } + } +} +impl Roflags { + pub const RECV_DATA_TRUNCATED: Roflags = Roflags(1); +} +pub type Siflags = u16; +#[repr(transparent)] +pub struct Sdflags(u8); +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::marker::Copy for Sdflags {} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::clone::Clone for Sdflags { + #[inline] + fn clone(&self) -> Sdflags { + { + let _: ::core::clone::AssertParamIsClone; + *self + } + } +} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::fmt::Debug for Sdflags { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + match *self { + Sdflags(ref __self_0_0) => { + let mut debug_trait_builder = f.debug_tuple("Sdflags"); + let _ = debug_trait_builder.field(&&(*__self_0_0)); + debug_trait_builder.finish() + } + } + } +} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::hash::Hash for Sdflags { + fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) -> () { + match *self { + Sdflags(ref __self_0_0) => ::core::hash::Hash::hash(&(*__self_0_0), state), + } + } +} +impl ::core::marker::StructuralEq for Sdflags {} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::cmp::Eq for Sdflags { + #[inline] + #[doc(hidden)] + fn assert_receiver_is_total_eq(&self) -> () { + { + let _: ::core::cmp::AssertParamIsEq; + } + } +} +impl ::core::marker::StructuralPartialEq for Sdflags {} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::cmp::PartialEq for Sdflags { + #[inline] + fn eq(&self, other: &Sdflags) -> bool { + match *other { + Sdflags(ref __self_1_0) => match *self { + Sdflags(ref __self_0_0) => (*__self_0_0) == (*__self_1_0), + }, + } + } + #[inline] + fn ne(&self, other: &Sdflags) -> bool { + match *other { + Sdflags(ref __self_1_0) => match *self { + Sdflags(ref __self_0_0) => (*__self_0_0) != (*__self_1_0), + }, + } + } +} +impl Sdflags { + pub const RD: Sdflags = Sdflags(1); + pub const WR: Sdflags = Sdflags(2); +} +#[repr(u8)] +pub enum Preopentype { + Dir, +} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::marker::Copy for Preopentype {} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::clone::Clone for Preopentype { + #[inline] + fn clone(&self) -> Preopentype { + { + *self + } + } +} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::fmt::Debug for Preopentype { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + match (&*self,) { + (&Preopentype::Dir,) => { + let mut debug_trait_builder = f.debug_tuple("Dir"); + debug_trait_builder.finish() + } + } + } +} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::hash::Hash for Preopentype { + fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) -> () { + match (&*self,) { + _ => {} + } + } +} +impl ::core::marker::StructuralEq for Preopentype {} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::cmp::Eq for Preopentype { + #[inline] + #[doc(hidden)] + fn assert_receiver_is_total_eq(&self) -> () { + {} + } +} +impl ::core::marker::StructuralPartialEq for Preopentype {} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::cmp::PartialEq for Preopentype { + #[inline] + fn eq(&self, other: &Preopentype) -> bool { + match (&*self, &*other) { + _ => true, + } + } +} From 9952f18b13c0675cca634f1c83f874faff84e422 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Fri, 17 Jan 2020 19:41:17 +0100 Subject: [PATCH 05/86] Add short README --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000000..5b6db02f81 --- /dev/null +++ b/README.md @@ -0,0 +1,6 @@ +# wiggle + +An experimental implementation of `bytecodealliance/wig` crate which +generates Rust bindings from `*.witx` that are meant to be more idiomatic +and hopefully allowing for easier polyfilling between different WASI +snapshot versions in the future. From 065a60831a6ce87efe25aafb932144fd4b4facce Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Fri, 17 Jan 2020 19:46:21 +0100 Subject: [PATCH 06/86] Add LICENSE matching that of wig --- LICENSE | 221 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 221 insertions(+) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000000..c46ee5f199 --- /dev/null +++ b/LICENSE @@ -0,0 +1,221 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + +--- LLVM Exceptions to the Apache 2.0 License ---- + +As an exception, if, as a result of your compiling your source code, portions +of this Software are embedded into an Object form of such source code, you +may redistribute such embedded portions in such Object form without complying +with the conditions of Sections 4(a), 4(b) and 4(d) of the License. + +In addition, if you combine or link compiled forms of this Software with +software that is licensed under the GPLv2 ("Combined Software") and if a +court of competent jurisdiction determines that the patent provision (Section +3), the indemnity provision (Section 9) or other Section of the License +conflicts with the conditions of the GPLv2, you may retroactively and +prospectively choose to deem waived or otherwise exclude such Section(s) of +the License, but only in their entirety and only with respect to the Combined +Software. + + From 0d47556cf739082749959aa8f76bb61315000825 Mon Sep 17 00:00:00 2001 From: Pat Hickey Date: Fri, 17 Jan 2020 15:41:19 -0800 Subject: [PATCH 07/86] import memory sub-crate --- .gitignore | 1 + Cargo.toml | 1 + crates/memory/.gitignore | 2 + crates/memory/Cargo.toml | 10 +++ crates/memory/src/lib.rs | 188 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 202 insertions(+) create mode 100644 crates/memory/.gitignore create mode 100644 crates/memory/Cargo.toml create mode 100644 crates/memory/src/lib.rs diff --git a/.gitignore b/.gitignore index 53eaa21960..693699042b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ /target **/*.rs.bk +Cargo.lock diff --git a/Cargo.toml b/Cargo.toml index 22ce267e3e..4089caf073 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,6 +6,7 @@ edition = "2018" [dependencies] generate = { path = "crates/generate" } +memory = { path = "crates/memory" } [workspace] members = ["crates/generate"] diff --git a/crates/memory/.gitignore b/crates/memory/.gitignore new file mode 100644 index 0000000000..a9d37c560c --- /dev/null +++ b/crates/memory/.gitignore @@ -0,0 +1,2 @@ +target +Cargo.lock diff --git a/crates/memory/Cargo.toml b/crates/memory/Cargo.toml new file mode 100644 index 0000000000..f605782c99 --- /dev/null +++ b/crates/memory/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "memory" +version = "0.1.0" +authors = ["Pat Hickey "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +thiserror = "1" diff --git a/crates/memory/src/lib.rs b/crates/memory/src/lib.rs new file mode 100644 index 0000000000..aa768b498c --- /dev/null +++ b/crates/memory/src/lib.rs @@ -0,0 +1,188 @@ +#![allow(dead_code, unused)] // DURING DEVELOPMENT + +use std::cell::RefCell; +use std::marker::PhantomData; +use std::rc::Rc; +use thiserror::Error; + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub struct Region { + start: u32, + len: u32, +} + +impl Region { + fn overlaps(&self, rhs: Region) -> bool { + let self_start = self.start as u64; + let self_end = self.start as u64 + self.len as u64; + + let rhs_start = rhs.start as u64; + let rhs_end = rhs.start as u64 + rhs.len as u64; + + // start of rhs inside self: + if (rhs_start >= self_start && rhs_start < self_end) { + return true; + } + + // end of rhs inside self: + if (rhs_end >= self_start && rhs_end < self_end) { + return true; + } + + // start of self inside rhs: + if (self_start >= rhs_start && self_start < rhs_end) { + return true; + } + + // end of self inside rhs: XXX is this redundant? i suspect it is but im too tired + if (self_end >= rhs_start && self_end < rhs_end) { + return true; + } + + false + } +} + +struct GuestBorrows { + immutable: Vec, + mutable: Vec, +} + +impl GuestBorrows { + pub fn new() -> Self { + GuestBorrows { + immutable: Vec::new(), + mutable: Vec::new(), + } + } + + fn is_borrowed_immut(&self, r: Region) -> bool { + !self.immutable.iter().all(|b| !b.overlaps(r)) + } + + fn is_borrowed_mut(&self, r: Region) -> bool { + !self.mutable.iter().all(|b| !b.overlaps(r)) + } + + pub fn borrow_immut(&mut self, r: Region) -> bool { + if self.is_borrowed_mut(r) { + return false; + } + self.immutable.push(r); + true + } + + pub fn unborrow_immut(&mut self, r: Region) { + let (ix, _) = self + .immutable + .iter() + .enumerate() + .find(|(_, reg)| r == **reg) + .expect("region exists in borrows"); + self.immutable.remove(ix); + } + + pub fn borrow_mut(&mut self, r: Region) -> bool { + if self.is_borrowed_immut(r) || self.is_borrowed_mut(r) { + return false; + } + self.mutable.push(r); + true + } + + pub fn unborrow_mut(&mut self, r: Region) { + let (ix, _) = self + .mutable + .iter() + .enumerate() + .find(|(_, reg)| r == **reg) + .expect("region exists in borrows"); + self.mutable.remove(ix); + } +} + +pub struct GuestMemory<'a> { + ptr: *mut u8, + len: u32, + lifetime: PhantomData<&'a ()>, + borrows: Rc>, +} + +impl<'a> GuestMemory<'a> { + pub fn new(ptr: *mut u8, len: u32) -> GuestMemory<'a> { + GuestMemory { + ptr, + len, + lifetime: PhantomData, + borrows: Rc::new(RefCell::new(GuestBorrows::new())), + } + } + + fn contains(&self, r: Region) -> bool { + r.start < self.len + && r.len < self.len // make sure next clause doesnt underflow + && r.start < (self.len - r.len) + } + + pub fn ptr(&'a self, r: Region) -> Result>, MemoryError> { + let mut borrows = self.borrows.borrow_mut(); + if !self.contains(r) { + Err(MemoryError::OutOfBounds)?; + } + if borrows.borrow_immut(r) { + Ok(Some(GuestPtr { + mem: &self, + region: r, + })) + } else { + Ok(None) + } + } + + pub fn ptr_mut(&'a self, r: Region) -> Result>, MemoryError> { + let mut borrows = self.borrows.borrow_mut(); + if !self.contains(r) { + Err(MemoryError::OutOfBounds)?; + } + if borrows.borrow_immut(r) { + Ok(Some(GuestPtrMut { + mem: &self, + region: r, + })) + } else { + Ok(None) + } + } + +} + +pub struct GuestPtr<'a> { + mem: &'a GuestMemory<'a>, + region: Region, +} + +impl<'a> Drop for GuestPtr<'a> { + fn drop(&mut self) { + let mut borrows = self.mem.borrows.borrow_mut(); + borrows.unborrow_immut(self.region); + } +} + + +pub struct GuestPtrMut<'a> { + mem: &'a GuestMemory<'a>, + region: Region, +} + +impl<'a> Drop for GuestPtrMut<'a> { + fn drop(&mut self) { + let mut borrows = self.mem.borrows.borrow_mut(); + borrows.unborrow_mut(self.region); + } +} + +#[derive(Debug, Error)] +pub enum MemoryError { + #[error("Out of bounds")] + OutOfBounds, +} From 4c7b3e8685887dce827ca16dacb2a7d68dd1b5bb Mon Sep 17 00:00:00 2001 From: Pat Hickey Date: Fri, 17 Jan 2020 15:48:51 -0800 Subject: [PATCH 08/86] import some other helpers from pat's crate * parse the proc macro argument into a file path * use a much simpler witx spec by default --- crates/generate/.gitignore | 1 + crates/generate/Cargo.toml | 1 + crates/generate/src/imp.rs | 11 +---------- crates/generate/src/lib.rs | 10 ++++++++-- crates/generate/src/parse.rs | 33 +++++++++++++++++++++++++++++++++ src/lib.rs | 10 ++++++++-- test.witx | 13 +++++++++++++ 7 files changed, 65 insertions(+), 14 deletions(-) create mode 100644 crates/generate/.gitignore create mode 100644 crates/generate/src/parse.rs create mode 100644 test.witx diff --git a/crates/generate/.gitignore b/crates/generate/.gitignore new file mode 100644 index 0000000000..eb5a316cbd --- /dev/null +++ b/crates/generate/.gitignore @@ -0,0 +1 @@ +target diff --git a/crates/generate/Cargo.toml b/crates/generate/Cargo.toml index 2403f089a8..cd720d549c 100644 --- a/crates/generate/Cargo.toml +++ b/crates/generate/Cargo.toml @@ -12,3 +12,4 @@ witx = { path = "../WASI/tools/witx" } quote = "1.0" proc-macro2 = "1.0" heck = "0.3" +anyhow = "1" diff --git a/crates/generate/src/imp.rs b/crates/generate/src/imp.rs index 24f9ee6c2f..bd980c139b 100644 --- a/crates/generate/src/imp.rs +++ b/crates/generate/src/imp.rs @@ -3,8 +3,6 @@ use proc_macro2::{Delimiter, Group, Literal, TokenStream, TokenTree}; use quote::{format_ident, quote}; use std::convert::TryFrom; -const WITX_PATH: &'static str = "crates/WASI/phases/snapshot/witx/wasi_snapshot_preview1.witx"; - #[derive(Copy, Clone, Debug, Eq, PartialEq)] pub enum Mode { Host, @@ -21,15 +19,8 @@ impl Mode { } } -pub fn gen() -> TokenStream { +pub fn gen(doc: witx::Document) -> TokenStream { let mut output = TokenStream::new(); - let doc = match witx::load(&[&WITX_PATH]) { - Ok(doc) => doc, - Err(e) => { - panic!("error opening file {}: {}", WITX_PATH, e); - } - }; - gen_datatypes(&mut output, &doc, Mode::Wasi); // gen_datatypes(&mut output, &doc, Mode::Wasi32); // gen_datatypes(&mut output, &doc, Mode::Host); diff --git a/crates/generate/src/lib.rs b/crates/generate/src/lib.rs index 8e214f3345..f3b9a93f5f 100644 --- a/crates/generate/src/lib.rs +++ b/crates/generate/src/lib.rs @@ -1,10 +1,16 @@ extern crate proc_macro; mod imp; +mod parse; use proc_macro::TokenStream; +use proc_macro2::TokenStream as TokenStream2; #[proc_macro] -pub fn from_witx(_args: TokenStream) -> TokenStream { - TokenStream::from(imp::gen()) +pub fn from_witx(args: TokenStream) -> TokenStream { + let args = TokenStream2::from(args); + let witx_paths = parse::witx_paths(args).expect("parsing macro arguments"); + let doc = witx::load(&witx_paths).expect("loading witx"); + let out = imp::gen(doc); + TokenStream::from(out) } diff --git a/crates/generate/src/parse.rs b/crates/generate/src/parse.rs new file mode 100644 index 0000000000..65c8a190cc --- /dev/null +++ b/crates/generate/src/parse.rs @@ -0,0 +1,33 @@ +use anyhow::{bail, Result}; +use proc_macro2::{Literal, TokenStream, TokenTree}; + +pub fn witx_paths(args: TokenStream) -> Result> { + let arg_strings = args + .into_iter() + .map(|arg| match arg { + TokenTree::Literal(lit) => string_literal(lit), + _ => bail!("expected string literal, got: {:?}", arg), + }) + .collect::>>()?; + + if arg_strings.is_empty() { + bail!("expected at least one argument"); + } + Ok(arg_strings) +} + +fn string_literal(literal: Literal) -> Result { + let s = literal.to_string(); + if !s.starts_with('"') || !s.ends_with('"') { + bail!("string literal must be enclosed in double quotes"); + } + + let trimmed = s[1..s.len() - 1].to_owned(); + if trimmed.contains('"') { + bail!("string literal must not contain quotes"); + } + if trimmed.contains('\\') { + bail!("string literal must not contain backslashes"); + } + Ok(trimmed) +} diff --git a/src/lib.rs b/src/lib.rs index 0136867e5c..4c58828ac7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,2 +1,8 @@ -generate::from_witx!(); - +pub mod test { + generate::from_witx!("test.witx"); +} +/* +pub mod wasi { + generate::from_witx!("crates/WASI/phases/snapshot/witx/wasi_snapshot_preview1.witx"); +} +*/ diff --git a/test.witx b/test.witx new file mode 100644 index 0000000000..f1185b63fe --- /dev/null +++ b/test.witx @@ -0,0 +1,13 @@ + + +(typename $errno + (enum u32 + $dont_want_to + $physically_unable + $picket_line)) + +(module $foo + (@interface func (export "bar") + (param $name string) + (result $error $errno)) +) From cd686915aa74e291b331d64db0998417c96d97d6 Mon Sep 17 00:00:00 2001 From: Pat Hickey Date: Sun, 19 Jan 2020 18:32:16 -0800 Subject: [PATCH 09/86] clear out a bunch of old code, reorganize --- crates/generate/Cargo.toml | 1 + crates/generate/src/imp.rs | 266 -------------------------------- crates/generate/src/lib.rs | 26 +++- crates/generate/src/types.rs | 82 ++++++++++ crates/memory/src/borrow.rs | 59 +++++++ crates/memory/src/guest_type.rs | 75 +++++++++ crates/memory/src/lib.rs | 194 +---------------------- crates/memory/src/memory.rs | 117 ++++++++++++++ crates/memory/src/region.rs | 37 +++++ test.witx | 3 +- 10 files changed, 403 insertions(+), 457 deletions(-) delete mode 100644 crates/generate/src/imp.rs create mode 100644 crates/generate/src/types.rs create mode 100644 crates/memory/src/borrow.rs create mode 100644 crates/memory/src/guest_type.rs create mode 100644 crates/memory/src/memory.rs create mode 100644 crates/memory/src/region.rs diff --git a/crates/generate/Cargo.toml b/crates/generate/Cargo.toml index cd720d549c..2b89101bd3 100644 --- a/crates/generate/Cargo.toml +++ b/crates/generate/Cargo.toml @@ -8,6 +8,7 @@ edition = "2018" proc-macro = true [dependencies] +memory = { path = "../memory" } witx = { path = "../WASI/tools/witx" } quote = "1.0" proc-macro2 = "1.0" diff --git a/crates/generate/src/imp.rs b/crates/generate/src/imp.rs deleted file mode 100644 index bd980c139b..0000000000 --- a/crates/generate/src/imp.rs +++ /dev/null @@ -1,266 +0,0 @@ -use heck::{CamelCase, MixedCase, ShoutySnakeCase}; -use proc_macro2::{Delimiter, Group, Literal, TokenStream, TokenTree}; -use quote::{format_ident, quote}; -use std::convert::TryFrom; - -#[derive(Copy, Clone, Debug, Eq, PartialEq)] -pub enum Mode { - Host, - Wasi32, - Wasi, -} - -impl Mode { - pub fn include_target_types(&self) -> bool { - match self { - Mode::Host | Mode::Wasi32 => true, - Mode::Wasi => false, - } - } -} - -pub fn gen(doc: witx::Document) -> TokenStream { - let mut output = TokenStream::new(); - gen_datatypes(&mut output, &doc, Mode::Wasi); - // gen_datatypes(&mut output, &doc, Mode::Wasi32); - // gen_datatypes(&mut output, &doc, Mode::Host); - - output -} - -fn gen_datatypes(output: &mut TokenStream, doc: &witx::Document, mode: Mode) { - for namedtype in doc.typenames() { - if mode.include_target_types() != namedtype_has_target_size(&namedtype) { - continue; - } - - gen_datatype(output, mode, &namedtype); - } -} - -fn gen_datatype(output: &mut TokenStream, mode: Mode, namedtype: &witx::NamedType) { - let wasi_name = format_ident!("{}", namedtype.name.as_str().to_camel_case()); - match &namedtype.tref { - witx::TypeRef::Name(alias_to) => { - let to = tref_tokens(mode, &alias_to.tref); - output.extend(quote!(pub type #wasi_name = #to;)); - } - witx::TypeRef::Value(v) => match &**v { - witx::Type::Enum(e) => { - let repr = int_repr_tokens(e.repr); - output.extend(quote!(#[repr(#repr)])); - output - .extend(quote!(#[derive(Copy, Clone, Debug, std::hash::Hash, Eq, PartialEq)])); - - let mut inner = TokenStream::new(); - for variant in &e.variants { - let value_name = if namedtype.name.as_str() == "errno" { - // FIXME discussion point! - format_ident!("E{}", variant.name.as_str().to_mixed_case()) - } else { - format_ident!("{}", variant.name.as_str().to_camel_case()) - }; - inner.extend(quote!(#value_name,)); - } - - output.extend(quote!(pub enum #wasi_name { - #inner - })); - } - witx::Type::Int(_) => {} // TODO - witx::Type::Flags(f) => { - let repr = int_repr_tokens(f.repr); - output.extend(quote!(#[repr(transparent)])); - output - .extend(quote!(#[derive(Copy, Clone, Debug, std::hash::Hash, Eq, PartialEq)])); - output.extend(quote!(pub struct #wasi_name(#repr);)); - // TODO - // Since `Flags` are represented by a "transparent" struct, we should probably - // auto-generate `from_raw(raw: #repr)` method or similar - - let mut inner = TokenStream::new(); - for (index, flag) in f.flags.iter().enumerate() { - let value_name = format_ident!("{}", flag.name.as_str().to_shouty_snake_case()); - let flag_value = Literal::u128_unsuffixed( - 1u128 - .checked_shl(u32::try_from(index).expect("flag value overflow")) - .expect("flag value overflow"), - ); - inner.extend( - quote!(pub const #value_name: #wasi_name = #wasi_name(#flag_value);), - ); - } - - output.extend(quote!(impl #wasi_name { - #inner - })); - } - witx::Type::Struct(s) => { - output.extend(quote!(#[repr(C)])); - // Types which contain unions can't trivially implement Debug, - // Hash, or Eq, because the type itself doesn't record which - // union member is active. - if struct_has_union(&s) { - output.extend(quote!(#[derive(Copy, Clone)])); - output.extend(quote!(#[allow(missing_debug_implementations)])); - } else { - output.extend(quote!(#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)])); - } - - output.extend(quote!(pub struct #wasi_name)); - - let mut inner = TokenStream::new(); - for member in &s.members { - let member_name = format_ident!("r#{}", member.name.as_str()); - let member_type = tref_tokens(mode, &member.tref); - inner.extend(quote!(pub #member_name: #member_type,)); - } - let braced = Group::new(Delimiter::Brace, inner); - output.extend(TokenStream::from(TokenTree::Group(braced))); - } - witx::Type::Union(u) => { - output.extend(quote!(#[repr(C)])); - output.extend(quote!(#[derive(Copy, Clone)])); - output.extend(quote!(#[allow(missing_debug_implementations)])); - - output.extend(quote!(pub union #wasi_name)); - - let mut inner = TokenStream::new(); - for variant in &u.variants { - let variant_name = format_ident!("r#{}", variant.name.as_str()); - let variant_type = tref_tokens(mode, &variant.tref); - inner.extend(quote!(pub #variant_name: #variant_type,)); - } - let braced = Group::new(Delimiter::Brace, inner); - output.extend(TokenStream::from(TokenTree::Group(braced))); - } - witx::Type::Handle(_h) => { - output.extend(quote!(pub type #wasi_name = u32;)); - } - witx::Type::Builtin(b) => { - if namedtype.name.as_str() == "size" { - match mode { - Mode::Host => output.extend(quote!(pub type #wasi_name = usize;)), - Mode::Wasi => panic!("size has target-specific size"), - Mode::Wasi32 => output.extend(quote!(pub type #wasi_name = u32;)), - } - } else { - let b_type = builtin_tokens(mode, *b); - output.extend(quote!(pub type #wasi_name = #b_type;)); - } - } - witx::Type::Pointer { .. } - | witx::Type::ConstPointer { .. } - | witx::Type::Array { .. } => { - let tref_tokens = tref_tokens(mode, &namedtype.tref); - output.extend(quote!(pub type #wasi_name = #tref_tokens;)); - } - }, - } -} - -fn int_repr_tokens(int_repr: witx::IntRepr) -> TokenStream { - match int_repr { - witx::IntRepr::U8 => quote!(u8), - witx::IntRepr::U16 => quote!(u16), - witx::IntRepr::U32 => quote!(u32), - witx::IntRepr::U64 => quote!(u64), - } -} - -fn builtin_tokens(mode: Mode, builtin: witx::BuiltinType) -> TokenStream { - match builtin { - witx::BuiltinType::String => match mode { - Mode::Host => quote!((*const u8, usize)), - Mode::Wasi => panic!("strings have target-specific size"), - Mode::Wasi32 => quote!((u32, u32)), - }, - witx::BuiltinType::U8 => quote!(u8), - witx::BuiltinType::U16 => quote!(u16), - witx::BuiltinType::U32 => quote!(u32), - witx::BuiltinType::U64 => quote!(u64), - witx::BuiltinType::S8 => quote!(i8), - witx::BuiltinType::S16 => quote!(i16), - witx::BuiltinType::S32 => quote!(i32), - witx::BuiltinType::S64 => quote!(i64), - witx::BuiltinType::F32 => quote!(f32), - witx::BuiltinType::F64 => quote!(f64), - witx::BuiltinType::Char8 => quote!(char), - witx::BuiltinType::USize => quote!(usize), - } -} - -fn tref_tokens(mode: Mode, tref: &witx::TypeRef) -> TokenStream { - match tref { - witx::TypeRef::Name(n) => TokenStream::from(TokenTree::Ident(format_ident!( - "{}", - n.name.as_str().to_camel_case() - ))), - witx::TypeRef::Value(v) => match &**v { - witx::Type::Builtin(b) => builtin_tokens(mode, *b), - witx::Type::Pointer(pointee) => { - let pointee = tref_tokens(mode, pointee); - match mode { - Mode::Host => quote!(*mut #pointee), - Mode::Wasi => panic!("pointers have target-specific size"), - Mode::Wasi32 => quote!(u32), - } - } - witx::Type::ConstPointer(pointee) => { - let pointee = tref_tokens(mode, pointee); - match mode { - Mode::Host => quote!(*const #pointee), - Mode::Wasi => panic!("pointers have target-specific size"), - Mode::Wasi32 => quote!(u32), - } - } - witx::Type::Array(element) => { - let element_name = tref_tokens(mode, element); - match mode { - Mode::Host => quote!((*const #element_name, usize)), - Mode::Wasi => panic!("arrays have target-specific size"), - Mode::Wasi32 => quote!((u32, u32)), - } - } - t => panic!("cannot give name to anonymous type {:?}", t), - }, - } -} - -/// Test whether the given struct contains any union members. -fn struct_has_union(s: &witx::StructDatatype) -> bool { - s.members.iter().any(|member| match &*member.tref.type_() { - witx::Type::Union { .. } => true, - witx::Type::Struct(s) => struct_has_union(&s), - _ => false, - }) -} - -/// Test whether the type referred to has a target-specific size. -fn tref_has_target_size(tref: &witx::TypeRef) -> bool { - match tref { - witx::TypeRef::Name(nt) => namedtype_has_target_size(&nt), - witx::TypeRef::Value(t) => type_has_target_size(&t), - } -} - -/// Test whether the given named type has a target-specific size. -fn namedtype_has_target_size(nt: &witx::NamedType) -> bool { - if nt.name.as_str() == "size" { - true - } else { - tref_has_target_size(&nt.tref) - } -} - -/// Test whether the given type has a target-specific size. -fn type_has_target_size(ty: &witx::Type) -> bool { - match ty { - witx::Type::Builtin(witx::BuiltinType::String) => true, - witx::Type::Pointer { .. } | witx::Type::ConstPointer { .. } => true, - witx::Type::Array(elem) => tref_has_target_size(elem), - witx::Type::Struct(s) => s.members.iter().any(|m| tref_has_target_size(&m.tref)), - witx::Type::Union(u) => u.variants.iter().any(|v| tref_has_target_size(&v.tref)), - _ => false, - } -} diff --git a/crates/generate/src/lib.rs b/crates/generate/src/lib.rs index f3b9a93f5f..f78a484153 100644 --- a/crates/generate/src/lib.rs +++ b/crates/generate/src/lib.rs @@ -1,16 +1,36 @@ extern crate proc_macro; -mod imp; mod parse; +mod types; +use heck::SnakeCase; use proc_macro::TokenStream; use proc_macro2::TokenStream as TokenStream2; +use quote::{format_ident, quote}; +use types::define_datatype; #[proc_macro] pub fn from_witx(args: TokenStream) -> TokenStream { let args = TokenStream2::from(args); let witx_paths = parse::witx_paths(args).expect("parsing macro arguments"); let doc = witx::load(&witx_paths).expect("loading witx"); - let out = imp::gen(doc); - TokenStream::from(out) + + let mut types = TokenStream2::new(); + for namedtype in doc.typenames() { + let def = define_datatype(&namedtype); + types.extend(def); + } + + let mut modules = TokenStream2::new(); + for module in doc.modules() { + let modname = format_ident!("{}", module.name.as_str().to_snake_case()); + let mut fs = TokenStream2::new(); + for func in module.funcs() { + let ident = format_ident!("{}", func.name.as_str().to_snake_case()); + fs.extend(quote!(pub fn #ident() { unimplemented!() })); + } + modules.extend(quote!(mod #modname { use super::types::*; #fs })); + } + + TokenStream::from(quote!(mod types { #types } #modules)) } diff --git a/crates/generate/src/types.rs b/crates/generate/src/types.rs new file mode 100644 index 0000000000..f9c98b9234 --- /dev/null +++ b/crates/generate/src/types.rs @@ -0,0 +1,82 @@ +use heck::{CamelCase, MixedCase}; +use proc_macro2::TokenStream; +use quote::{format_ident, quote}; + +pub fn define_datatype(namedtype: &witx::NamedType) -> TokenStream { + match &namedtype.tref { + witx::TypeRef::Name(alias_to) => define_alias(&namedtype.name, &alias_to), + witx::TypeRef::Value(v) => match &**v { + witx::Type::Enum(e) => define_enum(&namedtype.name, &e), + witx::Type::Int(_) => unimplemented!("int types"), + witx::Type::Flags(_) => unimplemented!("flag types"), + witx::Type::Struct(_) => unimplemented!("struct types"), + witx::Type::Union(_) => unimplemented!("union types"), + witx::Type::Handle(_h) => unimplemented!("handle types"), + witx::Type::Builtin(b) => define_builtin(&namedtype.name, &b), + witx::Type::Pointer { .. } => unimplemented!("pointer types"), + witx::Type::ConstPointer { .. } => unimplemented!("constpointer types"), + witx::Type::Array { .. } => unimplemented!("array types"), + }, + } +} + +fn define_alias(name: &witx::Id, to: &witx::NamedType) -> TokenStream { + let ident = format_ident!("{}", name.as_str().to_camel_case()); + let to = format_ident!("{}", to.name.as_str().to_camel_case()); + + quote!(pub type #ident = #to;) +} + +fn define_enum(name: &witx::Id, e: &witx::EnumDatatype) -> TokenStream { + let ident = format_ident!("{}", name.as_str().to_camel_case()); + let mut output = TokenStream::new(); + let repr = int_repr_tokens(e.repr); + output.extend(quote!(#[repr(#repr)])); + output.extend(quote!(#[derive(Copy, Clone, Debug, std::hash::Hash, Eq, PartialEq)])); + + let mut inner = TokenStream::new(); + for variant in &e.variants { + let value_name = if name.as_str() == "errno" { + // FIXME discussion point! + format_ident!("E{}", variant.name.as_str().to_mixed_case()) + } else { + format_ident!("{}", variant.name.as_str().to_camel_case()) + }; + inner.extend(quote!(#value_name,)); + } + + output.extend(quote!(pub enum #ident { + #inner + })); + + output +} + +fn define_builtin(name: &witx::Id, builtin: &witx::BuiltinType) -> TokenStream { + let ident = format_ident!("{}", name.as_str().to_camel_case()); + let prim = match builtin { + witx::BuiltinType::String => quote!(String), + witx::BuiltinType::U8 => quote!(u8), + witx::BuiltinType::U16 => quote!(u16), + witx::BuiltinType::U32 => quote!(u32), + witx::BuiltinType::U64 => quote!(u64), + witx::BuiltinType::S8 => quote!(i8), + witx::BuiltinType::S16 => quote!(i16), + witx::BuiltinType::S32 => quote!(i32), + witx::BuiltinType::S64 => quote!(i64), + witx::BuiltinType::F32 => quote!(f32), + witx::BuiltinType::F64 => quote!(f64), + witx::BuiltinType::Char8 => quote!(char), + witx::BuiltinType::USize => quote!(usize), + }; + quote!(pub type #ident = #prim;) +} + +fn int_repr_tokens(int_repr: witx::IntRepr) -> TokenStream { + match int_repr { + witx::IntRepr::U8 => quote!(u8), + witx::IntRepr::U16 => quote!(u16), + witx::IntRepr::U32 => quote!(u32), + witx::IntRepr::U64 => quote!(u64), + } +} diff --git a/crates/memory/src/borrow.rs b/crates/memory/src/borrow.rs new file mode 100644 index 0000000000..50d5656f53 --- /dev/null +++ b/crates/memory/src/borrow.rs @@ -0,0 +1,59 @@ +use crate::region::Region; + +pub struct GuestBorrows { + immutable: Vec, + mutable: Vec, +} + +impl GuestBorrows { + pub fn new() -> Self { + GuestBorrows { + immutable: Vec::new(), + mutable: Vec::new(), + } + } + + fn is_borrowed_immut(&self, r: Region) -> bool { + !self.immutable.iter().all(|b| !b.overlaps(r)) + } + + fn is_borrowed_mut(&self, r: Region) -> bool { + !self.mutable.iter().all(|b| !b.overlaps(r)) + } + + pub fn borrow_immut(&mut self, r: Region) -> bool { + if self.is_borrowed_mut(r) { + return false; + } + self.immutable.push(r); + true + } + + pub fn unborrow_immut(&mut self, r: Region) { + let (ix, _) = self + .immutable + .iter() + .enumerate() + .find(|(_, reg)| r == **reg) + .expect("region exists in borrows"); + self.immutable.remove(ix); + } + + pub fn borrow_mut(&mut self, r: Region) -> bool { + if self.is_borrowed_immut(r) || self.is_borrowed_mut(r) { + return false; + } + self.mutable.push(r); + true + } + + pub fn unborrow_mut(&mut self, r: Region) { + let (ix, _) = self + .mutable + .iter() + .enumerate() + .find(|(_, reg)| r == **reg) + .expect("region exists in borrows"); + self.mutable.remove(ix); + } +} diff --git a/crates/memory/src/guest_type.rs b/crates/memory/src/guest_type.rs new file mode 100644 index 0000000000..c34aa0fce4 --- /dev/null +++ b/crates/memory/src/guest_type.rs @@ -0,0 +1,75 @@ +pub trait GuestType { + fn len() -> u32; +} + +impl GuestType for u8 { + fn len() -> u32 { + 1 + } +} + +impl GuestType for i8 { + fn len() -> u32 { + 1 + } +} + +impl GuestType for u16 { + fn len() -> u32 { + 2 + } +} + +impl GuestType for i16 { + fn len() -> u32 { + 2 + } +} + +impl GuestType for u32 { + fn len() -> u32 { + 4 + } +} + +impl GuestType for i32 { + fn len() -> u32 { + 4 + } +} + +impl GuestType for f32 { + fn len() -> u32 { + 4 + } +} + +impl GuestType for u64 { + fn len() -> u32 { + 8 + } +} + +impl GuestType for i64 { + fn len() -> u32 { + 8 + } +} + +impl GuestType for f64 { + fn len() -> u32 { + 8 + } +} + +impl GuestType for char { + fn len() -> u32 { + 1 + } +} + +impl GuestType for usize { + fn len() -> u32 { + 4 + } +} diff --git a/crates/memory/src/lib.rs b/crates/memory/src/lib.rs index aa768b498c..b8ade8806c 100644 --- a/crates/memory/src/lib.rs +++ b/crates/memory/src/lib.rs @@ -1,188 +1,8 @@ -#![allow(dead_code, unused)] // DURING DEVELOPMENT +mod borrow; +mod guest_type; +mod memory; +mod region; -use std::cell::RefCell; -use std::marker::PhantomData; -use std::rc::Rc; -use thiserror::Error; - -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub struct Region { - start: u32, - len: u32, -} - -impl Region { - fn overlaps(&self, rhs: Region) -> bool { - let self_start = self.start as u64; - let self_end = self.start as u64 + self.len as u64; - - let rhs_start = rhs.start as u64; - let rhs_end = rhs.start as u64 + rhs.len as u64; - - // start of rhs inside self: - if (rhs_start >= self_start && rhs_start < self_end) { - return true; - } - - // end of rhs inside self: - if (rhs_end >= self_start && rhs_end < self_end) { - return true; - } - - // start of self inside rhs: - if (self_start >= rhs_start && self_start < rhs_end) { - return true; - } - - // end of self inside rhs: XXX is this redundant? i suspect it is but im too tired - if (self_end >= rhs_start && self_end < rhs_end) { - return true; - } - - false - } -} - -struct GuestBorrows { - immutable: Vec, - mutable: Vec, -} - -impl GuestBorrows { - pub fn new() -> Self { - GuestBorrows { - immutable: Vec::new(), - mutable: Vec::new(), - } - } - - fn is_borrowed_immut(&self, r: Region) -> bool { - !self.immutable.iter().all(|b| !b.overlaps(r)) - } - - fn is_borrowed_mut(&self, r: Region) -> bool { - !self.mutable.iter().all(|b| !b.overlaps(r)) - } - - pub fn borrow_immut(&mut self, r: Region) -> bool { - if self.is_borrowed_mut(r) { - return false; - } - self.immutable.push(r); - true - } - - pub fn unborrow_immut(&mut self, r: Region) { - let (ix, _) = self - .immutable - .iter() - .enumerate() - .find(|(_, reg)| r == **reg) - .expect("region exists in borrows"); - self.immutable.remove(ix); - } - - pub fn borrow_mut(&mut self, r: Region) -> bool { - if self.is_borrowed_immut(r) || self.is_borrowed_mut(r) { - return false; - } - self.mutable.push(r); - true - } - - pub fn unborrow_mut(&mut self, r: Region) { - let (ix, _) = self - .mutable - .iter() - .enumerate() - .find(|(_, reg)| r == **reg) - .expect("region exists in borrows"); - self.mutable.remove(ix); - } -} - -pub struct GuestMemory<'a> { - ptr: *mut u8, - len: u32, - lifetime: PhantomData<&'a ()>, - borrows: Rc>, -} - -impl<'a> GuestMemory<'a> { - pub fn new(ptr: *mut u8, len: u32) -> GuestMemory<'a> { - GuestMemory { - ptr, - len, - lifetime: PhantomData, - borrows: Rc::new(RefCell::new(GuestBorrows::new())), - } - } - - fn contains(&self, r: Region) -> bool { - r.start < self.len - && r.len < self.len // make sure next clause doesnt underflow - && r.start < (self.len - r.len) - } - - pub fn ptr(&'a self, r: Region) -> Result>, MemoryError> { - let mut borrows = self.borrows.borrow_mut(); - if !self.contains(r) { - Err(MemoryError::OutOfBounds)?; - } - if borrows.borrow_immut(r) { - Ok(Some(GuestPtr { - mem: &self, - region: r, - })) - } else { - Ok(None) - } - } - - pub fn ptr_mut(&'a self, r: Region) -> Result>, MemoryError> { - let mut borrows = self.borrows.borrow_mut(); - if !self.contains(r) { - Err(MemoryError::OutOfBounds)?; - } - if borrows.borrow_immut(r) { - Ok(Some(GuestPtrMut { - mem: &self, - region: r, - })) - } else { - Ok(None) - } - } - -} - -pub struct GuestPtr<'a> { - mem: &'a GuestMemory<'a>, - region: Region, -} - -impl<'a> Drop for GuestPtr<'a> { - fn drop(&mut self) { - let mut borrows = self.mem.borrows.borrow_mut(); - borrows.unborrow_immut(self.region); - } -} - - -pub struct GuestPtrMut<'a> { - mem: &'a GuestMemory<'a>, - region: Region, -} - -impl<'a> Drop for GuestPtrMut<'a> { - fn drop(&mut self) { - let mut borrows = self.mem.borrows.borrow_mut(); - borrows.unborrow_mut(self.region); - } -} - -#[derive(Debug, Error)] -pub enum MemoryError { - #[error("Out of bounds")] - OutOfBounds, -} +pub use guest_type::GuestType; +pub use memory::{GuestMemory, GuestPtr, GuestPtrMut}; +pub use region::Region; diff --git a/crates/memory/src/memory.rs b/crates/memory/src/memory.rs new file mode 100644 index 0000000000..7cffb52606 --- /dev/null +++ b/crates/memory/src/memory.rs @@ -0,0 +1,117 @@ +use std::cell::RefCell; +use std::marker::PhantomData; +use std::rc::Rc; +use thiserror::Error; + +use crate::borrow::GuestBorrows; +use crate::guest_type::GuestType; +use crate::region::Region; + +pub struct GuestMemory<'a> { + ptr: *mut u8, + len: u32, + lifetime: PhantomData<&'a ()>, + borrows: Rc>, +} + +impl<'a> GuestMemory<'a> { + pub fn new(ptr: *mut u8, len: u32) -> GuestMemory<'a> { + GuestMemory { + ptr, + len, + lifetime: PhantomData, + borrows: Rc::new(RefCell::new(GuestBorrows::new())), + } + } + + fn contains(&self, r: Region) -> bool { + r.start < self.len + && r.len < self.len // make sure next clause doesnt underflow + && r.start < (self.len - r.len) + } + + pub fn ptr(&'a self, at: u32) -> Result, MemoryError> { + let r = Region { + start: at, + len: T::len(), + }; + let mut borrows = self.borrows.borrow_mut(); + if !self.contains(r) { + Err(MemoryError::OutOfBounds(r))?; + } + if borrows.borrow_immut(r) { + Ok(GuestPtr { + mem: &self, + region: r, + type_: PhantomData, + }) + } else { + Err(MemoryError::Borrowed(r)) + } + } + + pub fn ptr_mut(&'a self, at: u32) -> Result, MemoryError> { + let r = Region { + start: at, + len: T::len(), + }; + let mut borrows = self.borrows.borrow_mut(); + if !self.contains(r) { + Err(MemoryError::OutOfBounds(r))?; + } + if borrows.borrow_mut(r) { + Ok(GuestPtrMut { + mem: &self, + region: r, + type_: PhantomData, + }) + } else { + Err(MemoryError::Borrowed(r)) + } + } +} + +pub struct GuestPtr<'a, T> { + mem: &'a GuestMemory<'a>, + region: Region, + type_: PhantomData, +} + +impl<'a, T> GuestPtr<'a, T> { + pub fn ptr(&self) -> *const u8 { + (self.mem.ptr as usize + self.region.start as usize) as *const u8 + } +} + +impl<'a, T> Drop for GuestPtr<'a, T> { + fn drop(&mut self) { + let mut borrows = self.mem.borrows.borrow_mut(); + borrows.unborrow_immut(self.region); + } +} + +pub struct GuestPtrMut<'a, T> { + mem: &'a GuestMemory<'a>, + region: Region, + type_: PhantomData, +} + +impl<'a, T> GuestPtrMut<'a, T> { + pub fn ptr_mut(&self) -> *mut u8 { + (self.mem.ptr as usize + self.region.start as usize) as *mut u8 + } +} +impl<'a, T> Drop for GuestPtrMut<'a, T> { + fn drop(&mut self) { + let mut borrows = self.mem.borrows.borrow_mut(); + borrows.unborrow_mut(self.region); + } +} + +#[derive(Debug, Error)] +pub enum MemoryError { + #[error("Out of bounds: {0:?}")] + OutOfBounds(Region), + #[error("Borrowed: {0:?}")] + Borrowed(Region), +} diff --git a/crates/memory/src/region.rs b/crates/memory/src/region.rs new file mode 100644 index 0000000000..3df191117f --- /dev/null +++ b/crates/memory/src/region.rs @@ -0,0 +1,37 @@ +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub struct Region { + pub start: u32, + pub len: u32, +} + +impl Region { + pub fn overlaps(&self, rhs: Region) -> bool { + let self_start = self.start as u64; + let self_end = self.start as u64 + self.len as u64; + + let rhs_start = rhs.start as u64; + let rhs_end = rhs.start as u64 + rhs.len as u64; + + // start of rhs inside self: + if rhs_start >= self_start && rhs_start < self_end { + return true; + } + + // end of rhs inside self: + if rhs_end >= self_start && rhs_end < self_end { + return true; + } + + // start of self inside rhs: + if self_start >= rhs_start && self_start < rhs_end { + return true; + } + + // end of self inside rhs: XXX is this redundant? i suspect it is but im too tired + if self_end >= rhs_start && self_end < rhs_end { + return true; + } + + false + } +} diff --git a/test.witx b/test.witx index f1185b63fe..414b9617cd 100644 --- a/test.witx +++ b/test.witx @@ -8,6 +8,7 @@ (module $foo (@interface func (export "bar") - (param $name string) + (param $an_int (@witx pointer u32)) + (param $an_float (@witx pointer f32)) (result $error $errno)) ) From 9291495e57aaf49f853977cc62acaa2a1d56a3e1 Mon Sep 17 00:00:00 2001 From: Pat Hickey Date: Sun, 19 Jan 2020 19:33:41 -0800 Subject: [PATCH 10/86] put names all in one place. some stub code for funcs! --- crates/generate/src/funcs.rs | 23 ++++++++++++ crates/generate/src/lib.rs | 19 ++++++---- crates/generate/src/names.rs | 68 ++++++++++++++++++++++++++++++++++++ crates/generate/src/types.rs | 57 +++++++++++------------------- test.witx | 4 +-- 5 files changed, 126 insertions(+), 45 deletions(-) create mode 100644 crates/generate/src/funcs.rs create mode 100644 crates/generate/src/names.rs diff --git a/crates/generate/src/funcs.rs b/crates/generate/src/funcs.rs new file mode 100644 index 0000000000..6a6430cb80 --- /dev/null +++ b/crates/generate/src/funcs.rs @@ -0,0 +1,23 @@ +use proc_macro2::TokenStream; +use quote::quote; + +use crate::names::Names; + +pub fn define_func(names: &Names, func: &witx::InterfaceFunc) -> TokenStream { + let ident = names.func(&func.name); + let mut args = TokenStream::new(); + + for param in func.params.iter() { + let name = names.func_param(¶m.name); + let type_ = names.type_ref(¶m.tref); + args.extend(quote!(#name: #type_,)); + } + + let mut rets = TokenStream::new(); + for result in func.results.iter() { + let type_ = names.type_ref(&result.tref); + rets.extend(quote!(#type_,)); + } + + quote!(pub fn #ident(#args) -> (#rets) { unimplemented!() }) +} diff --git a/crates/generate/src/lib.rs b/crates/generate/src/lib.rs index f78a484153..7f9deb9687 100644 --- a/crates/generate/src/lib.rs +++ b/crates/generate/src/lib.rs @@ -1,33 +1,40 @@ extern crate proc_macro; +mod funcs; +mod names; mod parse; mod types; -use heck::SnakeCase; use proc_macro::TokenStream; use proc_macro2::TokenStream as TokenStream2; -use quote::{format_ident, quote}; +use quote::quote; + +use funcs::define_func; +use names::Names; use types::define_datatype; #[proc_macro] pub fn from_witx(args: TokenStream) -> TokenStream { let args = TokenStream2::from(args); let witx_paths = parse::witx_paths(args).expect("parsing macro arguments"); + + let names = Names::new(); // TODO parse the names from the invocation of the macro, or from a file? + let doc = witx::load(&witx_paths).expect("loading witx"); let mut types = TokenStream2::new(); for namedtype in doc.typenames() { - let def = define_datatype(&namedtype); + let def = define_datatype(&names, &namedtype); types.extend(def); } let mut modules = TokenStream2::new(); for module in doc.modules() { - let modname = format_ident!("{}", module.name.as_str().to_snake_case()); + let modname = names.module(&module.name); + let mut fs = TokenStream2::new(); for func in module.funcs() { - let ident = format_ident!("{}", func.name.as_str().to_snake_case()); - fs.extend(quote!(pub fn #ident() { unimplemented!() })); + fs.extend(define_func(&names, &func)); } modules.extend(quote!(mod #modname { use super::types::*; #fs })); } diff --git a/crates/generate/src/names.rs b/crates/generate/src/names.rs new file mode 100644 index 0000000000..a1ca1a0407 --- /dev/null +++ b/crates/generate/src/names.rs @@ -0,0 +1,68 @@ +use heck::{CamelCase, SnakeCase}; +use proc_macro2::{Ident, TokenStream}; +use quote::{format_ident, quote}; +use witx::{BuiltinType, Id, TypeRef}; + +#[derive(Debug, Clone)] +pub struct Names { + // FIXME: overrides go in here, so we can map e.g. 2big => TooBig +} + +impl Names { + pub fn new() -> Names { + Names {} + } + pub fn type_(&self, id: &Id) -> Ident { + format_ident!("{}", id.as_str().to_camel_case()) + } + pub fn builtin_type(&self, b: BuiltinType) -> TokenStream { + match b { + BuiltinType::String => quote!(String), + BuiltinType::U8 => quote!(u8), + BuiltinType::U16 => quote!(u16), + BuiltinType::U32 => quote!(u32), + BuiltinType::U64 => quote!(u64), + BuiltinType::S8 => quote!(i8), + BuiltinType::S16 => quote!(i16), + BuiltinType::S32 => quote!(i32), + BuiltinType::S64 => quote!(i64), + BuiltinType::F32 => quote!(f32), + BuiltinType::F64 => quote!(f64), + BuiltinType::Char8 => quote!(char), + BuiltinType::USize => quote!(usize), + } + } + pub fn type_ref(&self, tref: &TypeRef) -> TokenStream { + match tref { + TypeRef::Name(nt) => { + let ident = self.type_(&nt.name); + quote!(#ident) + } + TypeRef::Value(ty) => match &**ty { + witx::Type::Builtin(builtin) => self.builtin_type(*builtin), + _ => unimplemented!("anonymous type ref"), + }, + } + } + + pub fn enum_variant(&self, id: &Id) -> Ident { + // FIXME this is a hack - just a proof of concept. + if id.as_str().starts_with('2') { + format_ident!("TooBig") + } else { + format_ident!("{}", id.as_str().to_camel_case()) + } + } + + pub fn module(&self, id: &Id) -> Ident { + format_ident!("{}", id.as_str().to_snake_case()) + } + + pub fn func(&self, id: &Id) -> Ident { + format_ident!("{}", id.as_str().to_snake_case()) + } + + pub fn func_param(&self, id: &Id) -> Ident { + format_ident!("{}", id.as_str().to_snake_case()) + } +} diff --git a/crates/generate/src/types.rs b/crates/generate/src/types.rs index f9c98b9234..22cc025c9b 100644 --- a/crates/generate/src/types.rs +++ b/crates/generate/src/types.rs @@ -1,18 +1,19 @@ -use heck::{CamelCase, MixedCase}; -use proc_macro2::TokenStream; -use quote::{format_ident, quote}; +use crate::names::Names; -pub fn define_datatype(namedtype: &witx::NamedType) -> TokenStream { +use proc_macro2::TokenStream; +use quote::quote; + +pub fn define_datatype(names: &Names, namedtype: &witx::NamedType) -> TokenStream { match &namedtype.tref { - witx::TypeRef::Name(alias_to) => define_alias(&namedtype.name, &alias_to), + witx::TypeRef::Name(alias_to) => define_alias(names, &namedtype.name, &alias_to), witx::TypeRef::Value(v) => match &**v { - witx::Type::Enum(e) => define_enum(&namedtype.name, &e), + witx::Type::Enum(e) => define_enum(names, &namedtype.name, &e), witx::Type::Int(_) => unimplemented!("int types"), witx::Type::Flags(_) => unimplemented!("flag types"), witx::Type::Struct(_) => unimplemented!("struct types"), witx::Type::Union(_) => unimplemented!("union types"), witx::Type::Handle(_h) => unimplemented!("handle types"), - witx::Type::Builtin(b) => define_builtin(&namedtype.name, &b), + witx::Type::Builtin(b) => define_builtin(names, &namedtype.name, *b), witx::Type::Pointer { .. } => unimplemented!("pointer types"), witx::Type::ConstPointer { .. } => unimplemented!("constpointer types"), witx::Type::Array { .. } => unimplemented!("array types"), @@ -20,15 +21,16 @@ pub fn define_datatype(namedtype: &witx::NamedType) -> TokenStream { } } -fn define_alias(name: &witx::Id, to: &witx::NamedType) -> TokenStream { - let ident = format_ident!("{}", name.as_str().to_camel_case()); - let to = format_ident!("{}", to.name.as_str().to_camel_case()); +fn define_alias(names: &Names, name: &witx::Id, to: &witx::NamedType) -> TokenStream { + let ident = names.type_(name); + let to = names.type_(&to.name); quote!(pub type #ident = #to;) } -fn define_enum(name: &witx::Id, e: &witx::EnumDatatype) -> TokenStream { - let ident = format_ident!("{}", name.as_str().to_camel_case()); +fn define_enum(names: &Names, name: &witx::Id, e: &witx::EnumDatatype) -> TokenStream { + let ident = names.type_(&name); + let mut output = TokenStream::new(); let repr = int_repr_tokens(e.repr); output.extend(quote!(#[repr(#repr)])); @@ -36,13 +38,8 @@ fn define_enum(name: &witx::Id, e: &witx::EnumDatatype) -> TokenStream { let mut inner = TokenStream::new(); for variant in &e.variants { - let value_name = if name.as_str() == "errno" { - // FIXME discussion point! - format_ident!("E{}", variant.name.as_str().to_mixed_case()) - } else { - format_ident!("{}", variant.name.as_str().to_camel_case()) - }; - inner.extend(quote!(#value_name,)); + let variant_name = names.enum_variant(&variant.name); + inner.extend(quote!(#variant_name,)); } output.extend(quote!(pub enum #ident { @@ -52,24 +49,10 @@ fn define_enum(name: &witx::Id, e: &witx::EnumDatatype) -> TokenStream { output } -fn define_builtin(name: &witx::Id, builtin: &witx::BuiltinType) -> TokenStream { - let ident = format_ident!("{}", name.as_str().to_camel_case()); - let prim = match builtin { - witx::BuiltinType::String => quote!(String), - witx::BuiltinType::U8 => quote!(u8), - witx::BuiltinType::U16 => quote!(u16), - witx::BuiltinType::U32 => quote!(u32), - witx::BuiltinType::U64 => quote!(u64), - witx::BuiltinType::S8 => quote!(i8), - witx::BuiltinType::S16 => quote!(i16), - witx::BuiltinType::S32 => quote!(i32), - witx::BuiltinType::S64 => quote!(i64), - witx::BuiltinType::F32 => quote!(f32), - witx::BuiltinType::F64 => quote!(f64), - witx::BuiltinType::Char8 => quote!(char), - witx::BuiltinType::USize => quote!(usize), - }; - quote!(pub type #ident = #prim;) +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;) } fn int_repr_tokens(int_repr: witx::IntRepr) -> TokenStream { diff --git a/test.witx b/test.witx index 414b9617cd..1750ec3d15 100644 --- a/test.witx +++ b/test.witx @@ -8,7 +8,7 @@ (module $foo (@interface func (export "bar") - (param $an_int (@witx pointer u32)) - (param $an_float (@witx pointer f32)) + (param $an_int u32) + (param $an_float f32) (result $error $errno)) ) From b8feffe6e1c0969087fc3b356c250df611f84a4e Mon Sep 17 00:00:00 2001 From: Pat Hickey Date: Mon, 20 Jan 2020 14:55:06 -0800 Subject: [PATCH 11/86] funcs get abi type definitions --- crates/generate/src/funcs.rs | 50 +++++++++++++++++++++++++++++------- crates/generate/src/lib.rs | 9 ++++++- crates/generate/src/names.rs | 16 +++++++++++- src/lib.rs | 1 + 4 files changed, 65 insertions(+), 11 deletions(-) diff --git a/crates/generate/src/funcs.rs b/crates/generate/src/funcs.rs index 6a6430cb80..3f856b863a 100644 --- a/crates/generate/src/funcs.rs +++ b/crates/generate/src/funcs.rs @@ -3,21 +3,53 @@ use quote::quote; use crate::names::Names; +// FIXME need to template what arguments are required to an import function - some context +// struct (e.g. WasiCtx) should be provided at the invocation of the `gen` proc macro. +// Rather than pass in memory as a `&mut [u8]` as today, require a `GuestMemory<'a>` be +// passed in. pub fn define_func(names: &Names, func: &witx::InterfaceFunc) -> TokenStream { let ident = names.func(&func.name); - let mut args = TokenStream::new(); + let mut args = quote!(wasi_ctx: &mut WasiCtx, memory: GuestMemory,); + + let arg_signature = |param: &witx::InterfaceFuncParam| -> TokenStream { + let name = names.func_param(¶m.name); + match param.tref.type_().passed_by() { + witx::TypePassedBy::Value(atom) => { + let atom = names.atom_type(atom); + quote!(#name: #atom,) + } + witx::TypePassedBy::Pointer => { + let atom = names.atom_type(witx::AtomType::I32); + quote!(#name: #atom,) + } + witx::TypePassedBy::PointerLengthPair => { + let atom = names.atom_type(witx::AtomType::I32); + let len_name = names.func_len_param(¶m.name); + quote!(#name: #atom, #len_name, #atom,) + } + } + }; for param in func.params.iter() { - let name = names.func_param(¶m.name); - let type_ = names.type_ref(¶m.tref); - args.extend(quote!(#name: #type_,)); + args.extend(arg_signature(param)); } - let mut rets = TokenStream::new(); - for result in func.results.iter() { - let type_ = names.type_ref(&result.tref); - rets.extend(quote!(#type_,)); + if let Some(arg_results) = func.results.get(1..) { + for result in arg_results { + args.extend(arg_signature(result)) + } } - quote!(pub fn #ident(#args) -> (#rets) { unimplemented!() }) + let ret = if let Some(first_result) = func.results.get(0) { + match first_result.tref.type_().passed_by() { + witx::TypePassedBy::Value(atom) => names.atom_type(atom), + _ => unreachable!("first result should always be passed by value"), + } + } else if func.noreturn { + quote!(!) + } else { + quote!(()) + }; + + quote!(pub fn #ident(#args) -> #ret { unimplemented!() }) } diff --git a/crates/generate/src/lib.rs b/crates/generate/src/lib.rs index 7f9deb9687..acf60e1401 100644 --- a/crates/generate/src/lib.rs +++ b/crates/generate/src/lib.rs @@ -36,7 +36,14 @@ pub fn from_witx(args: TokenStream) -> TokenStream { for func in module.funcs() { fs.extend(define_func(&names, &func)); } - modules.extend(quote!(mod #modname { use super::types::*; #fs })); + modules.extend(quote!( + mod #modname { + use super::*; + use super::types::*; + use memory::*; + #fs + } + )); } TokenStream::from(quote!(mod types { #types } #modules)) diff --git a/crates/generate/src/names.rs b/crates/generate/src/names.rs index a1ca1a0407..ed59e44e3c 100644 --- a/crates/generate/src/names.rs +++ b/crates/generate/src/names.rs @@ -1,7 +1,7 @@ use heck::{CamelCase, SnakeCase}; use proc_macro2::{Ident, TokenStream}; use quote::{format_ident, quote}; -use witx::{BuiltinType, Id, TypeRef}; +use witx::{AtomType, BuiltinType, Id, TypeRef}; #[derive(Debug, Clone)] pub struct Names { @@ -32,6 +32,15 @@ impl Names { BuiltinType::USize => quote!(usize), } } + pub fn atom_type(&self, atom: AtomType) -> TokenStream { + match atom { + AtomType::I32 => quote!(i32), + AtomType::I64 => quote!(i64), + AtomType::F32 => quote!(f32), + AtomType::F64 => quote!(f64), + } + } + pub fn type_ref(&self, tref: &TypeRef) -> TokenStream { match tref { TypeRef::Name(nt) => { @@ -65,4 +74,9 @@ impl Names { pub fn func_param(&self, id: &Id) -> Ident { format_ident!("{}", id.as_str().to_snake_case()) } + + /// For when you need a {name}_len parameter for passing an array: + pub fn func_len_param(&self, id: &Id) -> Ident { + format_ident!("{}_len", id.as_str().to_snake_case()) + } } diff --git a/src/lib.rs b/src/lib.rs index 4c58828ac7..227fdd8126 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,5 @@ pub mod test { + pub struct WasiCtx {} // FIXME: parameterize macro on what ctx type is used here generate::from_witx!("test.witx"); } /* From aa5c5f70188dcfc7137fc54bfc255b03079deab8 Mon Sep 17 00:00:00 2001 From: Pat Hickey Date: Wed, 22 Jan 2020 16:38:25 -0800 Subject: [PATCH 12/86] flesh out the guest type traits a bit further --- crates/generate/src/names.rs | 1 + crates/generate/src/types.rs | 56 ++++++++++++-- crates/memory/src/guest_type.rs | 129 +++++++++++++++----------------- crates/memory/src/lib.rs | 2 +- crates/memory/src/memory.rs | 15 +++- 5 files changed, 126 insertions(+), 77 deletions(-) diff --git a/crates/generate/src/names.rs b/crates/generate/src/names.rs index ed59e44e3c..a674f13cfe 100644 --- a/crates/generate/src/names.rs +++ b/crates/generate/src/names.rs @@ -41,6 +41,7 @@ impl Names { } } + #[allow(unused)] pub fn type_ref(&self, tref: &TypeRef) -> TokenStream { match tref { TypeRef::Name(nt) => { diff --git a/crates/generate/src/types.rs b/crates/generate/src/types.rs index 22cc025c9b..4b107f4f90 100644 --- a/crates/generate/src/types.rs +++ b/crates/generate/src/types.rs @@ -36,15 +36,61 @@ fn define_enum(names: &Names, name: &witx::Id, e: &witx::EnumDatatype) -> TokenS output.extend(quote!(#[repr(#repr)])); output.extend(quote!(#[derive(Copy, Clone, Debug, std::hash::Hash, Eq, PartialEq)])); - let mut inner = TokenStream::new(); - for variant in &e.variants { + let mut variants = TokenStream::new(); + let mut to_repr_cases = TokenStream::new(); + let mut tryfrom_repr_cases = TokenStream::new(); + for (n, variant) in e.variants.iter().enumerate() { + let n = n as u32; let variant_name = names.enum_variant(&variant.name); - inner.extend(quote!(#variant_name,)); + variants.extend(quote!(#variant_name,)); + tryfrom_repr_cases.extend(quote!(#n => Ok(#ident::#variant_name),)); + to_repr_cases.extend(quote!(#ident::#variant_name => #n,)); } + tryfrom_repr_cases + .extend(quote!(_ => Err(::memory::GuestValueError::InvalidEnum(stringify!(#ident))))); + output.extend(quote!(pub enum #ident { - #inner - })); + #variants + } + + impl ::std::convert::TryFrom<#repr> for #ident { + type Error = ::memory::GuestValueError; + fn try_from(value: #repr) -> Result<#ident, ::memory::GuestValueError> { + match value { + #tryfrom_repr_cases + } + } + } + + impl From<#ident> for #repr { + fn from(e: #ident) -> #repr { + match e { + #to_repr_cases + } + } + } + + impl ::memory::GuestType for #ident { + fn size() -> u32 { + ::std::mem::size_of::<#repr>() as u32 + } + fn name() -> &'static str { + stringify!(#ident) + } + } + + impl ::memory::GuestTypeCopy for #ident { + fn read_val(src: ::memory::GuestPtr<#ident>) -> Result<#ident, ::memory::GuestValueError> { + // Get the pointer to memory, cast it to *const #repr, read_unaligned, then use the tryinto + unimplemented!() + } + fn write_val(val: #ident, dest: ::memory::GuestPtrMut<#ident>) { + // use the into<#repr>, get pointer to memory, write_unaligned + unimplemented!() + } + } + )); output } diff --git a/crates/memory/src/guest_type.rs b/crates/memory/src/guest_type.rs index c34aa0fce4..820e3b28f2 100644 --- a/crates/memory/src/guest_type.rs +++ b/crates/memory/src/guest_type.rs @@ -1,75 +1,68 @@ -pub trait GuestType { - fn len() -> u32; +use crate::{GuestPtr, GuestPtrMut}; +use thiserror::Error; + +pub trait GuestType: Sized { + fn size() -> u32; + fn name() -> &'static str; } -impl GuestType for u8 { - fn len() -> u32 { - 1 +#[derive(Debug, Error)] +pub enum GuestValueError { + #[error("Invalid enum {0}")] + InvalidEnum(&'static str), +} + +pub trait GuestTypeCopy: GuestType + Copy { + fn read_val(src: GuestPtr) -> Result; + fn write_val(val: Self, dest: GuestPtrMut); +} + +pub trait GuestTypeClone: GuestType + Clone { + fn read_ref(src: GuestPtr, dest: &mut Self) -> Result<(), GuestValueError>; + fn write_ref(val: &Self, dest: GuestPtrMut); +} + +impl GuestTypeClone for T +where + T: GuestTypeCopy, +{ + fn read_ref(src: GuestPtr, dest: &mut T) -> Result<(), GuestValueError> { + let val = GuestTypeCopy::read_val(src)?; + *dest = val; + Ok(()) + } + fn write_ref(val: &T, dest: GuestPtrMut) { + GuestTypeCopy::write_val(*val, dest) } } -impl GuestType for i8 { - fn len() -> u32 { - 1 - } +macro_rules! builtin_copy { + ( $( $t:ident ), * ) => { + $( + impl GuestType for $t { + fn size() -> u32 { + ::std::mem::size_of::<$t>() as u32 + } + fn name() -> &'static str { + ::std::stringify!($t) + } + } + + impl GuestTypeCopy for $t { + fn read_val(src: GuestPtr<$t>) -> Result<$t, GuestValueError> { + Ok(unsafe { + ::std::ptr::read_unaligned(src.ptr() as *const $t) + }) + } + fn write_val(val: $t, dest: GuestPtrMut<$t>) { + unsafe { + ::std::ptr::write_unaligned(dest.ptr_mut() as *mut $t, val) + } + } + } + )* + }; } -impl GuestType for u16 { - fn len() -> u32 { - 2 - } -} - -impl GuestType for i16 { - fn len() -> u32 { - 2 - } -} - -impl GuestType for u32 { - fn len() -> u32 { - 4 - } -} - -impl GuestType for i32 { - fn len() -> u32 { - 4 - } -} - -impl GuestType for f32 { - fn len() -> u32 { - 4 - } -} - -impl GuestType for u64 { - fn len() -> u32 { - 8 - } -} - -impl GuestType for i64 { - fn len() -> u32 { - 8 - } -} - -impl GuestType for f64 { - fn len() -> u32 { - 8 - } -} - -impl GuestType for char { - fn len() -> u32 { - 1 - } -} - -impl GuestType for usize { - fn len() -> u32 { - 4 - } -} +// These definitions correspond to all the witx BuiltinType variants that are Copy: +builtin_copy!(u8, i8, u16, i16, u32, i32, u64, i64, f32, f64, usize, char); diff --git a/crates/memory/src/lib.rs b/crates/memory/src/lib.rs index b8ade8806c..6b8edeb287 100644 --- a/crates/memory/src/lib.rs +++ b/crates/memory/src/lib.rs @@ -3,6 +3,6 @@ mod guest_type; mod memory; mod region; -pub use guest_type::GuestType; +pub use guest_type::{GuestType, GuestTypeClone, GuestTypeCopy, GuestValueError}; pub use memory::{GuestMemory, GuestPtr, GuestPtrMut}; pub use region::Region; diff --git a/crates/memory/src/memory.rs b/crates/memory/src/memory.rs index 7cffb52606..efb027550e 100644 --- a/crates/memory/src/memory.rs +++ b/crates/memory/src/memory.rs @@ -33,7 +33,7 @@ impl<'a> GuestMemory<'a> { pub fn ptr(&'a self, at: u32) -> Result, MemoryError> { let r = Region { start: at, - len: T::len(), + len: T::size(), }; let mut borrows = self.borrows.borrow_mut(); if !self.contains(r) { @@ -53,7 +53,7 @@ impl<'a> GuestMemory<'a> { pub fn ptr_mut(&'a self, at: u32) -> Result, MemoryError> { let r = Region { start: at, - len: T::len(), + len: T::size(), }; let mut borrows = self.borrows.borrow_mut(); if !self.contains(r) { @@ -77,10 +77,19 @@ pub struct GuestPtr<'a, T> { type_: PhantomData, } -impl<'a, T> GuestPtr<'a, T> { +impl<'a, T: GuestType> GuestPtr<'a, T> { pub fn ptr(&self) -> *const u8 { (self.mem.ptr as usize + self.region.start as usize) as *const u8 } + + pub unsafe fn downcast(self) -> GuestPtr<'a, Q> { + debug_assert!(T::size() == Q::size(), "downcast to type of same size"); + GuestPtr { + mem: self.mem, + region: self.region, + type_: PhantomData, + } + } } impl<'a, T> Drop for GuestPtr<'a, T> { From 97077954f8bb3bc249a20ebde57eca47b01ff2fd Mon Sep 17 00:00:00 2001 From: Pat Hickey Date: Wed, 22 Jan 2020 17:23:29 -0800 Subject: [PATCH 13/86] enum generation: fill in GuestTypeCopy impl --- crates/generate/src/types.rs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/crates/generate/src/types.rs b/crates/generate/src/types.rs index 4b107f4f90..e517134c9f 100644 --- a/crates/generate/src/types.rs +++ b/crates/generate/src/types.rs @@ -82,12 +82,15 @@ fn define_enum(names: &Names, name: &witx::Id, e: &witx::EnumDatatype) -> TokenS impl ::memory::GuestTypeCopy for #ident { fn read_val(src: ::memory::GuestPtr<#ident>) -> Result<#ident, ::memory::GuestValueError> { - // Get the pointer to memory, cast it to *const #repr, read_unaligned, then use the tryinto - unimplemented!() + use ::std::convert::TryInto; + let val = unsafe { ::std::ptr::read_unaligned(src.ptr() as *const #repr) }; + val.try_into() } fn write_val(val: #ident, dest: ::memory::GuestPtrMut<#ident>) { - // use the into<#repr>, get pointer to memory, write_unaligned - unimplemented!() + let val: #repr = val.into(); + unsafe { + ::std::ptr::write_unaligned(dest.ptr_mut() as *mut #repr, val) + }; } } )); From c05475b806f1c768954adf8d979d3919f49546bf Mon Sep 17 00:00:00 2001 From: Pat Hickey Date: Wed, 22 Jan 2020 20:47:40 -0800 Subject: [PATCH 14/86] generate: now we have a way to do errors, i guess --- crates/generate/src/errors.rs | 45 ++++++++++++++ crates/generate/src/funcs.rs | 39 ++++++------ crates/generate/src/lib.rs | 35 +++++------ crates/generate/src/names.rs | 5 +- crates/generate/src/types.rs | 109 +++++++++++++++++----------------- crates/memory/src/lib.rs | 2 +- src/lib.rs | 17 +++++- test.witx | 2 + 8 files changed, 158 insertions(+), 96 deletions(-) create mode 100644 crates/generate/src/errors.rs diff --git a/crates/generate/src/errors.rs b/crates/generate/src/errors.rs new file mode 100644 index 0000000000..d1b4bbfbc3 --- /dev/null +++ b/crates/generate/src/errors.rs @@ -0,0 +1,45 @@ +use crate::names::Names; +use heck::SnakeCase; +use proc_macro2::TokenStream; +use quote::{format_ident, quote}; +use std::collections::HashSet; +use witx::{Document, TypeRef}; + +/// The context struct needs to implement a trait for converting memory and value errors into the +/// witx doc's error types. +pub fn define_error_trait(names: &Names, doc: &Document) -> TokenStream { + // All non-anonymous first return types are used to pass errors. + let error_typenames = doc + .modules() + .flat_map(|m| { + m.funcs() + .filter_map(|f| { + f.results.get(0).and_then(|r| match &r.tref { + TypeRef::Name(nt) => Some(nt.name.clone()), + _ => None, + }) + }) + .collect::>() + }) + .collect::>(); + + let methods = error_typenames.iter().map(|typename| { + let tname = names.type_(typename); + let methodfragment = typename.as_str().to_snake_case(); + let success = format_ident!("success_to_{}", methodfragment); + let memory_error = format_ident!("memory_error_to_{}", methodfragment); + let value_error = format_ident!("value_error_to_{}", methodfragment); + + quote! { + fn #success(&mut self) -> #tname; + fn #memory_error(&mut self, err: ::memory::MemoryError) -> #tname; + fn #value_error(&mut self, err: ::memory::GuestValueError) -> #tname; + } + }); + + quote!( + pub trait WitxErrorConversion { + #(#methods)* + } + ) +} diff --git a/crates/generate/src/funcs.rs b/crates/generate/src/funcs.rs index 3f856b863a..bfe720f2d9 100644 --- a/crates/generate/src/funcs.rs +++ b/crates/generate/src/funcs.rs @@ -3,44 +3,45 @@ use quote::quote; use crate::names::Names; -// FIXME need to template what arguments are required to an import function - some context +// FIXME need to template what argument is required to an import function - some context // struct (e.g. WasiCtx) should be provided at the invocation of the `gen` proc macro. -// Rather than pass in memory as a `&mut [u8]` as today, require a `GuestMemory<'a>` be -// passed in. +// +// Additionally - need to template how to transform GuestValueError and MemoryError into +// the error type returned! From impl is a good start, but what if we want to log +// a more informative error? Maybe the conversion should be a (generated, for each by-value first +// return type) trait impled by WasiCtx? pub fn define_func(names: &Names, func: &witx::InterfaceFunc) -> TokenStream { let ident = names.func(&func.name); - let mut args = quote!(wasi_ctx: &mut WasiCtx, memory: GuestMemory,); let arg_signature = |param: &witx::InterfaceFuncParam| -> TokenStream { let name = names.func_param(¶m.name); match param.tref.type_().passed_by() { witx::TypePassedBy::Value(atom) => { let atom = names.atom_type(atom); - quote!(#name: #atom,) + quote!(#name: #atom) } witx::TypePassedBy::Pointer => { let atom = names.atom_type(witx::AtomType::I32); - quote!(#name: #atom,) + quote!(#name: #atom) } witx::TypePassedBy::PointerLengthPair => { let atom = names.atom_type(witx::AtomType::I32); let len_name = names.func_len_param(¶m.name); - quote!(#name: #atom, #len_name, #atom,) + quote!(#name: #atom, #len_name: #atom) } } }; - for param in func.params.iter() { - args.extend(arg_signature(param)); - } - - if let Some(arg_results) = func.results.get(1..) { - for result in arg_results { - args.extend(arg_signature(result)) - } - } - - let ret = if let Some(first_result) = func.results.get(0) { + let params = func + .params + .iter() + .chain(func.results.iter().skip(1)) + .map(arg_signature); + let abi_args = quote!( + wasi_ctx: &mut WasiCtx, memory: GuestMemory, + #(#params),* + ); + let abi_ret = if let Some(first_result) = func.results.get(0) { match first_result.tref.type_().passed_by() { witx::TypePassedBy::Value(atom) => names.atom_type(atom), _ => unreachable!("first result should always be passed by value"), @@ -51,5 +52,5 @@ pub fn define_func(names: &Names, func: &witx::InterfaceFunc) -> TokenStream { quote!(()) }; - quote!(pub fn #ident(#args) -> #ret { unimplemented!() }) + quote!(pub fn #ident(#abi_args) -> #abi_ret { unimplemented!() }) } diff --git a/crates/generate/src/lib.rs b/crates/generate/src/lib.rs index acf60e1401..830e92e900 100644 --- a/crates/generate/src/lib.rs +++ b/crates/generate/src/lib.rs @@ -1,5 +1,6 @@ extern crate proc_macro; +mod errors; mod funcs; mod names; mod parse; @@ -9,6 +10,7 @@ use proc_macro::TokenStream; use proc_macro2::TokenStream as TokenStream2; use quote::quote; +use errors::define_error_trait; use funcs::define_func; use names::Names; use types::define_datatype; @@ -22,29 +24,28 @@ pub fn from_witx(args: TokenStream) -> TokenStream { let doc = witx::load(&witx_paths).expect("loading witx"); - let mut types = TokenStream2::new(); - for namedtype in doc.typenames() { - let def = define_datatype(&names, &namedtype); - types.extend(def); - } + let types = doc.typenames().map(|t| define_datatype(&names, &t)); - let mut modules = TokenStream2::new(); - for module in doc.modules() { + let modules = doc.modules().map(|module| { let modname = names.module(&module.name); - - let mut fs = TokenStream2::new(); - for func in module.funcs() { - fs.extend(define_func(&names, &func)); - } - modules.extend(quote!( + let fs = module.funcs().map(|f| define_func(&names, &f)); + quote!( mod #modname { use super::*; use super::types::*; use memory::*; - #fs + #(#fs)* } - )); - } + ) + }); - TokenStream::from(quote!(mod types { #types } #modules)) + let error_trait = define_error_trait(&names, &doc); + + TokenStream::from(quote!( + mod types { + #(#types)* + #error_trait + } + #(#modules)* + )) } diff --git a/crates/generate/src/names.rs b/crates/generate/src/names.rs index a674f13cfe..bd296ac88e 100644 --- a/crates/generate/src/names.rs +++ b/crates/generate/src/names.rs @@ -12,8 +12,9 @@ impl Names { pub fn new() -> Names { Names {} } - pub fn type_(&self, id: &Id) -> Ident { - format_ident!("{}", id.as_str().to_camel_case()) + pub fn type_(&self, id: &Id) -> TokenStream { + let ident = format_ident!("{}", id.as_str().to_camel_case()); + quote!(#ident) } pub fn builtin_type(&self, b: BuiltinType) -> TokenStream { match b { diff --git a/crates/generate/src/types.rs b/crates/generate/src/types.rs index e517134c9f..144b11f2e1 100644 --- a/crates/generate/src/types.rs +++ b/crates/generate/src/types.rs @@ -31,71 +31,68 @@ fn define_alias(names: &Names, name: &witx::Id, to: &witx::NamedType) -> TokenSt fn define_enum(names: &Names, name: &witx::Id, e: &witx::EnumDatatype) -> TokenStream { let ident = names.type_(&name); - let mut output = TokenStream::new(); let repr = int_repr_tokens(e.repr); - output.extend(quote!(#[repr(#repr)])); - output.extend(quote!(#[derive(Copy, Clone, Debug, std::hash::Hash, Eq, PartialEq)])); - let mut variants = TokenStream::new(); - let mut to_repr_cases = TokenStream::new(); - let mut tryfrom_repr_cases = TokenStream::new(); - for (n, variant) in e.variants.iter().enumerate() { + let variant_names = e.variants.iter().map(|v| names.enum_variant(&v.name)); + let tryfrom_repr_cases = e.variants.iter().enumerate().map(|(n, v)| { + let variant_name = names.enum_variant(&v.name); let n = n as u32; - let variant_name = names.enum_variant(&variant.name); - variants.extend(quote!(#variant_name,)); - tryfrom_repr_cases.extend(quote!(#n => Ok(#ident::#variant_name),)); - to_repr_cases.extend(quote!(#ident::#variant_name => #n,)); - } + quote!(#n => Ok(#ident::#variant_name)) + }); + let to_repr_cases = e.variants.iter().enumerate().map(|(n, v)| { + let variant_name = names.enum_variant(&v.name); + let n = n as u32; + quote!(#ident::#variant_name => #n) + }); - tryfrom_repr_cases - .extend(quote!(_ => Err(::memory::GuestValueError::InvalidEnum(stringify!(#ident))))); + quote! { + #[repr(#repr)] + #[derive(Copy, Clone, Debug, ::std::hash::Hash, Eq, PartialEq)] + pub enum #ident { + #(#variant_names),* + } - output.extend(quote!(pub enum #ident { - #variants - } + impl ::std::convert::TryFrom<#repr> for #ident { + type Error = ::memory::GuestValueError; + fn try_from(value: #repr) -> Result<#ident, ::memory::GuestValueError> { + match value { + #(#tryfrom_repr_cases),*, + _ => Err(::memory::GuestValueError::InvalidEnum(stringify!(#ident))), + } + } + } - impl ::std::convert::TryFrom<#repr> for #ident { - type Error = ::memory::GuestValueError; - fn try_from(value: #repr) -> Result<#ident, ::memory::GuestValueError> { - match value { - #tryfrom_repr_cases + impl From<#ident> for #repr { + fn from(e: #ident) -> #repr { + match e { + #(#to_repr_cases),* + } + } + } + + impl ::memory::GuestType for #ident { + fn size() -> u32 { + ::std::mem::size_of::<#repr>() as u32 + } + fn name() -> &'static str { + stringify!(#ident) + } + } + + impl ::memory::GuestTypeCopy for #ident { + fn read_val(src: ::memory::GuestPtr<#ident>) -> Result<#ident, ::memory::GuestValueError> { + use ::std::convert::TryInto; + let val = unsafe { ::std::ptr::read_unaligned(src.ptr() as *const #repr) }; + val.try_into() + } + fn write_val(val: #ident, dest: ::memory::GuestPtrMut<#ident>) { + let val: #repr = val.into(); + unsafe { + ::std::ptr::write_unaligned(dest.ptr_mut() as *mut #repr, val) + }; } } } - - impl From<#ident> for #repr { - fn from(e: #ident) -> #repr { - match e { - #to_repr_cases - } - } - } - - impl ::memory::GuestType for #ident { - fn size() -> u32 { - ::std::mem::size_of::<#repr>() as u32 - } - fn name() -> &'static str { - stringify!(#ident) - } - } - - impl ::memory::GuestTypeCopy for #ident { - fn read_val(src: ::memory::GuestPtr<#ident>) -> Result<#ident, ::memory::GuestValueError> { - use ::std::convert::TryInto; - let val = unsafe { ::std::ptr::read_unaligned(src.ptr() as *const #repr) }; - val.try_into() - } - fn write_val(val: #ident, dest: ::memory::GuestPtrMut<#ident>) { - let val: #repr = val.into(); - unsafe { - ::std::ptr::write_unaligned(dest.ptr_mut() as *mut #repr, val) - }; - } - } - )); - - output } fn define_builtin(names: &Names, name: &witx::Id, builtin: witx::BuiltinType) -> TokenStream { diff --git a/crates/memory/src/lib.rs b/crates/memory/src/lib.rs index 6b8edeb287..efc2317a47 100644 --- a/crates/memory/src/lib.rs +++ b/crates/memory/src/lib.rs @@ -4,5 +4,5 @@ mod memory; mod region; pub use guest_type::{GuestType, GuestTypeClone, GuestTypeCopy, GuestValueError}; -pub use memory::{GuestMemory, GuestPtr, GuestPtrMut}; +pub use memory::{GuestMemory, GuestPtr, GuestPtrMut, MemoryError}; pub use region::Region; diff --git a/src/lib.rs b/src/lib.rs index 227fdd8126..a146077675 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,21 @@ pub mod test { - pub struct WasiCtx {} // FIXME: parameterize macro on what ctx type is used here generate::from_witx!("test.witx"); + + pub struct WasiCtx {} // FIXME: parameterize macro on what ctx type is used here + + impl types::WitxErrorConversion for WasiCtx { + fn success_to_errno(&mut self) -> types::Errno { + types::Errno::Ok + } + fn memory_error_to_errno(&mut self, e: ::memory::MemoryError) -> types::Errno { + eprintln!("memory error: {:?}", e); + types::Errno::InvalidArg + } + fn value_error_to_errno(&mut self, e: ::memory::GuestValueError) -> types::Errno { + eprintln!("guest value error: {:?}", e); + types::Errno::InvalidArg + } + } } /* pub mod wasi { diff --git a/test.witx b/test.witx index 1750ec3d15..940f8594d8 100644 --- a/test.witx +++ b/test.witx @@ -2,6 +2,8 @@ (typename $errno (enum u32 + $ok + $invalid_arg $dont_want_to $physically_unable $picket_line)) From 7cc0073a3e569df06bedd4018b4d3b3c7bd93259 Mon Sep 17 00:00:00 2001 From: Pat Hickey Date: Wed, 22 Jan 2020 21:03:24 -0800 Subject: [PATCH 15/86] hmm my first idea was bad but not too bad --- crates/generate/src/errors.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/crates/generate/src/errors.rs b/crates/generate/src/errors.rs index d1b4bbfbc3..2dc2f243b0 100644 --- a/crates/generate/src/errors.rs +++ b/crates/generate/src/errors.rs @@ -7,6 +7,18 @@ use witx::{Document, TypeRef}; /// The context struct needs to implement a trait for converting memory and value errors into the /// witx doc's error types. +/// +// XXX im rethinking this. maybe each error type should impl +// pub trait WitxErrorType { +// type Context; +// fn is_success(&self) -> bool; +// fn from_memory_error(memory_error: MemoryError, ctx: &mut Context) -> Self; +// fn from_value_error(value_error: GuestValueError, ctx: &mut Context) -> Self; +// } +// +// where Context is mapped to their wasi ctx. +// It seems less "magic" to leave that impl up to the user, and the error message may be simpler? +// pub fn define_error_trait(names: &Names, doc: &Document) -> TokenStream { // All non-anonymous first return types are used to pass errors. let error_typenames = doc From cb24fd97c0f1a09533c55a66d54097acc0206317 Mon Sep 17 00:00:00 2001 From: Pat Hickey Date: Thu, 23 Jan 2020 11:21:04 -0800 Subject: [PATCH 16/86] better error trait design --- crates/generate/src/errors.rs | 57 --------------------------------- crates/generate/src/lib.rs | 5 --- crates/memory/src/guest_type.rs | 9 +++++- crates/memory/src/lib.rs | 2 +- src/lib.rs | 28 +++++++++++----- 5 files changed, 29 insertions(+), 72 deletions(-) delete mode 100644 crates/generate/src/errors.rs diff --git a/crates/generate/src/errors.rs b/crates/generate/src/errors.rs deleted file mode 100644 index 2dc2f243b0..0000000000 --- a/crates/generate/src/errors.rs +++ /dev/null @@ -1,57 +0,0 @@ -use crate::names::Names; -use heck::SnakeCase; -use proc_macro2::TokenStream; -use quote::{format_ident, quote}; -use std::collections::HashSet; -use witx::{Document, TypeRef}; - -/// The context struct needs to implement a trait for converting memory and value errors into the -/// witx doc's error types. -/// -// XXX im rethinking this. maybe each error type should impl -// pub trait WitxErrorType { -// type Context; -// fn is_success(&self) -> bool; -// fn from_memory_error(memory_error: MemoryError, ctx: &mut Context) -> Self; -// fn from_value_error(value_error: GuestValueError, ctx: &mut Context) -> Self; -// } -// -// where Context is mapped to their wasi ctx. -// It seems less "magic" to leave that impl up to the user, and the error message may be simpler? -// -pub fn define_error_trait(names: &Names, doc: &Document) -> TokenStream { - // All non-anonymous first return types are used to pass errors. - let error_typenames = doc - .modules() - .flat_map(|m| { - m.funcs() - .filter_map(|f| { - f.results.get(0).and_then(|r| match &r.tref { - TypeRef::Name(nt) => Some(nt.name.clone()), - _ => None, - }) - }) - .collect::>() - }) - .collect::>(); - - let methods = error_typenames.iter().map(|typename| { - let tname = names.type_(typename); - let methodfragment = typename.as_str().to_snake_case(); - let success = format_ident!("success_to_{}", methodfragment); - let memory_error = format_ident!("memory_error_to_{}", methodfragment); - let value_error = format_ident!("value_error_to_{}", methodfragment); - - quote! { - fn #success(&mut self) -> #tname; - fn #memory_error(&mut self, err: ::memory::MemoryError) -> #tname; - fn #value_error(&mut self, err: ::memory::GuestValueError) -> #tname; - } - }); - - quote!( - pub trait WitxErrorConversion { - #(#methods)* - } - ) -} diff --git a/crates/generate/src/lib.rs b/crates/generate/src/lib.rs index 830e92e900..76e1014c7e 100644 --- a/crates/generate/src/lib.rs +++ b/crates/generate/src/lib.rs @@ -1,6 +1,5 @@ extern crate proc_macro; -mod errors; mod funcs; mod names; mod parse; @@ -10,7 +9,6 @@ use proc_macro::TokenStream; use proc_macro2::TokenStream as TokenStream2; use quote::quote; -use errors::define_error_trait; use funcs::define_func; use names::Names; use types::define_datatype; @@ -39,12 +37,9 @@ pub fn from_witx(args: TokenStream) -> TokenStream { ) }); - let error_trait = define_error_trait(&names, &doc); - TokenStream::from(quote!( mod types { #(#types)* - #error_trait } #(#modules)* )) diff --git a/crates/memory/src/guest_type.rs b/crates/memory/src/guest_type.rs index 820e3b28f2..933b11c33e 100644 --- a/crates/memory/src/guest_type.rs +++ b/crates/memory/src/guest_type.rs @@ -1,4 +1,4 @@ -use crate::{GuestPtr, GuestPtrMut}; +use crate::{GuestPtr, GuestPtrMut, MemoryError}; use thiserror::Error; pub trait GuestType: Sized { @@ -66,3 +66,10 @@ macro_rules! builtin_copy { // These definitions correspond to all the witx BuiltinType variants that are Copy: builtin_copy!(u8, i8, u16, i16, u32, i32, u64, i64, f32, f64, usize, char); + +pub trait GuestError { + type Context; + fn is_success(&self) -> bool; + fn from_memory_error(memory_error: MemoryError, ctx: &mut Self::Context) -> Self; + fn from_value_error(value_error: GuestValueError, ctx: &mut Self::Context) -> Self; +} diff --git a/crates/memory/src/lib.rs b/crates/memory/src/lib.rs index efc2317a47..872c556b15 100644 --- a/crates/memory/src/lib.rs +++ b/crates/memory/src/lib.rs @@ -3,6 +3,6 @@ mod guest_type; mod memory; mod region; -pub use guest_type::{GuestType, GuestTypeClone, GuestTypeCopy, GuestValueError}; +pub use guest_type::{GuestError, GuestType, GuestTypeClone, GuestTypeCopy, GuestValueError}; pub use memory::{GuestMemory, GuestPtr, GuestPtrMut, MemoryError}; pub use region::Region; diff --git a/src/lib.rs b/src/lib.rs index a146077675..cdb98b3dd4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,18 +1,30 @@ pub mod test { + // FIXME: parameterize macro on what ctx type is used here generate::from_witx!("test.witx"); - pub struct WasiCtx {} // FIXME: parameterize macro on what ctx type is used here + pub struct WasiCtx { + mem_errors: Vec<::memory::MemoryError>, + value_errors: Vec<::memory::GuestValueError>, + } - impl types::WitxErrorConversion for WasiCtx { - fn success_to_errno(&mut self) -> types::Errno { - types::Errno::Ok + // Errno is used as a first return value in the functions above, therefore + // it must implement GuestError with type Context = WasiCtx. + // The context type should let you do logging or debugging or whatever you need + // with these errors. We just push them to vecs. + impl ::memory::GuestError for types::Errno { + type Context = WasiCtx; + fn is_success(&self) -> bool { + match self { + types::Errno::Ok => true, + _ => false, + } } - fn memory_error_to_errno(&mut self, e: ::memory::MemoryError) -> types::Errno { - eprintln!("memory error: {:?}", e); + fn from_memory_error(e: ::memory::MemoryError, ctx: &mut WasiCtx) -> types::Errno { + ctx.mem_errors.push(e); types::Errno::InvalidArg } - fn value_error_to_errno(&mut self, e: ::memory::GuestValueError) -> types::Errno { - eprintln!("guest value error: {:?}", e); + fn from_value_error(e: ::memory::GuestValueError, ctx: &mut WasiCtx) -> types::Errno { + ctx.value_errors.push(e); types::Errno::InvalidArg } } From b4f21752b045cea26d46cae215679f55b28ec5df Mon Sep 17 00:00:00 2001 From: Pat Hickey Date: Thu, 23 Jan 2020 12:46:57 -0800 Subject: [PATCH 17/86] generate a module trait and call it --- crates/generate/src/funcs.rs | 72 +++++++++++++++++++++++++---- crates/generate/src/lib.rs | 8 +++- crates/generate/src/module_trait.rs | 33 +++++++++++++ crates/generate/src/names.rs | 5 +- crates/generate/src/types.rs | 22 +++++++++ crates/memory/src/guest_type.rs | 2 +- src/lib.rs | 14 ++++-- 7 files changed, 139 insertions(+), 17 deletions(-) create mode 100644 crates/generate/src/module_trait.rs diff --git a/crates/generate/src/funcs.rs b/crates/generate/src/funcs.rs index bfe720f2d9..255e7a90e9 100644 --- a/crates/generate/src/funcs.rs +++ b/crates/generate/src/funcs.rs @@ -1,4 +1,4 @@ -use proc_macro2::TokenStream; +use proc_macro2::{Ident, TokenStream}; use quote::quote; use crate::names::Names; @@ -6,10 +6,6 @@ use crate::names::Names; // FIXME need to template what argument is required to an import function - some context // struct (e.g. WasiCtx) should be provided at the invocation of the `gen` proc macro. // -// Additionally - need to template how to transform GuestValueError and MemoryError into -// the error type returned! From impl is a good start, but what if we want to log -// a more informative error? Maybe the conversion should be a (generated, for each by-value first -// return type) trait impled by WasiCtx? pub fn define_func(names: &Names, func: &witx::InterfaceFunc) -> TokenStream { let ident = names.func(&func.name); @@ -38,7 +34,7 @@ pub fn define_func(names: &Names, func: &witx::InterfaceFunc) -> TokenStream { .chain(func.results.iter().skip(1)) .map(arg_signature); let abi_args = quote!( - wasi_ctx: &mut WasiCtx, memory: GuestMemory, + ctx: &mut WasiCtx, memory: ::memory::GuestMemory, #(#params),* ); let abi_ret = if let Some(first_result) = func.results.get(0) { @@ -47,10 +43,70 @@ pub fn define_func(names: &Names, func: &witx::InterfaceFunc) -> TokenStream { _ => unreachable!("first result should always be passed by value"), } } else if func.noreturn { - quote!(!) + // Ideally we would return `quote!(!)` here, but, we'd have to change + // the error handling logic in all the marshalling code to never return, + // and instead provide some other way to bail to the context... + // noreturn func + unimplemented!("noreturn funcs not supported yet!") } else { quote!(()) }; - quote!(pub fn #ident(#abi_args) -> #abi_ret { unimplemented!() }) + let err_type = func + .results + .get(0) + .map(|res| names.type_ref(&res.tref)) + .unwrap_or_else(|| abi_ret.clone()); + let err_val = func + .results + .get(0) + .map(|_res| quote!(#abi_ret::from(e))) + .unwrap_or_else(|| quote!(())); + + let marshal_args = func + .params + .iter() + .map(|param| match param.tref.type_().passed_by() { + witx::TypePassedBy::Value(_atom) => { + // FIXME atom -> param.tref can be either an `as` conversion, or `try_from` + let name = names.func_param(¶m.name); + let interface_type = names.type_ref(¶m.tref); + quote!( let #name = #name as #interface_type; ) + } + _ => unimplemented!(), + }); + let trait_args = func + .params + .iter() + .map(|param| names.func_param(¶m.name)); + + let trait_rets = func + .results + .iter() + .skip(1) + .map(|result| names.func_param(&result.name)) + .collect::>(); + let (trait_rets, trait_bindings) = if trait_rets.is_empty() { + (quote!({}), quote!(_)) + } else { + let tuple = quote!((#(#trait_rets),*)); + (tuple.clone(), tuple) + }; + + let marshal_rets = func + .results + .iter() + .skip(1) + .map(|_result| quote! { unimplemented!("convert result..."); }); + + quote!(pub fn #ident(#abi_args) -> #abi_ret { + #(#marshal_args)* + let #trait_bindings = match ctx.#ident(#(#trait_args),*) { + Ok(#trait_bindings) => #trait_rets, + Err(e) => { return #err_val; }, + }; + #(#marshal_rets)* + let success:#err_type = ::memory::GuestError::success(); + #abi_ret::from(success) + }) } diff --git a/crates/generate/src/lib.rs b/crates/generate/src/lib.rs index 76e1014c7e..49d9289d58 100644 --- a/crates/generate/src/lib.rs +++ b/crates/generate/src/lib.rs @@ -1,6 +1,7 @@ extern crate proc_macro; mod funcs; +mod module_trait; mod names; mod parse; mod types; @@ -10,6 +11,7 @@ use proc_macro2::TokenStream as TokenStream2; use quote::quote; use funcs::define_func; +use module_trait::define_module_trait; use names::Names; use types::define_datatype; @@ -27,12 +29,14 @@ pub fn from_witx(args: TokenStream) -> TokenStream { let modules = doc.modules().map(|module| { let modname = names.module(&module.name); let fs = module.funcs().map(|f| define_func(&names, &f)); + let modtrait = define_module_trait(&names, &module); quote!( mod #modname { - use super::*; + use super::WasiCtx; use super::types::*; - use memory::*; #(#fs)* + + #modtrait } ) }); diff --git a/crates/generate/src/module_trait.rs b/crates/generate/src/module_trait.rs new file mode 100644 index 0000000000..a25addd710 --- /dev/null +++ b/crates/generate/src/module_trait.rs @@ -0,0 +1,33 @@ +use proc_macro2::TokenStream; +use quote::quote; + +use crate::names::Names; +use witx::Module; + +pub fn define_module_trait(names: &Names, m: &Module) -> TokenStream { + let traitname = names.trait_name(&m.name); + let traitmethods = m.funcs().map(|f| { + let funcname = names.func(&f.name); + let args = f.params.iter().map(|arg| { + let arg_name = names.func_param(&arg.name); + let arg_type = names.type_ref(&arg.tref); + quote!(#arg_name: #arg_type) + }); + let rets = f + .results + .iter() + .skip(1) + .map(|ret| names.type_ref(&ret.tref)); + let err = f + .results + .get(0) + .map(|err_result| names.type_ref(&err_result.tref)) + .unwrap_or(quote!(())); + quote!(fn #funcname(&mut self, #(#args),*) -> Result<(#(#rets),*), #err>;) + }); + quote! { + pub trait #traitname { + #(#traitmethods)* + } + } +} diff --git a/crates/generate/src/names.rs b/crates/generate/src/names.rs index bd296ac88e..78cec1e0b8 100644 --- a/crates/generate/src/names.rs +++ b/crates/generate/src/names.rs @@ -42,7 +42,6 @@ impl Names { } } - #[allow(unused)] pub fn type_ref(&self, tref: &TypeRef) -> TokenStream { match tref { TypeRef::Name(nt) => { @@ -69,6 +68,10 @@ impl Names { format_ident!("{}", id.as_str().to_snake_case()) } + pub fn trait_name(&self, id: &Id) -> Ident { + format_ident!("{}", id.as_str().to_camel_case()) + } + pub fn func(&self, id: &Id) -> Ident { format_ident!("{}", id.as_str().to_snake_case()) } diff --git a/crates/generate/src/types.rs b/crates/generate/src/types.rs index 144b11f2e1..7d34f9688d 100644 --- a/crates/generate/src/types.rs +++ b/crates/generate/src/types.rs @@ -32,6 +32,7 @@ fn define_enum(names: &Names, name: &witx::Id, e: &witx::EnumDatatype) -> TokenS let ident = names.type_(&name); let repr = int_repr_tokens(e.repr); + let signed_repr = int_signed_repr_tokens(e.repr); let variant_names = e.variants.iter().map(|v| names.enum_variant(&v.name)); let tryfrom_repr_cases = e.variants.iter().enumerate().map(|(n, v)| { @@ -62,6 +63,13 @@ fn define_enum(names: &Names, name: &witx::Id, e: &witx::EnumDatatype) -> TokenS } } + impl ::std::convert::TryFrom<#signed_repr> for #ident { + type Error = ::memory::GuestValueError; + fn try_from(value: #signed_repr) -> Result<#ident, ::memory::GuestValueError> { + #ident::try_from(value as #repr) + } + } + impl From<#ident> for #repr { fn from(e: #ident) -> #repr { match e { @@ -70,6 +78,12 @@ fn define_enum(names: &Names, name: &witx::Id, e: &witx::EnumDatatype) -> TokenS } } + impl From<#ident> for #signed_repr { + fn from(e: #ident) -> #signed_repr { + #repr::from(e) as #signed_repr + } + } + impl ::memory::GuestType for #ident { fn size() -> u32 { ::std::mem::size_of::<#repr>() as u32 @@ -109,3 +123,11 @@ fn int_repr_tokens(int_repr: witx::IntRepr) -> TokenStream { witx::IntRepr::U64 => quote!(u64), } } +fn int_signed_repr_tokens(int_repr: witx::IntRepr) -> TokenStream { + match int_repr { + witx::IntRepr::U8 => quote!(i8), + witx::IntRepr::U16 => quote!(i16), + witx::IntRepr::U32 => quote!(i32), + witx::IntRepr::U64 => quote!(i64), + } +} diff --git a/crates/memory/src/guest_type.rs b/crates/memory/src/guest_type.rs index 933b11c33e..9410e6054d 100644 --- a/crates/memory/src/guest_type.rs +++ b/crates/memory/src/guest_type.rs @@ -69,7 +69,7 @@ builtin_copy!(u8, i8, u16, i16, u32, i32, u64, i64, f32, f64, usize, char); pub trait GuestError { type Context; - fn is_success(&self) -> bool; + fn success() -> Self; fn from_memory_error(memory_error: MemoryError, ctx: &mut Self::Context) -> Self; fn from_value_error(value_error: GuestValueError, ctx: &mut Self::Context) -> Self; } diff --git a/src/lib.rs b/src/lib.rs index cdb98b3dd4..79e3ebc7c7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,17 +7,21 @@ pub mod test { value_errors: Vec<::memory::GuestValueError>, } + impl foo::Foo for WasiCtx { + fn bar(&mut self, an_int: u32, an_float: f32) -> Result<(), types::Errno> { + println!("BAR: {} {}", an_int, an_float); + Ok(()) + } + } + // Errno is used as a first return value in the functions above, therefore // it must implement GuestError with type Context = WasiCtx. // The context type should let you do logging or debugging or whatever you need // with these errors. We just push them to vecs. impl ::memory::GuestError for types::Errno { type Context = WasiCtx; - fn is_success(&self) -> bool { - match self { - types::Errno::Ok => true, - _ => false, - } + fn success() -> types::Errno { + types::Errno::Ok } fn from_memory_error(e: ::memory::MemoryError, ctx: &mut WasiCtx) -> types::Errno { ctx.mem_errors.push(e); From b6d342ccb5691ac0f34b8165d28d3a180f481f4d Mon Sep 17 00:00:00 2001 From: Pat Hickey Date: Thu, 23 Jan 2020 17:55:59 -0800 Subject: [PATCH 18/86] some progress on marshalling args --- crates/generate/src/funcs.rs | 89 +++++++++++++++++++++++++++++------- crates/generate/src/types.rs | 10 ++-- src/lib.rs | 4 ++ test.witx | 9 ++++ 4 files changed, 89 insertions(+), 23 deletions(-) diff --git a/crates/generate/src/funcs.rs b/crates/generate/src/funcs.rs index 255e7a90e9..8db2755be7 100644 --- a/crates/generate/src/funcs.rs +++ b/crates/generate/src/funcs.rs @@ -1,4 +1,4 @@ -use proc_macro2::{Ident, TokenStream}; +use proc_macro2::TokenStream; use quote::quote; use crate::names::Names; @@ -66,29 +66,20 @@ pub fn define_func(names: &Names, func: &witx::InterfaceFunc) -> TokenStream { let marshal_args = func .params .iter() - .map(|param| match param.tref.type_().passed_by() { - witx::TypePassedBy::Value(_atom) => { - // FIXME atom -> param.tref can be either an `as` conversion, or `try_from` - let name = names.func_param(¶m.name); - let interface_type = names.type_ref(¶m.tref); - quote!( let #name = #name as #interface_type; ) - } - _ => unimplemented!(), - }); + .map(|p| marshal_arg(names, p, func.results.get(0).map(|r| &r.tref))); let trait_args = func .params .iter() .map(|param| names.func_param(¶m.name)); - let trait_rets = func - .results - .iter() - .skip(1) - .map(|result| names.func_param(&result.name)) - .collect::>(); - let (trait_rets, trait_bindings) = if trait_rets.is_empty() { + let (trait_rets, trait_bindings) = if func.results.len() < 2 { (quote!({}), quote!(_)) } else { + let trait_rets = func + .results + .iter() + .skip(1) + .map(|result| names.func_param(&result.name)); let tuple = quote!((#(#trait_rets),*)); (tuple.clone(), tuple) }; @@ -110,3 +101,67 @@ pub fn define_func(names: &Names, func: &witx::InterfaceFunc) -> TokenStream { #abi_ret::from(success) }) } + +fn marshal_arg( + names: &Names, + param: &witx::InterfaceFuncParam, + error_type: Option<&witx::TypeRef>, +) -> TokenStream { + let tref = ¶m.tref; + let interface_typename = names.type_ref(&tref); + let name = names.func_param(¶m.name); + + let value_error_handling = if let Some(tref) = error_type { + let abi_ret = match tref.type_().passed_by() { + witx::TypePassedBy::Value(atom) => names.atom_type(atom), + _ => unreachable!("err should always be passed by value"), + }; + let err_typename = names.type_ref(&tref); + quote! { + let err: #err_typename = ::memory::GuestError::from_memory_error(e, ctx); + return #abi_ret::from(err); + } + } else { + quote! { + panic!("memory error: {:?}", e) + } + }; + + let try_into_conversion = quote! { + use ::std::convert::TryInto; + let #name: #interface_typename = match #name.try_into() { + Ok(a) => a, + Err(e) => { + #value_error_handling + } + }; + }; + + match &*tref.type_() { + witx::Type::Enum(_e) => try_into_conversion, + witx::Type::Builtin(b) => match b { + witx::BuiltinType::U8 | witx::BuiltinType::U16 | witx::BuiltinType::Char8 => { + try_into_conversion + } + witx::BuiltinType::S8 | witx::BuiltinType::S16 => quote! { + let #name: #interface_typename = match (#name as i32).try_into() { + Ok(a) => a, + Err(e) => { + #value_error_handling + } + } + }, + witx::BuiltinType::U32 + | witx::BuiltinType::S32 + | witx::BuiltinType::U64 + | witx::BuiltinType::S64 + | witx::BuiltinType::USize + | witx::BuiltinType::F32 + | witx::BuiltinType::F64 => quote! { + let #name = #name as #interface_typename; + }, + witx::BuiltinType::String => unimplemented!("string types unimplemented"), + }, + _ => unimplemented!("only enums and builtins so far"), + } +} diff --git a/crates/generate/src/types.rs b/crates/generate/src/types.rs index 7d34f9688d..66df3583fa 100644 --- a/crates/generate/src/types.rs +++ b/crates/generate/src/types.rs @@ -37,13 +37,11 @@ fn define_enum(names: &Names, name: &witx::Id, e: &witx::EnumDatatype) -> TokenS let variant_names = e.variants.iter().map(|v| names.enum_variant(&v.name)); let tryfrom_repr_cases = e.variants.iter().enumerate().map(|(n, v)| { let variant_name = names.enum_variant(&v.name); - let n = n as u32; quote!(#n => Ok(#ident::#variant_name)) }); let to_repr_cases = e.variants.iter().enumerate().map(|(n, v)| { let variant_name = names.enum_variant(&v.name); - let n = n as u32; - quote!(#ident::#variant_name => #n) + quote!(#ident::#variant_name => #n as #repr) }); quote! { @@ -56,14 +54,14 @@ fn define_enum(names: &Names, name: &witx::Id, e: &witx::EnumDatatype) -> TokenS impl ::std::convert::TryFrom<#repr> for #ident { type Error = ::memory::GuestValueError; fn try_from(value: #repr) -> Result<#ident, ::memory::GuestValueError> { - match value { + match value as usize { #(#tryfrom_repr_cases),*, _ => Err(::memory::GuestValueError::InvalidEnum(stringify!(#ident))), } } } - impl ::std::convert::TryFrom<#signed_repr> for #ident { + impl ::std::convert::TryFrom<#signed_repr> for #ident { // XXX this one should always be from i32/i64 (abi size) type Error = ::memory::GuestValueError; fn try_from(value: #signed_repr) -> Result<#ident, ::memory::GuestValueError> { #ident::try_from(value as #repr) @@ -78,7 +76,7 @@ fn define_enum(names: &Names, name: &witx::Id, e: &witx::EnumDatatype) -> TokenS } } - impl From<#ident> for #signed_repr { + impl From<#ident> for #signed_repr { // XXX this should be to i32 or i64 (abi size) fn from(e: #ident) -> #signed_repr { #repr::from(e) as #signed_repr } diff --git a/src/lib.rs b/src/lib.rs index 79e3ebc7c7..f6500df8c0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -12,6 +12,10 @@ pub mod test { println!("BAR: {} {}", an_int, an_float); Ok(()) } + fn baz(&mut self, excuse: types::Excuse) -> Result<(), types::Errno> { + println!("BAZ: {:?}", excuse); + Ok(()) + } } // Errno is used as a first return value in the functions above, therefore diff --git a/test.witx b/test.witx index 940f8594d8..9611b9203b 100644 --- a/test.witx +++ b/test.witx @@ -8,9 +8,18 @@ $physically_unable $picket_line)) +(typename $excuse + (enum u8 + $dog_ate + $traffic + $sleeping)) + (module $foo (@interface func (export "bar") (param $an_int u32) (param $an_float f32) (result $error $errno)) + (@interface func (export "baz") + (param $an_excuse $excuse) + (result $error $errno)) ) From 42eca19b503c0fcfb237a92274689937154721ef Mon Sep 17 00:00:00 2001 From: Pat Hickey Date: Fri, 24 Jan 2020 15:12:51 -0800 Subject: [PATCH 19/86] types: make the tryfrom/to impl for the abi type, not the signed variant --- crates/generate/src/types.rs | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/crates/generate/src/types.rs b/crates/generate/src/types.rs index 66df3583fa..59b99507a5 100644 --- a/crates/generate/src/types.rs +++ b/crates/generate/src/types.rs @@ -32,7 +32,10 @@ fn define_enum(names: &Names, name: &witx::Id, e: &witx::EnumDatatype) -> TokenS let ident = names.type_(&name); let repr = int_repr_tokens(e.repr); - let signed_repr = int_signed_repr_tokens(e.repr); + let abi_repr = atom_token(match e.repr { + witx::IntRepr::U8 | witx::IntRepr::U16 | witx::IntRepr::U32 => witx::AtomType::I32, + witx::IntRepr::U64 => witx::AtomType::I64, + }); let variant_names = e.variants.iter().map(|v| names.enum_variant(&v.name)); let tryfrom_repr_cases = e.variants.iter().enumerate().map(|(n, v)| { @@ -61,9 +64,9 @@ fn define_enum(names: &Names, name: &witx::Id, e: &witx::EnumDatatype) -> TokenS } } - impl ::std::convert::TryFrom<#signed_repr> for #ident { // XXX this one should always be from i32/i64 (abi size) + impl ::std::convert::TryFrom<#abi_repr> for #ident { type Error = ::memory::GuestValueError; - fn try_from(value: #signed_repr) -> Result<#ident, ::memory::GuestValueError> { + fn try_from(value: #abi_repr) -> Result<#ident, ::memory::GuestValueError> { #ident::try_from(value as #repr) } } @@ -76,9 +79,9 @@ fn define_enum(names: &Names, name: &witx::Id, e: &witx::EnumDatatype) -> TokenS } } - impl From<#ident> for #signed_repr { // XXX this should be to i32 or i64 (abi size) - fn from(e: #ident) -> #signed_repr { - #repr::from(e) as #signed_repr + impl From<#ident> for #abi_repr { + fn from(e: #ident) -> #abi_repr { + #repr::from(e) as #abi_repr } } @@ -121,11 +124,11 @@ fn int_repr_tokens(int_repr: witx::IntRepr) -> TokenStream { witx::IntRepr::U64 => quote!(u64), } } -fn int_signed_repr_tokens(int_repr: witx::IntRepr) -> TokenStream { - match int_repr { - witx::IntRepr::U8 => quote!(i8), - witx::IntRepr::U16 => quote!(i16), - witx::IntRepr::U32 => quote!(i32), - witx::IntRepr::U64 => quote!(i64), +fn atom_token(atom: witx::AtomType) -> TokenStream { + match atom { + witx::AtomType::I32 => quote!(i32), + witx::AtomType::I64 => quote!(i64), + witx::AtomType::F32 => quote!(f32), + witx::AtomType::F64 => quote!(f64), } } From e789033651d67d42528044acc9c41adcf8d24e67 Mon Sep 17 00:00:00 2001 From: Pat Hickey Date: Fri, 24 Jan 2020 15:16:09 -0800 Subject: [PATCH 20/86] bugfix --- crates/generate/src/funcs.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/generate/src/funcs.rs b/crates/generate/src/funcs.rs index 8db2755be7..ccf7365eab 100644 --- a/crates/generate/src/funcs.rs +++ b/crates/generate/src/funcs.rs @@ -118,7 +118,7 @@ fn marshal_arg( }; let err_typename = names.type_ref(&tref); quote! { - let err: #err_typename = ::memory::GuestError::from_memory_error(e, ctx); + let err: #err_typename = ::memory::GuestError::from_value_error(e, ctx); return #abi_ret::from(err); } } else { From 020778b7da6acf2363f5eec67cca556d8fa58304 Mon Sep 17 00:00:00 2001 From: Pat Hickey Date: Fri, 24 Jan 2020 18:08:37 -0800 Subject: [PATCH 21/86] we can handle one layer of pointers! --- crates/generate/src/funcs.rs | 54 +++++++++++++++++++------- crates/generate/src/names.rs | 8 ++++ crates/generate/src/types.rs | 4 +- crates/memory/src/borrow.rs | 67 ++++++++++++++++++--------------- crates/memory/src/guest_type.rs | 18 ++++----- crates/memory/src/lib.rs | 2 +- crates/memory/src/memory.rs | 61 ++++++++++++++++-------------- src/lib.rs | 25 +++++++++++- test.witx | 2 + 9 files changed, 155 insertions(+), 86 deletions(-) diff --git a/crates/generate/src/funcs.rs b/crates/generate/src/funcs.rs index ccf7365eab..de2c7413da 100644 --- a/crates/generate/src/funcs.rs +++ b/crates/generate/src/funcs.rs @@ -111,21 +111,25 @@ fn marshal_arg( let interface_typename = names.type_ref(&tref); let name = names.func_param(¶m.name); - let value_error_handling = if let Some(tref) = error_type { - let abi_ret = match tref.type_().passed_by() { - witx::TypePassedBy::Value(atom) => names.atom_type(atom), - _ => unreachable!("err should always be passed by value"), - }; - let err_typename = names.type_ref(&tref); - quote! { - let err: #err_typename = ::memory::GuestError::from_value_error(e, ctx); - return #abi_ret::from(err); - } - } else { - quote! { - panic!("memory error: {:?}", e) + let error_handling = |method| -> TokenStream { + if let Some(tref) = error_type { + let abi_ret = match tref.type_().passed_by() { + witx::TypePassedBy::Value(atom) => names.atom_type(atom), + _ => unreachable!("err should always be passed by value"), + }; + let err_typename = names.type_ref(&tref); + quote! { + let err: #err_typename = ::memory::GuestError::#method(e, ctx); + return #abi_ret::from(err); + } + } else { + quote! { + panic!("error: {:?}", e) + } } }; + let value_error_handling = error_handling(quote!(from_value_error)); + let memory_error_handling = error_handling(quote!(from_memory_error)); let try_into_conversion = quote! { use ::std::convert::TryInto; @@ -162,6 +166,28 @@ fn marshal_arg( }, witx::BuiltinType::String => unimplemented!("string types unimplemented"), }, - _ => unimplemented!("only enums and builtins so far"), + witx::Type::Pointer(pointee) => { + let pointee_type = names.type_ref(pointee); + quote! { + let #name = match memory.ptr_mut::<#pointee_type>(#name as u32) { + Ok(p) => p, + Err(e) => { + #memory_error_handling + } + }; + } + } + witx::Type::ConstPointer(pointee) => { + let pointee_type = names.type_ref(pointee); + quote! { + let #name = match memory.ptr::<#pointee_type>(#name as u32) { + Ok(p) => p, + Err(e) => { + #memory_error_handling + } + }; + } + } + _ => unimplemented!("argument type marshalling"), } } diff --git a/crates/generate/src/names.rs b/crates/generate/src/names.rs index 78cec1e0b8..6465e7f832 100644 --- a/crates/generate/src/names.rs +++ b/crates/generate/src/names.rs @@ -50,6 +50,14 @@ impl Names { } TypeRef::Value(ty) => match &**ty { witx::Type::Builtin(builtin) => self.builtin_type(*builtin), + witx::Type::Pointer(pointee) => { + let pointee_type = self.type_ref(&pointee); + quote!(::memory::GuestPtrMut<#pointee_type>) + } + witx::Type::ConstPointer(pointee) => { + let pointee_type = self.type_ref(&pointee); + quote!(::memory::GuestPtr<#pointee_type>) + } _ => unimplemented!("anonymous type ref"), }, } diff --git a/crates/generate/src/types.rs b/crates/generate/src/types.rs index 59b99507a5..a03b4003a2 100644 --- a/crates/generate/src/types.rs +++ b/crates/generate/src/types.rs @@ -95,12 +95,12 @@ fn define_enum(names: &Names, name: &witx::Id, e: &witx::EnumDatatype) -> TokenS } impl ::memory::GuestTypeCopy for #ident { - fn read_val(src: ::memory::GuestPtr<#ident>) -> Result<#ident, ::memory::GuestValueError> { + fn read_val>(src: &P) -> Result<#ident, ::memory::GuestValueError> { use ::std::convert::TryInto; let val = unsafe { ::std::ptr::read_unaligned(src.ptr() as *const #repr) }; val.try_into() } - fn write_val(val: #ident, dest: ::memory::GuestPtrMut<#ident>) { + fn write_val(val: #ident, dest: &::memory::GuestPtrMut<#ident>) { let val: #repr = val.into(); unsafe { ::std::ptr::write_unaligned(dest.ptr_mut() as *mut #repr, val) diff --git a/crates/memory/src/borrow.rs b/crates/memory/src/borrow.rs index 50d5656f53..c691888db9 100644 --- a/crates/memory/src/borrow.rs +++ b/crates/memory/src/borrow.rs @@ -1,59 +1,66 @@ +use std::collections::HashMap; + use crate::region::Region; +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +pub struct BorrowHandle(usize); + pub struct GuestBorrows { - immutable: Vec, - mutable: Vec, + immutable: HashMap, + mutable: HashMap, + next_handle: BorrowHandle, } impl GuestBorrows { pub fn new() -> Self { GuestBorrows { - immutable: Vec::new(), - mutable: Vec::new(), + immutable: HashMap::new(), + mutable: HashMap::new(), + next_handle: BorrowHandle(0), } } fn is_borrowed_immut(&self, r: Region) -> bool { - !self.immutable.iter().all(|b| !b.overlaps(r)) + !self.immutable.values().all(|b| !b.overlaps(r)) } fn is_borrowed_mut(&self, r: Region) -> bool { - !self.mutable.iter().all(|b| !b.overlaps(r)) + !self.mutable.values().all(|b| !b.overlaps(r)) } - pub fn borrow_immut(&mut self, r: Region) -> bool { + fn new_handle(&mut self) -> BorrowHandle { + let h = self.next_handle; + self.next_handle = BorrowHandle(h.0 + 1); + h + } + + pub fn borrow_immut(&mut self, r: Region) -> Option { if self.is_borrowed_mut(r) { - return false; + return None; } - self.immutable.push(r); - true + let h = self.new_handle(); + self.immutable.insert(h, r); + Some(h) } - pub fn unborrow_immut(&mut self, r: Region) { - let (ix, _) = self - .immutable - .iter() - .enumerate() - .find(|(_, reg)| r == **reg) - .expect("region exists in borrows"); - self.immutable.remove(ix); + pub fn unborrow_immut(&mut self, h: BorrowHandle) { + self.immutable + .remove(&h) + .expect("handle exists in immutable borrows"); } - pub fn borrow_mut(&mut self, r: Region) -> bool { + pub fn borrow_mut(&mut self, r: Region) -> Option { if self.is_borrowed_immut(r) || self.is_borrowed_mut(r) { - return false; + return None; } - self.mutable.push(r); - true + let h = self.new_handle(); + self.mutable.insert(h, r); + Some(h) } - pub fn unborrow_mut(&mut self, r: Region) { - let (ix, _) = self - .mutable - .iter() - .enumerate() - .find(|(_, reg)| r == **reg) - .expect("region exists in borrows"); - self.mutable.remove(ix); + pub fn unborrow_mut(&mut self, h: BorrowHandle) { + self.mutable + .remove(&h) + .expect("handle exists in mutable borrows"); } } diff --git a/crates/memory/src/guest_type.rs b/crates/memory/src/guest_type.rs index 9410e6054d..324ca5bf09 100644 --- a/crates/memory/src/guest_type.rs +++ b/crates/memory/src/guest_type.rs @@ -1,4 +1,4 @@ -use crate::{GuestPtr, GuestPtrMut, MemoryError}; +use crate::{GuestPtrMut, GuestPtrRead, MemoryError}; use thiserror::Error; pub trait GuestType: Sized { @@ -13,25 +13,25 @@ pub enum GuestValueError { } pub trait GuestTypeCopy: GuestType + Copy { - fn read_val(src: GuestPtr) -> Result; - fn write_val(val: Self, dest: GuestPtrMut); + fn read_val>(src: &P) -> Result; + fn write_val(val: Self, dest: &GuestPtrMut); } pub trait GuestTypeClone: GuestType + Clone { - fn read_ref(src: GuestPtr, dest: &mut Self) -> Result<(), GuestValueError>; - fn write_ref(val: &Self, dest: GuestPtrMut); + fn read_ref>(src: &P, dest: &mut Self) -> Result<(), GuestValueError>; + fn write_ref(val: &Self, dest: &GuestPtrMut); } impl GuestTypeClone for T where T: GuestTypeCopy, { - fn read_ref(src: GuestPtr, dest: &mut T) -> Result<(), GuestValueError> { + fn read_ref>(src: &P, dest: &mut T) -> Result<(), GuestValueError> { let val = GuestTypeCopy::read_val(src)?; *dest = val; Ok(()) } - fn write_ref(val: &T, dest: GuestPtrMut) { + fn write_ref(val: &T, dest: &GuestPtrMut) { GuestTypeCopy::write_val(*val, dest) } } @@ -49,12 +49,12 @@ macro_rules! builtin_copy { } impl GuestTypeCopy for $t { - fn read_val(src: GuestPtr<$t>) -> Result<$t, GuestValueError> { + fn read_val>(src: &P) -> Result<$t, GuestValueError> { Ok(unsafe { ::std::ptr::read_unaligned(src.ptr() as *const $t) }) } - fn write_val(val: $t, dest: GuestPtrMut<$t>) { + fn write_val(val: $t, dest: &GuestPtrMut<$t>) { unsafe { ::std::ptr::write_unaligned(dest.ptr_mut() as *mut $t, val) } diff --git a/crates/memory/src/lib.rs b/crates/memory/src/lib.rs index 872c556b15..1099776f95 100644 --- a/crates/memory/src/lib.rs +++ b/crates/memory/src/lib.rs @@ -4,5 +4,5 @@ mod memory; mod region; pub use guest_type::{GuestError, GuestType, GuestTypeClone, GuestTypeCopy, GuestValueError}; -pub use memory::{GuestMemory, GuestPtr, GuestPtrMut, MemoryError}; +pub use memory::{GuestMemory, GuestPtr, GuestPtrMut, GuestPtrRead, MemoryError}; pub use region::Region; diff --git a/crates/memory/src/memory.rs b/crates/memory/src/memory.rs index efb027550e..64fa192953 100644 --- a/crates/memory/src/memory.rs +++ b/crates/memory/src/memory.rs @@ -3,7 +3,7 @@ use std::marker::PhantomData; use std::rc::Rc; use thiserror::Error; -use crate::borrow::GuestBorrows; +use crate::borrow::{BorrowHandle, GuestBorrows}; use crate::guest_type::GuestType; use crate::region::Region; @@ -31,80 +31,85 @@ impl<'a> GuestMemory<'a> { } pub fn ptr(&'a self, at: u32) -> Result, MemoryError> { - let r = Region { + let region = Region { start: at, len: T::size(), }; - let mut borrows = self.borrows.borrow_mut(); - if !self.contains(r) { - Err(MemoryError::OutOfBounds(r))?; + if !self.contains(region) { + Err(MemoryError::OutOfBounds(region))?; } - if borrows.borrow_immut(r) { + let mut borrows = self.borrows.borrow_mut(); + if let Some(handle) = borrows.borrow_immut(region) { Ok(GuestPtr { mem: &self, - region: r, + region, + handle, type_: PhantomData, }) } else { - Err(MemoryError::Borrowed(r)) + Err(MemoryError::Borrowed(region)) } } pub fn ptr_mut(&'a self, at: u32) -> Result, MemoryError> { - let r = Region { + let region = Region { start: at, len: T::size(), }; - let mut borrows = self.borrows.borrow_mut(); - if !self.contains(r) { - Err(MemoryError::OutOfBounds(r))?; + if !self.contains(region) { + Err(MemoryError::OutOfBounds(region))?; } - if borrows.borrow_mut(r) { + let mut borrows = self.borrows.borrow_mut(); + if let Some(handle) = borrows.borrow_mut(region) { Ok(GuestPtrMut { mem: &self, - region: r, + region, + handle, type_: PhantomData, }) } else { - Err(MemoryError::Borrowed(r)) + Err(MemoryError::Borrowed(region)) } } } +pub trait GuestPtrRead { + fn ptr(&self) -> *const u8; +} + pub struct GuestPtr<'a, T> { mem: &'a GuestMemory<'a>, region: Region, + handle: BorrowHandle, type_: PhantomData, } -impl<'a, T: GuestType> GuestPtr<'a, T> { - pub fn ptr(&self) -> *const u8 { +impl<'a, T: GuestType> GuestPtrRead for GuestPtr<'a, T> { + fn ptr(&self) -> *const u8 { (self.mem.ptr as usize + self.region.start as usize) as *const u8 } - - pub unsafe fn downcast(self) -> GuestPtr<'a, Q> { - debug_assert!(T::size() == Q::size(), "downcast to type of same size"); - GuestPtr { - mem: self.mem, - region: self.region, - type_: PhantomData, - } - } } impl<'a, T> Drop for GuestPtr<'a, T> { fn drop(&mut self) { let mut borrows = self.mem.borrows.borrow_mut(); - borrows.unborrow_immut(self.region); + borrows.unborrow_immut(self.handle); } } pub struct GuestPtrMut<'a, T> { mem: &'a GuestMemory<'a>, region: Region, + handle: BorrowHandle, type_: PhantomData, } +impl<'a, T: GuestType> GuestPtrRead for GuestPtrMut<'a, T> { + fn ptr(&self) -> *const u8 { + (self.mem.ptr as usize + self.region.start as usize) as *const u8 + } +} + impl<'a, T> GuestPtrMut<'a, T> { pub fn ptr_mut(&self) -> *mut u8 { (self.mem.ptr as usize + self.region.start as usize) as *mut u8 @@ -113,7 +118,7 @@ impl<'a, T> GuestPtrMut<'a, T> { impl<'a, T> Drop for GuestPtrMut<'a, T> { fn drop(&mut self) { let mut borrows = self.mem.borrows.borrow_mut(); - borrows.unborrow_mut(self.region); + borrows.unborrow_mut(self.handle); } } diff --git a/src/lib.rs b/src/lib.rs index f6500df8c0..2f8f782965 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -12,8 +12,29 @@ pub mod test { println!("BAR: {} {}", an_int, an_float); Ok(()) } - fn baz(&mut self, excuse: types::Excuse) -> Result<(), types::Errno> { - println!("BAZ: {:?}", excuse); + fn baz( + &mut self, + excuse: types::Excuse, + a_better_excuse_by_reference: ::memory::GuestPtrMut, + a_lamer_excuse_by_reference: ::memory::GuestPtr, + ) -> Result<(), types::Errno> { + use memory::GuestTypeCopy; + let a_better_excuse = + types::Excuse::read_val(&a_better_excuse_by_reference).map_err(|val_err| { + eprintln!("a_better_excuse_by_reference value error: {:?}", val_err); + types::Errno::InvalidArg + })?; + let a_lamer_excuse = + types::Excuse::read_val(&a_lamer_excuse_by_reference).map_err(|val_err| { + eprintln!("a_lamer_excuse_by_reference value error: {:?}", val_err); + types::Errno::InvalidArg + })?; + types::Excuse::write_val(a_lamer_excuse, &a_better_excuse_by_reference); + + println!( + "BAZ: {:?} {:?} {:?}", + excuse, a_better_excuse, a_lamer_excuse + ); Ok(()) } } diff --git a/test.witx b/test.witx index 9611b9203b..d8e8848cc5 100644 --- a/test.witx +++ b/test.witx @@ -21,5 +21,7 @@ (result $error $errno)) (@interface func (export "baz") (param $an_excuse $excuse) + (param $an_excuse_by_reference (@witx pointer $excuse)) + (param $a_lamer_excuse (@witx const_pointer $excuse)) (result $error $errno)) ) From a20ef36a49ed3b1dbbde44bb7ca67c38604d34fb Mon Sep 17 00:00:00 2001 From: Pat Hickey Date: Fri, 24 Jan 2020 19:57:18 -0800 Subject: [PATCH 22/86] multiple layers of pointers work! --- crates/generate/src/funcs.rs | 16 +++--- crates/generate/src/types.rs | 12 ++--- crates/memory/src/error.rs | 12 +++++ crates/memory/src/guest_type.rs | 22 +++----- crates/memory/src/lib.rs | 6 ++- crates/memory/src/memory.rs | 95 +++++++++++++++++++++++++-------- src/lib.rs | 53 ++++++++++++------ test.witx | 1 + 8 files changed, 146 insertions(+), 71 deletions(-) create mode 100644 crates/memory/src/error.rs diff --git a/crates/generate/src/funcs.rs b/crates/generate/src/funcs.rs index de2c7413da..f624e15146 100644 --- a/crates/generate/src/funcs.rs +++ b/crates/generate/src/funcs.rs @@ -97,7 +97,7 @@ pub fn define_func(names: &Names, func: &witx::InterfaceFunc) -> TokenStream { Err(e) => { return #err_val; }, }; #(#marshal_rets)* - let success:#err_type = ::memory::GuestError::success(); + let success:#err_type = ::memory::GuestErrorType::success(); #abi_ret::from(success) }) } @@ -111,7 +111,7 @@ fn marshal_arg( let interface_typename = names.type_ref(&tref); let name = names.func_param(¶m.name); - let error_handling = |method| -> TokenStream { + let error_handling: TokenStream = { if let Some(tref) = error_type { let abi_ret = match tref.type_().passed_by() { witx::TypePassedBy::Value(atom) => names.atom_type(atom), @@ -119,7 +119,7 @@ fn marshal_arg( }; let err_typename = names.type_ref(&tref); quote! { - let err: #err_typename = ::memory::GuestError::#method(e, ctx); + let err: #err_typename = ::memory::GuestErrorType::from_error(e, ctx); return #abi_ret::from(err); } } else { @@ -128,15 +128,13 @@ fn marshal_arg( } } }; - let value_error_handling = error_handling(quote!(from_value_error)); - let memory_error_handling = error_handling(quote!(from_memory_error)); let try_into_conversion = quote! { use ::std::convert::TryInto; let #name: #interface_typename = match #name.try_into() { Ok(a) => a, Err(e) => { - #value_error_handling + #error_handling } }; }; @@ -151,7 +149,7 @@ fn marshal_arg( let #name: #interface_typename = match (#name as i32).try_into() { Ok(a) => a, Err(e) => { - #value_error_handling + #error_handling } } }, @@ -172,7 +170,7 @@ fn marshal_arg( let #name = match memory.ptr_mut::<#pointee_type>(#name as u32) { Ok(p) => p, Err(e) => { - #memory_error_handling + #error_handling } }; } @@ -183,7 +181,7 @@ fn marshal_arg( let #name = match memory.ptr::<#pointee_type>(#name as u32) { Ok(p) => p, Err(e) => { - #memory_error_handling + #error_handling } }; } diff --git a/crates/generate/src/types.rs b/crates/generate/src/types.rs index a03b4003a2..d385ae3715 100644 --- a/crates/generate/src/types.rs +++ b/crates/generate/src/types.rs @@ -55,18 +55,18 @@ fn define_enum(names: &Names, name: &witx::Id, e: &witx::EnumDatatype) -> TokenS } impl ::std::convert::TryFrom<#repr> for #ident { - type Error = ::memory::GuestValueError; - fn try_from(value: #repr) -> Result<#ident, ::memory::GuestValueError> { + type Error = ::memory::GuestError; + fn try_from(value: #repr) -> Result<#ident, ::memory::GuestError> { match value as usize { #(#tryfrom_repr_cases),*, - _ => Err(::memory::GuestValueError::InvalidEnum(stringify!(#ident))), + _ => Err(::memory::GuestError::InvalidEnumValue(stringify!(#ident))), } } } impl ::std::convert::TryFrom<#abi_repr> for #ident { - type Error = ::memory::GuestValueError; - fn try_from(value: #abi_repr) -> Result<#ident, ::memory::GuestValueError> { + type Error = ::memory::GuestError; + fn try_from(value: #abi_repr) -> Result<#ident, ::memory::GuestError> { #ident::try_from(value as #repr) } } @@ -95,7 +95,7 @@ fn define_enum(names: &Names, name: &witx::Id, e: &witx::EnumDatatype) -> TokenS } impl ::memory::GuestTypeCopy for #ident { - fn read_val>(src: &P) -> Result<#ident, ::memory::GuestValueError> { + fn read_val<'a, P: ::memory::GuestPtrRead<'a, #ident>>(src: &P) -> Result<#ident, ::memory::GuestError> { use ::std::convert::TryInto; let val = unsafe { ::std::ptr::read_unaligned(src.ptr() as *const #repr) }; val.try_into() diff --git a/crates/memory/src/error.rs b/crates/memory/src/error.rs new file mode 100644 index 0000000000..7c5632ca05 --- /dev/null +++ b/crates/memory/src/error.rs @@ -0,0 +1,12 @@ +use crate::Region; +use thiserror::Error; + +#[derive(Debug, Error)] +pub enum GuestError { + #[error("Invalid enum value {0}")] + InvalidEnumValue(&'static str), + #[error("Out of bounds: {0:?}")] + PtrOutOfBounds(Region), + #[error("Borrowed: {0:?}")] + PtrBorrowed(Region), +} diff --git a/crates/memory/src/guest_type.rs b/crates/memory/src/guest_type.rs index 324ca5bf09..6233888540 100644 --- a/crates/memory/src/guest_type.rs +++ b/crates/memory/src/guest_type.rs @@ -1,24 +1,17 @@ -use crate::{GuestPtrMut, GuestPtrRead, MemoryError}; -use thiserror::Error; +use crate::{GuestError, GuestPtrMut, GuestPtrRead}; pub trait GuestType: Sized { fn size() -> u32; fn name() -> &'static str; } -#[derive(Debug, Error)] -pub enum GuestValueError { - #[error("Invalid enum {0}")] - InvalidEnum(&'static str), -} - pub trait GuestTypeCopy: GuestType + Copy { - fn read_val>(src: &P) -> Result; + fn read_val<'a, P: GuestPtrRead<'a, Self>>(src: &P) -> Result; fn write_val(val: Self, dest: &GuestPtrMut); } pub trait GuestTypeClone: GuestType + Clone { - fn read_ref>(src: &P, dest: &mut Self) -> Result<(), GuestValueError>; + fn read_ref<'a, P: GuestPtrRead<'a, Self>>(src: &P, dest: &mut Self) -> Result<(), GuestError>; fn write_ref(val: &Self, dest: &GuestPtrMut); } @@ -26,7 +19,7 @@ impl GuestTypeClone for T where T: GuestTypeCopy, { - fn read_ref>(src: &P, dest: &mut T) -> Result<(), GuestValueError> { + fn read_ref<'a, P: GuestPtrRead<'a, Self>>(src: &P, dest: &mut T) -> Result<(), GuestError> { let val = GuestTypeCopy::read_val(src)?; *dest = val; Ok(()) @@ -49,7 +42,7 @@ macro_rules! builtin_copy { } impl GuestTypeCopy for $t { - fn read_val>(src: &P) -> Result<$t, GuestValueError> { + fn read_val<'a, P: GuestPtrRead<'a, $t>>(src: &P) -> Result<$t, GuestError> { Ok(unsafe { ::std::ptr::read_unaligned(src.ptr() as *const $t) }) @@ -67,9 +60,8 @@ macro_rules! builtin_copy { // These definitions correspond to all the witx BuiltinType variants that are Copy: builtin_copy!(u8, i8, u16, i16, u32, i32, u64, i64, f32, f64, usize, char); -pub trait GuestError { +pub trait GuestErrorType { type Context; fn success() -> Self; - fn from_memory_error(memory_error: MemoryError, ctx: &mut Self::Context) -> Self; - fn from_value_error(value_error: GuestValueError, ctx: &mut Self::Context) -> Self; + fn from_error(e: GuestError, ctx: &mut Self::Context) -> Self; } diff --git a/crates/memory/src/lib.rs b/crates/memory/src/lib.rs index 1099776f95..f8705d6c80 100644 --- a/crates/memory/src/lib.rs +++ b/crates/memory/src/lib.rs @@ -1,8 +1,10 @@ mod borrow; +mod error; mod guest_type; mod memory; mod region; -pub use guest_type::{GuestError, GuestType, GuestTypeClone, GuestTypeCopy, GuestValueError}; -pub use memory::{GuestMemory, GuestPtr, GuestPtrMut, GuestPtrRead, MemoryError}; +pub use error::GuestError; +pub use guest_type::{GuestErrorType, GuestType, GuestTypeClone, GuestTypeCopy}; +pub use memory::{GuestMemory, GuestPtr, GuestPtrMut, GuestPtrRead}; pub use region::Region; diff --git a/crates/memory/src/memory.rs b/crates/memory/src/memory.rs index 64fa192953..3af1a0413d 100644 --- a/crates/memory/src/memory.rs +++ b/crates/memory/src/memory.rs @@ -1,11 +1,9 @@ use std::cell::RefCell; use std::marker::PhantomData; use std::rc::Rc; -use thiserror::Error; use crate::borrow::{BorrowHandle, GuestBorrows}; -use crate::guest_type::GuestType; -use crate::region::Region; +use crate::{GuestError, GuestType, Region}; pub struct GuestMemory<'a> { ptr: *mut u8, @@ -30,13 +28,13 @@ impl<'a> GuestMemory<'a> { && r.start < (self.len - r.len) } - pub fn ptr(&'a self, at: u32) -> Result, MemoryError> { + pub fn ptr(&'a self, at: u32) -> Result, GuestError> { let region = Region { start: at, len: T::size(), }; if !self.contains(region) { - Err(MemoryError::OutOfBounds(region))?; + Err(GuestError::PtrOutOfBounds(region))?; } let mut borrows = self.borrows.borrow_mut(); if let Some(handle) = borrows.borrow_immut(region) { @@ -47,17 +45,17 @@ impl<'a> GuestMemory<'a> { type_: PhantomData, }) } else { - Err(MemoryError::Borrowed(region)) + Err(GuestError::PtrBorrowed(region)) } } - pub fn ptr_mut(&'a self, at: u32) -> Result, MemoryError> { + pub fn ptr_mut(&'a self, at: u32) -> Result, GuestError> { let region = Region { start: at, len: T::size(), }; if !self.contains(region) { - Err(MemoryError::OutOfBounds(region))?; + Err(GuestError::PtrOutOfBounds(region))?; } let mut borrows = self.borrows.borrow_mut(); if let Some(handle) = borrows.borrow_mut(region) { @@ -68,13 +66,19 @@ impl<'a> GuestMemory<'a> { type_: PhantomData, }) } else { - Err(MemoryError::Borrowed(region)) + Err(GuestError::PtrBorrowed(region)) } } } -pub trait GuestPtrRead { - fn ptr(&self) -> *const u8; +/// These methods should not be used by the end user - just by implementations of the +/// GuestValueClone and GuestValueCopy traits! +pub trait GuestPtrRead<'a, T> { + fn mem(&self) -> &'a GuestMemory<'a>; + fn region(&self) -> &Region; + fn ptr(&self) -> *const u8 { + (self.mem().ptr as usize + self.region().start as usize) as *const u8 + } } pub struct GuestPtr<'a, T> { @@ -84,9 +88,31 @@ pub struct GuestPtr<'a, T> { type_: PhantomData, } -impl<'a, T: GuestType> GuestPtrRead for GuestPtr<'a, T> { - fn ptr(&self) -> *const u8 { - (self.mem.ptr as usize + self.region.start as usize) as *const u8 +impl<'a, T> GuestPtrRead<'a, T> for GuestPtr<'a, T> { + fn mem(&self) -> &'a GuestMemory<'a> { + self.mem + } + fn region(&self) -> &Region { + &self.region + } +} + +impl<'a, T> GuestType for GuestPtr<'a, T> { + fn size() -> u32 { + 4 + } + fn name() -> &'static str { + "GuestPtr<...>" + } +} + +impl<'a, T: GuestType> GuestPtr<'a, T> { + pub fn read_ptr>(src: &P) -> Result { + let raw_ptr = unsafe { ::std::ptr::read_unaligned(src.ptr() as *const u32) }; + src.mem().ptr(raw_ptr) + } + pub fn write_ptr(ptr: &Self, dest: &GuestPtrMut) { + unsafe { ::std::ptr::write_unaligned(dest.ptr_mut() as *mut u32, ptr.region.start) } } } @@ -104,9 +130,12 @@ pub struct GuestPtrMut<'a, T> { type_: PhantomData, } -impl<'a, T: GuestType> GuestPtrRead for GuestPtrMut<'a, T> { - fn ptr(&self) -> *const u8 { - (self.mem.ptr as usize + self.region.start as usize) as *const u8 +impl<'a, T> GuestPtrRead<'a, T> for GuestPtrMut<'a, T> { + fn mem(&self) -> &'a GuestMemory<'a> { + self.mem + } + fn region(&self) -> &Region { + &self.region } } @@ -115,6 +144,7 @@ impl<'a, T> GuestPtrMut<'a, T> { (self.mem.ptr as usize + self.region.start as usize) as *mut u8 } } + impl<'a, T> Drop for GuestPtrMut<'a, T> { fn drop(&mut self) { let mut borrows = self.mem.borrows.borrow_mut(); @@ -122,10 +152,29 @@ impl<'a, T> Drop for GuestPtrMut<'a, T> { } } -#[derive(Debug, Error)] -pub enum MemoryError { - #[error("Out of bounds: {0:?}")] - OutOfBounds(Region), - #[error("Borrowed: {0:?}")] - Borrowed(Region), +impl<'a, T> GuestType for GuestPtrMut<'a, T> { + fn size() -> u32 { + 4 + } + fn name() -> &'static str { + "GuestPtrMut<...>" + } +} + +impl<'a, T: GuestType> GuestPtrMut<'a, T> { + pub fn read_ptr>(src: &P) -> Result { + let raw_ptr = unsafe { ::std::ptr::read_unaligned(src.ptr() as *const u32) }; + src.mem().ptr_mut(raw_ptr) + } + pub fn write_ptr(ptr: &Self, dest: &GuestPtrMut) { + unsafe { ::std::ptr::write_unaligned(dest.ptr_mut() as *mut u32, ptr.region.start) } + } + + pub fn as_immut(self) -> GuestPtr<'a, T> { + let mem = self.mem; + let start = self.region.start; + drop(self); + mem.ptr(start) + .expect("can borrow just-dropped mutable region as immut") + } } diff --git a/src/lib.rs b/src/lib.rs index 2f8f782965..fd287a0f0c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,8 +3,7 @@ pub mod test { generate::from_witx!("test.witx"); pub struct WasiCtx { - mem_errors: Vec<::memory::MemoryError>, - value_errors: Vec<::memory::GuestValueError>, + guest_errors: Vec<::memory::GuestError>, } impl foo::Foo for WasiCtx { @@ -17,43 +16,65 @@ pub mod test { excuse: types::Excuse, a_better_excuse_by_reference: ::memory::GuestPtrMut, a_lamer_excuse_by_reference: ::memory::GuestPtr, + two_layers_of_excuses: ::memory::GuestPtrMut<::memory::GuestPtr>, ) -> Result<(), types::Errno> { use memory::GuestTypeCopy; + + // Read enum value from mutable: let a_better_excuse = - types::Excuse::read_val(&a_better_excuse_by_reference).map_err(|val_err| { - eprintln!("a_better_excuse_by_reference value error: {:?}", val_err); + types::Excuse::read_val(&a_better_excuse_by_reference).map_err(|e| { + eprintln!("a_better_excuse_by_reference error: {}", e); types::Errno::InvalidArg })?; + + // Read enum value from immutable ptr: let a_lamer_excuse = - types::Excuse::read_val(&a_lamer_excuse_by_reference).map_err(|val_err| { - eprintln!("a_lamer_excuse_by_reference value error: {:?}", val_err); + types::Excuse::read_val(&a_lamer_excuse_by_reference).map_err(|e| { + eprintln!("a_lamer_excuse_by_reference error: {}", e); types::Errno::InvalidArg })?; + + // Write enum to mutable ptr: types::Excuse::write_val(a_lamer_excuse, &a_better_excuse_by_reference); + // Read ptr value from mutable ptr: + let one_layer_down = + ::memory::GuestPtr::read_ptr(&two_layers_of_excuses).map_err(|e| { + eprintln!("one_layer_down error: {}", e); + types::Errno::InvalidArg + })?; + + // Read enum value from that ptr: + let two_layers_down = types::Excuse::read_val(&one_layer_down).map_err(|e| { + eprintln!("two_layers_down error: {}", e); + types::Errno::InvalidArg + })?; + + // Write ptr value to mutable ptr: + ::memory::GuestPtr::write_ptr( + &a_better_excuse_by_reference.as_immut(), + &two_layers_of_excuses, + ); + println!( - "BAZ: {:?} {:?} {:?}", - excuse, a_better_excuse, a_lamer_excuse + "BAZ: excuse: {:?}, better excuse: {:?}, lamer excuse: {:?}, two layers down: {:?}", + excuse, a_better_excuse, a_lamer_excuse, two_layers_down ); Ok(()) } } // Errno is used as a first return value in the functions above, therefore - // it must implement GuestError with type Context = WasiCtx. + // it must implement GuestErrorType with type Context = WasiCtx. // The context type should let you do logging or debugging or whatever you need // with these errors. We just push them to vecs. - impl ::memory::GuestError for types::Errno { + impl ::memory::GuestErrorType for types::Errno { type Context = WasiCtx; fn success() -> types::Errno { types::Errno::Ok } - fn from_memory_error(e: ::memory::MemoryError, ctx: &mut WasiCtx) -> types::Errno { - ctx.mem_errors.push(e); - types::Errno::InvalidArg - } - fn from_value_error(e: ::memory::GuestValueError, ctx: &mut WasiCtx) -> types::Errno { - ctx.value_errors.push(e); + fn from_error(e: ::memory::GuestError, ctx: &mut WasiCtx) -> types::Errno { + ctx.guest_errors.push(e); types::Errno::InvalidArg } } diff --git a/test.witx b/test.witx index d8e8848cc5..16964d8eea 100644 --- a/test.witx +++ b/test.witx @@ -23,5 +23,6 @@ (param $an_excuse $excuse) (param $an_excuse_by_reference (@witx pointer $excuse)) (param $a_lamer_excuse (@witx const_pointer $excuse)) + (param $two_layers_of_excuses (@witx pointer (@witx const_pointer $excuse))) (result $error $errno)) ) From e6a4ae205c5a3ad6a560da3c6ad3a6a3a1859978 Mon Sep 17 00:00:00 2001 From: Pat Hickey Date: Mon, 27 Jan 2020 12:40:54 -0800 Subject: [PATCH 23/86] return values written to pointers! --- crates/generate/src/funcs.rs | 211 +++++++++++++++++++++++------------ crates/generate/src/names.rs | 9 +- src/lib.rs | 6 +- test.witx | 4 + 4 files changed, 153 insertions(+), 77 deletions(-) diff --git a/crates/generate/src/funcs.rs b/crates/generate/src/funcs.rs index f624e15146..e886308f86 100644 --- a/crates/generate/src/funcs.rs +++ b/crates/generate/src/funcs.rs @@ -8,39 +8,34 @@ use crate::names::Names; // pub fn define_func(names: &Names, func: &witx::InterfaceFunc) -> TokenStream { let ident = names.func(&func.name); + let coretype = func.core_type(); - let arg_signature = |param: &witx::InterfaceFuncParam| -> TokenStream { - let name = names.func_param(¶m.name); - match param.tref.type_().passed_by() { - witx::TypePassedBy::Value(atom) => { - let atom = names.atom_type(atom); - quote!(#name: #atom) - } - witx::TypePassedBy::Pointer => { - let atom = names.atom_type(witx::AtomType::I32); - quote!(#name: #atom) - } - witx::TypePassedBy::PointerLengthPair => { - let atom = names.atom_type(witx::AtomType::I32); - let len_name = names.func_len_param(¶m.name); - quote!(#name: #atom, #len_name: #atom) - } + let params = coretype.args.iter().map(|arg| match arg.signifies { + witx::CoreParamSignifies::Value(atom) => { + let atom = names.atom_type(atom); + let name = names.func_param(&arg.param.name); + quote!(#name : #atom) } - }; + witx::CoreParamSignifies::PointerTo => { + let atom = names.atom_type(witx::AtomType::I32); + let name = names.func_ptr_binding(&arg.param.name); + quote!(#name: #atom) + } + witx::CoreParamSignifies::LengthOf => { + let atom = names.atom_type(witx::AtomType::I32); + let name = names.func_len_binding(&arg.param.name); + quote!(#name: #atom) + } + }); - let params = func - .params - .iter() - .chain(func.results.iter().skip(1)) - .map(arg_signature); let abi_args = quote!( ctx: &mut WasiCtx, memory: ::memory::GuestMemory, #(#params),* ); - let abi_ret = if let Some(first_result) = func.results.get(0) { - match first_result.tref.type_().passed_by() { - witx::TypePassedBy::Value(atom) => names.atom_type(atom), - _ => unreachable!("first result should always be passed by value"), + let abi_ret = if let Some(ret) = &coretype.ret { + match ret.signifies { + witx::CoreParamSignifies::Value(atom) => names.atom_type(atom), + _ => unreachable!("ret should always be passed by value"), } } else if func.noreturn { // Ideally we would return `quote!(!)` here, but, we'd have to change @@ -52,21 +47,34 @@ pub fn define_func(names: &Names, func: &witx::InterfaceFunc) -> TokenStream { quote!(()) }; - let err_type = func - .results - .get(0) - .map(|res| names.type_ref(&res.tref)) - .unwrap_or_else(|| abi_ret.clone()); - let err_val = func - .results - .get(0) + let err_type = coretype.ret.map(|ret| ret.param.tref); + let err_val = err_type + .clone() .map(|_res| quote!(#abi_ret::from(e))) .unwrap_or_else(|| quote!(())); + let error_handling: TokenStream = { + if let Some(tref) = &err_type { + let abi_ret = match tref.type_().passed_by() { + witx::TypePassedBy::Value(atom) => names.atom_type(atom), + _ => unreachable!("err should always be passed by value"), + }; + let err_typename = names.type_ref(&tref); + quote! { + let err: #err_typename = ::memory::GuestErrorType::from_error(e, ctx); + return #abi_ret::from(err); + } + } else { + quote! { + panic!("error: {:?}", e) + } + } + }; + let marshal_args = func .params .iter() - .map(|p| marshal_arg(names, p, func.results.get(0).map(|r| &r.tref))); + .map(|p| marshal_arg(names, p, error_handling.clone())); let trait_args = func .params .iter() @@ -84,88 +92,93 @@ pub fn define_func(names: &Names, func: &witx::InterfaceFunc) -> TokenStream { (tuple.clone(), tuple) }; + // Return value pointers need to be validated before the api call, then + // assigned to afterwards. marshal_result returns these two statements as a pair. let marshal_rets = func .results .iter() .skip(1) - .map(|_result| quote! { unimplemented!("convert result..."); }); + .map(|result| marshal_result(names, result, error_handling.clone())); + let marshal_rets_pre = marshal_rets.clone().map(|(pre, _post)| pre); + let marshal_rets_post = marshal_rets.map(|(_pre, post)| post); + + let success = if let Some(err_type) = err_type { + let err_typename = names.type_ref(&err_type); + quote! { + let success:#err_typename = ::memory::GuestErrorType::success(); + #abi_ret::from(success) + } + } else { + quote!() + }; quote!(pub fn #ident(#abi_args) -> #abi_ret { #(#marshal_args)* + #(#marshal_rets_pre)* let #trait_bindings = match ctx.#ident(#(#trait_args),*) { Ok(#trait_bindings) => #trait_rets, Err(e) => { return #err_val; }, }; - #(#marshal_rets)* - let success:#err_type = ::memory::GuestErrorType::success(); - #abi_ret::from(success) + #(#marshal_rets_post)* + #success }) } fn marshal_arg( names: &Names, param: &witx::InterfaceFuncParam, - error_type: Option<&witx::TypeRef>, + error_handling: TokenStream, ) -> TokenStream { let tref = ¶m.tref; let interface_typename = names.type_ref(&tref); - let name = names.func_param(¶m.name); - let error_handling: TokenStream = { - if let Some(tref) = error_type { - let abi_ret = match tref.type_().passed_by() { - witx::TypePassedBy::Value(atom) => names.atom_type(atom), - _ => unreachable!("err should always be passed by value"), + let try_into_conversion = { + let name = names.func_param(¶m.name); + quote! { + use ::std::convert::TryInto; + let #name: #interface_typename = match #name.try_into() { + Ok(a) => a, + Err(e) => { + #error_handling + } }; - let err_typename = names.type_ref(&tref); - quote! { - let err: #err_typename = ::memory::GuestErrorType::from_error(e, ctx); - return #abi_ret::from(err); - } - } else { - quote! { - panic!("error: {:?}", e) - } } }; - let try_into_conversion = quote! { - use ::std::convert::TryInto; - let #name: #interface_typename = match #name.try_into() { - Ok(a) => a, - Err(e) => { - #error_handling - } - }; - }; - match &*tref.type_() { witx::Type::Enum(_e) => try_into_conversion, witx::Type::Builtin(b) => match b { witx::BuiltinType::U8 | witx::BuiltinType::U16 | witx::BuiltinType::Char8 => { try_into_conversion } - witx::BuiltinType::S8 | witx::BuiltinType::S16 => quote! { - let #name: #interface_typename = match (#name as i32).try_into() { - Ok(a) => a, - Err(e) => { - #error_handling + witx::BuiltinType::S8 | witx::BuiltinType::S16 => { + let name = names.func_param(¶m.name); + quote! { + let #name: #interface_typename = match (#name as i32).try_into() { + Ok(a) => a, + Err(e) => { + #error_handling + } } } - }, + } witx::BuiltinType::U32 | witx::BuiltinType::S32 | witx::BuiltinType::U64 | witx::BuiltinType::S64 | witx::BuiltinType::USize | witx::BuiltinType::F32 - | witx::BuiltinType::F64 => quote! { - let #name = #name as #interface_typename; - }, + | witx::BuiltinType::F64 => { + let name = names.func_param(¶m.name); + quote! { + let #name = #name as #interface_typename; + } + } witx::BuiltinType::String => unimplemented!("string types unimplemented"), }, witx::Type::Pointer(pointee) => { let pointee_type = names.type_ref(pointee); + let name = names.func_param(¶m.name); quote! { let #name = match memory.ptr_mut::<#pointee_type>(#name as u32) { Ok(p) => p, @@ -177,6 +190,7 @@ fn marshal_arg( } witx::Type::ConstPointer(pointee) => { let pointee_type = names.type_ref(pointee); + let name = names.func_param(¶m.name); quote! { let #name = match memory.ptr::<#pointee_type>(#name as u32) { Ok(p) => p, @@ -189,3 +203,52 @@ fn marshal_arg( _ => unimplemented!("argument type marshalling"), } } + +fn marshal_result( + names: &Names, + result: &witx::InterfaceFuncParam, + error_handling: TokenStream, +) -> (TokenStream, TokenStream) { + let tref = &result.tref; + + let write_val_to_ptr = { + let pointee_type = names.type_ref(tref); + // core type is given func_ptr_binding name. + let ptr_name = names.func_ptr_binding(&result.name); + let pre = quote! { + let #ptr_name = match memory.ptr_mut::<#pointee_type>(#ptr_name as u32) { + Ok(p) => p, + Err(e) => { + #error_handling + } + }; + }; + // trait binding returns func_param name. + let val_name = names.func_param(&result.name); + let post = quote! { + use ::memory::GuestTypeCopy; + #pointee_type::write_val(#val_name, &#ptr_name); + }; + (pre, post) + }; + + match &*tref.type_() { + witx::Type::Builtin(b) => match b { + witx::BuiltinType::U8 + | witx::BuiltinType::S8 + | witx::BuiltinType::U16 + | witx::BuiltinType::S16 + | witx::BuiltinType::U32 + | witx::BuiltinType::S32 + | witx::BuiltinType::U64 + | witx::BuiltinType::S64 + | witx::BuiltinType::F32 + | witx::BuiltinType::F64 + | witx::BuiltinType::USize + | witx::BuiltinType::Char8 => write_val_to_ptr, + witx::BuiltinType::String => unimplemented!("string types"), + }, + witx::Type::Enum(_e) => write_val_to_ptr, + _ => unimplemented!("marshal result"), + } +} diff --git a/crates/generate/src/names.rs b/crates/generate/src/names.rs index 6465e7f832..acbb3b00aa 100644 --- a/crates/generate/src/names.rs +++ b/crates/generate/src/names.rs @@ -88,8 +88,13 @@ impl Names { format_ident!("{}", id.as_str().to_snake_case()) } - /// For when you need a {name}_len parameter for passing an array: - pub fn func_len_param(&self, id: &Id) -> Ident { + /// For when you need a {name}_ptr binding for passing a value by reference: + pub fn func_ptr_binding(&self, id: &Id) -> Ident { + format_ident!("{}_ptr", id.as_str().to_snake_case()) + } + + /// For when you need a {name}_len binding for passing an array: + pub fn func_len_binding(&self, id: &Id) -> Ident { format_ident!("{}_len", id.as_str().to_snake_case()) } } diff --git a/src/lib.rs b/src/lib.rs index fd287a0f0c..cf36236e83 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -62,8 +62,12 @@ pub mod test { ); Ok(()) } - } + fn bat(&mut self, an_int: u32) -> Result { + println!("bat: {}", an_int); + Ok((an_int as f32) * 2.0) + } + } // Errno is used as a first return value in the functions above, therefore // it must implement GuestErrorType with type Context = WasiCtx. // The context type should let you do logging or debugging or whatever you need diff --git a/test.witx b/test.witx index 16964d8eea..422d99bd72 100644 --- a/test.witx +++ b/test.witx @@ -25,4 +25,8 @@ (param $a_lamer_excuse (@witx const_pointer $excuse)) (param $two_layers_of_excuses (@witx pointer (@witx const_pointer $excuse))) (result $error $errno)) + (@interface func (export "bat") + (param $an_int u32) + (result $error $errno) + (result $doubled_it f32)) ) From ec456e9e509d72ffb285656d8657a7d34b9448e4 Mon Sep 17 00:00:00 2001 From: Pat Hickey Date: Mon, 27 Jan 2020 18:20:47 -0800 Subject: [PATCH 24/86] new memory model. not quite complete --- crates/generate/src/funcs.rs | 6 +- crates/generate/src/lib.rs | 2 +- crates/generate/src/types.rs | 32 ++-- crates/memory/src/error.rs | 6 +- crates/memory/src/guest_type.rs | 62 +++---- crates/memory/src/lib.rs | 4 +- crates/memory/src/memory.rs | 317 ++++++++++++++++++++++---------- src/lib.rs | 37 ++-- 8 files changed, 287 insertions(+), 179 deletions(-) diff --git a/crates/generate/src/funcs.rs b/crates/generate/src/funcs.rs index e886308f86..6f4eebe592 100644 --- a/crates/generate/src/funcs.rs +++ b/crates/generate/src/funcs.rs @@ -74,7 +74,8 @@ pub fn define_func(names: &Names, func: &witx::InterfaceFunc) -> TokenStream { let marshal_args = func .params .iter() - .map(|p| marshal_arg(names, p, error_handling.clone())); + //.map(|p| marshal_arg(names, p, error_handling.clone())); + .map(|_p| quote!(unimplemented!(); )); // FIXME let trait_args = func .params .iter() @@ -98,7 +99,8 @@ pub fn define_func(names: &Names, func: &witx::InterfaceFunc) -> TokenStream { .results .iter() .skip(1) - .map(|result| marshal_result(names, result, error_handling.clone())); + //.map(|result| marshal_result(names, result, error_handling.clone())); + .map(|_result| (quote!(unimplemented!();), quote!(unimplemented!();))); // FIXME let marshal_rets_pre = marshal_rets.clone().map(|(pre, _post)| pre); let marshal_rets_post = marshal_rets.map(|(_pre, post)| post); diff --git a/crates/generate/src/lib.rs b/crates/generate/src/lib.rs index 49d9289d58..15d07cba0c 100644 --- a/crates/generate/src/lib.rs +++ b/crates/generate/src/lib.rs @@ -34,7 +34,7 @@ pub fn from_witx(args: TokenStream) -> TokenStream { mod #modname { use super::WasiCtx; use super::types::*; - #(#fs)* + // #(#fs)* #modtrait } diff --git a/crates/generate/src/types.rs b/crates/generate/src/types.rs index d385ae3715..4ee337bbc0 100644 --- a/crates/generate/src/types.rs +++ b/crates/generate/src/types.rs @@ -89,24 +89,30 @@ fn define_enum(names: &Names, name: &witx::Id, e: &witx::EnumDatatype) -> TokenS fn size() -> u32 { ::std::mem::size_of::<#repr>() as u32 } - fn name() -> &'static str { - stringify!(#ident) + fn align() -> u32 { + ::std::mem::align_of::<#repr>() as u32 + } + fn name() -> String { + stringify!(#ident).to_owned() + } + fn validate<'a>(location: &::memory::GuestPtr<'a, #ident>) -> Result<(), ::memory::GuestError> { + use ::std::convert::TryFrom; + let raw: #repr = unsafe { (location.as_raw() as *const #repr).read() }; + let _ = #ident::try_from(raw)?; + Ok(()) } } - impl ::memory::GuestTypeCopy for #ident { - fn read_val<'a, P: ::memory::GuestPtrRead<'a, #ident>>(src: &P) -> Result<#ident, ::memory::GuestError> { - use ::std::convert::TryInto; - let val = unsafe { ::std::ptr::read_unaligned(src.ptr() as *const #repr) }; - val.try_into() - } - fn write_val(val: #ident, dest: &::memory::GuestPtrMut<#ident>) { - let val: #repr = val.into(); - unsafe { - ::std::ptr::write_unaligned(dest.ptr_mut() as *mut #repr, val) - }; + impl ::memory::GuestTypeCopy for #ident {} + impl ::memory::GuestTypeClone for #ident { + fn from_guest(location: &::memory::GuestPtr<#ident>) -> Result<#ident, ::memory::GuestError> { + use ::std::convert::TryFrom; + let raw: #repr = unsafe { (location.as_raw() as *const #repr).read() }; + let val = #ident::try_from(raw)?; + Ok(val) } } + } } diff --git a/crates/memory/src/error.rs b/crates/memory/src/error.rs index 7c5632ca05..b1ed3b49b0 100644 --- a/crates/memory/src/error.rs +++ b/crates/memory/src/error.rs @@ -5,8 +5,10 @@ use thiserror::Error; pub enum GuestError { #[error("Invalid enum value {0}")] InvalidEnumValue(&'static str), - #[error("Out of bounds: {0:?}")] + #[error("Pointer out of bounds: {0:?}")] PtrOutOfBounds(Region), - #[error("Borrowed: {0:?}")] + #[error("Pointer not aligned to {1}: {0:?}")] + PtrNotAligned(Region, u32), + #[error("Pointer already borrowed: {0:?}")] PtrBorrowed(Region), } diff --git a/crates/memory/src/guest_type.rs b/crates/memory/src/guest_type.rs index 6233888540..2b4b3f387a 100644 --- a/crates/memory/src/guest_type.rs +++ b/crates/memory/src/guest_type.rs @@ -1,56 +1,39 @@ -use crate::{GuestError, GuestPtrMut, GuestPtrRead}; +use crate::{GuestError, GuestPtr}; pub trait GuestType: Sized { + // These are morally the same as Rust ::std::mem::size_of / align_of, but they return + // a u32 because the wasm memory space is 32 bits. They have a different names so they + // don't collide with the std::mem methods. fn size() -> u32; - fn name() -> &'static str; -} - -pub trait GuestTypeCopy: GuestType + Copy { - fn read_val<'a, P: GuestPtrRead<'a, Self>>(src: &P) -> Result; - fn write_val(val: Self, dest: &GuestPtrMut); + fn align() -> u32; + fn name() -> String; + fn validate<'a>(location: &GuestPtr<'a, Self>) -> Result<(), GuestError>; } +pub trait GuestTypeCopy: GuestType + Copy {} pub trait GuestTypeClone: GuestType + Clone { - fn read_ref<'a, P: GuestPtrRead<'a, Self>>(src: &P, dest: &mut Self) -> Result<(), GuestError>; - fn write_ref(val: &Self, dest: &GuestPtrMut); + fn from_guest<'a>(location: &GuestPtr<'a, Self>) -> Result; +} +pub trait GuestTypeRef<'a>: GuestType { + type Ref; + fn from_guest(location: &GuestPtr<'a, Self>) -> Result; } -impl GuestTypeClone for T -where - T: GuestTypeCopy, -{ - fn read_ref<'a, P: GuestPtrRead<'a, Self>>(src: &P, dest: &mut T) -> Result<(), GuestError> { - let val = GuestTypeCopy::read_val(src)?; - *dest = val; - Ok(()) - } - fn write_ref(val: &T, dest: &GuestPtrMut) { - GuestTypeCopy::write_val(*val, dest) - } -} - -macro_rules! builtin_copy { +macro_rules! builtin_type { ( $( $t:ident ), * ) => { $( impl GuestType for $t { fn size() -> u32 { ::std::mem::size_of::<$t>() as u32 } - fn name() -> &'static str { - ::std::stringify!($t) + fn align() -> u32 { + ::std::mem::align_of::<$t>() as u32 } - } - - impl GuestTypeCopy for $t { - fn read_val<'a, P: GuestPtrRead<'a, $t>>(src: &P) -> Result<$t, GuestError> { - Ok(unsafe { - ::std::ptr::read_unaligned(src.ptr() as *const $t) - }) + fn name() -> String { + ::std::stringify!($t).to_owned() } - fn write_val(val: $t, dest: &GuestPtrMut<$t>) { - unsafe { - ::std::ptr::write_unaligned(dest.ptr_mut() as *mut $t, val) - } + fn validate(_ptr: &GuestPtr<$t>) -> Result<(), GuestError> { + Ok(()) } } )* @@ -58,7 +41,10 @@ macro_rules! builtin_copy { } // These definitions correspond to all the witx BuiltinType variants that are Copy: -builtin_copy!(u8, i8, u16, i16, u32, i32, u64, i64, f32, f64, usize, char); +builtin_type!(u8, i8, u16, i16, u32, i32, u64, i64, f32, f64, usize); + +// FIXME implement GuestType for char. needs to validate that its a code point. what is the sizeof a char? +// FIXME implement GuestType for String. how does validate work for array types? pub trait GuestErrorType { type Context; diff --git a/crates/memory/src/lib.rs b/crates/memory/src/lib.rs index f8705d6c80..5aceb413b3 100644 --- a/crates/memory/src/lib.rs +++ b/crates/memory/src/lib.rs @@ -5,6 +5,6 @@ mod memory; mod region; pub use error::GuestError; -pub use guest_type::{GuestErrorType, GuestType, GuestTypeClone, GuestTypeCopy}; -pub use memory::{GuestMemory, GuestPtr, GuestPtrMut, GuestPtrRead}; +pub use guest_type::{GuestErrorType, GuestType, GuestTypeClone, GuestTypeCopy, GuestTypeRef}; +pub use memory::{GuestMemory, GuestPtr, GuestPtrMut, GuestRef, GuestRefMut}; pub use region::Region; diff --git a/crates/memory/src/memory.rs b/crates/memory/src/memory.rs index 3af1a0413d..3b6897b8cd 100644 --- a/crates/memory/src/memory.rs +++ b/crates/memory/src/memory.rs @@ -3,7 +3,7 @@ use std::marker::PhantomData; use std::rc::Rc; use crate::borrow::{BorrowHandle, GuestBorrows}; -use crate::{GuestError, GuestType, Region}; +use crate::{GuestError, GuestType, GuestTypeCopy, GuestTypeRef, Region}; pub struct GuestMemory<'a> { ptr: *mut u8, @@ -14,6 +14,7 @@ pub struct GuestMemory<'a> { impl<'a> GuestMemory<'a> { pub fn new(ptr: *mut u8, len: u32) -> GuestMemory<'a> { + assert_eq!(ptr as usize % 4096, 0, "GuestMemory must be page-aligned"); GuestMemory { ptr, len, @@ -36,145 +37,259 @@ impl<'a> GuestMemory<'a> { if !self.contains(region) { Err(GuestError::PtrOutOfBounds(region))?; } - let mut borrows = self.borrows.borrow_mut(); - if let Some(handle) = borrows.borrow_immut(region) { - Ok(GuestPtr { - mem: &self, - region, - handle, - type_: PhantomData, - }) - } else { - Err(GuestError::PtrBorrowed(region)) + if at % T::align() != 0 { + Err(GuestError::PtrNotAligned(region, T::align()))?; } + Ok(GuestPtr { + mem: &self, + region, + type_: PhantomData, + }) } - pub fn ptr_mut(&'a self, at: u32) -> Result, GuestError> { - let region = Region { - start: at, - len: T::size(), - }; - if !self.contains(region) { - Err(GuestError::PtrOutOfBounds(region))?; - } - let mut borrows = self.borrows.borrow_mut(); - if let Some(handle) = borrows.borrow_mut(region) { - Ok(GuestPtrMut { - mem: &self, - region, - handle, - type_: PhantomData, - }) - } else { - Err(GuestError::PtrBorrowed(region)) - } - } -} - -/// These methods should not be used by the end user - just by implementations of the -/// GuestValueClone and GuestValueCopy traits! -pub trait GuestPtrRead<'a, T> { - fn mem(&self) -> &'a GuestMemory<'a>; - fn region(&self) -> &Region; - fn ptr(&self) -> *const u8 { - (self.mem().ptr as usize + self.region().start as usize) as *const u8 + let ptr = self.ptr(at)?; + Ok(GuestPtrMut { + mem: ptr.mem, + region: ptr.region, + type_: ptr.type_, + }) } } +#[derive(Clone)] pub struct GuestPtr<'a, T> { + mem: &'a GuestMemory<'a>, + region: Region, + type_: PhantomData, +} + +impl<'a, T: GuestType> GuestPtr<'a, T> { + pub fn as_raw(&self) -> *const u8 { + (self.mem.ptr as usize + self.region.start as usize) as *const u8 + } + pub fn as_ref(&self) -> Result, GuestError> { + T::validate(&self)?; + let handle = { + let mut borrows = self.mem.borrows.borrow_mut(); + borrows + .borrow_immut(self.region) + .ok_or_else(|| GuestError::PtrBorrowed(self.region))? + }; + Ok(GuestRef { + mem: self.mem, + region: self.region, + handle, + type_: self.type_, + }) + } +} + +impl<'a, T> GuestType for GuestPtr<'a, T> +where + T: GuestType, +{ + fn size() -> u32 { + 4 + } + fn align() -> u32 { + 4 + } + fn name() -> String { + format!("GuestPtr<{}>", T::name()) + } + fn validate<'b>(location: &GuestPtr<'b, GuestPtr<'b, T>>) -> Result<(), GuestError> { + // location is guaranteed to be in GuestMemory and aligned to 4 + let raw_ptr: u32 = unsafe { *(location.as_raw() as *const u32) }; + // GuestMemory can validate that the raw pointer contents are legal for T: + let _guest_ptr: GuestPtr = location.mem.ptr(raw_ptr)?; + Ok(()) + } +} + +impl<'a, T> GuestTypeRef<'a> for GuestPtr<'a, T> +where + T: GuestType, +{ + type Ref = GuestRef<'a, T>; + fn from_guest(location: &GuestPtr<'a, Self>) -> Result { + // location is guaranteed to be in GuestMemory and aligned to 4 + let raw_ptr: u32 = unsafe { *(location.as_raw() as *const u32) }; + // GuestMemory can validate that the raw pointer contents are legal for T: + let guest_ptr: GuestPtr<'a, T> = location.mem.ptr(raw_ptr)?; + Ok(guest_ptr.as_ref()?) + } +} + +#[derive(Clone)] +pub struct GuestPtrMut<'a, T> { + mem: &'a GuestMemory<'a>, + region: Region, + type_: PhantomData, +} + +impl<'a, T: GuestType> GuestPtrMut<'a, T> { + pub fn as_immut(&self) -> GuestPtr<'a, T> { + GuestPtr { + mem: self.mem, + region: self.region, + type_: self.type_, + } + } + + pub fn as_raw(&self) -> *const u8 { + self.as_immut().as_raw() + } + + pub fn as_ref(&self) -> Result, GuestError> { + self.as_immut().as_ref() + } + + pub fn as_ref_mut(&self) -> Result, GuestError> { + T::validate(&self.as_immut())?; + let handle = { + let mut borrows = self.mem.borrows.borrow_mut(); + borrows + .borrow_mut(self.region) + .ok_or_else(|| GuestError::PtrBorrowed(self.region))? + }; + Ok(GuestRefMut { + mem: self.mem, + region: self.region, + handle, + type_: self.type_, + }) + } +} + +impl<'a, T> GuestType for GuestPtrMut<'a, T> +where + T: GuestType, +{ + fn size() -> u32 { + 4 + } + fn align() -> u32 { + 4 + } + fn name() -> String { + format!("GuestPtrMut<{}>", T::name()) + } + fn validate<'b>(location: &GuestPtr<'b, GuestPtrMut<'b, T>>) -> Result<(), GuestError> { + // location is guaranteed to be in GuestMemory and aligned to 4 + let raw_ptr: u32 = unsafe { *(location.as_raw() as *const u32) }; + // GuestMemory can validate that the raw pointer contents are legal for T: + let _guest_ptr: GuestPtr = location.mem.ptr(raw_ptr)?; + Ok(()) + } +} + +impl<'a, T> GuestTypeRef<'a> for GuestPtrMut<'a, T> +where + T: GuestType, +{ + type Ref = GuestRefMut<'a, T>; + fn from_guest(location: &GuestPtr<'a, Self>) -> Result { + // location is guaranteed to be in GuestMemory and aligned to 4 + let raw_ptr: u32 = unsafe { *(location.as_raw() as *const u32) }; + // GuestMemory can validate that the raw pointer contents are legal for T: + let guest_ptr_mut: GuestPtrMut<'a, T> = location.mem.ptr_mut(raw_ptr)?; + Ok(guest_ptr_mut.as_ref_mut()?) + } +} + +pub struct GuestRef<'a, T> { mem: &'a GuestMemory<'a>, region: Region, handle: BorrowHandle, type_: PhantomData, } -impl<'a, T> GuestPtrRead<'a, T> for GuestPtr<'a, T> { - fn mem(&self) -> &'a GuestMemory<'a> { - self.mem - } - fn region(&self) -> &Region { - &self.region +impl<'a, T> ::std::ops::Deref for GuestRef<'a, T> +where + T: GuestTypeCopy, +{ + type Target = T; + fn deref(&self) -> &T { + unsafe { + ((self.mem.ptr as usize + self.region.start as usize) as *const T) + .as_ref() + .expect("GuestRef implies non-null") + } } } -impl<'a, T> GuestType for GuestPtr<'a, T> { - fn size() -> u32 { - 4 - } - fn name() -> &'static str { - "GuestPtr<...>" +impl<'a, T> GuestRef<'a, T> +where + T: GuestTypeRef<'a>, +{ + pub fn from_guest(&self) -> Result { + let ptr = GuestPtr { + mem: self.mem, + region: self.region, + type_: self.type_, + }; + GuestTypeRef::from_guest(&ptr) } } -impl<'a, T: GuestType> GuestPtr<'a, T> { - pub fn read_ptr>(src: &P) -> Result { - let raw_ptr = unsafe { ::std::ptr::read_unaligned(src.ptr() as *const u32) }; - src.mem().ptr(raw_ptr) - } - pub fn write_ptr(ptr: &Self, dest: &GuestPtrMut) { - unsafe { ::std::ptr::write_unaligned(dest.ptr_mut() as *mut u32, ptr.region.start) } - } -} - -impl<'a, T> Drop for GuestPtr<'a, T> { +impl<'a, T> Drop for GuestRef<'a, T> { fn drop(&mut self) { let mut borrows = self.mem.borrows.borrow_mut(); borrows.unborrow_immut(self.handle); } } -pub struct GuestPtrMut<'a, T> { +pub struct GuestRefMut<'a, T> { mem: &'a GuestMemory<'a>, region: Region, handle: BorrowHandle, type_: PhantomData, } -impl<'a, T> GuestPtrRead<'a, T> for GuestPtrMut<'a, T> { - fn mem(&self) -> &'a GuestMemory<'a> { - self.mem - } - fn region(&self) -> &Region { - &self.region +impl<'a, T> ::std::ops::Deref for GuestRefMut<'a, T> +where + T: GuestTypeCopy, +{ + type Target = T; + fn deref(&self) -> &T { + unsafe { + ((self.mem.ptr as usize + self.region.start as usize) as *const T) + .as_ref() + .expect("GuestRef implies non-null") + } } } -impl<'a, T> GuestPtrMut<'a, T> { - pub fn ptr_mut(&self) -> *mut u8 { - (self.mem.ptr as usize + self.region.start as usize) as *mut u8 +impl<'a, T> ::std::ops::DerefMut for GuestRefMut<'a, T> +where + T: GuestTypeCopy, +{ + fn deref_mut(&mut self) -> &mut T { + unsafe { + ((self.mem.ptr as usize + self.region.start as usize) as *mut T) + .as_mut() + .expect("GuestRef implies non-null") + } } } -impl<'a, T> Drop for GuestPtrMut<'a, T> { +impl<'a, T> GuestRefMut<'a, T> +where + T: GuestTypeRef<'a>, +{ + pub fn from_guest(&self) -> Result { + let ptr = GuestPtr { + mem: self.mem, + region: self.region, + type_: self.type_, + }; + GuestTypeRef::from_guest(&ptr) + } +} + +impl<'a, T> Drop for GuestRefMut<'a, T> { fn drop(&mut self) { let mut borrows = self.mem.borrows.borrow_mut(); borrows.unborrow_mut(self.handle); } } - -impl<'a, T> GuestType for GuestPtrMut<'a, T> { - fn size() -> u32 { - 4 - } - fn name() -> &'static str { - "GuestPtrMut<...>" - } -} - -impl<'a, T: GuestType> GuestPtrMut<'a, T> { - pub fn read_ptr>(src: &P) -> Result { - let raw_ptr = unsafe { ::std::ptr::read_unaligned(src.ptr() as *const u32) }; - src.mem().ptr_mut(raw_ptr) - } - pub fn write_ptr(ptr: &Self, dest: &GuestPtrMut) { - unsafe { ::std::ptr::write_unaligned(dest.ptr_mut() as *mut u32, ptr.region.start) } - } - - pub fn as_immut(self) -> GuestPtr<'a, T> { - let mem = self.mem; - let start = self.region.start; - drop(self); - mem.ptr(start) - .expect("can borrow just-dropped mutable region as immut") - } -} diff --git a/src/lib.rs b/src/lib.rs index cf36236e83..6fa441546d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -18,43 +18,40 @@ pub mod test { a_lamer_excuse_by_reference: ::memory::GuestPtr, two_layers_of_excuses: ::memory::GuestPtrMut<::memory::GuestPtr>, ) -> Result<(), types::Errno> { - use memory::GuestTypeCopy; - // Read enum value from mutable: - let a_better_excuse = - types::Excuse::read_val(&a_better_excuse_by_reference).map_err(|e| { + let mut a_better_excuse_ref: ::memory::GuestRefMut = + a_better_excuse_by_reference.as_ref_mut().map_err(|e| { eprintln!("a_better_excuse_by_reference error: {}", e); types::Errno::InvalidArg })?; + let a_better_excuse: types::Excuse = *a_better_excuse_ref; // Read enum value from immutable ptr: - let a_lamer_excuse = - types::Excuse::read_val(&a_lamer_excuse_by_reference).map_err(|e| { - eprintln!("a_lamer_excuse_by_reference error: {}", e); - types::Errno::InvalidArg - })?; + let a_lamer_excuse = *a_lamer_excuse_by_reference.as_ref().map_err(|e| { + eprintln!("a_lamer_excuse_by_reference error: {}", e); + types::Errno::InvalidArg + })?; // Write enum to mutable ptr: - types::Excuse::write_val(a_lamer_excuse, &a_better_excuse_by_reference); + *a_better_excuse_ref = a_lamer_excuse; // Read ptr value from mutable ptr: - let one_layer_down = - ::memory::GuestPtr::read_ptr(&two_layers_of_excuses).map_err(|e| { + let mut one_layer_down: ::memory::GuestRefMut<::memory::GuestPtr> = + two_layers_of_excuses.as_ref_mut().map_err(|e| { eprintln!("one_layer_down error: {}", e); types::Errno::InvalidArg })?; // Read enum value from that ptr: - let two_layers_down = types::Excuse::read_val(&one_layer_down).map_err(|e| { - eprintln!("two_layers_down error: {}", e); - types::Errno::InvalidArg - })?; + let two_layers_down: ::memory::GuestRef = + one_layer_down.from_guest().map_err(|e| { + eprintln!("two_layers_down error: {}", e); + types::Errno::InvalidArg + })?; // Write ptr value to mutable ptr: - ::memory::GuestPtr::write_ptr( - &a_better_excuse_by_reference.as_immut(), - &two_layers_of_excuses, - ); + // FIXME this is still impossible... + // two_layers_of_excuses.write_guest(&a_better_excuse_by_reference) println!( "BAZ: excuse: {:?}, better excuse: {:?}, lamer excuse: {:?}, two layers down: {:?}", From c780421c28463e70c016946c004a9db20e781720 Mon Sep 17 00:00:00 2001 From: Pat Hickey Date: Mon, 27 Jan 2020 20:21:53 -0800 Subject: [PATCH 25/86] i think the memory model is fixed now? at least until arrays.... --- crates/generate/src/types.rs | 6 +- crates/memory/src/guest_type.rs | 11 +-- crates/memory/src/lib.rs | 2 +- crates/memory/src/memory.rs | 123 ++++++++++++++++++++++---------- src/lib.rs | 16 ++--- 5 files changed, 105 insertions(+), 53 deletions(-) diff --git a/crates/generate/src/types.rs b/crates/generate/src/types.rs index 4ee337bbc0..a49995b260 100644 --- a/crates/generate/src/types.rs +++ b/crates/generate/src/types.rs @@ -105,12 +105,16 @@ fn define_enum(names: &Names, name: &witx::Id, e: &witx::EnumDatatype) -> TokenS impl ::memory::GuestTypeCopy for #ident {} impl ::memory::GuestTypeClone for #ident { - fn from_guest(location: &::memory::GuestPtr<#ident>) -> Result<#ident, ::memory::GuestError> { + fn read_from_guest(location: &::memory::GuestPtr<#ident>) -> Result<#ident, ::memory::GuestError> { use ::std::convert::TryFrom; let raw: #repr = unsafe { (location.as_raw() as *const #repr).read() }; let val = #ident::try_from(raw)?; Ok(val) } + fn write_to_guest(&self, location: &::memory::GuestPtrMut<#ident>) { + let val: #repr = #repr::from(*self); + unsafe { (location.as_raw() as *mut #repr).write(val) }; + } } } diff --git a/crates/memory/src/guest_type.rs b/crates/memory/src/guest_type.rs index 2b4b3f387a..1d71e72477 100644 --- a/crates/memory/src/guest_type.rs +++ b/crates/memory/src/guest_type.rs @@ -1,4 +1,4 @@ -use crate::{GuestError, GuestPtr}; +use crate::{GuestError, GuestPtr, GuestPtrMut}; pub trait GuestType: Sized { // These are morally the same as Rust ::std::mem::size_of / align_of, but they return @@ -12,11 +12,12 @@ pub trait GuestType: Sized { pub trait GuestTypeCopy: GuestType + Copy {} pub trait GuestTypeClone: GuestType + Clone { - fn from_guest<'a>(location: &GuestPtr<'a, Self>) -> Result; + fn read_from_guest<'a>(location: &GuestPtr<'a, Self>) -> Result; + fn write_to_guest<'a>(&self, location: &GuestPtrMut<'a, Self>); } -pub trait GuestTypeRef<'a>: GuestType { - type Ref; - fn from_guest(location: &GuestPtr<'a, Self>) -> Result; +pub trait GuestTypePtr<'a>: GuestType { + fn read_from_guest(location: &GuestPtr<'a, Self>) -> Result; + fn write_to_guest(&self, location: &GuestPtrMut<'a, Self>); } macro_rules! builtin_type { diff --git a/crates/memory/src/lib.rs b/crates/memory/src/lib.rs index 5aceb413b3..6f2b17ecfc 100644 --- a/crates/memory/src/lib.rs +++ b/crates/memory/src/lib.rs @@ -5,6 +5,6 @@ mod memory; mod region; pub use error::GuestError; -pub use guest_type::{GuestErrorType, GuestType, GuestTypeClone, GuestTypeCopy, GuestTypeRef}; +pub use guest_type::{GuestErrorType, GuestType, GuestTypeClone, GuestTypeCopy, GuestTypePtr}; pub use memory::{GuestMemory, GuestPtr, GuestPtrMut, GuestRef, GuestRefMut}; pub use region::Region; diff --git a/crates/memory/src/memory.rs b/crates/memory/src/memory.rs index 3b6897b8cd..c24a272d6c 100644 --- a/crates/memory/src/memory.rs +++ b/crates/memory/src/memory.rs @@ -3,7 +3,7 @@ use std::marker::PhantomData; use std::rc::Rc; use crate::borrow::{BorrowHandle, GuestBorrows}; -use crate::{GuestError, GuestType, GuestTypeCopy, GuestTypeRef, Region}; +use crate::{GuestError, GuestType, GuestTypeClone, GuestTypeCopy, GuestTypePtr, Region}; pub struct GuestMemory<'a> { ptr: *mut u8, @@ -67,6 +67,9 @@ impl<'a, T: GuestType> GuestPtr<'a, T> { pub fn as_raw(&self) -> *const u8 { (self.mem.ptr as usize + self.region.start as usize) as *const u8 } +} + +impl<'a, T: GuestTypeCopy> GuestPtr<'a, T> { pub fn as_ref(&self) -> Result, GuestError> { T::validate(&self)?; let handle = { @@ -84,6 +87,18 @@ impl<'a, T: GuestType> GuestPtr<'a, T> { } } +impl<'a, T: GuestTypeClone> GuestPtr<'a, T> { + pub fn clone_from_guest(&self) -> Result { + T::read_from_guest(self) + } +} + +impl<'a, T: GuestTypePtr<'a>> GuestPtr<'a, T> { + pub fn read_ptr_from_guest(&self) -> Result { + T::read_from_guest(self) + } +} + impl<'a, T> GuestType for GuestPtr<'a, T> where T: GuestType, @@ -106,17 +121,24 @@ where } } -impl<'a, T> GuestTypeRef<'a> for GuestPtr<'a, T> +// Operations for reading and writing Ptrs to memory: +impl<'a, T> GuestTypePtr<'a> for GuestPtr<'a, T> where T: GuestType, { - type Ref = GuestRef<'a, T>; - fn from_guest(location: &GuestPtr<'a, Self>) -> Result { + fn read_from_guest(location: &GuestPtr<'a, Self>) -> Result { // location is guaranteed to be in GuestMemory and aligned to 4 let raw_ptr: u32 = unsafe { *(location.as_raw() as *const u32) }; // GuestMemory can validate that the raw pointer contents are legal for T: let guest_ptr: GuestPtr<'a, T> = location.mem.ptr(raw_ptr)?; - Ok(guest_ptr.as_ref()?) + Ok(guest_ptr) + } + fn write_to_guest(&self, location: &GuestPtrMut<'a, Self>) { + // location is guaranteed to be in GuestMemory and aligned to 4 + unsafe { + let raw_ptr: *mut u32 = location.as_raw() as *mut u32; + raw_ptr.write(self.region.start); + } } } @@ -139,7 +161,9 @@ impl<'a, T: GuestType> GuestPtrMut<'a, T> { pub fn as_raw(&self) -> *const u8 { self.as_immut().as_raw() } +} +impl<'a, T: GuestTypeCopy> GuestPtrMut<'a, T> { pub fn as_ref(&self) -> Result, GuestError> { self.as_immut().as_ref() } @@ -161,6 +185,25 @@ impl<'a, T: GuestType> GuestPtrMut<'a, T> { } } +impl<'a, T: GuestTypePtr<'a>> GuestPtrMut<'a, T> { + pub fn read_ptr_from_guest(&self) -> Result { + T::read_from_guest(&self.as_immut()) + } + pub fn write_ptr_to_guest(&self, ptr: &T) { + T::write_to_guest(ptr, &self); + } +} + +impl<'a, T: GuestTypeClone> GuestPtrMut<'a, T> { + pub fn clone_from_guest(&self) -> Result { + T::read_from_guest(&self.as_immut()) + } + + pub fn clone_to_guest(&self, val: &T) { + T::write_to_guest(val, &self) + } +} + impl<'a, T> GuestType for GuestPtrMut<'a, T> where T: GuestType, @@ -183,17 +226,24 @@ where } } -impl<'a, T> GuestTypeRef<'a> for GuestPtrMut<'a, T> +// Reading and writing GuestPtrMuts to memory: +impl<'a, T> GuestTypePtr<'a> for GuestPtrMut<'a, T> where T: GuestType, { - type Ref = GuestRefMut<'a, T>; - fn from_guest(location: &GuestPtr<'a, Self>) -> Result { + fn read_from_guest(location: &GuestPtr<'a, Self>) -> Result { // location is guaranteed to be in GuestMemory and aligned to 4 let raw_ptr: u32 = unsafe { *(location.as_raw() as *const u32) }; // GuestMemory can validate that the raw pointer contents are legal for T: let guest_ptr_mut: GuestPtrMut<'a, T> = location.mem.ptr_mut(raw_ptr)?; - Ok(guest_ptr_mut.as_ref_mut()?) + Ok(guest_ptr_mut) + } + fn write_to_guest(&self, location: &GuestPtrMut<'a, Self>) { + // location is guaranteed to be in GuestMemory and aligned to 4 + unsafe { + let raw_ptr: *mut u32 = location.as_raw() as *mut u32; + raw_ptr.write(self.region.start); + } } } @@ -204,6 +254,16 @@ pub struct GuestRef<'a, T> { type_: PhantomData, } +impl<'a, T> GuestRef<'a, T> { + pub fn as_ptr(&self) -> GuestPtr<'a, T> { + GuestPtr { + mem: self.mem, + region: self.region, + type_: self.type_, + } + } +} + impl<'a, T> ::std::ops::Deref for GuestRef<'a, T> where T: GuestTypeCopy, @@ -218,20 +278,6 @@ where } } -impl<'a, T> GuestRef<'a, T> -where - T: GuestTypeRef<'a>, -{ - pub fn from_guest(&self) -> Result { - let ptr = GuestPtr { - mem: self.mem, - region: self.region, - type_: self.type_, - }; - GuestTypeRef::from_guest(&ptr) - } -} - impl<'a, T> Drop for GuestRef<'a, T> { fn drop(&mut self) { let mut borrows = self.mem.borrows.borrow_mut(); @@ -246,6 +292,23 @@ pub struct GuestRefMut<'a, T> { type_: PhantomData, } +impl<'a, T> GuestRefMut<'a, T> { + pub fn as_ptr(&self) -> GuestPtr<'a, T> { + GuestPtr { + mem: self.mem, + region: self.region, + type_: self.type_, + } + } + pub fn as_ptr_mut(&self) -> GuestPtrMut<'a, T> { + GuestPtrMut { + mem: self.mem, + region: self.region, + type_: self.type_, + } + } +} + impl<'a, T> ::std::ops::Deref for GuestRefMut<'a, T> where T: GuestTypeCopy, @@ -273,20 +336,6 @@ where } } -impl<'a, T> GuestRefMut<'a, T> -where - T: GuestTypeRef<'a>, -{ - pub fn from_guest(&self) -> Result { - let ptr = GuestPtr { - mem: self.mem, - region: self.region, - type_: self.type_, - }; - GuestTypeRef::from_guest(&ptr) - } -} - impl<'a, T> Drop for GuestRefMut<'a, T> { fn drop(&mut self) { let mut borrows = self.mem.borrows.borrow_mut(); diff --git a/src/lib.rs b/src/lib.rs index 6fa441546d..b907f0cb1e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -36,22 +36,20 @@ pub mod test { *a_better_excuse_ref = a_lamer_excuse; // Read ptr value from mutable ptr: - let mut one_layer_down: ::memory::GuestRefMut<::memory::GuestPtr> = - two_layers_of_excuses.as_ref_mut().map_err(|e| { + let one_layer_down: ::memory::GuestPtr = + two_layers_of_excuses.read_ptr_from_guest().map_err(|e| { eprintln!("one_layer_down error: {}", e); types::Errno::InvalidArg })?; // Read enum value from that ptr: - let two_layers_down: ::memory::GuestRef = - one_layer_down.from_guest().map_err(|e| { - eprintln!("two_layers_down error: {}", e); - types::Errno::InvalidArg - })?; + let two_layers_down: types::Excuse = *one_layer_down.as_ref().map_err(|e| { + eprintln!("two_layers_down error: {}", e); + types::Errno::InvalidArg + })?; // Write ptr value to mutable ptr: - // FIXME this is still impossible... - // two_layers_of_excuses.write_guest(&a_better_excuse_by_reference) + two_layers_of_excuses.write_ptr_to_guest(&a_better_excuse_by_reference.as_immut()); println!( "BAZ: excuse: {:?}, better excuse: {:?}, lamer excuse: {:?}, two layers down: {:?}", From 373560b88a1336aa8e24d9ed36f2186f20d4faf2 Mon Sep 17 00:00:00 2001 From: Pat Hickey Date: Mon, 27 Jan 2020 20:28:27 -0800 Subject: [PATCH 26/86] and now funcs work again --- crates/generate/src/funcs.rs | 18 ++++++++++-------- crates/generate/src/lib.rs | 2 +- crates/memory/src/guest_type.rs | 1 + 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/crates/generate/src/funcs.rs b/crates/generate/src/funcs.rs index 6f4eebe592..9a3c1d4719 100644 --- a/crates/generate/src/funcs.rs +++ b/crates/generate/src/funcs.rs @@ -74,8 +74,7 @@ pub fn define_func(names: &Names, func: &witx::InterfaceFunc) -> TokenStream { let marshal_args = func .params .iter() - //.map(|p| marshal_arg(names, p, error_handling.clone())); - .map(|_p| quote!(unimplemented!(); )); // FIXME + .map(|p| marshal_arg(names, p, error_handling.clone())); let trait_args = func .params .iter() @@ -99,8 +98,7 @@ pub fn define_func(names: &Names, func: &witx::InterfaceFunc) -> TokenStream { .results .iter() .skip(1) - //.map(|result| marshal_result(names, result, error_handling.clone())); - .map(|_result| (quote!(unimplemented!();), quote!(unimplemented!();))); // FIXME + .map(|result| marshal_result(names, result, error_handling.clone())); let marshal_rets_pre = marshal_rets.clone().map(|(pre, _post)| pre); let marshal_rets_post = marshal_rets.map(|(_pre, post)| post); @@ -218,8 +216,13 @@ fn marshal_result( // core type is given func_ptr_binding name. let ptr_name = names.func_ptr_binding(&result.name); let pre = quote! { - let #ptr_name = match memory.ptr_mut::<#pointee_type>(#ptr_name as u32) { - Ok(p) => p, + let mut #ptr_name = match memory.ptr_mut::<#pointee_type>(#ptr_name as u32) { + Ok(p) => match p.as_ref_mut() { + Ok(r) => r, + Err(e) => { + #error_handling + } + }, Err(e) => { #error_handling } @@ -228,8 +231,7 @@ fn marshal_result( // trait binding returns func_param name. let val_name = names.func_param(&result.name); let post = quote! { - use ::memory::GuestTypeCopy; - #pointee_type::write_val(#val_name, &#ptr_name); + *#ptr_name = #val_name; }; (pre, post) }; diff --git a/crates/generate/src/lib.rs b/crates/generate/src/lib.rs index 15d07cba0c..49d9289d58 100644 --- a/crates/generate/src/lib.rs +++ b/crates/generate/src/lib.rs @@ -34,7 +34,7 @@ pub fn from_witx(args: TokenStream) -> TokenStream { mod #modname { use super::WasiCtx; use super::types::*; - // #(#fs)* + #(#fs)* #modtrait } diff --git a/crates/memory/src/guest_type.rs b/crates/memory/src/guest_type.rs index 1d71e72477..6c2f98795e 100644 --- a/crates/memory/src/guest_type.rs +++ b/crates/memory/src/guest_type.rs @@ -37,6 +37,7 @@ macro_rules! builtin_type { Ok(()) } } + impl GuestTypeCopy for $t {} )* }; } From 62e00434b058c5ab6e97b86044dfa09e3c36e9a1 Mon Sep 17 00:00:00 2001 From: Pat Hickey Date: Tue, 28 Jan 2020 15:45:52 -0800 Subject: [PATCH 27/86] structs implementing Copy are scaffolded out. todo: need an unsafe method for casting pointers in order to validate contents via recursive descent --- crates/generate/src/funcs.rs | 31 +++++++++++++-- crates/generate/src/module_trait.rs | 7 +++- crates/generate/src/names.rs | 4 ++ crates/generate/src/types.rs | 62 ++++++++++++++++++++++++++++- src/lib.rs | 5 +++ test.witx | 10 +++++ 6 files changed, 113 insertions(+), 6 deletions(-) diff --git a/crates/generate/src/funcs.rs b/crates/generate/src/funcs.rs index 9a3c1d4719..fe13723f2c 100644 --- a/crates/generate/src/funcs.rs +++ b/crates/generate/src/funcs.rs @@ -2,6 +2,7 @@ use proc_macro2::TokenStream; use quote::quote; use crate::names::Names; +use crate::types::struct_is_copy; // FIXME need to template what argument is required to an import function - some context // struct (e.g. WasiCtx) should be provided at the invocation of the `gen` proc macro. @@ -75,10 +76,14 @@ pub fn define_func(names: &Names, func: &witx::InterfaceFunc) -> TokenStream { .params .iter() .map(|p| marshal_arg(names, p, error_handling.clone())); - let trait_args = func - .params - .iter() - .map(|param| names.func_param(¶m.name)); + let trait_args = func.params.iter().map(|param| { + let name = names.func_param(¶m.name); + match param.tref.type_().passed_by() { + witx::TypePassedBy::Value { .. } => quote!(#name), + witx::TypePassedBy::Pointer { .. } => quote!(&#name), + witx::TypePassedBy::PointerLengthPair { .. } => unimplemented!(), + } + }); let (trait_rets, trait_bindings) = if func.results.len() < 2 { (quote!({}), quote!(_)) @@ -200,6 +205,24 @@ fn marshal_arg( }; } } + witx::Type::Struct(s) if struct_is_copy(&s) => { + let pointee_type = names.type_ref(tref); + let arg_name = names.func_ptr_binding(¶m.name); + let name = names.func_param(¶m.name); + quote! { + let #name = match memory.ptr::<#pointee_type>(#arg_name as u32) { + Ok(p) => match p.as_ref() { + Ok(r) => r, + Err(e) => { + #error_handling + } + }, + Err(e) => { + #error_handling + } + }; + } + } _ => unimplemented!("argument type marshalling"), } } diff --git a/crates/generate/src/module_trait.rs b/crates/generate/src/module_trait.rs index a25addd710..19669fa410 100644 --- a/crates/generate/src/module_trait.rs +++ b/crates/generate/src/module_trait.rs @@ -10,7 +10,12 @@ pub fn define_module_trait(names: &Names, m: &Module) -> TokenStream { let funcname = names.func(&f.name); let args = f.params.iter().map(|arg| { let arg_name = names.func_param(&arg.name); - let arg_type = names.type_ref(&arg.tref); + let arg_typename = names.type_ref(&arg.tref); + let arg_type = match arg.tref.type_().passed_by() { + witx::TypePassedBy::Value { .. } => quote!(#arg_typename), + witx::TypePassedBy::Pointer { .. } => quote!(&#arg_typename), + witx::TypePassedBy::PointerLengthPair { .. } => unimplemented!(), + }; quote!(#arg_name: #arg_type) }); let rets = f diff --git a/crates/generate/src/names.rs b/crates/generate/src/names.rs index acbb3b00aa..558a7e28dc 100644 --- a/crates/generate/src/names.rs +++ b/crates/generate/src/names.rs @@ -72,6 +72,10 @@ impl Names { } } + pub fn struct_member(&self, id: &Id) -> Ident { + format_ident!("{}", id.as_str().to_snake_case()) + } + pub fn module(&self, id: &Id) -> Ident { format_ident!("{}", id.as_str().to_snake_case()) } diff --git a/crates/generate/src/types.rs b/crates/generate/src/types.rs index a49995b260..c5185a51a8 100644 --- a/crates/generate/src/types.rs +++ b/crates/generate/src/types.rs @@ -2,6 +2,7 @@ use crate::names::Names; use proc_macro2::TokenStream; use quote::quote; +use witx::Layout; pub fn define_datatype(names: &Names, namedtype: &witx::NamedType) -> TokenStream { match &namedtype.tref { @@ -10,7 +11,13 @@ pub fn define_datatype(names: &Names, namedtype: &witx::NamedType) -> TokenStrea witx::Type::Enum(e) => define_enum(names, &namedtype.name, &e), witx::Type::Int(_) => unimplemented!("int types"), witx::Type::Flags(_) => unimplemented!("flag types"), - witx::Type::Struct(_) => unimplemented!("struct types"), + witx::Type::Struct(s) => { + if struct_is_copy(s) { + define_copy_struct(names, &namedtype.name, &s) + } else { + unimplemented!("non-Copy struct") + } + } witx::Type::Union(_) => unimplemented!("union types"), witx::Type::Handle(_h) => unimplemented!("handle types"), witx::Type::Builtin(b) => define_builtin(names, &namedtype.name, *b), @@ -126,6 +133,59 @@ fn define_builtin(names: &Names, name: &witx::Id, builtin: witx::BuiltinType) -> quote!(pub type #ident = #built;) } +pub fn struct_is_copy(s: &witx::StructDatatype) -> bool { + s.members.iter().all(|m| match &*m.tref.type_() { + witx::Type::Struct(s) => struct_is_copy(&s), + witx::Type::Builtin(b) => match &*b { + witx::BuiltinType::String => false, + _ => true, + }, + witx::Type::ConstPointer { .. } + | witx::Type::Pointer { .. } + | witx::Type::Array { .. } + | witx::Type::Union { .. } => false, + witx::Type::Enum { .. } + | witx::Type::Int { .. } + | witx::Type::Flags { .. } + | witx::Type::Handle { .. } => true, + }) +} + +fn define_copy_struct(names: &Names, name: &witx::Id, s: &witx::StructDatatype) -> TokenStream { + let ident = names.type_(name); + let member_decls = s.members.iter().map(|m| { + let name = names.struct_member(&m.name); + let type_ = names.type_ref(&m.tref); + quote!(pub #name: #type_) + }); + let size = s.mem_size_align().size as u32; + let align = s.mem_size_align().align as u32; + + quote! { + #[repr(C)] + #[derive(Copy, Clone, Debug, ::std::hash::Hash, Eq, PartialEq)] + pub struct #ident { + #(#member_decls),* + } + + impl ::memory::GuestType for #ident { + fn size() -> u32 { + #size + } + fn align() -> u32 { + #align + } + fn name() -> String { + stringify!(#ident).to_owned() + } + fn validate(_ptr: &::memory::GuestPtr<#ident>) -> Result<(), ::memory::GuestError> { + Ok(()) // FIXME + } + } + impl ::memory::GuestTypeCopy for #ident {} + } +} + fn int_repr_tokens(int_repr: witx::IntRepr) -> TokenStream { match int_repr { witx::IntRepr::U8 => quote!(u8), diff --git a/src/lib.rs b/src/lib.rs index b907f0cb1e..b1898b77c4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -62,6 +62,11 @@ pub mod test { println!("bat: {}", an_int); Ok((an_int as f32) * 2.0) } + + fn sum_of_pair(&mut self, an_pair: &types::PairInts) -> Result { + println!("sum of pair: {:?}", an_pair); + Ok(an_pair.first as i64 + an_pair.second as i64) + } } // Errno is used as a first return value in the functions above, therefore // it must implement GuestErrorType with type Context = WasiCtx. diff --git a/test.witx b/test.witx index 422d99bd72..af5af5441f 100644 --- a/test.witx +++ b/test.witx @@ -14,6 +14,11 @@ $traffic $sleeping)) +(typename $pair_ints + (struct + (field $first s32) + (field $second s32))) + (module $foo (@interface func (export "bar") (param $an_int u32) @@ -29,4 +34,9 @@ (param $an_int u32) (result $error $errno) (result $doubled_it f32)) + + (@interface func (export "sum_of_pair") + (param $an_pair $pair_ints) + (result $error $errno) + (result $doubled s64)) ) From 35d93739769e91cca52102d3696d5b289aa77c34 Mon Sep 17 00:00:00 2001 From: Pat Hickey Date: Tue, 28 Jan 2020 16:34:34 -0800 Subject: [PATCH 28/86] we now validate Copy structs --- crates/generate/src/types.rs | 15 +++++++++++---- crates/memory/src/memory.rs | 9 +++++++++ test.witx | 1 - 3 files changed, 20 insertions(+), 5 deletions(-) diff --git a/crates/generate/src/types.rs b/crates/generate/src/types.rs index c5185a51a8..13338769e8 100644 --- a/crates/generate/src/types.rs +++ b/crates/generate/src/types.rs @@ -153,13 +153,19 @@ pub fn struct_is_copy(s: &witx::StructDatatype) -> bool { fn define_copy_struct(names: &Names, name: &witx::Id, s: &witx::StructDatatype) -> TokenStream { let ident = names.type_(name); + let size = s.mem_size_align().size as u32; + let align = s.mem_size_align().align as u32; + let member_decls = s.members.iter().map(|m| { let name = names.struct_member(&m.name); let type_ = names.type_ref(&m.tref); quote!(pub #name: #type_) }); - let size = s.mem_size_align().size as u32; - let align = s.mem_size_align().align as u32; + let member_valids = s.member_layout().into_iter().map(|ml| { + let type_ = names.type_ref(&ml.member.tref); + let offset = ml.offset as u32; + quote!( #type_::validate(&ptr.cast(#offset)?)?; ) + }); quote! { #[repr(C)] @@ -178,8 +184,9 @@ fn define_copy_struct(names: &Names, name: &witx::Id, s: &witx::StructDatatype) fn name() -> String { stringify!(#ident).to_owned() } - fn validate(_ptr: &::memory::GuestPtr<#ident>) -> Result<(), ::memory::GuestError> { - Ok(()) // FIXME + fn validate(ptr: &::memory::GuestPtr<#ident>) -> Result<(), ::memory::GuestError> { + #(#member_valids)* + Ok(()) } } impl ::memory::GuestTypeCopy for #ident {} diff --git a/crates/memory/src/memory.rs b/crates/memory/src/memory.rs index c24a272d6c..d33f4d707c 100644 --- a/crates/memory/src/memory.rs +++ b/crates/memory/src/memory.rs @@ -67,6 +67,15 @@ impl<'a, T: GuestType> GuestPtr<'a, T> { pub fn as_raw(&self) -> *const u8 { (self.mem.ptr as usize + self.region.start as usize) as *const u8 } + + pub fn elem(&self, elements: i32) -> Result, GuestError> { + self.mem + .ptr(self.region.start + (elements * self.region.len as i32) as u32) + } + + pub fn cast(&self, offset: u32) -> Result, GuestError> { + self.mem.ptr(self.region.start + offset) + } } impl<'a, T: GuestTypeCopy> GuestPtr<'a, T> { diff --git a/test.witx b/test.witx index af5af5441f..87e11396d7 100644 --- a/test.witx +++ b/test.witx @@ -34,7 +34,6 @@ (param $an_int u32) (result $error $errno) (result $doubled_it f32)) - (@interface func (export "sum_of_pair") (param $an_pair $pair_ints) (result $error $errno) From 814dd19488d536ee3a3b7efea9c3804fdcd5feec Mon Sep 17 00:00:00 2001 From: Pat Hickey Date: Tue, 28 Jan 2020 18:17:48 -0800 Subject: [PATCH 29/86] structs that contain pointers work! --- crates/generate/src/funcs.rs | 18 ++++++ crates/generate/src/types.rs | 119 ++++++++++++++++++++++++++++++++++- crates/memory/src/memory.rs | 8 +++ src/lib.rs | 9 +++ test.witx | 9 +++ 5 files changed, 162 insertions(+), 1 deletion(-) diff --git a/crates/generate/src/funcs.rs b/crates/generate/src/funcs.rs index fe13723f2c..15f3a8fae5 100644 --- a/crates/generate/src/funcs.rs +++ b/crates/generate/src/funcs.rs @@ -223,6 +223,24 @@ fn marshal_arg( }; } } + witx::Type::Struct(s) if !struct_is_copy(&s) => { + let pointee_type = names.type_ref(tref); + let arg_name = names.func_ptr_binding(¶m.name); + let name = names.func_param(¶m.name); + quote! { + let #name = match memory.ptr_mut::<#pointee_type>(#arg_name as u32) { + Ok(p) => match p.read_ptr_from_guest() { + Ok(r) => r, + Err(e) => { + #error_handling + } + }, + Err(e) => { + #error_handling + } + }; + } + } _ => unimplemented!("argument type marshalling"), } } diff --git a/crates/generate/src/types.rs b/crates/generate/src/types.rs index 13338769e8..21dcaaa4bb 100644 --- a/crates/generate/src/types.rs +++ b/crates/generate/src/types.rs @@ -15,7 +15,7 @@ pub fn define_datatype(names: &Names, namedtype: &witx::NamedType) -> TokenStrea if struct_is_copy(s) { define_copy_struct(names, &namedtype.name, &s) } else { - unimplemented!("non-Copy struct") + define_ptr_struct(names, &namedtype.name, &s) } } witx::Type::Union(_) => unimplemented!("union types"), @@ -193,6 +193,123 @@ fn define_copy_struct(names: &Names, name: &witx::Id, s: &witx::StructDatatype) } } +fn define_ptr_struct(names: &Names, name: &witx::Id, s: &witx::StructDatatype) -> TokenStream { + let ident = names.type_(name); + let size = s.mem_size_align().size as u32; + let align = s.mem_size_align().align as u32; + + let member_names = s.members.iter().map(|m| names.struct_member(&m.name)); + let member_decls = s.members.iter().map(|m| { + let name = names.struct_member(&m.name); + 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::Pointer(pointee) => { + let pointee_type = names.type_ref(&pointee); + quote!(::memory::GuestPtrMut<'a, #pointee_type>) + } + witx::Type::ConstPointer(pointee) => { + let pointee_type = names.type_ref(&pointee); + quote!(::memory::GuestPtr<'a, #pointee_type>) + } + _ => unimplemented!("other anonymous struct members"), + }, + }; + quote!(pub #name: #type_) + }); + let member_valids = s.member_layout().into_iter().map(|ml| { + 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::Pointer(pointee) => { + let pointee_type = names.type_ref(&pointee); + quote!(::memory::GuestPtrMut::<#pointee_type>) + } + witx::Type::ConstPointer(pointee) => { + let pointee_type = names.type_ref(&pointee); + quote!(::memory::GuestPtr::<#pointee_type>) + } + _ => unimplemented!("other anonymous struct members"), + }, + }; + let offset = ml.offset as u32; + quote!( #type_::validate(&ptr.cast(#offset)?)?; ) + }); + + let member_reads = s.member_layout().into_iter().map(|ml| { + let name = names.struct_member(&ml.member.name); + let offset = ml.offset as u32; + match &ml.member.tref { + witx::TypeRef::Name(nt) => { + let type_ = names.type_(&nt.name); + quote! { + let #name = #type_::read_from_guest(&location.cast(#offset)?)?; + } + } + witx::TypeRef::Value(ty) => match &**ty { + witx::Type::Builtin(builtin) => { + let type_ = names.builtin_type(*builtin); + quote! { + let #name = #type_::read_from_guest(&location.cast(#offset)?)?; + } + } + witx::Type::Pointer(pointee) => { + let pointee_type = names.type_ref(&pointee); + quote! { + let #name = ::memory::GuestPtrMut::<#pointee_type>::read_from_guest(&location.cast(#offset)?)?; + } + } + witx::Type::ConstPointer(pointee) => { + let pointee_type = names.type_ref(&pointee); + quote! { + let #name = ::memory::GuestPtr::<#pointee_type>::read_from_guest(&location.cast(#offset)?)?; + } + } + _ => unimplemented!("other anonymous struct members"), + }, + } + }); + + let member_writes = s.member_layout().into_iter().map(|ml| { + let name = names.struct_member(&ml.member.name); + let offset = ml.offset as u32; + quote!( self.#name.write_to_guest(&location.cast(#offset).expect("cast to inner member")); ) + }); + + quote! { + #[derive(Clone)] + pub struct #ident<'a> { + #(#member_decls),* + } + + impl<'a> ::memory::GuestType for #ident<'a> { + fn size() -> u32 { + #size + } + fn align() -> u32 { + #align + } + fn name() -> String { + stringify!(#ident).to_owned() + } + fn validate(ptr: &::memory::GuestPtr<#ident>) -> Result<(), ::memory::GuestError> { + #(#member_valids)* + Ok(()) + } + } + impl<'a> ::memory::GuestTypePtr<'a> for #ident<'a> { + fn read_from_guest(location: &::memory::GuestPtr<'a, #ident<'a>>) -> Result<#ident<'a>, ::memory::GuestError> { + #(#member_reads)* + Ok(#ident { #(#member_names),* }) + } + fn write_to_guest(&self, location: &::memory::GuestPtrMut<'a, Self>) { + #(#member_writes)* + } + } + } +} fn int_repr_tokens(int_repr: witx::IntRepr) -> TokenStream { match int_repr { witx::IntRepr::U8 => quote!(u8), diff --git a/crates/memory/src/memory.rs b/crates/memory/src/memory.rs index d33f4d707c..f301647c14 100644 --- a/crates/memory/src/memory.rs +++ b/crates/memory/src/memory.rs @@ -170,6 +170,14 @@ impl<'a, T: GuestType> GuestPtrMut<'a, T> { pub fn as_raw(&self) -> *const u8 { self.as_immut().as_raw() } + pub fn elem(&self, elements: i32) -> Result, GuestError> { + self.mem + .ptr_mut(self.region.start + (elements * self.region.len as i32) as u32) + } + + pub fn cast(&self, offset: u32) -> Result, GuestError> { + self.mem.ptr_mut(self.region.start + offset) + } } impl<'a, T: GuestTypeCopy> GuestPtrMut<'a, T> { diff --git a/src/lib.rs b/src/lib.rs index b1898b77c4..c28934faf9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -67,6 +67,15 @@ pub mod test { println!("sum of pair: {:?}", an_pair); Ok(an_pair.first as i64 + an_pair.second as i64) } + fn sum_of_pair_of_ptrs( + &mut self, + an_pair: &types::PairIntPtrs, + ) -> Result { + let first = *an_pair.first.as_ref().unwrap(); + let second = *an_pair.second.as_ref().unwrap(); + println!("sum of pair of ptrs: {} + {}", first, second); + Ok(first as i64 + second as i64) + } } // Errno is used as a first return value in the functions above, therefore // it must implement GuestErrorType with type Context = WasiCtx. diff --git a/test.witx b/test.witx index 87e11396d7..3fa2ae5ac7 100644 --- a/test.witx +++ b/test.witx @@ -19,6 +19,11 @@ (field $first s32) (field $second s32))) +(typename $pair_int_ptrs + (struct + (field $first (@witx const_pointer s32)) + (field $second (@witx const_pointer s32)))) + (module $foo (@interface func (export "bar") (param $an_int u32) @@ -38,4 +43,8 @@ (param $an_pair $pair_ints) (result $error $errno) (result $doubled s64)) + (@interface func (export "sum_of_pair_of_ptrs") + (param $an_pair $pair_int_ptrs) + (result $error $errno) + (result $doubled s64)) ) From e2079c085dbb2df503c429bddb590545bb8db3bc Mon Sep 17 00:00:00 2001 From: Pat Hickey Date: Wed, 29 Jan 2020 13:18:57 -0800 Subject: [PATCH 30/86] report which field a struct validation error occured in --- crates/generate/src/types.rs | 34 ++++++++++++++++++++++++++++++++-- crates/memory/src/error.rs | 7 +++++++ 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/crates/generate/src/types.rs b/crates/generate/src/types.rs index 21dcaaa4bb..096015ae21 100644 --- a/crates/generate/src/types.rs +++ b/crates/generate/src/types.rs @@ -164,7 +164,22 @@ fn define_copy_struct(names: &Names, name: &witx::Id, s: &witx::StructDatatype) let member_valids = s.member_layout().into_iter().map(|ml| { let type_ = names.type_ref(&ml.member.tref); let offset = ml.offset as u32; - quote!( #type_::validate(&ptr.cast(#offset)?)?; ) + let fieldname = names.struct_member(&ml.member.name); + quote! { + #type_::validate( + &ptr.cast(#offset).map_err(|e| + ::memory::GuestError::InField{ + typename: stringify!(#ident).to_owned(), + field: stringify!(#fieldname).to_owned(), + err: Box::new(e), + })? + ).map_err(|e| + ::memory::GuestError::InField { + typename: stringify!(#ident).to_owned(), + field: stringify!(#fieldname).to_owned(), + err: Box::new(e), + })?; + } }); quote! { @@ -235,7 +250,22 @@ fn define_ptr_struct(names: &Names, name: &witx::Id, s: &witx::StructDatatype) - }, }; let offset = ml.offset as u32; - quote!( #type_::validate(&ptr.cast(#offset)?)?; ) + let fieldname = names.struct_member(&ml.member.name); + quote! { + #type_::validate( + &ptr.cast(#offset).map_err(|e| + ::memory::GuestError::InField{ + typename: stringify!(#ident).to_owned(), + field: stringify!(#fieldname).to_owned(), + err: Box::new(e), + })? + ).map_err(|e| + ::memory::GuestError::InField { + typename: stringify!(#ident).to_owned(), + field: stringify!(#fieldname).to_owned(), + err: Box::new(e), + })?; + } }); let member_reads = s.member_layout().into_iter().map(|ml| { diff --git a/crates/memory/src/error.rs b/crates/memory/src/error.rs index b1ed3b49b0..afbed26416 100644 --- a/crates/memory/src/error.rs +++ b/crates/memory/src/error.rs @@ -11,4 +11,11 @@ pub enum GuestError { PtrNotAligned(Region, u32), #[error("Pointer already borrowed: {0:?}")] PtrBorrowed(Region), + #[error("In {typename}.{field}:")] + InField { + typename: String, + field: String, + #[source] + err: Box, + }, } From 0ba8e73184c5faec768a0a8670aab5ab68507b1e Mon Sep 17 00:00:00 2001 From: Pat Hickey Date: Thu, 30 Jan 2020 14:29:53 -0800 Subject: [PATCH 31/86] change proc macro argument parsing to use `syn` --- crates/generate/Cargo.toml | 1 + crates/generate/src/{parse.rs => config.rs} | 37 +++++++++++++++++++-- crates/generate/src/lib.rs | 10 +++--- src/lib.rs | 2 +- 4 files changed, 41 insertions(+), 9 deletions(-) rename crates/generate/src/{parse.rs => config.rs} (54%) diff --git a/crates/generate/Cargo.toml b/crates/generate/Cargo.toml index 2b89101bd3..886fea8b3b 100644 --- a/crates/generate/Cargo.toml +++ b/crates/generate/Cargo.toml @@ -14,3 +14,4 @@ quote = "1.0" proc-macro2 = "1.0" heck = "0.3" anyhow = "1" +syn = { version = "1.0", features = ["full"] } diff --git a/crates/generate/src/parse.rs b/crates/generate/src/config.rs similarity index 54% rename from crates/generate/src/parse.rs rename to crates/generate/src/config.rs index 65c8a190cc..3cb1cf5c09 100644 --- a/crates/generate/src/parse.rs +++ b/crates/generate/src/config.rs @@ -1,7 +1,37 @@ -use anyhow::{bail, Result}; -use proc_macro2::{Literal, TokenStream, TokenTree}; +use std::path::PathBuf; -pub fn witx_paths(args: TokenStream) -> Result> { +use syn::{ + bracketed, + parse::{Parse, ParseStream}, + punctuated::Punctuated, + token, LitStr, Result, Token, +}; + +pub struct Config { + _bracket_token: token::Bracket, + path_lits: Punctuated, +} + +impl Config { + pub fn witx_paths(&self) -> Vec { + self.path_lits + .iter() + .map(|lit| PathBuf::from(lit.value())) + .collect() + } +} + +impl Parse for Config { + fn parse(input: ParseStream) -> Result { + let content; + Ok(Config { + _bracket_token: bracketed!(content in input), + path_lits: content.parse_terminated(Parse::parse)?, + }) + } +} + +/* let arg_strings = args .into_iter() .map(|arg| match arg { @@ -31,3 +61,4 @@ fn string_literal(literal: Literal) -> Result { } Ok(trimmed) } +*/ diff --git a/crates/generate/src/lib.rs b/crates/generate/src/lib.rs index 49d9289d58..9b66c9e1e8 100644 --- a/crates/generate/src/lib.rs +++ b/crates/generate/src/lib.rs @@ -1,15 +1,16 @@ extern crate proc_macro; +mod config; mod funcs; mod module_trait; mod names; -mod parse; mod types; use proc_macro::TokenStream; -use proc_macro2::TokenStream as TokenStream2; use quote::quote; +use syn::parse_macro_input; +use config::Config; use funcs::define_func; use module_trait::define_module_trait; use names::Names; @@ -17,12 +18,11 @@ use types::define_datatype; #[proc_macro] pub fn from_witx(args: TokenStream) -> TokenStream { - let args = TokenStream2::from(args); - let witx_paths = parse::witx_paths(args).expect("parsing macro arguments"); + let config = parse_macro_input!(args as Config); let names = Names::new(); // TODO parse the names from the invocation of the macro, or from a file? - let doc = witx::load(&witx_paths).expect("loading witx"); + let doc = witx::load(&config.witx_paths()).expect("loading witx"); let types = doc.typenames().map(|t| define_datatype(&names, &t)); diff --git a/src/lib.rs b/src/lib.rs index c28934faf9..65356c89a5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,6 @@ pub mod test { // FIXME: parameterize macro on what ctx type is used here - generate::from_witx!("test.witx"); + generate::from_witx!(["test.witx"]); pub struct WasiCtx { guest_errors: Vec<::memory::GuestError>, From 29c3ef9d091a9e71c403f58bc13330c2dc32ae41 Mon Sep 17 00:00:00 2001 From: Pat Hickey Date: Thu, 30 Jan 2020 16:38:16 -0800 Subject: [PATCH 32/86] we now parse witx paths and the ctx type name in the macro invocation --- crates/generate/src/config.rs | 122 ++++++++++++++++++++++------------ crates/generate/src/funcs.rs | 6 +- crates/generate/src/lib.rs | 7 +- crates/generate/src/names.rs | 11 ++- src/lib.rs | 5 +- 5 files changed, 96 insertions(+), 55 deletions(-) diff --git a/crates/generate/src/config.rs b/crates/generate/src/config.rs index 3cb1cf5c09..9e7848e82c 100644 --- a/crates/generate/src/config.rs +++ b/crates/generate/src/config.rs @@ -1,64 +1,98 @@ use std::path::PathBuf; +use proc_macro2::Span; use syn::{ - bracketed, + braced, bracketed, parse::{Parse, ParseStream}, punctuated::Punctuated, - token, LitStr, Result, Token, + Error, Ident, LitStr, Result, Token, }; +#[derive(Debug, Clone)] pub struct Config { - _bracket_token: token::Bracket, - path_lits: Punctuated, + pub witx: WitxConf, + pub ctx: CtxConf, +} + +enum ConfigField { + Witx(WitxConf), + Ctx(CtxConf), +} + +impl Parse for ConfigField { + fn parse(input: ParseStream) -> Result { + let id: Ident = input.parse()?; + let _colon: Token![:] = input.parse()?; + match id.to_string().as_ref() { + "witx" => Ok(ConfigField::Witx(input.parse()?)), + "ctx" => Ok(ConfigField::Ctx(input.parse()?)), + _ => Err(Error::new(id.span(), "expected `witx` or `ctx`")), + } + } } impl Config { - pub fn witx_paths(&self) -> Vec { - self.path_lits - .iter() - .map(|lit| PathBuf::from(lit.value())) - .collect() + fn build(fields: impl Iterator, err_loc: Span) -> Result { + let mut witx = None; + let mut ctx = None; + for f in fields { + match f { + ConfigField::Witx(c) => { + witx = Some(c); + } + ConfigField::Ctx(c) => { + ctx = Some(c); + } + } + } + Ok(Config { + witx: witx + .take() + .ok_or_else(|| Error::new(err_loc, "`witx` field required"))?, + ctx: ctx + .take() + .ok_or_else(|| Error::new(err_loc, "`ctx` field required"))?, + }) } } impl Parse for Config { + fn parse(input: ParseStream) -> Result { + let contents; + let _lbrace = braced!(contents in input); + let fields: Punctuated = + contents.parse_terminated(ConfigField::parse)?; + Ok(Config::build(fields.into_iter(), input.span())?) + } +} + +#[derive(Debug, Clone)] +pub struct WitxConf { + pub paths: Vec, +} + +impl Parse for WitxConf { fn parse(input: ParseStream) -> Result { let content; - Ok(Config { - _bracket_token: bracketed!(content in input), - path_lits: content.parse_terminated(Parse::parse)?, + let _ = bracketed!(content in input); + let path_lits: Punctuated = content.parse_terminated(Parse::parse)?; + let paths: Vec = path_lits + .iter() + .map(|lit| PathBuf::from(lit.value())) + .collect(); + Ok(WitxConf { paths }) + } +} + +#[derive(Debug, Clone)] +pub struct CtxConf { + pub name: Ident, +} + +impl Parse for CtxConf { + fn parse(input: ParseStream) -> Result { + Ok(CtxConf { + name: input.parse()?, }) } } - -/* - let arg_strings = args - .into_iter() - .map(|arg| match arg { - TokenTree::Literal(lit) => string_literal(lit), - _ => bail!("expected string literal, got: {:?}", arg), - }) - .collect::>>()?; - - if arg_strings.is_empty() { - bail!("expected at least one argument"); - } - Ok(arg_strings) -} - -fn string_literal(literal: Literal) -> Result { - let s = literal.to_string(); - if !s.starts_with('"') || !s.ends_with('"') { - bail!("string literal must be enclosed in double quotes"); - } - - let trimmed = s[1..s.len() - 1].to_owned(); - if trimmed.contains('"') { - bail!("string literal must not contain quotes"); - } - if trimmed.contains('\\') { - bail!("string literal must not contain backslashes"); - } - Ok(trimmed) -} -*/ diff --git a/crates/generate/src/funcs.rs b/crates/generate/src/funcs.rs index 15f3a8fae5..8313fe0c1c 100644 --- a/crates/generate/src/funcs.rs +++ b/crates/generate/src/funcs.rs @@ -4,11 +4,9 @@ use quote::quote; use crate::names::Names; use crate::types::struct_is_copy; -// FIXME need to template what argument is required to an import function - some context -// struct (e.g. WasiCtx) should be provided at the invocation of the `gen` proc macro. -// pub fn define_func(names: &Names, func: &witx::InterfaceFunc) -> TokenStream { let ident = names.func(&func.name); + let ctx_type = names.ctx_type(); let coretype = func.core_type(); let params = coretype.args.iter().map(|arg| match arg.signifies { @@ -30,7 +28,7 @@ pub fn define_func(names: &Names, func: &witx::InterfaceFunc) -> TokenStream { }); let abi_args = quote!( - ctx: &mut WasiCtx, memory: ::memory::GuestMemory, + ctx: &mut #ctx_type, memory: ::memory::GuestMemory, #(#params),* ); let abi_ret = if let Some(ret) = &coretype.ret { diff --git a/crates/generate/src/lib.rs b/crates/generate/src/lib.rs index 9b66c9e1e8..4a50d88874 100644 --- a/crates/generate/src/lib.rs +++ b/crates/generate/src/lib.rs @@ -20,9 +20,9 @@ use types::define_datatype; pub fn from_witx(args: TokenStream) -> TokenStream { let config = parse_macro_input!(args as Config); - let names = Names::new(); // TODO parse the names from the invocation of the macro, or from a file? + let doc = witx::load(&config.witx.paths).expect("loading witx"); - let doc = witx::load(&config.witx_paths()).expect("loading witx"); + let names = Names::new(config); // TODO parse the names from the invocation of the macro, or from a file? let types = doc.typenames().map(|t| define_datatype(&names, &t)); @@ -30,9 +30,10 @@ pub fn from_witx(args: TokenStream) -> TokenStream { let modname = names.module(&module.name); let fs = module.funcs().map(|f| define_func(&names, &f)); let modtrait = define_module_trait(&names, &module); + let ctx_type = names.ctx_type(); quote!( mod #modname { - use super::WasiCtx; + use super::#ctx_type; use super::types::*; #(#fs)* diff --git a/crates/generate/src/names.rs b/crates/generate/src/names.rs index 558a7e28dc..c9ad2ba253 100644 --- a/crates/generate/src/names.rs +++ b/crates/generate/src/names.rs @@ -3,14 +3,19 @@ use proc_macro2::{Ident, TokenStream}; use quote::{format_ident, quote}; use witx::{AtomType, BuiltinType, Id, TypeRef}; +use crate::Config; + #[derive(Debug, Clone)] pub struct Names { - // FIXME: overrides go in here, so we can map e.g. 2big => TooBig + config: Config, } impl Names { - pub fn new() -> Names { - Names {} + pub fn new(config: Config) -> Names { + Names { config } + } + pub fn ctx_type(&self) -> Ident { + self.config.ctx.name.clone() } pub fn type_(&self, id: &Id) -> TokenStream { let ident = format_ident!("{}", id.as_str().to_camel_case()); diff --git a/src/lib.rs b/src/lib.rs index 65356c89a5..145e6ea783 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,9 @@ pub mod test { // FIXME: parameterize macro on what ctx type is used here - generate::from_witx!(["test.witx"]); + generate::from_witx!({ + witx: ["test.witx"], + ctx: WasiCtx, + }); pub struct WasiCtx { guest_errors: Vec<::memory::GuestError>, From f321f05a98e9542407df12d94fc55460371159a7 Mon Sep 17 00:00:00 2001 From: Pat Hickey Date: Fri, 31 Jan 2020 15:18:49 -0800 Subject: [PATCH 33/86] use lifetimes on types that require it --- crates/generate/src/funcs.rs | 18 ++++---- crates/generate/src/module_trait.rs | 7 +-- crates/generate/src/names.rs | 17 +++++--- crates/generate/src/types.rs | 67 +++++++++++++++++++++++------ test.witx | 3 ++ 5 files changed, 81 insertions(+), 31 deletions(-) diff --git a/crates/generate/src/funcs.rs b/crates/generate/src/funcs.rs index 8313fe0c1c..bd3d1f0349 100644 --- a/crates/generate/src/funcs.rs +++ b/crates/generate/src/funcs.rs @@ -2,7 +2,7 @@ use proc_macro2::TokenStream; use quote::quote; use crate::names::Names; -use crate::types::struct_is_copy; +use crate::types::{anon_lifetime, struct_is_copy}; pub fn define_func(names: &Names, func: &witx::InterfaceFunc) -> TokenStream { let ident = names.func(&func.name); @@ -58,7 +58,7 @@ pub fn define_func(names: &Names, func: &witx::InterfaceFunc) -> TokenStream { witx::TypePassedBy::Value(atom) => names.atom_type(atom), _ => unreachable!("err should always be passed by value"), }; - let err_typename = names.type_ref(&tref); + let err_typename = names.type_ref(&tref, anon_lifetime()); quote! { let err: #err_typename = ::memory::GuestErrorType::from_error(e, ctx); return #abi_ret::from(err); @@ -106,7 +106,7 @@ pub fn define_func(names: &Names, func: &witx::InterfaceFunc) -> TokenStream { let marshal_rets_post = marshal_rets.map(|(_pre, post)| post); let success = if let Some(err_type) = err_type { - let err_typename = names.type_ref(&err_type); + let err_typename = names.type_ref(&err_type, anon_lifetime()); quote! { let success:#err_typename = ::memory::GuestErrorType::success(); #abi_ret::from(success) @@ -133,7 +133,7 @@ fn marshal_arg( error_handling: TokenStream, ) -> TokenStream { let tref = ¶m.tref; - let interface_typename = names.type_ref(&tref); + let interface_typename = names.type_ref(&tref, anon_lifetime()); let try_into_conversion = { let name = names.func_param(¶m.name); @@ -180,7 +180,7 @@ fn marshal_arg( witx::BuiltinType::String => unimplemented!("string types unimplemented"), }, witx::Type::Pointer(pointee) => { - let pointee_type = names.type_ref(pointee); + let pointee_type = names.type_ref(pointee, anon_lifetime()); let name = names.func_param(¶m.name); quote! { let #name = match memory.ptr_mut::<#pointee_type>(#name as u32) { @@ -192,7 +192,7 @@ fn marshal_arg( } } witx::Type::ConstPointer(pointee) => { - let pointee_type = names.type_ref(pointee); + let pointee_type = names.type_ref(pointee, anon_lifetime()); let name = names.func_param(¶m.name); quote! { let #name = match memory.ptr::<#pointee_type>(#name as u32) { @@ -204,7 +204,7 @@ fn marshal_arg( } } witx::Type::Struct(s) if struct_is_copy(&s) => { - let pointee_type = names.type_ref(tref); + let pointee_type = names.type_ref(tref, anon_lifetime()); let arg_name = names.func_ptr_binding(¶m.name); let name = names.func_param(¶m.name); quote! { @@ -222,7 +222,7 @@ fn marshal_arg( } } witx::Type::Struct(s) if !struct_is_copy(&s) => { - let pointee_type = names.type_ref(tref); + let pointee_type = names.type_ref(tref, anon_lifetime()); let arg_name = names.func_ptr_binding(¶m.name); let name = names.func_param(¶m.name); quote! { @@ -251,7 +251,7 @@ fn marshal_result( let tref = &result.tref; let write_val_to_ptr = { - let pointee_type = names.type_ref(tref); + let pointee_type = names.type_ref(tref, anon_lifetime()); // core type is given func_ptr_binding name. let ptr_name = names.func_ptr_binding(&result.name); let pre = quote! { diff --git a/crates/generate/src/module_trait.rs b/crates/generate/src/module_trait.rs index 19669fa410..c92fe85ffc 100644 --- a/crates/generate/src/module_trait.rs +++ b/crates/generate/src/module_trait.rs @@ -2,6 +2,7 @@ use proc_macro2::TokenStream; use quote::quote; use crate::names::Names; +use crate::types::anon_lifetime; use witx::Module; pub fn define_module_trait(names: &Names, m: &Module) -> TokenStream { @@ -10,7 +11,7 @@ pub fn define_module_trait(names: &Names, m: &Module) -> TokenStream { let funcname = names.func(&f.name); let args = f.params.iter().map(|arg| { let arg_name = names.func_param(&arg.name); - let arg_typename = names.type_ref(&arg.tref); + let arg_typename = names.type_ref(&arg.tref, anon_lifetime()); let arg_type = match arg.tref.type_().passed_by() { witx::TypePassedBy::Value { .. } => quote!(#arg_typename), witx::TypePassedBy::Pointer { .. } => quote!(&#arg_typename), @@ -22,11 +23,11 @@ pub fn define_module_trait(names: &Names, m: &Module) -> TokenStream { .results .iter() .skip(1) - .map(|ret| names.type_ref(&ret.tref)); + .map(|ret| names.type_ref(&ret.tref, anon_lifetime())); let err = f .results .get(0) - .map(|err_result| names.type_ref(&err_result.tref)) + .map(|err_result| names.type_ref(&err_result.tref, anon_lifetime())) .unwrap_or(quote!(())); quote!(fn #funcname(&mut self, #(#args),*) -> Result<(#(#rets),*), #err>;) }); diff --git a/crates/generate/src/names.rs b/crates/generate/src/names.rs index c9ad2ba253..3759113218 100644 --- a/crates/generate/src/names.rs +++ b/crates/generate/src/names.rs @@ -3,6 +3,7 @@ use proc_macro2::{Ident, TokenStream}; use quote::{format_ident, quote}; use witx::{AtomType, BuiltinType, Id, TypeRef}; +use crate::types::type_needs_lifetime; use crate::Config; #[derive(Debug, Clone)] @@ -47,21 +48,25 @@ impl Names { } } - pub fn type_ref(&self, tref: &TypeRef) -> TokenStream { + pub fn type_ref(&self, tref: &TypeRef, lifetime: TokenStream) -> TokenStream { match tref { TypeRef::Name(nt) => { let ident = self.type_(&nt.name); - quote!(#ident) + if type_needs_lifetime(&nt.tref) { + quote!(#ident<#lifetime>) + } else { + quote!(#ident) + } } TypeRef::Value(ty) => match &**ty { witx::Type::Builtin(builtin) => self.builtin_type(*builtin), witx::Type::Pointer(pointee) => { - let pointee_type = self.type_ref(&pointee); - quote!(::memory::GuestPtrMut<#pointee_type>) + let pointee_type = self.type_ref(&pointee, lifetime.clone()); + quote!(::memory::GuestPtrMut<#lifetime, #pointee_type>) } witx::Type::ConstPointer(pointee) => { - let pointee_type = self.type_ref(&pointee); - quote!(::memory::GuestPtr<#pointee_type>) + let pointee_type = self.type_ref(&pointee, lifetime.clone()); + quote!(::memory::GuestPtr<#lifetime, #pointee_type>) } _ => unimplemented!("anonymous type ref"), }, diff --git a/crates/generate/src/types.rs b/crates/generate/src/types.rs index 096015ae21..e5cd4c0abe 100644 --- a/crates/generate/src/types.rs +++ b/crates/generate/src/types.rs @@ -21,8 +21,12 @@ pub fn define_datatype(names: &Names, namedtype: &witx::NamedType) -> TokenStrea witx::Type::Union(_) => unimplemented!("union types"), witx::Type::Handle(_h) => unimplemented!("handle types"), witx::Type::Builtin(b) => define_builtin(names, &namedtype.name, *b), - witx::Type::Pointer { .. } => unimplemented!("pointer types"), - witx::Type::ConstPointer { .. } => unimplemented!("constpointer types"), + witx::Type::Pointer(p) => { + define_witx_pointer(names, &namedtype.name, quote!(::memory::GuestPtrMut), p) + } + witx::Type::ConstPointer(p) => { + define_witx_pointer(names, &namedtype.name, quote!(::memory::GuestPtr), p) + } witx::Type::Array { .. } => unimplemented!("array types"), }, } @@ -30,9 +34,12 @@ pub fn define_datatype(names: &Names, namedtype: &witx::NamedType) -> TokenStrea fn define_alias(names: &Names, name: &witx::Id, to: &witx::NamedType) -> TokenStream { let ident = names.type_(name); - let to = names.type_(&to.name); - - quote!(pub type #ident = #to;) + let rhs = names.type_(&to.name); + if type_needs_lifetime(&to.tref) { + quote!(pub type #ident<'a> = #rhs<'a>;) + } else { + quote!(pub type #ident = #rhs;) + } } fn define_enum(names: &Names, name: &witx::Id, e: &witx::EnumDatatype) -> TokenStream { @@ -133,6 +140,23 @@ fn define_builtin(names: &Names, name: &witx::Id, builtin: witx::BuiltinType) -> 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!(), + _ => false, + }, + witx::Type::Enum { .. } + | witx::Type::Flags { .. } + | witx::Type::Int { .. } + | witx::Type::Handle { .. } => false, + witx::Type::Struct(s) => !struct_is_copy(&s), + witx::Type::Union { .. } => true, + witx::Type::Pointer { .. } | witx::Type::ConstPointer { .. } => true, + witx::Type::Array { .. } => unimplemented!(), + } +} + pub fn struct_is_copy(s: &witx::StructDatatype) -> bool { s.members.iter().all(|m| match &*m.tref.type_() { witx::Type::Struct(s) => struct_is_copy(&s), @@ -158,11 +182,11 @@ fn define_copy_struct(names: &Names, name: &witx::Id, s: &witx::StructDatatype) let member_decls = s.members.iter().map(|m| { let name = names.struct_member(&m.name); - let type_ = names.type_ref(&m.tref); + let type_ = names.type_ref(&m.tref, anon_lifetime()); quote!(pub #name: #type_) }); let member_valids = s.member_layout().into_iter().map(|ml| { - let type_ = names.type_ref(&ml.member.tref); + let type_ = names.type_ref(&ml.member.tref, anon_lifetime()); let offset = ml.offset as u32; let fieldname = names.struct_member(&ml.member.name); quote! { @@ -221,11 +245,11 @@ fn define_ptr_struct(names: &Names, name: &witx::Id, s: &witx::StructDatatype) - witx::TypeRef::Value(ty) => match &**ty { witx::Type::Builtin(builtin) => names.builtin_type(*builtin), witx::Type::Pointer(pointee) => { - let pointee_type = names.type_ref(&pointee); + let pointee_type = names.type_ref(&pointee, quote!('a)); quote!(::memory::GuestPtrMut<'a, #pointee_type>) } witx::Type::ConstPointer(pointee) => { - let pointee_type = names.type_ref(&pointee); + let pointee_type = names.type_ref(&pointee, quote!('a)); quote!(::memory::GuestPtr<'a, #pointee_type>) } _ => unimplemented!("other anonymous struct members"), @@ -239,11 +263,11 @@ fn define_ptr_struct(names: &Names, name: &witx::Id, s: &witx::StructDatatype) - witx::TypeRef::Value(ty) => match &**ty { witx::Type::Builtin(builtin) => names.builtin_type(*builtin), witx::Type::Pointer(pointee) => { - let pointee_type = names.type_ref(&pointee); + let pointee_type = names.type_ref(&pointee, anon_lifetime()); quote!(::memory::GuestPtrMut::<#pointee_type>) } witx::Type::ConstPointer(pointee) => { - let pointee_type = names.type_ref(&pointee); + let pointee_type = names.type_ref(&pointee, anon_lifetime()); quote!(::memory::GuestPtr::<#pointee_type>) } _ => unimplemented!("other anonymous struct members"), @@ -286,13 +310,13 @@ fn define_ptr_struct(names: &Names, name: &witx::Id, s: &witx::StructDatatype) - } } witx::Type::Pointer(pointee) => { - let pointee_type = names.type_ref(&pointee); + let pointee_type = names.type_ref(&pointee, anon_lifetime()); quote! { let #name = ::memory::GuestPtrMut::<#pointee_type>::read_from_guest(&location.cast(#offset)?)?; } } witx::Type::ConstPointer(pointee) => { - let pointee_type = names.type_ref(&pointee); + let pointee_type = names.type_ref(&pointee, anon_lifetime()); quote! { let #name = ::memory::GuestPtr::<#pointee_type>::read_from_guest(&location.cast(#offset)?)?; } @@ -340,6 +364,19 @@ fn define_ptr_struct(names: &Names, name: &witx::Id, s: &witx::StructDatatype) - } } } + +fn define_witx_pointer( + names: &Names, + name: &witx::Id, + pointer_type: TokenStream, + pointee: &witx::TypeRef, +) -> TokenStream { + let ident = names.type_(name); + let pointee_type = names.type_ref(pointee, quote!('a)); + + quote!(pub type #ident<'a> = #pointer_type<'a, #pointee_type>;) +} + fn int_repr_tokens(int_repr: witx::IntRepr) -> TokenStream { match int_repr { witx::IntRepr::U8 => quote!(u8), @@ -356,3 +393,7 @@ fn atom_token(atom: witx::AtomType) -> TokenStream { witx::AtomType::F64 => quote!(f64), } } + +pub fn anon_lifetime() -> TokenStream { + quote!('_) +} diff --git a/test.witx b/test.witx index 3fa2ae5ac7..41036284e1 100644 --- a/test.witx +++ b/test.witx @@ -24,6 +24,9 @@ (field $first (@witx const_pointer s32)) (field $second (@witx const_pointer s32)))) +(typename $named_ptr (@witx pointer f32)) +(typename $named_ptr_to_ptr (@witx pointer (@witx pointer f64))) + (module $foo (@interface func (export "bar") (param $an_int u32) From 3d428b828ffbfed76089844b8f62d25202ef158f Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Mon, 3 Feb 2020 07:38:48 +0100 Subject: [PATCH 34/86] Add some (incomplete set) basic sanity end-to-end tests (#2) * Add some (incomplete set) basic sanity end-to-end tests This commit adds some (an incomplete set of) basic sanity end-to-end tests. It uses `test.witx` to autogenerate types and module interface functions (aka the syscalls), and tests their implementation. For the host memory, it uses simplistic `&mut [u8]` where we have full control of the addressing and contents. * Add sanity test for baz interface func This commit adds a sanity test for the `Foo::baz` interface func. * Upcast start/len for Region to avoid overflow * Reenable alignment checking for memory * use an array to implement hostmemory Co-authored-by: Pat Hickey --- crates/memory/src/region.rs | 4 +- lib_generated.rs | 2821 ---------------------------------- src/lib.rs | 97 -- tests/main.rs | 202 +++ test.witx => tests/test.witx | 2 - 5 files changed, 204 insertions(+), 2922 deletions(-) delete mode 100644 lib_generated.rs create mode 100644 tests/main.rs rename test.witx => tests/test.witx (99%) diff --git a/crates/memory/src/region.rs b/crates/memory/src/region.rs index 3df191117f..414f6e1970 100644 --- a/crates/memory/src/region.rs +++ b/crates/memory/src/region.rs @@ -7,10 +7,10 @@ pub struct Region { impl Region { pub fn overlaps(&self, rhs: Region) -> bool { let self_start = self.start as u64; - let self_end = self.start as u64 + self.len as u64; + let self_end = ((self_start + self.len as u64) as i64 - 1) as u64; let rhs_start = rhs.start as u64; - let rhs_end = rhs.start as u64 + rhs.len as u64; + let rhs_end = ((rhs_start + rhs.len as u64) as i64 - 1) as u64; // start of rhs inside self: if rhs_start >= self_start && rhs_start < self_end { diff --git a/lib_generated.rs b/lib_generated.rs deleted file mode 100644 index ea181be91d..0000000000 --- a/lib_generated.rs +++ /dev/null @@ -1,2821 +0,0 @@ -#![feature(prelude_import)] -#[prelude_import] -use std::prelude::v1::*; -#[macro_use] -extern crate std; -pub type Filesize = u64; -pub type Timestamp = u64; -#[repr(u32)] -pub enum Clockid { - Realtime, - Monotonic, - ProcessCputimeId, - ThreadCputimeId, -} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::marker::Copy for Clockid {} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::clone::Clone for Clockid { - #[inline] - fn clone(&self) -> Clockid { - { - *self - } - } -} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::fmt::Debug for Clockid { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - match (&*self,) { - (&Clockid::Realtime,) => { - let mut debug_trait_builder = f.debug_tuple("Realtime"); - debug_trait_builder.finish() - } - (&Clockid::Monotonic,) => { - let mut debug_trait_builder = f.debug_tuple("Monotonic"); - debug_trait_builder.finish() - } - (&Clockid::ProcessCputimeId,) => { - let mut debug_trait_builder = f.debug_tuple("ProcessCputimeId"); - debug_trait_builder.finish() - } - (&Clockid::ThreadCputimeId,) => { - let mut debug_trait_builder = f.debug_tuple("ThreadCputimeId"); - debug_trait_builder.finish() - } - } - } -} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::hash::Hash for Clockid { - fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) -> () { - match (&*self,) { - _ => ::core::hash::Hash::hash( - &unsafe { ::core::intrinsics::discriminant_value(self) }, - state, - ), - } - } -} -impl ::core::marker::StructuralEq for Clockid {} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::cmp::Eq for Clockid { - #[inline] - #[doc(hidden)] - fn assert_receiver_is_total_eq(&self) -> () { - {} - } -} -impl ::core::marker::StructuralPartialEq for Clockid {} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::cmp::PartialEq for Clockid { - #[inline] - fn eq(&self, other: &Clockid) -> bool { - { - let __self_vi = unsafe { ::core::intrinsics::discriminant_value(&*self) } as u32; - let __arg_1_vi = unsafe { ::core::intrinsics::discriminant_value(&*other) } as u32; - if true && __self_vi == __arg_1_vi { - match (&*self, &*other) { - _ => true, - } - } else { - false - } - } - } -} -#[repr(u16)] -pub enum Errno { - Esuccess, - E2big, - Eacces, - Eaddrinuse, - Eaddrnotavail, - Eafnosupport, - Eagain, - Ealready, - Ebadf, - Ebadmsg, - Ebusy, - Ecanceled, - Echild, - Econnaborted, - Econnrefused, - Econnreset, - Edeadlk, - Edestaddrreq, - Edom, - Edquot, - Eexist, - Efault, - Efbig, - Ehostunreach, - Eidrm, - Eilseq, - Einprogress, - Eintr, - Einval, - Eio, - Eisconn, - Eisdir, - Eloop, - Emfile, - Emlink, - Emsgsize, - Emultihop, - Enametoolong, - Enetdown, - Enetreset, - Enetunreach, - Enfile, - Enobufs, - Enodev, - Enoent, - Enoexec, - Enolck, - Enolink, - Enomem, - Enomsg, - Enoprotoopt, - Enospc, - Enosys, - Enotconn, - Enotdir, - Enotempty, - Enotrecoverable, - Enotsock, - Enotsup, - Enotty, - Enxio, - Eoverflow, - Eownerdead, - Eperm, - Epipe, - Eproto, - Eprotonosupport, - Eprototype, - Erange, - Erofs, - Espipe, - Esrch, - Estale, - Etimedout, - Etxtbsy, - Exdev, - Enotcapable, -} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::marker::Copy for Errno {} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::clone::Clone for Errno { - #[inline] - fn clone(&self) -> Errno { - { - *self - } - } -} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::fmt::Debug for Errno { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - match (&*self,) { - (&Errno::Esuccess,) => { - let mut debug_trait_builder = f.debug_tuple("Esuccess"); - debug_trait_builder.finish() - } - (&Errno::E2big,) => { - let mut debug_trait_builder = f.debug_tuple("E2big"); - debug_trait_builder.finish() - } - (&Errno::Eacces,) => { - let mut debug_trait_builder = f.debug_tuple("Eacces"); - debug_trait_builder.finish() - } - (&Errno::Eaddrinuse,) => { - let mut debug_trait_builder = f.debug_tuple("Eaddrinuse"); - debug_trait_builder.finish() - } - (&Errno::Eaddrnotavail,) => { - let mut debug_trait_builder = f.debug_tuple("Eaddrnotavail"); - debug_trait_builder.finish() - } - (&Errno::Eafnosupport,) => { - let mut debug_trait_builder = f.debug_tuple("Eafnosupport"); - debug_trait_builder.finish() - } - (&Errno::Eagain,) => { - let mut debug_trait_builder = f.debug_tuple("Eagain"); - debug_trait_builder.finish() - } - (&Errno::Ealready,) => { - let mut debug_trait_builder = f.debug_tuple("Ealready"); - debug_trait_builder.finish() - } - (&Errno::Ebadf,) => { - let mut debug_trait_builder = f.debug_tuple("Ebadf"); - debug_trait_builder.finish() - } - (&Errno::Ebadmsg,) => { - let mut debug_trait_builder = f.debug_tuple("Ebadmsg"); - debug_trait_builder.finish() - } - (&Errno::Ebusy,) => { - let mut debug_trait_builder = f.debug_tuple("Ebusy"); - debug_trait_builder.finish() - } - (&Errno::Ecanceled,) => { - let mut debug_trait_builder = f.debug_tuple("Ecanceled"); - debug_trait_builder.finish() - } - (&Errno::Echild,) => { - let mut debug_trait_builder = f.debug_tuple("Echild"); - debug_trait_builder.finish() - } - (&Errno::Econnaborted,) => { - let mut debug_trait_builder = f.debug_tuple("Econnaborted"); - debug_trait_builder.finish() - } - (&Errno::Econnrefused,) => { - let mut debug_trait_builder = f.debug_tuple("Econnrefused"); - debug_trait_builder.finish() - } - (&Errno::Econnreset,) => { - let mut debug_trait_builder = f.debug_tuple("Econnreset"); - debug_trait_builder.finish() - } - (&Errno::Edeadlk,) => { - let mut debug_trait_builder = f.debug_tuple("Edeadlk"); - debug_trait_builder.finish() - } - (&Errno::Edestaddrreq,) => { - let mut debug_trait_builder = f.debug_tuple("Edestaddrreq"); - debug_trait_builder.finish() - } - (&Errno::Edom,) => { - let mut debug_trait_builder = f.debug_tuple("Edom"); - debug_trait_builder.finish() - } - (&Errno::Edquot,) => { - let mut debug_trait_builder = f.debug_tuple("Edquot"); - debug_trait_builder.finish() - } - (&Errno::Eexist,) => { - let mut debug_trait_builder = f.debug_tuple("Eexist"); - debug_trait_builder.finish() - } - (&Errno::Efault,) => { - let mut debug_trait_builder = f.debug_tuple("Efault"); - debug_trait_builder.finish() - } - (&Errno::Efbig,) => { - let mut debug_trait_builder = f.debug_tuple("Efbig"); - debug_trait_builder.finish() - } - (&Errno::Ehostunreach,) => { - let mut debug_trait_builder = f.debug_tuple("Ehostunreach"); - debug_trait_builder.finish() - } - (&Errno::Eidrm,) => { - let mut debug_trait_builder = f.debug_tuple("Eidrm"); - debug_trait_builder.finish() - } - (&Errno::Eilseq,) => { - let mut debug_trait_builder = f.debug_tuple("Eilseq"); - debug_trait_builder.finish() - } - (&Errno::Einprogress,) => { - let mut debug_trait_builder = f.debug_tuple("Einprogress"); - debug_trait_builder.finish() - } - (&Errno::Eintr,) => { - let mut debug_trait_builder = f.debug_tuple("Eintr"); - debug_trait_builder.finish() - } - (&Errno::Einval,) => { - let mut debug_trait_builder = f.debug_tuple("Einval"); - debug_trait_builder.finish() - } - (&Errno::Eio,) => { - let mut debug_trait_builder = f.debug_tuple("Eio"); - debug_trait_builder.finish() - } - (&Errno::Eisconn,) => { - let mut debug_trait_builder = f.debug_tuple("Eisconn"); - debug_trait_builder.finish() - } - (&Errno::Eisdir,) => { - let mut debug_trait_builder = f.debug_tuple("Eisdir"); - debug_trait_builder.finish() - } - (&Errno::Eloop,) => { - let mut debug_trait_builder = f.debug_tuple("Eloop"); - debug_trait_builder.finish() - } - (&Errno::Emfile,) => { - let mut debug_trait_builder = f.debug_tuple("Emfile"); - debug_trait_builder.finish() - } - (&Errno::Emlink,) => { - let mut debug_trait_builder = f.debug_tuple("Emlink"); - debug_trait_builder.finish() - } - (&Errno::Emsgsize,) => { - let mut debug_trait_builder = f.debug_tuple("Emsgsize"); - debug_trait_builder.finish() - } - (&Errno::Emultihop,) => { - let mut debug_trait_builder = f.debug_tuple("Emultihop"); - debug_trait_builder.finish() - } - (&Errno::Enametoolong,) => { - let mut debug_trait_builder = f.debug_tuple("Enametoolong"); - debug_trait_builder.finish() - } - (&Errno::Enetdown,) => { - let mut debug_trait_builder = f.debug_tuple("Enetdown"); - debug_trait_builder.finish() - } - (&Errno::Enetreset,) => { - let mut debug_trait_builder = f.debug_tuple("Enetreset"); - debug_trait_builder.finish() - } - (&Errno::Enetunreach,) => { - let mut debug_trait_builder = f.debug_tuple("Enetunreach"); - debug_trait_builder.finish() - } - (&Errno::Enfile,) => { - let mut debug_trait_builder = f.debug_tuple("Enfile"); - debug_trait_builder.finish() - } - (&Errno::Enobufs,) => { - let mut debug_trait_builder = f.debug_tuple("Enobufs"); - debug_trait_builder.finish() - } - (&Errno::Enodev,) => { - let mut debug_trait_builder = f.debug_tuple("Enodev"); - debug_trait_builder.finish() - } - (&Errno::Enoent,) => { - let mut debug_trait_builder = f.debug_tuple("Enoent"); - debug_trait_builder.finish() - } - (&Errno::Enoexec,) => { - let mut debug_trait_builder = f.debug_tuple("Enoexec"); - debug_trait_builder.finish() - } - (&Errno::Enolck,) => { - let mut debug_trait_builder = f.debug_tuple("Enolck"); - debug_trait_builder.finish() - } - (&Errno::Enolink,) => { - let mut debug_trait_builder = f.debug_tuple("Enolink"); - debug_trait_builder.finish() - } - (&Errno::Enomem,) => { - let mut debug_trait_builder = f.debug_tuple("Enomem"); - debug_trait_builder.finish() - } - (&Errno::Enomsg,) => { - let mut debug_trait_builder = f.debug_tuple("Enomsg"); - debug_trait_builder.finish() - } - (&Errno::Enoprotoopt,) => { - let mut debug_trait_builder = f.debug_tuple("Enoprotoopt"); - debug_trait_builder.finish() - } - (&Errno::Enospc,) => { - let mut debug_trait_builder = f.debug_tuple("Enospc"); - debug_trait_builder.finish() - } - (&Errno::Enosys,) => { - let mut debug_trait_builder = f.debug_tuple("Enosys"); - debug_trait_builder.finish() - } - (&Errno::Enotconn,) => { - let mut debug_trait_builder = f.debug_tuple("Enotconn"); - debug_trait_builder.finish() - } - (&Errno::Enotdir,) => { - let mut debug_trait_builder = f.debug_tuple("Enotdir"); - debug_trait_builder.finish() - } - (&Errno::Enotempty,) => { - let mut debug_trait_builder = f.debug_tuple("Enotempty"); - debug_trait_builder.finish() - } - (&Errno::Enotrecoverable,) => { - let mut debug_trait_builder = f.debug_tuple("Enotrecoverable"); - debug_trait_builder.finish() - } - (&Errno::Enotsock,) => { - let mut debug_trait_builder = f.debug_tuple("Enotsock"); - debug_trait_builder.finish() - } - (&Errno::Enotsup,) => { - let mut debug_trait_builder = f.debug_tuple("Enotsup"); - debug_trait_builder.finish() - } - (&Errno::Enotty,) => { - let mut debug_trait_builder = f.debug_tuple("Enotty"); - debug_trait_builder.finish() - } - (&Errno::Enxio,) => { - let mut debug_trait_builder = f.debug_tuple("Enxio"); - debug_trait_builder.finish() - } - (&Errno::Eoverflow,) => { - let mut debug_trait_builder = f.debug_tuple("Eoverflow"); - debug_trait_builder.finish() - } - (&Errno::Eownerdead,) => { - let mut debug_trait_builder = f.debug_tuple("Eownerdead"); - debug_trait_builder.finish() - } - (&Errno::Eperm,) => { - let mut debug_trait_builder = f.debug_tuple("Eperm"); - debug_trait_builder.finish() - } - (&Errno::Epipe,) => { - let mut debug_trait_builder = f.debug_tuple("Epipe"); - debug_trait_builder.finish() - } - (&Errno::Eproto,) => { - let mut debug_trait_builder = f.debug_tuple("Eproto"); - debug_trait_builder.finish() - } - (&Errno::Eprotonosupport,) => { - let mut debug_trait_builder = f.debug_tuple("Eprotonosupport"); - debug_trait_builder.finish() - } - (&Errno::Eprototype,) => { - let mut debug_trait_builder = f.debug_tuple("Eprototype"); - debug_trait_builder.finish() - } - (&Errno::Erange,) => { - let mut debug_trait_builder = f.debug_tuple("Erange"); - debug_trait_builder.finish() - } - (&Errno::Erofs,) => { - let mut debug_trait_builder = f.debug_tuple("Erofs"); - debug_trait_builder.finish() - } - (&Errno::Espipe,) => { - let mut debug_trait_builder = f.debug_tuple("Espipe"); - debug_trait_builder.finish() - } - (&Errno::Esrch,) => { - let mut debug_trait_builder = f.debug_tuple("Esrch"); - debug_trait_builder.finish() - } - (&Errno::Estale,) => { - let mut debug_trait_builder = f.debug_tuple("Estale"); - debug_trait_builder.finish() - } - (&Errno::Etimedout,) => { - let mut debug_trait_builder = f.debug_tuple("Etimedout"); - debug_trait_builder.finish() - } - (&Errno::Etxtbsy,) => { - let mut debug_trait_builder = f.debug_tuple("Etxtbsy"); - debug_trait_builder.finish() - } - (&Errno::Exdev,) => { - let mut debug_trait_builder = f.debug_tuple("Exdev"); - debug_trait_builder.finish() - } - (&Errno::Enotcapable,) => { - let mut debug_trait_builder = f.debug_tuple("Enotcapable"); - debug_trait_builder.finish() - } - } - } -} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::hash::Hash for Errno { - fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) -> () { - match (&*self,) { - _ => ::core::hash::Hash::hash( - &unsafe { ::core::intrinsics::discriminant_value(self) }, - state, - ), - } - } -} -impl ::core::marker::StructuralEq for Errno {} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::cmp::Eq for Errno { - #[inline] - #[doc(hidden)] - fn assert_receiver_is_total_eq(&self) -> () { - {} - } -} -impl ::core::marker::StructuralPartialEq for Errno {} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::cmp::PartialEq for Errno { - #[inline] - fn eq(&self, other: &Errno) -> bool { - { - let __self_vi = unsafe { ::core::intrinsics::discriminant_value(&*self) } as u16; - let __arg_1_vi = unsafe { ::core::intrinsics::discriminant_value(&*other) } as u16; - if true && __self_vi == __arg_1_vi { - match (&*self, &*other) { - _ => true, - } - } else { - false - } - } - } -} -#[repr(transparent)] -pub struct Rights(u64); -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::marker::Copy for Rights {} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::clone::Clone for Rights { - #[inline] - fn clone(&self) -> Rights { - { - let _: ::core::clone::AssertParamIsClone; - *self - } - } -} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::fmt::Debug for Rights { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - match *self { - Rights(ref __self_0_0) => { - let mut debug_trait_builder = f.debug_tuple("Rights"); - let _ = debug_trait_builder.field(&&(*__self_0_0)); - debug_trait_builder.finish() - } - } - } -} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::hash::Hash for Rights { - fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) -> () { - match *self { - Rights(ref __self_0_0) => ::core::hash::Hash::hash(&(*__self_0_0), state), - } - } -} -impl ::core::marker::StructuralEq for Rights {} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::cmp::Eq for Rights { - #[inline] - #[doc(hidden)] - fn assert_receiver_is_total_eq(&self) -> () { - { - let _: ::core::cmp::AssertParamIsEq; - } - } -} -impl ::core::marker::StructuralPartialEq for Rights {} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::cmp::PartialEq for Rights { - #[inline] - fn eq(&self, other: &Rights) -> bool { - match *other { - Rights(ref __self_1_0) => match *self { - Rights(ref __self_0_0) => (*__self_0_0) == (*__self_1_0), - }, - } - } - #[inline] - fn ne(&self, other: &Rights) -> bool { - match *other { - Rights(ref __self_1_0) => match *self { - Rights(ref __self_0_0) => (*__self_0_0) != (*__self_1_0), - }, - } - } -} -impl Rights { - pub const FD_DATASYNC: Rights = Rights(1); - pub const FD_READ: Rights = Rights(2); - pub const FD_SEEK: Rights = Rights(4); - pub const FD_FDSTAT_SET_FLAGS: Rights = Rights(8); - pub const FD_SYNC: Rights = Rights(16); - pub const FD_TELL: Rights = Rights(32); - pub const FD_WRITE: Rights = Rights(64); - pub const FD_ADVISE: Rights = Rights(128); - pub const FD_ALLOCATE: Rights = Rights(256); - pub const PATH_CREATE_DIRECTORY: Rights = Rights(512); - pub const PATH_CREATE_FILE: Rights = Rights(1024); - pub const PATH_LINK_SOURCE: Rights = Rights(2048); - pub const PATH_LINK_TARGET: Rights = Rights(4096); - pub const PATH_OPEN: Rights = Rights(8192); - pub const FD_READDIR: Rights = Rights(16384); - pub const PATH_READLINK: Rights = Rights(32768); - pub const PATH_RENAME_SOURCE: Rights = Rights(65536); - pub const PATH_RENAME_TARGET: Rights = Rights(131072); - pub const PATH_FILESTAT_GET: Rights = Rights(262144); - pub const PATH_FILESTAT_SET_SIZE: Rights = Rights(524288); - pub const PATH_FILESTAT_SET_TIMES: Rights = Rights(1048576); - pub const FD_FILESTAT_GET: Rights = Rights(2097152); - pub const FD_FILESTAT_SET_SIZE: Rights = Rights(4194304); - pub const FD_FILESTAT_SET_TIMES: Rights = Rights(8388608); - pub const PATH_SYMLINK: Rights = Rights(16777216); - pub const PATH_REMOVE_DIRECTORY: Rights = Rights(33554432); - pub const PATH_UNLINK_FILE: Rights = Rights(67108864); - pub const POLL_FD_READWRITE: Rights = Rights(134217728); - pub const SOCK_SHUTDOWN: Rights = Rights(268435456); -} -pub type Fd = u32; -pub type Filedelta = i64; -#[repr(u8)] -pub enum Whence { - Set, - Cur, - End, -} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::marker::Copy for Whence {} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::clone::Clone for Whence { - #[inline] - fn clone(&self) -> Whence { - { - *self - } - } -} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::fmt::Debug for Whence { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - match (&*self,) { - (&Whence::Set,) => { - let mut debug_trait_builder = f.debug_tuple("Set"); - debug_trait_builder.finish() - } - (&Whence::Cur,) => { - let mut debug_trait_builder = f.debug_tuple("Cur"); - debug_trait_builder.finish() - } - (&Whence::End,) => { - let mut debug_trait_builder = f.debug_tuple("End"); - debug_trait_builder.finish() - } - } - } -} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::hash::Hash for Whence { - fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) -> () { - match (&*self,) { - _ => ::core::hash::Hash::hash( - &unsafe { ::core::intrinsics::discriminant_value(self) }, - state, - ), - } - } -} -impl ::core::marker::StructuralEq for Whence {} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::cmp::Eq for Whence { - #[inline] - #[doc(hidden)] - fn assert_receiver_is_total_eq(&self) -> () { - {} - } -} -impl ::core::marker::StructuralPartialEq for Whence {} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::cmp::PartialEq for Whence { - #[inline] - fn eq(&self, other: &Whence) -> bool { - { - let __self_vi = unsafe { ::core::intrinsics::discriminant_value(&*self) } as u8; - let __arg_1_vi = unsafe { ::core::intrinsics::discriminant_value(&*other) } as u8; - if true && __self_vi == __arg_1_vi { - match (&*self, &*other) { - _ => true, - } - } else { - false - } - } - } -} -pub type Dircookie = u64; -pub type Dirnamlen = u32; -pub type Inode = u64; -#[repr(u8)] -pub enum Filetype { - Unknown, - BlockDevice, - CharacterDevice, - Directory, - RegularFile, - SocketDgram, - SocketStream, - SymbolicLink, -} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::marker::Copy for Filetype {} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::clone::Clone for Filetype { - #[inline] - fn clone(&self) -> Filetype { - { - *self - } - } -} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::fmt::Debug for Filetype { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - match (&*self,) { - (&Filetype::Unknown,) => { - let mut debug_trait_builder = f.debug_tuple("Unknown"); - debug_trait_builder.finish() - } - (&Filetype::BlockDevice,) => { - let mut debug_trait_builder = f.debug_tuple("BlockDevice"); - debug_trait_builder.finish() - } - (&Filetype::CharacterDevice,) => { - let mut debug_trait_builder = f.debug_tuple("CharacterDevice"); - debug_trait_builder.finish() - } - (&Filetype::Directory,) => { - let mut debug_trait_builder = f.debug_tuple("Directory"); - debug_trait_builder.finish() - } - (&Filetype::RegularFile,) => { - let mut debug_trait_builder = f.debug_tuple("RegularFile"); - debug_trait_builder.finish() - } - (&Filetype::SocketDgram,) => { - let mut debug_trait_builder = f.debug_tuple("SocketDgram"); - debug_trait_builder.finish() - } - (&Filetype::SocketStream,) => { - let mut debug_trait_builder = f.debug_tuple("SocketStream"); - debug_trait_builder.finish() - } - (&Filetype::SymbolicLink,) => { - let mut debug_trait_builder = f.debug_tuple("SymbolicLink"); - debug_trait_builder.finish() - } - } - } -} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::hash::Hash for Filetype { - fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) -> () { - match (&*self,) { - _ => ::core::hash::Hash::hash( - &unsafe { ::core::intrinsics::discriminant_value(self) }, - state, - ), - } - } -} -impl ::core::marker::StructuralEq for Filetype {} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::cmp::Eq for Filetype { - #[inline] - #[doc(hidden)] - fn assert_receiver_is_total_eq(&self) -> () { - {} - } -} -impl ::core::marker::StructuralPartialEq for Filetype {} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::cmp::PartialEq for Filetype { - #[inline] - fn eq(&self, other: &Filetype) -> bool { - { - let __self_vi = unsafe { ::core::intrinsics::discriminant_value(&*self) } as u8; - let __arg_1_vi = unsafe { ::core::intrinsics::discriminant_value(&*other) } as u8; - if true && __self_vi == __arg_1_vi { - match (&*self, &*other) { - _ => true, - } - } else { - false - } - } - } -} -#[repr(C)] -pub struct Dirent { - pub d_next: Dircookie, - pub d_ino: Inode, - pub d_namlen: Dirnamlen, - pub d_type: Filetype, -} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::marker::Copy for Dirent {} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::clone::Clone for Dirent { - #[inline] - fn clone(&self) -> Dirent { - { - let _: ::core::clone::AssertParamIsClone; - let _: ::core::clone::AssertParamIsClone; - let _: ::core::clone::AssertParamIsClone; - let _: ::core::clone::AssertParamIsClone; - *self - } - } -} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::fmt::Debug for Dirent { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - match *self { - Dirent { - d_next: ref __self_0_0, - d_ino: ref __self_0_1, - d_namlen: ref __self_0_2, - d_type: ref __self_0_3, - } => { - let mut debug_trait_builder = f.debug_struct("Dirent"); - let _ = debug_trait_builder.field("d_next", &&(*__self_0_0)); - let _ = debug_trait_builder.field("d_ino", &&(*__self_0_1)); - let _ = debug_trait_builder.field("d_namlen", &&(*__self_0_2)); - let _ = debug_trait_builder.field("d_type", &&(*__self_0_3)); - debug_trait_builder.finish() - } - } - } -} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::hash::Hash for Dirent { - fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) -> () { - match *self { - Dirent { - d_next: ref __self_0_0, - d_ino: ref __self_0_1, - d_namlen: ref __self_0_2, - d_type: ref __self_0_3, - } => { - ::core::hash::Hash::hash(&(*__self_0_0), state); - ::core::hash::Hash::hash(&(*__self_0_1), state); - ::core::hash::Hash::hash(&(*__self_0_2), state); - ::core::hash::Hash::hash(&(*__self_0_3), state) - } - } - } -} -impl ::core::marker::StructuralEq for Dirent {} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::cmp::Eq for Dirent { - #[inline] - #[doc(hidden)] - fn assert_receiver_is_total_eq(&self) -> () { - { - let _: ::core::cmp::AssertParamIsEq; - let _: ::core::cmp::AssertParamIsEq; - let _: ::core::cmp::AssertParamIsEq; - let _: ::core::cmp::AssertParamIsEq; - } - } -} -impl ::core::marker::StructuralPartialEq for Dirent {} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::cmp::PartialEq for Dirent { - #[inline] - fn eq(&self, other: &Dirent) -> bool { - match *other { - Dirent { - d_next: ref __self_1_0, - d_ino: ref __self_1_1, - d_namlen: ref __self_1_2, - d_type: ref __self_1_3, - } => match *self { - Dirent { - d_next: ref __self_0_0, - d_ino: ref __self_0_1, - d_namlen: ref __self_0_2, - d_type: ref __self_0_3, - } => { - (*__self_0_0) == (*__self_1_0) - && (*__self_0_1) == (*__self_1_1) - && (*__self_0_2) == (*__self_1_2) - && (*__self_0_3) == (*__self_1_3) - } - }, - } - } - #[inline] - fn ne(&self, other: &Dirent) -> bool { - match *other { - Dirent { - d_next: ref __self_1_0, - d_ino: ref __self_1_1, - d_namlen: ref __self_1_2, - d_type: ref __self_1_3, - } => match *self { - Dirent { - d_next: ref __self_0_0, - d_ino: ref __self_0_1, - d_namlen: ref __self_0_2, - d_type: ref __self_0_3, - } => { - (*__self_0_0) != (*__self_1_0) - || (*__self_0_1) != (*__self_1_1) - || (*__self_0_2) != (*__self_1_2) - || (*__self_0_3) != (*__self_1_3) - } - }, - } - } -} -#[repr(u8)] -pub enum Advice { - Normal, - Sequential, - Random, - Willneed, - Dontneed, - Noreuse, -} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::marker::Copy for Advice {} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::clone::Clone for Advice { - #[inline] - fn clone(&self) -> Advice { - { - *self - } - } -} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::fmt::Debug for Advice { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - match (&*self,) { - (&Advice::Normal,) => { - let mut debug_trait_builder = f.debug_tuple("Normal"); - debug_trait_builder.finish() - } - (&Advice::Sequential,) => { - let mut debug_trait_builder = f.debug_tuple("Sequential"); - debug_trait_builder.finish() - } - (&Advice::Random,) => { - let mut debug_trait_builder = f.debug_tuple("Random"); - debug_trait_builder.finish() - } - (&Advice::Willneed,) => { - let mut debug_trait_builder = f.debug_tuple("Willneed"); - debug_trait_builder.finish() - } - (&Advice::Dontneed,) => { - let mut debug_trait_builder = f.debug_tuple("Dontneed"); - debug_trait_builder.finish() - } - (&Advice::Noreuse,) => { - let mut debug_trait_builder = f.debug_tuple("Noreuse"); - debug_trait_builder.finish() - } - } - } -} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::hash::Hash for Advice { - fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) -> () { - match (&*self,) { - _ => ::core::hash::Hash::hash( - &unsafe { ::core::intrinsics::discriminant_value(self) }, - state, - ), - } - } -} -impl ::core::marker::StructuralEq for Advice {} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::cmp::Eq for Advice { - #[inline] - #[doc(hidden)] - fn assert_receiver_is_total_eq(&self) -> () { - {} - } -} -impl ::core::marker::StructuralPartialEq for Advice {} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::cmp::PartialEq for Advice { - #[inline] - fn eq(&self, other: &Advice) -> bool { - { - let __self_vi = unsafe { ::core::intrinsics::discriminant_value(&*self) } as u8; - let __arg_1_vi = unsafe { ::core::intrinsics::discriminant_value(&*other) } as u8; - if true && __self_vi == __arg_1_vi { - match (&*self, &*other) { - _ => true, - } - } else { - false - } - } - } -} -#[repr(transparent)] -pub struct Fdflags(u16); -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::marker::Copy for Fdflags {} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::clone::Clone for Fdflags { - #[inline] - fn clone(&self) -> Fdflags { - { - let _: ::core::clone::AssertParamIsClone; - *self - } - } -} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::fmt::Debug for Fdflags { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - match *self { - Fdflags(ref __self_0_0) => { - let mut debug_trait_builder = f.debug_tuple("Fdflags"); - let _ = debug_trait_builder.field(&&(*__self_0_0)); - debug_trait_builder.finish() - } - } - } -} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::hash::Hash for Fdflags { - fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) -> () { - match *self { - Fdflags(ref __self_0_0) => ::core::hash::Hash::hash(&(*__self_0_0), state), - } - } -} -impl ::core::marker::StructuralEq for Fdflags {} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::cmp::Eq for Fdflags { - #[inline] - #[doc(hidden)] - fn assert_receiver_is_total_eq(&self) -> () { - { - let _: ::core::cmp::AssertParamIsEq; - } - } -} -impl ::core::marker::StructuralPartialEq for Fdflags {} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::cmp::PartialEq for Fdflags { - #[inline] - fn eq(&self, other: &Fdflags) -> bool { - match *other { - Fdflags(ref __self_1_0) => match *self { - Fdflags(ref __self_0_0) => (*__self_0_0) == (*__self_1_0), - }, - } - } - #[inline] - fn ne(&self, other: &Fdflags) -> bool { - match *other { - Fdflags(ref __self_1_0) => match *self { - Fdflags(ref __self_0_0) => (*__self_0_0) != (*__self_1_0), - }, - } - } -} -impl Fdflags { - pub const APPEND: Fdflags = Fdflags(1); - pub const DSYNC: Fdflags = Fdflags(2); - pub const NONBLOCK: Fdflags = Fdflags(4); - pub const RSYNC: Fdflags = Fdflags(8); - pub const SYNC: Fdflags = Fdflags(16); -} -#[repr(C)] -pub struct Fdstat { - pub fs_filetype: Filetype, - pub fs_flags: Fdflags, - pub fs_rights_base: Rights, - pub fs_rights_inheriting: Rights, -} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::marker::Copy for Fdstat {} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::clone::Clone for Fdstat { - #[inline] - fn clone(&self) -> Fdstat { - { - let _: ::core::clone::AssertParamIsClone; - let _: ::core::clone::AssertParamIsClone; - let _: ::core::clone::AssertParamIsClone; - let _: ::core::clone::AssertParamIsClone; - *self - } - } -} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::fmt::Debug for Fdstat { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - match *self { - Fdstat { - fs_filetype: ref __self_0_0, - fs_flags: ref __self_0_1, - fs_rights_base: ref __self_0_2, - fs_rights_inheriting: ref __self_0_3, - } => { - let mut debug_trait_builder = f.debug_struct("Fdstat"); - let _ = debug_trait_builder.field("fs_filetype", &&(*__self_0_0)); - let _ = debug_trait_builder.field("fs_flags", &&(*__self_0_1)); - let _ = debug_trait_builder.field("fs_rights_base", &&(*__self_0_2)); - let _ = debug_trait_builder.field("fs_rights_inheriting", &&(*__self_0_3)); - debug_trait_builder.finish() - } - } - } -} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::hash::Hash for Fdstat { - fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) -> () { - match *self { - Fdstat { - fs_filetype: ref __self_0_0, - fs_flags: ref __self_0_1, - fs_rights_base: ref __self_0_2, - fs_rights_inheriting: ref __self_0_3, - } => { - ::core::hash::Hash::hash(&(*__self_0_0), state); - ::core::hash::Hash::hash(&(*__self_0_1), state); - ::core::hash::Hash::hash(&(*__self_0_2), state); - ::core::hash::Hash::hash(&(*__self_0_3), state) - } - } - } -} -impl ::core::marker::StructuralEq for Fdstat {} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::cmp::Eq for Fdstat { - #[inline] - #[doc(hidden)] - fn assert_receiver_is_total_eq(&self) -> () { - { - let _: ::core::cmp::AssertParamIsEq; - let _: ::core::cmp::AssertParamIsEq; - let _: ::core::cmp::AssertParamIsEq; - let _: ::core::cmp::AssertParamIsEq; - } - } -} -impl ::core::marker::StructuralPartialEq for Fdstat {} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::cmp::PartialEq for Fdstat { - #[inline] - fn eq(&self, other: &Fdstat) -> bool { - match *other { - Fdstat { - fs_filetype: ref __self_1_0, - fs_flags: ref __self_1_1, - fs_rights_base: ref __self_1_2, - fs_rights_inheriting: ref __self_1_3, - } => match *self { - Fdstat { - fs_filetype: ref __self_0_0, - fs_flags: ref __self_0_1, - fs_rights_base: ref __self_0_2, - fs_rights_inheriting: ref __self_0_3, - } => { - (*__self_0_0) == (*__self_1_0) - && (*__self_0_1) == (*__self_1_1) - && (*__self_0_2) == (*__self_1_2) - && (*__self_0_3) == (*__self_1_3) - } - }, - } - } - #[inline] - fn ne(&self, other: &Fdstat) -> bool { - match *other { - Fdstat { - fs_filetype: ref __self_1_0, - fs_flags: ref __self_1_1, - fs_rights_base: ref __self_1_2, - fs_rights_inheriting: ref __self_1_3, - } => match *self { - Fdstat { - fs_filetype: ref __self_0_0, - fs_flags: ref __self_0_1, - fs_rights_base: ref __self_0_2, - fs_rights_inheriting: ref __self_0_3, - } => { - (*__self_0_0) != (*__self_1_0) - || (*__self_0_1) != (*__self_1_1) - || (*__self_0_2) != (*__self_1_2) - || (*__self_0_3) != (*__self_1_3) - } - }, - } - } -} -pub type Device = u64; -#[repr(transparent)] -pub struct Fstflags(u16); -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::marker::Copy for Fstflags {} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::clone::Clone for Fstflags { - #[inline] - fn clone(&self) -> Fstflags { - { - let _: ::core::clone::AssertParamIsClone; - *self - } - } -} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::fmt::Debug for Fstflags { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - match *self { - Fstflags(ref __self_0_0) => { - let mut debug_trait_builder = f.debug_tuple("Fstflags"); - let _ = debug_trait_builder.field(&&(*__self_0_0)); - debug_trait_builder.finish() - } - } - } -} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::hash::Hash for Fstflags { - fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) -> () { - match *self { - Fstflags(ref __self_0_0) => ::core::hash::Hash::hash(&(*__self_0_0), state), - } - } -} -impl ::core::marker::StructuralEq for Fstflags {} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::cmp::Eq for Fstflags { - #[inline] - #[doc(hidden)] - fn assert_receiver_is_total_eq(&self) -> () { - { - let _: ::core::cmp::AssertParamIsEq; - } - } -} -impl ::core::marker::StructuralPartialEq for Fstflags {} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::cmp::PartialEq for Fstflags { - #[inline] - fn eq(&self, other: &Fstflags) -> bool { - match *other { - Fstflags(ref __self_1_0) => match *self { - Fstflags(ref __self_0_0) => (*__self_0_0) == (*__self_1_0), - }, - } - } - #[inline] - fn ne(&self, other: &Fstflags) -> bool { - match *other { - Fstflags(ref __self_1_0) => match *self { - Fstflags(ref __self_0_0) => (*__self_0_0) != (*__self_1_0), - }, - } - } -} -impl Fstflags { - pub const ATIM: Fstflags = Fstflags(1); - pub const ATIM_NOW: Fstflags = Fstflags(2); - pub const MTIM: Fstflags = Fstflags(4); - pub const MTIM_NOW: Fstflags = Fstflags(8); -} -#[repr(transparent)] -pub struct Lookupflags(u32); -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::marker::Copy for Lookupflags {} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::clone::Clone for Lookupflags { - #[inline] - fn clone(&self) -> Lookupflags { - { - let _: ::core::clone::AssertParamIsClone; - *self - } - } -} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::fmt::Debug for Lookupflags { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - match *self { - Lookupflags(ref __self_0_0) => { - let mut debug_trait_builder = f.debug_tuple("Lookupflags"); - let _ = debug_trait_builder.field(&&(*__self_0_0)); - debug_trait_builder.finish() - } - } - } -} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::hash::Hash for Lookupflags { - fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) -> () { - match *self { - Lookupflags(ref __self_0_0) => ::core::hash::Hash::hash(&(*__self_0_0), state), - } - } -} -impl ::core::marker::StructuralEq for Lookupflags {} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::cmp::Eq for Lookupflags { - #[inline] - #[doc(hidden)] - fn assert_receiver_is_total_eq(&self) -> () { - { - let _: ::core::cmp::AssertParamIsEq; - } - } -} -impl ::core::marker::StructuralPartialEq for Lookupflags {} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::cmp::PartialEq for Lookupflags { - #[inline] - fn eq(&self, other: &Lookupflags) -> bool { - match *other { - Lookupflags(ref __self_1_0) => match *self { - Lookupflags(ref __self_0_0) => (*__self_0_0) == (*__self_1_0), - }, - } - } - #[inline] - fn ne(&self, other: &Lookupflags) -> bool { - match *other { - Lookupflags(ref __self_1_0) => match *self { - Lookupflags(ref __self_0_0) => (*__self_0_0) != (*__self_1_0), - }, - } - } -} -impl Lookupflags { - pub const SYMLINK_FOLLOW: Lookupflags = Lookupflags(1); -} -#[repr(transparent)] -pub struct Oflags(u16); -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::marker::Copy for Oflags {} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::clone::Clone for Oflags { - #[inline] - fn clone(&self) -> Oflags { - { - let _: ::core::clone::AssertParamIsClone; - *self - } - } -} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::fmt::Debug for Oflags { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - match *self { - Oflags(ref __self_0_0) => { - let mut debug_trait_builder = f.debug_tuple("Oflags"); - let _ = debug_trait_builder.field(&&(*__self_0_0)); - debug_trait_builder.finish() - } - } - } -} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::hash::Hash for Oflags { - fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) -> () { - match *self { - Oflags(ref __self_0_0) => ::core::hash::Hash::hash(&(*__self_0_0), state), - } - } -} -impl ::core::marker::StructuralEq for Oflags {} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::cmp::Eq for Oflags { - #[inline] - #[doc(hidden)] - fn assert_receiver_is_total_eq(&self) -> () { - { - let _: ::core::cmp::AssertParamIsEq; - } - } -} -impl ::core::marker::StructuralPartialEq for Oflags {} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::cmp::PartialEq for Oflags { - #[inline] - fn eq(&self, other: &Oflags) -> bool { - match *other { - Oflags(ref __self_1_0) => match *self { - Oflags(ref __self_0_0) => (*__self_0_0) == (*__self_1_0), - }, - } - } - #[inline] - fn ne(&self, other: &Oflags) -> bool { - match *other { - Oflags(ref __self_1_0) => match *self { - Oflags(ref __self_0_0) => (*__self_0_0) != (*__self_1_0), - }, - } - } -} -impl Oflags { - pub const CREAT: Oflags = Oflags(1); - pub const DIRECTORY: Oflags = Oflags(2); - pub const EXCL: Oflags = Oflags(4); - pub const TRUNC: Oflags = Oflags(8); -} -pub type Linkcount = u64; -#[repr(C)] -pub struct Filestat { - pub dev: Device, - pub ino: Inode, - pub filetype: Filetype, - pub nlink: Linkcount, - pub size: Filesize, - pub atim: Timestamp, - pub mtim: Timestamp, - pub ctim: Timestamp, -} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::marker::Copy for Filestat {} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::clone::Clone for Filestat { - #[inline] - fn clone(&self) -> Filestat { - { - let _: ::core::clone::AssertParamIsClone; - let _: ::core::clone::AssertParamIsClone; - let _: ::core::clone::AssertParamIsClone; - let _: ::core::clone::AssertParamIsClone; - let _: ::core::clone::AssertParamIsClone; - let _: ::core::clone::AssertParamIsClone; - let _: ::core::clone::AssertParamIsClone; - let _: ::core::clone::AssertParamIsClone; - *self - } - } -} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::fmt::Debug for Filestat { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - match *self { - Filestat { - dev: ref __self_0_0, - ino: ref __self_0_1, - filetype: ref __self_0_2, - nlink: ref __self_0_3, - size: ref __self_0_4, - atim: ref __self_0_5, - mtim: ref __self_0_6, - ctim: ref __self_0_7, - } => { - let mut debug_trait_builder = f.debug_struct("Filestat"); - let _ = debug_trait_builder.field("dev", &&(*__self_0_0)); - let _ = debug_trait_builder.field("ino", &&(*__self_0_1)); - let _ = debug_trait_builder.field("filetype", &&(*__self_0_2)); - let _ = debug_trait_builder.field("nlink", &&(*__self_0_3)); - let _ = debug_trait_builder.field("size", &&(*__self_0_4)); - let _ = debug_trait_builder.field("atim", &&(*__self_0_5)); - let _ = debug_trait_builder.field("mtim", &&(*__self_0_6)); - let _ = debug_trait_builder.field("ctim", &&(*__self_0_7)); - debug_trait_builder.finish() - } - } - } -} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::hash::Hash for Filestat { - fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) -> () { - match *self { - Filestat { - dev: ref __self_0_0, - ino: ref __self_0_1, - filetype: ref __self_0_2, - nlink: ref __self_0_3, - size: ref __self_0_4, - atim: ref __self_0_5, - mtim: ref __self_0_6, - ctim: ref __self_0_7, - } => { - ::core::hash::Hash::hash(&(*__self_0_0), state); - ::core::hash::Hash::hash(&(*__self_0_1), state); - ::core::hash::Hash::hash(&(*__self_0_2), state); - ::core::hash::Hash::hash(&(*__self_0_3), state); - ::core::hash::Hash::hash(&(*__self_0_4), state); - ::core::hash::Hash::hash(&(*__self_0_5), state); - ::core::hash::Hash::hash(&(*__self_0_6), state); - ::core::hash::Hash::hash(&(*__self_0_7), state) - } - } - } -} -impl ::core::marker::StructuralEq for Filestat {} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::cmp::Eq for Filestat { - #[inline] - #[doc(hidden)] - fn assert_receiver_is_total_eq(&self) -> () { - { - let _: ::core::cmp::AssertParamIsEq; - let _: ::core::cmp::AssertParamIsEq; - let _: ::core::cmp::AssertParamIsEq; - let _: ::core::cmp::AssertParamIsEq; - let _: ::core::cmp::AssertParamIsEq; - let _: ::core::cmp::AssertParamIsEq; - let _: ::core::cmp::AssertParamIsEq; - let _: ::core::cmp::AssertParamIsEq; - } - } -} -impl ::core::marker::StructuralPartialEq for Filestat {} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::cmp::PartialEq for Filestat { - #[inline] - fn eq(&self, other: &Filestat) -> bool { - match *other { - Filestat { - dev: ref __self_1_0, - ino: ref __self_1_1, - filetype: ref __self_1_2, - nlink: ref __self_1_3, - size: ref __self_1_4, - atim: ref __self_1_5, - mtim: ref __self_1_6, - ctim: ref __self_1_7, - } => match *self { - Filestat { - dev: ref __self_0_0, - ino: ref __self_0_1, - filetype: ref __self_0_2, - nlink: ref __self_0_3, - size: ref __self_0_4, - atim: ref __self_0_5, - mtim: ref __self_0_6, - ctim: ref __self_0_7, - } => { - (*__self_0_0) == (*__self_1_0) - && (*__self_0_1) == (*__self_1_1) - && (*__self_0_2) == (*__self_1_2) - && (*__self_0_3) == (*__self_1_3) - && (*__self_0_4) == (*__self_1_4) - && (*__self_0_5) == (*__self_1_5) - && (*__self_0_6) == (*__self_1_6) - && (*__self_0_7) == (*__self_1_7) - } - }, - } - } - #[inline] - fn ne(&self, other: &Filestat) -> bool { - match *other { - Filestat { - dev: ref __self_1_0, - ino: ref __self_1_1, - filetype: ref __self_1_2, - nlink: ref __self_1_3, - size: ref __self_1_4, - atim: ref __self_1_5, - mtim: ref __self_1_6, - ctim: ref __self_1_7, - } => match *self { - Filestat { - dev: ref __self_0_0, - ino: ref __self_0_1, - filetype: ref __self_0_2, - nlink: ref __self_0_3, - size: ref __self_0_4, - atim: ref __self_0_5, - mtim: ref __self_0_6, - ctim: ref __self_0_7, - } => { - (*__self_0_0) != (*__self_1_0) - || (*__self_0_1) != (*__self_1_1) - || (*__self_0_2) != (*__self_1_2) - || (*__self_0_3) != (*__self_1_3) - || (*__self_0_4) != (*__self_1_4) - || (*__self_0_5) != (*__self_1_5) - || (*__self_0_6) != (*__self_1_6) - || (*__self_0_7) != (*__self_1_7) - } - }, - } - } -} -pub type Userdata = u64; -#[repr(u8)] -pub enum Eventtype { - Clock, - FdRead, - FdWrite, -} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::marker::Copy for Eventtype {} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::clone::Clone for Eventtype { - #[inline] - fn clone(&self) -> Eventtype { - { - *self - } - } -} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::fmt::Debug for Eventtype { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - match (&*self,) { - (&Eventtype::Clock,) => { - let mut debug_trait_builder = f.debug_tuple("Clock"); - debug_trait_builder.finish() - } - (&Eventtype::FdRead,) => { - let mut debug_trait_builder = f.debug_tuple("FdRead"); - debug_trait_builder.finish() - } - (&Eventtype::FdWrite,) => { - let mut debug_trait_builder = f.debug_tuple("FdWrite"); - debug_trait_builder.finish() - } - } - } -} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::hash::Hash for Eventtype { - fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) -> () { - match (&*self,) { - _ => ::core::hash::Hash::hash( - &unsafe { ::core::intrinsics::discriminant_value(self) }, - state, - ), - } - } -} -impl ::core::marker::StructuralEq for Eventtype {} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::cmp::Eq for Eventtype { - #[inline] - #[doc(hidden)] - fn assert_receiver_is_total_eq(&self) -> () { - {} - } -} -impl ::core::marker::StructuralPartialEq for Eventtype {} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::cmp::PartialEq for Eventtype { - #[inline] - fn eq(&self, other: &Eventtype) -> bool { - { - let __self_vi = unsafe { ::core::intrinsics::discriminant_value(&*self) } as u8; - let __arg_1_vi = unsafe { ::core::intrinsics::discriminant_value(&*other) } as u8; - if true && __self_vi == __arg_1_vi { - match (&*self, &*other) { - _ => true, - } - } else { - false - } - } - } -} -#[repr(transparent)] -pub struct Eventrwflags(u16); -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::marker::Copy for Eventrwflags {} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::clone::Clone for Eventrwflags { - #[inline] - fn clone(&self) -> Eventrwflags { - { - let _: ::core::clone::AssertParamIsClone; - *self - } - } -} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::fmt::Debug for Eventrwflags { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - match *self { - Eventrwflags(ref __self_0_0) => { - let mut debug_trait_builder = f.debug_tuple("Eventrwflags"); - let _ = debug_trait_builder.field(&&(*__self_0_0)); - debug_trait_builder.finish() - } - } - } -} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::hash::Hash for Eventrwflags { - fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) -> () { - match *self { - Eventrwflags(ref __self_0_0) => ::core::hash::Hash::hash(&(*__self_0_0), state), - } - } -} -impl ::core::marker::StructuralEq for Eventrwflags {} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::cmp::Eq for Eventrwflags { - #[inline] - #[doc(hidden)] - fn assert_receiver_is_total_eq(&self) -> () { - { - let _: ::core::cmp::AssertParamIsEq; - } - } -} -impl ::core::marker::StructuralPartialEq for Eventrwflags {} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::cmp::PartialEq for Eventrwflags { - #[inline] - fn eq(&self, other: &Eventrwflags) -> bool { - match *other { - Eventrwflags(ref __self_1_0) => match *self { - Eventrwflags(ref __self_0_0) => (*__self_0_0) == (*__self_1_0), - }, - } - } - #[inline] - fn ne(&self, other: &Eventrwflags) -> bool { - match *other { - Eventrwflags(ref __self_1_0) => match *self { - Eventrwflags(ref __self_0_0) => (*__self_0_0) != (*__self_1_0), - }, - } - } -} -impl Eventrwflags { - pub const FD_READWRITE_HANGUP: Eventrwflags = Eventrwflags(1); -} -#[repr(C)] -pub struct EventFdReadwrite { - pub nbytes: Filesize, - pub flags: Eventrwflags, -} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::marker::Copy for EventFdReadwrite {} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::clone::Clone for EventFdReadwrite { - #[inline] - fn clone(&self) -> EventFdReadwrite { - { - let _: ::core::clone::AssertParamIsClone; - let _: ::core::clone::AssertParamIsClone; - *self - } - } -} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::fmt::Debug for EventFdReadwrite { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - match *self { - EventFdReadwrite { - nbytes: ref __self_0_0, - flags: ref __self_0_1, - } => { - let mut debug_trait_builder = f.debug_struct("EventFdReadwrite"); - let _ = debug_trait_builder.field("nbytes", &&(*__self_0_0)); - let _ = debug_trait_builder.field("flags", &&(*__self_0_1)); - debug_trait_builder.finish() - } - } - } -} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::hash::Hash for EventFdReadwrite { - fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) -> () { - match *self { - EventFdReadwrite { - nbytes: ref __self_0_0, - flags: ref __self_0_1, - } => { - ::core::hash::Hash::hash(&(*__self_0_0), state); - ::core::hash::Hash::hash(&(*__self_0_1), state) - } - } - } -} -impl ::core::marker::StructuralEq for EventFdReadwrite {} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::cmp::Eq for EventFdReadwrite { - #[inline] - #[doc(hidden)] - fn assert_receiver_is_total_eq(&self) -> () { - { - let _: ::core::cmp::AssertParamIsEq; - let _: ::core::cmp::AssertParamIsEq; - } - } -} -impl ::core::marker::StructuralPartialEq for EventFdReadwrite {} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::cmp::PartialEq for EventFdReadwrite { - #[inline] - fn eq(&self, other: &EventFdReadwrite) -> bool { - match *other { - EventFdReadwrite { - nbytes: ref __self_1_0, - flags: ref __self_1_1, - } => match *self { - EventFdReadwrite { - nbytes: ref __self_0_0, - flags: ref __self_0_1, - } => (*__self_0_0) == (*__self_1_0) && (*__self_0_1) == (*__self_1_1), - }, - } - } - #[inline] - fn ne(&self, other: &EventFdReadwrite) -> bool { - match *other { - EventFdReadwrite { - nbytes: ref __self_1_0, - flags: ref __self_1_1, - } => match *self { - EventFdReadwrite { - nbytes: ref __self_0_0, - flags: ref __self_0_1, - } => (*__self_0_0) != (*__self_1_0) || (*__self_0_1) != (*__self_1_1), - }, - } - } -} -#[repr(C)] -#[allow(missing_debug_implementations)] -pub union EventU { - pub fd_readwrite: EventFdReadwrite, -} -#[automatically_derived] -#[allow(unused_qualifications)] -#[allow(missing_debug_implementations)] -impl ::core::marker::Copy for EventU {} -#[automatically_derived] -#[allow(unused_qualifications)] -#[allow(missing_debug_implementations)] -impl ::core::clone::Clone for EventU { - #[inline] - fn clone(&self) -> EventU { - { - let _: ::core::clone::AssertParamIsCopy; - *self - } - } -} -#[repr(C)] -#[allow(missing_debug_implementations)] -pub struct Event { - pub userdata: Userdata, - pub error: Errno, - pub r#type: Eventtype, - pub u: EventU, -} -#[automatically_derived] -#[allow(unused_qualifications)] -#[allow(missing_debug_implementations)] -impl ::core::marker::Copy for Event {} -#[automatically_derived] -#[allow(unused_qualifications)] -#[allow(missing_debug_implementations)] -impl ::core::clone::Clone for Event { - #[inline] - fn clone(&self) -> Event { - { - let _: ::core::clone::AssertParamIsClone; - let _: ::core::clone::AssertParamIsClone; - let _: ::core::clone::AssertParamIsClone; - let _: ::core::clone::AssertParamIsClone; - *self - } - } -} -#[repr(transparent)] -pub struct Subclockflags(u16); -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::marker::Copy for Subclockflags {} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::clone::Clone for Subclockflags { - #[inline] - fn clone(&self) -> Subclockflags { - { - let _: ::core::clone::AssertParamIsClone; - *self - } - } -} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::fmt::Debug for Subclockflags { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - match *self { - Subclockflags(ref __self_0_0) => { - let mut debug_trait_builder = f.debug_tuple("Subclockflags"); - let _ = debug_trait_builder.field(&&(*__self_0_0)); - debug_trait_builder.finish() - } - } - } -} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::hash::Hash for Subclockflags { - fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) -> () { - match *self { - Subclockflags(ref __self_0_0) => ::core::hash::Hash::hash(&(*__self_0_0), state), - } - } -} -impl ::core::marker::StructuralEq for Subclockflags {} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::cmp::Eq for Subclockflags { - #[inline] - #[doc(hidden)] - fn assert_receiver_is_total_eq(&self) -> () { - { - let _: ::core::cmp::AssertParamIsEq; - } - } -} -impl ::core::marker::StructuralPartialEq for Subclockflags {} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::cmp::PartialEq for Subclockflags { - #[inline] - fn eq(&self, other: &Subclockflags) -> bool { - match *other { - Subclockflags(ref __self_1_0) => match *self { - Subclockflags(ref __self_0_0) => (*__self_0_0) == (*__self_1_0), - }, - } - } - #[inline] - fn ne(&self, other: &Subclockflags) -> bool { - match *other { - Subclockflags(ref __self_1_0) => match *self { - Subclockflags(ref __self_0_0) => (*__self_0_0) != (*__self_1_0), - }, - } - } -} -impl Subclockflags { - pub const SUBSCRIPTION_CLOCK_ABSTIME: Subclockflags = Subclockflags(1); -} -#[repr(C)] -pub struct SubscriptionClock { - pub id: Clockid, - pub timeout: Timestamp, - pub precision: Timestamp, - pub flags: Subclockflags, -} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::marker::Copy for SubscriptionClock {} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::clone::Clone for SubscriptionClock { - #[inline] - fn clone(&self) -> SubscriptionClock { - { - let _: ::core::clone::AssertParamIsClone; - let _: ::core::clone::AssertParamIsClone; - let _: ::core::clone::AssertParamIsClone; - let _: ::core::clone::AssertParamIsClone; - *self - } - } -} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::fmt::Debug for SubscriptionClock { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - match *self { - SubscriptionClock { - id: ref __self_0_0, - timeout: ref __self_0_1, - precision: ref __self_0_2, - flags: ref __self_0_3, - } => { - let mut debug_trait_builder = f.debug_struct("SubscriptionClock"); - let _ = debug_trait_builder.field("id", &&(*__self_0_0)); - let _ = debug_trait_builder.field("timeout", &&(*__self_0_1)); - let _ = debug_trait_builder.field("precision", &&(*__self_0_2)); - let _ = debug_trait_builder.field("flags", &&(*__self_0_3)); - debug_trait_builder.finish() - } - } - } -} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::hash::Hash for SubscriptionClock { - fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) -> () { - match *self { - SubscriptionClock { - id: ref __self_0_0, - timeout: ref __self_0_1, - precision: ref __self_0_2, - flags: ref __self_0_3, - } => { - ::core::hash::Hash::hash(&(*__self_0_0), state); - ::core::hash::Hash::hash(&(*__self_0_1), state); - ::core::hash::Hash::hash(&(*__self_0_2), state); - ::core::hash::Hash::hash(&(*__self_0_3), state) - } - } - } -} -impl ::core::marker::StructuralEq for SubscriptionClock {} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::cmp::Eq for SubscriptionClock { - #[inline] - #[doc(hidden)] - fn assert_receiver_is_total_eq(&self) -> () { - { - let _: ::core::cmp::AssertParamIsEq; - let _: ::core::cmp::AssertParamIsEq; - let _: ::core::cmp::AssertParamIsEq; - let _: ::core::cmp::AssertParamIsEq; - } - } -} -impl ::core::marker::StructuralPartialEq for SubscriptionClock {} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::cmp::PartialEq for SubscriptionClock { - #[inline] - fn eq(&self, other: &SubscriptionClock) -> bool { - match *other { - SubscriptionClock { - id: ref __self_1_0, - timeout: ref __self_1_1, - precision: ref __self_1_2, - flags: ref __self_1_3, - } => match *self { - SubscriptionClock { - id: ref __self_0_0, - timeout: ref __self_0_1, - precision: ref __self_0_2, - flags: ref __self_0_3, - } => { - (*__self_0_0) == (*__self_1_0) - && (*__self_0_1) == (*__self_1_1) - && (*__self_0_2) == (*__self_1_2) - && (*__self_0_3) == (*__self_1_3) - } - }, - } - } - #[inline] - fn ne(&self, other: &SubscriptionClock) -> bool { - match *other { - SubscriptionClock { - id: ref __self_1_0, - timeout: ref __self_1_1, - precision: ref __self_1_2, - flags: ref __self_1_3, - } => match *self { - SubscriptionClock { - id: ref __self_0_0, - timeout: ref __self_0_1, - precision: ref __self_0_2, - flags: ref __self_0_3, - } => { - (*__self_0_0) != (*__self_1_0) - || (*__self_0_1) != (*__self_1_1) - || (*__self_0_2) != (*__self_1_2) - || (*__self_0_3) != (*__self_1_3) - } - }, - } - } -} -#[repr(C)] -pub struct SubscriptionFdReadwrite { - pub file_descriptor: Fd, -} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::marker::Copy for SubscriptionFdReadwrite {} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::clone::Clone for SubscriptionFdReadwrite { - #[inline] - fn clone(&self) -> SubscriptionFdReadwrite { - { - let _: ::core::clone::AssertParamIsClone; - *self - } - } -} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::fmt::Debug for SubscriptionFdReadwrite { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - match *self { - SubscriptionFdReadwrite { - file_descriptor: ref __self_0_0, - } => { - let mut debug_trait_builder = f.debug_struct("SubscriptionFdReadwrite"); - let _ = debug_trait_builder.field("file_descriptor", &&(*__self_0_0)); - debug_trait_builder.finish() - } - } - } -} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::hash::Hash for SubscriptionFdReadwrite { - fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) -> () { - match *self { - SubscriptionFdReadwrite { - file_descriptor: ref __self_0_0, - } => ::core::hash::Hash::hash(&(*__self_0_0), state), - } - } -} -impl ::core::marker::StructuralEq for SubscriptionFdReadwrite {} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::cmp::Eq for SubscriptionFdReadwrite { - #[inline] - #[doc(hidden)] - fn assert_receiver_is_total_eq(&self) -> () { - { - let _: ::core::cmp::AssertParamIsEq; - } - } -} -impl ::core::marker::StructuralPartialEq for SubscriptionFdReadwrite {} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::cmp::PartialEq for SubscriptionFdReadwrite { - #[inline] - fn eq(&self, other: &SubscriptionFdReadwrite) -> bool { - match *other { - SubscriptionFdReadwrite { - file_descriptor: ref __self_1_0, - } => match *self { - SubscriptionFdReadwrite { - file_descriptor: ref __self_0_0, - } => (*__self_0_0) == (*__self_1_0), - }, - } - } - #[inline] - fn ne(&self, other: &SubscriptionFdReadwrite) -> bool { - match *other { - SubscriptionFdReadwrite { - file_descriptor: ref __self_1_0, - } => match *self { - SubscriptionFdReadwrite { - file_descriptor: ref __self_0_0, - } => (*__self_0_0) != (*__self_1_0), - }, - } - } -} -#[repr(C)] -#[allow(missing_debug_implementations)] -pub union SubscriptionU { - pub clock: SubscriptionClock, - pub fd_readwrite: SubscriptionFdReadwrite, -} -#[automatically_derived] -#[allow(unused_qualifications)] -#[allow(missing_debug_implementations)] -impl ::core::marker::Copy for SubscriptionU {} -#[automatically_derived] -#[allow(unused_qualifications)] -#[allow(missing_debug_implementations)] -impl ::core::clone::Clone for SubscriptionU { - #[inline] - fn clone(&self) -> SubscriptionU { - { - let _: ::core::clone::AssertParamIsCopy; - *self - } - } -} -#[repr(C)] -#[allow(missing_debug_implementations)] -pub struct Subscription { - pub userdata: Userdata, - pub r#type: Eventtype, - pub u: SubscriptionU, -} -#[automatically_derived] -#[allow(unused_qualifications)] -#[allow(missing_debug_implementations)] -impl ::core::marker::Copy for Subscription {} -#[automatically_derived] -#[allow(unused_qualifications)] -#[allow(missing_debug_implementations)] -impl ::core::clone::Clone for Subscription { - #[inline] - fn clone(&self) -> Subscription { - { - let _: ::core::clone::AssertParamIsClone; - let _: ::core::clone::AssertParamIsClone; - let _: ::core::clone::AssertParamIsClone; - *self - } - } -} -pub type Exitcode = u32; -#[repr(u8)] -pub enum Signal { - None, - Hup, - Int, - Quit, - Ill, - Trap, - Abrt, - Bus, - Fpe, - Kill, - Usr1, - Segv, - Usr2, - Pipe, - Alrm, - Term, - Chld, - Cont, - Stop, - Tstp, - Ttin, - Ttou, - Urg, - Xcpu, - Xfsz, - Vtalrm, - Prof, - Winch, - Poll, - Pwr, - Sys, -} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::marker::Copy for Signal {} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::clone::Clone for Signal { - #[inline] - fn clone(&self) -> Signal { - { - *self - } - } -} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::fmt::Debug for Signal { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - match (&*self,) { - (&Signal::None,) => { - let mut debug_trait_builder = f.debug_tuple("None"); - debug_trait_builder.finish() - } - (&Signal::Hup,) => { - let mut debug_trait_builder = f.debug_tuple("Hup"); - debug_trait_builder.finish() - } - (&Signal::Int,) => { - let mut debug_trait_builder = f.debug_tuple("Int"); - debug_trait_builder.finish() - } - (&Signal::Quit,) => { - let mut debug_trait_builder = f.debug_tuple("Quit"); - debug_trait_builder.finish() - } - (&Signal::Ill,) => { - let mut debug_trait_builder = f.debug_tuple("Ill"); - debug_trait_builder.finish() - } - (&Signal::Trap,) => { - let mut debug_trait_builder = f.debug_tuple("Trap"); - debug_trait_builder.finish() - } - (&Signal::Abrt,) => { - let mut debug_trait_builder = f.debug_tuple("Abrt"); - debug_trait_builder.finish() - } - (&Signal::Bus,) => { - let mut debug_trait_builder = f.debug_tuple("Bus"); - debug_trait_builder.finish() - } - (&Signal::Fpe,) => { - let mut debug_trait_builder = f.debug_tuple("Fpe"); - debug_trait_builder.finish() - } - (&Signal::Kill,) => { - let mut debug_trait_builder = f.debug_tuple("Kill"); - debug_trait_builder.finish() - } - (&Signal::Usr1,) => { - let mut debug_trait_builder = f.debug_tuple("Usr1"); - debug_trait_builder.finish() - } - (&Signal::Segv,) => { - let mut debug_trait_builder = f.debug_tuple("Segv"); - debug_trait_builder.finish() - } - (&Signal::Usr2,) => { - let mut debug_trait_builder = f.debug_tuple("Usr2"); - debug_trait_builder.finish() - } - (&Signal::Pipe,) => { - let mut debug_trait_builder = f.debug_tuple("Pipe"); - debug_trait_builder.finish() - } - (&Signal::Alrm,) => { - let mut debug_trait_builder = f.debug_tuple("Alrm"); - debug_trait_builder.finish() - } - (&Signal::Term,) => { - let mut debug_trait_builder = f.debug_tuple("Term"); - debug_trait_builder.finish() - } - (&Signal::Chld,) => { - let mut debug_trait_builder = f.debug_tuple("Chld"); - debug_trait_builder.finish() - } - (&Signal::Cont,) => { - let mut debug_trait_builder = f.debug_tuple("Cont"); - debug_trait_builder.finish() - } - (&Signal::Stop,) => { - let mut debug_trait_builder = f.debug_tuple("Stop"); - debug_trait_builder.finish() - } - (&Signal::Tstp,) => { - let mut debug_trait_builder = f.debug_tuple("Tstp"); - debug_trait_builder.finish() - } - (&Signal::Ttin,) => { - let mut debug_trait_builder = f.debug_tuple("Ttin"); - debug_trait_builder.finish() - } - (&Signal::Ttou,) => { - let mut debug_trait_builder = f.debug_tuple("Ttou"); - debug_trait_builder.finish() - } - (&Signal::Urg,) => { - let mut debug_trait_builder = f.debug_tuple("Urg"); - debug_trait_builder.finish() - } - (&Signal::Xcpu,) => { - let mut debug_trait_builder = f.debug_tuple("Xcpu"); - debug_trait_builder.finish() - } - (&Signal::Xfsz,) => { - let mut debug_trait_builder = f.debug_tuple("Xfsz"); - debug_trait_builder.finish() - } - (&Signal::Vtalrm,) => { - let mut debug_trait_builder = f.debug_tuple("Vtalrm"); - debug_trait_builder.finish() - } - (&Signal::Prof,) => { - let mut debug_trait_builder = f.debug_tuple("Prof"); - debug_trait_builder.finish() - } - (&Signal::Winch,) => { - let mut debug_trait_builder = f.debug_tuple("Winch"); - debug_trait_builder.finish() - } - (&Signal::Poll,) => { - let mut debug_trait_builder = f.debug_tuple("Poll"); - debug_trait_builder.finish() - } - (&Signal::Pwr,) => { - let mut debug_trait_builder = f.debug_tuple("Pwr"); - debug_trait_builder.finish() - } - (&Signal::Sys,) => { - let mut debug_trait_builder = f.debug_tuple("Sys"); - debug_trait_builder.finish() - } - } - } -} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::hash::Hash for Signal { - fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) -> () { - match (&*self,) { - _ => ::core::hash::Hash::hash( - &unsafe { ::core::intrinsics::discriminant_value(self) }, - state, - ), - } - } -} -impl ::core::marker::StructuralEq for Signal {} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::cmp::Eq for Signal { - #[inline] - #[doc(hidden)] - fn assert_receiver_is_total_eq(&self) -> () { - {} - } -} -impl ::core::marker::StructuralPartialEq for Signal {} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::cmp::PartialEq for Signal { - #[inline] - fn eq(&self, other: &Signal) -> bool { - { - let __self_vi = unsafe { ::core::intrinsics::discriminant_value(&*self) } as u8; - let __arg_1_vi = unsafe { ::core::intrinsics::discriminant_value(&*other) } as u8; - if true && __self_vi == __arg_1_vi { - match (&*self, &*other) { - _ => true, - } - } else { - false - } - } - } -} -#[repr(transparent)] -pub struct Riflags(u16); -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::marker::Copy for Riflags {} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::clone::Clone for Riflags { - #[inline] - fn clone(&self) -> Riflags { - { - let _: ::core::clone::AssertParamIsClone; - *self - } - } -} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::fmt::Debug for Riflags { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - match *self { - Riflags(ref __self_0_0) => { - let mut debug_trait_builder = f.debug_tuple("Riflags"); - let _ = debug_trait_builder.field(&&(*__self_0_0)); - debug_trait_builder.finish() - } - } - } -} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::hash::Hash for Riflags { - fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) -> () { - match *self { - Riflags(ref __self_0_0) => ::core::hash::Hash::hash(&(*__self_0_0), state), - } - } -} -impl ::core::marker::StructuralEq for Riflags {} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::cmp::Eq for Riflags { - #[inline] - #[doc(hidden)] - fn assert_receiver_is_total_eq(&self) -> () { - { - let _: ::core::cmp::AssertParamIsEq; - } - } -} -impl ::core::marker::StructuralPartialEq for Riflags {} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::cmp::PartialEq for Riflags { - #[inline] - fn eq(&self, other: &Riflags) -> bool { - match *other { - Riflags(ref __self_1_0) => match *self { - Riflags(ref __self_0_0) => (*__self_0_0) == (*__self_1_0), - }, - } - } - #[inline] - fn ne(&self, other: &Riflags) -> bool { - match *other { - Riflags(ref __self_1_0) => match *self { - Riflags(ref __self_0_0) => (*__self_0_0) != (*__self_1_0), - }, - } - } -} -impl Riflags { - pub const RECV_PEEK: Riflags = Riflags(1); - pub const RECV_WAITALL: Riflags = Riflags(2); -} -#[repr(transparent)] -pub struct Roflags(u16); -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::marker::Copy for Roflags {} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::clone::Clone for Roflags { - #[inline] - fn clone(&self) -> Roflags { - { - let _: ::core::clone::AssertParamIsClone; - *self - } - } -} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::fmt::Debug for Roflags { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - match *self { - Roflags(ref __self_0_0) => { - let mut debug_trait_builder = f.debug_tuple("Roflags"); - let _ = debug_trait_builder.field(&&(*__self_0_0)); - debug_trait_builder.finish() - } - } - } -} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::hash::Hash for Roflags { - fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) -> () { - match *self { - Roflags(ref __self_0_0) => ::core::hash::Hash::hash(&(*__self_0_0), state), - } - } -} -impl ::core::marker::StructuralEq for Roflags {} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::cmp::Eq for Roflags { - #[inline] - #[doc(hidden)] - fn assert_receiver_is_total_eq(&self) -> () { - { - let _: ::core::cmp::AssertParamIsEq; - } - } -} -impl ::core::marker::StructuralPartialEq for Roflags {} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::cmp::PartialEq for Roflags { - #[inline] - fn eq(&self, other: &Roflags) -> bool { - match *other { - Roflags(ref __self_1_0) => match *self { - Roflags(ref __self_0_0) => (*__self_0_0) == (*__self_1_0), - }, - } - } - #[inline] - fn ne(&self, other: &Roflags) -> bool { - match *other { - Roflags(ref __self_1_0) => match *self { - Roflags(ref __self_0_0) => (*__self_0_0) != (*__self_1_0), - }, - } - } -} -impl Roflags { - pub const RECV_DATA_TRUNCATED: Roflags = Roflags(1); -} -pub type Siflags = u16; -#[repr(transparent)] -pub struct Sdflags(u8); -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::marker::Copy for Sdflags {} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::clone::Clone for Sdflags { - #[inline] - fn clone(&self) -> Sdflags { - { - let _: ::core::clone::AssertParamIsClone; - *self - } - } -} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::fmt::Debug for Sdflags { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - match *self { - Sdflags(ref __self_0_0) => { - let mut debug_trait_builder = f.debug_tuple("Sdflags"); - let _ = debug_trait_builder.field(&&(*__self_0_0)); - debug_trait_builder.finish() - } - } - } -} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::hash::Hash for Sdflags { - fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) -> () { - match *self { - Sdflags(ref __self_0_0) => ::core::hash::Hash::hash(&(*__self_0_0), state), - } - } -} -impl ::core::marker::StructuralEq for Sdflags {} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::cmp::Eq for Sdflags { - #[inline] - #[doc(hidden)] - fn assert_receiver_is_total_eq(&self) -> () { - { - let _: ::core::cmp::AssertParamIsEq; - } - } -} -impl ::core::marker::StructuralPartialEq for Sdflags {} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::cmp::PartialEq for Sdflags { - #[inline] - fn eq(&self, other: &Sdflags) -> bool { - match *other { - Sdflags(ref __self_1_0) => match *self { - Sdflags(ref __self_0_0) => (*__self_0_0) == (*__self_1_0), - }, - } - } - #[inline] - fn ne(&self, other: &Sdflags) -> bool { - match *other { - Sdflags(ref __self_1_0) => match *self { - Sdflags(ref __self_0_0) => (*__self_0_0) != (*__self_1_0), - }, - } - } -} -impl Sdflags { - pub const RD: Sdflags = Sdflags(1); - pub const WR: Sdflags = Sdflags(2); -} -#[repr(u8)] -pub enum Preopentype { - Dir, -} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::marker::Copy for Preopentype {} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::clone::Clone for Preopentype { - #[inline] - fn clone(&self) -> Preopentype { - { - *self - } - } -} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::fmt::Debug for Preopentype { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - match (&*self,) { - (&Preopentype::Dir,) => { - let mut debug_trait_builder = f.debug_tuple("Dir"); - debug_trait_builder.finish() - } - } - } -} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::hash::Hash for Preopentype { - fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) -> () { - match (&*self,) { - _ => {} - } - } -} -impl ::core::marker::StructuralEq for Preopentype {} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::cmp::Eq for Preopentype { - #[inline] - #[doc(hidden)] - fn assert_receiver_is_total_eq(&self) -> () { - {} - } -} -impl ::core::marker::StructuralPartialEq for Preopentype {} -#[automatically_derived] -#[allow(unused_qualifications)] -impl ::core::cmp::PartialEq for Preopentype { - #[inline] - fn eq(&self, other: &Preopentype) -> bool { - match (&*self, &*other) { - _ => true, - } - } -} diff --git a/src/lib.rs b/src/lib.rs index 145e6ea783..2b66a42972 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,100 +1,3 @@ -pub mod test { - // FIXME: parameterize macro on what ctx type is used here - generate::from_witx!({ - witx: ["test.witx"], - ctx: WasiCtx, - }); - - pub struct WasiCtx { - guest_errors: Vec<::memory::GuestError>, - } - - impl foo::Foo for WasiCtx { - fn bar(&mut self, an_int: u32, an_float: f32) -> Result<(), types::Errno> { - println!("BAR: {} {}", an_int, an_float); - Ok(()) - } - fn baz( - &mut self, - excuse: types::Excuse, - a_better_excuse_by_reference: ::memory::GuestPtrMut, - a_lamer_excuse_by_reference: ::memory::GuestPtr, - two_layers_of_excuses: ::memory::GuestPtrMut<::memory::GuestPtr>, - ) -> Result<(), types::Errno> { - // Read enum value from mutable: - let mut a_better_excuse_ref: ::memory::GuestRefMut = - a_better_excuse_by_reference.as_ref_mut().map_err(|e| { - eprintln!("a_better_excuse_by_reference error: {}", e); - types::Errno::InvalidArg - })?; - let a_better_excuse: types::Excuse = *a_better_excuse_ref; - - // Read enum value from immutable ptr: - let a_lamer_excuse = *a_lamer_excuse_by_reference.as_ref().map_err(|e| { - eprintln!("a_lamer_excuse_by_reference error: {}", e); - types::Errno::InvalidArg - })?; - - // Write enum to mutable ptr: - *a_better_excuse_ref = a_lamer_excuse; - - // Read ptr value from mutable ptr: - let one_layer_down: ::memory::GuestPtr = - two_layers_of_excuses.read_ptr_from_guest().map_err(|e| { - eprintln!("one_layer_down error: {}", e); - types::Errno::InvalidArg - })?; - - // Read enum value from that ptr: - let two_layers_down: types::Excuse = *one_layer_down.as_ref().map_err(|e| { - eprintln!("two_layers_down error: {}", e); - types::Errno::InvalidArg - })?; - - // Write ptr value to mutable ptr: - two_layers_of_excuses.write_ptr_to_guest(&a_better_excuse_by_reference.as_immut()); - - println!( - "BAZ: excuse: {:?}, better excuse: {:?}, lamer excuse: {:?}, two layers down: {:?}", - excuse, a_better_excuse, a_lamer_excuse, two_layers_down - ); - Ok(()) - } - - fn bat(&mut self, an_int: u32) -> Result { - println!("bat: {}", an_int); - Ok((an_int as f32) * 2.0) - } - - fn sum_of_pair(&mut self, an_pair: &types::PairInts) -> Result { - println!("sum of pair: {:?}", an_pair); - Ok(an_pair.first as i64 + an_pair.second as i64) - } - fn sum_of_pair_of_ptrs( - &mut self, - an_pair: &types::PairIntPtrs, - ) -> Result { - let first = *an_pair.first.as_ref().unwrap(); - let second = *an_pair.second.as_ref().unwrap(); - println!("sum of pair of ptrs: {} + {}", first, second); - Ok(first as i64 + second as i64) - } - } - // Errno is used as a first return value in the functions above, therefore - // it must implement GuestErrorType with type Context = WasiCtx. - // The context type should let you do logging or debugging or whatever you need - // with these errors. We just push them to vecs. - impl ::memory::GuestErrorType for types::Errno { - type Context = WasiCtx; - fn success() -> types::Errno { - types::Errno::Ok - } - fn from_error(e: ::memory::GuestError, ctx: &mut WasiCtx) -> types::Errno { - ctx.guest_errors.push(e); - types::Errno::InvalidArg - } - } -} /* pub mod wasi { generate::from_witx!("crates/WASI/phases/snapshot/witx/wasi_snapshot_preview1.witx"); diff --git a/tests/main.rs b/tests/main.rs new file mode 100644 index 0000000000..df4f6341c7 --- /dev/null +++ b/tests/main.rs @@ -0,0 +1,202 @@ +generate::from_witx!({ + witx: ["tests/test.witx"], + ctx: WasiCtx, +}); + +use crate::foo::Foo; + +pub struct WasiCtx { + guest_errors: Vec<::memory::GuestError>, +} + +impl WasiCtx { + pub fn new() -> Self { + Self { + guest_errors: vec![], + } + } +} + +impl foo::Foo for WasiCtx { + fn bar(&mut self, an_int: u32, an_float: f32) -> Result<(), types::Errno> { + println!("BAR: {} {}", an_int, an_float); + Ok(()) + } + + fn baz( + &mut self, + _excuse: types::Excuse, + a_better_excuse_by_reference: ::memory::GuestPtrMut, + a_lamer_excuse_by_reference: ::memory::GuestPtr, + two_layers_of_excuses: ::memory::GuestPtrMut<::memory::GuestPtr>, + ) -> Result<(), types::Errno> { + // Read enum value from mutable: + let mut a_better_excuse_ref: ::memory::GuestRefMut = + a_better_excuse_by_reference.as_ref_mut().map_err(|e| { + eprintln!("a_better_excuse_by_reference error: {}", e); + types::Errno::InvalidArg + })?; + let _a_better_excuse: types::Excuse = *a_better_excuse_ref; + + // Read enum value from immutable ptr: + let a_lamer_excuse = *a_lamer_excuse_by_reference.as_ref().map_err(|e| { + eprintln!("a_lamer_excuse_by_reference error: {}", e); + types::Errno::InvalidArg + })?; + println!("{:?}", a_lamer_excuse); + + // Write enum to mutable ptr: + *a_better_excuse_ref = a_lamer_excuse; + println!("{:?}", *a_better_excuse_ref); + + // Read ptr value from mutable ptr: + let one_layer_down: ::memory::GuestPtr = + two_layers_of_excuses.read_ptr_from_guest().map_err(|e| { + eprintln!("one_layer_down error: {}", e); + types::Errno::InvalidArg + })?; + + // Read enum value from that ptr: + let _two_layers_down: types::Excuse = *one_layer_down.as_ref().map_err(|e| { + eprintln!("two_layers_down error: {}", e); + types::Errno::InvalidArg + })?; + + // Write ptr value to mutable ptr: + two_layers_of_excuses.write_ptr_to_guest(&a_better_excuse_by_reference.as_immut()); + + Ok(()) + } + + fn bat(&mut self, an_int: u32) -> Result { + Ok((an_int as f32) * 2.0) + } + + fn sum_of_pair(&mut self, an_pair: &types::PairInts) -> Result { + Ok(an_pair.first as i64 + an_pair.second as i64) + } + + fn sum_of_pair_of_ptrs(&mut self, an_pair: &types::PairIntPtrs) -> Result { + let first = *an_pair + .first + .as_ref() + .expect("dereferencing GuestPtr should succeed"); + let second = *an_pair + .second + .as_ref() + .expect("dereferncing GuestPtr should succeed"); + Ok(first as i64 + second as i64) + } +} +// Errno is used as a first return value in the functions above, therefore +// it must implement GuestErrorType with type Context = WasiCtx. +// The context type should let you do logging or debugging or whatever you need +// with these errors. We just push them to vecs. +impl ::memory::GuestErrorType for types::Errno { + type Context = WasiCtx; + fn success() -> types::Errno { + types::Errno::Ok + } + fn from_error(e: ::memory::GuestError, ctx: &mut WasiCtx) -> types::Errno { + ctx.guest_errors.push(e); + types::Errno::InvalidArg + } +} + +#[repr(align(4096))] +struct HostMemory { + buffer: [u8; 4096], +} +impl HostMemory { + pub fn new() -> Self { + HostMemory { buffer: [0; 4096] } + } + pub fn as_mut_ptr(&mut self) -> *mut u8 { + self.buffer.as_mut_ptr() + } + pub fn len(&self) -> usize { + self.buffer.len() + } +} + +#[test] +fn hostmemory_is_aligned() { + let mut h = HostMemory::new(); + assert_eq!(h.as_mut_ptr() as usize % 4096, 0); + let mut h = Box::new(HostMemory::new()); + assert_eq!(h.as_mut_ptr() as usize % 4096, 0); +} + +#[test] +fn bat() { + let mut ctx = WasiCtx::new(); + assert_eq!(ctx.bat(2), Ok(4.0)); +} + +#[test] +fn baz() { + let mut ctx = WasiCtx::new(); + let mut host_memory = HostMemory::new(); + let guest_memory = memory::GuestMemory::new(host_memory.as_mut_ptr(), host_memory.len() as u32); + let sizeof_excuse = std::mem::size_of::(); + let padding = 4 - sizeof_excuse % 4; + { + let lame_mut: memory::GuestPtrMut = guest_memory.ptr_mut(0).unwrap(); + let mut lame = lame_mut.as_ref_mut().unwrap(); + *lame = types::Excuse::Sleeping; + } + let lame: memory::GuestPtr = guest_memory + .ptr(0) + .expect("GuestPtr fits in the memory"); + assert_eq!(*lame.as_ref().unwrap(), types::Excuse::Sleeping); + let better: memory::GuestPtrMut = guest_memory + .ptr_mut((sizeof_excuse + padding) as u32) + .expect("GuestPtr fits in the memory"); + let ptr_to_ptr: memory::GuestPtrMut> = guest_memory + .ptr_mut((sizeof_excuse + padding) as u32 * 2) + .expect("GuestPtr> fits in the memory"); + assert!(ctx + .baz( + types::Excuse::DogAte, + better.clone(), + lame, + ptr_to_ptr.clone() + ) + .is_ok()); + assert_eq!(*better.as_ref().unwrap(), types::Excuse::Sleeping); + let ptr = ptr_to_ptr.read_ptr_from_guest().unwrap(); + assert_eq!(*ptr.as_ref().unwrap(), types::Excuse::Sleeping); +} + +#[test] +fn sum_of_pair() { + let mut ctx = WasiCtx::new(); + let pair = types::PairInts { + first: 1, + second: 2, + }; + assert_eq!(ctx.sum_of_pair(&pair), Ok(3)); +} + +#[test] +fn sum_of_pair_of_ptrs() { + let mut ctx = WasiCtx::new(); + let mut host_memory = HostMemory::new(); + let guest_memory = memory::GuestMemory::new(host_memory.as_mut_ptr(), host_memory.len() as u32); + { + let first_mut: memory::GuestPtrMut = guest_memory.ptr_mut(0).unwrap(); + let mut x = first_mut.as_ref_mut().unwrap(); + *x = 1; + let second_mut: memory::GuestPtrMut = guest_memory.ptr_mut(4).unwrap(); + let mut x = second_mut.as_ref_mut().unwrap(); + *x = 2; + } + let first: memory::GuestPtr = guest_memory + .ptr(0) + .expect("GuestPtr fits in the memory"); + let second: memory::GuestPtr = guest_memory + .ptr(4) + .expect("GuestPtr fits in the memory"); + let pair = types::PairIntPtrs { first, second }; + assert_eq!(ctx.sum_of_pair_of_ptrs(&pair), Ok(3)); +} diff --git a/test.witx b/tests/test.witx similarity index 99% rename from test.witx rename to tests/test.witx index 41036284e1..40e2aab7ed 100644 --- a/test.witx +++ b/tests/test.witx @@ -1,5 +1,3 @@ - - (typename $errno (enum u32 $ok From e6cec049cba851b59d18bbb3b4bfa2818182a268 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Mon, 3 Feb 2020 09:59:09 +0100 Subject: [PATCH 35/86] Add basic CI conf (#3) * Add basic CI conf * Pull in submodules in Rustfmt job * Exclude WASI from workspace; fix name clash in memory crate * Refactor CI conf --- .github/workflows/main.yml | 61 ++++++++++++++++++++++++++++++++++++++ Cargo.toml | 6 +++- crates/memory/src/lib.rs | 2 +- 3 files changed, 67 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/main.yml diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000000..4d66e0c055 --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,61 @@ +name: CI + +on: + push: + branches: + - master + pull_request: + +jobs: + rustfmt: + name: Rustfmt + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + with: + submodules: true + - name: Install Rust + run: | + rustup update stable + rustup default stable + rustup component add rustfmt + - name: Cargo fmt + run: cargo fmt --all -- --check + + build: + name: Build + runs-on: ${{ matrix.os }}-latest + strategy: + matrix: + os: [ubuntu, macOS, windows] + + steps: + - uses: actions/checkout@v1 + with: + submodules: true + - name: Install Rust + shell: bash + run: | + rustup update stable + rustup default stable + - name: Build + run: cargo build --all --release -vv + + test: + name: Test + runs-on: ${{ matrix.os }}-latest + strategy: + matrix: + os: [ubuntu, macOS, windows] + + steps: + - uses: actions/checkout@v1 + with: + submodules: true + - name: Install Rust + shell: bash + run: | + rustup update stable + rustup default stable + - name: Test + run: cargo test --all diff --git a/Cargo.toml b/Cargo.toml index 4089caf073..80d1545ab0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,4 +9,8 @@ generate = { path = "crates/generate" } memory = { path = "crates/memory" } [workspace] -members = ["crates/generate"] +members = [ + "crates/generate", + "crates/memory" +] +exclude = ["crates/WASI"] diff --git a/crates/memory/src/lib.rs b/crates/memory/src/lib.rs index 6f2b17ecfc..fa1824f3b6 100644 --- a/crates/memory/src/lib.rs +++ b/crates/memory/src/lib.rs @@ -4,7 +4,7 @@ mod guest_type; mod memory; mod region; +pub use self::memory::{GuestMemory, GuestPtr, GuestPtrMut, GuestRef, GuestRefMut}; pub use error::GuestError; pub use guest_type::{GuestErrorType, GuestType, GuestTypeClone, GuestTypeCopy, GuestTypePtr}; -pub use memory::{GuestMemory, GuestPtr, GuestPtrMut, GuestRef, GuestRefMut}; pub use region::Region; From 67d2ce6d85af70aefa378fd65404e7deb9380929 Mon Sep 17 00:00:00 2001 From: Pat Hickey Date: Tue, 4 Feb 2020 13:35:39 -0800 Subject: [PATCH 36/86] Refactor tests to use proptest (#6) * generator: take an &mut GuestMemory rather than pass the owned GuestMemory in, just give exclusive access to it. Makes testing easier. * tests: start transforming tests to check abi-level generated code as well * finish lowering of test funcs * tests: rename variables to more sensible names * proptesting: reliably finds that we dont allow stuff to be right against end of memory! * memory: fix off-by-one calc in GuestMemory::contains(&self, Region) ty proptest! also, refactored the Region::overlaps to be the same code but easier to read. * generator: better location information in GuestError * testing: proptest generates memory areas, tests everything --- .gitignore | 1 + Cargo.toml | 3 + crates/generate/src/funcs.rs | 28 +- crates/generate/src/types.rs | 8 +- crates/memory/src/error.rs | 11 +- crates/memory/src/memory.rs | 2 +- crates/memory/src/region.rs | 4 +- tests/main.rs | 521 +++++++++++++++++++++++++++++------ 8 files changed, 471 insertions(+), 107 deletions(-) diff --git a/.gitignore b/.gitignore index 693699042b..d95f1f6a78 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ /target **/*.rs.bk Cargo.lock +proptest-regressions diff --git a/Cargo.toml b/Cargo.toml index 80d1545ab0..f71705b021 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,6 +8,9 @@ edition = "2018" generate = { path = "crates/generate" } memory = { path = "crates/memory" } +[dev-dependencies] +proptest = "0.9" + [workspace] members = [ "crates/generate", diff --git a/crates/generate/src/funcs.rs b/crates/generate/src/funcs.rs index bd3d1f0349..336199d222 100644 --- a/crates/generate/src/funcs.rs +++ b/crates/generate/src/funcs.rs @@ -5,6 +5,8 @@ use crate::names::Names; use crate::types::{anon_lifetime, struct_is_copy}; pub fn define_func(names: &Names, func: &witx::InterfaceFunc) -> TokenStream { + let funcname = func.name.as_str(); + let ident = names.func(&func.name); let ctx_type = names.ctx_type(); let coretype = func.core_type(); @@ -28,7 +30,7 @@ pub fn define_func(names: &Names, func: &witx::InterfaceFunc) -> TokenStream { }); let abi_args = quote!( - ctx: &mut #ctx_type, memory: ::memory::GuestMemory, + ctx: &mut #ctx_type, memory: &mut ::memory::GuestMemory, #(#params),* ); let abi_ret = if let Some(ret) = &coretype.ret { @@ -52,7 +54,7 @@ pub fn define_func(names: &Names, func: &witx::InterfaceFunc) -> TokenStream { .map(|_res| quote!(#abi_ret::from(e))) .unwrap_or_else(|| quote!(())); - let error_handling: TokenStream = { + let error_handling = |location: &str| -> TokenStream { if let Some(tref) = &err_type { let abi_ret = match tref.type_().passed_by() { witx::TypePassedBy::Value(atom) => names.atom_type(atom), @@ -60,6 +62,7 @@ pub fn define_func(names: &Names, func: &witx::InterfaceFunc) -> TokenStream { }; let err_typename = names.type_ref(&tref, anon_lifetime()); quote! { + let e = ::memory::GuestError::InFunc { funcname: #funcname, location: #location, err: Box::new(e) }; let err: #err_typename = ::memory::GuestErrorType::from_error(e, ctx); return #abi_ret::from(err); } @@ -73,7 +76,7 @@ pub fn define_func(names: &Names, func: &witx::InterfaceFunc) -> TokenStream { let marshal_args = func .params .iter() - .map(|p| marshal_arg(names, p, error_handling.clone())); + .map(|p| marshal_arg(names, p, error_handling(p.name.as_str()))); let trait_args = func.params.iter().map(|param| { let name = names.func_param(¶m.name); match param.tref.type_().passed_by() { @@ -101,11 +104,11 @@ pub fn define_func(names: &Names, func: &witx::InterfaceFunc) -> TokenStream { .results .iter() .skip(1) - .map(|result| marshal_result(names, result, error_handling.clone())); + .map(|result| marshal_result(names, result, &error_handling)); let marshal_rets_pre = marshal_rets.clone().map(|(pre, _post)| pre); let marshal_rets_post = marshal_rets.map(|(_pre, post)| post); - let success = if let Some(err_type) = err_type { + let success = if let Some(ref err_type) = err_type { let err_typename = names.type_ref(&err_type, anon_lifetime()); quote! { let success:#err_typename = ::memory::GuestErrorType::success(); @@ -243,27 +246,32 @@ fn marshal_arg( } } -fn marshal_result( +fn marshal_result( names: &Names, result: &witx::InterfaceFuncParam, - error_handling: TokenStream, -) -> (TokenStream, TokenStream) { + error_handling: F, +) -> (TokenStream, TokenStream) +where + F: Fn(&str) -> TokenStream, +{ let tref = &result.tref; let write_val_to_ptr = { let pointee_type = names.type_ref(tref, anon_lifetime()); // core type is given func_ptr_binding name. let ptr_name = names.func_ptr_binding(&result.name); + let ptr_err_handling = error_handling(&format!("{}:result_ptr_mut", result.name.as_str())); + let ref_err_handling = error_handling(&format!("{}:result_ref_mut", result.name.as_str())); let pre = quote! { let mut #ptr_name = match memory.ptr_mut::<#pointee_type>(#ptr_name as u32) { Ok(p) => match p.as_ref_mut() { Ok(r) => r, Err(e) => { - #error_handling + #ref_err_handling } }, Err(e) => { - #error_handling + #ptr_err_handling } }; }; diff --git a/crates/generate/src/types.rs b/crates/generate/src/types.rs index e5cd4c0abe..f5bfd42bd1 100644 --- a/crates/generate/src/types.rs +++ b/crates/generate/src/types.rs @@ -192,13 +192,13 @@ fn define_copy_struct(names: &Names, name: &witx::Id, s: &witx::StructDatatype) quote! { #type_::validate( &ptr.cast(#offset).map_err(|e| - ::memory::GuestError::InField{ + ::memory::GuestError::InDataField{ typename: stringify!(#ident).to_owned(), field: stringify!(#fieldname).to_owned(), err: Box::new(e), })? ).map_err(|e| - ::memory::GuestError::InField { + ::memory::GuestError::InDataField { typename: stringify!(#ident).to_owned(), field: stringify!(#fieldname).to_owned(), err: Box::new(e), @@ -278,13 +278,13 @@ fn define_ptr_struct(names: &Names, name: &witx::Id, s: &witx::StructDatatype) - quote! { #type_::validate( &ptr.cast(#offset).map_err(|e| - ::memory::GuestError::InField{ + ::memory::GuestError::InDataField{ typename: stringify!(#ident).to_owned(), field: stringify!(#fieldname).to_owned(), err: Box::new(e), })? ).map_err(|e| - ::memory::GuestError::InField { + ::memory::GuestError::InDataField { typename: stringify!(#ident).to_owned(), field: stringify!(#fieldname).to_owned(), err: Box::new(e), diff --git a/crates/memory/src/error.rs b/crates/memory/src/error.rs index afbed26416..657faa3384 100644 --- a/crates/memory/src/error.rs +++ b/crates/memory/src/error.rs @@ -11,8 +11,15 @@ pub enum GuestError { PtrNotAligned(Region, u32), #[error("Pointer already borrowed: {0:?}")] PtrBorrowed(Region), - #[error("In {typename}.{field}:")] - InField { + #[error("In func {funcname}:{location}:")] + InFunc { + funcname: &'static str, + location: &'static str, + #[source] + err: Box, + }, + #[error("In data {typename}.{field}:")] + InDataField { typename: String, field: String, #[source] diff --git a/crates/memory/src/memory.rs b/crates/memory/src/memory.rs index f301647c14..a8857f893d 100644 --- a/crates/memory/src/memory.rs +++ b/crates/memory/src/memory.rs @@ -26,7 +26,7 @@ impl<'a> GuestMemory<'a> { fn contains(&self, r: Region) -> bool { r.start < self.len && r.len < self.len // make sure next clause doesnt underflow - && r.start < (self.len - r.len) + && r.start <= (self.len - r.len) } pub fn ptr(&'a self, at: u32) -> Result, GuestError> { diff --git a/crates/memory/src/region.rs b/crates/memory/src/region.rs index 414f6e1970..2e0aadc50f 100644 --- a/crates/memory/src/region.rs +++ b/crates/memory/src/region.rs @@ -7,10 +7,10 @@ pub struct Region { impl Region { pub fn overlaps(&self, rhs: Region) -> bool { let self_start = self.start as u64; - let self_end = ((self_start + self.len as u64) as i64 - 1) as u64; + let self_end = self_start + (self.len - 1) as u64; let rhs_start = rhs.start as u64; - let rhs_end = ((rhs_start + rhs.len as u64) as i64 - 1) as u64; + let rhs_end = rhs_start + (rhs.len - 1) as u64; // start of rhs inside self: if rhs_start >= self_start && rhs_start < self_end { diff --git a/tests/main.rs b/tests/main.rs index df4f6341c7..c03a128d5a 100644 --- a/tests/main.rs +++ b/tests/main.rs @@ -1,10 +1,11 @@ +use memory::GuestRef; +use proptest::prelude::*; + generate::from_witx!({ witx: ["tests/test.witx"], ctx: WasiCtx, }); -use crate::foo::Foo; - pub struct WasiCtx { guest_errors: Vec<::memory::GuestError>, } @@ -25,45 +26,48 @@ impl foo::Foo for WasiCtx { fn baz( &mut self, - _excuse: types::Excuse, - a_better_excuse_by_reference: ::memory::GuestPtrMut, - a_lamer_excuse_by_reference: ::memory::GuestPtr, - two_layers_of_excuses: ::memory::GuestPtrMut<::memory::GuestPtr>, + input1: types::Excuse, + input2_ptr: ::memory::GuestPtrMut, + input3_ptr: ::memory::GuestPtr, + input4_ptr_ptr: ::memory::GuestPtrMut<::memory::GuestPtr>, ) -> Result<(), types::Errno> { + println!("BAZ input1 {:?}", input1); // Read enum value from mutable: - let mut a_better_excuse_ref: ::memory::GuestRefMut = - a_better_excuse_by_reference.as_ref_mut().map_err(|e| { - eprintln!("a_better_excuse_by_reference error: {}", e); + let mut input2_ref: ::memory::GuestRefMut = + input2_ptr.as_ref_mut().map_err(|e| { + eprintln!("input2_ptr error: {}", e); types::Errno::InvalidArg })?; - let _a_better_excuse: types::Excuse = *a_better_excuse_ref; + let input2: types::Excuse = *input2_ref; + println!("input2 {:?}", input2); // Read enum value from immutable ptr: - let a_lamer_excuse = *a_lamer_excuse_by_reference.as_ref().map_err(|e| { - eprintln!("a_lamer_excuse_by_reference error: {}", e); + let input3 = *input3_ptr.as_ref().map_err(|e| { + eprintln!("input3_ptr error: {}", e); types::Errno::InvalidArg })?; - println!("{:?}", a_lamer_excuse); + println!("input3 {:?}", input3); // Write enum to mutable ptr: - *a_better_excuse_ref = a_lamer_excuse; - println!("{:?}", *a_better_excuse_ref); + *input2_ref = input3; + println!("wrote to input2_ref {:?}", input3); // Read ptr value from mutable ptr: - let one_layer_down: ::memory::GuestPtr = - two_layers_of_excuses.read_ptr_from_guest().map_err(|e| { - eprintln!("one_layer_down error: {}", e); + let input4_ptr: ::memory::GuestPtr = + input4_ptr_ptr.read_ptr_from_guest().map_err(|e| { + eprintln!("input4_ptr_ptr error: {}", e); types::Errno::InvalidArg })?; // Read enum value from that ptr: - let _two_layers_down: types::Excuse = *one_layer_down.as_ref().map_err(|e| { - eprintln!("two_layers_down error: {}", e); + let input4: types::Excuse = *input4_ptr.as_ref().map_err(|e| { + eprintln!("input4_ptr error: {}", e); types::Errno::InvalidArg })?; + println!("input4 {:?}", input4); // Write ptr value to mutable ptr: - two_layers_of_excuses.write_ptr_to_guest(&a_better_excuse_by_reference.as_immut()); + input4_ptr_ptr.write_ptr_to_guest(&input2_ptr.as_immut()); Ok(()) } @@ -98,6 +102,7 @@ impl ::memory::GuestErrorType for types::Errno { types::Errno::Ok } fn from_error(e: ::memory::GuestError, ctx: &mut WasiCtx) -> types::Errno { + eprintln!("GUEST ERROR: {:?}", e); ctx.guest_errors.push(e); types::Errno::InvalidArg } @@ -117,6 +122,64 @@ impl HostMemory { pub fn len(&self) -> usize { self.buffer.len() } + pub fn mem_area_strat(align: u32) -> BoxedStrategy { + prop::num::u32::ANY + .prop_map(move |p| { + let p_aligned = p - (p % align); // Align according to argument + let ptr = p_aligned % 4096; // Put inside memory + MemArea { ptr, len: align } + }) + .boxed() + } +} + +#[derive(Debug)] +struct MemArea { + ptr: u32, + len: u32, +} + +// This code is a whole lot like the Region::overlaps func thats at the core of the code under +// test. +// So, I implemented this one with std::ops::Range so it is less likely I wrote the same bug in two +// places. +fn overlapping(a: &MemArea, b: &MemArea) -> bool { + // a_range is all elems in A + let a_range = std::ops::Range { + start: a.ptr, + end: a.ptr + a.len - 1, + }; + // b_range is all elems in B + let b_range = std::ops::Range { + start: b.ptr, + end: b.ptr + b.len - 1, + }; + // No element in B is contained in A: + for b_elem in b_range.clone() { + if a_range.contains(&b_elem) { + return true; + } + } + // No element in A is contained in B: + for a_elem in a_range { + if b_range.contains(&a_elem) { + return true; + } + } + return false; +} + +fn non_overlapping_set(areas: &[&MemArea]) -> bool { + // A is all areas + for (i, a) in areas.iter().enumerate() { + // (A, B) is every pair of areas + for b in areas[i + 1..].iter() { + if overlapping(a, b) { + return false; + } + } + } + return true; } #[test] @@ -127,76 +190,358 @@ fn hostmemory_is_aligned() { assert_eq!(h.as_mut_ptr() as usize % 4096, 0); } -#[test] -fn bat() { - let mut ctx = WasiCtx::new(); - assert_eq!(ctx.bat(2), Ok(4.0)); +#[derive(Debug)] +struct BatExercise { + pub input: u32, + pub return_loc: MemArea, } -#[test] -fn baz() { - let mut ctx = WasiCtx::new(); - let mut host_memory = HostMemory::new(); - let guest_memory = memory::GuestMemory::new(host_memory.as_mut_ptr(), host_memory.len() as u32); - let sizeof_excuse = std::mem::size_of::(); - let padding = 4 - sizeof_excuse % 4; - { - let lame_mut: memory::GuestPtrMut = guest_memory.ptr_mut(0).unwrap(); - let mut lame = lame_mut.as_ref_mut().unwrap(); - *lame = types::Excuse::Sleeping; +impl BatExercise { + pub fn test(&self) { + let mut ctx = WasiCtx::new(); + let mut host_memory = HostMemory::new(); + let mut guest_memory = + memory::GuestMemory::new(host_memory.as_mut_ptr(), host_memory.len() as u32); + + let bat_err = foo::bat( + &mut ctx, + &mut guest_memory, + self.input as i32, + self.return_loc.ptr as i32, + ); + + let return_val: GuestRef = guest_memory + .ptr(self.return_loc.ptr) + .expect("return loc ptr") + .as_ref() + .expect("return val ref"); + assert_eq!(bat_err, types::Errno::Ok.into(), "bat errno"); + assert_eq!(*return_val, (self.input as f32) * 2.0, "bat return val"); } - let lame: memory::GuestPtr = guest_memory - .ptr(0) - .expect("GuestPtr fits in the memory"); - assert_eq!(*lame.as_ref().unwrap(), types::Excuse::Sleeping); - let better: memory::GuestPtrMut = guest_memory - .ptr_mut((sizeof_excuse + padding) as u32) - .expect("GuestPtr fits in the memory"); - let ptr_to_ptr: memory::GuestPtrMut> = guest_memory - .ptr_mut((sizeof_excuse + padding) as u32 * 2) - .expect("GuestPtr> fits in the memory"); - assert!(ctx - .baz( - types::Excuse::DogAte, - better.clone(), - lame, - ptr_to_ptr.clone() + + pub fn strat() -> BoxedStrategy { + (prop::num::u32::ANY, HostMemory::mem_area_strat(4)) + .prop_map(|(input, return_loc)| BatExercise { input, return_loc }) + .boxed() + } +} + +proptest! { + #[test] + fn bat(e in BatExercise::strat()) { + e.test() + } +} + +fn excuse_strat() -> impl Strategy { + prop_oneof![ + Just(types::Excuse::DogAte), + Just(types::Excuse::Traffic), + Just(types::Excuse::Sleeping), + ] + .boxed() +} + +#[derive(Debug)] +struct BazExercise { + pub input1: types::Excuse, + pub input2: types::Excuse, + pub input2_loc: MemArea, + pub input3: types::Excuse, + pub input3_loc: MemArea, + pub input4: types::Excuse, + pub input4_loc: MemArea, + pub input4_ptr_loc: MemArea, +} + +impl BazExercise { + pub fn strat() -> BoxedStrategy { + ( + excuse_strat(), + excuse_strat(), + HostMemory::mem_area_strat(4), + excuse_strat(), + HostMemory::mem_area_strat(4), + excuse_strat(), + HostMemory::mem_area_strat(4), + HostMemory::mem_area_strat(4), ) - .is_ok()); - assert_eq!(*better.as_ref().unwrap(), types::Excuse::Sleeping); - let ptr = ptr_to_ptr.read_ptr_from_guest().unwrap(); - assert_eq!(*ptr.as_ref().unwrap(), types::Excuse::Sleeping); -} - -#[test] -fn sum_of_pair() { - let mut ctx = WasiCtx::new(); - let pair = types::PairInts { - first: 1, - second: 2, - }; - assert_eq!(ctx.sum_of_pair(&pair), Ok(3)); -} - -#[test] -fn sum_of_pair_of_ptrs() { - let mut ctx = WasiCtx::new(); - let mut host_memory = HostMemory::new(); - let guest_memory = memory::GuestMemory::new(host_memory.as_mut_ptr(), host_memory.len() as u32); - { - let first_mut: memory::GuestPtrMut = guest_memory.ptr_mut(0).unwrap(); - let mut x = first_mut.as_ref_mut().unwrap(); - *x = 1; - let second_mut: memory::GuestPtrMut = guest_memory.ptr_mut(4).unwrap(); - let mut x = second_mut.as_ref_mut().unwrap(); - *x = 2; + .prop_map( + |( + input1, + input2, + input2_loc, + input3, + input3_loc, + input4, + input4_loc, + input4_ptr_loc, + )| BazExercise { + input1, + input2, + input2_loc, + input3, + input3_loc, + input4, + input4_loc, + input4_ptr_loc, + }, + ) + .prop_filter("non-overlapping pointers", |e| { + non_overlapping_set(&[ + &e.input2_loc, + &e.input3_loc, + &e.input4_loc, + &e.input4_ptr_loc, + ]) + }) + .boxed() + } + pub fn test(&self) { + let mut ctx = WasiCtx::new(); + let mut host_memory = HostMemory::new(); + let mut guest_memory = + memory::GuestMemory::new(host_memory.as_mut_ptr(), host_memory.len() as u32); + + *guest_memory + .ptr_mut(self.input2_loc.ptr) + .expect("input2 ptr") + .as_ref_mut() + .expect("input2 ref_mut") = self.input2; + + *guest_memory + .ptr_mut(self.input3_loc.ptr) + .expect("input3 ptr") + .as_ref_mut() + .expect("input3 ref_mut") = self.input3; + + *guest_memory + .ptr_mut(self.input4_loc.ptr) + .expect("input4 ptr") + .as_ref_mut() + .expect("input4 ref_mut") = self.input4; + + *guest_memory + .ptr_mut(self.input4_ptr_loc.ptr) + .expect("input4 ptr ptr") + .as_ref_mut() + .expect("input4 ptr ref_mut") = self.input4_loc.ptr; + + let baz_err = foo::baz( + &mut ctx, + &mut guest_memory, + self.input1.into(), + self.input2_loc.ptr as i32, + self.input3_loc.ptr as i32, + self.input4_ptr_loc.ptr as i32, + ); + assert_eq!(baz_err, types::Errno::Ok.into(), "baz errno"); + + // Implementation of baz writes input3 to the input2_loc: + let written_to_input2_loc: i32 = *guest_memory + .ptr(self.input2_loc.ptr) + .expect("input2 ptr") + .as_ref() + .expect("input2 ref"); + + assert_eq!( + written_to_input2_loc, + self.input3.into(), + "baz written to input2" + ); + + // Implementation of baz writes input2_loc to input4_ptr_loc: + let written_to_input4_ptr: u32 = *guest_memory + .ptr(self.input4_ptr_loc.ptr) + .expect("input4_ptr_loc ptr") + .as_ref() + .expect("input4_ptr_loc ref"); + + assert_eq!( + written_to_input4_ptr, self.input2_loc.ptr, + "baz written to input4_ptr" + ); + } +} +proptest! { + #[test] + fn baz(e in BazExercise::strat()) { + e.test(); + } +} + +#[derive(Debug)] +struct SumOfPairExercise { + pub input: types::PairInts, + pub input_loc: MemArea, + pub return_loc: MemArea, +} + +impl SumOfPairExercise { + pub fn strat() -> BoxedStrategy { + ( + prop::num::i32::ANY, + prop::num::i32::ANY, + HostMemory::mem_area_strat(8), + HostMemory::mem_area_strat(8), + ) + .prop_map(|(first, second, input_loc, return_loc)| SumOfPairExercise { + input: types::PairInts { first, second }, + input_loc, + return_loc, + }) + .prop_filter("non-overlapping pointers", |e| { + non_overlapping_set(&[&e.input_loc, &e.return_loc]) + }) + .boxed() + } + + pub fn test(&self) { + let mut ctx = WasiCtx::new(); + let mut host_memory = HostMemory::new(); + let mut guest_memory = + memory::GuestMemory::new(host_memory.as_mut_ptr(), host_memory.len() as u32); + + *guest_memory + .ptr_mut(self.input_loc.ptr) + .expect("input ptr") + .as_ref_mut() + .expect("input ref_mut") = self.input.first; + *guest_memory + .ptr_mut(self.input_loc.ptr + 4) + .expect("input ptr") + .as_ref_mut() + .expect("input ref_mut") = self.input.second; + let sum_err = foo::sum_of_pair( + &mut ctx, + &mut guest_memory, + self.input_loc.ptr as i32, + self.return_loc.ptr as i32, + ); + + assert_eq!(sum_err, types::Errno::Ok.into(), "sum errno"); + + let return_val: i64 = *guest_memory + .ptr(self.return_loc.ptr) + .expect("return ptr") + .as_ref() + .expect("return ref"); + + assert_eq!( + return_val, + self.input.first as i64 + self.input.second as i64, + "sum return value" + ); + } +} + +proptest! { + #[test] + fn sum_of_pair(e in SumOfPairExercise::strat()) { + e.test(); + } +} + +#[derive(Debug)] +struct SumPairPtrsExercise { + input_first: i32, + input_second: i32, + input_first_loc: MemArea, + input_second_loc: MemArea, + input_struct_loc: MemArea, + return_loc: MemArea, +} + +impl SumPairPtrsExercise { + pub fn strat() -> BoxedStrategy { + ( + prop::num::i32::ANY, + prop::num::i32::ANY, + HostMemory::mem_area_strat(4), + HostMemory::mem_area_strat(4), + HostMemory::mem_area_strat(8), + HostMemory::mem_area_strat(8), + ) + .prop_map( + |( + input_first, + input_second, + input_first_loc, + input_second_loc, + input_struct_loc, + return_loc, + )| SumPairPtrsExercise { + input_first, + input_second, + input_first_loc, + input_second_loc, + input_struct_loc, + return_loc, + }, + ) + .prop_filter("non-overlapping pointers", |e| { + non_overlapping_set(&[ + &e.input_first_loc, + &e.input_second_loc, + &e.input_struct_loc, + &e.return_loc, + ]) + }) + .boxed() + } + pub fn test(&self) { + let mut ctx = WasiCtx::new(); + let mut host_memory = HostMemory::new(); + let mut guest_memory = + memory::GuestMemory::new(host_memory.as_mut_ptr(), host_memory.len() as u32); + + *guest_memory + .ptr_mut(self.input_first_loc.ptr) + .expect("input_first ptr") + .as_ref_mut() + .expect("input_first ref") = self.input_first; + *guest_memory + .ptr_mut(self.input_second_loc.ptr) + .expect("input_second ptr") + .as_ref_mut() + .expect("input_second ref") = self.input_second; + + *guest_memory + .ptr_mut(self.input_struct_loc.ptr) + .expect("input_struct ptr") + .as_ref_mut() + .expect("input_struct ref") = self.input_first_loc.ptr; + *guest_memory + .ptr_mut(self.input_struct_loc.ptr + 4) + .expect("input_struct ptr") + .as_ref_mut() + .expect("input_struct ref") = self.input_second_loc.ptr; + + let res = foo::sum_of_pair_of_ptrs( + &mut ctx, + &mut guest_memory, + self.input_struct_loc.ptr as i32, + self.return_loc.ptr as i32, + ); + + assert_eq!(res, types::Errno::Ok.into(), "sum of pair of ptrs errno"); + + let doubled: i64 = *guest_memory + .ptr(self.return_loc.ptr) + .expect("return ptr") + .as_ref() + .expect("return ref"); + + assert_eq!( + doubled, + (self.input_first as i64) + (self.input_second as i64), + "sum of pair of ptrs return val" + ); + } +} +proptest! { + #[test] + fn sum_of_pair_of_ptrs(e in SumPairPtrsExercise::strat()) { + e.test() } - let first: memory::GuestPtr = guest_memory - .ptr(0) - .expect("GuestPtr fits in the memory"); - let second: memory::GuestPtr = guest_memory - .ptr(4) - .expect("GuestPtr fits in the memory"); - let pair = types::PairIntPtrs { first, second }; - assert_eq!(ctx.sum_of_pair_of_ptrs(&pair), Ok(3)); } From 2687b0147455f1aee8978fd2aa39480720433823 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 4 Feb 2020 23:26:18 +0100 Subject: [PATCH 37/86] Refactor Region struct (#4) * Add some basic sanity tests for Region This commit adds some basic sanity tests for `overlap` method of `Region`. * Refactor overlaps method of Region struct This commit refactors `Region::overlaps` method. * Add some docs * Assert Region's len is nonzero --- crates/memory/src/region.rs | 66 ++++++++++++++++++++++++++----------- 1 file changed, 46 insertions(+), 20 deletions(-) diff --git a/crates/memory/src/region.rs b/crates/memory/src/region.rs index 2e0aadc50f..7ee542f388 100644 --- a/crates/memory/src/region.rs +++ b/crates/memory/src/region.rs @@ -1,3 +1,4 @@ +/// Represents a contiguous region in memory. #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub struct Region { pub start: u32, @@ -5,6 +6,12 @@ pub struct Region { } impl Region { + pub fn new(start: u32, len: u32) -> Self { + assert!(len > 0, "Region cannot have 0 length"); + Self { start, len } + } + + /// Checks if this `Region` overlaps with `rhs` `Region`. pub fn overlaps(&self, rhs: Region) -> bool { let self_start = self.start as u64; let self_end = self_start + (self.len - 1) as u64; @@ -12,26 +19,45 @@ impl Region { let rhs_start = rhs.start as u64; let rhs_end = rhs_start + (rhs.len - 1) as u64; - // start of rhs inside self: - if rhs_start >= self_start && rhs_start < self_end { - return true; + if self_start <= rhs_start { + self_end >= rhs_start + } else { + rhs_end >= self_start } - - // end of rhs inside self: - if rhs_end >= self_start && rhs_end < self_end { - return true; - } - - // start of self inside rhs: - if self_start >= rhs_start && self_start < rhs_end { - return true; - } - - // end of self inside rhs: XXX is this redundant? i suspect it is but im too tired - if self_end >= rhs_start && self_end < rhs_end { - return true; - } - - false + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn nonoverlapping() { + let r1 = Region::new(0, 10); + let r2 = Region::new(10, 10); + assert!(!r1.overlaps(r2)); + + let r1 = Region::new(10, 10); + let r2 = Region::new(0, 10); + assert!(!r1.overlaps(r2)); + } + + #[test] + fn overlapping() { + let r1 = Region::new(0, 10); + let r2 = Region::new(9, 10); + assert!(r1.overlaps(r2)); + + let r1 = Region::new(0, 10); + let r2 = Region::new(2, 5); + assert!(r1.overlaps(r2)); + + let r1 = Region::new(9, 10); + let r2 = Region::new(0, 10); + assert!(r1.overlaps(r2)); + + let r1 = Region::new(2, 5); + let r2 = Region::new(0, 10); + assert!(r1.overlaps(r2)); } } From 664f7d38e0a81597d83fd7805b752452dc7339c0 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Fri, 7 Feb 2020 08:27:21 +0100 Subject: [PATCH 38/86] Add first pass at GuestArray (#5) * Add first pass at GuestArray * Return &'a [T] instead of Vec Also, add a sanity test for `GuestArray`. * Change approach in GuestArray::as_ref The new approach should avoid unnecessary copying (does it?) by iterating through the memory and firstly validating the guest pointers, to then extracting the slice `&'a [T]` using the unsafe `slice::from_raw_parts` fn. * Redo implementation of GuestArray and GuestArrayMut This commit: * redos the impl of `as_ref` and `as_ref_mut` for `GuestArray` and `GuestArrayMut` structs in that they now return dynamically borrow checked `GuestArrayRef` and `GuestArrayRefMut` structs * introduces `GuestArrayRef` and `GuestArrayRefMut` structs which perform dynamic borrow-checking of memory region at runtime, and can be derefed to `&[T]` and `&mut [T]` * adds a few sanity checks for the introduced types * Rename r#ref to ref_ * Add constructors for GuestArray and GuestArrayMut This commit: * adds constructors for `GuestArray` and `GuestArrayMut` both of which can now *only* be constructed from `GuestPtr::array` and `GuestPtrMut::array_mut` respectively * changes `Region::extend` to extend the region by adding to the current `len` (avoids problem of asserting for > 0) * implements `fmt::Debug` for most of memory types for easier testing (implementation is *not* enforced on the generic parameter `T` in the struct; rather, if `T` impls `fmt::Debug` then so does the memory type such as `GuestPtr<'_, T>`) --- crates/memory/src/borrow.rs | 1 + crates/memory/src/error.rs | 2 +- crates/memory/src/memory.rs | 400 ++++++++++++++++++++++++++++++++++++ crates/memory/src/region.rs | 7 + 4 files changed, 409 insertions(+), 1 deletion(-) diff --git a/crates/memory/src/borrow.rs b/crates/memory/src/borrow.rs index c691888db9..bd1d077e17 100644 --- a/crates/memory/src/borrow.rs +++ b/crates/memory/src/borrow.rs @@ -5,6 +5,7 @@ use crate::region::Region; #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub struct BorrowHandle(usize); +#[derive(Debug)] pub struct GuestBorrows { immutable: HashMap, mutable: HashMap, diff --git a/crates/memory/src/error.rs b/crates/memory/src/error.rs index 657faa3384..cbd4bbb61a 100644 --- a/crates/memory/src/error.rs +++ b/crates/memory/src/error.rs @@ -1,7 +1,7 @@ use crate::Region; use thiserror::Error; -#[derive(Debug, Error)] +#[derive(Debug, Error, PartialEq, Eq)] pub enum GuestError { #[error("Invalid enum value {0}")] InvalidEnumValue(&'static str), diff --git a/crates/memory/src/memory.rs b/crates/memory/src/memory.rs index a8857f893d..bf3001b357 100644 --- a/crates/memory/src/memory.rs +++ b/crates/memory/src/memory.rs @@ -1,4 +1,5 @@ use std::cell::RefCell; +use std::fmt; use std::marker::PhantomData; use std::rc::Rc; @@ -12,6 +13,16 @@ pub struct GuestMemory<'a> { borrows: Rc>, } +impl<'a> fmt::Debug for GuestMemory<'a> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "GuestMemory {{ ptr: {:?}, len: {:?}, borrows: {:?} }}", + self.ptr, self.len, self.borrows + ) + } +} + impl<'a> GuestMemory<'a> { pub fn new(ptr: *mut u8, len: u32) -> GuestMemory<'a> { assert_eq!(ptr as usize % 4096, 0, "GuestMemory must be page-aligned"); @@ -63,6 +74,16 @@ pub struct GuestPtr<'a, T> { type_: PhantomData, } +impl<'a, T: fmt::Debug> fmt::Debug for GuestPtr<'a, T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "GuestPtr {{ mem: {:?}, region: {:?} }}", + self.mem, self.region + ) + } +} + impl<'a, T: GuestType> GuestPtr<'a, T> { pub fn as_raw(&self) -> *const u8 { (self.mem.ptr as usize + self.region.start as usize) as *const u8 @@ -76,6 +97,20 @@ impl<'a, T: GuestType> GuestPtr<'a, T> { pub fn cast(&self, offset: u32) -> Result, GuestError> { self.mem.ptr(self.region.start + offset) } + + pub fn array(&self, num_elems: u32) -> Result, GuestError> { + let region = self.region.extend((num_elems - 1) * T::size()); + if self.mem.contains(region) { + let ptr = GuestPtr { + mem: self.mem, + region: self.region, + type_: self.type_, + }; + Ok(GuestArray { ptr, num_elems }) + } else { + Err(GuestError::PtrOutOfBounds(region)) + } + } } impl<'a, T: GuestTypeCopy> GuestPtr<'a, T> { @@ -158,6 +193,16 @@ pub struct GuestPtrMut<'a, T> { type_: PhantomData, } +impl<'a, T: fmt::Debug> fmt::Debug for GuestPtrMut<'a, T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "GuestPtrMut {{ mem: {:?}, region: {:?} }}", + self.mem, self.region + ) + } +} + impl<'a, T: GuestType> GuestPtrMut<'a, T> { pub fn as_immut(&self) -> GuestPtr<'a, T> { GuestPtr { @@ -170,6 +215,7 @@ impl<'a, T: GuestType> GuestPtrMut<'a, T> { pub fn as_raw(&self) -> *const u8 { self.as_immut().as_raw() } + pub fn elem(&self, elements: i32) -> Result, GuestError> { self.mem .ptr_mut(self.region.start + (elements * self.region.len as i32) as u32) @@ -178,6 +224,20 @@ impl<'a, T: GuestType> GuestPtrMut<'a, T> { pub fn cast(&self, offset: u32) -> Result, GuestError> { self.mem.ptr_mut(self.region.start + offset) } + + pub fn array_mut(&self, num_elems: u32) -> Result, GuestError> { + let region = self.region.extend((num_elems - 1) * T::size()); + if self.mem.contains(region) { + let ptr = GuestPtrMut { + mem: self.mem, + region: self.region, + type_: self.type_, + }; + Ok(GuestArrayMut { ptr, num_elems }) + } else { + Err(GuestError::PtrOutOfBounds(region)) + } + } } impl<'a, T: GuestTypeCopy> GuestPtrMut<'a, T> { @@ -271,6 +331,16 @@ pub struct GuestRef<'a, T> { type_: PhantomData, } +impl<'a, T: fmt::Debug> fmt::Debug for GuestRef<'a, T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "GuestRef {{ mem: {:?}, region: {:?}, handle: {:?} }}", + self.mem, self.region, self.handle + ) + } +} + impl<'a, T> GuestRef<'a, T> { pub fn as_ptr(&self) -> GuestPtr<'a, T> { GuestPtr { @@ -309,6 +379,16 @@ pub struct GuestRefMut<'a, T> { type_: PhantomData, } +impl<'a, T: fmt::Debug> fmt::Debug for GuestRefMut<'a, T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "GuestRefMut {{ mem: {:?}, region: {:?}, handle: {:?} }}", + self.mem, self.region, self.handle + ) + } +} + impl<'a, T> GuestRefMut<'a, T> { pub fn as_ptr(&self) -> GuestPtr<'a, T> { GuestPtr { @@ -359,3 +439,323 @@ impl<'a, T> Drop for GuestRefMut<'a, T> { borrows.unborrow_mut(self.handle); } } + +pub struct GuestArray<'a, T> +where + T: GuestType, +{ + ptr: GuestPtr<'a, T>, + num_elems: u32, +} + +impl<'a, T: GuestType + fmt::Debug> fmt::Debug for GuestArray<'a, T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "GuestArray {{ ptr: {:?}, num_elems: {:?} }}", + self.ptr, self.num_elems + ) + } +} + +impl<'a, T> GuestArray<'a, T> +where + T: GuestTypeCopy, +{ + pub fn as_ref(&self) -> Result, GuestError> { + let mut ptr = self.ptr.clone(); + for _ in 0..self.num_elems { + ptr = ptr.elem(1)?; + T::validate(&ptr)?; + } + let region = self.ptr.region.extend((self.num_elems - 1) * T::size()); + let handle = { + let mut borrows = self.ptr.mem.borrows.borrow_mut(); + borrows + .borrow_immut(region) + .ok_or_else(|| GuestError::PtrBorrowed(region))? + }; + let ref_ = GuestRef { + mem: self.ptr.mem, + region, + handle, + type_: self.ptr.type_, + }; + Ok(GuestArrayRef { + ref_, + num_elems: self.num_elems, + }) + } +} + +pub struct GuestArrayRef<'a, T> +where + T: GuestType, +{ + ref_: GuestRef<'a, T>, + num_elems: u32, +} + +impl<'a, T> fmt::Debug for GuestArrayRef<'a, T> +where + T: GuestType + fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "GuestArrayRef {{ ref_: {:?}, num_elems: {:?} }}", + self.ref_, self.num_elems + ) + } +} + +impl<'a, T> ::std::ops::Deref for GuestArrayRef<'a, T> +where + T: GuestTypeCopy, +{ + type Target = [T]; + + fn deref(&self) -> &Self::Target { + unsafe { + std::slice::from_raw_parts( + self.ref_.as_ptr().as_raw() as *const T, + self.num_elems as usize, + ) + } + } +} + +pub struct GuestArrayMut<'a, T> +where + T: GuestType, +{ + ptr: GuestPtrMut<'a, T>, + num_elems: u32, +} + +impl<'a, T: GuestType + fmt::Debug> fmt::Debug for GuestArrayMut<'a, T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "GuestArrayMut {{ ptr: {:?}, num_elems: {:?} }}", + self.ptr, self.num_elems + ) + } +} + +impl<'a, T> GuestArrayMut<'a, T> +where + T: GuestTypeCopy, +{ + pub fn as_ref(&self) -> Result, GuestError> { + let arr = GuestArray { + ptr: self.ptr.as_immut(), + num_elems: self.num_elems, + }; + arr.as_ref() + } + + pub fn as_ref_mut(&self) -> Result, GuestError> { + let mut ptr = self.ptr.as_immut(); + for _ in 0..self.num_elems { + ptr = ptr.elem(1)?; + T::validate(&ptr)?; + } + let region = self.ptr.region.extend((self.num_elems - 1) * T::size()); + let handle = { + let mut borrows = self.ptr.mem.borrows.borrow_mut(); + borrows + .borrow_mut(region) + .ok_or_else(|| GuestError::PtrBorrowed(region))? + }; + let ref_mut = GuestRefMut { + mem: self.ptr.mem, + region, + handle, + type_: self.ptr.type_, + }; + Ok(GuestArrayRefMut { + ref_mut, + num_elems: self.num_elems, + }) + } +} + +pub struct GuestArrayRefMut<'a, T> +where + T: GuestType, +{ + ref_mut: GuestRefMut<'a, T>, + num_elems: u32, +} + +impl<'a, T> fmt::Debug for GuestArrayRefMut<'a, T> +where + T: GuestType + fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "GuestArrayRefMut {{ ref_mut: {:?}, num_elems: {:?} }}", + self.ref_mut, self.num_elems + ) + } +} + +impl<'a, T> ::std::ops::Deref for GuestArrayRefMut<'a, T> +where + T: GuestTypeCopy, +{ + type Target = [T]; + + fn deref(&self) -> &Self::Target { + unsafe { + std::slice::from_raw_parts( + self.ref_mut.as_ptr().as_raw() as *const T, + self.num_elems as usize, + ) + } + } +} + +impl<'a, T> ::std::ops::DerefMut for GuestArrayRefMut<'a, T> +where + T: GuestTypeCopy, +{ + fn deref_mut(&mut self) -> &mut Self::Target { + unsafe { + std::slice::from_raw_parts_mut( + self.ref_mut.as_ptr_mut().as_raw() as *mut T, + self.num_elems as usize, + ) + } + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[repr(align(4096))] + struct HostMemory { + buffer: [u8; 4096], + } + + impl HostMemory { + pub fn new() -> Self { + Self { buffer: [0; 4096] } + } + pub fn as_mut_ptr(&mut self) -> *mut u8 { + self.buffer.as_mut_ptr() + } + pub fn len(&self) -> usize { + self.buffer.len() + } + } + + #[test] + fn guest_array_out_of_bounds() { + let mut host_memory = HostMemory::new(); + let guest_memory = GuestMemory::new(host_memory.as_mut_ptr(), host_memory.len() as u32); + // try extracting an immutable array out of memory bounds + let ptr: GuestPtr = guest_memory.ptr(4092).expect("ptr to last i32 el"); + let err = ptr.array(2).expect_err("out of bounds ptr error"); + assert_eq!(err, GuestError::PtrOutOfBounds(Region::new(4092, 8))); + // try extracting an mutable array out of memory bounds + let ptr: GuestPtrMut = guest_memory.ptr_mut(4092).expect("ptr mut to last i32 el"); + let err = ptr.array_mut(2).expect_err("out of bounds ptr error"); + assert_eq!(err, GuestError::PtrOutOfBounds(Region::new(4092, 8))); + } + + #[test] + fn guest_array() { + let mut host_memory = HostMemory::new(); + let guest_memory = GuestMemory::new(host_memory.as_mut_ptr(), host_memory.len() as u32); + // write a simple array into memory + { + let ptr: GuestPtrMut = guest_memory.ptr_mut(0).expect("ptr mut to first el"); + let mut el = ptr.as_ref_mut().expect("ref mut to first el"); + *el = 1; + let ptr: GuestPtrMut = guest_memory.ptr_mut(4).expect("ptr mut to second el"); + let mut el = ptr.as_ref_mut().expect("ref mu to second el"); + *el = 2; + let ptr: GuestPtrMut = guest_memory.ptr_mut(8).expect("ptr mut to third el"); + let mut el = ptr.as_ref_mut().expect("ref mut to third el"); + *el = 3; + } + // extract as array + let ptr: GuestPtr = guest_memory.ptr(0).expect("ptr to first el"); + let arr = ptr.array(3).expect("convert ptr to array"); + let as_ref = arr.as_ref().expect("array borrowed immutably"); + assert_eq!(&*as_ref, &[1, 2, 3]); + // borrowing again should be fine + let as_ref2 = arr.as_ref().expect("array borrowed immutably again"); + assert_eq!(&*as_ref2, &*as_ref); + } + + #[test] + fn guest_array_mut() { + let mut host_memory = HostMemory::new(); + let guest_memory = GuestMemory::new(host_memory.as_mut_ptr(), host_memory.len() as u32); + // set elems of array to zero + { + let ptr: GuestPtrMut = guest_memory.ptr_mut(0).expect("ptr mut to first el"); + let mut el = ptr.as_ref_mut().expect("ref mut to first el"); + *el = 0; + let ptr: GuestPtrMut = guest_memory.ptr_mut(4).expect("ptr mut to second el"); + let mut el = ptr.as_ref_mut().expect("ref mu to second el"); + *el = 0; + let ptr: GuestPtrMut = guest_memory.ptr_mut(8).expect("ptr mut to third el"); + let mut el = ptr.as_ref_mut().expect("ref mut to third el"); + *el = 0; + } + // extract as array and verify all is zero + let ptr: GuestPtrMut = guest_memory.ptr_mut(0).expect("ptr mut to first el"); + let arr = ptr.array_mut(3).expect("convert ptr mut to array mut"); + assert_eq!(&*arr.as_ref().expect("array borrowed immutably"), &[0; 3]); + // populate the array and re-verify + for el in &mut *arr.as_ref_mut().expect("array borrowed mutably") { + *el = 10; + } + // re-validate + assert_eq!(&*arr.as_ref().expect("array borrowed immutably"), &[10; 3]); + } + + #[test] + #[should_panic( + expected = "array borrowed immutably while borrowed mutably: PtrBorrowed(Region { start: 0, len: 12 })" + )] + fn guest_array_mut_borrow_checker_1() { + let mut host_memory = HostMemory::new(); + let guest_memory = GuestMemory::new(host_memory.as_mut_ptr(), host_memory.len() as u32); + let ptr: GuestPtrMut = guest_memory.ptr_mut(0).expect("ptr mut to first el"); + let arr = GuestArrayMut { ptr, num_elems: 3 }; + // borrow mutably + let _as_mut = arr + .as_ref_mut() + .expect("array borrowed mutably for the first time"); + // borrow immutably should be fine + let _as_ref = arr + .as_ref() + .expect("array borrowed immutably while borrowed mutably"); + } + + #[test] + #[should_panic( + expected = "array borrowed mutably while borrowed mutably: PtrBorrowed(Region { start: 0, len: 12 })" + )] + fn guest_array_mut_borrow_checker_2() { + let mut host_memory = HostMemory::new(); + let guest_memory = GuestMemory::new(host_memory.as_mut_ptr(), host_memory.len() as u32); + let ptr: GuestPtrMut = guest_memory.ptr_mut(0).expect("ptr mut to first el"); + let arr = GuestArrayMut { ptr, num_elems: 3 }; + // borrow mutably + let _as_mut = arr + .as_ref_mut() + .expect("array borrowed mutably for the first time"); + // try borrowing mutably again + let _as_mut2 = arr + .as_ref_mut() + .expect("array borrowed mutably while borrowed mutably"); + } +} diff --git a/crates/memory/src/region.rs b/crates/memory/src/region.rs index 7ee542f388..fbbe752c85 100644 --- a/crates/memory/src/region.rs +++ b/crates/memory/src/region.rs @@ -25,6 +25,13 @@ impl Region { rhs_end >= self_start } } + + pub fn extend(&self, new_len: u32) -> Self { + Self { + start: self.start, + len: self.len + new_len, + } + } } #[cfg(test)] From 44584bccfc31209b716e55bfdbca36a63d8314e5 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Sat, 8 Feb 2020 11:19:37 +0100 Subject: [PATCH 39/86] Refactor memory module This commit refactors and reorganises the `memory.rs` module. Now, it consists of two submodules: `memory::ptr` which contains `GuestPtr` and `GuestRef` (and their mutable versions), and `memory::array` which contains `GuestArray` and `GuestArrayRef` (and their mutable versions). This commit also adds basic unit sanity tests for the `memory::ptr` submodule. --- crates/memory/src/memory.rs | 761 ------------------------------ crates/memory/src/memory/array.rs | 335 +++++++++++++ crates/memory/src/memory/mod.rs | 70 +++ crates/memory/src/memory/ptr.rs | 564 ++++++++++++++++++++++ 4 files changed, 969 insertions(+), 761 deletions(-) delete mode 100644 crates/memory/src/memory.rs create mode 100644 crates/memory/src/memory/array.rs create mode 100644 crates/memory/src/memory/mod.rs create mode 100644 crates/memory/src/memory/ptr.rs diff --git a/crates/memory/src/memory.rs b/crates/memory/src/memory.rs deleted file mode 100644 index bf3001b357..0000000000 --- a/crates/memory/src/memory.rs +++ /dev/null @@ -1,761 +0,0 @@ -use std::cell::RefCell; -use std::fmt; -use std::marker::PhantomData; -use std::rc::Rc; - -use crate::borrow::{BorrowHandle, GuestBorrows}; -use crate::{GuestError, GuestType, GuestTypeClone, GuestTypeCopy, GuestTypePtr, Region}; - -pub struct GuestMemory<'a> { - ptr: *mut u8, - len: u32, - lifetime: PhantomData<&'a ()>, - borrows: Rc>, -} - -impl<'a> fmt::Debug for GuestMemory<'a> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "GuestMemory {{ ptr: {:?}, len: {:?}, borrows: {:?} }}", - self.ptr, self.len, self.borrows - ) - } -} - -impl<'a> GuestMemory<'a> { - pub fn new(ptr: *mut u8, len: u32) -> GuestMemory<'a> { - assert_eq!(ptr as usize % 4096, 0, "GuestMemory must be page-aligned"); - GuestMemory { - ptr, - len, - lifetime: PhantomData, - borrows: Rc::new(RefCell::new(GuestBorrows::new())), - } - } - - fn contains(&self, r: Region) -> bool { - r.start < self.len - && r.len < self.len // make sure next clause doesnt underflow - && r.start <= (self.len - r.len) - } - - pub fn ptr(&'a self, at: u32) -> Result, GuestError> { - let region = Region { - start: at, - len: T::size(), - }; - if !self.contains(region) { - Err(GuestError::PtrOutOfBounds(region))?; - } - if at % T::align() != 0 { - Err(GuestError::PtrNotAligned(region, T::align()))?; - } - Ok(GuestPtr { - mem: &self, - region, - type_: PhantomData, - }) - } - pub fn ptr_mut(&'a self, at: u32) -> Result, GuestError> { - let ptr = self.ptr(at)?; - Ok(GuestPtrMut { - mem: ptr.mem, - region: ptr.region, - type_: ptr.type_, - }) - } -} - -#[derive(Clone)] -pub struct GuestPtr<'a, T> { - mem: &'a GuestMemory<'a>, - region: Region, - type_: PhantomData, -} - -impl<'a, T: fmt::Debug> fmt::Debug for GuestPtr<'a, T> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "GuestPtr {{ mem: {:?}, region: {:?} }}", - self.mem, self.region - ) - } -} - -impl<'a, T: GuestType> GuestPtr<'a, T> { - pub fn as_raw(&self) -> *const u8 { - (self.mem.ptr as usize + self.region.start as usize) as *const u8 - } - - pub fn elem(&self, elements: i32) -> Result, GuestError> { - self.mem - .ptr(self.region.start + (elements * self.region.len as i32) as u32) - } - - pub fn cast(&self, offset: u32) -> Result, GuestError> { - self.mem.ptr(self.region.start + offset) - } - - pub fn array(&self, num_elems: u32) -> Result, GuestError> { - let region = self.region.extend((num_elems - 1) * T::size()); - if self.mem.contains(region) { - let ptr = GuestPtr { - mem: self.mem, - region: self.region, - type_: self.type_, - }; - Ok(GuestArray { ptr, num_elems }) - } else { - Err(GuestError::PtrOutOfBounds(region)) - } - } -} - -impl<'a, T: GuestTypeCopy> GuestPtr<'a, T> { - pub fn as_ref(&self) -> Result, GuestError> { - T::validate(&self)?; - let handle = { - let mut borrows = self.mem.borrows.borrow_mut(); - borrows - .borrow_immut(self.region) - .ok_or_else(|| GuestError::PtrBorrowed(self.region))? - }; - Ok(GuestRef { - mem: self.mem, - region: self.region, - handle, - type_: self.type_, - }) - } -} - -impl<'a, T: GuestTypeClone> GuestPtr<'a, T> { - pub fn clone_from_guest(&self) -> Result { - T::read_from_guest(self) - } -} - -impl<'a, T: GuestTypePtr<'a>> GuestPtr<'a, T> { - pub fn read_ptr_from_guest(&self) -> Result { - T::read_from_guest(self) - } -} - -impl<'a, T> GuestType for GuestPtr<'a, T> -where - T: GuestType, -{ - fn size() -> u32 { - 4 - } - fn align() -> u32 { - 4 - } - fn name() -> String { - format!("GuestPtr<{}>", T::name()) - } - fn validate<'b>(location: &GuestPtr<'b, GuestPtr<'b, T>>) -> Result<(), GuestError> { - // location is guaranteed to be in GuestMemory and aligned to 4 - let raw_ptr: u32 = unsafe { *(location.as_raw() as *const u32) }; - // GuestMemory can validate that the raw pointer contents are legal for T: - let _guest_ptr: GuestPtr = location.mem.ptr(raw_ptr)?; - Ok(()) - } -} - -// Operations for reading and writing Ptrs to memory: -impl<'a, T> GuestTypePtr<'a> for GuestPtr<'a, T> -where - T: GuestType, -{ - fn read_from_guest(location: &GuestPtr<'a, Self>) -> Result { - // location is guaranteed to be in GuestMemory and aligned to 4 - let raw_ptr: u32 = unsafe { *(location.as_raw() as *const u32) }; - // GuestMemory can validate that the raw pointer contents are legal for T: - let guest_ptr: GuestPtr<'a, T> = location.mem.ptr(raw_ptr)?; - Ok(guest_ptr) - } - fn write_to_guest(&self, location: &GuestPtrMut<'a, Self>) { - // location is guaranteed to be in GuestMemory and aligned to 4 - unsafe { - let raw_ptr: *mut u32 = location.as_raw() as *mut u32; - raw_ptr.write(self.region.start); - } - } -} - -#[derive(Clone)] -pub struct GuestPtrMut<'a, T> { - mem: &'a GuestMemory<'a>, - region: Region, - type_: PhantomData, -} - -impl<'a, T: fmt::Debug> fmt::Debug for GuestPtrMut<'a, T> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "GuestPtrMut {{ mem: {:?}, region: {:?} }}", - self.mem, self.region - ) - } -} - -impl<'a, T: GuestType> GuestPtrMut<'a, T> { - pub fn as_immut(&self) -> GuestPtr<'a, T> { - GuestPtr { - mem: self.mem, - region: self.region, - type_: self.type_, - } - } - - pub fn as_raw(&self) -> *const u8 { - self.as_immut().as_raw() - } - - pub fn elem(&self, elements: i32) -> Result, GuestError> { - self.mem - .ptr_mut(self.region.start + (elements * self.region.len as i32) as u32) - } - - pub fn cast(&self, offset: u32) -> Result, GuestError> { - self.mem.ptr_mut(self.region.start + offset) - } - - pub fn array_mut(&self, num_elems: u32) -> Result, GuestError> { - let region = self.region.extend((num_elems - 1) * T::size()); - if self.mem.contains(region) { - let ptr = GuestPtrMut { - mem: self.mem, - region: self.region, - type_: self.type_, - }; - Ok(GuestArrayMut { ptr, num_elems }) - } else { - Err(GuestError::PtrOutOfBounds(region)) - } - } -} - -impl<'a, T: GuestTypeCopy> GuestPtrMut<'a, T> { - pub fn as_ref(&self) -> Result, GuestError> { - self.as_immut().as_ref() - } - - pub fn as_ref_mut(&self) -> Result, GuestError> { - T::validate(&self.as_immut())?; - let handle = { - let mut borrows = self.mem.borrows.borrow_mut(); - borrows - .borrow_mut(self.region) - .ok_or_else(|| GuestError::PtrBorrowed(self.region))? - }; - Ok(GuestRefMut { - mem: self.mem, - region: self.region, - handle, - type_: self.type_, - }) - } -} - -impl<'a, T: GuestTypePtr<'a>> GuestPtrMut<'a, T> { - pub fn read_ptr_from_guest(&self) -> Result { - T::read_from_guest(&self.as_immut()) - } - pub fn write_ptr_to_guest(&self, ptr: &T) { - T::write_to_guest(ptr, &self); - } -} - -impl<'a, T: GuestTypeClone> GuestPtrMut<'a, T> { - pub fn clone_from_guest(&self) -> Result { - T::read_from_guest(&self.as_immut()) - } - - pub fn clone_to_guest(&self, val: &T) { - T::write_to_guest(val, &self) - } -} - -impl<'a, T> GuestType for GuestPtrMut<'a, T> -where - T: GuestType, -{ - fn size() -> u32 { - 4 - } - fn align() -> u32 { - 4 - } - fn name() -> String { - format!("GuestPtrMut<{}>", T::name()) - } - fn validate<'b>(location: &GuestPtr<'b, GuestPtrMut<'b, T>>) -> Result<(), GuestError> { - // location is guaranteed to be in GuestMemory and aligned to 4 - let raw_ptr: u32 = unsafe { *(location.as_raw() as *const u32) }; - // GuestMemory can validate that the raw pointer contents are legal for T: - let _guest_ptr: GuestPtr = location.mem.ptr(raw_ptr)?; - Ok(()) - } -} - -// Reading and writing GuestPtrMuts to memory: -impl<'a, T> GuestTypePtr<'a> for GuestPtrMut<'a, T> -where - T: GuestType, -{ - fn read_from_guest(location: &GuestPtr<'a, Self>) -> Result { - // location is guaranteed to be in GuestMemory and aligned to 4 - let raw_ptr: u32 = unsafe { *(location.as_raw() as *const u32) }; - // GuestMemory can validate that the raw pointer contents are legal for T: - let guest_ptr_mut: GuestPtrMut<'a, T> = location.mem.ptr_mut(raw_ptr)?; - Ok(guest_ptr_mut) - } - fn write_to_guest(&self, location: &GuestPtrMut<'a, Self>) { - // location is guaranteed to be in GuestMemory and aligned to 4 - unsafe { - let raw_ptr: *mut u32 = location.as_raw() as *mut u32; - raw_ptr.write(self.region.start); - } - } -} - -pub struct GuestRef<'a, T> { - mem: &'a GuestMemory<'a>, - region: Region, - handle: BorrowHandle, - type_: PhantomData, -} - -impl<'a, T: fmt::Debug> fmt::Debug for GuestRef<'a, T> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "GuestRef {{ mem: {:?}, region: {:?}, handle: {:?} }}", - self.mem, self.region, self.handle - ) - } -} - -impl<'a, T> GuestRef<'a, T> { - pub fn as_ptr(&self) -> GuestPtr<'a, T> { - GuestPtr { - mem: self.mem, - region: self.region, - type_: self.type_, - } - } -} - -impl<'a, T> ::std::ops::Deref for GuestRef<'a, T> -where - T: GuestTypeCopy, -{ - type Target = T; - fn deref(&self) -> &T { - unsafe { - ((self.mem.ptr as usize + self.region.start as usize) as *const T) - .as_ref() - .expect("GuestRef implies non-null") - } - } -} - -impl<'a, T> Drop for GuestRef<'a, T> { - fn drop(&mut self) { - let mut borrows = self.mem.borrows.borrow_mut(); - borrows.unborrow_immut(self.handle); - } -} - -pub struct GuestRefMut<'a, T> { - mem: &'a GuestMemory<'a>, - region: Region, - handle: BorrowHandle, - type_: PhantomData, -} - -impl<'a, T: fmt::Debug> fmt::Debug for GuestRefMut<'a, T> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "GuestRefMut {{ mem: {:?}, region: {:?}, handle: {:?} }}", - self.mem, self.region, self.handle - ) - } -} - -impl<'a, T> GuestRefMut<'a, T> { - pub fn as_ptr(&self) -> GuestPtr<'a, T> { - GuestPtr { - mem: self.mem, - region: self.region, - type_: self.type_, - } - } - pub fn as_ptr_mut(&self) -> GuestPtrMut<'a, T> { - GuestPtrMut { - mem: self.mem, - region: self.region, - type_: self.type_, - } - } -} - -impl<'a, T> ::std::ops::Deref for GuestRefMut<'a, T> -where - T: GuestTypeCopy, -{ - type Target = T; - fn deref(&self) -> &T { - unsafe { - ((self.mem.ptr as usize + self.region.start as usize) as *const T) - .as_ref() - .expect("GuestRef implies non-null") - } - } -} - -impl<'a, T> ::std::ops::DerefMut for GuestRefMut<'a, T> -where - T: GuestTypeCopy, -{ - fn deref_mut(&mut self) -> &mut T { - unsafe { - ((self.mem.ptr as usize + self.region.start as usize) as *mut T) - .as_mut() - .expect("GuestRef implies non-null") - } - } -} - -impl<'a, T> Drop for GuestRefMut<'a, T> { - fn drop(&mut self) { - let mut borrows = self.mem.borrows.borrow_mut(); - borrows.unborrow_mut(self.handle); - } -} - -pub struct GuestArray<'a, T> -where - T: GuestType, -{ - ptr: GuestPtr<'a, T>, - num_elems: u32, -} - -impl<'a, T: GuestType + fmt::Debug> fmt::Debug for GuestArray<'a, T> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "GuestArray {{ ptr: {:?}, num_elems: {:?} }}", - self.ptr, self.num_elems - ) - } -} - -impl<'a, T> GuestArray<'a, T> -where - T: GuestTypeCopy, -{ - pub fn as_ref(&self) -> Result, GuestError> { - let mut ptr = self.ptr.clone(); - for _ in 0..self.num_elems { - ptr = ptr.elem(1)?; - T::validate(&ptr)?; - } - let region = self.ptr.region.extend((self.num_elems - 1) * T::size()); - let handle = { - let mut borrows = self.ptr.mem.borrows.borrow_mut(); - borrows - .borrow_immut(region) - .ok_or_else(|| GuestError::PtrBorrowed(region))? - }; - let ref_ = GuestRef { - mem: self.ptr.mem, - region, - handle, - type_: self.ptr.type_, - }; - Ok(GuestArrayRef { - ref_, - num_elems: self.num_elems, - }) - } -} - -pub struct GuestArrayRef<'a, T> -where - T: GuestType, -{ - ref_: GuestRef<'a, T>, - num_elems: u32, -} - -impl<'a, T> fmt::Debug for GuestArrayRef<'a, T> -where - T: GuestType + fmt::Debug, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "GuestArrayRef {{ ref_: {:?}, num_elems: {:?} }}", - self.ref_, self.num_elems - ) - } -} - -impl<'a, T> ::std::ops::Deref for GuestArrayRef<'a, T> -where - T: GuestTypeCopy, -{ - type Target = [T]; - - fn deref(&self) -> &Self::Target { - unsafe { - std::slice::from_raw_parts( - self.ref_.as_ptr().as_raw() as *const T, - self.num_elems as usize, - ) - } - } -} - -pub struct GuestArrayMut<'a, T> -where - T: GuestType, -{ - ptr: GuestPtrMut<'a, T>, - num_elems: u32, -} - -impl<'a, T: GuestType + fmt::Debug> fmt::Debug for GuestArrayMut<'a, T> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "GuestArrayMut {{ ptr: {:?}, num_elems: {:?} }}", - self.ptr, self.num_elems - ) - } -} - -impl<'a, T> GuestArrayMut<'a, T> -where - T: GuestTypeCopy, -{ - pub fn as_ref(&self) -> Result, GuestError> { - let arr = GuestArray { - ptr: self.ptr.as_immut(), - num_elems: self.num_elems, - }; - arr.as_ref() - } - - pub fn as_ref_mut(&self) -> Result, GuestError> { - let mut ptr = self.ptr.as_immut(); - for _ in 0..self.num_elems { - ptr = ptr.elem(1)?; - T::validate(&ptr)?; - } - let region = self.ptr.region.extend((self.num_elems - 1) * T::size()); - let handle = { - let mut borrows = self.ptr.mem.borrows.borrow_mut(); - borrows - .borrow_mut(region) - .ok_or_else(|| GuestError::PtrBorrowed(region))? - }; - let ref_mut = GuestRefMut { - mem: self.ptr.mem, - region, - handle, - type_: self.ptr.type_, - }; - Ok(GuestArrayRefMut { - ref_mut, - num_elems: self.num_elems, - }) - } -} - -pub struct GuestArrayRefMut<'a, T> -where - T: GuestType, -{ - ref_mut: GuestRefMut<'a, T>, - num_elems: u32, -} - -impl<'a, T> fmt::Debug for GuestArrayRefMut<'a, T> -where - T: GuestType + fmt::Debug, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "GuestArrayRefMut {{ ref_mut: {:?}, num_elems: {:?} }}", - self.ref_mut, self.num_elems - ) - } -} - -impl<'a, T> ::std::ops::Deref for GuestArrayRefMut<'a, T> -where - T: GuestTypeCopy, -{ - type Target = [T]; - - fn deref(&self) -> &Self::Target { - unsafe { - std::slice::from_raw_parts( - self.ref_mut.as_ptr().as_raw() as *const T, - self.num_elems as usize, - ) - } - } -} - -impl<'a, T> ::std::ops::DerefMut for GuestArrayRefMut<'a, T> -where - T: GuestTypeCopy, -{ - fn deref_mut(&mut self) -> &mut Self::Target { - unsafe { - std::slice::from_raw_parts_mut( - self.ref_mut.as_ptr_mut().as_raw() as *mut T, - self.num_elems as usize, - ) - } - } -} - -#[cfg(test)] -mod test { - use super::*; - - #[repr(align(4096))] - struct HostMemory { - buffer: [u8; 4096], - } - - impl HostMemory { - pub fn new() -> Self { - Self { buffer: [0; 4096] } - } - pub fn as_mut_ptr(&mut self) -> *mut u8 { - self.buffer.as_mut_ptr() - } - pub fn len(&self) -> usize { - self.buffer.len() - } - } - - #[test] - fn guest_array_out_of_bounds() { - let mut host_memory = HostMemory::new(); - let guest_memory = GuestMemory::new(host_memory.as_mut_ptr(), host_memory.len() as u32); - // try extracting an immutable array out of memory bounds - let ptr: GuestPtr = guest_memory.ptr(4092).expect("ptr to last i32 el"); - let err = ptr.array(2).expect_err("out of bounds ptr error"); - assert_eq!(err, GuestError::PtrOutOfBounds(Region::new(4092, 8))); - // try extracting an mutable array out of memory bounds - let ptr: GuestPtrMut = guest_memory.ptr_mut(4092).expect("ptr mut to last i32 el"); - let err = ptr.array_mut(2).expect_err("out of bounds ptr error"); - assert_eq!(err, GuestError::PtrOutOfBounds(Region::new(4092, 8))); - } - - #[test] - fn guest_array() { - let mut host_memory = HostMemory::new(); - let guest_memory = GuestMemory::new(host_memory.as_mut_ptr(), host_memory.len() as u32); - // write a simple array into memory - { - let ptr: GuestPtrMut = guest_memory.ptr_mut(0).expect("ptr mut to first el"); - let mut el = ptr.as_ref_mut().expect("ref mut to first el"); - *el = 1; - let ptr: GuestPtrMut = guest_memory.ptr_mut(4).expect("ptr mut to second el"); - let mut el = ptr.as_ref_mut().expect("ref mu to second el"); - *el = 2; - let ptr: GuestPtrMut = guest_memory.ptr_mut(8).expect("ptr mut to third el"); - let mut el = ptr.as_ref_mut().expect("ref mut to third el"); - *el = 3; - } - // extract as array - let ptr: GuestPtr = guest_memory.ptr(0).expect("ptr to first el"); - let arr = ptr.array(3).expect("convert ptr to array"); - let as_ref = arr.as_ref().expect("array borrowed immutably"); - assert_eq!(&*as_ref, &[1, 2, 3]); - // borrowing again should be fine - let as_ref2 = arr.as_ref().expect("array borrowed immutably again"); - assert_eq!(&*as_ref2, &*as_ref); - } - - #[test] - fn guest_array_mut() { - let mut host_memory = HostMemory::new(); - let guest_memory = GuestMemory::new(host_memory.as_mut_ptr(), host_memory.len() as u32); - // set elems of array to zero - { - let ptr: GuestPtrMut = guest_memory.ptr_mut(0).expect("ptr mut to first el"); - let mut el = ptr.as_ref_mut().expect("ref mut to first el"); - *el = 0; - let ptr: GuestPtrMut = guest_memory.ptr_mut(4).expect("ptr mut to second el"); - let mut el = ptr.as_ref_mut().expect("ref mu to second el"); - *el = 0; - let ptr: GuestPtrMut = guest_memory.ptr_mut(8).expect("ptr mut to third el"); - let mut el = ptr.as_ref_mut().expect("ref mut to third el"); - *el = 0; - } - // extract as array and verify all is zero - let ptr: GuestPtrMut = guest_memory.ptr_mut(0).expect("ptr mut to first el"); - let arr = ptr.array_mut(3).expect("convert ptr mut to array mut"); - assert_eq!(&*arr.as_ref().expect("array borrowed immutably"), &[0; 3]); - // populate the array and re-verify - for el in &mut *arr.as_ref_mut().expect("array borrowed mutably") { - *el = 10; - } - // re-validate - assert_eq!(&*arr.as_ref().expect("array borrowed immutably"), &[10; 3]); - } - - #[test] - #[should_panic( - expected = "array borrowed immutably while borrowed mutably: PtrBorrowed(Region { start: 0, len: 12 })" - )] - fn guest_array_mut_borrow_checker_1() { - let mut host_memory = HostMemory::new(); - let guest_memory = GuestMemory::new(host_memory.as_mut_ptr(), host_memory.len() as u32); - let ptr: GuestPtrMut = guest_memory.ptr_mut(0).expect("ptr mut to first el"); - let arr = GuestArrayMut { ptr, num_elems: 3 }; - // borrow mutably - let _as_mut = arr - .as_ref_mut() - .expect("array borrowed mutably for the first time"); - // borrow immutably should be fine - let _as_ref = arr - .as_ref() - .expect("array borrowed immutably while borrowed mutably"); - } - - #[test] - #[should_panic( - expected = "array borrowed mutably while borrowed mutably: PtrBorrowed(Region { start: 0, len: 12 })" - )] - fn guest_array_mut_borrow_checker_2() { - let mut host_memory = HostMemory::new(); - let guest_memory = GuestMemory::new(host_memory.as_mut_ptr(), host_memory.len() as u32); - let ptr: GuestPtrMut = guest_memory.ptr_mut(0).expect("ptr mut to first el"); - let arr = GuestArrayMut { ptr, num_elems: 3 }; - // borrow mutably - let _as_mut = arr - .as_ref_mut() - .expect("array borrowed mutably for the first time"); - // try borrowing mutably again - let _as_mut2 = arr - .as_ref_mut() - .expect("array borrowed mutably while borrowed mutably"); - } -} diff --git a/crates/memory/src/memory/array.rs b/crates/memory/src/memory/array.rs new file mode 100644 index 0000000000..2ba08587b7 --- /dev/null +++ b/crates/memory/src/memory/array.rs @@ -0,0 +1,335 @@ +use super::ptr::{GuestPtr, GuestPtrMut, GuestRef, GuestRefMut}; +use crate::{GuestError, GuestType, GuestTypeCopy}; +use std::{ + fmt, + ops::{Deref, DerefMut}, +}; + +pub struct GuestArray<'a, T> +where + T: GuestType, +{ + pub(super) ptr: GuestPtr<'a, T>, + pub(super) num_elems: u32, +} + +impl<'a, T> fmt::Debug for GuestArray<'a, T> +where + T: GuestType + fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "GuestArray {{ ptr: {:?}, num_elems: {:?} }}", + self.ptr, self.num_elems + ) + } +} + +impl<'a, T> GuestArray<'a, T> +where + T: GuestTypeCopy, +{ + pub fn as_ref(&self) -> Result, GuestError> { + let mut ptr = self.ptr.clone(); + for _ in 0..self.num_elems { + ptr = ptr.elem(1)?; + T::validate(&ptr)?; + } + let region = self.ptr.region.extend((self.num_elems - 1) * T::size()); + let handle = { + let mut borrows = self.ptr.mem.borrows.borrow_mut(); + borrows + .borrow_immut(region) + .ok_or_else(|| GuestError::PtrBorrowed(region))? + }; + let ref_ = GuestRef { + mem: self.ptr.mem, + region, + handle, + type_: self.ptr.type_, + }; + Ok(GuestArrayRef { + ref_, + num_elems: self.num_elems, + }) + } +} + +pub struct GuestArrayRef<'a, T> +where + T: GuestType, +{ + ref_: GuestRef<'a, T>, + num_elems: u32, +} + +impl<'a, T> fmt::Debug for GuestArrayRef<'a, T> +where + T: GuestType + fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "GuestArrayRef {{ ref_: {:?}, num_elems: {:?} }}", + self.ref_, self.num_elems + ) + } +} + +impl<'a, T> Deref for GuestArrayRef<'a, T> +where + T: GuestTypeCopy, +{ + type Target = [T]; + + fn deref(&self) -> &Self::Target { + unsafe { + std::slice::from_raw_parts( + self.ref_.as_ptr().as_raw() as *const T, + self.num_elems as usize, + ) + } + } +} + +pub struct GuestArrayMut<'a, T> +where + T: GuestType, +{ + pub(super) ptr: GuestPtrMut<'a, T>, + pub(super) num_elems: u32, +} + +impl<'a, T> fmt::Debug for GuestArrayMut<'a, T> +where + T: GuestType + fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "GuestArrayMut {{ ptr: {:?}, num_elems: {:?} }}", + self.ptr, self.num_elems + ) + } +} + +impl<'a, T> GuestArrayMut<'a, T> +where + T: GuestTypeCopy, +{ + pub fn as_ref(&self) -> Result, GuestError> { + let arr = GuestArray { + ptr: self.ptr.as_immut(), + num_elems: self.num_elems, + }; + arr.as_ref() + } + + pub fn as_ref_mut(&self) -> Result, GuestError> { + let mut ptr = self.ptr.as_immut(); + for _ in 0..self.num_elems { + ptr = ptr.elem(1)?; + T::validate(&ptr)?; + } + let region = self.ptr.region.extend((self.num_elems - 1) * T::size()); + let handle = { + let mut borrows = self.ptr.mem.borrows.borrow_mut(); + borrows + .borrow_mut(region) + .ok_or_else(|| GuestError::PtrBorrowed(region))? + }; + let ref_mut = GuestRefMut { + mem: self.ptr.mem, + region, + handle, + type_: self.ptr.type_, + }; + Ok(GuestArrayRefMut { + ref_mut, + num_elems: self.num_elems, + }) + } +} + +pub struct GuestArrayRefMut<'a, T> +where + T: GuestType, +{ + ref_mut: GuestRefMut<'a, T>, + num_elems: u32, +} + +impl<'a, T> fmt::Debug for GuestArrayRefMut<'a, T> +where + T: GuestType + fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "GuestArrayRefMut {{ ref_mut: {:?}, num_elems: {:?} }}", + self.ref_mut, self.num_elems + ) + } +} + +impl<'a, T> Deref for GuestArrayRefMut<'a, T> +where + T: GuestTypeCopy, +{ + type Target = [T]; + + fn deref(&self) -> &Self::Target { + unsafe { + std::slice::from_raw_parts( + self.ref_mut.as_ptr().as_raw() as *const T, + self.num_elems as usize, + ) + } + } +} + +impl<'a, T> DerefMut for GuestArrayRefMut<'a, T> +where + T: GuestTypeCopy, +{ + fn deref_mut(&mut self) -> &mut Self::Target { + unsafe { + std::slice::from_raw_parts_mut( + self.ref_mut.as_ptr_mut().as_raw() as *mut T, + self.num_elems as usize, + ) + } + } +} + +#[cfg(test)] +mod test { + use super::super::{ + ptr::{GuestPtr, GuestPtrMut}, + GuestError, GuestMemory, Region, + }; + + #[repr(align(4096))] + struct HostMemory { + buffer: [u8; 4096], + } + + impl HostMemory { + pub fn new() -> Self { + Self { buffer: [0; 4096] } + } + pub fn as_mut_ptr(&mut self) -> *mut u8 { + self.buffer.as_mut_ptr() + } + pub fn len(&self) -> usize { + self.buffer.len() + } + } + + #[test] + fn out_of_bounds() { + let mut host_memory = HostMemory::new(); + let guest_memory = GuestMemory::new(host_memory.as_mut_ptr(), host_memory.len() as u32); + // try extracting an immutable array out of memory bounds + let ptr: GuestPtr = guest_memory.ptr(4092).expect("ptr to last i32 el"); + let err = ptr.array(2).expect_err("out of bounds ptr error"); + assert_eq!(err, GuestError::PtrOutOfBounds(Region::new(4092, 8))); + // try extracting an mutable array out of memory bounds + let ptr: GuestPtrMut = guest_memory.ptr_mut(4092).expect("ptr mut to last i32 el"); + let err = ptr.array_mut(2).expect_err("out of bounds ptr error"); + assert_eq!(err, GuestError::PtrOutOfBounds(Region::new(4092, 8))); + } + + #[test] + fn ptr_to_array() { + let mut host_memory = HostMemory::new(); + let guest_memory = GuestMemory::new(host_memory.as_mut_ptr(), host_memory.len() as u32); + // write a simple array into memory + { + let ptr: GuestPtrMut = guest_memory.ptr_mut(0).expect("ptr mut to first el"); + let mut el = ptr.as_ref_mut().expect("ref mut to first el"); + *el = 1; + let ptr: GuestPtrMut = guest_memory.ptr_mut(4).expect("ptr mut to second el"); + let mut el = ptr.as_ref_mut().expect("ref mu to second el"); + *el = 2; + let ptr: GuestPtrMut = guest_memory.ptr_mut(8).expect("ptr mut to third el"); + let mut el = ptr.as_ref_mut().expect("ref mut to third el"); + *el = 3; + } + // extract as array + let ptr: GuestPtr = guest_memory.ptr(0).expect("ptr to first el"); + let arr = ptr.array(3).expect("convert ptr to array"); + let as_ref = arr.as_ref().expect("array borrowed immutably"); + assert_eq!(&*as_ref, &[1, 2, 3]); + // borrowing again should be fine + let as_ref2 = arr.as_ref().expect("array borrowed immutably again"); + assert_eq!(&*as_ref2, &*as_ref); + } + + #[test] + fn ptr_mut_to_array_mut() { + let mut host_memory = HostMemory::new(); + let guest_memory = GuestMemory::new(host_memory.as_mut_ptr(), host_memory.len() as u32); + // set elems of array to zero + { + let ptr: GuestPtrMut = guest_memory.ptr_mut(0).expect("ptr mut to first el"); + let mut el = ptr.as_ref_mut().expect("ref mut to first el"); + *el = 0; + let ptr: GuestPtrMut = guest_memory.ptr_mut(4).expect("ptr mut to second el"); + let mut el = ptr.as_ref_mut().expect("ref mu to second el"); + *el = 0; + let ptr: GuestPtrMut = guest_memory.ptr_mut(8).expect("ptr mut to third el"); + let mut el = ptr.as_ref_mut().expect("ref mut to third el"); + *el = 0; + } + // extract as array and verify all is zero + let ptr: GuestPtrMut = guest_memory.ptr_mut(0).expect("ptr mut to first el"); + let arr = ptr.array_mut(3).expect("convert ptr mut to array mut"); + assert_eq!(&*arr.as_ref().expect("array borrowed immutably"), &[0; 3]); + // populate the array and re-verify + for el in &mut *arr.as_ref_mut().expect("array borrowed mutably") { + *el = 10; + } + // re-validate + assert_eq!(&*arr.as_ref().expect("array borrowed immutably"), &[10; 3]); + } + + #[test] + #[should_panic( + expected = "array borrowed immutably while borrowed mutably: PtrBorrowed(Region { start: 0, len: 12 })" + )] + fn borrow_mut_then_immut() { + let mut host_memory = HostMemory::new(); + let guest_memory = GuestMemory::new(host_memory.as_mut_ptr(), host_memory.len() as u32); + let ptr: GuestPtrMut = guest_memory.ptr_mut(0).expect("ptr mut to first el"); + let arr = ptr.array_mut(3).expect("convert ptr mut to array mut"); + // borrow mutably + let _as_mut = arr + .as_ref_mut() + .expect("array borrowed mutably for the first time"); + // borrow immutably should fail + let _as_ref = arr + .as_ref() + .expect("array borrowed immutably while borrowed mutably"); + } + + #[test] + #[should_panic( + expected = "array borrowed mutably while borrowed mutably: PtrBorrowed(Region { start: 0, len: 12 })" + )] + fn borrow_mut_twice() { + let mut host_memory = HostMemory::new(); + let guest_memory = GuestMemory::new(host_memory.as_mut_ptr(), host_memory.len() as u32); + let ptr: GuestPtrMut = guest_memory.ptr_mut(0).expect("ptr mut to first el"); + let arr = ptr.array_mut(3).expect("convert ptr mut to array mut"); + // borrow mutably + let _as_mut = arr + .as_ref_mut() + .expect("array borrowed mutably for the first time"); + // try borrowing mutably again + let _as_mut2 = arr + .as_ref_mut() + .expect("array borrowed mutably while borrowed mutably"); + } +} diff --git a/crates/memory/src/memory/mod.rs b/crates/memory/src/memory/mod.rs new file mode 100644 index 0000000000..5892df29da --- /dev/null +++ b/crates/memory/src/memory/mod.rs @@ -0,0 +1,70 @@ +mod array; +mod ptr; + +pub use array::*; +pub use ptr::*; + +use crate::{borrow::GuestBorrows, GuestError, GuestType, Region}; +use std::{cell::RefCell, fmt, marker::PhantomData, rc::Rc}; + +pub struct GuestMemory<'a> { + ptr: *mut u8, + len: u32, + lifetime: PhantomData<&'a ()>, + borrows: Rc>, +} + +impl<'a> fmt::Debug for GuestMemory<'a> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "GuestMemory {{ ptr: {:?}, len: {:?}, borrows: {:?} }}", + self.ptr, self.len, self.borrows + ) + } +} + +impl<'a> GuestMemory<'a> { + pub fn new(ptr: *mut u8, len: u32) -> Self { + assert_eq!(ptr as usize % 4096, 0, "GuestMemory must be page-aligned"); + Self { + ptr, + len, + lifetime: PhantomData, + borrows: Rc::new(RefCell::new(GuestBorrows::new())), + } + } + + fn contains(&self, r: Region) -> bool { + r.start < self.len + && r.len < self.len // make sure next clause doesnt underflow + && r.start <= (self.len - r.len) + } + + pub fn ptr(&'a self, at: u32) -> Result, GuestError> { + let region = Region { + start: at, + len: T::size(), + }; + if !self.contains(region) { + Err(GuestError::PtrOutOfBounds(region))?; + } + if at % T::align() != 0 { + Err(GuestError::PtrNotAligned(region, T::align()))?; + } + Ok(GuestPtr { + mem: &self, + region, + type_: PhantomData, + }) + } + + pub fn ptr_mut(&'a self, at: u32) -> Result, GuestError> { + let ptr = self.ptr(at)?; + Ok(GuestPtrMut { + mem: ptr.mem, + region: ptr.region, + type_: ptr.type_, + }) + } +} diff --git a/crates/memory/src/memory/ptr.rs b/crates/memory/src/memory/ptr.rs new file mode 100644 index 0000000000..e9f64ba715 --- /dev/null +++ b/crates/memory/src/memory/ptr.rs @@ -0,0 +1,564 @@ +use super::{ + array::{GuestArray, GuestArrayMut}, + GuestMemory, +}; +use crate::{ + borrow::BorrowHandle, GuestError, GuestType, GuestTypeClone, GuestTypeCopy, GuestTypePtr, + Region, +}; +use std::{ + fmt, + marker::PhantomData, + ops::{Deref, DerefMut}, +}; + +#[derive(Clone)] +pub struct GuestPtr<'a, T> { + pub(super) mem: &'a GuestMemory<'a>, + pub(super) region: Region, + pub(super) type_: PhantomData, +} + +impl<'a, T> fmt::Debug for GuestPtr<'a, T> +where + T: fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "GuestPtr {{ mem: {:?}, region: {:?} }}", + self.mem, self.region + ) + } +} + +impl<'a, T: GuestType> GuestPtr<'a, T> { + pub fn as_raw(&self) -> *const u8 { + (self.mem.ptr as usize + self.region.start as usize) as *const u8 + } + + pub fn elem(&self, elements: i32) -> Result { + self.mem + .ptr(self.region.start + (elements * self.region.len as i32) as u32) + } + + pub fn cast(&self, offset: u32) -> Result, GuestError> { + self.mem.ptr(self.region.start + offset) + } + + pub fn array(&self, num_elems: u32) -> Result, GuestError> { + let region = self.region.extend((num_elems - 1) * T::size()); + if self.mem.contains(region) { + let ptr = GuestPtr { + mem: self.mem, + region: self.region, + type_: self.type_, + }; + Ok(GuestArray { ptr, num_elems }) + } else { + Err(GuestError::PtrOutOfBounds(region)) + } + } +} + +impl<'a, T> GuestPtr<'a, T> +where + T: GuestTypeCopy, +{ + pub fn as_ref(&self) -> Result, GuestError> { + T::validate(&self)?; + let handle = { + let mut borrows = self.mem.borrows.borrow_mut(); + borrows + .borrow_immut(self.region) + .ok_or_else(|| GuestError::PtrBorrowed(self.region))? + }; + Ok(GuestRef { + mem: self.mem, + region: self.region, + handle, + type_: self.type_, + }) + } +} + +impl<'a, T> GuestPtr<'a, T> +where + T: GuestTypeClone, +{ + pub fn clone_from_guest(&self) -> Result { + T::read_from_guest(self) + } +} + +impl<'a, T> GuestPtr<'a, T> +where + T: GuestTypePtr<'a>, +{ + pub fn read_ptr_from_guest(&self) -> Result { + T::read_from_guest(self) + } +} + +impl<'a, T> GuestType for GuestPtr<'a, T> +where + T: GuestType, +{ + fn size() -> u32 { + 4 + } + + fn align() -> u32 { + 4 + } + + fn name() -> String { + format!("GuestPtr<{}>", T::name()) + } + + fn validate<'b>(location: &GuestPtr<'b, GuestPtr<'b, T>>) -> Result<(), GuestError> { + // location is guaranteed to be in GuestMemory and aligned to 4 + let raw_ptr: u32 = unsafe { *(location.as_raw() as *const u32) }; + // GuestMemory can validate that the raw pointer contents are legal for T: + let _guest_ptr: GuestPtr = location.mem.ptr(raw_ptr)?; + Ok(()) + } +} + +// Operations for reading and writing Ptrs to memory: +impl<'a, T> GuestTypePtr<'a> for GuestPtr<'a, T> +where + T: GuestType, +{ + fn read_from_guest(location: &GuestPtr<'a, Self>) -> Result { + // location is guaranteed to be in GuestMemory and aligned to 4 + let raw_ptr: u32 = unsafe { *(location.as_raw() as *const u32) }; + // GuestMemory can validate that the raw pointer contents are legal for T: + let guest_ptr: GuestPtr<'a, T> = location.mem.ptr(raw_ptr)?; + Ok(guest_ptr) + } + + fn write_to_guest(&self, location: &GuestPtrMut<'a, Self>) { + // location is guaranteed to be in GuestMemory and aligned to 4 + unsafe { + let raw_ptr: *mut u32 = location.as_raw() as *mut u32; + raw_ptr.write(self.region.start); + } + } +} + +#[derive(Clone)] +pub struct GuestPtrMut<'a, T> { + pub(super) mem: &'a GuestMemory<'a>, + pub(super) region: Region, + pub(super) type_: PhantomData, +} + +impl<'a, T> fmt::Debug for GuestPtrMut<'a, T> +where + T: fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "GuestPtrMut {{ mem: {:?}, region: {:?} }}", + self.mem, self.region + ) + } +} + +impl<'a, T> GuestPtrMut<'a, T> +where + T: GuestType, +{ + pub fn as_immut(&self) -> GuestPtr<'a, T> { + GuestPtr { + mem: self.mem, + region: self.region, + type_: self.type_, + } + } + + pub fn as_raw(&self) -> *const u8 { + self.as_immut().as_raw() + } + + pub fn elem(&self, elements: i32) -> Result { + self.mem + .ptr_mut(self.region.start + (elements * self.region.len as i32) as u32) + } + + pub fn cast( + &self, + offset: u32, + ) -> Result, GuestError> { + self.mem.ptr_mut(self.region.start + offset) + } + + pub fn array_mut(&self, num_elems: u32) -> Result, GuestError> { + let region = self.region.extend((num_elems - 1) * T::size()); + if self.mem.contains(region) { + let ptr = GuestPtrMut { + mem: self.mem, + region: self.region, + type_: self.type_, + }; + Ok(GuestArrayMut { ptr, num_elems }) + } else { + Err(GuestError::PtrOutOfBounds(region)) + } + } +} + +impl<'a, T> GuestPtrMut<'a, T> +where + T: GuestTypeCopy, +{ + pub fn as_ref(&self) -> Result, GuestError> { + self.as_immut().as_ref() + } + + pub fn as_ref_mut(&self) -> Result, GuestError> { + T::validate(&self.as_immut())?; + let handle = { + let mut borrows = self.mem.borrows.borrow_mut(); + borrows + .borrow_mut(self.region) + .ok_or_else(|| GuestError::PtrBorrowed(self.region))? + }; + Ok(GuestRefMut { + mem: self.mem, + region: self.region, + handle, + type_: self.type_, + }) + } +} + +impl<'a, T> GuestPtrMut<'a, T> +where + T: GuestTypePtr<'a>, +{ + pub fn read_ptr_from_guest(&self) -> Result { + T::read_from_guest(&self.as_immut()) + } + + pub fn write_ptr_to_guest(&self, ptr: &T) { + T::write_to_guest(ptr, &self); + } +} + +impl<'a, T> GuestPtrMut<'a, T> +where + T: GuestTypeClone, +{ + pub fn clone_from_guest(&self) -> Result { + T::read_from_guest(&self.as_immut()) + } + + pub fn clone_to_guest(&self, val: &T) { + T::write_to_guest(val, &self) + } +} + +impl<'a, T> GuestType for GuestPtrMut<'a, T> +where + T: GuestType, +{ + fn size() -> u32 { + 4 + } + + fn align() -> u32 { + 4 + } + + fn name() -> String { + format!("GuestPtrMut<{}>", T::name()) + } + + fn validate<'b>(location: &GuestPtr<'b, GuestPtrMut<'b, T>>) -> Result<(), GuestError> { + // location is guaranteed to be in GuestMemory and aligned to 4 + let raw_ptr: u32 = unsafe { *(location.as_raw() as *const u32) }; + // GuestMemory can validate that the raw pointer contents are legal for T: + let _guest_ptr: GuestPtr = location.mem.ptr(raw_ptr)?; + Ok(()) + } +} + +// Reading and writing GuestPtrMuts to memory: +impl<'a, T> GuestTypePtr<'a> for GuestPtrMut<'a, T> +where + T: GuestType, +{ + fn read_from_guest(location: &GuestPtr<'a, Self>) -> Result { + // location is guaranteed to be in GuestMemory and aligned to 4 + let raw_ptr: u32 = unsafe { *(location.as_raw() as *const u32) }; + // GuestMemory can validate that the raw pointer contents are legal for T: + let guest_ptr_mut: GuestPtrMut<'a, T> = location.mem.ptr_mut(raw_ptr)?; + Ok(guest_ptr_mut) + } + + fn write_to_guest(&self, location: &GuestPtrMut<'a, Self>) { + // location is guaranteed to be in GuestMemory and aligned to 4 + unsafe { + let raw_ptr: *mut u32 = location.as_raw() as *mut u32; + raw_ptr.write(self.region.start); + } + } +} + +pub struct GuestRef<'a, T> { + pub(super) mem: &'a GuestMemory<'a>, + pub(super) region: Region, + pub(super) handle: BorrowHandle, + pub(super) type_: PhantomData, +} + +impl<'a, T> fmt::Debug for GuestRef<'a, T> +where + T: fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "GuestRef {{ mem: {:?}, region: {:?}, handle: {:?} }}", + self.mem, self.region, self.handle + ) + } +} + +impl<'a, T> GuestRef<'a, T> { + pub fn as_ptr(&self) -> GuestPtr<'a, T> { + GuestPtr { + mem: self.mem, + region: self.region, + type_: self.type_, + } + } +} + +impl<'a, T> Deref for GuestRef<'a, T> +where + T: GuestTypeCopy, +{ + type Target = T; + + fn deref(&self) -> &Self::Target { + unsafe { + ((self.mem.ptr as usize + self.region.start as usize) as *const T) + .as_ref() + .expect("GuestRef implies non-null") + } + } +} + +impl<'a, T> Drop for GuestRef<'a, T> { + fn drop(&mut self) { + let mut borrows = self.mem.borrows.borrow_mut(); + borrows.unborrow_immut(self.handle); + } +} + +pub struct GuestRefMut<'a, T> { + pub(super) mem: &'a GuestMemory<'a>, + pub(super) region: Region, + pub(super) handle: BorrowHandle, + pub(super) type_: PhantomData, +} + +impl<'a, T> fmt::Debug for GuestRefMut<'a, T> +where + T: fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "GuestRefMut {{ mem: {:?}, region: {:?}, handle: {:?} }}", + self.mem, self.region, self.handle + ) + } +} + +impl<'a, T> GuestRefMut<'a, T> { + pub fn as_ptr(&self) -> GuestPtr<'a, T> { + GuestPtr { + mem: self.mem, + region: self.region, + type_: self.type_, + } + } + pub fn as_ptr_mut(&self) -> GuestPtrMut<'a, T> { + GuestPtrMut { + mem: self.mem, + region: self.region, + type_: self.type_, + } + } +} + +impl<'a, T> ::std::ops::Deref for GuestRefMut<'a, T> +where + T: GuestTypeCopy, +{ + type Target = T; + + fn deref(&self) -> &Self::Target { + unsafe { + ((self.mem.ptr as usize + self.region.start as usize) as *const T) + .as_ref() + .expect("GuestRef implies non-null") + } + } +} + +impl<'a, T> DerefMut for GuestRefMut<'a, T> +where + T: GuestTypeCopy, +{ + fn deref_mut(&mut self) -> &mut Self::Target { + unsafe { + ((self.mem.ptr as usize + self.region.start as usize) as *mut T) + .as_mut() + .expect("GuestRef implies non-null") + } + } +} + +impl<'a, T> Drop for GuestRefMut<'a, T> { + fn drop(&mut self) { + let mut borrows = self.mem.borrows.borrow_mut(); + borrows.unborrow_mut(self.handle); + } +} + +#[cfg(test)] +mod test { + use super::{ + super::{GuestError, GuestMemory, Region}, + {GuestPtr, GuestPtrMut}, + }; + + #[repr(align(4096))] + struct HostMemory { + buffer: [u8; 4096], + } + + impl HostMemory { + pub fn new() -> Self { + Self { buffer: [0; 4096] } + } + pub fn as_mut_ptr(&mut self) -> *mut u8 { + self.buffer.as_mut_ptr() + } + pub fn len(&self) -> usize { + self.buffer.len() + } + } + + #[test] + fn out_of_bounds() { + let mut host_memory = HostMemory::new(); + let guest_memory = GuestMemory::new(host_memory.as_mut_ptr(), host_memory.len() as u32); + // try extracting an immutable ptr out of memory bounds + let err = guest_memory + .ptr::>(4096) + .expect_err("out of bounds ptr error"); + assert_eq!(err, GuestError::PtrOutOfBounds(Region::new(4096, 4))); + // try extracting an mutable ptr out of memory bounds + let err = guest_memory + .ptr_mut::>(4096) + .expect_err("out of bounds ptr error"); + assert_eq!(err, GuestError::PtrOutOfBounds(Region::new(4096, 4))); + } + + #[test] + fn not_aligned() { + let mut host_memory = HostMemory::new(); + let guest_memory = GuestMemory::new(host_memory.as_mut_ptr(), host_memory.len() as u32); + // try extracting a misaligned immutable ptr + let err = guest_memory + .ptr::>(2) + .expect_err("ptr misaligned"); + assert_eq!(err, GuestError::PtrNotAligned(Region::new(2, 4), 4)); + // try extracting a misaligned mutable ptr + let err = guest_memory + .ptr_mut::>(2) + .expect_err("ptr mut misaligned"); + assert_eq!(err, GuestError::PtrNotAligned(Region::new(2, 4), 4)); + } + + #[test] + fn ptr_from_memory() { + let mut host_memory = HostMemory::new(); + let guest_memory = GuestMemory::new(host_memory.as_mut_ptr(), host_memory.len() as u32); + // write something to memory + { + let ptr: GuestPtrMut = guest_memory.ptr_mut(8).expect("ptr mut to the el"); + let mut el = ptr.as_ref_mut().expect("ref mut to the el"); + *el = 100; + } + // extract as ref + let ptr: GuestPtr = guest_memory.ptr(8).expect("ptr to the el"); + let as_ref = ptr.as_ref().expect("el borrowed immutably"); + assert_eq!(*as_ref, 100); + // borrowing again should be fine + let as_ref2 = ptr.as_ref().expect("el borrowed immutably again"); + assert_eq!(*as_ref2, *as_ref); + } + + #[test] + fn ptr_mut_from_memory() { + let mut host_memory = HostMemory::new(); + let guest_memory = GuestMemory::new(host_memory.as_mut_ptr(), host_memory.len() as u32); + // set elems of array to zero + { + let ptr: GuestPtrMut = guest_memory.ptr_mut(8).expect("ptr mut to the el"); + let mut el = ptr.as_ref_mut().expect("ref mut to the el"); + *el = 100; + } + // extract as ref + let ptr: GuestPtrMut = guest_memory.ptr_mut(8).expect("ptr mut to the el"); + assert_eq!(*ptr.as_ref().expect("el borrowed immutably"), 100); + // overwrite the memory and re-verify + *ptr.as_ref_mut().expect("el borrowed mutably") = 2000; + // re-validate + assert_eq!(*ptr.as_ref().expect("el borrowed immutably"), 2000); + } + + #[test] + #[should_panic( + expected = "el borrowed immutably while borrowed mutably: PtrBorrowed(Region { start: 0, len: 2 })" + )] + fn borrow_mut_then_immut() { + let mut host_memory = HostMemory::new(); + let guest_memory = GuestMemory::new(host_memory.as_mut_ptr(), host_memory.len() as u32); + let ptr: GuestPtrMut = guest_memory.ptr_mut(0).expect("ptr mut to the el"); + // borrow mutably + let _as_mut = ptr + .as_ref_mut() + .expect("el borrowed mutably for the first time"); + // borrow immutably should fail + let _as_ref = ptr + .as_ref() + .expect("el borrowed immutably while borrowed mutably"); + } + + #[test] + #[should_panic( + expected = "el borrowed mutably while borrowed mutably: PtrBorrowed(Region { start: 0, len: 2 })" + )] + fn borrow_mut_twice() { + let mut host_memory = HostMemory::new(); + let guest_memory = GuestMemory::new(host_memory.as_mut_ptr(), host_memory.len() as u32); + let ptr: GuestPtrMut = guest_memory.ptr_mut(0).expect("ptr mut to the el"); + // borrow mutably + let _as_mut = ptr + .as_ref_mut() + .expect("el borrowed mutably for the first time"); + // try borrowing mutably again + let _as_mut2 = ptr + .as_ref_mut() + .expect("el borrowed mutably while borrowed mutably"); + } +} From 48a218b5c509e8fbcdc5265f732cbaa883a8b979 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 13 Feb 2020 22:40:42 +0100 Subject: [PATCH 40/86] Refactor naming and crates info (#8) * Refactor naming and crates info This commit: * changes workspace crates to have a `wiggle_` prefix in names * rename `memory` module of `wiggle-memory` crate to `runtime` * fixes authors of all crates * Rename wiggle memory crate to runtime --- Cargo.toml | 8 +-- crates/generate/Cargo.toml | 6 +- crates/generate/src/funcs.rs | 8 +-- crates/generate/src/names.rs | 4 +- crates/generate/src/types.rs | 69 ++++++++++--------- crates/memory/Cargo.toml | 10 --- crates/{memory => runtime}/.gitignore | 0 crates/runtime/Cargo.toml | 8 +++ crates/{memory => runtime}/src/borrow.rs | 0 crates/{memory => runtime}/src/error.rs | 0 crates/{memory => runtime}/src/guest_type.rs | 0 crates/{memory => runtime}/src/lib.rs | 2 +- .../{memory => runtime}/src/memory/array.rs | 0 crates/{memory => runtime}/src/memory/mod.rs | 0 crates/{memory => runtime}/src/memory/ptr.rs | 0 crates/{memory => runtime}/src/region.rs | 0 tests/main.rs | 41 +++++------ 17 files changed, 77 insertions(+), 79 deletions(-) delete mode 100644 crates/memory/Cargo.toml rename crates/{memory => runtime}/.gitignore (100%) create mode 100644 crates/runtime/Cargo.toml rename crates/{memory => runtime}/src/borrow.rs (100%) rename crates/{memory => runtime}/src/error.rs (100%) rename crates/{memory => runtime}/src/guest_type.rs (100%) rename crates/{memory => runtime}/src/lib.rs (71%) rename crates/{memory => runtime}/src/memory/array.rs (100%) rename crates/{memory => runtime}/src/memory/mod.rs (100%) rename crates/{memory => runtime}/src/memory/ptr.rs (100%) rename crates/{memory => runtime}/src/region.rs (100%) diff --git a/Cargo.toml b/Cargo.toml index f71705b021..1d65b86102 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,12 +1,12 @@ [package] name = "wiggle" version = "0.1.0" -authors = ["Jakub Konka "] +authors = ["Pat Hickey ", "Jakub Konka "] edition = "2018" [dependencies] -generate = { path = "crates/generate" } -memory = { path = "crates/memory" } +wiggle-generate = { path = "crates/generate" } +wiggle-runtime = { path = "crates/runtime" } [dev-dependencies] proptest = "0.9" @@ -14,6 +14,6 @@ proptest = "0.9" [workspace] members = [ "crates/generate", - "crates/memory" + "crates/runtime" ] exclude = ["crates/WASI"] diff --git a/crates/generate/Cargo.toml b/crates/generate/Cargo.toml index 886fea8b3b..9926ca7f80 100644 --- a/crates/generate/Cargo.toml +++ b/crates/generate/Cargo.toml @@ -1,14 +1,14 @@ [package] -name = "generate" +name = "wiggle-generate" version = "0.1.0" -authors = ["Jakub Konka "] +authors = ["Pat Hickey ", "Jakub Konka "] edition = "2018" [lib] proc-macro = true [dependencies] -memory = { path = "../memory" } +wiggle-runtime = { path = "../runtime" } witx = { path = "../WASI/tools/witx" } quote = "1.0" proc-macro2 = "1.0" diff --git a/crates/generate/src/funcs.rs b/crates/generate/src/funcs.rs index 336199d222..7c8ef19b90 100644 --- a/crates/generate/src/funcs.rs +++ b/crates/generate/src/funcs.rs @@ -30,7 +30,7 @@ pub fn define_func(names: &Names, func: &witx::InterfaceFunc) -> TokenStream { }); let abi_args = quote!( - ctx: &mut #ctx_type, memory: &mut ::memory::GuestMemory, + ctx: &mut #ctx_type, memory: &mut wiggle_runtime::GuestMemory, #(#params),* ); let abi_ret = if let Some(ret) = &coretype.ret { @@ -62,8 +62,8 @@ pub fn define_func(names: &Names, func: &witx::InterfaceFunc) -> TokenStream { }; let err_typename = names.type_ref(&tref, anon_lifetime()); quote! { - let e = ::memory::GuestError::InFunc { funcname: #funcname, location: #location, err: Box::new(e) }; - let err: #err_typename = ::memory::GuestErrorType::from_error(e, ctx); + let e = wiggle_runtime::GuestError::InFunc { funcname: #funcname, location: #location, err: Box::new(e) }; + let err: #err_typename = wiggle_runtime::GuestErrorType::from_error(e, ctx); return #abi_ret::from(err); } } else { @@ -111,7 +111,7 @@ pub fn define_func(names: &Names, func: &witx::InterfaceFunc) -> TokenStream { let success = if let Some(ref err_type) = err_type { let err_typename = names.type_ref(&err_type, anon_lifetime()); quote! { - let success:#err_typename = ::memory::GuestErrorType::success(); + let success:#err_typename = wiggle_runtime::GuestErrorType::success(); #abi_ret::from(success) } } else { diff --git a/crates/generate/src/names.rs b/crates/generate/src/names.rs index 3759113218..504f9ab67b 100644 --- a/crates/generate/src/names.rs +++ b/crates/generate/src/names.rs @@ -62,11 +62,11 @@ impl Names { witx::Type::Builtin(builtin) => self.builtin_type(*builtin), witx::Type::Pointer(pointee) => { let pointee_type = self.type_ref(&pointee, lifetime.clone()); - quote!(::memory::GuestPtrMut<#lifetime, #pointee_type>) + quote!(wiggle_runtime::GuestPtrMut<#lifetime, #pointee_type>) } witx::Type::ConstPointer(pointee) => { let pointee_type = self.type_ref(&pointee, lifetime.clone()); - quote!(::memory::GuestPtr<#lifetime, #pointee_type>) + quote!(wiggle_runtime::GuestPtr<#lifetime, #pointee_type>) } _ => unimplemented!("anonymous type ref"), }, diff --git a/crates/generate/src/types.rs b/crates/generate/src/types.rs index f5bfd42bd1..10e34894db 100644 --- a/crates/generate/src/types.rs +++ b/crates/generate/src/types.rs @@ -21,11 +21,14 @@ pub fn define_datatype(names: &Names, namedtype: &witx::NamedType) -> TokenStrea witx::Type::Union(_) => unimplemented!("union types"), witx::Type::Handle(_h) => unimplemented!("handle types"), witx::Type::Builtin(b) => define_builtin(names, &namedtype.name, *b), - witx::Type::Pointer(p) => { - define_witx_pointer(names, &namedtype.name, quote!(::memory::GuestPtrMut), p) - } + witx::Type::Pointer(p) => define_witx_pointer( + names, + &namedtype.name, + quote!(wiggle_runtime::GuestPtrMut), + p, + ), witx::Type::ConstPointer(p) => { - define_witx_pointer(names, &namedtype.name, quote!(::memory::GuestPtr), p) + define_witx_pointer(names, &namedtype.name, quote!(wiggle_runtime::GuestPtr), p) } witx::Type::Array { .. } => unimplemented!("array types"), }, @@ -69,18 +72,18 @@ fn define_enum(names: &Names, name: &witx::Id, e: &witx::EnumDatatype) -> TokenS } impl ::std::convert::TryFrom<#repr> for #ident { - type Error = ::memory::GuestError; - fn try_from(value: #repr) -> Result<#ident, ::memory::GuestError> { + type Error = wiggle_runtime::GuestError; + fn try_from(value: #repr) -> Result<#ident, wiggle_runtime::GuestError> { match value as usize { #(#tryfrom_repr_cases),*, - _ => Err(::memory::GuestError::InvalidEnumValue(stringify!(#ident))), + _ => Err(wiggle_runtime::GuestError::InvalidEnumValue(stringify!(#ident))), } } } impl ::std::convert::TryFrom<#abi_repr> for #ident { - type Error = ::memory::GuestError; - fn try_from(value: #abi_repr) -> Result<#ident, ::memory::GuestError> { + type Error = wiggle_runtime::GuestError; + fn try_from(value: #abi_repr) -> Result<#ident, wiggle_runtime::GuestError> { #ident::try_from(value as #repr) } } @@ -99,7 +102,7 @@ fn define_enum(names: &Names, name: &witx::Id, e: &witx::EnumDatatype) -> TokenS } } - impl ::memory::GuestType for #ident { + impl wiggle_runtime::GuestType for #ident { fn size() -> u32 { ::std::mem::size_of::<#repr>() as u32 } @@ -109,7 +112,7 @@ fn define_enum(names: &Names, name: &witx::Id, e: &witx::EnumDatatype) -> TokenS fn name() -> String { stringify!(#ident).to_owned() } - fn validate<'a>(location: &::memory::GuestPtr<'a, #ident>) -> Result<(), ::memory::GuestError> { + fn validate<'a>(location: &wiggle_runtime::GuestPtr<'a, #ident>) -> Result<(), wiggle_runtime::GuestError> { use ::std::convert::TryFrom; let raw: #repr = unsafe { (location.as_raw() as *const #repr).read() }; let _ = #ident::try_from(raw)?; @@ -117,15 +120,15 @@ fn define_enum(names: &Names, name: &witx::Id, e: &witx::EnumDatatype) -> TokenS } } - impl ::memory::GuestTypeCopy for #ident {} - impl ::memory::GuestTypeClone for #ident { - fn read_from_guest(location: &::memory::GuestPtr<#ident>) -> Result<#ident, ::memory::GuestError> { + impl wiggle_runtime::GuestTypeCopy for #ident {} + impl wiggle_runtime::GuestTypeClone for #ident { + fn read_from_guest(location: &wiggle_runtime::GuestPtr<#ident>) -> Result<#ident, wiggle_runtime::GuestError> { use ::std::convert::TryFrom; let raw: #repr = unsafe { (location.as_raw() as *const #repr).read() }; let val = #ident::try_from(raw)?; Ok(val) } - fn write_to_guest(&self, location: &::memory::GuestPtrMut<#ident>) { + fn write_to_guest(&self, location: &wiggle_runtime::GuestPtrMut<#ident>) { let val: #repr = #repr::from(*self); unsafe { (location.as_raw() as *mut #repr).write(val) }; } @@ -192,13 +195,13 @@ fn define_copy_struct(names: &Names, name: &witx::Id, s: &witx::StructDatatype) quote! { #type_::validate( &ptr.cast(#offset).map_err(|e| - ::memory::GuestError::InDataField{ + wiggle_runtime::GuestError::InDataField{ typename: stringify!(#ident).to_owned(), field: stringify!(#fieldname).to_owned(), err: Box::new(e), })? ).map_err(|e| - ::memory::GuestError::InDataField { + wiggle_runtime::GuestError::InDataField { typename: stringify!(#ident).to_owned(), field: stringify!(#fieldname).to_owned(), err: Box::new(e), @@ -213,7 +216,7 @@ fn define_copy_struct(names: &Names, name: &witx::Id, s: &witx::StructDatatype) #(#member_decls),* } - impl ::memory::GuestType for #ident { + impl wiggle_runtime::GuestType for #ident { fn size() -> u32 { #size } @@ -223,12 +226,12 @@ fn define_copy_struct(names: &Names, name: &witx::Id, s: &witx::StructDatatype) fn name() -> String { stringify!(#ident).to_owned() } - fn validate(ptr: &::memory::GuestPtr<#ident>) -> Result<(), ::memory::GuestError> { + fn validate(ptr: &wiggle_runtime::GuestPtr<#ident>) -> Result<(), wiggle_runtime::GuestError> { #(#member_valids)* Ok(()) } } - impl ::memory::GuestTypeCopy for #ident {} + impl wiggle_runtime::GuestTypeCopy for #ident {} } } @@ -246,11 +249,11 @@ fn define_ptr_struct(names: &Names, name: &witx::Id, s: &witx::StructDatatype) - witx::Type::Builtin(builtin) => names.builtin_type(*builtin), witx::Type::Pointer(pointee) => { let pointee_type = names.type_ref(&pointee, quote!('a)); - quote!(::memory::GuestPtrMut<'a, #pointee_type>) + quote!(wiggle_runtime::GuestPtrMut<'a, #pointee_type>) } witx::Type::ConstPointer(pointee) => { let pointee_type = names.type_ref(&pointee, quote!('a)); - quote!(::memory::GuestPtr<'a, #pointee_type>) + quote!(wiggle_runtime::GuestPtr<'a, #pointee_type>) } _ => unimplemented!("other anonymous struct members"), }, @@ -264,11 +267,11 @@ fn define_ptr_struct(names: &Names, name: &witx::Id, s: &witx::StructDatatype) - witx::Type::Builtin(builtin) => names.builtin_type(*builtin), witx::Type::Pointer(pointee) => { let pointee_type = names.type_ref(&pointee, anon_lifetime()); - quote!(::memory::GuestPtrMut::<#pointee_type>) + quote!(wiggle_runtime::GuestPtrMut::<#pointee_type>) } witx::Type::ConstPointer(pointee) => { let pointee_type = names.type_ref(&pointee, anon_lifetime()); - quote!(::memory::GuestPtr::<#pointee_type>) + quote!(wiggle_runtime::GuestPtr::<#pointee_type>) } _ => unimplemented!("other anonymous struct members"), }, @@ -278,13 +281,13 @@ fn define_ptr_struct(names: &Names, name: &witx::Id, s: &witx::StructDatatype) - quote! { #type_::validate( &ptr.cast(#offset).map_err(|e| - ::memory::GuestError::InDataField{ + wiggle_runtime::GuestError::InDataField{ typename: stringify!(#ident).to_owned(), field: stringify!(#fieldname).to_owned(), err: Box::new(e), })? ).map_err(|e| - ::memory::GuestError::InDataField { + wiggle_runtime::GuestError::InDataField { typename: stringify!(#ident).to_owned(), field: stringify!(#fieldname).to_owned(), err: Box::new(e), @@ -312,13 +315,13 @@ fn define_ptr_struct(names: &Names, name: &witx::Id, s: &witx::StructDatatype) - witx::Type::Pointer(pointee) => { let pointee_type = names.type_ref(&pointee, anon_lifetime()); quote! { - let #name = ::memory::GuestPtrMut::<#pointee_type>::read_from_guest(&location.cast(#offset)?)?; + let #name = wiggle_runtime::GuestPtrMut::<#pointee_type>::read_from_guest(&location.cast(#offset)?)?; } } witx::Type::ConstPointer(pointee) => { let pointee_type = names.type_ref(&pointee, anon_lifetime()); quote! { - let #name = ::memory::GuestPtr::<#pointee_type>::read_from_guest(&location.cast(#offset)?)?; + let #name = wiggle_runtime::GuestPtr::<#pointee_type>::read_from_guest(&location.cast(#offset)?)?; } } _ => unimplemented!("other anonymous struct members"), @@ -338,7 +341,7 @@ fn define_ptr_struct(names: &Names, name: &witx::Id, s: &witx::StructDatatype) - #(#member_decls),* } - impl<'a> ::memory::GuestType for #ident<'a> { + impl<'a> wiggle_runtime::GuestType for #ident<'a> { fn size() -> u32 { #size } @@ -348,17 +351,17 @@ fn define_ptr_struct(names: &Names, name: &witx::Id, s: &witx::StructDatatype) - fn name() -> String { stringify!(#ident).to_owned() } - fn validate(ptr: &::memory::GuestPtr<#ident>) -> Result<(), ::memory::GuestError> { + fn validate(ptr: &wiggle_runtime::GuestPtr<#ident>) -> Result<(), wiggle_runtime::GuestError> { #(#member_valids)* Ok(()) } } - impl<'a> ::memory::GuestTypePtr<'a> for #ident<'a> { - fn read_from_guest(location: &::memory::GuestPtr<'a, #ident<'a>>) -> Result<#ident<'a>, ::memory::GuestError> { + impl<'a> wiggle_runtime::GuestTypePtr<'a> for #ident<'a> { + fn read_from_guest(location: &wiggle_runtime::GuestPtr<'a, #ident<'a>>) -> Result<#ident<'a>, wiggle_runtime::GuestError> { #(#member_reads)* Ok(#ident { #(#member_names),* }) } - fn write_to_guest(&self, location: &::memory::GuestPtrMut<'a, Self>) { + fn write_to_guest(&self, location: &wiggle_runtime::GuestPtrMut<'a, Self>) { #(#member_writes)* } } diff --git a/crates/memory/Cargo.toml b/crates/memory/Cargo.toml deleted file mode 100644 index f605782c99..0000000000 --- a/crates/memory/Cargo.toml +++ /dev/null @@ -1,10 +0,0 @@ -[package] -name = "memory" -version = "0.1.0" -authors = ["Pat Hickey "] -edition = "2018" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -thiserror = "1" diff --git a/crates/memory/.gitignore b/crates/runtime/.gitignore similarity index 100% rename from crates/memory/.gitignore rename to crates/runtime/.gitignore diff --git a/crates/runtime/Cargo.toml b/crates/runtime/Cargo.toml new file mode 100644 index 0000000000..4e711424de --- /dev/null +++ b/crates/runtime/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "wiggle-runtime" +version = "0.1.0" +authors = ["Pat Hickey ", "Jakub Konka "] +edition = "2018" + +[dependencies] +thiserror = "1" diff --git a/crates/memory/src/borrow.rs b/crates/runtime/src/borrow.rs similarity index 100% rename from crates/memory/src/borrow.rs rename to crates/runtime/src/borrow.rs diff --git a/crates/memory/src/error.rs b/crates/runtime/src/error.rs similarity index 100% rename from crates/memory/src/error.rs rename to crates/runtime/src/error.rs diff --git a/crates/memory/src/guest_type.rs b/crates/runtime/src/guest_type.rs similarity index 100% rename from crates/memory/src/guest_type.rs rename to crates/runtime/src/guest_type.rs diff --git a/crates/memory/src/lib.rs b/crates/runtime/src/lib.rs similarity index 71% rename from crates/memory/src/lib.rs rename to crates/runtime/src/lib.rs index fa1824f3b6..6f2b17ecfc 100644 --- a/crates/memory/src/lib.rs +++ b/crates/runtime/src/lib.rs @@ -4,7 +4,7 @@ mod guest_type; mod memory; mod region; -pub use self::memory::{GuestMemory, GuestPtr, GuestPtrMut, GuestRef, GuestRefMut}; pub use error::GuestError; pub use guest_type::{GuestErrorType, GuestType, GuestTypeClone, GuestTypeCopy, GuestTypePtr}; +pub use memory::{GuestMemory, GuestPtr, GuestPtrMut, GuestRef, GuestRefMut}; pub use region::Region; diff --git a/crates/memory/src/memory/array.rs b/crates/runtime/src/memory/array.rs similarity index 100% rename from crates/memory/src/memory/array.rs rename to crates/runtime/src/memory/array.rs diff --git a/crates/memory/src/memory/mod.rs b/crates/runtime/src/memory/mod.rs similarity index 100% rename from crates/memory/src/memory/mod.rs rename to crates/runtime/src/memory/mod.rs diff --git a/crates/memory/src/memory/ptr.rs b/crates/runtime/src/memory/ptr.rs similarity index 100% rename from crates/memory/src/memory/ptr.rs rename to crates/runtime/src/memory/ptr.rs diff --git a/crates/memory/src/region.rs b/crates/runtime/src/region.rs similarity index 100% rename from crates/memory/src/region.rs rename to crates/runtime/src/region.rs diff --git a/tests/main.rs b/tests/main.rs index c03a128d5a..89bfe21f52 100644 --- a/tests/main.rs +++ b/tests/main.rs @@ -1,13 +1,15 @@ -use memory::GuestRef; use proptest::prelude::*; +use wiggle_runtime::{ + GuestError, GuestErrorType, GuestMemory, GuestPtr, GuestPtrMut, GuestRef, GuestRefMut, +}; -generate::from_witx!({ +wiggle_generate::from_witx!({ witx: ["tests/test.witx"], ctx: WasiCtx, }); pub struct WasiCtx { - guest_errors: Vec<::memory::GuestError>, + guest_errors: Vec, } impl WasiCtx { @@ -27,17 +29,16 @@ impl foo::Foo for WasiCtx { fn baz( &mut self, input1: types::Excuse, - input2_ptr: ::memory::GuestPtrMut, - input3_ptr: ::memory::GuestPtr, - input4_ptr_ptr: ::memory::GuestPtrMut<::memory::GuestPtr>, + input2_ptr: GuestPtrMut, + input3_ptr: GuestPtr, + input4_ptr_ptr: GuestPtrMut>, ) -> Result<(), types::Errno> { println!("BAZ input1 {:?}", input1); // Read enum value from mutable: - let mut input2_ref: ::memory::GuestRefMut = - input2_ptr.as_ref_mut().map_err(|e| { - eprintln!("input2_ptr error: {}", e); - types::Errno::InvalidArg - })?; + let mut input2_ref: GuestRefMut = input2_ptr.as_ref_mut().map_err(|e| { + eprintln!("input2_ptr error: {}", e); + types::Errno::InvalidArg + })?; let input2: types::Excuse = *input2_ref; println!("input2 {:?}", input2); @@ -53,7 +54,7 @@ impl foo::Foo for WasiCtx { println!("wrote to input2_ref {:?}", input3); // Read ptr value from mutable ptr: - let input4_ptr: ::memory::GuestPtr = + let input4_ptr: GuestPtr = input4_ptr_ptr.read_ptr_from_guest().map_err(|e| { eprintln!("input4_ptr_ptr error: {}", e); types::Errno::InvalidArg @@ -96,12 +97,12 @@ impl foo::Foo for WasiCtx { // it must implement GuestErrorType with type Context = WasiCtx. // The context type should let you do logging or debugging or whatever you need // with these errors. We just push them to vecs. -impl ::memory::GuestErrorType for types::Errno { +impl GuestErrorType for types::Errno { type Context = WasiCtx; fn success() -> types::Errno { types::Errno::Ok } - fn from_error(e: ::memory::GuestError, ctx: &mut WasiCtx) -> types::Errno { + fn from_error(e: GuestError, ctx: &mut WasiCtx) -> types::Errno { eprintln!("GUEST ERROR: {:?}", e); ctx.guest_errors.push(e); types::Errno::InvalidArg @@ -200,8 +201,7 @@ impl BatExercise { pub fn test(&self) { let mut ctx = WasiCtx::new(); let mut host_memory = HostMemory::new(); - let mut guest_memory = - memory::GuestMemory::new(host_memory.as_mut_ptr(), host_memory.len() as u32); + let mut guest_memory = GuestMemory::new(host_memory.as_mut_ptr(), host_memory.len() as u32); let bat_err = foo::bat( &mut ctx, @@ -300,8 +300,7 @@ impl BazExercise { pub fn test(&self) { let mut ctx = WasiCtx::new(); let mut host_memory = HostMemory::new(); - let mut guest_memory = - memory::GuestMemory::new(host_memory.as_mut_ptr(), host_memory.len() as u32); + let mut guest_memory = GuestMemory::new(host_memory.as_mut_ptr(), host_memory.len() as u32); *guest_memory .ptr_mut(self.input2_loc.ptr) @@ -399,8 +398,7 @@ impl SumOfPairExercise { pub fn test(&self) { let mut ctx = WasiCtx::new(); let mut host_memory = HostMemory::new(); - let mut guest_memory = - memory::GuestMemory::new(host_memory.as_mut_ptr(), host_memory.len() as u32); + let mut guest_memory = GuestMemory::new(host_memory.as_mut_ptr(), host_memory.len() as u32); *guest_memory .ptr_mut(self.input_loc.ptr) @@ -492,8 +490,7 @@ impl SumPairPtrsExercise { pub fn test(&self) { let mut ctx = WasiCtx::new(); let mut host_memory = HostMemory::new(); - let mut guest_memory = - memory::GuestMemory::new(host_memory.as_mut_ptr(), host_memory.len() as u32); + let mut guest_memory = GuestMemory::new(host_memory.as_mut_ptr(), host_memory.len() as u32); *guest_memory .ptr_mut(self.input_first_loc.ptr) From 2ad77538f56d33ea409520e8fd3eb36f1dc8b85d Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Wed, 19 Feb 2020 10:58:55 +0100 Subject: [PATCH 41/86] Add array generation to wiggle-generate crate (#9) * Add array generation to wiggle-generate crate This commit: * adds array generation to `wiggle-generate` crate which implies that we can now generate arrays from `witx` files * introduces two test interface functions `foo::reduce_excuses` and `foo::populate_excuses`, and adds matching prop-tests * adds an out-of-boundary check to `HostMemory::mem_area_strat` since now, given we're generating arrays for testing with an arbitrary but bounded number of elements, it is possible to violate the boundary * refactors `Region::extend` to a new signature `extend(times: u32)` which multiplies the current pointer `len` by `times` * fixes bug in `GuestArray::as_ref` and `GuestArrayMut::as_ref_mut` methods where we were not validating the first element (we always started the validation from the second element) * Fix generation of arrays in witx This commit fixes how `arrays` are auto-generated from `witx` files. In particular, the changes include: * Since by design every `array` in `witx` represents an immutable slab of memory, we will only ever operate on `GuestArray` in which case I've gone ahead and removed `GuestArrayMut` so as to unclutter the code somewhat. If we find ourselves in need for it in the future, I reckon it will be better to write it from scratch (since the codebase will inevitably evolve by then considerably) rather than maintaining an unused implementation. * I've rewritten `GuestArrayRef` to be a wrapper for `Vec>`. Also, `GuestArray::as_ref` now borrows each underlying "element" of the array one-by-one rather than borrowing the entire chunk of memory at once. This change is motivated by the inability to coerce type parameter `T` in `GuestArray` in more complicated cases such as arrays of guest pointers `GuestPtr` to `*const T` for reuse in `std::slice::from_raw_parts` call. (In general, the size of Wasm32 pointer is 4 bytes, while ``` std::mem::size_of::() == std::mem::size_of::>() == 16 ``` which is problematic; i.e., I can't see how I could properly extract guest pointers from slices of 4 bytes and at the same time not allocate.) * I've augmented fuzz tests by (re-)defining two `array` types: ``` (typename $const_excuse_array (array (@witx const_pointer $excuse))) (typename $excuse_array (array (@witx pointer $excuse))) ``` This should hopefully emulate and test the `iovec` and `ciovec` arrays present in WASI spec. --- crates/generate/src/funcs.rs | 32 ++- crates/generate/src/module_trait.rs | 2 +- crates/generate/src/types.rs | 10 +- crates/runtime/src/lib.rs | 2 +- crates/runtime/src/memory/array.rs | 313 ++++++++-------------------- crates/runtime/src/memory/ptr.rs | 21 +- crates/runtime/src/region.rs | 5 +- tests/main.rs | 249 +++++++++++++++++++++- tests/test.witx | 12 ++ 9 files changed, 395 insertions(+), 251 deletions(-) diff --git a/crates/generate/src/funcs.rs b/crates/generate/src/funcs.rs index 7c8ef19b90..08d8bda414 100644 --- a/crates/generate/src/funcs.rs +++ b/crates/generate/src/funcs.rs @@ -82,7 +82,7 @@ pub fn define_func(names: &Names, func: &witx::InterfaceFunc) -> TokenStream { match param.tref.type_().passed_by() { witx::TypePassedBy::Value { .. } => quote!(#name), witx::TypePassedBy::Pointer { .. } => quote!(&#name), - witx::TypePassedBy::PointerLengthPair { .. } => unimplemented!(), + witx::TypePassedBy::PointerLengthPair { .. } => quote!(&#name), } }); @@ -242,6 +242,36 @@ fn marshal_arg( }; } } + witx::Type::Array(arr) => { + let pointee_type = names.type_ref(arr, anon_lifetime()); + let ptr_name = names.func_ptr_binding(¶m.name); + let len_name = names.func_len_binding(¶m.name); + let name = names.func_param(¶m.name); + quote! { + let num_elems = match memory.ptr::(#len_name as u32) { + Ok(p) => match p.as_ref() { + Ok(r) => r, + Err(e) => { + #error_handling + } + } + Err(e) => { + #error_handling + } + }; + let #name = match memory.ptr::<#pointee_type>(#ptr_name as u32) { + Ok(p) => match p.array(*num_elems) { + Ok(s) => s, + Err(e) => { + #error_handling + } + } + Err(e) => { + #error_handling + } + }; + } + } _ => unimplemented!("argument type marshalling"), } } diff --git a/crates/generate/src/module_trait.rs b/crates/generate/src/module_trait.rs index c92fe85ffc..100144dca5 100644 --- a/crates/generate/src/module_trait.rs +++ b/crates/generate/src/module_trait.rs @@ -15,7 +15,7 @@ pub fn define_module_trait(names: &Names, m: &Module) -> TokenStream { let arg_type = match arg.tref.type_().passed_by() { witx::TypePassedBy::Value { .. } => quote!(#arg_typename), witx::TypePassedBy::Pointer { .. } => quote!(&#arg_typename), - witx::TypePassedBy::PointerLengthPair { .. } => unimplemented!(), + witx::TypePassedBy::PointerLengthPair { .. } => quote!(&#arg_typename), }; quote!(#arg_name: #arg_type) }); diff --git a/crates/generate/src/types.rs b/crates/generate/src/types.rs index 10e34894db..7aa728f050 100644 --- a/crates/generate/src/types.rs +++ b/crates/generate/src/types.rs @@ -30,7 +30,7 @@ pub fn define_datatype(names: &Names, namedtype: &witx::NamedType) -> TokenStrea witx::Type::ConstPointer(p) => { define_witx_pointer(names, &namedtype.name, quote!(wiggle_runtime::GuestPtr), p) } - witx::Type::Array { .. } => unimplemented!("array types"), + witx::Type::Array(arr) => define_witx_array(names, &namedtype.name, &arr), }, } } @@ -156,7 +156,7 @@ pub fn type_needs_lifetime(tref: &witx::TypeRef) -> bool { witx::Type::Struct(s) => !struct_is_copy(&s), witx::Type::Union { .. } => true, witx::Type::Pointer { .. } | witx::Type::ConstPointer { .. } => true, - witx::Type::Array { .. } => unimplemented!(), + witx::Type::Array { .. } => true, } } @@ -380,6 +380,12 @@ fn define_witx_pointer( quote!(pub type #ident<'a> = #pointer_type<'a, #pointee_type>;) } +fn define_witx_array(names: &Names, name: &witx::Id, arr_raw: &witx::TypeRef) -> TokenStream { + let ident = names.type_(name); + let pointee_type = names.type_ref(arr_raw, quote!('a)); + quote!(pub type #ident<'a> = wiggle_runtime::GuestArray<'a, #pointee_type>;) +} + fn int_repr_tokens(int_repr: witx::IntRepr) -> TokenStream { match int_repr { witx::IntRepr::U8 => quote!(u8), diff --git a/crates/runtime/src/lib.rs b/crates/runtime/src/lib.rs index 6f2b17ecfc..5c3cea5e70 100644 --- a/crates/runtime/src/lib.rs +++ b/crates/runtime/src/lib.rs @@ -6,5 +6,5 @@ mod region; pub use error::GuestError; pub use guest_type::{GuestErrorType, GuestType, GuestTypeClone, GuestTypeCopy, GuestTypePtr}; -pub use memory::{GuestMemory, GuestPtr, GuestPtrMut, GuestRef, GuestRefMut}; +pub use memory::{GuestArray, GuestMemory, GuestPtr, GuestPtrMut, GuestRef, GuestRefMut}; pub use region::Region; diff --git a/crates/runtime/src/memory/array.rs b/crates/runtime/src/memory/array.rs index 2ba08587b7..2250497a3e 100644 --- a/crates/runtime/src/memory/array.rs +++ b/crates/runtime/src/memory/array.rs @@ -1,9 +1,6 @@ -use super::ptr::{GuestPtr, GuestPtrMut, GuestRef, GuestRefMut}; -use crate::{GuestError, GuestType, GuestTypeCopy}; -use std::{ - fmt, - ops::{Deref, DerefMut}, -}; +use super::ptr::{GuestPtr, GuestRef}; +use crate::{GuestError, GuestType}; +use std::{fmt, ops::Deref}; pub struct GuestArray<'a, T> where @@ -28,31 +25,28 @@ where impl<'a, T> GuestArray<'a, T> where - T: GuestTypeCopy, + T: GuestType, { pub fn as_ref(&self) -> Result, GuestError> { - let mut ptr = self.ptr.clone(); + let mut refs = Vec::new(); + let mut next = self.ptr.elem(0)?; for _ in 0..self.num_elems { - ptr = ptr.elem(1)?; - T::validate(&ptr)?; + T::validate(&next)?; + let handle = { + let mut borrows = next.mem.borrows.borrow_mut(); + borrows + .borrow_immut(next.region) + .ok_or_else(|| GuestError::PtrBorrowed(next.region))? + }; + refs.push(GuestRef { + mem: next.mem, + region: next.region, + handle, + type_: next.type_, + }); + next = next.elem(1)?; } - let region = self.ptr.region.extend((self.num_elems - 1) * T::size()); - let handle = { - let mut borrows = self.ptr.mem.borrows.borrow_mut(); - borrows - .borrow_immut(region) - .ok_or_else(|| GuestError::PtrBorrowed(region))? - }; - let ref_ = GuestRef { - mem: self.ptr.mem, - region, - handle, - type_: self.ptr.type_, - }; - Ok(GuestArrayRef { - ref_, - num_elems: self.num_elems, - }) + Ok(GuestArrayRef { refs }) } } @@ -60,8 +54,7 @@ pub struct GuestArrayRef<'a, T> where T: GuestType, { - ref_: GuestRef<'a, T>, - num_elems: u32, + refs: Vec>, } impl<'a, T> fmt::Debug for GuestArrayRef<'a, T> @@ -69,137 +62,18 @@ where T: GuestType + fmt::Debug, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "GuestArrayRef {{ ref_: {:?}, num_elems: {:?} }}", - self.ref_, self.num_elems - ) + write!(f, "GuestArrayRef {{ refs: {:?} }}", self.refs) } } impl<'a, T> Deref for GuestArrayRef<'a, T> -where - T: GuestTypeCopy, -{ - type Target = [T]; - - fn deref(&self) -> &Self::Target { - unsafe { - std::slice::from_raw_parts( - self.ref_.as_ptr().as_raw() as *const T, - self.num_elems as usize, - ) - } - } -} - -pub struct GuestArrayMut<'a, T> where T: GuestType, { - pub(super) ptr: GuestPtrMut<'a, T>, - pub(super) num_elems: u32, -} - -impl<'a, T> fmt::Debug for GuestArrayMut<'a, T> -where - T: GuestType + fmt::Debug, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "GuestArrayMut {{ ptr: {:?}, num_elems: {:?} }}", - self.ptr, self.num_elems - ) - } -} - -impl<'a, T> GuestArrayMut<'a, T> -where - T: GuestTypeCopy, -{ - pub fn as_ref(&self) -> Result, GuestError> { - let arr = GuestArray { - ptr: self.ptr.as_immut(), - num_elems: self.num_elems, - }; - arr.as_ref() - } - - pub fn as_ref_mut(&self) -> Result, GuestError> { - let mut ptr = self.ptr.as_immut(); - for _ in 0..self.num_elems { - ptr = ptr.elem(1)?; - T::validate(&ptr)?; - } - let region = self.ptr.region.extend((self.num_elems - 1) * T::size()); - let handle = { - let mut borrows = self.ptr.mem.borrows.borrow_mut(); - borrows - .borrow_mut(region) - .ok_or_else(|| GuestError::PtrBorrowed(region))? - }; - let ref_mut = GuestRefMut { - mem: self.ptr.mem, - region, - handle, - type_: self.ptr.type_, - }; - Ok(GuestArrayRefMut { - ref_mut, - num_elems: self.num_elems, - }) - } -} - -pub struct GuestArrayRefMut<'a, T> -where - T: GuestType, -{ - ref_mut: GuestRefMut<'a, T>, - num_elems: u32, -} - -impl<'a, T> fmt::Debug for GuestArrayRefMut<'a, T> -where - T: GuestType + fmt::Debug, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "GuestArrayRefMut {{ ref_mut: {:?}, num_elems: {:?} }}", - self.ref_mut, self.num_elems - ) - } -} - -impl<'a, T> Deref for GuestArrayRefMut<'a, T> -where - T: GuestTypeCopy, -{ - type Target = [T]; + type Target = [GuestRef<'a, T>]; fn deref(&self) -> &Self::Target { - unsafe { - std::slice::from_raw_parts( - self.ref_mut.as_ptr().as_raw() as *const T, - self.num_elems as usize, - ) - } - } -} - -impl<'a, T> DerefMut for GuestArrayRefMut<'a, T> -where - T: GuestTypeCopy, -{ - fn deref_mut(&mut self) -> &mut Self::Target { - unsafe { - std::slice::from_raw_parts_mut( - self.ref_mut.as_ptr_mut().as_raw() as *mut T, - self.num_elems as usize, - ) - } + self.refs.as_slice() } } @@ -231,14 +105,10 @@ mod test { fn out_of_bounds() { let mut host_memory = HostMemory::new(); let guest_memory = GuestMemory::new(host_memory.as_mut_ptr(), host_memory.len() as u32); - // try extracting an immutable array out of memory bounds + // try extracting an array out of memory bounds let ptr: GuestPtr = guest_memory.ptr(4092).expect("ptr to last i32 el"); let err = ptr.array(2).expect_err("out of bounds ptr error"); assert_eq!(err, GuestError::PtrOutOfBounds(Region::new(4092, 8))); - // try extracting an mutable array out of memory bounds - let ptr: GuestPtrMut = guest_memory.ptr_mut(4092).expect("ptr mut to last i32 el"); - let err = ptr.array_mut(2).expect_err("out of bounds ptr error"); - assert_eq!(err, GuestError::PtrOutOfBounds(Region::new(4092, 8))); } #[test] @@ -260,76 +130,77 @@ mod test { // extract as array let ptr: GuestPtr = guest_memory.ptr(0).expect("ptr to first el"); let arr = ptr.array(3).expect("convert ptr to array"); - let as_ref = arr.as_ref().expect("array borrowed immutably"); - assert_eq!(&*as_ref, &[1, 2, 3]); - // borrowing again should be fine - let as_ref2 = arr.as_ref().expect("array borrowed immutably again"); - assert_eq!(&*as_ref2, &*as_ref); - } - - #[test] - fn ptr_mut_to_array_mut() { - let mut host_memory = HostMemory::new(); - let guest_memory = GuestMemory::new(host_memory.as_mut_ptr(), host_memory.len() as u32); - // set elems of array to zero - { - let ptr: GuestPtrMut = guest_memory.ptr_mut(0).expect("ptr mut to first el"); - let mut el = ptr.as_ref_mut().expect("ref mut to first el"); - *el = 0; - let ptr: GuestPtrMut = guest_memory.ptr_mut(4).expect("ptr mut to second el"); - let mut el = ptr.as_ref_mut().expect("ref mu to second el"); - *el = 0; - let ptr: GuestPtrMut = guest_memory.ptr_mut(8).expect("ptr mut to third el"); - let mut el = ptr.as_ref_mut().expect("ref mut to third el"); - *el = 0; - } - // extract as array and verify all is zero - let ptr: GuestPtrMut = guest_memory.ptr_mut(0).expect("ptr mut to first el"); - let arr = ptr.array_mut(3).expect("convert ptr mut to array mut"); - assert_eq!(&*arr.as_ref().expect("array borrowed immutably"), &[0; 3]); - // populate the array and re-verify - for el in &mut *arr.as_ref_mut().expect("array borrowed mutably") { - *el = 10; - } - // re-validate - assert_eq!(&*arr.as_ref().expect("array borrowed immutably"), &[10; 3]); - } - - #[test] - #[should_panic( - expected = "array borrowed immutably while borrowed mutably: PtrBorrowed(Region { start: 0, len: 12 })" - )] - fn borrow_mut_then_immut() { - let mut host_memory = HostMemory::new(); - let guest_memory = GuestMemory::new(host_memory.as_mut_ptr(), host_memory.len() as u32); - let ptr: GuestPtrMut = guest_memory.ptr_mut(0).expect("ptr mut to first el"); - let arr = ptr.array_mut(3).expect("convert ptr mut to array mut"); - // borrow mutably - let _as_mut = arr - .as_ref_mut() - .expect("array borrowed mutably for the first time"); - // borrow immutably should fail - let _as_ref = arr + let as_ref = arr .as_ref() - .expect("array borrowed immutably while borrowed mutably"); + .expect("array borrowed immutably") + .iter() + .map(|x| **x) + .collect::>(); + assert_eq!(&as_ref, &[1, 2, 3]); + // borrowing again should be fine + let as_ref2 = arr + .as_ref() + .expect("array borrowed immutably again") + .iter() + .map(|x| **x) + .collect::>(); + assert_eq!(&as_ref2, &as_ref); } #[test] - #[should_panic( - expected = "array borrowed mutably while borrowed mutably: PtrBorrowed(Region { start: 0, len: 12 })" - )] - fn borrow_mut_twice() { + fn ptr_to_ptr_array() { let mut host_memory = HostMemory::new(); let guest_memory = GuestMemory::new(host_memory.as_mut_ptr(), host_memory.len() as u32); - let ptr: GuestPtrMut = guest_memory.ptr_mut(0).expect("ptr mut to first el"); - let arr = ptr.array_mut(3).expect("convert ptr mut to array mut"); - // borrow mutably - let _as_mut = arr - .as_ref_mut() - .expect("array borrowed mutably for the first time"); - // try borrowing mutably again - let _as_mut2 = arr - .as_ref_mut() - .expect("array borrowed mutably while borrowed mutably"); + { + let val_ptr: GuestPtrMut = + guest_memory.ptr_mut(0).expect("ptr mut to the first value"); + let mut val = val_ptr.as_ref_mut().expect("ref mut to the first value"); + *val = 255; + let val_ptr: GuestPtrMut = guest_memory + .ptr_mut(4) + .expect("ptr mut to the second value"); + let mut val = val_ptr.as_ref_mut().expect("ref mut to the second value"); + *val = 254; + let val_ptr: GuestPtrMut = + guest_memory.ptr_mut(8).expect("ptr mut to the third value"); + let mut val = val_ptr.as_ref_mut().expect("ref mut to the third value"); + *val = 253; + } + { + let ptr = guest_memory.ptr_mut(12).expect("ptr mut to first el"); + ptr.write_ptr_to_guest( + &guest_memory + .ptr::>(0) + .expect("ptr to the first value"), + ); + let ptr = guest_memory.ptr_mut(16).expect("ptr mut to first el"); + ptr.write_ptr_to_guest( + &guest_memory + .ptr::>(4) + .expect("ptr to the second value"), + ); + let ptr = guest_memory.ptr_mut(20).expect("ptr mut to first el"); + ptr.write_ptr_to_guest( + &guest_memory + .ptr::>(8) + .expect("ptr to the third value"), + ); + } + // extract as array + let ptr: GuestPtr> = guest_memory.ptr(12).expect("ptr to first el"); + let arr = ptr.array(3).expect("convert ptr to array"); + let as_ref = arr + .as_ref() + .expect("array borrowed immutably") + .iter() + .map(|x| { + *x.as_ptr() + .read_ptr_from_guest() + .expect("valid ptr to some value") + .as_ref() + .expect("deref ptr to some value") + }) + .collect::>(); + assert_eq!(&as_ref, &[255, 254, 253]); } } diff --git a/crates/runtime/src/memory/ptr.rs b/crates/runtime/src/memory/ptr.rs index e9f64ba715..da1fd3e4bc 100644 --- a/crates/runtime/src/memory/ptr.rs +++ b/crates/runtime/src/memory/ptr.rs @@ -1,7 +1,4 @@ -use super::{ - array::{GuestArray, GuestArrayMut}, - GuestMemory, -}; +use super::{array::GuestArray, GuestMemory}; use crate::{ borrow::BorrowHandle, GuestError, GuestType, GuestTypeClone, GuestTypeCopy, GuestTypePtr, Region, @@ -47,7 +44,7 @@ impl<'a, T: GuestType> GuestPtr<'a, T> { } pub fn array(&self, num_elems: u32) -> Result, GuestError> { - let region = self.region.extend((num_elems - 1) * T::size()); + let region = self.region.extend(num_elems); if self.mem.contains(region) { let ptr = GuestPtr { mem: self.mem, @@ -194,20 +191,6 @@ where ) -> Result, GuestError> { self.mem.ptr_mut(self.region.start + offset) } - - pub fn array_mut(&self, num_elems: u32) -> Result, GuestError> { - let region = self.region.extend((num_elems - 1) * T::size()); - if self.mem.contains(region) { - let ptr = GuestPtrMut { - mem: self.mem, - region: self.region, - type_: self.type_, - }; - Ok(GuestArrayMut { ptr, num_elems }) - } else { - Err(GuestError::PtrOutOfBounds(region)) - } - } } impl<'a, T> GuestPtrMut<'a, T> diff --git a/crates/runtime/src/region.rs b/crates/runtime/src/region.rs index fbbe752c85..e1e6084dc0 100644 --- a/crates/runtime/src/region.rs +++ b/crates/runtime/src/region.rs @@ -26,10 +26,11 @@ impl Region { } } - pub fn extend(&self, new_len: u32) -> Self { + pub fn extend(&self, times: u32) -> Self { + let len = self.len * times; Self { start: self.start, - len: self.len + new_len, + len, } } } diff --git a/tests/main.rs b/tests/main.rs index 89bfe21f52..a84be06951 100644 --- a/tests/main.rs +++ b/tests/main.rs @@ -92,6 +92,39 @@ impl foo::Foo for WasiCtx { .expect("dereferncing GuestPtr should succeed"); Ok(first as i64 + second as i64) } + + fn reduce_excuses( + &mut self, + excuses: &types::ConstExcuseArray, + ) -> Result { + let excuses = &*excuses + .as_ref() + .expect("dereferencing GuestArray should succeed"); + let last = excuses + .last() + .expect("input array is non-empty") + .as_ptr() + .read_ptr_from_guest() + .expect("valid ptr to some Excuse value"); + Ok(*last.as_ref().expect("dereferencing ptr should succeed")) + } + + fn populate_excuses(&mut self, excuses: &types::ExcuseArray) -> Result<(), types::Errno> { + let excuses = &*excuses + .as_ref() + .expect("dereferencing GuestArray should succeed"); + for excuse in excuses { + let ptr_to_ptr = excuse + .as_ptr() + .read_ptr_from_guest() + .expect("valid ptr to some Excuse value"); + let mut ptr = ptr_to_ptr + .as_ref_mut() + .expect("dereferencing mut ptr should succeed"); + *ptr = types::Excuse::Sleeping; + } + Ok(()) + } } // Errno is used as a first return value in the functions above, therefore // it must implement GuestErrorType with type Context = WasiCtx. @@ -125,10 +158,14 @@ impl HostMemory { } pub fn mem_area_strat(align: u32) -> BoxedStrategy { prop::num::u32::ANY - .prop_map(move |p| { + .prop_filter_map("needs to fit in memory", move |p| { let p_aligned = p - (p % align); // Align according to argument let ptr = p_aligned % 4096; // Put inside memory - MemArea { ptr, len: align } + if ptr + align < 4096 { + Some(MemArea { ptr, len: align }) + } else { + None + } }) .boxed() } @@ -148,12 +185,12 @@ fn overlapping(a: &MemArea, b: &MemArea) -> bool { // a_range is all elems in A let a_range = std::ops::Range { start: a.ptr, - end: a.ptr + a.len - 1, + end: a.ptr + a.len, // std::ops::Range is open from the right }; // b_range is all elems in B let b_range = std::ops::Range { start: b.ptr, - end: b.ptr + b.len - 1, + end: b.ptr + b.len, }; // No element in B is contained in A: for b_elem in b_range.clone() { @@ -542,3 +579,207 @@ proptest! { e.test() } } + +#[derive(Debug)] +struct ReduceExcusesExcercise { + excuse_values: Vec, + excuse_ptr_locs: Vec, + array_ptr_loc: MemArea, + array_len_loc: MemArea, + return_ptr_loc: MemArea, +} + +impl ReduceExcusesExcercise { + pub fn strat() -> BoxedStrategy { + (1..256u32) + .prop_flat_map(|len| { + let len_usize = len as usize; + ( + proptest::collection::vec(excuse_strat(), len_usize..=len_usize), + proptest::collection::vec(HostMemory::mem_area_strat(4), len_usize..=len_usize), + HostMemory::mem_area_strat(4 * len), + HostMemory::mem_area_strat(4), + HostMemory::mem_area_strat(4), + ) + }) + .prop_map( + |(excuse_values, excuse_ptr_locs, array_ptr_loc, array_len_loc, return_ptr_loc)| { + Self { + excuse_values, + excuse_ptr_locs, + array_ptr_loc, + array_len_loc, + return_ptr_loc, + } + }, + ) + .prop_filter("non-overlapping pointers", |e| { + let mut all = vec![&e.array_ptr_loc, &e.array_len_loc, &e.return_ptr_loc]; + all.extend(e.excuse_ptr_locs.iter()); + non_overlapping_set(&all) + }) + .boxed() + } + + pub fn test(&self) { + let mut ctx = WasiCtx::new(); + let mut host_memory = HostMemory::new(); + let mut guest_memory = GuestMemory::new(host_memory.as_mut_ptr(), host_memory.len() as u32); + + // Populate memory with pointers to generated Excuse values + for (&excuse, ptr) in self.excuse_values.iter().zip(self.excuse_ptr_locs.iter()) { + *guest_memory + .ptr_mut(ptr.ptr) + .expect("ptr mut to Excuse value") + .as_ref_mut() + .expect("deref ptr mut to Excuse value") = excuse; + } + + // Populate array length info + *guest_memory + .ptr_mut(self.array_len_loc.ptr) + .expect("ptr to array len") + .as_ref_mut() + .expect("deref ptr mut to array len") = self.excuse_ptr_locs.len() as u32; + + // Populate the array with pointers to generated Excuse values + { + let mut next: GuestPtrMut<'_, GuestPtr> = guest_memory + .ptr_mut(self.array_ptr_loc.ptr) + .expect("ptr to array mut"); + for ptr in &self.excuse_ptr_locs { + next.write_ptr_to_guest( + &guest_memory + .ptr::(ptr.ptr) + .expect("ptr to Excuse value"), + ); + next = next.elem(1).expect("increment ptr by 1"); + } + } + + let res = foo::reduce_excuses( + &mut ctx, + &mut guest_memory, + self.array_ptr_loc.ptr as i32, + self.array_len_loc.ptr as i32, + self.return_ptr_loc.ptr as i32, + ); + + assert_eq!(res, types::Errno::Ok.into(), "reduce excuses errno"); + + let expected = *self + .excuse_values + .last() + .expect("generated vec of excuses should be non-empty"); + let given: types::Excuse = *guest_memory + .ptr(self.return_ptr_loc.ptr) + .expect("ptr to returned value") + .as_ref() + .expect("deref ptr to returned value"); + assert_eq!(expected, given, "reduce excuses return val"); + } +} +proptest! { + #[test] + fn reduce_excuses(e in ReduceExcusesExcercise::strat()) { + e.test() + } +} + +#[derive(Debug)] +struct PopulateExcusesExcercise { + array_ptr_loc: MemArea, + array_len_loc: MemArea, + elements: Vec, +} + +impl PopulateExcusesExcercise { + pub fn strat() -> BoxedStrategy { + (1..256u32) + .prop_flat_map(|len| { + let len_usize = len as usize; + ( + HostMemory::mem_area_strat(4 * len), + HostMemory::mem_area_strat(4), + proptest::collection::vec(HostMemory::mem_area_strat(4), len_usize..=len_usize), + ) + }) + .prop_map(|(array_ptr_loc, array_len_loc, elements)| Self { + array_ptr_loc, + array_len_loc, + elements, + }) + .prop_filter("non-overlapping pointers", |e| { + let mut all = vec![&e.array_ptr_loc, &e.array_len_loc]; + all.extend(e.elements.iter()); + non_overlapping_set(&all) + }) + .boxed() + } + + pub fn test(&self) { + let mut ctx = WasiCtx::new(); + let mut host_memory = HostMemory::new(); + let mut guest_memory = GuestMemory::new(host_memory.as_mut_ptr(), host_memory.len() as u32); + + // Populate array length info + *guest_memory + .ptr_mut(self.array_len_loc.ptr) + .expect("ptr mut to array len") + .as_ref_mut() + .expect("deref ptr mut to array len") = self.elements.len() as u32; + + // Populate array with valid pointers to Excuse type in memory + { + let mut next: GuestPtrMut<'_, GuestPtrMut> = guest_memory + .ptr_mut(self.array_ptr_loc.ptr) + .expect("ptr mut to the first element of array"); + for ptr in &self.elements { + next.write_ptr_to_guest( + &guest_memory + .ptr_mut::(ptr.ptr) + .expect("ptr mut to Excuse value"), + ); + next = next.elem(1).expect("increment ptr by 1"); + } + } + + let res = foo::populate_excuses( + &mut ctx, + &mut guest_memory, + self.array_ptr_loc.ptr as i32, + self.array_len_loc.ptr as i32, + ); + assert_eq!(res, types::Errno::Ok.into(), "populate excuses errno"); + + let arr = { + let ptr: GuestPtr<'_, GuestPtr<'_, types::Excuse>> = guest_memory + .ptr(self.array_ptr_loc.ptr) + .expect("ptr to the first element of array"); + &*ptr + .array(self.elements.len() as u32) + .expect("as array") + .as_ref() + .expect("deref to &[]") + }; + for el in arr { + let ptr_to_ptr = el + .as_ptr() + .read_ptr_from_guest() + .expect("valid ptr to some Excuse value"); + assert_eq!( + *ptr_to_ptr + .as_ref() + .expect("dereferencing ptr to some Excuse value"), + types::Excuse::Sleeping, + "element should equal Excuse::Sleeping" + ); + } + } +} +proptest! { + #[test] + fn populate_excuses(e in PopulateExcusesExcercise::strat()) { + e.test() + } +} diff --git a/tests/test.witx b/tests/test.witx index 40e2aab7ed..e5b0c6bbf1 100644 --- a/tests/test.witx +++ b/tests/test.witx @@ -25,6 +25,9 @@ (typename $named_ptr (@witx pointer f32)) (typename $named_ptr_to_ptr (@witx pointer (@witx pointer f64))) +(typename $const_excuse_array (array (@witx const_pointer $excuse))) +(typename $excuse_array (array (@witx pointer $excuse))) + (module $foo (@interface func (export "bar") (param $an_int u32) @@ -48,4 +51,13 @@ (param $an_pair $pair_int_ptrs) (result $error $errno) (result $doubled s64)) + (@interface func (export "reduce_excuses") + (param $excuses $const_excuse_array) + (result $error $errno) + (result $reduced $excuse) + ) + (@interface func (export "populate_excuses") + (param $excuses $excuse_array) + (result $error $errno) + ) ) From 898af8e2fb2774b5fc5e54cbc02369a483535e24 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 20 Feb 2020 14:51:22 +0100 Subject: [PATCH 42/86] Revisit GuestArray and their deref mechanics In preparation for landing `string` support in `wiggle`, I've revisited `GuestArray` and the deref mechanics, and decided to scrap the current approach in favour of the previous. Namely, for `T: GuestTypeCopy` we provide `GuestArray::as_ref` which then can be derefed directly to `&[T]` since the guest and host types have matching representation, and for other types (thinking here of our infamous arrays of pointers), I've instead added a way to iterate over the pointers. Then, it is up to the `wiggle`'s client to know whether they can deref whatever the pointer stored in array is pointing at. --- crates/runtime/src/memory/array.rs | 139 ++++++++++++++++++++--------- tests/main.rs | 35 +++----- 2 files changed, 110 insertions(+), 64 deletions(-) diff --git a/crates/runtime/src/memory/array.rs b/crates/runtime/src/memory/array.rs index 2250497a3e..40a5e37d82 100644 --- a/crates/runtime/src/memory/array.rs +++ b/crates/runtime/src/memory/array.rs @@ -1,5 +1,5 @@ use super::ptr::{GuestPtr, GuestRef}; -use crate::{GuestError, GuestType}; +use crate::{GuestError, GuestType, GuestTypeCopy}; use std::{fmt, ops::Deref}; pub struct GuestArray<'a, T> @@ -26,54 +26,120 @@ where impl<'a, T> GuestArray<'a, T> where T: GuestType, +{ + pub fn iter(&self) -> GuestArrayIter<'a, T> { + let next = GuestPtr { + mem: self.ptr.mem, + region: self.ptr.region, + type_: self.ptr.type_, + }; + GuestArrayIter { + next, + num_elems: self.num_elems, + count: 0, + } + } +} + +pub struct GuestArrayIter<'a, T> +where + T: GuestType, +{ + next: GuestPtr<'a, T>, + num_elems: u32, + count: u32, +} + +impl<'a, T> Iterator for GuestArrayIter<'a, T> +where + T: GuestType, +{ + type Item = Result, GuestError>; + + fn next(&mut self) -> Option { + if self.count < self.num_elems { + // ok... + Some(T::validate(&self.next).and_then(|()| { + let curr = GuestPtr { + mem: self.next.mem, + region: self.next.region, + type_: self.next.type_, + }; + self.next = self.next.elem(1)?; + self.count += 1; + Ok(curr) + })) + } else { + // no more elements... + None + } + } +} + +impl<'a, T> GuestArray<'a, T> +where + T: GuestTypeCopy, { pub fn as_ref(&self) -> Result, GuestError> { - let mut refs = Vec::new(); let mut next = self.ptr.elem(0)?; for _ in 0..self.num_elems { T::validate(&next)?; - let handle = { - let mut borrows = next.mem.borrows.borrow_mut(); - borrows - .borrow_immut(next.region) - .ok_or_else(|| GuestError::PtrBorrowed(next.region))? - }; - refs.push(GuestRef { - mem: next.mem, - region: next.region, - handle, - type_: next.type_, - }); next = next.elem(1)?; } - Ok(GuestArrayRef { refs }) + let region = self.ptr.region.extend(self.num_elems); + let handle = { + let mut borrows = self.ptr.mem.borrows.borrow_mut(); + borrows + .borrow_immut(region) + .ok_or_else(|| GuestError::PtrBorrowed(region))? + }; + let ref_ = GuestRef { + mem: self.ptr.mem, + region, + handle, + type_: self.ptr.type_, + }; + Ok(GuestArrayRef { + ref_, + num_elems: self.num_elems, + }) } } pub struct GuestArrayRef<'a, T> where - T: GuestType, + T: GuestTypeCopy, { - refs: Vec>, + ref_: GuestRef<'a, T>, + num_elems: u32, } impl<'a, T> fmt::Debug for GuestArrayRef<'a, T> where - T: GuestType + fmt::Debug, + T: GuestTypeCopy + fmt::Debug, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "GuestArrayRef {{ refs: {:?} }}", self.refs) + write!( + f, + "GuestArrayRef {{ ref_: {:?}, num_elems: {:?} }}", + self.ref_, self.num_elems + ) } } impl<'a, T> Deref for GuestArrayRef<'a, T> where - T: GuestType, + T: GuestTypeCopy, { - type Target = [GuestRef<'a, T>]; + type Target = [T]; fn deref(&self) -> &Self::Target { - self.refs.as_slice() + unsafe { + std::slice::from_raw_parts( + self.ref_.as_ptr().as_raw() as *const _, + self.num_elems as usize, + ) + } } } @@ -130,21 +196,11 @@ mod test { // extract as array let ptr: GuestPtr = guest_memory.ptr(0).expect("ptr to first el"); let arr = ptr.array(3).expect("convert ptr to array"); - let as_ref = arr - .as_ref() - .expect("array borrowed immutably") - .iter() - .map(|x| **x) - .collect::>(); - assert_eq!(&as_ref, &[1, 2, 3]); + let as_ref = &*arr.as_ref().expect("array borrowed immutably"); + assert_eq!(as_ref, &[1, 2, 3]); // borrowing again should be fine - let as_ref2 = arr - .as_ref() - .expect("array borrowed immutably again") - .iter() - .map(|x| **x) - .collect::>(); - assert_eq!(&as_ref2, &as_ref); + let as_ref2 = &*arr.as_ref().expect("array borrowed immutably again"); + assert_eq!(as_ref2, as_ref); } #[test] @@ -189,18 +245,17 @@ mod test { // extract as array let ptr: GuestPtr> = guest_memory.ptr(12).expect("ptr to first el"); let arr = ptr.array(3).expect("convert ptr to array"); - let as_ref = arr - .as_ref() - .expect("array borrowed immutably") + let contents = arr .iter() - .map(|x| { - *x.as_ptr() + .map(|ptr_ptr| { + *ptr_ptr + .expect("valid ptr to ptr") .read_ptr_from_guest() .expect("valid ptr to some value") .as_ref() .expect("deref ptr to some value") }) .collect::>(); - assert_eq!(&as_ref, &[255, 254, 253]); + assert_eq!(&contents, &[255, 254, 253]); } } diff --git a/tests/main.rs b/tests/main.rs index a84be06951..be06a41d80 100644 --- a/tests/main.rs +++ b/tests/main.rs @@ -1,6 +1,7 @@ use proptest::prelude::*; use wiggle_runtime::{ - GuestError, GuestErrorType, GuestMemory, GuestPtr, GuestPtrMut, GuestRef, GuestRefMut, + GuestArray, GuestError, GuestErrorType, GuestMemory, GuestPtr, GuestPtrMut, GuestRef, + GuestRefMut, }; wiggle_generate::from_witx!({ @@ -97,25 +98,20 @@ impl foo::Foo for WasiCtx { &mut self, excuses: &types::ConstExcuseArray, ) -> Result { - let excuses = &*excuses - .as_ref() - .expect("dereferencing GuestArray should succeed"); let last = excuses + .iter() .last() .expect("input array is non-empty") - .as_ptr() + .expect("valid ptr to ptr") .read_ptr_from_guest() .expect("valid ptr to some Excuse value"); Ok(*last.as_ref().expect("dereferencing ptr should succeed")) } fn populate_excuses(&mut self, excuses: &types::ExcuseArray) -> Result<(), types::Errno> { - let excuses = &*excuses - .as_ref() - .expect("dereferencing GuestArray should succeed"); - for excuse in excuses { + for excuse in excuses.iter() { let ptr_to_ptr = excuse - .as_ptr() + .expect("valid ptr to ptr") .read_ptr_from_guest() .expect("valid ptr to some Excuse value"); let mut ptr = ptr_to_ptr @@ -752,19 +748,14 @@ impl PopulateExcusesExcercise { ); assert_eq!(res, types::Errno::Ok.into(), "populate excuses errno"); - let arr = { - let ptr: GuestPtr<'_, GuestPtr<'_, types::Excuse>> = guest_memory - .ptr(self.array_ptr_loc.ptr) - .expect("ptr to the first element of array"); - &*ptr - .array(self.elements.len() as u32) - .expect("as array") - .as_ref() - .expect("deref to &[]") - }; - for el in arr { + let arr: GuestArray<'_, GuestPtr<'_, types::Excuse>> = guest_memory + .ptr(self.array_ptr_loc.ptr) + .expect("ptr to the first element of array") + .array(self.elements.len() as u32) + .expect("as array"); + for el in arr.iter() { let ptr_to_ptr = el - .as_ptr() + .expect("valid ptr to ptr") .read_ptr_from_guest() .expect("valid ptr to some Excuse value"); assert_eq!( From 3ef24a04febb67a935493632a763909843c16268 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Fri, 21 Feb 2020 09:58:43 +0100 Subject: [PATCH 43/86] Start drafting out flags datatype generation (#11) * Adds support for flags datatype This commit adds support for `FlagsDatatype`. In WASI, flags are represented as bitfields/bitflags, therefore, it is important for the type to have bitwise operations implemented, and some way of checking whether the current set of flags contains some other set (i.e., they intersect). Thus, this commit automatically derives `BitAnd`, etc. for the derived flags datatype. It also automatically provides an `ALL_FLAGS` value which corresponds to a bitwise-or of all flag values present and is provided for convenience. * Simplify read_from_guest --- crates/generate/src/funcs.rs | 3 +- crates/generate/src/names.rs | 6 +- crates/generate/src/types.rs | 150 ++++++++++++++++++++++++++++++++++- crates/runtime/src/error.rs | 2 + tests/main.rs | 92 +++++++++++++++++++++ tests/test.witx | 12 +++ 6 files changed, 261 insertions(+), 4 deletions(-) diff --git a/crates/generate/src/funcs.rs b/crates/generate/src/funcs.rs index 08d8bda414..b2728db3ca 100644 --- a/crates/generate/src/funcs.rs +++ b/crates/generate/src/funcs.rs @@ -153,6 +153,7 @@ fn marshal_arg( match &*tref.type_() { witx::Type::Enum(_e) => try_into_conversion, + witx::Type::Flags(_f) => try_into_conversion, witx::Type::Builtin(b) => match b { witx::BuiltinType::U8 | witx::BuiltinType::U16 | witx::BuiltinType::Char8 => { try_into_conversion @@ -329,7 +330,7 @@ where | witx::BuiltinType::Char8 => write_val_to_ptr, witx::BuiltinType::String => unimplemented!("string types"), }, - witx::Type::Enum(_e) => write_val_to_ptr, + witx::Type::Enum(_) | witx::Type::Flags(_) => write_val_to_ptr, _ => unimplemented!("marshal result"), } } diff --git a/crates/generate/src/names.rs b/crates/generate/src/names.rs index 504f9ab67b..e673af3197 100644 --- a/crates/generate/src/names.rs +++ b/crates/generate/src/names.rs @@ -1,4 +1,4 @@ -use heck::{CamelCase, SnakeCase}; +use heck::{CamelCase, ShoutySnakeCase, SnakeCase}; use proc_macro2::{Ident, TokenStream}; use quote::{format_ident, quote}; use witx::{AtomType, BuiltinType, Id, TypeRef}; @@ -82,6 +82,10 @@ impl Names { } } + pub fn flag_member(&self, id: &Id) -> Ident { + format_ident!("{}", id.as_str().to_shouty_snake_case()) + } + pub fn struct_member(&self, id: &Id) -> Ident { format_ident!("{}", id.as_str().to_snake_case()) } diff --git a/crates/generate/src/types.rs b/crates/generate/src/types.rs index 7aa728f050..4d2376ab99 100644 --- a/crates/generate/src/types.rs +++ b/crates/generate/src/types.rs @@ -1,7 +1,8 @@ use crate::names::Names; -use proc_macro2::TokenStream; +use proc_macro2::{Literal, TokenStream}; use quote::quote; +use std::convert::TryFrom; use witx::Layout; pub fn define_datatype(names: &Names, namedtype: &witx::NamedType) -> TokenStream { @@ -10,7 +11,7 @@ pub fn define_datatype(names: &Names, namedtype: &witx::NamedType) -> TokenStrea witx::TypeRef::Value(v) => match &**v { witx::Type::Enum(e) => define_enum(names, &namedtype.name, &e), witx::Type::Int(_) => unimplemented!("int types"), - witx::Type::Flags(_) => unimplemented!("flag types"), + witx::Type::Flags(f) => define_flags(names, &namedtype.name, &f), witx::Type::Struct(s) => { if struct_is_copy(s) { define_copy_struct(names, &namedtype.name, &s) @@ -45,6 +46,151 @@ fn define_alias(names: &Names, name: &witx::Id, to: &witx::NamedType) -> TokenSt } } +fn define_flags(names: &Names, name: &witx::Id, f: &witx::FlagsDatatype) -> TokenStream { + let ident = names.type_(&name); + let repr = int_repr_tokens(f.repr); + let abi_repr = atom_token(match f.repr { + witx::IntRepr::U8 | witx::IntRepr::U16 | witx::IntRepr::U32 => witx::AtomType::I32, + witx::IntRepr::U64 => witx::AtomType::I64, + }); + + let mut flag_constructors = vec![]; + let mut all_values = 0; + for (i, f) in f.flags.iter().enumerate() { + let name = names.flag_member(&f.name); + let value = 1u128 + .checked_shl(u32::try_from(i).expect("flag value overflow")) + .expect("flag value overflow"); + let value_token = Literal::u128_unsuffixed(value); + flag_constructors.push(quote!(pub const #name: #ident = #ident(#value_token))); + all_values += value; + } + let all_values_token = Literal::u128_unsuffixed(all_values); + + quote! { + #[repr(transparent)] + #[derive(Copy, Clone, Debug, ::std::hash::Hash, Eq, PartialEq)] + pub struct #ident(#repr); + + impl #ident { + #(#flag_constructors);*; + pub const ALL_FLAGS: #ident = #ident(#all_values_token); + + pub fn contains(&self, other: &#ident) -> bool { + #repr::from(!*self & *other) == 0 as #repr + } + } + + impl ::std::ops::BitAnd for #ident { + type Output = Self; + fn bitand(self, rhs: Self) -> Self::Output { + #ident(self.0 & rhs.0) + } + } + + impl ::std::ops::BitAndAssign for #ident { + fn bitand_assign(&mut self, rhs: Self) { + *self = *self & rhs + } + } + + impl ::std::ops::BitOr for #ident { + type Output = Self; + fn bitor(self, rhs: Self) -> Self::Output { + #ident(self.0 | rhs.0) + } + } + + impl ::std::ops::BitOrAssign for #ident { + fn bitor_assign(&mut self, rhs: Self) { + *self = *self | rhs + } + } + + impl ::std::ops::BitXor for #ident { + type Output = Self; + fn bitxor(self, rhs: Self) -> Self::Output { + #ident(self.0 ^ rhs.0) + } + } + + impl ::std::ops::BitXorAssign for #ident { + fn bitxor_assign(&mut self, rhs: Self) { + *self = *self ^ rhs + } + } + + impl ::std::ops::Not for #ident { + type Output = Self; + fn not(self) -> Self::Output { + #ident(!self.0) + } + } + + impl ::std::convert::TryFrom<#repr> for #ident { + type Error = wiggle_runtime::GuestError; + fn try_from(value: #repr) -> Result { + if #repr::from(!#ident::ALL_FLAGS) & value != 0 { + Err(wiggle_runtime::GuestError::InvalidFlagValue(stringify!(#ident))) + } else { + Ok(#ident(value)) + } + } + } + + impl ::std::convert::TryFrom<#abi_repr> for #ident { + type Error = wiggle_runtime::GuestError; + fn try_from(value: #abi_repr) -> Result<#ident, wiggle_runtime::GuestError> { + #ident::try_from(value as #repr) + } + } + + impl From<#ident> for #repr { + fn from(e: #ident) -> #repr { + e.0 + } + } + + impl From<#ident> for #abi_repr { + fn from(e: #ident) -> #abi_repr { + #repr::from(e) as #abi_repr + } + } + + impl wiggle_runtime::GuestType for #ident { + fn size() -> u32 { + ::std::mem::size_of::<#repr>() as u32 + } + + fn align() -> u32 { + ::std::mem::align_of::<#repr>() as u32 + } + + fn name() -> String { + stringify!(#ident).to_owned() + } + + fn validate<'a>(location: &wiggle_runtime::GuestPtr<'a, #ident>) -> Result<(), wiggle_runtime::GuestError> { + use ::std::convert::TryFrom; + let raw: #repr = unsafe { (location.as_raw() as *const #repr).read() }; + let _ = #ident::try_from(raw)?; + Ok(()) + } + } + + impl wiggle_runtime::GuestTypeCopy for #ident {} + impl wiggle_runtime::GuestTypeClone for #ident { + fn read_from_guest(location: &wiggle_runtime::GuestPtr<#ident>) -> Result<#ident, wiggle_runtime::GuestError> { + Ok(*location.as_ref()?) + } + fn write_to_guest(&self, location: &wiggle_runtime::GuestPtrMut<#ident>) { + let val: #repr = #repr::from(*self); + unsafe { (location.as_raw() as *mut #repr).write(val) }; + } + } + } +} + fn define_enum(names: &Names, name: &witx::Id, e: &witx::EnumDatatype) -> TokenStream { let ident = names.type_(&name); diff --git a/crates/runtime/src/error.rs b/crates/runtime/src/error.rs index cbd4bbb61a..6768f67060 100644 --- a/crates/runtime/src/error.rs +++ b/crates/runtime/src/error.rs @@ -3,6 +3,8 @@ use thiserror::Error; #[derive(Debug, Error, PartialEq, Eq)] pub enum GuestError { + #[error("Invalid flag value {0}")] + InvalidFlagValue(&'static str), #[error("Invalid enum value {0}")] InvalidEnumValue(&'static str), #[error("Pointer out of bounds: {0:?}")] diff --git a/tests/main.rs b/tests/main.rs index be06a41d80..2b0800ebab 100644 --- a/tests/main.rs +++ b/tests/main.rs @@ -1,4 +1,5 @@ use proptest::prelude::*; +use std::convert::TryFrom; use wiggle_runtime::{ GuestArray, GuestError, GuestErrorType, GuestMemory, GuestPtr, GuestPtrMut, GuestRef, GuestRefMut, @@ -121,6 +122,18 @@ impl foo::Foo for WasiCtx { } Ok(()) } + + fn configure_car( + &mut self, + old_config: types::CarConfig, + other_config_ptr: GuestPtr, + ) -> Result { + let other_config = *other_config_ptr.as_ref().map_err(|e| { + eprintln!("old_config_ptr error: {}", e); + types::Errno::InvalidArg + })?; + Ok(old_config ^ other_config) + } } // Errno is used as a first return value in the functions above, therefore // it must implement GuestErrorType with type Context = WasiCtx. @@ -774,3 +787,82 @@ proptest! { e.test() } } + +fn car_config_strat() -> impl Strategy { + (1u8..=types::CarConfig::ALL_FLAGS.into()) + .prop_map(|v| { + types::CarConfig::try_from(v).expect("invalid value for types::CarConfig flag") + }) + .boxed() +} + +#[derive(Debug)] +struct ConfigureCarExercise { + old_config: types::CarConfig, + other_config: types::CarConfig, + other_config_by_ptr: MemArea, + return_ptr_loc: MemArea, +} + +impl ConfigureCarExercise { + pub fn strat() -> BoxedStrategy { + ( + car_config_strat(), + car_config_strat(), + HostMemory::mem_area_strat(4), + HostMemory::mem_area_strat(4), + ) + .prop_map( + |(old_config, other_config, other_config_by_ptr, return_ptr_loc)| Self { + old_config, + other_config, + other_config_by_ptr, + return_ptr_loc, + }, + ) + .prop_filter("non-overlapping ptrs", |e| { + non_overlapping_set(&[&e.other_config_by_ptr, &e.return_ptr_loc]) + }) + .boxed() + } + + pub fn test(&self) { + let mut ctx = WasiCtx::new(); + let mut host_memory = HostMemory::new(); + let mut guest_memory = GuestMemory::new(host_memory.as_mut_ptr(), host_memory.len() as u32); + + // Populate input ptr + *guest_memory + .ptr_mut(self.other_config_by_ptr.ptr) + .expect("ptr mut to CarConfig") + .as_ref_mut() + .expect("deref ptr mut to CarConfig") = self.other_config; + + let res = foo::configure_car( + &mut ctx, + &mut guest_memory, + self.old_config.into(), + self.other_config_by_ptr.ptr as i32, + self.return_ptr_loc.ptr as i32, + ); + assert_eq!(res, types::Errno::Ok.into(), "configure car errno"); + + let res_config = *guest_memory + .ptr::(self.return_ptr_loc.ptr) + .expect("ptr to returned CarConfig") + .as_ref() + .expect("deref to CarConfig value"); + + assert_eq!( + self.old_config ^ self.other_config, + res_config, + "returned CarConfig should be an XOR of inputs" + ); + } +} +proptest! { + #[test] + fn configure_car(e in ConfigureCarExercise::strat()) { + e.test() + } +} diff --git a/tests/test.witx b/tests/test.witx index e5b0c6bbf1..e7dda02d5f 100644 --- a/tests/test.witx +++ b/tests/test.witx @@ -12,6 +12,12 @@ $traffic $sleeping)) +(typename $car_config + (flags u8 + $automatic + $awd + $suv)) + (typename $pair_ints (struct (field $first s32) @@ -60,4 +66,10 @@ (param $excuses $excuse_array) (result $error $errno) ) + (@interface func (export "configure_car") + (param $old_config $car_config) + (param $old_config_by_ptr (@witx const_pointer $car_config)) + (result $error $errno) + (result $new_config $car_config) + ) ) From 2f223acc559077cd299085078bfe7f5107ef7f14 Mon Sep 17 00:00:00 2001 From: Pat Hickey Date: Fri, 21 Feb 2020 08:43:45 -0800 Subject: [PATCH 44/86] merge GuestTypePtr and GuestTypeClone into a single GuestTypeClone<'a> trait (#14) * merge GuestTypePtr and GuestTypeClone into a single GuestTypeClone<'a> trait * GuestArray can derive Clone (but not impl GuestTypeClone) * fix array tests * Fix GuestTypeClone for flags Co-authored-by: Jakub Konka --- crates/generate/src/types.rs | 6 ++--- crates/runtime/src/guest_type.rs | 6 +---- crates/runtime/src/lib.rs | 2 +- crates/runtime/src/memory/array.rs | 11 ++++---- crates/runtime/src/memory/ptr.rs | 39 +++++----------------------- tests/main.rs | 41 +++++++++++++++--------------- 6 files changed, 38 insertions(+), 67 deletions(-) diff --git a/crates/generate/src/types.rs b/crates/generate/src/types.rs index 4d2376ab99..d2fc80fbce 100644 --- a/crates/generate/src/types.rs +++ b/crates/generate/src/types.rs @@ -179,7 +179,7 @@ fn define_flags(names: &Names, name: &witx::Id, f: &witx::FlagsDatatype) -> Toke } impl wiggle_runtime::GuestTypeCopy for #ident {} - impl wiggle_runtime::GuestTypeClone for #ident { + impl<'a> wiggle_runtime::GuestTypeClone<'a> for #ident { fn read_from_guest(location: &wiggle_runtime::GuestPtr<#ident>) -> Result<#ident, wiggle_runtime::GuestError> { Ok(*location.as_ref()?) } @@ -267,7 +267,7 @@ fn define_enum(names: &Names, name: &witx::Id, e: &witx::EnumDatatype) -> TokenS } impl wiggle_runtime::GuestTypeCopy for #ident {} - impl wiggle_runtime::GuestTypeClone for #ident { + impl<'a> wiggle_runtime::GuestTypeClone<'a> for #ident { fn read_from_guest(location: &wiggle_runtime::GuestPtr<#ident>) -> Result<#ident, wiggle_runtime::GuestError> { use ::std::convert::TryFrom; let raw: #repr = unsafe { (location.as_raw() as *const #repr).read() }; @@ -502,7 +502,7 @@ fn define_ptr_struct(names: &Names, name: &witx::Id, s: &witx::StructDatatype) - Ok(()) } } - impl<'a> wiggle_runtime::GuestTypePtr<'a> for #ident<'a> { + impl<'a> wiggle_runtime::GuestTypeClone<'a> for #ident<'a> { fn read_from_guest(location: &wiggle_runtime::GuestPtr<'a, #ident<'a>>) -> Result<#ident<'a>, wiggle_runtime::GuestError> { #(#member_reads)* Ok(#ident { #(#member_names),* }) diff --git a/crates/runtime/src/guest_type.rs b/crates/runtime/src/guest_type.rs index 6c2f98795e..83eacaa0ce 100644 --- a/crates/runtime/src/guest_type.rs +++ b/crates/runtime/src/guest_type.rs @@ -11,11 +11,7 @@ pub trait GuestType: Sized { } pub trait GuestTypeCopy: GuestType + Copy {} -pub trait GuestTypeClone: GuestType + Clone { - fn read_from_guest<'a>(location: &GuestPtr<'a, Self>) -> Result; - fn write_to_guest<'a>(&self, location: &GuestPtrMut<'a, Self>); -} -pub trait GuestTypePtr<'a>: GuestType { +pub trait GuestTypeClone<'a>: GuestType + Clone { fn read_from_guest(location: &GuestPtr<'a, Self>) -> Result; fn write_to_guest(&self, location: &GuestPtrMut<'a, Self>); } diff --git a/crates/runtime/src/lib.rs b/crates/runtime/src/lib.rs index 5c3cea5e70..b07da6a21d 100644 --- a/crates/runtime/src/lib.rs +++ b/crates/runtime/src/lib.rs @@ -5,6 +5,6 @@ mod memory; mod region; pub use error::GuestError; -pub use guest_type::{GuestErrorType, GuestType, GuestTypeClone, GuestTypeCopy, GuestTypePtr}; +pub use guest_type::{GuestErrorType, GuestType, GuestTypeClone, GuestTypeCopy}; pub use memory::{GuestArray, GuestMemory, GuestPtr, GuestPtrMut, GuestRef, GuestRefMut}; pub use region::Region; diff --git a/crates/runtime/src/memory/array.rs b/crates/runtime/src/memory/array.rs index 40a5e37d82..c9615b1e2f 100644 --- a/crates/runtime/src/memory/array.rs +++ b/crates/runtime/src/memory/array.rs @@ -2,6 +2,7 @@ use super::ptr::{GuestPtr, GuestRef}; use crate::{GuestError, GuestType, GuestTypeCopy}; use std::{fmt, ops::Deref}; +#[derive(Clone)] pub struct GuestArray<'a, T> where T: GuestType, @@ -145,9 +146,9 @@ where #[cfg(test)] mod test { - use super::super::{ - ptr::{GuestPtr, GuestPtrMut}, - GuestError, GuestMemory, Region, + use crate::{ + memory::ptr::{GuestPtr, GuestPtrMut}, + GuestError, GuestMemory, GuestTypeClone, Region, }; #[repr(align(4096))] @@ -248,9 +249,7 @@ mod test { let contents = arr .iter() .map(|ptr_ptr| { - *ptr_ptr - .expect("valid ptr to ptr") - .read_ptr_from_guest() + *GuestTypeClone::read_from_guest(&ptr_ptr.expect("valid ptr to ptr")) .expect("valid ptr to some value") .as_ref() .expect("deref ptr to some value") diff --git a/crates/runtime/src/memory/ptr.rs b/crates/runtime/src/memory/ptr.rs index da1fd3e4bc..afebb6c332 100644 --- a/crates/runtime/src/memory/ptr.rs +++ b/crates/runtime/src/memory/ptr.rs @@ -1,8 +1,5 @@ use super::{array::GuestArray, GuestMemory}; -use crate::{ - borrow::BorrowHandle, GuestError, GuestType, GuestTypeClone, GuestTypeCopy, GuestTypePtr, - Region, -}; +use crate::{borrow::BorrowHandle, GuestError, GuestType, GuestTypeClone, GuestTypeCopy, Region}; use std::{ fmt, marker::PhantomData, @@ -81,22 +78,13 @@ where impl<'a, T> GuestPtr<'a, T> where - T: GuestTypeClone, + T: GuestTypeClone<'a>, { pub fn clone_from_guest(&self) -> Result { T::read_from_guest(self) } } -impl<'a, T> GuestPtr<'a, T> -where - T: GuestTypePtr<'a>, -{ - pub fn read_ptr_from_guest(&self) -> Result { - T::read_from_guest(self) - } -} - impl<'a, T> GuestType for GuestPtr<'a, T> where T: GuestType, @@ -123,9 +111,9 @@ where } // Operations for reading and writing Ptrs to memory: -impl<'a, T> GuestTypePtr<'a> for GuestPtr<'a, T> +impl<'a, T> GuestTypeClone<'a> for GuestPtr<'a, T> where - T: GuestType, + T: GuestType + Clone, { fn read_from_guest(location: &GuestPtr<'a, Self>) -> Result { // location is guaranteed to be in GuestMemory and aligned to 4 @@ -220,7 +208,7 @@ where impl<'a, T> GuestPtrMut<'a, T> where - T: GuestTypePtr<'a>, + T: GuestTypeClone<'a>, { pub fn read_ptr_from_guest(&self) -> Result { T::read_from_guest(&self.as_immut()) @@ -231,19 +219,6 @@ where } } -impl<'a, T> GuestPtrMut<'a, T> -where - T: GuestTypeClone, -{ - pub fn clone_from_guest(&self) -> Result { - T::read_from_guest(&self.as_immut()) - } - - pub fn clone_to_guest(&self, val: &T) { - T::write_to_guest(val, &self) - } -} - impl<'a, T> GuestType for GuestPtrMut<'a, T> where T: GuestType, @@ -270,9 +245,9 @@ where } // Reading and writing GuestPtrMuts to memory: -impl<'a, T> GuestTypePtr<'a> for GuestPtrMut<'a, T> +impl<'a, T> GuestTypeClone<'a> for GuestPtrMut<'a, T> where - T: GuestType, + T: GuestType + Clone, { fn read_from_guest(location: &GuestPtr<'a, Self>) -> Result { // location is guaranteed to be in GuestMemory and aligned to 4 diff --git a/tests/main.rs b/tests/main.rs index 2b0800ebab..41aae0f5ab 100644 --- a/tests/main.rs +++ b/tests/main.rs @@ -56,11 +56,13 @@ impl foo::Foo for WasiCtx { println!("wrote to input2_ref {:?}", input3); // Read ptr value from mutable ptr: - let input4_ptr: GuestPtr = - input4_ptr_ptr.read_ptr_from_guest().map_err(|e| { - eprintln!("input4_ptr_ptr error: {}", e); - types::Errno::InvalidArg - })?; + let input4_ptr: GuestPtr = wiggle_runtime::GuestTypeClone::read_from_guest( + &input4_ptr_ptr.as_immut(), + ) + .map_err(|e| { + eprintln!("input4_ptr_ptr error: {}", e); + types::Errno::InvalidArg + })?; // Read enum value from that ptr: let input4: types::Excuse = *input4_ptr.as_ref().map_err(|e| { @@ -99,22 +101,22 @@ impl foo::Foo for WasiCtx { &mut self, excuses: &types::ConstExcuseArray, ) -> Result { - let last = excuses - .iter() - .last() - .expect("input array is non-empty") - .expect("valid ptr to ptr") - .read_ptr_from_guest() - .expect("valid ptr to some Excuse value"); + let last = wiggle_runtime::GuestTypeClone::read_from_guest( + &excuses + .iter() + .last() + .expect("input array is non-empty") + .expect("valid ptr to ptr"), + ) + .expect("valid ptr to some Excuse value"); Ok(*last.as_ref().expect("dereferencing ptr should succeed")) } fn populate_excuses(&mut self, excuses: &types::ExcuseArray) -> Result<(), types::Errno> { for excuse in excuses.iter() { - let ptr_to_ptr = excuse - .expect("valid ptr to ptr") - .read_ptr_from_guest() - .expect("valid ptr to some Excuse value"); + let ptr_to_ptr = + wiggle_runtime::GuestTypeClone::read_from_guest(&excuse.expect("valid ptr to ptr")) + .expect("valid ptr to some Excuse value"); let mut ptr = ptr_to_ptr .as_ref_mut() .expect("dereferencing mut ptr should succeed"); @@ -767,10 +769,9 @@ impl PopulateExcusesExcercise { .array(self.elements.len() as u32) .expect("as array"); for el in arr.iter() { - let ptr_to_ptr = el - .expect("valid ptr to ptr") - .read_ptr_from_guest() - .expect("valid ptr to some Excuse value"); + let ptr_to_ptr = + wiggle_runtime::GuestTypeClone::read_from_guest(&el.expect("valid ptr to ptr")) + .expect("valid ptr to some Excuse value"); assert_eq!( *ptr_to_ptr .as_ref() From 6ab3ff71d29d4b3a25bdbfa911bc649efa93342e Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Fri, 21 Feb 2020 22:37:22 +0100 Subject: [PATCH 45/86] 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 --- crates/generate/src/funcs.rs | 31 ++++++- crates/generate/src/names.rs | 8 +- crates/generate/src/types.rs | 16 ++-- crates/runtime/src/error.rs | 2 + crates/runtime/src/lib.rs | 5 +- crates/runtime/src/memory/mod.rs | 2 + crates/runtime/src/memory/string.rs | 124 ++++++++++++++++++++++++++++ tests/main.rs | 93 ++++++++++++++++++++- tests/test.witx | 5 ++ 9 files changed, 273 insertions(+), 13 deletions(-) create mode 100644 crates/runtime/src/memory/string.rs diff --git a/crates/generate/src/funcs.rs b/crates/generate/src/funcs.rs index b2728db3ca..57a9e63da7 100644 --- a/crates/generate/src/funcs.rs +++ b/crates/generate/src/funcs.rs @@ -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(¶m.name); + let len_name = names.func_len_binding(¶m.name); + let name = names.func_param(¶m.name); + quote! { + let num_elems = match memory.ptr::(#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::(#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()); diff --git a/crates/generate/src/names.rs b/crates/generate/src/names.rs index e673af3197..3337a18619 100644 --- a/crates/generate/src/names.rs +++ b/crates/generate/src/names.rs @@ -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>) diff --git a/crates/generate/src/types.rs b/crates/generate/src/types.rs index d2fc80fbce..61e21b71a5 100644 --- a/crates/generate/src/types.rs +++ b/crates/generate/src/types.rs @@ -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)?)?; } diff --git a/crates/runtime/src/error.rs b/crates/runtime/src/error.rs index 6768f67060..9c9e8a8e9a 100644 --- a/crates/runtime/src/error.rs +++ b/crates/runtime/src/error.rs @@ -27,4 +27,6 @@ pub enum GuestError { #[source] err: Box, }, + #[error("Invalid UTF-8 encountered")] + InvalidUtf8(#[from] std::str::Utf8Error), } diff --git a/crates/runtime/src/lib.rs b/crates/runtime/src/lib.rs index b07da6a21d..839c646b0b 100644 --- a/crates/runtime/src/lib.rs +++ b/crates/runtime/src/lib.rs @@ -6,5 +6,8 @@ mod region; pub use error::GuestError; pub use guest_type::{GuestErrorType, GuestType, GuestTypeClone, GuestTypeCopy}; -pub use memory::{GuestArray, GuestMemory, GuestPtr, GuestPtrMut, GuestRef, GuestRefMut}; +pub use memory::{ + GuestArray, GuestMemory, GuestPtr, GuestPtrMut, GuestRef, GuestRefMut, GuestString, + GuestStringRef, +}; pub use region::Region; diff --git a/crates/runtime/src/memory/mod.rs b/crates/runtime/src/memory/mod.rs index 5892df29da..2800da56f1 100644 --- a/crates/runtime/src/memory/mod.rs +++ b/crates/runtime/src/memory/mod.rs @@ -1,8 +1,10 @@ mod array; mod ptr; +mod string; pub use array::*; pub use ptr::*; +pub use string::*; use crate::{borrow::GuestBorrows, GuestError, GuestType, Region}; use std::{cell::RefCell, fmt, marker::PhantomData, rc::Rc}; diff --git a/crates/runtime/src/memory/string.rs b/crates/runtime/src/memory/string.rs new file mode 100644 index 0000000000..a4e6deac6d --- /dev/null +++ b/crates/runtime/src/memory/string.rs @@ -0,0 +1,124 @@ +use super::array::{GuestArray, GuestArrayRef}; +use crate::GuestError; +use std::fmt; + +pub struct GuestString<'a> { + pub(super) array: GuestArray<'a, u8>, +} + +impl<'a> fmt::Debug for GuestString<'a> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "GuestString {{ array: {:?} }}", self.array) + } +} + +impl<'a> GuestString<'a> { + pub fn as_ref(&self) -> Result, GuestError> { + let ref_ = self.array.as_ref()?; + Ok(GuestStringRef { ref_ }) + } + + pub fn to_string(&self) -> Result { + Ok(self.as_ref()?.as_str()?.to_owned()) + } +} + +impl<'a> From> for GuestString<'a> { + fn from(array: GuestArray<'a, u8>) -> Self { + Self { array } + } +} + +pub struct GuestStringRef<'a> { + pub(super) ref_: GuestArrayRef<'a, u8>, +} + +impl<'a> fmt::Debug for GuestStringRef<'a> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "GuestStringRef {{ ref_: {:?} }}", self.ref_) + } +} + +impl<'a> GuestStringRef<'a> { + pub fn as_str(&self) -> Result<&str, GuestError> { + std::str::from_utf8(&*self.ref_).map_err(Into::into) + } +} + +#[cfg(test)] +mod test { + use super::{ + super::{ + ptr::{GuestPtr, GuestPtrMut}, + GuestError, GuestMemory, + }, + GuestString, + }; + + #[repr(align(4096))] + struct HostMemory { + buffer: [u8; 4096], + } + + impl HostMemory { + pub fn new() -> Self { + Self { buffer: [0; 4096] } + } + pub fn as_mut_ptr(&mut self) -> *mut u8 { + self.buffer.as_mut_ptr() + } + pub fn len(&self) -> usize { + self.buffer.len() + } + } + + #[test] + fn valid_utf8() { + let mut host_memory = HostMemory::new(); + let guest_memory = GuestMemory::new(host_memory.as_mut_ptr(), host_memory.len() as u32); + // write string into memory + let mut ptr: GuestPtrMut = guest_memory.ptr_mut(0).expect("ptr mut to start of string"); + let input_str = "cześć WASI!"; + for byte in input_str.as_bytes() { + let mut ref_mut = ptr.as_ref_mut().expect("valid deref"); + *ref_mut = *byte; + ptr = ptr.elem(1).expect("next ptr"); + } + // read the string as GuestString + let ptr: GuestPtr = guest_memory.ptr(0).expect("ptr to start of string"); + let guest_string: GuestString<'_> = ptr + .array(input_str.len() as u32) + .expect("valid null-terminated string") + .into(); + let as_ref = guest_string.as_ref().expect("deref"); + assert_eq!(as_ref.as_str().expect("valid UTF-8"), input_str); + } + + #[test] + fn invalid_utf8() { + let mut host_memory = HostMemory::new(); + let guest_memory = GuestMemory::new(host_memory.as_mut_ptr(), host_memory.len() as u32); + // write string into memory + let mut ptr: GuestPtrMut = guest_memory.ptr_mut(0).expect("ptr mut to start of string"); + let input_str = "cześć WASI!"; + let mut bytes = input_str.as_bytes().to_vec(); + // insert 0xFE which is an invalid UTF-8 byte + bytes[5] = 0xfe; + for byte in &bytes { + let mut ref_mut = ptr.as_ref_mut().expect("valid deref"); + *ref_mut = *byte; + ptr = ptr.elem(1).expect("next ptr"); + } + // read the string as GuestString + let ptr: GuestPtr = guest_memory.ptr(0).expect("ptr to start of string"); + let guest_string: GuestString<'_> = ptr + .array(bytes.len() as u32) + .expect("valid null-terminated string") + .into(); + let as_ref = guest_string.as_ref().expect("deref"); + match as_ref.as_str().expect_err("should fail") { + GuestError::InvalidUtf8(_) => {} + x => assert!(false, "expected GuestError::InvalidUtf8(_), got {:?}", x), + } + } +} diff --git a/tests/main.rs b/tests/main.rs index 41aae0f5ab..a64395ffa3 100644 --- a/tests/main.rs +++ b/tests/main.rs @@ -2,7 +2,7 @@ use proptest::prelude::*; use std::convert::TryFrom; use wiggle_runtime::{ GuestArray, GuestError, GuestErrorType, GuestMemory, GuestPtr, GuestPtrMut, GuestRef, - GuestRefMut, + GuestRefMut, GuestString, }; wiggle_generate::from_witx!({ @@ -136,6 +136,13 @@ impl foo::Foo for WasiCtx { })?; Ok(old_config ^ other_config) } + + fn hello_string(&mut self, a_string: &GuestString<'_>) -> Result { + let as_ref = a_string.as_ref().expect("deref ptr should succeed"); + let as_str = as_ref.as_str().expect("valid UTF-8 string"); + println!("a_string='{}'", as_str); + Ok(as_str.len() as u32) + } } // Errno is used as a first return value in the functions above, therefore // it must implement GuestErrorType with type Context = WasiCtx. @@ -867,3 +874,87 @@ proptest! { e.test() } } + +fn test_string_strategy() -> impl Strategy { + "\\p{Greek}{1,256}" +} + +#[derive(Debug)] +struct HelloStringExercise { + test_word: String, + string_ptr_loc: MemArea, + string_len_loc: MemArea, + return_ptr_loc: MemArea, +} + +impl HelloStringExercise { + pub fn strat() -> BoxedStrategy { + (test_string_strategy(),) + .prop_flat_map(|(test_word,)| { + ( + Just(test_word.clone()), + HostMemory::mem_area_strat(test_word.len() as u32), + HostMemory::mem_area_strat(4), + HostMemory::mem_area_strat(4), + ) + }) + .prop_map( + |(test_word, string_ptr_loc, string_len_loc, return_ptr_loc)| Self { + test_word, + string_ptr_loc, + string_len_loc, + return_ptr_loc, + }, + ) + .prop_filter("non-overlapping pointers", |e| { + non_overlapping_set(&[&e.string_ptr_loc, &e.string_len_loc, &e.return_ptr_loc]) + }) + .boxed() + } + + pub fn test(&self) { + let mut ctx = WasiCtx::new(); + let mut host_memory = HostMemory::new(); + let mut guest_memory = GuestMemory::new(host_memory.as_mut_ptr(), host_memory.len() as u32); + + // Populate string length + *guest_memory + .ptr_mut(self.string_len_loc.ptr) + .expect("ptr mut to string len") + .as_ref_mut() + .expect("deref ptr mut to string len") = self.test_word.len() as u32; + + // Populate string in guest's memory + { + let mut next: GuestPtrMut<'_, u8> = guest_memory + .ptr_mut(self.string_ptr_loc.ptr) + .expect("ptr mut to the first byte of string"); + for byte in self.test_word.as_bytes() { + *next.as_ref_mut().expect("deref mut") = *byte; + next = next.elem(1).expect("increment ptr by 1"); + } + } + + let res = foo::hello_string( + &mut ctx, + &mut guest_memory, + self.string_ptr_loc.ptr as i32, + self.string_len_loc.ptr as i32, + self.return_ptr_loc.ptr as i32, + ); + assert_eq!(res, types::Errno::Ok.into(), "hello string errno"); + + let given = *guest_memory + .ptr::(self.return_ptr_loc.ptr) + .expect("ptr to return value") + .as_ref() + .expect("deref ptr to return value"); + assert_eq!(self.test_word.len() as u32, given); + } +} +proptest! { + #[test] + fn hello_string(e in HelloStringExercise::strat()) { + e.test() + } +} diff --git a/tests/test.witx b/tests/test.witx index e7dda02d5f..a6edac306f 100644 --- a/tests/test.witx +++ b/tests/test.witx @@ -72,4 +72,9 @@ (result $error $errno) (result $new_config $car_config) ) + (@interface func (export "hello_string") + (param $a_string string) + (result $error $errno) + (result $total_bytes u32) + ) ) From f48474b247ae6ccd1af7be83aa9cc3afd9838590 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Fri, 21 Feb 2020 22:53:10 +0100 Subject: [PATCH 46/86] Draft out IntDatatype in wiggle-generate (#15) * Draft out IntDatatype in wiggle-generate This commit drafts out basic layout for `IntDatatype` structure in `wiggle`. As it currently stands, an `Int` type is represented as a one-element tuple struct much like `FlagDatatype`, however, with this difference that we do not perform any checks on the input underlying representation since any value for the prescribed type is legal. * Finish drafting IntDatatype support in wiggle This commit adds necessary marshal stubs to properly pass `IntDatatype` in and out of interface functions. It also adds a basic proptest. --- crates/generate/src/funcs.rs | 3 +- crates/generate/src/names.rs | 4 ++ crates/generate/src/types.rs | 88 +++++++++++++++++++++++++++++++++++- tests/main.rs | 68 ++++++++++++++++++++++++++++ tests/test.witx | 14 ++++++ 5 files changed, 175 insertions(+), 2 deletions(-) diff --git a/crates/generate/src/funcs.rs b/crates/generate/src/funcs.rs index 57a9e63da7..861de4c258 100644 --- a/crates/generate/src/funcs.rs +++ b/crates/generate/src/funcs.rs @@ -154,6 +154,7 @@ fn marshal_arg( match &*tref.type_() { witx::Type::Enum(_e) => try_into_conversion, witx::Type::Flags(_f) => try_into_conversion, + witx::Type::Int(_i) => try_into_conversion, witx::Type::Builtin(b) => match b { witx::BuiltinType::U8 | witx::BuiltinType::U16 | witx::BuiltinType::Char8 => { try_into_conversion @@ -359,7 +360,7 @@ where | witx::BuiltinType::Char8 => write_val_to_ptr, witx::BuiltinType::String => unimplemented!("string types"), }, - witx::Type::Enum(_) | witx::Type::Flags(_) => write_val_to_ptr, + witx::Type::Enum(_) | witx::Type::Flags(_) | witx::Type::Int(_) => write_val_to_ptr, _ => unimplemented!("marshal result"), } } diff --git a/crates/generate/src/names.rs b/crates/generate/src/names.rs index 3337a18619..c967f6e1da 100644 --- a/crates/generate/src/names.rs +++ b/crates/generate/src/names.rs @@ -86,6 +86,10 @@ impl Names { format_ident!("{}", id.as_str().to_shouty_snake_case()) } + pub fn int_member(&self, id: &Id) -> Ident { + format_ident!("{}", id.as_str().to_shouty_snake_case()) + } + pub fn struct_member(&self, id: &Id) -> Ident { format_ident!("{}", id.as_str().to_snake_case()) } diff --git a/crates/generate/src/types.rs b/crates/generate/src/types.rs index 61e21b71a5..d2f94353da 100644 --- a/crates/generate/src/types.rs +++ b/crates/generate/src/types.rs @@ -10,7 +10,7 @@ pub fn define_datatype(names: &Names, namedtype: &witx::NamedType) -> TokenStrea witx::TypeRef::Name(alias_to) => define_alias(names, &namedtype.name, &alias_to), witx::TypeRef::Value(v) => match &**v { witx::Type::Enum(e) => define_enum(names, &namedtype.name, &e), - witx::Type::Int(_) => unimplemented!("int types"), + witx::Type::Int(i) => define_int(names, &namedtype.name, &i), witx::Type::Flags(f) => define_flags(names, &namedtype.name, &f), witx::Type::Struct(s) => { if struct_is_copy(s) { @@ -46,6 +46,92 @@ fn define_alias(names: &Names, name: &witx::Id, to: &witx::NamedType) -> TokenSt } } +fn define_int(names: &Names, name: &witx::Id, i: &witx::IntDatatype) -> TokenStream { + let ident = names.type_(&name); + let repr = int_repr_tokens(i.repr); + let abi_repr = atom_token(match i.repr { + witx::IntRepr::U8 | witx::IntRepr::U16 | witx::IntRepr::U32 => witx::AtomType::I32, + witx::IntRepr::U64 => witx::AtomType::I64, + }); + let consts = i + .consts + .iter() + .map(|r#const| { + let const_ident = names.int_member(&r#const.name); + let value = r#const.value; + quote!(pub const #const_ident: #ident = #ident(#value)) + }) + .collect::>(); + + quote! { + #[repr(transparent)] + #[derive(Copy, Clone, Debug, ::std::hash::Hash, Eq, PartialEq)] + pub struct #ident(#repr); + + impl #ident { + #(#consts;)* + } + + impl ::std::convert::TryFrom<#repr> for #ident { + type Error = wiggle_runtime::GuestError; + fn try_from(value: #repr) -> Result { + Ok(#ident(value)) + } + } + + impl ::std::convert::TryFrom<#abi_repr> for #ident { + type Error = wiggle_runtime::GuestError; + fn try_from(value: #abi_repr) -> Result<#ident, wiggle_runtime::GuestError> { + #ident::try_from(value as #repr) + } + } + + impl From<#ident> for #repr { + fn from(e: #ident) -> #repr { + e.0 + } + } + + impl From<#ident> for #abi_repr { + fn from(e: #ident) -> #abi_repr { + #repr::from(e) as #abi_repr + } + } + + impl wiggle_runtime::GuestType for #ident { + fn size() -> u32 { + ::std::mem::size_of::<#repr>() as u32 + } + + fn align() -> u32 { + ::std::mem::align_of::<#repr>() as u32 + } + + fn name() -> String { + stringify!(#ident).to_owned() + } + + fn validate<'a>(location: &wiggle_runtime::GuestPtr<'a, #ident>) -> Result<(), wiggle_runtime::GuestError> { + use ::std::convert::TryFrom; + let raw: #repr = unsafe { (location.as_raw() as *const #repr).read() }; + let _ = #ident::try_from(raw)?; + Ok(()) + } + } + + impl wiggle_runtime::GuestTypeCopy for #ident {} + impl<'a> wiggle_runtime::GuestTypeClone<'a> for #ident { + fn read_from_guest(location: &wiggle_runtime::GuestPtr<#ident>) -> Result<#ident, wiggle_runtime::GuestError> { + Ok(*location.as_ref()?) + } + fn write_to_guest(&self, location: &wiggle_runtime::GuestPtrMut<#ident>) { + let val: #repr = #repr::from(*self); + unsafe { (location.as_raw() as *mut #repr).write(val) }; + } + } + } +} + fn define_flags(names: &Names, name: &witx::Id, f: &witx::FlagsDatatype) -> TokenStream { let ident = names.type_(&name); let repr = int_repr_tokens(f.repr); diff --git a/tests/main.rs b/tests/main.rs index a64395ffa3..422173b7af 100644 --- a/tests/main.rs +++ b/tests/main.rs @@ -143,6 +143,15 @@ impl foo::Foo for WasiCtx { println!("a_string='{}'", as_str); Ok(as_str.len() as u32) } + + fn cookie_cutter(&mut self, init_cookie: types::Cookie) -> Result { + let res = if init_cookie == types::Cookie::START { + types::Bool::True + } else { + types::Bool::False + }; + Ok(res) + } } // Errno is used as a first return value in the functions above, therefore // it must implement GuestErrorType with type Context = WasiCtx. @@ -958,3 +967,62 @@ proptest! { e.test() } } + +fn cookie_strat() -> impl Strategy { + (0..std::u64::MAX) + .prop_map(|x| types::Cookie::try_from(x).expect("within range of cookie")) + .boxed() +} + +#[derive(Debug)] +struct CookieCutterExercise { + cookie: types::Cookie, + return_ptr_loc: MemArea, +} + +impl CookieCutterExercise { + pub fn strat() -> BoxedStrategy { + (cookie_strat(), HostMemory::mem_area_strat(4)) + .prop_map(|(cookie, return_ptr_loc)| Self { + cookie, + return_ptr_loc, + }) + .boxed() + } + + pub fn test(&self) { + let mut ctx = WasiCtx::new(); + let mut host_memory = HostMemory::new(); + let mut guest_memory = GuestMemory::new(host_memory.as_mut_ptr(), host_memory.len() as u32); + + let res = foo::cookie_cutter( + &mut ctx, + &mut guest_memory, + self.cookie.into(), + self.return_ptr_loc.ptr as i32, + ); + assert_eq!(res, types::Errno::Ok.into(), "cookie cutter errno"); + + let is_cookie_start = *guest_memory + .ptr::(self.return_ptr_loc.ptr) + .expect("ptr to returned Bool") + .as_ref() + .expect("deref to Bool value"); + + assert_eq!( + if is_cookie_start == types::Bool::True { + true + } else { + false + }, + self.cookie == types::Cookie::START, + "returned Bool should test if input was Cookie::START", + ); + } +} +proptest! { + #[test] + fn cookie_cutter(e in CookieCutterExercise::strat()) { + e.test() + } +} diff --git a/tests/test.witx b/tests/test.witx index a6edac306f..4a0930a616 100644 --- a/tests/test.witx +++ b/tests/test.witx @@ -18,6 +18,15 @@ $awd $suv)) +(typename $cookie + (int u64 + (const $start 0))) + +(typename $bool + (enum u8 + $false + $true)) + (typename $pair_ints (struct (field $first s32) @@ -77,4 +86,9 @@ (result $error $errno) (result $total_bytes u32) ) + (@interface func (export "cookie_cutter") + (param $init_cookie $cookie) + (result $error $errno) + (result $is_start $bool) + ) ) From 0909cf2660c6f24b9054beac151ff699f07c757f Mon Sep 17 00:00:00 2001 From: Pat Hickey Date: Fri, 21 Feb 2020 13:18:42 -0800 Subject: [PATCH 47/86] tests: factor common HostMemory / MemArea code into a separate crate --- Cargo.toml | 4 +- crates/test/.gitignore | 2 + crates/test/Cargo.toml | 9 ++++ crates/test/src/lib.rs | 92 ++++++++++++++++++++++++++++++++ tests/main.rs | 117 ++++++----------------------------------- 5 files changed, 122 insertions(+), 102 deletions(-) create mode 100644 crates/test/.gitignore create mode 100644 crates/test/Cargo.toml create mode 100644 crates/test/src/lib.rs diff --git a/Cargo.toml b/Cargo.toml index 1d65b86102..e834e66349 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,11 +9,13 @@ wiggle-generate = { path = "crates/generate" } wiggle-runtime = { path = "crates/runtime" } [dev-dependencies] +wiggle-test = { path = "crates/test" } proptest = "0.9" [workspace] members = [ "crates/generate", - "crates/runtime" + "crates/runtime", + "crates/test", ] exclude = ["crates/WASI"] diff --git a/crates/test/.gitignore b/crates/test/.gitignore new file mode 100644 index 0000000000..a9d37c560c --- /dev/null +++ b/crates/test/.gitignore @@ -0,0 +1,2 @@ +target +Cargo.lock diff --git a/crates/test/Cargo.toml b/crates/test/Cargo.toml new file mode 100644 index 0000000000..88c599e46c --- /dev/null +++ b/crates/test/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "wiggle-test" +version = "0.1.0" +authors = ["Pat Hickey ", "Jakub Konka "] +edition = "2018" + +[dependencies] +wiggle-runtime = { path = "../runtime" } +proptest = "0.9" diff --git a/crates/test/src/lib.rs b/crates/test/src/lib.rs new file mode 100644 index 0000000000..8b036111a3 --- /dev/null +++ b/crates/test/src/lib.rs @@ -0,0 +1,92 @@ +use proptest::prelude::*; +use wiggle_runtime::GuestMemory; + +#[repr(align(4096))] +pub struct HostMemory { + buffer: [u8; 4096], +} +impl HostMemory { + pub fn new() -> Self { + HostMemory { buffer: [0; 4096] } + } + + pub fn guest_memory<'a>(&'a mut self) -> GuestMemory<'a> { + GuestMemory::new(self.buffer.as_mut_ptr(), self.buffer.len() as u32) + } + + pub fn mem_area_strat(align: u32) -> BoxedStrategy { + prop::num::u32::ANY + .prop_filter_map("needs to fit in memory", move |p| { + let p_aligned = p - (p % align); // Align according to argument + let ptr = p_aligned % 4096; // Put inside memory + if ptr + align < 4096 { + Some(MemArea { ptr, len: align }) + } else { + None + } + }) + .boxed() + } +} + +#[derive(Debug)] +pub struct MemArea { + pub ptr: u32, + pub len: u32, +} + +impl MemArea { + // This code is a whole lot like the Region::overlaps func thats at the core of the code under + // test. + // So, I implemented this one with std::ops::Range so it is less likely I wrote the same bug in two + // places. + pub fn overlapping(&self, b: &Self) -> bool { + // a_range is all elems in A + let a_range = std::ops::Range { + start: self.ptr, + end: self.ptr + self.len, // std::ops::Range is open from the right + }; + // b_range is all elems in B + let b_range = std::ops::Range { + start: b.ptr, + end: b.ptr + b.len, + }; + // No element in B is contained in A: + for b_elem in b_range.clone() { + if a_range.contains(&b_elem) { + return true; + } + } + // No element in A is contained in B: + for a_elem in a_range { + if b_range.contains(&a_elem) { + return true; + } + } + return false; + } + pub fn non_overlapping_set(areas: &[&Self]) -> bool { + // A is all areas + for (i, a) in areas.iter().enumerate() { + // (A, B) is every pair of areas + for b in areas[i + 1..].iter() { + if a.overlapping(b) { + return false; + } + } + } + return true; + } +} + +#[cfg(test)] +mod test { + use super::*; + #[test] + fn hostmemory_is_aligned() { + let mut h = HostMemory::new(); + assert_eq!(h.buffer.as_mut_ptr() as usize % 4096, 0); + let mut h = Box::new(HostMemory::new()); + assert_eq!(h.buffer.as_mut_ptr() as usize % 4096, 0); + } +} diff --git a/tests/main.rs b/tests/main.rs index 422173b7af..9b09d9bf69 100644 --- a/tests/main.rs +++ b/tests/main.rs @@ -1,9 +1,10 @@ use proptest::prelude::*; use std::convert::TryFrom; use wiggle_runtime::{ - GuestArray, GuestError, GuestErrorType, GuestMemory, GuestPtr, GuestPtrMut, GuestRef, - GuestRefMut, GuestString, + GuestArray, GuestError, GuestErrorType, GuestPtr, GuestPtrMut, GuestRef, GuestRefMut, + GuestString, }; +use wiggle_test::{HostMemory, MemArea}; wiggle_generate::from_witx!({ witx: ["tests/test.witx"], @@ -169,92 +170,6 @@ impl GuestErrorType for types::Errno { } } -#[repr(align(4096))] -struct HostMemory { - buffer: [u8; 4096], -} -impl HostMemory { - pub fn new() -> Self { - HostMemory { buffer: [0; 4096] } - } - pub fn as_mut_ptr(&mut self) -> *mut u8 { - self.buffer.as_mut_ptr() - } - pub fn len(&self) -> usize { - self.buffer.len() - } - pub fn mem_area_strat(align: u32) -> BoxedStrategy { - prop::num::u32::ANY - .prop_filter_map("needs to fit in memory", move |p| { - let p_aligned = p - (p % align); // Align according to argument - let ptr = p_aligned % 4096; // Put inside memory - if ptr + align < 4096 { - Some(MemArea { ptr, len: align }) - } else { - None - } - }) - .boxed() - } -} - -#[derive(Debug)] -struct MemArea { - ptr: u32, - len: u32, -} - -// This code is a whole lot like the Region::overlaps func thats at the core of the code under -// test. -// So, I implemented this one with std::ops::Range so it is less likely I wrote the same bug in two -// places. -fn overlapping(a: &MemArea, b: &MemArea) -> bool { - // a_range is all elems in A - let a_range = std::ops::Range { - start: a.ptr, - end: a.ptr + a.len, // std::ops::Range is open from the right - }; - // b_range is all elems in B - let b_range = std::ops::Range { - start: b.ptr, - end: b.ptr + b.len, - }; - // No element in B is contained in A: - for b_elem in b_range.clone() { - if a_range.contains(&b_elem) { - return true; - } - } - // No element in A is contained in B: - for a_elem in a_range { - if b_range.contains(&a_elem) { - return true; - } - } - return false; -} - -fn non_overlapping_set(areas: &[&MemArea]) -> bool { - // A is all areas - for (i, a) in areas.iter().enumerate() { - // (A, B) is every pair of areas - for b in areas[i + 1..].iter() { - if overlapping(a, b) { - return false; - } - } - } - return true; -} - -#[test] -fn hostmemory_is_aligned() { - let mut h = HostMemory::new(); - assert_eq!(h.as_mut_ptr() as usize % 4096, 0); - let mut h = Box::new(HostMemory::new()); - assert_eq!(h.as_mut_ptr() as usize % 4096, 0); -} - #[derive(Debug)] struct BatExercise { pub input: u32, @@ -265,7 +180,7 @@ impl BatExercise { pub fn test(&self) { let mut ctx = WasiCtx::new(); let mut host_memory = HostMemory::new(); - let mut guest_memory = GuestMemory::new(host_memory.as_mut_ptr(), host_memory.len() as u32); + let mut guest_memory = host_memory.guest_memory(); let bat_err = foo::bat( &mut ctx, @@ -352,7 +267,7 @@ impl BazExercise { }, ) .prop_filter("non-overlapping pointers", |e| { - non_overlapping_set(&[ + MemArea::non_overlapping_set(&[ &e.input2_loc, &e.input3_loc, &e.input4_loc, @@ -364,7 +279,7 @@ impl BazExercise { pub fn test(&self) { let mut ctx = WasiCtx::new(); let mut host_memory = HostMemory::new(); - let mut guest_memory = GuestMemory::new(host_memory.as_mut_ptr(), host_memory.len() as u32); + let mut guest_memory = host_memory.guest_memory(); *guest_memory .ptr_mut(self.input2_loc.ptr) @@ -454,7 +369,7 @@ impl SumOfPairExercise { return_loc, }) .prop_filter("non-overlapping pointers", |e| { - non_overlapping_set(&[&e.input_loc, &e.return_loc]) + MemArea::non_overlapping_set(&[&e.input_loc, &e.return_loc]) }) .boxed() } @@ -462,7 +377,7 @@ impl SumOfPairExercise { pub fn test(&self) { let mut ctx = WasiCtx::new(); let mut host_memory = HostMemory::new(); - let mut guest_memory = GuestMemory::new(host_memory.as_mut_ptr(), host_memory.len() as u32); + let mut guest_memory = host_memory.guest_memory(); *guest_memory .ptr_mut(self.input_loc.ptr) @@ -542,7 +457,7 @@ impl SumPairPtrsExercise { }, ) .prop_filter("non-overlapping pointers", |e| { - non_overlapping_set(&[ + MemArea::non_overlapping_set(&[ &e.input_first_loc, &e.input_second_loc, &e.input_struct_loc, @@ -554,7 +469,7 @@ impl SumPairPtrsExercise { pub fn test(&self) { let mut ctx = WasiCtx::new(); let mut host_memory = HostMemory::new(); - let mut guest_memory = GuestMemory::new(host_memory.as_mut_ptr(), host_memory.len() as u32); + let mut guest_memory = host_memory.guest_memory(); *guest_memory .ptr_mut(self.input_first_loc.ptr) @@ -643,7 +558,7 @@ impl ReduceExcusesExcercise { .prop_filter("non-overlapping pointers", |e| { let mut all = vec![&e.array_ptr_loc, &e.array_len_loc, &e.return_ptr_loc]; all.extend(e.excuse_ptr_locs.iter()); - non_overlapping_set(&all) + MemArea::non_overlapping_set(&all) }) .boxed() } @@ -651,7 +566,7 @@ impl ReduceExcusesExcercise { pub fn test(&self) { let mut ctx = WasiCtx::new(); let mut host_memory = HostMemory::new(); - let mut guest_memory = GuestMemory::new(host_memory.as_mut_ptr(), host_memory.len() as u32); + let mut guest_memory = host_memory.guest_memory(); // Populate memory with pointers to generated Excuse values for (&excuse, ptr) in self.excuse_values.iter().zip(self.excuse_ptr_locs.iter()) { @@ -739,7 +654,7 @@ impl PopulateExcusesExcercise { .prop_filter("non-overlapping pointers", |e| { let mut all = vec![&e.array_ptr_loc, &e.array_len_loc]; all.extend(e.elements.iter()); - non_overlapping_set(&all) + MemArea::non_overlapping_set(&all) }) .boxed() } @@ -747,7 +662,7 @@ impl PopulateExcusesExcercise { pub fn test(&self) { let mut ctx = WasiCtx::new(); let mut host_memory = HostMemory::new(); - let mut guest_memory = GuestMemory::new(host_memory.as_mut_ptr(), host_memory.len() as u32); + let mut guest_memory = host_memory.guest_memory(); // Populate array length info *guest_memory @@ -838,7 +753,7 @@ impl ConfigureCarExercise { }, ) .prop_filter("non-overlapping ptrs", |e| { - non_overlapping_set(&[&e.other_config_by_ptr, &e.return_ptr_loc]) + MemArea::non_overlapping_set(&[&e.other_config_by_ptr, &e.return_ptr_loc]) }) .boxed() } @@ -846,7 +761,7 @@ impl ConfigureCarExercise { pub fn test(&self) { let mut ctx = WasiCtx::new(); let mut host_memory = HostMemory::new(); - let mut guest_memory = GuestMemory::new(host_memory.as_mut_ptr(), host_memory.len() as u32); + let mut guest_memory = host_memory.guest_memory(); // Populate input ptr *guest_memory From 8a110e4b136c1dcfd178133f893d2e91eb27c358 Mon Sep 17 00:00:00 2001 From: Pat Hickey Date: Fri, 21 Feb 2020 13:51:44 -0800 Subject: [PATCH 48/86] first pass at splitting out a test, making ctx/errno reusable --- tests/ctx.rs | 34 ++++++++++++++++++++++++++ tests/errno.witx | 8 ++++++ tests/main.rs | 34 +++----------------------- tests/test.witx | 12 +-------- tests/trivial.rs | 61 ++++++++++++++++++++++++++++++++++++++++++++++ tests/trivial.witx | 8 ++++++ 6 files changed, 115 insertions(+), 42 deletions(-) create mode 100644 tests/ctx.rs create mode 100644 tests/errno.witx create mode 100644 tests/trivial.rs create mode 100644 tests/trivial.witx diff --git a/tests/ctx.rs b/tests/ctx.rs new file mode 100644 index 0000000000..b521dbaa40 --- /dev/null +++ b/tests/ctx.rs @@ -0,0 +1,34 @@ +use wiggle_runtime::GuestError; + +pub struct WasiCtx { + pub guest_errors: Vec, +} + +impl WasiCtx { + pub fn new() -> Self { + Self { + guest_errors: vec![], + } + } +} + +// Errno is used as a first return value in the functions above, therefore +// it must implement GuestErrorType with type Context = WasiCtx. +// The context type should let you do logging or debugging or whatever you need +// with these errors. We just push them to vecs. +#[macro_export] +macro_rules! impl_errno { + ( $errno:ty ) => { + impl wiggle_runtime::GuestErrorType for $errno { + type Context = WasiCtx; + fn success() -> $errno { + <$errno>::Ok + } + fn from_error(e: GuestError, ctx: &mut WasiCtx) -> $errno { + eprintln!("GUEST ERROR: {:?}", e); + ctx.guest_errors.push(e); + types::Errno::InvalidArg + } + } + }; +} diff --git a/tests/errno.witx b/tests/errno.witx new file mode 100644 index 0000000000..5197c2c224 --- /dev/null +++ b/tests/errno.witx @@ -0,0 +1,8 @@ +(typename $errno + (enum u32 + $ok + $invalid_arg + $dont_want_to + $physically_unable + $picket_line)) + diff --git a/tests/main.rs b/tests/main.rs index 9b09d9bf69..836a20c659 100644 --- a/tests/main.rs +++ b/tests/main.rs @@ -11,24 +11,12 @@ wiggle_generate::from_witx!({ ctx: WasiCtx, }); -pub struct WasiCtx { - guest_errors: Vec, -} +mod ctx; +use ctx::WasiCtx; -impl WasiCtx { - pub fn new() -> Self { - Self { - guest_errors: vec![], - } - } -} +impl_errno!(types::Errno); impl foo::Foo for WasiCtx { - fn bar(&mut self, an_int: u32, an_float: f32) -> Result<(), types::Errno> { - println!("BAR: {} {}", an_int, an_float); - Ok(()) - } - fn baz( &mut self, input1: types::Excuse, @@ -154,22 +142,6 @@ impl foo::Foo for WasiCtx { Ok(res) } } -// Errno is used as a first return value in the functions above, therefore -// it must implement GuestErrorType with type Context = WasiCtx. -// The context type should let you do logging or debugging or whatever you need -// with these errors. We just push them to vecs. -impl GuestErrorType for types::Errno { - type Context = WasiCtx; - fn success() -> types::Errno { - types::Errno::Ok - } - fn from_error(e: GuestError, ctx: &mut WasiCtx) -> types::Errno { - eprintln!("GUEST ERROR: {:?}", e); - ctx.guest_errors.push(e); - types::Errno::InvalidArg - } -} - #[derive(Debug)] struct BatExercise { pub input: u32, diff --git a/tests/test.witx b/tests/test.witx index 4a0930a616..78f69a3276 100644 --- a/tests/test.witx +++ b/tests/test.witx @@ -1,10 +1,4 @@ -(typename $errno - (enum u32 - $ok - $invalid_arg - $dont_want_to - $physically_unable - $picket_line)) +(use "errno.witx") (typename $excuse (enum u8 @@ -44,10 +38,6 @@ (typename $excuse_array (array (@witx pointer $excuse))) (module $foo - (@interface func (export "bar") - (param $an_int u32) - (param $an_float f32) - (result $error $errno)) (@interface func (export "baz") (param $an_excuse $excuse) (param $an_excuse_by_reference (@witx pointer $excuse)) diff --git a/tests/trivial.rs b/tests/trivial.rs new file mode 100644 index 0000000000..8eb8fdccfe --- /dev/null +++ b/tests/trivial.rs @@ -0,0 +1,61 @@ +use proptest::prelude::*; +use std::convert::TryFrom; +use wiggle_runtime::{ + GuestArray, GuestError, GuestErrorType, GuestPtr, GuestPtrMut, GuestRef, GuestRefMut, +}; +use wiggle_test::{HostMemory, MemArea}; + +mod ctx; +use ctx::WasiCtx; + +wiggle_generate::from_witx!({ + witx: ["tests/trivial.witx"], + ctx: WasiCtx, +}); + +impl_errno!(types::Errno); + +impl trivial::Trivial for WasiCtx { + fn int_float_args(&mut self, an_int: u32, an_float: f32) -> Result<(), types::Errno> { + println!("INT FLOAT ARGS: {} {}", an_int, an_float); + Ok(()) + } +} + +// There's nothing meaningful to test here - this just demonstrates the test machinery + +#[derive(Debug)] +struct IntFloatExercise { + pub an_int: u32, + pub an_float: f32, +} + +impl IntFloatExercise { + pub fn test(&self) { + let mut ctx = WasiCtx::new(); + let mut host_memory = HostMemory::new(); + let mut guest_memory = host_memory.guest_memory(); + + let e = trivial::int_float_args( + &mut ctx, + &mut guest_memory, + self.an_int as i32, + self.an_float, + ); + + assert_eq!(e, types::Errno::Ok.into(), "int_float_args error"); + } + + pub fn strat() -> BoxedStrategy { + (prop::num::u32::ANY, prop::num::f32::ANY) + .prop_map(|(an_int, an_float)| IntFloatExercise { an_int, an_float }) + .boxed() + } +} + +proptest! { + #[test] + fn int_float_exercise(e in IntFloatExercise::strat()) { + e.test() + } +} diff --git a/tests/trivial.witx b/tests/trivial.witx new file mode 100644 index 0000000000..3fbbf13e5c --- /dev/null +++ b/tests/trivial.witx @@ -0,0 +1,8 @@ +(use "errno.witx") + +(module $trivial + (@interface func (export "int_float_args") + (param $an_int u32) + (param $an_float f32) + (result $error $errno)) +) From 2feab2ee2b54ba5389c312698480b2b222664fd4 Mon Sep 17 00:00:00 2001 From: Pat Hickey Date: Fri, 21 Feb 2020 13:55:49 -0800 Subject: [PATCH 49/86] fixes --- tests/main.rs | 13 ++++++++----- tests/trivial.rs | 7 ++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/tests/main.rs b/tests/main.rs index 836a20c659..a8ec2c5096 100644 --- a/tests/main.rs +++ b/tests/main.rs @@ -1,8 +1,7 @@ use proptest::prelude::*; use std::convert::TryFrom; use wiggle_runtime::{ - GuestArray, GuestError, GuestErrorType, GuestPtr, GuestPtrMut, GuestRef, GuestRefMut, - GuestString, + GuestArray, GuestError, GuestPtr, GuestPtrMut, GuestRef, GuestRefMut, GuestString, }; use wiggle_test::{HostMemory, MemArea}; @@ -803,7 +802,11 @@ impl HelloStringExercise { }, ) .prop_filter("non-overlapping pointers", |e| { - non_overlapping_set(&[&e.string_ptr_loc, &e.string_len_loc, &e.return_ptr_loc]) + MemArea::non_overlapping_set(&[ + &e.string_ptr_loc, + &e.string_len_loc, + &e.return_ptr_loc, + ]) }) .boxed() } @@ -811,7 +814,7 @@ impl HelloStringExercise { pub fn test(&self) { let mut ctx = WasiCtx::new(); let mut host_memory = HostMemory::new(); - let mut guest_memory = GuestMemory::new(host_memory.as_mut_ptr(), host_memory.len() as u32); + let mut guest_memory = host_memory.guest_memory(); // Populate string length *guest_memory @@ -880,7 +883,7 @@ impl CookieCutterExercise { pub fn test(&self) { let mut ctx = WasiCtx::new(); let mut host_memory = HostMemory::new(); - let mut guest_memory = GuestMemory::new(host_memory.as_mut_ptr(), host_memory.len() as u32); + let mut guest_memory = host_memory.guest_memory(); let res = foo::cookie_cutter( &mut ctx, diff --git a/tests/trivial.rs b/tests/trivial.rs index 8eb8fdccfe..36ca5d3ca5 100644 --- a/tests/trivial.rs +++ b/tests/trivial.rs @@ -1,9 +1,6 @@ use proptest::prelude::*; -use std::convert::TryFrom; -use wiggle_runtime::{ - GuestArray, GuestError, GuestErrorType, GuestPtr, GuestPtrMut, GuestRef, GuestRefMut, -}; -use wiggle_test::{HostMemory, MemArea}; +use wiggle_runtime::GuestError; +use wiggle_test::HostMemory; mod ctx; use ctx::WasiCtx; From f77000ad8f18503e2b552add95b91e657fe34394 Mon Sep 17 00:00:00 2001 From: Pat Hickey Date: Fri, 21 Feb 2020 13:58:28 -0800 Subject: [PATCH 50/86] move ctx into wiggle-tests crate --- crates/test/src/lib.rs | 35 +++++++++++++++++++++++++++++++++++ tests/ctx.rs | 34 ---------------------------------- tests/main.rs | 5 +---- tests/trivial.rs | 5 +---- 4 files changed, 37 insertions(+), 42 deletions(-) delete mode 100644 tests/ctx.rs diff --git a/crates/test/src/lib.rs b/crates/test/src/lib.rs index 8b036111a3..2ff31211c1 100644 --- a/crates/test/src/lib.rs +++ b/crates/test/src/lib.rs @@ -90,3 +90,38 @@ mod test { assert_eq!(h.buffer.as_mut_ptr() as usize % 4096, 0); } } + +use wiggle_runtime::GuestError; + +pub struct WasiCtx { + pub guest_errors: Vec, +} + +impl WasiCtx { + pub fn new() -> Self { + Self { + guest_errors: vec![], + } + } +} + +// Errno is used as a first return value in the functions above, therefore +// it must implement GuestErrorType with type Context = WasiCtx. +// The context type should let you do logging or debugging or whatever you need +// with these errors. We just push them to vecs. +#[macro_export] +macro_rules! impl_errno { + ( $errno:ty ) => { + impl wiggle_runtime::GuestErrorType for $errno { + type Context = WasiCtx; + fn success() -> $errno { + <$errno>::Ok + } + fn from_error(e: GuestError, ctx: &mut WasiCtx) -> $errno { + eprintln!("GUEST ERROR: {:?}", e); + ctx.guest_errors.push(e); + types::Errno::InvalidArg + } + } + }; +} diff --git a/tests/ctx.rs b/tests/ctx.rs deleted file mode 100644 index b521dbaa40..0000000000 --- a/tests/ctx.rs +++ /dev/null @@ -1,34 +0,0 @@ -use wiggle_runtime::GuestError; - -pub struct WasiCtx { - pub guest_errors: Vec, -} - -impl WasiCtx { - pub fn new() -> Self { - Self { - guest_errors: vec![], - } - } -} - -// Errno is used as a first return value in the functions above, therefore -// it must implement GuestErrorType with type Context = WasiCtx. -// The context type should let you do logging or debugging or whatever you need -// with these errors. We just push them to vecs. -#[macro_export] -macro_rules! impl_errno { - ( $errno:ty ) => { - impl wiggle_runtime::GuestErrorType for $errno { - type Context = WasiCtx; - fn success() -> $errno { - <$errno>::Ok - } - fn from_error(e: GuestError, ctx: &mut WasiCtx) -> $errno { - eprintln!("GUEST ERROR: {:?}", e); - ctx.guest_errors.push(e); - types::Errno::InvalidArg - } - } - }; -} diff --git a/tests/main.rs b/tests/main.rs index a8ec2c5096..43254739fb 100644 --- a/tests/main.rs +++ b/tests/main.rs @@ -3,16 +3,13 @@ use std::convert::TryFrom; use wiggle_runtime::{ GuestArray, GuestError, GuestPtr, GuestPtrMut, GuestRef, GuestRefMut, GuestString, }; -use wiggle_test::{HostMemory, MemArea}; +use wiggle_test::{impl_errno, HostMemory, MemArea, WasiCtx}; wiggle_generate::from_witx!({ witx: ["tests/test.witx"], ctx: WasiCtx, }); -mod ctx; -use ctx::WasiCtx; - impl_errno!(types::Errno); impl foo::Foo for WasiCtx { diff --git a/tests/trivial.rs b/tests/trivial.rs index 36ca5d3ca5..de97dd8a05 100644 --- a/tests/trivial.rs +++ b/tests/trivial.rs @@ -1,9 +1,6 @@ use proptest::prelude::*; use wiggle_runtime::GuestError; -use wiggle_test::HostMemory; - -mod ctx; -use ctx::WasiCtx; +use wiggle_test::{impl_errno, HostMemory, WasiCtx}; wiggle_generate::from_witx!({ witx: ["tests/trivial.witx"], From b7cd003b93fffe9b17efb6519b3b2da68b31d99e Mon Sep 17 00:00:00 2001 From: Pat Hickey Date: Sat, 22 Feb 2020 01:17:27 -0800 Subject: [PATCH 51/86] finish factoring tests (#17) * atoms in one test unit * factor out pointers test * factor structs into separate test unit * factor out arrays, flags * finally, separate into strings and ints --- tests/arrays.rs | 247 ++++++++++++ tests/arrays.witx | 17 + tests/atoms.rs | 99 +++++ tests/atoms.witx | 12 + tests/excuse.witx | 6 + tests/flags.rs | 104 +++++ tests/flags.witx | 16 + tests/ints.rs | 81 ++++ tests/ints.witx | 18 + tests/main.rs | 915 -------------------------------------------- tests/pointers.rs | 197 ++++++++++ tests/pointers.witx | 11 + tests/strings.rs | 107 ++++++ tests/strings.witx | 8 + tests/structs.rs | 202 ++++++++++ tests/structs.witx | 23 ++ tests/test.witx | 84 ---- tests/trivial.rs | 55 --- tests/trivial.witx | 8 - 19 files changed, 1148 insertions(+), 1062 deletions(-) create mode 100644 tests/arrays.rs create mode 100644 tests/arrays.witx create mode 100644 tests/atoms.rs create mode 100644 tests/atoms.witx create mode 100644 tests/excuse.witx create mode 100644 tests/flags.rs create mode 100644 tests/flags.witx create mode 100644 tests/ints.rs create mode 100644 tests/ints.witx delete mode 100644 tests/main.rs create mode 100644 tests/pointers.rs create mode 100644 tests/pointers.witx create mode 100644 tests/strings.rs create mode 100644 tests/strings.witx create mode 100644 tests/structs.rs create mode 100644 tests/structs.witx delete mode 100644 tests/test.witx delete mode 100644 tests/trivial.rs delete mode 100644 tests/trivial.witx diff --git a/tests/arrays.rs b/tests/arrays.rs new file mode 100644 index 0000000000..467dd3cc27 --- /dev/null +++ b/tests/arrays.rs @@ -0,0 +1,247 @@ +use proptest::prelude::*; +use wiggle_runtime::{GuestArray, GuestError, GuestPtr, GuestPtrMut}; +use wiggle_test::{impl_errno, HostMemory, MemArea, WasiCtx}; + +wiggle_generate::from_witx!({ + witx: ["tests/arrays.witx"], + ctx: WasiCtx, +}); + +impl_errno!(types::Errno); + +impl arrays::Arrays for WasiCtx { + fn reduce_excuses( + &mut self, + excuses: &types::ConstExcuseArray, + ) -> Result { + let last = wiggle_runtime::GuestTypeClone::read_from_guest( + &excuses + .iter() + .last() + .expect("input array is non-empty") + .expect("valid ptr to ptr"), + ) + .expect("valid ptr to some Excuse value"); + Ok(*last.as_ref().expect("dereferencing ptr should succeed")) + } + + fn populate_excuses(&mut self, excuses: &types::ExcuseArray) -> Result<(), types::Errno> { + for excuse in excuses.iter() { + let ptr_to_ptr = + wiggle_runtime::GuestTypeClone::read_from_guest(&excuse.expect("valid ptr to ptr")) + .expect("valid ptr to some Excuse value"); + let mut ptr = ptr_to_ptr + .as_ref_mut() + .expect("dereferencing mut ptr should succeed"); + *ptr = types::Excuse::Sleeping; + } + Ok(()) + } +} + +#[derive(Debug)] +struct ReduceExcusesExcercise { + excuse_values: Vec, + excuse_ptr_locs: Vec, + array_ptr_loc: MemArea, + array_len_loc: MemArea, + return_ptr_loc: MemArea, +} + +impl ReduceExcusesExcercise { + pub fn strat() -> BoxedStrategy { + (1..256u32) + .prop_flat_map(|len| { + let len_usize = len as usize; + ( + proptest::collection::vec(excuse_strat(), len_usize..=len_usize), + proptest::collection::vec(HostMemory::mem_area_strat(4), len_usize..=len_usize), + HostMemory::mem_area_strat(4 * len), + HostMemory::mem_area_strat(4), + HostMemory::mem_area_strat(4), + ) + }) + .prop_map( + |(excuse_values, excuse_ptr_locs, array_ptr_loc, array_len_loc, return_ptr_loc)| { + Self { + excuse_values, + excuse_ptr_locs, + array_ptr_loc, + array_len_loc, + return_ptr_loc, + } + }, + ) + .prop_filter("non-overlapping pointers", |e| { + let mut all = vec![&e.array_ptr_loc, &e.array_len_loc, &e.return_ptr_loc]; + all.extend(e.excuse_ptr_locs.iter()); + MemArea::non_overlapping_set(&all) + }) + .boxed() + } + + pub fn test(&self) { + let mut ctx = WasiCtx::new(); + let mut host_memory = HostMemory::new(); + let mut guest_memory = host_memory.guest_memory(); + + // Populate memory with pointers to generated Excuse values + for (&excuse, ptr) in self.excuse_values.iter().zip(self.excuse_ptr_locs.iter()) { + *guest_memory + .ptr_mut(ptr.ptr) + .expect("ptr mut to Excuse value") + .as_ref_mut() + .expect("deref ptr mut to Excuse value") = excuse; + } + + // Populate array length info + *guest_memory + .ptr_mut(self.array_len_loc.ptr) + .expect("ptr to array len") + .as_ref_mut() + .expect("deref ptr mut to array len") = self.excuse_ptr_locs.len() as u32; + + // Populate the array with pointers to generated Excuse values + { + let mut next: GuestPtrMut<'_, GuestPtr> = guest_memory + .ptr_mut(self.array_ptr_loc.ptr) + .expect("ptr to array mut"); + for ptr in &self.excuse_ptr_locs { + next.write_ptr_to_guest( + &guest_memory + .ptr::(ptr.ptr) + .expect("ptr to Excuse value"), + ); + next = next.elem(1).expect("increment ptr by 1"); + } + } + + let res = arrays::reduce_excuses( + &mut ctx, + &mut guest_memory, + self.array_ptr_loc.ptr as i32, + self.array_len_loc.ptr as i32, + self.return_ptr_loc.ptr as i32, + ); + + assert_eq!(res, types::Errno::Ok.into(), "reduce excuses errno"); + + let expected = *self + .excuse_values + .last() + .expect("generated vec of excuses should be non-empty"); + let given: types::Excuse = *guest_memory + .ptr(self.return_ptr_loc.ptr) + .expect("ptr to returned value") + .as_ref() + .expect("deref ptr to returned value"); + assert_eq!(expected, given, "reduce excuses return val"); + } +} +proptest! { + #[test] + fn reduce_excuses(e in ReduceExcusesExcercise::strat()) { + e.test() + } +} + +fn excuse_strat() -> impl Strategy { + prop_oneof![ + Just(types::Excuse::DogAte), + Just(types::Excuse::Traffic), + Just(types::Excuse::Sleeping), + ] + .boxed() +} + +#[derive(Debug)] +struct PopulateExcusesExcercise { + array_ptr_loc: MemArea, + array_len_loc: MemArea, + elements: Vec, +} + +impl PopulateExcusesExcercise { + pub fn strat() -> BoxedStrategy { + (1..256u32) + .prop_flat_map(|len| { + let len_usize = len as usize; + ( + HostMemory::mem_area_strat(4 * len), + HostMemory::mem_area_strat(4), + proptest::collection::vec(HostMemory::mem_area_strat(4), len_usize..=len_usize), + ) + }) + .prop_map(|(array_ptr_loc, array_len_loc, elements)| Self { + array_ptr_loc, + array_len_loc, + elements, + }) + .prop_filter("non-overlapping pointers", |e| { + let mut all = vec![&e.array_ptr_loc, &e.array_len_loc]; + all.extend(e.elements.iter()); + MemArea::non_overlapping_set(&all) + }) + .boxed() + } + + pub fn test(&self) { + let mut ctx = WasiCtx::new(); + let mut host_memory = HostMemory::new(); + let mut guest_memory = host_memory.guest_memory(); + + // Populate array length info + *guest_memory + .ptr_mut(self.array_len_loc.ptr) + .expect("ptr mut to array len") + .as_ref_mut() + .expect("deref ptr mut to array len") = self.elements.len() as u32; + + // Populate array with valid pointers to Excuse type in memory + { + let mut next: GuestPtrMut<'_, GuestPtrMut> = guest_memory + .ptr_mut(self.array_ptr_loc.ptr) + .expect("ptr mut to the first element of array"); + for ptr in &self.elements { + next.write_ptr_to_guest( + &guest_memory + .ptr_mut::(ptr.ptr) + .expect("ptr mut to Excuse value"), + ); + next = next.elem(1).expect("increment ptr by 1"); + } + } + + let res = arrays::populate_excuses( + &mut ctx, + &mut guest_memory, + self.array_ptr_loc.ptr as i32, + self.array_len_loc.ptr as i32, + ); + assert_eq!(res, types::Errno::Ok.into(), "populate excuses errno"); + + let arr: GuestArray<'_, GuestPtr<'_, types::Excuse>> = guest_memory + .ptr(self.array_ptr_loc.ptr) + .expect("ptr to the first element of array") + .array(self.elements.len() as u32) + .expect("as array"); + for el in arr.iter() { + let ptr_to_ptr = + wiggle_runtime::GuestTypeClone::read_from_guest(&el.expect("valid ptr to ptr")) + .expect("valid ptr to some Excuse value"); + assert_eq!( + *ptr_to_ptr + .as_ref() + .expect("dereferencing ptr to some Excuse value"), + types::Excuse::Sleeping, + "element should equal Excuse::Sleeping" + ); + } + } +} +proptest! { + #[test] + fn populate_excuses(e in PopulateExcusesExcercise::strat()) { + e.test() + } +} diff --git a/tests/arrays.witx b/tests/arrays.witx new file mode 100644 index 0000000000..e8a81cee95 --- /dev/null +++ b/tests/arrays.witx @@ -0,0 +1,17 @@ +(use "errno.witx") +(use "excuse.witx") + +(typename $const_excuse_array (array (@witx const_pointer $excuse))) +(typename $excuse_array (array (@witx pointer $excuse))) + +(module $arrays + (@interface func (export "reduce_excuses") + (param $excuses $const_excuse_array) + (result $error $errno) + (result $reduced $excuse) + ) + (@interface func (export "populate_excuses") + (param $excuses $excuse_array) + (result $error $errno) + ) +) diff --git a/tests/atoms.rs b/tests/atoms.rs new file mode 100644 index 0000000000..216613766f --- /dev/null +++ b/tests/atoms.rs @@ -0,0 +1,99 @@ +use proptest::prelude::*; +use wiggle_runtime::{GuestError, GuestRef}; +use wiggle_test::{impl_errno, HostMemory, MemArea, WasiCtx}; + +wiggle_generate::from_witx!({ + witx: ["tests/atoms.witx"], + ctx: WasiCtx, +}); + +impl_errno!(types::Errno); + +impl atoms::Atoms for WasiCtx { + fn int_float_args(&mut self, an_int: u32, an_float: f32) -> Result<(), types::Errno> { + println!("INT FLOAT ARGS: {} {}", an_int, an_float); + Ok(()) + } + fn double_int_return_float(&mut self, an_int: u32) -> Result { + Ok((an_int as f32) * 2.0) + } +} + +// There's nothing meaningful to test here - this just demonstrates the test machinery + +#[derive(Debug)] +struct IntFloatExercise { + pub an_int: u32, + pub an_float: f32, +} + +impl IntFloatExercise { + pub fn test(&self) { + let mut ctx = WasiCtx::new(); + let mut host_memory = HostMemory::new(); + let mut guest_memory = host_memory.guest_memory(); + + let e = atoms::int_float_args( + &mut ctx, + &mut guest_memory, + self.an_int as i32, + self.an_float, + ); + + assert_eq!(e, types::Errno::Ok.into(), "int_float_args error"); + } + + pub fn strat() -> BoxedStrategy { + (prop::num::u32::ANY, prop::num::f32::ANY) + .prop_map(|(an_int, an_float)| IntFloatExercise { an_int, an_float }) + .boxed() + } +} + +proptest! { + #[test] + fn int_float_exercise(e in IntFloatExercise::strat()) { + e.test() + } +} +#[derive(Debug)] +struct DoubleIntExercise { + pub input: u32, + pub return_loc: MemArea, +} + +impl DoubleIntExercise { + pub fn test(&self) { + let mut ctx = WasiCtx::new(); + let mut host_memory = HostMemory::new(); + let mut guest_memory = host_memory.guest_memory(); + + let e = atoms::double_int_return_float( + &mut ctx, + &mut guest_memory, + self.input as i32, + self.return_loc.ptr as i32, + ); + + let return_val: GuestRef = guest_memory + .ptr(self.return_loc.ptr) + .expect("return loc ptr") + .as_ref() + .expect("return val ref"); + assert_eq!(e, types::Errno::Ok.into(), "errno"); + assert_eq!(*return_val, (self.input as f32) * 2.0, "return val"); + } + + pub fn strat() -> BoxedStrategy { + (prop::num::u32::ANY, HostMemory::mem_area_strat(4)) + .prop_map(|(input, return_loc)| DoubleIntExercise { input, return_loc }) + .boxed() + } +} + +proptest! { + #[test] + fn double_int_return_float(e in DoubleIntExercise::strat()) { + e.test() + } +} diff --git a/tests/atoms.witx b/tests/atoms.witx new file mode 100644 index 0000000000..ef496d7600 --- /dev/null +++ b/tests/atoms.witx @@ -0,0 +1,12 @@ +(use "errno.witx") + +(module $atoms + (@interface func (export "int_float_args") + (param $an_int u32) + (param $an_float f32) + (result $error $errno)) + (@interface func (export "double_int_return_float") + (param $an_int u32) + (result $error $errno) + (result $doubled_it f32)) +) diff --git a/tests/excuse.witx b/tests/excuse.witx new file mode 100644 index 0000000000..14a927164e --- /dev/null +++ b/tests/excuse.witx @@ -0,0 +1,6 @@ +(typename $excuse + (enum u8 + $dog_ate + $traffic + $sleeping)) + diff --git a/tests/flags.rs b/tests/flags.rs new file mode 100644 index 0000000000..0dcc16d3b4 --- /dev/null +++ b/tests/flags.rs @@ -0,0 +1,104 @@ +use proptest::prelude::*; +use std::convert::TryFrom; +use wiggle_runtime::{GuestError, GuestPtr}; +use wiggle_test::{impl_errno, HostMemory, MemArea, WasiCtx}; + +wiggle_generate::from_witx!({ + witx: ["tests/flags.witx"], + ctx: WasiCtx, +}); + +impl_errno!(types::Errno); + +impl flags::Flags for WasiCtx { + fn configure_car( + &mut self, + old_config: types::CarConfig, + other_config_ptr: GuestPtr, + ) -> Result { + let other_config = *other_config_ptr.as_ref().map_err(|e| { + eprintln!("old_config_ptr error: {}", e); + types::Errno::InvalidArg + })?; + Ok(old_config ^ other_config) + } +} + +fn car_config_strat() -> impl Strategy { + (1u8..=types::CarConfig::ALL_FLAGS.into()) + .prop_map(|v| { + types::CarConfig::try_from(v).expect("invalid value for types::CarConfig flag") + }) + .boxed() +} + +#[derive(Debug)] +struct ConfigureCarExercise { + old_config: types::CarConfig, + other_config: types::CarConfig, + other_config_by_ptr: MemArea, + return_ptr_loc: MemArea, +} + +impl ConfigureCarExercise { + pub fn strat() -> BoxedStrategy { + ( + car_config_strat(), + car_config_strat(), + HostMemory::mem_area_strat(4), + HostMemory::mem_area_strat(4), + ) + .prop_map( + |(old_config, other_config, other_config_by_ptr, return_ptr_loc)| Self { + old_config, + other_config, + other_config_by_ptr, + return_ptr_loc, + }, + ) + .prop_filter("non-overlapping ptrs", |e| { + MemArea::non_overlapping_set(&[&e.other_config_by_ptr, &e.return_ptr_loc]) + }) + .boxed() + } + + pub fn test(&self) { + let mut ctx = WasiCtx::new(); + let mut host_memory = HostMemory::new(); + let mut guest_memory = host_memory.guest_memory(); + + // Populate input ptr + *guest_memory + .ptr_mut(self.other_config_by_ptr.ptr) + .expect("ptr mut to CarConfig") + .as_ref_mut() + .expect("deref ptr mut to CarConfig") = self.other_config; + + let res = flags::configure_car( + &mut ctx, + &mut guest_memory, + self.old_config.into(), + self.other_config_by_ptr.ptr as i32, + self.return_ptr_loc.ptr as i32, + ); + assert_eq!(res, types::Errno::Ok.into(), "configure car errno"); + + let res_config = *guest_memory + .ptr::(self.return_ptr_loc.ptr) + .expect("ptr to returned CarConfig") + .as_ref() + .expect("deref to CarConfig value"); + + assert_eq!( + self.old_config ^ self.other_config, + res_config, + "returned CarConfig should be an XOR of inputs" + ); + } +} +proptest! { + #[test] + fn configure_car(e in ConfigureCarExercise::strat()) { + e.test() + } +} diff --git a/tests/flags.witx b/tests/flags.witx new file mode 100644 index 0000000000..b46f73d5b5 --- /dev/null +++ b/tests/flags.witx @@ -0,0 +1,16 @@ +(use "errno.witx") + +(typename $car_config + (flags u8 + $automatic + $awd + $suv)) + +(module $flags + (@interface func (export "configure_car") + (param $old_config $car_config) + (param $old_config_by_ptr (@witx const_pointer $car_config)) + (result $error $errno) + (result $new_config $car_config) + ) +) diff --git a/tests/ints.rs b/tests/ints.rs new file mode 100644 index 0000000000..f6a00ccc4a --- /dev/null +++ b/tests/ints.rs @@ -0,0 +1,81 @@ +use proptest::prelude::*; +use std::convert::TryFrom; +use wiggle_runtime::GuestError; +use wiggle_test::{impl_errno, HostMemory, MemArea, WasiCtx}; + +wiggle_generate::from_witx!({ + witx: ["tests/ints.witx"], + ctx: WasiCtx, +}); + +impl_errno!(types::Errno); + +impl ints::Ints for WasiCtx { + fn cookie_cutter(&mut self, init_cookie: types::Cookie) -> Result { + let res = if init_cookie == types::Cookie::START { + types::Bool::True + } else { + types::Bool::False + }; + Ok(res) + } +} + +fn cookie_strat() -> impl Strategy { + (0..std::u64::MAX) + .prop_map(|x| types::Cookie::try_from(x).expect("within range of cookie")) + .boxed() +} + +#[derive(Debug)] +struct CookieCutterExercise { + cookie: types::Cookie, + return_ptr_loc: MemArea, +} + +impl CookieCutterExercise { + pub fn strat() -> BoxedStrategy { + (cookie_strat(), HostMemory::mem_area_strat(4)) + .prop_map(|(cookie, return_ptr_loc)| Self { + cookie, + return_ptr_loc, + }) + .boxed() + } + + pub fn test(&self) { + let mut ctx = WasiCtx::new(); + let mut host_memory = HostMemory::new(); + let mut guest_memory = host_memory.guest_memory(); + + let res = ints::cookie_cutter( + &mut ctx, + &mut guest_memory, + self.cookie.into(), + self.return_ptr_loc.ptr as i32, + ); + assert_eq!(res, types::Errno::Ok.into(), "cookie cutter errno"); + + let is_cookie_start = *guest_memory + .ptr::(self.return_ptr_loc.ptr) + .expect("ptr to returned Bool") + .as_ref() + .expect("deref to Bool value"); + + assert_eq!( + if is_cookie_start == types::Bool::True { + true + } else { + false + }, + self.cookie == types::Cookie::START, + "returned Bool should test if input was Cookie::START", + ); + } +} +proptest! { + #[test] + fn cookie_cutter(e in CookieCutterExercise::strat()) { + e.test() + } +} diff --git a/tests/ints.witx b/tests/ints.witx new file mode 100644 index 0000000000..09dc62f5ec --- /dev/null +++ b/tests/ints.witx @@ -0,0 +1,18 @@ +(use "errno.witx") + +(typename $cookie + (int u64 + (const $start 0))) + +(typename $bool + (enum u8 + $false + $true)) + +(module $ints + (@interface func (export "cookie_cutter") + (param $init_cookie $cookie) + (result $error $errno) + (result $is_start $bool) + ) +) diff --git a/tests/main.rs b/tests/main.rs deleted file mode 100644 index 43254739fb..0000000000 --- a/tests/main.rs +++ /dev/null @@ -1,915 +0,0 @@ -use proptest::prelude::*; -use std::convert::TryFrom; -use wiggle_runtime::{ - GuestArray, GuestError, GuestPtr, GuestPtrMut, GuestRef, GuestRefMut, GuestString, -}; -use wiggle_test::{impl_errno, HostMemory, MemArea, WasiCtx}; - -wiggle_generate::from_witx!({ - witx: ["tests/test.witx"], - ctx: WasiCtx, -}); - -impl_errno!(types::Errno); - -impl foo::Foo for WasiCtx { - fn baz( - &mut self, - input1: types::Excuse, - input2_ptr: GuestPtrMut, - input3_ptr: GuestPtr, - input4_ptr_ptr: GuestPtrMut>, - ) -> Result<(), types::Errno> { - println!("BAZ input1 {:?}", input1); - // Read enum value from mutable: - let mut input2_ref: GuestRefMut = input2_ptr.as_ref_mut().map_err(|e| { - eprintln!("input2_ptr error: {}", e); - types::Errno::InvalidArg - })?; - let input2: types::Excuse = *input2_ref; - println!("input2 {:?}", input2); - - // Read enum value from immutable ptr: - let input3 = *input3_ptr.as_ref().map_err(|e| { - eprintln!("input3_ptr error: {}", e); - types::Errno::InvalidArg - })?; - println!("input3 {:?}", input3); - - // Write enum to mutable ptr: - *input2_ref = input3; - println!("wrote to input2_ref {:?}", input3); - - // Read ptr value from mutable ptr: - let input4_ptr: GuestPtr = wiggle_runtime::GuestTypeClone::read_from_guest( - &input4_ptr_ptr.as_immut(), - ) - .map_err(|e| { - eprintln!("input4_ptr_ptr error: {}", e); - types::Errno::InvalidArg - })?; - - // Read enum value from that ptr: - let input4: types::Excuse = *input4_ptr.as_ref().map_err(|e| { - eprintln!("input4_ptr error: {}", e); - types::Errno::InvalidArg - })?; - println!("input4 {:?}", input4); - - // Write ptr value to mutable ptr: - input4_ptr_ptr.write_ptr_to_guest(&input2_ptr.as_immut()); - - Ok(()) - } - - fn bat(&mut self, an_int: u32) -> Result { - Ok((an_int as f32) * 2.0) - } - - fn sum_of_pair(&mut self, an_pair: &types::PairInts) -> Result { - Ok(an_pair.first as i64 + an_pair.second as i64) - } - - fn sum_of_pair_of_ptrs(&mut self, an_pair: &types::PairIntPtrs) -> Result { - let first = *an_pair - .first - .as_ref() - .expect("dereferencing GuestPtr should succeed"); - let second = *an_pair - .second - .as_ref() - .expect("dereferncing GuestPtr should succeed"); - Ok(first as i64 + second as i64) - } - - fn reduce_excuses( - &mut self, - excuses: &types::ConstExcuseArray, - ) -> Result { - let last = wiggle_runtime::GuestTypeClone::read_from_guest( - &excuses - .iter() - .last() - .expect("input array is non-empty") - .expect("valid ptr to ptr"), - ) - .expect("valid ptr to some Excuse value"); - Ok(*last.as_ref().expect("dereferencing ptr should succeed")) - } - - fn populate_excuses(&mut self, excuses: &types::ExcuseArray) -> Result<(), types::Errno> { - for excuse in excuses.iter() { - let ptr_to_ptr = - wiggle_runtime::GuestTypeClone::read_from_guest(&excuse.expect("valid ptr to ptr")) - .expect("valid ptr to some Excuse value"); - let mut ptr = ptr_to_ptr - .as_ref_mut() - .expect("dereferencing mut ptr should succeed"); - *ptr = types::Excuse::Sleeping; - } - Ok(()) - } - - fn configure_car( - &mut self, - old_config: types::CarConfig, - other_config_ptr: GuestPtr, - ) -> Result { - let other_config = *other_config_ptr.as_ref().map_err(|e| { - eprintln!("old_config_ptr error: {}", e); - types::Errno::InvalidArg - })?; - Ok(old_config ^ other_config) - } - - fn hello_string(&mut self, a_string: &GuestString<'_>) -> Result { - let as_ref = a_string.as_ref().expect("deref ptr should succeed"); - let as_str = as_ref.as_str().expect("valid UTF-8 string"); - println!("a_string='{}'", as_str); - Ok(as_str.len() as u32) - } - - fn cookie_cutter(&mut self, init_cookie: types::Cookie) -> Result { - let res = if init_cookie == types::Cookie::START { - types::Bool::True - } else { - types::Bool::False - }; - Ok(res) - } -} -#[derive(Debug)] -struct BatExercise { - pub input: u32, - pub return_loc: MemArea, -} - -impl BatExercise { - pub fn test(&self) { - let mut ctx = WasiCtx::new(); - let mut host_memory = HostMemory::new(); - let mut guest_memory = host_memory.guest_memory(); - - let bat_err = foo::bat( - &mut ctx, - &mut guest_memory, - self.input as i32, - self.return_loc.ptr as i32, - ); - - let return_val: GuestRef = guest_memory - .ptr(self.return_loc.ptr) - .expect("return loc ptr") - .as_ref() - .expect("return val ref"); - assert_eq!(bat_err, types::Errno::Ok.into(), "bat errno"); - assert_eq!(*return_val, (self.input as f32) * 2.0, "bat return val"); - } - - pub fn strat() -> BoxedStrategy { - (prop::num::u32::ANY, HostMemory::mem_area_strat(4)) - .prop_map(|(input, return_loc)| BatExercise { input, return_loc }) - .boxed() - } -} - -proptest! { - #[test] - fn bat(e in BatExercise::strat()) { - e.test() - } -} - -fn excuse_strat() -> impl Strategy { - prop_oneof![ - Just(types::Excuse::DogAte), - Just(types::Excuse::Traffic), - Just(types::Excuse::Sleeping), - ] - .boxed() -} - -#[derive(Debug)] -struct BazExercise { - pub input1: types::Excuse, - pub input2: types::Excuse, - pub input2_loc: MemArea, - pub input3: types::Excuse, - pub input3_loc: MemArea, - pub input4: types::Excuse, - pub input4_loc: MemArea, - pub input4_ptr_loc: MemArea, -} - -impl BazExercise { - pub fn strat() -> BoxedStrategy { - ( - excuse_strat(), - excuse_strat(), - HostMemory::mem_area_strat(4), - excuse_strat(), - HostMemory::mem_area_strat(4), - excuse_strat(), - HostMemory::mem_area_strat(4), - HostMemory::mem_area_strat(4), - ) - .prop_map( - |( - input1, - input2, - input2_loc, - input3, - input3_loc, - input4, - input4_loc, - input4_ptr_loc, - )| BazExercise { - input1, - input2, - input2_loc, - input3, - input3_loc, - input4, - input4_loc, - input4_ptr_loc, - }, - ) - .prop_filter("non-overlapping pointers", |e| { - MemArea::non_overlapping_set(&[ - &e.input2_loc, - &e.input3_loc, - &e.input4_loc, - &e.input4_ptr_loc, - ]) - }) - .boxed() - } - pub fn test(&self) { - let mut ctx = WasiCtx::new(); - let mut host_memory = HostMemory::new(); - let mut guest_memory = host_memory.guest_memory(); - - *guest_memory - .ptr_mut(self.input2_loc.ptr) - .expect("input2 ptr") - .as_ref_mut() - .expect("input2 ref_mut") = self.input2; - - *guest_memory - .ptr_mut(self.input3_loc.ptr) - .expect("input3 ptr") - .as_ref_mut() - .expect("input3 ref_mut") = self.input3; - - *guest_memory - .ptr_mut(self.input4_loc.ptr) - .expect("input4 ptr") - .as_ref_mut() - .expect("input4 ref_mut") = self.input4; - - *guest_memory - .ptr_mut(self.input4_ptr_loc.ptr) - .expect("input4 ptr ptr") - .as_ref_mut() - .expect("input4 ptr ref_mut") = self.input4_loc.ptr; - - let baz_err = foo::baz( - &mut ctx, - &mut guest_memory, - self.input1.into(), - self.input2_loc.ptr as i32, - self.input3_loc.ptr as i32, - self.input4_ptr_loc.ptr as i32, - ); - assert_eq!(baz_err, types::Errno::Ok.into(), "baz errno"); - - // Implementation of baz writes input3 to the input2_loc: - let written_to_input2_loc: i32 = *guest_memory - .ptr(self.input2_loc.ptr) - .expect("input2 ptr") - .as_ref() - .expect("input2 ref"); - - assert_eq!( - written_to_input2_loc, - self.input3.into(), - "baz written to input2" - ); - - // Implementation of baz writes input2_loc to input4_ptr_loc: - let written_to_input4_ptr: u32 = *guest_memory - .ptr(self.input4_ptr_loc.ptr) - .expect("input4_ptr_loc ptr") - .as_ref() - .expect("input4_ptr_loc ref"); - - assert_eq!( - written_to_input4_ptr, self.input2_loc.ptr, - "baz written to input4_ptr" - ); - } -} -proptest! { - #[test] - fn baz(e in BazExercise::strat()) { - e.test(); - } -} - -#[derive(Debug)] -struct SumOfPairExercise { - pub input: types::PairInts, - pub input_loc: MemArea, - pub return_loc: MemArea, -} - -impl SumOfPairExercise { - pub fn strat() -> BoxedStrategy { - ( - prop::num::i32::ANY, - prop::num::i32::ANY, - HostMemory::mem_area_strat(8), - HostMemory::mem_area_strat(8), - ) - .prop_map(|(first, second, input_loc, return_loc)| SumOfPairExercise { - input: types::PairInts { first, second }, - input_loc, - return_loc, - }) - .prop_filter("non-overlapping pointers", |e| { - MemArea::non_overlapping_set(&[&e.input_loc, &e.return_loc]) - }) - .boxed() - } - - pub fn test(&self) { - let mut ctx = WasiCtx::new(); - let mut host_memory = HostMemory::new(); - let mut guest_memory = host_memory.guest_memory(); - - *guest_memory - .ptr_mut(self.input_loc.ptr) - .expect("input ptr") - .as_ref_mut() - .expect("input ref_mut") = self.input.first; - *guest_memory - .ptr_mut(self.input_loc.ptr + 4) - .expect("input ptr") - .as_ref_mut() - .expect("input ref_mut") = self.input.second; - let sum_err = foo::sum_of_pair( - &mut ctx, - &mut guest_memory, - self.input_loc.ptr as i32, - self.return_loc.ptr as i32, - ); - - assert_eq!(sum_err, types::Errno::Ok.into(), "sum errno"); - - let return_val: i64 = *guest_memory - .ptr(self.return_loc.ptr) - .expect("return ptr") - .as_ref() - .expect("return ref"); - - assert_eq!( - return_val, - self.input.first as i64 + self.input.second as i64, - "sum return value" - ); - } -} - -proptest! { - #[test] - fn sum_of_pair(e in SumOfPairExercise::strat()) { - e.test(); - } -} - -#[derive(Debug)] -struct SumPairPtrsExercise { - input_first: i32, - input_second: i32, - input_first_loc: MemArea, - input_second_loc: MemArea, - input_struct_loc: MemArea, - return_loc: MemArea, -} - -impl SumPairPtrsExercise { - pub fn strat() -> BoxedStrategy { - ( - prop::num::i32::ANY, - prop::num::i32::ANY, - HostMemory::mem_area_strat(4), - HostMemory::mem_area_strat(4), - HostMemory::mem_area_strat(8), - HostMemory::mem_area_strat(8), - ) - .prop_map( - |( - input_first, - input_second, - input_first_loc, - input_second_loc, - input_struct_loc, - return_loc, - )| SumPairPtrsExercise { - input_first, - input_second, - input_first_loc, - input_second_loc, - input_struct_loc, - return_loc, - }, - ) - .prop_filter("non-overlapping pointers", |e| { - MemArea::non_overlapping_set(&[ - &e.input_first_loc, - &e.input_second_loc, - &e.input_struct_loc, - &e.return_loc, - ]) - }) - .boxed() - } - pub fn test(&self) { - let mut ctx = WasiCtx::new(); - let mut host_memory = HostMemory::new(); - let mut guest_memory = host_memory.guest_memory(); - - *guest_memory - .ptr_mut(self.input_first_loc.ptr) - .expect("input_first ptr") - .as_ref_mut() - .expect("input_first ref") = self.input_first; - *guest_memory - .ptr_mut(self.input_second_loc.ptr) - .expect("input_second ptr") - .as_ref_mut() - .expect("input_second ref") = self.input_second; - - *guest_memory - .ptr_mut(self.input_struct_loc.ptr) - .expect("input_struct ptr") - .as_ref_mut() - .expect("input_struct ref") = self.input_first_loc.ptr; - *guest_memory - .ptr_mut(self.input_struct_loc.ptr + 4) - .expect("input_struct ptr") - .as_ref_mut() - .expect("input_struct ref") = self.input_second_loc.ptr; - - let res = foo::sum_of_pair_of_ptrs( - &mut ctx, - &mut guest_memory, - self.input_struct_loc.ptr as i32, - self.return_loc.ptr as i32, - ); - - assert_eq!(res, types::Errno::Ok.into(), "sum of pair of ptrs errno"); - - let doubled: i64 = *guest_memory - .ptr(self.return_loc.ptr) - .expect("return ptr") - .as_ref() - .expect("return ref"); - - assert_eq!( - doubled, - (self.input_first as i64) + (self.input_second as i64), - "sum of pair of ptrs return val" - ); - } -} -proptest! { - #[test] - fn sum_of_pair_of_ptrs(e in SumPairPtrsExercise::strat()) { - e.test() - } -} - -#[derive(Debug)] -struct ReduceExcusesExcercise { - excuse_values: Vec, - excuse_ptr_locs: Vec, - array_ptr_loc: MemArea, - array_len_loc: MemArea, - return_ptr_loc: MemArea, -} - -impl ReduceExcusesExcercise { - pub fn strat() -> BoxedStrategy { - (1..256u32) - .prop_flat_map(|len| { - let len_usize = len as usize; - ( - proptest::collection::vec(excuse_strat(), len_usize..=len_usize), - proptest::collection::vec(HostMemory::mem_area_strat(4), len_usize..=len_usize), - HostMemory::mem_area_strat(4 * len), - HostMemory::mem_area_strat(4), - HostMemory::mem_area_strat(4), - ) - }) - .prop_map( - |(excuse_values, excuse_ptr_locs, array_ptr_loc, array_len_loc, return_ptr_loc)| { - Self { - excuse_values, - excuse_ptr_locs, - array_ptr_loc, - array_len_loc, - return_ptr_loc, - } - }, - ) - .prop_filter("non-overlapping pointers", |e| { - let mut all = vec![&e.array_ptr_loc, &e.array_len_loc, &e.return_ptr_loc]; - all.extend(e.excuse_ptr_locs.iter()); - MemArea::non_overlapping_set(&all) - }) - .boxed() - } - - pub fn test(&self) { - let mut ctx = WasiCtx::new(); - let mut host_memory = HostMemory::new(); - let mut guest_memory = host_memory.guest_memory(); - - // Populate memory with pointers to generated Excuse values - for (&excuse, ptr) in self.excuse_values.iter().zip(self.excuse_ptr_locs.iter()) { - *guest_memory - .ptr_mut(ptr.ptr) - .expect("ptr mut to Excuse value") - .as_ref_mut() - .expect("deref ptr mut to Excuse value") = excuse; - } - - // Populate array length info - *guest_memory - .ptr_mut(self.array_len_loc.ptr) - .expect("ptr to array len") - .as_ref_mut() - .expect("deref ptr mut to array len") = self.excuse_ptr_locs.len() as u32; - - // Populate the array with pointers to generated Excuse values - { - let mut next: GuestPtrMut<'_, GuestPtr> = guest_memory - .ptr_mut(self.array_ptr_loc.ptr) - .expect("ptr to array mut"); - for ptr in &self.excuse_ptr_locs { - next.write_ptr_to_guest( - &guest_memory - .ptr::(ptr.ptr) - .expect("ptr to Excuse value"), - ); - next = next.elem(1).expect("increment ptr by 1"); - } - } - - let res = foo::reduce_excuses( - &mut ctx, - &mut guest_memory, - self.array_ptr_loc.ptr as i32, - self.array_len_loc.ptr as i32, - self.return_ptr_loc.ptr as i32, - ); - - assert_eq!(res, types::Errno::Ok.into(), "reduce excuses errno"); - - let expected = *self - .excuse_values - .last() - .expect("generated vec of excuses should be non-empty"); - let given: types::Excuse = *guest_memory - .ptr(self.return_ptr_loc.ptr) - .expect("ptr to returned value") - .as_ref() - .expect("deref ptr to returned value"); - assert_eq!(expected, given, "reduce excuses return val"); - } -} -proptest! { - #[test] - fn reduce_excuses(e in ReduceExcusesExcercise::strat()) { - e.test() - } -} - -#[derive(Debug)] -struct PopulateExcusesExcercise { - array_ptr_loc: MemArea, - array_len_loc: MemArea, - elements: Vec, -} - -impl PopulateExcusesExcercise { - pub fn strat() -> BoxedStrategy { - (1..256u32) - .prop_flat_map(|len| { - let len_usize = len as usize; - ( - HostMemory::mem_area_strat(4 * len), - HostMemory::mem_area_strat(4), - proptest::collection::vec(HostMemory::mem_area_strat(4), len_usize..=len_usize), - ) - }) - .prop_map(|(array_ptr_loc, array_len_loc, elements)| Self { - array_ptr_loc, - array_len_loc, - elements, - }) - .prop_filter("non-overlapping pointers", |e| { - let mut all = vec![&e.array_ptr_loc, &e.array_len_loc]; - all.extend(e.elements.iter()); - MemArea::non_overlapping_set(&all) - }) - .boxed() - } - - pub fn test(&self) { - let mut ctx = WasiCtx::new(); - let mut host_memory = HostMemory::new(); - let mut guest_memory = host_memory.guest_memory(); - - // Populate array length info - *guest_memory - .ptr_mut(self.array_len_loc.ptr) - .expect("ptr mut to array len") - .as_ref_mut() - .expect("deref ptr mut to array len") = self.elements.len() as u32; - - // Populate array with valid pointers to Excuse type in memory - { - let mut next: GuestPtrMut<'_, GuestPtrMut> = guest_memory - .ptr_mut(self.array_ptr_loc.ptr) - .expect("ptr mut to the first element of array"); - for ptr in &self.elements { - next.write_ptr_to_guest( - &guest_memory - .ptr_mut::(ptr.ptr) - .expect("ptr mut to Excuse value"), - ); - next = next.elem(1).expect("increment ptr by 1"); - } - } - - let res = foo::populate_excuses( - &mut ctx, - &mut guest_memory, - self.array_ptr_loc.ptr as i32, - self.array_len_loc.ptr as i32, - ); - assert_eq!(res, types::Errno::Ok.into(), "populate excuses errno"); - - let arr: GuestArray<'_, GuestPtr<'_, types::Excuse>> = guest_memory - .ptr(self.array_ptr_loc.ptr) - .expect("ptr to the first element of array") - .array(self.elements.len() as u32) - .expect("as array"); - for el in arr.iter() { - let ptr_to_ptr = - wiggle_runtime::GuestTypeClone::read_from_guest(&el.expect("valid ptr to ptr")) - .expect("valid ptr to some Excuse value"); - assert_eq!( - *ptr_to_ptr - .as_ref() - .expect("dereferencing ptr to some Excuse value"), - types::Excuse::Sleeping, - "element should equal Excuse::Sleeping" - ); - } - } -} -proptest! { - #[test] - fn populate_excuses(e in PopulateExcusesExcercise::strat()) { - e.test() - } -} - -fn car_config_strat() -> impl Strategy { - (1u8..=types::CarConfig::ALL_FLAGS.into()) - .prop_map(|v| { - types::CarConfig::try_from(v).expect("invalid value for types::CarConfig flag") - }) - .boxed() -} - -#[derive(Debug)] -struct ConfigureCarExercise { - old_config: types::CarConfig, - other_config: types::CarConfig, - other_config_by_ptr: MemArea, - return_ptr_loc: MemArea, -} - -impl ConfigureCarExercise { - pub fn strat() -> BoxedStrategy { - ( - car_config_strat(), - car_config_strat(), - HostMemory::mem_area_strat(4), - HostMemory::mem_area_strat(4), - ) - .prop_map( - |(old_config, other_config, other_config_by_ptr, return_ptr_loc)| Self { - old_config, - other_config, - other_config_by_ptr, - return_ptr_loc, - }, - ) - .prop_filter("non-overlapping ptrs", |e| { - MemArea::non_overlapping_set(&[&e.other_config_by_ptr, &e.return_ptr_loc]) - }) - .boxed() - } - - pub fn test(&self) { - let mut ctx = WasiCtx::new(); - let mut host_memory = HostMemory::new(); - let mut guest_memory = host_memory.guest_memory(); - - // Populate input ptr - *guest_memory - .ptr_mut(self.other_config_by_ptr.ptr) - .expect("ptr mut to CarConfig") - .as_ref_mut() - .expect("deref ptr mut to CarConfig") = self.other_config; - - let res = foo::configure_car( - &mut ctx, - &mut guest_memory, - self.old_config.into(), - self.other_config_by_ptr.ptr as i32, - self.return_ptr_loc.ptr as i32, - ); - assert_eq!(res, types::Errno::Ok.into(), "configure car errno"); - - let res_config = *guest_memory - .ptr::(self.return_ptr_loc.ptr) - .expect("ptr to returned CarConfig") - .as_ref() - .expect("deref to CarConfig value"); - - assert_eq!( - self.old_config ^ self.other_config, - res_config, - "returned CarConfig should be an XOR of inputs" - ); - } -} -proptest! { - #[test] - fn configure_car(e in ConfigureCarExercise::strat()) { - e.test() - } -} - -fn test_string_strategy() -> impl Strategy { - "\\p{Greek}{1,256}" -} - -#[derive(Debug)] -struct HelloStringExercise { - test_word: String, - string_ptr_loc: MemArea, - string_len_loc: MemArea, - return_ptr_loc: MemArea, -} - -impl HelloStringExercise { - pub fn strat() -> BoxedStrategy { - (test_string_strategy(),) - .prop_flat_map(|(test_word,)| { - ( - Just(test_word.clone()), - HostMemory::mem_area_strat(test_word.len() as u32), - HostMemory::mem_area_strat(4), - HostMemory::mem_area_strat(4), - ) - }) - .prop_map( - |(test_word, string_ptr_loc, string_len_loc, return_ptr_loc)| Self { - test_word, - string_ptr_loc, - string_len_loc, - return_ptr_loc, - }, - ) - .prop_filter("non-overlapping pointers", |e| { - MemArea::non_overlapping_set(&[ - &e.string_ptr_loc, - &e.string_len_loc, - &e.return_ptr_loc, - ]) - }) - .boxed() - } - - pub fn test(&self) { - let mut ctx = WasiCtx::new(); - let mut host_memory = HostMemory::new(); - let mut guest_memory = host_memory.guest_memory(); - - // Populate string length - *guest_memory - .ptr_mut(self.string_len_loc.ptr) - .expect("ptr mut to string len") - .as_ref_mut() - .expect("deref ptr mut to string len") = self.test_word.len() as u32; - - // Populate string in guest's memory - { - let mut next: GuestPtrMut<'_, u8> = guest_memory - .ptr_mut(self.string_ptr_loc.ptr) - .expect("ptr mut to the first byte of string"); - for byte in self.test_word.as_bytes() { - *next.as_ref_mut().expect("deref mut") = *byte; - next = next.elem(1).expect("increment ptr by 1"); - } - } - - let res = foo::hello_string( - &mut ctx, - &mut guest_memory, - self.string_ptr_loc.ptr as i32, - self.string_len_loc.ptr as i32, - self.return_ptr_loc.ptr as i32, - ); - assert_eq!(res, types::Errno::Ok.into(), "hello string errno"); - - let given = *guest_memory - .ptr::(self.return_ptr_loc.ptr) - .expect("ptr to return value") - .as_ref() - .expect("deref ptr to return value"); - assert_eq!(self.test_word.len() as u32, given); - } -} -proptest! { - #[test] - fn hello_string(e in HelloStringExercise::strat()) { - e.test() - } -} - -fn cookie_strat() -> impl Strategy { - (0..std::u64::MAX) - .prop_map(|x| types::Cookie::try_from(x).expect("within range of cookie")) - .boxed() -} - -#[derive(Debug)] -struct CookieCutterExercise { - cookie: types::Cookie, - return_ptr_loc: MemArea, -} - -impl CookieCutterExercise { - pub fn strat() -> BoxedStrategy { - (cookie_strat(), HostMemory::mem_area_strat(4)) - .prop_map(|(cookie, return_ptr_loc)| Self { - cookie, - return_ptr_loc, - }) - .boxed() - } - - pub fn test(&self) { - let mut ctx = WasiCtx::new(); - let mut host_memory = HostMemory::new(); - let mut guest_memory = host_memory.guest_memory(); - - let res = foo::cookie_cutter( - &mut ctx, - &mut guest_memory, - self.cookie.into(), - self.return_ptr_loc.ptr as i32, - ); - assert_eq!(res, types::Errno::Ok.into(), "cookie cutter errno"); - - let is_cookie_start = *guest_memory - .ptr::(self.return_ptr_loc.ptr) - .expect("ptr to returned Bool") - .as_ref() - .expect("deref to Bool value"); - - assert_eq!( - if is_cookie_start == types::Bool::True { - true - } else { - false - }, - self.cookie == types::Cookie::START, - "returned Bool should test if input was Cookie::START", - ); - } -} -proptest! { - #[test] - fn cookie_cutter(e in CookieCutterExercise::strat()) { - e.test() - } -} diff --git a/tests/pointers.rs b/tests/pointers.rs new file mode 100644 index 0000000000..a85e11ec9f --- /dev/null +++ b/tests/pointers.rs @@ -0,0 +1,197 @@ +use proptest::prelude::*; +use wiggle_runtime::{GuestError, GuestPtr, GuestPtrMut, GuestRefMut}; +use wiggle_test::{impl_errno, HostMemory, MemArea, WasiCtx}; + +wiggle_generate::from_witx!({ + witx: ["tests/pointers.witx"], + ctx: WasiCtx, +}); + +impl_errno!(types::Errno); + +impl pointers::Pointers for WasiCtx { + fn pointers_and_enums( + &mut self, + input1: types::Excuse, + input2_ptr: GuestPtrMut, + input3_ptr: GuestPtr, + input4_ptr_ptr: GuestPtrMut>, + ) -> Result<(), types::Errno> { + println!("BAZ input1 {:?}", input1); + // Read enum value from mutable: + let mut input2_ref: GuestRefMut = input2_ptr.as_ref_mut().map_err(|e| { + eprintln!("input2_ptr error: {}", e); + types::Errno::InvalidArg + })?; + let input2: types::Excuse = *input2_ref; + println!("input2 {:?}", input2); + + // Read enum value from immutable ptr: + let input3 = *input3_ptr.as_ref().map_err(|e| { + eprintln!("input3_ptr error: {}", e); + types::Errno::InvalidArg + })?; + println!("input3 {:?}", input3); + + // Write enum to mutable ptr: + *input2_ref = input3; + println!("wrote to input2_ref {:?}", input3); + + // Read ptr value from mutable ptr: + let input4_ptr: GuestPtr = wiggle_runtime::GuestTypeClone::read_from_guest( + &input4_ptr_ptr.as_immut(), + ) + .map_err(|e| { + eprintln!("input4_ptr_ptr error: {}", e); + types::Errno::InvalidArg + })?; + + // Read enum value from that ptr: + let input4: types::Excuse = *input4_ptr.as_ref().map_err(|e| { + eprintln!("input4_ptr error: {}", e); + types::Errno::InvalidArg + })?; + println!("input4 {:?}", input4); + + // Write ptr value to mutable ptr: + input4_ptr_ptr.write_ptr_to_guest(&input2_ptr.as_immut()); + + Ok(()) + } +} + +fn excuse_strat() -> impl Strategy { + prop_oneof![ + Just(types::Excuse::DogAte), + Just(types::Excuse::Traffic), + Just(types::Excuse::Sleeping), + ] + .boxed() +} + +#[derive(Debug)] +struct PointersAndEnumsExercise { + pub input1: types::Excuse, + pub input2: types::Excuse, + pub input2_loc: MemArea, + pub input3: types::Excuse, + pub input3_loc: MemArea, + pub input4: types::Excuse, + pub input4_loc: MemArea, + pub input4_ptr_loc: MemArea, +} + +impl PointersAndEnumsExercise { + pub fn strat() -> BoxedStrategy { + ( + excuse_strat(), + excuse_strat(), + HostMemory::mem_area_strat(4), + excuse_strat(), + HostMemory::mem_area_strat(4), + excuse_strat(), + HostMemory::mem_area_strat(4), + HostMemory::mem_area_strat(4), + ) + .prop_map( + |( + input1, + input2, + input2_loc, + input3, + input3_loc, + input4, + input4_loc, + input4_ptr_loc, + )| PointersAndEnumsExercise { + input1, + input2, + input2_loc, + input3, + input3_loc, + input4, + input4_loc, + input4_ptr_loc, + }, + ) + .prop_filter("non-overlapping pointers", |e| { + MemArea::non_overlapping_set(&[ + &e.input2_loc, + &e.input3_loc, + &e.input4_loc, + &e.input4_ptr_loc, + ]) + }) + .boxed() + } + pub fn test(&self) { + let mut ctx = WasiCtx::new(); + let mut host_memory = HostMemory::new(); + let mut guest_memory = host_memory.guest_memory(); + + *guest_memory + .ptr_mut(self.input2_loc.ptr) + .expect("input2 ptr") + .as_ref_mut() + .expect("input2 ref_mut") = self.input2; + + *guest_memory + .ptr_mut(self.input3_loc.ptr) + .expect("input3 ptr") + .as_ref_mut() + .expect("input3 ref_mut") = self.input3; + + *guest_memory + .ptr_mut(self.input4_loc.ptr) + .expect("input4 ptr") + .as_ref_mut() + .expect("input4 ref_mut") = self.input4; + + *guest_memory + .ptr_mut(self.input4_ptr_loc.ptr) + .expect("input4 ptr ptr") + .as_ref_mut() + .expect("input4 ptr ref_mut") = self.input4_loc.ptr; + + let e = pointers::pointers_and_enums( + &mut ctx, + &mut guest_memory, + self.input1.into(), + self.input2_loc.ptr as i32, + self.input3_loc.ptr as i32, + self.input4_ptr_loc.ptr as i32, + ); + assert_eq!(e, types::Errno::Ok.into(), "errno"); + + // Implementation of pointers_and_enums writes input3 to the input2_loc: + let written_to_input2_loc: i32 = *guest_memory + .ptr(self.input2_loc.ptr) + .expect("input2 ptr") + .as_ref() + .expect("input2 ref"); + + assert_eq!( + written_to_input2_loc, + self.input3.into(), + "pointers_and_enums written to input2" + ); + + // Implementation of pointers_and_enums writes input2_loc to input4_ptr_loc: + let written_to_input4_ptr: u32 = *guest_memory + .ptr(self.input4_ptr_loc.ptr) + .expect("input4_ptr_loc ptr") + .as_ref() + .expect("input4_ptr_loc ref"); + + assert_eq!( + written_to_input4_ptr, self.input2_loc.ptr, + "pointers_and_enums written to input4_ptr" + ); + } +} +proptest! { + #[test] + fn pointers_and_enums(e in PointersAndEnumsExercise::strat()) { + e.test(); + } +} diff --git a/tests/pointers.witx b/tests/pointers.witx new file mode 100644 index 0000000000..9a73a37520 --- /dev/null +++ b/tests/pointers.witx @@ -0,0 +1,11 @@ +(use "errno.witx") +(use "excuse.witx") + +(module $pointers + (@interface func (export "pointers_and_enums") + (param $an_excuse $excuse) + (param $an_excuse_by_reference (@witx pointer $excuse)) + (param $a_lamer_excuse (@witx const_pointer $excuse)) + (param $two_layers_of_excuses (@witx pointer (@witx const_pointer $excuse))) + (result $error $errno)) +) diff --git a/tests/strings.rs b/tests/strings.rs new file mode 100644 index 0000000000..f25c8dc9e6 --- /dev/null +++ b/tests/strings.rs @@ -0,0 +1,107 @@ +use proptest::prelude::*; +use wiggle_runtime::{GuestError, GuestPtrMut, GuestString}; +use wiggle_test::{impl_errno, HostMemory, MemArea, WasiCtx}; + +wiggle_generate::from_witx!({ + witx: ["tests/strings.witx"], + ctx: WasiCtx, +}); + +impl_errno!(types::Errno); + +impl strings::Strings for WasiCtx { + fn hello_string(&mut self, a_string: &GuestString<'_>) -> Result { + let as_ref = a_string.as_ref().expect("deref ptr should succeed"); + let as_str = as_ref.as_str().expect("valid UTF-8 string"); + println!("a_string='{}'", as_str); + Ok(as_str.len() as u32) + } +} + +fn test_string_strategy() -> impl Strategy { + "\\p{Greek}{1,256}" +} + +#[derive(Debug)] +struct HelloStringExercise { + test_word: String, + string_ptr_loc: MemArea, + string_len_loc: MemArea, + return_ptr_loc: MemArea, +} + +impl HelloStringExercise { + pub fn strat() -> BoxedStrategy { + (test_string_strategy(),) + .prop_flat_map(|(test_word,)| { + ( + Just(test_word.clone()), + HostMemory::mem_area_strat(test_word.len() as u32), + HostMemory::mem_area_strat(4), + HostMemory::mem_area_strat(4), + ) + }) + .prop_map( + |(test_word, string_ptr_loc, string_len_loc, return_ptr_loc)| Self { + test_word, + string_ptr_loc, + string_len_loc, + return_ptr_loc, + }, + ) + .prop_filter("non-overlapping pointers", |e| { + MemArea::non_overlapping_set(&[ + &e.string_ptr_loc, + &e.string_len_loc, + &e.return_ptr_loc, + ]) + }) + .boxed() + } + + pub fn test(&self) { + let mut ctx = WasiCtx::new(); + let mut host_memory = HostMemory::new(); + let mut guest_memory = host_memory.guest_memory(); + + // Populate string length + *guest_memory + .ptr_mut(self.string_len_loc.ptr) + .expect("ptr mut to string len") + .as_ref_mut() + .expect("deref ptr mut to string len") = self.test_word.len() as u32; + + // Populate string in guest's memory + { + let mut next: GuestPtrMut<'_, u8> = guest_memory + .ptr_mut(self.string_ptr_loc.ptr) + .expect("ptr mut to the first byte of string"); + for byte in self.test_word.as_bytes() { + *next.as_ref_mut().expect("deref mut") = *byte; + next = next.elem(1).expect("increment ptr by 1"); + } + } + + let res = strings::hello_string( + &mut ctx, + &mut guest_memory, + self.string_ptr_loc.ptr as i32, + self.string_len_loc.ptr as i32, + self.return_ptr_loc.ptr as i32, + ); + assert_eq!(res, types::Errno::Ok.into(), "hello string errno"); + + let given = *guest_memory + .ptr::(self.return_ptr_loc.ptr) + .expect("ptr to return value") + .as_ref() + .expect("deref ptr to return value"); + assert_eq!(self.test_word.len() as u32, given); + } +} +proptest! { + #[test] + fn hello_string(e in HelloStringExercise::strat()) { + e.test() + } +} diff --git a/tests/strings.witx b/tests/strings.witx new file mode 100644 index 0000000000..ebc0f8bf05 --- /dev/null +++ b/tests/strings.witx @@ -0,0 +1,8 @@ +(use "errno.witx") +(module $strings + (@interface func (export "hello_string") + (param $a_string string) + (result $error $errno) + (result $total_bytes u32) + ) +) diff --git a/tests/structs.rs b/tests/structs.rs new file mode 100644 index 0000000000..c2e4263dad --- /dev/null +++ b/tests/structs.rs @@ -0,0 +1,202 @@ +use proptest::prelude::*; +use wiggle_runtime::GuestError; +use wiggle_test::{impl_errno, HostMemory, MemArea, WasiCtx}; + +wiggle_generate::from_witx!({ + witx: ["tests/structs.witx"], + ctx: WasiCtx, +}); + +impl_errno!(types::Errno); + +impl structs::Structs for WasiCtx { + fn sum_of_pair(&mut self, an_pair: &types::PairInts) -> Result { + Ok(an_pair.first as i64 + an_pair.second as i64) + } + + fn sum_of_pair_of_ptrs(&mut self, an_pair: &types::PairIntPtrs) -> Result { + let first = *an_pair + .first + .as_ref() + .expect("dereferencing GuestPtr should succeed"); + let second = *an_pair + .second + .as_ref() + .expect("dereferncing GuestPtr should succeed"); + Ok(first as i64 + second as i64) + } +} + +#[derive(Debug)] +struct SumOfPairExercise { + pub input: types::PairInts, + pub input_loc: MemArea, + pub return_loc: MemArea, +} + +impl SumOfPairExercise { + pub fn strat() -> BoxedStrategy { + ( + prop::num::i32::ANY, + prop::num::i32::ANY, + HostMemory::mem_area_strat(8), + HostMemory::mem_area_strat(8), + ) + .prop_map(|(first, second, input_loc, return_loc)| SumOfPairExercise { + input: types::PairInts { first, second }, + input_loc, + return_loc, + }) + .prop_filter("non-overlapping pointers", |e| { + MemArea::non_overlapping_set(&[&e.input_loc, &e.return_loc]) + }) + .boxed() + } + + pub fn test(&self) { + let mut ctx = WasiCtx::new(); + let mut host_memory = HostMemory::new(); + let mut guest_memory = host_memory.guest_memory(); + + *guest_memory + .ptr_mut(self.input_loc.ptr) + .expect("input ptr") + .as_ref_mut() + .expect("input ref_mut") = self.input.first; + *guest_memory + .ptr_mut(self.input_loc.ptr + 4) + .expect("input ptr") + .as_ref_mut() + .expect("input ref_mut") = self.input.second; + let sum_err = structs::sum_of_pair( + &mut ctx, + &mut guest_memory, + self.input_loc.ptr as i32, + self.return_loc.ptr as i32, + ); + + assert_eq!(sum_err, types::Errno::Ok.into(), "sum errno"); + + let return_val: i64 = *guest_memory + .ptr(self.return_loc.ptr) + .expect("return ptr") + .as_ref() + .expect("return ref"); + + assert_eq!( + return_val, + self.input.first as i64 + self.input.second as i64, + "sum return value" + ); + } +} + +proptest! { + #[test] + fn sum_of_pair(e in SumOfPairExercise::strat()) { + e.test(); + } +} + +#[derive(Debug)] +struct SumPairPtrsExercise { + input_first: i32, + input_second: i32, + input_first_loc: MemArea, + input_second_loc: MemArea, + input_struct_loc: MemArea, + return_loc: MemArea, +} + +impl SumPairPtrsExercise { + pub fn strat() -> BoxedStrategy { + ( + prop::num::i32::ANY, + prop::num::i32::ANY, + HostMemory::mem_area_strat(4), + HostMemory::mem_area_strat(4), + HostMemory::mem_area_strat(8), + HostMemory::mem_area_strat(8), + ) + .prop_map( + |( + input_first, + input_second, + input_first_loc, + input_second_loc, + input_struct_loc, + return_loc, + )| SumPairPtrsExercise { + input_first, + input_second, + input_first_loc, + input_second_loc, + input_struct_loc, + return_loc, + }, + ) + .prop_filter("non-overlapping pointers", |e| { + MemArea::non_overlapping_set(&[ + &e.input_first_loc, + &e.input_second_loc, + &e.input_struct_loc, + &e.return_loc, + ]) + }) + .boxed() + } + pub fn test(&self) { + let mut ctx = WasiCtx::new(); + let mut host_memory = HostMemory::new(); + let mut guest_memory = host_memory.guest_memory(); + + *guest_memory + .ptr_mut(self.input_first_loc.ptr) + .expect("input_first ptr") + .as_ref_mut() + .expect("input_first ref") = self.input_first; + *guest_memory + .ptr_mut(self.input_second_loc.ptr) + .expect("input_second ptr") + .as_ref_mut() + .expect("input_second ref") = self.input_second; + + *guest_memory + .ptr_mut(self.input_struct_loc.ptr) + .expect("input_struct ptr") + .as_ref_mut() + .expect("input_struct ref") = self.input_first_loc.ptr; + *guest_memory + .ptr_mut(self.input_struct_loc.ptr + 4) + .expect("input_struct ptr") + .as_ref_mut() + .expect("input_struct ref") = self.input_second_loc.ptr; + + let res = structs::sum_of_pair_of_ptrs( + &mut ctx, + &mut guest_memory, + self.input_struct_loc.ptr as i32, + self.return_loc.ptr as i32, + ); + + assert_eq!(res, types::Errno::Ok.into(), "sum of pair of ptrs errno"); + + let doubled: i64 = *guest_memory + .ptr(self.return_loc.ptr) + .expect("return ptr") + .as_ref() + .expect("return ref"); + + assert_eq!( + doubled, + (self.input_first as i64) + (self.input_second as i64), + "sum of pair of ptrs return val" + ); + } +} +proptest! { + #[test] + fn sum_of_pair_of_ptrs(e in SumPairPtrsExercise::strat()) { + e.test() + } +} diff --git a/tests/structs.witx b/tests/structs.witx new file mode 100644 index 0000000000..e9b4140c1e --- /dev/null +++ b/tests/structs.witx @@ -0,0 +1,23 @@ + +(use "errno.witx") + +(typename $pair_ints + (struct + (field $first s32) + (field $second s32))) + +(typename $pair_int_ptrs + (struct + (field $first (@witx const_pointer s32)) + (field $second (@witx const_pointer s32)))) + +(module $structs + (@interface func (export "sum_of_pair") + (param $an_pair $pair_ints) + (result $error $errno) + (result $doubled s64)) + (@interface func (export "sum_of_pair_of_ptrs") + (param $an_pair $pair_int_ptrs) + (result $error $errno) + (result $doubled s64)) +) diff --git a/tests/test.witx b/tests/test.witx deleted file mode 100644 index 78f69a3276..0000000000 --- a/tests/test.witx +++ /dev/null @@ -1,84 +0,0 @@ -(use "errno.witx") - -(typename $excuse - (enum u8 - $dog_ate - $traffic - $sleeping)) - -(typename $car_config - (flags u8 - $automatic - $awd - $suv)) - -(typename $cookie - (int u64 - (const $start 0))) - -(typename $bool - (enum u8 - $false - $true)) - -(typename $pair_ints - (struct - (field $first s32) - (field $second s32))) - -(typename $pair_int_ptrs - (struct - (field $first (@witx const_pointer s32)) - (field $second (@witx const_pointer s32)))) - -(typename $named_ptr (@witx pointer f32)) -(typename $named_ptr_to_ptr (@witx pointer (@witx pointer f64))) - -(typename $const_excuse_array (array (@witx const_pointer $excuse))) -(typename $excuse_array (array (@witx pointer $excuse))) - -(module $foo - (@interface func (export "baz") - (param $an_excuse $excuse) - (param $an_excuse_by_reference (@witx pointer $excuse)) - (param $a_lamer_excuse (@witx const_pointer $excuse)) - (param $two_layers_of_excuses (@witx pointer (@witx const_pointer $excuse))) - (result $error $errno)) - (@interface func (export "bat") - (param $an_int u32) - (result $error $errno) - (result $doubled_it f32)) - (@interface func (export "sum_of_pair") - (param $an_pair $pair_ints) - (result $error $errno) - (result $doubled s64)) - (@interface func (export "sum_of_pair_of_ptrs") - (param $an_pair $pair_int_ptrs) - (result $error $errno) - (result $doubled s64)) - (@interface func (export "reduce_excuses") - (param $excuses $const_excuse_array) - (result $error $errno) - (result $reduced $excuse) - ) - (@interface func (export "populate_excuses") - (param $excuses $excuse_array) - (result $error $errno) - ) - (@interface func (export "configure_car") - (param $old_config $car_config) - (param $old_config_by_ptr (@witx const_pointer $car_config)) - (result $error $errno) - (result $new_config $car_config) - ) - (@interface func (export "hello_string") - (param $a_string string) - (result $error $errno) - (result $total_bytes u32) - ) - (@interface func (export "cookie_cutter") - (param $init_cookie $cookie) - (result $error $errno) - (result $is_start $bool) - ) -) diff --git a/tests/trivial.rs b/tests/trivial.rs deleted file mode 100644 index de97dd8a05..0000000000 --- a/tests/trivial.rs +++ /dev/null @@ -1,55 +0,0 @@ -use proptest::prelude::*; -use wiggle_runtime::GuestError; -use wiggle_test::{impl_errno, HostMemory, WasiCtx}; - -wiggle_generate::from_witx!({ - witx: ["tests/trivial.witx"], - ctx: WasiCtx, -}); - -impl_errno!(types::Errno); - -impl trivial::Trivial for WasiCtx { - fn int_float_args(&mut self, an_int: u32, an_float: f32) -> Result<(), types::Errno> { - println!("INT FLOAT ARGS: {} {}", an_int, an_float); - Ok(()) - } -} - -// There's nothing meaningful to test here - this just demonstrates the test machinery - -#[derive(Debug)] -struct IntFloatExercise { - pub an_int: u32, - pub an_float: f32, -} - -impl IntFloatExercise { - pub fn test(&self) { - let mut ctx = WasiCtx::new(); - let mut host_memory = HostMemory::new(); - let mut guest_memory = host_memory.guest_memory(); - - let e = trivial::int_float_args( - &mut ctx, - &mut guest_memory, - self.an_int as i32, - self.an_float, - ); - - assert_eq!(e, types::Errno::Ok.into(), "int_float_args error"); - } - - pub fn strat() -> BoxedStrategy { - (prop::num::u32::ANY, prop::num::f32::ANY) - .prop_map(|(an_int, an_float)| IntFloatExercise { an_int, an_float }) - .boxed() - } -} - -proptest! { - #[test] - fn int_float_exercise(e in IntFloatExercise::strat()) { - e.test() - } -} diff --git a/tests/trivial.witx b/tests/trivial.witx deleted file mode 100644 index 3fbbf13e5c..0000000000 --- a/tests/trivial.witx +++ /dev/null @@ -1,8 +0,0 @@ -(use "errno.witx") - -(module $trivial - (@interface func (export "int_float_args") - (param $an_int u32) - (param $an_float f32) - (result $error $errno)) -) From 678065011eed1830aeb1aee722ffa8858c9677df Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Mon, 24 Feb 2020 21:11:08 +0100 Subject: [PATCH 52/86] Implement fmt::Display for enums, flags, and ints (#18) * Implement fmt::Display for enums `wasi_common` relies on `strerror` to nicely format error messages. `strerror` is autoimplemented in `wig`. I thought it might be useful to provide a Rust-idiomatic alternative which boils down to autoimplementing `fmt::Display` for all enums. * Implement fmt::Display for flags * Implement fmt::Display for ints --- crates/generate/src/types.rs | 47 +++++++++++++++++++++++++++++------- tests/errno.witx | 5 ++++ 2 files changed, 43 insertions(+), 9 deletions(-) diff --git a/crates/generate/src/types.rs b/crates/generate/src/types.rs index d2f94353da..a089dad629 100644 --- a/crates/generate/src/types.rs +++ b/crates/generate/src/types.rs @@ -72,6 +72,12 @@ fn define_int(names: &Names, name: &witx::Id, i: &witx::IntDatatype) -> TokenStr #(#consts;)* } + impl ::std::fmt::Display for #ident { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + write!(f, "{:?}", self) + } + } + impl ::std::convert::TryFrom<#repr> for #ident { type Error = wiggle_runtime::GuestError; fn try_from(value: #repr) -> Result { @@ -153,6 +159,8 @@ fn define_flags(names: &Names, name: &witx::Id, f: &witx::FlagsDatatype) -> Toke } let all_values_token = Literal::u128_unsuffixed(all_values); + let ident_str = ident.to_string(); + quote! { #[repr(transparent)] #[derive(Copy, Clone, Debug, ::std::hash::Hash, Eq, PartialEq)] @@ -167,6 +175,12 @@ fn define_flags(names: &Names, name: &witx::Id, f: &witx::FlagsDatatype) -> Toke } } + impl ::std::fmt::Display for #ident { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + write!(f, "{}({:#b})", #ident_str, self.0) + } + } + impl ::std::ops::BitAnd for #ident { type Output = Self; fn bitand(self, rhs: Self) -> Self::Output { @@ -286,15 +300,21 @@ fn define_enum(names: &Names, name: &witx::Id, e: &witx::EnumDatatype) -> TokenS witx::IntRepr::U64 => witx::AtomType::I64, }); - let variant_names = e.variants.iter().map(|v| names.enum_variant(&v.name)); - let tryfrom_repr_cases = e.variants.iter().enumerate().map(|(n, v)| { - let variant_name = names.enum_variant(&v.name); - quote!(#n => Ok(#ident::#variant_name)) - }); - let to_repr_cases = e.variants.iter().enumerate().map(|(n, v)| { - let variant_name = names.enum_variant(&v.name); - quote!(#ident::#variant_name => #n as #repr) - }); + let mut variant_names = vec![]; + let mut tryfrom_repr_cases = vec![]; + let mut to_repr_cases = vec![]; + let mut to_display = vec![]; + + for (n, variant) in e.variants.iter().enumerate() { + let variant_name = names.enum_variant(&variant.name); + let docs = variant.docs.trim(); + let ident_str = ident.to_string(); + let variant_str = variant_name.to_string(); + tryfrom_repr_cases.push(quote!(#n => Ok(#ident::#variant_name))); + to_repr_cases.push(quote!(#ident::#variant_name => #n as #repr)); + to_display.push(quote!(#ident::#variant_name => format!("{} ({}::{}({}))", #docs, #ident_str, #variant_str, #repr::from(*self)))); + variant_names.push(variant_name); + } quote! { #[repr(#repr)] @@ -303,6 +323,15 @@ fn define_enum(names: &Names, name: &witx::Id, e: &witx::EnumDatatype) -> TokenS #(#variant_names),* } + impl ::std::fmt::Display for #ident { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + let to_str = match self { + #(#to_display,)* + }; + write!(f, "{}", to_str) + } + } + impl ::std::convert::TryFrom<#repr> for #ident { type Error = wiggle_runtime::GuestError; fn try_from(value: #repr) -> Result<#ident, wiggle_runtime::GuestError> { diff --git a/tests/errno.witx b/tests/errno.witx index 5197c2c224..36ee67622e 100644 --- a/tests/errno.witx +++ b/tests/errno.witx @@ -1,8 +1,13 @@ (typename $errno (enum u32 + ;;; Success $ok + ;;; Invalid argument $invalid_arg + ;;; I really don't want to $dont_want_to + ;;; I am physically unable to $physically_unable + ;;; Well, that's a picket line alright! $picket_line)) From 7a4c881409f491be0a4ac12368e6837fda61e659 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Sun, 23 Feb 2020 18:16:26 +0100 Subject: [PATCH 53/86] Fix read/write for GuestTypeCopy members of non-copy structs This commit fixes stubs for struct members that are `GuestTypeCopy` but are not `GuestTypeClone`. In this case, we cannot rely on methods `T::read_from_guest` or `T::write_to_guest` since these are only available if `T: GuestTypeClone`. In those cases, we can and should dereference the location pointer to `T` and copy the result in/out respectively. --- crates/generate/src/types.rs | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/crates/generate/src/types.rs b/crates/generate/src/types.rs index a089dad629..9fb8cea87a 100644 --- a/crates/generate/src/types.rs +++ b/crates/generate/src/types.rs @@ -567,14 +567,14 @@ fn define_ptr_struct(names: &Names, name: &witx::Id, s: &witx::StructDatatype) - witx::TypeRef::Name(nt) => { let type_ = names.type_(&nt.name); quote! { - let #name = #type_::read_from_guest(&location.cast(#offset)?)?; + let #name: #type_ = *location.cast(#offset)?.as_ref()?; } } witx::TypeRef::Value(ty) => match &**ty { witx::Type::Builtin(builtin) => { let type_ = names.builtin_type(*builtin, anon_lifetime()); quote! { - let #name = #type_::read_from_guest(&location.cast(#offset)?)?; + let #name: #type_ = *location.cast(#offset)?.as_ref()?; } } witx::Type::Pointer(pointee) => { @@ -597,7 +597,31 @@ fn define_ptr_struct(names: &Names, name: &witx::Id, s: &witx::StructDatatype) - let member_writes = s.member_layout().into_iter().map(|ml| { let name = names.struct_member(&ml.member.name); let offset = ml.offset as u32; - quote!( self.#name.write_to_guest(&location.cast(#offset).expect("cast to inner member")); ) + match &ml.member.tref { + witx::TypeRef::Name(_) => { + quote! { + *location.cast(#offset).expect("cast to inner member").as_ref_mut().expect("inner member as ref mut") = self.#name; + } + } + witx::TypeRef::Value(ty) => match &**ty { + witx::Type::Builtin(_) => { + quote! { + *location.cast(#offset).expect("cast to inner member").as_ref_mut().expect("inner member as ref mut") = self.#name; + } + } + witx::Type::Pointer(_) => { + quote! { + self.#name.write_to_guest(&location.cast(#offset).expect("cast to inner member")); + } + } + witx::Type::ConstPointer(_) => { + quote! { + self.#name.write_to_guest(&location.cast(#offset).expect("cast to inner member")); + } + } + _ => unimplemented!("other anonymous struct members"), + }, + } }); quote! { From 694cf117bbc2595af7c94ca24b50f3a2029d2020 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Sun, 23 Feb 2020 18:36:10 +0100 Subject: [PATCH 54/86] Add proptests for structs with mixed members (copy/noncopy) --- tests/structs.rs | 97 ++++++++++++++++++++++++++++++++++++++++++++++ tests/structs.witx | 9 +++++ 2 files changed, 106 insertions(+) diff --git a/tests/structs.rs b/tests/structs.rs index c2e4263dad..0d9aa5c5a0 100644 --- a/tests/structs.rs +++ b/tests/structs.rs @@ -25,6 +25,15 @@ impl structs::Structs for WasiCtx { .expect("dereferncing GuestPtr should succeed"); Ok(first as i64 + second as i64) } + + fn sum_of_int_and_ptr(&mut self, an_pair: &types::PairIntAndPtr) -> Result { + let first = *an_pair + .first + .as_ref() + .expect("dereferencing GuestPtr should succeed"); + let second = an_pair.second as i64; + Ok(first as i64 + second) + } } #[derive(Debug)] @@ -200,3 +209,91 @@ proptest! { e.test() } } + +#[derive(Debug)] +struct SumIntAndPtrExercise { + input_first: i32, + input_second: i32, + input_first_loc: MemArea, + input_struct_loc: MemArea, + return_loc: MemArea, +} + +impl SumIntAndPtrExercise { + pub fn strat() -> BoxedStrategy { + ( + prop::num::i32::ANY, + prop::num::i32::ANY, + HostMemory::mem_area_strat(4), + HostMemory::mem_area_strat(8), + HostMemory::mem_area_strat(8), + ) + .prop_map( + |(input_first, input_second, input_first_loc, input_struct_loc, return_loc)| { + SumIntAndPtrExercise { + input_first, + input_second, + input_first_loc, + input_struct_loc, + return_loc, + } + }, + ) + .prop_filter("non-overlapping pointers", |e| { + MemArea::non_overlapping_set(&[ + &e.input_first_loc, + &e.input_struct_loc, + &e.return_loc, + ]) + }) + .boxed() + } + pub fn test(&self) { + let mut ctx = WasiCtx::new(); + let mut host_memory = HostMemory::new(); + let mut guest_memory = host_memory.guest_memory(); + + *guest_memory + .ptr_mut(self.input_first_loc.ptr) + .expect("input_first ptr") + .as_ref_mut() + .expect("input_first ref") = self.input_first; + *guest_memory + .ptr_mut(self.input_struct_loc.ptr) + .expect("input_struct ptr") + .as_ref_mut() + .expect("input_struct ref") = self.input_first_loc.ptr; + *guest_memory + .ptr_mut(self.input_struct_loc.ptr + 4) + .expect("input_struct ptr") + .as_ref_mut() + .expect("input_struct ref") = self.input_second; + + let res = structs::sum_of_int_and_ptr( + &mut ctx, + &mut guest_memory, + self.input_struct_loc.ptr as i32, + self.return_loc.ptr as i32, + ); + + assert_eq!(res, types::Errno::Ok.into(), "sum of int and ptr errno"); + + let doubled: i64 = *guest_memory + .ptr(self.return_loc.ptr) + .expect("return ptr") + .as_ref() + .expect("return ref"); + + assert_eq!( + doubled, + (self.input_first as i64) + (self.input_second as i64), + "sum of pair of ptrs return val" + ); + } +} +proptest! { + #[test] + fn sum_of_int_and_ptr(e in SumIntAndPtrExercise::strat()) { + e.test() + } +} diff --git a/tests/structs.witx b/tests/structs.witx index e9b4140c1e..c6bb8bb6b0 100644 --- a/tests/structs.witx +++ b/tests/structs.witx @@ -11,6 +11,11 @@ (field $first (@witx const_pointer s32)) (field $second (@witx const_pointer s32)))) +(typename $pair_int_and_ptr + (struct + (field $first (@witx const_pointer s32)) + (field $second s32))) + (module $structs (@interface func (export "sum_of_pair") (param $an_pair $pair_ints) @@ -20,4 +25,8 @@ (param $an_pair $pair_int_ptrs) (result $error $errno) (result $doubled s64)) + (@interface func (export "sum_of_int_and_ptr") + (param $an_pair $pair_int_and_ptr) + (result $error $errno) + (result $double s64)) ) From a02bce6eaff48eb4a51c1820e8272e57287dfe93 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Mon, 24 Feb 2020 21:38:12 +0100 Subject: [PATCH 55/86] Revert changes and require Clone if Copy This commit aligns `wiggle` a little bit closer with Rust proper in the sense that now `GuestTypeCopy` implies `GuestTypeClone` which in turn implies that any type implementing `GuestTypeCopy` will have to provide `read_from_guest` and `write_to_guest` methods. As a result, we can safely revert changes introduced in the previous commit. --- crates/generate/src/types.rs | 46 +++++++++++------------------- crates/runtime/src/guest_type.rs | 13 +++++++-- crates/runtime/src/memory/array.rs | 8 +++--- crates/runtime/src/memory/ptr.rs | 10 +++---- 4 files changed, 36 insertions(+), 41 deletions(-) diff --git a/crates/generate/src/types.rs b/crates/generate/src/types.rs index 9fb8cea87a..ebdb7d3567 100644 --- a/crates/generate/src/types.rs +++ b/crates/generate/src/types.rs @@ -125,7 +125,7 @@ fn define_int(names: &Names, name: &witx::Id, i: &witx::IntDatatype) -> TokenStr } } - impl wiggle_runtime::GuestTypeCopy for #ident {} + impl<'a> wiggle_runtime::GuestTypeCopy<'a> for #ident {} impl<'a> wiggle_runtime::GuestTypeClone<'a> for #ident { fn read_from_guest(location: &wiggle_runtime::GuestPtr<#ident>) -> Result<#ident, wiggle_runtime::GuestError> { Ok(*location.as_ref()?) @@ -278,7 +278,7 @@ fn define_flags(names: &Names, name: &witx::Id, f: &witx::FlagsDatatype) -> Toke } } - impl wiggle_runtime::GuestTypeCopy for #ident {} + impl<'a> wiggle_runtime::GuestTypeCopy<'a> for #ident {} impl<'a> wiggle_runtime::GuestTypeClone<'a> for #ident { fn read_from_guest(location: &wiggle_runtime::GuestPtr<#ident>) -> Result<#ident, wiggle_runtime::GuestError> { Ok(*location.as_ref()?) @@ -381,7 +381,7 @@ fn define_enum(names: &Names, name: &witx::Id, e: &witx::EnumDatatype) -> TokenS } } - impl wiggle_runtime::GuestTypeCopy for #ident {} + impl<'a> wiggle_runtime::GuestTypeCopy<'a> for #ident {} impl<'a> wiggle_runtime::GuestTypeClone<'a> for #ident { fn read_from_guest(location: &wiggle_runtime::GuestPtr<#ident>) -> Result<#ident, wiggle_runtime::GuestError> { use ::std::convert::TryFrom; @@ -496,7 +496,15 @@ fn define_copy_struct(names: &Names, name: &witx::Id, s: &witx::StructDatatype) Ok(()) } } - impl wiggle_runtime::GuestTypeCopy for #ident {} + impl<'a> wiggle_runtime::GuestTypeClone<'a> for #ident { + fn read_from_guest(location: &wiggle_runtime::GuestPtr<'a, #ident>) -> Result<#ident, wiggle_runtime::GuestError> { + Ok(*location.as_ref()?) + } + fn write_to_guest(&self, location: &wiggle_runtime::GuestPtrMut<'a, Self>) { + unsafe { (location.as_raw() as *mut #ident).write(*self) }; + } + } + impl<'a> wiggle_runtime::GuestTypeCopy<'a> for #ident {} } } @@ -567,14 +575,14 @@ fn define_ptr_struct(names: &Names, name: &witx::Id, s: &witx::StructDatatype) - witx::TypeRef::Name(nt) => { let type_ = names.type_(&nt.name); quote! { - let #name: #type_ = *location.cast(#offset)?.as_ref()?; + let #name = #type_::read_from_guest(&location.cast(#offset)?)?; } } witx::TypeRef::Value(ty) => match &**ty { witx::Type::Builtin(builtin) => { let type_ = names.builtin_type(*builtin, anon_lifetime()); quote! { - let #name: #type_ = *location.cast(#offset)?.as_ref()?; + let #name = #type_::read_from_guest(&location.cast(#offset)?)?; } } witx::Type::Pointer(pointee) => { @@ -597,30 +605,8 @@ fn define_ptr_struct(names: &Names, name: &witx::Id, s: &witx::StructDatatype) - let member_writes = s.member_layout().into_iter().map(|ml| { let name = names.struct_member(&ml.member.name); let offset = ml.offset as u32; - match &ml.member.tref { - witx::TypeRef::Name(_) => { - quote! { - *location.cast(#offset).expect("cast to inner member").as_ref_mut().expect("inner member as ref mut") = self.#name; - } - } - witx::TypeRef::Value(ty) => match &**ty { - witx::Type::Builtin(_) => { - quote! { - *location.cast(#offset).expect("cast to inner member").as_ref_mut().expect("inner member as ref mut") = self.#name; - } - } - witx::Type::Pointer(_) => { - quote! { - self.#name.write_to_guest(&location.cast(#offset).expect("cast to inner member")); - } - } - witx::Type::ConstPointer(_) => { - quote! { - self.#name.write_to_guest(&location.cast(#offset).expect("cast to inner member")); - } - } - _ => unimplemented!("other anonymous struct members"), - }, + quote! { + self.#name.write_to_guest(&location.cast(#offset).expect("cast to inner member")); } }); diff --git a/crates/runtime/src/guest_type.rs b/crates/runtime/src/guest_type.rs index 83eacaa0ce..6cf0972f2b 100644 --- a/crates/runtime/src/guest_type.rs +++ b/crates/runtime/src/guest_type.rs @@ -10,11 +10,12 @@ pub trait GuestType: Sized { fn validate<'a>(location: &GuestPtr<'a, Self>) -> Result<(), GuestError>; } -pub trait GuestTypeCopy: GuestType + Copy {} pub trait GuestTypeClone<'a>: GuestType + Clone { fn read_from_guest(location: &GuestPtr<'a, Self>) -> Result; fn write_to_guest(&self, location: &GuestPtrMut<'a, Self>); } +// Following trait system in Rust, Copy implies Clone +pub trait GuestTypeCopy<'a>: GuestTypeClone<'a> + Copy {} macro_rules! builtin_type { ( $( $t:ident ), * ) => { @@ -33,7 +34,15 @@ macro_rules! builtin_type { Ok(()) } } - impl GuestTypeCopy for $t {} + impl<'a> GuestTypeClone<'a> for $t { + fn read_from_guest(location: &GuestPtr<'a, Self>) -> Result { + Ok(*location.as_ref()?) + } + fn write_to_guest(&self, location: &GuestPtrMut<'a, Self>) { + unsafe { (location.as_raw() as *mut $t).write(*self) }; + } + } + impl<'a> GuestTypeCopy<'a> for $t {} )* }; } diff --git a/crates/runtime/src/memory/array.rs b/crates/runtime/src/memory/array.rs index c9615b1e2f..d55da51e2f 100644 --- a/crates/runtime/src/memory/array.rs +++ b/crates/runtime/src/memory/array.rs @@ -79,7 +79,7 @@ where impl<'a, T> GuestArray<'a, T> where - T: GuestTypeCopy, + T: GuestTypeCopy<'a>, { pub fn as_ref(&self) -> Result, GuestError> { let mut next = self.ptr.elem(0)?; @@ -109,7 +109,7 @@ where pub struct GuestArrayRef<'a, T> where - T: GuestTypeCopy, + T: GuestTypeCopy<'a>, { ref_: GuestRef<'a, T>, num_elems: u32, @@ -117,7 +117,7 @@ where impl<'a, T> fmt::Debug for GuestArrayRef<'a, T> where - T: GuestTypeCopy + fmt::Debug, + T: GuestTypeCopy<'a> + fmt::Debug, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( @@ -130,7 +130,7 @@ where impl<'a, T> Deref for GuestArrayRef<'a, T> where - T: GuestTypeCopy, + T: GuestTypeCopy<'a>, { type Target = [T]; diff --git a/crates/runtime/src/memory/ptr.rs b/crates/runtime/src/memory/ptr.rs index afebb6c332..f6a164fbd1 100644 --- a/crates/runtime/src/memory/ptr.rs +++ b/crates/runtime/src/memory/ptr.rs @@ -57,7 +57,7 @@ impl<'a, T: GuestType> GuestPtr<'a, T> { impl<'a, T> GuestPtr<'a, T> where - T: GuestTypeCopy, + T: GuestTypeCopy<'a>, { pub fn as_ref(&self) -> Result, GuestError> { T::validate(&self)?; @@ -183,7 +183,7 @@ where impl<'a, T> GuestPtrMut<'a, T> where - T: GuestTypeCopy, + T: GuestTypeCopy<'a>, { pub fn as_ref(&self) -> Result, GuestError> { self.as_immut().as_ref() @@ -298,7 +298,7 @@ impl<'a, T> GuestRef<'a, T> { impl<'a, T> Deref for GuestRef<'a, T> where - T: GuestTypeCopy, + T: GuestTypeCopy<'a>, { type Target = T; @@ -357,7 +357,7 @@ impl<'a, T> GuestRefMut<'a, T> { impl<'a, T> ::std::ops::Deref for GuestRefMut<'a, T> where - T: GuestTypeCopy, + T: GuestTypeCopy<'a>, { type Target = T; @@ -372,7 +372,7 @@ where impl<'a, T> DerefMut for GuestRefMut<'a, T> where - T: GuestTypeCopy, + T: GuestTypeCopy<'a>, { fn deref_mut(&mut self) -> &mut Self::Target { unsafe { From c8ea27553dd2778c79ac8f2d5456c1c1e59fa0a2 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Wed, 26 Feb 2020 18:32:03 +0100 Subject: [PATCH 56/86] Allow returning structs if copy (#19) * Allow returning structs if copy This commit does three things: 1. enables marshalling of structs as return args from interface funcs but so far *only* for the case when the struct itself is copy 2. puts bits that use `std::convert::TryInto` in a local scope to avoid multiple reimports 3. for added clarity, we now print for which `tref` type the marshalling of results is unimplemented The first case (1.) is required to make `fd_fdstat_get` WASI interface func work which returns `Fdstat` struct (which is copy). The second case (2.) caused me some grief somewhere along the lines when I was playing with snapshot1. Putting the code that requires it inside a local scope fixed all the issues * Add proptests for returing struct if copyable * Use write_ptr_to_guest to marshal value to guest * Successfully return non-copy struct --- crates/generate/src/funcs.rs | 25 ++--- crates/generate/src/module_trait.rs | 24 ++++- tests/structs.rs | 160 +++++++++++++++++++++++++++- tests/structs.witx | 8 ++ 4 files changed, 197 insertions(+), 20 deletions(-) diff --git a/crates/generate/src/funcs.rs b/crates/generate/src/funcs.rs index 861de4c258..2cf4507442 100644 --- a/crates/generate/src/funcs.rs +++ b/crates/generate/src/funcs.rs @@ -141,11 +141,13 @@ fn marshal_arg( let try_into_conversion = { let name = names.func_param(¶m.name); quote! { - use ::std::convert::TryInto; - let #name: #interface_typename = match #name.try_into() { - Ok(a) => a, - Err(e) => { - #error_handling + let #name: #interface_typename = { + use ::std::convert::TryInto; + match #name.try_into() { + Ok(a) => a, + Err(e) => { + #error_handling + } } }; } @@ -322,15 +324,9 @@ where // core type is given func_ptr_binding name. let ptr_name = names.func_ptr_binding(&result.name); let ptr_err_handling = error_handling(&format!("{}:result_ptr_mut", result.name.as_str())); - let ref_err_handling = error_handling(&format!("{}:result_ref_mut", result.name.as_str())); let pre = quote! { let mut #ptr_name = match memory.ptr_mut::<#pointee_type>(#ptr_name as u32) { - Ok(p) => match p.as_ref_mut() { - Ok(r) => r, - Err(e) => { - #ref_err_handling - } - }, + Ok(p) => p, Err(e) => { #ptr_err_handling } @@ -339,7 +335,7 @@ where // trait binding returns func_param name. let val_name = names.func_param(&result.name); let post = quote! { - *#ptr_name = #val_name; + #ptr_name.write_ptr_to_guest(&#val_name); }; (pre, post) }; @@ -361,6 +357,7 @@ where witx::BuiltinType::String => unimplemented!("string types"), }, witx::Type::Enum(_) | witx::Type::Flags(_) | witx::Type::Int(_) => write_val_to_ptr, - _ => unimplemented!("marshal result"), + witx::Type::Struct(_) => write_val_to_ptr, + _ => unimplemented!("missing marshalling result for {:?}", &*tref.type_()), } } diff --git a/crates/generate/src/module_trait.rs b/crates/generate/src/module_trait.rs index 100144dca5..4cb4c45045 100644 --- a/crates/generate/src/module_trait.rs +++ b/crates/generate/src/module_trait.rs @@ -2,16 +2,25 @@ use proc_macro2::TokenStream; use quote::quote; use crate::names::Names; -use crate::types::anon_lifetime; +use crate::types::{anon_lifetime, type_needs_lifetime}; use witx::Module; pub fn define_module_trait(names: &Names, m: &Module) -> TokenStream { let traitname = names.trait_name(&m.name); let traitmethods = m.funcs().map(|f| { + // Check if we're returning an entity anotated with a lifetime, + // in which case, we'll need to annotate the function itself, and + // hence will need an explicit lifetime (rather than anonymous) + let (lifetime, is_anonymous) = if f.results.iter().any(|ret| type_needs_lifetime(&ret.tref)) + { + (quote!('a), false) + } else { + (anon_lifetime(), true) + }; let funcname = names.func(&f.name); let args = f.params.iter().map(|arg| { let arg_name = names.func_param(&arg.name); - let arg_typename = names.type_ref(&arg.tref, anon_lifetime()); + let arg_typename = names.type_ref(&arg.tref, lifetime.clone()); let arg_type = match arg.tref.type_().passed_by() { witx::TypePassedBy::Value { .. } => quote!(#arg_typename), witx::TypePassedBy::Pointer { .. } => quote!(&#arg_typename), @@ -23,13 +32,18 @@ pub fn define_module_trait(names: &Names, m: &Module) -> TokenStream { .results .iter() .skip(1) - .map(|ret| names.type_ref(&ret.tref, anon_lifetime())); + .map(|ret| names.type_ref(&ret.tref, lifetime.clone())); let err = f .results .get(0) - .map(|err_result| names.type_ref(&err_result.tref, anon_lifetime())) + .map(|err_result| names.type_ref(&err_result.tref, lifetime.clone())) .unwrap_or(quote!(())); - quote!(fn #funcname(&mut self, #(#args),*) -> Result<(#(#rets),*), #err>;) + + if is_anonymous { + quote!(fn #funcname(&mut self, #(#args),*) -> Result<(#(#rets),*), #err>;) + } else { + quote!(fn #funcname<#lifetime>(&mut self, #(#args),*) -> Result<(#(#rets),*), #err>;) + } }); quote! { pub trait #traitname { diff --git a/tests/structs.rs b/tests/structs.rs index 0d9aa5c5a0..d282f0a91b 100644 --- a/tests/structs.rs +++ b/tests/structs.rs @@ -1,5 +1,5 @@ use proptest::prelude::*; -use wiggle_runtime::GuestError; +use wiggle_runtime::{GuestError, GuestPtr}; use wiggle_test::{impl_errno, HostMemory, MemArea, WasiCtx}; wiggle_generate::from_witx!({ @@ -34,6 +34,21 @@ impl structs::Structs for WasiCtx { let second = an_pair.second as i64; Ok(first as i64 + second) } + + fn return_pair_ints(&mut self) -> Result { + Ok(types::PairInts { + first: 10, + second: 20, + }) + } + + fn return_pair_of_ptrs<'a>( + &mut self, + first: GuestPtr<'a, i32>, + second: GuestPtr<'a, i32>, + ) -> Result, types::Errno> { + Ok(types::PairIntPtrs { first, second }) + } } #[derive(Debug)] @@ -297,3 +312,146 @@ proptest! { e.test() } } + +#[derive(Debug)] +struct ReturnPairInts { + pub return_loc: MemArea, +} + +impl ReturnPairInts { + pub fn strat() -> BoxedStrategy { + HostMemory::mem_area_strat(8) + .prop_map(|return_loc| ReturnPairInts { return_loc }) + .boxed() + } + + pub fn test(&self) { + let mut ctx = WasiCtx::new(); + let mut host_memory = HostMemory::new(); + let mut guest_memory = host_memory.guest_memory(); + + let err = + structs::return_pair_ints(&mut ctx, &mut guest_memory, self.return_loc.ptr as i32); + + assert_eq!(err, types::Errno::Ok.into(), "return struct errno"); + + let return_struct: types::PairInts = *guest_memory + .ptr(self.return_loc.ptr) + .expect("return ptr") + .as_ref() + .expect("return ref"); + + assert_eq!( + return_struct, + types::PairInts { + first: 10, + second: 20 + }, + "return_pair_ints return value" + ); + } +} + +proptest! { + #[test] + fn return_pair_ints(e in ReturnPairInts::strat()) { + e.test(); + } +} + +#[derive(Debug)] +struct ReturnPairPtrsExercise { + input_first: i32, + input_second: i32, + input_first_loc: MemArea, + input_second_loc: MemArea, + return_loc: MemArea, +} + +impl ReturnPairPtrsExercise { + pub fn strat() -> BoxedStrategy { + ( + prop::num::i32::ANY, + prop::num::i32::ANY, + HostMemory::mem_area_strat(4), + HostMemory::mem_area_strat(4), + HostMemory::mem_area_strat(8), + ) + .prop_map( + |(input_first, input_second, input_first_loc, input_second_loc, return_loc)| { + ReturnPairPtrsExercise { + input_first, + input_second, + input_first_loc, + input_second_loc, + return_loc, + } + }, + ) + .prop_filter("non-overlapping pointers", |e| { + MemArea::non_overlapping_set(&[ + &e.input_first_loc, + &e.input_second_loc, + &e.return_loc, + ]) + }) + .boxed() + } + pub fn test(&self) { + let mut ctx = WasiCtx::new(); + let mut host_memory = HostMemory::new(); + let mut guest_memory = host_memory.guest_memory(); + + *guest_memory + .ptr_mut(self.input_first_loc.ptr) + .expect("input_first ptr") + .as_ref_mut() + .expect("input_first ref") = self.input_first; + *guest_memory + .ptr_mut(self.input_second_loc.ptr) + .expect("input_second ptr") + .as_ref_mut() + .expect("input_second ref") = self.input_second; + + let res = structs::return_pair_of_ptrs( + &mut ctx, + &mut guest_memory, + self.input_first_loc.ptr as i32, + self.input_second_loc.ptr as i32, + self.return_loc.ptr as i32, + ); + + assert_eq!(res, types::Errno::Ok.into(), "return pair of ptrs errno"); + + let ptr_pair_int_ptrs: GuestPtr> = + guest_memory.ptr(self.return_loc.ptr).expect("return ptr"); + let ret_first_ptr: GuestPtr = ptr_pair_int_ptrs + .cast::>(0u32) + .expect("extract ptr to first element in struct") + .clone_from_guest() + .expect("read ptr to first element in struct"); + let ret_second_ptr: GuestPtr = ptr_pair_int_ptrs + .cast::>(4u32) + .expect("extract ptr to second element in struct") + .clone_from_guest() + .expect("read ptr to second element in struct"); + assert_eq!( + self.input_first, + *ret_first_ptr + .as_ref() + .expect("deref extracted ptr to first element") + ); + assert_eq!( + self.input_second, + *ret_second_ptr + .as_ref() + .expect("deref extracted ptr to second element") + ); + } +} +proptest! { + #[test] + fn return_pair_of_ptrs(e in ReturnPairPtrsExercise::strat()) { + e.test() + } +} diff --git a/tests/structs.witx b/tests/structs.witx index c6bb8bb6b0..0542bc68fa 100644 --- a/tests/structs.witx +++ b/tests/structs.witx @@ -29,4 +29,12 @@ (param $an_pair $pair_int_and_ptr) (result $error $errno) (result $double s64)) + (@interface func (export "return_pair_ints") + (result $error $errno) + (result $an_pair $pair_ints)) + (@interface func (export "return_pair_of_ptrs") + (param $first (@witx const_pointer s32)) + (param $second (@witx const_pointer s32)) + (result $error $errno) + (result $an_pair $pair_int_ptrs)) ) From 25a411d7fdf40de1e13d10b1d536f4407aa873ec Mon Sep 17 00:00:00 2001 From: Pat Hickey Date: Wed, 26 Feb 2020 10:44:04 -0800 Subject: [PATCH 57/86] rename the pointer read/write methods to read and write these names were artifacts of some early confusion / bad design i made in the traits. read and write are much simpler names! also, change a ptr_mut to ptr where we just read the contents in the argument marshalling for structs. this has no effect, but it is more correct. --- crates/generate/src/funcs.rs | 6 +++--- crates/runtime/src/memory/array.rs | 6 +++--- crates/runtime/src/memory/ptr.rs | 6 +++--- tests/arrays.rs | 4 ++-- tests/pointers.rs | 2 +- tests/structs.rs | 4 ++-- 6 files changed, 14 insertions(+), 14 deletions(-) diff --git a/crates/generate/src/funcs.rs b/crates/generate/src/funcs.rs index 2cf4507442..4045447628 100644 --- a/crates/generate/src/funcs.rs +++ b/crates/generate/src/funcs.rs @@ -262,8 +262,8 @@ fn marshal_arg( let arg_name = names.func_ptr_binding(¶m.name); let name = names.func_param(¶m.name); quote! { - let #name = match memory.ptr_mut::<#pointee_type>(#arg_name as u32) { - Ok(p) => match p.read_ptr_from_guest() { + let #name = match memory.ptr::<#pointee_type>(#arg_name as u32) { + Ok(p) => match p.read() { Ok(r) => r, Err(e) => { #error_handling @@ -335,7 +335,7 @@ where // trait binding returns func_param name. let val_name = names.func_param(&result.name); let post = quote! { - #ptr_name.write_ptr_to_guest(&#val_name); + #ptr_name.write(&#val_name); }; (pre, post) }; diff --git a/crates/runtime/src/memory/array.rs b/crates/runtime/src/memory/array.rs index d55da51e2f..db3408cc3f 100644 --- a/crates/runtime/src/memory/array.rs +++ b/crates/runtime/src/memory/array.rs @@ -225,19 +225,19 @@ mod test { } { let ptr = guest_memory.ptr_mut(12).expect("ptr mut to first el"); - ptr.write_ptr_to_guest( + ptr.write( &guest_memory .ptr::>(0) .expect("ptr to the first value"), ); let ptr = guest_memory.ptr_mut(16).expect("ptr mut to first el"); - ptr.write_ptr_to_guest( + ptr.write( &guest_memory .ptr::>(4) .expect("ptr to the second value"), ); let ptr = guest_memory.ptr_mut(20).expect("ptr mut to first el"); - ptr.write_ptr_to_guest( + ptr.write( &guest_memory .ptr::>(8) .expect("ptr to the third value"), diff --git a/crates/runtime/src/memory/ptr.rs b/crates/runtime/src/memory/ptr.rs index f6a164fbd1..9e853c3742 100644 --- a/crates/runtime/src/memory/ptr.rs +++ b/crates/runtime/src/memory/ptr.rs @@ -80,7 +80,7 @@ impl<'a, T> GuestPtr<'a, T> where T: GuestTypeClone<'a>, { - pub fn clone_from_guest(&self) -> Result { + pub fn read(&self) -> Result { T::read_from_guest(self) } } @@ -210,11 +210,11 @@ impl<'a, T> GuestPtrMut<'a, T> where T: GuestTypeClone<'a>, { - pub fn read_ptr_from_guest(&self) -> Result { + pub fn read(&self) -> Result { T::read_from_guest(&self.as_immut()) } - pub fn write_ptr_to_guest(&self, ptr: &T) { + pub fn write(&self, ptr: &T) { T::write_to_guest(ptr, &self); } } diff --git a/tests/arrays.rs b/tests/arrays.rs index 467dd3cc27..a5acfdbd45 100644 --- a/tests/arrays.rs +++ b/tests/arrays.rs @@ -107,7 +107,7 @@ impl ReduceExcusesExcercise { .ptr_mut(self.array_ptr_loc.ptr) .expect("ptr to array mut"); for ptr in &self.excuse_ptr_locs { - next.write_ptr_to_guest( + next.write( &guest_memory .ptr::(ptr.ptr) .expect("ptr to Excuse value"), @@ -203,7 +203,7 @@ impl PopulateExcusesExcercise { .ptr_mut(self.array_ptr_loc.ptr) .expect("ptr mut to the first element of array"); for ptr in &self.elements { - next.write_ptr_to_guest( + next.write( &guest_memory .ptr_mut::(ptr.ptr) .expect("ptr mut to Excuse value"), diff --git a/tests/pointers.rs b/tests/pointers.rs index a85e11ec9f..e22e1d67f2 100644 --- a/tests/pointers.rs +++ b/tests/pointers.rs @@ -54,7 +54,7 @@ impl pointers::Pointers for WasiCtx { println!("input4 {:?}", input4); // Write ptr value to mutable ptr: - input4_ptr_ptr.write_ptr_to_guest(&input2_ptr.as_immut()); + input4_ptr_ptr.write(&input2_ptr.as_immut()); Ok(()) } diff --git a/tests/structs.rs b/tests/structs.rs index d282f0a91b..40f212f551 100644 --- a/tests/structs.rs +++ b/tests/structs.rs @@ -428,12 +428,12 @@ impl ReturnPairPtrsExercise { let ret_first_ptr: GuestPtr = ptr_pair_int_ptrs .cast::>(0u32) .expect("extract ptr to first element in struct") - .clone_from_guest() + .read() .expect("read ptr to first element in struct"); let ret_second_ptr: GuestPtr = ptr_pair_int_ptrs .cast::>(4u32) .expect("extract ptr to second element in struct") - .clone_from_guest() + .read() .expect("read ptr to second element in struct"); assert_eq!( self.input_first, From f6a732b6cfaf4c8b1e12538a9f0146e63017454c Mon Sep 17 00:00:00 2001 From: Pat Hickey Date: Thu, 20 Feb 2020 14:36:53 -0800 Subject: [PATCH 58/86] squash all tagged union work into one commit --- crates/WASI | 2 +- crates/generate/src/funcs.rs | 4 + crates/generate/src/types.rs | 232 +++++++++++++++++++++++++++++++++-- tests/union.rs | 43 +++++++ tests/union.witx | 28 +++++ 5 files changed, 297 insertions(+), 12 deletions(-) create mode 100644 tests/union.rs create mode 100644 tests/union.witx diff --git a/crates/WASI b/crates/WASI index 77629f3442..6d96ec08bf 160000 --- a/crates/WASI +++ b/crates/WASI @@ -1 +1 @@ -Subproject commit 77629f34429c1bc65af797dac687fd47fc73df4b +Subproject commit 6d96ec08bf976ee5518ae2734bb01f9a8e919e7c diff --git a/crates/generate/src/funcs.rs b/crates/generate/src/funcs.rs index 4045447628..8814df19ab 100644 --- a/crates/generate/src/funcs.rs +++ b/crates/generate/src/funcs.rs @@ -305,6 +305,10 @@ fn marshal_arg( }; } } + witx::Type::Union(_u) => { + let name = names.func_param(¶m.name); + quote!(let #name = unimplemented!("union argument marshaling");) + } _ => unimplemented!("argument type marshalling"), } } diff --git a/crates/generate/src/types.rs b/crates/generate/src/types.rs index ebdb7d3567..e6767f9144 100644 --- a/crates/generate/src/types.rs +++ b/crates/generate/src/types.rs @@ -19,7 +19,7 @@ pub fn define_datatype(names: &Names, namedtype: &witx::NamedType) -> TokenStrea define_ptr_struct(names, &namedtype.name, &s) } } - witx::Type::Union(_) => unimplemented!("union types"), + witx::Type::Union(u) => define_union(names, &namedtype.name, &u), witx::Type::Handle(_h) => unimplemented!("handle types"), witx::Type::Builtin(b) => define_builtin(names, &namedtype.name, *b), witx::Type::Pointer(p) => define_witx_pointer( @@ -384,10 +384,9 @@ fn define_enum(names: &Names, name: &witx::Id, e: &witx::EnumDatatype) -> TokenS impl<'a> wiggle_runtime::GuestTypeCopy<'a> for #ident {} impl<'a> wiggle_runtime::GuestTypeClone<'a> for #ident { fn read_from_guest(location: &wiggle_runtime::GuestPtr<#ident>) -> Result<#ident, wiggle_runtime::GuestError> { - use ::std::convert::TryFrom; - let raw: #repr = unsafe { (location.as_raw() as *const #repr).read() }; - let val = #ident::try_from(raw)?; - Ok(val) + // Perform validation as part of as_ref: + let r = location.as_ref()?; + Ok(*r) } fn write_to_guest(&self, location: &wiggle_runtime::GuestPtrMut<#ident>) { let val: #repr = #repr::from(*self); @@ -408,6 +407,8 @@ fn define_builtin(names: &Names, name: &witx::Id, builtin: witx::BuiltinType) -> } } +// XXX DRY - should move these funcs to be a trait that Type, BuiltinType, StructDatatype, +// UnionDatatype all implement pub fn type_needs_lifetime(tref: &witx::TypeRef) -> bool { match &*tref.type_() { witx::Type::Builtin(b) => match b { @@ -419,7 +420,7 @@ pub fn type_needs_lifetime(tref: &witx::TypeRef) -> bool { | witx::Type::Int { .. } | witx::Type::Handle { .. } => false, witx::Type::Struct(s) => !struct_is_copy(&s), - witx::Type::Union { .. } => true, + witx::Type::Union(u) => !union_is_copy(&u), witx::Type::Pointer { .. } | witx::Type::ConstPointer { .. } => true, witx::Type::Array { .. } => true, } @@ -432,16 +433,39 @@ pub fn struct_is_copy(s: &witx::StructDatatype) -> bool { witx::BuiltinType::String => false, _ => true, }, - witx::Type::ConstPointer { .. } - | witx::Type::Pointer { .. } - | witx::Type::Array { .. } - | witx::Type::Union { .. } => false, + witx::Type::ConstPointer { .. } | witx::Type::Pointer { .. } | witx::Type::Array { .. } => { + false + } + witx::Type::Union(u) => union_is_copy(u), witx::Type::Enum { .. } | witx::Type::Int { .. } | witx::Type::Flags { .. } | witx::Type::Handle { .. } => true, }) } +pub fn union_is_copy(u: &witx::UnionDatatype) -> bool { + u.variants.iter().all(|m| { + if let Some(tref) = &m.tref { + match &*tref.type_() { + witx::Type::Struct(s) => struct_is_copy(&s), + witx::Type::Builtin(b) => match &*b { + witx::BuiltinType::String => false, + _ => true, + }, + witx::Type::ConstPointer { .. } + | witx::Type::Pointer { .. } + | witx::Type::Array { .. } => false, + witx::Type::Union(u) => union_is_copy(u), + witx::Type::Enum { .. } + | witx::Type::Int { .. } + | witx::Type::Flags { .. } + | witx::Type::Handle { .. } => true, + } + } else { + true + } + }) +} fn define_copy_struct(names: &Names, name: &witx::Id, s: &witx::StructDatatype) -> TokenStream { let ident = names.type_(name); @@ -498,7 +522,8 @@ fn define_copy_struct(names: &Names, name: &witx::Id, s: &witx::StructDatatype) } impl<'a> wiggle_runtime::GuestTypeClone<'a> for #ident { fn read_from_guest(location: &wiggle_runtime::GuestPtr<'a, #ident>) -> Result<#ident, wiggle_runtime::GuestError> { - Ok(*location.as_ref()?) + let r = location.as_ref()?; + Ok(*r) } fn write_to_guest(&self, location: &wiggle_runtime::GuestPtrMut<'a, Self>) { unsafe { (location.as_raw() as *mut #ident).write(*self) }; @@ -643,6 +668,191 @@ fn define_ptr_struct(names: &Names, name: &witx::Id, s: &witx::StructDatatype) - } } +fn union_validate( + names: &Names, + typename: TokenStream, + u: &witx::UnionDatatype, + ulayout: &witx::UnionLayout, +) -> TokenStream { + let tagname = names.type_(&u.tag.name); + let contents_offset = ulayout.contents_offset as u32; + + let with_err = |f: &str| -> TokenStream { + quote!(|e| wiggle_runtime::GuestError::InDataField { + typename: stringify!(#typename).to_owned(), + field: #f.to_owned(), + err: Box::new(e), + }) + }; + + let tag_err = with_err(""); + let variant_validation = u.variants.iter().map(|v| { + let err = with_err(v.name.as_str()); + let variantname = names.enum_variant(&v.name); + if let Some(tref) = &v.tref { + let varianttype = names.type_ref(tref, anon_lifetime()); + quote! { + #tagname::#variantname => { + let variant_ptr = ptr.cast::<#varianttype>(#contents_offset).map_err(#err)?; + wiggle_runtime::GuestType::validate(&variant_ptr).map_err(#err)?; + } + } + } else { + quote! { #tagname::#variantname => {} } + } + }); + + quote! { + let tag = *ptr.cast::<#tagname>(0).map_err(#tag_err)?.as_ref().map_err(#tag_err)?; + match tag { + #(#variant_validation)* + } + Ok(()) + } +} + +fn define_union(names: &Names, name: &witx::Id, u: &witx::UnionDatatype) -> TokenStream { + let ident = names.type_(name); + let size = u.mem_size_align().size as u32; + let align = u.mem_size_align().align as u32; + let ulayout = u.union_layout(); + let contents_offset = ulayout.contents_offset as u32; + + let lifetime = quote!('a); + + let variants = u.variants.iter().map(|v| { + let var_name = names.enum_variant(&v.name); + if let Some(tref) = &v.tref { + let var_type = names.type_ref(&tref, lifetime.clone()); + quote!(#var_name(#var_type)) + } else { + quote!(#var_name) + } + }); + + let tagname = names.type_(&u.tag.name); + + let read_variant = u.variants.iter().map(|v| { + let variantname = names.enum_variant(&v.name); + if let Some(tref) = &v.tref { + let varianttype = names.type_ref(tref, lifetime.clone()); + quote! { + #tagname::#variantname => { + let variant_ptr = location.cast::<#varianttype>(#contents_offset).expect("union variant ptr validated"); + let variant_val = wiggle_runtime::GuestTypeClone::read_from_guest(&variant_ptr)?; + Ok(#ident::#variantname(variant_val)) + } + } + } else { + quote! { #tagname::#variantname => Ok(#ident::#variantname), } + } + }); + + let write_variant = u.variants.iter().map(|v| { + let variantname = names.enum_variant(&v.name); + let write_tag = quote! { + let tag_ptr = location.cast::<#tagname>(0).expect("union tag ptr TODO error report"); + let mut tag_ref = tag_ptr.as_ref_mut().expect("union tag ref TODO error report"); + *tag_ref = #tagname::#variantname; + }; + if let Some(tref) = &v.tref { + let varianttype = names.type_ref(tref, lifetime.clone()); + quote! { + #ident::#variantname(contents) => { + #write_tag + let variant_ptr = location.cast::<#varianttype>(#contents_offset).expect("union variant ptr validated"); + contents.write_to_guest(&variant_ptr); + } + } + } else { + quote! { + #ident::#variantname => { + #write_tag + } + } + } + }); + let validate = union_validate(names, ident.clone(), u, &ulayout); + + if union_is_copy(u) { + // Type does not have a lifetime parameter: + quote! { + #[derive(Clone, Debug, PartialEq)] + pub enum #ident { + #(#variants),* + } + impl wiggle_runtime::GuestType for #ident { + fn size() -> u32 { + #size + } + fn align() -> u32 { + #align + } + fn name() -> String { + stringify!(#ident).to_owned() + } + fn validate(ptr: &wiggle_runtime::GuestPtr<#ident>) -> Result<(), wiggle_runtime::GuestError> { + #validate + } + } + impl<#lifetime> wiggle_runtime::GuestTypeClone<#lifetime> for #ident { + fn read_from_guest(location: &wiggle_runtime::GuestPtr<'a, #ident>) + -> Result { + wiggle_runtime::GuestType::validate(location)?; + let tag = *location.cast::<#tagname>(0).expect("validated tag ptr").as_ref().expect("validated tag ref"); + match tag { + #(#read_variant)* + } + + } + fn write_to_guest(&self, location: &wiggle_runtime::GuestPtrMut<'a, #ident>) { + match self { + #(#write_variant)* + } + } + } + } + } else { + quote! { + #[derive(Clone)] + pub enum #ident<#lifetime> { + #(#variants),* + } + + impl<#lifetime> wiggle_runtime::GuestType for #ident<#lifetime> { + fn size() -> u32 { + #size + } + fn align() -> u32 { + #align + } + fn name() -> String { + stringify!(#ident).to_owned() + } + fn validate(ptr: &wiggle_runtime::GuestPtr<#ident>) -> Result<(), wiggle_runtime::GuestError> { + #validate + } + } + impl<#lifetime> wiggle_runtime::GuestTypeClone<#lifetime> for #ident<#lifetime> { + fn read_from_guest(location: &wiggle_runtime::GuestPtr<'a, #ident>) + -> Result { + wiggle_runtime::GuestType::validate(location)?; + let tag = *location.cast::<#tagname>(0).expect("validated tag ptr").as_ref().expect("validated tag ref"); + match tag { + #(#read_variant)* + } + + } + fn write_to_guest(&self, location: &wiggle_runtime::GuestPtrMut<'a, #ident>) { + match self { + #(#write_variant)* + } + } + } + } + } +} + fn define_witx_pointer( names: &Names, name: &witx::Id, diff --git a/tests/union.rs b/tests/union.rs new file mode 100644 index 0000000000..d5da1bff96 --- /dev/null +++ b/tests/union.rs @@ -0,0 +1,43 @@ +use proptest::prelude::*; +use wiggle_runtime::{ + GuestError, GuestErrorType, GuestMemory, GuestPtr, GuestPtrMut, GuestRef, GuestRefMut, +}; +use wiggle_test::{impl_errno, HostMemory, MemArea, WasiCtx}; + +wiggle_generate::from_witx!({ + witx: ["tests/union.witx"], + ctx: WasiCtx, +}); + +impl_errno!(types::Errno); + +impl union_example::UnionExample for WasiCtx { + fn get_tag(&mut self, u: &types::Reason) -> Result { + println!("GET TAG: {:?}", u); + Ok(types::Excuse::DogAte) + } + fn reason_mult( + &mut self, + u: &types::ReasonMut<'_>, + multiply_by: u32, + ) -> Result<(), types::Errno> { + match u { + types::ReasonMut::DogAte(fptr) => { + let mut f = fptr.as_ref_mut().expect("valid pointer"); + let val = *f; + println!("REASON MULT DogAte({})", val); + *f = val * multiply_by as f32; + } + types::ReasonMut::Traffic(iptr) => { + let mut i = iptr.as_ref_mut().expect("valid pointer"); + let val = *i; + println!("REASON MULT Traffic({})", val); + *i = val * multiply_by as i32; + } + types::ReasonMut::Sleeping => { + println!("REASON MULT Sleeping"); + } + } + Ok(()) + } +} diff --git a/tests/union.witx b/tests/union.witx new file mode 100644 index 0000000000..7dda97f6e9 --- /dev/null +++ b/tests/union.witx @@ -0,0 +1,28 @@ +(use "errno.witx") +(use "excuse.witx") + +(typename $reason + (union $excuse + (field $dog_ate f32) + (field $traffic s32) + (empty $sleeping))) + +(typename $reason_mut + (union $excuse + (field $dog_ate (@witx pointer f32)) + (field $traffic (@witx pointer s32)) + (empty $sleeping))) + +(module $union_example + (@interface func (export "get_tag") + (param $r $reason) + (result $error $errno) + (result $t $excuse) + ) + + (@interface func (export "reason_mult") + (param $r $reason_mut) + (param $multiply_by u32) + (result $error $errno) + ) +) From 126d7b6825e04c0342067b1ce7dc84d7cc4a14a0 Mon Sep 17 00:00:00 2001 From: Pat Hickey Date: Wed, 26 Feb 2020 12:37:54 -0800 Subject: [PATCH 59/86] argument marshaling for unions --- crates/generate/src/funcs.rs | 43 ++++++++++++++++++------------------ 1 file changed, 21 insertions(+), 22 deletions(-) diff --git a/crates/generate/src/funcs.rs b/crates/generate/src/funcs.rs index 8814df19ab..50344f876e 100644 --- a/crates/generate/src/funcs.rs +++ b/crates/generate/src/funcs.rs @@ -153,6 +153,25 @@ fn marshal_arg( } }; + let read_conversion = { + let pointee_type = names.type_ref(tref, anon_lifetime()); + let arg_name = names.func_ptr_binding(¶m.name); + let name = names.func_param(¶m.name); + quote! { + let #name = match memory.ptr::<#pointee_type>(#arg_name as u32) { + Ok(p) => match p.read() { + Ok(r) => r, + Err(e) => { + #error_handling + } + }, + Err(e) => { + #error_handling + } + }; + } + }; + match &*tref.type_() { witx::Type::Enum(_e) => try_into_conversion, witx::Type::Flags(_f) => try_into_conversion, @@ -257,24 +276,7 @@ fn marshal_arg( }; } } - witx::Type::Struct(s) if !struct_is_copy(&s) => { - let pointee_type = names.type_ref(tref, anon_lifetime()); - let arg_name = names.func_ptr_binding(¶m.name); - let name = names.func_param(¶m.name); - quote! { - let #name = match memory.ptr::<#pointee_type>(#arg_name as u32) { - Ok(p) => match p.read() { - Ok(r) => r, - Err(e) => { - #error_handling - } - }, - Err(e) => { - #error_handling - } - }; - } - } + witx::Type::Struct(s) if !struct_is_copy(&s) => read_conversion, witx::Type::Array(arr) => { let pointee_type = names.type_ref(arr, anon_lifetime()); let ptr_name = names.func_ptr_binding(¶m.name); @@ -305,10 +307,7 @@ fn marshal_arg( }; } } - witx::Type::Union(_u) => { - let name = names.func_param(¶m.name); - quote!(let #name = unimplemented!("union argument marshaling");) - } + witx::Type::Union(_u) => read_conversion, _ => unimplemented!("argument type marshalling"), } } From 8ecbceb5de291e58e39e8400824a317232fd1f2c Mon Sep 17 00:00:00 2001 From: Pat Hickey Date: Wed, 26 Feb 2020 12:40:20 -0800 Subject: [PATCH 60/86] handle union result types (simplify logic) --- crates/generate/src/funcs.rs | 22 ++++++---------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/crates/generate/src/funcs.rs b/crates/generate/src/funcs.rs index 50344f876e..fc7593d4e2 100644 --- a/crates/generate/src/funcs.rs +++ b/crates/generate/src/funcs.rs @@ -345,22 +345,12 @@ where match &*tref.type_() { witx::Type::Builtin(b) => match b { - witx::BuiltinType::U8 - | witx::BuiltinType::S8 - | witx::BuiltinType::U16 - | witx::BuiltinType::S16 - | witx::BuiltinType::U32 - | witx::BuiltinType::S32 - | witx::BuiltinType::U64 - | witx::BuiltinType::S64 - | witx::BuiltinType::F32 - | witx::BuiltinType::F64 - | witx::BuiltinType::USize - | witx::BuiltinType::Char8 => write_val_to_ptr, - witx::BuiltinType::String => unimplemented!("string types"), + witx::BuiltinType::String => unimplemented!("string result types"), + _ => write_val_to_ptr, }, - witx::Type::Enum(_) | witx::Type::Flags(_) | witx::Type::Int(_) => write_val_to_ptr, - witx::Type::Struct(_) => write_val_to_ptr, - _ => unimplemented!("missing marshalling result for {:?}", &*tref.type_()), + witx::Type::Pointer { .. } | witx::Type::ConstPointer { .. } | witx::Type::Array { .. } => { + unimplemented!("pointer/array result types") + } + _ => write_val_to_ptr, } } From 02f311cbb1190292218bacc03d5748d6c8a7152d Mon Sep 17 00:00:00 2001 From: Pat Hickey Date: Wed, 26 Feb 2020 14:24:09 -0800 Subject: [PATCH 61/86] finish union testing --- tests/union.rs | 244 +++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 237 insertions(+), 7 deletions(-) diff --git a/tests/union.rs b/tests/union.rs index d5da1bff96..91b50c033f 100644 --- a/tests/union.rs +++ b/tests/union.rs @@ -1,7 +1,5 @@ use proptest::prelude::*; -use wiggle_runtime::{ - GuestError, GuestErrorType, GuestMemory, GuestPtr, GuestPtrMut, GuestRef, GuestRefMut, -}; +use wiggle_runtime::{GuestError, GuestType}; use wiggle_test::{impl_errno, HostMemory, MemArea, WasiCtx}; wiggle_generate::from_witx!({ @@ -11,10 +9,36 @@ wiggle_generate::from_witx!({ impl_errno!(types::Errno); +// Avoid panics on overflow +fn mult_lose_overflow(a: i32, b: u32) -> i32 { + let a_64: i64 = a as i64; + let b_64: i64 = b as i64; + let product = a_64 * b_64; + product as i32 +} + +// Avoid assert_eq(NaN, NaN) failures +fn mult_zero_nan(a: f32, b: u32) -> f32 { + if a.is_nan() { + 0.0 + } else { + let product = a * b as f32; + if product.is_nan() { + 0.0 + } else { + product + } + } +} + impl union_example::UnionExample for WasiCtx { fn get_tag(&mut self, u: &types::Reason) -> Result { println!("GET TAG: {:?}", u); - Ok(types::Excuse::DogAte) + match u { + types::Reason::DogAte { .. } => Ok(types::Excuse::DogAte), + types::Reason::Traffic { .. } => Ok(types::Excuse::Traffic), + types::Reason::Sleeping { .. } => Ok(types::Excuse::Sleeping), + } } fn reason_mult( &mut self, @@ -26,13 +50,13 @@ impl union_example::UnionExample for WasiCtx { let mut f = fptr.as_ref_mut().expect("valid pointer"); let val = *f; println!("REASON MULT DogAte({})", val); - *f = val * multiply_by as f32; + *f = mult_zero_nan(val, multiply_by); } types::ReasonMut::Traffic(iptr) => { let mut i = iptr.as_ref_mut().expect("valid pointer"); - let val = *i; + let val: i32 = *i; println!("REASON MULT Traffic({})", val); - *i = val * multiply_by as i32; + *i = mult_lose_overflow(val, multiply_by); } types::ReasonMut::Sleeping => { println!("REASON MULT Sleeping"); @@ -41,3 +65,209 @@ impl union_example::UnionExample for WasiCtx { Ok(()) } } + +fn reason_strat() -> impl Strategy { + prop_oneof![ + prop::num::f32::ANY.prop_map(|v| types::Reason::DogAte(v)), + prop::num::i32::ANY.prop_map(|v| types::Reason::Traffic(v)), + Just(types::Reason::Sleeping), + ] + .boxed() +} + +fn reason_tag(r: &types::Reason) -> types::Excuse { + match r { + types::Reason::DogAte { .. } => types::Excuse::DogAte, + types::Reason::Traffic { .. } => types::Excuse::Traffic, + types::Reason::Sleeping { .. } => types::Excuse::Sleeping, + } +} + +#[derive(Debug)] +struct GetTagExercise { + pub input: types::Reason, + pub input_loc: MemArea, + pub return_loc: MemArea, +} + +impl GetTagExercise { + pub fn strat() -> BoxedStrategy { + ( + reason_strat(), + HostMemory::mem_area_strat(types::Reason::size()), + HostMemory::mem_area_strat(types::Excuse::size()), + ) + .prop_map(|(input, input_loc, return_loc)| GetTagExercise { + input, + input_loc, + return_loc, + }) + .prop_filter("non-overlapping pointers", |e| { + MemArea::non_overlapping_set(&[&e.input_loc, &e.return_loc]) + }) + .boxed() + } + + pub fn test(&self) { + let mut ctx = WasiCtx::new(); + let mut host_memory = HostMemory::new(); + let mut guest_memory = host_memory.guest_memory(); + + let discriminant: u8 = reason_tag(&self.input).into(); + *guest_memory + .ptr_mut(self.input_loc.ptr) + .expect("input discriminant ptr") + .as_ref_mut() + .expect("input discriminant ref_mut") = discriminant; + match self.input { + types::Reason::DogAte(f) => { + *guest_memory + .ptr_mut(self.input_loc.ptr + 4) + .expect("input contents ptr") + .as_ref_mut() + .expect("input contents ref_mut") = f; + } + types::Reason::Traffic(v) => { + *guest_memory + .ptr_mut(self.input_loc.ptr + 4) + .expect("input contents ptr") + .as_ref_mut() + .expect("input contents ref_mut") = v; + } + types::Reason::Sleeping => {} // Do nothing + } + let e = union_example::get_tag( + &mut ctx, + &mut guest_memory, + self.input_loc.ptr as i32, + self.return_loc.ptr as i32, + ); + + assert_eq!(e, types::Errno::Ok.into(), "get_tag errno"); + + let return_val: types::Excuse = *guest_memory + .ptr(self.return_loc.ptr) + .expect("return ptr") + .as_ref() + .expect("return ref"); + + assert_eq!(return_val, reason_tag(&self.input), "get_tag return value"); + } +} + +proptest! { + #[test] + fn get_tag(e in GetTagExercise::strat()) { + e.test(); + } +} + +#[derive(Debug)] +struct ReasonMultExercise { + pub input: types::Reason, + pub input_loc: MemArea, + pub input_pointee_loc: MemArea, + pub multiply_by: u32, +} + +impl ReasonMultExercise { + pub fn strat() -> BoxedStrategy { + ( + reason_strat(), + HostMemory::mem_area_strat(types::Reason::size()), + HostMemory::mem_area_strat(4), + prop::num::u32::ANY, + ) + .prop_map( + |(input, input_loc, input_pointee_loc, multiply_by)| ReasonMultExercise { + input, + input_loc, + input_pointee_loc, + multiply_by, + }, + ) + .prop_filter("non-overlapping pointers", |e| { + MemArea::non_overlapping_set(&[&e.input_loc, &e.input_pointee_loc]) + }) + .boxed() + } + + pub fn test(&self) { + let mut ctx = WasiCtx::new(); + let mut host_memory = HostMemory::new(); + let mut guest_memory = host_memory.guest_memory(); + + let discriminant: u8 = reason_tag(&self.input).into(); + *guest_memory + .ptr_mut(self.input_loc.ptr) + .expect("input discriminant ptr") + .as_ref_mut() + .expect("input discriminant ref_mut") = discriminant; + *guest_memory + .ptr_mut(self.input_loc.ptr + 4) + .expect("input pointer ptr") + .as_ref_mut() + .expect("input pointer ref_mut") = self.input_pointee_loc.ptr; + + match self.input { + types::Reason::DogAte(f) => { + *guest_memory + .ptr_mut(self.input_pointee_loc.ptr) + .expect("input contents ptr") + .as_ref_mut() + .expect("input contents ref_mut") = f; + } + types::Reason::Traffic(v) => { + *guest_memory + .ptr_mut(self.input_pointee_loc.ptr) + .expect("input contents ptr") + .as_ref_mut() + .expect("input contents ref_mut") = v; + } + types::Reason::Sleeping => {} // Do nothing + } + let e = union_example::reason_mult( + &mut ctx, + &mut guest_memory, + self.input_loc.ptr as i32, + self.multiply_by as i32, + ); + + assert_eq!(e, types::Errno::Ok.into(), "reason_mult errno"); + + match self.input { + types::Reason::DogAte(f) => { + let f_result: f32 = *guest_memory + .ptr(self.input_pointee_loc.ptr) + .expect("input contents ptr") + .as_ref() + .expect("input contents ref_mut"); + assert_eq!( + mult_zero_nan(f, self.multiply_by), + f_result, + "DogAte result" + ) + } + types::Reason::Traffic(v) => { + let v_result: i32 = *guest_memory + .ptr(self.input_pointee_loc.ptr) + .expect("input contents ptr") + .as_ref() + .expect("input contents ref_mut"); + assert_eq!( + mult_lose_overflow(v, self.multiply_by), + v_result, + "Traffic result" + ) + } + types::Reason::Sleeping => {} // Do nothing + } + } +} + +proptest! { + #[test] + fn reason_mult(e in ReasonMultExercise::strat()) { + e.test(); + } +} From aa79e5c4584d21619a4fc5647de619597a613a90 Mon Sep 17 00:00:00 2001 From: Pat Hickey Date: Wed, 26 Feb 2020 14:26:51 -0800 Subject: [PATCH 62/86] propaganda --- tests/union.witx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/union.witx b/tests/union.witx index 7dda97f6e9..d76f150ddd 100644 --- a/tests/union.witx +++ b/tests/union.witx @@ -1,6 +1,9 @@ (use "errno.witx") (use "excuse.witx") +;; Every worker needs a union. Organize your workplace! +;; Fight for the full product of your labor! + (typename $reason (union $excuse (field $dog_ate f32) From 515b751333be2c998d8b02602b6af8bf9a9cf0d2 Mon Sep 17 00:00:00 2001 From: Pat Hickey Date: Wed, 26 Feb 2020 14:32:33 -0800 Subject: [PATCH 63/86] latest witx 0.8.3 --- crates/WASI | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/WASI b/crates/WASI index 6d96ec08bf..19ad34a27e 160000 --- a/crates/WASI +++ b/crates/WASI @@ -1 +1 @@ -Subproject commit 6d96ec08bf976ee5518ae2734bb01f9a8e919e7c +Subproject commit 19ad34a27ebcfa90c56afe0e4af6ad9fc33d3f41 From 10dcaeeab424e532a9d56aa5a4b6219502124649 Mon Sep 17 00:00:00 2001 From: Pat Hickey Date: Wed, 26 Feb 2020 15:25:52 -0800 Subject: [PATCH 64/86] factor lifetime determination into its own module --- crates/generate/src/funcs.rs | 6 +-- crates/generate/src/lib.rs | 1 + crates/generate/src/lifetimes.rs | 56 +++++++++++++++++++++++ crates/generate/src/module_trait.rs | 5 +- crates/generate/src/names.rs | 4 +- crates/generate/src/types.rs | 71 ++--------------------------- 6 files changed, 68 insertions(+), 75 deletions(-) create mode 100644 crates/generate/src/lifetimes.rs diff --git a/crates/generate/src/funcs.rs b/crates/generate/src/funcs.rs index fc7593d4e2..13eff6baa1 100644 --- a/crates/generate/src/funcs.rs +++ b/crates/generate/src/funcs.rs @@ -1,8 +1,8 @@ use proc_macro2::TokenStream; use quote::quote; +use crate::lifetimes::{anon_lifetime, LifetimeExt}; use crate::names::Names; -use crate::types::{anon_lifetime, struct_is_copy}; pub fn define_func(names: &Names, func: &witx::InterfaceFunc) -> TokenStream { let funcname = func.name.as_str(); @@ -258,7 +258,7 @@ fn marshal_arg( }; } } - witx::Type::Struct(s) if struct_is_copy(&s) => { + witx::Type::Struct(s) if !s.needs_lifetime() => { let pointee_type = names.type_ref(tref, anon_lifetime()); let arg_name = names.func_ptr_binding(¶m.name); let name = names.func_param(¶m.name); @@ -276,7 +276,7 @@ fn marshal_arg( }; } } - witx::Type::Struct(s) if !struct_is_copy(&s) => read_conversion, + witx::Type::Struct(s) if s.needs_lifetime() => read_conversion, witx::Type::Array(arr) => { let pointee_type = names.type_ref(arr, anon_lifetime()); let ptr_name = names.func_ptr_binding(¶m.name); diff --git a/crates/generate/src/lib.rs b/crates/generate/src/lib.rs index 4a50d88874..d7ea6d23a4 100644 --- a/crates/generate/src/lib.rs +++ b/crates/generate/src/lib.rs @@ -2,6 +2,7 @@ extern crate proc_macro; mod config; mod funcs; +mod lifetimes; mod module_trait; mod names; mod types; diff --git a/crates/generate/src/lifetimes.rs b/crates/generate/src/lifetimes.rs new file mode 100644 index 0000000000..ae9631a63b --- /dev/null +++ b/crates/generate/src/lifetimes.rs @@ -0,0 +1,56 @@ +use proc_macro2::TokenStream; +use quote::quote; + +pub trait LifetimeExt { + fn needs_lifetime(&self) -> bool; +} + +impl LifetimeExt for witx::TypeRef { + fn needs_lifetime(&self) -> bool { + self.type_().needs_lifetime() + } +} + +impl LifetimeExt for witx::Type { + fn needs_lifetime(&self) -> bool { + match self { + witx::Type::Builtin(b) => b.needs_lifetime(), + witx::Type::Struct(s) => s.needs_lifetime(), + witx::Type::Union(u) => u.needs_lifetime(), + witx::Type::Enum { .. } + | witx::Type::Flags { .. } + | witx::Type::Int { .. } + | witx::Type::Handle { .. } => false, + witx::Type::Pointer { .. } + | witx::Type::ConstPointer { .. } + | witx::Type::Array { .. } => true, + } + } +} + +impl LifetimeExt for witx::BuiltinType { + fn needs_lifetime(&self) -> bool { + match self { + witx::BuiltinType::String => true, + _ => false, + } + } +} + +impl LifetimeExt for witx::StructDatatype { + fn needs_lifetime(&self) -> bool { + self.members.iter().any(|m| m.tref.needs_lifetime()) + } +} + +impl LifetimeExt for witx::UnionDatatype { + fn needs_lifetime(&self) -> bool { + self.variants + .iter() + .any(|m| m.tref.as_ref().map(|t| t.needs_lifetime()).unwrap_or(false)) + } +} + +pub fn anon_lifetime() -> TokenStream { + quote!('_) +} diff --git a/crates/generate/src/module_trait.rs b/crates/generate/src/module_trait.rs index 4cb4c45045..7fc1f46cd2 100644 --- a/crates/generate/src/module_trait.rs +++ b/crates/generate/src/module_trait.rs @@ -1,8 +1,8 @@ use proc_macro2::TokenStream; use quote::quote; +use crate::lifetimes::{anon_lifetime, LifetimeExt}; use crate::names::Names; -use crate::types::{anon_lifetime, type_needs_lifetime}; use witx::Module; pub fn define_module_trait(names: &Names, m: &Module) -> TokenStream { @@ -11,8 +11,7 @@ pub fn define_module_trait(names: &Names, m: &Module) -> TokenStream { // Check if we're returning an entity anotated with a lifetime, // in which case, we'll need to annotate the function itself, and // hence will need an explicit lifetime (rather than anonymous) - let (lifetime, is_anonymous) = if f.results.iter().any(|ret| type_needs_lifetime(&ret.tref)) - { + let (lifetime, is_anonymous) = if f.results.iter().any(|ret| ret.tref.needs_lifetime()) { (quote!('a), false) } else { (anon_lifetime(), true) diff --git a/crates/generate/src/names.rs b/crates/generate/src/names.rs index c967f6e1da..f268514c1d 100644 --- a/crates/generate/src/names.rs +++ b/crates/generate/src/names.rs @@ -3,7 +3,7 @@ use proc_macro2::{Ident, TokenStream}; use quote::{format_ident, quote}; use witx::{AtomType, BuiltinType, Id, TypeRef}; -use crate::types::type_needs_lifetime; +use crate::lifetimes::LifetimeExt; use crate::Config; #[derive(Debug, Clone)] @@ -52,7 +52,7 @@ impl Names { match tref { TypeRef::Name(nt) => { let ident = self.type_(&nt.name); - if type_needs_lifetime(&nt.tref) { + if nt.tref.needs_lifetime() { quote!(#ident<#lifetime>) } else { quote!(#ident) diff --git a/crates/generate/src/types.rs b/crates/generate/src/types.rs index e6767f9144..bd05467d38 100644 --- a/crates/generate/src/types.rs +++ b/crates/generate/src/types.rs @@ -1,3 +1,4 @@ +use crate::lifetimes::{anon_lifetime, LifetimeExt}; use crate::names::Names; use proc_macro2::{Literal, TokenStream}; @@ -13,7 +14,7 @@ pub fn define_datatype(names: &Names, namedtype: &witx::NamedType) -> TokenStrea witx::Type::Int(i) => define_int(names, &namedtype.name, &i), witx::Type::Flags(f) => define_flags(names, &namedtype.name, &f), witx::Type::Struct(s) => { - if struct_is_copy(s) { + if !s.needs_lifetime() { define_copy_struct(names, &namedtype.name, &s) } else { define_ptr_struct(names, &namedtype.name, &s) @@ -39,7 +40,7 @@ pub fn define_datatype(names: &Names, namedtype: &witx::NamedType) -> TokenStrea fn define_alias(names: &Names, name: &witx::Id, to: &witx::NamedType) -> TokenStream { let ident = names.type_(name); let rhs = names.type_(&to.name); - if type_needs_lifetime(&to.tref) { + if to.tref.needs_lifetime() { quote!(pub type #ident<'a> = #rhs<'a>;) } else { quote!(pub type #ident = #rhs;) @@ -407,66 +408,6 @@ fn define_builtin(names: &Names, name: &witx::Id, builtin: witx::BuiltinType) -> } } -// XXX DRY - should move these funcs to be a trait that Type, BuiltinType, StructDatatype, -// UnionDatatype all implement -pub fn type_needs_lifetime(tref: &witx::TypeRef) -> bool { - match &*tref.type_() { - witx::Type::Builtin(b) => match b { - witx::BuiltinType::String => true, - _ => false, - }, - witx::Type::Enum { .. } - | witx::Type::Flags { .. } - | witx::Type::Int { .. } - | witx::Type::Handle { .. } => false, - witx::Type::Struct(s) => !struct_is_copy(&s), - witx::Type::Union(u) => !union_is_copy(&u), - witx::Type::Pointer { .. } | witx::Type::ConstPointer { .. } => true, - witx::Type::Array { .. } => true, - } -} - -pub fn struct_is_copy(s: &witx::StructDatatype) -> bool { - s.members.iter().all(|m| match &*m.tref.type_() { - witx::Type::Struct(s) => struct_is_copy(&s), - witx::Type::Builtin(b) => match &*b { - witx::BuiltinType::String => false, - _ => true, - }, - witx::Type::ConstPointer { .. } | witx::Type::Pointer { .. } | witx::Type::Array { .. } => { - false - } - witx::Type::Union(u) => union_is_copy(u), - witx::Type::Enum { .. } - | witx::Type::Int { .. } - | witx::Type::Flags { .. } - | witx::Type::Handle { .. } => true, - }) -} -pub fn union_is_copy(u: &witx::UnionDatatype) -> bool { - u.variants.iter().all(|m| { - if let Some(tref) = &m.tref { - match &*tref.type_() { - witx::Type::Struct(s) => struct_is_copy(&s), - witx::Type::Builtin(b) => match &*b { - witx::BuiltinType::String => false, - _ => true, - }, - witx::Type::ConstPointer { .. } - | witx::Type::Pointer { .. } - | witx::Type::Array { .. } => false, - witx::Type::Union(u) => union_is_copy(u), - witx::Type::Enum { .. } - | witx::Type::Int { .. } - | witx::Type::Flags { .. } - | witx::Type::Handle { .. } => true, - } - } else { - true - } - }) -} - fn define_copy_struct(names: &Names, name: &witx::Id, s: &witx::StructDatatype) -> TokenStream { let ident = names.type_(name); let size = s.mem_size_align().size as u32; @@ -774,7 +715,7 @@ fn define_union(names: &Names, name: &witx::Id, u: &witx::UnionDatatype) -> Toke }); let validate = union_validate(names, ident.clone(), u, &ulayout); - if union_is_copy(u) { + if !u.needs_lifetime() { // Type does not have a lifetime parameter: quote! { #[derive(Clone, Debug, PartialEq)] @@ -887,7 +828,3 @@ fn atom_token(atom: witx::AtomType) -> TokenStream { witx::AtomType::F64 => quote!(f64), } } - -pub fn anon_lifetime() -> TokenStream { - quote!('_) -} From 90182bc5da17ba3f8c0bcabe38ce5df597cd06e6 Mon Sep 17 00:00:00 2001 From: Pat Hickey Date: Thu, 27 Feb 2020 03:57:36 -0700 Subject: [PATCH 65/86] Handles (#22) * test coverage: make sure aliases to builtins get tested * trivial support for handles * add tests for handles --- crates/generate/src/funcs.rs | 5 +++ crates/generate/src/types.rs | 67 ++++++++++++++++++++++++++++++- tests/atoms.rs | 7 +++- tests/atoms.witx | 4 +- tests/handles.rs | 76 ++++++++++++++++++++++++++++++++++++ tests/handles.witx | 12 ++++++ 6 files changed, 166 insertions(+), 5 deletions(-) create mode 100644 tests/handles.rs create mode 100644 tests/handles.witx diff --git a/crates/generate/src/funcs.rs b/crates/generate/src/funcs.rs index 13eff6baa1..e788032e66 100644 --- a/crates/generate/src/funcs.rs +++ b/crates/generate/src/funcs.rs @@ -308,6 +308,11 @@ fn marshal_arg( } } witx::Type::Union(_u) => read_conversion, + witx::Type::Handle(_h) => { + let name = names.func_param(¶m.name); + let handle_type = names.type_ref(tref, anon_lifetime()); + quote!( let #name = #handle_type::from(#name); ) + } _ => unimplemented!("argument type marshalling"), } } diff --git a/crates/generate/src/types.rs b/crates/generate/src/types.rs index bd05467d38..05154ded3e 100644 --- a/crates/generate/src/types.rs +++ b/crates/generate/src/types.rs @@ -21,7 +21,7 @@ pub fn define_datatype(names: &Names, namedtype: &witx::NamedType) -> TokenStrea } } witx::Type::Union(u) => define_union(names, &namedtype.name, &u), - witx::Type::Handle(_h) => unimplemented!("handle types"), + witx::Type::Handle(h) => define_handle(names, &namedtype.name, &h), witx::Type::Builtin(b) => define_builtin(names, &namedtype.name, *b), witx::Type::Pointer(p) => define_witx_pointer( names, @@ -398,10 +398,73 @@ fn define_enum(names: &Names, name: &witx::Id, e: &witx::EnumDatatype) -> TokenS } } +fn define_handle(names: &Names, name: &witx::Id, h: &witx::HandleDatatype) -> TokenStream { + let ident = names.type_(name); + let size = h.mem_size_align().size as u32; + let align = h.mem_size_align().align as u32; + quote! { + #[derive(Copy, Clone, Debug, ::std::hash::Hash, Eq, PartialEq)] + pub struct #ident(u32); + + impl From<#ident> for u32 { + fn from(e: #ident) -> u32 { + e.0 + } + } + + impl From<#ident> for i32 { + fn from(e: #ident) -> i32 { + e.0 as i32 + } + } + + impl From for #ident { + fn from(e: u32) -> #ident { + #ident(e) + } + } + impl From for #ident { + fn from(e: i32) -> #ident { + #ident(e as u32) + } + } + + impl ::std::fmt::Display for #ident { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + write!(f, "{}({})", stringify!(#ident), self.0) + } + } + impl wiggle_runtime::GuestType for #ident { + fn size() -> u32 { + #size + } + fn align() -> u32 { + #align + } + fn name() -> String { + stringify!(#ident).to_owned() + } + fn validate(ptr: &wiggle_runtime::GuestPtr<#ident>) -> Result<(), wiggle_runtime::GuestError> { + Ok(()) + } + } + impl<'a> wiggle_runtime::GuestTypeClone<'a> for #ident { + fn read_from_guest(location: &wiggle_runtime::GuestPtr<'a, #ident>) -> Result<#ident, wiggle_runtime::GuestError> { + let r = location.as_ref()?; + Ok(*r) + } + fn write_to_guest(&self, location: &wiggle_runtime::GuestPtrMut<'a, Self>) { + unsafe { (location.as_raw() as *mut #ident).write(*self) }; + } + } + impl<'a> wiggle_runtime::GuestTypeCopy<'a> for #ident {} + } +} + fn define_builtin(names: &Names, name: &witx::Id, builtin: witx::BuiltinType) -> TokenStream { let ident = names.type_(name); let built = names.builtin_type(builtin, quote!('a)); - if let witx::BuiltinType::String = builtin { + if builtin.needs_lifetime() { quote!(pub type #ident<'a> = #built;) } else { quote!(pub type #ident = #built;) diff --git a/tests/atoms.rs b/tests/atoms.rs index 216613766f..e9be5479b6 100644 --- a/tests/atoms.rs +++ b/tests/atoms.rs @@ -14,7 +14,10 @@ impl atoms::Atoms for WasiCtx { println!("INT FLOAT ARGS: {} {}", an_int, an_float); Ok(()) } - fn double_int_return_float(&mut self, an_int: u32) -> Result { + fn double_int_return_float( + &mut self, + an_int: u32, + ) -> Result { Ok((an_int as f32) * 2.0) } } @@ -75,7 +78,7 @@ impl DoubleIntExercise { self.return_loc.ptr as i32, ); - let return_val: GuestRef = guest_memory + let return_val: GuestRef = guest_memory .ptr(self.return_loc.ptr) .expect("return loc ptr") .as_ref() diff --git a/tests/atoms.witx b/tests/atoms.witx index ef496d7600..932d7c9ffd 100644 --- a/tests/atoms.witx +++ b/tests/atoms.witx @@ -1,5 +1,7 @@ (use "errno.witx") +(typename $alias_to_float f32) + (module $atoms (@interface func (export "int_float_args") (param $an_int u32) @@ -8,5 +10,5 @@ (@interface func (export "double_int_return_float") (param $an_int u32) (result $error $errno) - (result $doubled_it f32)) + (result $doubled_it $alias_to_float)) ) diff --git a/tests/handles.rs b/tests/handles.rs new file mode 100644 index 0000000000..ed86a04519 --- /dev/null +++ b/tests/handles.rs @@ -0,0 +1,76 @@ +use proptest::prelude::*; +use wiggle_runtime::{GuestError, GuestType}; +use wiggle_test::{impl_errno, HostMemory, MemArea, WasiCtx}; + +const FD_VAL: u32 = 123; + +wiggle_generate::from_witx!({ + witx: ["tests/handles.witx"], + ctx: WasiCtx, +}); + +impl_errno!(types::Errno); + +impl handle_examples::HandleExamples for WasiCtx { + fn fd_create(&mut self) -> Result { + Ok(types::Fd::from(FD_VAL)) + } + fn fd_consume(&mut self, fd: types::Fd) -> Result<(), types::Errno> { + println!("FD_CONSUME {}", fd); + if fd == types::Fd::from(FD_VAL) { + Ok(()) + } else { + Err(types::Errno::InvalidArg) + } + } +} + +#[derive(Debug)] +struct HandleExercise { + pub return_loc: MemArea, +} + +impl HandleExercise { + pub fn test(&self) { + let mut ctx = WasiCtx::new(); + let mut host_memory = HostMemory::new(); + let mut guest_memory = host_memory.guest_memory(); + + let e = handle_examples::fd_create(&mut ctx, &mut guest_memory, self.return_loc.ptr as i32); + + assert_eq!(e, types::Errno::Ok.into(), "fd_create error"); + + let h_got: u32 = *guest_memory + .ptr(self.return_loc.ptr) + .expect("return ptr") + .as_ref() + .expect("return ref_mut"); + + assert_eq!(h_got, 123, "fd_create return val"); + + let e = handle_examples::fd_consume(&mut ctx, &mut guest_memory, h_got as i32); + + assert_eq!(e, types::Errno::Ok.into(), "fd_consume error"); + + let e = handle_examples::fd_consume(&mut ctx, &mut guest_memory, h_got as i32 + 1); + + assert_eq!( + e, + types::Errno::InvalidArg.into(), + "fd_consume invalid error" + ); + } + + pub fn strat() -> BoxedStrategy { + (HostMemory::mem_area_strat(types::Fd::size())) + .prop_map(|return_loc| HandleExercise { return_loc }) + .boxed() + } +} + +proptest! { + #[test] + fn handle_exercise(e in HandleExercise::strat()) { + e.test() + } +} diff --git a/tests/handles.witx b/tests/handles.witx new file mode 100644 index 0000000000..69c1b0546e --- /dev/null +++ b/tests/handles.witx @@ -0,0 +1,12 @@ +(use "errno.witx") + +(typename $fd (handle)) + +(module $handle_examples + (@interface func (export "fd_create") + (result $error $errno) + (result $fd $fd)) + (@interface func (export "fd_consume") + (param $fd $fd) + (result $error $errno)) +) From ed1d5180ef943823f050974e3713adb3a1f89b2e Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 27 Feb 2020 11:10:30 +0100 Subject: [PATCH 66/86] Add TryFromIntError to GuestError and fix error handling While working on the full WASI spec, it turned out that we need two tweaks to `GuestError`: 1. we need to support conversion from `TryFromIntError`, and 2. we need to invoke `e.into()` when unwrapping the result of `try_into()` in auto-implementation of raw interface functions. Both problems seem to originate for "transparent" builtin types since for those we don't really provide a `TryFrom` implementation like for compound types, e.g., enums, flags, etc. --- crates/generate/src/funcs.rs | 2 +- crates/runtime/src/error.rs | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/crates/generate/src/funcs.rs b/crates/generate/src/funcs.rs index e788032e66..eb3a02ac19 100644 --- a/crates/generate/src/funcs.rs +++ b/crates/generate/src/funcs.rs @@ -62,7 +62,7 @@ pub fn define_func(names: &Names, func: &witx::InterfaceFunc) -> TokenStream { }; let err_typename = names.type_ref(&tref, anon_lifetime()); quote! { - let e = wiggle_runtime::GuestError::InFunc { funcname: #funcname, location: #location, err: Box::new(e) }; + let e = wiggle_runtime::GuestError::InFunc { funcname: #funcname, location: #location, err: Box::new(e.into()) }; let err: #err_typename = wiggle_runtime::GuestErrorType::from_error(e, ctx); return #abi_ret::from(err); } diff --git a/crates/runtime/src/error.rs b/crates/runtime/src/error.rs index 9c9e8a8e9a..14c48be1b9 100644 --- a/crates/runtime/src/error.rs +++ b/crates/runtime/src/error.rs @@ -27,6 +27,8 @@ pub enum GuestError { #[source] err: Box, }, - #[error("Invalid UTF-8 encountered")] - InvalidUtf8(#[from] std::str::Utf8Error), + #[error("Invalid UTF-8 encountered: {0:?}")] + InvalidUtf8(#[from] ::std::str::Utf8Error), + #[error("Int conversion error: {0:?}")] + TryFromIntError(#[from] ::std::num::TryFromIntError), } From ec75f874eea6ff5450d8d4a98b9ed9b1d4e3bad5 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 27 Feb 2020 14:25:39 +0100 Subject: [PATCH 67/86] Unify GuestType and GuestTypeClone, rename GuestTypeCopy to GuestTypeTransparent This commit refactors trait system for guest types. Namely, as discussed offline on zulip, `GuestType` now includes `GuestTypeClone`, whereas `GuestTypeCopy` has been renamed to `GuestTypeTransparent`. --- crates/generate/src/types.rs | 146 +++++++++++++++++------------ crates/runtime/src/guest_type.rs | 28 +++--- crates/runtime/src/lib.rs | 2 +- crates/runtime/src/memory/array.rs | 24 ++--- crates/runtime/src/memory/mod.rs | 4 +- crates/runtime/src/memory/ptr.rs | 67 ++++++------- tests/arrays.rs | 14 ++- tests/pointers.rs | 14 ++- 8 files changed, 152 insertions(+), 147 deletions(-) diff --git a/crates/generate/src/types.rs b/crates/generate/src/types.rs index 05154ded3e..a049a72a6d 100644 --- a/crates/generate/src/types.rs +++ b/crates/generate/src/types.rs @@ -105,7 +105,7 @@ fn define_int(names: &Names, name: &witx::Id, i: &witx::IntDatatype) -> TokenStr } } - impl wiggle_runtime::GuestType for #ident { + impl<'a> wiggle_runtime::GuestType<'a> for #ident { fn size() -> u32 { ::std::mem::size_of::<#repr>() as u32 } @@ -118,24 +118,24 @@ fn define_int(names: &Names, name: &witx::Id, i: &witx::IntDatatype) -> TokenStr stringify!(#ident).to_owned() } - fn validate<'a>(location: &wiggle_runtime::GuestPtr<'a, #ident>) -> Result<(), wiggle_runtime::GuestError> { + fn validate(location: &wiggle_runtime::GuestPtr<'a, #ident>) -> Result<(), wiggle_runtime::GuestError> { use ::std::convert::TryFrom; let raw: #repr = unsafe { (location.as_raw() as *const #repr).read() }; let _ = #ident::try_from(raw)?; Ok(()) } - } - impl<'a> wiggle_runtime::GuestTypeCopy<'a> for #ident {} - impl<'a> wiggle_runtime::GuestTypeClone<'a> for #ident { - fn read_from_guest(location: &wiggle_runtime::GuestPtr<#ident>) -> Result<#ident, wiggle_runtime::GuestError> { + fn read(location: &wiggle_runtime::GuestPtr<#ident>) -> Result<#ident, wiggle_runtime::GuestError> { Ok(*location.as_ref()?) } - fn write_to_guest(&self, location: &wiggle_runtime::GuestPtrMut<#ident>) { + + fn write(&self, location: &wiggle_runtime::GuestPtrMut<#ident>) { let val: #repr = #repr::from(*self); unsafe { (location.as_raw() as *mut #repr).write(val) }; } } + + impl<'a> wiggle_runtime::GuestTypeTransparent<'a> for #ident {} } } @@ -258,7 +258,7 @@ fn define_flags(names: &Names, name: &witx::Id, f: &witx::FlagsDatatype) -> Toke } } - impl wiggle_runtime::GuestType for #ident { + impl<'a> wiggle_runtime::GuestType<'a> for #ident { fn size() -> u32 { ::std::mem::size_of::<#repr>() as u32 } @@ -271,24 +271,24 @@ fn define_flags(names: &Names, name: &witx::Id, f: &witx::FlagsDatatype) -> Toke stringify!(#ident).to_owned() } - fn validate<'a>(location: &wiggle_runtime::GuestPtr<'a, #ident>) -> Result<(), wiggle_runtime::GuestError> { + fn validate(location: &wiggle_runtime::GuestPtr<'a, #ident>) -> Result<(), wiggle_runtime::GuestError> { use ::std::convert::TryFrom; let raw: #repr = unsafe { (location.as_raw() as *const #repr).read() }; let _ = #ident::try_from(raw)?; Ok(()) } - } - impl<'a> wiggle_runtime::GuestTypeCopy<'a> for #ident {} - impl<'a> wiggle_runtime::GuestTypeClone<'a> for #ident { - fn read_from_guest(location: &wiggle_runtime::GuestPtr<#ident>) -> Result<#ident, wiggle_runtime::GuestError> { + fn read(location: &wiggle_runtime::GuestPtr<#ident>) -> Result<#ident, wiggle_runtime::GuestError> { Ok(*location.as_ref()?) } - fn write_to_guest(&self, location: &wiggle_runtime::GuestPtrMut<#ident>) { + + fn write(&self, location: &wiggle_runtime::GuestPtrMut<#ident>) { let val: #repr = #repr::from(*self); unsafe { (location.as_raw() as *mut #repr).write(val) }; } } + + impl<'a> wiggle_runtime::GuestTypeTransparent<'a> for #ident {} } } @@ -364,37 +364,39 @@ fn define_enum(names: &Names, name: &witx::Id, e: &witx::EnumDatatype) -> TokenS } } - impl wiggle_runtime::GuestType for #ident { + impl<'a> wiggle_runtime::GuestType<'a> for #ident { fn size() -> u32 { ::std::mem::size_of::<#repr>() as u32 } + fn align() -> u32 { ::std::mem::align_of::<#repr>() as u32 } + fn name() -> String { stringify!(#ident).to_owned() } - fn validate<'a>(location: &wiggle_runtime::GuestPtr<'a, #ident>) -> Result<(), wiggle_runtime::GuestError> { + + fn validate(location: &wiggle_runtime::GuestPtr<'a, #ident>) -> Result<(), wiggle_runtime::GuestError> { use ::std::convert::TryFrom; let raw: #repr = unsafe { (location.as_raw() as *const #repr).read() }; let _ = #ident::try_from(raw)?; Ok(()) } - } - impl<'a> wiggle_runtime::GuestTypeCopy<'a> for #ident {} - impl<'a> wiggle_runtime::GuestTypeClone<'a> for #ident { - fn read_from_guest(location: &wiggle_runtime::GuestPtr<#ident>) -> Result<#ident, wiggle_runtime::GuestError> { + fn read(location: &wiggle_runtime::GuestPtr<#ident>) -> Result<#ident, wiggle_runtime::GuestError> { // Perform validation as part of as_ref: let r = location.as_ref()?; Ok(*r) } - fn write_to_guest(&self, location: &wiggle_runtime::GuestPtrMut<#ident>) { + + fn write(&self, location: &wiggle_runtime::GuestPtrMut<#ident>) { let val: #repr = #repr::from(*self); unsafe { (location.as_raw() as *mut #repr).write(val) }; } } + impl<'a> wiggle_runtime::GuestTypeTransparent<'a> for #ident {} } } @@ -434,30 +436,35 @@ fn define_handle(names: &Names, name: &witx::Id, h: &witx::HandleDatatype) -> To write!(f, "{}({})", stringify!(#ident), self.0) } } - impl wiggle_runtime::GuestType for #ident { + + impl<'a> wiggle_runtime::GuestType<'a> for #ident { fn size() -> u32 { #size } + fn align() -> u32 { #align } + fn name() -> String { stringify!(#ident).to_owned() } + fn validate(ptr: &wiggle_runtime::GuestPtr<#ident>) -> Result<(), wiggle_runtime::GuestError> { Ok(()) } - } - impl<'a> wiggle_runtime::GuestTypeClone<'a> for #ident { - fn read_from_guest(location: &wiggle_runtime::GuestPtr<'a, #ident>) -> Result<#ident, wiggle_runtime::GuestError> { + + fn read(location: &wiggle_runtime::GuestPtr<'a, #ident>) -> Result<#ident, wiggle_runtime::GuestError> { let r = location.as_ref()?; Ok(*r) } - fn write_to_guest(&self, location: &wiggle_runtime::GuestPtrMut<'a, Self>) { + + fn write(&self, location: &wiggle_runtime::GuestPtrMut<'a, Self>) { unsafe { (location.as_raw() as *mut #ident).write(*self) }; } } - impl<'a> wiggle_runtime::GuestTypeCopy<'a> for #ident {} + + impl<'a> wiggle_runtime::GuestTypeTransparent<'a> for #ident {} } } @@ -509,31 +516,35 @@ fn define_copy_struct(names: &Names, name: &witx::Id, s: &witx::StructDatatype) #(#member_decls),* } - impl wiggle_runtime::GuestType for #ident { + impl<'a> wiggle_runtime::GuestType<'a> for #ident { fn size() -> u32 { #size } + fn align() -> u32 { #align } + fn name() -> String { stringify!(#ident).to_owned() } + fn validate(ptr: &wiggle_runtime::GuestPtr<#ident>) -> Result<(), wiggle_runtime::GuestError> { #(#member_valids)* Ok(()) } - } - impl<'a> wiggle_runtime::GuestTypeClone<'a> for #ident { - fn read_from_guest(location: &wiggle_runtime::GuestPtr<'a, #ident>) -> Result<#ident, wiggle_runtime::GuestError> { + + fn read(location: &wiggle_runtime::GuestPtr<'a, #ident>) -> Result<#ident, wiggle_runtime::GuestError> { let r = location.as_ref()?; Ok(*r) } - fn write_to_guest(&self, location: &wiggle_runtime::GuestPtrMut<'a, Self>) { + + fn write(&self, location: &wiggle_runtime::GuestPtrMut<'a, Self>) { unsafe { (location.as_raw() as *mut #ident).write(*self) }; } } - impl<'a> wiggle_runtime::GuestTypeCopy<'a> for #ident {} + + impl<'a> wiggle_runtime::GuestTypeTransparent<'a> for #ident {} } } @@ -604,26 +615,26 @@ fn define_ptr_struct(names: &Names, name: &witx::Id, s: &witx::StructDatatype) - witx::TypeRef::Name(nt) => { let type_ = names.type_(&nt.name); quote! { - let #name = #type_::read_from_guest(&location.cast(#offset)?)?; + let #name = <#type_ as wiggle_runtime::GuestType>::read(&location.cast(#offset)?)?; } } witx::TypeRef::Value(ty) => match &**ty { witx::Type::Builtin(builtin) => { let type_ = names.builtin_type(*builtin, anon_lifetime()); quote! { - let #name = #type_::read_from_guest(&location.cast(#offset)?)?; + let #name = <#type_ as wiggle_runtime::GuestType>::read(&location.cast(#offset)?)?; } } witx::Type::Pointer(pointee) => { let pointee_type = names.type_ref(&pointee, anon_lifetime()); quote! { - let #name = wiggle_runtime::GuestPtrMut::<#pointee_type>::read_from_guest(&location.cast(#offset)?)?; + let #name = as wiggle_runtime::GuestType>::read(&location.cast(#offset)?)?; } } witx::Type::ConstPointer(pointee) => { let pointee_type = names.type_ref(&pointee, anon_lifetime()); quote! { - let #name = wiggle_runtime::GuestPtr::<#pointee_type>::read_from_guest(&location.cast(#offset)?)?; + let #name = as wiggle_runtime::GuestType>::read(&location.cast(#offset)?)?; } } _ => unimplemented!("other anonymous struct members"), @@ -635,7 +646,7 @@ fn define_ptr_struct(names: &Names, name: &witx::Id, s: &witx::StructDatatype) - let name = names.struct_member(&ml.member.name); let offset = ml.offset as u32; quote! { - self.#name.write_to_guest(&location.cast(#offset).expect("cast to inner member")); + self.#name.write(&location.cast(#offset).expect("cast to inner member")); } }); @@ -645,27 +656,30 @@ fn define_ptr_struct(names: &Names, name: &witx::Id, s: &witx::StructDatatype) - #(#member_decls),* } - impl<'a> wiggle_runtime::GuestType for #ident<'a> { + impl<'a> wiggle_runtime::GuestType<'a> for #ident<'a> { fn size() -> u32 { #size } + fn align() -> u32 { #align } + fn name() -> String { stringify!(#ident).to_owned() } - fn validate(ptr: &wiggle_runtime::GuestPtr<#ident>) -> Result<(), wiggle_runtime::GuestError> { + + fn validate(ptr: &wiggle_runtime::GuestPtr<'a, #ident<'a>>) -> Result<(), wiggle_runtime::GuestError> { #(#member_valids)* Ok(()) } - } - impl<'a> wiggle_runtime::GuestTypeClone<'a> for #ident<'a> { - fn read_from_guest(location: &wiggle_runtime::GuestPtr<'a, #ident<'a>>) -> Result<#ident<'a>, wiggle_runtime::GuestError> { + + fn read(location: &wiggle_runtime::GuestPtr<'a, #ident<'a>>) -> Result<#ident<'a>, wiggle_runtime::GuestError> { #(#member_reads)* Ok(#ident { #(#member_names),* }) } - fn write_to_guest(&self, location: &wiggle_runtime::GuestPtrMut<'a, Self>) { + + fn write(&self, location: &wiggle_runtime::GuestPtrMut<'a, Self>) { #(#member_writes)* } } @@ -694,11 +708,12 @@ fn union_validate( let err = with_err(v.name.as_str()); let variantname = names.enum_variant(&v.name); if let Some(tref) = &v.tref { - let varianttype = names.type_ref(tref, anon_lifetime()); + let lifetime = anon_lifetime(); + let varianttype = names.type_ref(tref, lifetime.clone()); quote! { #tagname::#variantname => { let variant_ptr = ptr.cast::<#varianttype>(#contents_offset).map_err(#err)?; - wiggle_runtime::GuestType::validate(&variant_ptr).map_err(#err)?; + <#varianttype as wiggle_runtime::GuestType>::validate(&variant_ptr).map_err(#err)?; } } } else { @@ -743,7 +758,7 @@ fn define_union(names: &Names, name: &witx::Id, u: &witx::UnionDatatype) -> Toke quote! { #tagname::#variantname => { let variant_ptr = location.cast::<#varianttype>(#contents_offset).expect("union variant ptr validated"); - let variant_val = wiggle_runtime::GuestTypeClone::read_from_guest(&variant_ptr)?; + let variant_val = <#varianttype as wiggle_runtime::GuestType>::read(&variant_ptr)?; Ok(#ident::#variantname(variant_val)) } } @@ -765,7 +780,7 @@ fn define_union(names: &Names, name: &witx::Id, u: &witx::UnionDatatype) -> Toke #ident::#variantname(contents) => { #write_tag let variant_ptr = location.cast::<#varianttype>(#contents_offset).expect("union variant ptr validated"); - contents.write_to_guest(&variant_ptr); + <#varianttype as wiggle_runtime::GuestType>::write(&contents, &variant_ptr); } } } else { @@ -785,31 +800,35 @@ fn define_union(names: &Names, name: &witx::Id, u: &witx::UnionDatatype) -> Toke pub enum #ident { #(#variants),* } - impl wiggle_runtime::GuestType for #ident { + + impl<'a> wiggle_runtime::GuestType<'a> for #ident { fn size() -> u32 { #size } + fn align() -> u32 { #align } + fn name() -> String { stringify!(#ident).to_owned() } - fn validate(ptr: &wiggle_runtime::GuestPtr<#ident>) -> Result<(), wiggle_runtime::GuestError> { + + fn validate(ptr: &wiggle_runtime::GuestPtr<'a, #ident>) -> Result<(), wiggle_runtime::GuestError> { #validate } - } - impl<#lifetime> wiggle_runtime::GuestTypeClone<#lifetime> for #ident { - fn read_from_guest(location: &wiggle_runtime::GuestPtr<'a, #ident>) + + fn read(location: &wiggle_runtime::GuestPtr<'a, #ident>) -> Result { - wiggle_runtime::GuestType::validate(location)?; + <#ident as wiggle_runtime::GuestType>::validate(location)?; let tag = *location.cast::<#tagname>(0).expect("validated tag ptr").as_ref().expect("validated tag ref"); match tag { #(#read_variant)* } } - fn write_to_guest(&self, location: &wiggle_runtime::GuestPtrMut<'a, #ident>) { + + fn write(&self, location: &wiggle_runtime::GuestPtrMut<'a, #ident>) { match self { #(#write_variant)* } @@ -823,31 +842,34 @@ fn define_union(names: &Names, name: &witx::Id, u: &witx::UnionDatatype) -> Toke #(#variants),* } - impl<#lifetime> wiggle_runtime::GuestType for #ident<#lifetime> { + impl<#lifetime> wiggle_runtime::GuestType<#lifetime> for #ident<#lifetime> { fn size() -> u32 { #size } + fn align() -> u32 { #align } + fn name() -> String { stringify!(#ident).to_owned() } - fn validate(ptr: &wiggle_runtime::GuestPtr<#ident>) -> Result<(), wiggle_runtime::GuestError> { + + fn validate(ptr: &wiggle_runtime::GuestPtr<#lifetime, #ident<#lifetime>>) -> Result<(), wiggle_runtime::GuestError> { #validate } - } - impl<#lifetime> wiggle_runtime::GuestTypeClone<#lifetime> for #ident<#lifetime> { - fn read_from_guest(location: &wiggle_runtime::GuestPtr<'a, #ident>) + + fn read(location: &wiggle_runtime::GuestPtr<#lifetime, #ident<#lifetime>>) -> Result { - wiggle_runtime::GuestType::validate(location)?; + <#ident as wiggle_runtime::GuestType>::validate(location)?; let tag = *location.cast::<#tagname>(0).expect("validated tag ptr").as_ref().expect("validated tag ref"); match tag { #(#read_variant)* } } - fn write_to_guest(&self, location: &wiggle_runtime::GuestPtrMut<'a, #ident>) { + + fn write(&self, location: &wiggle_runtime::GuestPtrMut<#lifetime, #ident<#lifetime>>) { match self { #(#write_variant)* } diff --git a/crates/runtime/src/guest_type.rs b/crates/runtime/src/guest_type.rs index 6cf0972f2b..2a9eadc59c 100644 --- a/crates/runtime/src/guest_type.rs +++ b/crates/runtime/src/guest_type.rs @@ -1,26 +1,25 @@ use crate::{GuestError, GuestPtr, GuestPtrMut}; -pub trait GuestType: Sized { +pub trait GuestType<'a>: Sized + Clone { // These are morally the same as Rust ::std::mem::size_of / align_of, but they return // a u32 because the wasm memory space is 32 bits. They have a different names so they // don't collide with the std::mem methods. fn size() -> u32; fn align() -> u32; fn name() -> String; - fn validate<'a>(location: &GuestPtr<'a, Self>) -> Result<(), GuestError>; + fn validate(location: &GuestPtr<'a, Self>) -> Result<(), GuestError>; + fn read(location: &GuestPtr<'a, Self>) -> Result; + fn write(&self, location: &GuestPtrMut<'a, Self>); } -pub trait GuestTypeClone<'a>: GuestType + Clone { - fn read_from_guest(location: &GuestPtr<'a, Self>) -> Result; - fn write_to_guest(&self, location: &GuestPtrMut<'a, Self>); -} -// Following trait system in Rust, Copy implies Clone -pub trait GuestTypeCopy<'a>: GuestTypeClone<'a> + Copy {} +/// Represents any guest type which can transparently be represented +/// as a host type. +pub trait GuestTypeTransparent<'a>: GuestType<'a> + Copy {} macro_rules! builtin_type { ( $( $t:ident ), * ) => { $( - impl GuestType for $t { + impl<'a> GuestType<'a> for $t { fn size() -> u32 { ::std::mem::size_of::<$t>() as u32 } @@ -33,16 +32,14 @@ macro_rules! builtin_type { fn validate(_ptr: &GuestPtr<$t>) -> Result<(), GuestError> { Ok(()) } - } - impl<'a> GuestTypeClone<'a> for $t { - fn read_from_guest(location: &GuestPtr<'a, Self>) -> Result { + fn read(location: &GuestPtr<'a, Self>) -> Result { Ok(*location.as_ref()?) } - fn write_to_guest(&self, location: &GuestPtrMut<'a, Self>) { + fn write(&self, location: &GuestPtrMut<'a, Self>) { unsafe { (location.as_raw() as *mut $t).write(*self) }; } } - impl<'a> GuestTypeCopy<'a> for $t {} + impl<'a> GuestTypeTransparent<'a> for $t {} )* }; } @@ -50,9 +47,6 @@ macro_rules! builtin_type { // These definitions correspond to all the witx BuiltinType variants that are Copy: builtin_type!(u8, i8, u16, i16, u32, i32, u64, i64, f32, f64, usize); -// FIXME implement GuestType for char. needs to validate that its a code point. what is the sizeof a char? -// FIXME implement GuestType for String. how does validate work for array types? - pub trait GuestErrorType { type Context; fn success() -> Self; diff --git a/crates/runtime/src/lib.rs b/crates/runtime/src/lib.rs index 839c646b0b..99648c4b1a 100644 --- a/crates/runtime/src/lib.rs +++ b/crates/runtime/src/lib.rs @@ -5,7 +5,7 @@ mod memory; mod region; pub use error::GuestError; -pub use guest_type::{GuestErrorType, GuestType, GuestTypeClone, GuestTypeCopy}; +pub use guest_type::{GuestErrorType, GuestType, GuestTypeTransparent}; pub use memory::{ GuestArray, GuestMemory, GuestPtr, GuestPtrMut, GuestRef, GuestRefMut, GuestString, GuestStringRef, diff --git a/crates/runtime/src/memory/array.rs b/crates/runtime/src/memory/array.rs index db3408cc3f..3c7815536b 100644 --- a/crates/runtime/src/memory/array.rs +++ b/crates/runtime/src/memory/array.rs @@ -1,11 +1,11 @@ use super::ptr::{GuestPtr, GuestRef}; -use crate::{GuestError, GuestType, GuestTypeCopy}; +use crate::{GuestError, GuestType, GuestTypeTransparent}; use std::{fmt, ops::Deref}; #[derive(Clone)] pub struct GuestArray<'a, T> where - T: GuestType, + T: GuestType<'a>, { pub(super) ptr: GuestPtr<'a, T>, pub(super) num_elems: u32, @@ -13,7 +13,7 @@ where impl<'a, T> fmt::Debug for GuestArray<'a, T> where - T: GuestType + fmt::Debug, + T: GuestType<'a> + fmt::Debug, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( @@ -26,7 +26,7 @@ where impl<'a, T> GuestArray<'a, T> where - T: GuestType, + T: GuestType<'a>, { pub fn iter(&self) -> GuestArrayIter<'a, T> { let next = GuestPtr { @@ -44,7 +44,7 @@ where pub struct GuestArrayIter<'a, T> where - T: GuestType, + T: GuestType<'a>, { next: GuestPtr<'a, T>, num_elems: u32, @@ -53,7 +53,7 @@ where impl<'a, T> Iterator for GuestArrayIter<'a, T> where - T: GuestType, + T: GuestType<'a>, { type Item = Result, GuestError>; @@ -79,7 +79,7 @@ where impl<'a, T> GuestArray<'a, T> where - T: GuestTypeCopy<'a>, + T: GuestTypeTransparent<'a>, { pub fn as_ref(&self) -> Result, GuestError> { let mut next = self.ptr.elem(0)?; @@ -109,7 +109,7 @@ where pub struct GuestArrayRef<'a, T> where - T: GuestTypeCopy<'a>, + T: GuestTypeTransparent<'a>, { ref_: GuestRef<'a, T>, num_elems: u32, @@ -117,7 +117,7 @@ where impl<'a, T> fmt::Debug for GuestArrayRef<'a, T> where - T: GuestTypeCopy<'a> + fmt::Debug, + T: GuestTypeTransparent<'a> + fmt::Debug, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( @@ -130,7 +130,7 @@ where impl<'a, T> Deref for GuestArrayRef<'a, T> where - T: GuestTypeCopy<'a>, + T: GuestTypeTransparent<'a>, { type Target = [T]; @@ -148,7 +148,7 @@ where mod test { use crate::{ memory::ptr::{GuestPtr, GuestPtrMut}, - GuestError, GuestMemory, GuestTypeClone, Region, + GuestError, GuestMemory, GuestType, Region, }; #[repr(align(4096))] @@ -249,7 +249,7 @@ mod test { let contents = arr .iter() .map(|ptr_ptr| { - *GuestTypeClone::read_from_guest(&ptr_ptr.expect("valid ptr to ptr")) + *GuestType::read(&ptr_ptr.expect("valid ptr to ptr")) .expect("valid ptr to some value") .as_ref() .expect("deref ptr to some value") diff --git a/crates/runtime/src/memory/mod.rs b/crates/runtime/src/memory/mod.rs index 2800da56f1..9fd8f907fe 100644 --- a/crates/runtime/src/memory/mod.rs +++ b/crates/runtime/src/memory/mod.rs @@ -43,7 +43,7 @@ impl<'a> GuestMemory<'a> { && r.start <= (self.len - r.len) } - pub fn ptr(&'a self, at: u32) -> Result, GuestError> { + pub fn ptr>(&'a self, at: u32) -> Result, GuestError> { let region = Region { start: at, len: T::size(), @@ -61,7 +61,7 @@ impl<'a> GuestMemory<'a> { }) } - pub fn ptr_mut(&'a self, at: u32) -> Result, GuestError> { + pub fn ptr_mut>(&'a self, at: u32) -> Result, GuestError> { let ptr = self.ptr(at)?; Ok(GuestPtrMut { mem: ptr.mem, diff --git a/crates/runtime/src/memory/ptr.rs b/crates/runtime/src/memory/ptr.rs index 9e853c3742..a09068ae25 100644 --- a/crates/runtime/src/memory/ptr.rs +++ b/crates/runtime/src/memory/ptr.rs @@ -1,5 +1,5 @@ use super::{array::GuestArray, GuestMemory}; -use crate::{borrow::BorrowHandle, GuestError, GuestType, GuestTypeClone, GuestTypeCopy, Region}; +use crate::{borrow::BorrowHandle, GuestError, GuestType, GuestTypeTransparent, Region}; use std::{ fmt, marker::PhantomData, @@ -26,7 +26,7 @@ where } } -impl<'a, T: GuestType> GuestPtr<'a, T> { +impl<'a, T: GuestType<'a>> GuestPtr<'a, T> { pub fn as_raw(&self) -> *const u8 { (self.mem.ptr as usize + self.region.start as usize) as *const u8 } @@ -36,7 +36,10 @@ impl<'a, T: GuestType> GuestPtr<'a, T> { .ptr(self.region.start + (elements * self.region.len as i32) as u32) } - pub fn cast(&self, offset: u32) -> Result, GuestError> { + pub fn cast>( + &self, + offset: u32, + ) -> Result, GuestError> { self.mem.ptr(self.region.start + offset) } @@ -57,7 +60,7 @@ impl<'a, T: GuestType> GuestPtr<'a, T> { impl<'a, T> GuestPtr<'a, T> where - T: GuestTypeCopy<'a>, + T: GuestTypeTransparent<'a>, { pub fn as_ref(&self) -> Result, GuestError> { T::validate(&self)?; @@ -78,16 +81,16 @@ where impl<'a, T> GuestPtr<'a, T> where - T: GuestTypeClone<'a>, + T: GuestType<'a>, { pub fn read(&self) -> Result { - T::read_from_guest(self) + T::read(self) } } -impl<'a, T> GuestType for GuestPtr<'a, T> +impl<'a, T> GuestType<'a> for GuestPtr<'a, T> where - T: GuestType, + T: GuestType<'a>, { fn size() -> u32 { 4 @@ -101,21 +104,16 @@ where format!("GuestPtr<{}>", T::name()) } - fn validate<'b>(location: &GuestPtr<'b, GuestPtr<'b, T>>) -> Result<(), GuestError> { + fn validate(location: &GuestPtr<'a, GuestPtr<'a, T>>) -> Result<(), GuestError> { // location is guaranteed to be in GuestMemory and aligned to 4 let raw_ptr: u32 = unsafe { *(location.as_raw() as *const u32) }; // GuestMemory can validate that the raw pointer contents are legal for T: let _guest_ptr: GuestPtr = location.mem.ptr(raw_ptr)?; Ok(()) } -} -// Operations for reading and writing Ptrs to memory: -impl<'a, T> GuestTypeClone<'a> for GuestPtr<'a, T> -where - T: GuestType + Clone, -{ - fn read_from_guest(location: &GuestPtr<'a, Self>) -> Result { + // Operations for reading and writing Ptrs to memory: + fn read(location: &GuestPtr<'a, Self>) -> Result { // location is guaranteed to be in GuestMemory and aligned to 4 let raw_ptr: u32 = unsafe { *(location.as_raw() as *const u32) }; // GuestMemory can validate that the raw pointer contents are legal for T: @@ -123,7 +121,7 @@ where Ok(guest_ptr) } - fn write_to_guest(&self, location: &GuestPtrMut<'a, Self>) { + fn write(&self, location: &GuestPtrMut<'a, Self>) { // location is guaranteed to be in GuestMemory and aligned to 4 unsafe { let raw_ptr: *mut u32 = location.as_raw() as *mut u32; @@ -154,7 +152,7 @@ where impl<'a, T> GuestPtrMut<'a, T> where - T: GuestType, + T: GuestType<'a>, { pub fn as_immut(&self) -> GuestPtr<'a, T> { GuestPtr { @@ -173,7 +171,7 @@ where .ptr_mut(self.region.start + (elements * self.region.len as i32) as u32) } - pub fn cast( + pub fn cast>( &self, offset: u32, ) -> Result, GuestError> { @@ -183,7 +181,7 @@ where impl<'a, T> GuestPtrMut<'a, T> where - T: GuestTypeCopy<'a>, + T: GuestTypeTransparent<'a>, { pub fn as_ref(&self) -> Result, GuestError> { self.as_immut().as_ref() @@ -208,20 +206,20 @@ where impl<'a, T> GuestPtrMut<'a, T> where - T: GuestTypeClone<'a>, + T: GuestType<'a>, { pub fn read(&self) -> Result { - T::read_from_guest(&self.as_immut()) + T::read(&self.as_immut()) } pub fn write(&self, ptr: &T) { - T::write_to_guest(ptr, &self); + T::write(ptr, &self); } } -impl<'a, T> GuestType for GuestPtrMut<'a, T> +impl<'a, T> GuestType<'a> for GuestPtrMut<'a, T> where - T: GuestType, + T: GuestType<'a>, { fn size() -> u32 { 4 @@ -235,21 +233,16 @@ where format!("GuestPtrMut<{}>", T::name()) } - fn validate<'b>(location: &GuestPtr<'b, GuestPtrMut<'b, T>>) -> Result<(), GuestError> { + fn validate(location: &GuestPtr<'a, GuestPtrMut<'a, T>>) -> Result<(), GuestError> { // location is guaranteed to be in GuestMemory and aligned to 4 let raw_ptr: u32 = unsafe { *(location.as_raw() as *const u32) }; // GuestMemory can validate that the raw pointer contents are legal for T: let _guest_ptr: GuestPtr = location.mem.ptr(raw_ptr)?; Ok(()) } -} -// Reading and writing GuestPtrMuts to memory: -impl<'a, T> GuestTypeClone<'a> for GuestPtrMut<'a, T> -where - T: GuestType + Clone, -{ - fn read_from_guest(location: &GuestPtr<'a, Self>) -> Result { + // Reading and writing GuestPtrMuts to memory: + fn read(location: &GuestPtr<'a, Self>) -> Result { // location is guaranteed to be in GuestMemory and aligned to 4 let raw_ptr: u32 = unsafe { *(location.as_raw() as *const u32) }; // GuestMemory can validate that the raw pointer contents are legal for T: @@ -257,7 +250,7 @@ where Ok(guest_ptr_mut) } - fn write_to_guest(&self, location: &GuestPtrMut<'a, Self>) { + fn write(&self, location: &GuestPtrMut<'a, Self>) { // location is guaranteed to be in GuestMemory and aligned to 4 unsafe { let raw_ptr: *mut u32 = location.as_raw() as *mut u32; @@ -298,7 +291,7 @@ impl<'a, T> GuestRef<'a, T> { impl<'a, T> Deref for GuestRef<'a, T> where - T: GuestTypeCopy<'a>, + T: GuestTypeTransparent<'a>, { type Target = T; @@ -357,7 +350,7 @@ impl<'a, T> GuestRefMut<'a, T> { impl<'a, T> ::std::ops::Deref for GuestRefMut<'a, T> where - T: GuestTypeCopy<'a>, + T: GuestTypeTransparent<'a>, { type Target = T; @@ -372,7 +365,7 @@ where impl<'a, T> DerefMut for GuestRefMut<'a, T> where - T: GuestTypeCopy<'a>, + T: GuestTypeTransparent<'a>, { fn deref_mut(&mut self) -> &mut Self::Target { unsafe { diff --git a/tests/arrays.rs b/tests/arrays.rs index a5acfdbd45..0a5e0641fa 100644 --- a/tests/arrays.rs +++ b/tests/arrays.rs @@ -1,5 +1,5 @@ use proptest::prelude::*; -use wiggle_runtime::{GuestArray, GuestError, GuestPtr, GuestPtrMut}; +use wiggle_runtime::{GuestArray, GuestError, GuestPtr, GuestPtrMut, GuestType}; use wiggle_test::{impl_errno, HostMemory, MemArea, WasiCtx}; wiggle_generate::from_witx!({ @@ -14,7 +14,7 @@ impl arrays::Arrays for WasiCtx { &mut self, excuses: &types::ConstExcuseArray, ) -> Result { - let last = wiggle_runtime::GuestTypeClone::read_from_guest( + let last = GuestType::read( &excuses .iter() .last() @@ -27,9 +27,8 @@ impl arrays::Arrays for WasiCtx { fn populate_excuses(&mut self, excuses: &types::ExcuseArray) -> Result<(), types::Errno> { for excuse in excuses.iter() { - let ptr_to_ptr = - wiggle_runtime::GuestTypeClone::read_from_guest(&excuse.expect("valid ptr to ptr")) - .expect("valid ptr to some Excuse value"); + let ptr_to_ptr = GuestType::read(&excuse.expect("valid ptr to ptr")) + .expect("valid ptr to some Excuse value"); let mut ptr = ptr_to_ptr .as_ref_mut() .expect("dereferencing mut ptr should succeed"); @@ -226,9 +225,8 @@ impl PopulateExcusesExcercise { .array(self.elements.len() as u32) .expect("as array"); for el in arr.iter() { - let ptr_to_ptr = - wiggle_runtime::GuestTypeClone::read_from_guest(&el.expect("valid ptr to ptr")) - .expect("valid ptr to some Excuse value"); + let ptr_to_ptr = GuestType::read(&el.expect("valid ptr to ptr")) + .expect("valid ptr to some Excuse value"); assert_eq!( *ptr_to_ptr .as_ref() diff --git a/tests/pointers.rs b/tests/pointers.rs index e22e1d67f2..e086c59061 100644 --- a/tests/pointers.rs +++ b/tests/pointers.rs @@ -1,5 +1,5 @@ use proptest::prelude::*; -use wiggle_runtime::{GuestError, GuestPtr, GuestPtrMut, GuestRefMut}; +use wiggle_runtime::{GuestError, GuestPtr, GuestPtrMut, GuestRefMut, GuestType}; use wiggle_test::{impl_errno, HostMemory, MemArea, WasiCtx}; wiggle_generate::from_witx!({ @@ -38,13 +38,11 @@ impl pointers::Pointers for WasiCtx { println!("wrote to input2_ref {:?}", input3); // Read ptr value from mutable ptr: - let input4_ptr: GuestPtr = wiggle_runtime::GuestTypeClone::read_from_guest( - &input4_ptr_ptr.as_immut(), - ) - .map_err(|e| { - eprintln!("input4_ptr_ptr error: {}", e); - types::Errno::InvalidArg - })?; + let input4_ptr: GuestPtr = GuestType::read(&input4_ptr_ptr.as_immut()) + .map_err(|e| { + eprintln!("input4_ptr_ptr error: {}", e); + types::Errno::InvalidArg + })?; // Read enum value from that ptr: let input4: types::Excuse = *input4_ptr.as_ref().map_err(|e| { From 0fe3f11194e795d219d420655123d9ad3bac10f7 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 27 Feb 2020 22:09:45 +0100 Subject: [PATCH 68/86] Split wiggle-generate type generation into modules This commit splits `generate/src/types.rs` into submodules, each responsible for specifying a (compound) type. `generate/src/types.rs` grew in size a lot, and IMHO this should yield readability and maintenance. --- crates/generate/src/types.rs | 915 ---------------------------- crates/generate/src/types/enum.rs | 113 ++++ crates/generate/src/types/flags.rs | 159 +++++ crates/generate/src/types/handle.rs | 77 +++ crates/generate/src/types/int.rs | 97 +++ crates/generate/src/types/mod.rs | 93 +++ crates/generate/src/types/struct.rs | 226 +++++++ crates/generate/src/types/union.rs | 199 ++++++ 8 files changed, 964 insertions(+), 915 deletions(-) delete mode 100644 crates/generate/src/types.rs create mode 100644 crates/generate/src/types/enum.rs create mode 100644 crates/generate/src/types/flags.rs create mode 100644 crates/generate/src/types/handle.rs create mode 100644 crates/generate/src/types/int.rs create mode 100644 crates/generate/src/types/mod.rs create mode 100644 crates/generate/src/types/struct.rs create mode 100644 crates/generate/src/types/union.rs diff --git a/crates/generate/src/types.rs b/crates/generate/src/types.rs deleted file mode 100644 index a049a72a6d..0000000000 --- a/crates/generate/src/types.rs +++ /dev/null @@ -1,915 +0,0 @@ -use crate::lifetimes::{anon_lifetime, LifetimeExt}; -use crate::names::Names; - -use proc_macro2::{Literal, TokenStream}; -use quote::quote; -use std::convert::TryFrom; -use witx::Layout; - -pub fn define_datatype(names: &Names, namedtype: &witx::NamedType) -> TokenStream { - match &namedtype.tref { - witx::TypeRef::Name(alias_to) => define_alias(names, &namedtype.name, &alias_to), - witx::TypeRef::Value(v) => match &**v { - witx::Type::Enum(e) => define_enum(names, &namedtype.name, &e), - witx::Type::Int(i) => define_int(names, &namedtype.name, &i), - witx::Type::Flags(f) => define_flags(names, &namedtype.name, &f), - witx::Type::Struct(s) => { - if !s.needs_lifetime() { - define_copy_struct(names, &namedtype.name, &s) - } else { - define_ptr_struct(names, &namedtype.name, &s) - } - } - witx::Type::Union(u) => define_union(names, &namedtype.name, &u), - witx::Type::Handle(h) => define_handle(names, &namedtype.name, &h), - witx::Type::Builtin(b) => define_builtin(names, &namedtype.name, *b), - witx::Type::Pointer(p) => define_witx_pointer( - names, - &namedtype.name, - quote!(wiggle_runtime::GuestPtrMut), - p, - ), - witx::Type::ConstPointer(p) => { - define_witx_pointer(names, &namedtype.name, quote!(wiggle_runtime::GuestPtr), p) - } - witx::Type::Array(arr) => define_witx_array(names, &namedtype.name, &arr), - }, - } -} - -fn define_alias(names: &Names, name: &witx::Id, to: &witx::NamedType) -> TokenStream { - let ident = names.type_(name); - let rhs = names.type_(&to.name); - if to.tref.needs_lifetime() { - quote!(pub type #ident<'a> = #rhs<'a>;) - } else { - quote!(pub type #ident = #rhs;) - } -} - -fn define_int(names: &Names, name: &witx::Id, i: &witx::IntDatatype) -> TokenStream { - let ident = names.type_(&name); - let repr = int_repr_tokens(i.repr); - let abi_repr = atom_token(match i.repr { - witx::IntRepr::U8 | witx::IntRepr::U16 | witx::IntRepr::U32 => witx::AtomType::I32, - witx::IntRepr::U64 => witx::AtomType::I64, - }); - let consts = i - .consts - .iter() - .map(|r#const| { - let const_ident = names.int_member(&r#const.name); - let value = r#const.value; - quote!(pub const #const_ident: #ident = #ident(#value)) - }) - .collect::>(); - - quote! { - #[repr(transparent)] - #[derive(Copy, Clone, Debug, ::std::hash::Hash, Eq, PartialEq)] - pub struct #ident(#repr); - - impl #ident { - #(#consts;)* - } - - impl ::std::fmt::Display for #ident { - fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { - write!(f, "{:?}", self) - } - } - - impl ::std::convert::TryFrom<#repr> for #ident { - type Error = wiggle_runtime::GuestError; - fn try_from(value: #repr) -> Result { - Ok(#ident(value)) - } - } - - impl ::std::convert::TryFrom<#abi_repr> for #ident { - type Error = wiggle_runtime::GuestError; - fn try_from(value: #abi_repr) -> Result<#ident, wiggle_runtime::GuestError> { - #ident::try_from(value as #repr) - } - } - - impl From<#ident> for #repr { - fn from(e: #ident) -> #repr { - e.0 - } - } - - impl From<#ident> for #abi_repr { - fn from(e: #ident) -> #abi_repr { - #repr::from(e) as #abi_repr - } - } - - impl<'a> wiggle_runtime::GuestType<'a> for #ident { - fn size() -> u32 { - ::std::mem::size_of::<#repr>() as u32 - } - - fn align() -> u32 { - ::std::mem::align_of::<#repr>() as u32 - } - - fn name() -> String { - stringify!(#ident).to_owned() - } - - fn validate(location: &wiggle_runtime::GuestPtr<'a, #ident>) -> Result<(), wiggle_runtime::GuestError> { - use ::std::convert::TryFrom; - let raw: #repr = unsafe { (location.as_raw() as *const #repr).read() }; - let _ = #ident::try_from(raw)?; - Ok(()) - } - - fn read(location: &wiggle_runtime::GuestPtr<#ident>) -> Result<#ident, wiggle_runtime::GuestError> { - Ok(*location.as_ref()?) - } - - fn write(&self, location: &wiggle_runtime::GuestPtrMut<#ident>) { - let val: #repr = #repr::from(*self); - unsafe { (location.as_raw() as *mut #repr).write(val) }; - } - } - - impl<'a> wiggle_runtime::GuestTypeTransparent<'a> for #ident {} - } -} - -fn define_flags(names: &Names, name: &witx::Id, f: &witx::FlagsDatatype) -> TokenStream { - let ident = names.type_(&name); - let repr = int_repr_tokens(f.repr); - let abi_repr = atom_token(match f.repr { - witx::IntRepr::U8 | witx::IntRepr::U16 | witx::IntRepr::U32 => witx::AtomType::I32, - witx::IntRepr::U64 => witx::AtomType::I64, - }); - - let mut flag_constructors = vec![]; - let mut all_values = 0; - for (i, f) in f.flags.iter().enumerate() { - let name = names.flag_member(&f.name); - let value = 1u128 - .checked_shl(u32::try_from(i).expect("flag value overflow")) - .expect("flag value overflow"); - let value_token = Literal::u128_unsuffixed(value); - flag_constructors.push(quote!(pub const #name: #ident = #ident(#value_token))); - all_values += value; - } - let all_values_token = Literal::u128_unsuffixed(all_values); - - let ident_str = ident.to_string(); - - quote! { - #[repr(transparent)] - #[derive(Copy, Clone, Debug, ::std::hash::Hash, Eq, PartialEq)] - pub struct #ident(#repr); - - impl #ident { - #(#flag_constructors);*; - pub const ALL_FLAGS: #ident = #ident(#all_values_token); - - pub fn contains(&self, other: &#ident) -> bool { - #repr::from(!*self & *other) == 0 as #repr - } - } - - impl ::std::fmt::Display for #ident { - fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { - write!(f, "{}({:#b})", #ident_str, self.0) - } - } - - impl ::std::ops::BitAnd for #ident { - type Output = Self; - fn bitand(self, rhs: Self) -> Self::Output { - #ident(self.0 & rhs.0) - } - } - - impl ::std::ops::BitAndAssign for #ident { - fn bitand_assign(&mut self, rhs: Self) { - *self = *self & rhs - } - } - - impl ::std::ops::BitOr for #ident { - type Output = Self; - fn bitor(self, rhs: Self) -> Self::Output { - #ident(self.0 | rhs.0) - } - } - - impl ::std::ops::BitOrAssign for #ident { - fn bitor_assign(&mut self, rhs: Self) { - *self = *self | rhs - } - } - - impl ::std::ops::BitXor for #ident { - type Output = Self; - fn bitxor(self, rhs: Self) -> Self::Output { - #ident(self.0 ^ rhs.0) - } - } - - impl ::std::ops::BitXorAssign for #ident { - fn bitxor_assign(&mut self, rhs: Self) { - *self = *self ^ rhs - } - } - - impl ::std::ops::Not for #ident { - type Output = Self; - fn not(self) -> Self::Output { - #ident(!self.0) - } - } - - impl ::std::convert::TryFrom<#repr> for #ident { - type Error = wiggle_runtime::GuestError; - fn try_from(value: #repr) -> Result { - if #repr::from(!#ident::ALL_FLAGS) & value != 0 { - Err(wiggle_runtime::GuestError::InvalidFlagValue(stringify!(#ident))) - } else { - Ok(#ident(value)) - } - } - } - - impl ::std::convert::TryFrom<#abi_repr> for #ident { - type Error = wiggle_runtime::GuestError; - fn try_from(value: #abi_repr) -> Result<#ident, wiggle_runtime::GuestError> { - #ident::try_from(value as #repr) - } - } - - impl From<#ident> for #repr { - fn from(e: #ident) -> #repr { - e.0 - } - } - - impl From<#ident> for #abi_repr { - fn from(e: #ident) -> #abi_repr { - #repr::from(e) as #abi_repr - } - } - - impl<'a> wiggle_runtime::GuestType<'a> for #ident { - fn size() -> u32 { - ::std::mem::size_of::<#repr>() as u32 - } - - fn align() -> u32 { - ::std::mem::align_of::<#repr>() as u32 - } - - fn name() -> String { - stringify!(#ident).to_owned() - } - - fn validate(location: &wiggle_runtime::GuestPtr<'a, #ident>) -> Result<(), wiggle_runtime::GuestError> { - use ::std::convert::TryFrom; - let raw: #repr = unsafe { (location.as_raw() as *const #repr).read() }; - let _ = #ident::try_from(raw)?; - Ok(()) - } - - fn read(location: &wiggle_runtime::GuestPtr<#ident>) -> Result<#ident, wiggle_runtime::GuestError> { - Ok(*location.as_ref()?) - } - - fn write(&self, location: &wiggle_runtime::GuestPtrMut<#ident>) { - let val: #repr = #repr::from(*self); - unsafe { (location.as_raw() as *mut #repr).write(val) }; - } - } - - impl<'a> wiggle_runtime::GuestTypeTransparent<'a> for #ident {} - } -} - -fn define_enum(names: &Names, name: &witx::Id, e: &witx::EnumDatatype) -> TokenStream { - let ident = names.type_(&name); - - let repr = int_repr_tokens(e.repr); - let abi_repr = atom_token(match e.repr { - witx::IntRepr::U8 | witx::IntRepr::U16 | witx::IntRepr::U32 => witx::AtomType::I32, - witx::IntRepr::U64 => witx::AtomType::I64, - }); - - let mut variant_names = vec![]; - let mut tryfrom_repr_cases = vec![]; - let mut to_repr_cases = vec![]; - let mut to_display = vec![]; - - for (n, variant) in e.variants.iter().enumerate() { - let variant_name = names.enum_variant(&variant.name); - let docs = variant.docs.trim(); - let ident_str = ident.to_string(); - let variant_str = variant_name.to_string(); - tryfrom_repr_cases.push(quote!(#n => Ok(#ident::#variant_name))); - to_repr_cases.push(quote!(#ident::#variant_name => #n as #repr)); - to_display.push(quote!(#ident::#variant_name => format!("{} ({}::{}({}))", #docs, #ident_str, #variant_str, #repr::from(*self)))); - variant_names.push(variant_name); - } - - quote! { - #[repr(#repr)] - #[derive(Copy, Clone, Debug, ::std::hash::Hash, Eq, PartialEq)] - pub enum #ident { - #(#variant_names),* - } - - impl ::std::fmt::Display for #ident { - fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { - let to_str = match self { - #(#to_display,)* - }; - write!(f, "{}", to_str) - } - } - - impl ::std::convert::TryFrom<#repr> for #ident { - type Error = wiggle_runtime::GuestError; - fn try_from(value: #repr) -> Result<#ident, wiggle_runtime::GuestError> { - match value as usize { - #(#tryfrom_repr_cases),*, - _ => Err(wiggle_runtime::GuestError::InvalidEnumValue(stringify!(#ident))), - } - } - } - - impl ::std::convert::TryFrom<#abi_repr> for #ident { - type Error = wiggle_runtime::GuestError; - fn try_from(value: #abi_repr) -> Result<#ident, wiggle_runtime::GuestError> { - #ident::try_from(value as #repr) - } - } - - impl From<#ident> for #repr { - fn from(e: #ident) -> #repr { - match e { - #(#to_repr_cases),* - } - } - } - - impl From<#ident> for #abi_repr { - fn from(e: #ident) -> #abi_repr { - #repr::from(e) as #abi_repr - } - } - - impl<'a> wiggle_runtime::GuestType<'a> for #ident { - fn size() -> u32 { - ::std::mem::size_of::<#repr>() as u32 - } - - fn align() -> u32 { - ::std::mem::align_of::<#repr>() as u32 - } - - fn name() -> String { - stringify!(#ident).to_owned() - } - - fn validate(location: &wiggle_runtime::GuestPtr<'a, #ident>) -> Result<(), wiggle_runtime::GuestError> { - use ::std::convert::TryFrom; - let raw: #repr = unsafe { (location.as_raw() as *const #repr).read() }; - let _ = #ident::try_from(raw)?; - Ok(()) - } - - fn read(location: &wiggle_runtime::GuestPtr<#ident>) -> Result<#ident, wiggle_runtime::GuestError> { - // Perform validation as part of as_ref: - let r = location.as_ref()?; - Ok(*r) - } - - fn write(&self, location: &wiggle_runtime::GuestPtrMut<#ident>) { - let val: #repr = #repr::from(*self); - unsafe { (location.as_raw() as *mut #repr).write(val) }; - } - } - - impl<'a> wiggle_runtime::GuestTypeTransparent<'a> for #ident {} - } -} - -fn define_handle(names: &Names, name: &witx::Id, h: &witx::HandleDatatype) -> TokenStream { - let ident = names.type_(name); - let size = h.mem_size_align().size as u32; - let align = h.mem_size_align().align as u32; - quote! { - #[derive(Copy, Clone, Debug, ::std::hash::Hash, Eq, PartialEq)] - pub struct #ident(u32); - - impl From<#ident> for u32 { - fn from(e: #ident) -> u32 { - e.0 - } - } - - impl From<#ident> for i32 { - fn from(e: #ident) -> i32 { - e.0 as i32 - } - } - - impl From for #ident { - fn from(e: u32) -> #ident { - #ident(e) - } - } - impl From for #ident { - fn from(e: i32) -> #ident { - #ident(e as u32) - } - } - - impl ::std::fmt::Display for #ident { - fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { - write!(f, "{}({})", stringify!(#ident), self.0) - } - } - - impl<'a> wiggle_runtime::GuestType<'a> for #ident { - fn size() -> u32 { - #size - } - - fn align() -> u32 { - #align - } - - fn name() -> String { - stringify!(#ident).to_owned() - } - - fn validate(ptr: &wiggle_runtime::GuestPtr<#ident>) -> Result<(), wiggle_runtime::GuestError> { - Ok(()) - } - - fn read(location: &wiggle_runtime::GuestPtr<'a, #ident>) -> Result<#ident, wiggle_runtime::GuestError> { - let r = location.as_ref()?; - Ok(*r) - } - - fn write(&self, location: &wiggle_runtime::GuestPtrMut<'a, Self>) { - unsafe { (location.as_raw() as *mut #ident).write(*self) }; - } - } - - impl<'a> wiggle_runtime::GuestTypeTransparent<'a> for #ident {} - } -} - -fn define_builtin(names: &Names, name: &witx::Id, builtin: witx::BuiltinType) -> TokenStream { - let ident = names.type_(name); - let built = names.builtin_type(builtin, quote!('a)); - if builtin.needs_lifetime() { - quote!(pub type #ident<'a> = #built;) - } else { - quote!(pub type #ident = #built;) - } -} - -fn define_copy_struct(names: &Names, name: &witx::Id, s: &witx::StructDatatype) -> TokenStream { - let ident = names.type_(name); - let size = s.mem_size_align().size as u32; - let align = s.mem_size_align().align as u32; - - let member_decls = s.members.iter().map(|m| { - let name = names.struct_member(&m.name); - let type_ = names.type_ref(&m.tref, anon_lifetime()); - quote!(pub #name: #type_) - }); - let member_valids = s.member_layout().into_iter().map(|ml| { - let type_ = names.type_ref(&ml.member.tref, anon_lifetime()); - let offset = ml.offset as u32; - let fieldname = names.struct_member(&ml.member.name); - quote! { - #type_::validate( - &ptr.cast(#offset).map_err(|e| - wiggle_runtime::GuestError::InDataField{ - typename: stringify!(#ident).to_owned(), - field: stringify!(#fieldname).to_owned(), - err: Box::new(e), - })? - ).map_err(|e| - wiggle_runtime::GuestError::InDataField { - typename: stringify!(#ident).to_owned(), - field: stringify!(#fieldname).to_owned(), - err: Box::new(e), - })?; - } - }); - - quote! { - #[repr(C)] - #[derive(Copy, Clone, Debug, ::std::hash::Hash, Eq, PartialEq)] - pub struct #ident { - #(#member_decls),* - } - - impl<'a> wiggle_runtime::GuestType<'a> for #ident { - fn size() -> u32 { - #size - } - - fn align() -> u32 { - #align - } - - fn name() -> String { - stringify!(#ident).to_owned() - } - - fn validate(ptr: &wiggle_runtime::GuestPtr<#ident>) -> Result<(), wiggle_runtime::GuestError> { - #(#member_valids)* - Ok(()) - } - - fn read(location: &wiggle_runtime::GuestPtr<'a, #ident>) -> Result<#ident, wiggle_runtime::GuestError> { - let r = location.as_ref()?; - Ok(*r) - } - - fn write(&self, location: &wiggle_runtime::GuestPtrMut<'a, Self>) { - unsafe { (location.as_raw() as *mut #ident).write(*self) }; - } - } - - impl<'a> wiggle_runtime::GuestTypeTransparent<'a> for #ident {} - } -} - -fn define_ptr_struct(names: &Names, name: &witx::Id, s: &witx::StructDatatype) -> TokenStream { - let ident = names.type_(name); - let size = s.mem_size_align().size as u32; - let align = s.mem_size_align().align as u32; - - let member_names = s.members.iter().map(|m| names.struct_member(&m.name)); - let member_decls = s.members.iter().map(|m| { - let name = names.struct_member(&m.name); - 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, quote!('a)), - witx::Type::Pointer(pointee) => { - let pointee_type = names.type_ref(&pointee, quote!('a)); - quote!(wiggle_runtime::GuestPtrMut<'a, #pointee_type>) - } - witx::Type::ConstPointer(pointee) => { - let pointee_type = names.type_ref(&pointee, quote!('a)); - quote!(wiggle_runtime::GuestPtr<'a, #pointee_type>) - } - _ => unimplemented!("other anonymous struct members"), - }, - }; - quote!(pub #name: #type_) - }); - let member_valids = s.member_layout().into_iter().map(|ml| { - 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, quote!('a)), - witx::Type::Pointer(pointee) => { - let pointee_type = names.type_ref(&pointee, anon_lifetime()); - quote!(wiggle_runtime::GuestPtrMut::<#pointee_type>) - } - witx::Type::ConstPointer(pointee) => { - let pointee_type = names.type_ref(&pointee, anon_lifetime()); - quote!(wiggle_runtime::GuestPtr::<#pointee_type>) - } - _ => unimplemented!("other anonymous struct members"), - }, - }; - let offset = ml.offset as u32; - let fieldname = names.struct_member(&ml.member.name); - quote! { - #type_::validate( - &ptr.cast(#offset).map_err(|e| - wiggle_runtime::GuestError::InDataField{ - typename: stringify!(#ident).to_owned(), - field: stringify!(#fieldname).to_owned(), - err: Box::new(e), - })? - ).map_err(|e| - wiggle_runtime::GuestError::InDataField { - typename: stringify!(#ident).to_owned(), - field: stringify!(#fieldname).to_owned(), - err: Box::new(e), - })?; - } - }); - - let member_reads = s.member_layout().into_iter().map(|ml| { - let name = names.struct_member(&ml.member.name); - let offset = ml.offset as u32; - match &ml.member.tref { - witx::TypeRef::Name(nt) => { - let type_ = names.type_(&nt.name); - quote! { - let #name = <#type_ as wiggle_runtime::GuestType>::read(&location.cast(#offset)?)?; - } - } - witx::TypeRef::Value(ty) => match &**ty { - witx::Type::Builtin(builtin) => { - let type_ = names.builtin_type(*builtin, anon_lifetime()); - quote! { - let #name = <#type_ as wiggle_runtime::GuestType>::read(&location.cast(#offset)?)?; - } - } - witx::Type::Pointer(pointee) => { - let pointee_type = names.type_ref(&pointee, anon_lifetime()); - quote! { - let #name = as wiggle_runtime::GuestType>::read(&location.cast(#offset)?)?; - } - } - witx::Type::ConstPointer(pointee) => { - let pointee_type = names.type_ref(&pointee, anon_lifetime()); - quote! { - let #name = as wiggle_runtime::GuestType>::read(&location.cast(#offset)?)?; - } - } - _ => unimplemented!("other anonymous struct members"), - }, - } - }); - - let member_writes = s.member_layout().into_iter().map(|ml| { - let name = names.struct_member(&ml.member.name); - let offset = ml.offset as u32; - quote! { - self.#name.write(&location.cast(#offset).expect("cast to inner member")); - } - }); - - quote! { - #[derive(Clone)] - pub struct #ident<'a> { - #(#member_decls),* - } - - impl<'a> wiggle_runtime::GuestType<'a> for #ident<'a> { - fn size() -> u32 { - #size - } - - fn align() -> u32 { - #align - } - - fn name() -> String { - stringify!(#ident).to_owned() - } - - fn validate(ptr: &wiggle_runtime::GuestPtr<'a, #ident<'a>>) -> Result<(), wiggle_runtime::GuestError> { - #(#member_valids)* - Ok(()) - } - - fn read(location: &wiggle_runtime::GuestPtr<'a, #ident<'a>>) -> Result<#ident<'a>, wiggle_runtime::GuestError> { - #(#member_reads)* - Ok(#ident { #(#member_names),* }) - } - - fn write(&self, location: &wiggle_runtime::GuestPtrMut<'a, Self>) { - #(#member_writes)* - } - } - } -} - -fn union_validate( - names: &Names, - typename: TokenStream, - u: &witx::UnionDatatype, - ulayout: &witx::UnionLayout, -) -> TokenStream { - let tagname = names.type_(&u.tag.name); - let contents_offset = ulayout.contents_offset as u32; - - let with_err = |f: &str| -> TokenStream { - quote!(|e| wiggle_runtime::GuestError::InDataField { - typename: stringify!(#typename).to_owned(), - field: #f.to_owned(), - err: Box::new(e), - }) - }; - - let tag_err = with_err(""); - let variant_validation = u.variants.iter().map(|v| { - let err = with_err(v.name.as_str()); - let variantname = names.enum_variant(&v.name); - if let Some(tref) = &v.tref { - let lifetime = anon_lifetime(); - let varianttype = names.type_ref(tref, lifetime.clone()); - quote! { - #tagname::#variantname => { - let variant_ptr = ptr.cast::<#varianttype>(#contents_offset).map_err(#err)?; - <#varianttype as wiggle_runtime::GuestType>::validate(&variant_ptr).map_err(#err)?; - } - } - } else { - quote! { #tagname::#variantname => {} } - } - }); - - quote! { - let tag = *ptr.cast::<#tagname>(0).map_err(#tag_err)?.as_ref().map_err(#tag_err)?; - match tag { - #(#variant_validation)* - } - Ok(()) - } -} - -fn define_union(names: &Names, name: &witx::Id, u: &witx::UnionDatatype) -> TokenStream { - let ident = names.type_(name); - let size = u.mem_size_align().size as u32; - let align = u.mem_size_align().align as u32; - let ulayout = u.union_layout(); - let contents_offset = ulayout.contents_offset as u32; - - let lifetime = quote!('a); - - let variants = u.variants.iter().map(|v| { - let var_name = names.enum_variant(&v.name); - if let Some(tref) = &v.tref { - let var_type = names.type_ref(&tref, lifetime.clone()); - quote!(#var_name(#var_type)) - } else { - quote!(#var_name) - } - }); - - let tagname = names.type_(&u.tag.name); - - let read_variant = u.variants.iter().map(|v| { - let variantname = names.enum_variant(&v.name); - if let Some(tref) = &v.tref { - let varianttype = names.type_ref(tref, lifetime.clone()); - quote! { - #tagname::#variantname => { - let variant_ptr = location.cast::<#varianttype>(#contents_offset).expect("union variant ptr validated"); - let variant_val = <#varianttype as wiggle_runtime::GuestType>::read(&variant_ptr)?; - Ok(#ident::#variantname(variant_val)) - } - } - } else { - quote! { #tagname::#variantname => Ok(#ident::#variantname), } - } - }); - - let write_variant = u.variants.iter().map(|v| { - let variantname = names.enum_variant(&v.name); - let write_tag = quote! { - let tag_ptr = location.cast::<#tagname>(0).expect("union tag ptr TODO error report"); - let mut tag_ref = tag_ptr.as_ref_mut().expect("union tag ref TODO error report"); - *tag_ref = #tagname::#variantname; - }; - if let Some(tref) = &v.tref { - let varianttype = names.type_ref(tref, lifetime.clone()); - quote! { - #ident::#variantname(contents) => { - #write_tag - let variant_ptr = location.cast::<#varianttype>(#contents_offset).expect("union variant ptr validated"); - <#varianttype as wiggle_runtime::GuestType>::write(&contents, &variant_ptr); - } - } - } else { - quote! { - #ident::#variantname => { - #write_tag - } - } - } - }); - let validate = union_validate(names, ident.clone(), u, &ulayout); - - if !u.needs_lifetime() { - // Type does not have a lifetime parameter: - quote! { - #[derive(Clone, Debug, PartialEq)] - pub enum #ident { - #(#variants),* - } - - impl<'a> wiggle_runtime::GuestType<'a> for #ident { - fn size() -> u32 { - #size - } - - fn align() -> u32 { - #align - } - - fn name() -> String { - stringify!(#ident).to_owned() - } - - fn validate(ptr: &wiggle_runtime::GuestPtr<'a, #ident>) -> Result<(), wiggle_runtime::GuestError> { - #validate - } - - fn read(location: &wiggle_runtime::GuestPtr<'a, #ident>) - -> Result { - <#ident as wiggle_runtime::GuestType>::validate(location)?; - let tag = *location.cast::<#tagname>(0).expect("validated tag ptr").as_ref().expect("validated tag ref"); - match tag { - #(#read_variant)* - } - - } - - fn write(&self, location: &wiggle_runtime::GuestPtrMut<'a, #ident>) { - match self { - #(#write_variant)* - } - } - } - } - } else { - quote! { - #[derive(Clone)] - pub enum #ident<#lifetime> { - #(#variants),* - } - - impl<#lifetime> wiggle_runtime::GuestType<#lifetime> for #ident<#lifetime> { - fn size() -> u32 { - #size - } - - fn align() -> u32 { - #align - } - - fn name() -> String { - stringify!(#ident).to_owned() - } - - fn validate(ptr: &wiggle_runtime::GuestPtr<#lifetime, #ident<#lifetime>>) -> Result<(), wiggle_runtime::GuestError> { - #validate - } - - fn read(location: &wiggle_runtime::GuestPtr<#lifetime, #ident<#lifetime>>) - -> Result { - <#ident as wiggle_runtime::GuestType>::validate(location)?; - let tag = *location.cast::<#tagname>(0).expect("validated tag ptr").as_ref().expect("validated tag ref"); - match tag { - #(#read_variant)* - } - - } - - fn write(&self, location: &wiggle_runtime::GuestPtrMut<#lifetime, #ident<#lifetime>>) { - match self { - #(#write_variant)* - } - } - } - } - } -} - -fn define_witx_pointer( - names: &Names, - name: &witx::Id, - pointer_type: TokenStream, - pointee: &witx::TypeRef, -) -> TokenStream { - let ident = names.type_(name); - let pointee_type = names.type_ref(pointee, quote!('a)); - - quote!(pub type #ident<'a> = #pointer_type<'a, #pointee_type>;) -} - -fn define_witx_array(names: &Names, name: &witx::Id, arr_raw: &witx::TypeRef) -> TokenStream { - let ident = names.type_(name); - let pointee_type = names.type_ref(arr_raw, quote!('a)); - quote!(pub type #ident<'a> = wiggle_runtime::GuestArray<'a, #pointee_type>;) -} - -fn int_repr_tokens(int_repr: witx::IntRepr) -> TokenStream { - match int_repr { - witx::IntRepr::U8 => quote!(u8), - witx::IntRepr::U16 => quote!(u16), - witx::IntRepr::U32 => quote!(u32), - witx::IntRepr::U64 => quote!(u64), - } -} -fn atom_token(atom: witx::AtomType) -> TokenStream { - match atom { - witx::AtomType::I32 => quote!(i32), - witx::AtomType::I64 => quote!(i64), - witx::AtomType::F32 => quote!(f32), - witx::AtomType::F64 => quote!(f64), - } -} diff --git a/crates/generate/src/types/enum.rs b/crates/generate/src/types/enum.rs new file mode 100644 index 0000000000..e40d6b50de --- /dev/null +++ b/crates/generate/src/types/enum.rs @@ -0,0 +1,113 @@ +use super::{atom_token, int_repr_tokens}; +use crate::names::Names; + +use proc_macro2::TokenStream; +use quote::quote; + +pub(super) fn define_enum(names: &Names, name: &witx::Id, e: &witx::EnumDatatype) -> TokenStream { + let ident = names.type_(&name); + + let repr = int_repr_tokens(e.repr); + let abi_repr = atom_token(match e.repr { + witx::IntRepr::U8 | witx::IntRepr::U16 | witx::IntRepr::U32 => witx::AtomType::I32, + witx::IntRepr::U64 => witx::AtomType::I64, + }); + + let mut variant_names = vec![]; + let mut tryfrom_repr_cases = vec![]; + let mut to_repr_cases = vec![]; + let mut to_display = vec![]; + + for (n, variant) in e.variants.iter().enumerate() { + let variant_name = names.enum_variant(&variant.name); + let docs = variant.docs.trim(); + let ident_str = ident.to_string(); + let variant_str = variant_name.to_string(); + tryfrom_repr_cases.push(quote!(#n => Ok(#ident::#variant_name))); + to_repr_cases.push(quote!(#ident::#variant_name => #n as #repr)); + to_display.push(quote!(#ident::#variant_name => format!("{} ({}::{}({}))", #docs, #ident_str, #variant_str, #repr::from(*self)))); + variant_names.push(variant_name); + } + + quote! { + #[repr(#repr)] + #[derive(Copy, Clone, Debug, ::std::hash::Hash, Eq, PartialEq)] + pub enum #ident { + #(#variant_names),* + } + + impl ::std::fmt::Display for #ident { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + let to_str = match self { + #(#to_display,)* + }; + write!(f, "{}", to_str) + } + } + + impl ::std::convert::TryFrom<#repr> for #ident { + type Error = wiggle_runtime::GuestError; + fn try_from(value: #repr) -> Result<#ident, wiggle_runtime::GuestError> { + match value as usize { + #(#tryfrom_repr_cases),*, + _ => Err(wiggle_runtime::GuestError::InvalidEnumValue(stringify!(#ident))), + } + } + } + + impl ::std::convert::TryFrom<#abi_repr> for #ident { + type Error = wiggle_runtime::GuestError; + fn try_from(value: #abi_repr) -> Result<#ident, wiggle_runtime::GuestError> { + #ident::try_from(value as #repr) + } + } + + impl From<#ident> for #repr { + fn from(e: #ident) -> #repr { + match e { + #(#to_repr_cases),* + } + } + } + + impl From<#ident> for #abi_repr { + fn from(e: #ident) -> #abi_repr { + #repr::from(e) as #abi_repr + } + } + + impl<'a> wiggle_runtime::GuestType<'a> for #ident { + fn size() -> u32 { + ::std::mem::size_of::<#repr>() as u32 + } + + fn align() -> u32 { + ::std::mem::align_of::<#repr>() as u32 + } + + fn name() -> String { + stringify!(#ident).to_owned() + } + + fn validate(location: &wiggle_runtime::GuestPtr<'a, #ident>) -> Result<(), wiggle_runtime::GuestError> { + use ::std::convert::TryFrom; + let raw: #repr = unsafe { (location.as_raw() as *const #repr).read() }; + let _ = #ident::try_from(raw)?; + Ok(()) + } + + fn read(location: &wiggle_runtime::GuestPtr<#ident>) -> Result<#ident, wiggle_runtime::GuestError> { + // Perform validation as part of as_ref: + let r = location.as_ref()?; + Ok(*r) + } + + fn write(&self, location: &wiggle_runtime::GuestPtrMut<#ident>) { + let val: #repr = #repr::from(*self); + unsafe { (location.as_raw() as *mut #repr).write(val) }; + } + } + + impl<'a> wiggle_runtime::GuestTypeTransparent<'a> for #ident {} + } +} diff --git a/crates/generate/src/types/flags.rs b/crates/generate/src/types/flags.rs new file mode 100644 index 0000000000..8837ceb796 --- /dev/null +++ b/crates/generate/src/types/flags.rs @@ -0,0 +1,159 @@ +use super::{atom_token, int_repr_tokens}; +use crate::names::Names; + +use proc_macro2::{Literal, TokenStream}; +use quote::quote; +use std::convert::TryFrom; + +pub(super) fn define_flags(names: &Names, name: &witx::Id, f: &witx::FlagsDatatype) -> TokenStream { + let ident = names.type_(&name); + let repr = int_repr_tokens(f.repr); + let abi_repr = atom_token(match f.repr { + witx::IntRepr::U8 | witx::IntRepr::U16 | witx::IntRepr::U32 => witx::AtomType::I32, + witx::IntRepr::U64 => witx::AtomType::I64, + }); + + let mut flag_constructors = vec![]; + let mut all_values = 0; + for (i, f) in f.flags.iter().enumerate() { + let name = names.flag_member(&f.name); + let value = 1u128 + .checked_shl(u32::try_from(i).expect("flag value overflow")) + .expect("flag value overflow"); + let value_token = Literal::u128_unsuffixed(value); + flag_constructors.push(quote!(pub const #name: #ident = #ident(#value_token))); + all_values += value; + } + let all_values_token = Literal::u128_unsuffixed(all_values); + + let ident_str = ident.to_string(); + + quote! { + #[repr(transparent)] + #[derive(Copy, Clone, Debug, ::std::hash::Hash, Eq, PartialEq)] + pub struct #ident(#repr); + + impl #ident { + #(#flag_constructors);*; + pub const ALL_FLAGS: #ident = #ident(#all_values_token); + + pub fn contains(&self, other: &#ident) -> bool { + #repr::from(!*self & *other) == 0 as #repr + } + } + + impl ::std::fmt::Display for #ident { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + write!(f, "{}({:#b})", #ident_str, self.0) + } + } + + impl ::std::ops::BitAnd for #ident { + type Output = Self; + fn bitand(self, rhs: Self) -> Self::Output { + #ident(self.0 & rhs.0) + } + } + + impl ::std::ops::BitAndAssign for #ident { + fn bitand_assign(&mut self, rhs: Self) { + *self = *self & rhs + } + } + + impl ::std::ops::BitOr for #ident { + type Output = Self; + fn bitor(self, rhs: Self) -> Self::Output { + #ident(self.0 | rhs.0) + } + } + + impl ::std::ops::BitOrAssign for #ident { + fn bitor_assign(&mut self, rhs: Self) { + *self = *self | rhs + } + } + + impl ::std::ops::BitXor for #ident { + type Output = Self; + fn bitxor(self, rhs: Self) -> Self::Output { + #ident(self.0 ^ rhs.0) + } + } + + impl ::std::ops::BitXorAssign for #ident { + fn bitxor_assign(&mut self, rhs: Self) { + *self = *self ^ rhs + } + } + + impl ::std::ops::Not for #ident { + type Output = Self; + fn not(self) -> Self::Output { + #ident(!self.0) + } + } + + impl ::std::convert::TryFrom<#repr> for #ident { + type Error = wiggle_runtime::GuestError; + fn try_from(value: #repr) -> Result { + if #repr::from(!#ident::ALL_FLAGS) & value != 0 { + Err(wiggle_runtime::GuestError::InvalidFlagValue(stringify!(#ident))) + } else { + Ok(#ident(value)) + } + } + } + + impl ::std::convert::TryFrom<#abi_repr> for #ident { + type Error = wiggle_runtime::GuestError; + fn try_from(value: #abi_repr) -> Result<#ident, wiggle_runtime::GuestError> { + #ident::try_from(value as #repr) + } + } + + impl From<#ident> for #repr { + fn from(e: #ident) -> #repr { + e.0 + } + } + + impl From<#ident> for #abi_repr { + fn from(e: #ident) -> #abi_repr { + #repr::from(e) as #abi_repr + } + } + + impl<'a> wiggle_runtime::GuestType<'a> for #ident { + fn size() -> u32 { + ::std::mem::size_of::<#repr>() as u32 + } + + fn align() -> u32 { + ::std::mem::align_of::<#repr>() as u32 + } + + fn name() -> String { + stringify!(#ident).to_owned() + } + + fn validate(location: &wiggle_runtime::GuestPtr<'a, #ident>) -> Result<(), wiggle_runtime::GuestError> { + use ::std::convert::TryFrom; + let raw: #repr = unsafe { (location.as_raw() as *const #repr).read() }; + let _ = #ident::try_from(raw)?; + Ok(()) + } + + fn read(location: &wiggle_runtime::GuestPtr<#ident>) -> Result<#ident, wiggle_runtime::GuestError> { + Ok(*location.as_ref()?) + } + + fn write(&self, location: &wiggle_runtime::GuestPtrMut<#ident>) { + let val: #repr = #repr::from(*self); + unsafe { (location.as_raw() as *mut #repr).write(val) }; + } + } + + impl<'a> wiggle_runtime::GuestTypeTransparent<'a> for #ident {} + } +} diff --git a/crates/generate/src/types/handle.rs b/crates/generate/src/types/handle.rs new file mode 100644 index 0000000000..ab83593508 --- /dev/null +++ b/crates/generate/src/types/handle.rs @@ -0,0 +1,77 @@ +use crate::names::Names; + +use proc_macro2::TokenStream; +use quote::quote; +use witx::Layout; + +pub(super) fn define_handle( + names: &Names, + name: &witx::Id, + h: &witx::HandleDatatype, +) -> TokenStream { + let ident = names.type_(name); + let size = h.mem_size_align().size as u32; + let align = h.mem_size_align().align as u32; + quote! { + #[derive(Copy, Clone, Debug, ::std::hash::Hash, Eq, PartialEq)] + pub struct #ident(u32); + + impl From<#ident> for u32 { + fn from(e: #ident) -> u32 { + e.0 + } + } + + impl From<#ident> for i32 { + fn from(e: #ident) -> i32 { + e.0 as i32 + } + } + + impl From for #ident { + fn from(e: u32) -> #ident { + #ident(e) + } + } + impl From for #ident { + fn from(e: i32) -> #ident { + #ident(e as u32) + } + } + + impl ::std::fmt::Display for #ident { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + write!(f, "{}({})", stringify!(#ident), self.0) + } + } + + impl<'a> wiggle_runtime::GuestType<'a> for #ident { + fn size() -> u32 { + #size + } + + fn align() -> u32 { + #align + } + + fn name() -> String { + stringify!(#ident).to_owned() + } + + fn validate(ptr: &wiggle_runtime::GuestPtr<#ident>) -> Result<(), wiggle_runtime::GuestError> { + Ok(()) + } + + fn read(location: &wiggle_runtime::GuestPtr<'a, #ident>) -> Result<#ident, wiggle_runtime::GuestError> { + let r = location.as_ref()?; + Ok(*r) + } + + fn write(&self, location: &wiggle_runtime::GuestPtrMut<'a, Self>) { + unsafe { (location.as_raw() as *mut #ident).write(*self) }; + } + } + + impl<'a> wiggle_runtime::GuestTypeTransparent<'a> for #ident {} + } +} diff --git a/crates/generate/src/types/int.rs b/crates/generate/src/types/int.rs new file mode 100644 index 0000000000..bb2f40afee --- /dev/null +++ b/crates/generate/src/types/int.rs @@ -0,0 +1,97 @@ +use super::{atom_token, int_repr_tokens}; +use crate::names::Names; + +use proc_macro2::TokenStream; +use quote::quote; + +pub(super) fn define_int(names: &Names, name: &witx::Id, i: &witx::IntDatatype) -> TokenStream { + let ident = names.type_(&name); + let repr = int_repr_tokens(i.repr); + let abi_repr = atom_token(match i.repr { + witx::IntRepr::U8 | witx::IntRepr::U16 | witx::IntRepr::U32 => witx::AtomType::I32, + witx::IntRepr::U64 => witx::AtomType::I64, + }); + let consts = i + .consts + .iter() + .map(|r#const| { + let const_ident = names.int_member(&r#const.name); + let value = r#const.value; + quote!(pub const #const_ident: #ident = #ident(#value)) + }) + .collect::>(); + + quote! { + #[repr(transparent)] + #[derive(Copy, Clone, Debug, ::std::hash::Hash, Eq, PartialEq)] + pub struct #ident(#repr); + + impl #ident { + #(#consts;)* + } + + impl ::std::fmt::Display for #ident { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + write!(f, "{:?}", self) + } + } + + impl ::std::convert::TryFrom<#repr> for #ident { + type Error = wiggle_runtime::GuestError; + fn try_from(value: #repr) -> Result { + Ok(#ident(value)) + } + } + + impl ::std::convert::TryFrom<#abi_repr> for #ident { + type Error = wiggle_runtime::GuestError; + fn try_from(value: #abi_repr) -> Result<#ident, wiggle_runtime::GuestError> { + #ident::try_from(value as #repr) + } + } + + impl From<#ident> for #repr { + fn from(e: #ident) -> #repr { + e.0 + } + } + + impl From<#ident> for #abi_repr { + fn from(e: #ident) -> #abi_repr { + #repr::from(e) as #abi_repr + } + } + + impl<'a> wiggle_runtime::GuestType<'a> for #ident { + fn size() -> u32 { + ::std::mem::size_of::<#repr>() as u32 + } + + fn align() -> u32 { + ::std::mem::align_of::<#repr>() as u32 + } + + fn name() -> String { + stringify!(#ident).to_owned() + } + + fn validate(location: &wiggle_runtime::GuestPtr<'a, #ident>) -> Result<(), wiggle_runtime::GuestError> { + use ::std::convert::TryFrom; + let raw: #repr = unsafe { (location.as_raw() as *const #repr).read() }; + let _ = #ident::try_from(raw)?; + Ok(()) + } + + fn read(location: &wiggle_runtime::GuestPtr<#ident>) -> Result<#ident, wiggle_runtime::GuestError> { + Ok(*location.as_ref()?) + } + + fn write(&self, location: &wiggle_runtime::GuestPtrMut<#ident>) { + let val: #repr = #repr::from(*self); + unsafe { (location.as_raw() as *mut #repr).write(val) }; + } + } + + impl<'a> wiggle_runtime::GuestTypeTransparent<'a> for #ident {} + } +} diff --git a/crates/generate/src/types/mod.rs b/crates/generate/src/types/mod.rs new file mode 100644 index 0000000000..9d633d0c75 --- /dev/null +++ b/crates/generate/src/types/mod.rs @@ -0,0 +1,93 @@ +mod r#enum; +mod flags; +mod handle; +mod int; +mod r#struct; +mod union; + +use crate::lifetimes::LifetimeExt; +use crate::names::Names; + +use proc_macro2::TokenStream; +use quote::quote; + +pub fn define_datatype(names: &Names, namedtype: &witx::NamedType) -> TokenStream { + match &namedtype.tref { + witx::TypeRef::Name(alias_to) => define_alias(names, &namedtype.name, &alias_to), + witx::TypeRef::Value(v) => match &**v { + witx::Type::Enum(e) => r#enum::define_enum(names, &namedtype.name, &e), + witx::Type::Int(i) => int::define_int(names, &namedtype.name, &i), + witx::Type::Flags(f) => flags::define_flags(names, &namedtype.name, &f), + witx::Type::Struct(s) => r#struct::define_struct(names, &namedtype.name, &s), + witx::Type::Union(u) => union::define_union(names, &namedtype.name, &u), + witx::Type::Handle(h) => handle::define_handle(names, &namedtype.name, &h), + witx::Type::Builtin(b) => define_builtin(names, &namedtype.name, *b), + witx::Type::Pointer(p) => define_witx_pointer( + names, + &namedtype.name, + quote!(wiggle_runtime::GuestPtrMut), + p, + ), + witx::Type::ConstPointer(p) => { + define_witx_pointer(names, &namedtype.name, quote!(wiggle_runtime::GuestPtr), p) + } + witx::Type::Array(arr) => define_witx_array(names, &namedtype.name, &arr), + }, + } +} + +fn define_alias(names: &Names, name: &witx::Id, to: &witx::NamedType) -> TokenStream { + let ident = names.type_(name); + let rhs = names.type_(&to.name); + if to.tref.needs_lifetime() { + quote!(pub type #ident<'a> = #rhs<'a>;) + } else { + quote!(pub type #ident = #rhs;) + } +} + +fn define_builtin(names: &Names, name: &witx::Id, builtin: witx::BuiltinType) -> TokenStream { + let ident = names.type_(name); + let built = names.builtin_type(builtin, quote!('a)); + if builtin.needs_lifetime() { + quote!(pub type #ident<'a> = #built;) + } else { + quote!(pub type #ident = #built;) + } +} + +fn define_witx_pointer( + names: &Names, + name: &witx::Id, + pointer_type: TokenStream, + pointee: &witx::TypeRef, +) -> TokenStream { + let ident = names.type_(name); + let pointee_type = names.type_ref(pointee, quote!('a)); + + quote!(pub type #ident<'a> = #pointer_type<'a, #pointee_type>;) +} + +fn define_witx_array(names: &Names, name: &witx::Id, arr_raw: &witx::TypeRef) -> TokenStream { + let ident = names.type_(name); + let pointee_type = names.type_ref(arr_raw, quote!('a)); + quote!(pub type #ident<'a> = wiggle_runtime::GuestArray<'a, #pointee_type>;) +} + +fn int_repr_tokens(int_repr: witx::IntRepr) -> TokenStream { + match int_repr { + witx::IntRepr::U8 => quote!(u8), + witx::IntRepr::U16 => quote!(u16), + witx::IntRepr::U32 => quote!(u32), + witx::IntRepr::U64 => quote!(u64), + } +} + +fn atom_token(atom: witx::AtomType) -> TokenStream { + match atom { + witx::AtomType::I32 => quote!(i32), + witx::AtomType::I64 => quote!(i64), + witx::AtomType::F32 => quote!(f32), + witx::AtomType::F64 => quote!(f64), + } +} diff --git a/crates/generate/src/types/struct.rs b/crates/generate/src/types/struct.rs new file mode 100644 index 0000000000..8344513706 --- /dev/null +++ b/crates/generate/src/types/struct.rs @@ -0,0 +1,226 @@ +use crate::lifetimes::{anon_lifetime, LifetimeExt}; +use crate::names::Names; + +use proc_macro2::TokenStream; +use quote::quote; +use witx::Layout; + +pub(super) fn define_struct( + names: &Names, + name: &witx::Id, + s: &witx::StructDatatype, +) -> TokenStream { + if !s.needs_lifetime() { + define_copy_struct(names, name, s) + } else { + define_ptr_struct(names, name, s) + } +} + +fn define_copy_struct(names: &Names, name: &witx::Id, s: &witx::StructDatatype) -> TokenStream { + let ident = names.type_(name); + let size = s.mem_size_align().size as u32; + let align = s.mem_size_align().align as u32; + + let member_decls = s.members.iter().map(|m| { + let name = names.struct_member(&m.name); + let type_ = names.type_ref(&m.tref, anon_lifetime()); + quote!(pub #name: #type_) + }); + let member_valids = s.member_layout().into_iter().map(|ml| { + let type_ = names.type_ref(&ml.member.tref, anon_lifetime()); + let offset = ml.offset as u32; + let fieldname = names.struct_member(&ml.member.name); + quote! { + #type_::validate( + &ptr.cast(#offset).map_err(|e| + wiggle_runtime::GuestError::InDataField{ + typename: stringify!(#ident).to_owned(), + field: stringify!(#fieldname).to_owned(), + err: Box::new(e), + })? + ).map_err(|e| + wiggle_runtime::GuestError::InDataField { + typename: stringify!(#ident).to_owned(), + field: stringify!(#fieldname).to_owned(), + err: Box::new(e), + })?; + } + }); + + quote! { + #[repr(C)] + #[derive(Copy, Clone, Debug, ::std::hash::Hash, Eq, PartialEq)] + pub struct #ident { + #(#member_decls),* + } + + impl<'a> wiggle_runtime::GuestType<'a> for #ident { + fn size() -> u32 { + #size + } + + fn align() -> u32 { + #align + } + + fn name() -> String { + stringify!(#ident).to_owned() + } + + fn validate(ptr: &wiggle_runtime::GuestPtr<#ident>) -> Result<(), wiggle_runtime::GuestError> { + #(#member_valids)* + Ok(()) + } + + fn read(location: &wiggle_runtime::GuestPtr<'a, #ident>) -> Result<#ident, wiggle_runtime::GuestError> { + let r = location.as_ref()?; + Ok(*r) + } + + fn write(&self, location: &wiggle_runtime::GuestPtrMut<'a, Self>) { + unsafe { (location.as_raw() as *mut #ident).write(*self) }; + } + } + + impl<'a> wiggle_runtime::GuestTypeTransparent<'a> for #ident {} + } +} + +fn define_ptr_struct(names: &Names, name: &witx::Id, s: &witx::StructDatatype) -> TokenStream { + let ident = names.type_(name); + let size = s.mem_size_align().size as u32; + let align = s.mem_size_align().align as u32; + + let member_names = s.members.iter().map(|m| names.struct_member(&m.name)); + let member_decls = s.members.iter().map(|m| { + let name = names.struct_member(&m.name); + 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, quote!('a)), + witx::Type::Pointer(pointee) => { + let pointee_type = names.type_ref(&pointee, quote!('a)); + quote!(wiggle_runtime::GuestPtrMut<'a, #pointee_type>) + } + witx::Type::ConstPointer(pointee) => { + let pointee_type = names.type_ref(&pointee, quote!('a)); + quote!(wiggle_runtime::GuestPtr<'a, #pointee_type>) + } + _ => unimplemented!("other anonymous struct members"), + }, + }; + quote!(pub #name: #type_) + }); + let member_valids = s.member_layout().into_iter().map(|ml| { + 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, quote!('a)), + witx::Type::Pointer(pointee) => { + let pointee_type = names.type_ref(&pointee, anon_lifetime()); + quote!(wiggle_runtime::GuestPtrMut::<#pointee_type>) + } + witx::Type::ConstPointer(pointee) => { + let pointee_type = names.type_ref(&pointee, anon_lifetime()); + quote!(wiggle_runtime::GuestPtr::<#pointee_type>) + } + _ => unimplemented!("other anonymous struct members"), + }, + }; + let offset = ml.offset as u32; + let fieldname = names.struct_member(&ml.member.name); + quote! { + #type_::validate( + &ptr.cast(#offset).map_err(|e| + wiggle_runtime::GuestError::InDataField{ + typename: stringify!(#ident).to_owned(), + field: stringify!(#fieldname).to_owned(), + err: Box::new(e), + })? + ).map_err(|e| + wiggle_runtime::GuestError::InDataField { + typename: stringify!(#ident).to_owned(), + field: stringify!(#fieldname).to_owned(), + err: Box::new(e), + })?; + } + }); + + let member_reads = s.member_layout().into_iter().map(|ml| { + let name = names.struct_member(&ml.member.name); + let offset = ml.offset as u32; + match &ml.member.tref { + witx::TypeRef::Name(nt) => { + let type_ = names.type_(&nt.name); + quote! { + let #name = <#type_ as wiggle_runtime::GuestType>::read(&location.cast(#offset)?)?; + } + } + witx::TypeRef::Value(ty) => match &**ty { + witx::Type::Builtin(builtin) => { + let type_ = names.builtin_type(*builtin, anon_lifetime()); + quote! { + let #name = <#type_ as wiggle_runtime::GuestType>::read(&location.cast(#offset)?)?; + } + } + witx::Type::Pointer(pointee) => { + let pointee_type = names.type_ref(&pointee, anon_lifetime()); + quote! { + let #name = as wiggle_runtime::GuestType>::read(&location.cast(#offset)?)?; + } + } + witx::Type::ConstPointer(pointee) => { + let pointee_type = names.type_ref(&pointee, anon_lifetime()); + quote! { + let #name = as wiggle_runtime::GuestType>::read(&location.cast(#offset)?)?; + } + } + _ => unimplemented!("other anonymous struct members"), + }, + } + }); + + let member_writes = s.member_layout().into_iter().map(|ml| { + let name = names.struct_member(&ml.member.name); + let offset = ml.offset as u32; + quote! { + self.#name.write(&location.cast(#offset).expect("cast to inner member")); + } + }); + + quote! { + #[derive(Clone)] + pub struct #ident<'a> { + #(#member_decls),* + } + + impl<'a> wiggle_runtime::GuestType<'a> for #ident<'a> { + fn size() -> u32 { + #size + } + + fn align() -> u32 { + #align + } + + fn name() -> String { + stringify!(#ident).to_owned() + } + + fn validate(ptr: &wiggle_runtime::GuestPtr<'a, #ident<'a>>) -> Result<(), wiggle_runtime::GuestError> { + #(#member_valids)* + Ok(()) + } + + fn read(location: &wiggle_runtime::GuestPtr<'a, #ident<'a>>) -> Result<#ident<'a>, wiggle_runtime::GuestError> { + #(#member_reads)* + Ok(#ident { #(#member_names),* }) + } + + fn write(&self, location: &wiggle_runtime::GuestPtrMut<'a, Self>) { + #(#member_writes)* + } + } + } +} diff --git a/crates/generate/src/types/union.rs b/crates/generate/src/types/union.rs new file mode 100644 index 0000000000..713f914e80 --- /dev/null +++ b/crates/generate/src/types/union.rs @@ -0,0 +1,199 @@ +use crate::lifetimes::{anon_lifetime, LifetimeExt}; +use crate::names::Names; + +use proc_macro2::TokenStream; +use quote::quote; +use witx::Layout; + +pub(super) fn define_union(names: &Names, name: &witx::Id, u: &witx::UnionDatatype) -> TokenStream { + let ident = names.type_(name); + let size = u.mem_size_align().size as u32; + let align = u.mem_size_align().align as u32; + let ulayout = u.union_layout(); + let contents_offset = ulayout.contents_offset as u32; + + let lifetime = quote!('a); + + let variants = u.variants.iter().map(|v| { + let var_name = names.enum_variant(&v.name); + if let Some(tref) = &v.tref { + let var_type = names.type_ref(&tref, lifetime.clone()); + quote!(#var_name(#var_type)) + } else { + quote!(#var_name) + } + }); + + let tagname = names.type_(&u.tag.name); + + let read_variant = u.variants.iter().map(|v| { + let variantname = names.enum_variant(&v.name); + if let Some(tref) = &v.tref { + let varianttype = names.type_ref(tref, lifetime.clone()); + quote! { + #tagname::#variantname => { + let variant_ptr = location.cast::<#varianttype>(#contents_offset).expect("union variant ptr validated"); + let variant_val = <#varianttype as wiggle_runtime::GuestType>::read(&variant_ptr)?; + Ok(#ident::#variantname(variant_val)) + } + } + } else { + quote! { #tagname::#variantname => Ok(#ident::#variantname), } + } + }); + + let write_variant = u.variants.iter().map(|v| { + let variantname = names.enum_variant(&v.name); + let write_tag = quote! { + let tag_ptr = location.cast::<#tagname>(0).expect("union tag ptr TODO error report"); + let mut tag_ref = tag_ptr.as_ref_mut().expect("union tag ref TODO error report"); + *tag_ref = #tagname::#variantname; + }; + if let Some(tref) = &v.tref { + let varianttype = names.type_ref(tref, lifetime.clone()); + quote! { + #ident::#variantname(contents) => { + #write_tag + let variant_ptr = location.cast::<#varianttype>(#contents_offset).expect("union variant ptr validated"); + <#varianttype as wiggle_runtime::GuestType>::write(&contents, &variant_ptr); + } + } + } else { + quote! { + #ident::#variantname => { + #write_tag + } + } + } + }); + let validate = union_validate(names, ident.clone(), u, &ulayout); + + if !u.needs_lifetime() { + // Type does not have a lifetime parameter: + quote! { + #[derive(Clone, Debug, PartialEq)] + pub enum #ident { + #(#variants),* + } + + impl<'a> wiggle_runtime::GuestType<'a> for #ident { + fn size() -> u32 { + #size + } + + fn align() -> u32 { + #align + } + + fn name() -> String { + stringify!(#ident).to_owned() + } + + fn validate(ptr: &wiggle_runtime::GuestPtr<'a, #ident>) -> Result<(), wiggle_runtime::GuestError> { + #validate + } + + fn read(location: &wiggle_runtime::GuestPtr<'a, #ident>) + -> Result { + <#ident as wiggle_runtime::GuestType>::validate(location)?; + let tag = *location.cast::<#tagname>(0).expect("validated tag ptr").as_ref().expect("validated tag ref"); + match tag { + #(#read_variant)* + } + + } + + fn write(&self, location: &wiggle_runtime::GuestPtrMut<'a, #ident>) { + match self { + #(#write_variant)* + } + } + } + } + } else { + quote! { + #[derive(Clone)] + pub enum #ident<#lifetime> { + #(#variants),* + } + + impl<#lifetime> wiggle_runtime::GuestType<#lifetime> for #ident<#lifetime> { + fn size() -> u32 { + #size + } + + fn align() -> u32 { + #align + } + + fn name() -> String { + stringify!(#ident).to_owned() + } + + fn validate(ptr: &wiggle_runtime::GuestPtr<#lifetime, #ident<#lifetime>>) -> Result<(), wiggle_runtime::GuestError> { + #validate + } + + fn read(location: &wiggle_runtime::GuestPtr<#lifetime, #ident<#lifetime>>) + -> Result { + <#ident as wiggle_runtime::GuestType>::validate(location)?; + let tag = *location.cast::<#tagname>(0).expect("validated tag ptr").as_ref().expect("validated tag ref"); + match tag { + #(#read_variant)* + } + + } + + fn write(&self, location: &wiggle_runtime::GuestPtrMut<#lifetime, #ident<#lifetime>>) { + match self { + #(#write_variant)* + } + } + } + } + } +} + +fn union_validate( + names: &Names, + typename: TokenStream, + u: &witx::UnionDatatype, + ulayout: &witx::UnionLayout, +) -> TokenStream { + let tagname = names.type_(&u.tag.name); + let contents_offset = ulayout.contents_offset as u32; + + let with_err = |f: &str| -> TokenStream { + quote!(|e| wiggle_runtime::GuestError::InDataField { + typename: stringify!(#typename).to_owned(), + field: #f.to_owned(), + err: Box::new(e), + }) + }; + + let tag_err = with_err(""); + let variant_validation = u.variants.iter().map(|v| { + let err = with_err(v.name.as_str()); + let variantname = names.enum_variant(&v.name); + if let Some(tref) = &v.tref { + let lifetime = anon_lifetime(); + let varianttype = names.type_ref(tref, lifetime.clone()); + quote! { + #tagname::#variantname => { + let variant_ptr = ptr.cast::<#varianttype>(#contents_offset).map_err(#err)?; + <#varianttype as wiggle_runtime::GuestType>::validate(&variant_ptr).map_err(#err)?; + } + } + } else { + quote! { #tagname::#variantname => {} } + } + }); + + quote! { + let tag = *ptr.cast::<#tagname>(0).map_err(#tag_err)?.as_ref().map_err(#tag_err)?; + match tag { + #(#variant_validation)* + } + Ok(()) + } +} From bb6995ceaf484af2c5c8fd4e34d90b1507d78d89 Mon Sep 17 00:00:00 2001 From: Pat Hickey Date: Fri, 28 Feb 2020 11:43:43 -0800 Subject: [PATCH 69/86] make wiggle-generate ordinary lib, and wiggle the proc-macro lib this allows us to reuse the code in wiggle-generate elsewhere, because a proc-macro=true lib can only export a #[proc_macro] and not ordinary functions. In lucet, I will depend on wiggle-generate to define a proc macro that glues wiggle to the specifics of the runtime. --- Cargo.toml | 6 +++++- crates/generate/Cargo.toml | 1 - crates/generate/src/lib.rs | 24 +++++++++--------------- src/lib.rs | 13 +++++++++---- tests/arrays.rs | 2 +- tests/atoms.rs | 2 +- tests/flags.rs | 2 +- tests/handles.rs | 2 +- tests/ints.rs | 2 +- tests/pointers.rs | 2 +- tests/strings.rs | 2 +- tests/structs.rs | 2 +- tests/union.rs | 2 +- 13 files changed, 32 insertions(+), 30 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index e834e66349..2c6400e249 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,11 +4,15 @@ version = "0.1.0" authors = ["Pat Hickey ", "Jakub Konka "] edition = "2018" +[lib] +proc-macro = true + [dependencies] wiggle-generate = { path = "crates/generate" } -wiggle-runtime = { path = "crates/runtime" } +syn = { version = "1.0", features = ["full"] } [dev-dependencies] +wiggle-runtime = { path = "crates/runtime" } wiggle-test = { path = "crates/test" } proptest = "0.9" diff --git a/crates/generate/Cargo.toml b/crates/generate/Cargo.toml index 9926ca7f80..6008ca57ba 100644 --- a/crates/generate/Cargo.toml +++ b/crates/generate/Cargo.toml @@ -5,7 +5,6 @@ authors = ["Pat Hickey ", "Jakub Konka TokenStream { - let config = parse_macro_input!(args as Config); +pub use config::Config; +pub use funcs::define_func; +pub use module_trait::define_module_trait; +pub use names::Names; +pub use types::define_datatype; +pub fn generate(config: Config) -> TokenStream { let doc = witx::load(&config.witx.paths).expect("loading witx"); let names = Names::new(config); // TODO parse the names from the invocation of the macro, or from a file? @@ -43,10 +37,10 @@ pub fn from_witx(args: TokenStream) -> TokenStream { ) }); - TokenStream::from(quote!( + quote!( mod types { #(#types)* } #(#modules)* - )) + ) } diff --git a/src/lib.rs b/src/lib.rs index 2b66a42972..24e2e4909b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,10 @@ -/* -pub mod wasi { - generate::from_witx!("crates/WASI/phases/snapshot/witx/wasi_snapshot_preview1.witx"); +extern crate proc_macro; + +use proc_macro::TokenStream; +use syn::parse_macro_input; + +#[proc_macro] +pub fn from_witx(args: TokenStream) -> TokenStream { + let config = parse_macro_input!(args as wiggle_generate::Config); + TokenStream::from(wiggle_generate::generate(config)) } -*/ diff --git a/tests/arrays.rs b/tests/arrays.rs index 0a5e0641fa..f8ec9a3675 100644 --- a/tests/arrays.rs +++ b/tests/arrays.rs @@ -2,7 +2,7 @@ use proptest::prelude::*; use wiggle_runtime::{GuestArray, GuestError, GuestPtr, GuestPtrMut, GuestType}; use wiggle_test::{impl_errno, HostMemory, MemArea, WasiCtx}; -wiggle_generate::from_witx!({ +wiggle::from_witx!({ witx: ["tests/arrays.witx"], ctx: WasiCtx, }); diff --git a/tests/atoms.rs b/tests/atoms.rs index e9be5479b6..3a6f56ac52 100644 --- a/tests/atoms.rs +++ b/tests/atoms.rs @@ -2,7 +2,7 @@ use proptest::prelude::*; use wiggle_runtime::{GuestError, GuestRef}; use wiggle_test::{impl_errno, HostMemory, MemArea, WasiCtx}; -wiggle_generate::from_witx!({ +wiggle::from_witx!({ witx: ["tests/atoms.witx"], ctx: WasiCtx, }); diff --git a/tests/flags.rs b/tests/flags.rs index 0dcc16d3b4..c3973d10e6 100644 --- a/tests/flags.rs +++ b/tests/flags.rs @@ -3,7 +3,7 @@ use std::convert::TryFrom; use wiggle_runtime::{GuestError, GuestPtr}; use wiggle_test::{impl_errno, HostMemory, MemArea, WasiCtx}; -wiggle_generate::from_witx!({ +wiggle::from_witx!({ witx: ["tests/flags.witx"], ctx: WasiCtx, }); diff --git a/tests/handles.rs b/tests/handles.rs index ed86a04519..b082bd3ef8 100644 --- a/tests/handles.rs +++ b/tests/handles.rs @@ -4,7 +4,7 @@ use wiggle_test::{impl_errno, HostMemory, MemArea, WasiCtx}; const FD_VAL: u32 = 123; -wiggle_generate::from_witx!({ +wiggle::from_witx!({ witx: ["tests/handles.witx"], ctx: WasiCtx, }); diff --git a/tests/ints.rs b/tests/ints.rs index f6a00ccc4a..6a927adf00 100644 --- a/tests/ints.rs +++ b/tests/ints.rs @@ -3,7 +3,7 @@ use std::convert::TryFrom; use wiggle_runtime::GuestError; use wiggle_test::{impl_errno, HostMemory, MemArea, WasiCtx}; -wiggle_generate::from_witx!({ +wiggle::from_witx!({ witx: ["tests/ints.witx"], ctx: WasiCtx, }); diff --git a/tests/pointers.rs b/tests/pointers.rs index e086c59061..ec0977c8bc 100644 --- a/tests/pointers.rs +++ b/tests/pointers.rs @@ -2,7 +2,7 @@ use proptest::prelude::*; use wiggle_runtime::{GuestError, GuestPtr, GuestPtrMut, GuestRefMut, GuestType}; use wiggle_test::{impl_errno, HostMemory, MemArea, WasiCtx}; -wiggle_generate::from_witx!({ +wiggle::from_witx!({ witx: ["tests/pointers.witx"], ctx: WasiCtx, }); diff --git a/tests/strings.rs b/tests/strings.rs index f25c8dc9e6..4558108b37 100644 --- a/tests/strings.rs +++ b/tests/strings.rs @@ -2,7 +2,7 @@ use proptest::prelude::*; use wiggle_runtime::{GuestError, GuestPtrMut, GuestString}; use wiggle_test::{impl_errno, HostMemory, MemArea, WasiCtx}; -wiggle_generate::from_witx!({ +wiggle::from_witx!({ witx: ["tests/strings.witx"], ctx: WasiCtx, }); diff --git a/tests/structs.rs b/tests/structs.rs index 40f212f551..e36b0d4903 100644 --- a/tests/structs.rs +++ b/tests/structs.rs @@ -2,7 +2,7 @@ use proptest::prelude::*; use wiggle_runtime::{GuestError, GuestPtr}; use wiggle_test::{impl_errno, HostMemory, MemArea, WasiCtx}; -wiggle_generate::from_witx!({ +wiggle::from_witx!({ witx: ["tests/structs.witx"], ctx: WasiCtx, }); diff --git a/tests/union.rs b/tests/union.rs index 91b50c033f..7a3a6f0364 100644 --- a/tests/union.rs +++ b/tests/union.rs @@ -2,7 +2,7 @@ use proptest::prelude::*; use wiggle_runtime::{GuestError, GuestType}; use wiggle_test::{impl_errno, HostMemory, MemArea, WasiCtx}; -wiggle_generate::from_witx!({ +wiggle::from_witx!({ witx: ["tests/union.witx"], ctx: WasiCtx, }); From 974f5617e05a0b436e9fe7f6ac0912722ec24ecd Mon Sep 17 00:00:00 2001 From: Pat Hickey Date: Fri, 28 Feb 2020 13:12:58 -0700 Subject: [PATCH 70/86] depend on witx 0.8.3, rather than a path dependency. (#27) and delete the submodule that was providing the path dep --- .gitmodules | 3 --- crates/WASI | 1 - crates/generate/Cargo.toml | 2 +- 3 files changed, 1 insertion(+), 5 deletions(-) delete mode 160000 crates/WASI diff --git a/.gitmodules b/.gitmodules index dc4e4a07bb..e69de29bb2 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +0,0 @@ -[submodule "crates/WASI"] - path = crates/WASI - url = https://github.com/webassembly/wasi.git diff --git a/crates/WASI b/crates/WASI deleted file mode 160000 index 19ad34a27e..0000000000 --- a/crates/WASI +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 19ad34a27ebcfa90c56afe0e4af6ad9fc33d3f41 diff --git a/crates/generate/Cargo.toml b/crates/generate/Cargo.toml index 6008ca57ba..39a5814f86 100644 --- a/crates/generate/Cargo.toml +++ b/crates/generate/Cargo.toml @@ -8,7 +8,7 @@ edition = "2018" [dependencies] wiggle-runtime = { path = "../runtime" } -witx = { path = "../WASI/tools/witx" } +witx = "0.8.3" quote = "1.0" proc-macro2 = "1.0" heck = "0.3" From 5db335b7c7038baa458eb5ec2fa72125c54bb1bd Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Fri, 28 Feb 2020 21:15:22 +0100 Subject: [PATCH 71/86] Add EMPTY_FLAGS to flags generator This seems like a useful primitive to have especially when dealing with `Rights` flags in `wasi-common` (and WASI in general). --- crates/generate/src/types/flags.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/generate/src/types/flags.rs b/crates/generate/src/types/flags.rs index 8837ceb796..67e380ff77 100644 --- a/crates/generate/src/types/flags.rs +++ b/crates/generate/src/types/flags.rs @@ -35,10 +35,11 @@ pub(super) fn define_flags(names: &Names, name: &witx::Id, f: &witx::FlagsDataty impl #ident { #(#flag_constructors);*; + pub const EMPTY_FLAGS: #ident = #ident(0 as #repr); pub const ALL_FLAGS: #ident = #ident(#all_values_token); pub fn contains(&self, other: &#ident) -> bool { - #repr::from(!*self & *other) == 0 as #repr + !*self & *other == Self::EMPTY_FLAGS } } From 16fe947e6531b135d108b419cbd1536d8a26c139 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Fri, 28 Feb 2020 21:10:52 +0100 Subject: [PATCH 72/86] Make generated modules public While public might be an overkill, until we successfully merge `wiggle` with `wasi-common` (and others), I suggest we just make the modules fully public and work from there. --- crates/generate/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/generate/src/lib.rs b/crates/generate/src/lib.rs index bc0ae49e27..1b235ca2f5 100644 --- a/crates/generate/src/lib.rs +++ b/crates/generate/src/lib.rs @@ -27,7 +27,7 @@ pub fn generate(config: Config) -> TokenStream { let modtrait = define_module_trait(&names, &module); let ctx_type = names.ctx_type(); quote!( - mod #modname { + pub mod #modname { use super::#ctx_type; use super::types::*; #(#fs)* @@ -38,7 +38,7 @@ pub fn generate(config: Config) -> TokenStream { }); quote!( - mod types { + pub mod types { #(#types)* } #(#modules)* From 9cc9dacc080e918c70c81ab4b31baad427d0e16d Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Fri, 28 Feb 2020 21:06:12 +0100 Subject: [PATCH 73/86] Escape reserved keywords in generate This commit escapes certain (hopefully all keywords present in snapshot1!) reserved keywords in Rust that are autogenerated by wiggle. --- crates/generate/src/names.rs | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/crates/generate/src/names.rs b/crates/generate/src/names.rs index f268514c1d..1167d25a60 100644 --- a/crates/generate/src/names.rs +++ b/crates/generate/src/names.rs @@ -77,6 +77,8 @@ impl Names { // FIXME this is a hack - just a proof of concept. if id.as_str().starts_with('2') { format_ident!("TooBig") + } else if id.as_str() == "type" { + format_ident!("Type") } else { format_ident!("{}", id.as_str().to_camel_case()) } @@ -91,7 +93,12 @@ impl Names { } pub fn struct_member(&self, id: &Id) -> Ident { - format_ident!("{}", id.as_str().to_snake_case()) + // FIXME this is a hack - just a proof of concept. + if id.as_str() == "type" { + format_ident!("type_") + } else { + format_ident!("{}", id.as_str().to_snake_case()) + } } pub fn module(&self, id: &Id) -> Ident { @@ -107,7 +114,12 @@ impl Names { } pub fn func_param(&self, id: &Id) -> Ident { - format_ident!("{}", id.as_str().to_snake_case()) + // FIXME this is a hack - just a proof of concept. + if id.as_str() == "in" { + format_ident!("in_") + } else { + format_ident!("{}", id.as_str().to_snake_case()) + } } /// For when you need a {name}_ptr binding for passing a value by reference: From f4f4156c9dcf224a7089e05c8512fe879ac0df8d Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Sat, 29 Feb 2020 11:52:13 +0100 Subject: [PATCH 74/86] Delete .gitmodules Seems obsolete now since we reference `witx` by version number rather than path. --- .gitmodules | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 .gitmodules diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index e69de29bb2..0000000000 From db8fec354d6ac1a35e4876d597547bf125706a0e Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Mon, 2 Mar 2020 23:28:13 +0100 Subject: [PATCH 75/86] Add current snapshot1 WASI spec as test + fixes (#31) * Add WASI spec (minus unions) * Fill in all WASI shims * Clean up derives and fix noncopy struct write method This commit does three things: * it uses the full, current snapshot1 WASI spec as a compilation test * it fixes noncopy struct write method (which was incorrectly resolved in certain cases to the inherent method of the `GuestPtrMut` rather than the interface method `GuestType::write` * it cleans up derives for structs and unions which should not auto-derive `PartialEq`, `Eq`, or `Hash` since their members are not guaranteed to be compatible --- crates/generate/src/types/struct.rs | 6 +- crates/generate/src/types/union.rs | 4 +- tests/typenames.witx | 746 ++++++++++++++++++++++++++++ tests/wasi.rs | 324 ++++++++++++ tests/wasi.witx | 532 ++++++++++++++++++++ 5 files changed, 1607 insertions(+), 5 deletions(-) create mode 100644 tests/typenames.witx create mode 100644 tests/wasi.rs create mode 100644 tests/wasi.witx diff --git a/crates/generate/src/types/struct.rs b/crates/generate/src/types/struct.rs index 8344513706..465e47279f 100644 --- a/crates/generate/src/types/struct.rs +++ b/crates/generate/src/types/struct.rs @@ -50,7 +50,7 @@ fn define_copy_struct(names: &Names, name: &witx::Id, s: &witx::StructDatatype) quote! { #[repr(C)] - #[derive(Copy, Clone, Debug, ::std::hash::Hash, Eq, PartialEq)] + #[derive(Copy, Clone, Debug, PartialEq)] pub struct #ident { #(#member_decls),* } @@ -185,12 +185,12 @@ fn define_ptr_struct(names: &Names, name: &witx::Id, s: &witx::StructDatatype) - let name = names.struct_member(&ml.member.name); let offset = ml.offset as u32; quote! { - self.#name.write(&location.cast(#offset).expect("cast to inner member")); + wiggle_runtime::GuestType::write(&self.#name, &location.cast(#offset).expect("cast to inner member")); } }); quote! { - #[derive(Clone)] + #[derive(Clone, Debug)] pub struct #ident<'a> { #(#member_decls),* } diff --git a/crates/generate/src/types/union.rs b/crates/generate/src/types/union.rs index 713f914e80..4a9f6bff9a 100644 --- a/crates/generate/src/types/union.rs +++ b/crates/generate/src/types/union.rs @@ -71,7 +71,7 @@ pub(super) fn define_union(names: &Names, name: &witx::Id, u: &witx::UnionDataty if !u.needs_lifetime() { // Type does not have a lifetime parameter: quote! { - #[derive(Clone, Debug, PartialEq)] + #[derive(Copy, Clone, Debug, PartialEq)] pub enum #ident { #(#variants),* } @@ -112,7 +112,7 @@ pub(super) fn define_union(names: &Names, name: &witx::Id, u: &witx::UnionDataty } } else { quote! { - #[derive(Clone)] + #[derive(Clone, Debug)] pub enum #ident<#lifetime> { #(#variants),* } diff --git a/tests/typenames.witx b/tests/typenames.witx new file mode 100644 index 0000000000..1351fc4e13 --- /dev/null +++ b/tests/typenames.witx @@ -0,0 +1,746 @@ +;; Type names used by low-level WASI interfaces. +;; +;; Some content here is derived from [CloudABI](https://github.com/NuxiNL/cloudabi). +;; +;; This is a `witx` file. See [here](https://github.com/WebAssembly/WASI/tree/master/docs/witx.md) +;; for an explanation of what that means. + +(typename $size u32) + +;;; Non-negative file size or length of a region within a file. +(typename $filesize u64) + +;;; Timestamp in nanoseconds. +(typename $timestamp u64) + +;;; Identifiers for clocks. +(typename $clockid + (enum u32 + ;;; The clock measuring real time. Time value zero corresponds with + ;;; 1970-01-01T00:00:00Z. + $realtime + ;;; The store-wide monotonic clock, which is defined as a clock measuring + ;;; real time, whose value cannot be adjusted and which cannot have negative + ;;; clock jumps. The epoch of this clock is undefined. The absolute time + ;;; value of this clock therefore has no meaning. + $monotonic + ;;; The CPU-time clock associated with the current process. + $process_cputime_id + ;;; The CPU-time clock associated with the current thread. + $thread_cputime_id + ) +) + +;;; Error codes returned by functions. +;;; Not all of these error codes are returned by the functions provided by this +;;; API; some are used in higher-level library layers, and others are provided +;;; merely for alignment with POSIX. +(typename $errno + (enum u16 + ;;; No error occurred. System call completed successfully. + $success + ;;; Argument list too long. + $2big + ;;; Permission denied. + $acces + ;;; Address in use. + $addrinuse + ;;; Address not available. + $addrnotavail + ;;; Address family not supported. + $afnosupport + ;;; Resource unavailable, or operation would block. + $again + ;;; Connection already in progress. + $already + ;;; Bad file descriptor. + $badf + ;;; Bad message. + $badmsg + ;;; Device or resource busy. + $busy + ;;; Operation canceled. + $canceled + ;;; No child processes. + $child + ;;; Connection aborted. + $connaborted + ;;; Connection refused. + $connrefused + ;;; Connection reset. + $connreset + ;;; Resource deadlock would occur. + $deadlk + ;;; Destination address required. + $destaddrreq + ;;; Mathematics argument out of domain of function. + $dom + ;;; Reserved. + $dquot + ;;; File exists. + $exist + ;;; Bad address. + $fault + ;;; File too large. + $fbig + ;;; Host is unreachable. + $hostunreach + ;;; Identifier removed. + $idrm + ;;; Illegal byte sequence. + $ilseq + ;;; Operation in progress. + $inprogress + ;;; Interrupted function. + $intr + ;;; Invalid argument. + $inval + ;;; I/O error. + $io + ;;; Socket is connected. + $isconn + ;;; Is a directory. + $isdir + ;;; Too many levels of symbolic links. + $loop + ;;; File descriptor value too large. + $mfile + ;;; Too many links. + $mlink + ;;; Message too large. + $msgsize + ;;; Reserved. + $multihop + ;;; Filename too long. + $nametoolong + ;;; Network is down. + $netdown + ;;; Connection aborted by network. + $netreset + ;;; Network unreachable. + $netunreach + ;;; Too many files open in system. + $nfile + ;;; No buffer space available. + $nobufs + ;;; No such device. + $nodev + ;;; No such file or directory. + $noent + ;;; Executable file format error. + $noexec + ;;; No locks available. + $nolck + ;;; Reserved. + $nolink + ;;; Not enough space. + $nomem + ;;; No message of the desired type. + $nomsg + ;;; Protocol not available. + $noprotoopt + ;;; No space left on device. + $nospc + ;;; Function not supported. + $nosys + ;;; The socket is not connected. + $notconn + ;;; Not a directory or a symbolic link to a directory. + $notdir + ;;; Directory not empty. + $notempty + ;;; State not recoverable. + $notrecoverable + ;;; Not a socket. + $notsock + ;;; Not supported, or operation not supported on socket. + $notsup + ;;; Inappropriate I/O control operation. + $notty + ;;; No such device or address. + $nxio + ;;; Value too large to be stored in data type. + $overflow + ;;; Previous owner died. + $ownerdead + ;;; Operation not permitted. + $perm + ;;; Broken pipe. + $pipe + ;;; Protocol error. + $proto + ;;; Protocol not supported. + $protonosupport + ;;; Protocol wrong type for socket. + $prototype + ;;; Result too large. + $range + ;;; Read-only file system. + $rofs + ;;; Invalid seek. + $spipe + ;;; No such process. + $srch + ;;; Reserved. + $stale + ;;; Connection timed out. + $timedout + ;;; Text file busy. + $txtbsy + ;;; Cross-device link. + $xdev + ;;; Extension: Capabilities insufficient. + $notcapable + ) +) + +;;; File descriptor rights, determining which actions may be performed. +(typename $rights + (flags u64 + ;;; The right to invoke `fd_datasync`. + ;; + ;;; If `path_open` is set, includes the right to invoke + ;;; `path_open` with `fdflags::dsync`. + $fd_datasync + ;;; The right to invoke `fd_read` and `sock_recv`. + ;; + ;;; If `rights::fd_seek` is set, includes the right to invoke `fd_pread`. + $fd_read + ;;; The right to invoke `fd_seek`. This flag implies `rights::fd_tell`. + $fd_seek + ;;; The right to invoke `fd_fdstat_set_flags`. + $fd_fdstat_set_flags + ;;; The right to invoke `fd_sync`. + ;; + ;;; If `path_open` is set, includes the right to invoke + ;;; `path_open` with `fdflags::rsync` and `fdflags::dsync`. + $fd_sync + ;;; The right to invoke `fd_seek` in such a way that the file offset + ;;; remains unaltered (i.e., `whence::cur` with offset zero), or to + ;;; invoke `fd_tell`. + $fd_tell + ;;; The right to invoke `fd_write` and `sock_send`. + ;;; If `rights::fd_seek` is set, includes the right to invoke `fd_pwrite`. + $fd_write + ;;; The right to invoke `fd_advise`. + $fd_advise + ;;; The right to invoke `fd_allocate`. + $fd_allocate + ;;; The right to invoke `path_create_directory`. + $path_create_directory + ;;; If `path_open` is set, the right to invoke `path_open` with `oflags::creat`. + $path_create_file + ;;; The right to invoke `path_link` with the file descriptor as the + ;;; source directory. + $path_link_source + ;;; The right to invoke `path_link` with the file descriptor as the + ;;; target directory. + $path_link_target + ;;; The right to invoke `path_open`. + $path_open + ;;; The right to invoke `fd_readdir`. + $fd_readdir + ;;; The right to invoke `path_readlink`. + $path_readlink + ;;; The right to invoke `path_rename` with the file descriptor as the source directory. + $path_rename_source + ;;; The right to invoke `path_rename` with the file descriptor as the target directory. + $path_rename_target + ;;; The right to invoke `path_filestat_get`. + $path_filestat_get + ;;; The right to change a file's size (there is no `path_filestat_set_size`). + ;;; If `path_open` is set, includes the right to invoke `path_open` with `oflags::trunc`. + $path_filestat_set_size + ;;; The right to invoke `path_filestat_set_times`. + $path_filestat_set_times + ;;; The right to invoke `fd_filestat_get`. + $fd_filestat_get + ;;; The right to invoke `fd_filestat_set_size`. + $fd_filestat_set_size + ;;; The right to invoke `fd_filestat_set_times`. + $fd_filestat_set_times + ;;; The right to invoke `path_symlink`. + $path_symlink + ;;; The right to invoke `path_remove_directory`. + $path_remove_directory + ;;; The right to invoke `path_unlink_file`. + $path_unlink_file + ;;; If `rights::fd_read` is set, includes the right to invoke `poll_oneoff` to subscribe to `eventtype::fd_read`. + ;;; If `rights::fd_write` is set, includes the right to invoke `poll_oneoff` to subscribe to `eventtype::fd_write`. + $poll_fd_readwrite + ;;; The right to invoke `sock_shutdown`. + $sock_shutdown + ) +) + +;;; A file descriptor handle. +(typename $fd (handle)) + +;;; A region of memory for scatter/gather reads. +(typename $iovec + (struct + ;;; The address of the buffer to be filled. + (field $buf (@witx pointer u8)) + ;;; The length of the buffer to be filled. + (field $buf_len $size) + ) +) + +;;; A region of memory for scatter/gather writes. +(typename $ciovec + (struct + ;;; The address of the buffer to be written. + (field $buf (@witx const_pointer u8)) + ;;; The length of the buffer to be written. + (field $buf_len $size) + ) +) + +(typename $iovec_array (array $iovec)) +(typename $ciovec_array (array $ciovec)) + +;;; Relative offset within a file. +(typename $filedelta s64) + +;;; The position relative to which to set the offset of the file descriptor. +(typename $whence + (enum u8 + ;;; Seek relative to start-of-file. + $set + ;;; Seek relative to current position. + $cur + ;;; Seek relative to end-of-file. + $end + ) +) + +;;; A reference to the offset of a directory entry. +;;; +;;; The value 0 signifies the start of the directory. +(typename $dircookie u64) + +;;; The type for the $d_namlen field of $dirent. +(typename $dirnamlen u32) + +;;; File serial number that is unique within its file system. +(typename $inode u64) + +;;; The type of a file descriptor or file. +(typename $filetype + (enum u8 + ;;; The type of the file descriptor or file is unknown or is different from any of the other types specified. + $unknown + ;;; The file descriptor or file refers to a block device inode. + $block_device + ;;; The file descriptor or file refers to a character device inode. + $character_device + ;;; The file descriptor or file refers to a directory inode. + $directory + ;;; The file descriptor or file refers to a regular file inode. + $regular_file + ;;; The file descriptor or file refers to a datagram socket. + $socket_dgram + ;;; The file descriptor or file refers to a byte-stream socket. + $socket_stream + ;;; The file refers to a symbolic link inode. + $symbolic_link + ) +) + +;;; A directory entry. +(typename $dirent + (struct + ;;; The offset of the next directory entry stored in this directory. + (field $d_next $dircookie) + ;;; The serial number of the file referred to by this directory entry. + (field $d_ino $inode) + ;;; The length of the name of the directory entry. + (field $d_namlen $dirnamlen) + ;;; The type of the file referred to by this directory entry. + (field $d_type $filetype) + ) +) + +;;; File or memory access pattern advisory information. +(typename $advice + (enum u8 + ;;; The application has no advice to give on its behavior with respect to the specified data. + $normal + ;;; The application expects to access the specified data sequentially from lower offsets to higher offsets. + $sequential + ;;; The application expects to access the specified data in a random order. + $random + ;;; The application expects to access the specified data in the near future. + $willneed + ;;; The application expects that it will not access the specified data in the near future. + $dontneed + ;;; The application expects to access the specified data once and then not reuse it thereafter. + $noreuse + ) +) + +;;; File descriptor flags. +(typename $fdflags + (flags u16 + ;;; Append mode: Data written to the file is always appended to the file's end. + $append + ;;; Write according to synchronized I/O data integrity completion. Only the data stored in the file is synchronized. + $dsync + ;;; Non-blocking mode. + $nonblock + ;;; Synchronized read I/O operations. + $rsync + ;;; Write according to synchronized I/O file integrity completion. In + ;;; addition to synchronizing the data stored in the file, the implementation + ;;; may also synchronously update the file's metadata. + $sync + ) +) + +;;; File descriptor attributes. +(typename $fdstat + (struct + ;;; File type. + (field $fs_filetype $filetype) + ;;; File descriptor flags. + (field $fs_flags $fdflags) + ;;; Rights that apply to this file descriptor. + (field $fs_rights_base $rights) + ;;; Maximum set of rights that may be installed on new file descriptors that + ;;; are created through this file descriptor, e.g., through `path_open`. + (field $fs_rights_inheriting $rights) + ) +) + +;;; Identifier for a device containing a file system. Can be used in combination +;;; with `inode` to uniquely identify a file or directory in the filesystem. +(typename $device u64) + +;;; Which file time attributes to adjust. +(typename $fstflags + (flags u16 + ;;; Adjust the last data access timestamp to the value stored in `filestat::atim`. + $atim + ;;; Adjust the last data access timestamp to the time of clock `clockid::realtime`. + $atim_now + ;;; Adjust the last data modification timestamp to the value stored in `filestat::mtim`. + $mtim + ;;; Adjust the last data modification timestamp to the time of clock `clockid::realtime`. + $mtim_now + ) +) + +;;; Flags determining the method of how paths are resolved. +(typename $lookupflags + (flags u32 + ;;; As long as the resolved path corresponds to a symbolic link, it is expanded. + $symlink_follow + ) +) + +;;; Open flags used by `path_open`. +(typename $oflags + (flags u16 + ;;; Create file if it does not exist. + $creat + ;;; Fail if not a directory. + $directory + ;;; Fail if file already exists. + $excl + ;;; Truncate file to size 0. + $trunc + ) +) + +;;; Number of hard links to an inode. +(typename $linkcount u64) + +;;; File attributes. +(typename $filestat + (struct + ;;; Device ID of device containing the file. + (field $dev $device) + ;;; File serial number. + (field $ino $inode) + ;;; File type. + (field $filetype $filetype) + ;;; Number of hard links to the file. + (field $nlink $linkcount) + ;;; For regular files, the file size in bytes. For symbolic links, the length in bytes of the pathname contained in the symbolic link. + (field $size $filesize) + ;;; Last data access timestamp. + (field $atim $timestamp) + ;;; Last data modification timestamp. + (field $mtim $timestamp) + ;;; Last file status change timestamp. + (field $ctim $timestamp) + ) +) + +;;; User-provided value that may be attached to objects that is retained when +;;; extracted from the implementation. +(typename $userdata u64) + +;;; Type of a subscription to an event or its occurrence. +(typename $eventtype + (enum u8 + ;;; The time value of clock `subscription_clock::id` has + ;;; reached timestamp `subscription_clock::timeout`. + $clock + ;;; File descriptor `subscription_fd_readwrite::file_descriptor` has data + ;;; available for reading. This event always triggers for regular files. + $fd_read + ;;; File descriptor `subscription_fd_readwrite::file_descriptor` has capacity + ;;; available for writing. This event always triggers for regular files. + $fd_write + ) +) + +;;; The state of the file descriptor subscribed to with +;;; `eventtype::fd_read` or `eventtype::fd_write`. +(typename $eventrwflags + (flags u16 + ;;; The peer of this socket has closed or disconnected. + $fd_readwrite_hangup + ) +) + +;;; The contents of an $event when type is `eventtype::fd_read` or +;;; `eventtype::fd_write`. +(typename $event_fd_readwrite + (struct + ;;; The number of bytes available for reading or writing. + (field $nbytes $filesize) + ;;; The state of the file descriptor. + (field $flags $eventrwflags) + ) +) + +;;; An event that occurred. +(typename $event + (struct + ;;; User-provided value that got attached to `subscription::userdata`. + (field $userdata $userdata) + ;;; If non-zero, an error that occurred while processing the subscription request. + (field $error $errno) + ;;; The type of event that occured + (field $type $eventtype) + ;;; The contents of the event, if it is an `eventtype::fd_read` or + ;;; `eventtype::fd_write`. `eventtype::clock` events ignore this field. + (field $fd_readwrite $event_fd_readwrite) + ) +) + +;;; Flags determining how to interpret the timestamp provided in +;;; `subscription_clock::timeout`. +(typename $subclockflags + (flags u16 + ;;; If set, treat the timestamp provided in + ;;; `subscription_clock::timeout` as an absolute timestamp of clock + ;;; `subscription_clock::id`. If clear, treat the timestamp + ;;; provided in `subscription_clock::timeout` relative to the + ;;; current time value of clock `subscription_clock::id`. + $subscription_clock_abstime + ) +) + +;;; The contents of a `subscription` when type is `eventtype::clock`. +(typename $subscription_clock + (struct + ;;; The clock against which to compare the timestamp. + (field $id $clockid) + ;;; The absolute or relative timestamp. + (field $timeout $timestamp) + ;;; The amount of time that the implementation may wait additionally + ;;; to coalesce with other events. + (field $precision $timestamp) + ;;; Flags specifying whether the timeout is absolute or relative + (field $flags $subclockflags) + ) +) + +;;; The contents of a `subscription` when type is type is +;;; `eventtype::fd_read` or `eventtype::fd_write`. +(typename $subscription_fd_readwrite + (struct + ;;; The file descriptor on which to wait for it to become ready for reading or writing. + (field $file_descriptor $fd) + ) +) + +;;; The contents of a `subscription`. +(typename $subscription_u + (union $eventtype + (field $clock $subscription_clock) + (field $fd_read $subscription_fd_readwrite) + (field $fd_write $subscription_fd_readwrite) + ) +) + +;;; Subscription to an event. +(typename $subscription + (struct + ;;; User-provided value that is attached to the subscription in the + ;;; implementation and returned through `event::userdata`. + (field $userdata $userdata) + ;;; The type of the event to which to subscribe, and its contents + (field $u $subscription_u) + ) +) + +;;; Exit code generated by a process when exiting. +(typename $exitcode u32) + +;;; Signal condition. +(typename $signal + (enum u8 + ;;; No signal. Note that POSIX has special semantics for `kill(pid, 0)`, + ;;; so this value is reserved. + $none + ;;; Hangup. + ;;; Action: Terminates the process. + $hup + ;;; Terminate interrupt signal. + ;;; Action: Terminates the process. + $int + ;;; Terminal quit signal. + ;;; Action: Terminates the process. + $quit + ;;; Illegal instruction. + ;;; Action: Terminates the process. + $ill + ;;; Trace/breakpoint trap. + ;;; Action: Terminates the process. + $trap + ;;; Process abort signal. + ;;; Action: Terminates the process. + $abrt + ;;; Access to an undefined portion of a memory object. + ;;; Action: Terminates the process. + $bus + ;;; Erroneous arithmetic operation. + ;;; Action: Terminates the process. + $fpe + ;;; Kill. + ;;; Action: Terminates the process. + $kill + ;;; User-defined signal 1. + ;;; Action: Terminates the process. + $usr1 + ;;; Invalid memory reference. + ;;; Action: Terminates the process. + $segv + ;;; User-defined signal 2. + ;;; Action: Terminates the process. + $usr2 + ;;; Write on a pipe with no one to read it. + ;;; Action: Ignored. + $pipe + ;;; Alarm clock. + ;;; Action: Terminates the process. + $alrm + ;;; Termination signal. + ;;; Action: Terminates the process. + $term + ;;; Child process terminated, stopped, or continued. + ;;; Action: Ignored. + $chld + ;;; Continue executing, if stopped. + ;;; Action: Continues executing, if stopped. + $cont + ;;; Stop executing. + ;;; Action: Stops executing. + $stop + ;;; Terminal stop signal. + ;;; Action: Stops executing. + $tstp + ;;; Background process attempting read. + ;;; Action: Stops executing. + $ttin + ;;; Background process attempting write. + ;;; Action: Stops executing. + $ttou + ;;; High bandwidth data is available at a socket. + ;;; Action: Ignored. + $urg + ;;; CPU time limit exceeded. + ;;; Action: Terminates the process. + $xcpu + ;;; File size limit exceeded. + ;;; Action: Terminates the process. + $xfsz + ;;; Virtual timer expired. + ;;; Action: Terminates the process. + $vtalrm + ;;; Profiling timer expired. + ;;; Action: Terminates the process. + $prof + ;;; Window changed. + ;;; Action: Ignored. + $winch + ;;; I/O possible. + ;;; Action: Terminates the process. + $poll + ;;; Power failure. + ;;; Action: Terminates the process. + $pwr + ;;; Bad system call. + ;;; Action: Terminates the process. + $sys + ) +) + +;;; Flags provided to `sock_recv`. +(typename $riflags + (flags u16 + ;;; Returns the message without removing it from the socket's receive queue. + $recv_peek + ;;; On byte-stream sockets, block until the full amount of data can be returned. + $recv_waitall + ) +) + +;;; Flags returned by `sock_recv`. +(typename $roflags + (flags u16 + ;;; Returned by `sock_recv`: Message data has been truncated. + $recv_data_truncated + ) +) + +;;; Flags provided to `sock_send`. As there are currently no flags +;;; defined, it must be set to zero. +(typename $siflags u16) + +;;; Which channels on a socket to shut down. +(typename $sdflags + (flags u8 + ;;; Disables further receive operations. + $rd + ;;; Disables further send operations. + $wr + ) +) + +;;; Identifiers for preopened capabilities. +(typename $preopentype + (enum u8 + ;;; A pre-opened directory. + $dir + ) +) + +;;; The contents of a $prestat when type is `preopentype::dir`. +(typename $prestat_dir + (struct + ;;; The length of the directory name for use with `fd_prestat_dir_name`. + (field $pr_name_len $size) + ) +) + +;;; Information about a pre-opened capability. +(typename $prestat + (union $preopentype + (field $dir $prestat_dir) + ) +) diff --git a/tests/wasi.rs b/tests/wasi.rs new file mode 100644 index 0000000000..95322dc3f1 --- /dev/null +++ b/tests/wasi.rs @@ -0,0 +1,324 @@ +use wiggle_runtime::{GuestError, GuestErrorType, GuestPtr, GuestPtrMut, GuestString}; +use wiggle_test::WasiCtx; + +wiggle::from_witx!({ + witx: ["tests/wasi.witx"], + ctx: WasiCtx, +}); + +type Result = std::result::Result; + +impl GuestErrorType for types::Errno { + type Context = WasiCtx; + fn success() -> types::Errno { + types::Errno::Success + } + fn from_error(e: GuestError, ctx: &mut WasiCtx) -> types::Errno { + eprintln!("GUEST ERROR: {:?}", e); + ctx.guest_errors.push(e); + types::Errno::Io + } +} + +impl crate::wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { + fn args_get( + &mut self, + _argv: GuestPtrMut>, + _argv_buf: GuestPtrMut, + ) -> Result<()> { + unimplemented!("args_get") + } + + fn args_sizes_get(&mut self) -> Result<(types::Size, types::Size)> { + unimplemented!("args_sizes_get") + } + + fn environ_get( + &mut self, + _environ: GuestPtrMut>, + _environ_buf: GuestPtrMut, + ) -> Result<()> { + unimplemented!("environ_get") + } + + fn environ_sizes_get(&mut self) -> Result<(types::Size, types::Size)> { + unimplemented!("environ_sizes_get") + } + + fn clock_res_get(&mut self, _id: types::Clockid) -> Result { + unimplemented!("clock_res_get") + } + + fn clock_time_get( + &mut self, + _id: types::Clockid, + _precision: types::Timestamp, + ) -> Result { + unimplemented!("clock_time_get") + } + + fn fd_advise( + &mut self, + _fd: types::Fd, + _offset: types::Filesize, + _len: types::Filesize, + _advice: types::Advice, + ) -> Result<()> { + unimplemented!("fd_advise") + } + + fn fd_allocate( + &mut self, + _fd: types::Fd, + _offset: types::Filesize, + _len: types::Filesize, + ) -> Result<()> { + unimplemented!("fd_allocate") + } + + fn fd_close(&mut self, _fd: types::Fd) -> Result<()> { + unimplemented!("fd_close") + } + + fn fd_datasync(&mut self, _fd: types::Fd) -> Result<()> { + unimplemented!("fd_datasync") + } + + fn fd_fdstat_get(&mut self, _fd: types::Fd) -> Result { + unimplemented!("fd_fdstat_get") + } + + fn fd_fdstat_set_flags(&mut self, _fd: types::Fd, _flags: types::Fdflags) -> Result<()> { + unimplemented!("fd_fdstat_set_flags") + } + + fn fd_fdstat_set_rights( + &mut self, + _fd: types::Fd, + _fs_rights_base: types::Rights, + _fs_rights_inherting: types::Rights, + ) -> Result<()> { + unimplemented!("fd_fdstat_set_rights") + } + + fn fd_filestat_get(&mut self, _fd: types::Fd) -> Result { + unimplemented!("fd_filestat_get") + } + + fn fd_filestat_set_size(&mut self, _fd: types::Fd, _size: types::Filesize) -> Result<()> { + unimplemented!("fd_filestat_set_size") + } + + fn fd_filestat_set_times( + &mut self, + _fd: types::Fd, + _atim: types::Timestamp, + _mtim: types::Timestamp, + _fst_flags: types::Fstflags, + ) -> Result<()> { + unimplemented!("fd_filestat_set_times") + } + + fn fd_pread( + &mut self, + _fd: types::Fd, + _iovs: &types::IovecArray<'_>, + _offset: types::Filesize, + ) -> Result { + unimplemented!("fd_pread") + } + + fn fd_prestat_get(&mut self, _fd: types::Fd) -> Result { + unimplemented!("fd_prestat_get") + } + + fn fd_prestat_dir_name( + &mut self, + _fd: types::Fd, + _path: GuestPtrMut, + _path_len: types::Size, + ) -> Result<()> { + unimplemented!("fd_prestat_dir_name") + } + + fn fd_pwrite( + &mut self, + _fd: types::Fd, + _ciovs: &types::CiovecArray<'_>, + _offset: types::Filesize, + ) -> Result { + unimplemented!("fd_pwrite") + } + + fn fd_read(&mut self, _fd: types::Fd, _iovs: &types::IovecArray<'_>) -> Result { + unimplemented!("fd_read") + } + + fn fd_readdir( + &mut self, + _fd: types::Fd, + _buf: GuestPtrMut, + _buf_len: types::Size, + _cookie: types::Dircookie, + ) -> Result { + unimplemented!("fd_readdir") + } + + fn fd_renumber(&mut self, _fd: types::Fd, _to: types::Fd) -> Result<()> { + unimplemented!("fd_renumber") + } + + fn fd_seek( + &mut self, + _fd: types::Fd, + _offset: types::Filedelta, + _whence: types::Whence, + ) -> Result { + unimplemented!("fd_seek") + } + + fn fd_sync(&mut self, _fd: types::Fd) -> Result<()> { + unimplemented!("fd_sync") + } + + fn fd_tell(&mut self, _fd: types::Fd) -> Result { + unimplemented!("fd_tell") + } + + fn fd_write(&mut self, _fd: types::Fd, _ciovs: &types::CiovecArray<'_>) -> Result { + unimplemented!("fd_write") + } + + fn path_create_directory(&mut self, _fd: types::Fd, _path: &GuestString<'_>) -> Result<()> { + unimplemented!("path_create_directory") + } + + fn path_filestat_get( + &mut self, + _fd: types::Fd, + _flags: types::Lookupflags, + _path: &GuestString<'_>, + ) -> Result { + unimplemented!("path_filestat_get") + } + + fn path_filestat_set_times( + &mut self, + _fd: types::Fd, + _flags: types::Lookupflags, + _path: &GuestString<'_>, + _atim: types::Timestamp, + _mtim: types::Timestamp, + _fst_flags: types::Fstflags, + ) -> Result<()> { + unimplemented!("path_filestat_set_times") + } + + fn path_link( + &mut self, + _old_fd: types::Fd, + _old_flags: types::Lookupflags, + _old_path: &GuestString<'_>, + _new_fd: types::Fd, + _new_path: &GuestString<'_>, + ) -> Result<()> { + unimplemented!("path_link") + } + + fn path_open( + &mut self, + _fd: types::Fd, + _dirflags: types::Lookupflags, + _path: &GuestString<'_>, + _oflags: types::Oflags, + _fs_rights_base: types::Rights, + _fs_rights_inherting: types::Rights, + _fdflags: types::Fdflags, + ) -> Result { + unimplemented!("path_open") + } + + fn path_readlink( + &mut self, + _fd: types::Fd, + _path: &GuestString<'_>, + _buf: GuestPtrMut, + _buf_len: types::Size, + ) -> Result { + unimplemented!("path_readlink") + } + + fn path_remove_directory(&mut self, _fd: types::Fd, _path: &GuestString<'_>) -> Result<()> { + unimplemented!("path_remove_directory") + } + + fn path_rename( + &mut self, + _fd: types::Fd, + _old_path: &GuestString<'_>, + _new_fd: types::Fd, + _new_path: &GuestString<'_>, + ) -> Result<()> { + unimplemented!("path_rename") + } + + fn path_symlink( + &mut self, + _old_path: &GuestString<'_>, + _fd: types::Fd, + _new_path: &GuestString<'_>, + ) -> Result<()> { + unimplemented!("path_symlink") + } + + fn path_unlink_file(&mut self, _fd: types::Fd, _path: &GuestString<'_>) -> Result<()> { + unimplemented!("path_unlink_file") + } + + fn poll_oneoff( + &mut self, + _in_: GuestPtr, + _out: GuestPtrMut, + _nsubscriptions: types::Size, + ) -> Result { + unimplemented!("poll_oneoff") + } + + fn proc_exit(&mut self, _rval: types::Exitcode) -> std::result::Result<(), ()> { + unimplemented!("proc_exit") + } + + fn proc_raise(&mut self, _sig: types::Signal) -> Result<()> { + unimplemented!("proc_raise") + } + + fn sched_yield(&mut self) -> Result<()> { + unimplemented!("sched_yield") + } + + fn random_get(&mut self, _buf: GuestPtrMut, _buf_len: types::Size) -> Result<()> { + unimplemented!("random_get") + } + + fn sock_recv( + &mut self, + _fd: types::Fd, + _ri_data: &types::IovecArray<'_>, + _ri_flags: types::Riflags, + ) -> Result<(types::Size, types::Roflags)> { + unimplemented!("sock_recv") + } + + fn sock_send( + &mut self, + _fd: types::Fd, + _si_data: &types::CiovecArray<'_>, + _si_flags: types::Siflags, + ) -> Result { + unimplemented!("sock_send") + } + + fn sock_shutdown(&mut self, _fd: types::Fd, _how: types::Sdflags) -> Result<()> { + unimplemented!("sock_shutdown") + } +} diff --git a/tests/wasi.witx b/tests/wasi.witx new file mode 100644 index 0000000000..98cd947878 --- /dev/null +++ b/tests/wasi.witx @@ -0,0 +1,532 @@ +;; WASI Preview. This is an evolution of the API that WASI initially +;; launched with. +;; +;; Some content here is derived from [CloudABI](https://github.com/NuxiNL/cloudabi). +;; +;; This is a `witx` file. See [here](https://github.com/WebAssembly/WASI/tree/master/docs/witx.md) +;; for an explanation of what that means. + +(use "typenames.witx") + +(module $wasi_snapshot_preview1 + ;;; Linear memory to be accessed by WASI functions that need it. + (import "memory" (memory)) + + ;;; Read command-line argument data. + ;;; The size of the array should match that returned by `args_sizes_get` + (@interface func (export "args_get") + (param $argv (@witx pointer (@witx pointer u8))) + (param $argv_buf (@witx pointer u8)) + (result $error $errno) + ) + ;;; Return command-line argument data sizes. + (@interface func (export "args_sizes_get") + (result $error $errno) + ;;; The number of arguments. + (result $argc $size) + ;;; The size of the argument string data. + (result $argv_buf_size $size) + ) + + ;;; Read environment variable data. + ;;; The sizes of the buffers should match that returned by `environ_sizes_get`. + (@interface func (export "environ_get") + (param $environ (@witx pointer (@witx pointer u8))) + (param $environ_buf (@witx pointer u8)) + (result $error $errno) + ) + ;;; Return environment variable data sizes. + (@interface func (export "environ_sizes_get") + (result $error $errno) + ;;; The number of environment variable arguments. + (result $environc $size) + ;;; The size of the environment variable data. + (result $environ_buf_size $size) + ) + + ;;; Return the resolution of a clock. + ;;; Implementations are required to provide a non-zero value for supported clocks. For unsupported clocks, + ;;; return `errno::inval`. + ;;; Note: This is similar to `clock_getres` in POSIX. + (@interface func (export "clock_res_get") + ;;; The clock for which to return the resolution. + (param $id $clockid) + (result $error $errno) + ;;; The resolution of the clock. + (result $resolution $timestamp) + ) + ;;; Return the time value of a clock. + ;;; Note: This is similar to `clock_gettime` in POSIX. + (@interface func (export "clock_time_get") + ;;; The clock for which to return the time. + (param $id $clockid) + ;;; The maximum lag (exclusive) that the returned time value may have, compared to its actual value. + (param $precision $timestamp) + (result $error $errno) + ;;; The time value of the clock. + (result $time $timestamp) + ) + + ;;; Provide file advisory information on a file descriptor. + ;;; Note: This is similar to `posix_fadvise` in POSIX. + (@interface func (export "fd_advise") + (param $fd $fd) + ;;; The offset within the file to which the advisory applies. + (param $offset $filesize) + ;;; The length of the region to which the advisory applies. + (param $len $filesize) + ;;; The advice. + (param $advice $advice) + (result $error $errno) + ) + + ;;; Force the allocation of space in a file. + ;;; Note: This is similar to `posix_fallocate` in POSIX. + (@interface func (export "fd_allocate") + (param $fd $fd) + ;;; The offset at which to start the allocation. + (param $offset $filesize) + ;;; The length of the area that is allocated. + (param $len $filesize) + (result $error $errno) + ) + + ;;; Close a file descriptor. + ;;; Note: This is similar to `close` in POSIX. + (@interface func (export "fd_close") + (param $fd $fd) + (result $error $errno) + ) + + ;;; Synchronize the data of a file to disk. + ;;; Note: This is similar to `fdatasync` in POSIX. + (@interface func (export "fd_datasync") + (param $fd $fd) + (result $error $errno) + ) + + ;;; Get the attributes of a file descriptor. + ;;; Note: This returns similar flags to `fsync(fd, F_GETFL)` in POSIX, as well as additional fields. + (@interface func (export "fd_fdstat_get") + (param $fd $fd) + (result $error $errno) + ;;; The buffer where the file descriptor's attributes are stored. + (result $stat $fdstat) + ) + + ;;; Adjust the flags associated with a file descriptor. + ;;; Note: This is similar to `fcntl(fd, F_SETFL, flags)` in POSIX. + (@interface func (export "fd_fdstat_set_flags") + (param $fd $fd) + ;;; The desired values of the file descriptor flags. + (param $flags $fdflags) + (result $error $errno) + ) + + ;;; Adjust the rights associated with a file descriptor. + ;;; This can only be used to remove rights, and returns `errno::notcapable` if called in a way that would attempt to add rights + (@interface func (export "fd_fdstat_set_rights") + (param $fd $fd) + ;;; The desired rights of the file descriptor. + (param $fs_rights_base $rights) + (param $fs_rights_inheriting $rights) + (result $error $errno) + ) + + ;;; Return the attributes of an open file. + (@interface func (export "fd_filestat_get") + (param $fd $fd) + (result $error $errno) + ;;; The buffer where the file's attributes are stored. + (result $buf $filestat) + ) + + ;;; Adjust the size of an open file. If this increases the file's size, the extra bytes are filled with zeros. + ;;; Note: This is similar to `ftruncate` in POSIX. + (@interface func (export "fd_filestat_set_size") + (param $fd $fd) + ;;; The desired file size. + (param $size $filesize) + (result $error $errno) + ) + + ;;; Adjust the timestamps of an open file or directory. + ;;; Note: This is similar to `futimens` in POSIX. + (@interface func (export "fd_filestat_set_times") + (param $fd $fd) + ;;; The desired values of the data access timestamp. + (param $atim $timestamp) + ;;; The desired values of the data modification timestamp. + (param $mtim $timestamp) + ;;; A bitmask indicating which timestamps to adjust. + (param $fst_flags $fstflags) + (result $error $errno) + ) + + ;;; Read from a file descriptor, without using and updating the file descriptor's offset. + ;;; Note: This is similar to `preadv` in POSIX. + (@interface func (export "fd_pread") + (param $fd $fd) + ;;; List of scatter/gather vectors in which to store data. + (param $iovs $iovec_array) + ;;; The offset within the file at which to read. + (param $offset $filesize) + (result $error $errno) + ;;; The number of bytes read. + (result $nread $size) + ) + + ;;; Return a description of the given preopened file descriptor. + (@interface func (export "fd_prestat_get") + (param $fd $fd) + (result $error $errno) + ;;; The buffer where the description is stored. + (result $buf $prestat) + ) + + ;;; Return a description of the given preopened file descriptor. + (@interface func (export "fd_prestat_dir_name") + (param $fd $fd) + ;;; A buffer into which to write the preopened directory name. + (param $path (@witx pointer u8)) + (param $path_len $size) + (result $error $errno) + ) + + ;;; Write to a file descriptor, without using and updating the file descriptor's offset. + ;;; Note: This is similar to `pwritev` in POSIX. + (@interface func (export "fd_pwrite") + (param $fd $fd) + ;;; List of scatter/gather vectors from which to retrieve data. + (param $iovs $ciovec_array) + ;;; The offset within the file at which to write. + (param $offset $filesize) + (result $error $errno) + ;;; The number of bytes written. + (result $nwritten $size) + ) + + ;;; Read from a file descriptor. + ;;; Note: This is similar to `readv` in POSIX. + (@interface func (export "fd_read") + (param $fd $fd) + ;;; List of scatter/gather vectors to which to store data. + (param $iovs $iovec_array) + (result $error $errno) + ;;; The number of bytes read. + (result $nread $size) + ) + + ;;; Read directory entries from a directory. + ;;; When successful, the contents of the output buffer consist of a sequence of + ;;; directory entries. Each directory entry consists of a dirent_t object, + ;;; followed by dirent_t::d_namlen bytes holding the name of the directory + ;;; entry. + ;; + ;;; This function fills the output buffer as much as possible, potentially + ;;; truncating the last directory entry. This allows the caller to grow its + ;;; read buffer size in case it's too small to fit a single large directory + ;;; entry, or skip the oversized directory entry. + (@interface func (export "fd_readdir") + (param $fd $fd) + ;;; The buffer where directory entries are stored + (param $buf (@witx pointer u8)) + (param $buf_len $size) + ;;; The location within the directory to start reading + (param $cookie $dircookie) + (result $error $errno) + ;;; The number of bytes stored in the read buffer. If less than the size of the read buffer, the end of the directory has been reached. + (result $bufused $size) + ) + + ;;; Atomically replace a file descriptor by renumbering another file descriptor. + ;; + ;;; Due to the strong focus on thread safety, this environment does not provide + ;;; a mechanism to duplicate or renumber a file descriptor to an arbitrary + ;;; number, like `dup2()`. This would be prone to race conditions, as an actual + ;;; file descriptor with the same number could be allocated by a different + ;;; thread at the same time. + ;; + ;;; This function provides a way to atomically renumber file descriptors, which + ;;; would disappear if `dup2()` were to be removed entirely. + (@interface func (export "fd_renumber") + (param $fd $fd) + ;;; The file descriptor to overwrite. + (param $to $fd) + (result $error $errno) + ) + + ;;; Move the offset of a file descriptor. + ;;; Note: This is similar to `lseek` in POSIX. + (@interface func (export "fd_seek") + (param $fd $fd) + ;;; The number of bytes to move. + (param $offset $filedelta) + ;;; The base from which the offset is relative. + (param $whence $whence) + (result $error $errno) + ;;; The new offset of the file descriptor, relative to the start of the file. + (result $newoffset $filesize) + ) + + ;;; Synchronize the data and metadata of a file to disk. + ;;; Note: This is similar to `fsync` in POSIX. + (@interface func (export "fd_sync") + (param $fd $fd) + (result $error $errno) + ) + + ;;; Return the current offset of a file descriptor. + ;;; Note: This is similar to `lseek(fd, 0, SEEK_CUR)` in POSIX. + (@interface func (export "fd_tell") + (param $fd $fd) + (result $error $errno) + ;;; The current offset of the file descriptor, relative to the start of the file. + (result $offset $filesize) + ) + + ;;; Write to a file descriptor. + ;;; Note: This is similar to `writev` in POSIX. + (@interface func (export "fd_write") + (param $fd $fd) + ;;; List of scatter/gather vectors from which to retrieve data. + (param $iovs $ciovec_array) + (result $error $errno) + ;;; The number of bytes written. + (result $nwritten $size) + ) + + ;;; Create a directory. + ;;; Note: This is similar to `mkdirat` in POSIX. + (@interface func (export "path_create_directory") + (param $fd $fd) + ;;; The path at which to create the directory. + (param $path string) + (result $error $errno) + ) + + ;;; Return the attributes of a file or directory. + ;;; Note: This is similar to `stat` in POSIX. + (@interface func (export "path_filestat_get") + (param $fd $fd) + ;;; Flags determining the method of how the path is resolved. + (param $flags $lookupflags) + ;;; The path of the file or directory to inspect. + (param $path string) + (result $error $errno) + ;;; The buffer where the file's attributes are stored. + (result $buf $filestat) + ) + + ;;; Adjust the timestamps of a file or directory. + ;;; Note: This is similar to `utimensat` in POSIX. + (@interface func (export "path_filestat_set_times") + (param $fd $fd) + ;;; Flags determining the method of how the path is resolved. + (param $flags $lookupflags) + ;;; The path of the file or directory to operate on. + (param $path string) + ;;; The desired values of the data access timestamp. + (param $atim $timestamp) + ;;; The desired values of the data modification timestamp. + (param $mtim $timestamp) + ;;; A bitmask indicating which timestamps to adjust. + (param $fst_flags $fstflags) + (result $error $errno) + ) + + ;;; Create a hard link. + ;;; Note: This is similar to `linkat` in POSIX. + (@interface func (export "path_link") + (param $old_fd $fd) + ;;; Flags determining the method of how the path is resolved. + (param $old_flags $lookupflags) + ;;; The source path from which to link. + (param $old_path string) + ;;; The working directory at which the resolution of the new path starts. + (param $new_fd $fd) + ;;; The destination path at which to create the hard link. + (param $new_path string) + (result $error $errno) + ) + + ;;; Open a file or directory. + ;; + ;;; The returned file descriptor is not guaranteed to be the lowest-numbered + ;;; file descriptor not currently open; it is randomized to prevent + ;;; applications from depending on making assumptions about indexes, since this + ;;; is error-prone in multi-threaded contexts. The returned file descriptor is + ;;; guaranteed to be less than 2**31. + ;; + ;;; Note: This is similar to `openat` in POSIX. + (@interface func (export "path_open") + (param $fd $fd) + ;;; Flags determining the method of how the path is resolved. + (param $dirflags $lookupflags) + ;;; The relative path of the file or directory to open, relative to the + ;;; `path_open::fd` directory. + (param $path string) + ;;; The method by which to open the file. + (param $oflags $oflags) + ;;; The initial rights of the newly created file descriptor. The + ;;; implementation is allowed to return a file descriptor with fewer rights + ;;; than specified, if and only if those rights do not apply to the type of + ;;; file being opened. + ;; + ;;; The *base* rights are rights that will apply to operations using the file + ;;; descriptor itself, while the *inheriting* rights are rights that apply to + ;;; file descriptors derived from it. + (param $fs_rights_base $rights) + (param $fs_rights_inherting $rights) + (param $fdflags $fdflags) + (result $error $errno) + ;;; The file descriptor of the file that has been opened. + (result $opened_fd $fd) + ) + + ;;; Read the contents of a symbolic link. + ;;; Note: This is similar to `readlinkat` in POSIX. + (@interface func (export "path_readlink") + (param $fd $fd) + ;;; The path of the symbolic link from which to read. + (param $path string) + ;;; The buffer to which to write the contents of the symbolic link. + (param $buf (@witx pointer u8)) + (param $buf_len $size) + (result $error $errno) + ;;; The number of bytes placed in the buffer. + (result $bufused $size) + ) + + ;;; Remove a directory. + ;;; Return `errno::notempty` if the directory is not empty. + ;;; Note: This is similar to `unlinkat(fd, path, AT_REMOVEDIR)` in POSIX. + (@interface func (export "path_remove_directory") + (param $fd $fd) + ;;; The path to a directory to remove. + (param $path string) + (result $error $errno) + ) + + ;;; Rename a file or directory. + ;;; Note: This is similar to `renameat` in POSIX. + (@interface func (export "path_rename") + (param $fd $fd) + ;;; The source path of the file or directory to rename. + (param $old_path string) + ;;; The working directory at which the resolution of the new path starts. + (param $new_fd $fd) + ;;; The destination path to which to rename the file or directory. + (param $new_path string) + (result $error $errno) + ) + + ;;; Create a symbolic link. + ;;; Note: This is similar to `symlinkat` in POSIX. + (@interface func (export "path_symlink") + ;;; The contents of the symbolic link. + (param $old_path string) + (param $fd $fd) + ;;; The destination path at which to create the symbolic link. + (param $new_path string) + (result $error $errno) + ) + + + ;;; Unlink a file. + ;;; Return `errno::isdir` if the path refers to a directory. + ;;; Note: This is similar to `unlinkat(fd, path, 0)` in POSIX. + (@interface func (export "path_unlink_file") + (param $fd $fd) + ;;; The path to a file to unlink. + (param $path string) + (result $error $errno) + ) + + ;;; Concurrently poll for the occurrence of a set of events. + (@interface func (export "poll_oneoff") + ;;; The events to which to subscribe. + (param $in (@witx const_pointer $subscription)) + ;;; The events that have occurred. + (param $out (@witx pointer $event)) + ;;; Both the number of subscriptions and events. + (param $nsubscriptions $size) + (result $error $errno) + ;;; The number of events stored. + (result $nevents $size) + ) + + ;;; Terminate the process normally. An exit code of 0 indicates successful + ;;; termination of the program. The meanings of other values is dependent on + ;;; the environment. + (@interface func (export "proc_exit") + ;;; The exit code returned by the process. + (param $rval $exitcode) + ) + + ;;; Send a signal to the process of the calling thread. + ;;; Note: This is similar to `raise` in POSIX. + (@interface func (export "proc_raise") + ;;; The signal condition to trigger. + (param $sig $signal) + (result $error $errno) + ) + + ;;; Temporarily yield execution of the calling thread. + ;;; Note: This is similar to `sched_yield` in POSIX. + (@interface func (export "sched_yield") + (result $error $errno) + ) + + ;;; Write high-quality random data into a buffer. + ;;; This function blocks when the implementation is unable to immediately + ;;; provide sufficient high-quality random data. + ;;; This function may execute slowly, so when large mounts of random data are + ;;; required, it's advisable to use this function to seed a pseudo-random + ;;; number generator, rather than to provide the random data directly. + (@interface func (export "random_get") + ;;; The buffer to fill with random data. + (param $buf (@witx pointer u8)) + (param $buf_len $size) + (result $error $errno) + ) + + ;;; Receive a message from a socket. + ;;; Note: This is similar to `recv` in POSIX, though it also supports reading + ;;; the data into multiple buffers in the manner of `readv`. + (@interface func (export "sock_recv") + (param $fd $fd) + ;;; List of scatter/gather vectors to which to store data. + (param $ri_data $iovec_array) + ;;; Message flags. + (param $ri_flags $riflags) + (result $error $errno) + ;;; Number of bytes stored in ri_data. + (result $ro_datalen $size) + ;;; Message flags. + (result $ro_flags $roflags) + ) + + ;;; Send a message on a socket. + ;;; Note: This is similar to `send` in POSIX, though it also supports writing + ;;; the data from multiple buffers in the manner of `writev`. + (@interface func (export "sock_send") + (param $fd $fd) + ;;; List of scatter/gather vectors to which to retrieve data + (param $si_data $ciovec_array) + ;;; Message flags. + (param $si_flags $siflags) + (result $error $errno) + ;;; Number of bytes transmitted. + (result $so_datalen $size) + ) + + ;;; Shut down socket send and receive channels. + ;;; Note: This is similar to `shutdown` in POSIX. + (@interface func (export "sock_shutdown") + (param $fd $fd) + ;;; Which channels on the socket to shut down. + (param $how $sdflags) + (result $error $errno) + ) +) From ea4d2d05350511d5576dd7c40799eb2497e25b92 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 3 Mar 2020 00:26:18 +0100 Subject: [PATCH 76/86] Return *mut u8 in GuestPtrMut::as_raw Currently, we create an immutable `GuestPtr` from `self` and call `as_raw` on it which correctly returns `*const u8`. However, since we're dealing with `GuestPtrMut` I thought it might make more sense to return `*mut u8` directly instead. This will be needed (and will save us from silly casts `*const _ as *mut _`) in plugging in `Iovec<'_>` into `std::io::IoSliceMut` in `fd_read` and `fd_pread` calls. --- crates/runtime/src/memory/ptr.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/runtime/src/memory/ptr.rs b/crates/runtime/src/memory/ptr.rs index a09068ae25..54a80e8ab4 100644 --- a/crates/runtime/src/memory/ptr.rs +++ b/crates/runtime/src/memory/ptr.rs @@ -162,8 +162,8 @@ where } } - pub fn as_raw(&self) -> *const u8 { - self.as_immut().as_raw() + pub fn as_raw(&self) -> *mut u8 { + (self.mem.ptr as usize + self.region.start as usize) as *mut u8 } pub fn elem(&self, elements: i32) -> Result { From 37642042503384a3553451477ca69f4e4b903068 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 3 Mar 2020 15:25:11 +0100 Subject: [PATCH 77/86] Put context object behind a ref rather than mut ref This commit puts context object, i.e., the implementor of the WASI snapshot, behind a reference `&self` rather than a mutable reference `&mut self`. As suggested by @alexcrichton, this gives the implementor the possibility to determine how it handles its interior mutability. --- crates/generate/src/module_trait.rs | 4 +- crates/runtime/src/guest_type.rs | 2 +- crates/test/src/lib.rs | 9 +-- tests/arrays.rs | 4 +- tests/atoms.rs | 7 +-- tests/flags.rs | 2 +- tests/handles.rs | 4 +- tests/ints.rs | 2 +- tests/pointers.rs | 2 +- tests/strings.rs | 2 +- tests/structs.rs | 10 +-- tests/union.rs | 8 +-- tests/wasi.rs | 96 +++++++++++++++-------------- 13 files changed, 74 insertions(+), 78 deletions(-) diff --git a/crates/generate/src/module_trait.rs b/crates/generate/src/module_trait.rs index 7fc1f46cd2..96515499f0 100644 --- a/crates/generate/src/module_trait.rs +++ b/crates/generate/src/module_trait.rs @@ -39,9 +39,9 @@ pub fn define_module_trait(names: &Names, m: &Module) -> TokenStream { .unwrap_or(quote!(())); if is_anonymous { - quote!(fn #funcname(&mut self, #(#args),*) -> Result<(#(#rets),*), #err>;) + quote!(fn #funcname(&self, #(#args),*) -> Result<(#(#rets),*), #err>;) } else { - quote!(fn #funcname<#lifetime>(&mut self, #(#args),*) -> Result<(#(#rets),*), #err>;) + quote!(fn #funcname<#lifetime>(&self, #(#args),*) -> Result<(#(#rets),*), #err>;) } }); quote! { diff --git a/crates/runtime/src/guest_type.rs b/crates/runtime/src/guest_type.rs index 2a9eadc59c..2d69548554 100644 --- a/crates/runtime/src/guest_type.rs +++ b/crates/runtime/src/guest_type.rs @@ -50,5 +50,5 @@ builtin_type!(u8, i8, u16, i16, u32, i32, u64, i64, f32, f64, usize); pub trait GuestErrorType { type Context; fn success() -> Self; - fn from_error(e: GuestError, ctx: &mut Self::Context) -> Self; + fn from_error(e: GuestError, ctx: &Self::Context) -> Self; } diff --git a/crates/test/src/lib.rs b/crates/test/src/lib.rs index 2ff31211c1..2ddf5704f8 100644 --- a/crates/test/src/lib.rs +++ b/crates/test/src/lib.rs @@ -91,16 +91,17 @@ mod test { } } +use std::cell::RefCell; use wiggle_runtime::GuestError; pub struct WasiCtx { - pub guest_errors: Vec, + pub guest_errors: RefCell>, } impl WasiCtx { pub fn new() -> Self { Self { - guest_errors: vec![], + guest_errors: RefCell::new(vec![]), } } } @@ -117,9 +118,9 @@ macro_rules! impl_errno { fn success() -> $errno { <$errno>::Ok } - fn from_error(e: GuestError, ctx: &mut WasiCtx) -> $errno { + fn from_error(e: GuestError, ctx: &WasiCtx) -> $errno { eprintln!("GUEST ERROR: {:?}", e); - ctx.guest_errors.push(e); + ctx.guest_errors.borrow_mut().push(e); types::Errno::InvalidArg } } diff --git a/tests/arrays.rs b/tests/arrays.rs index f8ec9a3675..26583911ea 100644 --- a/tests/arrays.rs +++ b/tests/arrays.rs @@ -11,7 +11,7 @@ impl_errno!(types::Errno); impl arrays::Arrays for WasiCtx { fn reduce_excuses( - &mut self, + &self, excuses: &types::ConstExcuseArray, ) -> Result { let last = GuestType::read( @@ -25,7 +25,7 @@ impl arrays::Arrays for WasiCtx { Ok(*last.as_ref().expect("dereferencing ptr should succeed")) } - fn populate_excuses(&mut self, excuses: &types::ExcuseArray) -> Result<(), types::Errno> { + fn populate_excuses(&self, excuses: &types::ExcuseArray) -> Result<(), types::Errno> { for excuse in excuses.iter() { let ptr_to_ptr = GuestType::read(&excuse.expect("valid ptr to ptr")) .expect("valid ptr to some Excuse value"); diff --git a/tests/atoms.rs b/tests/atoms.rs index 3a6f56ac52..8eda99ba1d 100644 --- a/tests/atoms.rs +++ b/tests/atoms.rs @@ -10,14 +10,11 @@ wiggle::from_witx!({ impl_errno!(types::Errno); impl atoms::Atoms for WasiCtx { - fn int_float_args(&mut self, an_int: u32, an_float: f32) -> Result<(), types::Errno> { + fn int_float_args(&self, an_int: u32, an_float: f32) -> Result<(), types::Errno> { println!("INT FLOAT ARGS: {} {}", an_int, an_float); Ok(()) } - fn double_int_return_float( - &mut self, - an_int: u32, - ) -> Result { + fn double_int_return_float(&self, an_int: u32) -> Result { Ok((an_int as f32) * 2.0) } } diff --git a/tests/flags.rs b/tests/flags.rs index c3973d10e6..0b729c94d4 100644 --- a/tests/flags.rs +++ b/tests/flags.rs @@ -12,7 +12,7 @@ impl_errno!(types::Errno); impl flags::Flags for WasiCtx { fn configure_car( - &mut self, + &self, old_config: types::CarConfig, other_config_ptr: GuestPtr, ) -> Result { diff --git a/tests/handles.rs b/tests/handles.rs index b082bd3ef8..7f1e86d7f3 100644 --- a/tests/handles.rs +++ b/tests/handles.rs @@ -12,10 +12,10 @@ wiggle::from_witx!({ impl_errno!(types::Errno); impl handle_examples::HandleExamples for WasiCtx { - fn fd_create(&mut self) -> Result { + fn fd_create(&self) -> Result { Ok(types::Fd::from(FD_VAL)) } - fn fd_consume(&mut self, fd: types::Fd) -> Result<(), types::Errno> { + fn fd_consume(&self, fd: types::Fd) -> Result<(), types::Errno> { println!("FD_CONSUME {}", fd); if fd == types::Fd::from(FD_VAL) { Ok(()) diff --git a/tests/ints.rs b/tests/ints.rs index 6a927adf00..928e061e7c 100644 --- a/tests/ints.rs +++ b/tests/ints.rs @@ -11,7 +11,7 @@ wiggle::from_witx!({ impl_errno!(types::Errno); impl ints::Ints for WasiCtx { - fn cookie_cutter(&mut self, init_cookie: types::Cookie) -> Result { + fn cookie_cutter(&self, init_cookie: types::Cookie) -> Result { let res = if init_cookie == types::Cookie::START { types::Bool::True } else { diff --git a/tests/pointers.rs b/tests/pointers.rs index ec0977c8bc..3309cf7742 100644 --- a/tests/pointers.rs +++ b/tests/pointers.rs @@ -11,7 +11,7 @@ impl_errno!(types::Errno); impl pointers::Pointers for WasiCtx { fn pointers_and_enums( - &mut self, + &self, input1: types::Excuse, input2_ptr: GuestPtrMut, input3_ptr: GuestPtr, diff --git a/tests/strings.rs b/tests/strings.rs index 4558108b37..9bcb634391 100644 --- a/tests/strings.rs +++ b/tests/strings.rs @@ -10,7 +10,7 @@ wiggle::from_witx!({ impl_errno!(types::Errno); impl strings::Strings for WasiCtx { - fn hello_string(&mut self, a_string: &GuestString<'_>) -> Result { + fn hello_string(&self, a_string: &GuestString<'_>) -> Result { let as_ref = a_string.as_ref().expect("deref ptr should succeed"); let as_str = as_ref.as_str().expect("valid UTF-8 string"); println!("a_string='{}'", as_str); diff --git a/tests/structs.rs b/tests/structs.rs index e36b0d4903..511e2fffbd 100644 --- a/tests/structs.rs +++ b/tests/structs.rs @@ -10,11 +10,11 @@ wiggle::from_witx!({ impl_errno!(types::Errno); impl structs::Structs for WasiCtx { - fn sum_of_pair(&mut self, an_pair: &types::PairInts) -> Result { + fn sum_of_pair(&self, an_pair: &types::PairInts) -> Result { Ok(an_pair.first as i64 + an_pair.second as i64) } - fn sum_of_pair_of_ptrs(&mut self, an_pair: &types::PairIntPtrs) -> Result { + fn sum_of_pair_of_ptrs(&self, an_pair: &types::PairIntPtrs) -> Result { let first = *an_pair .first .as_ref() @@ -26,7 +26,7 @@ impl structs::Structs for WasiCtx { Ok(first as i64 + second as i64) } - fn sum_of_int_and_ptr(&mut self, an_pair: &types::PairIntAndPtr) -> Result { + fn sum_of_int_and_ptr(&self, an_pair: &types::PairIntAndPtr) -> Result { let first = *an_pair .first .as_ref() @@ -35,7 +35,7 @@ impl structs::Structs for WasiCtx { Ok(first as i64 + second) } - fn return_pair_ints(&mut self) -> Result { + fn return_pair_ints(&self) -> Result { Ok(types::PairInts { first: 10, second: 20, @@ -43,7 +43,7 @@ impl structs::Structs for WasiCtx { } fn return_pair_of_ptrs<'a>( - &mut self, + &self, first: GuestPtr<'a, i32>, second: GuestPtr<'a, i32>, ) -> Result, types::Errno> { diff --git a/tests/union.rs b/tests/union.rs index 7a3a6f0364..85e63a7a21 100644 --- a/tests/union.rs +++ b/tests/union.rs @@ -32,7 +32,7 @@ fn mult_zero_nan(a: f32, b: u32) -> f32 { } impl union_example::UnionExample for WasiCtx { - fn get_tag(&mut self, u: &types::Reason) -> Result { + fn get_tag(&self, u: &types::Reason) -> Result { println!("GET TAG: {:?}", u); match u { types::Reason::DogAte { .. } => Ok(types::Excuse::DogAte), @@ -40,11 +40,7 @@ impl union_example::UnionExample for WasiCtx { types::Reason::Sleeping { .. } => Ok(types::Excuse::Sleeping), } } - fn reason_mult( - &mut self, - u: &types::ReasonMut<'_>, - multiply_by: u32, - ) -> Result<(), types::Errno> { + fn reason_mult(&self, u: &types::ReasonMut<'_>, multiply_by: u32) -> Result<(), types::Errno> { match u { types::ReasonMut::DogAte(fptr) => { let mut f = fptr.as_ref_mut().expect("valid pointer"); diff --git a/tests/wasi.rs b/tests/wasi.rs index 95322dc3f1..16042ce86e 100644 --- a/tests/wasi.rs +++ b/tests/wasi.rs @@ -10,47 +10,49 @@ type Result = std::result::Result; impl GuestErrorType for types::Errno { type Context = WasiCtx; + fn success() -> types::Errno { types::Errno::Success } - fn from_error(e: GuestError, ctx: &mut WasiCtx) -> types::Errno { + + fn from_error(e: GuestError, ctx: &WasiCtx) -> types::Errno { eprintln!("GUEST ERROR: {:?}", e); - ctx.guest_errors.push(e); + ctx.guest_errors.borrow_mut().push(e); types::Errno::Io } } impl crate::wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { fn args_get( - &mut self, + &self, _argv: GuestPtrMut>, _argv_buf: GuestPtrMut, ) -> Result<()> { unimplemented!("args_get") } - fn args_sizes_get(&mut self) -> Result<(types::Size, types::Size)> { + fn args_sizes_get(&self) -> Result<(types::Size, types::Size)> { unimplemented!("args_sizes_get") } fn environ_get( - &mut self, + &self, _environ: GuestPtrMut>, _environ_buf: GuestPtrMut, ) -> Result<()> { unimplemented!("environ_get") } - fn environ_sizes_get(&mut self) -> Result<(types::Size, types::Size)> { + fn environ_sizes_get(&self) -> Result<(types::Size, types::Size)> { unimplemented!("environ_sizes_get") } - fn clock_res_get(&mut self, _id: types::Clockid) -> Result { + fn clock_res_get(&self, _id: types::Clockid) -> Result { unimplemented!("clock_res_get") } fn clock_time_get( - &mut self, + &self, _id: types::Clockid, _precision: types::Timestamp, ) -> Result { @@ -58,7 +60,7 @@ impl crate::wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { } fn fd_advise( - &mut self, + &self, _fd: types::Fd, _offset: types::Filesize, _len: types::Filesize, @@ -68,7 +70,7 @@ impl crate::wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { } fn fd_allocate( - &mut self, + &self, _fd: types::Fd, _offset: types::Filesize, _len: types::Filesize, @@ -76,24 +78,24 @@ impl crate::wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { unimplemented!("fd_allocate") } - fn fd_close(&mut self, _fd: types::Fd) -> Result<()> { + fn fd_close(&self, _fd: types::Fd) -> Result<()> { unimplemented!("fd_close") } - fn fd_datasync(&mut self, _fd: types::Fd) -> Result<()> { + fn fd_datasync(&self, _fd: types::Fd) -> Result<()> { unimplemented!("fd_datasync") } - fn fd_fdstat_get(&mut self, _fd: types::Fd) -> Result { + fn fd_fdstat_get(&self, _fd: types::Fd) -> Result { unimplemented!("fd_fdstat_get") } - fn fd_fdstat_set_flags(&mut self, _fd: types::Fd, _flags: types::Fdflags) -> Result<()> { + fn fd_fdstat_set_flags(&self, _fd: types::Fd, _flags: types::Fdflags) -> Result<()> { unimplemented!("fd_fdstat_set_flags") } fn fd_fdstat_set_rights( - &mut self, + &self, _fd: types::Fd, _fs_rights_base: types::Rights, _fs_rights_inherting: types::Rights, @@ -101,16 +103,16 @@ impl crate::wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { unimplemented!("fd_fdstat_set_rights") } - fn fd_filestat_get(&mut self, _fd: types::Fd) -> Result { + fn fd_filestat_get(&self, _fd: types::Fd) -> Result { unimplemented!("fd_filestat_get") } - fn fd_filestat_set_size(&mut self, _fd: types::Fd, _size: types::Filesize) -> Result<()> { + fn fd_filestat_set_size(&self, _fd: types::Fd, _size: types::Filesize) -> Result<()> { unimplemented!("fd_filestat_set_size") } fn fd_filestat_set_times( - &mut self, + &self, _fd: types::Fd, _atim: types::Timestamp, _mtim: types::Timestamp, @@ -120,7 +122,7 @@ impl crate::wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { } fn fd_pread( - &mut self, + &self, _fd: types::Fd, _iovs: &types::IovecArray<'_>, _offset: types::Filesize, @@ -128,12 +130,12 @@ impl crate::wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { unimplemented!("fd_pread") } - fn fd_prestat_get(&mut self, _fd: types::Fd) -> Result { + fn fd_prestat_get(&self, _fd: types::Fd) -> Result { unimplemented!("fd_prestat_get") } fn fd_prestat_dir_name( - &mut self, + &self, _fd: types::Fd, _path: GuestPtrMut, _path_len: types::Size, @@ -142,7 +144,7 @@ impl crate::wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { } fn fd_pwrite( - &mut self, + &self, _fd: types::Fd, _ciovs: &types::CiovecArray<'_>, _offset: types::Filesize, @@ -150,12 +152,12 @@ impl crate::wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { unimplemented!("fd_pwrite") } - fn fd_read(&mut self, _fd: types::Fd, _iovs: &types::IovecArray<'_>) -> Result { + fn fd_read(&self, _fd: types::Fd, _iovs: &types::IovecArray<'_>) -> Result { unimplemented!("fd_read") } fn fd_readdir( - &mut self, + &self, _fd: types::Fd, _buf: GuestPtrMut, _buf_len: types::Size, @@ -164,12 +166,12 @@ impl crate::wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { unimplemented!("fd_readdir") } - fn fd_renumber(&mut self, _fd: types::Fd, _to: types::Fd) -> Result<()> { + fn fd_renumber(&self, _fd: types::Fd, _to: types::Fd) -> Result<()> { unimplemented!("fd_renumber") } fn fd_seek( - &mut self, + &self, _fd: types::Fd, _offset: types::Filedelta, _whence: types::Whence, @@ -177,24 +179,24 @@ impl crate::wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { unimplemented!("fd_seek") } - fn fd_sync(&mut self, _fd: types::Fd) -> Result<()> { + fn fd_sync(&self, _fd: types::Fd) -> Result<()> { unimplemented!("fd_sync") } - fn fd_tell(&mut self, _fd: types::Fd) -> Result { + fn fd_tell(&self, _fd: types::Fd) -> Result { unimplemented!("fd_tell") } - fn fd_write(&mut self, _fd: types::Fd, _ciovs: &types::CiovecArray<'_>) -> Result { + fn fd_write(&self, _fd: types::Fd, _ciovs: &types::CiovecArray<'_>) -> Result { unimplemented!("fd_write") } - fn path_create_directory(&mut self, _fd: types::Fd, _path: &GuestString<'_>) -> Result<()> { + fn path_create_directory(&self, _fd: types::Fd, _path: &GuestString<'_>) -> Result<()> { unimplemented!("path_create_directory") } fn path_filestat_get( - &mut self, + &self, _fd: types::Fd, _flags: types::Lookupflags, _path: &GuestString<'_>, @@ -203,7 +205,7 @@ impl crate::wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { } fn path_filestat_set_times( - &mut self, + &self, _fd: types::Fd, _flags: types::Lookupflags, _path: &GuestString<'_>, @@ -215,7 +217,7 @@ impl crate::wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { } fn path_link( - &mut self, + &self, _old_fd: types::Fd, _old_flags: types::Lookupflags, _old_path: &GuestString<'_>, @@ -226,7 +228,7 @@ impl crate::wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { } fn path_open( - &mut self, + &self, _fd: types::Fd, _dirflags: types::Lookupflags, _path: &GuestString<'_>, @@ -239,7 +241,7 @@ impl crate::wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { } fn path_readlink( - &mut self, + &self, _fd: types::Fd, _path: &GuestString<'_>, _buf: GuestPtrMut, @@ -248,12 +250,12 @@ impl crate::wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { unimplemented!("path_readlink") } - fn path_remove_directory(&mut self, _fd: types::Fd, _path: &GuestString<'_>) -> Result<()> { + fn path_remove_directory(&self, _fd: types::Fd, _path: &GuestString<'_>) -> Result<()> { unimplemented!("path_remove_directory") } fn path_rename( - &mut self, + &self, _fd: types::Fd, _old_path: &GuestString<'_>, _new_fd: types::Fd, @@ -263,7 +265,7 @@ impl crate::wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { } fn path_symlink( - &mut self, + &self, _old_path: &GuestString<'_>, _fd: types::Fd, _new_path: &GuestString<'_>, @@ -271,12 +273,12 @@ impl crate::wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { unimplemented!("path_symlink") } - fn path_unlink_file(&mut self, _fd: types::Fd, _path: &GuestString<'_>) -> Result<()> { + fn path_unlink_file(&self, _fd: types::Fd, _path: &GuestString<'_>) -> Result<()> { unimplemented!("path_unlink_file") } fn poll_oneoff( - &mut self, + &self, _in_: GuestPtr, _out: GuestPtrMut, _nsubscriptions: types::Size, @@ -284,24 +286,24 @@ impl crate::wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { unimplemented!("poll_oneoff") } - fn proc_exit(&mut self, _rval: types::Exitcode) -> std::result::Result<(), ()> { + fn proc_exit(&self, _rval: types::Exitcode) -> std::result::Result<(), ()> { unimplemented!("proc_exit") } - fn proc_raise(&mut self, _sig: types::Signal) -> Result<()> { + fn proc_raise(&self, _sig: types::Signal) -> Result<()> { unimplemented!("proc_raise") } - fn sched_yield(&mut self) -> Result<()> { + fn sched_yield(&self) -> Result<()> { unimplemented!("sched_yield") } - fn random_get(&mut self, _buf: GuestPtrMut, _buf_len: types::Size) -> Result<()> { + fn random_get(&self, _buf: GuestPtrMut, _buf_len: types::Size) -> Result<()> { unimplemented!("random_get") } fn sock_recv( - &mut self, + &self, _fd: types::Fd, _ri_data: &types::IovecArray<'_>, _ri_flags: types::Riflags, @@ -310,7 +312,7 @@ impl crate::wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { } fn sock_send( - &mut self, + &self, _fd: types::Fd, _si_data: &types::CiovecArray<'_>, _si_flags: types::Siflags, @@ -318,7 +320,7 @@ impl crate::wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { unimplemented!("sock_send") } - fn sock_shutdown(&mut self, _fd: types::Fd, _how: types::Sdflags) -> Result<()> { + fn sock_shutdown(&self, _fd: types::Fd, _how: types::Sdflags) -> Result<()> { unimplemented!("sock_shutdown") } } From ca9f33b6d9ea075e03074f31d7a22cd53f21be54 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Wed, 4 Mar 2020 10:21:34 -0800 Subject: [PATCH 78/86] Rewrite for recursive safety This commit rewrites the runtime crate to provide safety in the face of recursive calls to the guest. The basic principle is that `GuestMemory` is now a trait which dynamically returns the pointer/length pair. This also has an implicit contract (hence the `unsafe` trait) that the pointer/length pair point to a valid list of bytes in host memory "until something is reentrant". After this changes the various suite of `Guest*` types were rewritten. `GuestRef` and `GuestRefMut` were both removed since they cannot safely exist. The `GuestPtrMut` type was removed for simplicity, and the final `GuestPtr` type subsumes `GuestString` and `GuestArray`. This means that there's only one guest pointer type, `GuestPtr<'a, T>`, where `'a` is the borrow into host memory, basically borrowing the `GuestMemory` trait object itself. Some core utilities are exposed on `GuestPtr`, but they're all 100% safe. Unsafety is now entirely contained within a few small locations: * Implementations of the `GuestType` for primitive types (e.g. `i8`, `u8`, etc) use `unsafe` to read/write memory. The `unsafe` trait of `GuestMemory` though should prove that they're safe. * `GuestPtr<'_, str>` has a method which validates utf-8 contents, and this requires `unsafe` internally to read all the bytes. This is guaranteed to be safe however given the contract of `GuestMemory`. And that's it! Everything else is a bunch of safe combinators all built up on the various utilities provided by `GuestPtr`. The general idioms are roughly the same as before, with various tweaks here and there. A summary of expected idioms are: * For small values you'd `.read()` or `.write()` very quickly. You'd pass around the type itself. * For strings, you'd pass `GuestPtr<'_, str>` down to the point where it's actually consumed. At that moment you'd either decide to copy it out (a safe operation) or you'd get a raw view to the string (an unsafe operation) and assert that you won't call back into wasm while you're holding that pointer. * Arrays are similar to strings, passing around `GuestPtr<'_, [T]>`. Arrays also have a `iter()` method which yields an iterator of `GuestPtr<'_, T>` for convenience. Overall there's still a lot of missing documentation on the runtime crate specifically around the safety of the `GuestMemory` trait as well as how the utilities/methods are expected to be used. Additionally there's utilities which aren't currently implemented which would be easy to implement. For example there's no method to copy out a string or a slice, although that would be pretty easy to add. In any case I'm curious to get feedback on this approach and see what y'all think! --- crates/generate/src/funcs.rs | 112 +----- crates/generate/src/module_trait.rs | 7 +- crates/generate/src/names.rs | 8 +- crates/generate/src/types/enum.rs | 34 +- crates/generate/src/types/flags.rs | 33 +- crates/generate/src/types/handle.rs | 23 +- crates/generate/src/types/int.rs | 30 +- crates/generate/src/types/mod.rs | 4 +- crates/generate/src/types/struct.rs | 170 ++------- crates/generate/src/types/union.rs | 174 +++------- crates/runtime/src/borrow.rs | 67 ---- crates/runtime/src/guest_type.rs | 122 ++++--- crates/runtime/src/lib.rs | 220 +++++++++++- crates/runtime/src/memory/array.rs | 260 -------------- crates/runtime/src/memory/mod.rs | 72 ---- crates/runtime/src/memory/ptr.rs | 515 ---------------------------- crates/runtime/src/memory/string.rs | 124 ------- crates/test/src/lib.rs | 18 +- tests/arrays.rs | 147 +++----- tests/atoms.rs | 34 +- tests/flags.rs | 27 +- tests/handles.rs | 20 +- tests/ints.rs | 16 +- tests/pointers.rs | 92 +++-- tests/strings.rs | 70 ++-- tests/structs.rs | 201 +++++------ tests/union.rs | 118 +++---- tests/wasi.rs | 46 +-- 28 files changed, 751 insertions(+), 2013 deletions(-) delete mode 100644 crates/runtime/src/borrow.rs delete mode 100644 crates/runtime/src/memory/array.rs delete mode 100644 crates/runtime/src/memory/mod.rs delete mode 100644 crates/runtime/src/memory/ptr.rs delete mode 100644 crates/runtime/src/memory/string.rs diff --git a/crates/generate/src/funcs.rs b/crates/generate/src/funcs.rs index eb3a02ac19..1902a9a6ff 100644 --- a/crates/generate/src/funcs.rs +++ b/crates/generate/src/funcs.rs @@ -1,7 +1,7 @@ use proc_macro2::TokenStream; use quote::quote; -use crate::lifetimes::{anon_lifetime, LifetimeExt}; +use crate::lifetimes::anon_lifetime; use crate::names::Names; pub fn define_func(names: &Names, func: &witx::InterfaceFunc) -> TokenStream { @@ -30,7 +30,7 @@ pub fn define_func(names: &Names, func: &witx::InterfaceFunc) -> TokenStream { }); let abi_args = quote!( - ctx: &mut #ctx_type, memory: &mut wiggle_runtime::GuestMemory, + ctx: &#ctx_type, memory: &dyn wiggle_runtime::GuestMemory, #(#params),* ); let abi_ret = if let Some(ret) = &coretype.ret { @@ -158,13 +158,8 @@ fn marshal_arg( let arg_name = names.func_ptr_binding(¶m.name); let name = names.func_param(¶m.name); quote! { - let #name = match memory.ptr::<#pointee_type>(#arg_name as u32) { - Ok(p) => match p.read() { - Ok(r) => r, - Err(e) => { - #error_handling - } - }, + let #name = match wiggle_runtime::GuestPtr::<#pointee_type>::new(memory, #arg_name as u32).read() { + Ok(r) => r, Err(e) => { #error_handling } @@ -209,102 +204,25 @@ fn marshal_arg( let len_name = names.func_len_binding(¶m.name); let name = names.func_param(¶m.name); quote! { - let num_elems = match memory.ptr::(#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::(#ptr_name as u32) { - Ok(p) => match p.array(*num_elems) { - Ok(s) => s.into(), - Err(e) => { - #error_handling - } - } - Err(e) => { - #error_handling - } - }; + let #name = wiggle_runtime::GuestPtr::<#lifetime, str>::new(memory, (#ptr_name as u32, #len_name as u32)); } } }, - witx::Type::Pointer(pointee) => { + witx::Type::Pointer(pointee) | witx::Type::ConstPointer(pointee) => { let pointee_type = names.type_ref(pointee, anon_lifetime()); let name = names.func_param(¶m.name); quote! { - let #name = match memory.ptr_mut::<#pointee_type>(#name as u32) { - Ok(p) => p, - Err(e) => { - #error_handling - } - }; + let #name = wiggle_runtime::GuestPtr::<#pointee_type>::new(memory, #name as u32); } } - witx::Type::ConstPointer(pointee) => { - let pointee_type = names.type_ref(pointee, anon_lifetime()); - let name = names.func_param(¶m.name); - quote! { - let #name = match memory.ptr::<#pointee_type>(#name as u32) { - Ok(p) => p, - Err(e) => { - #error_handling - } - }; - } - } - witx::Type::Struct(s) if !s.needs_lifetime() => { - let pointee_type = names.type_ref(tref, anon_lifetime()); - let arg_name = names.func_ptr_binding(¶m.name); - let name = names.func_param(¶m.name); - quote! { - let #name = match memory.ptr::<#pointee_type>(#arg_name as u32) { - Ok(p) => match p.as_ref() { - Ok(r) => r, - Err(e) => { - #error_handling - } - }, - Err(e) => { - #error_handling - } - }; - } - } - witx::Type::Struct(s) if s.needs_lifetime() => read_conversion, + witx::Type::Struct(_) => read_conversion, witx::Type::Array(arr) => { let pointee_type = names.type_ref(arr, anon_lifetime()); let ptr_name = names.func_ptr_binding(¶m.name); let len_name = names.func_len_binding(¶m.name); let name = names.func_param(¶m.name); quote! { - let num_elems = match memory.ptr::(#len_name as u32) { - Ok(p) => match p.as_ref() { - Ok(r) => r, - Err(e) => { - #error_handling - } - } - Err(e) => { - #error_handling - } - }; - let #name = match memory.ptr::<#pointee_type>(#ptr_name as u32) { - Ok(p) => match p.array(*num_elems) { - Ok(s) => s, - Err(e) => { - #error_handling - } - } - Err(e) => { - #error_handling - } - }; + let #name = wiggle_runtime::GuestPtr::<[#pointee_type]>::new(memory, (#ptr_name as u32, #len_name as u32)); } } witx::Type::Union(_u) => read_conversion, @@ -313,7 +231,6 @@ fn marshal_arg( let handle_type = names.type_ref(tref, anon_lifetime()); quote!( let #name = #handle_type::from(#name); ) } - _ => unimplemented!("argument type marshalling"), } } @@ -333,17 +250,14 @@ where let ptr_name = names.func_ptr_binding(&result.name); let ptr_err_handling = error_handling(&format!("{}:result_ptr_mut", result.name.as_str())); let pre = quote! { - let mut #ptr_name = match memory.ptr_mut::<#pointee_type>(#ptr_name as u32) { - Ok(p) => p, - Err(e) => { - #ptr_err_handling - } - }; + let #ptr_name = wiggle_runtime::GuestPtr::<#pointee_type>::new(memory, #ptr_name as u32); }; // trait binding returns func_param name. let val_name = names.func_param(&result.name); let post = quote! { - #ptr_name.write(&#val_name); + if let Err(e) = #ptr_name.write(#val_name) { + #ptr_err_handling + } }; (pre, post) }; diff --git a/crates/generate/src/module_trait.rs b/crates/generate/src/module_trait.rs index 96515499f0..bd94946bba 100644 --- a/crates/generate/src/module_trait.rs +++ b/crates/generate/src/module_trait.rs @@ -11,7 +11,12 @@ pub fn define_module_trait(names: &Names, m: &Module) -> TokenStream { // Check if we're returning an entity anotated with a lifetime, // in which case, we'll need to annotate the function itself, and // hence will need an explicit lifetime (rather than anonymous) - let (lifetime, is_anonymous) = if f.results.iter().any(|ret| ret.tref.needs_lifetime()) { + let (lifetime, is_anonymous) = if f + .params + .iter() + .chain(&f.results) + .any(|ret| ret.tref.needs_lifetime()) + { (quote!('a), false) } else { (anon_lifetime(), true) diff --git a/crates/generate/src/names.rs b/crates/generate/src/names.rs index 1167d25a60..e66b6f8d81 100644 --- a/crates/generate/src/names.rs +++ b/crates/generate/src/names.rs @@ -24,7 +24,7 @@ impl Names { } pub fn builtin_type(&self, b: BuiltinType, lifetime: TokenStream) -> TokenStream { match b { - BuiltinType::String => quote!(wiggle_runtime::GuestString<#lifetime>), + BuiltinType::String => quote!(wiggle_runtime::GuestPtr<#lifetime, str>), BuiltinType::U8 => quote!(u8), BuiltinType::U16 => quote!(u16), BuiltinType::U32 => quote!(u32), @@ -60,11 +60,7 @@ impl Names { } TypeRef::Value(ty) => match &**ty { 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>) - } - witx::Type::ConstPointer(pointee) => { + witx::Type::Pointer(pointee) | witx::Type::ConstPointer(pointee) => { let pointee_type = self.type_ref(&pointee, lifetime.clone()); quote!(wiggle_runtime::GuestPtr<#lifetime, #pointee_type>) } diff --git a/crates/generate/src/types/enum.rs b/crates/generate/src/types/enum.rs index e40d6b50de..ec9ab021b0 100644 --- a/crates/generate/src/types/enum.rs +++ b/crates/generate/src/types/enum.rs @@ -77,37 +77,25 @@ pub(super) fn define_enum(names: &Names, name: &witx::Id, e: &witx::EnumDatatype } impl<'a> wiggle_runtime::GuestType<'a> for #ident { - fn size() -> u32 { - ::std::mem::size_of::<#repr>() as u32 + fn guest_size() -> u32 { + #repr::guest_size() } - fn align() -> u32 { - ::std::mem::align_of::<#repr>() as u32 - } - - fn name() -> String { - stringify!(#ident).to_owned() - } - - fn validate(location: &wiggle_runtime::GuestPtr<'a, #ident>) -> Result<(), wiggle_runtime::GuestError> { - use ::std::convert::TryFrom; - let raw: #repr = unsafe { (location.as_raw() as *const #repr).read() }; - let _ = #ident::try_from(raw)?; - Ok(()) + fn guest_align() -> usize { + #repr::guest_align() } fn read(location: &wiggle_runtime::GuestPtr<#ident>) -> Result<#ident, wiggle_runtime::GuestError> { - // Perform validation as part of as_ref: - let r = location.as_ref()?; - Ok(*r) + use std::convert::TryFrom; + let val = #repr::read(&location.cast())?; + #ident::try_from(val) } - fn write(&self, location: &wiggle_runtime::GuestPtrMut<#ident>) { - let val: #repr = #repr::from(*self); - unsafe { (location.as_raw() as *mut #repr).write(val) }; + fn write(location: &wiggle_runtime::GuestPtr<'_, #ident>, val: Self) + -> Result<(), wiggle_runtime::GuestError> + { + #repr::write(&location.cast(), #repr::from(val)) } } - - impl<'a> wiggle_runtime::GuestTypeTransparent<'a> for #ident {} } } diff --git a/crates/generate/src/types/flags.rs b/crates/generate/src/types/flags.rs index 67e380ff77..201e0a4529 100644 --- a/crates/generate/src/types/flags.rs +++ b/crates/generate/src/types/flags.rs @@ -126,35 +126,24 @@ pub(super) fn define_flags(names: &Names, name: &witx::Id, f: &witx::FlagsDataty } impl<'a> wiggle_runtime::GuestType<'a> for #ident { - fn size() -> u32 { - ::std::mem::size_of::<#repr>() as u32 + fn guest_size() -> u32 { + #repr::guest_size() } - fn align() -> u32 { - ::std::mem::align_of::<#repr>() as u32 + fn guest_align() -> usize { + #repr::guest_align() } - fn name() -> String { - stringify!(#ident).to_owned() + fn read(location: &wiggle_runtime::GuestPtr<'a, #ident>) -> Result<#ident, wiggle_runtime::GuestError> { + use std::convert::TryFrom; + let bits = #repr::read(&location.cast())?; + #ident::try_from(bits) } - fn validate(location: &wiggle_runtime::GuestPtr<'a, #ident>) -> Result<(), wiggle_runtime::GuestError> { - use ::std::convert::TryFrom; - let raw: #repr = unsafe { (location.as_raw() as *const #repr).read() }; - let _ = #ident::try_from(raw)?; - Ok(()) - } - - fn read(location: &wiggle_runtime::GuestPtr<#ident>) -> Result<#ident, wiggle_runtime::GuestError> { - Ok(*location.as_ref()?) - } - - fn write(&self, location: &wiggle_runtime::GuestPtrMut<#ident>) { - let val: #repr = #repr::from(*self); - unsafe { (location.as_raw() as *mut #repr).write(val) }; + fn write(location: &wiggle_runtime::GuestPtr<'_, #ident>, val: Self) -> Result<(), wiggle_runtime::GuestError> { + let val: #repr = #repr::from(val); + #repr::write(&location.cast(), val) } } - - impl<'a> wiggle_runtime::GuestTypeTransparent<'a> for #ident {} } } diff --git a/crates/generate/src/types/handle.rs b/crates/generate/src/types/handle.rs index ab83593508..294e36a028 100644 --- a/crates/generate/src/types/handle.rs +++ b/crates/generate/src/types/handle.rs @@ -11,7 +11,7 @@ pub(super) fn define_handle( ) -> TokenStream { let ident = names.type_(name); let size = h.mem_size_align().size as u32; - let align = h.mem_size_align().align as u32; + let align = h.mem_size_align().align as usize; quote! { #[derive(Copy, Clone, Debug, ::std::hash::Hash, Eq, PartialEq)] pub struct #ident(u32); @@ -46,32 +46,21 @@ pub(super) fn define_handle( } impl<'a> wiggle_runtime::GuestType<'a> for #ident { - fn size() -> u32 { + fn guest_size() -> u32 { #size } - fn align() -> u32 { + fn guest_align() -> usize { #align } - fn name() -> String { - stringify!(#ident).to_owned() - } - - fn validate(ptr: &wiggle_runtime::GuestPtr<#ident>) -> Result<(), wiggle_runtime::GuestError> { - Ok(()) - } - fn read(location: &wiggle_runtime::GuestPtr<'a, #ident>) -> Result<#ident, wiggle_runtime::GuestError> { - let r = location.as_ref()?; - Ok(*r) + Ok(#ident(u32::read(&location.cast())?)) } - fn write(&self, location: &wiggle_runtime::GuestPtrMut<'a, Self>) { - unsafe { (location.as_raw() as *mut #ident).write(*self) }; + fn write(location: &wiggle_runtime::GuestPtr<'_, Self>, val: Self) -> Result<(), wiggle_runtime::GuestError> { + u32::write(&location.cast(), val.0) } } - - impl<'a> wiggle_runtime::GuestTypeTransparent<'a> for #ident {} } } diff --git a/crates/generate/src/types/int.rs b/crates/generate/src/types/int.rs index bb2f40afee..25375700b9 100644 --- a/crates/generate/src/types/int.rs +++ b/crates/generate/src/types/int.rs @@ -63,35 +63,21 @@ pub(super) fn define_int(names: &Names, name: &witx::Id, i: &witx::IntDatatype) } impl<'a> wiggle_runtime::GuestType<'a> for #ident { - fn size() -> u32 { - ::std::mem::size_of::<#repr>() as u32 + fn guest_size() -> u32 { + #repr::guest_size() } - fn align() -> u32 { - ::std::mem::align_of::<#repr>() as u32 + fn guest_align() -> usize { + #repr::guest_align() } - fn name() -> String { - stringify!(#ident).to_owned() + fn read(location: &wiggle_runtime::GuestPtr<'a, #ident>) -> Result<#ident, wiggle_runtime::GuestError> { + Ok(#ident(#repr::read(&location.cast())?)) } - fn validate(location: &wiggle_runtime::GuestPtr<'a, #ident>) -> Result<(), wiggle_runtime::GuestError> { - use ::std::convert::TryFrom; - let raw: #repr = unsafe { (location.as_raw() as *const #repr).read() }; - let _ = #ident::try_from(raw)?; - Ok(()) - } - - fn read(location: &wiggle_runtime::GuestPtr<#ident>) -> Result<#ident, wiggle_runtime::GuestError> { - Ok(*location.as_ref()?) - } - - fn write(&self, location: &wiggle_runtime::GuestPtrMut<#ident>) { - let val: #repr = #repr::from(*self); - unsafe { (location.as_raw() as *mut #repr).write(val) }; + fn write(location: &wiggle_runtime::GuestPtr<'_, #ident>, val: Self) -> Result<(), wiggle_runtime::GuestError> { + #repr::write(&location.cast(), val.0) } } - - impl<'a> wiggle_runtime::GuestTypeTransparent<'a> for #ident {} } } diff --git a/crates/generate/src/types/mod.rs b/crates/generate/src/types/mod.rs index 9d633d0c75..07c748b1d7 100644 --- a/crates/generate/src/types/mod.rs +++ b/crates/generate/src/types/mod.rs @@ -25,7 +25,7 @@ pub fn define_datatype(names: &Names, namedtype: &witx::NamedType) -> TokenStrea witx::Type::Pointer(p) => define_witx_pointer( names, &namedtype.name, - quote!(wiggle_runtime::GuestPtrMut), + quote!(wiggle_runtime::GuestPtr), p, ), witx::Type::ConstPointer(p) => { @@ -71,7 +71,7 @@ fn define_witx_pointer( fn define_witx_array(names: &Names, name: &witx::Id, arr_raw: &witx::TypeRef) -> TokenStream { let ident = names.type_(name); let pointee_type = names.type_ref(arr_raw, quote!('a)); - quote!(pub type #ident<'a> = wiggle_runtime::GuestArray<'a, #pointee_type>;) + quote!(pub type #ident<'a> = wiggle_runtime::GuestPtr<'a, [#pointee_type]>;) } fn int_repr_tokens(int_repr: witx::IntRepr) -> TokenStream { diff --git a/crates/generate/src/types/struct.rs b/crates/generate/src/types/struct.rs index 465e47279f..11a6bdf06f 100644 --- a/crates/generate/src/types/struct.rs +++ b/crates/generate/src/types/struct.rs @@ -10,87 +10,9 @@ pub(super) fn define_struct( name: &witx::Id, s: &witx::StructDatatype, ) -> TokenStream { - if !s.needs_lifetime() { - define_copy_struct(names, name, s) - } else { - define_ptr_struct(names, name, s) - } -} - -fn define_copy_struct(names: &Names, name: &witx::Id, s: &witx::StructDatatype) -> TokenStream { let ident = names.type_(name); let size = s.mem_size_align().size as u32; - let align = s.mem_size_align().align as u32; - - let member_decls = s.members.iter().map(|m| { - let name = names.struct_member(&m.name); - let type_ = names.type_ref(&m.tref, anon_lifetime()); - quote!(pub #name: #type_) - }); - let member_valids = s.member_layout().into_iter().map(|ml| { - let type_ = names.type_ref(&ml.member.tref, anon_lifetime()); - let offset = ml.offset as u32; - let fieldname = names.struct_member(&ml.member.name); - quote! { - #type_::validate( - &ptr.cast(#offset).map_err(|e| - wiggle_runtime::GuestError::InDataField{ - typename: stringify!(#ident).to_owned(), - field: stringify!(#fieldname).to_owned(), - err: Box::new(e), - })? - ).map_err(|e| - wiggle_runtime::GuestError::InDataField { - typename: stringify!(#ident).to_owned(), - field: stringify!(#fieldname).to_owned(), - err: Box::new(e), - })?; - } - }); - - quote! { - #[repr(C)] - #[derive(Copy, Clone, Debug, PartialEq)] - pub struct #ident { - #(#member_decls),* - } - - impl<'a> wiggle_runtime::GuestType<'a> for #ident { - fn size() -> u32 { - #size - } - - fn align() -> u32 { - #align - } - - fn name() -> String { - stringify!(#ident).to_owned() - } - - fn validate(ptr: &wiggle_runtime::GuestPtr<#ident>) -> Result<(), wiggle_runtime::GuestError> { - #(#member_valids)* - Ok(()) - } - - fn read(location: &wiggle_runtime::GuestPtr<'a, #ident>) -> Result<#ident, wiggle_runtime::GuestError> { - let r = location.as_ref()?; - Ok(*r) - } - - fn write(&self, location: &wiggle_runtime::GuestPtrMut<'a, Self>) { - unsafe { (location.as_raw() as *mut #ident).write(*self) }; - } - } - - impl<'a> wiggle_runtime::GuestTypeTransparent<'a> for #ident {} - } -} - -fn define_ptr_struct(names: &Names, name: &witx::Id, s: &witx::StructDatatype) -> TokenStream { - let ident = names.type_(name); - let size = s.mem_size_align().size as u32; - let align = s.mem_size_align().align as u32; + let align = s.mem_size_align().align as usize; let member_names = s.members.iter().map(|m| names.struct_member(&m.name)); let member_decls = s.members.iter().map(|m| { @@ -99,11 +21,7 @@ fn define_ptr_struct(names: &Names, name: &witx::Id, s: &witx::StructDatatype) - witx::TypeRef::Name(nt) => names.type_(&nt.name), witx::TypeRef::Value(ty) => match &**ty { 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>) - } - witx::Type::ConstPointer(pointee) => { + witx::Type::Pointer(pointee) | witx::Type::ConstPointer(pointee) => { let pointee_type = names.type_ref(&pointee, quote!('a)); quote!(wiggle_runtime::GuestPtr<'a, #pointee_type>) } @@ -112,68 +30,29 @@ fn define_ptr_struct(names: &Names, name: &witx::Id, s: &witx::StructDatatype) - }; quote!(pub #name: #type_) }); - let member_valids = s.member_layout().into_iter().map(|ml| { - 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, quote!('a)), - witx::Type::Pointer(pointee) => { - let pointee_type = names.type_ref(&pointee, anon_lifetime()); - quote!(wiggle_runtime::GuestPtrMut::<#pointee_type>) - } - witx::Type::ConstPointer(pointee) => { - let pointee_type = names.type_ref(&pointee, anon_lifetime()); - quote!(wiggle_runtime::GuestPtr::<#pointee_type>) - } - _ => unimplemented!("other anonymous struct members"), - }, - }; - let offset = ml.offset as u32; - let fieldname = names.struct_member(&ml.member.name); - quote! { - #type_::validate( - &ptr.cast(#offset).map_err(|e| - wiggle_runtime::GuestError::InDataField{ - typename: stringify!(#ident).to_owned(), - field: stringify!(#fieldname).to_owned(), - err: Box::new(e), - })? - ).map_err(|e| - wiggle_runtime::GuestError::InDataField { - typename: stringify!(#ident).to_owned(), - field: stringify!(#fieldname).to_owned(), - err: Box::new(e), - })?; - } - }); let member_reads = s.member_layout().into_iter().map(|ml| { let name = names.struct_member(&ml.member.name); let offset = ml.offset as u32; + let location = quote!(location.cast::().add(#offset)?.cast()); match &ml.member.tref { witx::TypeRef::Name(nt) => { let type_ = names.type_(&nt.name); quote! { - let #name = <#type_ as wiggle_runtime::GuestType>::read(&location.cast(#offset)?)?; + let #name = <#type_ as wiggle_runtime::GuestType>::read(&#location)?; } } witx::TypeRef::Value(ty) => match &**ty { witx::Type::Builtin(builtin) => { let type_ = names.builtin_type(*builtin, anon_lifetime()); quote! { - let #name = <#type_ as wiggle_runtime::GuestType>::read(&location.cast(#offset)?)?; + let #name = <#type_ as wiggle_runtime::GuestType>::read(&#location)?; } } - witx::Type::Pointer(pointee) => { + witx::Type::Pointer(pointee) | witx::Type::ConstPointer(pointee) => { let pointee_type = names.type_ref(&pointee, anon_lifetime()); quote! { - let #name = as wiggle_runtime::GuestType>::read(&location.cast(#offset)?)?; - } - } - witx::Type::ConstPointer(pointee) => { - let pointee_type = names.type_ref(&pointee, anon_lifetime()); - quote! { - let #name = as wiggle_runtime::GuestType>::read(&location.cast(#offset)?)?; + let #name = as wiggle_runtime::GuestType>::read(&#location)?; } } _ => unimplemented!("other anonymous struct members"), @@ -185,41 +64,42 @@ fn define_ptr_struct(names: &Names, name: &witx::Id, s: &witx::StructDatatype) - let name = names.struct_member(&ml.member.name); let offset = ml.offset as u32; quote! { - wiggle_runtime::GuestType::write(&self.#name, &location.cast(#offset).expect("cast to inner member")); + wiggle_runtime::GuestType::write( + &location.cast::().add(#offset)?.cast(), + val.#name, + )?; } }); + let (struct_lifetime, extra_derive) = if s.needs_lifetime() { + (quote!(<'a>), quote!()) + } else { + (quote!(), quote!(, Copy, PartialEq)) + }; + quote! { - #[derive(Clone, Debug)] - pub struct #ident<'a> { + #[derive(Clone, Debug #extra_derive)] + pub struct #ident #struct_lifetime { #(#member_decls),* } - impl<'a> wiggle_runtime::GuestType<'a> for #ident<'a> { - fn size() -> u32 { + impl<'a> wiggle_runtime::GuestType<'a> for #ident #struct_lifetime { + fn guest_size() -> u32 { #size } - fn align() -> u32 { + fn guest_align() -> usize { #align } - fn name() -> String { - stringify!(#ident).to_owned() - } - - fn validate(ptr: &wiggle_runtime::GuestPtr<'a, #ident<'a>>) -> Result<(), wiggle_runtime::GuestError> { - #(#member_valids)* - Ok(()) - } - - fn read(location: &wiggle_runtime::GuestPtr<'a, #ident<'a>>) -> Result<#ident<'a>, wiggle_runtime::GuestError> { + fn read(location: &wiggle_runtime::GuestPtr<'a, Self>) -> Result { #(#member_reads)* Ok(#ident { #(#member_names),* }) } - fn write(&self, location: &wiggle_runtime::GuestPtrMut<'a, Self>) { + fn write(location: &wiggle_runtime::GuestPtr<'_, Self>, val: Self) -> Result<(), wiggle_runtime::GuestError> { #(#member_writes)* + Ok(()) } } } diff --git a/crates/generate/src/types/union.rs b/crates/generate/src/types/union.rs index 4a9f6bff9a..4490690050 100644 --- a/crates/generate/src/types/union.rs +++ b/crates/generate/src/types/union.rs @@ -1,4 +1,4 @@ -use crate::lifetimes::{anon_lifetime, LifetimeExt}; +use crate::lifetimes::LifetimeExt; use crate::names::Names; use proc_macro2::TokenStream; @@ -8,7 +8,7 @@ use witx::Layout; pub(super) fn define_union(names: &Names, name: &witx::Id, u: &witx::UnionDatatype) -> TokenStream { let ident = names.type_(name); let size = u.mem_size_align().size as u32; - let align = u.mem_size_align().align as u32; + let align = u.mem_size_align().align as usize; let ulayout = u.union_layout(); let contents_offset = ulayout.contents_offset as u32; @@ -32,8 +32,8 @@ pub(super) fn define_union(names: &Names, name: &witx::Id, u: &witx::UnionDataty let varianttype = names.type_ref(tref, lifetime.clone()); quote! { #tagname::#variantname => { - let variant_ptr = location.cast::<#varianttype>(#contents_offset).expect("union variant ptr validated"); - let variant_val = <#varianttype as wiggle_runtime::GuestType>::read(&variant_ptr)?; + let variant_ptr = location.cast::().add(#contents_offset)?; + let variant_val = <#varianttype as wiggle_runtime::GuestType>::read(&variant_ptr.cast())?; Ok(#ident::#variantname(variant_val)) } } @@ -45,17 +45,15 @@ pub(super) fn define_union(names: &Names, name: &witx::Id, u: &witx::UnionDataty let write_variant = u.variants.iter().map(|v| { let variantname = names.enum_variant(&v.name); let write_tag = quote! { - let tag_ptr = location.cast::<#tagname>(0).expect("union tag ptr TODO error report"); - let mut tag_ref = tag_ptr.as_ref_mut().expect("union tag ref TODO error report"); - *tag_ref = #tagname::#variantname; + location.cast().write(#tagname::#variantname)?; }; if let Some(tref) = &v.tref { let varianttype = names.type_ref(tref, lifetime.clone()); quote! { #ident::#variantname(contents) => { #write_tag - let variant_ptr = location.cast::<#varianttype>(#contents_offset).expect("union variant ptr validated"); - <#varianttype as wiggle_runtime::GuestType>::write(&contents, &variant_ptr); + let variant_ptr = location.cast::().add(#contents_offset)?; + <#varianttype as wiggle_runtime::GuestType>::write(&variant_ptr.cast(), contents)?; } } } else { @@ -66,134 +64,46 @@ pub(super) fn define_union(names: &Names, name: &witx::Id, u: &witx::UnionDataty } } }); - let validate = union_validate(names, ident.clone(), u, &ulayout); - if !u.needs_lifetime() { - // Type does not have a lifetime parameter: - quote! { - #[derive(Copy, Clone, Debug, PartialEq)] - pub enum #ident { - #(#variants),* - } - - impl<'a> wiggle_runtime::GuestType<'a> for #ident { - fn size() -> u32 { - #size - } - - fn align() -> u32 { - #align - } - - fn name() -> String { - stringify!(#ident).to_owned() - } - - fn validate(ptr: &wiggle_runtime::GuestPtr<'a, #ident>) -> Result<(), wiggle_runtime::GuestError> { - #validate - } - - fn read(location: &wiggle_runtime::GuestPtr<'a, #ident>) - -> Result { - <#ident as wiggle_runtime::GuestType>::validate(location)?; - let tag = *location.cast::<#tagname>(0).expect("validated tag ptr").as_ref().expect("validated tag ref"); - match tag { - #(#read_variant)* - } - - } - - fn write(&self, location: &wiggle_runtime::GuestPtrMut<'a, #ident>) { - match self { - #(#write_variant)* - } - } - } - } + let (enum_lifetime, extra_derive) = if u.needs_lifetime() { + (quote!(<'a>), quote!()) } else { - quote! { - #[derive(Clone, Debug)] - pub enum #ident<#lifetime> { - #(#variants),* - } - - impl<#lifetime> wiggle_runtime::GuestType<#lifetime> for #ident<#lifetime> { - fn size() -> u32 { - #size - } - - fn align() -> u32 { - #align - } - - fn name() -> String { - stringify!(#ident).to_owned() - } - - fn validate(ptr: &wiggle_runtime::GuestPtr<#lifetime, #ident<#lifetime>>) -> Result<(), wiggle_runtime::GuestError> { - #validate - } - - fn read(location: &wiggle_runtime::GuestPtr<#lifetime, #ident<#lifetime>>) - -> Result { - <#ident as wiggle_runtime::GuestType>::validate(location)?; - let tag = *location.cast::<#tagname>(0).expect("validated tag ptr").as_ref().expect("validated tag ref"); - match tag { - #(#read_variant)* - } - - } - - fn write(&self, location: &wiggle_runtime::GuestPtrMut<#lifetime, #ident<#lifetime>>) { - match self { - #(#write_variant)* - } - } - } - } - } -} - -fn union_validate( - names: &Names, - typename: TokenStream, - u: &witx::UnionDatatype, - ulayout: &witx::UnionLayout, -) -> TokenStream { - let tagname = names.type_(&u.tag.name); - let contents_offset = ulayout.contents_offset as u32; - - let with_err = |f: &str| -> TokenStream { - quote!(|e| wiggle_runtime::GuestError::InDataField { - typename: stringify!(#typename).to_owned(), - field: #f.to_owned(), - err: Box::new(e), - }) + (quote!(), quote!(, Copy, PartialEq)) }; - let tag_err = with_err(""); - let variant_validation = u.variants.iter().map(|v| { - let err = with_err(v.name.as_str()); - let variantname = names.enum_variant(&v.name); - if let Some(tref) = &v.tref { - let lifetime = anon_lifetime(); - let varianttype = names.type_ref(tref, lifetime.clone()); - quote! { - #tagname::#variantname => { - let variant_ptr = ptr.cast::<#varianttype>(#contents_offset).map_err(#err)?; - <#varianttype as wiggle_runtime::GuestType>::validate(&variant_ptr).map_err(#err)?; - } - } - } else { - quote! { #tagname::#variantname => {} } - } - }); - quote! { - let tag = *ptr.cast::<#tagname>(0).map_err(#tag_err)?.as_ref().map_err(#tag_err)?; - match tag { - #(#variant_validation)* + #[derive(Clone, Debug #extra_derive)] + pub enum #ident #enum_lifetime { + #(#variants),* + } + + impl<'a> wiggle_runtime::GuestType<'a> for #ident #enum_lifetime { + fn guest_size() -> u32 { + #size + } + + fn guest_align() -> usize { + #align + } + + fn read(location: &wiggle_runtime::GuestPtr<'a, Self>) + -> Result + { + let tag = location.cast().read()?; + match tag { + #(#read_variant)* + } + + } + + fn write(location: &wiggle_runtime::GuestPtr<'_, Self>, val: Self) + -> Result<(), wiggle_runtime::GuestError> + { + match val { + #(#write_variant)* + } + Ok(()) + } } - Ok(()) } } diff --git a/crates/runtime/src/borrow.rs b/crates/runtime/src/borrow.rs deleted file mode 100644 index bd1d077e17..0000000000 --- a/crates/runtime/src/borrow.rs +++ /dev/null @@ -1,67 +0,0 @@ -use std::collections::HashMap; - -use crate::region::Region; - -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] -pub struct BorrowHandle(usize); - -#[derive(Debug)] -pub struct GuestBorrows { - immutable: HashMap, - mutable: HashMap, - next_handle: BorrowHandle, -} - -impl GuestBorrows { - pub fn new() -> Self { - GuestBorrows { - immutable: HashMap::new(), - mutable: HashMap::new(), - next_handle: BorrowHandle(0), - } - } - - fn is_borrowed_immut(&self, r: Region) -> bool { - !self.immutable.values().all(|b| !b.overlaps(r)) - } - - fn is_borrowed_mut(&self, r: Region) -> bool { - !self.mutable.values().all(|b| !b.overlaps(r)) - } - - fn new_handle(&mut self) -> BorrowHandle { - let h = self.next_handle; - self.next_handle = BorrowHandle(h.0 + 1); - h - } - - pub fn borrow_immut(&mut self, r: Region) -> Option { - if self.is_borrowed_mut(r) { - return None; - } - let h = self.new_handle(); - self.immutable.insert(h, r); - Some(h) - } - - pub fn unborrow_immut(&mut self, h: BorrowHandle) { - self.immutable - .remove(&h) - .expect("handle exists in immutable borrows"); - } - - pub fn borrow_mut(&mut self, r: Region) -> Option { - if self.is_borrowed_immut(r) || self.is_borrowed_mut(r) { - return None; - } - let h = self.new_handle(); - self.mutable.insert(h, r); - Some(h) - } - - pub fn unborrow_mut(&mut self, h: BorrowHandle) { - self.mutable - .remove(&h) - .expect("handle exists in mutable borrows"); - } -} diff --git a/crates/runtime/src/guest_type.rs b/crates/runtime/src/guest_type.rs index 2d69548554..b23830d062 100644 --- a/crates/runtime/src/guest_type.rs +++ b/crates/runtime/src/guest_type.rs @@ -1,54 +1,80 @@ -use crate::{GuestError, GuestPtr, GuestPtrMut}; - -pub trait GuestType<'a>: Sized + Clone { - // These are morally the same as Rust ::std::mem::size_of / align_of, but they return - // a u32 because the wasm memory space is 32 bits. They have a different names so they - // don't collide with the std::mem methods. - fn size() -> u32; - fn align() -> u32; - fn name() -> String; - fn validate(location: &GuestPtr<'a, Self>) -> Result<(), GuestError>; - fn read(location: &GuestPtr<'a, Self>) -> Result; - fn write(&self, location: &GuestPtrMut<'a, Self>); -} - -/// Represents any guest type which can transparently be represented -/// as a host type. -pub trait GuestTypeTransparent<'a>: GuestType<'a> + Copy {} - -macro_rules! builtin_type { - ( $( $t:ident ), * ) => { - $( - impl<'a> GuestType<'a> for $t { - fn size() -> u32 { - ::std::mem::size_of::<$t>() as u32 - } - fn align() -> u32 { - ::std::mem::align_of::<$t>() as u32 - } - fn name() -> String { - ::std::stringify!($t).to_owned() - } - fn validate(_ptr: &GuestPtr<$t>) -> Result<(), GuestError> { - Ok(()) - } - fn read(location: &GuestPtr<'a, Self>) -> Result { - Ok(*location.as_ref()?) - } - fn write(&self, location: &GuestPtrMut<'a, Self>) { - unsafe { (location.as_raw() as *mut $t).write(*self) }; - } - } - impl<'a> GuestTypeTransparent<'a> for $t {} - )* - }; -} - -// These definitions correspond to all the witx BuiltinType variants that are Copy: -builtin_type!(u8, i8, u16, i16, u32, i32, u64, i64, f32, f64, usize); +use crate::{GuestError, GuestPtr}; +use std::mem; pub trait GuestErrorType { type Context; fn success() -> Self; fn from_error(e: GuestError, ctx: &Self::Context) -> Self; } + +pub trait GuestType<'a>: Sized { + fn guest_size() -> u32; + fn guest_align() -> usize; + fn read(ptr: &GuestPtr<'a, Self>) -> Result; + fn write(ptr: &GuestPtr<'_, Self>, val: Self) -> Result<(), GuestError>; +} + +macro_rules! primitives { + ($($i:ident)*) => ($( + impl<'a> GuestType<'a> for $i { + fn guest_size() -> u32 { mem::size_of::() as u32 } + fn guest_align() -> usize { mem::align_of::() } + + fn read(ptr: &GuestPtr<'a, Self>) -> Result { + + // Any bit pattern for any primitive implemented with this + // macro is safe, so our `as_raw` method will guarantee that if + // we are given a pointer it's valid for the size of our type + // as well as properly aligned. Consequently we should be able + // to safely ready the pointer just after we validated it, + // returning it along here. + let host_ptr = ptr.mem().validate_size_align( + ptr.offset(), + Self::guest_align(), + Self::guest_size(), + )?; + Ok(unsafe { *host_ptr.cast::() }) + } + + fn write(ptr: &GuestPtr<'_, Self>, val: Self) -> Result<(), GuestError> { + let host_ptr = ptr.mem().validate_size_align( + ptr.offset(), + Self::guest_align(), + Self::guest_size(), + )?; + // Similar to above `as_raw` will do a lot of validation, and + // then afterwards we can safely write our value into the + // memory location. + unsafe { + *host_ptr.cast::() = val; + } + Ok(()) + } + } + )*) +} + +primitives! { + i8 i16 i32 i64 i128 isize + u8 u16 u32 u64 u128 usize + f32 f64 +} + +// Support pointers-to-pointers where pointers are always 32-bits in wasm land +impl<'a, T> GuestType<'a> for GuestPtr<'a, T> { + fn guest_size() -> u32 { + u32::guest_size() + } + fn guest_align() -> usize { + u32::guest_align() + } + + fn read(ptr: &GuestPtr<'a, Self>) -> Result { + let offset = ptr.cast::().read()?; + Ok(GuestPtr::new(ptr.mem(), offset)) + } + + fn write(ptr: &GuestPtr<'_, Self>, val: Self) -> Result<(), GuestError> { + ptr.cast::().write(val.offset()) + } +} diff --git a/crates/runtime/src/lib.rs b/crates/runtime/src/lib.rs index 99648c4b1a..93c45fbe87 100644 --- a/crates/runtime/src/lib.rs +++ b/crates/runtime/src/lib.rs @@ -1,13 +1,217 @@ -mod borrow; +use std::cell::Cell; +use std::slice; +use std::str; +use std::marker; +use std::fmt; + mod error; mod guest_type; -mod memory; mod region; - pub use error::GuestError; -pub use guest_type::{GuestErrorType, GuestType, GuestTypeTransparent}; -pub use memory::{ - GuestArray, GuestMemory, GuestPtr, GuestPtrMut, GuestRef, GuestRefMut, GuestString, - GuestStringRef, -}; +pub use guest_type::{GuestErrorType, GuestType}; pub use region::Region; + +pub unsafe trait GuestMemory { + fn base(&self) -> (*mut u8, u32); + + fn validate_size_align( + &self, + offset: u32, + align: usize, + len: u32, + ) -> Result<*mut u8, GuestError> { + let (base_ptr, base_len) = self.base(); + let region = Region { start: offset, len }; + + // Figure out our pointer to the start of memory + let start = match (base_ptr as usize).checked_add(offset as usize) { + Some(ptr) => ptr, + None => return Err(GuestError::PtrOutOfBounds(region)), + }; + // and use that to figure out the end pointer + let end = match start.checked_add(len as usize) { + Some(ptr) => ptr, + None => return Err(GuestError::PtrOutOfBounds(region)), + }; + // and then verify that our end doesn't reach past the end of our memory + if end > (base_ptr as usize) + (base_len as usize) { + return Err(GuestError::PtrOutOfBounds(region)); + } + // and finally verify that the alignment is correct + if start % align != 0 { + return Err(GuestError::PtrNotAligned(region, align as u32)); + } + Ok(start as *mut u8) + } + + fn ptr<'a, T>(&'a self, offset: T::Pointer) -> GuestPtr<'a, T> + where + Self: Sized, + T: ?Sized + Pointee, + { + GuestPtr::new(self, offset) + } +} + +unsafe impl<'a, T: ?Sized + GuestMemory> GuestMemory for &'a T { + fn base(&self) -> (*mut u8, u32) { + T::base(self) + } +} + +unsafe impl<'a, T: ?Sized + GuestMemory> GuestMemory for &'a mut T { + fn base(&self) -> (*mut u8, u32) { + T::base(self) + } +} + +pub struct GuestPtr<'a, T: ?Sized + Pointee> { + mem: &'a (dyn GuestMemory + 'a), + pointer: T::Pointer, + _marker: marker::PhantomData<&'a Cell>, +} + +impl<'a, T: ?Sized + Pointee> GuestPtr<'a, T> { + pub fn new(mem: &'a (dyn GuestMemory + 'a), pointer: T::Pointer) -> GuestPtr<'_, T> { + GuestPtr { + mem, + pointer, + _marker: marker::PhantomData, + } + } + + pub fn offset(&self) -> T::Pointer { + self.pointer + } + + pub fn mem(&self) -> &'a (dyn GuestMemory + 'a) { + self.mem + } + + pub fn cast(&self) -> GuestPtr<'a, U> + where + T: Pointee, + { + GuestPtr::new(self.mem, self.pointer) + } + + pub fn read(&self) -> Result + where + T: GuestType<'a>, + { + T::read(self) + } + + pub fn write(&self, val: T) -> Result<(), GuestError> + where + T: GuestType<'a>, + { + T::write(self, val) + } + + pub fn add(&self, amt: u32) -> Result, GuestError> + where T: GuestType<'a> + Pointee, + { + let offset = amt.checked_mul(T::guest_size()) + .and_then(|o| self.pointer.checked_add(o)); + let offset = match offset { + Some(o) => o, + None => return Err(GuestError::InvalidFlagValue("")), + }; + Ok(GuestPtr::new(self.mem, offset)) + } +} + +impl<'a, T> GuestPtr<'a, [T]> { + pub fn offset_base(&self) -> u32 { + self.pointer.0 + } + + pub fn len(&self) -> u32 { + self.pointer.1 + } + + pub fn iter<'b>(&'b self) -> impl ExactSizeIterator, GuestError>> + 'b + where + T: GuestType<'a>, + { + let base = GuestPtr::new(self.mem, self.offset_base()); + (0..self.len()).map(move |i| base.add(i)) + } +} + +impl<'a> GuestPtr<'a, str> { + pub fn offset_base(&self) -> u32 { + self.pointer.0 + } + + pub fn len(&self) -> u32 { + self.pointer.1 + } + + pub fn as_bytes(&self) -> GuestPtr<'a, [u8]> { + GuestPtr::new(self.mem, self.pointer) + } + + pub fn as_raw(&self) -> Result<*mut str, GuestError> { + let ptr = self.mem.validate_size_align(self.pointer.0, 1, self.pointer.1)?; + + // TODO: doc unsafety here + unsafe { + let s = slice::from_raw_parts_mut(ptr, self.pointer.1 as usize); + match str::from_utf8_mut(s) { + Ok(s) => Ok(s), + Err(e) => Err(GuestError::InvalidUtf8(e)) + } + } + } +} + +impl Clone for GuestPtr<'_, T> { + fn clone(&self) -> Self { + *self + } +} + +impl Copy for GuestPtr<'_, T> {} + +impl fmt::Debug for GuestPtr<'_, T> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + T::debug(self.pointer, f) + } +} + +mod private { + pub trait Sealed {} + impl Sealed for T {} + impl Sealed for [T] {} + impl Sealed for str {} +} + +pub trait Pointee: private::Sealed { + #[doc(hidden)] + type Pointer: Copy; + #[doc(hidden)] + fn debug(pointer: Self::Pointer, f: &mut fmt::Formatter) -> fmt::Result; +} + +impl Pointee for T { + type Pointer = u32; + fn debug(pointer: Self::Pointer, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "*guest {:#x}", pointer) + } +} + +impl Pointee for [T] { + type Pointer = (u32, u32); + fn debug(pointer: Self::Pointer, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "*guest {:#x}/{}", pointer.0, pointer.1) + } +} + +impl Pointee for str { + type Pointer = (u32, u32); + fn debug(pointer: Self::Pointer, f: &mut fmt::Formatter) -> fmt::Result { + <[u8]>::debug(pointer, f) + } +} diff --git a/crates/runtime/src/memory/array.rs b/crates/runtime/src/memory/array.rs deleted file mode 100644 index 3c7815536b..0000000000 --- a/crates/runtime/src/memory/array.rs +++ /dev/null @@ -1,260 +0,0 @@ -use super::ptr::{GuestPtr, GuestRef}; -use crate::{GuestError, GuestType, GuestTypeTransparent}; -use std::{fmt, ops::Deref}; - -#[derive(Clone)] -pub struct GuestArray<'a, T> -where - T: GuestType<'a>, -{ - pub(super) ptr: GuestPtr<'a, T>, - pub(super) num_elems: u32, -} - -impl<'a, T> fmt::Debug for GuestArray<'a, T> -where - T: GuestType<'a> + fmt::Debug, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "GuestArray {{ ptr: {:?}, num_elems: {:?} }}", - self.ptr, self.num_elems - ) - } -} - -impl<'a, T> GuestArray<'a, T> -where - T: GuestType<'a>, -{ - pub fn iter(&self) -> GuestArrayIter<'a, T> { - let next = GuestPtr { - mem: self.ptr.mem, - region: self.ptr.region, - type_: self.ptr.type_, - }; - GuestArrayIter { - next, - num_elems: self.num_elems, - count: 0, - } - } -} - -pub struct GuestArrayIter<'a, T> -where - T: GuestType<'a>, -{ - next: GuestPtr<'a, T>, - num_elems: u32, - count: u32, -} - -impl<'a, T> Iterator for GuestArrayIter<'a, T> -where - T: GuestType<'a>, -{ - type Item = Result, GuestError>; - - fn next(&mut self) -> Option { - if self.count < self.num_elems { - // ok... - Some(T::validate(&self.next).and_then(|()| { - let curr = GuestPtr { - mem: self.next.mem, - region: self.next.region, - type_: self.next.type_, - }; - self.next = self.next.elem(1)?; - self.count += 1; - Ok(curr) - })) - } else { - // no more elements... - None - } - } -} - -impl<'a, T> GuestArray<'a, T> -where - T: GuestTypeTransparent<'a>, -{ - pub fn as_ref(&self) -> Result, GuestError> { - let mut next = self.ptr.elem(0)?; - for _ in 0..self.num_elems { - T::validate(&next)?; - next = next.elem(1)?; - } - let region = self.ptr.region.extend(self.num_elems); - let handle = { - let mut borrows = self.ptr.mem.borrows.borrow_mut(); - borrows - .borrow_immut(region) - .ok_or_else(|| GuestError::PtrBorrowed(region))? - }; - let ref_ = GuestRef { - mem: self.ptr.mem, - region, - handle, - type_: self.ptr.type_, - }; - Ok(GuestArrayRef { - ref_, - num_elems: self.num_elems, - }) - } -} - -pub struct GuestArrayRef<'a, T> -where - T: GuestTypeTransparent<'a>, -{ - ref_: GuestRef<'a, T>, - num_elems: u32, -} - -impl<'a, T> fmt::Debug for GuestArrayRef<'a, T> -where - T: GuestTypeTransparent<'a> + fmt::Debug, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "GuestArrayRef {{ ref_: {:?}, num_elems: {:?} }}", - self.ref_, self.num_elems - ) - } -} - -impl<'a, T> Deref for GuestArrayRef<'a, T> -where - T: GuestTypeTransparent<'a>, -{ - type Target = [T]; - - fn deref(&self) -> &Self::Target { - unsafe { - std::slice::from_raw_parts( - self.ref_.as_ptr().as_raw() as *const _, - self.num_elems as usize, - ) - } - } -} - -#[cfg(test)] -mod test { - use crate::{ - memory::ptr::{GuestPtr, GuestPtrMut}, - GuestError, GuestMemory, GuestType, Region, - }; - - #[repr(align(4096))] - struct HostMemory { - buffer: [u8; 4096], - } - - impl HostMemory { - pub fn new() -> Self { - Self { buffer: [0; 4096] } - } - pub fn as_mut_ptr(&mut self) -> *mut u8 { - self.buffer.as_mut_ptr() - } - pub fn len(&self) -> usize { - self.buffer.len() - } - } - - #[test] - fn out_of_bounds() { - let mut host_memory = HostMemory::new(); - let guest_memory = GuestMemory::new(host_memory.as_mut_ptr(), host_memory.len() as u32); - // try extracting an array out of memory bounds - let ptr: GuestPtr = guest_memory.ptr(4092).expect("ptr to last i32 el"); - let err = ptr.array(2).expect_err("out of bounds ptr error"); - assert_eq!(err, GuestError::PtrOutOfBounds(Region::new(4092, 8))); - } - - #[test] - fn ptr_to_array() { - let mut host_memory = HostMemory::new(); - let guest_memory = GuestMemory::new(host_memory.as_mut_ptr(), host_memory.len() as u32); - // write a simple array into memory - { - let ptr: GuestPtrMut = guest_memory.ptr_mut(0).expect("ptr mut to first el"); - let mut el = ptr.as_ref_mut().expect("ref mut to first el"); - *el = 1; - let ptr: GuestPtrMut = guest_memory.ptr_mut(4).expect("ptr mut to second el"); - let mut el = ptr.as_ref_mut().expect("ref mu to second el"); - *el = 2; - let ptr: GuestPtrMut = guest_memory.ptr_mut(8).expect("ptr mut to third el"); - let mut el = ptr.as_ref_mut().expect("ref mut to third el"); - *el = 3; - } - // extract as array - let ptr: GuestPtr = guest_memory.ptr(0).expect("ptr to first el"); - let arr = ptr.array(3).expect("convert ptr to array"); - let as_ref = &*arr.as_ref().expect("array borrowed immutably"); - assert_eq!(as_ref, &[1, 2, 3]); - // borrowing again should be fine - let as_ref2 = &*arr.as_ref().expect("array borrowed immutably again"); - assert_eq!(as_ref2, as_ref); - } - - #[test] - fn ptr_to_ptr_array() { - let mut host_memory = HostMemory::new(); - let guest_memory = GuestMemory::new(host_memory.as_mut_ptr(), host_memory.len() as u32); - { - let val_ptr: GuestPtrMut = - guest_memory.ptr_mut(0).expect("ptr mut to the first value"); - let mut val = val_ptr.as_ref_mut().expect("ref mut to the first value"); - *val = 255; - let val_ptr: GuestPtrMut = guest_memory - .ptr_mut(4) - .expect("ptr mut to the second value"); - let mut val = val_ptr.as_ref_mut().expect("ref mut to the second value"); - *val = 254; - let val_ptr: GuestPtrMut = - guest_memory.ptr_mut(8).expect("ptr mut to the third value"); - let mut val = val_ptr.as_ref_mut().expect("ref mut to the third value"); - *val = 253; - } - { - let ptr = guest_memory.ptr_mut(12).expect("ptr mut to first el"); - ptr.write( - &guest_memory - .ptr::>(0) - .expect("ptr to the first value"), - ); - let ptr = guest_memory.ptr_mut(16).expect("ptr mut to first el"); - ptr.write( - &guest_memory - .ptr::>(4) - .expect("ptr to the second value"), - ); - let ptr = guest_memory.ptr_mut(20).expect("ptr mut to first el"); - ptr.write( - &guest_memory - .ptr::>(8) - .expect("ptr to the third value"), - ); - } - // extract as array - let ptr: GuestPtr> = guest_memory.ptr(12).expect("ptr to first el"); - let arr = ptr.array(3).expect("convert ptr to array"); - let contents = arr - .iter() - .map(|ptr_ptr| { - *GuestType::read(&ptr_ptr.expect("valid ptr to ptr")) - .expect("valid ptr to some value") - .as_ref() - .expect("deref ptr to some value") - }) - .collect::>(); - assert_eq!(&contents, &[255, 254, 253]); - } -} diff --git a/crates/runtime/src/memory/mod.rs b/crates/runtime/src/memory/mod.rs deleted file mode 100644 index 9fd8f907fe..0000000000 --- a/crates/runtime/src/memory/mod.rs +++ /dev/null @@ -1,72 +0,0 @@ -mod array; -mod ptr; -mod string; - -pub use array::*; -pub use ptr::*; -pub use string::*; - -use crate::{borrow::GuestBorrows, GuestError, GuestType, Region}; -use std::{cell::RefCell, fmt, marker::PhantomData, rc::Rc}; - -pub struct GuestMemory<'a> { - ptr: *mut u8, - len: u32, - lifetime: PhantomData<&'a ()>, - borrows: Rc>, -} - -impl<'a> fmt::Debug for GuestMemory<'a> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "GuestMemory {{ ptr: {:?}, len: {:?}, borrows: {:?} }}", - self.ptr, self.len, self.borrows - ) - } -} - -impl<'a> GuestMemory<'a> { - pub fn new(ptr: *mut u8, len: u32) -> Self { - assert_eq!(ptr as usize % 4096, 0, "GuestMemory must be page-aligned"); - Self { - ptr, - len, - lifetime: PhantomData, - borrows: Rc::new(RefCell::new(GuestBorrows::new())), - } - } - - fn contains(&self, r: Region) -> bool { - r.start < self.len - && r.len < self.len // make sure next clause doesnt underflow - && r.start <= (self.len - r.len) - } - - pub fn ptr>(&'a self, at: u32) -> Result, GuestError> { - let region = Region { - start: at, - len: T::size(), - }; - if !self.contains(region) { - Err(GuestError::PtrOutOfBounds(region))?; - } - if at % T::align() != 0 { - Err(GuestError::PtrNotAligned(region, T::align()))?; - } - Ok(GuestPtr { - mem: &self, - region, - type_: PhantomData, - }) - } - - pub fn ptr_mut>(&'a self, at: u32) -> Result, GuestError> { - let ptr = self.ptr(at)?; - Ok(GuestPtrMut { - mem: ptr.mem, - region: ptr.region, - type_: ptr.type_, - }) - } -} diff --git a/crates/runtime/src/memory/ptr.rs b/crates/runtime/src/memory/ptr.rs deleted file mode 100644 index 54a80e8ab4..0000000000 --- a/crates/runtime/src/memory/ptr.rs +++ /dev/null @@ -1,515 +0,0 @@ -use super::{array::GuestArray, GuestMemory}; -use crate::{borrow::BorrowHandle, GuestError, GuestType, GuestTypeTransparent, Region}; -use std::{ - fmt, - marker::PhantomData, - ops::{Deref, DerefMut}, -}; - -#[derive(Clone)] -pub struct GuestPtr<'a, T> { - pub(super) mem: &'a GuestMemory<'a>, - pub(super) region: Region, - pub(super) type_: PhantomData, -} - -impl<'a, T> fmt::Debug for GuestPtr<'a, T> -where - T: fmt::Debug, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "GuestPtr {{ mem: {:?}, region: {:?} }}", - self.mem, self.region - ) - } -} - -impl<'a, T: GuestType<'a>> GuestPtr<'a, T> { - pub fn as_raw(&self) -> *const u8 { - (self.mem.ptr as usize + self.region.start as usize) as *const u8 - } - - pub fn elem(&self, elements: i32) -> Result { - self.mem - .ptr(self.region.start + (elements * self.region.len as i32) as u32) - } - - pub fn cast>( - &self, - offset: u32, - ) -> Result, GuestError> { - self.mem.ptr(self.region.start + offset) - } - - pub fn array(&self, num_elems: u32) -> Result, GuestError> { - let region = self.region.extend(num_elems); - if self.mem.contains(region) { - let ptr = GuestPtr { - mem: self.mem, - region: self.region, - type_: self.type_, - }; - Ok(GuestArray { ptr, num_elems }) - } else { - Err(GuestError::PtrOutOfBounds(region)) - } - } -} - -impl<'a, T> GuestPtr<'a, T> -where - T: GuestTypeTransparent<'a>, -{ - pub fn as_ref(&self) -> Result, GuestError> { - T::validate(&self)?; - let handle = { - let mut borrows = self.mem.borrows.borrow_mut(); - borrows - .borrow_immut(self.region) - .ok_or_else(|| GuestError::PtrBorrowed(self.region))? - }; - Ok(GuestRef { - mem: self.mem, - region: self.region, - handle, - type_: self.type_, - }) - } -} - -impl<'a, T> GuestPtr<'a, T> -where - T: GuestType<'a>, -{ - pub fn read(&self) -> Result { - T::read(self) - } -} - -impl<'a, T> GuestType<'a> for GuestPtr<'a, T> -where - T: GuestType<'a>, -{ - fn size() -> u32 { - 4 - } - - fn align() -> u32 { - 4 - } - - fn name() -> String { - format!("GuestPtr<{}>", T::name()) - } - - fn validate(location: &GuestPtr<'a, GuestPtr<'a, T>>) -> Result<(), GuestError> { - // location is guaranteed to be in GuestMemory and aligned to 4 - let raw_ptr: u32 = unsafe { *(location.as_raw() as *const u32) }; - // GuestMemory can validate that the raw pointer contents are legal for T: - let _guest_ptr: GuestPtr = location.mem.ptr(raw_ptr)?; - Ok(()) - } - - // Operations for reading and writing Ptrs to memory: - fn read(location: &GuestPtr<'a, Self>) -> Result { - // location is guaranteed to be in GuestMemory and aligned to 4 - let raw_ptr: u32 = unsafe { *(location.as_raw() as *const u32) }; - // GuestMemory can validate that the raw pointer contents are legal for T: - let guest_ptr: GuestPtr<'a, T> = location.mem.ptr(raw_ptr)?; - Ok(guest_ptr) - } - - fn write(&self, location: &GuestPtrMut<'a, Self>) { - // location is guaranteed to be in GuestMemory and aligned to 4 - unsafe { - let raw_ptr: *mut u32 = location.as_raw() as *mut u32; - raw_ptr.write(self.region.start); - } - } -} - -#[derive(Clone)] -pub struct GuestPtrMut<'a, T> { - pub(super) mem: &'a GuestMemory<'a>, - pub(super) region: Region, - pub(super) type_: PhantomData, -} - -impl<'a, T> fmt::Debug for GuestPtrMut<'a, T> -where - T: fmt::Debug, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "GuestPtrMut {{ mem: {:?}, region: {:?} }}", - self.mem, self.region - ) - } -} - -impl<'a, T> GuestPtrMut<'a, T> -where - T: GuestType<'a>, -{ - pub fn as_immut(&self) -> GuestPtr<'a, T> { - GuestPtr { - mem: self.mem, - region: self.region, - type_: self.type_, - } - } - - pub fn as_raw(&self) -> *mut u8 { - (self.mem.ptr as usize + self.region.start as usize) as *mut u8 - } - - pub fn elem(&self, elements: i32) -> Result { - self.mem - .ptr_mut(self.region.start + (elements * self.region.len as i32) as u32) - } - - pub fn cast>( - &self, - offset: u32, - ) -> Result, GuestError> { - self.mem.ptr_mut(self.region.start + offset) - } -} - -impl<'a, T> GuestPtrMut<'a, T> -where - T: GuestTypeTransparent<'a>, -{ - pub fn as_ref(&self) -> Result, GuestError> { - self.as_immut().as_ref() - } - - pub fn as_ref_mut(&self) -> Result, GuestError> { - T::validate(&self.as_immut())?; - let handle = { - let mut borrows = self.mem.borrows.borrow_mut(); - borrows - .borrow_mut(self.region) - .ok_or_else(|| GuestError::PtrBorrowed(self.region))? - }; - Ok(GuestRefMut { - mem: self.mem, - region: self.region, - handle, - type_: self.type_, - }) - } -} - -impl<'a, T> GuestPtrMut<'a, T> -where - T: GuestType<'a>, -{ - pub fn read(&self) -> Result { - T::read(&self.as_immut()) - } - - pub fn write(&self, ptr: &T) { - T::write(ptr, &self); - } -} - -impl<'a, T> GuestType<'a> for GuestPtrMut<'a, T> -where - T: GuestType<'a>, -{ - fn size() -> u32 { - 4 - } - - fn align() -> u32 { - 4 - } - - fn name() -> String { - format!("GuestPtrMut<{}>", T::name()) - } - - fn validate(location: &GuestPtr<'a, GuestPtrMut<'a, T>>) -> Result<(), GuestError> { - // location is guaranteed to be in GuestMemory and aligned to 4 - let raw_ptr: u32 = unsafe { *(location.as_raw() as *const u32) }; - // GuestMemory can validate that the raw pointer contents are legal for T: - let _guest_ptr: GuestPtr = location.mem.ptr(raw_ptr)?; - Ok(()) - } - - // Reading and writing GuestPtrMuts to memory: - fn read(location: &GuestPtr<'a, Self>) -> Result { - // location is guaranteed to be in GuestMemory and aligned to 4 - let raw_ptr: u32 = unsafe { *(location.as_raw() as *const u32) }; - // GuestMemory can validate that the raw pointer contents are legal for T: - let guest_ptr_mut: GuestPtrMut<'a, T> = location.mem.ptr_mut(raw_ptr)?; - Ok(guest_ptr_mut) - } - - fn write(&self, location: &GuestPtrMut<'a, Self>) { - // location is guaranteed to be in GuestMemory and aligned to 4 - unsafe { - let raw_ptr: *mut u32 = location.as_raw() as *mut u32; - raw_ptr.write(self.region.start); - } - } -} - -pub struct GuestRef<'a, T> { - pub(super) mem: &'a GuestMemory<'a>, - pub(super) region: Region, - pub(super) handle: BorrowHandle, - pub(super) type_: PhantomData, -} - -impl<'a, T> fmt::Debug for GuestRef<'a, T> -where - T: fmt::Debug, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "GuestRef {{ mem: {:?}, region: {:?}, handle: {:?} }}", - self.mem, self.region, self.handle - ) - } -} - -impl<'a, T> GuestRef<'a, T> { - pub fn as_ptr(&self) -> GuestPtr<'a, T> { - GuestPtr { - mem: self.mem, - region: self.region, - type_: self.type_, - } - } -} - -impl<'a, T> Deref for GuestRef<'a, T> -where - T: GuestTypeTransparent<'a>, -{ - type Target = T; - - fn deref(&self) -> &Self::Target { - unsafe { - ((self.mem.ptr as usize + self.region.start as usize) as *const T) - .as_ref() - .expect("GuestRef implies non-null") - } - } -} - -impl<'a, T> Drop for GuestRef<'a, T> { - fn drop(&mut self) { - let mut borrows = self.mem.borrows.borrow_mut(); - borrows.unborrow_immut(self.handle); - } -} - -pub struct GuestRefMut<'a, T> { - pub(super) mem: &'a GuestMemory<'a>, - pub(super) region: Region, - pub(super) handle: BorrowHandle, - pub(super) type_: PhantomData, -} - -impl<'a, T> fmt::Debug for GuestRefMut<'a, T> -where - T: fmt::Debug, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "GuestRefMut {{ mem: {:?}, region: {:?}, handle: {:?} }}", - self.mem, self.region, self.handle - ) - } -} - -impl<'a, T> GuestRefMut<'a, T> { - pub fn as_ptr(&self) -> GuestPtr<'a, T> { - GuestPtr { - mem: self.mem, - region: self.region, - type_: self.type_, - } - } - pub fn as_ptr_mut(&self) -> GuestPtrMut<'a, T> { - GuestPtrMut { - mem: self.mem, - region: self.region, - type_: self.type_, - } - } -} - -impl<'a, T> ::std::ops::Deref for GuestRefMut<'a, T> -where - T: GuestTypeTransparent<'a>, -{ - type Target = T; - - fn deref(&self) -> &Self::Target { - unsafe { - ((self.mem.ptr as usize + self.region.start as usize) as *const T) - .as_ref() - .expect("GuestRef implies non-null") - } - } -} - -impl<'a, T> DerefMut for GuestRefMut<'a, T> -where - T: GuestTypeTransparent<'a>, -{ - fn deref_mut(&mut self) -> &mut Self::Target { - unsafe { - ((self.mem.ptr as usize + self.region.start as usize) as *mut T) - .as_mut() - .expect("GuestRef implies non-null") - } - } -} - -impl<'a, T> Drop for GuestRefMut<'a, T> { - fn drop(&mut self) { - let mut borrows = self.mem.borrows.borrow_mut(); - borrows.unborrow_mut(self.handle); - } -} - -#[cfg(test)] -mod test { - use super::{ - super::{GuestError, GuestMemory, Region}, - {GuestPtr, GuestPtrMut}, - }; - - #[repr(align(4096))] - struct HostMemory { - buffer: [u8; 4096], - } - - impl HostMemory { - pub fn new() -> Self { - Self { buffer: [0; 4096] } - } - pub fn as_mut_ptr(&mut self) -> *mut u8 { - self.buffer.as_mut_ptr() - } - pub fn len(&self) -> usize { - self.buffer.len() - } - } - - #[test] - fn out_of_bounds() { - let mut host_memory = HostMemory::new(); - let guest_memory = GuestMemory::new(host_memory.as_mut_ptr(), host_memory.len() as u32); - // try extracting an immutable ptr out of memory bounds - let err = guest_memory - .ptr::>(4096) - .expect_err("out of bounds ptr error"); - assert_eq!(err, GuestError::PtrOutOfBounds(Region::new(4096, 4))); - // try extracting an mutable ptr out of memory bounds - let err = guest_memory - .ptr_mut::>(4096) - .expect_err("out of bounds ptr error"); - assert_eq!(err, GuestError::PtrOutOfBounds(Region::new(4096, 4))); - } - - #[test] - fn not_aligned() { - let mut host_memory = HostMemory::new(); - let guest_memory = GuestMemory::new(host_memory.as_mut_ptr(), host_memory.len() as u32); - // try extracting a misaligned immutable ptr - let err = guest_memory - .ptr::>(2) - .expect_err("ptr misaligned"); - assert_eq!(err, GuestError::PtrNotAligned(Region::new(2, 4), 4)); - // try extracting a misaligned mutable ptr - let err = guest_memory - .ptr_mut::>(2) - .expect_err("ptr mut misaligned"); - assert_eq!(err, GuestError::PtrNotAligned(Region::new(2, 4), 4)); - } - - #[test] - fn ptr_from_memory() { - let mut host_memory = HostMemory::new(); - let guest_memory = GuestMemory::new(host_memory.as_mut_ptr(), host_memory.len() as u32); - // write something to memory - { - let ptr: GuestPtrMut = guest_memory.ptr_mut(8).expect("ptr mut to the el"); - let mut el = ptr.as_ref_mut().expect("ref mut to the el"); - *el = 100; - } - // extract as ref - let ptr: GuestPtr = guest_memory.ptr(8).expect("ptr to the el"); - let as_ref = ptr.as_ref().expect("el borrowed immutably"); - assert_eq!(*as_ref, 100); - // borrowing again should be fine - let as_ref2 = ptr.as_ref().expect("el borrowed immutably again"); - assert_eq!(*as_ref2, *as_ref); - } - - #[test] - fn ptr_mut_from_memory() { - let mut host_memory = HostMemory::new(); - let guest_memory = GuestMemory::new(host_memory.as_mut_ptr(), host_memory.len() as u32); - // set elems of array to zero - { - let ptr: GuestPtrMut = guest_memory.ptr_mut(8).expect("ptr mut to the el"); - let mut el = ptr.as_ref_mut().expect("ref mut to the el"); - *el = 100; - } - // extract as ref - let ptr: GuestPtrMut = guest_memory.ptr_mut(8).expect("ptr mut to the el"); - assert_eq!(*ptr.as_ref().expect("el borrowed immutably"), 100); - // overwrite the memory and re-verify - *ptr.as_ref_mut().expect("el borrowed mutably") = 2000; - // re-validate - assert_eq!(*ptr.as_ref().expect("el borrowed immutably"), 2000); - } - - #[test] - #[should_panic( - expected = "el borrowed immutably while borrowed mutably: PtrBorrowed(Region { start: 0, len: 2 })" - )] - fn borrow_mut_then_immut() { - let mut host_memory = HostMemory::new(); - let guest_memory = GuestMemory::new(host_memory.as_mut_ptr(), host_memory.len() as u32); - let ptr: GuestPtrMut = guest_memory.ptr_mut(0).expect("ptr mut to the el"); - // borrow mutably - let _as_mut = ptr - .as_ref_mut() - .expect("el borrowed mutably for the first time"); - // borrow immutably should fail - let _as_ref = ptr - .as_ref() - .expect("el borrowed immutably while borrowed mutably"); - } - - #[test] - #[should_panic( - expected = "el borrowed mutably while borrowed mutably: PtrBorrowed(Region { start: 0, len: 2 })" - )] - fn borrow_mut_twice() { - let mut host_memory = HostMemory::new(); - let guest_memory = GuestMemory::new(host_memory.as_mut_ptr(), host_memory.len() as u32); - let ptr: GuestPtrMut = guest_memory.ptr_mut(0).expect("ptr mut to the el"); - // borrow mutably - let _as_mut = ptr - .as_ref_mut() - .expect("el borrowed mutably for the first time"); - // try borrowing mutably again - let _as_mut2 = ptr - .as_ref_mut() - .expect("el borrowed mutably while borrowed mutably"); - } -} diff --git a/crates/runtime/src/memory/string.rs b/crates/runtime/src/memory/string.rs deleted file mode 100644 index a4e6deac6d..0000000000 --- a/crates/runtime/src/memory/string.rs +++ /dev/null @@ -1,124 +0,0 @@ -use super::array::{GuestArray, GuestArrayRef}; -use crate::GuestError; -use std::fmt; - -pub struct GuestString<'a> { - pub(super) array: GuestArray<'a, u8>, -} - -impl<'a> fmt::Debug for GuestString<'a> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "GuestString {{ array: {:?} }}", self.array) - } -} - -impl<'a> GuestString<'a> { - pub fn as_ref(&self) -> Result, GuestError> { - let ref_ = self.array.as_ref()?; - Ok(GuestStringRef { ref_ }) - } - - pub fn to_string(&self) -> Result { - Ok(self.as_ref()?.as_str()?.to_owned()) - } -} - -impl<'a> From> for GuestString<'a> { - fn from(array: GuestArray<'a, u8>) -> Self { - Self { array } - } -} - -pub struct GuestStringRef<'a> { - pub(super) ref_: GuestArrayRef<'a, u8>, -} - -impl<'a> fmt::Debug for GuestStringRef<'a> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "GuestStringRef {{ ref_: {:?} }}", self.ref_) - } -} - -impl<'a> GuestStringRef<'a> { - pub fn as_str(&self) -> Result<&str, GuestError> { - std::str::from_utf8(&*self.ref_).map_err(Into::into) - } -} - -#[cfg(test)] -mod test { - use super::{ - super::{ - ptr::{GuestPtr, GuestPtrMut}, - GuestError, GuestMemory, - }, - GuestString, - }; - - #[repr(align(4096))] - struct HostMemory { - buffer: [u8; 4096], - } - - impl HostMemory { - pub fn new() -> Self { - Self { buffer: [0; 4096] } - } - pub fn as_mut_ptr(&mut self) -> *mut u8 { - self.buffer.as_mut_ptr() - } - pub fn len(&self) -> usize { - self.buffer.len() - } - } - - #[test] - fn valid_utf8() { - let mut host_memory = HostMemory::new(); - let guest_memory = GuestMemory::new(host_memory.as_mut_ptr(), host_memory.len() as u32); - // write string into memory - let mut ptr: GuestPtrMut = guest_memory.ptr_mut(0).expect("ptr mut to start of string"); - let input_str = "cześć WASI!"; - for byte in input_str.as_bytes() { - let mut ref_mut = ptr.as_ref_mut().expect("valid deref"); - *ref_mut = *byte; - ptr = ptr.elem(1).expect("next ptr"); - } - // read the string as GuestString - let ptr: GuestPtr = guest_memory.ptr(0).expect("ptr to start of string"); - let guest_string: GuestString<'_> = ptr - .array(input_str.len() as u32) - .expect("valid null-terminated string") - .into(); - let as_ref = guest_string.as_ref().expect("deref"); - assert_eq!(as_ref.as_str().expect("valid UTF-8"), input_str); - } - - #[test] - fn invalid_utf8() { - let mut host_memory = HostMemory::new(); - let guest_memory = GuestMemory::new(host_memory.as_mut_ptr(), host_memory.len() as u32); - // write string into memory - let mut ptr: GuestPtrMut = guest_memory.ptr_mut(0).expect("ptr mut to start of string"); - let input_str = "cześć WASI!"; - let mut bytes = input_str.as_bytes().to_vec(); - // insert 0xFE which is an invalid UTF-8 byte - bytes[5] = 0xfe; - for byte in &bytes { - let mut ref_mut = ptr.as_ref_mut().expect("valid deref"); - *ref_mut = *byte; - ptr = ptr.elem(1).expect("next ptr"); - } - // read the string as GuestString - let ptr: GuestPtr = guest_memory.ptr(0).expect("ptr to start of string"); - let guest_string: GuestString<'_> = ptr - .array(bytes.len() as u32) - .expect("valid null-terminated string") - .into(); - let as_ref = guest_string.as_ref().expect("deref"); - match as_ref.as_str().expect_err("should fail") { - GuestError::InvalidUtf8(_) => {} - x => assert!(false, "expected GuestError::InvalidUtf8(_), got {:?}", x), - } - } -} diff --git a/crates/test/src/lib.rs b/crates/test/src/lib.rs index 2ddf5704f8..bf72036c6f 100644 --- a/crates/test/src/lib.rs +++ b/crates/test/src/lib.rs @@ -1,17 +1,14 @@ use proptest::prelude::*; +use std::cell::UnsafeCell; use wiggle_runtime::GuestMemory; #[repr(align(4096))] pub struct HostMemory { - buffer: [u8; 4096], + buffer: UnsafeCell<[u8; 4096]>, } impl HostMemory { pub fn new() -> Self { - HostMemory { buffer: [0; 4096] } - } - - pub fn guest_memory<'a>(&'a mut self) -> GuestMemory<'a> { - GuestMemory::new(self.buffer.as_mut_ptr(), self.buffer.len() as u32) + HostMemory { buffer: UnsafeCell::new([0; 4096]) } } pub fn mem_area_strat(align: u32) -> BoxedStrategy { @@ -29,6 +26,15 @@ impl HostMemory { } } +unsafe impl GuestMemory for HostMemory { + fn base(&self) -> (*mut u8, u32) { + unsafe { + let ptr = self.buffer.get(); + ((*ptr).as_mut_ptr(), (*ptr).len() as u32) + } + } +} + #[derive(Debug)] pub struct MemArea { pub ptr: u32, diff --git a/tests/arrays.rs b/tests/arrays.rs index 26583911ea..d9ccd4ce93 100644 --- a/tests/arrays.rs +++ b/tests/arrays.rs @@ -1,5 +1,5 @@ use proptest::prelude::*; -use wiggle_runtime::{GuestArray, GuestError, GuestPtr, GuestPtrMut, GuestType}; +use wiggle_runtime::{GuestError, GuestMemory, GuestPtr}; use wiggle_test::{impl_errno, HostMemory, MemArea, WasiCtx}; wiggle::from_witx!({ @@ -14,25 +14,25 @@ impl arrays::Arrays for WasiCtx { &self, excuses: &types::ConstExcuseArray, ) -> Result { - let last = GuestType::read( - &excuses - .iter() - .last() - .expect("input array is non-empty") - .expect("valid ptr to ptr"), - ) - .expect("valid ptr to some Excuse value"); - Ok(*last.as_ref().expect("dereferencing ptr should succeed")) + let last = &excuses + .iter() + .last() + .expect("input array is non-empty") + .expect("valid ptr to ptr") + .read() + .expect("valid ptr to some Excuse value"); + Ok(last.read().expect("dereferencing ptr should succeed")) } fn populate_excuses(&self, excuses: &types::ExcuseArray) -> Result<(), types::Errno> { for excuse in excuses.iter() { - let ptr_to_ptr = GuestType::read(&excuse.expect("valid ptr to ptr")) + let ptr_to_excuse = excuse + .expect("valid ptr to ptr") + .read() .expect("valid ptr to some Excuse value"); - let mut ptr = ptr_to_ptr - .as_ref_mut() + ptr_to_excuse + .write(types::Excuse::Sleeping) .expect("dereferencing mut ptr should succeed"); - *ptr = types::Excuse::Sleeping; } Ok(()) } @@ -43,7 +43,6 @@ struct ReduceExcusesExcercise { excuse_values: Vec, excuse_ptr_locs: Vec, array_ptr_loc: MemArea, - array_len_loc: MemArea, return_ptr_loc: MemArea, } @@ -57,22 +56,18 @@ impl ReduceExcusesExcercise { proptest::collection::vec(HostMemory::mem_area_strat(4), len_usize..=len_usize), HostMemory::mem_area_strat(4 * len), HostMemory::mem_area_strat(4), - HostMemory::mem_area_strat(4), ) }) .prop_map( - |(excuse_values, excuse_ptr_locs, array_ptr_loc, array_len_loc, return_ptr_loc)| { - Self { - excuse_values, - excuse_ptr_locs, - array_ptr_loc, - array_len_loc, - return_ptr_loc, - } + |(excuse_values, excuse_ptr_locs, array_ptr_loc, return_ptr_loc)| Self { + excuse_values, + excuse_ptr_locs, + array_ptr_loc, + return_ptr_loc, }, ) .prop_filter("non-overlapping pointers", |e| { - let mut all = vec![&e.array_ptr_loc, &e.array_len_loc, &e.return_ptr_loc]; + let mut all = vec![&e.array_ptr_loc, &e.return_ptr_loc]; all.extend(e.excuse_ptr_locs.iter()); MemArea::non_overlapping_set(&all) }) @@ -82,44 +77,31 @@ impl ReduceExcusesExcercise { pub fn test(&self) { let mut ctx = WasiCtx::new(); let mut host_memory = HostMemory::new(); - let mut guest_memory = host_memory.guest_memory(); // Populate memory with pointers to generated Excuse values for (&excuse, ptr) in self.excuse_values.iter().zip(self.excuse_ptr_locs.iter()) { - *guest_memory - .ptr_mut(ptr.ptr) - .expect("ptr mut to Excuse value") - .as_ref_mut() - .expect("deref ptr mut to Excuse value") = excuse; + host_memory + .ptr(ptr.ptr) + .write(excuse) + .expect("deref ptr mut to Excuse value"); } - // Populate array length info - *guest_memory - .ptr_mut(self.array_len_loc.ptr) - .expect("ptr to array len") - .as_ref_mut() - .expect("deref ptr mut to array len") = self.excuse_ptr_locs.len() as u32; - // Populate the array with pointers to generated Excuse values { - let mut next: GuestPtrMut<'_, GuestPtr> = guest_memory - .ptr_mut(self.array_ptr_loc.ptr) - .expect("ptr to array mut"); - for ptr in &self.excuse_ptr_locs { - next.write( - &guest_memory - .ptr::(ptr.ptr) - .expect("ptr to Excuse value"), - ); - next = next.elem(1).expect("increment ptr by 1"); + let array: GuestPtr<'_, [GuestPtr]> = + host_memory.ptr((self.array_ptr_loc.ptr, self.excuse_ptr_locs.len() as u32)); + for (slot, ptr) in array.iter().zip(&self.excuse_ptr_locs) { + let slot = slot.expect("array should be in bounds"); + slot.write(host_memory.ptr(ptr.ptr)) + .expect("should succeed in writing array"); } } let res = arrays::reduce_excuses( &mut ctx, - &mut guest_memory, + &mut host_memory, self.array_ptr_loc.ptr as i32, - self.array_len_loc.ptr as i32, + self.excuse_ptr_locs.len() as i32, self.return_ptr_loc.ptr as i32, ); @@ -129,10 +111,9 @@ impl ReduceExcusesExcercise { .excuse_values .last() .expect("generated vec of excuses should be non-empty"); - let given: types::Excuse = *guest_memory + let given: types::Excuse = host_memory .ptr(self.return_ptr_loc.ptr) - .expect("ptr to returned value") - .as_ref() + .read() .expect("deref ptr to returned value"); assert_eq!(expected, given, "reduce excuses return val"); } @@ -156,7 +137,6 @@ fn excuse_strat() -> impl Strategy { #[derive(Debug)] struct PopulateExcusesExcercise { array_ptr_loc: MemArea, - array_len_loc: MemArea, elements: Vec, } @@ -167,17 +147,15 @@ impl PopulateExcusesExcercise { let len_usize = len as usize; ( HostMemory::mem_area_strat(4 * len), - HostMemory::mem_area_strat(4), proptest::collection::vec(HostMemory::mem_area_strat(4), len_usize..=len_usize), ) }) - .prop_map(|(array_ptr_loc, array_len_loc, elements)| Self { + .prop_map(|(array_ptr_loc, elements)| Self { array_ptr_loc, - array_len_loc, elements, }) .prop_filter("non-overlapping pointers", |e| { - let mut all = vec![&e.array_ptr_loc, &e.array_len_loc]; + let mut all = vec![&e.array_ptr_loc]; all.extend(e.elements.iter()); MemArea::non_overlapping_set(&all) }) @@ -185,51 +163,38 @@ impl PopulateExcusesExcercise { } pub fn test(&self) { - let mut ctx = WasiCtx::new(); - let mut host_memory = HostMemory::new(); - let mut guest_memory = host_memory.guest_memory(); - - // Populate array length info - *guest_memory - .ptr_mut(self.array_len_loc.ptr) - .expect("ptr mut to array len") - .as_ref_mut() - .expect("deref ptr mut to array len") = self.elements.len() as u32; + let ctx = WasiCtx::new(); + let host_memory = HostMemory::new(); // Populate array with valid pointers to Excuse type in memory - { - let mut next: GuestPtrMut<'_, GuestPtrMut> = guest_memory - .ptr_mut(self.array_ptr_loc.ptr) - .expect("ptr mut to the first element of array"); - for ptr in &self.elements { - next.write( - &guest_memory - .ptr_mut::(ptr.ptr) - .expect("ptr mut to Excuse value"), - ); - next = next.elem(1).expect("increment ptr by 1"); - } + let ptr = host_memory.ptr::<[GuestPtr<'_, types::Excuse>]>(( + self.array_ptr_loc.ptr, + self.elements.len() as u32, + )); + for (ptr, val) in ptr.iter().zip(&self.elements) { + ptr.expect("should be valid pointer") + .write(host_memory.ptr(val.ptr)) + .expect("failed to write value"); } let res = arrays::populate_excuses( - &mut ctx, - &mut guest_memory, + &ctx, + &host_memory, self.array_ptr_loc.ptr as i32, - self.array_len_loc.ptr as i32, + self.elements.len() as i32, ); assert_eq!(res, types::Errno::Ok.into(), "populate excuses errno"); - let arr: GuestArray<'_, GuestPtr<'_, types::Excuse>> = guest_memory - .ptr(self.array_ptr_loc.ptr) - .expect("ptr to the first element of array") - .array(self.elements.len() as u32) - .expect("as array"); + let arr: GuestPtr<'_, [GuestPtr<'_, types::Excuse>]> = + host_memory.ptr((self.array_ptr_loc.ptr, self.elements.len() as u32)); for el in arr.iter() { - let ptr_to_ptr = GuestType::read(&el.expect("valid ptr to ptr")) + let ptr_to_ptr = el + .expect("valid ptr to ptr") + .read() .expect("valid ptr to some Excuse value"); assert_eq!( - *ptr_to_ptr - .as_ref() + ptr_to_ptr + .read() .expect("dereferencing ptr to some Excuse value"), types::Excuse::Sleeping, "element should equal Excuse::Sleeping" diff --git a/tests/atoms.rs b/tests/atoms.rs index 8eda99ba1d..594edb13ab 100644 --- a/tests/atoms.rs +++ b/tests/atoms.rs @@ -1,5 +1,5 @@ use proptest::prelude::*; -use wiggle_runtime::{GuestError, GuestRef}; +use wiggle_runtime::{GuestError, GuestMemory}; use wiggle_test::{impl_errno, HostMemory, MemArea, WasiCtx}; wiggle::from_witx!({ @@ -29,16 +29,10 @@ struct IntFloatExercise { impl IntFloatExercise { pub fn test(&self) { - let mut ctx = WasiCtx::new(); - let mut host_memory = HostMemory::new(); - let mut guest_memory = host_memory.guest_memory(); + let ctx = WasiCtx::new(); + let host_memory = HostMemory::new(); - let e = atoms::int_float_args( - &mut ctx, - &mut guest_memory, - self.an_int as i32, - self.an_float, - ); + let e = atoms::int_float_args(&ctx, &host_memory, self.an_int as i32, self.an_float); assert_eq!(e, types::Errno::Ok.into(), "int_float_args error"); } @@ -64,24 +58,22 @@ struct DoubleIntExercise { impl DoubleIntExercise { pub fn test(&self) { - let mut ctx = WasiCtx::new(); - let mut host_memory = HostMemory::new(); - let mut guest_memory = host_memory.guest_memory(); + let ctx = WasiCtx::new(); + let host_memory = HostMemory::new(); let e = atoms::double_int_return_float( - &mut ctx, - &mut guest_memory, + &ctx, + &host_memory, self.input as i32, self.return_loc.ptr as i32, ); - let return_val: GuestRef = guest_memory - .ptr(self.return_loc.ptr) - .expect("return loc ptr") - .as_ref() - .expect("return val ref"); + let return_val = host_memory + .ptr::(self.return_loc.ptr) + .read() + .expect("failed to read return"); assert_eq!(e, types::Errno::Ok.into(), "errno"); - assert_eq!(*return_val, (self.input as f32) * 2.0, "return val"); + assert_eq!(return_val, (self.input as f32) * 2.0, "return val"); } pub fn strat() -> BoxedStrategy { diff --git a/tests/flags.rs b/tests/flags.rs index 0b729c94d4..d008dcb03c 100644 --- a/tests/flags.rs +++ b/tests/flags.rs @@ -1,6 +1,6 @@ use proptest::prelude::*; use std::convert::TryFrom; -use wiggle_runtime::{GuestError, GuestPtr}; +use wiggle_runtime::{GuestError, GuestMemory, GuestPtr}; use wiggle_test::{impl_errno, HostMemory, MemArea, WasiCtx}; wiggle::from_witx!({ @@ -16,7 +16,7 @@ impl flags::Flags for WasiCtx { old_config: types::CarConfig, other_config_ptr: GuestPtr, ) -> Result { - let other_config = *other_config_ptr.as_ref().map_err(|e| { + let other_config = other_config_ptr.read().map_err(|e| { eprintln!("old_config_ptr error: {}", e); types::Errno::InvalidArg })?; @@ -63,30 +63,27 @@ impl ConfigureCarExercise { } pub fn test(&self) { - let mut ctx = WasiCtx::new(); - let mut host_memory = HostMemory::new(); - let mut guest_memory = host_memory.guest_memory(); + let ctx = WasiCtx::new(); + let host_memory = HostMemory::new(); // Populate input ptr - *guest_memory - .ptr_mut(self.other_config_by_ptr.ptr) - .expect("ptr mut to CarConfig") - .as_ref_mut() - .expect("deref ptr mut to CarConfig") = self.other_config; + host_memory + .ptr(self.other_config_by_ptr.ptr) + .write(self.other_config) + .expect("deref ptr mut to CarConfig"); let res = flags::configure_car( - &mut ctx, - &mut guest_memory, + &ctx, + &host_memory, self.old_config.into(), self.other_config_by_ptr.ptr as i32, self.return_ptr_loc.ptr as i32, ); assert_eq!(res, types::Errno::Ok.into(), "configure car errno"); - let res_config = *guest_memory + let res_config = host_memory .ptr::(self.return_ptr_loc.ptr) - .expect("ptr to returned CarConfig") - .as_ref() + .read() .expect("deref to CarConfig value"); assert_eq!( diff --git a/tests/handles.rs b/tests/handles.rs index 7f1e86d7f3..23037f38cf 100644 --- a/tests/handles.rs +++ b/tests/handles.rs @@ -1,5 +1,5 @@ use proptest::prelude::*; -use wiggle_runtime::{GuestError, GuestType}; +use wiggle_runtime::{GuestError, GuestType, GuestMemory}; use wiggle_test::{impl_errno, HostMemory, MemArea, WasiCtx}; const FD_VAL: u32 = 123; @@ -32,27 +32,25 @@ struct HandleExercise { impl HandleExercise { pub fn test(&self) { - let mut ctx = WasiCtx::new(); - let mut host_memory = HostMemory::new(); - let mut guest_memory = host_memory.guest_memory(); + let ctx = WasiCtx::new(); + let host_memory = HostMemory::new(); - let e = handle_examples::fd_create(&mut ctx, &mut guest_memory, self.return_loc.ptr as i32); + let e = handle_examples::fd_create(&ctx, &host_memory, self.return_loc.ptr as i32); assert_eq!(e, types::Errno::Ok.into(), "fd_create error"); - let h_got: u32 = *guest_memory + let h_got: u32 = host_memory .ptr(self.return_loc.ptr) - .expect("return ptr") - .as_ref() + .read() .expect("return ref_mut"); assert_eq!(h_got, 123, "fd_create return val"); - let e = handle_examples::fd_consume(&mut ctx, &mut guest_memory, h_got as i32); + let e = handle_examples::fd_consume(&ctx, &host_memory, h_got as i32); assert_eq!(e, types::Errno::Ok.into(), "fd_consume error"); - let e = handle_examples::fd_consume(&mut ctx, &mut guest_memory, h_got as i32 + 1); + let e = handle_examples::fd_consume(&ctx, &host_memory, h_got as i32 + 1); assert_eq!( e, @@ -62,7 +60,7 @@ impl HandleExercise { } pub fn strat() -> BoxedStrategy { - (HostMemory::mem_area_strat(types::Fd::size())) + (HostMemory::mem_area_strat(types::Fd::guest_size())) .prop_map(|return_loc| HandleExercise { return_loc }) .boxed() } diff --git a/tests/ints.rs b/tests/ints.rs index 928e061e7c..12cb8c929c 100644 --- a/tests/ints.rs +++ b/tests/ints.rs @@ -1,6 +1,6 @@ use proptest::prelude::*; use std::convert::TryFrom; -use wiggle_runtime::GuestError; +use wiggle_runtime::{GuestError, GuestMemory}; use wiggle_test::{impl_errno, HostMemory, MemArea, WasiCtx}; wiggle::from_witx!({ @@ -44,22 +44,20 @@ impl CookieCutterExercise { } pub fn test(&self) { - let mut ctx = WasiCtx::new(); - let mut host_memory = HostMemory::new(); - let mut guest_memory = host_memory.guest_memory(); + let ctx = WasiCtx::new(); + let host_memory = HostMemory::new(); let res = ints::cookie_cutter( - &mut ctx, - &mut guest_memory, + &ctx, + &host_memory, self.cookie.into(), self.return_ptr_loc.ptr as i32, ); assert_eq!(res, types::Errno::Ok.into(), "cookie cutter errno"); - let is_cookie_start = *guest_memory + let is_cookie_start = host_memory .ptr::(self.return_ptr_loc.ptr) - .expect("ptr to returned Bool") - .as_ref() + .read() .expect("deref to Bool value"); assert_eq!( diff --git a/tests/pointers.rs b/tests/pointers.rs index 3309cf7742..dc26ed33f6 100644 --- a/tests/pointers.rs +++ b/tests/pointers.rs @@ -1,5 +1,5 @@ use proptest::prelude::*; -use wiggle_runtime::{GuestError, GuestPtr, GuestPtrMut, GuestRefMut, GuestType}; +use wiggle_runtime::{GuestError, GuestMemory, GuestPtr}; use wiggle_test::{impl_errno, HostMemory, MemArea, WasiCtx}; wiggle::from_witx!({ @@ -10,49 +10,52 @@ wiggle::from_witx!({ impl_errno!(types::Errno); impl pointers::Pointers for WasiCtx { - fn pointers_and_enums( + fn pointers_and_enums<'a>( &self, input1: types::Excuse, - input2_ptr: GuestPtrMut, - input3_ptr: GuestPtr, - input4_ptr_ptr: GuestPtrMut>, + input2_ptr: GuestPtr<'a, types::Excuse>, + input3_ptr: GuestPtr<'a, types::Excuse>, + input4_ptr_ptr: GuestPtr<'a, GuestPtr<'a, types::Excuse>>, ) -> Result<(), types::Errno> { println!("BAZ input1 {:?}", input1); - // Read enum value from mutable: - let mut input2_ref: GuestRefMut = input2_ptr.as_ref_mut().map_err(|e| { + let input2: types::Excuse = input2_ptr.read().map_err(|e| { eprintln!("input2_ptr error: {}", e); types::Errno::InvalidArg })?; - let input2: types::Excuse = *input2_ref; println!("input2 {:?}", input2); // Read enum value from immutable ptr: - let input3 = *input3_ptr.as_ref().map_err(|e| { + let input3 = input3_ptr.read().map_err(|e| { eprintln!("input3_ptr error: {}", e); types::Errno::InvalidArg })?; println!("input3 {:?}", input3); // Write enum to mutable ptr: - *input2_ref = input3; + input2_ptr.write(input3).map_err(|e| { + eprintln!("input2_ptr error: {}", e); + types::Errno::InvalidArg + })?; println!("wrote to input2_ref {:?}", input3); // Read ptr value from mutable ptr: - let input4_ptr: GuestPtr = GuestType::read(&input4_ptr_ptr.as_immut()) - .map_err(|e| { - eprintln!("input4_ptr_ptr error: {}", e); - types::Errno::InvalidArg - })?; + let input4_ptr: GuestPtr = input4_ptr_ptr.read().map_err(|e| { + eprintln!("input4_ptr_ptr error: {}", e); + types::Errno::InvalidArg + })?; // Read enum value from that ptr: - let input4: types::Excuse = *input4_ptr.as_ref().map_err(|e| { + let input4: types::Excuse = input4_ptr.read().map_err(|e| { eprintln!("input4_ptr error: {}", e); types::Errno::InvalidArg })?; println!("input4 {:?}", input4); // Write ptr value to mutable ptr: - input4_ptr_ptr.write(&input2_ptr.as_immut()); + input4_ptr_ptr.write(input2_ptr).map_err(|e| { + eprintln!("input4_ptr_ptr error: {}", e); + types::Errno::InvalidArg + })?; Ok(()) } @@ -123,37 +126,32 @@ impl PointersAndEnumsExercise { .boxed() } pub fn test(&self) { - let mut ctx = WasiCtx::new(); - let mut host_memory = HostMemory::new(); - let mut guest_memory = host_memory.guest_memory(); + let ctx = WasiCtx::new(); + let host_memory = HostMemory::new(); - *guest_memory - .ptr_mut(self.input2_loc.ptr) - .expect("input2 ptr") - .as_ref_mut() - .expect("input2 ref_mut") = self.input2; + host_memory + .ptr(self.input2_loc.ptr) + .write(self.input2) + .expect("input2 ref_mut"); - *guest_memory - .ptr_mut(self.input3_loc.ptr) - .expect("input3 ptr") - .as_ref_mut() - .expect("input3 ref_mut") = self.input3; + host_memory + .ptr(self.input3_loc.ptr) + .write(self.input3) + .expect("input3 ref_mut"); - *guest_memory - .ptr_mut(self.input4_loc.ptr) - .expect("input4 ptr") - .as_ref_mut() - .expect("input4 ref_mut") = self.input4; + host_memory + .ptr(self.input4_loc.ptr) + .write(self.input4) + .expect("input4 ref_mut"); - *guest_memory - .ptr_mut(self.input4_ptr_loc.ptr) - .expect("input4 ptr ptr") - .as_ref_mut() - .expect("input4 ptr ref_mut") = self.input4_loc.ptr; + host_memory + .ptr(self.input4_ptr_loc.ptr) + .write(self.input4_loc.ptr) + .expect("input4 ptr ref_mut"); let e = pointers::pointers_and_enums( - &mut ctx, - &mut guest_memory, + &ctx, + &host_memory, self.input1.into(), self.input2_loc.ptr as i32, self.input3_loc.ptr as i32, @@ -162,10 +160,9 @@ impl PointersAndEnumsExercise { assert_eq!(e, types::Errno::Ok.into(), "errno"); // Implementation of pointers_and_enums writes input3 to the input2_loc: - let written_to_input2_loc: i32 = *guest_memory + let written_to_input2_loc: i32 = host_memory .ptr(self.input2_loc.ptr) - .expect("input2 ptr") - .as_ref() + .read() .expect("input2 ref"); assert_eq!( @@ -175,10 +172,9 @@ impl PointersAndEnumsExercise { ); // Implementation of pointers_and_enums writes input2_loc to input4_ptr_loc: - let written_to_input4_ptr: u32 = *guest_memory + let written_to_input4_ptr: u32 = host_memory .ptr(self.input4_ptr_loc.ptr) - .expect("input4_ptr_loc ptr") - .as_ref() + .read() .expect("input4_ptr_loc ref"); assert_eq!( diff --git a/tests/strings.rs b/tests/strings.rs index 9bcb634391..dc6526326e 100644 --- a/tests/strings.rs +++ b/tests/strings.rs @@ -1,5 +1,5 @@ use proptest::prelude::*; -use wiggle_runtime::{GuestError, GuestPtrMut, GuestString}; +use wiggle_runtime::{GuestError, GuestMemory, GuestPtr}; use wiggle_test::{impl_errno, HostMemory, MemArea, WasiCtx}; wiggle::from_witx!({ @@ -10,11 +10,12 @@ wiggle::from_witx!({ impl_errno!(types::Errno); impl strings::Strings for WasiCtx { - fn hello_string(&self, a_string: &GuestString<'_>) -> Result { - let as_ref = a_string.as_ref().expect("deref ptr should succeed"); - let as_str = as_ref.as_str().expect("valid UTF-8 string"); - println!("a_string='{}'", as_str); - Ok(as_str.len() as u32) + fn hello_string(&self, a_string: &GuestPtr) -> Result { + let s = a_string.as_raw().expect("should be valid string"); + unsafe { + println!("a_string='{}'", &*s); + Ok((*s).len() as u32) + } } } @@ -26,7 +27,6 @@ fn test_string_strategy() -> impl Strategy { struct HelloStringExercise { test_word: String, string_ptr_loc: MemArea, - string_len_loc: MemArea, return_ptr_loc: MemArea, } @@ -38,63 +38,43 @@ impl HelloStringExercise { Just(test_word.clone()), HostMemory::mem_area_strat(test_word.len() as u32), HostMemory::mem_area_strat(4), - HostMemory::mem_area_strat(4), ) }) - .prop_map( - |(test_word, string_ptr_loc, string_len_loc, return_ptr_loc)| Self { - test_word, - string_ptr_loc, - string_len_loc, - return_ptr_loc, - }, - ) + .prop_map(|(test_word, string_ptr_loc, return_ptr_loc)| Self { + test_word, + string_ptr_loc, + return_ptr_loc, + }) .prop_filter("non-overlapping pointers", |e| { - MemArea::non_overlapping_set(&[ - &e.string_ptr_loc, - &e.string_len_loc, - &e.return_ptr_loc, - ]) + MemArea::non_overlapping_set(&[&e.string_ptr_loc, &e.return_ptr_loc]) }) .boxed() } pub fn test(&self) { - let mut ctx = WasiCtx::new(); - let mut host_memory = HostMemory::new(); - let mut guest_memory = host_memory.guest_memory(); - - // Populate string length - *guest_memory - .ptr_mut(self.string_len_loc.ptr) - .expect("ptr mut to string len") - .as_ref_mut() - .expect("deref ptr mut to string len") = self.test_word.len() as u32; + let ctx = WasiCtx::new(); + let host_memory = HostMemory::new(); // Populate string in guest's memory - { - let mut next: GuestPtrMut<'_, u8> = guest_memory - .ptr_mut(self.string_ptr_loc.ptr) - .expect("ptr mut to the first byte of string"); - for byte in self.test_word.as_bytes() { - *next.as_ref_mut().expect("deref mut") = *byte; - next = next.elem(1).expect("increment ptr by 1"); - } + let ptr = host_memory.ptr::((self.string_ptr_loc.ptr, self.test_word.len() as u32)); + for (slot, byte) in ptr.as_bytes().iter().zip(self.test_word.bytes()) { + slot.expect("should be valid pointer") + .write(byte) + .expect("failed to write"); } let res = strings::hello_string( - &mut ctx, - &mut guest_memory, + &ctx, + &host_memory, self.string_ptr_loc.ptr as i32, - self.string_len_loc.ptr as i32, + self.test_word.len() as i32, self.return_ptr_loc.ptr as i32, ); assert_eq!(res, types::Errno::Ok.into(), "hello string errno"); - let given = *guest_memory + let given = host_memory .ptr::(self.return_ptr_loc.ptr) - .expect("ptr to return value") - .as_ref() + .read() .expect("deref ptr to return value"); assert_eq!(self.test_word.len() as u32, given); } diff --git a/tests/structs.rs b/tests/structs.rs index 511e2fffbd..b24bbee9bf 100644 --- a/tests/structs.rs +++ b/tests/structs.rs @@ -1,5 +1,5 @@ use proptest::prelude::*; -use wiggle_runtime::{GuestError, GuestPtr}; +use wiggle_runtime::{GuestError, GuestMemory, GuestPtr}; use wiggle_test::{impl_errno, HostMemory, MemArea, WasiCtx}; wiggle::from_witx!({ @@ -15,21 +15,21 @@ impl structs::Structs for WasiCtx { } fn sum_of_pair_of_ptrs(&self, an_pair: &types::PairIntPtrs) -> Result { - let first = *an_pair + let first = an_pair .first - .as_ref() + .read() .expect("dereferencing GuestPtr should succeed"); - let second = *an_pair + let second = an_pair .second - .as_ref() + .read() .expect("dereferncing GuestPtr should succeed"); Ok(first as i64 + second as i64) } fn sum_of_int_and_ptr(&self, an_pair: &types::PairIntAndPtr) -> Result { - let first = *an_pair + let first = an_pair .first - .as_ref() + .read() .expect("dereferencing GuestPtr should succeed"); let second = an_pair.second as i64; Ok(first as i64 + second) @@ -78,33 +78,29 @@ impl SumOfPairExercise { } pub fn test(&self) { - let mut ctx = WasiCtx::new(); - let mut host_memory = HostMemory::new(); - let mut guest_memory = host_memory.guest_memory(); + let ctx = WasiCtx::new(); + let host_memory = HostMemory::new(); - *guest_memory - .ptr_mut(self.input_loc.ptr) - .expect("input ptr") - .as_ref_mut() - .expect("input ref_mut") = self.input.first; - *guest_memory - .ptr_mut(self.input_loc.ptr + 4) - .expect("input ptr") - .as_ref_mut() - .expect("input ref_mut") = self.input.second; + host_memory + .ptr(self.input_loc.ptr) + .write(self.input.first) + .expect("input ref_mut"); + host_memory + .ptr(self.input_loc.ptr + 4) + .write(self.input.second) + .expect("input ref_mut"); let sum_err = structs::sum_of_pair( - &mut ctx, - &mut guest_memory, + &ctx, + &host_memory, self.input_loc.ptr as i32, self.return_loc.ptr as i32, ); assert_eq!(sum_err, types::Errno::Ok.into(), "sum errno"); - let return_val: i64 = *guest_memory + let return_val: i64 = host_memory .ptr(self.return_loc.ptr) - .expect("return ptr") - .as_ref() + .read() .expect("return ref"); assert_eq!( @@ -170,45 +166,39 @@ impl SumPairPtrsExercise { .boxed() } pub fn test(&self) { - let mut ctx = WasiCtx::new(); - let mut host_memory = HostMemory::new(); - let mut guest_memory = host_memory.guest_memory(); + let ctx = WasiCtx::new(); + let host_memory = HostMemory::new(); - *guest_memory - .ptr_mut(self.input_first_loc.ptr) - .expect("input_first ptr") - .as_ref_mut() - .expect("input_first ref") = self.input_first; - *guest_memory - .ptr_mut(self.input_second_loc.ptr) - .expect("input_second ptr") - .as_ref_mut() - .expect("input_second ref") = self.input_second; + host_memory + .ptr(self.input_first_loc.ptr) + .write(self.input_first) + .expect("input_first ref"); + host_memory + .ptr(self.input_second_loc.ptr) + .write(self.input_second) + .expect("input_second ref"); - *guest_memory - .ptr_mut(self.input_struct_loc.ptr) - .expect("input_struct ptr") - .as_ref_mut() - .expect("input_struct ref") = self.input_first_loc.ptr; - *guest_memory - .ptr_mut(self.input_struct_loc.ptr + 4) - .expect("input_struct ptr") - .as_ref_mut() - .expect("input_struct ref") = self.input_second_loc.ptr; + host_memory + .ptr(self.input_struct_loc.ptr) + .write(self.input_first_loc.ptr) + .expect("input_struct ref"); + host_memory + .ptr(self.input_struct_loc.ptr + 4) + .write(self.input_second_loc.ptr) + .expect("input_struct ref"); let res = structs::sum_of_pair_of_ptrs( - &mut ctx, - &mut guest_memory, + &ctx, + &host_memory, self.input_struct_loc.ptr as i32, self.return_loc.ptr as i32, ); assert_eq!(res, types::Errno::Ok.into(), "sum of pair of ptrs errno"); - let doubled: i64 = *guest_memory + let doubled: i64 = host_memory .ptr(self.return_loc.ptr) - .expect("return ptr") - .as_ref() + .read() .expect("return ref"); assert_eq!( @@ -264,39 +254,34 @@ impl SumIntAndPtrExercise { .boxed() } pub fn test(&self) { - let mut ctx = WasiCtx::new(); - let mut host_memory = HostMemory::new(); - let mut guest_memory = host_memory.guest_memory(); + let ctx = WasiCtx::new(); + let host_memory = HostMemory::new(); - *guest_memory - .ptr_mut(self.input_first_loc.ptr) - .expect("input_first ptr") - .as_ref_mut() - .expect("input_first ref") = self.input_first; - *guest_memory - .ptr_mut(self.input_struct_loc.ptr) - .expect("input_struct ptr") - .as_ref_mut() - .expect("input_struct ref") = self.input_first_loc.ptr; - *guest_memory - .ptr_mut(self.input_struct_loc.ptr + 4) - .expect("input_struct ptr") - .as_ref_mut() - .expect("input_struct ref") = self.input_second; + host_memory + .ptr(self.input_first_loc.ptr) + .write(self.input_first) + .expect("input_first ref"); + host_memory + .ptr(self.input_struct_loc.ptr) + .write(self.input_first_loc.ptr) + .expect("input_struct ref"); + host_memory + .ptr(self.input_struct_loc.ptr + 4) + .write(self.input_second) + .expect("input_struct ref"); let res = structs::sum_of_int_and_ptr( - &mut ctx, - &mut guest_memory, + &ctx, + &host_memory, self.input_struct_loc.ptr as i32, self.return_loc.ptr as i32, ); assert_eq!(res, types::Errno::Ok.into(), "sum of int and ptr errno"); - let doubled: i64 = *guest_memory + let doubled: i64 = host_memory .ptr(self.return_loc.ptr) - .expect("return ptr") - .as_ref() + .read() .expect("return ref"); assert_eq!( @@ -326,19 +311,16 @@ impl ReturnPairInts { } pub fn test(&self) { - let mut ctx = WasiCtx::new(); - let mut host_memory = HostMemory::new(); - let mut guest_memory = host_memory.guest_memory(); + let ctx = WasiCtx::new(); + let host_memory = HostMemory::new(); - let err = - structs::return_pair_ints(&mut ctx, &mut guest_memory, self.return_loc.ptr as i32); + let err = structs::return_pair_ints(&ctx, &host_memory, self.return_loc.ptr as i32); assert_eq!(err, types::Errno::Ok.into(), "return struct errno"); - let return_struct: types::PairInts = *guest_memory + let return_struct: types::PairInts = host_memory .ptr(self.return_loc.ptr) - .expect("return ptr") - .as_ref() + .read() .expect("return ref"); assert_eq!( @@ -398,24 +380,21 @@ impl ReturnPairPtrsExercise { .boxed() } pub fn test(&self) { - let mut ctx = WasiCtx::new(); - let mut host_memory = HostMemory::new(); - let mut guest_memory = host_memory.guest_memory(); + let ctx = WasiCtx::new(); + let host_memory = HostMemory::new(); - *guest_memory - .ptr_mut(self.input_first_loc.ptr) - .expect("input_first ptr") - .as_ref_mut() - .expect("input_first ref") = self.input_first; - *guest_memory - .ptr_mut(self.input_second_loc.ptr) - .expect("input_second ptr") - .as_ref_mut() - .expect("input_second ref") = self.input_second; + host_memory + .ptr(self.input_first_loc.ptr) + .write(self.input_first) + .expect("input_first ref"); + host_memory + .ptr(self.input_second_loc.ptr) + .write(self.input_second) + .expect("input_second ref"); let res = structs::return_pair_of_ptrs( - &mut ctx, - &mut guest_memory, + &ctx, + &host_memory, self.input_first_loc.ptr as i32, self.input_second_loc.ptr as i32, self.return_loc.ptr as i32, @@ -423,28 +402,20 @@ impl ReturnPairPtrsExercise { assert_eq!(res, types::Errno::Ok.into(), "return pair of ptrs errno"); - let ptr_pair_int_ptrs: GuestPtr> = - guest_memory.ptr(self.return_loc.ptr).expect("return ptr"); - let ret_first_ptr: GuestPtr = ptr_pair_int_ptrs - .cast::>(0u32) - .expect("extract ptr to first element in struct") - .read() - .expect("read ptr to first element in struct"); - let ret_second_ptr: GuestPtr = ptr_pair_int_ptrs - .cast::>(4u32) - .expect("extract ptr to second element in struct") - .read() - .expect("read ptr to second element in struct"); + let ptr_pair_int_ptrs: types::PairIntPtrs<'_> = + host_memory.ptr(self.return_loc.ptr).read().expect("failed to read return location"); + let ret_first_ptr = ptr_pair_int_ptrs.first; + let ret_second_ptr = ptr_pair_int_ptrs.second; assert_eq!( self.input_first, - *ret_first_ptr - .as_ref() + ret_first_ptr + .read() .expect("deref extracted ptr to first element") ); assert_eq!( self.input_second, - *ret_second_ptr - .as_ref() + ret_second_ptr + .read() .expect("deref extracted ptr to second element") ); } diff --git a/tests/union.rs b/tests/union.rs index 85e63a7a21..6081b0d4e7 100644 --- a/tests/union.rs +++ b/tests/union.rs @@ -1,5 +1,5 @@ use proptest::prelude::*; -use wiggle_runtime::{GuestError, GuestType}; +use wiggle_runtime::{GuestError, GuestMemory, GuestType}; use wiggle_test::{impl_errno, HostMemory, MemArea, WasiCtx}; wiggle::from_witx!({ @@ -43,16 +43,16 @@ impl union_example::UnionExample for WasiCtx { fn reason_mult(&self, u: &types::ReasonMut<'_>, multiply_by: u32) -> Result<(), types::Errno> { match u { types::ReasonMut::DogAte(fptr) => { - let mut f = fptr.as_ref_mut().expect("valid pointer"); - let val = *f; + let val = fptr.read().expect("valid pointer"); println!("REASON MULT DogAte({})", val); - *f = mult_zero_nan(val, multiply_by); + fptr.write(mult_zero_nan(val, multiply_by)) + .expect("valid pointer"); } types::ReasonMut::Traffic(iptr) => { - let mut i = iptr.as_ref_mut().expect("valid pointer"); - let val: i32 = *i; + let val = iptr.read().expect("valid pointer"); println!("REASON MULT Traffic({})", val); - *i = mult_lose_overflow(val, multiply_by); + iptr.write(mult_lose_overflow(val, multiply_by)) + .expect("valid pointer"); } types::ReasonMut::Sleeping => { println!("REASON MULT Sleeping"); @@ -90,8 +90,8 @@ impl GetTagExercise { pub fn strat() -> BoxedStrategy { ( reason_strat(), - HostMemory::mem_area_strat(types::Reason::size()), - HostMemory::mem_area_strat(types::Excuse::size()), + HostMemory::mem_area_strat(types::Reason::guest_size()), + HostMemory::mem_area_strat(types::Excuse::guest_size()), ) .prop_map(|(input, input_loc, return_loc)| GetTagExercise { input, @@ -105,46 +105,39 @@ impl GetTagExercise { } pub fn test(&self) { - let mut ctx = WasiCtx::new(); - let mut host_memory = HostMemory::new(); - let mut guest_memory = host_memory.guest_memory(); + let ctx = WasiCtx::new(); + let host_memory = HostMemory::new(); let discriminant: u8 = reason_tag(&self.input).into(); - *guest_memory - .ptr_mut(self.input_loc.ptr) - .expect("input discriminant ptr") - .as_ref_mut() - .expect("input discriminant ref_mut") = discriminant; + host_memory + .ptr(self.input_loc.ptr) + .write(discriminant) + .expect("input discriminant ptr"); match self.input { types::Reason::DogAte(f) => { - *guest_memory - .ptr_mut(self.input_loc.ptr + 4) - .expect("input contents ptr") - .as_ref_mut() - .expect("input contents ref_mut") = f; - } - types::Reason::Traffic(v) => { - *guest_memory - .ptr_mut(self.input_loc.ptr + 4) - .expect("input contents ptr") - .as_ref_mut() - .expect("input contents ref_mut") = v; + host_memory + .ptr(self.input_loc.ptr + 4) + .write(f) + .expect("input contents ref_mut"); } + types::Reason::Traffic(v) => host_memory + .ptr(self.input_loc.ptr + 4) + .write(v) + .expect("input contents ref_mut"), types::Reason::Sleeping => {} // Do nothing } let e = union_example::get_tag( - &mut ctx, - &mut guest_memory, + &ctx, + &host_memory, self.input_loc.ptr as i32, self.return_loc.ptr as i32, ); assert_eq!(e, types::Errno::Ok.into(), "get_tag errno"); - let return_val: types::Excuse = *guest_memory + let return_val: types::Excuse = host_memory .ptr(self.return_loc.ptr) - .expect("return ptr") - .as_ref() + .read() .expect("return ref"); assert_eq!(return_val, reason_tag(&self.input), "get_tag return value"); @@ -170,7 +163,7 @@ impl ReasonMultExercise { pub fn strat() -> BoxedStrategy { ( reason_strat(), - HostMemory::mem_area_strat(types::Reason::size()), + HostMemory::mem_area_strat(types::Reason::guest_size()), HostMemory::mem_area_strat(4), prop::num::u32::ANY, ) @@ -189,42 +182,37 @@ impl ReasonMultExercise { } pub fn test(&self) { - let mut ctx = WasiCtx::new(); - let mut host_memory = HostMemory::new(); - let mut guest_memory = host_memory.guest_memory(); + let ctx = WasiCtx::new(); + let host_memory = HostMemory::new(); let discriminant: u8 = reason_tag(&self.input).into(); - *guest_memory - .ptr_mut(self.input_loc.ptr) - .expect("input discriminant ptr") - .as_ref_mut() - .expect("input discriminant ref_mut") = discriminant; - *guest_memory - .ptr_mut(self.input_loc.ptr + 4) - .expect("input pointer ptr") - .as_ref_mut() - .expect("input pointer ref_mut") = self.input_pointee_loc.ptr; + host_memory + .ptr(self.input_loc.ptr) + .write(discriminant) + .expect("input discriminant ref_mut"); + host_memory + .ptr(self.input_loc.ptr + 4) + .write(self.input_pointee_loc.ptr) + .expect("input pointer ref_mut"); match self.input { types::Reason::DogAte(f) => { - *guest_memory - .ptr_mut(self.input_pointee_loc.ptr) - .expect("input contents ptr") - .as_ref_mut() - .expect("input contents ref_mut") = f; + host_memory + .ptr(self.input_pointee_loc.ptr) + .write(f) + .expect("input contents ref_mut"); } types::Reason::Traffic(v) => { - *guest_memory - .ptr_mut(self.input_pointee_loc.ptr) - .expect("input contents ptr") - .as_ref_mut() - .expect("input contents ref_mut") = v; + host_memory + .ptr(self.input_pointee_loc.ptr) + .write(v) + .expect("input contents ref_mut"); } types::Reason::Sleeping => {} // Do nothing } let e = union_example::reason_mult( - &mut ctx, - &mut guest_memory, + &ctx, + &host_memory, self.input_loc.ptr as i32, self.multiply_by as i32, ); @@ -233,10 +221,9 @@ impl ReasonMultExercise { match self.input { types::Reason::DogAte(f) => { - let f_result: f32 = *guest_memory + let f_result: f32 = host_memory .ptr(self.input_pointee_loc.ptr) - .expect("input contents ptr") - .as_ref() + .read() .expect("input contents ref_mut"); assert_eq!( mult_zero_nan(f, self.multiply_by), @@ -245,10 +232,9 @@ impl ReasonMultExercise { ) } types::Reason::Traffic(v) => { - let v_result: i32 = *guest_memory + let v_result: i32 = host_memory .ptr(self.input_pointee_loc.ptr) - .expect("input contents ptr") - .as_ref() + .read() .expect("input contents ref_mut"); assert_eq!( mult_lose_overflow(v, self.multiply_by), diff --git a/tests/wasi.rs b/tests/wasi.rs index 16042ce86e..daef095b23 100644 --- a/tests/wasi.rs +++ b/tests/wasi.rs @@ -1,4 +1,4 @@ -use wiggle_runtime::{GuestError, GuestErrorType, GuestPtr, GuestPtrMut, GuestString}; +use wiggle_runtime::{GuestError, GuestErrorType, GuestPtr}; use wiggle_test::WasiCtx; wiggle::from_witx!({ @@ -25,8 +25,8 @@ impl GuestErrorType for types::Errno { impl crate::wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { fn args_get( &self, - _argv: GuestPtrMut>, - _argv_buf: GuestPtrMut, + _argv: GuestPtr>, + _argv_buf: GuestPtr, ) -> Result<()> { unimplemented!("args_get") } @@ -37,8 +37,8 @@ impl crate::wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { fn environ_get( &self, - _environ: GuestPtrMut>, - _environ_buf: GuestPtrMut, + _environ: GuestPtr>, + _environ_buf: GuestPtr, ) -> Result<()> { unimplemented!("environ_get") } @@ -137,7 +137,7 @@ impl crate::wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { fn fd_prestat_dir_name( &self, _fd: types::Fd, - _path: GuestPtrMut, + _path: GuestPtr, _path_len: types::Size, ) -> Result<()> { unimplemented!("fd_prestat_dir_name") @@ -159,7 +159,7 @@ impl crate::wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { fn fd_readdir( &self, _fd: types::Fd, - _buf: GuestPtrMut, + _buf: GuestPtr, _buf_len: types::Size, _cookie: types::Dircookie, ) -> Result { @@ -191,7 +191,7 @@ impl crate::wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { unimplemented!("fd_write") } - fn path_create_directory(&self, _fd: types::Fd, _path: &GuestString<'_>) -> Result<()> { + fn path_create_directory(&self, _fd: types::Fd, _path: &GuestPtr<'_, str>) -> Result<()> { unimplemented!("path_create_directory") } @@ -199,7 +199,7 @@ impl crate::wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { &self, _fd: types::Fd, _flags: types::Lookupflags, - _path: &GuestString<'_>, + _path: &GuestPtr<'_, str>, ) -> Result { unimplemented!("path_filestat_get") } @@ -208,7 +208,7 @@ impl crate::wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { &self, _fd: types::Fd, _flags: types::Lookupflags, - _path: &GuestString<'_>, + _path: &GuestPtr<'_, str>, _atim: types::Timestamp, _mtim: types::Timestamp, _fst_flags: types::Fstflags, @@ -220,9 +220,9 @@ impl crate::wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { &self, _old_fd: types::Fd, _old_flags: types::Lookupflags, - _old_path: &GuestString<'_>, + _old_path: &GuestPtr<'_, str>, _new_fd: types::Fd, - _new_path: &GuestString<'_>, + _new_path: &GuestPtr<'_, str>, ) -> Result<()> { unimplemented!("path_link") } @@ -231,7 +231,7 @@ impl crate::wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { &self, _fd: types::Fd, _dirflags: types::Lookupflags, - _path: &GuestString<'_>, + _path: &GuestPtr<'_, str>, _oflags: types::Oflags, _fs_rights_base: types::Rights, _fs_rights_inherting: types::Rights, @@ -243,44 +243,44 @@ impl crate::wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { fn path_readlink( &self, _fd: types::Fd, - _path: &GuestString<'_>, - _buf: GuestPtrMut, + _path: &GuestPtr<'_, str>, + _buf: GuestPtr, _buf_len: types::Size, ) -> Result { unimplemented!("path_readlink") } - fn path_remove_directory(&self, _fd: types::Fd, _path: &GuestString<'_>) -> Result<()> { + fn path_remove_directory(&self, _fd: types::Fd, _path: &GuestPtr<'_, str>) -> Result<()> { unimplemented!("path_remove_directory") } fn path_rename( &self, _fd: types::Fd, - _old_path: &GuestString<'_>, + _old_path: &GuestPtr<'_, str>, _new_fd: types::Fd, - _new_path: &GuestString<'_>, + _new_path: &GuestPtr<'_, str>, ) -> Result<()> { unimplemented!("path_rename") } fn path_symlink( &self, - _old_path: &GuestString<'_>, + _old_path: &GuestPtr<'_, str>, _fd: types::Fd, - _new_path: &GuestString<'_>, + _new_path: &GuestPtr<'_, str>, ) -> Result<()> { unimplemented!("path_symlink") } - fn path_unlink_file(&self, _fd: types::Fd, _path: &GuestString<'_>) -> Result<()> { + fn path_unlink_file(&self, _fd: types::Fd, _path: &GuestPtr<'_, str>) -> Result<()> { unimplemented!("path_unlink_file") } fn poll_oneoff( &self, _in_: GuestPtr, - _out: GuestPtrMut, + _out: GuestPtr, _nsubscriptions: types::Size, ) -> Result { unimplemented!("poll_oneoff") @@ -298,7 +298,7 @@ impl crate::wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { unimplemented!("sched_yield") } - fn random_get(&self, _buf: GuestPtrMut, _buf_len: types::Size) -> Result<()> { + fn random_get(&self, _buf: GuestPtr, _buf_len: types::Size) -> Result<()> { unimplemented!("random_get") } From 8cee5475197cf917b6490be81c0f23e4baf2a33d Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Wed, 4 Mar 2020 10:36:26 -0800 Subject: [PATCH 79/86] Run rustfmt --- crates/generate/src/types/mod.rs | 9 +++------ crates/runtime/src/lib.rs | 20 +++++++++++++------- crates/test/src/lib.rs | 4 +++- tests/handles.rs | 2 +- tests/structs.rs | 8 +++++--- tests/wasi.rs | 6 +----- 6 files changed, 26 insertions(+), 23 deletions(-) diff --git a/crates/generate/src/types/mod.rs b/crates/generate/src/types/mod.rs index 07c748b1d7..93e7d5f892 100644 --- a/crates/generate/src/types/mod.rs +++ b/crates/generate/src/types/mod.rs @@ -22,12 +22,9 @@ pub fn define_datatype(names: &Names, namedtype: &witx::NamedType) -> TokenStrea witx::Type::Union(u) => union::define_union(names, &namedtype.name, &u), witx::Type::Handle(h) => handle::define_handle(names, &namedtype.name, &h), witx::Type::Builtin(b) => define_builtin(names, &namedtype.name, *b), - witx::Type::Pointer(p) => define_witx_pointer( - names, - &namedtype.name, - quote!(wiggle_runtime::GuestPtr), - p, - ), + witx::Type::Pointer(p) => { + define_witx_pointer(names, &namedtype.name, quote!(wiggle_runtime::GuestPtr), p) + } witx::Type::ConstPointer(p) => { define_witx_pointer(names, &namedtype.name, quote!(wiggle_runtime::GuestPtr), p) } diff --git a/crates/runtime/src/lib.rs b/crates/runtime/src/lib.rs index 93c45fbe87..1422a5f84b 100644 --- a/crates/runtime/src/lib.rs +++ b/crates/runtime/src/lib.rs @@ -1,8 +1,8 @@ use std::cell::Cell; +use std::fmt; +use std::marker; use std::slice; use std::str; -use std::marker; -use std::fmt; mod error; mod guest_type; @@ -110,9 +110,11 @@ impl<'a, T: ?Sized + Pointee> GuestPtr<'a, T> { } pub fn add(&self, amt: u32) -> Result, GuestError> - where T: GuestType<'a> + Pointee, + where + T: GuestType<'a> + Pointee, { - let offset = amt.checked_mul(T::guest_size()) + let offset = amt + .checked_mul(T::guest_size()) .and_then(|o| self.pointer.checked_add(o)); let offset = match offset { Some(o) => o, @@ -131,7 +133,9 @@ impl<'a, T> GuestPtr<'a, [T]> { self.pointer.1 } - pub fn iter<'b>(&'b self) -> impl ExactSizeIterator, GuestError>> + 'b + pub fn iter<'b>( + &'b self, + ) -> impl ExactSizeIterator, GuestError>> + 'b where T: GuestType<'a>, { @@ -154,14 +158,16 @@ impl<'a> GuestPtr<'a, str> { } pub fn as_raw(&self) -> Result<*mut str, GuestError> { - let ptr = self.mem.validate_size_align(self.pointer.0, 1, self.pointer.1)?; + let ptr = self + .mem + .validate_size_align(self.pointer.0, 1, self.pointer.1)?; // TODO: doc unsafety here unsafe { let s = slice::from_raw_parts_mut(ptr, self.pointer.1 as usize); match str::from_utf8_mut(s) { Ok(s) => Ok(s), - Err(e) => Err(GuestError::InvalidUtf8(e)) + Err(e) => Err(GuestError::InvalidUtf8(e)), } } } diff --git a/crates/test/src/lib.rs b/crates/test/src/lib.rs index bf72036c6f..281ceb826e 100644 --- a/crates/test/src/lib.rs +++ b/crates/test/src/lib.rs @@ -8,7 +8,9 @@ pub struct HostMemory { } impl HostMemory { pub fn new() -> Self { - HostMemory { buffer: UnsafeCell::new([0; 4096]) } + HostMemory { + buffer: UnsafeCell::new([0; 4096]), + } } pub fn mem_area_strat(align: u32) -> BoxedStrategy { diff --git a/tests/handles.rs b/tests/handles.rs index 23037f38cf..9cb391a190 100644 --- a/tests/handles.rs +++ b/tests/handles.rs @@ -1,5 +1,5 @@ use proptest::prelude::*; -use wiggle_runtime::{GuestError, GuestType, GuestMemory}; +use wiggle_runtime::{GuestError, GuestMemory, GuestType}; use wiggle_test::{impl_errno, HostMemory, MemArea, WasiCtx}; const FD_VAL: u32 = 123; diff --git a/tests/structs.rs b/tests/structs.rs index b24bbee9bf..420d02561e 100644 --- a/tests/structs.rs +++ b/tests/structs.rs @@ -402,9 +402,11 @@ impl ReturnPairPtrsExercise { assert_eq!(res, types::Errno::Ok.into(), "return pair of ptrs errno"); - let ptr_pair_int_ptrs: types::PairIntPtrs<'_> = - host_memory.ptr(self.return_loc.ptr).read().expect("failed to read return location"); - let ret_first_ptr = ptr_pair_int_ptrs.first; + let ptr_pair_int_ptrs: types::PairIntPtrs<'_> = host_memory + .ptr(self.return_loc.ptr) + .read() + .expect("failed to read return location"); + let ret_first_ptr = ptr_pair_int_ptrs.first; let ret_second_ptr = ptr_pair_int_ptrs.second; assert_eq!( self.input_first, diff --git a/tests/wasi.rs b/tests/wasi.rs index daef095b23..6841926b9d 100644 --- a/tests/wasi.rs +++ b/tests/wasi.rs @@ -23,11 +23,7 @@ impl GuestErrorType for types::Errno { } impl crate::wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { - fn args_get( - &self, - _argv: GuestPtr>, - _argv_buf: GuestPtr, - ) -> Result<()> { + fn args_get(&self, _argv: GuestPtr>, _argv_buf: GuestPtr) -> Result<()> { unimplemented!("args_get") } From 84bcbd4650f5939d9143529b2b3986b42870e22e Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Wed, 4 Mar 2020 10:37:10 -0800 Subject: [PATCH 80/86] Fix some tests --- crates/test/src/lib.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/test/src/lib.rs b/crates/test/src/lib.rs index 281ceb826e..c946df73ba 100644 --- a/crates/test/src/lib.rs +++ b/crates/test/src/lib.rs @@ -92,10 +92,10 @@ mod test { use super::*; #[test] fn hostmemory_is_aligned() { - let mut h = HostMemory::new(); - assert_eq!(h.buffer.as_mut_ptr() as usize % 4096, 0); - let mut h = Box::new(HostMemory::new()); - assert_eq!(h.buffer.as_mut_ptr() as usize % 4096, 0); + let h = HostMemory::new(); + assert_eq!(h.base().0 as usize % 4096, 0); + let h = Box::new(h); + assert_eq!(h.base().0 as usize % 4096, 0); } } From 92a6636b6d7428e6931f11f516ef4d18098a95f5 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 5 Mar 2020 08:25:04 -0800 Subject: [PATCH 81/86] Add more thorough safety documentation to types --- crates/runtime/src/guest_type.rs | 43 +++++- crates/runtime/src/lib.rs | 254 ++++++++++++++++++++++++++++++- 2 files changed, 290 insertions(+), 7 deletions(-) diff --git a/crates/runtime/src/guest_type.rs b/crates/runtime/src/guest_type.rs index b23830d062..c7517bf0ea 100644 --- a/crates/runtime/src/guest_type.rs +++ b/crates/runtime/src/guest_type.rs @@ -7,10 +7,36 @@ pub trait GuestErrorType { fn from_error(e: GuestError, ctx: &Self::Context) -> Self; } +/// A trait for types that are intended to be pointees in `GuestPtr`. +/// +/// This trait abstracts how to read/write information from the guest memory, as +/// well as how to offset elements in an array of guest memory. This layer of +/// abstraction allows the guest representation of a type to be different from +/// the host representation of a type, if necessary. It also allows for +/// validation when reading/writing. pub trait GuestType<'a>: Sized { + /// Returns the size, in bytes, of this type in the guest memory. fn guest_size() -> u32; + + /// Returns the required alignment of this type, in bytes, for both guest + /// and host memory. fn guest_align() -> usize; + + /// Reads this value from the provided `ptr`. + /// + /// Must internally perform any safety checks necessary and is allowed to + /// fail if the bytes pointed to are also invalid. + /// + /// Typically if you're implementing this by hand you'll want to delegate to + /// other safe implementations of this trait (e.g. for primitive types like + /// `u32`) rather than writing lots of raw code yourself. fn read(ptr: &GuestPtr<'a, Self>) -> Result; + + /// Writes a value to `ptr` after verifying that `ptr` is indeed valid to + /// store `val`. + /// + /// Similar to `read`, you'll probably want to implement this in terms of + /// other primitives. fn write(ptr: &GuestPtr<'_, Self>, val: Self) -> Result<(), GuestError>; } @@ -20,14 +46,14 @@ macro_rules! primitives { fn guest_size() -> u32 { mem::size_of::() as u32 } fn guest_align() -> usize { mem::align_of::() } + #[inline] fn read(ptr: &GuestPtr<'a, Self>) -> Result { - // Any bit pattern for any primitive implemented with this - // macro is safe, so our `as_raw` method will guarantee that if - // we are given a pointer it's valid for the size of our type - // as well as properly aligned. Consequently we should be able - // to safely ready the pointer just after we validated it, - // returning it along here. + // macro is safe, so our `validate_size_align` method will + // guarantee that if we are given a pointer it's valid for the + // size of our type as well as properly aligned. Consequently we + // should be able to safely ready the pointer just after we + // validated it, returning it along here. let host_ptr = ptr.mem().validate_size_align( ptr.offset(), Self::guest_align(), @@ -36,6 +62,7 @@ macro_rules! primitives { Ok(unsafe { *host_ptr.cast::() }) } + #[inline] fn write(ptr: &GuestPtr<'_, Self>, val: Self) -> Result<(), GuestError> { let host_ptr = ptr.mem().validate_size_align( ptr.offset(), @@ -55,8 +82,11 @@ macro_rules! primitives { } primitives! { + // signed i8 i16 i32 i64 i128 isize + // unsigned u8 u16 u32 u64 u128 usize + // floats f32 f64 } @@ -65,6 +95,7 @@ impl<'a, T> GuestType<'a> for GuestPtr<'a, T> { fn guest_size() -> u32 { u32::guest_size() } + fn guest_align() -> usize { u32::guest_align() } diff --git a/crates/runtime/src/lib.rs b/crates/runtime/src/lib.rs index 1422a5f84b..b57a5bfe08 100644 --- a/crates/runtime/src/lib.rs +++ b/crates/runtime/src/lib.rs @@ -1,8 +1,10 @@ use std::cell::Cell; use std::fmt; use std::marker; +use std::rc::Rc; use std::slice; use std::str; +use std::sync::Arc; mod error; mod guest_type; @@ -11,9 +13,100 @@ pub use error::GuestError; pub use guest_type::{GuestErrorType, GuestType}; pub use region::Region; +/// A trait which abstracts how to get at the region of host memory taht +/// contains guest memory. +/// +/// All `GuestPtr` types will contain a handle to this trait, signifying where +/// the pointer is actually pointing into. This type will need to be implemented +/// for the host's memory storage object. +/// +/// # Safety +/// +/// Safety around this type is tricky, and the trait is `unsafe` since there are +/// a few contracts you need to uphold to implement this type correctly and have +/// everything else in this crate work out safely. +/// +/// The most important method of this trait is the `base` method. This returns, +/// in host memory, a pointer and a length. The pointer should point to valid +/// memory for the guest to read/write for the length contiguous bytes +/// afterwards. +/// +/// The region returned by `base` must not only be valid, however, but it must +/// be valid for "a period of time before the guest is reentered". This isn't +/// exactly well defined but the general idea is that `GuestMemory` is allowed +/// to change under our feet to accomodate instructions like `memory.grow` or +/// other guest modifications. Memory, however, cannot be changed if the guest +/// is not reentered or if no explicitly action is taken to modify the guest +/// memory. +/// +/// This provides the guarantee that host pointers based on the return value of +/// `base` have a dynamic period for which they are valid. This time duration +/// must be "somehow nonzero in length" to allow users of `GuestMemory` and +/// `GuestPtr` to safely read and write interior data. +/// +/// # Using Raw Pointers +/// +/// Methods like [`GuestMemory::base`] or [`GuestPtr::as_raw`] will return raw +/// pointers to use. Returning raw pointers is significant because it shows +/// there are hazards with using the returned pointers, and they can't blanket +/// be used in a safe fashion. It is possible to use these pointers safely, but +/// any usage needs to uphold a few guarantees. +/// +/// * Whenever a `*mut T` is accessed or modified, it must be guaranteed that +/// since the pointer was originally obtained the guest memory wasn't +/// relocated in any way. This means you can't call back into the guest, call +/// other arbitrary functions which might call into the guest, etc. The +/// problem here is that the guest could execute instructions like +/// `memory.grow` which would invalidate the raw pointer. If, however, after +/// you acquire `*mut T` you only execute your own code and it doesn't touch +/// the guest, then `*mut T` is still guaranteed to point to valid code. +/// +/// * Furthermore, Rust's aliasing rules must still be upheld. For example you +/// can't have two `&mut T` types that point to the area or overlap in any +/// way. This in particular becomes an issue when you're dealing with multiple +/// `GuestPtr` types. If you want to simultaneously work with them then you +/// need to dynamically validate that you're either working with them all in a +/// shared fashion (e.g. as if they were `&T`) or you must verify that they do +/// not overlap to work with them as `&mut T`. +/// +/// Note that safely using the raw pointers is relatively difficult. This crate +/// strives to provide utilities to safely work with guest pointers so long as +/// the previous guarantees are all upheld. If advanced operations are done with +/// guest pointers it's recommended to be extremely cautious and thoroughly +/// consider possible ramifications with respect to this API before codifying +/// implementation details. pub unsafe trait GuestMemory { + /// Returns the base allocation of this guest memory, located in host + /// memory. + /// + /// A pointer/length pair are returned to signify where the guest memory + /// lives in the host, and how many contiguous bytes the memory is valid for + /// after the returned pointer. + /// + /// Note that there are safety guarantees about this method that + /// implementations must uphold, and for more details see the + /// [`GuestMemory`] documentation. fn base(&self) -> (*mut u8, u32); + /// Validates a guest-relative pointer given various attributes, and returns + /// the corresponding host pointer. + /// + /// * `offset` - this is the guest-relative pointer, an offset from the + /// base. + /// * `align` - this is the desired alignment of the guest pointer, and if + /// successful the host pointer will be guaranteed to have this alignment. + /// * `len` - this is the number of bytes, after `offset`, that the returned + /// pointer must be valid for. + /// + /// This function will guarantee that the returned pointer is in-bounds of + /// `base`, *at this time*, for `len` bytes and has alignment `align`. If + /// any guarantees are not upheld then an error will be returned. + /// + /// Note that the returned pointer is an unsafe pointer. This is not safe to + /// use in general because guest memory can be relocated. Additionally the + /// guest may be modifying/reading memory as well. Consult the + /// [`GuestMemory`] documentation for safety information about using this + /// returned pointer. fn validate_size_align( &self, offset: u32, @@ -44,6 +137,11 @@ pub unsafe trait GuestMemory { Ok(start as *mut u8) } + /// Convenience method for creating a `GuestPtr` at a particular offset. + /// + /// Note that `T` can be almost any type, and typically `offset` is a `u32`. + /// The exception is slices and strings, in which case `offset` is a `(u32, + /// u32)` of `(offset, length)`. fn ptr<'a, T>(&'a self, offset: T::Pointer) -> GuestPtr<'a, T> where Self: Sized, @@ -53,6 +151,8 @@ pub unsafe trait GuestMemory { } } +// Forwarding trait implementations to the original type + unsafe impl<'a, T: ?Sized + GuestMemory> GuestMemory for &'a T { fn base(&self) -> (*mut u8, u32) { T::base(self) @@ -65,6 +165,68 @@ unsafe impl<'a, T: ?Sized + GuestMemory> GuestMemory for &'a mut T { } } +unsafe impl GuestMemory for Box { + fn base(&self) -> (*mut u8, u32) { + T::base(self) + } +} + +unsafe impl GuestMemory for Rc { + fn base(&self) -> (*mut u8, u32) { + T::base(self) + } +} + +unsafe impl GuestMemory for Arc { + fn base(&self) -> (*mut u8, u32) { + T::base(self) + } +} + +/// A *guest* pointer into host memory. +/// +/// This type represents a pointer from the guest that points into host memory. +/// Internally a `GuestPtr` contains a handle to its original [`GuestMemory`] as +/// well as the offset into the memory that the pointer is pointing at. +/// +/// Presence of a [`GuestPtr`] does not imply any form of validity. Pointers can +/// be out-of-bounds, misaligned, etc. It is safe to construct a `GuestPtr` with +/// any offset at any time. Consider a `GuestPtr` roughly equivalent to `*mut +/// T`, although there are a few more safety guarantees around this type. +/// +/// ## Slices and Strings +/// +/// Note that the type parameter does not need to implement the `Sized` trait, +/// so you can implement types such as this: +/// +/// * `GuestPtr<'_, str>` - a pointer to a guest string +/// * `GuestPtr<'_, [T]>` - a pointer to a guest array +/// +/// Unsized types such as this may have extra methods and won't have methods +/// like [`GuestPtr::read`] or [`GuestPtr::write`]. +/// +/// ## Type parameter and pointee +/// +/// The `T` type parameter is largely intended for more static safety in Rust as +/// well as having a better handle on what we're pointing to. A `GuestPtr`, +/// however, does not necessarily literally imply a guest pointer pointing to +/// type `T`. Instead the [`GuestType`] trait is a layer of abstraction where +/// `GuestPtr` may actually be a pointer to `U` in guest memory, but you can +/// construct a `T` from a `U`. +/// +/// For example `GuestPtr>` is a valid type, but this is actually +/// more equivalent to `GuestPtr` because guest pointers are always +/// 32-bits. That being said you can create a `GuestPtr` from a `u32`. +/// +/// Additionally `GuestPtr` will actually delegate, typically, to and +/// implementation which loads the underlying data as `GuestPtr` (or +/// similar) and then the bytes loaded are validated to fit within the +/// definition of `MyEnum` before `MyEnum` is returned. +/// +/// For more information see the [`GuestPtr::read`] and [`GuestPtr::write`] +/// methods. In general though be extremely careful about writing `unsafe` code +/// when working with a `GuestPtr` if you're not using one of the +/// already-attached helper methods. pub struct GuestPtr<'a, T: ?Sized + Pointee> { mem: &'a (dyn GuestMemory + 'a), pointer: T::Pointer, @@ -72,6 +234,11 @@ pub struct GuestPtr<'a, T: ?Sized + Pointee> { } impl<'a, T: ?Sized + Pointee> GuestPtr<'a, T> { + /// Creates a new `GuestPtr` from the given `mem` and `pointer` values. + /// + /// Note that for sized types like `u32`, `GuestPtr`, etc, the `pointer` + /// vlue is a `u32` offset into guest memory. For slices and strings, + /// `pointer` is a `(u32, u32)` offset/length pair. pub fn new(mem: &'a (dyn GuestMemory + 'a), pointer: T::Pointer) -> GuestPtr<'_, T> { GuestPtr { mem, @@ -80,14 +247,25 @@ impl<'a, T: ?Sized + Pointee> GuestPtr<'a, T> { } } + /// Returns the offset of this pointer in guest memory. + /// + /// Note that for sized types this returns a `u32`, but for slices and + /// strings it returns a `(u32, u32)` pointer/length pair. pub fn offset(&self) -> T::Pointer { self.pointer } + /// Returns the guest memory that this pointer is coming from. pub fn mem(&self) -> &'a (dyn GuestMemory + 'a) { self.mem } + /// Casts this `GuestPtr` type to a different type. + /// + /// This is a safe method which is useful for simply reinterpreting the type + /// parameter on this `GuestPtr`. Note that this is a safe method, where + /// again there's no guarantees about alignment, validity, in-bounds-ness, + /// etc of the returned pointer. pub fn cast(&self) -> GuestPtr<'a, U> where T: Pointee, @@ -95,6 +273,29 @@ impl<'a, T: ?Sized + Pointee> GuestPtr<'a, T> { GuestPtr::new(self.mem, self.pointer) } + /// Safely read a value from this pointer. + /// + /// This is a fun method, and is one of the lynchpins of this + /// implementation. The highlight here is that this is a *safe* operation, + /// not an unsafe one like `*mut T`. This works for a few reasons: + /// + /// * The `unsafe` contract of the `GuestMemory` trait means that there's + /// always at least some backing memory for this `GuestPtr`. + /// + /// * This does not use Rust-intrinsics to read the type `T`, but rather it + /// delegates to `T`'s implementation of [`GuestType`] to actually read + /// the underlying data. This again is a safe method, so any unsafety, if + /// any, must be internally documented. + /// + /// * Eventually what typically happens it that this bottoms out in the read + /// implementations for primitives types (like `i32`) which can safely be + /// read at any time, and then it's up to the runtime to determine what to + /// do with the bytes it read in a safe manner. + /// + /// Naturally lots of things can still go wrong, such as out-of-bounds + /// checks, alignment checks, validity checks (e.g. for enums), etc. All of + /// these check failures, however, are returned as a [`GuestError`] in the + /// `Result` here, and `Ok` is only returned if all the checks passed. pub fn read(&self) -> Result where T: GuestType<'a>, @@ -102,6 +303,16 @@ impl<'a, T: ?Sized + Pointee> GuestPtr<'a, T> { T::read(self) } + /// Safely write a valud to this pointer. + /// + /// This method, like [`GuestPtr::read`], is pretty crucial for the safe + /// operation of this crate. All the same reasons apply though for why this + /// method is safe, even eventually bottoming out in primitives like writing + /// an `i32` which is safe to write bit patterns into memory at any time due + /// to the guarantees of [`GuestMemory`]. + /// + /// Like `read`, `write` can fail due to any manner of pointer checks, but + /// any failure is returned as a [`GuestError`]. pub fn write(&self, val: T) -> Result<(), GuestError> where T: GuestType<'a>, @@ -109,6 +320,12 @@ impl<'a, T: ?Sized + Pointee> GuestPtr<'a, T> { T::write(self, val) } + /// Performs pointer arithmetic on this pointer, moving the pointer forward + /// `amt` slots. + /// + /// This will either return the resulting pointer or `Err` if the pointer + /// arithmetic calculation would overflow around the end of the address + /// space. pub fn add(&self, amt: u32) -> Result, GuestError> where T: GuestType<'a> + Pointee, @@ -125,38 +342,69 @@ impl<'a, T: ?Sized + Pointee> GuestPtr<'a, T> { } impl<'a, T> GuestPtr<'a, [T]> { + /// For slices, specifically returns the relative pointer to the base of the + /// array. + /// + /// This is similar to `<[T]>::as_ptr()` pub fn offset_base(&self) -> u32 { self.pointer.0 } + /// For slices, returns the length of the slice, in units. pub fn len(&self) -> u32 { self.pointer.1 } + /// Returns an iterator over interior pointers. + /// + /// Each item is a `Result` indicating whether it overflowed past the end of + /// the address space or not. pub fn iter<'b>( &'b self, ) -> impl ExactSizeIterator, GuestError>> + 'b where T: GuestType<'a>, { - let base = GuestPtr::new(self.mem, self.offset_base()); + let base = self.as_ptr(); (0..self.len()).map(move |i| base.add(i)) } + + /// Returns a `GuestPtr` pointing to the base of the array for the interior + /// type `T`. + pub fn as_ptr(&self) -> GuestPtr<'a, T> { + GuestPtr::new(self.mem, self.offset_base()) + } } impl<'a> GuestPtr<'a, str> { + /// For strings, returns the relative pointer to the base of the string + /// allocation. pub fn offset_base(&self) -> u32 { self.pointer.0 } + /// Returns the length, in bytes, of th estring. pub fn len(&self) -> u32 { self.pointer.1 } + /// Returns a raw pointer for the underlying slice of bytes that this + /// pointer points to. pub fn as_bytes(&self) -> GuestPtr<'a, [u8]> { GuestPtr::new(self.mem, self.pointer) } + /// Attempts to read a raw `*mut str` pointer from this pointer, performing + /// bounds checks and utf-8 checks. + /// + /// This function will return a raw pointer into host memory if all checks + /// succeed (valid utf-8, valid pointers, etc). If any checks fail then + /// `GuestError` will be returned. + /// + /// Note that the `*mut str` pointer is still unsafe to use in general, but + /// there are specific situations that it is safe to use. For more + /// information about using the raw pointer, consult the [`GuestMemory`] + /// trait documentation. pub fn as_raw(&self) -> Result<*mut str, GuestError> { let ptr = self .mem @@ -194,6 +442,10 @@ mod private { impl Sealed for str {} } +/// Types that can be pointed to by `GuestPtr`. +/// +/// In essence everything can, and the only special-case is unsized types like +/// `str` and `[T]` which have special implementations. pub trait Pointee: private::Sealed { #[doc(hidden)] type Pointer: Copy; From c78416912c97d7beed1ac54bfa5338149fb40d15 Mon Sep 17 00:00:00 2001 From: Pat Hickey Date: Fri, 6 Mar 2020 16:04:56 -0800 Subject: [PATCH 82/86] Check safety of `as_raw` with a simplified borrow checker (#37) * wiggle-runtime: add as_raw method for [T] * add trivial borrow checker back in * integrate runtime borrow checker with as_raw methods * handle pointer arith overflow correctly in as_raw, create PtrOverflow error * runtime: add validation back to GuestType * generate: impl validate for enums, flags, handles, ints * oops! make validate its own method on trait GuestTypeTransparent * fix transparent impls for enum, flag, handle, int * some structs are transparent. fix tests. * tests: define byte_slice_strat and friends * wiggle-tests: i believe my allocator is working now * some type juggling around memset for ease of use * make GuestTypeTransparent an unsafe trait * delete redundant validation of pointer align * fix doc * wiggle_test: aha, you cant use sets to track memory areas * add multi-string test which exercises the runtime borrow checker against HostMemory::byte_slice_strat * oops left debug panic in * remove redundant (& incorrect, since unchecked) length calc * redesign validate again, and actually hook to as_raw * makr all validate impls as inline this should hopefully allow as_raw's check loop to be unrolled to a no-op in most cases! * code review fixes --- crates/generate/src/lifetimes.rs | 27 ++++ crates/generate/src/types/enum.rs | 16 ++- crates/generate/src/types/flags.rs | 18 ++- crates/generate/src/types/handle.rs | 11 ++ crates/generate/src/types/int.rs | 10 ++ crates/generate/src/types/struct.rs | 28 ++++ crates/runtime/src/borrow.rs | 91 ++++++++++++ crates/runtime/src/error.rs | 2 + crates/runtime/src/guest_type.rs | 25 ++++ crates/runtime/src/lib.rs | 75 +++++++++- crates/test/src/lib.rs | 206 ++++++++++++++++++++++++++-- tests/arrays.rs | 8 +- tests/flags.rs | 2 +- tests/pointers.rs | 8 +- tests/strings.rs | 144 ++++++++++++++++++- tests/strings.witx | 8 ++ tests/structs.rs | 22 +-- tests/union.rs | 4 +- 18 files changed, 655 insertions(+), 50 deletions(-) create mode 100644 crates/runtime/src/borrow.rs diff --git a/crates/generate/src/lifetimes.rs b/crates/generate/src/lifetimes.rs index ae9631a63b..75b102209c 100644 --- a/crates/generate/src/lifetimes.rs +++ b/crates/generate/src/lifetimes.rs @@ -2,16 +2,34 @@ use proc_macro2::TokenStream; use quote::quote; pub trait LifetimeExt { + fn is_transparent(&self) -> bool; fn needs_lifetime(&self) -> bool; } impl LifetimeExt for witx::TypeRef { + fn is_transparent(&self) -> bool { + self.type_().is_transparent() + } fn needs_lifetime(&self) -> bool { self.type_().needs_lifetime() } } impl LifetimeExt for witx::Type { + fn is_transparent(&self) -> bool { + match self { + witx::Type::Builtin(b) => b.is_transparent(), + witx::Type::Struct(s) => s.is_transparent(), + witx::Type::Enum { .. } + | witx::Type::Flags { .. } + | witx::Type::Int { .. } + | witx::Type::Handle { .. } => true, + witx::Type::Union { .. } + | witx::Type::Pointer { .. } + | witx::Type::ConstPointer { .. } + | witx::Type::Array { .. } => false, + } + } fn needs_lifetime(&self) -> bool { match self { witx::Type::Builtin(b) => b.needs_lifetime(), @@ -29,6 +47,9 @@ impl LifetimeExt for witx::Type { } impl LifetimeExt for witx::BuiltinType { + fn is_transparent(&self) -> bool { + !self.needs_lifetime() + } fn needs_lifetime(&self) -> bool { match self { witx::BuiltinType::String => true, @@ -38,12 +59,18 @@ impl LifetimeExt for witx::BuiltinType { } impl LifetimeExt for witx::StructDatatype { + fn is_transparent(&self) -> bool { + self.members.iter().all(|m| m.tref.is_transparent()) + } fn needs_lifetime(&self) -> bool { self.members.iter().any(|m| m.tref.needs_lifetime()) } } impl LifetimeExt for witx::UnionDatatype { + fn is_transparent(&self) -> bool { + false + } fn needs_lifetime(&self) -> bool { self.variants .iter() diff --git a/crates/generate/src/types/enum.rs b/crates/generate/src/types/enum.rs index ec9ab021b0..8aa77f8682 100644 --- a/crates/generate/src/types/enum.rs +++ b/crates/generate/src/types/enum.rs @@ -87,8 +87,9 @@ pub(super) fn define_enum(names: &Names, name: &witx::Id, e: &witx::EnumDatatype fn read(location: &wiggle_runtime::GuestPtr<#ident>) -> Result<#ident, wiggle_runtime::GuestError> { use std::convert::TryFrom; - let val = #repr::read(&location.cast())?; - #ident::try_from(val) + let reprval = #repr::read(&location.cast())?; + let value = #ident::try_from(reprval)?; + Ok(value) } fn write(location: &wiggle_runtime::GuestPtr<'_, #ident>, val: Self) @@ -97,5 +98,16 @@ pub(super) fn define_enum(names: &Names, name: &witx::Id, e: &witx::EnumDatatype #repr::write(&location.cast(), #repr::from(val)) } } + + unsafe impl <'a> wiggle_runtime::GuestTypeTransparent<'a> for #ident { + #[inline] + fn validate(location: *mut #ident) -> Result<(), wiggle_runtime::GuestError> { + use std::convert::TryFrom; + // Validate value in memory using #ident::try_from(reprval) + let reprval = unsafe { (location as *mut #repr).read() }; + let _val = #ident::try_from(reprval)?; + Ok(()) + } + } } } diff --git a/crates/generate/src/types/flags.rs b/crates/generate/src/types/flags.rs index 201e0a4529..60aa671310 100644 --- a/crates/generate/src/types/flags.rs +++ b/crates/generate/src/types/flags.rs @@ -134,10 +134,11 @@ pub(super) fn define_flags(names: &Names, name: &witx::Id, f: &witx::FlagsDataty #repr::guest_align() } - fn read(location: &wiggle_runtime::GuestPtr<'a, #ident>) -> Result<#ident, wiggle_runtime::GuestError> { + fn read(location: &wiggle_runtime::GuestPtr<#ident>) -> Result<#ident, wiggle_runtime::GuestError> { use std::convert::TryFrom; - let bits = #repr::read(&location.cast())?; - #ident::try_from(bits) + let reprval = #repr::read(&location.cast())?; + let value = #ident::try_from(reprval)?; + Ok(value) } fn write(location: &wiggle_runtime::GuestPtr<'_, #ident>, val: Self) -> Result<(), wiggle_runtime::GuestError> { @@ -145,5 +146,16 @@ pub(super) fn define_flags(names: &Names, name: &witx::Id, f: &witx::FlagsDataty #repr::write(&location.cast(), val) } } + unsafe impl <'a> wiggle_runtime::GuestTypeTransparent<'a> for #ident { + #[inline] + fn validate(location: *mut #ident) -> Result<(), wiggle_runtime::GuestError> { + use std::convert::TryFrom; + // Validate value in memory using #ident::try_from(reprval) + let reprval = unsafe { (location as *mut #repr).read() }; + let _val = #ident::try_from(reprval)?; + Ok(()) + } + } + } } diff --git a/crates/generate/src/types/handle.rs b/crates/generate/src/types/handle.rs index 294e36a028..4f922cbd7b 100644 --- a/crates/generate/src/types/handle.rs +++ b/crates/generate/src/types/handle.rs @@ -13,6 +13,7 @@ pub(super) fn define_handle( let size = h.mem_size_align().size as u32; let align = h.mem_size_align().align as usize; quote! { + #[repr(transparent)] #[derive(Copy, Clone, Debug, ::std::hash::Hash, Eq, PartialEq)] pub struct #ident(u32); @@ -62,5 +63,15 @@ pub(super) fn define_handle( u32::write(&location.cast(), val.0) } } + + unsafe impl<'a> wiggle_runtime::GuestTypeTransparent<'a> for #ident { + #[inline] + fn validate(_location: *mut #ident) -> Result<(), wiggle_runtime::GuestError> { + // All bit patterns accepted + Ok(()) + } + } + + } } diff --git a/crates/generate/src/types/int.rs b/crates/generate/src/types/int.rs index 25375700b9..ee870eb6a6 100644 --- a/crates/generate/src/types/int.rs +++ b/crates/generate/src/types/int.rs @@ -73,11 +73,21 @@ pub(super) fn define_int(names: &Names, name: &witx::Id, i: &witx::IntDatatype) fn read(location: &wiggle_runtime::GuestPtr<'a, #ident>) -> Result<#ident, wiggle_runtime::GuestError> { Ok(#ident(#repr::read(&location.cast())?)) + } fn write(location: &wiggle_runtime::GuestPtr<'_, #ident>, val: Self) -> Result<(), wiggle_runtime::GuestError> { #repr::write(&location.cast(), val.0) } } + + unsafe impl<'a> wiggle_runtime::GuestTypeTransparent<'a> for #ident { + #[inline] + fn validate(_location: *mut #ident) -> Result<(), wiggle_runtime::GuestError> { + // All bit patterns accepted + Ok(()) + } + } + } } diff --git a/crates/generate/src/types/struct.rs b/crates/generate/src/types/struct.rs index 11a6bdf06f..d0c3b03cdd 100644 --- a/crates/generate/src/types/struct.rs +++ b/crates/generate/src/types/struct.rs @@ -77,6 +77,32 @@ pub(super) fn define_struct( (quote!(), quote!(, Copy, PartialEq)) }; + let transparent = if s.is_transparent() { + let member_validate = s.member_layout().into_iter().map(|ml| { + let offset = ml.offset; + let typename = names.type_ref(&ml.member.tref, anon_lifetime()); + quote! { + // SAFETY: caller has validated bounds and alignment of `location`. + // member_layout gives correctly-aligned pointers inside that area. + #typename::validate( + unsafe { (location as *mut u8).add(#offset) as *mut _ } + )?; + } + }); + + quote! { + unsafe impl<'a> wiggle_runtime::GuestTypeTransparent<'a> for #ident { + #[inline] + fn validate(location: *mut #ident) -> Result<(), wiggle_runtime::GuestError> { + #(#member_validate)* + Ok(()) + } + } + } + } else { + quote!() + }; + quote! { #[derive(Clone, Debug #extra_derive)] pub struct #ident #struct_lifetime { @@ -102,5 +128,7 @@ pub(super) fn define_struct( Ok(()) } } + + #transparent } } diff --git a/crates/runtime/src/borrow.rs b/crates/runtime/src/borrow.rs new file mode 100644 index 0000000000..5c3c80f429 --- /dev/null +++ b/crates/runtime/src/borrow.rs @@ -0,0 +1,91 @@ +use crate::region::Region; +use crate::GuestError; + +#[derive(Debug)] +pub struct GuestBorrows { + borrows: Vec, +} + +impl GuestBorrows { + pub fn new() -> Self { + Self { + borrows: Vec::new(), + } + } + + fn is_borrowed(&self, r: Region) -> bool { + !self.borrows.iter().all(|b| !b.overlaps(r)) + } + + pub fn borrow(&mut self, r: Region) -> Result<(), GuestError> { + if self.is_borrowed(r) { + Err(GuestError::PtrBorrowed(r)) + } else { + self.borrows.push(r); + Ok(()) + } + } +} + +#[cfg(test)] +mod test { + use super::*; + #[test] + fn nonoverlapping() { + let mut bs = GuestBorrows::new(); + let r1 = Region::new(0, 10); + let r2 = Region::new(10, 10); + assert!(!r1.overlaps(r2)); + bs.borrow(r1).expect("can borrow r1"); + bs.borrow(r2).expect("can borrow r2"); + + let mut bs = GuestBorrows::new(); + let r1 = Region::new(10, 10); + let r2 = Region::new(0, 10); + assert!(!r1.overlaps(r2)); + bs.borrow(r1).expect("can borrow r1"); + bs.borrow(r2).expect("can borrow r2"); + } + + #[test] + fn overlapping() { + let mut bs = GuestBorrows::new(); + let r1 = Region::new(0, 10); + let r2 = Region::new(9, 10); + assert!(r1.overlaps(r2)); + bs.borrow(r1).expect("can borrow r1"); + assert!(bs.borrow(r2).is_err(), "cant borrow r2"); + + let mut bs = GuestBorrows::new(); + let r1 = Region::new(0, 10); + let r2 = Region::new(2, 5); + assert!(r1.overlaps(r2)); + bs.borrow(r1).expect("can borrow r1"); + assert!(bs.borrow(r2).is_err(), "cant borrow r2"); + + let mut bs = GuestBorrows::new(); + let r1 = Region::new(9, 10); + let r2 = Region::new(0, 10); + assert!(r1.overlaps(r2)); + bs.borrow(r1).expect("can borrow r1"); + assert!(bs.borrow(r2).is_err(), "cant borrow r2"); + + let mut bs = GuestBorrows::new(); + let r1 = Region::new(2, 5); + let r2 = Region::new(0, 10); + assert!(r1.overlaps(r2)); + bs.borrow(r1).expect("can borrow r1"); + assert!(bs.borrow(r2).is_err(), "cant borrow r2"); + + let mut bs = GuestBorrows::new(); + let r1 = Region::new(2, 5); + let r2 = Region::new(10, 5); + let r3 = Region::new(15, 5); + let r4 = Region::new(0, 10); + assert!(r1.overlaps(r4)); + bs.borrow(r1).expect("can borrow r1"); + bs.borrow(r2).expect("can borrow r2"); + bs.borrow(r3).expect("can borrow r3"); + assert!(bs.borrow(r4).is_err(), "cant borrow r4"); + } +} diff --git a/crates/runtime/src/error.rs b/crates/runtime/src/error.rs index 14c48be1b9..8a163dbb63 100644 --- a/crates/runtime/src/error.rs +++ b/crates/runtime/src/error.rs @@ -7,6 +7,8 @@ pub enum GuestError { InvalidFlagValue(&'static str), #[error("Invalid enum value {0}")] InvalidEnumValue(&'static str), + #[error("Pointer overflow")] + PtrOverflow, #[error("Pointer out of bounds: {0:?}")] PtrOutOfBounds(Region), #[error("Pointer not aligned to {1}: {0:?}")] diff --git a/crates/runtime/src/guest_type.rs b/crates/runtime/src/guest_type.rs index c7517bf0ea..981301da46 100644 --- a/crates/runtime/src/guest_type.rs +++ b/crates/runtime/src/guest_type.rs @@ -40,6 +40,22 @@ pub trait GuestType<'a>: Sized { fn write(ptr: &GuestPtr<'_, Self>, val: Self) -> Result<(), GuestError>; } +/// A trait for `GuestType`s that have the same representation in guest memory +/// as in Rust. These types can be used with the `GuestPtr::as_raw` method to +/// view as a slice. +/// +/// Unsafe trait because a correct GuestTypeTransparent implemengation ensures that the +/// GuestPtr::as_raw methods are safe. This trait should only ever be implemented +/// by wiggle_generate-produced code. +pub unsafe trait GuestTypeTransparent<'a>: GuestType<'a> { + /// Checks that the memory at `ptr` is a valid representation of `Self`. + /// + /// Assumes that memory safety checks have already been performed: `ptr` + /// has been checked to be aligned correctly and reside in memory using + /// `GuestMemory::validate_size_align` + fn validate(ptr: *mut Self) -> Result<(), GuestError>; +} + macro_rules! primitives { ($($i:ident)*) => ($( impl<'a> GuestType<'a> for $i { @@ -78,6 +94,15 @@ macro_rules! primitives { Ok(()) } } + + unsafe impl<'a> GuestTypeTransparent<'a> for $i { + #[inline] + fn validate(_ptr: *mut $i) -> Result<(), GuestError> { + // All bit patterns are safe, nothing to do here + Ok(()) + } + } + )*) } diff --git a/crates/runtime/src/lib.rs b/crates/runtime/src/lib.rs index b57a5bfe08..a101a19a13 100644 --- a/crates/runtime/src/lib.rs +++ b/crates/runtime/src/lib.rs @@ -6,11 +6,14 @@ use std::slice; use std::str; use std::sync::Arc; +mod borrow; mod error; mod guest_type; mod region; + +pub use borrow::GuestBorrows; pub use error::GuestError; -pub use guest_type::{GuestErrorType, GuestType}; +pub use guest_type::{GuestErrorType, GuestType, GuestTypeTransparent}; pub use region::Region; /// A trait which abstracts how to get at the region of host memory taht @@ -119,12 +122,12 @@ pub unsafe trait GuestMemory { // Figure out our pointer to the start of memory let start = match (base_ptr as usize).checked_add(offset as usize) { Some(ptr) => ptr, - None => return Err(GuestError::PtrOutOfBounds(region)), + None => return Err(GuestError::PtrOverflow), }; // and use that to figure out the end pointer let end = match start.checked_add(len as usize) { Some(ptr) => ptr, - None => return Err(GuestError::PtrOutOfBounds(region)), + None => return Err(GuestError::PtrOverflow), }; // and then verify that our end doesn't reach past the end of our memory if end > (base_ptr as usize) + (base_len as usize) { @@ -335,7 +338,7 @@ impl<'a, T: ?Sized + Pointee> GuestPtr<'a, T> { .and_then(|o| self.pointer.checked_add(o)); let offset = match offset { Some(o) => o, - None => return Err(GuestError::InvalidFlagValue("")), + None => return Err(GuestError::PtrOverflow), }; Ok(GuestPtr::new(self.mem, offset)) } @@ -369,6 +372,54 @@ impl<'a, T> GuestPtr<'a, [T]> { (0..self.len()).map(move |i| base.add(i)) } + /// Attempts to read a raw `*mut [T]` pointer from this pointer, performing + /// bounds checks and type validation. + /// The resulting `*mut [T]` can be used as a `&mut [t]` as long as the + /// reference is dropped before any Wasm code is re-entered. + /// + /// This function will return a raw pointer into host memory if all checks + /// succeed (valid utf-8, valid pointers, etc). If any checks fail then + /// `GuestError` will be returned. + /// + /// Note that the `*mut [T]` pointer is still unsafe to use in general, but + /// there are specific situations that it is safe to use. For more + /// information about using the raw pointer, consult the [`GuestMemory`] + /// trait documentation. + /// + /// For safety against overlapping mutable borrows, the user must use the + /// same `GuestBorrows` to create all *mut str or *mut [T] that are alive + /// at the same time. + pub fn as_raw(&self, bc: &mut GuestBorrows) -> Result<*mut [T], GuestError> + where + T: GuestTypeTransparent<'a>, + { + let len = match self.pointer.1.checked_mul(T::guest_size()) { + Some(l) => l, + None => return Err(GuestError::PtrOverflow), + }; + let ptr = + self.mem + .validate_size_align(self.pointer.0, T::guest_align(), len)? as *mut T; + + bc.borrow(Region { + start: self.pointer.0, + len, + })?; + + // Validate all elements in slice. + // SAFETY: ptr has been validated by self.mem.validate_size_align + for offs in 0..self.pointer.1 { + T::validate(unsafe { ptr.add(offs as usize) })?; + } + + // SAFETY: iff there are no overlapping borrows (all uses of as_raw use this same + // GuestBorrows), its valid to construct a *mut [T] + unsafe { + let s = slice::from_raw_parts_mut(ptr, self.pointer.1 as usize); + Ok(s as *mut [T]) + } + } + /// Returns a `GuestPtr` pointing to the base of the array for the interior /// type `T`. pub fn as_ptr(&self) -> GuestPtr<'a, T> { @@ -396,6 +447,8 @@ impl<'a> GuestPtr<'a, str> { /// Attempts to read a raw `*mut str` pointer from this pointer, performing /// bounds checks and utf-8 checks. + /// The resulting `*mut str` can be used as a `&mut str` as long as the + /// reference is dropped before any Wasm code is re-entered. /// /// This function will return a raw pointer into host memory if all checks /// succeed (valid utf-8, valid pointers, etc). If any checks fail then @@ -405,12 +458,22 @@ impl<'a> GuestPtr<'a, str> { /// there are specific situations that it is safe to use. For more /// information about using the raw pointer, consult the [`GuestMemory`] /// trait documentation. - pub fn as_raw(&self) -> Result<*mut str, GuestError> { + /// + /// For safety against overlapping mutable borrows, the user must use the + /// same `GuestBorrows` to create all *mut str or *mut [T] that are alive + /// at the same time. + pub fn as_raw(&self, bc: &mut GuestBorrows) -> Result<*mut str, GuestError> { let ptr = self .mem .validate_size_align(self.pointer.0, 1, self.pointer.1)?; - // TODO: doc unsafety here + bc.borrow(Region { + start: self.pointer.0, + len: self.pointer.1, + })?; + + // SAFETY: iff there are no overlapping borrows (all uses of as_raw use this same + // GuestBorrows), its valid to construct a *mut str unsafe { let s = slice::from_raw_parts_mut(ptr, self.pointer.1 as usize); match str::from_utf8_mut(s) { diff --git a/crates/test/src/lib.rs b/crates/test/src/lib.rs index c946df73ba..de4474a528 100644 --- a/crates/test/src/lib.rs +++ b/crates/test/src/lib.rs @@ -2,6 +2,45 @@ use proptest::prelude::*; use std::cell::UnsafeCell; use wiggle_runtime::GuestMemory; +#[derive(Debug, Clone)] +pub struct MemAreas(Vec); +impl MemAreas { + pub fn new() -> Self { + MemAreas(Vec::new()) + } + pub fn insert(&mut self, a: MemArea) { + // Find if `a` is already in the vector + match self.0.binary_search(&a) { + // It is present - insert it next to existing one + Ok(loc) => self.0.insert(loc, a), + // It is not present - heres where to insert it + Err(loc) => self.0.insert(loc, a), + } + } + pub fn iter(&self) -> impl Iterator { + self.0.iter() + } +} + +impl From for MemAreas +where + R: AsRef<[MemArea]>, +{ + fn from(ms: R) -> MemAreas { + let mut out = MemAreas::new(); + for m in ms.as_ref().into_iter() { + out.insert(*m); + } + out + } +} + +impl Into> for MemAreas { + fn into(self) -> Vec { + self.0.clone() + } +} + #[repr(align(4096))] pub struct HostMemory { buffer: UnsafeCell<[u8; 4096]>, @@ -26,6 +65,42 @@ impl HostMemory { }) .boxed() } + + /// Takes a sorted list or memareas, and gives a sorted list of memareas covering + /// the parts of memory not covered by the previous + pub fn invert(regions: &MemAreas) -> MemAreas { + let mut out = MemAreas::new(); + let mut start = 0; + for r in regions.iter() { + let len = r.ptr - start; + if len > 0 { + out.insert(MemArea { + ptr: start, + len: r.ptr - start, + }); + } + start = r.ptr + r.len; + } + if start < 4096 { + out.insert(MemArea { + ptr: start, + len: 4096 - start, + }); + } + out + } + + pub fn byte_slice_strat(size: u32, exclude: &MemAreas) -> BoxedStrategy { + let available: Vec = Self::invert(exclude) + .iter() + .flat_map(|a| a.inside(size)) + .collect(); + + Just(available) + .prop_filter("available memory for allocation", |a| !a.is_empty()) + .prop_flat_map(|a| prop::sample::select(a)) + .boxed() + } } unsafe impl GuestMemory for HostMemory { @@ -37,7 +112,7 @@ unsafe impl GuestMemory for HostMemory { } } -#[derive(Debug)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] pub struct MemArea { pub ptr: u32, pub len: u32, @@ -48,7 +123,7 @@ impl MemArea { // test. // So, I implemented this one with std::ops::Range so it is less likely I wrote the same bug in two // places. - pub fn overlapping(&self, b: &Self) -> bool { + pub fn overlapping(&self, b: Self) -> bool { // a_range is all elems in A let a_range = std::ops::Range { start: self.ptr, @@ -73,18 +148,33 @@ impl MemArea { } return false; } - pub fn non_overlapping_set(areas: &[&Self]) -> bool { - // A is all areas - for (i, a) in areas.iter().enumerate() { - // (A, B) is every pair of areas - for b in areas[i + 1..].iter() { - if a.overlapping(b) { - return false; + pub fn non_overlapping_set(areas: M) -> bool + where + M: Into, + { + let areas = areas.into(); + for (aix, a) in areas.iter().enumerate() { + for (bix, b) in areas.iter().enumerate() { + if aix != bix { + // (A, B) is every pairing of areas + if a.overlapping(*b) { + return false; + } } } } return true; } + + /// Enumerate all memareas of size `len` inside a given area + fn inside(&self, len: u32) -> impl Iterator { + let end: i64 = self.len as i64 - len as i64; + let start = self.ptr; + (0..end).into_iter().map(move |v| MemArea { + ptr: start + v as u32, + len, + }) + } } #[cfg(test)] @@ -97,6 +187,104 @@ mod test { let h = Box::new(h); assert_eq!(h.base().0 as usize % 4096, 0); } + + #[test] + fn invert() { + fn invert_equality(input: &[MemArea], expected: &[MemArea]) { + let input: MemAreas = input.into(); + let inverted: Vec = HostMemory::invert(&input).into(); + assert_eq!(expected, inverted.as_slice()); + } + + invert_equality(&[], &[MemArea { ptr: 0, len: 4096 }]); + invert_equality( + &[MemArea { ptr: 0, len: 1 }], + &[MemArea { ptr: 1, len: 4095 }], + ); + + invert_equality( + &[MemArea { ptr: 1, len: 1 }], + &[MemArea { ptr: 0, len: 1 }, MemArea { ptr: 2, len: 4094 }], + ); + + invert_equality( + &[MemArea { ptr: 1, len: 4095 }], + &[MemArea { ptr: 0, len: 1 }], + ); + + invert_equality( + &[MemArea { ptr: 0, len: 1 }, MemArea { ptr: 1, len: 4095 }], + &[], + ); + + invert_equality( + &[MemArea { ptr: 1, len: 2 }, MemArea { ptr: 4, len: 1 }], + &[ + MemArea { ptr: 0, len: 1 }, + MemArea { ptr: 3, len: 1 }, + MemArea { ptr: 5, len: 4091 }, + ], + ); + } + + fn set_of_slices_strat( + s1: u32, + s2: u32, + s3: u32, + ) -> BoxedStrategy<(MemArea, MemArea, MemArea)> { + HostMemory::byte_slice_strat(s1, &MemAreas::new()) + .prop_flat_map(move |a1| { + ( + Just(a1), + HostMemory::byte_slice_strat(s2, &MemAreas::from(&[a1])), + ) + }) + .prop_flat_map(move |(a1, a2)| { + ( + Just(a1), + Just(a2), + HostMemory::byte_slice_strat(s3, &MemAreas::from(&[a1, a2])), + ) + }) + .boxed() + } + + #[test] + fn trivial_inside() { + let a = MemArea { ptr: 24, len: 4072 }; + let interior = a.inside(24).collect::>(); + + assert!(interior.len() > 0); + } + + proptest! { + #[test] + // For some random region of decent size + fn inside(r in HostMemory::mem_area_strat(123)) { + let set_of_r = MemAreas::from(&[r]); + // All regions outside of r: + let exterior = HostMemory::invert(&set_of_r); + // All regions inside of r: + let interior = r.inside(22); + for i in interior { + // i overlaps with r: + assert!(r.overlapping(i)); + // i is inside r: + assert!(i.ptr >= r.ptr); + assert!(r.ptr + r.len >= i.ptr + i.len); + // the set of exterior and i is non-overlapping + let mut all = exterior.clone(); + all.insert(i); + assert!(MemArea::non_overlapping_set(all)); + } + } + + #[test] + fn byte_slices((s1, s2, s3) in set_of_slices_strat(12, 34, 56)) { + let all = MemAreas::from(&[s1, s2, s3]); + assert!(MemArea::non_overlapping_set(all)); + } + } } use std::cell::RefCell; diff --git a/tests/arrays.rs b/tests/arrays.rs index d9ccd4ce93..7e6e7b332f 100644 --- a/tests/arrays.rs +++ b/tests/arrays.rs @@ -67,9 +67,9 @@ impl ReduceExcusesExcercise { }, ) .prop_filter("non-overlapping pointers", |e| { - let mut all = vec![&e.array_ptr_loc, &e.return_ptr_loc]; + let mut all = vec![e.array_ptr_loc, e.return_ptr_loc]; all.extend(e.excuse_ptr_locs.iter()); - MemArea::non_overlapping_set(&all) + MemArea::non_overlapping_set(all) }) .boxed() } @@ -155,9 +155,9 @@ impl PopulateExcusesExcercise { elements, }) .prop_filter("non-overlapping pointers", |e| { - let mut all = vec![&e.array_ptr_loc]; + let mut all = vec![e.array_ptr_loc]; all.extend(e.elements.iter()); - MemArea::non_overlapping_set(&all) + MemArea::non_overlapping_set(all) }) .boxed() } diff --git a/tests/flags.rs b/tests/flags.rs index d008dcb03c..d00c5b84f0 100644 --- a/tests/flags.rs +++ b/tests/flags.rs @@ -57,7 +57,7 @@ impl ConfigureCarExercise { }, ) .prop_filter("non-overlapping ptrs", |e| { - MemArea::non_overlapping_set(&[&e.other_config_by_ptr, &e.return_ptr_loc]) + MemArea::non_overlapping_set(&[e.other_config_by_ptr, e.return_ptr_loc]) }) .boxed() } diff --git a/tests/pointers.rs b/tests/pointers.rs index dc26ed33f6..f15a53349e 100644 --- a/tests/pointers.rs +++ b/tests/pointers.rs @@ -117,10 +117,10 @@ impl PointersAndEnumsExercise { ) .prop_filter("non-overlapping pointers", |e| { MemArea::non_overlapping_set(&[ - &e.input2_loc, - &e.input3_loc, - &e.input4_loc, - &e.input4_ptr_loc, + e.input2_loc, + e.input3_loc, + e.input4_loc, + e.input4_ptr_loc, ]) }) .boxed() diff --git a/tests/strings.rs b/tests/strings.rs index dc6526326e..7cf0badf14 100644 --- a/tests/strings.rs +++ b/tests/strings.rs @@ -1,6 +1,6 @@ use proptest::prelude::*; -use wiggle_runtime::{GuestError, GuestMemory, GuestPtr}; -use wiggle_test::{impl_errno, HostMemory, MemArea, WasiCtx}; +use wiggle_runtime::{GuestBorrows, GuestError, GuestMemory, GuestPtr}; +use wiggle_test::{impl_errno, HostMemory, MemArea, MemAreas, WasiCtx}; wiggle::from_witx!({ witx: ["tests/strings.witx"], @@ -11,12 +11,33 @@ impl_errno!(types::Errno); impl strings::Strings for WasiCtx { fn hello_string(&self, a_string: &GuestPtr) -> Result { - let s = a_string.as_raw().expect("should be valid string"); + let mut bc = GuestBorrows::new(); + let s = a_string.as_raw(&mut bc).expect("should be valid string"); unsafe { println!("a_string='{}'", &*s); Ok((*s).len() as u32) } } + + fn multi_string( + &self, + a: &GuestPtr, + b: &GuestPtr, + c: &GuestPtr, + ) -> Result { + let mut bc = GuestBorrows::new(); + let sa = a.as_raw(&mut bc).expect("A should be valid string"); + let sb = b.as_raw(&mut bc).expect("B should be valid string"); + let sc = c.as_raw(&mut bc).expect("C should be valid string"); + unsafe { + let total_len = (&*sa).len() + (&*sb).len() + (&*sc).len(); + println!( + "len={}, a='{}', b='{}', c='{}'", + total_len, &*sa, &*sb, &*sc + ); + Ok(total_len as u32) + } + } } fn test_string_strategy() -> impl Strategy { @@ -46,7 +67,7 @@ impl HelloStringExercise { return_ptr_loc, }) .prop_filter("non-overlapping pointers", |e| { - MemArea::non_overlapping_set(&[&e.string_ptr_loc, &e.return_ptr_loc]) + MemArea::non_overlapping_set(&[e.string_ptr_loc, e.return_ptr_loc]) }) .boxed() } @@ -85,3 +106,118 @@ proptest! { e.test() } } + +#[derive(Debug)] +struct MultiStringExercise { + a: String, + b: String, + c: String, + sa_ptr_loc: MemArea, + sb_ptr_loc: MemArea, + sc_ptr_loc: MemArea, + return_ptr_loc: MemArea, +} + +impl MultiStringExercise { + pub fn strat() -> BoxedStrategy { + ( + test_string_strategy(), + test_string_strategy(), + test_string_strategy(), + HostMemory::mem_area_strat(4), + ) + .prop_flat_map(|(a, b, c, return_ptr_loc)| { + ( + Just(a.clone()), + Just(b.clone()), + Just(c.clone()), + HostMemory::byte_slice_strat(a.len() as u32, &MemAreas::from([return_ptr_loc])), + Just(return_ptr_loc), + ) + }) + .prop_flat_map(|(a, b, c, sa_ptr_loc, return_ptr_loc)| { + ( + Just(a.clone()), + Just(b.clone()), + Just(c.clone()), + Just(sa_ptr_loc), + HostMemory::byte_slice_strat( + b.len() as u32, + &MemAreas::from([sa_ptr_loc, return_ptr_loc]), + ), + Just(return_ptr_loc), + ) + }) + .prop_flat_map(|(a, b, c, sa_ptr_loc, sb_ptr_loc, return_ptr_loc)| { + ( + Just(a.clone()), + Just(b.clone()), + Just(c.clone()), + Just(sa_ptr_loc), + Just(sb_ptr_loc), + HostMemory::byte_slice_strat( + c.len() as u32, + &MemAreas::from([sa_ptr_loc, sb_ptr_loc, return_ptr_loc]), + ), + Just(return_ptr_loc), + ) + }) + .prop_map( + |(a, b, c, sa_ptr_loc, sb_ptr_loc, sc_ptr_loc, return_ptr_loc)| { + MultiStringExercise { + a, + b, + c, + sa_ptr_loc, + sb_ptr_loc, + sc_ptr_loc, + return_ptr_loc, + } + }, + ) + .boxed() + } + + pub fn test(&self) { + let ctx = WasiCtx::new(); + let host_memory = HostMemory::new(); + + let write_string = |val: &str, loc: MemArea| { + let ptr = host_memory.ptr::((loc.ptr, val.len() as u32)); + for (slot, byte) in ptr.as_bytes().iter().zip(val.bytes()) { + slot.expect("should be valid pointer") + .write(byte) + .expect("failed to write"); + } + }; + + write_string(&self.a, self.sa_ptr_loc); + write_string(&self.b, self.sb_ptr_loc); + write_string(&self.c, self.sc_ptr_loc); + + let res = strings::multi_string( + &ctx, + &host_memory, + self.sa_ptr_loc.ptr as i32, + self.a.len() as i32, + self.sb_ptr_loc.ptr as i32, + self.b.len() as i32, + self.sc_ptr_loc.ptr as i32, + self.c.len() as i32, + self.return_ptr_loc.ptr as i32, + ); + assert_eq!(res, types::Errno::Ok.into(), "multi string errno"); + + let given = host_memory + .ptr::(self.return_ptr_loc.ptr) + .read() + .expect("deref ptr to return value"); + assert_eq!((self.a.len() + self.b.len() + self.c.len()) as u32, given); + } +} +proptest! { + #[test] + fn multi_string(e in MultiStringExercise::strat()) { + e.test() + } +} diff --git a/tests/strings.witx b/tests/strings.witx index ebc0f8bf05..b3531e87bb 100644 --- a/tests/strings.witx +++ b/tests/strings.witx @@ -5,4 +5,12 @@ (result $error $errno) (result $total_bytes u32) ) + + (@interface func (export "multi_string") + (param $a string) + (param $b string) + (param $c string) + (result $error $errno) + (result $total_bytes u32) + ) ) diff --git a/tests/structs.rs b/tests/structs.rs index 420d02561e..69abc4b003 100644 --- a/tests/structs.rs +++ b/tests/structs.rs @@ -72,7 +72,7 @@ impl SumOfPairExercise { return_loc, }) .prop_filter("non-overlapping pointers", |e| { - MemArea::non_overlapping_set(&[&e.input_loc, &e.return_loc]) + MemArea::non_overlapping_set(&[e.input_loc, e.return_loc]) }) .boxed() } @@ -157,10 +157,10 @@ impl SumPairPtrsExercise { ) .prop_filter("non-overlapping pointers", |e| { MemArea::non_overlapping_set(&[ - &e.input_first_loc, - &e.input_second_loc, - &e.input_struct_loc, - &e.return_loc, + e.input_first_loc, + e.input_second_loc, + e.input_struct_loc, + e.return_loc, ]) }) .boxed() @@ -245,11 +245,7 @@ impl SumIntAndPtrExercise { }, ) .prop_filter("non-overlapping pointers", |e| { - MemArea::non_overlapping_set(&[ - &e.input_first_loc, - &e.input_struct_loc, - &e.return_loc, - ]) + MemArea::non_overlapping_set(&[e.input_first_loc, e.input_struct_loc, e.return_loc]) }) .boxed() } @@ -371,11 +367,7 @@ impl ReturnPairPtrsExercise { }, ) .prop_filter("non-overlapping pointers", |e| { - MemArea::non_overlapping_set(&[ - &e.input_first_loc, - &e.input_second_loc, - &e.return_loc, - ]) + MemArea::non_overlapping_set(&[e.input_first_loc, e.input_second_loc, e.return_loc]) }) .boxed() } diff --git a/tests/union.rs b/tests/union.rs index 6081b0d4e7..87f3fb5464 100644 --- a/tests/union.rs +++ b/tests/union.rs @@ -99,7 +99,7 @@ impl GetTagExercise { return_loc, }) .prop_filter("non-overlapping pointers", |e| { - MemArea::non_overlapping_set(&[&e.input_loc, &e.return_loc]) + MemArea::non_overlapping_set(&[e.input_loc, e.return_loc]) }) .boxed() } @@ -176,7 +176,7 @@ impl ReasonMultExercise { }, ) .prop_filter("non-overlapping pointers", |e| { - MemArea::non_overlapping_set(&[&e.input_loc, &e.input_pointee_loc]) + MemArea::non_overlapping_set(&[e.input_loc, e.input_pointee_loc]) }) .boxed() } From 6e3ec6a96dc8d6f0e7458bcf20d15c7c65756a96 Mon Sep 17 00:00:00 2001 From: Pat Hickey Date: Fri, 6 Mar 2020 16:55:05 -0800 Subject: [PATCH 83/86] Minor refactors to make wiggle-generate reusable externally (#34) * wiggle-generate: pass witx doc in explicitly * wiggle-generate: Names takes &Config, minor refactor for reuse --- Cargo.toml | 1 + crates/generate/src/funcs.rs | 20 ++++---------------- crates/generate/src/lib.rs | 4 +--- crates/generate/src/names.rs | 14 ++++++++++++-- src/lib.rs | 3 ++- 5 files changed, 20 insertions(+), 22 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 2c6400e249..1d65785e39 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,6 +9,7 @@ proc-macro = true [dependencies] wiggle-generate = { path = "crates/generate" } +witx = "0.8.3" syn = { version = "1.0", features = ["full"] } [dev-dependencies] diff --git a/crates/generate/src/funcs.rs b/crates/generate/src/funcs.rs index 1902a9a6ff..6433f7712a 100644 --- a/crates/generate/src/funcs.rs +++ b/crates/generate/src/funcs.rs @@ -11,22 +11,10 @@ pub fn define_func(names: &Names, func: &witx::InterfaceFunc) -> TokenStream { let ctx_type = names.ctx_type(); let coretype = func.core_type(); - let params = coretype.args.iter().map(|arg| match arg.signifies { - witx::CoreParamSignifies::Value(atom) => { - let atom = names.atom_type(atom); - let name = names.func_param(&arg.param.name); - quote!(#name : #atom) - } - witx::CoreParamSignifies::PointerTo => { - let atom = names.atom_type(witx::AtomType::I32); - let name = names.func_ptr_binding(&arg.param.name); - quote!(#name: #atom) - } - witx::CoreParamSignifies::LengthOf => { - let atom = names.atom_type(witx::AtomType::I32); - let name = names.func_len_binding(&arg.param.name); - quote!(#name: #atom) - } + let params = coretype.args.iter().map(|arg| { + let name = names.func_core_arg(arg); + let atom = names.atom_type(arg.repr()); + quote!(#name : #atom) }); let abi_args = quote!( diff --git a/crates/generate/src/lib.rs b/crates/generate/src/lib.rs index 1b235ca2f5..a84707a4da 100644 --- a/crates/generate/src/lib.rs +++ b/crates/generate/src/lib.rs @@ -14,9 +14,7 @@ pub use module_trait::define_module_trait; pub use names::Names; pub use types::define_datatype; -pub fn generate(config: Config) -> TokenStream { - let doc = witx::load(&config.witx.paths).expect("loading witx"); - +pub fn generate(doc: &witx::Document, config: &Config) -> TokenStream { let names = Names::new(config); // TODO parse the names from the invocation of the macro, or from a file? let types = doc.typenames().map(|t| define_datatype(&names, &t)); diff --git a/crates/generate/src/names.rs b/crates/generate/src/names.rs index e66b6f8d81..d545b37845 100644 --- a/crates/generate/src/names.rs +++ b/crates/generate/src/names.rs @@ -12,8 +12,10 @@ pub struct Names { } impl Names { - pub fn new(config: Config) -> Names { - Names { config } + pub fn new(config: &Config) -> Names { + Names { + config: config.clone(), + } } pub fn ctx_type(&self) -> Ident { self.config.ctx.name.clone() @@ -118,6 +120,14 @@ impl Names { } } + pub fn func_core_arg(&self, arg: &witx::CoreParamType) -> Ident { + match arg.signifies { + witx::CoreParamSignifies::Value { .. } => self.func_param(&arg.param.name), + witx::CoreParamSignifies::PointerTo => self.func_ptr_binding(&arg.param.name), + witx::CoreParamSignifies::LengthOf => self.func_len_binding(&arg.param.name), + } + } + /// For when you need a {name}_ptr binding for passing a value by reference: pub fn func_ptr_binding(&self, id: &Id) -> Ident { format_ident!("{}_ptr", id.as_str().to_snake_case()) diff --git a/src/lib.rs b/src/lib.rs index 24e2e4909b..84b96f1895 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,5 +6,6 @@ use syn::parse_macro_input; #[proc_macro] pub fn from_witx(args: TokenStream) -> TokenStream { let config = parse_macro_input!(args as wiggle_generate::Config); - TokenStream::from(wiggle_generate::generate(config)) + let doc = witx::load(&config.witx.paths).expect("loading witx"); + TokenStream::from(wiggle_generate::generate(&doc, &config)) } From 06bcac3e43c63d81e5993de604568dfa45eb80a5 Mon Sep 17 00:00:00 2001 From: Pat Hickey Date: Fri, 6 Mar 2020 17:45:09 -0800 Subject: [PATCH 84/86] Allow creation of GuestPtr<[T]> from GuestPtr and length (#39) * add GuestPtr::as_array method * wasi test: show with type signatures we have achieved the desired api --- crates/runtime/src/lib.rs | 11 ++++++++++- tests/wasi.rs | 22 ++++++++++++++++++++-- 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/crates/runtime/src/lib.rs b/crates/runtime/src/lib.rs index a101a19a13..a7f890ca56 100644 --- a/crates/runtime/src/lib.rs +++ b/crates/runtime/src/lib.rs @@ -306,7 +306,7 @@ impl<'a, T: ?Sized + Pointee> GuestPtr<'a, T> { T::read(self) } - /// Safely write a valud to this pointer. + /// Safely write a value to this pointer. /// /// This method, like [`GuestPtr::read`], is pretty crucial for the safe /// operation of this crate. All the same reasons apply though for why this @@ -342,6 +342,15 @@ impl<'a, T: ?Sized + Pointee> GuestPtr<'a, T> { }; Ok(GuestPtr::new(self.mem, offset)) } + + /// Returns a `GuestPtr` for an array of `T`s using this pointer as the + /// base. + pub fn as_array(&self, elems: u32) -> GuestPtr<'a, [T]> + where + T: GuestType<'a> + Pointee, + { + GuestPtr::new(self.mem, (self.pointer, elems)) + } } impl<'a, T> GuestPtr<'a, [T]> { diff --git a/tests/wasi.rs b/tests/wasi.rs index 6841926b9d..0b27fb3ea0 100644 --- a/tests/wasi.rs +++ b/tests/wasi.rs @@ -1,4 +1,4 @@ -use wiggle_runtime::{GuestError, GuestErrorType, GuestPtr}; +use wiggle_runtime::{GuestBorrows, GuestError, GuestErrorType, GuestPtr}; use wiggle_test::WasiCtx; wiggle::from_witx!({ @@ -120,9 +120,27 @@ impl crate::wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { fn fd_pread( &self, _fd: types::Fd, - _iovs: &types::IovecArray<'_>, + iovs: &types::IovecArray<'_>, _offset: types::Filesize, ) -> Result { + // This is not functional code, but the type annotations demonstrate + // that we can use the wiggle API to create the datastructures we want + // for efficient implementation of this function elsewhere. + + let mut bc = GuestBorrows::new(); + let mut slices: Vec<&'_ mut [u8]> = Vec::new(); + for iov_ptr in iovs.iter() { + let iov: types::Iovec = iov_ptr + .expect("iovec element pointer is valid") + .read() + .expect("read iovec element"); + let base: GuestPtr = iov.buf; + let len: u32 = iov.buf_len; + let buf: GuestPtr<[u8]> = base.as_array(len); + let slice = buf.as_raw(&mut bc).expect("borrow slice from iovec"); + slices.push(unsafe { &mut *slice }); + } + println!("iovec slices: {:?}", slices); unimplemented!("fd_pread") } From 2139020d6dd1230868318013a352bc848c702f50 Mon Sep 17 00:00:00 2001 From: Pat Hickey Date: Tue, 10 Mar 2020 12:33:02 -0700 Subject: [PATCH 85/86] add manual interface for borrowing a GuestPtr (#40) * add manual interface for borrowing a GuestPtr * add manual borrow checking methods for slice and str as well * update test to use borrow_slice --- crates/runtime/src/borrow.rs | 39 ++++++++++++++++++++++++++++++++++-- tests/wasi.rs | 10 +++++---- 2 files changed, 43 insertions(+), 6 deletions(-) diff --git a/crates/runtime/src/borrow.rs b/crates/runtime/src/borrow.rs index 5c3c80f429..8d5d81c01a 100644 --- a/crates/runtime/src/borrow.rs +++ b/crates/runtime/src/borrow.rs @@ -1,5 +1,5 @@ use crate::region::Region; -use crate::GuestError; +use crate::{GuestError, GuestPtr, GuestType}; #[derive(Debug)] pub struct GuestBorrows { @@ -17,7 +17,7 @@ impl GuestBorrows { !self.borrows.iter().all(|b| !b.overlaps(r)) } - pub fn borrow(&mut self, r: Region) -> Result<(), GuestError> { + pub(crate) fn borrow(&mut self, r: Region) -> Result<(), GuestError> { if self.is_borrowed(r) { Err(GuestError::PtrBorrowed(r)) } else { @@ -25,6 +25,41 @@ impl GuestBorrows { Ok(()) } } + + /// Borrow the region of memory pointed to by a `GuestPtr`. This is required for safety if + /// you are dereferencing `GuestPtr`s while holding a reference to a slice via + /// `GuestPtr::as_raw`. + pub fn borrow_pointee<'a, T>(&mut self, p: &GuestPtr<'a, T>) -> Result<(), GuestError> + where + T: GuestType<'a>, + { + self.borrow(Region { + start: p.offset(), + len: T::guest_size(), + }) + } + + /// Borrow the slice of memory pointed to by a `GuestPtr<[T]>`. This is required for safety if + /// you are dereferencing the `GuestPtr`s while holding a reference to another slice via + /// `GuestPtr::as_raw`. Not required if using `GuestPtr::as_raw` on this pointer. + pub fn borrow_slice<'a, T>(&mut self, p: &GuestPtr<'a, [T]>) -> Result<(), GuestError> + where + T: GuestType<'a>, + { + let (start, elems) = p.offset(); + let len = T::guest_size() + .checked_mul(elems) + .ok_or_else(|| GuestError::PtrOverflow)?; + self.borrow(Region { start, len }) + } + + /// Borrow the slice of memory pointed to by a `GuestPtr`. This is required for safety if + /// you are dereferencing the `GuestPtr`s while holding a reference to another slice via + /// `GuestPtr::as_raw`. Not required if using `GuestPtr::as_raw` on this pointer. + pub fn borrow_str(&mut self, p: &GuestPtr) -> Result<(), GuestError> { + let (start, len) = p.offset(); + self.borrow(Region { start, len }) + } } #[cfg(test)] diff --git a/tests/wasi.rs b/tests/wasi.rs index 0b27fb3ea0..accefb7584 100644 --- a/tests/wasi.rs +++ b/tests/wasi.rs @@ -129,11 +129,13 @@ impl crate::wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { let mut bc = GuestBorrows::new(); let mut slices: Vec<&'_ mut [u8]> = Vec::new(); + // Mark the iov elements as borrowed, to ensure that they does not + // overlap with any of the as_raw regions. + bc.borrow_slice(&iovs).expect("borrow iovec array"); for iov_ptr in iovs.iter() { - let iov: types::Iovec = iov_ptr - .expect("iovec element pointer is valid") - .read() - .expect("read iovec element"); + let iov_ptr = iov_ptr.expect("iovec element pointer is valid"); + + let iov: types::Iovec = iov_ptr.read().expect("read iovec element"); let base: GuestPtr = iov.buf; let len: u32 = iov.buf_len; let buf: GuestPtr<[u8]> = base.as_array(len); From cd484e49932d8dd8f1bd1a002e0717ad8bff07fb Mon Sep 17 00:00:00 2001 From: Pat Hickey Date: Tue, 10 Mar 2020 14:48:57 -0700 Subject: [PATCH 86/86] add a lifetime to the wiggle_runtime::GuestErrorType trait (#41) * add a lifetime to the wiggle_runtime::GuestErrorType trait, wiggle_tests::WasiCtx struct * wiggle-generate: make config parsing public so it can be reused in lucet --- crates/generate/src/config.rs | 21 ++++++++++++++------- crates/generate/src/lib.rs | 2 +- crates/runtime/src/guest_type.rs | 2 +- crates/test/src/lib.rs | 13 +++++++++---- tests/arrays.rs | 2 +- tests/atoms.rs | 2 +- tests/flags.rs | 2 +- tests/handles.rs | 2 +- tests/ints.rs | 2 +- tests/pointers.rs | 10 +++++----- tests/strings.rs | 2 +- tests/structs.rs | 10 +++++----- tests/union.rs | 2 +- tests/wasi.rs | 8 ++++---- 14 files changed, 46 insertions(+), 34 deletions(-) diff --git a/crates/generate/src/config.rs b/crates/generate/src/config.rs index 9e7848e82c..0941e73431 100644 --- a/crates/generate/src/config.rs +++ b/crates/generate/src/config.rs @@ -14,25 +14,32 @@ pub struct Config { pub ctx: CtxConf, } -enum ConfigField { +#[derive(Debug, Clone)] +pub enum ConfigField { Witx(WitxConf), Ctx(CtxConf), } +impl ConfigField { + pub fn parse_pair(ident: &str, value: ParseStream, err_loc: Span) -> Result { + match ident { + "witx" => Ok(ConfigField::Witx(value.parse()?)), + "ctx" => Ok(ConfigField::Ctx(value.parse()?)), + _ => Err(Error::new(err_loc, "expected `witx` or `ctx`")), + } + } +} + impl Parse for ConfigField { fn parse(input: ParseStream) -> Result { let id: Ident = input.parse()?; let _colon: Token![:] = input.parse()?; - match id.to_string().as_ref() { - "witx" => Ok(ConfigField::Witx(input.parse()?)), - "ctx" => Ok(ConfigField::Ctx(input.parse()?)), - _ => Err(Error::new(id.span(), "expected `witx` or `ctx`")), - } + Self::parse_pair(id.to_string().as_ref(), input, id.span()) } } impl Config { - fn build(fields: impl Iterator, err_loc: Span) -> Result { + pub fn build(fields: impl Iterator, err_loc: Span) -> Result { let mut witx = None; let mut ctx = None; for f in fields { diff --git a/crates/generate/src/lib.rs b/crates/generate/src/lib.rs index a84707a4da..d253f2604e 100644 --- a/crates/generate/src/lib.rs +++ b/crates/generate/src/lib.rs @@ -1,4 +1,4 @@ -mod config; +pub mod config; mod funcs; mod lifetimes; mod module_trait; diff --git a/crates/runtime/src/guest_type.rs b/crates/runtime/src/guest_type.rs index 981301da46..1a654da17e 100644 --- a/crates/runtime/src/guest_type.rs +++ b/crates/runtime/src/guest_type.rs @@ -1,7 +1,7 @@ use crate::{GuestError, GuestPtr}; use std::mem; -pub trait GuestErrorType { +pub trait GuestErrorType<'a> { type Context; fn success() -> Self; fn from_error(e: GuestError, ctx: &Self::Context) -> Self; diff --git a/crates/test/src/lib.rs b/crates/test/src/lib.rs index de4474a528..8ec8f916dd 100644 --- a/crates/test/src/lib.rs +++ b/crates/test/src/lib.rs @@ -1,5 +1,6 @@ use proptest::prelude::*; use std::cell::UnsafeCell; +use std::marker; use wiggle_runtime::GuestMemory; #[derive(Debug, Clone)] @@ -290,14 +291,18 @@ mod test { use std::cell::RefCell; use wiggle_runtime::GuestError; -pub struct WasiCtx { +// In lucet, our Ctx struct needs a lifetime, so we're using one +// on the test as well. +pub struct WasiCtx<'a> { pub guest_errors: RefCell>, + lifetime: marker::PhantomData<&'a ()>, } -impl WasiCtx { +impl<'a> WasiCtx<'a> { pub fn new() -> Self { Self { guest_errors: RefCell::new(vec![]), + lifetime: marker::PhantomData, } } } @@ -309,8 +314,8 @@ impl WasiCtx { #[macro_export] macro_rules! impl_errno { ( $errno:ty ) => { - impl wiggle_runtime::GuestErrorType for $errno { - type Context = WasiCtx; + impl<'a> wiggle_runtime::GuestErrorType<'a> for $errno { + type Context = WasiCtx<'a>; fn success() -> $errno { <$errno>::Ok } diff --git a/tests/arrays.rs b/tests/arrays.rs index 7e6e7b332f..b0dea82a54 100644 --- a/tests/arrays.rs +++ b/tests/arrays.rs @@ -9,7 +9,7 @@ wiggle::from_witx!({ impl_errno!(types::Errno); -impl arrays::Arrays for WasiCtx { +impl<'a> arrays::Arrays for WasiCtx<'a> { fn reduce_excuses( &self, excuses: &types::ConstExcuseArray, diff --git a/tests/atoms.rs b/tests/atoms.rs index 594edb13ab..d19d19c209 100644 --- a/tests/atoms.rs +++ b/tests/atoms.rs @@ -9,7 +9,7 @@ wiggle::from_witx!({ impl_errno!(types::Errno); -impl atoms::Atoms for WasiCtx { +impl<'a> atoms::Atoms for WasiCtx<'a> { fn int_float_args(&self, an_int: u32, an_float: f32) -> Result<(), types::Errno> { println!("INT FLOAT ARGS: {} {}", an_int, an_float); Ok(()) diff --git a/tests/flags.rs b/tests/flags.rs index d00c5b84f0..78939fc3d6 100644 --- a/tests/flags.rs +++ b/tests/flags.rs @@ -10,7 +10,7 @@ wiggle::from_witx!({ impl_errno!(types::Errno); -impl flags::Flags for WasiCtx { +impl<'a> flags::Flags for WasiCtx<'a> { fn configure_car( &self, old_config: types::CarConfig, diff --git a/tests/handles.rs b/tests/handles.rs index 9cb391a190..c9e44122ec 100644 --- a/tests/handles.rs +++ b/tests/handles.rs @@ -11,7 +11,7 @@ wiggle::from_witx!({ impl_errno!(types::Errno); -impl handle_examples::HandleExamples for WasiCtx { +impl<'a> handle_examples::HandleExamples for WasiCtx<'a> { fn fd_create(&self) -> Result { Ok(types::Fd::from(FD_VAL)) } diff --git a/tests/ints.rs b/tests/ints.rs index 12cb8c929c..b4adeb2969 100644 --- a/tests/ints.rs +++ b/tests/ints.rs @@ -10,7 +10,7 @@ wiggle::from_witx!({ impl_errno!(types::Errno); -impl ints::Ints for WasiCtx { +impl<'a> ints::Ints for WasiCtx<'a> { fn cookie_cutter(&self, init_cookie: types::Cookie) -> Result { let res = if init_cookie == types::Cookie::START { types::Bool::True diff --git a/tests/pointers.rs b/tests/pointers.rs index f15a53349e..188fa9822c 100644 --- a/tests/pointers.rs +++ b/tests/pointers.rs @@ -9,13 +9,13 @@ wiggle::from_witx!({ impl_errno!(types::Errno); -impl pointers::Pointers for WasiCtx { - fn pointers_and_enums<'a>( +impl<'a> pointers::Pointers for WasiCtx<'a> { + fn pointers_and_enums<'b>( &self, input1: types::Excuse, - input2_ptr: GuestPtr<'a, types::Excuse>, - input3_ptr: GuestPtr<'a, types::Excuse>, - input4_ptr_ptr: GuestPtr<'a, GuestPtr<'a, types::Excuse>>, + input2_ptr: GuestPtr<'b, types::Excuse>, + input3_ptr: GuestPtr<'b, types::Excuse>, + input4_ptr_ptr: GuestPtr<'b, GuestPtr<'b, types::Excuse>>, ) -> Result<(), types::Errno> { println!("BAZ input1 {:?}", input1); let input2: types::Excuse = input2_ptr.read().map_err(|e| { diff --git a/tests/strings.rs b/tests/strings.rs index 7cf0badf14..7fca007b4d 100644 --- a/tests/strings.rs +++ b/tests/strings.rs @@ -9,7 +9,7 @@ wiggle::from_witx!({ impl_errno!(types::Errno); -impl strings::Strings for WasiCtx { +impl<'a> strings::Strings for WasiCtx<'a> { fn hello_string(&self, a_string: &GuestPtr) -> Result { let mut bc = GuestBorrows::new(); let s = a_string.as_raw(&mut bc).expect("should be valid string"); diff --git a/tests/structs.rs b/tests/structs.rs index 69abc4b003..0b00057d9a 100644 --- a/tests/structs.rs +++ b/tests/structs.rs @@ -9,7 +9,7 @@ wiggle::from_witx!({ impl_errno!(types::Errno); -impl structs::Structs for WasiCtx { +impl<'a> structs::Structs for WasiCtx<'a> { fn sum_of_pair(&self, an_pair: &types::PairInts) -> Result { Ok(an_pair.first as i64 + an_pair.second as i64) } @@ -42,11 +42,11 @@ impl structs::Structs for WasiCtx { }) } - fn return_pair_of_ptrs<'a>( + fn return_pair_of_ptrs<'b>( &self, - first: GuestPtr<'a, i32>, - second: GuestPtr<'a, i32>, - ) -> Result, types::Errno> { + first: GuestPtr<'b, i32>, + second: GuestPtr<'b, i32>, + ) -> Result, types::Errno> { Ok(types::PairIntPtrs { first, second }) } } diff --git a/tests/union.rs b/tests/union.rs index 87f3fb5464..b2510e4ef9 100644 --- a/tests/union.rs +++ b/tests/union.rs @@ -31,7 +31,7 @@ fn mult_zero_nan(a: f32, b: u32) -> f32 { } } -impl union_example::UnionExample for WasiCtx { +impl<'a> union_example::UnionExample for WasiCtx<'a> { fn get_tag(&self, u: &types::Reason) -> Result { println!("GET TAG: {:?}", u); match u { diff --git a/tests/wasi.rs b/tests/wasi.rs index accefb7584..6ad5612b69 100644 --- a/tests/wasi.rs +++ b/tests/wasi.rs @@ -8,21 +8,21 @@ wiggle::from_witx!({ type Result = std::result::Result; -impl GuestErrorType for types::Errno { - type Context = WasiCtx; +impl<'a> GuestErrorType<'a> for types::Errno { + type Context = WasiCtx<'a>; fn success() -> types::Errno { types::Errno::Success } - fn from_error(e: GuestError, ctx: &WasiCtx) -> types::Errno { + fn from_error(e: GuestError, ctx: &Self::Context) -> types::Errno { eprintln!("GUEST ERROR: {:?}", e); ctx.guest_errors.borrow_mut().push(e); types::Errno::Io } } -impl crate::wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { +impl<'a> crate::wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx<'a> { fn args_get(&self, _argv: GuestPtr>, _argv_buf: GuestPtr) -> Result<()> { unimplemented!("args_get") }