summaryrefslogtreecommitdiffstats
path: root/third_party/rust/wasmparser
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
commit2aa4a82499d4becd2284cdb482213d541b8804dd (patch)
treeb80bf8bf13c3766139fbacc530efd0dd9d54394c /third_party/rust/wasmparser
parentInitial commit. (diff)
downloadfirefox-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')
-rw-r--r--third_party/rust/wasmparser/.cargo-checksum.json1
-rw-r--r--third_party/rust/wasmparser/Cargo.toml31
-rw-r--r--third_party/rust/wasmparser/README.md70
-rw-r--r--third_party/rust/wasmparser/benches/benchmark.rs236
-rwxr-xr-xthird_party/rust/wasmparser/compare-with-main.sh10
-rw-r--r--third_party/rust/wasmparser/examples/simple.rs37
-rw-r--r--third_party/rust/wasmparser/src/binary_reader.rs1879
-rw-r--r--third_party/rust/wasmparser/src/lib.rs42
-rw-r--r--third_party/rust/wasmparser/src/limits.rs37
-rw-r--r--third_party/rust/wasmparser/src/module_resources.rs275
-rw-r--r--third_party/rust/wasmparser/src/operators_validator.rs1857
-rw-r--r--third_party/rust/wasmparser/src/parser.rs1338
-rw-r--r--third_party/rust/wasmparser/src/primitives.rs809
-rw-r--r--third_party/rust/wasmparser/src/readers/alias_section.rs88
-rw-r--r--third_party/rust/wasmparser/src/readers/code_section.rs238
-rw-r--r--third_party/rust/wasmparser/src/readers/data_section.rs152
-rw-r--r--third_party/rust/wasmparser/src/readers/element_section.rs290
-rw-r--r--third_party/rust/wasmparser/src/readers/export_section.rs102
-rw-r--r--third_party/rust/wasmparser/src/readers/function_section.rs88
-rw-r--r--third_party/rust/wasmparser/src/readers/global_section.rs105
-rw-r--r--third_party/rust/wasmparser/src/readers/import_section.rs98
-rw-r--r--third_party/rust/wasmparser/src/readers/init_expr.rs42
-rw-r--r--third_party/rust/wasmparser/src/readers/instance_section.rs162
-rw-r--r--third_party/rust/wasmparser/src/readers/linking_section.rs78
-rw-r--r--third_party/rust/wasmparser/src/readers/memory_section.rs88
-rw-r--r--third_party/rust/wasmparser/src/readers/mod.rs63
-rw-r--r--third_party/rust/wasmparser/src/readers/module_code_section.rs96
-rw-r--r--third_party/rust/wasmparser/src/readers/module_section.rs61
-rw-r--r--third_party/rust/wasmparser/src/readers/name_section.rs253
-rw-r--r--third_party/rust/wasmparser/src/readers/operators.rs161
-rw-r--r--third_party/rust/wasmparser/src/readers/producers_section.rs197
-rw-r--r--third_party/rust/wasmparser/src/readers/reloc_section.rs118
-rw-r--r--third_party/rust/wasmparser/src/readers/section_reader.rs124
-rw-r--r--third_party/rust/wasmparser/src/readers/table_section.rs89
-rw-r--r--third_party/rust/wasmparser/src/readers/type_section.rs109
-rw-r--r--third_party/rust/wasmparser/src/validator.rs1845
-rw-r--r--third_party/rust/wasmparser/src/validator/func.rs199
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);
+ }
+}