use std::sync::atomic::{AtomicBool, Ordering};
use dashmap::DashSet;
use crate::metadata::token::Token;
pub struct ProcessingState {
pub needs_ssa_build: DashSet<Token>,
pub method_dirty: DashSet<Token>,
pub method_stable: DashSet<Token>,
pub newly_detected: DashSet<Token>,
pub type_dirty: DashSet<Token>,
pub type_stable: DashSet<Token>,
assembly_dirty: AtomicBool,
}
impl ProcessingState {
#[must_use]
pub fn new() -> Self {
Self {
needs_ssa_build: DashSet::new(),
method_dirty: DashSet::new(),
method_stable: DashSet::new(),
newly_detected: DashSet::new(),
type_dirty: DashSet::new(),
type_stable: DashSet::new(),
assembly_dirty: AtomicBool::new(false),
}
}
#[must_use]
pub fn from_methods(methods: impl IntoIterator<Item = Token>) -> Self {
let state = Self::new();
for token in methods {
state.method_dirty.insert(token);
}
state
}
pub fn mark_method_dirty(&self, token: Token) {
self.method_stable.remove(&token);
self.method_dirty.insert(token);
}
pub fn mark_method_stable(&self, token: Token) {
self.method_dirty.remove(&token);
self.method_stable.insert(token);
}
pub fn mark_needs_ssa_build(&self, token: Token) {
self.method_dirty.remove(&token);
self.method_stable.remove(&token);
self.needs_ssa_build.insert(token);
}
pub fn mark_ssa_built(&self, token: Token) {
self.needs_ssa_build.remove(&token);
self.method_dirty.insert(token);
}
pub fn mark_type_dirty(&self, token: Token) {
self.type_stable.remove(&token);
self.type_dirty.insert(token);
}
pub fn mark_type_stable(&self, token: Token) {
self.type_dirty.remove(&token);
self.type_stable.insert(token);
}
pub fn mark_assembly_dirty(&self) {
self.assembly_dirty.store(true, Ordering::Release);
}
pub fn clear_assembly_dirty(&self) {
self.assembly_dirty.store(false, Ordering::Release);
}
#[must_use]
pub fn is_assembly_dirty(&self) -> bool {
self.assembly_dirty.load(Ordering::Acquire)
}
#[must_use]
pub fn has_pending_work(&self) -> bool {
!self.method_dirty.is_empty()
|| !self.needs_ssa_build.is_empty()
|| !self.newly_detected.is_empty()
|| !self.type_dirty.is_empty()
|| self.is_assembly_dirty()
}
#[must_use]
pub fn dirty_method_count(&self) -> usize {
self.method_dirty.len()
}
#[must_use]
pub fn stable_method_count(&self) -> usize {
self.method_stable.len()
}
}
impl Default for ProcessingState {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use crate::{compiler::state::ProcessingState, metadata::token::Token};
#[test]
fn test_from_methods() {
let tokens: Vec<Token> = (1..=5).map(|i| Token::new(0x06000000 + i)).collect();
let state = ProcessingState::from_methods(tokens.clone());
for token in &tokens {
assert!(state.method_dirty.contains(token));
assert!(!state.method_stable.contains(token));
}
assert_eq!(state.dirty_method_count(), 5);
assert_eq!(state.stable_method_count(), 0);
}
#[test]
fn test_mark_dirty_removes_stable() {
let state = ProcessingState::new();
let token = Token::new(0x06000001);
state.mark_method_stable(token);
assert!(state.method_stable.contains(&token));
assert!(!state.method_dirty.contains(&token));
state.mark_method_dirty(token);
assert!(state.method_dirty.contains(&token));
assert!(!state.method_stable.contains(&token));
}
#[test]
fn test_mark_stable_removes_dirty() {
let state = ProcessingState::new();
let token = Token::new(0x06000001);
state.mark_method_dirty(token);
assert!(state.method_dirty.contains(&token));
state.mark_method_stable(token);
assert!(state.method_stable.contains(&token));
assert!(!state.method_dirty.contains(&token));
}
#[test]
fn test_needs_ssa_build_clears_both() {
let state = ProcessingState::new();
let token = Token::new(0x06000001);
state.method_dirty.insert(token);
state.method_stable.insert(token);
state.mark_needs_ssa_build(token);
assert!(state.needs_ssa_build.contains(&token));
assert!(!state.method_dirty.contains(&token));
assert!(!state.method_stable.contains(&token));
}
#[test]
fn test_ssa_built_transitions_to_dirty() {
let state = ProcessingState::new();
let token = Token::new(0x06000001);
state.mark_needs_ssa_build(token);
assert!(state.needs_ssa_build.contains(&token));
state.mark_ssa_built(token);
assert!(!state.needs_ssa_build.contains(&token));
assert!(state.method_dirty.contains(&token));
}
#[test]
fn test_type_transitions() {
let state = ProcessingState::new();
let token = Token::new(0x02000001);
state.mark_type_dirty(token);
assert!(state.type_dirty.contains(&token));
assert!(!state.type_stable.contains(&token));
state.mark_type_stable(token);
assert!(!state.type_dirty.contains(&token));
assert!(state.type_stable.contains(&token));
state.mark_type_dirty(token);
assert!(state.type_dirty.contains(&token));
assert!(!state.type_stable.contains(&token));
}
#[test]
fn test_assembly_dirty_flag() {
let state = ProcessingState::new();
assert!(!state.is_assembly_dirty());
state.mark_assembly_dirty();
assert!(state.is_assembly_dirty());
state.clear_assembly_dirty();
assert!(!state.is_assembly_dirty());
}
#[test]
fn test_has_pending_work() {
let state = ProcessingState::new();
assert!(!state.has_pending_work());
state.mark_method_dirty(Token::new(0x06000001));
assert!(state.has_pending_work());
state.mark_method_stable(Token::new(0x06000001));
assert!(!state.has_pending_work());
state.mark_needs_ssa_build(Token::new(0x06000002));
assert!(state.has_pending_work());
state.mark_ssa_built(Token::new(0x06000002));
assert!(state.has_pending_work());
state.mark_method_stable(Token::new(0x06000002));
assert!(!state.has_pending_work());
state.newly_detected.insert(Token::new(0x06000003));
assert!(state.has_pending_work());
state.newly_detected.remove(&Token::new(0x06000003));
assert!(!state.has_pending_work());
state.mark_type_dirty(Token::new(0x02000001));
assert!(state.has_pending_work());
state.mark_type_stable(Token::new(0x02000001));
assert!(!state.has_pending_work());
state.mark_assembly_dirty();
assert!(state.has_pending_work());
state.clear_assembly_dirty();
assert!(!state.has_pending_work());
}
#[test]
fn test_new_is_empty() {
let state = ProcessingState::new();
assert!(!state.has_pending_work());
assert_eq!(state.dirty_method_count(), 0);
assert_eq!(state.stable_method_count(), 0);
assert!(state.needs_ssa_build.is_empty());
assert!(state.newly_detected.is_empty());
assert!(state.type_dirty.is_empty());
assert!(state.type_stable.is_empty());
assert!(!state.is_assembly_dirty());
}
}