Skip to main content

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 the cached SSA form for this decompilation, if available.
59    ///
60    /// Call [`Self::compute_ssa`] first to populate the cached SSA value.
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 mut decompilation = decompiler.decompile_bytes(&nef_bytes)?;
70    /// decompilation.compute_ssa();
71    /// if let Some(ssa) = decompilation.ssa() {
72    ///     println!("SSA Stats: {}", ssa.stats());
73    ///     println!("{}", ssa.render());
74    /// }
75    /// ```
76    #[must_use]
77    pub fn ssa(&self) -> Option<&SsaForm> {
78        self.ssa.as_ref()
79    }
80
81    /// Compute SSA form if not already computed.
82    ///
83    /// This is a convenience method for computing SSA form lazily.
84    /// After calling this, `ssa()` will return `Some(...)`.
85    pub fn compute_ssa(&mut self) {
86        if self.ssa.is_none() && self.cfg.block_count() > 0 {
87            // Use SsaBuilder with both instructions and CFG for full SSA construction
88            let builder = SsaBuilder::new(&self.cfg, &self.instructions);
89            self.ssa = Some(builder.build());
90        }
91    }
92
93    /// Get SSA statistics if SSA form is available.
94    ///
95    /// # Returns
96    ///
97    /// `Option<String>` - Formatted statistics string, or `None` if SSA not computed.
98    #[must_use]
99    pub fn ssa_stats(&self) -> Option<String> {
100        self.ssa.as_ref().map(|ssa| format!("{}", ssa.stats()))
101    }
102
103    /// Render SSA form if available.
104    ///
105    /// # Returns
106    ///
107    /// `Option<String>` - Rendered SSA code, or `None` if SSA not computed.
108    #[must_use]
109    pub fn render_ssa(&self) -> Option<String> {
110        self.ssa.as_ref().map(SsaForm::render)
111    }
112}