use std::sync::RwLock;
use imbl::{HashMap as ImHashMap, HashSet as ImHashSet};
use crate::{emulation::EmValue, metadata::token::Token};
#[derive(Debug)]
pub struct StaticFieldStorage {
fields: RwLock<ImHashMap<Token, EmValue>>,
initialized_types: RwLock<ImHashSet<Token>>,
}
impl StaticFieldStorage {
#[must_use]
pub fn new() -> Self {
Self {
fields: RwLock::new(ImHashMap::new()),
initialized_types: RwLock::new(ImHashSet::new()),
}
}
#[must_use]
pub fn get(&self, field_token: Token) -> Option<EmValue> {
let fields = self.fields.read().expect("fields lock poisoned");
fields.get(&field_token).cloned()
}
pub fn set(&self, field_token: Token, value: EmValue) {
let mut fields = self.fields.write().expect("fields lock poisoned");
fields.insert(field_token, value);
}
#[must_use]
pub fn contains(&self, field_token: Token) -> bool {
let fields = self.fields.read().expect("fields lock poisoned");
fields.contains_key(&field_token)
}
pub fn remove(&self, field_token: Token) -> Option<EmValue> {
let mut fields = self.fields.write().expect("fields lock poisoned");
fields.remove(&field_token)
}
#[must_use]
pub fn is_type_initialized(&self, type_token: Token) -> bool {
let initialized = self
.initialized_types
.read()
.expect("initialized lock poisoned");
initialized.contains(&type_token)
}
pub fn mark_type_initialized(&self, type_token: Token) {
let mut initialized = self
.initialized_types
.write()
.expect("initialized lock poisoned");
initialized.insert(type_token);
}
pub fn clear(&self) {
let mut fields = self.fields.write().expect("fields lock poisoned");
let mut initialized = self
.initialized_types
.write()
.expect("initialized lock poisoned");
fields.clear();
initialized.clear();
}
#[must_use]
pub fn len(&self) -> usize {
let fields = self.fields.read().expect("fields lock poisoned");
fields.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
let fields = self.fields.read().expect("fields lock poisoned");
fields.is_empty()
}
#[must_use]
pub fn field_tokens(&self) -> Vec<Token> {
let fields = self.fields.read().expect("fields lock poisoned");
fields.keys().copied().collect()
}
#[must_use]
pub fn initialized_types(&self) -> Vec<Token> {
let initialized = self
.initialized_types
.read()
.expect("initialized lock poisoned");
initialized.iter().copied().collect()
}
#[must_use]
pub fn fork(&self) -> Self {
let fields = self.fields.read().expect("fields lock poisoned");
let initialized = self
.initialized_types
.read()
.expect("initialized lock poisoned");
Self {
fields: RwLock::new(fields.clone()),
initialized_types: RwLock::new(initialized.clone()),
}
}
}
impl Default for StaticFieldStorage {
fn default() -> Self {
Self::new()
}
}
impl Clone for StaticFieldStorage {
fn clone(&self) -> Self {
self.fork()
}
}
#[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).is_none());
assert!(!storage.contains(field));
storage.set(field, EmValue::I32(42));
assert!(storage.contains(field));
assert_eq!(storage.get(field), Some(EmValue::I32(42)));
storage.set(field, EmValue::I32(100));
assert_eq!(storage.get(field), Some(EmValue::I32(100)));
let removed = storage.remove(field);
assert_eq!(removed, Some(EmValue::I32(100)));
assert!(storage.get(field).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));
storage.mark_type_initialized(type_token);
assert!(storage.is_type_initialized(type_token));
let initialized = storage.initialized_types();
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));
storage.mark_type_initialized(type_token);
assert!(!storage.is_empty());
storage.clear();
assert!(storage.is_empty());
assert!(!storage.is_type_initialized(type_token));
}
#[test]
fn test_clone() {
let storage = StaticFieldStorage::new();
let field = Token::new(0x04000001);
storage.set(field, EmValue::I32(42));
let cloned = storage.clone();
assert_eq!(cloned.get(field), Some(EmValue::I32(42)));
cloned.set(field, EmValue::I32(100));
assert_eq!(storage.get(field), Some(EmValue::I32(42)));
assert_eq!(cloned.get(field), Some(EmValue::I32(100)));
}
#[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));
storage.mark_type_initialized(type_token);
let forked = storage.fork();
assert_eq!(storage.get(field1), Some(EmValue::I32(42)));
assert_eq!(forked.get(field1), Some(EmValue::I32(42)));
assert!(storage.is_type_initialized(type_token));
assert!(forked.is_type_initialized(type_token));
forked.set(field1, EmValue::I32(100));
forked.set(field2, EmValue::I32(200));
assert_eq!(storage.get(field1), Some(EmValue::I32(42)));
assert!(storage.get(field2).is_none());
assert_eq!(forked.get(field1), Some(EmValue::I32(100)));
assert_eq!(forked.get(field2), Some(EmValue::I32(200)));
}
#[test]
fn test_fork_isolation() {
let storage = StaticFieldStorage::new();
let field = Token::new(0x04000001);
storage.set(field, EmValue::I32(1));
let fork1 = storage.fork();
let fork2 = storage.fork();
fork1.set(field, EmValue::I32(10));
fork2.set(field, EmValue::I32(20));
assert_eq!(storage.get(field), Some(EmValue::I32(1)));
assert_eq!(fork1.get(field), Some(EmValue::I32(10)));
assert_eq!(fork2.get(field), Some(EmValue::I32(20)));
}
}