Passing all tests i guess

This commit is contained in:
2017-06-06 14:19:35 +02:00
commit 1b3c582ee2
13 changed files with 699 additions and 0 deletions

5
.gitignore vendored Normal file
View File

@@ -0,0 +1,5 @@
target
*.jas
*.ijvm
*.conf
goJASM

4
Cargo.lock generated Normal file
View File

@@ -0,0 +1,4 @@
[root]
name = "rustijvm"
version = "0.1.0"

6
Cargo.toml Normal file
View File

@@ -0,0 +1,6 @@
[package]
name = "rustijvm"
version = "0.1.0"
authors = ["Jur van den Berg <Jurl.berg@gmail.com>"]
[dependencies]

82
src/ijvm/binread.rs Normal file
View File

@@ -0,0 +1,82 @@
use ijvm::Result;
use ijvm::block::Block;
use ijvm::ops::{Operation,num_to_op};
pub trait BinReadable {
fn get(&mut self) -> Result<u8>;
fn cur(&self) -> usize;
fn len(&self) -> usize;
fn slice(&mut self, len: usize) -> &[u8];
}
pub trait BinRead {
fn has_i8(&self) -> bool;
fn has_i16(&self) -> bool;
fn has_i32(&self) -> bool;
fn read_u8(&mut self) -> Result<u8>;
fn read_u16(&mut self) -> Result<u16>;
fn read_u32(&mut self) -> Result<u32>;
fn read_i8(&mut self) -> Result<i8>;
fn read_i16(&mut self) -> Result<i16>;
fn read_i32(&mut self) -> Result<i32>;
fn read_block(&mut self) -> Result<Block>;
fn read_op(&mut self) -> Result<Operation>;
}
impl<T: BinReadable> BinRead for T {
fn has_i8(&self) -> bool {
self.cur() < self.len()
}
fn has_i16(&self) -> bool {
self.cur() + 1 < self.len()
}
fn has_i32(&self) -> bool {
self.cur() + 3 < self.len()
}
fn read_u8(&mut self) -> Result<u8> {
self.get()
}
fn read_u16(&mut self) -> Result<u16> {
let a = self.read_u8()? as u16;
let b = self.read_u8()? as u16;
Ok(a << 8 | b)
}
fn read_u32(&mut self) -> Result<u32> {
let a = self.read_u16()? as u32;
let b = self.read_u16()? as u32;
Ok(a << 16 | b)
}
fn read_i8(&mut self) -> Result<i8> {
Ok(self.get()? as i8)
}
fn read_i16(&mut self) -> Result<i16> {
let a = self.read_u8()? as i16;
let b = self.read_u8()? as i16;
Ok((a << 8 | b) as i16)
}
fn read_i32(&mut self) -> Result<i32> {
let a = self.read_u16()? as i32;
let b = self.read_u16()? as i32;
Ok(a << 16 | b)
}
fn read_block(&mut self) -> Result<Block> {
let origin = self.read_u32()?;
let len = self.read_u32()? as usize;
Ok(Block::new(origin, self.slice(len)))
}
fn read_op(&mut self) -> Result<Operation> {
Ok(num_to_op(self.read_u8()?))
}
}

71
src/ijvm/block.rs Normal file
View File

@@ -0,0 +1,71 @@
use ijvm::Result;
use ijvm::binread::BinReadable;
#[derive(Debug)]
pub struct Block {
origin: u32,
length: usize,
pointer: usize,
source: Vec<u8>
}
impl Block {
pub fn new(origin: u32, source: &[u8]) -> Block {
Block {
origin: origin,
length: source.len(),
pointer: 0,
source: source.to_owned()
}
}
pub fn jump(&mut self, offset: i32) -> Result<()> {
if offset < 0 {
if (-offset as usize) > self.pointer {
return Err("Jump before block")
}
self.pointer -= -offset as usize;
} else {
if self.pointer + (offset as usize) >= self.length {
return Err("Jump outside of block")
}
self.pointer += offset as usize;
}
Ok(())
}
pub fn seek(&mut self, location: usize) -> Result<()> {
if location >= self.length {
println!("hoho {}", location);
return Err("Seek outside of block")
}
self.pointer = location;
Ok(())
}
}
impl BinReadable for Block {
fn get(&mut self) -> Result<u8> {
if self.pointer >= self.length {
return Err("EOF reached")
}
let byte = self.source[self.pointer];
self.pointer += 1;
return Ok(byte)
}
fn len(&self) -> usize {
return self.source.len()
}
fn cur(&self) -> usize {
return self.pointer;
}
fn slice(&mut self, len: usize) -> &[u8] {
let slice = &self.source[self.pointer..self.pointer+len];
self.pointer += len;
slice
}
}

