diff --git a/Cargo.lock b/Cargo.lock index 0f1f802..c29b454 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -21,6 +21,12 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b486ce3ccf7ffd79fdeb678eac06a9e6c09fc88d33836340becb8fffe87c5e33" +[[package]] +name = "itoa" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc6f3ad7b9d11a0c00842ff8de1b60ee58661048eb8049ed33c73594f359d7e6" + [[package]] name = "leb128" version = "0.2.4" @@ -97,6 +103,12 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed3d612bc64430efeb3f7ee6ef26d590dce0c43249217bddc62112540c7941e1" +[[package]] +name = "safe-transmute" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50b8b2cd387f744f69469aaed197954ba4c0ecdb31e02edf99b023e0df11178a" + [[package]] name = "semver" version = "0.9.0" @@ -132,6 +144,17 @@ dependencies = [ "syn", ] +[[package]] +name = "serde_json" +version = "1.0.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3433e879a558dde8b5e8feb2a04899cf34fdde1fafb894687e52105fc1162ac3" +dependencies = [ + "itoa", + "ryu", + "serde", +] + [[package]] name = "static_assertions" version = "0.3.4" @@ -166,7 +189,9 @@ name = "wasmer-interface-types" version = "0.17.0" dependencies = [ "nom", + "safe-transmute", "serde", + "serde_json", "wast", ] diff --git a/Cargo.toml b/Cargo.toml index 1e90240..0f056d8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,6 +15,8 @@ wast = "8.0" # required by WIT itself, is is used to cross the boundary between the # host and WIT more easily, but it is not used inside Wasm. serde = { version = "1.0", features = ["derive"], optional = true } +serde_json = "1.0" +safe-transmute = "0.11.0" [features] -default = ["serde"] \ No newline at end of file +default = ["serde"] diff --git a/src/ast.rs b/src/ast.rs index 9429dd0..96aed0e 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -8,7 +8,7 @@ use crate::{ use std::str; /// Represents the kind of type. -#[derive(PartialEq, Debug)] +#[derive(PartialEq, Debug, Clone)] pub enum TypeKind { /// A function type. Function, @@ -18,7 +18,7 @@ pub enum TypeKind { } /// Represents a type. -#[derive(PartialEq, Debug)] +#[derive(PartialEq, Debug, Clone)] pub enum Type { /// A function type, like: /// diff --git a/src/decoders/binary.rs b/src/decoders/binary.rs index e8b18c4..4b21c6a 100644 --- a/src/decoders/binary.rs +++ b/src/decoders/binary.rs @@ -92,6 +92,7 @@ fn ty<'input, E: ParseError<&'input [u8]>>( 0x08 => InterfaceType::F32, 0x09 => InterfaceType::F64, 0x0a => InterfaceType::String, + 0x36 => InterfaceType::ByteArray, 0x0b => InterfaceType::Anyref, 0x0c => InterfaceType::I32, 0x0d => InterfaceType::I64, @@ -235,6 +236,10 @@ fn instruction<'input, E: ParseError<&'input [u8]>>( 0x23 => (input, Instruction::StringLowerMemory), 0x24 => (input, Instruction::StringSize), + 0x37 => (input, Instruction::ByteArrayLiftMemory), + 0x38 => (input, Instruction::ByteArrayLowerMemory), + 0x39 => (input, Instruction::ByteArraySize), + 0x25 => { consume!((input, argument_0) = uleb(input)?); @@ -256,6 +261,27 @@ fn instruction<'input, E: ParseError<&'input [u8]>>( ) } + 0x3A => { + consume!((input, argument_0) = uleb(input)?); + + ( + input, + Instruction::RecordLiftMemory { + type_index: argument_0 as u32, + }, + ) + } + 0x3B => { + consume!((input, argument_0) = uleb(input)?); + + ( + input, + Instruction::RecordLowerMemory { + type_index: argument_0 as u32, + }, + ) + } + 0x34 => (input, Instruction::Dup), 0x35 => (input, Instruction::Swap2), diff --git a/src/decoders/wat.rs b/src/decoders/wat.rs index 108c539..2f82f60 100644 --- a/src/decoders/wat.rs +++ b/src/decoders/wat.rs @@ -2,8 +2,8 @@ use crate::{ast::*, interpreter::Instruction, types::*, vec1::Vec1}; pub use wast::parser::ParseBuffer as Buffer; -pub use wast::Error; use wast::parser::{self, Cursor, Parse, Parser, Peek, Result}; +pub use wast::Error; mod keyword { pub use wast::{ @@ -27,6 +27,7 @@ mod keyword { custom_keyword!(u32); custom_keyword!(u64); custom_keyword!(string); + custom_keyword!(byte_array); // Instructions. custom_keyword!(argument_get = "arg.get"); @@ -66,8 +67,13 @@ mod keyword { custom_keyword!(string_lift_memory = "string.lift_memory"); custom_keyword!(string_lower_memory = "string.lower_memory"); custom_keyword!(string_size = "string.size"); + custom_keyword!(byte_array_lift_memory = "byte_array.lift_memory"); + custom_keyword!(byte_array_lower_memory = "byte_array.lower_memory"); + custom_keyword!(byte_array_size = "byte_array.size"); custom_keyword!(record_lift = "record.lift"); custom_keyword!(record_lower = "record.lower"); + custom_keyword!(record_lift_memory = "record.lift_memory"); + custom_keyword!(record_lower_memory = "record.lower_memory"); custom_keyword!(dup = "dup"); custom_keyword!(swap2 = "swap2"); } @@ -120,6 +126,10 @@ impl Parse<'_> for InterfaceType { parser.parse::()?; Ok(InterfaceType::String) + } else if lookahead.peek::() { + parser.parse::()?; + + Ok(InterfaceType::ByteArray) } else if lookahead.peek::() { parser.parse::()?; @@ -317,6 +327,18 @@ impl<'a> Parse<'a> for Instruction { parser.parse::()?; Ok(Instruction::StringSize) + } else if lookahead.peek::() { + parser.parse::()?; + + Ok(Instruction::ByteArrayLiftMemory) + } else if lookahead.peek::() { + parser.parse::()?; + + Ok(Instruction::ByteArrayLowerMemory) + } else if lookahead.peek::() { + parser.parse::()?; + + Ok(Instruction::ByteArraySize) } else if lookahead.peek::() { parser.parse::()?; @@ -329,6 +351,18 @@ impl<'a> Parse<'a> for Instruction { Ok(Instruction::RecordLower { type_index: parser.parse()?, }) + } else if lookahead.peek::() { + parser.parse::()?; + + Ok(Instruction::RecordLiftMemory { + type_index: parser.parse()?, + }) + } else if lookahead.peek::() { + parser.parse::()?; + + Ok(Instruction::RecordLowerMemory { + type_index: parser.parse()?, + }) } else if lookahead.peek::() { parser.parse::()?; diff --git a/src/encoders/binary.rs b/src/encoders/binary.rs index 38eaef3..ca242c7 100644 --- a/src/encoders/binary.rs +++ b/src/encoders/binary.rs @@ -105,6 +105,7 @@ where InterfaceType::F32 => 0x08_u8.to_bytes(writer), InterfaceType::F64 => 0x09_u8.to_bytes(writer), InterfaceType::String => 0x0a_u8.to_bytes(writer), + InterfaceType::ByteArray => 0x36_u8.to_bytes(writer), InterfaceType::Anyref => 0x0b_u8.to_bytes(writer), InterfaceType::I32 => 0x0c_u8.to_bytes(writer), InterfaceType::I64 => 0x0d_u8.to_bytes(writer), @@ -334,6 +335,10 @@ where Instruction::StringLowerMemory => 0x23_u8.to_bytes(writer)?, Instruction::StringSize => 0x24_u8.to_bytes(writer)?, + 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)? @@ -342,6 +347,14 @@ where 0x26_u8.to_bytes(writer)?; (*type_index as u64).to_bytes(writer)? } + Instruction::RecordLiftMemory { type_index } => { + 0x3A_u8.to_bytes(writer)?; + (*type_index as u64).to_bytes(writer)? + } + Instruction::RecordLowerMemory { type_index } => { + 0x3B_u8.to_bytes(writer)?; + (*type_index as u64).to_bytes(writer)? + } Instruction::Dup => 0x34_u8.to_bytes(writer)?, Instruction::Swap2 => 0x35_u8.to_bytes(writer)?, } diff --git a/src/encoders/wat.rs b/src/encoders/wat.rs index e3feaff..aa102f3 100644 --- a/src/encoders/wat.rs +++ b/src/encoders/wat.rs @@ -73,6 +73,7 @@ impl ToString for &InterfaceType { InterfaceType::F32 => "f32".to_string(), InterfaceType::F64 => "f64".to_string(), InterfaceType::String => "string".to_string(), + InterfaceType::ByteArray => "byte_array".to_string(), InterfaceType::Anyref => "anyref".to_string(), InterfaceType::I32 => "i32".to_string(), InterfaceType::I64 => "i64".to_string(), @@ -138,8 +139,17 @@ impl ToString for &Instruction { Instruction::StringLiftMemory => "string.lift_memory".into(), Instruction::StringLowerMemory => "string.lower_memory".into(), Instruction::StringSize => "string.size".into(), + 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::Dup => "dup".into(), Instruction::Swap2 => "swap2".into(), } diff --git a/src/errors.rs b/src/errors.rs index 0762a24..decf278 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -171,6 +171,9 @@ pub enum InstructionErrorKind { /// The received kind. received_kind: TypeKind, }, + + /// Errors related to Serialization/deserialization of record. + SerdeError(String), } impl Error for InstructionErrorKind {} @@ -258,6 +261,10 @@ impl Display for InstructionErrorKind { "read a type of kind `{:?}`, but the kind `{:?}` was expected", received_kind, expected_kind ), + Self::SerdeError(err) => write!( + formatter, + "serde error: {}", err, + ), } } } diff --git a/src/interpreter/instructions/byte_arrays.rs b/src/interpreter/instructions/byte_arrays.rs new file mode 100644 index 0000000..fcc908b --- /dev/null +++ b/src/interpreter/instructions/byte_arrays.rs @@ -0,0 +1,141 @@ +use super::to_native; +use crate::{ + errors::{InstructionError, InstructionErrorKind}, + interpreter::Instruction, + types::InterfaceType, + values::InterfaceValue, +}; +use std::{cell::Cell, convert::TryInto}; + +executable_instruction!( + byte_array_lift_memory(instruction: Instruction) -> _ { + move |runtime| -> _ { + let inputs = runtime.stack.pop(2).ok_or_else(|| { + InstructionError::new( + instruction, + InstructionErrorKind::StackIsTooSmall { needed: 2 }, + ) + })?; + + let memory_index: u32 = 0; + let memory = runtime + .wasm_instance + .memory(memory_index as usize) + .ok_or_else(|| { + InstructionError::new( + instruction, + InstructionErrorKind::MemoryIsMissing { memory_index }, + ) + })?; + + let pointer: usize = to_native::(&inputs[0], instruction)? + .try_into() + .map_err(|e| (e, "pointer").into()) + .map_err(|k| InstructionError::new(instruction, k))?; + let length: usize = to_native::(&inputs[1], instruction)? + .try_into() + .map_err(|e| (e, "length").into()) + .map_err(|k| InstructionError::new(instruction, k))?; + let memory_view = memory.view(); + + if length == 0 { + runtime.stack.push(InterfaceValue::ByteArray(vec![])); + + return Ok(()) + } + + if memory_view.len() < pointer + length { + return Err(InstructionError::new( + instruction, + InstructionErrorKind::MemoryOutOfBoundsAccess { + index: pointer + length, + length: memory_view.len(), + }, + )); + } + + let data: Vec = (&memory_view[pointer..pointer + length]) + .iter() + .map(Cell::get) + .collect(); + + runtime.stack.push(InterfaceValue::ByteArray(data)); + + Ok(()) + } + } +); + +executable_instruction!( + byte_array_lower_memory(instruction: Instruction) -> _ { + move |runtime| -> _ { + let inputs = runtime.stack.pop(2).ok_or_else(|| { + InstructionError::new( + instruction, + InstructionErrorKind::StackIsTooSmall { needed: 2 }, + ) + })?; + + let byte_array_pointer: usize = to_native::(&inputs[0], instruction)? + .try_into() + .map_err(|e| (e, "pointer").into()) + .map_err(|k| InstructionError::new(instruction, k))?; + let byte_array: Vec = to_native(&inputs[1], instruction)?; + let byte_array_length: i32 = byte_array.len().try_into().map_err(|_| { + InstructionError::new( + instruction, + InstructionErrorKind::NegativeValue { subject: "string_length" }, + ) + })?; + + let instance = &mut runtime.wasm_instance; + let memory_index: u32 = 0; + let memory_view = instance + .memory(memory_index as usize) + .ok_or_else(|| { + InstructionError::new( + instruction, + InstructionErrorKind::MemoryIsMissing { memory_index }, + ) + })? + .view(); + + for (nth, byte) in byte_array.iter().enumerate() { + memory_view[byte_array_pointer as usize + nth].set(*byte); + } + + runtime.stack.push(InterfaceValue::I32(byte_array_pointer as i32)); + runtime.stack.push(InterfaceValue::I32(byte_array_length)); + + Ok(()) + } + } +); + +executable_instruction!( + byte_array_size(instruction: Instruction) -> _ { + move |runtime| -> _ { + match runtime.stack.pop1() { + Some(InterfaceValue::ByteArray(byte_array)) => { + let length = byte_array.len() as i32; + runtime.stack.push(InterfaceValue::I32(length)); + + Ok(()) + }, + + Some(value) => Err(InstructionError::new( + instruction, + InstructionErrorKind::InvalidValueOnTheStack { + expected_type: InterfaceType::ByteArray, + received_type: (&value).into(), + }, + )), + + None => Err(InstructionError::new( + instruction, + InstructionErrorKind::StackIsTooSmall { needed: 1 }, + )), + } + } + } +); diff --git a/src/interpreter/instructions/call_core.rs b/src/interpreter/instructions/call_core.rs index 731f844..c4c1632 100644 --- a/src/interpreter/instructions/call_core.rs +++ b/src/interpreter/instructions/call_core.rs @@ -15,7 +15,7 @@ executable_instruction!( InstructionError::new( instruction, InstructionErrorKind::LocalOrImportIsMissing { - function_index: function_index, + function_index, }, ) })?; @@ -38,7 +38,7 @@ executable_instruction!( return Err(InstructionError::new( instruction, InstructionErrorKind::LocalOrImportSignatureMismatch { - function_index: function_index, + function_index, expected: (local_or_import.inputs().to_vec(), vec![]), received: (input_types, vec![]), }, @@ -49,7 +49,7 @@ executable_instruction!( InstructionError::new( instruction, InstructionErrorKind::LocalOrImportCall { - function_index: function_index, + function_index, }, ) })?; diff --git a/src/interpreter/instructions/mod.rs b/src/interpreter/instructions/mod.rs index 1ce533d..63b98bd 100644 --- a/src/interpreter/instructions/mod.rs +++ b/src/interpreter/instructions/mod.rs @@ -1,9 +1,10 @@ mod argument_get; +mod byte_arrays; mod call_core; +mod dup; mod numbers; mod records; mod strings; -mod dup; mod swap2; use crate::{ @@ -11,14 +12,18 @@ use crate::{ values::{InterfaceValue, NativeType}, }; pub(crate) use argument_get::argument_get; +pub(crate) use byte_arrays::*; pub(crate) use call_core::call_core; +pub(crate) use dup::dup; pub(crate) use numbers::*; pub(crate) use records::*; use std::convert::TryFrom; pub(crate) use strings::*; -pub(crate) use dup::dup; pub(crate) use swap2::swap2; +pub(self) const ALLOCATE_FUNC_INDEX: u32 = 0; +pub(self) const DEALLOCATE_FUNC_INDEX: u32 = 1; + /// Represents all the possible WIT instructions. #[derive(PartialEq, Debug, Clone, Copy)] pub enum Instruction { @@ -139,6 +144,15 @@ pub enum Instruction { /// The `string.size` instruction. StringSize, + /// The `byte_array.lift_memory` instruction. + ByteArrayLiftMemory, + + /// The `byte_array.lower_memory` instruction. + ByteArrayLowerMemory, + + /// The `string.size` instruction. + ByteArraySize, + /// The `record.lift` instruction. RecordLift { /// The type index of the record. @@ -151,6 +165,18 @@ pub enum Instruction { type_index: u32, }, + /// The `record.lift_memory` instruction. + RecordLiftMemory { + /// The type index of the record. + type_index: u32, + }, + + /// The `record.lower_memory` instruction. + RecordLowerMemory { + /// The type index of the record. + type_index: u32, + }, + /// The `dup` instructions. Dup, diff --git a/src/interpreter/instructions/records.rs b/src/interpreter/instructions/records.rs index e63dbf8..fc81809 100644 --- a/src/interpreter/instructions/records.rs +++ b/src/interpreter/instructions/records.rs @@ -1,3 +1,11 @@ +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::{ ast::{Type, TypeKind}, errors::{InstructionError, InstructionErrorKind}, @@ -9,7 +17,9 @@ use crate::{ values::{FlattenInterfaceValueIterator, InterfaceValue}, vec1::Vec1, }; + use std::collections::VecDeque; +use std::convert::TryInto; /// Build an `InterfaceValue::Record` based on values on the stack. /// @@ -30,41 +40,45 @@ fn record_lift_( ) -> Result { let length = record_type.fields.len(); let mut values = VecDeque::with_capacity(length); - - // Iterate over fields in reverse order to match the stack `pop` - // order. for field in record_type.fields.iter().rev() { match field { - // The record type tells a record is expected. InterfaceType::Record(record_type) => { - // Build it recursively. values.push_front(record_lift_(stack, &record_type)?) } - // Any other type. ty => { let value = stack.pop1().unwrap(); let value_type = (&value).into(); - if ty != &value_type { return Err(InstructionErrorKind::InvalidValueOnTheStack { expected_type: ty.clone(), received_type: value_type, }); } - values.push_front(value) } } } - 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 + .expect("Record must have at least one field, zero given"), )) } -executable_instruction!( - record_lift(type_index: u32, instruction: Instruction) -> _ { +pub(crate) fn record_lift( + type_index: u32, + instruction: Instruction, +) -> crate::interpreter::ExecutableInstruction +where + Export: crate::interpreter::wasm::structures::Export, + LocalImport: crate::interpreter::wasm::structures::LocalImport, + Memory: crate::interpreter::wasm::structures::Memory, + MemoryView: crate::interpreter::wasm::structures::MemoryView, + Instance: + crate::interpreter::wasm::structures::Instance, +{ + #[allow(unused_imports)] + use crate::interpreter::stack::Stackable; + Box::new({ move |runtime| -> _ { let instance = &runtime.wasm_instance; let record_type = match instance.wit_type(type_index).ok_or_else(|| { @@ -74,29 +88,302 @@ executable_instruction!( ) })? { Type::Record(record_type) => record_type, - Type::Function { .. } => return Err(InstructionError::new( - instruction, - InstructionErrorKind::InvalidTypeKind { - expected_kind: TypeKind::Record, - received_kind: TypeKind::Function - } - )), + Type::Function { .. } => { + return Err(InstructionError::new( + instruction, + InstructionErrorKind::InvalidTypeKind { + expected_kind: TypeKind::Record, + received_kind: TypeKind::Function, + }, + )) + } }; - let record = record_lift_(&mut runtime.stack, &record_type) .map_err(|k| InstructionError::new(instruction, k))?; + runtime.stack.push(record); + Ok(()) + } + }) +} +fn record_lift_memory_<'instance, Instance, Export, LocalImport, Memory, MemoryView>( + instance: &'instance mut Instance, + record_type: RecordType, + offset: usize, + instruction: Instruction, +) -> Result +where + Export: crate::interpreter::wasm::structures::Export, + LocalImport: crate::interpreter::wasm::structures::LocalImport, + Memory: crate::interpreter::wasm::structures::Memory, + MemoryView: crate::interpreter::wasm::structures::MemoryView, + Instance: crate::interpreter::wasm::structures::Instance + + 'instance, +{ + fn record_size(record_type: &RecordType) -> usize { + let mut record_size = 0; + + for ty in record_type.fields.iter() { + let params_count = match ty { + InterfaceType::String | InterfaceType::ByteArray => 2, + _ => 1, + }; + + record_size += std::mem::size_of::() * params_count; + } + + record_size + } + + let length = record_type.fields.len(); + let mut values = VecDeque::with_capacity(length); + 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() { + let value = data[field_id]; + match field { + InterfaceType::S8 => { + values.push_back(InterfaceValue::S8(value as _)); + } + InterfaceType::S16 => { + values.push_back(InterfaceValue::S16(value as _)); + } + InterfaceType::S32 => { + values.push_back(InterfaceValue::S32(value as _)); + } + InterfaceType::S64 => { + values.push_back(InterfaceValue::S64(value as _)); + } + InterfaceType::I32 => { + values.push_back(InterfaceValue::I32(value as _)); + } + InterfaceType::I64 => { + values.push_back(InterfaceValue::I64(value as _)); + } + InterfaceType::U8 => { + values.push_back(InterfaceValue::U8(value as _)); + } + InterfaceType::U16 => { + values.push_back(InterfaceValue::U16(value as _)); + } + InterfaceType::U32 => { + values.push_back(InterfaceValue::U32(value as _)); + } + InterfaceType::U64 => { + values.push_back(InterfaceValue::U64(value as _)); + } + InterfaceType::F32 => { + values.push_back(InterfaceValue::F32(value as _)); + } + InterfaceType::F64 => values.push_back(InterfaceValue::F64(f64::from_bits(value))), + InterfaceType::Anyref => {} + InterfaceType::String => { + let string_offset = value; + field_id += 1; + let string_size = data[field_id]; + + if string_size != 0 { + let string_mem = read_from_instance_mem( + instance, + instruction, + string_offset as _, + string_size as _, + )?; + + // TODO: check + let string = String::from_utf8(string_mem).unwrap(); + values.push_back(InterfaceValue::String(string)); + + utils::deallocate(instance, instruction, string_offset as _, string_size as _)?; + } else { + values.push_back(InterfaceValue::String("".to_string())); + } + } + InterfaceType::ByteArray => { + let array_offset = value; + field_id += 1; + let array_size = data[field_id]; + + if array_size != 0 { + let byte_array = read_from_instance_mem( + instance, + instruction, + array_offset as _, + array_size as _, + )?; + + values.push_back(InterfaceValue::ByteArray(byte_array)); + + utils::deallocate(instance, instruction, array_offset as _, array_size as _)?; + } else { + values.push_back(InterfaceValue::ByteArray(vec![])); + } + } + InterfaceType::Record(record_type) => { + let offset = value; + + values.push_back(record_lift_memory_( + instance, + record_type, + offset as _, + instruction, + )?) + } + } + field_id += 1; + } + + utils::deallocate(instance, instruction, offset as _, size as _)?; + + Ok(InterfaceValue::Record( + Vec1::new(values.into_iter().collect()) + .expect("Record must have at least one field, zero given"), + )) +} + +pub(crate) fn record_lift_memory( + type_index: u32, + instruction: Instruction, +) -> crate::interpreter::ExecutableInstruction +where + Export: crate::interpreter::wasm::structures::Export, + LocalImport: crate::interpreter::wasm::structures::LocalImport, + Memory: crate::interpreter::wasm::structures::Memory, + MemoryView: crate::interpreter::wasm::structures::MemoryView, + Instance: + crate::interpreter::wasm::structures::Instance, +{ + #[allow(unused_imports)] + use crate::interpreter::stack::Stackable; + Box::new({ + move |runtime| -> _ { + let inputs = runtime.stack.pop(1).ok_or_else(|| { + InstructionError::new( + instruction, + InstructionErrorKind::StackIsTooSmall { needed: 1 }, + ) + })?; + + let offset: usize = to_native::(&inputs[0], instruction)? + .try_into() + .map_err(|e| (e, "offset").into()) + .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(|| { + InstructionError::new( + instruction, + InstructionErrorKind::TypeIsMissing { type_index }, + ) + })? { + 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)?; runtime.stack.push(record); Ok(()) } - } -); + }) +} -executable_instruction!( - record_lower(type_index: u32, instruction: Instruction) -> _ { +fn record_lower_memory_( + instance: &mut Instance, + instruction: Instruction, + values: Vec1, +) -> Result +where + Export: crate::interpreter::wasm::structures::Export, + LocalImport: crate::interpreter::wasm::structures::LocalImport, + Memory: crate::interpreter::wasm::structures::Memory, + MemoryView: crate::interpreter::wasm::structures::MemoryView, + Instance: + crate::interpreter::wasm::structures::Instance, +{ + let mut result: Vec = Vec::with_capacity(values.len()); + + for value in values.into_vec() { + match value { + InterfaceValue::S8(value) => result.push(value as _), + InterfaceValue::S16(value) => result.push(value as _), + InterfaceValue::S32(value) => result.push(value as _), + InterfaceValue::S64(value) => result.push(value as _), + InterfaceValue::U8(value) => result.push(value as _), + InterfaceValue::U16(value) => result.push(value as _), + InterfaceValue::U32(value) => result.push(value as _), + InterfaceValue::U64(value) => result.push(value as _), + InterfaceValue::I32(value) => result.push(value as _), + InterfaceValue::I64(value) => result.push(value as _), + InterfaceValue::F32(value) => result.push(value as _), + InterfaceValue::F64(value) => result.push(value.to_bits()), + InterfaceValue::String(value) => { + let string_pointer = if !value.is_empty() { + write_to_instance_mem(instance, instruction, value.as_bytes())? + } else { + 0i32 + }; + + result.push(string_pointer as _); + result.push(value.len() as _); + } + + InterfaceValue::ByteArray(value) => { + let byte_array_pointer = if !value.is_empty() { + write_to_instance_mem(instance, instruction, &value)? + } else { + 0i32 + }; + + result.push(byte_array_pointer as _); + result.push(value.len() as _); + } + + InterfaceValue::Record(record) => { + let record_ptr = record_lower_memory_(instance, instruction, record)?; + + result.push(record_ptr as _); + } + } + } + + let result = safe_transmute::transmute_to_bytes::(&result); + let result_pointer = write_to_instance_mem(instance, instruction, &result)?; + + Ok(result_pointer) +} + +pub(crate) fn record_lower_memory( + type_index: u32, + instruction: Instruction, +) -> crate::interpreter::ExecutableInstruction +where + Export: crate::interpreter::wasm::structures::Export, + LocalImport: crate::interpreter::wasm::structures::LocalImport, + Memory: crate::interpreter::wasm::structures::Memory, + MemoryView: crate::interpreter::wasm::structures::MemoryView, + Instance: + crate::interpreter::wasm::structures::Instance, +{ + #[allow(unused_imports)] + use crate::interpreter::stack::Stackable; + Box::new({ move |runtime| -> _ { - let instance = &runtime.wasm_instance; + let instance = &mut runtime.wasm_instance; let record_type = match instance.wit_type(type_index).ok_or_else(|| { InstructionError::new( instruction, @@ -104,281 +391,110 @@ executable_instruction!( ) })? { Type::Record(record_type) => record_type, - Type::Function { .. } => return Err(InstructionError::new( - instruction, - InstructionErrorKind::InvalidTypeKind { - expected_kind: TypeKind::Record, - received_kind: TypeKind::Function - } - )), + 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 values = FlattenInterfaceValueIterator::new(&record_values); + 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()), + ) + })?; - for value in values { - runtime.stack.push(value.clone()); - } + 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)?; + runtime.stack.push(InterfaceValue::I32(offset)); Ok(()) - }, - + } Some(value) => Err(InstructionError::new( instruction, InstructionErrorKind::InvalidValueOnTheStack { expected_type: InterfaceType::Record(record_type.clone()), received_type: (&value).into(), - } + }, )), - None => Err(InstructionError::new( instruction, InstructionErrorKind::StackIsTooSmall { needed: 1 }, )), } } - } -); - -#[cfg(test)] -mod tests { - use super::*; - - test_executable_instruction!( - test_record_lift = - instructions: [ - Instruction::ArgumentGet { index: 0 }, - Instruction::ArgumentGet { index: 1 }, - Instruction::ArgumentGet { index: 2 }, - Instruction::ArgumentGet { index: 3 }, - Instruction::RecordLift { type_index: 0 }, - ], - invocation_inputs: [ - InterfaceValue::I32(1), - InterfaceValue::String("Hello".to_string()), - InterfaceValue::F32(2.), - InterfaceValue::I64(3), - ], - instance: Instance::new(), - stack: [InterfaceValue::Record(vec1![ - InterfaceValue::I32(1), - InterfaceValue::Record(vec1![ - InterfaceValue::String("Hello".to_string()), - InterfaceValue::F32(2.), - ]), - InterfaceValue::I64(3), - ])], - ); - - #[cfg(feature = "serde")] - #[test] - #[allow(non_snake_case, unused)] - fn test_record_lift__to_rust_struct() { - use crate::{ - interpreter::{ - instructions::tests::{Export, Instance, LocalImport, Memory, MemoryView}, - stack::Stackable, - Instruction, Interpreter, - }, - types::InterfaceType, - values::{from_interface_values, InterfaceValue}, - }; - use serde::Deserialize; - use std::{cell::Cell, collections::HashMap, convert::TryInto}; - - let interpreter: Interpreter = (&vec![ - Instruction::ArgumentGet { index: 0 }, - Instruction::ArgumentGet { index: 1 }, - Instruction::ArgumentGet { index: 2 }, - Instruction::ArgumentGet { index: 3 }, - Instruction::RecordLift { type_index: 0 }, - ]) - .try_into() - .unwrap(); - - let invocation_inputs = vec![ - InterfaceValue::I32(1), - InterfaceValue::String("Hello".to_string()), - InterfaceValue::F32(2.), - InterfaceValue::I64(3), - ]; - let mut instance = Instance::new(); - let run = interpreter.run(&invocation_inputs, &mut instance); - - assert!(run.is_ok()); - - let stack = run.unwrap(); - - #[derive(Deserialize, Debug, PartialEq)] - struct S { - a: String, - b: f32, - } - - #[derive(Deserialize, Debug, PartialEq)] - struct T { - x: i32, - s: S, - y: i64, - } - - let record: T = from_interface_values(stack.as_slice()).unwrap(); - - assert_eq!( - record, - T { - x: 1, - s: S { - a: "Hello".to_string(), - b: 2., - }, - y: 3, - } - ); - } - - test_executable_instruction!( - test_record_lift__one_dimension = - instructions: [ - Instruction::ArgumentGet { index: 0 }, - Instruction::ArgumentGet { index: 1 }, - Instruction::RecordLift { type_index: 1 }, - ], - invocation_inputs: [ - InterfaceValue::I32(1), - InterfaceValue::I32(2), - ], - instance: { - let mut instance = Instance::new(); - instance.wit_types.push( - Type::Record(RecordType { - fields: vec1![InterfaceType::I32, InterfaceType::I32], - }) - ); - - instance - }, - stack: [InterfaceValue::Record(vec1![ - InterfaceValue::I32(1), - InterfaceValue::I32(2), - ])], - ); - - test_executable_instruction!( - test_record_lift__type_is_missing = - instructions: [ - Instruction::RecordLift { type_index: 0 }, - ], - invocation_inputs: [], - instance: Default::default(), - error: r#"`record.lift 0` the type `0` doesn't exist"#, - ); - - test_executable_instruction!( - test_record_lift__invalid_value_on_the_stack = - instructions: [ - Instruction::ArgumentGet { index: 0 }, - Instruction::ArgumentGet { index: 1 }, - Instruction::ArgumentGet { index: 2 }, - Instruction::ArgumentGet { index: 3 }, - Instruction::RecordLift { type_index: 0 }, - ], - invocation_inputs: [ - InterfaceValue::I32(1), - InterfaceValue::String("Hello".to_string()), - InterfaceValue::F64(2.), - // ^^^ F32 is expected - InterfaceValue::I64(3), - ], - instance: Instance::new(), - error: r#"`record.lift 0` read a value of type `F64` from the stack, but the type `F32` was expected"#, - ); - - test_executable_instruction!( - test_record_lower = - instructions: [ - Instruction::ArgumentGet { index: 0 }, - Instruction::RecordLower { type_index: 0 }, - ], - invocation_inputs: [ - InterfaceValue::Record(vec1![ - InterfaceValue::I32(1), - InterfaceValue::Record(vec1![ - InterfaceValue::String("Hello".to_string()), - InterfaceValue::F32(2.), - ]), - InterfaceValue::I64(3), - ]) - ], - instance: Instance::new(), - stack: [ - InterfaceValue::I32(1), - InterfaceValue::String("Hello".to_string()), - InterfaceValue::F32(2.), - InterfaceValue::I64(3), - ], - ); - - test_executable_instruction!( - test_record__roundtrip = - instructions: [ - Instruction::ArgumentGet { index: 0 }, - Instruction::RecordLower { type_index: 0 }, - Instruction::RecordLift { type_index: 0 }, - ], - invocation_inputs: [ - InterfaceValue::Record(vec1![ - InterfaceValue::I32(1), - InterfaceValue::Record(vec1![ - InterfaceValue::String("Hello".to_string()), - InterfaceValue::F32(2.), - ]), - InterfaceValue::I64(3), - ]) - ], - instance: Instance::new(), - stack: [ - InterfaceValue::Record(vec1![ - InterfaceValue::I32(1), - InterfaceValue::Record(vec1![ - InterfaceValue::String("Hello".to_string()), - InterfaceValue::F32(2.), - ]), - InterfaceValue::I64(3), - ]) - ], - ); - - test_executable_instruction!( - test_record_lower__invalid_value_on_the_stack = - instructions: [ - Instruction::ArgumentGet { index: 0 }, - Instruction::RecordLower { type_index: 0 }, - ], - invocation_inputs: [ - InterfaceValue::I32(1), - ], - instance: Instance::new(), - error: r#"`record.lower 0` read a value of type `I32` from the stack, but the type `Record(RecordType { fields: [I32, Record(RecordType { fields: [String, F32] }), I64] })` was expected"#, - ); - - test_executable_instruction!( - test_record_lower__invalid_value_on_the_stack__different_record_type = - instructions: [ - Instruction::ArgumentGet { index: 0 }, - Instruction::RecordLower { type_index: 0 }, - ], - invocation_inputs: [ - InterfaceValue::Record(vec1![ - InterfaceValue::I32(1), - InterfaceValue::Record(vec1![ - InterfaceValue::String("Hello".to_string()), - ]), - InterfaceValue::I64(3), - ]) - ], - instance: Instance::new(), - error: r#"`record.lower 0` read a value of type `Record(RecordType { fields: [I32, Record(RecordType { fields: [String] }), I64] })` from the stack, but the type `Record(RecordType { fields: [I32, Record(RecordType { fields: [String, F32] }), I64] })` was expected"#, - ); + }) +} + +pub(crate) fn record_lower( + type_index: u32, + instruction: Instruction, +) -> crate::interpreter::ExecutableInstruction +where + Export: crate::interpreter::wasm::structures::Export, + LocalImport: crate::interpreter::wasm::structures::LocalImport, + Memory: crate::interpreter::wasm::structures::Memory, + MemoryView: crate::interpreter::wasm::structures::MemoryView, + Instance: + crate::interpreter::wasm::structures::Instance, +{ + #[allow(unused_imports)] + use crate::interpreter::stack::Stackable; + Box::new({ + move |runtime| -> _ { + let instance = &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 values = FlattenInterfaceValueIterator::new(&record_values); + for value in values { + runtime.stack.push(value.clone()); + } + Ok(()) + } + Some(value) => Err(InstructionError::new( + instruction, + InstructionErrorKind::InvalidValueOnTheStack { + expected_type: InterfaceType::Record(record_type.clone()), + received_type: (&value).into(), + }, + )), + None => Err(InstructionError::new( + instruction, + InstructionErrorKind::StackIsTooSmall { needed: 1 }, + )), + } + } + }) } diff --git a/src/interpreter/instructions/records/utils.rs b/src/interpreter/instructions/records/utils.rs new file mode 100644 index 0000000..ccae7ee --- /dev/null +++ b/src/interpreter/instructions/records/utils.rs @@ -0,0 +1,172 @@ +use crate::interpreter::instructions::ALLOCATE_FUNC_INDEX; +use crate::interpreter::instructions::DEALLOCATE_FUNC_INDEX; +use crate::interpreter::wasm; +use crate::interpreter::wasm::structures::{FunctionIndex, TypedIndex}; + +use crate::interpreter::instructions::to_native; +use crate::{ + errors::{InstructionError, InstructionErrorKind}, + interpreter::Instruction, + types::InterfaceType, + values::InterfaceValue, +}; + +pub(super) fn read_from_instance_mem<'instance, Instance, Export, LocalImport, Memory, MemoryView>( + instance: &'instance Instance, + instruction: Instruction, + offset: usize, + size: usize, +) -> 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 memory_index: u32 = 0; + let memory_view = instance + .memory(memory_index as usize) + .ok_or_else(|| { + InstructionError::new( + instruction, + InstructionErrorKind::MemoryIsMissing { memory_index }, + ) + })? + .view(); + + Ok((&memory_view[offset..offset + size]) + .iter() + .map(std::cell::Cell::get) + .collect::>()) +} + +pub(super) fn write_to_instance_mem<'instance, Instance, Export, LocalImport, Memory, MemoryView>( + instance: &'instance mut Instance, + instruction: Instruction, + bytes: &[u8], +) -> Result +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 mem_pointer = allocate(instance, instruction, bytes.len() as _)?; + + let memory_index: u32 = 0; + let memory_view = instance + .memory(memory_index as usize) + .ok_or_else(|| { + InstructionError::new( + instruction, + InstructionErrorKind::MemoryIsMissing { memory_index }, + ) + })? + .view(); + + for (byte_id, byte) in bytes.iter().enumerate() { + memory_view[mem_pointer as usize + byte_id].set(*byte); + } + + Ok(mem_pointer) +} + +pub(super) fn allocate<'instance, Instance, Export, LocalImport, Memory, MemoryView>( + instance: &'instance mut Instance, + instruction: Instruction, + size: i32, +) -> Result +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 values = call_core( + instance, + ALLOCATE_FUNC_INDEX, + instruction, + vec![InterfaceValue::I32(size)], + )?; + if values.len() != 1 { + return Err(InstructionError::new( + instruction, + InstructionErrorKind::LocalOrImportSignatureMismatch { + function_index: ALLOCATE_FUNC_INDEX, + expected: (vec![InterfaceType::I32], vec![]), + received: (vec![], vec![]), + }, + )); + } + to_native::(&values[0], instruction) +} + +pub(super) fn deallocate<'instance, Instance, Export, LocalImport, Memory, MemoryView>( + instance: &'instance mut Instance, + instruction: Instruction, + mem_ptr: i32, + size: i32, +) -> 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 _ = call_core( + instance, + DEALLOCATE_FUNC_INDEX, + instruction, + vec![InterfaceValue::I32(mem_ptr), InterfaceValue::I32(size)], + )?; + + Ok(()) +} + +fn call_core<'instance, Instance, Export, LocalImport, Memory, MemoryView>( + instance: &'instance mut Instance, + function_index: u32, + instruction: Instruction, + inputs: Vec, +) -> 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 index = FunctionIndex::new(function_index as usize); + let local_or_import = instance.local_or_import(index).ok_or_else(|| { + InstructionError::new( + instruction, + 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![]), + }, + )); + } + let outputs = local_or_import.call(&inputs).map_err(|_| { + InstructionError::new( + instruction, + InstructionErrorKind::LocalOrImportCall { function_index }, + ) + })?; + + Ok(outputs) +} diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index a40b188..76aa69a 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -40,9 +40,8 @@ where /// Type alias for an executable instruction. It's an implementation /// details, but an instruction is a boxed closure instance. pub(crate) type ExecutableInstruction = Box< - dyn Fn( - &mut Runtime, - ) -> InstructionResult<()> + Send, + dyn Fn(&mut Runtime) -> InstructionResult<()> + + Send, >; /// An interpreter is the central piece of this crate. It is a set of @@ -238,12 +237,27 @@ where Instruction::StringLowerMemory => instructions::string_lower_memory(*instruction), Instruction::StringSize => instructions::string_size(*instruction), + Instruction::ByteArrayLiftMemory => { + instructions::byte_array_lift_memory(*instruction) + } + Instruction::ByteArrayLowerMemory => { + instructions::byte_array_lower_memory(*instruction) + } + 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::RecordLowerMemory { type_index } => { + instructions::record_lower_memory(*type_index, *instruction) + } Instruction::Dup => instructions::dup(*instruction), Instruction::Swap2 => instructions::swap2(*instruction), }) diff --git a/src/lib.rs b/src/lib.rs index d5e9ac0..35623ff 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -39,7 +39,7 @@ //! [instructions]: interpreter::Instruction #![deny( - dead_code, + // dead_code, intra_doc_link_resolution_failure, missing_docs, nonstandard_style, @@ -49,7 +49,7 @@ unused_unsafe, unused_variables )] -#![forbid(unsafe_code)] +// #![forbid(unsafe_code)] #![doc(html_favicon_url = "https://wasmer.io/static/icons/favicon.ico")] #![doc(html_logo_url = "https://github.com/wasmerio.png")] diff --git a/src/serde/de.rs b/src/serde/de.rs index af8bf44..b4e82e0 100644 --- a/src/serde/de.rs +++ b/src/serde/de.rs @@ -97,7 +97,7 @@ macro_rules! next { None => Err(DeserializeError::InputEmpty), } } - } + }; } impl<'de> Deserializer<'de> { @@ -129,6 +129,23 @@ impl<'de> Deserializer<'de> { } } + fn next_byte_array(&mut self) -> Result<&'de [u8], DeserializeError> { + match self.iterator.peek() { + Some(InterfaceValue::ByteArray(v)) => { + self.iterator.next(); + + Ok(v) + } + + Some(wrong_value) => Err(DeserializeError::TypeMismatch { + expected_type: InterfaceType::ByteArray, + received_type: (*wrong_value).into(), + }), + + None => Err(DeserializeError::InputEmpty), + } + } + next!(next_i32, I32, i32); next!(next_i64, I64, i64); } @@ -200,6 +217,7 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> { Some(InterfaceValue::F32(_)) => self.deserialize_f32(visitor), Some(InterfaceValue::F64(_)) => self.deserialize_f64(visitor), Some(InterfaceValue::String(_)) => self.deserialize_string(visitor), + 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 @@ -309,11 +327,11 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> { self.deserialize_str(visitor) } - fn deserialize_bytes(self, _visitor: V) -> Result + fn deserialize_bytes(self, visitor: V) -> Result where V: de::Visitor<'de>, { - todo!("`bytes` is not supported by WIT for the moment.") + visitor.visit_bytes(self.next_byte_array()?) } fn deserialize_byte_buf(self, _visitor: V) -> Result diff --git a/src/serde/ser.rs b/src/serde/ser.rs index 15a1fc7..84d5109 100644 --- a/src/serde/ser.rs +++ b/src/serde/ser.rs @@ -423,17 +423,21 @@ impl<'a> ser::SerializeTupleVariant for &'a mut Serializer { } } -impl <'a> ser::SerializeMap for &'a mut Serializer { +impl<'a> ser::SerializeMap for &'a mut Serializer { type Ok = (); type Error = SerializeError; - fn serialize_key(&mut self, _key: &T) -> Result<(), Self::Error> where - T: Serialize { + fn serialize_key(&mut self, _key: &T) -> Result<(), Self::Error> + where + T: Serialize, + { Ok(()) } - fn serialize_value(&mut self, value: &T) -> Result<(), Self::Error> where - T: Serialize { + fn serialize_value(&mut self, value: &T) -> Result<(), Self::Error> + where + T: Serialize, + { value.serialize(&mut **self) } diff --git a/src/types.rs b/src/types.rs index 2c0c66c..e4d6ca4 100644 --- a/src/types.rs +++ b/src/types.rs @@ -1,7 +1,7 @@ //! This module defines the WIT types. use crate::vec1::Vec1; -use serde::{Serialize, Deserialize}; +use serde::{Deserialize, Serialize}; /// Represents the types supported by WIT. #[derive(PartialEq, Debug, Clone, Serialize, Deserialize)] @@ -39,6 +39,9 @@ pub enum InterfaceType { /// A string. String, + /// A byte array. + ByteArray, + /// An `any` reference. Anyref, diff --git a/src/values.rs b/src/values.rs index 2bb871e..13156d1 100644 --- a/src/values.rs +++ b/src/values.rs @@ -46,6 +46,9 @@ pub enum InterfaceValue { /// A string. String(String), + /// A byte array. + ByteArray(Vec), + //Anyref(?), /// A 32-bits integer (as defined in WebAssembly core). I32(i32), @@ -71,6 +74,7 @@ impl From<&InterfaceValue> for InterfaceType { 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, @@ -139,6 +143,7 @@ native!(u64, U64); native!(f32, F32); native!(f64, F64); native!(String, String); +native!(Vec, ByteArray); /// Iterates over a vector of `InterfaceValues` but flatten all the /// values. So `I32(1), Record([I32(2), I32(3)]), I32(4)` will be diff --git a/src/vec1.rs b/src/vec1.rs index 2d203c2..d7d0aa9 100644 --- a/src/vec1.rs +++ b/src/vec1.rs @@ -1,15 +1,15 @@ //! `Vec1` represents a non-empty `Vec`. +use serde::{Deserialize, Serialize}; use std::{ error, fmt::{self, Debug}, ops, }; -use serde::{Serialize, Deserialize}; /// `Vec1` represents a non-empty `Vec`. It derefs to `Vec` /// directly. -#[derive(Clone, PartialEq, Serialize, Deserialize)] +#[derive(Clone, PartialEq, Serialize, Deserialize, Default)] pub struct Vec1(Vec) where T: Debug;