153 lines
4.3 KiB
Rust
153 lines
4.3 KiB
Rust
use shared::protocol::StackState;
|
|
use wasm_bindgen::prelude::*;
|
|
|
|
#[wasm_bindgen]
|
|
pub fn run_program(program_b64: &str) -> JsValue {
|
|
use base64::Engine;
|
|
let bytes = match base64::engine::general_purpose::STANDARD.decode(program_b64) {
|
|
Ok(v) => v,
|
|
Err(_) => return JsValue::NULL,
|
|
};
|
|
let state = execute(&bytes);
|
|
match serde_wasm_bindgen::to_value(&state) {
|
|
Ok(v) => v,
|
|
Err(_) => JsValue::NULL,
|
|
}
|
|
}
|
|
|
|
fn execute(program: &[u8]) -> StackState {
|
|
shared::vm::execute(program)
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn test_push() {
|
|
// PUSH 42, PUSH 100
|
|
let program = vec![0x00, 42, 0, 0, 0, 0x00, 100, 0, 0, 0];
|
|
let state = execute(&program);
|
|
assert_eq!(state.stack, vec![42, 100]);
|
|
assert_eq!(state.ip, 10);
|
|
}
|
|
|
|
#[test]
|
|
fn test_add() {
|
|
// PUSH 5, PUSH 10, ADD
|
|
let program = vec![0x00, 5, 0, 0, 0, 0x00, 10, 0, 0, 0, 0x01];
|
|
let state = execute(&program);
|
|
assert_eq!(state.stack, vec![15]);
|
|
}
|
|
|
|
#[test]
|
|
fn test_add_wrapping() {
|
|
// PUSH u32::MAX, PUSH 1, ADD
|
|
let program = vec![0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 1, 0, 0, 0, 0x01];
|
|
let state = execute(&program);
|
|
assert_eq!(state.stack, vec![0]);
|
|
}
|
|
|
|
#[test]
|
|
fn test_sub() {
|
|
// PUSH 20, PUSH 7, SUB
|
|
let program = vec![0x00, 20, 0, 0, 0, 0x00, 7, 0, 0, 0, 0x02];
|
|
let state = execute(&program);
|
|
assert_eq!(state.stack, vec![13]);
|
|
}
|
|
|
|
#[test]
|
|
fn test_sub_wrapping() {
|
|
// PUSH 0, PUSH 1, SUB
|
|
let program = vec![0x00, 0, 0, 0, 0, 0x00, 1, 0, 0, 0, 0x02];
|
|
let state = execute(&program);
|
|
assert_eq!(state.stack, vec![u32::MAX]);
|
|
}
|
|
|
|
#[test]
|
|
fn test_mul() {
|
|
// PUSH 6, PUSH 7, MUL
|
|
let program = vec![0x00, 6, 0, 0, 0, 0x00, 7, 0, 0, 0, 0x03];
|
|
let state = execute(&program);
|
|
assert_eq!(state.stack, vec![42]);
|
|
}
|
|
|
|
#[test]
|
|
fn test_xor() {
|
|
// PUSH 0b1010, PUSH 0b1100, XOR
|
|
let program = vec![0x00, 0b1010, 0, 0, 0, 0x00, 0b1100, 0, 0, 0, 0x04];
|
|
let state = execute(&program);
|
|
assert_eq!(state.stack, vec![0b0110]);
|
|
}
|
|
|
|
#[test]
|
|
fn test_and() {
|
|
// PUSH 0b1010, PUSH 0b1100, AND
|
|
let program = vec![0x00, 0b1010, 0, 0, 0, 0x00, 0b1100, 0, 0, 0, 0x05];
|
|
let state = execute(&program);
|
|
assert_eq!(state.stack, vec![0b1000]);
|
|
}
|
|
|
|
#[test]
|
|
fn test_or() {
|
|
// PUSH 0b1010, PUSH 0b1100, OR
|
|
let program = vec![0x00, 0b1010, 0, 0, 0, 0x00, 0b1100, 0, 0, 0, 0x06];
|
|
let state = execute(&program);
|
|
assert_eq!(state.stack, vec![0b1110]);
|
|
}
|
|
|
|
#[test]
|
|
fn test_rot() {
|
|
// PUSH 1, PUSH 4, ROT
|
|
let program = vec![0x00, 1, 0, 0, 0, 0x00, 4, 0, 0, 0, 0x07];
|
|
let state = execute(&program);
|
|
assert_eq!(state.stack, vec![16]);
|
|
}
|
|
|
|
#[test]
|
|
fn test_not() {
|
|
// PUSH 0, NOT
|
|
let program = vec![0x00, 0, 0, 0, 0, 0x08];
|
|
let state = execute(&program);
|
|
assert_eq!(state.stack, vec![u32::MAX]);
|
|
}
|
|
|
|
#[test]
|
|
fn test_hash() {
|
|
// PUSH 10, PUSH 20, HASH
|
|
let program = vec![0x00, 10, 0, 0, 0, 0x00, 20, 0, 0, 0, 0x09];
|
|
let state = execute(&program);
|
|
assert_eq!(state.stack.len(), 1);
|
|
let expected_hash = shared::hashing::hash_stack(&[10, 20]);
|
|
assert_eq!(state.stack[0], expected_hash);
|
|
}
|
|
|
|
#[test]
|
|
fn test_underflow_binary() {
|
|
// PUSH 42, ADD (needs 2 values, only 1 on stack)
|
|
let program = vec![0x00, 42, 0, 0, 0, 0x01];
|
|
let state = execute(&program);
|
|
// ADD breaks when stack.len() < 2, stack has 42 left, ip is at the opcode ADD (6)
|
|
assert_eq!(state.stack, vec![42]);
|
|
assert_eq!(state.ip, 6);
|
|
}
|
|
|
|
#[test]
|
|
fn test_underflow_unary() {
|
|
// NOT (needs 1 value, empty stack)
|
|
let program = vec![0x08];
|
|
let state = execute(&program);
|
|
assert_eq!(state.stack, Vec::<u32>::new());
|
|
assert_eq!(state.ip, 1);
|
|
}
|
|
|
|
#[test]
|
|
fn test_incomplete_push() {
|
|
// PUSH opcode, but only 2 bytes instead of 4
|
|
let program = vec![0x00, 42, 0];
|
|
let state = execute(&program);
|
|
assert_eq!(state.stack, Vec::<u32>::new());
|
|
assert_eq!(state.ip, 1); // execution stopped at op 0x00 because ip + 4 > program.len()
|
|
}
|
|
}
|