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
/**
 * Post-processing passes for high-level decompiler output.
 *
 * Ported from Rust's decompiler/high_level/emitter/postprocess/ module.
 * These passes rewrite raw lifted statements into cleaner pseudo-code.
 *
 * Pass order MUST match the Rust finish() method in core.rs.
 */

import {
  collapseIfTrue,
  invertEmptyIfElse,
  removeEmptyIf,
  rewriteElseIfChains,
} from "./postprocess-branches.js";
import {
  collapseTempIntoStore,
  eliminateBareTempUses,
  eliminateIdentityTemps,
  stripStackComments,
} from "./postprocess-cleanup.js";
import {
  rewriteCompoundAssignments,
  rewriteIndexingSyntax,
} from "./postprocess-expressions.js";
import { inlineSingleUseTemps } from "./postprocess-inline.js";
import {
  eliminateFallthroughGotos,
  removeOrphanedLabels,
  rewriteGotoDoWhile,
  rewriteIfGotoToWhile,
  rewriteLabelGotoToLoop,
} from "./postprocess-labels.js";
import {
  inlineConditionTemps,
  inlineForIncrementTemps,
  rewriteForLoops,
} from "./postprocess-loops.js";
import { collapseOverflowChecks } from "./postprocess-overflow.js";
import {
  rewriteSwitchBreakGotos,
  rewriteSwitchStatements,
} from "./postprocess-switch.js";

/**
 * Apply all post-processing passes to lifted statements.
 *
 * @param {string[]} statements - flat list of pseudo-code lines, mutated in place
 * @param {object} [options] - optional settings
 * @param {boolean} [options.inlineSingleUseTemps] - enable single-use temp inlining
 */
export function postprocess(statements, options = {}) {
  rewriteElseIfChains(statements);
  collapseOverflowChecks(statements);
  rewriteGotoDoWhile(statements);
  rewriteIfGotoToWhile(statements);
  eliminateFallthroughGotos(statements);
  rewriteLabelGotoToLoop(statements);
  removeOrphanedLabels(statements);
  rewriteForLoops(statements);
  inlineConditionTemps(statements);
  inlineForIncrementTemps(statements);

  if (options.inlineSingleUseTemps) {
    inlineSingleUseTemps(statements);
    // Inlining can collapse the body between a transfer and its target label,
    // turning a previously necessary transfer into fallthrough noise.
    eliminateFallthroughGotos(statements);
    removeOrphanedLabels(statements);
  }

  rewriteCompoundAssignments(statements);
  rewriteIndexingSyntax(statements);
  collapseIfTrue(statements);
  invertEmptyIfElse(statements);
  removeEmptyIf(statements);
  stripStackComments(statements);
  eliminateIdentityTemps(statements);
  collapseTempIntoStore(statements);
  eliminateBareTempUses(statements);
  rewriteSwitchStatements(statements);
  rewriteSwitchBreakGotos(statements);

  retainNonBlank(statements);
}

function retainNonBlank(statements) {
  let write = 0;
  for (let read = 0; read < statements.length; read++) {
    if (statements[read].trim() !== "") {
      statements[write++] = statements[read];
    }
  }
  statements.length = write;
}