mirror of
https://github.com/fluencelabs/wasmer
synced 2025-06-29 00:21:34 +00:00
feat(interface-types) Add the Vec1
type to represent a non-empty vector.
`Vec1` is used by `RecordType` to ensure that a record have at least 1 field. Then an `InterfaceValue::Record` is ensured to get at least one value with the type-checking pass.
This commit is contained in:
@ -1,7 +1,7 @@
|
|||||||
//! Represents the WIT language as a tree. This is the central
|
//! Represents the WIT language as a tree. This is the central
|
||||||
//! representation of the language.
|
//! representation of the language.
|
||||||
|
|
||||||
use crate::interpreter::Instruction;
|
use crate::{interpreter::Instruction, vec1::Vec1};
|
||||||
use std::str;
|
use std::str;
|
||||||
|
|
||||||
/// Represents the types supported by WIT.
|
/// Represents the types supported by WIT.
|
||||||
@ -57,7 +57,7 @@ pub enum InterfaceType {
|
|||||||
#[derive(PartialEq, Debug, Clone)]
|
#[derive(PartialEq, Debug, Clone)]
|
||||||
pub struct RecordType {
|
pub struct RecordType {
|
||||||
/// Types representing the fields.
|
/// Types representing the fields.
|
||||||
pub fields: Vec<InterfaceType>,
|
pub fields: Vec1<InterfaceType>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Represents the kind of type.
|
/// Represents the kind of type.
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
//! Parse the WIT binary representation into an [AST](crate::ast).
|
//! Parse the WIT binary representation into an [AST](crate::ast).
|
||||||
|
|
||||||
use crate::{ast::*, interpreter::Instruction};
|
use crate::{ast::*, interpreter::Instruction, vec1::Vec1};
|
||||||
use nom::{
|
use nom::{
|
||||||
error::{make_error, ErrorKind, ParseError},
|
error::{make_error, ErrorKind, ParseError},
|
||||||
Err, IResult,
|
Err, IResult,
|
||||||
@ -112,7 +112,12 @@ fn record_type<'input, E: ParseError<&'input [u8]>>(
|
|||||||
) -> IResult<&'input [u8], RecordType, E> {
|
) -> IResult<&'input [u8], RecordType, E> {
|
||||||
let (output, fields) = list(input, ty)?;
|
let (output, fields) = list(input, ty)?;
|
||||||
|
|
||||||
Ok((output, RecordType { fields }))
|
Ok((
|
||||||
|
output,
|
||||||
|
RecordType {
|
||||||
|
fields: Vec1::new(fields).expect("Record must have at least one field, zero given."),
|
||||||
|
},
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse a UTF-8 string.
|
/// Parse a UTF-8 string.
|
||||||
@ -640,7 +645,7 @@ mod tests {
|
|||||||
InterfaceType::I32,
|
InterfaceType::I32,
|
||||||
InterfaceType::I64,
|
InterfaceType::I64,
|
||||||
InterfaceType::Record(RecordType {
|
InterfaceType::Record(RecordType {
|
||||||
fields: vec![InterfaceType::S32],
|
fields: vec1![InterfaceType::S32],
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
));
|
));
|
||||||
@ -670,16 +675,16 @@ mod tests {
|
|||||||
&[0x01][..],
|
&[0x01][..],
|
||||||
vec![
|
vec![
|
||||||
RecordType {
|
RecordType {
|
||||||
fields: vec![InterfaceType::String],
|
fields: vec1![InterfaceType::String],
|
||||||
},
|
},
|
||||||
RecordType {
|
RecordType {
|
||||||
fields: vec![InterfaceType::String, InterfaceType::I32],
|
fields: vec1![InterfaceType::String, InterfaceType::I32],
|
||||||
},
|
},
|
||||||
RecordType {
|
RecordType {
|
||||||
fields: vec![
|
fields: vec1![
|
||||||
InterfaceType::String,
|
InterfaceType::String,
|
||||||
InterfaceType::Record(RecordType {
|
InterfaceType::Record(RecordType {
|
||||||
fields: vec![InterfaceType::I32, InterfaceType::I32],
|
fields: vec1![InterfaceType::I32, InterfaceType::I32],
|
||||||
}),
|
}),
|
||||||
InterfaceType::F64,
|
InterfaceType::F64,
|
||||||
],
|
],
|
||||||
@ -864,7 +869,7 @@ mod tests {
|
|||||||
outputs: vec![InterfaceType::S32],
|
outputs: vec![InterfaceType::S32],
|
||||||
},
|
},
|
||||||
Type::Record(RecordType {
|
Type::Record(RecordType {
|
||||||
fields: vec![InterfaceType::S32, InterfaceType::S32],
|
fields: vec1![InterfaceType::S32, InterfaceType::S32],
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
));
|
));
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
//! Parse the WIT textual representation into an [AST](crate::ast).
|
//! Parse the WIT textual representation into an [AST](crate::ast).
|
||||||
|
|
||||||
use crate::{ast::*, interpreter::Instruction};
|
use crate::{ast::*, interpreter::Instruction, vec1::Vec1};
|
||||||
pub use wast::parser::ParseBuffer as Buffer;
|
pub use wast::parser::ParseBuffer as Buffer;
|
||||||
use wast::parser::{self, Cursor, Parse, Parser, Peek, Result};
|
use wast::parser::{self, Cursor, Parse, Parser, Peek, Result};
|
||||||
|
|
||||||
@ -151,7 +151,9 @@ impl Parse<'_> for RecordType {
|
|||||||
})?);
|
})?);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(RecordType { fields })
|
Ok(RecordType {
|
||||||
|
fields: Vec1::new(fields).expect("Record must have at least one field, zero given."),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -681,7 +683,7 @@ mod tests {
|
|||||||
InterfaceType::I32,
|
InterfaceType::I32,
|
||||||
InterfaceType::I64,
|
InterfaceType::I64,
|
||||||
InterfaceType::Record(RecordType {
|
InterfaceType::Record(RecordType {
|
||||||
fields: vec![InterfaceType::String],
|
fields: vec1![InterfaceType::String],
|
||||||
}),
|
}),
|
||||||
];
|
];
|
||||||
|
|
||||||
@ -704,16 +706,16 @@ mod tests {
|
|||||||
];
|
];
|
||||||
let outputs = vec![
|
let outputs = vec![
|
||||||
RecordType {
|
RecordType {
|
||||||
fields: vec![InterfaceType::String],
|
fields: vec1![InterfaceType::String],
|
||||||
},
|
},
|
||||||
RecordType {
|
RecordType {
|
||||||
fields: vec![InterfaceType::String, InterfaceType::I32],
|
fields: vec1![InterfaceType::String, InterfaceType::I32],
|
||||||
},
|
},
|
||||||
RecordType {
|
RecordType {
|
||||||
fields: vec![
|
fields: vec1![
|
||||||
InterfaceType::String,
|
InterfaceType::String,
|
||||||
InterfaceType::Record(RecordType {
|
InterfaceType::Record(RecordType {
|
||||||
fields: vec![InterfaceType::I32, InterfaceType::I32],
|
fields: vec1![InterfaceType::I32, InterfaceType::I32],
|
||||||
}),
|
}),
|
||||||
InterfaceType::F64,
|
InterfaceType::F64,
|
||||||
],
|
],
|
||||||
@ -874,7 +876,7 @@ mod tests {
|
|||||||
fn test_type_record() {
|
fn test_type_record() {
|
||||||
let input = buffer(r#"(@interface type (record (field string) (field i32)))"#);
|
let input = buffer(r#"(@interface type (record (field string) (field i32)))"#);
|
||||||
let output = Interface::Type(Type::Record(RecordType {
|
let output = Interface::Type(Type::Record(RecordType {
|
||||||
fields: vec![InterfaceType::String, InterfaceType::I32],
|
fields: vec1![InterfaceType::String, InterfaceType::I32],
|
||||||
}));
|
}));
|
||||||
|
|
||||||
assert_eq!(parser::parse::<Interface>(&input).unwrap(), output);
|
assert_eq!(parser::parse::<Interface>(&input).unwrap(), output);
|
||||||
|
@ -445,7 +445,7 @@ mod tests {
|
|||||||
assert_to_bytes!(InterfaceType::I64, &[0x0d]);
|
assert_to_bytes!(InterfaceType::I64, &[0x0d]);
|
||||||
assert_to_bytes!(
|
assert_to_bytes!(
|
||||||
InterfaceType::Record(RecordType {
|
InterfaceType::Record(RecordType {
|
||||||
fields: vec![InterfaceType::String]
|
fields: vec1![InterfaceType::String]
|
||||||
}),
|
}),
|
||||||
&[0x0e, 0x01, 0x0a]
|
&[0x0e, 0x01, 0x0a]
|
||||||
);
|
);
|
||||||
@ -455,7 +455,7 @@ mod tests {
|
|||||||
fn test_record_type() {
|
fn test_record_type() {
|
||||||
assert_to_bytes!(
|
assert_to_bytes!(
|
||||||
RecordType {
|
RecordType {
|
||||||
fields: vec![InterfaceType::String]
|
fields: vec1![InterfaceType::String]
|
||||||
},
|
},
|
||||||
&[
|
&[
|
||||||
0x01, // 1 field
|
0x01, // 1 field
|
||||||
@ -464,7 +464,7 @@ mod tests {
|
|||||||
);
|
);
|
||||||
assert_to_bytes!(
|
assert_to_bytes!(
|
||||||
RecordType {
|
RecordType {
|
||||||
fields: vec![InterfaceType::String, InterfaceType::I32]
|
fields: vec1![InterfaceType::String, InterfaceType::I32]
|
||||||
},
|
},
|
||||||
&[
|
&[
|
||||||
0x02, // 2 fields
|
0x02, // 2 fields
|
||||||
@ -474,10 +474,10 @@ mod tests {
|
|||||||
);
|
);
|
||||||
assert_to_bytes!(
|
assert_to_bytes!(
|
||||||
RecordType {
|
RecordType {
|
||||||
fields: vec![
|
fields: vec1![
|
||||||
InterfaceType::String,
|
InterfaceType::String,
|
||||||
InterfaceType::Record(RecordType {
|
InterfaceType::Record(RecordType {
|
||||||
fields: vec![InterfaceType::I32, InterfaceType::I32],
|
fields: vec1![InterfaceType::I32, InterfaceType::I32],
|
||||||
}),
|
}),
|
||||||
InterfaceType::F64,
|
InterfaceType::F64,
|
||||||
],
|
],
|
||||||
@ -542,7 +542,7 @@ mod tests {
|
|||||||
fn test_type_record() {
|
fn test_type_record() {
|
||||||
assert_to_bytes!(
|
assert_to_bytes!(
|
||||||
Type::Record(RecordType {
|
Type::Record(RecordType {
|
||||||
fields: vec![InterfaceType::I32, InterfaceType::I64],
|
fields: vec1![InterfaceType::I32, InterfaceType::I64],
|
||||||
}),
|
}),
|
||||||
&[
|
&[
|
||||||
0x01, // record type
|
0x01, // record type
|
||||||
|
@ -368,7 +368,7 @@ mod tests {
|
|||||||
(&InterfaceType::I32).to_string(),
|
(&InterfaceType::I32).to_string(),
|
||||||
(&InterfaceType::I64).to_string(),
|
(&InterfaceType::I64).to_string(),
|
||||||
(&InterfaceType::Record(RecordType {
|
(&InterfaceType::Record(RecordType {
|
||||||
fields: vec![InterfaceType::String],
|
fields: vec1![InterfaceType::String],
|
||||||
}))
|
}))
|
||||||
.to_string(),
|
.to_string(),
|
||||||
];
|
];
|
||||||
@ -397,18 +397,18 @@ mod tests {
|
|||||||
fn test_record_type() {
|
fn test_record_type() {
|
||||||
let inputs = vec![
|
let inputs = vec![
|
||||||
(&RecordType {
|
(&RecordType {
|
||||||
fields: vec![InterfaceType::String],
|
fields: vec1![InterfaceType::String],
|
||||||
})
|
})
|
||||||
.to_string(),
|
.to_string(),
|
||||||
(&RecordType {
|
(&RecordType {
|
||||||
fields: vec![InterfaceType::String, InterfaceType::I32],
|
fields: vec1![InterfaceType::String, InterfaceType::I32],
|
||||||
})
|
})
|
||||||
.to_string(),
|
.to_string(),
|
||||||
(&RecordType {
|
(&RecordType {
|
||||||
fields: vec![
|
fields: vec1![
|
||||||
InterfaceType::String,
|
InterfaceType::String,
|
||||||
InterfaceType::Record(RecordType {
|
InterfaceType::Record(RecordType {
|
||||||
fields: vec![InterfaceType::I32, InterfaceType::I32],
|
fields: vec1![InterfaceType::I32, InterfaceType::I32],
|
||||||
}),
|
}),
|
||||||
InterfaceType::F64,
|
InterfaceType::F64,
|
||||||
],
|
],
|
||||||
@ -539,7 +539,7 @@ mod tests {
|
|||||||
})
|
})
|
||||||
.to_string(),
|
.to_string(),
|
||||||
(&Type::Record(RecordType {
|
(&Type::Record(RecordType {
|
||||||
fields: vec![InterfaceType::String, InterfaceType::I32],
|
fields: vec1![InterfaceType::String, InterfaceType::I32],
|
||||||
}))
|
}))
|
||||||
.to_string(),
|
.to_string(),
|
||||||
];
|
];
|
||||||
|
@ -326,10 +326,10 @@ pub(crate) mod tests {
|
|||||||
},
|
},
|
||||||
memory: Memory::new(vec![Cell::new(0); 128]),
|
memory: Memory::new(vec![Cell::new(0); 128]),
|
||||||
wit_types: vec![ast::Type::Record(ast::RecordType {
|
wit_types: vec![ast::Type::Record(ast::RecordType {
|
||||||
fields: vec![
|
fields: vec1![
|
||||||
InterfaceType::I32,
|
InterfaceType::I32,
|
||||||
InterfaceType::Record(ast::RecordType {
|
InterfaceType::Record(ast::RecordType {
|
||||||
fields: vec![InterfaceType::String, InterfaceType::F32],
|
fields: vec1![InterfaceType::String, InterfaceType::F32],
|
||||||
}),
|
}),
|
||||||
InterfaceType::I64,
|
InterfaceType::I64,
|
||||||
],
|
],
|
||||||
|
@ -293,7 +293,7 @@ mod tests {
|
|||||||
let mut instance = Instance::new();
|
let mut instance = Instance::new();
|
||||||
instance.wit_types.push(
|
instance.wit_types.push(
|
||||||
Type::Record(RecordType {
|
Type::Record(RecordType {
|
||||||
fields: vec![InterfaceType::I32, InterfaceType::I32],
|
fields: vec1![InterfaceType::I32, InterfaceType::I32],
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
//! Defines WIT values and associated operations.
|
//! Defines WIT values and associated operations.
|
||||||
|
|
||||||
pub use crate::ast::{InterfaceType, RecordType};
|
pub use crate::ast::{InterfaceType, RecordType};
|
||||||
use crate::errors::WasmValueNativeCastError;
|
use crate::{errors::WasmValueNativeCastError, vec1::Vec1};
|
||||||
use std::{convert::TryFrom, slice::Iter};
|
use std::{convert::TryFrom, slice::Iter};
|
||||||
|
|
||||||
#[cfg(feature = "serde")]
|
#[cfg(feature = "serde")]
|
||||||
@ -85,7 +85,8 @@ impl Default for InterfaceValue {
|
|||||||
impl From<&Vec<InterfaceValue>> for RecordType {
|
impl From<&Vec<InterfaceValue>> for RecordType {
|
||||||
fn from(values: &Vec<InterfaceValue>) -> Self {
|
fn from(values: &Vec<InterfaceValue>) -> Self {
|
||||||
RecordType {
|
RecordType {
|
||||||
fields: values.iter().map(Into::into).collect(),
|
fields: Vec1::new(values.iter().map(Into::into).collect())
|
||||||
|
.expect("Record must have at least one field, zero given."),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -225,7 +226,7 @@ mod tests {
|
|||||||
InterfaceValue::S8(2)
|
InterfaceValue::S8(2)
|
||||||
])),
|
])),
|
||||||
InterfaceType::Record(RecordType {
|
InterfaceType::Record(RecordType {
|
||||||
fields: vec![InterfaceType::I32, InterfaceType::S8]
|
fields: vec1![InterfaceType::I32, InterfaceType::S8]
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -239,10 +240,10 @@ mod tests {
|
|||||||
InterfaceValue::S8(2)
|
InterfaceValue::S8(2)
|
||||||
])),
|
])),
|
||||||
InterfaceType::Record(RecordType {
|
InterfaceType::Record(RecordType {
|
||||||
fields: vec![
|
fields: vec1![
|
||||||
InterfaceType::I32,
|
InterfaceType::I32,
|
||||||
InterfaceType::Record(RecordType {
|
InterfaceType::Record(RecordType {
|
||||||
fields: vec![InterfaceType::String, InterfaceType::F64]
|
fields: vec1![InterfaceType::String, InterfaceType::F64]
|
||||||
}),
|
}),
|
||||||
InterfaceType::S8
|
InterfaceType::S8
|
||||||
]
|
]
|
||||||
|
@ -57,3 +57,4 @@ pub mod decoders;
|
|||||||
pub mod encoders;
|
pub mod encoders;
|
||||||
pub mod errors;
|
pub mod errors;
|
||||||
pub mod interpreter;
|
pub mod interpreter;
|
||||||
|
pub mod vec1;
|
||||||
|
@ -1,3 +1,28 @@
|
|||||||
|
/// This macro creates a `Vec1` by checking at compile-time that its
|
||||||
|
/// invariant holds.
|
||||||
|
#[allow(unused)]
|
||||||
|
macro_rules! vec1 {
|
||||||
|
($item:expr; 0) => {
|
||||||
|
compile_error!("Cannot create an empty `Vec1`, it violates its invariant.")
|
||||||
|
};
|
||||||
|
|
||||||
|
() => {
|
||||||
|
compile_error!("Cannot create an empty `Vec1`, it violates its invariant.")
|
||||||
|
};
|
||||||
|
|
||||||
|
($item:expr; $length:expr) => {
|
||||||
|
{
|
||||||
|
crate::vec1::Vec1::new(vec![$item; $length]).unwrap()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
($($item:expr),+ $(,)?) => {
|
||||||
|
{
|
||||||
|
crate::vec1::Vec1::new(vec![$($item),*]).unwrap()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/// This macro runs a parser, extracts the next input and the parser
|
/// This macro runs a parser, extracts the next input and the parser
|
||||||
/// output, and positions the next input on `$input`.
|
/// output, and positions the next input on `$input`.
|
||||||
macro_rules! consume {
|
macro_rules! consume {
|
||||||
|
62
lib/interface-types/src/vec1.rs
Normal file
62
lib/interface-types/src/vec1.rs
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
//! `Vec1<T>` represents a non-empty `Vec<T>`.
|
||||||
|
|
||||||
|
use std::{
|
||||||
|
error,
|
||||||
|
fmt::{self, Debug},
|
||||||
|
ops,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// `Vec1<T>` represents a non-empty `Vec<T>`. It derefs to `Vec<T>`
|
||||||
|
/// directly.
|
||||||
|
#[derive(Clone, PartialEq)]
|
||||||
|
pub struct Vec1<T>(Vec<T>)
|
||||||
|
where
|
||||||
|
T: Debug;
|
||||||
|
|
||||||
|
/// Represents the only error that can be emitted by `Vec1`, i.e. when
|
||||||
|
/// the number of items is zero.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct EmptyVec;
|
||||||
|
|
||||||
|
impl error::Error for EmptyVec {}
|
||||||
|
|
||||||
|
impl fmt::Display for EmptyVec {
|
||||||
|
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(formatter, "Vec1 must as least contain one item, zero given")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Vec1<T>
|
||||||
|
where
|
||||||
|
T: Debug,
|
||||||
|
{
|
||||||
|
/// Creates a new non-empty vector, based on an inner `Vec<T>`. If
|
||||||
|
/// the inner vector is empty, a `EmptyVec` error is returned.
|
||||||
|
pub fn new(items: Vec<T>) -> Result<Self, EmptyVec> {
|
||||||
|
if items.len() == 0 {
|
||||||
|
Err(EmptyVec)
|
||||||
|
} else {
|
||||||
|
Ok(Self(items))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> fmt::Debug for Vec1<T>
|
||||||
|
where
|
||||||
|
T: Debug,
|
||||||
|
{
|
||||||
|
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(formatter, "{:?}", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> ops::Deref for Vec1<T>
|
||||||
|
where
|
||||||
|
T: Debug,
|
||||||
|
{
|
||||||
|
type Target = Vec<T>;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,6 @@
|
|||||||
use wasmer_interface_types::{
|
use wasmer_interface_types::{
|
||||||
ast::*, decoders::binary::parse, encoders::binary::ToBytes, interpreter::Instruction,
|
ast::*, decoders::binary::parse, encoders::binary::ToBytes, interpreter::Instruction,
|
||||||
|
vec1::Vec1,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Tests an AST to binary, then binary to AST roundtrip.
|
/// Tests an AST to binary, then binary to AST roundtrip.
|
||||||
@ -16,7 +17,7 @@ fn test_binary_encoding_decoding_roundtrip() {
|
|||||||
outputs: vec![InterfaceType::S32],
|
outputs: vec![InterfaceType::S32],
|
||||||
},
|
},
|
||||||
Type::Record(RecordType {
|
Type::Record(RecordType {
|
||||||
fields: vec![InterfaceType::String, InterfaceType::I32],
|
fields: Vec1::new(vec![InterfaceType::String, InterfaceType::I32]).unwrap(),
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
imports: vec![Import {
|
imports: vec![Import {
|
||||||
|
Reference in New Issue
Block a user