Allow wasm embedders to reject wasm modules with unsupported features. (#345)

Define `WasmError` (and an accompanying `WasmResult`) to represent
errors translating WebAssembly functions. Make `translate_call` and
related functions return `WasmResult`s so that embedders have the
flexibility to reject features they don't support.

Move `InvalidInput` out of `CtonError` and into `WasmError`, where it's
now named `InvalidWebAssembly`, as it's a WebAssembly-specific error
condition. Also extend it to preserve the original error message and
bytecode offset.
This commit is contained in:
Dan Gohman
2018-05-21 20:49:19 -07:00
committed by GitHub
parent 923ea8ada9
commit 89e7d56120
11 changed files with 185 additions and 177 deletions

View File

@@ -105,7 +105,9 @@ fn handle_module(
} }
let mut dummy_environ = DummyEnvironment::with_flags(fisa.flags.clone()); let mut dummy_environ = DummyEnvironment::with_flags(fisa.flags.clone());
translate_module(&data, &mut dummy_environ)?; translate_module(&data, &mut dummy_environ).map_err(
|e| e.to_string(),
)?;
terminal.fg(term::color::GREEN).unwrap(); terminal.fg(term::color::GREEN).unwrap();
vprintln!(flag_verbose, "ok"); vprintln!(flag_verbose, "ok");

View File

