diff --git a/crates/wasmparser/examples/dump.rs b/crates/wasmparser/examples/dump.rs index d651e3c19f..af3b75cd9a 100644 --- a/crates/wasmparser/examples/dump.rs +++ b/crates/wasmparser/examples/dump.rs @@ -38,7 +38,7 @@ fn main() { ref ty, } => { println!( - "ImportSectionEntry {{ module: \"{}\", field: \"{}\", ty: {:?} }}", + "ImportSectionEntry {{ module: \"{}\", field: {:?}, ty: {:?} }}", module, field, ty ); } diff --git a/crates/wasmparser/examples/simple.rs b/crates/wasmparser/examples/simple.rs index 8843957fda..70dee2af99 100644 --- a/crates/wasmparser/examples/simple.rs +++ b/crates/wasmparser/examples/simple.rs @@ -29,9 +29,11 @@ fn main() { } => { println!(" Export {} {:?}", field, kind); } - ParserState::ImportSectionEntry { module, field, .. } => { - println!(" Import {}::{}", module, field) - } + ParserState::ImportSectionEntry { + module, + field: Some(field), + .. + } => println!(" Import {}::{}", module, field), ParserState::EndWasm => break, ParserState::Error(ref err) => panic!("Error: {:?}", err), _ => ( /* println!(" Other {:?}", state); */ ), diff --git a/crates/wasmparser/src/binary_reader.rs b/crates/wasmparser/src/binary_reader.rs index 1489146b26..2bcd35891d 100644 --- a/crates/wasmparser/src/binary_reader.rs +++ b/crates/wasmparser/src/binary_reader.rs @@ -18,16 +18,14 @@ use std::convert::TryInto; use std::str; use std::vec::Vec; -use crate::limits::{ - MAX_WASM_FUNCTION_LOCALS, MAX_WASM_FUNCTION_PARAMS, MAX_WASM_FUNCTION_RETURNS, - MAX_WASM_FUNCTION_SIZE, MAX_WASM_STRING_SIZE, -}; +use crate::limits::*; use crate::primitives::{ BinaryReaderError, BrTable, CustomSectionKind, ExternalKind, FuncType, GlobalType, Ieee32, Ieee64, LinkingType, MemoryImmediate, MemoryType, NameType, Operator, RelocType, ResizableLimits, Result, SIMDLaneIndex, SectionCode, TableType, Type, TypeOrFuncType, V128, }; +use crate::{ExportType, Import, ImportSectionEntryType, InstanceType, ModuleType}; const MAX_WASM_BR_TABLE_SIZE: usize = MAX_WASM_FUNCTION_SIZE; @@ -43,6 +41,7 @@ const WASM_MAGIC_NUMBER: &[u8; 4] = b"\0asm"; const WASM_EXPERIMENTAL_VERSION: u32 = 0xd; const WASM_SUPPORTED_VERSION: u32 = 0x1; +#[derive(Clone)] pub(crate) struct SectionHeader<'a> { pub code: SectionCode<'a>, pub payload_start: usize, @@ -235,6 +234,9 @@ impl<'a> BinaryReader<'a> { 1 => Ok(ExternalKind::Table), 2 => Ok(ExternalKind::Memory), 3 => Ok(ExternalKind::Global), + 5 => Ok(ExternalKind::Module), + 6 => Ok(ExternalKind::Instance), + 7 => Ok(ExternalKind::Type), _ => Err(BinaryReaderError::new( "Invalid external kind", self.original_position() - 1, @@ -243,13 +245,6 @@ impl<'a> BinaryReader<'a> { } pub(crate) fn read_func_type(&mut self) -> Result { - if self.read_type()? != Type::Func { - return Err(BinaryReaderError::new( - "type signature is not a func", - self.original_position() - 1, - )); - } - let params_len = self.read_var_u32()? as usize; if params_len > MAX_WASM_FUNCTION_PARAMS { return Err(BinaryReaderError::new( @@ -278,6 +273,77 @@ impl<'a> BinaryReader<'a> { }) } + pub(crate) fn read_module_type(&mut self) -> Result> { + let pos = self.original_position(); + let imports_len = self.read_var_u32()? as usize; + if imports_len > MAX_WASM_IMPORTS { + return Err(BinaryReaderError::new("imports size is out of bounds", pos)); + } + Ok(ModuleType { + imports: (0..imports_len) + .map(|_| self.read_import()) + .collect::>()?, + exports: self.read_export_types()?, + }) + } + + pub(crate) fn read_instance_type(&mut self) -> Result> { + Ok(InstanceType { + exports: self.read_export_types()?, + }) + } + + fn read_export_types(&mut self) -> Result]>> { + let pos = self.original_position(); + let exports_len = self.read_var_u32()? as usize; + if exports_len > MAX_WASM_EXPORTS { + return Err(BinaryReaderError::new("exports size is out of bound", pos)); + } + (0..exports_len).map(|_| self.read_export_type()).collect() + } + + pub(crate) fn read_import(&mut self) -> Result> { + let module = self.read_string()?; + + // For the `field`, figure out if we're the experimental encoding of + // single-level imports for the module linking proposal (a single-byte + // string which is 0xc0, which is invalid utf-8) or if we have a second + // level of import. + let mut clone = self.clone(); + let field = if clone.read_var_u32()? == 1 && clone.read_u8()? == 0xc0 { + *self = clone; + None + } else { + Some(self.read_string()?) + }; + + let ty = self.read_import_desc()?; + Ok(Import { module, field, ty }) + } + + pub(crate) fn read_export_type(&mut self) -> Result> { + let name = self.read_string()?; + let ty = self.read_import_desc()?; + Ok(ExportType { name, ty }) + } + + pub(crate) fn read_import_desc(&mut self) -> Result { + Ok(match self.read_external_kind()? { + ExternalKind::Function => ImportSectionEntryType::Function(self.read_var_u32()?), + ExternalKind::Table => ImportSectionEntryType::Table(self.read_table_type()?), + ExternalKind::Memory => ImportSectionEntryType::Memory(self.read_memory_type()?), + ExternalKind::Global => ImportSectionEntryType::Global(self.read_global_type()?), + ExternalKind::Module => ImportSectionEntryType::Module(self.read_var_u32()?), + ExternalKind::Instance => ImportSectionEntryType::Instance(self.read_var_u32()?), + ExternalKind::Type => { + return Err(BinaryReaderError::new( + "cannot import types", + self.original_position() - 1, + )) + } + }) + } + fn read_resizable_limits(&mut self, max_present: bool) -> Result { let initial = self.read_var_u32()?; let maximum = if max_present { @@ -362,6 +428,10 @@ impl<'a> BinaryReader<'a> { 10 => Ok(SectionCode::Code), 11 => Ok(SectionCode::Data), 12 => Ok(SectionCode::DataCount), + 100 => Ok(SectionCode::Module), + 101 => Ok(SectionCode::Instance), + 102 => Ok(SectionCode::Alias), + 103 => Ok(SectionCode::ModuleCode), _ => Err(BinaryReaderError::new("Invalid section code", offset)), } } diff --git a/crates/wasmparser/src/lib.rs b/crates/wasmparser/src/lib.rs index e581b932da..b40210873b 100644 --- a/crates/wasmparser/src/lib.rs +++ b/crates/wasmparser/src/lib.rs @@ -39,15 +39,18 @@ pub use crate::parser::WasmDecoder; pub use crate::primitives::BinaryReaderError; pub use crate::primitives::BrTable; pub use crate::primitives::CustomSectionKind; +pub use crate::primitives::ExportType; pub use crate::primitives::ExternalKind; pub use crate::primitives::FuncType; pub use crate::primitives::GlobalType; pub use crate::primitives::Ieee32; pub use crate::primitives::Ieee64; pub use crate::primitives::ImportSectionEntryType; +pub use crate::primitives::InstanceType; pub use crate::primitives::LinkingType; pub use crate::primitives::MemoryImmediate; pub use crate::primitives::MemoryType; +pub use crate::primitives::ModuleType; pub use crate::primitives::NameType; pub use crate::primitives::Naming; pub use crate::primitives::Operator; @@ -57,6 +60,7 @@ pub use crate::primitives::Result; pub use crate::primitives::SectionCode; pub use crate::primitives::TableType; pub use crate::primitives::Type; +pub use crate::primitives::TypeDef; pub use crate::primitives::TypeOrFuncType; pub use crate::primitives::V128; @@ -72,11 +76,15 @@ pub use crate::module_resources::WasmMemoryType; pub use crate::module_resources::WasmModuleResources; pub use crate::module_resources::WasmTableType; pub use crate::module_resources::WasmType; +pub use crate::module_resources::WasmTypeDef; pub(crate) use crate::module_resources::{wasm_func_type_inputs, wasm_func_type_outputs}; pub use crate::operators_validator::OperatorValidatorConfig; +pub use crate::readers::Alias; +pub use crate::readers::AliasSectionReader; +pub use crate::readers::AliasedInstance; pub use crate::readers::CodeSectionReader; pub use crate::readers::CustomSectionContent; pub use crate::readers::Data; @@ -97,10 +105,13 @@ pub use crate::readers::GlobalSectionReader; pub use crate::readers::Import; pub use crate::readers::ImportSectionReader; pub use crate::readers::InitExpr; +pub use crate::readers::InstanceSectionReader; pub use crate::readers::LinkingSectionReader; pub use crate::readers::LocalsReader; pub use crate::readers::MemorySectionReader; +pub use crate::readers::ModuleCodeSectionReader; pub use crate::readers::ModuleReader; +pub use crate::readers::ModuleSectionReader; pub use crate::readers::Name; pub use crate::readers::NameSectionReader; pub use crate::readers::NamingReader; diff --git a/crates/wasmparser/src/limits.rs b/crates/wasmparser/src/limits.rs index 38594a6362..e0307d14f6 100644 --- a/crates/wasmparser/src/limits.rs +++ b/crates/wasmparser/src/limits.rs @@ -17,8 +17,8 @@ // The limits are agreed upon with other engines for consistency. pub const MAX_WASM_TYPES: usize = 1_000_000; pub const MAX_WASM_FUNCTIONS: usize = 1_000_000; -pub const _MAX_WASM_IMPORTS: usize = 100_000; -pub const _MAX_WASM_EXPORTS: usize = 100_000; +pub const MAX_WASM_IMPORTS: usize = 100_000; +pub const MAX_WASM_EXPORTS: usize = 100_000; pub const MAX_WASM_GLOBALS: usize = 1_000_000; pub const _MAX_WASM_DATA_SEGMENTS: usize = 100_000; pub const MAX_WASM_MEMORY_PAGES: usize = 65536; @@ -32,3 +32,5 @@ pub const _MAX_WASM_TABLE_SIZE: usize = 10_000_000; pub const MAX_WASM_TABLE_ENTRIES: usize = 10_000_000; pub const MAX_WASM_TABLES: usize = 1; pub const MAX_WASM_MEMORIES: usize = 1; +pub const MAX_WASM_MODULES: usize = 1_000; +pub const MAX_WASM_INSTANCES: usize = 1_000; diff --git a/crates/wasmparser/src/module_resources.rs b/crates/wasmparser/src/module_resources.rs index d75eaae338..5f83835a73 100644 --- a/crates/wasmparser/src/module_resources.rs +++ b/crates/wasmparser/src/module_resources.rs @@ -13,6 +13,8 @@ * limitations under the License. */ +use crate::{FuncType, TypeDef}; + /// Types that qualify as Wasm types for validation purposes. /// /// Must be comparable with `wasmparser` given Wasm types and @@ -26,6 +28,12 @@ pub trait WasmType: PartialEq + PartialEq + Eq { fn to_parser_type(&self) -> crate::Type; } +pub trait WasmTypeDef { + type FuncType: WasmFuncType; + + fn as_func(&self) -> Option<&Self::FuncType>; +} + /// Types that qualify as Wasm function types for validation purposes. pub trait WasmFuncType { /// A type that is comparable with Wasm types. @@ -277,7 +285,7 @@ pub trait WasmGlobalType { /// the need of an additional parsing or validation step or copying data around. pub trait WasmModuleResources { /// The function type used for validation. - type FuncType: WasmFuncType; + type TypeDef: WasmTypeDef; /// The table type used for validation. type TableType: WasmTableType; /// The memory type used for validation. @@ -286,7 +294,7 @@ pub trait WasmModuleResources { type GlobalType: WasmGlobalType; /// Returns the type at given index. - fn type_at(&self, at: u32) -> Option<&Self::FuncType>; + fn type_at(&self, at: u32) -> Option<&Self::TypeDef>; /// Returns the table at given index if any. fn table_at(&self, at: u32) -> Option<&Self::TableType>; /// Returns the linear memory at given index. @@ -311,12 +319,12 @@ impl WasmModuleResources for &'_ T where T: ?Sized + WasmModuleResources, { - type FuncType = T::FuncType; + type TypeDef = T::TypeDef; type TableType = T::TableType; type MemoryType = T::MemoryType; type GlobalType = T::GlobalType; - fn type_at(&self, at: u32) -> Option<&Self::FuncType> { + fn type_at(&self, at: u32) -> Option<&Self::TypeDef> { T::type_at(self, at) } fn table_at(&self, at: u32) -> Option<&Self::TableType> { @@ -352,6 +360,17 @@ impl WasmType for crate::Type { } } +impl<'a> WasmTypeDef for TypeDef<'a> { + type FuncType = FuncType; + + fn as_func(&self) -> Option<&Self::FuncType> { + match self { + TypeDef::Func(f) => Some(f), + _ => None, + } + } +} + impl WasmFuncType for crate::FuncType { type Type = crate::Type; diff --git a/crates/wasmparser/src/operators_validator.rs b/crates/wasmparser/src/operators_validator.rs index 214bf91930..ad16a6926a 100644 --- a/crates/wasmparser/src/operators_validator.rs +++ b/crates/wasmparser/src/operators_validator.rs @@ -18,7 +18,7 @@ use std::cmp::min; use crate::primitives::{MemoryImmediate, Operator, SIMDLaneIndex, Type, TypeOrFuncType}; use crate::{ wasm_func_type_inputs, wasm_func_type_outputs, BinaryReaderError, WasmFuncType, WasmGlobalType, - WasmMemoryType, WasmModuleResources, WasmTableType, WasmType, + WasmModuleResources, WasmTableType, WasmType, WasmTypeDef, }; #[derive(Debug)] @@ -113,28 +113,17 @@ impl FuncState { } Ok(()) } - fn push_block( + fn push_block( &mut self, ty: TypeOrFuncType, block_type: BlockType, - resources: &dyn WasmModuleResources< - FuncType = F, - TableType = T, - MemoryType = M, - GlobalType = G, - >, + resources: impl WasmModuleResources, ) -> OperatorValidatorResult<()> { let (start_types, return_types) = match ty { TypeOrFuncType::Type(Type::EmptyBlockType) => (vec![], vec![]), TypeOrFuncType::Type(ty) => (vec![], vec![ty]), TypeOrFuncType::FuncType(idx) => { - let ty = resources - .type_at(idx) - // Note: This was an out-of-bounds memory access before - // the change to return `Option` at `type_at`. So - // I assumed that invalid indices at this point are - // bugs. - .expect("function type index is out of bounds"); + let ty = func_type_at(&resources, idx)?; ( wasm_func_type_inputs(ty) .map(WasmType::to_parser_type) @@ -328,6 +317,7 @@ pub struct OperatorValidatorConfig { pub enable_bulk_memory: bool, pub enable_multi_value: bool, pub enable_tail_call: bool, + pub enable_module_linking: bool, #[cfg(feature = "deterministic")] pub deterministic_only: bool, @@ -341,6 +331,7 @@ pub(crate) const DEFAULT_OPERATOR_VALIDATOR_CONFIG: OperatorValidatorConfig = enable_bulk_memory: false, enable_multi_value: true, enable_tail_call: false, + enable_module_linking: false, #[cfg(feature = "deterministic")] deterministic_only: true, @@ -541,9 +532,7 @@ impl OperatorValidator { ); } }; - let ty = resources - .type_at(type_index) - .expect("function type index is out of bounds"); + let ty = func_type_at(&resources, type_index)?; self.check_operands(wasm_func_type_inputs(ty).map(WasmType::to_parser_type))?; self.func_state.change_frame_with_types( ty.len_inputs(), @@ -563,26 +552,18 @@ impl OperatorValidator { "unknown table: table index out of bounds", )); } - match resources.type_at(index) { - None => { - return Err(OperatorValidatorError::new( - "unknown type: type index out of bounds", - )) - } - Some(ty) => { - let types = { - let mut types = Vec::with_capacity(ty.len_inputs() + 1); - types.extend(wasm_func_type_inputs(ty).map(WasmType::to_parser_type)); - types.push(Type::I32); - types - }; - self.check_operands(types.into_iter())?; - self.func_state.change_frame_with_types( - ty.len_inputs() + 1, - wasm_func_type_outputs(ty).map(WasmType::to_parser_type), - )?; - } - } + let ty = func_type_at(&resources, index)?; + let types = { + let mut types = Vec::with_capacity(ty.len_inputs() + 1); + types.extend(wasm_func_type_inputs(ty).map(WasmType::to_parser_type)); + types.push(Type::I32); + types + }; + self.check_operands(types.into_iter())?; + self.func_state.change_frame_with_types( + ty.len_inputs() + 1, + wasm_func_type_outputs(ty).map(WasmType::to_parser_type), + )?; Ok(()) } @@ -674,20 +655,10 @@ impl OperatorValidator { Ok(()) } - fn check_memory_index< - F: WasmFuncType, - T: WasmTableType, - M: WasmMemoryType, - G: WasmGlobalType, - >( + fn check_memory_index( &self, memory_index: u32, - resources: &dyn WasmModuleResources< - FuncType = F, - TableType = T, - MemoryType = M, - GlobalType = G, - >, + resources: impl WasmModuleResources, ) -> OperatorValidatorResult<()> { if resources.memory_at(memory_index).is_none() { bail_op_err!("unknown memory {}", memory_index); @@ -695,16 +666,11 @@ impl OperatorValidator { Ok(()) } - fn check_memarg( + fn check_memarg( &self, memarg: MemoryImmediate, max_align: u32, - resources: &dyn WasmModuleResources< - FuncType = F, - TableType = T, - MemoryType = M, - GlobalType = G, - >, + resources: impl WasmModuleResources, ) -> OperatorValidatorResult<()> { self.check_memory_index(0, resources)?; let align = memarg.flags; @@ -766,20 +732,10 @@ impl OperatorValidator { Ok(()) } - fn check_shared_memarg_wo_align< - F: WasmFuncType, - T: WasmTableType, - M: WasmMemoryType, - G: WasmGlobalType, - >( + fn check_shared_memarg_wo_align( &self, _: MemoryImmediate, - resources: &dyn WasmModuleResources< - FuncType = F, - TableType = T, - MemoryType = M, - GlobalType = G, - >, + resources: impl WasmModuleResources, ) -> OperatorValidatorResult<()> { self.check_memory_index(0, resources)?; Ok(()) @@ -792,15 +748,10 @@ impl OperatorValidator { Ok(()) } - fn check_block_type( + fn check_block_type( &self, ty: TypeOrFuncType, - resources: &dyn WasmModuleResources< - FuncType = F, - TableType = T, - MemoryType = M, - GlobalType = G, - >, + resources: impl WasmModuleResources, ) -> OperatorValidatorResult<()> { match ty { TypeOrFuncType::Type(Type::EmptyBlockType) @@ -812,9 +763,9 @@ impl OperatorValidator { self.check_reference_types_enabled() } TypeOrFuncType::Type(Type::V128) => self.check_simd_enabled(), - TypeOrFuncType::FuncType(idx) => match resources.type_at(idx) { - None => Err(OperatorValidatorError::new("type index out of bounds")), - Some(ty) if !self.config.enable_multi_value => { + TypeOrFuncType::FuncType(idx) => { + let ty = func_type_at(&resources, idx)?; + if !self.config.enable_multi_value { if ty.len_outputs() > 1 { return Err(OperatorValidatorError::new( "blocks, loops, and ifs may only return at most one \ @@ -827,38 +778,21 @@ impl OperatorValidator { when multi-value is not enabled", )); } - Ok(()) } - Some(_) => Ok(()), - }, + Ok(()) + } _ => Err(OperatorValidatorError::new("invalid block return type")), } } - fn check_block_params< - F: WasmFuncType, - T: WasmTableType, - M: WasmMemoryType, - G: WasmGlobalType, - >( + fn check_block_params( &self, ty: TypeOrFuncType, - resources: &dyn WasmModuleResources< - FuncType = F, - TableType = T, - MemoryType = M, - GlobalType = G, - >, + resources: impl WasmModuleResources, skip: usize, ) -> OperatorValidatorResult<()> { if let TypeOrFuncType::FuncType(idx) = ty { - let func_ty = resources - .type_at(idx) - // Note: This was an out-of-bounds memory access before - // the change to return `Option` at `type_at`. So - // I assumed that invalid indices at this point are - // bugs. - .expect("function type index is out of bounds"); + let func_ty = func_type_at(&resources, idx)?; let len = func_ty.len_inputs(); self.check_frame_size(len + skip)?; for (i, ty) in wasm_func_type_inputs(func_ty).enumerate() { @@ -908,20 +842,10 @@ impl OperatorValidator { Ok(Some(ty)) } - pub(crate) fn process_operator< - F: WasmFuncType, - T: WasmTableType, - M: WasmMemoryType, - G: WasmGlobalType, - >( + pub(crate) fn process_operator( &mut self, operator: &Operator, - resources: &dyn WasmModuleResources< - FuncType = F, - TableType = T, - MemoryType = M, - GlobalType = G, - >, + resources: &impl WasmModuleResources, ) -> OperatorValidatorResult { if self.func_state.end_function { return Err(OperatorValidatorError::new("unexpected operator")); @@ -2099,3 +2023,19 @@ impl OperatorValidator { Ok(()) } } + +fn func_type_at( + resources: &T, + at: u32, +) -> OperatorValidatorResult<&::FuncType> { + let ty = match resources.type_at(at) { + Some(ty) => ty, + None => { + return Err(OperatorValidatorError::new( + "unknown type: type index out of bounds", + )) + } + }; + ty.as_func() + .ok_or_else(|| OperatorValidatorError::new("type index not a function type")) +} diff --git a/crates/wasmparser/src/parser.rs b/crates/wasmparser/src/parser.rs index f8ef88d9a0..2263bf22ba 100644 --- a/crates/wasmparser/src/parser.rs +++ b/crates/wasmparser/src/parser.rs @@ -22,19 +22,12 @@ use crate::limits::{ }; use crate::primitives::{ - BinaryReaderError, CustomSectionKind, ExternalKind, FuncType, GlobalType, - ImportSectionEntryType, LinkingType, MemoryType, Naming, Operator, RelocType, Result, - SectionCode, TableType, Type, + BinaryReaderError, CustomSectionKind, ExternalKind, GlobalType, ImportSectionEntryType, + LinkingType, MemoryType, Naming, Operator, RelocType, Result, SectionCode, TableType, Type, + TypeDef, }; -use crate::readers::{ - CodeSectionReader, Data, DataKind, DataSectionReader, Element, ElementItem, ElementItems, - ElementKind, ElementSectionReader, Export, ExportSectionReader, FunctionBody, - FunctionSectionReader, Global, GlobalSectionReader, Import, ImportSectionReader, - LinkingSectionReader, MemorySectionReader, ModuleReader, Name, NameSectionReader, NamingReader, - OperatorsReader, Reloc, RelocSectionReader, Section, SectionReader, TableSectionReader, - TypeSectionReader, -}; +use crate::readers::*; use crate::binary_reader::{BinaryReader, Range}; @@ -85,10 +78,10 @@ pub enum ParserState<'a> { ReadingSectionRawData, SectionRawData(&'a [u8]), - TypeSectionEntry(FuncType), + TypeSectionEntry(TypeDef<'a>), ImportSectionEntry { module: &'a str, - field: &'a str, + field: Option<&'a str>, ty: ImportSectionEntryType, }, FunctionSectionEntry(u32), @@ -140,6 +133,19 @@ pub enum ParserState<'a> { LinkingSectionEntry(LinkingType), SourceMappingURL(&'a str), + + ModuleSectionEntry(u32), + AliasSectionEntry(Alias), + BeginInstantiate { + module: u32, + count: u32, + }, + InstantiateParameter { + kind: ExternalKind, + index: u32, + }, + EndInstantiate, + InlineModule(ModuleCode<'a>), } #[derive(Debug, Copy, Clone)] @@ -183,6 +189,10 @@ enum ParserSectionReader<'a> { NameSectionReader(NameSectionReader<'a>), LinkingSectionReader(LinkingSectionReader<'a>), RelocSectionReader(RelocSectionReader<'a>), + ModuleSectionReader(ModuleSectionReader<'a>), + AliasSectionReader(AliasSectionReader<'a>), + InstanceSectionReader(InstanceSectionReader<'a>), + ModuleCodeSectionReader(ModuleCodeSectionReader<'a>), } macro_rules! section_reader { @@ -221,6 +231,7 @@ pub struct Parser<'a> { current_data_segment: Option<&'a [u8]>, binary_reader: Option>, operators_reader: Option>, + instance_args: Option>, section_entries_left: u32, } @@ -247,6 +258,7 @@ impl<'a> Parser<'a> { current_data_segment: None, binary_reader: None, operators_reader: None, + instance_args: None, section_entries_left: 0, } } @@ -681,6 +693,63 @@ impl<'a> Parser<'a> { Ok(()) } + fn read_module_entry(&mut self) -> Result<()> { + if self.section_entries_left == 0 { + return self.check_section_end(); + } + let module_ty = section_reader!(self, ModuleSectionReader).read()?; + self.state = ParserState::ModuleSectionEntry(module_ty); + self.section_entries_left -= 1; + Ok(()) + } + + fn read_alias_entry(&mut self) -> Result<()> { + if self.section_entries_left == 0 { + return self.check_section_end(); + } + let alias_ty = section_reader!(self, AliasSectionReader).read()?; + self.state = ParserState::AliasSectionEntry(alias_ty); + self.section_entries_left -= 1; + Ok(()) + } + + fn read_instance_entry(&mut self) -> Result<()> { + if self.section_entries_left == 0 { + return self.check_section_end(); + } + let instance = section_reader!(self, InstanceSectionReader).read()?; + let args = instance.args()?; + self.state = ParserState::BeginInstantiate { + module: instance.module(), + count: args.get_count(), + }; + self.instance_args = Some(args); + self.section_entries_left -= 1; + Ok(()) + } + + fn read_instantiate_field(&mut self) -> Result<()> { + let instance = self.instance_args.as_mut().unwrap(); + if instance.eof() { + self.instance_args = None; + self.state = ParserState::EndInstantiate; + } else { + let (kind, index) = self.instance_args.as_mut().unwrap().read()?; + self.state = ParserState::InstantiateParameter { kind, index }; + } + Ok(()) + } + + fn read_module_code_entry(&mut self) -> Result<()> { + if self.section_entries_left == 0 { + return self.check_section_end(); + } + let module = section_reader!(self, ModuleCodeSectionReader).read()?; + self.state = ParserState::InlineModule(module); + self.section_entries_left -= 1; + Ok(()) + } + fn read_section_body(&mut self) -> Result<()> { match self.state { ParserState::BeginSection { @@ -775,6 +844,38 @@ impl<'a> Parser<'a> { .get_data_count_section_content()?; self.state = ParserState::DataCountSectionEntry(func_index); } + ParserState::BeginSection { + code: SectionCode::Module, + .. + } => { + start_section_reader!(self, ModuleSectionReader, get_module_section_reader); + self.read_module_entry()?; + } + ParserState::BeginSection { + code: SectionCode::Alias, + .. + } => { + start_section_reader!(self, AliasSectionReader, get_alias_section_reader); + self.read_alias_entry()?; + } + ParserState::BeginSection { + code: SectionCode::Instance, + .. + } => { + start_section_reader!(self, InstanceSectionReader, get_instance_section_reader); + self.read_instance_entry()?; + } + ParserState::BeginSection { + code: SectionCode::ModuleCode, + .. + } => { + start_section_reader!( + self, + ModuleCodeSectionReader, + get_module_code_section_reader + ); + self.read_module_code_entry()?; + } ParserState::BeginSection { code: SectionCode::Custom { .. }, .. @@ -849,6 +950,10 @@ impl<'a> Parser<'a> { ParserSectionReader::TypeSectionReader(ref reader) => reader.ensure_end()?, ParserSectionReader::LinkingSectionReader(ref reader) => reader.ensure_end()?, ParserSectionReader::RelocSectionReader(ref reader) => reader.ensure_end()?, + ParserSectionReader::ModuleSectionReader(ref reader) => reader.ensure_end()?, + ParserSectionReader::ModuleCodeSectionReader(ref reader) => reader.ensure_end()?, + ParserSectionReader::InstanceSectionReader(ref reader) => reader.ensure_end()?, + ParserSectionReader::AliasSectionReader(ref reader) => reader.ensure_end()?, _ => unreachable!(), } self.position_to_section_end() @@ -984,6 +1089,13 @@ impl<'a> Parser<'a> { ParserState::ReadingSectionRawData | ParserState::SectionRawData(_) => { self.read_section_body_bytes()? } + ParserState::ModuleSectionEntry(_) => self.read_module_entry()?, + ParserState::AliasSectionEntry(_) => self.read_alias_entry()?, + ParserState::BeginInstantiate { .. } | ParserState::InstantiateParameter { .. } => { + self.read_instantiate_field()? + } + ParserState::EndInstantiate => self.read_instance_entry()?, + ParserState::InlineModule(_) => self.read_module_code_entry()?, } Ok(()) } @@ -1085,7 +1197,7 @@ impl<'a> WasmDecoder<'a> for Parser<'a> { /// # 0x80, 0x80, 0x0, 0x0, 0xa, 0x91, 0x80, 0x80, 0x80, 0x0, /// # 0x2, 0x83, 0x80, 0x80, 0x80, 0x0, 0x0, 0x1, 0xb, 0x83, /// # 0x80, 0x80, 0x80, 0x0, 0x0, 0x0, 0xb]; - /// use wasmparser::{WasmDecoder, Parser, ParserState}; + /// use wasmparser::{WasmDecoder, Parser, ParserState, TypeDef}; /// let mut parser = Parser::new(data); /// let mut types = Vec::new(); /// let mut function_types = Vec::new(); @@ -1094,7 +1206,7 @@ impl<'a> WasmDecoder<'a> for Parser<'a> { /// match parser.read() { /// ParserState::Error(_) | /// ParserState::EndWasm => break, - /// ParserState::TypeSectionEntry(ty) => { + /// ParserState::TypeSectionEntry(TypeDef::Func(ty)) => { /// types.push(ty.clone()); /// } /// ParserState::FunctionSectionEntry(id) => { diff --git a/crates/wasmparser/src/primitives.rs b/crates/wasmparser/src/primitives.rs index 995825348f..08fa1fb7e0 100644 --- a/crates/wasmparser/src/primitives.rs +++ b/crates/wasmparser/src/primitives.rs @@ -83,18 +83,22 @@ pub enum SectionCode<'a> { name: &'a str, kind: CustomSectionKind, }, - Type, // Function signature declarations - Import, // Import declarations - Function, // Function declarations - Table, // Indirect function table and other tables - Memory, // Memory attributes - Global, // Global declarations - Export, // Exports - Start, // Start function declaration - Element, // Elements section - Code, // Function bodies (code) - Data, // Data segments - DataCount, // Count of passive data segments + Type, // Function signature declarations + Alias, // Aliased indices from nested/parent modules + Import, // Import declarations + Module, // Module declarations + Instance, // Instance definitions + Function, // Function declarations + Table, // Indirect function table and other tables + Memory, // Memory attributes + Global, // Global declarations + Export, // Exports + Start, // Start function declaration + Element, // Elements section + ModuleCode, // Module definitions + Code, // Function bodies (code) + Data, // Data segments + DataCount, // Count of passive data segments } /// Types as defined [here]. @@ -135,6 +139,16 @@ pub enum ExternalKind { Table, Memory, Global, + Type, + Module, + Instance, +} + +#[derive(Debug, Clone)] +pub enum TypeDef<'a> { + Func(FuncType), + Instance(InstanceType<'a>), + Module(ModuleType<'a>), } #[derive(Debug, Clone, Eq, PartialEq, Hash)] @@ -143,6 +157,23 @@ pub struct FuncType { pub returns: Box<[Type]>, } +#[derive(Debug, Clone)] +pub struct InstanceType<'a> { + pub exports: Box<[ExportType<'a>]>, +} + +#[derive(Debug, Clone)] +pub struct ModuleType<'a> { + pub imports: Box<[crate::Import<'a>]>, + pub exports: Box<[ExportType<'a>]>, +} + +#[derive(Debug, Clone)] +pub struct ExportType<'a> { + pub name: &'a str, + pub ty: ImportSectionEntryType, +} + #[derive(Debug, Copy, Clone)] pub struct ResizableLimits { pub initial: u32, @@ -161,7 +192,7 @@ pub struct MemoryType { pub shared: bool, } -#[derive(Debug, Copy, Clone)] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] pub struct GlobalType { pub content_type: Type, pub mutable: bool, @@ -173,6 +204,8 @@ pub enum ImportSectionEntryType { Table(TableType), Memory(MemoryType), Global(GlobalType), + Module(u32), + Instance(u32), } #[derive(Debug, Copy, Clone)] diff --git a/crates/wasmparser/src/readers/alias_section.rs b/crates/wasmparser/src/readers/alias_section.rs new file mode 100644 index 0000000000..f98d2f1a05 --- /dev/null +++ b/crates/wasmparser/src/readers/alias_section.rs @@ -0,0 +1,84 @@ +use crate::{ + BinaryReader, BinaryReaderError, ExternalKind, Result, SectionIteratorLimited, SectionReader, + SectionWithLimitedItems, +}; + +pub struct AliasSectionReader<'a> { + reader: BinaryReader<'a>, + count: u32, +} + +#[derive(Debug)] +pub struct Alias { + pub instance: AliasedInstance, + pub kind: ExternalKind, + pub index: u32, +} + +#[derive(Debug)] +pub enum AliasedInstance { + Parent, + Child(u32), +} + +impl<'a> AliasSectionReader<'a> { + pub fn new(data: &'a [u8], offset: usize) -> Result> { + let mut reader = BinaryReader::new_with_offset(data, offset); + let count = reader.read_var_u32()?; + Ok(AliasSectionReader { reader, count }) + } + + pub fn original_position(&self) -> usize { + self.reader.original_position() + } + + pub fn get_count(&self) -> u32 { + self.count + } + + pub fn read(&mut self) -> Result { + Ok(Alias { + instance: match self.reader.read_u8()? { + 0x00 => AliasedInstance::Child(self.reader.read_var_u32()?), + 0x01 => AliasedInstance::Parent, + _ => { + return Err(BinaryReaderError::new( + "invalid byte in alias", + self.original_position() - 1, + )) + } + }, + kind: self.reader.read_external_kind()?, + index: self.reader.read_var_u32()?, + }) + } +} + +impl<'a> SectionReader for AliasSectionReader<'a> { + type Item = Alias; + + fn read(&mut self) -> Result { + AliasSectionReader::read(self) + } + fn eof(&self) -> bool { + self.reader.eof() + } + fn original_position(&self) -> usize { + AliasSectionReader::original_position(self) + } +} + +impl<'a> SectionWithLimitedItems for AliasSectionReader<'a> { + fn get_count(&self) -> u32 { + AliasSectionReader::get_count(self) + } +} + +impl<'a> IntoIterator for AliasSectionReader<'a> { + type Item = Result; + type IntoIter = SectionIteratorLimited>; + + fn into_iter(self) -> Self::IntoIter { + SectionIteratorLimited::new(self) + } +} diff --git a/crates/wasmparser/src/readers/import_section.rs b/crates/wasmparser/src/readers/import_section.rs index 92fd5f5e99..68e1ff58c0 100644 --- a/crates/wasmparser/src/readers/import_section.rs +++ b/crates/wasmparser/src/readers/import_section.rs @@ -13,15 +13,15 @@ * limitations under the License. */ -use super::{ - BinaryReader, ExternalKind, ImportSectionEntryType, Result, SectionIteratorLimited, - SectionReader, SectionWithLimitedItems, +use crate::{ + BinaryReader, ImportSectionEntryType, Result, SectionIteratorLimited, SectionReader, + SectionWithLimitedItems, }; #[derive(Debug, Copy, Clone)] pub struct Import<'a> { pub module: &'a str, - pub field: &'a str, + pub field: Option<&'a str>, pub ty: ImportSectionEntryType, } @@ -67,16 +67,7 @@ impl<'a> ImportSectionReader<'a> { where 'a: 'b, { - let module = self.reader.read_string()?; - let field = self.reader.read_string()?; - let kind = self.reader.read_external_kind()?; - let ty = match kind { - ExternalKind::Function => ImportSectionEntryType::Function(self.reader.read_var_u32()?), - ExternalKind::Table => ImportSectionEntryType::Table(self.reader.read_table_type()?), - ExternalKind::Memory => ImportSectionEntryType::Memory(self.reader.read_memory_type()?), - ExternalKind::Global => ImportSectionEntryType::Global(self.reader.read_global_type()?), - }; - Ok(Import { module, field, ty }) + self.reader.read_import() } } diff --git a/crates/wasmparser/src/readers/instance_section.rs b/crates/wasmparser/src/readers/instance_section.rs new file mode 100644 index 0000000000..c7ac301c54 --- /dev/null +++ b/crates/wasmparser/src/readers/instance_section.rs @@ -0,0 +1,147 @@ +use crate::{ + BinaryReader, ExternalKind, Result, SectionIteratorLimited, SectionReader, + SectionWithLimitedItems, +}; + +pub struct InstanceSectionReader<'a> { + reader: BinaryReader<'a>, + count: u32, +} + +impl<'a> InstanceSectionReader<'a> { + pub fn new(data: &'a [u8], offset: usize) -> Result> { + let mut reader = BinaryReader::new_with_offset(data, offset); + let count = reader.read_var_u32()?; + Ok(InstanceSectionReader { reader, count }) + } + + pub fn original_position(&self) -> usize { + self.reader.original_position() + } + + pub fn get_count(&self) -> u32 { + self.count + } + + pub fn read(&mut self) -> Result> { + let instance = Instance::new( + &self.reader.buffer[self.reader.position..], + self.original_position(), + )?; + self.reader.skip_var_32()?; + let count = self.reader.read_var_u32()?; + for _ in 0..count { + self.reader.skip_bytes(1)?; + self.reader.skip_var_32()?; + } + Ok(instance) + } +} + +impl<'a> SectionReader for InstanceSectionReader<'a> { + type Item = Instance<'a>; + + fn read(&mut self) -> Result { + InstanceSectionReader::read(self) + } + fn eof(&self) -> bool { + self.reader.eof() + } + fn original_position(&self) -> usize { + InstanceSectionReader::original_position(self) + } +} + +impl<'a> SectionWithLimitedItems for InstanceSectionReader<'a> { + fn get_count(&self) -> u32 { + InstanceSectionReader::get_count(self) + } +} + +impl<'a> IntoIterator for InstanceSectionReader<'a> { + type Item = Result>; + type IntoIter = SectionIteratorLimited>; + + fn into_iter(self) -> Self::IntoIter { + SectionIteratorLimited::new(self) + } +} + +pub struct Instance<'a> { + reader: BinaryReader<'a>, + module: u32, +} + +impl<'a> Instance<'a> { + pub fn new(data: &'a [u8], offset: usize) -> Result> { + let mut reader = BinaryReader::new_with_offset(data, offset); + let module = reader.read_var_u32()?; + Ok(Instance { module, reader }) + } + + pub fn original_position(&self) -> usize { + self.reader.original_position() + } + + pub fn module(&self) -> u32 { + self.module + } + + pub fn args(&self) -> Result> { + let mut reader = self.reader.clone(); + let count = reader.read_var_u32()?; + Ok(InstanceArgsReader { + count, + remaining: count, + reader, + }) + } +} + +pub struct InstanceArgsReader<'a> { + reader: BinaryReader<'a>, + count: u32, + remaining: u32, +} + +impl<'a> InstanceArgsReader<'a> { + pub fn original_position(&self) -> usize { + self.reader.original_position() + } + + pub fn read(&mut self) -> Result<(ExternalKind, u32)> { + let kind = self.reader.read_external_kind()?; + let index = self.reader.read_var_u32()?; + self.remaining -= 1; + Ok((kind, index)) + } +} + +impl<'a> SectionReader for InstanceArgsReader<'a> { + type Item = (ExternalKind, u32); + + fn read(&mut self) -> Result { + InstanceArgsReader::read(self) + } + fn eof(&self) -> bool { + self.remaining == 0 + } + fn original_position(&self) -> usize { + InstanceArgsReader::original_position(self) + } +} + +impl<'a> SectionWithLimitedItems for InstanceArgsReader<'a> { + fn get_count(&self) -> u32 { + self.count + } +} + +impl<'a> IntoIterator for InstanceArgsReader<'a> { + type Item = Result<(ExternalKind, u32)>; + type IntoIter = SectionIteratorLimited>; + + fn into_iter(self) -> Self::IntoIter { + SectionIteratorLimited::new(self) + } +} diff --git a/crates/wasmparser/src/readers/mod.rs b/crates/wasmparser/src/readers/mod.rs index 6eca50d9ff..78d9e0c5be 100644 --- a/crates/wasmparser/src/readers/mod.rs +++ b/crates/wasmparser/src/readers/mod.rs @@ -14,9 +14,8 @@ */ use super::{ - BinaryReader, BinaryReaderError, CustomSectionKind, ExternalKind, FuncType, GlobalType, - ImportSectionEntryType, LinkingType, MemoryType, NameType, Naming, Operator, Range, RelocType, - Result, SectionCode, TableType, Type, + BinaryReader, BinaryReaderError, CustomSectionKind, ExternalKind, GlobalType, LinkingType, + MemoryType, NameType, Naming, Operator, Range, RelocType, Result, SectionCode, TableType, Type, }; use super::SectionHeader; @@ -56,6 +55,7 @@ pub use self::section_reader::SectionIteratorLimited; pub use self::section_reader::SectionReader; pub use self::section_reader::SectionWithLimitedItems; +pub use self::name_section::FunctionLocalReader; pub use self::name_section::FunctionName; pub use self::name_section::LocalName; pub use self::name_section::ModuleName; @@ -77,6 +77,12 @@ use self::sourcemappingurl_section::read_sourcemappingurl_section_content; pub use self::operators::OperatorsReader; +pub use self::alias_section::*; +pub use self::instance_section::*; +pub use self::module_code_section::*; +pub use self::module_section::*; + +mod alias_section; mod code_section; mod data_count_section; mod data_section; @@ -86,9 +92,12 @@ mod function_section; mod global_section; mod import_section; mod init_expr; +mod instance_section; mod linking_section; mod memory_section; mod module; +mod module_code_section; +mod module_section; mod name_section; mod operators; mod producers_section; diff --git a/crates/wasmparser/src/readers/module.rs b/crates/wasmparser/src/readers/module.rs index dd6499a443..6e8418a7c3 100644 --- a/crates/wasmparser/src/readers/module.rs +++ b/crates/wasmparser/src/readers/module.rs @@ -13,15 +13,18 @@ * limitations under the License. */ +use std::fmt; + use super::{ BinaryReader, BinaryReaderError, CustomSectionKind, Range, Result, SectionCode, SectionHeader, }; use super::{ read_data_count_section_content, read_sourcemappingurl_section_content, - read_start_section_content, CodeSectionReader, DataSectionReader, ElementSectionReader, - ExportSectionReader, FunctionSectionReader, GlobalSectionReader, ImportSectionReader, - LinkingSectionReader, MemorySectionReader, NameSectionReader, ProducersSectionReader, + read_start_section_content, AliasSectionReader, CodeSectionReader, DataSectionReader, + ElementSectionReader, ExportSectionReader, FunctionSectionReader, GlobalSectionReader, + ImportSectionReader, InstanceSectionReader, LinkingSectionReader, MemorySectionReader, + ModuleCodeSectionReader, ModuleSectionReader, NameSectionReader, ProducersSectionReader, RelocSectionReader, TableSectionReader, TypeSectionReader, }; @@ -232,6 +235,46 @@ impl<'a> Section<'a> { } } + pub fn get_module_section_reader<'b>(&self) -> Result> + where + 'a: 'b, + { + match self.code { + SectionCode::Module => ModuleSectionReader::new(self.data, self.offset), + _ => panic!("Invalid state for get_module_section_reader"), + } + } + + pub fn get_alias_section_reader<'b>(&self) -> Result> + where + 'a: 'b, + { + match self.code { + SectionCode::Alias => AliasSectionReader::new(self.data, self.offset), + _ => panic!("Invalid state for get_alias_section_reader"), + } + } + + pub fn get_instance_section_reader<'b>(&self) -> Result> + where + 'a: 'b, + { + match self.code { + SectionCode::Instance => InstanceSectionReader::new(self.data, self.offset), + _ => panic!("Invalid state for get_instance_section_reader"), + } + } + + pub fn get_module_code_section_reader<'b>(&self) -> Result> + where + 'a: 'b, + { + match self.code { + SectionCode::ModuleCode => ModuleCodeSectionReader::new(self.data, self.offset), + _ => panic!("Invalid state for get_module_code_section_reader"), + } + } + pub fn get_binary_reader<'b>(&self) -> BinaryReader<'b> where 'a: 'b, @@ -239,6 +282,10 @@ impl<'a> Section<'a> { BinaryReader::new_with_offset(self.data, self.offset) } + pub fn content_raw(&self) -> &'a [u8] { + self.data + } + pub fn range(&self) -> Range { Range { start: self.offset, @@ -262,6 +309,12 @@ impl<'a> Section<'a> { SectionCode::Table => SectionContent::Table(self.get_table_section_reader()?), SectionCode::Element => SectionContent::Element(self.get_element_section_reader()?), SectionCode::Start => SectionContent::Start(self.get_start_section_content()?), + SectionCode::Module => SectionContent::Module(self.get_module_section_reader()?), + SectionCode::Alias => SectionContent::Alias(self.get_alias_section_reader()?), + SectionCode::Instance => SectionContent::Instance(self.get_instance_section_reader()?), + SectionCode::ModuleCode => { + SectionContent::ModuleCode(self.get_module_code_section_reader()?) + } SectionCode::DataCount => { SectionContent::DataCount(self.get_data_count_section_content()?) } @@ -323,6 +376,10 @@ pub enum SectionContent<'a> { binary: BinaryReader<'a>, content: Option>, }, + Module(ModuleSectionReader<'a>), + Alias(AliasSectionReader<'a>), + Instance(InstanceSectionReader<'a>), + ModuleCode(ModuleCodeSectionReader<'a>), } pub enum CustomSectionContent<'a> { @@ -334,6 +391,7 @@ pub enum CustomSectionContent<'a> { } /// Reads top-level WebAssembly file structure: header and sections. +#[derive(Clone)] pub struct ModuleReader<'a> { reader: BinaryReader<'a>, version: u32, @@ -342,7 +400,11 @@ pub struct ModuleReader<'a> { impl<'a> ModuleReader<'a> { pub fn new(data: &[u8]) -> Result { - let mut reader = BinaryReader::new(data); + ModuleReader::new_with_offset(data, 0) + } + + pub(crate) fn new_with_offset(data: &[u8], offset: usize) -> Result { + let mut reader = BinaryReader::new_with_offset(data, offset); let version = reader.read_file_header()?; Ok(ModuleReader { reader, @@ -362,6 +424,10 @@ impl<'a> ModuleReader<'a> { } } + pub fn original_position(&self) -> usize { + self.reader.original_position() + } + pub fn eof(&self) -> bool { self.read_ahead.is_none() && self.reader.eof() } @@ -412,11 +478,12 @@ impl<'a> ModuleReader<'a> { }; let payload_end = payload_start + payload_len; self.verify_section_end(payload_end)?; + let offset = self.reader.original_position(); let body_start = self.reader.position; self.reader.skip_to(payload_end); Ok(Section { code, - offset: body_start, + offset, data: &self.reader.buffer[body_start..payload_end], }) } @@ -481,6 +548,14 @@ impl<'a> IntoIterator for ModuleReader<'a> { } } +impl<'a> fmt::Debug for ModuleReader<'a> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("ModuleReader") + .field("version", &self.version) + .finish() + } +} + pub struct ModuleIterator<'a> { reader: ModuleReader<'a>, err: bool, diff --git a/crates/wasmparser/src/readers/module_code_section.rs b/crates/wasmparser/src/readers/module_code_section.rs new file mode 100644 index 0000000000..cb13b6b959 --- /dev/null +++ b/crates/wasmparser/src/readers/module_code_section.rs @@ -0,0 +1,93 @@ +use crate::{ + BinaryReader, BinaryReaderError, ModuleReader, Result, SectionIteratorLimited, SectionReader, + SectionWithLimitedItems, +}; + +pub struct ModuleCodeSectionReader<'a> { + reader: BinaryReader<'a>, + count: u32, +} + +#[derive(Debug)] +pub struct ModuleCode<'a> { + reader: BinaryReader<'a>, +} + +impl<'a> ModuleCodeSectionReader<'a> { + pub fn new(data: &'a [u8], offset: usize) -> Result> { + let mut reader = BinaryReader::new_with_offset(data, offset); + let count = reader.read_var_u32()?; + Ok(ModuleCodeSectionReader { reader, count }) + } + + pub fn original_position(&self) -> usize { + self.reader.original_position() + } + + pub fn get_count(&self) -> u32 { + self.count + } + + fn verify_module_end(&self, end: usize) -> Result<()> { + if self.reader.buffer.len() < end { + return Err(BinaryReaderError::new( + "module body extends past end of the module code section", + self.reader.original_offset + self.reader.buffer.len(), + )); + } + Ok(()) + } + + pub fn read(&mut self) -> Result> { + let size = self.reader.read_var_u32()? as usize; + let module_start = self.reader.position; + let module_end = module_start + size; + self.verify_module_end(module_end)?; + self.reader.skip_to(module_end); + Ok(ModuleCode { + reader: BinaryReader::new_with_offset( + &self.reader.buffer[module_start..module_end], + self.reader.original_offset + module_start, + ), + }) + } +} + +impl<'a> SectionReader for ModuleCodeSectionReader<'a> { + type Item = ModuleCode<'a>; + + fn read(&mut self) -> Result { + ModuleCodeSectionReader::read(self) + } + fn eof(&self) -> bool { + self.reader.eof() + } + fn original_position(&self) -> usize { + ModuleCodeSectionReader::original_position(self) + } +} + +impl<'a> SectionWithLimitedItems for ModuleCodeSectionReader<'a> { + fn get_count(&self) -> u32 { + ModuleCodeSectionReader::get_count(self) + } +} + +impl<'a> IntoIterator for ModuleCodeSectionReader<'a> { + type Item = Result>; + type IntoIter = SectionIteratorLimited>; + + fn into_iter(self) -> Self::IntoIter { + SectionIteratorLimited::new(self) + } +} + +impl<'a> ModuleCode<'a> { + pub fn module(&self) -> Result> { + ModuleReader::new_with_offset(self.reader.buffer, self.reader.original_position()) + } + + pub fn original_position(&self) -> usize { + self.reader.original_position() + } +} diff --git a/crates/wasmparser/src/readers/module_section.rs b/crates/wasmparser/src/readers/module_section.rs new file mode 100644 index 0000000000..e17f30f4dd --- /dev/null +++ b/crates/wasmparser/src/readers/module_section.rs @@ -0,0 +1,55 @@ +use super::{BinaryReader, Result, SectionIteratorLimited, SectionReader, SectionWithLimitedItems}; + +pub struct ModuleSectionReader<'a> { + reader: BinaryReader<'a>, + count: u32, +} + +impl<'a> ModuleSectionReader<'a> { + pub fn new(data: &'a [u8], offset: usize) -> Result> { + let mut reader = BinaryReader::new_with_offset(data, offset); + let count = reader.read_var_u32()?; + Ok(ModuleSectionReader { reader, count }) + } + + pub fn original_position(&self) -> usize { + self.reader.original_position() + } + + pub fn get_count(&self) -> u32 { + self.count + } + + pub fn read(&mut self) -> Result { + self.reader.read_var_u32() + } +} + +impl<'a> SectionReader for ModuleSectionReader<'a> { + type Item = u32; + + fn read(&mut self) -> Result { + ModuleSectionReader::read(self) + } + fn eof(&self) -> bool { + self.reader.eof() + } + fn original_position(&self) -> usize { + ModuleSectionReader::original_position(self) + } +} + +impl<'a> SectionWithLimitedItems for ModuleSectionReader<'a> { + fn get_count(&self) -> u32 { + ModuleSectionReader::get_count(self) + } +} + +impl<'a> IntoIterator for ModuleSectionReader<'a> { + type Item = Result; + type IntoIter = SectionIteratorLimited>; + + fn into_iter(self) -> Self::IntoIter { + SectionIteratorLimited::new(self) + } +} diff --git a/crates/wasmparser/src/readers/name_section.rs b/crates/wasmparser/src/readers/name_section.rs index 9ecaff9930..9f7a6fc536 100644 --- a/crates/wasmparser/src/readers/name_section.rs +++ b/crates/wasmparser/src/readers/name_section.rs @@ -31,6 +31,10 @@ impl<'a> ModuleName<'a> { let mut reader = BinaryReader::new_with_offset(self.data, self.offset); reader.read_string() } + + pub fn original_position(&self) -> usize { + self.offset + } } pub struct NamingReader<'a> { @@ -85,6 +89,10 @@ impl<'a> FunctionName<'a> { { NamingReader::new(self.data, self.offset) } + + pub fn original_position(&self) -> usize { + self.offset + } } #[derive(Debug, Copy, Clone)] @@ -101,6 +109,10 @@ impl<'a> FunctionLocalName<'a> { { NamingReader::new(self.data, self.offset) } + + pub fn original_position(&self) -> usize { + self.offset + } } pub struct FunctionLocalReader<'a> { @@ -152,6 +164,10 @@ impl<'a> LocalName<'a> { { FunctionLocalReader::new(self.data, self.offset) } + + pub fn original_position(&self) -> usize { + self.offset + } } #[derive(Debug, Copy, Clone)] diff --git a/crates/wasmparser/src/readers/type_section.rs b/crates/wasmparser/src/readers/type_section.rs index 51fd6eea36..1fa288b21b 100644 --- a/crates/wasmparser/src/readers/type_section.rs +++ b/crates/wasmparser/src/readers/type_section.rs @@ -13,8 +13,9 @@ * limitations under the License. */ -use super::{ - BinaryReader, FuncType, Result, SectionIteratorLimited, SectionReader, SectionWithLimitedItems, +use crate::{ + BinaryReader, BinaryReaderError, Result, SectionIteratorLimited, SectionReader, + SectionWithLimitedItems, TypeDef, }; pub struct TypeSectionReader<'a> { @@ -53,13 +54,23 @@ impl<'a> TypeSectionReader<'a> { /// println!("Type {:?}", ty); /// } /// ``` - pub fn read(&mut self) -> Result { - self.reader.read_func_type() + pub fn read(&mut self) -> Result> { + Ok(match self.reader.read_u8()? { + 0x60 => TypeDef::Func(self.reader.read_func_type()?), + 0x61 => TypeDef::Module(self.reader.read_module_type()?), + 0x62 => TypeDef::Instance(self.reader.read_instance_type()?), + _ => { + return Err(BinaryReaderError::new( + "invalid leading byte in type definition", + self.original_position() - 1, + )) + } + }) } } impl<'a> SectionReader for TypeSectionReader<'a> { - type Item = FuncType; + type Item = TypeDef<'a>; fn read(&mut self) -> Result { TypeSectionReader::read(self) } @@ -78,7 +89,7 @@ impl<'a> SectionWithLimitedItems for TypeSectionReader<'a> { } impl<'a> IntoIterator for TypeSectionReader<'a> { - type Item = Result; + type Item = Result>; type IntoIter = SectionIteratorLimited>; /// Implements iterator over the type section. diff --git a/crates/wasmparser/src/validator.rs b/crates/wasmparser/src/validator.rs index 5c525d7f2c..94a6f6f890 100644 --- a/crates/wasmparser/src/validator.rs +++ b/crates/wasmparser/src/validator.rs @@ -13,20 +13,18 @@ * limitations under the License. */ -use std::collections::HashSet; +use std::collections::{HashMap, HashSet}; use std::result; use std::str; -use crate::limits::{ - MAX_WASM_FUNCTIONS, MAX_WASM_FUNCTION_LOCALS, MAX_WASM_GLOBALS, MAX_WASM_MEMORIES, - MAX_WASM_MEMORY_PAGES, MAX_WASM_TABLES, MAX_WASM_TYPES, -}; +use crate::limits::*; use crate::binary_reader::BinaryReader; use crate::primitives::{ - BinaryReaderError, ExternalKind, FuncType, GlobalType, ImportSectionEntryType, MemoryType, - Operator, ResizableLimits, Result, SectionCode, TableType, Type, + BinaryReaderError, ExportType, ExternalKind, FuncType, GlobalType, ImportSectionEntryType, + InstanceType, MemoryType, ModuleType, Operator, ResizableLimits, Result, SectionCode, + TableType, Type, TypeDef, }; use crate::operators_validator::{ @@ -34,8 +32,8 @@ use crate::operators_validator::{ OperatorValidatorError, DEFAULT_OPERATOR_VALIDATOR_CONFIG, }; use crate::parser::{Parser, ParserInput, ParserState, WasmDecoder}; -use crate::{ElemSectionEntryTable, ElementItem}; -use crate::{WasmFuncType, WasmGlobalType, WasmMemoryType, WasmModuleResources, WasmTableType}; +use crate::{AliasedInstance, WasmModuleResources}; +use crate::{ElemSectionEntryTable, ElementItem, WasmTypeDef}; use crate::readers::FunctionBody; @@ -53,6 +51,7 @@ enum SectionOrderState { Initial, Type, Import, + ModuleLinkingHeader, Function, Table, Memory, @@ -61,13 +60,22 @@ enum SectionOrderState { Start, Element, DataCount, + ModuleCode, Code, Data, } impl SectionOrderState { - pub fn from_section_code(code: &SectionCode) -> Option { + pub fn from_section_code( + code: &SectionCode, + config: &ValidatingParserConfig, + ) -> Option { match *code { + SectionCode::Type | SectionCode::Import + if config.operator_config.enable_module_linking => + { + Some(SectionOrderState::ModuleLinkingHeader) + } SectionCode::Type => Some(SectionOrderState::Type), SectionCode::Import => Some(SectionOrderState::Import), SectionCode::Function => Some(SectionOrderState::Function), @@ -80,7 +88,11 @@ impl SectionOrderState { SectionCode::Code => Some(SectionOrderState::Code), SectionCode::Data => Some(SectionOrderState::Data), SectionCode::DataCount => Some(SectionOrderState::DataCount), - _ => None, + SectionCode::Alias => Some(SectionOrderState::ModuleLinkingHeader), + SectionCode::Module => Some(SectionOrderState::ModuleLinkingHeader), + SectionCode::Instance => Some(SectionOrderState::ModuleLinkingHeader), + SectionCode::ModuleCode => Some(SectionOrderState::ModuleCode), + SectionCode::Custom { .. } => None, } } } @@ -94,24 +106,31 @@ const DEFAULT_VALIDATING_PARSER_CONFIG: ValidatingParserConfig = ValidatingParse operator_config: DEFAULT_OPERATOR_VALIDATOR_CONFIG, }; -struct ValidatingParserResources { - types: Vec, +struct ValidatingParserResources<'a> { + types: Vec>, tables: Vec, memories: Vec, globals: Vec, element_types: Vec, data_count: Option, func_type_indices: Vec, + module_type_indices: Vec, + instance_type_indices: Vec, function_references: HashSet, } -impl<'a> WasmModuleResources for ValidatingParserResources { - type FuncType = crate::FuncType; +enum InstanceDef { + Imported { type_idx: u32 }, + Instantiated { module_idx: u32 }, +} + +impl<'a> WasmModuleResources for ValidatingParserResources<'a> { + type TypeDef = crate::TypeDef<'a>; type TableType = crate::TableType; type MemoryType = crate::MemoryType; type GlobalType = crate::GlobalType; - fn type_at(&self, at: u32) -> Option<&Self::FuncType> { + fn type_at(&self, at: u32) -> Option<&Self::TypeDef> { self.types.get(at as usize) } @@ -153,13 +172,14 @@ pub struct ValidatingParser<'a> { validation_error: Option>, read_position: Option, section_order_state: SectionOrderState, - resources: ValidatingParserResources, + resources: ValidatingParserResources<'a>, current_func_index: u32, - func_imports_count: u32, + func_nonlocal_count: u32, init_expression_state: Option, data_found: u32, exported_names: HashSet, current_operator_validator: Option, + module_instantiation: Option<(u32, usize)>, config: ValidatingParserConfig, } @@ -178,26 +198,22 @@ impl<'a> ValidatingParser<'a> { element_types: Vec::new(), data_count: None, func_type_indices: Vec::new(), + instance_type_indices: Vec::new(), + module_type_indices: Vec::new(), function_references: HashSet::new(), }, current_func_index: 0, - func_imports_count: 0, + func_nonlocal_count: 0, current_operator_validator: None, init_expression_state: None, data_found: 0, exported_names: HashSet::new(), + module_instantiation: None, config: config.unwrap_or(DEFAULT_VALIDATING_PARSER_CONFIG), } } - pub fn get_resources( - &self, - ) -> &dyn WasmModuleResources< - FuncType = crate::FuncType, - TableType = crate::TableType, - MemoryType = crate::MemoryType, - GlobalType = crate::GlobalType, - > { + pub fn get_resources<'b>(&'b self) -> impl WasmModuleResources + 'b { &self.resources } @@ -251,6 +267,37 @@ impl<'a> ValidatingParser<'a> { } } + fn check_module_type(&self, ty: &ModuleType<'a>) -> ValidatorResult<'a, ()> { + if !self.config.operator_config.enable_module_linking { + return self.create_error("module linking proposal not enabled"); + } + for i in ty.imports.iter() { + self.check_import_entry(&i.ty)?; + } + let mut names = HashSet::new(); + for e in ty.exports.iter() { + if !names.insert(e.name) { + return self.create_error("duplicate export name"); + } + self.check_import_entry(&e.ty)?; + } + Ok(()) + } + + fn check_instance_type(&self, ty: &InstanceType<'a>) -> ValidatorResult<'a, ()> { + if !self.config.operator_config.enable_module_linking { + return self.create_error("module linking proposal not enabled"); + } + let mut names = HashSet::new(); + for e in ty.exports.iter() { + if !names.insert(e.name) { + return self.create_error("duplicate export name"); + } + self.check_import_entry(&e.ty)?; + } + Ok(()) + } + fn check_table_type(&self, table_type: &TableType) -> ValidatorResult<'a, ()> { match table_type.element_type { Type::FuncRef => {} @@ -294,13 +341,12 @@ impl<'a> ValidatingParser<'a> { if self.resources.func_type_indices.len() >= MAX_WASM_FUNCTIONS { return self.create_error("functions count out of bounds"); } - if type_index as usize >= self.resources.types.len() { - return self.create_error("unknown type: type index out of bounds"); - } + self.func_type_at(type_index)?; Ok(()) } ImportSectionEntryType::Table(ref table_type) => { if !self.config.operator_config.enable_reference_types + && !self.config.operator_config.enable_module_linking && self.resources.tables.len() >= MAX_WASM_TABLES { return self.create_error("multiple tables: tables count must be at most 1"); @@ -308,17 +354,33 @@ impl<'a> ValidatingParser<'a> { self.check_table_type(table_type) } ImportSectionEntryType::Memory(ref memory_type) => { - if self.resources.memories.len() >= MAX_WASM_MEMORIES { + if !self.config.operator_config.enable_module_linking + && self.resources.memories.len() >= MAX_WASM_MEMORIES + { return self.create_error("multiple memories: memory count must be at most 1"); } self.check_memory_type(memory_type) } ImportSectionEntryType::Global(global_type) => { if self.resources.globals.len() >= MAX_WASM_GLOBALS { - return self.create_error("functions count out of bounds"); + return self.create_error("globals count out of bounds"); } self.check_global_type(global_type) } + ImportSectionEntryType::Module(type_index) => { + if self.resources.module_type_indices.len() >= MAX_WASM_MODULES { + return self.create_error("modules count out of bounds"); + } + self.module_type_at(type_index)?; + Ok(()) + } + ImportSectionEntryType::Instance(type_index) => { + if self.resources.instance_type_indices.len() >= MAX_WASM_INSTANCES { + return self.create_error("instance count out of bounds"); + } + self.instance_type_at(type_index)?; + Ok(()) + } } } @@ -383,41 +445,83 @@ impl<'a> ValidatingParser<'a> { if self.exported_names.contains(field) { return self.create_error("duplicate export name"); } - match kind { - ExternalKind::Function => { - if index as usize >= self.resources.func_type_indices.len() { - return self - .create_error("unknown function: exported function index out of bounds"); - } - self.resources.function_references.insert(index); - } - ExternalKind::Table => { - if index as usize >= self.resources.tables.len() { - return self.create_error("unknown table: exported table index out of bounds"); - } - } - ExternalKind::Memory => { - if index as usize >= self.resources.memories.len() { - return self - .create_error("unknown memory: exported memory index out of bounds"); - } - } - ExternalKind::Global => { - if index as usize >= self.resources.globals.len() { - return self - .create_error("unknown global: exported global index out of bounds"); - } - } + if let ExternalKind::Type = kind { + return self.create_error("cannot export types"); + } + self.check_external_kind("exported", kind, index)?; + Ok(()) + } + + fn check_external_kind( + &mut self, + desc: &str, + kind: ExternalKind, + index: u32, + ) -> ValidatorResult<'a, ()> { + let (ty, total) = match kind { + ExternalKind::Function => ("function", self.resources.func_type_indices.len()), + ExternalKind::Table => ("table", self.resources.tables.len()), + ExternalKind::Memory => ("memory", self.resources.memories.len()), + ExternalKind::Global => ("global", self.resources.globals.len()), + ExternalKind::Module => ("module", self.resources.module_type_indices.len()), + ExternalKind::Instance => ("instance", self.resources.instance_type_indices.len()), + ExternalKind::Type => return self.create_error("cannot export types"), }; + if index as usize >= total { + return self.create_error(&format!( + "unknown {0}: {1} {0} index out of bounds", + ty, desc + )); + } + if let ExternalKind::Function = kind { + self.resources.function_references.insert(index); + } Ok(()) } + fn type_at<'me>(&'me self, type_index: u32) -> ValidatorResult<'a, &'me TypeDef<'a>> { + match self.resources.types.get(type_index as usize) { + Some(ty) => Ok(ty), + None => self.create_error("unknown type: type index out of bounds"), + } + } + + fn func_type_at<'me>(&'me self, type_index: u32) -> ValidatorResult<'a, &'me FuncType> { + match self.type_at(type_index)? { + TypeDef::Func(f) => Ok(f), + _ => self.create_error("type index is not a function"), + } + } + + fn module_type_at<'me>(&'me self, type_index: u32) -> ValidatorResult<'a, &'me ModuleType<'a>> { + if !self.config.operator_config.enable_module_linking { + return self.create_error("module linking proposal not enabled"); + } + match self.type_at(type_index)? { + TypeDef::Module(m) => Ok(m), + _ => self.create_error("type index is not a module"), + } + } + + fn instance_type_at<'me>( + &'me self, + type_index: u32, + ) -> ValidatorResult<'a, &'me InstanceType<'a>> { + if !self.config.operator_config.enable_module_linking { + return self.create_error("module linking proposal not enabled"); + } + match self.type_at(type_index)? { + TypeDef::Instance(i) => Ok(i), + _ => self.create_error("type index is not an instance"), + } + } + fn check_start(&self, func_index: u32) -> ValidatorResult<'a, ()> { if func_index as usize >= self.resources.func_type_indices.len() { return self.create_error("unknown function: start function index out of bounds"); } let type_index = self.resources.func_type_indices[func_index as usize]; - let ty = &self.resources.types[type_index as usize]; + let ty = self.func_type_at(type_index)?; if !ty.params.is_empty() || !ty.returns.is_empty() { return self.create_error("invlid start function type"); } @@ -425,22 +529,32 @@ impl<'a> ValidatingParser<'a> { } fn process_begin_section(&self, code: &SectionCode) -> ValidatorResult<'a, SectionOrderState> { - let order_state = SectionOrderState::from_section_code(code); + use SectionOrderState::*; + + let state = SectionOrderState::from_section_code(code, &self.config); + let state = match state { + Some(state) => state, + None => return Ok(self.section_order_state), + }; Ok(match self.section_order_state { - SectionOrderState::Initial => match order_state { - Some(section) => section, - _ => SectionOrderState::Initial, - }, - previous => { - if let Some(order_state_unwraped) = order_state { - if previous >= order_state_unwraped { - return self.create_error("section out of order"); - } - order_state_unwraped - } else { - previous - } + // Did we just start? In that case move to our newly-found state. + Initial => state, + + // If our previous state comes before our current state, nothing to + // worry about, just advance ourselves. + previous if previous < state => state, + + // In the module linking proposal we can see this state multiple + // times in a row. + ModuleLinkingHeader + if state == ModuleLinkingHeader + && self.config.operator_config.enable_module_linking => + { + ModuleLinkingHeader } + + // otherwise the sections are out of order + _ => return self.create_error("section out of order"), }) } @@ -459,14 +573,18 @@ impl<'a> ValidatingParser<'a> { self.section_order_state = check.ok().unwrap(); } } - ParserState::TypeSectionEntry(ref func_type) => { - let check = self.check_func_type(func_type); + ParserState::TypeSectionEntry(ref def) => { + let check = match def { + TypeDef::Func(ty) => self.check_func_type(ty), + TypeDef::Instance(ty) => self.check_instance_type(ty), + TypeDef::Module(ty) => self.check_module_type(ty), + }; if check.is_err() { self.validation_error = check.err(); } else if self.resources.types.len() > MAX_WASM_TYPES { self.set_validation_error("types count is out of bounds"); } else { - self.resources.types.push(func_type.clone()); + self.resources.types.push(def.clone()); } } ParserState::ImportSectionEntry { ref ty, .. } => { @@ -476,7 +594,7 @@ impl<'a> ValidatingParser<'a> { } else { match *ty { ImportSectionEntryType::Function(type_index) => { - self.func_imports_count += 1; + self.func_nonlocal_count += 1; self.resources.func_type_indices.push(type_index); } ImportSectionEntryType::Table(ref table_type) => { @@ -488,6 +606,16 @@ impl<'a> ValidatingParser<'a> { ImportSectionEntryType::Global(ref global_type) => { self.resources.globals.push(global_type.clone()); } + ImportSectionEntryType::Instance(type_index) => { + self.resources + .instance_type_indices + .push(InstanceDef::Imported { + type_idx: type_index, + }); + } + ImportSectionEntryType::Module(type_index) => { + self.resources.module_type_indices.push(type_index); + } } } } @@ -502,6 +630,7 @@ impl<'a> ValidatingParser<'a> { } ParserState::TableSectionEntry(ref table_type) => { if !self.config.operator_config.enable_reference_types + && !self.config.operator_config.enable_module_linking && self.resources.tables.len() >= MAX_WASM_TABLES { self.set_validation_error("multiple tables: tables count must be at most 1"); @@ -511,7 +640,9 @@ impl<'a> ValidatingParser<'a> { } } ParserState::MemorySectionEntry(ref memory_type) => { - if self.resources.memories.len() >= MAX_WASM_MEMORIES { + if !self.config.operator_config.enable_module_linking + && self.resources.memories.len() >= MAX_WASM_MEMORIES + { self.set_validation_error( "multiple memories: memories count must be at most 1", ); @@ -618,15 +749,16 @@ impl<'a> ValidatingParser<'a> { } } ParserState::BeginFunctionBody { .. } => { - let index = (self.current_func_index + self.func_imports_count) as usize; + let index = (self.current_func_index + self.func_nonlocal_count) as usize; if index as usize >= self.resources.func_type_indices.len() { self.set_validation_error("func type is not defined"); } } ParserState::FunctionBodyLocals { ref locals } => { - let index = (self.current_func_index + self.func_imports_count) as usize; - let func_type = - &self.resources.types[self.resources.func_type_indices[index] as usize]; + let index = (self.current_func_index + self.func_nonlocal_count) as usize; + let func_type = self + .func_type_at(self.resources.func_type_indices[index]) + .unwrap(); let operator_config = self.config.operator_config; match OperatorValidator::new(func_type, locals, operator_config) { Ok(validator) => self.current_operator_validator = Some(validator), @@ -679,7 +811,7 @@ impl<'a> ValidatingParser<'a> { } ParserState::EndWasm => { if self.resources.func_type_indices.len() - != self.current_func_index as usize + self.func_imports_count as usize + != self.current_func_index as usize + self.func_nonlocal_count as usize { self.set_validation_error( "function and code section have inconsistent lengths", @@ -691,6 +823,68 @@ impl<'a> ValidatingParser<'a> { } } } + + ParserState::ModuleSectionEntry(type_index) => { + if !self.config.operator_config.enable_module_linking { + self.set_validation_error("module linking proposal not enabled"); + } else if self.resources.module_type_indices.len() >= MAX_WASM_MODULES { + self.set_validation_error("modules count out of bounds"); + } else { + match self.module_type_at(type_index) { + Ok(_) => self.resources.module_type_indices.push(type_index), + Err(e) => self.validation_error = Some(e), + } + } + } + ParserState::BeginInstantiate { module, count } => { + if !self.config.operator_config.enable_module_linking { + self.set_validation_error("module linking proposal not enabled"); + } else if module as usize >= self.resources.module_type_indices.len() { + self.set_validation_error("module is not defined"); + } else if self.resources.instance_type_indices.len() >= MAX_WASM_INSTANCES { + self.set_validation_error("instance count out of bounds"); + } else { + self.resources + .instance_type_indices + .push(InstanceDef::Instantiated { module_idx: module }); + let module_ty = self.resources.module_type_indices[module as usize]; + if count as usize != self.module_type_at(module_ty).unwrap().imports.len() { + self.set_validation_error("wrong number of imports provided"); + } else { + self.module_instantiation = Some((module_ty, 0)); + } + } + } + ParserState::InstantiateParameter { kind, index } => { + let (module_ty_idx, import_idx) = self.module_instantiation.take().unwrap(); + let module_ty = self.module_type_at(module_ty_idx).unwrap(); + let ty = module_ty.imports[import_idx].ty.clone(); + match self.check_instantiate_field(&ty, kind, index) { + Ok(()) => { + self.module_instantiation = Some((module_ty_idx, import_idx + 1)); + } + Err(e) => self.validation_error = Some(e), + } + } + ParserState::EndInstantiate => { + let (module_ty, import_idx) = self.module_instantiation.take().unwrap(); + let module_ty = self.module_type_at(module_ty).unwrap(); + if import_idx != module_ty.imports.len() { + self.set_validation_error("not enough imports provided"); + } + } + ParserState::AliasSectionEntry(ref alias) => match alias.instance { + AliasedInstance::Parent => { + self.set_validation_error("parent instances not supported"); + } + AliasedInstance::Child(instance_idx) => { + let (kind, index) = (alias.kind, alias.index); + match self.check_alias_entry(instance_idx, kind, index) { + Ok(()) => {} + Err(e) => self.validation_error = Some(e), + } + } + }, _ => (), }; } @@ -708,9 +902,10 @@ impl<'a> ValidatingParser<'a> { self.read(); let operator_validator = match *self.last_state() { ParserState::FunctionBodyLocals { ref locals } => { - let index = (self.current_func_index + self.func_imports_count) as usize; - let func_type = - &self.resources.types[self.resources.func_type_indices[index] as usize]; + let index = (self.current_func_index + self.func_nonlocal_count) as usize; + let func_type = self + .func_type_at(self.resources.func_type_indices[index]) + .unwrap(); let operator_config = self.config.operator_config; OperatorValidator::new(func_type, locals, operator_config) .map_err(|e| ParserState::Error(e.set_offset(self.read_position.unwrap())))? @@ -728,6 +923,202 @@ impl<'a> ValidatingParser<'a> { pub fn current_position(&self) -> usize { self.parser.current_position() } + + fn check_instantiate_field( + &mut self, + expected: &ImportSectionEntryType, + kind: ExternalKind, + index: u32, + ) -> ValidatorResult<'a, ()> { + self.check_external_kind("referenced", kind, index)?; + let actual = match kind { + ExternalKind::Function => { + let actual_type = self.resources.func_type_indices[index as usize]; + ImportSectionEntryType::Function(actual_type) + } + ExternalKind::Table => { + ImportSectionEntryType::Table(self.resources.tables[index as usize]) + } + ExternalKind::Memory => { + ImportSectionEntryType::Memory(self.resources.memories[index as usize]) + } + ExternalKind::Global => { + ImportSectionEntryType::Global(self.resources.globals[index as usize]) + } + ExternalKind::Module => { + let actual_type = self.resources.module_type_indices[index as usize]; + ImportSectionEntryType::Module(actual_type) + } + ExternalKind::Instance => match self.resources.instance_type_indices[index as usize] { + InstanceDef::Imported { type_idx } => ImportSectionEntryType::Instance(type_idx), + InstanceDef::Instantiated { module_idx } => { + let expected = match expected { + ImportSectionEntryType::Instance(idx) => idx, + _ => return self.create_error("wrong kind of item used for instantiate"), + }; + let expected = self.instance_type_at(*expected)?; + let actual_ty = self.resources.module_type_indices[module_idx as usize]; + let actual = self.module_type_at(actual_ty)?; + return self.check_export_sets_match(&expected.exports, &actual.exports); + } + }, + ExternalKind::Type => return self.create_error("cannot export types"), + }; + self.check_imports_match(expected, &actual) + } + + // Note that this function is basically implementing + // https://webassembly.github.io/spec/core/exec/modules.html#import-matching + fn check_imports_match( + &self, + expected: &ImportSectionEntryType, + actual: &ImportSectionEntryType, + ) -> ValidatorResult<'a, ()> { + let limits_match = |expected: &ResizableLimits, actual: &ResizableLimits| { + actual.initial >= expected.initial + && match expected.maximum { + Some(expected_max) => match actual.maximum { + Some(actual_max) => actual_max <= expected_max, + None => false, + }, + None => true, + } + }; + match (expected, actual) { + ( + ImportSectionEntryType::Function(expected), + ImportSectionEntryType::Function(actual), + ) => { + let expected = self.func_type_at(*expected)?; + let actual = self.func_type_at(*actual)?; + if actual == expected { + return Ok(()); + } + self.create_error("function provided for instantiation has wrong type") + } + (ImportSectionEntryType::Table(expected), ImportSectionEntryType::Table(actual)) => { + if expected.element_type == actual.element_type + && limits_match(&expected.limits, &actual.limits) + { + return Ok(()); + } + self.create_error("table provided for instantiation has wrong type") + } + (ImportSectionEntryType::Memory(expected), ImportSectionEntryType::Memory(actual)) => { + if limits_match(&expected.limits, &actual.limits) + && expected.shared == actual.shared + { + return Ok(()); + } + self.create_error("memory provided for instantiation has wrong type") + } + (ImportSectionEntryType::Global(expected), ImportSectionEntryType::Global(actual)) => { + if expected == actual { + return Ok(()); + } + self.create_error("global provided for instantiation has wrong type") + } + ( + ImportSectionEntryType::Instance(expected), + ImportSectionEntryType::Instance(actual), + ) => { + let expected = self.instance_type_at(*expected)?; + let actual = self.instance_type_at(*actual)?; + self.check_export_sets_match(&expected.exports, &actual.exports)?; + Ok(()) + } + (ImportSectionEntryType::Module(expected), ImportSectionEntryType::Module(actual)) => { + let expected = self.module_type_at(*expected)?; + let actual = self.module_type_at(*actual)?; + if expected.imports.len() != actual.imports.len() { + return self.create_error("mismatched number of module imports"); + } + for (expected, actual) in expected.imports.iter().zip(actual.imports.iter()) { + self.check_imports_match(&expected.ty, &actual.ty)?; + } + self.check_export_sets_match(&expected.exports, &actual.exports)?; + Ok(()) + } + _ => self.create_error("wrong kind of item used for instantiate"), + } + } + + fn check_export_sets_match( + &self, + expected: &[ExportType<'_>], + actual: &[ExportType<'_>], + ) -> ValidatorResult<'a, ()> { + let name_to_idx = actual + .iter() + .enumerate() + .map(|(i, e)| (e.name, i)) + .collect::>(); + for expected in expected { + let idx = match name_to_idx.get(expected.name) { + Some(i) => *i, + None => return self.create_error(&format!("no export named `{}`", expected.name)), + }; + self.check_imports_match(&expected.ty, &actual[idx].ty)?; + } + Ok(()) + } + + fn check_alias_entry( + &mut self, + instance_idx: u32, + kind: ExternalKind, + export_idx: u32, + ) -> ValidatorResult<'a, ()> { + let ty = match self + .resources + .instance_type_indices + .get(instance_idx as usize) + { + Some(ty) => ty, + None => { + return self.create_error("unknown instance: aliased instance index out of bounds"); + } + }; + let exports = match ty { + InstanceDef::Imported { type_idx } => &self.instance_type_at(*type_idx)?.exports, + InstanceDef::Instantiated { module_idx } => { + let ty = self.resources.module_type_indices[*module_idx as usize]; + &self.module_type_at(ty)?.exports + } + }; + let export = match exports.get(export_idx as usize) { + Some(e) => e, + None => { + return self.create_error("aliased export index out of bounds"); + } + }; + match (export.ty, kind) { + (ImportSectionEntryType::Function(ty), ExternalKind::Function) => { + self.func_nonlocal_count += 1; + self.resources.func_type_indices.push(ty); + } + (ImportSectionEntryType::Table(ty), ExternalKind::Table) => { + self.resources.tables.push(ty); + } + (ImportSectionEntryType::Memory(ty), ExternalKind::Memory) => { + self.resources.memories.push(ty); + } + (ImportSectionEntryType::Global(ty), ExternalKind::Global) => { + self.resources.globals.push(ty); + } + (ImportSectionEntryType::Instance(ty), ExternalKind::Instance) => { + self.resources + .instance_type_indices + .push(InstanceDef::Imported { type_idx: ty }); + } + (ImportSectionEntryType::Module(ty), ExternalKind::Module) => { + self.resources.module_type_indices.push(ty); + } + _ => return self.create_error("alias kind mismatch with export kind"), + } + + Ok(()) + } } impl<'a> WasmDecoder<'a> for ValidatingParser<'a> { @@ -851,20 +1242,12 @@ impl<'b> ValidatingOperatorParser<'b> { /// } /// } /// ``` - pub fn next<'c, F: WasmFuncType, T: WasmTableType, M: WasmMemoryType, G: WasmGlobalType>( - &mut self, - resources: &dyn WasmModuleResources< - FuncType = F, - TableType = T, - MemoryType = M, - GlobalType = G, - >, - ) -> Result> + pub fn next<'c>(&mut self, resources: impl WasmModuleResources) -> Result> where 'b: 'c, { let op = self.reader.read_operator()?; - match self.operator_validator.process_operator(&op, resources) { + match self.operator_validator.process_operator(&op, &resources) { Err(err) => { let offset = self.func_body_offset + self.reader.current_position(); return Err(err.set_offset(offset)); @@ -886,21 +1269,11 @@ impl<'b> ValidatingOperatorParser<'b> { /// Test whether the given buffer contains a valid WebAssembly function. /// The resources parameter contains all needed data to validate the operators. -pub fn validate_function_body< - F: WasmFuncType, - T: WasmTableType, - M: WasmMemoryType, - G: WasmGlobalType, ->( +pub fn validate_function_body( bytes: &[u8], offset: usize, func_index: u32, - resources: &dyn WasmModuleResources< - FuncType = F, - TableType = T, - MemoryType = M, - GlobalType = G, - >, + resources: impl WasmModuleResources, operator_config: Option, ) -> Result<()> { let operator_config = operator_config.unwrap_or(DEFAULT_OPERATOR_VALIDATOR_CONFIG); @@ -940,7 +1313,9 @@ pub fn validate_function_body< // Note: This was an out-of-bounds access before the change to return `Option` // so I assumed it is considered a bug to access a non-existing function // id here and went with panicking instead of returning a proper error. - .expect("the function type indexof the validated function itself is out of bounds"); + .expect("the function type indexof the validated function itself is out of bounds") + .as_func() + .unwrap(); let mut operator_validator = OperatorValidator::new(func_type, &locals, operator_config) .map_err(|e| e.set_offset(offset))?; let mut eof_found = false; @@ -948,7 +1323,7 @@ pub fn validate_function_body< for item in operators_reader.into_iter_with_offsets() { let (ref op, offset) = item?; match operator_validator - .process_operator(op, resources) + .process_operator(op, &resources) .map_err(|e| e.set_offset(offset))? { FunctionEnd::Yes => { @@ -987,7 +1362,7 @@ pub fn validate(bytes: &[u8], config: Option) -> Result< let operator_config = config.map(|c| c.operator_config); for (i, range) in func_ranges.into_iter().enumerate() { let function_body = range.slice(bytes); - let function_index = i as u32 + parser.func_imports_count; + let function_index = i as u32 + parser.func_nonlocal_count; validate_function_body( function_body, range.start, diff --git a/crates/wasmprinter/src/lib.rs b/crates/wasmprinter/src/lib.rs index 5d404c2d7b..8eae4b1b26 100644 --- a/crates/wasmprinter/src/lib.rs +++ b/crates/wasmprinter/src/lib.rs @@ -40,12 +40,19 @@ pub fn print_bytes(wasm: impl AsRef<[u8]>) -> Result { pub struct Printer { printers: HashMap Result<()>>>, result: String, + state: ModuleState, + nesting: u32, +} + +#[derive(Default)] +struct ModuleState { func: u32, + module: u32, + instance: u32, memory: u32, global: u32, table: u32, - nesting: u32, - functypes: Vec, + types: Vec>, names: HashMap, local_names: HashMap>, module_name: Option, @@ -91,24 +98,28 @@ impl Printer { /// This function takes an entire `wasm` binary blob and will print it to /// the WebAssembly Text Format and return the result as a `String`. pub fn print(&mut self, wasm: &[u8]) -> Result { - // Reset internal state which will get configured throughout printing. - self.func = 0; - self.memory = 0; - self.global = 0; - self.table = 0; - self.functypes.truncate(0); - self.local_names.drain(); - self.module_name = None; + let parser = wasmparser::ModuleReader::new(wasm)?; + self.start_group("module"); + self.print_contents(parser, "")?; + self.end_group(); + Ok(mem::take(&mut self.result)) + } + fn print_contents(&mut self, mut parser: ModuleReader, module_ty: &str) -> Result<()> { // First up try to find the `name` subsection which we'll use to print // pretty names everywhere. Also look for the `code` section so we can // print out functions as soon as we hit the function section. - let mut parser = wasmparser::ModuleReader::new(wasm)?; let mut code = None; - while !parser.eof() { - let section = parser.read()?; + let mut module_code = None; + let mut pre_parser = parser.clone(); + let prev = mem::take(&mut self.state); + while !pre_parser.eof() { + let section = pre_parser.read()?; match section.code { SectionCode::Code => code = Some(section.get_code_section_reader()?), + SectionCode::ModuleCode => { + module_code = Some(section.get_module_code_section_reader()?) + } SectionCode::Custom { name: "name", .. } => { self.register_names(section.get_name_section_reader()?)?; } @@ -117,21 +128,21 @@ impl Printer { } // ... and here we go, time to print all the sections! - self.result.push_str("(module"); - if let Some(name) = &self.module_name { + if let Some(name) = &self.state.module_name { self.result.push_str(" $"); self.result.push_str(name); } - let mut parser = wasmparser::ModuleReader::new(wasm)?; - let mut printers = mem::replace(&mut self.printers, HashMap::new()); + self.result.push_str(module_ty); while !parser.eof() { let section = parser.read()?; match section.code { SectionCode::Custom { name, .. } => { let range = section.get_binary_reader().range(); + let mut printers = mem::replace(&mut self.printers, HashMap::new()); if let Some(printer) = printers.get_mut(name) { - printer(self, range.start, &wasm[range.start..range.end])?; + printer(self, range.start, section.content_raw())?; } + self.printers = printers; } SectionCode::Type => { self.print_types(section.get_type_section_reader()?)?; @@ -179,22 +190,52 @@ impl Printer { SectionCode::DataCount => { // not part of the text format } + SectionCode::Alias => { + self.print_aliases(section.get_alias_section_reader()?)?; + } + SectionCode::Module => { + let reader = section.get_module_section_reader()?; + if reader.get_count() == 0 { + continue; + } + let module_code = match module_code.as_mut() { + Some(f) => f, + None => bail!("found function section without code section"), + }; + self.print_module_code(module_code, reader)?; + } + SectionCode::Instance => { + self.print_instances(section.get_instance_section_reader()?)?; + } + SectionCode::ModuleCode => { + // printed in `Module` section + } } } + self.state = prev; + Ok(()) + } + + fn start_group(&mut self, name: &str) { + self.result.push_str("("); + self.result.push_str(name); + self.nesting += 1; + } + + fn end_group(&mut self) { self.result.push_str(")"); - self.printers = printers; - Ok(mem::replace(&mut self.result, String::new())) + self.nesting -= 1; } fn register_names(&mut self, names: NameSectionReader<'_>) -> Result<()> { for section in names { match section? { - Name::Module(n) => self.module_name = Some(n.get_name()?.to_string()), + Name::Module(n) => self.state.module_name = Some(n.get_name()?.to_string()), Name::Function(n) => { let mut map = n.get_map()?; for _ in 0..map.get_count() { let name = map.read()?; - self.names.insert(name.index, name.name.to_string()); + self.state.names.insert(name.index, name.name.to_string()); } } Name::Local(n) => { @@ -207,7 +248,9 @@ impl Printer { let name = map.read()?; local_map.insert(name.index, name.name.to_string()); } - self.local_names.insert(local_name.func_index, local_map); + self.state + .local_names + .insert(local_name.func_index, local_map); } } } @@ -216,19 +259,57 @@ impl Printer { } fn print_types(&mut self, parser: TypeSectionReader<'_>) -> Result<()> { - for (i, ty) in parser.into_iter().enumerate() { - let ty = ty?; - write!(self.result, "\n (type (;{};) (func", i)?; - self.print_functype(&ty, None)?; - self.result.push_str("))"); - self.functypes.push(ty); + for ty in parser { + self.newline(); + self.start_group("type"); + write!(self.result, " (;{};) ", self.state.types.len())?; + let ty = match ty? { + TypeDef::Func(ty) => { + self.start_group("func"); + self.print_functype(&ty, None)?; + Some(ty) + } + TypeDef::Module(ty) => { + self.newline(); + self.start_group("module"); + for import in ty.imports.iter() { + self.print_import(import, false)?; + } + for export in ty.exports.iter() { + self.newline(); + self.start_group("export "); + self.print_str(export.name)?; + self.result.push_str(" "); + self.print_import_ty(&export.ty, false)?; + self.end_group(); + } + None + } + TypeDef::Instance(ty) => { + self.newline(); + self.start_group("instance"); + for export in ty.exports.iter() { + self.newline(); + self.start_group("export "); + self.print_str(export.name)?; + self.result.push_str(" "); + self.print_import_ty(&export.ty, false)?; + self.end_group(); + } + None + } + }; + self.end_group(); // inner type + self.end_group(); // `type` itself + self.state.types.push(ty); } Ok(()) } fn print_functype_idx(&mut self, idx: u32, names_for: Option) -> Result { - let ty = match self.functypes.get(idx as usize) { - Some(ty) => ty.clone(), + let ty = match self.state.types.get(idx as usize) { + Some(Some(ty)) => ty.clone(), + Some(None) => bail!("function type index `{}` is not a function", idx), None => bail!("function type index `{}` out of bounds", idx), }; self.print_functype(&ty, names_for) @@ -242,7 +323,7 @@ impl Printer { // we need to be careful to terminate previous param blocks and open // a new one if that's the case with a named parameter. for (i, param) in ty.params.iter().enumerate() { - let local_names = &self.local_names; + let local_names = &self.state.local_names; let name = names_for .and_then(|n| local_names.get(&n)) .and_then(|n| n.get(&(i as u32))); @@ -286,49 +367,90 @@ impl Printer { } fn print_imports(&mut self, parser: ImportSectionReader<'_>) -> Result<()> { - use ImportSectionEntryType::*; for import in parser { let import = import?; - self.result.push_str("\n (import "); - self.print_str(import.module)?; - self.result.push_str(" "); - self.print_str(import.field)?; - self.result.push_str(" ("); + self.print_import(&import, true)?; match import.ty { - Function(f) => { - self.result.push_str("func "); - match self.names.get(&self.func) { - Some(name) => write!(self.result, "${}", name)?, - None => write!(self.result, "(;{};)", self.func)?, + ImportSectionEntryType::Function(_) => self.state.func += 1, + ImportSectionEntryType::Module(_) => self.state.module += 1, + ImportSectionEntryType::Instance(_) => self.state.instance += 1, + ImportSectionEntryType::Table(_) => self.state.table += 1, + ImportSectionEntryType::Memory(_) => self.state.memory += 1, + ImportSectionEntryType::Global(_) => self.state.global += 1, + } + } + Ok(()) + } + + fn print_import(&mut self, import: &Import<'_>, index: bool) -> Result<()> { + self.newline(); + self.start_group("import "); + self.print_str(import.module)?; + if let Some(field) = import.field { + self.result.push_str(" "); + self.print_str(field)?; + } + self.result.push_str(" "); + self.print_import_ty(&import.ty, index)?; + self.end_group(); + Ok(()) + } + + fn print_import_ty(&mut self, ty: &ImportSectionEntryType, index: bool) -> Result<()> { + use ImportSectionEntryType::*; + match ty { + Function(f) => { + self.start_group("func"); + if index { + match self.state.names.get(&self.state.func) { + Some(name) => write!(self.result, " ${}", name)?, + None => write!(self.result, " (;{};)", self.state.func)?, } - write!(self.result, " (type {})", f)?; - self.func += 1; } - Table(f) => self.print_table_type(&f)?, - Memory(f) => self.print_memory_type(&f)?, - Global(f) => self.print_global_type(&f)?, + write!(self.result, " (type {})", f)?; } - self.result.push_str("))"); + Module(f) => { + self.start_group("module "); + if index { + write!(self.result, "(;{};) ", self.state.module)?; + } + write!(self.result, "(type {})", f)?; + } + Instance(f) => { + self.start_group("instance "); + if index { + write!(self.result, "(;{};) ", self.state.instance)?; + } + write!(self.result, "(type {})", f)?; + } + Table(f) => self.print_table_type(&f, index)?, + Memory(f) => self.print_memory_type(&f, index)?, + Global(f) => self.print_global_type(&f, index)?, } + self.end_group(); Ok(()) } - fn print_table_type(&mut self, ty: &TableType) -> Result<()> { - write!(self.result, "table (;{};) ", self.table)?; + fn print_table_type(&mut self, ty: &TableType, index: bool) -> Result<()> { + self.start_group("table "); + if index { + write!(self.result, "(;{};) ", self.state.table)?; + } self.print_limits(&ty.limits)?; self.result.push_str(" "); self.print_valtype(ty.element_type)?; - self.table += 1; Ok(()) } - fn print_memory_type(&mut self, ty: &MemoryType) -> Result<()> { - write!(self.result, "memory (;{};) ", self.memory)?; + fn print_memory_type(&mut self, ty: &MemoryType, index: bool) -> Result<()> { + self.start_group("memory "); + if index { + write!(self.result, "(;{};) ", self.state.memory)?; + } self.print_limits(&ty.limits)?; if ty.shared { self.result.push_str(" shared"); } - self.memory += 1; Ok(()) } @@ -340,8 +462,11 @@ impl Printer { Ok(()) } - fn print_global_type(&mut self, ty: &GlobalType) -> Result<()> { - write!(self.result, "global (;{};) ", self.global)?; + fn print_global_type(&mut self, ty: &GlobalType, index: bool) -> Result<()> { + self.start_group("global "); + if index { + write!(self.result, "(;{};) ", self.state.global)?; + } if ty.mutable { self.result.push_str("(mut "); self.print_valtype(ty.content_type)?; @@ -349,16 +474,16 @@ impl Printer { } else { self.print_valtype(ty.content_type)?; } - self.global += 1; Ok(()) } fn print_tables(&mut self, parser: TableSectionReader<'_>) -> Result<()> { for table in parser { let table = table?; - self.result.push_str("\n ("); - self.print_table_type(&table)?; - self.result.push_str(")"); + self.newline(); + self.print_table_type(&table, true)?; + self.end_group(); + self.state.table += 1; } Ok(()) } @@ -366,9 +491,10 @@ impl Printer { fn print_memories(&mut self, parser: MemorySectionReader<'_>) -> Result<()> { for memory in parser { let memory = memory?; - self.result.push_str("\n ("); - self.print_memory_type(&memory)?; - self.result.push_str(")"); + self.newline(); + self.print_memory_type(&memory, true)?; + self.end_group(); + self.state.memory += 1; } Ok(()) } @@ -376,11 +502,12 @@ impl Printer { fn print_globals(&mut self, parser: GlobalSectionReader<'_>) -> Result<()> { for global in parser { let global = global?; - self.result.push_str("\n ("); - self.print_global_type(&global.ty)?; + self.newline(); + self.print_global_type(&global.ty, true)?; self.result.push_str(" "); self.print_init_expr(&global.init_expr)?; - self.result.push_str(")"); + self.end_group(); + self.state.global += 1; } Ok(()) } @@ -393,13 +520,14 @@ impl Printer { for body in code { let body = body?; let ty = funcs.read()?; - self.result.push_str("\n (func "); - match self.names.get(&self.func) { + self.newline(); + self.start_group("func "); + match self.state.names.get(&self.state.func) { Some(name) => write!(self.result, "${}", name)?, - None => write!(self.result, "(;{};)", self.func)?, + None => write!(self.result, "(;{};)", self.state.func)?, } write!(self.result, " (type {})", ty)?; - let params = self.print_functype_idx(ty, Some(self.func))?; + let params = self.print_functype_idx(ty, Some(self.state.func))?; let mut first = true; let mut local_idx = 0; @@ -412,8 +540,9 @@ impl Printer { first = false; } let name = self + .state .local_names - .get(&self.func) + .get(&self.state.func) .and_then(|m| m.get(&(params + local_idx))); locals.start_local(name, &mut self.result); self.print_valtype(ty)?; @@ -423,55 +552,58 @@ impl Printer { } locals.finish(&mut self.result); - self.nesting = 1; + let nesting_start = self.nesting; for operator in body.get_operators_reader()? { let operator = operator?; match operator { Operator::Else => self.nesting -= 1, - Operator::End if self.nesting == 1 => continue, + Operator::End if self.nesting == nesting_start => continue, Operator::End => self.nesting -= 1, _ => {} } - self.result.push_str("\n "); - for _ in 0..self.nesting { - self.result.push_str(" "); - } - self.print_operator(&operator)?; + self.newline(); + self.print_operator(&operator, nesting_start)?; } - self.result.push_str(")"); + self.end_group(); - self.func += 1; + self.state.func += 1; } Ok(()) } - fn print_operator(&mut self, op: &Operator<'_>) -> Result<()> { + fn newline(&mut self) { + self.result.push_str("\n"); + for _ in 0..self.nesting { + self.result.push_str(" "); + } + } + + fn print_operator(&mut self, op: &Operator<'_>, nesting_start: u32) -> Result<()> { use Operator::*; - let nesting = self.nesting; - let label = - |relative: u32| match nesting.checked_sub(relative).and_then(|s| s.checked_sub(1)) { - Some(i) => format!("@{}", i), - None => format!(" INVALID "), - }; + let cur_label = self.nesting - nesting_start + 1; + let label = |relative: u32| match cur_label.checked_sub(relative + 1) { + Some(i) => format!("@{}", i), + None => format!(" INVALID "), + }; match op { Nop => self.result.push_str("nop"), Unreachable => self.result.push_str("unreachable"), Block { ty } => { self.result.push_str("block"); self.print_blockty(ty)?; - write!(self.result, " ;; label = @{}", self.nesting)?; + write!(self.result, " ;; label = @{}", cur_label)?; self.nesting += 1; } Loop { ty } => { self.result.push_str("loop"); self.print_blockty(ty)?; - write!(self.result, " ;; label = @{}", self.nesting)?; + write!(self.result, " ;; label = @{}", cur_label)?; self.nesting += 1; } If { ty } => { self.result.push_str("if"); self.print_blockty(ty)?; - write!(self.result, " ;; label = @{}", self.nesting)?; + write!(self.result, " ;; label = @{}", cur_label)?; self.nesting += 1; } Else => { @@ -538,15 +670,15 @@ impl Printer { } LocalGet { local_index } => { self.result.push_str("local.get "); - self.print_local_idx(self.func, *local_index)?; + self.print_local_idx(self.state.func, *local_index)?; } LocalSet { local_index } => { self.result.push_str("local.set "); - self.print_local_idx(self.func, *local_index)?; + self.print_local_idx(self.state.func, *local_index)?; } LocalTee { local_index } => { self.result.push_str("local.tee "); - self.print_local_idx(self.func, *local_index)?; + self.print_local_idx(self.state.func, *local_index)?; } GlobalGet { global_index } => { @@ -1135,9 +1267,11 @@ impl Printer { fn print_exports(&mut self, data: ExportSectionReader) -> Result<()> { for export in data { let export = export?; - self.result.push_str("\n (export "); + self.newline(); + self.start_group("export "); self.print_str(export.field)?; - self.result.push_str(" ("); + self.result.push_str(" "); + self.start_group(""); match export.kind { ExternalKind::Function => { self.result.push_str("func "); @@ -1146,18 +1280,40 @@ impl Printer { ExternalKind::Table => write!(self.result, "table {}", export.index)?, ExternalKind::Global => write!(self.result, "global {}", export.index)?, ExternalKind::Memory => write!(self.result, "memory {}", export.index)?, + ExternalKind::Module => write!(self.result, "module {}", export.index)?, + ExternalKind::Instance => write!(self.result, "instance {}", export.index)?, + ExternalKind::Type => write!(self.result, "type {}", export.index)?, } - self.result.push_str("))"); + self.end_group(); // field + self.end_group(); // export } return Ok(()); } + fn print_external(&mut self, kind: ExternalKind, index: u32) -> Result<()> { + self.result.push_str("("); + match kind { + ExternalKind::Function => { + self.result.push_str("func "); + self.print_func_idx(index)?; + } + ExternalKind::Table => write!(self.result, "table {}", index)?, + ExternalKind::Global => write!(self.result, "global {}", index)?, + ExternalKind::Memory => write!(self.result, "memory {}", index)?, + ExternalKind::Module => write!(self.result, "module {}", index)?, + ExternalKind::Instance => write!(self.result, "instance {}", index)?, + ExternalKind::Type => write!(self.result, "type {}", index)?, + } + self.result.push_str(")"); + Ok(()) + } + /// Prints a function index specified, using the identifier for the function /// from the `name` section if it was present. /// /// This will either print `$foo` or `idx` as a raw integer. pub fn print_func_idx(&mut self, idx: u32) -> Result<()> { - match self.names.get(&idx) { + match self.state.names.get(&idx) { Some(name) => write!(self.result, "${}", name)?, None => write!(self.result, "{}", idx)?, } @@ -1165,7 +1321,7 @@ impl Printer { } fn print_local_idx(&mut self, func: u32, idx: u32) -> Result<()> { - match self.local_names.get(&func).and_then(|f| f.get(&idx)) { + match self.state.local_names.get(&func).and_then(|f| f.get(&idx)) { Some(name) => write!(self.result, "${}", name)?, None => write!(self.result, "{}", idx)?, } @@ -1243,9 +1399,109 @@ impl Printer { Ok(()) } + fn print_instances(&mut self, instances: InstanceSectionReader) -> Result<()> { + for (i, instance) in instances.into_iter().enumerate() { + let instance = instance?; + self.newline(); + self.start_group("instance"); + write!(self.result, " (;{};)", i)?; + self.newline(); + self.start_group("instantiate"); + write!(self.result, " {}", instance.module())?; + for export in instance.args()? { + let (kind, index) = export?; + self.newline(); + self.print_external(kind, index)?; + } + self.end_group(); // instantiate + self.end_group(); // instance + } + Ok(()) + } + + fn print_aliases(&mut self, aliases: AliasSectionReader) -> Result<()> { + for alias in aliases { + let alias = alias?; + self.newline(); + self.start_group("alias"); + match alias.kind { + ExternalKind::Function => { + match self.state.names.get(&self.state.func) { + Some(name) => write!(self.result, " ${}", name)?, + None => write!(self.result, " (;{};)", self.state.func)?, + } + self.state.func += 1; + } + ExternalKind::Table => { + write!(self.result, " (;{};)", self.state.table)?; + self.state.table += 1; + } + ExternalKind::Memory => { + write!(self.result, " (;{};)", self.state.memory)?; + self.state.memory += 1; + } + ExternalKind::Global => { + write!(self.result, " (;{};)", self.state.global)?; + self.state.global += 1; + } + ExternalKind::Instance => { + write!(self.result, " (;{};)", self.state.instance)?; + self.state.instance += 1; + } + ExternalKind::Module => { + write!(self.result, " (;{};)", self.state.module)?; + self.state.module += 1; + } + ExternalKind::Type => self.state.types.push(None), + } + self.result.push_str(" "); + match alias.instance { + AliasedInstance::Parent => self.result.push_str("parent"), + AliasedInstance::Child(i) => { + self.start_group("instance"); + write!(self.result, " {}", i)?; + self.end_group(); + } + } + self.result.push_str(" "); + match alias.kind { + ExternalKind::Function => self.start_group("func"), + ExternalKind::Table => self.start_group("table"), + ExternalKind::Memory => self.start_group("memory"), + ExternalKind::Global => self.start_group("global"), + ExternalKind::Instance => self.start_group("instance"), + ExternalKind::Module => self.start_group("module"), + ExternalKind::Type => self.start_group("type"), + } + write!(self.result, " {}", alias.index)?; + self.end_group(); + self.end_group(); + } + Ok(()) + } + + fn print_module_code( + &mut self, + module_code: &mut ModuleCodeSectionReader<'_>, + tys: ModuleSectionReader<'_>, + ) -> Result<()> { + for ty in tys { + let ty = ty?; + let module = module_code.read()?; + self.newline(); + self.start_group("module"); + self.print_contents( + module.module()?, + &format!(" (;{};) (type {})", self.state.module, ty), + )?; + self.end_group(); + self.state.module += 1; + } + Ok(()) + } + fn print_init_expr(&mut self, expr: &InitExpr) -> Result<()> { - self.result.push_str("("); - self.nesting = 1; + self.start_group(""); for (i, op) in expr.get_operators_reader().into_iter().enumerate() { match op? { Operator::End => {} @@ -1253,11 +1509,11 @@ impl Printer { if i > 0 { self.result.push_str(" "); } - self.print_operator(&other)? + self.print_operator(&other, self.nesting)? } } } - self.result.push_str(")"); + self.end_group(); Ok(()) } diff --git a/crates/wast/src/ast/alias.rs b/crates/wast/src/ast/alias.rs new file mode 100644 index 0000000000..fe98b86fcf --- /dev/null +++ b/crates/wast/src/ast/alias.rs @@ -0,0 +1,43 @@ +use crate::ast::{self, kw}; +use crate::parser::{Parse, Parser, Result}; + +/// An `alias` statement used to juggle indices with nested modules. +#[derive(Debug)] +pub struct Alias<'a> { + /// Where this `alias` was defined. + pub span: ast::Span, + /// An identifier that this alias is resolved with (optionally) for name + /// resolution. + pub id: Option>, + /// An optional name for this alias stored in the custom `name` section. + pub name: Option>, + /// The instance that we're aliasing. If `None` then we're aliasing the + /// parent instance. + pub instance: Option>, + /// The item in the parent instance that we're aliasing. + pub kind: ast::ExportKind<'a>, +} + +impl<'a> Parse<'a> for Alias<'a> { + fn parse(parser: Parser<'a>) -> Result { + let span = parser.parse::()?.0; + let id = parser.parse()?; + let name = parser.parse()?; + let instance = if parser.parse::>()?.is_some() { + None + } else { + Some(parser.parens(|p| { + p.parse::()?; + p.parse() + })?) + }; + + Ok(Alias { + span, + id, + name, + instance, + kind: parser.parens(|p| p.parse())?, + }) + } +} diff --git a/crates/wast/src/ast/custom.rs b/crates/wast/src/ast/custom.rs index 568614af56..4915b4ca55 100644 --- a/crates/wast/src/ast/custom.rs +++ b/crates/wast/src/ast/custom.rs @@ -1,4 +1,4 @@ -use crate::ast::{self, kw, annotation}; +use crate::ast::{self, annotation, kw}; use crate::parser::{Parse, Parser, Result}; /// A wasm custom section within a module. @@ -36,6 +36,9 @@ pub enum CustomPlace { pub enum CustomPlaceAnchor { Type, Import, + Module, + Instance, + Alias, Func, Table, Memory, @@ -43,6 +46,7 @@ pub enum CustomPlaceAnchor { Export, Start, Elem, + ModuleCode, Code, Data, Event, @@ -61,7 +65,12 @@ impl<'a> Parse<'a> for Custom<'a> { while !parser.is_empty() { data.push(parser.parse()?); } - Ok(Custom { span, name, place, data }) + Ok(Custom { + span, + name, + place, + data, + }) } } @@ -93,51 +102,67 @@ impl<'a> Parse<'a> for CustomPlaceAnchor { fn parse(parser: Parser<'a>) -> Result { if parser.peek::() { parser.parse::()?; - return Ok(CustomPlaceAnchor::Type) + return Ok(CustomPlaceAnchor::Type); } if parser.peek::() { parser.parse::()?; - return Ok(CustomPlaceAnchor::Import) + return Ok(CustomPlaceAnchor::Import); } if parser.peek::() { parser.parse::()?; - return Ok(CustomPlaceAnchor::Func) + return Ok(CustomPlaceAnchor::Func); } if parser.peek::() { parser.parse::()?; - return Ok(CustomPlaceAnchor::Table) + return Ok(CustomPlaceAnchor::Table); } if parser.peek::() { parser.parse::()?; - return Ok(CustomPlaceAnchor::Memory) + return Ok(CustomPlaceAnchor::Memory); } if parser.peek::() { parser.parse::()?; - return Ok(CustomPlaceAnchor::Global) + return Ok(CustomPlaceAnchor::Global); } if parser.peek::() { parser.parse::()?; - return Ok(CustomPlaceAnchor::Export) + return Ok(CustomPlaceAnchor::Export); } if parser.peek::() { parser.parse::()?; - return Ok(CustomPlaceAnchor::Start) + return Ok(CustomPlaceAnchor::Start); } if parser.peek::() { parser.parse::()?; - return Ok(CustomPlaceAnchor::Elem) + return Ok(CustomPlaceAnchor::Elem); } if parser.peek::() { parser.parse::()?; - return Ok(CustomPlaceAnchor::Code) + return Ok(CustomPlaceAnchor::Code); } if parser.peek::() { parser.parse::()?; - return Ok(CustomPlaceAnchor::Data) + return Ok(CustomPlaceAnchor::Data); } if parser.peek::() { parser.parse::()?; - return Ok(CustomPlaceAnchor::Event) + return Ok(CustomPlaceAnchor::Event); + } + if parser.peek::() { + parser.parse::()?; + return Ok(CustomPlaceAnchor::Instance); + } + if parser.peek::() { + parser.parse::()?; + return Ok(CustomPlaceAnchor::Module); + } + if parser.peek::() { + parser.parse::()?; + return Ok(CustomPlaceAnchor::ModuleCode); + } + if parser.peek::() { + parser.parse::()?; + return Ok(CustomPlaceAnchor::Alias); } Err(parser.error("expected a valid section name")) diff --git a/crates/wast/src/ast/event.rs b/crates/wast/src/ast/event.rs index 2d4402394e..0746afc4c2 100644 --- a/crates/wast/src/ast/event.rs +++ b/crates/wast/src/ast/event.rs @@ -15,11 +15,11 @@ pub struct Event<'a> { } /// Listing of various types of events that can be defined in a wasm module. -#[derive(Debug)] +#[derive(Clone, Debug)] pub enum EventType<'a> { /// An exception event, where the payload is the type signature of the event /// (constructor parameters, etc). - Exception(ast::TypeUse<'a>), + Exception(ast::TypeUse<'a, ast::FunctionType<'a>>), } impl<'a> Parse<'a> for Event<'a> { diff --git a/crates/wast/src/ast/export.rs b/crates/wast/src/ast/export.rs index d3e0b32036..f96607d0d3 100644 --- a/crates/wast/src/ast/export.rs +++ b/crates/wast/src/ast/export.rs @@ -1,9 +1,11 @@ use crate::ast::{self, kw}; -use crate::parser::{Parse, Parser, Result}; +use crate::parser::{Cursor, Parse, Parser, Peek, Result}; /// A entry in a WebAssembly module's export section. #[derive(Debug)] pub struct Export<'a> { + /// Where this export was defined. + pub span: ast::Span, /// The name of this export from the module. pub name: &'a str, /// What's being exported from the module. @@ -12,7 +14,7 @@ pub struct Export<'a> { /// Different kinds of elements that can be exported from a WebAssembly module, /// contained in an [`Export`]. -#[derive(Debug)] +#[derive(Debug, Clone)] #[allow(missing_docs)] pub enum ExportKind<'a> { Func(ast::Index<'a>), @@ -20,34 +22,50 @@ pub enum ExportKind<'a> { Memory(ast::Index<'a>), Global(ast::Index<'a>), Event(ast::Index<'a>), + Module(ast::Index<'a>), + Instance(ast::Index<'a>), + Type(ast::Index<'a>), } impl<'a> Parse<'a> for Export<'a> { fn parse(parser: Parser<'a>) -> Result { - parser.parse::()?; + let span = parser.parse::()?.0; let name = parser.parse()?; - let kind = parser.parens(|parser| { - let mut l = parser.lookahead1(); - if l.peek::() { - parser.parse::()?; - Ok(ExportKind::Func(parser.parse()?)) - } else if l.peek::() { - parser.parse::()?; - Ok(ExportKind::Table(parser.parse()?)) - } else if l.peek::() { - parser.parse::()?; - Ok(ExportKind::Memory(parser.parse()?)) - } else if l.peek::() { - parser.parse::()?; - Ok(ExportKind::Global(parser.parse()?)) - } else if l.peek::() { - parser.parse::()?; - Ok(ExportKind::Event(parser.parse()?)) - } else { - Err(l.error()) - } - })?; - Ok(Export { name, kind }) + let kind = parser.parens(|p| p.parse())?; + Ok(Export { span, name, kind }) + } +} + +impl<'a> Parse<'a> for ExportKind<'a> { + fn parse(parser: Parser<'a>) -> Result { + let mut l = parser.lookahead1(); + if l.peek::() { + parser.parse::()?; + Ok(ExportKind::Func(parser.parse()?)) + } else if l.peek::() { + parser.parse::()?; + Ok(ExportKind::Table(parser.parse()?)) + } else if l.peek::() { + parser.parse::()?; + Ok(ExportKind::Memory(parser.parse()?)) + } else if l.peek::() { + parser.parse::()?; + Ok(ExportKind::Global(parser.parse()?)) + } else if l.peek::() { + parser.parse::()?; + Ok(ExportKind::Event(parser.parse()?)) + } else if l.peek::() { + parser.parse::()?; + Ok(ExportKind::Module(parser.parse()?)) + } else if l.peek::() { + parser.parse::()?; + Ok(ExportKind::Instance(parser.parse()?)) + } else if l.peek::() { + parser.parse::()?; + Ok(ExportKind::Type(parser.parse()?)) + } else { + Err(l.error()) + } } } @@ -62,7 +80,7 @@ pub struct InlineExport<'a> { impl<'a> Parse<'a> for InlineExport<'a> { fn parse(parser: Parser<'a>) -> Result { let mut names = Vec::new(); - while parser.peek2::() { + while parser.peek::() { names.push(parser.parens(|p| { p.parse::()?; p.parse::<&str>() @@ -71,3 +89,25 @@ impl<'a> Parse<'a> for InlineExport<'a> { Ok(InlineExport { names }) } } + +impl Peek for InlineExport<'_> { + fn peek(cursor: Cursor<'_>) -> bool { + let cursor = match cursor.lparen() { + Some(cursor) => cursor, + None => return false, + }; + let cursor = match cursor.keyword() { + Some(("export", cursor)) => cursor, + _ => return false, + }; + let cursor = match cursor.string() { + Some((_, cursor)) => cursor, + None => return false, + }; + cursor.rparen().is_some() + } + + fn display() -> &'static str { + "inline export" + } +} diff --git a/crates/wast/src/ast/expr.rs b/crates/wast/src/ast/expr.rs index a5568eeb22..6eed0a4b53 100644 --- a/crates/wast/src/ast/expr.rs +++ b/crates/wast/src/ast/expr.rs @@ -897,14 +897,16 @@ instructions! { #[allow(missing_docs)] pub struct BlockType<'a> { pub label: Option>, - pub ty: ast::TypeUse<'a>, + pub ty: ast::TypeUse<'a, ast::FunctionType<'a>>, } impl<'a> Parse<'a> for BlockType<'a> { fn parse(parser: Parser<'a>) -> Result { Ok(BlockType { label: parser.parse()?, - ty: ast::TypeUse::parse_no_names(parser)?, + ty: parser + .parse::>>()? + .into(), }) } } @@ -991,13 +993,14 @@ pub struct CallIndirect<'a> { /// The table that this call is going to be indexing. pub table: ast::Index<'a>, /// Type type signature that this `call_indirect` instruction is using. - pub ty: ast::TypeUse<'a>, + pub ty: ast::TypeUse<'a, ast::FunctionType<'a>>, } impl<'a> Parse<'a> for CallIndirect<'a> { fn parse(parser: Parser<'a>) -> Result { + let prev_span = parser.prev_span(); let mut table: Option<_> = parser.parse()?; - let ty = ast::TypeUse::parse_no_names(parser)?; + let ty = parser.parse::>>()?; // Turns out the official test suite at this time thinks table // identifiers comes first but wabt's test suites asserts differently // putting them second. Let's just handle both. @@ -1005,8 +1008,8 @@ impl<'a> Parse<'a> for CallIndirect<'a> { table = parser.parse()?; } Ok(CallIndirect { - table: table.unwrap_or(ast::Index::Num(0)), - ty, + table: table.unwrap_or(ast::Index::Num(0, prev_span)), + ty: ty.into(), }) } } @@ -1022,10 +1025,11 @@ pub struct TableInit<'a> { impl<'a> Parse<'a> for TableInit<'a> { fn parse(parser: Parser<'a>) -> Result { + let prev_span = parser.prev_span(); let table_or_elem = parser.parse()?; let (table, elem) = match parser.parse()? { Some(elem) => (table_or_elem, elem), - None => (ast::Index::Num(0), table_or_elem), + None => (ast::Index::Num(0, prev_span), table_or_elem), }; Ok(TableInit { table, elem }) } @@ -1045,7 +1049,8 @@ impl<'a> Parse<'a> for TableCopy<'a> { let (dst, src) = if let Some(dst) = parser.parse()? { (dst, parser.parse()?) } else { - (ast::Index::Num(0), ast::Index::Num(0)) + let span = parser.prev_span(); + (ast::Index::Num(0, span), ast::Index::Num(0, span)) }; Ok(TableCopy { dst, src }) } @@ -1063,7 +1068,7 @@ impl<'a> Parse<'a> for TableArg<'a> { let dst = if let Some(dst) = parser.parse()? { dst } else { - ast::Index::Num(0) + ast::Index::Num(0, parser.prev_span()) }; Ok(TableArg { dst }) } diff --git a/crates/wast/src/ast/func.rs b/crates/wast/src/ast/func.rs index cf03627ccb..e54d2aee6e 100644 --- a/crates/wast/src/ast/func.rs +++ b/crates/wast/src/ast/func.rs @@ -20,7 +20,7 @@ pub struct Func<'a> { /// function. pub kind: FuncKind<'a>, /// The type that this function will have. - pub ty: ast::TypeUse<'a>, + pub ty: ast::TypeUse<'a, ast::FunctionType<'a>>, } /// Possible ways to define a function in the text format. @@ -31,19 +31,18 @@ pub enum FuncKind<'a> { /// ```text /// (func (type 3) (import "foo" "bar")) /// ``` - Import { - /// The module that this function is imported from - module: &'a str, - /// The module field name this function is imported from - field: &'a str, - }, + Import(ast::InlineImport<'a>), /// Almost all functions, those defined inline in a wasm module. Inline { /// The list of locals, if any, for this function. Each local has an /// optional identifier for name resolution and name for the custom /// `name` section associated with it. - locals: Vec<(Option>, Option>, ast::ValType<'a>)>, + locals: Vec<( + Option>, + Option>, + ast::ValType<'a>, + )>, /// The instructions of the function. expression: ast::Expression<'a>, @@ -57,12 +56,8 @@ impl<'a> Parse<'a> for Func<'a> { let name = parser.parse()?; let exports = parser.parse()?; - let (ty, kind) = if parser.peek2::() { - let (module, field) = parser.parens(|p| { - p.parse::()?; - Ok((p.parse()?, p.parse()?)) - })?; - (parser.parse()?, FuncKind::Import { module, field }) + let (ty, kind) = if let Some(import) = parser.parse()? { + (parser.parse()?, FuncKind::Import(import)) } else { let ty = parser.parse()?; let mut locals = Vec::new(); diff --git a/crates/wast/src/ast/global.rs b/crates/wast/src/ast/global.rs index acdc2532d1..78ca637488 100644 --- a/crates/wast/src/ast/global.rs +++ b/crates/wast/src/ast/global.rs @@ -25,12 +25,7 @@ pub enum GlobalKind<'a> { /// ```text /// (global i32 (import "foo" "bar")) /// ``` - Import { - /// The module that this function is imported from - module: &'a str, - /// The module field name this function is imported from - field: &'a str, - }, + Import(ast::InlineImport<'a>), /// A global defined inline in the module itself Inline(ast::Expression<'a>), @@ -42,12 +37,8 @@ impl<'a> Parse<'a> for Global<'a> { let id = parser.parse()?; let exports = parser.parse()?; - let (ty, kind) = if parser.peek2::() { - let (module, field) = parser.parens(|p| { - p.parse::()?; - Ok((p.parse()?, p.parse()?)) - })?; - (parser.parse()?, GlobalKind::Import { module, field }) + let (ty, kind) = if let Some(import) = parser.parse()? { + (parser.parse()?, GlobalKind::Import(import)) } else { (parser.parse()?, GlobalKind::Inline(parser.parse()?)) }; diff --git a/crates/wast/src/ast/import.rs b/crates/wast/src/ast/import.rs index 774ec679b1..2a57086448 100644 --- a/crates/wast/src/ast/import.rs +++ b/crates/wast/src/ast/import.rs @@ -1,73 +1,176 @@ use crate::ast::{self, kw}; -use crate::parser::{Parse, Parser, Result}; +use crate::parser::{Cursor, Parse, Parser, Peek, Result}; /// An `import` statement and entry in a WebAssembly module. -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct Import<'a> { /// Where this `import` was defined pub span: ast::Span, /// The module that this statement is importing from pub module: &'a str, /// The name of the field in the module this statement imports from. - pub field: &'a str, + pub field: Option<&'a str>, + /// The item that's being imported. + pub item: ItemSig<'a>, +} + +impl<'a> Parse<'a> for Import<'a> { + fn parse(parser: Parser<'a>) -> Result { + let span = parser.parse::()?.0; + let module = parser.parse()?; + let field = parser.parse()?; + let item = parser.parens(|p| p.parse())?; + Ok(Import { + span, + module, + field, + item, + }) + } +} + +#[derive(Debug, Clone)] +#[allow(missing_docs)] +pub struct ItemSig<'a> { + /// Where this item is defined in the source. + pub span: ast::Span, /// An optional identifier used during name resolution to refer to this item /// from the rest of the module. pub id: Option>, /// An optional name which, for functions, will be stored in the /// custom `name` section. pub name: Option>, - /// What kind of item is being imported. - pub kind: ImportKind<'a>, + /// What kind of item this is. + pub kind: ItemKind<'a>, } -/// All possible types of items that can be imported into a wasm module. -#[derive(Debug)] +#[derive(Debug, Clone)] #[allow(missing_docs)] -pub enum ImportKind<'a> { - Func(ast::TypeUse<'a>), +pub enum ItemKind<'a> { + Func(ast::TypeUse<'a, ast::FunctionType<'a>>), Table(ast::TableType), Memory(ast::MemoryType), Global(ast::GlobalType<'a>), Event(ast::EventType<'a>), + Module(ast::TypeUse<'a, ast::ModuleType<'a>>), + Instance(ast::TypeUse<'a, ast::InstanceType<'a>>), } -impl<'a> Parse<'a> for Import<'a> { +impl<'a> Parse<'a> for ItemSig<'a> { fn parse(parser: Parser<'a>) -> Result { - let span = parser.parse::()?.0; - let module = parser.parse()?; - let field = parser.parse()?; - let (id, name, kind) = parser.parens(|parser| { - let mut l = parser.lookahead1(); - if l.peek::() { - parser.parse::()?; - Ok(( - parser.parse()?, - parser.parse()?, - ImportKind::Func(parser.parse()?), - )) - } else if l.peek::() { - parser.parse::()?; - Ok((parser.parse()?, None, ImportKind::Table(parser.parse()?))) - } else if l.peek::() { - parser.parse::()?; - Ok((parser.parse()?, None, ImportKind::Memory(parser.parse()?))) - } else if l.peek::() { - parser.parse::()?; - Ok((parser.parse()?, None, ImportKind::Global(parser.parse()?))) - } else if l.peek::() { - parser.parse::()?; - Ok((parser.parse()?, None, ImportKind::Event(parser.parse()?))) - } else { - Err(l.error()) - } - })?; - Ok(Import { - span, - module, - field, - id, - kind, - name, + let mut l = parser.lookahead1(); + if l.peek::() { + let span = parser.parse::()?.0; + Ok(ItemSig { + span, + id: parser.parse()?, + name: parser.parse()?, + kind: ItemKind::Func(parser.parse()?), + }) + } else if l.peek::() { + let span = parser.parse::()?.0; + Ok(ItemSig { + span, + id: parser.parse()?, + name: None, + kind: ItemKind::Table(parser.parse()?), + }) + } else if l.peek::() { + let span = parser.parse::()?.0; + Ok(ItemSig { + span, + id: parser.parse()?, + name: None, + kind: ItemKind::Memory(parser.parse()?), + }) + } else if l.peek::() { + let span = parser.parse::()?.0; + Ok(ItemSig { + span, + id: parser.parse()?, + name: None, + kind: ItemKind::Global(parser.parse()?), + }) + } else if l.peek::() { + let span = parser.parse::()?.0; + Ok(ItemSig { + span, + id: parser.parse()?, + name: None, + kind: ItemKind::Event(parser.parse()?), + }) + } else if l.peek::() { + let span = parser.parse::()?.0; + Ok(ItemSig { + span, + id: parser.parse()?, + name: None, + kind: ItemKind::Module(parser.parse()?), + }) + } else if l.peek::() { + let span = parser.parse::()?.0; + Ok(ItemSig { + span, + id: parser.parse()?, + name: None, + kind: ItemKind::Instance(parser.parse()?), + }) + } else { + Err(l.error()) + } + } +} + +/// A listing of a inline `(import "foo")` statement. +/// +/// Note that when parsing this type it is somewhat unconventional that it +/// parses its own surrounding parentheses. This is typically an optional type, +/// so it's so far been a bit nicer to have the optionality handled through +/// `Peek` rather than `Option`. +#[derive(Debug, Copy, Clone)] +#[allow(missing_docs)] +pub struct InlineImport<'a> { + pub module: &'a str, + pub field: Option<&'a str>, +} + +impl<'a> Parse<'a> for InlineImport<'a> { + fn parse(parser: Parser<'a>) -> Result { + parser.parens(|p| { + p.parse::()?; + Ok(InlineImport { + module: p.parse()?, + field: p.parse()?, + }) }) } } + +impl Peek for InlineImport<'_> { + fn peek(cursor: Cursor<'_>) -> bool { + let cursor = match cursor.lparen() { + Some(cursor) => cursor, + None => return false, + }; + let cursor = match cursor.keyword() { + Some(("import", cursor)) => cursor, + _ => return false, + }; + let cursor = match cursor.string() { + Some((_, cursor)) => cursor, + None => return false, + }; + + // optional field + let cursor = match cursor.string() { + Some((_, cursor)) => cursor, + None => cursor, + }; + + cursor.rparen().is_some() + } + + fn display() -> &'static str { + "inline import" + } +} diff --git a/crates/wast/src/ast/instance.rs b/crates/wast/src/ast/instance.rs new file mode 100644 index 0000000000..03fb9b8444 --- /dev/null +++ b/crates/wast/src/ast/instance.rs @@ -0,0 +1,69 @@ +use crate::ast::{self, kw}; +use crate::parser::{Parse, Parser, Result}; + +/// A nested WebAssembly instance to be created as part of a module. +#[derive(Debug)] +pub struct Instance<'a> { + /// Where this `instance` was defined. + pub span: ast::Span, + /// An identifier that this instance is resolved with (optionally) for name + /// resolution. + pub id: Option>, + /// If present, inline export annotations which indicate names this + /// definition should be exported under. + pub exports: ast::InlineExport<'a>, + /// What kind of instance this is, be it an inline-defined or imported one. + pub kind: InstanceKind<'a>, +} + +/// Possible ways to define a instance in the text format. +#[derive(Debug)] +pub enum InstanceKind<'a> { + /// An instance which is actually defined as an import, such as: + Import { + /// Where we're importing from + import: ast::InlineImport<'a>, + /// The type that this instance will have. + ty: ast::TypeUse<'a, ast::InstanceType<'a>>, + }, + + /// Instances whose instantiation is defined inline. + Inline { + /// Module that we're instantiating + module: ast::Index<'a>, + /// Items used to instantiate the instance + items: Vec>, + }, +} + +impl<'a> Parse<'a> for Instance<'a> { + fn parse(parser: Parser<'a>) -> Result { + let span = parser.parse::()?.0; + let id = parser.parse()?; + let exports = parser.parse()?; + + let kind = if let Some(import) = parser.parse()? { + InstanceKind::Import { + import, + ty: parser.parse()?, + } + } else { + parser.parens(|p| { + p.parse::()?; + let module = p.parse()?; + let mut items = Vec::new(); + while !p.is_empty() { + items.push(p.parens(|p| p.parse())?); + } + Ok(InstanceKind::Inline { module, items }) + })? + }; + + Ok(Instance { + span, + id, + exports, + kind, + }) + } +} diff --git a/crates/wast/src/ast/memory.rs b/crates/wast/src/ast/memory.rs index 39c367f0bf..93b772bf58 100644 --- a/crates/wast/src/ast/memory.rs +++ b/crates/wast/src/ast/memory.rs @@ -21,8 +21,7 @@ pub enum MemoryKind<'a> { /// This memory is actually an inlined import definition. #[allow(missing_docs)] Import { - module: &'a str, - field: &'a str, + import: ast::InlineImport<'a>, ty: ast::MemoryType, }, @@ -41,39 +40,25 @@ impl<'a> Parse<'a> for Memory<'a> { // Afterwards figure out which style this is, either: // - // * `(data ...)` // * `(import "a" "b") limits` + // * `(data ...)` // * `limits` let mut l = parser.lookahead1(); - let kind = if l.peek::() { - enum Which<'a, T> { - Inline(Vec), - Import(&'a str, &'a str), + let kind = if let Some(import) = parser.parse()? { + MemoryKind::Import { + import, + ty: parser.parse()?, } - let result = parser.parens(|parser| { - let mut l = parser.lookahead1(); - if l.peek::() { - parser.parse::()?; - let mut data = Vec::new(); - while !parser.is_empty() { - data.push(parser.parse()?); - } - Ok(Which::Inline(data)) - } else if l.peek::() { - parser.parse::()?; - Ok(Which::Import(parser.parse()?, parser.parse()?)) - } else { - Err(l.error()) + } else if l.peek::() { + let data = parser.parens(|parser| { + parser.parse::()?; + let mut data = Vec::new(); + while !parser.is_empty() { + data.push(parser.parse()?); } + Ok(data) })?; - match result { - Which::Inline(data) => MemoryKind::Inline(data), - Which::Import(module, field) => MemoryKind::Import { - module, - field, - ty: parser.parse()?, - }, - } + MemoryKind::Inline(data) } else if l.peek::() { MemoryKind::Normal(parser.parse()?) } else { @@ -156,7 +141,7 @@ impl<'a> Parse<'a> for Data<'a> { parser.parse() })?; DataKind::Active { - memory: memory.unwrap_or(ast::Index::Num(0)), + memory: memory.unwrap_or(ast::Index::Num(0, span)), offset, } }; diff --git a/crates/wast/src/ast/mod.rs b/crates/wast/src/ast/mod.rs index 07cd6dfb59..2766d20ea2 100644 --- a/crates/wast/src/ast/mod.rs +++ b/crates/wast/src/ast/mod.rs @@ -301,6 +301,7 @@ reexport! { #[cfg(feature = "wasm-module")] reexport! { + mod alias; mod assert_expr; mod custom; mod event; @@ -309,8 +310,10 @@ reexport! { mod func; mod global; mod import; + mod instance; mod memory; mod module; + mod nested_module; mod table; mod types; mod wast; @@ -319,6 +322,7 @@ reexport! { /// Common keyword used to parse WebAssembly text files. pub mod kw { custom_keyword!(after); + custom_keyword!(alias); custom_keyword!(any); custom_keyword!(anyfunc); custom_keyword!(anyref); @@ -373,12 +377,15 @@ pub mod kw { custom_keyword!(i8); custom_keyword!(i8x16); custom_keyword!(import); + custom_keyword!(instance); + custom_keyword!(instantiate); custom_keyword!(invoke); custom_keyword!(item); custom_keyword!(last); custom_keyword!(local); custom_keyword!(memory); custom_keyword!(module); + custom_keyword!(modulecode); custom_keyword!(nan_arithmetic = "nan:arithmetic"); custom_keyword!(nan_canonical = "nan:canonical"); custom_keyword!(null); @@ -387,6 +394,7 @@ pub mod kw { custom_keyword!(opt); custom_keyword!(optref); custom_keyword!(param); + custom_keyword!(parent); custom_keyword!(passive); custom_keyword!(quote); custom_keyword!(r#else = "else"); diff --git a/crates/wast/src/ast/module.rs b/crates/wast/src/ast/module.rs index 10c84889a3..e923f01c5c 100644 --- a/crates/wast/src/ast/module.rs +++ b/crates/wast/src/ast/module.rs @@ -1,4 +1,4 @@ -use crate::ast::{self, kw, annotation}; +use crate::ast::{self, annotation, kw}; use crate::parser::{Parse, Parser, Result}; pub use crate::resolve::Names; @@ -166,11 +166,15 @@ pub enum ModuleField<'a> { Memory(ast::Memory<'a>), Global(ast::Global<'a>), Export(ast::Export<'a>), + ExportAll(ast::Span, ast::Id<'a>), Start(ast::Index<'a>), Elem(ast::Elem<'a>), Data(ast::Data<'a>), Event(ast::Event<'a>), Custom(ast::Custom<'a>), + Instance(ast::Instance<'a>), + NestedModule(ast::NestedModule<'a>), + Alias(ast::Alias<'a>), } impl<'a> ModuleField<'a> { @@ -204,6 +208,10 @@ impl<'a> Parse<'a> for ModuleField<'a> { return Ok(ModuleField::Global(parser.parse()?)); } if parser.peek::() { + if parser.peek2::() { + let span = parser.parse::()?.0; + return Ok(ModuleField::ExportAll(span, parser.parse()?)); + } return Ok(ModuleField::Export(parser.parse()?)); } if parser.peek::() { @@ -222,6 +230,15 @@ impl<'a> Parse<'a> for ModuleField<'a> { if parser.peek::() { return Ok(ModuleField::Custom(parser.parse()?)); } + if parser.peek::() { + return Ok(ModuleField::Instance(parser.parse()?)); + } + if parser.peek::() { + return Ok(ModuleField::NestedModule(parser.parse()?)); + } + if parser.peek::() { + return Ok(ModuleField::Alias(parser.parse()?)); + } Err(parser.error("expected valid module field")) } } diff --git a/crates/wast/src/ast/nested_module.rs b/crates/wast/src/ast/nested_module.rs new file mode 100644 index 0000000000..f9385aedad --- /dev/null +++ b/crates/wast/src/ast/nested_module.rs @@ -0,0 +1,112 @@ +use crate::ast::{self, kw}; +use crate::parser::{Cursor, Parse, Parser, Peek, Result}; + +/// A nested WebAssembly nested module to be created as part of a module. +#[derive(Debug)] +pub struct NestedModule<'a> { + /// Where this `nested module` was defined. + pub span: ast::Span, + /// An identifier that this nested module is resolved with (optionally) for name + /// resolution. + pub id: Option>, + /// An optional name for this module stored in the custom `name` section. + pub name: Option>, + /// If present, inline export annotations which indicate names this + /// definition should be exported under. + pub exports: ast::InlineExport<'a>, + /// What kind of nested module this is, be it an inline-defined or imported one. + pub kind: NestedModuleKind<'a>, +} + +/// Possible ways to define a nested module in the text format. +#[derive(Debug)] +pub enum NestedModuleKind<'a> { + /// An nested module which is actually defined as an import, such as: + Import { + /// Where this nested module is imported from + import: ast::InlineImport<'a>, + /// The type that this nested module will have. + ty: ast::TypeUse<'a, ast::ModuleType<'a>>, + }, + + /// Nested modules whose instantiation is defined inline. + Inline { + /// Optional type reference + ty: Option>, + /// Fields in the nested module. + fields: Vec>, + }, +} + +impl<'a> Parse<'a> for NestedModule<'a> { + fn parse(parser: Parser<'a>) -> Result { + let span = parser.parse::()?.0; + let id = parser.parse()?; + let name = parser.parse()?; + let exports = parser.parse()?; + + let kind = if let Some(import) = parser.parse()? { + NestedModuleKind::Import { + import, + ty: parser.parse()?, + } + } else { + let ty = if parser.peek::() { + Some(parser.parens(|p| { + p.parse::()?; + p.parse() + })?) + } else { + None + }; + let mut fields = Vec::new(); + while !parser.is_empty() { + fields.push(parser.parens(|p| p.parse())?); + } + NestedModuleKind::Inline { ty, fields } + }; + + Ok(NestedModule { + span, + id, + name, + exports, + kind, + }) + } +} + +// Note that this is a custom implementation to get multi-token lookahead to +// figure out how to parse `(type ...` when it's in an inline module. If this is +// `(type $x)` or `(type 0)` then it's an inline type annotation, otherwise it's +// probably a typedef like `(type $x (func))` or something like that. We only +// want to parse the two-token variant right now. +struct InlineType; + +impl Peek for InlineType { + fn peek(cursor: Cursor<'_>) -> bool { + let cursor = match cursor.lparen() { + Some(cursor) => cursor, + None => return false, + }; + let cursor = match cursor.keyword() { + Some(("type", cursor)) => cursor, + _ => return false, + }; + + // optional identifier + let cursor = match cursor.id() { + Some((_, cursor)) => cursor, + None => match cursor.integer() { + Some((_, cursor)) => cursor, + None => return false, + }, + }; + + cursor.rparen().is_some() + } + + fn display() -> &'static str { + "inline type" + } +} diff --git a/crates/wast/src/ast/table.rs b/crates/wast/src/ast/table.rs index eee4b24dbf..932905bffc 100644 --- a/crates/wast/src/ast/table.rs +++ b/crates/wast/src/ast/table.rs @@ -21,8 +21,7 @@ pub enum TableKind<'a> { /// This table is actually an inlined import definition. #[allow(missing_docs)] Import { - module: &'a str, - field: &'a str, + import: ast::InlineImport<'a>, ty: ast::TableType, }, @@ -65,14 +64,9 @@ impl<'a> Parse<'a> for Table<'a> { TableKind::Inline { elem, payload } } else if l.peek::() { TableKind::Normal(parser.parse()?) - } else if l.peek::() { - let (module, field) = parser.parens(|p| { - p.parse::()?; - Ok((p.parse()?, p.parse()?)) - })?; + } else if let Some(import) = parser.parse()? { TableKind::Import { - module, - field, + import, ty: parser.parse()?, } } else { @@ -149,7 +143,7 @@ impl<'a> Parse<'a> for Elem<'a> { p.parse() })?) } else if parser.peek::() { - Some(ast::Index::Num(parser.parse()?)) + Some(parser.parse()?) } else { None }; @@ -160,7 +154,7 @@ impl<'a> Parse<'a> for Elem<'a> { parser.parse() })?; ElemKind::Active { - table: table.unwrap_or(ast::Index::Num(0)), + table: table.unwrap_or(ast::Index::Num(0, span)), offset, } } else if parser.peek::() { @@ -221,7 +215,10 @@ impl<'a> ElemPayload<'a> { } } -fn parse_ref_func<'a>(parser: Parser<'a>, ty: ast::TableElemType) -> Result>> { +fn parse_ref_func<'a>( + parser: Parser<'a>, + ty: ast::TableElemType, +) -> Result>> { let mut l = parser.lookahead1(); if l.peek::() { parser.parse::()?; diff --git a/crates/wast/src/ast/token.rs b/crates/wast/src/ast/token.rs index 1247956ada..8ba13a3107 100644 --- a/crates/wast/src/ast/token.rs +++ b/crates/wast/src/ast/token.rs @@ -44,10 +44,23 @@ impl Span { #[derive(Copy, Clone)] pub struct Id<'a> { name: &'a str, + gen: u32, span: Span, } impl<'a> Id<'a> { + fn new(name: &'a str, span: Span) -> Id<'a> { + Id { name, gen: 0, span } + } + + pub(crate) fn gensym(span: Span, gen: u32) -> Id<'a> { + Id { + name: "gensym", + gen, + span, + } + } + /// Returns the underlying name of this identifier. /// /// The name returned does not contain the leading `$`. @@ -59,17 +72,22 @@ impl<'a> Id<'a> { pub fn span(&self) -> Span { self.span } + + pub(crate) fn is_gensym(&self) -> bool { + self.gen != 0 + } } impl<'a> Hash for Id<'a> { fn hash(&self, hasher: &mut H) { self.name.hash(hasher); + self.gen.hash(hasher); } } impl<'a> PartialEq for Id<'a> { fn eq(&self, other: &Id<'a>) -> bool { - self.name == other.name + self.name == other.name && self.gen == other.gen } } @@ -79,13 +97,7 @@ impl<'a> Parse<'a> for Id<'a> { fn parse(parser: Parser<'a>) -> Result { parser.step(|c| { if let Some((name, rest)) = c.id() { - return Ok(( - Id { - name, - span: c.cur_span(), - }, - rest, - )); + return Ok((Id::new(name, c.cur_span()), rest)); } Err(c.error("expected an identifier")) }) @@ -116,23 +128,34 @@ impl Peek for Id<'_> { /// /// The emission phase of a module will ensure that `Index::Id` is never used /// and switch them all to `Index::Num`. -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +#[derive(Copy, Clone, Debug)] pub enum Index<'a> { /// A numerical index that this references. The index space this is /// referencing is implicit based on where this [`Index`] is stored. - Num(u32), + Num(u32, Span), /// A human-readable identifier this references. Like `Num`, the namespace /// this references is based on where this is stored. Id(Id<'a>), } +impl Index<'_> { + /// Returns the source location where this `Index` was defined. + pub fn span(&self) -> Span { + match self { + Index::Num(_, span) => *span, + Index::Id(id) => id.span(), + } + } +} + impl<'a> Parse<'a> for Index<'a> { fn parse(parser: Parser<'a>) -> Result { let mut l = parser.lookahead1(); if l.peek::() { Ok(Index::Id(parser.parse()?)) } else if l.peek::() { - Ok(Index::Num(parser.parse()?)) + let (val, span) = parser.parse()?; + Ok(Index::Num(val, span)) } else { Err(l.error()) } @@ -149,6 +172,33 @@ impl Peek for Index<'_> { } } +impl PartialEq for Index<'_> { + fn eq(&self, other: &Index<'_>) -> bool { + match (self, other) { + (Index::Num(a, _), Index::Num(b, _)) => a == b, + (Index::Id(a), Index::Id(b)) => a == b, + _ => false, + } + } +} + +impl Eq for Index<'_> {} + +impl Hash for Index<'_> { + fn hash(&self, hasher: &mut H) { + match self { + Index::Num(a, _) => { + 0u8.hash(hasher); + a.hash(hasher); + } + Index::Id(a) => { + 1u8.hash(hasher); + a.hash(hasher); + } + } + } +} + /// An `@name` annotation in source, currently of the form `@name "foo"` #[derive(Copy, Clone, PartialEq, Debug)] pub struct NameAnnotation<'a> { @@ -178,6 +228,12 @@ impl<'a> Parse<'a> for Option> { macro_rules! integers { ($($i:ident($u:ident))*) => ($( impl<'a> Parse<'a> for $i { + fn parse(parser: Parser<'a>) -> Result { + Ok(parser.parse::<($i, Span)>()?.0) + } + } + + impl<'a> Parse<'a> for ($i, Span) { fn parse(parser: Parser<'a>) -> Result { parser.step(|c| { if let Some((i, rest)) = c.integer() { @@ -187,7 +243,7 @@ macro_rules! integers { $u::from_str_radix(s, base).map(|i| i as $i) }); return match val { - Ok(n) => Ok((n, rest)), + Ok(n) => Ok(((n, c.cur_span()), rest)), Err(_) => Err(c.error(concat!( "invalid ", stringify!($i), diff --git a/crates/wast/src/ast/types.rs b/crates/wast/src/ast/types.rs index 797792e6ad..a988c0be56 100644 --- a/crates/wast/src/ast/types.rs +++ b/crates/wast/src/ast/types.rs @@ -153,7 +153,7 @@ impl<'a> Parse<'a> for RefType<'a> { } /// Type for a `global` in a wasm module -#[derive(Copy, Clone, Debug)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] pub struct GlobalType<'a> { /// The element type of this `global` pub ty: ValType<'a>, @@ -181,7 +181,7 @@ impl<'a> Parse<'a> for GlobalType<'a> { } /// List of different kinds of table types we can have. -#[derive(Copy, Clone, Debug)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] pub enum TableElemType { /// An element for a table that is a list of functions. Funcref, @@ -233,7 +233,7 @@ impl Peek for TableElemType { } /// Min/max limits used for tables/memories. -#[derive(Copy, Clone, Debug)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] pub struct Limits { /// The minimum number of units for this type. pub min: u32, @@ -254,7 +254,7 @@ impl<'a> Parse<'a> for Limits { } /// Configuration for a table of a wasm mdoule -#[derive(Copy, Clone, Debug)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] pub struct TableType { /// Limits on the element sizes of this table pub limits: Limits, @@ -272,7 +272,7 @@ impl<'a> Parse<'a> for TableType { } /// Configuration for a memory of a wasm module -#[derive(Copy, Clone, Debug)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] pub struct MemoryType { /// Limits on the page sizes of this memory pub limits: Limits, @@ -289,11 +289,15 @@ impl<'a> Parse<'a> for MemoryType { } /// A function type with parameters and results. -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Default)] pub struct FunctionType<'a> { /// The parameters of a function, optionally each having an identifier for /// name resolution and a name for the custom `name` section. - pub params: Vec<(Option>, Option>, ValType<'a>)>, + pub params: Vec<( + Option>, + Option>, + ValType<'a>, + )>, /// The results types of a function. pub results: Vec>, } @@ -342,7 +346,6 @@ impl<'a> FunctionType<'a> { impl<'a> Parse<'a> for FunctionType<'a> { fn parse(parser: Parser<'a>) -> Result { - parser.parse::()?; let mut ret = FunctionType { params: Vec::new(), results: Vec::new(), @@ -352,6 +355,54 @@ impl<'a> Parse<'a> for FunctionType<'a> { } } +impl<'a> Peek for FunctionType<'a> { + fn peek(cursor: Cursor<'_>) -> bool { + if let Some(next) = cursor.lparen() { + match next.keyword() { + Some(("param", _)) | Some(("result", _)) => return true, + _ => {} + } + } + + false + } + + fn display() -> &'static str { + "function type" + } +} + +/// A function type with parameters and results. +#[derive(Clone, Debug, Default)] +pub struct FunctionTypeNoNames<'a>(pub FunctionType<'a>); + +impl<'a> Parse<'a> for FunctionTypeNoNames<'a> { + fn parse(parser: Parser<'a>) -> Result { + let mut ret = FunctionType { + params: Vec::new(), + results: Vec::new(), + }; + ret.finish_parse(false, parser)?; + Ok(FunctionTypeNoNames(ret)) + } +} + +impl<'a> Peek for FunctionTypeNoNames<'a> { + fn peek(cursor: Cursor<'_>) -> bool { + FunctionType::peek(cursor) + } + + fn display() -> &'static str { + FunctionType::display() + } +} + +impl<'a> From> for FunctionType<'a> { + fn from(ty: FunctionTypeNoNames<'a>) -> FunctionType<'a> { + ty.0 + } +} + /// A struct type with fields. #[derive(Clone, Debug)] pub struct StructType<'a> { @@ -361,10 +412,7 @@ pub struct StructType<'a> { impl<'a> Parse<'a> for StructType<'a> { fn parse(parser: Parser<'a>) -> Result { - parser.parse::()?; - let mut ret = StructType { - fields: Vec::new(), - }; + let mut ret = StructType { fields: Vec::new() }; while !parser.is_empty() { let field = if parser.peek2::() { parser.parens(|parser| { @@ -393,11 +441,7 @@ pub struct StructField<'a> { impl<'a> StructField<'a> { fn parse(parser: Parser<'a>, with_id: bool) -> Result { - let id = if with_id { - parser.parse()? - } else { - None - }; + let id = if with_id { parser.parse()? } else { None }; let (ty, mutable) = if parser.peek2::() { let ty = parser.parens(|parser| { parser.parse::()?; @@ -407,11 +451,7 @@ impl<'a> StructField<'a> { } else { (parser.parse::>()?, false) }; - Ok(StructField { - id, - mutable, - ty, - }) + Ok(StructField { id, mutable, ty }) } } @@ -426,7 +466,6 @@ pub struct ArrayType<'a> { impl<'a> Parse<'a> for ArrayType<'a> { fn parse(parser: Parser<'a>) -> Result { - parser.parse::()?; let (ty, mutable) = if parser.peek2::() { let ty = parser.parens(|parser| { parser.parse::()?; @@ -436,13 +475,119 @@ impl<'a> Parse<'a> for ArrayType<'a> { } else { (parser.parse::>()?, false) }; - Ok(ArrayType { - mutable, - ty + Ok(ArrayType { mutable, ty }) + } +} + +/// A type for a nested module +#[derive(Clone, Debug, Default)] +pub struct ModuleType<'a> { + /// The imports that are expected for this module type. + pub imports: Vec>, + /// The exports that this module type is expected to have. + pub exports: Vec>, + /// Instances within this module which are entirely exported. + pub instance_exports: Vec<(ast::Span, ast::Id<'a>)>, +} + +impl<'a> Parse<'a> for ModuleType<'a> { + fn parse(parser: Parser<'a>) -> Result { + let mut imports = Vec::new(); + while parser.peek2::() { + imports.push(parser.parens(|p| p.parse())?); + } + let mut exports = Vec::new(); + let mut instance_exports = Vec::new(); + while parser.peek2::() { + parser.parens(|p| { + if p.peek2::() { + let span = p.parse::()?.0; + instance_exports.push((span, p.parse()?)); + } else { + exports.push(p.parse()?); + } + Ok(()) + })?; + } + Ok(ModuleType { + imports, + exports, + instance_exports, }) } } +impl<'a> Peek for ModuleType<'a> { + fn peek(cursor: Cursor<'_>) -> bool { + if let Some(next) = cursor.lparen() { + match next.keyword() { + Some(("import", _)) | Some(("export", _)) => return true, + _ => {} + } + } + + false + } + + fn display() -> &'static str { + "module type" + } +} + +/// A type for a nested instance +#[derive(Clone, Debug, Default)] +pub struct InstanceType<'a> { + /// The exported types from this instance + pub exports: Vec>, +} + +impl<'a> Parse<'a> for InstanceType<'a> { + fn parse(parser: Parser<'a>) -> Result { + let mut exports = Vec::new(); + while !parser.is_empty() { + exports.push(parser.parens(|p| p.parse())?); + } + Ok(InstanceType { exports }) + } +} + +impl<'a> Peek for InstanceType<'a> { + fn peek(cursor: Cursor<'_>) -> bool { + if let Some(next) = cursor.lparen() { + match next.keyword() { + Some(("export", _)) => return true, + _ => {} + } + } + + false + } + + fn display() -> &'static str { + "instance type" + } +} + +/// The type of an exported item from a module or instance. +#[derive(Debug, Clone)] +pub struct ExportType<'a> { + /// Where this export was defined. + pub span: ast::Span, + /// The name of this export. + pub name: &'a str, + /// The signature of the item that's exported. + pub item: ast::ItemSig<'a>, +} + +impl<'a> Parse<'a> for ExportType<'a> { + fn parse(parser: Parser<'a>) -> Result { + let span = parser.parse::()?.0; + let name = parser.parse()?; + let item = parser.parens(|p| p.parse())?; + Ok(ExportType { span, name, item }) + } +} + /// A definition of a type. #[derive(Debug)] pub enum TypeDef<'a> { @@ -452,11 +597,17 @@ pub enum TypeDef<'a> { Struct(StructType<'a>), /// An array type definition. Array(ArrayType<'a>), + /// A module type definition. + Module(ModuleType<'a>), + /// An instance type definition. + Instance(InstanceType<'a>), } /// A type declaration in a module #[derive(Debug)] pub struct Type<'a> { + /// Where this type was defined. + pub span: ast::Span, /// An optional identifer to refer to this `type` by as part of name /// resolution. pub id: Option>, @@ -466,77 +617,74 @@ pub struct Type<'a> { impl<'a> Parse<'a> for Type<'a> { fn parse(parser: Parser<'a>) -> Result { - parser.parse::()?; + let span = parser.parse::()?.0; let id = parser.parse()?; let def = parser.parens(|parser| { let mut l = parser.lookahead1(); if l.peek::() { + parser.parse::()?; Ok(TypeDef::Func(parser.parse()?)) } else if l.peek::() { + parser.parse::()?; Ok(TypeDef::Struct(parser.parse()?)) } else if l.peek::() { + parser.parse::()?; Ok(TypeDef::Array(parser.parse()?)) + } else if l.peek::() { + parser.parse::()?; + Ok(TypeDef::Module(parser.parse()?)) + } else if l.peek::() { + parser.parse::()?; + Ok(TypeDef::Instance(parser.parse()?)) } else { Err(l.error()) } })?; - Ok(Type { id, def }) + Ok(Type { span, id, def }) } } /// A reference to a type defined in this module. -/// -/// This is a pretty tricky type used in a lot of places and is somewhat subtly -/// handled as well. In general `(type)` or `(param)` annotations are parsed as -/// this. #[derive(Clone, Debug)] -pub struct TypeUse<'a> { - /// The span of the index specifier, if it was found - pub index_span: Option, +pub struct TypeUse<'a, T> { /// The type that we're referencing, if it was present. pub index: Option>, - /// The inline function type defined. If nothing was defined inline this is - /// empty. - pub func_ty: ast::FunctionType<'a>, + /// The inline type, if present. + pub inline: Option, } -impl<'a> TypeUse<'a> { - /// Parse a `TypeUse`, but don't allow any names of `param` tokens. - pub fn parse_no_names(parser: Parser<'a>) -> Result { - TypeUse::parse_allow_names(parser, false) +impl<'a, T> TypeUse<'a, T> { + /// Constructs a new instance of `TypeUse` without an inline definition but + /// with an index specified. + pub fn new_with_index(index: ast::Index<'a>) -> TypeUse<'a, T> { + TypeUse { + index: Some(index), + inline: None, + } } +} - fn parse_allow_names(parser: Parser<'a>, allow_names: bool) -> Result { +impl<'a, T: Peek + Parse<'a>> Parse<'a> for TypeUse<'a, T> { + fn parse(parser: Parser<'a>) -> Result { let index = if parser.peek2::() { Some(parser.parens(|parser| { parser.parse::()?; - Ok((parser.cur_span(), parser.parse()?)) + Ok(parser.parse()?) })?) } else { None }; - let (index_span, index) = match index { - Some((a, b)) => (Some(a), Some(b)), - None => (None, None), - }; - let mut func_ty = FunctionType { - params: Vec::new(), - results: Vec::new(), - }; - if parser.peek2::() || parser.peek2::() { - func_ty.finish_parse(allow_names, parser)?; - } + let inline = parser.parse()?; - Ok(TypeUse { - index, - index_span, - func_ty, - }) + Ok(TypeUse { index, inline }) } } -impl<'a> Parse<'a> for TypeUse<'a> { - fn parse(parser: Parser<'a>) -> Result { - TypeUse::parse_allow_names(parser, true) +impl<'a> From>> for TypeUse<'a, FunctionType<'a>> { + fn from(src: TypeUse<'a, FunctionTypeNoNames<'a>>) -> TypeUse<'a, FunctionType<'a>> { + TypeUse { + index: src.index, + inline: src.inline.map(|x| x.into()), + } } } diff --git a/crates/wast/src/binary.rs b/crates/wast/src/binary.rs index 4d4c5b8b3a..a8b83a05dd 100644 --- a/crates/wast/src/binary.rs +++ b/crates/wast/src/binary.rs @@ -1,16 +1,20 @@ use crate::ast::*; pub fn encode(module: &Module<'_>) -> Vec { + match &module.kind { + ModuleKind::Text(fields) => encode_fields(&module.id, &module.name, fields), + ModuleKind::Binary(bytes) => bytes.iter().flat_map(|b| b.iter().cloned()).collect(), + } +} + +fn encode_fields( + module_id: &Option>, + module_name: &Option>, + fields: &[ModuleField<'_>], +) -> Vec { use crate::ast::CustomPlace::*; use crate::ast::CustomPlaceAnchor::*; - let fields = match &module.kind { - ModuleKind::Text(fields) => fields, - ModuleKind::Binary(bytes) => { - return bytes.iter().flat_map(|b| b.iter().cloned()).collect(); - } - }; - let mut types = Vec::new(); let mut imports = Vec::new(); let mut funcs = Vec::new(); @@ -23,6 +27,9 @@ pub fn encode(module: &Module<'_>) -> Vec { let mut data = Vec::new(); let mut events = Vec::new(); let mut customs = Vec::new(); + let mut instances = Vec::new(); + let mut modules = Vec::new(); + let mut aliases = Vec::new(); for field in fields { match field { ModuleField::Type(i) => types.push(i), @@ -32,11 +39,15 @@ pub fn encode(module: &Module<'_>) -> Vec { ModuleField::Memory(i) => memories.push(i), ModuleField::Global(i) => globals.push(i), ModuleField::Export(i) => exports.push(i), + ModuleField::ExportAll(..) => panic!("should not be present for encoding"), ModuleField::Start(i) => start.push(i), ModuleField::Elem(i) => elem.push(i), ModuleField::Data(i) => data.push(i), ModuleField::Event(i) => events.push(i), ModuleField::Custom(i) => customs.push(i), + ModuleField::Instance(i) => instances.push(i), + ModuleField::NestedModule(i) => modules.push(i), + ModuleField::Alias(a) => aliases.push(a), } } @@ -49,8 +60,62 @@ pub fn encode(module: &Module<'_>) -> Vec { e.wasm.extend(b"\x01\0\0\0"); e.custom_sections(BeforeFirst); - e.section_list(1, Type, &types); - e.section_list(2, Import, &imports); + + // let moduletys = modules + // .iter() + // .map(|m| match &m.kind { + // NestedModuleKind::Inline { ty, .. } => ty.as_ref().expect("type should be filled in"), + // _ => panic!("only inline modules should be present now"), + // }) + // .collect::>(); + // e.section_list(100, Module, &moduletys); + + let mut items = fields + .iter() + .filter(|i| match i { + ModuleField::Alias(_) + | ModuleField::Type(_) + | ModuleField::Import(_) + | ModuleField::NestedModule(_) + | ModuleField::Instance(_) => true, + _ => false, + }) + .peekable(); + + // A special path is used for now to handle non-module-linking modules to + // work around WebAssembly/annotations#11 + if aliases.len() == 0 && modules.len() == 0 && instances.len() == 0 { + e.section_list(1, Type, &types); + e.section_list(2, Import, &imports); + } else { + while let Some(field) = items.next() { + macro_rules! list { + ($code:expr, $name:ident) => { + list!($code, $name, $name, |f| f) + }; + ($code:expr, $field:ident, $custom:ident, |$f:ident| $e:expr) => { + if let ModuleField::$field($f) = field { + let mut list = vec![$e]; + while let Some(ModuleField::$field($f)) = items.peek() { + list.push($e); + items.next(); + } + e.section_list($code, $custom, &list); + } + }; + } + list!(1, Type); + list!(2, Import); + list!(100, NestedModule, Module, |m| match &m.kind { + NestedModuleKind::Inline { ty, .. } => + ty.as_ref().expect("type should be filled in"), + _ => panic!("only inline modules should be present now"), + }); + list!(101, Instance); + list!(102, Alias); + } + } + let functys = funcs.iter().map(|f| &f.ty).collect::>(); e.section_list(3, Func, &functys); e.section_list(4, Table, &tables); @@ -67,10 +132,11 @@ pub fn encode(module: &Module<'_>) -> Vec { if contains_bulk_memory(&funcs) { e.section(12, &data.len()); } + e.section_list(103, ModuleCode, &modules); e.section_list(10, Code, &funcs); e.section_list(11, Data, &data); - let names = find_names(module, fields); + let names = find_names(module_id, module_name, fields); if !names.is_empty() { e.section(0, &("name", names)); } @@ -208,6 +274,27 @@ impl Encode for ArrayType<'_> { } } +impl Encode for ModuleType<'_> { + fn encode(&self, e: &mut Vec) { + self.imports.encode(e); + self.exports.encode(e); + assert!(self.instance_exports.is_empty()); + } +} + +impl Encode for InstanceType<'_> { + fn encode(&self, e: &mut Vec) { + self.exports.encode(e); + } +} + +impl Encode for ExportType<'_> { + fn encode(&self, e: &mut Vec) { + self.name.encode(e); + self.item.encode(e); + } +} + impl Encode for Type<'_> { fn encode(&self, e: &mut Vec) { match &self.def { @@ -223,6 +310,14 @@ impl Encode for Type<'_> { e.push(0x5e); array.encode(e) } + TypeDef::Module(module) => { + e.push(0x61); + module.encode(e) + } + TypeDef::Instance(instance) => { + e.push(0x62); + instance.encode(e) + } } } } @@ -284,33 +379,53 @@ impl<'a> Encode for RefType<'a> { impl Encode for Import<'_> { fn encode(&self, e: &mut Vec) { self.module.encode(e); - self.field.encode(e); + match self.field { + Some(s) => s.encode(e), + None => { + e.push(0x01); + e.push(0xc0); + } + } + self.item.encode(e); + } +} + +impl Encode for ItemSig<'_> { + fn encode(&self, e: &mut Vec) { match &self.kind { - ImportKind::Func(f) => { + ItemKind::Func(f) => { e.push(0x00); f.encode(e); } - ImportKind::Table(f) => { + ItemKind::Table(f) => { e.push(0x01); f.encode(e); } - ImportKind::Memory(f) => { + ItemKind::Memory(f) => { e.push(0x02); f.encode(e); } - ImportKind::Global(f) => { + ItemKind::Global(f) => { e.push(0x03); f.encode(e); } - ImportKind::Event(f) => { + ItemKind::Event(f) => { e.push(0x04); f.encode(e); } + ItemKind::Module(m) => { + e.push(0x05); + m.encode(e); + } + ItemKind::Instance(i) => { + e.push(0x06); + i.encode(e); + } } } } -impl Encode for TypeUse<'_> { +impl Encode for TypeUse<'_, T> { fn encode(&self, e: &mut Vec) { self.index .as_ref() @@ -322,8 +437,8 @@ impl Encode for TypeUse<'_> { impl Encode for Index<'_> { fn encode(&self, e: &mut Vec) { match self { - Index::Num(n) => n.encode(e), - Index::Id(n) => panic!("unresolved index in emission: {}", n.name()), + Index::Num(n, _) => n.encode(e), + Index::Id(n) => panic!("unresolved index in emission: {:?}", n), } } } @@ -419,7 +534,13 @@ impl Encode for Global<'_> { impl Encode for Export<'_> { fn encode(&self, e: &mut Vec) { self.name.encode(e); - match &self.kind { + self.kind.encode(e); + } +} + +impl Encode for ExportKind<'_> { + fn encode(&self, e: &mut Vec) { + match self { ExportKind::Func(f) => { e.push(0x00); f.encode(e); @@ -440,6 +561,18 @@ impl Encode for Export<'_> { e.push(0x04); f.encode(e); } + ExportKind::Module(f) => { + e.push(0x05); + f.encode(e); + } + ExportKind::Instance(f) => { + e.push(0x06); + f.encode(e); + } + ExportKind::Type(f) => { + e.push(0x07); + f.encode(e); + } } } } @@ -462,7 +595,7 @@ impl Encode for Elem<'_> { match (&self.kind, &to_encode) { ( ElemKind::Active { - table: Index::Num(0), + table: Index::Num(0, _), offset, }, ElemPayload::Indices(_), @@ -482,7 +615,7 @@ impl Encode for Elem<'_> { } ( ElemKind::Active { - table: Index::Num(0), + table: Index::Num(0, _), offset, }, ElemPayload::Exprs { @@ -548,7 +681,7 @@ impl Encode for Data<'_> { match &self.kind { DataKind::Passive => e.push(0x01), DataKind::Active { memory, offset } => { - if *memory == Index::Num(0) { + if let Index::Num(0, _) = memory { e.push(0x00); } else { e.push(0x02); @@ -603,14 +736,19 @@ impl Encode for Expression<'_> { impl Encode for BlockType<'_> { fn encode(&self, e: &mut Vec) { // block types using an index are encoded as an sleb, not a uleb - if let Some(Index::Num(n)) = &self.ty.index { + if let Some(Index::Num(n, _)) = &self.ty.index { return i64::from(*n).encode(e); } - if self.ty.func_ty.params.is_empty() && self.ty.func_ty.results.is_empty() { + let ty = self + .ty + .inline + .as_ref() + .expect("function type not filled in"); + if ty.params.is_empty() && ty.results.is_empty() { return e.push(0x40); } - if self.ty.func_ty.params.is_empty() && self.ty.func_ty.results.len() == 1 { - return self.ty.func_ty.results[0].encode(e); + if ty.params.is_empty() && ty.results.len() == 1 { + return ty.results[0].encode(e); } panic!("multi-value block types should have an index"); } @@ -688,9 +826,19 @@ struct Names<'a> { locals: Vec<(u32, Vec<(u32, &'a str)>)>, } -fn find_names<'a>(module: &Module<'a>, fields: &[ModuleField<'a>]) -> Names<'a> { +fn find_names<'a>( + module_id: &Option>, + module_name: &Option>, + fields: &[ModuleField<'a>], +) -> Names<'a> { fn get_name<'a>(id: &Option>, name: &Option>) -> Option<&'a str> { - name.as_ref().map(|n| n.name).or(id.map(|id| id.name())) + name.as_ref().map(|n| n.name).or(id.and_then(|id| { + if id.is_gensym() { + None + } else { + Some(id.name()) + } + })) } let mut funcs = Vec::new(); @@ -699,12 +847,12 @@ fn find_names<'a>(module: &Module<'a>, fields: &[ModuleField<'a>]) -> Names<'a> for field in fields { match field { ModuleField::Import(i) => { - match i.kind { - ImportKind::Func(_) => {} + match i.item.kind { + ItemKind::Func(_) => {} _ => continue, } - if let Some(name) = get_name(&i.id, &i.name) { + if let Some(name) = get_name(&i.item.id, &i.item.name) { funcs.push((idx, name)); } @@ -716,11 +864,18 @@ fn find_names<'a>(module: &Module<'a>, fields: &[ModuleField<'a>]) -> Names<'a> } let mut local_names = Vec::new(); let mut local_idx = 0; - for (id, name, _) in f.ty.func_ty.params.iter() { - if let Some(name) = get_name(id, name) { - local_names.push((local_idx, name)); + + // Consult the inline type listed for local names of parameters. + // This is specifically preserved during the name resolution + // pass, but only for functions, so here we can look at the + // original source's names. + if let Some(ty) = &f.ty.inline { + for (id, name, _) in ty.params.iter() { + if let Some(name) = get_name(id, name) { + local_names.push((local_idx, name)); + } + local_idx += 1; } - local_idx += 1; } if let FuncKind::Inline { locals, .. } = &f.kind { for (id, name, _) in locals { @@ -735,12 +890,23 @@ fn find_names<'a>(module: &Module<'a>, fields: &[ModuleField<'a>]) -> Names<'a> } idx += 1; } + ModuleField::Alias(Alias { + id, + name, + kind: ExportKind::Func(_), + .. + }) => { + if let Some(name) = get_name(id, name) { + funcs.push((idx, name)); + } + idx += 1; + } _ => {} } } Names { - module: get_name(&module.id, &module.name), + module: get_name(module_id, module_name), funcs, locals, } @@ -779,6 +945,7 @@ impl Encode for Names<'_> { impl Encode for Id<'_> { fn encode(&self, dst: &mut Vec) { + assert!(!self.is_gensym()); self.name().encode(dst); } } @@ -852,3 +1019,40 @@ impl Encode for StructNarrow<'_> { self.to.encode(e); } } + +impl Encode for NestedModule<'_> { + fn encode(&self, e: &mut Vec) { + let fields = match &self.kind { + NestedModuleKind::Inline { fields, .. } => fields, + _ => panic!("should only have inline modules in emission"), + }; + + encode_fields(&self.id, &self.name, fields).encode(e); + } +} + +impl Encode for Instance<'_> { + fn encode(&self, e: &mut Vec) { + assert!(self.exports.names.is_empty()); + let (module, items) = match &self.kind { + InstanceKind::Inline { module, items } => (module, items), + _ => panic!("should only have inline instances in emission"), + }; + + module.encode(e); + items.encode(e); + } +} + +impl Encode for Alias<'_> { + fn encode(&self, e: &mut Vec) { + match self.instance { + Some(instance) => { + e.push(0x00); + instance.encode(e); + } + None => e.push(0x01), + } + self.kind.encode(e); + } +} diff --git a/crates/wast/src/parser.rs b/crates/wast/src/parser.rs index 0af1783744..07cc465e19 100644 --- a/crates/wast/src/parser.rs +++ b/crates/wast/src/parser.rs @@ -709,6 +709,11 @@ impl<'a> Parser<'a> { self.cursor().cur_span() } + /// Returns the span of the previous token + pub fn prev_span(&self) -> Span { + self.cursor().prev_span().unwrap_or(Span::from_offset(0)) + } + /// Registers a new known annotation with this parser to allow parsing /// annotations with this name. /// @@ -873,6 +878,16 @@ impl<'a> Cursor<'a> { Span { offset } } + /// Returns the span of the previous `Token` token. + /// + /// Does not take into account whitespace or comments. + pub(crate) fn prev_span(&self) -> Option { + let (token, _) = self.parser.buf.tokens.get(self.cur.checked_sub(1)?)?; + Some(Span { + offset: self.parser.buf.input_pos(token.src()), + }) + } + /// Same as [`Parser::error`], but works with the current token in this /// [`Cursor`] instead. pub fn error(&self, msg: impl fmt::Display) -> Error { diff --git a/crates/wast/src/resolve/deinline_import_export.rs b/crates/wast/src/resolve/deinline_import_export.rs new file mode 100644 index 0000000000..1089fcdf42 --- /dev/null +++ b/crates/wast/src/resolve/deinline_import_export.rs @@ -0,0 +1,274 @@ +use crate::ast::*; +use crate::resolve::gensym; +use std::mem; + +pub fn run(fields: &mut Vec) { + let mut cur = 0; + let mut to_append = Vec::new(); + while cur < fields.len() { + let item = &mut fields[cur]; + match item { + ModuleField::Func(f) => { + for name in f.exports.names.drain(..) { + let id = gensym::fill(f.span, &mut f.id); + to_append.push(ModuleField::Export(Export { + span: f.span, + name, + kind: ExportKind::Func(Index::Id(id)), + })); + } + match f.kind { + FuncKind::Import(import) => { + *item = ModuleField::Import(Import { + span: f.span, + module: import.module, + field: import.field, + item: ItemSig { + span: f.span, + id: f.id, + name: f.name, + kind: ItemKind::Func(f.ty.clone()), + }, + }); + } + FuncKind::Inline { .. } => {} + } + } + + ModuleField::Memory(m) => { + for name in m.exports.names.drain(..) { + let id = gensym::fill(m.span, &mut m.id); + to_append.push(ModuleField::Export(Export { + span: m.span, + name, + kind: ExportKind::Memory(Index::Id(id)), + })); + } + match m.kind { + MemoryKind::Import { import, ty } => { + *item = ModuleField::Import(Import { + span: m.span, + module: import.module, + field: import.field, + item: ItemSig { + span: m.span, + id: m.id, + name: None, + kind: ItemKind::Memory(ty), + }, + }); + } + // If data is defined inline insert an explicit `data` module + // field here instead, switching this to a `Normal` memory. + MemoryKind::Inline(ref data) => { + let len = data.iter().map(|l| l.len()).sum::() as u32; + let pages = (len + page_size() - 1) / page_size(); + let kind = MemoryKind::Normal(MemoryType { + limits: Limits { + min: pages, + max: Some(pages), + }, + shared: false, + }); + let data = match mem::replace(&mut m.kind, kind) { + MemoryKind::Inline(data) => data, + _ => unreachable!(), + }; + let id = gensym::fill(m.span, &mut m.id); + to_append.push(ModuleField::Data(Data { + span: m.span, + id: None, + kind: DataKind::Active { + memory: Index::Id(id), + offset: Expression { + instrs: vec![Instruction::I32Const(0)], + }, + }, + data, + })); + } + + MemoryKind::Normal(_) => {} + } + } + + ModuleField::Table(t) => { + for name in t.exports.names.drain(..) { + let id = gensym::fill(t.span, &mut t.id); + to_append.push(ModuleField::Export(Export { + span: t.span, + name, + kind: ExportKind::Table(Index::Id(id)), + })); + } + match &mut t.kind { + TableKind::Import { import, ty } => { + *item = ModuleField::Import(Import { + span: t.span, + module: import.module, + field: import.field, + item: ItemSig { + span: t.span, + id: t.id, + name: None, + kind: ItemKind::Table(*ty), + }, + }); + } + // If data is defined inline insert an explicit `data` module + // field here instead, switching this to a `Normal` memory. + TableKind::Inline { payload, elem } => { + let len = match payload { + ElemPayload::Indices(v) => v.len(), + ElemPayload::Exprs { exprs, .. } => exprs.len(), + }; + let kind = TableKind::Normal(TableType { + limits: Limits { + min: len as u32, + max: Some(len as u32), + }, + elem: *elem, + }); + let payload = match mem::replace(&mut t.kind, kind) { + TableKind::Inline { payload, .. } => payload, + _ => unreachable!(), + }; + let id = gensym::fill(t.span, &mut t.id); + to_append.push(ModuleField::Elem(Elem { + span: t.span, + id: None, + kind: ElemKind::Active { + table: Index::Id(id), + offset: Expression { + instrs: vec![Instruction::I32Const(0)], + }, + }, + payload, + })); + } + + TableKind::Normal(_) => {} + } + } + + ModuleField::Global(g) => { + for name in g.exports.names.drain(..) { + let id = gensym::fill(g.span, &mut g.id); + to_append.push(ModuleField::Export(Export { + span: g.span, + name, + kind: ExportKind::Global(Index::Id(id)), + })); + } + match g.kind { + GlobalKind::Import(import) => { + *item = ModuleField::Import(Import { + span: g.span, + module: import.module, + field: import.field, + item: ItemSig { + span: g.span, + id: g.id, + name: None, + kind: ItemKind::Global(g.ty), + }, + }); + } + GlobalKind::Inline { .. } => {} + } + } + + ModuleField::Event(e) => { + for name in e.exports.names.drain(..) { + let id = gensym::fill(e.span, &mut e.id); + to_append.push(ModuleField::Export(Export { + span: e.span, + name, + kind: ExportKind::Event(Index::Id(id)), + })); + } + } + + ModuleField::Instance(i) => { + for name in i.exports.names.drain(..) { + let id = gensym::fill(i.span, &mut i.id); + to_append.push(ModuleField::Export(Export { + span: i.span, + name, + kind: ExportKind::Instance(Index::Id(id)), + })); + } + match &mut i.kind { + InstanceKind::Import { import, ty } => { + *item = ModuleField::Import(Import { + span: i.span, + module: import.module, + field: import.field, + item: ItemSig { + span: i.span, + id: i.id, + name: None, + kind: ItemKind::Instance(mem::replace( + ty, + TypeUse::new_with_index(Index::Num(0, Span::from_offset(0))), + )), + }, + }); + } + InstanceKind::Inline { .. } => {} + } + } + + ModuleField::NestedModule(m) => { + for name in m.exports.names.drain(..) { + let id = gensym::fill(m.span, &mut m.id); + to_append.push(ModuleField::Export(Export { + span: m.span, + name, + kind: ExportKind::Module(Index::Id(id)), + })); + } + match &mut m.kind { + NestedModuleKind::Import { import, ty } => { + *item = ModuleField::Import(Import { + span: m.span, + module: import.module, + field: import.field, + item: ItemSig { + span: m.span, + id: m.id, + name: m.name, + kind: ItemKind::Module(mem::replace( + ty, + TypeUse::new_with_index(Index::Num(0, Span::from_offset(0))), + )), + }, + }); + } + NestedModuleKind::Inline { fields, .. } => { + run(fields); + } + }; + } + + ModuleField::ExportAll(..) + | ModuleField::Import(_) + | ModuleField::Type(_) + | ModuleField::Export(_) + | ModuleField::Alias(_) + | ModuleField::Start(_) + | ModuleField::Elem(_) + | ModuleField::Data(_) + | ModuleField::Custom(_) => {} + } + + fields.splice(cur..cur, to_append.drain(..)); + cur += 1; + } + + assert!(to_append.is_empty()); + + fn page_size() -> u32 { + 1 << 16 + } +} diff --git a/crates/wast/src/resolve/expand.rs b/crates/wast/src/resolve/expand.rs index 9eb81691e8..7709c4a47f 100644 --- a/crates/wast/src/resolve/expand.rs +++ b/crates/wast/src/resolve/expand.rs @@ -1,18 +1,74 @@ use crate::ast::*; -use std::mem; +use crate::resolve::gensym; +use crate::resolve::Ns; +use crate::Error; +use std::collections::{HashMap, HashSet}; + +/// Runs an expansion process on the fields provided to elaborate and expand +/// features from the module-linking proposal. Namely this handles> +/// +/// * Injection of child `alias` directives to fill in `$a.$b` identifiers +/// * Injection of parent `alias` directives for names that are likely to be +/// resolved from the parent. +/// * Expansion of export-all annotations in modules definitions and types. +/// +/// Note that this pass runs before name resolution so no indices are worked +/// with as part of this pass. Instead everything internally works off +/// identifiers and new items have generated identifiers assigned to them. +pub fn run(fields: &mut Vec) -> Result<(), Error> { + Expander::default().process(fields, None) +} #[derive(Default)] -pub struct Expander<'a> { +struct Expander<'a> { to_append: Vec>, - funcs: u32, - memories: u32, - tables: u32, - globals: u32, - events: u32, + + /// The set of all names reference in a module. Each missing name has an + /// associated namespace with it as well for which kind of item it's + /// referring to. + /// + /// This map is built as part of `record_missing_name`. + all_missing: HashSet<(Id<'a>, Ns)>, + + /// This is the set of all names defined in this module. Each name is + /// defined in a particular namespace as well. + /// + /// This map is built as part of `record_missing_name`. + defined_names: HashSet<(Id<'a>, Ns)>, + + /// This is the set of all missing names which might be filled in as + /// `$a.$b` names automatically. The first level of this hash map is the + /// instance name it would come from (e.g. "a") and the second level contains + /// the item name and namespace that would be used (e.g. "b" and Ns::Func). + /// + /// The final value in the map is the full `$a.$b` identifier used to + /// synthesize as the name of `alias` annotations. + /// + /// This map is built as part of `record_missing_name`. + missing_names: HashMap<&'a str, HashMap<(&'a str, Ns), Id<'a>>>, + + /// This map contains information about the exports of a module, used to + /// fill in export-all annotations. This is only recorded for inline modules + /// which have a name associated with them. + module_exports: HashMap, ExportInfo<'a>>, + + /// This map contains info about the exports of locally defined instances. + /// Each locally defined instance, with a name, is recorded here about how + /// we can learn about its export information. + instance_exports: HashMap, InstanceExports<'a>>, + + /// This map is similar to the above, but for instance types rather than + /// instances themselves. + instance_type_exports: HashMap, Vec>>, } -fn page_size() -> u32 { - 1 << 16 +enum InstanceExports<'a> { + /// This instance was an import and we have its export information available + /// inline from the definiton. + Inline(ExportInfo<'a>), + /// This instance is the instantiation of the module listed here. The + /// `module_exports` map will need to be consulted for export information. + Module(Id<'a>), } impl<'a> Expander<'a> { @@ -20,255 +76,634 @@ impl<'a> Expander<'a> { /// /// This method will handle the `to_append` field of this `Expander`, /// appending items after processing a `ModuleField` as necessary. - pub fn process( + fn process( + &mut self, + fields: &mut Vec>, + parent: Option<&Expander<'a>>, + ) -> Result<(), Error> { + // The first step here is to record information about defined/missing + // names in this module. At the same time we also scrape information + // about defined modules to learn about the exports of inline modules. + for field in fields.iter_mut() { + self.record_missing_name(field); + self.discover_module_names(field); + } + + // Next up we can start injecting alias annotations from our parent. Any + // name defined in our parent but not defined locally is inherited with + // an `Alias` annotation pointing to the parent. + if let Some(parent) = parent { + let mut names_to_add = self + .all_missing + .iter() + // skip anything defined in our own module ... + .filter(|key| !self.defined_names.contains(key)) + // ... and also skip anything that's not defined in the parent + // module + .filter(|key| parent.defined_names.contains(key)) + .collect::>(); + + // Ensure we have deterministic output by inserting aliases in a + // known order. + names_to_add.sort_by_key(|e| e.0.name()); + + for key in names_to_add { + // Inject an `(alias (parent ...))` annotation now that we know + // we're referencing an item from the parent module. + // + // For now we insert this at the front since these items may be + // referred to by later items (like types and modules), and + // inserting them at the front ensure that they're defined early + // in the index space. + fields.insert( + 0, + ModuleField::Alias(Alias { + id: Some(key.0), + instance: None, + name: None, + span: key.0.span(), + kind: key.1.to_export_kind(Index::Id(key.0)), + }), + ); + + // Now that we've defined a new name in our module we want to + // copy over all the metadata learned about that item into our + // module as well, so when the name is used in our module we + // still retain all metadata about it. + self.defined_names.insert(*key); + match key.1 { + Ns::Module => { + if let Some(info) = parent.module_exports.get(&key.0) { + self.module_exports.insert(key.0, info.clone()); + } + } + Ns::Type => { + if let Some(list) = parent.instance_type_exports.get(&key.0) { + self.instance_type_exports.insert(key.0, list.clone()); + } + } + // When copying over information about instances we just + // interpret everything as information listed inline to + // avoid the go-through-the-module-map indirection. + Ns::Instance => { + let info = match parent.instance_exports.get(&key.0) { + Some(InstanceExports::Inline(info)) => info, + Some(InstanceExports::Module(module_id)) => { + &parent.module_exports[module_id] + } + None => continue, + }; + self.instance_exports + .insert(key.0, InstanceExports::Inline(info.clone())); + } + _ => {} + } + } + } + + // After we've dealt with inherited identifiers from our parent we can + // start injecting aliases after local `instance` definitions based on + // referenced identifiers elsewhere in this module. + self.iter_and_expand(fields, |me, field| { + me.inject_aliases(field); + Ok(true) + })?; + assert!(self.to_append.is_empty()); + + // And last for our own module we can expand all the export-all + // annotations since we have all information about nested + // modules/instances at this point. + self.iter_and_expand(fields, |me, field| me.expand_export_all(field))?; + assert!(self.to_append.is_empty()); + + // And as a final processing step we recurse into our nested modules and + // process all of them. + for field in fields.iter_mut() { + if let ModuleField::NestedModule(m) = field { + if let NestedModuleKind::Inline { fields, .. } = &mut m.kind { + Expander::default().process(fields, Some(self))?; + } + } + } + Ok(()) + } + + fn discover_module_names(&mut self, field: &ModuleField<'a>) { + let (id, info) = match field { + ModuleField::Import(Import { + item: + ItemSig { + id: Some(id), + kind: + ItemKind::Module(TypeUse { + inline: Some(ty), .. + }), + .. + }, + .. + }) => (id, ExportInfo::from_exports(&ty.exports)), + + ModuleField::NestedModule(NestedModule { + id: Some(id), + kind: NestedModuleKind::Inline { fields, .. }, + .. + }) => { + // This is a bit of a cop-out at this point, but we don't really + // have full name resolution info available at this point. As a + // result we just consider `(export "" (func $foo))` as a + // candidate that `$foo` is available from this module for + // referencing. If you do, however, `(export "" (func 0))` and + // the 0th function is `$foo`, we won't realize that. + let mut info = ExportInfo::default(); + for field in fields { + let export = match field { + ModuleField::Export(e) => e, + _ => continue, + }; + let (idx, ns) = Ns::from_export(&export.kind); + if let Index::Id(id) = idx { + info.wat_name_to_export_idx + .insert((id.name(), ns), info.export_names.len() as u32); + } + info.export_names.push((export.name, ns)); + } + (id, info) + } + + _ => return, + }; + self.module_exports.insert(*id, info); + } + + /// Processes each element of `fields` individually and then inserts all + /// items in `self.to_append` after that field. + fn iter_and_expand( &mut self, fields: &mut Vec>, - mut f: impl FnMut(&mut Self, &mut ModuleField<'a>), - ) { + f: impl Fn(&mut Self, &mut ModuleField<'a>) -> Result, + ) -> Result<(), Error> { + assert!(self.to_append.is_empty()); let mut cur = 0; while cur < fields.len() { - f(self, &mut fields[cur]); + let keep = f(self, &mut fields[cur])?; + if keep { + cur += 1; + } else { + fields.remove(cur); + } for new in self.to_append.drain(..) { fields.insert(cur, new); cur += 1; } - cur += 1; } + Ok(()) } - /// Inverts inline `import` descriptions into actual `import` statements. - /// - /// In doing so this also takes care of inline `export` statements, if any, - /// since we'll be removing the corresponding `ModuleField` item and - /// replacing it in-place. - /// - /// To work right, `Import` field items must be visited first. - /// - /// This will replace `item` in-place with an `Import` directive if an - /// inline import is found, and after this pass has been run no more inline - /// import directives should be present. - pub fn deinline_import(&mut self, item: &mut ModuleField<'a>) { - match item { + fn record_missing_name(&mut self, field: &ModuleField<'a>) { + match field { + ModuleField::Type(t) => { + self.record_defined(&t.id, Ns::Type); + match &t.def { + TypeDef::Module(m) => m.record_missing(self), + TypeDef::Instance(i) => { + i.record_missing(self); + if let Some(name) = t.id { + self.instance_type_exports.insert(name, i.exports.clone()); + } + } + TypeDef::Func(f) => f.record_missing(self), + TypeDef::Array(_) | TypeDef::Struct(_) => {} + } + } + + ModuleField::Import(i) => { + self.record_defined(&i.item.id, Ns::from_item(&i.item)); + self.record_item_sig(&i.item); + } + ModuleField::Func(f) => { - let (module, field) = match f.kind { - FuncKind::Import { module, field } => (module, field), - _ => return, - }; - for name in f.exports.names.drain(..) { - self.to_append.push(ModuleField::Export(Export { - name, - kind: ExportKind::Func(Index::Num(self.funcs)), - })); + self.record_defined(&f.id, Ns::Func); + self.record_type_use(&f.ty); + if let FuncKind::Inline { expression, .. } = &f.kind { + self.record_expr(expression); } - *item = ModuleField::Import(Import { - span: f.span, - module, - field, - id: f.id, - name: f.name, - kind: ImportKind::Func(f.ty.clone()), - }); - self.funcs += 1; } ModuleField::Memory(m) => { - let (module, field, ty) = match m.kind { - MemoryKind::Import { module, field, ty } => (module, field, ty), - _ => return, - }; - for name in m.exports.names.drain(..) { - self.to_append.push(ModuleField::Export(Export { - name, - kind: ExportKind::Memory(Index::Num(self.memories)), - })); - } - *item = ModuleField::Import(Import { - span: m.span, - module, - field, - id: m.id, - name: None, - kind: ImportKind::Memory(ty), - }); - self.memories += 1; + self.record_defined(&m.id, Ns::Memory); } ModuleField::Table(t) => { - let (module, field, ty) = match t.kind { - TableKind::Import { module, field, ty } => (module, field, ty), - _ => return, - }; - for name in t.exports.names.drain(..) { - self.to_append.push(ModuleField::Export(Export { - name, - kind: ExportKind::Table(Index::Num(self.tables)), - })); - } - *item = ModuleField::Import(Import { - span: t.span, - module, - field, - id: t.id, - name: None, - kind: ImportKind::Table(ty), - }); - self.tables += 1; + self.record_defined(&t.id, Ns::Table); } ModuleField::Global(g) => { - let (module, field) = match g.kind { - GlobalKind::Import { module, field } => (module, field), - _ => return, - }; - for name in g.exports.names.drain(..) { - self.to_append.push(ModuleField::Export(Export { - name, - kind: ExportKind::Global(Index::Num(self.globals)), - })); + self.record_defined(&g.id, Ns::Global); + if let GlobalKind::Inline(e) = &g.kind { + self.record_expr(e); } - *item = ModuleField::Import(Import { - span: g.span, - module, - field, - id: g.id, - name: None, - kind: ImportKind::Global(g.ty), - }); - self.globals += 1; } - ModuleField::Import(i) => match i.kind { - ImportKind::Func(_) => self.funcs += 1, - ImportKind::Memory(_) => self.memories += 1, - ImportKind::Table(_) => self.tables += 1, - ImportKind::Global(_) => self.globals += 1, - ImportKind::Event(_) => self.events += 1, - }, - - _ => {} - } - } + ModuleField::Event(e) => { + self.record_defined(&e.id, Ns::Event); + match &e.ty { + EventType::Exception(ty) => self.record_type_use(ty), + } + } - /// Extracts all inline `export` annotations and creates - /// `ModuleField::Export` items. - /// - /// Note that this executes after the `deinline_import` pass to ensure - /// indices all line up right. - pub fn deinline_export(&mut self, item: &mut ModuleField<'a>) { - match item { - ModuleField::Func(f) => { - for name in f.exports.names.drain(..) { - self.to_append.push(ModuleField::Export(Export { - name, - kind: ExportKind::Func(Index::Num(self.funcs)), - })); + ModuleField::Instance(i) => { + self.record_defined(&i.id, Ns::Instance); + if let InstanceKind::Inline { items, module, .. } = &i.kind { + self.record_missing(module, Ns::Module); + for item in items { + let (idx, ns) = Ns::from_export(item); + self.record_missing(&idx, ns); + } } - self.funcs += 1; } - ModuleField::Memory(m) => { - for name in m.exports.names.drain(..) { - self.to_append.push(ModuleField::Export(Export { - name, - kind: ExportKind::Memory(Index::Num(self.memories)), - })); + ModuleField::Start(id) => self.record_missing(id, Ns::Func), + + ModuleField::Export(e) => { + let (idx, ns) = Ns::from_export(&e.kind); + self.record_missing(&idx, ns); + } + + ModuleField::Elem(e) => { + match &e.kind { + ElemKind::Active { table, offset } => { + self.record_missing(table, Ns::Table); + self.record_expr(offset); + } + ElemKind::Passive { .. } | ElemKind::Declared { .. } => {} + } + match &e.payload { + ElemPayload::Indices(elems) => { + for idx in elems { + self.record_missing(idx, Ns::Func); + } + } + ElemPayload::Exprs { exprs, .. } => { + for funcref in exprs { + if let Some(idx) = funcref { + self.record_missing(idx, Ns::Func); + } + } + } } + } - // If data is defined inline insert an explicit `data` module - // field here instead, switching this to a `Normal` memory. - if let MemoryKind::Inline(data) = &mut m.kind { - let len = data.iter().map(|l| l.len()).sum::() as u32; - let pages = (len + page_size() - 1) / page_size(); - let kind = MemoryKind::Normal(MemoryType { - limits: Limits { - min: pages, - max: Some(pages), - }, - shared: false, - }); - let data = match mem::replace(&mut m.kind, kind) { - MemoryKind::Inline(data) => data, - _ => unreachable!(), - }; - self.to_append.push(ModuleField::Data(Data { - span: m.span, - id: None, - kind: DataKind::Active { - memory: Index::Num(self.memories), - offset: Expression { - instrs: vec![Instruction::I32Const(0)], - }, - }, - data, - })); + ModuleField::Data(d) => { + if let DataKind::Active { memory, offset } = &d.kind { + self.record_missing(memory, Ns::Memory); + self.record_expr(offset); } + } - self.memories += 1; + ModuleField::Alias(a) => { + let (_idx, ns) = Ns::from_export(&a.kind); + self.record_defined(&a.id, ns); } - ModuleField::Table(t) => { - for name in t.exports.names.drain(..) { - self.to_append.push(ModuleField::Export(Export { - name, - kind: ExportKind::Table(Index::Num(self.tables)), - })); - } + ModuleField::NestedModule(m) => { + self.record_defined(&m.id, Ns::Module); + } - // If data is defined inline insert an explicit `data` module - // field here instead, switching this to a `Normal` memory. - if let TableKind::Inline { payload, elem } = &mut t.kind { - let len = match payload { - ElemPayload::Indices(v) => v.len(), - ElemPayload::Exprs { exprs, .. } => exprs.len(), - }; - let kind = TableKind::Normal(TableType { - limits: Limits { - min: len as u32, - max: Some(len as u32), - }, - elem: *elem, - }); - let payload = match mem::replace(&mut t.kind, kind) { - TableKind::Inline { payload, .. } => payload, - _ => unreachable!(), - }; - self.to_append.push(ModuleField::Elem(Elem { - span: t.span, - id: None, - kind: ElemKind::Active { - table: Index::Num(self.tables), - offset: Expression { - instrs: vec![Instruction::I32Const(0)], - }, - }, - payload, - })); + ModuleField::ExportAll(_span, instance) => { + self.record_missing(&Index::Id(*instance), Ns::Instance); + } + + ModuleField::Custom(_) => {} + } + } + fn record_type_use(&mut self, item: &TypeUse<'a, impl TypeReference<'a>>) { + if let Some(idx) = &item.index { + self.record_missing(idx, Ns::Type); + } + if let Some(inline) = &item.inline { + inline.record_missing(self); + } + } + + fn record_item_sig(&mut self, sig: &ItemSig<'a>) { + match &sig.kind { + ItemKind::Func(f) | ItemKind::Event(EventType::Exception(f)) => self.record_type_use(f), + ItemKind::Module(m) => self.record_type_use(m), + ItemKind::Instance(i) => self.record_type_use(i), + ItemKind::Table(_) | ItemKind::Memory(_) | ItemKind::Global(_) => {} + } + } + + fn record_expr(&mut self, expr: &Expression<'a>) { + use Instruction::*; + for instr in expr.instrs.iter() { + match instr { + Block(t) | Loop(t) | If(t) | Try(t) => { + self.record_type_use(&t.ty); + } + FuncBind(f) | Call(f) | ReturnCall(f) | RefFunc(f) => { + self.record_missing(f, Ns::Func) } + CallIndirect(f) | ReturnCallIndirect(f) => { + self.record_missing(&f.table, Ns::Table); + self.record_type_use(&f.ty); + } + GlobalGet(g) | GlobalSet(g) => self.record_missing(g, Ns::Global), + TableFill(g) | TableSize(g) | TableGrow(g) | TableGet(g) | TableSet(g) => { + self.record_missing(&g.dst, Ns::Table) + } + TableInit(t) => self.record_missing(&t.table, Ns::Table), + TableCopy(t) => { + self.record_missing(&t.src, Ns::Table); + self.record_missing(&t.dst, Ns::Table); + } + _ => {} + } + } + } - self.tables += 1; + fn record_defined(&mut self, name: &Option>, kind: Ns) { + if let Some(id) = name { + self.defined_names.insert((*id, kind)); + } + } + + fn record_missing(&mut self, name: &Index<'a>, kind: Ns) { + let id = match name { + Index::Num(..) => return, + Index::Id(id) => id, + }; + self.all_missing.insert((*id, kind)); + let (instance, field) = match self.split_name(id) { + Some(p) => p, + None => return, + }; + self.missing_names + .entry(instance) + .or_insert(HashMap::new()) + .insert((field, kind), *id); + } + + /// Splits an identifier into two halves if it looks like `$a.$b`. Returns + /// `("a", "b")` in that case. + fn split_name(&self, id: &Id<'a>) -> Option<(&'a str, &'a str)> { + let name = id.name(); + let i = name.find('.')?; + let (instance, field) = (&name[..i], &name[i + 1..]); + if field.starts_with("$") { + Some((instance, &field[1..])) + } else { + None + } + } + + fn inject_aliases(&mut self, field: &ModuleField<'a>) { + let defined_names = &self.defined_names; + let (id, span, export_info) = match field { + // For imported instances, only those with a name and an inline type + // listed are candidates for expansion with `$a.$b`. + ModuleField::Import(Import { + item: + ItemSig { + id: Some(id), + span, + kind: + ItemKind::Instance(TypeUse { + inline: Some(ty), .. + }), + .. + }, + .. + }) => { + let map = self.instance_exports.entry(*id).or_insert_with(|| { + InstanceExports::Inline(ExportInfo::from_exports(&ty.exports)) + }); + ( + id, + span, + match map { + InstanceExports::Inline(i) => &*i, + // If this instance name was already seen earlier we just + // skip expansion for now and let name resolution generate + // an error about it. + InstanceExports::Module(_) => return, + }, + ) } - ModuleField::Global(g) => { - for name in g.exports.names.drain(..) { + // Of locally defined instances, only those which have a name and + // instantiate a named module are candidates for expansion with + // `$a.$b`. This isn't the greatest of situations right now but it + // should be good enough. + ModuleField::Instance(Instance { + span, + id: Some(id), + kind: + InstanceKind::Inline { + module: Index::Id(module_id), + .. + }, + .. + }) => match self.module_exports.get(module_id) { + Some(map) => { + self.instance_exports + .insert(*id, InstanceExports::Module(*module_id)); + (id, span, map) + } + // If this named module doesn't exist let name resolution later + // on generate an error about that. + None => return, + }, + _ => return, + }; + + // Load the list of names missing for our instance's name. Note that + // there may be no names missing, so we can just skip that. + let missing = match self.missing_names.get(id.name()) { + Some(missing) => missing, + None => return, + }; + let mut to_add = missing + .iter() + // Skip names defined locally in our module already... + .filter(|((_, ns), full_name)| !defined_names.contains(&(**full_name, *ns))) + // ... and otherwise only work with names actually defined in the + // instance. + .filter_map(|(key, full_name)| { + export_info + .wat_name_to_export_idx + .get(key) + .map(move |idx| (&key.1, idx, full_name)) + }) + .collect::>(); + + // Sort the list of aliases to add to ensure that we have deterministic + // output. + to_add.sort_by_key(|e| e.1); + + for (ns, index, full_name) in to_add { + let export_index = Index::Num(*index, *span); + self.defined_names.insert((*full_name, *ns)); + self.to_append.push(ModuleField::Alias(Alias { + id: Some(*full_name), + instance: Some(Index::Id(*id)), + name: None, + span: *span, + kind: ns.to_export_kind(export_index), + })); + } + } + + fn expand_export_all(&mut self, field: &mut ModuleField<'a>) -> Result { + match field { + ModuleField::Type(t) => { + match &mut t.def { + TypeDef::Module(m) => m.expand_export_all(self)?, + TypeDef::Instance(i) => i.expand_export_all(self)?, + TypeDef::Func(f) => f.expand_export_all(self)?, + TypeDef::Array(_) | TypeDef::Struct(_) => {} + } + Ok(true) + } + ModuleField::Import(t) => { + self.expand_export_all_item_sig(&mut t.item)?; + Ok(true) + } + ModuleField::ExportAll(span, instance) => { + let info = match self.instance_exports.get(instance) { + Some(InstanceExports::Inline(i)) => i, + Some(InstanceExports::Module(i)) => &self.module_exports[i], + None => { + return Err(Error::new( + instance.span(), + format!("failed to find instance to inline exports"), + )) + } + }; + for (i, (name, ns)) in info.export_names.iter().enumerate() { + let index = Index::Num(i as u32, *span); + let alias_id = gensym::gen(*span); + self.to_append.push(ModuleField::Alias(Alias { + id: Some(alias_id), + instance: Some(Index::Id(*instance)), + name: None, + span: *span, + kind: ns.to_export_kind(index), + })); self.to_append.push(ModuleField::Export(Export { + span: *span, name, - kind: ExportKind::Global(Index::Num(self.globals)), + kind: ns.to_export_kind(Index::Id(alias_id)), })); } - self.globals += 1; + Ok(false) } + _ => Ok(true), + } + } - ModuleField::Event(e) => { - for name in e.exports.names.drain(..) { - self.to_append.push(ModuleField::Export(Export { - name, - kind: ExportKind::Event(Index::Num(self.events)), - })); + fn expand_export_all_item_sig(&mut self, item: &mut ItemSig<'a>) -> Result<(), Error> { + match &mut item.kind { + ItemKind::Module(t) => self.expand_export_all_type_use(t), + ItemKind::Instance(t) => self.expand_export_all_type_use(t), + ItemKind::Func(t) | ItemKind::Event(EventType::Exception(t)) => { + self.expand_export_all_type_use(t) + } + ItemKind::Memory(_) | ItemKind::Table(_) | ItemKind::Global(_) => Ok(()), + } + } + + fn expand_export_all_type_use( + &mut self, + item: &mut TypeUse<'a, impl TypeReference<'a>>, + ) -> Result<(), Error> { + if let Some(t) = &mut item.inline { + t.expand_export_all(self)?; + } + Ok(()) + } +} + +#[derive(Default, Clone)] +struct ExportInfo<'a> { + wat_name_to_export_idx: HashMap<(&'a str, Ns), u32>, + export_names: Vec<(&'a str, Ns)>, +} + +impl<'a> ExportInfo<'a> { + fn from_exports(exports: &[ExportType<'a>]) -> ExportInfo<'a> { + let mut wat_name_to_export_idx = HashMap::new(); + let mut export_names = Vec::new(); + for (i, export) in exports.iter().enumerate() { + let ns = Ns::from_item(&export.item); + if let Some(id) = export.item.id { + wat_name_to_export_idx.insert((id.name(), ns), i as u32); + } + export_names.push((export.name, ns)); + } + ExportInfo { + wat_name_to_export_idx, + export_names, + } + } +} + +trait TypeReference<'a> { + fn record_missing(&self, cx: &mut Expander<'a>); + fn expand_export_all(&mut self, cx: &mut Expander<'a>) -> Result<(), Error>; +} + +impl<'a> TypeReference<'a> for FunctionType<'a> { + fn record_missing(&self, _cx: &mut Expander<'a>) {} + fn expand_export_all(&mut self, _cx: &mut Expander<'a>) -> Result<(), Error> { + Ok(()) + } +} + +impl<'a> TypeReference<'a> for ModuleType<'a> { + fn record_missing(&self, cx: &mut Expander<'a>) { + for i in self.imports.iter() { + cx.record_item_sig(&i.item); + } + for e in self.exports.iter() { + cx.record_item_sig(&e.item); + } + for (_, name) in self.instance_exports.iter() { + cx.record_missing(&Index::Id(*name), Ns::Type); + } + } + fn expand_export_all(&mut self, cx: &mut Expander<'a>) -> Result<(), Error> { + for (_span, instance_ty_name) in self.instance_exports.drain(..) { + match cx.instance_type_exports.get(&instance_ty_name) { + Some(exports) => self.exports.extend(exports.iter().cloned()), + None => { + return Err(Error::new( + instance_ty_name.span(), + format!("failed to find instance type to inline exports from"), + )) } - self.events += 1; } + } + for i in self.imports.iter_mut() { + cx.expand_export_all_item_sig(&mut i.item)?; + } + for e in self.exports.iter_mut() { + cx.expand_export_all_item_sig(&mut e.item)?; + } + Ok(()) + } +} - _ => {} +impl<'a> TypeReference<'a> for InstanceType<'a> { + fn record_missing(&self, cx: &mut Expander<'a>) { + for e in self.exports.iter() { + cx.record_item_sig(&e.item); + } + } + fn expand_export_all(&mut self, cx: &mut Expander<'a>) -> Result<(), Error> { + for e in self.exports.iter_mut() { + cx.expand_export_all_item_sig(&mut e.item)?; } + Ok(()) } } diff --git a/crates/wast/src/resolve/gensym.rs b/crates/wast/src/resolve/gensym.rs new file mode 100644 index 0000000000..5f6d94133a --- /dev/null +++ b/crates/wast/src/resolve/gensym.rs @@ -0,0 +1,20 @@ +use crate::ast::{Id, Span}; +use std::cell::Cell; + +thread_local!(static NEXT: Cell = Cell::new(0)); + +pub fn reset() { + NEXT.with(|c| c.set(0)); +} + +pub fn gen(span: Span) -> Id<'static> { + NEXT.with(|next| { + let gen = next.get() + 1; + next.set(gen); + Id::gensym(span, gen) + }) +} + +pub fn fill<'a>(span: Span, slot: &mut Option>) -> Id<'a> { + *slot.get_or_insert_with(|| gen(span)) +} diff --git a/crates/wast/src/resolve/mod.rs b/crates/wast/src/resolve/mod.rs index aa04ee5fa8..4501990484 100644 --- a/crates/wast/src/resolve/mod.rs +++ b/crates/wast/src/resolve/mod.rs @@ -1,9 +1,88 @@ use crate::ast::*; use crate::Error; +mod deinline_import_export; mod expand; +mod gensym; mod names; -mod tyexpand; + +#[derive(PartialEq, Eq, Hash, Copy, Clone, Debug)] +pub enum Ns { + Func, + Table, + Global, + Memory, + Module, + Instance, + Event, + Type, +} + +impl Ns { + fn from_item(item: &ItemSig<'_>) -> Ns { + match item.kind { + ItemKind::Func(_) => Ns::Func, + ItemKind::Table(_) => Ns::Table, + ItemKind::Memory(_) => Ns::Memory, + ItemKind::Global(_) => Ns::Global, + ItemKind::Instance(_) => Ns::Instance, + ItemKind::Module(_) => Ns::Module, + ItemKind::Event(_) => Ns::Event, + } + } + + fn from_export<'a>(kind: &ExportKind<'a>) -> (Index<'a>, Ns) { + match *kind { + ExportKind::Func(f) => (f, Ns::Func), + ExportKind::Table(f) => (f, Ns::Table), + ExportKind::Global(f) => (f, Ns::Global), + ExportKind::Memory(f) => (f, Ns::Memory), + ExportKind::Instance(f) => (f, Ns::Instance), + ExportKind::Module(f) => (f, Ns::Module), + ExportKind::Event(f) => (f, Ns::Event), + ExportKind::Type(f) => (f, Ns::Type), + } + } + + fn from_export_mut<'a, 'b>(kind: &'b mut ExportKind<'a>) -> (&'b mut Index<'a>, Ns) { + match kind { + ExportKind::Func(f) => (f, Ns::Func), + ExportKind::Table(f) => (f, Ns::Table), + ExportKind::Global(f) => (f, Ns::Global), + ExportKind::Memory(f) => (f, Ns::Memory), + ExportKind::Instance(f) => (f, Ns::Instance), + ExportKind::Module(f) => (f, Ns::Module), + ExportKind::Event(f) => (f, Ns::Event), + ExportKind::Type(f) => (f, Ns::Type), + } + } + + fn to_export_kind<'a>(&self, index: Index<'a>) -> ExportKind<'a> { + match self { + Ns::Func => ExportKind::Func(index), + Ns::Table => ExportKind::Table(index), + Ns::Global => ExportKind::Global(index), + Ns::Memory => ExportKind::Memory(index), + Ns::Module => ExportKind::Module(index), + Ns::Instance => ExportKind::Instance(index), + Ns::Event => ExportKind::Event(index), + Ns::Type => ExportKind::Type(index), + } + } + + fn desc(&self) -> &'static str { + match self { + Ns::Func => "func", + Ns::Table => "table", + Ns::Global => "global", + Ns::Memory => "memory", + Ns::Module => "module", + Ns::Instance => "instance", + Ns::Event => "event", + Ns::Type => "type", + } + } +} pub fn resolve<'a>(module: &mut Module<'a>) -> Result, Error> { let fields = match &mut module.kind { @@ -11,24 +90,19 @@ pub fn resolve<'a>(module: &mut Module<'a>) -> Result, Error> { _ => return Ok(Default::default()), }; - // First up, let's de-inline import/export annotations since this'll - // restructure the module and affect how we count indices in future passes - // since function definitions turn into imports. - // - // The first pass switches all inline imports to explicit `Import` items. - // This pass also counts all `Import` items per-type to start building up - // the index space so we know the corresponding index for each item. - // - // In a second pass we then remove all inline `export` annotations, again - // counting indices as we go along to ensure we always know the index for - // what we're exporting. + // Ensure that each resolution of a module is deterministic in the names + // that it generates by resetting our thread-local symbol generator. + gensym::reset(); + + // First up, de-inline import/export annotations. // - // The final step is then taking all of the injected `export` fields and - // actually pushing them onto our list of fields. - let mut expander = expand::Expander::default(); - expander.process(fields, expand::Expander::deinline_import); - expander.process(fields, expand::Expander::deinline_export); + // This ensures we only have to deal with inline definitions and to + // calculate exports we only have to look for a particular kind of module + // field. + deinline_import_export::run(fields); + // With a canonical form of imports make sure that imports are all listed + // first. for i in 1..fields.len() { let span = match &fields[i] { ModuleField::Import(i) => i.span, @@ -44,55 +118,17 @@ pub fn resolve<'a>(module: &mut Module<'a>) -> Result, Error> { return Err(Error::new(span, format!("import after {}", name))); } - // For the second pass we resolve all inline type annotations. This will, in - // the order that we see them, append to the list of types. Note that types - // are indexed so we're careful to always insert new types just before the - // field that we're looking at. - // - // It's not strictly required that we `move_types_first` here but it gets - // our indexing to exactly match wabt's which our test suite is asserting. - let mut cur = 0; - let mut expander = tyexpand::Expander::default(); - move_types_first(fields); - while cur < fields.len() { - expander.expand(&mut fields[cur]); - for new in expander.to_prepend.drain(..) { - fields.insert(cur, new); - cur += 1; - } - cur += 1; - } + // Next inject any `alias` declarations and expand `(export $foo)` + // annotations. These are all part of the module-linking proposal and we try + // to resolve their injection here to keep index spaces stable for later. + expand::run(fields)?; // Perform name resolution over all `Index` items to resolve them all to // indices instead of symbolic names. - // - // For this operation we do need to make sure that imports are sorted first - // because otherwise we'll be calculating indices in the wrong order. - move_imports_first(fields); - let mut resolver = names::Resolver::default(); - for field in fields.iter_mut() { - resolver.register(field)?; - } - for field in fields.iter_mut() { - resolver.resolve(field)?; - } + let resolver = names::resolve(fields)?; Ok(Names { resolver }) } -fn move_imports_first(fields: &mut [ModuleField<'_>]) { - fields.sort_by_key(|f| match f { - ModuleField::Import(_) => false, - _ => true, - }); -} - -fn move_types_first(fields: &mut [ModuleField<'_>]) { - fields.sort_by_key(|f| match f { - ModuleField::Type(_) => false, - _ => true, - }); -} - /// Representation of the results of name resolution for a module. /// /// This structure is returned from the @@ -110,7 +146,8 @@ impl<'a> Names<'a> { /// looked up in the function namespace and converted to a `Num`. If the /// `Id` is not defined then an error will be returned. pub fn resolve_func(&self, idx: &mut Index<'a>) -> Result<(), Error> { - self.resolver.resolve_idx(idx, names::Ns::Func) + self.resolver.module().resolve(idx, Ns::Func)?; + Ok(()) } /// Resolves `idx` within the memory namespace. @@ -119,7 +156,8 @@ impl<'a> Names<'a> { /// looked up in the memory namespace and converted to a `Num`. If the /// `Id` is not defined then an error will be returned. pub fn resolve_memory(&self, idx: &mut Index<'a>) -> Result<(), Error> { - self.resolver.resolve_idx(idx, names::Ns::Memory) + self.resolver.module().resolve(idx, Ns::Memory)?; + Ok(()) } /// Resolves `idx` within the table namespace. @@ -128,7 +166,8 @@ impl<'a> Names<'a> { /// looked up in the table namespace and converted to a `Num`. If the /// `Id` is not defined then an error will be returned. pub fn resolve_table(&self, idx: &mut Index<'a>) -> Result<(), Error> { - self.resolver.resolve_idx(idx, names::Ns::Table) + self.resolver.module().resolve(idx, Ns::Table)?; + Ok(()) } /// Resolves `idx` within the global namespace. @@ -137,6 +176,7 @@ impl<'a> Names<'a> { /// looked up in the global namespace and converted to a `Num`. If the /// `Id` is not defined then an error will be returned. pub fn resolve_global(&self, idx: &mut Index<'a>) -> Result<(), Error> { - self.resolver.resolve_idx(idx, names::Ns::Global) + self.resolver.module().resolve(idx, Ns::Global)?; + Ok(()) } } diff --git a/crates/wast/src/resolve/names.rs b/crates/wast/src/resolve/names.rs index 6c025f9279..dbda62fc26 100644 --- a/crates/wast/src/resolve/names.rs +++ b/crates/wast/src/resolve/names.rs @@ -1,126 +1,1301 @@ use crate::ast::*; +use crate::resolve::gensym; +use crate::resolve::Ns; use crate::Error; use std::collections::HashMap; +use std::mem; -#[derive(Copy, Clone)] -pub enum Ns { - Data, - Elem, - Event, - Func, - Global, - Memory, - Table, - Type, - Field, -} - -impl Ns { - fn desc(&self) -> &'static str { - match self { - Ns::Data => "data", - Ns::Elem => "elem", - Ns::Event => "event", - Ns::Func => "func", - Ns::Global => "global", - Ns::Memory => "memory", - Ns::Table => "table", - Ns::Type => "type", - Ns::Field => "field", - } - } +pub fn resolve<'a>(fields: &mut Vec>) -> Result, Error> { + let mut resolver = Resolver::default(); + resolver.process(None, fields)?; + Ok(resolver) } +/// Context structure used to perform name resolution. #[derive(Default)] pub struct Resolver<'a> { - ns: [Namespace<'a>; 9], - func_tys: HashMap>, + modules: Vec>, + /// Index of the module currently being worked on. + cur: usize, } #[derive(Default)] -struct Namespace<'a> { - names: HashMap, u32>, - count: u32, +pub struct Module<'a> { + /// The index of this module in `resolver.modules`. + id: usize, + + /// The parent index of this module, if present. + parent: Option, + + /// Whether or not names have been registered in the namespaces below, used + /// in `key_to_idx` when we're adding new type fields. + registered: bool, + + // Namespaces within each module. Note that each namespace carries with it + // information about the signature of the item in that namespace. The + // signature is later used to synthesize the type of a module and inject + // type annotations if necessary. + funcs: Namespace<'a, Index<'a>>, + globals: Namespace<'a, GlobalType<'a>>, + tables: Namespace<'a, TableType>, + memories: Namespace<'a, MemoryType>, + types: Namespace<'a, TypeInfo<'a>>, + events: Namespace<'a, Index<'a>>, + modules: Namespace<'a, ModuleInfo<'a>>, + instances: Namespace<'a, InstanceDef<'a>>, + datas: Namespace<'a, ()>, + elems: Namespace<'a, ()>, + fields: Namespace<'a, ()>, + + /// Information about the exports of this module. + exports: ExportNamespace<'a>, + /// List of exported items in this module, along with what kind of item is + /// exported. + exported_ids: Vec>, + + // Maps used to "intern" types. These maps are populated as type annotations + // are seen and inline type annotations use previously defined ones if + // there's a match. + func_type_to_idx: HashMap, Index<'a>>, + instance_type_to_idx: HashMap, Index<'a>>, + module_type_to_idx: HashMap, Index<'a>>, + + /// Fields, during processing, which should be prepended to the + /// currently-being-processed field. This should always be empty after + /// processing is complete. + to_prepend: Vec>, +} + +enum InstanceDef<'a> { + /// An instance definition that was imported. + Import { + /// An index pointing to the type of the instance. + type_idx: Index<'a>, + /// Information about the exports of the instance. + exports: ExportNamespace<'a>, + }, + /// An instance definition that was the instantiation of a module. + Instantiate { + /// The module that is instantiated to create this instance + module_idx: Index<'a>, + }, +} + +enum ModuleInfo<'a> { + /// An imported module with inline information about the exports. + Imported { + /// Information about the exports of this module, inferred from the type. + exports: ExportNamespace<'a>, + /// The type of the imported module. + type_idx: Index<'a>, + }, + Nested { + /// The index in `resolver.modules` of where this module resides. It + /// will need to be consulted for export information. + idx: usize, + /// The type of the module. + type_idx: Index<'a>, + }, } impl<'a> Resolver<'a> { - pub fn register(&mut self, item: &ModuleField<'a>) -> Result<(), Error> { - let mut register = - |ns: Ns, name: Option>| self.ns_mut(ns).register(name, ns.desc()).map(|_| ()); + fn process( + &mut self, + parent: Option, + fields: &mut Vec>, + ) -> Result<(), Error> { + // First up create a new `Module` structure and add it to our list of + // modules. Afterwards configure some internal fields about it as well. + assert_eq!(self.modules.len(), self.cur); + self.modules.push(Module::default()); + let module = &mut self.modules[self.cur]; + module.id = self.cur; + module.parent = parent; + + // The first thing we do is expand `type` declarations and their + // specified-inline types. This will expand inline instance/module types + // and add more type declarations as necessary. This happens early on + // since it's pretty easy to do and it also will add more types to the + // index space, which we'll want to stabilize soon. + self.iter_and_prepend( + fields, + |me, field| Ok(me.modules[me.cur].expand_type(field)), + )?; + + // FIXME: this is a bad hack and is something we should not do. This is + // not forwards-compatible with type imports since we're reordering the + // text file. + // + // The purpose of this is: + // + // * In `me.expand(...)` below we'll be creating more types. This is + // for `TypeUse` on items other than type declarations (e.g. inline + // functions) as well as inline module types. Especially for inline + // modules we don't know how many types we'll be manufacturing for + // each item. + // + // * The injected types come *after* we've numbered the existing types + // in the module. This means that they need to come last in the index + // space. Somehow the binary emission needs to be coordinated with + // all this where the emitted types get emitted last. + // + // * However, according to the current module linking spec, modules + // can be interleaved with the type section. This means that we can't + // rely on types being declared before the module section. The module + // section can only refer to previously defined types as well. + // + // Practically there is no purpose to interleaving the type and module + // section today. As a result we can safely sort all types to the front. + // This, however, can break the roundtrip binary-text-binary for + // strictly-speaking compliant modules with the module linking spec. + // Anyway, this is a bummer, should figure out a better thing in the + // future. + // + // I've tried to open discussion about this at + // WebAssembly/module-linking#8 + fields.sort_by_key(|field| match field { + ModuleField::Type(_) + | ModuleField::Alias(Alias { + kind: ExportKind::Type(_), + .. + }) => 0, + _ => 1, + }); + + // Number everything in the module, recording what names correspond to + // what indices. + let module = &mut self.modules[self.cur]; + for field in fields.iter_mut() { + module.register(field)?; + } + module.registered = true; + + // After we've got our index spaces we then need to expand inline type + // annotations on everything other than `type` definitions. + // + // This is the key location we start recursively processing nested + // modules as well. Along the way of expansion we record the signature + // of all items. This way nested modules have access to all names + // defined in parent modules, but they only have access to type + // signatures of previously defined items. + self.iter_and_prepend(fields, |me, field| me.expand(field))?; + + // This is the same as the comment above, only we're doing it now after + // the full expansion process since all types should now be present in + // the module. + fields.sort_by_key(|field| match field { + ModuleField::Type(_) + | ModuleField::Alias(Alias { + kind: ExportKind::Type(_), + .. + }) => 0, + _ => 1, + }); + + // And finally the last step is to replace all our `Index::Id` instances + // with `Index::Num` in the AST. This does not recurse into nested + // modules because that was handled in `expand` above. + let module = &mut self.modules[self.cur]; + for field in fields.iter_mut() { + module.resolve_field(field)?; + } + Ok(()) + } + + pub fn module(&self) -> &Module<'a> { + &self.modules[self.cur] + } + + fn iter_and_prepend( + &mut self, + fields: &mut Vec>, + cb: impl Fn(&mut Self, &mut ModuleField<'a>) -> Result<(), Error>, + ) -> Result<(), Error> { + let module = &self.modules[self.cur]; + assert!(module.to_prepend.is_empty()); + let mut cur = 0; + // The name on the tin of this method is that we'll be *prepending* + // items in the `to_prepend` list. The only items we're prepending are + // types, however. In the module-linking proposal we really need to + // prepend types because the previous item (like the definition of a + // module) might refer back to the type and the type must precede the + // reference. If we don't have nested modules, however, it's fine to + // throw everything at the end. + // + // WABT throws everything at the end (expansion of inline type + // annotations from functions/etc), and we want to be binary-compatible + // with that. With all of that in mind we only prepend if some + // conditions are true: + // + // * If names have *not* been registered, then we're expanding types. If + // a type is expanded then it's an instance/module type, so we must + // prepend. + // + // * If `self.cur` is not zero, then we're in a nested module, so we're + // in the module linking proposal. + // + // * If we have any instances/modules then we're part of the module + // linking proposal, so we prepend. + // + // Basically if it looks like we're an "MVP-compatible" module we throw + // types at the end. Otherwise if it looks like module-linking is being + // used we prepend items. This will likely change as other tools + // implement module linking. + let actually_prepend = !module.registered + || self.cur != 0 + || module.instances.count > 0 + || module.modules.count > 0; + while cur < fields.len() { + cb(self, &mut fields[cur])?; + if actually_prepend { + for item in self.modules[self.cur].to_prepend.drain(..) { + fields.insert(cur, item); + cur += 1; + } + } + cur += 1; + } + if !actually_prepend { + fields.extend(self.modules[self.cur].to_prepend.drain(..)); + } + assert!(self.modules[self.cur].to_prepend.is_empty()); + Ok(()) + } + + fn expand(&mut self, item: &mut ModuleField<'a>) -> Result<(), Error> { + let module = &mut self.modules[self.cur]; match item { - ModuleField::Import(i) => match i.kind { - ImportKind::Func(_) => register(Ns::Func, i.id), - ImportKind::Memory(_) => register(Ns::Memory, i.id), - ImportKind::Table(_) => register(Ns::Table, i.id), - ImportKind::Global(_) => register(Ns::Global, i.id), - ImportKind::Event(_) => register(Ns::Event, i.id), + // expansion generally handled above, we're just handling the + // signature of each item here + ModuleField::Type(ty) => { + let info = match &ty.def { + TypeDef::Func(f) => TypeInfo::Func(f.key()), + TypeDef::Instance(i) => TypeInfo::Instance { + info: Module::from_exports(ty.span, self.cur, &i.exports)?, + key: i.key(), + }, + TypeDef::Module(m) => TypeInfo::Module { + info: Module::from_exports(ty.span, self.cur, &m.exports)?, + key: m.key(), + }, + TypeDef::Array(_) | TypeDef::Struct(_) => TypeInfo::Other, + }; + module.types.push_item(self.cur, info); + } + + ModuleField::Import(i) => { + module.expand_item_sig(&mut i.item, false); + + // Record each item's type information in the index space + // arrays. + match &mut i.item.kind { + ItemKind::Func(t) => module.funcs.push_item(self.cur, t.index.unwrap()), + ItemKind::Event(EventType::Exception(t)) => { + module.events.push_item(self.cur, t.index.unwrap()) + } + ItemKind::Instance(t) => { + let exports = match t.inline.take() { + Some(i) => ExportNamespace::from_exports(&i.exports)?, + None => ExportNamespace::default(), + }; + module.instances.push_item( + self.cur, + InstanceDef::Import { + type_idx: t.index.unwrap(), + exports, + }, + ); + } + ItemKind::Module(m) => { + let exports = match m.inline.take() { + Some(m) => ExportNamespace::from_exports(&m.exports)?, + None => ExportNamespace::default(), + }; + module.modules.push_item( + self.cur, + ModuleInfo::Imported { + type_idx: m.index.unwrap(), + exports, + }, + ); + } + ItemKind::Global(g) => module.globals.push_item(self.cur, g.clone()), + ItemKind::Table(t) => module.tables.push_item(self.cur, t.clone()), + ItemKind::Memory(m) => module.memories.push_item(self.cur, m.clone()), + } + } + ModuleField::Func(f) => { + let idx = module.expand_type_use(&mut f.ty); + module.funcs.push_item(self.cur, idx); + if let FuncKind::Inline { expression, .. } = &mut f.kind { + module.expand_expression(expression); + } + } + ModuleField::Global(g) => { + module.globals.push_item(self.cur, g.ty); + if let GlobalKind::Inline(expr) = &mut g.kind { + module.expand_expression(expr); + } + } + ModuleField::Data(d) => { + if let DataKind::Active { offset, .. } = &mut d.kind { + module.expand_expression(offset); + } + } + ModuleField::Elem(e) => { + if let ElemKind::Active { offset, .. } = &mut e.kind { + module.expand_expression(offset); + } + } + ModuleField::Event(e) => match &mut e.ty { + EventType::Exception(ty) => { + let idx = module.expand_type_use(ty); + module.events.push_item(self.cur, idx); + } }, - ModuleField::Global(i) => register(Ns::Global, i.id), - ModuleField::Memory(i) => register(Ns::Memory, i.id), - ModuleField::Func(i) => register(Ns::Func, i.id), - ModuleField::Table(i) => register(Ns::Table, i.id), + ModuleField::Instance(i) => { + if let InstanceKind::Inline { + module: module_idx, .. + } = i.kind + { + module + .instances + .push_item(self.cur, InstanceDef::Instantiate { module_idx }); + } + } + ModuleField::NestedModule(m) => { + let (fields, ty) = match &mut m.kind { + NestedModuleKind::Inline { fields, ty } => (fields, ty), + NestedModuleKind::Import { .. } => panic!("should only be inline"), + }; + + // And this is where the magic happens for recursive resolution + // of modules. Here we switch `self.cur` to a new module that + // will be injected (the length of the array since a new element + // is pushed). We then recurse and process, in its entirety, the + // nested module. + // + // Our nested module will have access to all names defined in + // our module, but it will only have access to item signatures + // of previously defined items. + // + // After this is all done if the inline type annotation is + // missing then we can infer the type of the inline module and + // inject the type here. + let prev = mem::replace(&mut self.cur, self.modules.len()); + self.process(Some(prev), fields)?; + let child = mem::replace(&mut self.cur, prev); + if ty.is_none() { + *ty = Some(self.infer_module_ty(m.span, child, fields)?); + } + self.modules[self.cur].modules.push_item( + self.cur, + ModuleInfo::Nested { + type_idx: ty.unwrap(), + idx: child, + }, + ); + } + ModuleField::Table(t) => { + let ty = match t.kind { + TableKind::Normal(ty) => ty, + _ => panic!("tables should be normalized by now"), + }; + module.tables.push_item(self.cur, ty.clone()); + } + ModuleField::Memory(t) => { + let ty = match t.kind { + MemoryKind::Normal(ty) => ty, + _ => panic!("memories should be normalized by now"), + }; + module.memories.push_item(self.cur, ty.clone()); + } + ModuleField::Alias(a) => self.expand_alias(a)?, + ModuleField::Start(_) | ModuleField::Export(_) | ModuleField::Custom(_) => {} + ModuleField::ExportAll(..) => unreachable!(), + } + return Ok(()); + } + + fn expand_alias(&mut self, a: &mut Alias<'a>) -> Result<(), Error> { + // This is a pretty awful method. I'd like to preemptively apologize for + // how much of a mess this is. Not only is what this doing inherently + // complicated but it's also complicated by working around + // Rust-the-language as well. + // + // The general idea is that we're taking a look at where this alias is + // coming from (nested instance or parent) and we manufacture an + // appropriate closure which, when invoked, creates a `SigAlias` + // appropriate for this signature. + // + // Cleanups are very welcome to this function if you've got an idea, I + // don't think anyone will be wed to it in its current form. + // + // As some more information on this method, though, the goal of this + // method is to expand the `alias` directive and fill in all the names + // here. Note that we do this eagerly because we want to fill in the + // type signature of the item this alias is introducing. We don't want + // to fill in the *full* type signature, though, just that it was an + // alias. The full signature may not yet be available in some valid + // situations, so we record the bare minimum necessary that an 'alias' + // was used, and then later on if that alias attempts to get followed + // (e.g. in `item_for`) we'll do the checks to make sure everything + // lines up. + let gen1; + let gen2; + let gen3; + let gen4; + let gen_alias: &dyn Fn(&mut Index<'a>, Ns) -> Result, Error> = + match &mut a.instance { + Some(i) => { + self.modules[self.cur].resolve(i, Ns::Instance)?; + let (def, m) = self.instance_for(self.cur, i)?; + match def { + InstanceDef::Import { type_idx, exports } => { + gen1 = move |idx: &mut Index<'a>, ns: Ns| { + Ok(SigAlias::TypeExport { + module: m, + ty: *type_idx, + export_idx: exports.resolve(idx, ns)?, + }) + }; + &gen1 + } + InstanceDef::Instantiate { module_idx } => { + let (info, m) = self.module_for(m, module_idx)?; + match info { + ModuleInfo::Imported { type_idx, exports } => { + gen2 = move |idx: &mut Index<'a>, ns: Ns| { + Ok(SigAlias::TypeExport { + module: m, + ty: *type_idx, + export_idx: exports.resolve(idx, ns)?, + }) + }; + &gen2 + } + ModuleInfo::Nested { + idx: module_idx, .. + } => { + let module = &self.modules[*module_idx]; + gen3 = move |idx: &mut Index<'a>, ns: Ns| { + Ok(SigAlias::ModuleExport { + module: *module_idx, + export_idx: module.exports.resolve(idx, ns)?, + }) + }; + &gen3 + } + } + } + } + } + None => { + let module_idx = match self.modules[self.cur].parent { + Some(p) => p, + None => { + return Err(Error::new( + a.span, + "cannot use alias parent directives in root module".to_string(), + )) + } + }; + let module = &self.modules[module_idx]; + gen4 = move |idx: &mut Index<'a>, ns: Ns| { + module.resolve(idx, ns)?; + Ok(SigAlias::Item { + module: module_idx, + idx: *idx, + }) + }; + &gen4 + } + }; + match &mut a.kind { + ExportKind::Func(idx) => { + let item = gen_alias(idx, Ns::Func)?; + self.modules[self.cur].funcs.push(Sig::Alias(item)); + } + ExportKind::Table(idx) => { + let item = gen_alias(idx, Ns::Table)?; + self.modules[self.cur].tables.push(Sig::Alias(item)); + } + ExportKind::Global(idx) => { + let item = gen_alias(idx, Ns::Global)?; + self.modules[self.cur].globals.push(Sig::Alias(item)); + } + ExportKind::Memory(idx) => { + let item = gen_alias(idx, Ns::Memory)?; + self.modules[self.cur].memories.push(Sig::Alias(item)); + } + ExportKind::Event(idx) => { + let item = gen_alias(idx, Ns::Event)?; + self.modules[self.cur].events.push(Sig::Alias(item)); + } + ExportKind::Type(idx) => { + let item = gen_alias(idx, Ns::Type)?; + self.modules[self.cur].types.push(Sig::Alias(item)); + } + ExportKind::Instance(idx) => { + let item = gen_alias(idx, Ns::Instance)?; + self.modules[self.cur].instances.push(Sig::Alias(item)); + } + ExportKind::Module(idx) => { + let item = gen_alias(idx, Ns::Module)?; + self.modules[self.cur].modules.push(Sig::Alias(item)); + } + } + Ok(()) + } + + /// Creates a local type definition that matches the signature of the + /// `child` module, which is defined by `fields`. + /// + /// The goal here is that we're injecting type definitions into the current + /// module to create a `module` type that matches the module provided, and + /// then we'll use that new type as the type of the module itself. + fn infer_module_ty( + &mut self, + span: Span, + child: usize, + fields: &[ModuleField<'a>], + ) -> Result, Error> { + let mut imports = Vec::new(); + let mut exports = Vec::new(); + + for field in fields { + match field { + // For each import it already has the type signature listed + // inline, so we use that and then copy out the signature from + // the module to our module. + ModuleField::Import(i) => { + let item = match &i.item.kind { + ItemKind::Func(f) => Item::Func(f.index.unwrap()), + ItemKind::Module(f) => Item::Module(f.index.unwrap()), + ItemKind::Instance(f) => Item::Instance(f.index.unwrap()), + ItemKind::Event(EventType::Exception(f)) => Item::Event(f.index.unwrap()), + ItemKind::Table(t) => Item::Table(t.clone()), + ItemKind::Memory(t) => Item::Memory(t.clone()), + ItemKind::Global(t) => Item::Global(t.clone()), + }; + let item = self.copy_item_from_module(i.span, child, &item)?; + imports.push((i.module, i.field, item)); + } + + // Exports are a little more complicated, we have to use the + // `*_for` family of functions to follow the trail of + // definitions to figure out the signature of the item referred + // to by the export. + ModuleField::Export(e) => { + let (module, item) = match &e.kind { + ExportKind::Func(i) => { + let (type_idx, m) = self.func_for(child, i)?; + (m, Item::Func(*type_idx)) + } + ExportKind::Event(i) => { + let (type_idx, m) = self.event_for(child, i)?; + (m, Item::Event(*type_idx)) + } + ExportKind::Table(i) => { + let (table_ty, m) = self.table_for(child, i)?; + (m, Item::Table(table_ty.clone())) + } + ExportKind::Memory(i) => { + let (memory_ty, m) = self.memory_for(child, i)?; + (m, Item::Memory(memory_ty.clone())) + } + ExportKind::Global(i) => { + let (global_ty, m) = self.global_for(child, i)?; + (m, Item::Global(global_ty.clone())) + } + ExportKind::Module(i) => { + let (info, m) = self.module_for(child, i)?; + let type_idx = match info { + ModuleInfo::Imported { type_idx, .. } => *type_idx, + ModuleInfo::Nested { type_idx, .. } => *type_idx, + }; + (m, Item::Module(type_idx)) + } + ExportKind::Instance(i) => { + let (def, m) = self.instance_for(child, i)?; + match def { + InstanceDef::Import { type_idx, .. } => { + (m, Item::Instance(*type_idx)) + } + InstanceDef::Instantiate { module_idx } => { + let (info, m) = self.module_for(m, module_idx)?; + let type_idx = match info { + ModuleInfo::Imported { type_idx, .. } => *type_idx, + ModuleInfo::Nested { type_idx, .. } => *type_idx, + }; + let item = + self.copy_type_from_module(e.span, m, &type_idx, true)?; + exports.push((e.name, item)); + continue; + } + } + } + ExportKind::Type(_) => { + return Err(Error::new( + span, + "exported types not supported yet".to_string(), + )) + } + }; + let item = self.copy_item_from_module(e.span, module, &item)?; + exports.push((e.name, item)); + } + _ => {} + } + } + let key = (imports, exports); + Ok(self.modules[self.cur].key_to_idx(span, key)) + } + + /// Copies an item signature from a child module into the current module. + /// This will recursively do so for referenced types and value types and + /// such. + fn copy_item_from_module( + &mut self, + span: Span, + child: usize, + item: &Item<'a>, + ) -> Result, Error> { + Ok(match item { + // These items have everything specified inline, nothing to recurse + // with, so we just clone it. + Item::Table(_) | Item::Memory(_) => item.clone(), + // Items with indirect indices means the contents of the index need + // to be fully copied into our module. + Item::Func(idx) | Item::Event(idx) | Item::Module(idx) | Item::Instance(idx) => { + self.copy_type_from_module(span, child, idx, false)? + } + // Globals just need to copy over the value type and otherwise + // contain most information inline. + Item::Global(ty) => Item::Global(GlobalType { + ty: self.copy_valtype_from_module(span, child, ty.ty)?, + mutable: ty.mutable, + }), + }) + } + + /// Copies a type index from one module into the current module. + /// + /// This method will chase the actual definition of `type_idx` within + /// `child` and recursively copy all contents into `self.cur`. + fn copy_type_from_module( + &mut self, + span: Span, + child: usize, + type_idx: &Index<'a>, + switch_module_to_instance: bool, + ) -> Result, Error> { + let (ty, child) = self.type_for(child, type_idx)?; + match ty { + TypeInfo::Func(key) => { + let key = key.clone(); + let my_key = ( + key.0 + .iter() + .map(|ty| self.copy_valtype_from_module(span, child, *ty)) + .collect::, Error>>()?, + key.1 + .iter() + .map(|ty| self.copy_valtype_from_module(span, child, *ty)) + .collect::, Error>>()?, + ); + Ok(Item::Func(self.modules[self.cur].key_to_idx(span, my_key))) + } + + TypeInfo::Instance { key, .. } => { + let key = key.clone(); + let my_key = key + .iter() + .map(|(name, item)| { + self.copy_item_from_module(span, child, item) + .map(|x| (*name, x)) + }) + .collect::, Error>>()?; + Ok(Item::Instance( + self.modules[self.cur].key_to_idx(span, my_key), + )) + } + + TypeInfo::Module { key, .. } => { + let key = key.clone(); + let exports = key + .1 + .iter() + .map(|(name, item)| { + self.copy_item_from_module(span, child, item) + .map(|x| (*name, x)) + }) + .collect::, Error>>()?; + if switch_module_to_instance { + return Ok(Item::Instance( + self.modules[self.cur].key_to_idx(span, exports), + )); + } + let imports = key + .0 + .iter() + .map(|(module, field, item)| { + self.copy_item_from_module(span, child, item) + .map(|x| (*module, *field, x)) + }) + .collect::, Error>>()?; + Ok(Item::Module( + self.modules[self.cur].key_to_idx(span, (imports, exports)), + )) + } + + TypeInfo::Other => Err(Error::new( + span, + format!("cannot copy reference types between modules right now"), + )), + } + } + + fn copy_valtype_from_module( + &mut self, + span: Span, + _child: usize, + ty: ValType<'a>, + ) -> Result, Error> { + match ty { + // It's not clear to me at this time what to do about these types. + // What to do here sort of depends on the GC proposal. This method + // is only invoked with the module-linking proposal, which means + // that we have two proposals interacting, and that's always weird + // territory anywya. + ValType::Ref(RefType::Type(_)) + | ValType::Ref(RefType::OptType(_)) + | ValType::Rtt(_) => Err(Error::new( + span, + format!("cannot copy reference types between modules right now"), + )), + + ValType::I32 + | ValType::I64 + | ValType::F32 + | ValType::F64 + | ValType::V128 + | ValType::I8 + | ValType::I16 + | ValType::Ref(RefType::Extern) + | ValType::Ref(RefType::Func) + | ValType::Ref(RefType::Exn) + | ValType::Ref(RefType::Eq) + | ValType::Ref(RefType::I31) => Ok(ty), + } + } + + // Below here is a suite of `*_for` family of functions. These all delegate + // to `item_for` below, which is a crux of the module-linking proposal. Each + // individual method is specialized for a particular namespace, but they all + // look the same. For comments on the meat of what's happening here see the + // `item_for` method below. + + fn type_for( + &self, + module_idx: usize, + idx: &Index<'a>, + ) -> Result<(&TypeInfo<'a>, usize), Error> { + self.item_for(&self.modules[module_idx], idx, Ns::Type, &|m| &m.types) + } + + fn global_for( + &self, + module_idx: usize, + idx: &Index<'a>, + ) -> Result<(&GlobalType<'a>, usize), Error> { + self.item_for(&self.modules[module_idx], idx, Ns::Global, &|m| &m.globals) + } + + fn table_for(&self, module_idx: usize, idx: &Index<'a>) -> Result<(&TableType, usize), Error> { + self.item_for(&self.modules[module_idx], idx, Ns::Table, &|m| &m.tables) + } + + fn module_for( + &self, + module_idx: usize, + idx: &Index<'a>, + ) -> Result<(&ModuleInfo<'a>, usize), Error> { + self.item_for(&self.modules[module_idx], idx, Ns::Module, &|m| &m.modules) + } + + fn instance_for( + &self, + module_idx: usize, + idx: &Index<'a>, + ) -> Result<(&InstanceDef<'a>, usize), Error> { + self.item_for(&self.modules[module_idx], idx, Ns::Instance, &|m| { + &m.instances + }) + } + + fn func_for(&self, module_idx: usize, idx: &Index<'a>) -> Result<(&Index<'a>, usize), Error> { + self.item_for(&self.modules[module_idx], idx, Ns::Func, &|m| &m.funcs) + } + + fn event_for(&self, module_idx: usize, idx: &Index<'a>) -> Result<(&Index<'a>, usize), Error> { + self.item_for(&self.modules[module_idx], idx, Ns::Event, &|m| &m.events) + } + + fn memory_for( + &self, + module_idx: usize, + idx: &Index<'a>, + ) -> Result<(&MemoryType, usize), Error> { + self.item_for(&self.modules[module_idx], idx, Ns::Memory, &|m| &m.memories) + } + + /// A gnarly method that resolves an `idx` within the `module` provided to + /// the actual signature of that item. + /// + /// The goal of this method is to recursively follow, if necessary, chains + /// of `alias` annotations to figure out the type signature of an item. This + /// is required to determine the signature of an inline `module` when a type + /// for it has otherwise not been listed. + /// + /// Note that on success this method returns both the type signature and the + /// index of the module this item was defined in. The index returned may be + /// different than the index of `module` due to alias annotations. + fn item_for<'b, T>( + &'b self, + module: &'b Module<'a>, + idx: &Index<'a>, + ns: Ns, + get_ns: &impl for<'c> Fn(&'c Module<'a>) -> &'c Namespace<'a, T>, + ) -> Result<(&'b T, usize), Error> { + let i = get_ns(module).resolve(&mut idx.clone(), ns.desc())?; + match get_ns(module).items.get(i as usize) { + // This can arise if you do something like `(export "" (func + // 10000))`, in which case we can't infer the type signature of that + // module, so we generate an error. + None => Err(Error::new( + idx.span(), + format!("reference to {} is out of bounds", ns.desc()), + )), + + // This happens when a module refers to something that's defined + // after the module's definition. For example + // + // (module + // (module (export "" (func $f))) ;; refer to later item + // (func $f) ;; item defined too late + // ) + // + // It's not entirely clear how the upstream spec wants to eventually + // handle these cases, but for now re return this error. + Some(Sig::Unknown) => Err(Error::new( + idx.span(), + format!("reference to {} before item is defined", ns.desc()), + )), + + // This item was defined in the module and we know its signature! + // Return as such. + Some(Sig::Defined { item, module }) => Ok((item, *module)), + + // This item is aliasing the export of another module. To figure out + // its type signature we need to recursie into the `module` listed + // here. + Some(Sig::Alias(SigAlias::ModuleExport { + module: module_idx, + export_idx, + })) => { + let module = &self.modules[*module_idx]; + // Look up the `export_idx`th export, then try to extract an + // `Index` from that `ExportKind`, assuming it points to the + // right kind of item. + let kind = module + .exported_ids + .get(*export_idx as usize) + .ok_or_else(|| { + Error::new( + idx.span(), + format!("aliased from an export that does not exist"), + ) + })?; + let (idx, ns2) = Ns::from_export(kind); + if ns != ns2 { + return Err(Error::new( + idx.span(), + format!("alias points to export of wrong kind of item"), + )); + } + + // Once we have our desired index within `module`, recurse! + self.item_for(module, &idx, ns, get_ns) + } + + // This item is aliasing the export of a type. This type could be an + // imported instance or an imported module. (in the latter case the + // alias was referring to an instance instantiated from an imported + // module). In either case we chase the definition of `ty`, get its + // export information, and go from there. + Some(Sig::Alias(SigAlias::TypeExport { + module, + ty, + export_idx, + })) => { + let (info, _m) = match self.type_for(*module, ty)? { + (TypeInfo::Instance { info, .. }, m) => (info, m), + (TypeInfo::Module { info, .. }, m) => (info, m), + _ => { + return Err(Error::new( + ty.span(), + format!( + "aliased from an instance/module that is listed with the wrong type" + ), + )) + } + }; + let kind = info.exported_ids.get(*export_idx as usize).ok_or_else(|| { + Error::new( + idx.span(), + format!("aliased from an export that does not exist"), + ) + })?; + let (idx, ns2) = Ns::from_export(kind); + if ns != ns2 { + return Err(Error::new( + idx.span(), + format!("alias points to export of wrong kind of item"), + )); + } + self.item_for(info, &idx, ns, get_ns) + } + + // In this final case we're aliasing an item defined in a child + // module, so we recurse with that! + Some(Sig::Alias(SigAlias::Item { + module: module_idx, + idx, + })) => self.item_for(&self.modules[*module_idx], &idx, ns, get_ns), + } + } +} + +impl<'a> Module<'a> { + /// This is a helper method to create a synthetic `Module` representing the + /// export information of a module type defined (or instance type). This + /// is used to resolve metadata for aliases which bottom out in imported + /// types of some kind. + fn from_exports( + span: Span, + parent: usize, + exports: &[ExportType<'a>], + ) -> Result, Error> { + let mut ret = Module::default(); + ret.exports = ExportNamespace::from_exports(exports)?; + for export in exports { + // Note that the `.unwrap()`s below should be ok because `TypeUse` + // annotations should all be expanded by this point. + let kind = match &export.item.kind { + ItemKind::Func(ty) => { + let idx = push(parent, span, &mut ret.funcs, ty.index.unwrap()); + ExportKind::Func(idx) + } + ItemKind::Event(EventType::Exception(ty)) => { + let idx = push(parent, span, &mut ret.events, ty.index.unwrap()); + ExportKind::Event(idx) + } + ItemKind::Global(ty) => { + let idx = push(parent, span, &mut ret.globals, ty.clone()); + ExportKind::Global(idx) + } + ItemKind::Memory(ty) => { + let idx = push(parent, span, &mut ret.memories, ty.clone()); + ExportKind::Memory(idx) + } + ItemKind::Table(ty) => { + let idx = push(parent, span, &mut ret.tables, ty.clone()); + ExportKind::Table(idx) + } + ItemKind::Instance(ty) => { + let idx = push( + parent, + span, + &mut ret.instances, + InstanceDef::Import { + type_idx: ty.index.unwrap(), + exports: ExportNamespace::default(), + }, + ); + ExportKind::Instance(idx) + } + ItemKind::Module(ty) => { + let idx = push( + parent, + span, + &mut ret.modules, + ModuleInfo::Imported { + type_idx: ty.index.unwrap(), + exports: ExportNamespace::default(), + }, + ); + ExportKind::Module(idx) + } + }; + ret.exported_ids.push(kind); + } + return Ok(ret); + + fn push<'a, T>(parent: usize, span: Span, ns: &mut Namespace<'a, T>, item: T) -> Index<'a> { + let ret = Index::Num(ns.alloc(), span); + ns.push_item(parent, item); + return ret; + } + } + + fn expand_type(&mut self, item: &mut ModuleField<'a>) { + let ty = match item { + ModuleField::Type(t) => t, + _ => return, + }; + let id = gensym::fill(ty.span, &mut ty.id); + match &mut ty.def { + TypeDef::Func(f) => { + f.key().insert(self, Index::Id(id)); + } + TypeDef::Instance(i) => { + i.expand(self); + i.key().insert(self, Index::Id(id)); + } + TypeDef::Module(m) => { + m.expand(self); + m.key().insert(self, Index::Id(id)); + } + TypeDef::Array(_) | TypeDef::Struct(_) => {} + } + } + + fn register(&mut self, item: &ModuleField<'a>) -> Result<(), Error> { + match item { + ModuleField::Import(i) => match &i.item.kind { + ItemKind::Func(_) => self.funcs.register(i.item.id, "func")?, + ItemKind::Memory(_) => self.memories.register(i.item.id, "memory")?, + ItemKind::Table(_) => self.tables.register(i.item.id, "table")?, + ItemKind::Global(_) => self.globals.register(i.item.id, "global")?, + ItemKind::Event(_) => self.events.register(i.item.id, "event")?, + ItemKind::Module(_) => self.modules.register(i.item.id, "module")?, + ItemKind::Instance(_) => self.instances.register(i.item.id, "instance")?, + }, + ModuleField::Global(i) => self.globals.register(i.id, "global")?, + ModuleField::Memory(i) => self.memories.register(i.id, "memory")?, + ModuleField::Func(i) => self.funcs.register(i.id, "func")?, + ModuleField::Table(i) => self.tables.register(i.id, "table")?, + ModuleField::NestedModule(m) => self.modules.register(m.id, "module")?, + ModuleField::Instance(i) => self.instances.register(i.id, "instance")?, + ModuleField::Type(i) => { - let index = self.ns_mut(Ns::Type).register(i.id, Ns::Type.desc())?; match &i.def { - TypeDef::Func(func) => { - // Store a copy of this function type for resolving - // type uses later. We can't resolve this copy yet, so - // we will take care of that when resolving the type - // use. - self.func_tys.insert(index, func.clone()); - } + // For GC structure types we need to be sure to populate the + // field namespace here as well. + // + // The field namespace is global, but the resolved indices + // are relative to the struct they are defined in TypeDef::Struct(r#struct) => { for (i, field) in r#struct.fields.iter().enumerate() { if let Some(id) = field.id { - // The field namespace is global, but the - // resolved indices are relative to the struct - // they are defined in - self.ns_mut(Ns::Field).register_specific( - id, - i as u32, - Ns::Field.desc(), - )?; + self.fields.register_specific(id, i as u32, "field")?; } } } - TypeDef::Array(_) => {} + + TypeDef::Instance(_) + | TypeDef::Array(_) + | TypeDef::Func(_) + | TypeDef::Module(_) => {} } - Ok(()) + self.types.register(i.id, "type")? } - ModuleField::Elem(e) => register(Ns::Elem, e.id), - ModuleField::Data(d) => register(Ns::Data, d.id), - ModuleField::Event(e) => register(Ns::Event, e.id), - ModuleField::Start(_) | ModuleField::Export(_) | ModuleField::Custom(_) => Ok(()), + ModuleField::Elem(e) => self.elems.register(e.id, "elem")?, + ModuleField::Data(d) => self.datas.register(d.id, "data")?, + ModuleField::Event(e) => self.events.register(e.id, "event")?, + ModuleField::Alias(e) => match e.kind { + ExportKind::Func(_) => self.funcs.register(e.id, "func")?, + ExportKind::Table(_) => self.tables.register(e.id, "table")?, + ExportKind::Memory(_) => self.memories.register(e.id, "memory")?, + ExportKind::Global(_) => self.globals.register(e.id, "global")?, + ExportKind::Instance(_) => self.instances.register(e.id, "instance")?, + ExportKind::Module(_) => self.modules.register(e.id, "module")?, + ExportKind::Event(_) => self.events.register(e.id, "event")?, + ExportKind::Type(_) => self.types.register(e.id, "type")?, + }, + ModuleField::Export(e) => { + let (idx, ns) = Ns::from_export(&e.kind); + self.exports.push( + ns, + match idx { + Index::Id(id) => Some(id), + Index::Num(..) => None, + }, + )?; + self.exported_ids.push(e.kind.clone()); + return Ok(()); + } + ModuleField::Start(_) | ModuleField::Custom(_) => return Ok(()), + + // should not be present in AST by this point + ModuleField::ExportAll(..) => unreachable!(), + }; + + Ok(()) + } + + fn expand_item_sig(&mut self, item: &mut ItemSig<'a>, take_inline: bool) { + match &mut item.kind { + ItemKind::Func(t) | ItemKind::Event(EventType::Exception(t)) => { + self.expand_type_use(t); + } + ItemKind::Instance(t) => { + self.expand_type_use(t); + if take_inline { + t.inline.take(); + } + } + ItemKind::Module(m) => { + self.expand_type_use(m); + if take_inline { + m.inline.take(); + } + } + ItemKind::Global(_) | ItemKind::Table(_) | ItemKind::Memory(_) => {} } } - fn ns_mut(&mut self, ns: Ns) -> &mut Namespace<'a> { - &mut self.ns[ns as usize] + fn expand_expression(&mut self, expr: &mut Expression<'a>) { + for instr in expr.instrs.iter_mut() { + self.expand_instr(instr); + } } - fn ns(&self, ns: Ns) -> &Namespace<'a> { - &self.ns[ns as usize] + fn expand_instr(&mut self, instr: &mut Instruction<'a>) { + match instr { + Instruction::Block(bt) + | Instruction::If(bt) + | Instruction::Loop(bt) + | Instruction::Try(bt) => { + // No expansion necessary, a type reference is already here. + // We'll verify that it's the same as the inline type, if any, + // later. + if bt.ty.index.is_some() { + return; + } + + match &bt.ty.inline { + // Only actually expand `TypeUse` with an index which appends a + // type if it looks like we need one. This way if the + // multi-value proposal isn't enabled and/or used we won't + // encode it. + Some(inline) => { + if inline.params.len() == 0 && inline.results.len() <= 1 { + return; + } + } + + // If we didn't have either an index or an inline type + // listed then assume our block has no inputs/outputs, so + // fill in the inline type here. + // + // Do not fall through to expanding the `TypeUse` because + // this doesn't force an empty function type to go into the + // type section. + None => { + bt.ty.inline = Some(FunctionType::default()); + return; + } + } + self.expand_type_use(&mut bt.ty); + } + Instruction::CallIndirect(c) | Instruction::ReturnCallIndirect(c) => { + self.expand_type_use(&mut c.ty); + } + _ => {} + } + } + + fn expand_type_use(&mut self, item: &mut TypeUse<'a, T>) -> Index<'a> + where + T: TypeReference<'a>, + { + if let Some(idx) = &item.index { + return idx.clone(); + } + let key = match item.inline.as_mut() { + Some(ty) => { + ty.expand(self); + ty.key() + } + None => T::default().key(), + }; + let span = Span::from_offset(0); // FIXME: don't manufacture + let idx = self.key_to_idx(span, key); + item.index = Some(idx); + return idx; + } + + fn key_to_idx(&mut self, span: Span, key: impl TypeKey<'a>) -> Index<'a> { + // First see if this `key` already exists in the type definitions we've + // seen so far... + if let Some(idx) = key.lookup(self) { + return idx; + } + + // ... and failing that we insert a new type definition. + let id = gensym::gen(span); + self.to_prepend.push(ModuleField::Type(Type { + span, + id: Some(id), + def: key.to_def(span), + })); + let idx = Index::Id(id); + key.insert(self, idx); + + // If we're a late call to `key_to_idx` and name registration has + // already happened then we are appending to the index space, so + // register the new type's information here as well. + if self.registered { + let idx = self.types.register(Some(id), "type").unwrap(); + self.types.items[idx as usize] = Sig::Defined { + item: key.into_info(span, self.id), + module: self.id, + }; + } + + return idx; } - pub fn resolve(&self, field: &mut ModuleField<'a>) -> Result<(), Error> { + fn resolve_field(&self, field: &mut ModuleField<'a>) -> Result<(), Error> { match field { ModuleField::Import(i) => { - match &mut i.kind { - ImportKind::Func(t) | ImportKind::Event(EventType::Exception(t)) => { - self.resolve_type_use(i.span, t)?; - } - ImportKind::Global(t) => { - self.resolve_valtype(&mut t.ty)?; + self.resolve_item_sig(&mut i.item)?; + Ok(()) + } + + ModuleField::Type(ty) => { + match &mut ty.def { + TypeDef::Func(func) => func.resolve(self)?, + TypeDef::Struct(struct_) => { + for field in &mut struct_.fields { + self.resolve_valtype(&mut field.ty)?; + } } - _ => {} + TypeDef::Array(array) => self.resolve_valtype(&mut array.ty)?, + TypeDef::Module(m) => m.resolve(self)?, + TypeDef::Instance(i) => i.resolve(self)?, } Ok(()) } - ModuleField::Type(t) => self.resolve_type(t), - ModuleField::Func(f) => { - self.resolve_type_use(f.span, &mut f.ty)?; + let (idx, inline) = self.resolve_type_use(&mut f.ty)?; + let n = match idx { + Index::Num(n, _) => *n, + Index::Id(_) => panic!("expected `Num`"), + }; if let FuncKind::Inline { locals, expression } = &mut f.kind { // Resolve (ref T) in locals for (_, _, valtype) in locals.iter_mut() { @@ -129,11 +1304,19 @@ impl<'a> Resolver<'a> { // Build a resolver with local namespace for the function // body - let mut resolver = ExprResolver::new(self, f.span); + let mut resolver = ExprResolver::new(self); // Parameters come first in the local namespace... - for (id, _, _) in f.ty.func_ty.params.iter() { - resolver.locals.register(*id, "local")?; + if let Some(inline) = &inline { + for (id, _, _) in inline.params.iter() { + resolver.locals.register(*id, "local")?; + } + } else if let Some(TypeInfo::Func((params, _))) = + self.types.items.get(n as usize).and_then(|i| i.item()) + { + for _ in 0..params.len() { + resolver.locals.register(None, "local")?; + } } // .. followed by locals themselves @@ -143,6 +1326,10 @@ impl<'a> Resolver<'a> { // and then we can resolve the expression! resolver.resolve(expression)?; + + // specifically save the original `sig`, if it was present, + // because that's what we're using for local names. + f.ty.inline = inline; } Ok(()) } @@ -150,21 +1337,21 @@ impl<'a> Resolver<'a> { ModuleField::Elem(e) => { match &mut e.kind { ElemKind::Active { table, offset } => { - self.resolve_idx(table, Ns::Table)?; - self.resolve_expr(e.span, offset)?; + self.resolve(table, Ns::Table)?; + self.resolve_expr(offset)?; } ElemKind::Passive { .. } | ElemKind::Declared { .. } => {} } match &mut e.payload { ElemPayload::Indices(elems) => { for idx in elems { - self.resolve_idx(idx, Ns::Func)?; + self.resolve(idx, Ns::Func)?; } } ElemPayload::Exprs { exprs, .. } => { for funcref in exprs { if let Some(idx) = funcref { - self.resolve_idx(idx, Ns::Func)?; + self.resolve(idx, Ns::Func)?; } } } @@ -174,34 +1361,63 @@ impl<'a> Resolver<'a> { ModuleField::Data(d) => { if let DataKind::Active { memory, offset } = &mut d.kind { - self.resolve_idx(memory, Ns::Memory)?; - self.resolve_expr(d.span, offset)?; + self.resolve(memory, Ns::Memory)?; + self.resolve_expr(offset)?; } Ok(()) } ModuleField::Start(i) => { - self.resolve_idx(i, Ns::Func)?; + self.resolve(i, Ns::Func)?; Ok(()) } - ModuleField::Export(e) => match &mut e.kind { - ExportKind::Func(f) => self.resolve_idx(f, Ns::Func), - ExportKind::Memory(f) => self.resolve_idx(f, Ns::Memory), - ExportKind::Global(f) => self.resolve_idx(f, Ns::Global), - ExportKind::Table(f) => self.resolve_idx(f, Ns::Table), - ExportKind::Event(f) => self.resolve_idx(f, Ns::Event), - }, + ModuleField::Export(e) => { + let (idx, ns) = Ns::from_export_mut(&mut e.kind); + self.resolve(idx, ns)?; + Ok(()) + } + + ModuleField::ExportAll(..) => unreachable!(), ModuleField::Global(g) => { self.resolve_valtype(&mut g.ty.ty)?; if let GlobalKind::Inline(expr) = &mut g.kind { - self.resolve_expr(g.span, expr)?; + self.resolve_expr(expr)?; } Ok(()) } - ModuleField::Event(e) => self.resolve_event_type(e.span, &mut e.ty), + ModuleField::Event(e) => { + match &mut e.ty { + EventType::Exception(ty) => { + self.resolve_type_use(ty)?; + } + } + Ok(()) + } + + ModuleField::Instance(i) => { + if let InstanceKind::Inline { module, items } = &mut i.kind { + self.resolve(module, Ns::Module)?; + for item in items { + let (idx, ns) = Ns::from_export_mut(item); + self.resolve(idx, ns)?; + } + } + Ok(()) + } + + ModuleField::NestedModule(i) => { + if let NestedModuleKind::Inline { ty, .. } = &mut i.kind { + let ty = ty.as_mut().expect("type index should be filled in"); + self.resolve(ty, Ns::Type)?; + } + Ok(()) + } + + // Everything about aliases is handled in `expand` above + ModuleField::Alias(_) => Ok(()), ModuleField::Table(_) | ModuleField::Memory(_) | ModuleField::Custom(_) => Ok(()), } @@ -211,9 +1427,7 @@ impl<'a> Resolver<'a> { match ty { ValType::Ref(ty) => self.resolve_reftype(ty)?, ValType::Rtt(i) => { - self.ns(Ns::Type) - .resolve(i) - .map_err(|id| self.resolve_error(id, "type"))?; + self.resolve(i, Ns::Type)?; } _ => {} } @@ -223,116 +1437,103 @@ impl<'a> Resolver<'a> { fn resolve_reftype(&self, ty: &mut RefType<'a>) -> Result<(), Error> { match ty { RefType::Type(i) | RefType::OptType(i) => { - self.ns(Ns::Type) - .resolve(i) - .map_err(|id| self.resolve_error(id, "type"))?; + self.resolve(i, Ns::Type)?; } - _ => {} + RefType::Eq | RefType::Func | RefType::Extern | RefType::Exn | RefType::I31 => {} } Ok(()) } - fn resolve_type(&self, ty: &mut Type<'a>) -> Result<(), Error> { - match &mut ty.def { - TypeDef::Func(func) => self.resolve_function_type(func), - TypeDef::Struct(r#struct) => self.resolve_struct_type(r#struct), - TypeDef::Array(array) => self.resolve_array_type(array), - } - } - - fn resolve_function_type(&self, func: &mut FunctionType<'a>) -> Result<(), Error> { - for param in &mut func.params { - self.resolve_valtype(&mut param.2)?; - } - for result in &mut func.results { - self.resolve_valtype(result)?; - } - Ok(()) - } - - fn resolve_struct_type(&self, r#struct: &mut StructType<'a>) -> Result<(), Error> { - for field in &mut r#struct.fields { - self.resolve_valtype(&mut field.ty)?; + fn resolve_item_sig(&self, item: &mut ItemSig<'a>) -> Result<(), Error> { + match &mut item.kind { + ItemKind::Func(t) | ItemKind::Event(EventType::Exception(t)) => { + self.resolve_type_use(t)?; + } + ItemKind::Global(t) => self.resolve_valtype(&mut t.ty)?, + ItemKind::Instance(t) => { + self.resolve_type_use(t)?; + } + ItemKind::Module(m) => { + self.resolve_type_use(m)?; + } + ItemKind::Table(_) | ItemKind::Memory(_) => {} } Ok(()) } - fn resolve_array_type(&self, array: &mut ArrayType<'a>) -> Result<(), Error> { - self.resolve_valtype(&mut array.ty)?; - Ok(()) - } - - fn resolve_type_use(&self, span: Span, ty: &mut TypeUse<'a>) -> Result { - assert!(ty.index.is_some()); - let idx = self - .ns(Ns::Type) - .resolve(ty.index.as_mut().unwrap()) - .map_err(|id| self.resolve_error(id, "type"))?; + fn resolve_type_use<'b, T>( + &self, + ty: &'b mut TypeUse<'a, T>, + ) -> Result<(&'b Index<'a>, Option), Error> + where + T: TypeReference<'a>, + { + let idx = ty.index.as_mut().unwrap(); + self.resolve(idx, Ns::Type)?; // If the type was listed inline *and* it was specified via a type index // we need to assert they're the same. - let expected = match self.func_tys.get(&(idx as u32)) { - Some(ty) => ty, - None => return Ok(idx), - }; - if ty.func_ty.params.len() > 0 || ty.func_ty.results.len() > 0 { - // The function type for this type use hasn't been resolved, - // but neither has the expected function type, so this comparison - // is valid. - let params_not_equal = expected.params.iter().map(|t| t.2).ne(ty - .func_ty - .params - .iter() - .map(|t| t.2)); - if params_not_equal || expected.results != ty.func_ty.results { - let span = ty.index_span.unwrap_or(span); - return Err(Error::new( - span, - format!("inline function type doesn't match type reference"), - )); - } - } else { - ty.func_ty.params = expected.params.clone(); - ty.func_ty.results = expected.results.clone(); + // + // Note that we resolve the type first to transform all names to + // indices to ensure that all the indices line up. + if let Some(inline) = &mut ty.inline { + inline.resolve(self)?; + inline.check_matches(idx, self)?; } - // Resolve the (ref T) value types in the final function type - self.resolve_function_type(&mut ty.func_ty)?; - - Ok(idx) + Ok((idx, ty.inline.take())) } - fn resolve_expr(&self, span: Span, expr: &mut Expression<'a>) -> Result<(), Error> { - ExprResolver::new(self, span).resolve(expr) + fn resolve_expr(&self, expr: &mut Expression<'a>) -> Result<(), Error> { + ExprResolver::new(self).resolve(expr) } - fn resolve_event_type(&self, span: Span, ty: &mut EventType<'a>) -> Result<(), Error> { - match ty { - EventType::Exception(ty) => { - self.resolve_type_use(span, ty)?; - } + pub fn resolve(&self, idx: &mut Index<'a>, ns: Ns) -> Result { + match ns { + Ns::Func => self.funcs.resolve(idx, "func"), + Ns::Table => self.tables.resolve(idx, "table"), + Ns::Global => self.globals.resolve(idx, "global"), + Ns::Memory => self.memories.resolve(idx, "memory"), + Ns::Instance => self.instances.resolve(idx, "instance"), + Ns::Module => self.modules.resolve(idx, "module"), + Ns::Event => self.events.resolve(idx, "event"), + Ns::Type => self.types.resolve(idx, "type"), } - Ok(()) } +} - pub fn resolve_idx(&self, idx: &mut Index<'a>, ns: Ns) -> Result<(), Error> { - match self.ns(ns).resolve(idx) { - Ok(_n) => Ok(()), - Err(id) => Err(self.resolve_error(id, ns.desc())), - } - } +pub struct Namespace<'a, T> { + names: HashMap, u32>, + count: u32, + next_item: usize, + items: Vec>, +} - fn resolve_error(&self, id: Id<'a>, ns: &str) -> Error { - Error::new( - id.span(), - format!("failed to find {} named `${}`", ns, id.name()), - ) - } +enum Sig<'a, T> { + Defined { item: T, module: usize }, + Alias(SigAlias<'a>), + Unknown, } -impl<'a> Namespace<'a> { +enum SigAlias<'a> { + TypeExport { + module: usize, + ty: Index<'a>, + export_idx: u32, + }, + ModuleExport { + module: usize, + export_idx: u32, + }, + Item { + module: usize, + idx: Index<'a>, + }, +} + +impl<'a, T> Namespace<'a, T> { fn register(&mut self, name: Option>, desc: &str) -> Result { - let index = self.count; + let index = self.alloc(); if let Some(name) = name { if let Some(_prev) = self.names.insert(name, index) { // FIXME: temporarily allow duplicately-named data and element @@ -363,10 +1564,16 @@ impl<'a> Namespace<'a> { } } } - self.count += 1; Ok(index) } + fn alloc(&mut self) -> u32 { + let index = self.count; + self.items.push(Sig::Unknown); + self.count += 1; + return index; + } + fn register_specific(&mut self, name: Id<'a>, index: u32, desc: &str) -> Result<(), Error> { if let Some(_prev) = self.names.insert(name, index) { return Err(Error::new( @@ -377,33 +1584,72 @@ impl<'a> Namespace<'a> { Ok(()) } - fn resolve(&self, idx: &mut Index<'a>) -> Result> { + fn resolve(&self, idx: &mut Index<'a>, desc: &str) -> Result { let id = match idx { - Index::Num(n) => return Ok(*n), + Index::Num(n, _) => return Ok(*n), Index::Id(id) => id, }; if let Some(&n) = self.names.get(id) { - *idx = Index::Num(n); + *idx = Index::Num(n, id.span()); return Ok(n); } - Err(*id) + Err(resolve_error(*id, desc)) + } + + fn push_item(&mut self, module: usize, item: T) { + self.push(Sig::Defined { item, module }); + } + + fn push(&mut self, item: Sig<'a, T>) { + match self.items[self.next_item] { + Sig::Unknown => {} + _ => panic!("item already known"), + } + self.items[self.next_item] = item; + self.next_item += 1; + } +} + +impl Sig<'_, T> { + fn item(&self) -> Option<&T> { + match self { + Sig::Defined { item, .. } => Some(item), + _ => None, + } + } +} + +fn resolve_error(id: Id<'_>, ns: &str) -> Error { + assert!(!id.is_gensym()); + Error::new( + id.span(), + format!("failed to find {} named `${}`", ns, id.name()), + ) +} + +impl<'a, T> Default for Namespace<'a, T> { + fn default() -> Namespace<'a, T> { + Namespace { + names: HashMap::new(), + count: 0, + next_item: 0, + items: Vec::new(), + } } } struct ExprResolver<'a, 'b> { - resolver: &'b Resolver<'a>, - locals: Namespace<'a>, + module: &'b Module<'a>, + locals: Namespace<'a, ()>, labels: Vec>>, - span: Span, } impl<'a, 'b> ExprResolver<'a, 'b> { - fn new(resolver: &'b Resolver<'a>, span: Span) -> ExprResolver<'a, 'b> { + fn new(module: &'b Module<'a>) -> ExprResolver<'a, 'b> { ExprResolver { - resolver, + module, locals: Default::default(), labels: Vec::new(), - span, } } @@ -418,41 +1664,50 @@ impl<'a, 'b> ExprResolver<'a, 'b> { use crate::ast::Instruction::*; match instr { - MemoryInit(i) => self.resolver.resolve_idx(&mut i.data, Ns::Data), - DataDrop(i) => self.resolver.resolve_idx(i, Ns::Data), + MemoryInit(i) => { + self.module.datas.resolve(&mut i.data, "data")?; + } + DataDrop(i) => { + self.module.datas.resolve(i, "data")?; + } TableInit(i) => { - self.resolver.resolve_idx(&mut i.elem, Ns::Elem)?; - self.resolver.resolve_idx(&mut i.table, Ns::Table) + self.module.elems.resolve(&mut i.elem, "elem")?; + self.module.resolve(&mut i.table, Ns::Table)?; + } + ElemDrop(i) => { + self.module.elems.resolve(i, "elem")?; } - ElemDrop(i) => self.resolver.resolve_idx(i, Ns::Elem), TableCopy(i) => { - self.resolver.resolve_idx(&mut i.dst, Ns::Table)?; - self.resolver.resolve_idx(&mut i.src, Ns::Table) + self.module.resolve(&mut i.dst, Ns::Table)?; + self.module.resolve(&mut i.src, Ns::Table)?; } TableFill(i) | TableSet(i) | TableGet(i) | TableSize(i) | TableGrow(i) => { - self.resolver.resolve_idx(&mut i.dst, Ns::Table) + self.module.resolve(&mut i.dst, Ns::Table)?; } - GlobalSet(i) | GlobalGet(i) => self.resolver.resolve_idx(i, Ns::Global), + GlobalSet(i) | GlobalGet(i) => { + self.module.resolve(i, Ns::Global)?; + } - LocalSet(i) | LocalGet(i) | LocalTee(i) => self - .locals - .resolve(i) - .map(|_| ()) - .map_err(|id| self.resolver.resolve_error(id, "local")), + LocalSet(i) | LocalGet(i) | LocalTee(i) => { + self.locals.resolve(i, "local")?; + } - Call(i) | RefFunc(i) | ReturnCall(i) => self.resolver.resolve_idx(i, Ns::Func), + Call(i) | RefFunc(i) | ReturnCall(i) => { + self.module.resolve(i, Ns::Func)?; + } CallIndirect(c) | ReturnCallIndirect(c) => { - self.resolver.resolve_idx(&mut c.table, Ns::Table)?; - self.resolver.resolve_type_use(self.span, &mut c.ty)?; - Ok(()) + self.module.resolve(&mut c.table, Ns::Table)?; + self.module.resolve_type_use(&mut c.ty)?; } - FuncBind(i) => self.resolver.resolve_idx(i, Ns::Type), + FuncBind(i) => { + self.module.resolve(i, Ns::Type)?; + } Block(bt) | If(bt) | Loop(bt) | Try(bt) => { self.labels.push(bt.label); @@ -483,22 +1738,34 @@ impl<'a, 'b> ExprResolver<'a, 'b> { // out `params` and `results` if we can, otherwise no work // happens. if bt.ty.index.is_some() { - let ty = self.resolver.resolve_type_use(self.span, &mut bt.ty)?; - let ty = match self.resolver.func_tys.get(&(ty as u32)) { - Some(ty) => ty, - None => return Ok(()), + let (ty, _) = self.module.resolve_type_use(&mut bt.ty)?; + let n = match ty { + Index::Num(n, _) => *n, + Index::Id(_) => panic!("expected `Num`"), + }; + let ty = match self + .module + .types + .items + .get(n as usize) + .and_then(|s| s.item()) + { + Some(TypeInfo::Func(ty)) => ty, + _ => return Ok(()), }; - if ty.params.len() == 0 && ty.results.len() <= 1 { - bt.ty.func_ty.params.truncate(0); - bt.ty.func_ty.results = ty.results.clone(); + if ty.0.len() == 0 && ty.1.len() <= 1 { + let mut inline = FunctionType::default(); + inline.results = ty.1.clone(); + bt.ty.inline = Some(inline); bt.ty.index = None; } } - // Resolve (ref T) params and results - self.resolver.resolve_function_type(&mut bt.ty.func_ty)?; - - Ok(()) + // If the inline annotation persists to this point then resolve + // all of its inline value types. + if let Some(inline) = &mut bt.ty.inline { + inline.resolve(self.module)?; + } } // On `End` instructions we pop a label from the stack, and for both @@ -527,51 +1794,58 @@ impl<'a, 'b> ExprResolver<'a, 'b> { )); } - Br(i) | BrIf(i) | BrOnCast(i) | BrOnNull(i) => self.resolve_label(i), + Br(i) | BrIf(i) | BrOnCast(i) | BrOnNull(i) => { + self.resolve_label(i)?; + } BrTable(i) => { for label in i.labels.iter_mut() { self.resolve_label(label)?; } - self.resolve_label(&mut i.default) + self.resolve_label(&mut i.default)?; } - Throw(i) => self.resolver.resolve_idx(i, Ns::Event), + Throw(i) => { + self.module.resolve(i, Ns::Event)?; + } BrOnExn(b) => { self.resolve_label(&mut b.label)?; - self.resolver.resolve_idx(&mut b.exn, Ns::Event) + self.module.resolve(&mut b.exn, Ns::Event)?; } Select(s) => { if let Some(list) = &mut s.tys { for ty in list { - self.resolver.resolve_valtype(ty)?; + self.module.resolve_valtype(ty)?; } } - Ok(()) } StructNew(i) | StructNewSub(i) | StructNewDefault(i) | ArrayNew(i) | ArrayNewSub(i) | ArrayNewDefault(i) | ArrayGet(i) | ArrayGetS(i) | ArrayGetU(i) | ArraySet(i) - | ArrayLen(i) | RTTGet(i) | RTTSub(i) => self.resolver.resolve_idx(i, Ns::Type), + | ArrayLen(i) | RTTGet(i) | RTTSub(i) => { + self.module.resolve(i, Ns::Type)?; + } + StructSet(s) | StructGet(s) | StructGetS(s) | StructGetU(s) => { - self.resolver.resolve_idx(&mut s.r#struct, Ns::Type)?; - self.resolver.resolve_idx(&mut s.field, Ns::Field) + self.module.resolve(&mut s.r#struct, Ns::Type)?; + self.module.fields.resolve(&mut s.field, "field")?; } StructNarrow(s) => { - self.resolver.resolve_valtype(&mut s.from)?; - self.resolver.resolve_valtype(&mut s.to) + self.module.resolve_valtype(&mut s.from)?; + self.module.resolve_valtype(&mut s.to)?; } - RefNull(ty) => self.resolver.resolve_reftype(ty), + RefNull(ty) => self.module.resolve_reftype(ty)?, - _ => Ok(()), + _ => {} } + Ok(()) } fn resolve_label(&self, label: &mut Index<'a>) -> Result<(), Error> { let id = match label { - Index::Num(_) => return Ok(()), + Index::Num(..) => return Ok(()), Index::Id(id) => *id, }; let idx = self @@ -583,10 +1857,397 @@ impl<'a, 'b> ExprResolver<'a, 'b> { .find(|(_, l)| *l == id); match idx { Some((idx, _)) => { - *label = Index::Num(idx as u32); + *label = Index::Num(idx as u32, id.span()); Ok(()) } - None => Err(self.resolver.resolve_error(id, "label")), + None => Err(resolve_error(id, "label")), + } + } +} + +enum TypeInfo<'a> { + Func(FuncKey<'a>), + Module { + key: ModuleKey<'a>, + info: Module<'a>, + }, + Instance { + key: InstanceKey<'a>, + info: Module<'a>, + }, + Other, +} + +trait TypeReference<'a>: Default { + type Key: TypeKey<'a>; + fn key(&self) -> Self::Key; + fn expand(&mut self, cx: &mut Module<'a>); + fn check_matches(&mut self, idx: &Index<'a>, cx: &Module<'a>) -> Result<(), Error>; + fn resolve(&mut self, cx: &Module<'a>) -> Result<(), Error>; +} + +trait TypeKey<'a> { + fn lookup(&self, cx: &Module<'a>) -> Option>; + fn to_def(&self, span: Span) -> TypeDef<'a>; + fn insert(&self, cx: &mut Module<'a>, id: Index<'a>); + fn into_info(self, span: Span, cur: usize) -> TypeInfo<'a>; +} + +type FuncKey<'a> = (Vec>, Vec>); + +impl<'a> TypeReference<'a> for FunctionType<'a> { + type Key = FuncKey<'a>; + + fn key(&self) -> Self::Key { + let params = self.params.iter().map(|p| p.2).collect::>(); + let results = self.results.clone(); + (params, results) + } + + fn expand(&mut self, _cx: &mut Module<'a>) {} + + fn check_matches(&mut self, idx: &Index<'a>, cx: &Module<'a>) -> Result<(), Error> { + let n = match idx { + Index::Num(n, _) => *n, + Index::Id(_) => panic!("expected `Num`"), + }; + let (params, results) = match cx.types.items.get(n as usize).and_then(|i| i.item()) { + Some(TypeInfo::Func(ty)) => ty, + _ => return Ok(()), + }; + + // Here we need to check that the inline type listed (ourselves) matches + // what was listed in the module itself (the `params` and `results` + // above). The listed values in `types` are not resolved yet, although + // we should be resolved. In any case we do name resolution + // opportunistically here to see if the values are equal. + + let types_not_equal = |a: &ValType, b: &ValType| { + let mut a = a.clone(); + let mut b = b.clone(); + drop(cx.resolve_valtype(&mut a)); + drop(cx.resolve_valtype(&mut b)); + a != b + }; + + let not_equal = params.len() != self.params.len() + || results.len() != self.results.len() + || params + .iter() + .zip(self.params.iter()) + .any(|(a, (_, _, b))| types_not_equal(a, b)) + || results + .iter() + .zip(self.results.iter()) + .any(|(a, b)| types_not_equal(a, b)); + if not_equal { + return Err(Error::new( + idx.span(), + format!("inline function type doesn't match type reference"), + )); + } + + Ok(()) + } + + fn resolve(&mut self, cx: &Module<'a>) -> Result<(), Error> { + // Resolve the (ref T) value types in the final function type + for param in &mut self.params { + cx.resolve_valtype(&mut param.2)?; + } + for result in &mut self.results { + cx.resolve_valtype(result)?; + } + Ok(()) + } +} + +impl<'a> TypeKey<'a> for FuncKey<'a> { + fn lookup(&self, cx: &Module<'a>) -> Option> { + cx.func_type_to_idx.get(self).cloned() + } + + fn to_def(&self, _span: Span) -> TypeDef<'a> { + TypeDef::Func(FunctionType { + params: self.0.iter().map(|t| (None, None, *t)).collect(), + results: self.1.clone(), + }) + } + + fn insert(&self, cx: &mut Module<'a>, idx: Index<'a>) { + cx.func_type_to_idx.entry(self.clone()).or_insert(idx); + } + + fn into_info(self, _span: Span, _cur: usize) -> TypeInfo<'a> { + TypeInfo::Func(self) + } +} + +// A list of the exports of a module as well as the signature they export. +type InstanceKey<'a> = Vec<(&'a str, Item<'a>)>; + +impl<'a> TypeReference<'a> for InstanceType<'a> { + type Key = InstanceKey<'a>; + + fn key(&self) -> Self::Key { + self.exports + .iter() + .map(|export| (export.name, Item::new(&export.item))) + .collect() + } + + fn expand(&mut self, cx: &mut Module<'a>) { + for export in self.exports.iter_mut() { + cx.expand_item_sig(&mut export.item, true); + } + } + + fn check_matches(&mut self, idx: &Index<'a>, cx: &Module<'a>) -> Result<(), Error> { + drop(cx); + Err(Error::new( + idx.span(), + format!("cannot specify instance type as a reference and inline"), + )) + } + + fn resolve(&mut self, cx: &Module<'a>) -> Result<(), Error> { + for export in self.exports.iter_mut() { + cx.resolve_item_sig(&mut export.item)?; + } + Ok(()) + } +} + +impl<'a> TypeKey<'a> for InstanceKey<'a> { + fn lookup(&self, cx: &Module<'a>) -> Option> { + cx.instance_type_to_idx.get(self).cloned() + } + + fn to_def(&self, span: Span) -> TypeDef<'a> { + let exports = self + .iter() + .map(|(name, item)| ExportType { + span, + name, + item: item.to_sig(span), + }) + .collect(); + TypeDef::Instance(InstanceType { exports }) + } + + fn insert(&self, cx: &mut Module<'a>, idx: Index<'a>) { + cx.instance_type_to_idx.entry(self.clone()).or_insert(idx); + } + + fn into_info(self, span: Span, cur: usize) -> TypeInfo<'a> { + let exports = self + .iter() + .map(|(name, item)| ExportType { + span, + name, + item: item.to_sig(span), + }) + .collect::>(); + TypeInfo::Instance { + key: self, + // if exports information fails to get generated for duplicate + // information we just don't make any names available for + // resolution to have errors generated elsewhere. + info: Module::from_exports(span, cur, &exports).unwrap_or_default(), + } + } +} + +// The first element of this pair is the list of imports in the module, and the +// second element is the list of exports. +type ModuleKey<'a> = ( + Vec<(&'a str, Option<&'a str>, Item<'a>)>, + Vec<(&'a str, Item<'a>)>, +); + +impl<'a> TypeReference<'a> for ModuleType<'a> { + type Key = ModuleKey<'a>; + + fn key(&self) -> Self::Key { + assert!(self.instance_exports.is_empty()); + let imports = self + .imports + .iter() + .map(|import| (import.module, import.field, Item::new(&import.item))) + .collect(); + let exports = self + .exports + .iter() + .map(|export| (export.name, Item::new(&export.item))) + .collect(); + (imports, exports) + } + + fn expand(&mut self, cx: &mut Module<'a>) { + assert!(self.instance_exports.is_empty()); + for export in self.exports.iter_mut() { + cx.expand_item_sig(&mut export.item, true); + } + for import in self.imports.iter_mut() { + cx.expand_item_sig(&mut import.item, true); + } + } + + fn check_matches(&mut self, idx: &Index<'a>, cx: &Module<'a>) -> Result<(), Error> { + drop(cx); + Err(Error::new( + idx.span(), + format!("cannot specify module type as a reference and inline"), + )) + } + + fn resolve(&mut self, cx: &Module<'a>) -> Result<(), Error> { + assert!(self.instance_exports.is_empty()); + for i in self.imports.iter_mut() { + cx.resolve_item_sig(&mut i.item)?; + } + for e in self.exports.iter_mut() { + cx.resolve_item_sig(&mut e.item)?; + } + Ok(()) + } +} + +impl<'a> TypeKey<'a> for ModuleKey<'a> { + fn lookup(&self, cx: &Module<'a>) -> Option> { + cx.module_type_to_idx.get(self).cloned() + } + + fn to_def(&self, span: Span) -> TypeDef<'a> { + let imports = self + .0 + .iter() + .map(|(module, field, item)| Import { + span, + module, + field: *field, + item: item.to_sig(span), + }) + .collect(); + let exports = self + .1 + .iter() + .map(|(name, item)| ExportType { + span, + name, + item: item.to_sig(span), + }) + .collect(); + TypeDef::Module(ModuleType { + imports, + exports, + instance_exports: Vec::new(), + }) + } + + fn insert(&self, cx: &mut Module<'a>, idx: Index<'a>) { + cx.module_type_to_idx.entry(self.clone()).or_insert(idx); + } + + fn into_info(self, span: Span, cur: usize) -> TypeInfo<'a> { + let exports = self + .1 + .iter() + .map(|(name, item)| ExportType { + span, + name, + item: item.to_sig(span), + }) + .collect::>(); + TypeInfo::Module { + key: self, + // see above comment for why `unwrap_or_default` is used here. + info: Module::from_exports(span, cur, &exports).unwrap_or_default(), + } + } +} + +// A lookalike to `ItemKind` except without all non-relevant information for +// hashing. This is used as a hash key for instance/module type lookup. +#[derive(Clone, PartialEq, Eq, Hash)] +enum Item<'a> { + Func(Index<'a>), + Table(TableType), + Memory(MemoryType), + Global(GlobalType<'a>), + Event(Index<'a>), + Module(Index<'a>), + Instance(Index<'a>), +} + +impl<'a> Item<'a> { + fn new(item: &ItemSig<'a>) -> Item<'a> { + match &item.kind { + ItemKind::Func(f) => Item::Func(f.index.expect("should have index")), + ItemKind::Instance(f) => Item::Instance(f.index.expect("should have index")), + ItemKind::Module(f) => Item::Module(f.index.expect("should have index")), + ItemKind::Event(EventType::Exception(f)) => { + Item::Event(f.index.expect("should have index")) + } + ItemKind::Table(t) => Item::Table(t.clone()), + ItemKind::Memory(t) => Item::Memory(t.clone()), + ItemKind::Global(t) => Item::Global(t.clone()), + } + } + + fn to_sig(&self, span: Span) -> ItemSig<'a> { + let kind = match self { + Item::Func(index) => ItemKind::Func(TypeUse::new_with_index(*index)), + Item::Event(index) => { + ItemKind::Event(EventType::Exception(TypeUse::new_with_index(*index))) + } + Item::Instance(index) => ItemKind::Instance(TypeUse::new_with_index(*index)), + Item::Module(index) => ItemKind::Module(TypeUse::new_with_index(*index)), + Item::Table(t) => ItemKind::Table(t.clone()), + Item::Memory(t) => ItemKind::Memory(t.clone()), + Item::Global(t) => ItemKind::Global(t.clone()), + }; + ItemSig { + span, + id: None, + name: None, + kind, + } + } +} + +#[derive(Default)] +struct ExportNamespace<'a> { + names: HashMap<(Ns, Id<'a>), u32>, + num: u32, +} + +impl<'a> ExportNamespace<'a> { + fn from_exports(exports: &[ExportType<'a>]) -> Result, Error> { + let mut ret = ExportNamespace::default(); + for export in exports { + ret.push(Ns::from_item(&export.item), export.item.id)?; + } + Ok(ret) + } + + fn push(&mut self, ns: Ns, id: Option>) -> Result<(), Error> { + if let Some(id) = id { + self.names.insert((ns, id), self.num); + } + self.num += 1; + Ok(()) + } + + fn resolve(&self, idx: &mut Index<'a>, ns: Ns) -> Result { + let id = match idx { + Index::Num(n, _) => return Ok(*n), + Index::Id(id) => id, + }; + if let Some(&n) = self.names.get(&(ns, *id)) { + *idx = Index::Num(n, id.span()); + return Ok(n); } + Err(resolve_error(*id, ns.desc())) } } diff --git a/crates/wast/src/resolve/tyexpand.rs b/crates/wast/src/resolve/tyexpand.rs deleted file mode 100644 index 30bdf2e8f4..0000000000 --- a/crates/wast/src/resolve/tyexpand.rs +++ /dev/null @@ -1,132 +0,0 @@ -use crate::ast::*; -use std::collections::HashMap; - -#[derive(Default)] -pub struct Expander<'a> { - pub to_prepend: Vec>, - func_types: HashMap<(Vec>, Vec>), u32>, - ntypes: u32, -} - -impl<'a> Expander<'a> { - pub fn expand(&mut self, item: &mut ModuleField<'a>) { - match item { - ModuleField::Type(t) => self.register_type(t), - ModuleField::Import(i) => self.expand_import(i), - ModuleField::Func(f) => self.expand_func(f), - ModuleField::Global(g) => self.expand_global(g), - ModuleField::Data(d) => self.expand_data(d), - ModuleField::Elem(e) => self.expand_elem(e), - ModuleField::Event(e) => self.expand_event(e), - _ => {} - } - } - - fn register_type(&mut self, ty: &Type<'a>) { - if let TypeDef::Func(func) = &ty.def { - let key = self.key(func); - if !self.func_types.contains_key(&key) { - self.func_types.insert(key, self.ntypes); - } - } - self.ntypes += 1; - } - - fn expand_import(&mut self, import: &mut Import<'a>) { - match &mut import.kind { - ImportKind::Func(t) | ImportKind::Event(EventType::Exception(t)) => { - self.expand_type_use(t) - } - _ => {} - } - } - - fn expand_func(&mut self, func: &mut Func<'a>) { - self.expand_type_use(&mut func.ty); - if let FuncKind::Inline { expression, .. } = &mut func.kind { - self.expand_expression(expression); - } - } - - fn expand_global(&mut self, global: &mut Global<'a>) { - if let GlobalKind::Inline(expr) = &mut global.kind { - self.expand_expression(expr); - } - } - - fn expand_elem(&mut self, elem: &mut Elem<'a>) { - if let ElemKind::Active { offset, .. } = &mut elem.kind { - self.expand_expression(offset); - } - } - - fn expand_event(&mut self, event: &mut Event<'a>) { - match &mut event.ty { - EventType::Exception(ty) => self.expand_type_use(ty), - } - } - - fn expand_data(&mut self, data: &mut Data<'a>) { - if let DataKind::Active { offset, .. } = &mut data.kind { - self.expand_expression(offset); - } - } - - fn expand_expression(&mut self, expr: &mut Expression<'a>) { - for instr in expr.instrs.iter_mut() { - self.expand_instr(instr); - } - } - - fn expand_instr(&mut self, instr: &mut Instruction<'a>) { - match instr { - Instruction::Block(bt) - | Instruction::If(bt) - | Instruction::Loop(bt) - | Instruction::Try(bt) => { - // Only actually expand `TypeUse` with an index which appends a - // type if it looks like we need one. This way if the - // multi-value proposal isn't enabled and/or used we won't - // encode it. - if bt.ty.func_ty.params.len() == 0 && bt.ty.func_ty.results.len() <= 1 { - return; - } - self.expand_type_use(&mut bt.ty) - } - Instruction::CallIndirect(c) | Instruction::ReturnCallIndirect(c) => { - self.expand_type_use(&mut c.ty) - } - _ => {} - } - } - - fn expand_type_use(&mut self, item: &mut TypeUse<'a>) { - if item.index.is_some() { - return; - } - let key = self.key(&item.func_ty); - item.index = Some(Index::Num(match self.func_types.get(&key) { - Some(i) => *i, - None => self.prepend(key), - })); - } - - fn key(&self, ty: &FunctionType<'a>) -> (Vec>, Vec>) { - let params = ty.params.iter().map(|p| p.2).collect::>(); - let results = ty.results.clone(); - (params, results) - } - - fn prepend(&mut self, key: (Vec>, Vec>)) -> u32 { - self.to_prepend.push(ModuleField::Type(Type { - id: None, - def: TypeDef::Func(FunctionType { - params: key.0.iter().map(|t| (None, None, *t)).collect(), - results: key.1.clone(), - }), - })); - self.func_types.insert(key, self.ntypes); - self.ntypes += 1; - return self.ntypes - 1; - } -} diff --git a/src/bin/wasm-validate-rs.rs b/src/bin/wasm-validate-rs.rs index 7a75034807..a894239242 100644 --- a/src/bin/wasm-validate-rs.rs +++ b/src/bin/wasm-validate-rs.rs @@ -19,6 +19,11 @@ fn main() -> Result<()> { ); opts.optflag("", "enable-multi-value", "Enable wasm multi-value feature"); opts.optflag("", "enable-tail-call", "Enable wasm tail-call feature"); + opts.optflag( + "", + "enable-module-linking", + "Enable wasm module-linking feature", + ); #[cfg(feature = "deterministic")] opts.optflag( "", @@ -47,6 +52,7 @@ fn main() -> Result<()> { enable_bulk_memory: matches.opt_present("enable-bulk-memory"), enable_multi_value: matches.opt_present("enable-multi-value"), enable_tail_call: matches.opt_present("enable-tail-call"), + enable_module_linking: matches.opt_present("enable-module-linking"), #[cfg(feature = "deterministic")] deterministic_only: matches.opt_present("deterministic-only"), }, diff --git a/tests/dump.rs b/tests/dump.rs index 0e922ce32f..635800dd10 100644 --- a/tests/dump.rs +++ b/tests/dump.rs @@ -120,6 +120,7 @@ struct Dump<'a> { cur: usize, state: String, dst: String, + nesting: u32, } const NBYTES: usize = 4; @@ -129,20 +130,30 @@ impl<'a> Dump<'a> { Dump { bytes, cur: 0, + nesting: 0, state: String::new(), dst: String::new(), } } fn run(&mut self) -> Result<()> { - let mut parser = ModuleReader::new(self.bytes)?; + self.print_module(ModuleReader::new(self.bytes)?)?; + assert_eq!(self.cur, self.bytes.len()); + Ok(()) + } + + fn print_module(&mut self, mut parser: ModuleReader<'a>) -> Result<()> { + self.nesting += 1; write!(self.state, "version {}", parser.get_version())?; - self.print(parser.current_position())?; + self.print(parser.original_position())?; let mut funcs = 0; let mut globals = 0; let mut tables = 0; let mut memories = 0; + let mut modules = 0; + let mut instances = 0; + let mut types = 0; while !parser.eof() { let section = parser.read()?; @@ -151,7 +162,8 @@ impl<'a> Dump<'a> { match section.code { SectionCode::Type => { self.print_iter(section.get_type_section_reader()?, |me, end, i| { - write!(me.state, "type {:?}", i)?; + write!(me.state, "[type {}] {:?}", types, i)?; + types += 1; me.print(end) })? } @@ -175,6 +187,14 @@ impl<'a> Dump<'a> { write!(me.state, "[global {}]", globals)?; globals += 1; } + ImportSectionEntryType::Instance(_) => { + write!(me.state, "[instance {}]", instances)?; + instances += 1; + } + ImportSectionEntryType::Module(_) => { + write!(me.state, "[module {}]", modules)?; + modules += 1; + } } write!(me.state, " {:?}", i)?; me.print(end) @@ -216,6 +236,48 @@ impl<'a> Dump<'a> { me.print_ops(i.init_expr.get_operators_reader()) })? } + SectionCode::Alias => { + self.print_iter(section.get_alias_section_reader()?, |me, end, i| { + write!(me.state, "[alias] {:?}", i)?; + match i.kind { + ExternalKind::Function => funcs += 1, + ExternalKind::Global => globals += 1, + ExternalKind::Module => modules += 1, + ExternalKind::Table => tables += 1, + ExternalKind::Instance => instances += 1, + ExternalKind::Memory => memories += 1, + ExternalKind::Type => types += 1, + } + me.print(end) + })? + } + SectionCode::Module => { + let mut cnt = 0; + self.print_iter(section.get_module_section_reader()?, |me, end, i| { + write!(me.state, "[module {}] type {:?}", cnt + modules, i)?; + cnt += 1; + me.print(end) + })? + } + SectionCode::Instance => { + self.print_iter(section.get_instance_section_reader()?, |me, _end, i| { + write!(me.state, "[instance {}] module:{}", instances, i.module())?; + me.print(i.original_position())?; + instances += 1; + me.print_iter(i.args()?, |me, end, (kind, index)| { + write!(me.state, "[instantiate arg] {:?} {}", kind, index)?; + me.print(end) + }) + })? + } + SectionCode::ModuleCode => { + self.print_iter(section.get_module_code_section_reader()?, |me, _end, i| { + write!(me.state, "inline module")?; + me.print(i.original_position())?; + modules += 1; + me.print_module(i.module()?) + })? + } SectionCode::Start => { let start = section.get_start_section_content()?; write!(self.state, "start function {}", start)?; @@ -306,22 +368,72 @@ impl<'a> Dump<'a> { })? } - SectionCode::Custom { .. } => { - write!(self.dst, "0x{:04x} |", self.cur)?; - for _ in 0..NBYTES { - write!(self.dst, "---")?; + SectionCode::Custom { kind, name: _ } => match kind { + CustomSectionKind::Name => { + let mut iter = section.get_name_section_reader()?; + while !iter.eof() { + self.print_custom_name_section(iter.read()?, iter.original_position())?; + } } - write!( - self.dst, - "-| ... {} bytes of data\n", - section.get_binary_reader().bytes_remaining() - )?; - self.cur = section.range().end; - } + _ => { + write!(self.dst, "0x{:04x} |", self.cur)?; + for _ in 0..NBYTES { + write!(self.dst, "---")?; + } + write!( + self.dst, + "-| ... {} bytes of data\n", + section.get_binary_reader().bytes_remaining() + )?; + self.cur = section.range().end; + } + }, } } - assert_eq!(self.cur, self.bytes.len()); + self.nesting -= 1; + Ok(()) + } + + fn print_custom_name_section(&mut self, name: Name<'_>, end: usize) -> Result<()> { + match name { + Name::Module(n) => { + write!(self.state, "module name")?; + self.print(n.original_position())?; + write!(self.state, "{:?}", n.get_name()?)?; + self.print(end)?; + } + Name::Function(n) => { + write!(self.state, "function names")?; + self.print(n.original_position())?; + let mut map = n.get_map()?; + write!(self.state, "{} count", map.get_count())?; + self.print(map.original_position())?; + for _ in 0..map.get_count() { + write!(self.state, "{:?}", map.read()?)?; + self.print(map.original_position())?; + } + } + Name::Local(n) => { + write!(self.state, "local names")?; + self.print(n.original_position())?; + let mut function_map = n.get_function_local_reader()?; + write!(self.state, "{} count", function_map.get_count())?; + self.print(function_map.original_position())?; + for _ in 0..function_map.get_count() { + let function_names = function_map.read()?; + write!(self.state, "function {} locals", function_names.func_index)?; + self.print(function_names.original_position())?; + let mut map = function_names.get_map()?; + write!(self.state, "{} count", map.get_count())?; + self.print(map.original_position())?; + for _ in 0..map.get_count() { + write!(self.state, "{:?}", map.read()?)?; + self.print(map.original_position())?; + } + } + } + } Ok(()) } @@ -357,11 +469,23 @@ impl<'a> Dump<'a> { } fn print(&mut self, end: usize) -> Result<()> { - assert!(self.cur < end); + assert!( + self.cur < end, + "{:#x} >= {:#x}\n{}", + self.cur, + end, + self.dst + ); let bytes = &self.bytes[self.cur..end]; + for _ in 0..self.nesting - 1 { + write!(self.dst, " ")?; + } write!(self.dst, "0x{:04x} |", self.cur)?; for (i, chunk) in bytes.chunks(NBYTES).enumerate() { if i > 0 { + for _ in 0..self.nesting - 1 { + write!(self.dst, " ")?; + } self.dst.push_str(" |"); } for j in 0..NBYTES { diff --git a/tests/dump/alias.wat b/tests/dump/alias.wat new file mode 100644 index 0000000000..28a6194505 --- /dev/null +++ b/tests/dump/alias.wat @@ -0,0 +1,9 @@ +(module + (import "i" (instance $i + (export "f1" (func $f1)) + (export "f2" (func $f2 (param i32))) + )) + (func (export "run") + call $i.$f1 + ) +) diff --git a/tests/dump/alias.wat.dump b/tests/dump/alias.wat.dump new file mode 100644 index 0000000000..65203918d4 --- /dev/null +++ b/tests/dump/alias.wat.dump @@ -0,0 +1,36 @@ +0x0000 | 00 61 73 6d | version 1 + | 01 00 00 00 +0x0008 | 01 14 | section Type +0x000a | 03 | 3 count +0x000b | 60 00 00 | [type 0] Func(FuncType { params: [], returns: [] }) +0x000e | 60 01 7f 00 | [type 1] Func(FuncType { params: [I32], returns: [] }) +0x0012 | 62 02 02 66 | [type 2] Instance(InstanceType { exports: [ExportType { name: "f1", ty: Function(0) }, ExportType { name: "f2", ty: Function(1) }] }) + | 31 00 00 02 + | 66 32 00 01 +0x001e | 02 07 | section Import +0x0020 | 01 | 1 count +0x0021 | 01 69 01 c0 | import [instance 0] Import { module: "i", field: None, ty: Instance(2) } + | 06 02 +0x0027 | 66 05 | section Alias +0x0029 | 01 | 1 count +0x002a | 00 00 00 00 | [alias] Alias { instance: Child(0), kind: Function, index: 0 } +0x002e | 03 02 | section Function +0x0030 | 01 | 1 count +0x0031 | 00 | [func 1] type 0 +0x0032 | 07 07 | section Export +0x0034 | 01 | 1 count +0x0035 | 03 72 75 6e | export Export { field: "run", kind: Function, index: 1 } + | 00 01 +0x003b | 0a 06 | section Code +0x003d | 01 | 1 count +============== func 1 ==================== +0x003e | 04 | size of function +0x003f | 00 | 0 local blocks +0x0040 | 10 00 | Call { function_index: 0 } +0x0042 | 0b | End +0x0043 | 00 0f 04 6e | section Custom { name: "name", kind: Name } + | 61 6d 65 +0x004a | 01 08 | function names +0x004c | 01 | 1 count +0x004d | 00 05 69 2e | Naming { index: 0, name: "i.$f1" } + | 24 66 31 diff --git a/tests/dump/alias2.wat b/tests/dump/alias2.wat new file mode 100644 index 0000000000..ff3609c612 --- /dev/null +++ b/tests/dump/alias2.wat @@ -0,0 +1,28 @@ +(module + (import "" (instance $i + (export "1" (func $func)) + (export "2" (memory $memory 1)) + (export "3" (table $table 1 funcref)) + (export "4" (global $global i32)) + (export "5" (module $module)) + (export "6" (instance $instance)) + )) + + (module $m + (import "" (func)) + (import "" (memory 1)) + (import "" (global i32)) + (import "" (table 1 funcref)) + (import "" (module)) + (import "" (instance)) + ) + + (instance (instantiate $m + (func $i.$func) + (memory $i.$memory) + (global $i.$global) + (table $i.$table) + (module $i.$module) + (instance $i.$instance) + )) +) diff --git a/tests/dump/alias2.wat.dump b/tests/dump/alias2.wat.dump new file mode 100644 index 0000000000..f074c04991 --- /dev/null +++ b/tests/dump/alias2.wat.dump @@ -0,0 +1,85 @@ +0x0000 | 00 61 73 6d | version 1 + | 01 00 00 00 +0x0008 | 01 4c | section Type +0x000a | 05 | 5 count +0x000b | 60 00 00 | [type 0] Func(FuncType { params: [], returns: [] }) +0x000e | 61 00 00 | [type 1] Module(ModuleType { imports: [], exports: [] }) +0x0011 | 62 00 | [type 2] Instance(InstanceType { exports: [] }) +0x0013 | 62 06 01 31 | [type 3] Instance(InstanceType { exports: [ExportType { name: "1", ty: Function(0) }, ExportType { name: "2", ty: Memory(MemoryType { limits: ResizableLimits { initial: 1, maximum: None }, shared: false }) }, ExportType { name: "3", ty: Table(TableType { element_type: FuncRef, limits: ResizableLimits { initial: 1, maximum: None } }) }, ExportType { name: "4", ty: Global(GlobalType { content_type: I32, mutable: false }) }, ExportType { name: "5", ty: Module(1) }, ExportType { name: "6", ty: Instance(2) }] }) + | 00 00 01 32 + | 02 00 01 01 + | 33 01 70 00 + | 01 01 34 03 + | 7f 00 01 35 + | 05 01 01 36 + | 06 02 +0x0031 | 61 06 00 01 | [type 4] Module(ModuleType { imports: [Import { module: "", field: None, ty: Function(0) }, Import { module: "", field: None, ty: Memory(MemoryType { limits: ResizableLimits { initial: 1, maximum: None }, shared: false }) }, Import { module: "", field: None, ty: Global(GlobalType { content_type: I32, mutable: false }) }, Import { module: "", field: None, ty: Table(TableType { element_type: FuncRef, limits: ResizableLimits { initial: 1, maximum: None } }) }, Import { module: "", field: None, ty: Module(1) }, Import { module: "", field: None, ty: Instance(2) }], exports: [] }) + | c0 00 00 00 + | 01 c0 02 00 + | 01 00 01 c0 + | 03 7f 00 00 + | 01 c0 01 70 + | 00 01 00 01 + | c0 05 01 00 + | 01 c0 06 02 + | 00 +0x0056 | 02 06 | section Import +0x0058 | 01 | 1 count +0x0059 | 00 01 c0 06 | import [instance 0] Import { module: "", field: None, ty: Instance(3) } + | 03 +0x005e | 66 19 | section Alias +0x0060 | 06 | 6 count +0x0061 | 00 00 00 00 | [alias] Alias { instance: Child(0), kind: Function, index: 0 } +0x0065 | 00 00 02 01 | [alias] Alias { instance: Child(0), kind: Memory, index: 1 } +0x0069 | 00 00 01 02 | [alias] Alias { instance: Child(0), kind: Table, index: 2 } +0x006d | 00 00 03 03 | [alias] Alias { instance: Child(0), kind: Global, index: 3 } +0x0071 | 00 00 05 04 | [alias] Alias { instance: Child(0), kind: Module, index: 4 } +0x0075 | 00 00 06 05 | [alias] Alias { instance: Child(0), kind: Instance, index: 5 } +0x0079 | 64 02 | section Module +0x007b | 01 | 1 count +0x007c | 04 | [module 1] type 4 +0x007d | 65 0f | section Instance +0x007f | 01 | 1 count +0x0080 | 01 | [instance 2] module:1 +0x0081 | 06 | 6 count +0x0082 | 00 00 | [instantiate arg] Function 0 +0x0084 | 02 00 | [instantiate arg] Memory 0 +0x0086 | 03 00 | [instantiate arg] Global 0 +0x0088 | 01 00 | [instantiate arg] Table 0 +0x008a | 05 00 | [instantiate arg] Module 0 +0x008c | 06 01 | [instantiate arg] Instance 1 +0x008e | 67 45 | section ModuleCode +0x0090 | 01 | 1 count +0x0091 | 43 | inline module + 0x0092 | 00 61 73 6d | version 1 + | 01 00 00 00 + 0x009a | 01 09 | section Type + 0x009c | 03 | 3 count + 0x009d | 60 00 00 | [type 0] Func(FuncType { params: [], returns: [] }) + 0x00a0 | 61 00 00 | [type 1] Module(ModuleType { imports: [], exports: [] }) + 0x00a3 | 62 00 | [type 2] Instance(InstanceType { exports: [] }) + 0x00a5 | 02 23 | section Import + 0x00a7 | 06 | 6 count + 0x00a8 | 00 01 c0 00 | import [func 0] Import { module: "", field: None, ty: Function(0) } + | 00 + 0x00ad | 00 01 c0 02 | import [memory 0] Import { module: "", field: None, ty: Memory(MemoryType { limits: ResizableLimits { initial: 1, maximum: None }, shared: false }) } + | 00 01 + 0x00b3 | 00 01 c0 03 | import [global 0] Import { module: "", field: None, ty: Global(GlobalType { content_type: I32, mutable: false }) } + | 7f 00 + 0x00b9 | 00 01 c0 01 | import [table 0] Import { module: "", field: None, ty: Table(TableType { element_type: FuncRef, limits: ResizableLimits { initial: 1, maximum: None } }) } + | 70 00 01 + 0x00c0 | 00 01 c0 05 | import [module 0] Import { module: "", field: None, ty: Module(1) } + | 01 + 0x00c5 | 00 01 c0 06 | import [instance 0] Import { module: "", field: None, ty: Instance(2) } + | 02 + 0x00ca | 00 09 04 6e | section Custom { name: "name", kind: Name } + | 61 6d 65 + 0x00d1 | 00 02 | module name + 0x00d3 | 01 6d | "m" +0x00d5 | 00 11 04 6e | section Custom { name: "name", kind: Name } + | 61 6d 65 +0x00dc | 01 0a | function names +0x00de | 01 | 1 count +0x00df | 00 07 69 2e | Naming { index: 0, name: "i.$func" } + | 24 66 75 6e + | 63 diff --git a/tests/dump/bundled.wat b/tests/dump/bundled.wat new file mode 100644 index 0000000000..b70cda8e5b --- /dev/null +++ b/tests/dump/bundled.wat @@ -0,0 +1,32 @@ +(module + (type $WasiFile (instance + (export "read" (func $read (param i32 i32 i32) (result i32))) + (export "write" (func $write (param i32 i32 i32) (result i32))) + )) + (import "wasi_file" (instance $real-wasi (type $WasiFile))) + + (module $CHILD + (import "wasi_file" (instance $wasi-file (type $WasiFile))) + (func $play (export "play") + ;; TODO: implement this + ;;call $wasi-file.$read + ) + ) + + + (module $VIRTUALIZE + (import "wasi_file" (instance $wasi-file (type $WasiFile))) + (func (export "read") (param i32 i32 i32) (result i32) + i32.const 0 + ) + (func (export "write") (param i32 i32 i32) (result i32) + i32.const 0 + ) + ) + + (instance $virt-wasi (instantiate $VIRTUALIZE (instance $real-wasi))) + (instance $child (instantiate $CHILD (instance $virt-wasi))) + (func (export "work") + call $child.$play + ) +) diff --git a/tests/dump/bundled.wat.dump b/tests/dump/bundled.wat.dump new file mode 100644 index 0000000000..c6807009b6 --- /dev/null +++ b/tests/dump/bundled.wat.dump @@ -0,0 +1,152 @@ +0x0000 | 00 61 73 6d | version 1 + | 01 00 00 00 +0x0008 | 01 54 | section Type +0x000a | 05 | 5 count +0x000b | 60 03 7f 7f | [type 0] Func(FuncType { params: [I32, I32, I32], returns: [I32] }) + | 7f 01 7f +0x0012 | 62 02 04 72 | [type 1] Instance(InstanceType { exports: [ExportType { name: "read", ty: Function(0) }, ExportType { name: "write", ty: Function(0) }] }) + | 65 61 64 00 + | 00 05 77 72 + | 69 74 65 00 + | 00 +0x0023 | 60 00 00 | [type 2] Func(FuncType { params: [], returns: [] }) +0x0026 | 61 01 09 77 | [type 3] Module(ModuleType { imports: [Import { module: "wasi_file", field: None, ty: Instance(1) }], exports: [ExportType { name: "play", ty: Function(2) }] }) + | 61 73 69 5f + | 66 69 6c 65 + | 01 c0 06 01 + | 01 04 70 6c + | 61 79 00 02 +0x003e | 61 01 09 77 | [type 4] Module(ModuleType { imports: [Import { module: "wasi_file", field: None, ty: Instance(1) }], exports: [ExportType { name: "read", ty: Function(0) }, ExportType { name: "write", ty: Function(0) }] }) + | 61 73 69 5f + | 66 69 6c 65 + | 01 c0 06 01 + | 02 04 72 65 + | 61 64 00 00 + | 05 77 72 69 + | 74 65 00 00 +0x005e | 02 0f | section Import +0x0060 | 01 | 1 count +0x0061 | 09 77 61 73 | import [instance 0] Import { module: "wasi_file", field: None, ty: Instance(1) } + | 69 5f 66 69 + | 6c 65 01 c0 + | 06 01 +0x006f | 64 03 | section Module +0x0071 | 02 | 2 count +0x0072 | 03 | [module 0] type 3 +0x0073 | 04 | [module 1] type 4 +0x0074 | 65 09 | section Instance +0x0076 | 02 | 2 count +0x0077 | 01 | [instance 1] module:1 +0x0078 | 01 | 1 count +0x0079 | 06 00 | [instantiate arg] Instance 0 +0x007b | 00 | [instance 2] module:0 +0x007c | 01 | 1 count +0x007d | 06 01 | [instantiate arg] Instance 1 +0x007f | 66 05 | section Alias +0x0081 | 01 | 1 count +0x0082 | 00 02 00 00 | [alias] Alias { instance: Child(2), kind: Function, index: 0 } +0x0086 | 03 02 | section Function +0x0088 | 01 | 1 count +0x0089 | 02 | [func 1] type 2 +0x008a | 07 08 | section Export +0x008c | 01 | 1 count +0x008d | 04 77 6f 72 | export Export { field: "work", kind: Function, index: 1 } + | 6b 00 01 +0x0094 | 67 b5 01 | section ModuleCode +0x0097 | 02 | 2 count +0x0098 | 51 | inline module + 0x0099 | 00 61 73 6d | version 1 + | 01 00 00 00 + 0x00a1 | 66 04 | section Alias + 0x00a3 | 01 | 1 count + 0x00a4 | 01 07 01 | [alias] Alias { instance: Parent, kind: Type, index: 1 } + 0x00a7 | 01 04 | section Type + 0x00a9 | 01 | 1 count + 0x00aa | 60 00 00 | [type 1] Func(FuncType { params: [], returns: [] }) + 0x00ad | 02 0f | section Import + 0x00af | 01 | 1 count + 0x00b0 | 09 77 61 73 | import [instance 0] Import { module: "wasi_file", field: None, ty: Instance(0) } + | 69 5f 66 69 + | 6c 65 01 c0 + | 06 00 + 0x00be | 03 02 | section Function + 0x00c0 | 01 | 1 count + 0x00c1 | 01 | [func 0] type 1 + 0x00c2 | 07 08 | section Export + 0x00c4 | 01 | 1 count + 0x00c5 | 04 70 6c 61 | export Export { field: "play", kind: Function, index: 0 } + | 79 00 00 + 0x00cc | 0a 04 | section Code + 0x00ce | 01 | 1 count +============== func 0 ==================== + 0x00cf | 02 | size of function + 0x00d0 | 00 | 0 local blocks + 0x00d1 | 0b | End + 0x00d2 | 00 16 04 6e | section Custom { name: "name", kind: Name } + | 61 6d 65 + 0x00d9 | 00 06 | module name + 0x00db | 05 43 48 49 | "CHILD" + | 4c 44 + 0x00e1 | 01 07 | function names + 0x00e3 | 01 | 1 count + 0x00e4 | 00 04 70 6c | Naming { index: 0, name: "play" } + | 61 79 +0x00ea | 61 | inline module + 0x00eb | 00 61 73 6d | version 1 + | 01 00 00 00 + 0x00f3 | 66 04 | section Alias + 0x00f5 | 01 | 1 count + 0x00f6 | 01 07 01 | [alias] Alias { instance: Parent, kind: Type, index: 1 } + 0x00f9 | 01 08 | section Type + 0x00fb | 01 | 1 count + 0x00fc | 60 03 7f 7f | [type 1] Func(FuncType { params: [I32, I32, I32], returns: [I32] }) + | 7f 01 7f + 0x0103 | 02 0f | section Import + 0x0105 | 01 | 1 count + 0x0106 | 09 77 61 73 | import [instance 0] Import { module: "wasi_file", field: None, ty: Instance(0) } + | 69 5f 66 69 + | 6c 65 01 c0 + | 06 00 + 0x0114 | 03 03 | section Function + 0x0116 | 02 | 2 count + 0x0117 | 01 | [func 0] type 1 + 0x0118 | 01 | [func 1] type 1 + 0x0119 | 07 10 | section Export + 0x011b | 02 | 2 count + 0x011c | 04 72 65 61 | export Export { field: "read", kind: Function, index: 0 } + | 64 00 00 + 0x0123 | 05 77 72 69 | export Export { field: "write", kind: Function, index: 1 } + | 74 65 00 01 + 0x012b | 0a 0b | section Code + 0x012d | 02 | 2 count +============== func 0 ==================== + 0x012e | 04 | size of function + 0x012f | 00 | 0 local blocks + 0x0130 | 41 00 | I32Const { value: 0 } + 0x0132 | 0b | End +============== func 1 ==================== + 0x0133 | 04 | size of function + 0x0134 | 00 | 0 local blocks + 0x0135 | 41 00 | I32Const { value: 0 } + 0x0137 | 0b | End + 0x0138 | 00 12 04 6e | section Custom { name: "name", kind: Name } + | 61 6d 65 + 0x013f | 00 0b | module name + 0x0141 | 0a 56 49 52 | "VIRTUALIZE" + | 54 55 41 4c + | 49 5a 45 +0x014c | 0a 06 | section Code +0x014e | 01 | 1 count +============== func 1 ==================== +0x014f | 04 | size of function +0x0150 | 00 | 0 local blocks +0x0151 | 10 00 | Call { function_index: 0 } +0x0153 | 0b | End +0x0154 | 00 15 04 6e | section Custom { name: "name", kind: Name } + | 61 6d 65 +0x015b | 01 0e | function names +0x015d | 01 | 1 count +0x015e | 00 0b 63 68 | Naming { index: 0, name: "child.$play" } + | 69 6c 64 2e + | 24 70 6c 61 + | 79 diff --git a/tests/dump/export-all.wat b/tests/dump/export-all.wat new file mode 100644 index 0000000000..ca49eaeddf --- /dev/null +++ b/tests/dump/export-all.wat @@ -0,0 +1,6 @@ +(module + (import "i" (instance $i + (export "f1" (func)) + )) + (export $i) +) diff --git a/tests/dump/export-all.wat.dump b/tests/dump/export-all.wat.dump new file mode 100644 index 0000000000..fbaa4198c5 --- /dev/null +++ b/tests/dump/export-all.wat.dump @@ -0,0 +1,18 @@ +0x0000 | 00 61 73 6d | version 1 + | 01 00 00 00 +0x0008 | 01 0b | section Type +0x000a | 02 | 2 count +0x000b | 60 00 00 | [type 0] Func(FuncType { params: [], returns: [] }) +0x000e | 62 01 02 66 | [type 1] Instance(InstanceType { exports: [ExportType { name: "f1", ty: Function(0) }] }) + | 31 00 00 +0x0015 | 02 07 | section Import +0x0017 | 01 | 1 count +0x0018 | 01 69 01 c0 | import [instance 0] Import { module: "i", field: None, ty: Instance(1) } + | 06 01 +0x001e | 66 05 | section Alias +0x0020 | 01 | 1 count +0x0021 | 00 00 00 00 | [alias] Alias { instance: Child(0), kind: Function, index: 0 } +0x0025 | 07 06 | section Export +0x0027 | 01 | 1 count +0x0028 | 02 66 31 00 | export Export { field: "f1", kind: Function, index: 0 } + | 00 diff --git a/tests/dump/import-modules.wat b/tests/dump/import-modules.wat new file mode 100644 index 0000000000..23f7221128 --- /dev/null +++ b/tests/dump/import-modules.wat @@ -0,0 +1,9 @@ +(module + (import "" (module $m + (import "" (module)) + )) + (import "" (module $i + (import "" (func)) + )) + (instance $i (instantiate $m (module $i))) +) diff --git a/tests/dump/import-modules.wat.dump b/tests/dump/import-modules.wat.dump new file mode 100644 index 0000000000..d050b97e41 --- /dev/null +++ b/tests/dump/import-modules.wat.dump @@ -0,0 +1,21 @@ +0x0000 | 00 61 73 6d | version 1 + | 01 00 00 00 +0x0008 | 01 17 | section Type +0x000a | 04 | 4 count +0x000b | 61 00 00 | [type 0] Module(ModuleType { imports: [], exports: [] }) +0x000e | 61 01 00 01 | [type 1] Module(ModuleType { imports: [Import { module: "", field: None, ty: Module(0) }], exports: [] }) + | c0 05 00 00 +0x0016 | 60 00 00 | [type 2] Func(FuncType { params: [], returns: [] }) +0x0019 | 61 01 00 01 | [type 3] Module(ModuleType { imports: [Import { module: "", field: None, ty: Function(2) }], exports: [] }) + | c0 00 02 00 +0x0021 | 02 0b | section Import +0x0023 | 02 | 2 count +0x0024 | 00 01 c0 05 | import [module 0] Import { module: "", field: None, ty: Module(1) } + | 01 +0x0029 | 00 01 c0 05 | import [module 1] Import { module: "", field: None, ty: Module(3) } + | 03 +0x002e | 65 05 | section Instance +0x0030 | 01 | 1 count +0x0031 | 00 | [instance 0] module:0 +0x0032 | 01 | 1 count +0x0033 | 05 01 | [instantiate arg] Module 1 diff --git a/tests/dump/instance-expand.wat b/tests/dump/instance-expand.wat new file mode 100644 index 0000000000..e2375e0e3c --- /dev/null +++ b/tests/dump/instance-expand.wat @@ -0,0 +1,7 @@ +(module + (type $i (instance + (export "" (func)) + )) + + (import "" (instance (type $i))) +) diff --git a/tests/dump/instance-expand.wat.dump b/tests/dump/instance-expand.wat.dump new file mode 100644 index 0000000000..ec2af38792 --- /dev/null +++ b/tests/dump/instance-expand.wat.dump @@ -0,0 +1,11 @@ +0x0000 | 00 61 73 6d | version 1 + | 01 00 00 00 +0x0008 | 01 09 | section Type +0x000a | 02 | 2 count +0x000b | 60 00 00 | [type 0] Func(FuncType { params: [], returns: [] }) +0x000e | 62 01 00 00 | [type 1] Instance(InstanceType { exports: [ExportType { name: "", ty: Function(0) }] }) + | 00 +0x0013 | 02 06 | section Import +0x0015 | 01 | 1 count +0x0016 | 00 01 c0 06 | import [instance 0] Import { module: "", field: None, ty: Instance(1) } + | 01 diff --git a/tests/dump/instance-type.wat b/tests/dump/instance-type.wat new file mode 100644 index 0000000000..a6775e72ed --- /dev/null +++ b/tests/dump/instance-type.wat @@ -0,0 +1,7 @@ +(module + (type (instance + (export "" (func)) + )) + + (type (instance (export "" (instance)))) +) diff --git a/tests/dump/instance-type.wat.dump b/tests/dump/instance-type.wat.dump new file mode 100644 index 0000000000..feaae1b894 --- /dev/null +++ b/tests/dump/instance-type.wat.dump @@ -0,0 +1,10 @@ +0x0000 | 00 61 73 6d | version 1 + | 01 00 00 00 +0x0008 | 01 10 | section Type +0x000a | 04 | 4 count +0x000b | 60 00 00 | [type 0] Func(FuncType { params: [], returns: [] }) +0x000e | 62 01 00 00 | [type 1] Instance(InstanceType { exports: [ExportType { name: "", ty: Function(0) }] }) + | 00 +0x0013 | 62 00 | [type 2] Instance(InstanceType { exports: [] }) +0x0015 | 62 01 00 06 | [type 3] Instance(InstanceType { exports: [ExportType { name: "", ty: Instance(2) }] }) + | 02 diff --git a/tests/dump/instance-type2.wat b/tests/dump/instance-type2.wat new file mode 100644 index 0000000000..1fc7f5d5ab --- /dev/null +++ b/tests/dump/instance-type2.wat @@ -0,0 +1,6 @@ +(module + (type (instance)) + (type (instance (export "" (instance (type 0))))) + (type $x (instance)) + (type (instance (export "" (instance (type $x))))) +) diff --git a/tests/dump/instance-type2.wat.dump b/tests/dump/instance-type2.wat.dump new file mode 100644 index 0000000000..fe48f01d96 --- /dev/null +++ b/tests/dump/instance-type2.wat.dump @@ -0,0 +1,10 @@ +0x0000 | 00 61 73 6d | version 1 + | 01 00 00 00 +0x0008 | 01 0f | section Type +0x000a | 04 | 4 count +0x000b | 62 00 | [type 0] Instance(InstanceType { exports: [] }) +0x000d | 62 01 00 06 | [type 1] Instance(InstanceType { exports: [ExportType { name: "", ty: Instance(0) }] }) + | 00 +0x0012 | 62 00 | [type 2] Instance(InstanceType { exports: [] }) +0x0014 | 62 01 00 06 | [type 3] Instance(InstanceType { exports: [ExportType { name: "", ty: Instance(2) }] }) + | 02 diff --git a/tests/dump/instantiate.wat b/tests/dump/instantiate.wat new file mode 100644 index 0000000000..648c4d7d6f --- /dev/null +++ b/tests/dump/instantiate.wat @@ -0,0 +1,21 @@ + +(module + (import "" (module $m)) + (import "" (module $m2)) + (import "" (instance $b)) + (func $f) + (global $g i32 (i32.const 0)) + (memory $mem 1) + (table $table 1 funcref) + + (instance $a + (instantiate $m + (module $m2) + (func $f) + (global $g) + (instance $b) + (memory $mem) + (table $table) + ) + ) +) diff --git a/tests/dump/instantiate.wat.dump b/tests/dump/instantiate.wat.dump new file mode 100644 index 0000000000..e4f9c2fea8 --- /dev/null +++ b/tests/dump/instantiate.wat.dump @@ -0,0 +1,50 @@ +0x0000 | 00 61 73 6d | version 1 + | 01 00 00 00 +0x0008 | 01 09 | section Type +0x000a | 03 | 3 count +0x000b | 61 00 00 | [type 0] Module(ModuleType { imports: [], exports: [] }) +0x000e | 62 00 | [type 1] Instance(InstanceType { exports: [] }) +0x0010 | 60 00 00 | [type 2] Func(FuncType { params: [], returns: [] }) +0x0013 | 02 10 | section Import +0x0015 | 03 | 3 count +0x0016 | 00 01 c0 05 | import [module 0] Import { module: "", field: None, ty: Module(0) } + | 00 +0x001b | 00 01 c0 05 | import [module 1] Import { module: "", field: None, ty: Module(0) } + | 00 +0x0020 | 00 01 c0 06 | import [instance 0] Import { module: "", field: None, ty: Instance(1) } + | 01 +0x0025 | 65 0f | section Instance +0x0027 | 01 | 1 count +0x0028 | 00 | [instance 1] module:0 +0x0029 | 06 | 6 count +0x002a | 05 01 | [instantiate arg] Module 1 +0x002c | 00 00 | [instantiate arg] Function 0 +0x002e | 03 00 | [instantiate arg] Global 0 +0x0030 | 06 00 | [instantiate arg] Instance 0 +0x0032 | 02 00 | [instantiate arg] Memory 0 +0x0034 | 01 00 | [instantiate arg] Table 0 +0x0036 | 03 02 | section Function +0x0038 | 01 | 1 count +0x0039 | 02 | [func 0] type 2 +0x003a | 04 04 | section Table +0x003c | 01 | 1 count +0x003d | 70 00 01 | [table 0] TableType { element_type: FuncRef, limits: ResizableLimits { initial: 1, maximum: None } } +0x0040 | 05 03 | section Memory +0x0042 | 01 | 1 count +0x0043 | 00 01 | [memory 0] MemoryType { limits: ResizableLimits { initial: 1, maximum: None }, shared: false } +0x0045 | 06 06 | section Global +0x0047 | 01 | 1 count +0x0048 | 7f 00 | [global 0] GlobalType { content_type: I32, mutable: false } +0x004a | 41 00 | I32Const { value: 0 } +0x004c | 0b | End +0x004d | 0a 04 | section Code +0x004f | 01 | 1 count +============== func 0 ==================== +0x0050 | 02 | size of function +0x0051 | 00 | 0 local blocks +0x0052 | 0b | End +0x0053 | 00 0b 04 6e | section Custom { name: "name", kind: Name } + | 61 6d 65 +0x005a | 01 04 | function names +0x005c | 01 | 1 count +0x005d | 00 01 66 | Naming { index: 0, name: "f" } diff --git a/tests/dump/instantiate2.wat b/tests/dump/instantiate2.wat new file mode 100644 index 0000000000..35b298739b --- /dev/null +++ b/tests/dump/instantiate2.wat @@ -0,0 +1,4 @@ +(module + (import "" (module $m (import "" (func)))) + (instance (instantiate $m)) +) diff --git a/tests/dump/instantiate2.wat.dump b/tests/dump/instantiate2.wat.dump new file mode 100644 index 0000000000..5dea98297f --- /dev/null +++ b/tests/dump/instantiate2.wat.dump @@ -0,0 +1,15 @@ +0x0000 | 00 61 73 6d | version 1 + | 01 00 00 00 +0x0008 | 01 0c | section Type +0x000a | 02 | 2 count +0x000b | 60 00 00 | [type 0] Func(FuncType { params: [], returns: [] }) +0x000e | 61 01 00 01 | [type 1] Module(ModuleType { imports: [Import { module: "", field: None, ty: Function(0) }], exports: [] }) + | c0 00 00 00 +0x0016 | 02 06 | section Import +0x0018 | 01 | 1 count +0x0019 | 00 01 c0 05 | import [module 0] Import { module: "", field: None, ty: Module(1) } + | 01 +0x001e | 65 03 | section Instance +0x0020 | 01 | 1 count +0x0021 | 00 | [instance 0] module:0 +0x0022 | 00 | 0 count diff --git a/tests/dump/module-linking.wat b/tests/dump/module-linking.wat new file mode 100644 index 0000000000..ff3609c612 --- /dev/null +++ b/tests/dump/module-linking.wat @@ -0,0 +1,28 @@ +(module + (import "" (instance $i + (export "1" (func $func)) + (export "2" (memory $memory 1)) + (export "3" (table $table 1 funcref)) + (export "4" (global $global i32)) + (export "5" (module $module)) + (export "6" (instance $instance)) + )) + + (module $m + (import "" (func)) + (import "" (memory 1)) + (import "" (global i32)) + (import "" (table 1 funcref)) + (import "" (module)) + (import "" (instance)) + ) + + (instance (instantiate $m + (func $i.$func) + (memory $i.$memory) + (global $i.$global) + (table $i.$table) + (module $i.$module) + (instance $i.$instance) + )) +) diff --git a/tests/dump/module-linking.wat.dump b/tests/dump/module-linking.wat.dump new file mode 100644 index 0000000000..f074c04991 --- /dev/null +++ b/tests/dump/module-linking.wat.dump @@ -0,0 +1,85 @@ +0x0000 | 00 61 73 6d | version 1 + | 01 00 00 00 +0x0008 | 01 4c | section Type +0x000a | 05 | 5 count +0x000b | 60 00 00 | [type 0] Func(FuncType { params: [], returns: [] }) +0x000e | 61 00 00 | [type 1] Module(ModuleType { imports: [], exports: [] }) +0x0011 | 62 00 | [type 2] Instance(InstanceType { exports: [] }) +0x0013 | 62 06 01 31 | [type 3] Instance(InstanceType { exports: [ExportType { name: "1", ty: Function(0) }, ExportType { name: "2", ty: Memory(MemoryType { limits: ResizableLimits { initial: 1, maximum: None }, shared: false }) }, ExportType { name: "3", ty: Table(TableType { element_type: FuncRef, limits: ResizableLimits { initial: 1, maximum: None } }) }, ExportType { name: "4", ty: Global(GlobalType { content_type: I32, mutable: false }) }, ExportType { name: "5", ty: Module(1) }, ExportType { name: "6", ty: Instance(2) }] }) + | 00 00 01 32 + | 02 00 01 01 + | 33 01 70 00 + | 01 01 34 03 + | 7f 00 01 35 + | 05 01 01 36 + | 06 02 +0x0031 | 61 06 00 01 | [type 4] Module(ModuleType { imports: [Import { module: "", field: None, ty: Function(0) }, Import { module: "", field: None, ty: Memory(MemoryType { limits: ResizableLimits { initial: 1, maximum: None }, shared: false }) }, Import { module: "", field: None, ty: Global(GlobalType { content_type: I32, mutable: false }) }, Import { module: "", field: None, ty: Table(TableType { element_type: FuncRef, limits: ResizableLimits { initial: 1, maximum: None } }) }, Import { module: "", field: None, ty: Module(1) }, Import { module: "", field: None, ty: Instance(2) }], exports: [] }) + | c0 00 00 00 + | 01 c0 02 00 + | 01 00 01 c0 + | 03 7f 00 00 + | 01 c0 01 70 + | 00 01 00 01 + | c0 05 01 00 + | 01 c0 06 02 + | 00 +0x0056 | 02 06 | section Import +0x0058 | 01 | 1 count +0x0059 | 00 01 c0 06 | import [instance 0] Import { module: "", field: None, ty: Instance(3) } + | 03 +0x005e | 66 19 | section Alias +0x0060 | 06 | 6 count +0x0061 | 00 00 00 00 | [alias] Alias { instance: Child(0), kind: Function, index: 0 } +0x0065 | 00 00 02 01 | [alias] Alias { instance: Child(0), kind: Memory, index: 1 } +0x0069 | 00 00 01 02 | [alias] Alias { instance: Child(0), kind: Table, index: 2 } +0x006d | 00 00 03 03 | [alias] Alias { instance: Child(0), kind: Global, index: 3 } +0x0071 | 00 00 05 04 | [alias] Alias { instance: Child(0), kind: Module, index: 4 } +0x0075 | 00 00 06 05 | [alias] Alias { instance: Child(0), kind: Instance, index: 5 } +0x0079 | 64 02 | section Module +0x007b | 01 | 1 count +0x007c | 04 | [module 1] type 4 +0x007d | 65 0f | section Instance +0x007f | 01 | 1 count +0x0080 | 01 | [instance 2] module:1 +0x0081 | 06 | 6 count +0x0082 | 00 00 | [instantiate arg] Function 0 +0x0084 | 02 00 | [instantiate arg] Memory 0 +0x0086 | 03 00 | [instantiate arg] Global 0 +0x0088 | 01 00 | [instantiate arg] Table 0 +0x008a | 05 00 | [instantiate arg] Module 0 +0x008c | 06 01 | [instantiate arg] Instance 1 +0x008e | 67 45 | section ModuleCode +0x0090 | 01 | 1 count +0x0091 | 43 | inline module + 0x0092 | 00 61 73 6d | version 1 + | 01 00 00 00 + 0x009a | 01 09 | section Type + 0x009c | 03 | 3 count + 0x009d | 60 00 00 | [type 0] Func(FuncType { params: [], returns: [] }) + 0x00a0 | 61 00 00 | [type 1] Module(ModuleType { imports: [], exports: [] }) + 0x00a3 | 62 00 | [type 2] Instance(InstanceType { exports: [] }) + 0x00a5 | 02 23 | section Import + 0x00a7 | 06 | 6 count + 0x00a8 | 00 01 c0 00 | import [func 0] Import { module: "", field: None, ty: Function(0) } + | 00 + 0x00ad | 00 01 c0 02 | import [memory 0] Import { module: "", field: None, ty: Memory(MemoryType { limits: ResizableLimits { initial: 1, maximum: None }, shared: false }) } + | 00 01 + 0x00b3 | 00 01 c0 03 | import [global 0] Import { module: "", field: None, ty: Global(GlobalType { content_type: I32, mutable: false }) } + | 7f 00 + 0x00b9 | 00 01 c0 01 | import [table 0] Import { module: "", field: None, ty: Table(TableType { element_type: FuncRef, limits: ResizableLimits { initial: 1, maximum: None } }) } + | 70 00 01 + 0x00c0 | 00 01 c0 05 | import [module 0] Import { module: "", field: None, ty: Module(1) } + | 01 + 0x00c5 | 00 01 c0 06 | import [instance 0] Import { module: "", field: None, ty: Instance(2) } + | 02 + 0x00ca | 00 09 04 6e | section Custom { name: "name", kind: Name } + | 61 6d 65 + 0x00d1 | 00 02 | module name + 0x00d3 | 01 6d | "m" +0x00d5 | 00 11 04 6e | section Custom { name: "name", kind: Name } + | 61 6d 65 +0x00dc | 01 0a | function names +0x00de | 01 | 1 count +0x00df | 00 07 69 2e | Naming { index: 0, name: "i.$func" } + | 24 66 75 6e + | 63 diff --git a/tests/dump/module-types.wat b/tests/dump/module-types.wat new file mode 100644 index 0000000000..887f6aaa1b --- /dev/null +++ b/tests/dump/module-types.wat @@ -0,0 +1,11 @@ +(module + (import "" (module $m + (import "" (module)) + (import "" (func)) + (import "" (global i32)) + (import "" (table 1 funcref)) + (import "" (instance)) + (import "" (memory 1)) + )) + (type $empty (instance)) +) diff --git a/tests/dump/module-types.wat.dump b/tests/dump/module-types.wat.dump new file mode 100644 index 0000000000..95d621e035 --- /dev/null +++ b/tests/dump/module-types.wat.dump @@ -0,0 +1,21 @@ +0x0000 | 00 61 73 6d | version 1 + | 01 00 00 00 +0x0008 | 01 2e | section Type +0x000a | 04 | 4 count +0x000b | 62 00 | [type 0] Instance(InstanceType { exports: [] }) +0x000d | 61 00 00 | [type 1] Module(ModuleType { imports: [], exports: [] }) +0x0010 | 60 00 00 | [type 2] Func(FuncType { params: [], returns: [] }) +0x0013 | 61 06 00 01 | [type 3] Module(ModuleType { imports: [Import { module: "", field: None, ty: Module(1) }, Import { module: "", field: None, ty: Function(2) }, Import { module: "", field: None, ty: Global(GlobalType { content_type: I32, mutable: false }) }, Import { module: "", field: None, ty: Table(TableType { element_type: FuncRef, limits: ResizableLimits { initial: 1, maximum: None } }) }, Import { module: "", field: None, ty: Instance(0) }, Import { module: "", field: None, ty: Memory(MemoryType { limits: ResizableLimits { initial: 1, maximum: None }, shared: false }) }], exports: [] }) + | c0 05 01 00 + | 01 c0 00 02 + | 00 01 c0 03 + | 7f 00 00 01 + | c0 01 70 00 + | 01 00 01 c0 + | 06 00 00 01 + | c0 02 00 01 + | 00 +0x0038 | 02 06 | section Import +0x003a | 01 | 1 count +0x003b | 00 01 c0 05 | import [module 0] Import { module: "", field: None, ty: Module(3) } + | 03 diff --git a/tests/dump/names.wat b/tests/dump/names.wat new file mode 100644 index 0000000000..64fc40dd5f --- /dev/null +++ b/tests/dump/names.wat @@ -0,0 +1,3 @@ +(module $foo + (func $f (param $x i32) + (local $y f64))) diff --git a/tests/dump/names.wat.dump b/tests/dump/names.wat.dump new file mode 100644 index 0000000000..9078a20487 --- /dev/null +++ b/tests/dump/names.wat.dump @@ -0,0 +1,28 @@ +0x0000 | 00 61 73 6d | version 1 + | 01 00 00 00 +0x0008 | 01 05 | section Type +0x000a | 01 | 1 count +0x000b | 60 01 7f 00 | [type 0] Func(FuncType { params: [I32], returns: [] }) +0x000f | 03 02 | section Function +0x0011 | 01 | 1 count +0x0012 | 00 | [func 0] type 0 +0x0013 | 0a 06 | section Code +0x0015 | 01 | 1 count +============== func 0 ==================== +0x0016 | 04 | size of function +0x0017 | 01 | 1 local blocks +0x0018 | 01 7c | 1 locals of type F64 +0x001a | 0b | End +0x001b | 00 1c 04 6e | section Custom { name: "name", kind: Name } + | 61 6d 65 +0x0022 | 00 04 | module name +0x0024 | 03 66 6f 6f | "foo" +0x0028 | 01 04 | function names +0x002a | 01 | 1 count +0x002b | 00 01 66 | Naming { index: 0, name: "f" } +0x002e | 02 09 | local names +0x0030 | 01 | 1 count +0x0031 | 00 | function 0 locals +0x0032 | 02 | 2 count +0x0033 | 00 01 78 | Naming { index: 0, name: "x" } +0x0036 | 01 01 79 | Naming { index: 1, name: "y" } diff --git a/tests/dump/nested-module.wat b/tests/dump/nested-module.wat new file mode 100644 index 0000000000..88c45d5cdf --- /dev/null +++ b/tests/dump/nested-module.wat @@ -0,0 +1,26 @@ + +(module + (module (import "")) + + (module) + (module) + + (type $f (module)) + (module (type $f)) + + (module (export "x")) + + (module + (module) + ) + + (module + (module $m) + (import "" (func (param i32))) + (export "a" (module $m)) + + (instance (export "b") (import "") + (export "b" (func)) + ) + ) +) diff --git a/tests/dump/nested-module.wat.dump b/tests/dump/nested-module.wat.dump new file mode 100644 index 0000000000..2311ab6e54 --- /dev/null +++ b/tests/dump/nested-module.wat.dump @@ -0,0 +1,90 @@ +0x0000 | 00 61 73 6d | version 1 + | 01 00 00 00 +0x0008 | 01 26 | section Type +0x000a | 05 | 5 count +0x000b | 61 00 00 | [type 0] Module(ModuleType { imports: [], exports: [] }) +0x000e | 60 01 7f 00 | [type 1] Func(FuncType { params: [I32], returns: [] }) +0x0012 | 60 00 00 | [type 2] Func(FuncType { params: [], returns: [] }) +0x0015 | 62 01 01 62 | [type 3] Instance(InstanceType { exports: [ExportType { name: "b", ty: Function(2) }] }) + | 00 02 +0x001b | 61 02 00 01 | [type 4] Module(ModuleType { imports: [Import { module: "", field: None, ty: Function(1) }, Import { module: "", field: None, ty: Instance(3) }], exports: [ExportType { name: "a", ty: Module(0) }, ExportType { name: "b", ty: Instance(3) }] }) + | c0 00 01 00 + | 01 c0 06 03 + | 02 01 61 05 + | 00 01 62 06 + | 03 +0x0030 | 02 06 | section Import +0x0032 | 01 | 1 count +0x0033 | 00 01 c0 05 | import [module 0] Import { module: "", field: None, ty: Module(0) } + | 00 +0x0038 | 64 07 | section Module +0x003a | 06 | 6 count +0x003b | 00 | [module 1] type 0 +0x003c | 00 | [module 2] type 0 +0x003d | 00 | [module 3] type 0 +0x003e | 00 | [module 4] type 0 +0x003f | 00 | [module 5] type 0 +0x0040 | 04 | [module 6] type 4 +0x0041 | 07 05 | section Export +0x0043 | 01 | 1 count +0x0044 | 01 78 05 04 | export Export { field: "x", kind: Module, index: 4 } +0x0048 | 67 93 01 | section ModuleCode +0x004b | 06 | 6 count +0x004c | 08 | inline module + 0x004d | 00 61 73 6d | version 1 + | 01 00 00 00 +0x0055 | 08 | inline module + 0x0056 | 00 61 73 6d | version 1 + | 01 00 00 00 +0x005e | 08 | inline module + 0x005f | 00 61 73 6d | version 1 + | 01 00 00 00 +0x0067 | 08 | inline module + 0x0068 | 00 61 73 6d | version 1 + | 01 00 00 00 +0x0070 | 1e | inline module + 0x0071 | 00 61 73 6d | version 1 + | 01 00 00 00 + 0x0079 | 01 04 | section Type + 0x007b | 01 | 1 count + 0x007c | 61 00 00 | [type 0] Module(ModuleType { imports: [], exports: [] }) + 0x007f | 64 02 | section Module + 0x0081 | 01 | 1 count + 0x0082 | 00 | [module 0] type 0 + 0x0083 | 67 0a | section ModuleCode + 0x0085 | 01 | 1 count + 0x0086 | 08 | inline module + 0x0087 | 00 61 73 6d | version 1 + | 01 00 00 00 +0x008f | 4e | inline module + 0x0090 | 00 61 73 6d | version 1 + | 01 00 00 00 + 0x0098 | 01 11 | section Type + 0x009a | 04 | 4 count + 0x009b | 61 00 00 | [type 0] Module(ModuleType { imports: [], exports: [] }) + 0x009e | 60 01 7f 00 | [type 1] Func(FuncType { params: [I32], returns: [] }) + 0x00a2 | 60 00 00 | [type 2] Func(FuncType { params: [], returns: [] }) + 0x00a5 | 62 01 01 62 | [type 3] Instance(InstanceType { exports: [ExportType { name: "b", ty: Function(2) }] }) + | 00 02 + 0x00ab | 64 02 | section Module + 0x00ad | 01 | 1 count + 0x00ae | 00 | [module 0] type 0 + 0x00af | 02 0b | section Import + 0x00b1 | 02 | 2 count + 0x00b2 | 00 01 c0 00 | import [func 0] Import { module: "", field: None, ty: Function(1) } + | 01 + 0x00b7 | 00 01 c0 06 | import [instance 0] Import { module: "", field: None, ty: Instance(3) } + | 03 + 0x00bc | 07 09 | section Export + 0x00be | 02 | 2 count + 0x00bf | 01 61 05 00 | export Export { field: "a", kind: Module, index: 0 } + 0x00c3 | 01 62 06 00 | export Export { field: "b", kind: Instance, index: 0 } + 0x00c7 | 67 15 | section ModuleCode + 0x00c9 | 01 | 1 count + 0x00ca | 13 | inline module + 0x00cb | 00 61 73 6d | version 1 + | 01 00 00 00 + 0x00d3 | 00 09 04 6e | section Custom { name: "name", kind: Name } + | 61 6d 65 + 0x00da | 00 02 | module name + 0x00dc | 01 6d | "m" diff --git a/tests/dump/select.wat.dump b/tests/dump/select.wat.dump index b8ddf91101..dad3d1174c 100644 --- a/tests/dump/select.wat.dump +++ b/tests/dump/select.wat.dump @@ -2,7 +2,7 @@ | 01 00 00 00 0x0008 | 01 04 | section Type 0x000a | 01 | 1 count -0x000b | 60 00 00 | type FuncType { params: [], returns: [] } +0x000b | 60 00 00 | [type 0] Func(FuncType { params: [], returns: [] }) 0x000e | 03 02 | section Function 0x0010 | 01 | 1 count 0x0011 | 00 | [func 0] type 0 diff --git a/tests/dump/simple.wat.dump b/tests/dump/simple.wat.dump index 9d1080a1af..ea15bb3de8 100644 --- a/tests/dump/simple.wat.dump +++ b/tests/dump/simple.wat.dump @@ -2,11 +2,11 @@ | 01 00 00 00 0x0008 | 01 08 | section Type 0x000a | 02 | 2 count -0x000b | 60 01 7f 00 | type FuncType { params: [I32], returns: [] } -0x000f | 60 00 00 | type FuncType { params: [], returns: [] } +0x000b | 60 01 7f 00 | [type 0] Func(FuncType { params: [I32], returns: [] }) +0x000f | 60 00 00 | [type 1] Func(FuncType { params: [], returns: [] }) 0x0012 | 02 07 | section Import 0x0014 | 01 | 1 count -0x0015 | 01 6d 01 6e | import [func 0] Import { module: "m", field: "n", ty: Function(0) } +0x0015 | 01 6d 01 6e | import [func 0] Import { module: "m", field: Some("n"), ty: Function(0) } | 00 00 0x001b | 03 04 | section Function 0x001d | 03 | 3 count diff --git a/tests/local/gc-ref.wat b/tests/local/gc-ref.wat index d459404e39..234f79d260 100644 --- a/tests/local/gc-ref.wat +++ b/tests/local/gc-ref.wat @@ -11,6 +11,9 @@ (global (ref $b)) (func (param (ref $a))) + (func (param (ref 0))) + (func (type $f1) (param (ref 0))) + (func (type 2) (param (ref 0))) (func (result (ref $a))) (func (local (ref $a))) diff --git a/tests/local/module-linking/alias.wast b/tests/local/module-linking/alias.wast new file mode 100644 index 0000000000..49dfe631fe --- /dev/null +++ b/tests/local/module-linking/alias.wast @@ -0,0 +1,476 @@ +(module + (import "i" (instance $i + (export "f1" (func $f1)) + (export "f2" (func $f2 (param i32))) + )) + (func (export "run") + call $i.$f1 + ) +) +(assert_malformed + (module quote + " (type $t (instance" + " (export \"f1\" (func $f1))" + " ))" + " (import \"i\" (instance $i (type $t)))" + " (func (export \"run\")" + " call $i.$f1" + " )" + ) + "failed to find func named `$i.$f1`") + +(module + (import "i" (module $m + (export "f1" (func $f1)) + (export "f2" (func $f2 (param i32))) + )) + (instance $i (instantiate $m)) + (func (export "run") + call $i.$f1 + ) +) +(assert_malformed + (module quote + "(type $t (module " + " (export \"f1\" (func $f1))" + "))" + "(import \"i\" (module $m (type $t)))" + "(instance $i (instantiate $m))" + "(func (export \"run\")" + " call $i.$f1" + ")" + ) + "failed to find func named `$i.$f1`") + +(module + (module $m + (func $f1 (export "f1")) + (func $f2 (export "f2") (param i32)) + ) + (instance $i (instantiate $m)) + (func (export "run") + call $i.$f1 + ) +) + + +(module + (import "libc" (instance $libc + (export "memory" (memory $mem 1)) + (export "table" (table $tbl 0 funcref)) + )) + (alias (instance $libc) (memory $mem)) + (alias (instance $libc) (table $tbl)) +) + +(module + (import "libc" (instance $libc + (export "memory" (memory $mem 1)) + (export "table" (table $tbl 0 funcref)) + (export "global" (global $glbl i32)) + )) + + (func $table_get + i32.const 0 + table.get $libc.$tbl + drop) + + (func $global_get + global.get $libc.$glbl + drop) +) + +;; make sure auto-expanded aliases can't shadow already-defined names +(module + (import "" (instance $i + (export "1" (func $func (param i32))) + (export "2" (func $memory)) + (export "3" (func $table)) + (export "4" (func $global)) + (export "5" (func $module)) + (export "6" (func $instance)) + )) + (import "" (instance $i2 + (export "1" (func $func (param i32))) + (export "2" (func $memory)) + (export "3" (func $table)) + (export "4" (func $global)) + (export "5" (func $module)) + (export "6" (func $instance)) + )) + + (import "" (instance $other + (export "1" (func $func)) + (export "2" (memory $memory 1)) + (export "3" (global $global i32)) + (export "4" (table $table 1 funcref)) + (export "5" (module $module)) + (export "6" (instance $instance)) + )) + + (func $i.$func (import "")) + (memory $i.$memory (import "") 1) + (global $i.$global (import "") i32) + (table $i.$table (import "") 1 funcref) + (module $i.$module (import "")) + (instance $i.$instance (import "")) + + (alias $i2.$func (instance $other) (func $func)) + (alias $i2.$global (instance $other) (global $global)) + (alias $i2.$table (instance $other) (table $table)) + (alias $i2.$memory (instance $other) (memory $memory)) + (alias $i2.$instance (instance $other) (instance $instance)) + (alias $i2.$module (instance $other) (module $module)) + + (module $m + (import "" (func)) + (import "" (memory 1)) + (import "" (global i32)) + (import "" (table 1 funcref)) + (import "" (module)) + (import "" (instance)) + ) + + (instance (instantiate $m + (func $i.$func) + (memory $i.$memory) + (global $i.$global) + (table $i.$table) + (module $i.$module) + (instance $i.$instance) + )) + (instance (instantiate $m + (func $i2.$func) + (memory $i2.$memory) + (global $i2.$global) + (table $i2.$table) + (module $i2.$module) + (instance $i2.$instance) + )) +) + +;; auto-expansion should visit everywhere +(module + (import "" (instance $i + (export "" (global $global i32)) + )) + + (func + global.get $i.$global + drop) +) +(module + (import "" (instance $i + (export "" (global $global (mut i32))) + )) + + (func + i32.const 0 + global.set $i.$global) +) +(module + (import "" (instance $i + (export "" (table $table 1 funcref)) + )) + + (func + i32.const 0 + table.get $i.$table + drop) +) +(module + (import "" (instance $i + (export "" (table $table 1 funcref)) + )) + + (func + i32.const 0 + ref.null func + table.set $i.$table) +) +(module + (import "" (instance $i + (export "" (table $table 1 funcref)) + )) + + (func + i32.const 0 + call_indirect $i.$table) +) +(module + (import "" (instance $i + (export "" (table $table 1 funcref)) + )) + + (func + table.size $i.$table + drop) +) +(module + (import "" (instance $i + (export "" (table $table 1 funcref)) + )) + + (func + ref.null func + i32.const 0 + table.grow $i.$table + drop) +) +(module + (import "" (instance $i + (export "" (table $table 1 funcref)) + )) + + (func + i32.const 0 + ref.null func + i32.const 0 + table.fill $i.$table) +) +(module + (import "" (instance $i + (export "" (table $table 1 funcref)) + )) + + (func + i32.const 0 + i32.const 0 + i32.const 0 + table.init $i.$table 0) + (elem func 0) +) +(module + (import "" (instance $i + (export "" (table $table 1 funcref)) + )) + + (func + i32.const 0 + i32.const 0 + i32.const 0 + table.copy $i.$table $i.$table) +) + +(module + (import "" (instance $i + (export "" (func $func)) + )) + (func + return_call $i.$func) +) + +(module + (import "" (instance $i + (export "" (table $table 1 funcref)) + )) + (func + i32.const 0 + return_call_indirect $i.$table) +) + +(module + (import "" (instance $i + (export "" (func $func)) + )) + (global funcref (ref.func $i.$func)) +) +(module + (import "" (instance $i + (export "" (func $func)) + )) + (start $i.$func) +) +(module + (import "" (instance $i + (export "a" (table $table 1 funcref)) + (export "b" (func $func)) + )) + (elem (table $i.$table) (i32.const 0) funcref (ref.func $i.$func)) +) +(module + (import "" (instance $i + (export "a" (table $table 1 funcref)) + (export "b" (func $func)) + )) + (elem (table $i.$table) (i32.const 0) func $i.$func) +) +(module + (import "" (instance $i + (export "" (memory $memory 1)) + )) + (data (memory $i.$memory) (i32.const 0) "") +) + +(module + (import "" (instance $i + (export "1" (func $func)) + (export "2" (memory $memory 1)) + (export "3" (table $table 1 funcref)) + (export "4" (global $global i32)) + (export "5" (module $module)) + (export "6" (instance $instance)) + )) + (export "1" (func $i.$func)) + (export "2" (memory $i.$memory)) + (export "3" (table $i.$table)) + (export "4" (global $i.$global)) + (export "5" (module $i.$module)) + (export "6" (instance $i.$instance)) +) + +(module + (import "" (instance $i + (export "1" (func $func)) + (export "2" (memory $memory 1)) + (export "3" (table $table 1 funcref)) + (export "4" (global $global i32)) + (export "5" (module $module)) + (export "6" (instance $instance)) + )) + + (module $m + (import "" (func)) + (import "" (memory 1)) + (import "" (global i32)) + (import "" (table 1 funcref)) + (import "" (module)) + (import "" (instance)) + ) + + (instance (instantiate $m + (func $i.$func) + (memory $i.$memory) + (global $i.$global) + (table $i.$table) + (module $i.$module) + (instance $i.$instance) + )) +) + +(module + (module $m + (func $f (export "")) + ) + + (instance $i (instantiate $m)) + + (func + call $i.$f) +) + +(module + (import "" (instance $i (export "a" (func)))) + + (import "" (module $m + (import "" (module (export "a" (func)))) + )) + + (module $local + (export $i)) + + (instance (instantiate $m (module $local))) +) + +(assert_malformed + (module quote + "(func)" + "(module" + " (export \"\" (func $f))" + ")" + "(func $f)" + ) + "reference to func before item is defined") + +(assert_malformed + (module quote + "(func)" + "(module" + " (import \"\" (instance))" + " (alias (instance 0) (func 0))" + " (export \"\" (func 0))" + ")" + ) + "aliased from an export that does not exist") + +(assert_malformed + (module quote + "(func)" + "(module" + " (import \"\" (instance (export \"\" (global i32))))" + " (alias (instance 0) (func 0))" + " (export \"\" (func 0))" + ")" + ) + "alias points to export of wrong kind of item") + +(assert_malformed + (module quote + "(func)" + "(module" + " (import \"\" (module))" + " (instance (instantiate 0))" + " (alias (instance 0) (func 0))" + " (export \"\" (func 0))" + ")" + ) + "aliased from an export that does not exist") + +(assert_malformed + (module quote + "(func)" + "(module" + " (import \"\" (module (export \"\" (global i32))))" + " (instance (instantiate 0))" + " (alias (instance 0) (func 0))" + " (export \"\" (func 0))" + ")" + ) + "alias points to export of wrong kind of item") + +(assert_malformed + (module quote + "(func)" + "(module" + " (module)" + " (instance (instantiate 0))" + " (alias (instance 0) (func 0))" + " (export \"\" (func 0))" + ")" + ) + "aliased from an export that does not exist") + +(assert_malformed + (module quote + "(func)" + "(module" + " (module (global (export \"\") i32 (i32.const 0)))" + " (instance (instantiate 0))" + " (alias (instance 0) (func 0))" + " (export \"\" (func 0))" + ")" + ) + "alias points to export of wrong kind of item") + +(assert_malformed + (module quote + "(func)" + "(module" + " (type (func))" + " (import \"\" (instance (type 0)))" + " (alias (instance 0) (func 0))" + " (export \"\" (func 0))" + ")" + ) + "aliased from an instance/module that is listed with the wrong type") + +(assert_malformed + (module quote + "(func)" + "(module" + " (instance (instantiate 100))" + " (alias (instance 0) (func 0))" + " (export \"\" (func 0))" + ")" + ) + "reference to module is out of bounds") diff --git a/tests/local/module-linking/export-all.wast b/tests/local/module-linking/export-all.wast new file mode 100644 index 0000000000..40dfb1d351 --- /dev/null +++ b/tests/local/module-linking/export-all.wast @@ -0,0 +1,67 @@ +(module + (import "" (instance $f1_instance (export "f1" (func)))) + (import "" (module $f1_module (export "f1" (func)))) + + (module $require_f1 + (import "" (instance + (export "f1" (func)) + )) + ) + + (instance (instantiate $require_f1 (instance $f1_instance))) + + (module $a + (import "i" (instance $i + (export "f1" (func)) + )) + (export $i) + ) + (instance $a (instantiate $a (instance $f1_instance))) + (instance (instantiate $require_f1 (instance $a))) + + (module $b + (import "i" (module $m + (export "f1" (func)) + )) + (instance $i (instantiate $m)) + (export $i) + ) + (instance $b (instantiate $b (module $f1_module))) + (instance (instantiate $require_f1 (instance $b))) + + (module $c + (module $m + (func (export "f1")) + ) + (instance $i (instantiate $m)) + (export $i) + ) + (instance $c (instantiate $c)) + (instance (instantiate $require_f1 (instance $c))) +) + +(module + (type $i (instance (export "a" (func)))) + (type $m (module (export $i))) + + (import "" (module $imported (type $m))) + (import "" (module $to_instantiate + (import "" (module (export "a" (func)))) + )) + + (instance (instantiate $to_instantiate (module $imported))) +) + +(module + (type $i (instance (export "a" (func)))) + (module + (type $m (module (export $i))) + + (import "" (module $imported (type $m))) + (import "" (module $to_instantiate + (import "" (module (export "a" (func)))) + )) + + (instance (instantiate $to_instantiate (module $imported))) + ) +) diff --git a/tests/local/module-linking/import.wast b/tests/local/module-linking/import.wast new file mode 100644 index 0000000000..282028d72d --- /dev/null +++ b/tests/local/module-linking/import.wast @@ -0,0 +1,31 @@ +(module + (import "" (func)) + (import "" (instance)) + + (import "" (instance + (export "" (func)) + )) + (import "" (module + (import "" (module)) + (export "" (func)) + )) +) + +(assert_invalid + (module + (type (func)) + (import "" (instance (type 0))) + ) + "type index is not an instance") +(assert_invalid + (module + (type (func)) + (import "" (module (type 0))) + ) + "type index is not a module") +(assert_invalid + (module + (type (module)) + (import "" (func (type 0))) + ) + "type index is not a function") diff --git a/tests/local/module-linking/infer-types.wast b/tests/local/module-linking/infer-types.wast new file mode 100644 index 0000000000..78a27ca3ae --- /dev/null +++ b/tests/local/module-linking/infer-types.wast @@ -0,0 +1,60 @@ +(module + (module + (import "" (func $f)) + (export "1" (func $f)) + + (import "" (global $g i32)) + (export "2" (global $g)) + + (import "" (table $t 1 funcref)) + (export "3" (table $t)) + + (import "" (memory $m 1)) + (export "4" (memory $m)) + + (import "" (instance $i)) + (export "5" (instance $i)) + + (import "" (module $m)) + (export "6" (module $m)) + ) +) + +(module + (module $m + (import "" (module $m2)) + + (func $f) + (export "1" (func $f)) + + (global $g i32 (i32.const 0)) + (export "2" (global $g)) + + (table $t 1 funcref) + (export "3" (table $t)) + + (memory $m 1) + (export "4" (memory $m)) + + (module $m) + (export "5" (module $m)) + + (instance $i (instantiate $m2)) + (export "6" (instance $i)) + + (instance $i2 (instantiate $m)) + (export "7" (instance $i2)) + ) + + (module + (module $empty) + (instance $i (instantiate $m (module $m))) + (export "1" (func $i.$f)) + (export "2" (global $i.$g)) + (export "3" (table $i.$t)) + (export "4" (memory $i.$m)) + (export "5" (module $i.$m)) + (export "6" (instance $i.$i)) + (export "7" (instance $i.$i2)) + ) +) diff --git a/tests/local/module-linking/instance-type.wast b/tests/local/module-linking/instance-type.wast new file mode 100644 index 0000000000..c7e3c025ff --- /dev/null +++ b/tests/local/module-linking/instance-type.wast @@ -0,0 +1,200 @@ +;; instances +(module + (type (instance)) + + (type $foo (func)) + + (type (func (result i32))) + + (type (instance + ;; functions + (export "a" (func)) + (export "b" (func $foo)) + (export "c" (func (@name "bar"))) + (export "d" (func $foo (@name "bar"))) + (export "e" (func (type 2))) + (export "f" (func (param i32))) + (export "g" (func (param i32) (result i32 i64))) + (export "h" (func (type 2) (result i32))) + + ;; globals + (export "i" (global i32)) + (export "j" (global $foo i32)) + (export "k" (global (mut i32))) + + ;; tables + (export "l" (table 1 funcref)) + (export "m" (table $foo 1 funcref)) + + ;; memory + (export "n" (memory 1)) + (export "o" (memory $foo 1)) + (export "p" (memory 1 2)) + (export "q" (memory 1 2 shared)) + )) +) + +;; expand inline types +(module + (type (instance (export "" (instance)))) +) + +;; reference outer types +(module + (type (instance)) + (type (instance (export "" (instance (type 0))))) + (type $x (instance)) + (type (instance (export "" (instance (type $x))))) +) + +;; recursive +(module + (type $functype (func)) + + (type (instance (export "" (instance + (export "a" (func)) + (export "b" (func (type 0))) + (export "c" (func (param i32))) + (export "d" (func (type $functype))) + + ;; globals + (export "e" (global i32)) + (export "f" (global (mut i32))) + + ;; tables + (export "g" (table 1 funcref)) + + ;; memory + (export "h" (memory 1)) + (export "i" (memory 1 2)) + (export "j" (memory 1 2 shared)) + + ;; instances + (export "k" (instance)) + )))) +) + +;; modules +(module + (type (module)) + + (type $foo (module)) + + (type $empty (func)) + (type $i (instance)) + + (type (module + (import "" "" (func)) + (import "" "" (func (type $empty))) + (import "" "" (func (param i32))) + (import "" "" (func (param i32) (result i32))) + + (import "" "" (global i32)) + (import "" "" (memory 1)) + (import "" "" (table 1 funcref)) + + (import "" "" (instance)) + (import "" "" (instance (type $i))) + (import "" "" (instance + (export "a" (func)) + (export "b" (func (type $empty))) + (export "c" (func (param i32))) + )) + + (import "" "" (module)) + (import "" "" (module + (import "" "" (func (type $empty))) + (import "" "" (func (param i32))) + (export "a" (func (type $empty))) + (export "b" (func (param i32))) + (export "c" (module)) + )) + + (export "a" (func)) + (export "b" (global i32)) + (export "c" (memory 1)) + (export "d" (table 1 funcref)) + + (export "e" (func (type $empty))) + (export "f" (func (param i32))) + + (export "g" (instance + (export "a" (func)) + (export "b" (func (type $empty))) + (export "c" (func (param i32))) + )) + + (export "h" (module + (import "" "" (func (type $empty))) + (import "" "" (func (param i32))) + (export "a" (func (type $empty))) + (export "b" (func (param i32))) + (export "c" (module)) + )) + )) +) + +(assert_invalid + (module + (type (instance + (export "" (func)) + (export "" (func))))) + "duplicate export name") + +(assert_invalid + (module + (type (func)) + (type (instance + (export "" (instance (type 0))) + ))) + "type index is not an instance") + +(assert_invalid + (module + (type (module)) + (type (instance + (export "" (instance (type 0))) + ))) + "type index is not an instance") + +(assert_invalid + (module + (type (func)) + (type (instance + (export "" (module (type 0))) + ))) + "type index is not a module") + +(assert_invalid + (module + (type (instance)) + (type (instance + (export "" (module (type 0))) + ))) + "type index is not a module") + +(assert_invalid + (module + (type (module)) + (type (instance + (export "" (func (type 0))) + ))) + "type index is not a function") + +(assert_invalid + (module + (type (instance)) + (type (instance + (export "" (func (type 0))) + ))) + "type index is not a function") + +(assert_invalid + (module + (type (instance)) + (type (instance + (export "" (instance + (export "" (func (type 0))) + )) + ))) + "type index is not a function") diff --git a/tests/local/module-linking/instantiate.wast b/tests/local/module-linking/instantiate.wast new file mode 100644 index 0000000000..f0043c3494 --- /dev/null +++ b/tests/local/module-linking/instantiate.wast @@ -0,0 +1,251 @@ +(module + (import "" (module $m)) + (instance $a (instantiate $m)) +) + +(module + (import "" (func)) + (import "" (global i32)) + (import "" (table 1 funcref)) + (import "" (instance)) + (import "" (memory 1)) + (import "" (module $empty)) + + (import "" (module $m + (import "" (module)) + (import "" (func)) + (import "" (global i32)) + (import "" (table 1 funcref)) + (import "" (instance)) + (import "" (memory 1)) + )) + + (instance $a (instantiate $m + (module $empty) + (func 0) + (global 0) + (table 0) + (instance 0) + (memory 0) + )) +) + +(module + (import "" (module $m + (import "" (module)) + (import "" (func)) + (import "" (global i32)) + (import "" (table 1 funcref)) + (import "" (instance)) + (import "" (memory 1)) + )) + (import "" (module $m2)) + (import "" (instance $b)) + (func $f (import "")) + (global $g (import "") i32) + (memory $mem (import "") 1) + (table $table (import "") 1 funcref) + + (instance $a + (instantiate $m + (module $m2) + (func $f) + (global $g) + (table $table) + (instance $b) + (memory $mem) + ) + ) + + ;; inline exports/imports + (instance $c (export "i") (instantiate $m2)) + (type $empty (instance)) + (instance $d (import "i") (type $empty)) + (instance (import "i")) + (instance (import "i") + (export "x" (func))) + (instance (export "a") (export "b") (instantiate $m2)) + (instance (export "c") (export "d") (import "x")) +) + +(assert_invalid + (module + (instance (instantiate 0)) + ) + "module is not defined") +(assert_invalid + (module + (import "" (module)) + (instance (instantiate 1)) + ) + "module is not defined") + +(assert_invalid + (module + (import "" (func $f)) + (import "" (module $m)) + (instance (instantiate $m (func $f))) + ) + "wrong number of imports provided") +(assert_invalid + (module + (import "" (module $m (import "" (func)))) + (instance (instantiate $m)) + ) + "wrong number of imports provided") + + +(assert_invalid + (module + (import "" (module $m + (import "" (func)) + )) + (import "" (global i32)) + (instance $i (instantiate $m (global 0))) + ) + "wrong kind of item used for instantiate") +(assert_invalid + (module + (import "" (module $m + (import "" (func)) + )) + (import "" (func (result i32))) + (instance $i (instantiate $m (func 0))) + ) + "function provided for instantiation has wrong type") +(assert_invalid + (module + (import "" (module $m + (import "" (func)) + )) + (import "" (func (param i32))) + (instance $i (instantiate $m (func 0))) + ) + "function provided for instantiation has wrong type") +(assert_invalid + (module + (import "" (module $m + (import "" (global i32)) + )) + (import "" (global i64)) + (instance $i (instantiate $m (global 0))) + ) + "global provided for instantiation has wrong type") +(assert_invalid + (module + (import "" (module $m + (import "" (table 1 externref)) + )) + (import "" (table 2 funcref)) + (instance $i (instantiate $m (table 0))) + ) + "table provided for instantiation has wrong type") +(assert_invalid + (module + (import "" (module $m + (import "" (table 1 2 funcref)) + )) + (import "" (table 2 funcref)) + (instance $i (instantiate $m (table 0))) + ) + "table provided for instantiation has wrong type") +(assert_invalid + (module + (import "" (module $m + (import "" (table 2 2 funcref)) + )) + (import "" (table 1 funcref)) + (instance $i (instantiate $m (table 0))) + ) + "table provided for instantiation has wrong type") +(assert_invalid + (module + (import "" (module $m + (import "" (table 2 2 funcref)) + )) + (import "" (table 2 3 funcref)) + (instance $i (instantiate $m (table 0))) + ) + "table provided for instantiation has wrong type") +(assert_invalid + (module + (import "" (module $m + (import "" (memory 1 2 shared)) + )) + (import "" (memory 1)) + (instance $i (instantiate $m (memory 0))) + ) + "memory provided for instantiation has wrong type") +(assert_invalid + (module + (import "" (module $m + (import "" (memory 1)) + )) + (import "" (memory 0)) + (instance $i (instantiate $m (memory 0))) + ) + "memory provided for instantiation has wrong type") +(assert_invalid + (module + (import "" (module $m + (import "" (module + (import "" (func)) + )) + )) + (import "" (module $i + (import "" (global i32)) + )) + (instance $i (instantiate $m (module $i))) + ) + "wrong kind of item used for instantiate") +(assert_invalid + (module + (import "" (module $m + (import "" (module)) + )) + (import "" (module $i + (import "" (global i32)) + )) + (instance $i (instantiate $m (module $i))) + ) + "mismatched number of module imports") +(assert_invalid + (module + (import "" (module $m + (import "" (module + (import "" (global i32)) + (import "" (func)) + )) + )) + (import "" (module $i + (import "" (global i32)) + )) + (instance $i (instantiate $m (module $i))) + ) + "mismatched number of module imports") + +;; export subsets +(module + (import "" (module $m + (import "" (module + (export "" (func)) + )) + )) + (import "" (module $i + (export "" (func)) + (export "a" (func)) + )) + (instance $i (instantiate $m (module $i))) +) +(module + (import "" (module $m + (import "" (instance + (export "" (func)) + )) + )) + (import "" (instance $i + (export "" (func)) + (export "a" (func)) + )) + (instance (instantiate $m (instance $i))) +) diff --git a/tests/local/module-linking/module-link.wast b/tests/local/module-linking/module-link.wast new file mode 100644 index 0000000000..fa9b8e201e --- /dev/null +++ b/tests/local/module-linking/module-link.wast @@ -0,0 +1,65 @@ +(module + (type $Wasi (instance)) + (module $B) + (module $B_wrap + (import "wasi" (instance $wasi (type $Wasi))) + (instance $b (instantiate $B)) + (export $b) + ) +) +(module + (type $Wasi (instance)) + (import "wasi" (instance $wasi (type $Wasi))) + + (module $A + (type $Wasi (instance)) + (import "wasi" (instance (type $Wasi))) + (func (export "a")) + ) + + (module $B + (type $Wasi (instance)) + (import "wasi" (instance $wasi (type $Wasi))) + (import "A:1.x" (module $A + (import "wasi" (instance (type $Wasi))) + (export "a" (func)) + )) + (instance $a (instantiate $A (instance $wasi))) + (func (export "b")) + ) + (module $B_wrap + (import "wasi" (instance $wasi (type $Wasi))) + (instance $b (instantiate $B (instance $wasi) (module $A))) + (export $b) + ) + + (module $C + (type $Wasi (instance)) + (import "wasi" (instance $wasi (type $Wasi))) + (import "B:1.x" (module $B + (import "wasi" (instance $wasi (type $Wasi))) + (export "b" (func)) + )) + (instance $b (instantiate $B (instance $wasi))) + (func (export "c")) + ) + (module $C_wrap + (import "wasi" (instance $wasi (type $Wasi))) + (instance $c (instantiate $C (instance $wasi) (module $B_wrap))) + (export $c) + ) + + (module $D + (type $Wasi (instance)) + (import "wasi" (instance $wasi (type $Wasi))) + (import "C:1.x" (module $C + (import "wasi" (instance $wasi (type $Wasi))) + (export "c" (func)) + )) + (instance $c (instantiate $C (instance $wasi))) + (func (export "d")) + ) + + (instance $d (instantiate $D (instance $wasi) (module $C_wrap))) + (export $d) +) diff --git a/tests/local/module-linking/nested-modules.wast b/tests/local/module-linking/nested-modules.wast new file mode 100644 index 0000000000..96abb6fb29 --- /dev/null +++ b/tests/local/module-linking/nested-modules.wast @@ -0,0 +1,31 @@ +(module + (module (import "")) + + (module) + (module) + + (type $f (module)) + (module (type $f)) + + (module (export "x")) + + (module + (module) + ) + + (module + (module $m) + (import "" (func (param i32))) + (export "a" (module $m)) + + (instance (export "b") (import "") + (export "b" (func)) + ) + ) +) + +;; does the `import` use the type annotation specified later? +(module + (import "" (module)) + (type (module)) +) diff --git a/tests/local/module-linking/single-import.wat b/tests/local/module-linking/single-import.wat new file mode 100644 index 0000000000..7da4134c45 --- /dev/null +++ b/tests/local/module-linking/single-import.wat @@ -0,0 +1,10 @@ +(module + (import "" (func)) + (import "" (module + (import "" (func)) + )) + (func (import "")) + (global (import "") i32) + (table (import "") 1 funcref) + (memory (import "") 1) +) diff --git a/tests/local/module-linking/virtualize.wast b/tests/local/module-linking/virtualize.wast new file mode 100644 index 0000000000..fdd21c39db --- /dev/null +++ b/tests/local/module-linking/virtualize.wast @@ -0,0 +1,80 @@ +(module $child + (import "wasi_file" (instance $wasi-file + (export "read" (func $read (param i32 i32 i32) (result i32))) + (export "write" (func $write (param i32 i32 i32) (result i32))) + )) + (func $play (export "play") + i32.const 0 + i32.const 0 + i32.const 0 + call $wasi-file.$read + drop + ) +) + + +(module $virtualize + (import "wasi_file" (instance $wasi-file + (export "read" (func $read (param i32 i32 i32) (result i32))) + (export "write" (func $write (param i32 i32 i32) (result i32))) + )) + (export $wasi-file) +) + + +;; parent.wat +(module + (type $WasiFile (instance + (export "read" (func $read (param i32 i32 i32) (result i32))) + (export "write" (func $write (param i32 i32 i32) (result i32))) + )) + (import "wasi_file" (instance $real-wasi (type $WasiFile))) + (import "./virtualize.wasm" (module $VIRTUALIZE + (import "wasi_file" (instance (type $WasiFile))) + (export $WasiFile) + )) + (import "./child.wasm" (module $CHILD + (import "wasi_file" (instance (type $WasiFile))) + (export "play" (func $play)) + )) + (instance $virt-wasi (instantiate $VIRTUALIZE (instance $real-wasi))) + (instance $child (instantiate $CHILD (instance $virt-wasi))) + (func (export "work") + call $child.$play + ) +) + + +;; bundled.wat +(module + (type $WasiFile (instance + (export "read" (func $read (param i32 i32 i32) (result i32))) + (export "write" (func $write (param i32 i32 i32) (result i32))) + )) + (import "wasi_file" (instance $real-wasi (type $WasiFile))) + + (module $CHILD + (import "wasi_file" (instance $wasi-file (type $WasiFile))) + (alias $wasi-file.$read (instance $wasi-file) (func 0)) + (func $play (export "play") + call $wasi-file.$read + ) + ) + + + (module $VIRTUALIZE + (import "wasi_file" (instance $wasi-file (type $WasiFile))) + (func (export "read") (param i32 i32 i32) (result i32) + i32.const 0 + ) + (func (export "write") (param i32 i32 i32) (result i32) + i32.const 0 + ) + ) + + (instance $virt-wasi (instantiate $VIRTUALIZE (instance $real-wasi))) + (instance $child (instantiate $CHILD (instance $virt-wasi))) + (func (export "work") + call $child.$play + ) +) diff --git a/tests/roundtrip.rs b/tests/roundtrip.rs index 8d8f929364..6c9901ef50 100644 --- a/tests/roundtrip.rs +++ b/tests/roundtrip.rs @@ -225,8 +225,10 @@ impl TestState { && !test.ends_with("dump/import.txt") // uses exceptions && !test.ends_with("parse/all-features.txt") + && !test.iter().any(|t| t == "module-linking") { - self.test_wasm(test, &binary, true)?; + self.test_wasm(test, &binary, true) + .context("failed testing the binary output of `wat`")?; } Ok(()) } @@ -247,6 +249,9 @@ impl TestState { // FIXME(WebAssembly/wabt#1447) && !test.ends_with("bulk-memory-operations/binary.wast") && !test.ends_with("reference-types/binary.wast") + + // not implemented in wabt + && !test.iter().any(|t| t == "module-linking") { if let Some(expected) = self.wasm2wat(contents)? { self.string_compare(&string, &expected)?; @@ -256,9 +261,11 @@ impl TestState { // If we can, convert the string back to bytes and assert it has the // same binary representation. if test_roundtrip { - let binary2 = wat::parse_str(&string)?; + let binary2 = + wat::parse_str(&string).context("failed to parse `wat` from `wasmprinter`")?; self.bump_ntests(); - self.binary_compare(&binary2, contents, false)?; + self.binary_compare(&binary2, contents, false) + .context("failed to compare original `wat` with roundtrip `wat`")?; } Ok(()) @@ -314,7 +321,11 @@ impl TestState { return Ok(()); } let mut s = format!("{} test failures in {}:", errors.len(), test.display()); - for error in errors { + for mut error in errors { + if let Some(err) = error.downcast_mut::() { + err.set_path(test); + err.set_text(contents); + } s.push_str("\n\n\t--------------------------------\n\n\t"); s.push_str(&format!("{:?}", error).replace("\n", "\n\t")); } @@ -349,7 +360,8 @@ impl TestState { // with wabt which does further parsing. ModuleKind::Binary(_) => false, }; - self.test_wasm(test, &actual, test_roundtrip)?; + self.test_wasm(test, &actual, test_roundtrip) + .context("failed testing wasm binary produced by `wast`")?; } WastDirective::QuoteModule { source, span: _ } => { @@ -645,6 +657,7 @@ impl TestState { enable_bulk_memory: true, enable_multi_value: true, enable_tail_call: true, + enable_module_linking: true, }, }; for part in test.iter().filter_map(|t| t.to_str()) { @@ -655,6 +668,7 @@ impl TestState { config.operator_config.enable_simd = false; config.operator_config.enable_bulk_memory = false; config.operator_config.enable_tail_call = false; + config.operator_config.enable_module_linking = false; } "threads" => config.operator_config.enable_threads = true, "simd" => config.operator_config.enable_simd = true,