diff --git a/src/decoders/binary.rs b/src/decoders/binary.rs index c7c7dd5..5fc6216 100644 --- a/src/decoders/binary.rs +++ b/src/decoders/binary.rs @@ -285,29 +285,28 @@ 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)?); diff --git a/src/decoders/wat.rs b/src/decoders/wat.rs index 265cfad..5de4da8 100644 --- a/src/decoders/wat.rs +++ b/src/decoders/wat.rs @@ -178,6 +178,7 @@ impl Parse<'_> for RecordType { } } +#[allow(clippy::suspicious_else_formatting)] impl<'a> Parse<'a> for Instruction { #[allow(clippy::cognitive_complexity)] fn parse(parser: Parser<'a>) -> Result { @@ -348,7 +349,7 @@ impl<'a> Parse<'a> for Instruction { Ok(Instruction::ByteArraySize) } - /* + /* else if lookahead.peek::() { parser.parse::()?; @@ -363,7 +364,7 @@ impl<'a> Parse<'a> for Instruction { }) } */ - else if lookahead.peek::() { + else if lookahead.peek::() { parser.parse::()?; Ok(Instruction::RecordLiftMemory { diff --git a/src/errors.rs b/src/errors.rs index 2ddbfe6..7ee4c5a 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -170,6 +170,9 @@ pub enum InstructionErrorKind { type_name: String, }, + /// Corrupted record's been popped from the stack. + CorruptedRecord(String), + /// Read a type that has an unexpected type. InvalidTypeKind { /// The expected kind. @@ -274,6 +277,13 @@ impl Display for InstructionErrorKind { "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/mod.rs b/src/interpreter/instructions/mod.rs index 219095a..909d886 100644 --- a/src/interpreter/instructions/mod.rs +++ b/src/interpreter/instructions/mod.rs @@ -8,7 +8,8 @@ mod strings; mod swap2; use crate::interpreter::wasm; -use crate::types::InterfaceType; +use crate::types::{InterfaceType, RecordType}; +use crate::vec1::Vec1; use crate::{ errors::{InstructionError, InstructionErrorKind, InstructionResult, WasmValueNativeCastError}, values::{InterfaceValue, NativeType}, @@ -169,7 +170,6 @@ pub enum Instruction { }, */ - /// The `record.lift_memory` instruction. RecordLiftMemory { /// The type index of the record. @@ -277,26 +277,7 @@ where ) })?; - if record_fields.len() != record_type.fields.len() { - return Err(InstructionError::new( - instruction, - InstructionErrorKind::InvalidValueOnTheStack { - expected_type: interface_type.clone(), - received_value: interface_value.clone(), - }, - )); - } - - 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, - )?; - } + is_record_fields_compatible_to_type(instance, record_type, record_fields, instruction)?; Ok(()) } @@ -310,6 +291,58 @@ where } } +pub(crate) fn is_record_fields_compatible_to_type< + 'instance, + Instance, + Export, + LocalImport, + Memory, + MemoryView, +>( + instance: &'instance Instance, + record_type: &RecordType, + 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, +{ + if record_fields.is_empty() { + return Err(InstructionError::new( + instruction, + InstructionErrorKind::CorruptedRecord(String::from("record contains no fields")), + )); + } + + if record_fields.len() != record_type.fields.len() { + return Err(InstructionError::new( + instruction, + InstructionErrorKind::InvalidValueOnTheStack { + expected_type: InterfaceType::Record(record_type.name.clone()), + // 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::*}; diff --git a/src/interpreter/instructions/records.rs b/src/interpreter/instructions/records.rs index 6f0dd15..6bb2113 100644 --- a/src/interpreter/instructions/records.rs +++ b/src/interpreter/instructions/records.rs @@ -3,11 +3,8 @@ 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::Instruction, types::{InterfaceType, RecordType}, @@ -287,25 +284,14 @@ where // TODO: size = 0 let instance = &runtime.wasm_instance; - let record_type = match instance.wit_type_by_id(type_index).ok_or_else(|| { + let record_type = instance.wit_record_by_id(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)?; + let record = record_lift_memory_(&**instance, record_type, offset, instruction)?; runtime.stack.push(record); Ok(()) @@ -395,40 +381,22 @@ where Box::new({ move |runtime| -> _ { let instance = &mut runtime.wasm_instance; - let record_type = match instance.wit_type_by_id(type_index).ok_or_else(|| { + let record_type = instance.wit_record_by_id(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)) => { - /* - 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)?; + Some(InterfaceValue::Record(record_fields)) => { + is_record_fields_compatible_to_type( + &**instance, + record_type, + &record_fields, + instruction, + )?; + let offset = record_lower_memory_(*instance, instruction, record_fields)?; runtime.stack.push(InterfaceValue::I32(offset)); Ok(()) diff --git a/src/interpreter/instructions/records/utils.rs b/src/interpreter/instructions/records/utils.rs index 4e99ed2..99a9f30 100644 --- a/src/interpreter/instructions/records/utils.rs +++ b/src/interpreter/instructions/records/utils.rs @@ -148,7 +148,12 @@ where ) })?; - crate::interpreter::instructions::check_function_signature(instance, local_or_import, &inputs, instruction)?; + crate::interpreter::instructions::check_function_signature( + instance, + local_or_import, + &inputs, + instruction, + )?; let outputs = local_or_import.call(&inputs).map_err(|_| { InstructionError::new( diff --git a/src/interpreter/wasm/structures.rs b/src/interpreter/wasm/structures.rs index 0893c86..d3be40e 100644 --- a/src/interpreter/wasm/structures.rs +++ b/src/interpreter/wasm/structures.rs @@ -1,7 +1,7 @@ #![allow(missing_docs)] use crate::types::RecordType; -use crate::{ast, types::InterfaceType, values::InterfaceValue}; +use crate::{types::InterfaceType, values::InterfaceValue}; use std::{cell::Cell, ops::Deref}; pub trait TypedIndex: Copy + Clone { @@ -75,7 +75,7 @@ where fn export(&self, export_name: &str) -> Option<&E>; fn local_or_import(&self, index: I) -> Option<&LI>; fn memory(&self, index: usize) -> Option<&M>; - fn wit_type_by_id(&self, index: u32) -> Option<&ast::Type>; + fn wit_record_by_id(&self, index: u32) -> Option<&RecordType>; fn wit_record_by_name(&self, name: &str) -> Option<&RecordType>; } @@ -160,7 +160,7 @@ where None } - fn wit_type_by_id(&self, _index: u32) -> Option<&ast::Type> { + fn wit_record_by_id(&self, _index: u32) -> Option<&RecordType> { None } diff --git a/src/values.rs b/src/values.rs index a705f5e..4cb952a 100644 --- a/src/values.rs +++ b/src/values.rs @@ -56,48 +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(name) => Self::Record(name.to_owned()), - } - } -} - */ - 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.