neo_decompiler/decompiler/decompilation.rs
1use crate::instruction::Instruction;
2use crate::manifest::ContractManifest;
3use crate::nef::NefFile;
4
5use super::analysis::call_graph::CallGraph;
6use super::analysis::types::TypeInfo;
7use super::analysis::xrefs::Xrefs;
8use super::cfg::ssa::{SsaBuilder, SsaForm};
9use super::cfg::Cfg;
10
11/// Result of a successful decompilation run.
12#[derive(Debug, Clone)]
13#[non_exhaustive]
14pub struct Decompilation {
15 /// Parsed NEF container.
16 pub nef: NefFile,
17 /// Optional parsed contract manifest.
18 pub manifest: Option<ContractManifest>,
19 /// Non-fatal warnings emitted during disassembly or rendering.
20 pub warnings: Vec<String>,
21 /// Disassembled instruction stream from the NEF script.
22 pub instructions: Vec<Instruction>,
23 /// Control flow graph built from the instruction stream.
24 pub cfg: Cfg,
25 /// Best-effort call graph extracted from the instruction stream.
26 pub call_graph: CallGraph,
27 /// Best-effort cross-reference information for locals/args/statics.
28 pub xrefs: Xrefs,
29 /// Best-effort primitive/collection type inference.
30 pub types: TypeInfo,
31 /// Optional rendered pseudocode output.
32 pub pseudocode: Option<String>,
33 /// Optional rendered high-level output.
34 pub high_level: Option<String>,
35 /// Optional rendered C# output.
36 pub csharp: Option<String>,
37 /// SSA form of the control flow graph (computed lazily).
38 pub ssa: Option<SsaForm>,
39}
40
41impl Decompilation {
42 /// Get the control flow graph as DOT format for visualization.
43 ///
44 /// The DOT output can be rendered using Graphviz or similar tools.
45 ///
46 /// # Example
47 /// ```ignore
48 /// let decompilation = decompiler.decompile_bytes(&nef_bytes)?;
49 /// let dot = decompilation.cfg_to_dot();
50 /// std::fs::write("cfg.dot", dot)?;
51 /// // Then run: dot -Tpng cfg.dot -o cfg.png
52 /// ```
53 #[must_use]
54 pub fn cfg_to_dot(&self) -> String {
55 self.cfg.to_dot()
56 }
57
58 /// Get or compute the SSA form of this decompilation.
59 ///
60 /// SSA form is computed lazily on first call and cached for subsequent calls.
61 ///
62 /// # Returns
63 ///
64 /// `Option<&SsaForm>` - The SSA form, or `None` if CFG has no blocks.
65 ///
66 /// # Examples
67 ///
68 /// ```ignore
69 /// let decompilation = decompiler.decompile_bytes(&nef_bytes)?;
70 /// if let Some(ssa) = decompilation.ssa() {
71 /// println!("SSA Stats: {}", ssa.stats());
72 /// println!("{}", ssa.render());
73 /// }
74 /// ```
75 #[must_use]
76 pub fn ssa(&self) -> Option<&SsaForm> {
77 self.ssa.as_ref()
78 }
79
80 /// Compute SSA form if not already computed.
81 ///
82 /// This is a convenience method for computing SSA form lazily.
83 /// After calling this, `ssa()` will return `Some(...)`.
84 pub fn compute_ssa(&mut self) {
85 if self.ssa.is_none() && self.cfg.block_count() > 0 {
86 // Use SsaBuilder with both instructions and CFG for full SSA construction
87 let builder = SsaBuilder::new(&self.cfg, &self.instructions);
88 self.ssa = Some(builder.build());
89 }
90 }
91
92 /// Get SSA statistics if SSA form is available.
93 ///
94 /// # Returns
95 ///
96 /// `Option<String>` - Formatted statistics string, or `None` if SSA not computed.
97 #[must_use]
98 pub fn ssa_stats(&self) -> Option<String> {
99 self.ssa.as_ref().map(|ssa| format!("{}", ssa.stats()))
100 }
101
102 /// Render SSA form if available.
103 ///
104 /// # Returns
105 ///
106 /// `Option<String>` - Rendered SSA code, or `None` if SSA not computed.
107 #[must_use]
108 pub fn render_ssa(&self) -> Option<String> {
109 self.ssa.as_ref().map(SsaForm::render)
110 }
111}