use std::collections::{BTreeSet, HashSet};
use crate::{
assembly::{decode_blocks, BasicBlock, Operand},
cilassembly::{
cleanup::utils::{
extract_local_var_sig_rid, remove_candidates_not_alive, with_method_body,
},
modifications::TableModifications,
operation::Operation,
CilAssembly,
},
metadata::{
method::MethodBody,
signatures::{
parse_field_signature, parse_local_var_signature, parse_method_signature,
parse_property_signature, parse_type_spec_signature, CustomModifier,
SignatureLocalVariable, SignatureParameter, TypeSignature,
},
streams::Blob,
tables::{
CustomAttributeRaw, FieldRaw, GenericParamConstraintRaw, InterfaceImplRaw,
MemberRefRaw, MethodDefRaw, MethodSpecRaw, PropertyRaw, StandAloneSigRaw,
TableDataOwned, TableId, TypeDefRaw, TypeSpecRaw,
},
token::Token,
},
};
pub(super) struct PreDeletionRefs {
pub(super) il_tokens: HashSet<Token>,
pub(super) typeref_rids: HashSet<u32>,
pub(super) standalonesig_rids: BTreeSet<u32>,
}
pub(super) fn collect_pre_deletion_references(
assembly: &CilAssembly,
methods: &BTreeSet<Token>,
fields: &BTreeSet<Token>,
types: &BTreeSet<Token>,
) -> PreDeletionRefs {
let mut il_tokens = HashSet::new();
let mut typeref_rids = HashSet::new();
let mut standalonesig_rids = BTreeSet::new();
{
let view = assembly.view();
let Some(tables) = view.tables() else {
return PreDeletionRefs {
il_tokens,
typeref_rids,
standalonesig_rids,
};
};
let blob_heap = view.blobs();
if let Some(methoddef_table) = tables.table::<MethodDefRaw>() {
for methoddef in methoddef_table {
let method_token = Token::from_parts(TableId::MethodDef, methoddef.rid);
if !methods.contains(&method_token) {
continue;
}
let effective_rva =
get_effective_method_rva(assembly, methoddef.rid, methoddef.rva);
if effective_rva == 0 {
continue;
}
with_method_body(assembly, effective_rva, &mut |data, base_rva| {
scan_method_body_bytes(data, base_rva, &mut il_tokens);
if let Some(sig_rid) = extract_local_var_sig_rid(data) {
standalonesig_rids.insert(sig_rid);
}
});
if let Some(blob) = &blob_heap {
scan_method_signature_blob(blob, methoddef.signature, &mut typeref_rids);
}
}
}
if let Some(field_table) = tables.table::<FieldRaw>() {
if let Some(blob) = &blob_heap {
for field in field_table {
let field_token = Token::from_parts(TableId::Field, field.rid);
if !fields.contains(&field_token) {
continue;
}
scan_field_signature_blob(blob, field.signature, &mut typeref_rids);
}
}
}
if let Some(typedef_table) = tables.table::<TypeDefRaw>() {
for type_token in types {
if let Some(typedef) = typedef_table.get(type_token.row()) {
if typedef.extends.token.is_table(TableId::TypeRef) {
typeref_rids.insert(typedef.extends.token.row());
}
}
}
}
if let Some(attr_table) = tables.table::<CustomAttributeRaw>() {
for attr in attr_table {
let parent_token = attr.parent.token;
let parent_deleted = types.contains(&parent_token)
|| methods.contains(&parent_token)
|| fields.contains(&parent_token);
if !parent_deleted {
continue;
}
il_tokens.insert(attr.constructor.token);
}
}
}
PreDeletionRefs {
il_tokens,
typeref_rids,
standalonesig_rids,
}
}
fn get_effective_method_rva(assembly: &CilAssembly, rid: u32, original_rva: u32) -> u32 {
if let Some(table_mods) = assembly.changes().table_changes.get(&TableId::MethodDef) {
match table_mods {
TableModifications::Sparse { operations, .. } => {
for op in operations.iter().rev() {
if op.get_rid() == rid {
match &op.operation {
Operation::Update(_, TableDataOwned::MethodDef(updated)) => {
return updated.rva;
}
Operation::Delete(_) => {
return 0;
}
_ => {}
}
}
}
}
TableModifications::Replaced(rows) => {
if let Some(TableDataOwned::MethodDef(row)) = rows.get((rid - 1) as usize) {
return row.rva;
}
}
}
}
original_rva
}
fn scan_method_body_bytes(data: &[u8], base_rva: usize, referenced: &mut HashSet<Token>) {
let Ok(body) = MethodBody::from(data) else {
return;
};
let code_start = body.size_header;
let code_end = code_start + body.size_code;
if code_end > data.len() {
return;
}
let code_data = &data[code_start..code_end];
let code_rva = base_rva + body.size_header;
let collect = |blocks: &[BasicBlock], out: &mut HashSet<Token>| {
for block in blocks {
for instr in &block.instructions {
if let Operand::Token(token) = instr.operand {
out.insert(token);
}
}
}
};
if let Ok(blocks) = decode_blocks(code_data, 0, code_rva, None) {
collect(&blocks, referenced);
}
for handler in &body.exception_handlers {
let h_offset = handler.handler_offset as usize;
if h_offset < code_data.len() {
let h_rva = code_rva + h_offset;
if let Ok(blocks) = decode_blocks(code_data, h_offset, h_rva, None) {
collect(&blocks, referenced);
}
}
if let Some(class_token) = handler.get_class_token() {
referenced.insert(Token::new(class_token));
}
}
}
fn collect_type_tokens_from_local_sig(blob_data: &[u8], referenced: &mut HashSet<Token>) {
let Ok(sig) = parse_local_var_signature(blob_data) else {
return;
};
for local in &sig.locals {
collect_all_type_tokens(&local.base, referenced);
}
}
fn collect_all_type_tokens(sig: &TypeSignature, out: &mut HashSet<Token>) {
match sig {
TypeSignature::Class(token) | TypeSignature::ValueType(token) => {
let table = token.table();
if table == 0x01 || table == 0x02 || table == 0x1B {
out.insert(*token);
}
}
TypeSignature::GenericInst(base, args) => {
collect_all_type_tokens(base, out);
for arg in args {
collect_all_type_tokens(arg, out);
}
}
TypeSignature::SzArray(arr) => collect_all_type_tokens(&arr.base, out),
TypeSignature::Array(arr) => collect_all_type_tokens(&arr.base, out),
TypeSignature::Ptr(ptr) => collect_all_type_tokens(&ptr.base, out),
TypeSignature::ByRef(inner) | TypeSignature::Pinned(inner) => {
collect_all_type_tokens(inner, out);
}
_ => {}
}
}
pub(super) fn collect_typedefs_from_field_signatures(assembly: &CilAssembly) -> HashSet<Token> {
let mut referenced = HashSet::new();
let view = assembly.view();
let Some(tables) = view.tables() else {
return referenced;
};
let Some(field_table) = tables.table::<FieldRaw>() else {
return referenced;
};
let Some(blob_heap) = view.blobs() else {
return referenced;
};
for field in field_table.iter() {
if assembly.changes().is_row_deleted(TableId::Field, field.rid) {
continue;
}
if let Ok(blob_data) = blob_heap.get(field.signature as usize) {
if let Ok(sig) = parse_field_signature(blob_data) {
collect_all_type_tokens(&sig.base, &mut referenced);
}
}
}
referenced
}
pub(super) fn collect_referenced_standalonesig_rids(assembly: &CilAssembly) -> HashSet<u32> {
let mut referenced = HashSet::new();
let method_rvas: Vec<u32> = {
let view = assembly.view();
let Some(tables) = view.tables() else {
return referenced;
};
let Some(methoddef_table) = tables.table::<MethodDefRaw>() else {
return referenced;
};
methoddef_table
.into_iter()
.filter(|m| !assembly.changes().is_row_deleted(TableId::MethodDef, m.rid))
.map(|m| get_effective_method_rva(assembly, m.rid, m.rva))
.filter(|&rva| rva != 0)
.collect()
};
for effective_rva in method_rvas {
with_method_body(assembly, effective_rva, &mut |data, _| {
if let Some(sig_rid) = extract_local_var_sig_rid(data) {
referenced.insert(sig_rid);
}
});
}
referenced
}
pub(super) fn scan_method_body_tokens(assembly: &CilAssembly) -> HashSet<Token> {
let mut referenced = HashSet::new();
let method_rvas: Vec<u32> = {
let view = assembly.view();
let Some(tables) = view.tables() else {
return referenced;
};
let Some(methoddef_table) = tables.table::<MethodDefRaw>() else {
return referenced;
};
methoddef_table
.into_iter()
.filter(|m| !assembly.changes().is_row_deleted(TableId::MethodDef, m.rid))
.map(|m| get_effective_method_rva(assembly, m.rid, m.rva))
.filter(|&rva| rva != 0)
.collect()
};
let mut local_sig_rids: HashSet<u32> = HashSet::new();
for &effective_rva in &method_rvas {
with_method_body(assembly, effective_rva, &mut |data, base_rva| {
scan_method_body_bytes(data, base_rva, &mut referenced);
if let Ok(body) = MethodBody::from(data) {
if body.local_var_sig_token != 0 {
local_sig_rids.insert(body.local_var_sig_token & 0x00FF_FFFF);
}
}
});
}
{
let view = assembly.view();
if let (Some(tables), Some(blob_heap)) = (view.tables(), view.blobs()) {
if let Some(sig_table) = tables.table::<StandAloneSigRaw>() {
for &rid in &local_sig_rids {
if let Some(sig_row) = sig_table.get(rid) {
if let Ok(blob_data) = blob_heap.get(sig_row.signature as usize) {
collect_type_tokens_from_local_sig(blob_data, &mut referenced);
}
}
}
}
}
}
referenced
}
pub(super) fn collect_typerefs_from_deleted_memberref_sigs(
assembly: &CilAssembly,
memberref_rids: &HashSet<u32>,
) -> HashSet<u32> {
let mut result = HashSet::new();
if memberref_rids.is_empty() {
return result;
}
let view = assembly.view();
let Some(tables) = view.tables() else {
return result;
};
let Some(blob_heap) = view.blobs() else {
return result;
};
let Some(memberref_table) = tables.table::<MemberRefRaw>() else {
return result;
};
for &rid in memberref_rids {
if let Some(memberref) = memberref_table.get(rid) {
if !scan_method_signature_blob(blob_heap, memberref.signature, &mut result) {
scan_field_signature_blob(blob_heap, memberref.signature, &mut result);
}
}
}
result
}
pub(super) fn scan_typeref_metadata_refs(assembly: &CilAssembly) -> HashSet<u32> {
let mut referenced_rids = HashSet::new();
let view = assembly.view();
let Some(tables) = view.tables() else {
return referenced_rids;
};
if let Some(typedef_table) = tables.table::<TypeDefRaw>() {
for typedef in typedef_table {
if assembly
.changes()
.is_row_deleted(TableId::TypeDef, typedef.rid)
{
continue;
}
if typedef.extends.token.is_table(TableId::TypeRef) {
referenced_rids.insert(typedef.extends.token.row());
}
}
}
if let Some(interfaceimpl_table) = tables.table::<InterfaceImplRaw>() {
for impl_ in interfaceimpl_table {
if assembly
.changes()
.is_row_deleted(TableId::InterfaceImpl, impl_.rid)
{
continue;
}
if impl_.interface.token.is_table(TableId::TypeRef) {
referenced_rids.insert(impl_.interface.token.row());
}
}
}
if let Some(memberref_table) = tables.table::<MemberRefRaw>() {
for memberref in memberref_table {
if assembly
.changes()
.is_row_deleted(TableId::MemberRef, memberref.rid)
{
continue;
}
if memberref.class.token.is_table(TableId::TypeRef) {
referenced_rids.insert(memberref.class.token.row());
}
}
}
if let Some(constraint_table) = tables.table::<GenericParamConstraintRaw>() {
for constraint in constraint_table {
if assembly
.changes()
.is_row_deleted(TableId::GenericParamConstraint, constraint.rid)
{
continue;
}
if constraint.constraint.token.is_table(TableId::TypeRef) {
referenced_rids.insert(constraint.constraint.token.row());
}
}
}
if let Some(attr_table) = tables.table::<CustomAttributeRaw>() {
if let Some(memberref_table) = tables.table::<MemberRefRaw>() {
for attr in attr_table {
if assembly
.changes()
.is_row_deleted(TableId::CustomAttribute, attr.rid)
{
continue;
}
if attr.constructor.token.is_table(TableId::MemberRef) {
let memberref_rid = attr.constructor.token.row();
if assembly
.changes()
.is_row_deleted(TableId::MemberRef, memberref_rid)
{
continue;
}
if let Some(memberref) = memberref_table.get(memberref_rid) {
if memberref.class.token.is_table(TableId::TypeRef) {
referenced_rids.insert(memberref.class.token.row());
}
}
}
}
}
}
referenced_rids
}
pub(super) fn scan_memberref_metadata_refs(assembly: &CilAssembly) -> HashSet<u32> {
let mut referenced_rids = HashSet::new();
let view = assembly.view();
let Some(tables) = view.tables() else {
return referenced_rids;
};
if let Some(attr_table) = tables.table::<CustomAttributeRaw>() {
for attr in attr_table {
if assembly
.changes()
.is_row_deleted(TableId::CustomAttribute, attr.rid)
{
continue;
}
if attr.constructor.token.is_table(TableId::MemberRef) {
referenced_rids.insert(attr.constructor.token.row());
}
}
}
if let Some(methodspec_table) = tables.table::<MethodSpecRaw>() {
for spec in methodspec_table {
if assembly
.changes()
.is_row_deleted(TableId::MethodSpec, spec.rid)
{
continue;
}
if spec.method.token.is_table(TableId::MemberRef) {
referenced_rids.insert(spec.method.token.row());
}
}
}
referenced_rids
}
pub(super) fn scan_typespec_metadata_refs(assembly: &CilAssembly) -> HashSet<u32> {
let mut referenced_rids = HashSet::new();
let view = assembly.view();
let Some(tables) = view.tables() else {
return referenced_rids;
};
if let Some(memberref_table) = tables.table::<MemberRefRaw>() {
for memberref in memberref_table {
if assembly
.changes()
.is_row_deleted(TableId::MemberRef, memberref.rid)
{
continue;
}
if memberref.class.token.is_table(TableId::TypeSpec) {
referenced_rids.insert(memberref.class.token.row());
}
}
}
if let Some(interfaceimpl_table) = tables.table::<InterfaceImplRaw>() {
for impl_ in interfaceimpl_table {
if assembly
.changes()
.is_row_deleted(TableId::InterfaceImpl, impl_.rid)
{
continue;
}
if impl_.interface.token.is_table(TableId::TypeSpec) {
referenced_rids.insert(impl_.interface.token.row());
}
}
}
if let Some(constraint_table) = tables.table::<GenericParamConstraintRaw>() {
for constraint in constraint_table {
if assembly
.changes()
.is_row_deleted(TableId::GenericParamConstraint, constraint.rid)
{
continue;
}
if constraint.constraint.token.is_table(TableId::TypeSpec) {
referenced_rids.insert(constraint.constraint.token.row());
}
}
}
if let Some(typedef_table) = tables.table::<TypeDefRaw>() {
for typedef in typedef_table {
if assembly
.changes()
.is_row_deleted(TableId::TypeDef, typedef.rid)
{
continue;
}
if typedef.extends.token.is_table(TableId::TypeSpec) {
referenced_rids.insert(typedef.extends.token.row());
}
}
}
referenced_rids
}
fn collect_typerefs_from_type(sig: &TypeSignature, referenced: &mut HashSet<u32>) {
match sig {
TypeSignature::Class(token) | TypeSignature::ValueType(token) => {
if token.is_table(TableId::TypeRef) {
referenced.insert(token.row());
}
}
TypeSignature::SzArray(arr) => {
for modifier in &arr.modifiers {
collect_typerefs_from_modifier(modifier, referenced);
}
collect_typerefs_from_type(&arr.base, referenced);
}
TypeSignature::Array(arr) => {
collect_typerefs_from_type(&arr.base, referenced);
}
TypeSignature::Ptr(ptr) => {
for modifier in &ptr.modifiers {
collect_typerefs_from_modifier(modifier, referenced);
}
collect_typerefs_from_type(&ptr.base, referenced);
}
TypeSignature::ByRef(inner) | TypeSignature::Pinned(inner) => {
collect_typerefs_from_type(inner, referenced);
}
TypeSignature::GenericInst(base, args) => {
collect_typerefs_from_type(base, referenced);
for arg in args {
collect_typerefs_from_type(arg, referenced);
}
}
TypeSignature::ModifiedRequired(modifiers) | TypeSignature::ModifiedOptional(modifiers) => {
for modifier in modifiers {
collect_typerefs_from_modifier(modifier, referenced);
}
}
TypeSignature::FnPtr(method_sig) => {
collect_typerefs_from_parameter(&method_sig.return_type, referenced);
for param in &method_sig.params {
collect_typerefs_from_parameter(param, referenced);
}
}
TypeSignature::Void
| TypeSignature::Boolean
| TypeSignature::Char
| TypeSignature::I1
| TypeSignature::U1
| TypeSignature::I2
| TypeSignature::U2
| TypeSignature::I4
| TypeSignature::U4
| TypeSignature::I8
| TypeSignature::U8
| TypeSignature::R4
| TypeSignature::R8
| TypeSignature::I
| TypeSignature::U
| TypeSignature::String
| TypeSignature::Object
| TypeSignature::TypedByRef
| TypeSignature::GenericParamType(_)
| TypeSignature::GenericParamMethod(_)
| TypeSignature::Sentinel
| TypeSignature::Internal
| TypeSignature::Unknown
| TypeSignature::Type
| TypeSignature::Boxed
| TypeSignature::Field
| TypeSignature::Modifier
| TypeSignature::Reserved => {}
}
}
fn collect_typerefs_from_modifier(modifier: &CustomModifier, referenced: &mut HashSet<u32>) {
if modifier.modifier_type.is_table(TableId::TypeRef) {
referenced.insert(modifier.modifier_type.row());
}
}
fn collect_typerefs_from_parameter(param: &SignatureParameter, referenced: &mut HashSet<u32>) {
for modifier in ¶m.modifiers {
collect_typerefs_from_modifier(modifier, referenced);
}
collect_typerefs_from_type(¶m.base, referenced);
}
fn collect_typerefs_from_local(local: &SignatureLocalVariable, referenced: &mut HashSet<u32>) {
for modifier in &local.modifiers {
collect_typerefs_from_modifier(modifier, referenced);
}
collect_typerefs_from_type(&local.base, referenced);
}
pub(super) fn scan_signature_typeref_refs(assembly: &CilAssembly) -> HashSet<u32> {
let mut referenced_rids = HashSet::new();
let view = assembly.view();
let Some(tables) = view.tables() else {
return referenced_rids;
};
let Some(blob_heap) = view.blobs() else {
return referenced_rids;
};
if let Some(methoddef_table) = tables.table::<MethodDefRaw>() {
for methoddef in methoddef_table {
if assembly
.changes()
.is_row_deleted(TableId::MethodDef, methoddef.rid)
{
continue;
}
scan_method_signature_blob(blob_heap, methoddef.signature, &mut referenced_rids);
}
}
if let Some(field_table) = tables.table::<FieldRaw>() {
for field in field_table {
if assembly.changes().is_row_deleted(TableId::Field, field.rid) {
continue;
}
scan_field_signature_blob(blob_heap, field.signature, &mut referenced_rids);
}
}
if let Some(memberref_table) = tables.table::<MemberRefRaw>() {
for memberref in memberref_table {
if assembly
.changes()
.is_row_deleted(TableId::MemberRef, memberref.rid)
{
continue;
}
if !scan_method_signature_blob(blob_heap, memberref.signature, &mut referenced_rids) {
scan_field_signature_blob(blob_heap, memberref.signature, &mut referenced_rids);
}
}
}
let referenced_sigs = collect_referenced_standalonesig_rids(assembly);
if let Some(standalonesig_table) = tables.table::<StandAloneSigRaw>() {
for sig in standalonesig_table {
if referenced_sigs.contains(&sig.rid) {
scan_local_var_signature_blob(blob_heap, sig.signature, &mut referenced_rids);
}
}
}
if let Some(typespec_table) = tables.table::<TypeSpecRaw>() {
for typespec in typespec_table {
if assembly
.changes()
.is_row_deleted(TableId::TypeSpec, typespec.rid)
{
continue;
}
scan_typespec_signature_blob(blob_heap, typespec.signature, &mut referenced_rids);
}
}
if let Some(property_table) = tables.table::<PropertyRaw>() {
for property in property_table {
if assembly
.changes()
.is_row_deleted(TableId::Property, property.rid)
{
continue;
}
scan_property_signature_blob(blob_heap, property.signature, &mut referenced_rids);
}
}
referenced_rids
}
fn scan_method_signature_blob(
blob_heap: &Blob<'_>,
blob_index: u32,
referenced: &mut HashSet<u32>,
) -> bool {
let Ok(blob_data) = blob_heap.get(blob_index as usize) else {
return false;
};
let Ok(sig) = parse_method_signature(blob_data) else {
return false;
};
collect_typerefs_from_parameter(&sig.return_type, referenced);
for param in &sig.params {
collect_typerefs_from_parameter(param, referenced);
}
true
}
fn scan_field_signature_blob(
blob_heap: &Blob<'_>,
blob_index: u32,
referenced: &mut HashSet<u32>,
) -> bool {
let Ok(blob_data) = blob_heap.get(blob_index as usize) else {
return false;
};
let Ok(sig) = parse_field_signature(blob_data) else {
return false;
};
for modifier in &sig.modifiers {
collect_typerefs_from_modifier(modifier, referenced);
}
collect_typerefs_from_type(&sig.base, referenced);
true
}
fn scan_local_var_signature_blob(
blob_heap: &Blob<'_>,
blob_index: u32,
referenced: &mut HashSet<u32>,
) {
let Ok(blob_data) = blob_heap.get(blob_index as usize) else {
return;
};
let Ok(sig) = parse_local_var_signature(blob_data) else {
return;
};
for local in &sig.locals {
collect_typerefs_from_local(local, referenced);
}
}
fn scan_typespec_signature_blob(
blob_heap: &Blob<'_>,
blob_index: u32,
referenced: &mut HashSet<u32>,
) {
let Ok(blob_data) = blob_heap.get(blob_index as usize) else {
return;
};
let Ok(sig) = parse_type_spec_signature(blob_data) else {
return;
};
collect_typerefs_from_type(&sig.base, referenced);
}
fn scan_property_signature_blob(
blob_heap: &Blob<'_>,
blob_index: u32,
referenced: &mut HashSet<u32>,
) {
let Ok(blob_data) = blob_heap.get(blob_index as usize) else {
return;
};
let Ok(sig) = parse_property_signature(blob_data) else {
return;
};
for modifier in &sig.modifiers {
collect_typerefs_from_modifier(modifier, referenced);
}
collect_typerefs_from_type(&sig.base, referenced);
for param in &sig.params {
collect_typerefs_from_parameter(param, referenced);
}
}
pub(super) fn remove_unreferenced_typerefs(
assembly: &mut CilAssembly,
candidates: &BTreeSet<u32>,
body_tokens: &HashSet<Token>,
) -> (usize, HashSet<u32>) {
if candidates.is_empty() {
return (0, HashSet::new());
}
let mut referenced_rids = HashSet::new();
for token in body_tokens {
if token.is_table(TableId::TypeRef) {
referenced_rids.insert(token.row());
}
}
referenced_rids.extend(scan_typeref_metadata_refs(assembly));
let signature_refs = scan_signature_typeref_refs(assembly);
referenced_rids.extend(signature_refs);
let memberref_body_rids: HashSet<u32> = body_tokens
.iter()
.filter(|t| t.is_table(TableId::MemberRef))
.map(Token::row)
.collect();
{
let view = assembly.view();
if let Some(tables) = view.tables() {
if let Some(memberref_table) = tables.table::<MemberRefRaw>() {
for memberref in memberref_table {
if assembly
.changes()
.is_row_deleted(TableId::MemberRef, memberref.rid)
{
continue;
}
if memberref_body_rids.contains(&memberref.rid)
&& memberref.class.token.is_table(TableId::TypeRef)
{
referenced_rids.insert(memberref.class.token.row());
}
}
}
}
}
remove_candidates_not_alive(assembly, TableId::TypeRef, candidates, &referenced_rids)
}
pub(super) fn remove_unreferenced_memberrefs(
assembly: &mut CilAssembly,
candidates: &BTreeSet<u32>,
body_tokens: &HashSet<Token>,
) -> (usize, HashSet<u32>) {
if candidates.is_empty() {
return (0, HashSet::new());
}
let mut referenced_rids = HashSet::new();
for token in body_tokens {
if token.is_table(TableId::MemberRef) {
referenced_rids.insert(token.row());
}
}
referenced_rids.extend(scan_memberref_metadata_refs(assembly));
let methodspec_body_rids: HashSet<u32> = body_tokens
.iter()
.filter(|t| t.is_table(TableId::MethodSpec))
.map(Token::row)
.collect();
{
let view = assembly.view();
if let Some(tables) = view.tables() {
if let Some(methodspec_table) = tables.table::<MethodSpecRaw>() {
for spec in methodspec_table {
if assembly
.changes()
.is_row_deleted(TableId::MethodSpec, spec.rid)
{
continue;
}
if methodspec_body_rids.contains(&spec.rid)
&& spec.method.token.is_table(TableId::MemberRef)
{
referenced_rids.insert(spec.method.token.row());
}
}
}
}
}
remove_candidates_not_alive(assembly, TableId::MemberRef, candidates, &referenced_rids)
}
pub(super) fn remove_unreferenced_typespecs(
assembly: &mut CilAssembly,
candidates: &BTreeSet<u32>,
body_tokens: &HashSet<Token>,
) -> (usize, HashSet<u32>) {
if candidates.is_empty() {
return (0, HashSet::new());
}
let mut referenced_rids = HashSet::new();
for token in body_tokens {
if token.is_table(TableId::TypeSpec) {
referenced_rids.insert(token.row());
}
}
referenced_rids.extend(scan_typespec_metadata_refs(assembly));
remove_candidates_not_alive(assembly, TableId::TypeSpec, candidates, &referenced_rids)
}
#[cfg(test)]
mod tests {
use crate::{
cilassembly::cleanup::utils::PLACEHOLDER_RVA_THRESHOLD,
metadata::{tables::TableId, token::Token},
};
#[test]
fn test_placeholder_rva_detection() {
const { assert!(0xF000_0000 >= PLACEHOLDER_RVA_THRESHOLD) };
const { assert!(0xF000_0001 >= PLACEHOLDER_RVA_THRESHOLD) };
const { assert!(0x2000 < PLACEHOLDER_RVA_THRESHOLD) };
const { assert!(0x0000_FFFF < PLACEHOLDER_RVA_THRESHOLD) };
}
#[test]
fn test_token_table_detection() {
let typeref_token = Token::new(0x01000005);
let memberref_token = Token::new(0x0A000010);
let typespec_token = Token::new(0x1B000003);
assert!(typeref_token.is_table(TableId::TypeRef));
assert!(memberref_token.is_table(TableId::MemberRef));
assert!(typespec_token.is_table(TableId::TypeSpec));
}
}