use std::collections::HashSet;
use std::fmt::Write;
use crate::instruction::Instruction;
use crate::manifest::ManifestMethod;
use super::super::super::super::helpers::offset_as_usize;
use super::super::super::helpers::{
collect_csharp_parameters, escape_csharp_string, format_csharp_parameters,
format_manifest_type_csharp, format_method_signature, sanitize_csharp_identifier,
};
use super::MethodsContext;
pub(super) struct ManifestRenderInfo<'a> {
pub(super) method: &'a ManifestMethod,
pub(super) method_name: String,
pub(super) param_signature: String,
pub(super) return_type: String,
}
pub(super) fn method_render_info<'a>(
method: &'a ManifestMethod,
used_signatures: &mut HashSet<(String, String)>,
) -> ManifestRenderInfo<'a> {
let params = collect_csharp_parameters(&method.parameters);
let signature_key = params
.iter()
.map(|param| param.ty.as_str())
.collect::<Vec<_>>()
.join(",");
let param_signature = format_csharp_parameters(¶ms);
let base_name = sanitize_csharp_identifier(&method.name);
let method_name = make_unique_method_name(base_name, &signature_key, used_signatures);
let return_type = format_manifest_type_csharp(&method.return_type, true);
ManifestRenderInfo {
method,
method_name,
param_signature,
return_type,
}
}
pub(super) fn write_method_open(output: &mut String, info: &ManifestRenderInfo<'_>) {
let signature =
format_method_signature(&info.method_name, &info.param_signature, &info.return_type);
write_method_attributes(
output,
&info.method_name,
&info.method.name,
info.method.safe,
);
writeln!(output, " {signature}").unwrap();
writeln!(output, " {{").unwrap();
}
pub(super) fn write_method_close(output: &mut String) {
writeln!(output, " }}").unwrap();
writeln!(output).unwrap();
}
pub(super) fn instruction_slice(
instructions: &[Instruction],
start: usize,
end: Option<usize>,
) -> Vec<Instruction> {
match end {
Some(end) => instructions
.iter()
.filter(|ins| ins.offset >= start && ins.offset < end)
.cloned()
.collect(),
None => instructions.to_vec(),
}
}
pub(super) fn non_empty_or_all(
slice: Vec<Instruction>,
instructions: &[Instruction],
) -> Vec<Instruction> {
if slice.is_empty() {
instructions.to_vec()
} else {
slice
}
}
pub(super) fn format_inferred_parameters(
arg_count: usize,
method_offset: usize,
context: &MethodsContext<'_>,
typed: bool,
fallback_type: &str,
) -> String {
let slot_types = typed
.then(|| {
context
.body_context
.slot_types_by_offset
.get(&method_offset)
})
.flatten();
(0..arg_count)
.map(|index| {
let ty = slot_types
.and_then(|types| types.argument_type(index))
.unwrap_or(fallback_type);
format!("{ty} arg{index}")
})
.collect::<Vec<_>>()
.join(", ")
}
pub(super) fn synthetic_entry_arguments(
slice: &[Instruction],
entry_offset: usize,
context: &MethodsContext<'_>,
typed: bool,
) -> (String, Vec<String>) {
let arg_count =
crate::decompiler::helpers::initslot_argument_count_at(slice, entry_offset).unwrap_or(0);
let parameters = format_inferred_parameters(arg_count, entry_offset, context, typed, "object");
let labels: Vec<String> = (0..arg_count).map(|i| format!("arg{i}")).collect();
(parameters, labels)
}
pub(super) fn method_offset(method: &ManifestMethod) -> usize {
offset_as_usize(method.offset).unwrap_or(0)
}
fn write_method_attributes(output: &mut String, method_name: &str, raw_name: &str, is_safe: bool) {
if method_name != raw_name {
writeln!(
output,
" [DisplayName(\"{}\")]",
escape_csharp_string(raw_name)
)
.unwrap();
}
if is_safe {
writeln!(output, " [Safe]").unwrap();
}
}
fn make_unique_method_name(
base: String,
signature: &str,
used: &mut HashSet<(String, String)>,
) -> String {
let mut candidate = base.clone();
let mut index = 1usize;
while !used.insert((candidate.clone(), signature.to_string())) {
candidate = format!("{base}_{index}");
index += 1;
}
candidate
}