diff options
Diffstat (limited to 'third_party/rust/wasmparser/src/readers')
26 files changed, 3143 insertions, 0 deletions
diff --git a/third_party/rust/wasmparser/src/readers/component.rs b/third_party/rust/wasmparser/src/readers/component.rs new file mode 100644 index 0000000000..24b490d0c3 --- /dev/null +++ b/third_party/rust/wasmparser/src/readers/component.rs @@ -0,0 +1,17 @@ +mod aliases; +mod canonicals; +mod exports; +mod imports; +mod instances; +mod names; +mod start; +mod types; + +pub use self::aliases::*; +pub use self::canonicals::*; +pub use self::exports::*; +pub use self::imports::*; +pub use self::instances::*; +pub use self::names::*; +pub use self::start::*; +pub use self::types::*; diff --git a/third_party/rust/wasmparser/src/readers/component/aliases.rs b/third_party/rust/wasmparser/src/readers/component/aliases.rs new file mode 100644 index 0000000000..fb71d579b4 --- /dev/null +++ b/third_party/rust/wasmparser/src/readers/component/aliases.rs @@ -0,0 +1,119 @@ +use crate::{BinaryReader, ComponentExternalKind, ExternalKind, FromReader, Result}; + +/// Represents the kind of an outer alias in a WebAssembly component. +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum ComponentOuterAliasKind { + /// The alias is to a core module. + CoreModule, + /// The alias is to a core type. + CoreType, + /// The alias is to a component type. + Type, + /// The alias is to a component. + Component, +} + +/// Represents an alias in a WebAssembly component. +#[derive(Debug, Clone)] +pub enum ComponentAlias<'a> { + /// The alias is to an export of a component instance. + InstanceExport { + /// The alias kind. + kind: ComponentExternalKind, + /// The instance index. + instance_index: u32, + /// The export name. + name: &'a str, + }, + /// The alias is to an export of a module instance. + CoreInstanceExport { + /// The alias kind. + kind: ExternalKind, + /// The instance index. + instance_index: u32, + /// The export name. + name: &'a str, + }, + /// The alias is to an outer item. + Outer { + /// The alias kind. + kind: ComponentOuterAliasKind, + /// The outward count, starting at zero for the current component. + count: u32, + /// The index of the item within the outer component. + index: u32, + }, +} + +/// Section reader for the component alias section +pub type ComponentAliasSectionReader<'a> = crate::SectionLimited<'a, ComponentAlias<'a>>; + +impl<'a> FromReader<'a> for ComponentAlias<'a> { + fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> { + // We don't know what type of alias it is yet, so just read the sort bytes + let offset = reader.original_position(); + let byte1 = reader.read_u8()?; + let byte2 = if byte1 == 0x00 { + Some(reader.read_u8()?) + } else { + None + }; + + Ok(match reader.read_u8()? { + 0x00 => ComponentAlias::InstanceExport { + kind: ComponentExternalKind::from_bytes(byte1, byte2, offset)?, + instance_index: reader.read_var_u32()?, + name: reader.read_string()?, + }, + 0x01 => ComponentAlias::CoreInstanceExport { + kind: BinaryReader::external_kind_from_byte( + byte2.ok_or_else(|| { + BinaryReader::invalid_leading_byte_error( + byte1, + "core instance export kind", + offset, + ) + })?, + offset, + )?, + instance_index: reader.read_var_u32()?, + name: reader.read_string()?, + }, + 0x02 => ComponentAlias::Outer { + kind: component_outer_alias_kind_from_bytes(byte1, byte2, offset)?, + count: reader.read_var_u32()?, + index: reader.read_var_u32()?, + }, + x => reader.invalid_leading_byte(x, "alias")?, + }) + } +} + +fn component_outer_alias_kind_from_bytes( + byte1: u8, + byte2: Option<u8>, + offset: usize, +) -> Result<ComponentOuterAliasKind> { + Ok(match byte1 { + 0x00 => match byte2.unwrap() { + 0x10 => ComponentOuterAliasKind::CoreType, + 0x11 => ComponentOuterAliasKind::CoreModule, + x => { + return Err(BinaryReader::invalid_leading_byte_error( + x, + "component outer alias kind", + offset + 1, + )) + } + }, + 0x03 => ComponentOuterAliasKind::Type, + 0x04 => ComponentOuterAliasKind::Component, + x => { + return Err(BinaryReader::invalid_leading_byte_error( + x, + "component outer alias kind", + offset, + )) + } + }) +} diff --git a/third_party/rust/wasmparser/src/readers/component/canonicals.rs b/third_party/rust/wasmparser/src/readers/component/canonicals.rs new file mode 100644 index 0000000000..e360d029c4 --- /dev/null +++ b/third_party/rust/wasmparser/src/readers/component/canonicals.rs @@ -0,0 +1,95 @@ +use crate::limits::MAX_WASM_CANONICAL_OPTIONS; +use crate::{BinaryReader, FromReader, Result, SectionLimited}; + +/// Represents options for component functions. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum CanonicalOption { + /// The string types in the function signature are UTF-8 encoded. + UTF8, + /// The string types in the function signature are UTF-16 encoded. + UTF16, + /// The string types in the function signature are compact UTF-16 encoded. + CompactUTF16, + /// The memory to use if the lifting or lowering of a function requires memory access. + /// + /// The value is an index to a core memory. + Memory(u32), + /// The realloc function to use if the lifting or lowering of a function requires memory + /// allocation. + /// + /// The value is an index to a core function of type `(func (param i32 i32 i32 i32) (result i32))`. + Realloc(u32), + /// The post-return function to use if the lifting of a function requires + /// cleanup after the function returns. + PostReturn(u32), +} + +/// Represents a canonical function in a WebAssembly component. +#[derive(Debug, Clone)] +pub enum CanonicalFunction { + /// The function lifts a core WebAssembly function to the canonical ABI. + Lift { + /// The index of the core WebAssembly function to lift. + core_func_index: u32, + /// The index of the lifted function's type. + type_index: u32, + /// The canonical options for the function. + options: Box<[CanonicalOption]>, + }, + /// The function lowers a canonical ABI function to a core WebAssembly function. + Lower { + /// The index of the function to lower. + func_index: u32, + /// The canonical options for the function. + options: Box<[CanonicalOption]>, + }, +} + +/// A reader for the canonical section of a WebAssembly component. +pub type ComponentCanonicalSectionReader<'a> = SectionLimited<'a, CanonicalFunction>; + +impl<'a> FromReader<'a> for CanonicalFunction { + fn from_reader(reader: &mut BinaryReader<'a>) -> Result<CanonicalFunction> { + Ok(match reader.read_u8()? { + 0x00 => match reader.read_u8()? { + 0x00 => { + let core_func_index = reader.read_var_u32()?; + let options = reader + .read_iter(MAX_WASM_CANONICAL_OPTIONS, "canonical options")? + .collect::<Result<_>>()?; + let type_index = reader.read_var_u32()?; + CanonicalFunction::Lift { + core_func_index, + options, + type_index, + } + } + x => return reader.invalid_leading_byte(x, "canonical function lift"), + }, + 0x01 => match reader.read_u8()? { + 0x00 => CanonicalFunction::Lower { + func_index: reader.read_var_u32()?, + options: reader + .read_iter(MAX_WASM_CANONICAL_OPTIONS, "canonical options")? + .collect::<Result<_>>()?, + }, + x => return reader.invalid_leading_byte(x, "canonical function lower"), + }, + x => return reader.invalid_leading_byte(x, "canonical function"), + }) + } +} + +impl<'a> FromReader<'a> for CanonicalOption { + fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> { + Ok(match reader.read_u8()? { + 0x00 => CanonicalOption::UTF8, + 0x01 => CanonicalOption::UTF16, + 0x02 => CanonicalOption::CompactUTF16, + 0x03 => CanonicalOption::Memory(reader.read_var_u32()?), + 0x04 => CanonicalOption::Realloc(reader.read_var_u32()?), + 0x05 => CanonicalOption::PostReturn(reader.read_var_u32()?), + x => return reader.invalid_leading_byte(x, "canonical option"), + }) + } +} diff --git a/third_party/rust/wasmparser/src/readers/component/exports.rs b/third_party/rust/wasmparser/src/readers/component/exports.rs new file mode 100644 index 0000000000..8ce5f43a00 --- /dev/null +++ b/third_party/rust/wasmparser/src/readers/component/exports.rs @@ -0,0 +1,105 @@ +use crate::{BinaryReader, ComponentTypeRef, FromReader, Result, SectionLimited}; + +/// Represents the kind of an external items of a WebAssembly component. +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum ComponentExternalKind { + /// The external kind is a core module. + Module, + /// The external kind is a function. + Func, + /// The external kind is a value. + Value, + /// The external kind is a type. + Type, + /// The external kind is an instance. + Instance, + /// The external kind is a component. + Component, +} + +impl ComponentExternalKind { + pub(crate) fn from_bytes( + byte1: u8, + byte2: Option<u8>, + offset: usize, + ) -> Result<ComponentExternalKind> { + Ok(match byte1 { + 0x00 => match byte2.unwrap() { + 0x11 => ComponentExternalKind::Module, + x => { + return Err(BinaryReader::invalid_leading_byte_error( + x, + "component external kind", + offset + 1, + )) + } + }, + 0x01 => ComponentExternalKind::Func, + 0x02 => ComponentExternalKind::Value, + 0x03 => ComponentExternalKind::Type, + 0x04 => ComponentExternalKind::Component, + 0x05 => ComponentExternalKind::Instance, + x => { + return Err(BinaryReader::invalid_leading_byte_error( + x, + "component external kind", + offset, + )) + } + }) + } +} + +/// Represents an export in a WebAssembly component. +#[derive(Debug, Clone)] +pub struct ComponentExport<'a> { + /// The name of the exported item. + pub name: &'a str, + /// The optional URL of the exported item. + pub url: &'a str, + /// The kind of the export. + pub kind: ComponentExternalKind, + /// The index of the exported item. + pub index: u32, + /// An optionally specified type ascribed to this export. + pub ty: Option<ComponentTypeRef>, +} + +/// A reader for the export section of a WebAssembly component. +pub type ComponentExportSectionReader<'a> = SectionLimited<'a, ComponentExport<'a>>; + +impl<'a> FromReader<'a> for ComponentExport<'a> { + fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> { + Ok(ComponentExport { + name: reader.read()?, + url: reader.read()?, + kind: reader.read()?, + index: reader.read()?, + ty: match reader.read_u8()? { + 0x00 => None, + 0x01 => Some(reader.read()?), + other => { + return Err(BinaryReader::invalid_leading_byte_error( + other, + "optional component export type", + reader.original_position() - 1, + )) + } + }, + }) + } +} + +impl<'a> FromReader<'a> for ComponentExternalKind { + fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> { + let offset = reader.original_position(); + let byte1 = reader.read_u8()?; + let byte2 = if byte1 == 0x00 { + Some(reader.read_u8()?) + } else { + None + }; + + ComponentExternalKind::from_bytes(byte1, byte2, offset) + } +} diff --git a/third_party/rust/wasmparser/src/readers/component/imports.rs b/third_party/rust/wasmparser/src/readers/component/imports.rs new file mode 100644 index 0000000000..c1313c11e2 --- /dev/null +++ b/third_party/rust/wasmparser/src/readers/component/imports.rs @@ -0,0 +1,109 @@ +use crate::{ + BinaryReader, ComponentExternalKind, ComponentValType, FromReader, Result, SectionLimited, +}; + +/// Represents the type bounds for imports and exports. +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum TypeBounds { + /// The type is bounded by equality. + Eq, +} + +impl<'a> FromReader<'a> for TypeBounds { + fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> { + Ok(match reader.read_u8()? { + 0x00 => TypeBounds::Eq, + x => return reader.invalid_leading_byte(x, "type bound"), + }) + } +} + +/// Represents a reference to a component type. +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum ComponentTypeRef { + /// The reference is to a core module type. + /// + /// The index is expected to be core type index to a core module type. + Module(u32), + /// The reference is to a function type. + /// + /// The index is expected to be a type index to a function type. + Func(u32), + /// The reference is to a value type. + Value(ComponentValType), + /// The reference is to a bounded type. + /// + /// The index is expected to be a type index. + Type(TypeBounds, u32), + /// The reference is to an instance type. + /// + /// The index is a type index to an instance type. + Instance(u32), + /// The reference is to a component type. + /// + /// The index is a type index to a component type. + Component(u32), +} + +impl ComponentTypeRef { + /// Returns the corresponding [`ComponentExternalKind`] for this reference. + pub fn kind(&self) -> ComponentExternalKind { + match self { + ComponentTypeRef::Module(_) => ComponentExternalKind::Module, + ComponentTypeRef::Func(_) => ComponentExternalKind::Func, + ComponentTypeRef::Value(_) => ComponentExternalKind::Value, + ComponentTypeRef::Type(..) => ComponentExternalKind::Type, + ComponentTypeRef::Instance(_) => ComponentExternalKind::Instance, + ComponentTypeRef::Component(_) => ComponentExternalKind::Component, + } + } +} + +impl<'a> FromReader<'a> for ComponentTypeRef { + fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> { + Ok(match reader.read()? { + ComponentExternalKind::Module => ComponentTypeRef::Module(reader.read()?), + ComponentExternalKind::Func => ComponentTypeRef::Func(reader.read()?), + ComponentExternalKind::Value => ComponentTypeRef::Value(reader.read()?), + ComponentExternalKind::Type => ComponentTypeRef::Type(reader.read()?, reader.read()?), + ComponentExternalKind::Instance => ComponentTypeRef::Instance(reader.read()?), + ComponentExternalKind::Component => ComponentTypeRef::Component(reader.read()?), + }) + } +} + +/// Represents an import in a WebAssembly component +#[derive(Debug, Copy, Clone)] +pub struct ComponentImport<'a> { + /// The name of the imported item. + pub name: &'a str, + /// The optional URL of the imported item. + pub url: &'a str, + /// The type reference for the import. + pub ty: ComponentTypeRef, +} + +impl<'a> FromReader<'a> for ComponentImport<'a> { + fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> { + Ok(ComponentImport { + name: reader.read()?, + url: reader.read()?, + ty: reader.read()?, + }) + } +} + +/// A reader for the import section of a WebAssembly component. +/// +/// # Examples +/// +/// ``` +/// use wasmparser::ComponentImportSectionReader; +/// let data: &[u8] = &[0x01, 0x01, 0x41, 0x00, 0x01, 0x66]; +/// let reader = ComponentImportSectionReader::new(data, 0).unwrap(); +/// for import in reader { +/// let import = import.expect("import"); +/// println!("Import: {:?}", import); +/// } +/// ``` +pub type ComponentImportSectionReader<'a> = SectionLimited<'a, ComponentImport<'a>>; diff --git a/third_party/rust/wasmparser/src/readers/component/instances.rs b/third_party/rust/wasmparser/src/readers/component/instances.rs new file mode 100644 index 0000000000..8166395edc --- /dev/null +++ b/third_party/rust/wasmparser/src/readers/component/instances.rs @@ -0,0 +1,164 @@ +use crate::limits::{MAX_WASM_INSTANTIATION_ARGS, MAX_WASM_INSTANTIATION_EXPORTS}; +use crate::{ + BinaryReader, ComponentExport, ComponentExternalKind, Export, FromReader, Result, + SectionLimited, +}; + +/// Represents the kind of an instantiation argument for a core instance. +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum InstantiationArgKind { + /// The instantiation argument is a core instance. + Instance, +} + +/// Represents an argument to instantiating a WebAssembly module. +#[derive(Debug, Clone)] +pub struct InstantiationArg<'a> { + /// The name of the module argument. + pub name: &'a str, + /// The kind of the module argument. + pub kind: InstantiationArgKind, + /// The index of the argument item. + pub index: u32, +} + +/// Represents an instance of a WebAssembly module. +#[derive(Debug, Clone)] +pub enum Instance<'a> { + /// The instance is from instantiating a WebAssembly module. + Instantiate { + /// The module index. + module_index: u32, + /// The module's instantiation arguments. + args: Box<[InstantiationArg<'a>]>, + }, + /// The instance is a from exporting local items. + FromExports(Box<[Export<'a>]>), +} + +/// A reader for the core instance section of a WebAssembly component. +/// +/// # Examples +/// +/// ``` +/// use wasmparser::InstanceSectionReader; +/// # let data: &[u8] = &[0x01, 0x00, 0x00, 0x01, 0x03, b'f', b'o', b'o', 0x12, 0x00]; +/// let mut reader = InstanceSectionReader::new(data, 0).unwrap(); +/// for inst in reader { +/// println!("Instance {:?}", inst.expect("instance")); +/// } +/// ``` +pub type InstanceSectionReader<'a> = SectionLimited<'a, Instance<'a>>; + +impl<'a> FromReader<'a> for Instance<'a> { + fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> { + Ok(match reader.read_u8()? { + 0x00 => Instance::Instantiate { + module_index: reader.read_var_u32()?, + args: reader + .read_iter(MAX_WASM_INSTANTIATION_ARGS, "core instantiation arguments")? + .collect::<Result<_>>()?, + }, + 0x01 => Instance::FromExports( + reader + .read_iter(MAX_WASM_INSTANTIATION_ARGS, "core instantiation arguments")? + .collect::<Result<_>>()?, + ), + x => return reader.invalid_leading_byte(x, "core instance"), + }) + } +} + +impl<'a> FromReader<'a> for InstantiationArg<'a> { + fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> { + Ok(InstantiationArg { + name: reader.read()?, + kind: reader.read()?, + index: reader.read()?, + }) + } +} + +impl<'a> FromReader<'a> for InstantiationArgKind { + fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> { + Ok(match reader.read_u8()? { + 0x12 => InstantiationArgKind::Instance, + x => return reader.invalid_leading_byte(x, "instantiation arg kind"), + }) + } +} + +/// Represents an argument to instantiating a WebAssembly component. +#[derive(Debug, Clone)] +pub struct ComponentInstantiationArg<'a> { + /// The name of the component argument. + pub name: &'a str, + /// The kind of the component argument. + pub kind: ComponentExternalKind, + /// The index of the argument item. + pub index: u32, +} + +/// Represents an instance in a WebAssembly component. +#[derive(Debug, Clone)] +pub enum ComponentInstance<'a> { + /// The instance is from instantiating a WebAssembly component. + Instantiate { + /// The component index. + component_index: u32, + /// The component's instantiation arguments. + args: Box<[ComponentInstantiationArg<'a>]>, + }, + /// The instance is a from exporting local items. + FromExports(Box<[ComponentExport<'a>]>), +} + +/// A reader for the component instance section of a WebAssembly component. +/// +/// # Examples +/// +/// ``` +/// use wasmparser::ComponentInstanceSectionReader; +/// # let data: &[u8] = &[0x01, 0x00, 0x00, 0x01, 0x03, b'f', b'o', b'o', 0x01, 0x00]; +/// let mut reader = ComponentInstanceSectionReader::new(data, 0).unwrap(); +/// for inst in reader { +/// println!("Instance {:?}", inst.expect("instance")); +/// } +/// ``` +pub type ComponentInstanceSectionReader<'a> = SectionLimited<'a, ComponentInstance<'a>>; + +impl<'a> FromReader<'a> for ComponentInstance<'a> { + fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> { + Ok(match reader.read_u8()? { + 0x00 => ComponentInstance::Instantiate { + component_index: reader.read_var_u32()?, + args: reader + .read_iter(MAX_WASM_INSTANTIATION_ARGS, "instantiation arguments")? + .collect::<Result<_>>()?, + }, + 0x01 => ComponentInstance::FromExports( + (0..reader.read_size(MAX_WASM_INSTANTIATION_EXPORTS, "instantiation exports")?) + .map(|_| { + Ok(ComponentExport { + name: reader.read()?, + url: "", + kind: reader.read()?, + index: reader.read()?, + ty: None, + }) + }) + .collect::<Result<_>>()?, + ), + x => return reader.invalid_leading_byte(x, "instance"), + }) + } +} +impl<'a> FromReader<'a> for ComponentInstantiationArg<'a> { + fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> { + Ok(ComponentInstantiationArg { + name: reader.read()?, + kind: reader.read()?, + index: reader.read()?, + }) + } +} diff --git a/third_party/rust/wasmparser/src/readers/component/names.rs b/third_party/rust/wasmparser/src/readers/component/names.rs new file mode 100644 index 0000000000..19de2752d0 --- /dev/null +++ b/third_party/rust/wasmparser/src/readers/component/names.rs @@ -0,0 +1,102 @@ +use crate::{BinaryReader, BinaryReaderError, NameMap, Result, Subsection, Subsections}; +use std::ops::Range; + +/// Type used to iterate and parse the contents of the `component-name` custom +/// section in compnents, similar to the `name` section of core modules. +pub type ComponentNameSectionReader<'a> = Subsections<'a, ComponentName<'a>>; + +/// Represents a name read from the names custom section. +#[derive(Clone)] +#[allow(missing_docs)] +pub enum ComponentName<'a> { + Component { + name: &'a str, + name_range: Range<usize>, + }, + CoreFuncs(NameMap<'a>), + CoreGlobals(NameMap<'a>), + CoreMemories(NameMap<'a>), + CoreTables(NameMap<'a>), + CoreModules(NameMap<'a>), + CoreInstances(NameMap<'a>), + CoreTypes(NameMap<'a>), + Types(NameMap<'a>), + Instances(NameMap<'a>), + Components(NameMap<'a>), + Funcs(NameMap<'a>), + Values(NameMap<'a>), + + /// An unknown [name subsection](https://webassembly.github.io/spec/core/appendix/custom.html#subsections). + Unknown { + /// The identifier for this subsection. + ty: u8, + /// The contents of this subsection. + data: &'a [u8], + /// The range of bytes, relative to the start of the original data + /// stream, that the contents of this subsection reside in. + range: Range<usize>, + }, +} + +impl<'a> Subsection<'a> for ComponentName<'a> { + fn from_reader(id: u8, mut reader: BinaryReader<'a>) -> Result<Self> { + let data = reader.remaining_buffer(); + let offset = reader.original_position(); + Ok(match id { + 0 => { + let name = reader.read_string()?; + if !reader.eof() { + return Err(BinaryReaderError::new( + "trailing data at the end of a name", + reader.original_position(), + )); + } + ComponentName::Component { + name, + name_range: offset..offset + reader.position, + } + } + 1 => { + let ctor: fn(NameMap<'a>) -> ComponentName<'a> = match reader.read_u8()? { + 0x00 => match reader.read_u8()? { + 0x00 => ComponentName::CoreFuncs, + 0x01 => ComponentName::CoreTables, + 0x02 => ComponentName::CoreMemories, + 0x03 => ComponentName::CoreGlobals, + 0x10 => ComponentName::CoreTypes, + 0x11 => ComponentName::CoreModules, + 0x12 => ComponentName::CoreInstances, + _ => { + return Ok(ComponentName::Unknown { + ty: 1, + data, + range: offset..offset + data.len(), + }); + } + }, + 0x01 => ComponentName::Funcs, + 0x02 => ComponentName::Values, + 0x03 => ComponentName::Types, + 0x04 => ComponentName::Components, + 0x05 => ComponentName::Instances, + _ => { + return Ok(ComponentName::Unknown { + ty: 1, + data, + range: offset..offset + data.len(), + }); + } + }; + ctor(NameMap::new( + reader.remaining_buffer(), + reader.original_position(), + )?) + } + ty => ComponentName::Unknown { + ty, + data, + range: offset..offset + data.len(), + }, + }) + } +} diff --git a/third_party/rust/wasmparser/src/readers/component/start.rs b/third_party/rust/wasmparser/src/readers/component/start.rs new file mode 100644 index 0000000000..dc01fa4340 --- /dev/null +++ b/third_party/rust/wasmparser/src/readers/component/start.rs @@ -0,0 +1,30 @@ +use crate::limits::{MAX_WASM_FUNCTION_RETURNS, MAX_WASM_START_ARGS}; +use crate::{BinaryReader, FromReader, Result}; + +/// Represents the start function in a WebAssembly component. +#[derive(Debug, Clone)] +pub struct ComponentStartFunction { + /// The index to the start function. + pub func_index: u32, + /// The start function arguments. + /// + /// The arguments are specified by value index. + pub arguments: Box<[u32]>, + /// The number of expected results for the start function. + pub results: u32, +} + +impl<'a> FromReader<'a> for ComponentStartFunction { + fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> { + let func_index = reader.read_var_u32()?; + let arguments = reader + .read_iter(MAX_WASM_START_ARGS, "start function arguments")? + .collect::<Result<_>>()?; + let results = reader.read_size(MAX_WASM_FUNCTION_RETURNS, "start function results")? as u32; + Ok(ComponentStartFunction { + func_index, + arguments, + results, + }) + } +} diff --git a/third_party/rust/wasmparser/src/readers/component/types.rs b/third_party/rust/wasmparser/src/readers/component/types.rs new file mode 100644 index 0000000000..b0e9687a4d --- /dev/null +++ b/third_party/rust/wasmparser/src/readers/component/types.rs @@ -0,0 +1,508 @@ +use crate::limits::*; +use crate::{ + BinaryReader, ComponentAlias, ComponentImport, ComponentTypeRef, FromReader, FuncType, Import, + Result, SectionLimited, Type, TypeRef, +}; + +/// Represents the kind of an outer core alias in a WebAssembly component. +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum OuterAliasKind { + /// The alias is to a core type. + Type, +} + +/// Represents a core type in a WebAssembly component. +#[derive(Debug, Clone)] +pub enum CoreType<'a> { + /// The type is for a core function. + Func(FuncType), + /// The type is for a core module. + Module(Box<[ModuleTypeDeclaration<'a>]>), +} + +impl<'a> FromReader<'a> for CoreType<'a> { + fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> { + Ok(match reader.read_u8()? { + 0x60 => CoreType::Func(reader.read()?), + 0x50 => CoreType::Module( + reader + .read_iter(MAX_WASM_MODULE_TYPE_DECLS, "module type declaration")? + .collect::<Result<_>>()?, + ), + x => return reader.invalid_leading_byte(x, "core type"), + }) + } +} + +/// Represents a module type declaration in a WebAssembly component. +#[derive(Debug, Clone)] +pub enum ModuleTypeDeclaration<'a> { + /// The module type definition is for a type. + Type(Type), + /// The module type definition is for an export. + Export { + /// The name of the exported item. + name: &'a str, + /// The type reference of the export. + ty: TypeRef, + }, + /// The module type declaration is for an outer alias. + OuterAlias { + /// The alias kind. + kind: OuterAliasKind, + /// The outward count, starting at zero for the current type. + count: u32, + /// The index of the item within the outer type. + index: u32, + }, + /// The module type definition is for an import. + Import(Import<'a>), +} + +impl<'a> FromReader<'a> for ModuleTypeDeclaration<'a> { + fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> { + Ok(match reader.read_u8()? { + 0x00 => ModuleTypeDeclaration::Import(reader.read()?), + 0x01 => ModuleTypeDeclaration::Type(reader.read()?), + 0x02 => { + let kind = match reader.read_u8()? { + 0x10 => OuterAliasKind::Type, + x => { + return reader.invalid_leading_byte(x, "outer alias kind"); + } + }; + match reader.read_u8()? { + 0x01 => ModuleTypeDeclaration::OuterAlias { + kind, + count: reader.read()?, + index: reader.read()?, + }, + x => { + return reader.invalid_leading_byte(x, "outer alias target"); + } + } + } + 0x03 => ModuleTypeDeclaration::Export { + name: reader.read()?, + ty: reader.read()?, + }, + x => return reader.invalid_leading_byte(x, "type definition"), + }) + } +} + +/// A reader for the core type section of a WebAssembly component. +/// +/// # Examples +/// ``` +/// use wasmparser::CoreTypeSectionReader; +/// # let data: &[u8] = &[0x01, 0x60, 0x00, 0x00]; +/// let mut reader = CoreTypeSectionReader::new(data, 0).unwrap(); +/// for ty in reader { +/// println!("Type {:?}", ty.expect("type")); +/// } +/// ``` +pub type CoreTypeSectionReader<'a> = SectionLimited<'a, CoreType<'a>>; + +/// Represents a value type in a WebAssembly component. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum ComponentValType { + /// The value type is a primitive type. + Primitive(PrimitiveValType), + /// The value type is a reference to a defined type. + Type(u32), +} + +impl<'a> FromReader<'a> for ComponentValType { + fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> { + if let Some(ty) = PrimitiveValType::from_byte(reader.peek()?) { + reader.position += 1; + return Ok(ComponentValType::Primitive(ty)); + } + + Ok(ComponentValType::Type(reader.read_var_s33()? as u32)) + } +} + +impl<'a> FromReader<'a> for Option<ComponentValType> { + fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> { + match reader.read_u8()? { + 0x0 => Ok(None), + 0x1 => Ok(Some(reader.read()?)), + x => reader.invalid_leading_byte(x, "optional component value type"), + } + } +} + +/// Represents a primitive value type. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum PrimitiveValType { + /// The type is a boolean. + Bool, + /// The type is a signed 8-bit integer. + S8, + /// The type is an unsigned 8-bit integer. + U8, + /// The type is a signed 16-bit integer. + S16, + /// The type is an unsigned 16-bit integer. + U16, + /// The type is a signed 32-bit integer. + S32, + /// The type is an unsigned 32-bit integer. + U32, + /// The type is a signed 64-bit integer. + S64, + /// The type is an unsigned 64-bit integer. + U64, + /// The type is a 32-bit floating point number. + Float32, + /// The type is a 64-bit floating point number. + Float64, + /// The type is a Unicode character. + Char, + /// The type is a string. + String, +} + +impl PrimitiveValType { + fn from_byte(byte: u8) -> Option<PrimitiveValType> { + Some(match byte { + 0x7f => PrimitiveValType::Bool, + 0x7e => PrimitiveValType::S8, + 0x7d => PrimitiveValType::U8, + 0x7c => PrimitiveValType::S16, + 0x7b => PrimitiveValType::U16, + 0x7a => PrimitiveValType::S32, + 0x79 => PrimitiveValType::U32, + 0x78 => PrimitiveValType::S64, + 0x77 => PrimitiveValType::U64, + 0x76 => PrimitiveValType::Float32, + 0x75 => PrimitiveValType::Float64, + 0x74 => PrimitiveValType::Char, + 0x73 => PrimitiveValType::String, + _ => return None, + }) + } + + pub(crate) fn requires_realloc(&self) -> bool { + matches!(self, Self::String) + } + + /// Determines if primitive value type `a` is a subtype of `b`. + pub fn is_subtype_of(a: Self, b: Self) -> bool { + // Note that this intentionally diverges from the upstream specification + // at this time and only considers exact equality for subtyping + // relationships. + // + // More information can be found in the subtyping implementation for + // component functions. + a == b + } +} + +/// Represents a type in a WebAssembly component. +#[derive(Debug, Clone)] +pub enum ComponentType<'a> { + /// The type is a component defined type. + Defined(ComponentDefinedType<'a>), + /// The type is a function type. + Func(ComponentFuncType<'a>), + /// The type is a component type. + Component(Box<[ComponentTypeDeclaration<'a>]>), + /// The type is an instance type. + Instance(Box<[InstanceTypeDeclaration<'a>]>), +} + +impl<'a> FromReader<'a> for ComponentType<'a> { + fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> { + Ok(match reader.read_u8()? { + 0x40 => { + let params = reader + .read_iter(MAX_WASM_FUNCTION_PARAMS, "component function parameters")? + .collect::<Result<_>>()?; + let results = reader.read()?; + ComponentType::Func(ComponentFuncType { params, results }) + } + 0x41 => ComponentType::Component( + reader + .read_iter(MAX_WASM_COMPONENT_TYPE_DECLS, "component type declaration")? + .collect::<Result<_>>()?, + ), + 0x42 => ComponentType::Instance( + reader + .read_iter(MAX_WASM_INSTANCE_TYPE_DECLS, "instance type declaration")? + .collect::<Result<_>>()?, + ), + x => { + if let Some(ty) = PrimitiveValType::from_byte(x) { + ComponentType::Defined(ComponentDefinedType::Primitive(ty)) + } else { + ComponentType::Defined(ComponentDefinedType::read(reader, x)?) + } + } + }) + } +} + +/// Represents part of a component type declaration in a WebAssembly component. +#[derive(Debug, Clone)] +pub enum ComponentTypeDeclaration<'a> { + /// The component type declaration is for a core type. + CoreType(CoreType<'a>), + /// The component type declaration is for a type. + Type(ComponentType<'a>), + /// The component type declaration is for an alias. + Alias(ComponentAlias<'a>), + /// The component type declaration is for an export. + Export { + /// The name of the export. + name: &'a str, + /// The optional URL of the export. + url: &'a str, + /// The type reference for the export. + ty: ComponentTypeRef, + }, + /// The component type declaration is for an import. + Import(ComponentImport<'a>), +} + +impl<'a> FromReader<'a> for ComponentTypeDeclaration<'a> { + fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> { + // Component types are effectively instance types with the additional + // variant of imports; check for imports here or delegate to + // `InstanceTypeDeclaration` with the appropriate conversions. + if reader.peek()? == 0x03 { + reader.position += 1; + return Ok(ComponentTypeDeclaration::Import(reader.read()?)); + } + + Ok(match reader.read()? { + InstanceTypeDeclaration::CoreType(t) => ComponentTypeDeclaration::CoreType(t), + InstanceTypeDeclaration::Type(t) => ComponentTypeDeclaration::Type(t), + InstanceTypeDeclaration::Alias(a) => ComponentTypeDeclaration::Alias(a), + InstanceTypeDeclaration::Export { name, url, ty } => { + ComponentTypeDeclaration::Export { name, url, ty } + } + }) + } +} + +/// Represents an instance type declaration in a WebAssembly component. +#[derive(Debug, Clone)] +pub enum InstanceTypeDeclaration<'a> { + /// The component type declaration is for a core type. + CoreType(CoreType<'a>), + /// The instance type declaration is for a type. + Type(ComponentType<'a>), + /// The instance type declaration is for an alias. + Alias(ComponentAlias<'a>), + /// The instance type declaration is for an export. + Export { + /// The name of the export. + name: &'a str, + /// The URL for the export. + url: &'a str, + /// The type reference for the export. + ty: ComponentTypeRef, + }, +} + +impl<'a> FromReader<'a> for InstanceTypeDeclaration<'a> { + fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> { + Ok(match reader.read_u8()? { + 0x00 => InstanceTypeDeclaration::CoreType(reader.read()?), + 0x01 => InstanceTypeDeclaration::Type(reader.read()?), + 0x02 => InstanceTypeDeclaration::Alias(reader.read()?), + 0x04 => InstanceTypeDeclaration::Export { + name: reader.read()?, + url: reader.read()?, + ty: reader.read()?, + }, + x => return reader.invalid_leading_byte(x, "component or instance type declaration"), + }) + } +} + +/// Represents the result type of a component function. +#[derive(Debug, Clone)] +pub enum ComponentFuncResult<'a> { + /// The function returns a singular, unnamed type. + Unnamed(ComponentValType), + /// The function returns zero or more named types. + Named(Box<[(&'a str, ComponentValType)]>), +} + +impl<'a> FromReader<'a> for ComponentFuncResult<'a> { + fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> { + Ok(match reader.read_u8()? { + 0x00 => ComponentFuncResult::Unnamed(reader.read()?), + 0x01 => ComponentFuncResult::Named( + reader + .read_iter(MAX_WASM_FUNCTION_RETURNS, "component function results")? + .collect::<Result<_>>()?, + ), + x => return reader.invalid_leading_byte(x, "component function results"), + }) + } +} + +impl ComponentFuncResult<'_> { + /// Gets the count of types returned by the function. + pub fn type_count(&self) -> usize { + match self { + Self::Unnamed(_) => 1, + Self::Named(vec) => vec.len(), + } + } + + /// Iterates over the types returned by the function. + pub fn iter(&self) -> impl Iterator<Item = (Option<&str>, &ComponentValType)> { + enum Either<L, R> { + Left(L), + Right(R), + } + + impl<L, R> Iterator for Either<L, R> + where + L: Iterator, + R: Iterator<Item = L::Item>, + { + type Item = L::Item; + + fn next(&mut self) -> Option<Self::Item> { + match self { + Either::Left(l) => l.next(), + Either::Right(r) => r.next(), + } + } + } + + match self { + Self::Unnamed(ty) => Either::Left(std::iter::once(ty).map(|ty| (None, ty))), + Self::Named(vec) => Either::Right(vec.iter().map(|(n, ty)| (Some(*n), ty))), + } + } +} + +/// Represents a type of a function in a WebAssembly component. +#[derive(Debug, Clone)] +pub struct ComponentFuncType<'a> { + /// The function parameters. + pub params: Box<[(&'a str, ComponentValType)]>, + /// The function result. + pub results: ComponentFuncResult<'a>, +} + +/// Represents a case in a variant type. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct VariantCase<'a> { + /// The name of the variant case. + pub name: &'a str, + /// The value type of the variant case. + pub ty: Option<ComponentValType>, + /// The index of the variant case that is refined by this one. + pub refines: Option<u32>, +} + +impl<'a> FromReader<'a> for VariantCase<'a> { + fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> { + Ok(VariantCase { + name: reader.read()?, + ty: reader.read()?, + refines: match reader.read_u8()? { + 0x0 => None, + 0x1 => Some(reader.read_var_u32()?), + x => return reader.invalid_leading_byte(x, "variant case refines"), + }, + }) + } +} + +/// Represents a defined type in a WebAssembly component. +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum ComponentDefinedType<'a> { + /// The type is one of the primitive value types. + Primitive(PrimitiveValType), + /// The type is a record with the given fields. + Record(Box<[(&'a str, ComponentValType)]>), + /// The type is a variant with the given cases. + Variant(Box<[VariantCase<'a>]>), + /// The type is a list of the given value type. + List(ComponentValType), + /// The type is a tuple of the given value types. + Tuple(Box<[ComponentValType]>), + /// The type is flags with the given names. + Flags(Box<[&'a str]>), + /// The type is an enum with the given tags. + Enum(Box<[&'a str]>), + /// The type is a union of the given value types. + Union(Box<[ComponentValType]>), + /// The type is an option of the given value type. + Option(ComponentValType), + /// The type is a result type. + Result { + /// The type returned for success. + ok: Option<ComponentValType>, + /// The type returned for failure. + err: Option<ComponentValType>, + }, +} + +impl<'a> ComponentDefinedType<'a> { + fn read(reader: &mut BinaryReader<'a>, byte: u8) -> Result<ComponentDefinedType<'a>> { + Ok(match byte { + 0x72 => ComponentDefinedType::Record( + reader + .read_iter(MAX_WASM_RECORD_FIELDS, "record field")? + .collect::<Result<_>>()?, + ), + 0x71 => ComponentDefinedType::Variant( + reader + .read_iter(MAX_WASM_VARIANT_CASES, "variant cases")? + .collect::<Result<_>>()?, + ), + 0x70 => ComponentDefinedType::List(reader.read()?), + 0x6f => ComponentDefinedType::Tuple( + reader + .read_iter(MAX_WASM_TUPLE_TYPES, "tuple types")? + .collect::<Result<_>>()?, + ), + 0x6e => ComponentDefinedType::Flags( + reader + .read_iter(MAX_WASM_FLAG_NAMES, "flag names")? + .collect::<Result<_>>()?, + ), + 0x6d => ComponentDefinedType::Enum( + reader + .read_iter(MAX_WASM_ENUM_CASES, "enum cases")? + .collect::<Result<_>>()?, + ), + 0x6c => ComponentDefinedType::Union( + reader + .read_iter(MAX_WASM_UNION_TYPES, "union types")? + .collect::<Result<_>>()?, + ), + 0x6b => ComponentDefinedType::Option(reader.read()?), + 0x6a => ComponentDefinedType::Result { + ok: reader.read()?, + err: reader.read()?, + }, + x => return reader.invalid_leading_byte(x, "component defined type"), + }) + } +} + +/// A reader for the type section of a WebAssembly component. +/// +/// # Examples +/// +/// ``` +/// use wasmparser::ComponentTypeSectionReader; +/// let data: &[u8] = &[0x01, 0x40, 0x01, 0x03, b'f', b'o', b'o', 0x73, 0x00, 0x73]; +/// let mut reader = ComponentTypeSectionReader::new(data, 0).unwrap(); +/// for ty in reader { +/// println!("Type {:?}", ty.expect("type")); +/// } +/// ``` +pub type ComponentTypeSectionReader<'a> = SectionLimited<'a, ComponentType<'a>>; diff --git a/third_party/rust/wasmparser/src/readers/core.rs b/third_party/rust/wasmparser/src/readers/core.rs new file mode 100644 index 0000000000..c42bbf8d9e --- /dev/null +++ b/third_party/rust/wasmparser/src/readers/core.rs @@ -0,0 +1,33 @@ +mod code; +mod custom; +mod data; +mod elements; +mod exports; +mod functions; +mod globals; +mod imports; +mod init; +mod memories; +mod names; +mod operators; +mod producers; +mod tables; +mod tags; +mod types; + +pub use self::code::*; +pub use self::custom::*; +pub use self::data::*; +pub use self::elements::*; +pub use self::exports::*; +pub use self::functions::*; +pub use self::globals::*; +pub use self::imports::*; +pub use self::init::*; +pub use self::memories::*; +pub use self::names::*; +pub use self::operators::*; +pub use self::producers::*; +pub use self::tables::*; +pub use self::tags::*; +pub use self::types::*; diff --git a/third_party/rust/wasmparser/src/readers/core/code.rs b/third_party/rust/wasmparser/src/readers/core/code.rs new file mode 100644 index 0000000000..2a463727e8 --- /dev/null +++ b/third_party/rust/wasmparser/src/readers/core/code.rs @@ -0,0 +1,146 @@ +/* Copyright 2018 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use crate::{BinaryReader, FromReader, OperatorsReader, Result, SectionLimited, ValType}; +use std::ops::Range; + +/// A reader for the code section of a WebAssembly module. +pub type CodeSectionReader<'a> = SectionLimited<'a, FunctionBody<'a>>; + +/// Represents a WebAssembly function body. +#[derive(Debug, Clone)] +pub struct FunctionBody<'a> { + reader: BinaryReader<'a>, +} + +impl<'a> FunctionBody<'a> { + /// Constructs a new `FunctionBody` for the given data and offset. + pub fn new(offset: usize, data: &'a [u8]) -> Self { + Self { + reader: BinaryReader::new_with_offset(data, offset), + } + } + + /// Whether or not to allow 64-bit memory arguments in the + /// function body. + /// + /// This is intended to be `true` when support for the memory64 + /// WebAssembly proposal is also enabled. + pub fn allow_memarg64(&mut self, allow: bool) { + self.reader.allow_memarg64(allow); + } + + /// Gets a binary reader for this function body. + pub fn get_binary_reader(&self) -> BinaryReader<'a> { + self.reader.clone() + } + + fn skip_locals(reader: &mut BinaryReader) -> Result<()> { + let count = reader.read_var_u32()?; + for _ in 0..count { + reader.read_var_u32()?; + reader.read::<ValType>()?; + } + Ok(()) + } + + /// Gets the locals reader for this function body. + pub fn get_locals_reader(&self) -> Result<LocalsReader<'a>> { + let mut reader = self.reader.clone(); + let count = reader.read_var_u32()?; + Ok(LocalsReader { reader, count }) + } + + /// Gets the operators reader for this function body. + pub fn get_operators_reader(&self) -> Result<OperatorsReader<'a>> { + let mut reader = self.reader.clone(); + Self::skip_locals(&mut reader)?; + Ok(OperatorsReader::new(reader)) + } + + /// Gets the range of the function body. + pub fn range(&self) -> Range<usize> { + self.reader.range() + } +} + +impl<'a> FromReader<'a> for FunctionBody<'a> { + fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> { + let reader = reader.read_reader("function body extends past end of the code section")?; + Ok(FunctionBody { reader }) + } +} + +/// A reader for a function body's locals. +pub struct LocalsReader<'a> { + reader: BinaryReader<'a>, + count: u32, +} + +impl<'a> LocalsReader<'a> { + /// Gets the count of locals in the function body. + pub fn get_count(&self) -> u32 { + self.count + } + + /// Gets the original position of the reader. + pub fn original_position(&self) -> usize { + self.reader.original_position() + } + + /// Reads an item from the reader. + pub fn read(&mut self) -> Result<(u32, ValType)> { + let count = self.reader.read()?; + let value_type = self.reader.read()?; + Ok((count, value_type)) + } +} + +impl<'a> IntoIterator for LocalsReader<'a> { + type Item = Result<(u32, ValType)>; + type IntoIter = LocalsIterator<'a>; + fn into_iter(self) -> Self::IntoIter { + let count = self.count; + LocalsIterator { + reader: self, + left: count, + err: false, + } + } +} + +/// An iterator over locals in a function body. +pub struct LocalsIterator<'a> { + reader: LocalsReader<'a>, + left: u32, + err: bool, +} + +impl<'a> Iterator for LocalsIterator<'a> { + type Item = Result<(u32, ValType)>; + fn next(&mut self) -> Option<Self::Item> { + if self.err || self.left == 0 { + return None; + } + let result = self.reader.read(); + self.err = result.is_err(); + self.left -= 1; + Some(result) + } + fn size_hint(&self) -> (usize, Option<usize>) { + let count = self.reader.get_count() as usize; + (count, Some(count)) + } +} diff --git a/third_party/rust/wasmparser/src/readers/core/custom.rs b/third_party/rust/wasmparser/src/readers/core/custom.rs new file mode 100644 index 0000000000..a04fe5a1ac --- /dev/null +++ b/third_party/rust/wasmparser/src/readers/core/custom.rs @@ -0,0 +1,63 @@ +use crate::{BinaryReader, Result}; +use std::ops::Range; + +/// A reader for custom sections of a WebAssembly module. +#[derive(Clone)] +pub struct CustomSectionReader<'a> { + // NB: these fields are public to the crate to make testing easier. + pub(crate) name: &'a str, + pub(crate) data_offset: usize, + pub(crate) data: &'a [u8], + pub(crate) range: Range<usize>, +} + +impl<'a> CustomSectionReader<'a> { + /// Constructs a new `CustomSectionReader` for the given data and offset. + pub fn new(data: &'a [u8], offset: usize) -> Result<CustomSectionReader<'a>> { + let mut reader = BinaryReader::new_with_offset(data, offset); + let name = reader.read_string()?; + let data_offset = reader.original_position(); + let data = reader.remaining_buffer(); + let range = reader.range(); + Ok(CustomSectionReader { + name, + data_offset, + data, + range, + }) + } + + /// The name of the custom section. + pub fn name(&self) -> &'a str { + self.name + } + + /// The offset, relative to the start of the original module or component, + /// that the `data` payload for this custom section starts at. + pub fn data_offset(&self) -> usize { + self.data_offset + } + + /// The actual contents of the custom section. + pub fn data(&self) -> &'a [u8] { + self.data + } + + /// The range of bytes that specify this whole custom section (including + /// both the name of this custom section and its data) specified in + /// offsets relative to the start of the byte stream. + pub fn range(&self) -> Range<usize> { + self.range.clone() + } +} + +impl<'a> std::fmt::Debug for CustomSectionReader<'a> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("CustomSectionReader") + .field("name", &self.name) + .field("data_offset", &self.data_offset) + .field("data", &"...") + .field("range", &self.range) + .finish() + } +} diff --git a/third_party/rust/wasmparser/src/readers/core/data.rs b/third_party/rust/wasmparser/src/readers/core/data.rs new file mode 100644 index 0000000000..5ea5f99457 --- /dev/null +++ b/third_party/rust/wasmparser/src/readers/core/data.rs @@ -0,0 +1,96 @@ +/* Copyright 2018 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use crate::{BinaryReader, BinaryReaderError, ConstExpr, FromReader, Result, SectionLimited}; +use std::ops::Range; + +/// Represents a data segment in a core WebAssembly module. +#[derive(Debug, Clone)] +pub struct Data<'a> { + /// The kind of data segment. + pub kind: DataKind<'a>, + /// The data of the data segment. + pub data: &'a [u8], + /// The range of the data segment. + pub range: Range<usize>, +} + +/// The kind of data segment. +#[derive(Debug, Copy, Clone)] +pub enum DataKind<'a> { + /// The data segment is passive. + Passive, + /// The data segment is active. + Active { + /// The memory index for the data segment. + memory_index: u32, + /// The initialization expression for the data segment. + offset_expr: ConstExpr<'a>, + }, +} + +/// A reader for the data section of a WebAssembly module. +pub type DataSectionReader<'a> = SectionLimited<'a, Data<'a>>; + +impl<'a> FromReader<'a> for Data<'a> { + fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> { + let segment_start = reader.original_position(); + + // The current handling of the flags is largely specified in the `bulk-memory` proposal, + // which at the time this comment is written has been merged to the main specification + // draft. + // + // Notably, this proposal allows multiple different encodings of the memory index 0. `00` + // and `02 00` are both valid ways to specify the 0-th memory. However it also makes + // another encoding of the 0-th memory `80 00` no longer valid. + // + // We, however maintain this by parsing `flags` as a LEB128 integer. In that case, `80 00` + // encoding is parsed out as `0` and is therefore assigned a `memidx` 0, even though the + // current specification draft does not allow for this. + // + // See also https://github.com/WebAssembly/spec/issues/1439 + let flags = reader.read_var_u32()?; + let kind = match flags { + 1 => DataKind::Passive, + 0 | 2 => { + let memory_index = if flags == 0 { + 0 + } else { + reader.read_var_u32()? + }; + let offset_expr = reader.read()?; + DataKind::Active { + memory_index, + offset_expr, + } + } + _ => { + return Err(BinaryReaderError::new( + "invalid flags byte in data segment", + segment_start, + )); + } + }; + + let data = reader.read_reader( + "unexpected end of section or function: data segment extends past end of the section", + )?; + Ok(Data { + kind, + data: data.remaining_buffer(), + range: segment_start..data.range().end, + }) + } +} diff --git a/third_party/rust/wasmparser/src/readers/core/elements.rs b/third_party/rust/wasmparser/src/readers/core/elements.rs new file mode 100644 index 0000000000..7e37e7d7b6 --- /dev/null +++ b/third_party/rust/wasmparser/src/readers/core/elements.rs @@ -0,0 +1,158 @@ +/* Copyright 2018 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use crate::{ + BinaryReader, BinaryReaderError, ConstExpr, ExternalKind, FromReader, RefType, Result, + SectionLimited, +}; +use std::ops::Range; + +/// Represents a core WebAssembly element segment. +#[derive(Clone)] +pub struct Element<'a> { + /// The kind of the element segment. + pub kind: ElementKind<'a>, + /// The initial elements of the element segment. + pub items: ElementItems<'a>, + /// The type of the elements. + pub ty: RefType, + /// The range of the the element segment. + pub range: Range<usize>, +} + +/// The kind of element segment. +#[derive(Clone)] +pub enum ElementKind<'a> { + /// The element segment is passive. + Passive, + /// The element segment is active. + Active { + /// The index of the table being initialized. + table_index: u32, + /// The initial expression of the element segment. + offset_expr: ConstExpr<'a>, + }, + /// The element segment is declared. + Declared, +} + +/// Represents the items of an element segment. +#[derive(Clone)] +pub enum ElementItems<'a> { + /// This element contains function indices. + Functions(SectionLimited<'a, u32>), + /// This element contains constant expressions used to initialize the table. + Expressions(SectionLimited<'a, ConstExpr<'a>>), +} + +/// A reader for the element section of a WebAssembly module. +pub type ElementSectionReader<'a> = SectionLimited<'a, Element<'a>>; + +impl<'a> FromReader<'a> for Element<'a> { + fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> { + let elem_start = reader.original_position(); + // The current handling of the flags is largely specified in the `bulk-memory` proposal, + // which at the time this commend is written has been merged to the main specification + // draft. + // + // Notably, this proposal allows multiple different encodings of the table index 0. `00` + // and `02 00` are both valid ways to specify the 0-th table. However it also makes + // another encoding of the 0-th memory `80 00` no longer valid. + // + // We, however maintain this support by parsing `flags` as a LEB128 integer. In that case, + // `80 00` encoding is parsed out as `0` and is therefore assigned a `tableidx` 0, even + // though the current specification draft does not allow for this. + // + // See also https://github.com/WebAssembly/spec/issues/1439 + let flags = reader.read_var_u32()?; + if (flags & !0b111) != 0 { + return Err(BinaryReaderError::new( + "invalid flags byte in element segment", + reader.original_position() - 1, + )); + } + let kind = if flags & 0b001 != 0 { + if flags & 0b010 != 0 { + ElementKind::Declared + } else { + ElementKind::Passive + } + } else { + let table_index = if flags & 0b010 == 0 { + 0 + } else { + reader.read_var_u32()? + }; + let offset_expr = reader.read()?; + ElementKind::Active { + table_index, + offset_expr, + } + }; + let exprs = flags & 0b100 != 0; + let ty = if flags & 0b011 != 0 { + if exprs { + reader.read()? + } else { + match reader.read()? { + ExternalKind::Func => RefType::FUNCREF, + _ => { + return Err(BinaryReaderError::new( + "only the function external type is supported in elem segment", + reader.original_position() - 1, + )); + } + } + } + } else { + RefType::FUNCREF + }; + // FIXME(#188) ideally wouldn't have to do skips here + let data = reader.skip(|reader| { + let items_count = reader.read_var_u32()?; + if exprs { + for _ in 0..items_count { + reader.skip_const_expr()?; + } + } else { + for _ in 0..items_count { + reader.read_var_u32()?; + } + } + Ok(()) + })?; + let items = if exprs { + ElementItems::Expressions(SectionLimited::new( + data.remaining_buffer(), + data.original_position(), + )?) + } else { + ElementItems::Functions(SectionLimited::new( + data.remaining_buffer(), + data.original_position(), + )?) + }; + + let elem_end = reader.original_position(); + let range = elem_start..elem_end; + + Ok(Element { + kind, + items, + ty, + range, + }) + } +} diff --git a/third_party/rust/wasmparser/src/readers/core/exports.rs b/third_party/rust/wasmparser/src/readers/core/exports.rs new file mode 100644 index 0000000000..c1bd62626b --- /dev/null +++ b/third_party/rust/wasmparser/src/readers/core/exports.rs @@ -0,0 +1,65 @@ +/* Copyright 2018 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use crate::{BinaryReader, FromReader, Result, SectionLimited}; + +/// A reader for the export section of a WebAssembly module. +pub type ExportSectionReader<'a> = SectionLimited<'a, Export<'a>>; + +/// External types as defined [here]. +/// +/// [here]: https://webassembly.github.io/spec/core/syntax/types.html#external-types +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum ExternalKind { + /// The external kind is a function. + Func, + /// The external kind if a table. + Table, + /// The external kind is a memory. + Memory, + /// The external kind is a global. + Global, + /// The external kind is a tag. + Tag, +} + +/// Represents an export in a WebAssembly module. +#[derive(Debug, Copy, Clone)] +pub struct Export<'a> { + /// The name of the exported item. + pub name: &'a str, + /// The kind of the export. + pub kind: ExternalKind, + /// The index of the exported item. + pub index: u32, +} + +impl<'a> FromReader<'a> for Export<'a> { + fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> { + Ok(Export { + name: reader.read_string()?, + kind: reader.read()?, + index: reader.read_var_u32()?, + }) + } +} + +impl<'a> FromReader<'a> for ExternalKind { + fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> { + let offset = reader.original_position(); + let byte = reader.read_u8()?; + BinaryReader::external_kind_from_byte(byte, offset) + } +} diff --git a/third_party/rust/wasmparser/src/readers/core/functions.rs b/third_party/rust/wasmparser/src/readers/core/functions.rs new file mode 100644 index 0000000000..ebddce05a3 --- /dev/null +++ b/third_party/rust/wasmparser/src/readers/core/functions.rs @@ -0,0 +1,17 @@ +/* Copyright 2018 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/// A reader for the function section of a WebAssembly module. +pub type FunctionSectionReader<'a> = crate::SectionLimited<'a, u32>; diff --git a/third_party/rust/wasmparser/src/readers/core/globals.rs b/third_party/rust/wasmparser/src/readers/core/globals.rs new file mode 100644 index 0000000000..6fd99bc0b8 --- /dev/null +++ b/third_party/rust/wasmparser/src/readers/core/globals.rs @@ -0,0 +1,49 @@ +/* Copyright 2018 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use crate::{BinaryReader, ConstExpr, FromReader, GlobalType, Result, SectionLimited}; + +/// Represents a core WebAssembly global. +#[derive(Debug, Copy, Clone)] +pub struct Global<'a> { + /// The global's type. + pub ty: GlobalType, + /// The global's initialization expression. + pub init_expr: ConstExpr<'a>, +} + +/// A reader for the global section of a WebAssembly module. +pub type GlobalSectionReader<'a> = SectionLimited<'a, Global<'a>>; + +impl<'a> FromReader<'a> for Global<'a> { + fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> { + let ty = reader.read()?; + let init_expr = reader.read()?; + Ok(Global { ty, init_expr }) + } +} + +impl<'a> FromReader<'a> for GlobalType { + fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> { + Ok(GlobalType { + content_type: reader.read()?, + mutable: match reader.read_u8()? { + 0x00 => false, + 0x01 => true, + _ => bail!(reader.original_position() - 1, "malformed mutability",), + }, + }) + } +} diff --git a/third_party/rust/wasmparser/src/readers/core/imports.rs b/third_party/rust/wasmparser/src/readers/core/imports.rs new file mode 100644 index 0000000000..d2a33c89e3 --- /dev/null +++ b/third_party/rust/wasmparser/src/readers/core/imports.rs @@ -0,0 +1,76 @@ +/* Copyright 2018 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use crate::{ + BinaryReader, ExternalKind, FromReader, GlobalType, MemoryType, Result, SectionLimited, + TableType, TagType, +}; + +/// Represents a reference to a type definition in a WebAssembly module. +#[derive(Debug, Clone, Copy)] +pub enum TypeRef { + /// The type is a function. + /// + /// The value is an index into the type section. + Func(u32), + /// The type is a table. + Table(TableType), + /// The type is a memory. + Memory(MemoryType), + /// The type is a global. + Global(GlobalType), + /// The type is a tag. + /// + /// This variant is only used for the exception handling proposal. + /// + /// The value is an index in the types index space. + Tag(TagType), +} + +/// Represents an import in a WebAssembly module. +#[derive(Debug, Copy, Clone)] +pub struct Import<'a> { + /// The module being imported from. + pub module: &'a str, + /// The name of the imported item. + pub name: &'a str, + /// The type of the imported item. + pub ty: TypeRef, +} + +/// A reader for the import section of a WebAssembly module. +pub type ImportSectionReader<'a> = SectionLimited<'a, Import<'a>>; + +impl<'a> FromReader<'a> for Import<'a> { + fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> { + Ok(Import { + module: reader.read()?, + name: reader.read()?, + ty: reader.read()?, + }) + } +} + +impl<'a> FromReader<'a> for TypeRef { + fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> { + Ok(match reader.read()? { + ExternalKind::Func => TypeRef::Func(reader.read_var_u32()?), + ExternalKind::Table => TypeRef::Table(reader.read()?), + ExternalKind::Memory => TypeRef::Memory(reader.read()?), + ExternalKind::Global => TypeRef::Global(reader.read()?), + ExternalKind::Tag => TypeRef::Tag(reader.read()?), + }) + } +} diff --git a/third_party/rust/wasmparser/src/readers/core/init.rs b/third_party/rust/wasmparser/src/readers/core/init.rs new file mode 100644 index 0000000000..fcd3bd73c9 --- /dev/null +++ b/third_party/rust/wasmparser/src/readers/core/init.rs @@ -0,0 +1,51 @@ +/* Copyright 2018 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use crate::{BinaryReader, FromReader, OperatorsReader, Result}; + +/// Represents an initialization expression. +#[derive(Debug, Copy, Clone)] +pub struct ConstExpr<'a> { + offset: usize, + data: &'a [u8], +} + +impl<'a> ConstExpr<'a> { + /// Constructs a new `ConstExpr` from the given data and offset. + pub fn new(data: &[u8], offset: usize) -> ConstExpr { + ConstExpr { offset, data } + } + + /// Gets a binary reader for the initialization expression. + pub fn get_binary_reader(&self) -> BinaryReader<'a> { + BinaryReader::new_with_offset(self.data, self.offset) + } + + /// Gets an operators reader for the initialization expression. + pub fn get_operators_reader(&self) -> OperatorsReader<'a> { + OperatorsReader::new(self.get_binary_reader()) + } +} + +impl<'a> FromReader<'a> for ConstExpr<'a> { + fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> { + // FIXME(#188) ideally shouldn't need to skip here + let reader = reader.skip(|r| r.skip_const_expr())?; + Ok(ConstExpr::new( + reader.remaining_buffer(), + reader.original_position(), + )) + } +} diff --git a/third_party/rust/wasmparser/src/readers/core/memories.rs b/third_party/rust/wasmparser/src/readers/core/memories.rs new file mode 100644 index 0000000000..d1941b1cdc --- /dev/null +++ b/third_party/rust/wasmparser/src/readers/core/memories.rs @@ -0,0 +1,56 @@ +/* Copyright 2018 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use crate::{BinaryReader, FromReader, MemoryType, Result, SectionLimited}; + +/// A reader for the memory section of a WebAssembly module. +pub type MemorySectionReader<'a> = SectionLimited<'a, MemoryType>; + +impl<'a> FromReader<'a> for MemoryType { + fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> { + let pos = reader.original_position(); + let flags = reader.read_u8()?; + if (flags & !0b111) != 0 { + bail!(pos, "invalid memory limits flags"); + } + + let memory64 = flags & 0b100 != 0; + let shared = flags & 0b010 != 0; + let has_max = flags & 0b001 != 0; + Ok(MemoryType { + memory64, + shared, + // FIXME(WebAssembly/memory64#21) as currently specified if the + // `shared` flag is set we should be reading a 32-bit limits field + // here. That seems a bit odd to me at the time of this writing so + // I've taken the liberty of reading a 64-bit limits field in those + // situations. I suspect that this is a typo in the spec, but if not + // we'll need to update this to read a 32-bit limits field when the + // shared flag is set. + initial: if memory64 { + reader.read_var_u64()? + } else { + reader.read_var_u32()?.into() + }, + maximum: if !has_max { + None + } else if memory64 { + Some(reader.read_var_u64()?) + } else { + Some(reader.read_var_u32()?.into()) + }, + }) + } +} diff --git a/third_party/rust/wasmparser/src/readers/core/names.rs b/third_party/rust/wasmparser/src/readers/core/names.rs new file mode 100644 index 0000000000..aa8a11dde2 --- /dev/null +++ b/third_party/rust/wasmparser/src/readers/core/names.rs @@ -0,0 +1,153 @@ +/* Copyright 2018 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use crate::{ + BinaryReader, BinaryReaderError, FromReader, Result, SectionLimited, Subsection, Subsections, +}; +use std::ops::Range; + +/// Represents a name map from the names custom section. +pub type NameMap<'a> = SectionLimited<'a, Naming<'a>>; + +/// Represents a name for an index from the names section. +#[derive(Debug, Copy, Clone)] +pub struct Naming<'a> { + /// The index being named. + pub index: u32, + /// The name for the index. + pub name: &'a str, +} + +impl<'a> FromReader<'a> for Naming<'a> { + fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> { + let index = reader.read_var_u32()?; + let name = reader.read_string()?; + Ok(Naming { index, name }) + } +} + +/// Represents a reader for indirect names from the names custom section. +pub type IndirectNameMap<'a> = SectionLimited<'a, IndirectNaming<'a>>; + +/// Represents an indirect name in the names custom section. +#[derive(Debug, Clone)] +pub struct IndirectNaming<'a> { + /// The indirect index of the name. + pub index: u32, + /// The map of names within the `index` prior. + pub names: NameMap<'a>, +} + +impl<'a> FromReader<'a> for IndirectNaming<'a> { + fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> { + let index = reader.read_var_u32()?; + + // Skip the `NameMap` manually here. + // + // FIXME(#188) shouldn't need to skip here + let names = reader.skip(|reader| { + let count = reader.read_var_u32()?; + for _ in 0..count { + reader.read_var_u32()?; + reader.skip_string()?; + } + Ok(()) + })?; + + Ok(IndirectNaming { + index, + names: NameMap::new(names.remaining_buffer(), names.original_position())?, + }) + } +} + +/// Represents a name read from the names custom section. +#[derive(Clone)] +pub enum Name<'a> { + /// The name is for the module. + Module { + /// The specified name. + name: &'a str, + /// The byte range that `name` occupies in the original binary. + name_range: Range<usize>, + }, + /// The name is for the functions. + Function(NameMap<'a>), + /// The name is for the function locals. + Local(IndirectNameMap<'a>), + /// The name is for the function labels. + Label(IndirectNameMap<'a>), + /// The name is for the types. + Type(NameMap<'a>), + /// The name is for the tables. + Table(NameMap<'a>), + /// The name is for the memories. + Memory(NameMap<'a>), + /// The name is for the globals. + Global(NameMap<'a>), + /// The name is for the element segments. + Element(NameMap<'a>), + /// The name is for the data segments. + Data(NameMap<'a>), + /// An unknown [name subsection](https://webassembly.github.io/spec/core/appendix/custom.html#subsections). + Unknown { + /// The identifier for this subsection. + ty: u8, + /// The contents of this subsection. + data: &'a [u8], + /// The range of bytes, relative to the start of the original data + /// stream, that the contents of this subsection reside in. + range: Range<usize>, + }, +} + +/// A reader for the name custom section of a WebAssembly module. +pub type NameSectionReader<'a> = Subsections<'a, Name<'a>>; + +impl<'a> Subsection<'a> for Name<'a> { + fn from_reader(id: u8, mut reader: BinaryReader<'a>) -> Result<Self> { + let data = reader.remaining_buffer(); + let offset = reader.original_position(); + Ok(match id { + 0 => { + let name = reader.read_string()?; + if !reader.eof() { + return Err(BinaryReaderError::new( + "trailing data at the end of a name", + reader.original_position(), + )); + } + Name::Module { + name, + name_range: offset..offset + reader.position, + } + } + 1 => Name::Function(NameMap::new(data, offset)?), + 2 => Name::Local(IndirectNameMap::new(data, offset)?), + 3 => Name::Label(IndirectNameMap::new(data, offset)?), + 4 => Name::Type(NameMap::new(data, offset)?), + 5 => Name::Table(NameMap::new(data, offset)?), + 6 => Name::Memory(NameMap::new(data, offset)?), + 7 => Name::Global(NameMap::new(data, offset)?), + 8 => Name::Element(NameMap::new(data, offset)?), + 9 => Name::Data(NameMap::new(data, offset)?), + ty => Name::Unknown { + ty, + data, + range: offset..offset + data.len(), + }, + }) + } +} diff --git a/third_party/rust/wasmparser/src/readers/core/operators.rs b/third_party/rust/wasmparser/src/readers/core/operators.rs new file mode 100644 index 0000000000..d1312c259f --- /dev/null +++ b/third_party/rust/wasmparser/src/readers/core/operators.rs @@ -0,0 +1,354 @@ +/* Copyright 2018 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use crate::{BinaryReader, BinaryReaderError, Result, ValType}; + +/// Represents a block type. +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum BlockType { + /// The block produces consumes nor produces any values. + Empty, + /// The block produces a singular value of the given type ([] -> \[t]). + Type(ValType), + /// The block is described by a function type. + /// + /// The index is to a function type in the types section. + FuncType(u32), +} + +/// Represents a memory immediate in a WebAssembly memory instruction. +#[derive(Debug, Copy, Clone)] +pub struct MemArg { + /// Alignment, stored as `n` where the actual alignment is `2^n` + pub align: u8, + /// Maximum alignment, stored as `n` where the actual alignment is `2^n`. + /// + /// Note that this field is not actually read from the binary format, it + /// will be a constant depending on which instruction this `MemArg` is a + /// payload for. + pub max_align: u8, + /// A fixed byte-offset that this memory immediate specifies. + /// + /// Note that the memory64 proposal can specify a full 64-bit byte offset + /// while otherwise only 32-bit offsets are allowed. Once validated + /// memory immediates for 32-bit memories are guaranteed to be at most + /// `u32::MAX` whereas 64-bit memories can use the full 64-bits. + pub offset: u64, + /// The index of the memory this immediate points to. + /// + /// Note that this points within the module's own memory index space, and + /// is always zero unless the multi-memory proposal of WebAssembly is + /// enabled. + pub memory: u32, +} + +/// A br_table entries representation. +#[derive(Clone)] +pub struct BrTable<'a> { + pub(crate) reader: crate::BinaryReader<'a>, + pub(crate) cnt: u32, + pub(crate) default: u32, +} + +/// An IEEE binary32 immediate floating point value, represented as a u32 +/// containing the bit pattern. +/// +/// All bit patterns are allowed. +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] +pub struct Ieee32(pub(crate) u32); + +impl Ieee32 { + /// Gets the underlying bits of the 32-bit float. + pub fn bits(self) -> u32 { + self.0 + } +} + +/// An IEEE binary64 immediate floating point value, represented as a u64 +/// containing the bit pattern. +/// +/// All bit patterns are allowed. +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] +pub struct Ieee64(pub(crate) u64); + +impl Ieee64 { + /// Gets the underlying bits of the 64-bit float. + pub fn bits(self) -> u64 { + self.0 + } +} + +/// Represents a 128-bit vector value. +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] +pub struct V128(pub(crate) [u8; 16]); + +impl V128 { + /// Gets the bytes of the vector value. + pub fn bytes(&self) -> &[u8; 16] { + &self.0 + } + + /// Gets a signed 128-bit integer value from the vector's bytes. + pub fn i128(&self) -> i128 { + i128::from_le_bytes(self.0) + } +} + +macro_rules! define_operator { + ($(@$proposal:ident $op:ident $({ $($payload:tt)* })? => $visit:ident)*) => { + /// Instructions as defined [here]. + /// + /// [here]: https://webassembly.github.io/spec/core/binary/instructions.html + #[derive(Debug, Clone)] + #[allow(missing_docs)] + pub enum Operator<'a> { + $( + $op $({ $($payload)* })?, + )* + } + } +} +for_each_operator!(define_operator); + +/// A reader for a core WebAssembly function's operators. +#[derive(Clone)] +pub struct OperatorsReader<'a> { + pub(crate) reader: BinaryReader<'a>, +} + +impl<'a> OperatorsReader<'a> { + pub(crate) fn new(reader: BinaryReader<'a>) -> OperatorsReader<'a> { + OperatorsReader { reader } + } + + /// Determines if the reader is at the end of the operators. + pub fn eof(&self) -> bool { + self.reader.eof() + } + + /// Gets the original position of the reader. + pub fn original_position(&self) -> usize { + self.reader.original_position() + } + + /// Whether or not to allow 64-bit memory arguments in the + /// the operators being read. + /// + /// This is intended to be `true` when support for the memory64 + /// WebAssembly proposal is also enabled. + pub fn allow_memarg64(&mut self, allow: bool) { + self.reader.allow_memarg64(allow); + } + + /// Ensures the reader is at the end. + /// + /// This function returns an error if there is extra data after the operators. + pub fn ensure_end(&self) -> Result<()> { + if self.eof() { + return Ok(()); + } + Err(BinaryReaderError::new( + "unexpected data at the end of operators", + self.reader.original_position(), + )) + } + + /// Reads an operator from the reader. + pub fn read(&mut self) -> Result<Operator<'a>> { + self.reader.read_operator() + } + + /// Converts to an iterator of operators paired with offsets. + pub fn into_iter_with_offsets(self) -> OperatorsIteratorWithOffsets<'a> { + OperatorsIteratorWithOffsets { + reader: self, + err: false, + } + } + + /// Reads an operator with its offset. + pub fn read_with_offset(&mut self) -> Result<(Operator<'a>, usize)> { + let pos = self.reader.original_position(); + Ok((self.read()?, pos)) + } + + /// Visit a single operator with the specified [`VisitOperator`] instance. + /// + /// See [`BinaryReader::visit_operator`] for more information. + pub fn visit_operator<T>(&mut self, visitor: &mut T) -> Result<<T as VisitOperator<'a>>::Output> + where + T: VisitOperator<'a>, + { + self.reader.visit_operator(visitor) + } + + /// Gets a binary reader from this operators reader. + pub fn get_binary_reader(&self) -> BinaryReader<'a> { + self.reader.clone() + } +} + +impl<'a> IntoIterator for OperatorsReader<'a> { + type Item = Result<Operator<'a>>; + type IntoIter = OperatorsIterator<'a>; + + /// Reads content of the code section. + /// + /// # Examples + /// ``` + /// use wasmparser::{Operator, CodeSectionReader, Result}; + /// # let data: &[u8] = &[ + /// # 0x01, 0x03, 0x00, 0x01, 0x0b]; + /// let code_reader = CodeSectionReader::new(data, 0).unwrap(); + /// for body in code_reader { + /// let body = body.expect("function body"); + /// let mut op_reader = body.get_operators_reader().expect("op reader"); + /// let ops = op_reader.into_iter().collect::<Result<Vec<Operator>>>().expect("ops"); + /// assert!( + /// if let [Operator::Nop, Operator::End] = ops.as_slice() { true } else { false }, + /// "found {:?}", + /// ops + /// ); + /// } + /// ``` + fn into_iter(self) -> Self::IntoIter { + OperatorsIterator { + reader: self, + err: false, + } + } +} + +/// An iterator over a function's operators. +pub struct OperatorsIterator<'a> { + reader: OperatorsReader<'a>, + err: bool, +} + +impl<'a> Iterator for OperatorsIterator<'a> { + type Item = Result<Operator<'a>>; + + fn next(&mut self) -> Option<Self::Item> { + if self.err || self.reader.eof() { + return None; + } + let result = self.reader.read(); + self.err = result.is_err(); + Some(result) + } +} + +/// An iterator over a function's operators with offsets. +pub struct OperatorsIteratorWithOffsets<'a> { + reader: OperatorsReader<'a>, + err: bool, +} + +impl<'a> Iterator for OperatorsIteratorWithOffsets<'a> { + type Item = Result<(Operator<'a>, usize)>; + + /// Reads content of the code section with offsets. + /// + /// # Examples + /// ``` + /// use wasmparser::{Operator, CodeSectionReader, Result}; + /// # let data: &[u8] = &[ + /// # 0x01, 0x03, 0x00, /* offset = 23 */ 0x01, 0x0b]; + /// let code_reader = CodeSectionReader::new(data, 20).unwrap(); + /// for body in code_reader { + /// let body = body.expect("function body"); + /// let mut op_reader = body.get_operators_reader().expect("op reader"); + /// let ops = op_reader.into_iter_with_offsets().collect::<Result<Vec<(Operator, usize)>>>().expect("ops"); + /// assert!( + /// if let [(Operator::Nop, 23), (Operator::End, 24)] = ops.as_slice() { true } else { false }, + /// "found {:?}", + /// ops + /// ); + /// } + /// ``` + fn next(&mut self) -> Option<Self::Item> { + if self.err || self.reader.eof() { + return None; + } + let result = self.reader.read_with_offset(); + self.err = result.is_err(); + Some(result) + } +} + +macro_rules! define_visit_operator { + ($(@$proposal:ident $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident)*) => { + $( + fn $visit(&mut self $($(,$arg: $argty)*)?) -> Self::Output; + )* + } +} + +/// Trait implemented by types that can visit all [`Operator`] variants. +#[allow(missing_docs)] +pub trait VisitOperator<'a> { + /// The result type of the visitor. + type Output: 'a; + + /// Visits the [`Operator`] `op` using the given `offset`. + /// + /// # Note + /// + /// This is a convenience method that is intended for non-performance + /// critical use cases. For performance critical implementations users + /// are recommended to directly use the respective `visit` methods or + /// implement [`VisitOperator`] on their own. + fn visit_operator(&mut self, op: &Operator<'a>) -> Self::Output { + macro_rules! visit_operator { + ($(@$proposal:ident $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident)*) => { + match op { + $( + Operator::$op $({ $($arg),* })? => self.$visit($($($arg.clone()),*)?), + )* + } + } + + } + for_each_operator!(visit_operator) + } + + for_each_operator!(define_visit_operator); +} + +macro_rules! define_visit_operator_delegate { + ($(@$proposal:ident $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident)*) => { + $( + fn $visit(&mut self $($(,$arg: $argty)*)?) -> Self::Output { + V::$visit(&mut *self, $($($arg),*)?) + } + )* + } +} + +impl<'a, 'b, V: VisitOperator<'a> + ?Sized> VisitOperator<'a> for &'b mut V { + type Output = V::Output; + fn visit_operator(&mut self, op: &Operator<'a>) -> Self::Output { + V::visit_operator(*self, op) + } + for_each_operator!(define_visit_operator_delegate); +} + +impl<'a, V: VisitOperator<'a> + ?Sized> VisitOperator<'a> for Box<V> { + type Output = V::Output; + fn visit_operator(&mut self, op: &Operator<'a>) -> Self::Output { + V::visit_operator(&mut *self, op) + } + for_each_operator!(define_visit_operator_delegate); +} diff --git a/third_party/rust/wasmparser/src/readers/core/producers.rs b/third_party/rust/wasmparser/src/readers/core/producers.rs new file mode 100644 index 0000000000..07785ed75a --- /dev/null +++ b/third_party/rust/wasmparser/src/readers/core/producers.rs @@ -0,0 +1,78 @@ +/* Copyright 2019 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use crate::{BinaryReader, FromReader, Result, SectionLimited}; + +/// A reader for the producers custom section of a WebAssembly module. +/// +/// # Examples +/// +/// ``` +/// # let data: &[u8] = &[0x01, 0x08, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, +/// # 0x02, 0x03, 0x77, 0x61, 0x74, 0x01, 0x31, 0x01, 0x43, 0x03, 0x39, 0x2e, 0x30]; +/// use wasmparser::{ProducersSectionReader, ProducersFieldValue, Result}; +/// let reader = ProducersSectionReader::new(data, 0).expect("producers reader"); +/// let field = reader.into_iter().next().unwrap().expect("producers field"); +/// assert!(field.name == "language"); +/// let value = field.values.into_iter().collect::<Result<Vec<_>>>().expect("values"); +/// assert!(value.len() == 2); +/// assert!(value[0].name == "wat" && value[0].version == "1"); +/// assert!(value[1].name == "C" && value[1].version == "9.0"); +/// ``` +pub type ProducersSectionReader<'a> = SectionLimited<'a, ProducersField<'a>>; + +/// A field from the producers custom section. +#[derive(Debug, Clone)] +pub struct ProducersField<'a> { + /// The name of the field. + pub name: &'a str, + /// The values specified for this field + pub values: SectionLimited<'a, ProducersFieldValue<'a>>, +} + +impl<'a> FromReader<'a> for ProducersField<'a> { + fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> { + let name = reader.read_string()?; + let values = reader.skip(|reader| { + // FIXME(#188) ideally shouldn't need to skip here + for _ in 0..reader.read_var_u32()? { + reader.skip_string()?; + reader.skip_string()?; + } + Ok(()) + })?; + Ok(ProducersField { + name, + values: SectionLimited::new(values.remaining_buffer(), values.original_position())?, + }) + } +} + +/// Represents a field value in the producers custom section. +#[derive(Debug, Copy, Clone)] +pub struct ProducersFieldValue<'a> { + /// The field name. + pub name: &'a str, + /// The field version. + pub version: &'a str, +} + +impl<'a> FromReader<'a> for ProducersFieldValue<'a> { + fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> { + let name = reader.read_string()?; + let version = reader.read_string()?; + Ok(ProducersFieldValue { name, version }) + } +} diff --git a/third_party/rust/wasmparser/src/readers/core/tables.rs b/third_party/rust/wasmparser/src/readers/core/tables.rs new file mode 100644 index 0000000000..211e415efd --- /dev/null +++ b/third_party/rust/wasmparser/src/readers/core/tables.rs @@ -0,0 +1,87 @@ +/* Copyright 2018 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use crate::{BinaryReader, ConstExpr, FromReader, Result, SectionLimited, TableType}; + +/// A reader for the table section of a WebAssembly module. +pub type TableSectionReader<'a> = SectionLimited<'a, Table<'a>>; + +/// Type information about a table defined in the table section of a WebAssembly +/// module. +#[derive(Debug)] +pub struct Table<'a> { + /// The type of this table, including its element type and its limits. + pub ty: TableType, + /// The initialization expression for the table. + pub init: TableInit<'a>, +} + +/// Different modes of initializing a table. +#[derive(Debug)] +pub enum TableInit<'a> { + /// The table is initialized to all null elements. + RefNull, + /// Each element in the table is initialized with the specified constant + /// expression. + Expr(ConstExpr<'a>), +} + +impl<'a> FromReader<'a> for Table<'a> { + fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> { + let has_init_expr = if reader.peek()? == 0x40 { + reader.read_u8()?; + true + } else { + false + }; + + if has_init_expr { + if reader.read_u8()? != 0x00 { + bail!(reader.original_position() - 1, "invalid table encoding"); + } + } + + let ty = reader.read::<TableType>()?; + let init = if has_init_expr { + TableInit::Expr(reader.read()?) + } else { + TableInit::RefNull + }; + Ok(Table { ty, init }) + } +} + +impl<'a> FromReader<'a> for TableType { + fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> { + let element_type = reader.read()?; + let has_max = match reader.read_u8()? { + 0x00 => false, + 0x01 => true, + _ => { + bail!( + reader.original_position() - 1, + "invalid table resizable limits flags", + ) + } + }; + let initial = reader.read()?; + let maximum = if has_max { Some(reader.read()?) } else { None }; + Ok(TableType { + element_type, + initial, + maximum, + }) + } +} diff --git a/third_party/rust/wasmparser/src/readers/core/tags.rs b/third_party/rust/wasmparser/src/readers/core/tags.rs new file mode 100644 index 0000000000..746b3ea7ac --- /dev/null +++ b/third_party/rust/wasmparser/src/readers/core/tags.rs @@ -0,0 +1,32 @@ +/* Copyright 2020 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use crate::{BinaryReader, FromReader, Result, SectionLimited, TagKind, TagType}; + +/// A reader for the tags section of a WebAssembly module. +pub type TagSectionReader<'a> = SectionLimited<'a, TagType>; + +impl<'a> FromReader<'a> for TagType { + fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> { + let attribute = reader.read_u8()?; + if attribute != 0 { + bail!(reader.original_position() - 1, "invalid tag attributes"); + } + Ok(TagType { + kind: TagKind::Exception, + func_type_idx: reader.read_var_u32()?, + }) + } +} diff --git a/third_party/rust/wasmparser/src/readers/core/types.rs b/third_party/rust/wasmparser/src/readers/core/types.rs new file mode 100644 index 0000000000..4358e2670c --- /dev/null +++ b/third_party/rust/wasmparser/src/readers/core/types.rs @@ -0,0 +1,380 @@ +/* Copyright 2018 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use crate::limits::{MAX_WASM_FUNCTION_PARAMS, MAX_WASM_FUNCTION_RETURNS}; +use crate::{BinaryReader, FromReader, Result, SectionLimited}; +use std::fmt::Debug; + +/// Represents the types of values in a WebAssembly module. +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +pub enum ValType { + /// The value type is i32. + I32, + /// The value type is i64. + I64, + /// The value type is f32. + F32, + /// The value type is f64. + F64, + /// The value type is v128. + V128, + /// The value type is a reference. Which type of reference is decided by + /// RefType. This is a change in syntax from the function references proposal, + /// which now provides FuncRef and ExternRef as sugar for the generic ref + /// construct. + Ref(RefType), +} + +/// A reference type. When the function references feature is disabled, this +/// only represents funcref and externref, using the following format: +/// RefType { nullable: true, heap_type: Func | Extern }) +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +#[repr(packed)] +pub struct RefType { + /// Whether it's nullable + pub nullable: bool, + /// The relevant heap type + pub heap_type: HeapType, +} + +impl RefType { + /// Alias for the wasm `funcref` type. + pub const FUNCREF: RefType = RefType { + nullable: true, + heap_type: HeapType::Func, + }; + /// Alias for the wasm `externref` type. + pub const EXTERNREF: RefType = RefType { + nullable: true, + heap_type: HeapType::Extern, + }; +} + +impl From<RefType> for ValType { + fn from(ty: RefType) -> ValType { + ValType::Ref(ty) + } +} + +/// Used as a performance optimization in HeapType. Call `.into()` to get the u32 +// A u16 forces 2-byte alignment, which forces HeapType to be 4 bytes, +// which forces ValType to 5 bytes. This newtype is annotated as unaligned to +// store the necessary bits compactly +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +#[repr(packed)] +pub struct PackedIndex(u16); + +impl TryFrom<u32> for PackedIndex { + type Error = (); + + fn try_from(idx: u32) -> Result<PackedIndex, ()> { + idx.try_into().map(PackedIndex).map_err(|_| ()) + } +} + +impl From<PackedIndex> for u32 { + fn from(x: PackedIndex) -> u32 { + x.0 as u32 + } +} + +/// A heap type from function references. When the proposal is disabled, Index +/// is an invalid type. +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +pub enum HeapType { + /// Function type index + /// Note: [PackedIndex] may need to be unpacked + TypedFunc(PackedIndex), + /// From reference types + Func, + /// From reference types + Extern, +} + +impl ValType { + /// Alias for the wasm `funcref` type. + pub const FUNCREF: ValType = ValType::Ref(RefType::FUNCREF); + /// Alias for the wasm `externref` type. + pub const EXTERNREF: ValType = ValType::Ref(RefType::EXTERNREF); + + /// Returns whether this value type is a "reference type". + /// + /// Only reference types are allowed in tables, for example, and with some + /// instructions. Current reference types include `funcref` and `externref`. + pub fn is_reference_type(&self) -> bool { + matches!(self, ValType::Ref(_)) + } + /// Whether the type is defaultable according to function references + /// spec. This amounts to whether it's a non-nullable ref + pub fn is_defaultable(&self) -> bool { + !matches!( + self, + ValType::Ref(RefType { + nullable: false, + .. + }) + ) + } + + pub(crate) fn is_valtype_byte(byte: u8) -> bool { + match byte { + 0x7F | 0x7E | 0x7D | 0x7C | 0x7B | 0x70 | 0x6F | 0x6B | 0x6C => true, + _ => false, + } + } +} + +impl<'a> FromReader<'a> for ValType { + fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> { + match reader.peek()? { + 0x7F => { + reader.position += 1; + Ok(ValType::I32) + } + 0x7E => { + reader.position += 1; + Ok(ValType::I64) + } + 0x7D => { + reader.position += 1; + Ok(ValType::F32) + } + 0x7C => { + reader.position += 1; + Ok(ValType::F64) + } + 0x7B => { + reader.position += 1; + Ok(ValType::V128) + } + 0x70 | 0x6F | 0x6B | 0x6C => Ok(ValType::Ref(reader.read()?)), + _ => bail!(reader.original_position(), "invalid value type"), + } + } +} + +impl<'a> FromReader<'a> for RefType { + fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> { + match reader.read()? { + 0x70 => Ok(RefType::FUNCREF), + 0x6F => Ok(RefType::EXTERNREF), + byte @ (0x6B | 0x6C) => Ok(RefType { + nullable: byte == 0x6C, + heap_type: reader.read()?, + }), + _ => bail!(reader.original_position(), "malformed reference type"), + } + } +} + +impl<'a> FromReader<'a> for HeapType { + fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> { + match reader.peek()? { + 0x70 => { + reader.position += 1; + Ok(HeapType::Func) + } + 0x6F => { + reader.position += 1; + Ok(HeapType::Extern) + } + _ => { + let idx = match u32::try_from(reader.read_var_s33()?) { + Ok(idx) => idx, + Err(_) => { + bail!(reader.original_position(), "invalid function heap type",); + } + }; + match idx.try_into() { + Ok(packed) => Ok(HeapType::TypedFunc(packed)), + Err(_) => { + bail!(reader.original_position(), "function index too large"); + } + } + } + } + } +} + +/// Represents a type in a WebAssembly module. +#[derive(Debug, Clone)] +pub enum Type { + /// The type is for a function. + Func(FuncType), +} + +/// Represents a type of a function in a WebAssembly module. +#[derive(Clone, Eq, PartialEq, Hash)] +pub struct FuncType { + /// The combined parameters and result types. + params_results: Box<[ValType]>, + /// The number of parameter types. + len_params: usize, +} + +impl Debug for FuncType { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("FuncType") + .field("params", &self.params()) + .field("returns", &self.results()) + .finish() + } +} + +impl FuncType { + /// Creates a new [`FuncType`] from the given `params` and `results`. + pub fn new<P, R>(params: P, results: R) -> Self + where + P: IntoIterator<Item = ValType>, + R: IntoIterator<Item = ValType>, + { + let mut buffer = params.into_iter().collect::<Vec<_>>(); + let len_params = buffer.len(); + buffer.extend(results); + Self { + params_results: buffer.into(), + len_params, + } + } + + /// Creates a new [`FuncType`] fom its raw parts. + /// + /// # Panics + /// + /// If `len_params` is greater than the length of `params_results` combined. + pub(crate) fn from_raw_parts(params_results: Box<[ValType]>, len_params: usize) -> Self { + assert!(len_params <= params_results.len()); + Self { + params_results, + len_params, + } + } + + /// Returns a shared slice to the parameter types of the [`FuncType`]. + #[inline] + pub fn params(&self) -> &[ValType] { + &self.params_results[..self.len_params] + } + + /// Returns a shared slice to the result types of the [`FuncType`]. + #[inline] + pub fn results(&self) -> &[ValType] { + &self.params_results[self.len_params..] + } +} + +/// Represents a table's type. +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +pub struct TableType { + /// The table's element type. + pub element_type: RefType, + /// Initial size of this table, in elements. + pub initial: u32, + /// Optional maximum size of the table, in elements. + pub maximum: Option<u32>, +} + +/// Represents a memory's type. +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +pub struct MemoryType { + /// Whether or not this is a 64-bit memory, using i64 as an index. If this + /// is false it's a 32-bit memory using i32 as an index. + /// + /// This is part of the memory64 proposal in WebAssembly. + pub memory64: bool, + + /// Whether or not this is a "shared" memory, indicating that it should be + /// send-able across threads and the `maximum` field is always present for + /// valid types. + /// + /// This is part of the threads proposal in WebAssembly. + pub shared: bool, + + /// Initial size of this memory, in wasm pages. + /// + /// For 32-bit memories (when `memory64` is `false`) this is guaranteed to + /// be at most `u32::MAX` for valid types. + pub initial: u64, + + /// Optional maximum size of this memory, in wasm pages. + /// + /// For 32-bit memories (when `memory64` is `false`) this is guaranteed to + /// be at most `u32::MAX` for valid types. This field is always present for + /// valid wasm memories when `shared` is `true`. + pub maximum: Option<u64>, +} + +impl MemoryType { + /// Gets the index type for the memory. + pub fn index_type(&self) -> ValType { + if self.memory64 { + ValType::I64 + } else { + ValType::I32 + } + } +} + +/// Represents a global's type. +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +pub struct GlobalType { + /// The global's type. + pub content_type: ValType, + /// Whether or not the global is mutable. + pub mutable: bool, +} + +/// Represents a tag kind. +#[derive(Clone, Copy, Debug)] +pub enum TagKind { + /// The tag is an exception type. + Exception, +} + +/// A tag's type. +#[derive(Clone, Copy, Debug)] +pub struct TagType { + /// The kind of tag + pub kind: TagKind, + /// The function type this tag uses. + pub func_type_idx: u32, +} + +/// A reader for the type section of a WebAssembly module. +pub type TypeSectionReader<'a> = SectionLimited<'a, Type>; + +impl<'a> FromReader<'a> for Type { + fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> { + Ok(match reader.read_u8()? { + 0x60 => Type::Func(reader.read()?), + x => return reader.invalid_leading_byte(x, "type"), + }) + } +} + +impl<'a> FromReader<'a> for FuncType { + fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> { + let mut params_results = reader + .read_iter(MAX_WASM_FUNCTION_PARAMS, "function params")? + .collect::<Result<Vec<_>>>()?; + let len_params = params_results.len(); + let results = reader.read_iter(MAX_WASM_FUNCTION_RETURNS, "function returns")?; + params_results.reserve(results.size_hint().0); + for result in results { + params_results.push(result?); + } + Ok(FuncType::from_raw_parts(params_results.into(), len_params)) + } +} |