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 {
  isLoadLocal,
  isLoadStatic,
  slotIndex,
} from "./call-graph-slots.js";
import { valueToPointer } from "./call-graph-values.js";

export function relativePointerTarget(instruction) {
  // PUSHA carries a signed I32 relative offset (backward pointers are
  // legal). Mirrors Rust's `pusha_absolute_target`: a target that falls
  // before the script start is unresolvable (`checked_add_signed` -> None).
  const target = instruction.offset + instruction.operand.value;
  return target >= 0 ? target : null;
}

export function pointerTargetBeforeIndex(instructions, index, localValues, staticValues) {
  let cursor = index - 1;
  while (cursor >= 0) {
    const previous = instructions[cursor];
    if (!previous) {
      return null;
    }
    if (previous.opcode.mnemonic === "NOP" || previous.opcode.mnemonic === "DUP") {
      cursor -= 1;
      continue;
    }
    if (previous.opcode.mnemonic === "PUSHA" && previous.operand?.kind === "I32") {
      return relativePointerTarget(previous);
    }
    const local = isLoadLocal(previous.opcode.mnemonic)
      ? slotIndex(previous.opcode.mnemonic, previous)
      : null;
    if (local !== null) {
      return valueToPointer(localValues.get(local) ?? null);
    }
    const staticSlot = isLoadStatic(previous.opcode.mnemonic)
      ? slotIndex(previous.opcode.mnemonic, previous)
      : null;
    if (staticSlot !== null) {
      return valueToPointer(staticValues.get(staticSlot) ?? null);
    }
    return null;
  }
  return null;
}

export function pointerTargetFromSlotFlow(previous, localValues, staticValues) {
  if (!previous) {
    return null;
  }
  const local = isLoadLocal(previous.opcode.mnemonic)
    ? slotIndex(previous.opcode.mnemonic, previous)
    : null;
  if (local !== null) {
    return valueToPointer(localValues.get(local) ?? null);
  }
  const staticSlot = isLoadStatic(previous.opcode.mnemonic)
    ? slotIndex(previous.opcode.mnemonic, previous)
    : null;
  if (staticSlot !== null) {
    return valueToPointer(staticValues.get(staticSlot) ?? null);
  }
  return null;
}