use crate::decompiler::cfg::ssa::optimize_ssa;
use crate::decompiler::cfg::structure_cfg;
use crate::decompiler::helpers::format_manifest_type;
use crate::decompiler::ir::render_block;
use crate::manifest::ContractManifest;
use super::MethodView;
pub(crate) fn render_envelope(
nef: &crate::nef::NefFile,
manifest: Option<&ContractManifest>,
methods: &[MethodView],
) -> String {
use crate::decompiler::write_contract_header;
let mut out = String::new();
write_contract_header(&mut out, nef, manifest);
for view in methods {
out.push_str(&render_method_body(view, manifest));
out.push('\n');
}
out.push_str("}\n");
out
}
pub(super) fn render_method_body(view: &MethodView, manifest: Option<&ContractManifest>) -> String {
let mut ssa =
crate::decompiler::cfg::ssa::SsaBuilder::new(&view.cfg, &view.instructions).build();
optimize_ssa(&mut ssa);
let block = structure_cfg(&ssa);
let body = render_block(&block, 0);
let ret = method_return_type(view, manifest);
let name = sanitize_name(&view.method.name);
if body.trim().is_empty() {
format!(" fn {name}() -> {ret} {{\n // empty body\n }}\n")
} else {
let indented = body
.lines()
.map(|l| {
if l.is_empty() {
String::new()
} else {
format!(" {l}")
}
})
.collect::<Vec<_>>()
.join("\n");
format!(" fn {name}() -> {ret} {{\n{indented}\n }}\n")
}
}
fn method_return_type(view: &MethodView, manifest: Option<&ContractManifest>) -> String {
let Some(manifest) = manifest else {
return "void".to_string();
};
manifest
.abi
.methods
.iter()
.find(|m| m.name == view.method.name)
.map(|m| format_manifest_type(&m.return_type))
.unwrap_or_else(|| "void".to_string())
}
fn sanitize_name(raw: &str) -> String {
let s: String = raw
.chars()
.map(|c| {
if c.is_ascii_alphanumeric() || c == '_' {
c
} else {
'_'
}
})
.collect();
if s.is_empty() {
"sub".to_string()
} else {
s
}
}