use crate::{
limits::MAX_WASM_FUNCTION_LOCALS, BinaryReaderError, BlockType, BrTable, HeapType, Ieee32,
Ieee64, MemArg, RefType, Result, ValType, VisitOperator, WasmFeatures, WasmFuncType,
WasmModuleResources, V128,
};
use std::ops::{Deref, DerefMut};
pub(crate) struct OperatorValidator {
pub(super) locals: Locals,
pub(super) local_inits: Vec<bool>,
pub(crate) features: WasmFeatures,
br_table_tmp: Vec<MaybeType>,
control: Vec<Frame>,
operands: Vec<MaybeType>,
inits: Vec<u32>,
end_which_emptied_control: Option<usize>,
}
const MAX_LOCALS_TO_TRACK: usize = 50;
pub(super) struct Locals {
num_locals: u32,
first: Vec<ValType>,
all: Vec<(u32, ValType)>,
}
#[derive(Debug, Copy, Clone)]
pub struct Frame {
pub kind: FrameKind,
pub block_type: BlockType,
pub height: usize,
pub unreachable: bool,
pub init_height: usize,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum FrameKind {
Block,
If,
Else,
Loop,
Try,
Catch,
CatchAll,
}
struct OperatorValidatorTemp<'validator, 'resources, T> {
offset: usize,
inner: &'validator mut OperatorValidator,
resources: &'resources T,
}
#[derive(Default)]
pub struct OperatorValidatorAllocations {
br_table_tmp: Vec<MaybeType>,
control: Vec<Frame>,
operands: Vec<MaybeType>,
local_inits: Vec<bool>,
inits: Vec<u32>,
locals_first: Vec<ValType>,
locals_all: Vec<(u32, ValType)>,
}
#[derive(Debug, Copy, Clone)]
enum MaybeType {
Bot,
HeapBot,
Type(ValType),
}
const _: () = {
assert!(std::mem::size_of::<MaybeType>() == 4);
};
impl From<ValType> for MaybeType {
fn from(ty: ValType) -> MaybeType {
MaybeType::Type(ty)
}
}
impl OperatorValidator {
fn new(features: &WasmFeatures, allocs: OperatorValidatorAllocations) -> Self {
let OperatorValidatorAllocations {
br_table_tmp,
control,
operands,
local_inits,
inits,
locals_first,
locals_all,
} = allocs;
debug_assert!(br_table_tmp.is_empty());
debug_assert!(control.is_empty());
debug_assert!(operands.is_empty());
debug_assert!(local_inits.is_empty());
debug_assert!(inits.is_empty());
debug_assert!(locals_first.is_empty());
debug_assert!(locals_all.is_empty());
OperatorValidator {
locals: Locals {
num_locals: 0,
first: locals_first,
all: locals_all,
},
local_inits,
inits,
features: *features,
br_table_tmp,
operands,
control,
end_which_emptied_control: None,
}
}
pub fn new_func<T>(
ty: u32,
offset: usize,
features: &WasmFeatures,
resources: &T,
allocs: OperatorValidatorAllocations,
) -> Result<Self>
where
T: WasmModuleResources,
{
let mut ret = OperatorValidator::new(features, allocs);
ret.control.push(Frame {
kind: FrameKind::Block,
block_type: BlockType::FuncType(ty),
height: 0,
unreachable: false,
init_height: 0,
});
let params = OperatorValidatorTemp {
offset,
inner: &mut ret,
resources,
}
.func_type_at(ty)?
.inputs();
for ty in params {
ret.locals.define(1, ty);
ret.local_inits.push(true);
}
Ok(ret)
}
pub fn new_const_expr(
features: &WasmFeatures,
ty: ValType,
allocs: OperatorValidatorAllocations,
) -> Self {
let mut ret = OperatorValidator::new(features, allocs);
ret.control.push(Frame {
kind: FrameKind::Block,
block_type: BlockType::Type(ty),
height: 0,
unreachable: false,
init_height: 0,
});
ret
}
pub fn define_locals(
&mut self,
offset: usize,
count: u32,
ty: ValType,
resources: &impl WasmModuleResources,
) -> Result<()> {
resources.check_value_type(ty, &self.features, offset)?;
if count == 0 {
return Ok(());
}
if !self.locals.define(count, ty) {
return Err(BinaryReaderError::new(
"too many locals: locals exceed maximum",
offset,
));
}
self.local_inits
.resize(self.local_inits.len() + count as usize, ty.is_defaultable());
Ok(())
}
pub fn operand_stack_height(&self) -> usize {
self.operands.len()
}
pub fn peek_operand_at(&self, depth: usize) -> Option<Option<ValType>> {
Some(match self.operands.iter().rev().nth(depth)? {
MaybeType::Type(t) => Some(*t),
MaybeType::Bot | MaybeType::HeapBot => None,
})
}
pub fn control_stack_height(&self) -> usize {
self.control.len()
}
pub fn get_frame(&self, depth: usize) -> Option<&Frame> {
self.control.iter().rev().nth(depth)
}
pub fn with_resources<'a, 'validator, 'resources, T>(
&'validator mut self,
resources: &'resources T,
offset: usize,
) -> impl VisitOperator<'a, Output = Result<()>> + 'validator
where
T: WasmModuleResources,
'resources: 'validator,
{
WasmProposalValidator(OperatorValidatorTemp {
offset,
inner: self,
resources,
})
}
pub fn finish(&mut self, offset: usize) -> Result<()> {
if self.control.last().is_some() {
bail!(
offset,
"control frames remain at end of function: END opcode expected"
);
}
if offset != self.end_which_emptied_control.unwrap() + 1 {
return Err(self.err_beyond_end(offset));
}
Ok(())
}
fn err_beyond_end(&self, offset: usize) -> BinaryReaderError {
format_err!(offset, "operators remaining after end of function")
}
pub fn into_allocations(self) -> OperatorValidatorAllocations {
fn truncate<T>(mut tmp: Vec<T>) -> Vec<T> {
tmp.truncate(0);
tmp
}
OperatorValidatorAllocations {
br_table_tmp: truncate(self.br_table_tmp),
control: truncate(self.control),
operands: truncate(self.operands),
local_inits: truncate(self.local_inits),
inits: truncate(self.inits),
locals_first: truncate(self.locals.first),
locals_all: truncate(self.locals.all),
}
}
}
impl<R> Deref for OperatorValidatorTemp<'_, '_, R> {
type Target = OperatorValidator;
fn deref(&self) -> &OperatorValidator {
self.inner
}
}
impl<R> DerefMut for OperatorValidatorTemp<'_, '_, R> {
fn deref_mut(&mut self) -> &mut OperatorValidator {
self.inner
}
}
impl<'resources, R: WasmModuleResources> OperatorValidatorTemp<'_, 'resources, R> {
fn push_operand<T>(&mut self, ty: T) -> Result<()>
where
T: Into<MaybeType>,
{
let maybe_ty = ty.into();
self.operands.push(maybe_ty);
Ok(())
}
fn pop_operand(&mut self, expected: Option<ValType>) -> Result<MaybeType> {
let popped = match self.operands.pop() {
Some(MaybeType::Type(actual_ty)) => {
if Some(actual_ty) == expected {
if let Some(control) = self.control.last() {
if self.operands.len() >= control.height {
return Ok(MaybeType::Type(actual_ty));
}
}
}
Some(MaybeType::Type(actual_ty))
}
other => other,
};
self._pop_operand(expected, popped)
}
#[cold]
fn _pop_operand(
&mut self,
expected: Option<ValType>,
popped: Option<MaybeType>,
) -> Result<MaybeType> {
self.operands.extend(popped);
let control = match self.control.last() {
Some(c) => c,
None => return Err(self.err_beyond_end(self.offset)),
};
let actual = if self.operands.len() == control.height && control.unreachable {
MaybeType::Bot
} else {
if self.operands.len() == control.height {
let desc = match expected {
Some(ty) => ty_to_str(ty),
None => "a type",
};
bail!(
self.offset,
"type mismatch: expected {desc} but nothing on stack"
)
} else {
self.operands.pop().unwrap()
}
};
if let Some(expected) = expected {
match (actual, expected) {
(MaybeType::Bot, _)
| (MaybeType::HeapBot, ValType::Ref(_)) => {}
(MaybeType::Type(actual), expected) => {
if !self.resources.matches(actual, expected) {
bail!(
self.offset,
"type mismatch: expected {}, found {}",
ty_to_str(expected),
ty_to_str(actual)
);
}
}
(
MaybeType::HeapBot,
ValType::I32 | ValType::I64 | ValType::F32 | ValType::F64 | ValType::V128,
) => {
bail!(
self.offset,
"type mismatche: expected {}, found heap type",
ty_to_str(expected)
)
}
}
}
Ok(actual)
}
fn pop_ref(&mut self) -> Result<Option<RefType>> {
match self.pop_operand(None)? {
MaybeType::Bot | MaybeType::HeapBot => Ok(None),
MaybeType::Type(ValType::Ref(rt)) => Ok(Some(rt)),
MaybeType::Type(ty) => bail!(
self.offset,
"type mismatch: expected ref but found {}",
ty_to_str(ty)
),
}
}
fn local(&self, idx: u32) -> Result<ValType> {
match self.locals.get(idx) {
Some(ty) => Ok(ty),
None => bail!(
self.offset,
"unknown local {}: local index out of bounds",
idx
),
}
}
fn unreachable(&mut self) -> Result<()> {
let control = match self.control.last_mut() {
Some(frame) => frame,
None => return Err(self.err_beyond_end(self.offset)),
};
control.unreachable = true;
let new_height = control.height;
self.operands.truncate(new_height);
Ok(())
}
fn push_ctrl(&mut self, kind: FrameKind, ty: BlockType) -> Result<()> {
let height = self.operands.len();
let init_height = self.inits.len();
self.control.push(Frame {
kind,
block_type: ty,
height,
unreachable: false,
init_height,
});
for ty in self.params(ty)? {
self.push_operand(ty)?;
}
Ok(())
}
fn pop_ctrl(&mut self) -> Result<Frame> {
let frame = match self.control.last() {
Some(f) => f,
None => return Err(self.err_beyond_end(self.offset)),
};
let ty = frame.block_type;
let height = frame.height;
let init_height = frame.init_height;
for init in self.inits.split_off(init_height) {
self.local_inits[init as usize] = false;
}
for ty in self.results(ty)?.rev() {
self.pop_operand(Some(ty))?;
}
if self.operands.len() != height {
bail!(
self.offset,
"type mismatch: values remaining on stack at end of block"
);
}
Ok(self.control.pop().unwrap())
}
fn jump(&self, depth: u32) -> Result<(BlockType, FrameKind)> {
if self.control.is_empty() {
return Err(self.err_beyond_end(self.offset));
}
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!(self.offset, "unknown label: branch depth too large"),
}
}
fn check_memory_index(&self, memory_index: u32) -> Result<ValType> {
match self.resources.memory_at(memory_index) {
Some(mem) => Ok(mem.index_type()),
None => bail!(self.offset, "unknown memory {}", memory_index),
}
}
fn check_memarg(&self, memarg: MemArg) -> Result<ValType> {
let index_ty = self.check_memory_index(memarg.memory)?;
if memarg.align > memarg.max_align {
bail!(self.offset, "alignment must not be larger than natural");
}
if index_ty == ValType::I32 && memarg.offset > u64::from(u32::MAX) {
bail!(self.offset, "offset out of range: must be <= 2**32");
}
Ok(index_ty)
}
fn check_floats_enabled(&self) -> Result<()> {
if !self.features.floats {
bail!(self.offset, "floating-point instruction disallowed");
}
Ok(())
}
fn check_shared_memarg(&self, memarg: MemArg) -> Result<ValType> {
if memarg.align != memarg.max_align {
bail!(
self.offset,
"atomic instructions must always specify maximum alignment"
);
}
self.check_memory_index(memarg.memory)
}
fn check_simd_lane_index(&self, index: u8, max: u8) -> Result<()> {
if index >= max {
bail!(self.offset, "SIMD index out of bounds");
}
Ok(())
}
fn check_block_type(&self, ty: BlockType) -> Result<()> {
match ty {
BlockType::Empty => Ok(()),
BlockType::Type(t) => self
.resources
.check_value_type(t, &self.features, self.offset),
BlockType::FuncType(idx) => {
if !self.features.multi_value {
bail!(
self.offset,
"blocks, loops, and ifs may only produce a resulttype \
when multi-value is not enabled",
);
}
self.func_type_at(idx)?;
Ok(())
}
}
}
fn check_call(&mut self, function_index: u32) -> Result<()> {
let ty = match self.resources.type_index_of_function(function_index) {
Some(i) => i,
None => {
bail!(
self.offset,
"unknown function {function_index}: function index out of bounds",
);
}
};
self.check_call_ty(ty)
}
fn check_call_ty(&mut self, type_index: u32) -> Result<()> {
let ty = match self.resources.func_type_at(type_index) {
Some(i) => i,
None => {
bail!(
self.offset,
"unknown type {type_index}: type index out of bounds",
);
}
};
for ty in ty.inputs().rev() {
self.pop_operand(Some(ty))?;
}
for ty in ty.outputs() {
self.push_operand(ty)?;
}
Ok(())
}
fn check_call_indirect(&mut self, index: u32, table_index: u32) -> Result<()> {
match self.resources.table_at(table_index) {
None => {
bail!(self.offset, "unknown table: table index out of bounds");
}
Some(tab) => {
if !self
.resources
.matches(ValType::Ref(tab.element_type), ValType::FUNCREF)
{
bail!(
self.offset,
"indirect calls must go through a table with type <= funcref",
);
}
}
}
let ty = self.func_type_at(index)?;
self.pop_operand(Some(ValType::I32))?;
for ty in ty.inputs().rev() {
self.pop_operand(Some(ty))?;
}
for ty in ty.outputs() {
self.push_operand(ty)?;
}
Ok(())
}
fn check_return(&mut self) -> Result<()> {
if self.control.is_empty() {
return Err(self.err_beyond_end(self.offset));
}
for ty in self.results(self.control[0].block_type)?.rev() {
self.pop_operand(Some(ty))?;
}
self.unreachable()?;
Ok(())
}
fn check_cmp_op(&mut self, ty: ValType) -> Result<()> {
self.pop_operand(Some(ty))?;
self.pop_operand(Some(ty))?;
self.push_operand(ValType::I32)?;
Ok(())
}
fn check_fcmp_op(&mut self, ty: ValType) -> Result<()> {
debug_assert!(matches!(ty, ValType::F32 | ValType::F64));
self.check_floats_enabled()?;
self.check_cmp_op(ty)
}
fn check_unary_op(&mut self, ty: ValType) -> Result<()> {
self.pop_operand(Some(ty))?;
self.push_operand(ty)?;
Ok(())
}
fn check_funary_op(&mut self, ty: ValType) -> Result<()> {
debug_assert!(matches!(ty, ValType::F32 | ValType::F64));
self.check_floats_enabled()?;
self.check_unary_op(ty)
}
fn check_conversion_op(&mut self, into: ValType, from: ValType) -> Result<()> {
self.pop_operand(Some(from))?;
self.push_operand(into)?;
Ok(())
}
fn check_fconversion_op(&mut self, into: ValType, from: ValType) -> Result<()> {
debug_assert!(matches!(into, ValType::F32 | ValType::F64));
self.check_floats_enabled()?;
self.check_conversion_op(into, from)
}
fn check_binary_op(&mut self, ty: ValType) -> Result<()> {
self.pop_operand(Some(ty))?;
self.pop_operand(Some(ty))?;
self.push_operand(ty)?;
Ok(())
}
fn check_fbinary_op(&mut self, ty: ValType) -> Result<()> {
debug_assert!(matches!(ty, ValType::F32 | ValType::F64));
self.check_floats_enabled()?;
self.check_binary_op(ty)
}
fn check_atomic_load(&mut self, memarg: MemArg, load_ty: ValType) -> Result<()> {
let ty = self.check_shared_memarg(memarg)?;
self.pop_operand(Some(ty))?;
self.push_operand(load_ty)?;
Ok(())
}
fn check_atomic_store(&mut self, memarg: MemArg, store_ty: ValType) -> Result<()> {
let ty = self.check_shared_memarg(memarg)?;
self.pop_operand(Some(store_ty))?;
self.pop_operand(Some(ty))?;
Ok(())
}
fn check_atomic_binary_op(&mut self, memarg: MemArg, op_ty: ValType) -> Result<()> {
let ty = self.check_shared_memarg(memarg)?;
self.pop_operand(Some(op_ty))?;
self.pop_operand(Some(ty))?;
self.push_operand(op_ty)?;
Ok(())
}
fn check_atomic_binary_cmpxchg(&mut self, memarg: MemArg, op_ty: ValType) -> Result<()> {
let ty = self.check_shared_memarg(memarg)?;
self.pop_operand(Some(op_ty))?;
self.pop_operand(Some(op_ty))?;
self.pop_operand(Some(ty))?;
self.push_operand(op_ty)?;
Ok(())
}
fn check_v128_splat(&mut self, src_ty: ValType) -> Result<()> {
self.pop_operand(Some(src_ty))?;
self.push_operand(ValType::V128)?;
Ok(())
}
fn check_v128_binary_op(&mut self) -> Result<()> {
self.pop_operand(Some(ValType::V128))?;
self.pop_operand(Some(ValType::V128))?;
self.push_operand(ValType::V128)?;
Ok(())
}
fn check_v128_fbinary_op(&mut self) -> Result<()> {
self.check_floats_enabled()?;
self.check_v128_binary_op()
}
fn check_v128_unary_op(&mut self) -> Result<()> {
self.pop_operand(Some(ValType::V128))?;
self.push_operand(ValType::V128)?;
Ok(())
}
fn check_v128_funary_op(&mut self) -> Result<()> {
self.check_floats_enabled()?;
self.check_v128_unary_op()
}
fn check_v128_ternary_op(&mut self) -> Result<()> {
self.pop_operand(Some(ValType::V128))?;
self.pop_operand(Some(ValType::V128))?;
self.pop_operand(Some(ValType::V128))?;
self.push_operand(ValType::V128)?;
Ok(())
}
fn check_v128_bitmask_op(&mut self) -> Result<()> {
self.pop_operand(Some(ValType::V128))?;
self.push_operand(ValType::I32)?;
Ok(())
}
fn check_v128_shift_op(&mut self) -> Result<()> {
self.pop_operand(Some(ValType::I32))?;
self.pop_operand(Some(ValType::V128))?;
self.push_operand(ValType::V128)?;
Ok(())
}
fn check_v128_load_op(&mut self, memarg: MemArg) -> Result<()> {
let idx = self.check_memarg(memarg)?;
self.pop_operand(Some(idx))?;
self.push_operand(ValType::V128)?;
Ok(())
}
fn func_type_at(&self, at: u32) -> Result<&'resources R::FuncType> {
self.resources
.func_type_at(at)
.ok_or_else(|| format_err!(self.offset, "unknown type: type index out of bounds"))
}
fn tag_at(&self, at: u32) -> Result<&'resources R::FuncType> {
self.resources
.tag_at(at)
.ok_or_else(|| format_err!(self.offset, "unknown tag {}: tag index out of bounds", at))
}
fn params(&self, ty: BlockType) -> Result<impl PreciseIterator<Item = ValType> + 'resources> {
Ok(match ty {
BlockType::Empty | BlockType::Type(_) => Either::B(None.into_iter()),
BlockType::FuncType(t) => Either::A(self.func_type_at(t)?.inputs()),
})
}
fn results(&self, ty: BlockType) -> Result<impl PreciseIterator<Item = ValType> + 'resources> {
Ok(match ty {
BlockType::Empty => Either::B(None.into_iter()),
BlockType::Type(t) => Either::B(Some(t).into_iter()),
BlockType::FuncType(t) => Either::A(self.func_type_at(t)?.outputs()),
})
}
fn label_types(
&self,
ty: BlockType,
kind: FrameKind,
) -> Result<impl PreciseIterator<Item = ValType> + 'resources> {
Ok(match kind {
FrameKind::Loop => Either::A(self.params(ty)?),
_ => Either::B(self.results(ty)?),
})
}
}
pub fn ty_to_str(ty: ValType) -> &'static str {
match ty {
ValType::I32 => "i32",
ValType::I64 => "i64",
ValType::F32 => "f32",
ValType::F64 => "f64",
ValType::V128 => "v128",
ValType::FUNCREF => "funcref",
ValType::EXTERNREF => "externref",
ValType::Ref(RefType {
nullable: false,
heap_type: HeapType::Func,
}) => "(ref func)",
ValType::Ref(RefType {
nullable: false,
heap_type: HeapType::Extern,
}) => "(ref extern)",
ValType::Ref(RefType {
nullable: false,
heap_type: HeapType::TypedFunc(_),
}) => "(ref $type)",
ValType::Ref(RefType {
nullable: true,
heap_type: HeapType::TypedFunc(_),
}) => "(ref null $type)",
}
}
struct WasmProposalValidator<'validator, 'resources, T>(
OperatorValidatorTemp<'validator, 'resources, T>,
);
impl<T> WasmProposalValidator<'_, '_, T> {
fn check_enabled(&self, flag: bool, desc: &str) -> Result<()> {
if flag {
return Ok(());
}
bail!(self.0.offset, "{desc} support is not enabled");
}
}
macro_rules! validate_proposal {
($( @$proposal:ident $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident)*) => {
$(
fn $visit(&mut self $($(,$arg: $argty)*)?) -> Result<()> {
validate_proposal!(validate self $proposal);
self.0.$visit($( $($arg),* )?)
}
)*
};
(validate self mvp) => {};
(validate $self:ident $proposal:ident) => {
$self.check_enabled($self.0.features.$proposal, validate_proposal!(desc $proposal))?
};
(desc simd) => ("SIMD");
(desc relaxed_simd) => ("relaxed SIMD");
(desc threads) => ("threads");
(desc saturating_float_to_int) => ("saturating float to int conversions");
(desc reference_types) => ("reference types");
(desc bulk_memory) => ("bulk memory");
(desc sign_extension) => ("sign extension operations");
(desc exceptions) => ("exceptions");
(desc tail_call) => ("tail calls");
(desc function_references) => ("function references");
(desc memory_control) => ("memory control");
}
impl<'a, T> VisitOperator<'a> for WasmProposalValidator<'_, '_, T>
where
T: WasmModuleResources,
{
type Output = Result<()>;
for_each_operator!(validate_proposal);
}
impl<'a, T> VisitOperator<'a> for OperatorValidatorTemp<'_, '_, T>
where
T: WasmModuleResources,
{
type Output = Result<()>;
fn visit_nop(&mut self) -> Self::Output {
Ok(())
}
fn visit_unreachable(&mut self) -> Self::Output {
self.unreachable()?;
Ok(())
}
fn visit_block(&mut self, ty: BlockType) -> Self::Output {
self.check_block_type(ty)?;
for ty in self.params(ty)?.rev() {
self.pop_operand(Some(ty))?;
}
self.push_ctrl(FrameKind::Block, ty)?;
Ok(())
}
fn visit_loop(&mut self, ty: BlockType) -> Self::Output {
self.check_block_type(ty)?;
for ty in self.params(ty)?.rev() {
self.pop_operand(Some(ty))?;
}
self.push_ctrl(FrameKind::Loop, ty)?;
Ok(())
}
fn visit_if(&mut self, ty: BlockType) -> Self::Output {
self.check_block_type(ty)?;
self.pop_operand(Some(ValType::I32))?;
for ty in self.params(ty)?.rev() {
self.pop_operand(Some(ty))?;
}
self.push_ctrl(FrameKind::If, ty)?;
Ok(())
}
fn visit_else(&mut self) -> Self::Output {
let frame = self.pop_ctrl()?;
if frame.kind != FrameKind::If {
bail!(self.offset, "else found outside of an `if` block");
}
self.push_ctrl(FrameKind::Else, frame.block_type)?;
Ok(())
}
fn visit_try(&mut self, ty: BlockType) -> Self::Output {
self.check_block_type(ty)?;
for ty in self.params(ty)?.rev() {
self.pop_operand(Some(ty))?;
}
self.push_ctrl(FrameKind::Try, ty)?;
Ok(())
}
fn visit_catch(&mut self, index: u32) -> Self::Output {
let frame = self.pop_ctrl()?;
if frame.kind != FrameKind::Try && frame.kind != FrameKind::Catch {
bail!(self.offset, "catch found outside of an `try` block");
}
let height = self.operands.len();
let init_height = self.inits.len();
self.control.push(Frame {
kind: FrameKind::Catch,
block_type: frame.block_type,
height,
unreachable: false,
init_height,
});
let ty = self.tag_at(index)?;
for ty in ty.inputs() {
self.push_operand(ty)?;
}
Ok(())
}
fn visit_throw(&mut self, index: u32) -> Self::Output {
let ty = self.tag_at(index)?;
for ty in ty.inputs().rev() {
self.pop_operand(Some(ty))?;
}
if ty.outputs().len() > 0 {
bail!(
self.offset,
"result type expected to be empty for exception"
);
}
self.unreachable()?;
Ok(())
}
fn visit_rethrow(&mut self, relative_depth: u32) -> Self::Output {
let (_, kind) = self.jump(relative_depth)?;
if kind != FrameKind::Catch && kind != FrameKind::CatchAll {
bail!(
self.offset,
"invalid rethrow label: target was not a `catch` block"
);
}
self.unreachable()?;
Ok(())
}
fn visit_delegate(&mut self, relative_depth: u32) -> Self::Output {
let frame = self.pop_ctrl()?;
if frame.kind != FrameKind::Try {
bail!(self.offset, "delegate found outside of an `try` block");
}
let _ = self.jump(relative_depth)?;
for ty in self.results(frame.block_type)? {
self.push_operand(ty)?;
}
Ok(())
}
fn visit_catch_all(&mut self) -> Self::Output {
let frame = self.pop_ctrl()?;
if frame.kind == FrameKind::CatchAll {
bail!(self.offset, "only one catch_all allowed per `try` block");
} else if frame.kind != FrameKind::Try && frame.kind != FrameKind::Catch {
bail!(self.offset, "catch_all found outside of a `try` block");
}
let height = self.operands.len();
let init_height = self.inits.len();
self.control.push(Frame {
kind: FrameKind::CatchAll,
block_type: frame.block_type,
height,
unreachable: false,
init_height,
});
Ok(())
}
fn visit_end(&mut self) -> Self::Output {
let mut frame = self.pop_ctrl()?;
if frame.kind == FrameKind::If {
self.push_ctrl(FrameKind::Else, frame.block_type)?;
frame = self.pop_ctrl()?;
}
for ty in self.results(frame.block_type)? {
self.push_operand(ty)?;
}
if self.control.is_empty() && self.end_which_emptied_control.is_none() {
assert_ne!(self.offset, 0);
self.end_which_emptied_control = Some(self.offset);
}
Ok(())
}
fn visit_br(&mut self, relative_depth: u32) -> Self::Output {
let (ty, kind) = self.jump(relative_depth)?;
for ty in self.label_types(ty, kind)?.rev() {
self.pop_operand(Some(ty))?;
}
self.unreachable()?;
Ok(())
}
fn visit_br_if(&mut self, relative_depth: u32) -> Self::Output {
self.pop_operand(Some(ValType::I32))?;
let (ty, kind) = self.jump(relative_depth)?;
let types = self.label_types(ty, kind)?;
for ty in types.clone().rev() {
self.pop_operand(Some(ty))?;
}
for ty in types {
self.push_operand(ty)?;
}
Ok(())
}
fn visit_br_table(&mut self, table: BrTable) -> Self::Output {
self.pop_operand(Some(ValType::I32))?;
let default = self.jump(table.default())?;
let default_types = self.label_types(default.0, default.1)?;
for element in table.targets() {
let relative_depth = element?;
let block = self.jump(relative_depth)?;
let tys = self.label_types(block.0, block.1)?;
if tys.len() != default_types.len() {
bail!(
self.offset,
"type mismatch: br_table target labels have different number of types"
);
}
debug_assert!(self.br_table_tmp.is_empty());
for ty in tys.rev() {
let ty = self.pop_operand(Some(ty))?;
self.br_table_tmp.push(ty);
}
for ty in self.inner.br_table_tmp.drain(..).rev() {
self.inner.operands.push(ty);
}
}
for ty in default_types.rev() {
self.pop_operand(Some(ty))?;
}
self.unreachable()?;
Ok(())
}
fn visit_return(&mut self) -> Self::Output {
self.check_return()?;
Ok(())
}
fn visit_call(&mut self, function_index: u32) -> Self::Output {
self.check_call(function_index)?;
Ok(())
}
fn visit_return_call(&mut self, function_index: u32) -> Self::Output {
self.check_call(function_index)?;
self.check_return()?;
Ok(())
}
fn visit_call_ref(&mut self, hty: HeapType) -> Self::Output {
self.resources
.check_heap_type(hty, &self.features, self.offset)?;
if let Some(rt) = self.pop_ref()? {
let expected = RefType {
nullable: true,
heap_type: hty,
};
if !self
.resources
.matches(ValType::Ref(rt), ValType::Ref(expected))
{
bail!(
self.offset,
"type mismatch: funcref on stack does not match specified type",
);
}
}
match hty {
HeapType::TypedFunc(type_index) => self.check_call_ty(type_index.into())?,
_ => bail!(
self.offset,
"type mismatch: instruction requires function reference type",
),
}
Ok(())
}
fn visit_return_call_ref(&mut self, hty: HeapType) -> Self::Output {
self.visit_call_ref(hty)?;
self.check_return()
}
fn visit_call_indirect(
&mut self,
index: u32,
table_index: u32,
table_byte: u8,
) -> Self::Output {
if table_byte != 0 && !self.features.reference_types {
bail!(
self.offset,
"reference-types not enabled: zero byte expected"
);
}
self.check_call_indirect(index, table_index)?;
Ok(())
}
fn visit_return_call_indirect(&mut self, index: u32, table_index: u32) -> Self::Output {
self.check_call_indirect(index, table_index)?;
self.check_return()?;
Ok(())
}
fn visit_drop(&mut self) -> Self::Output {
self.pop_operand(None)?;
Ok(())
}
fn visit_select(&mut self) -> Self::Output {
self.pop_operand(Some(ValType::I32))?;
let ty1 = self.pop_operand(None)?;
let ty2 = self.pop_operand(None)?;
let ty = match (ty1, ty2) {
(MaybeType::HeapBot, _)
| (_, MaybeType::HeapBot)
| (MaybeType::Type(ValType::Ref(_)), _)
| (_, MaybeType::Type(ValType::Ref(_))) => {
bail!(
self.offset,
"type mismatch: select only takes integral types"
)
}
(MaybeType::Bot, t) | (t, MaybeType::Bot) => t,
(t @ MaybeType::Type(t1), MaybeType::Type(t2)) => {
if t1 != t2 {
bail!(
self.offset,
"type mismatch: select operands have different types"
);
}
t
}
};
self.push_operand(ty)?;
Ok(())
}
fn visit_typed_select(&mut self, ty: ValType) -> Self::Output {
self.resources
.check_value_type(ty, &self.features, self.offset)?;
self.pop_operand(Some(ValType::I32))?;
self.pop_operand(Some(ty))?;
self.pop_operand(Some(ty))?;
self.push_operand(ty)?;
Ok(())
}
fn visit_local_get(&mut self, local_index: u32) -> Self::Output {
let ty = self.local(local_index)?;
if !self.local_inits[local_index as usize] {
bail!(self.offset, "uninitialized local: {}", local_index);
}
self.push_operand(ty)?;
Ok(())
}
fn visit_local_set(&mut self, local_index: u32) -> Self::Output {
let ty = self.local(local_index)?;
self.pop_operand(Some(ty))?;
if !self.local_inits[local_index as usize] {
self.local_inits[local_index as usize] = true;
self.inits.push(local_index);
}
Ok(())
}
fn visit_local_tee(&mut self, local_index: u32) -> Self::Output {
let ty = self.local(local_index)?;
self.pop_operand(Some(ty))?;
if !self.local_inits[local_index as usize] {
self.local_inits[local_index as usize] = true;
self.inits.push(local_index);
}
self.push_operand(ty)?;
Ok(())
}
fn visit_global_get(&mut self, global_index: u32) -> Self::Output {
if let Some(ty) = self.resources.global_at(global_index) {
self.push_operand(ty.content_type)?;
} else {
bail!(self.offset, "unknown global: global index out of bounds");
};
Ok(())
}
fn visit_global_set(&mut self, global_index: u32) -> Self::Output {
if let Some(ty) = self.resources.global_at(global_index) {
if !ty.mutable {
bail!(
self.offset,
"global is immutable: cannot modify it with `global.set`"
);
}
self.pop_operand(Some(ty.content_type))?;
} else {
bail!(self.offset, "unknown global: global index out of bounds");
};
Ok(())
}
fn visit_i32_load(&mut self, memarg: MemArg) -> Self::Output {
let ty = self.check_memarg(memarg)?;
self.pop_operand(Some(ty))?;
self.push_operand(ValType::I32)?;
Ok(())
}
fn visit_i64_load(&mut self, memarg: MemArg) -> Self::Output {
let ty = self.check_memarg(memarg)?;
self.pop_operand(Some(ty))?;
self.push_operand(ValType::I64)?;
Ok(())
}
fn visit_f32_load(&mut self, memarg: MemArg) -> Self::Output {
self.check_floats_enabled()?;
let ty = self.check_memarg(memarg)?;
self.pop_operand(Some(ty))?;
self.push_operand(ValType::F32)?;
Ok(())
}
fn visit_f64_load(&mut self, memarg: MemArg) -> Self::Output {
self.check_floats_enabled()?;
let ty = self.check_memarg(memarg)?;
self.pop_operand(Some(ty))?;
self.push_operand(ValType::F64)?;
Ok(())
}
fn visit_i32_load8_s(&mut self, memarg: MemArg) -> Self::Output {
let ty = self.check_memarg(memarg)?;
self.pop_operand(Some(ty))?;
self.push_operand(ValType::I32)?;
Ok(())
}
fn visit_i32_load8_u(&mut self, memarg: MemArg) -> Self::Output {
self.visit_i32_load8_s(memarg)
}
fn visit_i32_load16_s(&mut self, memarg: MemArg) -> Self::Output {
let ty = self.check_memarg(memarg)?;
self.pop_operand(Some(ty))?;
self.push_operand(ValType::I32)?;
Ok(())
}
fn visit_i32_load16_u(&mut self, memarg: MemArg) -> Self::Output {
self.visit_i32_load16_s(memarg)
}
fn visit_i64_load8_s(&mut self, memarg: MemArg) -> Self::Output {
let ty = self.check_memarg(memarg)?;
self.pop_operand(Some(ty))?;
self.push_operand(ValType::I64)?;
Ok(())
}
fn visit_i64_load8_u(&mut self, memarg: MemArg) -> Self::Output {
self.visit_i64_load8_s(memarg)
}
fn visit_i64_load16_s(&mut self, memarg: MemArg) -> Self::Output {
let ty = self.check_memarg(memarg)?;
self.pop_operand(Some(ty))?;
self.push_operand(ValType::I64)?;
Ok(())
}
fn visit_i64_load16_u(&mut self, memarg: MemArg) -> Self::Output {
self.visit_i64_load16_s(memarg)
}
fn visit_i64_load32_s(&mut self, memarg: MemArg) -> Self::Output {
let ty = self.check_memarg(memarg)?;
self.pop_operand(Some(ty))?;
self.push_operand(ValType::I64)?;
Ok(())
}
fn visit_i64_load32_u(&mut self, memarg: MemArg) -> Self::Output {
self.visit_i64_load32_s(memarg)
}
fn visit_i32_store(&mut self, memarg: MemArg) -> Self::Output {
let ty = self.check_memarg(memarg)?;
self.pop_operand(Some(ValType::I32))?;
self.pop_operand(Some(ty))?;
Ok(())
}
fn visit_i64_store(&mut self, memarg: MemArg) -> Self::Output {
let ty = self.check_memarg(memarg)?;
self.pop_operand(Some(ValType::I64))?;
self.pop_operand(Some(ty))?;
Ok(())
}
fn visit_f32_store(&mut self, memarg: MemArg) -> Self::Output {
self.check_floats_enabled()?;
let ty = self.check_memarg(memarg)?;
self.pop_operand(Some(ValType::F32))?;
self.pop_operand(Some(ty))?;
Ok(())
}
fn visit_f64_store(&mut self, memarg: MemArg) -> Self::Output {
self.check_floats_enabled()?;
let ty = self.check_memarg(memarg)?;
self.pop_operand(Some(ValType::F64))?;
self.pop_operand(Some(ty))?;
Ok(())
}
fn visit_i32_store8(&mut self, memarg: MemArg) -> Self::Output {
let ty = self.check_memarg(memarg)?;
self.pop_operand(Some(ValType::I32))?;
self.pop_operand(Some(ty))?;
Ok(())
}
fn visit_i32_store16(&mut self, memarg: MemArg) -> Self::Output {
let ty = self.check_memarg(memarg)?;
self.pop_operand(Some(ValType::I32))?;
self.pop_operand(Some(ty))?;
Ok(())
}
fn visit_i64_store8(&mut self, memarg: MemArg) -> Self::Output {
let ty = self.check_memarg(memarg)?;
self.pop_operand(Some(ValType::I64))?;
self.pop_operand(Some(ty))?;
Ok(())
}
fn visit_i64_store16(&mut self, memarg: MemArg) -> Self::Output {
let ty = self.check_memarg(memarg)?;
self.pop_operand(Some(ValType::I64))?;
self.pop_operand(Some(ty))?;
Ok(())
}
fn visit_i64_store32(&mut self, memarg: MemArg) -> Self::Output {
let ty = self.check_memarg(memarg)?;
self.pop_operand(Some(ValType::I64))?;
self.pop_operand(Some(ty))?;
Ok(())
}
fn visit_memory_size(&mut self, mem: u32, mem_byte: u8) -> Self::Output {
if mem_byte != 0 && !self.features.multi_memory {
bail!(self.offset, "multi-memory not enabled: zero byte expected");
}
let index_ty = self.check_memory_index(mem)?;
self.push_operand(index_ty)?;
Ok(())
}
fn visit_memory_grow(&mut self, mem: u32, mem_byte: u8) -> Self::Output {
if mem_byte != 0 && !self.features.multi_memory {
bail!(self.offset, "multi-memory not enabled: zero byte expected");
}
let index_ty = self.check_memory_index(mem)?;
self.pop_operand(Some(index_ty))?;
self.push_operand(index_ty)?;
Ok(())
}
fn visit_i32_const(&mut self, _value: i32) -> Self::Output {
self.push_operand(ValType::I32)?;
Ok(())
}
fn visit_i64_const(&mut self, _value: i64) -> Self::Output {
self.push_operand(ValType::I64)?;
Ok(())
}
fn visit_f32_const(&mut self, _value: Ieee32) -> Self::Output {
self.check_floats_enabled()?;
self.push_operand(ValType::F32)?;
Ok(())
}
fn visit_f64_const(&mut self, _value: Ieee64) -> Self::Output {
self.check_floats_enabled()?;
self.push_operand(ValType::F64)?;
Ok(())
}
fn visit_i32_eqz(&mut self) -> Self::Output {
self.pop_operand(Some(ValType::I32))?;
self.push_operand(ValType::I32)?;
Ok(())
}
fn visit_i32_eq(&mut self) -> Self::Output {
self.check_cmp_op(ValType::I32)
}
fn visit_i32_ne(&mut self) -> Self::Output {
self.check_cmp_op(ValType::I32)
}
fn visit_i32_lt_s(&mut self) -> Self::Output {
self.check_cmp_op(ValType::I32)
}
fn visit_i32_lt_u(&mut self) -> Self::Output {
self.check_cmp_op(ValType::I32)
}
fn visit_i32_gt_s(&mut self) -> Self::Output {
self.check_cmp_op(ValType::I32)
}
fn visit_i32_gt_u(&mut self) -> Self::Output {
self.check_cmp_op(ValType::I32)
}
fn visit_i32_le_s(&mut self) -> Self::Output {
self.check_cmp_op(ValType::I32)
}
fn visit_i32_le_u(&mut self) -> Self::Output {
self.check_cmp_op(ValType::I32)
}
fn visit_i32_ge_s(&mut self) -> Self::Output {
self.check_cmp_op(ValType::I32)
}
fn visit_i32_ge_u(&mut self) -> Self::Output {
self.check_cmp_op(ValType::I32)
}
fn visit_i64_eqz(&mut self) -> Self::Output {
self.pop_operand(Some(ValType::I64))?;
self.push_operand(ValType::I32)?;
Ok(())
}
fn visit_i64_eq(&mut self) -> Self::Output {
self.check_cmp_op(ValType::I64)
}
fn visit_i64_ne(&mut self) -> Self::Output {
self.check_cmp_op(ValType::I64)
}
fn visit_i64_lt_s(&mut self) -> Self::Output {
self.check_cmp_op(ValType::I64)
}
fn visit_i64_lt_u(&mut self) -> Self::Output {
self.check_cmp_op(ValType::I64)
}
fn visit_i64_gt_s(&mut self) -> Self::Output {
self.check_cmp_op(ValType::I64)
}
fn visit_i64_gt_u(&mut self) -> Self::Output {
self.check_cmp_op(ValType::I64)
}
fn visit_i64_le_s(&mut self) -> Self::Output {
self.check_cmp_op(ValType::I64)
}
fn visit_i64_le_u(&mut self) -> Self::Output {
self.check_cmp_op(ValType::I64)
}
fn visit_i64_ge_s(&mut self) -> Self::Output {
self.check_cmp_op(ValType::I64)
}
fn visit_i64_ge_u(&mut self) -> Self::Output {
self.check_cmp_op(ValType::I64)
}
fn visit_f32_eq(&mut self) -> Self::Output {
self.check_fcmp_op(ValType::F32)
}
fn visit_f32_ne(&mut self) -> Self::Output {
self.check_fcmp_op(ValType::F32)
}
fn visit_f32_lt(&mut self) -> Self::Output {
self.check_fcmp_op(ValType::F32)
}
fn visit_f32_gt(&mut self) -> Self::Output {
self.check_fcmp_op(ValType::F32)
}
fn visit_f32_le(&mut self) -> Self::Output {
self.check_fcmp_op(ValType::F32)
}
fn visit_f32_ge(&mut self) -> Self::Output {
self.check_fcmp_op(ValType::F32)
}
fn visit_f64_eq(&mut self) -> Self::Output {
self.check_fcmp_op(ValType::F64)
}
fn visit_f64_ne(&mut self) -> Self::Output {
self.check_fcmp_op(ValType::F64)
}
fn visit_f64_lt(&mut self) -> Self::Output {
self.check_fcmp_op(ValType::F64)
}
fn visit_f64_gt(&mut self) -> Self::Output {
self.check_fcmp_op(ValType::F64)
}
fn visit_f64_le(&mut self) -> Self::Output {
self.check_fcmp_op(ValType::F64)
}
fn visit_f64_ge(&mut self) -> Self::Output {
self.check_fcmp_op(ValType::F64)
}
fn visit_i32_clz(&mut self) -> Self::Output {
self.check_unary_op(ValType::I32)
}
fn visit_i32_ctz(&mut self) -> Self::Output {
self.check_unary_op(ValType::I32)
}
fn visit_i32_popcnt(&mut self) -> Self::Output {
self.check_unary_op(ValType::I32)
}
fn visit_i32_add(&mut self) -> Self::Output {
self.check_binary_op(ValType::I32)
}
fn visit_i32_sub(&mut self) -> Self::Output {
self.check_binary_op(ValType::I32)
}
fn visit_i32_mul(&mut self) -> Self::Output {
self.check_binary_op(ValType::I32)
}
fn visit_i32_div_s(&mut self) -> Self::Output {
self.check_binary_op(ValType::I32)
}
fn visit_i32_div_u(&mut self) -> Self::Output {
self.check_binary_op(ValType::I32)
}
fn visit_i32_rem_s(&mut self) -> Self::Output {
self.check_binary_op(ValType::I32)
}
fn visit_i32_rem_u(&mut self) -> Self::Output {
self.check_binary_op(ValType::I32)
}
fn visit_i32_and(&mut self) -> Self::Output {
self.check_binary_op(ValType::I32)
}
fn visit_i32_or(&mut self) -> Self::Output {
self.check_binary_op(ValType::I32)
}
fn visit_i32_xor(&mut self) -> Self::Output {
self.check_binary_op(ValType::I32)
}
fn visit_i32_shl(&mut self) -> Self::Output {
self.check_binary_op(ValType::I32)
}
fn visit_i32_shr_s(&mut self) -> Self::Output {
self.check_binary_op(ValType::I32)
}
fn visit_i32_shr_u(&mut self) -> Self::Output {
self.check_binary_op(ValType::I32)
}
fn visit_i32_rotl(&mut self) -> Self::Output {
self.check_binary_op(ValType::I32)
}
fn visit_i32_rotr(&mut self) -> Self::Output {
self.check_binary_op(ValType::I32)
}
fn visit_i64_clz(&mut self) -> Self::Output {
self.check_unary_op(ValType::I64)
}
fn visit_i64_ctz(&mut self) -> Self::Output {
self.check_unary_op(ValType::I64)
}
fn visit_i64_popcnt(&mut self) -> Self::Output {
self.check_unary_op(ValType::I64)
}
fn visit_i64_add(&mut self) -> Self::Output {
self.check_binary_op(ValType::I64)
}
fn visit_i64_sub(&mut self) -> Self::Output {
self.check_binary_op(ValType::I64)
}
fn visit_i64_mul(&mut self) -> Self::Output {
self.check_binary_op(ValType::I64)
}
fn visit_i64_div_s(&mut self) -> Self::Output {
self.check_binary_op(ValType::I64)
}
fn visit_i64_div_u(&mut self) -> Self::Output {
self.check_binary_op(ValType::I64)
}
fn visit_i64_rem_s(&mut self) -> Self::Output {
self.check_binary_op(ValType::I64)
}
fn visit_i64_rem_u(&mut self) -> Self::Output {
self.check_binary_op(ValType::I64)
}
fn visit_i64_and(&mut self) -> Self::Output {
self.check_binary_op(ValType::I64)
}
fn visit_i64_or(&mut self) -> Self::Output {
self.check_binary_op(ValType::I64)
}
fn visit_i64_xor(&mut self) -> Self::Output {
self.check_binary_op(ValType::I64)
}
fn visit_i64_shl(&mut self) -> Self::Output {
self.check_binary_op(ValType::I64)
}
fn visit_i64_shr_s(&mut self) -> Self::Output {
self.check_binary_op(ValType::I64)
}
fn visit_i64_shr_u(&mut self) -> Self::Output {
self.check_binary_op(ValType::I64)
}
fn visit_i64_rotl(&mut self) -> Self::Output {
self.check_binary_op(ValType::I64)
}
fn visit_i64_rotr(&mut self) -> Self::Output {
self.check_binary_op(ValType::I64)
}
fn visit_f32_abs(&mut self) -> Self::Output {
self.check_funary_op(ValType::F32)
}
fn visit_f32_neg(&mut self) -> Self::Output {
self.check_funary_op(ValType::F32)
}
fn visit_f32_ceil(&mut self) -> Self::Output {
self.check_funary_op(ValType::F32)
}
fn visit_f32_floor(&mut self) -> Self::Output {
self.check_funary_op(ValType::F32)
}
fn visit_f32_trunc(&mut self) -> Self::Output {
self.check_funary_op(ValType::F32)
}
fn visit_f32_nearest(&mut self) -> Self::Output {
self.check_funary_op(ValType::F32)
}
fn visit_f32_sqrt(&mut self) -> Self::Output {
self.check_funary_op(ValType::F32)
}
fn visit_f32_add(&mut self) -> Self::Output {
self.check_fbinary_op(ValType::F32)
}
fn visit_f32_sub(&mut self) -> Self::Output {
self.check_fbinary_op(ValType::F32)
}
fn visit_f32_mul(&mut self) -> Self::Output {
self.check_fbinary_op(ValType::F32)
}
fn visit_f32_div(&mut self) -> Self::Output {
self.check_fbinary_op(ValType::F32)
}
fn visit_f32_min(&mut self) -> Self::Output {
self.check_fbinary_op(ValType::F32)
}
fn visit_f32_max(&mut self) -> Self::Output {
self.check_fbinary_op(ValType::F32)
}
fn visit_f32_copysign(&mut self) -> Self::Output {
self.check_fbinary_op(ValType::F32)
}
fn visit_f64_abs(&mut self) -> Self::Output {
self.check_funary_op(ValType::F64)
}
fn visit_f64_neg(&mut self) -> Self::Output {
self.check_funary_op(ValType::F64)
}
fn visit_f64_ceil(&mut self) -> Self::Output {
self.check_funary_op(ValType::F64)
}
fn visit_f64_floor(&mut self) -> Self::Output {
self.check_funary_op(ValType::F64)
}
fn visit_f64_trunc(&mut self) -> Self::Output {
self.check_funary_op(ValType::F64)
}
fn visit_f64_nearest(&mut self) -> Self::Output {
self.check_funary_op(ValType::F64)
}
fn visit_f64_sqrt(&mut self) -> Self::Output {
self.check_funary_op(ValType::F64)
}
fn visit_f64_add(&mut self) -> Self::Output {
self.check_fbinary_op(ValType::F64)
}
fn visit_f64_sub(&mut self) -> Self::Output {
self.check_fbinary_op(ValType::F64)
}
fn visit_f64_mul(&mut self) -> Self::Output {
self.check_fbinary_op(ValType::F64)
}
fn visit_f64_div(&mut self) -> Self::Output {
self.check_fbinary_op(ValType::F64)
}
fn visit_f64_min(&mut self) -> Self::Output {
self.check_fbinary_op(ValType::F64)
}
fn visit_f64_max(&mut self) -> Self::Output {
self.check_fbinary_op(ValType::F64)
}
fn visit_f64_copysign(&mut self) -> Self::Output {
self.check_fbinary_op(ValType::F64)
}
fn visit_i32_wrap_i64(&mut self) -> Self::Output {
self.check_conversion_op(ValType::I32, ValType::I64)
}
fn visit_i32_trunc_f32_s(&mut self) -> Self::Output {
self.check_conversion_op(ValType::I32, ValType::F32)
}
fn visit_i32_trunc_f32_u(&mut self) -> Self::Output {
self.check_conversion_op(ValType::I32, ValType::F32)
}
fn visit_i32_trunc_f64_s(&mut self) -> Self::Output {
self.check_conversion_op(ValType::I32, ValType::F64)
}
fn visit_i32_trunc_f64_u(&mut self) -> Self::Output {
self.check_conversion_op(ValType::I32, ValType::F64)
}
fn visit_i64_extend_i32_s(&mut self) -> Self::Output {
self.check_conversion_op(ValType::I64, ValType::I32)
}
fn visit_i64_extend_i32_u(&mut self) -> Self::Output {
self.check_conversion_op(ValType::I64, ValType::I32)
}
fn visit_i64_trunc_f32_s(&mut self) -> Self::Output {
self.check_conversion_op(ValType::I64, ValType::F32)
}
fn visit_i64_trunc_f32_u(&mut self) -> Self::Output {
self.check_conversion_op(ValType::I64, ValType::F32)
}
fn visit_i64_trunc_f64_s(&mut self) -> Self::Output {
self.check_conversion_op(ValType::I64, ValType::F64)
}
fn visit_i64_trunc_f64_u(&mut self) -> Self::Output {
self.check_conversion_op(ValType::I64, ValType::F64)
}
fn visit_f32_convert_i32_s(&mut self) -> Self::Output {
self.check_fconversion_op(ValType::F32, ValType::I32)
}
fn visit_f32_convert_i32_u(&mut self) -> Self::Output {
self.check_fconversion_op(ValType::F32, ValType::I32)
}
fn visit_f32_convert_i64_s(&mut self) -> Self::Output {
self.check_fconversion_op(ValType::F32, ValType::I64)
}
fn visit_f32_convert_i64_u(&mut self) -> Self::Output {
self.check_fconversion_op(ValType::F32, ValType::I64)
}
fn visit_f32_demote_f64(&mut self) -> Self::Output {
self.check_fconversion_op(ValType::F32, ValType::F64)
}
fn visit_f64_convert_i32_s(&mut self) -> Self::Output {
self.check_fconversion_op(ValType::F64, ValType::I32)
}
fn visit_f64_convert_i32_u(&mut self) -> Self::Output {
self.check_fconversion_op(ValType::F64, ValType::I32)
}
fn visit_f64_convert_i64_s(&mut self) -> Self::Output {
self.check_fconversion_op(ValType::F64, ValType::I64)
}
fn visit_f64_convert_i64_u(&mut self) -> Self::Output {
self.check_fconversion_op(ValType::F64, ValType::I64)
}
fn visit_f64_promote_f32(&mut self) -> Self::Output {
self.check_fconversion_op(ValType::F64, ValType::F32)
}
fn visit_i32_reinterpret_f32(&mut self) -> Self::Output {
self.check_conversion_op(ValType::I32, ValType::F32)
}
fn visit_i64_reinterpret_f64(&mut self) -> Self::Output {
self.check_conversion_op(ValType::I64, ValType::F64)
}
fn visit_f32_reinterpret_i32(&mut self) -> Self::Output {
self.check_fconversion_op(ValType::F32, ValType::I32)
}
fn visit_f64_reinterpret_i64(&mut self) -> Self::Output {
self.check_fconversion_op(ValType::F64, ValType::I64)
}
fn visit_i32_trunc_sat_f32_s(&mut self) -> Self::Output {
self.check_conversion_op(ValType::I32, ValType::F32)
}
fn visit_i32_trunc_sat_f32_u(&mut self) -> Self::Output {
self.check_conversion_op(ValType::I32, ValType::F32)
}
fn visit_i32_trunc_sat_f64_s(&mut self) -> Self::Output {
self.check_conversion_op(ValType::I32, ValType::F64)
}
fn visit_i32_trunc_sat_f64_u(&mut self) -> Self::Output {
self.check_conversion_op(ValType::I32, ValType::F64)
}
fn visit_i64_trunc_sat_f32_s(&mut self) -> Self::Output {
self.check_conversion_op(ValType::I64, ValType::F32)
}
fn visit_i64_trunc_sat_f32_u(&mut self) -> Self::Output {
self.check_conversion_op(ValType::I64, ValType::F32)
}
fn visit_i64_trunc_sat_f64_s(&mut self) -> Self::Output {
self.check_conversion_op(ValType::I64, ValType::F64)
}
fn visit_i64_trunc_sat_f64_u(&mut self) -> Self::Output {
self.check_conversion_op(ValType::I64, ValType::F64)
}
fn visit_i32_extend8_s(&mut self) -> Self::Output {
self.check_unary_op(ValType::I32)
}
fn visit_i32_extend16_s(&mut self) -> Self::Output {
self.check_unary_op(ValType::I32)
}
fn visit_i64_extend8_s(&mut self) -> Self::Output {
self.check_unary_op(ValType::I64)
}
fn visit_i64_extend16_s(&mut self) -> Self::Output {
self.check_unary_op(ValType::I64)
}
fn visit_i64_extend32_s(&mut self) -> Self::Output {
self.check_unary_op(ValType::I64)
}
fn visit_i32_atomic_load(&mut self, memarg: MemArg) -> Self::Output {
self.check_atomic_load(memarg, ValType::I32)
}
fn visit_i32_atomic_load16_u(&mut self, memarg: MemArg) -> Self::Output {
self.check_atomic_load(memarg, ValType::I32)
}
fn visit_i32_atomic_load8_u(&mut self, memarg: MemArg) -> Self::Output {
self.check_atomic_load(memarg, ValType::I32)
}
fn visit_i64_atomic_load(&mut self, memarg: MemArg) -> Self::Output {
self.check_atomic_load(memarg, ValType::I64)
}
fn visit_i64_atomic_load32_u(&mut self, memarg: MemArg) -> Self::Output {
self.check_atomic_load(memarg, ValType::I64)
}
fn visit_i64_atomic_load16_u(&mut self, memarg: MemArg) -> Self::Output {
self.check_atomic_load(memarg, ValType::I64)
}
fn visit_i64_atomic_load8_u(&mut self, memarg: MemArg) -> Self::Output {
self.check_atomic_load(memarg, ValType::I64)
}
fn visit_i32_atomic_store(&mut self, memarg: MemArg) -> Self::Output {
self.check_atomic_store(memarg, ValType::I32)
}
fn visit_i32_atomic_store16(&mut self, memarg: MemArg) -> Self::Output {
self.check_atomic_store(memarg, ValType::I32)
}
fn visit_i32_atomic_store8(&mut self, memarg: MemArg) -> Self::Output {
self.check_atomic_store(memarg, ValType::I32)
}
fn visit_i64_atomic_store(&mut self, memarg: MemArg) -> Self::Output {
self.check_atomic_store(memarg, ValType::I64)
}
fn visit_i64_atomic_store32(&mut self, memarg: MemArg) -> Self::Output {
self.check_atomic_store(memarg, ValType::I64)
}
fn visit_i64_atomic_store16(&mut self, memarg: MemArg) -> Self::Output {
self.check_atomic_store(memarg, ValType::I64)
}
fn visit_i64_atomic_store8(&mut self, memarg: MemArg) -> Self::Output {
self.check_atomic_store(memarg, ValType::I64)
}
fn visit_i32_atomic_rmw_add(&mut self, memarg: MemArg) -> Self::Output {
self.check_atomic_binary_op(memarg, ValType::I32)
}
fn visit_i32_atomic_rmw_sub(&mut self, memarg: MemArg) -> Self::Output {
self.check_atomic_binary_op(memarg, ValType::I32)
}
fn visit_i32_atomic_rmw_and(&mut self, memarg: MemArg) -> Self::Output {
self.check_atomic_binary_op(memarg, ValType::I32)
}
fn visit_i32_atomic_rmw_or(&mut self, memarg: MemArg) -> Self::Output {
self.check_atomic_binary_op(memarg, ValType::I32)
}
fn visit_i32_atomic_rmw_xor(&mut self, memarg: MemArg) -> Self::Output {
self.check_atomic_binary_op(memarg, ValType::I32)
}
fn visit_i32_atomic_rmw16_add_u(&mut self, memarg: MemArg) -> Self::Output {
self.check_atomic_binary_op(memarg, ValType::I32)
}
fn visit_i32_atomic_rmw16_sub_u(&mut self, memarg: MemArg) -> Self::Output {
self.check_atomic_binary_op(memarg, ValType::I32)
}
fn visit_i32_atomic_rmw16_and_u(&mut self, memarg: MemArg) -> Self::Output {
self.check_atomic_binary_op(memarg, ValType::I32)
}
fn visit_i32_atomic_rmw16_or_u(&mut self, memarg: MemArg) -> Self::Output {
self.check_atomic_binary_op(memarg, ValType::I32)
}
fn visit_i32_atomic_rmw16_xor_u(&mut self, memarg: MemArg) -> Self::Output {
self.check_atomic_binary_op(memarg, ValType::I32)
}
fn visit_i32_atomic_rmw8_add_u(&mut self, memarg: MemArg) -> Self::Output {
self.check_atomic_binary_op(memarg, ValType::I32)
}
fn visit_i32_atomic_rmw8_sub_u(&mut self, memarg: MemArg) -> Self::Output {
self.check_atomic_binary_op(memarg, ValType::I32)
}
fn visit_i32_atomic_rmw8_and_u(&mut self, memarg: MemArg) -> Self::Output {
self.check_atomic_binary_op(memarg, ValType::I32)
}
fn visit_i32_atomic_rmw8_or_u(&mut self, memarg: MemArg) -> Self::Output {
self.check_atomic_binary_op(memarg, ValType::I32)
}
fn visit_i32_atomic_rmw8_xor_u(&mut self, memarg: MemArg) -> Self::Output {
self.check_atomic_binary_op(memarg, ValType::I32)
}
fn visit_i64_atomic_rmw_add(&mut self, memarg: MemArg) -> Self::Output {
self.check_atomic_binary_op(memarg, ValType::I64)
}
fn visit_i64_atomic_rmw_sub(&mut self, memarg: MemArg) -> Self::Output {
self.check_atomic_binary_op(memarg, ValType::I64)
}
fn visit_i64_atomic_rmw_and(&mut self, memarg: MemArg) -> Self::Output {
self.check_atomic_binary_op(memarg, ValType::I64)
}
fn visit_i64_atomic_rmw_or(&mut self, memarg: MemArg) -> Self::Output {
self.check_atomic_binary_op(memarg, ValType::I64)
}
fn visit_i64_atomic_rmw_xor(&mut self, memarg: MemArg) -> Self::Output {
self.check_atomic_binary_op(memarg, ValType::I64)
}
fn visit_i64_atomic_rmw32_add_u(&mut self, memarg: MemArg) -> Self::Output {
self.check_atomic_binary_op(memarg, ValType::I64)
}
fn visit_i64_atomic_rmw32_sub_u(&mut self, memarg: MemArg) -> Self::Output {
self.check_atomic_binary_op(memarg, ValType::I64)
}
fn visit_i64_atomic_rmw32_and_u(&mut self, memarg: MemArg) -> Self::Output {
self.check_atomic_binary_op(memarg, ValType::I64)
}
fn visit_i64_atomic_rmw32_or_u(&mut self, memarg: MemArg) -> Self::Output {
self.check_atomic_binary_op(memarg, ValType::I64)
}
fn visit_i64_atomic_rmw32_xor_u(&mut self, memarg: MemArg) -> Self::Output {
self.check_atomic_binary_op(memarg, ValType::I64)
}
fn visit_i64_atomic_rmw16_add_u(&mut self, memarg: MemArg) -> Self::Output {
self.check_atomic_binary_op(memarg, ValType::I64)
}
fn visit_i64_atomic_rmw16_sub_u(&mut self, memarg: MemArg) -> Self::Output {
self.check_atomic_binary_op(memarg, ValType::I64)
}
fn visit_i64_atomic_rmw16_and_u(&mut self, memarg: MemArg) -> Self::Output {
self.check_atomic_binary_op(memarg, ValType::I64)
}
fn visit_i64_atomic_rmw16_or_u(&mut self, memarg: MemArg) -> Self::Output {
self.check_atomic_binary_op(memarg, ValType::I64)
}
fn visit_i64_atomic_rmw16_xor_u(&mut self, memarg: MemArg) -> Self::Output {
self.check_atomic_binary_op(memarg, ValType::I64)
}
fn visit_i64_atomic_rmw8_add_u(&mut self, memarg: MemArg) -> Self::Output {
self.check_atomic_binary_op(memarg, ValType::I64)
}
fn visit_i64_atomic_rmw8_sub_u(&mut self, memarg: MemArg) -> Self::Output {
self.check_atomic_binary_op(memarg, ValType::I64)
}
fn visit_i64_atomic_rmw8_and_u(&mut self, memarg: MemArg) -> Self::Output {
self.check_atomic_binary_op(memarg, ValType::I64)
}
fn visit_i64_atomic_rmw8_or_u(&mut self, memarg: MemArg) -> Self::Output {
self.check_atomic_binary_op(memarg, ValType::I64)
}
fn visit_i64_atomic_rmw8_xor_u(&mut self, memarg: MemArg) -> Self::Output {
self.check_atomic_binary_op(memarg, ValType::I64)
}
fn visit_i32_atomic_rmw_xchg(&mut self, memarg: MemArg) -> Self::Output {
self.check_atomic_binary_op(memarg, ValType::I32)
}
fn visit_i32_atomic_rmw16_xchg_u(&mut self, memarg: MemArg) -> Self::Output {
self.check_atomic_binary_op(memarg, ValType::I32)
}
fn visit_i32_atomic_rmw8_xchg_u(&mut self, memarg: MemArg) -> Self::Output {
self.check_atomic_binary_op(memarg, ValType::I32)
}
fn visit_i32_atomic_rmw_cmpxchg(&mut self, memarg: MemArg) -> Self::Output {
self.check_atomic_binary_cmpxchg(memarg, ValType::I32)
}
fn visit_i32_atomic_rmw16_cmpxchg_u(&mut self, memarg: MemArg) -> Self::Output {
self.check_atomic_binary_cmpxchg(memarg, ValType::I32)
}
fn visit_i32_atomic_rmw8_cmpxchg_u(&mut self, memarg: MemArg) -> Self::Output {
self.check_atomic_binary_cmpxchg(memarg, ValType::I32)
}
fn visit_i64_atomic_rmw_xchg(&mut self, memarg: MemArg) -> Self::Output {
self.check_atomic_binary_op(memarg, ValType::I64)
}
fn visit_i64_atomic_rmw32_xchg_u(&mut self, memarg: MemArg) -> Self::Output {
self.check_atomic_binary_op(memarg, ValType::I64)
}
fn visit_i64_atomic_rmw16_xchg_u(&mut self, memarg: MemArg) -> Self::Output {
self.check_atomic_binary_op(memarg, ValType::I64)
}
fn visit_i64_atomic_rmw8_xchg_u(&mut self, memarg: MemArg) -> Self::Output {
self.check_atomic_binary_op(memarg, ValType::I64)
}
fn visit_i64_atomic_rmw_cmpxchg(&mut self, memarg: MemArg) -> Self::Output {
self.check_atomic_binary_cmpxchg(memarg, ValType::I64)
}
fn visit_i64_atomic_rmw32_cmpxchg_u(&mut self, memarg: MemArg) -> Self::Output {
self.check_atomic_binary_cmpxchg(memarg, ValType::I64)
}
fn visit_i64_atomic_rmw16_cmpxchg_u(&mut self, memarg: MemArg) -> Self::Output {
self.check_atomic_binary_cmpxchg(memarg, ValType::I64)
}
fn visit_i64_atomic_rmw8_cmpxchg_u(&mut self, memarg: MemArg) -> Self::Output {
self.check_atomic_binary_cmpxchg(memarg, ValType::I64)
}
fn visit_memory_atomic_notify(&mut self, memarg: MemArg) -> Self::Output {
self.check_atomic_binary_op(memarg, ValType::I32)
}
fn visit_memory_atomic_wait32(&mut self, memarg: MemArg) -> Self::Output {
let ty = self.check_shared_memarg(memarg)?;
self.pop_operand(Some(ValType::I64))?;
self.pop_operand(Some(ValType::I32))?;
self.pop_operand(Some(ty))?;
self.push_operand(ValType::I32)?;
Ok(())
}
fn visit_memory_atomic_wait64(&mut self, memarg: MemArg) -> Self::Output {
let ty = self.check_shared_memarg(memarg)?;
self.pop_operand(Some(ValType::I64))?;
self.pop_operand(Some(ValType::I64))?;
self.pop_operand(Some(ty))?;
self.push_operand(ValType::I32)?;
Ok(())
}
fn visit_atomic_fence(&mut self) -> Self::Output {
Ok(())
}
fn visit_ref_null(&mut self, heap_type: HeapType) -> Self::Output {
self.resources
.check_heap_type(heap_type, &self.features, self.offset)?;
self.push_operand(ValType::Ref(RefType {
nullable: true,
heap_type,
}))?;
Ok(())
}
fn visit_ref_as_non_null(&mut self) -> Self::Output {
let ty = match self.pop_ref()? {
Some(ty) => MaybeType::Type(ValType::Ref(RefType {
nullable: false,
heap_type: ty.heap_type,
})),
None => MaybeType::HeapBot,
};
self.push_operand(ty)?;
Ok(())
}
fn visit_br_on_null(&mut self, relative_depth: u32) -> Self::Output {
let ty = match self.pop_ref()? {
None => MaybeType::HeapBot,
Some(ty) => MaybeType::Type(ValType::Ref(RefType {
nullable: false,
heap_type: ty.heap_type,
})),
};
let (ft, kind) = self.jump(relative_depth)?;
for ty in self.label_types(ft, kind)?.rev() {
self.pop_operand(Some(ty))?;
}
for ty in self.label_types(ft, kind)? {
self.push_operand(ty)?;
}
self.push_operand(ty)?;
Ok(())
}
fn visit_br_on_non_null(&mut self, relative_depth: u32) -> Self::Output {
let ty = self.pop_ref()?;
let (ft, kind) = self.jump(relative_depth)?;
let mut lts = self.label_types(ft, kind)?;
match (lts.next_back(), ty) {
(None, _) => bail!(
self.offset,
"type mismatch: br_on_non_null target has no label types",
),
(Some(ValType::Ref(_)), None) => {}
(Some(rt1 @ ValType::Ref(_)), Some(rt0)) => {
let ty = RefType {
nullable: false,
heap_type: rt0.heap_type,
};
if !self.resources.matches(ty.into(), rt1) {
bail!(
self.offset,
"type mismatch: expected {} but found {}",
ty_to_str(rt0.into()),
ty_to_str(rt1)
)
}
}
(Some(_), _) => bail!(
self.offset,
"type mismatch: br_on_non_null target does not end with heap type",
),
}
for ty in self.label_types(ft, kind)?.rev().skip(1) {
self.pop_operand(Some(ty))?;
}
for ty in lts {
self.push_operand(ty)?;
}
Ok(())
}
fn visit_ref_is_null(&mut self) -> Self::Output {
self.pop_ref()?;
self.push_operand(ValType::I32)?;
Ok(())
}
fn visit_ref_func(&mut self, function_index: u32) -> Self::Output {
let type_index = match self.resources.type_index_of_function(function_index) {
Some(idx) => idx,
None => bail!(
self.offset,
"unknown function {}: function index out of bounds",
function_index,
),
};
if !self.resources.is_function_referenced(function_index) {
bail!(self.offset, "undeclared function reference");
}
if self.features.function_references {
let heap_type = HeapType::TypedFunc(match type_index.try_into() {
Ok(packed) => packed,
Err(_) => {
bail!(self.offset, "type index of `ref.func` target too large")
}
});
self.push_operand(ValType::Ref(RefType {
nullable: false,
heap_type,
}))?;
} else {
self.push_operand(ValType::FUNCREF)?;
}
Ok(())
}
fn visit_v128_load(&mut self, memarg: MemArg) -> Self::Output {
let ty = self.check_memarg(memarg)?;
self.pop_operand(Some(ty))?;
self.push_operand(ValType::V128)?;
Ok(())
}
fn visit_v128_store(&mut self, memarg: MemArg) -> Self::Output {
let ty = self.check_memarg(memarg)?;
self.pop_operand(Some(ValType::V128))?;
self.pop_operand(Some(ty))?;
Ok(())
}
fn visit_v128_const(&mut self, _value: V128) -> Self::Output {
self.push_operand(ValType::V128)?;
Ok(())
}
fn visit_i8x16_splat(&mut self) -> Self::Output {
self.check_v128_splat(ValType::I32)
}
fn visit_i16x8_splat(&mut self) -> Self::Output {
self.check_v128_splat(ValType::I32)
}
fn visit_i32x4_splat(&mut self) -> Self::Output {
self.check_v128_splat(ValType::I32)
}
fn visit_i64x2_splat(&mut self) -> Self::Output {
self.check_v128_splat(ValType::I64)
}
fn visit_f32x4_splat(&mut self) -> Self::Output {
self.check_floats_enabled()?;
self.check_v128_splat(ValType::F32)
}
fn visit_f64x2_splat(&mut self) -> Self::Output {
self.check_floats_enabled()?;
self.check_v128_splat(ValType::F64)
}
fn visit_i8x16_extract_lane_s(&mut self, lane: u8) -> Self::Output {
self.check_simd_lane_index(lane, 16)?;
self.pop_operand(Some(ValType::V128))?;
self.push_operand(ValType::I32)?;
Ok(())
}
fn visit_i8x16_extract_lane_u(&mut self, lane: u8) -> Self::Output {
self.visit_i8x16_extract_lane_s(lane)
}
fn visit_i16x8_extract_lane_s(&mut self, lane: u8) -> Self::Output {
self.check_simd_lane_index(lane, 8)?;
self.pop_operand(Some(ValType::V128))?;
self.push_operand(ValType::I32)?;
Ok(())
}
fn visit_i16x8_extract_lane_u(&mut self, lane: u8) -> Self::Output {
self.visit_i16x8_extract_lane_s(lane)
}
fn visit_i32x4_extract_lane(&mut self, lane: u8) -> Self::Output {
self.check_simd_lane_index(lane, 4)?;
self.pop_operand(Some(ValType::V128))?;
self.push_operand(ValType::I32)?;
Ok(())
}
fn visit_i8x16_replace_lane(&mut self, lane: u8) -> Self::Output {
self.check_simd_lane_index(lane, 16)?;
self.pop_operand(Some(ValType::I32))?;
self.pop_operand(Some(ValType::V128))?;
self.push_operand(ValType::V128)?;
Ok(())
}
fn visit_i16x8_replace_lane(&mut self, lane: u8) -> Self::Output {
self.check_simd_lane_index(lane, 8)?;
self.pop_operand(Some(ValType::I32))?;
self.pop_operand(Some(ValType::V128))?;
self.push_operand(ValType::V128)?;
Ok(())
}
fn visit_i32x4_replace_lane(&mut self, lane: u8) -> Self::Output {
self.check_simd_lane_index(lane, 4)?;
self.pop_operand(Some(ValType::I32))?;
self.pop_operand(Some(ValType::V128))?;
self.push_operand(ValType::V128)?;
Ok(())
}
fn visit_i64x2_extract_lane(&mut self, lane: u8) -> Self::Output {
self.check_simd_lane_index(lane, 2)?;
self.pop_operand(Some(ValType::V128))?;
self.push_operand(ValType::I64)?;
Ok(())
}
fn visit_i64x2_replace_lane(&mut self, lane: u8) -> Self::Output {
self.check_simd_lane_index(lane, 2)?;
self.pop_operand(Some(ValType::I64))?;
self.pop_operand(Some(ValType::V128))?;
self.push_operand(ValType::V128)?;
Ok(())
}
fn visit_f32x4_extract_lane(&mut self, lane: u8) -> Self::Output {
self.check_floats_enabled()?;
self.check_simd_lane_index(lane, 4)?;
self.pop_operand(Some(ValType::V128))?;
self.push_operand(ValType::F32)?;
Ok(())
}
fn visit_f32x4_replace_lane(&mut self, lane: u8) -> Self::Output {
self.check_floats_enabled()?;
self.check_simd_lane_index(lane, 4)?;
self.pop_operand(Some(ValType::F32))?;
self.pop_operand(Some(ValType::V128))?;
self.push_operand(ValType::V128)?;
Ok(())
}
fn visit_f64x2_extract_lane(&mut self, lane: u8) -> Self::Output {
self.check_floats_enabled()?;
self.check_simd_lane_index(lane, 2)?;
self.pop_operand(Some(ValType::V128))?;
self.push_operand(ValType::F64)?;
Ok(())
}
fn visit_f64x2_replace_lane(&mut self, lane: u8) -> Self::Output {
self.check_floats_enabled()?;
self.check_simd_lane_index(lane, 2)?;
self.pop_operand(Some(ValType::F64))?;
self.pop_operand(Some(ValType::V128))?;
self.push_operand(ValType::V128)?;
Ok(())
}
fn visit_f32x4_eq(&mut self) -> Self::Output {
self.check_v128_fbinary_op()
}
fn visit_f32x4_ne(&mut self) -> Self::Output {
self.check_v128_fbinary_op()
}
fn visit_f32x4_lt(&mut self) -> Self::Output {
self.check_v128_fbinary_op()
}
fn visit_f32x4_gt(&mut self) -> Self::Output {
self.check_v128_fbinary_op()
}
fn visit_f32x4_le(&mut self) -> Self::Output {
self.check_v128_fbinary_op()
}
fn visit_f32x4_ge(&mut self) -> Self::Output {
self.check_v128_fbinary_op()
}
fn visit_f64x2_eq(&mut self) -> Self::Output {
self.check_v128_fbinary_op()
}
fn visit_f64x2_ne(&mut self) -> Self::Output {
self.check_v128_fbinary_op()
}
fn visit_f64x2_lt(&mut self) -> Self::Output {
self.check_v128_fbinary_op()
}
fn visit_f64x2_gt(&mut self) -> Self::Output {
self.check_v128_fbinary_op()
}
fn visit_f64x2_le(&mut self) -> Self::Output {
self.check_v128_fbinary_op()
}
fn visit_f64x2_ge(&mut self) -> Self::Output {
self.check_v128_fbinary_op()
}
fn visit_f32x4_add(&mut self) -> Self::Output {
self.check_v128_fbinary_op()
}
fn visit_f32x4_sub(&mut self) -> Self::Output {
self.check_v128_fbinary_op()
}
fn visit_f32x4_mul(&mut self) -> Self::Output {
self.check_v128_fbinary_op()
}
fn visit_f32x4_div(&mut self) -> Self::Output {
self.check_v128_fbinary_op()
}
fn visit_f32x4_min(&mut self) -> Self::Output {
self.check_v128_fbinary_op()
}
fn visit_f32x4_max(&mut self) -> Self::Output {
self.check_v128_fbinary_op()
}
fn visit_f32x4_pmin(&mut self) -> Self::Output {
self.check_v128_fbinary_op()
}
fn visit_f32x4_pmax(&mut self) -> Self::Output {
self.check_v128_fbinary_op()
}
fn visit_f64x2_add(&mut self) -> Self::Output {
self.check_v128_fbinary_op()
}
fn visit_f64x2_sub(&mut self) -> Self::Output {
self.check_v128_fbinary_op()
}
fn visit_f64x2_mul(&mut self) -> Self::Output {
self.check_v128_fbinary_op()
}
fn visit_f64x2_div(&mut self) -> Self::Output {
self.check_v128_fbinary_op()
}
fn visit_f64x2_min(&mut self) -> Self::Output {
self.check_v128_fbinary_op()
}
fn visit_f64x2_max(&mut self) -> Self::Output {
self.check_v128_fbinary_op()
}
fn visit_f64x2_pmin(&mut self) -> Self::Output {
self.check_v128_fbinary_op()
}
fn visit_f64x2_pmax(&mut self) -> Self::Output {
self.check_v128_fbinary_op()
}
fn visit_i8x16_eq(&mut self) -> Self::Output {
self.check_v128_binary_op()
}
fn visit_i8x16_ne(&mut self) -> Self::Output {
self.check_v128_binary_op()
}
fn visit_i8x16_lt_s(&mut self) -> Self::Output {
self.check_v128_binary_op()
}
fn visit_i8x16_lt_u(&mut self) -> Self::Output {
self.check_v128_binary_op()
}
fn visit_i8x16_gt_s(&mut self) -> Self::Output {
self.check_v128_binary_op()
}
fn visit_i8x16_gt_u(&mut self) -> Self::Output {
self.check_v128_binary_op()
}
fn visit_i8x16_le_s(&mut self) -> Self::Output {
self.check_v128_binary_op()
}
fn visit_i8x16_le_u(&mut self) -> Self::Output {
self.check_v128_binary_op()
}
fn visit_i8x16_ge_s(&mut self) -> Self::Output {
self.check_v128_binary_op()
}
fn visit_i8x16_ge_u(&mut self) -> Self::Output {
self.check_v128_binary_op()
}
fn visit_i16x8_eq(&mut self) -> Self::Output {
self.check_v128_binary_op()
}
fn visit_i16x8_ne(&mut self) -> Self::Output {
self.check_v128_binary_op()
}
fn visit_i16x8_lt_s(&mut self) -> Self::Output {
self.check_v128_binary_op()
}
fn visit_i16x8_lt_u(&mut self) -> Self::Output {
self.check_v128_binary_op()
}
fn visit_i16x8_gt_s(&mut self) -> Self::Output {
self.check_v128_binary_op()
}
fn visit_i16x8_gt_u(&mut self) -> Self::Output {
self.check_v128_binary_op()
}
fn visit_i16x8_le_s(&mut self) -> Self::Output {
self.check_v128_binary_op()
}
fn visit_i16x8_le_u(&mut self) -> Self::Output {
self.check_v128_binary_op()
}
fn visit_i16x8_ge_s(&mut self) -> Self::Output {
self.check_v128_binary_op()
}
fn visit_i16x8_ge_u(&mut self) -> Self::Output {
self.check_v128_binary_op()
}
fn visit_i32x4_eq(&mut self) -> Self::Output {
self.check_v128_binary_op()
}
fn visit_i32x4_ne(&mut self) -> Self::Output {
self.check_v128_binary_op()
}
fn visit_i32x4_lt_s(&mut self) -> Self::Output {
self.check_v128_binary_op()
}
fn visit_i32x4_lt_u(&mut self) -> Self::Output {
self.check_v128_binary_op()
}
fn visit_i32x4_gt_s(&mut self) -> Self::Output {
self.check_v128_binary_op()
}
fn visit_i32x4_gt_u(&mut self) -> Self::Output {
self.check_v128_binary_op()
}
fn visit_i32x4_le_s(&mut self) -> Self::Output {
self.check_v128_binary_op()
}
fn visit_i32x4_le_u(&mut self) -> Self::Output {
self.check_v128_binary_op()
}
fn visit_i32x4_ge_s(&mut self) -> Self::Output {
self.check_v128_binary_op()
}
fn visit_i32x4_ge_u(&mut self) -> Self::Output {
self.check_v128_binary_op()
}
fn visit_i64x2_eq(&mut self) -> Self::Output {
self.check_v128_binary_op()
}
fn visit_i64x2_ne(&mut self) -> Self::Output {
self.check_v128_binary_op()
}
fn visit_i64x2_lt_s(&mut self) -> Self::Output {
self.check_v128_binary_op()
}
fn visit_i64x2_gt_s(&mut self) -> Self::Output {
self.check_v128_binary_op()
}
fn visit_i64x2_le_s(&mut self) -> Self::Output {
self.check_v128_binary_op()
}
fn visit_i64x2_ge_s(&mut self) -> Self::Output {
self.check_v128_binary_op()
}
fn visit_v128_and(&mut self) -> Self::Output {
self.check_v128_binary_op()
}
fn visit_v128_andnot(&mut self) -> Self::Output {
self.check_v128_binary_op()
}
fn visit_v128_or(&mut self) -> Self::Output {
self.check_v128_binary_op()
}
fn visit_v128_xor(&mut self) -> Self::Output {
self.check_v128_binary_op()
}
fn visit_i8x16_add(&mut self) -> Self::Output {
self.check_v128_binary_op()
}
fn visit_i8x16_add_sat_s(&mut self) -> Self::Output {
self.check_v128_binary_op()
}
fn visit_i8x16_add_sat_u(&mut self) -> Self::Output {
self.check_v128_binary_op()
}
fn visit_i8x16_sub(&mut self) -> Self::Output {
self.check_v128_binary_op()
}
fn visit_i8x16_sub_sat_s(&mut self) -> Self::Output {
self.check_v128_binary_op()
}
fn visit_i8x16_sub_sat_u(&mut self) -> Self::Output {
self.check_v128_binary_op()
}
fn visit_i8x16_min_s(&mut self) -> Self::Output {
self.check_v128_binary_op()
}
fn visit_i8x16_min_u(&mut self) -> Self::Output {
self.check_v128_binary_op()
}
fn visit_i8x16_max_s(&mut self) -> Self::Output {
self.check_v128_binary_op()
}
fn visit_i8x16_max_u(&mut self) -> Self::Output {
self.check_v128_binary_op()
}
fn visit_i16x8_add(&mut self) -> Self::Output {
self.check_v128_binary_op()
}
fn visit_i16x8_add_sat_s(&mut self) -> Self::Output {
self.check_v128_binary_op()
}
fn visit_i16x8_add_sat_u(&mut self) -> Self::Output {
self.check_v128_binary_op()
}
fn visit_i16x8_sub(&mut self) -> Self::Output {
self.check_v128_binary_op()
}
fn visit_i16x8_sub_sat_s(&mut self) -> Self::Output {
self.check_v128_binary_op()
}
fn visit_i16x8_sub_sat_u(&mut self) -> Self::Output {
self.check_v128_binary_op()
}
fn visit_i16x8_mul(&mut self) -> Self::Output {
self.check_v128_binary_op()
}
fn visit_i16x8_min_s(&mut self) -> Self::Output {
self.check_v128_binary_op()
}
fn visit_i16x8_min_u(&mut self) -> Self::Output {
self.check_v128_binary_op()
}
fn visit_i16x8_max_s(&mut self) -> Self::Output {
self.check_v128_binary_op()
}
fn visit_i16x8_max_u(&mut self) -> Self::Output {
self.check_v128_binary_op()
}
fn visit_i32x4_add(&mut self) -> Self::Output {
self.check_v128_binary_op()
}
fn visit_i32x4_sub(&mut self) -> Self::Output {
self.check_v128_binary_op()
}
fn visit_i32x4_mul(&mut self) -> Self::Output {
self.check_v128_binary_op()
}
fn visit_i32x4_min_s(&mut self) -> Self::Output {
self.check_v128_binary_op()
}
fn visit_i32x4_min_u(&mut self) -> Self::Output {
self.check_v128_binary_op()
}
fn visit_i32x4_max_s(&mut self) -> Self::Output {
self.check_v128_binary_op()
}
fn visit_i32x4_max_u(&mut self) -> Self::Output {
self.check_v128_binary_op()
}
fn visit_i32x4_dot_i16x8_s(&mut self) -> Self::Output {
self.check_v128_binary_op()
}
fn visit_i64x2_add(&mut self) -> Self::Output {
self.check_v128_binary_op()
}
fn visit_i64x2_sub(&mut self) -> Self::Output {
self.check_v128_binary_op()
}
fn visit_i64x2_mul(&mut self) -> Self::Output {
self.check_v128_binary_op()
}
fn visit_i8x16_avgr_u(&mut self) -> Self::Output {
self.check_v128_binary_op()
}
fn visit_i16x8_avgr_u(&mut self) -> Self::Output {
self.check_v128_binary_op()
}
fn visit_i8x16_narrow_i16x8_s(&mut self) -> Self::Output {
self.check_v128_binary_op()
}
fn visit_i8x16_narrow_i16x8_u(&mut self) -> Self::Output {
self.check_v128_binary_op()
}
fn visit_i16x8_narrow_i32x4_s(&mut self) -> Self::Output {
self.check_v128_binary_op()
}
fn visit_i16x8_narrow_i32x4_u(&mut self) -> Self::Output {
self.check_v128_binary_op()
}
fn visit_i16x8_extmul_low_i8x16_s(&mut self) -> Self::Output {
self.check_v128_binary_op()
}
fn visit_i16x8_extmul_high_i8x16_s(&mut self) -> Self::Output {
self.check_v128_binary_op()
}
fn visit_i16x8_extmul_low_i8x16_u(&mut self) -> Self::Output {
self.check_v128_binary_op()
}
fn visit_i16x8_extmul_high_i8x16_u(&mut self) -> Self::Output {
self.check_v128_binary_op()
}
fn visit_i32x4_extmul_low_i16x8_s(&mut self) -> Self::Output {
self.check_v128_binary_op()
}
fn visit_i32x4_extmul_high_i16x8_s(&mut self) -> Self::Output {
self.check_v128_binary_op()
}
fn visit_i32x4_extmul_low_i16x8_u(&mut self) -> Self::Output {
self.check_v128_binary_op()
}
fn visit_i32x4_extmul_high_i16x8_u(&mut self) -> Self::Output {
self.check_v128_binary_op()
}
fn visit_i64x2_extmul_low_i32x4_s(&mut self) -> Self::Output {
self.check_v128_binary_op()
}
fn visit_i64x2_extmul_high_i32x4_s(&mut self) -> Self::Output {
self.check_v128_binary_op()
}
fn visit_i64x2_extmul_low_i32x4_u(&mut self) -> Self::Output {
self.check_v128_binary_op()
}
fn visit_i64x2_extmul_high_i32x4_u(&mut self) -> Self::Output {
self.check_v128_binary_op()
}
fn visit_i16x8_q15mulr_sat_s(&mut self) -> Self::Output {
self.check_v128_binary_op()
}
fn visit_f32x4_ceil(&mut self) -> Self::Output {
self.check_v128_funary_op()
}
fn visit_f32x4_floor(&mut self) -> Self::Output {
self.check_v128_funary_op()
}
fn visit_f32x4_trunc(&mut self) -> Self::Output {
self.check_v128_funary_op()
}
fn visit_f32x4_nearest(&mut self) -> Self::Output {
self.check_v128_funary_op()
}
fn visit_f64x2_ceil(&mut self) -> Self::Output {
self.check_v128_funary_op()
}
fn visit_f64x2_floor(&mut self) -> Self::Output {
self.check_v128_funary_op()
}
fn visit_f64x2_trunc(&mut self) -> Self::Output {
self.check_v128_funary_op()
}
fn visit_f64x2_nearest(&mut self) -> Self::Output {
self.check_v128_funary_op()
}
fn visit_f32x4_abs(&mut self) -> Self::Output {
self.check_v128_funary_op()
}
fn visit_f32x4_neg(&mut self) -> Self::Output {
self.check_v128_funary_op()
}
fn visit_f32x4_sqrt(&mut self) -> Self::Output {
self.check_v128_funary_op()
}
fn visit_f64x2_abs(&mut self) -> Self::Output {
self.check_v128_funary_op()
}
fn visit_f64x2_neg(&mut self) -> Self::Output {
self.check_v128_funary_op()
}
fn visit_f64x2_sqrt(&mut self) -> Self::Output {
self.check_v128_funary_op()
}
fn visit_f32x4_demote_f64x2_zero(&mut self) -> Self::Output {
self.check_v128_funary_op()
}
fn visit_f64x2_promote_low_f32x4(&mut self) -> Self::Output {
self.check_v128_funary_op()
}
fn visit_f64x2_convert_low_i32x4_s(&mut self) -> Self::Output {
self.check_v128_funary_op()
}
fn visit_f64x2_convert_low_i32x4_u(&mut self) -> Self::Output {
self.check_v128_funary_op()
}
fn visit_i32x4_trunc_sat_f32x4_s(&mut self) -> Self::Output {
self.check_v128_funary_op()
}
fn visit_i32x4_trunc_sat_f32x4_u(&mut self) -> Self::Output {
self.check_v128_funary_op()
}
fn visit_i32x4_trunc_sat_f64x2_s_zero(&mut self) -> Self::Output {
self.check_v128_funary_op()
}
fn visit_i32x4_trunc_sat_f64x2_u_zero(&mut self) -> Self::Output {
self.check_v128_funary_op()
}
fn visit_f32x4_convert_i32x4_s(&mut self) -> Self::Output {
self.check_v128_funary_op()
}
fn visit_f32x4_convert_i32x4_u(&mut self) -> Self::Output {
self.check_v128_funary_op()
}
fn visit_v128_not(&mut self) -> Self::Output {
self.check_v128_unary_op()
}
fn visit_i8x16_abs(&mut self) -> Self::Output {
self.check_v128_unary_op()
}
fn visit_i8x16_neg(&mut self) -> Self::Output {
self.check_v128_unary_op()
}
fn visit_i8x16_popcnt(&mut self) -> Self::Output {
self.check_v128_unary_op()
}
fn visit_i16x8_abs(&mut self) -> Self::Output {
self.check_v128_unary_op()
}
fn visit_i16x8_neg(&mut self) -> Self::Output {
self.check_v128_unary_op()
}
fn visit_i32x4_abs(&mut self) -> Self::Output {
self.check_v128_unary_op()
}
fn visit_i32x4_neg(&mut self) -> Self::Output {
self.check_v128_unary_op()
}
fn visit_i64x2_abs(&mut self) -> Self::Output {
self.check_v128_unary_op()
}
fn visit_i64x2_neg(&mut self) -> Self::Output {
self.check_v128_unary_op()
}
fn visit_i16x8_extend_low_i8x16_s(&mut self) -> Self::Output {
self.check_v128_unary_op()
}
fn visit_i16x8_extend_high_i8x16_s(&mut self) -> Self::Output {
self.check_v128_unary_op()
}
fn visit_i16x8_extend_low_i8x16_u(&mut self) -> Self::Output {
self.check_v128_unary_op()
}
fn visit_i16x8_extend_high_i8x16_u(&mut self) -> Self::Output {
self.check_v128_unary_op()
}
fn visit_i32x4_extend_low_i16x8_s(&mut self) -> Self::Output {
self.check_v128_unary_op()
}
fn visit_i32x4_extend_high_i16x8_s(&mut self) -> Self::Output {
self.check_v128_unary_op()
}
fn visit_i32x4_extend_low_i16x8_u(&mut self) -> Self::Output {
self.check_v128_unary_op()
}
fn visit_i32x4_extend_high_i16x8_u(&mut self) -> Self::Output {
self.check_v128_unary_op()
}
fn visit_i64x2_extend_low_i32x4_s(&mut self) -> Self::Output {
self.check_v128_unary_op()
}
fn visit_i64x2_extend_high_i32x4_s(&mut self) -> Self::Output {
self.check_v128_unary_op()
}
fn visit_i64x2_extend_low_i32x4_u(&mut self) -> Self::Output {
self.check_v128_unary_op()
}
fn visit_i64x2_extend_high_i32x4_u(&mut self) -> Self::Output {
self.check_v128_unary_op()
}
fn visit_i16x8_extadd_pairwise_i8x16_s(&mut self) -> Self::Output {
self.check_v128_unary_op()
}
fn visit_i16x8_extadd_pairwise_i8x16_u(&mut self) -> Self::Output {
self.check_v128_unary_op()
}
fn visit_i32x4_extadd_pairwise_i16x8_s(&mut self) -> Self::Output {
self.check_v128_unary_op()
}
fn visit_i32x4_extadd_pairwise_i16x8_u(&mut self) -> Self::Output {
self.check_v128_unary_op()
}
fn visit_v128_bitselect(&mut self) -> Self::Output {
self.pop_operand(Some(ValType::V128))?;
self.pop_operand(Some(ValType::V128))?;
self.pop_operand(Some(ValType::V128))?;
self.push_operand(ValType::V128)?;
Ok(())
}
fn visit_i8x16_relaxed_swizzle(&mut self) -> Self::Output {
self.pop_operand(Some(ValType::V128))?;
self.pop_operand(Some(ValType::V128))?;
self.push_operand(ValType::V128)?;
Ok(())
}
fn visit_i32x4_relaxed_trunc_f32x4_s(&mut self) -> Self::Output {
self.check_v128_unary_op()
}
fn visit_i32x4_relaxed_trunc_f32x4_u(&mut self) -> Self::Output {
self.check_v128_unary_op()
}
fn visit_i32x4_relaxed_trunc_f64x2_s_zero(&mut self) -> Self::Output {
self.check_v128_unary_op()
}
fn visit_i32x4_relaxed_trunc_f64x2_u_zero(&mut self) -> Self::Output {
self.check_v128_unary_op()
}
fn visit_f32x4_relaxed_madd(&mut self) -> Self::Output {
self.check_v128_ternary_op()
}
fn visit_f32x4_relaxed_nmadd(&mut self) -> Self::Output {
self.check_v128_ternary_op()
}
fn visit_f64x2_relaxed_madd(&mut self) -> Self::Output {
self.check_v128_ternary_op()
}
fn visit_f64x2_relaxed_nmadd(&mut self) -> Self::Output {
self.check_v128_ternary_op()
}
fn visit_i8x16_relaxed_laneselect(&mut self) -> Self::Output {
self.check_v128_ternary_op()
}
fn visit_i16x8_relaxed_laneselect(&mut self) -> Self::Output {
self.check_v128_ternary_op()
}
fn visit_i32x4_relaxed_laneselect(&mut self) -> Self::Output {
self.check_v128_ternary_op()
}
fn visit_i64x2_relaxed_laneselect(&mut self) -> Self::Output {
self.check_v128_ternary_op()
}
fn visit_f32x4_relaxed_min(&mut self) -> Self::Output {
self.check_v128_binary_op()
}
fn visit_f32x4_relaxed_max(&mut self) -> Self::Output {
self.check_v128_binary_op()
}
fn visit_f64x2_relaxed_min(&mut self) -> Self::Output {
self.check_v128_binary_op()
}
fn visit_f64x2_relaxed_max(&mut self) -> Self::Output {
self.check_v128_binary_op()
}
fn visit_i16x8_relaxed_q15mulr_s(&mut self) -> Self::Output {
self.check_v128_binary_op()
}
fn visit_i16x8_relaxed_dot_i8x16_i7x16_s(&mut self) -> Self::Output {
self.check_v128_binary_op()
}
fn visit_i32x4_relaxed_dot_i8x16_i7x16_add_s(&mut self) -> Self::Output {
self.check_v128_ternary_op()
}
fn visit_v128_any_true(&mut self) -> Self::Output {
self.check_v128_bitmask_op()
}
fn visit_i8x16_all_true(&mut self) -> Self::Output {
self.check_v128_bitmask_op()
}
fn visit_i8x16_bitmask(&mut self) -> Self::Output {
self.check_v128_bitmask_op()
}
fn visit_i16x8_all_true(&mut self) -> Self::Output {
self.check_v128_bitmask_op()
}
fn visit_i16x8_bitmask(&mut self) -> Self::Output {
self.check_v128_bitmask_op()
}
fn visit_i32x4_all_true(&mut self) -> Self::Output {
self.check_v128_bitmask_op()
}
fn visit_i32x4_bitmask(&mut self) -> Self::Output {
self.check_v128_bitmask_op()
}
fn visit_i64x2_all_true(&mut self) -> Self::Output {
self.check_v128_bitmask_op()
}
fn visit_i64x2_bitmask(&mut self) -> Self::Output {
self.check_v128_bitmask_op()
}
fn visit_i8x16_shl(&mut self) -> Self::Output {
self.check_v128_shift_op()
}
fn visit_i8x16_shr_s(&mut self) -> Self::Output {
self.check_v128_shift_op()
}
fn visit_i8x16_shr_u(&mut self) -> Self::Output {
self.check_v128_shift_op()
}
fn visit_i16x8_shl(&mut self) -> Self::Output {
self.check_v128_shift_op()
}
fn visit_i16x8_shr_s(&mut self) -> Self::Output {
self.check_v128_shift_op()
}
fn visit_i16x8_shr_u(&mut self) -> Self::Output {
self.check_v128_shift_op()
}
fn visit_i32x4_shl(&mut self) -> Self::Output {
self.check_v128_shift_op()
}
fn visit_i32x4_shr_s(&mut self) -> Self::Output {
self.check_v128_shift_op()
}
fn visit_i32x4_shr_u(&mut self) -> Self::Output {
self.check_v128_shift_op()
}
fn visit_i64x2_shl(&mut self) -> Self::Output {
self.check_v128_shift_op()
}
fn visit_i64x2_shr_s(&mut self) -> Self::Output {
self.check_v128_shift_op()
}
fn visit_i64x2_shr_u(&mut self) -> Self::Output {
self.check_v128_shift_op()
}
fn visit_i8x16_swizzle(&mut self) -> Self::Output {
self.pop_operand(Some(ValType::V128))?;
self.pop_operand(Some(ValType::V128))?;
self.push_operand(ValType::V128)?;
Ok(())
}
fn visit_i8x16_shuffle(&mut self, lanes: [u8; 16]) -> Self::Output {
self.pop_operand(Some(ValType::V128))?;
self.pop_operand(Some(ValType::V128))?;
for i in lanes {
self.check_simd_lane_index(i, 32)?;
}
self.push_operand(ValType::V128)?;
Ok(())
}
fn visit_v128_load8_splat(&mut self, memarg: MemArg) -> Self::Output {
let ty = self.check_memarg(memarg)?;
self.pop_operand(Some(ty))?;
self.push_operand(ValType::V128)?;
Ok(())
}
fn visit_v128_load16_splat(&mut self, memarg: MemArg) -> Self::Output {
let ty = self.check_memarg(memarg)?;
self.pop_operand(Some(ty))?;
self.push_operand(ValType::V128)?;
Ok(())
}
fn visit_v128_load32_splat(&mut self, memarg: MemArg) -> Self::Output {
let ty = self.check_memarg(memarg)?;
self.pop_operand(Some(ty))?;
self.push_operand(ValType::V128)?;
Ok(())
}
fn visit_v128_load32_zero(&mut self, memarg: MemArg) -> Self::Output {
self.visit_v128_load32_splat(memarg)
}
fn visit_v128_load64_splat(&mut self, memarg: MemArg) -> Self::Output {
self.check_v128_load_op(memarg)
}
fn visit_v128_load64_zero(&mut self, memarg: MemArg) -> Self::Output {
self.check_v128_load_op(memarg)
}
fn visit_v128_load8x8_s(&mut self, memarg: MemArg) -> Self::Output {
self.check_v128_load_op(memarg)
}
fn visit_v128_load8x8_u(&mut self, memarg: MemArg) -> Self::Output {
self.check_v128_load_op(memarg)
}
fn visit_v128_load16x4_s(&mut self, memarg: MemArg) -> Self::Output {
self.check_v128_load_op(memarg)
}
fn visit_v128_load16x4_u(&mut self, memarg: MemArg) -> Self::Output {
self.check_v128_load_op(memarg)
}
fn visit_v128_load32x2_s(&mut self, memarg: MemArg) -> Self::Output {
self.check_v128_load_op(memarg)
}
fn visit_v128_load32x2_u(&mut self, memarg: MemArg) -> Self::Output {
self.check_v128_load_op(memarg)
}
fn visit_v128_load8_lane(&mut self, memarg: MemArg, lane: u8) -> Self::Output {
let idx = self.check_memarg(memarg)?;
self.check_simd_lane_index(lane, 16)?;
self.pop_operand(Some(ValType::V128))?;
self.pop_operand(Some(idx))?;
self.push_operand(ValType::V128)?;
Ok(())
}
fn visit_v128_load16_lane(&mut self, memarg: MemArg, lane: u8) -> Self::Output {
let idx = self.check_memarg(memarg)?;
self.check_simd_lane_index(lane, 8)?;
self.pop_operand(Some(ValType::V128))?;
self.pop_operand(Some(idx))?;
self.push_operand(ValType::V128)?;
Ok(())
}
fn visit_v128_load32_lane(&mut self, memarg: MemArg, lane: u8) -> Self::Output {
let idx = self.check_memarg(memarg)?;
self.check_simd_lane_index(lane, 4)?;
self.pop_operand(Some(ValType::V128))?;
self.pop_operand(Some(idx))?;
self.push_operand(ValType::V128)?;
Ok(())
}
fn visit_v128_load64_lane(&mut self, memarg: MemArg, lane: u8) -> Self::Output {
let idx = self.check_memarg(memarg)?;
self.check_simd_lane_index(lane, 2)?;
self.pop_operand(Some(ValType::V128))?;
self.pop_operand(Some(idx))?;
self.push_operand(ValType::V128)?;
Ok(())
}
fn visit_v128_store8_lane(&mut self, memarg: MemArg, lane: u8) -> Self::Output {
let idx = self.check_memarg(memarg)?;
self.check_simd_lane_index(lane, 16)?;
self.pop_operand(Some(ValType::V128))?;
self.pop_operand(Some(idx))?;
Ok(())
}
fn visit_v128_store16_lane(&mut self, memarg: MemArg, lane: u8) -> Self::Output {
let idx = self.check_memarg(memarg)?;
self.check_simd_lane_index(lane, 8)?;
self.pop_operand(Some(ValType::V128))?;
self.pop_operand(Some(idx))?;
Ok(())
}
fn visit_v128_store32_lane(&mut self, memarg: MemArg, lane: u8) -> Self::Output {
let idx = self.check_memarg(memarg)?;
self.check_simd_lane_index(lane, 4)?;
self.pop_operand(Some(ValType::V128))?;
self.pop_operand(Some(idx))?;
Ok(())
}
fn visit_v128_store64_lane(&mut self, memarg: MemArg, lane: u8) -> Self::Output {
let idx = self.check_memarg(memarg)?;
self.check_simd_lane_index(lane, 2)?;
self.pop_operand(Some(ValType::V128))?;
self.pop_operand(Some(idx))?;
Ok(())
}
fn visit_memory_init(&mut self, segment: u32, mem: u32) -> Self::Output {
let ty = self.check_memory_index(mem)?;
match self.resources.data_count() {
None => bail!(self.offset, "data count section required"),
Some(count) if segment < count => {}
Some(_) => bail!(self.offset, "unknown data segment {}", segment),
}
self.pop_operand(Some(ValType::I32))?;
self.pop_operand(Some(ValType::I32))?;
self.pop_operand(Some(ty))?;
Ok(())
}
fn visit_data_drop(&mut self, segment: u32) -> Self::Output {
match self.resources.data_count() {
None => bail!(self.offset, "data count section required"),
Some(count) if segment < count => {}
Some(_) => bail!(self.offset, "unknown data segment {}", segment),
}
Ok(())
}
fn visit_memory_copy(&mut self, dst: u32, src: u32) -> Self::Output {
let dst_ty = self.check_memory_index(dst)?;
let src_ty = self.check_memory_index(src)?;
self.pop_operand(Some(match src_ty {
ValType::I32 => ValType::I32,
_ => dst_ty,
}))?;
self.pop_operand(Some(src_ty))?;
self.pop_operand(Some(dst_ty))?;
Ok(())
}
fn visit_memory_fill(&mut self, mem: u32) -> Self::Output {
let ty = self.check_memory_index(mem)?;
self.pop_operand(Some(ty))?;
self.pop_operand(Some(ValType::I32))?;
self.pop_operand(Some(ty))?;
Ok(())
}
fn visit_memory_discard(&mut self, mem: u32) -> Self::Output {
let ty = self.check_memory_index(mem)?;
self.pop_operand(Some(ty))?;
self.pop_operand(Some(ty))?;
Ok(())
}
fn visit_table_init(&mut self, segment: u32, table: u32) -> Self::Output {
if table > 0 {}
let table = match self.resources.table_at(table) {
Some(table) => table,
None => bail!(
self.offset,
"unknown table {}: table index out of bounds",
table
),
};
let segment_ty = match self.resources.element_type_at(segment) {
Some(ty) => ty,
None => bail!(
self.offset,
"unknown elem segment {}: segment index out of bounds",
segment
),
};
if segment_ty != table.element_type {
bail!(self.offset, "type mismatch");
}
self.pop_operand(Some(ValType::I32))?;
self.pop_operand(Some(ValType::I32))?;
self.pop_operand(Some(ValType::I32))?;
Ok(())
}
fn visit_elem_drop(&mut self, segment: u32) -> Self::Output {
if segment >= self.resources.element_count() {
bail!(
self.offset,
"unknown elem segment {}: segment index out of bounds",
segment
);
}
Ok(())
}
fn visit_table_copy(&mut self, dst_table: u32, src_table: u32) -> Self::Output {
if src_table > 0 || dst_table > 0 {}
let (src, dst) = match (
self.resources.table_at(src_table),
self.resources.table_at(dst_table),
) {
(Some(a), Some(b)) => (a, b),
_ => bail!(self.offset, "table index out of bounds"),
};
if !self.resources.matches(
ValType::Ref(src.element_type),
ValType::Ref(dst.element_type),
) {
bail!(self.offset, "type mismatch");
}
self.pop_operand(Some(ValType::I32))?;
self.pop_operand(Some(ValType::I32))?;
self.pop_operand(Some(ValType::I32))?;
Ok(())
}
fn visit_table_get(&mut self, table: u32) -> Self::Output {
let ty = match self.resources.table_at(table) {
Some(ty) => ty.element_type,
None => bail!(self.offset, "table index out of bounds"),
};
self.pop_operand(Some(ValType::I32))?;
self.push_operand(ValType::Ref(ty))?;
Ok(())
}
fn visit_table_set(&mut self, table: u32) -> Self::Output {
let ty = match self.resources.table_at(table) {
Some(ty) => ty.element_type,
None => bail!(self.offset, "table index out of bounds"),
};
self.pop_operand(Some(ValType::Ref(ty)))?;
self.pop_operand(Some(ValType::I32))?;
Ok(())
}
fn visit_table_grow(&mut self, table: u32) -> Self::Output {
let ty = match self.resources.table_at(table) {
Some(ty) => ty.element_type,
None => bail!(self.offset, "table index out of bounds"),
};
self.pop_operand(Some(ValType::I32))?;
self.pop_operand(Some(ValType::Ref(ty)))?;
self.push_operand(ValType::I32)?;
Ok(())
}
fn visit_table_size(&mut self, table: u32) -> Self::Output {
if self.resources.table_at(table).is_none() {
bail!(self.offset, "table index out of bounds");
}
self.push_operand(ValType::I32)?;
Ok(())
}
fn visit_table_fill(&mut self, table: u32) -> Self::Output {
let ty = match self.resources.table_at(table) {
Some(ty) => ty.element_type,
None => bail!(self.offset, "table index out of bounds"),
};
self.pop_operand(Some(ValType::I32))?;
self.pop_operand(Some(ValType::Ref(ty)))?;
self.pop_operand(Some(ValType::I32))?;
Ok(())
}
}
#[derive(Clone)]
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(),
}
}
}
impl<A, B> ExactSizeIterator for Either<A, B>
where
A: ExactSizeIterator,
B: ExactSizeIterator<Item = A::Item>,
{
fn len(&self) -> usize {
match self {
Either::A(a) => a.len(),
Either::B(b) => b.len(),
}
}
}
trait PreciseIterator: ExactSizeIterator + DoubleEndedIterator + Clone {}
impl<T: ExactSizeIterator + DoubleEndedIterator + Clone> PreciseIterator for T {}
impl Locals {
fn define(&mut self, count: u32, ty: ValType) -> bool {
match self.num_locals.checked_add(count) {
Some(n) => self.num_locals = n,
None => return false,
}
if self.num_locals > (MAX_WASM_FUNCTION_LOCALS as u32) {
return false;
}
for _ in 0..count {
if self.first.len() >= MAX_LOCALS_TO_TRACK {
break;
}
self.first.push(ty);
}
self.all.push((self.num_locals - 1, ty));
true
}
pub(super) fn len_locals(&self) -> u32 {
self.num_locals
}
#[inline]
pub(super) fn get(&self, idx: u32) -> Option<ValType> {
match self.first.get(idx as usize) {
Some(ty) => Some(*ty),
None => self.get_bsearch(idx),
}
}
fn get_bsearch(&self, idx: u32) -> Option<ValType> {
match self.all.binary_search_by_key(&idx, |(idx, _)| *idx) {
Err(i) if i == self.all.len() => None,
Ok(i) | Err(i) => Some(self.all[i].1),
}
}
}