From f2adfdaea532e8aeaac56e61c47b76f3012c2320 Mon Sep 17 00:00:00 2001 From: primoly <168267431+primoly@users.noreply.github.com> Date: Thu, 9 May 2024 14:30:19 +0200 Subject: [PATCH 1/7] initial support for value sections in wasmparser --- crates/wasmparser/src/binary_reader.rs | 85 ++++++ crates/wasmparser/src/parser.rs | 26 +- crates/wasmparser/src/readers/component.rs | 2 + .../src/readers/component/values.rs | 250 ++++++++++++++++++ crates/wasmparser/src/validator.rs | 37 +++ crates/wasmparser/src/validator/component.rs | 25 +- 6 files changed, 418 insertions(+), 7 deletions(-) create mode 100644 crates/wasmparser/src/readers/component/values.rs diff --git a/crates/wasmparser/src/binary_reader.rs b/crates/wasmparser/src/binary_reader.rs index 00a417fafc..92fb1b841c 100644 --- a/crates/wasmparser/src/binary_reader.rs +++ b/crates/wasmparser/src/binary_reader.rs @@ -407,6 +407,47 @@ impl<'a> BinaryReader<'a> { BinaryReaderError::eof(self.original_position(), 1) } + /// Advances the `BinaryReader` up to four bytes to parse a variable + /// length integer as a `u16`. + /// + /// # Errors + /// + /// If `BinaryReader` has less than one or up to two bytes remaining, or + /// the integer is larger than 16 bits. + #[inline] + pub fn read_var_u16(&mut self) -> Result { + // Optimization for single byte u16. + let byte = self.read_u8()?; + if (byte & 0x80) == 0 { + Ok(u16::from(byte)) + } else { + self.read_var_u16_big(byte) + } + } + + fn read_var_u16_big(&mut self, byte: u8) -> Result { + let mut result = (byte & 0x7F) as u16; + let mut shift = 7; + loop { + let byte = self.read_u8()?; + result |= ((byte & 0x7F) as u16) << shift; + if shift >= 9 && (byte >> (16 - shift)) != 0 { + let msg = if byte & 0x80 != 0 { + "invalid var_u16: integer representation too long" + } else { + "invalid var_u16: integer too large" + }; + // The continuation bit or unused bits are set. + return Err(BinaryReaderError::new(msg, self.original_position() - 1)); + } + shift += 7; + if (byte & 0x80) == 0 { + break; + } + } + Ok(result) + } + /// Advances the `BinaryReader` up to four bytes to parse a variable /// length integer as a `u32`. /// @@ -519,6 +560,50 @@ impl<'a> BinaryReader<'a> { Ok(()) } + /// Advances the `BinaryReader` up to four bytes to parse a variable + /// length integer as a `i16`. + /// # Errors + /// If `BinaryReader` has less than one or up to two bytes remaining, or + /// the integer is larger than 16 bits. + #[inline] + pub fn read_var_i16(&mut self) -> Result { + // Optimization for single byte i16. + let byte = self.read_u8()?; + if (byte & 0x80) == 0 { + Ok(((byte as i16) << 9) >> 9) + } else { + self.read_var_i16_big(byte) + } + } + + fn read_var_i16_big(&mut self, byte: u8) -> Result { + let mut result = (byte & 0x7F) as i16; + let mut shift = 7; + loop { + let byte = self.read_u8()?; + result |= ((byte & 0x7F) as i16) << shift; + if shift >= 9 { + let continuation_bit = (byte & 0x80) != 0; + let sign_and_unused_bit = (byte << 1) as i8 >> (16 - shift); + if continuation_bit || (sign_and_unused_bit != 0 && sign_and_unused_bit != -1) { + let msg = if continuation_bit { + "invalid var_i16: integer representation too long" + } else { + "invalid var_i16: integer too large" + }; + return Err(BinaryReaderError::new(msg, self.original_position() - 1)); + } + return Ok(result); + } + shift += 7; + if (byte & 0x80) == 0 { + break; + } + } + let ashift = 16 - shift; + Ok((result << ashift) >> ashift) + } + /// Advances the `BinaryReader` up to four bytes to parse a variable /// length integer as a `i32`. /// # Errors diff --git a/crates/wasmparser/src/parser.rs b/crates/wasmparser/src/parser.rs index 75afed4239..8635e39722 100644 --- a/crates/wasmparser/src/parser.rs +++ b/crates/wasmparser/src/parser.rs @@ -4,10 +4,11 @@ use crate::CoreTypeSectionReader; use crate::{ limits::MAX_WASM_MODULE_SIZE, BinaryReader, BinaryReaderError, ComponentCanonicalSectionReader, ComponentExportSectionReader, ComponentImportSectionReader, ComponentInstanceSectionReader, - ComponentStartFunction, ComponentTypeSectionReader, CustomSectionReader, DataSectionReader, - ElementSectionReader, ExportSectionReader, FromReader, FunctionBody, FunctionSectionReader, - GlobalSectionReader, ImportSectionReader, InstanceSectionReader, MemorySectionReader, Result, - SectionLimited, TableSectionReader, TagSectionReader, TypeSectionReader, + ComponentStartFunction, ComponentTypeSectionReader, ComponentValueSectionReader, + CustomSectionReader, DataSectionReader, ElementSectionReader, ExportSectionReader, FromReader, + FunctionBody, FunctionSectionReader, GlobalSectionReader, ImportSectionReader, + InstanceSectionReader, MemorySectionReader, Result, SectionLimited, TableSectionReader, + TagSectionReader, TypeSectionReader, }; use core::fmt; use core::iter; @@ -275,6 +276,9 @@ pub enum Payload<'a> { /// A component export section was received, and the provided reader can be /// used to parse the contents of the component export section. ComponentExportSection(ComponentExportSectionReader<'a>), + /// A component value section was received, and the provided reader can be + /// used to parse the contents of the component export section. + ComponentValueSection(ComponentValueSectionReader<'a>), /// A module or component custom section was received. CustomSection(CustomSectionReader<'a>), @@ -328,6 +332,7 @@ const COMPONENT_CANONICAL_SECTION: u8 = 8; const COMPONENT_START_SECTION: u8 = 9; const COMPONENT_IMPORT_SECTION: u8 = 10; const COMPONENT_EXPORT_SECTION: u8 = 11; +const COMPONENT_VALUE_SECTION: u8 = 12; impl Parser { /// Creates a new parser. @@ -491,6 +496,7 @@ impl Parser { /// ComponentStartSection { .. } => { /* ... */ } /// ComponentImportSection(_) => { /* ... */ } /// ComponentExportSection(_) => { /* ... */ } + /// ComponentValueSection(_) => { /* ... */ } /// /// ModuleSection { parser, .. } /// | ComponentSection { parser, .. } => { @@ -748,6 +754,12 @@ impl Parser { ComponentExportSectionReader::new, ComponentExportSection, ), + (Encoding::Component, COMPONENT_VALUE_SECTION) => section( + reader, + len, + ComponentValueSectionReader::new, + ComponentValueSection, + ), (_, id) => { let offset = reader.original_position(); let contents = reader.read_bytes(len as usize)?; @@ -875,6 +887,7 @@ impl Parser { /// ComponentStartSection { .. } => { /* ... */ } /// ComponentImportSection(_) => { /* ... */ } /// ComponentExportSection(_) => { /* ... */ } + /// ComponentValueSection(_) => { /* ... */} /// /// CustomSection(_) => { /* ... */ } /// @@ -1128,6 +1141,7 @@ impl Payload<'_> { ComponentStartSection { range, .. } => Some((COMPONENT_START_SECTION, range.clone())), ComponentImportSection(s) => Some((COMPONENT_IMPORT_SECTION, s.range())), ComponentExportSection(s) => Some((COMPONENT_EXPORT_SECTION, s.range())), + ComponentValueSection(s) => Some((COMPONENT_VALUE_SECTION, s.range())), CustomSection(c) => Some((CUSTOM_SECTION, c.range())), @@ -1224,6 +1238,10 @@ impl fmt::Debug for Payload<'_> { .debug_tuple("ComponentExportSection") .field(&"...") .finish(), + ComponentValueSection(_) => f + .debug_tuple("ComponentValueSection") + .field(&"...") + .finish(), CustomSection(c) => f.debug_tuple("CustomSection").field(c).finish(), diff --git a/crates/wasmparser/src/readers/component.rs b/crates/wasmparser/src/readers/component.rs index 24b490d0c3..ccc94dc333 100644 --- a/crates/wasmparser/src/readers/component.rs +++ b/crates/wasmparser/src/readers/component.rs @@ -6,6 +6,7 @@ mod instances; mod names; mod start; mod types; +mod values; pub use self::aliases::*; pub use self::canonicals::*; @@ -15,3 +16,4 @@ pub use self::instances::*; pub use self::names::*; pub use self::start::*; pub use self::types::*; +pub use self::values::*; diff --git a/crates/wasmparser/src/readers/component/values.rs b/crates/wasmparser/src/readers/component/values.rs new file mode 100644 index 0000000000..7c2b1142e5 --- /dev/null +++ b/crates/wasmparser/src/readers/component/values.rs @@ -0,0 +1,250 @@ +use crate::prelude::*; +use crate::{ + BinaryReader, BinaryReaderError, ComponentDefinedType, ComponentType, ComponentValType, + FromReader, Ieee32, Ieee64, PrimitiveValType, Result, SectionLimited, +}; + +/// A component value with its type. +#[derive(Debug, Clone, Eq, PartialEq)] +pub struct ComponentValue<'a> { + /// The type of this value. + pub ty: ComponentValType, + bytes: &'a [u8], + original_offset: usize, +} + +impl<'a> ComponentValue<'a> { + /// A component model value. + /// This takes the types from the current components type section + /// in the same order as they where read from there. + pub fn val(&self, types: &[ComponentType]) -> Result { + read_val( + &mut BinaryReader::new_with_offset(self.bytes, self.original_offset), + self.ty, + types, + ) + } +} + +/// A component value. +#[derive(Debug, Clone, Eq, PartialEq)] +pub enum Val { + /// A boolean value. + Bool(bool), + /// A signed 8-bit integer. + S8(i8), + /// An unsigned 8-bit integer. + U8(u8), + /// An signed 16-bit integer. + S16(i16), + /// An unsigned 16-bit integer. + U16(u16), + /// A signed 32-bit integer. + S32(i32), + /// An unsigned 32-bit integer. + U32(u32), + /// A signed 64-bit integer. + S64(i64), + /// An unsigned 64-bit integer. + U64(u64), + /// A 32-bit floating point number. + F32(Ieee32), + /// A 64-bit floating point number. + F64(Ieee64), + /// A Unicode scalar value. + Char(char), + /// A Unicode string. + String(String), + /// A record. + Record(Vec), + /// A variant case. + VariantCase { + /// The label of the variant case. + label: u32, + /// The value of the variant case. + v: Option>, + }, + /// A list. + List(Vec), + /// A tuple. + Tuple(Vec), + /// A flags value. + Flags(Vec), + /// An enum case. + EnumCase(u32), + /// A none case of an option. + None, + /// A some case of an option with a given value. + Some(Box), + /// An ok case of a result with an optional value. + Ok(Option>), + /// An error case of a result with an optional value. + Error(Option>), +} + +/// A reader for the value section of a WebAssembly component. +pub type ComponentValueSectionReader<'a> = SectionLimited<'a, ComponentValue<'a>>; + +impl<'a> FromReader<'a> for ComponentValue<'a> { + fn from_reader(reader: &mut BinaryReader<'a>) -> Result { + let ty = ComponentValType::from_reader(reader)?; + let len = reader.read_var_u32()?; + let original_offset = reader.original_position(); + let bytes = reader.read_bytes(len as usize)?; + Ok(ComponentValue { + ty, + bytes, + original_offset, + }) + } +} + +fn read_val( + reader: &mut BinaryReader, + ty: ComponentValType, + types: &[ComponentType], +) -> Result { + let ty = get_defined_type(ty, types, reader.original_position())?; + match ty { + ComponentDefinedType::Primitive(prim_ty) => read_primitive_value(reader, prim_ty), + ComponentDefinedType::Record(record_ty) => { + let mut fields = Vec::with_capacity(record_ty.len()); + for field in record_ty.iter() { + fields.push(read_val(reader, field.1, types)?); + } + Ok(Val::Record(fields)) + } + ComponentDefinedType::Variant(variant_ty) => { + let label = reader.read_var_u32()?; + if label as usize >= variant_ty.len() { + bail!( + reader.original_position(), + "invalid variant case label: {label}" + ); + } + let case_ty = variant_ty[label as usize].ty; + let v = if let Some(case_ty) = case_ty { + Some(Box::new(read_val(reader, case_ty, types)?)) + } else { + None + }; + Ok(Val::VariantCase { label, v }) + } + ComponentDefinedType::List(element_ty) => { + let len = reader.read_var_u32()?; + let mut elements = Vec::with_capacity(len as usize); + for _ in 0..len { + elements.push(read_val(reader, element_ty, types)?); + } + Ok(Val::List(elements)) + } + ComponentDefinedType::Tuple(tuple_ty) => { + let mut fields = Vec::with_capacity(tuple_ty.len()); + for field_ty in tuple_ty.iter() { + fields.push(read_val(reader, *field_ty, types)?); + } + Ok(Val::Tuple(fields)) + } + ComponentDefinedType::Flags(flags_ty) => Ok(Val::Flags({ + let mut value = vec![false; flags_ty.len()]; + let n = reader.read_var_u64()?; + for (i, field) in value.iter_mut().enumerate() { + if ((n >> (i as u64)) & 1) == 1 { + *field = true; + } + } + value + })), + ComponentDefinedType::Enum(enum_ty) => Ok(Val::EnumCase({ + let label = reader.read_var_u32()?; + if label as usize >= enum_ty.len() { + bail!( + reader.original_position(), + "invalid enum case label: {label}" + ); + } + label + })), + ComponentDefinedType::Option(option_ty) => Ok(match reader.read_u8()? { + 0x0 => Val::None, + 0x1 => Val::Some(Box::new(read_val(reader, option_ty, types)?)), + x => return reader.invalid_leading_byte(x, "invalid option label"), + }), + ComponentDefinedType::Result { + ok: ok_ty, + err: err_ty, + } => { + let label = reader.read_u8()?; + Ok(match label { + 0x0 => Val::Ok(if let Some(ok_ty) = ok_ty { + Some(Box::new(read_val(reader, ok_ty, types)?)) + } else { + None + }), + 0x1 => Val::Error(if let Some(err_ty) = err_ty { + Some(Box::new(read_val(reader, err_ty, types)?)) + } else { + None + }), + x => return reader.invalid_leading_byte(x, "invalid result label"), + }) + } + ComponentDefinedType::Own(_) | ComponentDefinedType::Borrow(_) => { + Err(BinaryReaderError::new( + "resource handles not supported in value section", + reader.original_position(), + )) + } + } +} + +fn read_primitive_value(reader: &mut BinaryReader, ty: PrimitiveValType) -> Result { + Ok(match ty { + PrimitiveValType::Bool => Val::Bool(match reader.read_u8()? { + 0 => false, + 1 => true, + n => bail!(reader.original_position(), "invalid bool value: {n}"), + }), + PrimitiveValType::S8 => Val::S8(reader.read_u8()? as i8), + PrimitiveValType::U8 => Val::U8(reader.read_u8()?), + PrimitiveValType::S16 => Val::S16(reader.read_var_i16()?), + PrimitiveValType::U16 => Val::U16(reader.read_var_u16()?), + PrimitiveValType::S32 => Val::S32(reader.read_var_i32()?), + PrimitiveValType::U32 => Val::U32(reader.read_var_u32()?), + PrimitiveValType::S64 => Val::S64(reader.read_var_i64()?), + PrimitiveValType::U64 => Val::U64(reader.read_var_u64()?), + PrimitiveValType::F32 => Val::F32({ + let value = reader.read_f32()?; + if f32::from_bits(value.0).is_nan() && value.0 != 0x7f_c0_00_00 { + bail!(reader.original_position(), "invalid f32: non canonical NaN"); + } + value + }), + PrimitiveValType::F64 => Val::F64({ + let value = reader.read_f64()?; + if f64::from_bits(value.0).is_nan() && value.0 != 0x7f_f8_00_00_00_00_00_00 { + bail!(reader.original_position(), "invalid f64: non canonical NaN"); + } + value + }), + PrimitiveValType::Char => Val::Char(char::from_u32(reader.read_var_u32()?).ok_or( + BinaryReaderError::new("invalid Unicode scalar value", reader.original_position()), + )?), + PrimitiveValType::String => Val::String(reader.read_string()?.into()), + }) +} + +fn get_defined_type<'a>( + ty: ComponentValType, + types: &[ComponentType<'a>], + offset: usize, +) -> Result> { + match ty { + ComponentValType::Primitive(prim_ty) => Ok(ComponentDefinedType::Primitive(prim_ty)), + ComponentValType::Type(idx) => match types.get(idx as usize) { + Some(ComponentType::Defined(cdt)) => Ok(cdt.clone()), + Some(_) => bail!(offset, "not a component defined type at index {idx}"), + None => bail!(offset, "type index out of bounds: {idx}"), + }, + } +} diff --git a/crates/wasmparser/src/validator.rs b/crates/wasmparser/src/validator.rs index 75e5798032..3e26ef6318 100644 --- a/crates/wasmparser/src/validator.rs +++ b/crates/wasmparser/src/validator.rs @@ -728,6 +728,7 @@ impl Validator { ComponentStartSection { start, range } => self.component_start_section(start, range)?, ComponentImportSection(s) => self.component_import_section(s)?, ComponentExportSection(s) => self.component_export_section(s)?, + ComponentValueSection(s) => self.component_value_section(s)?, End(offset) => return Ok(ValidPayload::End(self.end(*offset)?)), @@ -1455,6 +1456,42 @@ impl Validator { ) } + /// Validates [`Payload::ComponentValueSection`](crate::Payload). + /// + /// This method should only be called when parsing a component. + pub fn component_value_section( + &mut self, + section: &crate::ComponentValueSectionReader, + ) -> Result<()> { + if !self.features.contains(WasmFeatures::COMPONENT_MODEL_VALUES) { + bail!( + section.range().start, + "support for component model `value`s is not enabled" + ); + } + self.process_component_section( + section, + "value", + |components, _types, count, offset| { + let current = components.last_mut().unwrap(); + check_max( + current.values.len(), + count, + MAX_WASM_VALUES, + "values", + offset, + )?; + current.values.reserve(count as usize); + Ok(()) + }, + |components, types, features, value, offset| { + let current = components.last_mut().unwrap(); + current.add_value(value, features, types, offset)?; + Ok(()) + }, + ) + } + /// Validates [`Payload::UnknownSection`](crate::Payload). /// /// Currently always returns an error. diff --git a/crates/wasmparser/src/validator/component.rs b/crates/wasmparser/src/validator/component.rs index 13f13a497a..9284bdbbc6 100644 --- a/crates/wasmparser/src/validator/component.rs +++ b/crates/wasmparser/src/validator/component.rs @@ -20,9 +20,9 @@ use crate::{ TupleType, TypeInfo, VariantType, }, BinaryReaderError, CanonicalOption, ComponentExportName, ComponentExternalKind, - ComponentOuterAliasKind, ComponentTypeRef, CompositeType, ExternalKind, FuncType, GlobalType, - InstantiationArgKind, MemoryType, RecGroup, Result, SubType, TableType, TypeBounds, ValType, - WasmFeatures, + ComponentOuterAliasKind, ComponentTypeRef, ComponentValue, CompositeType, ExternalKind, + FuncType, GlobalType, InstantiationArgKind, MemoryType, RecGroup, Result, SubType, TableType, + TypeBounds, ValType, WasmFeatures, }; use core::mem; use indexmap::map::Entry; @@ -1242,6 +1242,25 @@ impl ComponentState { Ok(()) } + pub fn add_value( + &mut self, + value: ComponentValue, + features: &WasmFeatures, + types: &mut TypeList, + offset: usize, + ) -> Result<()> { + if !features.contains(WasmFeatures::COMPONENT_MODEL_VALUES) { + bail!( + offset, + "support for component model `value`s is not enabled" + ); + } + + let ty = self.create_component_val_type(value.ty, offset)?; + self.values.push((ty, false)); + Ok(()) + } + fn check_options( &self, core_ty: Option<&FuncType>, From 1b0d244d0f4ef8c4faaf4b1befa667077601b4eb Mon Sep 17 00:00:00 2001 From: primoly <168267431+primoly@users.noreply.github.com> Date: Sat, 11 May 2024 01:12:07 +0200 Subject: [PATCH 2/7] visitor Switch to `types::TypesRef` and using the visitor pattern for value parsing. --- .../src/readers/component/values.rs | 293 +++++++++++------- 1 file changed, 186 insertions(+), 107 deletions(-) diff --git a/crates/wasmparser/src/readers/component/values.rs b/crates/wasmparser/src/readers/component/values.rs index 7c2b1142e5..c188eb5e43 100644 --- a/crates/wasmparser/src/readers/component/values.rs +++ b/crates/wasmparser/src/readers/component/values.rs @@ -1,14 +1,15 @@ use crate::prelude::*; +use crate::types::{ComponentDefinedType, ComponentValType, TypesRef}; use crate::{ - BinaryReader, BinaryReaderError, ComponentDefinedType, ComponentType, ComponentValType, - FromReader, Ieee32, Ieee64, PrimitiveValType, Result, SectionLimited, + BinaryReader, BinaryReaderError, FromReader, Ieee32, Ieee64, PrimitiveValType, Result, + SectionLimited, }; /// A component value with its type. #[derive(Debug, Clone, Eq, PartialEq)] pub struct ComponentValue<'a> { /// The type of this value. - pub ty: ComponentValType, + pub ty: crate::ComponentValType, bytes: &'a [u8], original_offset: usize, } @@ -17,18 +18,28 @@ impl<'a> ComponentValue<'a> { /// A component model value. /// This takes the types from the current components type section /// in the same order as they where read from there. - pub fn val(&self, types: &[ComponentType]) -> Result { + pub fn val(&self, types: &TypesRef, visitor: V) -> Result<()> + where + V: Val, + { + let ty = match self.ty { + crate::ComponentValType::Primitive(prim_ty) => ComponentValType::Primitive(prim_ty), + crate::ComponentValType::Type(idx) => { + ComponentValType::Type(types.component_defined_type_at(idx)) + } + }; read_val( &mut BinaryReader::new_with_offset(self.bytes, self.original_offset), - self.ty, + ty, types, + visitor, ) } } -/// A component value. +/// A primitive value. #[derive(Debug, Clone, Eq, PartialEq)] -pub enum Val { +pub enum PrimitiveValue { /// A boolean value. Bool(bool), /// A signed 8-bit integer. @@ -55,31 +66,78 @@ pub enum Val { Char(char), /// A Unicode string. String(String), +} + +/// A value visitor. +pub trait Val: Sized { + /// A record visitor. + type R: Record; + /// A list visitor. + type L: List; + /// A tuple visitor. + type T: Tuple; + /// A flags visitor. + type F: Flags; + /// A primitive value. + fn primitive(self, v: PrimitiveValue); /// A record. - Record(Vec), - /// A variant case. - VariantCase { - /// The label of the variant case. - label: u32, - /// The value of the variant case. - v: Option>, - }, + fn record(self, length: u32) -> Self::R; + /// A variant case with a given value. + fn variant_case(self, label_index: u32, name: &str) -> Self; + /// A variant case without a value. + fn variant_case_empty(self, index: u32, name: &str); /// A list. - List(Vec), + fn list(self, length: u32) -> Self::L; /// A tuple. - Tuple(Vec), + fn tuple(self, length: u32) -> Self::T; /// A flags value. - Flags(Vec), + fn flags(self, length: u32) -> Self::F; /// An enum case. - EnumCase(u32), + fn enum_case(self, label_index: u32, name: &str); /// A none case of an option. - None, + fn none(self); /// A some case of an option with a given value. - Some(Box), - /// An ok case of a result with an optional value. - Ok(Option>), - /// An error case of a result with an optional value. - Error(Option>), + fn some(self) -> Self; + /// An ok case of a result with a given value. + fn ok(self) -> Self; + /// An ok case of a result without a value. + fn ok_empty(self); + /// An error case of a result with a given value. + fn error(self) -> Self; + /// An error case of a result without a given value. + fn error_empty(self); +} + +/// A visitor for record fields. +pub trait Record: Sized { + /// Visitor for the next record field. + fn field(&mut self, name: &str) -> V; + /// No more fields. + fn end(self); +} + +/// A visitor for list elements. +pub trait List: Sized { + /// Visitor for the next list element. + fn element(&mut self) -> V; + /// No more elements. + fn end(self); +} + +/// A visitor for tuple fields. +pub trait Tuple: Sized { + /// Visitor for the next tuple field. + fn field(&mut self) -> V; + /// No more fields. + fn end(self); +} + +/// A visitor for flags fields. +pub trait Flags: Sized { + /// Visitor for the next flags field. + fn field(&mut self, name: &str, val: bool); + /// No more fields. + fn end(self); } /// A reader for the value section of a WebAssembly component. @@ -87,7 +145,7 @@ pub type ComponentValueSectionReader<'a> = SectionLimited<'a, ComponentValue<'a> impl<'a> FromReader<'a> for ComponentValue<'a> { fn from_reader(reader: &mut BinaryReader<'a>) -> Result { - let ty = ComponentValType::from_reader(reader)?; + let ty = crate::ComponentValType::from_reader(reader)?; let len = reader.read_var_u32()?; let original_offset = reader.original_position(); let bytes = reader.read_bytes(len as usize)?; @@ -99,63 +157,71 @@ impl<'a> FromReader<'a> for ComponentValue<'a> { } } -fn read_val( +fn read_val( reader: &mut BinaryReader, ty: ComponentValType, - types: &[ComponentType], -) -> Result { + types: &TypesRef, + visitor: V, +) -> Result<()> +where + V: Val, +{ let ty = get_defined_type(ty, types, reader.original_position())?; match ty { - ComponentDefinedType::Primitive(prim_ty) => read_primitive_value(reader, prim_ty), + ComponentDefinedType::Primitive(prim_ty) => { + visitor.primitive(read_primitive_value(reader, prim_ty)?); + } ComponentDefinedType::Record(record_ty) => { - let mut fields = Vec::with_capacity(record_ty.len()); - for field in record_ty.iter() { - fields.push(read_val(reader, field.1, types)?); + let mut record = visitor.record(record_ty.fields.len() as u32); + for field in record_ty.fields.iter() { + read_val(reader, *field.1, types, record.field(field.0))?; } - Ok(Val::Record(fields)) + record.end(); } ComponentDefinedType::Variant(variant_ty) => { let label = reader.read_var_u32()?; - if label as usize >= variant_ty.len() { + if label as usize >= variant_ty.cases.len() { bail!( reader.original_position(), "invalid variant case label: {label}" ); } - let case_ty = variant_ty[label as usize].ty; - let v = if let Some(case_ty) = case_ty { - Some(Box::new(read_val(reader, case_ty, types)?)) + let case_ty = variant_ty.cases[label as usize].ty; + if let Some(case_ty) = case_ty { + read_val(reader, case_ty, types, visitor.some())?; } else { - None + visitor.none(); }; - Ok(Val::VariantCase { label, v }) } ComponentDefinedType::List(element_ty) => { let len = reader.read_var_u32()?; - let mut elements = Vec::with_capacity(len as usize); + let mut list = visitor.list(len); for _ in 0..len { - elements.push(read_val(reader, element_ty, types)?); + read_val(reader, element_ty, types, list.element())?; } - Ok(Val::List(elements)) + list.end(); } ComponentDefinedType::Tuple(tuple_ty) => { - let mut fields = Vec::with_capacity(tuple_ty.len()); - for field_ty in tuple_ty.iter() { - fields.push(read_val(reader, *field_ty, types)?); + let mut tuple = visitor.tuple(tuple_ty.types.len() as u32); + for field_ty in tuple_ty.types.iter() { + read_val(reader, *field_ty, types, tuple.field())?; } - Ok(Val::Tuple(fields)) + tuple.end(); } - ComponentDefinedType::Flags(flags_ty) => Ok(Val::Flags({ - let mut value = vec![false; flags_ty.len()]; + ComponentDefinedType::Flags(flags_ty) => { let n = reader.read_var_u64()?; - for (i, field) in value.iter_mut().enumerate() { - if ((n >> (i as u64)) & 1) == 1 { - *field = true; - } + let mut flags = visitor.flags(flags_ty.len() as u32); + for i in 0..flags_ty.len() { + let v = if ((n >> (i as u64)) & 1) == 1 { + true + } else { + false + }; + flags.field(flags_ty.get_index(i).unwrap(), v); } - value - })), - ComponentDefinedType::Enum(enum_ty) => Ok(Val::EnumCase({ + flags.end(); + } + ComponentDefinedType::Enum(enum_ty) => { let label = reader.read_var_u32()?; if label as usize >= enum_ty.len() { bail!( @@ -163,88 +229,101 @@ fn read_val( "invalid enum case label: {label}" ); } - label - })), - ComponentDefinedType::Option(option_ty) => Ok(match reader.read_u8()? { - 0x0 => Val::None, - 0x1 => Val::Some(Box::new(read_val(reader, option_ty, types)?)), + visitor.enum_case(label, enum_ty.get_index(label as usize).unwrap()); + } + ComponentDefinedType::Option(option_ty) => match reader.read_u8()? { + 0x0 => { + visitor.none(); + } + 0x1 => { + read_val(reader, option_ty, types, visitor.some())?; + } x => return reader.invalid_leading_byte(x, "invalid option label"), - }), + }, ComponentDefinedType::Result { ok: ok_ty, err: err_ty, } => { let label = reader.read_u8()?; - Ok(match label { - 0x0 => Val::Ok(if let Some(ok_ty) = ok_ty { - Some(Box::new(read_val(reader, ok_ty, types)?)) - } else { - None - }), - 0x1 => Val::Error(if let Some(err_ty) = err_ty { - Some(Box::new(read_val(reader, err_ty, types)?)) - } else { - None - }), + match label { + 0x0 => { + if let Some(ok_ty) = ok_ty { + read_val(reader, ok_ty, types, visitor.ok())?; + } else { + visitor.ok_empty(); + } + } + 0x1 => { + if let Some(err_ty) = err_ty { + read_val(reader, err_ty, types, visitor.error())?; + } else { + visitor.error_empty(); + } + } x => return reader.invalid_leading_byte(x, "invalid result label"), - }) + } } ComponentDefinedType::Own(_) | ComponentDefinedType::Borrow(_) => { - Err(BinaryReaderError::new( - "resource handles not supported in value section", + bail!( reader.original_position(), - )) + "resource handles not supported in value section" + ) } } + Ok(()) } -fn read_primitive_value(reader: &mut BinaryReader, ty: PrimitiveValType) -> Result { +fn read_primitive_value(reader: &mut BinaryReader, ty: PrimitiveValType) -> Result { Ok(match ty { - PrimitiveValType::Bool => Val::Bool(match reader.read_u8()? { - 0 => false, - 1 => true, - n => bail!(reader.original_position(), "invalid bool value: {n}"), + PrimitiveValType::Bool => PrimitiveValue::Bool(match reader.read_u8()? { + 0x0 => false, + 0x1 => true, + x => return reader.invalid_leading_byte(x, "invalid bool value: {n}"), }), - PrimitiveValType::S8 => Val::S8(reader.read_u8()? as i8), - PrimitiveValType::U8 => Val::U8(reader.read_u8()?), - PrimitiveValType::S16 => Val::S16(reader.read_var_i16()?), - PrimitiveValType::U16 => Val::U16(reader.read_var_u16()?), - PrimitiveValType::S32 => Val::S32(reader.read_var_i32()?), - PrimitiveValType::U32 => Val::U32(reader.read_var_u32()?), - PrimitiveValType::S64 => Val::S64(reader.read_var_i64()?), - PrimitiveValType::U64 => Val::U64(reader.read_var_u64()?), - PrimitiveValType::F32 => Val::F32({ + PrimitiveValType::S8 => PrimitiveValue::S8(reader.read_u8()? as i8), + PrimitiveValType::U8 => PrimitiveValue::U8(reader.read_u8()?), + PrimitiveValType::S16 => PrimitiveValue::S16(reader.read_var_i16()?), + PrimitiveValType::U16 => PrimitiveValue::U16(reader.read_var_u16()?), + PrimitiveValType::S32 => PrimitiveValue::S32(reader.read_var_i32()?), + PrimitiveValType::U32 => PrimitiveValue::U32(reader.read_var_u32()?), + PrimitiveValType::S64 => PrimitiveValue::S64(reader.read_var_i64()?), + PrimitiveValType::U64 => PrimitiveValue::U64(reader.read_var_u64()?), + PrimitiveValType::F32 => PrimitiveValue::F32({ let value = reader.read_f32()?; if f32::from_bits(value.0).is_nan() && value.0 != 0x7f_c0_00_00 { bail!(reader.original_position(), "invalid f32: non canonical NaN"); } value }), - PrimitiveValType::F64 => Val::F64({ + PrimitiveValType::F64 => PrimitiveValue::F64({ let value = reader.read_f64()?; if f64::from_bits(value.0).is_nan() && value.0 != 0x7f_f8_00_00_00_00_00_00 { bail!(reader.original_position(), "invalid f64: non canonical NaN"); } value }), - PrimitiveValType::Char => Val::Char(char::from_u32(reader.read_var_u32()?).ok_or( - BinaryReaderError::new("invalid Unicode scalar value", reader.original_position()), - )?), - PrimitiveValType::String => Val::String(reader.read_string()?.into()), + PrimitiveValType::Char => { + PrimitiveValue::Char(char::from_u32(reader.read_var_u32()?).ok_or( + BinaryReaderError::new("invalid Unicode scalar value", reader.original_position()), + )?) + } + PrimitiveValType::String => PrimitiveValue::String(reader.read_string()?.into()), }) } -fn get_defined_type<'a>( +fn get_defined_type( ty: ComponentValType, - types: &[ComponentType<'a>], + types: &TypesRef, offset: usize, -) -> Result> { - match ty { - ComponentValType::Primitive(prim_ty) => Ok(ComponentDefinedType::Primitive(prim_ty)), - ComponentValType::Type(idx) => match types.get(idx as usize) { - Some(ComponentType::Defined(cdt)) => Ok(cdt.clone()), - Some(_) => bail!(offset, "not a component defined type at index {idx}"), - None => bail!(offset, "type index out of bounds: {idx}"), - }, - } +) -> Result { + Ok(match ty { + ComponentValType::Primitive(prim_ty) => ComponentDefinedType::Primitive(prim_ty), + ComponentValType::Type(id) => { + if let Some(def_ty) = types.get(id) { + def_ty.clone() + } else { + bail!(offset, "invalid type"); + } + } + }) } From 8dd7428a69a23a28f6604f8080c8863eb026b786 Mon Sep 17 00:00:00 2001 From: primoly <168267431+primoly@users.noreply.github.com> Date: Sat, 11 May 2024 13:42:54 +0200 Subject: [PATCH 3/7] Fix variant case parsing --- .../src/readers/component/values.rs | 40 ++++++++++--------- crates/wasmparser/src/validator/component.rs | 2 +- 2 files changed, 22 insertions(+), 20 deletions(-) diff --git a/crates/wasmparser/src/readers/component/values.rs b/crates/wasmparser/src/readers/component/values.rs index c188eb5e43..5fb2dd2a35 100644 --- a/crates/wasmparser/src/readers/component/values.rs +++ b/crates/wasmparser/src/readers/component/values.rs @@ -1,4 +1,3 @@ -use crate::prelude::*; use crate::types::{ComponentDefinedType, ComponentValType, TypesRef}; use crate::{ BinaryReader, BinaryReaderError, FromReader, Ieee32, Ieee64, PrimitiveValType, Result, @@ -18,7 +17,7 @@ impl<'a> ComponentValue<'a> { /// A component model value. /// This takes the types from the current components type section /// in the same order as they where read from there. - pub fn val(&self, types: &TypesRef, visitor: V) -> Result<()> + pub fn val(&self, types: TypesRef, visitor: V) -> Result<()> where V: Val, { @@ -39,7 +38,7 @@ impl<'a> ComponentValue<'a> { /// A primitive value. #[derive(Debug, Clone, Eq, PartialEq)] -pub enum PrimitiveValue { +pub enum PrimitiveValue<'a> { /// A boolean value. Bool(bool), /// A signed 8-bit integer. @@ -65,7 +64,7 @@ pub enum PrimitiveValue { /// A Unicode scalar value. Char(char), /// A Unicode string. - String(String), + String(&'a str), } /// A value visitor. @@ -85,7 +84,7 @@ pub trait Val: Sized { /// A variant case with a given value. fn variant_case(self, label_index: u32, name: &str) -> Self; /// A variant case without a value. - fn variant_case_empty(self, index: u32, name: &str); + fn variant_case_empty(self, label_index: u32, name: &str); /// A list. fn list(self, length: u32) -> Self::L; /// A tuple. @@ -134,7 +133,7 @@ pub trait Tuple: Sized { /// A visitor for flags fields. pub trait Flags: Sized { - /// Visitor for the next flags field. + /// The next flags field. fn field(&mut self, name: &str, val: bool); /// No more fields. fn end(self); @@ -160,7 +159,7 @@ impl<'a> FromReader<'a> for ComponentValue<'a> { fn read_val( reader: &mut BinaryReader, ty: ComponentValType, - types: &TypesRef, + types: TypesRef, visitor: V, ) -> Result<()> where @@ -173,25 +172,25 @@ where } ComponentDefinedType::Record(record_ty) => { let mut record = visitor.record(record_ty.fields.len() as u32); - for field in record_ty.fields.iter() { - read_val(reader, *field.1, types, record.field(field.0))?; + for (name, field_ty) in record_ty.fields.iter() { + read_val(reader, *field_ty, types, record.field(name))?; } record.end(); } ComponentDefinedType::Variant(variant_ty) => { let label = reader.read_var_u32()?; - if label as usize >= variant_ty.cases.len() { + if let Some((name, case_ty)) = variant_ty.cases.get_index(label as usize) { + if let Some(case_ty) = case_ty.ty { + read_val(reader, case_ty, types, visitor.variant_case(label, name))?; + } else { + visitor.variant_case_empty(label, name); + } + } else { bail!( reader.original_position(), "invalid variant case label: {label}" ); } - let case_ty = variant_ty.cases[label as usize].ty; - if let Some(case_ty) = case_ty { - read_val(reader, case_ty, types, visitor.some())?; - } else { - visitor.none(); - }; } ComponentDefinedType::List(element_ty) => { let len = reader.read_var_u32()?; @@ -273,7 +272,10 @@ where Ok(()) } -fn read_primitive_value(reader: &mut BinaryReader, ty: PrimitiveValType) -> Result { +fn read_primitive_value<'a>( + reader: &'a mut BinaryReader, + ty: PrimitiveValType, +) -> Result> { Ok(match ty { PrimitiveValType::Bool => PrimitiveValue::Bool(match reader.read_u8()? { 0x0 => false, @@ -307,13 +309,13 @@ fn read_primitive_value(reader: &mut BinaryReader, ty: PrimitiveValType) -> Resu BinaryReaderError::new("invalid Unicode scalar value", reader.original_position()), )?) } - PrimitiveValType::String => PrimitiveValue::String(reader.read_string()?.into()), + PrimitiveValType::String => PrimitiveValue::String(reader.read_string()?), }) } fn get_defined_type( ty: ComponentValType, - types: &TypesRef, + types: TypesRef, offset: usize, ) -> Result { Ok(match ty { diff --git a/crates/wasmparser/src/validator/component.rs b/crates/wasmparser/src/validator/component.rs index 9284bdbbc6..d522b4fde4 100644 --- a/crates/wasmparser/src/validator/component.rs +++ b/crates/wasmparser/src/validator/component.rs @@ -1246,7 +1246,7 @@ impl ComponentState { &mut self, value: ComponentValue, features: &WasmFeatures, - types: &mut TypeList, + _types: &mut TypeList, offset: usize, ) -> Result<()> { if !features.contains(WasmFeatures::COMPONENT_MODEL_VALUES) { From c7cdcfb5355957730e6c1df62a0f8f7fa195735d Mon Sep 17 00:00:00 2001 From: primoly <168267431+primoly@users.noreply.github.com> Date: Sun, 12 May 2024 02:00:42 +0200 Subject: [PATCH 4/7] validation --- .../src/readers/component/values.rs | 104 ++++++++++++++++-- crates/wasmparser/src/validator.rs | 8 +- crates/wasmparser/src/validator/component.rs | 2 - 3 files changed, 98 insertions(+), 16 deletions(-) diff --git a/crates/wasmparser/src/readers/component/values.rs b/crates/wasmparser/src/readers/component/values.rs index 5fb2dd2a35..1f14834916 100644 --- a/crates/wasmparser/src/readers/component/values.rs +++ b/crates/wasmparser/src/readers/component/values.rs @@ -76,7 +76,7 @@ pub trait Val: Sized { /// A tuple visitor. type T: Tuple; /// A flags visitor. - type F: Flags; + type F: Flags; /// A primitive value. fn primitive(self, v: PrimitiveValue); /// A record. @@ -132,13 +132,97 @@ pub trait Tuple: Sized { } /// A visitor for flags fields. -pub trait Flags: Sized { +pub trait Flags: Sized { /// The next flags field. - fn field(&mut self, name: &str, val: bool); + fn field(&mut self, name: &str, v: bool); /// No more fields. fn end(self); } +/// A val visitor intended for validation. +pub struct VacuousVisitor(); + +impl Val for VacuousVisitor { + type R = Self; + type T = Self; + type L = Self; + type F = Self; + + fn primitive(self, _v: PrimitiveValue) {} + + fn record(self, _length: u32) -> Self::R { + VacuousVisitor() + } + + fn variant_case(self, _label_index: u32, _name: &str) -> Self { + VacuousVisitor() + } + + fn variant_case_empty(self, _label_index: u32, _name: &str) {} + + fn list(self, _length: u32) -> Self::L { + VacuousVisitor() + } + + fn tuple(self, _length: u32) -> Self::T { + VacuousVisitor() + } + + fn flags(self, _length: u32) -> Self::F { + VacuousVisitor() + } + + fn enum_case(self, _label_index: u32, _name: &str) {} + + fn none(self) {} + + fn some(self) -> Self { + VacuousVisitor() + } + + fn ok(self) -> Self { + VacuousVisitor() + } + + fn ok_empty(self) {} + + fn error(self) -> Self { + VacuousVisitor() + } + + fn error_empty(self) {} +} + +impl Record for VacuousVisitor { + fn field(&mut self, _name: &str) -> VacuousVisitor { + VacuousVisitor() + } + + fn end(self) {} +} + +impl List for VacuousVisitor { + fn element(&mut self) -> VacuousVisitor { + VacuousVisitor() + } + + fn end(self) {} +} + +impl Tuple for VacuousVisitor { + fn field(&mut self) -> VacuousVisitor { + VacuousVisitor() + } + + fn end(self) {} +} + +impl Flags for VacuousVisitor { + fn field(&mut self, _name: &str, _v: bool) {} + + fn end(self) {} +} + /// A reader for the value section of a WebAssembly component. pub type ComponentValueSectionReader<'a> = SectionLimited<'a, ComponentValue<'a>>; @@ -210,25 +294,21 @@ where ComponentDefinedType::Flags(flags_ty) => { let n = reader.read_var_u64()?; let mut flags = visitor.flags(flags_ty.len() as u32); - for i in 0..flags_ty.len() { - let v = if ((n >> (i as u64)) & 1) == 1 { - true - } else { - false - }; - flags.field(flags_ty.get_index(i).unwrap(), v); + for (i, name) in flags_ty.iter().enumerate() { + flags.field(name, ((n >> (i as u64)) & 1) == 1); } flags.end(); } ComponentDefinedType::Enum(enum_ty) => { let label = reader.read_var_u32()?; - if label as usize >= enum_ty.len() { + if let Some(name) = enum_ty.get_index(label as usize) { + visitor.enum_case(label, name); + } else { bail!( reader.original_position(), "invalid enum case label: {label}" ); } - visitor.enum_case(label, enum_ty.get_index(label as usize).unwrap()); } ComponentDefinedType::Option(option_ty) => match reader.read_u8()? { 0x0 => { diff --git a/crates/wasmparser/src/validator.rs b/crates/wasmparser/src/validator.rs index 3e26ef6318..5d2c5cea6a 100644 --- a/crates/wasmparser/src/validator.rs +++ b/crates/wasmparser/src/validator.rs @@ -16,7 +16,8 @@ use crate::prelude::*; use crate::{ limits::*, BinaryReaderError, Encoding, FromReader, FunctionBody, HeapType, Parser, Payload, - RefType, Result, SectionLimited, ValType, WASM_COMPONENT_VERSION, WASM_MODULE_VERSION, + RefType, Result, SectionLimited, VacuousVisitor, ValType, WASM_COMPONENT_VERSION, + WASM_MODULE_VERSION, }; use ::core::mem; use ::core::ops::Range; @@ -1469,6 +1470,7 @@ impl Validator { "support for component model `value`s is not enabled" ); } + let validator_id = self.id; self.process_component_section( section, "value", @@ -1486,7 +1488,9 @@ impl Validator { }, |components, types, features, value, offset| { let current = components.last_mut().unwrap(); - current.add_value(value, features, types, offset)?; + let types = TypesRef::from_component(validator_id, types, current); + value.val(types, VacuousVisitor())?; + current.add_value(value, features, offset)?; Ok(()) }, ) diff --git a/crates/wasmparser/src/validator/component.rs b/crates/wasmparser/src/validator/component.rs index d522b4fde4..1708829ab0 100644 --- a/crates/wasmparser/src/validator/component.rs +++ b/crates/wasmparser/src/validator/component.rs @@ -1246,7 +1246,6 @@ impl ComponentState { &mut self, value: ComponentValue, features: &WasmFeatures, - _types: &mut TypeList, offset: usize, ) -> Result<()> { if !features.contains(WasmFeatures::COMPONENT_MODEL_VALUES) { @@ -1255,7 +1254,6 @@ impl ComponentState { "support for component model `value`s is not enabled" ); } - let ty = self.create_component_val_type(value.ty, offset)?; self.values.push((ty, false)); Ok(()) From e0cbf6185a8fc3efe90aa405f010f3f6c7fd3cb2 Mon Sep 17 00:00:00 2001 From: primoly <168267431+primoly@users.noreply.github.com> Date: Sun, 12 May 2024 18:29:18 +0200 Subject: [PATCH 5/7] cleanup --- .../src/readers/component/values.rs | 41 ++++++++----------- crates/wasmparser/src/validator.rs | 2 +- 2 files changed, 18 insertions(+), 25 deletions(-) diff --git a/crates/wasmparser/src/readers/component/values.rs b/crates/wasmparser/src/readers/component/values.rs index 1f14834916..48b4fb4740 100644 --- a/crates/wasmparser/src/readers/component/values.rs +++ b/crates/wasmparser/src/readers/component/values.rs @@ -14,13 +14,9 @@ pub struct ComponentValue<'a> { } impl<'a> ComponentValue<'a> { - /// A component model value. - /// This takes the types from the current components type section - /// in the same order as they where read from there. - pub fn val(&self, types: TypesRef, visitor: V) -> Result<()> - where - V: Val, - { + /// Visits a component model value. + /// Expects the types from the component it belongs to. + pub fn val(&self, types: TypesRef, visitor: V) -> Result<()> { let ty = match self.ty { crate::ComponentValType::Primitive(prim_ty) => ComponentValType::Primitive(prim_ty), crate::ComponentValType::Type(idx) => { @@ -140,7 +136,7 @@ pub trait Flags: Sized { } /// A val visitor intended for validation. -pub struct VacuousVisitor(); +pub struct VacuousVisitor; impl Val for VacuousVisitor { type R = Self; @@ -151,25 +147,25 @@ impl Val for VacuousVisitor { fn primitive(self, _v: PrimitiveValue) {} fn record(self, _length: u32) -> Self::R { - VacuousVisitor() + VacuousVisitor } fn variant_case(self, _label_index: u32, _name: &str) -> Self { - VacuousVisitor() + VacuousVisitor } fn variant_case_empty(self, _label_index: u32, _name: &str) {} fn list(self, _length: u32) -> Self::L { - VacuousVisitor() + VacuousVisitor } fn tuple(self, _length: u32) -> Self::T { - VacuousVisitor() + VacuousVisitor } fn flags(self, _length: u32) -> Self::F { - VacuousVisitor() + VacuousVisitor } fn enum_case(self, _label_index: u32, _name: &str) {} @@ -177,17 +173,17 @@ impl Val for VacuousVisitor { fn none(self) {} fn some(self) -> Self { - VacuousVisitor() + VacuousVisitor } fn ok(self) -> Self { - VacuousVisitor() + VacuousVisitor } fn ok_empty(self) {} fn error(self) -> Self { - VacuousVisitor() + VacuousVisitor } fn error_empty(self) {} @@ -195,7 +191,7 @@ impl Val for VacuousVisitor { impl Record for VacuousVisitor { fn field(&mut self, _name: &str) -> VacuousVisitor { - VacuousVisitor() + VacuousVisitor } fn end(self) {} @@ -203,7 +199,7 @@ impl Record for VacuousVisitor { impl List for VacuousVisitor { fn element(&mut self) -> VacuousVisitor { - VacuousVisitor() + VacuousVisitor } fn end(self) {} @@ -211,7 +207,7 @@ impl List for VacuousVisitor { impl Tuple for VacuousVisitor { fn field(&mut self) -> VacuousVisitor { - VacuousVisitor() + VacuousVisitor } fn end(self) {} @@ -240,15 +236,12 @@ impl<'a> FromReader<'a> for ComponentValue<'a> { } } -fn read_val( +fn read_val( reader: &mut BinaryReader, ty: ComponentValType, types: TypesRef, visitor: V, -) -> Result<()> -where - V: Val, -{ +) -> Result<()> { let ty = get_defined_type(ty, types, reader.original_position())?; match ty { ComponentDefinedType::Primitive(prim_ty) => { diff --git a/crates/wasmparser/src/validator.rs b/crates/wasmparser/src/validator.rs index 5d2c5cea6a..20d4e81f80 100644 --- a/crates/wasmparser/src/validator.rs +++ b/crates/wasmparser/src/validator.rs @@ -1489,7 +1489,7 @@ impl Validator { |components, types, features, value, offset| { let current = components.last_mut().unwrap(); let types = TypesRef::from_component(validator_id, types, current); - value.val(types, VacuousVisitor())?; + value.val(types, VacuousVisitor)?; current.add_value(value, features, offset)?; Ok(()) }, From a0d55655b79c96c8c32326ce93b0056c99d5baef Mon Sep 17 00:00:00 2001 From: primoly <168267431+primoly@users.noreply.github.com> Date: Wed, 29 May 2024 18:22:19 +0200 Subject: [PATCH 6/7] update flags, variant and enum decoding --- .../src/readers/component/values.rs | 24 +++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/crates/wasmparser/src/readers/component/values.rs b/crates/wasmparser/src/readers/component/values.rs index 48b4fb4740..8988c1698f 100644 --- a/crates/wasmparser/src/readers/component/values.rs +++ b/crates/wasmparser/src/readers/component/values.rs @@ -255,7 +255,7 @@ fn read_val( record.end(); } ComponentDefinedType::Variant(variant_ty) => { - let label = reader.read_var_u32()?; + let label = read_label(variant_ty.cases.len() as u32, reader)?; if let Some((name, case_ty)) = variant_ty.cases.get_index(label as usize) { if let Some(case_ty) = case_ty.ty { read_val(reader, case_ty, types, visitor.variant_case(label, name))?; @@ -285,15 +285,16 @@ fn read_val( tuple.end(); } ComponentDefinedType::Flags(flags_ty) => { - let n = reader.read_var_u64()?; + let data = reader.read_bytes(flags_ty.len().div_ceil(8))?; let mut flags = visitor.flags(flags_ty.len() as u32); for (i, name) in flags_ty.iter().enumerate() { - flags.field(name, ((n >> (i as u64)) & 1) == 1); + let v = (data[i / 8] >> (i % 8) & 1) == 1; + flags.field(name, v); } flags.end(); } ComponentDefinedType::Enum(enum_ty) => { - let label = reader.read_var_u32()?; + let label = read_label(enum_ty.len() as u32, reader)?; if let Some(name) = enum_ty.get_index(label as usize) { visitor.enum_case(label, name); } else { @@ -386,6 +387,21 @@ fn read_primitive_value<'a>( }) } +fn read_label(number_of_cases: u32, reader: &mut BinaryReader) -> Result { + Ok(match number_of_cases { + 0 => bail!( + reader.original_position(), + "variants and enums must have at least one case" + ), + 1..=256 => reader.read_u8()? as u32, + 257..=65536 => u32::from_le_bytes([reader.read_u8()?, reader.read_u8()?, 0, 0]), + 65537..=16777216 => { + u32::from_le_bytes([reader.read_u8()?, reader.read_u8()?, reader.read_u8()?, 0]) + } + _ => u32::from_le_bytes(reader.read_bytes(4)?.try_into().unwrap()), + }) +} + fn get_defined_type( ty: ComponentValType, types: TypesRef, From 64025b07c95f9e3a5db379dfcc1f8f9bd749289d Mon Sep 17 00:00:00 2001 From: primoly <168267431+primoly@users.noreply.github.com> Date: Mon, 3 Jun 2024 20:53:41 +0200 Subject: [PATCH 7/7] revert label parsing --- .../src/readers/component/values.rs | 19 ++----------------- 1 file changed, 2 insertions(+), 17 deletions(-) diff --git a/crates/wasmparser/src/readers/component/values.rs b/crates/wasmparser/src/readers/component/values.rs index 8988c1698f..ce49e5837a 100644 --- a/crates/wasmparser/src/readers/component/values.rs +++ b/crates/wasmparser/src/readers/component/values.rs @@ -255,7 +255,7 @@ fn read_val( record.end(); } ComponentDefinedType::Variant(variant_ty) => { - let label = read_label(variant_ty.cases.len() as u32, reader)?; + let label = reader.read_var_u32()?; if let Some((name, case_ty)) = variant_ty.cases.get_index(label as usize) { if let Some(case_ty) = case_ty.ty { read_val(reader, case_ty, types, visitor.variant_case(label, name))?; @@ -294,7 +294,7 @@ fn read_val( flags.end(); } ComponentDefinedType::Enum(enum_ty) => { - let label = read_label(enum_ty.len() as u32, reader)?; + let label = reader.read_var_u32()?; if let Some(name) = enum_ty.get_index(label as usize) { visitor.enum_case(label, name); } else { @@ -387,21 +387,6 @@ fn read_primitive_value<'a>( }) } -fn read_label(number_of_cases: u32, reader: &mut BinaryReader) -> Result { - Ok(match number_of_cases { - 0 => bail!( - reader.original_position(), - "variants and enums must have at least one case" - ), - 1..=256 => reader.read_u8()? as u32, - 257..=65536 => u32::from_le_bytes([reader.read_u8()?, reader.read_u8()?, 0, 0]), - 65537..=16777216 => { - u32::from_le_bytes([reader.read_u8()?, reader.read_u8()?, reader.read_u8()?, 0]) - } - _ => u32::from_le_bytes(reader.read_bytes(4)?.try_into().unwrap()), - }) -} - fn get_defined_type( ty: ComponentValType, types: TypesRef,