use std::sync::{Arc, OnceLock};
use crate::{
file::File,
metadata::{
cor20header::Cor20Header,
diagnostics::{DiagnosticCategory, Diagnostics},
exports::Exports,
imports::Imports,
method::MethodMap,
resources::Resources,
root::Root,
streams::{Blob, Guid, Strings, TablesHeader, UserStrings},
tables::{
AssemblyOsRc, AssemblyProcessorRc, AssemblyRc, AssemblyRefMap, AssemblyRefOsMap,
AssemblyRefProcessorMap, ClassLayoutMap, CodedIndex, ConstantMap, CustomAttributeMap,
CustomDebugInformationMap, DeclSecurityMap, DocumentMap, EncLogMap, EncMapMap,
EventMap, EventMapEntryMap, EventPtrMap, FieldLayoutMap, FieldMap, FieldMarshalMap,
FieldPtrMap, FieldRVAMap, FileMap, GenericParamConstraintMap, GenericParamMap,
ImportScopeMap, InterfaceImplMap, LocalConstantMap, LocalScopeMap, LocalVariableMap,
MemberRefMap, MethodDebugInformationMap, MethodImplMap, MethodPtrMap,
MethodSemanticsMap, MethodSpecMap, ModuleRc, ModuleRefMap, NestedClassMap, ParamMap,
ParamPtrMap, PropertyMap, PropertyMapEntryMap, PropertyPtrMap, StandAloneSigMap,
StateMachineMethodMap, TableId, TypeSpecMap,
},
typesystem::{CilTypeReference, TypeRegistry},
},
};
pub(crate) struct LoaderContext<'a> {
pub input: Arc<File>,
pub data: &'a [u8],
pub header: &'a Cor20Header,
pub header_root: &'a Root,
pub diagnostics: &'a Arc<Diagnostics>,
pub lenient: bool,
pub meta: Option<&'a TablesHeader<'a>>,
pub strings: Option<&'a Strings<'a>>,
pub userstrings: Option<&'a UserStrings<'a>>,
pub guids: Option<&'a Guid<'a>>,
pub blobs: Option<&'a Blob<'a>>,
pub assembly: &'a OnceLock<AssemblyRc>,
pub assembly_os: &'a OnceLock<AssemblyOsRc>,
pub assembly_processor: &'a OnceLock<AssemblyProcessorRc>,
pub assembly_ref: &'a AssemblyRefMap,
pub assembly_ref_os: AssemblyRefOsMap,
pub assembly_ref_processor: AssemblyRefProcessorMap,
pub module: &'a OnceLock<ModuleRc>,
pub module_ref: &'a ModuleRefMap,
pub type_spec: TypeSpecMap,
pub method_def: &'a MethodMap,
pub method_impl: MethodImplMap,
pub method_semantics: MethodSemanticsMap,
pub method_spec: &'a MethodSpecMap,
pub field: FieldMap,
pub field_ptr: FieldPtrMap,
pub method_ptr: MethodPtrMap,
pub field_layout: FieldLayoutMap,
pub field_marshal: FieldMarshalMap,
pub field_rva: FieldRVAMap,
pub enc_log: EncLogMap,
pub enc_map: EncMapMap,
pub document: DocumentMap,
pub method_debug_information: MethodDebugInformationMap,
pub local_scope: LocalScopeMap,
pub local_variable: LocalVariableMap,
pub local_constant: LocalConstantMap,
pub import_scope: ImportScopeMap,
pub state_machine_method: StateMachineMethodMap,
pub custom_debug_information: CustomDebugInformationMap,
pub param: ParamMap,
pub param_ptr: ParamPtrMap,
pub generic_param: GenericParamMap,
pub generic_param_constraint: GenericParamConstraintMap,
pub property: PropertyMap,
pub property_ptr: PropertyPtrMap,
pub property_map: PropertyMapEntryMap,
pub event: EventMap,
pub event_ptr: EventPtrMap,
pub event_map: EventMapEntryMap,
pub member_ref: &'a MemberRefMap,
pub class_layout: ClassLayoutMap,
pub nested_class: NestedClassMap,
pub interface_impl: InterfaceImplMap,
pub constant: ConstantMap,
pub custom_attribute: CustomAttributeMap,
pub decl_security: &'a DeclSecurityMap,
pub file: &'a FileMap,
pub exported_type: &'a Exports,
pub standalone_sig: StandAloneSigMap,
pub imports: &'a Imports,
pub resources: &'a Resources,
pub types: &'a Arc<TypeRegistry>,
}
impl LoaderContext<'_> {
pub fn get_ref(&self, coded_index: &CodedIndex) -> CilTypeReference {
match coded_index.tag {
TableId::TypeDef => {
if let Some(type_def) = self.types.get(&coded_index.token) {
CilTypeReference::TypeDef(type_def.into())
} else {
CilTypeReference::None
}
}
TableId::TypeRef => {
if let Some(type_ref) = self.types.get(&coded_index.token) {
CilTypeReference::TypeRef(type_ref.into())
} else {
CilTypeReference::None
}
}
TableId::TypeSpec => {
if let Some(type_spec) = self.types.get(&coded_index.token) {
CilTypeReference::TypeSpec(type_spec.into())
} else {
CilTypeReference::None
}
}
TableId::MethodDef => {
if let Some(method_def) = self.method_def.get(&coded_index.token) {
CilTypeReference::MethodDef(method_def.value().clone().into())
} else {
CilTypeReference::None
}
}
TableId::MemberRef => {
if let Some(member_ref) = self.member_ref.get(&coded_index.token) {
CilTypeReference::MemberRef(member_ref.value().clone())
} else {
CilTypeReference::None
}
}
TableId::Field => {
if let Some(field) = self.field.get(&coded_index.token) {
CilTypeReference::Field(field.value().clone())
} else {
CilTypeReference::None
}
}
TableId::Param => {
if let Some(param) = self.param.get(&coded_index.token) {
CilTypeReference::Param(param.value().clone())
} else {
CilTypeReference::None
}
}
TableId::Property => {
if let Some(property) = self.property.get(&coded_index.token) {
CilTypeReference::Property(property.value().clone())
} else {
CilTypeReference::None
}
}
TableId::Event => {
if let Some(event) = self.event.get(&coded_index.token) {
CilTypeReference::Event(event.value().clone())
} else {
CilTypeReference::None
}
}
TableId::InterfaceImpl => {
if let Some(interface_impl) = self.interface_impl.get(&coded_index.token) {
CilTypeReference::InterfaceImpl(interface_impl.value().clone())
} else {
CilTypeReference::None
}
}
TableId::Module => {
if let Some(module) = self.module.get() {
CilTypeReference::Module(module.clone())
} else {
CilTypeReference::None
}
}
TableId::ModuleRef => {
if let Some(module_ref) = self.module_ref.get(&coded_index.token) {
CilTypeReference::ModuleRef(module_ref.value().clone())
} else {
CilTypeReference::None
}
}
TableId::Assembly => {
if let Some(assembly) = self.assembly.get() {
CilTypeReference::Assembly(assembly.clone())
} else {
CilTypeReference::None
}
}
TableId::AssemblyRef => {
if let Some(assembly_ref) = self.assembly_ref.get(&coded_index.token) {
CilTypeReference::AssemblyRef(assembly_ref.value().clone())
} else {
CilTypeReference::None
}
}
TableId::File => {
if let Some(file) = self.file.get(&coded_index.token) {
CilTypeReference::File(file.value().clone())
} else {
CilTypeReference::None
}
}
TableId::ExportedType => {
if let Some(exported_type) = self.exported_type.get(&coded_index.token) {
CilTypeReference::ExportedType(exported_type.value().clone())
} else {
CilTypeReference::None
}
}
TableId::GenericParam => {
if let Some(generic_param) = self.generic_param.get(&coded_index.token) {
CilTypeReference::GenericParam(generic_param.value().clone())
} else {
CilTypeReference::None
}
}
TableId::GenericParamConstraint => {
if let Some(constraint) = self.generic_param_constraint.get(&coded_index.token) {
CilTypeReference::GenericParamConstraint(constraint.value().clone())
} else {
CilTypeReference::None
}
}
TableId::MethodSpec => {
if let Some(method_spec) = self.method_spec.get(&coded_index.token) {
CilTypeReference::MethodSpec(method_spec.value().clone())
} else {
CilTypeReference::None
}
}
TableId::DeclSecurity => {
if let Some(decl_security) = self.decl_security.get(&coded_index.token) {
CilTypeReference::DeclSecurity(decl_security.value().clone())
} else {
CilTypeReference::None
}
}
TableId::StandAloneSig => {
if let Some(standalone_sig) = self.standalone_sig.get(&coded_index.token) {
CilTypeReference::StandAloneSig(standalone_sig.value().clone())
} else {
CilTypeReference::None
}
}
_ => CilTypeReference::None,
}
}
pub fn handle_result<T, E: std::fmt::Display, F: FnOnce() -> String>(
&self,
result: std::result::Result<T, E>,
category: DiagnosticCategory,
context_msg: F,
) -> crate::Result<Option<T>> {
match result {
Ok(value) => Ok(Some(value)),
Err(e) => {
let msg = context_msg();
if self.lenient {
self.diagnostics
.warning(category, format!("Failed to load {msg}: {e}"));
Ok(None)
} else {
Err(malformed_error!("Failed to load {}: {}", msg, e))
}
}
}
}
pub fn handle_error<E: std::fmt::Display, F: FnOnce() -> String>(
&self,
result: std::result::Result<(), E>,
category: DiagnosticCategory,
context_msg: F,
) -> crate::Result<()> {
match result {
Ok(()) => Ok(()),
Err(e) => {
let msg = context_msg();
if self.lenient {
self.diagnostics
.warning(category, format!("Failed to process {msg}: {e}"));
Ok(())
} else {
Err(malformed_error!("Failed to process {}: {}", msg, e))
}
}
}
}
}