From 5446f3e1089a45c26bbda8df53b6d09d54e8a4f0 Mon Sep 17 00:00:00 2001 From: vms Date: Sat, 25 Jul 2020 11:01:41 +0300 Subject: [PATCH] add support of structs --- Cargo.lock | 25 + Cargo.toml | 4 +- src/decoders/binary.rs | 21 + src/decoders/wat.rs | 16 +- src/encoders/binary.rs | 8 + src/encoders/wat.rs | 6 + src/errors.rs | 7 + src/interpreter/instructions/mod.rs | 23 +- src/interpreter/instructions/records.rs | 659 ++++++++++-------- src/interpreter/instructions/records/utils.rs | 172 +++++ src/interpreter/mod.rs | 22 +- src/lib.rs | 4 +- src/serde/de.rs | 2 +- src/serde/ser.rs | 14 +- src/types.rs | 2 +- src/vec1.rs | 4 +- 16 files changed, 681 insertions(+), 308 deletions(-) create mode 100644 src/interpreter/instructions/records/utils.rs 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/decoders/binary.rs b/src/decoders/binary.rs index 4d9484c..4b21c6a 100644 --- a/src/decoders/binary.rs +++ b/src/decoders/binary.rs @@ -261,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 7e91fad..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::{ @@ -72,6 +72,8 @@ mod keyword { 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"); } @@ -349,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 cb00423..ca242c7 100644 --- a/src/encoders/binary.rs +++ b/src/encoders/binary.rs @@ -347,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 33dfd53..aa102f3 100644 --- a/src/encoders/wat.rs +++ b/src/encoders/wat.rs @@ -144,6 +144,12 @@ impl ToString for &Instruction { 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/mod.rs b/src/interpreter/instructions/mod.rs index eace0d4..63b98bd 100644 --- a/src/interpreter/instructions/mod.rs +++ b/src/interpreter/instructions/mod.rs @@ -1,10 +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::{ @@ -14,13 +14,16 @@ use crate::{ 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 { @@ -141,10 +144,10 @@ pub enum Instruction { /// The `string.size` instruction. StringSize, - /// The `string.lift_memory` instruction. + /// The `byte_array.lift_memory` instruction. ByteArrayLiftMemory, - /// The `string.lower_memory` instruction. + /// The `byte_array.lower_memory` instruction. ByteArrayLowerMemory, /// The `string.size` instruction. @@ -162,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..6e85359 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,273 @@ 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, + size: 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, +{ + let length = record_type.fields.len(); + let mut values = VecDeque::with_capacity(length); + 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.0 { + let value = data[field_id]; + match field { + InterfaceType::S8 => { + values.push_front(InterfaceValue::S8(value as _)); + } + InterfaceType::S16 => { + values.push_front(InterfaceValue::S16(value as _)); + } + InterfaceType::S32 => { + values.push_front(InterfaceValue::S32(value as _)); + } + InterfaceType::S64 => { + values.push_front(InterfaceValue::S64(value as _)); + } + InterfaceType::I32 => { + values.push_front(InterfaceValue::I32(value as _)); + } + InterfaceType::I64 => { + values.push_front(InterfaceValue::I64(value as _)); + } + InterfaceType::U8 => { + values.push_front(InterfaceValue::U8(value as _)); + } + InterfaceType::U16 => { + values.push_front(InterfaceValue::U16(value as _)); + } + InterfaceType::U32 => { + values.push_front(InterfaceValue::U32(value as _)); + } + InterfaceType::U64 => { + values.push_front(InterfaceValue::U64(value as _)); + } + InterfaceType::F32 => { + values.push_front(InterfaceValue::F32(value as _)); + } + InterfaceType::F64 => { + unsafe { + values.push_front(InterfaceValue::F64(std::mem::transmute::(value))) + }; + } + InterfaceType::Anyref => {} + InterfaceType::String => { + let offset = value; + field_id += 1; + let size = data[field_id]; + + let string_mem = + read_from_instance_mem(instance, instruction, offset as _, size as _)?; + // TODO: check + let string = String::from_utf8(string_mem).unwrap(); + + utils::deallocate(instance, instruction, offset as _, size as _)?; + + values.push_front(InterfaceValue::String(string)); + } + InterfaceType::ByteArray => { + let offset = value; + field_id += 1; + let size = data[field_id]; + + let byte_array = + read_from_instance_mem(instance, instruction, offset as _, size as _)?; + + values.push_front(InterfaceValue::ByteArray(byte_array)); + } + InterfaceType::Record(record_type) => { + let offset = value; + field_id += 1; + let size = data[field_id]; + + values.push_front(record_lift_memory_( + instance, + record_type, + offset as _, + size as _, + instruction, + )?) + } + } + field_id += 1; + } + 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(2).ok_or_else(|| { + InstructionError::new( + instruction, + InstructionErrorKind::StackIsTooSmall { needed: 2 }, + ) + })?; + + let offset: usize = to_native::(&inputs[0], instruction)? + .try_into() + .map_err(|e| (e, "offset").into()) + .map_err(|k| InstructionError::new(instruction, k))?; + let size: usize = to_native::(&inputs[1], instruction)? + .try_into() + .map_err(|e| (e, "size").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, size, 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<(i32, i32), InstructionError> +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.0 { + 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(unsafe { std::mem::transmute::(value) }) + } + InterfaceValue::String(value) => { + let string_pointer = + write_to_instance_mem(instance, instruction.clone(), value.as_bytes())?; + result.push(string_pointer as _); + result.push(value.len() as _); + } + + InterfaceValue::ByteArray(value) => { + let byte_array_pointer = + write_to_instance_mem(instance, instruction.clone(), &value)?; + result.push(byte_array_pointer as _); + result.push(value.len() as _); + } + + InterfaceValue::Record(record) => { + let (record_ptr, record_size) = + record_lower_memory_(instance, instruction, record)?; + + result.push(record_ptr as _); + result.push(record_size as _); + } + } + } + + let result = safe_transmute::transmute_to_bytes::(&result); + let result_pointer = write_to_instance_mem(instance, instruction.clone(), &result)?; + + Ok((result_pointer, result.len() as _)) +} + +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 +362,112 @@ 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, size) = + record_lower_memory_(*instance, instruction, record_values)?; + runtime.stack.push(InterfaceValue::I32(offset)); + runtime.stack.push(InterfaceValue::I32(size)); 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..9344a59 --- /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.clone(), 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.clone(), + 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.clone()) +} + +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.clone(), + 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 855119c..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,8 +237,12 @@ 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::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 } => { @@ -247,7 +250,14 @@ where } 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 ac78003..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> { 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 f223ce1..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)] diff --git a/src/vec1.rs b/src/vec1.rs index b970fd0..6ce9129 100644 --- a/src/vec1.rs +++ b/src/vec1.rs @@ -1,16 +1,16 @@ //! `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, Default)] -pub struct Vec1(Vec) +pub struct Vec1(pub(crate) Vec) where T: Debug;