Merge branch 'stack-type-safety'

* stack-type-safety:
  Fix tests
  Initial stack typing
This commit is contained in:
2019-05-29 15:41:13 +02:00
9 changed files with 135 additions and 77 deletions

View File

@@ -1,11 +1,12 @@
use stack::Stack; use stack::Stack;
use Result; use Result;
use value::Value;
#[derive(Debug)] #[derive(Debug)]
pub struct Frame { pub struct Frame {
pub base: usize, pub base: usize,
pub stack: Stack, pub stack: Stack,
pub locals: Vec<i32>, pub locals: Vec<Value>,
pub can_extend_locals: bool, pub can_extend_locals: bool,
} }
@@ -17,7 +18,7 @@ impl Frame {
Frame { Frame {
base, base,
stack: Stack::new(), stack: Stack::new(),
locals: vec![0; num_locals], locals: vec![Value::Int(0); num_locals],
can_extend_locals: false, can_extend_locals: false,
} }
} }
@@ -42,21 +43,21 @@ impl Frame {
self.locals.is_empty() self.locals.is_empty()
} }
pub fn get(&mut self, offset: usize) -> Result<i32> { pub fn get(&mut self, offset: usize) -> Result<&Value> {
if offset >= self.locals.len() { if offset >= self.locals.len() {
if self.can_extend_locals { if self.can_extend_locals {
self.locals.resize(offset + 1, 0); self.locals.resize(offset + 1, Value::Int(0));
} else { } else {
return Err("Local variable out of range"); return Err("Local variable out of range");
} }
} }
Ok(self.locals[offset]) Ok(&self.locals[offset])
} }
pub fn set(&mut self, offset: usize, val: i32) -> Result<()> { pub fn set(&mut self, offset: usize, val: Value) -> Result<()> {
if offset >= self.locals.len() { if offset >= self.locals.len() {
if self.can_extend_locals { if self.can_extend_locals {
self.locals.resize(offset + 1, 0); self.locals.resize(offset + 1, Value::Int(0));
} else { } else {
return Err("Local variable out of range"); return Err("Local variable out of range");
} }

View File

@@ -6,6 +6,7 @@ extern crate lazy_static;
pub mod ijvmreader; pub mod ijvmreader;
pub mod binread; pub mod binread;
pub mod block; pub mod block;
pub mod value;
pub mod ops; pub mod ops;
pub mod stack; pub mod stack;
pub mod pool; pub mod pool;

View File

@@ -14,6 +14,7 @@ use Result;
use heap::Heap; use heap::Heap;
#[cfg(feature = "bonus:network")] #[cfg(feature = "bonus:network")]
use netstack::NetStack; use netstack::NetStack;
use std::convert::TryInto;
pub const MAGIC_HEADER: u32 = 0x1DEA_DFAD; pub const MAGIC_HEADER: u32 = 0x1DEA_DFAD;
@@ -118,7 +119,7 @@ impl Machine {
} }
pub fn get_tos(&mut self) -> Result<i32> { pub fn get_tos(&mut self) -> Result<i32> {
self.cur_stack().top() self.cur_stack().top()?.try_into()
} }
pub fn cur_frame(&mut self) -> &mut Frame { pub fn cur_frame(&mut self) -> &mut Frame {

View File

@@ -4,6 +4,8 @@ use machine::Machine;
use std::io::Read; use std::io::Read;
use std::num::Wrapping; use std::num::Wrapping;
use Result; use Result;
use value::Value;
use std::convert::TryInto;
pub type OpFunc = fn(&mut Machine) -> Result<()>; pub type OpFunc = fn(&mut Machine) -> Result<()>;
@@ -91,23 +93,24 @@ fn bipush(machine: &mut Machine) -> Result<()> {
Ok(a) => i32::from(a), Ok(a) => i32::from(a),
Err(_) => return Err("Expected argument"), Err(_) => return Err("Expected argument"),
}; };
machine.cur_stack().push(val); machine.cur_stack().push(Value::Int(val));
Ok(()) Ok(())
} }
fn dup(machine: &mut Machine) -> Result<()> { fn dup(machine: &mut Machine) -> Result<()> {
let val = machine.cur_stack().top()?; let val = machine.cur_stack().top()?.clone();
machine.cur_stack().push(val); machine.cur_stack().push(val);
Ok(()) Ok(())
} }
fn out(machine: &mut Machine) -> Result<()> { fn out(machine: &mut Machine) -> Result<()> {
let val = machine.cur_stack().pop()?; let val: i32 = machine.cur_stack().pop()?.try_into()?;
let buffer = [val as u8]; let buffer = [val as u8];
let mut out = machine.stream_out.lock().unwrap(); let mut out = machine.stream_out.lock().unwrap();
out.write_all(&buffer).unwrap(); out.write_all(&buffer).unwrap();
out.flush().unwrap(); out.flush().unwrap();
Ok(()) return Ok(());
} }
fn pop(machine: &mut Machine) -> Result<()> { fn pop(machine: &mut Machine) -> Result<()> {
@@ -116,16 +119,17 @@ fn pop(machine: &mut Machine) -> Result<()> {
} }
fn iadd(machine: &mut Machine) -> Result<()> { fn iadd(machine: &mut Machine) -> Result<()> {
let a = Wrapping(machine.cur_stack().pop()?); let a: i32 = machine.cur_stack().pop()?.try_into()?;
let b = Wrapping(machine.cur_stack().pop()?); let b: i32 = machine.cur_stack().pop()?.try_into()?;
machine.cur_stack().push((a + b).0); let res = Wrapping(a) + Wrapping(b);
machine.cur_stack().push(Value::Int(res.0));
Ok(()) Ok(())
} }
fn isub(machine: &mut Machine) -> Result<()> { fn isub(machine: &mut Machine) -> Result<()> {
let a = Wrapping(machine.cur_stack().pop()?); let a: Wrapping<i32> = Wrapping(machine.cur_stack().pop()?.try_into()?);
let b = Wrapping(machine.cur_stack().pop()?); let b: Wrapping<i32> = Wrapping(machine.cur_stack().pop()?.try_into()?);
machine.cur_stack().push((b - a).0); machine.cur_stack().push(Value::Int((b - a).0));
Ok(()) Ok(())
} }
@@ -135,7 +139,7 @@ fn _in(machine: &mut Machine) -> Result<()> {
Err(_) => 0i32, Err(_) => 0i32,
Ok(_) => i32::from(buffer[0]), Ok(_) => i32::from(buffer[0]),
}; };
machine.cur_stack().push(val); machine.cur_stack().push(Value::Int(val));
Ok(()) Ok(())
} }
@@ -157,7 +161,8 @@ fn halt(machine: &mut Machine) -> Result<()> {
fn ifeq(machine: &mut Machine) -> Result<()> { fn ifeq(machine: &mut Machine) -> Result<()> {
let offset = i32::from(machine.block.read_i16()?) - JUMP_OFFSET; let offset = i32::from(machine.block.read_i16()?) - JUMP_OFFSET;
if machine.cur_stack().pop()? == 0 { let compare: i32 = machine.cur_stack().pop()?.try_into()?;
if compare == 0 {
return machine.block.jump(offset); return machine.block.jump(offset);
} }
Ok(()) Ok(())
@@ -165,7 +170,8 @@ fn ifeq(machine: &mut Machine) -> Result<()> {
fn iflt(machine: &mut Machine) -> Result<()> { fn iflt(machine: &mut Machine) -> Result<()> {
let offset = i32::from(machine.block.read_i16()?) - JUMP_OFFSET; let offset = i32::from(machine.block.read_i16()?) - JUMP_OFFSET;
if machine.cur_stack().pop()? < 0 { let compare: i32 = machine.cur_stack().pop()?.try_into()?;
if compare < 0 {
return machine.block.jump(offset); return machine.block.jump(offset);
} }
Ok(()) Ok(())
@@ -180,23 +186,23 @@ fn if_icmpeq(machine: &mut Machine) -> Result<()> {
} }
fn iand(machine: &mut Machine) -> Result<()> { fn iand(machine: &mut Machine) -> Result<()> {
let a = machine.cur_stack().pop()?; let a: i32 = machine.cur_stack().pop()?.try_into()?;
let b = machine.cur_stack().pop()?; let b: i32 = machine.cur_stack().pop()?.try_into()?;
machine.cur_stack().push(a & b); machine.cur_stack().push(Value::Int(a & b));
Ok(()) Ok(())
} }
fn ior(machine: &mut Machine) -> Result<()> { fn ior(machine: &mut Machine) -> Result<()> {
let a = machine.cur_stack().pop()?; let a: i32 = machine.cur_stack().pop()?.try_into()?;
let b = machine.cur_stack().pop()?; let b: i32 = machine.cur_stack().pop()?.try_into()?;
machine.cur_stack().push(a | b); machine.cur_stack().push(Value::Int(a | b));
Ok(()) Ok(())
} }
fn ldc_w(machine: &mut Machine) -> Result<()> { fn ldc_w(machine: &mut Machine) -> Result<()> {
let offset = machine.block.read_i16()? as usize; let offset = machine.block.read_i16()? as usize;
let val = machine.pool.get(offset)?; let val = machine.pool.get(offset)?;
machine.cur_stack().push(val); machine.cur_stack().push(Value::Int(val));
Ok(()) Ok(())
} }
@@ -215,7 +221,7 @@ fn wide(machine: &mut Machine) -> Result<()> {
fn iload(machine: &mut Machine) -> Result<()> { fn iload(machine: &mut Machine) -> Result<()> {
let a = machine.read_index()?; let a = machine.read_index()?;
let val = machine.cur_frame().get(a)?; let val = machine.cur_frame().get(a)?.clone();
machine.cur_stack().push(val); machine.cur_stack().push(val);
Ok(()) Ok(())
} }
@@ -229,8 +235,8 @@ fn istore(machine: &mut Machine) -> Result<()> {
fn iinc(machine: &mut Machine) -> Result<()> { fn iinc(machine: &mut Machine) -> Result<()> {
let a = machine.read_index()?; let a = machine.read_index()?;
let b = i32::from(machine.block.read_i8()?); let b = i32::from(machine.block.read_i8()?);
let val = machine.cur_frame().get(a)?; let val: i32 = machine.cur_frame().get(a)?.try_into()?;
machine.cur_frame().set(a, val + b) machine.cur_frame().set(a, Value::Int(val + b))
} }
fn invokevirtual(machine: &mut Machine) -> Result<()> { fn invokevirtual(machine: &mut Machine) -> Result<()> {
@@ -253,7 +259,7 @@ fn invokevirtual(machine: &mut Machine) -> Result<()> {
cur_stack.pop()?; // Nuke the OBJREF cur_stack.pop()?; // Nuke the OBJREF
} }
newframe.set(0, return_addr as i32)?; newframe.set(0, Value::Int(return_addr as i32))?;
machine.frame.push(newframe); machine.frame.push(newframe);
Ok(()) Ok(())
@@ -265,7 +271,7 @@ fn ireturn(machine: &mut Machine) -> Result<()> {
None => return Err("Got no frame... somehow..."), None => return Err("Got no frame... somehow..."),
}; };
let result = prev_frame.stack.pop()?; let result = prev_frame.stack.pop()?;
let return_addr = prev_frame.get(0)?; let return_addr: i32 = prev_frame.get(0)?.try_into()?;
machine.cur_stack().push(result); machine.cur_stack().push(result);
machine.block.seek(return_addr as usize) machine.block.seek(return_addr as usize)
} }
@@ -285,28 +291,28 @@ fn slp(machine: &mut Machine) -> Result<()> {
#[cfg(feature = "bonus:network")] #[cfg(feature = "bonus:network")]
fn netbind(machine: &mut Machine) -> Result<()> { fn netbind(machine: &mut Machine) -> Result<()> {
let port = machine.cur_stack().pop()? as u16; let port: i32 = machine.cur_stack().pop()?.try_into()?;
machine.net.bind(port) machine.net.bind(port as u16)
} }
#[cfg(feature = "bonus:network")] #[cfg(feature = "bonus:network")]
fn netconnect(machine: &mut Machine) -> Result<()> { fn netconnect(machine: &mut Machine) -> Result<()> {
let port = machine.cur_stack().pop()? as u16; let port: i32 = machine.cur_stack().pop()?.try_into()?;
let host = machine.cur_stack().pop()? as u32; let host: i32 = machine.cur_stack().pop()?.try_into()?;
machine.net.connect(host, port) machine.net.connect(host as u32, port as u16)
} }
#[cfg(feature = "bonus:network")] #[cfg(feature = "bonus:network")]
fn netin(machine: &mut Machine) -> Result<()> { fn netin(machine: &mut Machine) -> Result<()> {
let byte = i32::from(machine.net.read_byte()?); let byte = i32::from(machine.net.read_byte()?);
machine.cur_stack().push(byte); machine.cur_stack().push(Value::Int(byte));
Ok(()) Ok(())
} }
#[cfg(feature = "bonus:network")] #[cfg(feature = "bonus:network")]
fn netout(machine: &mut Machine) -> Result<()> { fn netout(machine: &mut Machine) -> Result<()> {
let val = machine.cur_stack().pop()? as u8; let val: i32 = machine.cur_stack().pop()?.try_into()?;
machine.net.write_byte(val) machine.net.write_byte(val as u8)
} }
#[cfg(feature = "bonus:network")] #[cfg(feature = "bonus:network")]
@@ -316,35 +322,41 @@ fn netclose(machine: &mut Machine) -> Result<()> {
#[cfg(feature = "bonus:heap")] #[cfg(feature = "bonus:heap")]
fn newarray(machine: &mut Machine) -> Result<()> { fn newarray(machine: &mut Machine) -> Result<()> {
let size = machine.cur_stack().pop()? as usize; let size: i32 = machine.cur_stack().pop()?.try_into()?;
let heapref = machine.heap.alloc(size); let heapref = machine.heap.alloc(size as usize);
machine.cur_stack().push(heapref as i32); machine.cur_stack().push(Value::HeapRef(heapref));
Ok(()) Ok(())
} }
#[cfg(feature = "bonus:heap")] #[cfg(feature = "bonus:heap")]
fn iastore(machine: &mut Machine) -> Result<()> { fn iastore(machine: &mut Machine) -> Result<()> {
let heapref = machine.cur_stack().pop()? as usize; let heapref = match machine.cur_stack().pop()? {
let index = machine.cur_stack().pop()? as usize; Value::HeapRef(a) => a,
let value = machine.cur_stack().pop()?; _ => return Err("Cannot use int as heapref"),
};
let index: i32 = machine.cur_stack().pop()?.try_into()?;
let value: i32 = machine.cur_stack().pop()?.try_into()?;
let heap = machine.heap.get(heapref); let heap = machine.heap.get(heapref);
heap[index] = value; heap[index as usize] = value;
Ok(()) Ok(())
} }
#[cfg(feature = "bonus:heap")] #[cfg(feature = "bonus:heap")]
fn iaload(machine: &mut Machine) -> Result<()> { fn iaload(machine: &mut Machine) -> Result<()> {
let heapref = machine.cur_stack().pop()? as usize; let heapref = match machine.cur_stack().pop()? {
let index = machine.cur_stack().pop()? as usize; Value::HeapRef(a) => a,
_ => return Err("Cannot use int as heapref"),
};
let index: i32 = machine.cur_stack().pop()?.try_into()?;
let value: i32; let value: i32;
{ {
let heap = machine.heap.get(heapref); let heap = machine.heap.get(heapref);
value = heap[index]; value = heap[index as usize];
} }
machine.cur_stack().push(value); machine.cur_stack().push(Value::Int(value));
Ok(()) Ok(())
} }

View File

@@ -1,8 +1,10 @@
use Result; use Result;
use value::Value;
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub struct Stack { pub struct Stack {
pub data: Vec<i32> pub data: Vec<Value>
} }
impl Stack { impl Stack {
@@ -20,25 +22,25 @@ impl Stack {
self.len() == 0 self.len() == 0
} }
pub fn get(&self, i: usize) -> i32 { pub fn get(&self, i: usize) -> &Value {
self.data[i] &self.data[i]
} }
pub fn top(&self) -> Result<i32> { pub fn top(&self) -> Result<&Value> {
Ok(match self.data.last() { Ok(match self.data.last() {
Some(a) => *a, Some(a) => a,
None => return Err("Stack empty"), None => return Err("Stack empty"),
}) })
} }
pub fn pop(&mut self) -> Result<i32> { pub fn pop(&mut self) -> Result<Value> {
Ok(match self.data.pop() { Ok(match self.data.pop() {
Some(a) => a, Some(a) => a,
None => return Err("Stack empty"), None => return Err("Stack empty"),
}) })
} }
pub fn push(&mut self, val: i32) { pub fn push(&mut self, val: Value) {
self.data.push(val); self.data.push(val);
} }
} }

30
src/value.rs Normal file
View File

@@ -0,0 +1,30 @@
use Result;
use std::convert::TryInto;
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum Value {
Int(i32),
HeapRef(usize),
}
impl TryInto<i32> for Value {
type Error = &'static str;
fn try_into(self) -> Result<i32> {
match self {
Value::Int(a) => Ok(a),
Value::HeapRef(_) => Err("Cannot use HeapRef as i32"),
}
}
}
impl TryInto<i32> for &Value {
type Error = &'static str;
fn try_into(self) -> Result<i32> {
match self {
&Value::Int(a) => Ok(a),
&Value::HeapRef(_) => Err("Cannot use HeapRef as i32"),
}
}
}

View File

@@ -8,12 +8,13 @@ fn steps(machine: &mut rustijvm::Machine, num: usize) {
#[test] #[test]
fn advanced3_wide1() { fn advanced3_wide1() {
let mut machine = rustijvm::Machine::new_from_file("files/advanced/test-wide1.ijvm").unwrap(); use rustijvm::value::Value::Int;
let mut machine = rustijvm::Machine::new_from_file("files/advanced/test-wide1.ijvm").unwrap();
steps(&mut machine, 6); steps(&mut machine, 6);
assert_eq!(machine.cur_frame().get(1), Ok(0x1)); assert_eq!(machine.cur_frame().get(1), Ok(&Int(0x1)));
steps(&mut machine, 2); steps(&mut machine, 2);
assert_eq!(machine.cur_frame().get(257), Ok(0x2)); assert_eq!(machine.cur_frame().get(257), Ok(&Int(0x2)));
steps(&mut machine, 1); steps(&mut machine, 1);
assert_eq!(machine.get_tos(), Ok(0x1)); assert_eq!(machine.get_tos(), Ok(0x1));
@@ -23,12 +24,13 @@ fn advanced3_wide1() {
#[test] #[test]
fn advanced3_wide2() { fn advanced3_wide2() {
use rustijvm::value::Value::Int;
let mut machine = rustijvm::Machine::new_from_file("files/advanced/test-wide2.ijvm").unwrap(); let mut machine = rustijvm::Machine::new_from_file("files/advanced/test-wide2.ijvm").unwrap();
steps(&mut machine, 6); steps(&mut machine, 6);
assert_eq!(machine.cur_frame().get(1), Ok(0x1)); assert_eq!(machine.cur_frame().get(1), Ok(&Int(0x1)));
steps(&mut machine, 2); steps(&mut machine, 2);
assert_eq!(machine.cur_frame().get(32768), Ok(0x2)); assert_eq!(machine.cur_frame().get(32768), Ok(&Int(0x2)));
steps(&mut machine, 1); steps(&mut machine, 1);
assert_eq!(machine.get_tos(), Ok(0x1)); assert_eq!(machine.get_tos(), Ok(0x1));

View File

@@ -1,5 +1,7 @@
extern crate rustijvm; extern crate rustijvm;
use std::convert::TryInto;
fn steps(machine: &mut rustijvm::Machine, num: usize) { fn steps(machine: &mut rustijvm::Machine, num: usize) {
for _ in 0..num { for _ in 0..num {
machine.step().unwrap(); machine.step().unwrap();
@@ -44,7 +46,9 @@ fn advancedstack_contents() {
let size = stack.len(); let size = stack.len();
assert!(size >= 100); assert!(size >= 100);
let markers = (0..size).filter(|i| { stack.get(*i) == 0x2 }).count(); let markers = (0..size).filter(|i| {
stack.get(*i).try_into().unwrap_or(0) == 0x2
}).count();
assert!(markers >= 99); assert!(markers >= 99);
} }
@@ -55,7 +59,9 @@ fn advancedstack_method() {
steps(&mut machine, 8); steps(&mut machine, 8);
{ {
let stack = machine.cur_stack(); let stack = machine.cur_stack();
let markers = (0..stack.len()).filter(|i| { stack.get(*i) == 0x2 }).count(); let markers = (0..stack.len()).filter(|i| {
stack.get(*i).try_into().unwrap_or(0) == 0x2
}).count();
assert_eq!(markers, 4); assert_eq!(markers, 4);
} }
@@ -63,7 +69,9 @@ fn advancedstack_method() {
steps(&mut machine, 2 * 5000); steps(&mut machine, 2 * 5000);
{ {
let stack = machine.cur_stack(); let stack = machine.cur_stack();
let markers = (0..stack.len()).filter(|i| { stack.get(*i) == 0x2 }).count(); let markers = (0..stack.len()).filter(|i| {
stack.get(*i).try_into().unwrap_or(0) == 0x2
}).count();
assert!(markers > 4000); assert!(markers > 4000);
} }
} }

View File

@@ -100,23 +100,24 @@ fn task4_iteration_load() {
#[test] #[test]
fn task4_iinc() { fn task4_iinc() {
use rustijvm::value::Value::Int;
let mut machine = rustijvm::Machine::new_from_file("files/task4/IINCTest.ijvm").unwrap(); let mut machine = rustijvm::Machine::new_from_file("files/task4/IINCTest.ijvm").unwrap();
steps(&mut machine, 4); steps(&mut machine, 4);
assert_eq!(machine.cur_frame().get(0), Ok(0)); assert_eq!(machine.cur_frame().get(0), Ok(&Int(0)));
assert_eq!(machine.cur_frame().get(1), Ok(0)); assert_eq!(machine.cur_frame().get(1), Ok(&Int(0)));
steps(&mut machine, 1); steps(&mut machine, 1);
assert_eq!(machine.cur_frame().get(0), Ok(0)); assert_eq!(machine.cur_frame().get(0), Ok(&Int(0)));
steps(&mut machine, 1); steps(&mut machine, 1);
assert_eq!(machine.cur_frame().get(0), Ok(1)); assert_eq!(machine.cur_frame().get(0), Ok(&Int(1)));
steps(&mut machine, 1); steps(&mut machine, 1);
assert_eq!(machine.cur_frame().get(0), Ok(4)); assert_eq!(machine.cur_frame().get(0), Ok(&Int(4)));
steps(&mut machine, 1); steps(&mut machine, 1);
assert_eq!(machine.cur_frame().get(1), Ok(0)); assert_eq!(machine.cur_frame().get(1), Ok(&Int(0)));
steps(&mut machine, 1); steps(&mut machine, 1);
assert_eq!(machine.cur_frame().get(1), Ok(-1)); assert_eq!(machine.cur_frame().get(1), Ok(&Int(-1)));
steps(&mut machine, 1); steps(&mut machine, 1);
assert_eq!(machine.cur_frame().get(1), Ok(-4)); assert_eq!(machine.cur_frame().get(1), Ok(&Int(-4)));
} }