use std::collections::HashSet;
use serde::Serialize;
mod instructions;
mod manifest;
mod method_tokens;
use crate::decompiler::analysis::{call_graph::CallGraph, types::TypeInfo, xrefs::Xrefs};
use crate::decompiler::Decompilation;
use crate::disassembler::DisassemblyOutput;
use crate::manifest::ContractManifest;
use crate::nef::NefFile;
use crate::util;
use instructions::InstructionReport;
use manifest::{summarize_manifest, ManifestSummary};
use method_tokens::{build_method_token_report, collect_warnings, MethodTokenReport};
#[derive(Debug, Clone, Serialize)]
pub struct WebInfoReport {
compiler: String,
source: Option<String>,
script_length: usize,
script_hash_le: String,
script_hash_be: String,
checksum: String,
method_tokens: Vec<MethodTokenReport>,
manifest: Option<ManifestSummary>,
warnings: Vec<String>,
}
#[derive(Debug, Clone, Serialize)]
pub struct WebDisasmReport {
script_hash_le: String,
script_hash_be: String,
instructions: Vec<InstructionReport>,
warnings: Vec<String>,
}
#[derive(Debug, Clone, Serialize)]
pub struct WebDecompileReport {
compiler: String,
source: Option<String>,
script_hash_le: String,
script_hash_be: String,
csharp: String,
high_level: String,
pseudocode: String,
instructions: Vec<InstructionReport>,
method_tokens: Vec<MethodTokenReport>,
manifest: Option<ManifestSummary>,
analysis: AnalysisReport,
warnings: Vec<String>,
}
pub(super) fn build_info_report(
nef: &NefFile,
manifest: Option<&ContractManifest>,
parse_warnings: &[String],
) -> WebInfoReport {
let script_hash = nef.script_hash();
let method_tokens: Vec<MethodTokenReport> = nef
.method_tokens
.iter()
.map(build_method_token_report)
.collect();
let mut warnings = parse_warnings.to_vec();
for warning in collect_warnings(&method_tokens) {
if !warnings.contains(&warning) {
warnings.push(warning);
}
}
WebInfoReport {
compiler: nef.header.compiler.trim_end_matches('\0').to_string(),
source: (!nef.header.source.is_empty()).then(|| nef.header.source.clone()),
script_length: nef.script.len(),
script_hash_le: util::format_hash(&script_hash),
script_hash_be: util::format_hash_be(&script_hash),
checksum: format!("0x{:08X}", nef.checksum),
warnings,
method_tokens,
manifest: manifest.map(summarize_manifest),
}
}
pub(super) fn build_disasm_report(
nef: &NefFile,
output: DisassemblyOutput,
parse_warnings: &[String],
) -> WebDisasmReport {
let script_hash = nef.script_hash();
let mut warnings = parse_warnings.to_vec();
for warning in output.warnings.iter().map(ToString::to_string) {
if !warnings.contains(&warning) {
warnings.push(warning);
}
}
WebDisasmReport {
script_hash_le: util::format_hash(&script_hash),
script_hash_be: util::format_hash_be(&script_hash),
instructions: output
.instructions
.iter()
.map(InstructionReport::from)
.collect(),
warnings,
}
}
pub(super) fn build_decompile_report(result: Decompilation) -> WebDecompileReport {
let Decompilation {
nef,
manifest,
warnings: decompile_warnings,
instructions,
call_graph,
xrefs,
types,
pseudocode,
high_level,
csharp,
..
} = result;
let script_hash = nef.script_hash();
let method_tokens: Vec<MethodTokenReport> = nef
.method_tokens
.iter()
.map(build_method_token_report)
.collect();
let mut warnings = Vec::new();
let mut seen = HashSet::new();
for warning in collect_warnings(&method_tokens)
.into_iter()
.chain(decompile_warnings)
{
if seen.insert(warning.clone()) {
warnings.push(warning);
}
}
WebDecompileReport {
compiler: nef.header.compiler.trim_end_matches('\0').to_string(),
source: (!nef.header.source.is_empty()).then(|| nef.header.source.clone()),
script_hash_le: util::format_hash(&script_hash),
script_hash_be: util::format_hash_be(&script_hash),
csharp: csharp.unwrap_or_default(),
high_level: high_level.unwrap_or_default(),
pseudocode: pseudocode.unwrap_or_default(),
instructions: instructions.iter().map(InstructionReport::from).collect(),
method_tokens,
manifest: manifest.as_ref().map(summarize_manifest),
analysis: AnalysisReport {
call_graph,
xrefs,
types,
},
warnings,
}
}
#[derive(Debug, Clone, Serialize)]
struct AnalysisReport {
call_graph: CallGraph,
xrefs: Xrefs,
types: TypeInfo,
}