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 self.inline_single_use_temps,
128 );
129 for warning in render.warnings {
130 push_warning(warning);
131 }
132 render.text
133 });
134 let csharp = output_format.wants_csharp().then(|| {
135 let render = csharp::render_csharp(&nef, &instructions, manifest.as_ref());
136 for warning in render.warnings {
137 push_warning(warning);
138 }
139 render.source
140 });
141
142 Ok(Decompilation {
143 nef,
144 manifest,
145 warnings,
146 instructions,
147 cfg,
148 call_graph,
149 xrefs,
150 types,
151 pseudocode,
152 high_level,
153 csharp,
154 ssa: None,
155 })
156 }
157
158 pub fn decompile_file<P: AsRef<std::path::Path>>(&self, path: P) -> Result<Decompilation> {
165 self.io_decompile_file(path)
166 }
167
168 pub fn disassemble_file<P: AsRef<std::path::Path>>(
175 &self,
176 path: P,
177 ) -> Result<DisassemblyOutput> {
178 self.io_disassemble_file(path)
179 }
180
181 pub fn decompile_file_with_manifest<P, Q>(
188 &self,
189 nef_path: P,
190 manifest_path: Option<Q>,
191 output_format: OutputFormat,
192 ) -> Result<Decompilation>
193 where
194 P: AsRef<std::path::Path>,
195 Q: AsRef<std::path::Path>,
196 {
197 self.io_decompile_file_with_manifest(nef_path, manifest_path, output_format)
198 }
199}