Skip to content

Commit

Permalink
Flatten MemoryType into a struct instead of an enum
Browse files Browse the repository at this point in the history
Should make the fields easier to access and consumers can know that
valid memories will have their limits fit within a `u32` despite the
`u64` datatype being used.
  • Loading branch information
alexcrichton committed Aug 2, 2021
1 parent 2cea28d commit 52a8049
Show file tree
Hide file tree
Showing 11 changed files with 149 additions and 163 deletions.
61 changes: 28 additions & 33 deletions crates/wasmparser/src/binary_reader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,8 @@ use crate::limits::*;

use crate::primitives::{
BinaryReaderError, BrTable, CustomSectionKind, ExternalKind, FuncType, GlobalType, Ieee32,
Ieee64, LinkingType, MemoryImmediate, MemoryType, NameType, Operator, RelocType,
ResizableLimits, ResizableLimits64, Result, SIMDLaneIndex, SectionCode, TableType, Type,
TypeOrFuncType, V128,
Ieee64, LinkingType, MemoryImmediate, MemoryType, NameType, Operator, RelocType, Result,
SIMDLaneIndex, SectionCode, TableType, Type, TypeOrFuncType, V128,
};
use crate::{ExportType, Import, ImportSectionEntryType, InstanceType, ModuleType, TagType};

Expand Down Expand Up @@ -322,26 +321,6 @@ impl<'a> BinaryReader<'a> {
})
}

fn read_resizable_limits(&mut self, max_present: bool) -> Result<ResizableLimits> {
let initial = self.read_var_u32()?;
let maximum = if max_present {
Some(self.read_var_u32()?)
} else {
None
};
Ok(ResizableLimits { initial, maximum })
}

fn read_resizable_limits64(&mut self, max_present: bool) -> Result<ResizableLimits64> {
let initial = self.read_var_u64()?;
let maximum = if max_present {
Some(self.read_var_u64()?)
} else {
None
};
Ok(ResizableLimits64 { initial, maximum })
}

pub(crate) fn read_table_type(&mut self) -> Result<TableType> {
let element_type = self.read_type()?;
let flags = self.read_var_u32()?;
Expand All @@ -351,10 +330,17 @@ impl<'a> BinaryReader<'a> {
self.original_position() - 1,
));
}
let limits = self.read_resizable_limits((flags & 0x1) != 0)?;
let has_max = (flags & 0b1) != 0;
let initial = self.read_var_u32()?;
let maximum = if has_max {
Some(self.read_var_u32()?)
} else {
None
};
Ok(TableType {
element_type,
limits,
initial,
maximum,
})
}

Expand All @@ -365,23 +351,32 @@ impl<'a> BinaryReader<'a> {
return Err(BinaryReaderError::new("invalid memory limits flags", pos));
}

let b64 = flags & 0b100 != 0;
let memory64 = flags & 0b100 != 0;
let shared = flags & 0b010 != 0;
let has_max = flags & 0b001 != 0;
if b64 {
Ok(MemoryType {
memory64,
shared,
// FIXME(WebAssembly/memory64#21) as currently specified if the
// `shared` flag is set we should be reading a 32-bit limits field
// here. That seems a bit odd to me at the time of this writing so
// I've taken the liberty of reading a 64-bit limits field in those
// situations. I suspect that this is a typo in the spec, but if not
// we'll need to update this to read a 32-bit limits field when the
// shared flag is set.
let limits = self.read_resizable_limits64(has_max)?;
Ok(MemoryType::M64 { limits, shared })
} else {
let limits = self.read_resizable_limits(has_max)?;
Ok(MemoryType::M32 { limits, shared })
}
initial: if memory64 {
self.read_var_u64()?
} else {
self.read_var_u32()?.into()
},
maximum: if !has_max {
None
} else if memory64 {
Some(self.read_var_u64()?)
} else {
Some(self.read_var_u32()?.into())
},
})
}

