neo-decompiler 0.11.0

Neo N3 NEF decompiler: parse, disassemble, lift bytecode to high-level pseudocode and C# skeletons, with a CLI, JSON reports, and optional WebAssembly bindings.
Documentation
use std::collections::HashSet;

use crate::disassembler::DisassemblyOutput;
use crate::error::Result;
use crate::manifest::ContractManifest;

use super::super::cfg::CfgBuilder;
use super::super::decompilation::Decompilation;
use super::super::output_format::{OutputFormat, RenderOptions};
use super::super::{analysis, csharp, high_level, pseudocode};
use super::Decompiler;

impl Decompiler {
    pub(super) fn disassemble_bytes_inner(&self, bytes: &[u8]) -> Result<DisassemblyOutput> {
        let nef = self.parser.parse(bytes)?;
        self.disassembler.disassemble_with_warnings(&nef.script)
    }

    pub(super) fn decompile_bytes_with_manifest_inner(
        &self,
        bytes: &[u8],
        manifest: Option<ContractManifest>,
        output_format: OutputFormat,
    ) -> Result<Decompilation> {
        let parse_output = self.parser.parse_with_warnings(bytes)?;
        let nef = parse_output.nef;
        let disassembly = self.disassembler.disassemble_with_warnings(&nef.script)?;
        let instructions = disassembly.instructions;

        let mut warnings = UniqueWarnings::default();
        warnings.extend(
            parse_output
                .warnings
                .into_iter()
                .map(|warning| warning.to_string()),
        );
        warnings.extend(
            disassembly
                .warnings
                .into_iter()
                .map(|warning| warning.to_string()),
        );

        let cfg = CfgBuilder::new(&instructions).build();
        let call_graph =
            analysis::call_graph::build_call_graph(&nef, &instructions, manifest.as_ref());
        let xrefs = analysis::xrefs::build_xrefs(&instructions, manifest.as_ref());
        let types = analysis::types::infer_types_with_method_tokens(
            &instructions,
            manifest.as_ref(),
            &nef.method_tokens,
        );

        let render_opts = RenderOptions {
            inline_single_use_temps: self.inline_single_use_temps,
            emit_trace_comments: self.emit_trace_comments,
            typed_declarations: self.typed_declarations,
        };

        let pseudocode = output_format
            .wants_pseudocode()
            .then(|| pseudocode::render(&instructions));
        let high_level = output_format.wants_high_level().then(|| {
            let render = high_level::render_high_level(
                &nef,
                &instructions,
                manifest.as_ref(),
                &call_graph,
                &types,
                &render_opts,
            );
            warnings.extend(render.warnings);
            render.text
        });
        let csharp = output_format.wants_csharp().then(|| {
            let render = csharp::render_csharp(
                &nef,
                &instructions,
                manifest.as_ref(),
                &call_graph,
                &types,
                &render_opts,
            );
            warnings.extend(render.warnings);
            render.source
        });

        Ok(Decompilation {
            nef,
            manifest,
            warnings: warnings.into_vec(),
            instructions,
            cfg,
            call_graph,
            xrefs,
            types,
            pseudocode,
            high_level,
            csharp,
            ssa: None,
        })
    }
}

#[derive(Default)]
struct UniqueWarnings {
    values: Vec<String>,
    seen: HashSet<String>,
}

impl UniqueWarnings {
    fn extend(&mut self, warnings: impl IntoIterator<Item = String>) {
        for warning in warnings {
            if self.seen.insert(warning.clone()) {
                self.values.push(warning);
            }
        }
    }

    fn into_vec(self) -> Vec<String> {
        self.values
    }
}