Finish garbage collected heap
This commit adds unit tests for garbage collection as well as a deep copy implementation for cons which fully deep copies the whole tree and a clone implementation for Datum which deep copies cons. Signed-off-by: Ava Affine <ava@sunnypup.io>
This commit is contained in:
parent
524f79733c
commit
2392d5c2fa
2 changed files with 104 additions and 11 deletions
|
|
@ -48,15 +48,15 @@ use organelle::Number;
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
pub struct Gc<T>(NonNull<Rc<T>>);
|
pub struct Gc<T>(NonNull<Rc<T>>);
|
||||||
|
|
||||||
impl From<Rc<Datum>> for Gc<Datum> {
|
impl<T> From<Rc<T>> for Gc<T> {
|
||||||
fn from(src: Rc<Datum>) -> Self {
|
fn from(src: Rc<T>) -> Self {
|
||||||
Gc(NonNull::new(Box::into_raw(Box::new(src.clone())))
|
Gc(NonNull::new(Box::into_raw(Box::new(src.clone())))
|
||||||
.expect("GC obj from rc nonnull ptr check"))
|
.expect("GC obj from rc nonnull ptr check"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Datum> for Gc<Datum> {
|
impl<T> From<T> for Gc<T> {
|
||||||
fn from(value: Datum) -> Self {
|
fn from(value: T) -> Self {
|
||||||
Gc(NonNull::new(Box::into_raw(Box::new(Rc::from(value))))
|
Gc(NonNull::new(Box::into_raw(Box::new(Rc::from(value))))
|
||||||
.expect("GC obj from datum nonnull ptr check"))
|
.expect("GC obj from datum nonnull ptr check"))
|
||||||
}
|
}
|
||||||
|
|
@ -103,7 +103,7 @@ macro_rules! shallow_copy_rc {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Clone> Clone for Gc<T> {
|
impl<T> Clone for Gc<T> {
|
||||||
fn clone(&self) -> Self {
|
fn clone(&self) -> Self {
|
||||||
Gc(shallow_copy_rc!(self.0.as_ptr()))
|
Gc(shallow_copy_rc!(self.0.as_ptr()))
|
||||||
}
|
}
|
||||||
|
|
@ -116,7 +116,8 @@ impl<T: Clone> Clone for Gc<T> {
|
||||||
impl<T> Drop for Gc<T> {
|
impl<T> Drop for Gc<T> {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
unsafe {
|
unsafe {
|
||||||
drop(Box::from_raw(self.0.as_ptr() as *mut Box<Rc<T>>))
|
let a = Box::<Rc<T>>::from_raw(self.0.as_ptr());
|
||||||
|
drop(a)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -132,7 +133,7 @@ impl<T: Clone> Gc<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, PartialEq)]
|
#[derive(PartialEq)]
|
||||||
pub enum Datum {
|
pub enum Datum {
|
||||||
Number(Number),
|
Number(Number),
|
||||||
Bool(bool),
|
Bool(bool),
|
||||||
|
|
@ -145,14 +146,57 @@ pub enum Datum {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// implemented by hand to force deep copy on Cons datum
|
||||||
|
impl Clone for Datum {
|
||||||
|
fn clone(&self) -> Datum {
|
||||||
|
match self {
|
||||||
|
Datum::Number(n) => Datum::Number(n.clone()),
|
||||||
|
Datum::Bool(n) => Datum::Bool(n.clone()),
|
||||||
|
Datum::Cons(n) => Datum::Cons(n.deep_copy()),
|
||||||
|
Datum::Symbol(n) => Datum::Symbol(n.clone()),
|
||||||
|
Datum::Char(n) => Datum::Char(n.clone()),
|
||||||
|
Datum::String(n) => Datum::String(n.clone()),
|
||||||
|
Datum::Vector(n) =>
|
||||||
|
Datum::Vector(n.clone()),
|
||||||
|
Datum::ByteVector(n) =>
|
||||||
|
Datum::ByteVector(n.clone()),
|
||||||
|
Datum::None => Datum::None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clone_from(&mut self, source: &Self) {
|
||||||
|
*self = source.clone();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, PartialEq)]
|
#[derive(Clone, PartialEq)]
|
||||||
pub struct Cons(pub Option<Gc<Datum>>, pub Option<Gc<Datum>>);
|
pub struct Cons(pub Option<Gc<Datum>>, pub Option<Gc<Datum>>);
|
||||||
|
|
||||||
impl Cons {
|
impl Cons {
|
||||||
pub fn deep_copy(&self) -> Cons {
|
pub fn deep_copy(&self) -> Cons {
|
||||||
// TODO: recursive deep copy through the whole list
|
macro_rules! car_cpy {
|
||||||
Cons(self.0.as_ref().map(|x| x.deep_copy()),
|
() => {
|
||||||
self.1.as_ref().map(|x| x.deep_copy()))
|
if let Some(ref car) = self.0 {
|
||||||
|
if let Datum::Cons(ref car) = **car {
|
||||||
|
Some(Datum::Cons(car.deep_copy()).into())
|
||||||
|
} else {
|
||||||
|
self.0.as_ref().map(|x| x.deep_copy())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(ref next) = self.1 {
|
||||||
|
if let Datum::Cons(ref next) = **next {
|
||||||
|
Cons(car_cpy!(), Some(Datum::Cons(next.deep_copy()).into()))
|
||||||
|
} else {
|
||||||
|
Cons(car_cpy!(), self.1.as_ref().map(|x| x.deep_copy()))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Cons(car_cpy!(), None)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn subsl(&self, start: isize, end: isize) -> Cons {
|
pub fn subsl(&self, start: isize, end: isize) -> Cons {
|
||||||
|
|
@ -242,3 +286,52 @@ impl Index<usize> for Cons {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
struct GcTester<'a>(&'a mut bool);
|
||||||
|
|
||||||
|
// clone implementation leaks memory... but its for test
|
||||||
|
impl Clone for GcTester<'_> {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
unsafe {
|
||||||
|
GcTester(Box::into_raw(Box::new(self.0.clone()))
|
||||||
|
.as_mut()
|
||||||
|
.expect("Gc Test obj mut ref fail"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clone_from(&mut self, _: &Self) {
|
||||||
|
unimplemented!("test impl")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// in non test code Id have to get the box back
|
||||||
|
impl Drop for GcTester<'_> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
*self.0 = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_gc_basic_behavior() {
|
||||||
|
let mut flag = false;
|
||||||
|
let a = Into::<Gc<GcTester>>::into(GcTester(&mut flag));
|
||||||
|
drop(a);
|
||||||
|
assert!(flag)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_gc_shallow_copy() {
|
||||||
|
let mut flag = false;
|
||||||
|
let a =
|
||||||
|
Into::<Gc<GcTester>>::into(GcTester(&mut flag)).clone();
|
||||||
|
drop(a);
|
||||||
|
assert!(flag)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: test deep copy
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -102,7 +102,7 @@ impl VM {
|
||||||
Address::Oper3 => &self.oper[2],
|
Address::Oper3 => &self.oper[2],
|
||||||
Address::Oper4 => &self.oper[3],
|
Address::Oper4 => &self.oper[3],
|
||||||
Address::Stack => &self.stack[$oper.1],
|
Address::Stack => &self.stack[$oper.1],
|
||||||
Address::Numer => e!("cannot access constant numeric"),
|
Address::Numer => e!("cannot access constant numeric data"),
|
||||||
Address::Instr => e!("bad access to instruction data"),
|
Address::Instr => e!("bad access to instruction data"),
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue