use crate::error::{Error, Result};
use crate::heaps::{BlobHeap, GuidHeap, StringsHeap, UserStringsHeap};
use crate::reader::Reader;
use crate::root::MetadataRoot;
use crate::stream::StreamHeader;
use crate::tables::{
AssemblyOsRow, AssemblyProcessorRow, AssemblyRefOsRow, AssemblyRefProcessorRow, AssemblyRefRow,
AssemblyRow, ClassLayoutRow, ConstantRow, CustomAttributeRow, DeclSecurityRow, EncLogRow,
EncMapRow, EventMapRow, EventPtrRow, EventRow, ExportedTypeRow, FieldLayoutRow,
FieldMarshalRow, FieldPtrRow, FieldRow, FieldRvaRow, FileRow, GenericParamConstraintRow,
GenericParamRow, ImplMapRow, InterfaceImplRow, ManifestResourceRow, MemberRefRow, MethodDefRow,
MethodImplRow, MethodPtrRow, MethodSemanticsRow, MethodSpecRow, ModuleRefRow, ModuleRow,
NestedClassRow, ParamPtrRow, ParamRow, PropertyMapRow, PropertyPtrRow, PropertyRow,
StandAloneSigRow, TableContext, TableId, TablesHeader, TypeDefRow, TypeRefRow, TypeSpecRow,
};
use crate::writer::Writer;
#[derive(Debug, Clone)]
pub struct Metadata {
pub root: MetadataRoot,
pub strings: StringsHeap,
pub user_strings: UserStringsHeap,
pub guids: GuidHeap,
pub blobs: BlobHeap,
pub tables_header: TablesHeader,
pub modules: Vec<ModuleRow>,
pub type_refs: Vec<TypeRefRow>,
pub type_defs: Vec<TypeDefRow>,
pub field_ptrs: Vec<FieldPtrRow>,
pub fields: Vec<FieldRow>,
pub method_ptrs: Vec<MethodPtrRow>,
pub method_defs: Vec<MethodDefRow>,
pub param_ptrs: Vec<ParamPtrRow>,
pub params: Vec<ParamRow>,
pub interface_impls: Vec<InterfaceImplRow>,
pub member_refs: Vec<MemberRefRow>,
pub constants: Vec<ConstantRow>,
pub custom_attributes: Vec<CustomAttributeRow>,
pub field_marshals: Vec<FieldMarshalRow>,
pub decl_securities: Vec<DeclSecurityRow>,
pub class_layouts: Vec<ClassLayoutRow>,
pub field_layouts: Vec<FieldLayoutRow>,
pub stand_alone_sigs: Vec<StandAloneSigRow>,
pub event_maps: Vec<EventMapRow>,
pub event_ptrs: Vec<EventPtrRow>,
pub events: Vec<EventRow>,
pub property_maps: Vec<PropertyMapRow>,
pub property_ptrs: Vec<PropertyPtrRow>,
pub properties: Vec<PropertyRow>,
pub method_semantics: Vec<MethodSemanticsRow>,
pub method_impls: Vec<MethodImplRow>,
pub module_refs: Vec<ModuleRefRow>,
pub type_specs: Vec<TypeSpecRow>,
pub impl_maps: Vec<ImplMapRow>,
pub field_rvas: Vec<FieldRvaRow>,
pub enc_logs: Vec<EncLogRow>,
pub enc_maps: Vec<EncMapRow>,
pub assemblies: Vec<AssemblyRow>,
pub assembly_processors: Vec<AssemblyProcessorRow>,
pub assembly_oses: Vec<AssemblyOsRow>,
pub assembly_refs: Vec<AssemblyRefRow>,
pub assembly_ref_processors: Vec<AssemblyRefProcessorRow>,
pub assembly_ref_oses: Vec<AssemblyRefOsRow>,
pub files: Vec<FileRow>,
pub exported_types: Vec<ExportedTypeRow>,
pub manifest_resources: Vec<ManifestResourceRow>,
pub nested_classes: Vec<NestedClassRow>,
pub generic_params: Vec<GenericParamRow>,
pub method_specs: Vec<MethodSpecRow>,
pub generic_param_constraints: Vec<GenericParamConstraintRow>,
}
impl Metadata {
pub fn parse(data: &[u8]) -> Result<Self> {
let root = MetadataRoot::parse(data)?;
let strings = Self::parse_heap(&root, data, StreamHeader::STRINGS, StringsHeap::parse)?;
let user_strings = Self::parse_heap(
&root,
data,
StreamHeader::USER_STRINGS,
UserStringsHeap::parse,
)?;
let guids = Self::parse_heap(&root, data, StreamHeader::GUID, GuidHeap::parse)?;
let blobs = Self::parse_heap(&root, data, StreamHeader::BLOB, BlobHeap::parse)?;
let tables_stream = root
.tables_stream()
.ok_or_else(|| Error::StreamNotFound(StreamHeader::TABLES.to_string()))?;
let uncompressed = tables_stream.name == StreamHeader::TABLES_UNCOMPRESSED;
let tables_data = &data
[tables_stream.offset as usize..(tables_stream.offset + tables_stream.size) as usize];
let mut reader = Reader::new(tables_data);
let tables_header = TablesHeader::parse(&mut reader, uncompressed)?;
let ctx = tables_header.context();
let modules = Self::parse_table(&mut reader, &ctx, TableId::Module, ModuleRow::parse)?;
let type_refs = Self::parse_table(&mut reader, &ctx, TableId::TypeRef, TypeRefRow::parse)?;
let type_defs = Self::parse_table(&mut reader, &ctx, TableId::TypeDef, TypeDefRow::parse)?;
let field_ptrs =
Self::parse_table(&mut reader, &ctx, TableId::FieldPtr, FieldPtrRow::parse)?;
let fields = Self::parse_table(&mut reader, &ctx, TableId::Field, FieldRow::parse)?;
let method_ptrs =
Self::parse_table(&mut reader, &ctx, TableId::MethodPtr, MethodPtrRow::parse)?;
let method_defs =
Self::parse_table(&mut reader, &ctx, TableId::MethodDef, MethodDefRow::parse)?;
let param_ptrs =
Self::parse_table(&mut reader, &ctx, TableId::ParamPtr, ParamPtrRow::parse)?;
let params = Self::parse_table(&mut reader, &ctx, TableId::Param, ParamRow::parse)?;
let interface_impls = Self::parse_table(
&mut reader,
&ctx,
TableId::InterfaceImpl,
InterfaceImplRow::parse,
)?;
let member_refs =
Self::parse_table(&mut reader, &ctx, TableId::MemberRef, MemberRefRow::parse)?;
let constants =
Self::parse_table(&mut reader, &ctx, TableId::Constant, ConstantRow::parse)?;
let custom_attributes = Self::parse_table(
&mut reader,
&ctx,
TableId::CustomAttribute,
CustomAttributeRow::parse,
)?;
let field_marshals = Self::parse_table(
&mut reader,
&ctx,
TableId::FieldMarshal,
FieldMarshalRow::parse,
)?;
let decl_securities = Self::parse_table(
&mut reader,
&ctx,
TableId::DeclSecurity,
DeclSecurityRow::parse,
)?;
let class_layouts = Self::parse_table(
&mut reader,
&ctx,
TableId::ClassLayout,
ClassLayoutRow::parse,
)?;
let field_layouts = Self::parse_table(
&mut reader,
&ctx,
TableId::FieldLayout,
FieldLayoutRow::parse,
)?;
let stand_alone_sigs = Self::parse_table(
&mut reader,
&ctx,
TableId::StandAloneSig,
StandAloneSigRow::parse,
)?;
let event_maps =
Self::parse_table(&mut reader, &ctx, TableId::EventMap, EventMapRow::parse)?;
let event_ptrs =
Self::parse_table(&mut reader, &ctx, TableId::EventPtr, EventPtrRow::parse)?;
let events = Self::parse_table(&mut reader, &ctx, TableId::Event, EventRow::parse)?;
let property_maps = Self::parse_table(
&mut reader,
&ctx,
TableId::PropertyMap,
PropertyMapRow::parse,
)?;
let property_ptrs = Self::parse_table(
&mut reader,
&ctx,
TableId::PropertyPtr,
PropertyPtrRow::parse,
)?;
let properties =
Self::parse_table(&mut reader, &ctx, TableId::Property, PropertyRow::parse)?;
let method_semantics = Self::parse_table(
&mut reader,
&ctx,
TableId::MethodSemantics,
MethodSemanticsRow::parse,
)?;
let method_impls =
Self::parse_table(&mut reader, &ctx, TableId::MethodImpl, MethodImplRow::parse)?;
let module_refs =
Self::parse_table(&mut reader, &ctx, TableId::ModuleRef, ModuleRefRow::parse)?;
let type_specs =
Self::parse_table(&mut reader, &ctx, TableId::TypeSpec, TypeSpecRow::parse)?;
let impl_maps = Self::parse_table(&mut reader, &ctx, TableId::ImplMap, ImplMapRow::parse)?;
let field_rvas =
Self::parse_table(&mut reader, &ctx, TableId::FieldRva, FieldRvaRow::parse)?;
let enc_logs = Self::parse_table(&mut reader, &ctx, TableId::EncLog, EncLogRow::parse)?;
let enc_maps = Self::parse_table(&mut reader, &ctx, TableId::EncMap, EncMapRow::parse)?;
let assemblies =
Self::parse_table(&mut reader, &ctx, TableId::Assembly, AssemblyRow::parse)?;
let assembly_processors = Self::parse_table(
&mut reader,
&ctx,
TableId::AssemblyProcessor,
AssemblyProcessorRow::parse,
)?;
let assembly_oses =
Self::parse_table(&mut reader, &ctx, TableId::AssemblyOs, AssemblyOsRow::parse)?;
let assembly_refs = Self::parse_table(
&mut reader,
&ctx,
TableId::AssemblyRef,
AssemblyRefRow::parse,
)?;
let assembly_ref_processors = Self::parse_table(
&mut reader,
&ctx,
TableId::AssemblyRefProcessor,
AssemblyRefProcessorRow::parse,
)?;
let assembly_ref_oses = Self::parse_table(
&mut reader,
&ctx,
TableId::AssemblyRefOs,
AssemblyRefOsRow::parse,
)?;
let files = Self::parse_table(&mut reader, &ctx, TableId::File, FileRow::parse)?;
let exported_types = Self::parse_table(
&mut reader,
&ctx,
TableId::ExportedType,
ExportedTypeRow::parse,
)?;
let manifest_resources = Self::parse_table(
&mut reader,
&ctx,
TableId::ManifestResource,
ManifestResourceRow::parse,
)?;
let nested_classes = Self::parse_table(
&mut reader,
&ctx,
TableId::NestedClass,
NestedClassRow::parse,
)?;
let generic_params = Self::parse_table(
&mut reader,
&ctx,
TableId::GenericParam,
GenericParamRow::parse,
)?;
let method_specs =
Self::parse_table(&mut reader, &ctx, TableId::MethodSpec, MethodSpecRow::parse)?;
let generic_param_constraints = Self::parse_table(
&mut reader,
&ctx,
TableId::GenericParamConstraint,
GenericParamConstraintRow::parse,
)?;
Ok(Self {
root,
strings,
user_strings,
guids,
blobs,
tables_header,
modules,
type_refs,
type_defs,
field_ptrs,
fields,
method_ptrs,
method_defs,
param_ptrs,
params,
interface_impls,
member_refs,
constants,
custom_attributes,
field_marshals,
decl_securities,
class_layouts,
field_layouts,
stand_alone_sigs,
event_maps,
event_ptrs,
events,
property_maps,
property_ptrs,
properties,
method_semantics,
method_impls,
module_refs,
type_specs,
impl_maps,
field_rvas,
enc_logs,
enc_maps,
assemblies,
assembly_processors,
assembly_oses,
assembly_refs,
assembly_ref_processors,
assembly_ref_oses,
files,
exported_types,
manifest_resources,
nested_classes,
generic_params,
method_specs,
generic_param_constraints,
})
}
fn parse_heap<T, F>(root: &MetadataRoot, data: &[u8], name: &str, parser: F) -> Result<T>
where
F: FnOnce(&[u8]) -> T,
T: Default,
{
if let Some(stream) = root.find_stream(name) {
let start = stream.offset as usize;
let end = start + stream.size as usize;
if end <= data.len() {
return Ok(parser(&data[start..end]));
}
}
Ok(T::default())
}
fn parse_table<T, F>(
reader: &mut Reader<'_>,
ctx: &TableContext,
table: TableId,
parser: F,
) -> Result<Vec<T>>
where
F: Fn(&mut Reader<'_>, &TableContext) -> Result<T>,
{
let count = ctx.row_count(table) as usize;
let mut rows = Vec::with_capacity(count);
for _ in 0..count {
rows.push(parser(reader, ctx)?);
}
Ok(rows)
}
#[must_use]
pub fn version(&self) -> &str {
&self.root.version
}
#[must_use]
pub fn assembly(&self) -> Option<AssemblyInfo> {
self.assemblies.first().map(|row| {
let name = self.strings.get(row.name).unwrap_or("").to_string();
let culture = if row.culture != 0 {
self.strings.get(row.culture).ok().map(|s| s.to_string())
} else {
None
};
let public_key = if row.public_key != 0 {
self.blobs.get(row.public_key).ok().map(|b| b.to_vec())
} else {
None
};
AssemblyInfo {
name,
version: (
row.major_version,
row.minor_version,
row.build_number,
row.revision_number,
),
culture,
public_key,
flags: row.flags,
hash_alg_id: row.hash_alg_id,
}
})
}
pub fn types(&self) -> Vec<TypeInfo> {
self.type_defs
.iter()
.map(|row| {
let name = self.strings.get(row.type_name).unwrap_or("").to_string();
let namespace = if row.type_namespace != 0 {
self.strings
.get(row.type_namespace)
.ok()
.map(|s| s.to_string())
} else {
None
};
TypeInfo {
name,
namespace,
flags: row.flags,
}
})
.collect()
}
pub fn methods(&self) -> Vec<MethodInfo> {
self.method_defs
.iter()
.map(|row| {
let name = self.strings.get(row.name).unwrap_or("").to_string();
MethodInfo {
name,
rva: row.rva,
flags: row.flags,
impl_flags: row.impl_flags,
}
})
.collect()
}
pub fn assembly_refs(&self) -> Vec<AssemblyRefInfo> {
self.assembly_refs
.iter()
.map(|row| {
let name = self.strings.get(row.name).unwrap_or("").to_string();
let culture = if row.culture != 0 {
self.strings.get(row.culture).ok().map(|s| s.to_string())
} else {
None
};
let public_key_token = if row.public_key_or_token != 0 {
self.blobs
.get(row.public_key_or_token)
.ok()
.map(|b| b.to_vec())
} else {
None
};
AssemblyRefInfo {
name,
version: (
row.major_version,
row.minor_version,
row.build_number,
row.revision_number,
),
culture,
public_key_token,
flags: row.flags,
}
})
.collect()
}
#[must_use]
pub fn get_type_def(&self, index: u32) -> Option<&TypeDefRow> {
if index == 0 || index as usize > self.type_defs.len() {
return None;
}
Some(&self.type_defs[(index - 1) as usize])
}
#[must_use]
pub fn get_type_ref(&self, index: u32) -> Option<&TypeRefRow> {
if index == 0 || index as usize > self.type_refs.len() {
return None;
}
Some(&self.type_refs[(index - 1) as usize])
}
#[must_use]
pub fn get_type_spec(&self, index: u32) -> Option<&TypeSpecRow> {
if index == 0 || index as usize > self.type_specs.len() {
return None;
}
Some(&self.type_specs[(index - 1) as usize])
}
#[must_use]
pub fn resolve_type(&self, coded_index: &crate::tables::CodedIndex) -> Option<ResolvedType> {
if coded_index.is_null() {
return None;
}
match coded_index.table? {
TableId::TypeDef => {
let row = self.get_type_def(coded_index.row)?;
let name = self.strings.get(row.type_name).ok()?.to_string();
let namespace = if row.type_namespace != 0 {
self.strings
.get(row.type_namespace)
.ok()
.map(|s| s.to_string())
} else {
None
};
Some(ResolvedType::TypeDef {
index: coded_index.row,
name,
namespace,
})
}
TableId::TypeRef => {
let row = self.get_type_ref(coded_index.row)?;
let name = self.strings.get(row.type_name).ok()?.to_string();
let namespace = if row.type_namespace != 0 {
self.strings
.get(row.type_namespace)
.ok()
.map(|s| s.to_string())
} else {
None
};
Some(ResolvedType::TypeRef {
index: coded_index.row,
name,
namespace,
})
}
TableId::TypeSpec => {
let row = self.get_type_spec(coded_index.row)?;
Some(ResolvedType::TypeSpec {
index: coded_index.row,
signature: row.signature,
})
}
_ => None,
}
}
#[must_use]
pub fn get_base_type(&self, type_def_index: u32) -> Option<ResolvedType> {
let row = self.get_type_def(type_def_index)?;
self.resolve_type(&row.extends)
}
pub fn get_interfaces(&self, type_def_index: u32) -> Vec<ResolvedType> {
self.interface_impls
.iter()
.filter(|row| row.class == type_def_index)
.filter_map(|row| self.resolve_type(&row.interface))
.collect()
}
pub fn get_type_methods(&self, type_def_index: u32) -> Vec<(u32, &MethodDefRow)> {
let row = match self.get_type_def(type_def_index) {
Some(r) => r,
None => return Vec::new(),
};
let start = row.method_list;
let end = self
.get_type_def(type_def_index + 1)
.map(|r| r.method_list)
.unwrap_or((self.method_defs.len() + 1) as u32);
((start as usize)..(end as usize))
.filter_map(|i| {
if i > 0 && i <= self.method_defs.len() {
Some((i as u32, &self.method_defs[i - 1]))
} else {
None
}
})
.collect()
}
pub fn get_type_fields(&self, type_def_index: u32) -> Vec<(u32, &FieldRow)> {
let row = match self.get_type_def(type_def_index) {
Some(r) => r,
None => return Vec::new(),
};
let start = row.field_list;
let end = self
.get_type_def(type_def_index + 1)
.map(|r| r.field_list)
.unwrap_or((self.fields.len() + 1) as u32);
((start as usize)..(end as usize))
.filter_map(|i| {
if i > 0 && i <= self.fields.len() {
Some((i as u32, &self.fields[i - 1]))
} else {
None
}
})
.collect()
}
pub fn find_type(&self, name: &str, namespace: Option<&str>) -> Option<(u32, &TypeDefRow)> {
for (i, row) in self.type_defs.iter().enumerate() {
let type_name = self.strings.get(row.type_name).ok()?;
if type_name != name {
continue;
}
let type_ns = if row.type_namespace != 0 {
self.strings.get(row.type_namespace).ok()
} else {
None
};
match (namespace, type_ns) {
(Some(ns), Some(tns)) if ns == tns => return Some(((i + 1) as u32, row)),
(None, None) | (None, Some("")) => return Some(((i + 1) as u32, row)),
(Some(""), None) | (Some(""), Some("")) => return Some(((i + 1) as u32, row)),
_ => continue,
}
}
None
}
#[must_use]
pub fn get_method_owner(&self, method_index: u32) -> Option<(u32, &TypeDefRow)> {
for (i, row) in self.type_defs.iter().enumerate() {
let start = row.method_list;
let end = self
.get_type_def((i + 2) as u32)
.map(|r| r.method_list)
.unwrap_or((self.method_defs.len() + 1) as u32);
if method_index >= start && method_index < end {
return Some(((i + 1) as u32, row));
}
}
None
}
#[must_use]
pub fn get_field_owner(&self, field_index: u32) -> Option<(u32, &TypeDefRow)> {
for (i, row) in self.type_defs.iter().enumerate() {
let start = row.field_list;
let end = self
.get_type_def((i + 2) as u32)
.map(|r| r.field_list)
.unwrap_or((self.fields.len() + 1) as u32);
if field_index >= start && field_index < end {
return Some(((i + 1) as u32, row));
}
}
None
}
#[must_use]
pub fn validate(&self) -> Vec<String> {
let mut errors = Vec::new();
if self.modules.is_empty() {
errors.push("Module table must have at least 1 row".to_string());
}
for (i, row) in self.modules.iter().enumerate() {
self.validate_string_index(&mut errors, "Module", i, "name", row.name);
self.validate_guid_index(&mut errors, "Module", i, "mvid", row.mvid);
}
for (i, row) in self.type_refs.iter().enumerate() {
self.validate_string_index(&mut errors, "TypeRef", i, "type_name", row.type_name);
self.validate_string_index(
&mut errors,
"TypeRef",
i,
"type_namespace",
row.type_namespace,
);
}
for (i, row) in self.type_defs.iter().enumerate() {
self.validate_string_index(&mut errors, "TypeDef", i, "type_name", row.type_name);
self.validate_string_index(
&mut errors,
"TypeDef",
i,
"type_namespace",
row.type_namespace,
);
self.validate_table_index(
&mut errors,
"TypeDef",
i,
"field_list",
row.field_list,
self.fields.len(),
);
self.validate_table_index(
&mut errors,
"TypeDef",
i,
"method_list",
row.method_list,
self.method_defs.len(),
);
}
for (i, row) in self.fields.iter().enumerate() {
self.validate_string_index(&mut errors, "Field", i, "name", row.name);
self.validate_blob_index(&mut errors, "Field", i, "signature", row.signature);
}
for (i, row) in self.method_defs.iter().enumerate() {
self.validate_string_index(&mut errors, "MethodDef", i, "name", row.name);
self.validate_blob_index(&mut errors, "MethodDef", i, "signature", row.signature);
self.validate_table_index(
&mut errors,
"MethodDef",
i,
"param_list",
row.param_list,
self.params.len(),
);
}
for (i, row) in self.params.iter().enumerate() {
self.validate_string_index(&mut errors, "Param", i, "name", row.name);
}
for (i, row) in self.member_refs.iter().enumerate() {
self.validate_string_index(&mut errors, "MemberRef", i, "name", row.name);
self.validate_blob_index(&mut errors, "MemberRef", i, "signature", row.signature);
}
for (i, row) in self.constants.iter().enumerate() {
self.validate_blob_index(&mut errors, "Constant", i, "value", row.value);
}
for (i, row) in self.custom_attributes.iter().enumerate() {
self.validate_blob_index(&mut errors, "CustomAttribute", i, "value", row.value);
}
for (i, row) in self.assemblies.iter().enumerate() {
self.validate_string_index(&mut errors, "Assembly", i, "name", row.name);
self.validate_string_index(&mut errors, "Assembly", i, "culture", row.culture);
self.validate_blob_index(&mut errors, "Assembly", i, "public_key", row.public_key);
}
for (i, row) in self.assembly_refs.iter().enumerate() {
self.validate_string_index(&mut errors, "AssemblyRef", i, "name", row.name);
self.validate_string_index(&mut errors, "AssemblyRef", i, "culture", row.culture);
self.validate_blob_index(
&mut errors,
"AssemblyRef",
i,
"public_key_or_token",
row.public_key_or_token,
);
self.validate_blob_index(&mut errors, "AssemblyRef", i, "hash_value", row.hash_value);
}
self.validate_coded_indices(&mut errors);
self.validate_sorted_tables(&mut errors);
errors
}
fn validate_coded_indices(&self, errors: &mut Vec<String>) {
use crate::tables::{CodedIndex, CodedIndexKind};
let validate_coded = |errors: &mut Vec<String>,
table: &str,
row: usize,
field: &str,
idx: &CodedIndex,
kind: CodedIndexKind| {
if idx.is_null() {
return;
}
if let Some(target_table) = idx.table {
let max_row = self.table_row_count(target_table);
if idx.row == 0 || idx.row > max_row {
errors.push(format!(
"{table}[{row}].{field}: invalid {kind:?} index pointing to {target_table:?} row {} (max: {max_row})",
idx.row
));
}
} else {
errors.push(format!(
"{table}[{row}].{field}: coded index has invalid table tag for {kind:?}"
));
}
};
for (i, row) in self.type_defs.iter().enumerate() {
validate_coded(
errors,
"TypeDef",
i,
"extends",
&row.extends,
CodedIndexKind::TypeDefOrRef,
);
}
for (i, row) in self.interface_impls.iter().enumerate() {
validate_coded(
errors,
"InterfaceImpl",
i,
"interface",
&row.interface,
CodedIndexKind::TypeDefOrRef,
);
}
for (i, row) in self.member_refs.iter().enumerate() {
validate_coded(
errors,
"MemberRef",
i,
"class",
&row.class,
CodedIndexKind::MemberRefParent,
);
}
for (i, row) in self.constants.iter().enumerate() {
validate_coded(
errors,
"Constant",
i,
"parent",
&row.parent,
CodedIndexKind::HasConstant,
);
}
for (i, row) in self.custom_attributes.iter().enumerate() {
validate_coded(
errors,
"CustomAttribute",
i,
"parent",
&row.parent,
CodedIndexKind::HasCustomAttribute,
);
validate_coded(
errors,
"CustomAttribute",
i,
"type",
&row.attr_type,
CodedIndexKind::CustomAttributeType,
);
}
for (i, row) in self.generic_param_constraints.iter().enumerate() {
validate_coded(
errors,
"GenericParamConstraint",
i,
"constraint",
&row.constraint,
CodedIndexKind::TypeDefOrRef,
);
}
}
fn validate_sorted_tables(&self, errors: &mut Vec<String>) {
for window in self.interface_impls.windows(2) {
if window[1].class < window[0].class {
errors.push("InterfaceImpl table is not sorted by Class column".to_string());
break;
}
}
for window in self.constants.windows(2) {
let key0 = self.coded_index_sort_key(&window[0].parent);
let key1 = self.coded_index_sort_key(&window[1].parent);
if key1 < key0 {
errors.push("Constant table is not sorted by Parent column".to_string());
break;
}
}
for window in self.field_marshals.windows(2) {
let key0 = self.coded_index_sort_key(&window[0].parent);
let key1 = self.coded_index_sort_key(&window[1].parent);
if key1 < key0 {
errors.push("FieldMarshal table is not sorted by Parent column".to_string());
break;
}
}
for window in self.method_semantics.windows(2) {
let key0 = self.coded_index_sort_key(&window[0].association);
let key1 = self.coded_index_sort_key(&window[1].association);
if key1 < key0 {
errors
.push("MethodSemantics table is not sorted by Association column".to_string());
break;
}
}
for window in self.class_layouts.windows(2) {
if window[1].parent < window[0].parent {
errors.push("ClassLayout table is not sorted by Parent column".to_string());
break;
}
}
for window in self.nested_classes.windows(2) {
if window[1].nested_class < window[0].nested_class {
errors.push("NestedClass table is not sorted by NestedClass column".to_string());
break;
}
}
for window in self.generic_params.windows(2) {
let key0 = self.coded_index_sort_key(&window[0].owner);
let key1 = self.coded_index_sort_key(&window[1].owner);
if key1 < key0 {
errors.push("GenericParam table is not sorted by Owner column".to_string());
break;
}
}
}
fn coded_index_sort_key(&self, idx: &crate::tables::CodedIndex) -> (u8, u32) {
let table_id = idx.table.map_or(0xff, |t| t as u8);
(table_id, idx.row)
}
fn table_row_count(&self, table: TableId) -> u32 {
match table {
TableId::Module => self.modules.len() as u32,
TableId::TypeRef => self.type_refs.len() as u32,
TableId::TypeDef => self.type_defs.len() as u32,
TableId::FieldPtr => self.field_ptrs.len() as u32,
TableId::Field => self.fields.len() as u32,
TableId::MethodPtr => self.method_ptrs.len() as u32,
TableId::MethodDef => self.method_defs.len() as u32,
TableId::ParamPtr => self.param_ptrs.len() as u32,
TableId::Param => self.params.len() as u32,
TableId::InterfaceImpl => self.interface_impls.len() as u32,
TableId::MemberRef => self.member_refs.len() as u32,
TableId::Constant => self.constants.len() as u32,
TableId::CustomAttribute => self.custom_attributes.len() as u32,
TableId::FieldMarshal => self.field_marshals.len() as u32,
TableId::DeclSecurity => self.decl_securities.len() as u32,
TableId::ClassLayout => self.class_layouts.len() as u32,
TableId::FieldLayout => self.field_layouts.len() as u32,
TableId::StandAloneSig => self.stand_alone_sigs.len() as u32,
TableId::EventMap => self.event_maps.len() as u32,
TableId::EventPtr => self.event_ptrs.len() as u32,
TableId::Event => self.events.len() as u32,
TableId::PropertyMap => self.property_maps.len() as u32,
TableId::PropertyPtr => self.property_ptrs.len() as u32,
TableId::Property => self.properties.len() as u32,
TableId::MethodSemantics => self.method_semantics.len() as u32,
TableId::MethodImpl => self.method_impls.len() as u32,
TableId::ModuleRef => self.module_refs.len() as u32,
TableId::TypeSpec => self.type_specs.len() as u32,
TableId::ImplMap => self.impl_maps.len() as u32,
TableId::FieldRva => self.field_rvas.len() as u32,
TableId::EncLog => self.enc_logs.len() as u32,
TableId::EncMap => self.enc_maps.len() as u32,
TableId::Assembly => self.assemblies.len() as u32,
TableId::AssemblyProcessor => self.assembly_processors.len() as u32,
TableId::AssemblyOs => self.assembly_oses.len() as u32,
TableId::AssemblyRef => self.assembly_refs.len() as u32,
TableId::AssemblyRefProcessor => self.assembly_ref_processors.len() as u32,
TableId::AssemblyRefOs => self.assembly_ref_oses.len() as u32,
TableId::File => self.files.len() as u32,
TableId::ExportedType => self.exported_types.len() as u32,
TableId::ManifestResource => self.manifest_resources.len() as u32,
TableId::NestedClass => self.nested_classes.len() as u32,
TableId::GenericParam => self.generic_params.len() as u32,
TableId::MethodSpec => self.method_specs.len() as u32,
TableId::GenericParamConstraint => self.generic_param_constraints.len() as u32,
}
}
pub fn validate_strict(&self) -> Result<()> {
let errors = self.validate();
if let Some(first_error) = errors.into_iter().next() {
Err(Error::ValidationError(first_error))
} else {
Ok(())
}
}
fn validate_string_index(
&self,
errors: &mut Vec<String>,
table: &str,
row: usize,
field: &str,
index: u32,
) {
if index != 0 && self.strings.get(index).is_err() {
errors.push(format!(
"{table}[{row}].{field}: invalid string index {index}"
));
}
}
fn validate_guid_index(
&self,
errors: &mut Vec<String>,
table: &str,
row: usize,
field: &str,
index: u32,
) {
if index != 0 && self.guids.get(index).is_err() {
errors.push(format!(
"{table}[{row}].{field}: invalid GUID index {index}"
));
}
}
fn validate_blob_index(
&self,
errors: &mut Vec<String>,
table: &str,
row: usize,
field: &str,
index: u32,
) {
if index != 0 && self.blobs.get(index).is_err() {
errors.push(format!(
"{table}[{row}].{field}: invalid blob index {index}"
));
}
}
fn validate_table_index(
&self,
errors: &mut Vec<String>,
table: &str,
row: usize,
field: &str,
index: u32,
max_rows: usize,
) {
if index > (max_rows as u32) + 1 {
errors.push(format!(
"{table}[{row}].{field}: invalid table index {index} (max {max_rows})"
));
}
}
}
#[derive(Debug, Clone)]
pub struct AssemblyInfo {
pub name: String,
pub version: (u16, u16, u16, u16),
pub culture: Option<String>,
pub public_key: Option<Vec<u8>>,
pub flags: u32,
pub hash_alg_id: u32,
}
impl AssemblyInfo {
#[must_use]
pub fn version_string(&self) -> String {
format!(
"{}.{}.{}.{}",
self.version.0, self.version.1, self.version.2, self.version.3
)
}
#[must_use]
pub fn public_key_token(&self) -> Option<[u8; 8]> {
self.public_key
.as_ref()
.map(|pk| crate::crypto::public_key_token(pk))
}
#[must_use]
pub fn public_key_token_string(&self) -> Option<String> {
self.public_key_token()
.map(|token| token.iter().map(|b| format!("{b:02x}")).collect::<String>())
}
}
#[derive(Debug, Clone)]
pub struct TypeInfo {
pub name: String,
pub namespace: Option<String>,
pub flags: u32,
}
impl TypeInfo {
#[must_use]
pub fn full_name(&self) -> String {
if let Some(ns) = &self.namespace
&& !ns.is_empty()
{
return format!("{}.{}", ns, self.name);
}
self.name.clone()
}
}
#[derive(Debug, Clone)]
pub struct MethodInfo {
pub name: String,
pub rva: u32,
pub flags: u16,
pub impl_flags: u16,
}
#[derive(Debug, Clone)]
pub struct AssemblyRefInfo {
pub name: String,
pub version: (u16, u16, u16, u16),
pub culture: Option<String>,
pub public_key_token: Option<Vec<u8>>,
pub flags: u32,
}
impl AssemblyRefInfo {
#[must_use]
pub fn version_string(&self) -> String {
format!(
"{}.{}.{}.{}",
self.version.0, self.version.1, self.version.2, self.version.3
)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub enum ResolvedType {
TypeDef {
index: u32,
name: String,
namespace: Option<String>,
},
TypeRef {
index: u32,
name: String,
namespace: Option<String>,
},
TypeSpec {
index: u32,
signature: u32,
},
}
impl ResolvedType {
#[must_use]
pub fn full_name(&self) -> String {
match self {
Self::TypeDef {
name, namespace, ..
}
| Self::TypeRef {
name, namespace, ..
} => {
if let Some(ns) = namespace
&& !ns.is_empty()
{
return format!("{ns}.{name}");
}
name.clone()
}
Self::TypeSpec { signature, .. } => format!("<TypeSpec sig={signature}>"),
}
}
#[must_use]
pub const fn is_type_def(&self) -> bool {
matches!(self, Self::TypeDef { .. })
}
#[must_use]
pub const fn is_type_ref(&self) -> bool {
matches!(self, Self::TypeRef { .. })
}
#[must_use]
pub const fn is_type_spec(&self) -> bool {
matches!(self, Self::TypeSpec { .. })
}
}
impl Metadata {
#[must_use]
pub fn write(&self) -> Vec<u8> {
let mut writer = Writer::new();
self.write_to(&mut writer);
writer.into_inner()
}
pub fn write_to(&self, writer: &mut Writer) {
let heap_sizes = self.calculate_heap_sizes();
let mut root = self.root.clone();
let header_size = root.header_size();
let mut current_offset = header_size;
for stream in &mut root.streams {
stream.offset = current_offset as u32;
match stream.name.as_str() {
StreamHeader::TABLES | StreamHeader::TABLES_UNCOMPRESSED => {
stream.size = self.calculate_tables_size() as u32;
}
StreamHeader::STRINGS => {
stream.size = self.strings.size() as u32;
}
StreamHeader::USER_STRINGS => {
stream.size = self.user_strings.size() as u32;
}
StreamHeader::GUID => {
stream.size = self.guids.size() as u32;
}
StreamHeader::BLOB => {
stream.size = self.blobs.size() as u32;
}
_ => {}
}
current_offset += stream.size as usize;
current_offset = (current_offset + 3) & !3;
}
root.write_to(writer);
for stream in &root.streams {
match stream.name.as_str() {
StreamHeader::TABLES | StreamHeader::TABLES_UNCOMPRESSED => {
self.write_tables(writer, heap_sizes);
}
StreamHeader::STRINGS => {
self.strings.write_to(writer);
}
StreamHeader::USER_STRINGS => {
self.user_strings.write_to(writer);
}
StreamHeader::GUID => {
self.guids.write_to(writer);
}
StreamHeader::BLOB => {
self.blobs.write_to(writer);
}
_ => {
}
}
writer.align(4);
}
}
fn calculate_heap_sizes(&self) -> u8 {
let mut heap_sizes = 0u8;
if self.strings.uses_wide_indices() {
heap_sizes |= 0x01;
}
if self.guids.uses_wide_indices() {
heap_sizes |= 0x02;
}
if self.blobs.uses_wide_indices() {
heap_sizes |= 0x04;
}
heap_sizes
}
fn calculate_tables_size(&self) -> usize {
let ctx = self.tables_header.context();
let mut size = self.tables_header.size();
for (table, count) in self.tables_header.tables() {
size += count as usize * ctx.row_size(table);
}
size
}
fn write_tables(&self, writer: &mut Writer, heap_sizes: u8) {
let mut header = self.tables_header.clone();
header.heap_sizes = heap_sizes;
header.set_row_count(TableId::Module, self.modules.len() as u32);
header.set_row_count(TableId::TypeRef, self.type_refs.len() as u32);
header.set_row_count(TableId::TypeDef, self.type_defs.len() as u32);
header.set_row_count(TableId::FieldPtr, self.field_ptrs.len() as u32);
header.set_row_count(TableId::Field, self.fields.len() as u32);
header.set_row_count(TableId::MethodPtr, self.method_ptrs.len() as u32);
header.set_row_count(TableId::MethodDef, self.method_defs.len() as u32);
header.set_row_count(TableId::ParamPtr, self.param_ptrs.len() as u32);
header.set_row_count(TableId::Param, self.params.len() as u32);
header.set_row_count(TableId::InterfaceImpl, self.interface_impls.len() as u32);
header.set_row_count(TableId::MemberRef, self.member_refs.len() as u32);
header.set_row_count(TableId::Constant, self.constants.len() as u32);
header.set_row_count(
TableId::CustomAttribute,
self.custom_attributes.len() as u32,
);
header.set_row_count(TableId::FieldMarshal, self.field_marshals.len() as u32);
header.set_row_count(TableId::DeclSecurity, self.decl_securities.len() as u32);
header.set_row_count(TableId::ClassLayout, self.class_layouts.len() as u32);
header.set_row_count(TableId::FieldLayout, self.field_layouts.len() as u32);
header.set_row_count(TableId::StandAloneSig, self.stand_alone_sigs.len() as u32);
header.set_row_count(TableId::EventMap, self.event_maps.len() as u32);
header.set_row_count(TableId::EventPtr, self.event_ptrs.len() as u32);
header.set_row_count(TableId::Event, self.events.len() as u32);
header.set_row_count(TableId::PropertyMap, self.property_maps.len() as u32);
header.set_row_count(TableId::PropertyPtr, self.property_ptrs.len() as u32);
header.set_row_count(TableId::Property, self.properties.len() as u32);
header.set_row_count(TableId::MethodSemantics, self.method_semantics.len() as u32);
header.set_row_count(TableId::MethodImpl, self.method_impls.len() as u32);
header.set_row_count(TableId::ModuleRef, self.module_refs.len() as u32);
header.set_row_count(TableId::TypeSpec, self.type_specs.len() as u32);
header.set_row_count(TableId::ImplMap, self.impl_maps.len() as u32);
header.set_row_count(TableId::FieldRva, self.field_rvas.len() as u32);
header.set_row_count(TableId::EncLog, self.enc_logs.len() as u32);
header.set_row_count(TableId::EncMap, self.enc_maps.len() as u32);
header.set_row_count(TableId::Assembly, self.assemblies.len() as u32);
header.set_row_count(
TableId::AssemblyProcessor,
self.assembly_processors.len() as u32,
);
header.set_row_count(TableId::AssemblyOs, self.assembly_oses.len() as u32);
header.set_row_count(TableId::AssemblyRef, self.assembly_refs.len() as u32);
header.set_row_count(
TableId::AssemblyRefProcessor,
self.assembly_ref_processors.len() as u32,
);
header.set_row_count(TableId::AssemblyRefOs, self.assembly_ref_oses.len() as u32);
header.set_row_count(TableId::File, self.files.len() as u32);
header.set_row_count(TableId::ExportedType, self.exported_types.len() as u32);
header.set_row_count(
TableId::ManifestResource,
self.manifest_resources.len() as u32,
);
header.set_row_count(TableId::NestedClass, self.nested_classes.len() as u32);
header.set_row_count(TableId::GenericParam, self.generic_params.len() as u32);
header.set_row_count(TableId::MethodSpec, self.method_specs.len() as u32);
header.set_row_count(
TableId::GenericParamConstraint,
self.generic_param_constraints.len() as u32,
);
header.write_to(writer);
let ctx = header.context();
for row in &self.modules {
row.write(writer, &ctx);
}
for row in &self.type_refs {
row.write(writer, &ctx);
}
for row in &self.type_defs {
row.write(writer, &ctx);
}
for row in &self.field_ptrs {
row.write(writer, &ctx);
}
for row in &self.fields {
row.write(writer, &ctx);
}
for row in &self.method_ptrs {
row.write(writer, &ctx);
}
for row in &self.method_defs {
row.write(writer, &ctx);
}
for row in &self.param_ptrs {
row.write(writer, &ctx);
}
for row in &self.params {
row.write(writer, &ctx);
}
for row in &self.interface_impls {
row.write(writer, &ctx);
}
for row in &self.member_refs {
row.write(writer, &ctx);
}
for row in &self.constants {
row.write(writer, &ctx);
}
for row in &self.custom_attributes {
row.write(writer, &ctx);
}
for row in &self.field_marshals {
row.write(writer, &ctx);
}
for row in &self.decl_securities {
row.write(writer, &ctx);
}
for row in &self.class_layouts {
row.write(writer, &ctx);
}
for row in &self.field_layouts {
row.write(writer, &ctx);
}
for row in &self.stand_alone_sigs {
row.write(writer, &ctx);
}
for row in &self.event_maps {
row.write(writer, &ctx);
}
for row in &self.event_ptrs {
row.write(writer, &ctx);
}
for row in &self.events {
row.write(writer, &ctx);
}
for row in &self.property_maps {
row.write(writer, &ctx);
}
for row in &self.property_ptrs {
row.write(writer, &ctx);
}
for row in &self.properties {
row.write(writer, &ctx);
}
for row in &self.method_semantics {
row.write(writer, &ctx);
}
for row in &self.method_impls {
row.write(writer, &ctx);
}
for row in &self.module_refs {
row.write(writer, &ctx);
}
for row in &self.type_specs {
row.write(writer, &ctx);
}
for row in &self.impl_maps {
row.write(writer, &ctx);
}
for row in &self.field_rvas {
row.write(writer, &ctx);
}
for row in &self.enc_logs {
row.write(writer, &ctx);
}
for row in &self.enc_maps {
row.write(writer, &ctx);
}
for row in &self.assemblies {
row.write(writer, &ctx);
}
for row in &self.assembly_processors {
row.write(writer, &ctx);
}
for row in &self.assembly_oses {
row.write(writer, &ctx);
}
for row in &self.assembly_refs {
row.write(writer, &ctx);
}
for row in &self.assembly_ref_processors {
row.write(writer, &ctx);
}
for row in &self.assembly_ref_oses {
row.write(writer, &ctx);
}
for row in &self.files {
row.write(writer, &ctx);
}
for row in &self.exported_types {
row.write(writer, &ctx);
}
for row in &self.manifest_resources {
row.write(writer, &ctx);
}
for row in &self.nested_classes {
row.write(writer, &ctx);
}
for row in &self.generic_params {
row.write(writer, &ctx);
}
for row in &self.method_specs {
row.write(writer, &ctx);
}
for row in &self.generic_param_constraints {
row.write(writer, &ctx);
}
}
}