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 { stripOuterParens, wrapExpression } from "./high-level-stack-shared.js";
import { warnStackUnderflow } from "./high-level-state.js";

const BINARY_OPERATORS = {
  ADD: "+",
  SUB: "-",
  MUL: "*",
  DIV: "/",
  MOD: "%",
  AND: "&",
  OR: "|",
  XOR: "^",
  EQUAL: "==",
  NOTEQUAL: "!=",
  LT: "<",
  LE: "<=",
  GT: ">",
  GE: ">=",
  BOOLAND: "&&",
  BOOLOR: "||",
  NUMEQUAL: "==",
  NUMNOTEQUAL: "!=",
  CAT: "cat",
};

export function tryBinaryExpression(state, instruction) {
  const { stack } = state;
  const mnemonic = instruction.opcode.mnemonic;
  const operator = BINARY_OPERATORS[mnemonic];
  if (operator) {
    if (!ensureStack(state, instruction, 2)) return true;
    const right = stack.pop();
    const left = stack.pop();
    stack.push(`${wrapExpression(left)} ${operator} ${wrapExpression(right)}`);
    return true;
  }
  switch (mnemonic) {
    case "POW": {
      if (!ensureStack(state, instruction, 2)) return true;
      const exponent = stack.pop();
      const base = stack.pop();
      stack.push(`pow(${base}, ${exponent})`);
      return true;
    }
    case "MODPOW": {
      if (!ensureStack(state, instruction, 3)) return true;
      const modulus = stack.pop();
      const exponent = stack.pop();
      const base = stack.pop();
      stack.push(`modpow(${base}, ${exponent}, ${modulus})`);
      return true;
    }
    case "MODMUL": {
      if (!ensureStack(state, instruction, 3)) return true;
      const modulus = stack.pop();
      const right = stack.pop();
      const left = stack.pop();
      stack.push(`modmul(${left}, ${right}, ${modulus})`);
      return true;
    }
    case "MAX": {
      if (!ensureStack(state, instruction, 2)) return true;
      const right = stack.pop();
      const left = stack.pop();
      stack.push(`max(${left}, ${right})`);
      return true;
    }
    case "MIN": {
      if (!ensureStack(state, instruction, 2)) return true;
      const right = stack.pop();
      const left = stack.pop();
      stack.push(`min(${left}, ${right})`);
      return true;
    }
    case "WITHIN": {
      if (!ensureStack(state, instruction, 3)) return true;
      const upper = stack.pop();
      const lower = stack.pop();
      const value = stack.pop();
      stack.push(`within(${value}, ${lower}, ${upper})`);
      return true;
    }
    case "LEFT": {
      if (!ensureStack(state, instruction, 2)) return true;
      const count = stack.pop();
      const value = stack.pop();
      stack.push(`left(${value}, ${count})`);
      return true;
    }
    case "RIGHT": {
      if (!ensureStack(state, instruction, 2)) return true;
      const count = stack.pop();
      const value = stack.pop();
      stack.push(`right(${value}, ${count})`);
      return true;
    }
    case "SHL": {
      if (!ensureStack(state, instruction, 2)) return true;
      const shift = stripOuterParens(stack.pop());
      const value = stripOuterParens(stack.pop());
      stack.push(`${wrapExpression(value)} << ${shift}`);
      return true;
    }
    case "SHR": {
      if (!ensureStack(state, instruction, 2)) return true;
      const shift = stripOuterParens(stack.pop());
      const value = stripOuterParens(stack.pop());
      stack.push(`${wrapExpression(value)} >> ${shift}`);
      return true;
    }
    default:
      return false;
  }
}

function ensureStack(state, instruction, needed) {
  if (state.stack.length >= needed) return true;
  warnStackUnderflow(state, instruction, needed);
  return false;
}