From 5ee40307397dad725ef9d793823f9a6dc09c0a01 Mon Sep 17 00:00:00 2001 From: Andrew Brown Date: Tue, 10 Sep 2024 10:28:44 -0700 Subject: [PATCH] Allow parsing `rectypes` in components (#1764) * Allow parsing `rectypes` in components This change supports [#392], which adds a way to use GC's `rectypes` as type definitions in components. Previously, only function types were supported and there was no way express array and struct types. This keeps the previous function decoding support based on peeking the function type `0x60` prefix but adds support for encoding `rectypes` with a new `0x00` prefix. [#392]: https://github.com/WebAssembly/component-model/pull/392 Co-authored-by: Alex Crichton * Apply `0x00` prefix to non-final `sub`; add tests This follows along with the most recent discussion in the component model PR ([#392]). [#392]: https://github.com/WebAssembly/component-model/pull/392 Co-authored-by: Alex Crichton * review: keep variant as `ComponentCoreTypeId::Sub` * review: remove leftover comment * review: remove resolved TODOs * review: move `From` implementations to `core/binary.rs` * review: remove `parse_component_sub_type` --------- Co-authored-by: Alex Crichton --- crates/wasm-compose/src/encoding.rs | 2 +- crates/wasm-encoder/src/component/builder.rs | 2 +- crates/wasm-encoder/src/component/types.rs | 70 ++---- crates/wasm-encoder/src/core/code.rs | 2 +- crates/wasm-encoder/src/core/types.rs | 200 ++++++++++-------- crates/wasm-encoder/src/lib.rs | 2 +- crates/wasm-encoder/src/reencode.rs | 15 +- crates/wasm-encoder/src/reencode/component.rs | 48 +---- crates/wasm-mutate/src/mutators/add_type.rs | 6 +- crates/wasm-mutate/src/mutators/translate.rs | 2 +- crates/wasm-smith/src/component/encode.rs | 5 +- crates/wasm-smith/src/core/encode.rs | 18 +- .../wasmparser/src/readers/component/types.rs | 30 ++- crates/wasmparser/src/validator/component.rs | 19 +- crates/wasmparser/src/validator/types.rs | 2 +- crates/wasmparser/tests/big-module.rs | 2 +- crates/wasmprinter/src/lib.rs | 91 ++++---- crates/wast/src/component/binary.rs | 56 +++-- crates/wast/src/core/binary.rs | 58 +++++ crates/wit-component/src/encoding.rs | 4 +- crates/wit-component/src/gc.rs | 8 +- crates/wit-component/src/linking.rs | 18 +- src/bin/wasm-tools/component.rs | 4 +- tests/cli/dump/import-modules.wat.stdout | 2 +- tests/cli/dump/module-types.wat.stdout | 2 +- tests/local/component-model/gc.wast | 54 +++++ .../local/component-model/gc.wast.json | 34 +++ .../local/component-model/gc.wast/0.print | 3 + .../local/component-model/gc.wast/1.print | 6 + .../local/component-model/gc.wast/2.print | 7 + 30 files changed, 439 insertions(+), 333 deletions(-) create mode 100644 tests/local/component-model/gc.wast create mode 100644 tests/snapshots/local/component-model/gc.wast.json create mode 100644 tests/snapshots/local/component-model/gc.wast/0.print create mode 100644 tests/snapshots/local/component-model/gc.wast/1.print create mode 100644 tests/snapshots/local/component-model/gc.wast/2.print diff --git a/crates/wasm-compose/src/encoding.rs b/crates/wasm-compose/src/encoding.rs index daa2bd98a9..468ceb9782 100644 --- a/crates/wasm-compose/src/encoding.rs +++ b/crates/wasm-compose/src/encoding.rs @@ -68,7 +68,7 @@ impl Encodable { } } - fn core_type(&mut self) -> CoreTypeEncoder { + fn core_type(&mut self) -> ComponentCoreTypeEncoder { match self { Encodable::Component(t) => t.core_type(), Encodable::Instance(t) => t.core_type(), diff --git a/crates/wasm-encoder/src/component/builder.rs b/crates/wasm-encoder/src/component/builder.rs index 66f5fe41cf..12e73ca464 100644 --- a/crates/wasm-encoder/src/component/builder.rs +++ b/crates/wasm-encoder/src/component/builder.rs @@ -299,7 +299,7 @@ impl ComponentBuilder { } /// Creates a new encoder for the next core type in this component. - pub fn core_type(&mut self) -> (u32, CoreTypeEncoder<'_>) { + pub fn core_type(&mut self) -> (u32, ComponentCoreTypeEncoder<'_>) { (inc(&mut self.core_types), self.core_types().ty()) } diff --git a/crates/wasm-encoder/src/component/types.rs b/crates/wasm-encoder/src/component/types.rs index aa79d69c94..39853d5f7b 100644 --- a/crates/wasm-encoder/src/component/types.rs +++ b/crates/wasm-encoder/src/component/types.rs @@ -1,7 +1,7 @@ use super::CORE_TYPE_SORT; use crate::{ encode_section, Alias, ComponentExportKind, ComponentOuterAliasKind, ComponentSection, - ComponentSectionId, ComponentTypeRef, Encode, EntityType, ValType, + ComponentSectionId, ComponentTypeRef, CoreTypeEncoder, Encode, EntityType, ValType, }; /// Represents the type of a core module. @@ -36,7 +36,10 @@ impl ModuleType { self.bytes.push(0x01); self.num_added += 1; self.types_added += 1; - CoreTypeEncoder(&mut self.bytes) + CoreTypeEncoder { + push_prefix_if_component_core_type: false, + bytes: &mut self.bytes, + } } /// Defines an outer core type alias in this module type. @@ -76,31 +79,22 @@ impl Encode for ModuleType { /// Used to encode core types. #[derive(Debug)] -pub struct CoreTypeEncoder<'a>(pub(crate) &'a mut Vec); - -impl<'a> CoreTypeEncoder<'a> { - /// Define a function type. - pub fn function(self, params: P, results: R) - where - P: IntoIterator, - P::IntoIter: ExactSizeIterator, - R: IntoIterator, - R::IntoIter: ExactSizeIterator, - { - let params = params.into_iter(); - let results = results.into_iter(); - - self.0.push(0x60); - params.len().encode(self.0); - params.for_each(|p| p.encode(self.0)); - results.len().encode(self.0); - results.for_each(|p| p.encode(self.0)); - } +pub struct ComponentCoreTypeEncoder<'a>(pub(crate) &'a mut Vec); +impl<'a> ComponentCoreTypeEncoder<'a> { /// Define a module type. pub fn module(self, ty: &ModuleType) { ty.encode(self.0); } + + /// Define any core type other than a module type. + #[must_use = "the encoder must be used to encode the type"] + pub fn core(self) -> CoreTypeEncoder<'a> { + CoreTypeEncoder { + bytes: self.0, + push_prefix_if_component_core_type: true, + } + } } /// An encoder for the core type section of WebAssembly components. @@ -112,7 +106,7 @@ impl<'a> CoreTypeEncoder<'a> { /// /// let mut types = CoreTypeSection::new(); /// -/// types.module(&ModuleType::new()); +/// types.ty().module(&ModuleType::new()); /// /// let mut component = Component::new(); /// component.section(&types); @@ -145,29 +139,9 @@ impl CoreTypeSection { /// /// The returned encoder must be finished before adding another type. #[must_use = "the encoder must be used to encode the type"] - pub fn ty(&mut self) -> CoreTypeEncoder<'_> { + pub fn ty(&mut self) -> ComponentCoreTypeEncoder<'_> { self.num_added += 1; - CoreTypeEncoder(&mut self.bytes) - } - - /// Define a function type in this type section. - pub fn function(&mut self, params: P, results: R) -> &mut Self - where - P: IntoIterator, - P::IntoIter: ExactSizeIterator, - R: IntoIterator, - R::IntoIter: ExactSizeIterator, - { - self.ty().function(params, results); - self - } - - /// Define a module type in this type section. - /// - /// Currently this is only used for core type sections in components. - pub fn module(&mut self, ty: &ModuleType) -> &mut Self { - self.ty().module(ty); - self + ComponentCoreTypeEncoder(&mut self.bytes) } } @@ -203,11 +177,11 @@ impl ComponentType { /// /// The returned encoder must be used before adding another definition. #[must_use = "the encoder must be used to encode the type"] - pub fn core_type(&mut self) -> CoreTypeEncoder { + pub fn core_type(&mut self) -> ComponentCoreTypeEncoder { self.bytes.push(0x00); self.num_added += 1; self.core_types_added += 1; - CoreTypeEncoder(&mut self.bytes) + ComponentCoreTypeEncoder(&mut self.bytes) } /// Define a type in this component type. @@ -316,7 +290,7 @@ impl InstanceType { /// /// The returned encoder must be used before adding another definition. #[must_use = "the encoder must be used to encode the type"] - pub fn core_type(&mut self) -> CoreTypeEncoder { + pub fn core_type(&mut self) -> ComponentCoreTypeEncoder { self.0.core_type() } diff --git a/crates/wasm-encoder/src/core/code.rs b/crates/wasm-encoder/src/core/code.rs index 6b23235b54..f954c5bd8f 100644 --- a/crates/wasm-encoder/src/core/code.rs +++ b/crates/wasm-encoder/src/core/code.rs @@ -14,7 +14,7 @@ use std::borrow::Cow; /// }; /// /// let mut types = TypeSection::new(); -/// types.function(vec![], vec![ValType::I32]); +/// types.ty().function(vec![], vec![ValType::I32]); /// /// let mut functions = FunctionSection::new(); /// let type_index = 0; diff --git a/crates/wasm-encoder/src/core/types.rs b/crates/wasm-encoder/src/core/types.rs index 8b05ac4f66..bca27f8780 100644 --- a/crates/wasm-encoder/src/core/types.rs +++ b/crates/wasm-encoder/src/core/types.rs @@ -5,24 +5,13 @@ use crate::{encode_section, Encode, Section, SectionId}; pub struct SubType { /// Is the subtype final. pub is_final: bool, - /// The list of supertype indexes. As of GC MVP, there can be at most one supertype. + /// The list of supertype indexes. As of GC MVP, there can be at most one + /// supertype. pub supertype_idx: Option, /// The composite type of the subtype. pub composite_type: CompositeType, } -impl Encode for SubType { - fn encode(&self, sink: &mut Vec) { - // We only need to emit a prefix byte before the actual composite type - // when either the type is not final or it has a declared super type. - if self.supertype_idx.is_some() || !self.is_final { - sink.push(if self.is_final { 0x4f } else { 0x50 }); - self.supertype_idx.encode(sink); - } - self.composite_type.encode(sink); - } -} - /// Represents a composite type in a WebAssembly module. #[derive(Debug, Clone)] pub struct CompositeType { @@ -33,27 +22,6 @@ pub struct CompositeType { pub shared: bool, } -impl Encode for CompositeType { - fn encode(&self, sink: &mut Vec) { - if self.shared { - sink.push(0x65); - } - match &self.inner { - CompositeInnerType::Func(ty) => TypeSection::encode_function( - sink, - ty.params().iter().copied(), - ty.results().iter().copied(), - ), - CompositeInnerType::Array(ArrayType(ty)) => { - TypeSection::encode_array(sink, &ty.element_type, ty.mutable) - } - CompositeInnerType::Struct(ty) => { - TypeSection::encode_struct(sink, ty.fields.iter().cloned()) - } - } - } -} - /// A [`CompositeType`] can contain one of these types. #[derive(Debug, Clone)] pub enum CompositeInnerType { @@ -506,7 +474,7 @@ impl Encode for AbstractHeapType { /// /// let mut types = TypeSection::new(); /// -/// types.function([ValType::I32, ValType::I32], [ValType::I64]); +/// types.ty().function([ValType::I32, ValType::I32], [ValType::I64]); /// /// let mut module = Module::new(); /// module.section(&types); @@ -535,31 +503,61 @@ impl TypeSection { self.num_added == 0 } + /// Encode a function type in this type section. + #[must_use = "the encoder must be used to encode the type"] + pub fn ty(&mut self) -> CoreTypeEncoder { + self.num_added += 1; + CoreTypeEncoder { + bytes: &mut self.bytes, + push_prefix_if_component_core_type: false, + } + } +} + +impl Encode for TypeSection { + fn encode(&self, sink: &mut Vec) { + encode_section(sink, self.num_added, &self.bytes); + } +} + +impl Section for TypeSection { + fn id(&self) -> u8 { + SectionId::Type.into() + } +} + +/// A single-use encoder for encoding a type; this forces all encoding for a +/// type to be done in a single shot. +#[derive(Debug)] +pub struct CoreTypeEncoder<'a> { + pub(crate) bytes: &'a mut Vec, + // For the time being, this flag handles an ambiguous encoding in the + // component model: the `0x50` opcode represents both a core module type as + // well as a GC non-final `sub` type. To avoid this, the component model + // specification requires us to prefix a non-final `sub` type with `0x00` + // when it is used as a top-level core type of a component. Eventually + // (prior to the component model's v1.0 release), a module type will get a + // new opcode and this special logic can go away. + pub(crate) push_prefix_if_component_core_type: bool, +} +impl<'a> CoreTypeEncoder<'a> { /// Define a function type in this type section. - pub fn function(&mut self, params: P, results: R) -> &mut Self + pub fn function(mut self, params: P, results: R) where P: IntoIterator, P::IntoIter: ExactSizeIterator, R: IntoIterator, R::IntoIter: ExactSizeIterator, { - Self::encode_function(&mut self.bytes, params, results); - self.num_added += 1; - self + self.encode_function(params, results); } /// Define a function type in this type section. - pub fn func_type(&mut self, ty: &FuncType) -> &mut Self { - Self::encode_function( - &mut self.bytes, - ty.params().iter().cloned(), - ty.results().iter().cloned(), - ); - self.num_added += 1; - self + pub fn func_type(mut self, ty: &FuncType) { + self.encode_function(ty.params().iter().cloned(), ty.results().iter().cloned()); } - fn encode_function(sink: &mut Vec, params: P, results: R) + fn encode_function(&mut self, params: P, results: R) where P: IntoIterator, P::IntoIter: ExactSizeIterator, @@ -569,85 +567,101 @@ impl TypeSection { let params = params.into_iter(); let results = results.into_iter(); - sink.push(0x60); - params.len().encode(sink); - params.for_each(|p| p.encode(sink)); - results.len().encode(sink); - results.for_each(|p| p.encode(sink)); + self.bytes.push(0x60); + params.len().encode(self.bytes); + params.for_each(|p| p.encode(self.bytes)); + results.len().encode(self.bytes); + results.for_each(|p| p.encode(self.bytes)); } /// Define an array type in this type section. - pub fn array(&mut self, ty: &StorageType, mutable: bool) -> &mut Self { - Self::encode_array(&mut self.bytes, ty, mutable); - self.num_added += 1; - self + pub fn array(mut self, ty: &StorageType, mutable: bool) { + self.encode_array(ty, mutable); } - fn encode_array(sink: &mut Vec, ty: &StorageType, mutable: bool) { - sink.push(0x5e); - Self::encode_field(sink, ty, mutable); + fn encode_array(&mut self, ty: &StorageType, mutable: bool) { + self.bytes.push(0x5e); + self.encode_field(ty, mutable); } - fn encode_field(sink: &mut Vec, ty: &StorageType, mutable: bool) { - ty.encode(sink); - sink.push(mutable as u8); + fn encode_field(&mut self, ty: &StorageType, mutable: bool) { + ty.encode(self.bytes); + self.bytes.push(mutable as u8); } /// Define a struct type in this type section. - pub fn struct_(&mut self, fields: F) -> &mut Self + pub fn struct_(mut self, fields: F) where F: IntoIterator, F::IntoIter: ExactSizeIterator, { - Self::encode_struct(&mut self.bytes, fields); - self.num_added += 1; - self + self.encode_struct(fields); } - fn encode_struct(sink: &mut Vec, fields: F) + fn encode_struct(&mut self, fields: F) where F: IntoIterator, F::IntoIter: ExactSizeIterator, { let fields = fields.into_iter(); - sink.push(0x5f); - fields.len().encode(sink); + self.bytes.push(0x5f); + fields.len().encode(self.bytes); for f in fields { - Self::encode_field(sink, &f.element_type, f.mutable); + self.encode_field(&f.element_type, f.mutable); } } /// Define an explicit subtype in this type section. - pub fn subtype(&mut self, ty: &SubType) -> &mut Self { - ty.encode(&mut self.bytes); - self.num_added += 1; - self + pub fn subtype(mut self, ty: &SubType) { + self.encode_subtype(ty) + } + + /// Define an explicit subtype in this type section. + fn encode_subtype(&mut self, ty: &SubType) { + // We only need to emit a prefix byte before the actual composite type + // when either the `sub` type is not final or it has a declared super + // type (see notes on `push_prefix_if_component_core_type`). + if ty.supertype_idx.is_some() || !ty.is_final { + if ty.is_final { + self.bytes.push(0x4f); + } else { + if self.push_prefix_if_component_core_type { + self.bytes.push(0x00); + } + self.bytes.push(0x50); + } + ty.supertype_idx.encode(self.bytes); + } + if ty.composite_type.shared { + self.bytes.push(0x65); + } + match &ty.composite_type.inner { + CompositeInnerType::Func(ty) => { + self.encode_function(ty.params().iter().copied(), ty.results().iter().copied()) + } + CompositeInnerType::Array(ArrayType(ty)) => { + self.encode_array(&ty.element_type, ty.mutable) + } + CompositeInnerType::Struct(ty) => self.encode_struct(ty.fields.iter().cloned()), + } } /// Define an explicit recursion group in this type section. - pub fn rec(&mut self, types: T) -> &mut Self + pub fn rec(mut self, types: T) where T: IntoIterator, T::IntoIter: ExactSizeIterator, { + // When emitting a `rec` group, we will never emit `sub`'s special + // `0x00` prefix; that is only necessary when `sub` is not wrapped by + // `rec` (see notes on `push_prefix_if_component_core_type`). + self.push_prefix_if_component_core_type = false; let types = types.into_iter(); self.bytes.push(0x4e); - types.len().encode(&mut self.bytes); - types.for_each(|t| t.encode(&mut self.bytes)); - self.num_added += 1; - self - } -} - -impl Encode for TypeSection { - fn encode(&self, sink: &mut Vec) { - encode_section(sink, self.num_added, &self.bytes); - } -} - -impl Section for TypeSection { - fn id(&self) -> u8 { - SectionId::Type.into() + types.len().encode(self.bytes); + types.for_each(|t| { + self.encode_subtype(&t); + }); } } @@ -660,7 +674,7 @@ mod tests { #[test] fn func_types_dont_require_wasm_gc() { let mut types = TypeSection::new(); - types.subtype(&SubType { + types.ty().subtype(&SubType { is_final: true, supertype_idx: None, composite_type: CompositeType { diff --git a/crates/wasm-encoder/src/lib.rs b/crates/wasm-encoder/src/lib.rs index 756bf517d0..3ea386625f 100644 --- a/crates/wasm-encoder/src/lib.rs +++ b/crates/wasm-encoder/src/lib.rs @@ -36,7 +36,7 @@ //! let mut types = TypeSection::new(); //! let params = vec![ValType::I32, ValType::I32]; //! let results = vec![ValType::I32]; -//! types.function(params, results); +//! types.ty().function(params, results); //! module.section(&types); //! //! // Encode the function section. diff --git a/crates/wasm-encoder/src/reencode.rs b/crates/wasm-encoder/src/reencode.rs index 83fc5286b2..918a8fb42e 100644 --- a/crates/wasm-encoder/src/reencode.rs +++ b/crates/wasm-encoder/src/reencode.rs @@ -3,6 +3,7 @@ //! The [`RoundtripReencoder`] allows encoding identical wasm to the parsed //! input. +use crate::CoreTypeEncoder; use std::convert::Infallible; mod component; @@ -421,10 +422,10 @@ pub trait Reencode { /// Parses a single [`wasmparser::RecGroup`] and adds it to the `types` section. fn parse_recursive_type_group( &mut self, - types: &mut crate::TypeSection, + encoder: CoreTypeEncoder, rec_group: wasmparser::RecGroup, ) -> Result<(), Error> { - utils::parse_recursive_type_group(self, types, rec_group) + utils::parse_recursive_type_group(self, encoder, rec_group) } fn parse_unknown_section( @@ -568,7 +569,7 @@ impl Reencode for RoundtripReencoder { #[allow(missing_docs)] // FIXME pub mod utils { use super::{Error, Reencode}; - use crate::Encode; + use crate::{CoreTypeEncoder, Encode}; pub fn parse_core_module( reencoder: &mut T, @@ -992,7 +993,7 @@ pub mod utils { section: wasmparser::TypeSectionReader<'_>, ) -> Result<(), Error> { for rec_group in section { - reencoder.parse_recursive_type_group(types, rec_group?)?; + reencoder.parse_recursive_type_group(types.ty(), rec_group?)?; } Ok(()) } @@ -1000,7 +1001,7 @@ pub mod utils { /// Parses a single [`wasmparser::RecGroup`] and adds it to the `types` section. pub fn parse_recursive_type_group( reencoder: &mut T, - types: &mut crate::TypeSection, + encoder: CoreTypeEncoder, rec_group: wasmparser::RecGroup, ) -> Result<(), Error> { if rec_group.is_explicit_rec_group() { @@ -1008,10 +1009,10 @@ pub mod utils { .into_types() .map(|t| reencoder.sub_type(t)) .collect::, _>>()?; - types.rec(subtypes); + encoder.rec(subtypes); } else { let ty = rec_group.into_types().next().unwrap(); - types.subtype(&reencoder.sub_type(ty)?); + encoder.subtype(&reencoder.sub_type(ty)?); } Ok(()) } diff --git a/crates/wasm-encoder/src/reencode/component.rs b/crates/wasm-encoder/src/reencode/component.rs index b0d7139a53..771b9c2e96 100644 --- a/crates/wasm-encoder/src/reencode/component.rs +++ b/crates/wasm-encoder/src/reencode/component.rs @@ -162,7 +162,7 @@ pub trait ReencodeComponent: Reencode { fn parse_component_core_type( &mut self, - ty: crate::CoreTypeEncoder<'_>, + ty: crate::ComponentCoreTypeEncoder<'_>, core: wasmparser::CoreType<'_>, ) -> Result<(), Error> { component_utils::parse_component_core_type(self, ty, core) @@ -221,14 +221,6 @@ pub trait ReencodeComponent: Reencode { component_utils::component_alias(self, alias) } - fn parse_component_sub_type( - &mut self, - ty: crate::CoreTypeEncoder<'_>, - sub: wasmparser::SubType, - ) -> Result<(), Error> { - component_utils::parse_component_sub_type(self, ty, sub) - } - fn parse_component_import_section( &mut self, imports: &mut crate::ComponentImportSection, @@ -668,11 +660,13 @@ pub mod component_utils { pub fn parse_component_core_type( reencoder: &mut T, - ty: crate::CoreTypeEncoder<'_>, + ty: crate::ComponentCoreTypeEncoder<'_>, decl: wasmparser::CoreType<'_>, ) -> Result<(), Error> { match decl { - wasmparser::CoreType::Sub(core) => reencoder.parse_component_sub_type(ty, core)?, + wasmparser::CoreType::Rec(rec) => { + reencoder.parse_recursive_type_group(ty.core(), rec)?; + } wasmparser::CoreType::Module(decls) => { ty.module(&reencoder.component_module_type(decls)?); } @@ -821,8 +815,8 @@ pub mod component_utils { decl: wasmparser::ModuleTypeDeclaration<'_>, ) -> Result<(), Error> { match decl { - wasmparser::ModuleTypeDeclaration::Type(ty) => { - reencoder.parse_component_sub_type(module.ty(), ty)? + wasmparser::ModuleTypeDeclaration::Type(rec) => { + reencoder.parse_recursive_type_group(module.ty(), rec)?; } wasmparser::ModuleTypeDeclaration::Export { name, ty } => { module.export(name, reencoder.entity_type(ty)?); @@ -890,34 +884,6 @@ pub mod component_utils { } } - pub fn parse_component_sub_type( - reencoder: &mut T, - ty: crate::CoreTypeEncoder<'_>, - sub: wasmparser::SubType, - ) -> Result<(), Error> { - if !sub.is_final || sub.supertype_idx.is_some() || sub.composite_type.shared { - return Err(Error::UnsupportedCoreTypeInComponent); - } - - let func = match sub.composite_type.inner { - wasmparser::CompositeInnerType::Func(f) => f, - _ => return Err(Error::UnsupportedCoreTypeInComponent), - }; - - ty.function( - func.params() - .iter() - .map(|t| reencoder.val_type(*t)) - .collect::, _>>()?, - func.results() - .iter() - .map(|t| reencoder.val_type(*t)) - .collect::, _>>()?, - ); - - Ok(()) - } - pub fn parse_component_import_section( reencoder: &mut T, imports: &mut crate::ComponentImportSection, diff --git a/crates/wasm-mutate/src/mutators/add_type.rs b/crates/wasm-mutate/src/mutators/add_type.rs index f007c75783..3bbdbc4041 100644 --- a/crates/wasm-mutate/src/mutators/add_type.rs +++ b/crates/wasm-mutate/src/mutators/add_type.rs @@ -70,16 +70,16 @@ impl Mutator for AddTypeMutator { .copied() .map(map_type) .collect::, _>>()?; - types.function(params, results); + types.ty().function(params, results); } // And then add our new type. - types.function(params, results); + types.ty().function(params, results); let types_section_index = config.info().types.unwrap(); Ok(Box::new(iter::once(Ok(config .info() .replace_section(types_section_index, &types))))) } else { - types.function(params, results); + types.ty().function(params, results); Ok(Box::new(iter::once(Ok(config .info() .insert_section(0, &types))))) diff --git a/crates/wasm-mutate/src/mutators/translate.rs b/crates/wasm-mutate/src/mutators/translate.rs index d1ac04c532..db60a0e5f3 100644 --- a/crates/wasm-mutate/src/mutators/translate.rs +++ b/crates/wasm-mutate/src/mutators/translate.rs @@ -134,7 +134,7 @@ pub fn func_type( ty: wasmparser::FuncType, s: &mut TypeSection, ) -> Result<()> { - s.function( + s.ty().function( ty.params() .iter() .map(|ty| t.translate_ty(ty)) diff --git a/crates/wasm-smith/src/component/encode.rs b/crates/wasm-smith/src/component/encode.rs index 3d53f2bfb5..5579e8e5cd 100644 --- a/crates/wasm-smith/src/component/encode.rs +++ b/crates/wasm-smith/src/component/encode.rs @@ -117,10 +117,11 @@ impl CoreTypeSection { } impl CoreType { - fn encode(&self, enc: wasm_encoder::CoreTypeEncoder<'_>) { + fn encode(&self, enc: wasm_encoder::ComponentCoreTypeEncoder<'_>) { match self { Self::Func(ty) => { - enc.function(ty.params.iter().copied(), ty.results.iter().copied()); + enc.core() + .function(ty.params.iter().copied(), ty.results.iter().copied()); } Self::Module(mod_ty) => { let mut enc_mod_ty = wasm_encoder::ModuleType::new(); diff --git a/crates/wasm-smith/src/core/encode.rs b/crates/wasm-smith/src/core/encode.rs index fc408e9c44..b7727996f8 100644 --- a/crates/wasm-smith/src/core/encode.rs +++ b/crates/wasm-smith/src/core/encode.rs @@ -36,21 +36,19 @@ impl Module { for group in &self.rec_groups { if group.end - group.start == 1 { let ty = &self.types[group.start]; - section.subtype(&wasm_encoder::SubType { + section.ty().subtype(&wasm_encoder::SubType { is_final: ty.is_final, supertype_idx: ty.supertype, composite_type: (&ty.composite_type).into(), }); } else { - section.rec( - self.types[group.clone()] - .iter() - .map(|ty| wasm_encoder::SubType { - is_final: ty.is_final, - supertype_idx: ty.supertype, - composite_type: (&ty.composite_type).into(), - }), - ); + section.ty().rec(self.types[group.clone()].iter().map(|ty| { + wasm_encoder::SubType { + is_final: ty.is_final, + supertype_idx: ty.supertype, + composite_type: (&ty.composite_type).into(), + } + })); } } diff --git a/crates/wasmparser/src/readers/component/types.rs b/crates/wasmparser/src/readers/component/types.rs index d707cb1b37..152c1c17d2 100644 --- a/crates/wasmparser/src/readers/component/types.rs +++ b/crates/wasmparser/src/readers/component/types.rs @@ -1,8 +1,9 @@ use crate::limits::*; use crate::prelude::*; +use crate::RecGroup; use crate::{ BinaryReader, ComponentAlias, ComponentExportName, ComponentImport, ComponentTypeRef, - FromReader, Import, Result, SectionLimited, SubType, TypeRef, ValType, + FromReader, Import, Result, SectionLimited, TypeRef, ValType, }; use core::fmt; @@ -17,19 +18,30 @@ pub enum OuterAliasKind { #[derive(Debug, Clone, Eq, PartialEq)] pub enum CoreType<'a> { /// The type is for a core subtype. - Sub(SubType), + Rec(RecGroup), /// The type is for a core module. Module(Box<[ModuleTypeDeclaration<'a>]>), } impl<'a> FromReader<'a> for CoreType<'a> { fn from_reader(reader: &mut BinaryReader<'a>) -> Result { + // For the time being, this special logic handles an ambiguous encoding + // in the component model: the `0x50` opcode represents both a core + // module type as well as a GC non-final `sub` type. To avoid this, the + // component model specification requires us to prefix a non-final `sub` + // type with `0x00` when it is used as a top-level core type of a + // component. Eventually (prior to the component model's v1.0 release), + // a module type will get a new opcode and this special logic can go + // away. Ok(match reader.peek()? { - 0x60 => CoreType::Sub(reader.read()?), - 0x5e | 0x5f => bail!( - reader.current_position(), - "no support for GC types in the component model yet" - ), + 0x00 => { + reader.read_u8()?; + let x = reader.peek()?; + if x != 0x50 { + return reader.invalid_leading_byte(x, "non-final sub type"); + } + CoreType::Rec(reader.read()?) + } 0x50 => { reader.read_u8()?; CoreType::Module( @@ -38,7 +50,7 @@ impl<'a> FromReader<'a> for CoreType<'a> { .collect::>()?, ) } - x => return reader.invalid_leading_byte(x, "core type"), + _ => CoreType::Rec(reader.read()?), }) } } @@ -47,7 +59,7 @@ impl<'a> FromReader<'a> for CoreType<'a> { #[derive(Debug, Clone, Eq, PartialEq)] pub enum ModuleTypeDeclaration<'a> { /// The module type definition is for a type. - Type(SubType), + Type(RecGroup), /// The module type definition is for an export. Export { /// The name of the exported item. diff --git a/crates/wasmparser/src/validator/component.rs b/crates/wasmparser/src/validator/component.rs index d29f896d74..7b4524fabc 100644 --- a/crates/wasmparser/src/validator/component.rs +++ b/crates/wasmparser/src/validator/component.rs @@ -266,13 +266,8 @@ impl ComponentState { check_max(current.type_count(), 1, MAX_WASM_TYPES, "types", offset)?; } match ty { - crate::CoreType::Sub(sub) => { - current.canonicalize_and_intern_rec_group( - features, - types, - RecGroup::implicit(offset, sub), - offset, - )?; + crate::CoreType::Rec(rec) => { + current.canonicalize_and_intern_rec_group(features, types, rec, offset)?; } crate::CoreType::Module(decls) => { let mod_ty = Self::create_module_type( @@ -1506,14 +1501,8 @@ impl ComponentState { for decl in decls { match decl { - crate::ModuleTypeDeclaration::Type(ty) => { - state.add_types( - RecGroup::implicit(offset, ty), - features, - types, - offset, - true, - )?; + crate::ModuleTypeDeclaration::Type(rec) => { + state.add_types(rec, features, types, offset, true)?; } crate::ModuleTypeDeclaration::Export { name, mut ty } => { let ty = state.check_type_ref(&mut ty, features, types, offset)?; diff --git a/crates/wasmparser/src/validator/types.rs b/crates/wasmparser/src/validator/types.rs index 2ed69c8fa3..aa0ace0166 100644 --- a/crates/wasmparser/src/validator/types.rs +++ b/crates/wasmparser/src/validator/types.rs @@ -257,7 +257,7 @@ pub enum CoreType { } /// Represents a unique identifier for a core type type known to a -/// [`crate::Validator`] +/// [`crate::Validator`]. #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[repr(C)] pub struct CoreTypeId { diff --git a/crates/wasmparser/tests/big-module.rs b/crates/wasmparser/tests/big-module.rs index 06cf02d630..68d4d7f0ab 100644 --- a/crates/wasmparser/tests/big-module.rs +++ b/crates/wasmparser/tests/big-module.rs @@ -6,7 +6,7 @@ fn big_type_indices() { let mut module = Module::new(); let mut types = TypeSection::new(); for _ in 0..N { - types.function([], []); + types.ty().function([], []); } module.section(&types); let mut funcs = FunctionSection::new(); diff --git a/crates/wasmprinter/src/lib.rs b/crates/wasmprinter/src/lib.rs index c682e92d58..40422e9fae 100644 --- a/crates/wasmprinter/src/lib.rs +++ b/crates/wasmprinter/src/lib.rs @@ -744,61 +744,59 @@ impl Printer<'_, '_> { } fn print_core_type(&mut self, states: &mut Vec, ty: CoreType) -> Result<()> { - self.start_group("core type ")?; - self.print_name( - &states.last().unwrap().core.type_names, - states.last().unwrap().core.types.len() as u32, - )?; - let ty = match ty { - CoreType::Sub(ty) => { - let ty = match &ty.composite_type.inner { - CompositeInnerType::Func(f) => f, - CompositeInnerType::Array(_) | CompositeInnerType::Struct(_) => { - unreachable!("Wasm GC types cannot appear in components yet") - } - }; - self.result.write_str(" ")?; - self.start_group("func")?; - self.print_func_type(states.last().unwrap(), &ty, None)?; - self.end_group()?; - let composite_type = CompositeType { - inner: CompositeInnerType::Func(ty.clone()), - shared: false, - }; - Some(SubType { - is_final: true, - supertype_idx: None, - composite_type, - }) + match ty { + CoreType::Rec(rec) => { + self.print_rec(states.last_mut().unwrap(), None, rec, true)?; } CoreType::Module(decls) => { + self.start_group("core type ")?; + self.print_name( + &states.last().unwrap().core.type_names, + states.last().unwrap().core.types.len() as u32, + )?; self.print_module_type(states, decls.into_vec())?; - None + self.end_group()?; // `core type` itself + states.last_mut().unwrap().core.types.push(None); } - }; - self.end_group()?; // `core type` itself - - states.last_mut().unwrap().core.types.push(ty); + } Ok(()) } fn print_rec( &mut self, state: &mut State, - offset: usize, - types: impl Iterator, + offset: Option, + rec: RecGroup, + is_component: bool, ) -> Result<()> { - self.start_group("rec")?; - for ty in types { - self.newline(offset + 2)?; - self.print_type(state, ty)?; + if rec.is_explicit_rec_group() { + if is_component { + self.start_group("core rec")?; + } else { + self.start_group("rec")?; + } + for ty in rec.into_types() { + match offset { + Some(offset) => self.newline(offset + 2)?, + None => self.newline_unknown_pos()?, + } + self.print_type(state, ty, false)?; + } + self.end_group()?; // `rec` + } else { + assert_eq!(rec.types().len(), 1); + let ty = rec.into_types().next().unwrap(); + self.print_type(state, ty, is_component)?; } - self.end_group()?; // `rec` Ok(()) } - fn print_type(&mut self, state: &mut State, ty: SubType) -> Result<()> { - self.start_group("type ")?; + fn print_type(&mut self, state: &mut State, ty: SubType, is_component: bool) -> Result<()> { + if is_component { + self.start_group("core type ")?; + } else { + self.start_group("type ")?; + } let ty_idx = state.core.types.len() as u32; self.print_name(&state.core.type_names, ty_idx)?; self.result.write_str(" ")?; @@ -869,15 +867,8 @@ impl Printer<'_, '_> { for ty in parser.into_iter_with_offsets() { let (offset, rec_group) = ty?; self.newline(offset)?; - if rec_group.is_explicit_rec_group() { - self.print_rec(state, offset, rec_group.into_types())? - } else { - assert_eq!(rec_group.types().len(), 1); - let ty = rec_group.into_types().next().unwrap(); - self.print_type(state, ty)?; - } + self.print_rec(state, Some(offset), rec_group, false)?; } - Ok(()) } @@ -1831,8 +1822,8 @@ impl Printer<'_, '_> { for decl in decls { self.newline_unknown_pos()?; match decl { - ModuleTypeDeclaration::Type(ty) => { - self.print_type(states.last_mut().unwrap(), ty)? + ModuleTypeDeclaration::Type(rec) => { + self.print_rec(states.last_mut().unwrap(), None, rec, false)? } ModuleTypeDeclaration::OuterAlias { kind, count, index } => { self.print_outer_alias(states, kind, count, index)?; diff --git a/crates/wast/src/component/binary.rs b/crates/wast/src/component/binary.rs index db1b8b5343..58bec50fa2 100644 --- a/crates/wast/src/component/binary.rs +++ b/crates/wast/src/component/binary.rs @@ -3,11 +3,12 @@ use crate::core; use crate::core::EncodeOptions; use crate::token::{Id, Index, NameAnnotation, Span}; use wasm_encoder::{ - CanonicalFunctionSection, ComponentAliasSection, ComponentDefinedTypeEncoder, - ComponentExportSection, ComponentImportSection, ComponentInstanceSection, ComponentNameSection, - ComponentSection, ComponentSectionId, ComponentStartSection, ComponentTypeEncoder, - ComponentTypeSection, CoreTypeEncoder, CoreTypeSection, InstanceSection, NameMap, - NestedComponentSection, RawSection, SectionId, + CanonicalFunctionSection, ComponentAliasSection, ComponentCoreTypeEncoder, + ComponentDefinedTypeEncoder, ComponentExportSection, ComponentImportSection, + ComponentInstanceSection, ComponentNameSection, ComponentSection, ComponentSectionId, + ComponentStartSection, ComponentTypeEncoder, ComponentTypeSection, CompositeType, + CoreTypeSection, InstanceSection, NameMap, NestedComponentSection, RawSection, SectionId, + SubType, }; pub fn encode(component: &Component<'_>, options: &EncodeOptions) -> Vec { @@ -55,23 +56,18 @@ fn encode_fields( e.component } -fn encode_core_type(encoder: CoreTypeEncoder, ty: &CoreTypeDef) { +fn encode_core_type(encoder: ComponentCoreTypeEncoder, ty: &CoreTypeDef) { match ty { CoreTypeDef::Def(def) => { - if def.shared { - todo!("encoding of shared types not yet implemented") - } - match &def.kind { - core::InnerTypeKind::Func(f) => { - encoder.function( - f.params.iter().map(|(_, _, ty)| (*ty).into()), - f.results.iter().copied().map(Into::into), - ); - } - core::InnerTypeKind::Struct(_) | core::InnerTypeKind::Array(_) => { - todo!("encoding of GC proposal types not yet implemented") - } - } + let sub_type = SubType { + is_final: true, + supertype_idx: None, + composite_type: CompositeType { + shared: def.shared, + inner: (&def.kind).into(), + }, + }; + encoder.core().subtype(&sub_type); } CoreTypeDef::Module(t) => { encoder.module(&t.into()); @@ -884,15 +880,17 @@ impl From<&ModuleType<'_>> for wasm_encoder::ModuleType { for decl in &ty.decls { match decl { - ModuleTypeDecl::Type(t) => match &t.def.kind { - core::InnerTypeKind::Func(f) => encoded.ty().function( - f.params.iter().map(|(_, _, ty)| (*ty).into()), - f.results.iter().copied().map(Into::into), - ), - core::InnerTypeKind::Struct(_) | core::InnerTypeKind::Array(_) => { - todo!("encoding of GC proposal types not yet implemented") - } - }, + ModuleTypeDecl::Type(t) => { + let sub_type = SubType { + is_final: t.final_type.unwrap_or(true), + supertype_idx: t.parent.map(u32::from), + composite_type: CompositeType { + shared: t.def.shared, + inner: (&t.def.kind).into(), + }, + }; + encoded.ty().subtype(&sub_type); + } ModuleTypeDecl::Alias(a) => match &a.target { AliasTarget::Outer { outer, diff --git a/crates/wast/src/core/binary.rs b/crates/wast/src/core/binary.rs index 8758270936..b68f4cf410 100644 --- a/crates/wast/src/core/binary.rs +++ b/crates/wast/src/core/binary.rs @@ -312,6 +312,15 @@ impl Encode for FunctionType<'_> { } } +impl From<&FunctionType<'_>> for wasm_encoder::FuncType { + fn from(ft: &FunctionType) -> Self { + wasm_encoder::FuncType::new( + ft.params.iter().map(|(_, _, ty)| (*ty).into()), + ft.results.iter().map(|ty| (*ty).into()), + ) + } +} + impl Encode for StructType<'_> { fn encode(&self, e: &mut Vec) { self.fields.len().encode(e); @@ -322,6 +331,23 @@ impl Encode for StructType<'_> { } } +impl From<&StructType<'_>> for wasm_encoder::StructType { + fn from(st: &StructType) -> wasm_encoder::StructType { + wasm_encoder::StructType { + fields: st.fields.iter().map(|f| f.into()).collect(), + } + } +} + +impl From<&StructField<'_>> for wasm_encoder::FieldType { + fn from(f: &StructField) -> wasm_encoder::FieldType { + wasm_encoder::FieldType { + element_type: f.ty.into(), + mutable: f.mutable, + } + } +} + impl Encode for ArrayType<'_> { fn encode(&self, e: &mut Vec) { self.ty.encode(e); @@ -329,6 +355,16 @@ impl Encode for ArrayType<'_> { } } +impl From<&ArrayType<'_>> for wasm_encoder::ArrayType { + fn from(at: &ArrayType) -> Self { + let field = wasm_encoder::FieldType { + element_type: at.ty.into(), + mutable: at.mutable, + }; + wasm_encoder::ArrayType(field) + } +} + impl Encode for ExportType<'_> { fn encode(&self, e: &mut Vec) { self.name.encode(e); @@ -392,6 +428,17 @@ impl Encode for Type<'_> { } } +impl From<&InnerTypeKind<'_>> for wasm_encoder::CompositeInnerType { + fn from(kind: &InnerTypeKind) -> Self { + use wasm_encoder::CompositeInnerType::*; + match kind { + InnerTypeKind::Func(ft) => Func(ft.into()), + InnerTypeKind::Struct(st) => Struct(st.into()), + InnerTypeKind::Array(at) => Array(at.into()), + } + } +} + impl Encode for Rec<'_> { fn encode(&self, e: &mut Vec) { e.push(0x4e); @@ -507,6 +554,17 @@ impl<'a> Encode for StorageType<'a> { } } +impl From> for wasm_encoder::StorageType { + fn from(st: StorageType) -> Self { + use wasm_encoder::StorageType::*; + match st { + StorageType::I8 => I8, + StorageType::I16 => I16, + StorageType::Val(vt) => Val(vt.into()), + } + } +} + impl Encode for Import<'_> { fn encode(&self, e: &mut Vec) { self.module.encode(e); diff --git a/crates/wit-component/src/encoding.rs b/crates/wit-component/src/encoding.rs index 2124d80cf2..17f68972bd 100644 --- a/crates/wit-component/src/encoding.rs +++ b/crates/wit-component/src/encoding.rs @@ -1247,7 +1247,7 @@ impl<'a> EncodingState<'a> { let i = i as u32; let type_index = *sigs.entry(sig).or_insert_with(|| { let index = types.len(); - types.function( + types.ty().function( sig.params.iter().map(to_val_type), sig.results.iter().map(to_val_type), ); @@ -1724,7 +1724,7 @@ impl<'a> EncodingState<'a> { ); let mut shim = Module::default(); let mut section = TypeSection::new(); - section.function([], []); + section.ty().function([], []); shim.section(§ion); let mut section = ImportSection::new(); section.import("", "", EntityType::Function(0)); diff --git a/crates/wit-component/src/gc.rs b/crates/wit-component/src/gc.rs index de87da4521..045ff424bd 100644 --- a/crates/wit-component/src/gc.rs +++ b/crates/wit-component/src/gc.rs @@ -551,7 +551,7 @@ impl<'a> Module<'a> { map.types.push(i); let ty = map.func_type(ty.clone())?; - types.func_type(&ty); + types.ty().func_type(&ty); // Keep track of the "empty type" to see if we can reuse an // existing one or one needs to be injected if a `start` @@ -645,7 +645,7 @@ impl<'a> Module<'a> { let add_realloc_type = |types: &mut wasm_encoder::TypeSection| { let type_index = types.len(); - types.function( + types.ty().function( [ wasm_encoder::ValType::I32, wasm_encoder::ValType::I32, @@ -659,7 +659,7 @@ impl<'a> Module<'a> { let add_empty_type = |types: &mut wasm_encoder::TypeSection| { let type_index = types.len(); - types.function([], []); + types.ty().function([], []); type_index }; @@ -788,7 +788,7 @@ impl<'a> Module<'a> { // Generate a function type for this start function, adding a new // function type to the module if necessary. let empty_type = empty_type.unwrap_or_else(|| { - types.function([], []); + types.ty().function([], []); types.len() - 1 }); funcs.function(empty_type); diff --git a/crates/wit-component/src/linking.rs b/crates/wit-component/src/linking.rs index 0ba89cbe0b..1bae4105f6 100644 --- a/crates/wit-component/src/linking.rs +++ b/crates/wit-component/src/linking.rs @@ -289,7 +289,7 @@ fn make_env_module<'a>( Type::Function(ty) => { let index = get_and_increment(&mut function_count); entry.insert(index); - types.function( + types.ty().function( ty.parameters.iter().copied().map(ValType::from), ty.results.iter().copied().map(ValType::from), ); @@ -314,7 +314,7 @@ fn make_env_module<'a>( } let index = get_and_increment(&mut function_count); - types.function(vec![], vec![]); + types.ty().function(vec![], vec![]); imports.import(metadata.name, "_start", EntityType::Function(index)); wasi_start = Some(index); @@ -332,7 +332,7 @@ fn make_env_module<'a>( if let Some(exporter) = cabi_realloc_exporter { let index = get_and_increment(&mut function_count); - types.function([ValType::I32; 4], [ValType::I32]); + types.ty().function([ValType::I32; 4], [ValType::I32]); imports.import(exporter, "cabi_realloc", EntityType::Function(index)); exports.export("cabi_realloc", ExportKind::Func, index); } @@ -423,7 +423,7 @@ fn make_env_module<'a>( let mut code = CodeSection::new(); for (name, ty, _) in function_exports { let index = get_and_increment(&mut function_count); - types.function( + types.ty().function( ty.parameters.iter().copied().map(ValType::from), ty.results.iter().copied().map(ValType::from), ); @@ -515,9 +515,9 @@ fn make_init_module( // TODO: deduplicate types let mut types = TypeSection::new(); - types.function([], []); + types.ty().function([], []); let thunk_ty = 0; - types.function([ValType::I32], []); + types.ty().function([ValType::I32], []); let one_i32_param_ty = 1; let mut type_offset = 2; @@ -525,7 +525,7 @@ fn make_init_module( if metadata.dl_openable { for export in &metadata.exports { if let Type::Function(ty) = &export.key.ty { - types.function( + types.ty().function( ty.parameters.iter().copied().map(ValType::from), ty.results.iter().copied().map(ValType::from), ); @@ -534,7 +534,7 @@ fn make_init_module( } } for (_, ty, _) in function_exports { - types.function( + types.ty().function( ty.parameters.iter().copied().map(ValType::from), ty.results.iter().copied().map(ValType::from), ); @@ -1139,7 +1139,7 @@ fn make_stubs_module(missing: &[(&str, Export)]) -> Vec { unreachable!(); }; - types.function( + types.ty().function( ty.parameters.iter().copied().map(ValType::from), ty.results.iter().copied().map(ValType::from), ); diff --git a/src/bin/wasm-tools/component.rs b/src/bin/wasm-tools/component.rs index f9813c632b..adfd394567 100644 --- a/src/bin/wasm-tools/component.rs +++ b/src/bin/wasm-tools/component.rs @@ -880,7 +880,7 @@ impl UnbundleOpts { } let module_type_idx = module_ty.component.section.len(); - module_ty.component.section.module(&module_ty.module); + module_ty.component.section.ty().module(&module_ty.module); let name = format!("unbundled-module{}", imports.len()); imports.import( &name, @@ -943,7 +943,7 @@ impl CoreTypeInterner { }; let ret = self.section.len(); - self.section.function( + self.section.ty().core().function( f.params() .iter() .map(|p| RoundtripReencoder.val_type(*p)) diff --git a/tests/cli/dump/import-modules.wat.stdout b/tests/cli/dump/import-modules.wat.stdout index a128f8a5b1..6ce5dff9f5 100644 --- a/tests/cli/dump/import-modules.wat.stdout +++ b/tests/cli/dump/import-modules.wat.stdout @@ -2,7 +2,7 @@ | 0d 00 01 00 0x8 | 03 0d | core type section 0xa | 01 | 1 count - 0xb | 50 02 01 60 | [core type 0] Module([Type(SubType { is_final: true, supertype_idx: None, composite_type: CompositeType { inner: Func(FuncType { params: [], results: [] }), shared: false } }), Import(Import { module: "", name: "f", ty: Func(0) })]) + 0xb | 50 02 01 60 | [core type 0] Module([Type(RecGroup { inner: Implicit((14, SubType { is_final: true, supertype_idx: None, composite_type: CompositeType { inner: Func(FuncType { params: [], results: [] }), shared: false } })) }), Import(Import { module: "", name: "f", ty: Func(0) })]) | 00 00 00 00 | 01 66 00 00 0x17 | 0a 07 | component import section diff --git a/tests/cli/dump/module-types.wat.stdout b/tests/cli/dump/module-types.wat.stdout index 1e36b9d1b7..46fe586c58 100644 --- a/tests/cli/dump/module-types.wat.stdout +++ b/tests/cli/dump/module-types.wat.stdout @@ -2,7 +2,7 @@ | 0d 00 01 00 0x8 | 03 23 | core type section 0xa | 01 | 1 count - 0xb | 50 05 01 60 | [core type 0] Module([Type(SubType { is_final: true, supertype_idx: None, composite_type: CompositeType { inner: Func(FuncType { params: [], results: [] }), shared: false } }), Import(Import { module: "", name: "f", ty: Func(0) }), Import(Import { module: "", name: "g", ty: Global(GlobalType { content_type: I32, mutable: false, shared: false }) }), Import(Import { module: "", name: "t", ty: Table(TableType { element_type: funcref, table64: false, initial: 1, maximum: None, shared: false }) }), Import(Import { module: "", name: "m", ty: Memory(MemoryType { memory64: false, shared: false, initial: 1, maximum: None, page_size_log2: None }) })]) + 0xb | 50 05 01 60 | [core type 0] Module([Type(RecGroup { inner: Implicit((14, SubType { is_final: true, supertype_idx: None, composite_type: CompositeType { inner: Func(FuncType { params: [], results: [] }), shared: false } })) }), Import(Import { module: "", name: "f", ty: Func(0) }), Import(Import { module: "", name: "g", ty: Global(GlobalType { content_type: I32, mutable: false, shared: false }) }), Import(Import { module: "", name: "t", ty: Table(TableType { element_type: funcref, table64: false, initial: 1, maximum: None, shared: false }) }), Import(Import { module: "", name: "m", ty: Memory(MemoryType { memory64: false, shared: false, initial: 1, maximum: None, page_size_log2: None }) })]) | 00 00 00 00 | 01 66 00 00 | 00 00 01 67 diff --git a/tests/local/component-model/gc.wast b/tests/local/component-model/gc.wast new file mode 100644 index 0000000000..5496b7454d --- /dev/null +++ b/tests/local/component-model/gc.wast @@ -0,0 +1,54 @@ +(component binary + "\00asm" "\0d\00\01\00" ;; component header + "\03\07" ;; core type section, 7 bytes large + "\01" ;; 1 count + "\00\50" ;; sub type + "\00" ;; no supertypes + "\60" ;; function type + "\00\00" ;; no parameters, no results +) + +(component binary + "\00asm" "\0d\00\01\00" ;; component header + "\03\06" ;; core type section, 6 bytes large + "\02" ;; 2 count + "\50" ;; module type + "\00" ;; empty + "\60" ;; function type + "\00\00" ;; no parameters, no results +) + +(component binary + "\00asm" "\0d\00\01\00" ;; component header + "\03\09" ;; core type section, 9 bytes large + "\01" ;; 1 count + "\50" ;; module type + "\01" ;; 1 count + "\01" ;; core type in module + "\50" ;; sub type + "\00" ;; no supertypes + "\60" ;; function type + "\00\00" ;; no parameters, no results +) + +(assert_malformed + (component binary + "\00asm" "\0d\00\01\00" ;; component header + "\03\06" ;; core type section, 6 bytes large + "\01" ;; 1 count + "\50" ;; attempted sub type, but actually a module type + "\00" ;; attempted zero super types, but actually empty + "\60" ;; function type + "\00\00" ;; no parameters, no results + ) +"unexpected data at the end of the section") + +(assert_malformed + (component binary + "\00asm" "\0d\00\01\00" ;; component header + "\03\05" ;; core type section, 5 bytes large + "\01" ;; 1 count + "\00\60" ;; attempted function type with invalid prefix + "\00\00" ;; no parameters, no results + ) +"invalid leading byte (0x60) for non-final sub type") diff --git a/tests/snapshots/local/component-model/gc.wast.json b/tests/snapshots/local/component-model/gc.wast.json new file mode 100644 index 0000000000..8f91052e09 --- /dev/null +++ b/tests/snapshots/local/component-model/gc.wast.json @@ -0,0 +1,34 @@ +{ + "source_filename": "tests/local/component-model/gc.wast", + "commands": [ + { + "type": "module", + "line": 1, + "filename": "gc.0.wasm" + }, + { + "type": "module", + "line": 11, + "filename": "gc.1.wasm" + }, + { + "type": "module", + "line": 21, + "filename": "gc.2.wasm" + }, + { + "type": "assert_malformed", + "line": 35, + "filename": "gc.3.wasm", + "text": "unexpected data at the end of the section", + "module_type": "binary" + }, + { + "type": "assert_malformed", + "line": 47, + "filename": "gc.4.wasm", + "text": "invalid leading byte (0x60) for non-final sub type", + "module_type": "binary" + } + ] +} \ No newline at end of file diff --git a/tests/snapshots/local/component-model/gc.wast/0.print b/tests/snapshots/local/component-model/gc.wast/0.print new file mode 100644 index 0000000000..c6b16f991b --- /dev/null +++ b/tests/snapshots/local/component-model/gc.wast/0.print @@ -0,0 +1,3 @@ +(component + (core type (;0;) (sub (func))) +) diff --git a/tests/snapshots/local/component-model/gc.wast/1.print b/tests/snapshots/local/component-model/gc.wast/1.print new file mode 100644 index 0000000000..e2b5d6ef42 --- /dev/null +++ b/tests/snapshots/local/component-model/gc.wast/1.print @@ -0,0 +1,6 @@ +(component + (core type (;0;) + (module) + ) + (core type (;1;) (func)) +) diff --git a/tests/snapshots/local/component-model/gc.wast/2.print b/tests/snapshots/local/component-model/gc.wast/2.print new file mode 100644 index 0000000000..b68d01c884 --- /dev/null +++ b/tests/snapshots/local/component-model/gc.wast/2.print @@ -0,0 +1,7 @@ +(component + (core type (;0;) + (module + (type (;0;) (sub (func))) + ) + ) +)