pub(crate) fn read_tag_type(&mut self) -> Result<TagType> {
Expand Down
2 changes: 1 addition & 1 deletion crates/wasmparser/src/limits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ pub const MAX_WASM_IMPORTS: usize = 100_000;
pub const MAX_WASM_EXPORTS: usize = 100_000;
pub const MAX_WASM_GLOBALS: usize = 1_000_000;
pub const MAX_WASM_DATA_SEGMENTS: usize = 100_000;
pub const MAX_WASM_MEMORY_PAGES: usize = 65536;
pub const MAX_WASM_MEMORY32_PAGES: u64 = 65536;
pub const MAX_WASM_MEMORY64_PAGES: u64 = 1 << 48;
pub const MAX_WASM_STRING_SIZE: usize = 100_000;
pub const _MAX_WASM_MODULE_SIZE: usize = 1024 * 1024 * 1024; //= 1 GiB
Expand Down
55 changes: 32 additions & 23 deletions crates/wasmparser/src/primitives.rs
Original file line number Diff line number Diff line change
Expand Up @@ -193,33 +193,41 @@ pub struct ExportType<'a> {
}

#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct ResizableLimits {
pub struct TableType {
pub element_type: Type,
/// Initial size of this table, in elements.
pub initial: u32,
/// Optional maximum size of the table, in elements.
pub maximum: Option<u32>,
}

#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct ResizableLimits64 {
pub initial: u64,
pub maximum: Option<u64>,
}
pub struct MemoryType {
/// Whether or not this is a 64-bit memory, using i64 as an index. If this
/// is false it's a 32-bit memory using i32 as an index.
///
/// This is part of the memory64 proposal in WebAssembly.
pub memory64: bool,

#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct TableType {
pub element_type: Type,
pub limits: ResizableLimits,
}
/// Whether or not this is a "shared" memory, indicating that it should be
/// send-able across threads and the `maximum` field is always present for
/// valid types.
///
/// This is part of the threads proposal in WebAssembly.
pub shared: bool,

#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum MemoryType {
M32 {
limits: ResizableLimits,
shared: bool,
},
M64 {
limits: ResizableLimits64,
shared: bool,
},
/// Initial size of this memory, in wasm pages.
///
/// For 32-bit memories (when `memory64` is `false`) this is guaranteed to
/// be at most `u32::MAX` for valid types.
pub initial: u64,

/// Optional maximum size of this memory, in wasm pages.
///
/// For 32-bit memories (when `memory64` is `false`) this is guaranteed to
/// be at most `u32::MAX` for valid types. This field is always present for
/// valid wasm memories when `shared` is `true`.
pub maximum: Option<u64>,
}

