diff --git a/.gitignore b/.gitignore index 0c74b1e..6803e72 100644 --- a/.gitignore +++ b/.gitignore @@ -2,5 +2,5 @@ target /*.jas /*.ijvm *.conf -goJASM +gojasm rustijvm diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000..22b0f26 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,17 @@ +stages: + - build + +rust-latest: + stage: build + image: rust:latest + script: + - cargo build --verbose + - cargo test --verbose + allow_failure: true + +rust-nightly: + stage: build + image: rustlang/rust:nightly + script: + - cargo build --verbose + - cargo test --verbose diff --git a/Cargo.toml b/Cargo.toml index e63c1e9..ad0dc8b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,6 +18,7 @@ bonus = ["bonus:network", "bonus:heap"] extra = ["extra:sleep"] "extra:sleep" = [] -debug = ["debug:instr", "debug:frame"] +debug = ["debug:instr", "debug:frame", "debug:gc"] "debug:instr" = [] -"debug:frame" = [] \ No newline at end of file +"debug:frame" = [] +"debug:gc" = [] diff --git a/ijvm.config b/ijvm.config new file mode 100644 index 0000000..f8abc45 --- /dev/null +++ b/ijvm.config @@ -0,0 +1,37 @@ +0x10 BIPUSH byte +0x59 DUP +0xA7 GOTO label +0x60 IADD +0x7E IAND +0x99 IFEQ label +0x9B IFLT label +0x9F IF_ICMPEQ label +0x84 IINC var byte +0x15 ILOAD var +0xB6 INVOKEVIRTUAL method +0xB0 IOR +0xAC IRETURN +0x36 ISTORE var +0x64 ISUB +0x13 LDC_W constant +0x00 NOP +0x57 POP +0x5F SWAP +0xC4 WIDE +0xFF HALT +0xFE ERR +0xFD OUT +0xFC IN + +0xD1 NEWARRAY +0xD2 IALOAD +0xD3 IASTORE +0xD4 GC + +0xE1 NETBIND +0xE2 NETCONNECT +0xE3 NETIN +0xE4 NETOUT +0xE5 NETCLOSE + +0xF0 SLP byte diff --git a/src/binread.rs b/src/binread.rs index 461eb88..ec4ccad 100644 --- a/src/binread.rs +++ b/src/binread.rs @@ -2,7 +2,6 @@ use block::Block; use ops::{num_to_op, Operation}; use Result; -#[allow(unknown_lints, len_without_is_empty)] pub trait BinReadable { fn get(&mut self) -> Result; fn cur(&self) -> usize; diff --git a/src/heap.rs b/src/heap.rs index 5253294..26785f0 100644 --- a/src/heap.rs +++ b/src/heap.rs @@ -1,11 +1,17 @@ +use value::Value; +use std::rc::Rc; +use std::cell::RefCell; + +pub type Heap = Rc>>; + #[derive(Debug,Default)] -pub struct Heap { - pub heaps: Vec> +pub struct Heaps { + pub heaps: Vec } -impl Heap { - pub fn new() -> Heap { - Heap { +impl Heaps { + pub fn new() -> Heaps { + Heaps { heaps: Vec::new() } } @@ -14,16 +20,38 @@ impl Heap { self.heaps.len() } - pub fn get(&mut self, i: usize) -> &mut Vec { - &mut self.heaps[i] + pub fn get(&mut self, i: usize) -> Heap { + self.heaps[i].clone() } - pub fn alloc(&mut self, size: usize) -> usize { - self.heaps.push(vec![0; size]); - self.len() - 1 + pub fn alloc(&mut self, size: usize) -> Heap { + let heap = Rc::new(RefCell::new(vec![Value::Int(0); size])); + self.heaps.push(heap.clone()); + heap } pub fn is_empty(&self) -> bool { self.heaps.is_empty() } -} \ No newline at end of file + + pub fn gc(&mut self) { + let pre_size = self.len(); + + if cfg!(feature = "debug:gc") { + for (i, heap) in self.heaps.iter().enumerate() { + eprintln!("GC heap {} ref = {}", i, Rc::strong_count(heap)); + } + } + + self.heaps = self.heaps.iter() + .filter(|e| Rc::strong_count(e) > 1) + .map(|e| Rc::clone(e)) + .collect(); + + let post_size = self.len(); + + if cfg!(feature = "debug:gc") { + eprintln!("GC reaped {} heaps", pre_size - post_size); + } + } +} diff --git a/src/lib.rs b/src/lib.rs index 6faa5a3..7f0b3d3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,6 +2,7 @@ extern crate serde_derive; #[macro_use] extern crate lazy_static; +extern crate core; pub mod ijvmreader; pub mod binread; diff --git a/src/machine.rs b/src/machine.rs index ab6ede3..e488c56 100644 --- a/src/machine.rs +++ b/src/machine.rs @@ -11,7 +11,7 @@ use stack::Stack; use Result; #[cfg(feature = "bonus:heap")] -use heap::Heap; +use heap::Heaps; #[cfg(feature = "bonus:network")] use netstack::NetStack; use std::convert::TryInto; @@ -28,7 +28,7 @@ pub struct Machine { #[cfg(feature = "bonus:network")] pub net: NetStack, #[cfg(feature = "bonus:heap")] - pub heap: Heap, + pub heap: Heaps, pub stream_in: Box, pub stream_out: Arc>, @@ -49,7 +49,7 @@ impl Machine { net: NetStack::new(), #[cfg(feature = "bonus:heap")] - heap: Heap::new(), + heap: Heaps::new(), } } diff --git a/src/ops.rs b/src/ops.rs index 76c1189..4755621 100644 --- a/src/ops.rs +++ b/src/ops.rs @@ -74,6 +74,7 @@ lazy_static! { 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![]); } m @@ -110,7 +111,7 @@ fn out(machine: &mut Machine) -> Result<()> { let mut out = machine.stream_out.lock().unwrap(); out.write_all(&buffer).unwrap(); out.flush().unwrap(); - return Ok(()); + Ok(()) } fn pop(machine: &mut Machine) -> Result<()> { @@ -266,14 +267,20 @@ fn invokevirtual(machine: &mut Machine) -> Result<()> { } 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) + // Lifetime for prev frame, allows gc to reap into the frame + { + 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)?; + } + + machine.heap.gc(); + Ok(()) } #[cfg(feature = "extra:sleep")] @@ -323,40 +330,46 @@ fn netclose(machine: &mut Machine) -> Result<()> { #[cfg(feature = "bonus:heap")] fn newarray(machine: &mut Machine) -> Result<()> { let size: i32 = machine.cur_stack().pop()?.try_into()?; - let heapref = machine.heap.alloc(size as usize); - machine.cur_stack().push(Value::HeapRef(heapref)); + 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<()> { - let heapref = match machine.cur_stack().pop()? { + 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: i32 = machine.cur_stack().pop()?.try_into()?; + let value = machine.cur_stack().pop()?.clone(); - let heap = machine.heap.get(heapref); - heap[index as usize] = value; + RefCell::borrow_mut(&heap)[index as usize] = value; Ok(()) } #[cfg(feature = "bonus:heap")] fn iaload(machine: &mut Machine) -> Result<()> { - let heapref = match machine.cur_stack().pop()? { + 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: i32; + let value: Value; { - let heap = machine.heap.get(heapref); - value = heap[index as usize]; + value = heap.borrow()[index as usize].clone(); } - machine.cur_stack().push(Value::Int(value)); + machine.cur_stack().push(value); Ok(()) } + +#[cfg(feature = "bonus:heap")] +fn gc(machine: &mut Machine) -> Result<()> { + machine.heap.gc(); + Ok(()) +} diff --git a/src/value.rs b/src/value.rs index 9c2b1a6..a5d2b10 100644 --- a/src/value.rs +++ b/src/value.rs @@ -1,10 +1,11 @@ use Result; use std::convert::TryInto; +use heap::Heap; -#[derive(Debug, Copy, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq)] pub enum Value { Int(i32), - HeapRef(usize), + HeapRef(Heap), } impl TryInto for Value { @@ -22,9 +23,9 @@ impl TryInto for &Value { type Error = &'static str; fn try_into(self) -> Result { - match self { - &Value::Int(a) => Ok(a), - &Value::HeapRef(_) => Err("Cannot use HeapRef as i32"), + match *self { + Value::Int(a) => Ok(a), + Value::HeapRef(_) => Err("Cannot use HeapRef as i32"), } } } diff --git a/tests/advanced1.rs b/tests/advanced1.rs index 9fff3d5..8e34d57 100644 --- a/tests/advanced1.rs +++ b/tests/advanced1.rs @@ -1,7 +1,6 @@ extern crate rustijvm; -use std::sync::{Arc, Mutex}; -use std::io::{Cursor, Seek, SeekFrom, Read}; +use std::io::{Seek, SeekFrom, Read}; fn steps(machine: &mut rustijvm::Machine, num: usize) { for _ in 0..num { diff --git a/tests/advanced4.rs b/tests/advanced4.rs index 4e41eb2..4a39881 100644 --- a/tests/advanced4.rs +++ b/tests/advanced4.rs @@ -1,7 +1,6 @@ extern crate rustijvm; -use std::sync::{Arc, Mutex}; -use std::io::{Cursor, Seek, SeekFrom, Read}; +use std::io::{Seek, SeekFrom, Read}; #[test] fn advanced4_tanenbaum() { diff --git a/tests/advanced5.rs b/tests/advanced5.rs index d5b4a8f..a23577b 100644 --- a/tests/advanced5.rs +++ b/tests/advanced5.rs @@ -1,7 +1,6 @@ extern crate rustijvm; -use std::sync::{Arc, Mutex}; -use std::io::{Cursor, Seek, SeekFrom, Read}; +use std::io::{Seek, SeekFrom, Read}; fn run_calc(input: &'static str, expected: &str) { let rc = rustijvm::stubs::output_stub(); diff --git a/tests/advanced6.rs b/tests/advanced6.rs index fb65d9e..73d2a2a 100644 --- a/tests/advanced6.rs +++ b/tests/advanced6.rs @@ -1,7 +1,6 @@ extern crate rustijvm; -use std::sync::{Arc, Mutex}; -use std::io::{Cursor, Seek, SeekFrom, Read}; +use std::io::{Seek, SeekFrom, Read}; fn run_calc(input: &'static str, expected: &str) { let rc = rustijvm::stubs::output_stub(); diff --git a/tests/bonusheap.rs b/tests/bonusheap.rs index 5478054..f49e013 100644 --- a/tests/bonusheap.rs +++ b/tests/bonusheap.rs @@ -1,8 +1,7 @@ extern crate rustijvm; use std::fs::File; -use std::sync::{Arc, Mutex}; -use std::io::{Cursor, Seek, SeekFrom, Read}; +use std::io::{Seek, SeekFrom, Read}; fn run_bfi(file: &str) -> String { let file = File::open(file).expect("Missing bf file"); diff --git a/tests/task4.rs b/tests/task4.rs index f90b789..1f7f781 100644 --- a/tests/task4.rs +++ b/tests/task4.rs @@ -1,7 +1,6 @@ extern crate rustijvm; -use std::io::{Cursor, Read, Seek, SeekFrom}; -use std::sync::{Arc, Mutex}; +use std::io::{Read, Seek, SeekFrom}; fn steps(machine: &mut rustijvm::Machine, num: usize) { for _ in 0..num {