#[cfg_attr(coverage_nightly, coverage(off))]
#[cfg(test)]
mod stack_analyzer_tests {
use super::*;
#[test]
fn test_stack_analyzer_new() {
let analyzer = StackAnalyzer::new();
assert!(analyzer.type_stack.is_empty());
}
#[test]
fn test_stack_analyzer_i32_const() {
let analyzer = StackAnalyzer::new();
let mut stack = Vec::new();
let op = Operator::I32Const { value: 42 };
let result = analyzer.update_stack(&mut stack, &op);
assert!(result.is_ok());
assert_eq!(stack.len(), 1);
assert_eq!(stack[0], ValType::I32);
}
#[test]
fn test_stack_analyzer_i64_const() {
let analyzer = StackAnalyzer::new();
let mut stack = Vec::new();
let op = Operator::I64Const { value: 42 };
let result = analyzer.update_stack(&mut stack, &op);
assert!(result.is_ok());
assert_eq!(stack.len(), 1);
assert_eq!(stack[0], ValType::I64);
}
#[test]
fn test_stack_analyzer_f32_const() {
let analyzer = StackAnalyzer::new();
let mut stack = Vec::new();
let op = Operator::F32Const {
value: wasmparser::Ieee32::from(0.0f32),
};
let result = analyzer.update_stack(&mut stack, &op);
assert!(result.is_ok());
assert_eq!(stack.len(), 1);
assert_eq!(stack[0], ValType::F32);
}
#[test]
fn test_stack_analyzer_f64_const() {
let analyzer = StackAnalyzer::new();
let mut stack = Vec::new();
let op = Operator::F64Const {
value: wasmparser::Ieee64::from(0.0f64),
};
let result = analyzer.update_stack(&mut stack, &op);
assert!(result.is_ok());
assert_eq!(stack.len(), 1);
assert_eq!(stack[0], ValType::F64);
}
#[test]
fn test_stack_analyzer_i32_add_valid() {
let analyzer = StackAnalyzer::new();
let mut stack = vec![ValType::I32, ValType::I32];
let op = Operator::I32Add;
let result = analyzer.update_stack(&mut stack, &op);
assert!(result.is_ok());
assert_eq!(stack.len(), 1);
assert_eq!(stack[0], ValType::I32);
}
#[test]
fn test_stack_analyzer_i32_add_underflow() {
let analyzer = StackAnalyzer::new();
let mut stack = vec![ValType::I32]; let op = Operator::I32Add;
let result = analyzer.update_stack(&mut stack, &op);
assert!(result.is_err());
}
#[test]
fn test_stack_analyzer_i64_add_valid() {
let analyzer = StackAnalyzer::new();
let mut stack = vec![ValType::I64, ValType::I64];
let op = Operator::I64Add;
let result = analyzer.update_stack(&mut stack, &op);
assert!(result.is_ok());
assert_eq!(stack.len(), 1);
assert_eq!(stack[0], ValType::I64);
}
#[test]
fn test_stack_analyzer_i64_add_underflow() {
let analyzer = StackAnalyzer::new();
let mut stack = vec![ValType::I64]; let op = Operator::I64Add;
let result = analyzer.update_stack(&mut stack, &op);
assert!(
result.is_err(),
"i64.add with <2 operands must hit pop_binary_i64 stack-underflow arm"
);
}
#[test]
fn test_stack_analyzer_i32_eqz_valid() {
let analyzer = StackAnalyzer::new();
let mut stack = vec![ValType::I32];
let op = Operator::I32Eqz;
let result = analyzer.update_stack(&mut stack, &op);
assert!(result.is_ok());
assert_eq!(stack.len(), 1);
assert_eq!(stack[0], ValType::I32);
}
#[test]
fn test_stack_analyzer_i32_eqz_type_error() {
let analyzer = StackAnalyzer::new();
let mut stack = vec![ValType::I64]; let op = Operator::I32Eqz;
let result = analyzer.update_stack(&mut stack, &op);
assert!(result.is_err());
}
#[test]
fn test_stack_analyzer_i32_eq_valid() {
let analyzer = StackAnalyzer::new();
let mut stack = vec![ValType::I32, ValType::I32];
let op = Operator::I32Eq;
let result = analyzer.update_stack(&mut stack, &op);
assert!(result.is_ok());
assert_eq!(stack.len(), 1);
}
#[test]
fn test_stack_analyzer_i32_eq_underflow() {
let analyzer = StackAnalyzer::new();
let mut stack = vec![ValType::I32]; let op = Operator::I32Eq;
let result = analyzer.update_stack(&mut stack, &op);
assert!(
result.is_err(),
"i32.eq with <2 operands must hit the stack-underflow arm"
);
}
#[test]
fn test_stack_analyzer_i32_lt_type_error() {
let analyzer = StackAnalyzer::new();
let mut stack = vec![ValType::I32, ValType::I64]; let op = Operator::I32LtS;
let result = analyzer.update_stack(&mut stack, &op);
assert!(
result.is_err(),
"i32.lt_s with an i64 operand must hit the `expected i32` type-error arm"
);
}
#[test]
fn test_stack_analyzer_local_get() {
let analyzer = StackAnalyzer::new();
let mut stack = Vec::new();
let op = Operator::LocalGet { local_index: 0 };
let result = analyzer.update_stack(&mut stack, &op);
assert!(result.is_ok());
assert_eq!(stack.len(), 1);
}
#[test]
fn test_stack_analyzer_local_set_valid() {
let analyzer = StackAnalyzer::new();
let mut stack = vec![ValType::I32];
let op = Operator::LocalSet { local_index: 0 };
let result = analyzer.update_stack(&mut stack, &op);
assert!(result.is_ok());
assert!(stack.is_empty());
}
#[test]
fn test_stack_analyzer_local_set_underflow() {
let analyzer = StackAnalyzer::new();
let mut stack = Vec::new();
let op = Operator::LocalSet { local_index: 0 };
let result = analyzer.update_stack(&mut stack, &op);
assert!(result.is_err());
}
#[test]
fn test_stack_analyzer_drop_valid() {
let analyzer = StackAnalyzer::new();
let mut stack = vec![ValType::I32];
let op = Operator::Drop;
let result = analyzer.update_stack(&mut stack, &op);
assert!(result.is_ok());
assert!(stack.is_empty());
}
#[test]
fn test_stack_analyzer_drop_underflow() {
let analyzer = StackAnalyzer::new();
let mut stack = Vec::new();
let op = Operator::Drop;
let result = analyzer.update_stack(&mut stack, &op);
assert!(result.is_err());
}
#[test]
fn test_stack_analyzer_nop() {
let analyzer = StackAnalyzer::new();
let mut stack = Vec::new();
let op = Operator::Nop;
let result = analyzer.update_stack(&mut stack, &op);
assert!(result.is_ok());
assert!(stack.is_empty());
}
#[test]
fn test_check_i32_load_store_out_of_bounds() {
let mut stack = vec![ValType::I32];
let memarg = wasmparser::MemArg {
align: 2,
max_align: 2,
offset: 100,
memory: 0,
};
let result = check_i32_load_store(&mut stack, &memarg, 10, 4);
assert!(matches!(
result,
Some(VerificationResult::OutOfBounds { .. })
));
}
#[test]
fn test_check_i32_load_store_type_error() {
let mut stack = vec![ValType::I64];
let memarg = wasmparser::MemArg {
align: 2,
max_align: 2,
offset: 0,
memory: 0,
};
let result = check_i32_load_store(&mut stack, &memarg, 1024, 4);
assert!(matches!(result, Some(VerificationResult::TypeError { .. })));
}
#[test]
fn test_check_i32_load_store_success() {
let mut stack = vec![ValType::I32];
let memarg = wasmparser::MemArg {
align: 2,
max_align: 2,
offset: 0,
memory: 0,
};
let result = check_i32_load_store(&mut stack, &memarg, 1024, 4);
assert!(result.is_none());
assert!(stack.is_empty());
}
#[test]
fn test_check_i32_load_store_empty_stack() {
let mut stack: Vec<ValType> = Vec::new();
let memarg = wasmparser::MemArg {
align: 2,
max_align: 2,
offset: 0,
memory: 0,
};
let result = check_i32_load_store(&mut stack, &memarg, 1024, 4);
assert!(matches!(result, Some(VerificationResult::TypeError { .. })));
}
}