33
src/ijvm/frame.rs Normal file
View File

@@ -0,0 +1,33 @@
use ijvm::Result;
use ijvm::stack::Stack;
#[derive(Debug)]
pub struct Frame {
pub stack: Stack,
locals: Vec<i32>,
}
impl Frame {
pub fn new(num_locals: usize) -> Frame {
// println!("Initializing frame of len {}", num_locals);
return Frame {
stack: Stack::new(),
locals: vec![0; num_locals+1],
}
}
pub fn get(&self, offset: usize) -> Result<i32> {
if offset >= self.locals.len() {
return Err("Local variable out of range");
}
return Ok(self.locals[offset])
}
pub fn set(&mut self, offset: usize, val: i32) -> Result<()> {
if offset >= self.locals.len() {
return Err("Local variable out of range");
}
self.locals[offset] = val;
return Ok(());
}
}

48
src/ijvm/ijvmreader.rs Normal file
View File

@@ -0,0 +1,48 @@
use std::io::Read;
use std::fs::File;
use ijvm::Result;
use ijvm::binread::BinReadable;
pub struct IJVMReader {
pointer: usize,
source: Vec<u8>
}
impl IJVMReader {
pub fn new(path: &str) -> Result<IJVMReader> {
let file = match File::open(path) {
Ok(file) => file,
Err(_) => return Err("Could not open file"),
};
let abc: Vec<u8> = file.bytes().map(|e| e.unwrap()).collect();
Ok(IJVMReader {
pointer: 0,
source: abc
})
}
}
impl BinReadable for IJVMReader {
fn get(&mut self) -> Result<u8> {
if self.pointer >= self.source.len() {
return Err("EOF reached")
}
let byte = self.source[self.pointer];
self.pointer += 1;
return Ok(byte)
}
fn len(&self) -> usize {
return self.source.len()
}
fn cur(&self) -> usize {
return self.pointer;
}
fn slice(&mut self, len: usize) -> &[u8] {
let slice = &self.source[self.pointer..self.pointer+len];
self.pointer += len;
slice
}
}

96
src/ijvm/machine.rs Normal file
View File

@@ -0,0 +1,96 @@
use ijvm::block::Block;
use ijvm::ops::Operation;
use ijvm::ijvmreader::IJVMReader;
use ijvm::binread::BinReadable;
use ijvm::binread::BinRead;
use ijvm::frame::Frame;
use ijvm::stack::Stack;
use ijvm::pool::Pool;
use ijvm::Result;
const MAGIC_HEADER:u32 = 0x1deadfad;
const ANTI_BS_SIZE:usize = 0xFFFF;
pub struct Machine {
pub wide: bool,
pub pool: Pool,
pub block: Block,
pub frame: Vec<Frame>
}
impl Machine {
pub fn new(pool: Pool, block: Block) -> Machine{
Machine {
wide: false,
pool: pool,
block: block,
frame: vec![Frame::new(ANTI_BS_SIZE)],
}
}
pub fn new_from_file(file: &str) -> Result<Machine> {
let mut reader = IJVMReader::new(file).unwrap();
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)?;
return Ok(Machine::new(pool, text));
}
pub fn step(&mut self) -> Result<()> {
match self.block.read_op() {
Ok(Operation::Op(a, func)) => {
// println!("{}", a);
// println!("Stack: {:?}", self.cur_frame().stack.data);
let x = func(self);
// println!("Stack: {:?}", self.cur_frame().stack.data);
x
},
Ok(Operation::Invalid(a)) => {
println!("OP: {}", a);
Err("Invalid op")
},
Err(str) => Err(str)
}
}
pub fn has_step(&self) -> bool {
return self.block.has_i8();
}
pub fn get_program_counter(&self) -> usize {
return self.block.cur()
}
pub fn cur_frame(&mut self) -> &mut Frame {
return self.frame.last_mut().unwrap();
}
pub fn cur_stack(&mut self) -> &mut Stack {
return &mut self.cur_frame().stack;
}
// 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)
}
}

