Now with a hashmap for ops lookup

This commit is contained in:
2019-04-08 16:03:49 +02:00
parent 21b6fc4e64
commit 6ed58151ee
7 changed files with 149 additions and 119 deletions

9
Cargo.lock generated
View File

@@ -1,3 +1,10 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
[[package]]
name = "lazy_static"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "proc-macro2"
version = "0.4.4"
@@ -18,6 +25,7 @@ dependencies = [
name = "rustijvm"
version = "1.0.0"
dependencies = [
"lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.65 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_derive 1.0.65 (registry+https://github.com/rust-lang/crates.io-index)",
]
@@ -53,6 +61,7 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[metadata]
"checksum lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bc5729f27f159ddd61f4df6228e827e86643d4d3e7c32183cb30a1c08f604a14"
"checksum proc-macro2 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "1fa93823f53cfd0f5ac117b189aed6cfdfb2cfc0a9d82e956dd7927595ed7d46"
"checksum quote 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e44651a0dc4cdd99f71c83b561e221f714912d11af1a4dff0631f923d53af035"
"checksum serde 1.0.65 (registry+https://github.com/rust-lang/crates.io-index)" = "5d47469df098fe8701d4da22680da5145e83801bdaaafea0cf91a180436fc343"

View File

@@ -6,6 +6,7 @@ authors = ["Jur van den Berg <Jurl.berg@gmail.com>"]
[dependencies]
serde = "1.0"
serde_derive = "1.0"
lazy_static = "1.3.0"
[features]
default = ["bonus", "extra"]

View File

@@ -1,13 +1,13 @@
use Result;
use block::Block;
use ops::{Operation,num_to_op};
use ops::{num_to_op, Operation};
use Result;
#[allow(unknown_lints, len_without_is_empty)]
pub trait BinReadable {
fn get(&mut self) -> Result<u8>;
fn cur(&self) -> usize;
fn len(&self) -> usize;
fn slice(&mut self, len: usize) -> &[u8];
fn is_empty(&self) -> bool;
}
@@ -25,7 +25,7 @@ pub trait BinRead {
fn read_i32(&mut self) -> Result<i32>;
fn read_block(&mut self) -> Result<Block>;
fn read_op(&mut self) -> Result<Operation>;
fn read_op(&mut self) -> Result<&'static Operation>;
}
impl<T: BinReadable> BinRead for T {
@@ -47,13 +47,13 @@ impl<T: BinReadable> BinRead for T {
fn read_u16(&mut self) -> Result<u16> {
let a = u16::from(self.read_u8()?);
let b = u16::from( self.read_u8()?);
let b = u16::from(self.read_u8()?);
Ok(a << 8 | b)
}
fn read_u32(&mut self) -> Result<u32> {
let a = u32::from(self.read_u16()?);
let b = u32::from( self.read_u16()?);
let b = u32::from(self.read_u16()?);
Ok(a << 16 | b)
}
fn read_i8(&mut self) -> Result<i8> {
@@ -78,7 +78,7 @@ impl<T: BinReadable> BinRead for T {
Ok(Block::new(origin, self.slice(len)))
}
fn read_op(&mut self) -> Result<Operation> {
fn read_op(&mut self) -> Result<&'static Operation> {
Ok(num_to_op(self.read_u8()?))
}
}
}

View File

@@ -1,12 +1,12 @@
use binread::{BinRead, BinReadable};
use block::Block;
use ijvmreader::IJVMReader;
use binread::{BinRead, BinReadable};
use machine::MAGIC_HEADER;
use Result;
use ops;
use Result;
use std::collections::HashMap;
use std::clone::Clone;
use std::collections::HashMap;
use std::fmt;
use std::rc::Rc;
@@ -73,7 +73,7 @@ pub struct Instruction {
op: OpCode,
name: &'static str,
params: Vec<i32>,
types: Vec<ops::Args>,
types: &'static Vec<ops::Args>,
wide: bool,
pos: usize,
label: bool,
@@ -84,7 +84,8 @@ pub struct Instruction {
impl fmt::Display for Instruction {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if self.label {
let name = self.symbols
let name = self
.symbols
.lookup_label(self.pos)
.unwrap_or_else(|| format!("LBL_{}", self.pos));
writeln!(f, "\n{}:", name)?;
@@ -99,14 +100,16 @@ impl fmt::Display for Instruction {
for (typ, val) in self.types.iter().zip(&self.params) {
match *typ {
ops::Args::Label => {
let name = self.symbols
let name = self
.symbols
.lookup_label(*val as usize)
.unwrap_or_else(|| format!("LBL_{}", val));
write!(f, " {}", name)?;
},
}
ops::Args::Constant => {
if self.op == 0xB6 {
let name = self.symbols
let name = self
.symbols
.lookup_method_idx(*val as usize)
.unwrap_or_else(|| format!("func_{}", val));
@@ -114,7 +117,7 @@ impl fmt::Display for Instruction {
} else {
write!(f, " CONST_{}", val)?;
}
},
}
ops::Args::Var => write!(f, " var{}", val)?,
_ => write!(f, " {}", val)?,
}
@@ -166,16 +169,15 @@ impl Method {
}
pub fn update_vars(&mut self) {
let max = self.instructions
let max = self
.instructions
.iter()
.filter(|e| !e.types.is_empty())
.filter(|e| {
match e.types[0] {
ops::Args::Var => true,
_ => false,
}
.filter(|e| match e.types[0] {
ops::Args::Var => true,
_ => false,
})
.map(|e| e.params[0] )
.map(|e| e.params[0])
.max();
if let Some(v) = max {
@@ -205,7 +207,7 @@ impl fmt::Display for Method {
write!(f, "{}var{}", comma, i)?;
comma = ", ";
}
writeln!(f,")")?;
writeln!(f, ")")?;
0
};
@@ -223,7 +225,6 @@ impl fmt::Display for Method {
write!(f, "{}", inst)?;
}
if self.name == "main" {
writeln!(f, "\n.end-main\n")?;
} else {
@@ -273,10 +274,10 @@ impl Disassembler {
}
let (name, args) = match ops::num_to_op(opcode) {
ops::Operation::Invalid(x) => {
eprintln!("INVALID OP 0x{:X}\n", x);
ops::Operation::Invalid => {
eprintln!("INVALID OP 0x{:X}\n", opcode);
return Err("Invalid operation");
},
}
ops::Operation::Op(name, _, args) => (name, args),
};
@@ -290,7 +291,7 @@ impl Disassembler {
} else {
i32::from(self.text.read_u8()?)
}
},
}
ops::Args::Label => {
let offset = self.text.read_i16()?;
let target = pos as i64 + i64::from(offset);
@@ -299,7 +300,7 @@ impl Disassembler {
}
method.labels.push(target as usize);
target as i32
},
}
ops::Args::Constant => i32::from(self.text.read_u16()?),
};
params.push(v);
@@ -338,7 +339,8 @@ impl Disassembler {
let arg_count = (self.text.read_u16()? - 1) as usize;
let var_count = self.text.read_u16()? as usize;
let callee = self.disasm_method(inst.params[0], target_addr, arg_count, var_count)?;
let callee =
self.disasm_method(inst.params[0], target_addr, arg_count, var_count)?;
self.disassembly.methods.push(callee);
self.text.seek(save_pos)?;
@@ -351,7 +353,10 @@ impl Disassembler {
}
fn disasm_method(&mut self, i: i32, pc: usize, args: usize, vars: usize) -> Result<Method> {
let name = self.symbols.lookup_method(pc).unwrap_or_else(|| format!("func_{}", i));
let name = self
.symbols
.lookup_method(pc)
.unwrap_or_else(|| format!("func_{}", i));
let mut method = Method::new(name, pc, args, vars);
while self.text.has_i8() && !self.is_method(self.text.cur()) {
@@ -400,7 +405,6 @@ impl Disassembler {
main.update_vars();
self.disassembly.methods.insert(0, main);
Ok(self.disassembly.clone())
}
}
@@ -419,14 +423,13 @@ impl Disassembly {
}
let mut pool = match reader.read_block() {
Ok(a) => a,
Err(_) => return Err("Failed to read constants block")
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")
Err(_) => return Err("Failed to read text block"),
};
let mut symbols = DebugSymbols::default();
if let Ok(block) = reader.read_block() {
symbols.add_methods(block)?;
@@ -454,14 +457,13 @@ impl Disassembly {
let reader = IJVMReader::new_from_slice(source);
Self::disassemble_reader(reader)
}
}
impl fmt::Display for Disassembly {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if !self.constants.is_empty() {
writeln!(f, ".constant")?;
for (i, c) in self.constants.iter().enumerate() {
for (i, c) in self.constants.iter().enumerate() {
if !c.method {
writeln!(f, " CONST_{:<6} {}", i, c.value)?;
}

View File

@@ -1,5 +1,7 @@
#[macro_use]
extern crate serde_derive;
#[macro_use]
extern crate lazy_static;
pub mod ijvmreader;
pub mod binread;

View File

@@ -1,22 +1,22 @@
use std::io::{Read, Write};
use std::sync::{Mutex, Arc};
use std::sync::{Arc, Mutex};
use Result;
use block::Block;
use ops::{Operation, Args, num_to_op};
use ijvmreader::IJVMReader;
use binread::{BinRead, BinReadable};
use block::Block;
use frame::Frame;
use stack::Stack;
use ijvmreader::IJVMReader;
use ops::{num_to_op, Args, Operation};
use pool::Pool;
use stack::Stack;
use Result;
#[cfg(feature = "bonus:network")]
use netstack::NetStack;
#[cfg(feature = "bonus:heap")]
use heap::Heap;
#[cfg(feature = "bonus:network")]
use netstack::NetStack;
pub const MAGIC_HEADER:u32 = 0x1DEA_DFAD;
const ANTI_BS_SIZE:usize = 0xFF;
pub const MAGIC_HEADER: u32 = 0x1DEA_DFAD;
const ANTI_BS_SIZE: usize = 0xFF;
pub struct Machine {
pub wide: bool,
@@ -32,11 +32,10 @@ pub struct Machine {
pub stream_in: Box<Read + Send + Sync>,
pub stream_out: Arc<Mutex<Write + Send + Sync>>,
}
impl Machine {
pub fn new(pool: Pool, block: Block) -> Machine{
pub fn new(pool: Pool, block: Block) -> Machine {
Machine {
wide: false,
halted: false,
@@ -61,11 +60,11 @@ impl Machine {
}
let constants = match reader.read_block() {
Ok(a) => a,
Err(_) => return Err("Failed to read constants block")
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")
Err(_) => return Err("Failed to read text block"),
};
let pool = Pool::new(constants)?;
@@ -95,12 +94,12 @@ impl Machine {
} else {
func(self)
}
},
Ok(Operation::Invalid(a)) => {
println!("UNDEFINED OP: 0x{:X}", a);
}
Ok(Operation::Invalid) => {
println!("Reached invalid operation");
Err("Invalid op")
},
Err(str) => Err(str)
}
Err(str) => Err(str),
}
}
@@ -149,11 +148,11 @@ impl Machine {
}
let (name, args) = match num_to_op(opcode) {
Operation::Invalid(_) => return Err("Invalid operation"),
Operation::Invalid => return Err("Invalid operation"),
Operation::Op(name, _, args) => (name, args),
};
for arg in &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()?),
@@ -163,7 +162,7 @@ impl Machine {
} else {
i32::from(self.block.read_u8()?)
}
},
}
Args::Label => i32::from(self.block.read_i16()?),
Args::Constant => i32::from(self.block.read_u16()?),
};
@@ -174,14 +173,14 @@ impl Machine {
Ok((name.to_string(), params))
}
// pub fn get_stack_pointer(&self) -> usize {
// return self.frame.last().unwrap().stack.len();
// }
// 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)
return Ok(self.block.read_u16()? as usize);
}
Ok(self.block.read_u8()? as usize)
}
@@ -193,4 +192,4 @@ impl Machine {
pub fn set_output(&mut self, outstream: Arc<Mutex<Write + Send + Sync>>) {
self.stream_out = outstream;
}
}
}

View File

@@ -1,8 +1,10 @@
use binread::BinRead;
use machine::Machine;
use frame::Frame;
use machine::Machine;
use std::collections::HashMap;
use std::io::Read;
use std::num::Wrapping;
use Result;
use std::io::{Read};
pub type OpFunc = fn(&mut Machine) -> Result<()>;
@@ -17,61 +19,77 @@ pub enum Args {
pub enum Operation {
Op(&'static str, OpFunc, Vec<Args>),
Invalid(u8)
Invalid,
}
const JUMP_OFFSET: i32 = 3;
const INVALID_OP: Operation = Operation::Invalid;
pub fn num_to_op(op: u8) -> Operation {
match op {
0x00 => Operation::Op("NOP", nop, vec![]),
0x10 => Operation::Op("BIPUSH", bipush, vec![Args::Byte]),
0x13 => Operation::Op("LDC_W", ldc_w, vec![Args::Constant]),
0x15 => Operation::Op("ILOAD", iload, vec![Args::Var]),
0x36 => Operation::Op("ISTORE", istore, vec![Args::Var]),
0x57 => Operation::Op("POP", pop, vec![]),
0x59 => Operation::Op("DUP", dup, vec![]),
0x5F => Operation::Op("SWAP", swap, vec![]),
0x60 => Operation::Op("IADD", iadd, vec![]),
0x64 => Operation::Op("ISUB", isub, vec![]),
0x7E => Operation::Op("IAND", iand, vec![]),
0xB0 => Operation::Op("IOR", ior, vec![]),
0x84 => Operation::Op("IINC", iinc, vec![Args::Var, Args::Byte]),
0x99 => Operation::Op("IFEQ", ifeq, vec![Args::Label]),
0x9b => Operation::Op("IFLT", iflt, vec![Args::Label]),
0x9F => Operation::Op("IF_ICMPEQ", if_icmpeq, vec![Args::Label]),
0xA7 => Operation::Op("GOTO", goto, vec![Args::Label]),
0xAC => Operation::Op("IRETURN", ireturn, vec![]),
0xB6 => Operation::Op("INVOKEVIRTUAL", invokevirtual, vec![Args::Constant]),
0xC4 => Operation::Op("WIDE", wide, vec![]),
0xFC => Operation::Op("IN", _in, vec![]),
0xFD => Operation::Op("OUT", out, vec![]),
0xFE => Operation::Op("ERR", err, vec![]),
0xFF => Operation::Op("HALT", halt, vec![]),
lazy_static! {
static ref OPS_MAP: HashMap<u8, Operation> = {
let mut m = HashMap::new();
m.insert(0x00, Operation::Op("NOP", nop, vec![]));
m.insert(0x10, Operation::Op("BIPUSH", bipush, vec![Args::Byte]));
m.insert(0x13, Operation::Op("LDC_W", ldc_w, vec![Args::Constant]));
m.insert(0x15, Operation::Op("ILOAD", iload, vec![Args::Var]));
m.insert(0x36, Operation::Op("ISTORE", istore, vec![Args::Var]));
m.insert(0x57, Operation::Op("POP", pop, vec![]));
m.insert(0x59, Operation::Op("DUP", dup, vec![]));
m.insert(0x5F, Operation::Op("SWAP", swap, vec![]));
m.insert(0x60, Operation::Op("IADD", iadd, vec![]));
m.insert(0x64, Operation::Op("ISUB", isub, vec![]));
m.insert(0x7E, Operation::Op("IAND", iand, vec![]));
m.insert(0xB0, Operation::Op("IOR", ior, vec![]));
m.insert(
0x84,
Operation::Op("IINC", iinc, vec![Args::Var, Args::Byte]),
);
m.insert(0x99, Operation::Op("IFEQ", ifeq, vec![Args::Label]));
m.insert(0x9b, Operation::Op("IFLT", iflt, vec![Args::Label]));
m.insert(
0x9F,
Operation::Op("IF_ICMPEQ", if_icmpeq, vec![Args::Label]),
);
m.insert(0xA7, Operation::Op("GOTO", goto, vec![Args::Label]));
m.insert(0xAC, Operation::Op("IRETURN", ireturn, vec![]));
m.insert(
0xB6,
Operation::Op("INVOKEVIRTUAL", invokevirtual, vec![Args::Constant]),
);
m.insert(0xC4, Operation::Op("WIDE", wide, vec![]));
m.insert(0xFC, Operation::Op("IN", _in, vec![]));
m.insert(0xFD, Operation::Op("OUT", out, vec![]));
m.insert(0xFE, Operation::Op("ERR", err, vec![]));
m.insert(0xFF, Operation::Op("HALT", halt, vec![]));
#[cfg(feature = "extra:sleep")]
0xF0 => Operation::Op("SLP", slp, vec![Args::Byte]),
m.insert(0xF0, Operation::Op("SLP", slp, vec![Args::Byte]));
#[cfg(feature = "bonus:network")]
0xE1 => Operation::Op("NETBIND", netbind, vec![]),
m.insert(0xE1, Operation::Op("NETBIND", netbind, vec![]));
#[cfg(feature = "bonus:network")]
0xE2 => Operation::Op("NETCONNECT", netconnect, vec![]),
m.insert(0xE2, Operation::Op("NETCONNECT", netconnect, vec![]));
#[cfg(feature = "bonus:network")]
0xE3 => Operation::Op("NETIN", netin, vec![]),
m.insert(0xE3, Operation::Op("NETIN", netin, vec![]));
#[cfg(feature = "bonus:network")]
0xE4 => Operation::Op("NETOUT", netout, vec![]),
m.insert(0xE4, Operation::Op("NETOUT", netout, vec![]));
#[cfg(feature = "bonus:network")]
0xE5 => Operation::Op("NETCLOSE", netclose, vec![]),
m.insert(0xE5, Operation::Op("NETCLOSE", netclose, vec![]));
#[cfg(feature = "bonus:heap")]
0xD1 => Operation::Op("NEWARRAY", newarray, vec![]),
m.insert(0xD1, Operation::Op("NEWARRAY", newarray, vec![]));
#[cfg(feature = "bonus:heap")]
0xD2 => Operation::Op("IALOAD", iaload, vec![]),
m.insert(0xD2, Operation::Op("IALOAD", iaload, vec![]));
#[cfg(feature = "bonus:heap")]
0xD3 => Operation::Op("IASTORE", iastore, vec![]),
m.insert(0xD3, Operation::Op("IASTORE", iastore, vec![]));
x => Operation::Invalid(x)
}
m
};
}
pub fn num_to_op(op: u8) -> &'static Operation {
OPS_MAP.get(&op).unwrap_or(&INVALID_OP)
}
fn nop(_: &mut Machine) -> Result<()> {
@@ -108,16 +126,16 @@ fn pop(machine: &mut Machine) -> Result<()> {
}
fn iadd(machine: &mut Machine) -> Result<()> {
let a = machine.cur_stack().pop()?;
let b = machine.cur_stack().pop()?;
machine.cur_stack().push(a + b);
let a = Wrapping(machine.cur_stack().pop()?);
let b = Wrapping(machine.cur_stack().pop()?);
machine.cur_stack().push((a + b).0);
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);
let a = Wrapping(machine.cur_stack().pop()?);
let b = Wrapping(machine.cur_stack().pop()?);
machine.cur_stack().push((b - a).0);
Ok(())
}
@@ -150,7 +168,7 @@ fn halt(machine: &mut Machine) -> Result<()> {
fn ifeq(machine: &mut Machine) -> Result<()> {
let offset = i32::from(machine.block.read_i16()?) - JUMP_OFFSET;
if machine.cur_stack().pop()? == 0 {
return machine.block.jump(offset)
return machine.block.jump(offset);
}
Ok(())
}
@@ -158,7 +176,7 @@ fn ifeq(machine: &mut Machine) -> Result<()> {
fn iflt(machine: &mut Machine) -> Result<()> {
let offset = i32::from(machine.block.read_i16()?) - JUMP_OFFSET;
if machine.cur_stack().pop()? < 0 {
return machine.block.jump(offset)
return machine.block.jump(offset);
}
Ok(())
}
@@ -166,7 +184,7 @@ fn iflt(machine: &mut Machine) -> Result<()> {
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)
return machine.block.jump(offset);
}
Ok(())
}
@@ -249,13 +267,12 @@ fn invokevirtual(machine: &mut Machine) -> Result<()> {
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...")
None => return Err("Got no frame... somehow..."),
};
let result = prev_frame.stack.pop()?;
let return_addr = prev_frame.get(0)?;
@@ -265,13 +282,13 @@ fn ireturn(machine: &mut Machine) -> Result<()> {
#[cfg(feature = "extra:sleep")]
fn slp(machine: &mut Machine) -> Result<()> {
use std::time;
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);
let slpdur = time::Duration::from_millis(val * 100);
thread::sleep(slpdur);
Ok(())
}