use crate::stack::{Stack, peek, pop, pop_two, push};
use crate::value::Value;
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_push_int(stack: Stack, value: i64) -> Stack {
unsafe { push(stack, Value::Int(value)) }
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_push_bool(stack: Stack, value: bool) -> Stack {
unsafe { push(stack, Value::Bool(value)) }
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_add(stack: Stack) -> Stack {
let (rest, a, b) = unsafe { pop_two(stack, "add") };
match (a, b) {
(Value::Int(a_val), Value::Int(b_val)) => unsafe {
push(rest, Value::Int(a_val.wrapping_add(b_val)))
},
_ => panic!("add: expected two integers on stack"),
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_subtract(stack: Stack) -> Stack {
let (rest, a, b) = unsafe { pop_two(stack, "subtract") };
match (a, b) {
(Value::Int(a_val), Value::Int(b_val)) => unsafe {
push(rest, Value::Int(a_val.wrapping_sub(b_val)))
},
_ => panic!("subtract: expected two integers on stack"),
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_multiply(stack: Stack) -> Stack {
let (rest, a, b) = unsafe { pop_two(stack, "multiply") };
match (a, b) {
(Value::Int(a_val), Value::Int(b_val)) => unsafe {
push(rest, Value::Int(a_val.wrapping_mul(b_val)))
},
_ => panic!("multiply: expected two integers on stack"),
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_divide(stack: Stack) -> Stack {
let (rest, a, b) = unsafe { pop_two(stack, "divide") };
match (a, b) {
(Value::Int(_a_val), Value::Int(0)) => {
let stack = unsafe { push(rest, Value::Int(0)) };
unsafe { push(stack, Value::Bool(false)) }
}
(Value::Int(a_val), Value::Int(b_val)) => {
let stack = unsafe { push(rest, Value::Int(a_val.wrapping_div(b_val))) };
unsafe { push(stack, Value::Bool(true)) }
}
_ => {
panic!("divide: expected two integers on stack");
}
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_modulo(stack: Stack) -> Stack {
let (rest, a, b) = unsafe { pop_two(stack, "modulo") };
match (a, b) {
(Value::Int(_a_val), Value::Int(0)) => {
let stack = unsafe { push(rest, Value::Int(0)) };
unsafe { push(stack, Value::Bool(false)) }
}
(Value::Int(a_val), Value::Int(b_val)) => {
let stack = unsafe { push(rest, Value::Int(a_val.wrapping_rem(b_val))) };
unsafe { push(stack, Value::Bool(true)) }
}
_ => {
panic!("modulo: expected two integers on stack");
}
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_eq(stack: Stack) -> Stack {
let (rest, a, b) = unsafe { pop_two(stack, "=") };
match (a, b) {
(Value::Int(a_val), Value::Int(b_val)) => unsafe {
push(rest, Value::Bool(a_val == b_val))
},
_ => panic!("=: expected two integers on stack"),
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_lt(stack: Stack) -> Stack {
let (rest, a, b) = unsafe { pop_two(stack, "<") };
match (a, b) {
(Value::Int(a_val), Value::Int(b_val)) => unsafe { push(rest, Value::Bool(a_val < b_val)) },
_ => panic!("<: expected two integers on stack"),
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_gt(stack: Stack) -> Stack {
let (rest, a, b) = unsafe { pop_two(stack, ">") };
match (a, b) {
(Value::Int(a_val), Value::Int(b_val)) => unsafe { push(rest, Value::Bool(a_val > b_val)) },
_ => panic!(">: expected two integers on stack"),
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_lte(stack: Stack) -> Stack {
let (rest, a, b) = unsafe { pop_two(stack, "<=") };
match (a, b) {
(Value::Int(a_val), Value::Int(b_val)) => unsafe {
push(rest, Value::Bool(a_val <= b_val))
},
_ => panic!("<=: expected two integers on stack"),
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_gte(stack: Stack) -> Stack {
let (rest, a, b) = unsafe { pop_two(stack, ">=") };
match (a, b) {
(Value::Int(a_val), Value::Int(b_val)) => unsafe {
push(rest, Value::Bool(a_val >= b_val))
},
_ => panic!(">=: expected two integers on stack"),
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_neq(stack: Stack) -> Stack {
let (rest, a, b) = unsafe { pop_two(stack, "<>") };
match (a, b) {
(Value::Int(a_val), Value::Int(b_val)) => unsafe {
push(rest, Value::Bool(a_val != b_val))
},
_ => panic!("<>: expected two integers on stack"),
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_and(stack: Stack) -> Stack {
let (rest, a, b) = unsafe { pop_two(stack, "and") };
match (a, b) {
(Value::Int(a_val), Value::Int(b_val)) => unsafe {
push(
rest,
Value::Int(if a_val != 0 && b_val != 0 { 1 } else { 0 }),
)
},
_ => panic!("and: expected two integers on stack"),
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_or(stack: Stack) -> Stack {
let (rest, a, b) = unsafe { pop_two(stack, "or") };
match (a, b) {
(Value::Int(a_val), Value::Int(b_val)) => unsafe {
push(
rest,
Value::Int(if a_val != 0 || b_val != 0 { 1 } else { 0 }),
)
},
_ => panic!("or: expected two integers on stack"),
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_not(stack: Stack) -> Stack {
assert!(!stack.is_null(), "not: stack is empty");
let (rest, a) = unsafe { pop(stack) };
match a {
Value::Int(a_val) => unsafe { push(rest, Value::Int(if a_val == 0 { 1 } else { 0 })) },
_ => panic!("not: expected integer on stack"),
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_band(stack: Stack) -> Stack {
let (rest, a, b) = unsafe { pop_two(stack, "band") };
match (a, b) {
(Value::Int(a_val), Value::Int(b_val)) => unsafe { push(rest, Value::Int(a_val & b_val)) },
_ => panic!("band: expected two integers on stack"),
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_bor(stack: Stack) -> Stack {
let (rest, a, b) = unsafe { pop_two(stack, "bor") };
match (a, b) {
(Value::Int(a_val), Value::Int(b_val)) => unsafe { push(rest, Value::Int(a_val | b_val)) },
_ => panic!("bor: expected two integers on stack"),
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_bxor(stack: Stack) -> Stack {
let (rest, a, b) = unsafe { pop_two(stack, "bxor") };
match (a, b) {
(Value::Int(a_val), Value::Int(b_val)) => unsafe { push(rest, Value::Int(a_val ^ b_val)) },
_ => panic!("bxor: expected two integers on stack"),
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_bnot(stack: Stack) -> Stack {
assert!(!stack.is_null(), "bnot: stack is empty");
let (rest, a) = unsafe { pop(stack) };
match a {
Value::Int(a_val) => unsafe { push(rest, Value::Int(!a_val)) },
_ => panic!("bnot: expected integer on stack"),
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_shl(stack: Stack) -> Stack {
let (rest, value, count) = unsafe { pop_two(stack, "shl") };
match (value, count) {
(Value::Int(v), Value::Int(c)) => {
let result = if c < 0 {
0
} else {
v.checked_shl(c as u32).unwrap_or(0)
};
unsafe { push(rest, Value::Int(result)) }
}
_ => panic!("shl: expected two integers on stack"),
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_shr(stack: Stack) -> Stack {
let (rest, value, count) = unsafe { pop_two(stack, "shr") };
match (value, count) {
(Value::Int(v), Value::Int(c)) => {
let result = if c < 0 {
0
} else {
(v as u64).checked_shr(c as u32).unwrap_or(0) as i64
};
unsafe { push(rest, Value::Int(result)) }
}
_ => panic!("shr: expected two integers on stack"),
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_popcount(stack: Stack) -> Stack {
assert!(!stack.is_null(), "popcount: stack is empty");
let (rest, a) = unsafe { pop(stack) };
match a {
Value::Int(v) => unsafe { push(rest, Value::Int(v.count_ones() as i64)) },
_ => panic!("popcount: expected integer on stack"),
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_clz(stack: Stack) -> Stack {
assert!(!stack.is_null(), "clz: stack is empty");
let (rest, a) = unsafe { pop(stack) };
match a {
Value::Int(v) => unsafe { push(rest, Value::Int(v.leading_zeros() as i64)) },
_ => panic!("clz: expected integer on stack"),
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_ctz(stack: Stack) -> Stack {
assert!(!stack.is_null(), "ctz: stack is empty");
let (rest, a) = unsafe { pop(stack) };
match a {
Value::Int(v) => unsafe { push(rest, Value::Int(v.trailing_zeros() as i64)) },
_ => panic!("ctz: expected integer on stack"),
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_int_bits(stack: Stack) -> Stack {
unsafe { push(stack, Value::Int(64)) }
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_peek_int_value(stack: Stack) -> i64 {
assert!(!stack.is_null(), "peek_int_value: stack is empty");
let val = unsafe { peek(stack) };
match val {
Value::Int(i) => i,
other => panic!("peek_int_value: expected Int on stack, got {:?}", other),
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_peek_bool_value(stack: Stack) -> bool {
assert!(!stack.is_null(), "peek_bool_value: stack is empty");
let val = unsafe { peek(stack) };
match val {
Value::Bool(b) => b,
other => panic!("peek_bool_value: expected Bool on stack, got {:?}", other),
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_pop_stack(stack: Stack) -> Stack {
assert!(!stack.is_null(), "pop_stack: stack is empty");
let (rest, _value) = unsafe { pop(stack) };
rest
}
pub use patch_seq_add as add;
pub use patch_seq_and as and;
pub use patch_seq_band as band;
pub use patch_seq_bnot as bnot;
pub use patch_seq_bor as bor;
pub use patch_seq_bxor as bxor;
pub use patch_seq_clz as clz;
pub use patch_seq_ctz as ctz;
pub use patch_seq_divide as divide;
pub use patch_seq_eq as eq;
pub use patch_seq_gt as gt;
pub use patch_seq_gte as gte;
pub use patch_seq_int_bits as int_bits;
pub use patch_seq_lt as lt;
pub use patch_seq_lte as lte;
pub use patch_seq_multiply as multiply;
pub use patch_seq_neq as neq;
pub use patch_seq_not as not;
pub use patch_seq_or as or;
pub use patch_seq_popcount as popcount;
pub use patch_seq_push_bool as push_bool;
pub use patch_seq_push_int as push_int;
pub use patch_seq_shl as shl;
pub use patch_seq_shr as shr;
pub use patch_seq_subtract as subtract;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_add() {
unsafe {
let stack = crate::stack::alloc_test_stack();
let stack = push_int(stack, 5);
let stack = push_int(stack, 3);
let stack = add(stack);
let (_stack, result) = pop(stack);
assert_eq!(result, Value::Int(8));
}
}
#[test]
fn test_subtract() {
unsafe {
let stack = crate::stack::alloc_test_stack();
let stack = push_int(stack, 10);
let stack = push_int(stack, 3);
let stack = subtract(stack);
let (_stack, result) = pop(stack);
assert_eq!(result, Value::Int(7));
}
}
#[test]
fn test_multiply() {
unsafe {
let stack = crate::stack::alloc_test_stack();
let stack = push_int(stack, 4);
let stack = push_int(stack, 5);
let stack = multiply(stack);
let (_stack, result) = pop(stack);
assert_eq!(result, Value::Int(20));
}
}
#[test]
fn test_divide() {
unsafe {
let stack = crate::stack::alloc_test_stack();
let stack = push_int(stack, 20);
let stack = push_int(stack, 4);
let stack = divide(stack);
let (stack, success) = pop(stack);
assert_eq!(success, Value::Bool(true));
let (_stack, result) = pop(stack);
assert_eq!(result, Value::Int(5));
}
}
#[test]
fn test_comparisons() {
unsafe {
let stack = crate::stack::alloc_test_stack();
let stack = push_int(stack, 5);
let stack = push_int(stack, 5);
let stack = eq(stack);
let (_stack, result) = pop(stack);
assert_eq!(result, Value::Bool(true));
let stack = push_int(stack, 3);
let stack = push_int(stack, 5);
let stack = lt(stack);
let (_stack, result) = pop(stack);
assert_eq!(result, Value::Bool(true));
let stack = push_int(stack, 7);
let stack = push_int(stack, 5);
let stack = gt(stack);
let (_stack, result) = pop(stack);
assert_eq!(result, Value::Bool(true));
}
}
#[test]
fn test_63bit_overflow_wrapping() {
unsafe {
let int63_max = (1i64 << 62) - 1;
let stack = crate::stack::alloc_test_stack();
let stack = push(stack, Value::Int(int63_max));
let stack = push(stack, Value::Int(1));
let stack = add(stack);
let (_stack, result) = pop(stack);
assert!(matches!(result, Value::Int(_)));
}
}
#[test]
fn test_negative_division() {
unsafe {
let stack = crate::stack::alloc_test_stack();
let stack = push_int(stack, -10);
let stack = push_int(stack, 3);
let stack = divide(stack);
let (stack, success) = pop(stack);
assert_eq!(success, Value::Bool(true));
let (_stack, result) = pop(stack);
assert_eq!(result, Value::Int(-3));
let stack = push_int(stack, 10);
let stack = push_int(stack, -3);
let stack = divide(stack);
let (stack, success) = pop(stack);
assert_eq!(success, Value::Bool(true));
let (_stack, result) = pop(stack);
assert_eq!(result, Value::Int(-3));
let stack = push_int(stack, -10);
let stack = push_int(stack, -3);
let stack = divide(stack);
let (stack, success) = pop(stack);
assert_eq!(success, Value::Bool(true));
let (_stack, result) = pop(stack);
assert_eq!(result, Value::Int(3));
}
}
#[test]
fn test_and_true_true() {
unsafe {
let stack = crate::stack::alloc_test_stack();
let stack = push_int(stack, 1);
let stack = push_int(stack, 1);
let stack = and(stack);
let (_stack, result) = pop(stack);
assert_eq!(result, Value::Int(1));
}
}
#[test]
fn test_and_true_false() {
unsafe {
let stack = crate::stack::alloc_test_stack();
let stack = push_int(stack, 1);
let stack = push_int(stack, 0);
let stack = and(stack);
let (_stack, result) = pop(stack);
assert_eq!(result, Value::Int(0));
}
}
#[test]
fn test_and_false_false() {
unsafe {
let stack = crate::stack::alloc_test_stack();
let stack = push_int(stack, 0);
let stack = push_int(stack, 0);
let stack = and(stack);
let (_stack, result) = pop(stack);
assert_eq!(result, Value::Int(0));
}
}
#[test]
fn test_or_true_true() {
unsafe {
let stack = crate::stack::alloc_test_stack();
let stack = push_int(stack, 1);
let stack = push_int(stack, 1);
let stack = or(stack);
let (_stack, result) = pop(stack);
assert_eq!(result, Value::Int(1));
}
}
#[test]
fn test_or_true_false() {
unsafe {
let stack = crate::stack::alloc_test_stack();
let stack = push_int(stack, 1);
let stack = push_int(stack, 0);
let stack = or(stack);
let (_stack, result) = pop(stack);
assert_eq!(result, Value::Int(1));
}
}
#[test]
fn test_or_false_false() {
unsafe {
let stack = crate::stack::alloc_test_stack();
let stack = push_int(stack, 0);
let stack = push_int(stack, 0);
let stack = or(stack);
let (_stack, result) = pop(stack);
assert_eq!(result, Value::Int(0));
}
}
#[test]
fn test_not_true() {
unsafe {
let stack = crate::stack::alloc_test_stack();
let stack = push_int(stack, 1);
let stack = not(stack);
let (_stack, result) = pop(stack);
assert_eq!(result, Value::Int(0));
}
}
#[test]
fn test_not_false() {
unsafe {
let stack = crate::stack::alloc_test_stack();
let stack = push_int(stack, 0);
let stack = not(stack);
let (_stack, result) = pop(stack);
assert_eq!(result, Value::Int(1));
}
}
#[test]
fn test_and_nonzero_values() {
unsafe {
let stack = crate::stack::alloc_test_stack();
let stack = push_int(stack, 42);
let stack = push_int(stack, -5);
let stack = and(stack);
let (_stack, result) = pop(stack);
assert_eq!(result, Value::Int(1));
}
}
#[test]
fn test_divide_by_zero_returns_false() {
unsafe {
let stack = crate::stack::alloc_test_stack();
let stack = push_int(stack, 42);
let stack = push_int(stack, 0);
let stack = divide(stack);
let (stack, success) = pop(stack);
assert_eq!(success, Value::Bool(false));
let (_stack, result) = pop(stack);
assert_eq!(result, Value::Int(0));
}
}
}