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
import {
  findMatchingBrace,
  isBlank,
  isTempIdent,
  leadingWhitespace,
  nextCodeLine,
  parseAssignment,
} from "./postprocess-shared.js";

const OVERFLOW_BOUNDS = [
  "-2147483648",
  "0",
  "-9223372036854775808",
  "2147483647",
  "4294967295",
  "9223372036854775807",
  "18446744073709551615",
];

export function collapseOverflowChecks(statements) {
  let index = 0;
  while (index < statements.length) {
    const collapse = tryMatchOverflow(statements, index);
    if (collapse) {
      applyOverflowCollapse(statements, collapse);
      continue;
    }
    index++;
  }
}

function tryMatchOverflow(statements, idx) {
  const line0 = statements[idx].trim();
  if (line0 === "" || line0.startsWith("//")) return null;
  const a1 = parseLetAssignment(line0);
  if (!a1) return null;

  const dupIdx = nextCodeLine(statements, idx + 1);
  if (dupIdx < 0) return null;
  const a2 = parseLetAssignment(statements[dupIdx].trim());
  if (!a2 || a2.rhs !== a1.lhs) return null;

  const boundIdx = nextCodeLine(statements, dupIdx + 1);
  if (boundIdx < 0) return null;
  const a3 = parseLetAssignment(statements[boundIdx].trim());
  if (!a3 || !OVERFLOW_BOUNDS.includes(a3.rhs)) return null;

  const ifIdx = nextCodeLine(statements, boundIdx + 1);
  if (ifIdx < 0) return null;
  const ifLine = statements[ifIdx].trim();
  if (!ifLine.startsWith("if ") || !ifLine.endsWith("{")) return null;
  if (!ifLine.includes(`${a2.lhs} <`) && !ifLine.includes(`${a2.lhs} ==`) && !ifLine.includes(`${a2.lhs} >`)) {
    return null;
  }

  const ifBlockEnd = findMatchingBrace(statements, ifIdx);
  if (ifBlockEnd < 0) return null;

  let firstBody = null;
  for (let i = ifIdx + 1; i < statements.length; i++) {
    const t = statements[i].trim();
    if (!isBlank(t)) {
      firstBody = t;
      break;
    }
  }
  const isChecked = firstBody !== null && firstBody.startsWith("throw(");

  let blankEnd = ifBlockEnd;
  let elseUnwrap = null;

  if (!isChecked) {
    const next = nextCodeLine(statements, ifBlockEnd + 1);
    if (next >= 0) {
      const nt = statements[next].trim();
      if (nt === "else {" || nt === "} else {") {
        const elseEnd = findMatchingBrace(statements, next);
        if (elseEnd >= 0) blankEnd = elseEnd;
      }
    }
  } else {
    const next = nextCodeLine(statements, ifBlockEnd + 1);
    if (next >= 0) {
      const nt = statements[next].trim();
      if (nt === "else {" || nt === "} else {") {
        const elseEnd = findMatchingBrace(statements, next);
        if (elseEnd >= 0) elseUnwrap = [next, elseEnd];
      }
    }
  }

  return {
    opLine: idx,
    expr: a1.rhs,
    resultVar: a1.lhs,
    blankStart: idx + 1,
    blankEnd,
    isChecked,
    elseUnwrap,
  };
}

function applyOverflowCollapse(statements, c) {
  if (c.isChecked) {
    const wrapped = c.expr.startsWith("checked(") ? c.expr : `checked(${c.expr})`;
    statements[c.opLine] = `${leadingWhitespace(statements[c.opLine])}let ${c.resultVar} = ${wrapped};`;
  }
  for (let i = c.blankStart; i <= c.blankEnd; i++) {
    statements[i] = "";
  }
  if (c.elseUnwrap) {
    statements[c.elseUnwrap[0]] = "";
    statements[c.elseUnwrap[1]] = "";
  }
  if (!c.isChecked) {
    const next = nextCodeLine(statements, c.blankEnd + 1);
    if (next >= 0) {
      const line = statements[next].trim();
      if (!line.startsWith("let ") && !line.startsWith("if ") && !line.startsWith("//")) {
        const a = parseAssignment(line);
        if (a && isTempIdent(a.rhs) && a.rhs !== c.resultVar) {
          statements[next] = `${leadingWhitespace(statements[next])}${a.lhs} = ${c.resultVar};`;
        }
      }
    }
  }
}

function parseLetAssignment(line) {
  if (!line.startsWith("let ")) return null;
  const rest = line.slice(4);
  const semiPos = rest.indexOf(";");
  if (semiPos < 0) return null;
  const body = rest.slice(0, semiPos);
  const eqPos = body.indexOf(" = ");
  if (eqPos < 0) return null;
  return { lhs: body.slice(0, eqPos).trim(), rhs: body.slice(eqPos + 3).trim() };
}