diff --git a/Cargo.lock b/Cargo.lock index efaaf9f..9b35e64 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -47,6 +47,15 @@ dependencies = [ "static_assertions", ] +[[package]] +name = "log" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b" +dependencies = [ + "cfg-if", +] + [[package]] name = "memchr" version = "2.3.3" @@ -186,8 +195,9 @@ checksum = "078775d0255232fb988e6fccf26ddc9d1ac274299aaedcedce21c6f72cc533ce" [[package]] name = "wasmer-interface-types-fl" -version = "0.17.2" +version = "0.17.5" dependencies = [ + "log", "nom", "safe-transmute", "serde", diff --git a/Cargo.toml b/Cargo.toml index 31721b8..10109f1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-interface-types-fl" -version = "0.17.2" +version = "0.17.5" description = "WebAssembly Interface Types library for Wasmer" license = "MIT" authors = ["The Wasmer Engineering Team "] @@ -17,6 +17,7 @@ wast = "8.0" serde = { version = "1.0", features = ["derive"], optional = true } serde_json = "1.0" safe-transmute = "0.11.0" +log = "0.4.11" [features] default = ["serde"] diff --git a/src/ast.rs b/src/ast.rs index 6bc68cc..e51559e 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -17,6 +17,16 @@ pub enum TypeKind { Record, } +/// Represents the function argument type. +#[derive(PartialEq, Debug, Clone)] +pub struct FunctionArg { + /// A function argument name. + pub name: String, + + /// A function argument type. + pub ty: InterfaceType, +} + /// Represents a type. #[derive(PartialEq, Debug, Clone)] pub enum Type { @@ -26,15 +36,8 @@ pub enum Type { /// (@interface type (func (param i32 i32) (result string))) /// ``` Function { - /// Name of a function. - name: String, - - /// Types for the parameters (`(param …)`). - arg_types: Vec, - - /// Name of function argument types. - // TODO: introduce a struct combines name and type of a field - arg_names: Vec, + /// Types for the parameters (`(param (name i32))`). + arguments: Vec, /// Types for the results (`(result …)`). output_types: Vec, diff --git a/src/decoders/binary.rs b/src/decoders/binary.rs index e2a1e2d..a24ce74 100644 --- a/src/decoders/binary.rs +++ b/src/decoders/binary.rs @@ -1,6 +1,6 @@ //! Parse the WIT binary representation into an [AST](crate::ast). -use crate::{ast::*, interpreter::Instruction, types::*, vec1::Vec1}; +use crate::{ast::*, interpreter::Instruction, types::*}; use nom::{ error::{make_error, ErrorKind, ParseError}, Err, IResult, @@ -70,6 +70,32 @@ fn uleb<'input, E: ParseError<&'input [u8]>>(input: &'input [u8]) -> IResult<&'i )) } +fn record_field<'input, E: ParseError<&'input [u8]>>( + mut input: &'input [u8], +) -> IResult<&'input [u8], RecordFieldType, E> { + if input.is_empty() { + return Err(Err::Error(make_error(input, ErrorKind::Eof))); + } + + consume!((input, name) = owned_string(input)?); + consume!((input, ty) = ty(input)?); + + Ok((input, RecordFieldType { name, ty })) +} + +fn function_arg<'input, E: ParseError<&'input [u8]>>( + mut input: &'input [u8], +) -> IResult<&'input [u8], FunctionArg, E> { + if input.is_empty() { + return Err(Err::Error(make_error(input, ErrorKind::Eof))); + } + + consume!((input, name) = owned_string(input)?); + consume!((input, ty) = ty(input)?); + + Ok((input, FunctionArg { name, ty })) +} + /// Parse an interface type. fn ty<'input, E: ParseError<&'input [u8]>>( mut input: &'input [u8], @@ -97,9 +123,9 @@ fn ty<'input, E: ParseError<&'input [u8]>>( 0x0c => InterfaceType::I32, 0x0d => InterfaceType::I64, 0x0e => { - consume!((input, record_type) = record_type(input)?); + consume!((input, record_id) = uleb(input)?); - InterfaceType::Record(record_type) + InterfaceType::Record(record_id) } _ => return Err(Err::Error(make_error(input, ErrorKind::ParseTo))), }; @@ -111,11 +137,15 @@ fn ty<'input, E: ParseError<&'input [u8]>>( fn record_type<'input, E: ParseError<&'input [u8]>>( input: &'input [u8], ) -> IResult<&'input [u8], RecordType, E> { - let (output, fields) = list(input, ty)?; + use crate::vec1::Vec1; + + let (output, name) = owned_string(input)?; + let (output, fields) = list(output, record_field)?; Ok(( output, RecordType { + name, fields: Vec1::new(fields).expect("Record must have at least one field, zero given."), }, )) @@ -262,34 +292,35 @@ fn instruction<'input, E: ParseError<&'input [u8]>>( 0x38 => (input, Instruction::ByteArrayLowerMemory), 0x39 => (input, Instruction::ByteArraySize), - 0x25 => { - consume!((input, argument_0) = uleb(input)?); + /* + 0x25 => { + consume!((input, argument_0) = uleb(input)?); - ( - input, - Instruction::RecordLift { - type_index: argument_0 as u32, - }, - ) - } - 0x26 => { - consume!((input, argument_0) = uleb(input)?); - - ( - input, - Instruction::RecordLower { - type_index: argument_0 as u32, - }, - ) - } + ( + input, + Instruction::RecordLift { + type_index: argument_0 as u32, + }, + ) + } + 0x26 => { + consume!((input, argument_0) = uleb(input)?); + ( + input, + Instruction::RecordLower { + type_index: argument_0 as u32, + }, + ) + } + */ 0x3A => { consume!((input, argument_0) = uleb(input)?); ( input, Instruction::RecordLiftMemory { - type_index: argument_0 as u32, + record_type_id: argument_0 as u32, }, ) } @@ -299,7 +330,7 @@ fn instruction<'input, E: ParseError<&'input [u8]>>( ( input, Instruction::RecordLowerMemory { - type_index: argument_0 as u32, + record_type_id: argument_0 as u32, }, ) } @@ -328,15 +359,11 @@ fn types<'input, E: ParseError<&'input [u8]>>( match type_kind { TypeKind::Function => { - consume!((input, name) = string(input)?); - consume!((input, arg_types) = list(input, ty)?); - consume!((input, arg_names) = list(input, owned_string)?); + consume!((input, arguments) = list(input, function_arg)?); consume!((input, output_types) = list(input, ty)?); types.push(Type::Function { - name: String::from(name), - arg_types, - arg_names, + arguments, output_types, }); } @@ -861,8 +888,11 @@ mod tests { Instruction::StringLiftMemory, Instruction::StringLowerMemory, Instruction::StringSize, + /* Instruction::RecordLift { type_index: 1 }, Instruction::RecordLower { type_index: 1 }, + + */ ], )); diff --git a/src/decoders/wat.rs b/src/decoders/wat.rs index f6aa2c9..876b222 100644 --- a/src/decoders/wat.rs +++ b/src/decoders/wat.rs @@ -17,6 +17,10 @@ mod keyword { custom_keyword!(record); custom_keyword!(field); + // Special symbols + custom_keyword!(comma = ","); + custom_keyword!(colon = ":"); + // New types. custom_keyword!(s8); custom_keyword!(s16); @@ -154,22 +158,31 @@ impl Parse<'_> for RecordType { fn parse(parser: Parser<'_>) -> Result { parser.parse::()?; + parser.parse::()?; + let record_name = parser.parse()?; + let mut fields = vec![]; while !parser.is_empty() { fields.push(parser.parens(|parser| { - parser.parse::()?; + parser.parse::()?; + let name = parser.parse()?; - parser.parse() + parser.parse::()?; + let ty = parser.parse()?; + + Ok(RecordFieldType { name, ty }) })?); } Ok(RecordType { + name: record_name, fields: Vec1::new(fields).expect("Record must have at least one field, zero given."), }) } } +#[allow(clippy::suspicious_else_formatting)] impl<'a> Parse<'a> for Instruction { #[allow(clippy::cognitive_complexity)] fn parse(parser: Parser<'a>) -> Result { @@ -339,7 +352,9 @@ impl<'a> Parse<'a> for Instruction { parser.parse::()?; Ok(Instruction::ByteArraySize) - } else if lookahead.peek::() { + } + /* + else if lookahead.peek::() { parser.parse::()?; Ok(Instruction::RecordLift { @@ -351,17 +366,19 @@ impl<'a> Parse<'a> for Instruction { Ok(Instruction::RecordLower { type_index: parser.parse()?, }) - } else if lookahead.peek::() { + } + */ + else if lookahead.peek::() { parser.parse::()?; Ok(Instruction::RecordLiftMemory { - type_index: parser.parse()?, + record_type_id: parser.parse()?, }) } else if lookahead.peek::() { parser.parse::()?; Ok(Instruction::RecordLowerMemory { - type_index: parser.parse()?, + record_type_id: parser.parse()?, }) } else if lookahead.peek::() { parser.parse::()?; @@ -403,7 +420,7 @@ impl Parse<'_> for AtInterface { #[derive(PartialEq, Debug)] enum FunctionType { - Header(String, Vec, Vec), + Header(Vec), Output(Vec), } @@ -414,17 +431,34 @@ impl Parse<'_> for FunctionType { if lookahead.peek::() { parser.parse::()?; - let func_name = parser.parse()?; - let mut names = vec![]; - let mut types = vec![]; + let mut arguments = vec![]; while !parser.is_empty() { - names.push(parser.parse()?); - types.push(parser.parse()?); + let arg_name = parser.step(|cursor| { + cursor + .id() + .ok_or_else(|| cursor.error("expecting argument identifier")) + })?; + + if !arg_name.ends_with(':') { + parser.step(|cursor| { + if let Some((":", rest)) = cursor.reserved() { + return Ok(("", rest)); + } + Err(cursor.error("expected : between an argument and a type")) + })?; + } + + let arg_type: InterfaceType = parser.parse()?; + + arguments.push(FunctionArg { + name: arg_name.trim_end_matches(':').to_string(), + ty: arg_type, + }); } - Ok(FunctionType::Header(func_name, names, types)) + Ok(FunctionType::Header(arguments)) } else if lookahead.peek::() { parser.parse::()?; @@ -491,38 +525,22 @@ impl<'a> Parse<'a> for Type { if lookahead.peek::() { parser.parse::()?; - let mut arg_types = vec![]; - let mut arg_names = vec![]; + let mut arguments = vec![]; let mut output_types = vec![]; - let mut name: Option = None; while !parser.is_empty() { let function_type = parser.parse::()?; match function_type { - FunctionType::Header(func_name, mut names, mut types) => { - name = Some(func_name); - arg_names.append(&mut names); - arg_types.append(&mut types); - }, + FunctionType::Header(mut func_arguments) => { + arguments.append(&mut func_arguments); + } FunctionType::Output(mut outputs) => output_types.append(&mut outputs), } } - if name.is_none() { - return Err(parser.error("Malformed wast: function doesn't contain name")); - } - - if arg_types.len() != arg_names.len() { - return Err(parser.error("Malformed wast: function argument types count should be equal to argument names count")); - } - - // It's has been already checked for None. - let name = name.unwrap(); Ok(Type::Function { - name, - arg_types, - arg_names, + arguments, output_types, }) } else if lookahead.peek::() { @@ -878,8 +896,10 @@ mod tests { Instruction::StringLiftMemory, Instruction::StringLowerMemory, Instruction::StringSize, + /* Instruction::RecordLift { type_index: 42 }, Instruction::RecordLower { type_index: 42 }, + */ ]; assert_eq!(inputs.len(), outputs.len()); diff --git a/src/encoders/binary.rs b/src/encoders/binary.rs index 2b3a90f..65b6a54 100644 --- a/src/encoders/binary.rs +++ b/src/encoders/binary.rs @@ -127,20 +127,32 @@ where InterfaceType::Anyref => 0x0b_u8.to_bytes(writer), InterfaceType::I32 => 0x0c_u8.to_bytes(writer), InterfaceType::I64 => 0x0d_u8.to_bytes(writer), - InterfaceType::Record(record_type) => { + InterfaceType::Record(record_id) => { 0x0e_u8.to_bytes(writer)?; - record_type.to_bytes(writer) + record_id.to_bytes(writer) } } } } +/// Encode a `RecordType` into bytes. +impl ToBytes for RecordFieldType +where + W: Write, +{ + fn to_bytes(&self, writer: &mut W) -> io::Result<()> { + self.name.as_str().to_bytes(writer)?; + self.ty.to_bytes(writer) + } +} + /// Encode a `RecordType` into bytes. impl ToBytes for RecordType where W: Write, { fn to_bytes(&self, writer: &mut W) -> io::Result<()> { + self.name.as_str().to_bytes(writer)?; self.fields.to_bytes(writer) } } @@ -174,6 +186,16 @@ where } } +impl ToBytes for FunctionArg +where + W: Write, +{ + fn to_bytes(&self, writer: &mut W) -> io::Result<()> { + self.name.to_bytes(writer)?; + self.ty.to_bytes(writer) + } +} + /// Encode a `Type` into bytes. /// /// Decoder is in `decoders::binary::types`. @@ -184,15 +206,11 @@ where fn to_bytes(&self, writer: &mut W) -> io::Result<()> { match self { Type::Function { - name, - arg_types, - arg_names, + arguments, output_types, } => { TypeKind::Function.to_bytes(writer)?; - name.to_bytes(writer)?; - arg_types.to_bytes(writer)?; - arg_names.to_bytes(writer)?; + arguments.to_bytes(writer)?; output_types.to_bytes(writer)?; } @@ -363,7 +381,7 @@ where Instruction::ByteArrayLiftMemory => 0x37_u8.to_bytes(writer)?, Instruction::ByteArrayLowerMemory => 0x38_u8.to_bytes(writer)?, Instruction::ByteArraySize => 0x39_u8.to_bytes(writer)?, - + /* Instruction::RecordLift { type_index } => { 0x25_u8.to_bytes(writer)?; (*type_index as u64).to_bytes(writer)? @@ -372,11 +390,16 @@ where 0x26_u8.to_bytes(writer)?; (*type_index as u64).to_bytes(writer)? } - Instruction::RecordLiftMemory { type_index } => { + */ + Instruction::RecordLiftMemory { + record_type_id: type_index, + } => { 0x3A_u8.to_bytes(writer)?; (*type_index as u64).to_bytes(writer)? } - Instruction::RecordLowerMemory { type_index } => { + Instruction::RecordLowerMemory { + record_type_id: type_index, + } => { 0x3B_u8.to_bytes(writer)?; (*type_index as u64).to_bytes(writer)? } diff --git a/src/encoders/wat.rs b/src/encoders/wat.rs index 3b06d27..9c3d5a4 100644 --- a/src/encoders/wat.rs +++ b/src/encoders/wat.rs @@ -85,13 +85,18 @@ impl ToString for &InterfaceType { impl ToString for &RecordType { fn to_string(&self) -> String { format!( - "record{fields}", + "record {} {{\n{fields}}}", + self.name, fields = self .fields .iter() - .fold(String::new(), |mut accumulator, interface_type| { + .fold(String::new(), |mut accumulator, field_type| { accumulator.push(' '); - accumulator.push_str(&format!("(field {})", &interface_type.to_string())); + accumulator.push_str(&format!( + "{}: {}\n", + field_type.name, + (&field_type.ty).to_string() + )); accumulator }), ) @@ -142,14 +147,16 @@ impl ToString for &Instruction { Instruction::ByteArrayLiftMemory => "byte_array.lift_memory".into(), Instruction::ByteArrayLowerMemory => "byte_array.lower_memory".into(), Instruction::ByteArraySize => "byte_array.size".into(), + /* Instruction::RecordLift { type_index } => format!("record.lift {}", type_index), Instruction::RecordLower { type_index } => format!("record.lower {}", type_index), - Instruction::RecordLiftMemory { type_index } => { - format!("record.lift_memory {}", type_index) - } - Instruction::RecordLowerMemory { type_index } => { - format!("record.lower_memory {}", type_index) - } + */ + Instruction::RecordLiftMemory { + record_type_id: type_index, + } => format!("record.lift_memory {}", type_index), + Instruction::RecordLowerMemory { + record_type_id: type_index, + } => format!("record.lower_memory {}", type_index), Instruction::Dup => "dup".into(), Instruction::Swap2 => "swap2".into(), } @@ -158,17 +165,17 @@ impl ToString for &Instruction { /// Encode a list of `InterfaceType` representing inputs into a /// string. -fn encode_arguments(arg_types: &[InterfaceType], arg_names: &[String]) -> String { +fn encode_function_arguments(arguments: &[FunctionArg]) -> String { // here we know that arg_names and arg_types have the same length - if arg_names.is_empty() { + if arguments.is_empty() { String::from("") } else { format!( "\n (param{})", - arg_names.iter().zip(arg_types.iter()).fold( + arguments.iter().fold( String::new(), - |mut accumulator, (name, ty)| { - accumulator.push(' '); + |mut accumulator, FunctionArg { name, ty }| { + accumulator.push_str(" $"); accumulator.push_str(name); accumulator.push_str(": "); accumulator.push_str(&ty.to_string()); @@ -203,14 +210,11 @@ impl<'input> ToString for &Type { fn to_string(&self) -> String { match self { Type::Function { - name, - arg_types, - arg_names, + arguments, output_types, } => format!( - r#"(@interface type (func {name} {args}{output_types}))"#, - name = name, - args = encode_arguments(arg_types, arg_names), + r#"(@interface type (func {args} {output_types}))"#, + args = encode_function_arguments(arguments), output_types = output_types_to_result(&output_types), ), @@ -279,14 +283,16 @@ impl<'input> ToString for &Interfaces<'input> { fn to_string(&self) -> String { let mut output = String::new(); - let types = self - .types - .iter() - .fold(String::new(), |mut accumulator, ty| { - accumulator.push('\n'); - accumulator.push_str(&ty.to_string()); - accumulator - }); + let types = + self.types + .iter() + .enumerate() + .fold(String::new(), |mut accumulator, (id, ty)| { + accumulator.push('\n'); + accumulator.push_str(&ty.to_string()); + accumulator.push_str(&format!(" ;; {}", id)); + accumulator + }); let imports = self .imports @@ -485,8 +491,10 @@ mod tests { (&Instruction::StringLiftMemory).to_string(), (&Instruction::StringLowerMemory).to_string(), (&Instruction::StringSize).to_string(), + /* (&Instruction::RecordLift { type_index: 42 }).to_string(), (&Instruction::RecordLower { type_index: 42 }).to_string(), + */ ]; let outputs = vec![ "arg.get 7", diff --git a/src/errors.rs b/src/errors.rs index decf278..efa5d03 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -1,6 +1,7 @@ //! The error module contains all the data structures that represent //! an error. +use crate::values::InterfaceValue; use crate::{ast::TypeKind, interpreter::Instruction, types::InterfaceType}; use std::{ error::Error, @@ -21,7 +22,7 @@ pub type InterpreterResult = Result; #[derive(Debug)] pub struct WasmValueNativeCastError { /// The initial type. - pub from: InterfaceType, + pub from: InterfaceValue, /// The targeted type. /// @@ -98,7 +99,7 @@ pub enum InstructionErrorKind { expected_type: InterfaceType, /// The received type. - received_type: InterfaceType, + received_value: InterfaceValue, }, /// Need to read some values from the stack, but it doesn't @@ -163,6 +164,15 @@ pub enum InstructionErrorKind { type_index: u32, }, + /// The searched by name type doesn't exist. + RecordTypeByNameIsMissing { + /// The record type name. + record_type_id: u64, + }, + + /// Corrupted record's been popped from the stack. + CorruptedRecord(String), + /// Read a type that has an unexpected type. InvalidTypeKind { /// The expected kind. @@ -199,11 +209,11 @@ impl Display for InstructionErrorKind { Self::InvalidValueOnTheStack { expected_type, - received_type, + received_value, } => write!( formatter, - "read a value of type `{:?}` from the stack, but the type `{:?}` was expected", - received_type, expected_type, + "read a value `{:?}` from the stack, that can't be converted to `{:?}`", + received_value, expected_type, ), Self::StackIsTooSmall { needed } => write!( @@ -261,6 +271,19 @@ impl Display for InstructionErrorKind { "read a type of kind `{:?}`, but the kind `{:?}` was expected", received_kind, expected_kind ), + + Self::RecordTypeByNameIsMissing { record_type_id: type_name } => write!( + formatter, + "type with `{}` is missing in a Wasm binary", + type_name + ), + + Self::CorruptedRecord(err) => write!( + formatter, + "{}", + err + ), + Self::SerdeError(err) => write!( formatter, "serde error: {}", err, diff --git a/src/interpreter/instructions/argument_get.rs b/src/interpreter/instructions/argument_get.rs index 3b95405..87cbc52 100644 --- a/src/interpreter/instructions/argument_get.rs +++ b/src/interpreter/instructions/argument_get.rs @@ -15,6 +15,8 @@ executable_instruction!( )); } + log::trace!("arg.get: pushing {:?} on the stack", invocation_inputs[index as usize]); + runtime.stack.push(invocation_inputs[index as usize].clone()); Ok(()) diff --git a/src/interpreter/instructions/byte_arrays.rs b/src/interpreter/instructions/byte_arrays.rs index fcc908b..c4f4ede 100644 --- a/src/interpreter/instructions/byte_arrays.rs +++ b/src/interpreter/instructions/byte_arrays.rs @@ -59,6 +59,8 @@ executable_instruction!( .map(Cell::get) .collect(); + log::trace!("bytearray.lift_memory: pushing {:?} on the stack", data); + runtime.stack.push(InterfaceValue::ByteArray(data)); Ok(()) @@ -104,6 +106,8 @@ executable_instruction!( memory_view[byte_array_pointer as usize + nth].set(*byte); } + log::trace!("bytearray.lower_memory: pushing {}, {} on the stack", byte_array_pointer, byte_array_length); + runtime.stack.push(InterfaceValue::I32(byte_array_pointer as i32)); runtime.stack.push(InterfaceValue::I32(byte_array_length)); @@ -127,7 +131,7 @@ executable_instruction!( instruction, InstructionErrorKind::InvalidValueOnTheStack { expected_type: InterfaceType::ByteArray, - received_type: (&value).into(), + received_value: value, }, )), diff --git a/src/interpreter/instructions/call_core.rs b/src/interpreter/instructions/call_core.rs index c4c1632..0f54912 100644 --- a/src/interpreter/instructions/call_core.rs +++ b/src/interpreter/instructions/call_core.rs @@ -2,13 +2,12 @@ use crate::{ errors::{InstructionError, InstructionErrorKind}, interpreter::wasm::structures::{FunctionIndex, TypedIndex}, interpreter::Instruction, - types::InterfaceType, }; executable_instruction!( call_core(function_index: u32, instruction: Instruction) -> _ { move |runtime| -> _ { - let instance = &mut runtime.wasm_instance; + let instance = &runtime.wasm_instance; let index = FunctionIndex::new(function_index as usize); let local_or_import = instance.local_or_import(index).ok_or_else(|| { @@ -29,21 +28,10 @@ executable_instruction!( }, ) })?; - let input_types = inputs - .iter() - .map(Into::into) - .collect::>(); - if input_types != local_or_import.inputs() { - return Err(InstructionError::new( - instruction, - InstructionErrorKind::LocalOrImportSignatureMismatch { - function_index, - expected: (local_or_import.inputs().to_vec(), vec![]), - received: (input_types, vec![]), - }, - )); - } + super::check_function_signature(&**instance, local_or_import, &inputs, instruction)?; + + log::trace!("call-core: calling {} with arguments: {:?}", function_index, inputs); let outputs = local_or_import.call(&inputs).map_err(|_| { InstructionError::new( @@ -54,7 +42,7 @@ executable_instruction!( ) })?; - + log::trace!("call-core: call to {} succeeded with result {:?}", function_index, outputs); for output in outputs.into_iter() { runtime.stack.push(output) diff --git a/src/interpreter/instructions/dup.rs b/src/interpreter/instructions/dup.rs index f6c6c42..4d455c3 100644 --- a/src/interpreter/instructions/dup.rs +++ b/src/interpreter/instructions/dup.rs @@ -14,6 +14,7 @@ executable_instruction!( })?; let value = value.clone(); + log::trace!("dup: duplication {:?} value on the stack", value); runtime.stack.push(value); Ok(()) diff --git a/src/interpreter/instructions/mod.rs b/src/interpreter/instructions/mod.rs index 63b98bd..518850b 100644 --- a/src/interpreter/instructions/mod.rs +++ b/src/interpreter/instructions/mod.rs @@ -7,6 +7,9 @@ mod records; mod strings; mod swap2; +use crate::interpreter::wasm; +use crate::types::InterfaceType; +use crate::vec1::Vec1; use crate::{ errors::{InstructionError, InstructionErrorKind, InstructionResult, WasmValueNativeCastError}, values::{InterfaceValue, NativeType}, @@ -153,6 +156,7 @@ pub enum Instruction { /// The `string.size` instruction. ByteArraySize, + /* /// The `record.lift` instruction. RecordLift { /// The type index of the record. @@ -165,16 +169,17 @@ pub enum Instruction { type_index: u32, }, + */ /// The `record.lift_memory` instruction. RecordLiftMemory { /// The type index of the record. - type_index: u32, + record_type_id: u32, }, /// The `record.lower_memory` instruction. RecordLowerMemory { /// The type index of the record. - type_index: u32, + record_type_id: u32, }, /// The `dup` instructions. @@ -197,6 +202,143 @@ where .map_err(|error| InstructionError::new(instruction, InstructionErrorKind::ToNative(error))) } +pub(crate) fn check_function_signature< + 'instance, + Instance, + Export, + LocalImport, + Memory, + MemoryView, +>( + instance: &'instance Instance, + local_import: &LocalImport, + values: &[InterfaceValue], + instruction: Instruction, +) -> Result<(), InstructionError> +where + Export: wasm::structures::Export + 'instance, + LocalImport: wasm::structures::LocalImport + 'instance, + Memory: wasm::structures::Memory + 'instance, + MemoryView: wasm::structures::MemoryView, + Instance: wasm::structures::Instance, +{ + let func_inputs = local_import.arguments(); + + for (func_input_arg, value) in func_inputs.iter().zip(values.iter()) { + is_value_compatible_to_type(instance, &func_input_arg.ty, value, instruction)?; + } + + Ok(()) +} + +/// Check whether the provided value could be a value of the provided type. +pub(crate) fn is_value_compatible_to_type< + 'instance, + Instance, + Export, + LocalImport, + Memory, + MemoryView, +>( + instance: &'instance Instance, + interface_type: &InterfaceType, + interface_value: &InterfaceValue, + instruction: Instruction, +) -> Result<(), InstructionError> +where + Export: wasm::structures::Export + 'instance, + LocalImport: wasm::structures::LocalImport + 'instance, + Memory: wasm::structures::Memory + 'instance, + MemoryView: wasm::structures::MemoryView, + Instance: wasm::structures::Instance, +{ + match (&interface_type, interface_value) { + (InterfaceType::S8, InterfaceValue::S8(_)) => Ok(()), + (InterfaceType::S16, InterfaceValue::S16(_)) => Ok(()), + (InterfaceType::S32, InterfaceValue::S32(_)) => Ok(()), + (InterfaceType::S64, InterfaceValue::S64(_)) => Ok(()), + (InterfaceType::U8, InterfaceValue::U8(_)) => Ok(()), + (InterfaceType::U16, InterfaceValue::U16(_)) => Ok(()), + (InterfaceType::U32, InterfaceValue::U32(_)) => Ok(()), + (InterfaceType::U64, InterfaceValue::U64(_)) => Ok(()), + (InterfaceType::I32, InterfaceValue::I32(_)) => Ok(()), + (InterfaceType::I64, InterfaceValue::I64(_)) => Ok(()), + (InterfaceType::F32, InterfaceValue::F32(_)) => Ok(()), + (InterfaceType::F64, InterfaceValue::F64(_)) => Ok(()), + (InterfaceType::String, InterfaceValue::String(_)) => Ok(()), + (InterfaceType::ByteArray, InterfaceValue::ByteArray(_)) => Ok(()), + (InterfaceType::Record(ref record_type_id), InterfaceValue::Record(record_fields)) => { + is_record_fields_compatible_to_type( + instance, + *record_type_id, + record_fields, + instruction, + )?; + + Ok(()) + } + _ => Err(InstructionError::new( + instruction, + InstructionErrorKind::InvalidValueOnTheStack { + expected_type: interface_type.clone(), + received_value: interface_value.clone(), + }, + )), + } +} + +pub(crate) fn is_record_fields_compatible_to_type< + 'instance, + Instance, + Export, + LocalImport, + Memory, + MemoryView, +>( + instance: &'instance Instance, + record_type_id: u64, + record_fields: &[InterfaceValue], + instruction: Instruction, +) -> Result<(), InstructionError> +where + Export: wasm::structures::Export + 'instance, + LocalImport: wasm::structures::LocalImport + 'instance, + Memory: wasm::structures::Memory + 'instance, + MemoryView: wasm::structures::MemoryView, + Instance: wasm::structures::Instance, +{ + let record_type = instance.wit_record_by_id(record_type_id).ok_or_else(|| { + InstructionError::new( + instruction, + InstructionErrorKind::RecordTypeByNameIsMissing { record_type_id }, + ) + })?; + + if record_fields.len() != record_type.fields.len() { + return Err(InstructionError::new( + instruction, + InstructionErrorKind::InvalidValueOnTheStack { + expected_type: InterfaceType::Record(record_type_id), + // unwrap is safe here - len's been already checked + received_value: InterfaceValue::Record(Vec1::new(record_fields.to_vec()).unwrap()), + }, + )); + } + + for (record_type_field, record_value_field) in + record_type.fields.iter().zip(record_fields.iter()) + { + is_value_compatible_to_type( + instance, + &record_type_field.ty, + record_value_field, + instruction, + )?; + } + + Ok(()) +} + #[cfg(test)] pub(crate) mod tests { use crate::{ast::*, interpreter::wasm, types::*, values::*}; @@ -217,7 +359,7 @@ pub(crate) mod tests { self.outputs.len() } - fn inputs(&self) -> &[InterfaceType] { + fn arguments(&self) -> &[InterfaceType] { &self.inputs } @@ -245,7 +387,7 @@ pub(crate) mod tests { self.outputs.len() } - fn inputs(&self) -> &[InterfaceType] { + fn arguments(&self) -> &[InterfaceType] { &self.inputs } @@ -353,12 +495,32 @@ pub(crate) mod tests { }, memory: Memory::new(vec![Cell::new(0); 128]), wit_types: vec![Type::Record(RecordType { + name: String::from("RecordType0"), fields: vec1![ - InterfaceType::I32, - InterfaceType::Record(RecordType { - fields: vec1![InterfaceType::String, InterfaceType::F32], - }), - InterfaceType::I64, + RecordFieldType { + name: String::from("field_0"), + ty: InterfaceType::I32, + }, + RecordFieldType { + name: String::from("field_1"), + ty: InterfaceType::Record(RecordType { + name: String::from("RecordType1"), + fields: vec1![ + RecordFieldType { + name: String::from("field_0"), + ty: InterfaceType::String, + }, + RecordFieldType { + name: String::from("field1"), + ty: InterfaceType::F32 + } + ], + }), + }, + RecordFieldType { + name: String::from("field_2"), + ty: InterfaceType::I64, + } ], })], } @@ -381,8 +543,12 @@ pub(crate) mod tests { Some(&self.memory) } - fn wit_type(&self, index: u32) -> Option<&Type> { + fn wit_type_by_id(&self, index: u32) -> Option<&Type> { self.wit_types.get(index as usize) } + + fn wit_record_by_id(&self, index: u64) -> Option<&RecordType> { + self.wit_types.get(index as _) + } } } diff --git a/src/interpreter/instructions/numbers.rs b/src/interpreter/instructions/numbers.rs index bedae26..53e4ad3 100644 --- a/src/interpreter/instructions/numbers.rs +++ b/src/interpreter/instructions/numbers.rs @@ -15,25 +15,30 @@ macro_rules! lowering_lifting { Some(InterfaceValue::$from_variant(value)) => { runtime .stack - .push(InterfaceValue::$to_variant(value.try_into().map_err( - |_| { - InstructionError::new( - instruction, - InstructionErrorKind::LoweringLifting { - from: InterfaceType::$from_variant, - to: InterfaceType::$to_variant - }, - ) - }, - )?)) - } + .push({ + let converted_value = InterfaceValue::$to_variant(value.try_into().map_err( + |_| { + InstructionError::new( + instruction, + InstructionErrorKind::LoweringLifting { + from: InterfaceType::$from_variant, + to: InterfaceType::$to_variant + }, + ) + }, + )?); + log::trace!("{}: converting {:?} to {:?}", $instruction_name, value, converted_value); + + converted_value + }) + } Some(wrong_value) => { return Err(InstructionError::new( instruction, InstructionErrorKind::InvalidValueOnTheStack { expected_type: InterfaceType::$from_variant, - received_type: (&wrong_value).into(), + received_value: wrong_value, } )) }, diff --git a/src/interpreter/instructions/records.rs b/src/interpreter/instructions/records.rs index f125f4c..8f6a301 100644 --- a/src/interpreter/instructions/records.rs +++ b/src/interpreter/instructions/records.rs @@ -3,24 +3,19 @@ mod utils; use utils::read_from_instance_mem; use utils::write_to_instance_mem; -// use crate::interpreter::wasm; - -use crate::interpreter::instructions::to_native; +use crate::interpreter::instructions::{is_record_fields_compatible_to_type, to_native}; use crate::{ - ast::{Type, TypeKind}, errors::{InstructionError, InstructionErrorKind}, - interpreter::{ - stack::{Stack, Stackable}, - Instruction, - }, + interpreter::Instruction, types::{InterfaceType, RecordType}, - values::{FlattenInterfaceValueIterator, InterfaceValue}, + values::InterfaceValue, vec1::Vec1, }; use std::collections::VecDeque; use std::convert::TryInto; +/* /// Build an `InterfaceValue::Record` based on values on the stack. /// /// To fill a record, every field `field_1` to `field_n` must get its @@ -105,10 +100,11 @@ where } }) } + */ fn record_lift_memory_<'instance, Instance, Export, LocalImport, Memory, MemoryView>( - instance: &'instance mut Instance, - record_type: RecordType, + instance: &'instance Instance, + record_type: &RecordType, offset: usize, instruction: Instruction, ) -> Result @@ -123,8 +119,8 @@ where fn record_size(record_type: &RecordType) -> usize { let mut record_size = 0; - for ty in record_type.fields.iter() { - let params_count = match ty { + for field_type in record_type.fields.iter() { + let params_count = match field_type.ty { InterfaceType::String | InterfaceType::ByteArray => 2, _ => 1, }; @@ -137,16 +133,16 @@ where let length = record_type.fields.len(); let mut values = VecDeque::with_capacity(length); - let size = record_size(&record_type); + let size = record_size(record_type); let data = read_from_instance_mem(instance, instruction, offset, size)?; // TODO: add error handling let data = safe_transmute::transmute_many::(&data).unwrap(); let mut field_id = 0; - for field in record_type.fields.into_vec() { + for field in (*record_type.fields).iter() { let value = data[field_id]; - match field { + match &field.ty { InterfaceType::S8 => { values.push_back(InterfaceValue::S8(value as _)); } @@ -199,7 +195,7 @@ where let string = String::from_utf8(string_mem).unwrap(); values.push_back(InterfaceValue::String(string)); } else { - values.push_back(InterfaceValue::String("".to_string())); + values.push_back(InterfaceValue::String(String::new())); } } InterfaceType::ByteArray => { @@ -220,9 +216,18 @@ where values.push_back(InterfaceValue::ByteArray(vec![])); } } - InterfaceType::Record(record_type) => { + InterfaceType::Record(record_type_id) => { let offset = value; + let record_type = instance.wit_record_by_id(*record_type_id).ok_or_else(|| { + InstructionError::new( + instruction, + InstructionErrorKind::RecordTypeByNameIsMissing { + record_type_id: *record_type_id, + }, + ) + })?; + values.push_back(record_lift_memory_( instance, record_type, @@ -243,7 +248,7 @@ where } pub(crate) fn record_lift_memory( - type_index: u32, + record_type_id: u64, instruction: Instruction, ) -> crate::interpreter::ExecutableInstruction where @@ -271,26 +276,17 @@ where .map_err(|k| InstructionError::new(instruction, k))?; // TODO: size = 0 - let instance = &mut runtime.wasm_instance; - let record_type = match instance.wit_type(type_index).ok_or_else(|| { + let instance = &runtime.wasm_instance; + let record_type = instance.wit_record_by_id(record_type_id).ok_or_else(|| { InstructionError::new( instruction, - InstructionErrorKind::TypeIsMissing { type_index }, + InstructionErrorKind::RecordTypeByNameIsMissing { record_type_id }, ) - })? { - Type::Record(record_type) => record_type.clone(), - Type::Function { .. } => { - return Err(InstructionError::new( - instruction, - InstructionErrorKind::InvalidTypeKind { - expected_kind: TypeKind::Record, - received_kind: TypeKind::Function, - }, - )) - } - }; + })?; - let record = record_lift_memory_(*instance, record_type, offset, instruction)?; + let record = record_lift_memory_(&**instance, record_type, offset, instruction)?; + + log::trace!("record.lift_memory: pushing {:?} on the stack", record); runtime.stack.push(record); Ok(()) @@ -349,9 +345,10 @@ where result.push(value.len() as _); } - InterfaceValue::Record(record) => { - let record_ptr = record_lower_memory_(instance, instruction, record)?; + InterfaceValue::Record(values) => { + let record_ptr = record_lower_memory_(instance, instruction, values)?; + log::trace!("record.lower_memory: pushing {:?} on the stack", record_ptr); result.push(record_ptr as _); } } @@ -364,7 +361,7 @@ where } pub(crate) fn record_lower_memory( - type_index: u32, + record_type_id: u64, instruction: Instruction, ) -> crate::interpreter::ExecutableInstruction where @@ -380,41 +377,16 @@ where Box::new({ move |runtime| -> _ { let instance = &mut runtime.wasm_instance; - let record_type = match instance.wit_type(type_index).ok_or_else(|| { - InstructionError::new( - instruction, - InstructionErrorKind::TypeIsMissing { type_index }, - ) - })? { - Type::Record(record_type) => record_type, - Type::Function { .. } => { - return Err(InstructionError::new( - instruction, - InstructionErrorKind::InvalidTypeKind { - expected_kind: TypeKind::Record, - received_kind: TypeKind::Function, - }, - )) - } - }; - match runtime.stack.pop1() { - Some(InterfaceValue::Record(record_values)) - if record_type == &(&*record_values).into() => - { - /* - let value: Vec = crate::serde::de::from_interface_values(&record_values) - .map_err(|e| { - InstructionError::new( - instruction, - InstructionErrorKind::SerdeError(e.to_string()), - ) - })?; - let value_pointer = write_to_instance_mem(*instance, instruction, &value)?; - runtime.stack.push(InterfaceValue::I32(value_pointer)); - runtime.stack.push(InterfaceValue::I32(value.len() as _)); - */ - let offset = record_lower_memory_(*instance, instruction, record_values)?; + match runtime.stack.pop1() { + Some(InterfaceValue::Record(record_fields)) => { + is_record_fields_compatible_to_type( + &**instance, + record_type_id, + &record_fields, + instruction, + )?; + let offset = record_lower_memory_(*instance, instruction, record_fields)?; runtime.stack.push(InterfaceValue::I32(offset)); Ok(()) @@ -422,8 +394,8 @@ where Some(value) => Err(InstructionError::new( instruction, InstructionErrorKind::InvalidValueOnTheStack { - expected_type: InterfaceType::Record(record_type.clone()), - received_type: (&value).into(), + expected_type: InterfaceType::Record(record_type_id), + received_value: value, }, )), None => Err(InstructionError::new( @@ -435,6 +407,7 @@ where }) } +/* pub(crate) fn record_lower( type_index: u32, instruction: Instruction, @@ -494,3 +467,4 @@ where } }) } + */ diff --git a/src/interpreter/instructions/records/utils.rs b/src/interpreter/instructions/records/utils.rs index ccae7ee..99a9f30 100644 --- a/src/interpreter/instructions/records/utils.rs +++ b/src/interpreter/instructions/records/utils.rs @@ -42,7 +42,7 @@ where } pub(super) fn write_to_instance_mem<'instance, Instance, Export, LocalImport, Memory, MemoryView>( - instance: &'instance mut Instance, + instance: &'instance Instance, instruction: Instruction, bytes: &[u8], ) -> Result @@ -74,7 +74,7 @@ where } pub(super) fn allocate<'instance, Instance, Export, LocalImport, Memory, MemoryView>( - instance: &'instance mut Instance, + instance: &'instance Instance, instruction: Instruction, size: i32, ) -> Result @@ -105,7 +105,7 @@ where } pub(super) fn deallocate<'instance, Instance, Export, LocalImport, Memory, MemoryView>( - instance: &'instance mut Instance, + instance: &'instance Instance, instruction: Instruction, mem_ptr: i32, size: i32, @@ -128,7 +128,7 @@ where } fn call_core<'instance, Instance, Export, LocalImport, Memory, MemoryView>( - instance: &'instance mut Instance, + instance: &'instance Instance, function_index: u32, instruction: Instruction, inputs: Vec, @@ -147,20 +147,14 @@ where InstructionErrorKind::LocalOrImportIsMissing { function_index }, ) })?; - let input_types = inputs - .iter() - .map(Into::into) - .collect::>(); - if input_types != local_or_import.inputs() { - return Err(InstructionError::new( - instruction, - InstructionErrorKind::LocalOrImportSignatureMismatch { - function_index, - expected: (local_or_import.inputs().to_vec(), vec![]), - received: (input_types, vec![]), - }, - )); - } + + crate::interpreter::instructions::check_function_signature( + instance, + local_or_import, + &inputs, + instruction, + )?; + let outputs = local_or_import.call(&inputs).map_err(|_| { InstructionError::new( instruction, diff --git a/src/interpreter/instructions/strings.rs b/src/interpreter/instructions/strings.rs index cdbfe80..93876bd 100644 --- a/src/interpreter/instructions/strings.rs +++ b/src/interpreter/instructions/strings.rs @@ -62,6 +62,7 @@ executable_instruction!( let string = String::from_utf8(data) .map_err(|error| InstructionError::new(instruction, InstructionErrorKind::String(error)))?; + log::trace!("string.lift_memory: pushing {:?} on the stack", string); runtime.stack.push(InterfaceValue::String(string)); Ok(()) @@ -108,6 +109,7 @@ executable_instruction!( memory_view[string_pointer as usize + nth].set(*byte); } + log::trace!("string.lower_memory: pushing {}, {} on the stack", string_pointer, string_length); runtime.stack.push(InterfaceValue::I32(string_pointer as i32)); runtime.stack.push(InterfaceValue::I32(string_length)); @@ -122,6 +124,8 @@ executable_instruction!( match runtime.stack.pop1() { Some(InterfaceValue::String(string)) => { let length = string.len() as i32; + + log::trace!("string.size: pushing {} on the stack", length); runtime.stack.push(InterfaceValue::I32(length)); Ok(()) @@ -131,7 +135,7 @@ executable_instruction!( instruction, InstructionErrorKind::InvalidValueOnTheStack { expected_type: InterfaceType::String, - received_type: (&value).into(), + received_value: (&value).clone(), }, )), diff --git a/src/interpreter/instructions/swap2.rs b/src/interpreter/instructions/swap2.rs index 5e465b4..6a487c5 100644 --- a/src/interpreter/instructions/swap2.rs +++ b/src/interpreter/instructions/swap2.rs @@ -13,6 +13,7 @@ executable_instruction!( ) })?; + log::trace!("swap2: swapping {:?}, {:?} values on the stack", values[0], values[1]); runtime.stack.push(values.remove(1)); runtime.stack.push(values.remove(0)); diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 76aa69a..5ff7bd4 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -245,18 +245,19 @@ where } Instruction::ByteArraySize => instructions::byte_array_size(*instruction), + /* Instruction::RecordLift { type_index } => { instructions::record_lift(*type_index, *instruction) } Instruction::RecordLower { type_index } => { instructions::record_lower(*type_index, *instruction) } - - Instruction::RecordLiftMemory { type_index } => { - instructions::record_lift_memory(*type_index, *instruction) + */ + Instruction::RecordLiftMemory { record_type_id } => { + instructions::record_lift_memory(*record_type_id as _, *instruction) } - Instruction::RecordLowerMemory { type_index } => { - instructions::record_lower_memory(*type_index, *instruction) + Instruction::RecordLowerMemory { record_type_id } => { + instructions::record_lower_memory(*record_type_id as _, *instruction) } Instruction::Dup => instructions::dup(*instruction), Instruction::Swap2 => instructions::swap2(*instruction), diff --git a/src/interpreter/wasm/structures.rs b/src/interpreter/wasm/structures.rs index 217a418..e6dd9ac 100644 --- a/src/interpreter/wasm/structures.rs +++ b/src/interpreter/wasm/structures.rs @@ -1,6 +1,8 @@ #![allow(missing_docs)] -use crate::{ast, types::InterfaceType, values::InterfaceValue}; +use crate::ast::FunctionArg; +use crate::types::RecordType; +use crate::{types::InterfaceType, values::InterfaceValue}; use std::{cell::Cell, ops::Deref}; pub trait TypedIndex: Copy + Clone { @@ -42,7 +44,7 @@ impl LocalImportIndex for FunctionIndex { pub trait Export { fn inputs_cardinality(&self) -> usize; fn outputs_cardinality(&self) -> usize; - fn inputs(&self) -> &[InterfaceType]; + fn arguments(&self) -> &[FunctionArg]; fn outputs(&self) -> &[InterfaceType]; fn call(&self, arguments: &[InterfaceValue]) -> Result, ()>; } @@ -50,7 +52,7 @@ pub trait Export { pub trait LocalImport { fn inputs_cardinality(&self) -> usize; fn outputs_cardinality(&self) -> usize; - fn inputs(&self) -> &[InterfaceType]; + fn arguments(&self) -> &[FunctionArg]; fn outputs(&self) -> &[InterfaceType]; fn call(&self, arguments: &[InterfaceValue]) -> Result, ()>; } @@ -72,9 +74,9 @@ where MV: MemoryView, { fn export(&self, export_name: &str) -> Option<&E>; - fn local_or_import(&mut self, index: I) -> Option<&LI>; + fn local_or_import(&self, index: I) -> Option<&LI>; fn memory(&self, index: usize) -> Option<&M>; - fn wit_type(&self, index: u32) -> Option<&ast::Type>; + fn wit_record_by_id(&self, index: u64) -> Option<&RecordType>; } impl Export for () { @@ -86,7 +88,7 @@ impl Export for () { 0 } - fn inputs(&self) -> &[InterfaceType] { + fn arguments(&self) -> &[FunctionArg] { &[] } @@ -108,7 +110,7 @@ impl LocalImport for () { 0 } - fn inputs(&self) -> &[InterfaceType] { + fn arguments(&self) -> &[FunctionArg] { &[] } @@ -154,11 +156,11 @@ where None } - fn local_or_import(&mut self, _index: I) -> Option<&LI> { + fn local_or_import(&self, _index: I) -> Option<&LI> { None } - fn wit_type(&self, _index: u32) -> Option<&ast::Type> { + fn wit_record_by_id(&self, _index: u64) -> Option<&RecordType> { None } } diff --git a/src/serde/de.rs b/src/serde/de.rs index b4e82e0..9d901ea 100644 --- a/src/serde/de.rs +++ b/src/serde/de.rs @@ -91,7 +91,7 @@ macro_rules! next { Some(wrong_value) => Err(DeserializeError::TypeMismatch { expected_type: InterfaceType::$variant, - received_type: (*wrong_value).into(), + received_value: (*wrong_value).clone(), }), None => Err(DeserializeError::InputEmpty), @@ -122,7 +122,7 @@ impl<'de> Deserializer<'de> { Some(wrong_value) => Err(DeserializeError::TypeMismatch { expected_type: InterfaceType::String, - received_type: (*wrong_value).into(), + received_value: (*wrong_value).clone(), }), None => Err(DeserializeError::InputEmpty), @@ -139,7 +139,7 @@ impl<'de> Deserializer<'de> { Some(wrong_value) => Err(DeserializeError::TypeMismatch { expected_type: InterfaceType::ByteArray, - received_type: (*wrong_value).into(), + received_value: (*wrong_value).clone(), }), None => Err(DeserializeError::InputEmpty), @@ -165,7 +165,7 @@ pub enum DeserializeError { expected_type: InterfaceType, /// The received type. - received_type: InterfaceType, + received_value: InterfaceValue, }, /// Arbitrary message. @@ -186,11 +186,11 @@ impl Display for DeserializeError { Self::InputEmpty => write!(formatter, "Unexpected end of input"), Self::TypeMismatch { ref expected_type, - ref received_type, + ref received_value, } => write!( formatter, - "Type mismatch detected, expected `{:?}` but received `{:?}`", - expected_type, received_type + "Type mismatch detected: `{:?}` can't be converted to `{:?}`", + received_value, expected_type, ), } } @@ -220,7 +220,7 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> { Some(InterfaceValue::ByteArray(_)) => self.deserialize_bytes(visitor), Some(InterfaceValue::I32(_)) => self.deserialize_i32(visitor), Some(InterfaceValue::I64(_)) => self.deserialize_i64(visitor), - Some(InterfaceValue::Record(_)) => unreachable!("Records should have been flattened."), // already flattened + Some(InterfaceValue::Record(..)) => unreachable!("Records should have been flattened."), // already flattened None => Err(DeserializeError::InputEmpty), } } diff --git a/src/types.rs b/src/types.rs index e4d6ca4..15472ed 100644 --- a/src/types.rs +++ b/src/types.rs @@ -4,7 +4,7 @@ use crate::vec1::Vec1; use serde::{Deserialize, Serialize}; /// Represents the types supported by WIT. -#[derive(PartialEq, Debug, Clone, Serialize, Deserialize)] +#[derive(PartialEq, Eq, Debug, Clone, Hash, Serialize, Deserialize)] pub enum InterfaceType { /// A 8-bits signed integer. S8, @@ -51,15 +51,42 @@ pub enum InterfaceType { /// A 64-bits integer (as defiend in WebAssembly core). I64, - /// A record. - Record(RecordType), + /// A record contains record index from interfaces AST. + Record(u64), +} + +/// Represents a record field type. +#[derive(PartialEq, Eq, Debug, Clone, Hash, Serialize, Deserialize)] +pub struct RecordFieldType { + // TODO: make name optional to support structures with anonymous fields in Rust + /// A field name. + pub name: String, + + /// A field type. + pub ty: InterfaceType, } /// Represents a record type. -#[derive(PartialEq, Debug, Clone, Serialize, Deserialize)] +#[derive(PartialEq, Eq, Debug, Clone, Hash, Serialize, Deserialize)] pub struct RecordType { - /// Types representing the fields. + /// A record name. + pub name: String, + + /// Types and names representing the fields. /// A record must have at least one field, hence the /// [`Vec1`][crate::vec1::Vec1]. - pub fields: Vec1, + pub fields: Vec1, +} + +impl Default for RecordType { + fn default() -> Self { + Self { + name: String::new(), + fields: Vec1::new(vec![RecordFieldType { + name: String::new(), + ty: InterfaceType::S8, + }]) + .unwrap(), + } + } } diff --git a/src/values.rs b/src/values.rs index 13156d1..4cb952a 100644 --- a/src/values.rs +++ b/src/values.rs @@ -1,10 +1,6 @@ //! Defines WIT values and associated operations. -use crate::{ - errors::WasmValueNativeCastError, - types::{InterfaceType, RecordType}, - vec1::Vec1, -}; +use crate::{errors::WasmValueNativeCastError, types::InterfaceType, vec1::Vec1}; use std::{convert::TryFrom, slice::Iter}; #[cfg(feature = "serde")] @@ -60,44 +56,12 @@ pub enum InterfaceValue { Record(Vec1), } -impl From<&InterfaceValue> for InterfaceType { - fn from(value: &InterfaceValue) -> Self { - match value { - InterfaceValue::S8(_) => Self::S8, - InterfaceValue::S16(_) => Self::S16, - InterfaceValue::S32(_) => Self::S32, - InterfaceValue::S64(_) => Self::S64, - InterfaceValue::U8(_) => Self::U8, - InterfaceValue::U16(_) => Self::U16, - InterfaceValue::U32(_) => Self::U32, - InterfaceValue::U64(_) => Self::U64, - InterfaceValue::F32(_) => Self::F32, - InterfaceValue::F64(_) => Self::F64, - InterfaceValue::String(_) => Self::String, - InterfaceValue::ByteArray(_) => Self::ByteArray, - //InterfaceValue::Anyref(_) => Self::Anyref, - InterfaceValue::I32(_) => Self::I32, - InterfaceValue::I64(_) => Self::I64, - InterfaceValue::Record(values) => Self::Record((&**values).into()), - } - } -} - impl Default for InterfaceValue { fn default() -> Self { Self::I32(0) } } -impl From<&Vec> for RecordType { - fn from(values: &Vec) -> Self { - RecordType { - fields: Vec1::new(values.iter().map(Into::into).collect()) - .expect("Record must have at least one field, zero given."), - } - } -} - /// Represents a native type supported by WIT. pub trait NativeType { /// The associated interface type that maps to the native type. @@ -123,7 +87,7 @@ macro_rules! native { match w { InterfaceValue::$variant(n) => Ok(n.clone()), _ => Err(WasmValueNativeCastError { - from: w.into(), + from: w.clone(), to: <$native_type>::INTERFACE_TYPE, }), } @@ -173,8 +137,8 @@ impl<'a> Iterator for FlattenInterfaceValueIterator<'a> { } // Recursively iterate over the record. - Some(InterfaceValue::Record(values)) => { - self.iterators.push(values.iter()); + Some(InterfaceValue::Record(fields)) => { + self.iterators.push(fields.iter()); self.next() } diff --git a/src/vec1.rs b/src/vec1.rs index d7d0aa9..360a3e3 100644 --- a/src/vec1.rs +++ b/src/vec1.rs @@ -9,7 +9,7 @@ use std::{ /// `Vec1` represents a non-empty `Vec`. It derefs to `Vec` /// directly. -#[derive(Clone, PartialEq, Serialize, Deserialize, Default)] +#[derive(Clone, PartialEq, Eq, Serialize, Hash, Deserialize, Default)] pub struct Vec1(Vec) where T: Debug;