neo-decompiler 0.10.2

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

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

use super::super::super::super::helpers::{
    find_manifest_entry_method, next_inferred_method_offset,
};
use super::super::super::helpers::format_method_signature;
use super::super::body;
use super::util::{
    instruction_slice, non_empty_or_all, synthetic_entry_arguments, write_method_close,
};
use super::MethodsContext;

pub(super) fn write_script_entry_if_needed<'a>(
    output: &mut String,
    manifest: &'a ContractManifest,
    context: &MethodsContext<'_>,
    warnings: &mut Vec<String>,
) -> Option<(&'a ManifestMethod, bool)> {
    let entry_offset = context.instructions.first().map(|ins| ins.offset)?;

    let entry_method = find_manifest_entry_method(manifest, entry_offset);
    if entry_method.is_some() {
        return entry_method;
    }

    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,
    );

    writeln!(
        output,
        "        // warning: manifest entry offset did not match script entry at 0x{entry_offset:04X}; using synthetic ScriptEntry"
    )
    .unwrap();
    write_script_entry_body(output, &slice, entry_offset, context, warnings);
    None
}

pub(super) fn write_fallback_entry(
    output: &mut String,
    context: &MethodsContext<'_>,
    warnings: &mut Vec<String>,
) {
    let entry_offset = context
        .instructions
        .first()
        .map(|ins| ins.offset)
        .unwrap_or(0);
    let end = next_inferred_method_offset(context.inferred_method_starts, entry_offset)
        .or_else(|| context.instructions.last().map(|i| i.offset + 1));
    let slice = non_empty_or_all(
        instruction_slice(context.instructions, entry_offset, end),
        context.instructions,
    );

    write_script_entry_body(output, &slice, entry_offset, context, warnings);
}

fn write_script_entry_body(
    output: &mut String,
    slice: &[Instruction],
    entry_offset: usize,
    context: &MethodsContext<'_>,
    warnings: &mut Vec<String>,
) {
    let (parameters, argument_labels) = synthetic_entry_arguments(
        slice,
        entry_offset,
        context,
        context.body_context.typed_declarations,
    );
    let entry_signature = format_method_signature("ScriptEntry", &parameters, "object");
    writeln!(output, "        {entry_signature}").unwrap();
    writeln!(output, "        {{").unwrap();
    body::write_lifted_body(
        output,
        slice,
        (!argument_labels.is_empty()).then_some(argument_labels.as_slice()),
        warnings,
        &context.body_context,
        false,
    );
    write_method_close(output);
}