use std::collections::{BTreeMap, HashSet};
use crate::instruction::Instruction;
use crate::manifest::ContractManifest;
use super::manifest::{find_manifest_entry_method, offset_as_usize};
use crate::decompiler::helpers::identifiers::make_unique_identifier;
pub(in super::super::super) fn build_method_labels_by_offset(
instructions: &[Instruction],
inferred_starts: &[usize],
manifest: Option<&ContractManifest>,
sanitize: impl Fn(&str) -> String,
fallback_name: &str,
) -> BTreeMap<usize, String> {
let mut labels = BTreeMap::new();
let mut used = HashSet::new();
let entry_offset = instructions.first().map(|ins| ins.offset).unwrap_or(0);
let entry_method = manifest.and_then(|m| find_manifest_entry_method(m, entry_offset));
let use_manifest_entry = entry_method.is_some();
let entry_name = if use_manifest_entry {
entry_method
.as_ref()
.map(|(method, _)| sanitize(&method.name))
.unwrap_or_else(|| fallback_name.to_string())
} else {
fallback_name.to_string()
};
labels.insert(entry_offset, make_unique_identifier(entry_name, &mut used));
let entry_manifest_marker = if use_manifest_entry {
entry_method
.as_ref()
.map(|(method, _)| (method.name.clone(), method.offset))
} else {
None
};
if let Some(manifest) = manifest {
let mut methods: Vec<_> = manifest.abi.methods.iter().collect();
methods.sort_by_key(|m| m.offset.unwrap_or(i32::MAX));
for method in methods {
if entry_manifest_marker
.as_ref()
.map(|(name, offset)| name == &method.name && offset == &method.offset)
.unwrap_or(false)
{
continue;
}
let Some(start) = offset_as_usize(method.offset) else {
continue;
};
labels
.entry(start)
.or_insert_with(|| make_unique_identifier(sanitize(&method.name), &mut used));
}
}
let entry_manifest_offset = entry_manifest_marker
.as_ref()
.and_then(|(_, offset)| offset.and_then(|value| usize::try_from(value).ok()));
let manifest_offsets: HashSet<usize> = manifest
.map(|m| {
m.abi
.methods
.iter()
.filter_map(|method| offset_as_usize(method.offset))
.collect()
})
.unwrap_or_default();
for start in inferred_starts {
if Some(*start) == Some(entry_offset)
|| Some(*start) == entry_manifest_offset
|| manifest_offsets.contains(start)
{
continue;
}
labels.entry(*start).or_insert_with(|| {
let base_name = format!("sub_0x{start:04X}");
make_unique_identifier(base_name, &mut used)
});
}
labels
}