use std::{collections::HashMap, sync::Arc};
use crate::{emulation::HeapRef, metadata::token::Token, CilObject};
#[derive(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>,
loaded_cilobjects: Vec<Arc<CilObject>>,
}
impl std::fmt::Debug for AppDomainState {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("AppDomainState")
.field("loaded_assemblies", &self.loaded_assemblies)
.field("interned_strings_count", &self.interned_strings.len())
.field("assembly_resolve_handlers", &self.assembly_resolve_handlers)
.field("type_resolve_handlers", &self.type_resolve_handlers)
.field("executing_assembly", &self.executing_assembly)
.field("entry_assembly", &self.entry_assembly)
.field("loaded_cilobjects_count", &self.loaded_cilobjects.len())
.finish()
}
}
#[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
}
pub fn register_parsed_assembly(&mut self, asm: Arc<CilObject>) -> usize {
let index = self.loaded_cilobjects.len();
self.loaded_cilobjects.push(asm);
index
}
#[must_use]
pub fn get_parsed_assembly(&self, index: usize) -> Option<&Arc<CilObject>> {
self.loaded_cilobjects.get(index)
}
#[must_use]
pub fn find_type_across_assemblies(
&self,
namespace: &str,
name: &str,
) -> Option<(usize, Token)> {
for (index, asm) in self.loaded_cilobjects.iter().enumerate() {
for cil_type in asm.types().all_types() {
if cil_type.namespace == namespace && cil_type.name == name {
return Some((index, cil_type.token));
}
}
}
None
}
#[must_use]
pub fn parsed_assembly_count(&self) -> usize {
self.loaded_cilobjects.len()
}
}
#[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)));
}
}