Skip to content

Commit

Permalink
Initial implementation of module linking (#26)
Browse files Browse the repository at this point in the history
* Initial implementation of module linking

This commit is the initial implementation of the module linking proposal
in the three tooling crates of this repository. Unfortunately this is
just one massive commit which isn't really able to be reviewed in a
nuanced way. I wanted to bundle everything up though because
implementing each of the features of the module linking proposal ended
up having a pretty major impact on at least the text parser.

The main focus of this commit is getting the text parser to a "pretty
complete" point. I suspect there's still various odds and ends
remaining, but I believe it's almost entirely up to date with the
current state of the proposal. The `wasmprinter` crate should be in good
shape but I'm pretty certian that `wasmparser` is still missing at least
some pieces of validation.

This is hoped to be a good unit of work to start from after which I can
focus more on a complete implementation in `wasmparser` in terms of
validation. From there I think we'll have a good suite of tests and can
start moving on towards an implementation in an engine and/or
implementation in toolchains.

The largest changes here are to the text parser, and it's important to
note that the text parser at least is ideally pretty low-impact in terms
of risky changes. We've got a pretty good regression test suite, and the
interface of the text parser is "given this string give me the binary
wasm". Coupled with the fact that the text parser is 100% safe code I'm
pretty certain that the only bad pieces which can arise are panics,
which fuzzers in theory should find relatively quickly.

* Tidy up `item_for` method with `Ns`

Also replace a "wut" string with an actual namespace description.

* FIx wasmpaser examples

* Check in more `*.dump` files

* Fix custom section annotations

* Fix a doc comment in wasmparser

* Address some review comments
  • Loading branch information
alexcrichton authored Jun 18, 2020
1 parent 1a1f366 commit c1aa81e
Show file tree
Hide file tree
Showing 88 changed files with 8,015 additions and 1,356 deletions.
2 changes: 1 addition & 1 deletion crates/wasmparser/examples/dump.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ fn main() {
ref ty,
} => {
println!(
"ImportSectionEntry {{ module: \"{}\", field: \"{}\", ty: {:?} }}",
"ImportSectionEntry {{ module: \"{}\", field: {:?}, ty: {:?} }}",
module, field, ty
);
}
Expand Down
8 changes: 5 additions & 3 deletions crates/wasmparser/examples/simple.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,11 @@ fn main() {
} => {
println!(" Export {} {:?}", field, kind);
}
ParserState::ImportSectionEntry { module, field, .. } => {
println!(" Import {}::{}", module, field)
}
ParserState::ImportSectionEntry {
module,
field: Some(field),
..
} => println!(" Import {}::{}", module, field),
ParserState::EndWasm => break,
ParserState::Error(ref err) => panic!("Error: {:?}", err),
_ => ( /* println!(" Other {:?}", state); */ ),
Expand Down
92 changes: 81 additions & 11 deletions crates/wasmparser/src/binary_reader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,14 @@ use std::convert::TryInto;
use std::str;
use std::vec::Vec;

use crate::limits::{
MAX_WASM_FUNCTION_LOCALS, MAX_WASM_FUNCTION_PARAMS, MAX_WASM_FUNCTION_RETURNS,
MAX_WASM_FUNCTION_SIZE, MAX_WASM_STRING_SIZE,
};
use crate::limits::*;

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

const MAX_WASM_BR_TABLE_SIZE: usize = MAX_WASM_FUNCTION_SIZE;

Expand All @@ -43,6 +41,7 @@ const WASM_MAGIC_NUMBER: &[u8; 4] = b"\0asm";
const WASM_EXPERIMENTAL_VERSION: u32 = 0xd;
const WASM_SUPPORTED_VERSION: u32 = 0x1;

#[derive(Clone)]
pub(crate) struct SectionHeader<'a> {
pub code: SectionCode<'a>,
pub payload_start: usize,
Expand Down Expand Up @@ -235,6 +234,9 @@ impl<'a> BinaryReader<'a> {
1 => Ok(ExternalKind::Table),
2 => Ok(ExternalKind::Memory),
3 => Ok(ExternalKind::Global),
5 => Ok(ExternalKind::Module),
6 => Ok(ExternalKind::Instance),
7 => Ok(ExternalKind::Type),
_ => Err(BinaryReaderError::new(
"Invalid external kind",
self.original_position() - 1,
Expand All @@ -243,13 +245,6 @@ impl<'a> BinaryReader<'a> {
}

pub(crate) fn read_func_type(&mut self) -> Result<FuncType> {
if self.read_type()? != Type::Func {
return Err(BinaryReaderError::new(
"type signature is not a func",
self.original_position() - 1,
));
}

let params_len = self.read_var_u32()? as usize;
if params_len > MAX_WASM_FUNCTION_PARAMS {
return Err(BinaryReaderError::new(
Expand Down Expand Up @@ -278,6 +273,77 @@ impl<'a> BinaryReader<'a> {
})
}

pub(crate) fn read_module_type(&mut self) -> Result<ModuleType<'a>> {
let pos = self.original_position();
let imports_len = self.read_var_u32()? as usize;
if imports_len > MAX_WASM_IMPORTS {
return Err(BinaryReaderError::new("imports size is out of bounds", pos));
}
Ok(ModuleType {
imports: (0..imports_len)
.map(|_| self.read_import())
.collect::<Result<_>>()?,
exports: self.read_export_types()?,
})
}

pub(crate) fn read_instance_type(&mut self) -> Result<InstanceType<'a>> {
Ok(InstanceType {
exports: self.read_export_types()?,
})
}

fn read_export_types(&mut self) -> Result<Box<[ExportType<'a>]>> {
let pos = self.original_position();
let exports_len = self.read_var_u32()? as usize;
if exports_len > MAX_WASM_EXPORTS {
return Err(BinaryReaderError::new("exports size is out of bound", pos));
}
(0..exports_len).map(|_| self.read_export_type()).collect()
}

pub(crate) fn read_import(&mut self) -> Result<Import<'a>> {
let module = self.read_string()?;

// For the `field`, figure out if we're the experimental encoding of
// single-level imports for the module linking proposal (a single-byte
// string which is 0xc0, which is invalid utf-8) or if we have a second
// level of import.
let mut clone = self.clone();
let field = if clone.read_var_u32()? == 1 && clone.read_u8()? == 0xc0 {
*self = clone;
None
} else {
Some(self.read_string()?)
};

let ty = self.read_import_desc()?;
Ok(Import { module, field, ty })
}

pub(crate) fn read_export_type(&mut self) -> Result<ExportType<'a>> {
let name = self.read_string()?;
let ty = self.read_import_desc()?;
Ok(ExportType { name, ty })
}

pub(crate) fn read_import_desc(&mut self) -> Result<ImportSectionEntryType> {
Ok(match self.read_external_kind()? {
ExternalKind::Function => ImportSectionEntryType::Function(self.read_var_u32()?),
ExternalKind::Table => ImportSectionEntryType::Table(self.read_table_type()?),
ExternalKind::Memory => ImportSectionEntryType::Memory(self.read_memory_type()?),
ExternalKind::Global => ImportSectionEntryType::Global(self.read_global_type()?),
ExternalKind::Module => ImportSectionEntryType::Module(self.read_var_u32()?),
ExternalKind::Instance => ImportSectionEntryType::Instance(self.read_var_u32()?),
ExternalKind::Type => {
return Err(BinaryReaderError::new(
"cannot import types",
self.original_position() - 1,
))
}
})
}

fn read_resizable_limits(&mut self, max_present: bool) -> Result<ResizableLimits> {
let initial = self.read_var_u32()?;
let maximum = if max_present {
Expand Down Expand Up @@ -362,6 +428,10 @@ impl<'a> BinaryReader<'a> {
10 => Ok(SectionCode::Code),
11 => Ok(SectionCode::Data),
12 => Ok(SectionCode::DataCount),
100 => Ok(SectionCode::Module),
101 => Ok(SectionCode::Instance),
102 => Ok(SectionCode::Alias),
103 => Ok(SectionCode::ModuleCode),
_ => Err(BinaryReaderError::new("Invalid section code", offset)),
}
}
Expand Down
11 changes: 11 additions & 0 deletions crates/wasmparser/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,15 +39,18 @@ pub use crate::parser::WasmDecoder;
pub use crate::primitives::BinaryReaderError;
pub use crate::primitives::BrTable;
pub use crate::primitives::CustomSectionKind;
pub use crate::primitives::ExportType;
pub use crate::primitives::ExternalKind;
pub use crate::primitives::FuncType;
pub use crate::primitives::GlobalType;
pub use crate::primitives::Ieee32;
pub use crate::primitives::Ieee64;
pub use crate::primitives::ImportSectionEntryType;
pub use crate::primitives::InstanceType;
pub use crate::primitives::LinkingType;
pub use crate::primitives::MemoryImmediate;
pub use crate::primitives::MemoryType;
pub use crate::primitives::ModuleType;
pub use crate::primitives::NameType;
pub use crate::primitives::Naming;
pub use crate::primitives::Operator;
Expand All @@ -57,6 +60,7 @@ pub use crate::primitives::Result;
pub use crate::primitives::SectionCode;
pub use crate::primitives::TableType;
pub use crate::primitives::Type;
pub use crate::primitives::TypeDef;
pub use crate::primitives::TypeOrFuncType;
pub use crate::primitives::V128;

Expand All @@ -72,11 +76,15 @@ pub use crate::module_resources::WasmMemoryType;
pub use crate::module_resources::WasmModuleResources;
pub use crate::module_resources::WasmTableType;
pub use crate::module_resources::WasmType;
pub use crate::module_resources::WasmTypeDef;

pub(crate) use crate::module_resources::{wasm_func_type_inputs, wasm_func_type_outputs};

pub use crate::operators_validator::OperatorValidatorConfig;

pub use crate::readers::Alias;
pub use crate::readers::AliasSectionReader;
pub use crate::readers::AliasedInstance;
pub use crate::readers::CodeSectionReader;
pub use crate::readers::CustomSectionContent;
pub use crate::readers::Data;
Expand All @@ -97,10 +105,13 @@ pub use crate::readers::GlobalSectionReader;
pub use crate::readers::Import;
pub use crate::readers::ImportSectionReader;
pub use crate::readers::InitExpr;
pub use crate::readers::InstanceSectionReader;
pub use crate::readers::LinkingSectionReader;
pub use crate::readers::LocalsReader;
pub use crate::readers::MemorySectionReader;
pub use crate::readers::ModuleCodeSectionReader;
pub use crate::readers::ModuleReader;
pub use crate::readers::ModuleSectionReader;
pub use crate::readers::Name;
pub use crate::readers::NameSectionReader;
pub use crate::readers::NamingReader;
Expand Down
6 changes: 4 additions & 2 deletions crates/wasmparser/src/limits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@
// The limits are agreed upon with other engines for consistency.
pub const MAX_WASM_TYPES: usize = 1_000_000;
pub const MAX_WASM_FUNCTIONS: usize = 1_000_000;
pub const _MAX_WASM_IMPORTS: usize = 100_000;
pub const _MAX_WASM_EXPORTS: usize = 100_000;
pub const MAX_WASM_IMPORTS: usize = 100_000;
pub const MAX_WASM_EXPORTS: usize = 100_000;
pub const MAX_WASM_GLOBALS: usize = 1_000_000;
pub const _MAX_WASM_DATA_SEGMENTS: usize = 100_000;
pub const MAX_WASM_MEMORY_PAGES: usize = 65536;
Expand All @@ -32,3 +32,5 @@ pub const _MAX_WASM_TABLE_SIZE: usize = 10_000_000;
pub const MAX_WASM_TABLE_ENTRIES: usize = 10_000_000;
pub const MAX_WASM_TABLES: usize = 1;
pub const MAX_WASM_MEMORIES: usize = 1;
pub const MAX_WASM_MODULES: usize = 1_000;
pub const MAX_WASM_INSTANCES: usize = 1_000;
27 changes: 23 additions & 4 deletions crates/wasmparser/src/module_resources.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
* limitations under the License.
*/

use crate::{FuncType, TypeDef};

/// Types that qualify as Wasm types for validation purposes.
///
/// Must be comparable with `wasmparser` given Wasm types and
Expand All @@ -26,6 +28,12 @@ pub trait WasmType: PartialEq<crate::Type> + PartialEq + Eq {
fn to_parser_type(&self) -> crate::Type;
}

pub trait WasmTypeDef {
type FuncType: WasmFuncType;

fn as_func(&self) -> Option<&Self::FuncType>;
}

/// Types that qualify as Wasm function types for validation purposes.
pub trait WasmFuncType {
/// A type that is comparable with Wasm types.
Expand Down Expand Up @@ -277,7 +285,7 @@ pub trait WasmGlobalType {
/// the need of an additional parsing or validation step or copying data around.
pub trait WasmModuleResources {
/// The function type used for validation.
type FuncType: WasmFuncType;
type TypeDef: WasmTypeDef;
/// The table type used for validation.
type TableType: WasmTableType;
/// The memory type used for validation.
Expand All @@ -286,7 +294,7 @@ pub trait WasmModuleResources {
type GlobalType: WasmGlobalType;

/// Returns the type at given index.
fn type_at(&self, at: u32) -> Option<&Self::FuncType>;
fn type_at(&self, at: u32) -> Option<&Self::TypeDef>;
/// Returns the table at given index if any.
fn table_at(&self, at: u32) -> Option<&Self::TableType>;
/// Returns the linear memory at given index.
Expand All @@ -311,12 +319,12 @@ impl<T> WasmModuleResources for &'_ T
where
T: ?Sized + WasmModuleResources,
{
type FuncType = T::FuncType;
type TypeDef = T::TypeDef;
type TableType = T::TableType;
type MemoryType = T::MemoryType;
type GlobalType = T::GlobalType;

fn type_at(&self, at: u32) -> Option<&Self::FuncType> {
fn type_at(&self, at: u32) -> Option<&Self::TypeDef> {
T::type_at(self, at)
}
fn table_at(&self, at: u32) -> Option<&Self::TableType> {
Expand Down Expand Up @@ -352,6 +360,17 @@ impl WasmType for crate::Type {
}
}

impl<'a> WasmTypeDef for TypeDef<'a> {
type FuncType = FuncType;

fn as_func(&self) -> Option<&Self::FuncType> {
match self {
TypeDef::Func(f) => Some(f),
_ => None,
}
}
}

impl WasmFuncType for crate::FuncType {
type Type = crate::Type;

Expand Down
Loading

0 comments on commit c1aa81e

Please sign in to comment.