@@ -7,13 +7,6 @@ use verifier;
/// When Cretonne fails to compile a function, it will return one of these error codes. /// When Cretonne fails to compile a function, it will return one of these error codes.
#[derive(Fail, Debug, PartialEq, Eq)] #[derive(Fail, Debug, PartialEq, Eq)]
pub enum CtonError { pub enum CtonError {
/// The input is invalid.
///
/// This error code is used by a WebAssembly translator when it encounters invalid WebAssembly
/// code. This should never happen for validated WebAssembly code.
#[fail(display = "Invalid input code")]
InvalidInput,
/// An IR verifier error. /// An IR verifier error.
/// ///
/// This always represents a bug, either in the code that generated IR for Cretonne, or a bug /// This always represents a bug, either in the code that generated IR for Cretonne, or a bug

View File

@@ -13,6 +13,8 @@ wasmparser = { version = "0.16.1", default-features = false }
cretonne-codegen = { path = "../codegen", version = "0.8.0", default-features = false } cretonne-codegen = { path = "../codegen", version = "0.8.0", default-features = false }
cretonne-frontend = { path = "../frontend", version = "0.8.0", default-features = false } cretonne-frontend = { path = "../frontend", version = "0.8.0", default-features = false }
hashmap_core = { version = "0.1.4", optional = true } hashmap_core = { version = "0.1.4", optional = true }
failure = { version = "0.1.1", default-features = false, features = ["derive"] }
failure_derive = { version = "0.1.1", default-features = false }
[dev-dependencies] [dev-dependencies]
tempdir = "0.3.5" tempdir = "0.3.5"

View File

@@ -27,7 +27,7 @@ use cretonne_codegen::ir::types::*;
use cretonne_codegen::ir::{self, InstBuilder, JumpTableData, MemFlags}; use cretonne_codegen::ir::{self, InstBuilder, JumpTableData, MemFlags};
use cretonne_codegen::packed_option::ReservedValue; use cretonne_codegen::packed_option::ReservedValue;
use cretonne_frontend::{FunctionBuilder, Variable}; use cretonne_frontend::{FunctionBuilder, Variable};
use environ::{FuncEnvironment, GlobalValue}; use environ::{FuncEnvironment, GlobalValue, WasmResult};
use state::{ControlStackFrame, TranslationState}; use state::{ControlStackFrame, TranslationState};
use std::collections::{hash_map, HashMap}; use std::collections::{hash_map, HashMap};
use std::vec::Vec; use std::vec::Vec;
@@ -45,13 +45,13 @@ pub fn translate_operator<FE: FuncEnvironment + ?Sized>(
builder: &mut FunctionBuilder<Variable>, builder: &mut FunctionBuilder<Variable>,
state: &mut TranslationState, state: &mut TranslationState,
environ: &mut FE, environ: &mut FE,
) { ) -> WasmResult<()> {
if !state.reachable { if !state.reachable {
return translate_unreachable_operator(&op, builder, state); return Ok(translate_unreachable_operator(&op, builder, state));
} }
// This big match treats all Wasm code operators. // This big match treats all Wasm code operators.
match op { Ok(match op {
/********************************** Locals **************************************** /********************************** Locals ****************************************
* `get_local` and `set_local` are treated as non-SSA variables and will completely * `get_local` and `set_local` are treated as non-SSA variables and will completely
* disappear in the Cretonne Code * disappear in the Cretonne Code
@@ -362,7 +362,7 @@ pub fn translate_operator<FE: FuncEnvironment + ?Sized>(
function_index as FunctionIndex, function_index as FunctionIndex,
fref, fref,
state.peekn(num_args), state.peekn(num_args),
); )?;
state.popn(num_args); state.popn(num_args);
state.pushn(builder.inst_results(call)); state.pushn(builder.inst_results(call));
} }
@@ -378,7 +378,7 @@ pub fn translate_operator<FE: FuncEnvironment + ?Sized>(
sigref, sigref,
callee, callee,
state.peekn(num_args), state.peekn(num_args),
); )?;
state.popn(num_args); state.popn(num_args);
state.pushn(builder.inst_results(call)); state.pushn(builder.inst_results(call));
} }
@@ -397,7 +397,7 @@ pub fn translate_operator<FE: FuncEnvironment + ?Sized>(
heap_index, heap_index,
heap, heap,
val, val,
)) )?)
} }
Operator::CurrentMemory { reserved } => { Operator::CurrentMemory { reserved } => {
let heap_index = reserved as MemoryIndex; let heap_index = reserved as MemoryIndex;
@@ -406,7 +406,7 @@ pub fn translate_operator<FE: FuncEnvironment + ?Sized>(
builder.cursor(), builder.cursor(),
heap_index, heap_index,
heap, heap,
)); )?);
} }
/******************************* Load instructions *********************************** /******************************* Load instructions ***********************************
* Wasm specifies an integer alignment flag but we drop it in Cretonne. * Wasm specifies an integer alignment flag but we drop it in Cretonne.
@@ -857,7 +857,7 @@ pub fn translate_operator<FE: FuncEnvironment + ?Sized>(
Operator::I64AtomicRmw32UCmpxchg { .. } => { Operator::I64AtomicRmw32UCmpxchg { .. } => {
panic!("proposed thread operators not yet supported"); panic!("proposed thread operators not yet supported");
} }
} })
} }
// Clippy warns us of some fields we are deliberately ignoring // Clippy warns us of some fields we are deliberately ignoring

View File

@@ -4,7 +4,7 @@ use cretonne_codegen::cursor::FuncCursor;
use cretonne_codegen::ir::types::*; use cretonne_codegen::ir::types::*;
use cretonne_codegen::ir::{self, InstBuilder}; use cretonne_codegen::ir::{self, InstBuilder};
use cretonne_codegen::settings; use cretonne_codegen::settings;
use environ::{FuncEnvironment, GlobalValue, ModuleEnvironment}; use environ::{FuncEnvironment, GlobalValue, ModuleEnvironment, WasmResult};
use func_translator::FuncTranslator; use func_translator::FuncTranslator;
use std::string::String; use std::string::String;
use std::vec::Vec; use std::vec::Vec;
@@ -196,7 +196,7 @@ impl<'dummy_environment> FuncEnvironment for DummyFuncEnvironment<'dummy_environ
sig_ref: ir::SigRef, sig_ref: ir::SigRef,
callee: ir::Value, callee: ir::Value,
call_args: &[ir::Value], call_args: &[ir::Value],
) -> ir::Inst { ) -> WasmResult<ir::Inst> {
// Pass the current function's vmctx parameter on to the callee. // Pass the current function's vmctx parameter on to the callee.
let vmctx = pos.func let vmctx = pos.func
.special_param(ir::ArgumentPurpose::VMContext) .special_param(ir::ArgumentPurpose::VMContext)
@@ -224,9 +224,11 @@ impl<'dummy_environment> FuncEnvironment for DummyFuncEnvironment<'dummy_environ
args.extend(call_args.iter().cloned(), &mut pos.func.dfg.value_lists); args.extend(call_args.iter().cloned(), &mut pos.func.dfg.value_lists);
args.push(vmctx, &mut pos.func.dfg.value_lists); args.push(vmctx, &mut pos.func.dfg.value_lists);
pos.ins() Ok(
.CallIndirect(ir::Opcode::CallIndirect, VOID, sig_ref, args) pos.ins()
.0 .CallIndirect(ir::Opcode::CallIndirect, VOID, sig_ref, args)
.0,
)
} }
fn translate_call( fn translate_call(
@@ -235,7 +237,7 @@ impl<'dummy_environment> FuncEnvironment for DummyFuncEnvironment<'dummy_environ
_callee_index: FunctionIndex, _callee_index: FunctionIndex,
callee: ir::FuncRef, callee: ir::FuncRef,
call_args: &[ir::Value], call_args: &[ir::Value],
) -> ir::Inst { ) -> WasmResult<ir::Inst> {
// Pass the current function's vmctx parameter on to the callee. // Pass the current function's vmctx parameter on to the callee.
let vmctx = pos.func let vmctx = pos.func
.special_param(ir::ArgumentPurpose::VMContext) .special_param(ir::ArgumentPurpose::VMContext)
@@ -247,7 +249,7 @@ impl<'dummy_environment> FuncEnvironment for DummyFuncEnvironment<'dummy_environ
args.extend(call_args.iter().cloned(), &mut pos.func.dfg.value_lists); args.extend(call_args.iter().cloned(), &mut pos.func.dfg.value_lists);
args.push(vmctx, &mut pos.func.dfg.value_lists); args.push(vmctx, &mut pos.func.dfg.value_lists);
pos.ins().Call(ir::Opcode::Call, VOID, callee, args).0 Ok(pos.ins().Call(ir::Opcode::Call, VOID, callee, args).0)
} }
fn translate_grow_memory( fn translate_grow_memory(
@@ -256,8 +258,8 @@ impl<'dummy_environment> FuncEnvironment for DummyFuncEnvironment<'dummy_environ
_index: MemoryIndex, _index: MemoryIndex,
_heap: ir::Heap, _heap: ir::Heap,
_val: ir::Value, _val: ir::Value,
) -> ir::Value { ) -> WasmResult<ir::Value> {
pos.ins().iconst(I32, -1) Ok(pos.ins().iconst(I32, -1))
} }
fn translate_current_memory( fn translate_current_memory(
@@ -265,8 +267,8 @@ impl<'dummy_environment> FuncEnvironment for DummyFuncEnvironment<'dummy_environ
mut pos: FuncCursor, mut pos: FuncCursor,
_index: MemoryIndex, _index: MemoryIndex,
_heap: ir::Heap, _heap: ir::Heap,
) -> ir::Value { ) -> WasmResult<ir::Value> {
pos.ins().iconst(I32, -1) Ok(pos.ins().iconst(I32, -1))
} }
} }
@@ -385,7 +387,7 @@ impl<'data> ModuleEnvironment<'data> for DummyEnvironment {
self.info.start_func = Some(func_index); self.info.start_func = Some(func_index);
} }
fn define_function_body(&mut self, body_bytes: &'data [u8]) -> Result<(), String> { fn define_function_body(&mut self, body_bytes: &'data [u8]) -> WasmResult<()> {
let func = { let func = {
let mut func_environ = DummyFuncEnvironment::new(&self.info); let mut func_environ = DummyFuncEnvironment::new(&self.info);
let function_index = self.get_num_func_imports() + self.info.function_bodies.len(); let function_index = self.get_num_func_imports() + self.info.function_bodies.len();
@@ -393,9 +395,11 @@ impl<'data> ModuleEnvironment<'data> for DummyEnvironment {
let sig = func_environ.vmctx_sig(self.get_func_type(function_index)); let sig = func_environ.vmctx_sig(self.get_func_type(function_index));
let mut func = ir::Function::with_name_signature(name, sig); let mut func = ir::Function::with_name_signature(name, sig);
let reader = wasmparser::BinaryReader::new(body_bytes); let reader = wasmparser::BinaryReader::new(body_bytes);
self.trans self.trans.translate_from_reader(
.translate_from_reader(reader, &mut func, &mut func_environ) reader,
.map_err(|e| format!("{}", e))?; &mut func,
&mut func_environ,
)?;
func func
}; };
self.func_bytecode_sizes.push(body_bytes.len()); self.func_bytecode_sizes.push(body_bytes.len());

View File

@@ -4,4 +4,4 @@ mod dummy;
mod spec; mod spec;
pub use environ::dummy::DummyEnvironment; pub use environ::dummy::DummyEnvironment;
pub use environ::spec::{FuncEnvironment, GlobalValue, ModuleEnvironment}; pub use environ::spec::{FuncEnvironment, GlobalValue, ModuleEnvironment, WasmError, WasmResult};

View File

@@ -3,10 +3,10 @@
use cretonne_codegen::cursor::FuncCursor; use cretonne_codegen::cursor::FuncCursor;
use cretonne_codegen::ir::{self, InstBuilder}; use cretonne_codegen::ir::{self, InstBuilder};
use cretonne_codegen::settings::Flags; use cretonne_codegen::settings::Flags;
use std::string::String;
use std::vec::Vec; use std::vec::Vec;
use translation_utils::{FunctionIndex, Global, GlobalIndex, Memory, MemoryIndex, SignatureIndex, use translation_utils::{FunctionIndex, Global, GlobalIndex, Memory, MemoryIndex, SignatureIndex,
Table, TableIndex}; Table, TableIndex};
use wasmparser::BinaryReaderError;
/// The value of a WebAssembly global variable. /// The value of a WebAssembly global variable.
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
@@ -23,6 +23,49 @@ pub enum GlobalValue {
}, },
} }
/// A WebAssembly translation error.
///
/// When a WebAssembly function can't be translated, one of these error codes will be returned
/// to describe the failure.
#[derive(Fail, Debug, PartialEq, Eq)]
pub enum WasmError {
/// The input WebAssembly code is invalid.
///
/// This error code is used by a WebAssembly translator when it encounters invalid WebAssembly
/// code. This should never happen for validated WebAssembly code.
#[fail(display = "Invalid input WebAssembly code at offset {}: {}", _1, _0)]
InvalidWebAssembly {
message: &'static str,
offset: usize,
},
/// A feature used by the WebAssembly code is not supported by the embedding environment.
///
/// Embedding environments may have their own limitations and feature restrictions.
#[fail(display = "Unsupported feature: {}", _0)]
Unsupported(&'static str),
/// An implementation limit was exceeded.
///
/// Cretonne can compile very large and complicated functions, but the [implementation has
/// limits][limits] that cause compilation to fail when they are exceeded.
///
/// [limits]: https://cretonne.readthedocs.io/en/latest/langref.html#implementation-limits
#[fail(display = "Implementation limit exceeded")]
ImplLimitExceeded,
}
impl WasmError {
/// Convert from a `BinaryReaderError` to a `WasmError`.
pub fn from_binary_reader_error(e: BinaryReaderError) -> Self {
let BinaryReaderError { message, offset } = e;
WasmError::InvalidWebAssembly { message, offset }
}
}
/// A convenient alias for a `Result` that uses `WasmError` as the error type.
pub type WasmResult<T> = Result<T, WasmError>;
/// Environment affecting the translation of a single WebAssembly function. /// Environment affecting the translation of a single WebAssembly function.
/// ///
/// A `FuncEnvironment` trait object is required to translate a WebAssembly function to Cretonne /// A `FuncEnvironment` trait object is required to translate a WebAssembly function to Cretonne
@@ -99,7 +142,7 @@ pub trait FuncEnvironment {
sig_ref: ir::SigRef, sig_ref: ir::SigRef,
callee: ir::Value, callee: ir::Value,
call_args: &[ir::Value], call_args: &[ir::Value],
) -> ir::Inst; ) -> WasmResult<ir::Inst>;
/// Translate a `call` WebAssembly instruction at `pos`. /// Translate a `call` WebAssembly instruction at `pos`.
/// ///
@@ -114,8 +157,8 @@ pub trait FuncEnvironment {
_callee_index: FunctionIndex, _callee_index: FunctionIndex,
callee: ir::FuncRef, callee: ir::FuncRef,
call_args: &[ir::Value], call_args: &[ir::Value],
) -> ir::Inst { ) -> WasmResult<ir::Inst> {
pos.ins().call(callee, call_args) Ok(pos.ins().call(callee, call_args))
} }
/// Translate a `grow_memory` WebAssembly instruction. /// Translate a `grow_memory` WebAssembly instruction.
@@ -132,7 +175,7 @@ pub trait FuncEnvironment {
index: MemoryIndex, index: MemoryIndex,
heap: ir::Heap, heap: ir::Heap,
val: ir::Value, val: ir::Value,
) -> ir::Value; ) -> WasmResult<ir::Value>;
/// Translates a `current_memory` WebAssembly instruction. /// Translates a `current_memory` WebAssembly instruction.
/// ///
@@ -145,7 +188,7 @@ pub trait FuncEnvironment {
pos: FuncCursor, pos: FuncCursor,
index: MemoryIndex, index: MemoryIndex,
heap: ir::Heap, heap: ir::Heap,
) -> ir::Value; ) -> WasmResult<ir::Value>;
/// Emit code at the beginning of every wasm loop. /// Emit code at the beginning of every wasm loop.
/// ///
@@ -229,5 +272,5 @@ pub trait ModuleEnvironment<'data> {
fn declare_start_func(&mut self, index: FunctionIndex); fn declare_start_func(&mut self, index: FunctionIndex);
/// Provides the contents of a function body. /// Provides the contents of a function body.
fn define_function_body(&mut self, body_bytes: &'data [u8]) -> Result<(), String>; fn define_function_body(&mut self, body_bytes: &'data [u8]) -> WasmResult<()>;
} }

View File

@@ -7,10 +7,9 @@
use code_translator::translate_operator; use code_translator::translate_operator;
use cretonne_codegen::entity::EntityRef; use cretonne_codegen::entity::EntityRef;
use cretonne_codegen::ir::{self, Ebb, InstBuilder}; use cretonne_codegen::ir::{self, Ebb, InstBuilder};
use cretonne_codegen::result::{CtonError, CtonResult};
use cretonne_codegen::timing; use cretonne_codegen::timing;
use cretonne_frontend::{FunctionBuilder, FunctionBuilderContext, Variable}; use cretonne_frontend::{FunctionBuilder, FunctionBuilderContext, Variable};
use environ::FuncEnvironment; use environ::{FuncEnvironment, WasmError, WasmResult};
use state::TranslationState; use state::TranslationState;
use wasmparser::{self, BinaryReader}; use wasmparser::{self, BinaryReader};
@@ -56,7 +55,7 @@ impl FuncTranslator {
code: &[u8], code: &[u8],
func: &mut ir::Function, func: &mut ir::Function,
environ: &mut FE, environ: &mut FE,
) -> CtonResult { ) -> WasmResult<()> {
self.translate_from_reader(BinaryReader::new(code), func, environ) self.translate_from_reader(BinaryReader::new(code), func, environ)
} }
@@ -66,7 +65,7 @@ impl FuncTranslator {
mut reader: BinaryReader, mut reader: BinaryReader,
func: &mut ir::Function, func: &mut ir::Function,
environ: &mut FE, environ: &mut FE,
) -> CtonResult { ) -> WasmResult<()> {
let _tt = timing::wasm_translate_function(); let _tt = timing::wasm_translate_function();
dbg!( dbg!(
"translate({} bytes, {}{})", "translate({} bytes, {}{})",
@@ -134,17 +133,17 @@ fn parse_local_decls(
reader: &mut BinaryReader, reader: &mut BinaryReader,
builder: &mut FunctionBuilder<Variable>, builder: &mut FunctionBuilder<Variable>,
num_params: usize, num_params: usize,
) -> CtonResult { ) -> WasmResult<()> {
let mut next_local = num_params; let mut next_local = num_params;
let local_count = reader.read_local_count().map_err( let local_count = reader.read_local_count().map_err(|e| {
|_| CtonError::InvalidInput, WasmError::from_binary_reader_error(e)
)?; })?;
let mut locals_total = 0; let mut locals_total = 0;
for _ in 0..local_count { for _ in 0..local_count {
builder.set_srcloc(cur_srcloc(reader)); builder.set_srcloc(cur_srcloc(reader));
let (count, ty) = reader.read_local_decl(&mut locals_total).map_err(|_| { let (count, ty) = reader.read_local_decl(&mut locals_total).map_err(|e| {
CtonError::InvalidInput WasmError::from_binary_reader_error(e)
})?; })?;
declare_locals(builder, count, ty, &mut next_local); declare_locals(builder, count, ty, &mut next_local);
} }
@@ -189,15 +188,17 @@ fn parse_function_body<FE: FuncEnvironment + ?Sized>(
builder: &mut FunctionBuilder<Variable>, builder: &mut FunctionBuilder<Variable>,
state: &mut TranslationState, state: &mut TranslationState,
environ: &mut FE, environ: &mut FE,
) -> CtonResult { ) -> WasmResult<()> {
// The control stack is initialized with a single block representing the whole function. // The control stack is initialized with a single block representing the whole function.
debug_assert_eq!(state.control_stack.len(), 1, "State not initialized"); debug_assert_eq!(state.control_stack.len(), 1, "State not initialized");
// Keep going until the final `End` operator which pops the outermost block. // Keep going until the final `End` operator which pops the outermost block.
while !state.control_stack.is_empty() { while !state.control_stack.is_empty() {
builder.set_srcloc(cur_srcloc(&reader)); builder.set_srcloc(cur_srcloc(&reader));
let op = reader.read_operator().map_err(|_| CtonError::InvalidInput)?; let op = reader.read_operator().map_err(|e| {
translate_operator(op, builder, state, environ); WasmError::from_binary_reader_error(e)
})?;
translate_operator(op, builder, state, environ)?;
} }
// The final `End` operator left us in the exit block where we need to manually add a return // The final `End` operator left us in the exit block where we need to manually add a return

View File

@@ -42,6 +42,10 @@ extern crate cretonne_codegen;
extern crate cretonne_frontend; extern crate cretonne_frontend;
extern crate wasmparser; extern crate wasmparser;
extern crate failure;
#[macro_use]
extern crate failure_derive;
mod code_translator; mod code_translator;
mod environ; mod environ;
mod func_translator; mod func_translator;

View File

@@ -1,14 +1,12 @@
//! Translation skeleton that traverses the whole WebAssembly module and call helper functions //! Translation skeleton that traverses the whole WebAssembly module and call helper functions
//! to deal with each part of it. //! to deal with each part of it.
use cretonne_codegen::timing; use cretonne_codegen::timing;
use environ::ModuleEnvironment; use environ::{ModuleEnvironment, WasmError, WasmResult};
use sections_translator::{parse_data_section, parse_elements_section, parse_export_section, use sections_translator::{parse_data_section, parse_elements_section, parse_export_section,
parse_function_section, parse_function_signatures, parse_global_section, parse_function_section, parse_function_signatures, parse_global_section,
parse_import_section, parse_memory_section, parse_start_section, parse_import_section, parse_memory_section, parse_start_section,
parse_table_section, SectionParsingError}; parse_table_section};
use wasmparser::{BinaryReaderError, Parser, ParserInput, ParserState, SectionCode, WasmDecoder}; use wasmparser::{Parser, ParserInput, ParserState, SectionCode, WasmDecoder};
use std::string::String;
/// Translate a sequence of bytes forming a valid Wasm binary into a list of valid Cretonne IR /// Translate a sequence of bytes forming a valid Wasm binary into a list of valid Cretonne IR
/// [`Function`](../codegen/ir/function/struct.Function.html). /// [`Function`](../codegen/ir/function/struct.Function.html).
@@ -17,13 +15,13 @@ use std::string::String;
pub fn translate_module<'data>( pub fn translate_module<'data>(
data: &'data [u8], data: &'data [u8],
environ: &mut ModuleEnvironment<'data>, environ: &mut ModuleEnvironment<'data>,
) -> Result<(), String> { ) -> WasmResult<()> {
let _tt = timing::wasm_translate_module(); let _tt = timing::wasm_translate_module();
let mut parser = Parser::new(data); let mut parser = Parser::new(data);
match *parser.read() { match *parser.read() {
ParserState::BeginWasm { .. } => {} ParserState::BeginWasm { .. } => {}
ParserState::Error(BinaryReaderError { message, offset }) => { ParserState::Error(e) => {
return Err(format!("at offset {}: {}", offset, message)); return Err(WasmError::from_binary_reader_error(e));
} }
ref s => panic!("modules should begin properly: {:?}", s), ref s => panic!("modules should begin properly: {:?}", s),
} }
@@ -31,83 +29,38 @@ pub fn translate_module<'data>(
loop { loop {
match *parser.read_with_input(next_input) { match *parser.read_with_input(next_input) {
ParserState::BeginSection { code: SectionCode::Type, .. } => { ParserState::BeginSection { code: SectionCode::Type, .. } => {
match parse_function_signatures(&mut parser, environ) { parse_function_signatures(&mut parser, environ)?;
Ok(()) => (),
Err(SectionParsingError::WrongSectionContent(s)) => {
return Err(format!("wrong content in the type section: {}", s))
}
};
next_input = ParserInput::Default; next_input = ParserInput::Default;
} }
ParserState::BeginSection { code: SectionCode::Import, .. } => { ParserState::BeginSection { code: SectionCode::Import, .. } => {
match parse_import_section(&mut parser, environ) { parse_import_section(&mut parser, environ)?;
Ok(()) => {}
Err(SectionParsingError::WrongSectionContent(s)) => {
return Err(format!("wrong content in the import section: {}", s))
}
}
next_input = ParserInput::Default; next_input = ParserInput::Default;
} }
ParserState::BeginSection { code: SectionCode::Function, .. } => { ParserState::BeginSection { code: SectionCode::Function, .. } => {
match parse_function_section(&mut parser, environ) { parse_function_section(&mut parser, environ)?;
Ok(()) => {}
Err(SectionParsingError::WrongSectionContent(s)) => {
return Err(format!("wrong content in the function section: {}", s))
}
}
next_input = ParserInput::Default; next_input = ParserInput::Default;
} }
ParserState::BeginSection { code: SectionCode::Table, .. } => { ParserState::BeginSection { code: SectionCode::Table, .. } => {
match parse_table_section(&mut parser, environ) { parse_table_section(&mut parser, environ)?;
Ok(()) => (),
Err(SectionParsingError::WrongSectionContent(s)) => {
return Err(format!("wrong content in the table section: {}", s))
}
}
} }
ParserState::BeginSection { code: SectionCode::Memory, .. } => { ParserState::BeginSection { code: SectionCode::Memory, .. } => {
match parse_memory_section(&mut parser, environ) { parse_memory_section(&mut parser, environ)?;
Ok(()) => {}
Err(SectionParsingError::WrongSectionContent(s)) => {
return Err(format!("wrong content in the memory section: {}", s))
}
}
next_input = ParserInput::Default; next_input = ParserInput::Default;
} }
ParserState::BeginSection { code: SectionCode::Global, .. } => { ParserState::BeginSection { code: SectionCode::Global, .. } => {
match parse_global_section(&mut parser, environ) { parse_global_section(&mut parser, environ)?;
Ok(()) => {}
Err(SectionParsingError::WrongSectionContent(s)) => {
return Err(format!("wrong content in the global section: {}", s))
}
}
next_input = ParserInput::Default; next_input = ParserInput::Default;
} }
ParserState::BeginSection { code: SectionCode::Export, .. } => { ParserState::BeginSection { code: SectionCode::Export, .. } => {
match parse_export_section(&mut parser, environ) { parse_export_section(&mut parser, environ)?;
Ok(()) => {}
Err(SectionParsingError::WrongSectionContent(s)) => {
return Err(format!("wrong content in the export section: {}", s))
}
}
next_input = ParserInput::Default; next_input = ParserInput::Default;
} }
ParserState::BeginSection { code: SectionCode::Start, .. } => { ParserState::BeginSection { code: SectionCode::Start, .. } => {
match parse_start_section(&mut parser, environ) { parse_start_section(&mut parser, environ)?;
Ok(()) => (),
Err(SectionParsingError::WrongSectionContent(s)) => {
return Err(format!("wrong content in the start section: {}", s))
}
}
next_input = ParserInput::Default; next_input = ParserInput::Default;
} }
ParserState::BeginSection { code: SectionCode::Element, .. } => { ParserState::BeginSection { code: SectionCode::Element, .. } => {
match parse_elements_section(&mut parser, environ) { parse_elements_section(&mut parser, environ)?;
Ok(()) => (),
Err(SectionParsingError::WrongSectionContent(s)) => {
return Err(format!("wrong content in the element section: {}", s))
}
}
next_input = ParserInput::Default; next_input = ParserInput::Default;
} }
ParserState::BeginSection { code: SectionCode::Code, .. } => { ParserState::BeginSection { code: SectionCode::Code, .. } => {
@@ -119,18 +72,14 @@ pub fn translate_module<'data>(
} }
ParserState::EndWasm => return Ok(()), ParserState::EndWasm => return Ok(()),
ParserState::BeginSection { code: SectionCode::Data, .. } => { ParserState::BeginSection { code: SectionCode::Data, .. } => {
match parse_data_section(&mut parser, environ) { parse_data_section(&mut parser, environ)?;
Ok(()) => (),
Err(SectionParsingError::WrongSectionContent(s)) => {
return Err(format!("wrong content in the data section: {}", s))
}
}
} }
ParserState::BeginSection { code: SectionCode::Custom { .. }, .. } => { ParserState::BeginSection { code: SectionCode::Custom { .. }, .. } => {
// Ignore unknown custom sections. // Ignore unknown custom sections.
next_input = ParserInput::SkipSection; next_input = ParserInput::SkipSection;
} }
_ => return Err(String::from("wrong content in the preamble")), ParserState::Error(e) => return Err(WasmError::from_binary_reader_error(e)),
_ => panic!("wrong content in the preamble"),
}; };
} }
// At this point we've entered the code section // At this point we've entered the code section
@@ -138,25 +87,21 @@ pub fn translate_module<'data>(
match *parser.read() { match *parser.read() {
ParserState::BeginFunctionBody { .. } => {} ParserState::BeginFunctionBody { .. } => {}
ParserState::EndSection => break, ParserState::EndSection => break,
_ => return Err(String::from("wrong content in code section")), ParserState::Error(e) => return Err(WasmError::from_binary_reader_error(e)),
ref s => panic!("wrong content in code section: {:?}", s),
} }
let mut reader = parser.create_binary_reader(); let mut reader = parser.create_binary_reader();
let size = reader.bytes_remaining(); let size = reader.bytes_remaining();
environ.define_function_body( environ.define_function_body(
reader.read_bytes(size).map_err(|e| { reader.read_bytes(size).map_err(|e| {
format!("at offset {}: {}", e.offset, e.message) WasmError::from_binary_reader_error(e)
})?, })?,
)?; )?;
} }
loop { loop {
match *parser.read() { match *parser.read() {
ParserState::BeginSection { code: SectionCode::Data, .. } => { ParserState::BeginSection { code: SectionCode::Data, .. } => {
match parse_data_section(&mut parser, environ) { parse_data_section(&mut parser, environ)?;
Ok(()) => (),
Err(SectionParsingError::WrongSectionContent(s)) => {
return Err(format!("wrong content in the data section: {}", s))
}
}
} }
ParserState::EndWasm => break, ParserState::EndWasm => break,
_ => (), _ => (),

View File

@@ -8,9 +8,8 @@
//! is handled, according to the semantics of WebAssembly, to only specific expressions that are //! is handled, according to the semantics of WebAssembly, to only specific expressions that are
//! interpreted on the fly. //! interpreted on the fly.
use cretonne_codegen::ir::{self, AbiParam, Signature}; use cretonne_codegen::ir::{self, AbiParam, Signature};
use environ::ModuleEnvironment; use environ::{ModuleEnvironment, WasmError, WasmResult};
use std::str::from_utf8; use std::str::from_utf8;
use std::string::String;
use std::vec::Vec; use std::vec::Vec;
use translation_utils::{type_to_type, FunctionIndex, Global, GlobalIndex, GlobalInit, Memory, use translation_utils::{type_to_type, FunctionIndex, Global, GlobalIndex, GlobalInit, Memory,
MemoryIndex, SignatureIndex, Table, TableElementType, TableIndex}; MemoryIndex, SignatureIndex, Table, TableElementType, TableIndex};
@@ -18,15 +17,11 @@ use wasmparser;
use wasmparser::{ExternalKind, FuncType, ImportSectionEntryType, MemoryType, Operator, Parser, use wasmparser::{ExternalKind, FuncType, ImportSectionEntryType, MemoryType, Operator, Parser,
ParserState, WasmDecoder}; ParserState, WasmDecoder};
pub enum SectionParsingError {
WrongSectionContent(String),
}
/// Reads the Type Section of the wasm module and returns the corresponding function signatures. /// Reads the Type Section of the wasm module and returns the corresponding function signatures.
pub fn parse_function_signatures( pub fn parse_function_signatures(
parser: &mut Parser, parser: &mut Parser,
environ: &mut ModuleEnvironment, environ: &mut ModuleEnvironment,
) -> Result<(), SectionParsingError> { ) -> WasmResult<()> {
loop { loop {
match *parser.read() { match *parser.read() {
ParserState::EndSection => break, ParserState::EndSection => break,
@@ -50,7 +45,8 @@ pub fn parse_function_signatures(
})); }));
environ.declare_signature(&sig); environ.declare_signature(&sig);
} }
ref s => return Err(SectionParsingError::WrongSectionContent(format!("{:?}", s))), ParserState::Error(e) => return Err(WasmError::from_binary_reader_error(e)),
ref s => panic!("unexpected section content: {:?}", s),
} }
} }
Ok(()) Ok(())
@@ -60,7 +56,7 @@ pub fn parse_function_signatures(
pub fn parse_import_section<'data>( pub fn parse_import_section<'data>(
parser: &mut Parser<'data>, parser: &mut Parser<'data>,
environ: &mut ModuleEnvironment<'data>, environ: &mut ModuleEnvironment<'data>,
) -> Result<(), SectionParsingError> { ) -> WasmResult<()> {
loop { loop {
match *parser.read() { match *parser.read() {
ParserState::ImportSectionEntry { ParserState::ImportSectionEntry {
@@ -110,7 +106,8 @@ pub fn parse_import_section<'data>(
}) })
} }
ParserState::EndSection => break, ParserState::EndSection => break,
ref s => return Err(SectionParsingError::WrongSectionContent(format!("{:?}", s))), ParserState::Error(e) => return Err(WasmError::from_binary_reader_error(e)),
ref s => panic!("unexpected section content: {:?}", s),
}; };
} }
Ok(()) Ok(())
@@ -120,14 +117,15 @@ pub fn parse_import_section<'data>(
pub fn parse_function_section( pub fn parse_function_section(
parser: &mut Parser, parser: &mut Parser,
environ: &mut ModuleEnvironment, environ: &mut ModuleEnvironment,
) -> Result<(), SectionParsingError> { ) -> WasmResult<()> {
loop { loop {
match *parser.read() { match *parser.read() {
ParserState::FunctionSectionEntry(sigindex) => { ParserState::FunctionSectionEntry(sigindex) => {
environ.declare_func_type(sigindex as SignatureIndex); environ.declare_func_type(sigindex as SignatureIndex);
} }
ParserState::EndSection => break, ParserState::EndSection => break,
ref s => return Err(SectionParsingError::WrongSectionContent(format!("{:?}", s))), ParserState::Error(e) => return Err(WasmError::from_binary_reader_error(e)),
ref s => panic!("unexpected section content: {:?}", s),
}; };
} }
Ok(()) Ok(())
@@ -137,7 +135,7 @@ pub fn parse_function_section(
pub fn parse_export_section<'data>( pub fn parse_export_section<'data>(
parser: &mut Parser<'data>, parser: &mut Parser<'data>,
environ: &mut ModuleEnvironment<'data>, environ: &mut ModuleEnvironment<'data>,
) -> Result<(), SectionParsingError> { ) -> WasmResult<()> {
loop { loop {
match *parser.read() { match *parser.read() {
ParserState::ExportSectionEntry { ParserState::ExportSectionEntry {
@@ -158,24 +156,23 @@ pub fn parse_export_section<'data>(
} }
} }
ParserState::EndSection => break, ParserState::EndSection => break,
ref s => return Err(SectionParsingError::WrongSectionContent(format!("{:?}", s))), ParserState::Error(e) => return Err(WasmError::from_binary_reader_error(e)),
ref s => panic!("unexpected section content: {:?}", s),
}; };
} }
Ok(()) Ok(())
} }
/// Retrieves the start function index from the start section /// Retrieves the start function index from the start section
pub fn parse_start_section( pub fn parse_start_section(parser: &mut Parser, environ: &mut ModuleEnvironment) -> WasmResult<()> {
parser: &mut Parser,
environ: &mut ModuleEnvironment,
) -> Result<(), SectionParsingError> {
loop { loop {
match *parser.read() { match *parser.read() {
ParserState::StartSectionEntry(index) => { ParserState::StartSectionEntry(index) => {
environ.declare_start_func(index as FunctionIndex); environ.declare_start_func(index as FunctionIndex);
} }
ParserState::EndSection => break, ParserState::EndSection => break,
ref s => return Err(SectionParsingError::WrongSectionContent(format!("{:?}", s))), ParserState::Error(e) => return Err(WasmError::from_binary_reader_error(e)),
ref s => panic!("unexpected section content: {:?}", s),
}; };
} }
Ok(()) Ok(())
@@ -185,7 +182,7 @@ pub fn parse_start_section(
pub fn parse_memory_section( pub fn parse_memory_section(
parser: &mut Parser, parser: &mut Parser,
environ: &mut ModuleEnvironment, environ: &mut ModuleEnvironment,
) -> Result<(), SectionParsingError> { ) -> WasmResult<()> {
loop { loop {
match *parser.read() { match *parser.read() {
ParserState::MemorySectionEntry(ref ty) => { ParserState::MemorySectionEntry(ref ty) => {
@@ -196,7 +193,8 @@ pub fn parse_memory_section(
}); });
} }
ParserState::EndSection => break, ParserState::EndSection => break,
ref s => return Err(SectionParsingError::WrongSectionContent(format!("{:?}", s))), ParserState::Error(e) => return Err(WasmError::from_binary_reader_error(e)),
ref s => panic!("unexpected section content: {:?}", s),
}; };
} }
Ok(()) Ok(())
@@ -206,16 +204,18 @@ pub fn parse_memory_section(
pub fn parse_global_section( pub fn parse_global_section(
parser: &mut Parser, parser: &mut Parser,
environ: &mut ModuleEnvironment, environ: &mut ModuleEnvironment,
) -> Result<(), SectionParsingError> { ) -> WasmResult<()> {
loop { loop {
let (content_type, mutability) = match *parser.read() { let (content_type, mutability) = match *parser.read() {
ParserState::BeginGlobalSectionEntry(ref ty) => (ty.content_type, ty.mutable), ParserState::BeginGlobalSectionEntry(ref ty) => (ty.content_type, ty.mutable),
ParserState::EndSection => break, ParserState::EndSection => break,
ref s => return Err(SectionParsingError::WrongSectionContent(format!("{:?}", s))), ParserState::Error(e) => return Err(WasmError::from_binary_reader_error(e)),
ref s => panic!("unexpected section content: {:?}", s),
}; };
match *parser.read() { match *parser.read() {
ParserState::BeginInitExpressionBody => (), ParserState::BeginInitExpressionBody => (),
ref s => return Err(SectionParsingError::WrongSectionContent(format!("{:?}", s))), ParserState::Error(e) => return Err(WasmError::from_binary_reader_error(e)),
ref s => panic!("unexpected section content: {:?}", s),
} }
let initializer = match *parser.read() { let initializer = match *parser.read() {
ParserState::InitExpressionOperator(Operator::I32Const { value }) => { ParserState::InitExpressionOperator(Operator::I32Const { value }) => {
@@ -233,11 +233,13 @@ pub fn parse_global_section(
ParserState::InitExpressionOperator(Operator::GetGlobal { global_index }) => { ParserState::InitExpressionOperator(Operator::GetGlobal { global_index }) => {
GlobalInit::GlobalRef(global_index as GlobalIndex) GlobalInit::GlobalRef(global_index as GlobalIndex)
} }
ref s => return Err(SectionParsingError::WrongSectionContent(format!("{:?}", s))), ParserState::Error(e) => return Err(WasmError::from_binary_reader_error(e)),
ref s => panic!("unexpected section content: {:?}", s),
}; };
match *parser.read() { match *parser.read() {
ParserState::EndInitExpressionBody => (), ParserState::EndInitExpressionBody => (),
ref s => return Err(SectionParsingError::WrongSectionContent(format!("{:?}", s))), ParserState::Error(e) => return Err(WasmError::from_binary_reader_error(e)),
ref s => panic!("unexpected section content: {:?}", s),
} }
let global = Global { let global = Global {
ty: type_to_type(&content_type).unwrap(), ty: type_to_type(&content_type).unwrap(),
@@ -247,7 +249,8 @@ pub fn parse_global_section(
environ.declare_global(global); environ.declare_global(global);
match *parser.read() { match *parser.read() {
ParserState::EndGlobalSectionEntry => (), ParserState::EndGlobalSectionEntry => (),
ref s => return Err(SectionParsingError::WrongSectionContent(format!("{:?}", s))), ParserState::Error(e) => return Err(WasmError::from_binary_reader_error(e)),
ref s => panic!("unexpected section content: {:?}", s),
} }
} }
Ok(()) Ok(())
@@ -256,16 +259,18 @@ pub fn parse_global_section(
pub fn parse_data_section<'data>( pub fn parse_data_section<'data>(
parser: &mut Parser<'data>, parser: &mut Parser<'data>,
environ: &mut ModuleEnvironment<'data>, environ: &mut ModuleEnvironment<'data>,
) -> Result<(), SectionParsingError> { ) -> WasmResult<()> {
loop { loop {
let memory_index = match *parser.read() { let memory_index = match *parser.read() {
ParserState::BeginDataSectionEntry(memory_index) => memory_index, ParserState::BeginDataSectionEntry(memory_index) => memory_index,
ParserState::EndSection => break, ParserState::EndSection => break,
ref s => return Err(SectionParsingError::WrongSectionContent(format!("{:?}", s))), ParserState::Error(e) => return Err(WasmError::from_binary_reader_error(e)),
ref s => panic!("unexpected section content: {:?}", s),
}; };
match *parser.read() { match *parser.read() {
ParserState::BeginInitExpressionBody => (), ParserState::BeginInitExpressionBody => (),
ref s => return Err(SectionParsingError::WrongSectionContent(format!("{:?}", s))), ParserState::Error(e) => return Err(WasmError::from_binary_reader_error(e)),
ref s => panic!("unexpected section content: {:?}", s),
}; };
let (base, offset) = match *parser.read() { let (base, offset) = match *parser.read() {
ParserState::InitExpressionOperator(Operator::I32Const { value }) => { ParserState::InitExpressionOperator(Operator::I32Const { value }) => {
@@ -278,22 +283,26 @@ pub fn parse_data_section<'data>(
_ => panic!("should not happen"), _ => panic!("should not happen"),
} }
} }
ref s => return Err(SectionParsingError::WrongSectionContent(format!("{:?}", s))), ParserState::Error(e) => return Err(WasmError::from_binary_reader_error(e)),
ref s => panic!("unexpected section content: {:?}", s),
}; };
match *parser.read() { match *parser.read() {
ParserState::EndInitExpressionBody => (), ParserState::EndInitExpressionBody => (),
ref s => return Err(SectionParsingError::WrongSectionContent(format!("{:?}", s))), ParserState::Error(e) => return Err(WasmError::from_binary_reader_error(e)),
ref s => panic!("unexpected section content: {:?}", s),
}; };
match *parser.read() { match *parser.read() {
ParserState::BeginDataSectionEntryBody(_) => (), ParserState::BeginDataSectionEntryBody(_) => (),
ref s => return Err(SectionParsingError::WrongSectionContent(format!("{:?}", s))), ParserState::Error(e) => return Err(WasmError::from_binary_reader_error(e)),
ref s => panic!("unexpected section content: {:?}", s),
}; };
let mut running_offset = offset; let mut running_offset = offset;
loop { loop {
let data = match *parser.read() { let data = match *parser.read() {
ParserState::DataSectionEntryBodyChunk(data) => data, ParserState::DataSectionEntryBodyChunk(data) => data,
ParserState::EndDataSectionEntryBody => break, ParserState::EndDataSectionEntryBody => break,
ref s => return Err(SectionParsingError::WrongSectionContent(format!("{:?}", s))), ParserState::Error(e) => return Err(WasmError::from_binary_reader_error(e)),
ref s => panic!("unexpected section content: {:?}", s),
}; };
environ.declare_data_initialization( environ.declare_data_initialization(
memory_index as MemoryIndex, memory_index as MemoryIndex,
@@ -305,17 +314,15 @@ pub fn parse_data_section<'data>(
} }
match *parser.read() { match *parser.read() {
ParserState::EndDataSectionEntry => (), ParserState::EndDataSectionEntry => (),
ref s => return Err(SectionParsingError::WrongSectionContent(format!("{:?}", s))), ParserState::Error(e) => return Err(WasmError::from_binary_reader_error(e)),
ref s => panic!("unexpected section content: {:?}", s),
}; };
} }
Ok(()) Ok(())
} }
/// Retrieves the tables from the table section /// Retrieves the tables from the table section
pub fn parse_table_section( pub fn parse_table_section(parser: &mut Parser, environ: &mut ModuleEnvironment) -> WasmResult<()> {
parser: &mut Parser,
environ: &mut ModuleEnvironment,
) -> Result<(), SectionParsingError> {
loop { loop {
match *parser.read() { match *parser.read() {
ParserState::TableSectionEntry(ref table) => { ParserState::TableSectionEntry(ref table) => {
@@ -329,7 +336,8 @@ pub fn parse_table_section(
}) })
} }
ParserState::EndSection => break, ParserState::EndSection => break,
ref s => return Err(SectionParsingError::WrongSectionContent(format!("{:?}", s))), ParserState::Error(e) => return Err(WasmError::from_binary_reader_error(e)),
ref s => panic!("unexpected section content: {:?}", s),
}; };
} }
Ok(()) Ok(())
@@ -339,16 +347,18 @@ pub fn parse_table_section(
pub fn parse_elements_section( pub fn parse_elements_section(
parser: &mut Parser, parser: &mut Parser,
environ: &mut ModuleEnvironment, environ: &mut ModuleEnvironment,
) -> Result<(), SectionParsingError> { ) -> WasmResult<()> {
loop { loop {
let table_index = match *parser.read() { let table_index = match *parser.read() {
ParserState::BeginElementSectionEntry(table_index) => table_index as TableIndex, ParserState::BeginElementSectionEntry(table_index) => table_index as TableIndex,
ParserState::EndSection => break, ParserState::EndSection => break,
ref s => return Err(SectionParsingError::WrongSectionContent(format!("{:?}", s))), ParserState::Error(e) => return Err(WasmError::from_binary_reader_error(e)),
ref s => panic!("unexpected section content: {:?}", s),
}; };
match *parser.read() { match *parser.read() {
ParserState::BeginInitExpressionBody => (), ParserState::BeginInitExpressionBody => (),
ref s => return Err(SectionParsingError::WrongSectionContent(format!("{:?}", s))), ParserState::Error(e) => return Err(WasmError::from_binary_reader_error(e)),
ref s => panic!("unexpected section content: {:?}", s),
}; };
let (base, offset) = match *parser.read() { let (base, offset) = match *parser.read() {
ParserState::InitExpressionOperator(Operator::I32Const { value }) => { ParserState::InitExpressionOperator(Operator::I32Const { value }) => {
@@ -361,11 +371,13 @@ pub fn parse_elements_section(
_ => panic!("should not happen"), _ => panic!("should not happen"),
} }
} }
ref s => return Err(SectionParsingError::WrongSectionContent(format!("{:?}", s))), ParserState::Error(e) => return Err(WasmError::from_binary_reader_error(e)),
ref s => panic!("unexpected section content: {:?}", s),
}; };
match *parser.read() { match *parser.read() {
ParserState::EndInitExpressionBody => (), ParserState::EndInitExpressionBody => (),
ref s => return Err(SectionParsingError::WrongSectionContent(format!("{:?}", s))), ParserState::Error(e) => return Err(WasmError::from_binary_reader_error(e)),
ref s => panic!("unexpected section content: {:?}", s),
}; };
match *parser.read() { match *parser.read() {
ParserState::ElementSectionEntryBody(ref elements) => { ParserState::ElementSectionEntryBody(ref elements) => {
@@ -373,11 +385,13 @@ pub fn parse_elements_section(
elements.iter().map(|&x| x as FunctionIndex).collect(); elements.iter().map(|&x| x as FunctionIndex).collect();
environ.declare_table_elements(table_index, base, offset, elems) environ.declare_table_elements(table_index, base, offset, elems)
} }
ref s => return Err(SectionParsingError::WrongSectionContent(format!("{:?}", s))), ParserState::Error(e) => return Err(WasmError::from_binary_reader_error(e)),
ref s => panic!("unexpected section content: {:?}", s),
}; };
match *parser.read() { match *parser.read() {
ParserState::EndElementSectionEntry => (), ParserState::EndElementSectionEntry => (),
ref s => return Err(SectionParsingError::WrongSectionContent(format!("{:?}", s))), ParserState::Error(e) => return Err(WasmError::from_binary_reader_error(e)),
ref s => panic!("unexpected section content: {:?}", s),
}; };
} }
Ok(()) Ok(())