use std::cell::Cell;
use sim_kernel::{Class, ClassId, ClassRef, Cx, Result};
pub(crate) const MAX_SHAPE_DEPTH: usize = 256;
thread_local! {
static SHAPE_DEPTH: Cell<usize> = const { Cell::new(0) };
}
pub(crate) struct DepthGuard {
_seal: (),
}
impl DepthGuard {
pub(crate) fn enter() -> Option<Self> {
SHAPE_DEPTH.with(|depth| {
let current = depth.get();
if current >= MAX_SHAPE_DEPTH {
None
} else {
depth.set(current + 1);
Some(Self { _seal: () })
}
})
}
}
impl Drop for DepthGuard {
fn drop(&mut self) {
SHAPE_DEPTH.with(|depth| depth.set(depth.get().saturating_sub(1)));
}
}
pub(crate) fn class_is_subclass_of_guarded(
cx: &mut Cx,
child: &dyn Class,
expected: ClassRef,
) -> Result<bool> {
let Some(expected_class) = expected.object().as_class() else {
return Ok(false);
};
let expected_id = expected_class.id();
let mut visited = Vec::new();
subclass_walk(cx, child, expected_id, &mut visited)
}
pub(crate) fn is_cyclic_parent_edge(
cx: &mut Cx,
child_id: ClassId,
parent: &dyn Class,
) -> Result<bool> {
let mut visited = Vec::new();
subclass_walk(cx, parent, child_id, &mut visited)
}
fn subclass_walk(
cx: &mut Cx,
child: &dyn Class,
expected_id: ClassId,
visited: &mut Vec<ClassId>,
) -> Result<bool> {
let child_id = child.id();
if child_id == expected_id {
return Ok(true);
}
if visited.contains(&child_id) {
return Ok(false);
}
visited.push(child_id);
for parent in child.parents(cx)? {
let Some(parent) = parent.object().as_class() else {
continue;
};
if subclass_walk(cx, parent, expected_id, visited)? {
return Ok(true);
}
}
Ok(false)
}