83 lines
2.1 KiB
Rust
83 lines
2.1 KiB
Rust
#![no_main]
|
|
use gc::{self, gc_ref::GcRef, test_utils::GotDropped, trace::GCTrace};
|
|
use libfuzzer_sys::fuzz_target;
|
|
use std::cell::RefCell;
|
|
|
|
use libfuzzer_sys::arbitrary;
|
|
|
|
#[derive(arbitrary::Arbitrary, Debug)]
|
|
struct ChildIDx(usize);
|
|
|
|
impl ChildIDx {
|
|
fn choose<'a, T>(&self, choices: &'a [T]) -> Option<&'a T> {
|
|
let idx = self.choose_index(choices.len())?;
|
|
Some(choices.get(idx).unwrap())
|
|
}
|
|
|
|
fn choose_index(&self, len: usize) -> Option<usize> {
|
|
(len != 0).then(|| self.0 % len)
|
|
}
|
|
}
|
|
|
|
#[derive(arbitrary::Arbitrary, Debug)]
|
|
enum GraphExplore {
|
|
ExploreChild(ChildIDx, Box<GraphExplore>),
|
|
Stop,
|
|
}
|
|
|
|
#[derive(arbitrary::Arbitrary, Debug)]
|
|
enum Op {
|
|
AddLink(GraphExplore, Option<GraphExplore>),
|
|
RemoveChild(GraphExplore, ChildIDx),
|
|
CollectGarbage,
|
|
}
|
|
|
|
#[derive(GCTrace, Default)]
|
|
struct Node {
|
|
children: Vec<GCNode>,
|
|
}
|
|
|
|
type GCNode = GcRef<RefCell<Node>>;
|
|
|
|
impl Node {
|
|
fn explore(node: GCNode, instructions: GraphExplore) -> GCNode {
|
|
match instructions {
|
|
GraphExplore::ExploreChild(child_idx, child_instructions) => {
|
|
let child = child_idx
|
|
.choose(&*node.borrow().children)
|
|
.map(|v| v.clone());
|
|
child
|
|
.map(|c| Node::explore(c, *child_instructions))
|
|
.unwrap_or(node)
|
|
}
|
|
GraphExplore::Stop => node,
|
|
}
|
|
}
|
|
}
|
|
|
|
fuzz_target!(|ops: Vec<Op>| {
|
|
let mut allocator = gc::allocator::GCAllocator::default();
|
|
let root: GCNode = allocator.alloc(RefCell::default());
|
|
for op in ops {
|
|
match op {
|
|
Op::AddLink(parent_path, child_path) => {
|
|
let child = child_path
|
|
.map(|c| Node::explore(root.clone(), c))
|
|
.unwrap_or_else(|| allocator.alloc(RefCell::default()));
|
|
|
|
let parent = Node::explore(root.clone(), parent_path);
|
|
parent.borrow_mut().children.push(child);
|
|
}
|
|
Op::RemoveChild(parent_path, child_idx) => {
|
|
let parent = Node::explore(root.clone(), parent_path);
|
|
let children = &mut parent.borrow_mut().children;
|
|
let idx = child_idx.choose_index(children.len());
|
|
if let Some(idx) = idx {
|
|
children.remove(idx);
|
|
}
|
|
}
|
|
Op::CollectGarbage => unsafe { allocator.gc(&root) },
|
|
}
|
|
}
|
|
unsafe { allocator.gc(&()) };
|
|
});
|