use crate::limits::MAX_WASM_FUNCTION_LOCALS;
use crate::primitives::{
EventType, MemoryImmediate, Operator, SIMDLaneIndex, Type, TypeOrFuncType,
};
use crate::{BinaryReaderError, Result, WasmFeatures, WasmFuncType, WasmModuleResources};
pub(crate) struct OperatorValidatorError(pub(crate) BinaryReaderError);
macro_rules! format_op_err {
( $( $arg:expr ),* $(,)* ) => {
OperatorValidatorError::new(format!( $( $arg ),* ))
}
}
macro_rules! bail_op_err {
( $( $arg:expr ),* $(,)* ) => {
return Err(format_op_err!( $( $arg ),* ));
}
}
impl OperatorValidatorError {
pub(crate) fn new(message: impl Into<String>) -> Self {
let offset = std::usize::MAX;
let e = BinaryReaderError::new(message, offset);
OperatorValidatorError(e)
}
pub(crate) fn set_offset(mut self, offset: usize) -> BinaryReaderError {
debug_assert_eq!(self.0.inner.offset, std::usize::MAX);
self.0.inner.offset = offset;
self.0
}
}
type OperatorValidatorResult<T> = std::result::Result<T, OperatorValidatorError>;
pub(crate) struct OperatorValidator {
num_locals: u32,
locals: Vec<(u32, Type)>,
pub(crate) operands: Vec<Option<Type>>,
control: Vec<Frame>,
features: WasmFeatures,
}
struct Frame {
kind: FrameKind,
block_type: TypeOrFuncType,
height: usize,
unreachable: bool,
}
#[derive(PartialEq, Copy, Clone)]
enum FrameKind {
Block,
If,
Else,
Loop,
Try,
Catch,
CatchAll,
Unwind,
}
impl OperatorValidator {
pub fn new(
ty: u32,
offset: usize,
features: &WasmFeatures,
resources: &impl WasmModuleResources,
) -> Result<OperatorValidator> {
let locals = func_type_at(resources, ty)
.map_err(|e| e.set_offset(offset))?
.inputs()
.enumerate()
.map(|(i, ty)| (i as u32, ty))
.collect::<Vec<_>>();
Ok(OperatorValidator {
num_locals: locals.len() as u32,
locals,
operands: Vec::new(),
control: vec![Frame {
kind: FrameKind::Block,
block_type: TypeOrFuncType::FuncType(ty),
height: 0,
unreachable: false,
}],
features: *features,
})
}
pub fn define_locals(&mut self, offset: usize, count: u32, ty: Type) -> Result<()> {
self.features
.check_value_type(ty)
.map_err(|e| BinaryReaderError::new(e, offset))?;
if count == 0 {
return Ok(());
}
match self.num_locals.checked_add(count) {
Some(n) => self.num_locals = n,
None => return Err(BinaryReaderError::new("locals overflow", offset)),
}
if self.num_locals > (MAX_WASM_FUNCTION_LOCALS as u32) {
return Err(BinaryReaderError::new("locals exceed maximum", offset));
}
self.locals.push((self.num_locals - 1, ty));
Ok(())
}
fn local(&self, idx: u32) -> OperatorValidatorResult<Type> {
match self.locals.binary_search_by_key(&idx, |(idx, _)| *idx) {
Err(i) if i == self.locals.len() => {
bail_op_err!("unknown local {}: local index out of bounds", idx)
}
Ok(i) | Err(i) => Ok(self.locals[i].1),
}
}
fn push_operand(&mut self, ty: Type) -> OperatorValidatorResult<()> {
self.features
.check_value_type(ty)
.map_err(OperatorValidatorError::new)?;
self.operands.push(Some(ty));
Ok(())
}
fn pop_operand(&mut self, expected: Option<Type>) -> OperatorValidatorResult<Option<Type>> {
let control = self.control.last().unwrap();
let actual = if self.operands.len() == control.height {
if control.unreachable {
None
} else {
let desc = match expected {
Some(ty) => ty_to_str(ty),
None => "a type",
};
bail_op_err!("type mismatch: expected {} but nothing on stack", desc)
}
} else {
self.operands.pop().unwrap()
};
let actual_ty = match actual {
Some(ty) => ty,
None => return Ok(expected),
};
let expected_ty = match expected {
Some(ty) => ty,
None => return Ok(actual),
};
if actual_ty != expected_ty {
bail_op_err!(
"type mismatch: expected {}, found {}",
ty_to_str(expected_ty),
ty_to_str(actual_ty)
)
} else {
Ok(actual)
}
}
fn unreachable(&mut self) {
let control = self.control.last_mut().unwrap();
self.operands.truncate(control.height);
control.unreachable = true;
}
fn push_ctrl(
&mut self,
kind: FrameKind,
ty: TypeOrFuncType,
resources: &impl WasmModuleResources,
) -> OperatorValidatorResult<()> {
self.control.push(Frame {
kind,
block_type: ty,
height: self.operands.len(),
unreachable: false,
});
for ty in params(ty, resources)? {
self.push_operand(ty)?;
}
Ok(())
}
fn pop_ctrl(&mut self, resources: &impl WasmModuleResources) -> OperatorValidatorResult<Frame> {
let frame = self.control.last().unwrap();
let ty = frame.block_type;
let height = frame.height;
for ty in results(ty, resources)?.rev() {
self.pop_operand(Some(ty))?;
}
if self.operands.len() != height {
bail_op_err!("type mismatch: values remaining on stack at end of block");
}
Ok(self.control.pop().unwrap())
}
fn jump(&self, depth: u32) -> OperatorValidatorResult<(TypeOrFuncType, FrameKind)> {
match (self.control.len() - 1).checked_sub(depth as usize) {
Some(i) => {
let frame = &self.control[i];
Ok((frame.block_type, frame.kind))
}
None => bail_op_err!("unknown label: branch depth too large"),
}
}
fn check_memory_index(
&self,
memory_index: u32,
resources: impl WasmModuleResources,
) -> OperatorValidatorResult<Type> {
if memory_index > 0 && !self.features.multi_memory {
return Err(OperatorValidatorError::new(
"multi-memory support is not enabled",
));
}
match resources.memory_at(memory_index) {
Some(mem) => Ok(mem.index_type()),
None => bail_op_err!("unknown memory {}", memory_index),
}
}
fn check_memarg(
&self,
memarg: MemoryImmediate,
max_align: u8,
resources: impl WasmModuleResources,
) -> OperatorValidatorResult<Type> {
let index_ty = self.check_memory_index(memarg.memory, resources)?;
let align = memarg.align;
if align > max_align {
return Err(OperatorValidatorError::new(
"alignment must not be larger than natural",
));
}
Ok(index_ty)
}
#[cfg(feature = "deterministic")]
fn check_non_deterministic_enabled(&self) -> OperatorValidatorResult<()> {
if !self.features.deterministic_only {
return Err(OperatorValidatorError::new(
"deterministic_only support is not enabled",
));
}
Ok(())
}
#[inline(always)]
#[cfg(not(feature = "deterministic"))]
fn check_non_deterministic_enabled(&self) -> OperatorValidatorResult<()> {
Ok(())
}
fn check_threads_enabled(&self) -> OperatorValidatorResult<()> {
if !self.features.threads {
return Err(OperatorValidatorError::new(
"threads support is not enabled",
));
}
Ok(())
}
fn check_reference_types_enabled(&self) -> OperatorValidatorResult<()> {
if !self.features.reference_types {
return Err(OperatorValidatorError::new(
"reference types support is not enabled",
));
}
Ok(())
}
fn check_simd_enabled(&self) -> OperatorValidatorResult<()> {
if !self.features.simd {
return Err(OperatorValidatorError::new("SIMD support is not enabled"));
}
Ok(())
}
fn check_exceptions_enabled(&self) -> OperatorValidatorResult<()> {
if !self.features.exceptions {
return Err(OperatorValidatorError::new(
"Exceptions support is not enabled",
));
}
Ok(())
}
fn check_bulk_memory_enabled(&self) -> OperatorValidatorResult<()> {
if !self.features.bulk_memory {
return Err(OperatorValidatorError::new(
"bulk memory support is not enabled",
));
}
Ok(())
}
fn check_shared_memarg_wo_align(
&self,
memarg: MemoryImmediate,
resources: impl WasmModuleResources,
) -> OperatorValidatorResult<Type> {
self.check_memory_index(memarg.memory, resources)
}
fn check_simd_lane_index(&self, index: SIMDLaneIndex, max: u8) -> OperatorValidatorResult<()> {
if index >= max {
return Err(OperatorValidatorError::new("SIMD index out of bounds"));
}
Ok(())
}
fn check_block_type(
&self,
ty: TypeOrFuncType,
resources: impl WasmModuleResources,
) -> OperatorValidatorResult<()> {
match ty {
TypeOrFuncType::Type(Type::EmptyBlockType)
| TypeOrFuncType::Type(Type::I32)
| TypeOrFuncType::Type(Type::I64)
| TypeOrFuncType::Type(Type::F32)
| TypeOrFuncType::Type(Type::F64) => Ok(()),
TypeOrFuncType::Type(Type::ExternRef) | TypeOrFuncType::Type(Type::FuncRef) => {
self.check_reference_types_enabled()
}
TypeOrFuncType::Type(Type::V128) => self.check_simd_enabled(),
TypeOrFuncType::FuncType(idx) => {
let ty = func_type_at(&resources, idx)?;
if !self.features.multi_value {
if ty.len_outputs() > 1 {
return Err(OperatorValidatorError::new(
"blocks, loops, and ifs may only return at most one \
value when multi-value is not enabled",
));
}
if ty.len_inputs() > 0 {
return Err(OperatorValidatorError::new(
"blocks, loops, and ifs accept no parameters \
when multi-value is not enabled",
));
}
}
Ok(())
}
_ => Err(OperatorValidatorError::new("invalid block return type")),
}
}
fn check_call(
&mut self,
function_index: u32,
resources: &impl WasmModuleResources,
) -> OperatorValidatorResult<()> {
let ty = match resources.type_of_function(function_index) {
Some(i) => i,
None => {
bail_op_err!(
"unknown function {}: function index out of bounds",
function_index
);
}
};
for ty in ty.inputs().rev() {
self.pop_operand(Some(ty))?;
}
for ty in ty.outputs() {
self.push_operand(ty)?;
}
Ok(())
}
fn check_call_indirect(
&mut self,
index: u32,
table_index: u32,
resources: &impl WasmModuleResources,
) -> OperatorValidatorResult<()> {
match resources.table_at(table_index) {
None => {
return Err(OperatorValidatorError::new(
"unknown table: table index out of bounds",
));
}
Some(tab) => {
if tab.element_type != Type::FuncRef {
return Err(OperatorValidatorError::new(
"indirect calls must go through a table of funcref",
));
}
}
}
let ty = func_type_at(&resources, index)?;
self.pop_operand(Some(Type::I32))?;
for ty in ty.inputs().rev() {
self.pop_operand(Some(ty))?;
}
for ty in ty.outputs() {
self.push_operand(ty)?;
}
Ok(())
}
fn check_return(
&mut self,
resources: &impl WasmModuleResources,
) -> OperatorValidatorResult<()> {
for ty in results(self.control[0].block_type, resources)?.rev() {
self.pop_operand(Some(ty))?;
}
self.unreachable();
Ok(())
}
pub fn process_operator(
&mut self,
operator: &Operator,
resources: &impl WasmModuleResources,
) -> OperatorValidatorResult<()> {
if self.control.len() == 0 {
bail_op_err!("operators remaining after end of function");
}
match *operator {
Operator::Nop => {}
Operator::Unreachable => self.unreachable(),
Operator::Block { ty } => {
self.check_block_type(ty, resources)?;
for ty in params(ty, resources)?.rev() {
self.pop_operand(Some(ty))?;
}
self.push_ctrl(FrameKind::Block, ty, resources)?;
}
Operator::Loop { ty } => {
self.check_block_type(ty, resources)?;
for ty in params(ty, resources)?.rev() {
self.pop_operand(Some(ty))?;
}
self.push_ctrl(FrameKind::Loop, ty, resources)?;
}
Operator::If { ty } => {
self.check_block_type(ty, resources)?;
self.pop_operand(Some(Type::I32))?;
for ty in params(ty, resources)?.rev() {
self.pop_operand(Some(ty))?;
}
self.push_ctrl(FrameKind::If, ty, resources)?;
}
Operator::Else => {
let frame = self.pop_ctrl(resources)?;
match frame.kind {
FrameKind::If => {
self.push_ctrl(FrameKind::Else, frame.block_type, resources)?
}
FrameKind::Try | FrameKind::Catch => {
self.control.push(Frame {
kind: FrameKind::CatchAll,
block_type: frame.block_type,
height: self.operands.len(),
unreachable: false,
});
}
_ => bail_op_err!("else found outside of an `if` block"),
}
}
Operator::Try { ty } => {
self.check_exceptions_enabled()?;
self.check_block_type(ty, resources)?;
for ty in params(ty, resources)?.rev() {
self.pop_operand(Some(ty))?;
}
self.push_ctrl(FrameKind::Try, ty, resources)?;
}
Operator::Catch { index } => {
self.check_exceptions_enabled()?;
let frame = self.pop_ctrl(resources)?;
if frame.kind != FrameKind::Try && frame.kind != FrameKind::Catch {
bail_op_err!("catch found outside of an `try` block");
}
self.control.push(Frame {
kind: FrameKind::Catch,
block_type: frame.block_type,
height: self.operands.len(),
unreachable: false,
});
let event_ty = event_at(&resources, index)?;
let ty = func_type_at(&resources, event_ty.type_index)?;
for ty in ty.inputs() {
self.push_operand(ty)?;
}
}
Operator::Throw { index } => {
self.check_exceptions_enabled()?;
let event_ty = event_at(&resources, index)?;
let ty = func_type_at(&resources, event_ty.type_index)?;
for ty in ty.inputs().rev() {
self.pop_operand(Some(ty))?;
}
if ty.outputs().len() > 0 {
bail_op_err!("result type expected to be empty for exception");
}
self.unreachable();
}
Operator::Rethrow { relative_depth } => {
self.check_exceptions_enabled()?;
let (_, kind) = self.jump(relative_depth)?;
if kind != FrameKind::Catch && kind != FrameKind::CatchAll {
bail_op_err!("rethrow target was not a `catch` block");
}
self.unreachable();
}
Operator::Unwind => {
self.check_exceptions_enabled()?;
let frame = self.pop_ctrl(resources)?;
if frame.kind != FrameKind::Try {
bail_op_err!("unwind found outside of an `try` block");
}
self.control.push(Frame {
kind: FrameKind::Unwind,
block_type: TypeOrFuncType::Type(Type::EmptyBlockType),
height: self.operands.len(),
unreachable: false,
});
}
Operator::End => {
let mut frame = self.pop_ctrl(resources)?;
match frame.kind {
FrameKind::If => {
self.push_ctrl(FrameKind::Else, frame.block_type, resources)?;
frame = self.pop_ctrl(resources)?;
}
FrameKind::Try => {
bail_op_err!("expected catch block");
}
_ => (),
}
for ty in results(frame.block_type, resources)? {
self.push_operand(ty)?;
}
}
Operator::Br { relative_depth } => {
let (ty, kind) = self.jump(relative_depth)?;
for ty in label_types(ty, resources, kind)?.rev() {
self.pop_operand(Some(ty))?;
}
self.unreachable();
}
Operator::BrIf { relative_depth } => {
self.pop_operand(Some(Type::I32))?;
let (ty, kind) = self.jump(relative_depth)?;
for ty in label_types(ty, resources, kind)?.rev() {
self.pop_operand(Some(ty))?;
}
for ty in label_types(ty, resources, kind)? {
self.push_operand(ty)?;
}
}
Operator::BrTable { ref table } => {
self.pop_operand(Some(Type::I32))?;
let mut label = None;
for element in table.targets() {
let (relative_depth, _is_default) = element.map_err(|mut e| {
e.inner.offset = usize::max_value();
OperatorValidatorError(e)
})?;
let block = self.jump(relative_depth)?;
match label {
None => label = Some(block),
Some(prev) => {
let a = label_types(block.0, resources, block.1)?;
let b = label_types(prev.0, resources, prev.1)?;
if a.ne(b) {
bail_op_err!(
"type mismatch: br_table target labels have different types"
);
}
}
}
}
let (ty, kind) = label.unwrap();
for ty in label_types(ty, resources, kind)?.rev() {
self.pop_operand(Some(ty))?;
}
self.unreachable();
}
Operator::Return => self.check_return(resources)?,
Operator::Call { function_index } => self.check_call(function_index, resources)?,
Operator::ReturnCall { function_index } => {
if !self.features.tail_call {
return Err(OperatorValidatorError::new(
"tail calls support is not enabled",
));
}
self.check_call(function_index, resources)?;
self.check_return(resources)?;
}
Operator::CallIndirect { index, table_index } => {
self.check_call_indirect(index, table_index, resources)?
}
Operator::ReturnCallIndirect { index, table_index } => {
if !self.features.tail_call {
return Err(OperatorValidatorError::new(
"tail calls support is not enabled",
));
}
self.check_call_indirect(index, table_index, resources)?;
self.check_return(resources)?;
}
Operator::Drop => {
self.pop_operand(None)?;
}
Operator::Select => {
self.pop_operand(Some(Type::I32))?;
let ty = self.pop_operand(None)?;
match self.pop_operand(ty)? {
ty @ Some(Type::I32)
| ty @ Some(Type::I64)
| ty @ Some(Type::F32)
| ty @ Some(Type::F64) => self.operands.push(ty),
ty @ Some(Type::V128) => {
self.check_simd_enabled()?;
self.operands.push(ty)
}
None => self.operands.push(None),
Some(_) => bail_op_err!("type mismatch: select only takes integral types"),
}
}
Operator::TypedSelect { ty } => {
self.pop_operand(Some(Type::I32))?;
self.pop_operand(Some(ty))?;
self.pop_operand(Some(ty))?;
self.push_operand(ty)?;
}
Operator::LocalGet { local_index } => {
let ty = self.local(local_index)?;
self.push_operand(ty)?;
}
Operator::LocalSet { local_index } => {
let ty = self.local(local_index)?;
self.pop_operand(Some(ty))?;
}
Operator::LocalTee { local_index } => {
let ty = self.local(local_index)?;
self.pop_operand(Some(ty))?;
self.push_operand(ty)?;
}
Operator::GlobalGet { global_index } => {
if let Some(ty) = resources.global_at(global_index) {
self.push_operand(ty.content_type)?;
} else {
return Err(OperatorValidatorError::new(
"unknown global: global index out of bounds",
));
};
}
Operator::GlobalSet { global_index } => {
if let Some(ty) = resources.global_at(global_index) {
if !ty.mutable {
return Err(OperatorValidatorError::new(
"global is immutable: cannot modify it with `global.set`",
));
}
self.pop_operand(Some(ty.content_type))?;
} else {
return Err(OperatorValidatorError::new(
"unknown global: global index out of bounds",
));
};
}
Operator::I32Load { memarg } => {
let ty = self.check_memarg(memarg, 2, resources)?;
self.pop_operand(Some(ty))?;
self.push_operand(Type::I32)?;
}
Operator::I64Load { memarg } => {
let ty = self.check_memarg(memarg, 3, resources)?;
self.pop_operand(Some(ty))?;
self.push_operand(Type::I64)?;
}
Operator::F32Load { memarg } => {
self.check_non_deterministic_enabled()?;
let ty = self.check_memarg(memarg, 2, resources)?;
self.pop_operand(Some(ty))?;
self.push_operand(Type::F32)?;
}
Operator::F64Load { memarg } => {
self.check_non_deterministic_enabled()?;
let ty = self.check_memarg(memarg, 3, resources)?;
self.pop_operand(Some(ty))?;
self.push_operand(Type::F64)?;
}
Operator::I32Load8S { memarg } | Operator::I32Load8U { memarg } => {
let ty = self.check_memarg(memarg, 0, resources)?;
self.pop_operand(Some(ty))?;
self.push_operand(Type::I32)?;
}
Operator::I32Load16S { memarg } | Operator::I32Load16U { memarg } => {
let ty = self.check_memarg(memarg, 1, resources)?;
self.pop_operand(Some(ty))?;
self.push_operand(Type::I32)?;
}
Operator::I64Load8S { memarg } | Operator::I64Load8U { memarg } => {
let ty = self.check_memarg(memarg, 0, resources)?;
self.pop_operand(Some(ty))?;
self.push_operand(Type::I64)?;
}
Operator::I64Load16S { memarg } | Operator::I64Load16U { memarg } => {
let ty = self.check_memarg(memarg, 1, resources)?;
self.pop_operand(Some(ty))?;
self.push_operand(Type::I64)?;
}
Operator::I64Load32S { memarg } | Operator::I64Load32U { memarg } => {
let ty = self.check_memarg(memarg, 2, resources)?;
self.pop_operand(Some(ty))?;
self.push_operand(Type::I64)?;
}
Operator::I32Store { memarg } => {
let ty = self.check_memarg(memarg, 2, resources)?;
self.pop_operand(Some(Type::I32))?;
self.pop_operand(Some(ty))?;
}
Operator::I64Store { memarg } => {
let ty = self.check_memarg(memarg, 3, resources)?;
self.pop_operand(Some(Type::I64))?;
self.pop_operand(Some(ty))?;
}
Operator::F32Store { memarg } => {
self.check_non_deterministic_enabled()?;
let ty = self.check_memarg(memarg, 2, resources)?;
self.pop_operand(Some(Type::F32))?;
self.pop_operand(Some(ty))?;
}
Operator::F64Store { memarg } => {
self.check_non_deterministic_enabled()?;
let ty = self.check_memarg(memarg, 3, resources)?;
self.pop_operand(Some(Type::F64))?;
self.pop_operand(Some(ty))?;
}
Operator::I32Store8 { memarg } => {
let ty = self.check_memarg(memarg, 0, resources)?;
self.pop_operand(Some(Type::I32))?;
self.pop_operand(Some(ty))?;
}
Operator::I32Store16 { memarg } => {
let ty = self.check_memarg(memarg, 1, resources)?;
self.pop_operand(Some(Type::I32))?;
self.pop_operand(Some(ty))?;
}
Operator::I64Store8 { memarg } => {
let ty = self.check_memarg(memarg, 0, resources)?;
self.pop_operand(Some(Type::I64))?;
self.pop_operand(Some(ty))?;
}
Operator::I64Store16 { memarg } => {
let ty = self.check_memarg(memarg, 1, resources)?;
self.pop_operand(Some(Type::I64))?;
self.pop_operand(Some(ty))?;
}
Operator::I64Store32 { memarg } => {
let ty = self.check_memarg(memarg, 2, resources)?;
self.pop_operand(Some(Type::I64))?;
self.pop_operand(Some(ty))?;
}
Operator::MemorySize { mem, mem_byte } => {
if mem_byte != 0 && !self.features.multi_memory {
return Err(OperatorValidatorError::new("multi-memory not enabled"));
}
let index_ty = self.check_memory_index(mem, resources)?;
self.push_operand(index_ty)?;
}
Operator::MemoryGrow { mem, mem_byte } => {
if mem_byte != 0 && !self.features.multi_memory {
return Err(OperatorValidatorError::new("multi-memory not enabled"));
}
let index_ty = self.check_memory_index(mem, resources)?;
self.pop_operand(Some(index_ty))?;
self.push_operand(index_ty)?;
}
Operator::I32Const { .. } => self.push_operand(Type::I32)?,
Operator::I64Const { .. } => self.push_operand(Type::I64)?,
Operator::F32Const { .. } => {
self.check_non_deterministic_enabled()?;
self.push_operand(Type::F32)?;
}
Operator::F64Const { .. } => {
self.check_non_deterministic_enabled()?;
self.push_operand(Type::F64)?;
}
Operator::I32Eqz => {
self.pop_operand(Some(Type::I32))?;
self.push_operand(Type::I32)?;
}
Operator::I32Eq
| Operator::I32Ne
| Operator::I32LtS
| Operator::I32LtU
| Operator::I32GtS
| Operator::I32GtU
| Operator::I32LeS
| Operator::I32LeU
| Operator::I32GeS
| Operator::I32GeU => {
self.pop_operand(Some(Type::I32))?;
self.pop_operand(Some(Type::I32))?;
self.push_operand(Type::I32)?;
}
Operator::I64Eqz => {
self.pop_operand(Some(Type::I64))?;
self.push_operand(Type::I32)?;
}
Operator::I64Eq
| Operator::I64Ne
| Operator::I64LtS
| Operator::I64LtU
| Operator::I64GtS
| Operator::I64GtU
| Operator::I64LeS
| Operator::I64LeU
| Operator::I64GeS
| Operator::I64GeU => {
self.pop_operand(Some(Type::I64))?;
self.pop_operand(Some(Type::I64))?;
self.push_operand(Type::I32)?;
}
Operator::F32Eq
| Operator::F32Ne
| Operator::F32Lt
| Operator::F32Gt
| Operator::F32Le
| Operator::F32Ge => {
self.check_non_deterministic_enabled()?;
self.pop_operand(Some(Type::F32))?;
self.pop_operand(Some(Type::F32))?;
self.push_operand(Type::I32)?;
}
Operator::F64Eq
| Operator::F64Ne
| Operator::F64Lt
| Operator::F64Gt
| Operator::F64Le
| Operator::F64Ge => {
self.check_non_deterministic_enabled()?;
self.pop_operand(Some(Type::F64))?;
self.pop_operand(Some(Type::F64))?;
self.push_operand(Type::I32)?;
}
Operator::I32Clz | Operator::I32Ctz | Operator::I32Popcnt => {
self.pop_operand(Some(Type::I32))?;
self.push_operand(Type::I32)?;
}
Operator::I32Add
| Operator::I32Sub
| Operator::I32Mul
| Operator::I32DivS
| Operator::I32DivU
| Operator::I32RemS
| Operator::I32RemU
| Operator::I32And
| Operator::I32Or
| Operator::I32Xor
| Operator::I32Shl
| Operator::I32ShrS
| Operator::I32ShrU
| Operator::I32Rotl
| Operator::I32Rotr => {
self.pop_operand(Some(Type::I32))?;
self.pop_operand(Some(Type::I32))?;
self.push_operand(Type::I32)?;
}
Operator::I64Clz | Operator::I64Ctz | Operator::I64Popcnt => {
self.pop_operand(Some(Type::I64))?;
self.push_operand(Type::I64)?;
}
Operator::I64Add
| Operator::I64Sub
| Operator::I64Mul
| Operator::I64DivS
| Operator::I64DivU
| Operator::I64RemS
| Operator::I64RemU
| Operator::I64And
| Operator::I64Or
| Operator::I64Xor
| Operator::I64Shl
| Operator::I64ShrS
| Operator::I64ShrU
| Operator::I64Rotl
| Operator::I64Rotr => {
self.pop_operand(Some(Type::I64))?;
self.pop_operand(Some(Type::I64))?;
self.push_operand(Type::I64)?;
}
Operator::F32Abs
| Operator::F32Neg
| Operator::F32Ceil
| Operator::F32Floor
| Operator::F32Trunc
| Operator::F32Nearest
| Operator::F32Sqrt => {
self.check_non_deterministic_enabled()?;
self.pop_operand(Some(Type::F32))?;
self.push_operand(Type::F32)?;
}
Operator::F32Add
| Operator::F32Sub
| Operator::F32Mul
| Operator::F32Div
| Operator::F32Min
| Operator::F32Max
| Operator::F32Copysign => {
self.check_non_deterministic_enabled()?;
self.pop_operand(Some(Type::F32))?;
self.pop_operand(Some(Type::F32))?;
self.push_operand(Type::F32)?;
}
Operator::F64Abs
| Operator::F64Neg
| Operator::F64Ceil
| Operator::F64Floor
| Operator::F64Trunc
| Operator::F64Nearest
| Operator::F64Sqrt => {
self.check_non_deterministic_enabled()?;
self.pop_operand(Some(Type::F64))?;
self.push_operand(Type::F64)?;
}
Operator::F64Add
| Operator::F64Sub
| Operator::F64Mul
| Operator::F64Div
| Operator::F64Min
| Operator::F64Max
| Operator::F64Copysign => {
self.check_non_deterministic_enabled()?;
self.pop_operand(Some(Type::F64))?;
self.pop_operand(Some(Type::F64))?;
self.push_operand(Type::F64)?;
}
Operator::I32WrapI64 => {
self.pop_operand(Some(Type::I64))?;
self.push_operand(Type::I32)?;
}
Operator::I32TruncF32S | Operator::I32TruncF32U => {
self.pop_operand(Some(Type::F32))?;
self.push_operand(Type::I32)?;
}
Operator::I32TruncF64S | Operator::I32TruncF64U => {
self.pop_operand(Some(Type::F64))?;
self.push_operand(Type::I32)?;
}
Operator::I64ExtendI32S | Operator::I64ExtendI32U => {
self.pop_operand(Some(Type::I32))?;
self.push_operand(Type::I64)?;
}
Operator::I64TruncF32S | Operator::I64TruncF32U => {
self.pop_operand(Some(Type::F32))?;
self.push_operand(Type::I64)?;
}
Operator::I64TruncF64S | Operator::I64TruncF64U => {
self.pop_operand(Some(Type::F64))?;
self.push_operand(Type::I64)?;
}
Operator::F32ConvertI32S | Operator::F32ConvertI32U => {
self.check_non_deterministic_enabled()?;
self.pop_operand(Some(Type::I32))?;
self.push_operand(Type::F32)?;
}
Operator::F32ConvertI64S | Operator::F32ConvertI64U => {
self.check_non_deterministic_enabled()?;
self.pop_operand(Some(Type::I64))?;
self.push_operand(Type::F32)?;
}
Operator::F32DemoteF64 => {
self.check_non_deterministic_enabled()?;
self.pop_operand(Some(Type::F64))?;
self.push_operand(Type::F32)?;
}
Operator::F64ConvertI32S | Operator::F64ConvertI32U => {
self.check_non_deterministic_enabled()?;
self.pop_operand(Some(Type::I32))?;
self.push_operand(Type::F64)?;
}
Operator::F64ConvertI64S | Operator::F64ConvertI64U => {
self.check_non_deterministic_enabled()?;
self.pop_operand(Some(Type::I64))?;
self.push_operand(Type::F64)?;
}
Operator::F64PromoteF32 => {
self.check_non_deterministic_enabled()?;
self.pop_operand(Some(Type::F32))?;
self.push_operand(Type::F64)?;
}
Operator::I32ReinterpretF32 => {
self.pop_operand(Some(Type::F32))?;
self.push_operand(Type::I32)?;
}
Operator::I64ReinterpretF64 => {
self.pop_operand(Some(Type::F64))?;
self.push_operand(Type::I64)?;
}
Operator::F32ReinterpretI32 => {
self.check_non_deterministic_enabled()?;
self.pop_operand(Some(Type::I32))?;
self.push_operand(Type::F32)?;
}
Operator::F64ReinterpretI64 => {
self.check_non_deterministic_enabled()?;
self.pop_operand(Some(Type::I64))?;
self.push_operand(Type::F64)?;
}
Operator::I32TruncSatF32S | Operator::I32TruncSatF32U => {
self.pop_operand(Some(Type::F32))?;
self.push_operand(Type::I32)?;
}
Operator::I32TruncSatF64S | Operator::I32TruncSatF64U => {
self.pop_operand(Some(Type::F64))?;
self.push_operand(Type::I32)?;
}
Operator::I64TruncSatF32S | Operator::I64TruncSatF32U => {
self.pop_operand(Some(Type::F32))?;
self.push_operand(Type::I64)?;
}
Operator::I64TruncSatF64S | Operator::I64TruncSatF64U => {
self.pop_operand(Some(Type::F64))?;
self.push_operand(Type::I64)?;
}
Operator::I32Extend16S | Operator::I32Extend8S => {
self.pop_operand(Some(Type::I32))?;
self.push_operand(Type::I32)?;
}
Operator::I64Extend32S | Operator::I64Extend16S | Operator::I64Extend8S => {
self.pop_operand(Some(Type::I64))?;
self.push_operand(Type::I64)?;
}
Operator::I32AtomicLoad { memarg }
| Operator::I32AtomicLoad16U { memarg }
| Operator::I32AtomicLoad8U { memarg } => {
self.check_threads_enabled()?;
let ty = self.check_shared_memarg_wo_align(memarg, resources)?;
self.pop_operand(Some(ty))?;
self.push_operand(Type::I32)?;
}
Operator::I64AtomicLoad { memarg }
| Operator::I64AtomicLoad32U { memarg }
| Operator::I64AtomicLoad16U { memarg }
| Operator::I64AtomicLoad8U { memarg } => {
self.check_threads_enabled()?;
let ty = self.check_shared_memarg_wo_align(memarg, resources)?;
self.pop_operand(Some(ty))?;
self.push_operand(Type::I64)?;
}
Operator::I32AtomicStore { memarg }
| Operator::I32AtomicStore16 { memarg }
| Operator::I32AtomicStore8 { memarg } => {
self.check_threads_enabled()?;
let ty = self.check_shared_memarg_wo_align(memarg, resources)?;
self.pop_operand(Some(Type::I32))?;
self.pop_operand(Some(ty))?;
}
Operator::I64AtomicStore { memarg }
| Operator::I64AtomicStore32 { memarg }
| Operator::I64AtomicStore16 { memarg }
| Operator::I64AtomicStore8 { memarg } => {
self.check_threads_enabled()?;
let ty = self.check_shared_memarg_wo_align(memarg, resources)?;
self.pop_operand(Some(Type::I64))?;
self.pop_operand(Some(ty))?;
}
Operator::I32AtomicRmwAdd { memarg }
| Operator::I32AtomicRmwSub { memarg }
| Operator::I32AtomicRmwAnd { memarg }
| Operator::I32AtomicRmwOr { memarg }
| Operator::I32AtomicRmwXor { memarg }
| Operator::I32AtomicRmw16AddU { memarg }
| Operator::I32AtomicRmw16SubU { memarg }
| Operator::I32AtomicRmw16AndU { memarg }
| Operator::I32AtomicRmw16OrU { memarg }
| Operator::I32AtomicRmw16XorU { memarg }
| Operator::I32AtomicRmw8AddU { memarg }
| Operator::I32AtomicRmw8SubU { memarg }
| Operator::I32AtomicRmw8AndU { memarg }
| Operator::I32AtomicRmw8OrU { memarg }
| Operator::I32AtomicRmw8XorU { memarg } => {
self.check_threads_enabled()?;
let ty = self.check_shared_memarg_wo_align(memarg, resources)?;
self.pop_operand(Some(Type::I32))?;
self.pop_operand(Some(ty))?;
self.push_operand(Type::I32)?;
}
Operator::I64AtomicRmwAdd { memarg }
| Operator::I64AtomicRmwSub { memarg }
| Operator::I64AtomicRmwAnd { memarg }
| Operator::I64AtomicRmwOr { memarg }
| Operator::I64AtomicRmwXor { memarg }
| Operator::I64AtomicRmw32AddU { memarg }
| Operator::I64AtomicRmw32SubU { memarg }
| Operator::I64AtomicRmw32AndU { memarg }
| Operator::I64AtomicRmw32OrU { memarg }
| Operator::I64AtomicRmw32XorU { memarg }
| Operator::I64AtomicRmw16AddU { memarg }
| Operator::I64AtomicRmw16SubU { memarg }
| Operator::I64AtomicRmw16AndU { memarg }
| Operator::I64AtomicRmw16OrU { memarg }
| Operator::I64AtomicRmw16XorU { memarg }
| Operator::I64AtomicRmw8AddU { memarg }
| Operator::I64AtomicRmw8SubU { memarg }
| Operator::I64AtomicRmw8AndU { memarg }
| Operator::I64AtomicRmw8OrU { memarg }
| Operator::I64AtomicRmw8XorU { memarg } => {
self.check_threads_enabled()?;
let ty = self.check_shared_memarg_wo_align(memarg, resources)?;
self.pop_operand(Some(Type::I64))?;
self.pop_operand(Some(ty))?;
self.push_operand(Type::I64)?;
}
Operator::I32AtomicRmwXchg { memarg }
| Operator::I32AtomicRmw16XchgU { memarg }
| Operator::I32AtomicRmw8XchgU { memarg } => {
self.check_threads_enabled()?;
let ty = self.check_shared_memarg_wo_align(memarg, resources)?;
self.pop_operand(Some(Type::I32))?;
self.pop_operand(Some(ty))?;
self.push_operand(Type::I32)?;
}
Operator::I32AtomicRmwCmpxchg { memarg }
| Operator::I32AtomicRmw16CmpxchgU { memarg }
| Operator::I32AtomicRmw8CmpxchgU { memarg } => {
self.check_threads_enabled()?;
let ty = self.check_shared_memarg_wo_align(memarg, resources)?;
self.pop_operand(Some(Type::I32))?;
self.pop_operand(Some(Type::I32))?;
self.pop_operand(Some(ty))?;
self.push_operand(Type::I32)?;
}
Operator::I64AtomicRmwXchg { memarg }
| Operator::I64AtomicRmw32XchgU { memarg }
| Operator::I64AtomicRmw16XchgU { memarg }
| Operator::I64AtomicRmw8XchgU { memarg } => {
self.check_threads_enabled()?;
let ty = self.check_shared_memarg_wo_align(memarg, resources)?;
self.pop_operand(Some(Type::I64))?;
self.pop_operand(Some(ty))?;
self.push_operand(Type::I64)?;
}
Operator::I64AtomicRmwCmpxchg { memarg }
| Operator::I64AtomicRmw32CmpxchgU { memarg }
| Operator::I64AtomicRmw16CmpxchgU { memarg }
| Operator::I64AtomicRmw8CmpxchgU { memarg } => {
self.check_threads_enabled()?;
let ty = self.check_shared_memarg_wo_align(memarg, resources)?;
self.pop_operand(Some(Type::I64))?;
self.pop_operand(Some(Type::I64))?;
self.pop_operand(Some(ty))?;
self.push_operand(Type::I64)?;
}
Operator::MemoryAtomicNotify { memarg } => {
self.check_threads_enabled()?;
let ty = self.check_shared_memarg_wo_align(memarg, resources)?;
self.pop_operand(Some(Type::I32))?;
self.pop_operand(Some(ty))?;
self.push_operand(Type::I32)?;
}
Operator::MemoryAtomicWait32 { memarg } => {
self.check_threads_enabled()?;
let ty = self.check_shared_memarg_wo_align(memarg, resources)?;
self.pop_operand(Some(Type::I64))?;
self.pop_operand(Some(Type::I32))?;
self.pop_operand(Some(ty))?;
self.push_operand(Type::I32)?;
}
Operator::MemoryAtomicWait64 { memarg } => {
self.check_threads_enabled()?;
let ty = self.check_shared_memarg_wo_align(memarg, resources)?;
self.pop_operand(Some(Type::I64))?;
self.pop_operand(Some(Type::I64))?;
self.pop_operand(Some(ty))?;
self.push_operand(Type::I32)?;
}
Operator::AtomicFence { ref flags } => {
self.check_threads_enabled()?;
if *flags != 0 {
return Err(OperatorValidatorError::new(
"non-zero flags for fence not supported yet",
));
}
}
Operator::RefNull { ty } => {
self.check_reference_types_enabled()?;
match ty {
Type::FuncRef | Type::ExternRef => {}
_ => {
return Err(OperatorValidatorError::new(
"invalid reference type in ref.null",
))
}
}
self.push_operand(ty)?;
}
Operator::RefIsNull => {
self.check_reference_types_enabled()?;
match self.pop_operand(None)? {
None | Some(Type::FuncRef) | Some(Type::ExternRef) => {}
_ => {
return Err(OperatorValidatorError::new(
"type mismatch: invalid reference type in ref.is_null",
))
}
}
self.push_operand(Type::I32)?;
}
Operator::RefFunc { function_index } => {
self.check_reference_types_enabled()?;
if resources.type_of_function(function_index).is_none() {
return Err(OperatorValidatorError::new(
"unknown function: function index out of bounds",
));
}
if !resources.is_function_referenced(function_index) {
return Err(OperatorValidatorError::new("undeclared function reference"));
}
self.push_operand(Type::FuncRef)?;
}
Operator::V128Load { memarg } => {
self.check_simd_enabled()?;
let ty = self.check_memarg(memarg, 4, resources)?;
self.pop_operand(Some(ty))?;
self.push_operand(Type::V128)?;
}
Operator::V128Store { memarg } => {
self.check_simd_enabled()?;
let ty = self.check_memarg(memarg, 4, resources)?;
self.pop_operand(Some(Type::V128))?;
self.pop_operand(Some(ty))?;
}
Operator::V128Const { .. } => {
self.check_simd_enabled()?;
self.push_operand(Type::V128)?;
}
Operator::I8x16Splat | Operator::I16x8Splat | Operator::I32x4Splat => {
self.check_simd_enabled()?;
self.pop_operand(Some(Type::I32))?;
self.push_operand(Type::V128)?;
}
Operator::I64x2Splat => {
self.check_simd_enabled()?;
self.pop_operand(Some(Type::I64))?;
self.push_operand(Type::V128)?;
}
Operator::F32x4Splat => {
self.check_non_deterministic_enabled()?;
self.check_simd_enabled()?;
self.pop_operand(Some(Type::F32))?;
self.push_operand(Type::V128)?;
}
Operator::F64x2Splat => {
self.check_non_deterministic_enabled()?;
self.check_simd_enabled()?;
self.pop_operand(Some(Type::F64))?;
self.push_operand(Type::V128)?;
}
Operator::I8x16ExtractLaneS { lane } | Operator::I8x16ExtractLaneU { lane } => {
self.check_simd_enabled()?;
self.check_simd_lane_index(lane, 16)?;
self.pop_operand(Some(Type::V128))?;
self.push_operand(Type::I32)?;
}
Operator::I16x8ExtractLaneS { lane } | Operator::I16x8ExtractLaneU { lane } => {
self.check_simd_enabled()?;
self.check_simd_lane_index(lane, 8)?;
self.pop_operand(Some(Type::V128))?;
self.push_operand(Type::I32)?;
}
Operator::I32x4ExtractLane { lane } => {
self.check_simd_enabled()?;
self.check_simd_lane_index(lane, 4)?;
self.pop_operand(Some(Type::V128))?;
self.push_operand(Type::I32)?;
}
Operator::I8x16ReplaceLane { lane } => {
self.check_simd_enabled()?;
self.check_simd_lane_index(lane, 16)?;
self.pop_operand(Some(Type::I32))?;
self.pop_operand(Some(Type::V128))?;
self.push_operand(Type::V128)?;
}
Operator::I16x8ReplaceLane { lane } => {
self.check_simd_enabled()?;
self.check_simd_lane_index(lane, 8)?;
self.pop_operand(Some(Type::I32))?;
self.pop_operand(Some(Type::V128))?;
self.push_operand(Type::V128)?;
}
Operator::I32x4ReplaceLane { lane } => {
self.check_simd_enabled()?;
self.check_simd_lane_index(lane, 4)?;
self.pop_operand(Some(Type::I32))?;
self.pop_operand(Some(Type::V128))?;
self.push_operand(Type::V128)?;
}
Operator::I64x2ExtractLane { lane } => {
self.check_simd_enabled()?;
self.check_simd_lane_index(lane, 2)?;
self.pop_operand(Some(Type::V128))?;
self.push_operand(Type::I64)?;
}
Operator::I64x2ReplaceLane { lane } => {
self.check_simd_enabled()?;
self.check_simd_lane_index(lane, 2)?;
self.pop_operand(Some(Type::I64))?;
self.pop_operand(Some(Type::V128))?;
self.push_operand(Type::V128)?;
}
Operator::F32x4ExtractLane { lane } => {
self.check_non_deterministic_enabled()?;
self.check_simd_enabled()?;
self.check_simd_lane_index(lane, 4)?;
self.pop_operand(Some(Type::V128))?;
self.push_operand(Type::F32)?;
}
Operator::F32x4ReplaceLane { lane } => {
self.check_non_deterministic_enabled()?;
self.check_simd_enabled()?;
self.check_simd_lane_index(lane, 4)?;
self.pop_operand(Some(Type::F32))?;
self.pop_operand(Some(Type::V128))?;
self.push_operand(Type::V128)?;
}
Operator::F64x2ExtractLane { lane } => {
self.check_non_deterministic_enabled()?;
self.check_simd_enabled()?;
self.check_simd_lane_index(lane, 2)?;
self.pop_operand(Some(Type::V128))?;
self.push_operand(Type::F64)?;
}
Operator::F64x2ReplaceLane { lane } => {
self.check_non_deterministic_enabled()?;
self.check_simd_enabled()?;
self.check_simd_lane_index(lane, 2)?;
self.pop_operand(Some(Type::F64))?;
self.pop_operand(Some(Type::V128))?;
self.push_operand(Type::V128)?;
}
Operator::F32x4Eq
| Operator::F32x4Ne
| Operator::F32x4Lt
| Operator::F32x4Gt
| Operator::F32x4Le
| Operator::F32x4Ge
| Operator::F64x2Eq
| Operator::F64x2Ne
| Operator::F64x2Lt
| Operator::F64x2Gt
| Operator::F64x2Le
| Operator::F64x2Ge
| Operator::F32x4Add
| Operator::F32x4Sub
| Operator::F32x4Mul
| Operator::F32x4Div
| Operator::F32x4Min
| Operator::F32x4Max
| Operator::F32x4PMin
| Operator::F32x4PMax
| Operator::F64x2Add
| Operator::F64x2Sub
| Operator::F64x2Mul
| Operator::F64x2Div
| Operator::F64x2Min
| Operator::F64x2Max
| Operator::F64x2PMin
| Operator::F64x2PMax => {
self.check_non_deterministic_enabled()?;
self.check_simd_enabled()?;
self.pop_operand(Some(Type::V128))?;
self.pop_operand(Some(Type::V128))?;
self.push_operand(Type::V128)?;
}
Operator::I8x16Eq
| Operator::I8x16Ne
| Operator::I8x16LtS
| Operator::I8x16LtU
| Operator::I8x16GtS
| Operator::I8x16GtU
| Operator::I8x16LeS
| Operator::I8x16LeU
| Operator::I8x16GeS
| Operator::I8x16GeU
| Operator::I16x8Eq
| Operator::I16x8Ne
| Operator::I16x8LtS
| Operator::I16x8LtU
| Operator::I16x8GtS
| Operator::I16x8GtU
| Operator::I16x8LeS
| Operator::I16x8LeU
| Operator::I16x8GeS
| Operator::I16x8GeU
| Operator::I32x4Eq
| Operator::I32x4Ne
| Operator::I32x4LtS
| Operator::I32x4LtU
| Operator::I32x4GtS
| Operator::I32x4GtU
| Operator::I32x4LeS
| Operator::I32x4LeU
| Operator::I32x4GeS
| Operator::I32x4GeU
| Operator::V128And
| Operator::V128AndNot
| Operator::V128Or
| Operator::V128Xor
| Operator::I8x16Add
| Operator::I8x16AddSatS
| Operator::I8x16AddSatU
| Operator::I8x16Sub
| Operator::I8x16SubSatS
| Operator::I8x16SubSatU
| Operator::I8x16MinS
| Operator::I8x16MinU
| Operator::I8x16MaxS
| Operator::I8x16MaxU
| Operator::I16x8Add
| Operator::I16x8AddSatS
| Operator::I16x8AddSatU
| Operator::I16x8Sub
| Operator::I16x8SubSatS
| Operator::I16x8SubSatU
| Operator::I16x8Mul
| Operator::I16x8MinS
| Operator::I16x8MinU
| Operator::I16x8MaxS
| Operator::I16x8MaxU
| Operator::I32x4Add
| Operator::I32x4Sub
| Operator::I32x4Mul
| Operator::I32x4MinS
| Operator::I32x4MinU
| Operator::I32x4MaxS
| Operator::I32x4MaxU
| Operator::I32x4DotI16x8S
| Operator::I64x2Add
| Operator::I64x2Sub
| Operator::I64x2Mul
| Operator::I8x16RoundingAverageU
| Operator::I16x8RoundingAverageU
| Operator::I8x16NarrowI16x8S
| Operator::I8x16NarrowI16x8U
| Operator::I16x8NarrowI32x4S
| Operator::I16x8NarrowI32x4U
| Operator::I16x8ExtMulLowI8x16S
| Operator::I16x8ExtMulHighI8x16S
| Operator::I16x8ExtMulLowI8x16U
| Operator::I16x8ExtMulHighI8x16U
| Operator::I32x4ExtMulLowI16x8S
| Operator::I32x4ExtMulHighI16x8S
| Operator::I32x4ExtMulLowI16x8U
| Operator::I32x4ExtMulHighI16x8U
| Operator::I64x2ExtMulLowI32x4S
| Operator::I64x2ExtMulHighI32x4S
| Operator::I64x2ExtMulLowI32x4U
| Operator::I64x2ExtMulHighI32x4U => {
self.check_simd_enabled()?;
self.pop_operand(Some(Type::V128))?;
self.pop_operand(Some(Type::V128))?;
self.push_operand(Type::V128)?;
}
Operator::F32x4Ceil
| Operator::F32x4Floor
| Operator::F32x4Trunc
| Operator::F32x4Nearest
| Operator::F64x2Ceil
| Operator::F64x2Floor
| Operator::F64x2Trunc
| Operator::F64x2Nearest
| Operator::F32x4Abs
| Operator::F32x4Neg
| Operator::F32x4Sqrt
| Operator::F64x2Abs
| Operator::F64x2Neg
| Operator::F64x2Sqrt
| Operator::F32x4ConvertI32x4S
| Operator::F32x4ConvertI32x4U => {
self.check_non_deterministic_enabled()?;
self.check_simd_enabled()?;
self.pop_operand(Some(Type::V128))?;
self.push_operand(Type::V128)?;
}
Operator::V128Not
| Operator::I8x16Abs
| Operator::I8x16Neg
| Operator::I16x8Abs
| Operator::I16x8Neg
| Operator::I32x4Abs
| Operator::I32x4Neg
| Operator::I64x2Neg
| Operator::I32x4TruncSatF32x4S
| Operator::I32x4TruncSatF32x4U
| Operator::I16x8WidenLowI8x16S
| Operator::I16x8WidenHighI8x16S
| Operator::I16x8WidenLowI8x16U
| Operator::I16x8WidenHighI8x16U
| Operator::I32x4WidenLowI16x8S
| Operator::I32x4WidenHighI16x8S
| Operator::I32x4WidenLowI16x8U
| Operator::I32x4WidenHighI16x8U => {
self.check_simd_enabled()?;
self.pop_operand(Some(Type::V128))?;
self.push_operand(Type::V128)?;
}
Operator::V128Bitselect => {
self.check_simd_enabled()?;
self.pop_operand(Some(Type::V128))?;
self.pop_operand(Some(Type::V128))?;
self.pop_operand(Some(Type::V128))?;
self.push_operand(Type::V128)?;
}
Operator::I8x16AnyTrue
| Operator::I8x16AllTrue
| Operator::I8x16Bitmask
| Operator::I16x8AnyTrue
| Operator::I16x8AllTrue
| Operator::I16x8Bitmask
| Operator::I32x4AnyTrue
| Operator::I32x4AllTrue
| Operator::I32x4Bitmask => {
self.check_simd_enabled()?;
self.pop_operand(Some(Type::V128))?;
self.push_operand(Type::I32)?;
}
Operator::I8x16Shl
| Operator::I8x16ShrS
| Operator::I8x16ShrU
| Operator::I16x8Shl
| Operator::I16x8ShrS
| Operator::I16x8ShrU
| Operator::I32x4Shl
| Operator::I32x4ShrS
| Operator::I32x4ShrU
| Operator::I64x2Shl
| Operator::I64x2ShrS
| Operator::I64x2ShrU => {
self.check_simd_enabled()?;
self.pop_operand(Some(Type::I32))?;
self.pop_operand(Some(Type::V128))?;
self.push_operand(Type::V128)?;
}
Operator::I8x16Swizzle => {
self.check_simd_enabled()?;
self.pop_operand(Some(Type::V128))?;
self.pop_operand(Some(Type::V128))?;
self.push_operand(Type::V128)?;
}
Operator::I8x16Shuffle { ref lanes } => {
self.check_simd_enabled()?;
self.pop_operand(Some(Type::V128))?;
self.pop_operand(Some(Type::V128))?;
for i in lanes {
self.check_simd_lane_index(*i, 32)?;
}
self.push_operand(Type::V128)?;
}
Operator::V128Load8Splat { memarg } => {
self.check_simd_enabled()?;
let ty = self.check_memarg(memarg, 0, resources)?;
self.pop_operand(Some(ty))?;
self.push_operand(Type::V128)?;
}
Operator::V128Load16Splat { memarg } => {
self.check_simd_enabled()?;
let ty = self.check_memarg(memarg, 1, resources)?;
self.pop_operand(Some(ty))?;
self.push_operand(Type::V128)?;
}
Operator::V128Load32Splat { memarg } | Operator::V128Load32Zero { memarg } => {
self.check_simd_enabled()?;
let ty = self.check_memarg(memarg, 2, resources)?;
self.pop_operand(Some(ty))?;
self.push_operand(Type::V128)?;
}
Operator::V128Load64Splat { memarg }
| Operator::V128Load64Zero { memarg }
| Operator::V128Load8x8S { memarg }
| Operator::V128Load8x8U { memarg }
| Operator::V128Load16x4S { memarg }
| Operator::V128Load16x4U { memarg }
| Operator::V128Load32x2S { memarg }
| Operator::V128Load32x2U { memarg } => {
self.check_simd_enabled()?;
let idx = self.check_memarg(memarg, 3, resources)?;
self.pop_operand(Some(idx))?;
self.push_operand(Type::V128)?;
}
Operator::MemoryInit { mem, segment } => {
self.check_bulk_memory_enabled()?;
let ty = self.check_memory_index(mem, resources)?;
if segment >= resources.data_count() {
bail_op_err!("unknown data segment {}", segment);
}
self.pop_operand(Some(Type::I32))?;
self.pop_operand(Some(Type::I32))?;
self.pop_operand(Some(ty))?;
}
Operator::DataDrop { segment } => {
self.check_bulk_memory_enabled()?;
if segment >= resources.data_count() {
bail_op_err!("unknown data segment {}", segment);
}
}
Operator::MemoryCopy { src, dst } => {
self.check_bulk_memory_enabled()?;
let dst_ty = self.check_memory_index(dst, resources)?;
let src_ty = self.check_memory_index(src, resources)?;
self.pop_operand(Some(match src_ty {
Type::I32 => Type::I32,
_ => dst_ty,
}))?;
self.pop_operand(Some(src_ty))?;
self.pop_operand(Some(dst_ty))?;
}
Operator::MemoryFill { mem } => {
self.check_bulk_memory_enabled()?;
let ty = self.check_memory_index(mem, resources)?;
self.pop_operand(Some(ty))?;
self.pop_operand(Some(Type::I32))?;
self.pop_operand(Some(ty))?;
}
Operator::TableInit { segment, table } => {
self.check_bulk_memory_enabled()?;
if table > 0 {
self.check_reference_types_enabled()?;
}
let table = match resources.table_at(table) {
Some(table) => table,
None => bail_op_err!("unknown table {}: table index out of bounds", table),
};
let segment_ty = match resources.element_type_at(segment) {
Some(ty) => ty,
None => bail_op_err!(
"unknown elem segment {}: segment index out of bounds",
segment
),
};
if segment_ty != table.element_type {
return Err(OperatorValidatorError::new("type mismatch"));
}
self.pop_operand(Some(Type::I32))?;
self.pop_operand(Some(Type::I32))?;
self.pop_operand(Some(Type::I32))?;
}
Operator::ElemDrop { segment } => {
self.check_bulk_memory_enabled()?;
if segment >= resources.element_count() {
bail_op_err!(
"unknown elem segment {}: segment index out of bounds",
segment
);
}
}
Operator::TableCopy {
src_table,
dst_table,
} => {
self.check_bulk_memory_enabled()?;
if src_table > 0 || dst_table > 0 {
self.check_reference_types_enabled()?;
}
let (src, dst) =
match (resources.table_at(src_table), resources.table_at(dst_table)) {
(Some(a), Some(b)) => (a, b),
_ => return Err(OperatorValidatorError::new("table index out of bounds")),
};
if src.element_type != dst.element_type {
return Err(OperatorValidatorError::new("type mismatch"));
}
self.pop_operand(Some(Type::I32))?;
self.pop_operand(Some(Type::I32))?;
self.pop_operand(Some(Type::I32))?;
}
Operator::TableGet { table } => {
self.check_reference_types_enabled()?;
let ty = match resources.table_at(table) {
Some(ty) => ty.element_type,
None => return Err(OperatorValidatorError::new("table index out of bounds")),
};
self.pop_operand(Some(Type::I32))?;
self.push_operand(ty)?;
}
Operator::TableSet { table } => {
self.check_reference_types_enabled()?;
let ty = match resources.table_at(table) {
Some(ty) => ty.element_type,
None => return Err(OperatorValidatorError::new("table index out of bounds")),
};
self.pop_operand(Some(ty))?;
self.pop_operand(Some(Type::I32))?;
}
Operator::TableGrow { table } => {
self.check_reference_types_enabled()?;
let ty = match resources.table_at(table) {
Some(ty) => ty.element_type,
None => return Err(OperatorValidatorError::new("table index out of bounds")),
};
self.pop_operand(Some(Type::I32))?;
self.pop_operand(Some(ty))?;
self.push_operand(Type::I32)?;
}
Operator::TableSize { table } => {
self.check_reference_types_enabled()?;
if resources.table_at(table).is_none() {
return Err(OperatorValidatorError::new("table index out of bounds"));
}
self.push_operand(Type::I32)?;
}
Operator::TableFill { table } => {
self.check_bulk_memory_enabled()?;
let ty = match resources.table_at(table) {
Some(ty) => ty.element_type,
None => return Err(OperatorValidatorError::new("table index out of bounds")),
};
self.pop_operand(Some(Type::I32))?;
self.pop_operand(Some(ty))?;
self.pop_operand(Some(Type::I32))?;
}
}
Ok(())
}
pub fn finish(&mut self) -> OperatorValidatorResult<()> {
if self.control.len() != 0 {
bail_op_err!("control frames remain at end of function");
}
Ok(())
}
}
fn func_type_at<T: WasmModuleResources>(
resources: &T,
at: u32,
) -> OperatorValidatorResult<&T::FuncType> {
resources
.func_type_at(at)
.ok_or_else(|| OperatorValidatorError::new("unknown type: type index out of bounds"))
}
fn event_at<T: WasmModuleResources>(resources: &T, at: u32) -> OperatorValidatorResult<EventType> {
resources
.event_at(at)
.ok_or_else(|| OperatorValidatorError::new("unknown event: event index out of bounds"))
}
enum Either<A, B> {
A(A),
B(B),
}
impl<A, B> Iterator for Either<A, B>
where
A: Iterator,
B: Iterator<Item = A::Item>,
{
type Item = A::Item;
fn next(&mut self) -> Option<A::Item> {
match self {
Either::A(a) => a.next(),
Either::B(b) => b.next(),
}
}
}
impl<A, B> DoubleEndedIterator for Either<A, B>
where
A: DoubleEndedIterator,
B: DoubleEndedIterator<Item = A::Item>,
{
fn next_back(&mut self) -> Option<A::Item> {
match self {
Either::A(a) => a.next_back(),
Either::B(b) => b.next_back(),
}
}
}
fn params<'a>(
ty: TypeOrFuncType,
resources: &'a impl WasmModuleResources,
) -> OperatorValidatorResult<impl DoubleEndedIterator<Item = Type> + 'a> {
Ok(match ty {
TypeOrFuncType::FuncType(t) => Either::A(func_type_at(resources, t)?.inputs()),
TypeOrFuncType::Type(_) => Either::B(None.into_iter()),
})
}
fn results<'a>(
ty: TypeOrFuncType,
resources: &'a impl WasmModuleResources,
) -> OperatorValidatorResult<impl DoubleEndedIterator<Item = Type> + 'a> {
Ok(match ty {
TypeOrFuncType::FuncType(t) => Either::A(func_type_at(resources, t)?.outputs()),
TypeOrFuncType::Type(Type::EmptyBlockType) => Either::B(None.into_iter()),
TypeOrFuncType::Type(t) => Either::B(Some(t).into_iter()),
})
}
fn label_types<'a>(
ty: TypeOrFuncType,
resources: &'a impl WasmModuleResources,
kind: FrameKind,
) -> OperatorValidatorResult<impl DoubleEndedIterator<Item = Type> + 'a> {
Ok(match kind {
FrameKind::Loop => Either::A(params(ty, resources)?),
_ => Either::B(results(ty, resources)?),
})
}
fn ty_to_str(ty: Type) -> &'static str {
match ty {
Type::I32 => "i32",
Type::I64 => "i64",
Type::F32 => "f32",
Type::F64 => "f64",
Type::V128 => "v128",
Type::FuncRef => "funcref",
Type::ExternRef => "externref",
Type::ExnRef => "exnref",
Type::Func => "func",
Type::EmptyBlockType => "nil",
}
}