use std::sync::{Arc, OnceLock};
use crossbeam_skiplist::SkipMap;
use crate::{
metadata::{
cilassemblyview::CilAssemblyView,
exports::{NativeExports, UnifiedExportContainer},
imports::{NativeImports, UnifiedImportContainer},
loader::{execute_loaders_in_parallel, LoaderContext},
method::MethodMap,
resources::Resources,
tables::{
AssemblyOsRc, AssemblyProcessorRc, AssemblyRc, AssemblyRefMap, DeclSecurityMap,
FileMap, MemberRefMap, MethodSpecMap, ModuleRc, ModuleRefMap,
},
typesystem::TypeRegistry,
},
project::ProjectContext,
Result,
};
pub(crate) struct CilObjectData {
pub refs_assembly: AssemblyRefMap,
pub refs_module: ModuleRefMap,
pub refs_member: MemberRefMap,
pub refs_file: FileMap,
pub decl_security: DeclSecurityMap,
pub module: OnceLock<ModuleRc>,
pub assembly: OnceLock<AssemblyRc>,
pub assembly_os: OnceLock<AssemblyOsRc>,
pub assembly_processor: OnceLock<AssemblyProcessorRc>,
pub types: Arc<TypeRegistry>,
pub import_container: UnifiedImportContainer,
pub export_container: UnifiedExportContainer,
pub methods: MethodMap,
pub method_specs: MethodSpecMap,
pub resources: Resources,
}
impl CilObjectData {
pub(crate) fn from_assembly_view(
view: &CilAssemblyView,
project_context: Option<&ProjectContext>,
lenient: bool,
) -> Result<Self> {
let real_identity = view.identity()?;
let type_registry_identity = view.identity_or_fallback()?;
let mut cil_object = CilObjectData {
refs_assembly: SkipMap::default(),
refs_module: SkipMap::default(),
refs_member: SkipMap::default(),
refs_file: SkipMap::default(),
decl_security: SkipMap::default(),
module: OnceLock::new(),
assembly: OnceLock::new(),
assembly_os: OnceLock::new(),
assembly_processor: OnceLock::new(),
types: Arc::new(TypeRegistry::new(type_registry_identity)?),
import_container: UnifiedImportContainer::new(),
export_container: UnifiedExportContainer::new(),
methods: SkipMap::default(),
method_specs: SkipMap::default(),
resources: Resources::new(view.file().clone()),
};
let (native_imports, native_exports) = load_native_tables(view)?;
*cil_object.import_container.native_mut() = native_imports;
*cil_object.export_container.native_mut() = native_exports;
if let (Some(context), Some(identity)) = (project_context, real_identity) {
context.register_and_wait_stage1(identity, cil_object.types.clone())?;
context.link_all_registries(&cil_object.types);
}
{
let context = LoaderContext {
input: view.file().clone(),
data: view.data(),
header: view.cor20header(),
header_root: view.metadata_root(),
diagnostics: view.diagnostics(),
lenient,
meta: view.tables(),
strings: view.strings(),
userstrings: view.userstrings(),
guids: view.guids(),
blobs: view.blobs(),
assembly: &cil_object.assembly,
assembly_os: &cil_object.assembly_os,
assembly_processor: &cil_object.assembly_processor,
assembly_ref: &cil_object.refs_assembly,
assembly_ref_os: SkipMap::default(),
assembly_ref_processor: SkipMap::default(),
module: &cil_object.module,
module_ref: &cil_object.refs_module,
type_spec: SkipMap::default(),
method_def: &cil_object.methods,
method_impl: SkipMap::default(),
method_semantics: SkipMap::default(),
method_spec: &cil_object.method_specs,
field: SkipMap::default(),
field_ptr: SkipMap::default(),
method_ptr: SkipMap::default(),
field_layout: SkipMap::default(),
field_marshal: SkipMap::default(),
field_rva: SkipMap::default(),
enc_log: SkipMap::default(),
enc_map: SkipMap::default(),
document: SkipMap::default(),
method_debug_information: SkipMap::default(),
local_scope: SkipMap::default(),
local_variable: SkipMap::default(),
local_constant: SkipMap::default(),
import_scope: SkipMap::default(),
state_machine_method: SkipMap::default(),
custom_debug_information: SkipMap::default(),
param: SkipMap::default(),
param_ptr: SkipMap::default(),
generic_param: SkipMap::default(),
generic_param_constraint: SkipMap::default(),
property: SkipMap::default(),
property_ptr: SkipMap::default(),
property_map: SkipMap::default(),
event: SkipMap::default(),
event_ptr: SkipMap::default(),
event_map: SkipMap::default(),
member_ref: &cil_object.refs_member,
class_layout: SkipMap::default(),
nested_class: SkipMap::default(),
interface_impl: SkipMap::default(),
constant: SkipMap::default(),
custom_attribute: SkipMap::default(),
decl_security: &cil_object.decl_security,
file: &cil_object.refs_file,
exported_type: cil_object.export_container.cil(),
standalone_sig: SkipMap::default(),
imports: cil_object.import_container.cil(),
resources: &cil_object.resources,
types: &cil_object.types,
};
execute_loaders_in_parallel(&context, project_context)?;
if let Some(proj_ctx) = project_context {
proj_ctx.wait_stage4()?;
}
}
Ok(cil_object)
}
}
fn load_native_tables(view: &CilAssemblyView) -> Result<(NativeImports, NativeExports)> {
let native_imports = if let Some(owned_imports) = view.file().imports() {
if owned_imports.is_empty() {
NativeImports::default()
} else {
let is_pe32_plus = view.file().is_pe32_plus_format().unwrap_or(false);
NativeImports::from_pe_imports(owned_imports, is_pe32_plus)?
}
} else {
NativeImports::default()
};
let native_exports = if let Some(owned_exports) = view.file().exports() {
if owned_exports.is_empty() {
NativeExports::default()
} else {
NativeExports::from_pe_exports(owned_exports)?
}
} else {
NativeExports::default()
};
Ok((native_imports, native_exports))
}