Skip to content

Commit

Permalink
GC: wasmparser: Add user defined struct types
Browse files Browse the repository at this point in the history
  • Loading branch information
imikushin committed May 24, 2023
1 parent e7e7976 commit afd8f5d
Show file tree
Hide file tree
Showing 17 changed files with 210 additions and 26 deletions.
2 changes: 1 addition & 1 deletion crates/wasm-compose/src/encoding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -468,7 +468,7 @@ impl<'a> TypeEncoder<'a> {
let ty = self.0.type_from_id(id).unwrap();

match ty {
Type::Func(_) | Type::Array(_) | Type::Instance(_) => {
Type::Func(_) | Type::Array(_) | Type::Struct(_) | Type::Instance(_) => {
unreachable!()
}
Type::Module(_) => self.module_type(encodable, types, id),
Expand Down
26 changes: 25 additions & 1 deletion crates/wasm-encoder/src/core/types.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@
use crate::{encode_section, Encode, Section, SectionId};

/// Array or struct field type.
/// Field type in structural types (structs, arrays).
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Ord, PartialOrd)]
pub struct FieldType {
/// Storage type of the field.
pub ty: StorageType,
/// Is the field mutable.
pub mutable: bool,
}

/// Storage type for structural type fields.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Ord, PartialOrd)]
pub enum StorageType {
/// The `i8` type.
Expand Down Expand Up @@ -225,12 +234,27 @@ impl TypeSection {

/// Define an array type in this type section.
pub fn array(&mut self, ty: StorageType, mutable: bool) -> &mut Self {
self.field(ty, mutable)
}

fn field(&mut self, ty: StorageType, mutable: bool) -> &mut Self {
self.bytes.push(0x5e);
ty.encode(&mut self.bytes);
self.bytes.push(mutable as u8);
self.num_added += 1;
self
}

/// Define a struct type in this type section.
pub fn struct_(&mut self, fields: Vec<FieldType>) -> &mut Self {
self.bytes.push(0x5f);
fields.len().encode(&mut self.bytes);
for f in fields.iter() {
self.field(f.ty, f.mutable);
}
self.num_added += 1;
self
}
}

impl Encode for TypeSection {
Expand Down
5 changes: 4 additions & 1 deletion crates/wasm-mutate/src/module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,10 @@ impl TryFrom<wasmparser::Type> for TypeInfo {
.collect(),
})),
wasmparser::Type::Array(_) => {
unimplemented!("Array and struct types are not supported yet.")
unimplemented!("Array types are not supported yet.")
}
wasmparser::Type::Struct(_) => {
unimplemented!("Struct types are not supported yet.")
}
}
}
Expand Down
3 changes: 3 additions & 0 deletions crates/wasm-mutate/src/mutators/add_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,9 @@ impl Mutator for AddTypeMutator {
wasmparser::Type::Array(_) => {
return Err(Error::unsupported("Array types are not supported yet."));
}
wasmparser::Type::Struct(_) => {
return Err(Error::unsupported("Struct types are not supported yet."));
}
}
}
// And then add our new type.
Expand Down
3 changes: 2 additions & 1 deletion crates/wasm-mutate/src/mutators/translate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,8 @@ pub fn type_def(t: &mut dyn Translator, ty: Type, s: &mut TypeSection) -> Result
);
Ok(())
}
Type::Array(_) => unimplemented!("Array and struct types are not supported yet."),
Type::Array(_) => unimplemented!("Array types are not supported yet."),
Type::Struct(_) => unimplemented!("Struct types are not supported yet."),
}
}

