neo_decompiler/decompiler/
pipeline.rs1use std::collections::HashSet;
2
3use crate::disassembler::{Disassembler, DisassemblyOutput, UnknownHandling};
4use crate::error::Result;
5use crate::manifest::ContractManifest;
6use crate::nef::NefParser;
7
8use super::cfg::CfgBuilder;
9use super::decompilation::Decompilation;
10use super::output_format::OutputFormat;
11use super::{analysis, csharp, high_level, pseudocode};
12
13mod io;
14
15#[derive(Debug, Default)]
17pub struct Decompiler {
18 parser: NefParser,
19 disassembler: Disassembler,
20 inline_single_use_temps: bool,
21}
22
23impl Decompiler {
24 #[must_use]
28 pub fn new() -> Self {
29 Self::with_unknown_handling(UnknownHandling::Permit)
30 }
31
32 #[must_use]
47 pub fn with_unknown_handling(handling: UnknownHandling) -> Self {
48 Self {
49 parser: NefParser::new(),
50 disassembler: Disassembler::with_unknown_handling(handling),
51 inline_single_use_temps: false,
52 }
53 }
54
55 #[must_use]
60 pub fn with_inline_single_use_temps(mut self, enabled: bool) -> Self {
61 self.inline_single_use_temps = enabled;
62 self
63 }
64
65 pub fn decompile_bytes(&self, bytes: &[u8]) -> Result<Decompilation> {
71 self.decompile_bytes_with_manifest(bytes, None, OutputFormat::All)
72 }
73
74 pub fn disassemble_bytes(&self, bytes: &[u8]) -> Result<DisassemblyOutput> {
83 let nef = self.parser.parse(bytes)?;
84 self.disassembler.disassemble_with_warnings(&nef.script)
85 }
86
87 pub fn decompile_bytes_with_manifest(
93 &self,
94 bytes: &[u8],
95 manifest: Option<ContractManifest>,
96 output_format: OutputFormat,
97 ) -> Result<Decompilation> {
98 let nef = self.parser.parse(bytes)?;
99 let disassembly = self.disassembler.disassemble_with_warnings(&nef.script)?;
100 let instructions = disassembly.instructions;
101
102 let mut warnings = Vec::new();
103 let mut seen_warnings = HashSet::new();
104 let mut push_warning = |warning: String| {
105 if seen_warnings.insert(warning.clone()) {
106 warnings.push(warning);
107 }
108 };
109 for warning in disassembly.warnings {
110 push_warning(warning.to_string());
111 }
112
113 let cfg = CfgBuilder::new(&instructions).build();
114 let call_graph =
115 analysis::call_graph::build_call_graph(&nef, &instructions, manifest.as_ref());
116 let xrefs = analysis::xrefs::build_xrefs(&instructions, manifest.as_ref());
117 let types = analysis::types::infer_types(&instructions, manifest.as_ref());
118
119 let pseudocode = output_format
120 .wants_pseudocode()
121 .then(|| pseudocode::render(&instructions));
122 let high_level = output_format.wants_high_level().then(|| {
123 let render = high_level::render_high_level(
124 &nef,
125 &instructions,
126 manifest.as_ref(),
127 &call_graph,
128 self.inline_single_use_temps,
129 );
130 for warning in render.warnings {
131 push_warning(warning);
132 }
133 render.text
134 });
135 let csharp = output_format.wants_csharp().then(|| {
136 let render = csharp::render_csharp(&nef, &instructions, manifest.as_ref(), &call_graph);
137 for warning in render.warnings {
138 push_warning(warning);
139 }
140 render.source
141 });
142
143 Ok(Decompilation {
144 nef,
145 manifest,
146 warnings,
147 instructions,
148 cfg,
149 call_graph,
150 xrefs,
151 types,
152 pseudocode,
153 high_level,
154 csharp,
155 ssa: None,
156 })
157 }
158
159 pub fn decompile_file<P: AsRef<std::path::Path>>(&self, path: P) -> Result<Decompilation> {
166 self.io_decompile_file(path)
167 }
168
169 pub fn disassemble_file<P: AsRef<std::path::Path>>(
176 &self,
177 path: P,
178 ) -> Result<DisassemblyOutput> {
179 self.io_disassemble_file(path)
180 }
181
182 pub fn decompile_file_with_manifest<P, Q>(
189 &self,
190 nef_path: P,
191 manifest_path: Option<Q>,
192 output_format: OutputFormat,
193 ) -> Result<Decompilation>
194 where
195 P: AsRef<std::path::Path>,
196 Q: AsRef<std::path::Path>,
197 {
198 self.io_decompile_file_with_manifest(nef_path, manifest_path, output_format)
199 }
200}