use crate::Error::InvalidStackValue;
use crate::JavaError::ArithmeticException;
use crate::JavaError::{ArrayIndexOutOfBoundsException, NullPointerException};
use crate::frame::ExecutionResult::Return;
use crate::frame::{ExecutionResult, ExecutionResult::Continue};
use crate::local_variables::LocalVariables;
use crate::operand_stack::OperandStack;
use crate::{Result, Value};
use ristretto_classloader::Reference;
use std::cmp::Ordering;
#[inline]
pub(crate) fn lconst_0(stack: &mut OperandStack) -> Result<ExecutionResult> {
stack.push_long(0)?;
Ok(Continue)
}
#[inline]
pub(crate) fn lconst_1(stack: &mut OperandStack) -> Result<ExecutionResult> {
stack.push_long(1)?;
Ok(Continue)
}
#[inline]
pub(crate) fn lload(
locals: &LocalVariables,
stack: &mut OperandStack,
index: u8,
) -> Result<ExecutionResult> {
let value = locals.get_long(usize::from(index))?;
stack.push_long(value)?;
Ok(Continue)
}
#[inline]
pub(crate) fn lload_w(
locals: &LocalVariables,
stack: &mut OperandStack,
index: u16,
) -> Result<ExecutionResult> {
let value = locals.get_long(usize::from(index))?;
stack.push_long(value)?;
Ok(Continue)
}
#[inline]
pub(crate) fn lload_0(
locals: &LocalVariables,
stack: &mut OperandStack,
) -> Result<ExecutionResult> {
let value = locals.get_long(0)?;
stack.push_long(value)?;
Ok(Continue)
}
#[inline]
pub(crate) fn lload_1(
locals: &LocalVariables,
stack: &mut OperandStack,
) -> Result<ExecutionResult> {
let value = locals.get_long(1)?;
stack.push_long(value)?;
Ok(Continue)
}
#[inline]
pub(crate) fn lload_2(
locals: &LocalVariables,
stack: &mut OperandStack,
) -> Result<ExecutionResult> {
let value = locals.get_long(2)?;
stack.push_long(value)?;
Ok(Continue)
}
#[inline]
pub(crate) fn lload_3(
locals: &LocalVariables,
stack: &mut OperandStack,
) -> Result<ExecutionResult> {
let value = locals.get_long(3)?;
stack.push_long(value)?;
Ok(Continue)
}
#[inline]
pub(crate) fn lstore(
locals: &mut LocalVariables,
stack: &mut OperandStack,
index: u8,
) -> Result<ExecutionResult> {
let value = stack.pop_long()?;
locals.set_long(usize::from(index), value)?;
Ok(Continue)
}
#[inline]
pub(crate) fn lstore_w(
locals: &mut LocalVariables,
stack: &mut OperandStack,
index: u16,
) -> Result<ExecutionResult> {
let value = stack.pop_long()?;
locals.set_long(usize::from(index), value)?;
Ok(Continue)
}
#[inline]
pub(crate) fn lstore_0(
locals: &mut LocalVariables,
stack: &mut OperandStack,
) -> Result<ExecutionResult> {
let value = stack.pop_long()?;
locals.set_long(0, value)?;
Ok(Continue)
}
#[inline]
pub(crate) fn lstore_1(
locals: &mut LocalVariables,
stack: &mut OperandStack,
) -> Result<ExecutionResult> {
let value = stack.pop_long()?;
locals.set_long(1, value)?;
Ok(Continue)
}
#[inline]
pub(crate) fn lstore_2(
locals: &mut LocalVariables,
stack: &mut OperandStack,
) -> Result<ExecutionResult> {
let value = stack.pop_long()?;
locals.set_long(2, value)?;
Ok(Continue)
}
#[inline]
pub(crate) fn lstore_3(
locals: &mut LocalVariables,
stack: &mut OperandStack,
) -> Result<ExecutionResult> {
let value = stack.pop_long()?;
locals.set_long(3, value)?;
Ok(Continue)
}
#[inline]
pub(crate) fn laload(stack: &mut OperandStack) -> Result<ExecutionResult> {
let index = stack.pop_int()?;
let Some(reference) = stack.pop_object()? else {
return Err(NullPointerException(None).into());
};
let guard = reference.read();
let Reference::LongArray(array) = &*guard else {
return Err(InvalidStackValue {
expected: "long array".to_string(),
actual: guard.to_string(),
});
};
let original_index = index;
let length = array.len();
let index = usize::try_from(index).map_err(|_| ArrayIndexOutOfBoundsException {
index: original_index,
length,
})?;
let Some(value) = array.get(index) else {
return Err(ArrayIndexOutOfBoundsException {
index: original_index,
length,
}
.into());
};
stack.push_long(*value)?;
Ok(Continue)
}
#[inline]
pub(crate) fn lastore(stack: &mut OperandStack) -> Result<ExecutionResult> {
let value = stack.pop_long()?;
let index = stack.pop_int()?;
let Some(reference) = stack.pop_object()? else {
return Err(NullPointerException(None).into());
};
let mut guard = reference.write();
let Reference::LongArray(array) = &mut *guard else {
return Err(InvalidStackValue {
expected: "long array".to_string(),
actual: guard.to_string(),
});
};
let length = array.len();
let original_index = index;
let index = usize::try_from(index).map_err(|_| ArrayIndexOutOfBoundsException {
index: original_index,
length,
})?;
if let Some(element) = array.get_mut(index) {
*element = value;
} else {
return Err(ArrayIndexOutOfBoundsException {
index: original_index,
length,
}
.into());
}
Ok(Continue)
}
#[inline]
pub(crate) fn ladd(stack: &mut OperandStack) -> Result<ExecutionResult> {
let value2 = stack.pop_long()?;
let value1 = stack.pop_long()?;
stack.push_long(i64::wrapping_add(value1, value2))?;
Ok(Continue)
}
#[inline]
pub(crate) fn lsub(stack: &mut OperandStack) -> Result<ExecutionResult> {
let value2 = stack.pop_long()?;
let value1 = stack.pop_long()?;
stack.push_long(i64::wrapping_sub(value1, value2))?;
Ok(Continue)
}
#[inline]
pub(crate) fn lmul(stack: &mut OperandStack) -> Result<ExecutionResult> {
let value2 = stack.pop_long()?;
let value1 = stack.pop_long()?;
stack.push_long(i64::wrapping_mul(value1, value2))?;
Ok(Continue)
}
#[inline]
pub(crate) fn ldiv(stack: &mut OperandStack) -> Result<ExecutionResult> {
let value2 = stack.pop_long()?;
let value1 = stack.pop_long()?;
if value2 == 0 {
return Err(ArithmeticException("/ by zero".to_string()).into());
}
stack.push_long(value1.wrapping_div(value2))?;
Ok(Continue)
}
#[inline]
pub(crate) fn lrem(stack: &mut OperandStack) -> Result<ExecutionResult> {
let value2 = stack.pop_long()?;
let value1 = stack.pop_long()?;
if value2 == 0 {
return Err(ArithmeticException("/ by zero".to_string()).into());
}
stack.push_long(value1.wrapping_rem(value2))?;
Ok(Continue)
}
#[inline]
pub(crate) fn lneg(stack: &mut OperandStack) -> Result<ExecutionResult> {
let value = stack.pop_long()?;
stack.push_long(value.wrapping_neg())?;
Ok(Continue)
}
#[inline]
pub(crate) fn lshl(stack: &mut OperandStack) -> Result<ExecutionResult> {
let value2 = stack.pop_int()?;
let value1 = stack.pop_long()?;
stack.push_long(value1 << (value2 & 0x3f))?;
Ok(Continue)
}
#[inline]
pub(crate) fn lshr(stack: &mut OperandStack) -> Result<ExecutionResult> {
let value2 = stack.pop_int()?;
let value1 = stack.pop_long()?;
stack.push_long(value1 >> (value2 & 0x3f))?;
Ok(Continue)
}
#[inline]
pub(crate) fn lushr(stack: &mut OperandStack) -> Result<ExecutionResult> {
let value2 = stack.pop_int()?;
let value1 = stack.pop_long()?;
#[expect(clippy::cast_sign_loss)]
let value1 = value1 as u64;
let result = value1 >> (value2 & 0x3f);
let result: i64 = zerocopy::transmute!(result);
stack.push_long(result)?;
Ok(Continue)
}
#[inline]
pub(crate) fn land(stack: &mut OperandStack) -> Result<ExecutionResult> {
let value2 = stack.pop_long()?;
let value1 = stack.pop_long()?;
stack.push_long(value1 & value2)?;
Ok(Continue)
}
#[inline]
pub(crate) fn lor(stack: &mut OperandStack) -> Result<ExecutionResult> {
let value2 = stack.pop_long()?;
let value1 = stack.pop_long()?;
stack.push_long(value1 | value2)?;
Ok(Continue)
}
#[inline]
pub(crate) fn lxor(stack: &mut OperandStack) -> Result<ExecutionResult> {
let value2 = stack.pop_long()?;
let value1 = stack.pop_long()?;
stack.push_long(value1 ^ value2)?;
Ok(Continue)
}
#[inline]
pub(crate) fn lcmp(stack: &mut OperandStack) -> Result<ExecutionResult> {
let value2 = stack.pop_long()?;
let value1 = stack.pop_long()?;
let cmp = match value1.cmp(&value2) {
Ordering::Greater => 1,
Ordering::Less => -1,
Ordering::Equal => 0,
};
stack.push_int(cmp)?;
Ok(Continue)
}
#[inline]
pub(crate) fn lreturn(stack: &mut OperandStack) -> Result<ExecutionResult> {
let value = stack.pop_long()?;
Ok(Return(Some(Value::Long(value))))
}
#[cfg(test)]
mod tests {
use super::*;
use crate::Error::{InvalidOperand, JavaError};
use crate::JavaError::ArithmeticException;
#[test]
fn test_lconst_0() -> Result<()> {
let stack = &mut OperandStack::with_max_size(1);
let result = lconst_0(stack)?;
assert_eq!(Continue, result);
assert_eq!(0, stack.pop_long()?);
Ok(())
}
#[test]
fn test_lconst_1() -> Result<()> {
let stack = &mut OperandStack::with_max_size(1);
let result = lconst_1(stack)?;
assert_eq!(Continue, result);
assert_eq!(1, stack.pop_long()?);
Ok(())
}
#[test]
fn test_lload() -> Result<()> {
let mut locals = LocalVariables::with_max_size(1);
locals.set_long(0, 42)?;
let stack = &mut OperandStack::with_max_size(1);
let result = lload(&locals, stack, 0)?;
assert_eq!(Continue, result);
assert_eq!(42, stack.pop_long()?);
Ok(())
}
#[test]
fn test_lload_w() -> Result<()> {
let mut locals = LocalVariables::with_max_size(1);
locals.set_long(0, 42)?;
let stack = &mut OperandStack::with_max_size(1);
let result = lload_w(&locals, stack, 0)?;
assert_eq!(Continue, result);
assert_eq!(42, stack.pop_long()?);
Ok(())
}
#[test]
fn test_lload_0() -> Result<()> {
let mut locals = LocalVariables::with_max_size(1);
locals.set_long(0, 42)?;
let stack = &mut OperandStack::with_max_size(1);
let result = lload_0(&locals, stack)?;
assert_eq!(Continue, result);
assert_eq!(42, stack.pop_long()?);
Ok(())
}
#[test]
fn test_lload_1() -> Result<()> {
let mut locals = LocalVariables::with_max_size(2);
locals.set_long(1, 42)?;
let stack = &mut OperandStack::with_max_size(1);
let result = lload_1(&locals, stack)?;
assert_eq!(Continue, result);
assert_eq!(42, stack.pop_long()?);
Ok(())
}
#[test]
fn test_lload_2() -> Result<()> {
let mut locals = LocalVariables::with_max_size(3);
locals.set_long(2, 42)?;
let stack = &mut OperandStack::with_max_size(1);
let result = lload_2(&locals, stack)?;
assert_eq!(Continue, result);
assert_eq!(42, stack.pop_long()?);
Ok(())
}
#[test]
fn test_lload_3() -> Result<()> {
let mut locals = LocalVariables::with_max_size(4);
locals.set_long(3, 42)?;
let stack = &mut OperandStack::with_max_size(1);
let result = lload_3(&locals, stack)?;
assert_eq!(Continue, result);
assert_eq!(42, stack.pop_long()?);
Ok(())
}
#[test]
fn test_lstore() -> Result<()> {
let locals = &mut LocalVariables::with_max_size(1);
let stack = &mut OperandStack::with_max_size(1);
stack.push_long(42)?;
let result = lstore(locals, stack, 0)?;
assert_eq!(Continue, result);
assert_eq!(42, locals.get_long(0)?);
Ok(())
}
#[test]
fn test_lstore_w() -> Result<()> {
let locals = &mut LocalVariables::with_max_size(1);
let stack = &mut OperandStack::with_max_size(1);
stack.push_long(42)?;
let result = lstore_w(locals, stack, 0)?;
assert_eq!(Continue, result);
assert_eq!(42, locals.get_long(0)?);
Ok(())
}
#[test]
fn test_lstore_0() -> Result<()> {
let locals = &mut LocalVariables::with_max_size(1);
let stack = &mut OperandStack::with_max_size(1);
stack.push_long(42)?;
let result = lstore_0(locals, stack)?;
assert_eq!(Continue, result);
assert_eq!(42, locals.get_long(0)?);
Ok(())
}
#[test]
fn test_lstore_1() -> Result<()> {
let locals = &mut LocalVariables::with_max_size(2);
let stack = &mut OperandStack::with_max_size(1);
stack.push_long(42)?;
let result = lstore_1(locals, stack)?;
assert_eq!(Continue, result);
assert_eq!(42, locals.get_long(1)?);
Ok(())
}
#[test]
fn test_lstore_2() -> Result<()> {
let locals = &mut LocalVariables::with_max_size(3);
let stack = &mut OperandStack::with_max_size(1);
stack.push_long(42)?;
let result = lstore_2(locals, stack)?;
assert_eq!(Continue, result);
assert_eq!(42, locals.get_long(2)?);
Ok(())
}
#[test]
fn test_lstore_3() -> Result<()> {
let locals = &mut LocalVariables::with_max_size(4);
let stack = &mut OperandStack::with_max_size(1);
stack.push_long(42)?;
let result = lstore_3(locals, stack)?;
assert_eq!(Continue, result);
assert_eq!(42, locals.get_long(3)?);
Ok(())
}
#[tokio::test]
async fn test_laload() -> Result<()> {
let (_vm, thread) = crate::test::thread().await?;
let stack = &mut OperandStack::with_max_size(2);
let reference = Reference::from(vec![42i64]);
let array = Value::new_object(thread.vm()?.garbage_collector(), reference);
stack.push(array)?;
stack.push_int(0)?;
let result = laload(stack)?;
assert_eq!(Continue, result);
assert_eq!(42, stack.pop_long()?);
Ok(())
}
#[tokio::test]
async fn test_laload_invalid_value() -> Result<()> {
let (_vm, thread) = crate::test::thread().await?;
let stack = &mut OperandStack::with_max_size(2);
let reference = Reference::from(vec![42i32]);
let object = Value::new_object(thread.vm()?.garbage_collector(), reference);
stack.push(object)?;
stack.push_int(2)?;
let result = laload(stack);
assert!(matches!(
result,
Err(InvalidStackValue {
expected,
actual
}) if expected == "long array" && actual == "int[42]"
));
Ok(())
}
#[tokio::test]
async fn test_laload_invalid_index_negative() -> Result<()> {
let (_vm, thread) = crate::test::thread().await?;
let stack = &mut OperandStack::with_max_size(2);
let reference = Reference::from(vec![42i64]);
let array = Value::new_object(thread.vm()?.garbage_collector(), reference);
stack.push(array)?;
stack.push_int(-1)?;
let result = laload(stack);
assert!(matches!(
result,
Err(JavaError(ArrayIndexOutOfBoundsException { index, length }))
if index == -1 && length == 1
));
Ok(())
}
#[tokio::test]
async fn test_laload_invalid_index() -> Result<()> {
let (_vm, thread) = crate::test::thread().await?;
let stack = &mut OperandStack::with_max_size(2);
let reference = Reference::from(vec![42i64]);
let array = Value::new_object(thread.vm()?.garbage_collector(), reference);
stack.push(array)?;
stack.push_int(2)?;
let result = laload(stack);
assert!(matches!(
result,
Err(JavaError(ArrayIndexOutOfBoundsException { index, length }))
if index == 2 && length == 1
));
Ok(())
}
#[test]
fn test_laload_null_pointer() -> Result<()> {
let stack = &mut OperandStack::with_max_size(2);
stack.push_object(None)?;
stack.push_int(0)?;
let result = laload(stack);
assert!(matches!(result, Err(JavaError(NullPointerException(None)))));
Ok(())
}
#[tokio::test]
async fn test_lastore() -> Result<()> {
let (_vm, thread) = crate::test::thread().await?;
let stack = &mut OperandStack::with_max_size(3);
let reference = Reference::from(vec![3i64]);
let array = Value::new_object(thread.vm()?.garbage_collector(), reference);
stack.push(array)?;
stack.push_int(0)?;
stack.push_long(42)?;
let result = lastore(stack)?;
assert_eq!(Continue, result);
Ok(())
}
#[tokio::test]
async fn test_lastore_invalid_value() -> Result<()> {
let (_vm, thread) = crate::test::thread().await?;
let stack = &mut OperandStack::with_max_size(3);
let reference = Reference::from(vec![42i32]);
let object = Value::new_object(thread.vm()?.garbage_collector(), reference);
stack.push(object)?;
stack.push_int(2)?;
stack.push_long(42)?;
let result = lastore(stack);
assert!(matches!(
result,
Err(InvalidStackValue {
expected,
actual
}) if expected == "long array" && actual == "int[42]"
));
Ok(())
}
#[tokio::test]
async fn test_lastore_invalid_index_negative() -> Result<()> {
let (_vm, thread) = crate::test::thread().await?;
let stack = &mut OperandStack::with_max_size(3);
let reference = Reference::from(vec![3i64]);
let array = Value::new_object(thread.vm()?.garbage_collector(), reference);
stack.push(array)?;
stack.push_int(-1)?;
stack.push_long(42)?;
let result = lastore(stack);
assert!(matches!(
result,
Err(JavaError(ArrayIndexOutOfBoundsException { index, length }))
if index == -1 && length == 1
));
Ok(())
}
#[tokio::test]
async fn test_lastore_invalid_index() -> Result<()> {
let (_vm, thread) = crate::test::thread().await?;
let stack = &mut OperandStack::with_max_size(3);
let reference = Reference::from(vec![3i64]);
let array = Value::new_object(thread.vm()?.garbage_collector(), reference);
stack.push(array)?;
stack.push_int(2)?;
stack.push_long(42)?;
let result = lastore(stack);
assert!(matches!(
result,
Err(JavaError(ArrayIndexOutOfBoundsException { index, length }))
if index == 2 && length == 1
));
Ok(())
}
#[test]
fn test_lastore_null_pointer() -> Result<()> {
let stack = &mut OperandStack::with_max_size(3);
stack.push_object(None)?;
stack.push_int(0)?;
stack.push_long(42)?;
let result = lastore(stack);
assert!(matches!(result, Err(JavaError(NullPointerException(None)))));
Ok(())
}
#[test]
fn test_ladd() -> Result<()> {
let stack = &mut OperandStack::with_max_size(2);
stack.push_long(1)?;
stack.push_long(2)?;
let result = ladd(stack)?;
assert_eq!(Continue, result);
assert_eq!(3, stack.pop_long()?);
Ok(())
}
#[test]
fn test_ladd_overflow() -> Result<()> {
let stack = &mut OperandStack::with_max_size(2);
stack.push_long(i64::MAX)?;
stack.push_long(1)?;
let result = ladd(stack)?;
assert_eq!(Continue, result);
assert_eq!(i64::MIN, stack.pop_long()?);
Ok(())
}
#[test]
fn test_lsub() -> Result<()> {
let stack = &mut OperandStack::with_max_size(2);
stack.push_long(2)?;
stack.push_long(1)?;
let result = lsub(stack)?;
assert_eq!(Continue, result);
assert_eq!(1, stack.pop_long()?);
Ok(())
}
#[test]
fn test_lsub_underflow() -> Result<()> {
let stack = &mut OperandStack::with_max_size(2);
stack.push_long(i64::MIN)?;
stack.push_long(1)?;
let result = lsub(stack)?;
assert_eq!(Continue, result);
assert_eq!(i64::MAX, stack.pop_long()?);
Ok(())
}
#[test]
fn test_lmul() -> Result<()> {
let stack = &mut OperandStack::with_max_size(2);
stack.push_long(2)?;
stack.push_long(3)?;
let result = lmul(stack)?;
assert_eq!(Continue, result);
assert_eq!(6, stack.pop_long()?);
Ok(())
}
#[test]
fn test_ldiv() -> Result<()> {
let stack = &mut OperandStack::with_max_size(2);
stack.push_long(6)?;
stack.push_long(3)?;
let result = ldiv(stack)?;
assert_eq!(Continue, result);
assert_eq!(2, stack.pop_long()?);
Ok(())
}
#[test]
fn test_ldiv_divide_by_zero() -> Result<()> {
let stack = &mut OperandStack::with_max_size(2);
stack.push_long(1)?;
stack.push_long(0)?;
let result = ldiv(stack);
assert!(matches!(result, Err(JavaError(ArithmeticException(_)))));
Ok(())
}
#[test]
fn test_lrem() -> Result<()> {
let stack = &mut OperandStack::with_max_size(2);
stack.push_long(1)?;
stack.push_long(2)?;
let result = lrem(stack)?;
assert_eq!(Continue, result);
assert_eq!(1, stack.pop_long()?);
Ok(())
}
#[test]
fn test_lrem_divide_by_zero() -> Result<()> {
let stack = &mut OperandStack::with_max_size(2);
stack.push_long(1)?;
stack.push_long(0)?;
let result = lrem(stack);
assert!(matches!(result, Err(JavaError(ArithmeticException(_)))));
Ok(())
}
#[test]
fn test_lneg() -> Result<()> {
let stack = &mut OperandStack::with_max_size(1);
stack.push_long(1)?;
let result = lneg(stack)?;
assert_eq!(Continue, result);
assert_eq!(-1, stack.pop_long()?);
Ok(())
}
#[test]
fn test_lneg_minimum() -> Result<()> {
let stack = &mut OperandStack::with_max_size(1);
stack.push_long(i64::MIN)?;
let result = lneg(stack)?;
assert_eq!(Continue, result);
assert_eq!(i64::MIN, stack.pop_long()?);
Ok(())
}
#[test]
fn test_lshl() -> Result<()> {
let stack = &mut OperandStack::with_max_size(2);
stack.push_long(4)?;
stack.push_int(1)?;
let result = lshl(stack)?;
assert_eq!(Continue, result);
assert_eq!(8, stack.pop_long()?);
Ok(())
}
#[test]
fn test_lshr() -> Result<()> {
let stack = &mut OperandStack::with_max_size(2);
stack.push_long(4)?;
stack.push_int(1)?;
let result = lshr(stack)?;
assert_eq!(Continue, result);
assert_eq!(2, stack.pop_long()?);
Ok(())
}
#[test]
fn test_lushr() -> Result<()> {
let stack = &mut OperandStack::with_max_size(2);
stack.push_long(4)?;
stack.push_int(1)?;
let result = lushr(stack)?;
assert_eq!(Continue, result);
assert_eq!(2, stack.pop_long()?);
Ok(())
}
#[test]
fn test_lushr_mask() -> Result<()> {
let stack = &mut OperandStack::with_max_size(2);
stack.push_long(57_558_190_860)?;
stack.push_int(32)?;
let result = lushr(stack)?;
assert_eq!(Continue, result);
assert_eq!(13, stack.pop_long()?);
Ok(())
}
#[test]
fn test_lushr_negative_value1() -> Result<()> {
let stack = &mut OperandStack::with_max_size(2);
stack.push_long(-1)?;
stack.push_int(60)?;
let result = lushr(stack)?;
assert_eq!(Continue, result);
assert_eq!(15, stack.pop_long()?);
Ok(())
}
#[test]
fn test_land() -> Result<()> {
let stack = &mut OperandStack::with_max_size(2);
stack.push_long(2)?;
stack.push_long(3)?;
let result = land(stack)?;
assert_eq!(Continue, result);
assert_eq!(2, stack.pop_long()?);
Ok(())
}
#[test]
fn test_lor() -> Result<()> {
let stack = &mut OperandStack::with_max_size(2);
stack.push_long(2)?;
stack.push_long(4)?;
let result = lor(stack)?;
assert_eq!(Continue, result);
assert_eq!(6, stack.pop_long()?);
Ok(())
}
#[test]
fn test_lxor() -> Result<()> {
let stack = &mut OperandStack::with_max_size(2);
stack.push_long(2)?;
stack.push_long(3)?;
let result = lxor(stack)?;
assert_eq!(Continue, result);
assert_eq!(1, stack.pop_long()?);
Ok(())
}
#[test]
fn test_lcmp_equal() -> Result<()> {
let stack = &mut OperandStack::with_max_size(2);
stack.push_long(1)?;
stack.push_long(1)?;
let result = lcmp(stack)?;
assert_eq!(Continue, result);
assert_eq!(0, stack.pop_int()?);
Ok(())
}
#[test]
fn test_lcmp_greater_than() -> Result<()> {
let stack = &mut OperandStack::with_max_size(2);
stack.push_long(2)?;
stack.push_long(1)?;
let result = lcmp(stack)?;
assert_eq!(Continue, result);
assert_eq!(1, stack.pop_int()?);
Ok(())
}
#[test]
fn test_lcmp_less_than() -> Result<()> {
let stack = &mut OperandStack::with_max_size(2);
stack.push_long(1)?;
stack.push_long(2)?;
let result = lcmp(stack)?;
assert_eq!(Continue, result);
assert_eq!(-1, stack.pop_int()?);
Ok(())
}
#[test]
fn test_lreturn() -> Result<()> {
let stack = &mut OperandStack::with_max_size(1);
stack.push_long(42)?;
let result = lreturn(stack)?;
assert!(matches!(result, Return(Some(Value::Long(42)))));
Ok(())
}
#[test]
fn test_lreturn_invalid_type() -> Result<()> {
let stack = &mut OperandStack::with_max_size(1);
stack.push_object(None)?;
let result = lreturn(stack);
assert!(matches!(
result,
Err(InvalidOperand {
expected,
actual
}) if expected == "long" && actual == "Object(null)"
));
Ok(())
}
}