use std::sync::RwLock;
use imbl::HashMap as ImHashMap;
use crate::{
emulation::{engine::EmulationError, EmValue},
metadata::token::Token,
Result,
};
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum TypeInitState {
Uninitialized,
InProgress,
Initialized,
}
#[derive(Debug)]
pub struct StaticFieldStorage {
fields: RwLock<ImHashMap<Token, EmValue>>,
type_init_state: RwLock<ImHashMap<Token, TypeInitState>>,
}
impl StaticFieldStorage {
#[must_use]
pub fn new() -> Self {
Self {
fields: RwLock::new(ImHashMap::new()),
type_init_state: RwLock::new(ImHashMap::new()),
}
}
pub fn get(&self, field_token: Token) -> Result<Option<EmValue>> {
let fields = self
.fields
.read()
.map_err(|_| EmulationError::LockPoisoned {
description: "static field storage",
})?;
Ok(fields.get(&field_token).cloned())
}
pub fn set(&self, field_token: Token, value: EmValue) -> Result<()> {
let mut fields = self
.fields
.write()
.map_err(|_| EmulationError::LockPoisoned {
description: "static field storage",
})?;
fields.insert(field_token, value);
Ok(())
}
pub fn contains(&self, field_token: Token) -> Result<bool> {
let fields = self
.fields
.read()
.map_err(|_| EmulationError::LockPoisoned {
description: "static field storage",
})?;
Ok(fields.contains_key(&field_token))
}
pub fn remove(&self, field_token: Token) -> Result<Option<EmValue>> {
let mut fields = self
.fields
.write()
.map_err(|_| EmulationError::LockPoisoned {
description: "static field storage",
})?;
Ok(fields.remove(&field_token))
}
pub fn is_type_initialized(&self, type_token: Token) -> Result<bool> {
let state = self
.type_init_state
.read()
.map_err(|_| EmulationError::LockPoisoned {
description: "static field storage",
})?;
Ok(matches!(
state.get(&type_token),
Some(TypeInitState::InProgress | TypeInitState::Initialized)
))
}
pub fn type_init_state(&self, type_token: Token) -> Result<TypeInitState> {
let state = self
.type_init_state
.read()
.map_err(|_| EmulationError::LockPoisoned {
description: "static field storage",
})?;
Ok(state
.get(&type_token)
.copied()
.unwrap_or(TypeInitState::Uninitialized))
}
pub fn set_type_init_state(&self, type_token: Token, init_state: TypeInitState) -> Result<()> {
let mut state = self
.type_init_state
.write()
.map_err(|_| EmulationError::LockPoisoned {
description: "static field storage",
})?;
state.insert(type_token, init_state);
Ok(())
}
pub fn mark_type_initialized(&self, type_token: Token) -> Result<()> {
self.set_type_init_state(type_token, TypeInitState::Initialized)
}
pub fn clear(&self) -> Result<()> {
let mut fields = self
.fields
.write()
.map_err(|_| EmulationError::LockPoisoned {
description: "static field storage",
})?;
let mut state = self
.type_init_state
.write()
.map_err(|_| EmulationError::LockPoisoned {
description: "static field storage",
})?;
fields.clear();
state.clear();
Ok(())
}
pub fn len(&self) -> Result<usize> {
let fields = self
.fields
.read()
.map_err(|_| EmulationError::LockPoisoned {
description: "static field storage",
})?;
Ok(fields.len())
}
pub fn is_empty(&self) -> Result<bool> {
let fields = self
.fields
.read()
.map_err(|_| EmulationError::LockPoisoned {
description: "static field storage",
})?;
Ok(fields.is_empty())
}
pub fn field_tokens(&self) -> Result<Vec<Token>> {
let fields = self
.fields
.read()
.map_err(|_| EmulationError::LockPoisoned {
description: "static field storage",
})?;
Ok(fields.keys().copied().collect())
}
pub fn initialized_types(&self) -> Result<Vec<Token>> {
let state = self
.type_init_state
.read()
.map_err(|_| EmulationError::LockPoisoned {
description: "static field storage",
})?;
Ok(state
.iter()
.filter(|(_, s)| matches!(s, TypeInitState::Initialized))
.map(|(t, _)| *t)
.collect())
}
pub fn fork(&self) -> Result<Self> {
let fields = self
.fields
.read()
.map_err(|_| EmulationError::LockPoisoned {
description: "static field storage",
})?;
let state = self
.type_init_state
.read()
.map_err(|_| EmulationError::LockPoisoned {
description: "static field storage",
})?;
Ok(Self {
fields: RwLock::new(fields.clone()),
type_init_state: RwLock::new(state.clone()),
})
}
}
impl Default for StaticFieldStorage {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use crate::{
emulation::{memory::statics::StaticFieldStorage, EmValue},
metadata::token::Token,
};
#[test]
fn test_static_field_storage() {
let storage = StaticFieldStorage::new();
let field = Token::new(0x04000001);
assert!(storage.get(field).unwrap().is_none());
assert!(!storage.contains(field).unwrap());
storage.set(field, EmValue::I32(42)).unwrap();
assert!(storage.contains(field).unwrap());
assert_eq!(storage.get(field).unwrap(), Some(EmValue::I32(42)));
storage.set(field, EmValue::I32(100)).unwrap();
assert_eq!(storage.get(field).unwrap(), Some(EmValue::I32(100)));
let removed = storage.remove(field).unwrap();
assert_eq!(removed, Some(EmValue::I32(100)));
assert!(storage.get(field).unwrap().is_none());
}
#[test]
fn test_type_initialization_tracking() {
let storage = StaticFieldStorage::new();
let type_token = Token::new(0x02000001);
assert!(!storage.is_type_initialized(type_token).unwrap());
storage.mark_type_initialized(type_token).unwrap();
assert!(storage.is_type_initialized(type_token).unwrap());
let initialized = storage.initialized_types().unwrap();
assert!(initialized.contains(&type_token));
}
#[test]
fn test_clear() {
let storage = StaticFieldStorage::new();
let field = Token::new(0x04000001);
let type_token = Token::new(0x02000001);
storage.set(field, EmValue::I32(42)).unwrap();
storage.mark_type_initialized(type_token).unwrap();
assert!(!storage.is_empty().unwrap());
storage.clear().unwrap();
assert!(storage.is_empty().unwrap());
assert!(!storage.is_type_initialized(type_token).unwrap());
}
#[test]
fn test_fork() {
let storage = StaticFieldStorage::new();
let field1 = Token::new(0x04000001);
let field2 = Token::new(0x04000002);
let type_token = Token::new(0x02000001);
storage.set(field1, EmValue::I32(42)).unwrap();
storage.mark_type_initialized(type_token).unwrap();
let forked = storage.fork().unwrap();
assert_eq!(storage.get(field1).unwrap(), Some(EmValue::I32(42)));
assert_eq!(forked.get(field1).unwrap(), Some(EmValue::I32(42)));
assert!(storage.is_type_initialized(type_token).unwrap());
assert!(forked.is_type_initialized(type_token).unwrap());
forked.set(field1, EmValue::I32(100)).unwrap();
forked.set(field2, EmValue::I32(200)).unwrap();
assert_eq!(storage.get(field1).unwrap(), Some(EmValue::I32(42)));
assert!(storage.get(field2).unwrap().is_none());
assert_eq!(forked.get(field1).unwrap(), Some(EmValue::I32(100)));
assert_eq!(forked.get(field2).unwrap(), Some(EmValue::I32(200)));
}
#[test]
fn test_fork_isolation() {
let storage = StaticFieldStorage::new();
let field = Token::new(0x04000001);
storage.set(field, EmValue::I32(1)).unwrap();
let fork1 = storage.fork().unwrap();
let fork2 = storage.fork().unwrap();
fork1.set(field, EmValue::I32(10)).unwrap();
fork2.set(field, EmValue::I32(20)).unwrap();
assert_eq!(storage.get(field).unwrap(), Some(EmValue::I32(1)));
assert_eq!(fork1.get(field).unwrap(), Some(EmValue::I32(10)));
assert_eq!(fork2.get(field).unwrap(), Some(EmValue::I32(20)));
}
}