[ares] Add noun acyclicality check

This commit is contained in:
Edward Amsden 2023-02-01 18:33:42 -06:00
parent dabafb685d
commit 903ffe694c
4 changed files with 62 additions and 1 deletions

View File

@ -1,4 +1,5 @@
use crate::noun::{CellMemory, IndirectAtom, Noun, NounAllocator, Cell};
use crate::assert_acyclic;
use either::Either::{self, Left, Right};
use libc::{c_void, memcmp};
use memmap::MmapMut;
@ -422,6 +423,7 @@ impl NockStack {
}
}
*self.previous_stack_pointer_pointer_east() = other_stack_pointer;
assert_acyclic!(*noun);
}
/** Copy a result noun and its subnouns from a west frame to its parent east frame
@ -532,6 +534,7 @@ impl NockStack {
}
}
*self.previous_stack_pointer_pointer_west() = other_stack_pointer;
assert_acyclic!(*noun);
}
/** Pop a frame from the (east) stack, providing a result, which will be copied to the return target
@ -733,6 +736,8 @@ pub unsafe fn unifying_equality(stack: &mut NockStack, a: *mut Noun, b: *mut Nou
}
stack.restore_prev_stack_pointer_from_local(0);
stack.pop_no_copy();
assert_acyclic!(*a);
assert_acyclic!(*b);
(*a).raw_equals(*b)
}

View File

@ -1,3 +1,4 @@
use crate::assert_acyclic;
use crate::mem::*;
use crate::noun::{Allocated, Atom, DirectAtom, Noun};
use either::Either::*;
@ -177,6 +178,7 @@ pub fn mug_u32_one(noun: Noun) -> Option<u32> {
}
pub fn mug_u32(stack: &mut NockStack, noun: Noun) -> u32 {
assert_acyclic!(noun);
stack.push(1);
unsafe {
stack.save_prev_stack_pointer_to_local(0);

View File

@ -30,6 +30,50 @@ const FORWARDING_TAG: u64 = u64::MAX & CELL_MASK;
/** Tag mask for a forwarding pointer */
const FORWARDING_MASK: u64 = CELL_MASK;
#[cfg(feature = "check_acyclic")]
#[macro_export]
macro_rules! assert_acyclic {
( $x:expr ) => {
assert!(acyclic_noun($x));
}
}
#[cfg(not(feature = "check_acyclic"))]
#[macro_export]
macro_rules! assert_acyclic {
( $x:expr ) => {
}
}
pub fn acyclic_noun(noun: Noun) -> bool {
let mut seen = IntMap::new();
acyclic_noun_go(noun, &mut seen)
}
fn acyclic_noun_go(noun: Noun, seen: &mut IntMap<()>) -> bool {
match noun.as_either_atom_cell() {
Either::Left(_atom) => true,
Either::Right(cell) => {
if let Some(_) = seen.get(cell.0) {
false
} else {
seen.insert(cell.0, ());
if acyclic_noun_go(cell.head(), seen) {
if acyclic_noun_go(cell.tail(), seen) {
seen.remove(cell.0);
true
} else {
false
}
} else {
false
}
}
}
}
}
/** Test if a noun is a direct atom. */
fn is_direct_atom(noun: u64) -> bool {
noun & DIRECT_MASK == DIRECT_TAG

View File

@ -1,3 +1,4 @@
use crate::assert_acyclic;
use crate::mem::unifying_equality;
use crate::mem::NockStack;
use crate::mug::mug_u32;
@ -34,6 +35,7 @@ pub fn cue(stack: &mut NockStack, buffer: Atom) -> Noun {
loop {
if unsafe { stack.prev_stack_pointer_equals_local(0) } {
let mut result = unsafe { *(stack.local_noun_pointer(1)) };
assert_acyclic!(result);
unsafe {
stack.pop(&mut result);
};
@ -46,9 +48,15 @@ pub fn cue(stack: &mut NockStack, buffer: Atom) -> Noun {
// 11 bits - cue backreference
cursor += 2;
unsafe {
*dest_ptr = *(backref_map
let reffed_noun = *(backref_map
.get(rub_backref(&mut cursor, buffer_bitslice))
.expect("Invalid backref in cue"));
assert_acyclic!(reffed_noun);
if let Ok(indirect) = reffed_noun.as_indirect() {
debug_assert!(indirect.size() > 0);
}
*dest_ptr = reffed_noun;
assert_acyclic!(reffed_noun);
stack.reclaim_in_previous_frame::<*mut Noun>();
}
continue;
@ -61,6 +69,8 @@ pub fn cue(stack: &mut NockStack, buffer: Atom) -> Noun {
*dest_ptr = cell.as_noun();
backref_map.insert(backref as u64, *dest_ptr);
stack.reclaim_in_previous_frame::<*mut Noun>();
(*cell_mem_ptr).tail = DirectAtom::new_unchecked(0xEDBEEF).as_atom().as_noun();
(*cell_mem_ptr).head = DirectAtom::new_unchecked(0xDEBEEF).as_atom().as_noun();
*(stack.alloc_in_previous_frame::<*mut Noun>()) =
&mut ((*cell_mem_ptr).tail);
*(stack.alloc_in_previous_frame::<*mut Noun>()) =