diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
commit | 2aa4a82499d4becd2284cdb482213d541b8804dd (patch) | |
tree | b80bf8bf13c3766139fbacc530efd0dd9d54394c /third_party/rust/wasmparser | |
parent | Initial commit. (diff) | |
download | firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.tar.xz firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.zip |
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/wasmparser')
37 files changed, 11468 insertions, 0 deletions
diff --git a/third_party/rust/wasmparser/.cargo-checksum.json b/third_party/rust/wasmparser/.cargo-checksum.json new file mode 100644 index 0000000000..785c5f37a8 --- /dev/null +++ b/third_party/rust/wasmparser/.cargo-checksum.json @@ -0,0 +1 @@ +{"files":{"Cargo.toml":"a16cf97fa88a9adb6f61c99df640bd83904854f9a98342bbb575ba47e86b5a7a","README.md":"2e252886759b5ee5137ec39efc0765850be2cb4242c68e6b44452b75d3191db3","benches/benchmark.rs":"2ed1bd83b9df9d91b11b0ab34053a77a228e9d3961a4a161477dd74882e95378","compare-with-main.sh":"2ddfab71ba571055292a29a36c1ede05f64ba094c78495b945d1486bf32ab6d7","examples/simple.rs":"606072a46c5c80df29da3ecd98a989feb1289243550033cd3c3e1df6045ce8ce","src/binary_reader.rs":"62acf2d5f4216735847beb464afcad2afbedc48a90c91d704e352a5de1223c02","src/lib.rs":"372776c7339cc879ba2a1109e4692d09441dd6832955ed48aed1806b135b72a4","src/limits.rs":"dff24bbeea651c6ad06f7c63916c17e024f892e5262e939ed88274021e967d0b","src/module_resources.rs":"8d4bc965c11d3d2adfe2162cc81ceb075ac2a73da8a77ce25f154af3449a79c9","src/operators_validator.rs":"40e5001adf0179233b321c8e3d1e175dca160fb3968ddf0a687e16f4985ad530","src/parser.rs":"c05a92a04020e990635728101e3f7d6653ccdeb54f2ce3615fc6c058b344cd8e","src/primitives.rs":"46edb5cb01925403b6f19d829ee2b2029f49120f10ab0e4640628d80d0142612","src/readers/alias_section.rs":"ef6556c3e300549958010aba9f1a0f6852c80ceddc763b4c11463d97234488b3","src/readers/code_section.rs":"d7d8be8f8bf158dac6ba7ee1d6583ea386e440f728a1f6ce1aa2e91352f884f4","src/readers/data_section.rs":"d919a22ebc44c53ca434df6c1d16efc8e126e7b55ed99691d5ae73c10bfadfff","src/readers/element_section.rs":"f168a3cb02439aeaa81621525e2747d3bc4743fac2237dcdf8658b67e010ca08","src/readers/export_section.rs":"3fe296f1789e789009a79115052194a1352d947f2a8830945d6b7d9983bb8579","src/readers/function_section.rs":"5467d7a375c22a4cc225819212e616f275ef01516f185b346eae2ffbb5c53cb3","src/readers/global_section.rs":"359450911ac662503f90288798baec2415df6d3b80990a7b75794683df7894b8","src/readers/import_section.rs":"80906451f78c64d31a76772d97c96d18d208eeabaaaf82372b0567a8991795c1","src/readers/init_expr.rs":"7020c80013dad4518a5f969c3ab4d624b46d778f03e632871cf343964f63441c","src/readers/instance_section.rs":"0f6cc9ed6bb6520493090eff5bbd6a9030ba1432206799b7dfed0b9285bafd73","src/readers/linking_section.rs":"db3091a48827a5b035e2f79f40f7ed9a7ea10acd4db6ab2bbd01e17a65a4265e","src/readers/memory_section.rs":"67d8457d3167b39fc9ae2c04f3c3e28bc10be97bbdaccd681675fb8d3eba2bd3","src/readers/mod.rs":"0fbaa4e1d7e3e68d8857fd8b3ce5c3fba886a1b460cd37398afcbf4802280b4e","src/readers/module_code_section.rs":"806eea527c84570ca6c5b8ca556f95727edae7da29e4f384b067113231b8e5f5","src/readers/module_section.rs":"6e28be8f1f4d2f1a9470ec356c46a76c2f21916143a1f0e245b96d2272d0301e","src/readers/name_section.rs":"60d4aa007cfdc16eedc1b4cb0bee560f6eebd82aaa81e9be9c844e515b16e445","src/readers/operators.rs":"3800f0321a776ddc5e8fb030828e4f2a65ebafa4b7f0808774384559ddfe49ea","src/readers/producers_section.rs":"77f93449e4bdcd61e4c79e47a685742f49cd5dac837ba002bce14120f14c9470","src/readers/reloc_section.rs":"e48e6acaa5145d6fbe1d74eb406ee59c43235faa47fbf0b07288504e60573a5e","src/readers/section_reader.rs":"e99763ce9c48994fd1e92f011a449936c4206a5c91d50fa580d003b6cc824ec5","src/readers/table_section.rs":"5d94185f68c4c7526a8836a2ebdb5b20fe754af58b68d2d0eb8fea62b7e6fe71","src/readers/type_section.rs":"87a54d238bf900aac0d0508e5c644d71b1e591df99367587feb68146a25a5a61","src/validator.rs":"4843a7343d81b4e656943b0f31a7f7aee6e901657a7ff569cf6779dfe177dfb1","src/validator/func.rs":"ed93410b8572a39449a682f4d9c13530a8806cffc51fa4b904b89a72fa89dfed"},"package":null}
\ No newline at end of file diff --git a/third_party/rust/wasmparser/Cargo.toml b/third_party/rust/wasmparser/Cargo.toml new file mode 100644 index 0000000000..e26c34aff2 --- /dev/null +++ b/third_party/rust/wasmparser/Cargo.toml @@ -0,0 +1,31 @@ +[package] +name = "wasmparser" +version = "0.67.0" +authors = ["Yury Delendik <ydelendik@mozilla.com>"] +license = "Apache-2.0 WITH LLVM-exception" +repository = "https://github.com/bytecodealliance/wasm-tools/tree/main/crates/wasmparser" +homepage = "https://github.com/bytecodealliance/wasm-tools/tree/main/crates/wasmparser" +keywords = ["parser", "WebAssembly", "wasm"] +description = """ +A simple event-driven library for parsing WebAssembly binary files. +""" +edition = "2018" + +[dev-dependencies] +anyhow = "1.0" +criterion = "0.3" +getopts = "0.2" +wat = { path = "../wat" } +wast = { path = "../wast" } +rayon = "1.3" +wasmparser-dump = { path = "../dump" } + +[[bench]] +name = "benchmark" +harness = false + +[features] +# The "deterministic" feature supports only Wasm code with "deterministic" execution +# across any hardware. This feature is very critical for many Blockchain infrastructures +# that rely on deterministic executions of smart contracts across different hardwares. +deterministic = [] diff --git a/third_party/rust/wasmparser/README.md b/third_party/rust/wasmparser/README.md new file mode 100644 index 0000000000..88e460e9cd --- /dev/null +++ b/third_party/rust/wasmparser/README.md @@ -0,0 +1,70 @@ +# The WebAssembly binary file decoder in Rust + +**A [Bytecode Alliance](https://bytecodealliance.org/) project** + +[![crates.io link](https://img.shields.io/crates/v/wasmparser.svg)](https://crates.io/crates/wasmparser) +[![docs.rs docs](https://img.shields.io/static/v1?label=docs&message=wasmparser&color=blue&style=flat-square)](https://docs.rs/wasmparser/) + +The decoder library provides lightweight and fast decoding/parsing of WebAssembly binary files. + +The other goal is minimal memory footprint. For this reason, there is no AST or IR of WebAssembly data. + +See also its sibling at https://github.com/wasdk/wasmparser + + +## Documentation + +The documentation and examples can be found at the https://docs.rs/wasmparser/ + + +## Example + +```rust +use wasmparser::WasmDecoder; +use wasmparser::Parser; +use wasmparser::ParserState; + +fn get_name(bytes: &[u8]) -> &str { + str::from_utf8(bytes).ok().unwrap() +} + +fn main() { + let ref buf: Vec<u8> = read_wasm_bytes(); + let mut parser = Parser::new(buf); + loop { + let state = parser.read(); + match *state { + ParserState::BeginWasm { .. } => { + println!("====== Module"); + } + ParserState::ExportSectionEntry { field, ref kind, .. } => { + println!(" Export {} {:?}", get_name(field), kind); + } + ParserState::ImportSectionEntry { module, field, .. } => { + println!(" Import {}::{}", get_name(module), get_name(field)) + } + ParserState::EndWasm => break, + _ => ( /* println!(" Other {:?}", state) */ ) + } + } +} +``` + + +## Fuzzing + +To fuzz test wasmparser.rs, switch to a nightly Rust compiler and install [cargo-fuzz]: + +``` +cargo install cargo-fuzz +``` + +Then, from the root of the repository, run: + +``` +cargo fuzz run parse +``` + +If you want to use files as seeds for the fuzzer, add them to `fuzz/corpus/parse/` and restart cargo-fuzz. + +[cargo-fuzz]: https://github.com/rust-fuzz/cargo-fuzz diff --git a/third_party/rust/wasmparser/benches/benchmark.rs b/third_party/rust/wasmparser/benches/benchmark.rs new file mode 100644 index 0000000000..25a7257cb0 --- /dev/null +++ b/third_party/rust/wasmparser/benches/benchmark.rs @@ -0,0 +1,236 @@ +#[macro_use] +extern crate criterion; + +use anyhow::Result; +use criterion::Criterion; +use std::fs; +use std::path::Path; +use std::path::PathBuf; +use wasmparser::{DataKind, ElementKind, Parser, Payload, Validator, WasmFeatures}; + +/// A benchmark input. +pub struct BenchmarkInput { + /// The path to the benchmark file important for handling errors. + pub path: PathBuf, + /// The encoded Wasm module that is run by the benchmark. + pub wasm: Vec<u8>, +} + +impl BenchmarkInput { + /// Creates a new benchmark input. + pub fn new(test_path: PathBuf, encoded_wasm: Vec<u8>) -> Self { + Self { + path: test_path, + wasm: encoded_wasm, + } + } +} + +/// Returns a vector of all found benchmark input files under the given directory. +/// +/// Benchmark input files can be `.wat` or `.wast` formatted files. +/// For `.wast` files we pull out all the module directives and run them in the benchmarks. +fn collect_test_files(path: &Path, list: &mut Vec<BenchmarkInput>) -> Result<()> { + for entry in path.read_dir()? { + let entry = entry?; + let path = entry.path(); + if path.is_dir() { + collect_test_files(&path, list)?; + continue; + } + match path.extension().and_then(|ext| ext.to_str()) { + Some("wasm") => { + let wasm = fs::read(&path)?; + list.push(BenchmarkInput::new(path, wasm)); + } + Some("wat") | Some("txt") => { + if let Ok(wasm) = wat::parse_file(&path) { + list.push(BenchmarkInput::new(path, wasm)); + } + } + Some("wast") => { + let contents = fs::read_to_string(&path)?; + let buf = match wast::parser::ParseBuffer::new(&contents) { + Ok(buf) => buf, + Err(_) => continue, + }; + let wast: wast::Wast<'_> = match wast::parser::parse(&buf) { + Ok(wast) => wast, + Err(_) => continue, + }; + for directive in wast.directives { + match directive { + wast::WastDirective::Module(mut module) => { + let wasm = module.encode()?; + list.push(BenchmarkInput::new(path.clone(), wasm)); + } + _ => continue, + } + } + } + _ => (), + } + } + Ok(()) +} + +/// Reads the input given the Wasm parser or validator. +/// +/// The `path` specifies which benchmark input file we are currently operating on +/// so that we can report better errors in case of failures. +fn read_all_wasm(wasm: &[u8]) -> Result<()> { + use Payload::*; + for item in Parser::new(0).parse_all(wasm) { + match item? { + TypeSection(s) => { + for item in s { + item?; + } + } + ImportSection(s) => { + for item in s { + item?; + } + } + AliasSection(s) => { + for item in s { + item?; + } + } + InstanceSection(s) => { + for item in s { + for arg in item?.args()? { + arg?; + } + } + } + ModuleSection(s) => { + for item in s { + item?; + } + } + FunctionSection(s) => { + for item in s { + item?; + } + } + TableSection(s) => { + for item in s { + item?; + } + } + MemorySection(s) => { + for item in s { + item?; + } + } + GlobalSection(s) => { + for item in s { + for op in item?.init_expr.get_operators_reader() { + op?; + } + } + } + ExportSection(s) => { + for item in s { + item?; + } + } + ElementSection(s) => { + for item in s { + let item = item?; + if let ElementKind::Active { init_expr, .. } = item.kind { + for op in init_expr.get_operators_reader() { + op?; + } + } + for op in item.items.get_items_reader()? { + op?; + } + } + } + DataSection(s) => { + for item in s { + let item = item?; + if let DataKind::Active { init_expr, .. } = item.kind { + for op in init_expr.get_operators_reader() { + op?; + } + } + } + } + CodeSectionEntry(body) => { + for local in body.get_locals_reader()? { + local?; + } + for op in body.get_operators_reader()? { + op?; + } + } + + Version { .. } + | StartSection { .. } + | DataCountSection { .. } + | UnknownSection { .. } + | CustomSection { .. } + | CodeSectionStart { .. } + | ModuleCodeSectionStart { .. } + | ModuleCodeSectionEntry { .. } + | End => {} + } + } + Ok(()) +} + +/// Returns the default benchmark inputs that are proper `wasmparser` benchmark +/// test inputs. +fn collect_benchmark_inputs() -> Vec<BenchmarkInput> { + let mut ret = Vec::new(); + collect_test_files("../../tests".as_ref(), &mut ret).unwrap(); + return ret; +} + +fn it_works_benchmark(c: &mut Criterion) { + let mut inputs = collect_benchmark_inputs(); + // Filter out all benchmark inputs that fail to parse via `wasmparser`. + inputs.retain(|input| read_all_wasm(input.wasm.as_slice()).is_ok()); + c.bench_function("it works benchmark", move |b| { + b.iter(|| { + for input in &mut inputs { + read_all_wasm(input.wasm.as_slice()).unwrap(); + } + }) + }); +} + +fn validate_benchmark(c: &mut Criterion) { + fn validator() -> Validator { + let mut ret = Validator::new(); + ret.wasm_features(WasmFeatures { + reference_types: true, + multi_value: true, + simd: true, + module_linking: true, + bulk_memory: true, + threads: true, + tail_call: true, + multi_memory: true, + memory64: true, + deterministic_only: false, + }); + return ret; + } + let mut inputs = collect_benchmark_inputs(); + // Filter out all benchmark inputs that fail to validate via `wasmparser`. + inputs.retain(|input| validator().validate_all(&input.wasm).is_ok()); + c.bench_function("validate benchmark", move |b| { + b.iter(|| { + for input in &mut inputs { + validator().validate_all(&input.wasm).unwrap(); + } + }) + }); +} + +criterion_group!(benchmark, it_works_benchmark, validate_benchmark); +criterion_main!(benchmark); diff --git a/third_party/rust/wasmparser/compare-with-main.sh b/third_party/rust/wasmparser/compare-with-main.sh new file mode 100755 index 0000000000..eed367bfab --- /dev/null +++ b/third_party/rust/wasmparser/compare-with-main.sh @@ -0,0 +1,10 @@ +#!/usr/bin/env bash +set -e + +# switch to main branch and record its bench results +git checkout main && \ +cargo bench --bench benchmark -- --noplot --save-baseline before + +# record current bench results +git checkout - && \ +cargo bench --bench benchmark -- --noplot --baseline before diff --git a/third_party/rust/wasmparser/examples/simple.rs b/third_party/rust/wasmparser/examples/simple.rs new file mode 100644 index 0000000000..7d0ae55aa7 --- /dev/null +++ b/third_party/rust/wasmparser/examples/simple.rs @@ -0,0 +1,37 @@ +use anyhow::Result; +use std::env; +use wasmparser::{Parser, Payload}; + +fn main() -> Result<()> { + let args = env::args().collect::<Vec<_>>(); + if args.len() != 2 { + println!("Usage: {} in.wasm", args[0]); + return Ok(()); + } + + let buf: Vec<u8> = std::fs::read(&args[1])?; + for payload in Parser::new(0).parse_all(&buf) { + match payload? { + Payload::Version { .. } => { + println!("====== Module"); + } + Payload::ExportSection(s) => { + for export in s { + let export = export?; + println!(" Export {} {:?}", export.field, export.kind); + } + } + Payload::ImportSection(s) => { + for import in s { + let import = import?; + println!(" Import {}::{}", import.module, import.field.unwrap()); + } + } + _other => { + // println!("found payload {:?}", _other); + } + } + } + + Ok(()) +} diff --git a/third_party/rust/wasmparser/src/binary_reader.rs b/third_party/rust/wasmparser/src/binary_reader.rs new file mode 100644 index 0000000000..08c5ab438a --- /dev/null +++ b/third_party/rust/wasmparser/src/binary_reader.rs @@ -0,0 +1,1879 @@ +/* Copyright 2018 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use std::convert::TryInto; +use std::fmt; +use std::str; + +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, +}; +use crate::{ExportType, Import, ImportSectionEntryType, InstanceType, ModuleType}; + +const MAX_WASM_BR_TABLE_SIZE: usize = MAX_WASM_FUNCTION_SIZE; + +fn is_name(name: &str, expected: &'static str) -> bool { + name == expected +} + +fn is_name_prefix(name: &str, prefix: &'static str) -> bool { + name.starts_with(prefix) +} + +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, + pub payload_len: usize, +} + +/// Bytecode range in the WebAssembly module. +#[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub struct Range { + /// The start bound of the range. + pub start: usize, + /// The end bound of the range. + pub end: usize, +} + +impl Range { + /// Constructs a new instance of `Range`. + /// + /// # Panics + /// If `start` is greater than `end`. + pub fn new(start: usize, end: usize) -> Range { + assert!(start <= end); + Range { start, end } + } + + /// Returns a new slice between `start` and `end - 1` from `data`. + pub fn slice<'a>(&self, data: &'a [u8]) -> &'a [u8] { + &data[self.start..self.end] + } +} + +/// A binary reader of the WebAssembly structures and types. +#[derive(Clone, Debug, Hash)] +pub struct BinaryReader<'a> { + pub(crate) buffer: &'a [u8], + pub(crate) position: usize, + pub(crate) original_offset: usize, +} + +impl<'a> BinaryReader<'a> { + /// Constructs `BinaryReader` type. + /// + /// # Examples + /// ``` + /// let fn_body = &vec![0x41, 0x00, 0x10, 0x00, 0x0B]; + /// let mut reader = wasmparser::BinaryReader::new(fn_body); + /// while !reader.eof() { + /// let op = reader.read_operator(); + /// println!("{:?}", op) + /// } + /// ``` + pub fn new(data: &[u8]) -> BinaryReader { + BinaryReader { + buffer: data, + position: 0, + original_offset: 0, + } + } + + /// Constructs a `BinaryReader` with an explicit starting offset. + pub fn new_with_offset(data: &[u8], original_offset: usize) -> BinaryReader { + BinaryReader { + buffer: data, + position: 0, + original_offset, + } + } + + pub fn original_position(&self) -> usize { + self.original_offset + self.position + } + + /// Returns a range from the starting offset to the end of the buffer. + pub fn range(&self) -> Range { + Range { + start: self.original_offset, + end: self.original_offset + self.buffer.len(), + } + } + + pub(crate) fn remaining_buffer(&self) -> &'a [u8] { + &self.buffer[self.position..] + } + + fn ensure_has_byte(&self) -> Result<()> { + if self.position < self.buffer.len() { + Ok(()) + } else { + Err(BinaryReaderError::eof(self.original_position(), 1)) + } + } + + pub(crate) fn ensure_has_bytes(&self, len: usize) -> Result<()> { + if self.position + len <= self.buffer.len() { + Ok(()) + } else { + let hint = self.position + len - self.buffer.len(); + Err(BinaryReaderError::eof(self.original_position(), hint)) + } + } + + fn read_var_u1(&mut self) -> Result<u32> { + let b = self.read_u8()?; + if (b & 0xFE) != 0 { + return Err(BinaryReaderError::new( + "Invalid var_u1", + self.original_position() - 1, + )); + } + Ok(b) + } + + fn read_var_i7(&mut self) -> Result<i32> { + let b = self.read_u8()?; + if (b & 0x80) != 0 { + return Err(BinaryReaderError::new( + "Invalid var_i7", + self.original_position() - 1, + )); + } + Ok((b << 25) as i32 >> 25) + } + + pub(crate) fn read_var_u7(&mut self) -> Result<u32> { + let b = self.read_u8()?; + if (b & 0x80) != 0 { + return Err(BinaryReaderError::new( + "Invalid var_u7", + self.original_position() - 1, + )); + } + Ok(b) + } + + pub fn read_type(&mut self) -> Result<Type> { + let code = self.read_var_i7()?; + match code { + -0x01 => Ok(Type::I32), + -0x02 => Ok(Type::I64), + -0x03 => Ok(Type::F32), + -0x04 => Ok(Type::F64), + -0x05 => Ok(Type::V128), + -0x10 => Ok(Type::FuncRef), + -0x11 => Ok(Type::ExternRef), + -0x20 => Ok(Type::Func), + -0x40 => Ok(Type::EmptyBlockType), + _ => Err(BinaryReaderError::new( + "Invalid type", + self.original_position() - 1, + )), + } + } + + pub(crate) fn read_external_kind(&mut self) -> Result<ExternalKind> { + let code = self.read_u8()?; + match code { + 0 => Ok(ExternalKind::Function), + 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, + )), + } + } + + pub(crate) fn read_func_type(&mut self) -> Result<FuncType> { + let params_len = self.read_var_u32()? as usize; + if params_len > MAX_WASM_FUNCTION_PARAMS { + return Err(BinaryReaderError::new( + "function params size is out of bound", + self.original_position() - 1, + )); + } + let mut params: Vec<Type> = Vec::with_capacity(params_len); + for _ in 0..params_len { + params.push(self.read_type()?); + } + let returns_len = self.read_var_u32()? as usize; + if returns_len > MAX_WASM_FUNCTION_RETURNS { + return Err(BinaryReaderError::new( + "function returns size is out of bound", + self.original_position() - 1, + )); + } + let mut returns: Vec<Type> = Vec::with_capacity(returns_len); + for _ in 0..returns_len { + returns.push(self.read_type()?); + } + Ok(FuncType { + params: params.into_boxed_slice(), + returns: returns.into_boxed_slice(), + }) + } + + 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 { + 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()?; + if (flags & !0x1) != 0 { + return Err(BinaryReaderError::new( + "invalid table resizable limits flags", + self.original_position() - 1, + )); + } + let limits = self.read_resizable_limits((flags & 0x1) != 0)?; + Ok(TableType { + element_type, + limits, + }) + } + + pub(crate) fn read_memory_type(&mut self) -> Result<MemoryType> { + let pos = self.original_position(); + let flags = self.read_u8()?; + if (flags & !0x7) != 0 { + return Err(BinaryReaderError::new( + "invalid table resizable limits flags", + pos, + )); + } + if flags & 0x4 == 0 { + let limits = self.read_resizable_limits((flags & 0x1) != 0)?; + let shared = (flags & 0x2) != 0; + Ok(MemoryType::M32 { limits, shared }) + } else { + let limits = self.read_resizable_limits64((flags & 0x1) != 0)?; + let shared = (flags & 0x2) != 0; + Ok(MemoryType::M64 { limits, shared }) + } + } + + pub(crate) fn read_global_type(&mut self) -> Result<GlobalType> { + Ok(GlobalType { + content_type: self.read_type()?, + mutable: self.read_var_u1()? != 0, + }) + } + + fn read_first_byte_and_var_u32(&mut self) -> Result<(u8, u32)> { + let pos = self.position; + let val = self.read_var_u32()?; + Ok((self.buffer[pos], val)) + } + + fn read_memarg(&mut self) -> Result<MemoryImmediate> { + let flags_pos = self.original_position(); + let mut flags = self.read_var_u32()?; + let offset = self.read_var_u32()?; + let memory = if flags & (1 << 6) != 0 { + flags ^= 1 << 6; + self.read_var_u32()? + } else { + 0 + }; + let align = if flags >= (1 << 6) { + return Err(BinaryReaderError::new("alignment too large", flags_pos)); + } else { + flags as u8 + }; + Ok(MemoryImmediate { + align, + offset, + memory, + }) + } + + pub(crate) fn read_section_code(&mut self, id: u32, offset: usize) -> Result<SectionCode<'a>> { + match id { + 0 => { + let name = self.read_string()?; + let kind = if is_name(name, "name") { + CustomSectionKind::Name + } else if is_name(name, "producers") { + CustomSectionKind::Producers + } else if is_name(name, "sourceMappingURL") { + CustomSectionKind::SourceMappingURL + } else if is_name_prefix(name, "reloc.") { + CustomSectionKind::Reloc + } else if is_name(name, "linking") { + CustomSectionKind::Linking + } else { + CustomSectionKind::Unknown + }; + Ok(SectionCode::Custom { name, kind }) + } + 1 => Ok(SectionCode::Type), + 2 => Ok(SectionCode::Import), + 3 => Ok(SectionCode::Function), + 4 => Ok(SectionCode::Table), + 5 => Ok(SectionCode::Memory), + 6 => Ok(SectionCode::Global), + 7 => Ok(SectionCode::Export), + 8 => Ok(SectionCode::Start), + 9 => Ok(SectionCode::Element), + 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)), + } + } + + fn read_br_table(&mut self) -> Result<BrTable<'a>> { + let targets_len = self.read_var_u32()? as usize; + if targets_len > MAX_WASM_BR_TABLE_SIZE { + return Err(BinaryReaderError::new( + "br_table size is out of bound", + self.original_position() - 1, + )); + } + let start = self.position; + for _ in 0..targets_len { + self.skip_var_32()?; + } + self.skip_var_32()?; + let end = self.position; + Ok(BrTable { + reader: BinaryReader::new_with_offset(&self.buffer[start..end], start), + cnt: targets_len as usize, + }) + } + + /// Returns whether the `BinaryReader` has reached the end of the file. + pub fn eof(&self) -> bool { + self.position >= self.buffer.len() + } + + /// Returns the `BinaryReader`'s current position. + pub fn current_position(&self) -> usize { + self.position + } + + /// Returns the number of bytes remaining in the `BinaryReader`. + pub fn bytes_remaining(&self) -> usize { + self.buffer.len() - self.position + } + + /// Advances the `BinaryReader` `size` bytes, and returns a slice from the + /// current position of `size` length. + /// + /// # Errors + /// If `size` exceeds the remaining length in `BinaryReader`. + pub fn read_bytes(&mut self, size: usize) -> Result<&'a [u8]> { + self.ensure_has_bytes(size)?; + let start = self.position; + self.position += size; + Ok(&self.buffer[start..self.position]) + } + + /// Advances the `BinaryReader` four bytes and returns a `u32`. + /// # Errors + /// If `BinaryReader` has less than four bytes remaining. + pub fn read_u32(&mut self) -> Result<u32> { + self.ensure_has_bytes(4)?; + let word = u32::from_le_bytes( + self.buffer[self.position..self.position + 4] + .try_into() + .unwrap(), + ); + self.position += 4; + Ok(word) + } + + /// Advances the `BinaryReader` eight bytes and returns a `u64`. + /// # Errors + /// If `BinaryReader` has less than eight bytes remaining. + pub fn read_u64(&mut self) -> Result<u64> { + self.ensure_has_bytes(8)?; + let word = u64::from_le_bytes( + self.buffer[self.position..self.position + 8] + .try_into() + .unwrap(), + ); + self.position += 8; + Ok(word) + } + + /// Advances the `BinaryReader` a single byte, and returns the data as + /// a `u32`. + /// + /// # Errors + /// + /// If `BinaryReader` has no bytes remaining. + pub fn read_u8(&mut self) -> Result<u32> { + self.ensure_has_byte()?; + let b = u32::from(self.buffer[self.position]); + self.position += 1; + Ok(b) + } + + /// Advances the `BinaryReader` up to two bytes to parse a variable + /// length integer as a `u8`. + /// + /// # Errors + /// + /// If `BinaryReader` has less than one or two bytes remaining, or the + /// integer is larger than eight bits. + pub fn read_var_u8(&mut self) -> Result<u32> { + // Optimization for single byte i32. + let byte = self.read_u8()?; + if (byte & 0x80) == 0 { + return Ok(byte); + } + + let result = (self.read_u8()? << 7) | (byte & 0x7F); + if result >= 0x100 { + return Err(BinaryReaderError::new( + "Invalid var_u8", + self.original_position() - 1, + )); + } + Ok(result) + } + + /// Advances the `BinaryReader` up to four bytes to parse a variable + /// length integer as a `u32`. + /// + /// # Errors + /// + /// If `BinaryReader` has less than one or up to four bytes remaining, or + /// the integer is larger than 32 bits. + pub fn read_var_u32(&mut self) -> Result<u32> { + // Optimization for single byte i32. + let byte = self.read_u8()?; + if (byte & 0x80) == 0 { + return Ok(byte); + } + + let mut result = byte & 0x7F; + let mut shift = 7; + loop { + let byte = self.read_u8()?; + result |= ((byte & 0x7F) as u32) << shift; + if shift >= 25 && (byte >> (32 - shift)) != 0 { + // The continuation bit or unused bits are set. + return Err(BinaryReaderError::new( + "Invalid var_u32", + self.original_position() - 1, + )); + } + shift += 7; + if (byte & 0x80) == 0 { + break; + } + } + Ok(result) + } + + /// Advances the `BinaryReader` up to four bytes to parse a variable + /// length integer as a `u64`. + /// + /// # Errors + /// + /// If `BinaryReader` has less than one or up to eight bytes remaining, or + /// the integer is larger than 64 bits. + pub fn read_var_u64(&mut self) -> Result<u64> { + // Optimization for single byte u64. + let byte = u64::from(self.read_u8()?); + if (byte & 0x80) == 0 { + return Ok(byte); + } + + let mut result = byte & 0x7F; + let mut shift = 7; + loop { + let byte = u64::from(self.read_u8()?); + result |= (byte & 0x7F) << shift; + if shift >= 57 && (byte >> (64 - shift)) != 0 { + // The continuation bit or unused bits are set. + return Err(BinaryReaderError::new( + "Invalid var_u64", + self.original_position() - 1, + )); + } + shift += 7; + if (byte & 0x80) == 0 { + break; + } + } + Ok(result) + } + + /// Advances the `BinaryReader` up to four bytes over a variable length 32 + /// bit integer, discarding the result. + /// # Errors + /// If `BinaryReader` has less than one or up to four bytes remaining, or + /// the integer is larger than 32 bits. + pub fn skip_var_32(&mut self) -> Result<()> { + for _ in 0..5 { + let byte = self.read_u8()?; + if (byte & 0x80) == 0 { + return Ok(()); + } + } + Err(BinaryReaderError::new( + "Invalid var_32", + self.original_position() - 1, + )) + } + + /// Alias method for `BinaryReader::skip_var_u32`. + pub fn skip_type(&mut self) -> Result<()> { + self.skip_var_32() + } + + /// Advances the `BinaryReader` `len` bytes, skipping the result. + /// # Errors + /// If `BinaryReader` has less than `len` bytes remaining. + pub fn skip_bytes(&mut self, len: usize) -> Result<()> { + self.ensure_has_bytes(len)?; + self.position += len; + Ok(()) + } + + /// Advances the `BinaryReader` past a WebAssembly string. This method does + /// not perform any utf-8 validation. + /// # Errors + /// If `BinaryReader` has less than four bytes, the string's length exceeds + /// the remaining bytes, or the string length + /// exceeds `limits::MAX_WASM_STRING_SIZE`. + pub fn skip_string(&mut self) -> Result<()> { + let len = self.read_var_u32()? as usize; + if len > MAX_WASM_STRING_SIZE { + return Err(BinaryReaderError::new( + "string size in out of bounds", + self.original_position() - 1, + )); + } + self.skip_bytes(len) + } + + pub(crate) fn skip_to(&mut self, position: usize) { + assert!( + self.position <= position && position <= self.buffer.len(), + "skip_to allowed only into region past current position" + ); + self.position = position; + } + + /// Advances the `BinaryReader` up to four bytes to parse a variable + /// length integer as a `i32`. + /// # Errors + /// If `BinaryReader` has less than one or up to four bytes remaining, or + /// the integer is larger than 32 bits. + pub fn read_var_i32(&mut self) -> Result<i32> { + // Optimization for single byte i32. + let byte = self.read_u8()?; + if (byte & 0x80) == 0 { + return Ok(((byte as i32) << 25) >> 25); + } + + let mut result = (byte & 0x7F) as i32; + let mut shift = 7; + loop { + let byte = self.read_u8()?; + result |= ((byte & 0x7F) as i32) << shift; + if shift >= 25 { + let continuation_bit = (byte & 0x80) != 0; + let sign_and_unused_bit = (byte << 1) as i8 >> (32 - shift); + if continuation_bit || (sign_and_unused_bit != 0 && sign_and_unused_bit != -1) { + return Err(BinaryReaderError::new( + "Invalid var_i32", + self.original_position() - 1, + )); + } + return Ok(result); + } + shift += 7; + if (byte & 0x80) == 0 { + break; + } + } + let ashift = 32 - shift; + Ok((result << ashift) >> ashift) + } + + /// Advances the `BinaryReader` up to four bytes to parse a variable + /// length integer as a signed 33 bit integer, returned as a `i64`. + /// # Errors + /// If `BinaryReader` has less than one or up to five bytes remaining, or + /// the integer is larger than 33 bits. + pub fn read_var_s33(&mut self) -> Result<i64> { + // Optimization for single byte. + let byte = self.read_u8()?; + if (byte & 0x80) == 0 { + return Ok(((byte as i8) << 1) as i64 >> 1); + } + + let mut result = (byte & 0x7F) as i64; + let mut shift = 7; + loop { + let byte = self.read_u8()?; + result |= ((byte & 0x7F) as i64) << shift; + if shift >= 25 { + let continuation_bit = (byte & 0x80) != 0; + let sign_and_unused_bit = (byte << 1) as i8 >> (33 - shift); + if continuation_bit || (sign_and_unused_bit != 0 && sign_and_unused_bit != -1) { + return Err(BinaryReaderError::new( + "Invalid var_s33", + self.original_position() - 1, + )); + } + return Ok(result); + } + shift += 7; + if (byte & 0x80) == 0 { + break; + } + } + let ashift = 64 - shift; + Ok((result << ashift) >> ashift) + } + + /// Advances the `BinaryReader` up to eight bytes to parse a variable + /// length integer as a 64 bit integer, returned as a `i64`. + /// # Errors + /// If `BinaryReader` has less than one or up to eight bytes remaining, or + /// the integer is larger than 64 bits. + pub fn read_var_i64(&mut self) -> Result<i64> { + let mut result: i64 = 0; + let mut shift = 0; + loop { + let byte = self.read_u8()?; + result |= i64::from(byte & 0x7F) << shift; + if shift >= 57 { + let continuation_bit = (byte & 0x80) != 0; + let sign_and_unused_bit = ((byte << 1) as i8) >> (64 - shift); + if continuation_bit || (sign_and_unused_bit != 0 && sign_and_unused_bit != -1) { + return Err(BinaryReaderError::new( + "Invalid var_i64", + self.original_position() - 1, + )); + } + return Ok(result); + } + shift += 7; + if (byte & 0x80) == 0 { + break; + } + } + let ashift = 64 - shift; + Ok((result << ashift) >> ashift) + } + + /// Advances the `BinaryReader` up to four bytes to parse a variable + /// length integer as a 32 bit floating point integer, returned as `Ieee32`. + /// # Errors + /// If `BinaryReader` has less than one or up to four bytes remaining, or + /// the integer is larger than 32 bits. + pub fn read_f32(&mut self) -> Result<Ieee32> { + let value = self.read_u32()?; + Ok(Ieee32(value)) + } + + /// Advances the `BinaryReader` up to four bytes to parse a variable + /// length integer as a 32 bit floating point integer, returned as `Ieee32`. + /// # Errors + /// If `BinaryReader` has less than one or up to four bytes remaining, or + /// the integer is larger than 32 bits. + pub fn read_f64(&mut self) -> Result<Ieee64> { + let value = self.read_u64()?; + Ok(Ieee64(value)) + } + + /// Reads a WebAssembly string from the module. + /// # Errors + /// If `BinaryReader` has less than up to four bytes remaining, the string's + /// length exceeds the remaining bytes, the string's length exceeds + /// `limits::MAX_WASM_STRING_SIZE`, or the string contains invalid utf-8. + pub fn read_string(&mut self) -> Result<&'a str> { + let len = self.read_var_u32()? as usize; + if len > MAX_WASM_STRING_SIZE { + return Err(BinaryReaderError::new( + "string size in out of bounds", + self.original_position() - 1, + )); + } + let bytes = self.read_bytes(len)?; + str::from_utf8(bytes).map_err(|_| { + BinaryReaderError::new("invalid UTF-8 encoding", self.original_position() - 1) + }) + } + + fn read_memarg_of_align(&mut self, max_align: u8) -> Result<MemoryImmediate> { + let align_pos = self.original_position(); + let imm = self.read_memarg()?; + if imm.align > max_align { + return Err(BinaryReaderError::new( + "alignment must not be larger than natural", + align_pos, + )); + } + Ok(imm) + } + + fn read_0xfe_operator(&mut self) -> Result<Operator<'a>> { + let code = self.read_var_u32()?; + Ok(match code { + 0x00 => Operator::MemoryAtomicNotify { + memarg: self.read_memarg_of_align(2)?, + }, + 0x01 => Operator::MemoryAtomicWait32 { + memarg: self.read_memarg_of_align(2)?, + }, + 0x02 => Operator::MemoryAtomicWait64 { + memarg: self.read_memarg_of_align(3)?, + }, + 0x03 => Operator::AtomicFence { + flags: self.read_u8()? as u8, + }, + 0x10 => Operator::I32AtomicLoad { + memarg: self.read_memarg_of_align(2)?, + }, + 0x11 => Operator::I64AtomicLoad { + memarg: self.read_memarg_of_align(3)?, + }, + 0x12 => Operator::I32AtomicLoad8U { + memarg: self.read_memarg_of_align(0)?, + }, + 0x13 => Operator::I32AtomicLoad16U { + memarg: self.read_memarg_of_align(1)?, + }, + 0x14 => Operator::I64AtomicLoad8U { + memarg: self.read_memarg_of_align(0)?, + }, + 0x15 => Operator::I64AtomicLoad16U { + memarg: self.read_memarg_of_align(1)?, + }, + 0x16 => Operator::I64AtomicLoad32U { + memarg: self.read_memarg_of_align(2)?, + }, + 0x17 => Operator::I32AtomicStore { + memarg: self.read_memarg_of_align(2)?, + }, + 0x18 => Operator::I64AtomicStore { + memarg: self.read_memarg_of_align(3)?, + }, + 0x19 => Operator::I32AtomicStore8 { + memarg: self.read_memarg_of_align(0)?, + }, + 0x1a => Operator::I32AtomicStore16 { + memarg: self.read_memarg_of_align(1)?, + }, + 0x1b => Operator::I64AtomicStore8 { + memarg: self.read_memarg_of_align(0)?, + }, + 0x1c => Operator::I64AtomicStore16 { + memarg: self.read_memarg_of_align(1)?, + }, + 0x1d => Operator::I64AtomicStore32 { + memarg: self.read_memarg_of_align(2)?, + }, + 0x1e => Operator::I32AtomicRmwAdd { + memarg: self.read_memarg_of_align(2)?, + }, + 0x1f => Operator::I64AtomicRmwAdd { + memarg: self.read_memarg_of_align(3)?, + }, + 0x20 => Operator::I32AtomicRmw8AddU { + memarg: self.read_memarg_of_align(0)?, + }, + 0x21 => Operator::I32AtomicRmw16AddU { + memarg: self.read_memarg_of_align(1)?, + }, + 0x22 => Operator::I64AtomicRmw8AddU { + memarg: self.read_memarg_of_align(0)?, + }, + 0x23 => Operator::I64AtomicRmw16AddU { + memarg: self.read_memarg_of_align(1)?, + }, + 0x24 => Operator::I64AtomicRmw32AddU { + memarg: self.read_memarg_of_align(2)?, + }, + 0x25 => Operator::I32AtomicRmwSub { + memarg: self.read_memarg_of_align(2)?, + }, + 0x26 => Operator::I64AtomicRmwSub { + memarg: self.read_memarg_of_align(3)?, + }, + 0x27 => Operator::I32AtomicRmw8SubU { + memarg: self.read_memarg_of_align(0)?, + }, + 0x28 => Operator::I32AtomicRmw16SubU { + memarg: self.read_memarg_of_align(1)?, + }, + 0x29 => Operator::I64AtomicRmw8SubU { + memarg: self.read_memarg_of_align(0)?, + }, + 0x2a => Operator::I64AtomicRmw16SubU { + memarg: self.read_memarg_of_align(1)?, + }, + 0x2b => Operator::I64AtomicRmw32SubU { + memarg: self.read_memarg_of_align(2)?, + }, + 0x2c => Operator::I32AtomicRmwAnd { + memarg: self.read_memarg_of_align(2)?, + }, + 0x2d => Operator::I64AtomicRmwAnd { + memarg: self.read_memarg_of_align(3)?, + }, + 0x2e => Operator::I32AtomicRmw8AndU { + memarg: self.read_memarg_of_align(0)?, + }, + 0x2f => Operator::I32AtomicRmw16AndU { + memarg: self.read_memarg_of_align(1)?, + }, + 0x30 => Operator::I64AtomicRmw8AndU { + memarg: self.read_memarg_of_align(0)?, + }, + 0x31 => Operator::I64AtomicRmw16AndU { + memarg: self.read_memarg_of_align(1)?, + }, + 0x32 => Operator::I64AtomicRmw32AndU { + memarg: self.read_memarg_of_align(2)?, + }, + 0x33 => Operator::I32AtomicRmwOr { + memarg: self.read_memarg_of_align(2)?, + }, + 0x34 => Operator::I64AtomicRmwOr { + memarg: self.read_memarg_of_align(3)?, + }, + 0x35 => Operator::I32AtomicRmw8OrU { + memarg: self.read_memarg_of_align(0)?, + }, + 0x36 => Operator::I32AtomicRmw16OrU { + memarg: self.read_memarg_of_align(1)?, + }, + 0x37 => Operator::I64AtomicRmw8OrU { + memarg: self.read_memarg_of_align(0)?, + }, + 0x38 => Operator::I64AtomicRmw16OrU { + memarg: self.read_memarg_of_align(1)?, + }, + 0x39 => Operator::I64AtomicRmw32OrU { + memarg: self.read_memarg_of_align(2)?, + }, + 0x3a => Operator::I32AtomicRmwXor { + memarg: self.read_memarg_of_align(2)?, + }, + 0x3b => Operator::I64AtomicRmwXor { + memarg: self.read_memarg_of_align(3)?, + }, + 0x3c => Operator::I32AtomicRmw8XorU { + memarg: self.read_memarg_of_align(0)?, + }, + 0x3d => Operator::I32AtomicRmw16XorU { + memarg: self.read_memarg_of_align(1)?, + }, + 0x3e => Operator::I64AtomicRmw8XorU { + memarg: self.read_memarg_of_align(0)?, + }, + 0x3f => Operator::I64AtomicRmw16XorU { + memarg: self.read_memarg_of_align(1)?, + }, + 0x40 => Operator::I64AtomicRmw32XorU { + memarg: self.read_memarg_of_align(2)?, + }, + 0x41 => Operator::I32AtomicRmwXchg { + memarg: self.read_memarg_of_align(2)?, + }, + 0x42 => Operator::I64AtomicRmwXchg { + memarg: self.read_memarg_of_align(3)?, + }, + 0x43 => Operator::I32AtomicRmw8XchgU { + memarg: self.read_memarg_of_align(0)?, + }, + 0x44 => Operator::I32AtomicRmw16XchgU { + memarg: self.read_memarg_of_align(1)?, + }, + 0x45 => Operator::I64AtomicRmw8XchgU { + memarg: self.read_memarg_of_align(0)?, + }, + 0x46 => Operator::I64AtomicRmw16XchgU { + memarg: self.read_memarg_of_align(1)?, + }, + 0x47 => Operator::I64AtomicRmw32XchgU { + memarg: self.read_memarg_of_align(2)?, + }, + 0x48 => Operator::I32AtomicRmwCmpxchg { + memarg: self.read_memarg_of_align(2)?, + }, + 0x49 => Operator::I64AtomicRmwCmpxchg { + memarg: self.read_memarg_of_align(3)?, + }, + 0x4a => Operator::I32AtomicRmw8CmpxchgU { + memarg: self.read_memarg_of_align(0)?, + }, + 0x4b => Operator::I32AtomicRmw16CmpxchgU { + memarg: self.read_memarg_of_align(1)?, + }, + 0x4c => Operator::I64AtomicRmw8CmpxchgU { + memarg: self.read_memarg_of_align(0)?, + }, + 0x4d => Operator::I64AtomicRmw16CmpxchgU { + memarg: self.read_memarg_of_align(1)?, + }, + 0x4e => Operator::I64AtomicRmw32CmpxchgU { + memarg: self.read_memarg_of_align(2)?, + }, + + _ => { + return Err(BinaryReaderError::new( + format!("Unknown 0xfe subopcode: 0x{:x}", code), + self.original_position() - 1, + )); + } + }) + } + + fn read_blocktype(&mut self) -> Result<TypeOrFuncType> { + let position = self.position; + if let Ok(ty) = self.read_type() { + Ok(TypeOrFuncType::Type(ty)) + } else { + self.position = position; + let idx = self.read_var_s33()?; + if idx < 0 || idx > (std::u32::MAX as i64) { + return Err(BinaryReaderError::new("invalid function type", position)); + } + Ok(TypeOrFuncType::FuncType(idx as u32)) + } + } + + /// Reads the next available `Operator`. + /// # Errors + /// If `BinaryReader` has less bytes remaining than required to parse + /// the `Operator`. + pub fn read_operator(&mut self) -> Result<Operator<'a>> { + let code = self.read_u8()? as u8; + Ok(match code { + 0x00 => Operator::Unreachable, + 0x01 => Operator::Nop, + 0x02 => Operator::Block { + ty: self.read_blocktype()?, + }, + 0x03 => Operator::Loop { + ty: self.read_blocktype()?, + }, + 0x04 => Operator::If { + ty: self.read_blocktype()?, + }, + 0x05 => Operator::Else, + 0x0b => Operator::End, + 0x0c => Operator::Br { + relative_depth: self.read_var_u32()?, + }, + 0x0d => Operator::BrIf { + relative_depth: self.read_var_u32()?, + }, + 0x0e => Operator::BrTable { + table: self.read_br_table()?, + }, + 0x0f => Operator::Return, + 0x10 => Operator::Call { + function_index: self.read_var_u32()?, + }, + 0x11 => Operator::CallIndirect { + index: self.read_var_u32()?, + table_index: self.read_var_u32()?, + }, + 0x12 => Operator::ReturnCall { + function_index: self.read_var_u32()?, + }, + 0x13 => Operator::ReturnCallIndirect { + index: self.read_var_u32()?, + table_index: self.read_var_u32()?, + }, + 0x1a => Operator::Drop, + 0x1b => Operator::Select, + 0x1c => { + let results = self.read_var_u32()?; + if results != 1 { + return Err(BinaryReaderError::new( + "invalid result arity", + self.position, + )); + } + Operator::TypedSelect { + ty: self.read_type()?, + } + } + 0x20 => Operator::LocalGet { + local_index: self.read_var_u32()?, + }, + 0x21 => Operator::LocalSet { + local_index: self.read_var_u32()?, + }, + 0x22 => Operator::LocalTee { + local_index: self.read_var_u32()?, + }, + 0x23 => Operator::GlobalGet { + global_index: self.read_var_u32()?, + }, + 0x24 => Operator::GlobalSet { + global_index: self.read_var_u32()?, + }, + 0x25 => Operator::TableGet { + table: self.read_var_u32()?, + }, + 0x26 => Operator::TableSet { + table: self.read_var_u32()?, + }, + 0x28 => Operator::I32Load { + memarg: self.read_memarg()?, + }, + 0x29 => Operator::I64Load { + memarg: self.read_memarg()?, + }, + 0x2a => Operator::F32Load { + memarg: self.read_memarg()?, + }, + 0x2b => Operator::F64Load { + memarg: self.read_memarg()?, + }, + 0x2c => Operator::I32Load8S { + memarg: self.read_memarg()?, + }, + 0x2d => Operator::I32Load8U { + memarg: self.read_memarg()?, + }, + 0x2e => Operator::I32Load16S { + memarg: self.read_memarg()?, + }, + 0x2f => Operator::I32Load16U { + memarg: self.read_memarg()?, + }, + 0x30 => Operator::I64Load8S { + memarg: self.read_memarg()?, + }, + 0x31 => Operator::I64Load8U { + memarg: self.read_memarg()?, + }, + 0x32 => Operator::I64Load16S { + memarg: self.read_memarg()?, + }, + 0x33 => Operator::I64Load16U { + memarg: self.read_memarg()?, + }, + 0x34 => Operator::I64Load32S { + memarg: self.read_memarg()?, + }, + 0x35 => Operator::I64Load32U { + memarg: self.read_memarg()?, + }, + 0x36 => Operator::I32Store { + memarg: self.read_memarg()?, + }, + 0x37 => Operator::I64Store { + memarg: self.read_memarg()?, + }, + 0x38 => Operator::F32Store { + memarg: self.read_memarg()?, + }, + 0x39 => Operator::F64Store { + memarg: self.read_memarg()?, + }, + 0x3a => Operator::I32Store8 { + memarg: self.read_memarg()?, + }, + 0x3b => Operator::I32Store16 { + memarg: self.read_memarg()?, + }, + 0x3c => Operator::I64Store8 { + memarg: self.read_memarg()?, + }, + 0x3d => Operator::I64Store16 { + memarg: self.read_memarg()?, + }, + 0x3e => Operator::I64Store32 { + memarg: self.read_memarg()?, + }, + 0x3f => { + let (mem_byte, mem) = self.read_first_byte_and_var_u32()?; + Operator::MemorySize { mem_byte, mem } + } + 0x40 => { + let (mem_byte, mem) = self.read_first_byte_and_var_u32()?; + Operator::MemoryGrow { mem_byte, mem } + } + 0x41 => Operator::I32Const { + value: self.read_var_i32()?, + }, + 0x42 => Operator::I64Const { + value: self.read_var_i64()?, + }, + 0x43 => Operator::F32Const { + value: self.read_f32()?, + }, + 0x44 => Operator::F64Const { + value: self.read_f64()?, + }, + 0x45 => Operator::I32Eqz, + 0x46 => Operator::I32Eq, + 0x47 => Operator::I32Ne, + 0x48 => Operator::I32LtS, + 0x49 => Operator::I32LtU, + 0x4a => Operator::I32GtS, + 0x4b => Operator::I32GtU, + 0x4c => Operator::I32LeS, + 0x4d => Operator::I32LeU, + 0x4e => Operator::I32GeS, + 0x4f => Operator::I32GeU, + 0x50 => Operator::I64Eqz, + 0x51 => Operator::I64Eq, + 0x52 => Operator::I64Ne, + 0x53 => Operator::I64LtS, + 0x54 => Operator::I64LtU, + 0x55 => Operator::I64GtS, + 0x56 => Operator::I64GtU, + 0x57 => Operator::I64LeS, + 0x58 => Operator::I64LeU, + 0x59 => Operator::I64GeS, + 0x5a => Operator::I64GeU, + 0x5b => Operator::F32Eq, + 0x5c => Operator::F32Ne, + 0x5d => Operator::F32Lt, + 0x5e => Operator::F32Gt, + 0x5f => Operator::F32Le, + 0x60 => Operator::F32Ge, + 0x61 => Operator::F64Eq, + 0x62 => Operator::F64Ne, + 0x63 => Operator::F64Lt, + 0x64 => Operator::F64Gt, + 0x65 => Operator::F64Le, + 0x66 => Operator::F64Ge, + 0x67 => Operator::I32Clz, + 0x68 => Operator::I32Ctz, + 0x69 => Operator::I32Popcnt, + 0x6a => Operator::I32Add, + 0x6b => Operator::I32Sub, + 0x6c => Operator::I32Mul, + 0x6d => Operator::I32DivS, + 0x6e => Operator::I32DivU, + 0x6f => Operator::I32RemS, + 0x70 => Operator::I32RemU, + 0x71 => Operator::I32And, + 0x72 => Operator::I32Or, + 0x73 => Operator::I32Xor, + 0x74 => Operator::I32Shl, + 0x75 => Operator::I32ShrS, + 0x76 => Operator::I32ShrU, + 0x77 => Operator::I32Rotl, + 0x78 => Operator::I32Rotr, + 0x79 => Operator::I64Clz, + 0x7a => Operator::I64Ctz, + 0x7b => Operator::I64Popcnt, + 0x7c => Operator::I64Add, + 0x7d => Operator::I64Sub, + 0x7e => Operator::I64Mul, + 0x7f => Operator::I64DivS, + 0x80 => Operator::I64DivU, + 0x81 => Operator::I64RemS, + 0x82 => Operator::I64RemU, + 0x83 => Operator::I64And, + 0x84 => Operator::I64Or, + 0x85 => Operator::I64Xor, + 0x86 => Operator::I64Shl, + 0x87 => Operator::I64ShrS, + 0x88 => Operator::I64ShrU, + 0x89 => Operator::I64Rotl, + 0x8a => Operator::I64Rotr, + 0x8b => Operator::F32Abs, + 0x8c => Operator::F32Neg, + 0x8d => Operator::F32Ceil, + 0x8e => Operator::F32Floor, + 0x8f => Operator::F32Trunc, + 0x90 => Operator::F32Nearest, + 0x91 => Operator::F32Sqrt, + 0x92 => Operator::F32Add, + 0x93 => Operator::F32Sub, + 0x94 => Operator::F32Mul, + 0x95 => Operator::F32Div, + 0x96 => Operator::F32Min, + 0x97 => Operator::F32Max, + 0x98 => Operator::F32Copysign, + 0x99 => Operator::F64Abs, + 0x9a => Operator::F64Neg, + 0x9b => Operator::F64Ceil, + 0x9c => Operator::F64Floor, + 0x9d => Operator::F64Trunc, + 0x9e => Operator::F64Nearest, + 0x9f => Operator::F64Sqrt, + 0xa0 => Operator::F64Add, + 0xa1 => Operator::F64Sub, + 0xa2 => Operator::F64Mul, + 0xa3 => Operator::F64Div, + 0xa4 => Operator::F64Min, + 0xa5 => Operator::F64Max, + 0xa6 => Operator::F64Copysign, + 0xa7 => Operator::I32WrapI64, + 0xa8 => Operator::I32TruncF32S, + 0xa9 => Operator::I32TruncF32U, + 0xaa => Operator::I32TruncF64S, + 0xab => Operator::I32TruncF64U, + 0xac => Operator::I64ExtendI32S, + 0xad => Operator::I64ExtendI32U, + 0xae => Operator::I64TruncF32S, + 0xaf => Operator::I64TruncF32U, + 0xb0 => Operator::I64TruncF64S, + 0xb1 => Operator::I64TruncF64U, + 0xb2 => Operator::F32ConvertI32S, + 0xb3 => Operator::F32ConvertI32U, + 0xb4 => Operator::F32ConvertI64S, + 0xb5 => Operator::F32ConvertI64U, + 0xb6 => Operator::F32DemoteF64, + 0xb7 => Operator::F64ConvertI32S, + 0xb8 => Operator::F64ConvertI32U, + 0xb9 => Operator::F64ConvertI64S, + 0xba => Operator::F64ConvertI64U, + 0xbb => Operator::F64PromoteF32, + 0xbc => Operator::I32ReinterpretF32, + 0xbd => Operator::I64ReinterpretF64, + 0xbe => Operator::F32ReinterpretI32, + 0xbf => Operator::F64ReinterpretI64, + + 0xc0 => Operator::I32Extend8S, + 0xc1 => Operator::I32Extend16S, + 0xc2 => Operator::I64Extend8S, + 0xc3 => Operator::I64Extend16S, + 0xc4 => Operator::I64Extend32S, + + 0xd0 => Operator::RefNull { + ty: self.read_type()?, + }, + 0xd1 => Operator::RefIsNull, + 0xd2 => Operator::RefFunc { + function_index: self.read_var_u32()?, + }, + + 0xfc => self.read_0xfc_operator()?, + 0xfd => self.read_0xfd_operator()?, + 0xfe => self.read_0xfe_operator()?, + + _ => { + return Err(BinaryReaderError::new( + format!("Unknown opcode: 0x{:x}", code), + self.original_position() - 1, + )); + } + }) + } + + fn read_0xfc_operator(&mut self) -> Result<Operator<'a>> { + let code = self.read_var_u32()?; + Ok(match code { + 0x00 => Operator::I32TruncSatF32S, + 0x01 => Operator::I32TruncSatF32U, + 0x02 => Operator::I32TruncSatF64S, + 0x03 => Operator::I32TruncSatF64U, + 0x04 => Operator::I64TruncSatF32S, + 0x05 => Operator::I64TruncSatF32U, + 0x06 => Operator::I64TruncSatF64S, + 0x07 => Operator::I64TruncSatF64U, + + 0x08 => { + let segment = self.read_var_u32()?; + let mem = self.read_var_u32()?; + Operator::MemoryInit { segment, mem } + } + 0x09 => { + let segment = self.read_var_u32()?; + Operator::DataDrop { segment } + } + 0x0a => { + let dst = self.read_var_u32()?; + let src = self.read_var_u32()?; + Operator::MemoryCopy { src, dst } + } + 0x0b => { + let mem = self.read_var_u32()?; + Operator::MemoryFill { mem } + } + 0x0c => { + let segment = self.read_var_u32()?; + let table = self.read_var_u32()?; + Operator::TableInit { segment, table } + } + 0x0d => { + let segment = self.read_var_u32()?; + Operator::ElemDrop { segment } + } + 0x0e => { + let dst_table = self.read_var_u32()?; + let src_table = self.read_var_u32()?; + Operator::TableCopy { + src_table, + dst_table, + } + } + + 0x0f => { + let table = self.read_var_u32()?; + Operator::TableGrow { table } + } + 0x10 => { + let table = self.read_var_u32()?; + Operator::TableSize { table } + } + + 0x11 => { + let table = self.read_var_u32()?; + Operator::TableFill { table } + } + + _ => { + return Err(BinaryReaderError::new( + format!("Unknown 0xfc subopcode: 0x{:x}", code), + self.original_position() - 1, + )); + } + }) + } + + fn read_lane_index(&mut self, max: u32) -> Result<SIMDLaneIndex> { + let index = self.read_u8()?; + if index >= max { + return Err(BinaryReaderError::new( + "invalid lane index", + self.original_position() - 1, + )); + } + Ok(index as SIMDLaneIndex) + } + + fn read_v128(&mut self) -> Result<V128> { + let mut bytes = [0; 16]; + bytes.clone_from_slice(self.read_bytes(16)?); + Ok(V128(bytes)) + } + + fn read_0xfd_operator(&mut self) -> Result<Operator<'a>> { + let code = self.read_var_u32()?; + Ok(match code { + 0x00 => Operator::V128Load { + memarg: self.read_memarg()?, + }, + 0x01 => Operator::V128Load8x8S { + memarg: self.read_memarg_of_align(3)?, + }, + 0x02 => Operator::V128Load8x8U { + memarg: self.read_memarg_of_align(3)?, + }, + 0x03 => Operator::V128Load16x4S { + memarg: self.read_memarg_of_align(3)?, + }, + 0x04 => Operator::V128Load16x4U { + memarg: self.read_memarg_of_align(3)?, + }, + 0x05 => Operator::V128Load32x2S { + memarg: self.read_memarg_of_align(3)?, + }, + 0x06 => Operator::V128Load32x2U { + memarg: self.read_memarg_of_align(3)?, + }, + 0x07 => Operator::V128Load8Splat { + memarg: self.read_memarg_of_align(0)?, + }, + 0x08 => Operator::V128Load16Splat { + memarg: self.read_memarg_of_align(1)?, + }, + 0x09 => Operator::V128Load32Splat { + memarg: self.read_memarg_of_align(2)?, + }, + 0x0a => Operator::V128Load64Splat { + memarg: self.read_memarg_of_align(3)?, + }, + 0x0b => Operator::V128Store { + memarg: self.read_memarg()?, + }, + 0x0c => Operator::V128Const { + value: self.read_v128()?, + }, + 0x0d => { + let mut lanes = [0 as SIMDLaneIndex; 16]; + for lane in &mut lanes { + *lane = self.read_lane_index(32)? + } + Operator::I8x16Shuffle { lanes } + } + 0x0e => Operator::I8x16Swizzle, + 0x0f => Operator::I8x16Splat, + 0x10 => Operator::I16x8Splat, + 0x11 => Operator::I32x4Splat, + 0x12 => Operator::I64x2Splat, + 0x13 => Operator::F32x4Splat, + 0x14 => Operator::F64x2Splat, + 0x15 => Operator::I8x16ExtractLaneS { + lane: self.read_lane_index(16)?, + }, + 0x16 => Operator::I8x16ExtractLaneU { + lane: self.read_lane_index(16)?, + }, + 0x17 => Operator::I8x16ReplaceLane { + lane: self.read_lane_index(16)?, + }, + 0x18 => Operator::I16x8ExtractLaneS { + lane: self.read_lane_index(8)?, + }, + 0x19 => Operator::I16x8ExtractLaneU { + lane: self.read_lane_index(8)?, + }, + 0x1a => Operator::I16x8ReplaceLane { + lane: self.read_lane_index(8)?, + }, + 0x1b => Operator::I32x4ExtractLane { + lane: self.read_lane_index(4)?, + }, + 0x1c => Operator::I32x4ReplaceLane { + lane: self.read_lane_index(4)?, + }, + 0x1d => Operator::I64x2ExtractLane { + lane: self.read_lane_index(2)?, + }, + 0x1e => Operator::I64x2ReplaceLane { + lane: self.read_lane_index(2)?, + }, + 0x1f => Operator::F32x4ExtractLane { + lane: self.read_lane_index(4)?, + }, + 0x20 => Operator::F32x4ReplaceLane { + lane: self.read_lane_index(4)?, + }, + 0x21 => Operator::F64x2ExtractLane { + lane: self.read_lane_index(2)?, + }, + 0x22 => Operator::F64x2ReplaceLane { + lane: self.read_lane_index(2)?, + }, + 0x23 => Operator::I8x16Eq, + 0x24 => Operator::I8x16Ne, + 0x25 => Operator::I8x16LtS, + 0x26 => Operator::I8x16LtU, + 0x27 => Operator::I8x16GtS, + 0x28 => Operator::I8x16GtU, + 0x29 => Operator::I8x16LeS, + 0x2a => Operator::I8x16LeU, + 0x2b => Operator::I8x16GeS, + 0x2c => Operator::I8x16GeU, + 0x2d => Operator::I16x8Eq, + 0x2e => Operator::I16x8Ne, + 0x2f => Operator::I16x8LtS, + 0x30 => Operator::I16x8LtU, + 0x31 => Operator::I16x8GtS, + 0x32 => Operator::I16x8GtU, + 0x33 => Operator::I16x8LeS, + 0x34 => Operator::I16x8LeU, + 0x35 => Operator::I16x8GeS, + 0x36 => Operator::I16x8GeU, + 0x37 => Operator::I32x4Eq, + 0x38 => Operator::I32x4Ne, + 0x39 => Operator::I32x4LtS, + 0x3a => Operator::I32x4LtU, + 0x3b => Operator::I32x4GtS, + 0x3c => Operator::I32x4GtU, + 0x3d => Operator::I32x4LeS, + 0x3e => Operator::I32x4LeU, + 0x3f => Operator::I32x4GeS, + 0x40 => Operator::I32x4GeU, + 0x41 => Operator::F32x4Eq, + 0x42 => Operator::F32x4Ne, + 0x43 => Operator::F32x4Lt, + 0x44 => Operator::F32x4Gt, + 0x45 => Operator::F32x4Le, + 0x46 => Operator::F32x4Ge, + 0x47 => Operator::F64x2Eq, + 0x48 => Operator::F64x2Ne, + 0x49 => Operator::F64x2Lt, + 0x4a => Operator::F64x2Gt, + 0x4b => Operator::F64x2Le, + 0x4c => Operator::F64x2Ge, + 0x4d => Operator::V128Not, + 0x4e => Operator::V128And, + 0x4f => Operator::V128AndNot, + 0x50 => Operator::V128Or, + 0x51 => Operator::V128Xor, + 0x52 => Operator::V128Bitselect, + 0x60 => Operator::I8x16Abs, + 0x61 => Operator::I8x16Neg, + 0x62 => Operator::I8x16AnyTrue, + 0x63 => Operator::I8x16AllTrue, + 0x64 => Operator::I8x16Bitmask, + 0x65 => Operator::I8x16NarrowI16x8S, + 0x66 => Operator::I8x16NarrowI16x8U, + 0x6b => Operator::I8x16Shl, + 0x6c => Operator::I8x16ShrS, + 0x6d => Operator::I8x16ShrU, + 0x6e => Operator::I8x16Add, + 0x6f => Operator::I8x16AddSatS, + 0x70 => Operator::I8x16AddSatU, + 0x71 => Operator::I8x16Sub, + 0x72 => Operator::I8x16SubSatS, + 0x73 => Operator::I8x16SubSatU, + 0x76 => Operator::I8x16MinS, + 0x77 => Operator::I8x16MinU, + 0x78 => Operator::I8x16MaxS, + 0x79 => Operator::I8x16MaxU, + 0x7b => Operator::I8x16RoundingAverageU, + 0x80 => Operator::I16x8Abs, + 0x81 => Operator::I16x8Neg, + 0x82 => Operator::I16x8AnyTrue, + 0x83 => Operator::I16x8AllTrue, + 0x84 => Operator::I16x8Bitmask, + 0x85 => Operator::I16x8NarrowI32x4S, + 0x86 => Operator::I16x8NarrowI32x4U, + 0x87 => Operator::I16x8WidenLowI8x16S, + 0x88 => Operator::I16x8WidenHighI8x16S, + 0x89 => Operator::I16x8WidenLowI8x16U, + 0x8a => Operator::I16x8WidenHighI8x16U, + 0x8b => Operator::I16x8Shl, + 0x8c => Operator::I16x8ShrS, + 0x8d => Operator::I16x8ShrU, + 0x8e => Operator::I16x8Add, + 0x8f => Operator::I16x8AddSatS, + 0x90 => Operator::I16x8AddSatU, + 0x91 => Operator::I16x8Sub, + 0x92 => Operator::I16x8SubSatS, + 0x93 => Operator::I16x8SubSatU, + 0x95 => Operator::I16x8Mul, + 0x96 => Operator::I16x8MinS, + 0x97 => Operator::I16x8MinU, + 0x98 => Operator::I16x8MaxS, + 0x99 => Operator::I16x8MaxU, + 0x9b => Operator::I16x8RoundingAverageU, + 0xa0 => Operator::I32x4Abs, + 0xa1 => Operator::I32x4Neg, + 0xa2 => Operator::I32x4AnyTrue, + 0xa3 => Operator::I32x4AllTrue, + 0xa4 => Operator::I32x4Bitmask, + 0xa7 => Operator::I32x4WidenLowI16x8S, + 0xa8 => Operator::I32x4WidenHighI16x8S, + 0xa9 => Operator::I32x4WidenLowI16x8U, + 0xaa => Operator::I32x4WidenHighI16x8U, + 0xab => Operator::I32x4Shl, + 0xac => Operator::I32x4ShrS, + 0xad => Operator::I32x4ShrU, + 0xae => Operator::I32x4Add, + 0xb1 => Operator::I32x4Sub, + 0xb5 => Operator::I32x4Mul, + 0xb6 => Operator::I32x4MinS, + 0xb7 => Operator::I32x4MinU, + 0xb8 => Operator::I32x4MaxS, + 0xb9 => Operator::I32x4MaxU, + 0xba => Operator::I32x4DotI16x8S, + 0xc1 => Operator::I64x2Neg, + 0xcb => Operator::I64x2Shl, + 0xcc => Operator::I64x2ShrS, + 0xcd => Operator::I64x2ShrU, + 0xce => Operator::I64x2Add, + 0xd1 => Operator::I64x2Sub, + 0xd5 => Operator::I64x2Mul, + 0xd8 => Operator::F32x4Ceil, + 0xd9 => Operator::F32x4Floor, + 0xda => Operator::F32x4Trunc, + 0xdb => Operator::F32x4Nearest, + 0xdc => Operator::F64x2Ceil, + 0xdd => Operator::F64x2Floor, + 0xde => Operator::F64x2Trunc, + 0xdf => Operator::F64x2Nearest, + 0xe0 => Operator::F32x4Abs, + 0xe1 => Operator::F32x4Neg, + 0xe3 => Operator::F32x4Sqrt, + 0xe4 => Operator::F32x4Add, + 0xe5 => Operator::F32x4Sub, + 0xe6 => Operator::F32x4Mul, + 0xe7 => Operator::F32x4Div, + 0xe8 => Operator::F32x4Min, + 0xe9 => Operator::F32x4Max, + 0xea => Operator::F32x4PMin, + 0xeb => Operator::F32x4PMax, + 0xec => Operator::F64x2Abs, + 0xed => Operator::F64x2Neg, + 0xef => Operator::F64x2Sqrt, + 0xf0 => Operator::F64x2Add, + 0xf1 => Operator::F64x2Sub, + 0xf2 => Operator::F64x2Mul, + 0xf3 => Operator::F64x2Div, + 0xf4 => Operator::F64x2Min, + 0xf5 => Operator::F64x2Max, + 0xf6 => Operator::F64x2PMin, + 0xf7 => Operator::F64x2PMax, + 0xf8 => Operator::I32x4TruncSatF32x4S, + 0xf9 => Operator::I32x4TruncSatF32x4U, + 0xfa => Operator::F32x4ConvertI32x4S, + 0xfb => Operator::F32x4ConvertI32x4U, + 0xfc => Operator::V128Load32Zero { + memarg: self.read_memarg_of_align(2)?, + }, + 0xfd => Operator::V128Load64Zero { + memarg: self.read_memarg_of_align(3)?, + }, + _ => { + return Err(BinaryReaderError::new( + format!("Unknown 0xfd subopcode: 0x{:x}", code), + self.original_position() - 1, + )); + } + }) + } + + pub(crate) fn read_file_header(&mut self) -> Result<u32> { + let magic_number = self.read_bytes(4)?; + if magic_number != WASM_MAGIC_NUMBER { + return Err(BinaryReaderError::new( + "Bad magic number", + self.original_position() - 4, + )); + } + let version = self.read_u32()?; + if version != WASM_SUPPORTED_VERSION && version != WASM_EXPERIMENTAL_VERSION { + return Err(BinaryReaderError::new( + "Bad version number", + self.original_position() - 4, + )); + } + Ok(version) + } + + pub(crate) fn read_name_type(&mut self) -> Result<NameType> { + let code = self.read_var_u7()?; + match code { + 0 => Ok(NameType::Module), + 1 => Ok(NameType::Function), + 2 => Ok(NameType::Local), + _ => Err(BinaryReaderError::new( + "Invalid name type", + self.original_position() - 1, + )), + } + } + + pub(crate) fn read_linking_type(&mut self) -> Result<LinkingType> { + let ty = self.read_var_u32()?; + Ok(match ty { + 1 => LinkingType::StackPointer(self.read_var_u32()?), + _ => { + return Err(BinaryReaderError::new( + "Invalid linking type", + self.original_position() - 1, + )); + } + }) + } + + pub(crate) fn read_reloc_type(&mut self) -> Result<RelocType> { + let code = self.read_var_u7()?; + match code { + 0 => Ok(RelocType::FunctionIndexLEB), + 1 => Ok(RelocType::TableIndexSLEB), + 2 => Ok(RelocType::TableIndexI32), + 3 => Ok(RelocType::GlobalAddrLEB), + 4 => Ok(RelocType::GlobalAddrSLEB), + 5 => Ok(RelocType::GlobalAddrI32), + 6 => Ok(RelocType::TypeIndexLEB), + 7 => Ok(RelocType::GlobalIndexLEB), + _ => Err(BinaryReaderError::new( + "Invalid reloc type", + self.original_position() - 1, + )), + } + } + + pub(crate) fn skip_init_expr(&mut self) -> Result<()> { + // TODO add skip_operator() method and/or validate init_expr operators. + loop { + if let Operator::End = self.read_operator()? { + return Ok(()); + } + } + } +} + +impl<'a> BrTable<'a> { + /// Returns the number of `br_table` entries, not including the default + /// label + pub fn len(&self) -> usize { + self.cnt + } + + /// Returns whether `BrTable` doesn't have any labels apart from the default one. + pub fn is_empty(&self) -> bool { + self.len() == 0 + } + + /// Returns the list of targets that this `br_table` instruction will be + /// jumping to. + /// + /// This method will return an iterator which parses each target of this + /// `br_table` as well as the default target. The returned iterator will + /// yield `self.len() + 1` elements. + /// + /// Each iterator item is a tuple of `(u32, bool)`, where the first item is + /// the relative depth of the jump and the second item is `true` if the item + /// is the default label. You're guaranteed that `true` will only show up + /// for the final element of the iterator. + /// + /// #Examples + /// + /// ```rust + /// let buf = [0x0e, 0x02, 0x01, 0x02, 0x00]; + /// let mut reader = wasmparser::BinaryReader::new(&buf); + /// let op = reader.read_operator().unwrap(); + /// if let wasmparser::Operator::BrTable { table } = op { + /// let targets = table.targets().collect::<Result<Vec<_>, _>>().unwrap(); + /// assert_eq!(targets, [(1, false), (2, false), (0, true)]); + /// } + /// ``` + pub fn targets<'b>(&'b self) -> impl Iterator<Item = Result<(u32, bool)>> + 'b { + let mut reader = self.reader.clone(); + (0..self.cnt + 1).map(move |i| { + let label = reader.read_var_u32()?; + let ret = (label, i == self.cnt); + if ret.1 && !reader.eof() { + return Err(BinaryReaderError::new( + "trailing data in br_table", + reader.original_position(), + )); + } + Ok(ret) + }) + } +} + +impl fmt::Debug for BrTable<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut f = f.debug_struct("BrTable"); + f.field("count", &self.cnt); + match self.targets().collect::<Result<Vec<_>>>() { + Ok(targets) => { + f.field("targets", &targets); + } + Err(_) => { + f.field("reader", &self.reader); + } + } + f.finish() + } +} diff --git a/third_party/rust/wasmparser/src/lib.rs b/third_party/rust/wasmparser/src/lib.rs new file mode 100644 index 0000000000..33ab655ec3 --- /dev/null +++ b/third_party/rust/wasmparser/src/lib.rs @@ -0,0 +1,42 @@ +/* Copyright 2017 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//! A simple event-driven library for parsing WebAssembly binary files +//! (or streams). +//! +//! The parser library reports events as they happend and only stores +//! parsing information for a brief period of time, making it very fast +//! and memory-efficient. The event-driven model, however, has some drawbacks. +//! If you need random access to the entire WebAssembly data-structure, +//! this is not the right library for you. You could however, build such +//! a data-structure using this library. + +pub use crate::binary_reader::BinaryReader; +pub use crate::binary_reader::Range; + +pub use crate::module_resources::*; +pub use crate::parser::*; +pub use crate::primitives::*; +pub use crate::readers::*; +pub use crate::validator::*; + +mod binary_reader; +mod limits; +mod module_resources; +mod operators_validator; +mod parser; +mod primitives; +mod readers; +mod validator; diff --git a/third_party/rust/wasmparser/src/limits.rs b/third_party/rust/wasmparser/src/limits.rs new file mode 100644 index 0000000000..285f2a8cb3 --- /dev/null +++ b/third_party/rust/wasmparser/src/limits.rs @@ -0,0 +1,37 @@ +/* Copyright 2017 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// The following limits are imposed by wasmparser on WebAssembly modules. +// 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_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_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 +pub const MAX_WASM_FUNCTION_SIZE: usize = 128 * 1024; +pub const MAX_WASM_FUNCTION_LOCALS: usize = 50000; +pub const MAX_WASM_FUNCTION_PARAMS: usize = 1000; +pub const MAX_WASM_FUNCTION_RETURNS: usize = 1000; +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 = 100; +pub const MAX_WASM_MEMORIES: usize = 100; +pub const MAX_WASM_MODULES: usize = 1_000; +pub const MAX_WASM_INSTANCES: usize = 1_000; diff --git a/third_party/rust/wasmparser/src/module_resources.rs b/third_party/rust/wasmparser/src/module_resources.rs new file mode 100644 index 0000000000..bdf137ce12 --- /dev/null +++ b/third_party/rust/wasmparser/src/module_resources.rs @@ -0,0 +1,275 @@ +/* Copyright 2019 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use crate::{FuncType, GlobalType, MemoryType, TableType, Type}; +use std::ops::Range; + +/// Types that qualify as Wasm function types for validation purposes. +pub trait WasmFuncType { + /// Returns the number of input types. + fn len_inputs(&self) -> usize; + /// Returns the number of output types. + fn len_outputs(&self) -> usize; + /// Returns the type at given index if any. + /// + /// # Note + /// + /// The returned type may be wrapped by the user crate and thus + /// the actually returned type only has to be comparable to a Wasm type. + fn input_at(&self, at: u32) -> Option<Type>; + /// Returns the type at given index if any. + /// + /// # Note + /// + /// The returned type may be wrapped by the user crate and thus + /// the actually returned type only has to be comparable to a Wasm type. + fn output_at(&self, at: u32) -> Option<Type>; + + /// Returns the list of inputs as an iterator. + fn inputs(&self) -> WasmFuncTypeInputs<'_, Self> + where + Self: Sized, + { + WasmFuncTypeInputs { + func_type: self, + range: 0..self.len_inputs() as u32, + } + } + + /// Returns the list of outputs as an iterator. + fn outputs(&self) -> WasmFuncTypeOutputs<'_, Self> + where + Self: Sized, + { + WasmFuncTypeOutputs { + func_type: self, + range: 0..self.len_outputs() as u32, + } + } +} + +impl<T> WasmFuncType for &'_ T +where + T: ?Sized + WasmFuncType, +{ + fn len_inputs(&self) -> usize { + T::len_inputs(self) + } + fn len_outputs(&self) -> usize { + T::len_outputs(self) + } + fn input_at(&self, at: u32) -> Option<Type> { + T::input_at(self, at) + } + fn output_at(&self, at: u32) -> Option<Type> { + T::output_at(self, at) + } +} + +/// Iterator over the inputs of a Wasm function type. +pub struct WasmFuncTypeInputs<'a, T> { + /// The iterated-over function type. + func_type: &'a T, + /// The range we're iterating over. + range: Range<u32>, +} + +impl<T> Iterator for WasmFuncTypeInputs<'_, T> +where + T: WasmFuncType, +{ + type Item = crate::Type; + + fn next(&mut self) -> Option<Self::Item> { + self.range + .next() + .map(|i| self.func_type.input_at(i).unwrap()) + } + + fn size_hint(&self) -> (usize, Option<usize>) { + self.range.size_hint() + } +} + +impl<T> DoubleEndedIterator for WasmFuncTypeInputs<'_, T> +where + T: WasmFuncType, +{ + fn next_back(&mut self) -> Option<Self::Item> { + self.range + .next_back() + .map(|i| self.func_type.input_at(i).unwrap()) + } +} + +impl<T> ExactSizeIterator for WasmFuncTypeInputs<'_, T> +where + T: WasmFuncType, +{ + fn len(&self) -> usize { + self.range.len() + } +} + +impl<'a, T> Clone for WasmFuncTypeInputs<'a, T> { + fn clone(&self) -> WasmFuncTypeInputs<'a, T> { + WasmFuncTypeInputs { + func_type: self.func_type, + range: self.range.clone(), + } + } +} + +/// Iterator over the outputs of a Wasm function type. +pub struct WasmFuncTypeOutputs<'a, T> { + /// The iterated-over function type. + func_type: &'a T, + /// The range we're iterating over. + range: Range<u32>, +} + +impl<T> Iterator for WasmFuncTypeOutputs<'_, T> +where + T: WasmFuncType, +{ + type Item = crate::Type; + + fn next(&mut self) -> Option<Self::Item> { + self.range + .next() + .map(|i| self.func_type.output_at(i).unwrap()) + } + + fn size_hint(&self) -> (usize, Option<usize>) { + self.range.size_hint() + } +} + +impl<T> DoubleEndedIterator for WasmFuncTypeOutputs<'_, T> +where + T: WasmFuncType, +{ + fn next_back(&mut self) -> Option<Self::Item> { + self.range + .next_back() + .map(|i| self.func_type.output_at(i).unwrap()) + } +} + +impl<T> ExactSizeIterator for WasmFuncTypeOutputs<'_, T> +where + T: WasmFuncType, +{ + fn len(&self) -> usize { + self.range.len() + } +} + +impl<'a, T> Clone for WasmFuncTypeOutputs<'a, T> { + fn clone(&self) -> WasmFuncTypeOutputs<'a, T> { + WasmFuncTypeOutputs { + func_type: self.func_type, + range: self.range.clone(), + } + } +} + +/// Types that qualify as Wasm valiation database. +/// +/// # Note +/// +/// The `wasmparser` crate provides a builtin validation framework but allows +/// users of this crate to also feed the parsed Wasm into their own data +/// structure while parsing and also validate at the same time without +/// 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; + + /// Returns the table at given index if any. + fn table_at(&self, at: u32) -> Option<TableType>; + /// Returns the linear memory at given index. + fn memory_at(&self, at: u32) -> Option<MemoryType>; + /// Returns the global variable at given index. + fn global_at(&self, at: u32) -> Option<GlobalType>; + /// Returns the `FuncType` associated with the given type index. + fn func_type_at(&self, type_idx: u32) -> Option<&Self::FuncType>; + /// Returns the `FuncType` associated with the given function index. + fn type_of_function(&self, func_idx: u32) -> Option<&Self::FuncType>; + /// Returns the element type at the given index. + fn element_type_at(&self, at: u32) -> Option<Type>; + + /// Returns the number of elements. + fn element_count(&self) -> u32; + /// Returns the number of bytes in the Wasm data section. + fn data_count(&self) -> u32; + /// Returns whether the function index is referenced in the module anywhere + /// outside of the start/function sections. + fn is_function_referenced(&self, idx: u32) -> bool; +} + +impl<T> WasmModuleResources for &'_ T +where + T: ?Sized + WasmModuleResources, +{ + type FuncType = T::FuncType; + + fn table_at(&self, at: u32) -> Option<TableType> { + T::table_at(self, at) + } + fn memory_at(&self, at: u32) -> Option<MemoryType> { + T::memory_at(self, at) + } + fn global_at(&self, at: u32) -> Option<GlobalType> { + T::global_at(self, at) + } + fn func_type_at(&self, at: u32) -> Option<&Self::FuncType> { + T::func_type_at(self, at) + } + fn type_of_function(&self, func_idx: u32) -> Option<&Self::FuncType> { + T::type_of_function(self, func_idx) + } + fn element_type_at(&self, at: u32) -> Option<Type> { + T::element_type_at(self, at) + } + + fn element_count(&self) -> u32 { + T::element_count(self) + } + fn data_count(&self) -> u32 { + T::data_count(self) + } + fn is_function_referenced(&self, idx: u32) -> bool { + T::is_function_referenced(self, idx) + } +} + +impl WasmFuncType for FuncType { + fn len_inputs(&self) -> usize { + self.params.len() + } + + fn len_outputs(&self) -> usize { + self.returns.len() + } + + fn input_at(&self, at: u32) -> Option<Type> { + self.params.get(at as usize).copied() + } + + fn output_at(&self, at: u32) -> Option<Type> { + self.returns.get(at as usize).copied() + } +} diff --git a/third_party/rust/wasmparser/src/operators_validator.rs b/third_party/rust/wasmparser/src/operators_validator.rs new file mode 100644 index 0000000000..adc71fd213 --- /dev/null +++ b/third_party/rust/wasmparser/src/operators_validator.rs @@ -0,0 +1,1857 @@ +/* Copyright 2019 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// The basic validation algorithm here is copied from the "Validation +// Algorithm" section of the WebAssembly specification - +// https://webassembly.github.io/spec/core/appendix/algorithm.html. +// +// That algorithm is followed pretty closely here, namely `push_operand`, +// `pop_operand`, `push_ctrl`, and `pop_ctrl`. If anything here is a bit +// confusing it's recomended to read over that section to see how it maps to +// the various methods here. + +use crate::limits::MAX_WASM_FUNCTION_LOCALS; +use crate::primitives::{MemoryImmediate, Operator, SIMDLaneIndex, Type, TypeOrFuncType}; +use crate::{BinaryReaderError, Result, WasmFeatures, WasmFuncType, WasmModuleResources}; + +/// A wrapper around a `BinaryReaderError` where the inner error's offset is a +/// temporary placeholder value. This can be converted into a proper +/// `BinaryReaderError` via the `set_offset` method, which replaces the +/// placeholder offset with an actual offset. +pub(crate) struct OperatorValidatorError(pub(crate) BinaryReaderError); + +/// Create an `OperatorValidatorError` with a format string. +macro_rules! format_op_err { + ( $( $arg:expr ),* $(,)* ) => { + OperatorValidatorError::new(format!( $( $arg ),* )) + } +} + +/// Early return an `Err(OperatorValidatorError)` with a format string. +macro_rules! bail_op_err { + ( $( $arg:expr ),* $(,)* ) => { + return Err(format_op_err!( $( $arg ),* )); + } +} + +impl OperatorValidatorError { + /// Create a new `OperatorValidatorError` with a placeholder offset. + pub(crate) fn new(message: impl Into<String>) -> Self { + let offset = std::usize::MAX; + let e = BinaryReaderError::new(message, offset); + OperatorValidatorError(e) + } + + /// Convert this `OperatorValidatorError` into a `BinaryReaderError` by + /// supplying an actual offset to replace the internal placeholder offset. + pub(crate) fn set_offset(mut self, offset: usize) -> BinaryReaderError { + debug_assert_eq!(self.0.inner.offset, std::usize::MAX); + self.0.inner.offset = offset; + self.0 + } +} + +type OperatorValidatorResult<T> = std::result::Result<T, OperatorValidatorError>; + +pub(crate) struct OperatorValidator { + // The total number of locals that this function contains + num_locals: u32, + // This is a "compressed" list of locals for this function. The list of + // locals are represented as a list of tuples. The second element is the + // type of the local, and the first element is monotonically increasing as + // you visit elements of this list. The first element is the maximum index + // of the local, after the previous index, of the type specified. + // + // This allows us to do a binary search on the list for a local's index for + // `local.{get,set,tee}`. We do a binary search for the index desired, and + // it either lies in a "hole" where the maximum index is specified later, + // or it's at the end of the list meaning it's out of bounds. + locals: Vec<(u32, Type)>, + + // The `operands` is the current type stack, and the `control` list is the + // list of blocks that we're currently in. + pub(crate) operands: Vec<Option<Type>>, + control: Vec<Frame>, + + // This is a list of flags for wasm features which are used to gate various + // instructions. + features: WasmFeatures, +} + +// This structure corresponds to `ctrl_frame` as specified at in the validation +// appendix of the wasm spec +struct Frame { + // Indicator for what kind of instruction pushed this frame. + kind: FrameKind, + // The type signature of this frame, represented as a singular return type + // or a type index pointing into the module's types. + block_type: TypeOrFuncType, + // The index, below which, this frame cannot modify the operand stack. + height: usize, + // Whether this frame is unreachable so far. + unreachable: bool, +} + +#[derive(PartialEq, Copy, Clone)] +enum FrameKind { + Block, + If, + Else, + Loop, +} + +impl OperatorValidator { + pub fn new( + ty: u32, + offset: usize, + features: &WasmFeatures, + resources: &impl WasmModuleResources, + ) -> Result<OperatorValidator> { + let locals = func_type_at(resources, ty) + .map_err(|e| e.set_offset(offset))? + .inputs() + .enumerate() + .map(|(i, ty)| (i as u32, ty)) + .collect::<Vec<_>>(); + Ok(OperatorValidator { + num_locals: locals.len() as u32, + locals, + operands: Vec::new(), + control: vec![Frame { + kind: FrameKind::Block, + block_type: TypeOrFuncType::FuncType(ty), + height: 0, + unreachable: false, + }], + features: *features, + }) + } + + pub fn define_locals(&mut self, offset: usize, count: u32, ty: Type) -> Result<()> { + self.features + .check_value_type(ty) + .map_err(|e| BinaryReaderError::new(e, offset))?; + if count == 0 { + return Ok(()); + } + match self.num_locals.checked_add(count) { + Some(n) => self.num_locals = n, + None => return Err(BinaryReaderError::new("locals overflow", offset)), + } + if self.num_locals > (MAX_WASM_FUNCTION_LOCALS as u32) { + return Err(BinaryReaderError::new("locals exceed maximum", offset)); + } + self.locals.push((self.num_locals - 1, ty)); + Ok(()) + } + + /// Fetches the type for the local at `idx`, returning an error if it's out + /// of bounds. + fn local(&self, idx: u32) -> OperatorValidatorResult<Type> { + match self.locals.binary_search_by_key(&idx, |(idx, _)| *idx) { + // If this index would be inserted at the end of the list, then the + // index is out of bounds and we return an error. + Err(i) if i == self.locals.len() => { + bail_op_err!("unknown local {}: local index out of bounds", idx) + } + // If `Ok` is returned we found the index exactly, or if `Err` is + // returned the position is the one which is the least index + // greater thatn `idx`, which is still the type of `idx` according + // to our "compressed" representation. In both cases we access the + // list at index `i`. + Ok(i) | Err(i) => Ok(self.locals[i].1), + } + } + + /// Pushes a type onto the operand stack. + /// + /// This is used by instructions to represent a value that is pushed to the + /// operand stack. This can fail, but only if `Type` is feature gated. + /// Otherwise the push operation always succeeds. + fn push_operand(&mut self, ty: Type) -> OperatorValidatorResult<()> { + self.features + .check_value_type(ty) + .map_err(OperatorValidatorError::new)?; + self.operands.push(Some(ty)); + Ok(()) + } + + /// Attempts to pop a type from the operand stack. + /// + /// This function is used to remove types from the operand stack. The + /// `expected` argument can be used to indicate that a type is required, or + /// simply that something is needed to be popped. + /// + /// If `expected` is `Some(T)` then this will be guaranteed to return + /// `Some(T)`, and it will only return success if the current block is + /// unreachable or if `T` was found at the top of the operand stack. + /// + /// If `expected` is `None` then it indicates that something must be on the + /// operand stack, but it doesn't matter what's on the operand stack. This + /// is useful for polymorphic instructions like `select`. + /// + /// If `Some(T)` is returned then `T` was popped from the operand stack and + /// matches `expected`. If `None` is returned then it means that `None` was + /// expected and a type was successfully popped, but its exact type is + /// indeterminate because the current block is unreachable. + fn pop_operand(&mut self, expected: Option<Type>) -> OperatorValidatorResult<Option<Type>> { + let control = self.control.last().unwrap(); + let actual = if self.operands.len() == control.height { + if control.unreachable { + None + } else { + let desc = match expected { + Some(ty) => ty_to_str(ty), + None => "a type", + }; + bail_op_err!("type mismatch: expected {} but nothing on stack", desc) + } + } else { + self.operands.pop().unwrap() + }; + let actual_ty = match actual { + Some(ty) => ty, + None => return Ok(expected), + }; + let expected_ty = match expected { + Some(ty) => ty, + None => return Ok(actual), + }; + if actual_ty != expected_ty { + bail_op_err!( + "type mismatch: expected {}, found {}", + ty_to_str(expected_ty), + ty_to_str(actual_ty) + ) + } else { + Ok(actual) + } + } + + /// Flags the current control frame as unreachable, additionally truncating + /// the currently active operand stack. + fn unreachable(&mut self) { + let control = self.control.last_mut().unwrap(); + self.operands.truncate(control.height); + control.unreachable = true; + } + + /// Pushes a new frame onto the control stack. + /// + /// This operation is used when entering a new block such as an if, loop, + /// or block itself. The `kind` of block is specified which indicates how + /// breaks interact with this block's type. Additionally the type signature + /// of the block is specified by `ty`. + fn push_ctrl( + &mut self, + kind: FrameKind, + ty: TypeOrFuncType, + resources: &impl WasmModuleResources, + ) -> OperatorValidatorResult<()> { + // Push a new frame which has a snapshot of the height of the current + // operand stack. + self.control.push(Frame { + kind, + block_type: ty, + height: self.operands.len(), + unreachable: false, + }); + // All of the parameters are now also available in this control frame, + // so we push them here in order. + for ty in params(ty, resources)? { + self.push_operand(ty)?; + } + Ok(()) + } + + /// Pops a frame from the control stack. + /// + /// This function is used when exiting a block and leaves a block scope. + /// Internally this will validate that blocks have the correct result type. + fn pop_ctrl(&mut self, resources: &impl WasmModuleResources) -> OperatorValidatorResult<Frame> { + // Read the expected type and expected height of the operand stack the + // end of the frame. + let frame = self.control.last().unwrap(); + let ty = frame.block_type; + let height = frame.height; + + // Pop all the result types, in reverse order, from the operand stack. + // These types will, possibly, be transferred to the next frame. + for ty in results(ty, resources)?.rev() { + self.pop_operand(Some(ty))?; + } + + // Make sure that the operand stack has returned to is original + // height... + if self.operands.len() != height { + bail_op_err!("type mismatch: values remaining on stack at end of block"); + } + + // And then we can remove it! + Ok(self.control.pop().unwrap()) + } + + /// Validates a relative jump to the `depth` specified. + /// + /// Returns the type signature of the block that we're jumping to as well + /// as the kind of block if the jump is valid. Otherwise returns an error. + fn jump(&self, depth: u32) -> OperatorValidatorResult<(TypeOrFuncType, FrameKind)> { + match (self.control.len() - 1).checked_sub(depth as usize) { + Some(i) => { + let frame = &self.control[i]; + Ok((frame.block_type, frame.kind)) + } + None => bail_op_err!("unknown label: branch depth too large"), + } + } + + /// Validates that `memory_index` is valid in this module, and returns the + /// type of address used to index the memory specified. + fn check_memory_index( + &self, + memory_index: u32, + resources: impl WasmModuleResources, + ) -> OperatorValidatorResult<Type> { + if memory_index > 0 && !self.features.multi_memory { + return Err(OperatorValidatorError::new( + "multi-memory support is not enabled", + )); + } + match resources.memory_at(memory_index) { + Some(mem) => Ok(mem.index_type()), + None => bail_op_err!("unknown memory {}", memory_index), + } + } + + /// Validates a `memarg for alignment and such (also the memory it + /// references), and returns the type of index used to address the memory. + fn check_memarg( + &self, + memarg: MemoryImmediate, + max_align: u8, + resources: impl WasmModuleResources, + ) -> OperatorValidatorResult<Type> { + let index_ty = self.check_memory_index(memarg.memory, resources)?; + let align = memarg.align; + if align > max_align { + return Err(OperatorValidatorError::new( + "alignment must not be larger than natural", + )); + } + Ok(index_ty) + } + + #[cfg(feature = "deterministic")] + fn check_non_deterministic_enabled(&self) -> OperatorValidatorResult<()> { + if !self.features.deterministic_only { + return Err(OperatorValidatorError::new( + "deterministic_only support is not enabled", + )); + } + Ok(()) + } + + #[inline(always)] + #[cfg(not(feature = "deterministic"))] + fn check_non_deterministic_enabled(&self) -> OperatorValidatorResult<()> { + Ok(()) + } + + fn check_threads_enabled(&self) -> OperatorValidatorResult<()> { + if !self.features.threads { + return Err(OperatorValidatorError::new( + "threads support is not enabled", + )); + } + Ok(()) + } + + fn check_reference_types_enabled(&self) -> OperatorValidatorResult<()> { + if !self.features.reference_types { + return Err(OperatorValidatorError::new( + "reference types support is not enabled", + )); + } + Ok(()) + } + + fn check_simd_enabled(&self) -> OperatorValidatorResult<()> { + if !self.features.simd { + return Err(OperatorValidatorError::new("SIMD support is not enabled")); + } + Ok(()) + } + + fn check_bulk_memory_enabled(&self) -> OperatorValidatorResult<()> { + if !self.features.bulk_memory { + return Err(OperatorValidatorError::new( + "bulk memory support is not enabled", + )); + } + Ok(()) + } + + fn check_shared_memarg_wo_align( + &self, + memarg: MemoryImmediate, + resources: impl WasmModuleResources, + ) -> OperatorValidatorResult<Type> { + self.check_memory_index(memarg.memory, resources) + } + + fn check_simd_lane_index(&self, index: SIMDLaneIndex, max: u8) -> OperatorValidatorResult<()> { + if index >= max { + return Err(OperatorValidatorError::new("SIMD index out of bounds")); + } + Ok(()) + } + + /// Validates a block type, primarily with various in-flight proposals. + fn check_block_type( + &self, + ty: TypeOrFuncType, + resources: impl WasmModuleResources, + ) -> OperatorValidatorResult<()> { + match ty { + TypeOrFuncType::Type(Type::EmptyBlockType) + | TypeOrFuncType::Type(Type::I32) + | TypeOrFuncType::Type(Type::I64) + | TypeOrFuncType::Type(Type::F32) + | TypeOrFuncType::Type(Type::F64) => Ok(()), + TypeOrFuncType::Type(Type::ExternRef) | TypeOrFuncType::Type(Type::FuncRef) => { + self.check_reference_types_enabled() + } + TypeOrFuncType::Type(Type::V128) => self.check_simd_enabled(), + TypeOrFuncType::FuncType(idx) => { + let ty = func_type_at(&resources, idx)?; + if !self.features.multi_value { + if ty.len_outputs() > 1 { + return Err(OperatorValidatorError::new( + "blocks, loops, and ifs may only return at most one \ + value when multi-value is not enabled", + )); + } + if ty.len_inputs() > 0 { + return Err(OperatorValidatorError::new( + "blocks, loops, and ifs accept no parameters \ + when multi-value is not enabled", + )); + } + } + Ok(()) + } + _ => Err(OperatorValidatorError::new("invalid block return type")), + } + } + + /// Validates a `call` instruction, ensuring that the function index is + /// in-bounds and the right types are on the stack to call the function. + fn check_call( + &mut self, + function_index: u32, + resources: &impl WasmModuleResources, + ) -> OperatorValidatorResult<()> { + let ty = match resources.type_of_function(function_index) { + Some(i) => i, + None => { + bail_op_err!( + "unknown function {}: function index out of bounds", + function_index + ); + } + }; + for ty in ty.inputs().rev() { + self.pop_operand(Some(ty))?; + } + for ty in ty.outputs() { + self.push_operand(ty)?; + } + Ok(()) + } + + /// Validates a call to an indirect function, very similar to `check_call`. + fn check_call_indirect( + &mut self, + index: u32, + table_index: u32, + resources: &impl WasmModuleResources, + ) -> OperatorValidatorResult<()> { + match resources.table_at(table_index) { + None => { + return Err(OperatorValidatorError::new( + "unknown table: table index out of bounds", + )); + } + Some(tab) => { + if tab.element_type != Type::FuncRef { + return Err(OperatorValidatorError::new( + "indirect calls must go through a table of funcref", + )); + } + } + } + let ty = func_type_at(&resources, index)?; + self.pop_operand(Some(Type::I32))?; + for ty in ty.inputs().rev() { + self.pop_operand(Some(ty))?; + } + for ty in ty.outputs() { + self.push_operand(ty)?; + } + Ok(()) + } + + /// Validates a `return` instruction, popping types from the operand + /// stack that the function needs. + fn check_return( + &mut self, + resources: &impl WasmModuleResources, + ) -> OperatorValidatorResult<()> { + for ty in results(self.control[0].block_type, resources)?.rev() { + self.pop_operand(Some(ty))?; + } + self.unreachable(); + Ok(()) + } + + pub fn process_operator( + &mut self, + operator: &Operator, + resources: &impl WasmModuleResources, + ) -> OperatorValidatorResult<()> { + if self.control.len() == 0 { + bail_op_err!("operators remaining after end of function"); + } + match *operator { + Operator::Nop => {} + + // Note that the handling of these control flow operators are the + // same as specified in the "Validation Algorithm" appendix of the + // online wasm specification (referenced at the top of this module). + Operator::Unreachable => self.unreachable(), + Operator::Block { ty } => { + self.check_block_type(ty, resources)?; + for ty in params(ty, resources)?.rev() { + self.pop_operand(Some(ty))?; + } + self.push_ctrl(FrameKind::Block, ty, resources)?; + } + Operator::Loop { ty } => { + self.check_block_type(ty, resources)?; + for ty in params(ty, resources)?.rev() { + self.pop_operand(Some(ty))?; + } + self.push_ctrl(FrameKind::Loop, ty, resources)?; + } + Operator::If { ty } => { + self.check_block_type(ty, resources)?; + self.pop_operand(Some(Type::I32))?; + for ty in params(ty, resources)?.rev() { + self.pop_operand(Some(ty))?; + } + self.push_ctrl(FrameKind::If, ty, resources)?; + } + Operator::Else => { + let frame = self.pop_ctrl(resources)?; + if frame.kind != FrameKind::If { + bail_op_err!("else found outside of an `if` block"); + } + self.push_ctrl(FrameKind::Else, frame.block_type, resources)?; + } + Operator::End => { + let mut frame = self.pop_ctrl(resources)?; + + // Note that this `if` isn't included in the appendix right + // now, but it's used to allow for `if` statements that are + // missing an `else` block which have the same parameter/return + // types on the block (since that's valid). + if frame.kind == FrameKind::If { + self.push_ctrl(FrameKind::Else, frame.block_type, resources)?; + frame = self.pop_ctrl(resources)?; + } + for ty in results(frame.block_type, resources)? { + self.push_operand(ty)?; + } + } + Operator::Br { relative_depth } => { + let (ty, kind) = self.jump(relative_depth)?; + for ty in label_types(ty, resources, kind)?.rev() { + self.pop_operand(Some(ty))?; + } + self.unreachable(); + } + Operator::BrIf { relative_depth } => { + self.pop_operand(Some(Type::I32))?; + let (ty, kind) = self.jump(relative_depth)?; + for ty in label_types(ty, resources, kind)?.rev() { + self.pop_operand(Some(ty))?; + } + for ty in label_types(ty, resources, kind)? { + self.push_operand(ty)?; + } + } + Operator::BrTable { ref table } => { + self.pop_operand(Some(Type::I32))?; + let mut label = None; + for element in table.targets() { + let (relative_depth, _is_default) = element.map_err(|mut e| { + e.inner.offset = usize::max_value(); + OperatorValidatorError(e) + })?; + let block = self.jump(relative_depth)?; + match label { + None => label = Some(block), + Some(prev) => { + let a = label_types(block.0, resources, block.1)?; + let b = label_types(prev.0, resources, prev.1)?; + if a.ne(b) { + bail_op_err!( + "type mismatch: br_table target labels have different types" + ); + } + } + } + } + let (ty, kind) = label.unwrap(); + for ty in label_types(ty, resources, kind)?.rev() { + self.pop_operand(Some(ty))?; + } + self.unreachable(); + } + Operator::Return => self.check_return(resources)?, + Operator::Call { function_index } => self.check_call(function_index, resources)?, + Operator::ReturnCall { function_index } => { + if !self.features.tail_call { + return Err(OperatorValidatorError::new( + "tail calls support is not enabled", + )); + } + self.check_call(function_index, resources)?; + self.check_return(resources)?; + } + Operator::CallIndirect { index, table_index } => { + self.check_call_indirect(index, table_index, resources)? + } + Operator::ReturnCallIndirect { index, table_index } => { + if !self.features.tail_call { + return Err(OperatorValidatorError::new( + "tail calls support is not enabled", + )); + } + self.check_call_indirect(index, table_index, resources)?; + self.check_return(resources)?; + } + Operator::Drop => { + self.pop_operand(None)?; + } + Operator::Select => { + self.pop_operand(Some(Type::I32))?; + let ty = self.pop_operand(None)?; + match self.pop_operand(ty)? { + ty @ Some(Type::I32) + | ty @ Some(Type::I64) + | ty @ Some(Type::F32) + | ty @ Some(Type::F64) => self.operands.push(ty), + ty @ Some(Type::V128) => { + self.check_simd_enabled()?; + self.operands.push(ty) + } + None => self.operands.push(None), + Some(_) => bail_op_err!("type mismatch: select only takes integral types"), + } + } + Operator::TypedSelect { ty } => { + self.pop_operand(Some(Type::I32))?; + self.pop_operand(Some(ty))?; + self.pop_operand(Some(ty))?; + self.push_operand(ty)?; + } + Operator::LocalGet { local_index } => { + let ty = self.local(local_index)?; + self.push_operand(ty)?; + } + Operator::LocalSet { local_index } => { + let ty = self.local(local_index)?; + self.pop_operand(Some(ty))?; + } + Operator::LocalTee { local_index } => { + let ty = self.local(local_index)?; + self.pop_operand(Some(ty))?; + self.push_operand(ty)?; + } + Operator::GlobalGet { global_index } => { + if let Some(ty) = resources.global_at(global_index) { + self.push_operand(ty.content_type)?; + } else { + return Err(OperatorValidatorError::new( + "unknown global: global index out of bounds", + )); + }; + } + Operator::GlobalSet { global_index } => { + if let Some(ty) = resources.global_at(global_index) { + if !ty.mutable { + return Err(OperatorValidatorError::new( + "global is immutable: cannot modify it with `global.set`", + )); + } + self.pop_operand(Some(ty.content_type))?; + } else { + return Err(OperatorValidatorError::new( + "unknown global: global index out of bounds", + )); + }; + } + Operator::I32Load { memarg } => { + let ty = self.check_memarg(memarg, 2, resources)?; + self.pop_operand(Some(ty))?; + self.push_operand(Type::I32)?; + } + Operator::I64Load { memarg } => { + let ty = self.check_memarg(memarg, 3, resources)?; + self.pop_operand(Some(ty))?; + self.push_operand(Type::I64)?; + } + Operator::F32Load { memarg } => { + self.check_non_deterministic_enabled()?; + let ty = self.check_memarg(memarg, 2, resources)?; + self.pop_operand(Some(ty))?; + self.push_operand(Type::F32)?; + } + Operator::F64Load { memarg } => { + self.check_non_deterministic_enabled()?; + let ty = self.check_memarg(memarg, 3, resources)?; + self.pop_operand(Some(ty))?; + self.push_operand(Type::F64)?; + } + Operator::I32Load8S { memarg } | Operator::I32Load8U { memarg } => { + let ty = self.check_memarg(memarg, 0, resources)?; + self.pop_operand(Some(ty))?; + self.push_operand(Type::I32)?; + } + Operator::I32Load16S { memarg } | Operator::I32Load16U { memarg } => { + let ty = self.check_memarg(memarg, 1, resources)?; + self.pop_operand(Some(ty))?; + self.push_operand(Type::I32)?; + } + Operator::I64Load8S { memarg } | Operator::I64Load8U { memarg } => { + let ty = self.check_memarg(memarg, 0, resources)?; + self.pop_operand(Some(ty))?; + self.push_operand(Type::I64)?; + } + Operator::I64Load16S { memarg } | Operator::I64Load16U { memarg } => { + let ty = self.check_memarg(memarg, 1, resources)?; + self.pop_operand(Some(ty))?; + self.push_operand(Type::I64)?; + } + Operator::I64Load32S { memarg } | Operator::I64Load32U { memarg } => { + let ty = self.check_memarg(memarg, 2, resources)?; + self.pop_operand(Some(ty))?; + self.push_operand(Type::I64)?; + } + Operator::I32Store { memarg } => { + let ty = self.check_memarg(memarg, 2, resources)?; + self.pop_operand(Some(Type::I32))?; + self.pop_operand(Some(ty))?; + } + Operator::I64Store { memarg } => { + let ty = self.check_memarg(memarg, 3, resources)?; + self.pop_operand(Some(Type::I64))?; + self.pop_operand(Some(ty))?; + } + Operator::F32Store { memarg } => { + self.check_non_deterministic_enabled()?; + let ty = self.check_memarg(memarg, 2, resources)?; + self.pop_operand(Some(Type::F32))?; + self.pop_operand(Some(ty))?; + } + Operator::F64Store { memarg } => { + self.check_non_deterministic_enabled()?; + let ty = self.check_memarg(memarg, 3, resources)?; + self.pop_operand(Some(Type::F64))?; + self.pop_operand(Some(ty))?; + } + Operator::I32Store8 { memarg } => { + let ty = self.check_memarg(memarg, 0, resources)?; + self.pop_operand(Some(Type::I32))?; + self.pop_operand(Some(ty))?; + } + Operator::I32Store16 { memarg } => { + let ty = self.check_memarg(memarg, 1, resources)?; + self.pop_operand(Some(Type::I32))?; + self.pop_operand(Some(ty))?; + } + Operator::I64Store8 { memarg } => { + let ty = self.check_memarg(memarg, 0, resources)?; + self.pop_operand(Some(Type::I64))?; + self.pop_operand(Some(ty))?; + } + Operator::I64Store16 { memarg } => { + let ty = self.check_memarg(memarg, 1, resources)?; + self.pop_operand(Some(Type::I64))?; + self.pop_operand(Some(ty))?; + } + Operator::I64Store32 { memarg } => { + let ty = self.check_memarg(memarg, 2, resources)?; + self.pop_operand(Some(Type::I64))?; + self.pop_operand(Some(ty))?; + } + Operator::MemorySize { mem, mem_byte } => { + if mem_byte != 0 && !self.features.multi_memory { + return Err(OperatorValidatorError::new("multi-memory not enabled")); + } + let index_ty = self.check_memory_index(mem, resources)?; + self.push_operand(index_ty)?; + } + Operator::MemoryGrow { mem, mem_byte } => { + if mem_byte != 0 && !self.features.multi_memory { + return Err(OperatorValidatorError::new("multi-memory not enabled")); + } + let index_ty = self.check_memory_index(mem, resources)?; + self.pop_operand(Some(index_ty))?; + self.push_operand(index_ty)?; + } + Operator::I32Const { .. } => self.push_operand(Type::I32)?, + Operator::I64Const { .. } => self.push_operand(Type::I64)?, + Operator::F32Const { .. } => { + self.check_non_deterministic_enabled()?; + self.push_operand(Type::F32)?; + } + Operator::F64Const { .. } => { + self.check_non_deterministic_enabled()?; + self.push_operand(Type::F64)?; + } + Operator::I32Eqz => { + self.pop_operand(Some(Type::I32))?; + self.push_operand(Type::I32)?; + } + Operator::I32Eq + | Operator::I32Ne + | Operator::I32LtS + | Operator::I32LtU + | Operator::I32GtS + | Operator::I32GtU + | Operator::I32LeS + | Operator::I32LeU + | Operator::I32GeS + | Operator::I32GeU => { + self.pop_operand(Some(Type::I32))?; + self.pop_operand(Some(Type::I32))?; + self.push_operand(Type::I32)?; + } + Operator::I64Eqz => { + self.pop_operand(Some(Type::I64))?; + self.push_operand(Type::I32)?; + } + Operator::I64Eq + | Operator::I64Ne + | Operator::I64LtS + | Operator::I64LtU + | Operator::I64GtS + | Operator::I64GtU + | Operator::I64LeS + | Operator::I64LeU + | Operator::I64GeS + | Operator::I64GeU => { + self.pop_operand(Some(Type::I64))?; + self.pop_operand(Some(Type::I64))?; + self.push_operand(Type::I32)?; + } + Operator::F32Eq + | Operator::F32Ne + | Operator::F32Lt + | Operator::F32Gt + | Operator::F32Le + | Operator::F32Ge => { + self.check_non_deterministic_enabled()?; + self.pop_operand(Some(Type::F32))?; + self.pop_operand(Some(Type::F32))?; + self.push_operand(Type::I32)?; + } + Operator::F64Eq + | Operator::F64Ne + | Operator::F64Lt + | Operator::F64Gt + | Operator::F64Le + | Operator::F64Ge => { + self.check_non_deterministic_enabled()?; + self.pop_operand(Some(Type::F64))?; + self.pop_operand(Some(Type::F64))?; + self.push_operand(Type::I32)?; + } + Operator::I32Clz | Operator::I32Ctz | Operator::I32Popcnt => { + self.pop_operand(Some(Type::I32))?; + self.push_operand(Type::I32)?; + } + Operator::I32Add + | Operator::I32Sub + | Operator::I32Mul + | Operator::I32DivS + | Operator::I32DivU + | Operator::I32RemS + | Operator::I32RemU + | Operator::I32And + | Operator::I32Or + | Operator::I32Xor + | Operator::I32Shl + | Operator::I32ShrS + | Operator::I32ShrU + | Operator::I32Rotl + | Operator::I32Rotr => { + self.pop_operand(Some(Type::I32))?; + self.pop_operand(Some(Type::I32))?; + self.push_operand(Type::I32)?; + } + Operator::I64Clz | Operator::I64Ctz | Operator::I64Popcnt => { + self.pop_operand(Some(Type::I64))?; + self.push_operand(Type::I64)?; + } + Operator::I64Add + | Operator::I64Sub + | Operator::I64Mul + | Operator::I64DivS + | Operator::I64DivU + | Operator::I64RemS + | Operator::I64RemU + | Operator::I64And + | Operator::I64Or + | Operator::I64Xor + | Operator::I64Shl + | Operator::I64ShrS + | Operator::I64ShrU + | Operator::I64Rotl + | Operator::I64Rotr => { + self.pop_operand(Some(Type::I64))?; + self.pop_operand(Some(Type::I64))?; + self.push_operand(Type::I64)?; + } + Operator::F32Abs + | Operator::F32Neg + | Operator::F32Ceil + | Operator::F32Floor + | Operator::F32Trunc + | Operator::F32Nearest + | Operator::F32Sqrt => { + self.check_non_deterministic_enabled()?; + self.pop_operand(Some(Type::F32))?; + self.push_operand(Type::F32)?; + } + Operator::F32Add + | Operator::F32Sub + | Operator::F32Mul + | Operator::F32Div + | Operator::F32Min + | Operator::F32Max + | Operator::F32Copysign => { + self.check_non_deterministic_enabled()?; + self.pop_operand(Some(Type::F32))?; + self.pop_operand(Some(Type::F32))?; + self.push_operand(Type::F32)?; + } + Operator::F64Abs + | Operator::F64Neg + | Operator::F64Ceil + | Operator::F64Floor + | Operator::F64Trunc + | Operator::F64Nearest + | Operator::F64Sqrt => { + self.check_non_deterministic_enabled()?; + self.pop_operand(Some(Type::F64))?; + self.push_operand(Type::F64)?; + } + Operator::F64Add + | Operator::F64Sub + | Operator::F64Mul + | Operator::F64Div + | Operator::F64Min + | Operator::F64Max + | Operator::F64Copysign => { + self.check_non_deterministic_enabled()?; + self.pop_operand(Some(Type::F64))?; + self.pop_operand(Some(Type::F64))?; + self.push_operand(Type::F64)?; + } + Operator::I32WrapI64 => { + self.pop_operand(Some(Type::I64))?; + self.push_operand(Type::I32)?; + } + Operator::I32TruncF32S | Operator::I32TruncF32U => { + self.pop_operand(Some(Type::F32))?; + self.push_operand(Type::I32)?; + } + Operator::I32TruncF64S | Operator::I32TruncF64U => { + self.pop_operand(Some(Type::F64))?; + self.push_operand(Type::I32)?; + } + Operator::I64ExtendI32S | Operator::I64ExtendI32U => { + self.pop_operand(Some(Type::I32))?; + self.push_operand(Type::I64)?; + } + Operator::I64TruncF32S | Operator::I64TruncF32U => { + self.pop_operand(Some(Type::F32))?; + self.push_operand(Type::I64)?; + } + Operator::I64TruncF64S | Operator::I64TruncF64U => { + self.pop_operand(Some(Type::F64))?; + self.push_operand(Type::I64)?; + } + Operator::F32ConvertI32S | Operator::F32ConvertI32U => { + self.check_non_deterministic_enabled()?; + self.pop_operand(Some(Type::I32))?; + self.push_operand(Type::F32)?; + } + Operator::F32ConvertI64S | Operator::F32ConvertI64U => { + self.check_non_deterministic_enabled()?; + self.pop_operand(Some(Type::I64))?; + self.push_operand(Type::F32)?; + } + Operator::F32DemoteF64 => { + self.check_non_deterministic_enabled()?; + self.pop_operand(Some(Type::F64))?; + self.push_operand(Type::F32)?; + } + Operator::F64ConvertI32S | Operator::F64ConvertI32U => { + self.check_non_deterministic_enabled()?; + self.pop_operand(Some(Type::I32))?; + self.push_operand(Type::F64)?; + } + Operator::F64ConvertI64S | Operator::F64ConvertI64U => { + self.check_non_deterministic_enabled()?; + self.pop_operand(Some(Type::I64))?; + self.push_operand(Type::F64)?; + } + Operator::F64PromoteF32 => { + self.check_non_deterministic_enabled()?; + self.pop_operand(Some(Type::F32))?; + self.push_operand(Type::F64)?; + } + Operator::I32ReinterpretF32 => { + self.pop_operand(Some(Type::F32))?; + self.push_operand(Type::I32)?; + } + Operator::I64ReinterpretF64 => { + self.pop_operand(Some(Type::F64))?; + self.push_operand(Type::I64)?; + } + Operator::F32ReinterpretI32 => { + self.check_non_deterministic_enabled()?; + self.pop_operand(Some(Type::I32))?; + self.push_operand(Type::F32)?; + } + Operator::F64ReinterpretI64 => { + self.check_non_deterministic_enabled()?; + self.pop_operand(Some(Type::I64))?; + self.push_operand(Type::F64)?; + } + Operator::I32TruncSatF32S | Operator::I32TruncSatF32U => { + self.pop_operand(Some(Type::F32))?; + self.push_operand(Type::I32)?; + } + Operator::I32TruncSatF64S | Operator::I32TruncSatF64U => { + self.pop_operand(Some(Type::F64))?; + self.push_operand(Type::I32)?; + } + Operator::I64TruncSatF32S | Operator::I64TruncSatF32U => { + self.pop_operand(Some(Type::F32))?; + self.push_operand(Type::I64)?; + } + Operator::I64TruncSatF64S | Operator::I64TruncSatF64U => { + self.pop_operand(Some(Type::F64))?; + self.push_operand(Type::I64)?; + } + Operator::I32Extend16S | Operator::I32Extend8S => { + self.pop_operand(Some(Type::I32))?; + self.push_operand(Type::I32)?; + } + + Operator::I64Extend32S | Operator::I64Extend16S | Operator::I64Extend8S => { + self.pop_operand(Some(Type::I64))?; + self.push_operand(Type::I64)?; + } + + Operator::I32AtomicLoad { memarg } + | Operator::I32AtomicLoad16U { memarg } + | Operator::I32AtomicLoad8U { memarg } => { + self.check_threads_enabled()?; + let ty = self.check_shared_memarg_wo_align(memarg, resources)?; + self.pop_operand(Some(ty))?; + self.push_operand(Type::I32)?; + } + Operator::I64AtomicLoad { memarg } + | Operator::I64AtomicLoad32U { memarg } + | Operator::I64AtomicLoad16U { memarg } + | Operator::I64AtomicLoad8U { memarg } => { + self.check_threads_enabled()?; + let ty = self.check_shared_memarg_wo_align(memarg, resources)?; + self.pop_operand(Some(ty))?; + self.push_operand(Type::I64)?; + } + Operator::I32AtomicStore { memarg } + | Operator::I32AtomicStore16 { memarg } + | Operator::I32AtomicStore8 { memarg } => { + self.check_threads_enabled()?; + let ty = self.check_shared_memarg_wo_align(memarg, resources)?; + self.pop_operand(Some(Type::I32))?; + self.pop_operand(Some(ty))?; + } + Operator::I64AtomicStore { memarg } + | Operator::I64AtomicStore32 { memarg } + | Operator::I64AtomicStore16 { memarg } + | Operator::I64AtomicStore8 { memarg } => { + self.check_threads_enabled()?; + let ty = self.check_shared_memarg_wo_align(memarg, resources)?; + self.pop_operand(Some(Type::I64))?; + self.pop_operand(Some(ty))?; + } + Operator::I32AtomicRmwAdd { memarg } + | Operator::I32AtomicRmwSub { memarg } + | Operator::I32AtomicRmwAnd { memarg } + | Operator::I32AtomicRmwOr { memarg } + | Operator::I32AtomicRmwXor { memarg } + | Operator::I32AtomicRmw16AddU { memarg } + | Operator::I32AtomicRmw16SubU { memarg } + | Operator::I32AtomicRmw16AndU { memarg } + | Operator::I32AtomicRmw16OrU { memarg } + | Operator::I32AtomicRmw16XorU { memarg } + | Operator::I32AtomicRmw8AddU { memarg } + | Operator::I32AtomicRmw8SubU { memarg } + | Operator::I32AtomicRmw8AndU { memarg } + | Operator::I32AtomicRmw8OrU { memarg } + | Operator::I32AtomicRmw8XorU { memarg } => { + self.check_threads_enabled()?; + let ty = self.check_shared_memarg_wo_align(memarg, resources)?; + self.pop_operand(Some(Type::I32))?; + self.pop_operand(Some(ty))?; + self.push_operand(Type::I32)?; + } + Operator::I64AtomicRmwAdd { memarg } + | Operator::I64AtomicRmwSub { memarg } + | Operator::I64AtomicRmwAnd { memarg } + | Operator::I64AtomicRmwOr { memarg } + | Operator::I64AtomicRmwXor { memarg } + | Operator::I64AtomicRmw32AddU { memarg } + | Operator::I64AtomicRmw32SubU { memarg } + | Operator::I64AtomicRmw32AndU { memarg } + | Operator::I64AtomicRmw32OrU { memarg } + | Operator::I64AtomicRmw32XorU { memarg } + | Operator::I64AtomicRmw16AddU { memarg } + | Operator::I64AtomicRmw16SubU { memarg } + | Operator::I64AtomicRmw16AndU { memarg } + | Operator::I64AtomicRmw16OrU { memarg } + | Operator::I64AtomicRmw16XorU { memarg } + | Operator::I64AtomicRmw8AddU { memarg } + | Operator::I64AtomicRmw8SubU { memarg } + | Operator::I64AtomicRmw8AndU { memarg } + | Operator::I64AtomicRmw8OrU { memarg } + | Operator::I64AtomicRmw8XorU { memarg } => { + self.check_threads_enabled()?; + let ty = self.check_shared_memarg_wo_align(memarg, resources)?; + self.pop_operand(Some(Type::I64))?; + self.pop_operand(Some(ty))?; + self.push_operand(Type::I64)?; + } + Operator::I32AtomicRmwXchg { memarg } + | Operator::I32AtomicRmw16XchgU { memarg } + | Operator::I32AtomicRmw8XchgU { memarg } => { + self.check_threads_enabled()?; + let ty = self.check_shared_memarg_wo_align(memarg, resources)?; + self.pop_operand(Some(Type::I32))?; + self.pop_operand(Some(ty))?; + self.push_operand(Type::I32)?; + } + Operator::I32AtomicRmwCmpxchg { memarg } + | Operator::I32AtomicRmw16CmpxchgU { memarg } + | Operator::I32AtomicRmw8CmpxchgU { memarg } => { + self.check_threads_enabled()?; + let ty = self.check_shared_memarg_wo_align(memarg, resources)?; + self.pop_operand(Some(Type::I32))?; + self.pop_operand(Some(Type::I32))?; + self.pop_operand(Some(ty))?; + self.push_operand(Type::I32)?; + } + Operator::I64AtomicRmwXchg { memarg } + | Operator::I64AtomicRmw32XchgU { memarg } + | Operator::I64AtomicRmw16XchgU { memarg } + | Operator::I64AtomicRmw8XchgU { memarg } => { + self.check_threads_enabled()?; + let ty = self.check_shared_memarg_wo_align(memarg, resources)?; + self.pop_operand(Some(Type::I64))?; + self.pop_operand(Some(ty))?; + self.push_operand(Type::I64)?; + } + Operator::I64AtomicRmwCmpxchg { memarg } + | Operator::I64AtomicRmw32CmpxchgU { memarg } + | Operator::I64AtomicRmw16CmpxchgU { memarg } + | Operator::I64AtomicRmw8CmpxchgU { memarg } => { + self.check_threads_enabled()?; + let ty = self.check_shared_memarg_wo_align(memarg, resources)?; + self.pop_operand(Some(Type::I64))?; + self.pop_operand(Some(Type::I64))?; + self.pop_operand(Some(ty))?; + self.push_operand(Type::I64)?; + } + Operator::MemoryAtomicNotify { memarg } => { + self.check_threads_enabled()?; + let ty = self.check_shared_memarg_wo_align(memarg, resources)?; + self.pop_operand(Some(Type::I32))?; + self.pop_operand(Some(ty))?; + self.push_operand(Type::I32)?; + } + Operator::MemoryAtomicWait32 { memarg } => { + self.check_threads_enabled()?; + let ty = self.check_shared_memarg_wo_align(memarg, resources)?; + self.pop_operand(Some(Type::I64))?; + self.pop_operand(Some(Type::I32))?; + self.pop_operand(Some(ty))?; + self.push_operand(Type::I32)?; + } + Operator::MemoryAtomicWait64 { memarg } => { + self.check_threads_enabled()?; + let ty = self.check_shared_memarg_wo_align(memarg, resources)?; + self.pop_operand(Some(Type::I64))?; + self.pop_operand(Some(Type::I64))?; + self.pop_operand(Some(ty))?; + self.push_operand(Type::I32)?; + } + Operator::AtomicFence { ref flags } => { + self.check_threads_enabled()?; + if *flags != 0 { + return Err(OperatorValidatorError::new( + "non-zero flags for fence not supported yet", + )); + } + } + Operator::RefNull { ty } => { + self.check_reference_types_enabled()?; + match ty { + Type::FuncRef | Type::ExternRef => {} + _ => { + return Err(OperatorValidatorError::new( + "invalid reference type in ref.null", + )) + } + } + self.push_operand(ty)?; + } + Operator::RefIsNull => { + self.check_reference_types_enabled()?; + match self.pop_operand(None)? { + None | Some(Type::FuncRef) | Some(Type::ExternRef) => {} + _ => { + return Err(OperatorValidatorError::new( + "type mismatch: invalid reference type in ref.is_null", + )) + } + } + self.push_operand(Type::I32)?; + } + Operator::RefFunc { function_index } => { + self.check_reference_types_enabled()?; + if resources.type_of_function(function_index).is_none() { + return Err(OperatorValidatorError::new( + "unknown function: function index out of bounds", + )); + } + if !resources.is_function_referenced(function_index) { + return Err(OperatorValidatorError::new("undeclared function reference")); + } + self.push_operand(Type::FuncRef)?; + } + Operator::V128Load { memarg } => { + self.check_simd_enabled()?; + let ty = self.check_memarg(memarg, 4, resources)?; + self.pop_operand(Some(ty))?; + self.push_operand(Type::V128)?; + } + Operator::V128Store { memarg } => { + self.check_simd_enabled()?; + let ty = self.check_memarg(memarg, 4, resources)?; + self.pop_operand(Some(Type::V128))?; + self.pop_operand(Some(ty))?; + } + Operator::V128Const { .. } => { + self.check_simd_enabled()?; + self.push_operand(Type::V128)?; + } + Operator::I8x16Splat | Operator::I16x8Splat | Operator::I32x4Splat => { + self.check_simd_enabled()?; + self.pop_operand(Some(Type::I32))?; + self.push_operand(Type::V128)?; + } + Operator::I64x2Splat => { + self.check_simd_enabled()?; + self.pop_operand(Some(Type::I64))?; + self.push_operand(Type::V128)?; + } + Operator::F32x4Splat => { + self.check_non_deterministic_enabled()?; + self.check_simd_enabled()?; + self.pop_operand(Some(Type::F32))?; + self.push_operand(Type::V128)?; + } + Operator::F64x2Splat => { + self.check_non_deterministic_enabled()?; + self.check_simd_enabled()?; + self.pop_operand(Some(Type::F64))?; + self.push_operand(Type::V128)?; + } + Operator::I8x16ExtractLaneS { lane } | Operator::I8x16ExtractLaneU { lane } => { + self.check_simd_enabled()?; + self.check_simd_lane_index(lane, 16)?; + self.pop_operand(Some(Type::V128))?; + self.push_operand(Type::I32)?; + } + Operator::I16x8ExtractLaneS { lane } | Operator::I16x8ExtractLaneU { lane } => { + self.check_simd_enabled()?; + self.check_simd_lane_index(lane, 8)?; + self.pop_operand(Some(Type::V128))?; + self.push_operand(Type::I32)?; + } + Operator::I32x4ExtractLane { lane } => { + self.check_simd_enabled()?; + self.check_simd_lane_index(lane, 4)?; + self.pop_operand(Some(Type::V128))?; + self.push_operand(Type::I32)?; + } + Operator::I8x16ReplaceLane { lane } => { + self.check_simd_enabled()?; + self.check_simd_lane_index(lane, 16)?; + self.pop_operand(Some(Type::I32))?; + self.pop_operand(Some(Type::V128))?; + self.push_operand(Type::V128)?; + } + Operator::I16x8ReplaceLane { lane } => { + self.check_simd_enabled()?; + self.check_simd_lane_index(lane, 8)?; + self.pop_operand(Some(Type::I32))?; + self.pop_operand(Some(Type::V128))?; + self.push_operand(Type::V128)?; + } + Operator::I32x4ReplaceLane { lane } => { + self.check_simd_enabled()?; + self.check_simd_lane_index(lane, 4)?; + self.pop_operand(Some(Type::I32))?; + self.pop_operand(Some(Type::V128))?; + self.push_operand(Type::V128)?; + } + Operator::I64x2ExtractLane { lane } => { + self.check_simd_enabled()?; + self.check_simd_lane_index(lane, 2)?; + self.pop_operand(Some(Type::V128))?; + self.push_operand(Type::I64)?; + } + Operator::I64x2ReplaceLane { lane } => { + self.check_simd_enabled()?; + self.check_simd_lane_index(lane, 2)?; + self.pop_operand(Some(Type::I64))?; + self.pop_operand(Some(Type::V128))?; + self.push_operand(Type::V128)?; + } + Operator::F32x4ExtractLane { lane } => { + self.check_non_deterministic_enabled()?; + self.check_simd_enabled()?; + self.check_simd_lane_index(lane, 4)?; + self.pop_operand(Some(Type::V128))?; + self.push_operand(Type::F32)?; + } + Operator::F32x4ReplaceLane { lane } => { + self.check_non_deterministic_enabled()?; + self.check_simd_enabled()?; + self.check_simd_lane_index(lane, 4)?; + self.pop_operand(Some(Type::F32))?; + self.pop_operand(Some(Type::V128))?; + self.push_operand(Type::V128)?; + } + Operator::F64x2ExtractLane { lane } => { + self.check_non_deterministic_enabled()?; + self.check_simd_enabled()?; + self.check_simd_lane_index(lane, 2)?; + self.pop_operand(Some(Type::V128))?; + self.push_operand(Type::F64)?; + } + Operator::F64x2ReplaceLane { lane } => { + self.check_non_deterministic_enabled()?; + self.check_simd_enabled()?; + self.check_simd_lane_index(lane, 2)?; + self.pop_operand(Some(Type::F64))?; + self.pop_operand(Some(Type::V128))?; + self.push_operand(Type::V128)?; + } + Operator::F32x4Eq + | Operator::F32x4Ne + | Operator::F32x4Lt + | Operator::F32x4Gt + | Operator::F32x4Le + | Operator::F32x4Ge + | Operator::F64x2Eq + | Operator::F64x2Ne + | Operator::F64x2Lt + | Operator::F64x2Gt + | Operator::F64x2Le + | Operator::F64x2Ge + | Operator::F32x4Add + | Operator::F32x4Sub + | Operator::F32x4Mul + | Operator::F32x4Div + | Operator::F32x4Min + | Operator::F32x4Max + | Operator::F32x4PMin + | Operator::F32x4PMax + | Operator::F64x2Add + | Operator::F64x2Sub + | Operator::F64x2Mul + | Operator::F64x2Div + | Operator::F64x2Min + | Operator::F64x2Max + | Operator::F64x2PMin + | Operator::F64x2PMax => { + self.check_non_deterministic_enabled()?; + self.check_simd_enabled()?; + self.pop_operand(Some(Type::V128))?; + self.pop_operand(Some(Type::V128))?; + self.push_operand(Type::V128)?; + } + Operator::I8x16Eq + | Operator::I8x16Ne + | Operator::I8x16LtS + | Operator::I8x16LtU + | Operator::I8x16GtS + | Operator::I8x16GtU + | Operator::I8x16LeS + | Operator::I8x16LeU + | Operator::I8x16GeS + | Operator::I8x16GeU + | Operator::I16x8Eq + | Operator::I16x8Ne + | Operator::I16x8LtS + | Operator::I16x8LtU + | Operator::I16x8GtS + | Operator::I16x8GtU + | Operator::I16x8LeS + | Operator::I16x8LeU + | Operator::I16x8GeS + | Operator::I16x8GeU + | Operator::I32x4Eq + | Operator::I32x4Ne + | Operator::I32x4LtS + | Operator::I32x4LtU + | Operator::I32x4GtS + | Operator::I32x4GtU + | Operator::I32x4LeS + | Operator::I32x4LeU + | Operator::I32x4GeS + | Operator::I32x4GeU + | Operator::V128And + | Operator::V128AndNot + | Operator::V128Or + | Operator::V128Xor + | Operator::I8x16Add + | Operator::I8x16AddSatS + | Operator::I8x16AddSatU + | Operator::I8x16Sub + | Operator::I8x16SubSatS + | Operator::I8x16SubSatU + | Operator::I8x16MinS + | Operator::I8x16MinU + | Operator::I8x16MaxS + | Operator::I8x16MaxU + | Operator::I16x8Add + | Operator::I16x8AddSatS + | Operator::I16x8AddSatU + | Operator::I16x8Sub + | Operator::I16x8SubSatS + | Operator::I16x8SubSatU + | Operator::I16x8Mul + | Operator::I16x8MinS + | Operator::I16x8MinU + | Operator::I16x8MaxS + | Operator::I16x8MaxU + | Operator::I32x4Add + | Operator::I32x4Sub + | Operator::I32x4Mul + | Operator::I32x4MinS + | Operator::I32x4MinU + | Operator::I32x4MaxS + | Operator::I32x4MaxU + | Operator::I32x4DotI16x8S + | Operator::I64x2Add + | Operator::I64x2Sub + | Operator::I64x2Mul + | Operator::I8x16RoundingAverageU + | Operator::I16x8RoundingAverageU + | Operator::I8x16NarrowI16x8S + | Operator::I8x16NarrowI16x8U + | Operator::I16x8NarrowI32x4S + | Operator::I16x8NarrowI32x4U => { + self.check_simd_enabled()?; + self.pop_operand(Some(Type::V128))?; + self.pop_operand(Some(Type::V128))?; + self.push_operand(Type::V128)?; + } + Operator::F32x4Ceil + | Operator::F32x4Floor + | Operator::F32x4Trunc + | Operator::F32x4Nearest + | Operator::F64x2Ceil + | Operator::F64x2Floor + | Operator::F64x2Trunc + | Operator::F64x2Nearest + | Operator::F32x4Abs + | Operator::F32x4Neg + | Operator::F32x4Sqrt + | Operator::F64x2Abs + | Operator::F64x2Neg + | Operator::F64x2Sqrt + | Operator::F32x4ConvertI32x4S + | Operator::F32x4ConvertI32x4U => { + self.check_non_deterministic_enabled()?; + self.check_simd_enabled()?; + self.pop_operand(Some(Type::V128))?; + self.push_operand(Type::V128)?; + } + Operator::V128Not + | Operator::I8x16Abs + | Operator::I8x16Neg + | Operator::I16x8Abs + | Operator::I16x8Neg + | Operator::I32x4Abs + | Operator::I32x4Neg + | Operator::I64x2Neg + | Operator::I32x4TruncSatF32x4S + | Operator::I32x4TruncSatF32x4U + | Operator::I16x8WidenLowI8x16S + | Operator::I16x8WidenHighI8x16S + | Operator::I16x8WidenLowI8x16U + | Operator::I16x8WidenHighI8x16U + | Operator::I32x4WidenLowI16x8S + | Operator::I32x4WidenHighI16x8S + | Operator::I32x4WidenLowI16x8U + | Operator::I32x4WidenHighI16x8U => { + self.check_simd_enabled()?; + self.pop_operand(Some(Type::V128))?; + self.push_operand(Type::V128)?; + } + Operator::V128Bitselect => { + self.check_simd_enabled()?; + self.pop_operand(Some(Type::V128))?; + self.pop_operand(Some(Type::V128))?; + self.pop_operand(Some(Type::V128))?; + self.push_operand(Type::V128)?; + } + Operator::I8x16AnyTrue + | Operator::I8x16AllTrue + | Operator::I8x16Bitmask + | Operator::I16x8AnyTrue + | Operator::I16x8AllTrue + | Operator::I16x8Bitmask + | Operator::I32x4AnyTrue + | Operator::I32x4AllTrue + | Operator::I32x4Bitmask => { + self.check_simd_enabled()?; + self.pop_operand(Some(Type::V128))?; + self.push_operand(Type::I32)?; + } + Operator::I8x16Shl + | Operator::I8x16ShrS + | Operator::I8x16ShrU + | Operator::I16x8Shl + | Operator::I16x8ShrS + | Operator::I16x8ShrU + | Operator::I32x4Shl + | Operator::I32x4ShrS + | Operator::I32x4ShrU + | Operator::I64x2Shl + | Operator::I64x2ShrS + | Operator::I64x2ShrU => { + self.check_simd_enabled()?; + self.pop_operand(Some(Type::I32))?; + self.pop_operand(Some(Type::V128))?; + self.push_operand(Type::V128)?; + } + Operator::I8x16Swizzle => { + self.check_simd_enabled()?; + self.pop_operand(Some(Type::V128))?; + self.pop_operand(Some(Type::V128))?; + self.push_operand(Type::V128)?; + } + Operator::I8x16Shuffle { ref lanes } => { + self.check_simd_enabled()?; + self.pop_operand(Some(Type::V128))?; + self.pop_operand(Some(Type::V128))?; + for i in lanes { + self.check_simd_lane_index(*i, 32)?; + } + self.push_operand(Type::V128)?; + } + Operator::V128Load8Splat { memarg } => { + self.check_simd_enabled()?; + let ty = self.check_memarg(memarg, 0, resources)?; + self.pop_operand(Some(ty))?; + self.push_operand(Type::V128)?; + } + Operator::V128Load16Splat { memarg } => { + self.check_simd_enabled()?; + let ty = self.check_memarg(memarg, 1, resources)?; + self.pop_operand(Some(ty))?; + self.push_operand(Type::V128)?; + } + Operator::V128Load32Splat { memarg } | Operator::V128Load32Zero { memarg } => { + self.check_simd_enabled()?; + let ty = self.check_memarg(memarg, 2, resources)?; + self.pop_operand(Some(ty))?; + self.push_operand(Type::V128)?; + } + Operator::V128Load64Splat { memarg } + | Operator::V128Load64Zero { memarg } + | Operator::V128Load8x8S { memarg } + | Operator::V128Load8x8U { memarg } + | Operator::V128Load16x4S { memarg } + | Operator::V128Load16x4U { memarg } + | Operator::V128Load32x2S { memarg } + | Operator::V128Load32x2U { memarg } => { + self.check_simd_enabled()?; + let idx = self.check_memarg(memarg, 3, resources)?; + self.pop_operand(Some(idx))?; + self.push_operand(Type::V128)?; + } + + Operator::MemoryInit { mem, segment } => { + self.check_bulk_memory_enabled()?; + let ty = self.check_memory_index(mem, resources)?; + if segment >= resources.data_count() { + bail_op_err!("unknown data segment {}", segment); + } + self.pop_operand(Some(Type::I32))?; + self.pop_operand(Some(Type::I32))?; + self.pop_operand(Some(ty))?; + } + Operator::DataDrop { segment } => { + self.check_bulk_memory_enabled()?; + if segment >= resources.data_count() { + bail_op_err!("unknown data segment {}", segment); + } + } + Operator::MemoryCopy { src, dst } => { + self.check_bulk_memory_enabled()?; + let dst_ty = self.check_memory_index(dst, resources)?; + let src_ty = self.check_memory_index(src, resources)?; + self.pop_operand(Some(match src_ty { + Type::I32 => Type::I32, + _ => dst_ty, + }))?; + self.pop_operand(Some(src_ty))?; + self.pop_operand(Some(dst_ty))?; + } + Operator::MemoryFill { mem } => { + self.check_bulk_memory_enabled()?; + let ty = self.check_memory_index(mem, resources)?; + self.pop_operand(Some(ty))?; + self.pop_operand(Some(Type::I32))?; + self.pop_operand(Some(ty))?; + } + Operator::TableInit { segment, table } => { + self.check_bulk_memory_enabled()?; + if table > 0 { + self.check_reference_types_enabled()?; + } + let table = match resources.table_at(table) { + Some(table) => table, + None => bail_op_err!("unknown table {}: table index out of bounds", table), + }; + let segment_ty = match resources.element_type_at(segment) { + Some(ty) => ty, + None => bail_op_err!( + "unknown elem segment {}: segment index out of bounds", + segment + ), + }; + if segment_ty != table.element_type { + return Err(OperatorValidatorError::new("type mismatch")); + } + self.pop_operand(Some(Type::I32))?; + self.pop_operand(Some(Type::I32))?; + self.pop_operand(Some(Type::I32))?; + } + Operator::ElemDrop { segment } => { + self.check_bulk_memory_enabled()?; + if segment >= resources.element_count() { + bail_op_err!( + "unknown elem segment {}: segment index out of bounds", + segment + ); + } + } + Operator::TableCopy { + src_table, + dst_table, + } => { + self.check_bulk_memory_enabled()?; + if src_table > 0 || dst_table > 0 { + self.check_reference_types_enabled()?; + } + let (src, dst) = + match (resources.table_at(src_table), resources.table_at(dst_table)) { + (Some(a), Some(b)) => (a, b), + _ => return Err(OperatorValidatorError::new("table index out of bounds")), + }; + if src.element_type != dst.element_type { + return Err(OperatorValidatorError::new("type mismatch")); + } + self.pop_operand(Some(Type::I32))?; + self.pop_operand(Some(Type::I32))?; + self.pop_operand(Some(Type::I32))?; + } + Operator::TableGet { table } => { + self.check_reference_types_enabled()?; + let ty = match resources.table_at(table) { + Some(ty) => ty.element_type, + None => return Err(OperatorValidatorError::new("table index out of bounds")), + }; + self.pop_operand(Some(Type::I32))?; + self.push_operand(ty)?; + } + Operator::TableSet { table } => { + self.check_reference_types_enabled()?; + let ty = match resources.table_at(table) { + Some(ty) => ty.element_type, + None => return Err(OperatorValidatorError::new("table index out of bounds")), + }; + self.pop_operand(Some(ty))?; + self.pop_operand(Some(Type::I32))?; + } + Operator::TableGrow { table } => { + self.check_reference_types_enabled()?; + let ty = match resources.table_at(table) { + Some(ty) => ty.element_type, + None => return Err(OperatorValidatorError::new("table index out of bounds")), + }; + self.pop_operand(Some(Type::I32))?; + self.pop_operand(Some(ty))?; + self.push_operand(Type::I32)?; + } + Operator::TableSize { table } => { + self.check_reference_types_enabled()?; + if resources.table_at(table).is_none() { + return Err(OperatorValidatorError::new("table index out of bounds")); + } + self.push_operand(Type::I32)?; + } + Operator::TableFill { table } => { + self.check_bulk_memory_enabled()?; + let ty = match resources.table_at(table) { + Some(ty) => ty.element_type, + None => return Err(OperatorValidatorError::new("table index out of bounds")), + }; + self.pop_operand(Some(Type::I32))?; + self.pop_operand(Some(ty))?; + self.pop_operand(Some(Type::I32))?; + } + } + Ok(()) + } + + pub fn finish(&mut self) -> OperatorValidatorResult<()> { + if self.control.len() != 0 { + bail_op_err!("control frames remain at end of function"); + } + Ok(()) + } +} + +fn func_type_at<T: WasmModuleResources>( + resources: &T, + at: u32, +) -> OperatorValidatorResult<&T::FuncType> { + resources + .func_type_at(at) + .ok_or_else(|| OperatorValidatorError::new("unknown type: type index out of bounds")) +} + +enum Either<A, B> { + A(A), + B(B), +} + +impl<A, B> Iterator for Either<A, B> +where + A: Iterator, + B: Iterator<Item = A::Item>, +{ + type Item = A::Item; + fn next(&mut self) -> Option<A::Item> { + match self { + Either::A(a) => a.next(), + Either::B(b) => b.next(), + } + } +} +impl<A, B> DoubleEndedIterator for Either<A, B> +where + A: DoubleEndedIterator, + B: DoubleEndedIterator<Item = A::Item>, +{ + fn next_back(&mut self) -> Option<A::Item> { + match self { + Either::A(a) => a.next_back(), + Either::B(b) => b.next_back(), + } + } +} + +fn params<'a>( + ty: TypeOrFuncType, + resources: &'a impl WasmModuleResources, +) -> OperatorValidatorResult<impl DoubleEndedIterator<Item = Type> + 'a> { + Ok(match ty { + TypeOrFuncType::FuncType(t) => Either::A(func_type_at(resources, t)?.inputs()), + TypeOrFuncType::Type(_) => Either::B(None.into_iter()), + }) +} + +fn results<'a>( + ty: TypeOrFuncType, + resources: &'a impl WasmModuleResources, +) -> OperatorValidatorResult<impl DoubleEndedIterator<Item = Type> + 'a> { + Ok(match ty { + TypeOrFuncType::FuncType(t) => Either::A(func_type_at(resources, t)?.outputs()), + TypeOrFuncType::Type(Type::EmptyBlockType) => Either::B(None.into_iter()), + TypeOrFuncType::Type(t) => Either::B(Some(t).into_iter()), + }) +} + +fn label_types<'a>( + ty: TypeOrFuncType, + resources: &'a impl WasmModuleResources, + kind: FrameKind, +) -> OperatorValidatorResult<impl DoubleEndedIterator<Item = Type> + 'a> { + Ok(match kind { + FrameKind::Loop => Either::A(params(ty, resources)?), + _ => Either::B(results(ty, resources)?), + }) +} + +fn ty_to_str(ty: Type) -> &'static str { + match ty { + Type::I32 => "i32", + Type::I64 => "i64", + Type::F32 => "f32", + Type::F64 => "f64", + Type::V128 => "v128", + Type::FuncRef => "funcref", + Type::ExternRef => "externref", + Type::Func => "func", + Type::EmptyBlockType => "nil", + } +} diff --git a/third_party/rust/wasmparser/src/parser.rs b/third_party/rust/wasmparser/src/parser.rs new file mode 100644 index 0000000000..8e6569e334 --- /dev/null +++ b/third_party/rust/wasmparser/src/parser.rs @@ -0,0 +1,1338 @@ +use crate::{AliasSectionReader, InstanceSectionReader, ModuleSectionReader}; +use crate::{BinaryReader, BinaryReaderError, FunctionBody, Range, Result}; +use crate::{DataSectionReader, ElementSectionReader, ExportSectionReader}; +use crate::{FunctionSectionReader, ImportSectionReader, TypeSectionReader}; +use crate::{GlobalSectionReader, MemorySectionReader, TableSectionReader}; +use std::convert::TryInto; +use std::fmt; +use std::iter; + +/// An incremental parser of a binary WebAssembly module. +/// +/// This type is intended to be used to incrementally parse a WebAssembly module +/// as bytes become available for the module. This can also be used to parse +/// modules that are already entirely resident within memory. +/// +/// This primary function for a parser is the [`Parser::parse`] function which +/// will incrementally consume input. You can also use the [`Parser::parse_all`] +/// function to parse a module that is entirely resident in memory. +#[derive(Debug, Clone)] +pub struct Parser { + state: State, + offset: u64, + max_size: u64, +} + +#[derive(Debug, Clone)] +enum State { + ModuleHeader, + SectionStart, + FunctionBody { remaining: u32, len: u32 }, + ModuleCode { remaining: u32, len: u32 }, +} + +/// A successful return payload from [`Parser::parse`]. +/// +/// On success one of two possible values can be returned, either that more data +/// is needed to continue parsing or a chunk of the input was parsed, indicating +/// how much of it was parsed. +#[derive(Debug)] +pub enum Chunk<'a> { + /// This can be returned at any time and indicates that more data is needed + /// to proceed with parsing. Zero bytes were consumed from the input to + /// [`Parser::parse`]. The `usize` value here is a hint as to how many more + /// bytes are needed to continue parsing. + NeedMoreData(u64), + + /// A chunk was successfully parsed. + Parsed { + /// This many bytes of the `data` input to [`Parser::parse`] were + /// consumed to produce `payload`. + consumed: usize, + /// The value that we actually parsed. + payload: Payload<'a>, + }, +} + +/// Values that can be parsed from a wasm module. +/// +/// This enumeration is all possible chunks of pieces that can be parsed by a +/// [`Parser`] from a binary WebAssembly module. Note that for many sections the +/// entire section is parsed all at once, whereas other functions, like the code +/// section, are parsed incrementally. This is a distinction where some +/// sections, like the type section, are required to be fully resident in memory +/// (fully downloaded) before proceeding. Other sections, like the code section, +/// can be processed in a streaming fashion where each function is extracted +/// individually so it can possibly be shipped to another thread while you wait +/// for more functions to get downloaded. +/// +/// Note that payloads, when returned, do not indicate that the wasm module is +/// valid. For example when you receive a `Payload::TypeSection` the type +/// section itself has not yet actually been parsed. The reader returned will be +/// able to parse it, but you'll have to actually iterate the reader to do the +/// full parse. Each payload returned is intended to be a *window* into the +/// original `data` passed to [`Parser::parse`] which can be further processed +/// if necessary. +pub enum Payload<'a> { + /// Indicates the header of a WebAssembly binary. + /// + /// This header also indicates the version number that was parsed, which is + /// currently always 1. + Version { + /// The version number found + num: u32, + /// The range of bytes that were parsed to consume the header of the + /// module. Note that this range is relative to the start of the byte + /// stream. + range: Range, + }, + + /// A type section was received, and the provided reader can be used to + /// parse the contents of the type section. + TypeSection(crate::TypeSectionReader<'a>), + /// A import section was received, and the provided reader can be used to + /// parse the contents of the import section. + ImportSection(crate::ImportSectionReader<'a>), + /// An alias section was received, and the provided reader can be used to + /// parse the contents of the alias section. + AliasSection(crate::AliasSectionReader<'a>), + /// An instance section was received, and the provided reader can be used to + /// parse the contents of the instance section. + InstanceSection(crate::InstanceSectionReader<'a>), + /// A module section was received, and the provided reader can be used to + /// parse the contents of the module section. + ModuleSection(crate::ModuleSectionReader<'a>), + /// A function section was received, and the provided reader can be used to + /// parse the contents of the function section. + FunctionSection(crate::FunctionSectionReader<'a>), + /// A table section was received, and the provided reader can be used to + /// parse the contents of the table section. + TableSection(crate::TableSectionReader<'a>), + /// A memory section was received, and the provided reader can be used to + /// parse the contents of the memory section. + MemorySection(crate::MemorySectionReader<'a>), + /// A global section was received, and the provided reader can be used to + /// parse the contents of the global section. + GlobalSection(crate::GlobalSectionReader<'a>), + /// An export section was received, and the provided reader can be used to + /// parse the contents of the export section. + ExportSection(crate::ExportSectionReader<'a>), + /// A start section was received, and the `u32` here is the index of the + /// start function. + StartSection { + /// The start function index + func: u32, + /// The range of bytes that specify the `func` field, specified in + /// offsets relative to the start of the byte stream. + range: Range, + }, + /// An element section was received, and the provided reader can be used to + /// parse the contents of the element section. + ElementSection(crate::ElementSectionReader<'a>), + /// A data count section was received, and the `u32` here is the contents of + /// the data count section. + DataCountSection { + /// The number of data segments. + count: u32, + /// The range of bytes that specify the `count` field, specified in + /// offsets relative to the start of the byte stream. + range: Range, + }, + /// A data section was received, and the provided reader can be used to + /// parse the contents of the data section. + DataSection(crate::DataSectionReader<'a>), + /// A custom section was found. + CustomSection { + /// The name of the custom section. + name: &'a str, + /// The offset, relative to the start of the original module, that the + /// payload for this custom section starts at. + data_offset: usize, + /// The actual contents of the custom section. + data: &'a [u8], + }, + + /// Indicator of the start of the code section. + /// + /// This entry is returned whenever the code section starts. The `count` + /// field indicates how many entries are in this code section. After + /// receiving this start marker you're guaranteed that the next `count` + /// items will be either `CodeSectionEntry` or an error will be returned. + /// + /// This, unlike other sections, is intended to be used for streaming the + /// contents of the code section. The code section is not required to be + /// fully resident in memory when we parse it. Instead a [`Parser`] is + /// capable of parsing piece-by-piece of a code section. + CodeSectionStart { + /// The number of functions in this section. + count: u32, + /// The range of bytes that represent this section, specified in + /// offsets relative to the start of the byte stream. + range: Range, + /// The size, in bytes, of the remaining contents of this section. + /// + /// This can be used in combination with [`Parser::skip_section`] + /// where the caller will know how many bytes to skip before feeding + /// bytes into `Parser` again. + size: u32, + }, + + /// An entry of the code section, a function, was parsed. + /// + /// This entry indicates that a function was successfully received from the + /// code section, and the payload here is the window into the original input + /// where the function resides. Note that the function itself has not been + /// parsed, it's only been outlined. You'll need to process the + /// `FunctionBody` provided to test whether it parses and/or is valid. + CodeSectionEntry(crate::FunctionBody<'a>), + + /// Indicator of the start of the module code section. + /// + /// This behaves the same as the `CodeSectionStart` payload being returned. + /// You're guaranteed the next `count` items will be of type + /// `ModuleCodeSectionEntry`. + ModuleCodeSectionStart { + /// The number of inline modules in this section. + count: u32, + /// The range of bytes that represent this section, specified in + /// offsets relative to the start of the byte stream. + range: Range, + /// The size, in bytes, of the remaining contents of this section. + size: u32, + }, + + /// An entry of the module code section, a module, was parsed. + /// + /// This variant is special in that it returns a sub-`Parser`. Upon + /// receiving a `ModuleCodeSectionEntry` it is expected that the returned + /// `Parser` will be used instead of the parent `Parser` until the parse has + /// finished. You'll need to feed data into the `Parser` returned until it + /// returns `Payload::End`. After that you'll switch back to the parent + /// parser to resume parsing the rest of the module code section. + /// + /// Note that binaries will not be parsed correctly if you feed the data for + /// a nested module into the parent [`Parser`]. + ModuleCodeSectionEntry { + /// The parser to use to parse the contents of the nested submodule. + /// This parser should be used until it reports `End`. + parser: Parser, + /// The range of bytes, relative to the start of the input stream, of + /// the bytes containing this submodule. + range: Range, + }, + + /// An unknown section was found. + /// + /// This variant is returned for all unknown sections in a wasm file. This + /// likely wants to be interpreted as an error by consumers of the parser, + /// but this can also be used to parse sections unknown to wasmparser at + /// this time. + UnknownSection { + /// The 8-bit identifier for this section. + id: u8, + /// The contents of this section. + contents: &'a [u8], + /// The range of bytes, relative to the start of the original data + /// stream, that the contents of this section reside in. + range: Range, + }, + + /// The end of the WebAssembly module was reached. + End, +} + +impl Parser { + /// Creates a new module parser. + /// + /// Reports errors and ranges relative to `offset` provided, where `offset` + /// is some logical offset within the input stream that we're parsing. + pub fn new(offset: u64) -> Parser { + Parser { + state: State::ModuleHeader, + offset, + max_size: u64::max_value(), + } + } + + /// Attempts to parse a chunk of data. + /// + /// This method will attempt to parse the next incremental portion of a + /// WebAssembly binary. Data available for the module is provided as `data`, + /// and the data can be incomplete if more data has yet to arrive for the + /// module. The `eof` flag indicates whether `data` represents all possible + /// data for the module and no more data will ever be received. + /// + /// There are two ways parsing can succeed with this method: + /// + /// * `Chunk::NeedMoreData` - this indicates that there is not enough bytes + /// in `data` to parse a chunk of this module. The caller needs to wait + /// for more data to be available in this situation before calling this + /// method again. It is guaranteed that this is only returned if `eof` is + /// `false`. + /// + /// * `Chunk::Parsed` - this indicates that a chunk of the input was + /// successfully parsed. The payload is available in this variant of what + /// was parsed, and this also indicates how many bytes of `data` was + /// consumed. It's expected that the caller will not provide these bytes + /// back to the [`Parser`] again. + /// + /// Note that all `Chunk` return values are connected, with a lifetime, to + /// the input buffer. Each parsed chunk borrows the input buffer and is a + /// view into it for successfully parsed chunks. + /// + /// It is expected that you'll call this method until `Payload::End` is + /// reached, at which point you're guaranteed that the module has completely + /// parsed. Note that complete parsing, for the top-level wasm module, + /// implies that `data` is empty and `eof` is `true`. + /// + /// # Errors + /// + /// Parse errors are returned as an `Err`. Errors can happen when the + /// structure of the module is unexpected, or if sections are too large for + /// example. Note that errors are not returned for malformed *contents* of + /// sections here. Sections are generally not individually parsed and each + /// returned [`Payload`] needs to be iterated over further to detect all + /// errors. + /// + /// # Examples + /// + /// An example of reading a wasm file from a stream (`std::io::Read`) and + /// incrementally parsing it. + /// + /// ``` + /// use std::io::Read; + /// use anyhow::Result; + /// use wasmparser::{Parser, Chunk, Payload::*}; + /// + /// fn parse(mut reader: impl Read) -> Result<()> { + /// let mut buf = Vec::new(); + /// let mut parser = Parser::new(0); + /// let mut eof = false; + /// let mut stack = Vec::new(); + /// + /// loop { + /// let (payload, consumed) = match parser.parse(&buf, eof)? { + /// Chunk::NeedMoreData(hint) => { + /// assert!(!eof); // otherwise an error would be returned + /// + /// // Use the hint to preallocate more space, then read + /// // some more data into our buffer. + /// // + /// // Note that the buffer management here is not ideal, + /// // but it's compact enough to fit in an example! + /// let len = buf.len(); + /// buf.extend((0..hint).map(|_| 0u8)); + /// let n = reader.read(&mut buf[len..])?; + /// buf.truncate(len + n); + /// eof = n == 0; + /// continue; + /// } + /// + /// Chunk::Parsed { consumed, payload } => (payload, consumed), + /// }; + /// + /// match payload { + /// // Each of these would be handled individually as necessary + /// Version { .. } => { /* ... */ } + /// TypeSection(_) => { /* ... */ } + /// ImportSection(_) => { /* ... */ } + /// AliasSection(_) => { /* ... */ } + /// InstanceSection(_) => { /* ... */ } + /// ModuleSection(_) => { /* ... */ } + /// FunctionSection(_) => { /* ... */ } + /// TableSection(_) => { /* ... */ } + /// MemorySection(_) => { /* ... */ } + /// GlobalSection(_) => { /* ... */ } + /// ExportSection(_) => { /* ... */ } + /// StartSection { .. } => { /* ... */ } + /// ElementSection(_) => { /* ... */ } + /// DataCountSection { .. } => { /* ... */ } + /// DataSection(_) => { /* ... */ } + /// + /// // Here we know how many functions we'll be receiving as + /// // `CodeSectionEntry`, so we can prepare for that, and + /// // afterwards we can parse and handle each function + /// // individually. + /// CodeSectionStart { .. } => { /* ... */ } + /// CodeSectionEntry(body) => { + /// // here we can iterate over `body` to parse the function + /// // and its locals + /// } + /// + /// // When parsing nested modules we need to switch which + /// // `Parser` we're using. + /// ModuleCodeSectionStart { .. } => { /* ... */ } + /// ModuleCodeSectionEntry { parser: subparser, .. } => { + /// stack.push(parser); + /// parser = subparser; + /// } + /// + /// CustomSection { name, .. } => { /* ... */ } + /// + /// // most likely you'd return an error here + /// UnknownSection { id, .. } => { /* ... */ } + /// + /// // Once we've reached the end of a module we either resume + /// // at the parent module or we break out of the loop because + /// // we're done. + /// End => { + /// if let Some(parent_parser) = stack.pop() { + /// parser = parent_parser; + /// } else { + /// break; + /// } + /// } + /// } + /// + /// // once we're done processing the payload we can forget the + /// // original. + /// buf.drain(..consumed); + /// } + /// + /// Ok(()) + /// } + /// + /// # parse(&b"\0asm\x01\0\0\0"[..]).unwrap(); + /// ``` + pub fn parse<'a>(&mut self, data: &'a [u8], eof: bool) -> Result<Chunk<'a>> { + let (data, eof) = if usize_to_u64(data.len()) > self.max_size { + (&data[..(self.max_size as usize)], true) + } else { + (data, eof) + }; + // TODO: thread through `offset: u64` to `BinaryReader`, remove + // the cast here. + let mut reader = BinaryReader::new_with_offset(data, self.offset as usize); + match self.parse_reader(&mut reader, eof) { + Ok(payload) => { + // Be sure to update our offset with how far we got in the + // reader + self.offset += usize_to_u64(reader.position); + self.max_size -= usize_to_u64(reader.position); + Ok(Chunk::Parsed { + consumed: reader.position, + payload, + }) + } + Err(e) => { + // If we're at EOF then there's no way we can recover from any + // error, so continue to propagate it. + if eof { + return Err(e); + } + + // If our error doesn't look like it can be resolved with more + // data being pulled down, then propagate it, otherwise switch + // the error to "feed me please" + match e.inner.needed_hint { + Some(hint) => Ok(Chunk::NeedMoreData(usize_to_u64(hint))), + None => Err(e), + } + } + } + } + + fn parse_reader<'a>( + &mut self, + reader: &mut BinaryReader<'a>, + eof: bool, + ) -> Result<Payload<'a>> { + use Payload::*; + + match self.state { + State::ModuleHeader => { + let start = reader.original_position(); + let num = reader.read_file_header()?; + self.state = State::SectionStart; + Ok(Version { + num, + range: Range { + start, + end: reader.original_position(), + }, + }) + } + State::SectionStart => { + // If we're at eof and there are no bytes in our buffer, then + // that means we reached the end of the wasm file since it's + // just a bunch of sections concatenated after the module + // header. + if eof && reader.bytes_remaining() == 0 { + return Ok(Payload::End); + } + + let id = reader.read_var_u7()? as u8; + let len_pos = reader.position; + let mut len = reader.read_var_u32()?; + + // Test to make sure that this section actually fits within + // `Parser::max_size`. This doesn't matter for top-level modules + // but it is required for nested modules to correctly ensure + // that all sections live entirely within their section of the + // file. + let section_overflow = self + .max_size + .checked_sub(usize_to_u64(reader.position)) + .and_then(|s| s.checked_sub(len.into())) + .is_none(); + if section_overflow { + return Err(BinaryReaderError::new("section too large", len_pos)); + } + + match id { + 0 => { + let mut content = subreader(reader, len)?; + // Note that if this fails we can't read any more bytes, + // so clear the "we'd succeed if we got this many more + // bytes" because we can't recover from "eof" at this point. + let name = content.read_string().map_err(clear_hint)?; + Ok(Payload::CustomSection { + name, + data_offset: content.original_position(), + data: content.remaining_buffer(), + }) + } + 1 => section(reader, len, TypeSectionReader::new, TypeSection), + 2 => section(reader, len, ImportSectionReader::new, ImportSection), + 3 => section(reader, len, FunctionSectionReader::new, FunctionSection), + 4 => section(reader, len, TableSectionReader::new, TableSection), + 5 => section(reader, len, MemorySectionReader::new, MemorySection), + 6 => section(reader, len, GlobalSectionReader::new, GlobalSection), + 7 => section(reader, len, ExportSectionReader::new, ExportSection), + 8 => { + let (func, range) = single_u32(reader, len, "start")?; + Ok(StartSection { func, range }) + } + 9 => section(reader, len, ElementSectionReader::new, ElementSection), + 10 => { + let start = reader.original_position(); + let count = delimited(reader, &mut len, |r| r.read_var_u32())?; + let range = Range { + start, + end: reader.original_position() + len as usize, + }; + self.state = State::FunctionBody { + remaining: count, + len, + }; + Ok(CodeSectionStart { + count, + range, + size: len, + }) + } + 11 => section(reader, len, DataSectionReader::new, DataSection), + 12 => { + let (count, range) = single_u32(reader, len, "data count")?; + Ok(DataCountSection { count, range }) + } + 100 => section(reader, len, ModuleSectionReader::new, ModuleSection), + 101 => section(reader, len, InstanceSectionReader::new, InstanceSection), + 102 => section(reader, len, AliasSectionReader::new, AliasSection), + 103 => { + let start = reader.original_position(); + let count = delimited(reader, &mut len, |r| r.read_var_u32())?; + let range = Range { + start, + end: reader.original_position() + len as usize, + }; + self.state = State::ModuleCode { + remaining: count, + len, + }; + Ok(ModuleCodeSectionStart { + count, + range, + size: len, + }) + } + id => { + let offset = reader.original_position(); + let contents = reader.read_bytes(len as usize)?; + let range = Range { + start: offset, + end: offset + len as usize, + }; + Ok(UnknownSection { + id, + contents, + range, + }) + } + } + } + + // Once we hit 0 remaining incrementally parsed items, with 0 + // remaining bytes in each section, we're done and can switch back + // to parsing sections. + State::FunctionBody { + remaining: 0, + len: 0, + } + | State::ModuleCode { + remaining: 0, + len: 0, + } => { + self.state = State::SectionStart; + self.parse_reader(reader, eof) + } + + // ... otherwise trailing bytes with no remaining entries in these + // sections indicates an error. + State::FunctionBody { remaining: 0, len } | State::ModuleCode { remaining: 0, len } => { + debug_assert!(len > 0); + let offset = reader.original_position(); + Err(BinaryReaderError::new( + "trailing bytes at end of section", + offset, + )) + } + + // Functions are relatively easy to parse when we know there's at + // least one remaining and at least one byte available to read + // things. + // + // We use the remaining length try to read a u32 size of the + // function, and using that size we require the entire function be + // resident in memory. This means that we're reading whole chunks of + // functions at a time. + // + // Limiting via `Parser::max_size` (nested modules) happens above in + // `fn parse`, and limiting by our section size happens via + // `delimited`. Actual parsing of the function body is delegated to + // the caller to iterate over the `FunctionBody` structure. + State::FunctionBody { remaining, mut len } => { + let body = delimited(reader, &mut len, |r| { + let size = r.read_var_u32()?; + let offset = r.original_position(); + Ok(FunctionBody::new(offset, r.read_bytes(size as usize)?)) + })?; + self.state = State::FunctionBody { + remaining: remaining - 1, + len, + }; + Ok(CodeSectionEntry(body)) + } + + // Modules are trickier than functions. What's going to happen here + // is that we'll be offloading parsing to a sub-`Parser`. This + // sub-`Parser` will be delimited to not read past the size of the + // module that's specified. + // + // So the first thing that happens here is we read the size of the + // module. We use `delimited` to make sure the bytes specifying the + // size of the module are themselves within the module code section. + // + // Once we've read the size of a module, however, there's a few + // pieces of state that we need to update. We as a parser will not + // receive the next `size` bytes, so we need to update our internal + // bookkeeping to account for that: + // + // * The `len`, number of bytes remaining in this section, is + // decremented by `size`. This can underflow, however, meaning + // that the size of the module doesn't fit within the section. + // + // * Our `Parser::max_size` field needs to account for the bytes + // that we're reading. Note that this is guaranteed to not + // underflow, however, because whenever we parse a section header + // we guarantee that its contents fit within our `max_size`. + // + // To update `len` we do that when updating `self.state`, and to + // update `max_size` we do that inline. Note that this will get + // further tweaked after we return with the bytes we read specifying + // the size of the module itself. + State::ModuleCode { remaining, mut len } => { + let size = delimited(reader, &mut len, |r| r.read_var_u32())?; + match len.checked_sub(size) { + Some(i) => len = i, + None => { + return Err(BinaryReaderError::new( + "Unexpected EOF", + reader.original_position(), + )); + } + } + self.state = State::ModuleCode { + remaining: remaining - 1, + len, + }; + let range = Range { + start: reader.original_position(), + end: reader.original_position() + size as usize, + }; + self.max_size -= u64::from(size); + self.offset += u64::from(size); + let mut parser = Parser::new(usize_to_u64(reader.original_position())); + parser.max_size = size.into(); + Ok(ModuleCodeSectionEntry { parser, range }) + } + } + } + + /// Convenience function that can be used to parse a module entirely + /// resident in memory. + /// + /// This function will parse the `data` provided as a WebAssembly module, + /// assuming that `data` represents the entire WebAssembly module. + /// + /// Note that when this function yields `ModuleCodeSectionEntry` + /// no action needs to be taken with the returned parser. The parser will be + /// automatically switched to internally and more payloads will continue to + /// get returned. + pub fn parse_all<'a>( + self, + mut data: &'a [u8], + ) -> impl Iterator<Item = Result<Payload<'a>>> + 'a { + let mut stack = Vec::new(); + let mut cur = self; + let mut done = false; + iter::from_fn(move || { + if done { + return None; + } + let payload = match cur.parse(data, true) { + // Propagate all errors + Err(e) => return Some(Err(e)), + + // This isn't possible because `eof` is always true. + Ok(Chunk::NeedMoreData(_)) => unreachable!(), + + Ok(Chunk::Parsed { payload, consumed }) => { + data = &data[consumed..]; + payload + } + }; + + match &payload { + // If a module ends then we either finished the current + // module or, if there's a parent, we switch back to + // resuming parsing the parent. + Payload::End => match stack.pop() { + Some(p) => cur = p, + None => done = true, + }, + + // When we enter a nested module then we need to update our + // current parser, saving off the previous state. + // + // Afterwards we turn the loop again to recurse in parsing the + // nested module. + Payload::ModuleCodeSectionEntry { parser, range: _ } => { + stack.push(cur.clone()); + cur = parser.clone(); + } + + _ => {} + } + + Some(Ok(payload)) + }) + } + + /// Skip parsing the code or module code section entirely. + /// + /// This function can be used to indicate, after receiving + /// `CodeSectionStart` or `ModuleCodeSectionStart`, that the section + /// will not be parsed. + /// + /// The caller will be responsible for skipping `size` bytes (found in the + /// `CodeSectionStart` or `ModuleCodeSectionStart` payload). Bytes should + /// only be fed into `parse` after the `size` bytes have been skipped. + /// + /// # Panics + /// + /// This function will panic if the parser is not in a state where it's + /// parsing the code or module code section. + /// + /// # Examples + /// + /// ``` + /// use wasmparser::{Result, Parser, Chunk, Range, SectionReader, Payload::*}; + /// + /// fn objdump_headers(mut wasm: &[u8]) -> Result<()> { + /// let mut parser = Parser::new(0); + /// loop { + /// let payload = match parser.parse(wasm, true)? { + /// Chunk::Parsed { consumed, payload } => { + /// wasm = &wasm[consumed..]; + /// payload + /// } + /// // this state isn't possible with `eof = true` + /// Chunk::NeedMoreData(_) => unreachable!(), + /// }; + /// match payload { + /// TypeSection(s) => print_range("type section", &s.range()), + /// ImportSection(s) => print_range("import section", &s.range()), + /// // .. other sections + /// + /// // Print the range of the code section we see, but don't + /// // actually iterate over each individual function. + /// CodeSectionStart { range, size, .. } => { + /// print_range("code section", &range); + /// parser.skip_section(); + /// wasm = &wasm[size as usize..]; + /// } + /// End => break, + /// _ => {} + /// } + /// } + /// Ok(()) + /// } + /// + /// fn print_range(section: &str, range: &Range) { + /// println!("{:>40}: {:#010x} - {:#010x}", section, range.start, range.end); + /// } + /// ``` + pub fn skip_section(&mut self) { + let skip = match self.state { + State::FunctionBody { remaining: _, len } | State::ModuleCode { remaining: _, len } => { + len + } + _ => panic!("wrong state to call `skip_section`"), + }; + self.offset += u64::from(skip); + self.max_size -= u64::from(skip); + self.state = State::SectionStart; + } +} + +fn usize_to_u64(a: usize) -> u64 { + a.try_into().unwrap() +} + +/// Parses an entire section resident in memory into a `Payload`. +/// +/// Requires that `len` bytes are resident in `reader` and uses `ctor`/`variant` +/// to construct the section to return. +fn section<'a, T>( + reader: &mut BinaryReader<'a>, + len: u32, + ctor: fn(&'a [u8], usize) -> Result<T>, + variant: fn(T) -> Payload<'a>, +) -> Result<Payload<'a>> { + let offset = reader.original_position(); + let payload = reader.read_bytes(len as usize)?; + // clear the hint for "need this many more bytes" here because we already + // read all the bytes, so it's not possible to read more bytes if this + // fails. + let reader = ctor(payload, offset).map_err(clear_hint)?; + Ok(variant(reader)) +} + +/// Creates a new `BinaryReader` from the given `reader` which will be reading +/// the first `len` bytes. +/// +/// This means that `len` bytes must be resident in memory at the time of this +/// reading. +fn subreader<'a>(reader: &mut BinaryReader<'a>, len: u32) -> Result<BinaryReader<'a>> { + let offset = reader.original_position(); + let payload = reader.read_bytes(len as usize)?; + Ok(BinaryReader::new_with_offset(payload, offset)) +} + +/// Reads a section that is represented by a single uleb-encoded `u32`. +fn single_u32<'a>(reader: &mut BinaryReader<'a>, len: u32, desc: &str) -> Result<(u32, Range)> { + let range = Range { + start: reader.original_position(), + end: reader.original_position() + len as usize, + }; + let mut content = subreader(reader, len)?; + // We can't recover from "unexpected eof" here because our entire section is + // already resident in memory, so clear the hint for how many more bytes are + // expected. + let index = content.read_var_u32().map_err(clear_hint)?; + if !content.eof() { + return Err(BinaryReaderError::new( + format!("Unexpected content in the {} section", desc), + content.original_position(), + )); + } + Ok((index, range)) +} + +/// Attempts to parse using `f`. +/// +/// This will update `*len` with the number of bytes consumed, and it will cause +/// a failure to be returned instead of the number of bytes consumed exceeds +/// what `*len` currently is. +fn delimited<'a, T>( + reader: &mut BinaryReader<'a>, + len: &mut u32, + f: impl FnOnce(&mut BinaryReader<'a>) -> Result<T>, +) -> Result<T> { + let start = reader.position; + let ret = f(reader)?; + *len = match (reader.position - start) + .try_into() + .ok() + .and_then(|i| len.checked_sub(i)) + { + Some(i) => i, + None => return Err(BinaryReaderError::new("Unexpected EOF", start)), + }; + Ok(ret) +} + +impl Default for Parser { + fn default() -> Parser { + Parser::new(0) + } +} + +impl fmt::Debug for Payload<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + use Payload::*; + match self { + CustomSection { + name, + data_offset, + data: _, + } => f + .debug_struct("CustomSection") + .field("name", name) + .field("data_offset", data_offset) + .field("data", &"...") + .finish(), + Version { num, range } => f + .debug_struct("Version") + .field("num", num) + .field("range", range) + .finish(), + TypeSection(_) => f.debug_tuple("TypeSection").field(&"...").finish(), + ImportSection(_) => f.debug_tuple("ImportSection").field(&"...").finish(), + AliasSection(_) => f.debug_tuple("AliasSection").field(&"...").finish(), + InstanceSection(_) => f.debug_tuple("InstanceSection").field(&"...").finish(), + ModuleSection(_) => f.debug_tuple("ModuleSection").field(&"...").finish(), + FunctionSection(_) => f.debug_tuple("FunctionSection").field(&"...").finish(), + TableSection(_) => f.debug_tuple("TableSection").field(&"...").finish(), + MemorySection(_) => f.debug_tuple("MemorySection").field(&"...").finish(), + GlobalSection(_) => f.debug_tuple("GlobalSection").field(&"...").finish(), + ExportSection(_) => f.debug_tuple("ExportSection").field(&"...").finish(), + ElementSection(_) => f.debug_tuple("ElementSection").field(&"...").finish(), + DataSection(_) => f.debug_tuple("DataSection").field(&"...").finish(), + StartSection { func, range } => f + .debug_struct("StartSection") + .field("func", func) + .field("range", range) + .finish(), + DataCountSection { count, range } => f + .debug_struct("DataCountSection") + .field("count", count) + .field("range", range) + .finish(), + CodeSectionStart { count, range, size } => f + .debug_struct("CodeSectionStart") + .field("count", count) + .field("range", range) + .field("size", size) + .finish(), + CodeSectionEntry(_) => f.debug_tuple("CodeSectionEntry").field(&"...").finish(), + ModuleCodeSectionStart { count, range, size } => f + .debug_struct("ModuleCodeSectionStart") + .field("count", count) + .field("range", range) + .field("size", size) + .finish(), + ModuleCodeSectionEntry { parser: _, range } => f + .debug_struct("ModuleCodeSectionEntry") + .field("range", range) + .finish(), + UnknownSection { id, range, .. } => f + .debug_struct("UnknownSection") + .field("id", id) + .field("range", range) + .finish(), + End => f.write_str("End"), + } + } +} + +fn clear_hint(mut err: BinaryReaderError) -> BinaryReaderError { + err.inner.needed_hint = None; + err +} + +#[cfg(test)] +mod tests { + use super::*; + + macro_rules! assert_matches { + ($a:expr, $b:pat $(,)?) => { + match $a { + $b => {} + a => panic!("`{:?}` doesn't match `{}`", a, stringify!($b)), + } + }; + } + + #[test] + fn header() { + assert!(Parser::default().parse(&[], true).is_err()); + assert_matches!( + Parser::default().parse(&[], false), + Ok(Chunk::NeedMoreData(4)), + ); + assert_matches!( + Parser::default().parse(b"\0", false), + Ok(Chunk::NeedMoreData(3)), + ); + assert_matches!( + Parser::default().parse(b"\0asm", false), + Ok(Chunk::NeedMoreData(4)), + ); + assert_matches!( + Parser::default().parse(b"\0asm\x01\0\0\0", false), + Ok(Chunk::Parsed { + consumed: 8, + payload: Payload::Version { num: 1, .. }, + }), + ); + } + + fn parser_after_header() -> Parser { + let mut p = Parser::default(); + assert_matches!( + p.parse(b"\0asm\x01\0\0\0", false), + Ok(Chunk::Parsed { + consumed: 8, + payload: Payload::Version { num: 1, .. }, + }), + ); + return p; + } + + #[test] + fn start_section() { + assert_matches!( + parser_after_header().parse(&[], false), + Ok(Chunk::NeedMoreData(1)), + ); + assert!(parser_after_header().parse(&[8], true).is_err()); + assert!(parser_after_header().parse(&[8, 1], true).is_err()); + assert!(parser_after_header().parse(&[8, 2], true).is_err()); + assert_matches!( + parser_after_header().parse(&[8], false), + Ok(Chunk::NeedMoreData(1)), + ); + assert_matches!( + parser_after_header().parse(&[8, 1], false), + Ok(Chunk::NeedMoreData(1)), + ); + assert_matches!( + parser_after_header().parse(&[8, 2], false), + Ok(Chunk::NeedMoreData(2)), + ); + assert_matches!( + parser_after_header().parse(&[8, 1, 1], false), + Ok(Chunk::Parsed { + consumed: 3, + payload: Payload::StartSection { func: 1, .. }, + }), + ); + assert!(parser_after_header().parse(&[8, 2, 1, 1], false).is_err()); + assert!(parser_after_header().parse(&[8, 0], false).is_err()); + } + + #[test] + fn end_works() { + assert_matches!( + parser_after_header().parse(&[], true), + Ok(Chunk::Parsed { + consumed: 0, + payload: Payload::End, + }), + ); + } + + #[test] + fn type_section() { + assert!(parser_after_header().parse(&[1], true).is_err()); + assert!(parser_after_header().parse(&[1, 0], false).is_err()); + // assert!(parser_after_header().parse(&[8, 2], true).is_err()); + assert_matches!( + parser_after_header().parse(&[1], false), + Ok(Chunk::NeedMoreData(1)), + ); + assert_matches!( + parser_after_header().parse(&[1, 1], false), + Ok(Chunk::NeedMoreData(1)), + ); + assert_matches!( + parser_after_header().parse(&[1, 1, 1], false), + Ok(Chunk::Parsed { + consumed: 3, + payload: Payload::TypeSection(_), + }), + ); + assert_matches!( + parser_after_header().parse(&[1, 1, 1, 2, 3, 4], false), + Ok(Chunk::Parsed { + consumed: 3, + payload: Payload::TypeSection(_), + }), + ); + } + + #[test] + fn custom_section() { + assert!(parser_after_header().parse(&[0], true).is_err()); + assert!(parser_after_header().parse(&[0, 0], false).is_err()); + assert!(parser_after_header().parse(&[0, 1, 1], false).is_err()); + assert_matches!( + parser_after_header().parse(&[0, 2, 1], false), + Ok(Chunk::NeedMoreData(1)), + ); + assert_matches!( + parser_after_header().parse(&[0, 1, 0], false), + Ok(Chunk::Parsed { + consumed: 3, + payload: Payload::CustomSection { + name: "", + data_offset: 11, + data: b"", + }, + }), + ); + assert_matches!( + parser_after_header().parse(&[0, 2, 1, b'a'], false), + Ok(Chunk::Parsed { + consumed: 4, + payload: Payload::CustomSection { + name: "a", + data_offset: 12, + data: b"", + }, + }), + ); + assert_matches!( + parser_after_header().parse(&[0, 2, 0, b'a'], false), + Ok(Chunk::Parsed { + consumed: 4, + payload: Payload::CustomSection { + name: "", + data_offset: 11, + data: b"a", + }, + }), + ); + } + + #[test] + fn function_section() { + assert!(parser_after_header().parse(&[10], true).is_err()); + assert!(parser_after_header().parse(&[10, 0], true).is_err()); + assert!(parser_after_header().parse(&[10, 1], true).is_err()); + assert_matches!( + parser_after_header().parse(&[10], false), + Ok(Chunk::NeedMoreData(1)) + ); + assert_matches!( + parser_after_header().parse(&[10, 1], false), + Ok(Chunk::NeedMoreData(1)) + ); + let mut p = parser_after_header(); + assert_matches!( + p.parse(&[10, 1, 0], false), + Ok(Chunk::Parsed { + consumed: 3, + payload: Payload::CodeSectionStart { count: 0, .. }, + }), + ); + assert_matches!( + p.parse(&[], true), + Ok(Chunk::Parsed { + consumed: 0, + payload: Payload::End, + }), + ); + let mut p = parser_after_header(); + assert_matches!( + p.parse(&[10, 2, 1, 0], false), + Ok(Chunk::Parsed { + consumed: 3, + payload: Payload::CodeSectionStart { count: 1, .. }, + }), + ); + assert_matches!( + p.parse(&[0], false), + Ok(Chunk::Parsed { + consumed: 1, + payload: Payload::CodeSectionEntry(_), + }), + ); + assert_matches!( + p.parse(&[], true), + Ok(Chunk::Parsed { + consumed: 0, + payload: Payload::End, + }), + ); + + // 1 byte section with 1 function can't read the function body because + // the section is too small + let mut p = parser_after_header(); + assert_matches!( + p.parse(&[10, 1, 1], false), + Ok(Chunk::Parsed { + consumed: 3, + payload: Payload::CodeSectionStart { count: 1, .. }, + }), + ); + assert_eq!( + p.parse(&[0], false).unwrap_err().message(), + "Unexpected EOF" + ); + + // section with 2 functions but section is cut off + let mut p = parser_after_header(); + assert_matches!( + p.parse(&[10, 2, 2], false), + Ok(Chunk::Parsed { + consumed: 3, + payload: Payload::CodeSectionStart { count: 2, .. }, + }), + ); + assert_matches!( + p.parse(&[0], false), + Ok(Chunk::Parsed { + consumed: 1, + payload: Payload::CodeSectionEntry(_), + }), + ); + assert_matches!(p.parse(&[], false), Ok(Chunk::NeedMoreData(1))); + assert_eq!( + p.parse(&[0], false).unwrap_err().message(), + "Unexpected EOF", + ); + + // trailing data is bad + let mut p = parser_after_header(); + assert_matches!( + p.parse(&[10, 3, 1], false), + Ok(Chunk::Parsed { + consumed: 3, + payload: Payload::CodeSectionStart { count: 1, .. }, + }), + ); + assert_matches!( + p.parse(&[0], false), + Ok(Chunk::Parsed { + consumed: 1, + payload: Payload::CodeSectionEntry(_), + }), + ); + assert_eq!( + p.parse(&[0], false).unwrap_err().message(), + "trailing bytes at end of section", + ); + } + + #[test] + fn module_code_errors() { + // no bytes to say size of section + assert!(parser_after_header().parse(&[103], true).is_err()); + // section must start with a u32 + assert!(parser_after_header().parse(&[103, 0], true).is_err()); + // EOF before we finish reading the section + assert!(parser_after_header().parse(&[103, 1], true).is_err()); + } + + #[test] + fn module_code_one() { + let mut p = parser_after_header(); + assert_matches!(p.parse(&[103], false), Ok(Chunk::NeedMoreData(1))); + assert_matches!(p.parse(&[103, 9], false), Ok(Chunk::NeedMoreData(1))); + // Module code section, 10 bytes large, one module. + assert_matches!( + p.parse(&[103, 10, 1], false), + Ok(Chunk::Parsed { + consumed: 3, + payload: Payload::ModuleCodeSectionStart { count: 1, .. }, + }) + ); + // Declare an empty module, which will be 8 bytes large for the header. + // Switch to the sub-parser on success. + let mut sub = match p.parse(&[8], false) { + Ok(Chunk::Parsed { + consumed: 1, + payload: Payload::ModuleCodeSectionEntry { parser, .. }, + }) => parser, + other => panic!("bad parse {:?}", other), + }; + + // Parse the header of the submodule with the sub-parser. + assert_matches!(sub.parse(&[], false), Ok(Chunk::NeedMoreData(4))); + assert_matches!(sub.parse(b"\0asm", false), Ok(Chunk::NeedMoreData(4))); + assert_matches!( + sub.parse(b"\0asm\x01\0\0\0", false), + Ok(Chunk::Parsed { + consumed: 8, + payload: Payload::Version { num: 1, .. }, + }), + ); + + // The sub-parser should be byte-limited so the next byte shouldn't get + // consumed, it's intended for the parent parser. + assert_matches!( + sub.parse(&[10], false), + Ok(Chunk::Parsed { + consumed: 0, + payload: Payload::End, + }), + ); + + // The parent parser should now be back to resuming, and we simulate it + // being done with bytes to ensure that it's safely at the end, + // completing the module code section. + assert_matches!(p.parse(&[], false), Ok(Chunk::NeedMoreData(1))); + assert_matches!( + p.parse(&[], true), + Ok(Chunk::Parsed { + consumed: 0, + payload: Payload::End, + }), + ); + } + + #[test] + fn nested_section_too_big() { + let mut p = parser_after_header(); + // Module code section, 12 bytes large, one module. This leaves 11 bytes + // of payload for the module definition itself. + assert_matches!( + p.parse(&[103, 12, 1], false), + Ok(Chunk::Parsed { + consumed: 3, + payload: Payload::ModuleCodeSectionStart { count: 1, .. }, + }) + ); + // Use one byte to say we're a 10 byte module, which fits exactly within + // our module code section. + let mut sub = match p.parse(&[10], false) { + Ok(Chunk::Parsed { + consumed: 1, + payload: Payload::ModuleCodeSectionEntry { parser, .. }, + }) => parser, + other => panic!("bad parse {:?}", other), + }; + + // use 8 bytes to parse the header, leaving 2 remaining bytes in our + // module. + assert_matches!( + sub.parse(b"\0asm\x01\0\0\0", false), + Ok(Chunk::Parsed { + consumed: 8, + payload: Payload::Version { num: 1, .. }, + }), + ); + + // We can't parse a section which declares its bigger than the outer + // module. This is section 1, one byte big, with one content byte. The + // content byte, however, lives outside of the parent's module code + // section. + assert_eq!( + sub.parse(&[1, 1, 0], false).unwrap_err().message(), + "section too large", + ); + } +} diff --git a/third_party/rust/wasmparser/src/primitives.rs b/third_party/rust/wasmparser/src/primitives.rs new file mode 100644 index 0000000000..4c64b28260 --- /dev/null +++ b/third_party/rust/wasmparser/src/primitives.rs @@ -0,0 +1,809 @@ +/* Copyright 2018 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use std::error::Error; +use std::fmt; +use std::result; + +#[derive(Debug, Clone)] +pub struct BinaryReaderError { + // Wrap the actual error data in a `Box` so that the error is just one + // word. This means that we can continue returning small `Result`s in + // registers. + pub(crate) inner: Box<BinaryReaderErrorInner>, +} + +#[derive(Debug, Clone)] +pub(crate) struct BinaryReaderErrorInner { + pub(crate) message: String, + pub(crate) offset: usize, + pub(crate) needed_hint: Option<usize>, +} + +pub type Result<T, E = BinaryReaderError> = result::Result<T, E>; + +impl Error for BinaryReaderError {} + +impl fmt::Display for BinaryReaderError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!( + f, + "{} (at offset {})", + self.inner.message, self.inner.offset + ) + } +} + +impl BinaryReaderError { + pub(crate) fn new(message: impl Into<String>, offset: usize) -> Self { + let message = message.into(); + BinaryReaderError { + inner: Box::new(BinaryReaderErrorInner { + message, + offset, + needed_hint: None, + }), + } + } + + pub(crate) fn eof(offset: usize, needed_hint: usize) -> Self { + BinaryReaderError { + inner: Box::new(BinaryReaderErrorInner { + message: "Unexpected EOF".to_string(), + offset, + needed_hint: Some(needed_hint), + }), + } + } + + /// Get this error's message. + pub fn message(&self) -> &str { + &self.inner.message + } + + /// Get the offset within the Wasm binary where the error occured. + pub fn offset(&self) -> usize { + self.inner.offset + } +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] +pub enum CustomSectionKind { + Unknown, + Name, + Producers, + SourceMappingURL, + Reloc, + Linking, +} + +/// Section code as defined [here]. +/// +/// [here]: https://webassembly.github.io/spec/core/binary/modules.html#sections +#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] +pub enum SectionCode<'a> { + Custom { + name: &'a str, + kind: CustomSectionKind, + }, + Type, // Function signature declarations + Alias, // Aliased indices from nested/parent modules + Import, // Import declarations + Module, // Module declarations + Instance, // Instance definitions + Function, // Function declarations + Table, // Indirect function table and other tables + Memory, // Memory attributes + Global, // Global declarations + Export, // Exports + Start, // Start function declaration + Element, // Elements section + ModuleCode, // Module definitions + Code, // Function bodies (code) + Data, // Data segments + DataCount, // Count of passive data segments +} + +/// Types as defined [here]. +/// +/// [here]: https://webassembly.github.io/spec/core/syntax/types.html#types +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +pub enum Type { + I32, + I64, + F32, + F64, + V128, + FuncRef, + ExternRef, + Func, + EmptyBlockType, +} + +/// Either a value type or a function type. +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum TypeOrFuncType { + /// A value type. + /// + /// When used as the type for a block, this type is the optional result + /// type: `[] -> [t?]`. + Type(Type), + + /// A function type (referenced as an index into the types section). + FuncType(u32), +} + +/// External types as defined [here]. +/// +/// [here]: https://webassembly.github.io/spec/core/syntax/types.html#external-types +#[derive(Debug, Copy, Clone)] +pub enum ExternalKind { + Function, + Table, + Memory, + Global, + Type, + Module, + Instance, +} + +#[derive(Debug, Clone)] +pub enum TypeDef<'a> { + Func(FuncType), + Instance(InstanceType<'a>), + Module(ModuleType<'a>), +} + +#[derive(Debug, Clone, Eq, PartialEq, Hash)] +pub struct FuncType { + pub params: Box<[Type]>, + pub returns: Box<[Type]>, +} + +#[derive(Debug, Clone)] +pub struct InstanceType<'a> { + pub exports: Box<[ExportType<'a>]>, +} + +#[derive(Debug, Clone)] +pub struct ModuleType<'a> { + pub imports: Box<[crate::Import<'a>]>, + pub exports: Box<[ExportType<'a>]>, +} + +#[derive(Debug, Clone)] +pub struct ExportType<'a> { + pub name: &'a str, + pub ty: ImportSectionEntryType, +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub struct ResizableLimits { + pub initial: u32, + pub maximum: Option<u32>, +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub struct ResizableLimits64 { + pub initial: u64, + pub maximum: Option<u64>, +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub struct TableType { + pub element_type: Type, + pub limits: ResizableLimits, +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum MemoryType { + M32 { + limits: ResizableLimits, + shared: bool, + }, + M64 { + limits: ResizableLimits64, + shared: bool, + }, +} + +impl MemoryType { + pub fn index_type(&self) -> Type { + match self { + MemoryType::M32 { .. } => Type::I32, + MemoryType::M64 { .. } => Type::I64, + } + } +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub struct GlobalType { + pub content_type: Type, + pub mutable: bool, +} + +#[derive(Debug, Copy, Clone)] +pub enum ImportSectionEntryType { + Function(u32), + Table(TableType), + Memory(MemoryType), + Global(GlobalType), + Module(u32), + Instance(u32), +} + +#[derive(Debug, Copy, Clone)] +pub struct MemoryImmediate { + /// Alignment, stored as `n` where the actual alignment is `2^n` + pub align: u8, + pub offset: u32, + pub memory: u32, +} + +#[derive(Debug, Copy, Clone)] +pub struct Naming<'a> { + pub index: u32, + pub name: &'a str, +} + +#[derive(Debug, Copy, Clone)] +pub enum NameType { + Module, + Function, + Local, +} + +#[derive(Debug, Copy, Clone)] +pub enum LinkingType { + StackPointer(u32), +} + +#[derive(Debug, Copy, Clone)] +pub enum RelocType { + FunctionIndexLEB, + TableIndexSLEB, + TableIndexI32, + GlobalAddrLEB, + GlobalAddrSLEB, + GlobalAddrI32, + TypeIndexLEB, + GlobalIndexLEB, +} + +/// A br_table entries representation. +#[derive(Clone)] +pub struct BrTable<'a> { + pub(crate) reader: crate::BinaryReader<'a>, + pub(crate) cnt: usize, +} + +/// An IEEE binary32 immediate floating point value, represented as a u32 +/// containing the bitpattern. +/// +/// All bit patterns are allowed. +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] +pub struct Ieee32(pub(crate) u32); + +impl Ieee32 { + pub fn bits(self) -> u32 { + self.0 + } +} + +/// An IEEE binary64 immediate floating point value, represented as a u64 +/// containing the bitpattern. +/// +/// All bit patterns are allowed. +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] +pub struct Ieee64(pub(crate) u64); + +impl Ieee64 { + pub fn bits(self) -> u64 { + self.0 + } +} + +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] +pub struct V128(pub(crate) [u8; 16]); + +impl V128 { + pub fn bytes(&self) -> &[u8; 16] { + &self.0 + } +} + +pub type SIMDLaneIndex = u8; + +/// Instructions as defined [here]. +/// +/// [here]: https://webassembly.github.io/spec/core/binary/instructions.html +#[derive(Debug, Clone)] +pub enum Operator<'a> { + Unreachable, + Nop, + Block { ty: TypeOrFuncType }, + Loop { ty: TypeOrFuncType }, + If { ty: TypeOrFuncType }, + Else, + End, + Br { relative_depth: u32 }, + BrIf { relative_depth: u32 }, + BrTable { table: BrTable<'a> }, + Return, + Call { function_index: u32 }, + CallIndirect { index: u32, table_index: u32 }, + ReturnCall { function_index: u32 }, + ReturnCallIndirect { index: u32, table_index: u32 }, + Drop, + Select, + TypedSelect { ty: Type }, + LocalGet { local_index: u32 }, + LocalSet { local_index: u32 }, + LocalTee { local_index: u32 }, + GlobalGet { global_index: u32 }, + GlobalSet { global_index: u32 }, + I32Load { memarg: MemoryImmediate }, + I64Load { memarg: MemoryImmediate }, + F32Load { memarg: MemoryImmediate }, + F64Load { memarg: MemoryImmediate }, + I32Load8S { memarg: MemoryImmediate }, + I32Load8U { memarg: MemoryImmediate }, + I32Load16S { memarg: MemoryImmediate }, + I32Load16U { memarg: MemoryImmediate }, + I64Load8S { memarg: MemoryImmediate }, + I64Load8U { memarg: MemoryImmediate }, + I64Load16S { memarg: MemoryImmediate }, + I64Load16U { memarg: MemoryImmediate }, + I64Load32S { memarg: MemoryImmediate }, + I64Load32U { memarg: MemoryImmediate }, + I32Store { memarg: MemoryImmediate }, + I64Store { memarg: MemoryImmediate }, + F32Store { memarg: MemoryImmediate }, + F64Store { memarg: MemoryImmediate }, + I32Store8 { memarg: MemoryImmediate }, + I32Store16 { memarg: MemoryImmediate }, + I64Store8 { memarg: MemoryImmediate }, + I64Store16 { memarg: MemoryImmediate }, + I64Store32 { memarg: MemoryImmediate }, + MemorySize { mem: u32, mem_byte: u8 }, + MemoryGrow { mem: u32, mem_byte: u8 }, + I32Const { value: i32 }, + I64Const { value: i64 }, + F32Const { value: Ieee32 }, + F64Const { value: Ieee64 }, + RefNull { ty: Type }, + RefIsNull, + RefFunc { function_index: u32 }, + I32Eqz, + I32Eq, + I32Ne, + I32LtS, + I32LtU, + I32GtS, + I32GtU, + I32LeS, + I32LeU, + I32GeS, + I32GeU, + I64Eqz, + I64Eq, + I64Ne, + I64LtS, + I64LtU, + I64GtS, + I64GtU, + I64LeS, + I64LeU, + I64GeS, + I64GeU, + F32Eq, + F32Ne, + F32Lt, + F32Gt, + F32Le, + F32Ge, + F64Eq, + F64Ne, + F64Lt, + F64Gt, + F64Le, + F64Ge, + I32Clz, + I32Ctz, + I32Popcnt, + I32Add, + I32Sub, + I32Mul, + I32DivS, + I32DivU, + I32RemS, + I32RemU, + I32And, + I32Or, + I32Xor, + I32Shl, + I32ShrS, + I32ShrU, + I32Rotl, + I32Rotr, + I64Clz, + I64Ctz, + I64Popcnt, + I64Add, + I64Sub, + I64Mul, + I64DivS, + I64DivU, + I64RemS, + I64RemU, + I64And, + I64Or, + I64Xor, + I64Shl, + I64ShrS, + I64ShrU, + I64Rotl, + I64Rotr, + F32Abs, + F32Neg, + F32Ceil, + F32Floor, + F32Trunc, + F32Nearest, + F32Sqrt, + F32Add, + F32Sub, + F32Mul, + F32Div, + F32Min, + F32Max, + F32Copysign, + F64Abs, + F64Neg, + F64Ceil, + F64Floor, + F64Trunc, + F64Nearest, + F64Sqrt, + F64Add, + F64Sub, + F64Mul, + F64Div, + F64Min, + F64Max, + F64Copysign, + I32WrapI64, + I32TruncF32S, + I32TruncF32U, + I32TruncF64S, + I32TruncF64U, + I64ExtendI32S, + I64ExtendI32U, + I64TruncF32S, + I64TruncF32U, + I64TruncF64S, + I64TruncF64U, + F32ConvertI32S, + F32ConvertI32U, + F32ConvertI64S, + F32ConvertI64U, + F32DemoteF64, + F64ConvertI32S, + F64ConvertI32U, + F64ConvertI64S, + F64ConvertI64U, + F64PromoteF32, + I32ReinterpretF32, + I64ReinterpretF64, + F32ReinterpretI32, + F64ReinterpretI64, + I32Extend8S, + I32Extend16S, + I64Extend8S, + I64Extend16S, + I64Extend32S, + + // 0xFC operators + // Non-trapping Float-to-int Conversions + I32TruncSatF32S, + I32TruncSatF32U, + I32TruncSatF64S, + I32TruncSatF64U, + I64TruncSatF32S, + I64TruncSatF32U, + I64TruncSatF64S, + I64TruncSatF64U, + + // 0xFC operators + // bulk memory https://github.com/WebAssembly/bulk-memory-operations/blob/master/proposals/bulk-memory-operations/Overview.md + MemoryInit { segment: u32, mem: u32 }, + DataDrop { segment: u32 }, + MemoryCopy { src: u32, dst: u32 }, + MemoryFill { mem: u32 }, + TableInit { segment: u32, table: u32 }, + ElemDrop { segment: u32 }, + TableCopy { dst_table: u32, src_table: u32 }, + TableFill { table: u32 }, + TableGet { table: u32 }, + TableSet { table: u32 }, + TableGrow { table: u32 }, + TableSize { table: u32 }, + + // 0xFE operators + // https://github.com/WebAssembly/threads/blob/master/proposals/threads/Overview.md + MemoryAtomicNotify { memarg: MemoryImmediate }, + MemoryAtomicWait32 { memarg: MemoryImmediate }, + MemoryAtomicWait64 { memarg: MemoryImmediate }, + AtomicFence { flags: u8 }, + I32AtomicLoad { memarg: MemoryImmediate }, + I64AtomicLoad { memarg: MemoryImmediate }, + I32AtomicLoad8U { memarg: MemoryImmediate }, + I32AtomicLoad16U { memarg: MemoryImmediate }, + I64AtomicLoad8U { memarg: MemoryImmediate }, + I64AtomicLoad16U { memarg: MemoryImmediate }, + I64AtomicLoad32U { memarg: MemoryImmediate }, + I32AtomicStore { memarg: MemoryImmediate }, + I64AtomicStore { memarg: MemoryImmediate }, + I32AtomicStore8 { memarg: MemoryImmediate }, + I32AtomicStore16 { memarg: MemoryImmediate }, + I64AtomicStore8 { memarg: MemoryImmediate }, + I64AtomicStore16 { memarg: MemoryImmediate }, + I64AtomicStore32 { memarg: MemoryImmediate }, + I32AtomicRmwAdd { memarg: MemoryImmediate }, + I64AtomicRmwAdd { memarg: MemoryImmediate }, + I32AtomicRmw8AddU { memarg: MemoryImmediate }, + I32AtomicRmw16AddU { memarg: MemoryImmediate }, + I64AtomicRmw8AddU { memarg: MemoryImmediate }, + I64AtomicRmw16AddU { memarg: MemoryImmediate }, + I64AtomicRmw32AddU { memarg: MemoryImmediate }, + I32AtomicRmwSub { memarg: MemoryImmediate }, + I64AtomicRmwSub { memarg: MemoryImmediate }, + I32AtomicRmw8SubU { memarg: MemoryImmediate }, + I32AtomicRmw16SubU { memarg: MemoryImmediate }, + I64AtomicRmw8SubU { memarg: MemoryImmediate }, + I64AtomicRmw16SubU { memarg: MemoryImmediate }, + I64AtomicRmw32SubU { memarg: MemoryImmediate }, + I32AtomicRmwAnd { memarg: MemoryImmediate }, + I64AtomicRmwAnd { memarg: MemoryImmediate }, + I32AtomicRmw8AndU { memarg: MemoryImmediate }, + I32AtomicRmw16AndU { memarg: MemoryImmediate }, + I64AtomicRmw8AndU { memarg: MemoryImmediate }, + I64AtomicRmw16AndU { memarg: MemoryImmediate }, + I64AtomicRmw32AndU { memarg: MemoryImmediate }, + I32AtomicRmwOr { memarg: MemoryImmediate }, + I64AtomicRmwOr { memarg: MemoryImmediate }, + I32AtomicRmw8OrU { memarg: MemoryImmediate }, + I32AtomicRmw16OrU { memarg: MemoryImmediate }, + I64AtomicRmw8OrU { memarg: MemoryImmediate }, + I64AtomicRmw16OrU { memarg: MemoryImmediate }, + I64AtomicRmw32OrU { memarg: MemoryImmediate }, + I32AtomicRmwXor { memarg: MemoryImmediate }, + I64AtomicRmwXor { memarg: MemoryImmediate }, + I32AtomicRmw8XorU { memarg: MemoryImmediate }, + I32AtomicRmw16XorU { memarg: MemoryImmediate }, + I64AtomicRmw8XorU { memarg: MemoryImmediate }, + I64AtomicRmw16XorU { memarg: MemoryImmediate }, + I64AtomicRmw32XorU { memarg: MemoryImmediate }, + I32AtomicRmwXchg { memarg: MemoryImmediate }, + I64AtomicRmwXchg { memarg: MemoryImmediate }, + I32AtomicRmw8XchgU { memarg: MemoryImmediate }, + I32AtomicRmw16XchgU { memarg: MemoryImmediate }, + I64AtomicRmw8XchgU { memarg: MemoryImmediate }, + I64AtomicRmw16XchgU { memarg: MemoryImmediate }, + I64AtomicRmw32XchgU { memarg: MemoryImmediate }, + I32AtomicRmwCmpxchg { memarg: MemoryImmediate }, + I64AtomicRmwCmpxchg { memarg: MemoryImmediate }, + I32AtomicRmw8CmpxchgU { memarg: MemoryImmediate }, + I32AtomicRmw16CmpxchgU { memarg: MemoryImmediate }, + I64AtomicRmw8CmpxchgU { memarg: MemoryImmediate }, + I64AtomicRmw16CmpxchgU { memarg: MemoryImmediate }, + I64AtomicRmw32CmpxchgU { memarg: MemoryImmediate }, + + // 0xFD operators + // SIMD https://github.com/WebAssembly/simd/blob/master/proposals/simd/BinarySIMD.md + V128Load { memarg: MemoryImmediate }, + V128Store { memarg: MemoryImmediate }, + V128Const { value: V128 }, + I8x16Splat, + I8x16ExtractLaneS { lane: SIMDLaneIndex }, + I8x16ExtractLaneU { lane: SIMDLaneIndex }, + I8x16ReplaceLane { lane: SIMDLaneIndex }, + I16x8Splat, + I16x8ExtractLaneS { lane: SIMDLaneIndex }, + I16x8ExtractLaneU { lane: SIMDLaneIndex }, + I16x8ReplaceLane { lane: SIMDLaneIndex }, + I32x4Splat, + I32x4ExtractLane { lane: SIMDLaneIndex }, + I32x4ReplaceLane { lane: SIMDLaneIndex }, + I64x2Splat, + I64x2ExtractLane { lane: SIMDLaneIndex }, + I64x2ReplaceLane { lane: SIMDLaneIndex }, + F32x4Splat, + F32x4ExtractLane { lane: SIMDLaneIndex }, + F32x4ReplaceLane { lane: SIMDLaneIndex }, + F64x2Splat, + F64x2ExtractLane { lane: SIMDLaneIndex }, + F64x2ReplaceLane { lane: SIMDLaneIndex }, + I8x16Eq, + I8x16Ne, + I8x16LtS, + I8x16LtU, + I8x16GtS, + I8x16GtU, + I8x16LeS, + I8x16LeU, + I8x16GeS, + I8x16GeU, + I16x8Eq, + I16x8Ne, + I16x8LtS, + I16x8LtU, + I16x8GtS, + I16x8GtU, + I16x8LeS, + I16x8LeU, + I16x8GeS, + I16x8GeU, + I32x4Eq, + I32x4Ne, + I32x4LtS, + I32x4LtU, + I32x4GtS, + I32x4GtU, + I32x4LeS, + I32x4LeU, + I32x4GeS, + I32x4GeU, + F32x4Eq, + F32x4Ne, + F32x4Lt, + F32x4Gt, + F32x4Le, + F32x4Ge, + F64x2Eq, + F64x2Ne, + F64x2Lt, + F64x2Gt, + F64x2Le, + F64x2Ge, + V128Not, + V128And, + V128AndNot, + V128Or, + V128Xor, + V128Bitselect, + I8x16Abs, + I8x16Neg, + I8x16AnyTrue, + I8x16AllTrue, + I8x16Bitmask, + I8x16Shl, + I8x16ShrS, + I8x16ShrU, + I8x16Add, + I8x16AddSatS, + I8x16AddSatU, + I8x16Sub, + I8x16SubSatS, + I8x16SubSatU, + I8x16MinS, + I8x16MinU, + I8x16MaxS, + I8x16MaxU, + I16x8Abs, + I16x8Neg, + I16x8AnyTrue, + I16x8AllTrue, + I16x8Bitmask, + I16x8Shl, + I16x8ShrS, + I16x8ShrU, + I16x8Add, + I16x8AddSatS, + I16x8AddSatU, + I16x8Sub, + I16x8SubSatS, + I16x8SubSatU, + I16x8Mul, + I16x8MinS, + I16x8MinU, + I16x8MaxS, + I16x8MaxU, + I32x4Abs, + I32x4Neg, + I32x4AnyTrue, + I32x4AllTrue, + I32x4Bitmask, + I32x4Shl, + I32x4ShrS, + I32x4ShrU, + I32x4Add, + I32x4Sub, + I32x4Mul, + I32x4MinS, + I32x4MinU, + I32x4MaxS, + I32x4MaxU, + I32x4DotI16x8S, + I64x2Neg, + I64x2Shl, + I64x2ShrS, + I64x2ShrU, + I64x2Add, + I64x2Sub, + I64x2Mul, + F32x4Ceil, + F32x4Floor, + F32x4Trunc, + F32x4Nearest, + F64x2Ceil, + F64x2Floor, + F64x2Trunc, + F64x2Nearest, + F32x4Abs, + F32x4Neg, + F32x4Sqrt, + F32x4Add, + F32x4Sub, + F32x4Mul, + F32x4Div, + F32x4Min, + F32x4Max, + F32x4PMin, + F32x4PMax, + F64x2Abs, + F64x2Neg, + F64x2Sqrt, + F64x2Add, + F64x2Sub, + F64x2Mul, + F64x2Div, + F64x2Min, + F64x2Max, + F64x2PMin, + F64x2PMax, + I32x4TruncSatF32x4S, + I32x4TruncSatF32x4U, + F32x4ConvertI32x4S, + F32x4ConvertI32x4U, + I8x16Swizzle, + I8x16Shuffle { lanes: [SIMDLaneIndex; 16] }, + V128Load8Splat { memarg: MemoryImmediate }, + V128Load16Splat { memarg: MemoryImmediate }, + V128Load32Splat { memarg: MemoryImmediate }, + V128Load32Zero { memarg: MemoryImmediate }, + V128Load64Splat { memarg: MemoryImmediate }, + V128Load64Zero { memarg: MemoryImmediate }, + I8x16NarrowI16x8S, + I8x16NarrowI16x8U, + I16x8NarrowI32x4S, + I16x8NarrowI32x4U, + I16x8WidenLowI8x16S, + I16x8WidenHighI8x16S, + I16x8WidenLowI8x16U, + I16x8WidenHighI8x16U, + I32x4WidenLowI16x8S, + I32x4WidenHighI16x8S, + I32x4WidenLowI16x8U, + I32x4WidenHighI16x8U, + V128Load8x8S { memarg: MemoryImmediate }, + V128Load8x8U { memarg: MemoryImmediate }, + V128Load16x4S { memarg: MemoryImmediate }, + V128Load16x4U { memarg: MemoryImmediate }, + V128Load32x2S { memarg: MemoryImmediate }, + V128Load32x2U { memarg: MemoryImmediate }, + I8x16RoundingAverageU, + I16x8RoundingAverageU, +} diff --git a/third_party/rust/wasmparser/src/readers/alias_section.rs b/third_party/rust/wasmparser/src/readers/alias_section.rs new file mode 100644 index 0000000000..bf64c18069 --- /dev/null +++ b/third_party/rust/wasmparser/src/readers/alias_section.rs @@ -0,0 +1,88 @@ +use crate::{ + BinaryReader, BinaryReaderError, ExternalKind, Range, Result, SectionIteratorLimited, + SectionReader, SectionWithLimitedItems, +}; + +#[derive(Clone)] +pub struct AliasSectionReader<'a> { + reader: BinaryReader<'a>, + count: u32, +} + +#[derive(Debug)] +pub struct Alias { + pub instance: AliasedInstance, + pub kind: ExternalKind, + pub index: u32, +} + +#[derive(Debug)] +pub enum AliasedInstance { + Parent, + Child(u32), +} + +impl<'a> AliasSectionReader<'a> { + pub fn new(data: &'a [u8], offset: usize) -> Result<AliasSectionReader<'a>> { + let mut reader = BinaryReader::new_with_offset(data, offset); + let count = reader.read_var_u32()?; + Ok(AliasSectionReader { reader, count }) + } + + pub fn original_position(&self) -> usize { + self.reader.original_position() + } + + pub fn get_count(&self) -> u32 { + self.count + } + + pub fn read(&mut self) -> Result<Alias> { + Ok(Alias { + instance: match self.reader.read_u8()? { + 0x00 => AliasedInstance::Child(self.reader.read_var_u32()?), + 0x01 => AliasedInstance::Parent, + _ => { + return Err(BinaryReaderError::new( + "invalid byte in alias", + self.original_position() - 1, + )) + } + }, + kind: self.reader.read_external_kind()?, + index: self.reader.read_var_u32()?, + }) + } +} + +impl<'a> SectionReader for AliasSectionReader<'a> { + type Item = Alias; + + fn read(&mut self) -> Result<Self::Item> { + AliasSectionReader::read(self) + } + fn eof(&self) -> bool { + self.reader.eof() + } + fn original_position(&self) -> usize { + AliasSectionReader::original_position(self) + } + fn range(&self) -> Range { + self.reader.range() + } +} + +impl<'a> SectionWithLimitedItems for AliasSectionReader<'a> { + fn get_count(&self) -> u32 { + AliasSectionReader::get_count(self) + } +} + +impl<'a> IntoIterator for AliasSectionReader<'a> { + type Item = Result<Alias>; + type IntoIter = SectionIteratorLimited<AliasSectionReader<'a>>; + + fn into_iter(self) -> Self::IntoIter { + SectionIteratorLimited::new(self) + } +} diff --git a/third_party/rust/wasmparser/src/readers/code_section.rs b/third_party/rust/wasmparser/src/readers/code_section.rs new file mode 100644 index 0000000000..f17f66ac57 --- /dev/null +++ b/third_party/rust/wasmparser/src/readers/code_section.rs @@ -0,0 +1,238 @@ +/* Copyright 2018 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use super::{ + BinaryReader, BinaryReaderError, OperatorsReader, Range, Result, SectionIteratorLimited, + SectionReader, SectionWithLimitedItems, Type, +}; + +#[derive(Debug, Clone)] +pub struct FunctionBody<'a> { + offset: usize, + data: &'a [u8], +} + +impl<'a> FunctionBody<'a> { + pub fn new(offset: usize, data: &'a [u8]) -> Self { + Self { offset, data } + } + + pub fn get_binary_reader<'b>(&self) -> BinaryReader<'b> + where + 'a: 'b, + { + BinaryReader::new_with_offset(self.data, self.offset) + } + + fn skip_locals(reader: &mut BinaryReader) -> Result<()> { + let count = reader.read_var_u32()?; + for _ in 0..count { + reader.skip_var_32()?; + reader.skip_type()?; + } + Ok(()) + } + + pub fn get_locals_reader<'b>(&self) -> Result<LocalsReader<'b>> + where + 'a: 'b, + { + let mut reader = BinaryReader::new_with_offset(self.data, self.offset); + let count = reader.read_var_u32()?; + Ok(LocalsReader { reader, count }) + } + + pub fn get_operators_reader<'b>(&self) -> Result<OperatorsReader<'b>> + where + 'a: 'b, + { + let mut reader = BinaryReader::new_with_offset(self.data, self.offset); + Self::skip_locals(&mut reader)?; + let pos = reader.position; + Ok(OperatorsReader::new(&self.data[pos..], self.offset + pos)) + } + + pub fn range(&self) -> Range { + Range { + start: self.offset, + end: self.offset + self.data.len(), + } + } +} + +pub struct LocalsReader<'a> { + reader: BinaryReader<'a>, + count: u32, +} + +impl<'a> LocalsReader<'a> { + pub fn get_count(&self) -> u32 { + self.count + } + + pub fn original_position(&self) -> usize { + self.reader.original_position() + } + + pub fn read(&mut self) -> Result<(u32, Type)> { + let count = self.reader.read_var_u32()?; + let value_type = self.reader.read_type()?; + Ok((count, value_type)) + } +} + +pub struct CodeSectionReader<'a> { + reader: BinaryReader<'a>, + count: u32, +} + +impl<'a> IntoIterator for LocalsReader<'a> { + type Item = Result<(u32, Type)>; + type IntoIter = LocalsIterator<'a>; + fn into_iter(self) -> Self::IntoIter { + let count = self.count; + LocalsIterator { + reader: self, + left: count, + err: false, + } + } +} + +pub struct LocalsIterator<'a> { + reader: LocalsReader<'a>, + left: u32, + err: bool, +} + +impl<'a> Iterator for LocalsIterator<'a> { + type Item = Result<(u32, Type)>; + fn next(&mut self) -> Option<Self::Item> { + if self.err || self.left == 0 { + return None; + } + let result = self.reader.read(); + self.err = result.is_err(); + self.left -= 1; + Some(result) + } + fn size_hint(&self) -> (usize, Option<usize>) { + let count = self.reader.get_count() as usize; + (count, Some(count)) + } +} + +impl<'a> CodeSectionReader<'a> { + pub fn new(data: &'a [u8], offset: usize) -> Result<CodeSectionReader<'a>> { + let mut reader = BinaryReader::new_with_offset(data, offset); + let count = reader.read_var_u32()?; + Ok(CodeSectionReader { reader, count }) + } + + pub fn original_position(&self) -> usize { + self.reader.original_position() + } + + pub fn get_count(&self) -> u32 { + self.count + } + + fn verify_body_end(&self, end: usize) -> Result<()> { + if self.reader.buffer.len() < end { + return Err(BinaryReaderError::new( + "Function body extends past end of the code section", + self.reader.original_offset + self.reader.buffer.len(), + )); + } + Ok(()) + } + + /// Reads content of the code section. + /// + /// # Examples + /// ``` + /// use wasmparser::CodeSectionReader; + /// # let data: &[u8] = &[ + /// # 0x01, 0x03, 0x00, 0x01, 0x0b]; + /// let mut code_reader = CodeSectionReader::new(data, 0).unwrap(); + /// for _ in 0..code_reader.get_count() { + /// let body = code_reader.read().expect("function body"); + /// let mut binary_reader = body.get_binary_reader(); + /// assert!(binary_reader.read_var_u32().expect("local count") == 0); + /// let op = binary_reader.read_operator().expect("first operator"); + /// println!("First operator: {:?}", op); + /// } + /// ``` + pub fn read<'b>(&mut self) -> Result<FunctionBody<'b>> + where + 'a: 'b, + { + let size = self.reader.read_var_u32()? as usize; + let body_start = self.reader.position; + let body_end = body_start + size; + self.verify_body_end(body_end)?; + self.reader.skip_to(body_end); + Ok(FunctionBody { + offset: self.reader.original_offset + body_start, + data: &self.reader.buffer[body_start..body_end], + }) + } +} + +impl<'a> SectionReader for CodeSectionReader<'a> { + type Item = FunctionBody<'a>; + fn read(&mut self) -> Result<Self::Item> { + CodeSectionReader::read(self) + } + fn eof(&self) -> bool { + self.reader.eof() + } + fn original_position(&self) -> usize { + CodeSectionReader::original_position(self) + } + fn range(&self) -> Range { + self.reader.range() + } +} + +impl<'a> SectionWithLimitedItems for CodeSectionReader<'a> { + fn get_count(&self) -> u32 { + CodeSectionReader::get_count(self) + } +} + +impl<'a> IntoIterator for CodeSectionReader<'a> { + type Item = Result<FunctionBody<'a>>; + type IntoIter = SectionIteratorLimited<CodeSectionReader<'a>>; + + /// Implements iterator over the code section. + /// + /// # Examples + /// ``` + /// use wasmparser::CodeSectionReader; + /// # let data: &[u8] = &[ + /// # 0x01, 0x03, 0x00, 0x01, 0x0b]; + /// let mut code_reader = CodeSectionReader::new(data, 0).unwrap(); + /// for body in code_reader { + /// let mut binary_reader = body.expect("b").get_binary_reader(); + /// assert!(binary_reader.read_var_u32().expect("local count") == 0); + /// let op = binary_reader.read_operator().expect("first operator"); + /// println!("First operator: {:?}", op); + /// } + /// ``` + fn into_iter(self) -> Self::IntoIter { + SectionIteratorLimited::new(self) + } +} diff --git a/third_party/rust/wasmparser/src/readers/data_section.rs b/third_party/rust/wasmparser/src/readers/data_section.rs new file mode 100644 index 0000000000..b9675e2de8 --- /dev/null +++ b/third_party/rust/wasmparser/src/readers/data_section.rs @@ -0,0 +1,152 @@ +/* Copyright 2018 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use super::{ + BinaryReader, BinaryReaderError, InitExpr, Range, Result, SectionIteratorLimited, + SectionReader, SectionWithLimitedItems, +}; + +#[derive(Debug, Copy, Clone)] +pub struct Data<'a> { + pub kind: DataKind<'a>, + pub data: &'a [u8], +} + +#[derive(Debug, Copy, Clone)] +pub enum DataKind<'a> { + Passive, + Active { + memory_index: u32, + init_expr: InitExpr<'a>, + }, +} + +#[derive(Clone)] +pub struct DataSectionReader<'a> { + reader: BinaryReader<'a>, + count: u32, +} + +impl<'a> DataSectionReader<'a> { + pub fn new(data: &'a [u8], offset: usize) -> Result<DataSectionReader<'a>> { + let mut reader = BinaryReader::new_with_offset(data, offset); + let count = reader.read_var_u32()?; + Ok(DataSectionReader { reader, count }) + } + + pub fn original_position(&self) -> usize { + self.reader.original_position() + } + + pub fn get_count(&self) -> u32 { + self.count + } + + fn verify_data_end(&self, end: usize) -> Result<()> { + if self.reader.buffer.len() < end { + return Err(BinaryReaderError::new( + "Data segment extends past end of the data section", + self.reader.original_offset + self.reader.buffer.len(), + )); + } + Ok(()) + } + + /// Reads content of the data section. + /// + /// # Examples + /// ``` + /// use wasmparser::{DataSectionReader, DataKind}; + /// # let data: &[u8] = &[ + /// # 0x01, 0x00, 0x41, 0x80, 0x08, 0x0b, 0x04, 0x00, 0x00, 0x00, 0x00]; + /// let mut data_reader = DataSectionReader::new(data, 0).unwrap(); + /// for _ in 0..data_reader.get_count() { + /// let data = data_reader.read().expect("data"); + /// println!("Data: {:?}", data); + /// if let DataKind::Active { init_expr, .. } = data.kind { + /// let mut init_expr_reader = init_expr.get_binary_reader(); + /// let op = init_expr_reader.read_operator().expect("op"); + /// println!("Init const: {:?}", op); + /// } + /// } + /// ``` + pub fn read<'b>(&mut self) -> Result<Data<'b>> + where + 'a: 'b, + { + let flags = self.reader.read_var_u32()?; + let kind = if flags == 1 { + DataKind::Passive + } else { + let memory_index = match flags { + 0 => 0, + 2 => self.reader.read_var_u32()?, + _ => { + return Err(BinaryReaderError::new( + "invalid flags byte in data segment", + self.reader.original_position() - 1, + )); + } + }; + let init_expr = { + let expr_offset = self.reader.position; + self.reader.skip_init_expr()?; + let data = &self.reader.buffer[expr_offset..self.reader.position]; + InitExpr::new(data, self.reader.original_offset + expr_offset) + }; + DataKind::Active { + memory_index, + init_expr, + } + }; + let data_len = self.reader.read_var_u32()? as usize; + let data_end = self.reader.position + data_len; + self.verify_data_end(data_end)?; + let data = &self.reader.buffer[self.reader.position..data_end]; + self.reader.skip_to(data_end); + Ok(Data { kind, data }) + } +} + +impl<'a> SectionReader for DataSectionReader<'a> { + type Item = Data<'a>; + fn read(&mut self) -> Result<Self::Item> { + DataSectionReader::read(self) + } + fn eof(&self) -> bool { + self.reader.eof() + } + fn original_position(&self) -> usize { + DataSectionReader::original_position(self) + } + fn range(&self) -> Range { + self.reader.range() + } +} + +impl<'a> SectionWithLimitedItems for DataSectionReader<'a> { + fn get_count(&self) -> u32 { + DataSectionReader::get_count(self) + } +} + +impl<'a> IntoIterator for DataSectionReader<'a> { + type Item = Result<Data<'a>>; + type IntoIter = SectionIteratorLimited<DataSectionReader<'a>>; + + fn into_iter(self) -> Self::IntoIter { + SectionIteratorLimited::new(self) + } +} diff --git a/third_party/rust/wasmparser/src/readers/element_section.rs b/third_party/rust/wasmparser/src/readers/element_section.rs new file mode 100644 index 0000000000..f01c2d1f62 --- /dev/null +++ b/third_party/rust/wasmparser/src/readers/element_section.rs @@ -0,0 +1,290 @@ +/* Copyright 2018 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use super::{ + BinaryReader, BinaryReaderError, InitExpr, Range, Result, SectionIteratorLimited, + SectionReader, SectionWithLimitedItems, Type, +}; +use crate::{ExternalKind, Operator}; + +#[derive(Clone)] +pub struct Element<'a> { + pub kind: ElementKind<'a>, + pub items: ElementItems<'a>, + pub ty: Type, +} + +#[derive(Clone)] +pub enum ElementKind<'a> { + Passive, + Active { + table_index: u32, + init_expr: InitExpr<'a>, + }, + Declared, +} + +#[derive(Debug, Copy, Clone)] +pub struct ElementItems<'a> { + exprs: bool, + offset: usize, + data: &'a [u8], +} + +#[derive(Debug)] +pub enum ElementItem { + Null(Type), + Func(u32), +} + +impl<'a> ElementItems<'a> { + pub fn get_items_reader<'b>(&self) -> Result<ElementItemsReader<'b>> + where + 'a: 'b, + { + ElementItemsReader::new(self.data, self.offset, self.exprs) + } +} + +pub struct ElementItemsReader<'a> { + reader: BinaryReader<'a>, + count: u32, + exprs: bool, +} + +impl<'a> ElementItemsReader<'a> { + pub fn new(data: &[u8], offset: usize, exprs: bool) -> Result<ElementItemsReader> { + let mut reader = BinaryReader::new_with_offset(data, offset); + let count = reader.read_var_u32()?; + Ok(ElementItemsReader { + reader, + count, + exprs, + }) + } + + pub fn original_position(&self) -> usize { + self.reader.original_position() + } + + pub fn get_count(&self) -> u32 { + self.count + } + + pub fn uses_exprs(&self) -> bool { + self.exprs + } + + pub fn read(&mut self) -> Result<ElementItem> { + if self.exprs { + let offset = self.reader.original_position(); + let ret = match self.reader.read_operator()? { + Operator::RefNull { ty } => ElementItem::Null(ty), + Operator::RefFunc { function_index } => ElementItem::Func(function_index), + _ => return Err(BinaryReaderError::new("invalid passive segment", offset)), + }; + match self.reader.read_operator()? { + Operator::End => {} + _ => return Err(BinaryReaderError::new("invalid passive segment", offset)), + } + Ok(ret) + } else { + self.reader.read_var_u32().map(ElementItem::Func) + } + } +} + +impl<'a> IntoIterator for ElementItemsReader<'a> { + type Item = Result<ElementItem>; + type IntoIter = ElementItemsIterator<'a>; + fn into_iter(self) -> Self::IntoIter { + let count = self.count; + ElementItemsIterator { + reader: self, + left: count, + err: false, + } + } +} + +pub struct ElementItemsIterator<'a> { + reader: ElementItemsReader<'a>, + left: u32, + err: bool, +} + +impl<'a> Iterator for ElementItemsIterator<'a> { + type Item = Result<ElementItem>; + fn next(&mut self) -> Option<Self::Item> { + if self.err || self.left == 0 { + return None; + } + let result = self.reader.read(); + self.err = result.is_err(); + self.left -= 1; + Some(result) + } + fn size_hint(&self) -> (usize, Option<usize>) { + let count = self.reader.get_count() as usize; + (count, Some(count)) + } +} + +#[derive(Clone)] +pub struct ElementSectionReader<'a> { + reader: BinaryReader<'a>, + count: u32, +} + +impl<'a> ElementSectionReader<'a> { + pub fn new(data: &'a [u8], offset: usize) -> Result<ElementSectionReader<'a>> { + let mut reader = BinaryReader::new_with_offset(data, offset); + let count = reader.read_var_u32()?; + Ok(ElementSectionReader { reader, count }) + } + + pub fn original_position(&self) -> usize { + self.reader.original_position() + } + + pub fn get_count(&self) -> u32 { + self.count + } + + /// Reads content of the element section. + /// + /// # Examples + /// + /// ```no_run + /// # let data: &[u8] = &[]; + /// use wasmparser::{ElementSectionReader, ElementKind}; + /// let mut element_reader = ElementSectionReader::new(data, 0).unwrap(); + /// for _ in 0..element_reader.get_count() { + /// let element = element_reader.read().expect("element"); + /// if let ElementKind::Active { init_expr, .. } = element.kind { + /// let mut init_expr_reader = init_expr.get_binary_reader(); + /// let op = init_expr_reader.read_operator().expect("op"); + /// println!("Init const: {:?}", op); + /// } + /// let mut items_reader = element.items.get_items_reader().expect("items reader"); + /// for _ in 0..items_reader.get_count() { + /// let item = items_reader.read().expect("item"); + /// println!(" Item: {:?}", item); + /// } + /// } + /// ``` + pub fn read<'b>(&mut self) -> Result<Element<'b>> + where + 'a: 'b, + { + let flags = self.reader.read_var_u32()?; + if (flags & !0b111) != 0 { + return Err(BinaryReaderError::new( + "invalid flags byte in element segment", + self.reader.original_position() - 1, + )); + } + let kind = if flags & 0b001 != 0 { + if flags & 0b010 != 0 { + ElementKind::Declared + } else { + ElementKind::Passive + } + } else { + let table_index = if flags & 0b010 == 0 { + 0 + } else { + self.reader.read_var_u32()? + }; + let init_expr = { + let expr_offset = self.reader.position; + self.reader.skip_init_expr()?; + let data = &self.reader.buffer[expr_offset..self.reader.position]; + InitExpr::new(data, self.reader.original_offset + expr_offset) + }; + ElementKind::Active { + table_index, + init_expr, + } + }; + let exprs = flags & 0b100 != 0; + let ty = if flags & 0b011 != 0 { + if exprs { + self.reader.read_type()? + } else { + match self.reader.read_external_kind()? { + ExternalKind::Function => Type::FuncRef, + _ => { + return Err(BinaryReaderError::new( + "only the function external type is supported in elem segment", + self.reader.original_position() - 1, + )); + } + } + } + } else { + Type::FuncRef + }; + let data_start = self.reader.position; + let items_count = self.reader.read_var_u32()?; + if exprs { + for _ in 0..items_count { + self.reader.skip_init_expr()?; + } + } else { + for _ in 0..items_count { + self.reader.skip_var_32()?; + } + } + let data_end = self.reader.position; + let items = ElementItems { + offset: self.reader.original_offset + data_start, + data: &self.reader.buffer[data_start..data_end], + exprs, + }; + Ok(Element { kind, items, ty }) + } +} + +impl<'a> SectionReader for ElementSectionReader<'a> { + type Item = Element<'a>; + fn read(&mut self) -> Result<Self::Item> { + ElementSectionReader::read(self) + } + fn eof(&self) -> bool { + self.reader.eof() + } + fn original_position(&self) -> usize { + ElementSectionReader::original_position(self) + } + fn range(&self) -> Range { + self.reader.range() + } +} + +impl<'a> SectionWithLimitedItems for ElementSectionReader<'a> { + fn get_count(&self) -> u32 { + ElementSectionReader::get_count(self) + } +} + +impl<'a> IntoIterator for ElementSectionReader<'a> { + type Item = Result<Element<'a>>; + type IntoIter = SectionIteratorLimited<ElementSectionReader<'a>>; + + fn into_iter(self) -> Self::IntoIter { + SectionIteratorLimited::new(self) + } +} diff --git a/third_party/rust/wasmparser/src/readers/export_section.rs b/third_party/rust/wasmparser/src/readers/export_section.rs new file mode 100644 index 0000000000..4e3ff3f892 --- /dev/null +++ b/third_party/rust/wasmparser/src/readers/export_section.rs @@ -0,0 +1,102 @@ +/* Copyright 2018 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use super::{ + BinaryReader, ExternalKind, Range, Result, SectionIteratorLimited, SectionReader, + SectionWithLimitedItems, +}; + +#[derive(Debug, Copy, Clone)] +pub struct Export<'a> { + pub field: &'a str, + pub kind: ExternalKind, + pub index: u32, +} + +#[derive(Clone)] +pub struct ExportSectionReader<'a> { + reader: BinaryReader<'a>, + count: u32, +} + +impl<'a> ExportSectionReader<'a> { + pub fn new(data: &'a [u8], offset: usize) -> Result<ExportSectionReader<'a>> { + let mut reader = BinaryReader::new_with_offset(data, offset); + let count = reader.read_var_u32()?; + Ok(ExportSectionReader { reader, count }) + } + + pub fn original_position(&self) -> usize { + self.reader.original_position() + } + + pub fn get_count(&self) -> u32 { + self.count + } + + /// Reads content of the export section. + /// + /// # Examples + /// ``` + /// use wasmparser::ExportSectionReader; + /// + /// # let data: &[u8] = &[0x01, 0x01, 0x65, 0x00, 0x00]; + /// let mut export_reader = ExportSectionReader::new(data, 0).unwrap(); + /// for _ in 0..export_reader.get_count() { + /// let export = export_reader.read().expect("export"); + /// println!("Export: {:?}", export); + /// } + /// ``` + pub fn read<'b>(&mut self) -> Result<Export<'b>> + where + 'a: 'b, + { + let field = self.reader.read_string()?; + let kind = self.reader.read_external_kind()?; + let index = self.reader.read_var_u32()?; + Ok(Export { field, kind, index }) + } +} + +impl<'a> SectionReader for ExportSectionReader<'a> { + type Item = Export<'a>; + fn read(&mut self) -> Result<Self::Item> { + ExportSectionReader::read(self) + } + fn eof(&self) -> bool { + self.reader.eof() + } + fn original_position(&self) -> usize { + ExportSectionReader::original_position(self) + } + fn range(&self) -> Range { + self.reader.range() + } +} + +impl<'a> SectionWithLimitedItems for ExportSectionReader<'a> { + fn get_count(&self) -> u32 { + ExportSectionReader::get_count(self) + } +} + +impl<'a> IntoIterator for ExportSectionReader<'a> { + type Item = Result<Export<'a>>; + type IntoIter = SectionIteratorLimited<ExportSectionReader<'a>>; + + fn into_iter(self) -> Self::IntoIter { + SectionIteratorLimited::new(self) + } +} diff --git a/third_party/rust/wasmparser/src/readers/function_section.rs b/third_party/rust/wasmparser/src/readers/function_section.rs new file mode 100644 index 0000000000..0d96cf2f92 --- /dev/null +++ b/third_party/rust/wasmparser/src/readers/function_section.rs @@ -0,0 +1,88 @@ +/* Copyright 2018 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use super::{ + BinaryReader, Range, Result, SectionIteratorLimited, SectionReader, SectionWithLimitedItems, +}; + +#[derive(Clone)] +pub struct FunctionSectionReader<'a> { + reader: BinaryReader<'a>, + count: u32, +} + +impl<'a> FunctionSectionReader<'a> { + pub fn new(data: &'a [u8], offset: usize) -> Result<FunctionSectionReader<'a>> { + let mut reader = BinaryReader::new_with_offset(data, offset); + let count = reader.read_var_u32()?; + Ok(FunctionSectionReader { reader, count }) + } + + pub fn original_position(&self) -> usize { + self.reader.original_position() + } + + pub fn get_count(&self) -> u32 { + self.count + } + + /// Reads function type index from the function section. + /// + /// # Examples + /// + /// ``` + /// use wasmparser::FunctionSectionReader; + /// # let data: &[u8] = &[0x01, 0x00]; + /// let mut function_reader = FunctionSectionReader::new(data, 0).unwrap(); + /// for _ in 0..function_reader.get_count() { + /// let ty = function_reader.read().expect("function type index"); + /// println!("Function type index: {}", ty); + /// } + /// ``` + pub fn read(&mut self) -> Result<u32> { + self.reader.read_var_u32() + } +} + +impl<'a> SectionReader for FunctionSectionReader<'a> { + type Item = u32; + fn read(&mut self) -> Result<Self::Item> { + FunctionSectionReader::read(self) + } + fn eof(&self) -> bool { + self.reader.eof() + } + fn original_position(&self) -> usize { + FunctionSectionReader::original_position(self) + } + fn range(&self) -> Range { + self.reader.range() + } +} + +impl<'a> SectionWithLimitedItems for FunctionSectionReader<'a> { + fn get_count(&self) -> u32 { + FunctionSectionReader::get_count(self) + } +} + +impl<'a> IntoIterator for FunctionSectionReader<'a> { + type Item = Result<u32>; + type IntoIter = SectionIteratorLimited<FunctionSectionReader<'a>>; + + fn into_iter(self) -> Self::IntoIter { + SectionIteratorLimited::new(self) + } +} diff --git a/third_party/rust/wasmparser/src/readers/global_section.rs b/third_party/rust/wasmparser/src/readers/global_section.rs new file mode 100644 index 0000000000..fdbf75797a --- /dev/null +++ b/third_party/rust/wasmparser/src/readers/global_section.rs @@ -0,0 +1,105 @@ +/* Copyright 2018 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use super::{ + BinaryReader, GlobalType, InitExpr, Range, Result, SectionIteratorLimited, SectionReader, + SectionWithLimitedItems, +}; + +#[derive(Debug, Copy, Clone)] +pub struct Global<'a> { + pub ty: GlobalType, + pub init_expr: InitExpr<'a>, +} + +#[derive(Clone)] +pub struct GlobalSectionReader<'a> { + reader: BinaryReader<'a>, + count: u32, +} + +impl<'a> GlobalSectionReader<'a> { + pub fn new(data: &'a [u8], offset: usize) -> Result<GlobalSectionReader<'a>> { + let mut reader = BinaryReader::new_with_offset(data, offset); + let count = reader.read_var_u32()?; + Ok(GlobalSectionReader { reader, count }) + } + + pub fn original_position(&self) -> usize { + self.reader.original_position() + } + + pub fn get_count(&self) -> u32 { + self.count + } + + /// Reads content of the global section. + /// + /// # Examples + /// ``` + /// use wasmparser::GlobalSectionReader; + /// # let data: &[u8] = &[0x01, 0x7F, 0x01, 0x41, 0x90, 0x88, 0x04, 0x0B]; + /// let mut global_reader = GlobalSectionReader::new(data, 0).unwrap(); + /// for _ in 0..global_reader.get_count() { + /// let global = global_reader.read().expect("global"); + /// println!("Global: {:?}", global); + /// let mut init_expr_reader = global.init_expr.get_binary_reader(); + /// let op = init_expr_reader.read_operator().expect("op"); + /// println!("Init const: {:?}", op); + /// } + /// ``` + pub fn read<'b>(&mut self) -> Result<Global<'b>> + where + 'a: 'b, + { + let ty = self.reader.read_global_type()?; + let expr_offset = self.reader.position; + self.reader.skip_init_expr()?; + let data = &self.reader.buffer[expr_offset..self.reader.position]; + let init_expr = InitExpr::new(data, self.reader.original_offset + expr_offset); + Ok(Global { ty, init_expr }) + } +} + +impl<'a> SectionReader for GlobalSectionReader<'a> { + type Item = Global<'a>; + fn read(&mut self) -> Result<Self::Item> { + GlobalSectionReader::read(self) + } + fn eof(&self) -> bool { + self.reader.eof() + } + fn original_position(&self) -> usize { + GlobalSectionReader::original_position(self) + } + fn range(&self) -> Range { + self.reader.range() + } +} + +impl<'a> SectionWithLimitedItems for GlobalSectionReader<'a> { + fn get_count(&self) -> u32 { + GlobalSectionReader::get_count(self) + } +} + +impl<'a> IntoIterator for GlobalSectionReader<'a> { + type Item = Result<Global<'a>>; + type IntoIter = SectionIteratorLimited<GlobalSectionReader<'a>>; + + fn into_iter(self) -> Self::IntoIter { + SectionIteratorLimited::new(self) + } +} diff --git a/third_party/rust/wasmparser/src/readers/import_section.rs b/third_party/rust/wasmparser/src/readers/import_section.rs new file mode 100644 index 0000000000..4f75bcfe86 --- /dev/null +++ b/third_party/rust/wasmparser/src/readers/import_section.rs @@ -0,0 +1,98 @@ +/* Copyright 2018 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use crate::{ + BinaryReader, ImportSectionEntryType, Range, Result, SectionIteratorLimited, SectionReader, + SectionWithLimitedItems, +}; + +#[derive(Debug, Copy, Clone)] +pub struct Import<'a> { + pub module: &'a str, + pub field: Option<&'a str>, + pub ty: ImportSectionEntryType, +} + +#[derive(Clone)] +pub struct ImportSectionReader<'a> { + reader: BinaryReader<'a>, + count: u32, +} + +impl<'a> ImportSectionReader<'a> { + pub fn new(data: &'a [u8], offset: usize) -> Result<ImportSectionReader<'a>> { + let mut reader = BinaryReader::new_with_offset(data, offset); + let count = reader.read_var_u32()?; + Ok(ImportSectionReader { reader, count }) + } + + pub fn original_position(&self) -> usize { + self.reader.original_position() + } + + pub fn get_count(&self) -> u32 { + self.count + } + + /// Reads content of the import section. + /// + /// # Examples + /// ``` + /// use wasmparser::ImportSectionReader; + /// # let data: &[u8] = &[0x01, 0x01, 0x41, 0x01, 0x66, 0x00, 0x00]; + /// let mut import_reader = ImportSectionReader::new(data, 0).unwrap(); + /// for _ in 0..import_reader.get_count() { + /// let import = import_reader.read().expect("import"); + /// println!("Import: {:?}", import); + /// } + /// ``` + pub fn read<'b>(&mut self) -> Result<Import<'b>> + where + 'a: 'b, + { + self.reader.read_import() + } +} + +impl<'a> SectionReader for ImportSectionReader<'a> { + type Item = Import<'a>; + fn read(&mut self) -> Result<Self::Item> { + ImportSectionReader::read(self) + } + fn eof(&self) -> bool { + self.reader.eof() + } + fn original_position(&self) -> usize { + ImportSectionReader::original_position(self) + } + fn range(&self) -> Range { + self.reader.range() + } +} + +impl<'a> SectionWithLimitedItems for ImportSectionReader<'a> { + fn get_count(&self) -> u32 { + ImportSectionReader::get_count(self) + } +} + +impl<'a> IntoIterator for ImportSectionReader<'a> { + type Item = Result<Import<'a>>; + type IntoIter = SectionIteratorLimited<ImportSectionReader<'a>>; + + fn into_iter(self) -> Self::IntoIter { + SectionIteratorLimited::new(self) + } +} diff --git a/third_party/rust/wasmparser/src/readers/init_expr.rs b/third_party/rust/wasmparser/src/readers/init_expr.rs new file mode 100644 index 0000000000..98c301d350 --- /dev/null +++ b/third_party/rust/wasmparser/src/readers/init_expr.rs @@ -0,0 +1,42 @@ +/* Copyright 2018 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use super::{BinaryReader, OperatorsReader}; + +#[derive(Debug, Copy, Clone)] +pub struct InitExpr<'a> { + offset: usize, + data: &'a [u8], +} + +impl<'a> InitExpr<'a> { + pub fn new(data: &[u8], offset: usize) -> InitExpr { + InitExpr { offset, data } + } + + pub fn get_binary_reader<'b>(&self) -> BinaryReader<'b> + where + 'a: 'b, + { + BinaryReader::new_with_offset(self.data, self.offset) + } + + pub fn get_operators_reader<'b>(&self) -> OperatorsReader<'b> + where + 'a: 'b, + { + OperatorsReader::new(self.data, self.offset) + } +} diff --git a/third_party/rust/wasmparser/src/readers/instance_section.rs b/third_party/rust/wasmparser/src/readers/instance_section.rs new file mode 100644 index 0000000000..5c27954044 --- /dev/null +++ b/third_party/rust/wasmparser/src/readers/instance_section.rs @@ -0,0 +1,162 @@ +use crate::{ + BinaryReader, BinaryReaderError, ExternalKind, Range, Result, SectionIteratorLimited, + SectionReader, SectionWithLimitedItems, +}; + +#[derive(Clone)] +pub struct InstanceSectionReader<'a> { + reader: BinaryReader<'a>, + count: u32, +} + +impl<'a> InstanceSectionReader<'a> { + pub fn new(data: &'a [u8], offset: usize) -> Result<InstanceSectionReader<'a>> { + let mut reader = BinaryReader::new_with_offset(data, offset); + let count = reader.read_var_u32()?; + Ok(InstanceSectionReader { reader, count }) + } + + pub fn original_position(&self) -> usize { + self.reader.original_position() + } + + pub fn get_count(&self) -> u32 { + self.count + } + + pub fn read(&mut self) -> Result<Instance<'a>> { + let instance = Instance::new( + &self.reader.buffer[self.reader.position..], + self.original_position(), + )?; + self.reader.skip_bytes(1)?; + self.reader.skip_var_32()?; + let count = self.reader.read_var_u32()?; + for _ in 0..count { + self.reader.skip_bytes(1)?; + self.reader.skip_var_32()?; + } + Ok(instance) + } +} + +impl<'a> SectionReader for InstanceSectionReader<'a> { + type Item = Instance<'a>; + + fn read(&mut self) -> Result<Self::Item> { + InstanceSectionReader::read(self) + } + fn eof(&self) -> bool { + self.reader.eof() + } + fn original_position(&self) -> usize { + InstanceSectionReader::original_position(self) + } + fn range(&self) -> Range { + self.reader.range() + } +} + +impl<'a> SectionWithLimitedItems for InstanceSectionReader<'a> { + fn get_count(&self) -> u32 { + InstanceSectionReader::get_count(self) + } +} + +impl<'a> IntoIterator for InstanceSectionReader<'a> { + type Item = Result<Instance<'a>>; + type IntoIter = SectionIteratorLimited<InstanceSectionReader<'a>>; + + fn into_iter(self) -> Self::IntoIter { + SectionIteratorLimited::new(self) + } +} + +pub struct Instance<'a> { + reader: BinaryReader<'a>, + module: u32, +} + +impl<'a> Instance<'a> { + pub fn new(data: &'a [u8], offset: usize) -> Result<Instance<'a>> { + let mut reader = BinaryReader::new_with_offset(data, offset); + if reader.read_u8()? != 0 { + return Err(BinaryReaderError::new( + "instantiate instruction not found", + offset, + )); + } + let module = reader.read_var_u32()?; + Ok(Instance { module, reader }) + } + + pub fn original_position(&self) -> usize { + self.reader.original_position() + } + + pub fn module(&self) -> u32 { + self.module + } + + pub fn args(&self) -> Result<InstanceArgsReader<'a>> { + let mut reader = self.reader.clone(); + let count = reader.read_var_u32()?; + Ok(InstanceArgsReader { + count, + remaining: count, + reader, + }) + } +} + +#[derive(Clone)] +pub struct InstanceArgsReader<'a> { + reader: BinaryReader<'a>, + count: u32, + remaining: u32, +} + +impl<'a> InstanceArgsReader<'a> { + pub fn original_position(&self) -> usize { + self.reader.original_position() + } + + pub fn read(&mut self) -> Result<(ExternalKind, u32)> { + let kind = self.reader.read_external_kind()?; + let index = self.reader.read_var_u32()?; + self.remaining -= 1; + Ok((kind, index)) + } +} + +impl<'a> SectionReader for InstanceArgsReader<'a> { + type Item = (ExternalKind, u32); + + fn read(&mut self) -> Result<Self::Item> { + InstanceArgsReader::read(self) + } + fn eof(&self) -> bool { + self.remaining == 0 + } + fn original_position(&self) -> usize { + InstanceArgsReader::original_position(self) + } + fn range(&self) -> Range { + self.reader.range() + } +} + +impl<'a> SectionWithLimitedItems for InstanceArgsReader<'a> { + fn get_count(&self) -> u32 { + self.count + } +} + +impl<'a> IntoIterator for InstanceArgsReader<'a> { + type Item = Result<(ExternalKind, u32)>; + type IntoIter = SectionIteratorLimited<InstanceArgsReader<'a>>; + + fn into_iter(self) -> Self::IntoIter { + SectionIteratorLimited::new(self) + } +} diff --git a/third_party/rust/wasmparser/src/readers/linking_section.rs b/third_party/rust/wasmparser/src/readers/linking_section.rs new file mode 100644 index 0000000000..4b999744f0 --- /dev/null +++ b/third_party/rust/wasmparser/src/readers/linking_section.rs @@ -0,0 +1,78 @@ +/* Copyright 2018 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use super::{ + BinaryReader, LinkingType, Range, Result, SectionIteratorLimited, SectionReader, + SectionWithLimitedItems, +}; + +pub struct LinkingSectionReader<'a> { + reader: BinaryReader<'a>, + count: u32, +} + +impl<'a> LinkingSectionReader<'a> { + pub fn new(data: &'a [u8], offset: usize) -> Result<LinkingSectionReader<'a>> { + let mut reader = BinaryReader::new_with_offset(data, offset); + let count = reader.read_var_u32()?; + Ok(LinkingSectionReader { reader, count }) + } + + pub fn get_count(&self) -> u32 { + self.count + } + + pub fn original_position(&self) -> usize { + self.reader.original_position() + } + + pub fn read<'b>(&mut self) -> Result<LinkingType> + where + 'a: 'b, + { + Ok(self.reader.read_linking_type()?) + } +} + +impl<'a> SectionReader for LinkingSectionReader<'a> { + type Item = LinkingType; + fn read(&mut self) -> Result<Self::Item> { + LinkingSectionReader::read(self) + } + fn eof(&self) -> bool { + self.reader.eof() + } + fn original_position(&self) -> usize { + LinkingSectionReader::original_position(self) + } + fn range(&self) -> Range { + self.reader.range() + } +} + +impl<'a> SectionWithLimitedItems for LinkingSectionReader<'a> { + fn get_count(&self) -> u32 { + LinkingSectionReader::get_count(self) + } +} + +impl<'a> IntoIterator for LinkingSectionReader<'a> { + type Item = Result<LinkingType>; + type IntoIter = SectionIteratorLimited<LinkingSectionReader<'a>>; + + fn into_iter(self) -> Self::IntoIter { + SectionIteratorLimited::new(self) + } +} diff --git a/third_party/rust/wasmparser/src/readers/memory_section.rs b/third_party/rust/wasmparser/src/readers/memory_section.rs new file mode 100644 index 0000000000..b84a59dff1 --- /dev/null +++ b/third_party/rust/wasmparser/src/readers/memory_section.rs @@ -0,0 +1,88 @@ +/* Copyright 2018 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use super::{ + BinaryReader, MemoryType, Range, Result, SectionIteratorLimited, SectionReader, + SectionWithLimitedItems, +}; + +#[derive(Clone)] +pub struct MemorySectionReader<'a> { + reader: BinaryReader<'a>, + count: u32, +} + +impl<'a> MemorySectionReader<'a> { + pub fn new(data: &'a [u8], offset: usize) -> Result<MemorySectionReader<'a>> { + let mut reader = BinaryReader::new_with_offset(data, offset); + let count = reader.read_var_u32()?; + Ok(MemorySectionReader { reader, count }) + } + + pub fn original_position(&self) -> usize { + self.reader.original_position() + } + + pub fn get_count(&self) -> u32 { + self.count + } + + /// Reads content of the memory section. + /// + /// # Examples + /// ``` + /// use wasmparser::MemorySectionReader; + /// # let data: &[u8] = &[0x01, 0x00, 0x02]; + /// let mut memory_reader = MemorySectionReader::new(data, 0).unwrap(); + /// for _ in 0..memory_reader.get_count() { + /// let memory = memory_reader.read().expect("memory"); + /// println!("Memory: {:?}", memory); + /// } + /// ``` + pub fn read(&mut self) -> Result<MemoryType> { + self.reader.read_memory_type() + } +} + +impl<'a> SectionReader for MemorySectionReader<'a> { + type Item = MemoryType; + fn read(&mut self) -> Result<Self::Item> { + MemorySectionReader::read(self) + } + fn eof(&self) -> bool { + self.reader.eof() + } + fn original_position(&self) -> usize { + MemorySectionReader::original_position(self) + } + fn range(&self) -> Range { + self.reader.range() + } +} + +impl<'a> SectionWithLimitedItems for MemorySectionReader<'a> { + fn get_count(&self) -> u32 { + MemorySectionReader::get_count(self) + } +} + +impl<'a> IntoIterator for MemorySectionReader<'a> { + type Item = Result<MemoryType>; + type IntoIter = SectionIteratorLimited<MemorySectionReader<'a>>; + + fn into_iter(self) -> Self::IntoIter { + SectionIteratorLimited::new(self) + } +} diff --git a/third_party/rust/wasmparser/src/readers/mod.rs b/third_party/rust/wasmparser/src/readers/mod.rs new file mode 100644 index 0000000000..9cf8bf1a81 --- /dev/null +++ b/third_party/rust/wasmparser/src/readers/mod.rs @@ -0,0 +1,63 @@ +/* Copyright 2018 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use super::{ + BinaryReader, BinaryReaderError, ExternalKind, GlobalType, LinkingType, MemoryType, NameType, + Naming, Operator, Range, RelocType, Result, SectionCode, TableType, Type, +}; + +pub use self::alias_section::*; +pub use self::code_section::*; +pub use self::data_section::*; +pub use self::element_section::*; +pub use self::export_section::*; +pub use self::function_section::*; +pub use self::global_section::*; +pub use self::import_section::*; +pub use self::init_expr::*; +pub use self::instance_section::*; +pub use self::linking_section::*; +pub use self::memory_section::*; +pub use self::module_code_section::*; +pub use self::module_section::*; +pub use self::name_section::*; +pub use self::operators::*; +pub use self::producers_section::*; +pub use self::reloc_section::*; +pub use self::section_reader::*; +pub use self::table_section::*; +pub use self::type_section::*; + +mod alias_section; +mod code_section; +mod data_section; +mod element_section; +mod export_section; +mod function_section; +mod global_section; +mod import_section; +mod init_expr; +mod instance_section; +mod linking_section; +mod memory_section; +mod module_code_section; +mod module_section; +mod name_section; +mod operators; +mod producers_section; +mod reloc_section; +mod section_reader; +mod table_section; +mod type_section; diff --git a/third_party/rust/wasmparser/src/readers/module_code_section.rs b/third_party/rust/wasmparser/src/readers/module_code_section.rs new file mode 100644 index 0000000000..866cd443af --- /dev/null +++ b/third_party/rust/wasmparser/src/readers/module_code_section.rs @@ -0,0 +1,96 @@ +use crate::{ + BinaryReader, BinaryReaderError, Range, Result, SectionIteratorLimited, SectionReader, + SectionWithLimitedItems, +}; + +pub struct ModuleCodeSectionReader<'a> { + reader: BinaryReader<'a>, + count: u32, +} + +#[derive(Debug)] +pub struct ModuleCode<'a> { + reader: BinaryReader<'a>, +} + +impl<'a> ModuleCodeSectionReader<'a> { + pub fn new(data: &'a [u8], offset: usize) -> Result<ModuleCodeSectionReader<'a>> { + let mut reader = BinaryReader::new_with_offset(data, offset); + let count = reader.read_var_u32()?; + Ok(ModuleCodeSectionReader { reader, count }) + } + + pub fn original_position(&self) -> usize { + self.reader.original_position() + } + + pub fn get_count(&self) -> u32 { + self.count + } + + fn verify_module_end(&self, end: usize) -> Result<()> { + if self.reader.buffer.len() < end { + return Err(BinaryReaderError::new( + "module body extends past end of the module code section", + self.reader.original_offset + self.reader.buffer.len(), + )); + } + Ok(()) + } + + pub fn read(&mut self) -> Result<ModuleCode<'a>> { + let size = self.reader.read_var_u32()? as usize; + let module_start = self.reader.position; + let module_end = module_start + size; + self.verify_module_end(module_end)?; + self.reader.skip_to(module_end); + Ok(ModuleCode { + reader: BinaryReader::new_with_offset( + &self.reader.buffer[module_start..module_end], + self.reader.original_offset + module_start, + ), + }) + } +} + +impl<'a> SectionReader for ModuleCodeSectionReader<'a> { + type Item = ModuleCode<'a>; + + fn read(&mut self) -> Result<Self::Item> { + ModuleCodeSectionReader::read(self) + } + fn eof(&self) -> bool { + self.reader.eof() + } + fn original_position(&self) -> usize { + ModuleCodeSectionReader::original_position(self) + } + fn range(&self) -> Range { + self.reader.range() + } +} + +impl<'a> SectionWithLimitedItems for ModuleCodeSectionReader<'a> { + fn get_count(&self) -> u32 { + ModuleCodeSectionReader::get_count(self) + } +} + +impl<'a> IntoIterator for ModuleCodeSectionReader<'a> { + type Item = Result<ModuleCode<'a>>; + type IntoIter = SectionIteratorLimited<ModuleCodeSectionReader<'a>>; + + fn into_iter(self) -> Self::IntoIter { + SectionIteratorLimited::new(self) + } +} + +impl<'a> ModuleCode<'a> { + pub fn raw_bytes(&self) -> (usize, &[u8]) { + (self.reader.original_position(), self.reader.buffer) + } + + pub fn original_position(&self) -> usize { + self.reader.original_position() + } +} diff --git a/third_party/rust/wasmparser/src/readers/module_section.rs b/third_party/rust/wasmparser/src/readers/module_section.rs new file mode 100644 index 0000000000..86b73f7a7f --- /dev/null +++ b/third_party/rust/wasmparser/src/readers/module_section.rs @@ -0,0 +1,61 @@ +use super::{ + BinaryReader, Range, Result, SectionIteratorLimited, SectionReader, SectionWithLimitedItems, +}; + +#[derive(Clone)] +pub struct ModuleSectionReader<'a> { + reader: BinaryReader<'a>, + count: u32, +} + +impl<'a> ModuleSectionReader<'a> { + pub fn new(data: &'a [u8], offset: usize) -> Result<ModuleSectionReader<'a>> { + let mut reader = BinaryReader::new_with_offset(data, offset); + let count = reader.read_var_u32()?; + Ok(ModuleSectionReader { reader, count }) + } + + pub fn original_position(&self) -> usize { + self.reader.original_position() + } + + pub fn get_count(&self) -> u32 { + self.count + } + + pub fn read(&mut self) -> Result<u32> { + self.reader.read_var_u32() + } +} + +impl<'a> SectionReader for ModuleSectionReader<'a> { + type Item = u32; + + fn read(&mut self) -> Result<Self::Item> { + ModuleSectionReader::read(self) + } + fn eof(&self) -> bool { + self.reader.eof() + } + fn original_position(&self) -> usize { + ModuleSectionReader::original_position(self) + } + fn range(&self) -> Range { + self.reader.range() + } +} + +impl<'a> SectionWithLimitedItems for ModuleSectionReader<'a> { + fn get_count(&self) -> u32 { + ModuleSectionReader::get_count(self) + } +} + +impl<'a> IntoIterator for ModuleSectionReader<'a> { + type Item = Result<u32>; + type IntoIter = SectionIteratorLimited<ModuleSectionReader<'a>>; + + fn into_iter(self) -> Self::IntoIter { + SectionIteratorLimited::new(self) + } +} diff --git a/third_party/rust/wasmparser/src/readers/name_section.rs b/third_party/rust/wasmparser/src/readers/name_section.rs new file mode 100644 index 0000000000..343a7441ab --- /dev/null +++ b/third_party/rust/wasmparser/src/readers/name_section.rs @@ -0,0 +1,253 @@ +/* Copyright 2018 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use super::{ + BinaryReader, BinaryReaderError, NameType, Naming, Range, Result, SectionIterator, + SectionReader, +}; + +#[derive(Debug, Copy, Clone)] +pub struct ModuleName<'a> { + data: &'a [u8], + offset: usize, +} + +impl<'a> ModuleName<'a> { + pub fn get_name<'b>(&self) -> Result<&'b str> + where + 'a: 'b, + { + let mut reader = BinaryReader::new_with_offset(self.data, self.offset); + reader.read_string() + } + + pub fn original_position(&self) -> usize { + self.offset + } +} + +pub struct NamingReader<'a> { + reader: BinaryReader<'a>, + count: u32, +} + +impl<'a> NamingReader<'a> { + fn new(data: &'a [u8], offset: usize) -> Result<NamingReader<'a>> { + let mut reader = BinaryReader::new_with_offset(data, offset); + let count = reader.read_var_u32()?; + Ok(NamingReader { reader, count }) + } + + fn skip(reader: &mut BinaryReader) -> Result<()> { + let count = reader.read_var_u32()?; + for _ in 0..count { + reader.skip_var_32()?; + reader.skip_string()?; + } + Ok(()) + } + + pub fn original_position(&self) -> usize { + self.reader.original_position() + } + + pub fn get_count(&self) -> u32 { + self.count + } + + pub fn read<'b>(&mut self) -> Result<Naming<'b>> + where + 'a: 'b, + { + let index = self.reader.read_var_u32()?; + let name = self.reader.read_string()?; + Ok(Naming { index, name }) + } +} + +#[derive(Debug, Copy, Clone)] +pub struct FunctionName<'a> { + data: &'a [u8], + offset: usize, +} + +impl<'a> FunctionName<'a> { + pub fn get_map<'b>(&self) -> Result<NamingReader<'b>> + where + 'a: 'b, + { + NamingReader::new(self.data, self.offset) + } + + pub fn original_position(&self) -> usize { + self.offset + } +} + +#[derive(Debug, Copy, Clone)] +pub struct FunctionLocalName<'a> { + pub func_index: u32, + data: &'a [u8], + offset: usize, +} + +impl<'a> FunctionLocalName<'a> { + pub fn get_map<'b>(&self) -> Result<NamingReader<'b>> + where + 'a: 'b, + { + NamingReader::new(self.data, self.offset) + } + + pub fn original_position(&self) -> usize { + self.offset + } +} + +pub struct FunctionLocalReader<'a> { + reader: BinaryReader<'a>, + count: u32, +} + +impl<'a> FunctionLocalReader<'a> { + fn new(data: &'a [u8], offset: usize) -> Result<FunctionLocalReader<'a>> { + let mut reader = BinaryReader::new_with_offset(data, offset); + let count = reader.read_var_u32()?; + Ok(FunctionLocalReader { reader, count }) + } + + pub fn get_count(&self) -> u32 { + self.count + } + + pub fn original_position(&self) -> usize { + self.reader.original_position() + } + + pub fn read<'b>(&mut self) -> Result<FunctionLocalName<'b>> + where + 'a: 'b, + { + let func_index = self.reader.read_var_u32()?; + let start = self.reader.position; + NamingReader::skip(&mut self.reader)?; + let end = self.reader.position; + Ok(FunctionLocalName { + func_index, + data: &self.reader.buffer[start..end], + offset: self.reader.original_offset + start, + }) + } +} + +#[derive(Debug, Copy, Clone)] +pub struct LocalName<'a> { + data: &'a [u8], + offset: usize, +} + +impl<'a> LocalName<'a> { + pub fn get_function_local_reader<'b>(&self) -> Result<FunctionLocalReader<'b>> + where + 'a: 'b, + { + FunctionLocalReader::new(self.data, self.offset) + } + + pub fn original_position(&self) -> usize { + self.offset + } +} + +#[derive(Debug, Copy, Clone)] +pub enum Name<'a> { + Module(ModuleName<'a>), + Function(FunctionName<'a>), + Local(LocalName<'a>), +} + +pub struct NameSectionReader<'a> { + reader: BinaryReader<'a>, +} + +impl<'a> NameSectionReader<'a> { + pub fn new(data: &'a [u8], offset: usize) -> Result<NameSectionReader<'a>> { + Ok(NameSectionReader { + reader: BinaryReader::new_with_offset(data, offset), + }) + } + + fn verify_section_end(&self, end: usize) -> Result<()> { + if self.reader.buffer.len() < end { + return Err(BinaryReaderError::new( + "Name entry extends past end of the code section", + self.reader.original_offset + self.reader.buffer.len(), + )); + } + Ok(()) + } + + pub fn eof(&self) -> bool { + self.reader.eof() + } + + pub fn original_position(&self) -> usize { + self.reader.original_position() + } + + pub fn read<'b>(&mut self) -> Result<Name<'b>> + where + 'a: 'b, + { + let ty = self.reader.read_name_type()?; + let payload_len = self.reader.read_var_u32()? as usize; + let payload_start = self.reader.position; + let payload_end = payload_start + payload_len; + self.verify_section_end(payload_end)?; + let offset = self.reader.original_offset + payload_start; + let data = &self.reader.buffer[payload_start..payload_end]; + self.reader.skip_to(payload_end); + Ok(match ty { + NameType::Module => Name::Module(ModuleName { data, offset }), + NameType::Function => Name::Function(FunctionName { data, offset }), + NameType::Local => Name::Local(LocalName { data, offset }), + }) + } +} + +impl<'a> SectionReader for NameSectionReader<'a> { + type Item = Name<'a>; + fn read(&mut self) -> Result<Self::Item> { + NameSectionReader::read(self) + } + fn eof(&self) -> bool { + NameSectionReader::eof(self) + } + fn original_position(&self) -> usize { + NameSectionReader::original_position(self) + } + fn range(&self) -> Range { + self.reader.range() + } +} + +impl<'a> IntoIterator for NameSectionReader<'a> { + type Item = Result<Name<'a>>; + type IntoIter = SectionIterator<NameSectionReader<'a>>; + + fn into_iter(self) -> Self::IntoIter { + SectionIterator::new(self) + } +} diff --git a/third_party/rust/wasmparser/src/readers/operators.rs b/third_party/rust/wasmparser/src/readers/operators.rs new file mode 100644 index 0000000000..cda42571dd --- /dev/null +++ b/third_party/rust/wasmparser/src/readers/operators.rs @@ -0,0 +1,161 @@ +/* Copyright 2018 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use super::{BinaryReader, BinaryReaderError, Operator, Result}; + +#[derive(Clone)] +pub struct OperatorsReader<'a> { + pub(crate) reader: BinaryReader<'a>, +} + +impl<'a> OperatorsReader<'a> { + pub(crate) fn new<'b>(data: &'a [u8], offset: usize) -> OperatorsReader<'b> + where + 'a: 'b, + { + OperatorsReader { + reader: BinaryReader::new_with_offset(data, offset), + } + } + + pub fn eof(&self) -> bool { + self.reader.eof() + } + + pub fn original_position(&self) -> usize { + self.reader.original_position() + } + + pub fn ensure_end(&self) -> Result<()> { + if self.eof() { + return Ok(()); + } + Err(BinaryReaderError::new( + "Unexpected data at the end of operators", + self.reader.original_position(), + )) + } + + pub fn read<'b>(&mut self) -> Result<Operator<'b>> + where + 'a: 'b, + { + self.reader.read_operator() + } + + pub fn into_iter_with_offsets<'b>(self) -> OperatorsIteratorWithOffsets<'b> + where + 'a: 'b, + { + OperatorsIteratorWithOffsets { + reader: self, + err: false, + } + } + + pub fn read_with_offset<'b>(&mut self) -> Result<(Operator<'b>, usize)> + where + 'a: 'b, + { + let pos = self.reader.original_position(); + Ok((self.read()?, pos)) + } +} + +impl<'a> IntoIterator for OperatorsReader<'a> { + type Item = Result<Operator<'a>>; + type IntoIter = OperatorsIterator<'a>; + + /// Reads content of the code section. + /// + /// # Examples + /// ``` + /// use wasmparser::{Operator, CodeSectionReader, Result}; + /// # let data: &[u8] = &[ + /// # 0x01, 0x03, 0x00, 0x01, 0x0b]; + /// let mut code_reader = CodeSectionReader::new(data, 0).unwrap(); + /// for _ in 0..code_reader.get_count() { + /// let body = code_reader.read().expect("function body"); + /// let mut op_reader = body.get_operators_reader().expect("op reader"); + /// let ops = op_reader.into_iter().collect::<Result<Vec<Operator>>>().expect("ops"); + /// assert!( + /// if let [Operator::Nop, Operator::End] = ops.as_slice() { true } else { false }, + /// "found {:?}", + /// ops + /// ); + /// } + /// ``` + fn into_iter(self) -> Self::IntoIter { + OperatorsIterator { + reader: self, + err: false, + } + } +} + +pub struct OperatorsIterator<'a> { + reader: OperatorsReader<'a>, + err: bool, +} + +impl<'a> Iterator for OperatorsIterator<'a> { + type Item = Result<Operator<'a>>; + + fn next(&mut self) -> Option<Self::Item> { + if self.err || self.reader.eof() { + return None; + } + let result = self.reader.read(); + self.err = result.is_err(); + Some(result) + } +} + +pub struct OperatorsIteratorWithOffsets<'a> { + reader: OperatorsReader<'a>, + err: bool, +} + +impl<'a> Iterator for OperatorsIteratorWithOffsets<'a> { + type Item = Result<(Operator<'a>, usize)>; + + /// Reads content of the code section with offsets. + /// + /// # Examples + /// ``` + /// use wasmparser::{Operator, CodeSectionReader, Result}; + /// # let data: &[u8] = &[ + /// # 0x01, 0x03, 0x00, /* offset = 23 */ 0x01, 0x0b]; + /// let mut code_reader = CodeSectionReader::new(data, 20).unwrap(); + /// for _ in 0..code_reader.get_count() { + /// let body = code_reader.read().expect("function body"); + /// let mut op_reader = body.get_operators_reader().expect("op reader"); + /// let ops = op_reader.into_iter_with_offsets().collect::<Result<Vec<(Operator, usize)>>>().expect("ops"); + /// assert!( + /// if let [(Operator::Nop, 23), (Operator::End, 24)] = ops.as_slice() { true } else { false }, + /// "found {:?}", + /// ops + /// ); + /// } + /// ``` + fn next(&mut self) -> Option<Self::Item> { + if self.err || self.reader.eof() { + return None; + } + let result = self.reader.read_with_offset(); + self.err = result.is_err(); + Some(result) + } +} diff --git a/third_party/rust/wasmparser/src/readers/producers_section.rs b/third_party/rust/wasmparser/src/readers/producers_section.rs new file mode 100644 index 0000000000..d6a9051b16 --- /dev/null +++ b/third_party/rust/wasmparser/src/readers/producers_section.rs @@ -0,0 +1,197 @@ +/* Copyright 2019 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use super::{ + BinaryReader, Range, Result, SectionIteratorLimited, SectionReader, SectionWithLimitedItems, +}; + +#[derive(Debug, Copy, Clone)] +pub struct ProducersFieldValue<'a> { + pub name: &'a str, + pub version: &'a str, +} + +pub struct ProducersFieldValuesReader<'a> { + reader: BinaryReader<'a>, + count: u32, +} + +impl<'a> ProducersFieldValuesReader<'a> { + pub fn get_count(&self) -> u32 { + self.count + } + + pub fn original_position(&self) -> usize { + self.reader.original_position() + } + + fn skip(reader: &mut BinaryReader, values_count: u32) -> Result<()> { + for _ in 0..values_count { + reader.skip_string()?; + reader.skip_string()?; + } + Ok(()) + } + + pub fn read<'b>(&mut self) -> Result<ProducersFieldValue<'b>> + where + 'a: 'b, + { + let name = self.reader.read_string()?; + let version = self.reader.read_string()?; + Ok(ProducersFieldValue { name, version }) + } +} + +impl<'a> IntoIterator for ProducersFieldValuesReader<'a> { + type Item = Result<ProducersFieldValue<'a>>; + type IntoIter = ProducersFieldValuesIterator<'a>; + fn into_iter(self) -> Self::IntoIter { + let count = self.count; + ProducersFieldValuesIterator { + reader: self, + left: count, + err: false, + } + } +} + +pub struct ProducersFieldValuesIterator<'a> { + reader: ProducersFieldValuesReader<'a>, + left: u32, + err: bool, +} + +impl<'a> Iterator for ProducersFieldValuesIterator<'a> { + type Item = Result<ProducersFieldValue<'a>>; + fn next(&mut self) -> Option<Self::Item> { + if self.err || self.left == 0 { + return None; + } + let result = self.reader.read(); + self.err = result.is_err(); + self.left -= 1; + Some(result) + } + fn size_hint(&self) -> (usize, Option<usize>) { + let count = self.reader.get_count() as usize; + (count, Some(count)) + } +} + +#[derive(Debug, Copy, Clone)] +pub struct ProducersField<'a> { + pub name: &'a str, + values_count: u32, + values_data: &'a [u8], + values_offset: usize, +} + +impl<'a> ProducersField<'a> { + pub fn get_producer_field_values_reader<'b>(&self) -> Result<ProducersFieldValuesReader<'b>> + where + 'a: 'b, + { + Ok(ProducersFieldValuesReader { + reader: BinaryReader::new_with_offset(self.values_data, self.values_offset), + count: self.values_count, + }) + } +} + +pub struct ProducersSectionReader<'a> { + reader: BinaryReader<'a>, + count: u32, +} + +impl<'a> ProducersSectionReader<'a> { + /// Creates reader for the producers section. + /// + /// # Examples + /// ``` + /// # let data: &[u8] = &[0x01, 0x08, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, + /// # 0x02, 0x03, 0x77, 0x61, 0x74, 0x01, 0x31, 0x01, 0x43, 0x03, 0x39, 0x2e, 0x30]; + /// use wasmparser::{ProducersSectionReader, ProducersFieldValue, Result}; + /// let mut reader = ProducersSectionReader::new(data, 0).expect("producers reader"); + /// let field = reader.read().expect("producers field"); + /// assert!(field.name == "language"); + /// let mut values_reader = field.get_producer_field_values_reader().expect("values reader"); + /// let value = values_reader.into_iter().collect::<Result<Vec<ProducersFieldValue>>>().expect("values"); + /// assert!(value.len() == 2); + /// assert!(value[0].name == "wat" && value[0].version == "1"); + /// assert!(value[1].name == "C" && value[1].version == "9.0"); + /// ``` + pub fn new(data: &'a [u8], offset: usize) -> Result<ProducersSectionReader<'a>> { + let mut reader = BinaryReader::new_with_offset(data, offset); + let count = reader.read_var_u32()?; + Ok(ProducersSectionReader { reader, count }) + } + + pub fn original_position(&self) -> usize { + self.reader.original_position() + } + + pub fn get_count(&self) -> u32 { + self.count + } + + pub fn read<'b>(&mut self) -> Result<ProducersField<'b>> + where + 'a: 'b, + { + let name = self.reader.read_string()?; + let values_count = self.reader.read_var_u32()?; + let values_start = self.reader.position; + ProducersFieldValuesReader::skip(&mut self.reader, values_count)?; + let values_end = self.reader.position; + Ok(ProducersField { + name, + values_count, + values_data: &self.reader.buffer[values_start..values_end], + values_offset: self.reader.original_offset + values_start, + }) + } +} + +impl<'a> SectionReader for ProducersSectionReader<'a> { + type Item = ProducersField<'a>; + fn read(&mut self) -> Result<Self::Item> { + ProducersSectionReader::read(self) + } + fn eof(&self) -> bool { + self.reader.eof() + } + fn original_position(&self) -> usize { + ProducersSectionReader::original_position(self) + } + fn range(&self) -> Range { + self.reader.range() + } +} + +impl<'a> SectionWithLimitedItems for ProducersSectionReader<'a> { + fn get_count(&self) -> u32 { + ProducersSectionReader::get_count(self) + } +} + +impl<'a> IntoIterator for ProducersSectionReader<'a> { + type Item = Result<ProducersField<'a>>; + type IntoIter = SectionIteratorLimited<ProducersSectionReader<'a>>; + + fn into_iter(self) -> Self::IntoIter { + SectionIteratorLimited::new(self) + } +} diff --git a/third_party/rust/wasmparser/src/readers/reloc_section.rs b/third_party/rust/wasmparser/src/readers/reloc_section.rs new file mode 100644 index 0000000000..b390ff90b6 --- /dev/null +++ b/third_party/rust/wasmparser/src/readers/reloc_section.rs @@ -0,0 +1,118 @@ +/* Copyright 2018 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use super::{ + BinaryReader, Range, RelocType, Result, SectionCode, SectionIteratorLimited, SectionReader, + SectionWithLimitedItems, +}; + +#[derive(Debug, Copy, Clone)] +pub struct Reloc { + pub ty: RelocType, + pub offset: u32, + pub index: u32, + pub addend: Option<u32>, +} + +pub struct RelocSectionReader<'a> { + reader: BinaryReader<'a>, + section_code: SectionCode<'a>, + count: u32, +} + +impl<'a> RelocSectionReader<'a> { + pub fn new(data: &'a [u8], offset: usize) -> Result<RelocSectionReader<'a>> { + let mut reader = BinaryReader::new_with_offset(data, offset); + + let section_id_position = reader.position; + let section_id = reader.read_var_u7()?; + let section_code = reader.read_section_code(section_id, section_id_position)?; + + let count = reader.read_var_u32()?; + Ok(RelocSectionReader { + reader, + section_code, + count, + }) + } + + pub fn get_count(&self) -> u32 { + self.count + } + + pub fn get_section_code<'b>(&self) -> SectionCode<'b> + where + 'a: 'b, + { + self.section_code + } + + pub fn original_position(&self) -> usize { + self.reader.original_position() + } + + pub fn read(&mut self) -> Result<Reloc> { + let ty = self.reader.read_reloc_type()?; + let offset = self.reader.read_var_u32()?; + let index = self.reader.read_var_u32()?; + let addend = match ty { + RelocType::FunctionIndexLEB + | RelocType::TableIndexSLEB + | RelocType::TableIndexI32 + | RelocType::TypeIndexLEB + | RelocType::GlobalIndexLEB => None, + RelocType::GlobalAddrLEB | RelocType::GlobalAddrSLEB | RelocType::GlobalAddrI32 => { + Some(self.reader.read_var_u32()?) + } + }; + Ok(Reloc { + ty, + offset, + index, + addend, + }) + } +} + +impl<'a> SectionReader for RelocSectionReader<'a> { + type Item = Reloc; + fn read(&mut self) -> Result<Self::Item> { + RelocSectionReader::read(self) + } + fn eof(&self) -> bool { + self.reader.eof() + } + fn original_position(&self) -> usize { + RelocSectionReader::original_position(self) + } + fn range(&self) -> Range { + self.reader.range() + } +} + +impl<'a> SectionWithLimitedItems for RelocSectionReader<'a> { + fn get_count(&self) -> u32 { + RelocSectionReader::get_count(self) + } +} + +impl<'a> IntoIterator for RelocSectionReader<'a> { + type Item = Result<Reloc>; + type IntoIter = SectionIteratorLimited<RelocSectionReader<'a>>; + + fn into_iter(self) -> Self::IntoIter { + SectionIteratorLimited::new(self) + } +} diff --git a/third_party/rust/wasmparser/src/readers/section_reader.rs b/third_party/rust/wasmparser/src/readers/section_reader.rs new file mode 100644 index 0000000000..395c1a2fd4 --- /dev/null +++ b/third_party/rust/wasmparser/src/readers/section_reader.rs @@ -0,0 +1,124 @@ +/* Copyright 2018 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use super::{BinaryReaderError, Range, Result}; + +pub trait SectionReader { + type Item; + fn read(&mut self) -> Result<Self::Item>; + fn eof(&self) -> bool; + fn original_position(&self) -> usize; + fn range(&self) -> Range; + fn ensure_end(&self) -> Result<()> { + if self.eof() { + return Ok(()); + } + Err(BinaryReaderError::new( + "Unexpected data at the end of the section", + self.original_position(), + )) + } +} + +pub trait SectionWithLimitedItems { + fn get_count(&self) -> u32; +} + +pub struct SectionIterator<R> +where + R: SectionReader, +{ + reader: R, + err: bool, +} + +impl<R> SectionIterator<R> +where + R: SectionReader, +{ + pub fn new(reader: R) -> SectionIterator<R> { + SectionIterator { reader, err: false } + } +} + +impl<R> Iterator for SectionIterator<R> +where + R: SectionReader, +{ + type Item = Result<R::Item>; + + fn next(&mut self) -> Option<Self::Item> { + if self.err || self.reader.eof() { + return None; + } + let result = self.reader.read(); + self.err = result.is_err(); + Some(result) + } +} + +pub struct SectionIteratorLimited<R> +where + R: SectionReader + SectionWithLimitedItems, +{ + reader: R, + left: u32, + end: bool, +} + +impl<R> SectionIteratorLimited<R> +where + R: SectionReader + SectionWithLimitedItems, +{ + pub fn new(reader: R) -> SectionIteratorLimited<R> { + let left = reader.get_count(); + SectionIteratorLimited { + reader, + left, + end: false, + } + } +} + +impl<R> Iterator for SectionIteratorLimited<R> +where + R: SectionReader + SectionWithLimitedItems, +{ + type Item = Result<R::Item>; + + fn next(&mut self) -> Option<Self::Item> { + if self.end { + return None; + } + if self.left == 0 { + return match self.reader.ensure_end() { + Ok(()) => None, + Err(err) => { + self.end = true; + Some(Err(err)) + } + }; + } + let result = self.reader.read(); + self.end = result.is_err(); + self.left -= 1; + Some(result) + } + + fn size_hint(&self) -> (usize, Option<usize>) { + let count = self.reader.get_count() as usize; + (count, Some(count)) + } +} diff --git a/third_party/rust/wasmparser/src/readers/table_section.rs b/third_party/rust/wasmparser/src/readers/table_section.rs new file mode 100644 index 0000000000..40fb4942ee --- /dev/null +++ b/third_party/rust/wasmparser/src/readers/table_section.rs @@ -0,0 +1,89 @@ +/* Copyright 2018 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use super::{ + BinaryReader, Range, Result, SectionIteratorLimited, SectionReader, SectionWithLimitedItems, + TableType, +}; + +#[derive(Clone)] +pub struct TableSectionReader<'a> { + reader: BinaryReader<'a>, + count: u32, +} + +impl<'a> TableSectionReader<'a> { + pub fn new(data: &'a [u8], offset: usize) -> Result<TableSectionReader<'a>> { + let mut reader = BinaryReader::new_with_offset(data, offset); + let count = reader.read_var_u32()?; + Ok(TableSectionReader { reader, count }) + } + + pub fn original_position(&self) -> usize { + self.reader.original_position() + } + + pub fn get_count(&self) -> u32 { + self.count + } + + /// Reads content of the table section. + /// + /// # Examples + /// ``` + /// use wasmparser::TableSectionReader; + /// + /// # let data: &[u8] = &[0x01, 0x70, 0x01, 0x01, 0x01]; + /// let mut table_reader = TableSectionReader::new(data, 0).unwrap(); + /// for _ in 0..table_reader.get_count() { + /// let table = table_reader.read().expect("table"); + /// println!("Table: {:?}", table); + /// } + /// ``` + pub fn read(&mut self) -> Result<TableType> { + self.reader.read_table_type() + } +} + +impl<'a> SectionReader for TableSectionReader<'a> { + type Item = TableType; + fn read(&mut self) -> Result<Self::Item> { + TableSectionReader::read(self) + } + fn eof(&self) -> bool { + self.reader.eof() + } + fn original_position(&self) -> usize { + TableSectionReader::original_position(self) + } + fn range(&self) -> Range { + self.reader.range() + } +} + +impl<'a> SectionWithLimitedItems for TableSectionReader<'a> { + fn get_count(&self) -> u32 { + TableSectionReader::get_count(self) + } +} + +impl<'a> IntoIterator for TableSectionReader<'a> { + type Item = Result<TableType>; + type IntoIter = SectionIteratorLimited<TableSectionReader<'a>>; + + fn into_iter(self) -> Self::IntoIter { + SectionIteratorLimited::new(self) + } +} diff --git a/third_party/rust/wasmparser/src/readers/type_section.rs b/third_party/rust/wasmparser/src/readers/type_section.rs new file mode 100644 index 0000000000..addd756727 --- /dev/null +++ b/third_party/rust/wasmparser/src/readers/type_section.rs @@ -0,0 +1,109 @@ +/* Copyright 2018 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use crate::{ + BinaryReader, BinaryReaderError, Range, Result, SectionIteratorLimited, SectionReader, + SectionWithLimitedItems, TypeDef, +}; + +#[derive(Clone)] +pub struct TypeSectionReader<'a> { + reader: BinaryReader<'a>, + count: u32, +} + +impl<'a> TypeSectionReader<'a> { + pub fn new(data: &'a [u8], offset: usize) -> Result<TypeSectionReader<'a>> { + let mut reader = BinaryReader::new_with_offset(data, offset); + let count = reader.read_var_u32()?; + Ok(TypeSectionReader { reader, count }) + } + + pub fn original_position(&self) -> usize { + self.reader.original_position() + } + + pub fn get_count(&self) -> u32 { + self.count + } + + /// Reads content of the type section. + /// + /// # Examples + /// ``` + /// use wasmparser::TypeSectionReader; + /// # let data: &[u8] = &[0x01, 0x60, 0x00, 0x00]; + /// let mut type_reader = TypeSectionReader::new(data, 0).unwrap(); + /// for _ in 0..type_reader.get_count() { + /// let ty = type_reader.read().expect("type"); + /// println!("Type {:?}", ty); + /// } + /// ``` + pub fn read(&mut self) -> Result<TypeDef<'a>> { + Ok(match self.reader.read_u8()? { + 0x60 => TypeDef::Func(self.reader.read_func_type()?), + 0x61 => TypeDef::Module(self.reader.read_module_type()?), + 0x62 => TypeDef::Instance(self.reader.read_instance_type()?), + _ => { + return Err(BinaryReaderError::new( + "invalid leading byte in type definition", + self.original_position() - 1, + )) + } + }) + } +} + +impl<'a> SectionReader for TypeSectionReader<'a> { + type Item = TypeDef<'a>; + fn read(&mut self) -> Result<Self::Item> { + TypeSectionReader::read(self) + } + fn eof(&self) -> bool { + self.reader.eof() + } + fn original_position(&self) -> usize { + TypeSectionReader::original_position(self) + } + fn range(&self) -> Range { + self.reader.range() + } +} + +impl<'a> SectionWithLimitedItems for TypeSectionReader<'a> { + fn get_count(&self) -> u32 { + TypeSectionReader::get_count(self) + } +} + +impl<'a> IntoIterator for TypeSectionReader<'a> { + type Item = Result<TypeDef<'a>>; + type IntoIter = SectionIteratorLimited<TypeSectionReader<'a>>; + + /// Implements iterator over the type section. + /// + /// # Examples + /// ``` + /// use wasmparser::TypeSectionReader; + /// # let data: &[u8] = &[0x01, 0x60, 0x00, 0x00]; + /// let mut type_reader = TypeSectionReader::new(data, 0).unwrap(); + /// for ty in type_reader { + /// println!("Type {:?}", ty); + /// } + /// ``` + fn into_iter(self) -> Self::IntoIter { + SectionIteratorLimited::new(self) + } +} diff --git a/third_party/rust/wasmparser/src/validator.rs b/third_party/rust/wasmparser/src/validator.rs new file mode 100644 index 0000000000..fe83a3f326 --- /dev/null +++ b/third_party/rust/wasmparser/src/validator.rs @@ -0,0 +1,1845 @@ +/* Copyright 2018 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use crate::limits::*; +use crate::ResizableLimits64; +use crate::WasmModuleResources; +use crate::{Alias, AliasedInstance, ExternalKind, Import, ImportSectionEntryType}; +use crate::{BinaryReaderError, GlobalType, MemoryType, Range, Result, TableType, Type}; +use crate::{DataKind, ElementItem, ElementKind, InitExpr, Instance, Operator}; +use crate::{Export, ExportType, FunctionBody, Parser, Payload}; +use crate::{FuncType, ResizableLimits, SectionReader, SectionWithLimitedItems}; +use std::collections::{HashMap, HashSet}; +use std::mem; +use std::sync::Arc; + +/// Test whether the given buffer contains a valid WebAssembly module, +/// analogous to [`WebAssembly.validate`][js] in the JS API. +/// +/// This functions requires the wasm module is entirely resident in memory and +/// is specified by `bytes`. Additionally this validates the given bytes with +/// the default set of WebAssembly features implemented by `wasmparser`. +/// +/// For more fine-tuned control over validation it's recommended to review the +/// documentation of [`Validator`]. +/// +/// [js]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/validate +pub fn validate(bytes: &[u8]) -> Result<()> { + Validator::new().validate_all(bytes) +} + +#[test] +fn test_validate() { + assert!(validate(&[0x0, 0x61, 0x73, 0x6d, 0x1, 0x0, 0x0, 0x0]).is_ok()); + assert!(validate(&[0x0, 0x61, 0x73, 0x6d, 0x2, 0x0, 0x0, 0x0]).is_err()); +} + +mod func; +pub use func::FuncValidator; + +/// Validator for a WebAssembly binary module. +/// +/// This structure encapsulates state necessary to validate a WebAssembly +/// binary. This implements validation as defined by the [core +/// specification][core]. A `Validator` is designed, like +/// [`Parser`], to accept incremental input over time. +/// Additionally a `Validator` is also designed for parallel validation of +/// functions as they are received. +/// +/// It's expected that you'll be using a [`Parser`] in tandem with a +/// `Validator`. As each [`Payload`](crate::Payload) is received from a +/// [`Parser`] you'll pass it into a `Validator` to test the validity of the +/// payload. Note that all payloads received from a [`Parser`] are expected to +/// be passed to a [`Validator`]. For example if you receive +/// [`Payload::TypeSection`](crate::Payload) you'll call +/// [`Validator::type_section`] to validate this. +/// +/// The design of [`Validator`] is intended that you'll interleave, in your own +/// application's processing, calls to validation. Each variant, after it's +/// received, will be validated and then your application would proceed as +/// usual. At all times, however, you'll have access to the [`Validator`] and +/// the validation context up to that point. This enables applications to check +/// the types of functions and learn how many globals there are, for example. +/// +/// [core]: https://webassembly.github.io/spec/core/valid/index.html +#[derive(Default)] +pub struct Validator { + /// Internal state that is incrementally built-up for the module being + /// validate. This houses type information for all wasm items, like + /// functions. Note that this starts out as a solely owned `Arc<T>` so we can + /// get mutable access, but after we get to the code section this is never + /// mutated to we can clone it cheaply and hand it to sub-validators. + state: arc::MaybeOwned<ModuleState>, + + /// Enabled WebAssembly feature flags, dictating what's valid and what + /// isn't. + features: WasmFeatures, + + /// Where we are, order-wise, in the wasm binary. + order: Order, + + /// The current byte-level offset in the wasm binary. This is updated to + /// produce error messages in `create_error`. + offset: usize, + + /// The number of data segments we ended up finding in this module, or 0 if + /// they either weren't present or none were found. + data_found: u32, + + /// The number of functions we expect to be defined in the code section, or + /// basically the length of the function section if it was found. The next + /// index is where we are, in the function index space, for the next entry + /// in the code section (used to figure out what type is next for the + /// function being validated. + expected_code_bodies: Option<u32>, + code_section_index: usize, + + /// Similar to code bodies above, but for module bodies instead. + expected_modules: Option<u32>, + module_code_section_index: usize, + + /// If this validator is for a nested module then this keeps track of the + /// type of the module that we're matching against. The `expected_type` is + /// an entry in our parent's type index space, and the two positional + /// indices keep track of where we are in matching against imports/exports. + /// + /// Note that the exact algorithm for how it's determine that a submodule + /// matches its declare type is a bit up for debate. For now we go for 1:1 + /// "everything must be equal" matching. This is the subject of + /// WebAssembly/module-linking#7, though. + expected_type: Option<Def<u32>>, + expected_import_pos: usize, + expected_export_pos: usize, +} + +#[derive(Default)] +struct ModuleState { + depth: usize, + types: Vec<ValidatedType>, + tables: Vec<Def<TableType>>, + memories: Vec<MemoryType>, + globals: Vec<Def<GlobalType>>, + element_types: Vec<Type>, + data_count: Option<u32>, + func_type_indices: Vec<Def<u32>>, + module_type_indices: Vec<Def<u32>>, + instance_type_indices: Vec<Def<InstanceDef>>, + function_references: HashSet<u32>, + parent: Option<Arc<ModuleState>>, +} + +/// Flags for features that are enabled for validation. +#[derive(Hash, Debug, Copy, Clone)] +pub struct WasmFeatures { + /// The WebAssembly reference types proposal + pub reference_types: bool, + /// The WebAssembly module linking proposal + pub module_linking: bool, + /// The WebAssembly SIMD proposal + pub simd: bool, + /// The WebAssembly multi-value proposal (enabled by default) + pub multi_value: bool, + /// The WebAssembly threads proposal + pub threads: bool, + /// The WebAssembly tail-call proposal + pub tail_call: bool, + /// The WebAssembly bulk memory operations proposal + pub bulk_memory: bool, + /// Whether or not only deterministic instructions are allowed + pub deterministic_only: bool, + /// The WebAssembly multi memory proposal + pub multi_memory: bool, + /// The WebAssembly memory64 proposal + pub memory64: bool, +} + +impl Default for WasmFeatures { + fn default() -> WasmFeatures { + WasmFeatures { + // off-by-default features + reference_types: false, + module_linking: false, + simd: false, + threads: false, + tail_call: false, + bulk_memory: false, + multi_memory: false, + memory64: false, + deterministic_only: cfg!(feature = "deterministic"), + + // on-by-default features + multi_value: true, + } + } +} + +#[derive(Copy, Clone, PartialOrd, Ord, PartialEq, Eq, Debug)] +enum Order { + Initial, + AfterHeader, + Type, + Import, + ModuleLinkingHeader, + Function, + Table, + Memory, + Global, + Export, + Start, + Element, + DataCount, + ModuleCode, + Code, + Data, +} + +impl Default for Order { + fn default() -> Order { + Order::Initial + } +} + +enum InstanceDef { + Imported { type_idx: u32 }, + Instantiated { module_idx: u32 }, +} + +enum ValidatedType { + Def(TypeDef), + Alias(Def<u32>), +} + +enum TypeDef { + Func(FuncType), + Module(ModuleType), + Instance(InstanceType), +} + +struct ModuleType { + imports: Vec<(String, Option<String>, ImportSectionEntryType)>, + exports: Vec<(String, ImportSectionEntryType)>, +} + +struct InstanceType { + exports: Vec<(String, ImportSectionEntryType)>, +} + +fn to_import(item: &(String, Option<String>, ImportSectionEntryType)) -> Import<'_> { + Import { + module: &item.0, + field: item.1.as_deref(), + ty: item.2, + } +} + +fn to_export(item: &(String, ImportSectionEntryType)) -> ExportType<'_> { + ExportType { + name: &item.0, + ty: item.1, + } +} + +/// Possible return values from [`Validator::payload`]. +pub enum ValidPayload<'a> { + /// The payload validated, no further action need be taken. + Ok, + /// The payload validated, but it started a nested module. + /// + /// This result indicates that the current validator needs to be saved until + /// later. The returned parser and validator should be used instead. + Push(Parser, Validator), + /// The payload validated, and the current validator is finished. The last + /// validator that was in use should be popped off the stack to resume. + Pop, + /// A function was found to be validate. + Func(FuncValidator<ValidatorResources>, FunctionBody<'a>), +} + +impl Validator { + /// Creates a new [`Validator`] ready to validate a WebAssembly module. + /// + /// The new validator will receive payloads parsed from + /// [`Parser`], and expects the first payload received to be + /// the version header from the parser. + pub fn new() -> Validator { + Validator::default() + } + + /// Configures the enabled WebAssembly features for this `Validator`. + pub fn wasm_features(&mut self, features: WasmFeatures) -> &mut Validator { + self.features = features; + self + } + + /// Validates an entire in-memory module with this validator. + /// + /// This function will internally create a [`Parser`] to parse the `bytes` + /// provided. The entire wasm module specified by `bytes` will be parsed and + /// validated. Parse and validation errors will be returned through + /// `Err(_)`, and otherwise a successful validation means `Ok(())` is + /// returned. + pub fn validate_all(self, bytes: &[u8]) -> Result<()> { + let mut functions_to_validate = Vec::new(); + let mut stack = Vec::new(); + let mut cur = self; + for payload in Parser::new(0).parse_all(bytes) { + match cur.payload(&payload?)? { + ValidPayload::Ok => {} + ValidPayload::Pop => cur = stack.pop().unwrap(), + ValidPayload::Push(_parser, validator) => { + stack.push(cur); + cur = validator + } + ValidPayload::Func(validator, ops) => functions_to_validate.push((validator, ops)), + } + } + + for (mut validator, body) in functions_to_validate { + validator.validate(&body)?; + } + Ok(()) + } + + /// Convenience function to validate a single [`Payload`]. + /// + /// This function is intended to be used as a convenience. It will + /// internally perform any validation necessary to validate the [`Payload`] + /// provided. The convenience part is that you're likely already going to + /// be matching on [`Payload`] in your application, at which point it's more + /// appropriate to call the individual methods on [`Validator`] per-variant + /// in [`Payload`], such as [`Validator::type_section`]. + /// + /// This function returns a [`ValidPayload`] variant on success, indicating + /// one of a few possible actions that need to be taken after a payload is + /// validated. For example function contents are not validated here, they're + /// returned through [`ValidPayload`] for validation by the caller. + pub fn payload<'a>(&mut self, payload: &Payload<'a>) -> Result<ValidPayload<'a>> { + use crate::Payload::*; + match payload { + Version { num, range } => self.version(*num, range)?, + TypeSection(s) => self.type_section(s)?, + ImportSection(s) => self.import_section(s)?, + AliasSection(s) => self.alias_section(s)?, + InstanceSection(s) => self.instance_section(s)?, + ModuleSection(s) => self.module_section(s)?, + FunctionSection(s) => self.function_section(s)?, + TableSection(s) => self.table_section(s)?, + MemorySection(s) => self.memory_section(s)?, + GlobalSection(s) => self.global_section(s)?, + ExportSection(s) => self.export_section(s)?, + StartSection { func, range } => self.start_section(*func, range)?, + ElementSection(s) => self.element_section(s)?, + DataCountSection { count, range } => self.data_count_section(*count, range)?, + CodeSectionStart { + count, + range, + size: _, + } => self.code_section_start(*count, range)?, + CodeSectionEntry(body) => { + let func_validator = self.code_section_entry()?; + return Ok(ValidPayload::Func(func_validator, body.clone())); + } + ModuleCodeSectionStart { + count, + range, + size: _, + } => self.module_code_section_start(*count, range)?, + DataSection(s) => self.data_section(s)?, + End => { + self.end()?; + return Ok(if self.state.depth > 0 { + ValidPayload::Pop + } else { + ValidPayload::Ok + }); + } + + CustomSection { .. } => {} // no validation for custom sections + UnknownSection { id, range, .. } => self.unknown_section(*id, range)?, + ModuleCodeSectionEntry { parser, range: _ } => { + let subvalidator = self.module_code_section_entry(); + return Ok(ValidPayload::Push(parser.clone(), subvalidator)); + } + } + Ok(ValidPayload::Ok) + } + + fn create_error<T>(&self, msg: impl Into<String>) -> Result<T> { + Err(BinaryReaderError::new(msg.into(), self.offset)) + } + + /// Validates [`Payload::Version`](crate::Payload) + pub fn version(&mut self, num: u32, range: &Range) -> Result<()> { + self.offset = range.start; + if self.order != Order::Initial { + return self.create_error("wasm version header out of order"); + } + self.order = Order::AfterHeader; + if num != 1 { + return self.create_error("bad wasm file version"); + } + Ok(()) + } + + fn update_order(&mut self, order: Order) -> Result<()> { + let prev = mem::replace(&mut self.order, order); + // If the previous section came before this section, then that's always + // valid. + if prev < order { + return Ok(()); + } + // ... otherwise if this is a repeated section then only the "module + // linking header" is allows to have repeats + if prev == self.order && self.order == Order::ModuleLinkingHeader { + return Ok(()); + } + self.create_error("section out of order") + } + + fn header_order(&mut self, order: Order) -> Order { + if self.features.module_linking { + Order::ModuleLinkingHeader + } else { + order + } + } + + fn get_type<'me>(&'me self, idx: Def<u32>) -> Result<Def<&'me TypeDef>> { + match self.state.get_type(idx) { + Some(t) => Ok(t), + None => self.create_error("unknown type: type index out of bounds"), + } + } + + fn get_table<'me>(&'me self, idx: Def<u32>) -> Result<&'me Def<TableType>> { + match self.state.get_table(idx) { + Some(t) => Ok(t), + None => self.create_error("unknown table: table index out of bounds"), + } + } + + fn get_memory<'me>(&'me self, idx: Def<u32>) -> Result<&'me MemoryType> { + match self.state.get_memory(idx) { + Some(t) => Ok(t), + None => self.create_error("unknown memory: memory index out of bounds"), + } + } + + fn get_global<'me>(&'me self, idx: Def<u32>) -> Result<&'me Def<GlobalType>> { + match self.state.get_global(idx) { + Some(t) => Ok(t), + None => self.create_error("unknown global: global index out of bounds"), + } + } + + fn get_func_type_index<'me>(&'me self, idx: Def<u32>) -> Result<Def<u32>> { + match self.state.get_func_type_index(idx) { + Some(t) => Ok(t), + None => self.create_error(format!( + "unknown function {}: func index out of bounds", + idx.item + )), + } + } + + fn get_module_type_index<'me>(&'me self, idx: Def<u32>) -> Result<Def<u32>> { + match self.state.get_module_type_index(idx) { + Some(t) => Ok(t), + None => self.create_error("unknown module: module index out of bounds"), + } + } + + fn get_instance_def<'me>(&'me self, idx: Def<u32>) -> Result<&'me Def<InstanceDef>> { + match self.state.get_instance_def(idx) { + Some(t) => Ok(t), + None => self.create_error("unknown instance: instance index out of bounds"), + } + } + + fn func_type_at<'me>(&'me self, type_index: Def<u32>) -> Result<Def<&'me FuncType>> { + let def = self.get_type(type_index)?; + match &def.item { + TypeDef::Func(item) => Ok(def.with(item)), + _ => self.create_error("type index is not a function"), + } + } + + fn module_type_at<'me>(&'me self, type_index: Def<u32>) -> Result<Def<&'me ModuleType>> { + if !self.features.module_linking { + return self.create_error("module linking proposal not enabled"); + } + let ty = self.get_type(type_index)?; + match &ty.item { + TypeDef::Module(item) => Ok(ty.with(item)), + _ => self.create_error("type index is not a module"), + } + } + + fn instance_type_at<'me>(&'me self, type_index: Def<u32>) -> Result<Def<&'me InstanceType>> { + if !self.features.module_linking { + return self.create_error("module linking proposal not enabled"); + } + let def = self.get_type(type_index)?; + match &def.item { + TypeDef::Instance(item) => Ok(def.with(item)), + _ => self.create_error("type index is not an instance"), + } + } + + fn check_max(&self, cur_len: usize, amt_added: u32, max: usize, desc: &str) -> Result<()> { + let overflow = max + .checked_sub(cur_len) + .and_then(|amt| amt.checked_sub(amt_added as usize)) + .is_none(); + if overflow { + return if max == 1 { + self.create_error(format!("multiple {}", desc)) + } else { + self.create_error(format!("{} count is out of bounds", desc)) + }; + } + Ok(()) + } + + fn section<T>( + &mut self, + order: Order, + section: &T, + mut validate_item: impl FnMut(&mut Self, T::Item) -> Result<()>, + ) -> Result<()> + where + T: SectionReader + Clone + SectionWithLimitedItems, + { + self.offset = section.range().start; + self.update_order(order)?; + + let mut section = section.clone(); + for _ in 0..section.get_count() { + self.offset = section.original_position(); + let item = section.read()?; + validate_item(self, item)?; + } + self.offset = section.range().end; + section.ensure_end()?; + Ok(()) + } + + /// Validates [`Payload::TypeSection`](crate::Payload) + pub fn type_section(&mut self, section: &crate::TypeSectionReader<'_>) -> Result<()> { + let order = self.header_order(Order::Type); + self.check_max( + self.state.types.len(), + section.get_count(), + MAX_WASM_TYPES, + "types", + )?; + self.section(order, section, |me, item| me.type_def(item)) + } + + fn type_def(&mut self, def: crate::TypeDef<'_>) -> Result<()> { + let def = match def { + crate::TypeDef::Func(t) => { + for ty in t.params.iter().chain(t.returns.iter()) { + self.value_type(*ty)?; + } + if t.returns.len() > 1 && !self.features.multi_value { + return self + .create_error("invalid result arity: func type returns multiple values"); + } + TypeDef::Func(t) + } + crate::TypeDef::Module(t) => { + if !self.features.module_linking { + return self.create_error("module linking proposal not enabled"); + } + let imports = t + .imports + .iter() + .map(|i| { + self.import_entry_type(&i.ty)?; + Ok((i.module.to_string(), i.field.map(|i| i.to_string()), i.ty)) + }) + .collect::<Result<_>>()?; + let mut names = HashSet::new(); + let exports = t + .exports + .iter() + .map(|e| { + if !names.insert(e.name) { + return self.create_error("duplicate export name"); + } + self.import_entry_type(&e.ty)?; + Ok((e.name.to_string(), e.ty)) + }) + .collect::<Result<_>>()?; + TypeDef::Module(ModuleType { imports, exports }) + } + crate::TypeDef::Instance(t) => { + if !self.features.module_linking { + return self.create_error("module linking proposal not enabled"); + } + let mut names = HashSet::new(); + let exports = t + .exports + .iter() + .map(|e| { + if !names.insert(e.name) { + return self.create_error("duplicate export name"); + } + self.import_entry_type(&e.ty)?; + Ok((e.name.to_string(), e.ty)) + }) + .collect::<Result<_>>()?; + TypeDef::Instance(InstanceType { exports }) + } + }; + let def = ValidatedType::Def(def); + self.state.assert_mut().types.push(def); + Ok(()) + } + + fn value_type(&self, ty: Type) -> Result<()> { + match self.features.check_value_type(ty) { + Ok(()) => Ok(()), + Err(e) => self.create_error(e), + } + } + + fn import_entry_type(&self, import_type: &ImportSectionEntryType) -> Result<()> { + match import_type { + ImportSectionEntryType::Function(type_index) => { + self.func_type_at(self.state.def(*type_index))?; + Ok(()) + } + ImportSectionEntryType::Table(t) => self.table_type(t), + ImportSectionEntryType::Memory(t) => self.memory_type(t), + ImportSectionEntryType::Global(t) => self.global_type(t), + ImportSectionEntryType::Module(type_index) => { + self.module_type_at(self.state.def(*type_index))?; + Ok(()) + } + ImportSectionEntryType::Instance(type_index) => { + self.instance_type_at(self.state.def(*type_index))?; + Ok(()) + } + } + } + + fn table_type(&self, ty: &TableType) -> Result<()> { + match ty.element_type { + Type::FuncRef => {} + Type::ExternRef => { + if !self.features.reference_types { + return self.create_error("element is not anyfunc"); + } + } + _ => return self.create_error("element is not reference type"), + } + self.limits(&ty.limits)?; + if ty.limits.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"); + } + } + } + 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"); + } + } + } + } + Ok(()) + } + + fn global_type(&self, ty: &GlobalType) -> Result<()> { + 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 { + return self.create_error("size minimum must not be greater than maximum"); + } + } + Ok(()) + } + + /// Validates [`Payload::ImportSection`](crate::Payload) + pub fn import_section(&mut self, section: &crate::ImportSectionReader<'_>) -> Result<()> { + let order = self.header_order(Order::Import); + self.section(order, section, |me, item| me.import(item)) + } + + fn import(&mut self, entry: Import<'_>) -> Result<()> { + if !self.features.module_linking && entry.field.is_none() { + return self.create_error("module linking proposal is not enabled"); + } + self.import_entry_type(&entry.ty)?; + let (len, max, desc) = match entry.ty { + ImportSectionEntryType::Function(type_index) => { + let def = self.state.def(type_index); + let state = self.state.assert_mut(); + state.func_type_indices.push(def); + (state.func_type_indices.len(), MAX_WASM_FUNCTIONS, "funcs") + } + ImportSectionEntryType::Table(ty) => { + let def = self.state.def(ty); + let state = self.state.assert_mut(); + state.tables.push(def); + (state.tables.len(), self.max_tables(), "tables") + } + ImportSectionEntryType::Memory(ty) => { + let state = self.state.assert_mut(); + state.memories.push(ty); + (state.memories.len(), self.max_memories(), "memories") + } + ImportSectionEntryType::Global(ty) => { + let def = self.state.def(ty); + let state = self.state.assert_mut(); + state.globals.push(def); + (state.globals.len(), MAX_WASM_GLOBALS, "globals") + } + ImportSectionEntryType::Instance(type_idx) => { + let def = self.state.def(InstanceDef::Imported { type_idx }); + let state = self.state.assert_mut(); + state.instance_type_indices.push(def); + ( + state.instance_type_indices.len(), + MAX_WASM_INSTANCES, + "instances", + ) + } + ImportSectionEntryType::Module(type_index) => { + let def = self.state.def(type_index); + let state = self.state.assert_mut(); + state.module_type_indices.push(def); + (state.module_type_indices.len(), MAX_WASM_MODULES, "modules") + } + }; + self.check_max(len, 0, max, desc)?; + + if let Some(ty) = self.expected_type { + let idx = self.expected_import_pos; + self.expected_import_pos += 1; + let module_ty = self.module_type_at(ty)?; + let equal = match module_ty.item.imports.get(idx) { + Some(import) => { + self.imports_equal(self.state.def(entry), module_ty.with(to_import(import))) + } + None => false, + }; + if !equal { + return self.create_error("inline module type does not match declared type"); + } + } + Ok(()) + } + + /// Validates [`Payload::AliasSection`](crate::Payload) + pub fn alias_section(&mut self, section: &crate::AliasSectionReader<'_>) -> Result<()> { + if !self.features.module_linking { + return self.create_error("module linking proposal not enabled"); + } + self.section(Order::ModuleLinkingHeader, section, |me, a| me.alias(a)) + } + + fn alias(&mut self, alias: Alias) -> Result<()> { + match alias.instance { + AliasedInstance::Child(instance_idx) => { + let ty = self.get_instance_def(self.state.def(instance_idx))?; + let exports = match ty.item { + InstanceDef::Imported { type_idx } => { + let ty = self.instance_type_at(ty.with(type_idx))?; + ty.map(|t| &t.exports) + } + InstanceDef::Instantiated { module_idx } => { + let ty = self.get_module_type_index(ty.with(module_idx))?; + let ty = self.module_type_at(ty)?; + ty.map(|t| &t.exports) + } + }; + let export = match exports.item.get(alias.index as usize) { + Some(e) => e, + None => { + return self.create_error("aliased export index out of bounds"); + } + }; + match (export.1, alias.kind) { + (ImportSectionEntryType::Function(ty), ExternalKind::Function) => { + let def = exports.with(ty); + self.state.assert_mut().func_type_indices.push(def); + } + (ImportSectionEntryType::Table(ty), ExternalKind::Table) => { + let def = exports.with(ty); + self.state.assert_mut().tables.push(def); + } + (ImportSectionEntryType::Memory(ty), ExternalKind::Memory) => { + self.state.assert_mut().memories.push(ty); + } + (ImportSectionEntryType::Global(ty), ExternalKind::Global) => { + let def = exports.with(ty); + self.state.assert_mut().globals.push(def); + } + (ImportSectionEntryType::Instance(ty), ExternalKind::Instance) => { + let def = exports.with(InstanceDef::Imported { type_idx: ty }); + self.state.assert_mut().instance_type_indices.push(def); + } + (ImportSectionEntryType::Module(ty), ExternalKind::Module) => { + let def = exports.with(ty); + self.state.assert_mut().module_type_indices.push(def); + } + _ => return self.create_error("alias kind mismatch with export kind"), + } + } + AliasedInstance::Parent => { + let idx = match self.state.depth.checked_sub(1) { + None => return self.create_error("no parent module to alias from"), + Some(depth) => Def { + depth, + item: alias.index, + }, + }; + match alias.kind { + ExternalKind::Module => { + let ty = self.get_module_type_index(idx)?; + self.state.assert_mut().module_type_indices.push(ty); + } + ExternalKind::Type => { + // make sure this type actually exists, then push it as + // ourselve aliasing that type. + self.get_type(idx)?; + self.state + .assert_mut() + .types + .push(ValidatedType::Alias(idx)); + } + _ => return self.create_error("only parent types/modules can be aliased"), + } + } + } + + Ok(()) + } + + /// Validates [`Payload::ModuleSection`](crate::Payload) + pub fn module_section(&mut self, section: &crate::ModuleSectionReader<'_>) -> Result<()> { + if !self.features.module_linking { + return self.create_error("module linking proposal not enabled"); + } + self.check_max( + self.state.module_type_indices.len(), + section.get_count(), + MAX_WASM_MODULES, + "modules", + )?; + self.expected_modules = Some(section.get_count() + self.expected_modules.unwrap_or(0)); + self.section(Order::ModuleLinkingHeader, section, |me, type_index| { + let type_index = me.state.def(type_index); + me.module_type_at(type_index)?; + me.state.assert_mut().module_type_indices.push(type_index); + Ok(()) + }) + } + + /// Validates [`Payload::InstanceSection`](crate::Payload) + pub fn instance_section(&mut self, section: &crate::InstanceSectionReader<'_>) -> Result<()> { + if !self.features.module_linking { + return self.create_error("module linking proposal not enabled"); + } + self.check_max( + self.state.instance_type_indices.len(), + section.get_count(), + MAX_WASM_INSTANCES, + "instances", + )?; + self.section(Order::ModuleLinkingHeader, section, |me, i| me.instance(i)) + } + + fn instance(&mut self, instance: Instance<'_>) -> Result<()> { + // Fetch the type of the instantiated module so we can typecheck all of + // the import items. + let module_idx = instance.module(); + let module_ty = self.get_module_type_index(self.state.def(module_idx))?; + let ty = self.module_type_at(module_ty)?; + + // Make sure the right number of imports are provided + let mut args = instance.args()?; + if args.get_count() as usize != ty.item.imports.len() { + return self.create_error("wrong number of imports provided"); + } + + // Now pairwise match each import against the expected type, making sure + // that the provided type is a subtype of the expected type. + for import_ty in ty.item.imports.iter() { + let (kind, index) = args.read()?; + let index = self.state.def(index); + let actual = match kind { + ExternalKind::Function => self + .get_func_type_index(index)? + .map(ImportSectionEntryType::Function), + ExternalKind::Table => self.get_table(index)?.map(ImportSectionEntryType::Table), + ExternalKind::Memory => self + .state + .def(ImportSectionEntryType::Memory(*self.get_memory(index)?)), + ExternalKind::Global => self.get_global(index)?.map(ImportSectionEntryType::Global), + ExternalKind::Module => self + .get_module_type_index(index)? + .map(ImportSectionEntryType::Module), + ExternalKind::Instance => { + let def = self.get_instance_def(index)?; + match def.item { + InstanceDef::Imported { type_idx } => { + def.with(ImportSectionEntryType::Instance(type_idx)) + } + InstanceDef::Instantiated { module_idx } => { + let expected = match import_ty.2 { + ImportSectionEntryType::Instance(idx) => ty.with(idx), + _ => { + return self + .create_error("wrong kind of item used for instantiate") + } + }; + let expected = self.instance_type_at(expected)?; + let module_idx = def.with(module_idx); + let actual = self.get_module_type_index(module_idx)?; + let actual = self.module_type_at(actual)?; + self.check_export_sets_match( + expected.map(|m| &*m.exports), + actual.map(|m| &*m.exports), + )?; + continue; + } + } + } + ExternalKind::Type => return self.create_error("cannot export types"), + }; + let item = actual.item; + self.check_imports_match(ty.with(&import_ty.2), actual.map(|_| &item))?; + } + args.ensure_end()?; + + let def = self.state.def(InstanceDef::Instantiated { module_idx }); + self.state.assert_mut().instance_type_indices.push(def); + Ok(()) + } + + // Note that this function is basically implementing + // https://webassembly.github.io/spec/core/exec/modules.html#import-matching + fn check_imports_match( + &self, + expected: Def<&ImportSectionEntryType>, + actual: Def<&ImportSectionEntryType>, + ) -> Result<()> { + macro_rules! limits_match { + ($expected:expr, $actual:expr) => {{ + let expected = $expected; + let actual = $actual; + actual.initial >= expected.initial + && match expected.maximum { + Some(expected_max) => match actual.maximum { + Some(actual_max) => actual_max <= expected_max, + None => false, + }, + None => true, + } + }}; + } + match (expected.item, actual.item) { + ( + ImportSectionEntryType::Function(expected_idx), + ImportSectionEntryType::Function(actual_idx), + ) => { + let expected = self.func_type_at(expected.map(|_| *expected_idx))?; + let actual = self.func_type_at(actual.map(|_| *actual_idx))?; + if actual.item == expected.item { + return Ok(()); + } + self.create_error("function provided for instantiation has wrong type") + } + (ImportSectionEntryType::Table(expected), ImportSectionEntryType::Table(actual)) => { + if expected.element_type == actual.element_type + && limits_match!(&expected.limits, &actual.limits) + { + return Ok(()); + } + self.create_error("table provided for instantiation has wrong type") + } + (ImportSectionEntryType::Memory(expected), ImportSectionEntryType::Memory(actual)) => { + match (expected, actual) { + ( + MemoryType::M32 { + limits: a, + shared: ash, + }, + MemoryType::M32 { + limits: b, + shared: bsh, + }, + ) => { + if limits_match!(a, b) && ash == bsh { + return Ok(()); + } + } + ( + MemoryType::M64 { + limits: a, + shared: ash, + }, + MemoryType::M64 { + limits: b, + shared: bsh, + }, + ) => { + if limits_match!(a, b) && ash == bsh { + return Ok(()); + } + } + _ => {} + } + self.create_error("memory provided for instantiation has wrong type") + } + (ImportSectionEntryType::Global(expected), ImportSectionEntryType::Global(actual)) => { + if expected == actual { + return Ok(()); + } + self.create_error("global provided for instantiation has wrong type") + } + ( + ImportSectionEntryType::Instance(expected_idx), + ImportSectionEntryType::Instance(actual_idx), + ) => { + let expected = self.instance_type_at(expected.map(|_| *expected_idx))?; + let actual = self.instance_type_at(actual.map(|_| *actual_idx))?; + self.check_export_sets_match( + expected.map(|i| &*i.exports), + actual.map(|i| &*i.exports), + )?; + Ok(()) + } + ( + ImportSectionEntryType::Module(expected_idx), + ImportSectionEntryType::Module(actual_idx), + ) => { + let expected = self.module_type_at(expected.map(|_| *expected_idx))?; + let actual = self.module_type_at(actual.map(|_| *actual_idx))?; + if expected.item.imports.len() != actual.item.imports.len() { + return self.create_error("mismatched number of module imports"); + } + for (a, b) in expected.item.imports.iter().zip(actual.item.imports.iter()) { + self.check_imports_match(expected.map(|_| &a.2), actual.map(|_| &b.2))?; + } + self.check_export_sets_match( + expected.map(|i| &*i.exports), + actual.map(|i| &*i.exports), + )?; + Ok(()) + } + _ => self.create_error("wrong kind of item used for instantiate"), + } + } + + fn check_export_sets_match( + &self, + expected: Def<&[(String, ImportSectionEntryType)]>, + actual: Def<&[(String, ImportSectionEntryType)]>, + ) -> Result<()> { + let name_to_idx = actual + .item + .iter() + .enumerate() + .map(|(i, e)| (&e.0, i)) + .collect::<HashMap<_, _>>(); + for expected_export in expected.item { + let idx = match name_to_idx.get(&expected_export.0) { + Some(i) => *i, + None => { + return self.create_error(&format!("no export named `{}`", expected_export.0)) + } + }; + self.check_imports_match( + expected.map(|_| &expected_export.1), + actual.map(|_| &actual.item[idx].1), + )?; + } + Ok(()) + } + + /// Validates [`Payload::FunctionSection`](crate::Payload) + pub fn function_section(&mut self, section: &crate::FunctionSectionReader<'_>) -> Result<()> { + self.expected_code_bodies = Some(section.get_count()); + self.check_max( + self.state.func_type_indices.len(), + section.get_count(), + MAX_WASM_FUNCTIONS, + "funcs", + )?; + // Assert that each type index is indeed a function type, and otherwise + // just push it for handling later. + self.section(Order::Function, section, |me, i| { + let type_index = me.state.def(i); + me.func_type_at(type_index)?; + me.state.assert_mut().func_type_indices.push(type_index); + Ok(()) + }) + } + + fn max_tables(&self) -> usize { + if self.features.reference_types || self.features.module_linking { + MAX_WASM_TABLES + } else { + 1 + } + } + + /// Validates [`Payload::TableSection`](crate::Payload) + pub fn table_section(&mut self, section: &crate::TableSectionReader<'_>) -> Result<()> { + self.check_max( + self.state.tables.len(), + section.get_count(), + self.max_tables(), + "tables", + )?; + self.section(Order::Table, section, |me, ty| { + me.table_type(&ty)?; + let def = me.state.def(ty); + me.state.assert_mut().tables.push(def); + Ok(()) + }) + } + + fn max_memories(&self) -> usize { + if self.features.multi_memory { + MAX_WASM_MEMORIES + } else { + 1 + } + } + + pub fn memory_section(&mut self, section: &crate::MemorySectionReader<'_>) -> Result<()> { + self.check_max( + self.state.memories.len(), + section.get_count(), + self.max_memories(), + "memories", + )?; + self.section(Order::Memory, section, |me, ty| { + me.memory_type(&ty)?; + me.state.assert_mut().memories.push(ty); + Ok(()) + }) + } + + /// Validates [`Payload::GlobalSection`](crate::Payload) + pub fn global_section(&mut self, section: &crate::GlobalSectionReader<'_>) -> Result<()> { + self.check_max( + self.state.globals.len(), + section.get_count(), + MAX_WASM_GLOBALS, + "globals", + )?; + self.section(Order::Global, section, |me, g| { + me.global_type(&g.ty)?; + me.init_expr(&g.init_expr, g.ty.content_type, false)?; + let def = me.state.def(g.ty); + me.state.assert_mut().globals.push(def); + Ok(()) + }) + } + + fn init_expr(&mut self, expr: &InitExpr<'_>, expected_ty: Type, allow32: bool) -> Result<()> { + let mut ops = expr.get_operators_reader().into_iter_with_offsets(); + let (op, offset) = match ops.next() { + Some(Err(e)) => return Err(e), + Some(Ok(pair)) => pair, + None => return self.create_error("type mismatch: init_expr is empty"), + }; + self.offset = offset; + let ty = match op { + Operator::I32Const { .. } => Type::I32, + Operator::I64Const { .. } => Type::I64, + Operator::F32Const { .. } => Type::F32, + Operator::F64Const { .. } => Type::F64, + Operator::RefNull { ty } => ty, + Operator::V128Const { .. } => Type::V128, + Operator::GlobalGet { global_index } => { + self.get_global(self.state.def(global_index))? + .item + .content_type + } + Operator::RefFunc { function_index } => { + self.get_func_type_index(self.state.def(function_index))?; + self.state + .assert_mut() + .function_references + .insert(function_index); + Type::FuncRef + } + Operator::End => return self.create_error("type mismatch: init_expr is empty"), + _ => { + return self + .create_error("constant expression required: invalid init_expr operator") + } + }; + if ty != expected_ty { + if !allow32 || ty != Type::I32 { + return self.create_error("type mismatch: invalid init_expr type"); + } + } + + // Make sure the next instruction is an `end` + match ops.next() { + Some(Err(e)) => return Err(e), + Some(Ok((Operator::End, _))) => {} + Some(Ok(_)) => { + return self + .create_error("constant expression required: type mismatch: only one init_expr operator is expected") + } + None => return self.create_error("type mismatch: init_expr is not terminated"), + } + + // ... and verify we're done after that + match ops.next() { + Some(Err(e)) => Err(e), + Some(Ok(_)) => { + self.create_error("constant expression required: invalid init_expr operator") + } + None => Ok(()), + } + } + + /// Validates [`Payload::ExportSection`](crate::Payload) + pub fn export_section(&mut self, section: &crate::ExportSectionReader<'_>) -> Result<()> { + let mut exported_names = HashSet::new(); + self.section(Order::Export, section, |me, e| { + if !exported_names.insert(e.field.to_string()) { + return me.create_error("duplicate export name"); + } + if let ExternalKind::Type = e.kind { + return me.create_error("cannot export types"); + } + me.check_external_kind("exported", e.kind, e.index)?; + if !me.export_is_expected(e)? { + return me.create_error("inline module type does not match declared type"); + } + Ok(()) + }) + } + + fn check_external_kind(&mut self, desc: &str, kind: ExternalKind, index: u32) -> Result<()> { + let (ty, total) = match kind { + ExternalKind::Function => ("function", self.state.func_type_indices.len()), + ExternalKind::Table => ("table", self.state.tables.len()), + ExternalKind::Memory => ("memory", self.state.memories.len()), + ExternalKind::Global => ("global", self.state.globals.len()), + ExternalKind::Module => ("module", self.state.module_type_indices.len()), + ExternalKind::Instance => ("instance", self.state.instance_type_indices.len()), + ExternalKind::Type => return self.create_error("cannot export types"), + }; + if index as usize >= total { + return self.create_error(&format!( + "unknown {ty} {index}: {desc} {ty} index out of bounds", + desc = desc, + index = index, + ty = ty, + )); + } + if let ExternalKind::Function = kind { + self.state.assert_mut().function_references.insert(index); + } + Ok(()) + } + + fn export_is_expected(&mut self, actual: Export<'_>) -> Result<bool> { + let expected_ty = match self.expected_type { + Some(ty) => ty, + None => return Ok(true), + }; + let idx = self.expected_export_pos; + self.expected_export_pos += 1; + let module_ty = self.module_type_at(expected_ty)?; + let expected = match module_ty.item.exports.get(idx) { + Some(expected) => module_ty.with(to_export(expected)), + None => return Ok(false), + }; + let index = self.state.def(actual.index); + let ty = match actual.kind { + ExternalKind::Function => self + .get_func_type_index(index)? + .map(ImportSectionEntryType::Function), + ExternalKind::Table => self.get_table(index)?.map(ImportSectionEntryType::Table), + ExternalKind::Memory => { + let mem = *self.get_memory(index)?; + let ty = ImportSectionEntryType::Memory(mem); + self.state.def(ty) + } + ExternalKind::Global => self.get_global(index)?.map(ImportSectionEntryType::Global), + ExternalKind::Module => self + .get_module_type_index(index)? + .map(ImportSectionEntryType::Module), + ExternalKind::Instance => { + let def = self.get_instance_def(index)?; + match def.item { + InstanceDef::Imported { type_idx } => { + def.with(ImportSectionEntryType::Instance(type_idx)) + } + InstanceDef::Instantiated { module_idx } => { + let a = self.get_module_type_index(def.with(module_idx))?; + let a = self.module_type_at(a)?; + let b = match expected.item.ty { + ImportSectionEntryType::Instance(idx) => { + self.instance_type_at(expected.with(idx))? + } + _ => return Ok(false), + }; + return Ok(actual.field == expected.item.name + && a.item.exports.len() == b.item.exports.len() + && a.item + .exports + .iter() + .map(to_export) + .zip(b.item.exports.iter().map(to_export)) + .all(|(ae, be)| self.exports_equal(a.with(ae), b.with(be)))); + } + } + } + ExternalKind::Type => unreachable!(), // already validated to not exist + }; + let actual = ty.map(|ty| ExportType { + name: actual.field, + ty, + }); + Ok(self.exports_equal(actual, expected)) + } + + fn imports_equal(&self, a: Def<Import<'_>>, b: Def<Import<'_>>) -> bool { + a.item.module == b.item.module + && a.item.field == b.item.field + && self.import_ty_equal(a.with(&a.item.ty), b.with(&b.item.ty)) + } + + fn exports_equal(&self, a: Def<ExportType<'_>>, b: Def<ExportType<'_>>) -> bool { + a.item.name == b.item.name && self.import_ty_equal(a.with(&a.item.ty), b.with(&b.item.ty)) + } + + fn import_ty_equal( + &self, + a: Def<&ImportSectionEntryType>, + b: Def<&ImportSectionEntryType>, + ) -> bool { + match (a.item, b.item) { + (ImportSectionEntryType::Function(ai), ImportSectionEntryType::Function(bi)) => { + self.func_type_at(a.with(*ai)).unwrap().item + == self.func_type_at(b.with(*bi)).unwrap().item + } + (ImportSectionEntryType::Table(a), ImportSectionEntryType::Table(b)) => a == b, + (ImportSectionEntryType::Memory(a), ImportSectionEntryType::Memory(b)) => a == b, + (ImportSectionEntryType::Global(a), ImportSectionEntryType::Global(b)) => a == b, + (ImportSectionEntryType::Instance(ai), ImportSectionEntryType::Instance(bi)) => { + let a = self.instance_type_at(a.with(*ai)).unwrap(); + let b = self.instance_type_at(b.with(*bi)).unwrap(); + a.item.exports.len() == b.item.exports.len() + && a.item + .exports + .iter() + .map(to_export) + .zip(b.item.exports.iter().map(to_export)) + .all(|(ae, be)| self.exports_equal(a.with(ae), b.with(be))) + } + (ImportSectionEntryType::Module(ai), ImportSectionEntryType::Module(bi)) => { + let a = self.module_type_at(a.with(*ai)).unwrap(); + let b = self.module_type_at(b.with(*bi)).unwrap(); + a.item.imports.len() == b.item.imports.len() + && a.item + .imports + .iter() + .map(to_import) + .zip(b.item.imports.iter().map(to_import)) + .all(|(ai, bi)| self.imports_equal(a.with(ai), b.with(bi))) + && a.item.exports.len() == b.item.exports.len() + && a.item + .exports + .iter() + .map(to_export) + .zip(b.item.exports.iter().map(to_export)) + .all(|(ae, be)| self.exports_equal(a.with(ae), b.with(be))) + } + _ => false, + } + } + + /// Validates [`Payload::StartSection`](crate::Payload) + pub fn start_section(&mut self, func: u32, range: &Range) -> Result<()> { + self.offset = range.start; + self.update_order(Order::Start)?; + let ty = self.get_func_type_index(self.state.def(func))?; + let ty = self.func_type_at(ty)?; + if !ty.item.params.is_empty() || !ty.item.returns.is_empty() { + return self.create_error("invalid start function type"); + } + Ok(()) + } + + /// Validates [`Payload::ElementSection`](crate::Payload) + pub fn element_section(&mut self, section: &crate::ElementSectionReader<'_>) -> Result<()> { + self.section(Order::Element, section, |me, e| { + match e.ty { + Type::FuncRef => {} + Type::ExternRef if me.features.reference_types => {} + Type::ExternRef => { + return me + .create_error("reference types must be enabled for anyref elem segment"); + } + _ => return me.create_error("invalid reference type"), + } + match e.kind { + ElementKind::Active { + table_index, + init_expr, + } => { + let table = me.get_table(me.state.def(table_index))?; + if e.ty != table.item.element_type { + return me.create_error("element_type != table type"); + } + me.init_expr(&init_expr, Type::I32, false)?; + } + ElementKind::Passive | ElementKind::Declared => { + if !me.features.bulk_memory { + return me.create_error("reference types must be enabled"); + } + } + } + let mut items = e.items.get_items_reader()?; + if items.get_count() > MAX_WASM_TABLE_ENTRIES as u32 { + return me.create_error("num_elements is out of bounds"); + } + for _ in 0..items.get_count() { + me.offset = items.original_position(); + match items.read()? { + ElementItem::Null(ty) => { + if ty != e.ty { + return me.create_error( + "type mismatch: null type doesn't match element type", + ); + } + } + ElementItem::Func(f) => { + if e.ty != Type::FuncRef { + return me + .create_error("type mismatch: segment does not have funcref type"); + } + me.get_func_type_index(me.state.def(f))?; + me.state.assert_mut().function_references.insert(f); + } + } + } + + me.state.assert_mut().element_types.push(e.ty); + Ok(()) + }) + } + + /// Validates [`Payload::DataCountSection`](crate::Payload) + pub fn data_count_section(&mut self, count: u32, range: &Range) -> Result<()> { + self.offset = range.start; + self.update_order(Order::DataCount)?; + self.state.assert_mut().data_count = Some(count); + if count > MAX_WASM_DATA_SEGMENTS as u32 { + return self.create_error("data count section specifies too many data segments"); + } + Ok(()) + } + + /// Validates [`Payload::ModuleCodeSectionStart`](crate::Payload) + pub fn module_code_section_start(&mut self, count: u32, range: &Range) -> Result<()> { + if !self.features.module_linking { + return self.create_error("module linking proposal not enabled"); + } + self.offset = range.start; + self.update_order(Order::ModuleCode)?; + match self.expected_modules.take() { + Some(n) if n == count => {} + Some(_) => { + return self + .create_error("module and module code section have inconsistent lengths"); + } + None if count == 0 => {} + None => return self.create_error("module code section without module section"), + } + self.module_code_section_index = self.state.module_type_indices.len() - (count as usize); + Ok(()) + } + + /// Validates [`Payload::ModuleCodeSectionEntry`](crate::Payload). + /// + /// Note that this does not actually perform any validation itself. The + /// `ModuleCodeSectionEntry` payload is associated with a sub-parser for the + /// sub-module, and it's expected that the returned [`Validator`] will be + /// paired with the [`Parser`] otherwise used with the module. + /// + /// Note that the returned [`Validator`] should be used for the nested + /// module. It will correctly work with parent aliases as well as ensure the + /// type of the inline module matches the declared type. Using + /// [`Validator::new`] will result in incorrect validation. + pub fn module_code_section_entry<'a>(&mut self) -> Validator { + let mut ret = Validator::default(); + ret.features = self.features.clone(); + ret.expected_type = Some(self.state.module_type_indices[self.module_code_section_index]); + self.module_code_section_index += 1; + let state = ret.state.assert_mut(); + state.parent = Some(self.state.arc().clone()); + state.depth = self.state.depth + 1; + return ret; + } + + /// Validates [`Payload::CodeSectionStart`](crate::Payload). + pub fn code_section_start(&mut self, count: u32, range: &Range) -> Result<()> { + self.offset = range.start; + self.update_order(Order::Code)?; + match self.expected_code_bodies.take() { + Some(n) if n == count => {} + Some(_) => { + return self.create_error("function and code section have inconsistent lengths"); + } + // empty code sections are allowed even if the function section is + // missing + None if count == 0 => {} + None => return self.create_error("code section without function section"), + } + self.code_section_index = self.state.func_type_indices.len() - (count as usize); + Ok(()) + } + + /// Validates [`Payload::CodeSectionEntry`](crate::Payload). + /// + /// This function will prepare a [`FuncValidator`] which can be used to + /// validate the function. The function body provided will be parsed only + /// enough to create the function validation context. After this the + /// [`OperatorsReader`](crate::readers::OperatorsReader) returned can be used to read the + /// opcodes of the function as well as feed information into the validator. + /// + /// Note that the returned [`FuncValidator`] is "connected" to this + /// [`Validator`] in that it uses the internal context of this validator for + /// validating the function. The [`FuncValidator`] can be sent to + /// another thread, for example, to offload actual processing of functions + /// elsewhere. + pub fn code_section_entry(&mut self) -> Result<FuncValidator<ValidatorResources>> { + let ty_index = self.state.func_type_indices[self.code_section_index]; + self.code_section_index += 1; + let resources = ValidatorResources(self.state.arc().clone()); + Ok(FuncValidator::new(ty_index.item, 0, resources, &self.features).unwrap()) + } + + /// Validates [`Payload::DataSection`](crate::Payload). + pub fn data_section(&mut self, section: &crate::DataSectionReader<'_>) -> Result<()> { + self.data_found = section.get_count(); + self.check_max(0, section.get_count(), MAX_WASM_DATA_SEGMENTS, "segments")?; + self.section(Order::Data, section, |me, d| { + match d.kind { + DataKind::Passive => {} + DataKind::Active { + memory_index, + init_expr, + } => { + let ty = me.get_memory(me.state.def(memory_index))?.index_type(); + let allow32 = ty == Type::I64; + me.init_expr(&init_expr, ty, allow32)?; + } + } + Ok(()) + }) + } + + /// Validates [`Payload::UnknownSection`](crate::Payload). + /// + /// Currently always returns an error. + pub fn unknown_section(&mut self, id: u8, range: &Range) -> Result<()> { + self.offset = range.start; + self.create_error(format!("invalid section code: {}", id)) + } + + /// Validates [`Payload::End`](crate::Payload). + pub fn end(&mut self) -> Result<()> { + if let Some(data_count) = self.state.data_count { + if data_count != self.data_found { + return self.create_error("data count section and passive data mismatch"); + } + } + if let Some(n) = self.expected_code_bodies.take() { + if n > 0 { + return self.create_error("function and code sections have inconsistent lengths"); + } + } + if let Some(n) = self.expected_modules.take() { + if n > 0 { + return self + .create_error("module and module code sections have inconsistent lengths"); + } + } + if let Some(t) = self.expected_type { + let ty = self.module_type_at(t)?; + if self.expected_import_pos != ty.item.imports.len() + || self.expected_export_pos != ty.item.exports.len() + { + return self.create_error("inline module type does not match declared type"); + } + } + Ok(()) + } +} + +impl WasmFeatures { + pub(crate) fn check_value_type(&self, ty: Type) -> Result<(), &'static str> { + match ty { + Type::I32 | Type::I64 | Type::F32 | Type::F64 => Ok(()), + Type::FuncRef | Type::ExternRef => { + if self.reference_types { + Ok(()) + } else { + Err("reference types support is not enabled") + } + } + Type::V128 => { + if self.simd { + Ok(()) + } else { + Err("SIMD support is not enabled") + } + } + _ => Err("invalid value type"), + } + } +} + +impl ModuleState { + fn def<T>(&self, item: T) -> Def<T> { + Def { + depth: self.depth, + item, + } + } + + fn get<'me, T>( + &'me self, + idx: Def<u32>, + get_list: impl FnOnce(&'me ModuleState) -> &'me [T], + ) -> Option<&'me T> { + let mut cur = self; + for _ in 0..(self.depth - idx.depth) { + cur = cur.parent.as_ref().unwrap(); + } + get_list(cur).get(idx.item as usize) + } + + fn get_type<'me>(&'me self, mut idx: Def<u32>) -> Option<Def<&'me TypeDef>> { + loop { + let def = self.get(idx, |v| &v.types)?; + match def { + ValidatedType::Def(item) => break Some(idx.with(item)), + ValidatedType::Alias(other) => idx = *other, + } + } + } + + fn get_table<'me>(&'me self, idx: Def<u32>) -> Option<&'me Def<TableType>> { + self.get(idx, |v| &v.tables) + } + + fn get_memory<'me>(&'me self, idx: Def<u32>) -> Option<&'me MemoryType> { + self.get(idx, |v| &v.memories) + } + + fn get_global<'me>(&'me self, idx: Def<u32>) -> Option<&'me Def<GlobalType>> { + self.get(idx, |v| &v.globals) + } + + fn get_func_type_index<'me>(&'me self, idx: Def<u32>) -> Option<Def<u32>> { + Some(*self.get(idx, |v| &v.func_type_indices)?) + } + + fn get_module_type_index<'me>(&'me self, idx: Def<u32>) -> Option<Def<u32>> { + Some(*self.get(idx, |v| &v.module_type_indices)?) + } + + fn get_instance_def<'me>(&'me self, idx: Def<u32>) -> Option<&'me Def<InstanceDef>> { + self.get(idx, |v| &v.instance_type_indices) + } +} + +#[derive(Copy, Clone)] +struct Def<T> { + depth: usize, + item: T, +} + +impl<T> Def<T> { + fn map<U>(self, map: impl FnOnce(T) -> U) -> Def<U> { + Def { + depth: self.depth, + item: map(self.item), + } + } + + fn with<U>(&self, item: U) -> Def<U> { + Def { + depth: self.depth, + item: item, + } + } +} + +/// The implementation of [`WasmModuleResources`] used by [`Validator`]. +pub struct ValidatorResources(Arc<ModuleState>); + +impl WasmModuleResources for ValidatorResources { + type FuncType = crate::FuncType; + + fn table_at(&self, at: u32) -> Option<TableType> { + self.0.get_table(self.0.def(at)).map(|t| t.item) + } + + fn memory_at(&self, at: u32) -> Option<MemoryType> { + self.0.get_memory(self.0.def(at)).copied() + } + + fn global_at(&self, at: u32) -> Option<GlobalType> { + self.0.get_global(self.0.def(at)).map(|t| t.item) + } + + fn func_type_at(&self, at: u32) -> Option<&Self::FuncType> { + match self.0.get_type(self.0.def(at))?.item { + TypeDef::Func(f) => Some(f), + _ => None, + } + } + + fn type_of_function(&self, at: u32) -> Option<&Self::FuncType> { + let ty = self.0.get_func_type_index(self.0.def(at))?; + match self.0.get_type(ty)?.item { + TypeDef::Func(f) => Some(f), + _ => None, + } + } + + fn element_type_at(&self, at: u32) -> Option<Type> { + self.0.element_types.get(at as usize).cloned() + } + + fn element_count(&self) -> u32 { + self.0.element_types.len() as u32 + } + + fn data_count(&self) -> u32 { + self.0.data_count.unwrap_or(0) + } + + fn is_function_referenced(&self, idx: u32) -> bool { + self.0.function_references.contains(&idx) + } +} + +mod arc { + use std::ops::Deref; + use std::sync::Arc; + + pub struct MaybeOwned<T> { + owned: bool, + arc: Arc<T>, + } + + impl<T> MaybeOwned<T> { + pub fn as_mut(&mut self) -> Option<&mut T> { + if !self.owned { + return None; + } + debug_assert!(Arc::get_mut(&mut self.arc).is_some()); + Some(unsafe { &mut *(&*self.arc as *const T as *mut T) }) + } + + pub fn assert_mut(&mut self) -> &mut T { + self.as_mut().unwrap() + } + + pub fn arc(&mut self) -> &Arc<T> { + self.owned = false; + &self.arc + } + } + + impl<T: Default> Default for MaybeOwned<T> { + fn default() -> MaybeOwned<T> { + MaybeOwned { + owned: true, + arc: Arc::default(), + } + } + } + + impl<T> Deref for MaybeOwned<T> { + type Target = T; + + fn deref(&self) -> &T { + &self.arc + } + } +} diff --git a/third_party/rust/wasmparser/src/validator/func.rs b/third_party/rust/wasmparser/src/validator/func.rs new file mode 100644 index 0000000000..791827aff2 --- /dev/null +++ b/third_party/rust/wasmparser/src/validator/func.rs @@ -0,0 +1,199 @@ +use crate::operators_validator::OperatorValidator; +use crate::{BinaryReader, Result, Type}; +use crate::{FunctionBody, Operator, WasmFeatures, WasmModuleResources}; + +/// Validation context for a WebAssembly function. +/// +/// This structure is created by +/// [`Validator::code_section_entry`](crate::Validator::code_section_entry) +/// and is created per-function in a WebAssembly module. This structure is +/// suitable for sending to other threads while the original +/// [`Validator`](crate::Validator) continues processing other functions. +pub struct FuncValidator<T> { + validator: OperatorValidator, + resources: T, +} + +impl<T: WasmModuleResources> FuncValidator<T> { + /// Creates a new `FuncValidator`. + /// + /// The returned `FuncValidator` can be used to validate a function with + /// the type `ty` specified. The `resources` indicate what the containing + /// module has for the function to use, and the `features` configure what + /// WebAssembly proposals are enabled for this function. + /// + /// The returned validator can be used to then parse a [`FunctionBody`], for + /// example, to read locals and validate operators. + pub fn new( + ty: u32, + offset: usize, + resources: T, + features: &WasmFeatures, + ) -> Result<FuncValidator<T>> { + Ok(FuncValidator { + validator: OperatorValidator::new(ty, offset, features, &resources)?, + resources, + }) + } + + /// Get the current height of the operand stack. + /// + /// This returns the height of the whole operand stack for this function, + /// not just for the current control frame. + pub fn operand_stack_height(&self) -> u32 { + self.validator.operands.len() as u32 + } + + /// Convenience function to validate an entire function's body. + /// + /// You may not end up using this in final implementations because you'll + /// often want to interleave validation with parsing. + pub fn validate(&mut self, body: &FunctionBody<'_>) -> Result<()> { + let mut reader = body.get_binary_reader(); + self.read_locals(&mut reader)?; + while !reader.eof() { + let pos = reader.original_position(); + let op = reader.read_operator()?; + self.op(pos, &op)?; + } + self.finish(reader.original_position()) + } + + /// Reads the local defintions from the given `BinaryReader`, often sourced + /// from a `FunctionBody`. + /// + /// This function will automatically advance the `BinaryReader` forward, + /// leaving reading operators up to the caller afterwards. + pub fn read_locals(&mut self, reader: &mut BinaryReader<'_>) -> Result<()> { + for _ in 0..reader.read_var_u32()? { + let offset = reader.original_position(); + let cnt = reader.read_var_u32()?; + let ty = reader.read_type()?; + self.define_locals(offset, cnt, ty)?; + } + Ok(()) + } + + /// Defines locals into this validator. + /// + /// This should be used if the application is already reading local + /// definitions and there's no need to re-parse the function again. + pub fn define_locals(&mut self, offset: usize, count: u32, ty: Type) -> Result<()> { + self.validator.define_locals(offset, count, ty) + } + + /// Validates the next operator in a function. + /// + /// This functions is expected to be called once-per-operator in a + /// WebAssembly function. Each operator's offset in the original binary and + /// the operator itself are passed to this function to provide more useful + /// error messages. + pub fn op(&mut self, offset: usize, operator: &Operator<'_>) -> Result<()> { + self.validator + .process_operator(operator, &self.resources) + .map_err(|e| e.set_offset(offset))?; + Ok(()) + } + + /// Function that must be called after the last opcode has been processed. + /// + /// This will validate that the function was properly terminated with the + /// `end` opcode. If this function is not called then the function will not + /// be properly validated. + /// + /// The `offset` provided to this function will be used as a position for an + /// error if validation fails. + pub fn finish(&mut self, offset: usize) -> Result<()> { + self.validator.finish().map_err(|e| e.set_offset(offset))?; + Ok(()) + } + + /// Returns the underlying module resources that this validator is using. + pub fn resources(&self) -> &T { + &self.resources + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::WasmFuncType; + + struct EmptyResources; + + impl WasmModuleResources for EmptyResources { + type FuncType = EmptyFuncType; + + fn table_at(&self, _at: u32) -> Option<crate::TableType> { + todo!() + } + fn memory_at(&self, _at: u32) -> Option<crate::MemoryType> { + todo!() + } + fn global_at(&self, _at: u32) -> Option<crate::GlobalType> { + todo!() + } + fn func_type_at(&self, _type_idx: u32) -> Option<&Self::FuncType> { + Some(&EmptyFuncType) + } + fn type_of_function(&self, _func_idx: u32) -> Option<&Self::FuncType> { + todo!() + } + fn element_type_at(&self, _at: u32) -> Option<Type> { + todo!() + } + fn element_count(&self) -> u32 { + todo!() + } + fn data_count(&self) -> u32 { + todo!() + } + fn is_function_referenced(&self, _idx: u32) -> bool { + todo!() + } + } + + struct EmptyFuncType; + + impl WasmFuncType for EmptyFuncType { + fn len_inputs(&self) -> usize { + 0 + } + fn len_outputs(&self) -> usize { + 0 + } + fn input_at(&self, _at: u32) -> Option<Type> { + todo!() + } + fn output_at(&self, _at: u32) -> Option<Type> { + todo!() + } + } + + #[test] + fn operand_stack_height() { + let mut v = FuncValidator::new(0, 0, &EmptyResources, &Default::default()).unwrap(); + + // Initially zero values on the stack. + assert_eq!(v.operand_stack_height(), 0); + + // Pushing a constant value makes use have one value on the stack. + assert!(v.op(0, &Operator::I32Const { value: 0 }).is_ok()); + assert_eq!(v.operand_stack_height(), 1); + + // Entering a new control block does not affect the stack height. + assert!(v + .op( + 1, + &Operator::Block { + ty: crate::TypeOrFuncType::Type(crate::Type::EmptyBlockType) + } + ) + .is_ok()); + assert_eq!(v.operand_stack_height(), 1); + + // Pushing another constant value makes use have two values on the stack. + assert!(v.op(2, &Operator::I32Const { value: 99 }).is_ok()); + assert_eq!(v.operand_stack_height(), 2); + } +} |