Expand Down
5 changes: 4 additions & 1 deletion crates/wasm-smith/src/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -610,7 +610,10 @@ impl Module {
new_index
}
Some((wasmparser::Type::Array(_array_type), _index_store)) => {
unimplemented!("Array and struct types are not supported yet.");
unimplemented!("Array types are not supported yet.");
}
Some((wasmparser::Type::Struct(_struct_type), _index_store)) => {
unimplemented!("Struct types are not supported yet.");
}
};
match &new_types[serialized_sig_idx - first_type_index] {
Expand Down
5 changes: 4 additions & 1 deletion crates/wasm-smith/tests/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,10 @@ fn smoke_test_imports_config() {
match ty.unwrap() {
wasmparser::Type::Func(ft) => sig_types.push(ft),
wasmparser::Type::Array(_) => {
unimplemented!("Array and struct types are not supported yet.")
unimplemented!("Array types are not supported yet.")
}
wasmparser::Type::Struct(_) => {
unimplemented!("Struct types are not supported yet.")
}
}
}
Expand Down
1 change: 1 addition & 0 deletions crates/wasmparser/src/limits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ pub const MAX_WASM_TABLES: usize = 100;
pub const MAX_WASM_MEMORIES: usize = 100;
pub const MAX_WASM_TAGS: usize = 1_000_000;
pub const MAX_WASM_BR_TABLE_SIZE: usize = MAX_WASM_FUNCTION_SIZE;
pub const MAX_WASM_STRUCT_FIELDS: usize = 100_000;

// Component-related limits
pub const MAX_WASM_MODULE_SIZE: usize = 1024 * 1024 * 1024; //= 1 GiB
Expand Down
38 changes: 33 additions & 5 deletions crates/wasmparser/src/readers/core/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
* limitations under the License.
*/

use crate::limits::{MAX_WASM_FUNCTION_PARAMS, MAX_WASM_FUNCTION_RETURNS};
use crate::limits::{MAX_WASM_FUNCTION_PARAMS, MAX_WASM_FUNCTION_RETURNS, MAX_WASM_STRUCT_FIELDS};
use crate::{BinaryReader, FromReader, Result, SectionLimited};
use std::fmt::{self, Debug, Write};

Expand Down Expand Up @@ -658,7 +658,8 @@ pub enum Type {
Func(FuncType),
/// The type is for an array.
Array(ArrayType),
// Struct(StructType),
/// The type is for a struct.
Struct(StructType),
}

/// Represents a type of a function in a WebAssembly module.
Expand All @@ -672,13 +673,24 @@ pub struct FuncType {

/// Represents a type of an array in a WebAssembly module.
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub struct ArrayType {
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 Debug for FuncType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("FuncType")
Expand Down Expand Up @@ -835,6 +847,7 @@ impl<'a> FromReader<'a> for Type {
Ok(match reader.read_u8()? {
0x60 => Type::Func(reader.read()?),
0x5e => Type::Array(reader.read()?),
0x5f => Type::Struct(reader.read()?),
x => return reader.invalid_leading_byte(x, "type"),
})
}
Expand All @@ -855,11 +868,11 @@ impl<'a> FromReader<'a> for FuncType {
}
}

impl<'a> FromReader<'a> for ArrayType {
impl<'a> FromReader<'a> for FieldType {
fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> {
let element_type = reader.read()?;
let mutable = reader.read_u8()?;
Ok(ArrayType {
Ok(FieldType {
element_type,
mutable: match mutable {
0 => false,
Expand All @@ -872,3 +885,18 @@ impl<'a> FromReader<'a> for ArrayType {
})
}
}

impl<'a> FromReader<'a> for ArrayType {
fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> {
Ok(ArrayType(FieldType::from_reader(reader)?))
}
}

impl<'a> FromReader<'a> for StructType {
fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> {
let fields = reader.read_iter(MAX_WASM_STRUCT_FIELDS, "struct fields")?;
Ok(StructType {
fields: fields.collect::<Result<_>>()?,
})
}
}
6 changes: 5 additions & 1 deletion crates/wasmparser/src/validator/component.rs
Original file line number Diff line number Diff line change
Expand Up @@ -744,7 +744,11 @@ impl ComponentState {

// Core wasm constructs are always valid with respect to
// exported types, since they have none.
Type::Module(_) | Type::Instance(_) | Type::Func(_) | Type::Array(_) => true,
Type::Module(_)
| Type::Instance(_)
| Type::Func(_)
| Type::Array(_)
| Type::Struct(_) => true,

// Resource types, in isolation, are always valid to import
// or export since they're either attached to an import or
Expand Down
43 changes: 35 additions & 8 deletions crates/wasmparser/src/validator/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ use super::{
use crate::limits::*;
use crate::validator::core::arc::MaybeOwned;
use crate::{
BinaryReaderError, ConstExpr, Data, DataKind, Element, ElementKind, ExternalKind, FuncType,
Global, GlobalType, HeapType, MemoryType, RefType, Result, StorageType, Table, TableInit,
TableType, TagType, TypeRef, ValType, VisitOperator, WasmFeatures, WasmFuncType,
ArrayType, BinaryReaderError, ConstExpr, Data, DataKind, Element, ElementKind, ExternalKind,
FuncType, Global, GlobalType, HeapType, MemoryType, RefType, Result, StorageType, Table,
TableInit, TableType, TagType, TypeRef, ValType, VisitOperator, WasmFeatures, WasmFuncType,
WasmModuleResources,
};
use indexmap::IndexMap;
Expand Down Expand Up @@ -520,14 +520,26 @@ impl Module {
offset,
));
}
match t.element_type {
crate::StorageType::I8 | crate::StorageType::I16 => {}
crate::StorageType::Val(value_type) => {
match t.0.element_type {
StorageType::I8 | StorageType::I16 => {}
StorageType::Val(value_type) => {
self.check_value_type(value_type, features, offset)?;
}
};
Type::Array(t)
}
crate::Type::Struct(t) => {
if !features.gc {
return Err(BinaryReaderError::new(
"struct indexed types not supported without the gc feature",
offset,
));
}
for ty in t.fields.iter() {
self.check_storage_type(ty.element_type, features, offset)?;
}
Type::Struct(t)
}
};

if check_limit {
Expand Down Expand Up @@ -798,6 +810,21 @@ impl Module {
.collect::<Result<_>>()
}

fn check_storage_type(
&self,
ty: StorageType,
features: &WasmFeatures,
offset: usize,
) -> Result<()> {
match ty {
StorageType::I8 | StorageType::I16 => {}
StorageType::Val(value_type) => {
self.check_value_type(value_type, features, offset)?;
}
}
Ok(())
}

fn check_value_type(&self, ty: ValType, features: &WasmFeatures, offset: usize) -> Result<()> {
match features.check_value_type(ty) {
Ok(()) => Ok(()),
Expand Down Expand Up @@ -855,7 +882,7 @@ impl Module {
let n2 = self.type_at(types, n2.into(), 0).unwrap();
match (n1, n2) {
(Type::Func(f1), Type::Func(f2)) => self.eq_fns(f1, f2, types),
(Type::Array(a1), Type::Array(a2)) => {
(Type::Array(ArrayType(a1)), Type::Array(ArrayType(a2))) => {
a1.mutable == a2.mutable
&& match (a1.element_type, a2.element_type) {
(StorageType::Val(vt1), StorageType::Val(vt2)) => {
Expand Down Expand Up @@ -897,7 +924,7 @@ impl Module {
let n2 = self.type_at(types, n2.into(), 0);
match (n1, n2) {
(Ok(Type::Func(n1)), Ok(Type::Func(n2))) => self.eq_fns(n1, n2, types),
(Ok(Type::Array(n1)), Ok(Type::Array(n2))) => {
(Ok(Type::Array(ArrayType(n1))), Ok(Type::Array(ArrayType(n2)))) => {
(n1.mutable == n2.mutable || n2.mutable)
&& match (n1.element_type, n2.element_type) {
(StorageType::Val(vt1), StorageType::Val(vt2)) => {
Expand Down
26 changes: 23 additions & 3 deletions crates/wasmparser/src/validator/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use super::{
use crate::validator::names::{KebabName, KebabString};
use crate::{
ArrayType, BinaryReaderError, Export, ExternalKind, FuncType, GlobalType, Import, MemoryType,
PrimitiveValType, RefType, Result, TableType, TypeRef, ValType,
PrimitiveValType, RefType, Result, StructType, TableType, TypeRef, ValType,
};
use indexmap::{IndexMap, IndexSet};
use std::collections::HashMap;
Expand Down Expand Up @@ -173,6 +173,8 @@ pub enum Type {
Func(FuncType),
/// The definition is for a core array type.
Array(ArrayType),
/// The definition is for a core struct type.
Struct(StructType),
/// The definition is for a core module type.
///
/// This variant is only supported when parsing a component.
Expand Down Expand Up @@ -220,6 +222,14 @@ impl Type {
}
}

/// Converts the type to a struct type.
pub fn as_struct_type(&self) -> Option<&StructType> {
match self {
Self::Struct(ty) => Some(ty),
_ => None,
}
}

/// Converts the type to a core module type.
pub fn as_module_type(&self) -> Option<&ModuleType> {
match self {
Expand Down Expand Up @@ -277,9 +287,11 @@ impl Type {
}

pub(crate) fn type_size(&self) -> u32 {
// TODO calculate actual size for func, array, struct (#1036)
match self {
Self::Func(ty) => 1 + (ty.params().len() + ty.results().len()) as u32,
Self::Array(_) => 2, // 2 is a guess.
Self::Struct(ty) => 1 + 2 * ty.fields.len() as u32,
Self::Module(ty) => ty.type_size,
Self::Instance(ty) => ty.type_size,
Self::Component(ty) => ty.type_size,
Expand Down Expand Up @@ -1789,7 +1801,11 @@ impl TypeAlloc {
pub fn free_variables_type_id(&self, id: TypeId, set: &mut IndexSet<ResourceId>) {
match &self[id] {
// Core wasm constructs cannot reference resources.
Type::Func(_) | Type::Array(_) | Type::Module(_) | Type::Instance(_) => {}
Type::Func(_)
| Type::Array(_)
| Type::Struct(_)
| Type::Module(_)
| Type::Instance(_) => {}

// Recurse on the imports/exports of components, but remove the
// imported and defined resources within the component itself.
Expand Down Expand Up @@ -2010,7 +2026,11 @@ pub(crate) trait Remap: Index<TypeId, Output = Type> {
let ty = match &self[*id] {
// Core wasm functions/modules/instances don't have resource types
// in them.
Type::Func(_) | Type::Array(_) | Type::Module(_) | Type::Instance(_) => return false,
Type::Func(_)
| Type::Array(_)
| Type::Struct(_)
| Type::Module(_)
| Type::Instance(_) => return false,

Type::Component(i) => {
let mut tmp = i.clone();
Expand Down
Loading

0 comments on commit afd8f5d

Please sign in to comment.