use std::collections::HashMap;
use crate::{emulation::HeapRef, metadata::token::Token};
#[derive(Debug, Default)]
pub struct AppDomainState {
loaded_assemblies: HashMap<String, LoadedAssemblyInfo>,
interned_strings: HashMap<String, HeapRef>,
assembly_resolve_handlers: Vec<Token>,
type_resolve_handlers: Vec<Token>,
executing_assembly: Option<Token>,
entry_assembly: Option<Token>,
}
#[derive(Clone, Debug)]
pub struct LoadedAssemblyInfo {
pub name: String,
pub token: Token,
pub full_name: Option<String>,
pub location: Option<String>,
pub loaded_from_bytes: bool,
}
impl AppDomainState {
#[must_use]
pub fn new() -> Self {
Self::default()
}
pub fn register_assembly(&mut self, info: LoadedAssemblyInfo) {
self.loaded_assemblies.insert(info.name.clone(), info);
}
#[must_use]
pub fn get_assembly(&self, name: &str) -> Option<&LoadedAssemblyInfo> {
self.loaded_assemblies.get(name)
}
pub fn loaded_assemblies(&self) -> impl Iterator<Item = &LoadedAssemblyInfo> {
self.loaded_assemblies.values()
}
pub fn set_executing_assembly(&mut self, token: Token) {
self.executing_assembly = Some(token);
}
#[must_use]
pub fn executing_assembly(&self) -> Option<Token> {
self.executing_assembly
}
pub fn set_entry_assembly(&mut self, token: Token) {
self.entry_assembly = Some(token);
}
#[must_use]
pub fn entry_assembly(&self) -> Option<Token> {
self.entry_assembly
}
pub fn intern_string(&mut self, value: String, heap_ref: HeapRef) -> HeapRef {
if let Some(&existing) = self.interned_strings.get(&value) {
existing
} else {
self.interned_strings.insert(value, heap_ref);
heap_ref
}
}
#[must_use]
pub fn get_interned(&self, value: &str) -> Option<HeapRef> {
self.interned_strings.get(value).copied()
}
pub fn add_assembly_resolve_handler(&mut self, handler: Token) {
self.assembly_resolve_handlers.push(handler);
}
#[must_use]
pub fn assembly_resolve_handlers(&self) -> &[Token] {
&self.assembly_resolve_handlers
}
pub fn add_type_resolve_handler(&mut self, handler: Token) {
self.type_resolve_handlers.push(handler);
}
#[must_use]
pub fn type_resolve_handlers(&self) -> &[Token] {
&self.type_resolve_handlers
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_app_domain_creation() {
let domain = AppDomainState::new();
assert!(domain.loaded_assemblies().next().is_none());
}
#[test]
fn test_register_assembly() {
let mut domain = AppDomainState::new();
let info = LoadedAssemblyInfo {
name: "TestAssembly".to_string(),
token: Token::new(0x20000001),
full_name: Some("TestAssembly, Version=1.0.0.0".to_string()),
location: None,
loaded_from_bytes: false,
};
domain.register_assembly(info);
let loaded = domain.get_assembly("TestAssembly");
assert!(loaded.is_some());
assert_eq!(loaded.unwrap().token, Token::new(0x20000001));
}
#[test]
fn test_string_interning() {
let mut domain = AppDomainState::new();
let ref1 = HeapRef::new(1);
let ref2 = HeapRef::new(2);
let result1 = domain.intern_string("hello".to_string(), ref1);
assert_eq!(result1, ref1);
let result2 = domain.intern_string("hello".to_string(), ref2);
assert_eq!(result2, ref1);
let result3 = domain.intern_string("world".to_string(), ref2);
assert_eq!(result3, ref2);
}
#[test]
fn test_executing_assembly() {
let mut domain = AppDomainState::new();
assert!(domain.executing_assembly().is_none());
domain.set_executing_assembly(Token::new(0x20000001));
assert_eq!(domain.executing_assembly(), Some(Token::new(0x20000001)));
}
}