diff --git a/src/interpreter/instructions/records.rs b/src/interpreter/instructions/records.rs index a5d46ec..e63dbf8 100644 --- a/src/interpreter/instructions/records.rs +++ b/src/interpreter/instructions/records.rs @@ -7,6 +7,7 @@ use crate::{ }, types::{InterfaceType, RecordType}, values::{FlattenInterfaceValueIterator, InterfaceValue}, + vec1::Vec1, }; use std::collections::VecDeque; @@ -56,7 +57,10 @@ fn record_lift_( } } - Ok(InterfaceValue::Record(values.into_iter().collect())) + Ok(InterfaceValue::Record( + Vec1::new(values.into_iter().collect()) + .expect("Record must have at least one field, zero given"), // normally unreachable because of the type-checking + )) } executable_instruction!( @@ -110,7 +114,7 @@ executable_instruction!( }; match runtime.stack.pop1() { - Some(InterfaceValue::Record(record_values)) if record_type == &(&record_values).into() => { + Some(InterfaceValue::Record(record_values)) if record_type == &(&*record_values).into() => { let values = FlattenInterfaceValueIterator::new(&record_values); for value in values { @@ -157,9 +161,9 @@ mod tests { InterfaceValue::I64(3), ], instance: Instance::new(), - stack: [InterfaceValue::Record(vec![ + stack: [InterfaceValue::Record(vec1![ InterfaceValue::I32(1), - InterfaceValue::Record(vec![ + InterfaceValue::Record(vec1![ InterfaceValue::String("Hello".to_string()), InterfaceValue::F32(2.), ]), @@ -255,7 +259,7 @@ mod tests { instance }, - stack: [InterfaceValue::Record(vec![ + stack: [InterfaceValue::Record(vec1![ InterfaceValue::I32(1), InterfaceValue::I32(2), ])], @@ -298,9 +302,9 @@ mod tests { Instruction::RecordLower { type_index: 0 }, ], invocation_inputs: [ - InterfaceValue::Record(vec![ + InterfaceValue::Record(vec1![ InterfaceValue::I32(1), - InterfaceValue::Record(vec![ + InterfaceValue::Record(vec1![ InterfaceValue::String("Hello".to_string()), InterfaceValue::F32(2.), ]), @@ -324,9 +328,9 @@ mod tests { Instruction::RecordLift { type_index: 0 }, ], invocation_inputs: [ - InterfaceValue::Record(vec![ + InterfaceValue::Record(vec1![ InterfaceValue::I32(1), - InterfaceValue::Record(vec![ + InterfaceValue::Record(vec1![ InterfaceValue::String("Hello".to_string()), InterfaceValue::F32(2.), ]), @@ -335,9 +339,9 @@ mod tests { ], instance: Instance::new(), stack: [ - InterfaceValue::Record(vec![ + InterfaceValue::Record(vec1![ InterfaceValue::I32(1), - InterfaceValue::Record(vec![ + InterfaceValue::Record(vec1![ InterfaceValue::String("Hello".to_string()), InterfaceValue::F32(2.), ]), @@ -366,9 +370,9 @@ mod tests { Instruction::RecordLower { type_index: 0 }, ], invocation_inputs: [ - InterfaceValue::Record(vec![ + InterfaceValue::Record(vec1![ InterfaceValue::I32(1), - InterfaceValue::Record(vec![ + InterfaceValue::Record(vec1![ InterfaceValue::String("Hello".to_string()), ]), InterfaceValue::I64(3), diff --git a/src/lib.rs b/src/lib.rs index c93bb51..f56a6f6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,16 +1,18 @@ //! This crate contains an implementation of [WebAssembly Interface -//! Types][wit] (abbreviated WIT). It is composed of 4 parts: +//! Types][wit] (abbreviated WIT). It is composed of 5 parts: //! -//! 1. [AST]: To represent the WIT language as a tree +//! 1. [Types] and [Values]: To represent the WIT types and values +//! representations, +//! 2. [AST]: To represent the WIT language as a tree //! (which is not really abstract). This is the central //! representation of the language. -//! 2. [Decoders](decoders): To read the [AST] from a particular data +//! 3. [Decoders](decoders): To read the [AST] from a particular data //! representation; for instance, [`decoders::binary`] reads the //! [AST] from a binary. -//! 3. [Encoders](encoders): To write the [AST](ast) into a particular +//! 4. [Encoders](encoders): To write the [AST](ast) into a particular //! format; for instance, [`encoders::wat`] writes the [AST] into a //! string representing WIT with its textual format. -//! 4. [Interpreter](interpreter): WIT defines a concept called +//! 5. [Interpreter](interpreter): WIT defines a concept called //! Adapters. An adapter contains a set of [instructions]. So, in //! more details, this module contains: //! * [A very light and generic stack @@ -30,8 +32,9 @@ //! this is the part a runtime should take a look to use the //! `wasmer-interface-types` crate—. //! -//! //! [wit]: https://github.com/WebAssembly/interface-types +//! [Types]: types +//! [Values]: values //! [AST]: ast //! [instructions]: interpreter::Instruction diff --git a/src/macros.rs b/src/macros.rs index 4b1612e..88e7674 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -1,6 +1,8 @@ +//! Collection of helpful macros. + /// This macro creates a `Vec1` by checking at compile-time that its /// invariant holds. -#[allow(unused)] +#[macro_export] macro_rules! vec1 { ($item:expr; 0) => { compile_error!("Cannot create an empty `Vec1`, it violates its invariant.") diff --git a/src/serde/de.rs b/src/serde/de.rs index 5230862..af8bf44 100644 --- a/src/serde/de.rs +++ b/src/serde/de.rs @@ -20,9 +20,9 @@ use std::{ /// # Example /// /// ```rust -/// use wasmer_interface_types::values::{ -/// InterfaceValue, -/// from_interface_values, +/// use wasmer_interface_types::{ +/// values::{InterfaceValue, from_interface_values}, +/// vec1::Vec1, /// }; /// use serde::Deserialize; /// @@ -36,11 +36,11 @@ use std::{ /// y: f32, /// }; /// -/// let values = vec![InterfaceValue::Record(vec![ +/// let values = vec![InterfaceValue::Record(Vec1::new(vec![ /// InterfaceValue::String("abc".to_string()), -/// InterfaceValue::Record(vec![InterfaceValue::I32(1), InterfaceValue::I64(2)]), +/// InterfaceValue::Record(Vec1::new(vec![InterfaceValue::I32(1), InterfaceValue::I64(2)]).unwrap()), /// InterfaceValue::F32(3.), -/// ])]; +/// ]).unwrap())]; /// let t = from_interface_values::(&values).unwrap(); /// /// assert_eq!( @@ -513,7 +513,7 @@ mod tests { #[derive(Deserialize, Debug, PartialEq)] struct S(i8); - let input = vec![InterfaceValue::Record(vec![InterfaceValue::S8(42)])]; + let input = vec![InterfaceValue::Record(vec1![InterfaceValue::S8(42)])]; let output = S(42); assert_eq!(from_interface_values::(&input).unwrap(), output); @@ -525,7 +525,7 @@ mod tests { #[derive(Deserialize, Debug, PartialEq)] struct S(i8, f32); - let input = vec![InterfaceValue::Record(vec![ + let input = vec![InterfaceValue::Record(vec1![ InterfaceValue::S8(7), InterfaceValue::F32(42.), ])]; @@ -543,7 +543,7 @@ mod tests { y: f32, } - let input = vec![InterfaceValue::Record(vec![ + let input = vec![InterfaceValue::Record(vec1![ InterfaceValue::S8(7), InterfaceValue::F32(42.), ])]; @@ -568,13 +568,13 @@ mod tests { p2: Point, } - let input = vec![InterfaceValue::Record(vec![ - InterfaceValue::Record(vec![ + let input = vec![InterfaceValue::Record(vec1![ + InterfaceValue::Record(vec1![ InterfaceValue::I32(1), InterfaceValue::I32(2), InterfaceValue::I32(3), ]), - InterfaceValue::Record(vec![ + InterfaceValue::Record(vec1![ InterfaceValue::I32(4), InterfaceValue::I32(5), InterfaceValue::I32(6), diff --git a/src/serde/ser.rs b/src/serde/ser.rs index d87be5f..093a5d5 100644 --- a/src/serde/ser.rs +++ b/src/serde/ser.rs @@ -1,6 +1,6 @@ //! Provides a serializer from Rust value to WIT values. -use crate::values::InterfaceValue; +use crate::{values::InterfaceValue, vec1::Vec1}; use serde::{ser, Serialize}; use std::fmt::{self, Display}; @@ -13,9 +13,9 @@ use std::fmt::{self, Display}; /// # Example /// /// ```rust -/// use wasmer_interface_types::values::{ -/// InterfaceValue, -/// to_interface_value, +/// use wasmer_interface_types::{ +/// values::{InterfaceValue, to_interface_value}, +/// vec1::Vec1, /// }; /// use serde::Serialize; /// @@ -37,11 +37,11 @@ use std::fmt::{self, Display}; /// /// assert_eq!( /// to_interface_value(&input).unwrap(), -/// InterfaceValue::Record(vec![ +/// InterfaceValue::Record(Vec1::new(vec![ /// InterfaceValue::String("abc".to_string()), -/// InterfaceValue::Record(vec![InterfaceValue::I32(1), InterfaceValue::I64(2)]), +/// InterfaceValue::Record(Vec1::new(vec![InterfaceValue::I32(1), InterfaceValue::I64(2)]).unwrap()), /// InterfaceValue::F32(3.), -/// ]), +/// ]).unwrap()), /// ); /// ``` pub fn to_interface_value(value: &T) -> Result @@ -107,6 +107,9 @@ pub enum SerializeError { /// serialization. InternalValuesCorrupted, + /// A record must contain at least one field. + RecordNeedsAtLeastOneField, + /// Arbitrary message. Message(String), } @@ -128,6 +131,10 @@ impl Display for SerializeError { formatter, "the internal values have been corrutped during the serialization" ), + Self::RecordNeedsAtLeastOneField => write!( + formatter, + "a record must contain at least one field, zero given" + ), Self::Message(ref msg) => write!(formatter, "{}", msg), } } @@ -374,7 +381,9 @@ impl<'a> ser::SerializeTupleStruct for &'a mut Serializer { } fn end(self) -> Result { - let record = InterfaceValue::Record(self.pop()?); + let record = InterfaceValue::Record( + Vec1::new(self.pop()?).map_err(|_| Self::Error::RecordNeedsAtLeastOneField)?, + ); self.last().push(record); Ok(()) @@ -432,7 +441,9 @@ impl<'a> ser::SerializeStruct for &'a mut Serializer { } fn end(self) -> Result { - let record = InterfaceValue::Record(self.pop()?); + let record = InterfaceValue::Record( + Vec1::new(self.pop()?).map_err(|_| Self::Error::RecordNeedsAtLeastOneField)?, + ); self.last().push(record); Ok(()) @@ -512,7 +523,7 @@ mod tests { struct S(i8, f32); let input = S(7, 42.); - let output = InterfaceValue::Record(vec![InterfaceValue::S8(7), InterfaceValue::F32(42.)]); + let output = InterfaceValue::Record(vec1![InterfaceValue::S8(7), InterfaceValue::F32(42.)]); assert_eq!(to_interface_value(&input).unwrap(), output); } @@ -527,7 +538,7 @@ mod tests { } let input = S { x: 7, y: 42. }; - let output = InterfaceValue::Record(vec![InterfaceValue::S8(7), InterfaceValue::F32(42.)]); + let output = InterfaceValue::Record(vec1![InterfaceValue::S8(7), InterfaceValue::F32(42.)]); assert_eq!(to_interface_value(&input).unwrap(), output); } @@ -552,13 +563,13 @@ mod tests { p1: Point { x: 1, y: 2, z: 3 }, p2: Point { x: 4, y: 5, z: 6 }, }; - let output = InterfaceValue::Record(vec![ - InterfaceValue::Record(vec![ + let output = InterfaceValue::Record(vec1![ + InterfaceValue::Record(vec1![ InterfaceValue::I32(1), InterfaceValue::I32(2), InterfaceValue::I32(3), ]), - InterfaceValue::Record(vec![ + InterfaceValue::Record(vec1![ InterfaceValue::I32(4), InterfaceValue::I32(5), InterfaceValue::I32(6), diff --git a/src/types.rs b/src/types.rs index 70a4ca5..48ac786 100644 --- a/src/types.rs +++ b/src/types.rs @@ -55,5 +55,7 @@ pub enum InterfaceType { #[derive(PartialEq, Debug, Clone)] pub struct RecordType { /// Types representing the fields. + /// A record must have at least one field, hence the + /// [`Vec1`][crate::vec1::Vec1]. pub fields: Vec1, } diff --git a/src/values.rs b/src/values.rs index 1d3cb26..2bb871e 100644 --- a/src/values.rs +++ b/src/values.rs @@ -1,8 +1,9 @@ //! Defines WIT values and associated operations. use crate::{ + errors::WasmValueNativeCastError, types::{InterfaceType, RecordType}, - {errors::WasmValueNativeCastError, vec1::Vec1}, + vec1::Vec1, }; use std::{convert::TryFrom, slice::Iter}; @@ -53,7 +54,7 @@ pub enum InterfaceValue { I64(i64), /// A record. - Record(Vec), + Record(Vec1), } impl From<&InterfaceValue> for InterfaceType { @@ -73,7 +74,7 @@ impl From<&InterfaceValue> for InterfaceType { //InterfaceValue::Anyref(_) => Self::Anyref, InterfaceValue::I32(_) => Self::I32, InterfaceValue::I64(_) => Self::I64, - InterfaceValue::Record(values) => Self::Record(values.into()), + InterfaceValue::Record(values) => Self::Record((&**values).into()), } } } @@ -217,7 +218,7 @@ mod tests { #[allow(non_snake_case)] fn interface_type_from_interface_value__record() { assert_eq!( - InterfaceType::from(&InterfaceValue::Record(vec![ + InterfaceType::from(&InterfaceValue::Record(vec1![ InterfaceValue::I32(1), InterfaceValue::S8(2) ])), @@ -227,9 +228,9 @@ mod tests { ); assert_eq!( - InterfaceType::from(&InterfaceValue::Record(vec![ + InterfaceType::from(&InterfaceValue::Record(vec1![ InterfaceValue::I32(1), - InterfaceValue::Record(vec![ + InterfaceValue::Record(vec1![ InterfaceValue::String("a".to_string()), InterfaceValue::F64(42.) ]), diff --git a/tests/binary.rs b/tests/binary.rs index 4e31214..81f8730 100644 --- a/tests/binary.rs +++ b/tests/binary.rs @@ -1,6 +1,6 @@ use wasmer_interface_types::{ ast::*, decoders::binary::parse, encoders::binary::ToBytes, interpreter::Instruction, types::*, - vec1::Vec1, + vec1, }; /// Tests an AST to binary, then binary to AST roundtrip. @@ -17,7 +17,7 @@ fn test_binary_encoding_decoding_roundtrip() { outputs: vec![InterfaceType::S32], }, Type::Record(RecordType { - fields: Vec1::new(vec![InterfaceType::String, InterfaceType::I32]).unwrap(), + fields: vec1![InterfaceType::String, InterfaceType::I32], }), ], imports: vec![Import {