diff --git a/Cargo.lock b/Cargo.lock index b3e631cc8f..d803d35322 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1397,9 +1397,9 @@ checksum = "826167069c09b99d56f31e9ae5c99049e932a98c9dc2dac47645b08dbbf76ba7" [[package]] name = "smallvec" -version = "1.11.0" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9" +checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a" [[package]] name = "spdx" @@ -1926,6 +1926,7 @@ dependencies = [ "once_cell", "rayon", "semver", + "smallvec", "wasm-encoder 0.35.0", "wast", "wat", diff --git a/Cargo.toml b/Cargo.toml index 1658fb03ce..9e7e1ac80b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -44,6 +44,7 @@ wasmtime = { version = "12.0.0", default-features = false, features = ['cranelif url = "2.0.0" pretty_assertions = "1.3.0" semver = "1.0.0" +smallvec = "1.11.1" wasm-compose = { version = "0.4.10", path = "crates/wasm-compose" } wasm-encoder = { version = "0.35.0", path = "crates/wasm-encoder" } diff --git a/crates/wasm-compose/src/encoding.rs b/crates/wasm-compose/src/encoding.rs index 5303193c9c..14c6f1a604 100644 --- a/crates/wasm-compose/src/encoding.rs +++ b/crates/wasm-compose/src/encoding.rs @@ -434,7 +434,7 @@ impl<'a> TypeEncoder<'a> { wasmparser::HeapType::Struct => HeapType::Struct, wasmparser::HeapType::Array => HeapType::Array, wasmparser::HeapType::I31 => HeapType::I31, - wasmparser::HeapType::Indexed(i) => HeapType::Indexed(i), + wasmparser::HeapType::Concrete(i) => HeapType::Concrete(i), }, } } diff --git a/crates/wasm-encoder/src/core/types.rs b/crates/wasm-encoder/src/core/types.rs index a36a913a58..d28d863f70 100644 --- a/crates/wasm-encoder/src/core/types.rs +++ b/crates/wasm-encoder/src/core/types.rs @@ -7,8 +7,8 @@ pub struct SubType { pub is_final: bool, /// The list of supertype indexes. As of GC MVP, there can be at most one supertype. pub supertype_idx: Option, - /// The structural type of the subtype. - pub structural_type: StructuralType, + /// The composite type of the subtype. + pub composite_type: CompositeType, } #[cfg(feature = "wasmparser")] @@ -17,14 +17,14 @@ impl From for SubType { SubType { is_final: sub_ty.is_final, supertype_idx: sub_ty.supertype_idx, - structural_type: sub_ty.structural_type.into(), + composite_type: sub_ty.composite_type.into(), } } } -/// Represents a structural type in a WebAssembly module. +/// Represents a composite type in a WebAssembly module. #[derive(Debug, Clone)] -pub enum StructuralType { +pub enum CompositeType { /// The type is for a function. Func(FuncType), /// The type is for an array. @@ -33,18 +33,18 @@ pub enum StructuralType { Struct(StructType), } -impl Encode for StructuralType { +impl Encode for CompositeType { fn encode(&self, sink: &mut Vec) { match self { - StructuralType::Func(ty) => TypeSection::encode_function( + CompositeType::Func(ty) => TypeSection::encode_function( sink, ty.params().iter().copied(), ty.results().iter().copied(), ), - StructuralType::Array(ArrayType(ty)) => { + CompositeType::Array(ArrayType(ty)) => { TypeSection::encode_array(sink, &ty.element_type, ty.mutable) } - StructuralType::Struct(ty) => { + CompositeType::Struct(ty) => { TypeSection::encode_struct(sink, ty.fields.iter().cloned()) } } @@ -52,12 +52,12 @@ impl Encode for StructuralType { } #[cfg(feature = "wasmparser")] -impl From for StructuralType { - fn from(structural_ty: wasmparser::StructuralType) -> Self { - match structural_ty { - wasmparser::StructuralType::Func(f) => StructuralType::Func(f.into()), - wasmparser::StructuralType::Array(a) => StructuralType::Array(a.into()), - wasmparser::StructuralType::Struct(s) => StructuralType::Struct(s.into()), +impl From for CompositeType { + fn from(composite_ty: wasmparser::CompositeType) -> Self { + match composite_ty { + wasmparser::CompositeType::Func(f) => CompositeType::Func(f.into()), + wasmparser::CompositeType::Array(a) => CompositeType::Array(a.into()), + wasmparser::CompositeType::Struct(s) => CompositeType::Struct(s.into()), } } } @@ -108,7 +108,7 @@ impl From for StructType { } } -/// Field type in structural types (structs, arrays). +/// Field type in composite types (structs, arrays). #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Ord, PartialOrd)] pub struct FieldType { /// Storage type of the field. @@ -127,7 +127,7 @@ impl From for FieldType { } } -/// Storage type for structural type fields. +/// Storage type for composite type fields. #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Ord, PartialOrd)] pub enum StorageType { /// The `i8` type. @@ -313,27 +313,51 @@ impl From for ValType { pub enum HeapType { /// Untyped (any) function. Func, - /// External heap type. + + /// The abstract external heap type. Extern, - /// The `any` heap type. The common supertype (a.k.a. top) of all internal types. + + /// The abstract `any` heap type. + /// + /// The common supertype (a.k.a. top) of all internal types. Any, - /// The `none` heap type. The common subtype (a.k.a. bottom) of all internal types. + + /// The abstract `none` heap type. + /// + /// The common subtype (a.k.a. bottom) of all internal types. None, - /// The `noextern` heap type. The common subtype (a.k.a. bottom) of all external types. + + /// The abstract `noextern` heap type. + /// + /// The common subtype (a.k.a. bottom) of all external types. NoExtern, - /// The `nofunc` heap type. The common subtype (a.k.a. bottom) of all function types. + + /// The abstract `nofunc` heap type. + /// + /// The common subtype (a.k.a. bottom) of all function types. NoFunc, - /// The `eq` heap type. The common supertype of all referenceable types on which comparison + + /// The abstract `eq` heap type. + /// + /// The common supertype of all referenceable types on which comparison /// (ref.eq) is allowed. Eq, - /// The `struct` heap type. The common supertype of all struct types. + + /// The abstract `struct` heap type. + /// + /// The common supertype of all struct types. Struct, - /// The `array` heap type. The common supertype of all array types. + + /// The abstract `array` heap type. + /// + /// The common supertype of all array types. Array, - /// The i31 heap type. + + /// The unboxed `i31` heap type. I31, - /// User defined type at the given index. - Indexed(u32), + + /// A concrete Wasm-defined type at the given index. + Concrete(u32), } impl Encode for HeapType { @@ -351,7 +375,7 @@ impl Encode for HeapType { HeapType::I31 => sink.push(0x6C), // Note that this is encoded as a signed type rather than unsigned // as it's decoded as an s33 - HeapType::Indexed(i) => i64::from(*i).encode(sink), + HeapType::Concrete(i) => i64::from(*i).encode(sink), } } } @@ -360,7 +384,7 @@ impl Encode for HeapType { impl From for HeapType { fn from(heap_type: wasmparser::HeapType) -> Self { match heap_type { - wasmparser::HeapType::Indexed(i) => HeapType::Indexed(i), + wasmparser::HeapType::Concrete(i) => HeapType::Concrete(i), wasmparser::HeapType::Func => HeapType::Func, wasmparser::HeapType::Extern => HeapType::Extern, wasmparser::HeapType::Any => HeapType::Any, @@ -486,14 +510,14 @@ impl TypeSection { /// Define an explicit subtype in this type section. pub fn subtype(&mut self, ty: &SubType) -> &mut Self { - // We only need to emit a prefix byte before the actual structural type + // 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 ty.supertype_idx.is_some() || !ty.is_final { self.bytes.push(if ty.is_final { 0x4f } else { 0x50 }); ty.supertype_idx.encode(&mut self.bytes); } - ty.structural_type.encode(&mut self.bytes); + ty.composite_type.encode(&mut self.bytes); self.num_added += 1; self } @@ -522,7 +546,7 @@ mod tests { types.subtype(&SubType { is_final: true, supertype_idx: None, - structural_type: StructuralType::Func(FuncType::new([], [])), + composite_type: CompositeType::Func(FuncType::new([], [])), }); let mut module = Module::new(); diff --git a/crates/wasm-mutate/src/module.rs b/crates/wasm-mutate/src/module.rs index 4431a14fed..0e4cf6148f 100644 --- a/crates/wasm-mutate/src/module.rs +++ b/crates/wasm-mutate/src/module.rs @@ -90,7 +90,7 @@ pub fn map_ref_type(ref_ty: wasmparser::RefType) -> Result { wasmparser::HeapType::Struct => HeapType::Struct, wasmparser::HeapType::Array => HeapType::Array, wasmparser::HeapType::I31 => HeapType::I31, - wasmparser::HeapType::Indexed(i) => HeapType::Indexed(i.into()), + wasmparser::HeapType::Concrete(i) => HeapType::Concrete(i.into()), }, }) } diff --git a/crates/wasm-mutate/src/mutators/translate.rs b/crates/wasm-mutate/src/mutators/translate.rs index f3044817df..6587826d6a 100644 --- a/crates/wasm-mutate/src/mutators/translate.rs +++ b/crates/wasm-mutate/src/mutators/translate.rs @@ -210,8 +210,8 @@ pub fn heapty(t: &mut dyn Translator, ty: &wasmparser::HeapType) -> Result Ok(HeapType::Struct), wasmparser::HeapType::Array => Ok(HeapType::Array), wasmparser::HeapType::I31 => Ok(HeapType::I31), - wasmparser::HeapType::Indexed(i) => { - Ok(HeapType::Indexed(t.remap(Item::Type, (*i).into())?)) + wasmparser::HeapType::Concrete(i) => { + Ok(HeapType::Concrete(t.remap(Item::Type, (*i).into())?)) } } } diff --git a/crates/wasm-smith/src/core.rs b/crates/wasm-smith/src/core.rs index 9fe15faa79..ddc01154a4 100644 --- a/crates/wasm-smith/src/core.rs +++ b/crates/wasm-smith/src/core.rs @@ -1654,7 +1654,7 @@ fn convert_reftype(ty: wasmparser::RefType) -> RefType { wasmparser::HeapType::Struct => HeapType::Struct, wasmparser::HeapType::Array => HeapType::Array, wasmparser::HeapType::I31 => HeapType::I31, - wasmparser::HeapType::Indexed(i) => HeapType::Indexed(i.into()), + wasmparser::HeapType::Concrete(i) => HeapType::Concrete(i.into()), }, } } diff --git a/crates/wasmparser/Cargo.toml b/crates/wasmparser/Cargo.toml index d32cbd6c0d..de9bdfd096 100644 --- a/crates/wasmparser/Cargo.toml +++ b/crates/wasmparser/Cargo.toml @@ -15,6 +15,7 @@ exclude = ["benches/*.wasm"] [dependencies] indexmap = { workspace = true } semver = { workspace = true } +smallvec = { workspace = true } [dev-dependencies] anyhow = { workspace = true } diff --git a/crates/wasmparser/src/define_types.rs b/crates/wasmparser/src/define_types.rs new file mode 100644 index 0000000000..b65bca500f --- /dev/null +++ b/crates/wasmparser/src/define_types.rs @@ -0,0 +1,746 @@ +// Define the core Wasm type hierarchy with the given index type. +// +// The index type must satisfy the following constraints: +// +// * It must implement `Display` +// +// * It must implement `Into` and `From` +// +// * `$index_type::from(u32::from(index))` must be the identity function. +// +// * `u32::from($index_type::from(x))` must also be the identity function. +// +// * Its `u32` representation must fit within 20 bits, that is +// +// index.into() <= (1 << 20) - 1 +// +// must hold true for all indices. +macro_rules! define_core_wasm_types { + ($index_type:ty) => { + /// Represents a recursive type group in a WebAssembly module. + #[derive(Debug, Clone)] + pub struct RecGroup { + pub(crate) types: smallvec::SmallVec<[SubType; 1]>, + + /// Whether or not this rec group was explicitly encoded in the + /// binary or was implicitly created for a type that was not + /// contained in a `(rec ...)` in the types section. + pub(crate) explicit_rec_group: bool, + } + + impl RecGroup { + /// Create an explicit `RecGroup` for the given types. + pub(crate) fn explicit(types: smallvec::SmallVec<[SubType; 1]>) -> Self { + RecGroup { + types, + explicit_rec_group: true, + } + } + + /// Create an implicit `RecGroup` for a type that was not contained + /// in a `(rec ...)`. + pub(crate) fn implicit(ty: SubType) -> Self { + RecGroup { + types: std::iter::once(ty).collect(), + explicit_rec_group: false, + } + } + + /// Returns the list of subtypes in the recursive type group. + pub fn types(&self) -> &[SubType] { + &self.types + } + + /// Returns an owning iterator of all subtypes in this recursion + /// group. + pub fn into_types(self) -> impl ExactSizeIterator { + self.types.into_iter() + } + } + + /// Represents a subtype of possible other types in a WebAssembly module. + #[derive(Debug, Clone)] + 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. + pub supertype_idx: Option<$index_type>, + /// The composite type of the subtype. + pub composite_type: CompositeType, + } + + impl SubType { + /// Unwrap an `ArrayType` or panic. + /// + /// Does not check finality or whether there is a supertype. + pub fn unwrap_array(&self) -> &ArrayType { + self.composite_type.unwrap_array() + } + + /// Unwrap an `FuncType` or panic. + /// + /// Does not check finality or whether there is a supertype. + pub fn unwrap_func(&self) -> &FuncType { + self.composite_type.unwrap_func() + } + + /// Unwrap an `StructType` or panic. + /// + /// Does not check finality or whether there is a supertype. + pub fn unwrap_struct(&self) -> &StructType { + self.composite_type.unwrap_struct() + } + } + + /// Represents a composite type in a WebAssembly module. + #[derive(Debug, Clone)] + pub enum CompositeType { + /// The type is for a function. + Func(FuncType), + /// The type is for an array. + Array(ArrayType), + /// The type is for a struct. + Struct(StructType), + } + + impl CompositeType { + /// Unwrap a `FuncType` or panic. + pub fn unwrap_func(&self) -> &FuncType { + match self { + Self::Func(f) => f, + _ => panic!("not a func"), + } + } + + /// Unwrap a `ArrayType` or panic. + pub fn unwrap_array(&self) -> &ArrayType { + match self { + Self::Array(a) => a, + _ => panic!("not a array"), + } + } + + /// Unwrap a `StructType` or panic. + pub fn unwrap_struct(&self) -> &StructType { + match self { + Self::Struct(s) => s, + _ => panic!("not a struct"), + } + } + } + + /// Represents a type of a function in a WebAssembly module. + #[derive(Clone, Eq, PartialEq, Hash)] + pub struct FuncType { + /// The combined parameters and result types. + params_results: Box<[ValType]>, + /// The number of parameter types. + len_params: usize, + } + + impl std::fmt::Debug for FuncType { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("FuncType") + .field("params", &self.params()) + .field("results", &self.results()) + .finish() + } + } + + impl FuncType { + /// Creates a new [`FuncType`] from the given `params` and `results`. + pub fn new(params: P, results: R) -> Self + where + P: IntoIterator, + R: IntoIterator, + { + let mut buffer = params.into_iter().collect::>(); + let len_params = buffer.len(); + buffer.extend(results); + Self { + params_results: buffer.into(), + len_params, + } + } + + /// Creates a new [`FuncType`] fom its raw parts. + /// + /// # Panics + /// + /// If `len_params` is greater than the length of `params_results` combined. + pub(crate) fn from_raw_parts( + params_results: Box<[ValType]>, + len_params: usize, + ) -> Self { + assert!(len_params <= params_results.len()); + Self { + params_results, + len_params, + } + } + + /// Returns a shared slice to the parameter types of the [`FuncType`]. + #[inline] + pub fn params(&self) -> &[ValType] { + &self.params_results[..self.len_params] + } + + /// Returns a shared slice to the result types of the [`FuncType`]. + #[inline] + pub fn results(&self) -> &[ValType] { + &self.params_results[self.len_params..] + } + + pub(crate) fn desc(&self) -> String { + let mut s = String::new(); + s.push_str("["); + for (i, param) in self.params().iter().enumerate() { + if i > 0 { + s.push_str(" "); + } + write!(s, "{param}").unwrap(); + } + s.push_str("] -> ["); + for (i, result) in self.results().iter().enumerate() { + if i > 0 { + s.push_str(" "); + } + write!(s, "{result}").unwrap(); + } + s.push_str("]"); + s + } + } + + /// Represents a type of an array in a WebAssembly module. + #[derive(Debug, Clone, Eq, PartialEq, Hash)] + pub struct ArrayType(pub FieldType); + + /// Represents a field type of an array or a struct. + #[derive(Debug, Clone, Eq, PartialEq, Hash)] + pub struct FieldType { + /// Array element type. + pub element_type: StorageType, + /// Are elements mutable. + pub mutable: bool, + } + + /// Represents storage types introduced in the GC spec for array and struct fields. + #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] + pub enum StorageType { + /// The storage type is i8. + I8, + /// The storage type is i16. + I16, + /// The storage type is a value type. + Val(ValType), + } + + /// Represents a type of a struct in a WebAssembly module. + #[derive(Debug, Clone, Eq, PartialEq, Hash)] + pub struct StructType { + /// Struct fields. + pub fields: Box<[FieldType]>, + } + + /// Represents the types of values in a WebAssembly module. + #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] + pub enum ValType { + /// The value type is i32. + I32, + /// The value type is i64. + I64, + /// The value type is f32. + F32, + /// The value type is f64. + F64, + /// The value type is v128. + V128, + /// The value type is a reference. + Ref(RefType), + } + + impl From for ValType { + #[inline] + fn from(ty: RefType) -> ValType { + ValType::Ref(ty) + } + } + + impl std::fmt::Display for ValType { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + ValType::I32 => f.write_str("i32"), + ValType::I64 => f.write_str("i64"), + ValType::F32 => f.write_str("f32"), + ValType::F64 => f.write_str("f64"), + ValType::V128 => f.write_str("v128"), + ValType::Ref(r) => std::fmt::Display::fmt(r, f), + } + } + } + + impl ValType { + /// Alias for the wasm `funcref` type. + pub const FUNCREF: ValType = ValType::Ref(RefType::FUNCREF); + + /// Alias for the wasm `externref` type. + pub const EXTERNREF: ValType = ValType::Ref(RefType::EXTERNREF); + + /// Returns whether this value type is a "reference type". + /// + /// Only reference types are allowed in tables, for example, and with some + /// instructions. Current reference types include `funcref` and `externref`. + pub fn is_reference_type(&self) -> bool { + matches!(self, ValType::Ref(_)) + } + + /// Whether the type is defaultable, i.e. it is not a non-nullable reference + /// type. + pub fn is_defaultable(&self) -> bool { + match *self { + Self::I32 | Self::I64 | Self::F32 | Self::F64 | Self::V128 => true, + Self::Ref(rt) => rt.is_nullable(), + } + } + } + + /// A reference type. + /// + /// The reference types proposal first introduced `externref` and + /// `funcref`. + /// + /// The function references proposal introduced typed function + /// references. + /// + /// The GC proposal introduces heap types: any, eq, i31, struct, array, + /// nofunc, noextern, none. + // + // RefType is a bit-packed enum that fits in a `u24` aka `[u8; 3]`. + // Note that its content is opaque (and subject to change), but its API + // is stable. + // + // It has the following internal structure: + // + // ``` + // [nullable:u1 concrete==1:u1 unused:u2 index:u20] + // [nullable:u1 concrete==0:u1 abstype:u4 (unused):u18] + // ``` + // + // Where + // + // - `nullable` determines nullability of the ref, + // + // - `concrete` determines if the ref is of a dynamically defined type + // with an index (encoded in a following bit-packing section) or of a + // known fixed type, + // + // - `index` is the type index, + // + // - `abstype` is an enumeration of abstract types: + // + // ``` + // 1111 = any + // + // 1101 = eq + // 1000 = i31 + // 1001 = struct + // 1100 = array + // + // 0101 = func + // 0100 = nofunc + // + // 0011 = extern + // 0010 = noextern + // + // 0000 = none + // ``` + #[derive(Copy, Clone, PartialEq, Eq, Hash)] + pub struct RefType([u8; 3]); + + impl std::fmt::Debug for RefType { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match (self.is_nullable(), self.heap_type()) { + (true, HeapType::Any) => write!(f, "anyref"), + (false, HeapType::Any) => write!(f, "(ref any)"), + (true, HeapType::None) => write!(f, "nullref"), + (false, HeapType::None) => write!(f, "(ref none)"), + (true, HeapType::NoExtern) => write!(f, "nullexternref"), + (false, HeapType::NoExtern) => write!(f, "(ref noextern)"), + (true, HeapType::NoFunc) => write!(f, "nullfuncref"), + (false, HeapType::NoFunc) => write!(f, "(ref nofunc)"), + (true, HeapType::Eq) => write!(f, "eqref"), + (false, HeapType::Eq) => write!(f, "(ref eq)"), + (true, HeapType::Struct) => write!(f, "structref"), + (false, HeapType::Struct) => write!(f, "(ref struct)"), + (true, HeapType::Array) => write!(f, "arrayref"), + (false, HeapType::Array) => write!(f, "(ref array)"), + (true, HeapType::I31) => write!(f, "i31ref"), + (false, HeapType::I31) => write!(f, "(ref i31)"), + (true, HeapType::Extern) => write!(f, "externref"), + (false, HeapType::Extern) => write!(f, "(ref extern)"), + (true, HeapType::Func) => write!(f, "funcref"), + (false, HeapType::Func) => write!(f, "(ref func)"), + (true, HeapType::Concrete(idx)) => write!(f, "(ref null {idx})"), + (false, HeapType::Concrete(idx)) => write!(f, "(ref {idx})"), + } + } + } + + impl std::fmt::Display for RefType { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + std::fmt::Debug::fmt(self, f) + } + } + + // Assert that we can fit indices up to `MAX_WASM_TYPES` inside `RefType`. + #[test] + fn can_fit_max_wasm_types_in_ref_type() { + fn can_roundtrip_index(index: u32) -> bool { + assert!(RefType::can_represent_type_index(index)); + let rt = match RefType::concrete(true, index) { + Some(rt) => rt, + None => panic!(), + }; + assert!(rt.is_nullable()); + let actual_index = match rt.type_index() { + Some(i) => i, + None => panic!(), + }; + actual_index == index + } + + assert!(can_roundtrip_index(crate::limits::MAX_WASM_TYPES as u32)); + assert!(can_roundtrip_index(0b00000000_00001111_00000000_00000000)); + assert!(can_roundtrip_index(0b00000000_00000000_11111111_00000000)); + assert!(can_roundtrip_index(0b00000000_00000000_00000000_11111111)); + assert!(can_roundtrip_index(0)); + } + + impl RefType { + const NULLABLE_BIT: u32 = 1 << 23; // bit #23 + const CONCRETE_BIT: u32 = 1 << 22; // bit #22 + + const ABSTYPE_MASK: u32 = 0b1111 << 18; // 4 bits #21-#18 (if `concrete == 0`) + const ANY_ABSTYPE: u32 = 0b1111 << 18; + const EQ_ABSTYPE: u32 = 0b1101 << 18; + const I31_ABSTYPE: u32 = 0b1000 << 18; + const STRUCT_ABSTYPE: u32 = 0b1001 << 18; + const ARRAY_ABSTYPE: u32 = 0b1100 << 18; + const FUNC_ABSTYPE: u32 = 0b0101 << 18; + const NOFUNC_ABSTYPE: u32 = 0b0100 << 18; + const EXTERN_ABSTYPE: u32 = 0b0011 << 18; + const NOEXTERN_ABSTYPE: u32 = 0b0010 << 18; + const NONE_ABSTYPE: u32 = 0b0000 << 18; + + const INDEX_MASK: u32 = (1 << 20) - 1; // 20 bits #19-#0 (if `concrete == 1`) + + /// A nullable untyped function reference aka `(ref null func)` aka + /// `funcref` aka `anyfunc`. + pub const FUNCREF: Self = RefType::FUNC.nullable(); + + /// A nullable reference to an extern object aka `(ref null extern)` aka + /// `externref`. + pub const EXTERNREF: Self = RefType::EXTERN.nullable(); + + /// A non-nullable untyped function reference aka `(ref func)`. + pub const FUNC: Self = RefType::from_u32(Self::FUNC_ABSTYPE); + + /// A non-nullable reference to an extern object aka `(ref extern)`. + pub const EXTERN: Self = RefType::from_u32(Self::EXTERN_ABSTYPE); + + /// A non-nullable reference to any object aka `(ref any)`. + pub const ANY: Self = RefType::from_u32(Self::ANY_ABSTYPE); + + /// A non-nullable reference to no object aka `(ref none)`. + pub const NONE: Self = RefType::from_u32(Self::NONE_ABSTYPE); + + /// A non-nullable reference to a noextern object aka `(ref noextern)`. + pub const NOEXTERN: Self = RefType::from_u32(Self::NOEXTERN_ABSTYPE); + + /// A non-nullable reference to a nofunc object aka `(ref nofunc)`. + pub const NOFUNC: Self = RefType::from_u32(Self::NOFUNC_ABSTYPE); + + /// A non-nullable reference to an eq object aka `(ref eq)`. + pub const EQ: Self = RefType::from_u32(Self::EQ_ABSTYPE); + + /// A non-nullable reference to a struct aka `(ref struct)`. + pub const STRUCT: Self = RefType::from_u32(Self::STRUCT_ABSTYPE); + + /// A non-nullable reference to an array aka `(ref array)`. + pub const ARRAY: Self = RefType::from_u32(Self::ARRAY_ABSTYPE); + + /// A non-nullable reference to an i31 object aka `(ref i31)`. + pub const I31: Self = RefType::from_u32(Self::I31_ABSTYPE); + + const fn can_represent_type_index(index: u32) -> bool { + index & Self::INDEX_MASK == index + } + + const fn u24_to_u32(bytes: [u8; 3]) -> u32 { + let expanded_bytes = [bytes[0], bytes[1], bytes[2], 0]; + u32::from_le_bytes(expanded_bytes) + } + + const fn u32_to_u24(x: u32) -> [u8; 3] { + let bytes = x.to_le_bytes(); + debug_assert!(bytes[3] == 0); + [bytes[0], bytes[1], bytes[2]] + } + + #[inline] + const fn as_u32(&self) -> u32 { + Self::u24_to_u32(self.0) + } + + #[inline] + const fn from_u32(x: u32) -> Self { + debug_assert!(x & (0b11111111 << 24) == 0); + + // Either concrete or it must be a known abstract type. + debug_assert!( + x & Self::CONCRETE_BIT != 0 + || matches!( + x & Self::ABSTYPE_MASK, + Self::ANY_ABSTYPE + | Self::EQ_ABSTYPE + | Self::I31_ABSTYPE + | Self::STRUCT_ABSTYPE + | Self::ARRAY_ABSTYPE + | Self::FUNC_ABSTYPE + | Self::NOFUNC_ABSTYPE + | Self::EXTERN_ABSTYPE + | Self::NOEXTERN_ABSTYPE + | Self::NONE_ABSTYPE + ) + ); + + RefType(Self::u32_to_u24(x)) + } + + /// Create a reference to a concrete Wasm-defined type at the given + /// index. + /// + /// Returns `None` when the type index is beyond this crate's + /// implementation limits and therefore is not representable. + pub fn concrete(nullable: bool, index: $index_type) -> Option { + let index: u32 = index.into(); + if Self::can_represent_type_index(index) { + let nullable32 = Self::NULLABLE_BIT * nullable as u32; + Some(RefType::from_u32(nullable32 | Self::CONCRETE_BIT | index)) + } else { + None + } + } + + /// Create a new `RefType`. + /// + /// Returns `None` when the heap type's type index (if any) is + /// beyond this crate's implementation limits and therfore is not + /// representable. + pub fn new(nullable: bool, heap_type: HeapType) -> Option { + let nullable32 = Self::NULLABLE_BIT * (nullable as u32); + match heap_type { + HeapType::Concrete(index) => RefType::concrete(nullable, index), + HeapType::Func => Some(Self::from_u32(nullable32 | Self::FUNC_ABSTYPE)), + HeapType::Extern => Some(Self::from_u32(nullable32 | Self::EXTERN_ABSTYPE)), + HeapType::Any => Some(Self::from_u32(nullable32 | Self::ANY_ABSTYPE)), + HeapType::None => Some(Self::from_u32(nullable32 | Self::NONE_ABSTYPE)), + HeapType::NoExtern => Some(Self::from_u32(nullable32 | Self::NOEXTERN_ABSTYPE)), + HeapType::NoFunc => Some(Self::from_u32(nullable32 | Self::NOFUNC_ABSTYPE)), + HeapType::Eq => Some(Self::from_u32(nullable32 | Self::EQ_ABSTYPE)), + HeapType::Struct => Some(Self::from_u32(nullable32 | Self::STRUCT_ABSTYPE)), + HeapType::Array => Some(Self::from_u32(nullable32 | Self::ARRAY_ABSTYPE)), + HeapType::I31 => Some(Self::from_u32(nullable32 | Self::I31_ABSTYPE)), + } + } + + /// Is this a reference to an concrete type? + pub const fn is_concrete_type_ref(&self) -> bool { + self.as_u32() & Self::CONCRETE_BIT != 0 + } + + /// If this is a reference to a typed function, get its type index. + pub fn type_index(&self) -> Option<$index_type> { + if self.is_concrete_type_ref() { + let index = self.as_u32() & Self::INDEX_MASK; + Some(<$index_type>::from(index)) + } else { + None + } + } + + const fn abstype(&self) -> u32 { + self.as_u32() & Self::ABSTYPE_MASK + } + + /// Is this the abstract untyped function reference type aka `(ref + /// null func)` aka `funcref` aka `anyfunc`? + pub const fn is_func_ref(&self) -> bool { + !self.is_concrete_type_ref() && self.abstype() == Self::FUNC_ABSTYPE + } + + /// Is this the abstract external reference type aka `(ref null + /// extern)` aka `externref`? + pub const fn is_extern_ref(&self) -> bool { + !self.is_concrete_type_ref() && self.abstype() == Self::EXTERN_ABSTYPE + } + + /// Is this the abstract untyped array refrence type aka `(ref null + /// array)` aka `arrayref`? + pub const fn is_array_ref(&self) -> bool { + !self.is_concrete_type_ref() && self.abstype() == Self::ARRAY_ABSTYPE + } + + /// Is this the abstract untyped struct reference type aka `(ref + /// null struct)` aka `structref`? + pub const fn is_struct_ref(&self) -> bool { + !self.is_concrete_type_ref() && self.abstype() == Self::STRUCT_ABSTYPE + } + + /// Is this ref type nullable? + pub const fn is_nullable(&self) -> bool { + self.as_u32() & Self::NULLABLE_BIT != 0 + } + + /// Get the non-nullable version of this ref type. + pub const fn as_non_null(&self) -> Self { + Self::from_u32(self.as_u32() & !Self::NULLABLE_BIT) + } + + /// Get the non-nullable version of this ref type. + pub const fn nullable(&self) -> Self { + Self::from_u32(self.as_u32() | Self::NULLABLE_BIT) + } + + /// Get the heap type that this is a reference to. + pub fn heap_type(&self) -> HeapType { + let s = self.as_u32(); + if self.is_concrete_type_ref() { + HeapType::Concrete(self.type_index().unwrap()) + } else { + match s & Self::ABSTYPE_MASK { + Self::FUNC_ABSTYPE => HeapType::Func, + Self::EXTERN_ABSTYPE => HeapType::Extern, + Self::ANY_ABSTYPE => HeapType::Any, + Self::NONE_ABSTYPE => HeapType::None, + Self::NOEXTERN_ABSTYPE => HeapType::NoExtern, + Self::NOFUNC_ABSTYPE => HeapType::NoFunc, + Self::EQ_ABSTYPE => HeapType::Eq, + Self::STRUCT_ABSTYPE => HeapType::Struct, + Self::ARRAY_ABSTYPE => HeapType::Array, + Self::I31_ABSTYPE => HeapType::I31, + _ => unreachable!(), + } + } + } + + // Note that this is similar to `Display for RefType` except that it has + // the indexes stubbed out. + pub(crate) fn wat(&self) -> &'static str { + match (self.is_nullable(), self.heap_type()) { + (true, HeapType::Func) => "funcref", + (true, HeapType::Extern) => "externref", + (true, HeapType::Concrete(_)) => "(ref null $type)", + (true, HeapType::Any) => "anyref", + (true, HeapType::None) => "nullref", + (true, HeapType::NoExtern) => "nullexternref", + (true, HeapType::NoFunc) => "nullfuncref", + (true, HeapType::Eq) => "eqref", + (true, HeapType::Struct) => "structref", + (true, HeapType::Array) => "arrayref", + (true, HeapType::I31) => "i31ref", + (false, HeapType::Func) => "(ref func)", + (false, HeapType::Extern) => "(ref extern)", + (false, HeapType::Concrete(_)) => "(ref $type)", + (false, HeapType::Any) => "(ref any)", + (false, HeapType::None) => "(ref none)", + (false, HeapType::NoExtern) => "(ref noextern)", + (false, HeapType::NoFunc) => "(ref nofunc)", + (false, HeapType::Eq) => "(ref eq)", + (false, HeapType::Struct) => "(ref struct)", + (false, HeapType::Array) => "(ref array)", + (false, HeapType::I31) => "(ref i31)", + } + } + } + + /// A heap type. + #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] + pub enum HeapType { + /// A concrete, user-defined type. + /// + /// Introduced in the function-references proposal. + Concrete($index_type), + + /// The abstract, untyped (any) function. + /// + /// Introduced in the references-types proposal. + Func, + + /// The abstract, external heap type. + /// + /// Introduced in the references-types proposal. + Extern, + + /// The abstract `any` heap type. + /// + /// The common supertype (a.k.a. top) of all internal types. + /// + /// Introduced in the GC proposal. + Any, + + /// The abstract `none` heap type. + /// + /// The common subtype (a.k.a. bottom) of all internal types. + /// + /// Introduced in the GC proposal. + None, + + /// The abstract `noextern` heap type. + /// + /// The common subtype (a.k.a. bottom) of all external types. + /// + /// Introduced in the GC proposal. + NoExtern, + + /// The abstract `nofunc` heap type. + /// + /// The common subtype (a.k.a. bottom) of all function types. + /// + /// Introduced in the GC proposal. + NoFunc, + + /// The abstract `eq` heap type. + /// + /// The common supertype of all heap types on which the `ref.eq` + /// instruction is allowed. + /// + /// Introduced in the GC proposal. + Eq, + + /// The abstract `struct` heap type. + /// + /// The common supertype of all struct types. + /// + /// Introduced in the GC proposal. + Struct, + + /// The abstract `array` heap type. + /// + /// The common supertype of all array types. + /// + /// Introduced in the GC proposal. + Array, + + /// The abstract `i31` heap type. + /// + /// It is not expected that Wasm runtimes actually store these + /// values on the heap, but unbox them inline into the `i31ref`s + /// themselves instead. + /// + /// Introduced in the GC proposal. + I31, + } + }; +} diff --git a/crates/wasmparser/src/lib.rs b/crates/wasmparser/src/lib.rs index 194167230d..b377b54b18 100644 --- a/crates/wasmparser/src/lib.rs +++ b/crates/wasmparser/src/lib.rs @@ -718,6 +718,9 @@ pub use crate::readers::*; pub use crate::resources::*; pub use crate::validator::*; +#[macro_use] +mod define_types; + mod binary_reader; mod limits; mod parser; diff --git a/crates/wasmparser/src/readers/core/types.rs b/crates/wasmparser/src/readers/core/types.rs index 53e6aa6137..b1ac1f24b2 100644 --- a/crates/wasmparser/src/readers/core/types.rs +++ b/crates/wasmparser/src/readers/core/types.rs @@ -14,7 +14,6 @@ */ use std::fmt::{self, Debug, Write}; -use std::slice; use crate::limits::{ MAX_WASM_FUNCTION_PARAMS, MAX_WASM_FUNCTION_RETURNS, MAX_WASM_STRUCT_FIELDS, @@ -22,75 +21,15 @@ use crate::limits::{ }; use crate::{BinaryReader, BinaryReaderError, FromReader, Result, SectionLimited}; -/// Represents the types of values in a WebAssembly module. -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] -pub enum ValType { - /// The value type is i32. - I32, - /// The value type is i64. - I64, - /// The value type is f32. - F32, - /// The value type is f64. - F64, - /// The value type is v128. - V128, - /// The value type is a reference. - Ref(RefType), -} - -/// Represents storage types introduced in the GC spec for array and struct fields. -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] -pub enum StorageType { - /// The storage type is i8. - I8, - /// The storage type is i16. - I16, - /// The storage type is a value type. - Val(ValType), -} - -// The size of `ValType` is performance sensitive. -const _: () = { - assert!(std::mem::size_of::() == 4); -}; - pub(crate) trait Matches { fn matches<'a, F>(&self, other: &Self, type_at: &F) -> bool where F: Fn(u32) -> &'a SubType; } -impl From for ValType { - fn from(ty: RefType) -> ValType { - ValType::Ref(ty) - } -} +define_core_wasm_types!(u32); impl ValType { - /// Alias for the wasm `funcref` type. - pub const FUNCREF: ValType = ValType::Ref(RefType::FUNCREF); - - /// Alias for the wasm `externref` type. - pub const EXTERNREF: ValType = ValType::Ref(RefType::EXTERNREF); - - /// Returns whether this value type is a "reference type". - /// - /// Only reference types are allowed in tables, for example, and with some - /// instructions. Current reference types include `funcref` and `externref`. - pub fn is_reference_type(&self) -> bool { - matches!(self, ValType::Ref(_)) - } - - /// Whether the type is defaultable, i.e. it is not a non-nullable reference - /// type. - pub fn is_defaultable(&self) -> bool { - match *self { - Self::I32 | Self::I64 | Self::F32 | Self::F64 | Self::V128 => true, - Self::Ref(rt) => rt.is_nullable(), - } - } - pub(crate) fn is_valtype_byte(byte: u8) -> bool { match byte { 0x7F | 0x7E | 0x7D | 0x7C | 0x7B | 0x70 | 0x6F | 0x64 | 0x63 | 0x6E | 0x71 | 0x72 @@ -159,392 +98,6 @@ impl<'a> FromReader<'a> for ValType { } } -impl fmt::Display for ValType { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let s = match self { - ValType::I32 => "i32", - ValType::I64 => "i64", - ValType::F32 => "f32", - ValType::F64 => "f64", - ValType::V128 => "v128", - ValType::Ref(r) => return fmt::Display::fmt(r, f), - }; - f.write_str(s) - } -} - -/// A reference type. -/// -/// The reference types proposal first introduced `externref` and `funcref`. -/// -/// The function references proposal introduced typed function references. -/// -/// The GC proposal introduces heap types: any, eq, i31, struct, array, nofunc, noextern, none. -// -// RefType is a bit-packed enum that fits in a `u24` aka `[u8; 3]`. -// Note that its content is opaque (and subject to change), but its API is stable. -// -// It has the following internal structure: -// -// ``` -// [nullable:u1] [indexed==1:u1] [kind:u2] [index:u20] -// [nullable:u1] [indexed==0:u1] [type:u4] [(unused):u18] -// ``` -// -// Where -// -// - `nullable` determines nullability of the ref -// -// - `indexed` determines if the ref is of a dynamically defined type with an -// index (encoded in a following bit-packing section) or of a known fixed type -// -// - `kind` determines what kind of indexed type the index is pointing to: -// -// ``` -// 10 = struct -// 11 = array -// 01 = function -// ``` -// -// - `index` is the type index -// -// - `type` is an enumeration of known types: -// -// ``` -// 1111 = any -// -// 1101 = eq -// 1000 = i31 -// 1001 = struct -// 1100 = array -// -// 0101 = func -// 0100 = nofunc -// -// 0011 = extern -// 0010 = noextern -// -// 0000 = none -// ``` -// -// - `(unused)` is unused sequence of bits -#[derive(Copy, Clone, PartialEq, Eq, Hash)] -pub struct RefType([u8; 3]); - -impl Debug for RefType { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match (self.is_nullable(), self.heap_type()) { - (true, HeapType::Any) => write!(f, "anyref"), - (false, HeapType::Any) => write!(f, "(ref any)"), - (true, HeapType::None) => write!(f, "nullref"), - (false, HeapType::None) => write!(f, "(ref none)"), - (true, HeapType::NoExtern) => write!(f, "nullexternref"), - (false, HeapType::NoExtern) => write!(f, "(ref noextern)"), - (true, HeapType::NoFunc) => write!(f, "nullfuncref"), - (false, HeapType::NoFunc) => write!(f, "(ref nofunc)"), - (true, HeapType::Eq) => write!(f, "eqref"), - (false, HeapType::Eq) => write!(f, "(ref eq)"), - (true, HeapType::Struct) => write!(f, "structref"), - (false, HeapType::Struct) => write!(f, "(ref struct)"), - (true, HeapType::Array) => write!(f, "arrayref"), - (false, HeapType::Array) => write!(f, "(ref array)"), - (true, HeapType::I31) => write!(f, "i31ref"), - (false, HeapType::I31) => write!(f, "(ref i31)"), - (true, HeapType::Extern) => write!(f, "externref"), - (false, HeapType::Extern) => write!(f, "(ref extern)"), - (true, HeapType::Func) => write!(f, "funcref"), - (false, HeapType::Func) => write!(f, "(ref func)"), - (true, HeapType::Indexed(idx)) => write!(f, "(ref null {idx})"), - (false, HeapType::Indexed(idx)) => write!(f, "(ref {idx})"), - } - } -} - -// Static assert that we can fit indices up to `MAX_WASM_TYPES` inside `RefType`. -const _: () = { - const fn can_roundtrip_index(index: u32) -> bool { - assert!(RefType::can_represent_type_index(index)); - let rt = match RefType::indexed_func(true, index) { - Some(rt) => rt, - None => panic!(), - }; - assert!(rt.is_nullable()); - let actual_index = match rt.type_index() { - Some(i) => i, - None => panic!(), - }; - actual_index == index - } - - assert!(can_roundtrip_index(crate::limits::MAX_WASM_TYPES as u32)); - assert!(can_roundtrip_index(0b00000000_00001111_00000000_00000000)); - assert!(can_roundtrip_index(0b00000000_00000000_11111111_00000000)); - assert!(can_roundtrip_index(0b00000000_00000000_00000000_11111111)); - assert!(can_roundtrip_index(0)); -}; - -impl RefType { - const NULLABLE_BIT: u32 = 1 << 23; // bit #23 - const INDEXED_BIT: u32 = 1 << 22; // bit #22 - - const TYPE_MASK: u32 = 0b1111 << 18; // 4 bits #21-#18 (if `indexed == 0`) - const ANY_TYPE: u32 = 0b1111 << 18; - const EQ_TYPE: u32 = 0b1101 << 18; - const I31_TYPE: u32 = 0b1000 << 18; - const STRUCT_TYPE: u32 = 0b1001 << 18; - const ARRAY_TYPE: u32 = 0b1100 << 18; - const FUNC_TYPE: u32 = 0b0101 << 18; - const NOFUNC_TYPE: u32 = 0b0100 << 18; - const EXTERN_TYPE: u32 = 0b0011 << 18; - const NOEXTERN_TYPE: u32 = 0b0010 << 18; - const NONE_TYPE: u32 = 0b0000 << 18; - - const KIND_MASK: u32 = 0b11 << 20; // 2 bits #21-#20 (if `indexed == 1`) - const STRUCT_KIND: u32 = 0b10 << 20; - const ARRAY_KIND: u32 = 0b11 << 20; - const FUNC_KIND: u32 = 0b01 << 20; - - const INDEX_MASK: u32 = (1 << 20) - 1; // 20 bits #19-#0 (if `indexed == 1`) - - /// A nullable untyped function reference aka `(ref null func)` aka - /// `funcref` aka `anyfunc`. - pub const FUNCREF: Self = RefType::FUNC.nullable(); - - /// A nullable reference to an extern object aka `(ref null extern)` aka - /// `externref`. - pub const EXTERNREF: Self = RefType::EXTERN.nullable(); - - /// A non-nullable untyped function reference aka `(ref func)`. - pub const FUNC: Self = RefType::from_u32(Self::FUNC_TYPE); - - /// A non-nullable reference to an extern object aka `(ref extern)`. - pub const EXTERN: Self = RefType::from_u32(Self::EXTERN_TYPE); - - /// A non-nullable reference to any object aka `(ref any)`. - pub const ANY: Self = RefType::from_u32(Self::ANY_TYPE); - - /// A non-nullable reference to no object aka `(ref none)`. - pub const NONE: Self = RefType::from_u32(Self::NONE_TYPE); - - /// A non-nullable reference to a noextern object aka `(ref noextern)`. - pub const NOEXTERN: Self = RefType::from_u32(Self::NOEXTERN_TYPE); - - /// A non-nullable reference to a nofunc object aka `(ref nofunc)`. - pub const NOFUNC: Self = RefType::from_u32(Self::NOFUNC_TYPE); - - /// A non-nullable reference to an eq object aka `(ref eq)`. - pub const EQ: Self = RefType::from_u32(Self::EQ_TYPE); - - /// A non-nullable reference to a struct aka `(ref struct)`. - pub const STRUCT: Self = RefType::from_u32(Self::STRUCT_TYPE); - - /// A non-nullable reference to an array aka `(ref array)`. - pub const ARRAY: Self = RefType::from_u32(Self::ARRAY_TYPE); - - /// A non-nullable reference to an i31 object aka `(ref i31)`. - pub const I31: Self = RefType::from_u32(Self::I31_TYPE); - - const fn can_represent_type_index(index: u32) -> bool { - index & Self::INDEX_MASK == index - } - - const fn u24_to_u32(bytes: [u8; 3]) -> u32 { - let expanded_bytes = [bytes[0], bytes[1], bytes[2], 0]; - u32::from_le_bytes(expanded_bytes) - } - - const fn u32_to_u24(x: u32) -> [u8; 3] { - let bytes = x.to_le_bytes(); - debug_assert!(bytes[3] == 0); - [bytes[0], bytes[1], bytes[2]] - } - - #[inline] - const fn as_u32(&self) -> u32 { - Self::u24_to_u32(self.0) - } - - #[inline] - const fn from_u32(x: u32) -> Self { - debug_assert!(x & (0b11111111 << 24) == 0); - - // if not indexed, type must be any/eq/i31/struct/array/func/extern/nofunc/noextern/none - debug_assert!( - x & Self::INDEXED_BIT != 0 - || matches!( - x & Self::TYPE_MASK, - Self::ANY_TYPE - | Self::EQ_TYPE - | Self::I31_TYPE - | Self::STRUCT_TYPE - | Self::ARRAY_TYPE - | Self::FUNC_TYPE - | Self::NOFUNC_TYPE - | Self::EXTERN_TYPE - | Self::NOEXTERN_TYPE - | Self::NONE_TYPE - ) - ); - RefType(Self::u32_to_u24(x)) - } - - /// Create a reference to a typed function with the type at the given index. - /// - /// Returns `None` when the type index is beyond this crate's implementation - /// limits and therefore is not representable. - pub const fn indexed_func(nullable: bool, index: u32) -> Option { - Self::indexed(nullable, Self::FUNC_KIND, index) - } - - /// Create a reference to an array with the type at the given index. - /// - /// Returns `None` when the type index is beyond this crate's implementation - /// limits and therefore is not representable. - pub const fn indexed_array(nullable: bool, index: u32) -> Option { - Self::indexed(nullable, Self::ARRAY_KIND, index) - } - - /// Create a reference to a struct with the type at the given index. - /// - /// Returns `None` when the type index is beyond this crate's implementation - /// limits and therefore is not representable. - pub const fn indexed_struct(nullable: bool, index: u32) -> Option { - Self::indexed(nullable, Self::STRUCT_KIND, index) - } - - /// Create a reference to a user defined type at the given index. - /// - /// Returns `None` when the type index is beyond this crate's implementation - /// limits and therefore is not representable, or when the heap type is not - /// a typed array, struct or function. - const fn indexed(nullable: bool, kind: u32, index: u32) -> Option { - if Self::can_represent_type_index(index) { - let nullable32 = Self::NULLABLE_BIT * nullable as u32; - Some(RefType::from_u32( - nullable32 | Self::INDEXED_BIT | kind | index, - )) - } else { - None - } - } - - /// Create a new `RefType`. - /// - /// Returns `None` when the heap type's type index (if any) is beyond this - /// crate's implementation limits and therfore is not representable. - pub const fn new(nullable: bool, heap_type: HeapType) -> Option { - let nullable32 = Self::NULLABLE_BIT * nullable as u32; - match heap_type { - HeapType::Indexed(index) => RefType::indexed(nullable, 0, index), // 0 bc we don't know the kind - HeapType::Func => Some(Self::from_u32(nullable32 | Self::FUNC_TYPE)), - HeapType::Extern => Some(Self::from_u32(nullable32 | Self::EXTERN_TYPE)), - HeapType::Any => Some(Self::from_u32(nullable32 | Self::ANY_TYPE)), - HeapType::None => Some(Self::from_u32(nullable32 | Self::NONE_TYPE)), - HeapType::NoExtern => Some(Self::from_u32(nullable32 | Self::NOEXTERN_TYPE)), - HeapType::NoFunc => Some(Self::from_u32(nullable32 | Self::NOFUNC_TYPE)), - HeapType::Eq => Some(Self::from_u32(nullable32 | Self::EQ_TYPE)), - HeapType::Struct => Some(Self::from_u32(nullable32 | Self::STRUCT_TYPE)), - HeapType::Array => Some(Self::from_u32(nullable32 | Self::ARRAY_TYPE)), - HeapType::I31 => Some(Self::from_u32(nullable32 | Self::I31_TYPE)), - } - } - - /// Is this a reference to a typed function? - pub const fn is_typed_func_ref(&self) -> bool { - self.is_indexed_type_ref() && self.as_u32() & Self::KIND_MASK == Self::FUNC_KIND - } - - /// Is this a reference to an indexed type? - pub const fn is_indexed_type_ref(&self) -> bool { - self.as_u32() & Self::INDEXED_BIT != 0 - } - - /// If this is a reference to a typed function, get its type index. - pub const fn type_index(&self) -> Option { - if self.is_indexed_type_ref() { - Some(self.as_u32() & Self::INDEX_MASK) - } else { - None - } - } - - /// Is this an untyped function reference aka `(ref null func)` aka `funcref` aka `anyfunc`? - pub const fn is_func_ref(&self) -> bool { - !self.is_indexed_type_ref() && self.as_u32() & Self::TYPE_MASK == Self::FUNC_TYPE - } - - /// Is this a `(ref null extern)` aka `externref`? - pub const fn is_extern_ref(&self) -> bool { - !self.is_indexed_type_ref() && self.as_u32() & Self::TYPE_MASK == Self::EXTERN_TYPE - } - - /// Is this ref type nullable? - pub const fn is_nullable(&self) -> bool { - self.as_u32() & Self::NULLABLE_BIT != 0 - } - - /// Get the non-nullable version of this ref type. - pub const fn as_non_null(&self) -> Self { - Self::from_u32(self.as_u32() & !Self::NULLABLE_BIT) - } - - /// Get the non-nullable version of this ref type. - pub const fn nullable(&self) -> Self { - Self::from_u32(self.as_u32() | Self::NULLABLE_BIT) - } - - /// Get the heap type that this is a reference to. - pub fn heap_type(&self) -> HeapType { - let s = self.as_u32(); - if self.is_indexed_type_ref() { - HeapType::Indexed(self.type_index().unwrap()) - } else { - match s & Self::TYPE_MASK { - Self::FUNC_TYPE => HeapType::Func, - Self::EXTERN_TYPE => HeapType::Extern, - Self::ANY_TYPE => HeapType::Any, - Self::NONE_TYPE => HeapType::None, - Self::NOEXTERN_TYPE => HeapType::NoExtern, - Self::NOFUNC_TYPE => HeapType::NoFunc, - Self::EQ_TYPE => HeapType::Eq, - Self::STRUCT_TYPE => HeapType::Struct, - Self::ARRAY_TYPE => HeapType::Array, - Self::I31_TYPE => HeapType::I31, - _ => unreachable!(), - } - } - } - - // Note that this is similar to `Display for RefType` except that it has - // the indexes stubbed out. - pub(crate) fn wat(&self) -> &'static str { - match (self.is_nullable(), self.heap_type()) { - (true, HeapType::Func) => "funcref", - (true, HeapType::Extern) => "externref", - (true, HeapType::Indexed(_)) => "(ref null $type)", - (true, HeapType::Any) => "anyref", - (true, HeapType::None) => "nullref", - (true, HeapType::NoExtern) => "nullexternref", - (true, HeapType::NoFunc) => "nullfuncref", - (true, HeapType::Eq) => "eqref", - (true, HeapType::Struct) => "structref", - (true, HeapType::Array) => "arrayref", - (true, HeapType::I31) => "i31ref", - (false, HeapType::Func) => "(ref func)", - (false, HeapType::Extern) => "(ref extern)", - (false, HeapType::Indexed(_)) => "(ref $type)", - (false, HeapType::Any) => "(ref any)", - (false, HeapType::None) => "(ref none)", - (false, HeapType::NoExtern) => "(ref noextern)", - (false, HeapType::NoFunc) => "(ref nofunc)", - (false, HeapType::Eq) => "(ref eq)", - (false, HeapType::Struct) => "(ref struct)", - (false, HeapType::Array) => "(ref array)", - (false, HeapType::I31) => "(ref i31)", - } - } -} - impl Matches for RefType { fn matches<'a, F>(&self, other: &Self, type_at: &F) -> bool where @@ -580,67 +133,6 @@ impl<'a> FromReader<'a> for RefType { } } -impl fmt::Display for RefType { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - // Note that this is similar to `RefType::wat` except that it has the - // indexes filled out. - let s = match (self.is_nullable(), self.heap_type()) { - (true, HeapType::Func) => "funcref", - (true, HeapType::Extern) => "externref", - (true, HeapType::Indexed(i)) => return write!(f, "(ref null {i})"), - (true, HeapType::Any) => "anyref", - (true, HeapType::None) => "nullref", - (true, HeapType::NoExtern) => "nullexternref", - (true, HeapType::NoFunc) => "nullfuncref", - (true, HeapType::Eq) => "eqref", - (true, HeapType::Struct) => "structref", - (true, HeapType::Array) => "arrayref", - (true, HeapType::I31) => "i31ref", - (false, HeapType::Func) => "(ref func)", - (false, HeapType::Extern) => "(ref extern)", - (false, HeapType::Indexed(i)) => return write!(f, "(ref {i})"), - (false, HeapType::Any) => "(ref any)", - (false, HeapType::None) => "(ref none)", - (false, HeapType::NoExtern) => "(ref noextern)", - (false, HeapType::NoFunc) => "(ref nofunc)", - (false, HeapType::Eq) => "(ref eq)", - (false, HeapType::Struct) => "(ref struct)", - (false, HeapType::Array) => "(ref array)", - (false, HeapType::I31) => "(ref i31)", - }; - f.write_str(s) - } -} - -/// A heap type from function references. When the proposal is disabled, Index -/// is an invalid type. -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] -pub enum HeapType { - /// User defined type at the given index. - Indexed(u32), - /// Untyped (any) function. - Func, - /// External heap type. - Extern, - /// The `any` heap type. The common supertype (a.k.a. top) of all internal types. - Any, - /// The `none` heap type. The common subtype (a.k.a. bottom) of all internal types. - None, - /// The `noextern` heap type. The common subtype (a.k.a. bottom) of all external types. - NoExtern, - /// The `nofunc` heap type. The common subtype (a.k.a. bottom) of all function types. - NoFunc, - /// The `eq` heap type. The common supertype of all referenceable types on which comparison - /// (ref.eq) is allowed. - Eq, - /// The `struct` heap type. The common supertype of all struct types. - Struct, - /// The `array` heap type. The common supertype of all array types. - Array, - /// The i31 heap type. - I31, -} - impl Matches for HeapType { fn matches<'a, F>(&self, other: &Self, type_at: &F) -> bool where @@ -658,34 +150,34 @@ impl Matches for HeapType { (HT::NoFunc, HT::Func) => true, (HT::None, HT::I31 | HT::Array | HT::Struct) => true, - (HT::Indexed(a), HT::Eq | HT::Any) => matches!( - type_at(*a).structural_type, - StructuralType::Array(_) | StructuralType::Struct(_) + (HT::Concrete(a), HT::Eq | HT::Any) => matches!( + type_at(*a).composite_type, + CompositeType::Array(_) | CompositeType::Struct(_) ), - (HT::Indexed(a), HT::Struct) => { - matches!(type_at(*a).structural_type, StructuralType::Struct(_)) + (HT::Concrete(a), HT::Struct) => { + matches!(type_at(*a).composite_type, CompositeType::Struct(_)) } - (HT::Indexed(a), HT::Array) => { - matches!(type_at(*a).structural_type, StructuralType::Array(_)) + (HT::Concrete(a), HT::Array) => { + matches!(type_at(*a).composite_type, CompositeType::Array(_)) } - (HT::Indexed(a), HT::Func) => { - matches!(type_at(*a).structural_type, StructuralType::Func(_)) + (HT::Concrete(a), HT::Func) => { + matches!(type_at(*a).composite_type, CompositeType::Func(_)) } - (HT::Indexed(a), HT::Indexed(b)) => type_at(*a) - .structural_type - .matches(&type_at(*b).structural_type, type_at), + (HT::Concrete(a), HT::Concrete(b)) => type_at(*a) + .composite_type + .matches(&type_at(*b).composite_type, type_at), - (HT::None, HT::Indexed(b)) => matches!( - type_at(*b).structural_type, - StructuralType::Array(_) | StructuralType::Struct(_) + (HT::None, HT::Concrete(b)) => matches!( + type_at(*b).composite_type, + CompositeType::Array(_) | CompositeType::Struct(_) ), - (HT::NoFunc, HT::Indexed(b)) => { - matches!(type_at(*b).structural_type, StructuralType::Func(_)) + (HT::NoFunc, HT::Concrete(b)) => { + matches!(type_at(*b).composite_type, CompositeType::Func(_)) } _ => false, @@ -743,231 +235,35 @@ impl<'a> FromReader<'a> for HeapType { bail!(reader.original_position(), "invalid indexed ref heap type"); } }; - Ok(HeapType::Indexed(idx)) + Ok(HeapType::Concrete(idx)) } } } } -/// Represents a structural type in a WebAssembly module. -#[derive(Debug, Clone)] -pub enum StructuralType { - /// The type is for a function. - Func(FuncType), - /// The type is for an array. - Array(ArrayType), - /// The type is for a struct. - Struct(StructType), -} - -impl StructuralType { - /// Unwrap a `FuncType` or panic. - pub fn unwrap_func(&self) -> &FuncType { - match self { - Self::Func(f) => f, - _ => panic!("not a func"), - } - } -} - -/// Represents a subtype of possible other types in a WebAssembly module. -#[derive(Debug, Clone)] -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. - pub supertype_idx: Option, - /// The structural type of the subtype. - pub structural_type: StructuralType, -} - -impl SubType { - /// Unwrap a `FuncType` or panic. - pub fn unwrap_func(&self) -> &FuncType { - self.structural_type.unwrap_func() - } -} - -/// Represents a recursive type group in a WebAssembly module. -#[derive(Debug, Clone)] -pub enum RecGroup { - /// The list of subtypes in the recursive type group. - Many(Vec), - /// A single subtype in the recursive type group. - Single(SubType), -} - -impl RecGroup { - /// Returns the list of subtypes in the recursive type group. - pub fn types(&self) -> &[SubType] { - match self { - RecGroup::Many(types) => types, - RecGroup::Single(ty) => slice::from_ref(ty), - } - } - - /// Return an iterator over the types in this rec group, giving ownership of - /// the types. - pub fn into_types(self) -> impl ExactSizeIterator { - return match self { - RecGroup::Single(ty) => IntoIter::Single(Some(ty)), - RecGroup::Many(tys) => IntoIter::Many(tys.into_iter()), - }; - - enum IntoIter { - Single(Option), - Many(std::vec::IntoIter), - } - - impl Iterator for IntoIter { - type Item = SubType; - - fn next(&mut self) -> Option { - match self { - IntoIter::Single(ty) => ty.take(), - IntoIter::Many(tys) => tys.next(), - } - } - - fn size_hint(&self) -> (usize, Option) { - match self { - IntoIter::Single(None) => (0, Some(0)), - IntoIter::Single(Some(_)) => (1, Some(1)), - IntoIter::Many(tys) => tys.size_hint(), - } - } - } - - impl ExactSizeIterator for IntoIter {} - } -} - impl Matches for SubType { fn matches<'a, F>(&self, other: &Self, type_at: &F) -> bool where F: Fn(u32) -> &'a SubType, { - !other.is_final - && self - .structural_type - .matches(&other.structural_type, type_at) + !other.is_final && self.composite_type.matches(&other.composite_type, type_at) } } -/// Represents a type of a function in a WebAssembly module. -#[derive(Clone, Eq, PartialEq, Hash)] -pub struct FuncType { - /// The combined parameters and result types. - params_results: Box<[ValType]>, - /// The number of parameter types. - len_params: usize, -} - -/// Represents a type of an array in a WebAssembly module. -#[derive(Debug, Clone, Eq, PartialEq, Hash)] -pub struct ArrayType(pub FieldType); - -/// Represents a field type of an array or a struct. -#[derive(Debug, Clone, Eq, PartialEq, Hash)] -pub struct FieldType { - /// Array element type. - pub element_type: StorageType, - /// Are elements mutable. - pub mutable: bool, -} - -/// Represents a type of a struct in a WebAssembly module. -#[derive(Debug, Clone, Eq, PartialEq, Hash)] -pub struct StructType { - /// Struct fields. - pub fields: Box<[FieldType]>, -} - -impl Matches for StructuralType { +impl Matches for CompositeType { fn matches<'a, F>(&self, other: &Self, type_at: &F) -> bool where F: Fn(u32) -> &'a SubType, { match (self, other) { - (StructuralType::Func(a), StructuralType::Func(b)) => a.matches(b, type_at), - (StructuralType::Array(a), StructuralType::Array(b)) => a.matches(b, type_at), - (StructuralType::Struct(a), StructuralType::Struct(b)) => a.matches(b, type_at), + (CompositeType::Func(a), CompositeType::Func(b)) => a.matches(b, type_at), + (CompositeType::Array(a), CompositeType::Array(b)) => a.matches(b, type_at), + (CompositeType::Struct(a), CompositeType::Struct(b)) => a.matches(b, type_at), _ => false, } } } -impl Debug for FuncType { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("FuncType") - .field("params", &self.params()) - .field("returns", &self.results()) - .finish() - } -} - -impl FuncType { - /// Creates a new [`FuncType`] from the given `params` and `results`. - pub fn new(params: P, results: R) -> Self - where - P: IntoIterator, - R: IntoIterator, - { - let mut buffer = params.into_iter().collect::>(); - let len_params = buffer.len(); - buffer.extend(results); - Self { - params_results: buffer.into(), - len_params, - } - } - - /// Creates a new [`FuncType`] fom its raw parts. - /// - /// # Panics - /// - /// If `len_params` is greater than the length of `params_results` combined. - pub(crate) fn from_raw_parts(params_results: Box<[ValType]>, len_params: usize) -> Self { - assert!(len_params <= params_results.len()); - Self { - params_results, - len_params, - } - } - - /// Returns a shared slice to the parameter types of the [`FuncType`]. - #[inline] - pub fn params(&self) -> &[ValType] { - &self.params_results[..self.len_params] - } - - /// Returns a shared slice to the result types of the [`FuncType`]. - #[inline] - pub fn results(&self) -> &[ValType] { - &self.params_results[self.len_params..] - } - - pub(crate) fn desc(&self) -> String { - let mut s = String::new(); - s.push_str("["); - for (i, param) in self.params().iter().enumerate() { - if i > 0 { - s.push_str(" "); - } - write!(s, "{param}").unwrap(); - } - s.push_str("] -> ["); - for (i, result) in self.results().iter().enumerate() { - if i > 0 { - s.push_str(" "); - } - write!(s, "{result}").unwrap(); - } - s.push_str("]"); - s - } -} - impl Matches for FuncType { fn matches<'a, F>(&self, other: &Self, type_at: &F) -> bool where @@ -1122,16 +418,17 @@ impl<'a> TypeSectionReader<'a> { pub fn into_iter_err_on_gc_types(self) -> impl Iterator> + 'a { self.into_iter_with_offsets().map(|item| { let (offset, group) = item?; - let ty = match group { - RecGroup::Single(ty) => ty, - RecGroup::Many(_) => bail!(offset, "gc proposal not supported"), + let mut types = group.into_types(); + let ty = match (types.next(), types.next()) { + (Some(ty), None) => ty, + _ => bail!(offset, "gc proposal not supported"), }; if !ty.is_final || ty.supertype_idx.is_some() { bail!(offset, "gc proposal not supported"); } - match ty.structural_type { - StructuralType::Func(f) => Ok(f), - StructuralType::Array(_) | StructuralType::Struct(_) => { + match ty.composite_type { + CompositeType::Func(f) => Ok(f), + CompositeType::Array(_) | CompositeType::Struct(_) => { bail!(offset, "gc proposal not supported"); } } @@ -1139,34 +436,34 @@ impl<'a> TypeSectionReader<'a> { } } -impl<'a> FromReader<'a> for StructuralType { +impl<'a> FromReader<'a> for CompositeType { fn from_reader(reader: &mut BinaryReader<'a>) -> Result { - read_structural_type(reader.read_u8()?, reader) + read_composite_type(reader.read_u8()?, reader) } } -fn read_structural_type( +fn read_composite_type( opcode: u8, reader: &mut BinaryReader, -) -> Result { +) -> Result { Ok(match opcode { - 0x60 => StructuralType::Func(reader.read()?), - 0x5e => StructuralType::Array(reader.read()?), - 0x5f => StructuralType::Struct(reader.read()?), + 0x60 => CompositeType::Func(reader.read()?), + 0x5e => CompositeType::Array(reader.read()?), + 0x5f => CompositeType::Struct(reader.read()?), x => return reader.invalid_leading_byte(x, "type"), }) } impl<'a> FromReader<'a> for RecGroup { fn from_reader(reader: &mut BinaryReader<'a>) -> Result { - Ok(match reader.peek()? { + match reader.peek()? { 0x4e => { reader.read_u8()?; let types = reader.read_iter(MAX_WASM_TYPES, "rec group types")?; - RecGroup::Many(types.collect::>()?) + Ok(RecGroup::explicit(types.collect::>()?)) } - _ => RecGroup::Single(reader.read()?), - }) + _ => Ok(RecGroup::implicit(reader.read()?)), + } } } @@ -1186,13 +483,13 @@ impl<'a> FromReader<'a> for SubType { SubType { is_final: opcode == 0x4f, supertype_idx: idxs.first().copied(), - structural_type: read_structural_type(reader.read_u8()?, reader)?, + composite_type: read_composite_type(reader.read_u8()?, reader)?, } } opcode => SubType { is_final: true, supertype_idx: None, - structural_type: read_structural_type(opcode, reader)?, + composite_type: read_composite_type(opcode, reader)?, }, }) } diff --git a/crates/wasmparser/src/validator.rs b/crates/wasmparser/src/validator.rs index c64233f81b..30a8b3a52b 100644 --- a/crates/wasmparser/src/validator.rs +++ b/crates/wasmparser/src/validator.rs @@ -311,7 +311,7 @@ impl WasmFeatures { } // indexed types require at least the function-references // proposal - (HeapType::Indexed(_), _) => { + (HeapType::Concrete(_), _) => { if self.function_references { Ok(()) } else { diff --git a/crates/wasmparser/src/validator/component.rs b/crates/wasmparser/src/validator/component.rs index 8550bf280d..97e5ff888d 100644 --- a/crates/wasmparser/src/validator/component.rs +++ b/crates/wasmparser/src/validator/component.rs @@ -19,9 +19,9 @@ use crate::{ TupleType, TypeInfo, VariantType, }, BinaryReaderError, CanonicalOption, ComponentExternName, ComponentExternalKind, - ComponentOuterAliasKind, ComponentTypeRef, ExternalKind, FuncType, GlobalType, - InstantiationArgKind, MemoryType, RecGroup, Result, StructuralType, SubType, TableType, - TypeBounds, ValType, WasmFeatures, + ComponentOuterAliasKind, ComponentTypeRef, CompositeType, ExternalKind, FuncType, GlobalType, + InstantiationArgKind, MemoryType, RecGroup, Result, SubType, TableType, TypeBounds, ValType, + WasmFeatures, }; use indexmap::{map::Entry, IndexMap, IndexSet}; use std::collections::{HashMap, HashSet}; @@ -392,7 +392,7 @@ impl ComponentState { // function and has the correct signature. if let Some(dtor) = dtor { let ty = component.core_function_at(dtor, offset)?; - let ty = types[ty].structural_type.unwrap_func(); + let ty = types[ty].composite_type.unwrap_func(); if ty.params() != [rep] || ty.results() != [] { bail!( offset, @@ -1002,7 +1002,7 @@ impl ComponentState { let lowered_ty = SubType { is_final: false, supertype_idx: None, - structural_type: StructuralType::Func(info.into_func_type()), + composite_type: CompositeType::Func(info.into_func_type()), }; let id = types.push_ty(lowered_ty); @@ -1021,7 +1021,7 @@ impl ComponentState { let core_ty = SubType { is_final: false, supertype_idx: None, - structural_type: StructuralType::Func(FuncType::new([rep], [ValType::I32])), + composite_type: CompositeType::Func(FuncType::new([rep], [ValType::I32])), }; self.core_funcs.push(types.push_ty(core_ty)); Ok(()) @@ -1037,7 +1037,7 @@ impl ComponentState { let core_ty = SubType { is_final: false, supertype_idx: None, - structural_type: StructuralType::Func(FuncType::new([ValType::I32], [])), + composite_type: CompositeType::Func(FuncType::new([ValType::I32], [])), }; self.core_funcs.push(types.push_ty(core_ty)); Ok(()) @@ -1053,7 +1053,7 @@ impl ComponentState { let core_ty = SubType { is_final: false, supertype_idx: None, - structural_type: StructuralType::Func(FuncType::new([ValType::I32], [rep])), + composite_type: CompositeType::Func(FuncType::new([ValType::I32], [rep])), }; self.core_funcs.push(types.push_ty(core_ty)); Ok(()) @@ -1473,7 +1473,7 @@ impl ComponentState { for decl in decls { match decl { crate::ModuleTypeDeclaration::Type(ty) => { - state.add_types(&RecGroup::Single(ty), features, types, offset, true)?; + state.add_types(&RecGroup::implicit(ty), features, types, offset, true)?; } crate::ModuleTypeDeclaration::Export { name, ty } => { let ty = state.check_type_ref(&ty, features, types, offset)?; @@ -1482,9 +1482,10 @@ impl ComponentState { crate::ModuleTypeDeclaration::OuterAlias { kind, count, index } => { if count > 1 { return Err(BinaryReaderError::new( - "outer type aliases in module type declarations are limited to a maximum count of 1", - offset, - )); + "outer type aliases in module type declarations are limited to a \ + maximum count of 1", + offset, + )); } match kind { crate::OuterAliasKind::Type => { diff --git a/crates/wasmparser/src/validator/core.rs b/crates/wasmparser/src/validator/core.rs index 46fee3e7d5..464c0b49a8 100644 --- a/crates/wasmparser/src/validator/core.rs +++ b/crates/wasmparser/src/validator/core.rs @@ -9,9 +9,9 @@ use crate::limits::*; use crate::readers::Matches; use crate::validator::core::arc::MaybeOwned; use crate::{ - BinaryReaderError, ConstExpr, Data, DataKind, Element, ElementKind, ExternalKind, FuncType, - Global, GlobalType, HeapType, MemoryType, RecGroup, RefType, Result, StorageType, - StructuralType, SubType, Table, TableInit, TableType, TagType, TypeRef, ValType, VisitOperator, + BinaryReaderError, CompositeType, ConstExpr, Data, DataKind, Element, ElementKind, + ExternalKind, FuncType, Global, GlobalType, HeapType, MemoryType, RecGroup, RefType, Result, + StorageType, SubType, Table, TableInit, TableType, TagType, TypeRef, ValType, VisitOperator, WasmFeatures, WasmModuleResources, }; @@ -504,7 +504,8 @@ impl Module { offset: usize, check_limit: bool, ) -> Result<()> { - if matches!(&rec_group, RecGroup::Many(_)) && !features.gc { + debug_assert!(rec_group.explicit_rec_group || rec_group.types.len() == 1); + if rec_group.explicit_rec_group && !features.gc { bail!( offset, "rec group usage requires `gc` proposal to be enabled" @@ -556,7 +557,7 @@ impl Module { bail!(offset, "gc proposal must be enabled to use subtypes"); } - self.check_structural_type(&ty.structural_type, features, offset)?; + self.check_composite_type(&ty.composite_type, features, offset)?; if let Some(supertype_index) = ty.supertype_idx { // Check the supertype exists, is not final, and the subtype matches it. @@ -575,14 +576,14 @@ impl Module { Ok(()) } - fn check_structural_type( + fn check_composite_type( &mut self, - ty: &StructuralType, + ty: &CompositeType, features: &WasmFeatures, offset: usize, ) -> Result<()> { match ty { - StructuralType::Func(t) => { + CompositeType::Func(t) => { for ty in t.params().iter().chain(t.results()) { self.check_value_type(*ty, features, offset)?; } @@ -593,7 +594,7 @@ impl Module { )); } } - StructuralType::Array(t) => { + CompositeType::Array(t) => { if !features.gc { return Err(BinaryReaderError::new( "array indexed types not supported without the gc feature", @@ -607,7 +608,7 @@ impl Module { } }; } - StructuralType::Struct(t) => { + CompositeType::Struct(t) => { if !features.gc { return Err(BinaryReaderError::new( "struct indexed types not supported without the gc feature", @@ -756,8 +757,8 @@ impl Module { types: &'a TypeList, offset: usize, ) -> Result<&'a FuncType> { - match &self.sub_type_at(types, type_index, offset)?.structural_type { - StructuralType::Func(f) => Ok(f), + match &self.sub_type_at(types, type_index, offset)?.composite_type { + CompositeType::Func(f) => Ok(f), _ => bail!(offset, "type index {type_index} is not a function type"), } } @@ -928,7 +929,7 @@ impl Module { | HeapType::Struct | HeapType::Array | HeapType::I31 => (), - HeapType::Indexed(type_index) => { + HeapType::Concrete(type_index) => { // Just check that the index is valid self.type_id_at(type_index, offset)?; } @@ -1140,7 +1141,11 @@ impl WasmModuleResources for OperatorValidatorResources<'_> { } fn func_type_at(&self, at: u32) -> Option<&Self::FuncType> { - Some(self.types[*self.module.types.get(at as usize)?].unwrap_func()) + let id = *self.module.types.get(at as usize)?; + match &self.types[id].composite_type { + CompositeType::Func(f) => Some(f), + _ => None, + } } fn type_index_of_function(&self, at: u32) -> Option { @@ -1192,7 +1197,11 @@ impl WasmModuleResources for ValidatorResources { } fn tag_at(&self, at: u32) -> Option<&Self::FuncType> { - Some(self.0.snapshot.as_ref().unwrap()[*self.0.tags.get(at as usize)?].unwrap_func()) + let id = *self.0.tags.get(at as usize)?; + match &self.0.snapshot.as_ref().unwrap()[id].composite_type { + CompositeType::Func(f) => Some(f), + _ => None, + } } fn global_at(&self, at: u32) -> Option { @@ -1200,7 +1209,11 @@ impl WasmModuleResources for ValidatorResources { } fn func_type_at(&self, at: u32) -> Option<&Self::FuncType> { - Some(self.0.snapshot.as_ref().unwrap()[*self.0.types.get(at as usize)?].unwrap_func()) + let id = *self.0.types.get(at as usize)?; + match &self.0.snapshot.as_ref().unwrap()[id].composite_type { + CompositeType::Func(f) => Some(f), + _ => None, + } } fn type_index_of_function(&self, at: u32) -> Option { diff --git a/crates/wasmparser/src/validator/operators.rs b/crates/wasmparser/src/validator/operators.rs index 308a464b73..feab8786c4 100644 --- a/crates/wasmparser/src/validator/operators.rs +++ b/crates/wasmparser/src/validator/operators.rs @@ -1272,13 +1272,13 @@ where Ok(()) } fn visit_call_ref(&mut self, type_index: u32) -> Self::Output { - let hty = HeapType::Indexed(type_index); + let hty = HeapType::Concrete(type_index); self.resources .check_heap_type(hty, &self.features, self.offset)?; // If `None` is popped then that means a "bottom" type was popped which // is always considered equivalent to the `hty` tag. if let Some(rt) = self.pop_ref()? { - let expected = RefType::indexed_func(true, type_index) + let expected = RefType::concrete(true, type_index) .expect("existing heap types should be within our limits"); if !self .resources @@ -2292,7 +2292,7 @@ where // proposals. if self.features.function_references { self.push_operand( - RefType::indexed_func(false, type_index) + RefType::concrete(false, type_index) .expect("our limits on number of types should fit into ref type"), )?; } else { diff --git a/crates/wasmparser/src/validator/types.rs b/crates/wasmparser/src/validator/types.rs index eec81143ff..786f63b5da 100644 --- a/crates/wasmparser/src/validator/types.rs +++ b/crates/wasmparser/src/validator/types.rs @@ -6,8 +6,8 @@ use super::{ }; use crate::validator::names::KebabString; use crate::{ - BinaryReaderError, Export, ExternalKind, FuncType, GlobalType, Import, MemoryType, - PrimitiveValType, RefType, Result, StructuralType, SubType, TableType, TypeRef, ValType, + BinaryReaderError, CompositeType, Export, ExternalKind, FuncType, GlobalType, Import, + MemoryType, PrimitiveValType, RefType, Result, SubType, TableType, TypeRef, ValType, }; use indexmap::{IndexMap, IndexSet}; use std::collections::HashMap; @@ -294,10 +294,10 @@ impl TypeData for SubType { fn type_info(&self, _types: &TypeList) -> TypeInfo { // TODO(#1036): calculate actual size for func, array, struct. - let size = 1 + match &self.structural_type { - StructuralType::Func(ty) => 1 + (ty.params().len() + ty.results().len()) as u32, - StructuralType::Array(_) => 2, - StructuralType::Struct(ty) => 1 + 2 * ty.fields.len() as u32, + let size = 1 + match &self.composite_type { + CompositeType::Func(ty) => 1 + (ty.params().len() + ty.results().len()) as u32, + CompositeType::Array(_) => 2, + CompositeType::Struct(ty) => 1 + 2 * ty.fields.len() as u32, }; TypeInfo::core(size) } @@ -314,10 +314,10 @@ impl CoreType { /// Get the underlying `FuncType` within this `SubType` or panic. pub fn unwrap_func(&self) -> &FuncType { - match &self.unwrap_sub().structural_type { - StructuralType::Func(f) => f, - StructuralType::Array(_) | StructuralType::Struct(_) => { - panic!("`unwrap_func` on non-func structural type") + match &self.unwrap_sub().composite_type { + CompositeType::Func(f) => f, + CompositeType::Array(_) | CompositeType::Struct(_) => { + panic!("`unwrap_func` on non-func composite type") } } } diff --git a/crates/wasmprinter/src/lib.rs b/crates/wasmprinter/src/lib.rs index 1a6b0386e8..a5cdc8cf03 100644 --- a/crates/wasmprinter/src/lib.rs +++ b/crates/wasmprinter/src/lib.rs @@ -641,9 +641,9 @@ impl Printer { )?; let ty = match ty { CoreType::Sub(ty) => { - let ty = match &ty.structural_type { - StructuralType::Func(f) => f, - StructuralType::Array(_) | StructuralType::Struct(_) => { + let ty = match &ty.composite_type { + CompositeType::Func(f) => f, + CompositeType::Array(_) | CompositeType::Struct(_) => { unreachable!("Wasm GC types cannot appear in components yet") } }; @@ -654,7 +654,7 @@ impl Printer { Some(SubType { is_final: true, supertype_idx: None, - structural_type: StructuralType::Func(ty.clone()), + composite_type: CompositeType::Func(ty.clone()), }) } CoreType::Module(decls) => { @@ -668,7 +668,12 @@ impl Printer { Ok(()) } - fn print_rec(&mut self, state: &mut State, offset: usize, types: Vec) -> Result<()> { + fn print_rec( + &mut self, + state: &mut State, + offset: usize, + types: impl Iterator, + ) -> Result<()> { self.start_group("rec"); for ty in types { self.newline(offset + 2); @@ -692,35 +697,35 @@ impl Printer { let r = if !ty.is_final || !ty.supertype_idx.is_none() { self.start_group("sub"); self.print_sub_type(state, ty)?; - let r = self.print_structural(state, &ty.structural_type, names_for)?; + let r = self.print_composite(state, &ty.composite_type, names_for)?; self.end_group(); // `sub` r } else { - self.print_structural(state, &ty.structural_type, names_for)? + self.print_composite(state, &ty.composite_type, names_for)? }; Ok(r) } - fn print_structural( + fn print_composite( &mut self, state: &State, - ty: &StructuralType, + ty: &CompositeType, names_for: Option, ) -> Result { let r = match &ty { - StructuralType::Func(ty) => { + CompositeType::Func(ty) => { self.start_group("func"); let r = self.print_func_type(state, ty, names_for)?; self.end_group(); // `func` r } - StructuralType::Array(ty) => { + CompositeType::Array(ty) => { self.start_group("array"); let r = self.print_array_type(ty)?; self.end_group(); // `array` r } - StructuralType::Struct(ty) => { + CompositeType::Struct(ty) => { self.start_group("struct"); let r = self.print_struct_type(ty)?; self.end_group(); // `struct` @@ -748,9 +753,11 @@ impl Printer { for ty in parser.into_iter_with_offsets() { let (offset, rec_group) = ty?; self.newline(offset); - match rec_group { - RecGroup::Many(items) => self.print_rec(state, offset, items)?, - RecGroup::Single(ty) => self.print_type(state, ty)?, + if rec_group.types().len() == 1 { + let ty = rec_group.into_types().next().unwrap(); + self.print_type(state, ty)?; + } else { + self.print_rec(state, offset, rec_group.into_types())? } } @@ -767,7 +774,7 @@ impl Printer { match state.core.types.get(idx as usize) { Some(Some(SubType { - structural_type: StructuralType::Func(ty), + composite_type: CompositeType::Func(ty), .. })) => self.print_func_type(state, ty, names_for).map(Some), Some(Some(_)) | Some(None) | None => Ok(None), @@ -905,7 +912,7 @@ impl Printer { HeapType::Struct => self.result.push_str("struct"), HeapType::Array => self.result.push_str("array"), HeapType::I31 => self.result.push_str("i31"), - HeapType::Indexed(i) => self.result.push_str(&format!("{}", u32::from(i))), + HeapType::Concrete(i) => self.result.push_str(&format!("{}", u32::from(i))), } Ok(()) } diff --git a/crates/wast/src/component/binary.rs b/crates/wast/src/component/binary.rs index 7edb4c2b16..24e4589332 100644 --- a/crates/wast/src/component/binary.rs +++ b/crates/wast/src/component/binary.rs @@ -602,8 +602,8 @@ impl From> for wasm_encoder::HeapType { core::HeapType::Exn => { todo!("encoding of exceptions proposal types not yet implemented") } - core::HeapType::Index(Index::Num(i, _)) => Self::Indexed(i), - core::HeapType::Index(_) => panic!("unresolved index"), + core::HeapType::Concrete(Index::Num(i, _)) => Self::Concrete(i), + core::HeapType::Concrete(_) => panic!("unresolved index"), core::HeapType::Any | core::HeapType::Eq | core::HeapType::Struct diff --git a/crates/wast/src/component/resolve.rs b/crates/wast/src/component/resolve.rs index 3e3169aa6d..c0122ef73f 100644 --- a/crates/wast/src/component/resolve.rs +++ b/crates/wast/src/component/resolve.rs @@ -532,7 +532,7 @@ impl<'a> Resolver<'a> { | core::HeapType::None | core::HeapType::NoFunc | core::HeapType::NoExtern => {} - core::HeapType::Index(id) => { + core::HeapType::Concrete(id) => { self.resolve_ns(id, Ns::Type)?; } }, diff --git a/crates/wast/src/core/binary.rs b/crates/wast/src/core/binary.rs index 3cfe18a356..2daba2aaae 100644 --- a/crates/wast/src/core/binary.rs +++ b/crates/wast/src/core/binary.rs @@ -257,8 +257,8 @@ impl<'a> Encode for HeapType<'a> { HeapType::None => e.push(0x71), // Note that this is encoded as a signed leb128 so be sure to cast // to an i64 first - HeapType::Index(Index::Num(n, _)) => i64::from(*n).encode(e), - HeapType::Index(Index::Id(n)) => { + HeapType::Concrete(Index::Num(n, _)) => i64::from(*n).encode(e), + HeapType::Concrete(Index::Id(n)) => { panic!("unresolved index in emission: {:?}", n) } } diff --git a/crates/wast/src/core/resolve/names.rs b/crates/wast/src/core/resolve/names.rs index 4f252ad44b..b06eb67f9f 100644 --- a/crates/wast/src/core/resolve/names.rs +++ b/crates/wast/src/core/resolve/names.rs @@ -293,7 +293,7 @@ impl<'a> Resolver<'a> { fn resolve_heaptype(&self, ty: &mut HeapType<'a>) -> Result<(), Error> { match ty { - HeapType::Index(i) => { + HeapType::Concrete(i) => { self.resolve(i, Ns::Type)?; } _ => {} diff --git a/crates/wast/src/core/types.rs b/crates/wast/src/core/types.rs index 4bf69fe051..5f9489fd9c 100644 --- a/crates/wast/src/core/types.rs +++ b/crates/wast/src/core/types.rs @@ -86,9 +86,9 @@ pub enum HeapType<'a> { NoExtern, /// The bottom type of the anyref hierarchy. Part of the GC proposal. None, - /// A reference to a function, struct, or array: ref T. This is part of the - /// GC proposal. - Index(Index<'a>), + /// A reference to a concrete function, struct, or array type defined by + /// Wasm: `ref T`. This is part of the function references and GC proposals. + Concrete(Index<'a>), } impl<'a> Parse<'a> for HeapType<'a> { @@ -128,7 +128,7 @@ impl<'a> Parse<'a> for HeapType<'a> { parser.parse::()?; Ok(HeapType::None) } else if l.peek::()? { - Ok(HeapType::Index(parser.parse()?)) + Ok(HeapType::Concrete(parser.parse()?)) } else { Err(l.error()) } diff --git a/crates/wit-component/src/gc.rs b/crates/wit-component/src/gc.rs index c4e1dff613..c5a7928b19 100644 --- a/crates/wit-component/src/gc.rs +++ b/crates/wit-component/src/gc.rs @@ -492,7 +492,7 @@ impl<'a> Module<'a> { | HeapType::Struct | HeapType::Array | HeapType::I31 => {} - HeapType::Indexed(i) => self.ty(i), + HeapType::Concrete(i) => self.ty(i), } } @@ -1114,7 +1114,7 @@ impl Encoder { HeapType::Struct => wasm_encoder::HeapType::Struct, HeapType::Array => wasm_encoder::HeapType::Array, HeapType::I31 => wasm_encoder::HeapType::I31, - HeapType::Indexed(idx) => wasm_encoder::HeapType::Indexed(self.types.remap(idx)), + HeapType::Concrete(idx) => wasm_encoder::HeapType::Concrete(self.types.remap(idx)), } } } diff --git a/tests/cli/dump/alias.wat.stdout b/tests/cli/dump/alias.wat.stdout index a8089ee796..fb83075d3a 100644 --- a/tests/cli/dump/alias.wat.stdout +++ b/tests/cli/dump/alias.wat.stdout @@ -30,7 +30,7 @@ | 01 00 00 00 0x57 | 01 04 | type section 0x59 | 01 | 1 count - 0x5a | 60 00 00 | [type 0] Single(SubType { is_final: true, supertype_idx: None, structural_type: Func(FuncType { params: [], returns: [] }) }) + 0x5a | 60 00 00 | [type 0] RecGroup { types: [SubType { is_final: true, supertype_idx: None, composite_type: Func(FuncType { params: [], results: [] }) }], explicit_rec_group: false } 0x5d | 03 02 | func section 0x5f | 01 | 1 count 0x60 | 00 | [func 0] type 0 diff --git a/tests/cli/dump/alias2.wat.stdout b/tests/cli/dump/alias2.wat.stdout index b43761d624..a9ed9e4fe9 100644 --- a/tests/cli/dump/alias2.wat.stdout +++ b/tests/cli/dump/alias2.wat.stdout @@ -126,7 +126,7 @@ | 01 00 00 00 0x158 | 01 04 | type section 0x15a | 01 | 1 count - 0x15b | 60 00 00 | [type 0] Single(SubType { is_final: true, supertype_idx: None, structural_type: Func(FuncType { params: [], returns: [] }) }) + 0x15b | 60 00 00 | [type 0] RecGroup { types: [SubType { is_final: true, supertype_idx: None, composite_type: Func(FuncType { params: [], results: [] }) }], explicit_rec_group: false } 0x15e | 03 02 | func section 0x160 | 01 | 1 count 0x161 | 00 | [func 0] type 0 @@ -162,7 +162,7 @@ | 01 00 00 00 0x1a2 | 01 04 | type section 0x1a4 | 01 | 1 count - 0x1a5 | 60 00 00 | [type 0] Single(SubType { is_final: true, supertype_idx: None, structural_type: Func(FuncType { params: [], returns: [] }) }) + 0x1a5 | 60 00 00 | [type 0] RecGroup { types: [SubType { is_final: true, supertype_idx: None, composite_type: Func(FuncType { params: [], results: [] }) }], explicit_rec_group: false } 0x1a8 | 02 19 | import section 0x1aa | 04 | 4 count 0x1ab | 00 01 31 00 | import [func 0] Import { module: "", name: "1", ty: Func(0) } diff --git a/tests/cli/dump/blockty.wat.stdout b/tests/cli/dump/blockty.wat.stdout index 9dc651ec29..3e212dbae8 100644 --- a/tests/cli/dump/blockty.wat.stdout +++ b/tests/cli/dump/blockty.wat.stdout @@ -2,12 +2,12 @@ | 01 00 00 00 0x8 | 01 17 | type section 0xa | 05 | 5 count - 0xb | 60 00 00 | [type 0] Single(SubType { is_final: true, supertype_idx: None, structural_type: Func(FuncType { params: [], returns: [] }) }) - 0xe | 60 00 01 7f | [type 1] Single(SubType { is_final: true, supertype_idx: None, structural_type: Func(FuncType { params: [], returns: [I32] }) }) - 0x12 | 60 01 7f 00 | [type 2] Single(SubType { is_final: true, supertype_idx: None, structural_type: Func(FuncType { params: [I32], returns: [] }) }) - 0x16 | 60 01 7f 01 | [type 3] Single(SubType { is_final: true, supertype_idx: None, structural_type: Func(FuncType { params: [I32], returns: [I32] }) }) + 0xb | 60 00 00 | [type 0] RecGroup { types: [SubType { is_final: true, supertype_idx: None, composite_type: Func(FuncType { params: [], results: [] }) }], explicit_rec_group: false } + 0xe | 60 00 01 7f | [type 1] RecGroup { types: [SubType { is_final: true, supertype_idx: None, composite_type: Func(FuncType { params: [], results: [I32] }) }], explicit_rec_group: false } + 0x12 | 60 01 7f 00 | [type 2] RecGroup { types: [SubType { is_final: true, supertype_idx: None, composite_type: Func(FuncType { params: [I32], results: [] }) }], explicit_rec_group: false } + 0x16 | 60 01 7f 01 | [type 3] RecGroup { types: [SubType { is_final: true, supertype_idx: None, composite_type: Func(FuncType { params: [I32], results: [I32] }) }], explicit_rec_group: false } | 7f - 0x1b | 60 01 7f 02 | [type 4] Single(SubType { is_final: true, supertype_idx: None, structural_type: Func(FuncType { params: [I32], returns: [I32, I32] }) }) + 0x1b | 60 01 7f 02 | [type 4] RecGroup { types: [SubType { is_final: true, supertype_idx: None, composite_type: Func(FuncType { params: [I32], results: [I32, I32] }) }], explicit_rec_group: false } | 7f 7f 0x21 | 03 02 | func section 0x23 | 01 | 1 count diff --git a/tests/cli/dump/bundled.wat.stdout b/tests/cli/dump/bundled.wat.stdout index 51d4310870..20972fb5f6 100644 --- a/tests/cli/dump/bundled.wat.stdout +++ b/tests/cli/dump/bundled.wat.stdout @@ -25,7 +25,7 @@ | 01 00 00 00 0x54 | 01 09 | type section 0x56 | 01 | 1 count - 0x57 | 60 04 7f 7f | [type 0] Single(SubType { is_final: true, supertype_idx: None, structural_type: Func(FuncType { params: [I32, I32, I32, I32], returns: [I32] }) }) + 0x57 | 60 04 7f 7f | [type 0] RecGroup { types: [SubType { is_final: true, supertype_idx: None, composite_type: Func(FuncType { params: [I32, I32, I32, I32], results: [I32] }) }], explicit_rec_group: false } | 7f 7f 01 7f 0x5f | 03 02 | func section 0x61 | 01 | 1 count @@ -61,9 +61,9 @@ | 01 00 00 00 0xa0 | 01 09 | type section 0xa2 | 02 | 2 count - 0xa3 | 60 02 7f 7f | [type 0] Single(SubType { is_final: true, supertype_idx: None, structural_type: Func(FuncType { params: [I32, I32], returns: [] }) }) + 0xa3 | 60 02 7f 7f | [type 0] RecGroup { types: [SubType { is_final: true, supertype_idx: None, composite_type: Func(FuncType { params: [I32, I32], results: [] }) }], explicit_rec_group: false } | 00 - 0xa8 | 60 00 00 | [type 1] Single(SubType { is_final: true, supertype_idx: None, structural_type: Func(FuncType { params: [], returns: [] }) }) + 0xa8 | 60 00 00 | [type 1] RecGroup { types: [SubType { is_final: true, supertype_idx: None, composite_type: Func(FuncType { params: [], results: [] }) }], explicit_rec_group: false } 0xab | 02 12 | import section 0xad | 01 | 1 count 0xae | 09 77 61 73 | import [func 0] Import { module: "wasi-file", name: "read", ty: Func(0) } @@ -103,9 +103,9 @@ | 01 00 00 00 0x101 | 01 0c | type section 0x103 | 02 | 2 count - 0x104 | 60 02 7f 7f | [type 0] Single(SubType { is_final: true, supertype_idx: None, structural_type: Func(FuncType { params: [I32, I32], returns: [] }) }) + 0x104 | 60 02 7f 7f | [type 0] RecGroup { types: [SubType { is_final: true, supertype_idx: None, composite_type: Func(FuncType { params: [I32, I32], results: [] }) }], explicit_rec_group: false } | 00 - 0x109 | 60 03 7f 7f | [type 1] Single(SubType { is_final: true, supertype_idx: None, structural_type: Func(FuncType { params: [I32, I32, I32], returns: [] }) }) + 0x109 | 60 03 7f 7f | [type 1] RecGroup { types: [SubType { is_final: true, supertype_idx: None, composite_type: Func(FuncType { params: [I32, I32, I32], results: [] }) }], explicit_rec_group: false } | 7f 00 0x10f | 02 12 | import section 0x111 | 01 | 1 count diff --git a/tests/cli/dump/component-expand-bundle.wat.stdout b/tests/cli/dump/component-expand-bundle.wat.stdout index a1a2b3f02f..4a23eb0855 100644 --- a/tests/cli/dump/component-expand-bundle.wat.stdout +++ b/tests/cli/dump/component-expand-bundle.wat.stdout @@ -5,7 +5,7 @@ | 01 00 00 00 0x12 | 01 04 | type section 0x14 | 01 | 1 count - 0x15 | 60 00 00 | [type 0] Single(SubType { is_final: true, supertype_idx: None, structural_type: Func(FuncType { params: [], returns: [] }) }) + 0x15 | 60 00 00 | [type 0] RecGroup { types: [SubType { is_final: true, supertype_idx: None, composite_type: Func(FuncType { params: [], results: [] }) }], explicit_rec_group: false } 0x18 | 03 02 | func section 0x1a | 01 | 1 count 0x1b | 00 | [func 0] type 0 @@ -28,7 +28,7 @@ | 01 00 00 00 0x3d | 01 04 | type section 0x3f | 01 | 1 count - 0x40 | 60 00 00 | [type 0] Single(SubType { is_final: true, supertype_idx: None, structural_type: Func(FuncType { params: [], returns: [] }) }) + 0x40 | 60 00 00 | [type 0] RecGroup { types: [SubType { is_final: true, supertype_idx: None, composite_type: Func(FuncType { params: [], results: [] }) }], explicit_rec_group: false } 0x43 | 02 06 | import section 0x45 | 01 | 1 count 0x46 | 00 01 61 00 | import [func 0] Import { module: "", name: "a", ty: Func(0) } diff --git a/tests/cli/dump/import-modules.wat.stdout b/tests/cli/dump/import-modules.wat.stdout index 7609dbc7db..ff7921d8a2 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, structural_type: Func(FuncType { params: [], returns: [] }) }), Import(Import { module: "", name: "f", ty: Func(0) })]) + 0xb | 50 02 01 60 | [core type 0] Module([Type(SubType { is_final: true, supertype_idx: None, composite_type: Func(FuncType { params: [], results: [] }) }), Import(Import { module: "", name: "f", ty: Func(0) })]) | 00 00 00 00 | 01 66 00 00 0x17 | 0a 07 | component import section @@ -14,7 +14,7 @@ | 01 00 00 00 0x2a | 01 04 | type section 0x2c | 01 | 1 count - 0x2d | 60 00 00 | [type 0] Single(SubType { is_final: true, supertype_idx: None, structural_type: Func(FuncType { params: [], returns: [] }) }) + 0x2d | 60 00 00 | [type 0] RecGroup { types: [SubType { is_final: true, supertype_idx: None, composite_type: Func(FuncType { params: [], results: [] }) }], explicit_rec_group: false } 0x30 | 03 02 | func section 0x32 | 01 | 1 count 0x33 | 00 | [func 0] type 0 diff --git a/tests/cli/dump/module-types.wat.stdout b/tests/cli/dump/module-types.wat.stdout index e4fbcbe681..2430291f05 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, structural_type: Func(FuncType { params: [], returns: [] }) }), Import(Import { module: "", name: "f", ty: Func(0) }), Import(Import { module: "", name: "g", ty: Global(GlobalType { content_type: I32, mutable: false }) }), Import(Import { module: "", name: "t", ty: Table(TableType { element_type: funcref, initial: 1, maximum: None }) }), Import(Import { module: "", name: "m", ty: Memory(MemoryType { memory64: false, shared: false, initial: 1, maximum: None }) })]) + 0xb | 50 05 01 60 | [core type 0] Module([Type(SubType { is_final: true, supertype_idx: None, composite_type: Func(FuncType { params: [], results: [] }) }), Import(Import { module: "", name: "f", ty: Func(0) }), Import(Import { module: "", name: "g", ty: Global(GlobalType { content_type: I32, mutable: false }) }), Import(Import { module: "", name: "t", ty: Table(TableType { element_type: funcref, initial: 1, maximum: None }) }), Import(Import { module: "", name: "m", ty: Memory(MemoryType { memory64: false, shared: false, initial: 1, maximum: None }) })]) | 00 00 00 00 | 01 66 00 00 | 00 00 01 67 diff --git a/tests/cli/dump/names.wat.stdout b/tests/cli/dump/names.wat.stdout index 35981448f4..c4fc603b1b 100644 --- a/tests/cli/dump/names.wat.stdout +++ b/tests/cli/dump/names.wat.stdout @@ -2,7 +2,7 @@ | 01 00 00 00 0x8 | 01 05 | type section 0xa | 01 | 1 count - 0xb | 60 01 7f 00 | [type 0] Single(SubType { is_final: true, supertype_idx: None, structural_type: Func(FuncType { params: [I32], returns: [] }) }) + 0xb | 60 01 7f 00 | [type 0] RecGroup { types: [SubType { is_final: true, supertype_idx: None, composite_type: Func(FuncType { params: [I32], results: [] }) }], explicit_rec_group: false } 0xf | 03 02 | func section 0x11 | 01 | 1 count 0x12 | 00 | [func 0] type 0 diff --git a/tests/cli/dump/select.wat.stdout b/tests/cli/dump/select.wat.stdout index 07d338db27..b622625541 100644 --- a/tests/cli/dump/select.wat.stdout +++ b/tests/cli/dump/select.wat.stdout @@ -2,7 +2,7 @@ | 01 00 00 00 0x8 | 01 04 | type section 0xa | 01 | 1 count - 0xb | 60 00 00 | [type 0] Single(SubType { is_final: true, supertype_idx: None, structural_type: Func(FuncType { params: [], returns: [] }) }) + 0xb | 60 00 00 | [type 0] RecGroup { types: [SubType { is_final: true, supertype_idx: None, composite_type: Func(FuncType { params: [], results: [] }) }], explicit_rec_group: false } 0xe | 03 02 | func section 0x10 | 01 | 1 count 0x11 | 00 | [func 0] type 0 diff --git a/tests/cli/dump/simple.wat.stdout b/tests/cli/dump/simple.wat.stdout index 12c36351a4..0620e3d982 100644 --- a/tests/cli/dump/simple.wat.stdout +++ b/tests/cli/dump/simple.wat.stdout @@ -2,8 +2,8 @@ | 01 00 00 00 0x8 | 01 08 | type section 0xa | 02 | 2 count - 0xb | 60 01 7f 00 | [type 0] Single(SubType { is_final: true, supertype_idx: None, structural_type: Func(FuncType { params: [I32], returns: [] }) }) - 0xf | 60 00 00 | [type 1] Single(SubType { is_final: true, supertype_idx: None, structural_type: Func(FuncType { params: [], returns: [] }) }) + 0xb | 60 01 7f 00 | [type 0] RecGroup { types: [SubType { is_final: true, supertype_idx: None, composite_type: Func(FuncType { params: [I32], results: [] }) }], explicit_rec_group: false } + 0xf | 60 00 00 | [type 1] RecGroup { types: [SubType { is_final: true, supertype_idx: None, composite_type: Func(FuncType { params: [], results: [] }) }], explicit_rec_group: false } 0x12 | 02 07 | import section 0x14 | 01 | 1 count 0x15 | 01 6d 01 6e | import [func 0] Import { module: "m", name: "n", ty: Func(0) } diff --git a/tests/cli/dump/try-delegate.wat.stdout b/tests/cli/dump/try-delegate.wat.stdout index bb0255f19d..c1f23ddec6 100644 --- a/tests/cli/dump/try-delegate.wat.stdout +++ b/tests/cli/dump/try-delegate.wat.stdout @@ -2,7 +2,7 @@ | 01 00 00 00 0x8 | 01 04 | type section 0xa | 01 | 1 count - 0xb | 60 00 00 | [type 0] Single(SubType { is_final: true, supertype_idx: None, structural_type: Func(FuncType { params: [], returns: [] }) }) + 0xb | 60 00 00 | [type 0] RecGroup { types: [SubType { is_final: true, supertype_idx: None, composite_type: Func(FuncType { params: [], results: [] }) }], explicit_rec_group: false } 0xe | 03 02 | func section 0x10 | 01 | 1 count 0x11 | 00 | [func 0] type 0