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 { asCount, replaceWithUnknowns, reverseTop } from "./type-support.js";

export function handleStackInstruction(ins, stack, unk, cloneValue, popU) {
  switch (ins.opcode.mnemonic) {
    case "CLEAR":
      stack.length = 0;
      return true;
    case "DEPTH":
      stack.push({ ty: "integer", lit: stack.length, origins: [] });
      return true;
    case "DROP":
      popU();
      return true;
    case "DUP":
      stack.push(stack.length ? cloneValue(stack[stack.length - 1]) : unk());
      return true;
    case "SWAP":
      if (stack.length >= 2) {
        const last = stack.length - 1;
        const value = stack[last];
        stack[last] = stack[last - 1];
        stack[last - 1] = value;
      }
      return true;
    case "OVER":
      stack.push(stack.length >= 2 ? cloneValue(stack[stack.length - 2]) : unk());
      return true;
    case "NIP":
      if (stack.length >= 2) stack.splice(stack.length - 2, 1);
      return true;
    case "ROT": {
      // Pops unconditionally (matching Rust): on a short stack the available
      // elements are still removed, and nothing is pushed back.
      const top = stack.pop();
      const mid = stack.pop();
      const bottom = stack.pop();
      if (top !== undefined && mid !== undefined && bottom !== undefined) {
        stack.push(mid, top, bottom);
      }
      return true;
    }
    case "TUCK": {
      const top = stack.pop();
      const second = stack.pop();
      if (top !== undefined && second !== undefined) {
        stack.push(top, second, cloneValue(top));
      }
      return true;
    }
    case "PICK": {
      const depth = asCount(popU().lit);
      if (depth !== null) {
        const pos = stack.length - 1 - depth;
        if (pos >= 0) {
          stack.push(cloneValue(stack[pos]));
          return true;
        }
      }
      stack.push(unk());
      return true;
    }
    case "ROLL": {
      const depth = asCount(popU().lit);
      if (depth !== null && depth < stack.length) {
        const [value] = stack.splice(stack.length - 1 - depth, 1);
        stack.push(value);
      } else {
        popU();
        replaceWithUnknowns(stack);
        stack.push(unk());
      }
      return true;
    }
    case "XDROP": {
      const depth = asCount(popU().lit);
      if (depth !== null && depth < stack.length) {
        stack.splice(stack.length - 1 - depth, 1);
        return true;
      }
      popU();
      replaceWithUnknowns(stack);
      return true;
    }
    case "REVERSE3":
      reverseTop(stack, 3);
      return true;
    case "REVERSE4":
      reverseTop(stack, 4);
      return true;
    case "REVERSEN": {
      const depth = asCount(popU().lit);
      if (depth !== null) {
        reverseTop(stack, depth);
      } else {
        replaceWithUnknowns(stack);
      }
      return true;
    }
    default:
      return false;
  }
}