10
src/ijvm/mod.rs Normal file
View File

@@ -0,0 +1,10 @@
pub mod ijvmreader;
pub mod binread;
pub mod block;
pub mod ops;
pub mod machine;
pub mod stack;
pub mod pool;
pub mod frame;
type Result<T> = ::std::result::Result<T, &'static str>;

245
src/ijvm/ops.rs Normal file
View File

@@ -0,0 +1,245 @@
use ijvm::binread::BinRead;
use ijvm::machine::Machine;
use ijvm::frame::Frame;
use ijvm::Result;
use std::io::Write;
use std::io::Read;
pub type OpFunc = fn(&mut Machine) -> Result<()>;
pub enum Operation {
Op(&'static str, OpFunc),
Invalid(u8)
}
const JUMP_OFFSET: i32 = 3;
pub fn num_to_op(op: u8) -> Operation {
match op {
0x00 => Operation::Op("NOP", nop),
0x10 => Operation::Op("BIPUSH", bipush),
0x13 => Operation::Op("LDC_W", ldc_w),
0x15 => Operation::Op("ILOAD", iload),
0x36 => Operation::Op("ISTORE", istore),
0x57 => Operation::Op("POP", pop),
0x59 => Operation::Op("DUP", dup),
0x5F => Operation::Op("SWAP", swap),
0x60 => Operation::Op("IADD", iadd),
0x64 => Operation::Op("ISUB", isub),
0x7E => Operation::Op("IAND", iand),
0xB0 => Operation::Op("IOR", ior),
0x84 => Operation::Op("IINC", iinc),
0x99 => Operation::Op("IFEQ", ifeq),
0x9b => Operation::Op("IFLT", iflt),
0x9F => Operation::Op("IF_ICMPEQ", if_icmpeq),
0xA7 => Operation::Op("GOTO", goto),
0xAC => Operation::Op("IRETURN", ireturn),
0xB6 => Operation::Op("INVOKEVIRTUAL", invokevirtual),
0xC4 => Operation::Op("WIDE", wide),
0xFC => Operation::Op("IN", _in),
0xFD => Operation::Op("OUT", out),
0xFF => Operation::Op("HALT", halt),
0xF0 => Operation::Op("SLP", slp),
x => Operation::Invalid(x)
}
}
fn nop(_: &mut Machine) -> Result<()> {
Ok(())
}
fn bipush(machine: &mut Machine) -> Result<()> {
let val = match machine.block.read_i8() {
Ok(a) => a,
Err(_) => return Err("Expected argument"),
} as i32;
machine.cur_stack().push(val);
Ok(())
}
fn dup(machine: &mut Machine) -> Result<()> {
let val = machine.cur_stack().top()?;
machine.cur_stack().push(val);
Ok(())
}
fn out(machine: &mut Machine) -> Result<()> {
let val = machine.cur_stack().pop()?;
print!("{}", val as u8 as char);
::std::io::stdout().flush().unwrap();
Ok(())
}
fn pop(machine: &mut Machine) -> Result<()> {
machine.cur_stack().pop()?;
Ok(())
}
fn iadd(machine: &mut Machine) -> Result<()> {
let a = machine.cur_stack().pop()?;
let b = machine.cur_stack().pop()?;
machine.cur_stack().push(a + b);
Ok(())
}
fn isub(machine: &mut Machine) -> Result<()> {
let a = machine.cur_stack().pop()?;
let b = machine.cur_stack().pop()?;
machine.cur_stack().push(b - a);
Ok(())
}
fn _in(machine: &mut Machine) -> Result<()> {
let char: Option<u8> =
::std::io::stdin()
.bytes()
.next()
.and_then(|r| r.ok());
let val = match char {
None => 0 as i32,
Some(i) => i as i32
};
machine.cur_stack().push(val);
Ok(())
}
fn goto(machine: &mut Machine) -> Result<()> {
let offset = machine.block.read_i16()? as i32 - JUMP_OFFSET;
machine.block.jump(offset)
}
fn halt(_: &mut Machine) -> Result<()> {
Err("Halt")
}
fn ifeq(machine: &mut Machine) -> Result<()> {
let offset = machine.block.read_i16()? as i32 - JUMP_OFFSET;
if machine.cur_stack().pop()? == 0 {
return machine.block.jump(offset)
}
Ok(())
}
fn iflt(machine: &mut Machine) -> Result<()> {
let offset = machine.block.read_i16()? as i32 - JUMP_OFFSET;
if machine.cur_stack().pop()? < 0 {
return machine.block.jump(offset)
}
Ok(())
}
fn if_icmpeq(machine: &mut Machine) -> Result<()> {
let offset = machine.block.read_i16()? as i32 - 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 = machine.cur_stack().pop()?;
let b = machine.cur_stack().pop()?;
machine.cur_stack().push(a & b);
Ok(())
}
fn ior(machine: &mut Machine) -> Result<()> {
let a = machine.cur_stack().pop()?;
let b = machine.cur_stack().pop()?;
machine.cur_stack().push(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(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;
Ok(())
}
fn iload(machine: &mut Machine) -> Result<()> {
let a = machine.read_index()?;
let val = machine.cur_frame().get(a)?;
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 = machine.block.read_i8()?;
let val = machine.cur_frame().get(a)?;
machine.cur_frame().set(a, val + b as i32)
}
fn invokevirtual(machine: &mut Machine) -> Result<()> {
let method_index = machine.block.read_u16()?;
let invoke_addr = machine.pool.get(method_index as usize)?;
let return_addr = machine.get_program_counter();
// println!("METHOD {}", method_index);
machine.block.seek(invoke_addr as usize)?;
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);
// Lifetime for cur_stack
{
let mut 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
}
// println!("retaddr set {}" ,return_addr);
newframe.set(0, 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...")
};
// println!("stack: {:?}", prev_frame.stack);
let result = prev_frame.stack.pop()?;
let return_addr = prev_frame.get(0)?;
// println!("result: {}\nretaddr: {}", result, return_addr);
machine.cur_stack().push(result);
machine.block.seek(return_addr as usize)
}
fn slp(machine: &mut Machine) -> Result<()> {
use std::time;
use std::thread;
let val = match machine.block.read_u8() {
Ok(a) => a,
Err(_) => return Err("Expected argument"),
} as u64;
let slpdur = time::Duration::from_millis(val*100);
thread::sleep(slpdur);
Ok(())
}

28
src/ijvm/pool.rs Normal file
View File

@@ -0,0 +1,28 @@
use ijvm::Result;
use ijvm::block::Block;
use ijvm::binread::{BinRead, BinReadable};
#[derive(Debug)]
pub struct Pool {
pub data: Vec<i32>
}
impl Pool {
pub fn new(mut block: Block) -> Result<Pool> {
let cap = block.len() / 4;
let mut vec = Vec::with_capacity(cap);
while block.has_i32() {
vec.push(block.read_i32()?);
}
Ok(Pool {
data: vec
})
}
pub fn get(&self, index: usize) -> Result<i32> {
if index >= self.data.len() {
return Err("No such constant")
}
Ok(self.data[index])
}
}

40
src/ijvm/stack.rs Normal file
View File

@@ -0,0 +1,40 @@
use ijvm::Result;
#[derive(Debug)]
pub struct Stack {
pub data: Vec<i32>
}
impl Stack {
pub fn new() -> Stack {
Stack {
data: Vec::new()
}
}
// pub fn len(&self) -> usize {
// return self.data.len();
// }
pub fn top(&self) -> Result<i32> {
Ok(match self.data.last() {
Some(a) => *a,
None => return Err("Stack empty"),
})
}
pub fn pop(&mut self) -> Result<i32> {
Ok(match self.data.pop() {
Some(a) => a,
None => return Err("Stack empty"),
})
}
pub fn push(&mut self, val: i32) {
self.data.push(val);
}
pub fn len(&self) -> usize {
self.data.len()
}
}

31
src/main.rs Normal file
View File

@@ -0,0 +1,31 @@
mod ijvm;
use ijvm::machine::Machine;
use std::env;
fn main() {
let args: Vec<String> = env::args().collect();
if args.len() < 2 {
println!("Usage: {} ijvmfile", args[0]);
return;
}
let ijvmfile = &args[1];
let mut machine = Machine::new_from_file(ijvmfile).unwrap();
while machine.has_step() {
match machine.step() {
Ok(_) => (),
Err(str) => {
println!("\n\nERROR: pc=0x{:x} fp=0x{:x}: {}\nProgram exit",
machine.get_program_counter(),
machine.frame.len() - 1,
str);
return;
}
}
}
}