use std::ffi::c_void;
use vj_abi::{SimHandle, SlotId};
use super::write::VcState;
#[repr(C)]
pub struct StateKernelCtxFields {
pub arena_resolution_cb: extern "C" fn(handle: SimHandle, net: SlotId),
pub vc_side_channel: *mut c_void,
}
impl StateKernelCtxFields {
#[allow(dead_code)]
pub(crate) fn new(
arena_resolution_cb: extern "C" fn(handle: SimHandle, net: SlotId),
vc: &std::rc::Rc<VcState>,
) -> Self {
StateKernelCtxFields {
arena_resolution_cb,
vc_side_channel: std::rc::Rc::as_ptr(vc) as *mut VcState as *mut c_void,
}
}
}
pub trait PureKernel {}
#[cfg(test)]
mod tests {
use super::*;
use crate::state::instance::{StateEngine, TemplateRegistry};
use crate::state::write::{SlotLocation, commit_write_at};
use crate::state::{FourStateValue, SlotClass};
use vj_abi::SimTime;
fn fresh_engine() -> StateEngine {
StateEngine::new(TemplateRegistry::new())
}
fn narrow_word(val: u64, unk: u64) -> u128 {
(val as u128) | ((unk as u128) << 64)
}
fn t(units: i64) -> SimTime {
SimTime {
sim_units: units,
delta: 0,
}
}
extern "C" fn dummy_resolution_cb(_handle: SimHandle, _net: SlotId) {}
#[test]
fn state_kernel_ctx_fields_has_exactly_the_two_named_fields() {
let fields = StateKernelCtxFields {
arena_resolution_cb: dummy_resolution_cb,
vc_side_channel: std::ptr::null_mut(),
};
assert_eq!(fields.vc_side_channel, std::ptr::null_mut());
(fields.arena_resolution_cb)(SimHandle(std::ptr::null_mut()), SlotId(0));
}
fn forbidden_mutable_global_patterns() -> Vec<String> {
vec![["static", " mut"].concat(), ["lazy", "_static!"].concat()]
}
fn production_code(src: &str) -> String {
let marker = "#[cfg(test)]";
let mut result = String::with_capacity(src.len());
let mut cursor = 0usize;
loop {
let Some(rel_idx) = src[cursor..].find(marker) else {
result.push_str(&src[cursor..]);
break;
};
let idx = cursor + rel_idx;
let after = &src[idx + marker.len()..];
let after_trimmed = after.trim_start();
if after_trimmed.starts_with("mod tests") {
let trim_offset = after.len() - after_trimmed.len();
let mod_tests_abs = idx + marker.len() + trim_offset;
let after_keyword = &src[mod_tests_abs + "mod tests".len()..];
let brace_abs = after_keyword
.find('{')
.map(|rel| mod_tests_abs + "mod tests".len() + rel);
let close_abs = brace_abs.and_then(|b| find_matching_brace(src, b));
match close_abs {
Some(close_abs) => {
result.push_str(&src[cursor..idx]);
cursor = close_abs + 1;
continue;
}
None => {
result.push_str(&src[cursor..idx]);
break;
}
}
}
result.push_str(&src[cursor..idx + marker.len()]);
cursor = idx + marker.len();
}
result
}
fn find_matching_brace(src: &str, open_idx: usize) -> Option<usize> {
let bytes = src.as_bytes();
debug_assert_eq!(bytes.get(open_idx), Some(&b'{'));
let mut depth: i32 = 0;
let mut i = open_idx;
while i < bytes.len() {
match bytes[i] {
b'{' => {
depth += 1;
i += 1;
}
b'}' => {
depth -= 1;
if depth == 0 {
return Some(i);
}
i += 1;
}
b'/' if bytes.get(i + 1) == Some(&b'/') => {
while i < bytes.len() && bytes[i] != b'\n' {
i += 1;
}
}
b'/' if bytes.get(i + 1) == Some(&b'*') => {
i += 2;
let mut comment_depth = 1u32;
while i < bytes.len() && comment_depth > 0 {
if bytes[i] == b'/' && bytes.get(i + 1) == Some(&b'*') {
comment_depth += 1;
i += 2;
} else if bytes[i] == b'*' && bytes.get(i + 1) == Some(&b'/') {
comment_depth -= 1;
i += 2;
} else {
i += 1;
}
}
}
b'"' => {
i += 1;
while i < bytes.len() && bytes[i] != b'"' {
if bytes[i] == b'\\' {
i += 1;
}
i += 1;
}
i += 1;
}
b'\'' => {
const MAX_CHAR_LITERAL_LEN: usize = 12;
let window_end = (i + 1 + MAX_CHAR_LITERAL_LEN).min(bytes.len());
let mut j = i + 1;
let mut found_close = None;
while j < window_end {
if bytes[j] == b'\n' {
break;
}
if bytes[j] == b'\'' && j > i + 1 {
found_close = Some(j);
break;
}
j += 1;
}
match found_close {
Some(close) => i = close + 1,
None => i += 1,
}
}
_ => {
i += 1;
}
}
}
None
}
#[test]
fn no_state_owned_api_exposes_a_thread_local_or_global_mutable_handle_to_a_kernel() {
let forbidden = forbidden_mutable_global_patterns();
let state_dir = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("src/state");
let entries = std::fs::read_dir(&state_dir).expect("state/ directory is readable");
let mut thread_local_hits: Vec<String> = Vec::new();
for entry in entries {
let entry = entry.expect("directory entry is readable");
let path = entry.path();
if path.extension().and_then(|e| e.to_str()) != Some("rs") {
continue;
}
let contents = std::fs::read_to_string(&path)
.unwrap_or_else(|e| panic!("{} must be readable: {e}", path.display()));
let scanned = production_code(&contents);
let file_name = path.file_name().unwrap().to_string_lossy().into_owned();
for pattern in &forbidden {
assert!(
!scanned.contains(pattern.as_str()),
"{file_name}'s production code must not contain a `{pattern}` -- \
no STATE-owned API may expose a hidden mutable global to a kernel"
);
}
for _ in 0..scanned.matches("thread_local!").count() {
thread_local_hits.push(file_name.clone());
}
}
thread_local_hits.sort();
assert_eq!(
thread_local_hits,
vec!["event_plan.rs".to_string(), "write.rs".to_string()],
"exactly two `thread_local!` declarations must exist in state/'s production \
code -- Task 8's documented WORKER_CACHE speculative-buffer cache in write.rs, \
and event_plan.rs's read-only design-scope PLAN (the design-wide event pool \
plan, installed per design by EventPlanScope like the other thread-local \
design scopes; it holds no kernel handle) -- and nothing else; \
found: {thread_local_hits:?}"
);
}
#[test]
fn thread_local_scan_machinery_catches_a_synthetic_second_declaration() {
let shaped = concat!(
"pub struct Whatever;\n",
"\n",
"thread_local! {\n",
" static SNEAKY: std::cell::RefCell<u32> = const { std::cell::RefCell::new(0) };\n",
"}\n",
"\n",
"#[cfg(test)]\n",
"mod tests {\n",
" // real test code, excluded from the scan\n",
"}\n",
);
let scanned = production_code(shaped);
assert_eq!(
scanned.matches("thread_local!").count(),
1,
"the synthetic fixture's own injected thread_local! must survive the \
mod-tests-exclusion scan (it lives outside `mod tests`) so the real test's \
exactly-one-total-across-the-crate check would trip on it"
);
let forbidden = forbidden_mutable_global_patterns();
let static_mut_shaped = "pub static mut SNEAKY_GLOBAL: u32 = 0;\n";
assert!(
forbidden
.iter()
.any(|p| static_mut_shaped.contains(p.as_str())),
"a reintroduced `static mut` must be caught by the forbidden-pattern list"
);
let lazy_static_shaped = "lazy_static! { static ref SNEAKY: u32 = 0; }\n";
assert!(
forbidden
.iter()
.any(|p| lazy_static_shaped.contains(p.as_str())),
"a reintroduced `lazy_static!` must be caught by the forbidden-pattern list"
);
}
#[test]
fn vc_side_channel_pointer_round_trips_to_the_same_speculative_buffer() {
let mut engine = fresh_engine();
let word_offset = unsafe { engine.arena.alloc_inline_word() };
let loc = SlotLocation {
class: SlotClass::Inline,
word_offset,
bit_width: 8,
elem_word_count: 1,
row_stride_words: 0,
num_rows: 0,
};
let cell = engine.alloc_cell(loc);
let fields = StateKernelCtxFields::new(dummy_resolution_cb, &engine.vc);
let vc_ref: &VcState = unsafe { &*(fields.vc_side_channel as *const VcState) };
engine.set_current_time(t(5));
let at = t(5);
let result = commit_write_at(
&engine.arena,
vc_ref,
loc,
cell,
FourStateValue::Narrow(narrow_word(0x2A, 0)),
&mut (),
);
assert!(
result.changed,
"the write through the recovered side channel must register as a change"
);
engine.advance_gvt(at);
let drained = engine.drain_committed_changes(at);
assert_eq!(
drained.len(),
1,
"exactly the one record appended through the side channel must be visible"
);
assert_eq!(drained[0].cell, cell);
match &drained[0].new {
FourStateValue::Narrow(w) => assert_eq!(*w, narrow_word(0x2A, 0)),
FourStateValue::Wide { .. } => panic!("expected Narrow"),
}
let mut engine2 = fresh_engine();
let word_offset2 = unsafe { engine2.arena.alloc_inline_word() };
let loc2 = SlotLocation {
class: SlotClass::Inline,
word_offset: word_offset2,
bit_width: 8,
elem_word_count: 1,
row_stride_words: 0,
num_rows: 0,
};
let cell2 = engine2.alloc_cell(loc2);
let fields2 = StateKernelCtxFields::new(dummy_resolution_cb, &engine2.vc);
let vc_ref2: &VcState = unsafe { &*(fields2.vc_side_channel as *const VcState) };
engine2.set_current_time(t(10));
commit_write_at(
&engine2.arena,
vc_ref2,
loc2,
cell2,
FourStateValue::Narrow(narrow_word(0x7, 0)),
&mut (),
);
let too_early = engine2.drain_committed_changes(t(0));
assert!(
too_early.is_empty(),
"before advance_gvt reaches the write's timestamp, drain_committed_changes must not see it"
);
}
}