summaryrefslogtreecommitdiff
path: root/crates/parser
diff options
context:
space:
mode:
Diffstat (limited to 'crates/parser')
-rw-r--r--crates/parser/src/conversion.rs336
-rw-r--r--crates/parser/src/lib.rs8
-rw-r--r--crates/parser/src/module.rs27
-rw-r--r--crates/parser/src/visit.rs487
4 files changed, 511 insertions, 347 deletions
diff --git a/crates/parser/src/conversion.rs b/crates/parser/src/conversion.rs
index f81b9e7..03b5f82 100644
--- a/crates/parser/src/conversion.rs
+++ b/crates/parser/src/conversion.rs
@@ -1,10 +1,9 @@
-use crate::log;
+use crate::visit::process_operators;
+use crate::Result;
use alloc::{boxed::Box, format, string::ToString, vec::Vec};
use tinywasm_types::*;
use wasmparser::{FuncValidator, OperatorsReader, ValidatorResources};
-use crate::{module::CodeSection, Result};
-
pub(crate) fn convert_module_elements<'a, T: IntoIterator<Item = wasmparser::Result<wasmparser::Element<'a>>>>(
elements: T,
) -> Result<Vec<tinywasm_types::Element>> {
@@ -160,12 +159,12 @@ pub(crate) fn convert_module_export(export: wasmparser::Export<'_>) -> Result<Ex
pub(crate) fn convert_module_code(
func: wasmparser::FunctionBody<'_>,
mut validator: FuncValidator<ValidatorResources>,
-) -> Result<CodeSection> {
+) -> Result<(Box<[Instruction]>, Box<[ValType]>)> {
let locals_reader = func.get_locals_reader()?;
let count = locals_reader.get_count();
let pos = locals_reader.original_position();
- let mut locals = Vec::with_capacity(count as usize);
+ let mut locals = Vec::with_capacity(count as usize);
for (i, local) in locals_reader.into_iter().enumerate() {
let local = local?;
validator.define_locals(pos + i, local.0, local.1)?;
@@ -174,10 +173,9 @@ pub(crate) fn convert_module_code(
}
}
- let body_reader = func.get_operators_reader()?;
- let body = process_operators(body_reader, validator)?;
-
- Ok(CodeSection { locals: locals.into_boxed_slice(), body })
+ let body = process_operators(&mut validator, &func)?;
+ let locals = locals.into_boxed_slice();
+ Ok((body, locals))
}
pub(crate) fn convert_module_type(ty: wasmparser::RecGroup) -> Result<FuncType> {
@@ -254,328 +252,10 @@ pub(crate) fn process_const_operator(op: wasmparser::Operator<'_>) -> Result<Con
}
}
-fn convert_heaptype(heap: wasmparser::HeapType) -> ValType {
+pub(crate) fn convert_heaptype(heap: wasmparser::HeapType) -> ValType {
match heap {
wasmparser::HeapType::Func => ValType::RefFunc,
wasmparser::HeapType::Extern => ValType::RefExtern,
_ => unimplemented!("Unsupported heap type: {:?}", heap),
}
}
-
-pub(crate) fn process_operators(
- ops: OperatorsReader<'_>,
- mut validator: FuncValidator<ValidatorResources>,
-) -> Result<Box<[Instruction]>> {
- let mut instructions = Vec::with_capacity(1024);
- let mut labels_ptrs = Vec::with_capacity(32);
- // indexes into the instructions array
- let mut offset = ops.original_position();
-
- for op in ops {
- let op = match op {
- Ok(op) => op,
- Err(e) => {
- cold();
- log::error!("Error while processing operators: {:?}", e);
- return Err(crate::ParseError::UnsupportedOperator("Error while processing operators".to_string()));
- }
- };
-
- match validator.op(offset, &op) {
- Ok(_) => (),
- Err(e) => {
- cold();
- log::error!("Error while processing operators: {:?}", e);
- return Err(crate::ParseError::UnsupportedOperator("Error while processing operators".to_string()));
- }
- }
- offset += 1;
-
- use wasmparser::Operator::*;
- let res = match op {
- BrTable { targets } => {
- let def = targets.default();
-
- let instrs = targets
- .targets()
- .map(|t| t.map(Instruction::BrLabel))
- .collect::<Result<Vec<Instruction>, wasmparser::BinaryReaderError>>()?;
-
- instructions.push(Instruction::BrTable(def, instrs.len()));
- instructions.extend(instrs);
- continue;
- }
- Unreachable => Instruction::Unreachable,
- Nop => Instruction::Nop,
- Block { blockty } => {
- labels_ptrs.push(instructions.len());
- Instruction::Block(convert_blocktype(blockty), 0)
- }
- Loop { blockty } => {
- labels_ptrs.push(instructions.len());
- Instruction::Loop(convert_blocktype(blockty), 0)
- }
- If { blockty } => {
- labels_ptrs.push(instructions.len());
- Instruction::If(convert_blocktype(blockty), None, 0)
- }
- Else => {
- labels_ptrs.push(instructions.len());
- Instruction::Else(0)
- }
- End => {
- if let Some(label_pointer) = labels_ptrs.pop() {
- let current_instr_ptr = instructions.len();
-
- // last_label_pointer is Some if we're ending a block
- match instructions[label_pointer] {
- Instruction::Else(ref mut else_instr_end_offset) => {
- *else_instr_end_offset = current_instr_ptr - label_pointer;
-
- // since we're ending an else block, we need to end the if block as well
- let if_label_pointer = labels_ptrs.pop().ok_or(crate::ParseError::UnsupportedOperator(
- "Expected to end an if block, but the last label was not an if".to_string(),
- ))?;
-
- let if_instruction = &mut instructions[if_label_pointer];
- let Instruction::If(_, ref mut else_offset, ref mut end_offset) = if_instruction else {
- return Err(crate::ParseError::UnsupportedOperator(
- "Expected to end an if block, but the last label was not an if".to_string(),
- ));
- };
-
- *else_offset = Some(label_pointer - if_label_pointer);
- *end_offset = current_instr_ptr - if_label_pointer;
- }
- Instruction::Block(_, ref mut end_offset)
- | Instruction::Loop(_, ref mut end_offset)
- | Instruction::If(_, _, ref mut end_offset) => {
- *end_offset = current_instr_ptr - label_pointer;
- }
- _ => {
- return Err(crate::ParseError::UnsupportedOperator(
- "Expected to end a block, but the last label was not a block".to_string(),
- ))
- }
- }
-
- Instruction::EndBlockFrame
- } else {
- // last_label_pointer is None if we're ending the function
- Instruction::EndFunc
- }
- }
-
- Br { relative_depth } => Instruction::Br(relative_depth),
- BrIf { relative_depth } => Instruction::BrIf(relative_depth),
- Return => Instruction::Return,
- Call { function_index } => Instruction::Call(function_index),
- CallIndirect { type_index, table_index, .. } => Instruction::CallIndirect(type_index, table_index),
- Drop => Instruction::Drop,
- Select => Instruction::Select(None),
- TypedSelect { ty } => Instruction::Select(Some(convert_valtype(&ty))),
- LocalGet { local_index } => Instruction::LocalGet(local_index),
- LocalSet { local_index } => Instruction::LocalSet(local_index),
- LocalTee { local_index } => Instruction::LocalTee(local_index),
- GlobalGet { global_index } => Instruction::GlobalGet(global_index),
- GlobalSet { global_index } => Instruction::GlobalSet(global_index),
- MemorySize { mem, mem_byte } => Instruction::MemorySize(mem, mem_byte),
- MemoryGrow { mem, mem_byte } => Instruction::MemoryGrow(mem, mem_byte),
-
- MemoryCopy { dst_mem, src_mem } => Instruction::MemoryCopy(src_mem, dst_mem),
- MemoryFill { mem } => Instruction::MemoryFill(mem),
- MemoryInit { data_index, mem } => Instruction::MemoryInit(data_index, mem),
- DataDrop { data_index } => Instruction::DataDrop(data_index),
-
- I32Const { value } => Instruction::I32Const(value),
- I64Const { value } => Instruction::I64Const(value),
- F32Const { value } => Instruction::F32Const(f32::from_bits(value.bits())),
- F64Const { value } => Instruction::F64Const(f64::from_bits(value.bits())),
- RefNull { hty } => Instruction::RefNull(convert_heaptype(hty)),
- RefIsNull => Instruction::RefIsNull,
- RefFunc { function_index } => Instruction::RefFunc(function_index),
- I32Load { memarg } => Instruction::I32Load(convert_memarg(memarg)),
- I64Load { memarg } => Instruction::I64Load(convert_memarg(memarg)),
- F32Load { memarg } => Instruction::F32Load(convert_memarg(memarg)),
- F64Load { memarg } => Instruction::F64Load(convert_memarg(memarg)),
- I32Load8S { memarg } => Instruction::I32Load8S(convert_memarg(memarg)),
- I32Load8U { memarg } => Instruction::I32Load8U(convert_memarg(memarg)),
- I32Load16S { memarg } => Instruction::I32Load16S(convert_memarg(memarg)),
- I32Load16U { memarg } => Instruction::I32Load16U(convert_memarg(memarg)),
- I64Load8S { memarg } => Instruction::I64Load8S(convert_memarg(memarg)),
- I64Load8U { memarg } => Instruction::I64Load8U(convert_memarg(memarg)),
- I64Load16S { memarg } => Instruction::I64Load16S(convert_memarg(memarg)),
- I64Load16U { memarg } => Instruction::I64Load16U(convert_memarg(memarg)),
- I64Load32S { memarg } => Instruction::I64Load32S(convert_memarg(memarg)),
- I64Load32U { memarg } => Instruction::I64Load32U(convert_memarg(memarg)),
- I32Store { memarg } => Instruction::I32Store(convert_memarg(memarg)),
- I64Store { memarg } => Instruction::I64Store(convert_memarg(memarg)),
- F32Store { memarg } => Instruction::F32Store(convert_memarg(memarg)),
- F64Store { memarg } => Instruction::F64Store(convert_memarg(memarg)),
- I32Store8 { memarg } => Instruction::I32Store8(convert_memarg(memarg)),
- I32Store16 { memarg } => Instruction::I32Store16(convert_memarg(memarg)),
- I64Store8 { memarg } => Instruction::I64Store8(convert_memarg(memarg)),
- I64Store16 { memarg } => Instruction::I64Store16(convert_memarg(memarg)),
- I64Store32 { memarg } => Instruction::I64Store32(convert_memarg(memarg)),
- I32Eqz => Instruction::I32Eqz,
- I32Eq => Instruction::I32Eq,
- I32Ne => Instruction::I32Ne,
- I32LtS => Instruction::I32LtS,
- I32LtU => Instruction::I32LtU,
- I32GtS => Instruction::I32GtS,
- I32GtU => Instruction::I32GtU,
- I32LeS => Instruction::I32LeS,
- I32LeU => Instruction::I32LeU,
- I32GeS => Instruction::I32GeS,
- I32GeU => Instruction::I32GeU,
- I64Eqz => Instruction::I64Eqz,
- I64Eq => Instruction::I64Eq,
- I64Ne => Instruction::I64Ne,
- I64LtS => Instruction::I64LtS,
- I64LtU => Instruction::I64LtU,
- I64GtS => Instruction::I64GtS,
- I64GtU => Instruction::I64GtU,
- I64LeS => Instruction::I64LeS,
- I64LeU => Instruction::I64LeU,
- I64GeS => Instruction::I64GeS,
- I64GeU => Instruction::I64GeU,
- F32Eq => Instruction::F32Eq,
- F32Ne => Instruction::F32Ne,
- F32Lt => Instruction::F32Lt,
- F32Gt => Instruction::F32Gt,
- F32Le => Instruction::F32Le,
- F32Ge => Instruction::F32Ge,
- F64Eq => Instruction::F64Eq,
- F64Ne => Instruction::F64Ne,
- F64Lt => Instruction::F64Lt,
- F64Gt => Instruction::F64Gt,
- F64Le => Instruction::F64Le,
- F64Ge => Instruction::F64Ge,
- I32Clz => Instruction::I32Clz,
- I32Ctz => Instruction::I32Ctz,
- I32Popcnt => Instruction::I32Popcnt,
- I32Add => Instruction::I32Add,
- I32Sub => Instruction::I32Sub,
- I32Mul => Instruction::I32Mul,
- I32DivS => Instruction::I32DivS,
- I32DivU => Instruction::I32DivU,
- I32RemS => Instruction::I32RemS,
- I32RemU => Instruction::I32RemU,
- I32And => Instruction::I32And,
- I32Or => Instruction::I32Or,
- I32Xor => Instruction::I32Xor,
- I32Shl => Instruction::I32Shl,
- I32ShrS => Instruction::I32ShrS,
- I32ShrU => Instruction::I32ShrU,
- I32Rotl => Instruction::I32Rotl,
- I32Rotr => Instruction::I32Rotr,
- I64Clz => Instruction::I64Clz,
- I64Ctz => Instruction::I64Ctz,
- I64Popcnt => Instruction::I64Popcnt,
- I64Add => Instruction::I64Add,
- I64Sub => Instruction::I64Sub,
- I64Mul => Instruction::I64Mul,
- I64DivS => Instruction::I64DivS,
- I64DivU => Instruction::I64DivU,
- I64RemS => Instruction::I64RemS,
- I64RemU => Instruction::I64RemU,
- I64And => Instruction::I64And,
- I64Or => Instruction::I64Or,
- I64Xor => Instruction::I64Xor,
- I64Shl => Instruction::I64Shl,
- I64ShrS => Instruction::I64ShrS,
- I64ShrU => Instruction::I64ShrU,
- I64Rotl => Instruction::I64Rotl,
- I64Rotr => Instruction::I64Rotr,
- F32Abs => Instruction::F32Abs,
- F32Neg => Instruction::F32Neg,
- F32Ceil => Instruction::F32Ceil,
- F32Floor => Instruction::F32Floor,
- F32Trunc => Instruction::F32Trunc,
- F32Nearest => Instruction::F32Nearest,
- F32Sqrt => Instruction::F32Sqrt,
- F32Add => Instruction::F32Add,
- F32Sub => Instruction::F32Sub,
- F32Mul => Instruction::F32Mul,
- F32Div => Instruction::F32Div,
- F32Min => Instruction::F32Min,
- F32Max => Instruction::F32Max,
- F32Copysign => Instruction::F32Copysign,
- F64Abs => Instruction::F64Abs,
- F64Neg => Instruction::F64Neg,
- F64Ceil => Instruction::F64Ceil,
- F64Floor => Instruction::F64Floor,
- F64Trunc => Instruction::F64Trunc,
- F64Nearest => Instruction::F64Nearest,
- F64Sqrt => Instruction::F64Sqrt,
- F64Add => Instruction::F64Add,
- F64Sub => Instruction::F64Sub,
- F64Mul => Instruction::F64Mul,
- F64Div => Instruction::F64Div,
- F64Min => Instruction::F64Min,
- F64Max => Instruction::F64Max,
- F64Copysign => Instruction::F64Copysign,
- I32WrapI64 => Instruction::I32WrapI64,
- I32TruncF32S => Instruction::I32TruncF32S,
- I32TruncF32U => Instruction::I32TruncF32U,
- I32TruncF64S => Instruction::I32TruncF64S,
- I32TruncF64U => Instruction::I32TruncF64U,
- I64Extend8S => Instruction::I64Extend8S,
- I64Extend16S => Instruction::I64Extend16S,
- I64Extend32S => Instruction::I64Extend32S,
- I64ExtendI32S => Instruction::I64ExtendI32S,
- I64ExtendI32U => Instruction::I64ExtendI32U,
- I32Extend8S => Instruction::I32Extend8S,
- I32Extend16S => Instruction::I32Extend16S,
- I64TruncF32S => Instruction::I64TruncF32S,
- I64TruncF32U => Instruction::I64TruncF32U,
- I64TruncF64S => Instruction::I64TruncF64S,
- I64TruncF64U => Instruction::I64TruncF64U,
- F32ConvertI32S => Instruction::F32ConvertI32S,
- F32ConvertI32U => Instruction::F32ConvertI32U,
- F32ConvertI64S => Instruction::F32ConvertI64S,
- F32ConvertI64U => Instruction::F32ConvertI64U,
- F32DemoteF64 => Instruction::F32DemoteF64,
- F64ConvertI32S => Instruction::F64ConvertI32S,
- F64ConvertI32U => Instruction::F64ConvertI32U,
- F64ConvertI64S => Instruction::F64ConvertI64S,
- F64ConvertI64U => Instruction::F64ConvertI64U,
- F64PromoteF32 => Instruction::F64PromoteF32,
- I32ReinterpretF32 => Instruction::I32ReinterpretF32,
- I64ReinterpretF64 => Instruction::I64ReinterpretF64,
- F32ReinterpretI32 => Instruction::F32ReinterpretI32,
- F64ReinterpretI64 => Instruction::F64ReinterpretI64,
- I32TruncSatF32S => Instruction::I32TruncSatF32S,
- I32TruncSatF32U => Instruction::I32TruncSatF32U,
- I32TruncSatF64S => Instruction::I32TruncSatF64S,
- I32TruncSatF64U => Instruction::I32TruncSatF64U,
- I64TruncSatF32S => Instruction::I64TruncSatF32S,
- I64TruncSatF32U => Instruction::I64TruncSatF32U,
- I64TruncSatF64S => Instruction::I64TruncSatF64S,
- I64TruncSatF64U => Instruction::I64TruncSatF64U,
- TableGet { table } => Instruction::TableGet(table),
- TableSet { table } => Instruction::TableSet(table),
- TableInit { table, elem_index } => Instruction::TableInit(table, elem_index),
- TableCopy { src_table, dst_table } => Instruction::TableCopy { from: src_table, to: dst_table },
- TableGrow { table } => Instruction::TableGrow(table),
- TableSize { table } => Instruction::TableSize(table),
- TableFill { table } => Instruction::TableFill(table),
- op => {
- cold();
- log::error!("Unsupported instruction: {:?}", op);
- return Err(crate::ParseError::UnsupportedOperator(format!("Unsupported instruction: {:?}", op)));
- }
- };
- instructions.push(res);
- }
-
- if !labels_ptrs.is_empty() {
- panic!("last_label_pointer should be None after processing all instructions: {:?}", labels_ptrs);
- }
-
- validator.finish(offset)?;
- Ok(instructions.into_boxed_slice())
-}
-
-#[cold]
-fn cold() {}
diff --git a/crates/parser/src/lib.rs b/crates/parser/src/lib.rs
index 72b3f81..dd4b931 100644
--- a/crates/parser/src/lib.rs
+++ b/crates/parser/src/lib.rs
@@ -7,6 +7,7 @@
#![forbid(unsafe_code)]
#![cfg_attr(not(feature = "std"), feature(error_in_core))]
//! See [`tinywasm`](https://docs.rs/tinywasm) for documentation.
+#![recursion_limit = "1028"]
mod std;
extern crate alloc;
@@ -28,6 +29,7 @@ mod log {
mod conversion;
mod error;
mod module;
+mod visit;
use alloc::{string::ToString, vec::Vec};
pub use error::*;
use module::ModuleReader;
@@ -155,11 +157,11 @@ impl TryFrom<ModuleReader> for TinyWasmModule {
.code
.into_iter()
.zip(code_type_addrs)
- .map(|(f, ty_idx)| TypedWasmFunction {
+ .map(|((instructions, locals), ty_idx)| TypedWasmFunction {
type_addr: ty_idx,
wasm_function: WasmFunction {
- instructions: f.body,
- locals: f.locals,
+ instructions,
+ locals,
ty: reader.func_types.get(ty_idx as usize).expect("No func type for func, this is a bug").clone(),
},
})
diff --git a/crates/parser/src/module.rs b/crates/parser/src/module.rs
index a18d343..813a65d 100644
--- a/crates/parser/src/module.rs
+++ b/crates/parser/src/module.rs
@@ -1,16 +1,9 @@
use crate::log::debug;
use crate::{conversion, ParseError, Result};
use alloc::{boxed::Box, format, vec::Vec};
-use core::fmt::Debug;
use tinywasm_types::{Data, Element, Export, FuncType, Global, Import, Instruction, MemoryType, TableType, ValType};
use wasmparser::{Payload, Validator};
-#[derive(Debug, Clone)]
-pub(crate) struct CodeSection {
- pub(crate) locals: Box<[ValType]>,
- pub(crate) body: Box<[Instruction]>,
-}
-
#[derive(Default)]
pub(crate) struct ModuleReader {
pub(crate) version: Option<u16>,
@@ -18,7 +11,7 @@ pub(crate) struct ModuleReader {
pub(crate) func_types: Vec<FuncType>,
pub(crate) code_type_addrs: Vec<u32>,
pub(crate) exports: Vec<Export>,
- pub(crate) code: Vec<CodeSection>,
+ pub(crate) code: Vec<(Box<[Instruction]>, Box<[ValType]>)>,
pub(crate) globals: Vec<Global>,
pub(crate) table_types: Vec<TableType>,
pub(crate) memory_types: Vec<MemoryType>,
@@ -66,15 +59,7 @@ impl ModuleReader {
.map(|t| conversion::convert_module_type(t?))
.collect::<Result<Vec<FuncType>>>()?;
}
- FunctionSection(reader) => {
- if !self.code_type_addrs.is_empty() {
- return Err(ParseError::DuplicateSection("Function section".into()));
- }
- debug!("Found function section");
- validator.function_section(&reader)?;
- self.code_type_addrs = reader.into_iter().map(|f| Ok(f?)).collect::<Result<Vec<_>>>()?;
- }
GlobalSection(reader) => {
if !self.globals.is_empty() {
return Err(ParseError::DuplicateSection("Global section".into()));
@@ -122,11 +107,21 @@ impl ModuleReader {
}
validator.data_count_section(count, &range)?;
}
+ FunctionSection(reader) => {
+ if !self.code_type_addrs.is_empty() {
+ return Err(ParseError::DuplicateSection("Function section".into()));
+ }
+
+ debug!("Found function section");
+ validator.function_section(&reader)?;
+ self.code_type_addrs = reader.into_iter().map(|f| Ok(f?)).collect::<Result<Vec<_>>>()?;
+ }
CodeSectionStart { count, range, .. } => {
debug!("Found code section ({} functions)", count);
if !self.code.is_empty() {
return Err(ParseError::DuplicateSection("Code section".into()));
}
+ self.code.reserve(count as usize);
validator.code_section_start(count, &range)?;
}
CodeSectionEntry(function) => {
diff --git a/crates/parser/src/visit.rs b/crates/parser/src/visit.rs
new file mode 100644
index 0000000..b42e462
--- /dev/null
+++ b/crates/parser/src/visit.rs
@@ -0,0 +1,487 @@
+use crate::{conversion::convert_blocktype, Result};
+
+use crate::conversion::{convert_heaptype, convert_memarg, convert_valtype};
+use alloc::string::ToString;
+use alloc::{boxed::Box, format, vec::Vec};
+use tinywasm_types::Instruction;
+use wasmparser::{FuncValidator, FunctionBody, VisitOperator, WasmModuleResources};
+
+struct ValidateThenVisit<'a, T, U>(T, &'a mut U);
+macro_rules! validate_then_visit {
+ ($( @$proposal:ident $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident)*) => {
+ $(
+ fn $visit(&mut self $($(,$arg: $argty)*)?) -> Self::Output {
+ self.0.$visit($($($arg.clone()),*)?)?;
+ Ok(self.1.$visit($($($arg),*)?))
+ }
+ )*
+ };
+}
+
+impl<'a, T, U> VisitOperator<'a> for ValidateThenVisit<'_, T, U>
+where
+ T: VisitOperator<'a, Output = wasmparser::Result<()>>,
+ U: VisitOperator<'a>,
+{
+ type Output = Result<U::Output>;
+
+ wasmparser::for_each_operator!(validate_then_visit);
+}
+
+pub(crate) fn process_operators<R: WasmModuleResources>(
+ validator: &mut FuncValidator<R>,
+ body: &FunctionBody<'_>,
+) -> Result<Box<[Instruction]>> {
+ let mut reader = body.get_operators_reader()?;
+ let mut builder = FunctionBuilder::new(1024);
+
+ while !reader.eof() {
+ let validate = validator.visitor(reader.original_position());
+ reader.visit_operator(&mut ValidateThenVisit(validate, &mut builder))???;
+ }
+
+ validator.finish(reader.original_position())?;
+ Ok(builder.instructions.into_boxed_slice())
+}
+
+macro_rules! define_operands {
+ ($($name:ident, $instr:expr),*) => {
+ $(
+ fn $name(&mut self) -> Self::Output {
+ self.instructions.push($instr);
+ Ok(())
+ }
+ )*
+ };
+}
+
+macro_rules! define_primitive_operands {
+ ($($name:ident, $instr:expr, $ty:ty),*) => {
+ $(
+ fn $name(&mut self, arg: $ty) -> Self::Output {
+ self.instructions.push($instr(arg));
+ Ok(())
+ }
+ )*
+ };
+}
+
+macro_rules! define_mem_operands {
+ ($($name:ident, $instr:expr),*) => {
+ $(
+ fn $name(&mut self, mem_arg: wasmparser::MemArg) -> Self::Output {
+ self.instructions.push($instr(
+ convert_memarg(mem_arg)
+ ));
+ Ok(())
+ }
+ )*
+ };
+}
+
+macro_rules! impl_visit_operator {
+ ( @mvp $($rest:tt)* ) => {
+ impl_visit_operator!(@@skipped $($rest)*);
+ };
+ ( @sign_extension $($rest:tt)* ) => {
+ impl_visit_operator!(@@skipped $($rest)*);
+ };
+ ( @saturating_float_to_int $($rest:tt)* ) => {
+ impl_visit_operator!(@@skipped $($rest)*);
+ };
+ ( @bulk_memory $($rest:tt)* ) => {
+ impl_visit_operator!(@@skipped $($rest)*);
+ };
+ ( @reference_types $($rest:tt)* ) => {
+ impl_visit_operator!(@@skipped $($rest)*);
+ };
+ ( @@skipped $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident $($rest:tt)* ) => {
+ impl_visit_operator!($($rest)*);
+ };
+ ( @$proposal:ident $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident $($rest:tt)* ) => {
+ fn $visit(&mut self $($(, $arg: $argty)*)?) -> Self::Output {
+ self.unsupported(stringify!($op))
+ }
+ impl_visit_operator!($($rest)*);
+ };
+ () => {};
+}
+
+pub(crate) struct FunctionBuilder {
+ instructions: Vec<Instruction>,
+ label_ptrs: Vec<usize>,
+}
+
+impl FunctionBuilder {
+ pub(crate) fn new(instr_capacity: usize) -> Self {
+ Self { instructions: Vec::with_capacity(instr_capacity), label_ptrs: Vec::with_capacity(64) }
+ }
+
+ #[cold]
+ fn unsupported(&self, name: &str) -> Result<()> {
+ Err(crate::ParseError::UnsupportedOperator(format!("Unsupported instruction: {:?}", name)))
+ }
+
+ #[inline]
+ fn visit(&mut self, op: Instruction) -> Result<()> {
+ Ok(self.instructions.push(op))
+ }
+}
+
+impl<'a> wasmparser::VisitOperator<'a> for FunctionBuilder {
+ type Output = Result<()>;
+
+ define_primitive_operands! {
+ visit_br, Instruction::Br, u32,
+ visit_br_if, Instruction::BrIf, u32,
+ visit_local_get, Instruction::LocalGet, u32,
+ visit_local_set, Instruction::LocalSet, u32,
+ visit_local_tee, Instruction::LocalTee, u32,
+ visit_global_get, Instruction::GlobalGet, u32,
+ visit_global_set, Instruction::GlobalSet, u32,
+ visit_i32_const, Instruction::I32Const, i32,
+ visit_i64_const, Instruction::I64Const, i64
+ }
+
+ define_mem_operands! {
+ visit_i32_load, Instruction::I32Load,
+ visit_i64_load, Instruction::I64Load,
+ visit_f32_load, Instruction::F32Load,
+ visit_f64_load, Instruction::F64Load,
+ visit_i32_load8_s, Instruction::I32Load8S,
+ visit_i32_load8_u, Instruction::I32Load8U,
+ visit_i32_load16_s, Instruction::I32Load16S,
+ visit_i32_load16_u, Instruction::I32Load16U,
+ visit_i64_load8_s, Instruction::I64Load8S,
+ visit_i64_load8_u, Instruction::I64Load8U,
+ visit_i64_load16_s, Instruction::I64Load16S,
+ visit_i64_load16_u, Instruction::I64Load16U,
+ visit_i64_load32_s, Instruction::I64Load32S,
+ visit_i64_load32_u, Instruction::I64Load32U,
+ visit_i32_store, Instruction::I32Store,
+ visit_i64_store, Instruction::I64Store,
+ visit_f32_store, Instruction::F32Store,
+ visit_f64_store, Instruction::F64Store,
+ visit_i32_store8, Instruction::I32Store8,
+ visit_i32_store16, Instruction::I32Store16,
+ visit_i64_store8, Instruction::I64Store8,
+ visit_i64_store16, Instruction::I64Store16,
+ visit_i64_store32, Instruction::I64Store32
+ }
+
+ define_operands! {
+ visit_unreachable, Instruction::Unreachable,
+ visit_nop, Instruction::Nop,
+ visit_return, Instruction::Return,
+ visit_drop, Instruction::Drop,
+ visit_select, Instruction::Select(None),
+ visit_i32_eqz, Instruction::I32Eqz,
+ visit_i32_eq, Instruction::I32Eq,
+ visit_i32_ne, Instruction::I32Ne,
+ visit_i32_lt_s, Instruction::I32LtS,
+ visit_i32_lt_u, Instruction::I32LtU,
+ visit_i32_gt_s, Instruction::I32GtS,
+ visit_i32_gt_u, Instruction::I32GtU,
+ visit_i32_le_s, Instruction::I32LeS,
+ visit_i32_le_u, Instruction::I32LeU,
+ visit_i32_ge_s, Instruction::I32GeS,
+ visit_i32_ge_u, Instruction::I32GeU,
+ visit_i64_eqz, Instruction::I64Eqz,
+ visit_i64_eq, Instruction::I64Eq,
+ visit_i64_ne, Instruction::I64Ne,
+ visit_i64_lt_s, Instruction::I64LtS,
+ visit_i64_lt_u, Instruction::I64LtU,
+ visit_i64_gt_s, Instruction::I64GtS,
+ visit_i64_gt_u, Instruction::I64GtU,
+ visit_i64_le_s, Instruction::I64LeS,
+ visit_i64_le_u, Instruction::I64LeU,
+ visit_i64_ge_s, Instruction::I64GeS,
+ visit_i64_ge_u, Instruction::I64GeU,
+ visit_f32_eq, Instruction::F32Eq,
+ visit_f32_ne, Instruction::F32Ne,
+ visit_f32_lt, Instruction::F32Lt,
+ visit_f32_gt, Instruction::F32Gt,
+ visit_f32_le, Instruction::F32Le,
+ visit_f32_ge, Instruction::F32Ge,
+ visit_f64_eq, Instruction::F64Eq,
+ visit_f64_ne, Instruction::F64Ne,
+ visit_f64_lt, Instruction::F64Lt,
+ visit_f64_gt, Instruction::F64Gt,
+ visit_f64_le, Instruction::F64Le,
+ visit_f64_ge, Instruction::F64Ge,
+ visit_i32_clz, Instruction::I32Clz,
+ visit_i32_ctz, Instruction::I32Ctz,
+ visit_i32_popcnt, Instruction::I32Popcnt,
+ visit_i32_add, Instruction::I32Add,
+ visit_i32_sub, Instruction::I32Sub,
+ visit_i32_mul, Instruction::I32Mul,
+ visit_i32_div_s, Instruction::I32DivS,
+ visit_i32_div_u, Instruction::I32DivU,
+ visit_i32_rem_s, Instruction::I32RemS,
+ visit_i32_rem_u, Instruction::I32RemU,
+ visit_i32_and, Instruction::I32And,
+ visit_i32_or, Instruction::I32Or,
+ visit_i32_xor, Instruction::I32Xor,
+ visit_i32_shl, Instruction::I32Shl,
+ visit_i32_shr_s, Instruction::I32ShrS,
+ visit_i32_shr_u, Instruction::I32ShrU,
+ visit_i32_rotl, Instruction::I32Rotl,
+ visit_i32_rotr, Instruction::I32Rotr,
+ visit_i64_clz, Instruction::I64Clz,
+ visit_i64_ctz, Instruction::I64Ctz,
+ visit_i64_popcnt, Instruction::I64Popcnt,
+ visit_i64_add, Instruction::I64Add,
+ visit_i64_sub, Instruction::I64Sub,
+ visit_i64_mul, Instruction::I64Mul,
+ visit_i64_div_s, Instruction::I64DivS,
+ visit_i64_div_u, Instruction::I64DivU,
+ visit_i64_rem_s, Instruction::I64RemS,
+ visit_i64_rem_u, Instruction::I64RemU,
+ visit_i64_and, Instruction::I64And,
+ visit_i64_or, Instruction::I64Or,
+ visit_i64_xor, Instruction::I64Xor,
+ visit_i64_shl, Instruction::I64Shl,
+ visit_i64_shr_s, Instruction::I64ShrS,
+ visit_i64_shr_u, Instruction::I64ShrU,
+ visit_i64_rotl, Instruction::I64Rotl,
+ visit_i64_rotr, Instruction::I64Rotr,
+ visit_f32_abs, Instruction::F32Abs,
+ visit_f32_neg, Instruction::F32Neg,
+ visit_f32_ceil, Instruction::F32Ceil,
+ visit_f32_floor, Instruction::F32Floor,
+ visit_f32_trunc, Instruction::F32Trunc,
+ visit_f32_nearest, Instruction::F32Nearest,
+ visit_f32_sqrt, Instruction::F32Sqrt,
+ visit_f32_add, Instruction::F32Add,
+ visit_f32_sub, Instruction::F32Sub,
+ visit_f32_mul, Instruction::F32Mul,
+ visit_f32_div, Instruction::F32Div,
+ visit_f32_min, Instruction::F32Min,
+ visit_f32_max, Instruction::F32Max,
+ visit_f32_copysign, Instruction::F32Copysign,
+ visit_f64_abs, Instruction::F64Abs,
+ visit_f64_neg, Instruction::F64Neg,
+ visit_f64_ceil, Instruction::F64Ceil,
+ visit_f64_floor, Instruction::F64Floor,
+ visit_f64_trunc, Instruction::F64Trunc,
+ visit_f64_nearest, Instruction::F64Nearest,
+ visit_f64_sqrt, Instruction::F64Sqrt,
+ visit_f64_add, Instruction::F64Add,
+ visit_f64_sub, Instruction::F64Sub,
+ visit_f64_mul, Instruction::F64Mul,
+ visit_f64_div, Instruction::F64Div,
+ visit_f64_min, Instruction::F64Min,
+ visit_f64_max, Instruction::F64Max,
+ visit_f64_copysign, Instruction::F64Copysign,
+ visit_i32_wrap_i64, Instruction::I32WrapI64,
+ visit_i32_trunc_f32_s, Instruction::I32TruncF32S,
+ visit_i32_trunc_f32_u, Instruction::I32TruncF32U,
+ visit_i32_trunc_f64_s, Instruction::I32TruncF64S,
+ visit_i32_trunc_f64_u, Instruction::I32TruncF64U,
+ visit_i64_extend_i32_s, Instruction::I64ExtendI32S,
+ visit_i64_extend_i32_u, Instruction::I64ExtendI32U,
+ visit_i64_trunc_f32_s, Instruction::I64TruncF32S,
+ visit_i64_trunc_f32_u, Instruction::I64TruncF32U,
+ visit_i64_trunc_f64_s, Instruction::I64TruncF64S,
+ visit_i64_trunc_f64_u, Instruction::I64TruncF64U,
+ visit_f32_convert_i32_s, Instruction::F32ConvertI32S,
+ visit_f32_convert_i32_u, Instruction::F32ConvertI32U,
+ visit_f32_convert_i64_s, Instruction::F32ConvertI64S,
+ visit_f32_convert_i64_u, Instruction::F32ConvertI64U,
+ visit_f32_demote_f64, Instruction::F32DemoteF64,
+ visit_f64_convert_i32_s, Instruction::F64ConvertI32S,
+ visit_f64_convert_i32_u, Instruction::F64ConvertI32U,
+ visit_f64_convert_i64_s, Instruction::F64ConvertI64S,
+ visit_f64_convert_i64_u, Instruction::F64ConvertI64U,
+ visit_f64_promote_f32, Instruction::F64PromoteF32,
+ visit_i32_reinterpret_f32, Instruction::I32ReinterpretF32,
+ visit_i64_reinterpret_f64, Instruction::I64ReinterpretF64,
+ visit_f32_reinterpret_i32, Instruction::F32ReinterpretI32,
+ visit_f64_reinterpret_i64, Instruction::F64ReinterpretI64,
+
+ // sign_extension
+ visit_i32_extend8_s, Instruction::I32Extend8S,
+ visit_i32_extend16_s, Instruction::I32Extend16S,
+ visit_i64_extend8_s, Instruction::I64Extend8S,
+ visit_i64_extend16_s, Instruction::I64Extend16S,
+ visit_i64_extend32_s, Instruction::I64Extend32S,
+
+ // Non-trapping Float-to-int Conversions
+ visit_i32_trunc_sat_f32_s, Instruction::I32TruncSatF32S,
+ visit_i32_trunc_sat_f32_u, Instruction::I32TruncSatF32U,
+ visit_i32_trunc_sat_f64_s, Instruction::I32TruncSatF64S,
+ visit_i32_trunc_sat_f64_u, Instruction::I32TruncSatF64U,
+ visit_i64_trunc_sat_f32_s, Instruction::I64TruncSatF32S,
+ visit_i64_trunc_sat_f32_u, Instruction::I64TruncSatF32U,
+ visit_i64_trunc_sat_f64_s, Instruction::I64TruncSatF64S,
+ visit_i64_trunc_sat_f64_u, Instruction::I64TruncSatF64U
+ }
+
+ fn visit_block(&mut self, blockty: wasmparser::BlockType) -> Self::Output {
+ self.label_ptrs.push(self.instructions.len());
+ self.visit(Instruction::Block(convert_blocktype(blockty), 0))
+ }
+
+ fn visit_loop(&mut self, ty: wasmparser::BlockType) -> Self::Output {
+ self.label_ptrs.push(self.instructions.len());
+ self.visit(Instruction::Loop(convert_blocktype(ty), 0))
+ }
+
+ fn visit_if(&mut self, ty: wasmparser::BlockType) -> Self::Output {
+ self.label_ptrs.push(self.instructions.len());
+ self.visit(Instruction::If(convert_blocktype(ty), None, 0))
+ }
+
+ fn visit_else(&mut self) -> Self::Output {
+ self.label_ptrs.push(self.instructions.len());
+ self.visit(Instruction::Else(0))
+ }
+
+ fn visit_end(&mut self) -> Self::Output {
+ let Some(label_pointer) = self.label_ptrs.pop() else {
+ return self.visit(Instruction::EndFunc);
+ };
+
+ let current_instr_ptr = self.instructions.len();
+
+ match self.instructions[label_pointer] {
+ Instruction::Else(ref mut else_instr_end_offset) => {
+ *else_instr_end_offset = current_instr_ptr - label_pointer;
+
+ // since we're ending an else block, we need to end the if block as well
+ let if_label_pointer = self.label_ptrs.pop().ok_or(crate::ParseError::UnsupportedOperator(
+ "Expected to end an if block, but the last label was not an if".to_string(),
+ ))?;
+
+ let if_instruction = &mut self.instructions[if_label_pointer];
+ let Instruction::If(_, ref mut else_offset, ref mut end_offset) = if_instruction else {
+ return Err(crate::ParseError::UnsupportedOperator(
+ "Expected to end an if block, but the last label was not an if".to_string(),
+ ));
+ };
+
+ *else_offset = Some(label_pointer - if_label_pointer);
+ *end_offset = current_instr_ptr - if_label_pointer;
+ }
+ Instruction::Block(_, ref mut end_offset)
+ | Instruction::Loop(_, ref mut end_offset)
+ | Instruction::If(_, _, ref mut end_offset) => {
+ *end_offset = current_instr_ptr - label_pointer;
+ }
+ _ => {
+ return Err(crate::ParseError::UnsupportedOperator(
+ "Expected to end a block, but the last label was not a block".to_string(),
+ ))
+ }
+ };
+
+ self.visit(Instruction::EndBlockFrame)
+ }
+
+ fn visit_br_table(&mut self, targets: wasmparser::BrTable<'_>) -> Self::Output {
+ let def = targets.default();
+ let instrs = targets
+ .targets()
+ .map(|t| t.map(Instruction::BrLabel))
+ .collect::<Result<Vec<Instruction>, wasmparser::BinaryReaderError>>()
+ .expect("BrTable targets are invalid, this should have been caught by the validator");
+
+ self.instructions
+ .extend(IntoIterator::into_iter([Instruction::BrTable(def, instrs.len())]).chain(instrs.into_iter()));
+ Ok(())
+ }
+
+ fn visit_call(&mut self, idx: u32) -> Self::Output {
+ self.visit(Instruction::Call(idx))
+ }
+
+ fn visit_call_indirect(&mut self, ty: u32, table: u32, _table_byte: u8) -> Self::Output {
+ self.visit(Instruction::CallIndirect(ty, table))
+ }
+
+ fn visit_memory_size(&mut self, mem: u32, mem_byte: u8) -> Self::Output {
+ self.visit(Instruction::MemorySize(mem, mem_byte))
+ }
+
+ fn visit_memory_grow(&mut self, mem: u32, mem_byte: u8) -> Self::Output {
+ self.visit(Instruction::MemoryGrow(mem, mem_byte))
+ }
+
+ fn visit_f32_const(&mut self, val: wasmparser::Ieee32) -> Self::Output {
+ self.visit(Instruction::F32Const(f32::from_bits(val.bits())))
+ }
+
+ fn visit_f64_const(&mut self, val: wasmparser::Ieee64) -> Self::Output {
+ self.visit(Instruction::F64Const(f64::from_bits(val.bits())))
+ }
+
+ // Bulk Memory Operations
+
+ fn visit_memory_init(&mut self, data_index: u32, mem: u32) -> Self::Output {
+ self.visit(Instruction::MemoryInit(data_index, mem))
+ }
+
+ fn visit_data_drop(&mut self, data_index: u32) -> Self::Output {
+ self.visit(Instruction::DataDrop(data_index))
+ }
+
+ fn visit_memory_copy(&mut self, dst_mem: u32, src_mem: u32) -> Self::Output {
+ self.visit(Instruction::MemoryCopy(dst_mem, src_mem))
+ }
+
+ fn visit_memory_fill(&mut self, mem: u32) -> Self::Output {
+ self.visit(Instruction::MemoryFill(mem))
+ }
+
+ fn visit_table_init(&mut self, elem_index: u32, table: u32) -> Self::Output {
+ self.visit(Instruction::TableInit(elem_index, table))
+ }
+
+ fn visit_elem_drop(&mut self, _elem_index: u32) -> Self::Output {
+ self.unsupported("elem_drop")
+ }
+
+ fn visit_table_copy(&mut self, dst_table: u32, src_table: u32) -> Self::Output {
+ self.visit(Instruction::TableCopy { from: src_table, to: dst_table })
+ }
+
+ // Reference Types
+
+ fn visit_ref_null(&mut self, ty: wasmparser::HeapType) -> Self::Output {
+ self.visit(Instruction::RefNull(convert_heaptype(ty)))
+ }
+
+ fn visit_ref_is_null(&mut self) -> Self::Output {
+ self.visit(Instruction::RefIsNull)
+ }
+
+ fn visit_ref_func(&mut self, idx: u32) -> Self::Output {
+ self.visit(Instruction::RefFunc(idx))
+ }
+
+ fn visit_typed_select(&mut self, ty: wasmparser::ValType) -> Self::Output {
+ self.visit(Instruction::Select(Some(convert_valtype(&ty))))
+ }
+
+ fn visit_table_fill(&mut self, table: u32) -> Self::Output {
+ self.visit(Instruction::TableFill(table))
+ }
+
+ fn visit_table_get(&mut self, table: u32) -> Self::Output {
+ self.visit(Instruction::TableGet(table))
+ }
+
+ fn visit_table_set(&mut self, table: u32) -> Self::Output {
+ self.visit(Instruction::TableSet(table))
+ }
+
+ fn visit_table_grow(&mut self, table: u32) -> Self::Output {
+ self.visit(Instruction::TableGrow(table))
+ }
+
+ fn visit_table_size(&mut self, table: u32) -> Self::Output {
+ self.visit(Instruction::TableSize(table))
+ }
+
+ wasmparser::for_each_operator!(impl_visit_operator);
+}