neo-decompiler 0.10.1

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 std::fmt::Write;

use crate::manifest::{ContractManifest, ManifestMethod};

use super::super::super::super::helpers::next_inferred_method_offset;
use super::super::super::helpers::collect_csharp_parameters;
use super::super::body;
use super::util::{
    instruction_slice, method_offset, method_render_info, non_empty_or_all, write_method_close,
    write_method_open,
};
use super::MethodsContext;

pub(super) fn write_manifest_methods(
    output: &mut String,
    manifest: &ContractManifest,
    context: &MethodsContext<'_>,
    warnings: &mut Vec<String>,
) {
    let entry_method =
        super::entry::write_script_entry_if_needed(output, manifest, context, warnings);
    let entry_offset = context
        .instructions
        .first()
        .map(|ins| ins.offset)
        .unwrap_or(0);

    let mut used_signatures: HashSet<(String, String)> = HashSet::new();
    let mut sorted_methods: Vec<&ManifestMethod> = manifest.abi.methods.iter().collect();
    sorted_methods.sort_by_key(|m| m.offset.unwrap_or(i32::MAX));

    let (with_offsets, without_offsets): (Vec<_>, Vec<_>) =
        sorted_methods.into_iter().partition(|m| m.offset.is_some());

    for method in with_offsets {
        write_offset_manifest_method(output, method, context, warnings, &mut used_signatures);
    }
    for method in without_offsets {
        write_offsetless_manifest_method(
            output,
            method,
            entry_method,
            entry_offset,
            context,
            warnings,
            &mut used_signatures,
        );
    }
}

fn write_offset_manifest_method(
    output: &mut String,
    method: &ManifestMethod,
    context: &MethodsContext<'_>,
    warnings: &mut Vec<String>,
    used_signatures: &mut HashSet<(String, String)>,
) {
    let start = method_offset(method);
    let end = next_inferred_method_offset(context.inferred_method_starts, start)
        .or_else(|| context.instructions.last().map(|i| i.offset + 1))
        .unwrap_or(start);
    let slice = instruction_slice(context.instructions, start, Some(end));

    let info = method_render_info(method, used_signatures);
    write_method_open(output, &info);
    if slice.is_empty() {
        writeln!(output, "            throw new NotImplementedException();").unwrap();
    } else {
        let params = collect_csharp_parameters(&method.parameters);
        let labels: Vec<String> = params.iter().map(|p| p.name.clone()).collect();
        body::write_lifted_body(
            output,
            &slice,
            Some(&labels),
            warnings,
            &context.body_context,
            info.return_type == "void",
        );
    }
    write_method_close(output);
}

fn write_offsetless_manifest_method(
    output: &mut String,
    method: &ManifestMethod,
    entry_method: Option<(&ManifestMethod, bool)>,
    entry_offset: usize,
    context: &MethodsContext<'_>,
    warnings: &mut Vec<String>,
    used_signatures: &mut HashSet<(String, String)>,
) {
    let info = method_render_info(method, used_signatures);
    write_method_open(output, &info);

    if entry_method
        .as_ref()
        .map(|(entry, _)| std::ptr::eq(*entry, method))
        .unwrap_or(false)
    {
        let end = next_inferred_method_offset(context.inferred_method_starts, entry_offset);
        let slice = non_empty_or_all(
            instruction_slice(context.instructions, entry_offset, end),
            context.instructions,
        );
        let params = collect_csharp_parameters(&method.parameters);
        let labels: Vec<String> = params.iter().map(|p| p.name.clone()).collect();
        body::write_lifted_body(
            output,
            &slice,
            Some(&labels),
            warnings,
            &context.body_context,
            info.return_type == "void",
        );
    } else {
        writeln!(output, "            throw new NotImplementedException();").unwrap();
    }

    write_method_close(output);
}