use crate::binread::BinRead; use crate::frame::Frame; use crate::machine::Machine; use std::io::Read; use std::num::Wrapping; use crate::Result; use crate::value::Value; use std::convert::TryInto; use crate::netstack::NetStack; pub type OpFunc = fn(&mut Machine) -> Result<()>; #[derive(Debug, Clone, Serialize, Deserialize)] pub enum Args { Byte, Short, Var, Label, Constant, } #[derive(Clone)] pub enum Operation { Op(&'static str, OpFunc, Vec), Invalid, } const JUMP_OFFSET: i32 = 3; lazy_static! { static ref OPS_MAP: Vec = { let mut m = vec![Operation::Invalid; 0xFF + 1]; m[0x00] = Operation::Op("NOP", nop, vec![]); m[0x10] = Operation::Op("BIPUSH", bipush, vec![Args::Byte]); m[0x13] = Operation::Op("LDC_W", ldc_w, vec![Args::Constant]); m[0x15] = Operation::Op("ILOAD", iload, vec![Args::Var]); m[0x36] = Operation::Op("ISTORE", istore, vec![Args::Var]); m[0x57] = Operation::Op("POP", pop, vec![]); m[0x59] = Operation::Op("DUP", dup, vec![]); m[0x5F] = Operation::Op("SWAP", swap, vec![]); m[0x60] = Operation::Op("IADD", iadd, vec![]); m[0x64] = Operation::Op("ISUB", isub, vec![]); #[cfg(feature = "extra.arithmetic")] { m[0x70] = Operation::Op("SHL", shl, vec![]); m[0x71] = Operation::Op("SHR", shr, vec![]); m[0x72] = Operation::Op("IMUL", imul, vec![]); m[0x73] = Operation::Op("IDIV", idiv, vec![]); } m[0x7E] = Operation::Op("IAND", iand, vec![]); m[0x84] = Operation::Op("IINC", iinc, vec![Args::Var, Args::Byte]); m[0x99] = Operation::Op("IFEQ", ifeq, vec![Args::Label]); m[0x9b] = Operation::Op("IFLT", iflt, vec![Args::Label]); m[0x9F] = Operation::Op("IF_ICMPEQ", if_icmpeq, vec![Args::Label]); m[0xA7] = Operation::Op("GOTO", goto, vec![Args::Label]); m[0xAC] = Operation::Op("IRETURN", ireturn, vec![]); m[0xB0] = Operation::Op("IOR", ior, vec![]); m[0xB6] = Operation::Op("INVOKEVIRTUAL", invokevirtual, vec![Args::Constant]); m[0xC4] = Operation::Op("WIDE", wide, vec![]); #[cfg(feature = "bonus.heap")] { m[0xD1] = Operation::Op("NEWARRAY", newarray, vec![]); m[0xD2] = Operation::Op("IALOAD", iaload, vec![]); m[0xD3] = Operation::Op("IASTORE", iastore, vec![]); m[0xD4] = Operation::Op("GC", gc, vec![]); } #[cfg(feature = "bonus.network")] { m[0xE1] = Operation::Op("NETBIND", netbind, vec![]); m[0xE2] = Operation::Op("NETCONNECT", netconnect, vec![]); m[0xE3] = Operation::Op("NETIN", netin, vec![]); m[0xE4] = Operation::Op("NETOUT", netout, vec![]); m[0xE5] = Operation::Op("NETCLOSE", netclose, vec![]); } #[cfg(feature = "extra.sleep")] { m[0xF0] = Operation::Op("SLP", slp, vec![Args::Byte]); } m[0xFC] = Operation::Op("IN", _in, vec![]); m[0xFD] = Operation::Op("OUT", out, vec![]); m[0xFE] = Operation::Op("ERR", err, vec![]); m[0xFF] = Operation::Op("HALT", halt, vec![]); m }; } pub fn num_to_op(op: u8) -> &'static Operation { &OPS_MAP[op as usize] } fn nop(_: &mut Machine) -> Result<()> { Ok(()) } fn bipush(machine: &mut Machine) -> Result<()> { let val = match machine.block.read_i8() { Ok(a) => i32::from(a), Err(_) => return Err("Expected argument"), }; machine.cur_stack().push(Value::Int(val)); Ok(()) } fn dup(machine: &mut Machine) -> Result<()> { let val = machine.cur_stack().top()?.clone(); machine.cur_stack().push(val); Ok(()) } fn out(machine: &mut Machine) -> Result<()> { let val: i32 = machine.cur_stack().pop()?.try_into()?; let buffer = [val as u8]; let mut out = machine.stream_out.lock().unwrap(); out.write_all(&buffer).unwrap(); out.flush().unwrap(); Ok(()) } fn pop(machine: &mut Machine) -> Result<()> { machine.cur_stack().pop()?; Ok(()) } fn iadd(machine: &mut Machine) -> Result<()> { let a: i32 = machine.cur_stack().pop()?.try_into()?; let b: i32 = machine.cur_stack().pop()?.try_into()?; let res = Wrapping(a) + Wrapping(b); machine.cur_stack().push(Value::Int(res.0)); Ok(()) } fn isub(machine: &mut Machine) -> Result<()> { let a: Wrapping = Wrapping(machine.cur_stack().pop()?.try_into()?); let b: Wrapping = Wrapping(machine.cur_stack().pop()?.try_into()?); machine.cur_stack().push(Value::Int((b - a).0)); Ok(()) } fn _in(machine: &mut Machine) -> Result<()> { let mut buffer = [0; 1]; let val = match machine.stream_in.read_exact(&mut buffer) { Err(_) => 0i32, Ok(_) => i32::from(buffer[0]), }; machine.cur_stack().push(Value::Int(val)); Ok(()) } fn goto(machine: &mut Machine) -> Result<()> { let offset = i32::from(machine.block.read_i16()?) - JUMP_OFFSET; machine.block.jump(offset) } fn err(machine: &mut Machine) -> Result<()> { machine.halted = true; eprintln!("MACHINE CALLED ERR"); Ok(()) } fn halt(machine: &mut Machine) -> Result<()> { machine.halted = true; Ok(()) } fn ifeq(machine: &mut Machine) -> Result<()> { let offset = i32::from(machine.block.read_i16()?) - JUMP_OFFSET; let tos = machine.cur_stack().pop()?; // IFEQ is special because it gets special support for NetRefs let val = match tos { Value::NetRef(v) => v as i32, Value::Int(v) => v, Value::HeapRef(_) => Err("cannot use heapref in IFEQ")?, }; if val == 0 { return machine.block.jump(offset); } Ok(()) } fn iflt(machine: &mut Machine) -> Result<()> { let offset = i32::from(machine.block.read_i16()?) - JUMP_OFFSET; let compare: i32 = machine.cur_stack().pop()?.try_into()?; if compare < 0 { return machine.block.jump(offset); } Ok(()) } fn if_icmpeq(machine: &mut Machine) -> Result<()> { let offset = i32::from(machine.block.read_i16()?) - JUMP_OFFSET; if machine.cur_stack().pop()? == machine.cur_stack().pop()? { return machine.block.jump(offset); } Ok(()) } fn iand(machine: &mut Machine) -> Result<()> { let a: i32 = machine.cur_stack().pop()?.try_into()?; let b: i32 = machine.cur_stack().pop()?.try_into()?; machine.cur_stack().push(Value::Int(a & b)); Ok(()) } fn ior(machine: &mut Machine) -> Result<()> { let a: i32 = machine.cur_stack().pop()?.try_into()?; let b: i32 = machine.cur_stack().pop()?.try_into()?; machine.cur_stack().push(Value::Int(a | b)); Ok(()) } fn ldc_w(machine: &mut Machine) -> Result<()> { let offset = machine.block.read_i16()? as usize; let val = machine.pool.get(offset)?; machine.cur_stack().push(Value::Int(val)); Ok(()) } fn swap(machine: &mut Machine) -> Result<()> { let a = machine.cur_stack().pop()?; let b = machine.cur_stack().pop()?; machine.cur_stack().push(a); machine.cur_stack().push(b); Ok(()) } fn wide(machine: &mut Machine) -> Result<()> { machine.wide = true; machine.step() } fn iload(machine: &mut Machine) -> Result<()> { let a = machine.read_index()?; let val = machine.cur_frame().get(a)?.clone(); machine.cur_stack().push(val); Ok(()) } fn istore(machine: &mut Machine) -> Result<()> { let a = machine.read_index()?; let val = machine.cur_stack().pop()?; machine.cur_frame().set(a, val) } fn iinc(machine: &mut Machine) -> Result<()> { let a = machine.read_index()?; let b = i32::from(machine.block.read_i8()?); let val: i32 = machine.cur_frame().get(a)?.try_into()?; machine.cur_frame().set(a, Value::Int(val + b)) } fn invokevirtual(machine: &mut Machine) -> Result<()> { let method_index = machine.block.read_u16()?; let invoke_addr = machine.pool.get(method_index as usize)? as usize; let return_addr = machine.get_program_counter(); machine.block.seek(invoke_addr)?; let arg_count = machine.block.read_u16()?; let var_count = machine.block.read_u16()?; let mut newframe = Frame::new((arg_count + var_count) as usize, invoke_addr); // Lifetime for cur_stack { let cur_stack = machine.cur_stack(); for i in 1..arg_count { newframe.set((arg_count - i) as usize, cur_stack.pop()?)?; } cur_stack.pop()?; // Nuke the OBJREF } newframe.set(0, Value::Int(return_addr as i32))?; machine.frame.push(newframe); Ok(()) } fn ireturn(machine: &mut Machine) -> Result<()> { let mut prev_frame: Frame = match machine.frame.pop() { Some(a) => a, None => return Err("Got no frame... somehow..."), }; let result = prev_frame.stack.pop()?; let return_addr: i32 = prev_frame.get(0)?.try_into()?; machine.cur_stack().push(result); machine.block.seek(return_addr as usize) } #[cfg(feature = "extra.sleep")] fn slp(machine: &mut Machine) -> Result<()> { use std::thread; use std::time; let val = match machine.block.read_u8() { Ok(a) => u64::from(a), Err(_) => return Err("Expected argument"), }; let slpdur = time::Duration::from_millis(val * 100); thread::sleep(slpdur); Ok(()) } #[cfg(feature = "bonus.network")] fn netbind(machine: &mut Machine) -> Result<()> { let port: i32 = machine.cur_stack().pop()?.try_into()?; let mut stack = NetStack::new(); if stack.bind(port as u16).is_err() { machine.cur_stack().push(Value::Int(0)); return Ok(()); } machine.net.push(stack); let idx = machine.net.len(); machine.cur_stack().push(Value::NetRef(idx)); Ok(()) } #[cfg(feature = "bonus.network")] fn netconnect(machine: &mut Machine) -> Result<()> { let port: i32 = machine.cur_stack().pop()?.try_into()?; let host: i32 = machine.cur_stack().pop()?.try_into()?; let mut stack = NetStack::new(); if stack.connect(host as u32, port as u16).is_err() { machine.cur_stack().push(Value::Int(0)); return Ok(()); } machine.net.push(stack); let idx = machine.net.len(); machine.cur_stack().push(Value::NetRef(idx)); Ok(()) } #[cfg(feature = "bonus.network")] fn netin(machine: &mut Machine) -> Result<()> { let idx = match machine.cur_stack().pop()? { Value::NetRef(x) => x, _ => Err("Invalid netref given to network instruction")?, }; let conn = machine.net.get_mut(idx - 1).ok_or("Invalid network connection")?; let byte = i32::from(conn.read_byte()?); machine.cur_stack().push(Value::Int(byte)); Ok(()) } #[cfg(feature = "bonus.network")] fn netout(machine: &mut Machine) -> Result<()> { let idx = match machine.cur_stack().pop()? { Value::NetRef(x) => x, _ => Err("Invalid netref given to network instruction")?, }; let val: i32 = machine.cur_stack().pop()?.try_into()?; let conn = machine.net.get_mut(idx - 1).ok_or("Invalid network connection")?; conn.write_byte(val as u8)?; Ok(()) } #[cfg(feature = "bonus.network")] fn netclose(machine: &mut Machine) -> Result<()> { let idx = match machine.cur_stack().pop()? { Value::NetRef(x) => x, _ => Err("Invalid netref given to network instruction")?, }; machine.net.get_mut(idx - 1).ok_or("Invalid network connection")?.close()?; Ok(()) } #[cfg(feature = "bonus.heap")] fn newarray(machine: &mut Machine) -> Result<()> { let size: i32 = machine.cur_stack().pop()?.try_into()?; let heap = machine.heap.alloc(size as usize); machine.cur_stack().push(Value::HeapRef(heap)); Ok(()) } #[cfg(feature = "bonus.heap")] fn iastore(machine: &mut Machine) -> Result<()> { use std::cell::RefCell; let heap = match machine.cur_stack().pop()? { Value::HeapRef(a) => a, _ => return Err("Cannot use int as heapref"), }; let index: i32 = machine.cur_stack().pop()?.try_into()?; let value = machine.cur_stack().pop()?; RefCell::borrow_mut(&heap)[index as usize] = value; Ok(()) } #[cfg(feature = "bonus.heap")] fn iaload(machine: &mut Machine) -> Result<()> { let heap = match machine.cur_stack().pop()? { Value::HeapRef(a) => a, _ => return Err("Cannot use int as heapref"), }; let index: i32 = machine.cur_stack().pop()?.try_into()?; let value: Value; { value = heap.borrow()[index as usize].clone(); } machine.cur_stack().push(value); Ok(()) } #[cfg(feature = "bonus.heap")] fn gc(machine: &mut Machine) -> Result<()> { machine.heap.gc(); Ok(()) } #[cfg(feature = "extra.arithmetic")] fn shl(machine: &mut Machine) -> Result<()> { let shift: i32 = machine.cur_stack().pop()?.try_into()?; let value: i32 = machine.cur_stack().pop()?.try_into()?; machine.cur_stack().push(Value::Int(value << shift)); Ok(()) } #[cfg(feature = "extra.arithmetic")] fn shr(machine: &mut Machine) -> Result<()> { let shift: i32 = machine.cur_stack().pop()?.try_into()?; let value: i32 = machine.cur_stack().pop()?.try_into()?; let result: u32 = (value as u32) >> (shift as u32); machine.cur_stack().push(Value::Int(result as i32)); Ok(()) } #[cfg(feature = "extra.arithmetic")] fn imul(machine: &mut Machine) -> Result<()> { let a: i32 = machine.cur_stack().pop()?.try_into()?; let b: i32 = machine.cur_stack().pop()?.try_into()?; machine.cur_stack().push(Value::Int(a * b)); Ok(()) } #[cfg(feature = "extra.arithmetic")] fn idiv(machine: &mut Machine) -> Result<()> { let divisor: i32 = machine.cur_stack().pop()?.try_into()?; let value: i32 = machine.cur_stack().pop()?.try_into()?; machine.cur_stack().push(Value::Int(value / divisor)); Ok(()) }