#[derive(Debug, Copy, Clone, PartialEq, Eq)]
Expand All @@ -229,9 +237,10 @@ pub struct TagType {

impl MemoryType {
pub fn index_type(&self) -> Type {
match self {
MemoryType::M32 { .. } => Type::I32,
MemoryType::M64 { .. } => Type::I64,
if self.memory64 {
Type::I64
} else {
Type::I32
}
}
}
Expand Down
116 changes: 42 additions & 74 deletions crates/wasmparser/src/validator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,11 @@
*/

use crate::limits::*;
use crate::ResizableLimits64;
use crate::WasmModuleResources;
use crate::{Alias, ExternalKind, Import, ImportSectionEntryType};
use crate::{BinaryReaderError, GlobalType, MemoryType, Range, Result, TableType, TagType, Type};
use crate::{DataKind, ElementItem, ElementKind, InitExpr, Instance, Operator};
use crate::{FuncType, ResizableLimits, SectionReader, SectionWithLimitedItems};
use crate::{FuncType, SectionReader, SectionWithLimitedItems};
use crate::{FunctionBody, Parser, Payload};
use std::collections::{HashMap, HashSet};
use std::mem;
Expand Down Expand Up @@ -682,57 +681,43 @@ impl Validator {
}
_ => return self.create_error("element is not reference type"),
}
self.limits(&ty.limits)?;
if ty.limits.initial > MAX_WASM_TABLE_ENTRIES as u32 {
self.limits(ty.initial, ty.maximum)?;
if ty.initial > MAX_WASM_TABLE_ENTRIES as u32 {
return self.create_error("minimum table size is out of bounds");
}
Ok(())
}

fn memory_type(&self, ty: &MemoryType) -> Result<()> {
match ty {
MemoryType::M32 { limits, shared } => {
self.limits(limits)?;
let initial = limits.initial;
if initial as usize > MAX_WASM_MEMORY_PAGES {
return self.create_error("memory size must be at most 65536 pages (4GiB)");
}
if let Some(maximum) = limits.maximum {
if maximum as usize > MAX_WASM_MEMORY_PAGES {
return self.create_error("memory size must be at most 65536 pages (4GiB)");
}
}
if *shared {
if !self.features.threads {
return self.create_error("threads must be enabled for shared memories");
}
if limits.maximum.is_none() {
return self.create_error("shared memory must have maximum size");
}
}
self.limits(ty.initial, ty.maximum)?;
let (true_maximum, err) = if ty.memory64 {
if !self.features.memory64 {
return self.create_error("memory64 must be enabled for 64-bit memories");
}
(
MAX_WASM_MEMORY64_PAGES,
"memory size must be at most 2**48 pages",
)
} else {
(
MAX_WASM_MEMORY32_PAGES,
"memory size must be at most 65536 pages (4GiB)",
)
};
if ty.initial > true_maximum {
return self.create_error(err);
}
if let Some(maximum) = ty.maximum {
if maximum > true_maximum {
return self.create_error(err);
}
MemoryType::M64 { limits, shared } => {
if !self.features.memory64 {
return self.create_error("memory64 must be enabled for 64-bit memories");
}
self.limits64(&limits)?;
let initial = limits.initial;
if initial > MAX_WASM_MEMORY64_PAGES {
return self.create_error("memory initial size too large");
}
if let Some(maximum) = limits.maximum {
if maximum > MAX_WASM_MEMORY64_PAGES {
return self.create_error("memory initial size too large");
}
}
if *shared {
if !self.features.threads {
return self.create_error("threads must be enabled for shared memories");
}
if limits.maximum.is_none() {
return self.create_error("shared memory must have maximum size");
}
}
}
if ty.shared {
if !self.features.threads {
return self.create_error("threads must be enabled for shared memories");
}
if ty.maximum.is_none() {
return self.create_error("shared memory must have maximum size");
}
}
Ok(())
Expand All @@ -750,18 +735,12 @@ impl Validator {
self.value_type(ty.content_type)
}

fn limits(&self, limits: &ResizableLimits) -> Result<()> {
if let Some(max) = limits.maximum {
if limits.initial > max {
return self.create_error("size minimum must not be greater than maximum");
}
}
Ok(())
}

fn limits64(&self, limits: &ResizableLimits64) -> Result<()> {
if let Some(max) = limits.maximum {
if limits.initial > max {
fn limits<T>(&self, initial: T, maximum: Option<T>) -> Result<()>
where
T: Into<u64>,
{
if let Some(max) = maximum {
if initial.into() > max.into() {
return self.create_error("size minimum must not be greater than maximum");
}
}
Expand Down Expand Up @@ -1061,7 +1040,7 @@ impl Validator {
EntityType::Table(b) => b,
_ => return self.create_error("item type mismatch"),
};
if a.element_type == b.element_type && limits_match!(&a.limits, &b.limits) {
if a.element_type == b.element_type && limits_match!(a, b) {
Ok(())
} else {
self.create_error("table type mismatch")
Expand Down Expand Up @@ -1089,23 +1068,12 @@ impl Validator {
self.create_error("tag type mismatch")
}
}
EntityType::Memory(MemoryType::M32 { limits, shared }) => {
let (b_limits, b_shared) = match b {
EntityType::Memory(MemoryType::M32 { limits, shared }) => (limits, shared),
_ => return self.create_error("item type mismatch"),
};
if limits_match!(limits, b_limits) && shared == b_shared {
Ok(())
} else {
self.create_error("memory type mismatch")
}
}
EntityType::Memory(MemoryType::M64 { limits, shared }) => {
let (b_limits, b_shared) = match b {
EntityType::Memory(MemoryType::M64 { limits, shared }) => (limits, shared),
EntityType::Memory(a) => {
let b = match b {
EntityType::Memory(b) => b,
_ => return self.create_error("item type mismatch"),
};
if limits_match!(limits, b_limits) && shared == b_shared {
if limits_match!(a, b) && a.shared == b.shared && a.memory64 == b.memory64 {
Ok(())
} else {
self.create_error("memory type mismatch")
Expand Down
Loading

0 comments on commit 52a8049

Please sign in to comment.