use std::collections::BTreeSet;
use crate::env::Env;
use crate::heap::{Address, Heap};
use crate::value::Value;
enum Lookup<'a> {
Dangling,
Found(&'a Value),
}
fn lookup(heap: &Heap, address: Address) -> Lookup<'_> {
heap.load(address).map_or(Lookup::Dangling, Lookup::Found)
}
#[must_use]
pub fn reachable(roots: &[&Value], env: &Env, heap: &Heap) -> BTreeSet<Address> {
let from_env = mark_env(env, heap, BTreeSet::new());
roots
.iter()
.fold(from_env, |acc, &value| mark_value(value, heap, acc))
}
#[must_use]
pub fn collect(roots: &[&Value], env: &Env, heap: &Heap) -> Heap {
heap.retain(&reachable(roots, env, heap))
}
fn mark_env(env: &Env, heap: &Heap, live: BTreeSet<Address>) -> BTreeSet<Address> {
match env {
Env::Empty => live,
Env::Cons { value, rest, .. } => {
let live = mark_value(value, heap, live);
mark_env(rest, heap, live)
}
}
}
fn mark_value(value: &Value, heap: &Heap, live: BTreeSet<Address>) -> BTreeSet<Address> {
match value {
Value::Closure { env: capture, .. } => mark_env(capture, heap, live),
Value::Ref(address) => mark_reference(*address, heap, live),
Value::Object {
properties,
prototype,
} => mark_object(properties, *prototype, heap, live),
}
}
fn mark_object(
properties: &std::collections::BTreeMap<crate::syntax::VarName, Value>,
prototype: Option<Address>,
heap: &Heap,
live: BTreeSet<Address>,
) -> BTreeSet<Address> {
let with_proto = prototype
.iter()
.copied()
.fold(live, |acc, addr| mark_reference(addr, heap, acc));
properties
.values()
.fold(with_proto, |acc, v| mark_value(v, heap, acc))
}
fn mark_reference(address: Address, heap: &Heap, live: BTreeSet<Address>) -> BTreeSet<Address> {
if live.contains(&address) {
live
} else {
let extended: BTreeSet<Address> =
live.into_iter().chain(std::iter::once(address)).collect();
match lookup(heap, address) {
Lookup::Dangling => extended,
Lookup::Found(value) => mark_value(value, heap, extended),
}
}
}