From f8117ed032e518f0300d8bcb1c875b180a4a7c64 Mon Sep 17 00:00:00 2001 From: Nicolas Polomack Date: Wed, 1 Feb 2023 17:43:40 +0100 Subject: [PATCH 01/14] feat: initial custom GC implementation --- Cargo.lock | 4 ++ Cargo.toml | 1 + som-gc/Cargo.toml | 8 +++ som-gc/README.md | 4 ++ som-gc/src/gc.rs | 55 +++++++++++++++ som-gc/src/gc_box.rs | 76 ++++++++++++++++++++ som-gc/src/heap.rs | 98 ++++++++++++++++++++++++++ som-gc/src/lib.rs | 8 +++ som-gc/src/trace.rs | 155 +++++++++++++++++++++++++++++++++++++++++ som-gc/tests/cycle.rs | 44 ++++++++++++ som-gc/tests/simple.rs | 47 +++++++++++++ 11 files changed, 500 insertions(+) create mode 100644 som-gc/Cargo.toml create mode 100644 som-gc/README.md create mode 100644 som-gc/src/gc.rs create mode 100644 som-gc/src/gc_box.rs create mode 100644 som-gc/src/heap.rs create mode 100644 som-gc/src/lib.rs create mode 100644 som-gc/src/trace.rs create mode 100644 som-gc/tests/cycle.rs create mode 100644 som-gc/tests/simple.rs diff --git a/Cargo.lock b/Cargo.lock index a3106b18..be07de7e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -291,6 +291,10 @@ dependencies = [ name = "som-core" version = "0.1.0" +[[package]] +name = "som-gc" +version = "0.1.0" + [[package]] name = "som-interpreter-ast" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index 39c4f210..7d7dcd1d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,6 +7,7 @@ members = [ "som-parser-core", "som-parser-symbols", "som-parser-text", + "som-gc", ] [profile.release] diff --git a/som-gc/Cargo.toml b/som-gc/Cargo.toml new file mode 100644 index 00000000..2946b735 --- /dev/null +++ b/som-gc/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "som-gc" +version = "0.1.0" +description = "A garbage collector for the Simple Object Machine" +authors = ["Nicolas Polomack "] +edition = "2021" +publish = false +license = "MIT OR Apache-2.0" diff --git a/som-gc/README.md b/som-gc/README.md new file mode 100644 index 00000000..3a4ff66d --- /dev/null +++ b/som-gc/README.md @@ -0,0 +1,4 @@ +The SOM Garbage Collector +========================= + +This is the mark-and-sweep garbage collector implementation for `som-rs`. diff --git a/som-gc/src/gc.rs b/som-gc/src/gc.rs new file mode 100644 index 00000000..28a0bf7f --- /dev/null +++ b/som-gc/src/gc.rs @@ -0,0 +1,55 @@ +use std::cell::Cell; +use std::marker::PhantomData; +use std::ops::Deref; +use std::ptr::NonNull; +use std::rc::Rc; + +use crate::gc_box::GcBox; +use crate::trace::Trace; + +/// Represent a handle to a GC-allocated value. +pub struct Gc +where + T: Trace + 'static, +{ + /// The pointer to the referenced `GcBox`. + pub(crate) ptr: Cell>>, + // needed for drop-related reasons. + pub(crate) marker: PhantomData>, +} + +impl Clone for Gc +where + T: Trace + 'static, +{ + fn clone(&self) -> Self { + Self { + ptr: self.ptr.clone(), + marker: PhantomData, + } + } +} + +impl Trace for Gc +where + T: Trace + 'static, +{ + fn trace(&self) { + let ptr = unsafe { self.ptr.get().as_mut() }; + if !ptr.is_marked() { + ptr.mark(); + ptr.value.trace(); + } + } +} + +impl Deref for Gc +where + T: Trace + 'static, +{ + type Target = T; + + fn deref(&self) -> &Self::Target { + unsafe { self.ptr.get().as_ref() } + } +} diff --git a/som-gc/src/gc_box.rs b/som-gc/src/gc_box.rs new file mode 100644 index 00000000..2d091a97 --- /dev/null +++ b/som-gc/src/gc_box.rs @@ -0,0 +1,76 @@ +use std::ops::{Deref, DerefMut}; + +/// Represents a value, as it is stored within the GC. +pub(crate) struct GcBox { + /// Pointer to next value in the GC chain. + /// + /// We also use the unused bits of this pointer to store the `marked` bitflag. + /// Therefore, it not suitable for it to be dereferenced as-is, it must first + /// be cleared of these flags before any accesses are performed. + /// This is also why this is not represented as a `Option>>` + pub(crate) next: *mut GcBox, + pub(crate) value: T, +} + +impl GcBox { + /// Bitflag for whether the current GC object is marked (still referenced). + pub const MARK_BITMASK: usize = 0b0000_0001; + /// Bitmask for the bits reserved for GC flags within the `next` pointer in `GcBox`. + pub const FLAGS_BITMASK: usize = 0b0000_0011; + + /// Creates a singleton `GcBox` value (which isn't part of any chain yet). + pub fn new(value: T) -> Self { + Self { + next: std::ptr::null_mut(), + value, + } + } + + /// Clears the `mark` bit for this GC object. + pub fn clear_mark(&mut self) { + self.next = (self.next as usize & !Self::MARK_BITMASK) as *mut _; + } + + /// Sets the `mark` bit for this GC object. + pub fn mark(&mut self) { + self.next = (self.next as usize | Self::MARK_BITMASK) as *mut _; + } + + /// Returns whether the `mark` bit is set for this GC object. + pub fn is_marked(&self) -> bool { + self.next as usize & Self::MARK_BITMASK == 1 + } + + /// Returns the pointer to the next GC object in the chain. + pub fn next(&self) -> *mut GcBox { + (self.next as usize & !Self::FLAGS_BITMASK) as *mut _ + } + + /// Sets the pointer to the next GC object in the chain, and preserving all the current bitflags. + pub fn set_next(&mut self, next: *mut GcBox) { + self.next = (next as usize | (self.next as usize & Self::MARK_BITMASK)) as *mut _; + } +} + +impl Default for GcBox { + fn default() -> Self { + Self { + next: std::ptr::null_mut(), + value: T::default(), + } + } +} + +impl Deref for GcBox { + type Target = T; + + fn deref(&self) -> &Self::Target { + &self.value + } +} + +impl DerefMut for GcBox { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.value + } +} diff --git a/som-gc/src/heap.rs b/som-gc/src/heap.rs new file mode 100644 index 00000000..b68c24f7 --- /dev/null +++ b/som-gc/src/heap.rs @@ -0,0 +1,98 @@ +use std::cell::Cell; +use std::marker::PhantomData; +use std::ptr::NonNull; + +use crate::gc_box::GcBox; + +use crate::gc::Gc; +use crate::trace::Trace; + +/// The GC heap itself, which is the storage for all GC-ed objects. +pub struct GcHeap +where + T: Trace + 'static, +{ + head: *mut GcBox, +} + +impl Drop for GcHeap +where + T: Trace + 'static, +{ + // We properly drop all objects in the heap. + fn drop(&mut self) { + let mut head: *mut GcBox = self.head; + while !head.is_null() { + // SAFETY: we don't access that reference again after we drop it. + let next = unsafe { &*head }.next(); + drop(unsafe { Box::from_raw(head) }); + head = next; + } + } +} + +impl GcHeap +where + T: Trace + 'static, +{ + /// Creates a new empty GC heap. + pub fn new() -> Self { + Self { + head: std::ptr::null_mut(), + } + } + + /// Allocates an object on the GC heap, returning its handle. + pub fn allocate(&mut self, value: T) -> Gc { + // TODO: trigger `collect_garbage` + let mut allocated = Box::new(GcBox::new(value)); + allocated.set_next(self.head); + self.head = Box::into_raw(allocated); + Gc { + // SAFETY: `self.head` is guaranteed to be properly aligned and non-null by `Box::into_raw`. + ptr: Cell::new(unsafe { NonNull::new_unchecked(self.head) }), + marker: PhantomData, + } + } + + /// Clears the `mark` bits on every GC object. + fn clear_marks(&mut self) { + let mut head = self.head; + while !head.is_null() { + let head_ref = unsafe { &mut *head }; + head_ref.clear_mark(); + head = head_ref.next(); + } + } + + /// Performs a sweep on the GC heap (drops all unmarked objects). + fn sweep(&mut self) { + let mut head: *mut GcBox = self.head; + let mut prev: *mut GcBox = std::ptr::null_mut(); + while !head.is_null() { + // SAFETY: we don't access that reference again after we drop it. + let head_ref = unsafe { &*head }; + let next = head_ref.next(); + if !head_ref.is_marked() { + if !prev.is_null() { + unsafe { &mut *prev }.set_next(next); + } else { + self.head = next; + } + // TODO: introduce a `Finalize`-like mechanism. + // TODO: maybe perform the drops in a separate thread. + drop(unsafe { Box::from_raw(head) }); + } else { + prev = head; + } + head = next; + } + } + + /// Performs garbage collection (mark-and-sweep) on the GC heap. + pub fn collect_garbage(&mut self, mut mark_fn: impl FnMut()) { + self.clear_marks(); + mark_fn(); + self.sweep(); + } +} diff --git a/som-gc/src/lib.rs b/som-gc/src/lib.rs new file mode 100644 index 00000000..5bd1c383 --- /dev/null +++ b/som-gc/src/lib.rs @@ -0,0 +1,8 @@ +mod gc; +mod gc_box; +mod heap; +mod trace; + +pub use crate::gc::Gc; +pub use crate::heap::GcHeap; +pub use crate::trace::Trace; diff --git a/som-gc/src/trace.rs b/som-gc/src/trace.rs new file mode 100644 index 00000000..acac0070 --- /dev/null +++ b/som-gc/src/trace.rs @@ -0,0 +1,155 @@ +use std::cell::RefCell; +use std::collections::{BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, LinkedList, VecDeque}; +use std::num::{ + NonZeroI128, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI8, NonZeroIsize, NonZeroU128, + NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU8, NonZeroUsize, +}; +use std::path::{Path, PathBuf}; +use std::rc::Rc; +use std::sync::atomic::{ + AtomicBool, AtomicI16, AtomicI32, AtomicI64, AtomicI8, AtomicIsize, AtomicU16, AtomicU32, + AtomicU64, AtomicU8, AtomicUsize, +}; + +pub trait Trace { + fn trace(&self); +} + +macro_rules! trivial_trace { + ($($ty:ty),* $(,)?) => { + $( + impl $crate::trace::Trace for $ty { + fn trace(&self) {} + } + )* + }; +} + +trivial_trace![ + (), + bool, + isize, + usize, + i8, + u8, + i16, + u16, + i32, + u32, + i64, + u64, + i128, + u128, + f32, + f64, + char, + String, + Box, + Rc, + Path, + PathBuf, + NonZeroIsize, + NonZeroUsize, + NonZeroI8, + NonZeroU8, + NonZeroI16, + NonZeroU16, + NonZeroI32, + NonZeroU32, + NonZeroI64, + NonZeroU64, + NonZeroI128, + NonZeroU128, + AtomicBool, + AtomicIsize, + AtomicUsize, + AtomicI8, + AtomicU8, + AtomicI16, + AtomicU16, + AtomicI32, + AtomicU32, + AtomicI64, + AtomicU64, +]; + +macro_rules! tuple_trace { + () => {}; + ($head:ident $($X:ident)*) => { + tuple_trace!($($X)*); + tuple_trace!(~ $head $($X)*); + }; + (~ $($X:ident)*) => { + #[allow(non_snake_case)] + impl<$($X: $crate::trace::Trace),*> Trace for ($($X,)*) { + fn trace(&self) { + let ($($X,)*) = self; + $($X.trace();)* + } + } + }; +} + +tuple_trace!(A_ B_ C_ D_ E_ F_ G_ H_ I_ J_ K_ L_ M_ N_ O_ P_ Q_ S_ T_ U_ V_ W_ X_ Y_ Z_); + +macro_rules! iter_1_trace { + ($($ty:ty),* $(,)?) => { + $( + impl $crate::trace::Trace for $ty { + fn trace(&self) { + for it in self.into_iter() { + it.trace(); + } + } + } + )* + }; +} + +iter_1_trace!( + Vec, + VecDeque, + LinkedList, + HashSet, + BTreeSet, + BinaryHeap +); + +macro_rules! iter_2_trace { + ($($ty:ty),* $(,)?) => { + $( + impl $crate::trace::Trace for $ty { + fn trace(&self) { + for (k, v) in self.into_iter() { + k.trace(); + v.trace(); + } + } + } + )* + }; +} + +iter_2_trace!(HashMap, BTreeMap); + +impl Trace for &[T] { + fn trace(&self) { + for it in self.into_iter() { + it.trace(); + } + } +} + +impl Trace for [T; N] { + fn trace(&self) { + for it in self.into_iter() { + it.trace(); + } + } +} + +impl Trace for RefCell { + fn trace(&self) { + self.borrow().trace(); + } +} diff --git a/som-gc/tests/cycle.rs b/som-gc/tests/cycle.rs new file mode 100644 index 00000000..56f18d44 --- /dev/null +++ b/som-gc/tests/cycle.rs @@ -0,0 +1,44 @@ +use std::cell::RefCell; + +use som_gc::{Gc, GcHeap, Trace}; + +struct Node { + id: usize, + edge: Option>>, +} + +impl Trace for Node { + fn trace(&self) { + if let Some(edge) = self.edge.as_ref() { + edge.trace(); + } + } +} + +impl Drop for Node { + fn drop(&mut self) { + println!("dropped node {}", self.id); + } +} + +fn main() { + let mut heap = GcHeap::new(); + + let a = heap.allocate(RefCell::new(Node { id: 1, edge: None })); + let b = heap.allocate(RefCell::new(Node { id: 2, edge: None })); + + a.borrow_mut().edge = Some(b.clone()); + b.borrow_mut().edge = Some(a.clone()); + + drop(b); + + println!("collection 1: START"); + heap.collect_garbage(|| a.trace()); + println!("collection 1: END"); + + drop(a); + + println!("collection 2: START"); + heap.collect_garbage(|| {}); + println!("collection 2: END"); +} diff --git a/som-gc/tests/simple.rs b/som-gc/tests/simple.rs new file mode 100644 index 00000000..1d739dc6 --- /dev/null +++ b/som-gc/tests/simple.rs @@ -0,0 +1,47 @@ +use som_gc::{GcHeap, Trace}; + +struct Test { + value: usize, +} + +impl Test { + pub fn new(value: usize) -> Self { + Self { value } + } + + pub fn say_hello(&self) { + println!("hello from {}", self.value); + } +} + +impl Drop for Test { + fn drop(&mut self) { + println!("dropped value {}", self.value); + } +} + +impl Trace for Test { + fn trace(&self) {} +} + +fn main() { + let mut heap = GcHeap::new(); + + let mut stack = Vec::new(); + + stack.push(heap.allocate(Test::new(3))); + stack.push(heap.allocate(Test::new(4))); + stack.push(heap.allocate(Test::new(5))); + + stack.iter().for_each(|it| it.say_hello()); + + println!("collection 1: START"); + heap.collect_garbage(|| stack.trace()); + println!("collection 1: END"); + + stack.remove(1); + + println!("collection 2: START"); + heap.collect_garbage(|| stack.trace()); + println!("collection 2: END"); +} From b738e235fc7b39fbe29f7d862de50fe9cfd3146a Mon Sep 17 00:00:00 2001 From: Nicolas Polomack Date: Wed, 1 Feb 2023 18:14:19 +0100 Subject: [PATCH 02/14] feat: GC heap can now allocate different types --- som-gc/src/gc_box.rs | 43 +++++++++++----------------- som-gc/src/heap.rs | 63 ++++++++++++++++++------------------------ som-gc/tests/cycle.rs | 3 +- som-gc/tests/simple.rs | 3 +- 4 files changed, 47 insertions(+), 65 deletions(-) diff --git a/som-gc/src/gc_box.rs b/som-gc/src/gc_box.rs index 2d091a97..49e6a951 100644 --- a/som-gc/src/gc_box.rs +++ b/som-gc/src/gc_box.rs @@ -1,61 +1,50 @@ use std::ops::{Deref, DerefMut}; +use std::cell::Cell; +use std::ptr::NonNull; + +use crate::Trace; /// Represents a value, as it is stored within the GC. -pub(crate) struct GcBox { +pub(crate) struct GcBox { /// Pointer to next value in the GC chain. - /// - /// We also use the unused bits of this pointer to store the `marked` bitflag. - /// Therefore, it not suitable for it to be dereferenced as-is, it must first - /// be cleared of these flags before any accesses are performed. - /// This is also why this is not represented as a `Option>>` - pub(crate) next: *mut GcBox, + pub(crate) marked: Cell, + pub(crate) next: Option>>, pub(crate) value: T, } impl GcBox { - /// Bitflag for whether the current GC object is marked (still referenced). - pub const MARK_BITMASK: usize = 0b0000_0001; - /// Bitmask for the bits reserved for GC flags within the `next` pointer in `GcBox`. - pub const FLAGS_BITMASK: usize = 0b0000_0011; - /// Creates a singleton `GcBox` value (which isn't part of any chain yet). pub fn new(value: T) -> Self { Self { - next: std::ptr::null_mut(), + marked: Cell::new(false), + next: None, value, } } +} +impl GcBox { /// Clears the `mark` bit for this GC object. pub fn clear_mark(&mut self) { - self.next = (self.next as usize & !Self::MARK_BITMASK) as *mut _; + self.marked.set(false); } /// Sets the `mark` bit for this GC object. pub fn mark(&mut self) { - self.next = (self.next as usize | Self::MARK_BITMASK) as *mut _; + self.marked.set(true); } /// Returns whether the `mark` bit is set for this GC object. pub fn is_marked(&self) -> bool { - self.next as usize & Self::MARK_BITMASK == 1 - } - - /// Returns the pointer to the next GC object in the chain. - pub fn next(&self) -> *mut GcBox { - (self.next as usize & !Self::FLAGS_BITMASK) as *mut _ - } - - /// Sets the pointer to the next GC object in the chain, and preserving all the current bitflags. - pub fn set_next(&mut self, next: *mut GcBox) { - self.next = (next as usize | (self.next as usize & Self::MARK_BITMASK)) as *mut _; + self.marked.get() } } impl Default for GcBox { fn default() -> Self { Self { - next: std::ptr::null_mut(), + marked: Cell::new(false), + next: None, value: T::default(), } } diff --git a/som-gc/src/heap.rs b/som-gc/src/heap.rs index b68c24f7..55b54025 100644 --- a/som-gc/src/heap.rs +++ b/som-gc/src/heap.rs @@ -8,49 +8,40 @@ use crate::gc::Gc; use crate::trace::Trace; /// The GC heap itself, which is the storage for all GC-ed objects. -pub struct GcHeap -where - T: Trace + 'static, -{ - head: *mut GcBox, +pub struct GcHeap { + head: Option>>, } -impl Drop for GcHeap -where - T: Trace + 'static, -{ +impl Drop for GcHeap { // We properly drop all objects in the heap. fn drop(&mut self) { - let mut head: *mut GcBox = self.head; - while !head.is_null() { + let mut head = self.head; + while let Some(cur) = head { // SAFETY: we don't access that reference again after we drop it. - let next = unsafe { &*head }.next(); - drop(unsafe { Box::from_raw(head) }); - head = next; + head = unsafe { cur.as_ref() }.next; + drop(unsafe { Box::from_raw(cur.as_ptr()) }); } } } -impl GcHeap -where - T: Trace + 'static, -{ +impl GcHeap { /// Creates a new empty GC heap. pub fn new() -> Self { Self { - head: std::ptr::null_mut(), + head: None, } } /// Allocates an object on the GC heap, returning its handle. - pub fn allocate(&mut self, value: T) -> Gc { + pub fn allocate(&mut self, value: T) -> Gc { // TODO: trigger `collect_garbage` let mut allocated = Box::new(GcBox::new(value)); - allocated.set_next(self.head); - self.head = Box::into_raw(allocated); + allocated.next = self.head; + let ptr = unsafe { NonNull::new_unchecked(Box::into_raw(allocated)) }; + self.head = Some(ptr as NonNull>); Gc { // SAFETY: `self.head` is guaranteed to be properly aligned and non-null by `Box::into_raw`. - ptr: Cell::new(unsafe { NonNull::new_unchecked(self.head) }), + ptr: Cell::new(ptr), marker: PhantomData, } } @@ -58,30 +49,30 @@ where /// Clears the `mark` bits on every GC object. fn clear_marks(&mut self) { let mut head = self.head; - while !head.is_null() { - let head_ref = unsafe { &mut *head }; - head_ref.clear_mark(); - head = head_ref.next(); + while let Some(mut cur) = head { + let cur = unsafe { cur.as_mut() }; + cur.clear_mark(); + head = cur.next; } } /// Performs a sweep on the GC heap (drops all unmarked objects). fn sweep(&mut self) { - let mut head: *mut GcBox = self.head; - let mut prev: *mut GcBox = std::ptr::null_mut(); - while !head.is_null() { + let mut head = self.head; + let mut prev = None::>>; + while let Some(cur) = head { // SAFETY: we don't access that reference again after we drop it. - let head_ref = unsafe { &*head }; - let next = head_ref.next(); - if !head_ref.is_marked() { - if !prev.is_null() { - unsafe { &mut *prev }.set_next(next); + let cur_ref = unsafe { cur.as_ref() }; + let next = cur_ref.next; + if !cur_ref.is_marked() { + if let Some(mut prev_cur) = prev { + unsafe { prev_cur.as_mut() }.next = next; } else { self.head = next; } // TODO: introduce a `Finalize`-like mechanism. // TODO: maybe perform the drops in a separate thread. - drop(unsafe { Box::from_raw(head) }); + drop(unsafe { Box::from_raw(cur.as_ptr()) }); } else { prev = head; } diff --git a/som-gc/tests/cycle.rs b/som-gc/tests/cycle.rs index 56f18d44..a8da219e 100644 --- a/som-gc/tests/cycle.rs +++ b/som-gc/tests/cycle.rs @@ -21,7 +21,8 @@ impl Drop for Node { } } -fn main() { +#[test] +fn cycle_test() { let mut heap = GcHeap::new(); let a = heap.allocate(RefCell::new(Node { id: 1, edge: None })); diff --git a/som-gc/tests/simple.rs b/som-gc/tests/simple.rs index 1d739dc6..21bca673 100644 --- a/som-gc/tests/simple.rs +++ b/som-gc/tests/simple.rs @@ -24,7 +24,8 @@ impl Trace for Test { fn trace(&self) {} } -fn main() { +#[test] +fn simple_test() { let mut heap = GcHeap::new(); let mut stack = Vec::new(); From f4a2e9988969cf5d9230a89c033621caaec14446 Mon Sep 17 00:00:00 2001 From: Nicolas Polomack Date: Thu, 2 Feb 2023 16:03:58 +0100 Subject: [PATCH 03/14] feat: GC configuration parameters feat: more trait impls for `Gc` --- som-gc/src/gc.rs | 118 +++++++++++++++++++++++++++++++++++++++++++ som-gc/src/gc_box.rs | 8 +++ som-gc/src/heap.rs | 72 +++++++++++++++++++++++++- som-gc/src/lib.rs | 2 +- som-gc/src/trace.rs | 26 ++++++++++ 5 files changed, 223 insertions(+), 3 deletions(-) diff --git a/som-gc/src/gc.rs b/som-gc/src/gc.rs index 28a0bf7f..adfbaef9 100644 --- a/som-gc/src/gc.rs +++ b/som-gc/src/gc.rs @@ -1,4 +1,7 @@ +use std::borrow::Borrow; use std::cell::Cell; +use std::fmt; +use std::hash::Hash; use std::marker::PhantomData; use std::ops::Deref; use std::ptr::NonNull; @@ -18,10 +21,123 @@ where pub(crate) marker: PhantomData>, } +impl Gc +where + T: Trace + 'static, +{ + #[inline] + pub fn as_mut_ptr(&self) -> *const T { + unsafe { &mut self.ptr.get().as_mut().value as *mut T } + } + + #[inline] + pub fn as_ptr(&self) -> *const T { + unsafe { &self.ptr.get().as_ref().value as *const T } + } + + #[inline] + pub fn ptr_eq(&self, other: &Self) -> bool { + self.ptr == other.ptr + } +} + +impl Gc +where + T: Deref + Trace + 'static, +{ + #[inline] + pub fn as_deref(&self) -> &T::Target { + &**self + } +} + +impl fmt::Debug for Gc +where + T: fmt::Debug + Trace + 'static, +{ + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.as_ref().fmt(f) + } +} + +impl fmt::Display for Gc +where + T: fmt::Display + Trace + 'static, +{ + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.as_ref().fmt(f) + } +} + +impl PartialEq for Gc +where + T: PartialEq + Trace + 'static, +{ + #[inline] + fn eq(&self, other: &Self) -> bool { + self.as_ref().eq(other.as_ref()) + } +} + +impl Eq for Gc where T: Eq + Trace + 'static {} + +impl Hash for Gc +where + T: Hash + Trace + 'static, +{ + #[inline] + fn hash(&self, state: &mut H) { + self.as_ref().hash(state) + } +} + +impl PartialOrd for Gc +where + T: PartialOrd + Trace + 'static, +{ + #[inline] + fn partial_cmp(&self, other: &Self) -> Option { + self.as_ref().partial_cmp(other.as_ref()) + } +} + +impl Ord for Gc +where + T: Ord + Trace + 'static, +{ + #[inline] + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + self.as_ref().cmp(other.as_ref()) + } +} + +impl Borrow for Gc +where + T: Trace + 'static, +{ + #[inline] + fn borrow(&self) -> &T { + &*self + } +} + +impl AsRef for Gc +where + T: Trace + 'static, +{ + #[inline] + fn as_ref(&self) -> &T { + &*self + } +} + impl Clone for Gc where T: Trace + 'static, { + #[inline] fn clone(&self) -> Self { Self { ptr: self.ptr.clone(), @@ -34,6 +150,7 @@ impl Trace for Gc where T: Trace + 'static, { + #[inline] fn trace(&self) { let ptr = unsafe { self.ptr.get().as_mut() }; if !ptr.is_marked() { @@ -49,6 +166,7 @@ where { type Target = T; + #[inline] fn deref(&self) -> &Self::Target { unsafe { self.ptr.get().as_ref() } } diff --git a/som-gc/src/gc_box.rs b/som-gc/src/gc_box.rs index 49e6a951..0a9ba263 100644 --- a/som-gc/src/gc_box.rs +++ b/som-gc/src/gc_box.rs @@ -5,6 +5,7 @@ use std::ptr::NonNull; use crate::Trace; /// Represents a value, as it is stored within the GC. +#[repr(C)] pub(crate) struct GcBox { /// Pointer to next value in the GC chain. pub(crate) marked: Cell, @@ -14,6 +15,7 @@ pub(crate) struct GcBox { impl GcBox { /// Creates a singleton `GcBox` value (which isn't part of any chain yet). + #[inline] pub fn new(value: T) -> Self { Self { marked: Cell::new(false), @@ -25,22 +27,26 @@ impl GcBox { impl GcBox { /// Clears the `mark` bit for this GC object. + #[inline] pub fn clear_mark(&mut self) { self.marked.set(false); } /// Sets the `mark` bit for this GC object. + #[inline] pub fn mark(&mut self) { self.marked.set(true); } /// Returns whether the `mark` bit is set for this GC object. + #[inline] pub fn is_marked(&self) -> bool { self.marked.get() } } impl Default for GcBox { + #[inline] fn default() -> Self { Self { marked: Cell::new(false), @@ -53,12 +59,14 @@ impl Default for GcBox { impl Deref for GcBox { type Target = T; + #[inline] fn deref(&self) -> &Self::Target { &self.value } } impl DerefMut for GcBox { + #[inline] fn deref_mut(&mut self) -> &mut Self::Target { &mut self.value } diff --git a/som-gc/src/heap.rs b/som-gc/src/heap.rs index 55b54025..8b4556fc 100644 --- a/som-gc/src/heap.rs +++ b/som-gc/src/heap.rs @@ -1,17 +1,41 @@ use std::cell::Cell; use std::marker::PhantomData; use std::ptr::NonNull; +use std::time::{Duration, Instant}; use crate::gc_box::GcBox; use crate::gc::Gc; use crate::trace::Trace; +pub struct GcStats { + pub collections_performed: usize, + pub bytes_allocated: usize, + pub bytes_swept: usize, + pub total_time_spent: Duration, +} + +pub struct GcParams { + pub threshold: usize, + pub used_space_ratio: f64, +} + /// The GC heap itself, which is the storage for all GC-ed objects. pub struct GcHeap { + stats: GcStats, + params: GcParams, head: Option>>, } +impl Default for GcParams { + fn default() -> Self { + Self { + threshold: 10_000_000, + used_space_ratio: 0.7, + } + } +} + impl Drop for GcHeap { // We properly drop all objects in the heap. fn drop(&mut self) { @@ -25,13 +49,35 @@ impl Drop for GcHeap { } impl GcHeap { - /// Creates a new empty GC heap. + /// Creates a new empty GC heap, with the default parameters. pub fn new() -> Self { + Self::with_params(GcParams::default()) + } + + /// Creates a new empty GC heap, with the specified parameters. + pub fn with_params(params: GcParams) -> Self { Self { + params, + stats: GcStats { + collections_performed: 0, + bytes_allocated: 0, + bytes_swept: 0, + total_time_spent: Duration::ZERO, + }, head: None, } } + /// Returns a reference to the GC's stats. + pub fn stats(&self) -> &GcStats { + &self.stats + } + + /// Returns a reference to the GC's parameters. + pub fn params(&self) -> &GcParams { + &self.params + } + /// Allocates an object on the GC heap, returning its handle. pub fn allocate(&mut self, value: T) -> Gc { // TODO: trigger `collect_garbage` @@ -39,6 +85,7 @@ impl GcHeap { allocated.next = self.head; let ptr = unsafe { NonNull::new_unchecked(Box::into_raw(allocated)) }; self.head = Some(ptr as NonNull>); + self.stats.bytes_allocated += std::mem::size_of::>(); Gc { // SAFETY: `self.head` is guaranteed to be properly aligned and non-null by `Box::into_raw`. ptr: Cell::new(ptr), @@ -72,7 +119,9 @@ impl GcHeap { } // TODO: introduce a `Finalize`-like mechanism. // TODO: maybe perform the drops in a separate thread. - drop(unsafe { Box::from_raw(cur.as_ptr()) }); + let value = unsafe { Box::from_raw(cur.as_ptr()) }; + self.stats.bytes_allocated -= std::mem::size_of_val::>(&*value); + drop(value); } else { prev = head; } @@ -82,8 +131,27 @@ impl GcHeap { /// Performs garbage collection (mark-and-sweep) on the GC heap. pub fn collect_garbage(&mut self, mut mark_fn: impl FnMut()) { + let start = Instant::now(); + let allocated_start = self.stats.bytes_allocated; self.clear_marks(); mark_fn(); self.sweep(); + self.stats.bytes_swept += allocated_start - self.stats.bytes_allocated; + self.stats.total_time_spent += start.elapsed(); + self.stats.collections_performed += 1; + } + + /// Performs garbage collection (mark-and-sweep) on the GC heap, only if necessary. + pub fn maybe_collect_garbage(&mut self, mark_fn: impl FnMut()) { + if self.stats.bytes_allocated > self.params.threshold { + self.collect_garbage(mark_fn); + + if self.stats.bytes_allocated as f64 + > self.params.threshold as f64 * self.params.used_space_ratio + { + self.params.threshold = + (self.stats.bytes_allocated as f64 / self.params.used_space_ratio) as usize + } + } } } diff --git a/som-gc/src/lib.rs b/som-gc/src/lib.rs index 5bd1c383..e9f395ee 100644 --- a/som-gc/src/lib.rs +++ b/som-gc/src/lib.rs @@ -4,5 +4,5 @@ mod heap; mod trace; pub use crate::gc::Gc; -pub use crate::heap::GcHeap; +pub use crate::heap::{GcHeap, GcParams}; pub use crate::trace::Trace; diff --git a/som-gc/src/trace.rs b/som-gc/src/trace.rs index acac0070..6d1811fc 100644 --- a/som-gc/src/trace.rs +++ b/som-gc/src/trace.rs @@ -19,6 +19,7 @@ macro_rules! trivial_trace { ($($ty:ty),* $(,)?) => { $( impl $crate::trace::Trace for $ty { + #[inline] fn trace(&self) {} } )* @@ -82,6 +83,7 @@ macro_rules! tuple_trace { (~ $($X:ident)*) => { #[allow(non_snake_case)] impl<$($X: $crate::trace::Trace),*> Trace for ($($X,)*) { + #[inline] fn trace(&self) { let ($($X,)*) = self; $($X.trace();)* @@ -96,6 +98,7 @@ macro_rules! iter_1_trace { ($($ty:ty),* $(,)?) => { $( impl $crate::trace::Trace for $ty { + #[inline] fn trace(&self) { for it in self.into_iter() { it.trace(); @@ -119,6 +122,7 @@ macro_rules! iter_2_trace { ($($ty:ty),* $(,)?) => { $( impl $crate::trace::Trace for $ty { + #[inline] fn trace(&self) { for (k, v) in self.into_iter() { k.trace(); @@ -133,6 +137,7 @@ macro_rules! iter_2_trace { iter_2_trace!(HashMap, BTreeMap); impl Trace for &[T] { + #[inline] fn trace(&self) { for it in self.into_iter() { it.trace(); @@ -141,6 +146,7 @@ impl Trace for &[T] { } impl Trace for [T; N] { + #[inline] fn trace(&self) { for it in self.into_iter() { it.trace(); @@ -149,7 +155,27 @@ impl Trace for [T; N] { } impl Trace for RefCell { + #[inline] fn trace(&self) { self.borrow().trace(); } } + +impl Trace for Option { + #[inline] + fn trace(&self) { + if let Some(value) = self { + value.trace(); + } + } +} + +impl Trace for *const T { + #[inline] + fn trace(&self) {} +} + +impl Trace for *mut T { + #[inline] + fn trace(&self) {} +} From 1011d0f9ec2d305fa6cff39f5ec1607baf79fd82 Mon Sep 17 00:00:00 2001 From: Nicolas Polomack Date: Thu, 2 Feb 2023 16:05:46 +0100 Subject: [PATCH 04/14] feat: bytecode interpreter GC integration --- Cargo.lock | 1 + som-gc/src/gc.rs | 2 +- som-gc/src/gc_box.rs | 2 +- som-interpreter-bc/Cargo.toml | 1 + som-interpreter-bc/src/block.rs | 23 ++- som-interpreter-bc/src/class.rs | 92 +++++----- som-interpreter-bc/src/compiler.rs | 106 +++++++---- som-interpreter-bc/src/frame.rs | 42 ++++- som-interpreter-bc/src/hashcode.rs | 6 +- som-interpreter-bc/src/instance.rs | 10 ++ som-interpreter-bc/src/interpreter.rs | 150 ++++++++++------ som-interpreter-bc/src/lib.rs | 9 +- som-interpreter-bc/src/main.rs | 77 ++++++-- som-interpreter-bc/src/method.rs | 62 ++++--- som-interpreter-bc/src/primitives/array.rs | 20 +-- som-interpreter-bc/src/primitives/blocks.rs | 16 +- som-interpreter-bc/src/primitives/class.rs | 37 ++-- som-interpreter-bc/src/primitives/double.rs | 36 ++-- som-interpreter-bc/src/primitives/integer.rs | 49 +++-- som-interpreter-bc/src/primitives/method.rs | 19 +- som-interpreter-bc/src/primitives/mod.rs | 5 +- som-interpreter-bc/src/primitives/object.rs | 51 ++++-- som-interpreter-bc/src/primitives/string.rs | 29 +-- som-interpreter-bc/src/primitives/symbol.rs | 10 +- som-interpreter-bc/src/primitives/system.rs | 63 ++++--- som-interpreter-bc/src/shell.rs | 13 +- som-interpreter-bc/src/universe.rs | 168 +++++++++++++----- som-interpreter-bc/src/value.rs | 79 +++++--- .../tests/basic_interpreter_tests.rs | 36 ++-- 29 files changed, 787 insertions(+), 427 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index be07de7e..296914be 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -323,6 +323,7 @@ dependencies = [ "num-traits", "rand", "som-core", + "som-gc", "som-lexer", "som-parser-symbols", ] diff --git a/som-gc/src/gc.rs b/som-gc/src/gc.rs index adfbaef9..1bfd5c26 100644 --- a/som-gc/src/gc.rs +++ b/som-gc/src/gc.rs @@ -28,7 +28,7 @@ where #[inline] pub fn as_mut_ptr(&self) -> *const T { unsafe { &mut self.ptr.get().as_mut().value as *mut T } - } + } #[inline] pub fn as_ptr(&self) -> *const T { diff --git a/som-gc/src/gc_box.rs b/som-gc/src/gc_box.rs index 0a9ba263..0687756a 100644 --- a/som-gc/src/gc_box.rs +++ b/som-gc/src/gc_box.rs @@ -1,5 +1,5 @@ -use std::ops::{Deref, DerefMut}; use std::cell::Cell; +use std::ops::{Deref, DerefMut}; use std::ptr::NonNull; use crate::Trace; diff --git a/som-interpreter-bc/Cargo.toml b/som-interpreter-bc/Cargo.toml index 21f32c1e..b648b64b 100644 --- a/som-interpreter-bc/Cargo.toml +++ b/som-interpreter-bc/Cargo.toml @@ -13,6 +13,7 @@ som-core = { path = "../som-core", version = "0.1.0" } som-lexer = { path = "../som-lexer", version = "0.1.0" } som-parser = { package = "som-parser-symbols", path = "../som-parser-symbols", version = "0.1.0" } # som-parser = { package = "som-parser-text", path = "../som-parser-text", version = "0.1.0" } +som-gc = { path = "../som-gc", version = "0.1.0" } # CLI interface clap = { version = "3.0", features = ["derive"] } diff --git a/som-interpreter-bc/src/block.rs b/som-interpreter-bc/src/block.rs index d4719cc9..13af535f 100644 --- a/som-interpreter-bc/src/block.rs +++ b/som-interpreter-bc/src/block.rs @@ -1,8 +1,8 @@ use std::cell::RefCell; use std::fmt; -use std::rc::Rc; use som_core::bytecode::Bytecode; +use som_gc::{Gc, Trace}; use crate::class::Class; use crate::compiler::Literal; @@ -18,7 +18,7 @@ pub struct BlockInfo { pub literals: Vec, pub body: Vec, pub nb_params: usize, - pub inline_cache: RefCell)>>>, + pub inline_cache: RefCell)>>>, } /// Represents an executable block. @@ -26,7 +26,24 @@ pub struct BlockInfo { pub struct Block { /// Reference to the captured stack frame. pub frame: Option>, - pub blk_info: Rc, + pub blk_info: Gc, +} + +impl Trace for BlockInfo { + #[inline] + fn trace(&self) { + self.locals.trace(); + self.literals.trace(); + self.inline_cache.trace(); + } +} + +impl Trace for Block { + #[inline] + fn trace(&self) { + self.frame.trace(); + self.blk_info.trace(); + } } impl Block { diff --git a/som-interpreter-bc/src/class.rs b/som-interpreter-bc/src/class.rs index 2f41b74c..f6311316 100644 --- a/som-interpreter-bc/src/class.rs +++ b/som-interpreter-bc/src/class.rs @@ -1,21 +1,13 @@ use std::fmt; -use std::rc::Rc; use indexmap::IndexMap; +use som_gc::{Gc, Trace}; + use crate::interner::Interned; use crate::method::Method; use crate::value::Value; -use crate::{SOMRef, SOMWeakRef}; - -/// A reference that may be either weak or owned/strong. -#[derive(Debug, Clone)] -pub enum MaybeWeak { - /// An owned reference. - Strong(SOMRef), - /// A weak reference. - Weak(SOMWeakRef), -} +use crate::SOMRef; /// Represents a loaded class. #[derive(Clone)] @@ -23,18 +15,31 @@ pub struct Class { /// The class' name. pub name: String, /// The class of this class. - pub class: MaybeWeak, + pub class: Option>, /// The superclass of this class. - // TODO: Should probably be `Option>`. - pub super_class: SOMWeakRef, + pub super_class: Option>, /// The class' locals. pub locals: IndexMap, /// The class' methods/invokables. - pub methods: IndexMap>, + pub methods: IndexMap>, /// Is this class a static one ? pub is_static: bool, } +impl Trace for Class { + #[inline] + fn trace(&self) { + self.class.trace(); + self.super_class.trace(); + for it in self.locals.values() { + it.trace(); + } + for it in self.methods.values() { + it.trace(); + } + } +} + impl Class { /// Get the class' name. pub fn name(&self) -> &str { @@ -43,51 +48,40 @@ impl Class { /// Get the class of this class. pub fn class(&self) -> SOMRef { - match self.class { - MaybeWeak::Weak(ref weak) => weak.upgrade().unwrap_or_else(|| { - panic!("superclass dropped, cannot upgrade ref ({})", self.name()) - }), - MaybeWeak::Strong(ref owned) => owned.clone(), - } + self.class.clone().unwrap() } - /// Set the class of this class (as a weak reference). - pub fn set_class(&mut self, class: &SOMRef) { - self.class = MaybeWeak::Weak(Rc::downgrade(class)); - } - - /// Set the class of this class (as a strong reference). - pub fn set_class_owned(&mut self, class: &SOMRef) { - self.class = MaybeWeak::Strong(class.clone()); + /// Set the class of this class. + pub fn set_class(&mut self, class: SOMRef) { + self.class = Some(class); } /// Get the superclass of this class. pub fn super_class(&self) -> Option> { - self.super_class.upgrade() + self.super_class.clone() } - /// Set the superclass of this class (as a weak reference). - pub fn set_super_class(&mut self, class: &SOMRef) { - self.super_class = Rc::downgrade(class); + /// Set the superclass of this class. + pub fn set_super_class(&mut self, class: SOMRef) { + self.super_class = Some(class); } /// Search for a given method within this class. - pub fn lookup_method(&self, signature: Interned) -> Option> { - self.methods.get(&signature).cloned().or_else(|| { - self.super_class - .upgrade()? - .borrow() - .lookup_method(signature) - }) + pub fn lookup_method(&self, signature: Interned) -> Option> { + if let Some(method) = self.methods.get(&signature).cloned() { + return Some(method); + } + + self.super_class.as_ref()?.borrow().lookup_method(signature) } /// Search for a local binding. pub fn lookup_local(&self, idx: usize) -> Option { - self.locals.values().nth(idx).cloned().or_else(|| { - let super_class = self.super_class()?; - let local = super_class.borrow_mut().lookup_local(idx)?; - Some(local) - }) + if let Some(local) = self.locals.values().nth(idx).cloned() { + return Some(local); + } + + self.super_class.as_ref()?.borrow_mut().lookup_local(idx) } /// Assign a value to a local binding. @@ -96,9 +90,11 @@ impl Class { *local = value; return Some(()); } - let super_class = self.super_class()?; - super_class.borrow_mut().assign_local(idx, value)?; - Some(()) + + self.super_class + .as_ref()? + .borrow_mut() + .assign_local(idx, value) } } diff --git a/som-interpreter-bc/src/compiler.rs b/som-interpreter-bc/src/compiler.rs index 0692ef86..1bdc4e8a 100644 --- a/som-interpreter-bc/src/compiler.rs +++ b/som-interpreter-bc/src/compiler.rs @@ -3,16 +3,16 @@ //! use std::cell::RefCell; use std::hash::{Hash, Hasher}; -use std::rc::{Rc, Weak}; use indexmap::{IndexMap, IndexSet}; use num_bigint::BigInt; use som_core::ast; use som_core::bytecode::Bytecode; +use som_gc::{Gc, GcHeap, Trace}; use crate::block::{Block, BlockInfo}; -use crate::class::{Class, MaybeWeak}; +use crate::class::Class; use crate::interner::{Interned, Interner}; use crate::method::{Method, MethodEnv, MethodKind}; use crate::primitives; @@ -22,12 +22,31 @@ use crate::SOMRef; #[derive(Debug, Clone)] pub enum Literal { Symbol(Interned), - String(Rc), + String(Gc), Double(f64), Integer(i64), BigInteger(BigInt), Array(Vec), - Block(Rc), + Block(Gc), +} + +impl Trace for Literal { + #[inline] + fn trace(&self) { + match self { + Literal::Symbol(_) => {} + Literal::String(string) => { + string.trace(); + } + Literal::Double(_) => {} + Literal::Integer(_) => {} + Literal::BigInteger(_) => {} + Literal::Array(_) => {} + Literal::Block(block) => { + block.trace(); + } + } + } } impl PartialEq for Literal { @@ -39,7 +58,7 @@ impl PartialEq for Literal { (Literal::Integer(val1), Literal::Integer(val2)) => val1.eq(val2), (Literal::BigInteger(val1), Literal::BigInteger(val2)) => val1.eq(val2), (Literal::Array(val1), Literal::Array(val2)) => val1.eq(val2), - (Literal::Block(val1), Literal::Block(val2)) => Rc::ptr_eq(val1, val2), + (Literal::Block(val1), Literal::Block(val2)) => Gc::ptr_eq(val1, val2), _ => false, } } @@ -92,6 +111,7 @@ trait GenCtxt { fn find_var(&mut self, name: &str) -> Option; fn intern_symbol(&mut self, name: &str) -> Interned; fn class_name(&self) -> &str; + fn heap(&mut self) -> &mut GcHeap; } trait InnerGenCtxt: GenCtxt { @@ -136,6 +156,10 @@ impl GenCtxt for BlockGenCtxt<'_> { fn class_name(&self) -> &str { self.outer.class_name() } + + fn heap(&mut self) -> &mut GcHeap { + self.outer.heap() + } } impl InnerGenCtxt for BlockGenCtxt<'_> { @@ -219,6 +243,10 @@ impl GenCtxt for MethodGenCtxt<'_> { fn class_name(&self) -> &str { self.inner.class_name() } + + fn heap(&mut self) -> &mut GcHeap { + self.inner.heap() + } } impl InnerGenCtxt for MethodGenCtxt<'_> { @@ -359,7 +387,9 @@ impl MethodCodegen for ast::Expression { ast::Literal::Symbol(val) => { Literal::Symbol(ctxt.intern_symbol(val.as_str())) } - ast::Literal::String(val) => Literal::String(Rc::new(val.clone())), + ast::Literal::String(val) => { + Literal::String(ctxt.heap().allocate(val.clone())) + } ast::Literal::Double(val) => Literal::Double(*val), ast::Literal::Integer(val) => Literal::Integer(*val), ast::Literal::BigInteger(val) => Literal::BigInteger(val.parse().unwrap()), @@ -396,7 +426,7 @@ impl MethodCodegen for ast::Expression { } ast::Expression::Block(val) => { let block = compile_block(ctxt.as_gen_ctxt(), val)?; - let block = Rc::new(block); + let block = ctxt.heap().allocate(block); let block = Literal::Block(block); let idx = ctxt.push_literal(block); ctxt.push_instr(Bytecode::PushBlock(idx as u8)); @@ -409,8 +439,9 @@ impl MethodCodegen for ast::Expression { struct ClassGenCtxt<'a> { pub name: String, pub fields: IndexSet, - pub methods: IndexMap>, + pub methods: IndexMap>, pub interner: &'a mut Interner, + pub heap: &'a mut GcHeap, } impl GenCtxt for ClassGenCtxt<'_> { @@ -428,9 +459,17 @@ impl GenCtxt for ClassGenCtxt<'_> { fn class_name(&self) -> &str { self.name.as_str() } + + fn heap(&mut self) -> &mut GcHeap { + self.heap + } } -fn compile_method(outer: &mut dyn GenCtxt, defn: &ast::MethodDef) -> Option { +fn compile_method( + outer: &mut dyn GenCtxt, + holder: &SOMRef, + defn: &ast::MethodDef, +) -> Option { // println!("(method) compiling '{}' ...", defn.signature); let mut ctxt = MethodGenCtxt { @@ -499,7 +538,7 @@ fn compile_method(outer: &mut dyn GenCtxt, defn: &ast::MethodDef) -> Option Option { ctxt.push_instr(Bytecode::ReturnLocal); } + let body_len = ctxt.body.as_ref().map_or(0, |body| body.len()); + let inline_cache = RefCell::new(vec![None; body_len]); + let frame = None; let locals = { let locals = std::mem::take(&mut ctxt.locals); @@ -540,11 +582,10 @@ fn compile_block(outer: &mut dyn GenCtxt, defn: &ast::Block) -> Option { let literals = ctxt.literals.into_iter().collect(); let body = ctxt.body.unwrap_or_default(); let nb_params = ctxt.args.len(); - let inline_cache = RefCell::new(vec![None; body.len()]); let block = Block { frame, - blk_info: Rc::new(BlockInfo { + blk_info: ctxt.heap().allocate(BlockInfo { locals, literals, body, @@ -559,6 +600,7 @@ fn compile_block(outer: &mut dyn GenCtxt, defn: &ast::Block) -> Option { } pub fn compile_class( + heap: &mut GcHeap, interner: &mut Interner, defn: &ast::ClassDef, super_class: Option<&SOMRef>, @@ -591,12 +633,13 @@ pub fn compile_class( fields: locals, methods: IndexMap::new(), interner, + heap, }; - let static_class = Rc::new(RefCell::new(Class { + let static_class = static_class_ctxt.heap.allocate(RefCell::new(Class { name: static_class_ctxt.name.clone(), - class: MaybeWeak::Weak(Weak::new()), - super_class: Weak::new(), + class: None, + super_class: None, locals: IndexMap::new(), methods: IndexMap::new(), is_static: true, @@ -604,9 +647,9 @@ pub fn compile_class( for method in &defn.static_methods { let signature = static_class_ctxt.interner.intern(method.signature.as_str()); - let mut method = compile_method(&mut static_class_ctxt, method)?; - method.holder = Rc::downgrade(&static_class); - static_class_ctxt.methods.insert(signature, Rc::new(method)); + let method = compile_method(&mut static_class_ctxt, &static_class, method)?; + let method = static_class_ctxt.heap.allocate(method); + static_class_ctxt.methods.insert(signature, method); } if let Some(primitives) = primitives::get_class_primitives(&defn.name) { @@ -622,10 +665,11 @@ pub fn compile_class( let method = Method { signature: signature.to_string(), kind: MethodKind::Primitive(primitive), - holder: Rc::downgrade(&static_class), + holder: static_class.clone(), }; let signature = static_class_ctxt.interner.intern(signature); - static_class_ctxt.methods.insert(signature, Rc::new(method)); + let method = static_class_ctxt.heap.allocate(method); + static_class_ctxt.methods.insert(signature, method); } } @@ -670,12 +714,13 @@ pub fn compile_class( fields: locals, methods: IndexMap::new(), interner, + heap, }; - let instance_class = Rc::new(RefCell::new(Class { + let instance_class = instance_class_ctxt.heap.allocate(RefCell::new(Class { name: instance_class_ctxt.name.clone(), - class: MaybeWeak::Strong(static_class.clone()), - super_class: Weak::new(), + class: Some(static_class.clone()), + super_class: None, locals: IndexMap::new(), methods: IndexMap::new(), is_static: false, @@ -685,11 +730,9 @@ pub fn compile_class( let signature = instance_class_ctxt .interner .intern(method.signature.as_str()); - let mut method = compile_method(&mut instance_class_ctxt, method)?; - method.holder = Rc::downgrade(&instance_class); - instance_class_ctxt - .methods - .insert(signature, Rc::new(method)); + let method = compile_method(&mut instance_class_ctxt, &instance_class, method)?; + let method = instance_class_ctxt.heap.allocate(method); + instance_class_ctxt.methods.insert(signature, method); } if let Some(primitives) = primitives::get_instance_primitives(&defn.name) { @@ -705,12 +748,11 @@ pub fn compile_class( let method = Method { signature: signature.to_string(), kind: MethodKind::Primitive(primitive), - holder: Rc::downgrade(&instance_class), + holder: instance_class.clone(), }; let signature = instance_class_ctxt.interner.intern(signature); - instance_class_ctxt - .methods - .insert(signature, Rc::new(method)); + let method = instance_class_ctxt.heap.allocate(method); + instance_class_ctxt.methods.insert(signature, method); } } diff --git a/som-interpreter-bc/src/frame.rs b/som-interpreter-bc/src/frame.rs index c9f3a07c..9da46625 100644 --- a/som-interpreter-bc/src/frame.rs +++ b/som-interpreter-bc/src/frame.rs @@ -1,6 +1,5 @@ -use std::rc::Rc; - use som_core::bytecode::Bytecode; +use som_gc::{Gc, Trace}; use crate::block::Block; use crate::class::Class; @@ -15,14 +14,14 @@ pub enum FrameKind { /// A frame created from a block evaluation. Block { /// The block instance for the current frame. - block: Rc, + block: Gc, }, /// A frame created from a method invocation. Method { /// The holder of the current method (used for lexical self/super). holder: SOMRef, /// The current method. - method: Rc, + method: Gc, /// The self value. self_value: Value, }, @@ -40,6 +39,33 @@ pub struct Frame { pub bytecode_idx: usize, } +impl Trace for FrameKind { + #[inline] + fn trace(&self) { + match self { + FrameKind::Block { block } => block.trace(), + FrameKind::Method { + holder, + method, + self_value, + } => { + holder.trace(); + method.trace(); + self_value.trace(); + } + } + } +} + +impl Trace for Frame { + #[inline] + fn trace(&self) { + self.kind.trace(); + self.args.trace(); + self.locals.trace(); + } +} + impl Frame { /// Construct a new empty frame from its kind. pub fn from_kind(kind: FrameKind) -> Self { @@ -54,7 +80,7 @@ impl Frame { } } FrameKind::Method { method, .. } => { - if let MethodKind::Defined(env) = method.kind() { + if let MethodKind::Defined(env) = &method.kind { let locals = env.locals.iter().map(|_| Value::Nil).collect(); Self { kind, @@ -98,7 +124,7 @@ impl Frame { } /// Get the current method itself. - pub fn get_method(&self) -> Rc { + pub fn get_method(&self) -> Gc { match &self.kind { FrameKind::Method { method, .. } => method.clone(), FrameKind::Block { block, .. } => block.frame.as_ref().unwrap().borrow().get_method(), @@ -108,7 +134,7 @@ impl Frame { /// Get the bytecode at the specified index for the current method. pub fn get_bytecode(&self, idx: usize) -> Option { match &self.kind { - FrameKind::Method { method, .. } => match method.kind() { + FrameKind::Method { method, .. } => match &method.kind { MethodKind::Defined(env) => env.body.get(idx).copied(), MethodKind::Primitive(_) => None, MethodKind::NotImplemented(_) => None, @@ -125,7 +151,7 @@ impl Frame { pub fn lookup_constant(&self, idx: usize) -> Option { match self.kind() { FrameKind::Block { block } => block.blk_info.literals.get(idx).cloned(), - FrameKind::Method { method, .. } => match method.kind() { + FrameKind::Method { method, .. } => match &method.kind { MethodKind::Defined(env) => env.literals.get(idx).cloned(), MethodKind::Primitive(_) => None, MethodKind::NotImplemented(_) => None, diff --git a/som-interpreter-bc/src/hashcode.rs b/som-interpreter-bc/src/hashcode.rs index 212fc219..d3eef9ee 100644 --- a/som-interpreter-bc/src/hashcode.rs +++ b/som-interpreter-bc/src/hashcode.rs @@ -100,11 +100,7 @@ impl Hash for Block { impl Hash for Method { fn hash(&self, hasher: &mut H) { - if let Some(holder) = self.holder().upgrade() { - holder.borrow().hash(hasher); - } else { - hasher.write(b"??"); - } + self.holder.borrow().hash(hasher); hasher.write(b">>"); self.signature.hash(hasher); } diff --git a/som-interpreter-bc/src/instance.rs b/som-interpreter-bc/src/instance.rs index a8f61ee0..0afc1ed1 100644 --- a/som-interpreter-bc/src/instance.rs +++ b/som-interpreter-bc/src/instance.rs @@ -1,5 +1,7 @@ use std::fmt; +use som_gc::Trace; + use crate::class::Class; use crate::value::Value; use crate::SOMRef; @@ -13,6 +15,14 @@ pub struct Instance { pub locals: Vec, } +impl Trace for Instance { + #[inline] + fn trace(&self) { + self.class.trace(); + self.locals.trace(); + } +} + impl Instance { /// Construct an instance for a given class. pub fn from_class(class: SOMRef) -> Self { diff --git a/som-interpreter-bc/src/interpreter.rs b/som-interpreter-bc/src/interpreter.rs index 5ee45216..8bf9e865 100644 --- a/som-interpreter-bc/src/interpreter.rs +++ b/som-interpreter-bc/src/interpreter.rs @@ -1,8 +1,8 @@ use std::cell::RefCell; -use std::rc::Rc; use std::time::Instant; use som_core::bytecode::Bytecode; +use som_gc::{Gc, GcHeap, Trace}; use crate::block::Block; use crate::class::Class; @@ -18,7 +18,7 @@ const INT_0: Value = Value::Integer(0); const INT_1: Value = Value::Integer(1); macro_rules! send { - ($interp:expr, $universe:expr, $frame:expr, $lit_idx:expr, $nb_params:expr, $bytecode_idx:expr) => {{ + ($interp:expr, $universe:expr, $heap:expr, $frame:expr, $lit_idx:expr, $nb_params:expr, $bytecode_idx:expr) => {{ let literal = $frame.borrow().lookup_constant($lit_idx as usize).unwrap(); let Literal::Symbol(symbol) = literal else { return None; @@ -35,12 +35,19 @@ macro_rules! send { let receiver_class = receiver.class($universe); resolve_method($frame, &receiver_class, symbol, $bytecode_idx) }; - do_send($interp, $universe, method, symbol, nb_params as usize); + do_send( + $interp, + $universe, + $heap, + method, + symbol, + nb_params as usize, + ); }}; } macro_rules! super_send { - ($interp:expr, $universe:expr, $frame_expr:expr, $lit_idx:expr, $nb_params:expr, $bytecode_idx:expr) => {{ + ($interp:expr, $universe:expr, $heap:expr, $frame_expr:expr, $lit_idx:expr, $nb_params:expr, $bytecode_idx:expr) => {{ let literal = $frame_expr .borrow() .lookup_constant($lit_idx as usize) @@ -60,7 +67,14 @@ macro_rules! super_send { let super_class = holder.borrow().super_class().unwrap(); resolve_method($frame_expr, &super_class, symbol, $bytecode_idx) }; - do_send($interp, $universe, method, symbol, nb_params as usize); + do_send( + $interp, + $universe, + $heap, + method, + symbol, + nb_params as usize, + ); }}; } @@ -73,6 +87,14 @@ pub struct Interpreter { pub start_time: Instant, } +impl Trace for Interpreter { + #[inline] + fn trace(&self) { + self.frames.trace(); + self.stack.trace(); + } +} + impl Interpreter { pub fn new() -> Self { Self { @@ -82,8 +104,8 @@ impl Interpreter { } } - pub fn push_frame(&mut self, kind: FrameKind) -> SOMRef { - let frame = Rc::new(RefCell::new(Frame::from_kind(kind))); + pub fn push_frame(&mut self, heap: &mut GcHeap, kind: FrameKind) -> SOMRef { + let frame = heap.allocate(RefCell::new(Frame::from_kind(kind))); self.frames.push(frame.clone()); frame } @@ -96,25 +118,29 @@ impl Interpreter { self.frames.last() } - pub fn run(&mut self, universe: &mut Universe) -> Option { + pub fn run(&mut self, heap: &mut GcHeap, universe: &mut Universe) -> Option { loop { - let frame = match self.current_frame() { - Some(frame) => frame, - None => return Some(self.stack.pop().unwrap_or(Value::Nil)), + heap.maybe_collect_garbage(|| { + self.trace(); + universe.trace(); + }); + + let Some(frame) = self.current_frame() else { + return Some(self.stack.pop().unwrap_or(Value::Nil)); }; - let bytecode_idx = frame.borrow().bytecode_idx; - let opt_bytecode = frame.borrow().get_current_bytecode(); - let bytecode = match opt_bytecode { - Some(bytecode) => bytecode, - None => { - self.pop_frame(); - self.stack.push(Value::Nil); - continue; - } + let mut cur_frame = frame.borrow_mut(); + let Some(bytecode) = cur_frame.get_current_bytecode() else { + drop(cur_frame); + self.pop_frame(); + self.stack.push(Value::Nil); + continue; }; - frame.borrow_mut().bytecode_idx += 1; + let bytecode_idx = cur_frame.bytecode_idx; + cur_frame.bytecode_idx += 1; + + drop(cur_frame); match bytecode { Bytecode::Halt => { @@ -168,27 +194,27 @@ impl Interpreter { Literal::Block(blk) => Block::clone(&blk), _ => return None, }; - block.frame.replace(Rc::clone(&frame)); - self.stack.push(Value::Block(Rc::new(block))); + block.frame.replace(Gc::clone(&frame)); + self.stack.push(Value::Block(heap.allocate(block))); } Bytecode::PushConstant(idx) => { let literal = frame.borrow().lookup_constant(idx as usize).unwrap(); - let value = convert_literal(frame, literal).unwrap(); + let value = convert_literal(heap, &frame, literal).unwrap(); self.stack.push(value); } Bytecode::PushConstant0 => { let literal = frame.borrow().lookup_constant(0).unwrap(); - let value = convert_literal(frame, literal).unwrap(); + let value = convert_literal(heap, &frame, literal).unwrap(); self.stack.push(value); } Bytecode::PushConstant1 => { let literal = frame.borrow().lookup_constant(1).unwrap(); - let value = convert_literal(frame, literal).unwrap(); + let value = convert_literal(heap, &frame, literal).unwrap(); self.stack.push(value); } Bytecode::PushConstant2 => { let literal = frame.borrow().lookup_constant(2).unwrap(); - let value = convert_literal(frame, literal).unwrap(); + let value = convert_literal(heap, &frame, literal).unwrap(); self.stack.push(value); } Bytecode::PushGlobal(idx) => { @@ -201,7 +227,9 @@ impl Interpreter { self.stack.push(value); } else { let self_value = frame.borrow().get_self(); - universe.unknown_global(self, self_value, symbol).unwrap(); + universe + .unknown_global(self, heap, self_value, symbol) + .unwrap(); } } Bytecode::Push0 => { @@ -263,28 +291,29 @@ impl Interpreter { } } Bytecode::Send1(idx) => { - send! {self, universe, frame, idx, Some(0), bytecode_idx} // Send1 => receiver + 0 args, so we pass Some(0) + // Send1 => receiver + 0 args, so we pass Some(0) + send!(self, universe, heap, frame, idx, Some(0), bytecode_idx) } Bytecode::Send2(idx) => { - send! {self, universe, frame, idx, Some(1), bytecode_idx} + send!(self, universe, heap, frame, idx, Some(1), bytecode_idx) } Bytecode::Send3(idx) => { - send! {self, universe, frame, idx, Some(2), bytecode_idx} + send!(self, universe, heap, frame, idx, Some(2), bytecode_idx) } Bytecode::SendN(idx) => { - send! {self, universe, frame, idx, None, bytecode_idx} + send!(self, universe, heap, frame, idx, None, bytecode_idx) } Bytecode::SuperSend1(idx) => { - super_send! {self, universe, frame, idx, Some(0), bytecode_idx} + super_send!(self, universe, heap, frame, idx, Some(0), bytecode_idx) } Bytecode::SuperSend2(idx) => { - super_send! {self, universe, frame, idx, Some(1), bytecode_idx} + super_send!(self, universe, heap, frame, idx, Some(1), bytecode_idx) } Bytecode::SuperSend3(idx) => { - super_send! {self, universe, frame, idx, Some(2), bytecode_idx} + super_send!(self, universe, heap, frame, idx, Some(2), bytecode_idx) } Bytecode::SuperSendN(idx) => { - super_send! {self, universe, frame, idx, None, bytecode_idx} + super_send!(self, universe, heap, frame, idx, None, bytecode_idx) } Bytecode::ReturnLocal => { let value = self.stack.pop().unwrap(); @@ -299,7 +328,7 @@ impl Interpreter { .frames .iter() .rev() - .position(|live_frame| Rc::ptr_eq(&live_frame, &method_frame)); + .position(|live_frame| Gc::ptr_eq(&live_frame, &method_frame)); if let Some(count) = escaped_frames { (0..count).for_each(|_| self.pop_frame()); @@ -317,7 +346,7 @@ impl Interpreter { } }; // TODO: should we call `doesNotUnderstand:` here ? - universe.escaped_block(self, instance, block).expect( + universe.escaped_block(self, heap, instance, block).expect( "A block has escaped and `escapedBlock:` is not defined on receiver", ); } @@ -328,7 +357,8 @@ impl Interpreter { fn do_send( interpreter: &mut Interpreter, universe: &mut Universe, - method: Option>, + heap: &mut GcHeap, + method: Option>, symbol: Interned, nb_params: usize, ) { @@ -343,7 +373,7 @@ impl Interpreter { args.reverse(); - universe.does_not_understand(interpreter, self_value, symbol, args) + universe.does_not_understand(interpreter, heap, self_value, symbol, args) .expect( "A message cannot be handled and `doesNotUnderstand:arguments:` is not defined on receiver" ); @@ -351,7 +381,7 @@ impl Interpreter { return; }; - match method.kind() { + match &method.kind { MethodKind::Defined(_) => { let mut args = Vec::with_capacity(nb_params + 1); @@ -364,16 +394,18 @@ impl Interpreter { args.reverse(); - let holder = method.holder.upgrade().unwrap(); - let frame = interpreter.push_frame(FrameKind::Method { - self_value, - method, - holder, - }); + let frame = interpreter.push_frame( + heap, + FrameKind::Method { + holder: method.holder.clone(), + self_value, + method, + }, + ); frame.borrow_mut().args = args; } MethodKind::Primitive(func) => { - func(interpreter, universe); + func(interpreter, heap, universe); } MethodKind::NotImplemented(err) => { let self_value = interpreter.stack.iter().nth_back(nb_params).unwrap(); @@ -392,7 +424,7 @@ impl Interpreter { class: &SOMRef, signature: Interned, bytecode_idx: usize, - ) -> Option> { + ) -> Option> { match frame.borrow().kind() { FrameKind::Block { block } => { let mut inline_cache = block.blk_info.inline_cache.borrow_mut(); @@ -403,8 +435,8 @@ impl Interpreter { let maybe_found = unsafe { inline_cache.get_unchecked_mut(bytecode_idx) }; match maybe_found { - Some((receiver, method)) if *receiver == class.as_ptr() => { - Some(Rc::clone(method)) + Some((receiver, method)) if *receiver == RefCell::as_ptr(&class) => { + Some(Gc::clone(method)) } place @ None => { let found = class.borrow().lookup_method(signature); @@ -417,7 +449,7 @@ impl Interpreter { } } FrameKind::Method { method, .. } => { - if let MethodKind::Defined(env) = method.kind() { + if let MethodKind::Defined(env) = &method.kind { let mut inline_cache = env.inline_cache.borrow_mut(); // SAFETY: this access is actually safe because the bytecode compiler @@ -426,8 +458,8 @@ impl Interpreter { let maybe_found = unsafe { inline_cache.get_unchecked_mut(bytecode_idx) }; match maybe_found { - Some((receiver, method)) if *receiver == class.as_ptr() => { - Some(Rc::clone(method)) + Some((receiver, method)) if *receiver == RefCell::as_ptr(&class) => { + Some(Gc::clone(method)) } place @ None => { let found = class.borrow().lookup_method(signature); @@ -445,7 +477,11 @@ impl Interpreter { } } - fn convert_literal(frame: &SOMRef, literal: Literal) -> Option { + fn convert_literal( + heap: &mut GcHeap, + frame: &SOMRef, + literal: Literal, + ) -> Option { let value = match literal { Literal::Symbol(sym) => Value::Symbol(sym), Literal::String(val) => Value::String(val), @@ -459,11 +495,11 @@ impl Interpreter { frame .borrow() .lookup_constant(idx as usize) - .and_then(|lit| convert_literal(frame, lit)) + .and_then(|lit| convert_literal(heap, frame, lit)) }) .collect::>>() .unwrap(); - Value::Array(Rc::new(RefCell::new(arr))) + Value::Array(heap.allocate(RefCell::new(arr))) } Literal::Block(val) => Value::Block(val), }; diff --git a/som-interpreter-bc/src/lib.rs b/som-interpreter-bc/src/lib.rs index 2ede99f3..0cb78f4a 100644 --- a/som-interpreter-bc/src/lib.rs +++ b/som-interpreter-bc/src/lib.rs @@ -3,7 +3,8 @@ //! use std::cell::RefCell; -use std::rc::{Rc, Weak}; + +use som_gc::Gc; /// Facilities for manipulating blocks. pub mod block; @@ -32,7 +33,5 @@ pub mod universe; /// Facilities for manipulating values. pub mod value; -/// A strong and owning reference to an object. -pub type SOMRef = Rc>; -/// A weak reference to an object. -pub type SOMWeakRef = Weak>; +/// A reference to a GC-ed mutable object. +pub type SOMRef = Gc>; diff --git a/som-interpreter-bc/src/main.rs b/som-interpreter-bc/src/main.rs index 62aa7502..39e19c93 100644 --- a/som-interpreter-bc/src/main.rs +++ b/som-interpreter-bc/src/main.rs @@ -4,7 +4,6 @@ #![warn(missing_docs)] use std::path::PathBuf; -use std::rc::Rc; use anyhow::{bail, Context}; use clap::Parser; @@ -13,6 +12,8 @@ use jemallocator::Jemalloc; mod shell; +use som_gc::{GcHeap, GcParams}; + use som_interpreter_bc::disassembler::disassemble_method_body; use som_interpreter_bc::interpreter::Interpreter; use som_interpreter_bc::method::{Method, MethodKind}; @@ -23,6 +24,31 @@ use som_interpreter_bc::value::Value; #[global_allocator] static GLOBAL: Jemalloc = Jemalloc; +#[derive(Debug, Clone, PartialEq, clap::StructOpt)] +struct GcOptions { + /// Minimum amount of bytes allocated to trigger garbage collection. + #[structopt(long, default_value = "10000000")] + gc_initial_threshold: usize, + + /// How much heap should grow after unsuccessful garbage collection. + #[structopt(long, default_value = "0.7")] + gc_used_space_ratio: f64, + + /// Whether to collect on exit, to avoid leaking memory. + #[structopt(long)] + gc_collect_on_exit: bool, + + /// Print GC stats before exiting. + #[structopt(long)] + gc_print_stats: bool, + + /// Force garbage collection before printing stats. + /// Useful for checking for memory leaks. + /// Does nothing unless --gc-print-stats is specified. + #[structopt(long)] + gc_collect_before_stats: bool, +} + #[derive(Debug, Clone, PartialEq, clap::StructOpt)] #[clap(about, author)] struct Options { @@ -43,20 +69,28 @@ struct Options { /// Enable verbose output (with timing information). #[clap(short = 'v')] verbose: bool, + + #[structopt(flatten)] + gc: GcOptions, } fn main() -> anyhow::Result<()> { - let opts: Options = Options::from_args(); + let opts: Options = Options::parse(); + + let mut params = GcParams::default(); + params.threshold = opts.gc.gc_initial_threshold; + params.used_space_ratio = opts.gc.gc_used_space_ratio; + let mut heap = GcHeap::with_params(params); let mut interpreter = Interpreter::new(); if opts.disassemble { - return disassemble_class(opts); + return disassemble_class(&mut heap, opts); } let Some(file) = opts.file else { - let mut universe = Universe::with_classpath(opts.classpath)?; - return shell::interactive(&mut interpreter, &mut universe, opts.verbose); + let mut universe = Universe::with_classpath(&mut heap, opts.classpath)?; + return shell::interactive(&mut heap, &mut interpreter, &mut universe, opts.verbose); }; let file_stem = file @@ -70,7 +104,7 @@ fn main() -> anyhow::Result<()> { classpath.push(directory.to_path_buf()); } - let mut universe = Universe::with_classpath(classpath)?; + let mut universe = Universe::with_classpath(&mut heap, classpath)?; let args = std::iter::once(String::from(file_stem)) .chain(opts.args.iter().cloned()) @@ -79,10 +113,10 @@ fn main() -> anyhow::Result<()> { .collect(); universe - .initialize(&mut interpreter, args) + .initialize(&mut heap, &mut interpreter, args) .expect("issue running program"); - interpreter.run(&mut universe); + interpreter.run(&mut heap, &mut universe); // let class = universe.load_class_from_path(file)?; // let instance = som_interpreter::instance::Instance::from_class(class); @@ -97,10 +131,31 @@ fn main() -> anyhow::Result<()> { // _ => {} // } + if opts.gc.gc_print_stats { + if opts.gc.gc_collect_before_stats { + heap.collect_garbage(|| {}); + } + + let stats = heap.stats(); + let params = heap.params(); + + println!(); + println!("total GC runs: {}", stats.collections_performed); + println!("total bytes swept: {}", stats.bytes_swept); + println!("total bytes still allocated: {}", stats.bytes_allocated); + println!("total GC time: {:?}", stats.total_time_spent); + println!("final GC threshold: {}", params.threshold); + println!(); + } + + if opts.gc.gc_collect_on_exit { + heap.collect_garbage(|| {}); + } + Ok(()) } -fn disassemble_class(opts: Options) -> anyhow::Result<()> { +fn disassemble_class(heap: &mut GcHeap, opts: Options) -> anyhow::Result<()> { let Some(file) = opts.file else { bail!("no class specified for disassembly"); }; @@ -115,9 +170,9 @@ fn disassemble_class(opts: Options) -> anyhow::Result<()> { if let Some(directory) = file.parent() { classpath.push(directory.to_path_buf()); } - let mut universe = Universe::with_classpath(classpath)?; + let mut universe = Universe::with_classpath(heap, classpath)?; - let class = universe.load_class(file_stem)?; + let class = universe.load_class(heap, file_stem)?; let methods: Vec> = if opts.args.is_empty() { class.borrow().methods.values().cloned().collect() diff --git a/som-interpreter-bc/src/method.rs b/som-interpreter-bc/src/method.rs index 3563a3c4..528d3fa8 100644 --- a/som-interpreter-bc/src/method.rs +++ b/som-interpreter-bc/src/method.rs @@ -1,8 +1,8 @@ use std::cell::RefCell; use std::fmt; -use std::rc::Rc; use som_core::bytecode::Bytecode; +use som_gc::{Gc, GcHeap, Trace}; use crate::class::Class; use crate::compiler::Literal; @@ -12,14 +12,14 @@ use crate::interpreter::Interpreter; use crate::primitives::PrimitiveFn; use crate::universe::Universe; use crate::value::Value; -use crate::{SOMRef, SOMWeakRef}; +use crate::SOMRef; #[derive(Clone)] pub struct MethodEnv { pub locals: Vec, pub literals: Vec, pub body: Vec, - pub inline_cache: RefCell)>>>, + pub inline_cache: RefCell)>>>, } /// The kind of a class method. @@ -44,10 +44,38 @@ impl MethodKind { #[derive(Clone)] pub struct Method { pub kind: MethodKind, - pub holder: SOMWeakRef, + pub holder: SOMRef, pub signature: String, } +impl Trace for MethodKind { + #[inline] + fn trace(&self) { + match self { + MethodKind::Defined(env) => env.trace(), + MethodKind::Primitive(_) => {} + MethodKind::NotImplemented(_) => {} + } + } +} + +impl Trace for MethodEnv { + #[inline] + fn trace(&self) { + self.locals.trace(); + self.literals.trace(); + self.inline_cache.trace(); + } +} + +impl Trace for Method { + #[inline] + fn trace(&self) { + self.kind.trace(); + self.holder.trace(); + } +} + impl Method { pub fn class(&self, universe: &Universe) -> SOMRef { if self.is_primitive() { @@ -57,14 +85,6 @@ impl Method { } } - pub fn kind(&self) -> &MethodKind { - &self.kind - } - - pub fn holder(&self) -> &SOMWeakRef { - &self.holder - } - pub fn signature(&self) -> &str { self.signature.as_str() } @@ -75,29 +95,29 @@ impl Method { } pub fn invoke( - self: Rc, + this: Gc, interpreter: &mut Interpreter, + heap: &mut GcHeap, universe: &mut Universe, receiver: Value, mut args: Vec, ) { - match self.kind() { + match &this.kind { MethodKind::Defined(_) => { - let holder = self.holder().upgrade().unwrap(); let kind = FrameKind::Method { - method: self, - holder, + holder: this.holder.clone(), + method: this, self_value: receiver.clone(), }; - let frame = interpreter.push_frame(kind); + let frame = interpreter.push_frame(heap, kind); frame.borrow_mut().args.push(receiver); frame.borrow_mut().args.append(&mut args); } MethodKind::Primitive(func) => { interpreter.stack.push(receiver); interpreter.stack.append(&mut args); - func(interpreter, universe) + func(interpreter, heap, universe) } MethodKind::NotImplemented(_) => todo!(), } @@ -109,8 +129,8 @@ impl fmt::Display for Method { write!( f, "#{}>>#{} = ", - self.holder.upgrade().unwrap().borrow().name(), - self.signature + self.holder.borrow().name(), + self.signature, )?; match &self.kind { MethodKind::Defined(env) => { diff --git a/som-interpreter-bc/src/primitives/array.rs b/som-interpreter-bc/src/primitives/array.rs index 35e02fb9..ede16fbb 100644 --- a/som-interpreter-bc/src/primitives/array.rs +++ b/som-interpreter-bc/src/primitives/array.rs @@ -1,6 +1,7 @@ use std::cell::RefCell; use std::convert::TryFrom; -use std::rc::Rc; + +use som_gc::GcHeap; use crate::interpreter::Interpreter; use crate::primitives::PrimitiveFn; @@ -16,7 +17,7 @@ pub static INSTANCE_PRIMITIVES: &[(&str, PrimitiveFn, bool)] = &[ pub static CLASS_PRIMITIVES: &[(&str, PrimitiveFn, bool)] = &[("new:", self::new, true)]; -fn at(interpreter: &mut Interpreter, _: &mut Universe) { +fn at(interpreter: &mut Interpreter, _: &mut GcHeap, _: &mut Universe) { const SIGNATURE: &str = "Array>>#at:"; expect_args!(SIGNATURE, interpreter, [ @@ -32,7 +33,7 @@ fn at(interpreter: &mut Interpreter, _: &mut Universe) { interpreter.stack.push(value) } -fn at_put(interpreter: &mut Interpreter, _: &mut Universe) { +fn at_put(interpreter: &mut Interpreter, _: &mut GcHeap, _: &mut Universe) { const SIGNATURE: &str = "Array>>#at:put:"; expect_args!(SIGNATURE, interpreter, [ @@ -51,7 +52,7 @@ fn at_put(interpreter: &mut Interpreter, _: &mut Universe) { interpreter.stack.push(Value::Array(values)) } -fn length(interpreter: &mut Interpreter, _: &mut Universe) { +fn length(interpreter: &mut Interpreter, _: &mut GcHeap, _: &mut Universe) { const SIGNATURE: &str = "Array>>#length"; expect_args!(SIGNATURE, interpreter, [ @@ -65,7 +66,7 @@ fn length(interpreter: &mut Interpreter, _: &mut Universe) { } } -fn new(interpreter: &mut Interpreter, _: &mut Universe) { +fn new(interpreter: &mut Interpreter, heap: &mut GcHeap, _: &mut Universe) { const SIGNATURE: &str = "Array>>#new:"; expect_args!(SIGNATURE, interpreter, [ @@ -74,12 +75,9 @@ fn new(interpreter: &mut Interpreter, _: &mut Universe) { ]); match usize::try_from(count) { - Ok(length) => interpreter - .stack - .push(Value::Array(Rc::new(RefCell::new(vec![ - Value::Nil; - length - ])))), + Ok(length) => interpreter.stack.push(Value::Array( + heap.allocate(RefCell::new(vec![Value::Nil; length])), + )), Err(err) => panic!("'{}': {}", SIGNATURE, err), } } diff --git a/som-interpreter-bc/src/primitives/blocks.rs b/som-interpreter-bc/src/primitives/blocks.rs index ddd3dc2f..0a3b7e86 100644 --- a/som-interpreter-bc/src/primitives/blocks.rs +++ b/som-interpreter-bc/src/primitives/blocks.rs @@ -1,3 +1,5 @@ +use som_gc::GcHeap; + use crate::frame::FrameKind; use crate::interpreter::Interpreter; use crate::primitives::PrimitiveFn; @@ -15,7 +17,7 @@ pub mod block1 { ]; pub static CLASS_PRIMITIVES: &[(&str, PrimitiveFn, bool)] = &[]; - fn value(interpreter: &mut Interpreter, _: &mut Universe) { + fn value(interpreter: &mut Interpreter, heap: &mut GcHeap, _: &mut Universe) { const SIGNATURE: &str = "Block1>>#value"; expect_args!(SIGNATURE, interpreter, [ @@ -26,10 +28,10 @@ pub mod block1 { block: block.clone(), }; - interpreter.push_frame(kind); + interpreter.push_frame(heap, kind); } - fn restart(interpreter: &mut Interpreter, _: &mut Universe) { + fn restart(interpreter: &mut Interpreter, _: &mut GcHeap, _: &mut Universe) { const SIGNATURE: &str = "Block>>#restart"; expect_args!(SIGNATURE, interpreter, [Value::Block(_)]); @@ -62,7 +64,7 @@ pub mod block2 { pub static INSTANCE_PRIMITIVES: &[(&str, PrimitiveFn, bool)] = &[("value:", self::value, true)]; pub static CLASS_PRIMITIVES: &[(&str, PrimitiveFn, bool)] = &[]; - fn value(interpreter: &mut Interpreter, _: &mut Universe) { + fn value(interpreter: &mut Interpreter, heap: &mut GcHeap, _: &mut Universe) { const SIGNATURE: &str = "Block2>>#value:"; expect_args!(SIGNATURE, interpreter, [ @@ -74,7 +76,7 @@ pub mod block2 { block: block.clone(), }; - let frame = interpreter.push_frame(kind); + let frame = interpreter.push_frame(heap, kind); frame.borrow_mut().args.push(argument); } @@ -103,7 +105,7 @@ pub mod block3 { &[("value:with:", self::value_with, true)]; pub static CLASS_PRIMITIVES: &[(&str, PrimitiveFn, bool)] = &[]; - fn value_with(interpreter: &mut Interpreter, _: &mut Universe) { + fn value_with(interpreter: &mut Interpreter, heap: &mut GcHeap, _: &mut Universe) { const SIGNATURE: &str = "Block3>>#value:with:"; expect_args!(SIGNATURE, interpreter, [ @@ -116,7 +118,7 @@ pub mod block3 { block: block.clone(), }; - let frame = interpreter.push_frame(kind); + let frame = interpreter.push_frame(heap, kind); frame.borrow_mut().args.push(argument1); frame.borrow_mut().args.push(argument2); } diff --git a/som-interpreter-bc/src/primitives/class.rs b/som-interpreter-bc/src/primitives/class.rs index ab951736..b18eb2ad 100644 --- a/som-interpreter-bc/src/primitives/class.rs +++ b/som-interpreter-bc/src/primitives/class.rs @@ -1,5 +1,6 @@ use std::cell::RefCell; -use std::rc::Rc; + +use som_gc::GcHeap; use crate::instance::Instance; use crate::interpreter::Interpreter; @@ -17,7 +18,7 @@ pub static INSTANCE_PRIMITIVES: &[(&str, PrimitiveFn, bool)] = &[ ]; pub static CLASS_PRIMITIVES: &[(&str, PrimitiveFn, bool)] = &[]; -fn superclass(interpreter: &mut Interpreter, _: &mut Universe) { +fn superclass(interpreter: &mut Interpreter, _: &mut GcHeap, _: &mut Universe) { const SIGNATURE: &str = "Class>>#superclass"; expect_args!(SIGNATURE, interpreter, [ @@ -30,7 +31,7 @@ fn superclass(interpreter: &mut Interpreter, _: &mut Universe) { .push(super_class.map(Value::Class).unwrap_or(Value::Nil)); } -fn new(interpreter: &mut Interpreter, _: &mut Universe) { +fn new(interpreter: &mut Interpreter, heap: &mut GcHeap, _: &mut Universe) { const SIGNATURE: &str = "Class>>#new"; expect_args!(SIGNATURE, interpreter, [ @@ -38,11 +39,11 @@ fn new(interpreter: &mut Interpreter, _: &mut Universe) { ]); let instance = Instance::from_class(class); - let instance = Rc::new(RefCell::new(instance)); + let instance = heap.allocate(RefCell::new(instance)); interpreter.stack.push(Value::Instance(instance)); } -fn name(interpreter: &mut Interpreter, universe: &mut Universe) { +fn name(interpreter: &mut Interpreter, _: &mut GcHeap, universe: &mut Universe) { const SIGNATURE: &str = "Class>>#name"; expect_args!(SIGNATURE, interpreter, [ @@ -53,7 +54,7 @@ fn name(interpreter: &mut Interpreter, universe: &mut Universe) { interpreter.stack.push(Value::Symbol(sym)); } -fn methods(interpreter: &mut Interpreter, _: &mut Universe) { +fn methods(interpreter: &mut Interpreter, heap: &mut GcHeap, _: &mut Universe) { const SIGNATURE: &str = "Class>>#methods"; expect_args!(SIGNATURE, interpreter, [ @@ -69,25 +70,27 @@ fn methods(interpreter: &mut Interpreter, _: &mut Universe) { interpreter .stack - .push(Value::Array(Rc::new(RefCell::new(methods)))); + .push(Value::Array(heap.allocate(RefCell::new(methods)))); } -fn fields(interpreter: &mut Interpreter, _: &mut Universe) { +fn fields(interpreter: &mut Interpreter, heap: &mut GcHeap, _: &mut Universe) { const SIGNATURE: &str = "Class>>#fields"; expect_args!(SIGNATURE, interpreter, [ Value::Class(class) => class, ]); - interpreter.stack.push(Value::Array(Rc::new(RefCell::new( - class - .borrow() - .locals - .keys() - .copied() - .map(Value::Symbol) - .collect(), - )))); + interpreter.stack.push(Value::Array( + heap.allocate(RefCell::new( + class + .borrow() + .locals + .keys() + .copied() + .map(Value::Symbol) + .collect(), + )), + )); } /// Search for an instance primitive matching the given signature. diff --git a/som-interpreter-bc/src/primitives/double.rs b/som-interpreter-bc/src/primitives/double.rs index cf6b4650..a55c50a3 100644 --- a/som-interpreter-bc/src/primitives/double.rs +++ b/som-interpreter-bc/src/primitives/double.rs @@ -1,7 +1,7 @@ -use std::rc::Rc; - use num_traits::ToPrimitive; +use som_gc::GcHeap; + use crate::interpreter::Interpreter; use crate::primitives::PrimitiveFn; use crate::universe::Universe; @@ -50,7 +50,7 @@ macro_rules! promote { }; } -fn from_string(interpreter: &mut Interpreter, _: &mut Universe) { +fn from_string(interpreter: &mut Interpreter, _: &mut GcHeap, _: &mut Universe) { const SIGNATURE: &str = "Double>>#fromString:"; expect_args!(SIGNATURE, interpreter, [ @@ -64,7 +64,7 @@ fn from_string(interpreter: &mut Interpreter, _: &mut Universe) { } } -fn as_string(interpreter: &mut Interpreter, _: &mut Universe) { +fn as_string(interpreter: &mut Interpreter, heap: &mut GcHeap, _: &mut Universe) { const SIGNATURE: &str = "Double>>#asString"; expect_args!(SIGNATURE, interpreter, [ @@ -75,10 +75,10 @@ fn as_string(interpreter: &mut Interpreter, _: &mut Universe) { interpreter .stack - .push(Value::String(Rc::new(value.to_string()))); + .push(Value::String(heap.allocate(value.to_string()))); } -fn as_integer(interpreter: &mut Interpreter, _: &mut Universe) { +fn as_integer(interpreter: &mut Interpreter, _: &mut GcHeap, _: &mut Universe) { const SIGNATURE: &str = "Double>>#asInteger"; expect_args!(SIGNATURE, interpreter, [ @@ -88,7 +88,7 @@ fn as_integer(interpreter: &mut Interpreter, _: &mut Universe) { interpreter.stack.push(Value::Integer(value.trunc() as i64)); } -fn sqrt(interpreter: &mut Interpreter, _: &mut Universe) { +fn sqrt(interpreter: &mut Interpreter, _: &mut GcHeap, _: &mut Universe) { const SIGNATURE: &str = "Double>>#sqrt"; expect_args!(SIGNATURE, interpreter, [ @@ -100,7 +100,7 @@ fn sqrt(interpreter: &mut Interpreter, _: &mut Universe) { interpreter.stack.push(Value::Double(value.sqrt())); } -fn round(interpreter: &mut Interpreter, _: &mut Universe) { +fn round(interpreter: &mut Interpreter, _: &mut GcHeap, _: &mut Universe) { const SIGNATURE: &str = "Double>>#round"; expect_args!(SIGNATURE, interpreter, [ @@ -112,7 +112,7 @@ fn round(interpreter: &mut Interpreter, _: &mut Universe) { interpreter.stack.push(Value::Double(value.round())); } -fn cos(interpreter: &mut Interpreter, _: &mut Universe) { +fn cos(interpreter: &mut Interpreter, _: &mut GcHeap, _: &mut Universe) { const SIGNATURE: &str = "Double>>#cos"; expect_args!(SIGNATURE, interpreter, [ @@ -124,7 +124,7 @@ fn cos(interpreter: &mut Interpreter, _: &mut Universe) { interpreter.stack.push(Value::Double(value.cos())); } -fn sin(interpreter: &mut Interpreter, _: &mut Universe) { +fn sin(interpreter: &mut Interpreter, _: &mut GcHeap, _: &mut Universe) { const SIGNATURE: &str = "Double>>#sin"; expect_args!(SIGNATURE, interpreter, [ @@ -136,7 +136,7 @@ fn sin(interpreter: &mut Interpreter, _: &mut Universe) { interpreter.stack.push(Value::Double(value.sin())); } -fn eq(interpreter: &mut Interpreter, _: &mut Universe) { +fn eq(interpreter: &mut Interpreter, _: &mut GcHeap, _: &mut Universe) { const SIGNATURE: &str = "Double>>#="; expect_args!(SIGNATURE, interpreter, [ @@ -149,7 +149,7 @@ fn eq(interpreter: &mut Interpreter, _: &mut Universe) { interpreter.stack.push(Value::Boolean(a == b)); } -fn lt(interpreter: &mut Interpreter, _: &mut Universe) { +fn lt(interpreter: &mut Interpreter, _: &mut GcHeap, _: &mut Universe) { const SIGNATURE: &str = "Double>>#<"; expect_args!(SIGNATURE, interpreter, [ @@ -163,7 +163,7 @@ fn lt(interpreter: &mut Interpreter, _: &mut Universe) { interpreter.stack.push(Value::Boolean(a < b)); } -fn plus(interpreter: &mut Interpreter, _: &mut Universe) { +fn plus(interpreter: &mut Interpreter, _: &mut GcHeap, _: &mut Universe) { const SIGNATURE: &str = "Double>>#+"; expect_args!(SIGNATURE, interpreter, [ @@ -177,7 +177,7 @@ fn plus(interpreter: &mut Interpreter, _: &mut Universe) { interpreter.stack.push(Value::Double(a + b)); } -fn minus(interpreter: &mut Interpreter, _: &mut Universe) { +fn minus(interpreter: &mut Interpreter, _: &mut GcHeap, _: &mut Universe) { const SIGNATURE: &str = "Double>>#-"; expect_args!(SIGNATURE, interpreter, [ @@ -191,7 +191,7 @@ fn minus(interpreter: &mut Interpreter, _: &mut Universe) { interpreter.stack.push(Value::Double(a - b)); } -fn times(interpreter: &mut Interpreter, _: &mut Universe) { +fn times(interpreter: &mut Interpreter, _: &mut GcHeap, _: &mut Universe) { const SIGNATURE: &str = "Double>>#*"; expect_args!(SIGNATURE, interpreter, [ @@ -205,7 +205,7 @@ fn times(interpreter: &mut Interpreter, _: &mut Universe) { interpreter.stack.push(Value::Double(a * b)); } -fn divide(interpreter: &mut Interpreter, _: &mut Universe) { +fn divide(interpreter: &mut Interpreter, _: &mut GcHeap, _: &mut Universe) { const SIGNATURE: &str = "Double>>#//"; expect_args!(SIGNATURE, interpreter, [ @@ -219,7 +219,7 @@ fn divide(interpreter: &mut Interpreter, _: &mut Universe) { interpreter.stack.push(Value::Double(a / b)); } -fn modulo(interpreter: &mut Interpreter, _: &mut Universe) { +fn modulo(interpreter: &mut Interpreter, _: &mut GcHeap, _: &mut Universe) { const SIGNATURE: &str = "Double>>#%"; expect_args!(SIGNATURE, interpreter, [ @@ -233,7 +233,7 @@ fn modulo(interpreter: &mut Interpreter, _: &mut Universe) { interpreter.stack.push(Value::Double(a % b)); } -fn positive_infinity(interpreter: &mut Interpreter, _: &mut Universe) { +fn positive_infinity(interpreter: &mut Interpreter, _: &mut GcHeap, _: &mut Universe) { const SIGNATURE: &str = "Double>>#positiveInfinity"; expect_args!(SIGNATURE, interpreter, [_]); diff --git a/som-interpreter-bc/src/primitives/integer.rs b/som-interpreter-bc/src/primitives/integer.rs index 89765955..02155bbc 100644 --- a/som-interpreter-bc/src/primitives/integer.rs +++ b/som-interpreter-bc/src/primitives/integer.rs @@ -1,10 +1,10 @@ -use std::rc::Rc; - use num_bigint::{BigInt, Sign}; use num_traits::ToPrimitive; use rand::distributions::Uniform; use rand::Rng; +use som_gc::GcHeap; + use crate::interpreter::Interpreter; use crate::primitives::PrimitiveFn; use crate::universe::Universe; @@ -45,7 +45,7 @@ macro_rules! demote { }}; } -fn from_string(interpreter: &mut Interpreter, universe: &mut Universe) { +fn from_string(interpreter: &mut Interpreter, _: &mut GcHeap, universe: &mut Universe) { const SIGNATURE: &str = "Integer>>#fromString:"; expect_args!(SIGNATURE, interpreter, [ @@ -71,7 +71,7 @@ fn from_string(interpreter: &mut Interpreter, universe: &mut Universe) { } } -fn as_string(interpreter: &mut Interpreter, _: &mut Universe) { +fn as_string(interpreter: &mut Interpreter, heap: &mut GcHeap, _: &mut Universe) { const SIGNATURE: &str = "Integer>>#asString"; expect_args!(SIGNATURE, interpreter, [ @@ -84,13 +84,10 @@ fn as_string(interpreter: &mut Interpreter, _: &mut Universe) { _ => panic!("'{}': wrong types", SIGNATURE), }; - { - interpreter.stack.push(Value::String(Rc::new(value))); - return; - } + interpreter.stack.push(Value::String(heap.allocate(value))); } -fn as_double(interpreter: &mut Interpreter, _: &mut Universe) { +fn as_double(interpreter: &mut Interpreter, _: &mut GcHeap, _: &mut Universe) { const SIGNATURE: &str = "Integer>>#asDouble"; expect_args!(SIGNATURE, interpreter, [ @@ -112,7 +109,7 @@ fn as_double(interpreter: &mut Interpreter, _: &mut Universe) { interpreter.stack.push(value); } -fn at_random(interpreter: &mut Interpreter, _: &mut Universe) { +fn at_random(interpreter: &mut Interpreter, _: &mut GcHeap, _: &mut Universe) { const SIGNATURE: &str = "Integer>>#atRandom"; expect_args!(SIGNATURE, interpreter, [ @@ -138,7 +135,7 @@ fn at_random(interpreter: &mut Interpreter, _: &mut Universe) { } } -fn as_32bit_signed_value(interpreter: &mut Interpreter, _: &mut Universe) { +fn as_32bit_signed_value(interpreter: &mut Interpreter, _: &mut GcHeap, _: &mut Universe) { const SIGNATURE: &str = "Integer>>#as32BitSignedValue"; expect_args!(SIGNATURE, interpreter, [ @@ -160,7 +157,7 @@ fn as_32bit_signed_value(interpreter: &mut Interpreter, _: &mut Universe) { } } -fn as_32bit_unsigned_value(interpreter: &mut Interpreter, _: &mut Universe) { +fn as_32bit_unsigned_value(interpreter: &mut Interpreter, _: &mut GcHeap, _: &mut Universe) { const SIGNATURE: &str = "Integer>>#as32BitUnsignedValue"; expect_args!(SIGNATURE, interpreter, [ @@ -182,7 +179,7 @@ fn as_32bit_unsigned_value(interpreter: &mut Interpreter, _: &mut Universe) { } } -fn plus(interpreter: &mut Interpreter, _: &mut Universe) { +fn plus(interpreter: &mut Interpreter, _: &mut GcHeap, _: &mut Universe) { const SIGNATURE: &str = "Integer>>#+"; expect_args!(SIGNATURE, interpreter, [ @@ -218,7 +215,7 @@ fn plus(interpreter: &mut Interpreter, _: &mut Universe) { interpreter.stack.push(value); } -fn minus(interpreter: &mut Interpreter, _: &mut Universe) { +fn minus(interpreter: &mut Interpreter, _: &mut GcHeap, _: &mut Universe) { const SIGNATURE: &str = "Integer>>#-"; expect_args!(SIGNATURE, interpreter, [ @@ -262,7 +259,7 @@ fn minus(interpreter: &mut Interpreter, _: &mut Universe) { interpreter.stack.push(value); } -fn times(interpreter: &mut Interpreter, _: &mut Universe) { +fn times(interpreter: &mut Interpreter, _: &mut GcHeap, _: &mut Universe) { const SIGNATURE: &str = "Integer>>#*"; expect_args!(SIGNATURE, interpreter, [ @@ -298,7 +295,7 @@ fn times(interpreter: &mut Interpreter, _: &mut Universe) { interpreter.stack.push(value); } -fn divide(interpreter: &mut Interpreter, _: &mut Universe) { +fn divide(interpreter: &mut Interpreter, _: &mut GcHeap, _: &mut Universe) { const SIGNATURE: &str = "Integer>>#/"; expect_args!(SIGNATURE, interpreter, [ @@ -342,7 +339,7 @@ fn divide(interpreter: &mut Interpreter, _: &mut Universe) { interpreter.stack.push(value); } -fn divide_float(interpreter: &mut Interpreter, _: &mut Universe) { +fn divide_float(interpreter: &mut Interpreter, _: &mut GcHeap, _: &mut Universe) { const SIGNATURE: &str = "Integer>>#//"; expect_args!(SIGNATURE, interpreter, [ @@ -379,7 +376,7 @@ fn divide_float(interpreter: &mut Interpreter, _: &mut Universe) { interpreter.stack.push(Value::Double(a / b)); } -fn modulo(interpreter: &mut Interpreter, _: &mut Universe) { +fn modulo(interpreter: &mut Interpreter, _: &mut GcHeap, _: &mut Universe) { const SIGNATURE: &str = "Integer>>#%"; expect_args!(SIGNATURE, interpreter, [ @@ -395,7 +392,7 @@ fn modulo(interpreter: &mut Interpreter, _: &mut Universe) { } } -fn remainder(interpreter: &mut Interpreter, _: &mut Universe) { +fn remainder(interpreter: &mut Interpreter, _: &mut GcHeap, _: &mut Universe) { const SIGNATURE: &str = "Integer>>#rem:"; expect_args!(SIGNATURE, interpreter, [ @@ -411,7 +408,7 @@ fn remainder(interpreter: &mut Interpreter, _: &mut Universe) { } } -fn sqrt(interpreter: &mut Interpreter, _: &mut Universe) { +fn sqrt(interpreter: &mut Interpreter, _: &mut GcHeap, _: &mut Universe) { const SIGNATURE: &str = "Integer>>#sqrt"; expect_args!(SIGNATURE, interpreter, [ @@ -436,7 +433,7 @@ fn sqrt(interpreter: &mut Interpreter, _: &mut Universe) { interpreter.stack.push(value); } -fn bitand(interpreter: &mut Interpreter, _: &mut Universe) { +fn bitand(interpreter: &mut Interpreter, _: &mut GcHeap, _: &mut Universe) { const SIGNATURE: &str = "Integer>>#&"; expect_args!(SIGNATURE, interpreter, [ @@ -456,7 +453,7 @@ fn bitand(interpreter: &mut Interpreter, _: &mut Universe) { interpreter.stack.push(value); } -fn bitxor(interpreter: &mut Interpreter, _: &mut Universe) { +fn bitxor(interpreter: &mut Interpreter, _: &mut GcHeap, _: &mut Universe) { const SIGNATURE: &str = "Integer>>#bitXor:"; expect_args!(SIGNATURE, interpreter, [ @@ -476,7 +473,7 @@ fn bitxor(interpreter: &mut Interpreter, _: &mut Universe) { interpreter.stack.push(value); } -fn lt(interpreter: &mut Interpreter, _: &mut Universe) { +fn lt(interpreter: &mut Interpreter, _: &mut GcHeap, _: &mut Universe) { const SIGNATURE: &str = "Integer>>#<"; expect_args!(SIGNATURE, interpreter, [ @@ -498,7 +495,7 @@ fn lt(interpreter: &mut Interpreter, _: &mut Universe) { interpreter.stack.push(value); } -fn eq(interpreter: &mut Interpreter, _: &mut Universe) { +fn eq(interpreter: &mut Interpreter, _: &mut GcHeap, _: &mut Universe) { const SIGNATURE: &str = "Integer>>#="; expect_args!(SIGNATURE, interpreter, [ @@ -518,7 +515,7 @@ fn eq(interpreter: &mut Interpreter, _: &mut Universe) { interpreter.stack.push(value); } -fn shift_left(interpreter: &mut Interpreter, _: &mut Universe) { +fn shift_left(interpreter: &mut Interpreter, _: &mut GcHeap, _: &mut Universe) { const SIGNATURE: &str = "Integer>>#<<"; expect_args!(SIGNATURE, interpreter, [ @@ -538,7 +535,7 @@ fn shift_left(interpreter: &mut Interpreter, _: &mut Universe) { interpreter.stack.push(value); } -fn shift_right(interpreter: &mut Interpreter, _: &mut Universe) { +fn shift_right(interpreter: &mut Interpreter, _: &mut GcHeap, _: &mut Universe) { const SIGNATURE: &str = "Integer>>#>>"; expect_args!(SIGNATURE, interpreter, [ diff --git a/som-interpreter-bc/src/primitives/method.rs b/som-interpreter-bc/src/primitives/method.rs index 80485104..e82e5b3e 100644 --- a/som-interpreter-bc/src/primitives/method.rs +++ b/som-interpreter-bc/src/primitives/method.rs @@ -1,4 +1,7 @@ +use som_gc::GcHeap; + use crate::interpreter::Interpreter; +use crate::method::Method; use crate::primitives::PrimitiveFn; use crate::universe::Universe; use crate::value::Value; @@ -11,20 +14,19 @@ pub static INSTANCE_PRIMITIVES: &[(&str, PrimitiveFn, bool)] = &[ ]; pub static CLASS_PRIMITIVES: &[(&str, PrimitiveFn, bool)] = &[]; -fn holder(interpreter: &mut Interpreter, _: &mut Universe) { +fn holder(interpreter: &mut Interpreter, _: &mut GcHeap, _: &mut Universe) { const SIGNATURE: &str = "Method>>#holder"; expect_args!(SIGNATURE, interpreter, [ Value::Invokable(invokable) => invokable, ]); - match invokable.holder().upgrade() { - Some(holder) => interpreter.stack.push(Value::Class(holder)), - None => panic!("'{}': method sholder has been collected", SIGNATURE), - } + interpreter + .stack + .push(Value::Class(invokable.holder.clone())); } -fn signature(interpreter: &mut Interpreter, universe: &mut Universe) { +fn signature(interpreter: &mut Interpreter, _: &mut GcHeap, universe: &mut Universe) { const SIGNATURE: &str = "Method>>#signature"; expect_args!(SIGNATURE, interpreter, [ @@ -35,7 +37,7 @@ fn signature(interpreter: &mut Interpreter, universe: &mut Universe) { interpreter.stack.push(Value::Symbol(sym)) } -fn invoke_on_with(interpreter: &mut Interpreter, universe: &mut Universe) { +fn invoke_on_with(interpreter: &mut Interpreter, heap: &mut GcHeap, universe: &mut Universe) { const SIGNATURE: &str = "Method>>#invokeOn:with:"; expect_args!(SIGNATURE, interpreter, [ @@ -45,7 +47,8 @@ fn invoke_on_with(interpreter: &mut Interpreter, universe: &mut Universe) { ]); let args = args.borrow().iter().cloned().collect(); - invokable.invoke(interpreter, universe, receiver, args); + + Method::invoke(invokable, interpreter, heap, universe, receiver, args); } /// Search for an instance primitive matching the given signature. diff --git a/som-interpreter-bc/src/primitives/mod.rs b/som-interpreter-bc/src/primitives/mod.rs index 4c26bdab..a0250110 100644 --- a/som-interpreter-bc/src/primitives/mod.rs +++ b/som-interpreter-bc/src/primitives/mod.rs @@ -19,13 +19,16 @@ pub mod symbol; /// Primitives for the **System** class. pub mod system; +use som_gc::GcHeap; + pub use self::blocks::{block1, block2, block3}; use crate::interpreter::Interpreter; use crate::universe::Universe; /// A interpreter primitive (just a bare function pointer). -pub type PrimitiveFn = fn(interpreter: &mut Interpreter, universe: &mut Universe); +pub type PrimitiveFn = + fn(interpreter: &mut Interpreter, heap: &mut GcHeap, universe: &mut Universe); #[macro_export] macro_rules! reverse { diff --git a/som-interpreter-bc/src/primitives/object.rs b/som-interpreter-bc/src/primitives/object.rs index c1f5c2e5..8664d422 100644 --- a/som-interpreter-bc/src/primitives/object.rs +++ b/som-interpreter-bc/src/primitives/object.rs @@ -2,7 +2,10 @@ use std::collections::hash_map::DefaultHasher; use std::convert::TryFrom; use std::hash::{Hash, Hasher}; +use som_gc::GcHeap; + use crate::interpreter::Interpreter; +use crate::method::Method; use crate::primitives::PrimitiveFn; use crate::universe::Universe; use crate::value::Value; @@ -26,7 +29,7 @@ pub static INSTANCE_PRIMITIVES: &[(&str, PrimitiveFn, bool)] = &[ ]; pub static CLASS_PRIMITIVES: &[(&str, PrimitiveFn, bool)] = &[]; -fn class(interpreter: &mut Interpreter, universe: &mut Universe) { +fn class(interpreter: &mut Interpreter, _: &mut GcHeap, universe: &mut Universe) { const SIGNATURE: &'static str = "Object>>#class"; expect_args!(SIGNATURE, interpreter, [ @@ -36,7 +39,7 @@ fn class(interpreter: &mut Interpreter, universe: &mut Universe) { interpreter.stack.push(Value::Class(object.class(universe))); } -fn object_size(interpreter: &mut Interpreter, _: &mut Universe) { +fn object_size(interpreter: &mut Interpreter, _: &mut GcHeap, _: &mut Universe) { const _: &'static str = "Object>>#objectSize"; interpreter @@ -44,7 +47,7 @@ fn object_size(interpreter: &mut Interpreter, _: &mut Universe) { .push(Value::Integer(std::mem::size_of::() as i64)); } -fn hashcode(interpreter: &mut Interpreter, _: &mut Universe) { +fn hashcode(interpreter: &mut Interpreter, _: &mut GcHeap, _: &mut Universe) { const SIGNATURE: &'static str = "Object>>#hashcode"; expect_args!(SIGNATURE, interpreter, [ @@ -58,7 +61,7 @@ fn hashcode(interpreter: &mut Interpreter, _: &mut Universe) { interpreter.stack.push(Value::Integer(hash)); } -fn eq(interpreter: &mut Interpreter, _: &mut Universe) { +fn eq(interpreter: &mut Interpreter, _: &mut GcHeap, _: &mut Universe) { const SIGNATURE: &'static str = "Object>>#=="; expect_args!(SIGNATURE, interpreter, [ @@ -69,7 +72,7 @@ fn eq(interpreter: &mut Interpreter, _: &mut Universe) { interpreter.stack.push(Value::Boolean(a == b)); } -fn perform(interpreter: &mut Interpreter, universe: &mut Universe) { +fn perform(interpreter: &mut Interpreter, heap: &mut GcHeap, universe: &mut Universe) { const SIGNATURE: &'static str = "Object>>#perform:"; expect_args!(SIGNATURE, interpreter, [ @@ -83,11 +86,11 @@ fn perform(interpreter: &mut Interpreter, universe: &mut Universe) { let method = object.lookup_method(universe, sym); match method { - Some(invokable) => invokable.invoke(interpreter, universe, object, vec![]), + Some(invokable) => Method::invoke(invokable, interpreter, heap, universe, object, vec![]), None => { let signature = signature.to_string(); universe - .does_not_understand(interpreter, object.clone(), sym, vec![object.clone()]) + .does_not_understand(interpreter, heap, object.clone(), sym, vec![object.clone()]) .unwrap_or_else(|| { panic!( "'{}': method '{}' not found for '{}'", @@ -101,7 +104,11 @@ fn perform(interpreter: &mut Interpreter, universe: &mut Universe) { } } -fn perform_with_arguments(interpreter: &mut Interpreter, universe: &mut Universe) { +fn perform_with_arguments( + interpreter: &mut Interpreter, + heap: &mut GcHeap, + universe: &mut Universe, +) { const SIGNATURE: &'static str = "Object>>#perform:withArguments:"; expect_args!(SIGNATURE, interpreter, [ @@ -116,7 +123,7 @@ fn perform_with_arguments(interpreter: &mut Interpreter, universe: &mut Universe match method { Some(invokable) => { let args = arr.borrow().iter().cloned().collect(); - invokable.invoke(interpreter, universe, object, args) + Method::invoke(invokable, interpreter, heap, universe, object, args); } None => { let signature = signature.to_string(); @@ -124,7 +131,7 @@ fn perform_with_arguments(interpreter: &mut Interpreter, universe: &mut Universe .chain(arr.borrow().iter().cloned()) .collect(); universe - .does_not_understand(interpreter, object.clone(), sym, args) + .does_not_understand(interpreter, heap, object.clone(), sym, args) .unwrap_or_else(|| { panic!( "'{}': method '{}' not found for '{}'", @@ -138,7 +145,11 @@ fn perform_with_arguments(interpreter: &mut Interpreter, universe: &mut Universe } } -fn perform_in_super_class(interpreter: &mut Interpreter, universe: &mut Universe) { +fn perform_in_super_class( + interpreter: &mut Interpreter, + heap: &mut GcHeap, + universe: &mut Universe, +) { const SIGNATURE: &'static str = "Object>>#perform:inSuperclass:"; expect_args!(SIGNATURE, interpreter, [ @@ -151,12 +162,12 @@ fn perform_in_super_class(interpreter: &mut Interpreter, universe: &mut Universe let method = class.borrow().lookup_method(sym); match method { - Some(invokable) => invokable.invoke(interpreter, universe, object, vec![]), + Some(invokable) => Method::invoke(invokable, interpreter, heap, universe, object, vec![]), None => { let signature = signature.to_string(); let args = vec![object.clone()]; universe - .does_not_understand(interpreter, Value::Class(class), sym, args) + .does_not_understand(interpreter, heap, Value::Class(class), sym, args) .unwrap_or_else(|| { panic!( "'{}': method '{}' not found for '{}'", @@ -170,7 +181,11 @@ fn perform_in_super_class(interpreter: &mut Interpreter, universe: &mut Universe } } -fn perform_with_arguments_in_super_class(interpreter: &mut Interpreter, universe: &mut Universe) { +fn perform_with_arguments_in_super_class( + interpreter: &mut Interpreter, + heap: &mut GcHeap, + universe: &mut Universe, +) { const SIGNATURE: &'static str = "Object>>#perform:withArguments:inSuperclass:"; expect_args!(SIGNATURE, interpreter, [ @@ -186,7 +201,7 @@ fn perform_with_arguments_in_super_class(interpreter: &mut Interpreter, universe match method { Some(invokable) => { let args = arr.borrow().iter().cloned().collect(); - invokable.invoke(interpreter, universe, object, args) + Method::invoke(invokable, interpreter, heap, universe, object, args) } None => { let args = std::iter::once(object.clone()) @@ -194,7 +209,7 @@ fn perform_with_arguments_in_super_class(interpreter: &mut Interpreter, universe .collect(); let signature = signature.to_string(); universe - .does_not_understand(interpreter, Value::Class(class), sym, args) + .does_not_understand(interpreter, heap, Value::Class(class), sym, args) .unwrap_or_else(|| { panic!( "'{}': method '{}' not found for '{}'", @@ -208,7 +223,7 @@ fn perform_with_arguments_in_super_class(interpreter: &mut Interpreter, universe } } -fn inst_var_at(interpreter: &mut Interpreter, _: &mut Universe) { +fn inst_var_at(interpreter: &mut Interpreter, _: &mut GcHeap, _: &mut Universe) { const SIGNATURE: &'static str = "Object>>#instVarAt:"; expect_args!(SIGNATURE, interpreter, [ @@ -226,7 +241,7 @@ fn inst_var_at(interpreter: &mut Interpreter, _: &mut Universe) { interpreter.stack.push(local); } -fn inst_var_at_put(interpreter: &mut Interpreter, _: &mut Universe) { +fn inst_var_at_put(interpreter: &mut Interpreter, _: &mut GcHeap, _: &mut Universe) { const SIGNATURE: &'static str = "Object>>#instVarAt:put:"; expect_args!(SIGNATURE, interpreter, [ diff --git a/som-interpreter-bc/src/primitives/string.rs b/som-interpreter-bc/src/primitives/string.rs index 3e745470..e2ea41b9 100644 --- a/som-interpreter-bc/src/primitives/string.rs +++ b/som-interpreter-bc/src/primitives/string.rs @@ -1,7 +1,8 @@ use std::collections::hash_map::DefaultHasher; use std::convert::TryFrom; use std::hash::Hasher; -use std::rc::Rc; + +use som_gc::GcHeap; use crate::interpreter::Interpreter; use crate::primitives::PrimitiveFn; @@ -22,7 +23,7 @@ pub static INSTANCE_PRIMITIVES: &[(&str, PrimitiveFn, bool)] = &[ ]; pub static CLASS_PRIMITIVES: &[(&str, PrimitiveFn, bool)] = &[]; -fn length(interpreter: &mut Interpreter, universe: &mut Universe) { +fn length(interpreter: &mut Interpreter, _: &mut GcHeap, universe: &mut Universe) { const SIGNATURE: &str = "String>>#length"; expect_args!(SIGNATURE, interpreter, [ @@ -41,7 +42,7 @@ fn length(interpreter: &mut Interpreter, universe: &mut Universe) { } } -fn hashcode(interpreter: &mut Interpreter, universe: &mut Universe) { +fn hashcode(interpreter: &mut Interpreter, _: &mut GcHeap, universe: &mut Universe) { const SIGNATURE: &str = "String>>#hashcode"; expect_args!(SIGNATURE, interpreter, [ @@ -68,7 +69,7 @@ fn hashcode(interpreter: &mut Interpreter, universe: &mut Universe) { .push(Value::Integer((hasher.finish() as i64).abs())) } -fn is_letters(interpreter: &mut Interpreter, universe: &mut Universe) { +fn is_letters(interpreter: &mut Interpreter, _: &mut GcHeap, universe: &mut Universe) { const SIGNATURE: &str = "String>>#isLetters"; expect_args!(SIGNATURE, interpreter, [ @@ -86,7 +87,7 @@ fn is_letters(interpreter: &mut Interpreter, universe: &mut Universe) { )) } -fn is_digits(interpreter: &mut Interpreter, universe: &mut Universe) { +fn is_digits(interpreter: &mut Interpreter, _: &mut GcHeap, universe: &mut Universe) { const SIGNATURE: &str = "String>>#isDigits"; expect_args!(SIGNATURE, interpreter, [ @@ -104,7 +105,7 @@ fn is_digits(interpreter: &mut Interpreter, universe: &mut Universe) { )) } -fn is_whitespace(interpreter: &mut Interpreter, universe: &mut Universe) { +fn is_whitespace(interpreter: &mut Interpreter, _: &mut GcHeap, universe: &mut Universe) { const SIGNATURE: &str = "String>>#isWhiteSpace"; expect_args!(SIGNATURE, interpreter, [ @@ -122,7 +123,7 @@ fn is_whitespace(interpreter: &mut Interpreter, universe: &mut Universe) { )) } -fn concatenate(interpreter: &mut Interpreter, universe: &mut Universe) { +fn concatenate(interpreter: &mut Interpreter, heap: &mut GcHeap, universe: &mut Universe) { const SIGNATURE: &str = "String>>#concatenate:"; expect_args!(SIGNATURE, interpreter, [ @@ -143,10 +144,10 @@ fn concatenate(interpreter: &mut Interpreter, universe: &mut Universe) { interpreter .stack - .push(Value::String(Rc::new(format!("{}{}", s1, s2)))) + .push(Value::String(heap.allocate(format!("{}{}", s1, s2)))) } -fn as_symbol(interpreter: &mut Interpreter, universe: &mut Universe) { +fn as_symbol(interpreter: &mut Interpreter, _: &mut GcHeap, universe: &mut Universe) { const SIGNATURE: &str = "String>>#asSymbol"; expect_args!(SIGNATURE, interpreter, [ @@ -162,7 +163,7 @@ fn as_symbol(interpreter: &mut Interpreter, universe: &mut Universe) { } } -fn eq(interpreter: &mut Interpreter, universe: &mut Universe) { +fn eq(interpreter: &mut Interpreter, _: &mut GcHeap, universe: &mut Universe) { const SIGNATURE: &str = "String>>#="; expect_args!(SIGNATURE, interpreter, [ @@ -191,7 +192,11 @@ fn eq(interpreter: &mut Interpreter, universe: &mut Universe) { interpreter.stack.push(Value::Boolean(s1 == s2)) } -fn prim_substring_from_to(interpreter: &mut Interpreter, universe: &mut Universe) { +fn prim_substring_from_to( + interpreter: &mut Interpreter, + heap: &mut GcHeap, + universe: &mut Universe, +) { const SIGNATURE: &str = "String>>#primSubstringFrom:to:"; expect_args!(SIGNATURE, interpreter, [ @@ -206,7 +211,7 @@ fn prim_substring_from_to(interpreter: &mut Interpreter, universe: &mut Universe (_, _, _) => panic!("'{}': wrong types", SIGNATURE), }; - let string = Rc::new(value.chars().skip(from).take(to - from).collect()); + let string = heap.allocate(value.chars().skip(from).take(to - from).collect()); interpreter.stack.push(Value::String(string)) } diff --git a/som-interpreter-bc/src/primitives/symbol.rs b/som-interpreter-bc/src/primitives/symbol.rs index f453988f..a12c4a7b 100644 --- a/som-interpreter-bc/src/primitives/symbol.rs +++ b/som-interpreter-bc/src/primitives/symbol.rs @@ -1,4 +1,4 @@ -use std::rc::Rc; +use som_gc::GcHeap; use crate::interpreter::Interpreter; use crate::primitives::PrimitiveFn; @@ -10,16 +10,16 @@ pub static INSTANCE_PRIMITIVES: &[(&str, PrimitiveFn, bool)] = &[("asString", self::as_string, true)]; pub static CLASS_PRIMITIVES: &[(&str, PrimitiveFn, bool)] = &[]; -fn as_string(interpreter: &mut Interpreter, universe: &mut Universe) { +fn as_string(interpreter: &mut Interpreter, heap: &mut GcHeap, universe: &mut Universe) { const SIGNATURE: &str = "Symbol>>#asString"; expect_args!(SIGNATURE, interpreter, [ Value::Symbol(sym) => sym, ]); - interpreter.stack.push(Value::String(Rc::new( - universe.lookup_symbol(sym).to_string(), - ))); + interpreter.stack.push(Value::String( + heap.allocate(universe.lookup_symbol(sym).to_string()), + )); } /// Search for an instance primitive matching the given signature. diff --git a/som-interpreter-bc/src/primitives/system.rs b/som-interpreter-bc/src/primitives/system.rs index 65543274..373d39a2 100644 --- a/som-interpreter-bc/src/primitives/system.rs +++ b/som-interpreter-bc/src/primitives/system.rs @@ -1,6 +1,10 @@ +use std::cell::RefCell; use std::convert::TryFrom; use std::fs; -use std::rc::Rc; + +use num_bigint::ToBigInt; + +use som_gc::{GcHeap, Trace}; use crate::frame::FrameKind; use crate::interpreter::Interpreter; @@ -19,6 +23,7 @@ pub static INSTANCE_PRIMITIVES: &[(&str, PrimitiveFn, bool)] = &[ ("ticks", self::ticks, true), ("time", self::time, true), ("fullGC", self::full_gc, true), + ("gcStats", self::gc_stats, true), ("exit:", self::exit, true), ("global:", self::global, true), ("global:put:", self::global_put, true), @@ -27,8 +32,8 @@ pub static INSTANCE_PRIMITIVES: &[(&str, PrimitiveFn, bool)] = &[ ]; pub static CLASS_PRIMITIVES: &[(&str, PrimitiveFn, bool)] = &[]; -fn load_file(interpreter: &mut Interpreter, universe: &mut Universe) { - const SIGNATURE: &str = "System>>#loadFile:"; +fn load_file(interpreter: &mut Interpreter, heap: &mut GcHeap, universe: &mut Universe) { + const SIGNATURE: &str = "System>>#loadFie:"; expect_args!(SIGNATURE, interpreter, [ Value::System, @@ -42,14 +47,14 @@ fn load_file(interpreter: &mut Interpreter, universe: &mut Universe) { }; let value = match fs::read_to_string(path) { - Ok(value) => Value::String(Rc::new(value)), + Ok(value) => Value::String(heap.allocate(value)), Err(_) => Value::Nil, }; interpreter.stack.push(value); } -fn print_string(interpreter: &mut Interpreter, universe: &mut Universe) { +fn print_string(interpreter: &mut Interpreter, _: &mut GcHeap, universe: &mut Universe) { const SIGNATURE: &str = "System>>#printString:"; expect_args!(SIGNATURE, interpreter, [ @@ -69,7 +74,7 @@ fn print_string(interpreter: &mut Interpreter, universe: &mut Universe) { interpreter.stack.push(Value::System) } -fn print_newline(interpreter: &mut Interpreter, _: &mut Universe) { +fn print_newline(interpreter: &mut Interpreter, _: &mut GcHeap, _: &mut Universe) { const SIGNATURE: &'static str = "System>>#printNewline"; expect_args!(SIGNATURE, interpreter, [Value::System]); @@ -78,7 +83,7 @@ fn print_newline(interpreter: &mut Interpreter, _: &mut Universe) { interpreter.stack.push(Value::Nil); } -fn error_print(interpreter: &mut Interpreter, universe: &mut Universe) { +fn error_print(interpreter: &mut Interpreter, _: &mut GcHeap, universe: &mut Universe) { const SIGNATURE: &str = "System>>#errorPrint:"; expect_args!(SIGNATURE, interpreter, [ @@ -96,7 +101,7 @@ fn error_print(interpreter: &mut Interpreter, universe: &mut Universe) { interpreter.stack.push(Value::System); } -fn error_println(interpreter: &mut Interpreter, universe: &mut Universe) { +fn error_println(interpreter: &mut Interpreter, _: &mut GcHeap, universe: &mut Universe) { const SIGNATURE: &str = "System>>#errorPrintln:"; expect_args!(SIGNATURE, interpreter, [ @@ -114,7 +119,7 @@ fn error_println(interpreter: &mut Interpreter, universe: &mut Universe) { interpreter.stack.push(Value::System); } -fn load(interpreter: &mut Interpreter, universe: &mut Universe) { +fn load(interpreter: &mut Interpreter, heap: &mut GcHeap, universe: &mut Universe) { const SIGNATURE: &str = "System>>#load:"; expect_args!(SIGNATURE, interpreter, [ @@ -123,13 +128,13 @@ fn load(interpreter: &mut Interpreter, universe: &mut Universe) { ]); let name = universe.lookup_symbol(sym).to_string(); - match universe.load_class(name) { + match universe.load_class(heap, name) { Ok(class) => interpreter.stack.push(Value::Class(class)), Err(err) => panic!("'{}': {}", SIGNATURE, err), } } -fn has_global(interpreter: &mut Interpreter, universe: &mut Universe) { +fn has_global(interpreter: &mut Interpreter, _: &mut GcHeap, universe: &mut Universe) { const SIGNATURE: &str = "System>>#hasGlobal:"; expect_args!(SIGNATURE, interpreter, [ @@ -142,7 +147,7 @@ fn has_global(interpreter: &mut Interpreter, universe: &mut Universe) { interpreter.stack.push(value); } -fn global(interpreter: &mut Interpreter, universe: &mut Universe) { +fn global(interpreter: &mut Interpreter, _: &mut GcHeap, universe: &mut Universe) { const SIGNATURE: &str = "System>>#global:"; expect_args!(SIGNATURE, interpreter, [ @@ -155,7 +160,7 @@ fn global(interpreter: &mut Interpreter, universe: &mut Universe) { interpreter.stack.push(value); } -fn global_put(interpreter: &mut Interpreter, universe: &mut Universe) { +fn global_put(interpreter: &mut Interpreter, _: &mut GcHeap, universe: &mut Universe) { const SIGNATURE: &str = "System>>#global:put:"; expect_args!(SIGNATURE, interpreter, [ @@ -168,7 +173,7 @@ fn global_put(interpreter: &mut Interpreter, universe: &mut Universe) { interpreter.stack.push(value); } -fn exit(interpreter: &mut Interpreter, _: &mut Universe) { +fn exit(interpreter: &mut Interpreter, _: &mut GcHeap, _: &mut Universe) { const SIGNATURE: &str = "System>>#exit:"; expect_args!(SIGNATURE, interpreter, [ @@ -182,7 +187,7 @@ fn exit(interpreter: &mut Interpreter, _: &mut Universe) { } } -fn ticks(interpreter: &mut Interpreter, _: &mut Universe) { +fn ticks(interpreter: &mut Interpreter, _: &mut GcHeap, _: &mut Universe) { const SIGNATURE: &str = "System>>#ticks"; expect_args!(SIGNATURE, interpreter, [Value::System]); @@ -193,7 +198,7 @@ fn ticks(interpreter: &mut Interpreter, _: &mut Universe) { } } -fn time(interpreter: &mut Interpreter, _: &mut Universe) { +fn time(interpreter: &mut Interpreter, _: &mut GcHeap, _: &mut Universe) { const SIGNATURE: &str = "System>>#time"; expect_args!(SIGNATURE, interpreter, [Value::System]); @@ -204,7 +209,7 @@ fn time(interpreter: &mut Interpreter, _: &mut Universe) { } } -fn print_stack_trace(interpreter: &mut Interpreter, _: &mut Universe) { +fn print_stack_trace(interpreter: &mut Interpreter, _: &mut GcHeap, _: &mut Universe) { const SIGNATURE: &str = "System>>#printStackTrace"; expect_args!(SIGNATURE, interpreter, [Value::System]); @@ -229,13 +234,31 @@ fn print_stack_trace(interpreter: &mut Interpreter, _: &mut Universe) { interpreter.stack.push(Value::Boolean(true)); } -fn full_gc(interpreter: &mut Interpreter, _: &mut Universe) { +fn full_gc(interpreter: &mut Interpreter, heap: &mut GcHeap, universe: &mut Universe) { + const SIGNATURE: &str = "System>>#fullGC"; + + expect_args!(SIGNATURE, interpreter, [Value::System]); + + heap.collect_garbage(|| { + interpreter.trace(); + universe.trace(); + }); + + interpreter.stack.push(Value::Boolean(true)); +} + +fn gc_stats(interpreter: &mut Interpreter, heap: &mut GcHeap, _: &mut Universe) { const SIGNATURE: &str = "System>>#fullGC"; expect_args!(SIGNATURE, interpreter, [Value::System]); - // We don't do any garbage collection at all, so we return false. - interpreter.stack.push(Value::Boolean(false)); + let stats = heap.stats(); + let output = heap.allocate(RefCell::new(vec![ + Value::Integer(stats.collections_performed as i64), + Value::BigInteger(stats.total_time_spent.as_millis().to_bigint().unwrap()), + Value::Integer(stats.bytes_allocated as i64), + ])); + interpreter.stack.push(Value::Array(output)); } /// Search for an instance primitive matching the given signature. diff --git a/som-interpreter-bc/src/shell.rs b/som-interpreter-bc/src/shell.rs index cfd30226..afbfdfba 100644 --- a/som-interpreter-bc/src/shell.rs +++ b/som-interpreter-bc/src/shell.rs @@ -4,6 +4,7 @@ use std::time::Instant; use anyhow::Error; +use som_gc::GcHeap; use som_lexer::{Lexer, Token}; use som_parser::lang; @@ -15,6 +16,7 @@ use som_interpreter_bc::value::Value; /// Launches an interactive Read-Eval-Print-Loop within the given universe. pub fn interactive( + heap: &mut GcHeap, interpreter: &mut Interpreter, universe: &mut Universe, verbose: bool, @@ -82,6 +84,7 @@ pub fn interactive( let object_class = universe.object_class(); let class = match compiler::compile_class( + heap, &mut universe.interner, &class_def, Some(&object_class), @@ -93,17 +96,17 @@ pub fn interactive( } }; let metaclass_class = universe.metaclass_class(); - class.borrow_mut().set_super_class(&object_class); + class.borrow_mut().set_super_class(object_class.clone()); class .borrow() .class() .borrow_mut() - .set_super_class(&object_class.borrow().class()); + .set_super_class(object_class.borrow().class().clone()); class .borrow() .class() .borrow_mut() - .set_class(&metaclass_class); + .set_class(metaclass_class.clone()); let method = class .borrow() @@ -115,10 +118,10 @@ pub fn interactive( holder: class.clone(), self_value: Value::Class(class), }; - let frame = interpreter.push_frame(kind); + let frame = interpreter.push_frame(heap, kind); frame.borrow_mut().args.push(Value::System); frame.borrow_mut().args.push(last_value.clone()); - if let Some(value) = interpreter.run(universe) { + if let Some(value) = interpreter.run(heap, universe) { writeln!( &mut stdout, "returned: {} ({:?})", diff --git a/som-interpreter-bc/src/universe.rs b/som-interpreter-bc/src/universe.rs index af145cc4..635c1081 100644 --- a/som-interpreter-bc/src/universe.rs +++ b/som-interpreter-bc/src/universe.rs @@ -3,10 +3,13 @@ use std::collections::HashMap; use std::fs; use std::io; use std::path::{Path, PathBuf}; -use std::rc::Rc; use anyhow::{anyhow, Error}; +use som_gc::Gc; +use som_gc::GcHeap; +use som_gc::Trace; + use crate::block::Block; use crate::class::Class; use crate::compiler; @@ -65,6 +68,34 @@ pub struct CoreClasses { pub false_class: SOMRef, } +impl Trace for CoreClasses { + #[inline] + fn trace(&self) { + self.object_class.trace(); + self.class_class.trace(); + self.metaclass_class.trace(); + + self.nil_class.trace(); + self.integer_class.trace(); + self.double_class.trace(); + self.array_class.trace(); + self.method_class.trace(); + self.primitive_class.trace(); + self.symbol_class.trace(); + self.string_class.trace(); + self.system_class.trace(); + + self.block_class.trace(); + self.block1_class.trace(); + self.block2_class.trace(); + self.block3_class.trace(); + + self.boolean_class.trace(); + self.true_class.trace(); + self.false_class.trace(); + } +} + /// The central data structure for the interpreter. /// /// It represents the complete state of the interpreter, like the known class definitions, @@ -80,38 +111,62 @@ pub struct Universe { pub core: CoreClasses, } +impl Trace for Universe { + #[inline] + fn trace(&self) { + for it in self.globals.values() { + it.trace(); + } + self.core.trace(); + } +} + impl Universe { /// Initialize the universe from the given classpath. - pub fn with_classpath(classpath: Vec) -> Result { + pub fn with_classpath(heap: &mut GcHeap, classpath: Vec) -> Result { let mut interner = Interner::with_capacity(100); let mut globals = HashMap::new(); - let object_class = Self::load_system_class(&mut interner, classpath.as_slice(), "Object")?; - let class_class = Self::load_system_class(&mut interner, classpath.as_slice(), "Class")?; + let object_class = + Self::load_system_class(heap, &mut interner, classpath.as_slice(), "Object")?; + let class_class = + Self::load_system_class(heap, &mut interner, classpath.as_slice(), "Class")?; let metaclass_class = - Self::load_system_class(&mut interner, classpath.as_slice(), "Metaclass")?; + Self::load_system_class(heap, &mut interner, classpath.as_slice(), "Metaclass")?; - let nil_class = Self::load_system_class(&mut interner, classpath.as_slice(), "Nil")?; + let nil_class = Self::load_system_class(heap, &mut interner, classpath.as_slice(), "Nil")?; let integer_class = - Self::load_system_class(&mut interner, classpath.as_slice(), "Integer")?; - let array_class = Self::load_system_class(&mut interner, classpath.as_slice(), "Array")?; - let method_class = Self::load_system_class(&mut interner, classpath.as_slice(), "Method")?; - let symbol_class = Self::load_system_class(&mut interner, classpath.as_slice(), "Symbol")?; + Self::load_system_class(heap, &mut interner, classpath.as_slice(), "Integer")?; + let array_class = + Self::load_system_class(heap, &mut interner, classpath.as_slice(), "Array")?; + let method_class = + Self::load_system_class(heap, &mut interner, classpath.as_slice(), "Method")?; + let symbol_class = + Self::load_system_class(heap, &mut interner, classpath.as_slice(), "Symbol")?; let primitive_class = - Self::load_system_class(&mut interner, classpath.as_slice(), "Primitive")?; - let string_class = Self::load_system_class(&mut interner, classpath.as_slice(), "String")?; - let system_class = Self::load_system_class(&mut interner, classpath.as_slice(), "System")?; - let double_class = Self::load_system_class(&mut interner, classpath.as_slice(), "Double")?; - - let block_class = Self::load_system_class(&mut interner, classpath.as_slice(), "Block")?; - let block1_class = Self::load_system_class(&mut interner, classpath.as_slice(), "Block1")?; - let block2_class = Self::load_system_class(&mut interner, classpath.as_slice(), "Block2")?; - let block3_class = Self::load_system_class(&mut interner, classpath.as_slice(), "Block3")?; + Self::load_system_class(heap, &mut interner, classpath.as_slice(), "Primitive")?; + let string_class = + Self::load_system_class(heap, &mut interner, classpath.as_slice(), "String")?; + let system_class = + Self::load_system_class(heap, &mut interner, classpath.as_slice(), "System")?; + let double_class = + Self::load_system_class(heap, &mut interner, classpath.as_slice(), "Double")?; + + let block_class = + Self::load_system_class(heap, &mut interner, classpath.as_slice(), "Block")?; + let block1_class = + Self::load_system_class(heap, &mut interner, classpath.as_slice(), "Block1")?; + let block2_class = + Self::load_system_class(heap, &mut interner, classpath.as_slice(), "Block2")?; + let block3_class = + Self::load_system_class(heap, &mut interner, classpath.as_slice(), "Block3")?; let boolean_class = - Self::load_system_class(&mut interner, classpath.as_slice(), "Boolean")?; - let true_class = Self::load_system_class(&mut interner, classpath.as_slice(), "True")?; - let false_class = Self::load_system_class(&mut interner, classpath.as_slice(), "False")?; + Self::load_system_class(heap, &mut interner, classpath.as_slice(), "Boolean")?; + let true_class = + Self::load_system_class(heap, &mut interner, classpath.as_slice(), "True")?; + let false_class = + Self::load_system_class(heap, &mut interner, classpath.as_slice(), "False")?; // initializeSystemClass(objectClass, null, "Object"); // set_super_class(&object_class, &nil_class, &metaclass_class); @@ -119,12 +174,12 @@ impl Universe { .borrow() .class() .borrow_mut() - .set_class(&metaclass_class); + .set_class(metaclass_class.clone()); object_class .borrow() .class() .borrow_mut() - .set_super_class(&class_class); + .set_super_class(class_class.clone()); // initializeSystemClass(classClass, objectClass, "Class"); set_super_class(&class_class, &object_class, &metaclass_class); // initializeSystemClass(metaclassClass, classClass, "Metaclass"); @@ -214,6 +269,7 @@ impl Universe { /// Load a system class (with an incomplete hierarchy). pub fn load_system_class( + heap: &mut GcHeap, interner: &mut Interner, classpath: &[impl AsRef], class_name: impl Into, @@ -248,7 +304,7 @@ impl Universe { path.display(), )); } - let class = compiler::compile_class(interner, &defn, None) + let class = compiler::compile_class(heap, interner, &defn, None) .ok_or_else(|| Error::msg(format!("")))?; return Ok(class); @@ -258,7 +314,11 @@ impl Universe { } /// Load a class from its name into this universe. - pub fn load_class(&mut self, class_name: impl Into) -> Result, Error> { + pub fn load_class( + &mut self, + heap: &mut GcHeap, + class_name: impl Into, + ) -> Result, Error> { let class_name = class_name.into(); for path in self.classpath.iter() { let mut path = path.join(class_name.as_str()); @@ -293,14 +353,15 @@ impl Universe { let symbol = self.intern_symbol(super_class.as_str()); match self.lookup_global(symbol) { Some(Value::Class(super_class)) => super_class, - _ => self.load_class(super_class)?, + _ => self.load_class(heap, super_class)?, } } else { self.core.object_class.clone() }; - let class = compiler::compile_class(&mut self.interner, &defn, Some(&super_class)) - .ok_or_else(|| Error::msg(format!("")))?; + let class = + compiler::compile_class(heap, &mut self.interner, &defn, Some(&super_class)) + .ok_or_else(|| Error::msg(format!("")))?; set_super_class(&class, &super_class, &self.core.metaclass_class); // fn has_duplicated_field(class: &SOMRef) -> Option<(String, (String, String))> { @@ -349,7 +410,11 @@ impl Universe { } /// Load a class from its path into this universe. - pub fn load_class_from_path(&mut self, path: impl AsRef) -> Result, Error> { + pub fn load_class_from_path( + &mut self, + heap: &mut GcHeap, + path: impl AsRef, + ) -> Result, Error> { let path = path.as_ref(); let file_stem = path .file_stem() @@ -384,13 +449,13 @@ impl Universe { let symbol = self.intern_symbol(super_class); match self.lookup_global(symbol) { Some(Value::Class(class)) => class, - _ => self.load_class(super_class)?, + _ => self.load_class(heap, super_class)?, } } else { self.core.object_class.clone() }; - let class = compiler::compile_class(&mut self.interner, &defn, Some(&super_class)) + let class = compiler::compile_class(heap, &mut self.interner, &defn, Some(&super_class)) .ok_or_else(|| Error::msg(format!("")))?; set_super_class(&class, &super_class, &self.core.metaclass_class); @@ -506,20 +571,20 @@ impl Universe { pub fn escaped_block( &mut self, interpreter: &mut Interpreter, + heap: &mut GcHeap, value: Value, - block: Rc, + block: Gc, ) -> Option<()> { let method_name = self.intern_symbol("escapedBlock:"); let method = value.lookup_method(self, method_name)?; - let holder = method.holder().upgrade().unwrap(); let kind = FrameKind::Method { + holder: method.holder.clone(), method, - holder, self_value: value.clone(), }; - let frame = interpreter.push_frame(kind); + let frame = interpreter.push_frame(heap, kind); frame.borrow_mut().args.push(value); frame.borrow_mut().args.push(Value::Block(block)); @@ -530,6 +595,7 @@ impl Universe { pub fn does_not_understand( &mut self, interpreter: &mut Interpreter, + heap: &mut GcHeap, value: Value, symbol: Interned, args: Vec, @@ -537,17 +603,16 @@ impl Universe { let method_name = self.intern_symbol("doesNotUnderstand:arguments:"); let method = value.lookup_method(self, method_name)?; - let holder = method.holder().upgrade().unwrap(); let kind = FrameKind::Method { + holder: method.holder.clone(), method, - holder, self_value: value.clone(), }; - let frame = interpreter.push_frame(kind); + let frame = interpreter.push_frame(heap, kind); frame.borrow_mut().args.push(value); frame.borrow_mut().args.push(Value::Symbol(symbol)); - let args = Value::Array(Rc::new(RefCell::new(args))); + let args = Value::Array(heap.allocate(RefCell::new(args))); frame.borrow_mut().args.push(args); Some(()) @@ -557,20 +622,20 @@ impl Universe { pub fn unknown_global( &mut self, interpreter: &mut Interpreter, + heap: &mut GcHeap, value: Value, name: Interned, ) -> Option<()> { let method_name = self.intern_symbol("unknownGlobal:"); let method = value.lookup_method(self, method_name)?; - let holder = method.holder().upgrade().unwrap(); let kind = FrameKind::Method { + holder: method.holder.clone(), method, - holder, self_value: value.clone(), }; - let frame = interpreter.push_frame(kind); + let frame = interpreter.push_frame(heap, kind); frame.borrow_mut().args.push(value); frame.borrow_mut().args.push(Value::Symbol(name)); @@ -578,7 +643,12 @@ impl Universe { } /// Call `System>>#initialize:` with the given name, if it is defined. - pub fn initialize(&mut self, interpreter: &mut Interpreter, args: Vec) -> Option<()> { + pub fn initialize( + &mut self, + heap: &mut GcHeap, + interpreter: &mut Interpreter, + args: Vec, + ) -> Option<()> { let method_name = self.interner.intern("initialize:"); let method = Value::System.lookup_method(self, method_name)?; @@ -588,9 +658,9 @@ impl Universe { self_value: Value::System, }; - let frame = interpreter.push_frame(kind); + let frame = interpreter.push_frame(heap, kind); frame.borrow_mut().args.push(Value::System); - let args = Value::Array(Rc::new(RefCell::new(args))); + let args = Value::Array(heap.allocate(RefCell::new(args))); frame.borrow_mut().args.push(args); Some(()) @@ -602,15 +672,15 @@ fn set_super_class( super_class: &SOMRef, metaclass_class: &SOMRef, ) { - class.borrow_mut().set_super_class(super_class); + class.borrow_mut().set_super_class(super_class.clone()); class .borrow() .class() .borrow_mut() - .set_super_class(&super_class.borrow().class()); + .set_super_class(super_class.borrow().class()); class .borrow() .class() .borrow_mut() - .set_class(metaclass_class); + .set_class(metaclass_class.clone()); } diff --git a/som-interpreter-bc/src/value.rs b/som-interpreter-bc/src/value.rs index 364030d7..bde4e031 100644 --- a/som-interpreter-bc/src/value.rs +++ b/som-interpreter-bc/src/value.rs @@ -1,7 +1,7 @@ use std::fmt; -use std::rc::Rc; use num_bigint::BigInt; +use som_gc::{Gc, Trace}; use crate::block::Block; use crate::class::Class; @@ -29,17 +29,50 @@ pub enum Value { /// An interned symbol value. Symbol(Interned), /// A string value. - String(Rc), + String(Gc), /// An array of values. Array(SOMRef>), /// A block value, ready to be evaluated. - Block(Rc), + Block(Gc), /// A generic (non-primitive) class instance. Instance(SOMRef), /// A bare class object. Class(SOMRef), /// A bare invokable. - Invokable(Rc), + Invokable(Gc), +} + +impl Trace for Value { + #[inline] + fn trace(&self) { + match self { + Value::Nil => {} + Value::System => {} + Value::Boolean(_) => {} + Value::Integer(_) => {} + Value::BigInteger(_) => {} + Value::Double(_) => {} + Value::Symbol(_) => {} + Value::String(string) => { + string.trace(); + } + Value::Array(array) => { + array.trace(); + } + Value::Block(block) => { + block.trace(); + } + Value::Instance(instance) => { + instance.trace(); + } + Value::Class(class) => { + class.trace(); + } + Value::Invokable(method) => { + method.trace(); + } + } + } } impl Value { @@ -64,7 +97,7 @@ impl Value { } /// Search for a given method for this value. - pub fn lookup_method(&self, universe: &Universe, signature: Interned) -> Option> { + pub fn lookup_method(&self, universe: &Universe, signature: Interned) -> Option> { self.class(universe).borrow().lookup_method(signature) } @@ -119,11 +152,11 @@ impl Value { instance.borrow().class().borrow().name(), ), Self::Class(class) => class.borrow().name().to_string(), - Self::Invokable(invokable) => invokable - .holder() - .upgrade() - .map(|holder| format!("{}>>#{}", holder.borrow().name(), invokable.signature())) - .unwrap_or_else(|| format!("??>>#{}", invokable.signature())), + Self::Invokable(invokable) => format!( + "{}>>#{}", + invokable.holder.borrow().name(), + invokable.signature(), + ), } } } @@ -143,12 +176,12 @@ impl PartialEq for Value { a.eq(&BigInt::from(*b)) } (Self::Symbol(a), Self::Symbol(b)) => a.eq(b), - (Self::String(a), Self::String(b)) => Rc::ptr_eq(a, b), - (Self::Array(a), Self::Array(b)) => Rc::ptr_eq(a, b), - (Self::Instance(a), Self::Instance(b)) => Rc::ptr_eq(a, b), - (Self::Class(a), Self::Class(b)) => Rc::ptr_eq(a, b), - (Self::Block(a), Self::Block(b)) => Rc::ptr_eq(a, b), - (Self::Invokable(a), Self::Invokable(b)) => Rc::ptr_eq(a, b), + (Self::String(a), Self::String(b)) => Gc::ptr_eq(a, b), + (Self::Array(a), Self::Array(b)) => Gc::ptr_eq(a, b), + (Self::Instance(a), Self::Instance(b)) => Gc::ptr_eq(a, b), + (Self::Class(a), Self::Class(b)) => Gc::ptr_eq(a, b), + (Self::Block(a), Self::Block(b)) => Gc::ptr_eq(a, b), + (Self::Invokable(a), Self::Invokable(b)) => Gc::ptr_eq(a, b), _ => false, } } @@ -164,17 +197,13 @@ impl fmt::Debug for Value { Self::BigInteger(val) => f.debug_tuple("BigInteger").field(val).finish(), Self::Double(val) => f.debug_tuple("Double").field(val).finish(), Self::Symbol(val) => f.debug_tuple("Symbol").field(val).finish(), - Self::String(val) => f.debug_tuple("String").field(val).finish(), - Self::Array(val) => f.debug_tuple("Array").field(&val.borrow()).finish(), - Self::Block(val) => f.debug_tuple("Block").field(val).finish(), + Self::String(val) => f.debug_tuple("String").field(val.as_ref()).finish(), + Self::Array(val) => f.debug_tuple("Array").field(val.as_ref()).finish(), + Self::Block(val) => f.debug_tuple("Block").field(val.as_ref()).finish(), Self::Instance(val) => f.debug_tuple("Instance").field(&val.borrow()).finish(), - Self::Class(val) => f.debug_tuple("Class").field(&val.borrow()).finish(), + Self::Class(val) => f.debug_tuple("Class").field(val.as_ref()).finish(), Self::Invokable(val) => { - let signature = val - .holder() - .upgrade() - .map(|holder| format!("{}>>#{}", holder.borrow().name(), val.signature())) - .unwrap_or_else(|| format!("??>>#{}", val.signature())); + let signature = format!("{}>>#{}", val.holder.borrow().name(), val.signature()); f.debug_tuple("Invokable").field(&signature).finish() } } diff --git a/som-interpreter-bc/tests/basic_interpreter_tests.rs b/som-interpreter-bc/tests/basic_interpreter_tests.rs index 50f1c691..3c60d53d 100644 --- a/som-interpreter-bc/tests/basic_interpreter_tests.rs +++ b/som-interpreter-bc/tests/basic_interpreter_tests.rs @@ -1,5 +1,6 @@ use std::path::PathBuf; +use som_gc::GcHeap; use som_interpreter_bc::compiler; use som_interpreter_bc::frame::FrameKind; use som_interpreter_bc::interpreter::Interpreter; @@ -8,21 +9,26 @@ use som_interpreter_bc::value::Value; use som_lexer::{Lexer, Token}; use som_parser::lang; -fn setup_universe() -> Universe { +fn setup_universe(heap: &mut GcHeap) -> Universe { let classpath = vec![ PathBuf::from("../core-lib/Smalltalk"), PathBuf::from("../core-lib/TestSuite/BasicInterpreterTests"), ]; - Universe::with_classpath(classpath).expect("could not setup test universe") + Universe::with_classpath(heap, classpath).expect("could not setup test universe") } #[test] fn basic_interpreter_tests() { - let mut universe = setup_universe(); + let mut heap = GcHeap::new(); - let return_class = Value::Class(universe.load_class("Return").unwrap()); - let compiler_simplification_class = - Value::Class(universe.load_class("CompilerSimplification").unwrap()); + let mut universe = setup_universe(&mut heap); + + let return_class = Value::Class(universe.load_class(&mut heap, "Return").unwrap()); + let compiler_simplification_class = Value::Class( + universe + .load_class(&mut heap, "CompilerSimplification") + .unwrap(), + ); let method_name = universe.intern_symbol("run"); @@ -176,23 +182,27 @@ fn basic_interpreter_tests() { let class_def = som_parser::apply(lang::class_def(), tokens.as_slice()).unwrap(); let object_class = universe.object_class(); - let class = - compiler::compile_class(&mut universe.interner, &class_def, Some(&object_class)); + let class = compiler::compile_class( + &mut heap, + &mut universe.interner, + &class_def, + Some(&object_class), + ); assert!(class.is_some(), "could not compile test expression"); let class = class.unwrap(); let metaclass_class = universe.metaclass_class(); - class.borrow_mut().set_super_class(&object_class); + class.borrow_mut().set_super_class(object_class.clone()); class .borrow() .class() .borrow_mut() - .set_super_class(&object_class.borrow().class()); + .set_super_class(object_class.borrow().class().clone()); class .borrow() .class() .borrow_mut() - .set_class(&metaclass_class); + .set_class(metaclass_class.clone()); let method = class .borrow() @@ -203,8 +213,8 @@ fn basic_interpreter_tests() { holder: class.clone(), self_value: Value::Class(class), }; - interpreter.push_frame(kind); - if let Some(output) = interpreter.run(&mut universe) { + interpreter.push_frame(&mut heap, kind); + if let Some(output) = interpreter.run(&mut heap, &mut universe) { assert_eq!(&output, expected, "unexpected test output value"); } } From 1b714372539e2df0a26f22256bfd41088ab59014 Mon Sep 17 00:00:00 2001 From: Nicolas Polomack Date: Thu, 2 Feb 2023 18:00:13 +0100 Subject: [PATCH 05/14] fix: fixed wrong comment location --- som-gc/src/heap.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/som-gc/src/heap.rs b/som-gc/src/heap.rs index 8b4556fc..65965538 100644 --- a/som-gc/src/heap.rs +++ b/som-gc/src/heap.rs @@ -83,11 +83,11 @@ impl GcHeap { // TODO: trigger `collect_garbage` let mut allocated = Box::new(GcBox::new(value)); allocated.next = self.head; + // SAFETY: `self.head` is guaranteed to be properly aligned and non-null by `Box::into_raw`. let ptr = unsafe { NonNull::new_unchecked(Box::into_raw(allocated)) }; self.head = Some(ptr as NonNull>); self.stats.bytes_allocated += std::mem::size_of::>(); Gc { - // SAFETY: `self.head` is guaranteed to be properly aligned and non-null by `Box::into_raw`. ptr: Cell::new(ptr), marker: PhantomData, } From 285e360c482eb9ca6afd91b9a07f94097e562aee Mon Sep 17 00:00:00 2001 From: Nicolas Polomack Date: Fri, 3 Feb 2023 16:24:32 +0100 Subject: [PATCH 06/14] feat: GC mark bits now cleared after sweeping --- som-gc/src/heap.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/som-gc/src/heap.rs b/som-gc/src/heap.rs index 65965538..635faa57 100644 --- a/som-gc/src/heap.rs +++ b/som-gc/src/heap.rs @@ -94,6 +94,7 @@ impl GcHeap { } /// Clears the `mark` bits on every GC object. + #[allow(unused)] fn clear_marks(&mut self) { let mut head = self.head; while let Some(mut cur) = head { @@ -123,6 +124,7 @@ impl GcHeap { self.stats.bytes_allocated -= std::mem::size_of_val::>(&*value); drop(value); } else { + cur_ref.marked.set(false); prev = head; } head = next; @@ -133,7 +135,6 @@ impl GcHeap { pub fn collect_garbage(&mut self, mut mark_fn: impl FnMut()) { let start = Instant::now(); let allocated_start = self.stats.bytes_allocated; - self.clear_marks(); mark_fn(); self.sweep(); self.stats.bytes_swept += allocated_start - self.stats.bytes_allocated; From c3580add87d968b00677d74cba0f2a3c5d8306e2 Mon Sep 17 00:00:00 2001 From: Nicolas Polomack Date: Fri, 3 Feb 2023 16:27:05 +0100 Subject: [PATCH 07/14] feat: adjusted when the GC is allowed to run --- som-interpreter-bc/src/interpreter.rs | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/som-interpreter-bc/src/interpreter.rs b/som-interpreter-bc/src/interpreter.rs index 8bf9e865..c0a01c6f 100644 --- a/som-interpreter-bc/src/interpreter.rs +++ b/som-interpreter-bc/src/interpreter.rs @@ -43,6 +43,10 @@ macro_rules! send { symbol, nb_params as usize, ); + $heap.maybe_collect_garbage(|| { + $interp.trace(); + $universe.trace(); + }); }}; } @@ -75,6 +79,10 @@ macro_rules! super_send { symbol, nb_params as usize, ); + $heap.maybe_collect_garbage(|| { + $interp.trace(); + $universe.trace(); + }); }}; } @@ -120,12 +128,12 @@ impl Interpreter { pub fn run(&mut self, heap: &mut GcHeap, universe: &mut Universe) -> Option { loop { - heap.maybe_collect_garbage(|| { - self.trace(); - universe.trace(); - }); - let Some(frame) = self.current_frame() else { + heap.maybe_collect_garbage(|| { + self.trace(); + universe.trace(); + }); + return Some(self.stack.pop().unwrap_or(Value::Nil)); }; @@ -144,6 +152,11 @@ impl Interpreter { match bytecode { Bytecode::Halt => { + heap.maybe_collect_garbage(|| { + self.trace(); + universe.trace(); + }); + return Some(Value::Nil); } Bytecode::Dup => { From ed80187f3be0aede89888d8c2532681287402aab Mon Sep 17 00:00:00 2001 From: Nicolas Polomack Date: Fri, 3 Feb 2023 16:43:10 +0100 Subject: [PATCH 08/14] chore: adjusted README for the GC's introduction --- README.md | 1 + som-interpreter-bc/README.md | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 9f1e35b4..838bc960 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,7 @@ Here is a rundown of these different crates (as of now, layout may change in the | **`som-parser-core`** | The common foundational types for building parsers | | **`som-parser-text`** | A SOM parser that works directly with text (without a lexer). | | **`som-parser-symbols`** | A SOM parser that works with **`som-lexer`**'s output. | +| **`som-gc`** | The SOM mark-and-sweep garbage collector as a library. | [**Cargo workspace**]: https://doc.rust-lang.org/cargo/reference/workspaces.html diff --git a/som-interpreter-bc/README.md b/som-interpreter-bc/README.md index 48a65394..35daa945 100644 --- a/som-interpreter-bc/README.md +++ b/som-interpreter-bc/README.md @@ -5,4 +5,4 @@ This is the interpreter for the Simple Object Machine. It is bytecode-based, in that it works by compiling nodes from the Abstract Syntax Tree from **`som-core`** into stack-based bytecode instructions and then executing them. -Resources are managed and tracked through reference-counting (using Rust's **`Rc`**/**`Weak`** types). +Resources are managed using a mark-and-sweep garbage collector (from the `som-gc` library). From 2847330194a01da80306777d7284eaafa87d94a2 Mon Sep 17 00:00:00 2001 From: Nicolas Polomack Date: Fri, 3 Feb 2023 17:33:22 +0100 Subject: [PATCH 09/14] fix: fixed minor oversight --- som-interpreter-bc/src/primitives/system.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/som-interpreter-bc/src/primitives/system.rs b/som-interpreter-bc/src/primitives/system.rs index 373d39a2..cfa1c5d1 100644 --- a/som-interpreter-bc/src/primitives/system.rs +++ b/som-interpreter-bc/src/primitives/system.rs @@ -248,7 +248,7 @@ fn full_gc(interpreter: &mut Interpreter, heap: &mut GcHeap, universe: &mut Univ } fn gc_stats(interpreter: &mut Interpreter, heap: &mut GcHeap, _: &mut Universe) { - const SIGNATURE: &str = "System>>#fullGC"; + const SIGNATURE: &str = "System>>#gcStats"; expect_args!(SIGNATURE, interpreter, [Value::System]); From 90fe1cb3b30332a6bb483ecdef7e0fc47a67cf0c Mon Sep 17 00:00:00 2001 From: Nicolas Polomack Date: Thu, 9 Feb 2023 09:16:46 +0100 Subject: [PATCH 10/14] fix: removed superfluous `PhantomData` --- som-gc/src/gc.rs | 5 ----- som-gc/src/heap.rs | 2 -- 2 files changed, 7 deletions(-) diff --git a/som-gc/src/gc.rs b/som-gc/src/gc.rs index 1bfd5c26..0e1ca8bd 100644 --- a/som-gc/src/gc.rs +++ b/som-gc/src/gc.rs @@ -2,10 +2,8 @@ use std::borrow::Borrow; use std::cell::Cell; use std::fmt; use std::hash::Hash; -use std::marker::PhantomData; use std::ops::Deref; use std::ptr::NonNull; -use std::rc::Rc; use crate::gc_box::GcBox; use crate::trace::Trace; @@ -17,8 +15,6 @@ where { /// The pointer to the referenced `GcBox`. pub(crate) ptr: Cell>>, - // needed for drop-related reasons. - pub(crate) marker: PhantomData>, } impl Gc @@ -141,7 +137,6 @@ where fn clone(&self) -> Self { Self { ptr: self.ptr.clone(), - marker: PhantomData, } } } diff --git a/som-gc/src/heap.rs b/som-gc/src/heap.rs index 635faa57..24b73a2e 100644 --- a/som-gc/src/heap.rs +++ b/som-gc/src/heap.rs @@ -1,5 +1,4 @@ use std::cell::Cell; -use std::marker::PhantomData; use std::ptr::NonNull; use std::time::{Duration, Instant}; @@ -89,7 +88,6 @@ impl GcHeap { self.stats.bytes_allocated += std::mem::size_of::>(); Gc { ptr: Cell::new(ptr), - marker: PhantomData, } } From 5fe855aaf99c9dc66e3a64da399da7a081f0ce53 Mon Sep 17 00:00:00 2001 From: Nicolas Polomack Date: Fri, 10 Nov 2023 18:07:02 +0100 Subject: [PATCH 11/14] fix: fixed compiler errors --- som-interpreter-bc/src/block.rs | 1 - som-interpreter-bc/src/compiler.rs | 4 ++-- som-interpreter-bc/src/main.rs | 7 +++---- som-interpreter-bc/src/method.rs | 1 - 4 files changed, 5 insertions(+), 8 deletions(-) diff --git a/som-interpreter-bc/src/block.rs b/som-interpreter-bc/src/block.rs index 13af535f..365eb717 100644 --- a/som-interpreter-bc/src/block.rs +++ b/som-interpreter-bc/src/block.rs @@ -32,7 +32,6 @@ pub struct Block { impl Trace for BlockInfo { #[inline] fn trace(&self) { - self.locals.trace(); self.literals.trace(); self.inline_cache.trace(); } diff --git a/som-interpreter-bc/src/compiler.rs b/som-interpreter-bc/src/compiler.rs index 1bdc4e8a..bdb9a468 100644 --- a/som-interpreter-bc/src/compiler.rs +++ b/som-interpreter-bc/src/compiler.rs @@ -579,8 +579,8 @@ fn compile_block(outer: &mut dyn GenCtxt, defn: &ast::Block) -> Option { .map(|name| ctxt.intern_symbol(&name)) .collect() }; - let literals = ctxt.literals.into_iter().collect(); - let body = ctxt.body.unwrap_or_default(); + let literals = std::mem::take(&mut ctxt.literals).into_iter().collect(); + let body = ctxt.body.take().unwrap_or_default(); let nb_params = ctxt.args.len(); let block = Block { diff --git a/som-interpreter-bc/src/main.rs b/som-interpreter-bc/src/main.rs index 39e19c93..456dbedb 100644 --- a/som-interpreter-bc/src/main.rs +++ b/som-interpreter-bc/src/main.rs @@ -16,7 +16,7 @@ use som_gc::{GcHeap, GcParams}; use som_interpreter_bc::disassembler::disassemble_method_body; use som_interpreter_bc::interpreter::Interpreter; -use som_interpreter_bc::method::{Method, MethodKind}; +use som_interpreter_bc::method::MethodKind; use som_interpreter_bc::universe::Universe; use som_interpreter_bc::value::Value; @@ -108,8 +108,7 @@ fn main() -> anyhow::Result<()> { let args = std::iter::once(String::from(file_stem)) .chain(opts.args.iter().cloned()) - .map(Rc::new) - .map(Value::String) + .map(|it| Value::String(heap.allocate(it))) .collect(); universe @@ -174,7 +173,7 @@ fn disassemble_class(heap: &mut GcHeap, opts: Options) -> anyhow::Result<()> { let class = universe.load_class(heap, file_stem)?; - let methods: Vec> = if opts.args.is_empty() { + let methods: Vec<_> = if opts.args.is_empty() { class.borrow().methods.values().cloned().collect() } else { opts.args diff --git a/som-interpreter-bc/src/method.rs b/som-interpreter-bc/src/method.rs index 528d3fa8..0110cdea 100644 --- a/som-interpreter-bc/src/method.rs +++ b/som-interpreter-bc/src/method.rs @@ -62,7 +62,6 @@ impl Trace for MethodKind { impl Trace for MethodEnv { #[inline] fn trace(&self) { - self.locals.trace(); self.literals.trace(); self.inline_cache.trace(); } From 3bce35c49874403028545a870b75c024e325afaf Mon Sep 17 00:00:00 2001 From: Nicolas Polomack Date: Wed, 14 Feb 2024 19:13:37 +0100 Subject: [PATCH 12/14] fix: fixed inline caches pointer checks --- som-interpreter-bc/src/block.rs | 2 +- som-interpreter-bc/src/interpreter.rs | 12 ++++-------- som-interpreter-bc/src/method.rs | 2 +- 3 files changed, 6 insertions(+), 10 deletions(-) diff --git a/som-interpreter-bc/src/block.rs b/som-interpreter-bc/src/block.rs index 365eb717..949a141c 100644 --- a/som-interpreter-bc/src/block.rs +++ b/som-interpreter-bc/src/block.rs @@ -18,7 +18,7 @@ pub struct BlockInfo { pub literals: Vec, pub body: Vec, pub nb_params: usize, - pub inline_cache: RefCell)>>>, + pub inline_cache: RefCell, Gc)>>>, } /// Represents an executable block. diff --git a/som-interpreter-bc/src/interpreter.rs b/som-interpreter-bc/src/interpreter.rs index c0a01c6f..8cf56618 100644 --- a/som-interpreter-bc/src/interpreter.rs +++ b/som-interpreter-bc/src/interpreter.rs @@ -448,14 +448,12 @@ impl Interpreter { let maybe_found = unsafe { inline_cache.get_unchecked_mut(bytecode_idx) }; match maybe_found { - Some((receiver, method)) if *receiver == RefCell::as_ptr(&class) => { + Some((receiver, method)) if *receiver == Gc::as_ptr(class) => { Some(Gc::clone(method)) } place @ None => { let found = class.borrow().lookup_method(signature); - *place = found - .clone() - .map(|method| (class.as_ptr() as *const _, method)); + *place = found.clone().map(|method| (Gc::as_ptr(class), method)); found } _ => class.borrow().lookup_method(signature), @@ -471,14 +469,12 @@ impl Interpreter { let maybe_found = unsafe { inline_cache.get_unchecked_mut(bytecode_idx) }; match maybe_found { - Some((receiver, method)) if *receiver == RefCell::as_ptr(&class) => { + Some((receiver, method)) if *receiver == Gc::as_ptr(class) => { Some(Gc::clone(method)) } place @ None => { let found = class.borrow().lookup_method(signature); - *place = found - .clone() - .map(|method| (class.as_ptr() as *const _, method)); + *place = found.clone().map(|method| (Gc::as_ptr(class), method)); found } _ => class.borrow().lookup_method(signature), diff --git a/som-interpreter-bc/src/method.rs b/som-interpreter-bc/src/method.rs index 0110cdea..932c616d 100644 --- a/som-interpreter-bc/src/method.rs +++ b/som-interpreter-bc/src/method.rs @@ -19,7 +19,7 @@ pub struct MethodEnv { pub locals: Vec, pub literals: Vec, pub body: Vec, - pub inline_cache: RefCell)>>>, + pub inline_cache: RefCell, Gc)>>>, } /// The kind of a class method. From 4962f59ec973bc7f0c918c13c73d69f4ac319e1d Mon Sep 17 00:00:00 2001 From: Nicolas Polomack Date: Wed, 14 Feb 2024 19:30:28 +0100 Subject: [PATCH 13/14] feat: GC scans now use a Vec --- som-gc/src/gc_box.rs | 6 ------ som-gc/src/heap.rs | 49 +++++++++++++++----------------------------- 2 files changed, 16 insertions(+), 39 deletions(-) diff --git a/som-gc/src/gc_box.rs b/som-gc/src/gc_box.rs index 0687756a..c53065f9 100644 --- a/som-gc/src/gc_box.rs +++ b/som-gc/src/gc_box.rs @@ -1,15 +1,11 @@ use std::cell::Cell; use std::ops::{Deref, DerefMut}; -use std::ptr::NonNull; - -use crate::Trace; /// Represents a value, as it is stored within the GC. #[repr(C)] pub(crate) struct GcBox { /// Pointer to next value in the GC chain. pub(crate) marked: Cell, - pub(crate) next: Option>>, pub(crate) value: T, } @@ -19,7 +15,6 @@ impl GcBox { pub fn new(value: T) -> Self { Self { marked: Cell::new(false), - next: None, value, } } @@ -50,7 +45,6 @@ impl Default for GcBox { fn default() -> Self { Self { marked: Cell::new(false), - next: None, value: T::default(), } } diff --git a/som-gc/src/heap.rs b/som-gc/src/heap.rs index 24b73a2e..e5222f05 100644 --- a/som-gc/src/heap.rs +++ b/som-gc/src/heap.rs @@ -23,7 +23,7 @@ pub struct GcParams { pub struct GcHeap { stats: GcStats, params: GcParams, - head: Option>>, + objects: Vec>>, } impl Default for GcParams { @@ -38,11 +38,9 @@ impl Default for GcParams { impl Drop for GcHeap { // We properly drop all objects in the heap. fn drop(&mut self) { - let mut head = self.head; - while let Some(cur) = head { + for obj in self.objects.iter() { // SAFETY: we don't access that reference again after we drop it. - head = unsafe { cur.as_ref() }.next; - drop(unsafe { Box::from_raw(cur.as_ptr()) }); + drop(unsafe { Box::from_raw(obj.as_ptr()) }); } } } @@ -63,7 +61,7 @@ impl GcHeap { bytes_swept: 0, total_time_spent: Duration::ZERO, }, - head: None, + objects: Vec::default(), } } @@ -80,11 +78,11 @@ impl GcHeap { /// Allocates an object on the GC heap, returning its handle. pub fn allocate(&mut self, value: T) -> Gc { // TODO: trigger `collect_garbage` - let mut allocated = Box::new(GcBox::new(value)); - allocated.next = self.head; + let allocated = Box::new(GcBox::new(value)); // SAFETY: `self.head` is guaranteed to be properly aligned and non-null by `Box::into_raw`. let ptr = unsafe { NonNull::new_unchecked(Box::into_raw(allocated)) }; - self.head = Some(ptr as NonNull>); + self.objects + .push(ptr as NonNull>); self.stats.bytes_allocated += std::mem::size_of::>(); Gc { ptr: Cell::new(ptr), @@ -94,39 +92,24 @@ impl GcHeap { /// Clears the `mark` bits on every GC object. #[allow(unused)] fn clear_marks(&mut self) { - let mut head = self.head; - while let Some(mut cur) = head { - let cur = unsafe { cur.as_mut() }; - cur.clear_mark(); - head = cur.next; + for obj in self.objects.iter_mut() { + let obj = unsafe { obj.as_mut() }; + obj.clear_mark(); } } /// Performs a sweep on the GC heap (drops all unmarked objects). fn sweep(&mut self) { - let mut head = self.head; - let mut prev = None::>>; - while let Some(cur) = head { + self.objects.retain_mut(|obj| { // SAFETY: we don't access that reference again after we drop it. - let cur_ref = unsafe { cur.as_ref() }; - let next = cur_ref.next; - if !cur_ref.is_marked() { - if let Some(mut prev_cur) = prev { - unsafe { prev_cur.as_mut() }.next = next; - } else { - self.head = next; - } - // TODO: introduce a `Finalize`-like mechanism. - // TODO: maybe perform the drops in a separate thread. - let value = unsafe { Box::from_raw(cur.as_ptr()) }; + let is_retained = unsafe { obj.as_ref() }.marked.replace(false); + if !is_retained { + let value = unsafe { Box::from_raw(obj.as_ptr()) }; self.stats.bytes_allocated -= std::mem::size_of_val::>(&*value); drop(value); - } else { - cur_ref.marked.set(false); - prev = head; } - head = next; - } + is_retained + }); } /// Performs garbage collection (mark-and-sweep) on the GC heap. From 823b85a87d11b8eef162ed761f60a88606865a0f Mon Sep 17 00:00:00 2001 From: Nicolas Polomack Date: Wed, 8 May 2024 15:36:26 +0200 Subject: [PATCH 14/14] fix: fixed compilation error in tests --- som-interpreter-bc/tests/specialized_bc.rs | 24 ++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/som-interpreter-bc/tests/specialized_bc.rs b/som-interpreter-bc/tests/specialized_bc.rs index 8092b4f5..d6d8d66d 100644 --- a/som-interpreter-bc/tests/specialized_bc.rs +++ b/som-interpreter-bc/tests/specialized_bc.rs @@ -1,23 +1,26 @@ -use som_core::bytecode::Bytecode; -use som_core::bytecode::Bytecode::*; use std::path::PathBuf; +use som_core::bytecode::Bytecode::{self, *}; +use som_lexer::{Lexer, Token}; +use som_parser::lang; + +use som_gc::GcHeap; + use som_interpreter_bc::compiler; use som_interpreter_bc::method::MethodKind; use som_interpreter_bc::universe::Universe; -use som_lexer::{Lexer, Token}; -use som_parser::lang; -fn setup_universe() -> Universe { +fn setup_universe(heap: &mut GcHeap) -> Universe { let classpath = vec![ PathBuf::from("../core-lib/Smalltalk"), PathBuf::from("../core-lib/TestSuite/BasicInterpreterTests"), ]; - Universe::with_classpath(classpath).expect("could not setup test universe") + Universe::with_classpath(heap, classpath).expect("could not setup test universe") } fn get_bytecodes_from_method(class_txt: &str, method_name: &str) -> Vec { - let mut universe = setup_universe(); + let mut heap = GcHeap::new(); + let mut universe = setup_universe(&mut heap); let method_name_interned = universe.intern_symbol(method_name); @@ -33,7 +36,12 @@ fn get_bytecodes_from_method(class_txt: &str, method_name: &str) -> Vec