diff --git a/tests/should_perform_calculations_with_overflow.rs b/tests/should_perform_calculations_with_overflow.rs new file mode 100644 index 00000000..6f3c3173 --- /dev/null +++ b/tests/should_perform_calculations_with_overflow.rs @@ -0,0 +1,9 @@ +mod utils; +use utils::get_int; +use vm::vm::VM; + +#[test] +fn should_perform_calculations_with_overflow() { + let last_frame_value = VM::run("samples.arithmetics.overflow.ArithmeticOverflow").unwrap(); + assert_eq!(1, get_int(last_frame_value)) +} diff --git a/tests/test_data/ArithmeticOverflow.java b/tests/test_data/ArithmeticOverflow.java new file mode 100644 index 00000000..a703ad70 --- /dev/null +++ b/tests/test_data/ArithmeticOverflow.java @@ -0,0 +1,184 @@ +package samples.arithmetics.overflow; + +public class ArithmeticOverflow { + public static void main(String[] args) { + int intsResult = intsOverflow(); + int longsResult = longsOverflow(); + + boolean result = intsResult == 511 && longsResult == 511; + } + + private static int intsOverflow() { + // Add + int addOverflow = getAddOverflow(1); + int bit0 = addOverflow == -2147483648 ? 1 : 0; + + // Multiply + int mulOverflow = getMulOverflow(2); + int bit1 = mulOverflow == -2 ? 1 : 0; + + // Division + int divOverflow = getDivOverflow(-1); // Causes overflow + int bit2 = divOverflow == -2147483648 ? 1 : 0; + + // Negation + int negOverflow = getNegOverflow(Integer.MIN_VALUE); // Integer.MIN_VALUE has no positive counterpart + int bit3 = negOverflow == -2147483648 ? 1 : 0; + + // Remainder + int remOverflow = getRemOverflow(-1); // Same as division overflow + int bit4 = remOverflow == 0 ? 1 : 0; + + // Shift Left + int shlOverflow = getShlOverflow(1); // Shifting may cause bits to overflow + int bit5 = shlOverflow == -2 ? 1 : 0; + + // Shift Right + int shrOverflow = getShrOverflow(1); // Arithmetic shift retains sign, but no overflow + int bit6 = shrOverflow == -1073741824 ? 1 : 0; + + int uShlOverflow = getUShlOverflow(1);// Logical shift retains sign, but no overflow + int bit7 = uShlOverflow == 1073741824 ? 1 : 0; + + // Subtract + int subOverflow = getSubOverflow(1); + int bit8 = subOverflow == 2147483647 ? 1 : 0; + + int result = 0; + result = setBit(result, 0, bit0); + result = setBit(result, 1, bit1); + result = setBit(result, 2, bit2); + result = setBit(result, 3, bit3); + result = setBit(result, 4, bit4); + result = setBit(result, 5, bit5); + result = setBit(result, 6, bit6); + result = setBit(result, 7, bit7); + result = setBit(result, 8, bit8); + return result; + } + + private static int longsOverflow() { + // Add + long addOverflow = getAddOverflow(1L); + int bit0 = addOverflow == Long.MIN_VALUE ? 1 : 0; + + // Multiply + long mulOverflow = getMulOverflow(2L); + int bit1 = mulOverflow == -2L ? 1 : 0; + + // Division + long divOverflow = getDivOverflow(-1L); // Causes overflow + int bit2 = divOverflow == Long.MIN_VALUE ? 1 : 0; + + // Negation + long negOverflow = getNegOverflow(Long.MIN_VALUE); // Long.MIN_VALUE has no positive counterpart + int bit3 = negOverflow == Long.MIN_VALUE ? 1 : 0; + + // Remainder + long remOverflow = getRemOverflow(-1L); // Same as division overflow + int bit4 = remOverflow == 0L ? 1 : 0; + + // Shift Left + long shlOverflow = getShlOverflow(1L); // Shifting may cause bits to overflow + int bit5 = shlOverflow == -2L ? 1 : 0; + + // Shift Right + long shrOverflow = getShrOverflow(1L); // Arithmetic shift retains sign, but no overflow + int bit6 = shrOverflow == -4611686018427387904L ? 1 : 0; + + long uShlOverflow = getUShlOverflow(1L); // Logical shift retains sign, but no overflow + int bit7 = uShlOverflow == 4611686018427387904L ? 1 : 0; + + // Subtract + long subOverflow = getSubOverflow(1L); + int bit8 = subOverflow == Long.MAX_VALUE ? 1 : 0; + + int result = 0; + result = setBit(result, 0, bit0); + result = setBit(result, 1, bit1); + result = setBit(result, 2, bit2); + result = setBit(result, 3, bit3); + result = setBit(result, 4, bit4); + result = setBit(result, 5, bit5); + result = setBit(result, 6, bit6); + result = setBit(result, 7, bit7); + result = setBit(result, 8, bit8); + return result; + } + + private static int getSubOverflow(int value) { + return Integer.MIN_VALUE - value; + } + + private static int getShrOverflow(int value) { + return Integer.MIN_VALUE >> value; + } + + private static int getShlOverflow(int value) { + return Integer.MAX_VALUE << value; + } + + private static int getUShlOverflow(int value) { + return Integer.MIN_VALUE >>> value; + } + + private static int getRemOverflow(int value) { + return Integer.MIN_VALUE % value; + } + + private static int getNegOverflow(int value) { + return -value; + } + + private static int getDivOverflow(int value) { + return Integer.MIN_VALUE / value; + } + + private static int getMulOverflow(int value) { + return Integer.MAX_VALUE * value; + } + + private static int getAddOverflow(int value) { + return Integer.MAX_VALUE + value; + } + + private static long getSubOverflow(long value) { + return Long.MIN_VALUE - value; + } + + private static long getShrOverflow(long value) { + return Long.MIN_VALUE >> value; + } + + private static long getShlOverflow(long value) { + return Long.MAX_VALUE << value; + } + + private static long getUShlOverflow(long value) { + return Long.MIN_VALUE >>> value; + } + + private static long getRemOverflow(long value) { + return Long.MIN_VALUE % value; + } + + private static long getNegOverflow(long value) { + return -value; + } + + private static long getDivOverflow(long value) { + return Long.MIN_VALUE / value; + } + + private static long getMulOverflow(long value) { + return Long.MAX_VALUE * value; + } + + private static long getAddOverflow(long value) { + return Long.MAX_VALUE + value; + } + + private static int setBit(int num, int position, int value) { + return value == 0 ? num & ~(1 << position) : num | (1 << position); + } +} diff --git a/tests/test_data/samples/arithmetics/overflow/ArithmeticOverflow.class b/tests/test_data/samples/arithmetics/overflow/ArithmeticOverflow.class new file mode 100644 index 00000000..fc7c35f5 Binary files /dev/null and b/tests/test_data/samples/arithmetics/overflow/ArithmeticOverflow.class differ diff --git a/vm/src/execution_engine/ops_math_processor.rs b/vm/src/execution_engine/ops_math_processor.rs index c3b35386..7998ccf6 100644 --- a/vm/src/execution_engine/ops_math_processor.rs +++ b/vm/src/execution_engine/ops_math_processor.rs @@ -8,7 +8,7 @@ pub(crate) fn process(code: u8, stack_frames: &mut Vec) -> crate::er IADD => { let b: i32 = stack_frame.pop(); let a: i32 = stack_frame.pop(); - let result = a + b; + let result = a.wrapping_add(b); stack_frame.push(result); stack_frame.incr_pc(); @@ -48,7 +48,7 @@ pub(crate) fn process(code: u8, stack_frames: &mut Vec) -> crate::er ISUB => { let b: i32 = stack_frame.pop(); let a: i32 = stack_frame.pop(); - let result = a - b; + let result = a.wrapping_sub(b); stack_frame.push(result); stack_frame.incr_pc(); @@ -58,7 +58,7 @@ pub(crate) fn process(code: u8, stack_frames: &mut Vec) -> crate::er let b: i64 = stack_frame.pop(); let a: i64 = stack_frame.pop(); - let result = a - b; + let result = a.wrapping_sub(b); stack_frame.push(result); @@ -128,7 +128,7 @@ pub(crate) fn process(code: u8, stack_frames: &mut Vec) -> crate::er IDIV => { let b: i32 = stack_frame.pop(); let a: i32 = stack_frame.pop(); - let result = a / b; //todo add check for ArithmeticException here + let result = a.wrapping_div(b); //todo add check for ArithmeticException here stack_frame.push(result); stack_frame.incr_pc(); @@ -138,7 +138,7 @@ pub(crate) fn process(code: u8, stack_frames: &mut Vec) -> crate::er let b: i64 = stack_frame.pop(); let a: i64 = stack_frame.pop(); - let result = a / b; //todo add check for ArithmeticException here + let result = a.wrapping_div(b); //todo add check for ArithmeticException here stack_frame.push(result); @@ -168,7 +168,7 @@ pub(crate) fn process(code: u8, stack_frames: &mut Vec) -> crate::er IREM => { let b: i32 = stack_frame.pop(); let a: i32 = stack_frame.pop(); - let result = a % b; + let result = a.wrapping_rem(b); stack_frame.push(result); stack_frame.incr_pc(); @@ -178,7 +178,7 @@ pub(crate) fn process(code: u8, stack_frames: &mut Vec) -> crate::er let b: i64 = stack_frame.pop(); let a: i64 = stack_frame.pop(); - let result = a % b; + let result = a.wrapping_rem(b); stack_frame.push(result); @@ -198,7 +198,7 @@ pub(crate) fn process(code: u8, stack_frames: &mut Vec) -> crate::er } INEG => { let value: i32 = stack_frame.pop(); - let result = -value; + let result = value.wrapping_neg(); stack_frame.push(result); stack_frame.incr_pc(); @@ -206,7 +206,7 @@ pub(crate) fn process(code: u8, stack_frames: &mut Vec) -> crate::er } LNEG => { let value: i64 = stack_frame.pop(); - let result = -value; + let result = value.wrapping_neg(); stack_frame.push(result); stack_frame.incr_pc(); @@ -266,7 +266,7 @@ pub(crate) fn process(code: u8, stack_frames: &mut Vec) -> crate::er stack_frame.push(result); stack_frame.incr_pc(); - trace!("IUSHR -> {a} >> {b} = {result}"); + trace!("IUSHR -> {a} >>> {b} = {result}"); } LUSHR => { let b: i32 = stack_frame.pop(); @@ -277,7 +277,7 @@ pub(crate) fn process(code: u8, stack_frames: &mut Vec) -> crate::er stack_frame.push(result); stack_frame.incr_pc(); - trace!("LUSHR -> {a} >> {b} = {result}"); + trace!("LUSHR -> {a} >>> {b} = {result}"); } IAND => { let b: i32 = stack_frame.pop();