196 lines
5.3 KiB
Rust
196 lines
5.3 KiB
Rust
use std::io::{Read, Write};
|
|
use std::sync::{Arc, Mutex};
|
|
|
|
use binread::{BinRead, BinReadable};
|
|
use block::Block;
|
|
use frame::Frame;
|
|
use ijvmreader::IJVMReader;
|
|
use ops::{num_to_op, Args, Operation};
|
|
use pool::Pool;
|
|
use stack::Stack;
|
|
use Result;
|
|
|
|
#[cfg(feature = "bonus:heap")]
|
|
use heap::Heaps;
|
|
#[cfg(feature = "bonus:network")]
|
|
use netstack::NetStack;
|
|
use std::convert::TryInto;
|
|
|
|
pub const MAGIC_HEADER: u32 = 0x1DEA_DFAD;
|
|
|
|
pub struct Machine {
|
|
pub wide: bool,
|
|
pub halted: bool,
|
|
pub pool: Pool,
|
|
pub block: Block,
|
|
pub frame: Vec<Frame>,
|
|
|
|
#[cfg(feature = "bonus:network")]
|
|
pub net: NetStack,
|
|
#[cfg(feature = "bonus:heap")]
|
|
pub heap: Heaps,
|
|
|
|
pub stream_in: Box<dyn Read + Send + Sync>,
|
|
pub stream_out: Arc<Mutex<dyn Write + Send + Sync>>,
|
|
}
|
|
|
|
impl Machine {
|
|
pub fn new(pool: Pool, block: Block) -> Machine {
|
|
Machine {
|
|
wide: false,
|
|
halted: false,
|
|
pool,
|
|
block,
|
|
frame: vec![Frame::new_extendable(0)],
|
|
stream_in: Box::new(::std::io::stdin()),
|
|
stream_out: Arc::new(Mutex::new(::std::io::stdout())),
|
|
|
|
#[cfg(feature = "bonus:network")]
|
|
net: NetStack::new(),
|
|
|
|
#[cfg(feature = "bonus:heap")]
|
|
heap: Heaps::new(),
|
|
}
|
|
}
|
|
|
|
pub fn new_from_reader(mut reader: IJVMReader) -> Result<Machine> {
|
|
let magic = reader.read_u32()?;
|
|
if magic != MAGIC_HEADER {
|
|
return Err("Invalid magic header");
|
|
}
|
|
let constants = match reader.read_block() {
|
|
Ok(a) => a,
|
|
Err(_) => return Err("Failed to read constants block"),
|
|
};
|
|
let text = match reader.read_block() {
|
|
Ok(block) => block,
|
|
Err(_) => return Err("Failed to read text block"),
|
|
};
|
|
|
|
let pool = Pool::new(constants)?;
|
|
|
|
Ok(Machine::new(pool, text))
|
|
}
|
|
|
|
pub fn new_from_file(file: &str) -> Result<Machine> {
|
|
let reader = IJVMReader::new(file).unwrap();
|
|
Machine::new_from_reader(reader)
|
|
}
|
|
|
|
pub fn new_from_slice(source: &[u8]) -> Result<Machine> {
|
|
let reader = IJVMReader::new_from_slice(source);
|
|
Machine::new_from_reader(reader)
|
|
}
|
|
|
|
pub fn step(&mut self) -> Result<()> {
|
|
match self.block.read_op() {
|
|
Ok(Operation::Op(a, func, _)) => {
|
|
if cfg!(feature = "debug:instr") {
|
|
println!("{}", a);
|
|
println!("Stack: {:?}", self.cur_frame().stack.data);
|
|
let x = func(self);
|
|
println!("Stack: {:?}", self.cur_frame().stack.data);
|
|
x
|
|
} else {
|
|
func(self)
|
|
}
|
|
}
|
|
Ok(Operation::Invalid) => {
|
|
println!("Reached invalid operation");
|
|
Err("Invalid op")
|
|
}
|
|
Err(str) => Err(str),
|
|
}
|
|
}
|
|
|
|
pub fn run(&mut self) -> Result<()> {
|
|
while self.has_step() {
|
|
self.step()?;
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
pub fn has_step(&self) -> bool {
|
|
!self.halted && self.block.has_i8()
|
|
}
|
|
|
|
pub fn get_program_counter(&self) -> usize {
|
|
self.block.cur()
|
|
}
|
|
|
|
pub fn get_tos(&mut self) -> Result<i32> {
|
|
self.cur_stack().top()?.try_into()
|
|
}
|
|
|
|
pub fn cur_frame(&mut self) -> &mut Frame {
|
|
self.frame.last_mut().unwrap()
|
|
}
|
|
|
|
pub fn cur_stack(&mut self) -> &mut Stack {
|
|
&mut self.cur_frame().stack
|
|
}
|
|
|
|
pub fn cur_instruction(&mut self) -> u8 {
|
|
self.block[self.get_program_counter()]
|
|
}
|
|
|
|
pub fn next_instruction(&mut self) -> Result<(String, Vec<i32>)> {
|
|
let checkpoint = self.block.cur();
|
|
|
|
let mut opcode = self.block.read_u8()?;
|
|
let mut params = Vec::new();
|
|
let mut wide = false;
|
|
|
|
// WIDE
|
|
if opcode == 0xC4 {
|
|
wide = true;
|
|
opcode = self.block.read_u8()?;
|
|
}
|
|
|
|
let (name, args) = match num_to_op(opcode) {
|
|
Operation::Invalid => return Err("Invalid operation"),
|
|
Operation::Op(name, _, args) => (name, args),
|
|
};
|
|
|
|
for arg in args {
|
|
let v = match arg {
|
|
Args::Byte => i32::from(self.block.read_i8()?),
|
|
Args::Short => i32::from(self.block.read_i16()?),
|
|
Args::Var => {
|
|
if wide {
|
|
i32::from(self.block.read_u16()?)
|
|
} else {
|
|
i32::from(self.block.read_u8()?)
|
|
}
|
|
}
|
|
Args::Label => i32::from(self.block.read_i16()?),
|
|
Args::Constant => i32::from(self.block.read_u16()?),
|
|
};
|
|
params.push(v);
|
|
}
|
|
|
|
self.block.seek(checkpoint).unwrap();
|
|
Ok(((*name).to_string(), params))
|
|
}
|
|
|
|
// pub fn get_stack_pointer(&self) -> usize {
|
|
// return self.frame.last().unwrap().stack.len();
|
|
// }
|
|
|
|
pub fn read_index(&mut self) -> Result<usize> {
|
|
if self.wide {
|
|
self.wide = false;
|
|
return Ok(self.block.read_u16()? as usize);
|
|
}
|
|
Ok(self.block.read_u8()? as usize)
|
|
}
|
|
|
|
pub fn set_input(&mut self, instream: Box<dyn Read + Send + Sync>) {
|
|
self.stream_in = instream;
|
|
}
|
|
|
|
pub fn set_output(&mut self, outstream: Arc<Mutex<dyn Write + Send + Sync>>) {
|
|
self.stream_out = outstream;
|
|
}
|
|
}
|